diff --git a/soh/.clang-format b/soh/.clang-format new file mode 100644 index 000000000..c7b900f06 --- /dev/null +++ b/soh/.clang-format @@ -0,0 +1,23 @@ +IndentWidth: 4 +Language: Cpp +UseTab: Never +ColumnLimit: 120 +PointerAlignment: Left +BreakBeforeBraces: Attach +SpaceAfterCStyleCast: false +Cpp11BracedListStyle: false +IndentCaseLabels: true +BinPackArguments: true +BinPackParameters: true +AlignAfterOpenBracket: Align +AlignOperands: true +BreakBeforeTernaryOperators: true +BreakBeforeBinaryOperators: None +AllowShortBlocksOnASingleLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AlignEscapedNewlines: Left +AlignTrailingComments: true +SortIncludes: false diff --git a/soh/.clang-tidy b/soh/.clang-tidy new file mode 100644 index 000000000..4022a3f68 --- /dev/null +++ b/soh/.clang-tidy @@ -0,0 +1,5 @@ +Checks: '-*,readability-braces-around-statements' +WarningsAsErrors: '' +HeaderFilterRegex: '(src|include)\/.*\.h$' +FormatStyle: 'file' +CheckOptions: diff --git a/soh/.gitattributes b/soh/.gitattributes new file mode 100644 index 000000000..dfe077042 --- /dev/null +++ b/soh/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/soh/.gitignore b/soh/.gitignore new file mode 100644 index 000000000..fafae443d --- /dev/null +++ b/soh/.gitignore @@ -0,0 +1,402 @@ +# Cache files +__pycache__/ +.pyc +.DS_Store + +# Text editor remnants +.vscode/ +.vs/ +.idea/ +CMakeLists.txt +cmake-build-debug +venv/ + +# Project-specific ignores +build/ +expected/ +notes/ +baserom/ +docs/doxygen/ +*.elf +*.sra +*.z64 +*.n64 +*.v64 +*.map +*.dump +out.txt + +# Tool artifacts +tools/mipspro7.2_compiler/ +tools/overlayhelpers/batchdisasm/output/* +tools/overlayhelpers/batchdisasm/output2/* +tools/overlayhelpers/batchdisasm/mipsdisasm/* +tools/disasm/output/* +tools/asmsplitter/asm/* +tools/asmsplitter/c/* +ctx.c +tools/*dSYM/ +graphs/ + +# Assets +*.png +*.jpg +*.mdli +*.anmi +*.obj +*.mtl +*.fbx +!*_custom* +.extracted-assets.json + +# Docs +!docs/tutorial/ + +# Per-user configuration +.python-version + + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +*.out +*.o +*.d +lib/libgfxd/libgfxd.a +ExporterTest/ExporterTest.a +ZAPDUtils/ZAPDUtils.a +.vscode/ +build/ +ZAPDUtils/build/ +ZAPD/BuildInfo.h + +DebugObj/* +ReleaseObj/* \ No newline at end of file diff --git a/soh/.gitrepo b/soh/.gitrepo new file mode 100644 index 000000000..1186c7e01 --- /dev/null +++ b/soh/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/HarbourMasters/soh.git + branch = master + commit = ba904bbd0d724784f8f37ff4bea378f6fe26151b + parent = 0bb0e7b53bd80bdc7f78e08c441691737e039b2b + method = rebase + cmdver = 0.4.1 diff --git a/soh/Resource.rc b/soh/Resource.rc new file mode 100644 index 000000000..03e3c9906 --- /dev/null +++ b/soh/Resource.rc @@ -0,0 +1,71 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON "SHIPOFHARKINIAN.ico" + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/soh/SHIPOFHARKINIAN.ico b/soh/SHIPOFHARKINIAN.ico new file mode 100644 index 000000000..87e59d6cd Binary files /dev/null and b/soh/SHIPOFHARKINIAN.ico differ diff --git a/soh/assets/.gitignore b/soh/assets/.gitignore new file mode 100644 index 000000000..ce6902a6a --- /dev/null +++ b/soh/assets/.gitignore @@ -0,0 +1,8 @@ +*.bin +*.c +!text/*.c +*.h +*.cfg +*.vtx.inc +*.dlist.inc +*.txt \ No newline at end of file diff --git a/soh/assets/code/fbdemo_circle/.gitempty b/soh/assets/code/fbdemo_circle/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/code/fbdemo_triforce/.gitempty b/soh/assets/code/fbdemo_triforce/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/code/fbdemo_wipe1/.gitempty b/soh/assets/code/fbdemo_wipe1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/misc/link_animetion/.gitempty b/soh/assets/misc/link_animetion/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/gameplay_dangeon_keep/.gitempty b/soh/assets/objects/gameplay_dangeon_keep/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/gameplay_field_keep/.gitempty b/soh/assets/objects/gameplay_field_keep/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/gameplay_keep/.gitempty b/soh/assets/objects/gameplay_keep/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_Bb/.gitempty b/soh/assets/objects/object_Bb/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ahg/.gitempty b/soh/assets/objects/object_ahg/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_am/.gitempty b/soh/assets/objects/object_am/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ane/.gitempty b/soh/assets/objects/object_ane/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ani/.gitempty b/soh/assets/objects/object_ani/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_anubice/.gitempty b/soh/assets/objects/object_anubice/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_aob/.gitempty b/soh/assets/objects/object_aob/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_b_heart/.gitempty b/soh/assets/objects/object_b_heart/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bba/.gitempty b/soh/assets/objects/object_bba/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bdan_objects/.gitempty b/soh/assets/objects/object_bdan_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bdoor/.gitempty b/soh/assets/objects/object_bdoor/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bg/.gitempty b/soh/assets/objects/object_bg/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bigokuta/.gitempty b/soh/assets/objects/object_bigokuta/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bird/.gitempty b/soh/assets/objects/object_bird/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bji/.gitempty b/soh/assets/objects/object_bji/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bl/.gitempty b/soh/assets/objects/object_bl/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_blkobj/.gitempty b/soh/assets/objects/object_blkobj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bob/.gitempty b/soh/assets/objects/object_bob/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_boj/.gitempty b/soh/assets/objects/object_boj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bombf/.gitempty b/soh/assets/objects/object_bombf/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bombiwa/.gitempty b/soh/assets/objects/object_bombiwa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bowl/.gitempty b/soh/assets/objects/object_bowl/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_box/.gitempty b/soh/assets/objects/object_box/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_brob/.gitempty b/soh/assets/objects/object_brob/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bubble/.gitempty b/soh/assets/objects/object_bubble/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bv/.gitempty b/soh/assets/objects/object_bv/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bw/.gitempty b/soh/assets/objects/object_bw/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bwall/.gitempty b/soh/assets/objects/object_bwall/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_bxa/.gitempty b/soh/assets/objects/object_bxa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_cne/.gitempty b/soh/assets/objects/object_cne/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_cob/.gitempty b/soh/assets/objects/object_cob/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_cow/.gitempty b/soh/assets/objects/object_cow/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_crow/.gitempty b/soh/assets/objects/object_crow/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_cs/.gitempty b/soh/assets/objects/object_cs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_d_elevator/.gitempty b/soh/assets/objects/object_d_elevator/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_d_hsblock/.gitempty b/soh/assets/objects/object_d_hsblock/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_d_lift/.gitempty b/soh/assets/objects/object_d_lift/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_daiku/.gitempty b/soh/assets/objects/object_daiku/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ddan_objects/.gitempty b/soh/assets/objects/object_ddan_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dekubaba/.gitempty b/soh/assets/objects/object_dekubaba/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dekujr/.gitempty b/soh/assets/objects/object_dekujr/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dekunuts/.gitempty b/soh/assets/objects/object_dekunuts/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_demo_6k/.gitempty b/soh/assets/objects/object_demo_6k/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_demo_kekkai/.gitempty b/soh/assets/objects/object_demo_kekkai/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_demo_tre_lgt/.gitempty b/soh/assets/objects/object_demo_tre_lgt/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dh/.gitempty b/soh/assets/objects/object_dh/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dnk/.gitempty b/soh/assets/objects/object_dnk/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dns/.gitempty b/soh/assets/objects/object_dns/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dodojr/.gitempty b/soh/assets/objects/object_dodojr/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dodongo/.gitempty b/soh/assets/objects/object_dodongo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dog/.gitempty b/soh/assets/objects/object_dog/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_door_gerudo/.gitempty b/soh/assets/objects/object_door_gerudo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_door_killer/.gitempty b/soh/assets/objects/object_door_killer/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ds/.gitempty b/soh/assets/objects/object_ds/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ds2/.gitempty b/soh/assets/objects/object_ds2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_du/.gitempty b/soh/assets/objects/object_du/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_dy_obj/.gitempty b/soh/assets/objects/object_dy_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ec/.gitempty b/soh/assets/objects/object_ec/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_crystal_light/.gitempty b/soh/assets/objects/object_efc_crystal_light/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_doughnut/.gitempty b/soh/assets/objects/object_efc_doughnut/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_erupc/.gitempty b/soh/assets/objects/object_efc_erupc/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_fire_ball/.gitempty b/soh/assets/objects/object_efc_fire_ball/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_flash/.gitempty b/soh/assets/objects/object_efc_flash/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_lgt_shower/.gitempty b/soh/assets/objects/object_efc_lgt_shower/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_star_field/.gitempty b/soh/assets/objects/object_efc_star_field/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_efc_tw/.gitempty b/soh/assets/objects/object_efc_tw/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ei/.gitempty b/soh/assets/objects/object_ei/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fa/.gitempty b/soh/assets/objects/object_fa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fd/.gitempty b/soh/assets/objects/object_fd/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fd2/.gitempty b/soh/assets/objects/object_fd2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fhg/.gitempty b/soh/assets/objects/object_fhg/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fire/.gitempty b/soh/assets/objects/object_fire/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_firefly/.gitempty b/soh/assets/objects/object_firefly/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fish/.gitempty b/soh/assets/objects/object_fish/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fr/.gitempty b/soh/assets/objects/object_fr/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fu/.gitempty b/soh/assets/objects/object_fu/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fw/.gitempty b/soh/assets/objects/object_fw/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_fz/.gitempty b/soh/assets/objects/object_fz/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ganon/.gitempty b/soh/assets/objects/object_ganon/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ganon2/.gitempty b/soh/assets/objects/object_ganon2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ganon_anime1/.gitempty b/soh/assets/objects/object_ganon_anime1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ganon_anime2/.gitempty b/soh/assets/objects/object_ganon_anime2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ganon_anime3/.gitempty b/soh/assets/objects/object_ganon_anime3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ganon_objects/.gitempty b/soh/assets/objects/object_ganon_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ge1/.gitempty b/soh/assets/objects/object_ge1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_geff/.gitempty b/soh/assets/objects/object_geff/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_geldb/.gitempty b/soh/assets/objects/object_geldb/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_arrow/.gitempty b/soh/assets/objects/object_gi_arrow/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_arrowcase/.gitempty b/soh/assets/objects/object_gi_arrowcase/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bean/.gitempty b/soh/assets/objects/object_gi_bean/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bomb_1/.gitempty b/soh/assets/objects/object_gi_bomb_1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bomb_2/.gitempty b/soh/assets/objects/object_gi_bomb_2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bombpouch/.gitempty b/soh/assets/objects/object_gi_bombpouch/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_boomerang/.gitempty b/soh/assets/objects/object_gi_boomerang/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_boots_2/.gitempty b/soh/assets/objects/object_gi_boots_2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bosskey/.gitempty b/soh/assets/objects/object_gi_bosskey/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bottle/.gitempty b/soh/assets/objects/object_gi_bottle/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bottle_letter/.gitempty b/soh/assets/objects/object_gi_bottle_letter/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bow/.gitempty b/soh/assets/objects/object_gi_bow/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_bracelet/.gitempty b/soh/assets/objects/object_gi_bracelet/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_brokensword/.gitempty b/soh/assets/objects/object_gi_brokensword/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_butterfly/.gitempty b/soh/assets/objects/object_gi_butterfly/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_clothes/.gitempty b/soh/assets/objects/object_gi_clothes/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_coin/.gitempty b/soh/assets/objects/object_gi_coin/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_compass/.gitempty b/soh/assets/objects/object_gi_compass/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_dekupouch/.gitempty b/soh/assets/objects/object_gi_dekupouch/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_egg/.gitempty b/soh/assets/objects/object_gi_egg/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_eye_lotion/.gitempty b/soh/assets/objects/object_gi_eye_lotion/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_fire/.gitempty b/soh/assets/objects/object_gi_fire/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_fish/.gitempty b/soh/assets/objects/object_gi_fish/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_frog/.gitempty b/soh/assets/objects/object_gi_frog/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_gerudo/.gitempty b/soh/assets/objects/object_gi_gerudo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_gerudomask/.gitempty b/soh/assets/objects/object_gi_gerudomask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_ghost/.gitempty b/soh/assets/objects/object_gi_ghost/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_glasses/.gitempty b/soh/assets/objects/object_gi_glasses/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_gloves/.gitempty b/soh/assets/objects/object_gi_gloves/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_goddess/.gitempty b/soh/assets/objects/object_gi_goddess/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_golonmask/.gitempty b/soh/assets/objects/object_gi_golonmask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_grass/.gitempty b/soh/assets/objects/object_gi_grass/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_hammer/.gitempty b/soh/assets/objects/object_gi_hammer/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_heart/.gitempty b/soh/assets/objects/object_gi_heart/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_hearts/.gitempty b/soh/assets/objects/object_gi_hearts/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_hookshot/.gitempty b/soh/assets/objects/object_gi_hookshot/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_hoverboots/.gitempty b/soh/assets/objects/object_gi_hoverboots/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_insect/.gitempty b/soh/assets/objects/object_gi_insect/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_jewel/.gitempty b/soh/assets/objects/object_gi_jewel/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_key/.gitempty b/soh/assets/objects/object_gi_key/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_ki_tan_mask/.gitempty b/soh/assets/objects/object_gi_ki_tan_mask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_letter/.gitempty b/soh/assets/objects/object_gi_letter/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_liquid/.gitempty b/soh/assets/objects/object_gi_liquid/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_longsword/.gitempty b/soh/assets/objects/object_gi_longsword/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_m_arrow/.gitempty b/soh/assets/objects/object_gi_m_arrow/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_magicpot/.gitempty b/soh/assets/objects/object_gi_magicpot/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_map/.gitempty b/soh/assets/objects/object_gi_map/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_medal/.gitempty b/soh/assets/objects/object_gi_medal/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_melody/.gitempty b/soh/assets/objects/object_gi_melody/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_milk/.gitempty b/soh/assets/objects/object_gi_milk/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_mushroom/.gitempty b/soh/assets/objects/object_gi_mushroom/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_niwatori/.gitempty b/soh/assets/objects/object_gi_niwatori/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_nuts/.gitempty b/soh/assets/objects/object_gi_nuts/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_ocarina/.gitempty b/soh/assets/objects/object_gi_ocarina/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_ocarina_0/.gitempty b/soh/assets/objects/object_gi_ocarina_0/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_pachinko/.gitempty b/soh/assets/objects/object_gi_pachinko/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_powder/.gitempty b/soh/assets/objects/object_gi_powder/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_prescription/.gitempty b/soh/assets/objects/object_gi_prescription/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_purse/.gitempty b/soh/assets/objects/object_gi_purse/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_rabit_mask/.gitempty b/soh/assets/objects/object_gi_rabit_mask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_redead_mask/.gitempty b/soh/assets/objects/object_gi_redead_mask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_rupy/.gitempty b/soh/assets/objects/object_gi_rupy/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_saw/.gitempty b/soh/assets/objects/object_gi_saw/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_scale/.gitempty b/soh/assets/objects/object_gi_scale/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_seed/.gitempty b/soh/assets/objects/object_gi_seed/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_shield_1/.gitempty b/soh/assets/objects/object_gi_shield_1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_shield_2/.gitempty b/soh/assets/objects/object_gi_shield_2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_shield_3/.gitempty b/soh/assets/objects/object_gi_shield_3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_skj_mask/.gitempty b/soh/assets/objects/object_gi_skj_mask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_soldout/.gitempty b/soh/assets/objects/object_gi_soldout/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_soul/.gitempty b/soh/assets/objects/object_gi_soul/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_stick/.gitempty b/soh/assets/objects/object_gi_stick/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_sutaru/.gitempty b/soh/assets/objects/object_gi_sutaru/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_sword_1/.gitempty b/soh/assets/objects/object_gi_sword_1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_ticketstone/.gitempty b/soh/assets/objects/object_gi_ticketstone/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_truth_mask/.gitempty b/soh/assets/objects/object_gi_truth_mask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gi_zoramask/.gitempty b/soh/assets/objects/object_gi_zoramask/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gj/.gitempty b/soh/assets/objects/object_gj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gjyo_objects/.gitempty b/soh/assets/objects/object_gjyo_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gla/.gitempty b/soh/assets/objects/object_gla/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gm/.gitempty b/soh/assets/objects/object_gm/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gnd/.gitempty b/soh/assets/objects/object_gnd/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gnd_magic/.gitempty b/soh/assets/objects/object_gnd_magic/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gndd/.gitempty b/soh/assets/objects/object_gndd/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_god_lgt/.gitempty b/soh/assets/objects/object_god_lgt/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gol/.gitempty b/soh/assets/objects/object_gol/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_goma/.gitempty b/soh/assets/objects/object_goma/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_goroiwa/.gitempty b/soh/assets/objects/object_goroiwa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gr/.gitempty b/soh/assets/objects/object_gr/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gs/.gitempty b/soh/assets/objects/object_gs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_gt/.gitempty b/soh/assets/objects/object_gt/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_haka/.gitempty b/soh/assets/objects/object_haka/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_haka_door/.gitempty b/soh/assets/objects/object_haka_door/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_haka_objects/.gitempty b/soh/assets/objects/object_haka_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_hakach_objects/.gitempty b/soh/assets/objects/object_hakach_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_hata/.gitempty b/soh/assets/objects/object_hata/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_heavy_object/.gitempty b/soh/assets/objects/object_heavy_object/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_hidan_objects/.gitempty b/soh/assets/objects/object_hidan_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_hintnuts/.gitempty b/soh/assets/objects/object_hintnuts/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_hni/.gitempty b/soh/assets/objects/object_hni/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_horse/.gitempty b/soh/assets/objects/object_horse/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_horse_ganon/.gitempty b/soh/assets/objects/object_horse_ganon/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_horse_link_child/.gitempty b/soh/assets/objects/object_horse_link_child/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_horse_normal/.gitempty b/soh/assets/objects/object_horse_normal/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_horse_zelda/.gitempty b/soh/assets/objects/object_horse_zelda/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_hs/.gitempty b/soh/assets/objects/object_hs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_human/.gitempty b/soh/assets/objects/object_human/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ice_objects/.gitempty b/soh/assets/objects/object_ice_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ik/.gitempty b/soh/assets/objects/object_ik/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_im/.gitempty b/soh/assets/objects/object_im/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_in/.gitempty b/soh/assets/objects/object_in/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ingate/.gitempty b/soh/assets/objects/object_ingate/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_jj/.gitempty b/soh/assets/objects/object_jj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_js/.gitempty b/soh/assets/objects/object_js/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_jya_door/.gitempty b/soh/assets/objects/object_jya_door/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_jya_iron/.gitempty b/soh/assets/objects/object_jya_iron/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_jya_obj/.gitempty b/soh/assets/objects/object_jya_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ka/.gitempty b/soh/assets/objects/object_ka/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_kanban/.gitempty b/soh/assets/objects/object_kanban/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_kibako2/.gitempty b/soh/assets/objects/object_kibako2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_kingdodongo/.gitempty b/soh/assets/objects/object_kingdodongo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_km1/.gitempty b/soh/assets/objects/object_km1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_kusa/.gitempty b/soh/assets/objects/object_kusa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_kw1/.gitempty b/soh/assets/objects/object_kw1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_kz/.gitempty b/soh/assets/objects/object_kz/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_light_ring/.gitempty b/soh/assets/objects/object_light_ring/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_lightbox/.gitempty b/soh/assets/objects/object_lightbox/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_lightswitch/.gitempty b/soh/assets/objects/object_lightswitch/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_link_boy/.gitempty b/soh/assets/objects/object_link_boy/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_link_child/.gitempty b/soh/assets/objects/object_link_child/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ma1/.gitempty b/soh/assets/objects/object_ma1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ma2/.gitempty b/soh/assets/objects/object_ma2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mag/.gitempty b/soh/assets/objects/object_mag/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mamenoki/.gitempty b/soh/assets/objects/object_mamenoki/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mastergolon/.gitempty b/soh/assets/objects/object_mastergolon/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_masterkokiri/.gitempty b/soh/assets/objects/object_masterkokiri/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_masterkokirihead/.gitempty b/soh/assets/objects/object_masterkokirihead/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_masterzoora/.gitempty b/soh/assets/objects/object_masterzoora/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mb/.gitempty b/soh/assets/objects/object_mb/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_md/.gitempty b/soh/assets/objects/object_md/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_medal/.gitempty b/soh/assets/objects/object_medal/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_menkuri_objects/.gitempty b/soh/assets/objects/object_menkuri_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mir_ray/.gitempty b/soh/assets/objects/object_mir_ray/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mizu_objects/.gitempty b/soh/assets/objects/object_mizu_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin/.gitempty b/soh/assets/objects/object_mjin/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_dark/.gitempty b/soh/assets/objects/object_mjin_dark/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_flame/.gitempty b/soh/assets/objects/object_mjin_flame/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_flash/.gitempty b/soh/assets/objects/object_mjin_flash/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_ice/.gitempty b/soh/assets/objects/object_mjin_ice/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_oka/.gitempty b/soh/assets/objects/object_mjin_oka/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_soul/.gitempty b/soh/assets/objects/object_mjin_soul/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mjin_wind/.gitempty b/soh/assets/objects/object_mjin_wind/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mk/.gitempty b/soh/assets/objects/object_mk/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mm/.gitempty b/soh/assets/objects/object_mm/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mo/.gitempty b/soh/assets/objects/object_mo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mori_hineri1/.gitempty b/soh/assets/objects/object_mori_hineri1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mori_hineri1a/.gitempty b/soh/assets/objects/object_mori_hineri1a/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mori_hineri2/.gitempty b/soh/assets/objects/object_mori_hineri2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mori_hineri2a/.gitempty b/soh/assets/objects/object_mori_hineri2a/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mori_objects/.gitempty b/soh/assets/objects/object_mori_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mori_tex/.gitempty b/soh/assets/objects/object_mori_tex/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ms/.gitempty b/soh/assets/objects/object_ms/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_mu/.gitempty b/soh/assets/objects/object_mu/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_nb/.gitempty b/soh/assets/objects/object_nb/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_niw/.gitempty b/soh/assets/objects/object_niw/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_nwc/.gitempty b/soh/assets/objects/object_nwc/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ny/.gitempty b/soh/assets/objects/object_ny/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA1/.gitempty b/soh/assets/objects/object_oA1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA10/.gitempty b/soh/assets/objects/object_oA10/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA11/.gitempty b/soh/assets/objects/object_oA11/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA2/.gitempty b/soh/assets/objects/object_oA2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA3/.gitempty b/soh/assets/objects/object_oA3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA4/.gitempty b/soh/assets/objects/object_oA4/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA5/.gitempty b/soh/assets/objects/object_oA5/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA6/.gitempty b/soh/assets/objects/object_oA6/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA7/.gitempty b/soh/assets/objects/object_oA7/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA8/.gitempty b/soh/assets/objects/object_oA8/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oA9/.gitempty b/soh/assets/objects/object_oA9/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oB1/.gitempty b/soh/assets/objects/object_oB1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oB2/.gitempty b/soh/assets/objects/object_oB2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oB3/.gitempty b/soh/assets/objects/object_oB3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oB4/.gitempty b/soh/assets/objects/object_oB4/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE1/.gitempty b/soh/assets/objects/object_oE1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE10/.gitempty b/soh/assets/objects/object_oE10/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE11/.gitempty b/soh/assets/objects/object_oE11/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE12/.gitempty b/soh/assets/objects/object_oE12/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE1s/.gitempty b/soh/assets/objects/object_oE1s/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE2/.gitempty b/soh/assets/objects/object_oE2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE3/.gitempty b/soh/assets/objects/object_oE3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE4/.gitempty b/soh/assets/objects/object_oE4/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE4s/.gitempty b/soh/assets/objects/object_oE4s/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE5/.gitempty b/soh/assets/objects/object_oE5/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE6/.gitempty b/soh/assets/objects/object_oE6/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE7/.gitempty b/soh/assets/objects/object_oE7/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE8/.gitempty b/soh/assets/objects/object_oE8/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE9/.gitempty b/soh/assets/objects/object_oE9/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oE_anime/.gitempty b/soh/assets/objects/object_oE_anime/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oF1d_map/.gitempty b/soh/assets/objects/object_oF1d_map/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_oF1s/.gitempty b/soh/assets/objects/object_oF1s/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_o_anime/.gitempty b/soh/assets/objects/object_o_anime/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_okuta/.gitempty b/soh/assets/objects/object_okuta/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_opening_demo1/.gitempty b/soh/assets/objects/object_opening_demo1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_os/.gitempty b/soh/assets/objects/object_os/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_os_anime/.gitempty b/soh/assets/objects/object_os_anime/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ossan/.gitempty b/soh/assets/objects/object_ossan/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ouke_haka/.gitempty b/soh/assets/objects/object_ouke_haka/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_owl/.gitempty b/soh/assets/objects/object_owl/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_peehat/.gitempty b/soh/assets/objects/object_peehat/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_po_composer/.gitempty b/soh/assets/objects/object_po_composer/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_po_field/.gitempty b/soh/assets/objects/object_po_field/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_po_sisters/.gitempty b/soh/assets/objects/object_po_sisters/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_poh/.gitempty b/soh/assets/objects/object_poh/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ps/.gitempty b/soh/assets/objects/object_ps/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_pu_box/.gitempty b/soh/assets/objects/object_pu_box/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_rd/.gitempty b/soh/assets/objects/object_rd/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_reeba/.gitempty b/soh/assets/objects/object_reeba/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_relay_objects/.gitempty b/soh/assets/objects/object_relay_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_rl/.gitempty b/soh/assets/objects/object_rl/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_rr/.gitempty b/soh/assets/objects/object_rr/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_rs/.gitempty b/soh/assets/objects/object_rs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ru1/.gitempty b/soh/assets/objects/object_ru1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ru2/.gitempty b/soh/assets/objects/object_ru2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_sa/.gitempty b/soh/assets/objects/object_sa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_sb/.gitempty b/soh/assets/objects/object_sb/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_sd/.gitempty b/soh/assets/objects/object_sd/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_shop_dungen/.gitempty b/soh/assets/objects/object_shop_dungen/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_shopnuts/.gitempty b/soh/assets/objects/object_shopnuts/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_siofuki/.gitempty b/soh/assets/objects/object_siofuki/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_sk2/.gitempty b/soh/assets/objects/object_sk2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_skb/.gitempty b/soh/assets/objects/object_skb/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_skj/.gitempty b/soh/assets/objects/object_skj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot00_break/.gitempty b/soh/assets/objects/object_spot00_break/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot00_objects/.gitempty b/soh/assets/objects/object_spot00_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot01_matoya/.gitempty b/soh/assets/objects/object_spot01_matoya/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot01_matoyab/.gitempty b/soh/assets/objects/object_spot01_matoyab/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot01_objects/.gitempty b/soh/assets/objects/object_spot01_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot01_objects2/.gitempty b/soh/assets/objects/object_spot01_objects2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot02_objects/.gitempty b/soh/assets/objects/object_spot02_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot03_object/.gitempty b/soh/assets/objects/object_spot03_object/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot04_objects/.gitempty b/soh/assets/objects/object_spot04_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot05_objects/.gitempty b/soh/assets/objects/object_spot05_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot06_objects/.gitempty b/soh/assets/objects/object_spot06_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot07_object/.gitempty b/soh/assets/objects/object_spot07_object/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot08_obj/.gitempty b/soh/assets/objects/object_spot08_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot09_obj/.gitempty b/soh/assets/objects/object_spot09_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot11_obj/.gitempty b/soh/assets/objects/object_spot11_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot12_obj/.gitempty b/soh/assets/objects/object_spot12_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot15_obj/.gitempty b/soh/assets/objects/object_spot15_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot16_obj/.gitempty b/soh/assets/objects/object_spot16_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot17_obj/.gitempty b/soh/assets/objects/object_spot17_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_spot18_obj/.gitempty b/soh/assets/objects/object_spot18_obj/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ssh/.gitempty b/soh/assets/objects/object_ssh/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_sst/.gitempty b/soh/assets/objects/object_sst/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_st/.gitempty b/soh/assets/objects/object_st/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_stream/.gitempty b/soh/assets/objects/object_stream/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_syokudai/.gitempty b/soh/assets/objects/object_syokudai/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ta/.gitempty b/soh/assets/objects/object_ta/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_timeblock/.gitempty b/soh/assets/objects/object_timeblock/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_tite/.gitempty b/soh/assets/objects/object_tite/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_tk/.gitempty b/soh/assets/objects/object_tk/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_toki_objects/.gitempty b/soh/assets/objects/object_toki_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_torch2/.gitempty b/soh/assets/objects/object_torch2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_toryo/.gitempty b/soh/assets/objects/object_toryo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_tp/.gitempty b/soh/assets/objects/object_tp/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_tr/.gitempty b/soh/assets/objects/object_tr/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_trap/.gitempty b/soh/assets/objects/object_trap/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_triforce_spot/.gitempty b/soh/assets/objects/object_triforce_spot/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ts/.gitempty b/soh/assets/objects/object_ts/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_tsubo/.gitempty b/soh/assets/objects/object_tsubo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_tw/.gitempty b/soh/assets/objects/object_tw/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_umajump/.gitempty b/soh/assets/objects/object_umajump/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_vali/.gitempty b/soh/assets/objects/object_vali/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_vase/.gitempty b/soh/assets/objects/object_vase/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_vm/.gitempty b/soh/assets/objects/object_vm/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_wallmaster/.gitempty b/soh/assets/objects/object_wallmaster/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_warp1/.gitempty b/soh/assets/objects/object_warp1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_warp2/.gitempty b/soh/assets/objects/object_warp2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_wf/.gitempty b/soh/assets/objects/object_wf/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_wood02/.gitempty b/soh/assets/objects/object_wood02/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_xc/.gitempty b/soh/assets/objects/object_xc/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_yabusame_point/.gitempty b/soh/assets/objects/object_yabusame_point/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_ydan_objects/.gitempty b/soh/assets/objects/object_ydan_objects/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_yukabyun/.gitempty b/soh/assets/objects/object_yukabyun/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zf/.gitempty b/soh/assets/objects/object_zf/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zg/.gitempty b/soh/assets/objects/object_zg/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zl1/.gitempty b/soh/assets/objects/object_zl1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zl2/.gitempty b/soh/assets/objects/object_zl2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zl2_anime1/.gitempty b/soh/assets/objects/object_zl2_anime1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zl2_anime2/.gitempty b/soh/assets/objects/object_zl2_anime2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zl4/.gitempty b/soh/assets/objects/object_zl4/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/objects/object_zo/.gitempty b/soh/assets/objects/object_zo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Arrow_Fire/.gitempty b/soh/assets/overlays/ovl_Arrow_Fire/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Arrow_Ice/.gitempty b/soh/assets/overlays/ovl_Arrow_Ice/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Arrow_Light/.gitempty b/soh/assets/overlays/ovl_Arrow_Light/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Bg_Ganon_Otyuka/.gitempty b/soh/assets/overlays/ovl_Bg_Ganon_Otyuka/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Bg_Jya_Cobra/.gitempty b/soh/assets/overlays/ovl_Bg_Jya_Cobra/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Boss_Dodongo/.gitempty b/soh/assets/overlays/ovl_Boss_Dodongo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Boss_Ganon/.gitempty b/soh/assets/overlays/ovl_Boss_Ganon/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Boss_Ganon2/.gitempty b/soh/assets/overlays/ovl_Boss_Ganon2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Boss_Sst/.gitempty b/soh/assets/overlays/ovl_Boss_Sst/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Demo_Shd/.gitempty b/soh/assets/overlays/ovl_Demo_Shd/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Elf_Msg/.gitempty b/soh/assets/overlays/ovl_Elf_Msg/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Elf_Msg2/.gitempty b/soh/assets/overlays/ovl_Elf_Msg2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Bili/.gitempty b/soh/assets/overlays/ovl_En_Bili/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Clear_Tag/.gitempty b/soh/assets/overlays/ovl_En_Clear_Tag/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Ganon_Mant/.gitempty b/soh/assets/overlays/ovl_En_Ganon_Mant/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Ganon_Organ/.gitempty b/soh/assets/overlays/ovl_En_Ganon_Organ/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Holl/.gitempty b/soh/assets/overlays/ovl_En_Holl/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Jsjutan/.gitempty b/soh/assets/overlays/ovl_En_Jsjutan/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Kanban/.gitempty b/soh/assets/overlays/ovl_En_Kanban/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Sda/.gitempty b/soh/assets/overlays/ovl_En_Sda/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Ssh/.gitempty b/soh/assets/overlays/ovl_En_Ssh/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_St/.gitempty b/soh/assets/overlays/ovl_En_St/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_En_Sth/.gitempty b/soh/assets/overlays/ovl_En_Sth/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_End_Title/.gitempty b/soh/assets/overlays/ovl_End_Title/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_File_Choose/.gitempty b/soh/assets/overlays/ovl_File_Choose/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Magic_Dark/.gitempty b/soh/assets/overlays/ovl_Magic_Dark/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Magic_Fire/.gitempty b/soh/assets/overlays/ovl_Magic_Fire/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Magic_Wind/.gitempty b/soh/assets/overlays/ovl_Magic_Wind/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Oceff_Spot/.gitempty b/soh/assets/overlays/ovl_Oceff_Spot/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Oceff_Storm/.gitempty b/soh/assets/overlays/ovl_Oceff_Storm/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Oceff_Wipe/.gitempty b/soh/assets/overlays/ovl_Oceff_Wipe/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Oceff_Wipe2/.gitempty b/soh/assets/overlays/ovl_Oceff_Wipe2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Oceff_Wipe3/.gitempty b/soh/assets/overlays/ovl_Oceff_Wipe3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/overlays/ovl_Oceff_Wipe4/.gitempty b/soh/assets/overlays/ovl_Oceff_Wipe4/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/Bmori1/.gitempty b/soh/assets/scenes/dungeons/Bmori1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/FIRE_bs/.gitempty b/soh/assets/scenes/dungeons/FIRE_bs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/HAKAdan/.gitempty b/soh/assets/scenes/dungeons/HAKAdan/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/HAKAdanCH/.gitempty b/soh/assets/scenes/dungeons/HAKAdanCH/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/HAKAdan_bs/.gitempty b/soh/assets/scenes/dungeons/HAKAdan_bs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/HIDAN/.gitempty b/soh/assets/scenes/dungeons/HIDAN/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/MIZUsin/.gitempty b/soh/assets/scenes/dungeons/MIZUsin/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/MIZUsin_bs/.gitempty b/soh/assets/scenes/dungeons/MIZUsin_bs/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/bdan/.gitempty b/soh/assets/scenes/dungeons/bdan/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/bdan_boss/.gitempty b/soh/assets/scenes/dungeons/bdan_boss/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ddan/.gitempty b/soh/assets/scenes/dungeons/ddan/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ddan_boss/.gitempty b/soh/assets/scenes/dungeons/ddan_boss/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganon/.gitempty b/soh/assets/scenes/dungeons/ganon/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganon_boss/.gitempty b/soh/assets/scenes/dungeons/ganon_boss/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganon_demo/.gitempty b/soh/assets/scenes/dungeons/ganon_demo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganon_final/.gitempty b/soh/assets/scenes/dungeons/ganon_final/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganon_sonogo/.gitempty b/soh/assets/scenes/dungeons/ganon_sonogo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganon_tou/.gitempty b/soh/assets/scenes/dungeons/ganon_tou/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganontika/.gitempty b/soh/assets/scenes/dungeons/ganontika/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ganontikasonogo/.gitempty b/soh/assets/scenes/dungeons/ganontikasonogo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/gerudoway/.gitempty b/soh/assets/scenes/dungeons/gerudoway/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ice_doukutu/.gitempty b/soh/assets/scenes/dungeons/ice_doukutu/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/jyasinboss/.gitempty b/soh/assets/scenes/dungeons/jyasinboss/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/jyasinzou/.gitempty b/soh/assets/scenes/dungeons/jyasinzou/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/men/.gitempty b/soh/assets/scenes/dungeons/men/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/moribossroom/.gitempty b/soh/assets/scenes/dungeons/moribossroom/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ydan/.gitempty b/soh/assets/scenes/dungeons/ydan/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/dungeons/ydan_boss/.gitempty b/soh/assets/scenes/dungeons/ydan_boss/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/bowling/.gitempty b/soh/assets/scenes/indoors/bowling/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/daiyousei_izumi/.gitempty b/soh/assets/scenes/indoors/daiyousei_izumi/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/hairal_niwa/.gitempty b/soh/assets/scenes/indoors/hairal_niwa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/hairal_niwa2/.gitempty b/soh/assets/scenes/indoors/hairal_niwa2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/hairal_niwa_n/.gitempty b/soh/assets/scenes/indoors/hairal_niwa_n/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/hakasitarelay/.gitempty b/soh/assets/scenes/indoors/hakasitarelay/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/hut/.gitempty b/soh/assets/scenes/indoors/hut/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/hylia_labo/.gitempty b/soh/assets/scenes/indoors/hylia_labo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/impa/.gitempty b/soh/assets/scenes/indoors/impa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/kakariko/.gitempty b/soh/assets/scenes/indoors/kakariko/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/kenjyanoma/.gitempty b/soh/assets/scenes/indoors/kenjyanoma/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/kokiri_home/.gitempty b/soh/assets/scenes/indoors/kokiri_home/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/kokiri_home3/.gitempty b/soh/assets/scenes/indoors/kokiri_home3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/kokiri_home4/.gitempty b/soh/assets/scenes/indoors/kokiri_home4/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/kokiri_home5/.gitempty b/soh/assets/scenes/indoors/kokiri_home5/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/labo/.gitempty b/soh/assets/scenes/indoors/labo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/link_home/.gitempty b/soh/assets/scenes/indoors/link_home/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/mahouya/.gitempty b/soh/assets/scenes/indoors/mahouya/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/malon_stable/.gitempty b/soh/assets/scenes/indoors/malon_stable/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/miharigoya/.gitempty b/soh/assets/scenes/indoors/miharigoya/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/nakaniwa/.gitempty b/soh/assets/scenes/indoors/nakaniwa/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/syatekijyou/.gitempty b/soh/assets/scenes/indoors/syatekijyou/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/takaraya/.gitempty b/soh/assets/scenes/indoors/takaraya/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/tent/.gitempty b/soh/assets/scenes/indoors/tent/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/tokinoma/.gitempty b/soh/assets/scenes/indoors/tokinoma/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/yousei_izumi_tate/.gitempty b/soh/assets/scenes/indoors/yousei_izumi_tate/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/indoors/yousei_izumi_yoko/.gitempty b/soh/assets/scenes/indoors/yousei_izumi_yoko/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/enrui/.gitempty b/soh/assets/scenes/misc/enrui/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/entra_n/.gitempty b/soh/assets/scenes/misc/entra_n/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/hakaana/.gitempty b/soh/assets/scenes/misc/hakaana/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/hakaana2/.gitempty b/soh/assets/scenes/misc/hakaana2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/hakaana_ouke/.gitempty b/soh/assets/scenes/misc/hakaana_ouke/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/hiral_demo/.gitempty b/soh/assets/scenes/misc/hiral_demo/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/kakariko3/.gitempty b/soh/assets/scenes/misc/kakariko3/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/kakusiana/.gitempty b/soh/assets/scenes/misc/kakusiana/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/kinsuta/.gitempty b/soh/assets/scenes/misc/kinsuta/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/market_alley/.gitempty b/soh/assets/scenes/misc/market_alley/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/market_alley_n/.gitempty b/soh/assets/scenes/misc/market_alley_n/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/market_day/.gitempty b/soh/assets/scenes/misc/market_day/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/market_night/.gitempty b/soh/assets/scenes/misc/market_night/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/market_ruins/.gitempty b/soh/assets/scenes/misc/market_ruins/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/shrine/.gitempty b/soh/assets/scenes/misc/shrine/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/shrine_n/.gitempty b/soh/assets/scenes/misc/shrine_n/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/shrine_r/.gitempty b/soh/assets/scenes/misc/shrine_r/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/misc/turibori/.gitempty b/soh/assets/scenes/misc/turibori/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/entra/.gitempty b/soh/assets/scenes/overworld/entra/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/souko/.gitempty b/soh/assets/scenes/overworld/souko/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot00/.gitempty b/soh/assets/scenes/overworld/spot00/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot01/.gitempty b/soh/assets/scenes/overworld/spot01/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot02/.gitempty b/soh/assets/scenes/overworld/spot02/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot03/.gitempty b/soh/assets/scenes/overworld/spot03/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot04/.gitempty b/soh/assets/scenes/overworld/spot04/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot05/.gitempty b/soh/assets/scenes/overworld/spot05/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot06/.gitempty b/soh/assets/scenes/overworld/spot06/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot07/.gitempty b/soh/assets/scenes/overworld/spot07/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot08/.gitempty b/soh/assets/scenes/overworld/spot08/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot09/.gitempty b/soh/assets/scenes/overworld/spot09/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot10/.gitempty b/soh/assets/scenes/overworld/spot10/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot11/.gitempty b/soh/assets/scenes/overworld/spot11/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot12/.gitempty b/soh/assets/scenes/overworld/spot12/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot13/.gitempty b/soh/assets/scenes/overworld/spot13/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot15/.gitempty b/soh/assets/scenes/overworld/spot15/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot16/.gitempty b/soh/assets/scenes/overworld/spot16/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot17/.gitempty b/soh/assets/scenes/overworld/spot17/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot18/.gitempty b/soh/assets/scenes/overworld/spot18/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/overworld/spot20/.gitempty b/soh/assets/scenes/overworld/spot20/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/alley_shop/.gitempty b/soh/assets/scenes/shops/alley_shop/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/drag/.gitempty b/soh/assets/scenes/shops/drag/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/face_shop/.gitempty b/soh/assets/scenes/shops/face_shop/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/golon/.gitempty b/soh/assets/scenes/shops/golon/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/kokiri_shop/.gitempty b/soh/assets/scenes/shops/kokiri_shop/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/night_shop/.gitempty b/soh/assets/scenes/shops/night_shop/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/shop1/.gitempty b/soh/assets/scenes/shops/shop1/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/shops/zoora/.gitempty b/soh/assets/scenes/shops/zoora/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/besitu/.gitempty b/soh/assets/scenes/test_levels/besitu/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/depth_test/.gitempty b/soh/assets/scenes/test_levels/depth_test/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/sasatest/.gitempty b/soh/assets/scenes/test_levels/sasatest/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/sutaru/.gitempty b/soh/assets/scenes/test_levels/sutaru/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/syotes/.gitempty b/soh/assets/scenes/test_levels/syotes/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/syotes2/.gitempty b/soh/assets/scenes/test_levels/syotes2/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/test01/.gitempty b/soh/assets/scenes/test_levels/test01/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/scenes/test_levels/testroom/.gitempty b/soh/assets/scenes/test_levels/testroom/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/text/nes_message_data_static/.gitempty b/soh/assets/text/nes_message_data_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/text/staff_message_data_static/.gitempty b/soh/assets/text/staff_message_data_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/backgrounds/.gitempty b/soh/assets/textures/backgrounds/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/do_action_static/.gitempty b/soh/assets/textures/do_action_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_24_static/.gitempty b/soh/assets/textures/icon_item_24_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_dungeon_static/.gitempty b/soh/assets/textures/icon_item_dungeon_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_field_static/.gitempty b/soh/assets/textures/icon_item_field_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_fra_static/.gitempty b/soh/assets/textures/icon_item_fra_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_gameover_static/.gitempty b/soh/assets/textures/icon_item_gameover_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_ger_static/.gitempty b/soh/assets/textures/icon_item_ger_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_nes_static/.gitempty b/soh/assets/textures/icon_item_nes_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/icon_item_static/.gitempty b/soh/assets/textures/icon_item_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/item_name_static/.gitempty b/soh/assets/textures/item_name_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/map_48x85_static/.gitempty b/soh/assets/textures/map_48x85_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/map_grand_static/.gitempty b/soh/assets/textures/map_grand_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/map_i_static/.gitempty b/soh/assets/textures/map_i_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/map_name_static/.gitempty b/soh/assets/textures/map_name_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/message_static/.gitempty b/soh/assets/textures/message_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/message_texture_static/.gitempty b/soh/assets/textures/message_texture_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/nes_font_static/.gitempty b/soh/assets/textures/nes_font_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/nintendo_rogo_static/.gitempty b/soh/assets/textures/nintendo_rogo_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/parameter_static/.gitempty b/soh/assets/textures/parameter_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/place_title_cards/.gitempty b/soh/assets/textures/place_title_cards/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/skyboxes/.gitempty b/soh/assets/textures/skyboxes/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/textures/title_static/.gitempty b/soh/assets/textures/title_static/.gitempty new file mode 100644 index 000000000..e69de29bb diff --git a/soh/assets/xml/code/fbdemo_circle.xml b/soh/assets/xml/code/fbdemo_circle.xml new file mode 100644 index 000000000..a58e6ae09 --- /dev/null +++ b/soh/assets/xml/code/fbdemo_circle.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/code/fbdemo_triforce.xml b/soh/assets/xml/code/fbdemo_triforce.xml new file mode 100644 index 000000000..1a21af3bc --- /dev/null +++ b/soh/assets/xml/code/fbdemo_triforce.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/code/fbdemo_wipe1.xml b/soh/assets/xml/code/fbdemo_wipe1.xml new file mode 100644 index 000000000..f826fec32 --- /dev/null +++ b/soh/assets/xml/code/fbdemo_wipe1.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/misc/link_animetion.xml b/soh/assets/xml/misc/link_animetion.xml new file mode 100644 index 000000000..9de1e92d3 --- /dev/null +++ b/soh/assets/xml/misc/link_animetion.xml @@ -0,0 +1,577 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/gameplay_dangeon_keep.xml b/soh/assets/xml/objects/gameplay_dangeon_keep.xml new file mode 100644 index 000000000..d16eb4c41 --- /dev/null +++ b/soh/assets/xml/objects/gameplay_dangeon_keep.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/gameplay_field_keep.xml b/soh/assets/xml/objects/gameplay_field_keep.xml new file mode 100644 index 000000000..28fa975d7 --- /dev/null +++ b/soh/assets/xml/objects/gameplay_field_keep.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/gameplay_keep.xml b/soh/assets/xml/objects/gameplay_keep.xml new file mode 100644 index 000000000..45c669307 --- /dev/null +++ b/soh/assets/xml/objects/gameplay_keep.xml @@ -0,0 +1,960 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_Bb.xml b/soh/assets/xml/objects/object_Bb.xml new file mode 100644 index 000000000..5d04bde45 --- /dev/null +++ b/soh/assets/xml/objects/object_Bb.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ahg.xml b/soh/assets/xml/objects/object_ahg.xml new file mode 100644 index 000000000..a32c00914 --- /dev/null +++ b/soh/assets/xml/objects/object_ahg.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_am.xml b/soh/assets/xml/objects/object_am.xml new file mode 100644 index 000000000..0a4b88ddf --- /dev/null +++ b/soh/assets/xml/objects/object_am.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ane.xml b/soh/assets/xml/objects/object_ane.xml new file mode 100644 index 000000000..1e29b351e --- /dev/null +++ b/soh/assets/xml/objects/object_ane.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ani.xml b/soh/assets/xml/objects/object_ani.xml new file mode 100644 index 000000000..c69656043 --- /dev/null +++ b/soh/assets/xml/objects/object_ani.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_anubice.xml b/soh/assets/xml/objects/object_anubice.xml new file mode 100644 index 000000000..2140187e4 --- /dev/null +++ b/soh/assets/xml/objects/object_anubice.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_aob.xml b/soh/assets/xml/objects/object_aob.xml new file mode 100644 index 000000000..e6202d0cf --- /dev/null +++ b/soh/assets/xml/objects/object_aob.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_b_heart.xml b/soh/assets/xml/objects/object_b_heart.xml new file mode 100644 index 000000000..00f7cd677 --- /dev/null +++ b/soh/assets/xml/objects/object_b_heart.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_bba.xml b/soh/assets/xml/objects/object_bba.xml new file mode 100644 index 000000000..ccc5c2136 --- /dev/null +++ b/soh/assets/xml/objects/object_bba.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bdan_objects.xml b/soh/assets/xml/objects/object_bdan_objects.xml new file mode 100644 index 000000000..dcd56d385 --- /dev/null +++ b/soh/assets/xml/objects/object_bdan_objects.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bdoor.xml b/soh/assets/xml/objects/object_bdoor.xml new file mode 100644 index 000000000..644c06ea8 --- /dev/null +++ b/soh/assets/xml/objects/object_bdoor.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bg.xml b/soh/assets/xml/objects/object_bg.xml new file mode 100644 index 000000000..791226b71 --- /dev/null +++ b/soh/assets/xml/objects/object_bg.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bigokuta.xml b/soh/assets/xml/objects/object_bigokuta.xml new file mode 100644 index 000000000..1c7d39e31 --- /dev/null +++ b/soh/assets/xml/objects/object_bigokuta.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bird.xml b/soh/assets/xml/objects/object_bird.xml new file mode 100644 index 000000000..167a5022d --- /dev/null +++ b/soh/assets/xml/objects/object_bird.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bji.xml b/soh/assets/xml/objects/object_bji.xml new file mode 100644 index 000000000..e6b6c4221 --- /dev/null +++ b/soh/assets/xml/objects/object_bji.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bl.xml b/soh/assets/xml/objects/object_bl.xml new file mode 100644 index 000000000..1c2ef4ec6 --- /dev/null +++ b/soh/assets/xml/objects/object_bl.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_blkobj.xml b/soh/assets/xml/objects/object_blkobj.xml new file mode 100644 index 000000000..a073077b8 --- /dev/null +++ b/soh/assets/xml/objects/object_blkobj.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bob.xml b/soh/assets/xml/objects/object_bob.xml new file mode 100644 index 000000000..289e994e4 --- /dev/null +++ b/soh/assets/xml/objects/object_bob.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_boj.xml b/soh/assets/xml/objects/object_boj.xml new file mode 100644 index 000000000..4cc39906e --- /dev/null +++ b/soh/assets/xml/objects/object_boj.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bombf.xml b/soh/assets/xml/objects/object_bombf.xml new file mode 100644 index 000000000..83e60248d --- /dev/null +++ b/soh/assets/xml/objects/object_bombf.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bombiwa.xml b/soh/assets/xml/objects/object_bombiwa.xml new file mode 100644 index 000000000..26ab6b4f3 --- /dev/null +++ b/soh/assets/xml/objects/object_bombiwa.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_bowl.xml b/soh/assets/xml/objects/object_bowl.xml new file mode 100644 index 000000000..ffe6d43db --- /dev/null +++ b/soh/assets/xml/objects/object_bowl.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_box.xml b/soh/assets/xml/objects/object_box.xml new file mode 100644 index 000000000..efcec619b --- /dev/null +++ b/soh/assets/xml/objects/object_box.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_brob.xml b/soh/assets/xml/objects/object_brob.xml new file mode 100644 index 000000000..718dcd561 --- /dev/null +++ b/soh/assets/xml/objects/object_brob.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bubble.xml b/soh/assets/xml/objects/object_bubble.xml new file mode 100644 index 000000000..f5de18692 --- /dev/null +++ b/soh/assets/xml/objects/object_bubble.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_bv.xml b/soh/assets/xml/objects/object_bv.xml new file mode 100644 index 000000000..83ff039f5 --- /dev/null +++ b/soh/assets/xml/objects/object_bv.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bw.xml b/soh/assets/xml/objects/object_bw.xml new file mode 100644 index 000000000..f7d5ecead --- /dev/null +++ b/soh/assets/xml/objects/object_bw.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_bwall.xml b/soh/assets/xml/objects/object_bwall.xml new file mode 100644 index 000000000..d4f43ddc1 --- /dev/null +++ b/soh/assets/xml/objects/object_bwall.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_bxa.xml b/soh/assets/xml/objects/object_bxa.xml new file mode 100644 index 000000000..fb78c117e --- /dev/null +++ b/soh/assets/xml/objects/object_bxa.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_cne.xml b/soh/assets/xml/objects/object_cne.xml new file mode 100644 index 000000000..ce9c16082 --- /dev/null +++ b/soh/assets/xml/objects/object_cne.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_cob.xml b/soh/assets/xml/objects/object_cob.xml new file mode 100644 index 000000000..ba051cfa7 --- /dev/null +++ b/soh/assets/xml/objects/object_cob.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_cow.xml b/soh/assets/xml/objects/object_cow.xml new file mode 100644 index 000000000..5d067c329 --- /dev/null +++ b/soh/assets/xml/objects/object_cow.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_crow.xml b/soh/assets/xml/objects/object_crow.xml new file mode 100644 index 000000000..77337c644 --- /dev/null +++ b/soh/assets/xml/objects/object_crow.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_cs.xml b/soh/assets/xml/objects/object_cs.xml new file mode 100644 index 000000000..83804ccaf --- /dev/null +++ b/soh/assets/xml/objects/object_cs.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_d_elevator.xml b/soh/assets/xml/objects/object_d_elevator.xml new file mode 100644 index 000000000..6be59c154 --- /dev/null +++ b/soh/assets/xml/objects/object_d_elevator.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_d_hsblock.xml b/soh/assets/xml/objects/object_d_hsblock.xml new file mode 100644 index 000000000..b9111b38b --- /dev/null +++ b/soh/assets/xml/objects/object_d_hsblock.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_d_lift.xml b/soh/assets/xml/objects/object_d_lift.xml new file mode 100644 index 000000000..291238519 --- /dev/null +++ b/soh/assets/xml/objects/object_d_lift.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_daiku.xml b/soh/assets/xml/objects/object_daiku.xml new file mode 100644 index 000000000..1524c7a26 --- /dev/null +++ b/soh/assets/xml/objects/object_daiku.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ddan_objects.xml b/soh/assets/xml/objects/object_ddan_objects.xml new file mode 100644 index 000000000..3fc563b96 --- /dev/null +++ b/soh/assets/xml/objects/object_ddan_objects.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dekubaba.xml b/soh/assets/xml/objects/object_dekubaba.xml new file mode 100644 index 000000000..7202a833f --- /dev/null +++ b/soh/assets/xml/objects/object_dekubaba.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dekujr.xml b/soh/assets/xml/objects/object_dekujr.xml new file mode 100644 index 000000000..e906bf61c --- /dev/null +++ b/soh/assets/xml/objects/object_dekujr.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dekunuts.xml b/soh/assets/xml/objects/object_dekunuts.xml new file mode 100644 index 000000000..5fceef416 --- /dev/null +++ b/soh/assets/xml/objects/object_dekunuts.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_demo_6k.xml b/soh/assets/xml/objects/object_demo_6k.xml new file mode 100644 index 000000000..73058d5e4 --- /dev/null +++ b/soh/assets/xml/objects/object_demo_6k.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_demo_kekkai.xml b/soh/assets/xml/objects/object_demo_kekkai.xml new file mode 100644 index 000000000..1b9c9b402 --- /dev/null +++ b/soh/assets/xml/objects/object_demo_kekkai.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_demo_tre_lgt.xml b/soh/assets/xml/objects/object_demo_tre_lgt.xml new file mode 100644 index 000000000..9ea19012b --- /dev/null +++ b/soh/assets/xml/objects/object_demo_tre_lgt.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_dh.xml b/soh/assets/xml/objects/object_dh.xml new file mode 100644 index 000000000..7a52d7dbe --- /dev/null +++ b/soh/assets/xml/objects/object_dh.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dnk.xml b/soh/assets/xml/objects/object_dnk.xml new file mode 100644 index 000000000..b1e79464b --- /dev/null +++ b/soh/assets/xml/objects/object_dnk.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dns.xml b/soh/assets/xml/objects/object_dns.xml new file mode 100644 index 000000000..086c4bd11 --- /dev/null +++ b/soh/assets/xml/objects/object_dns.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dodojr.xml b/soh/assets/xml/objects/object_dodojr.xml new file mode 100644 index 000000000..4dfd7d6e4 --- /dev/null +++ b/soh/assets/xml/objects/object_dodojr.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dodongo.xml b/soh/assets/xml/objects/object_dodongo.xml new file mode 100644 index 000000000..779e20e9d --- /dev/null +++ b/soh/assets/xml/objects/object_dodongo.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dog.xml b/soh/assets/xml/objects/object_dog.xml new file mode 100644 index 000000000..ef00442b6 --- /dev/null +++ b/soh/assets/xml/objects/object_dog.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_door_gerudo.xml b/soh/assets/xml/objects/object_door_gerudo.xml new file mode 100644 index 000000000..2f1726204 --- /dev/null +++ b/soh/assets/xml/objects/object_door_gerudo.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_door_killer.xml b/soh/assets/xml/objects/object_door_killer.xml new file mode 100644 index 000000000..a37f590d6 --- /dev/null +++ b/soh/assets/xml/objects/object_door_killer.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ds.xml b/soh/assets/xml/objects/object_ds.xml new file mode 100644 index 000000000..0413a9e2e --- /dev/null +++ b/soh/assets/xml/objects/object_ds.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ds2.xml b/soh/assets/xml/objects/object_ds2.xml new file mode 100644 index 000000000..683bcc68d --- /dev/null +++ b/soh/assets/xml/objects/object_ds2.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_du.xml b/soh/assets/xml/objects/object_du.xml new file mode 100644 index 000000000..44bbd1aa0 --- /dev/null +++ b/soh/assets/xml/objects/object_du.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_dy_obj.xml b/soh/assets/xml/objects/object_dy_obj.xml new file mode 100644 index 000000000..4d141d546 --- /dev/null +++ b/soh/assets/xml/objects/object_dy_obj.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ec.xml b/soh/assets/xml/objects/object_ec.xml new file mode 100644 index 000000000..7ec83e284 --- /dev/null +++ b/soh/assets/xml/objects/object_ec.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_efc_crystal_light.xml b/soh/assets/xml/objects/object_efc_crystal_light.xml new file mode 100644 index 000000000..9215b6a39 --- /dev/null +++ b/soh/assets/xml/objects/object_efc_crystal_light.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_efc_doughnut.xml b/soh/assets/xml/objects/object_efc_doughnut.xml new file mode 100644 index 000000000..7f12c56f5 --- /dev/null +++ b/soh/assets/xml/objects/object_efc_doughnut.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_efc_erupc.xml b/soh/assets/xml/objects/object_efc_erupc.xml new file mode 100644 index 000000000..800d9535f --- /dev/null +++ b/soh/assets/xml/objects/object_efc_erupc.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_efc_fire_ball.xml b/soh/assets/xml/objects/object_efc_fire_ball.xml new file mode 100644 index 000000000..2fc65b16f --- /dev/null +++ b/soh/assets/xml/objects/object_efc_fire_ball.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_efc_flash.xml b/soh/assets/xml/objects/object_efc_flash.xml new file mode 100644 index 000000000..5f962f162 --- /dev/null +++ b/soh/assets/xml/objects/object_efc_flash.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_efc_lgt_shower.xml b/soh/assets/xml/objects/object_efc_lgt_shower.xml new file mode 100644 index 000000000..67ef5ce7e --- /dev/null +++ b/soh/assets/xml/objects/object_efc_lgt_shower.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_efc_star_field.xml b/soh/assets/xml/objects/object_efc_star_field.xml new file mode 100644 index 000000000..00b47c460 --- /dev/null +++ b/soh/assets/xml/objects/object_efc_star_field.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_efc_tw.xml b/soh/assets/xml/objects/object_efc_tw.xml new file mode 100644 index 000000000..de5376569 --- /dev/null +++ b/soh/assets/xml/objects/object_efc_tw.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ei.xml b/soh/assets/xml/objects/object_ei.xml new file mode 100644 index 000000000..e4132491f --- /dev/null +++ b/soh/assets/xml/objects/object_ei.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fa.xml b/soh/assets/xml/objects/object_fa.xml new file mode 100644 index 000000000..2f1bf9a5e --- /dev/null +++ b/soh/assets/xml/objects/object_fa.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fd.xml b/soh/assets/xml/objects/object_fd.xml new file mode 100644 index 000000000..1b96e9f22 --- /dev/null +++ b/soh/assets/xml/objects/object_fd.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fd2.xml b/soh/assets/xml/objects/object_fd2.xml new file mode 100644 index 000000000..8689098b9 --- /dev/null +++ b/soh/assets/xml/objects/object_fd2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fhg.xml b/soh/assets/xml/objects/object_fhg.xml new file mode 100644 index 000000000..f3a4a55ca --- /dev/null +++ b/soh/assets/xml/objects/object_fhg.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fire.xml b/soh/assets/xml/objects/object_fire.xml new file mode 100644 index 000000000..49bf620cb --- /dev/null +++ b/soh/assets/xml/objects/object_fire.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_firefly.xml b/soh/assets/xml/objects/object_firefly.xml new file mode 100644 index 000000000..8c26a4341 --- /dev/null +++ b/soh/assets/xml/objects/object_firefly.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fish.xml b/soh/assets/xml/objects/object_fish.xml new file mode 100644 index 000000000..f5ad4f6da --- /dev/null +++ b/soh/assets/xml/objects/object_fish.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fr.xml b/soh/assets/xml/objects/object_fr.xml new file mode 100644 index 000000000..25ee2ad42 --- /dev/null +++ b/soh/assets/xml/objects/object_fr.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fu.xml b/soh/assets/xml/objects/object_fu.xml new file mode 100644 index 000000000..82c449e3d --- /dev/null +++ b/soh/assets/xml/objects/object_fu.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fw.xml b/soh/assets/xml/objects/object_fw.xml new file mode 100644 index 000000000..e71f83092 --- /dev/null +++ b/soh/assets/xml/objects/object_fw.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_fz.xml b/soh/assets/xml/objects/object_fz.xml new file mode 100644 index 000000000..dad6fec5c --- /dev/null +++ b/soh/assets/xml/objects/object_fz.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ganon.xml b/soh/assets/xml/objects/object_ganon.xml new file mode 100644 index 000000000..96adabb2b --- /dev/null +++ b/soh/assets/xml/objects/object_ganon.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ganon2.xml b/soh/assets/xml/objects/object_ganon2.xml new file mode 100644 index 000000000..8b66cfcc2 --- /dev/null +++ b/soh/assets/xml/objects/object_ganon2.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ganon_anime1.xml b/soh/assets/xml/objects/object_ganon_anime1.xml new file mode 100644 index 000000000..6021fdd9b --- /dev/null +++ b/soh/assets/xml/objects/object_ganon_anime1.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ganon_anime2.xml b/soh/assets/xml/objects/object_ganon_anime2.xml new file mode 100644 index 000000000..96121689e --- /dev/null +++ b/soh/assets/xml/objects/object_ganon_anime2.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ganon_anime3.xml b/soh/assets/xml/objects/object_ganon_anime3.xml new file mode 100644 index 000000000..2af1e94f9 --- /dev/null +++ b/soh/assets/xml/objects/object_ganon_anime3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ganon_objects.xml b/soh/assets/xml/objects/object_ganon_objects.xml new file mode 100644 index 000000000..5e32610ff --- /dev/null +++ b/soh/assets/xml/objects/object_ganon_objects.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_ge1.xml b/soh/assets/xml/objects/object_ge1.xml new file mode 100644 index 000000000..79e0768b8 --- /dev/null +++ b/soh/assets/xml/objects/object_ge1.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_geff.xml b/soh/assets/xml/objects/object_geff.xml new file mode 100644 index 000000000..f0667743b --- /dev/null +++ b/soh/assets/xml/objects/object_geff.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_geldb.xml b/soh/assets/xml/objects/object_geldb.xml new file mode 100644 index 000000000..a1dbd18f4 --- /dev/null +++ b/soh/assets/xml/objects/object_geldb.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_arrow.xml b/soh/assets/xml/objects/object_gi_arrow.xml new file mode 100644 index 000000000..4b58787a9 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_arrow.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_arrowcase.xml b/soh/assets/xml/objects/object_gi_arrowcase.xml new file mode 100644 index 000000000..267b0a734 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_arrowcase.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_bean.xml b/soh/assets/xml/objects/object_gi_bean.xml new file mode 100644 index 000000000..e74247816 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bean.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_bomb_1.xml b/soh/assets/xml/objects/object_gi_bomb_1.xml new file mode 100644 index 000000000..bae86c11d --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bomb_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_bomb_2.xml b/soh/assets/xml/objects/object_gi_bomb_2.xml new file mode 100644 index 000000000..1400c4a4d --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bomb_2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_bombpouch.xml b/soh/assets/xml/objects/object_gi_bombpouch.xml new file mode 100644 index 000000000..07134748c --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bombpouch.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_boomerang.xml b/soh/assets/xml/objects/object_gi_boomerang.xml new file mode 100644 index 000000000..3c1a0fe7f --- /dev/null +++ b/soh/assets/xml/objects/object_gi_boomerang.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_boots_2.xml b/soh/assets/xml/objects/object_gi_boots_2.xml new file mode 100644 index 000000000..8e9cd1028 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_boots_2.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_bosskey.xml b/soh/assets/xml/objects/object_gi_bosskey.xml new file mode 100644 index 000000000..e5e4bc86c --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bosskey.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_bottle.xml b/soh/assets/xml/objects/object_gi_bottle.xml new file mode 100644 index 000000000..3b0884a03 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bottle.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_bottle_letter.xml b/soh/assets/xml/objects/object_gi_bottle_letter.xml new file mode 100644 index 000000000..443219caf --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bottle_letter.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_bow.xml b/soh/assets/xml/objects/object_gi_bow.xml new file mode 100644 index 000000000..946e9c496 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bow.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_bracelet.xml b/soh/assets/xml/objects/object_gi_bracelet.xml new file mode 100644 index 000000000..5a0b62a60 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_bracelet.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_brokensword.xml b/soh/assets/xml/objects/object_gi_brokensword.xml new file mode 100644 index 000000000..408741ac5 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_brokensword.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_butterfly.xml b/soh/assets/xml/objects/object_gi_butterfly.xml new file mode 100644 index 000000000..76ac70609 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_butterfly.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_clothes.xml b/soh/assets/xml/objects/object_gi_clothes.xml new file mode 100644 index 000000000..2a10dce11 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_clothes.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_coin.xml b/soh/assets/xml/objects/object_gi_coin.xml new file mode 100644 index 000000000..22e29f255 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_coin.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_compass.xml b/soh/assets/xml/objects/object_gi_compass.xml new file mode 100644 index 000000000..076229b11 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_compass.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_dekupouch.xml b/soh/assets/xml/objects/object_gi_dekupouch.xml new file mode 100644 index 000000000..3d6d95d98 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_dekupouch.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_egg.xml b/soh/assets/xml/objects/object_gi_egg.xml new file mode 100644 index 000000000..2b2e2847d --- /dev/null +++ b/soh/assets/xml/objects/object_gi_egg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_eye_lotion.xml b/soh/assets/xml/objects/object_gi_eye_lotion.xml new file mode 100644 index 000000000..5128c5f6f --- /dev/null +++ b/soh/assets/xml/objects/object_gi_eye_lotion.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_fire.xml b/soh/assets/xml/objects/object_gi_fire.xml new file mode 100644 index 000000000..a57fba6e8 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_fire.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_fish.xml b/soh/assets/xml/objects/object_gi_fish.xml new file mode 100644 index 000000000..edb39dc29 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_fish.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_frog.xml b/soh/assets/xml/objects/object_gi_frog.xml new file mode 100644 index 000000000..f180fd267 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_frog.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_gerudo.xml b/soh/assets/xml/objects/object_gi_gerudo.xml new file mode 100644 index 000000000..3be8d3462 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_gerudo.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_gerudomask.xml b/soh/assets/xml/objects/object_gi_gerudomask.xml new file mode 100644 index 000000000..b53bf24af --- /dev/null +++ b/soh/assets/xml/objects/object_gi_gerudomask.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_ghost.xml b/soh/assets/xml/objects/object_gi_ghost.xml new file mode 100644 index 000000000..186f4c74b --- /dev/null +++ b/soh/assets/xml/objects/object_gi_ghost.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_glasses.xml b/soh/assets/xml/objects/object_gi_glasses.xml new file mode 100644 index 000000000..63d32ab5a --- /dev/null +++ b/soh/assets/xml/objects/object_gi_glasses.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_gloves.xml b/soh/assets/xml/objects/object_gi_gloves.xml new file mode 100644 index 000000000..1b4282dca --- /dev/null +++ b/soh/assets/xml/objects/object_gi_gloves.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_goddess.xml b/soh/assets/xml/objects/object_gi_goddess.xml new file mode 100644 index 000000000..50cbdd891 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_goddess.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_golonmask.xml b/soh/assets/xml/objects/object_gi_golonmask.xml new file mode 100644 index 000000000..98f53f1c2 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_golonmask.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_grass.xml b/soh/assets/xml/objects/object_gi_grass.xml new file mode 100644 index 000000000..ae7834201 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_grass.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_hammer.xml b/soh/assets/xml/objects/object_gi_hammer.xml new file mode 100644 index 000000000..feb5c8659 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_hammer.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_heart.xml b/soh/assets/xml/objects/object_gi_heart.xml new file mode 100644 index 000000000..48e78b1b5 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_heart.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_hearts.xml b/soh/assets/xml/objects/object_gi_hearts.xml new file mode 100644 index 000000000..d2e763e63 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_hearts.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_hookshot.xml b/soh/assets/xml/objects/object_gi_hookshot.xml new file mode 100644 index 000000000..a6b3b0fda --- /dev/null +++ b/soh/assets/xml/objects/object_gi_hookshot.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_hoverboots.xml b/soh/assets/xml/objects/object_gi_hoverboots.xml new file mode 100644 index 000000000..c4a67877b --- /dev/null +++ b/soh/assets/xml/objects/object_gi_hoverboots.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_insect.xml b/soh/assets/xml/objects/object_gi_insect.xml new file mode 100644 index 000000000..7af5b5b3b --- /dev/null +++ b/soh/assets/xml/objects/object_gi_insect.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_jewel.xml b/soh/assets/xml/objects/object_gi_jewel.xml new file mode 100644 index 000000000..e83c65116 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_jewel.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_key.xml b/soh/assets/xml/objects/object_gi_key.xml new file mode 100644 index 000000000..a37d45a71 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_key.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_ki_tan_mask.xml b/soh/assets/xml/objects/object_gi_ki_tan_mask.xml new file mode 100644 index 000000000..f66535af3 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_ki_tan_mask.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_letter.xml b/soh/assets/xml/objects/object_gi_letter.xml new file mode 100644 index 000000000..167a7ff5d --- /dev/null +++ b/soh/assets/xml/objects/object_gi_letter.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_liquid.xml b/soh/assets/xml/objects/object_gi_liquid.xml new file mode 100644 index 000000000..5eeeca0fd --- /dev/null +++ b/soh/assets/xml/objects/object_gi_liquid.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_longsword.xml b/soh/assets/xml/objects/object_gi_longsword.xml new file mode 100644 index 000000000..7161e42e3 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_longsword.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_m_arrow.xml b/soh/assets/xml/objects/object_gi_m_arrow.xml new file mode 100644 index 000000000..0fd659ae9 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_m_arrow.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_magicpot.xml b/soh/assets/xml/objects/object_gi_magicpot.xml new file mode 100644 index 000000000..7623e6353 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_magicpot.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_map.xml b/soh/assets/xml/objects/object_gi_map.xml new file mode 100644 index 000000000..8887dd22c --- /dev/null +++ b/soh/assets/xml/objects/object_gi_map.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_medal.xml b/soh/assets/xml/objects/object_gi_medal.xml new file mode 100644 index 000000000..28e5b1e68 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_medal.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_melody.xml b/soh/assets/xml/objects/object_gi_melody.xml new file mode 100644 index 000000000..b01fca953 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_melody.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_milk.xml b/soh/assets/xml/objects/object_gi_milk.xml new file mode 100644 index 000000000..00b9530d9 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_milk.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_mushroom.xml b/soh/assets/xml/objects/object_gi_mushroom.xml new file mode 100644 index 000000000..5b19bca74 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_mushroom.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_niwatori.xml b/soh/assets/xml/objects/object_gi_niwatori.xml new file mode 100644 index 000000000..713cc1ce5 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_niwatori.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_nuts.xml b/soh/assets/xml/objects/object_gi_nuts.xml new file mode 100644 index 000000000..5508fe856 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_nuts.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_ocarina.xml b/soh/assets/xml/objects/object_gi_ocarina.xml new file mode 100644 index 000000000..7693cc122 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_ocarina.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_ocarina_0.xml b/soh/assets/xml/objects/object_gi_ocarina_0.xml new file mode 100644 index 000000000..336d33894 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_ocarina_0.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_pachinko.xml b/soh/assets/xml/objects/object_gi_pachinko.xml new file mode 100644 index 000000000..5f808baf9 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_pachinko.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_powder.xml b/soh/assets/xml/objects/object_gi_powder.xml new file mode 100644 index 000000000..fb7dc0155 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_powder.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_prescription.xml b/soh/assets/xml/objects/object_gi_prescription.xml new file mode 100644 index 000000000..081de301c --- /dev/null +++ b/soh/assets/xml/objects/object_gi_prescription.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_purse.xml b/soh/assets/xml/objects/object_gi_purse.xml new file mode 100644 index 000000000..3034942ff --- /dev/null +++ b/soh/assets/xml/objects/object_gi_purse.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_rabit_mask.xml b/soh/assets/xml/objects/object_gi_rabit_mask.xml new file mode 100644 index 000000000..75f855550 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_rabit_mask.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_redead_mask.xml b/soh/assets/xml/objects/object_gi_redead_mask.xml new file mode 100644 index 000000000..7de08487f --- /dev/null +++ b/soh/assets/xml/objects/object_gi_redead_mask.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_rupy.xml b/soh/assets/xml/objects/object_gi_rupy.xml new file mode 100644 index 000000000..0a1af96d0 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_rupy.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_saw.xml b/soh/assets/xml/objects/object_gi_saw.xml new file mode 100644 index 000000000..dcc038b7c --- /dev/null +++ b/soh/assets/xml/objects/object_gi_saw.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_scale.xml b/soh/assets/xml/objects/object_gi_scale.xml new file mode 100644 index 000000000..1cf5de5b9 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_scale.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_seed.xml b/soh/assets/xml/objects/object_gi_seed.xml new file mode 100644 index 000000000..82c15c82d --- /dev/null +++ b/soh/assets/xml/objects/object_gi_seed.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_shield_1.xml b/soh/assets/xml/objects/object_gi_shield_1.xml new file mode 100644 index 000000000..2e9369569 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_shield_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_shield_2.xml b/soh/assets/xml/objects/object_gi_shield_2.xml new file mode 100644 index 000000000..176ae025f --- /dev/null +++ b/soh/assets/xml/objects/object_gi_shield_2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_shield_3.xml b/soh/assets/xml/objects/object_gi_shield_3.xml new file mode 100644 index 000000000..250bf7fea --- /dev/null +++ b/soh/assets/xml/objects/object_gi_shield_3.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_skj_mask.xml b/soh/assets/xml/objects/object_gi_skj_mask.xml new file mode 100644 index 000000000..e6c90f76f --- /dev/null +++ b/soh/assets/xml/objects/object_gi_skj_mask.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_soldout.xml b/soh/assets/xml/objects/object_gi_soldout.xml new file mode 100644 index 000000000..ae3f4685f --- /dev/null +++ b/soh/assets/xml/objects/object_gi_soldout.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_soul.xml b/soh/assets/xml/objects/object_gi_soul.xml new file mode 100644 index 000000000..76ffc5685 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_soul.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_gi_stick.xml b/soh/assets/xml/objects/object_gi_stick.xml new file mode 100644 index 000000000..fed89f465 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_stick.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_sutaru.xml b/soh/assets/xml/objects/object_gi_sutaru.xml new file mode 100644 index 000000000..a2606afbe --- /dev/null +++ b/soh/assets/xml/objects/object_gi_sutaru.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_sword_1.xml b/soh/assets/xml/objects/object_gi_sword_1.xml new file mode 100644 index 000000000..e1119abca --- /dev/null +++ b/soh/assets/xml/objects/object_gi_sword_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gi_ticketstone.xml b/soh/assets/xml/objects/object_gi_ticketstone.xml new file mode 100644 index 000000000..0fa37fe11 --- /dev/null +++ b/soh/assets/xml/objects/object_gi_ticketstone.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_truth_mask.xml b/soh/assets/xml/objects/object_gi_truth_mask.xml new file mode 100644 index 000000000..f28c7cbca --- /dev/null +++ b/soh/assets/xml/objects/object_gi_truth_mask.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gi_zoramask.xml b/soh/assets/xml/objects/object_gi_zoramask.xml new file mode 100644 index 000000000..096fdeedf --- /dev/null +++ b/soh/assets/xml/objects/object_gi_zoramask.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gj.xml b/soh/assets/xml/objects/object_gj.xml new file mode 100644 index 000000000..50d6337b4 --- /dev/null +++ b/soh/assets/xml/objects/object_gj.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gjyo_objects.xml b/soh/assets/xml/objects/object_gjyo_objects.xml new file mode 100644 index 000000000..ea4f8444f --- /dev/null +++ b/soh/assets/xml/objects/object_gjyo_objects.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_gla.xml b/soh/assets/xml/objects/object_gla.xml new file mode 100644 index 000000000..499a3720d --- /dev/null +++ b/soh/assets/xml/objects/object_gla.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gm.xml b/soh/assets/xml/objects/object_gm.xml new file mode 100644 index 000000000..91b10cc42 --- /dev/null +++ b/soh/assets/xml/objects/object_gm.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_gnd.xml b/soh/assets/xml/objects/object_gnd.xml new file mode 100644 index 000000000..dfbe02315 --- /dev/null +++ b/soh/assets/xml/objects/object_gnd.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gnd_magic.xml b/soh/assets/xml/objects/object_gnd_magic.xml new file mode 100644 index 000000000..a88d419d7 --- /dev/null +++ b/soh/assets/xml/objects/object_gnd_magic.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_gndd.xml b/soh/assets/xml/objects/object_gndd.xml new file mode 100644 index 000000000..96e264c34 --- /dev/null +++ b/soh/assets/xml/objects/object_gndd.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_god_lgt.xml b/soh/assets/xml/objects/object_god_lgt.xml new file mode 100644 index 000000000..60df9e469 --- /dev/null +++ b/soh/assets/xml/objects/object_god_lgt.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gol.xml b/soh/assets/xml/objects/object_gol.xml new file mode 100644 index 000000000..7e04508cb --- /dev/null +++ b/soh/assets/xml/objects/object_gol.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_goma.xml b/soh/assets/xml/objects/object_goma.xml new file mode 100644 index 000000000..e7a6d0a54 --- /dev/null +++ b/soh/assets/xml/objects/object_goma.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_goroiwa.xml b/soh/assets/xml/objects/object_goroiwa.xml new file mode 100644 index 000000000..5d406e957 --- /dev/null +++ b/soh/assets/xml/objects/object_goroiwa.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_gr.xml b/soh/assets/xml/objects/object_gr.xml new file mode 100644 index 000000000..103d4412e --- /dev/null +++ b/soh/assets/xml/objects/object_gr.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_gs.xml b/soh/assets/xml/objects/object_gs.xml new file mode 100644 index 000000000..145be1665 --- /dev/null +++ b/soh/assets/xml/objects/object_gs.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_gt.xml b/soh/assets/xml/objects/object_gt.xml new file mode 100644 index 000000000..1d79d4e39 --- /dev/null +++ b/soh/assets/xml/objects/object_gt.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_haka.xml b/soh/assets/xml/objects/object_haka.xml new file mode 100644 index 000000000..f6b263c37 --- /dev/null +++ b/soh/assets/xml/objects/object_haka.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_haka_door.xml b/soh/assets/xml/objects/object_haka_door.xml new file mode 100644 index 000000000..753492445 --- /dev/null +++ b/soh/assets/xml/objects/object_haka_door.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_haka_objects.xml b/soh/assets/xml/objects/object_haka_objects.xml new file mode 100644 index 000000000..3ca6d27c0 --- /dev/null +++ b/soh/assets/xml/objects/object_haka_objects.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_hakach_objects.xml b/soh/assets/xml/objects/object_hakach_objects.xml new file mode 100644 index 000000000..b978e77a3 --- /dev/null +++ b/soh/assets/xml/objects/object_hakach_objects.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_hata.xml b/soh/assets/xml/objects/object_hata.xml new file mode 100644 index 000000000..b0324c3be --- /dev/null +++ b/soh/assets/xml/objects/object_hata.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_heavy_object.xml b/soh/assets/xml/objects/object_heavy_object.xml new file mode 100644 index 000000000..ecc795321 --- /dev/null +++ b/soh/assets/xml/objects/object_heavy_object.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_hidan_objects.xml b/soh/assets/xml/objects/object_hidan_objects.xml new file mode 100644 index 000000000..5c54244bb --- /dev/null +++ b/soh/assets/xml/objects/object_hidan_objects.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_hintnuts.xml b/soh/assets/xml/objects/object_hintnuts.xml new file mode 100644 index 000000000..f419f7546 --- /dev/null +++ b/soh/assets/xml/objects/object_hintnuts.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_hni.xml b/soh/assets/xml/objects/object_hni.xml new file mode 100644 index 000000000..6b317eb7a --- /dev/null +++ b/soh/assets/xml/objects/object_hni.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_horse.xml b/soh/assets/xml/objects/object_horse.xml new file mode 100644 index 000000000..74619e597 --- /dev/null +++ b/soh/assets/xml/objects/object_horse.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_horse_ganon.xml b/soh/assets/xml/objects/object_horse_ganon.xml new file mode 100644 index 000000000..98babb080 --- /dev/null +++ b/soh/assets/xml/objects/object_horse_ganon.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_horse_link_child.xml b/soh/assets/xml/objects/object_horse_link_child.xml new file mode 100644 index 000000000..1491189a1 --- /dev/null +++ b/soh/assets/xml/objects/object_horse_link_child.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_horse_normal.xml b/soh/assets/xml/objects/object_horse_normal.xml new file mode 100644 index 000000000..2bc2cb0c6 --- /dev/null +++ b/soh/assets/xml/objects/object_horse_normal.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_horse_zelda.xml b/soh/assets/xml/objects/object_horse_zelda.xml new file mode 100644 index 000000000..9595c3245 --- /dev/null +++ b/soh/assets/xml/objects/object_horse_zelda.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_hs.xml b/soh/assets/xml/objects/object_hs.xml new file mode 100644 index 000000000..cf5d06e1a --- /dev/null +++ b/soh/assets/xml/objects/object_hs.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_human.xml b/soh/assets/xml/objects/object_human.xml new file mode 100644 index 000000000..c52398446 --- /dev/null +++ b/soh/assets/xml/objects/object_human.xml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ice_objects.xml b/soh/assets/xml/objects/object_ice_objects.xml new file mode 100644 index 000000000..a4e2e9b12 --- /dev/null +++ b/soh/assets/xml/objects/object_ice_objects.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ik.xml b/soh/assets/xml/objects/object_ik.xml new file mode 100644 index 000000000..95606e6df --- /dev/null +++ b/soh/assets/xml/objects/object_ik.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_im.xml b/soh/assets/xml/objects/object_im.xml new file mode 100644 index 000000000..a628a113a --- /dev/null +++ b/soh/assets/xml/objects/object_im.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_in.xml b/soh/assets/xml/objects/object_in.xml new file mode 100644 index 000000000..84c114047 --- /dev/null +++ b/soh/assets/xml/objects/object_in.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ingate.xml b/soh/assets/xml/objects/object_ingate.xml new file mode 100644 index 000000000..e6be42beb --- /dev/null +++ b/soh/assets/xml/objects/object_ingate.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_jj.xml b/soh/assets/xml/objects/object_jj.xml new file mode 100644 index 000000000..62bae8cb9 --- /dev/null +++ b/soh/assets/xml/objects/object_jj.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_js.xml b/soh/assets/xml/objects/object_js.xml new file mode 100644 index 000000000..ff7bd3efa --- /dev/null +++ b/soh/assets/xml/objects/object_js.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_jya_door.xml b/soh/assets/xml/objects/object_jya_door.xml new file mode 100644 index 000000000..60a7b3788 --- /dev/null +++ b/soh/assets/xml/objects/object_jya_door.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_jya_iron.xml b/soh/assets/xml/objects/object_jya_iron.xml new file mode 100644 index 000000000..c6f6773c6 --- /dev/null +++ b/soh/assets/xml/objects/object_jya_iron.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_jya_obj.xml b/soh/assets/xml/objects/object_jya_obj.xml new file mode 100644 index 000000000..4a203b0b9 --- /dev/null +++ b/soh/assets/xml/objects/object_jya_obj.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ka.xml b/soh/assets/xml/objects/object_ka.xml new file mode 100644 index 000000000..eb952732b --- /dev/null +++ b/soh/assets/xml/objects/object_ka.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_kanban.xml b/soh/assets/xml/objects/object_kanban.xml new file mode 100644 index 000000000..9ffc7a2ce --- /dev/null +++ b/soh/assets/xml/objects/object_kanban.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_kibako2.xml b/soh/assets/xml/objects/object_kibako2.xml new file mode 100644 index 000000000..a701b294d --- /dev/null +++ b/soh/assets/xml/objects/object_kibako2.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_kingdodongo.xml b/soh/assets/xml/objects/object_kingdodongo.xml new file mode 100644 index 000000000..9b104b3ea --- /dev/null +++ b/soh/assets/xml/objects/object_kingdodongo.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_km1.xml b/soh/assets/xml/objects/object_km1.xml new file mode 100644 index 000000000..0a918a798 --- /dev/null +++ b/soh/assets/xml/objects/object_km1.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_kusa.xml b/soh/assets/xml/objects/object_kusa.xml new file mode 100644 index 000000000..67291a554 --- /dev/null +++ b/soh/assets/xml/objects/object_kusa.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_kw1.xml b/soh/assets/xml/objects/object_kw1.xml new file mode 100644 index 000000000..eab561b1f --- /dev/null +++ b/soh/assets/xml/objects/object_kw1.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_kz.xml b/soh/assets/xml/objects/object_kz.xml new file mode 100644 index 000000000..20aeec4d9 --- /dev/null +++ b/soh/assets/xml/objects/object_kz.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_light_ring.xml b/soh/assets/xml/objects/object_light_ring.xml new file mode 100644 index 000000000..babe7d2aa --- /dev/null +++ b/soh/assets/xml/objects/object_light_ring.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_lightbox.xml b/soh/assets/xml/objects/object_lightbox.xml new file mode 100644 index 000000000..552a79d7c --- /dev/null +++ b/soh/assets/xml/objects/object_lightbox.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_lightswitch.xml b/soh/assets/xml/objects/object_lightswitch.xml new file mode 100644 index 000000000..cfd5dcfb4 --- /dev/null +++ b/soh/assets/xml/objects/object_lightswitch.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_link_boy.xml b/soh/assets/xml/objects/object_link_boy.xml new file mode 100644 index 000000000..2f3cd943c --- /dev/null +++ b/soh/assets/xml/objects/object_link_boy.xml @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_link_child.xml b/soh/assets/xml/objects/object_link_child.xml new file mode 100644 index 000000000..0de400373 --- /dev/null +++ b/soh/assets/xml/objects/object_link_child.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ma1.xml b/soh/assets/xml/objects/object_ma1.xml new file mode 100644 index 000000000..63eeb3895 --- /dev/null +++ b/soh/assets/xml/objects/object_ma1.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ma2.xml b/soh/assets/xml/objects/object_ma2.xml new file mode 100644 index 000000000..ce5910fb8 --- /dev/null +++ b/soh/assets/xml/objects/object_ma2.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mag.xml b/soh/assets/xml/objects/object_mag.xml new file mode 100644 index 000000000..e691fbeb3 --- /dev/null +++ b/soh/assets/xml/objects/object_mag.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mamenoki.xml b/soh/assets/xml/objects/object_mamenoki.xml new file mode 100644 index 000000000..819d89022 --- /dev/null +++ b/soh/assets/xml/objects/object_mamenoki.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mastergolon.xml b/soh/assets/xml/objects/object_mastergolon.xml new file mode 100644 index 000000000..6dd9d1659 --- /dev/null +++ b/soh/assets/xml/objects/object_mastergolon.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_masterkokiri.xml b/soh/assets/xml/objects/object_masterkokiri.xml new file mode 100644 index 000000000..af5a9ec15 --- /dev/null +++ b/soh/assets/xml/objects/object_masterkokiri.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_masterkokirihead.xml b/soh/assets/xml/objects/object_masterkokirihead.xml new file mode 100644 index 000000000..1e489323c --- /dev/null +++ b/soh/assets/xml/objects/object_masterkokirihead.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_masterzoora.xml b/soh/assets/xml/objects/object_masterzoora.xml new file mode 100644 index 000000000..2c703274e --- /dev/null +++ b/soh/assets/xml/objects/object_masterzoora.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mb.xml b/soh/assets/xml/objects/object_mb.xml new file mode 100644 index 000000000..a8471429d --- /dev/null +++ b/soh/assets/xml/objects/object_mb.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_md.xml b/soh/assets/xml/objects/object_md.xml new file mode 100644 index 000000000..0c80fb780 --- /dev/null +++ b/soh/assets/xml/objects/object_md.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_medal.xml b/soh/assets/xml/objects/object_medal.xml new file mode 100644 index 000000000..533872a55 --- /dev/null +++ b/soh/assets/xml/objects/object_medal.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_menkuri_objects.xml b/soh/assets/xml/objects/object_menkuri_objects.xml new file mode 100644 index 000000000..4c2b51df5 --- /dev/null +++ b/soh/assets/xml/objects/object_menkuri_objects.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mir_ray.xml b/soh/assets/xml/objects/object_mir_ray.xml new file mode 100644 index 000000000..2acc9391a --- /dev/null +++ b/soh/assets/xml/objects/object_mir_ray.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mizu_objects.xml b/soh/assets/xml/objects/object_mizu_objects.xml new file mode 100644 index 000000000..1abd73bc0 --- /dev/null +++ b/soh/assets/xml/objects/object_mizu_objects.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mjin.xml b/soh/assets/xml/objects/object_mjin.xml new file mode 100644 index 000000000..daf9b0541 --- /dev/null +++ b/soh/assets/xml/objects/object_mjin.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_mjin_dark.xml b/soh/assets/xml/objects/object_mjin_dark.xml new file mode 100644 index 000000000..2fa0bdbe4 --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_dark.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mjin_flame.xml b/soh/assets/xml/objects/object_mjin_flame.xml new file mode 100644 index 000000000..6274feaee --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_flame.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mjin_flash.xml b/soh/assets/xml/objects/object_mjin_flash.xml new file mode 100644 index 000000000..c1040ea11 --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_flash.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mjin_ice.xml b/soh/assets/xml/objects/object_mjin_ice.xml new file mode 100644 index 000000000..1aaeea193 --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_ice.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mjin_oka.xml b/soh/assets/xml/objects/object_mjin_oka.xml new file mode 100644 index 000000000..0f823b923 --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_oka.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_mjin_soul.xml b/soh/assets/xml/objects/object_mjin_soul.xml new file mode 100644 index 000000000..6386f7f9e --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_soul.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mjin_wind.xml b/soh/assets/xml/objects/object_mjin_wind.xml new file mode 100644 index 000000000..8bba7eb91 --- /dev/null +++ b/soh/assets/xml/objects/object_mjin_wind.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/soh/assets/xml/objects/object_mk.xml b/soh/assets/xml/objects/object_mk.xml new file mode 100644 index 000000000..0b4aaae8a --- /dev/null +++ b/soh/assets/xml/objects/object_mk.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mm.xml b/soh/assets/xml/objects/object_mm.xml new file mode 100644 index 000000000..834afe5ef --- /dev/null +++ b/soh/assets/xml/objects/object_mm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mo.xml b/soh/assets/xml/objects/object_mo.xml new file mode 100644 index 000000000..526fa94dd --- /dev/null +++ b/soh/assets/xml/objects/object_mo.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mori_hineri1.xml b/soh/assets/xml/objects/object_mori_hineri1.xml new file mode 100644 index 000000000..bb1b31e2f --- /dev/null +++ b/soh/assets/xml/objects/object_mori_hineri1.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_mori_hineri1a.xml b/soh/assets/xml/objects/object_mori_hineri1a.xml new file mode 100644 index 000000000..c750ce20d --- /dev/null +++ b/soh/assets/xml/objects/object_mori_hineri1a.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_mori_hineri2.xml b/soh/assets/xml/objects/object_mori_hineri2.xml new file mode 100644 index 000000000..785b85857 --- /dev/null +++ b/soh/assets/xml/objects/object_mori_hineri2.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_mori_hineri2a.xml b/soh/assets/xml/objects/object_mori_hineri2a.xml new file mode 100644 index 000000000..ad1bb4d5f --- /dev/null +++ b/soh/assets/xml/objects/object_mori_hineri2a.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_mori_objects.xml b/soh/assets/xml/objects/object_mori_objects.xml new file mode 100644 index 000000000..58ea8c085 --- /dev/null +++ b/soh/assets/xml/objects/object_mori_objects.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mori_tex.xml b/soh/assets/xml/objects/object_mori_tex.xml new file mode 100644 index 000000000..b1a4b803c --- /dev/null +++ b/soh/assets/xml/objects/object_mori_tex.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ms.xml b/soh/assets/xml/objects/object_ms.xml new file mode 100644 index 000000000..a87d0e6b3 --- /dev/null +++ b/soh/assets/xml/objects/object_ms.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_mu.xml b/soh/assets/xml/objects/object_mu.xml new file mode 100644 index 000000000..7251ff721 --- /dev/null +++ b/soh/assets/xml/objects/object_mu.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_nb.xml b/soh/assets/xml/objects/object_nb.xml new file mode 100644 index 000000000..74de9e285 --- /dev/null +++ b/soh/assets/xml/objects/object_nb.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_niw.xml b/soh/assets/xml/objects/object_niw.xml new file mode 100644 index 000000000..a74cbe7da --- /dev/null +++ b/soh/assets/xml/objects/object_niw.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_nwc.xml b/soh/assets/xml/objects/object_nwc.xml new file mode 100644 index 000000000..96ffa3238 --- /dev/null +++ b/soh/assets/xml/objects/object_nwc.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ny.xml b/soh/assets/xml/objects/object_ny.xml new file mode 100644 index 000000000..e2e8187ff --- /dev/null +++ b/soh/assets/xml/objects/object_ny.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA1.xml b/soh/assets/xml/objects/object_oA1.xml new file mode 100644 index 000000000..1d665f101 --- /dev/null +++ b/soh/assets/xml/objects/object_oA1.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA10.xml b/soh/assets/xml/objects/object_oA10.xml new file mode 100644 index 000000000..65d080834 --- /dev/null +++ b/soh/assets/xml/objects/object_oA10.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA11.xml b/soh/assets/xml/objects/object_oA11.xml new file mode 100644 index 000000000..92ae474b6 --- /dev/null +++ b/soh/assets/xml/objects/object_oA11.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA2.xml b/soh/assets/xml/objects/object_oA2.xml new file mode 100644 index 000000000..ffdcd7989 --- /dev/null +++ b/soh/assets/xml/objects/object_oA2.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA3.xml b/soh/assets/xml/objects/object_oA3.xml new file mode 100644 index 000000000..a492bd875 --- /dev/null +++ b/soh/assets/xml/objects/object_oA3.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA4.xml b/soh/assets/xml/objects/object_oA4.xml new file mode 100644 index 000000000..dcdbbfff9 --- /dev/null +++ b/soh/assets/xml/objects/object_oA4.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA5.xml b/soh/assets/xml/objects/object_oA5.xml new file mode 100644 index 000000000..55072176d --- /dev/null +++ b/soh/assets/xml/objects/object_oA5.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA6.xml b/soh/assets/xml/objects/object_oA6.xml new file mode 100644 index 000000000..c17a51088 --- /dev/null +++ b/soh/assets/xml/objects/object_oA6.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA7.xml b/soh/assets/xml/objects/object_oA7.xml new file mode 100644 index 000000000..52c68f639 --- /dev/null +++ b/soh/assets/xml/objects/object_oA7.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA8.xml b/soh/assets/xml/objects/object_oA8.xml new file mode 100644 index 000000000..8ed2d3df5 --- /dev/null +++ b/soh/assets/xml/objects/object_oA8.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oA9.xml b/soh/assets/xml/objects/object_oA9.xml new file mode 100644 index 000000000..d5c16abfc --- /dev/null +++ b/soh/assets/xml/objects/object_oA9.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_oB1.xml b/soh/assets/xml/objects/object_oB1.xml new file mode 100644 index 000000000..2f50c967b --- /dev/null +++ b/soh/assets/xml/objects/object_oB1.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oB2.xml b/soh/assets/xml/objects/object_oB2.xml new file mode 100644 index 000000000..230783cea --- /dev/null +++ b/soh/assets/xml/objects/object_oB2.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oB3.xml b/soh/assets/xml/objects/object_oB3.xml new file mode 100644 index 000000000..1c42ddacc --- /dev/null +++ b/soh/assets/xml/objects/object_oB3.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oB4.xml b/soh/assets/xml/objects/object_oB4.xml new file mode 100644 index 000000000..2924c9e75 --- /dev/null +++ b/soh/assets/xml/objects/object_oB4.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE1.xml b/soh/assets/xml/objects/object_oE1.xml new file mode 100644 index 000000000..4d5b1663f --- /dev/null +++ b/soh/assets/xml/objects/object_oE1.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE10.xml b/soh/assets/xml/objects/object_oE10.xml new file mode 100644 index 000000000..04ce91437 --- /dev/null +++ b/soh/assets/xml/objects/object_oE10.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE11.xml b/soh/assets/xml/objects/object_oE11.xml new file mode 100644 index 000000000..04f6f186d --- /dev/null +++ b/soh/assets/xml/objects/object_oE11.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE12.xml b/soh/assets/xml/objects/object_oE12.xml new file mode 100644 index 000000000..a5fa39fd2 --- /dev/null +++ b/soh/assets/xml/objects/object_oE12.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE1s.xml b/soh/assets/xml/objects/object_oE1s.xml new file mode 100644 index 000000000..faf6ab734 --- /dev/null +++ b/soh/assets/xml/objects/object_oE1s.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE2.xml b/soh/assets/xml/objects/object_oE2.xml new file mode 100644 index 000000000..af62ad6d1 --- /dev/null +++ b/soh/assets/xml/objects/object_oE2.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE3.xml b/soh/assets/xml/objects/object_oE3.xml new file mode 100644 index 000000000..95227da38 --- /dev/null +++ b/soh/assets/xml/objects/object_oE3.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE4.xml b/soh/assets/xml/objects/object_oE4.xml new file mode 100644 index 000000000..ae48d2ade --- /dev/null +++ b/soh/assets/xml/objects/object_oE4.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE4s.xml b/soh/assets/xml/objects/object_oE4s.xml new file mode 100644 index 000000000..584019568 --- /dev/null +++ b/soh/assets/xml/objects/object_oE4s.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE5.xml b/soh/assets/xml/objects/object_oE5.xml new file mode 100644 index 000000000..f5edf745a --- /dev/null +++ b/soh/assets/xml/objects/object_oE5.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE6.xml b/soh/assets/xml/objects/object_oE6.xml new file mode 100644 index 000000000..95bb602b6 --- /dev/null +++ b/soh/assets/xml/objects/object_oE6.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE7.xml b/soh/assets/xml/objects/object_oE7.xml new file mode 100644 index 000000000..b3e588d4c --- /dev/null +++ b/soh/assets/xml/objects/object_oE7.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE8.xml b/soh/assets/xml/objects/object_oE8.xml new file mode 100644 index 000000000..be5de2494 --- /dev/null +++ b/soh/assets/xml/objects/object_oE8.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE9.xml b/soh/assets/xml/objects/object_oE9.xml new file mode 100644 index 000000000..8ea7ee18c --- /dev/null +++ b/soh/assets/xml/objects/object_oE9.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oE_anime.xml b/soh/assets/xml/objects/object_oE_anime.xml new file mode 100644 index 000000000..f6b4659e2 --- /dev/null +++ b/soh/assets/xml/objects/object_oE_anime.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oF1d_map.xml b/soh/assets/xml/objects/object_oF1d_map.xml new file mode 100644 index 000000000..c4f3a41f8 --- /dev/null +++ b/soh/assets/xml/objects/object_oF1d_map.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_oF1s.xml b/soh/assets/xml/objects/object_oF1s.xml new file mode 100644 index 000000000..11359eff5 --- /dev/null +++ b/soh/assets/xml/objects/object_oF1s.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_o_anime.xml b/soh/assets/xml/objects/object_o_anime.xml new file mode 100644 index 000000000..4ef4ac44c --- /dev/null +++ b/soh/assets/xml/objects/object_o_anime.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_okuta.xml b/soh/assets/xml/objects/object_okuta.xml new file mode 100644 index 000000000..b4fccea6f --- /dev/null +++ b/soh/assets/xml/objects/object_okuta.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_opening_demo1.xml b/soh/assets/xml/objects/object_opening_demo1.xml new file mode 100644 index 000000000..fd0323027 --- /dev/null +++ b/soh/assets/xml/objects/object_opening_demo1.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_os.xml b/soh/assets/xml/objects/object_os.xml new file mode 100644 index 000000000..7c3029443 --- /dev/null +++ b/soh/assets/xml/objects/object_os.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_os_anime.xml b/soh/assets/xml/objects/object_os_anime.xml new file mode 100644 index 000000000..c90e37b37 --- /dev/null +++ b/soh/assets/xml/objects/object_os_anime.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ossan.xml b/soh/assets/xml/objects/object_ossan.xml new file mode 100644 index 000000000..963053f3f --- /dev/null +++ b/soh/assets/xml/objects/object_ossan.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ouke_haka.xml b/soh/assets/xml/objects/object_ouke_haka.xml new file mode 100644 index 000000000..11da9b26e --- /dev/null +++ b/soh/assets/xml/objects/object_ouke_haka.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_owl.xml b/soh/assets/xml/objects/object_owl.xml new file mode 100644 index 000000000..fdc64b3aa --- /dev/null +++ b/soh/assets/xml/objects/object_owl.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_peehat.xml b/soh/assets/xml/objects/object_peehat.xml new file mode 100644 index 000000000..42ee66cde --- /dev/null +++ b/soh/assets/xml/objects/object_peehat.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_po_composer.xml b/soh/assets/xml/objects/object_po_composer.xml new file mode 100644 index 000000000..2ab8d64d8 --- /dev/null +++ b/soh/assets/xml/objects/object_po_composer.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_po_field.xml b/soh/assets/xml/objects/object_po_field.xml new file mode 100644 index 000000000..10b402e2c --- /dev/null +++ b/soh/assets/xml/objects/object_po_field.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_po_sisters.xml b/soh/assets/xml/objects/object_po_sisters.xml new file mode 100644 index 000000000..e0c9f0834 --- /dev/null +++ b/soh/assets/xml/objects/object_po_sisters.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_poh.xml b/soh/assets/xml/objects/object_poh.xml new file mode 100644 index 000000000..009d0fca3 --- /dev/null +++ b/soh/assets/xml/objects/object_poh.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ps.xml b/soh/assets/xml/objects/object_ps.xml new file mode 100644 index 000000000..043e232d6 --- /dev/null +++ b/soh/assets/xml/objects/object_ps.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_pu_box.xml b/soh/assets/xml/objects/object_pu_box.xml new file mode 100644 index 000000000..e1d686e97 --- /dev/null +++ b/soh/assets/xml/objects/object_pu_box.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_rd.xml b/soh/assets/xml/objects/object_rd.xml new file mode 100644 index 000000000..05fa416d7 --- /dev/null +++ b/soh/assets/xml/objects/object_rd.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_reeba.xml b/soh/assets/xml/objects/object_reeba.xml new file mode 100644 index 000000000..32c697bd9 --- /dev/null +++ b/soh/assets/xml/objects/object_reeba.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_relay_objects.xml b/soh/assets/xml/objects/object_relay_objects.xml new file mode 100644 index 000000000..47b92fcd2 --- /dev/null +++ b/soh/assets/xml/objects/object_relay_objects.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_rl.xml b/soh/assets/xml/objects/object_rl.xml new file mode 100644 index 000000000..625ab83a1 --- /dev/null +++ b/soh/assets/xml/objects/object_rl.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_rr.xml b/soh/assets/xml/objects/object_rr.xml new file mode 100644 index 000000000..98726b14b --- /dev/null +++ b/soh/assets/xml/objects/object_rr.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_rs.xml b/soh/assets/xml/objects/object_rs.xml new file mode 100644 index 000000000..1da1488af --- /dev/null +++ b/soh/assets/xml/objects/object_rs.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ru1.xml b/soh/assets/xml/objects/object_ru1.xml new file mode 100644 index 000000000..3d413619f --- /dev/null +++ b/soh/assets/xml/objects/object_ru1.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ru2.xml b/soh/assets/xml/objects/object_ru2.xml new file mode 100644 index 000000000..2a8d70af3 --- /dev/null +++ b/soh/assets/xml/objects/object_ru2.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_sa.xml b/soh/assets/xml/objects/object_sa.xml new file mode 100644 index 000000000..a19446453 --- /dev/null +++ b/soh/assets/xml/objects/object_sa.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_sb.xml b/soh/assets/xml/objects/object_sb.xml new file mode 100644 index 000000000..64d3bb005 --- /dev/null +++ b/soh/assets/xml/objects/object_sb.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_sd.xml b/soh/assets/xml/objects/object_sd.xml new file mode 100644 index 000000000..c72efeae1 --- /dev/null +++ b/soh/assets/xml/objects/object_sd.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_shop_dungen.xml b/soh/assets/xml/objects/object_shop_dungen.xml new file mode 100644 index 000000000..594c43d39 --- /dev/null +++ b/soh/assets/xml/objects/object_shop_dungen.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_shopnuts.xml b/soh/assets/xml/objects/object_shopnuts.xml new file mode 100644 index 000000000..a4e12e38a --- /dev/null +++ b/soh/assets/xml/objects/object_shopnuts.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_siofuki.xml b/soh/assets/xml/objects/object_siofuki.xml new file mode 100644 index 000000000..a23240a72 --- /dev/null +++ b/soh/assets/xml/objects/object_siofuki.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_sk2.xml b/soh/assets/xml/objects/object_sk2.xml new file mode 100644 index 000000000..f2e7ef6d8 --- /dev/null +++ b/soh/assets/xml/objects/object_sk2.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_skb.xml b/soh/assets/xml/objects/object_skb.xml new file mode 100644 index 000000000..5b5efb484 --- /dev/null +++ b/soh/assets/xml/objects/object_skb.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_skj.xml b/soh/assets/xml/objects/object_skj.xml new file mode 100644 index 000000000..3abdd610f --- /dev/null +++ b/soh/assets/xml/objects/object_skj.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot00_break.xml b/soh/assets/xml/objects/object_spot00_break.xml new file mode 100644 index 000000000..d3be88315 --- /dev/null +++ b/soh/assets/xml/objects/object_spot00_break.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot00_objects.xml b/soh/assets/xml/objects/object_spot00_objects.xml new file mode 100644 index 000000000..10fb13aea --- /dev/null +++ b/soh/assets/xml/objects/object_spot00_objects.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot01_matoya.xml b/soh/assets/xml/objects/object_spot01_matoya.xml new file mode 100644 index 000000000..d44b944e0 --- /dev/null +++ b/soh/assets/xml/objects/object_spot01_matoya.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot01_matoyab.xml b/soh/assets/xml/objects/object_spot01_matoyab.xml new file mode 100644 index 000000000..720f50cd3 --- /dev/null +++ b/soh/assets/xml/objects/object_spot01_matoyab.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot01_objects.xml b/soh/assets/xml/objects/object_spot01_objects.xml new file mode 100644 index 000000000..413e032a9 --- /dev/null +++ b/soh/assets/xml/objects/object_spot01_objects.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot01_objects2.xml b/soh/assets/xml/objects/object_spot01_objects2.xml new file mode 100644 index 000000000..36741a949 --- /dev/null +++ b/soh/assets/xml/objects/object_spot01_objects2.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_spot02_objects.xml b/soh/assets/xml/objects/object_spot02_objects.xml new file mode 100644 index 000000000..65f45ff51 --- /dev/null +++ b/soh/assets/xml/objects/object_spot02_objects.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot03_object.xml b/soh/assets/xml/objects/object_spot03_object.xml new file mode 100644 index 000000000..f182e7d99 --- /dev/null +++ b/soh/assets/xml/objects/object_spot03_object.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot04_objects.xml b/soh/assets/xml/objects/object_spot04_objects.xml new file mode 100644 index 000000000..66293d752 --- /dev/null +++ b/soh/assets/xml/objects/object_spot04_objects.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot05_objects.xml b/soh/assets/xml/objects/object_spot05_objects.xml new file mode 100644 index 000000000..6e7079149 --- /dev/null +++ b/soh/assets/xml/objects/object_spot05_objects.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot06_objects.xml b/soh/assets/xml/objects/object_spot06_objects.xml new file mode 100644 index 000000000..e3da63b0f --- /dev/null +++ b/soh/assets/xml/objects/object_spot06_objects.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot07_object.xml b/soh/assets/xml/objects/object_spot07_object.xml new file mode 100644 index 000000000..5bc2bfb6b --- /dev/null +++ b/soh/assets/xml/objects/object_spot07_object.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot08_obj.xml b/soh/assets/xml/objects/object_spot08_obj.xml new file mode 100644 index 000000000..4d71f3473 --- /dev/null +++ b/soh/assets/xml/objects/object_spot08_obj.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot09_obj.xml b/soh/assets/xml/objects/object_spot09_obj.xml new file mode 100644 index 000000000..b4d9ae42a --- /dev/null +++ b/soh/assets/xml/objects/object_spot09_obj.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot11_obj.xml b/soh/assets/xml/objects/object_spot11_obj.xml new file mode 100644 index 000000000..257298f7c --- /dev/null +++ b/soh/assets/xml/objects/object_spot11_obj.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot12_obj.xml b/soh/assets/xml/objects/object_spot12_obj.xml new file mode 100644 index 000000000..d619c781e --- /dev/null +++ b/soh/assets/xml/objects/object_spot12_obj.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot15_obj.xml b/soh/assets/xml/objects/object_spot15_obj.xml new file mode 100644 index 000000000..bd59b8cd1 --- /dev/null +++ b/soh/assets/xml/objects/object_spot15_obj.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot16_obj.xml b/soh/assets/xml/objects/object_spot16_obj.xml new file mode 100644 index 000000000..d39de812a --- /dev/null +++ b/soh/assets/xml/objects/object_spot16_obj.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot17_obj.xml b/soh/assets/xml/objects/object_spot17_obj.xml new file mode 100644 index 000000000..d4239d8d2 --- /dev/null +++ b/soh/assets/xml/objects/object_spot17_obj.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_spot18_obj.xml b/soh/assets/xml/objects/object_spot18_obj.xml new file mode 100644 index 000000000..cc12b7894 --- /dev/null +++ b/soh/assets/xml/objects/object_spot18_obj.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ssh.xml b/soh/assets/xml/objects/object_ssh.xml new file mode 100644 index 000000000..6283f8e9f --- /dev/null +++ b/soh/assets/xml/objects/object_ssh.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_sst.xml b/soh/assets/xml/objects/object_sst.xml new file mode 100644 index 000000000..a80780741 --- /dev/null +++ b/soh/assets/xml/objects/object_sst.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_st.xml b/soh/assets/xml/objects/object_st.xml new file mode 100644 index 000000000..fe617cf14 --- /dev/null +++ b/soh/assets/xml/objects/object_st.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_stream.xml b/soh/assets/xml/objects/object_stream.xml new file mode 100644 index 000000000..59b0838e4 --- /dev/null +++ b/soh/assets/xml/objects/object_stream.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_syokudai.xml b/soh/assets/xml/objects/object_syokudai.xml new file mode 100644 index 000000000..c1670edec --- /dev/null +++ b/soh/assets/xml/objects/object_syokudai.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ta.xml b/soh/assets/xml/objects/object_ta.xml new file mode 100644 index 000000000..5e446bf5c --- /dev/null +++ b/soh/assets/xml/objects/object_ta.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_timeblock.xml b/soh/assets/xml/objects/object_timeblock.xml new file mode 100644 index 000000000..1d6b9e698 --- /dev/null +++ b/soh/assets/xml/objects/object_timeblock.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_tite.xml b/soh/assets/xml/objects/object_tite.xml new file mode 100644 index 000000000..1be65c495 --- /dev/null +++ b/soh/assets/xml/objects/object_tite.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_tk.xml b/soh/assets/xml/objects/object_tk.xml new file mode 100644 index 000000000..6869280e6 --- /dev/null +++ b/soh/assets/xml/objects/object_tk.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_toki_objects.xml b/soh/assets/xml/objects/object_toki_objects.xml new file mode 100644 index 000000000..cd07e60d2 --- /dev/null +++ b/soh/assets/xml/objects/object_toki_objects.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_torch2.xml b/soh/assets/xml/objects/object_torch2.xml new file mode 100644 index 000000000..ec55bbe26 --- /dev/null +++ b/soh/assets/xml/objects/object_torch2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_toryo.xml b/soh/assets/xml/objects/object_toryo.xml new file mode 100644 index 000000000..73f3dcb1e --- /dev/null +++ b/soh/assets/xml/objects/object_toryo.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_tp.xml b/soh/assets/xml/objects/object_tp.xml new file mode 100644 index 000000000..8ea908825 --- /dev/null +++ b/soh/assets/xml/objects/object_tp.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_tr.xml b/soh/assets/xml/objects/object_tr.xml new file mode 100644 index 000000000..e84d0896d --- /dev/null +++ b/soh/assets/xml/objects/object_tr.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_trap.xml b/soh/assets/xml/objects/object_trap.xml new file mode 100644 index 000000000..56195cf41 --- /dev/null +++ b/soh/assets/xml/objects/object_trap.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_triforce_spot.xml b/soh/assets/xml/objects/object_triforce_spot.xml new file mode 100644 index 000000000..a9d52dd96 --- /dev/null +++ b/soh/assets/xml/objects/object_triforce_spot.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_ts.xml b/soh/assets/xml/objects/object_ts.xml new file mode 100644 index 000000000..567277bc3 --- /dev/null +++ b/soh/assets/xml/objects/object_ts.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_tsubo.xml b/soh/assets/xml/objects/object_tsubo.xml new file mode 100644 index 000000000..ee2c5e184 --- /dev/null +++ b/soh/assets/xml/objects/object_tsubo.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_tw.xml b/soh/assets/xml/objects/object_tw.xml new file mode 100644 index 000000000..66cd3a7c2 --- /dev/null +++ b/soh/assets/xml/objects/object_tw.xml @@ -0,0 +1,332 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_umajump.xml b/soh/assets/xml/objects/object_umajump.xml new file mode 100644 index 000000000..2ee4ade46 --- /dev/null +++ b/soh/assets/xml/objects/object_umajump.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_vali.xml b/soh/assets/xml/objects/object_vali.xml new file mode 100644 index 000000000..2bab5efaa --- /dev/null +++ b/soh/assets/xml/objects/object_vali.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_vase.xml b/soh/assets/xml/objects/object_vase.xml new file mode 100644 index 000000000..091d4c51e --- /dev/null +++ b/soh/assets/xml/objects/object_vase.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/objects/object_vm.xml b/soh/assets/xml/objects/object_vm.xml new file mode 100644 index 000000000..c5b949b36 --- /dev/null +++ b/soh/assets/xml/objects/object_vm.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_wallmaster.xml b/soh/assets/xml/objects/object_wallmaster.xml new file mode 100644 index 000000000..fbbc4f4af --- /dev/null +++ b/soh/assets/xml/objects/object_wallmaster.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_warp1.xml b/soh/assets/xml/objects/object_warp1.xml new file mode 100644 index 000000000..1497d7a10 --- /dev/null +++ b/soh/assets/xml/objects/object_warp1.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_warp2.xml b/soh/assets/xml/objects/object_warp2.xml new file mode 100644 index 000000000..b6d1b3624 --- /dev/null +++ b/soh/assets/xml/objects/object_warp2.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/objects/object_wf.xml b/soh/assets/xml/objects/object_wf.xml new file mode 100644 index 000000000..ebfb708d2 --- /dev/null +++ b/soh/assets/xml/objects/object_wf.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_wood02.xml b/soh/assets/xml/objects/object_wood02.xml new file mode 100644 index 000000000..47ec342ca --- /dev/null +++ b/soh/assets/xml/objects/object_wood02.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_xc.xml b/soh/assets/xml/objects/object_xc.xml new file mode 100644 index 000000000..030ae9394 --- /dev/null +++ b/soh/assets/xml/objects/object_xc.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_yabusame_point.xml b/soh/assets/xml/objects/object_yabusame_point.xml new file mode 100644 index 000000000..bed9e8a60 --- /dev/null +++ b/soh/assets/xml/objects/object_yabusame_point.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_ydan_objects.xml b/soh/assets/xml/objects/object_ydan_objects.xml new file mode 100644 index 000000000..e2adf307f --- /dev/null +++ b/soh/assets/xml/objects/object_ydan_objects.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_yukabyun.xml b/soh/assets/xml/objects/object_yukabyun.xml new file mode 100644 index 000000000..d594ebbb8 --- /dev/null +++ b/soh/assets/xml/objects/object_yukabyun.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zf.xml b/soh/assets/xml/objects/object_zf.xml new file mode 100644 index 000000000..7f20eb786 --- /dev/null +++ b/soh/assets/xml/objects/object_zf.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zg.xml b/soh/assets/xml/objects/object_zg.xml new file mode 100644 index 000000000..79005516c --- /dev/null +++ b/soh/assets/xml/objects/object_zg.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/objects/object_zl1.xml b/soh/assets/xml/objects/object_zl1.xml new file mode 100644 index 000000000..c7082a3db --- /dev/null +++ b/soh/assets/xml/objects/object_zl1.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zl2.xml b/soh/assets/xml/objects/object_zl2.xml new file mode 100644 index 000000000..9af0a6527 --- /dev/null +++ b/soh/assets/xml/objects/object_zl2.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zl2_anime1.xml b/soh/assets/xml/objects/object_zl2_anime1.xml new file mode 100644 index 000000000..cada50486 --- /dev/null +++ b/soh/assets/xml/objects/object_zl2_anime1.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zl2_anime2.xml b/soh/assets/xml/objects/object_zl2_anime2.xml new file mode 100644 index 000000000..47016e2e0 --- /dev/null +++ b/soh/assets/xml/objects/object_zl2_anime2.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zl4.xml b/soh/assets/xml/objects/object_zl4.xml new file mode 100644 index 000000000..b43e5ba34 --- /dev/null +++ b/soh/assets/xml/objects/object_zl4.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/objects/object_zo.xml b/soh/assets/xml/objects/object_zo.xml new file mode 100644 index 000000000..77465760e --- /dev/null +++ b/soh/assets/xml/objects/object_zo.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Arrow_Fire.xml b/soh/assets/xml/overlays/ovl_Arrow_Fire.xml new file mode 100644 index 000000000..175acfb26 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Arrow_Fire.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Arrow_Ice.xml b/soh/assets/xml/overlays/ovl_Arrow_Ice.xml new file mode 100644 index 000000000..3989b1976 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Arrow_Ice.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Arrow_Light.xml b/soh/assets/xml/overlays/ovl_Arrow_Light.xml new file mode 100644 index 000000000..5bc88833a --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Arrow_Light.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Bg_Ganon_Otyuka.xml b/soh/assets/xml/overlays/ovl_Bg_Ganon_Otyuka.xml new file mode 100644 index 000000000..ed3b3bd01 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Bg_Ganon_Otyuka.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Bg_Jya_Cobra.xml b/soh/assets/xml/overlays/ovl_Bg_Jya_Cobra.xml new file mode 100644 index 000000000..d5157149e --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Bg_Jya_Cobra.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Boss_Dodongo.xml b/soh/assets/xml/overlays/ovl_Boss_Dodongo.xml new file mode 100644 index 000000000..f6a52f044 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Boss_Dodongo.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Boss_Ganon.xml b/soh/assets/xml/overlays/ovl_Boss_Ganon.xml new file mode 100644 index 000000000..638dd6f95 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Boss_Ganon.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Boss_Ganon2.xml b/soh/assets/xml/overlays/ovl_Boss_Ganon2.xml new file mode 100644 index 000000000..fd25d4e51 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Boss_Ganon2.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Boss_Sst.xml b/soh/assets/xml/overlays/ovl_Boss_Sst.xml new file mode 100644 index 000000000..3a5f81f7d --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Boss_Sst.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Demo_Shd.xml b/soh/assets/xml/overlays/ovl_Demo_Shd.xml new file mode 100644 index 000000000..3f40a3688 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Demo_Shd.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Elf_Msg.xml b/soh/assets/xml/overlays/ovl_Elf_Msg.xml new file mode 100644 index 000000000..f028ef989 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Elf_Msg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Elf_Msg2.xml b/soh/assets/xml/overlays/ovl_Elf_Msg2.xml new file mode 100644 index 000000000..143253150 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Elf_Msg2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Bili.xml b/soh/assets/xml/overlays/ovl_En_Bili.xml new file mode 100644 index 000000000..0b98f70fe --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Bili.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Clear_Tag.xml b/soh/assets/xml/overlays/ovl_En_Clear_Tag.xml new file mode 100644 index 000000000..36c893f3b --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Clear_Tag.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Ganon_Mant.xml b/soh/assets/xml/overlays/ovl_En_Ganon_Mant.xml new file mode 100644 index 000000000..a279dbfbc --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Ganon_Mant.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Ganon_Organ.xml b/soh/assets/xml/overlays/ovl_En_Ganon_Organ.xml new file mode 100644 index 000000000..cee8ae38d --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Ganon_Organ.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Holl.xml b/soh/assets/xml/overlays/ovl_En_Holl.xml new file mode 100644 index 000000000..af4cc290b --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Holl.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Jsjutan.xml b/soh/assets/xml/overlays/ovl_En_Jsjutan.xml new file mode 100644 index 000000000..4a40bf56d --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Jsjutan.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Kanban.xml b/soh/assets/xml/overlays/ovl_En_Kanban.xml new file mode 100644 index 000000000..d73ef9d2e --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Kanban.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Sda.xml b/soh/assets/xml/overlays/ovl_En_Sda.xml new file mode 100644 index 000000000..c4293e92b --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Sda.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Ssh.xml b/soh/assets/xml/overlays/ovl_En_Ssh.xml new file mode 100644 index 000000000..5467eed62 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Ssh.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_St.xml b/soh/assets/xml/overlays/ovl_En_St.xml new file mode 100644 index 000000000..0252a1387 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_St.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_En_Sth.xml b/soh/assets/xml/overlays/ovl_En_Sth.xml new file mode 100644 index 000000000..ccc45fb1c --- /dev/null +++ b/soh/assets/xml/overlays/ovl_En_Sth.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_End_Title.xml b/soh/assets/xml/overlays/ovl_End_Title.xml new file mode 100644 index 000000000..82dceca25 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_End_Title.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_File_Choose.xml b/soh/assets/xml/overlays/ovl_File_Choose.xml new file mode 100644 index 000000000..8bebff602 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_File_Choose.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Magic_Dark.xml b/soh/assets/xml/overlays/ovl_Magic_Dark.xml new file mode 100644 index 000000000..cd8d45592 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Magic_Dark.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Magic_Fire.xml b/soh/assets/xml/overlays/ovl_Magic_Fire.xml new file mode 100644 index 000000000..696bdc0dc --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Magic_Fire.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Magic_Wind.xml b/soh/assets/xml/overlays/ovl_Magic_Wind.xml new file mode 100644 index 000000000..8fc20ce1b --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Magic_Wind.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Oceff_Spot.xml b/soh/assets/xml/overlays/ovl_Oceff_Spot.xml new file mode 100644 index 000000000..9a53952f0 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Oceff_Spot.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Oceff_Storm.xml b/soh/assets/xml/overlays/ovl_Oceff_Storm.xml new file mode 100644 index 000000000..a46b4330c --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Oceff_Storm.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Oceff_Wipe.xml b/soh/assets/xml/overlays/ovl_Oceff_Wipe.xml new file mode 100644 index 000000000..52e29f1bc --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Oceff_Wipe.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Oceff_Wipe2.xml b/soh/assets/xml/overlays/ovl_Oceff_Wipe2.xml new file mode 100644 index 000000000..b42c72a66 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Oceff_Wipe2.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Oceff_Wipe3.xml b/soh/assets/xml/overlays/ovl_Oceff_Wipe3.xml new file mode 100644 index 000000000..11f278866 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Oceff_Wipe3.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/overlays/ovl_Oceff_Wipe4.xml b/soh/assets/xml/overlays/ovl_Oceff_Wipe4.xml new file mode 100644 index 000000000..e0b4cd7b6 --- /dev/null +++ b/soh/assets/xml/overlays/ovl_Oceff_Wipe4.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/Bmori1.xml b/soh/assets/xml/scenes/dungeons/Bmori1.xml new file mode 100644 index 000000000..70aac3464 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/Bmori1.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/FIRE_bs.xml b/soh/assets/xml/scenes/dungeons/FIRE_bs.xml new file mode 100644 index 000000000..9e65ba617 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/FIRE_bs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/HAKAdan.xml b/soh/assets/xml/scenes/dungeons/HAKAdan.xml new file mode 100644 index 000000000..cc2935a1c --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/HAKAdan.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/HAKAdanCH.xml b/soh/assets/xml/scenes/dungeons/HAKAdanCH.xml new file mode 100644 index 000000000..eb55a19f5 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/HAKAdanCH.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/HAKAdan_bs.xml b/soh/assets/xml/scenes/dungeons/HAKAdan_bs.xml new file mode 100644 index 000000000..d3280ffe6 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/HAKAdan_bs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/HIDAN.xml b/soh/assets/xml/scenes/dungeons/HIDAN.xml new file mode 100644 index 000000000..dc4d56849 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/HIDAN.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/MIZUsin.xml b/soh/assets/xml/scenes/dungeons/MIZUsin.xml new file mode 100644 index 000000000..beb765c85 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/MIZUsin.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/MIZUsin_bs.xml b/soh/assets/xml/scenes/dungeons/MIZUsin_bs.xml new file mode 100644 index 000000000..b64089530 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/MIZUsin_bs.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/bdan.xml b/soh/assets/xml/scenes/dungeons/bdan.xml new file mode 100644 index 000000000..c4edc9bea --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/bdan.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/bdan_boss.xml b/soh/assets/xml/scenes/dungeons/bdan_boss.xml new file mode 100644 index 000000000..12d8d3e12 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/bdan_boss.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ddan.xml b/soh/assets/xml/scenes/dungeons/ddan.xml new file mode 100644 index 000000000..f6572b9f8 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ddan.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ddan_boss.xml b/soh/assets/xml/scenes/dungeons/ddan_boss.xml new file mode 100644 index 000000000..03eb73ea3 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ddan_boss.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganon.xml b/soh/assets/xml/scenes/dungeons/ganon.xml new file mode 100644 index 000000000..0582da79f --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganon.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganon_boss.xml b/soh/assets/xml/scenes/dungeons/ganon_boss.xml new file mode 100644 index 000000000..c75914c29 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganon_boss.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganon_demo.xml b/soh/assets/xml/scenes/dungeons/ganon_demo.xml new file mode 100644 index 000000000..779cb9335 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganon_demo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganon_final.xml b/soh/assets/xml/scenes/dungeons/ganon_final.xml new file mode 100644 index 000000000..afe1cffdf --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganon_final.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganon_sonogo.xml b/soh/assets/xml/scenes/dungeons/ganon_sonogo.xml new file mode 100644 index 000000000..70313c8cf --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganon_sonogo.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganon_tou.xml b/soh/assets/xml/scenes/dungeons/ganon_tou.xml new file mode 100644 index 000000000..6642a8902 --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganon_tou.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganontika.xml b/soh/assets/xml/scenes/dungeons/ganontika.xml new file mode 100644 index 000000000..de24b26ff --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganontika.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ganontikasonogo.xml b/soh/assets/xml/scenes/dungeons/ganontikasonogo.xml new file mode 100644 index 000000000..60020188f --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ganontikasonogo.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/gerudoway.xml b/soh/assets/xml/scenes/dungeons/gerudoway.xml new file mode 100644 index 000000000..531724ead --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/gerudoway.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ice_doukutu.xml b/soh/assets/xml/scenes/dungeons/ice_doukutu.xml new file mode 100644 index 000000000..4d25aa74f --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ice_doukutu.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/jyasinboss.xml b/soh/assets/xml/scenes/dungeons/jyasinboss.xml new file mode 100644 index 000000000..0f81c26fb --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/jyasinboss.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/jyasinzou.xml b/soh/assets/xml/scenes/dungeons/jyasinzou.xml new file mode 100644 index 000000000..ca5d0024a --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/jyasinzou.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/men.xml b/soh/assets/xml/scenes/dungeons/men.xml new file mode 100644 index 000000000..acba3e7cb --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/men.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/moribossroom.xml b/soh/assets/xml/scenes/dungeons/moribossroom.xml new file mode 100644 index 000000000..9c4552e9e --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/moribossroom.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ydan.xml b/soh/assets/xml/scenes/dungeons/ydan.xml new file mode 100644 index 000000000..465c5f0ca --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ydan.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/dungeons/ydan_boss.xml b/soh/assets/xml/scenes/dungeons/ydan_boss.xml new file mode 100644 index 000000000..f4971e7fe --- /dev/null +++ b/soh/assets/xml/scenes/dungeons/ydan_boss.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/bowling.xml b/soh/assets/xml/scenes/indoors/bowling.xml new file mode 100644 index 000000000..e12fd269a --- /dev/null +++ b/soh/assets/xml/scenes/indoors/bowling.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/daiyousei_izumi.xml b/soh/assets/xml/scenes/indoors/daiyousei_izumi.xml new file mode 100644 index 000000000..3c1fafb8d --- /dev/null +++ b/soh/assets/xml/scenes/indoors/daiyousei_izumi.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/hairal_niwa.xml b/soh/assets/xml/scenes/indoors/hairal_niwa.xml new file mode 100644 index 000000000..04547ab21 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/hairal_niwa.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/hairal_niwa2.xml b/soh/assets/xml/scenes/indoors/hairal_niwa2.xml new file mode 100644 index 000000000..4cc41acec --- /dev/null +++ b/soh/assets/xml/scenes/indoors/hairal_niwa2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/hairal_niwa_n.xml b/soh/assets/xml/scenes/indoors/hairal_niwa_n.xml new file mode 100644 index 000000000..a33d77b07 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/hairal_niwa_n.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/hakasitarelay.xml b/soh/assets/xml/scenes/indoors/hakasitarelay.xml new file mode 100644 index 000000000..bc1e2b966 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/hakasitarelay.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/hut.xml b/soh/assets/xml/scenes/indoors/hut.xml new file mode 100644 index 000000000..a4ef5715f --- /dev/null +++ b/soh/assets/xml/scenes/indoors/hut.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/hylia_labo.xml b/soh/assets/xml/scenes/indoors/hylia_labo.xml new file mode 100644 index 000000000..829708604 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/hylia_labo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/impa.xml b/soh/assets/xml/scenes/indoors/impa.xml new file mode 100644 index 000000000..db757e3d3 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/impa.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/kakariko.xml b/soh/assets/xml/scenes/indoors/kakariko.xml new file mode 100644 index 000000000..e4e9b102c --- /dev/null +++ b/soh/assets/xml/scenes/indoors/kakariko.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/kenjyanoma.xml b/soh/assets/xml/scenes/indoors/kenjyanoma.xml new file mode 100644 index 000000000..e6bcef90a --- /dev/null +++ b/soh/assets/xml/scenes/indoors/kenjyanoma.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/kokiri_home.xml b/soh/assets/xml/scenes/indoors/kokiri_home.xml new file mode 100644 index 000000000..8c0305493 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/kokiri_home.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/kokiri_home3.xml b/soh/assets/xml/scenes/indoors/kokiri_home3.xml new file mode 100644 index 000000000..9f36eb41c --- /dev/null +++ b/soh/assets/xml/scenes/indoors/kokiri_home3.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/kokiri_home4.xml b/soh/assets/xml/scenes/indoors/kokiri_home4.xml new file mode 100644 index 000000000..eb861aedb --- /dev/null +++ b/soh/assets/xml/scenes/indoors/kokiri_home4.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/kokiri_home5.xml b/soh/assets/xml/scenes/indoors/kokiri_home5.xml new file mode 100644 index 000000000..974987fed --- /dev/null +++ b/soh/assets/xml/scenes/indoors/kokiri_home5.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/labo.xml b/soh/assets/xml/scenes/indoors/labo.xml new file mode 100644 index 000000000..c0a9d2eb8 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/labo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/link_home.xml b/soh/assets/xml/scenes/indoors/link_home.xml new file mode 100644 index 000000000..2fb88ae34 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/link_home.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/mahouya.xml b/soh/assets/xml/scenes/indoors/mahouya.xml new file mode 100644 index 000000000..653cffc3b --- /dev/null +++ b/soh/assets/xml/scenes/indoors/mahouya.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/malon_stable.xml b/soh/assets/xml/scenes/indoors/malon_stable.xml new file mode 100644 index 000000000..aff97eeb8 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/malon_stable.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/miharigoya.xml b/soh/assets/xml/scenes/indoors/miharigoya.xml new file mode 100644 index 000000000..41ff2db9f --- /dev/null +++ b/soh/assets/xml/scenes/indoors/miharigoya.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/nakaniwa.xml b/soh/assets/xml/scenes/indoors/nakaniwa.xml new file mode 100644 index 000000000..49ff4a29c --- /dev/null +++ b/soh/assets/xml/scenes/indoors/nakaniwa.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/syatekijyou.xml b/soh/assets/xml/scenes/indoors/syatekijyou.xml new file mode 100644 index 000000000..5490e84f1 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/syatekijyou.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/takaraya.xml b/soh/assets/xml/scenes/indoors/takaraya.xml new file mode 100644 index 000000000..ddee91543 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/takaraya.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/tent.xml b/soh/assets/xml/scenes/indoors/tent.xml new file mode 100644 index 000000000..de72cc5f4 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/tent.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/tokinoma.xml b/soh/assets/xml/scenes/indoors/tokinoma.xml new file mode 100644 index 000000000..e43f0a933 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/tokinoma.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/yousei_izumi_tate.xml b/soh/assets/xml/scenes/indoors/yousei_izumi_tate.xml new file mode 100644 index 000000000..990e1763b --- /dev/null +++ b/soh/assets/xml/scenes/indoors/yousei_izumi_tate.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/indoors/yousei_izumi_yoko.xml b/soh/assets/xml/scenes/indoors/yousei_izumi_yoko.xml new file mode 100644 index 000000000..98923c569 --- /dev/null +++ b/soh/assets/xml/scenes/indoors/yousei_izumi_yoko.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/enrui.xml b/soh/assets/xml/scenes/misc/enrui.xml new file mode 100644 index 000000000..d8c06e545 --- /dev/null +++ b/soh/assets/xml/scenes/misc/enrui.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/entra_n.xml b/soh/assets/xml/scenes/misc/entra_n.xml new file mode 100644 index 000000000..5b408ac3a --- /dev/null +++ b/soh/assets/xml/scenes/misc/entra_n.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/hakaana.xml b/soh/assets/xml/scenes/misc/hakaana.xml new file mode 100644 index 000000000..b13273bed --- /dev/null +++ b/soh/assets/xml/scenes/misc/hakaana.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/hakaana2.xml b/soh/assets/xml/scenes/misc/hakaana2.xml new file mode 100644 index 000000000..146d0ce45 --- /dev/null +++ b/soh/assets/xml/scenes/misc/hakaana2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/hakaana_ouke.xml b/soh/assets/xml/scenes/misc/hakaana_ouke.xml new file mode 100644 index 000000000..1d31d66a4 --- /dev/null +++ b/soh/assets/xml/scenes/misc/hakaana_ouke.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/hiral_demo.xml b/soh/assets/xml/scenes/misc/hiral_demo.xml new file mode 100644 index 000000000..1331fdd4c --- /dev/null +++ b/soh/assets/xml/scenes/misc/hiral_demo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/kakariko3.xml b/soh/assets/xml/scenes/misc/kakariko3.xml new file mode 100644 index 000000000..07b62c44d --- /dev/null +++ b/soh/assets/xml/scenes/misc/kakariko3.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/kakusiana.xml b/soh/assets/xml/scenes/misc/kakusiana.xml new file mode 100644 index 000000000..e4254b750 --- /dev/null +++ b/soh/assets/xml/scenes/misc/kakusiana.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/kinsuta.xml b/soh/assets/xml/scenes/misc/kinsuta.xml new file mode 100644 index 000000000..d5dbacdb7 --- /dev/null +++ b/soh/assets/xml/scenes/misc/kinsuta.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/market_alley.xml b/soh/assets/xml/scenes/misc/market_alley.xml new file mode 100644 index 000000000..c30467303 --- /dev/null +++ b/soh/assets/xml/scenes/misc/market_alley.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/market_alley_n.xml b/soh/assets/xml/scenes/misc/market_alley_n.xml new file mode 100644 index 000000000..65db60411 --- /dev/null +++ b/soh/assets/xml/scenes/misc/market_alley_n.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/market_day.xml b/soh/assets/xml/scenes/misc/market_day.xml new file mode 100644 index 000000000..00887e69c --- /dev/null +++ b/soh/assets/xml/scenes/misc/market_day.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/market_night.xml b/soh/assets/xml/scenes/misc/market_night.xml new file mode 100644 index 000000000..c58e38744 --- /dev/null +++ b/soh/assets/xml/scenes/misc/market_night.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/market_ruins.xml b/soh/assets/xml/scenes/misc/market_ruins.xml new file mode 100644 index 000000000..f0ec75e75 --- /dev/null +++ b/soh/assets/xml/scenes/misc/market_ruins.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/shrine.xml b/soh/assets/xml/scenes/misc/shrine.xml new file mode 100644 index 000000000..b9ddf2cf7 --- /dev/null +++ b/soh/assets/xml/scenes/misc/shrine.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/shrine_n.xml b/soh/assets/xml/scenes/misc/shrine_n.xml new file mode 100644 index 000000000..4878338aa --- /dev/null +++ b/soh/assets/xml/scenes/misc/shrine_n.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/shrine_r.xml b/soh/assets/xml/scenes/misc/shrine_r.xml new file mode 100644 index 000000000..122036791 --- /dev/null +++ b/soh/assets/xml/scenes/misc/shrine_r.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/misc/turibori.xml b/soh/assets/xml/scenes/misc/turibori.xml new file mode 100644 index 000000000..ef6848edf --- /dev/null +++ b/soh/assets/xml/scenes/misc/turibori.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/entra.xml b/soh/assets/xml/scenes/overworld/entra.xml new file mode 100644 index 000000000..92ee57f59 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/entra.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/souko.xml b/soh/assets/xml/scenes/overworld/souko.xml new file mode 100644 index 000000000..508beb85d --- /dev/null +++ b/soh/assets/xml/scenes/overworld/souko.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot00.xml b/soh/assets/xml/scenes/overworld/spot00.xml new file mode 100644 index 000000000..4e933910c --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot00.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot01.xml b/soh/assets/xml/scenes/overworld/spot01.xml new file mode 100644 index 000000000..e2da37e2d --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot01.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot02.xml b/soh/assets/xml/scenes/overworld/spot02.xml new file mode 100644 index 000000000..334d0e599 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot02.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot03.xml b/soh/assets/xml/scenes/overworld/spot03.xml new file mode 100644 index 000000000..bf4039249 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot03.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot04.xml b/soh/assets/xml/scenes/overworld/spot04.xml new file mode 100644 index 000000000..c5824727f --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot04.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot05.xml b/soh/assets/xml/scenes/overworld/spot05.xml new file mode 100644 index 000000000..7c2c012bf --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot05.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot06.xml b/soh/assets/xml/scenes/overworld/spot06.xml new file mode 100644 index 000000000..c369533bb --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot06.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot07.xml b/soh/assets/xml/scenes/overworld/spot07.xml new file mode 100644 index 000000000..e083e336d --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot07.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot08.xml b/soh/assets/xml/scenes/overworld/spot08.xml new file mode 100644 index 000000000..136727c6d --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot08.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot09.xml b/soh/assets/xml/scenes/overworld/spot09.xml new file mode 100644 index 000000000..175fac890 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot09.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot10.xml b/soh/assets/xml/scenes/overworld/spot10.xml new file mode 100644 index 000000000..06349cf1e --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot10.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot11.xml b/soh/assets/xml/scenes/overworld/spot11.xml new file mode 100644 index 000000000..99bf24c82 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot11.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot12.xml b/soh/assets/xml/scenes/overworld/spot12.xml new file mode 100644 index 000000000..77b0cfb07 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot12.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot13.xml b/soh/assets/xml/scenes/overworld/spot13.xml new file mode 100644 index 000000000..5686bbde5 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot13.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot15.xml b/soh/assets/xml/scenes/overworld/spot15.xml new file mode 100644 index 000000000..f17e76d55 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot15.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot16.xml b/soh/assets/xml/scenes/overworld/spot16.xml new file mode 100644 index 000000000..026da9fd2 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot16.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot17.xml b/soh/assets/xml/scenes/overworld/spot17.xml new file mode 100644 index 000000000..3aecc15a6 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot17.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot18.xml b/soh/assets/xml/scenes/overworld/spot18.xml new file mode 100644 index 000000000..513e67cbc --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot18.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/overworld/spot20.xml b/soh/assets/xml/scenes/overworld/spot20.xml new file mode 100644 index 000000000..b75f3f5c0 --- /dev/null +++ b/soh/assets/xml/scenes/overworld/spot20.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/alley_shop.xml b/soh/assets/xml/scenes/shops/alley_shop.xml new file mode 100644 index 000000000..95548de1b --- /dev/null +++ b/soh/assets/xml/scenes/shops/alley_shop.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/drag.xml b/soh/assets/xml/scenes/shops/drag.xml new file mode 100644 index 000000000..acb6515fb --- /dev/null +++ b/soh/assets/xml/scenes/shops/drag.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/face_shop.xml b/soh/assets/xml/scenes/shops/face_shop.xml new file mode 100644 index 000000000..16a974608 --- /dev/null +++ b/soh/assets/xml/scenes/shops/face_shop.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/golon.xml b/soh/assets/xml/scenes/shops/golon.xml new file mode 100644 index 000000000..c58672798 --- /dev/null +++ b/soh/assets/xml/scenes/shops/golon.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/kokiri_shop.xml b/soh/assets/xml/scenes/shops/kokiri_shop.xml new file mode 100644 index 000000000..50cd633e5 --- /dev/null +++ b/soh/assets/xml/scenes/shops/kokiri_shop.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/night_shop.xml b/soh/assets/xml/scenes/shops/night_shop.xml new file mode 100644 index 000000000..5954d9c83 --- /dev/null +++ b/soh/assets/xml/scenes/shops/night_shop.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/shop1.xml b/soh/assets/xml/scenes/shops/shop1.xml new file mode 100644 index 000000000..525a06d63 --- /dev/null +++ b/soh/assets/xml/scenes/shops/shop1.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/shops/zoora.xml b/soh/assets/xml/scenes/shops/zoora.xml new file mode 100644 index 000000000..0e4ac18f8 --- /dev/null +++ b/soh/assets/xml/scenes/shops/zoora.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/besitu.xml b/soh/assets/xml/scenes/test_levels/besitu.xml new file mode 100644 index 000000000..e80f79299 --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/besitu.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/depth_test.xml b/soh/assets/xml/scenes/test_levels/depth_test.xml new file mode 100644 index 000000000..0eafb27d9 --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/depth_test.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/sasatest.xml b/soh/assets/xml/scenes/test_levels/sasatest.xml new file mode 100644 index 000000000..54d53311e --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/sasatest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/sutaru.xml b/soh/assets/xml/scenes/test_levels/sutaru.xml new file mode 100644 index 000000000..7c3e822d8 --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/sutaru.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/syotes.xml b/soh/assets/xml/scenes/test_levels/syotes.xml new file mode 100644 index 000000000..3a7b594d1 --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/syotes.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/syotes2.xml b/soh/assets/xml/scenes/test_levels/syotes2.xml new file mode 100644 index 000000000..2dc61577f --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/syotes2.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/test01.xml b/soh/assets/xml/scenes/test_levels/test01.xml new file mode 100644 index 000000000..e12e79ac1 --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/test01.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/scenes/test_levels/testroom.xml b/soh/assets/xml/scenes/test_levels/testroom.xml new file mode 100644 index 000000000..da575355c --- /dev/null +++ b/soh/assets/xml/scenes/test_levels/testroom.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/text/elf_message_field.xml b/soh/assets/xml/text/elf_message_field.xml new file mode 100644 index 000000000..789a554ce --- /dev/null +++ b/soh/assets/xml/text/elf_message_field.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/soh/assets/xml/text/elf_message_ydan.xml b/soh/assets/xml/text/elf_message_ydan.xml new file mode 100644 index 000000000..f784afcf4 --- /dev/null +++ b/soh/assets/xml/text/elf_message_ydan.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/soh/assets/xml/text/nes_message_data_static.xml b/soh/assets/xml/text/nes_message_data_static.xml new file mode 100644 index 000000000..d7c0559c5 --- /dev/null +++ b/soh/assets/xml/text/nes_message_data_static.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/soh/assets/xml/text/staff_message_data_static.xml b/soh/assets/xml/text/staff_message_data_static.xml new file mode 100644 index 000000000..447b91ccd --- /dev/null +++ b/soh/assets/xml/text/staff_message_data_static.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/soh/assets/xml/textures/backgrounds.xml b/soh/assets/xml/textures/backgrounds.xml new file mode 100644 index 000000000..474734e71 --- /dev/null +++ b/soh/assets/xml/textures/backgrounds.xml @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/soh/assets/xml/textures/do_action_static.xml b/soh/assets/xml/textures/do_action_static.xml new file mode 100644 index 000000000..9ae8d9815 --- /dev/null +++ b/soh/assets/xml/textures/do_action_static.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_24_static.xml b/soh/assets/xml/textures/icon_item_24_static.xml new file mode 100644 index 000000000..0c5f7621e --- /dev/null +++ b/soh/assets/xml/textures/icon_item_24_static.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_dungeon_static.xml b/soh/assets/xml/textures/icon_item_dungeon_static.xml new file mode 100644 index 000000000..873c490fc --- /dev/null +++ b/soh/assets/xml/textures/icon_item_dungeon_static.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_field_static.xml b/soh/assets/xml/textures/icon_item_field_static.xml new file mode 100644 index 000000000..ed991406b --- /dev/null +++ b/soh/assets/xml/textures/icon_item_field_static.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_fra_static.xml b/soh/assets/xml/textures/icon_item_fra_static.xml new file mode 100644 index 000000000..2c8ccad5b --- /dev/null +++ b/soh/assets/xml/textures/icon_item_fra_static.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_gameover_static.xml b/soh/assets/xml/textures/icon_item_gameover_static.xml new file mode 100644 index 000000000..fc2a50642 --- /dev/null +++ b/soh/assets/xml/textures/icon_item_gameover_static.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_ger_static.xml b/soh/assets/xml/textures/icon_item_ger_static.xml new file mode 100644 index 000000000..ea71dda35 --- /dev/null +++ b/soh/assets/xml/textures/icon_item_ger_static.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_nes_static.xml b/soh/assets/xml/textures/icon_item_nes_static.xml new file mode 100644 index 000000000..6cd92acfc --- /dev/null +++ b/soh/assets/xml/textures/icon_item_nes_static.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/icon_item_static.xml b/soh/assets/xml/textures/icon_item_static.xml new file mode 100644 index 000000000..7b61cf35c --- /dev/null +++ b/soh/assets/xml/textures/icon_item_static.xml @@ -0,0 +1,190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/item_name_static.xml b/soh/assets/xml/textures/item_name_static.xml new file mode 100644 index 000000000..00c497490 --- /dev/null +++ b/soh/assets/xml/textures/item_name_static.xml @@ -0,0 +1,373 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/map_48x85_static.xml b/soh/assets/xml/textures/map_48x85_static.xml new file mode 100644 index 000000000..b5b1d1a85 --- /dev/null +++ b/soh/assets/xml/textures/map_48x85_static.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/map_grand_static.xml b/soh/assets/xml/textures/map_grand_static.xml new file mode 100644 index 000000000..201955bfc --- /dev/null +++ b/soh/assets/xml/textures/map_grand_static.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/map_i_static.xml b/soh/assets/xml/textures/map_i_static.xml new file mode 100644 index 000000000..1721f6d84 --- /dev/null +++ b/soh/assets/xml/textures/map_i_static.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/map_name_static.xml b/soh/assets/xml/textures/map_name_static.xml new file mode 100644 index 000000000..7f9d31589 --- /dev/null +++ b/soh/assets/xml/textures/map_name_static.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/message_static.xml b/soh/assets/xml/textures/message_static.xml new file mode 100644 index 000000000..afdde60de --- /dev/null +++ b/soh/assets/xml/textures/message_static.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/soh/assets/xml/textures/message_texture_static.xml b/soh/assets/xml/textures/message_texture_static.xml new file mode 100644 index 000000000..bbf7e1259 --- /dev/null +++ b/soh/assets/xml/textures/message_texture_static.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/soh/assets/xml/textures/nes_font_static.xml b/soh/assets/xml/textures/nes_font_static.xml new file mode 100644 index 000000000..b01646cb5 --- /dev/null +++ b/soh/assets/xml/textures/nes_font_static.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/nintendo_rogo_static.xml b/soh/assets/xml/textures/nintendo_rogo_static.xml new file mode 100644 index 000000000..7a60524b2 --- /dev/null +++ b/soh/assets/xml/textures/nintendo_rogo_static.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/soh/assets/xml/textures/parameter_static.xml b/soh/assets/xml/textures/parameter_static.xml new file mode 100644 index 000000000..bd4173431 --- /dev/null +++ b/soh/assets/xml/textures/parameter_static.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/place_title_cards.xml b/soh/assets/xml/textures/place_title_cards.xml new file mode 100644 index 000000000..c1771d4c8 --- /dev/null +++ b/soh/assets/xml/textures/place_title_cards.xml @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/skyboxes.xml b/soh/assets/xml/textures/skyboxes.xml new file mode 100644 index 000000000..9ff554c94 --- /dev/null +++ b/soh/assets/xml/textures/skyboxes.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/assets/xml/textures/title_static.xml b/soh/assets/xml/textures/title_static.xml new file mode 100644 index 000000000..422b60cdc --- /dev/null +++ b/soh/assets/xml/textures/title_static.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/soh/build.c b/soh/build.c new file mode 100644 index 000000000..acb8d151d --- /dev/null +++ b/soh/build.c @@ -0,0 +1,8 @@ +const char gBuildVersion[] = "DECKARD"; +const char gBuildTeam[] = "github.com/harbourmasters"; +#ifdef __TIMESTAMP__ +const char gBuildDate[] = __TIMESTAMP__; +#else +const char gBuildDate[] = __DATE__ " " __TIME__; +#endif +const char gBuildMakeOption[] = ""; \ No newline at end of file diff --git a/soh/extract_baserom.py b/soh/extract_baserom.py new file mode 100755 index 000000000..a3eb83a8f --- /dev/null +++ b/soh/extract_baserom.py @@ -0,0 +1,1608 @@ +#!/usr/bin/python3 + +import os +import sys +import struct +from multiprocessing import Pool, cpu_count + + +ROM_FILE_NAME = 'baserom_non_mq.z64' +FILE_TABLE_OFFSET = 0x12F70 + +FILE_NAMES = [ + 'makerom', + 'boot', + 'dmadata', + 'Audiobank', + 'Audioseq', + 'Audiotable', + 'link_animetion', + 'icon_item_static', + 'icon_item_24_static', + 'icon_item_field_static', + 'icon_item_dungeon_static', + 'icon_item_gameover_static', + 'icon_item_nes_static', + 'icon_item_ger_static', + 'icon_item_fra_static', + 'item_name_static', + 'map_name_static', + 'do_action_static', + 'message_static', + 'message_texture_static', + 'nes_font_static', + 'nes_message_data_static', + 'ger_message_data_static', + 'fra_message_data_static', + 'staff_message_data_static', + 'map_grand_static', + 'map_i_static', + 'map_48x85_static', + 'code', + 'ovl_title', + 'ovl_select', + 'ovl_opening', + 'ovl_file_choose', + 'ovl_kaleido_scope', + 'ovl_player_actor', + 'ovl_map_mark_data', + 'ovl_En_Test', + 'ovl_Arms_Hook', + 'ovl_Arrow_Fire', + 'ovl_Arrow_Ice', + 'ovl_Arrow_Light', + 'ovl_Bg_Bdan_Objects', + 'ovl_Bg_Bdan_Switch', + 'ovl_Bg_Bom_Guard', + 'ovl_Bg_Bombwall', + 'ovl_Bg_Bowl_Wall', + 'ovl_Bg_Breakwall', + 'ovl_Bg_Ddan_Jd', + 'ovl_Bg_Ddan_Kd', + 'ovl_Bg_Dodoago', + 'ovl_Bg_Dy_Yoseizo', + 'ovl_Bg_Ganon_Otyuka', + 'ovl_Bg_Gate_Shutter', + 'ovl_Bg_Gjyo_Bridge', + 'ovl_Bg_Gnd_Darkmeiro', + 'ovl_Bg_Gnd_Firemeiro', + 'ovl_Bg_Gnd_Iceblock', + 'ovl_Bg_Gnd_Nisekabe', + 'ovl_Bg_Gnd_Soulmeiro', + 'ovl_Bg_Haka', + 'ovl_Bg_Haka_Gate', + 'ovl_Bg_Haka_Huta', + 'ovl_Bg_Haka_Megane', + 'ovl_Bg_Haka_MeganeBG', + 'ovl_Bg_Haka_Sgami', + 'ovl_Bg_Haka_Ship', + 'ovl_Bg_Haka_Trap', + 'ovl_Bg_Haka_Tubo', + 'ovl_Bg_Haka_Water', + 'ovl_Bg_Haka_Zou', + 'ovl_Bg_Heavy_Block', + 'ovl_Bg_Hidan_Curtain', + 'ovl_Bg_Hidan_Dalm', + 'ovl_Bg_Hidan_Firewall', + 'ovl_Bg_Hidan_Fslift', + 'ovl_Bg_Hidan_Fwbig', + 'ovl_Bg_Hidan_Hamstep', + 'ovl_Bg_Hidan_Hrock', + 'ovl_Bg_Hidan_Kousi', + 'ovl_Bg_Hidan_Kowarerukabe', + 'ovl_Bg_Hidan_Rock', + 'ovl_Bg_Hidan_Rsekizou', + 'ovl_Bg_Hidan_Sekizou', + 'ovl_Bg_Hidan_Sima', + 'ovl_Bg_Hidan_Syoku', + 'ovl_Bg_Ice_Objects', + 'ovl_Bg_Ice_Shelter', + 'ovl_Bg_Ice_Shutter', + 'ovl_Bg_Ice_Turara', + 'ovl_Bg_Ingate', + 'ovl_Bg_Jya_1flift', + 'ovl_Bg_Jya_Amishutter', + 'ovl_Bg_Jya_Bigmirror', + 'ovl_Bg_Jya_Block', + 'ovl_Bg_Jya_Bombchuiwa', + 'ovl_Bg_Jya_Bombiwa', + 'ovl_Bg_Jya_Cobra', + 'ovl_Bg_Jya_Goroiwa', + 'ovl_Bg_Jya_Haheniron', + 'ovl_Bg_Jya_Ironobj', + 'ovl_Bg_Jya_Kanaami', + 'ovl_Bg_Jya_Lift', + 'ovl_Bg_Jya_Megami', + 'ovl_Bg_Jya_Zurerukabe', + 'ovl_Bg_Menkuri_Eye', + 'ovl_Bg_Menkuri_Kaiten', + 'ovl_Bg_Menkuri_Nisekabe', + 'ovl_Bg_Mizu_Bwall', + 'ovl_Bg_Mizu_Movebg', + 'ovl_Bg_Mizu_Shutter', + 'ovl_Bg_Mizu_Uzu', + 'ovl_Bg_Mizu_Water', + 'ovl_Bg_Mjin', + 'ovl_Bg_Mori_Bigst', + 'ovl_Bg_Mori_Elevator', + 'ovl_Bg_Mori_Hashigo', + 'ovl_Bg_Mori_Hashira4', + 'ovl_Bg_Mori_Hineri', + 'ovl_Bg_Mori_Idomizu', + 'ovl_Bg_Mori_Kaitenkabe', + 'ovl_Bg_Mori_Rakkatenjo', + 'ovl_Bg_Po_Event', + 'ovl_Bg_Po_Syokudai', + 'ovl_Bg_Pushbox', + 'ovl_Bg_Relay_Objects', + 'ovl_Bg_Spot00_Break', + 'ovl_Bg_Spot00_Hanebasi', + 'ovl_Bg_Spot01_Fusya', + 'ovl_Bg_Spot01_Idohashira', + 'ovl_Bg_Spot01_Idomizu', + 'ovl_Bg_Spot01_Idosoko', + 'ovl_Bg_Spot01_Objects2', + 'ovl_Bg_Spot02_Objects', + 'ovl_Bg_Spot03_Taki', + 'ovl_Bg_Spot05_Soko', + 'ovl_Bg_Spot06_Objects', + 'ovl_Bg_Spot07_Taki', + 'ovl_Bg_Spot08_Bakudankabe', + 'ovl_Bg_Spot08_Iceblock', + 'ovl_Bg_Spot09_Obj', + 'ovl_Bg_Spot11_Bakudankabe', + 'ovl_Bg_Spot11_Oasis', + 'ovl_Bg_Spot12_Gate', + 'ovl_Bg_Spot12_Saku', + 'ovl_Bg_Spot15_Rrbox', + 'ovl_Bg_Spot15_Saku', + 'ovl_Bg_Spot16_Bombstone', + 'ovl_Bg_Spot16_Doughnut', + 'ovl_Bg_Spot17_Bakudankabe', + 'ovl_Bg_Spot17_Funen', + 'ovl_Bg_Spot18_Basket', + 'ovl_Bg_Spot18_Futa', + 'ovl_Bg_Spot18_Obj', + 'ovl_Bg_Spot18_Shutter', + 'ovl_Bg_Sst_Floor', + 'ovl_Bg_Toki_Hikari', + 'ovl_Bg_Toki_Swd', + 'ovl_Bg_Treemouth', + 'ovl_Bg_Umajump', + 'ovl_Bg_Vb_Sima', + 'ovl_Bg_Ydan_Hasi', + 'ovl_Bg_Ydan_Maruta', + 'ovl_Bg_Ydan_Sp', + 'ovl_Bg_Zg', + 'ovl_Boss_Dodongo', + 'ovl_Boss_Fd', + 'ovl_Boss_Fd2', + 'ovl_Boss_Ganon', + 'ovl_Boss_Ganon2', + 'ovl_Boss_Ganondrof', + 'ovl_Boss_Goma', + 'ovl_Boss_Mo', + 'ovl_Boss_Sst', + 'ovl_Boss_Tw', + 'ovl_Boss_Va', + 'ovl_Demo_6K', + 'ovl_Demo_Du', + 'ovl_Demo_Ec', + 'ovl_Demo_Effect', + 'ovl_Demo_Ext', + 'ovl_Demo_Geff', + 'ovl_Demo_Gj', + 'ovl_Demo_Go', + 'ovl_Demo_Gt', + 'ovl_Demo_Ik', + 'ovl_Demo_Im', + 'ovl_Demo_Kankyo', + 'ovl_Demo_Kekkai', + 'ovl_Demo_Sa', + 'ovl_Demo_Shd', + 'ovl_Demo_Tre_Lgt', + 'ovl_Door_Ana', + 'ovl_Door_Gerudo', + 'ovl_Door_Killer', + 'ovl_Door_Shutter', + 'ovl_Door_Toki', + 'ovl_Door_Warp1', + 'ovl_Efc_Erupc', + 'ovl_Eff_Dust', + 'ovl_Effect_Ss_Blast', + 'ovl_Effect_Ss_Bomb', + 'ovl_Effect_Ss_Bomb2', + 'ovl_Effect_Ss_Bubble', + 'ovl_Effect_Ss_D_Fire', + 'ovl_Effect_Ss_Dead_Db', + 'ovl_Effect_Ss_Dead_Dd', + 'ovl_Effect_Ss_Dead_Ds', + 'ovl_Effect_Ss_Dead_Sound', + 'ovl_Effect_Ss_Dt_Bubble', + 'ovl_Effect_Ss_Dust', + 'ovl_Effect_Ss_En_Fire', + 'ovl_Effect_Ss_En_Ice', + 'ovl_Effect_Ss_Extra', + 'ovl_Effect_Ss_Fcircle', + 'ovl_Effect_Ss_Fhg_Flash', + 'ovl_Effect_Ss_Fire_Tail', + 'ovl_Effect_Ss_G_Fire', + 'ovl_Effect_Ss_G_Magma', + 'ovl_Effect_Ss_G_Magma2', + 'ovl_Effect_Ss_G_Ripple', + 'ovl_Effect_Ss_G_Spk', + 'ovl_Effect_Ss_G_Splash', + 'ovl_Effect_Ss_Hahen', + 'ovl_Effect_Ss_HitMark', + 'ovl_Effect_Ss_Ice_Piece', + 'ovl_Effect_Ss_Ice_Smoke', + 'ovl_Effect_Ss_K_Fire', + 'ovl_Effect_Ss_Kakera', + 'ovl_Effect_Ss_KiraKira', + 'ovl_Effect_Ss_Lightning', + 'ovl_Effect_Ss_Sibuki', + 'ovl_Effect_Ss_Sibuki2', + 'ovl_Effect_Ss_Solder_Srch_Ball', + 'ovl_Effect_Ss_Stick', + 'ovl_Effect_Ss_Stone1', + 'ovl_Elf_Msg', + 'ovl_Elf_Msg2', + 'ovl_En_Am', + 'ovl_En_Ani', + 'ovl_En_Anubice', + 'ovl_En_Anubice_Fire', + 'ovl_En_Anubice_Tag', + 'ovl_En_Arow_Trap', + 'ovl_En_Arrow', + 'ovl_En_Attack_Niw', + 'ovl_En_Ba', + 'ovl_En_Bb', + 'ovl_En_Bdfire', + 'ovl_En_Bigokuta', + 'ovl_En_Bili', + 'ovl_En_Bird', + 'ovl_En_Blkobj', + 'ovl_En_Bom', + 'ovl_En_Bom_Bowl_Man', + 'ovl_En_Bom_Bowl_Pit', + 'ovl_En_Bom_Chu', + 'ovl_En_Bombf', + 'ovl_En_Boom', + 'ovl_En_Box', + 'ovl_En_Brob', + 'ovl_En_Bubble', + 'ovl_En_Butte', + 'ovl_En_Bw', + 'ovl_En_Bx', + 'ovl_En_Changer', + 'ovl_En_Clear_Tag', + 'ovl_En_Cow', + 'ovl_En_Crow', + 'ovl_En_Cs', + 'ovl_En_Daiku', + 'ovl_En_Daiku_Kakariko', + 'ovl_En_Dekubaba', + 'ovl_En_Dekunuts', + 'ovl_En_Dh', + 'ovl_En_Dha', + 'ovl_En_Diving_Game', + 'ovl_En_Dns', + 'ovl_En_Dnt_Demo', + 'ovl_En_Dnt_Jiji', + 'ovl_En_Dnt_Nomal', + 'ovl_En_Dodojr', + 'ovl_En_Dodongo', + 'ovl_En_Dog', + 'ovl_En_Door', + 'ovl_En_Ds', + 'ovl_En_Du', + 'ovl_En_Dy_Extra', + 'ovl_En_Eg', + 'ovl_En_Eiyer', + 'ovl_En_Elf', + 'ovl_En_Encount1', + 'ovl_En_Encount2', + 'ovl_En_Ex_Item', + 'ovl_En_Ex_Ruppy', + 'ovl_En_Fd', + 'ovl_En_Fd_Fire', + 'ovl_En_Fhg_Fire', + 'ovl_En_Fire_Rock', + 'ovl_En_Firefly', + 'ovl_En_Fish', + 'ovl_En_Floormas', + 'ovl_En_Fr', + 'ovl_En_Fu', + 'ovl_En_Fw', + 'ovl_En_Fz', + 'ovl_En_G_Switch', + 'ovl_En_Ganon_Mant', + 'ovl_En_Ganon_Organ', + 'ovl_En_Gb', + 'ovl_En_Ge1', + 'ovl_En_Ge2', + 'ovl_En_Ge3', + 'ovl_En_GeldB', + 'ovl_En_GirlA', + 'ovl_En_Gm', + 'ovl_En_Go', + 'ovl_En_Go2', + 'ovl_En_Goma', + 'ovl_En_Goroiwa', + 'ovl_En_Gs', + 'ovl_En_Guest', + 'ovl_En_Hata', + 'ovl_En_Heishi1', + 'ovl_En_Heishi2', + 'ovl_En_Heishi3', + 'ovl_En_Heishi4', + 'ovl_En_Hintnuts', + 'ovl_En_Holl', + 'ovl_En_Honotrap', + 'ovl_En_Horse', + 'ovl_En_Horse_Game_Check', + 'ovl_En_Horse_Ganon', + 'ovl_En_Horse_Link_Child', + 'ovl_En_Horse_Normal', + 'ovl_En_Horse_Zelda', + 'ovl_En_Hs', + 'ovl_En_Hs2', + 'ovl_En_Hy', + 'ovl_En_Ice_Hono', + 'ovl_En_Ik', + 'ovl_En_In', + 'ovl_En_Insect', + 'ovl_En_Ishi', + 'ovl_En_It', + 'ovl_En_Jj', + 'ovl_En_Js', + 'ovl_En_Jsjutan', + 'ovl_En_Kakasi', + 'ovl_En_Kakasi2', + 'ovl_En_Kakasi3', + 'ovl_En_Kanban', + 'ovl_En_Karebaba', + 'ovl_En_Ko', + 'ovl_En_Kusa', + 'ovl_En_Kz', + 'ovl_En_Light', + 'ovl_En_Lightbox', + 'ovl_En_M_Fire1', + 'ovl_En_M_Thunder', + 'ovl_En_Ma1', + 'ovl_En_Ma2', + 'ovl_En_Ma3', + 'ovl_En_Mag', + 'ovl_En_Mb', + 'ovl_En_Md', + 'ovl_En_Mk', + 'ovl_En_Mm', + 'ovl_En_Mm2', + 'ovl_En_Ms', + 'ovl_En_Mu', + 'ovl_En_Nb', + 'ovl_En_Niw', + 'ovl_En_Niw_Girl', + 'ovl_En_Niw_Lady', + 'ovl_En_Nutsball', + 'ovl_En_Nwc', + 'ovl_En_Ny', + 'ovl_En_OE2', + 'ovl_En_Okarina_Effect', + 'ovl_En_Okarina_Tag', + 'ovl_En_Okuta', + 'ovl_En_Ossan', + 'ovl_En_Owl', + 'ovl_En_Part', + 'ovl_En_Peehat', + 'ovl_En_Po_Desert', + 'ovl_En_Po_Field', + 'ovl_En_Po_Relay', + 'ovl_En_Po_Sisters', + 'ovl_En_Poh', + 'ovl_En_Pu_box', + 'ovl_En_Rd', + 'ovl_En_Reeba', + 'ovl_En_River_Sound', + 'ovl_En_Rl', + 'ovl_En_Rr', + 'ovl_En_Ru1', + 'ovl_En_Ru2', + 'ovl_En_Sa', + 'ovl_En_Sb', + 'ovl_En_Scene_Change', + 'ovl_En_Sda', + 'ovl_En_Shopnuts', + 'ovl_En_Si', + 'ovl_En_Siofuki', + 'ovl_En_Skb', + 'ovl_En_Skj', + 'ovl_En_Skjneedle', + 'ovl_En_Ssh', + 'ovl_En_St', + 'ovl_En_Sth', + 'ovl_En_Stream', + 'ovl_En_Sw', + 'ovl_En_Syateki_Itm', + 'ovl_En_Syateki_Man', + 'ovl_En_Syateki_Niw', + 'ovl_En_Ta', + 'ovl_En_Takara_Man', + 'ovl_En_Tana', + 'ovl_En_Tg', + 'ovl_En_Tite', + 'ovl_En_Tk', + 'ovl_En_Torch', + 'ovl_En_Torch2', + 'ovl_En_Toryo', + 'ovl_En_Tp', + 'ovl_En_Tr', + 'ovl_En_Trap', + 'ovl_En_Tubo_Trap', + 'ovl_En_Vali', + 'ovl_En_Vase', + 'ovl_En_Vb_Ball', + 'ovl_En_Viewer', + 'ovl_En_Vm', + 'ovl_En_Wall_Tubo', + 'ovl_En_Wallmas', + 'ovl_En_Weather_Tag', + 'ovl_En_Weiyer', + 'ovl_En_Wf', + 'ovl_En_Wonder_Item', + 'ovl_En_Wonder_Talk', + 'ovl_En_Wonder_Talk2', + 'ovl_En_Wood02', + 'ovl_En_Xc', + 'ovl_En_Yabusame_Mark', + 'ovl_En_Yukabyun', + 'ovl_En_Zf', + 'ovl_En_Zl1', + 'ovl_En_Zl2', + 'ovl_En_Zl3', + 'ovl_En_Zl4', + 'ovl_En_Zo', + 'ovl_En_fHG', + 'ovl_End_Title', + 'ovl_Fishing', + 'ovl_Item_B_Heart', + 'ovl_Item_Etcetera', + 'ovl_Item_Inbox', + 'ovl_Item_Ocarina', + 'ovl_Item_Shield', + 'ovl_Magic_Dark', + 'ovl_Magic_Fire', + 'ovl_Magic_Wind', + 'ovl_Mir_Ray', + 'ovl_Obj_Bean', + 'ovl_Obj_Blockstop', + 'ovl_Obj_Bombiwa', + 'ovl_Obj_Comb', + 'ovl_Obj_Dekujr', + 'ovl_Obj_Elevator', + 'ovl_Obj_Hamishi', + 'ovl_Obj_Hana', + 'ovl_Obj_Hsblock', + 'ovl_Obj_Ice_Poly', + 'ovl_Obj_Kibako', + 'ovl_Obj_Kibako2', + 'ovl_Obj_Lift', + 'ovl_Obj_Lightswitch', + 'ovl_Obj_Makekinsuta', + 'ovl_Obj_Makeoshihiki', + 'ovl_Obj_Mure', + 'ovl_Obj_Mure2', + 'ovl_Obj_Mure3', + 'ovl_Obj_Oshihiki', + 'ovl_Obj_Roomtimer', + 'ovl_Obj_Switch', + 'ovl_Obj_Syokudai', + 'ovl_Obj_Timeblock', + 'ovl_Obj_Tsubo', + 'ovl_Obj_Warp2block', + 'ovl_Object_Kankyo', + 'ovl_Oceff_Spot', + 'ovl_Oceff_Storm', + 'ovl_Oceff_Wipe', + 'ovl_Oceff_Wipe2', + 'ovl_Oceff_Wipe3', + 'ovl_Oceff_Wipe4', + 'ovl_Shot_Sun', + 'gameplay_keep', + 'gameplay_field_keep', + 'gameplay_dangeon_keep', + 'gameplay_object_exchange_static', + 'object_link_boy', + 'object_link_child', + 'object_box', + 'object_human', + 'object_okuta', + 'object_poh', + 'object_wallmaster', + 'object_dy_obj', + 'object_firefly', + 'object_dodongo', + 'object_fire', + 'object_niw', + 'object_tite', + 'object_reeba', + 'object_peehat', + 'object_kingdodongo', + 'object_horse', + 'object_zf', + 'object_goma', + 'object_zl1', + 'object_gol', + 'object_bubble', + 'object_dodojr', + 'object_torch2', + 'object_bl', + 'object_tp', + 'object_oA1', + 'object_st', + 'object_bw', + 'object_ei', + 'object_horse_normal', + 'object_oB1', + 'object_o_anime', + 'object_spot04_objects', + 'object_ddan_objects', + 'object_hidan_objects', + 'object_horse_ganon', + 'object_oA2', + 'object_spot00_objects', + 'object_mb', + 'object_bombf', + 'object_sk2', + 'object_oE1', + 'object_oE_anime', + 'object_oE2', + 'object_ydan_objects', + 'object_gnd', + 'object_am', + 'object_dekubaba', + 'object_oA3', + 'object_oA4', + 'object_oA5', + 'object_oA6', + 'object_oA7', + 'object_jj', + 'object_oA8', + 'object_oA9', + 'object_oB2', + 'object_oB3', + 'object_oB4', + 'object_horse_zelda', + 'object_opening_demo1', + 'object_warp1', + 'object_b_heart', + 'object_dekunuts', + 'object_oE3', + 'object_oE4', + 'object_menkuri_objects', + 'object_oE5', + 'object_oE6', + 'object_oE7', + 'object_oE8', + 'object_oE9', + 'object_oE10', + 'object_oE11', + 'object_oE12', + 'object_vali', + 'object_oA10', + 'object_oA11', + 'object_mizu_objects', + 'object_fhg', + 'object_ossan', + 'object_mori_hineri1', + 'object_Bb', + 'object_toki_objects', + 'object_yukabyun', + 'object_zl2', + 'object_mjin', + 'object_mjin_flash', + 'object_mjin_dark', + 'object_mjin_flame', + 'object_mjin_ice', + 'object_mjin_soul', + 'object_mjin_wind', + 'object_mjin_oka', + 'object_haka_objects', + 'object_spot06_objects', + 'object_ice_objects', + 'object_relay_objects', + 'object_mori_hineri1a', + 'object_mori_hineri2', + 'object_mori_hineri2a', + 'object_mori_objects', + 'object_mori_tex', + 'object_spot08_obj', + 'object_warp2', + 'object_hata', + 'object_bird', + 'object_wood02', + 'object_lightbox', + 'object_pu_box', + 'object_trap', + 'object_vase', + 'object_im', + 'object_ta', + 'object_tk', + 'object_xc', + 'object_vm', + 'object_bv', + 'object_hakach_objects', + 'object_efc_crystal_light', + 'object_efc_fire_ball', + 'object_efc_flash', + 'object_efc_lgt_shower', + 'object_efc_star_field', + 'object_god_lgt', + 'object_light_ring', + 'object_triforce_spot', + 'object_medal', + 'object_bdan_objects', + 'object_sd', + 'object_rd', + 'object_po_sisters', + 'object_heavy_object', + 'object_gndd', + 'object_fd', + 'object_du', + 'object_fw', + 'object_horse_link_child', + 'object_spot02_objects', + 'object_haka', + 'object_ru1', + 'object_syokudai', + 'object_fd2', + 'object_dh', + 'object_rl', + 'object_efc_tw', + 'object_demo_tre_lgt', + 'object_gi_key', + 'object_mir_ray', + 'object_brob', + 'object_gi_jewel', + 'object_spot09_obj', + 'object_spot18_obj', + 'object_bdoor', + 'object_spot17_obj', + 'object_shop_dungen', + 'object_nb', + 'object_mo', + 'object_sb', + 'object_gi_melody', + 'object_gi_heart', + 'object_gi_compass', + 'object_gi_bosskey', + 'object_gi_medal', + 'object_gi_nuts', + 'object_sa', + 'object_gi_hearts', + 'object_gi_arrowcase', + 'object_gi_bombpouch', + 'object_in', + 'object_tr', + 'object_spot16_obj', + 'object_oE1s', + 'object_oE4s', + 'object_os_anime', + 'object_gi_bottle', + 'object_gi_stick', + 'object_gi_map', + 'object_oF1d_map', + 'object_ru2', + 'object_gi_shield_1', + 'object_dekujr', + 'object_gi_magicpot', + 'object_gi_bomb_1', + 'object_oF1s', + 'object_ma2', + 'object_gi_purse', + 'object_hni', + 'object_tw', + 'object_rr', + 'object_bxa', + 'object_anubice', + 'object_gi_gerudo', + 'object_gi_arrow', + 'object_gi_bomb_2', + 'object_gi_egg', + 'object_gi_scale', + 'object_gi_shield_2', + 'object_gi_hookshot', + 'object_gi_ocarina', + 'object_gi_milk', + 'object_ma1', + 'object_ganon', + 'object_sst', + 'object_ny', + 'object_fr', + 'object_gi_pachinko', + 'object_gi_boomerang', + 'object_gi_bow', + 'object_gi_glasses', + 'object_gi_liquid', + 'object_ani', + 'object_demo_6k', + 'object_gi_shield_3', + 'object_gi_letter', + 'object_spot15_obj', + 'object_jya_obj', + 'object_gi_clothes', + 'object_gi_bean', + 'object_gi_fish', + 'object_gi_saw', + 'object_gi_hammer', + 'object_gi_grass', + 'object_gi_longsword', + 'object_spot01_objects', + 'object_md', + 'object_km1', + 'object_kw1', + 'object_zo', + 'object_kz', + 'object_umajump', + 'object_masterkokiri', + 'object_masterkokirihead', + 'object_mastergolon', + 'object_masterzoora', + 'object_aob', + 'object_ik', + 'object_ahg', + 'object_cne', + 'object_gi_niwatori', + 'object_skj', + 'object_gi_bottle_letter', + 'object_bji', + 'object_bba', + 'object_gi_ocarina_0', + 'object_ds', + 'object_ane', + 'object_boj', + 'object_spot03_object', + 'object_spot07_object', + 'object_fz', + 'object_bob', + 'object_ge1', + 'object_yabusame_point', + 'object_gi_boots_2', + 'object_gi_seed', + 'object_gnd_magic', + 'object_d_elevator', + 'object_d_hsblock', + 'object_d_lift', + 'object_mamenoki', + 'object_goroiwa', + 'object_toryo', + 'object_daiku', + 'object_nwc', + 'object_blkobj', + 'object_gm', + 'object_ms', + 'object_hs', + 'object_ingate', + 'object_lightswitch', + 'object_kusa', + 'object_tsubo', + 'object_gi_gloves', + 'object_gi_coin', + 'object_kanban', + 'object_gjyo_objects', + 'object_owl', + 'object_mk', + 'object_fu', + 'object_gi_ki_tan_mask', + 'object_gi_redead_mask', + 'object_gi_skj_mask', + 'object_gi_rabit_mask', + 'object_gi_truth_mask', + 'object_ganon_objects', + 'object_siofuki', + 'object_stream', + 'object_mm', + 'object_fa', + 'object_os', + 'object_gi_eye_lotion', + 'object_gi_powder', + 'object_gi_mushroom', + 'object_gi_ticketstone', + 'object_gi_brokensword', + 'object_js', + 'object_cs', + 'object_gi_prescription', + 'object_gi_bracelet', + 'object_gi_soldout', + 'object_gi_frog', + 'object_mag', + 'object_door_gerudo', + 'object_gt', + 'object_efc_erupc', + 'object_zl2_anime1', + 'object_zl2_anime2', + 'object_gi_golonmask', + 'object_gi_zoramask', + 'object_gi_gerudomask', + 'object_ganon2', + 'object_ka', + 'object_ts', + 'object_zg', + 'object_gi_hoverboots', + 'object_gi_m_arrow', + 'object_ds2', + 'object_ec', + 'object_fish', + 'object_gi_sutaru', + 'object_gi_goddess', + 'object_ssh', + 'object_bigokuta', + 'object_bg', + 'object_spot05_objects', + 'object_spot12_obj', + 'object_bombiwa', + 'object_hintnuts', + 'object_rs', + 'object_spot00_break', + 'object_gla', + 'object_shopnuts', + 'object_geldb', + 'object_gr', + 'object_dog', + 'object_jya_iron', + 'object_jya_door', + 'object_spot01_objects2', + 'object_spot11_obj', + 'object_kibako2', + 'object_dns', + 'object_dnk', + 'object_gi_fire', + 'object_gi_insect', + 'object_gi_butterfly', + 'object_gi_ghost', + 'object_gi_soul', + 'object_bowl', + 'object_po_field', + 'object_demo_kekkai', + 'object_efc_doughnut', + 'object_gi_dekupouch', + 'object_ganon_anime1', + 'object_ganon_anime2', + 'object_ganon_anime3', + 'object_gi_rupy', + 'object_spot01_matoya', + 'object_spot01_matoyab', + 'object_po_composer', + 'object_mu', + 'object_wf', + 'object_skb', + 'object_gj', + 'object_geff', + 'object_haka_door', + 'object_gs', + 'object_ps', + 'object_bwall', + 'object_crow', + 'object_cow', + 'object_cob', + 'object_gi_sword_1', + 'object_door_killer', + 'object_ouke_haka', + 'object_timeblock', + 'object_zl4', + 'g_pn_01', + 'g_pn_02', + 'g_pn_03', + 'g_pn_04', + 'g_pn_05', + 'g_pn_06', + 'g_pn_07', + 'g_pn_08', + 'g_pn_09', + 'g_pn_10', + 'g_pn_11', + 'g_pn_12', + 'g_pn_13', + 'g_pn_14', + 'g_pn_15', + 'g_pn_16', + 'g_pn_17', + 'g_pn_18', + 'g_pn_19', + 'g_pn_20', + 'g_pn_21', + 'g_pn_22', + 'g_pn_23', + 'g_pn_24', + 'g_pn_25', + 'g_pn_26', + 'g_pn_27', + 'g_pn_28', + 'g_pn_29', + 'g_pn_30', + 'g_pn_31', + 'g_pn_32', + 'g_pn_33', + 'g_pn_34', + 'g_pn_35', + 'g_pn_36', + 'g_pn_37', + 'g_pn_38', + 'g_pn_39', + 'g_pn_40', + 'g_pn_41', + 'g_pn_42', + 'g_pn_43', + 'g_pn_44', + 'g_pn_45', + 'g_pn_46', + 'g_pn_47', + 'g_pn_48', + 'g_pn_49', + 'g_pn_50', + 'g_pn_51', + 'g_pn_52', + 'g_pn_53', + 'g_pn_54', + 'g_pn_55', + 'g_pn_56', + 'g_pn_57', + 'z_select_static', + 'nintendo_rogo_static', + 'title_static', + 'parameter_static', + 'vr_fine0_static', + 'vr_fine0_pal_static', + 'vr_fine1_static', + 'vr_fine1_pal_static', + 'vr_fine2_static', + 'vr_fine2_pal_static', + 'vr_fine3_static', + 'vr_fine3_pal_static', + 'vr_cloud0_static', + 'vr_cloud0_pal_static', + 'vr_cloud1_static', + 'vr_cloud1_pal_static', + 'vr_cloud2_static', + 'vr_cloud2_pal_static', + 'vr_cloud3_static', + 'vr_cloud3_pal_static', + 'vr_holy0_static', + 'vr_holy0_pal_static', + 'vr_holy1_static', + 'vr_holy1_pal_static', + 'vr_MDVR_static', + 'vr_MDVR_pal_static', + 'vr_MNVR_static', + 'vr_MNVR_pal_static', + 'vr_RUVR_static', + 'vr_RUVR_pal_static', + 'vr_LHVR_static', + 'vr_LHVR_pal_static', + 'vr_KHVR_static', + 'vr_KHVR_pal_static', + 'vr_K3VR_static', + 'vr_K3VR_pal_static', + 'vr_K4VR_static', + 'vr_K4VR_pal_static', + 'vr_K5VR_static', + 'vr_K5VR_pal_static', + 'vr_SP1a_static', + 'vr_SP1a_pal_static', + 'vr_MLVR_static', + 'vr_MLVR_pal_static', + 'vr_KKRVR_static', + 'vr_KKRVR_pal_static', + 'vr_KR3VR_static', + 'vr_KR3VR_pal_static', + 'vr_IPVR_static', + 'vr_IPVR_pal_static', + 'vr_KSVR_static', + 'vr_KSVR_pal_static', + 'vr_GLVR_static', + 'vr_GLVR_pal_static', + 'vr_ZRVR_static', + 'vr_ZRVR_pal_static', + 'vr_DGVR_static', + 'vr_DGVR_pal_static', + 'vr_ALVR_static', + 'vr_ALVR_pal_static', + 'vr_NSVR_static', + 'vr_NSVR_pal_static', + 'vr_LBVR_static', + 'vr_LBVR_pal_static', + 'vr_TTVR_static', + 'vr_TTVR_pal_static', + 'vr_FCVR_static', + 'vr_FCVR_pal_static', + 'elf_message_field', + 'elf_message_ydan', + 'syotes_scene', + 'syotes_room_0', + 'syotes2_scene', + 'syotes2_room_0', + 'depth_test_scene', + 'depth_test_room_0', + 'spot00_scene', + 'spot00_room_0', + 'spot01_scene', + 'spot01_room_0', + 'spot02_scene', + 'spot02_room_0', + 'spot02_room_1', + 'spot03_scene', + 'spot03_room_0', + 'spot03_room_1', + 'spot04_scene', + 'spot04_room_0', + 'spot04_room_1', + 'spot04_room_2', + 'spot05_scene', + 'spot05_room_0', + 'spot06_scene', + 'spot06_room_0', + 'spot07_scene', + 'spot07_room_0', + 'spot07_room_1', + 'spot08_scene', + 'spot08_room_0', + 'spot09_scene', + 'spot09_room_0', + 'spot10_scene', + 'spot10_room_0', + 'spot10_room_1', + 'spot10_room_2', + 'spot10_room_3', + 'spot10_room_4', + 'spot10_room_5', + 'spot10_room_6', + 'spot10_room_7', + 'spot10_room_8', + 'spot10_room_9', + 'spot11_scene', + 'spot11_room_0', + 'spot12_scene', + 'spot12_room_0', + 'spot12_room_1', + 'spot13_scene', + 'spot13_room_0', + 'spot13_room_1', + 'spot15_scene', + 'spot15_room_0', + 'spot16_scene', + 'spot16_room_0', + 'spot17_scene', + 'spot17_room_0', + 'spot17_room_1', + 'spot18_scene', + 'spot18_room_0', + 'spot18_room_1', + 'spot18_room_2', + 'spot18_room_3', + 'ydan_scene', + 'ydan_room_0', + 'ydan_room_1', + 'ydan_room_2', + 'ydan_room_3', + 'ydan_room_4', + 'ydan_room_5', + 'ydan_room_6', + 'ydan_room_7', + 'ydan_room_8', + 'ydan_room_9', + 'ydan_room_10', + 'ydan_room_11', + 'ddan_scene', + 'ddan_room_0', + 'ddan_room_1', + 'ddan_room_2', + 'ddan_room_3', + 'ddan_room_4', + 'ddan_room_5', + 'ddan_room_6', + 'ddan_room_7', + 'ddan_room_8', + 'ddan_room_9', + 'ddan_room_10', + 'ddan_room_11', + 'ddan_room_12', + 'ddan_room_13', + 'ddan_room_14', + 'ddan_room_15', + 'ddan_room_16', + 'bdan_scene', + 'bdan_room_0', + 'bdan_room_1', + 'bdan_room_2', + 'bdan_room_3', + 'bdan_room_4', + 'bdan_room_5', + 'bdan_room_6', + 'bdan_room_7', + 'bdan_room_8', + 'bdan_room_9', + 'bdan_room_10', + 'bdan_room_11', + 'bdan_room_12', + 'bdan_room_13', + 'bdan_room_14', + 'bdan_room_15', + 'Bmori1_scene', + 'Bmori1_room_0', + 'Bmori1_room_1', + 'Bmori1_room_2', + 'Bmori1_room_3', + 'Bmori1_room_4', + 'Bmori1_room_5', + 'Bmori1_room_6', + 'Bmori1_room_7', + 'Bmori1_room_8', + 'Bmori1_room_9', + 'Bmori1_room_10', + 'Bmori1_room_11', + 'Bmori1_room_12', + 'Bmori1_room_13', + 'Bmori1_room_14', + 'Bmori1_room_15', + 'Bmori1_room_16', + 'Bmori1_room_17', + 'Bmori1_room_18', + 'Bmori1_room_19', + 'Bmori1_room_20', + 'Bmori1_room_21', + 'Bmori1_room_22', + 'HIDAN_scene', + 'HIDAN_room_0', + 'HIDAN_room_1', + 'HIDAN_room_2', + 'HIDAN_room_3', + 'HIDAN_room_4', + 'HIDAN_room_5', + 'HIDAN_room_6', + 'HIDAN_room_7', + 'HIDAN_room_8', + 'HIDAN_room_9', + 'HIDAN_room_10', + 'HIDAN_room_11', + 'HIDAN_room_12', + 'HIDAN_room_13', + 'HIDAN_room_14', + 'HIDAN_room_15', + 'HIDAN_room_16', + 'HIDAN_room_17', + 'HIDAN_room_18', + 'HIDAN_room_19', + 'HIDAN_room_20', + 'HIDAN_room_21', + 'HIDAN_room_22', + 'HIDAN_room_23', + 'HIDAN_room_24', + 'HIDAN_room_25', + 'HIDAN_room_26', + 'MIZUsin_scene', + 'MIZUsin_room_0', + 'MIZUsin_room_1', + 'MIZUsin_room_2', + 'MIZUsin_room_3', + 'MIZUsin_room_4', + 'MIZUsin_room_5', + 'MIZUsin_room_6', + 'MIZUsin_room_7', + 'MIZUsin_room_8', + 'MIZUsin_room_9', + 'MIZUsin_room_10', + 'MIZUsin_room_11', + 'MIZUsin_room_12', + 'MIZUsin_room_13', + 'MIZUsin_room_14', + 'MIZUsin_room_15', + 'MIZUsin_room_16', + 'MIZUsin_room_17', + 'MIZUsin_room_18', + 'MIZUsin_room_19', + 'MIZUsin_room_20', + 'MIZUsin_room_21', + 'MIZUsin_room_22', + 'jyasinzou_scene', + 'jyasinzou_room_0', + 'jyasinzou_room_1', + 'jyasinzou_room_2', + 'jyasinzou_room_3', + 'jyasinzou_room_4', + 'jyasinzou_room_5', + 'jyasinzou_room_6', + 'jyasinzou_room_7', + 'jyasinzou_room_8', + 'jyasinzou_room_9', + 'jyasinzou_room_10', + 'jyasinzou_room_11', + 'jyasinzou_room_12', + 'jyasinzou_room_13', + 'jyasinzou_room_14', + 'jyasinzou_room_15', + 'jyasinzou_room_16', + 'jyasinzou_room_17', + 'jyasinzou_room_18', + 'jyasinzou_room_19', + 'jyasinzou_room_20', + 'jyasinzou_room_21', + 'jyasinzou_room_22', + 'jyasinzou_room_23', + 'jyasinzou_room_24', + 'jyasinzou_room_25', + 'jyasinzou_room_26', + 'jyasinzou_room_27', + 'jyasinzou_room_28', + 'HAKAdan_scene', + 'HAKAdan_room_0', + 'HAKAdan_room_1', + 'HAKAdan_room_2', + 'HAKAdan_room_3', + 'HAKAdan_room_4', + 'HAKAdan_room_5', + 'HAKAdan_room_6', + 'HAKAdan_room_7', + 'HAKAdan_room_8', + 'HAKAdan_room_9', + 'HAKAdan_room_10', + 'HAKAdan_room_11', + 'HAKAdan_room_12', + 'HAKAdan_room_13', + 'HAKAdan_room_14', + 'HAKAdan_room_15', + 'HAKAdan_room_16', + 'HAKAdan_room_17', + 'HAKAdan_room_18', + 'HAKAdan_room_19', + 'HAKAdan_room_20', + 'HAKAdan_room_21', + 'HAKAdan_room_22', + 'HAKAdanCH_scene', + 'HAKAdanCH_room_0', + 'HAKAdanCH_room_1', + 'HAKAdanCH_room_2', + 'HAKAdanCH_room_3', + 'HAKAdanCH_room_4', + 'HAKAdanCH_room_5', + 'HAKAdanCH_room_6', + 'ice_doukutu_scene', + 'ice_doukutu_room_0', + 'ice_doukutu_room_1', + 'ice_doukutu_room_2', + 'ice_doukutu_room_3', + 'ice_doukutu_room_4', + 'ice_doukutu_room_5', + 'ice_doukutu_room_6', + 'ice_doukutu_room_7', + 'ice_doukutu_room_8', + 'ice_doukutu_room_9', + 'ice_doukutu_room_10', + 'ice_doukutu_room_11', + 'men_scene', + 'men_room_0', + 'men_room_1', + 'men_room_2', + 'men_room_3', + 'men_room_4', + 'men_room_5', + 'men_room_6', + 'men_room_7', + 'men_room_8', + 'men_room_9', + 'men_room_10', + 'ganontika_scene', + 'ganontika_room_0', + 'ganontika_room_1', + 'ganontika_room_2', + 'ganontika_room_3', + 'ganontika_room_4', + 'ganontika_room_5', + 'ganontika_room_6', + 'ganontika_room_7', + 'ganontika_room_8', + 'ganontika_room_9', + 'ganontika_room_10', + 'ganontika_room_11', + 'ganontika_room_12', + 'ganontika_room_13', + 'ganontika_room_14', + 'ganontika_room_15', + 'ganontika_room_16', + 'ganontika_room_17', + 'ganontika_room_18', + 'ganontika_room_19', + 'market_day_scene', + 'market_day_room_0', + 'market_night_scene', + 'market_night_room_0', + 'testroom_scene', + 'testroom_room_0', + 'testroom_room_1', + 'testroom_room_2', + 'testroom_room_3', + 'testroom_room_4', + 'kenjyanoma_scene', + 'kenjyanoma_room_0', + 'tokinoma_scene', + 'tokinoma_room_0', + 'tokinoma_room_1', + 'sutaru_scene', + 'sutaru_room_0', + 'link_home_scene', + 'link_home_room_0', + 'kokiri_shop_scene', + 'kokiri_shop_room_0', + 'kokiri_home_scene', + 'kokiri_home_room_0', + 'kakusiana_scene', + 'kakusiana_room_0', + 'kakusiana_room_1', + 'kakusiana_room_2', + 'kakusiana_room_3', + 'kakusiana_room_4', + 'kakusiana_room_5', + 'kakusiana_room_6', + 'kakusiana_room_7', + 'kakusiana_room_8', + 'kakusiana_room_9', + 'kakusiana_room_10', + 'kakusiana_room_11', + 'kakusiana_room_12', + 'kakusiana_room_13', + 'entra_scene', + 'entra_room_0', + 'moribossroom_scene', + 'moribossroom_room_0', + 'moribossroom_room_1', + 'syatekijyou_scene', + 'syatekijyou_room_0', + 'shop1_scene', + 'shop1_room_0', + 'hairal_niwa_scene', + 'hairal_niwa_room_0', + 'ganon_tou_scene', + 'ganon_tou_room_0', + 'sasatest_scene', + 'sasatest_room_0', + 'market_alley_scene', + 'market_alley_room_0', + 'spot20_scene', + 'spot20_room_0', + 'market_ruins_scene', + 'market_ruins_room_0', + 'entra_n_scene', + 'entra_n_room_0', + 'enrui_scene', + 'enrui_room_0', + 'market_alley_n_scene', + 'market_alley_n_room_0', + 'hiral_demo_scene', + 'hiral_demo_room_0', + 'kokiri_home3_scene', + 'kokiri_home3_room_0', + 'malon_stable_scene', + 'malon_stable_room_0', + 'kakariko_scene', + 'kakariko_room_0', + 'bdan_boss_scene', + 'bdan_boss_room_0', + 'bdan_boss_room_1', + 'FIRE_bs_scene', + 'FIRE_bs_room_0', + 'FIRE_bs_room_1', + 'hut_scene', + 'hut_room_0', + 'daiyousei_izumi_scene', + 'daiyousei_izumi_room_0', + 'hakaana_scene', + 'hakaana_room_0', + 'yousei_izumi_tate_scene', + 'yousei_izumi_tate_room_0', + 'yousei_izumi_yoko_scene', + 'yousei_izumi_yoko_room_0', + 'golon_scene', + 'golon_room_0', + 'zoora_scene', + 'zoora_room_0', + 'drag_scene', + 'drag_room_0', + 'alley_shop_scene', + 'alley_shop_room_0', + 'night_shop_scene', + 'night_shop_room_0', + 'impa_scene', + 'impa_room_0', + 'labo_scene', + 'labo_room_0', + 'tent_scene', + 'tent_room_0', + 'nakaniwa_scene', + 'nakaniwa_room_0', + 'ddan_boss_scene', + 'ddan_boss_room_0', + 'ddan_boss_room_1', + 'ydan_boss_scene', + 'ydan_boss_room_0', + 'ydan_boss_room_1', + 'HAKAdan_bs_scene', + 'HAKAdan_bs_room_0', + 'HAKAdan_bs_room_1', + 'MIZUsin_bs_scene', + 'MIZUsin_bs_room_0', + 'MIZUsin_bs_room_1', + 'ganon_scene', + 'ganon_room_0', + 'ganon_room_1', + 'ganon_room_2', + 'ganon_room_3', + 'ganon_room_4', + 'ganon_room_5', + 'ganon_room_6', + 'ganon_room_7', + 'ganon_room_8', + 'ganon_room_9', + 'ganon_boss_scene', + 'ganon_boss_room_0', + 'jyasinboss_scene', + 'jyasinboss_room_0', + 'jyasinboss_room_1', + 'jyasinboss_room_2', + 'jyasinboss_room_3', + 'kokiri_home4_scene', + 'kokiri_home4_room_0', + 'kokiri_home5_scene', + 'kokiri_home5_room_0', + 'ganon_final_scene', + 'ganon_final_room_0', + 'kakariko3_scene', + 'kakariko3_room_0', + 'hairal_niwa2_scene', + 'hairal_niwa2_room_0', + 'hakasitarelay_scene', + 'hakasitarelay_room_0', + 'hakasitarelay_room_1', + 'hakasitarelay_room_2', + 'hakasitarelay_room_3', + 'hakasitarelay_room_4', + 'hakasitarelay_room_5', + 'hakasitarelay_room_6', + 'shrine_scene', + 'shrine_room_0', + 'turibori_scene', + 'turibori_room_0', + 'shrine_n_scene', + 'shrine_n_room_0', + 'shrine_r_scene', + 'shrine_r_room_0', + 'hakaana2_scene', + 'hakaana2_room_0', + 'gerudoway_scene', + 'gerudoway_room_0', + 'gerudoway_room_1', + 'gerudoway_room_2', + 'gerudoway_room_3', + 'gerudoway_room_4', + 'gerudoway_room_5', + 'hairal_niwa_n_scene', + 'hairal_niwa_n_room_0', + 'bowling_scene', + 'bowling_room_0', + 'hakaana_ouke_scene', + 'hakaana_ouke_room_0', + 'hakaana_ouke_room_1', + 'hakaana_ouke_room_2', + 'hylia_labo_scene', + 'hylia_labo_room_0', + 'souko_scene', + 'souko_room_0', + 'souko_room_1', + 'souko_room_2', + 'miharigoya_scene', + 'miharigoya_room_0', + 'mahouya_scene', + 'mahouya_room_0', + 'takaraya_scene', + 'takaraya_room_0', + 'takaraya_room_1', + 'takaraya_room_2', + 'takaraya_room_3', + 'takaraya_room_4', + 'takaraya_room_5', + 'takaraya_room_6', + 'ganon_sonogo_scene', + 'ganon_sonogo_room_0', + 'ganon_sonogo_room_1', + 'ganon_sonogo_room_2', + 'ganon_sonogo_room_3', + 'ganon_sonogo_room_4', + 'ganon_demo_scene', + 'ganon_demo_room_0', + 'besitu_scene', + 'besitu_room_0', + 'face_shop_scene', + 'face_shop_room_0', + 'kinsuta_scene', + 'kinsuta_room_0', + 'ganontikasonogo_scene', + 'ganontikasonogo_room_0', + 'ganontikasonogo_room_1', + 'test01_scene', + 'test01_room_0', + 'bump_texture_static', + 'anime_model_1_static', + 'anime_model_2_static', + 'anime_model_3_static', + 'anime_model_4_static', + 'anime_model_5_static', + 'anime_model_6_static', + 'anime_texture_1_static', + 'anime_texture_2_static', + 'anime_texture_3_static', + 'anime_texture_4_static', + 'anime_texture_5_static', + 'anime_texture_6_static', + 'softsprite_matrix_static', +] + +romData = None + + +def initialize_worker(rom_data): + global romData + romData = rom_data + +def read_uint32_be(offset): + return struct.unpack('>I', romData[offset:offset+4])[0] + +def write_output_file(name, offset, size): + try: + with open(name, 'wb') as f: + f.write(romData[offset:offset+size]) + except IOError: + print('failed to write file ' + name) + +def ExtractFunc(i): + filename = 'baserom/' + FILE_NAMES[i] + entryOffset = FILE_TABLE_OFFSET + 16 * i + + virtStart = read_uint32_be(entryOffset + 0) + virtEnd = read_uint32_be(entryOffset + 4) + physStart = read_uint32_be(entryOffset + 8) + physEnd = read_uint32_be(entryOffset + 12) + + if physEnd == 0: # uncompressed + compressed = False + size = virtEnd - virtStart + else: # compressed + compressed = True + size = physEnd - physStart + + print('extracting ' + filename + " (0x%08X, 0x%08X)" % (virtStart, virtEnd)) + write_output_file(filename, physStart, size) + if compressed: + os.system('tools/yaz0 -d ' + filename + ' ' + filename) + +##################################################################### + +def main(): + try: + os.mkdir('baserom') + except: + pass + + # read baserom data + try: + with open(ROM_FILE_NAME, 'rb') as f: + rom_data = f.read() + except IOError: + print('failed to read ' + ROM_FILE_NAME) + sys.exit(1) + + # extract files + num_cores = cpu_count() + print("Extracting baserom with " + str(num_cores) + " CPU cores.") + with Pool(num_cores, initialize_worker, (rom_data,)) as p: + p.map(ExtractFunc, range(len(FILE_NAMES))) + +if __name__ == "__main__": + main() diff --git a/soh/fixbaserom.py b/soh/fixbaserom.py new file mode 100644 index 000000000..d8d86692f --- /dev/null +++ b/soh/fixbaserom.py @@ -0,0 +1,96 @@ +from os import path +import sys +import struct +import hashlib + + +def get_str_hash(byte_array): + return str(hashlib.md5(byte_array).hexdigest()) + + +# If the baserom exists and is correct, we don't need to change anything +if path.exists("baserom_non_mq.z64"): + with open("baserom_non_mq.z64", mode="rb") as file: + fileContent = bytearray(file.read()) + if get_str_hash(fileContent) == "9c1d795429220f5389045693a011b8f6": + print("Found valid baserom - exiting early") + sys.exit(0) + +# Determine if we have a ROM file +romFileExtensions = ["z64", "n64", "v64"] + +def find_baserom_original(): + for romFileExtLower in romFileExtensions: + for romFileExt in (romFileExtLower, romFileExtLower.upper()): + romFileNameCandidate = "baserom_original_non_mq." + romFileExt + if path.exists(romFileNameCandidate): + return romFileNameCandidate + return None + +romFileName = find_baserom_original() + +if romFileName is None: + print("Error: Could not find baserom_original_non_mq.z64/baserom_original_non_mq.n64/baserom_original_non_mq.v64.") + sys.exit(1) + +# Read in the original ROM +print("File '" + romFileName + "' found.") +with open(romFileName, mode="rb") as file: + fileContent = bytearray(file.read()) + +# Strip the overdump +print("Stripping overdump...") +fileContent = fileContent[0:0x3600000] + +fileContentLen = len(fileContent) + +# Check if ROM needs to be byte/word swapped +# Little-endian +if fileContent[0] == 0x40: + # Word Swap ROM + print("ROM needs to be word swapped...") + words = str(int(fileContentLen/4)) + little_byte_format = "<" + words + "I" + big_byte_format = ">" + words + "I" + tmp = struct.unpack_from(little_byte_format, fileContent, 0) + struct.pack_into(big_byte_format, fileContent, 0, *tmp) + + print("Word swapping done.") + +# Byte-swapped +elif fileContent[0] == 0x37: + # Byte Swap ROM + print("ROM needs to be byte swapped...") + halfwords = str(int(fileContentLen/2)) + little_byte_format = "<" + halfwords + "H" + big_byte_format = ">" + halfwords + "H" + tmp = struct.unpack_from(little_byte_format, fileContent, 0) + struct.pack_into(big_byte_format, fileContent, 0, *tmp) + + print("Byte swapping done.") + +# Patch the header +print("Patching header...") +fileContent[0x3E] = 0x50 + +for i in range(0x35CF000, len(fileContent)): + fileContent[i] = 0xFF + +# Check to see if the ROM is a "vanilla" Debug ROM +str_hash = get_str_hash(bytearray(fileContent)) +if str_hash != "9c1d795429220f5389045693a011b8f6": + print("Error: Expected a hash of 9c1d795429220f5389045693a011b8f6 but got " + str_hash + ". " + + "The baserom has probably been tampered, find a new one") + + if str_hash == "32fe2770c0f9b1a9cd2a4d449348c1cb": + print("The provided baserom is a rom which has been edited with ZeldaEdit and is not suitable for use with decomp. " + + "Find a new one.") + + sys.exit(1) + +# Write out our new ROM +print("Writing new ROM 'baserom_non_mq.z64'.") +with open("baserom_non_mq.z64", mode="wb") as file: + file.write(bytes(fileContent)) + +print("Done!") diff --git a/soh/format.sh b/soh/format.sh new file mode 100755 index 000000000..26f6f8a5b --- /dev/null +++ b/soh/format.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +FORMAT_OPTS="-i -style=file" +TIDY_OPTS="-p . --fix --fix-errors" +COMPILER_OPTS="-fno-builtin -std=gnu90 -Iinclude -Isrc -D_LANGUAGE_C -DNON_MATCHING" + +shopt -s globstar + +if (( $# > 0 )); then + echo "Formatting file(s) $*" + echo "Running clang-format..." + clang-format ${FORMAT_OPTS} "$@" + echo "Running clang-tidy..." + clang-tidy ${TIDY_OPTS} "$@" -- ${COMPILER_OPTS} &> /dev/null + echo "Adding missing final new lines..." + sed -i -e '$a\' "$@" + echo "Done formatting file(s) $*" + exit +fi + +echo "Formatting C files. This will take a bit" +echo "Running clang-format..." +clang-format ${FORMAT_OPTS} src/**/*.c +echo "Running clang-tidy..." +clang-tidy ${TIDY_OPTS} src/**/*.c -- ${COMPILER_OPTS} &> /dev/null +echo "Adding missing final new lines..." +find src/ -type f -name "*.c" -exec sed -i -e '$a\' {} \; +find assets/xml/ -type f -name "*.xml" -exec sed -i -e '$a\' {} \; +echo "Done formatting all files." diff --git a/soh/include/alloca.h b/soh/include/alloca.h new file mode 100644 index 000000000..9c6a0ab94 --- /dev/null +++ b/soh/include/alloca.h @@ -0,0 +1,8 @@ +#ifndef ALLOCA_H +#define ALLOCA_H + +void* alloca(u32); +//#define alloca __builtin_alloca +#define alloca malloc + +#endif diff --git a/soh/include/color.h b/soh/include/color.h new file mode 100644 index 000000000..1a833b0d4 --- /dev/null +++ b/soh/include/color.h @@ -0,0 +1,34 @@ +#ifndef COLOR_H +#define COLOR_H + +typedef struct { + u8 r, g, b; +} Color_RGB8; + +typedef struct { + u8 r, g, b, a; +} Color_RGBA8; + +// only use when necessary for alignment purposes +typedef union { + struct { + u8 a, b, g, r; + }; + u32 rgba; +} Color_RGBA8_u32; + +typedef struct { + f32 r, g, b, a; +} Color_RGBAf; + +typedef union { + struct { + u16 r : 5; + u16 g : 5; + u16 b : 5; + u16 a : 1; + }; + u16 rgba; +} Color_RGBA16; + +#endif diff --git a/soh/include/command_macros_base.h b/soh/include/command_macros_base.h new file mode 100644 index 000000000..bae557909 --- /dev/null +++ b/soh/include/command_macros_base.h @@ -0,0 +1,27 @@ +#ifndef COMMAND_MACROS_BASE_H +#define COMMAND_MACROS_BASE_H + +/** + * Command Base macros intended for use in designing of more specific command macros + * Each macro packs bytes (B), halfwords (H) and words (W, for consistency) into a single word + */ + +#define CMD_BBBB(a, b, c, d) (_SHIFTL(a, 0, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(c, 16, 8) | _SHIFTL(d, 24, 8)) + +#define CMD_BBH(a, b, c) (_SHIFTL(a, 0, 8) | _SHIFTL(b, 8, 8) | _SHIFTL(c, 16, 16)) + +#define CMD_HBB(a, b, c) (_SHIFTL(a, 0, 16) | _SHIFTL(b, 16, 8) | _SHIFTL(c, 24, 8)) + +#define CMD_HH(a, b) (_SHIFTL(a, 0, 16) | _SHIFTL(b, 16, 16)) + +#define CMD_W(a) (a) + +#if defined(__GNUC__) || defined(_MSC_VER) +#define CMD_F(a) {.f = (a)} +#else +#define CMD_F(a) {(a)} +#endif + +#define CMD_PTR(a) (uintptr_t)(a) + +#endif diff --git a/soh/include/fp.h b/soh/include/fp.h new file mode 100644 index 000000000..c733be914 --- /dev/null +++ b/soh/include/fp.h @@ -0,0 +1,34 @@ +#ifndef FP_H +#define FP_H +#include "ultra64.h" + +extern f32 qNaN0x3FFFFF; +extern f32 qNaN0x10000; +extern f32 sNaN0x3FFFFF; + +f32 floorf(f32 x); +f64 floor(f64 x); +s32 lfloorf(f32 x); +s32 lfloor(f64 x); + +f32 ceilf(f32 x); +f64 ceil(f64 x); +s32 lceilf(f32 x); +s32 lceil(f64 x); + +f32 truncf(f32 x); +f64 trunc(f64 x); +s32 ltruncf(f32 x); +s32 ltrunc(f64 x); + +f32 nearbyintf(f32 x); +f64 nearbyint(f64 x); +s32 lnearbyintf(f32 x); +s32 lnearbyint(f64 x); + +f32 roundf(f32 x); +f64 round(f64 x); +s32 lroundf(f32 x); +s32 lround(f64 x); + +#endif diff --git a/soh/include/functions.h b/soh/include/functions.h new file mode 100644 index 000000000..060022ac9 --- /dev/null +++ b/soh/include/functions.h @@ -0,0 +1,2404 @@ +#ifndef FUNCTIONS_H +#define FUNCTIONS_H + +#include "z64.h" + +#ifdef __cplusplus +#define this thisx +extern "C" +{ +#endif + +#include "../../libultraship/libultraship/luslog.h" + +#if defined(INCLUDE_GAME_PRINTF) && !defined(NDEBUG) +#define osSyncPrintf(fmt, ...) lusprintf(__FILE__, __LINE__, 0, fmt, __VA_ARGS__) +#else +#define osSyncPrintf(fmt, ...) osSyncPrintfUnused(fmt, __VA_ARGS__) +#endif + +f32 fabsf(f32 f); +#pragma intrinsic(fabsf) +f32 sqrtf(f32 f); +#pragma intrinsic(sqrtf) +f64 sqrt(f64 d); +#pragma intrinsic(sqrt) + +void gSPSegment(void* value, int segNum, uintptr_t target); +void gDPSetTextureImage(Gfx* pkt, u32 f, u32 s, u32 w, uintptr_t i); +void gSPDisplayList(Gfx* pkt, Gfx* dl); +void gSPDisplayListOffset(Gfx* pkt, Gfx* dl, int offset); +void gSPVertex(Gfx* pkt, uintptr_t v, int n, int v0); + +void cleararena(void); +void bootproc(void); +void Main_ThreadEntry(void* arg); +void Idle_ThreadEntry(void* arg); +void ViConfig_UpdateVi(u32 mode); +void ViConfig_UpdateBlack(void); +s32 DmaMgr_CompareName(const char* name1, const char* name2); +s32 DmaMgr_DmaRomToRam(uintptr_t rom, uintptr_t ram, size_t size); +s32 DmaMgr_DmaHandler(OSPiHandle* pihandle, OSIoMesg* mb, s32 direction); +void DmaMgr_Error(DmaRequest* req, const char* file, const char* errorName, const char* errorDesc); +const char* DmaMgr_GetFileNameImpl(uintptr_t vrom); +const char* DmaMgr_GetFileName(uintptr_t vrom); +void DmaMgr_ProcessMsg(DmaRequest* req); +void DmaMgr_ThreadEntry(void* arg0); +s32 DmaMgr_SendRequestImpl(DmaRequest* req, uintptr_t ram, uintptr_t vrom, size_t size, u32 unk, OSMesgQueue* queue, OSMesg msg); +s32 DmaMgr_SendRequest0(uintptr_t ram, uintptr_t vrom, size_t size); +void DmaMgr_Init(void); +s32 DmaMgr_SendRequest2(DmaRequest* req, uintptr_t ram, uintptr_t vrom, size_t size, u32 unk5, OSMesgQueue* queue, OSMesg msg, + const char* file, s32 line); +s32 DmaMgr_SendRequest1(void* ram0, uintptr_t vrom, size_t size, const char* file, s32 line); +void* Yaz0_FirstDMA(void); +void* Yaz0_NextDMA(void* curSrcPos); +void Yaz0_DecompressImpl(Yaz0Header* hdr, u8* dst); +void Yaz0_Decompress(uintptr_t romStart, void* dst, size_t size); +void Locale_Init(void); +void Locale_ResetRegion(void); +u32 func_80001F48(void); +u32 func_80001F8C(void); +u32 Locale_IsRegionNative(void); +void __assert(const char* exp, const char* file, s32 line); +void isPrintfInit(void); +void osSyncPrintfUnused(const char* fmt, ...); +//void osSyncPrintf(const char* fmt, ...); +void rmonPrintf(const char* fmt, ...); +void* is_proutSyncPrintf(void* arg, const char* str, u32 count); +void func_80002384(const char* exp, const char* file, u32 line); +OSPiHandle* osDriveRomInit(void); +void StackCheck_Init(StackEntry* entry, void* stackTop, void* stackBottom, u32 initValue, s32 minSpace, + const char* name); +void StackCheck_Cleanup(StackEntry* entry); +StackStatus StackCheck_GetState(StackEntry* entry); +u32 StackCheck_CheckAll(void); +u32 StackCheck_Check(StackEntry* entry); +f32 LogUtils_CheckFloatRange(const char* exp, s32 line, const char* valueName, f32 value, const char* minName, f32 min, + const char* maxName, f32 max); +s32 LogUtils_CheckIntRange(const char* exp, s32 line, const char* valueName, s32 value, const char* minName, s32 min, + const char* maxName, s32 max); +void LogUtils_LogHexDump(void* ptr, ptrdiff_t size0); +void LogUtils_LogPointer(s32 value, u32 max, void* ptr, const char* name, const char* file, s32 line); +void LogUtils_CheckBoundary(const char* name, s32 value, s32 unk, const char* file, s32 line); +void LogUtils_CheckNullPointer(const char* exp, void* ptr, const char* file, s32 line); +void LogUtils_CheckValidPointer(const char* exp, void* ptr, const char* file, s32 line); +void LogUtils_LogThreadId(const char* name, s32 line); +void LogUtils_HungupThread(const char* name, s32 line); +void LogUtils_ResetHungup(void); +s32 vsprintf(char* dst, const char* fmt, va_list args); +s32 sprintf(char* dst, const char* fmt, ...); +void __osPiCreateAccessQueue(void); +void __osPiGetAccess(void); +void __osPiRelAccess(void); +s32 osSendMesg(OSMesgQueue* mq, OSMesg mesg, s32 flag); +void osStopThread(OSThread* thread); +void osViExtendVStart(u32 arg0); +s32 osRecvMesg(OSMesgQueue* mq, OSMesg* msg, s32 flag); +void __osInitialize_common(void); +void __osInitialize_autodetect(void); +void __osExceptionPreamble(); +// ? __osException(?); +void __osEnqueueAndYield(OSThread**); +void __osEnqueueThread(OSThread**, OSThread*); +OSThread* __osPopThread(OSThread**); +// ? __osNop(?); +void __osDispatchThread(); +void __osCleanupThread(void); +void __osDequeueThread(OSThread** queue, OSThread* thread); +void osDestroyThread(OSThread* thread); +void osCreateThread(OSThread* thread, OSId id, void (*entry)(void*), void* arg, void* sp, OSPri pri); +void __osSetSR(u32); +u32 __osGetSR(); +void osWritebackDCache(void* vaddr, s32 nbytes); +void* osViGetNextFramebuffer(void); +void osCreatePiManager(OSPri pri, OSMesgQueue* cmdQ, OSMesg* cmdBuf, s32 cmdMsgCnt); +void __osDevMgrMain(void* arg); +s32 __osPiRawStartDma(s32 dir, u32 cartAddr, void* dramAddr, size_t size); +u32 osVirtualToPhysical(void* vaddr); +void osViBlack(u8 active); +s32 __osSiRawReadIo(void* devAddr, u32* dst); +OSId osGetThreadId(OSThread* thread); +void osViSetMode(OSViMode* mode); +u32 __osProbeTLB(void*); +u32 osGetMemSize(void); +void osSetEventMesg(OSEvent e, OSMesgQueue* mq, OSMesg msg); +s32 _Printf(PrintCallback, void* arg, const char* fmt, va_list ap); +void osUnmapTLBAll(void); +s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction); +void osInvalICache(void* vaddr, s32 nbytes); +void osCreateMesgQueue(OSMesgQueue* mq, OSMesg* msg, s32 count); +void osInvalDCache(void* vaddr, s32 nbytes); +s32 __osSiDeviceBusy(void); +s32 osJamMesg(OSMesgQueue* mq, OSMesg mesg, s32 flag); +void osSetThreadPri(OSThread* thread, OSPri pri); +OSPri osGetThreadPri(OSThread* thread); +s32 __osEPiRawReadIo(OSPiHandle* handle, u32 devAddr, u32* data); +void osViSwapBuffer(void* vaddr); +s32 __osEPiRawStartDma(OSPiHandle* handle, s32 direction, u32 cartAddr, void* dramAddr, size_t size); +void __osTimerServicesInit(void); +void __osTimerInterrupt(void); +void __osSetTimerIntr(OSTime time); +OSTime __osInsertTimer(OSTimer* timer); +#ifndef __cplusplus +void __osSetGlobalIntMask(OSHWIntr mask); +#endif +void __osSetCompare(u32); +#ifndef __cplusplus +void __osResetGlobalIntMask(OSHWIntr mask); +#endif +s32 __osDisableInt(void); +void __osRestoreInt(s32); +void __osViInit(void); +void __osViSwapContext(void); +OSMesgQueue* osPiGetCmdQueue(void); +s32 osEPiReadIo(OSPiHandle* handle, u32 devAddr, u32* data); +void osViSetSpecialFeatures(u32 func); +OSPiHandle* osCartRomInit(void); +void __osSetFpcCsr(u32); +u32 __osGetFpcCsr(); +s32 osEPiWriteIo(OSPiHandle* handle, u32 devAddr, u32 data); +void osMapTLBRdb(void); +void osYieldThread(void); +u32 __osGetCause(); +s32 __osEPiRawWriteIo(OSPiHandle* handle, u32 devAddr, u32 data); +void _Litob(_Pft* args, u8 type); +//ldiv_t ldiv(s32 num, s32 denom); +//lldiv_t lldiv(s64 num, s64 denom); +void _Ldtob(_Pft* args, u8 type); +s32 __osSiRawWriteIo(void* devAddr, u32 val); +void osCreateViManager(OSPri pri); +OSViContext* __osViGetCurrentContext(void); +void osStartThread(OSThread* thread); +void osViSetYScale(f32 scale); +void osViSetXScale(f32 value); +void __osSetWatchLo(u32); + +EnItem00* Item_DropCollectible(GlobalContext* globalCtx, Vec3f* spawnPos, s16 params); +EnItem00* Item_DropCollectible2(GlobalContext* globalCtx, Vec3f* spawnPos, s16 params); +void Item_DropCollectibleRandom(GlobalContext* globalCtx, Actor* fromActor, Vec3f* spawnPos, s16 params); +void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2); +void EffectBlure_AddSpace(EffectBlure* this); +void EffectBlure_Init1(void* thisx, void* initParamsx); +void EffectBlure_Init2(void* thisx, void* initParamsx); +void EffectBlure_Destroy(void* thisx); +s32 EffectBlure_Update(void* thisx); +void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx); +void EffectShieldParticle_Init(void* thisx, void* initParamsx); +void EffectShieldParticle_Destroy(void* thisx); +s32 EffectShieldParticle_Update(void* thisx); +void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx); +void EffectSpark_Init(void* thisx, void* initParamsx); +void EffectSpark_Destroy(void* thisx); +s32 EffectSpark_Update(void* thisx); +void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx); +void func_80026230(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3); +void func_80026400(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3); +void func_80026608(GlobalContext* globalCtx); +void func_80026690(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3); +void func_80026860(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3); +void func_80026A6C(GlobalContext* globalCtx); +GlobalContext* Effect_GetGlobalCtx(void); +void* Effect_GetByIndex(s32 index); +void Effect_InitContext(GlobalContext* globalCtx); +void Effect_Add(GlobalContext* globalCtx, s32* pIndex, s32 type, u8 arg3, u8 arg4, void* initParams); +void Effect_DrawAll(GraphicsContext* gfxCtx); +void Effect_UpdateAll(GlobalContext* globalCtx); +void Effect_Delete(GlobalContext* globalCtx, s32 index); +void Effect_DeleteAll(GlobalContext* globalCtx); +void EffectSs_InitInfo(GlobalContext* globalCtx, s32 tableSize); +void EffectSs_ClearAll(GlobalContext* globalCtx); +void EffectSs_Delete(EffectSs* effectSs); +void EffectSs_Reset(EffectSs* effectSs); +void EffectSs_Insert(GlobalContext* globalCtx, EffectSs* effectSs); +void EffectSs_Spawn(GlobalContext* globalCtx, s32 type, s32 priority, void* initParams); +void EffectSs_UpdateAll(GlobalContext* globalCtx); +void EffectSs_DrawAll(GlobalContext* globalCtx); +s16 func_80027DD4(s16 arg0, s16 arg1, s32 arg2); +s16 func_80027E34(s16 arg0, s16 arg1, f32 arg2); +u8 func_80027E84(u8 arg0, u8 arg1, f32 arg2); +void EffectSs_DrawGEffect(GlobalContext* globalCtx, EffectSs* this, void* texture); +void EffectSsDust_Spawn(GlobalContext* globalCtx, u16 drawFlags, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life, + u8 updateMode); +void func_8002829C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep); +void func_80028304(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep); +void func_8002836C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life); +void func_800283D4(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life); +void func_8002843C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life); +void func_800284A4(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep); +void func_80028510(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep); +void func_8002857C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void func_800285EC(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void func_8002865C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep); +void func_800286CC(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep); +void func_8002873C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 life); +void func_800287AC(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 life); +void func_8002881C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor); +void func_80028858(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor); +void func_80028990(GlobalContext* globalCtx, f32 randScale, Vec3f* srcPos); +void func_80028A54(GlobalContext* globalCtx, f32 randScale, Vec3f* srcPos); +void EffectSsKiraKira_SpawnSmallYellow(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void EffectSsKiraKira_SpawnSmall(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor); +void EffectSsKiraKira_SpawnDispersed(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s32 life); +void EffectSsKiraKira_SpawnFocused(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s32 life); +void EffectSsBomb_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void EffectSsBomb2_SpawnFade(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void EffectSsBomb2_SpawnLayered(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + s16 scaleStep); +void EffectSsBlast_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 sclaeStepDecay, s16 life); +void EffectSsBlast_SpawnWhiteCustomScale(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + s16 scaleStep, s16 life); +void EffectSsBlast_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 life); +void EffectSsBlast_SpawnWhiteShockwave(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void EffectSsGSpk_SpawnAccel(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 scaleStep); +void EffectSsGSpk_SpawnNoAccel(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 scaleStep); +void EffectSsGSpk_SpawnFuse(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void EffectSsGSpk_SpawnRandColor(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + s16 scale, s16 scaleStep); +void EffectSsGSpk_SpawnSmall(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor); +void EffectSsDFire_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 alpha, s16 fadeDelay, s32 life); +void EffectSsDFire_SpawnFixedScale(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 alpha, + s16 fadeDelay); +void EffectSsBubble_Spawn(GlobalContext* globalCtx, Vec3f* pos, f32 yPosOffset, f32 yPosRandScale, f32 xzPosRandScale, + f32 scale); +void EffectSsGRipple_Spawn(GlobalContext* globalCtx, Vec3f* pos, s16 radius, s16 radiusMax, s16 life); +void EffectSsGSplash_Spawn(GlobalContext* globalCtx, Vec3f* pos, Color_RGBA8* primColor, Color_RGBA8* envColor, + s16 type, s16 scale); +void EffectSsGMagma_Spawn(GlobalContext* globalCtx, Vec3f* pos); +void EffectSsGFire_Spawn(GlobalContext* globalCtx, Vec3f* pos); +void EffectSsLightning_Spawn(GlobalContext* globalCtx, Vec3f* pos, Color_RGBA8* primColor, Color_RGBA8* envColor, + s16 scale, s16 yaw, s16 life, s16 numBolts); +void EffectSsDtBubble_SpawnColorProfile(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + s16 life, s16 colorProfile, s16 randXZ); +void EffectSsDtBubble_SpawnCustomColor(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 life, s16 randXZ); +void EffectSsHahen_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 unused, s16 scale, + s16 objId, s16 life, Gfx* dList); +void EffectSsHahen_SpawnBurst(GlobalContext* globalCtx, Vec3f* pos, f32 burstScale, s16 unused, s16 scale, + s16 randScaleRange, s16 count, s16 objId, s16 life, Gfx* dList); +void EffectSsStick_Spawn(GlobalContext* globalCtx, Vec3f* pos, s16 yaw); +void EffectSsSibuki_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 moveDelay, + s16 direction, s16 scale); +void EffectSsSibuki_SpawnBurst(GlobalContext* globalCtx, Vec3f* pos); +void EffectSsSibuki2_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale); +void EffectSsGMagma2_Spawn(GlobalContext* globalCtx, Vec3f* pos, Color_RGBA8* primColor, Color_RGBA8* envColor, + s16 updateRate, s16 drawMode, s16 scale); +void EffectSsStone1_Spawn(GlobalContext* globalCtx, Vec3f* pos, s32 arg2); +void EffectSsHitMark_Spawn(GlobalContext* globalCtx, s32 type, s16 scale, Vec3f* pos); +void EffectSsHitMark_SpawnFixedScale(GlobalContext* globalCtx, s32 type, Vec3f* pos); +void EffectSsHitMark_SpawnCustomScale(GlobalContext* globalCtx, s32 type, s16 scale, Vec3f* pos); +void EffectSsFhgFlash_SpawnLightBall(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + u8 param); +void EffectSsFhgFlash_SpawnShock(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 scale, u8 param); +void EffectSsKFire_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scaleMax, u8 type); +void EffectSsSolderSrchBall_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 unused, + s16* linkDetected); +void EffectSsKakera_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* arg3, s16 gravity, s16 arg5, + s16 arg6, s16 arg7, s16 arg8, s16 scale, s16 arg10, s16 arg11, s32 life, s16 colorIdx, + s16 objId, Gfx* dList); +void EffectSsIcePiece_Spawn(GlobalContext* globalCtx, Vec3f* pos, f32 scale, Vec3f* velocity, Vec3f* accel, s32 life); +void EffectSsIcePiece_SpawnBurst(GlobalContext* globalCtx, Vec3f* refPos, f32 scale); +void EffectSsEnIce_SpawnFlyingVec3f(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 primR, s16 primG, s16 primB, + s16 primA, s16 envR, s16 envG, s16 envB, f32 scale); +void EffectSsEnIce_SpawnFlyingVec3s(GlobalContext* globalCtx, Actor* actor, Vec3s* pos, s16 primR, s16 primG, s16 primB, + s16 primA, s16 envR, s16 envG, s16 envB, f32 scale); +void EffectSsEnIce_Spawn(GlobalContext* arg0, Vec3f* pos, f32 scale, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s32 life); +void EffectSsFireTail_Spawn(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, f32 scale, Vec3f* arg4, s16 arg5, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 type, s16 bodyPart, s32 life); +void EffectSsFireTail_SpawnFlame(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, f32 arg3, s16 bodyPart, + f32 colorIntensity); +void EffectSsFireTail_SpawnFlameOnPlayer(GlobalContext* globalCtx, f32 scale, s16 bodyPart, f32 colorIntensity); +void EffectSsEnFire_SpawnVec3f(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 scale, s16 unk_12, s16 flags, + s16 bodyPart); +void EffectSsEnFire_SpawnVec3s(GlobalContext* globalCtx, Actor* actor, Vec3s* vec, s16 scale, s16 arg4, s16 flags, + s16 bodyPart); +void EffectSsExtra_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scoreIdx); +void EffectSsFCircle_Spawn(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 radius, s16 height); +void EffectSsDeadDb_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 primR, s16 primG, s16 primB, s16 primA, s16 envR, s16 envG, s16 envB, s16 unused, + s32 arg14, s16 playSound); +void EffectSsDeadDd_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 primR, s16 primG, s16 primB, s16 alpha, s16 envR, s16 envG, s16 envB, s16 alphaStep, + s32 life); +void EffectSsDeadDd_SpawnRandYellow(GlobalContext* globalCtx, Vec3f* pos, s16 scale, s16 scaleStep, f32 randPosScale, + s32 randIter, s32 life); +void EffectSsDeadDs_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 alpha, s32 life); +void EffectSsDeadDs_SpawnStationary(GlobalContext* globalCtx, Vec3f* pos, s16 scale, s16 scaleStep, s16 alpha, + s32 life); +void EffectSsDeadSound_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u16 sfxId, + s16 lowerPriority, s16 repeatMode, s32 life); +void EffectSsDeadSound_SpawnStationary(GlobalContext* globalCtx, Vec3f* pos, u16 sfxId, s16 lowerPriority, + s16 repeatMode, s32 life); +void EffectSsIceSmoke_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale); +void FlagSet_Update(GlobalContext* globalCtx); +void Overlay_LoadGameState(GameStateOverlay* overlayEntry); +void Overlay_FreeGameState(GameStateOverlay* overlayEntry); +void ActorShape_Init(ActorShape* shape, f32 yOffset, ActorShadowFunc shadowDraw, f32 shadowScale); +void ActorShadow_DrawCircle(Actor* actor, Lights* lights, GlobalContext* globalCtx); +void ActorShadow_DrawWhiteCircle(Actor* actor, Lights* lights, GlobalContext* globalCtx); +void ActorShadow_DrawHorse(Actor* actor, Lights* lights, GlobalContext* globalCtx); +void ActorShadow_DrawFeet(Actor* actor, Lights* lights, GlobalContext* globalCtx); +void Actor_SetFeetPos(Actor* actor, s32 limbIndex, s32 leftFootIndex, Vec3f* leftFootPos, s32 rightFootIndex, + Vec3f* rightFootPos); +void func_8002BE04(GlobalContext* globalCtx, Vec3f* arg1, Vec3f* arg2, f32* arg3); +void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx); +s32 Flags_GetSwitch(GlobalContext* globalCtx, s32 flag); +void Flags_SetSwitch(GlobalContext* globalCtx, s32 flag); +void Flags_UnsetSwitch(GlobalContext* globalCtx, s32 flag); +s32 Flags_GetUnknown(GlobalContext* globalCtx, s32 flag); +void Flags_SetUnknown(GlobalContext* globalCtx, s32 flag); +void Flags_UnsetUnknown(GlobalContext* globalCtx, s32 flag); +s32 Flags_GetTreasure(GlobalContext* globalCtx, s32 flag); +void Flags_SetTreasure(GlobalContext* globalCtx, s32 flag); +s32 Flags_GetClear(GlobalContext* globalCtx, s32 flag); +void Flags_SetClear(GlobalContext* globalCtx, s32 flag); +void Flags_UnsetClear(GlobalContext* globalCtx, s32 flag); +s32 Flags_GetTempClear(GlobalContext* globalCtx, s32 flag); +void Flags_SetTempClear(GlobalContext* globalCtx, s32 flag); +void Flags_UnsetTempClear(GlobalContext* globalCtx, s32 flag); +s32 Flags_GetCollectible(GlobalContext* globalCtx, s32 flag); +void Flags_SetCollectible(GlobalContext* globalCtx, s32 flag); +void TitleCard_InitBossName(GlobalContext* globalCtx, TitleCardContext* titleCtx, void* texture, s16 x, s16 y, u8 width, + u8 height); +void TitleCard_InitPlaceName(GlobalContext* globalCtx, TitleCardContext* titleCtx, void* texture, s32 x, s32 y, + s32 width, s32 height, s32 delay); +s32 func_8002D53C(GlobalContext* globalCtx, TitleCardContext* titleCtx); +void Actor_Kill(Actor* actor); +void Actor_SetFocus(Actor* actor, f32 offset); +void Actor_SetScale(Actor* actor, f32 scale); +void Actor_SetObjectDependency(GlobalContext* globalCtx, Actor* actor); +void func_8002D7EC(Actor* actor); +void func_8002D868(Actor* actor); +void Actor_MoveForward(Actor* actor); +void func_8002D908(Actor* actor); +void func_8002D97C(Actor* actor); +void func_8002D9A4(Actor* actor, f32 arg1); +s16 Actor_WorldYawTowardActor(Actor* actorA, Actor* actorB); +s16 Actor_WorldYawTowardPoint(Actor* actor, Vec3f* refPoint); +f32 Actor_WorldDistXYZToActor(Actor* actorA, Actor* actorB); +f32 Actor_WorldDistXYZToPoint(Actor* actor, Vec3f* refPoint); +s16 Actor_WorldPitchTowardActor(Actor* actorA, Actor* actorB); +s16 Actor_WorldPitchTowardPoint(Actor* actor, Vec3f* refPoint); +f32 Actor_WorldDistXZToActor(Actor* actorA, Actor* actorB); +f32 Actor_WorldDistXZToPoint(Actor* actor, Vec3f* refPoint); +void func_8002DBD0(Actor* actor, Vec3f* result, Vec3f* arg2); +f32 Actor_HeightDiff(Actor* actorA, Actor* actorB); +f32 Player_GetHeight(Player* player); +f32 func_8002DCE4(Player* player); +s32 func_8002DD6C(Player* player); +s32 func_8002DD78(Player* player); +s32 func_8002DDE4(GlobalContext* globalCtx); +s32 func_8002DDF4(GlobalContext* globalCtx); +void func_8002DE04(GlobalContext* globalCtx, Actor* actorA, Actor* actorB); +void func_8002DE74(GlobalContext* globalCtx, Player* player); +void Actor_MountHorse(GlobalContext* globalCtx, Player* player, Actor* horse); +s32 func_8002DEEC(Player* player); +void func_8002DF18(GlobalContext* globalCtx, Player* player); +s32 func_8002DF38(GlobalContext* globalCtx, Actor* actor, u8 csMode); +s32 func_8002DF54(GlobalContext* globalCtx, Actor* actor, u8 arg2); +void func_8002DF90(DynaPolyActor* dynaActor); +void func_8002DFA4(DynaPolyActor* dynaActor, f32 arg1, s16 arg2); +s32 Player_IsFacingActor(Actor* actor, s16 angle, GlobalContext* globalCtx); +s32 Actor_ActorBIsFacingActorA(Actor* actorA, Actor* actorB, s16 angle); +s32 Actor_IsFacingPlayer(Actor* actor, s16 angle); +s32 Actor_ActorAIsFacingActorB(Actor* actorA, Actor* actorB, s16 angle); +s32 Actor_IsFacingAndNearPlayer(Actor* actor, f32 range, s16 angle); +s32 Actor_ActorAIsFacingAndNearActorB(Actor* actorA, Actor* actorB, f32 range, s16 angle); +void Actor_UpdateBgCheckInfo(GlobalContext* globalCtx, Actor* actor, f32 wallCheckHeight, f32 wallCheckRadius, + f32 ceilingCheckHeight, s32 flags); +Hilite* func_8002EABC(Vec3f* object, Vec3f* eye, Vec3f* lightDir, GraphicsContext* gfxCtx); +Hilite* func_8002EB44(Vec3f* object, Vec3f* eye, Vec3f* lightDir, GraphicsContext* gfxCtx); +void func_8002EBCC(Actor* actor, GlobalContext* globalCtx, s32 flag); +void func_8002ED80(Actor* actor, GlobalContext* globalCtx, s32 flag); +PosRot* Actor_GetFocus(PosRot* arg0, Actor* actor); +PosRot* Actor_GetWorld(PosRot* arg0, Actor* actor); +PosRot* Actor_GetWorldPosShapeRot(PosRot* arg0, Actor* actor); +s32 func_8002F0C8(Actor* actor, Player* player, s32 arg2); +u32 Actor_ProcessTalkRequest(Actor* actor, GlobalContext* globalCtx); +s32 func_8002F1C4(Actor* actor, GlobalContext* globalCtx, f32 arg2, f32 arg3, u32 arg4); +s32 func_8002F298(Actor* actor, GlobalContext* globalCtx, f32 arg2, u32 arg3); +s32 func_8002F2CC(Actor* actor, GlobalContext* globalCtx, f32 arg2); +s32 func_8002F2F4(Actor* actor, GlobalContext* globalCtx); +u32 Actor_TextboxIsClosing(Actor* actor, GlobalContext* globalCtx); +s8 func_8002F368(GlobalContext* globalCtx); +void Actor_GetScreenPos(GlobalContext* globalCtx, Actor* actor, s16* x, s16* y); +u32 Actor_HasParent(Actor* actor, GlobalContext* globalCtx); +s32 func_8002F434(Actor* actor, GlobalContext* globalCtx, s32 getItemId, f32 xzRange, f32 yRange); +void func_8002F554(Actor* actor, GlobalContext* globalCtx, s32 getItemId); +void func_8002F580(Actor* actor, GlobalContext* globalCtx); +u32 Actor_HasNoParent(Actor* actor, GlobalContext* globalCtx); +void func_8002F5C4(Actor* actorA, Actor* actorB, GlobalContext* globalCtx); +void func_8002F5F0(Actor* actor, GlobalContext* globalCtx); +s32 Actor_IsMounted(GlobalContext* globalCtx, Actor* horse); +u32 Actor_SetRideActor(GlobalContext* globalCtx, Actor* horse, s32 arg2); +s32 Actor_NotMounted(GlobalContext* globalCtx, Actor* horse); +void func_8002F698(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6); +void func_8002F6D4(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5); +void func_8002F71C(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4); +void func_8002F758(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5); +void func_8002F7A0(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4); +void func_8002F7DC(Actor* actor, u16 sfxId); +void Audio_PlayActorSound2(Actor* actor, u16 sfxId); +void func_8002F850(GlobalContext* globalCtx, Actor* actor); +void func_8002F8F0(Actor* actor, u16 sfxId); +void func_8002F91C(Actor* actor, u16 sfxId); +void func_8002F948(Actor* actor, u16 sfxId); +void func_8002F974(Actor* actor, u16 sfxId); +void func_8002F994(Actor* actor, s32 arg1); +s32 func_8002F9EC(GlobalContext* globalCtx, Actor* actor, CollisionPoly* poly, s32 bgId, Vec3f* pos); +void func_800304B0(GlobalContext* globalCtx); +void func_800304DC(GlobalContext* globalCtx, ActorContext* actorCtx, ActorEntry* actorEntry); +void Actor_UpdateAll(GlobalContext* globalCtx, ActorContext* actorCtx); +s32 func_800314D4(GlobalContext* globalCtx, Actor* actorB, Vec3f* arg2, f32 arg3); +void func_800315AC(GlobalContext* globalCtx, ActorContext* actorCtx); +void func_80031A28(GlobalContext* globalCtx, ActorContext* actorCtx); +void func_80031B14(GlobalContext* globalCtx, ActorContext* actorCtx); +void func_80031C3C(ActorContext* actorCtx, GlobalContext* globalCtx); +Actor* Actor_Spawn(ActorContext* actorCtx, GlobalContext* globalCtx, s16 actorId, f32 posX, f32 posY, f32 posZ, + s16 rotX, s16 rotY, s16 rotZ, s16 params); +Actor* Actor_SpawnAsChild(ActorContext* actorCtx, Actor* parent, GlobalContext* globalCtx, s16 actorId, f32 posX, + f32 posY, f32 posZ, s16 rotX, s16 rotY, s16 rotZ, s16 params); +void Actor_SpawnTransitionActors(GlobalContext* globalCtx, ActorContext* actorCtx); +Actor* Actor_SpawnEntry(ActorContext* actorCtx, ActorEntry* actorEntry, GlobalContext* globalCtx); +Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, GlobalContext* globalCtx); +Actor* func_80032AF0(GlobalContext* globalCtx, ActorContext* actorCtx, Actor** actorPtr, Player* player); +Actor* Actor_Find(ActorContext* actorCtx, s32 actorId, s32 actorCategory); +void Enemy_StartFinishingBlow(GlobalContext* globalCtx, Actor* actor); +s16 func_80032CB4(s16* arg0, s16 arg1, s16 arg2, s16 arg3); +void BodyBreak_Alloc(BodyBreak* bodyBreak, s32 count, GlobalContext* globalCtx); +void BodyBreak_SetInfo(BodyBreak* bodyBreak, s32 limbIndex, s32 minLimbIndex, s32 maxLimbIndex, u32 count, Gfx** dList, + s16 objectId); +s32 BodyBreak_SpawnParts(Actor* actor, BodyBreak* bodyBreak, GlobalContext* globalCtx, s16 type); +void Actor_SpawnFloorDustRing(GlobalContext* globalCtx, Actor* actor, Vec3f* posXZ, f32 radius, s32 amountMinusOne, + f32 randAccelWeight, s16 scale, s16 scaleStep, u8 useLighting); +void func_80033480(GlobalContext* globalCtx, Vec3f* posBase, f32 randRangeDiameter, s32 amountMinusOne, s16 scaleBase, + s16 scaleStep, u8 arg6); +Actor* Actor_GetCollidedExplosive(GlobalContext* globalCtx, Collider* collider); +Actor* func_80033684(GlobalContext* globalCtx, Actor* explosiveActor); +Actor* Actor_GetProjectileActor(GlobalContext* globalCtx, Actor* refActor, f32 radius); +void Actor_ChangeCategory(GlobalContext* globalCtx, ActorContext* actorCtx, Actor* actor, u8 actorCategory); +void Actor_SetTextWithPrefix(GlobalContext* globalCtx, Actor* actor, s16 textIdLower); +s16 Actor_TestFloorInDirection(Actor* actor, GlobalContext* globalCtx, f32 distance, s16 angle); +s32 Actor_IsTargeted(GlobalContext* globalCtx, Actor* actor); +s32 Actor_OtherIsTargeted(GlobalContext* globalCtx, Actor* actor); +f32 func_80033AEC(Vec3f* arg0, Vec3f* arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5); +void func_80033C30(Vec3f* arg0, Vec3f* arg1, u8 alpha, GlobalContext* globalCtx); +void func_80033DB8(GlobalContext* globalCtx, s16 arg1, s16 arg2); +void func_80033E1C(GlobalContext* globalCtx, s16 arg1, s16 arg2, s16 arg3); +void func_80033E88(Actor* actor, GlobalContext* globalCtx, s16 arg2, s16 arg3); +f32 Rand_ZeroFloat(f32 f); +f32 Rand_CenteredFloat(f32 f); +void Actor_DrawDoorLock(GlobalContext* globalCtx, s32 arg1, s32 arg2); +void func_8003424C(GlobalContext* globalCtx, Vec3f* arg1); +void Actor_SetColorFilter(Actor* actor, s16 colorFlag, s16 colorIntensityMax, s16 xluFlag, s16 duration); +Hilite* func_800342EC(Vec3f* object, GlobalContext* globalCtx); +Hilite* func_8003435C(Vec3f* object, GlobalContext* globalCtx); +s32 func_800343CC(GlobalContext* globalCtx, Actor* actor, s16* arg2, f32 interactRange, + u16 (*unkFunc1)(GlobalContext*, Actor*), s16 (*unkFunc2)(GlobalContext*, Actor*)); +s16 func_800347E8(s16 arg0); +void func_80034A14(Actor* actor, struct_80034A14_arg1* arg1, s16 arg2, s16 arg3); +void func_80034BA0(GlobalContext* globalCtx, SkelAnime* skelAnime, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, Actor* actor, s16 alpha); +void func_80034CC4(GlobalContext* globalCtx, SkelAnime* skelAnime, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, Actor* actor, s16 alpha); +s16 func_80034DD4(Actor* actor, GlobalContext* globalCtx, s16 arg2, f32 arg3); +void Animation_ChangeByInfo(SkelAnime* skelAnime, AnimationInfo* animationInfo, s32 index); +void func_80034F54(GlobalContext* globalCtx, s16* arg1, s16* arg2, s32 arg3); +void Actor_Noop(Actor* actor, GlobalContext* globalCtx); +void Gfx_DrawDListOpa(GlobalContext* globalCtx, Gfx* dlist); +void Gfx_DrawDListXlu(GlobalContext* globalCtx, Gfx* dlist); +Actor* Actor_FindNearby(GlobalContext* globalCtx, Actor* refActor, s16 actorId, u8 actorCategory, f32 range); +s32 func_800354B4(GlobalContext* globalCtx, Actor* actor, f32 range, s16 arg3, s16 arg4, s16 arg5); +void func_8003555C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel); +void func_800355B8(GlobalContext* globalCtx, Vec3f* pos); +u8 func_800355E4(GlobalContext* globalCtx, Collider* collider); +u8 Actor_ApplyDamage(Actor* actor); +void Actor_SetDropFlag(Actor* actor, ColliderInfo* colBody, s32 freezeFlag); +void Actor_SetDropFlagJntSph(Actor* actor, ColliderJntSph* colBody, s32 freezeFlag); +void func_80035844(Vec3f* arg0, Vec3f* arg1, Vec3s* arg2, s32 arg3); +Actor* func_800358DC(Actor* actor, Vec3f* spawnPos, Vec3s* spawnRot, f32* arg3, s32 timer, s16* unused, + GlobalContext* globalCtx, s16 params, s32 arg8); +void func_800359B8(Actor* actor, s16 arg1, Vec3s* arg2); +s32 Flags_GetEventChkInf(s32 flag); +void Flags_SetEventChkInf(s32 flag); +s32 Flags_GetInfTable(s32 flag); +void Flags_SetInfTable(s32 flag); +u16 func_80037C30(GlobalContext* globalCtx, s16 arg1); +s32 func_80037D98(GlobalContext* globalCtx, Actor* actor, s16 arg2, s32* arg3); +s32 func_80038290(GlobalContext* globalCtx, Actor* actor, Vec3s* arg2, Vec3s* arg3, Vec3f arg4); +void ActorOverlayTable_LogPrint(void); +void ActorOverlayTable_Init(void); +void ActorOverlayTable_Cleanup(void); +// ? func_80038600(?); +u16 DynaSSNodeList_GetNextNodeIdx(DynaSSNodeList*); +void func_80038A28(CollisionPoly* poly, f32 tx, f32 ty, f32 tz, MtxF* dest); +f32 CollisionPoly_GetPointDistanceFromPlane(CollisionPoly* poly, Vec3f* point); +void CollisionPoly_GetVerticesByBgId(CollisionPoly* poly, s32 bgId, CollisionContext* colCtx, Vec3f* dest); +s32 BgCheck_CheckStaticCeiling(StaticLookup* lookup, u16 xpFlags, CollisionContext* colCtx, f32* outY, Vec3f* pos, + f32 checkHeight, CollisionPoly** outPoly); +s32 BgCheck_CheckLineAgainstSSList(SSList* headNodeId, CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, + Vec3f* posA, Vec3f* posB, Vec3f* outPos, CollisionPoly** outPoly, f32* outDistSq, + f32 chkDist, s32 bccFlags); +void BgCheck_GetStaticLookupIndicesFromPos(CollisionContext* colCtx, Vec3f* pos, Vec3i* arg2); +void BgCheck_Allocate(CollisionContext* colCtx, GlobalContext* globalCtx, CollisionHeader* colHeader); +s32 BgCheck_PosInStaticBoundingBox(CollisionContext* colCtx, Vec3f* pos); +f32 BgCheck_EntityRaycastFloor1(CollisionContext* colCtx, CollisionPoly** outPoly, Vec3f* pos); +f32 BgCheck_EntityRaycastFloor2(GlobalContext* globalCtx, CollisionContext* colCtx, CollisionPoly** outPoly, + Vec3f* pos); +f32 BgCheck_EntityRaycastFloor3(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos); +f32 BgCheck_EntityRaycastFloor4(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, + Vec3f* arg4); +f32 BgCheck_EntityRaycastFloor5(GlobalContext* globalCtx, CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, + Actor* actor, Vec3f* pos); +f32 BgCheck_EntityRaycastFloor6(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, Vec3f* pos, + f32 chkDist); +f32 BgCheck_EntityRaycastFloor7(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, Vec3f* pos); +f32 BgCheck_AnyRaycastFloor1(CollisionContext* colCtx, CollisionPoly* outPoly, Vec3f* pos); +f32 BgCheck_AnyRaycastFloor2(CollisionContext* colCtx, CollisionPoly* outPoly, s32* bgId, Vec3f* pos); +f32 BgCheck_CameraRaycastFloor2(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos); +f32 BgCheck_EntityRaycastFloor8(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, Vec3f* pos); +f32 BgCheck_EntityRaycastFloor9(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos); +s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, + f32 radius, CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight, u8 argA); +s32 BgCheck_EntitySphVsWall1(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, f32 checkHeight); +s32 BgCheck_EntitySphVsWall2(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, s32* outBgId, f32 checkHeight); +s32 BgCheck_EntitySphVsWall3(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight); +s32 BgCheck_EntitySphVsWall4(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight); +s32 BgCheck_AnyCheckCeiling(CollisionContext* colCtx, f32* outY, Vec3f* pos, f32 checkHeight); +s32 BgCheck_EntityCheckCeiling(CollisionContext* colCtx, f32* arg1, Vec3f* arg2, f32 arg3, CollisionPoly** outPoly, + s32* outBgId, Actor* actor); +s32 BgCheck_CheckLineImpl(CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, Vec3f* posA, Vec3f* posB, + Vec3f* posResult, CollisionPoly** outPoly, s32* bgId, Actor* actor, f32 chkDist, + u32 bccFlags); +s32 BgCheck_CameraLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId); +s32 BgCheck_CameraLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId); +s32 BgCheck_EntityLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId); +s32 BgCheck_EntityLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId, + Actor* actor); +s32 BgCheck_EntityLineTest3(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId, + Actor* actor, f32 chkDist); +s32 BgCheck_ProjectileLineTest(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, + s32* bgId); +s32 BgCheck_AnyLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly, + s32 chkOneFace); +s32 BgCheck_AnyLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly, + s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace); +s32 BgCheck_AnyLineTest3(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly, + s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId); +s32 BgCheck_SphVsFirstPoly(CollisionContext* colCtx, Vec3f* center, f32 radius); +void SSNodeList_Initialize(SSNodeList*); +void SSNodeList_Alloc(GlobalContext* globalCtx, SSNodeList* this, s32 tblMax, s32 numPolys); +u16 SSNodeList_GetNextNodeIdx(SSNodeList* this); +s32 DynaPoly_IsBgIdBgActor(s32 bgId); +void DynaPoly_Init(GlobalContext* globalCtx, DynaCollisionContext* dyna); +void DynaPoly_Alloc(GlobalContext* globalCtx, DynaCollisionContext* dyna); +void func_8003EBF8(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId); +void func_8003EC50(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId); +void func_8003ECA8(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId); +s32 DynaPoly_SetBgActor(GlobalContext* globalCtx, DynaCollisionContext* dyna, Actor* actor, CollisionHeader* colHeader); +DynaPolyActor* DynaPoly_GetActor(CollisionContext* colCtx, s32 bgId); +void DynaPoly_DeleteBgActor(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId); +void func_8003EE6C(GlobalContext* globalCtx, DynaCollisionContext* dyna); +void func_8003F8EC(GlobalContext* globalCtx, DynaCollisionContext* dyna, Actor* actor); +void DynaPoly_Setup(GlobalContext* globalCtx, DynaCollisionContext* dyna); +void DynaPoly_UpdateBgActorTransforms(GlobalContext* globalCtx, DynaCollisionContext* dyna); +f32 BgCheck_RaycastFloorDyna(DynaRaycast* dynaRaycast); +s32 BgCheck_SphVsDynaWall(CollisionContext* colCtx, u16 xpFlags, f32* outX, f32* outZ, Vec3f* pos, f32 radius, + CollisionPoly** outPoly, s32* outBgId, Actor* actor); +s32 BgCheck_CheckDynaCeiling(CollisionContext* colCtx, u16 xpFlags, f32* outY, Vec3f* pos, f32 chkDist, + CollisionPoly** outPoly, s32* outBgId, Actor* actor); +s32 BgCheck_CheckLineAgainstDyna(CollisionContext* colCtx, u16 xpFlags, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, f32* distSq, s32* outBgId, Actor* actor, f32 chkDist, + s32 bccFlags); +s32 BgCheck_SphVsFirstDynaPoly(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, s32* outBgId, + Vec3f* center, f32 radius, Actor* actor, u16 bciFlags); +void CollisionHeader_GetVirtual(void* colHeader, CollisionHeader** dest); +void func_800418D0(CollisionContext* colCtx, GlobalContext* globalCtx); +void BgCheck_ResetPolyCheckTbl(SSNodeList* nodeList, s32 numPolys); +u32 SurfaceType_GetCamDataIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u16 func_80041A4C(CollisionContext* colCtx, u32 camId, s32 bgId); +u16 SurfaceType_GetCameraSType(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u16 SurfaceType_GetNumCameras(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +Vec3s* func_80041C10(CollisionContext* colCtx, s32 camId, s32 bgId); +Vec3s* SurfaceType_GetCamPosData(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_GetSceneExitIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 func_80041D4C(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 func_80041D70(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 func_80041DB8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 func_80041DE4(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 func_80041E18(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 func_80041E4C(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 func_80041EA4(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 func_80041EC8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_IsHorseBlocked(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 func_80041F10(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u16 SurfaceType_GetSfx(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_GetSlope(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_GetLightSettingIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_GetEcho(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_IsHookshotSurface(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 SurfaceType_IsIgnoredByEntities(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 SurfaceType_IsIgnoredByProjectiles(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 SurfaceType_IsConveyor(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_GetConveyorSpeed(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_GetConveyorDirection(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +u32 SurfaceType_IsWallDamage(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId); +s32 WaterBox_GetSurface1(GlobalContext* globalCtx, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface, + WaterBox** outWaterBox); +s32 WaterBox_GetSurface2(GlobalContext* globalCtx, CollisionContext* colCtx, Vec3f* pos, f32 surfaceChkDist, + WaterBox** outWaterBox); +s32 WaterBox_GetSurfaceImpl(GlobalContext* globalCtx, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface, + WaterBox** outWaterBox); +u32 WaterBox_GetCamDataIndex(CollisionContext* colCtx, WaterBox* waterBox); +u16 WaterBox_GetCameraSType(CollisionContext* colCtx, WaterBox* waterBox); +u32 WaterBox_GetLightSettingIndex(CollisionContext* colCtx, WaterBox* waterBox); +s32 func_80042708(CollisionPoly* polyA, CollisionPoly* polyB, Vec3f* point, Vec3f* closestPoint); +s32 func_800427B4(CollisionPoly* polyA, CollisionPoly* polyB, Vec3f* pointA, Vec3f* pointB, Vec3f* closestPoint); +void BgCheck_DrawDynaCollision(GlobalContext*, CollisionContext*); +void BgCheck_DrawStaticCollision(GlobalContext*, CollisionContext*); +void func_80043334(CollisionContext* colCtx, Actor* actor, s32 bgId); +s32 func_800433A4(CollisionContext* colCtx, s32 bgId, Actor* actor); +void DynaPolyActor_Init(DynaPolyActor* dynaActor, s32 flags); +void func_800434A0(DynaPolyActor* dynaActor); +void func_800434A8(DynaPolyActor* dynaActor); +void func_800434C8(CollisionContext* colCtx, s32 floorBgId); +void func_80043508(CollisionContext* colCtx, s32 floorBgId); +void func_80043538(DynaPolyActor* dynaActor); +s32 func_80043548(DynaPolyActor* dynaActor); +s32 func_8004356C(DynaPolyActor* dynaActor); +s32 func_80043590(DynaPolyActor* dynaActor); +s32 func_800435B4(DynaPolyActor* dynaActor); +s32 func_800435D8(GlobalContext* globalCtx, DynaPolyActor* dynaActor, s16 arg2, s16 arg3, s16 arg4); +void Camera_Init(Camera* camera, View* view, CollisionContext* colCtx, GlobalContext* globalCtx); +void Camera_InitPlayerSettings(Camera* camera, Player* player); +s16 Camera_ChangeStatus(Camera* camera, s16 status); +Vec3s Camera_Update(Camera* camera); +void Camera_Finish(Camera* camera); +s32 Camera_ChangeMode(Camera* camera, s16 mode); +s32 Camera_CheckValidMode(Camera* camera, s16 mode); +s32 Camera_ChangeSetting(Camera* camera, s16 setting); +s32 Camera_ChangeDataIdx(Camera* camera, s32 camDataIdx); +s16 Camera_GetInputDirYaw(Camera* camera); +Vec3s* Camera_GetCamDir(Vec3s* dir, Camera* camera); +s16 Camera_GetCamDirPitch(Camera* camera); +s16 Camera_GetCamDirYaw(Camera* camera); +s32 Camera_AddQuake(Camera* camera, s32 arg1, s16 y, s32 countdown); +s32 Camera_SetParam(Camera* camera, s32 param, void* value); +s32 func_8005AC48(Camera* camera, s16 arg1); +s16 func_8005ACFC(Camera* camera, s16 arg1); +s16 func_8005AD1C(Camera* camera, s16 arg1); +s32 Camera_ResetAnim(Camera* camera); +s32 Camera_SetCSParams(Camera* camera, CutsceneCameraPoint* atPoints, CutsceneCameraPoint* eyePoints, Player* player, + s16 relativeToPlayer); +s32 Camera_ChangeDoorCam(Camera* camera, Actor* doorActor, s16 camDataIdx, f32 arg3, s16 timer1, s16 timer2, + s16 timer3); +s32 Camera_Copy(Camera* dstCamera, Camera* srcCamera); +Vec3f* Camera_GetSkyboxOffset(Vec3f* dst, Camera* camera); +void Camera_SetCameraData(Camera* camera, s16 setDataFlags, void* data0, void* data1, s16 data2, s16 data3, + UNK_TYPE arg6); +s32 func_8005B198(void); +s16 func_8005B1A4(Camera* camera); +DamageTable* DamageTable_Get(s32 index); +void DamageTable_Clear(DamageTable* table); +void Collider_DrawRedPoly(GraphicsContext* gfxCtx, Vec3f* vA, Vec3f* vB, Vec3f* vC); +void Collider_DrawPoly(GraphicsContext* gfxCtx, Vec3f* vA, Vec3f* vB, Vec3f* vC, u8 r, u8 g, u8 b); +s32 Collider_InitJntSph(GlobalContext* globalCtx, ColliderJntSph* collider); +s32 Collider_FreeJntSph(GlobalContext* globalCtx, ColliderJntSph* collider); +s32 Collider_DestroyJntSph(GlobalContext* globalCtx, ColliderJntSph* collider); +s32 Collider_SetJntSphToActor(GlobalContext* globalCtx, ColliderJntSph* dest, ColliderJntSphInitToActor* src); +s32 Collider_SetJntSphAllocType1(GlobalContext* globalCtx, ColliderJntSph* dest, Actor* actor, + ColliderJntSphInitType1* src); +s32 Collider_SetJntSphAlloc(GlobalContext* globalCtx, ColliderJntSph* dest, Actor* actor, ColliderJntSphInit* src); +s32 Collider_SetJntSph(GlobalContext* globalCtx, ColliderJntSph* dest, Actor* actor, ColliderJntSphInit* src, + ColliderJntSphElement* elements); +s32 Collider_ResetJntSphAT(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetJntSphAC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetJntSphOC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_InitCylinder(GlobalContext* globalCtx, ColliderCylinder* collider); +s32 Collider_DestroyCylinder(GlobalContext* globalCtx, ColliderCylinder* collider); +s32 Collider_SetCylinderToActor(GlobalContext* globalCtx, ColliderCylinder* collider, ColliderCylinderInitToActor* src); +s32 Collider_SetCylinderType1(GlobalContext* globalCtx, ColliderCylinder* collider, Actor* actor, + ColliderCylinderInitType1* src); +s32 Collider_SetCylinder(GlobalContext* globalCtx, ColliderCylinder* collider, Actor* actor, ColliderCylinderInit* src); +s32 Collider_ResetCylinderAT(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetCylinderAC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetCylinderOC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_InitTris(GlobalContext* globalCtx, ColliderTris* tris); +s32 Collider_FreeTris(GlobalContext* globalCtx, ColliderTris* tris); +s32 Collider_DestroyTris(GlobalContext* globalCtx, ColliderTris* tris); +s32 Collider_SetTrisAllocType1(GlobalContext* globalCtx, ColliderTris* dest, Actor* actor, ColliderTrisInitType1* src); +s32 Collider_SetTrisAlloc(GlobalContext* globalCtx, ColliderTris* dest, Actor* actor, ColliderTrisInit* src); +s32 Collider_SetTris(GlobalContext* globalCtx, ColliderTris* dest, Actor* actor, ColliderTrisInit* src, + ColliderTrisElement* elements); +s32 Collider_ResetTrisAT(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetTrisAC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetTrisOC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_InitQuad(GlobalContext* globalCtx, ColliderQuad* collider); +s32 Collider_DestroyQuad(GlobalContext* globalCtx, ColliderQuad* collider); +s32 Collider_SetQuadType1(GlobalContext* globalCtx, ColliderQuad* collider, Actor* actor, ColliderQuadInitType1* src); +s32 Collider_SetQuad(GlobalContext* globalCtx, ColliderQuad* collider, Actor* actor, ColliderQuadInit* src); +s32 Collider_ResetQuadAT(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetQuadAC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_ResetQuadOC(GlobalContext* globalCtx, Collider* collider); +s32 Collider_InitLine(GlobalContext* globalCtx, OcLine* line); +s32 Collider_DestroyLine(GlobalContext* globalCtx, OcLine* line); +s32 Collider_SetLinePoints(GlobalContext* globalCtx, OcLine* ocLine, Vec3f* a, Vec3f* b); +s32 Collider_SetLine(GlobalContext* globalCtx, OcLine* dest, OcLine* src); +s32 Collider_ResetLineOC(GlobalContext* globalCtx, OcLine* line); +void CollisionCheck_InitContext(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void CollisionCheck_DestroyContext(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void CollisionCheck_ClearContext(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void CollisionCheck_EnableSAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void CollisionCheck_DisableSAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void Collider_Draw(GlobalContext* globalCtx, Collider* collider); +void CollisionCheck_DrawCollision(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +s32 CollisionCheck_SetAT(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider); +s32 CollisionCheck_SetAT_SAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, s32 index); +s32 CollisionCheck_SetAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider); +s32 CollisionCheck_SetAC_SAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, s32 index); +s32 CollisionCheck_SetOC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider); +s32 CollisionCheck_SetOC_SAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, s32 index); +s32 CollisionCheck_SetOCLine(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, OcLine* collider); +void CollisionCheck_BlueBlood(GlobalContext* globalCtx, Collider* collider, Vec3f* v); +void CollisionCheck_AT(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void CollisionCheck_OC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +void CollisionCheck_InitInfo(CollisionCheckInfo* info); +void CollisionCheck_ResetDamage(CollisionCheckInfo* info); +void CollisionCheck_SetInfoNoDamageTable(CollisionCheckInfo* info, CollisionCheckInfoInit* init); +void CollisionCheck_SetInfo(CollisionCheckInfo* info, DamageTable* damageTable, CollisionCheckInfoInit* init); +void CollisionCheck_SetInfo2(CollisionCheckInfo* info, DamageTable* damageTable, CollisionCheckInfoInit2* init); +void CollisionCheck_SetInfoGetDamageTable(CollisionCheckInfo* info, s32 index, CollisionCheckInfoInit2* init); +void CollisionCheck_Damage(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx); +s32 CollisionCheck_LineOCCheckAll(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b); +s32 CollisionCheck_LineOCCheck(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b, + Actor** exclusions, s32 numExclusions); +void Collider_UpdateCylinder(Actor* actor, ColliderCylinder* collider); +void Collider_SetCylinderPosition(ColliderCylinder* collider, Vec3s* pos); +void Collider_SetQuadVertices(ColliderQuad* collider, Vec3f* a, Vec3f* b, Vec3f* c, Vec3f* d); +void Collider_SetTrisVertices(ColliderTris* collider, s32 index, Vec3f* a, Vec3f* b, Vec3f* c); +void Collider_SetTrisDim(GlobalContext* globalCtx, ColliderTris* collider, s32 index, ColliderTrisElementDimInit* init); +void Collider_UpdateSpheres(s32 limb, ColliderJntSph* collider); +void CollisionCheck_SpawnRedBlood(GlobalContext* globalCtx, Vec3f* v); +void CollisionCheck_SpawnWaterDroplets(GlobalContext* globalCtx, Vec3f* v); +void CollisionCheck_SpawnShieldParticles(GlobalContext* globalCtx, Vec3f* v); +void CollisionCheck_SpawnShieldParticlesMetal(GlobalContext* globalCtx, Vec3f* v); +void CollisionCheck_SpawnShieldParticlesMetalSound(GlobalContext* globalCtx, Vec3f* v, Vec3f* actorPos); +void CollisionCheck_SpawnShieldParticlesMetal2(GlobalContext* globalCtx, Vec3f* v); +void CollisionCheck_SpawnShieldParticlesWood(GlobalContext* globalCtx, Vec3f* b, Vec3f* actorPos); +s32 CollisionCheck_CylSideVsLineSeg(f32 radius, f32 height, f32 offset, Vec3f* actorPos, Vec3f* itemPos, + Vec3f* itemProjPos, Vec3f* out1, Vec3f* out2); +u8 CollisionCheck_GetSwordDamage(s32 dmgFlags); +void SaveContext_Init(void); +s32 func_800635D0(s32); +void func_800636C0(void); +void func_8006375C(s32 arg0, s32 arg1, const char* text); +void func_8006376C(u8 x, u8 y, u8 colorId, const char* text); +// ? func_80063828(?); +void func_8006390C(Input* input); +// ? func_80063C04(?); +void func_80063D7C(GraphicsContext* gfxCtx); +void DebugDisplay_Init(void); +DebugDispObject* DebugDisplay_AddObject(f32 posX, f32 posY, f32 posZ, s16 rotX, s16 rotY, s16 rotZ, f32 scaleX, + f32 scaleY, f32 scaleZ, u8 red, u8 green, u8 blue, u8 alpha, s16 type, + GraphicsContext* gfxCtx); +void DebugDisplay_DrawObjects(GlobalContext* globalCtx); +void func_8006450C(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80064520(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80064534(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80064558(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_800645A0(GlobalContext* globalCtx, CutsceneContext* csCtx); +void Cutscene_HandleEntranceTriggers(GlobalContext* globalCtx); +void Cutscene_HandleConditionalTriggers(GlobalContext* globalCtx); +void Cutscene_SetSegment(GlobalContext* globalCtx, void* segment); +void GetItem_Draw(GlobalContext* globalCtx, s16 drawId); +void SoundSource_InitAll(GlobalContext* globalCtx); +void SoundSource_UpdateAll(GlobalContext* globalCtx); +void SoundSource_PlaySfxAtFixedWorldPos(GlobalContext* globalCtx, Vec3f* pos, s32 duration, u16 sfxId); +u16 ElfMessage_GetSariaText(GlobalContext* globalCtx); +u16 ElfMessage_GetCUpText(GlobalContext* globalCtx); +u16 Text_GetFaceReaction(GlobalContext* globalCtx, u32 reactionSet); +void Flags_UnsetAllEnv(GlobalContext* globalCtx); +void Flags_SetEnv(GlobalContext* globalCtx, s16 flag); +void Flags_UnsetEnv(GlobalContext* globalCtx, s16 flag); +s32 Flags_GetEnv(GlobalContext* globalCtx, s16 flag); +f32 func_8006C5A8(f32 target, TransformData* transData, s32 refIdx); +void SkelCurve_Clear(SkelAnimeCurve* skelCurve); +s32 SkelCurve_Init(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, SkelCurveLimbList* limbListSeg, + TransformUpdateIndex* transUpdIdx); +void SkelCurve_Destroy(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve); +void SkelCurve_SetAnim(SkelAnimeCurve* skelCurve, TransformUpdateIndex* transUpdIdx, f32 arg2, f32 animFinalFrame, + f32 animCurFrame, f32 animSpeed); +s32 SkelCurve_Update(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve); +void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, + OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* data); +s32 func_8006CFC0(s32 scene); +void func_8006D074(GlobalContext* globalCtx); +void func_8006D0AC(GlobalContext* globalCtx); +void func_8006D0EC(GlobalContext* globalCtx, Player* player); +void func_8006D684(GlobalContext* globalCtx, Player* player); +void func_8006DC68(GlobalContext* globalCtx, Player* player); +void func_8006DD9C(Actor* actor, Vec3f* arg1, s16 arg2); +s32 Jpeg_Decode(void* data, void* zbuffer, void* workBuff, u32 workSize); +void KaleidoSetup_Update(GlobalContext* globalCtx); +void KaleidoSetup_Init(GlobalContext* globalCtx); +void KaleidoSetup_Destroy(GlobalContext* globalCtx); +void func_8006EE50(Font* font, u16 arg1, u16 arg2); +void Font_LoadChar(Font* font, u8 character, u16 codePointIndex); +void Font_LoadMessageBoxIcon(Font* font, u16 icon); +void Font_LoadOrderedFont(Font* font); +s32 func_8006F0A0(s32 arg0); +u16 Environment_GetPixelDepth(s32 x, s32 y); +void Environment_GraphCallback(GraphicsContext* gfxCtx, void* param); +void Environment_Init(GlobalContext* globalCtx, EnvironmentContext* envCtx, s32 unused); +u8 Environment_SmoothStepToU8(u8* pvalue, u8 target, u8 scale, u8 step, u8 minStep); +u8 Environment_SmoothStepToS8(s8* pvalue, s8 target, u8 scale, u8 step, u8 minStep); +f32 Environment_LerpWeight(u16 max, u16 min, u16 val); +f32 Environment_LerpWeightAccelDecel(u16 endFrame, u16 startFrame, u16 curFrame, u16 accelDuration, u16 decelDuration); +void Environment_UpdateSkybox(GlobalContext* globalCtx, u8 skyboxId, EnvironmentContext* envCtx, SkyboxContext* skyboxCtx); +void Environment_EnableUnderwaterLights(GlobalContext* globalCtx, s32 waterLightsIndex); +void Environment_DisableUnderwaterLights(GlobalContext* globalCtx); +void Environment_Update(GlobalContext* globalCtx, EnvironmentContext* envCtx, LightContext* lightCtx, + PauseContext* pauseCtx, MessageContext* msgCtx, GameOverContext* gameOverCtx, + GraphicsContext* gfxCtx); +void Environment_DrawSunAndMoon(GlobalContext* globalCtx); +void Environment_DrawSunLensFlare(GlobalContext* globalCtx, EnvironmentContext* envCtx, View* view, + GraphicsContext* gfxCtx, Vec3f pos, s32 unused); +void Environment_DrawLensFlare(GlobalContext* globalCtx, EnvironmentContext* envCtx, View* view, + GraphicsContext* gfxCtx, Vec3f pos, s32 unused, s16 arg6, f32 arg7, s16 arg8, u8 arg9); +void Environment_DrawRain(GlobalContext* globalCtx, View* view, GraphicsContext* gfxCtx); +void func_80074CE8(GlobalContext* globalCtx, u32 arg1); +void Environment_DrawSkyboxFilters(GlobalContext* globalCtx); +void Environment_UpdateLightningStrike(GlobalContext* globalCtx); +void Environment_AddLightningBolts(GlobalContext* globalCtx, u8 num); +void Environment_DrawLightning(GlobalContext* globalCtx, s32 unused); +void Environment_PlaySceneSequence(GlobalContext* globalCtx); +void Environment_DrawCustomLensFlare(GlobalContext* globalCtx); +void Environment_InitGameOverLights(GlobalContext* globalCtx); +void Environment_FadeInGameOverLights(GlobalContext* globalCtx); +void Environment_FadeOutGameOverLights(GlobalContext* globalCtx); +void Environment_FillScreen(GraphicsContext* gfxCtx, u8 red, u8 green, u8 blue, u8 alpha, u8 drawFlags); +void Environment_DrawSandstorm(GlobalContext* globalCtx, u8 sandstormState); +void Environment_AdjustLights(GlobalContext* globalCtx, f32 arg1, f32 arg2, f32 arg3, f32 arg4); +s32 Environment_GetBgsDayCount(void); +void Environment_ClearBgsDayCount(void); +s32 Environment_GetTotalDays(void); +void Environment_ForcePlaySequence(u16 seqId); +s32 Environment_IsForcedSequenceDisabled(void); +void Environment_PlayStormNatureAmbience(GlobalContext* globalCtx); +void Environment_StopStormNatureAmbience(GlobalContext* globalCtx); +void Environment_WarpSongLeave(GlobalContext* globalCtx); +f32 Math_CosS(s16 angle); +f32 Math_SinS(s16 angle); +s32 Math_ScaledStepToS(s16* pValue, s16 target, s16 step); +s32 Math_StepToS(s16* pValue, s16 target, s16 step); +s32 Math_StepToF(f32* pValue, f32 target, f32 step); +s32 Math_StepUntilAngleS(s16* pValue, s16 limit, s16 step); +s32 Math_StepUntilS(s16* pValue, s16 limit, s16 step); +s32 Math_StepToAngleS(s16* pValue, s16 target, s16 step); +s32 Math_StepUntilF(f32* pValue, f32 limit, f32 step); +s32 Math_AsymStepToF(f32* pValue, f32 target, f32 incrStep, f32 decrStep); +void func_80077D10(f32* arg0, s16* arg1, Input* input); +s16 Rand_S16Offset(s16 base, s16 range); +void Math_Vec3f_Copy(Vec3f* dest, Vec3f* src); +void Math_Vec3s_ToVec3f(Vec3f* dest, Vec3s* src); +void Math_Vec3f_Sum(Vec3f* a, Vec3f* b, Vec3f* dest); +void Math_Vec3f_Diff(Vec3f* a, Vec3f* b, Vec3f* dest); +void Math_Vec3s_DiffToVec3f(Vec3f* dest, Vec3s* a, Vec3s* b); +void Math_Vec3f_Scale(Vec3f* vec, f32 scaleF); +f32 Math_Vec3f_DistXYZ(Vec3f* a, Vec3f* b); +f32 Math_Vec3f_DistXYZAndStoreDiff(Vec3f* a, Vec3f* b, Vec3f* dest); +f32 Math_Vec3f_DistXZ(Vec3f* a, Vec3f* b); +s16 Math_Vec3f_Yaw(Vec3f* a, Vec3f* b); +s16 Math_Vec3f_Pitch(Vec3f* a, Vec3f* b); +void Actor_ProcessInitChain(Actor* actor, InitChainEntry* initChain); +f32 Math_SmoothStepToF(f32* pValue, f32 target, f32 fraction, f32 step, f32 minStep); +void Math_ApproachF(f32* pValue, f32 target, f32 fraction, f32 step); +void Math_ApproachZeroF(f32* pValue, f32 fraction, f32 step); +f32 Math_SmoothStepToDegF(f32* pValue, f32 target, f32 fraction, f32 step, f32 minStep); +s16 Math_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step, s16 minStep); +void Math_ApproachS(s16* pValue, s16 target, s16 scale, s16 step); +void Color_RGBA8_Copy(Color_RGBA8* dst, Color_RGBA8* src); +void func_80078884(u16 sfxId); +void func_800788CC(u16 sfxId); +void func_80078914(Vec3f* arg0, u16 sfxId); +void HealthMeter_Init(GlobalContext* globalCtx); +void HealthMeter_Update(GlobalContext* globalCtx); +void HealthMeter_Draw(GlobalContext* globalCtx); +void HealthMeter_HandleCriticalAlarm(GlobalContext* globalCtx); +u32 HealthMeter_IsCritical(void); +void Lights_PointSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius, s32 type); +void Lights_PointNoGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius); +void Lights_PointGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius); +void Lights_PointSetColorAndRadius(LightInfo* info, u8 r, u8 g, u8 b, s16 radius); +void Lights_DirectionalSetInfo(LightInfo* info, s8 x, s8 y, s8 z, u8 r, u8 g, u8 b); +void Lights_Reset(Lights* lights, u8 ambentR, u8 ambentG, u8 ambentB); +void Lights_Draw(Lights* lights, GraphicsContext* gfxCtx); +void Lights_BindAll(Lights* lights, LightNode* listHead, Vec3f* vec); +void LightContext_Init(GlobalContext* globalCtx, LightContext* lightCtx); +void LightContext_SetAmbientColor(LightContext* lightCtx, u8 r, u8 g, u8 b); +void LightContext_SetFog(LightContext* lightCtx, u8 arg1, u8 arg2, u8 arg3, s16 numLights, s16 arg5); +Lights* LightContext_NewLights(LightContext* lightCtx, GraphicsContext* gfxCtx); +void LightContext_InitList(GlobalContext* globalCtx, LightContext* lightCtx); +void LightContext_DestroyList(GlobalContext* globalCtx, LightContext* lightCtx); +LightNode* LightContext_InsertLight(GlobalContext* globalCtx, LightContext* lightCtx, LightInfo* info); +void LightContext_RemoveLight(GlobalContext* globalCtx, LightContext* lightCtx, LightNode* node); +Lights* Lights_NewAndDraw(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB, u8 numLights, u8 r, u8 g, + u8 b, s8 x, s8 y, s8 z); +Lights* Lights_New(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB); +void Lights_GlowCheck(GlobalContext* globalCtx); +void Lights_DrawGlow(GlobalContext* globalCtx); +void ZeldaArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action); +void* ZeldaArena_Malloc(size_t size); +void* ZeldaArena_MallocDebug(size_t size, const char* file, s32 line); +void* ZeldaArena_MallocR(size_t size); +void* ZeldaArena_MallocRDebug(size_t size, const char* file, s32 line); +void* ZeldaArena_Realloc(void* ptr, size_t newSize); +void* ZeldaArena_ReallocDebug(void* ptr, size_t newSize, const char* file, s32 line); +void ZeldaArena_Free(void* ptr); +void ZeldaArena_FreeDebug(void* ptr, const char* file, s32 line); +void* ZeldaArena_Calloc(size_t num, size_t size); +void ZeldaArena_Display(); +void ZeldaArena_GetSizes(u32* outMaxFree, u32* outFree, u32* outAlloc); +void ZeldaArena_Check(); +void ZeldaArena_Init(void* start, size_t size); +void ZeldaArena_Cleanup(); +u8 ZeldaArena_IsInitalized(); +void MapMark_Init(GlobalContext* globalCtx); +void MapMark_ClearPointers(GlobalContext* globalCtx); +void MapMark_Draw(GlobalContext* globalCtx); +void PreNmiBuff_Init(PreNmiBuff* this); +void PreNmiBuff_SetReset(PreNmiBuff* this); +u32 PreNmiBuff_IsResetting(PreNmiBuff* this); +void MsgEvent_SendNullTask(void); +f32 OLib_Vec3fDist(Vec3f* a, Vec3f* b); +f32 OLib_Vec3fDistXZ(Vec3f* a, Vec3f* b); +f32 OLib_ClampMinDist(f32 val, f32 min); +f32 OLib_ClampMaxDist(f32 val, f32 max); +Vec3f* OLib_Vec3fDistNormalize(Vec3f* dest, Vec3f* a, Vec3f* b); +Vec3f* OLib_VecSphGeoToVec3f(Vec3f* dest, VecSph* sph); +VecSph* OLib_Vec3fToVecSph(VecSph* dest, Vec3f* vec); +VecSph* OLib_Vec3fToVecSphGeo(VecSph* arg0, Vec3f* arg1); +VecSph* OLib_Vec3fDiffToVecSphGeo(VecSph* arg0, Vec3f* a, Vec3f* b); +Vec3f* OLib_Vec3fDiffRad(Vec3f* dest, Vec3f* a, Vec3f* b); +s16 OnePointCutscene_Init(GlobalContext* globalCtx, s16 csId, s16 timer, Actor* actor, s16 camIdx); +s16 OnePointCutscene_EndCutscene(GlobalContext* globalCtx, s16 camIdx); +s32 OnePointCutscene_Attention(GlobalContext* globalCtx, Actor* actor); +s32 OnePointCutscene_AttentionSetSfx(GlobalContext* globalCtx, Actor* actor, s32 sfxId); +void OnePointCutscene_EnableAttention(void); +void OnePointCutscene_DisableAttention(void); +s32 OnePointCutscene_CheckForCategory(GlobalContext* globalCtx, s32 actorCategory); +void OnePointCutscene_Noop(GlobalContext* globalCtx, s32 arg1); +void Map_SavePlayerInitialInfo(GlobalContext* globalCtx); +void Map_SetFloorPalettesData(GlobalContext* globalCtx, s16 floor); +void Map_InitData(GlobalContext* globalCtx, s16 room); +void Map_InitRoomData(GlobalContext* globalCtx, s16 room); +void Map_Destroy(GlobalContext* globalCtx); +void Map_Init(GlobalContext* globalCtx); +void Minimap_Draw(GlobalContext* globalCtx); +void Map_Update(GlobalContext* globalCtx); +void Interface_ChangeAlpha(u16 alphaType); +void Interface_SetSceneRestrictions(GlobalContext* globalCtx); +void Inventory_SwapAgeEquipment(void); +void Interface_InitHorsebackArchery(GlobalContext* globalCtx); +void func_800849EC(GlobalContext* globalCtx); +void Interface_LoadItemIcon1(GlobalContext* globalCtx, u16 button); +void Interface_LoadItemIcon2(GlobalContext* globalCtx, u16 button); +void func_80084BF4(GlobalContext* globalCtx, u16 flag); +u8 Item_Give(GlobalContext* globalCtx, u8 item); +u8 Item_CheckObtainability(u8 item); +void Inventory_DeleteItem(u16 item, u16 invSlot); +s32 Inventory_ReplaceItem(GlobalContext* globalCtx, u16 oldItem, u16 newItem); +s32 Inventory_HasEmptyBottle(void); +s32 Inventory_HasSpecificBottle(u8 bottleItem); +void Inventory_UpdateBottleItem(GlobalContext* globalCtx, u8 item, u8 cButton); +s32 Inventory_ConsumeFairy(GlobalContext* globalCtx); +void Interface_SetDoAction(GlobalContext* globalCtx, u16 action); +void Interface_SetNaviCall(GlobalContext* globalCtx, u16 naviCallState); +void Interface_LoadActionLabelB(GlobalContext* globalCtx, u16 action); +s32 Health_ChangeBy(GlobalContext* globalCtx, s16 healthChange); +void Rupees_ChangeBy(s16 rupeeChange); +void Inventory_ChangeAmmo(s16 item, s16 ammoChange); +void Magic_Fill(GlobalContext* globalCtx); +void func_800876C8(GlobalContext* globalCtx); +s32 func_80087708(GlobalContext* globalCtx, s16 arg1, s16 arg2); +void func_80088AA0(s16 seconds); +void func_80088AF0(GlobalContext* globalCtx); +void func_80088B34(s16 arg0); +void Interface_Draw(GlobalContext* globalCtx); +void Interface_Update(GlobalContext* globalCtx); +Path* Path_GetByIndex(GlobalContext* globalCtx, s16 index, s16 max); +f32 Path_OrientAndGetDistSq(Actor* actor, Path* path, s16 waypoint, s16* yaw); +void Path_CopyLastPoint(Path* path, Vec3f* dest); +void FrameAdvance_Init(FrameAdvanceContext* frameAdvCtx); +s32 FrameAdvance_Update(FrameAdvanceContext* frameAdvCtx, Input* input); +void Player_SetBootData(GlobalContext* globalCtx, Player* player); +s32 Player_InBlockingCsMode(GlobalContext* globalCtx, Player* player); +s32 Player_InCsMode(GlobalContext* globalCtx); +s32 func_8008E9C4(Player* player); +s32 Player_IsChildWithHylianShield(Player* player); +s32 Player_ActionToModelGroup(Player* player, s32 actionParam); +void Player_SetModelsForHoldingShield(Player* player); +void Player_SetModels(Player* player, s32 modelGroup); +void Player_SetModelGroup(Player* player, s32 modelGroup); +void func_8008EC70(Player* player); +void Player_SetEquipmentData(GlobalContext* globalCtx, Player* player); +void Player_UpdateBottleHeld(GlobalContext* globalCtx, Player* player, s32 item, s32 actionParam); +void func_8008EDF0(Player* player); +void func_8008EE08(Player* player); +void func_8008EEAC(GlobalContext* globalCtx, Actor* actor); +s32 func_8008EF44(GlobalContext* globalCtx, s32 ammo); +s32 Player_IsBurningStickInRange(GlobalContext* globalCtx, Vec3f* pos, f32 radius, f32 arg3); +s32 Player_GetStrength(void); +u8 Player_GetMask(GlobalContext* globalCtx); +Player* Player_UnsetMask(GlobalContext* globalCtx); +s32 Player_HasMirrorShieldEquipped(GlobalContext* globalCtx); +s32 Player_HasMirrorShieldSetToDraw(GlobalContext* globalCtx); +s32 Player_ActionToMagicSpell(Player* player, s32 actionParam); +s32 Player_HoldsHookshot(Player* player); +s32 func_8008F128(Player* player); +s32 Player_ActionToSword(s32 actionParam); +s32 Player_GetSwordHeld(Player* player); +s32 Player_HoldsTwoHandedWeapon(Player* player); +s32 Player_HoldsBrokenKnife(Player* player); +s32 Player_ActionToBottle(Player* player, s32 actionParam); +s32 Player_GetBottleHeld(Player* player); +s32 Player_ActionToExplosive(Player* player, s32 actionParam); +s32 Player_GetExplosiveHeld(Player* player); +s32 func_8008F2BC(Player* player, s32 actionParam); +s32 func_8008F2F8(GlobalContext* globalCtx); +void func_8008F470(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, s32 lod, s32 tunic, + s32 boots, s32 face, OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* this); +s32 func_8008FCC8(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* data); +s32 func_80090014(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* data); +s32 func_800902F0(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* data); +s32 func_80090440(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* data); +u8 func_80090480(GlobalContext* globalCtx, ColliderQuad* collider, WeaponInfo* weaponDim, Vec3f* newTip, + Vec3f* newBase); +void Player_DrawGetItem(GlobalContext* globalCtx, Player* player); +void func_80090D20(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* data); +u32 func_80091738(GlobalContext* globalCtx, u8* segment, SkelAnime* skelAnime); +void func_8009214C(GlobalContext* globalCtx, u8* segment, SkelAnime* skelAnime, Vec3f* pos, Vec3s* rot, f32 scale, + s32 sword, s32 tunic, s32 shield, s32 boots); +void PreNMI_Init(GameState* thisx); +Vec3f* Quake_AddVec(Vec3f* dst, Vec3f* arg1, VecSph* arg2); +void Quake_UpdateShakeInfo(QuakeRequest* req, ShakeInfo* shake, f32 y, f32 x); +s16 Quake_Callback1(QuakeRequest* req, ShakeInfo* shake); +s16 Quake_Callback2(QuakeRequest* req, ShakeInfo* shake); +s16 Quake_Callback3(QuakeRequest* req, ShakeInfo* shake); +s16 Quake_Callback4(QuakeRequest* req, ShakeInfo* shake); +s16 Quake_Callback5(QuakeRequest* req, ShakeInfo* shake); +s16 Quake_Callback6(QuakeRequest* req, ShakeInfo* shake); +s16 Quake_GetFreeIndex(void); +QuakeRequest* Quake_AddImpl(Camera* cam, u32 callbackIdx); +void Quake_Remove(QuakeRequest* req); +QuakeRequest* Quake_GetRequest(s16 idx); +QuakeRequest* Quake_SetValue(s16 idx, s16 valueType, s16 value); +u32 Quake_SetSpeed(s16 idx, s16 value); +u32 Quake_SetCountdown(s16 idx, s16 value); +s16 Quake_GetCountdown(s16 idx); +u32 Quake_SetQuakeValues(s16 idx, s16 y, s16 x, s16 zoom, s16 rotZ); +u32 Quake_SetUnkValues(s16 idx, s16 arg1, SubQuakeRequest14 arg2); +void Quake_Init(void); +s16 Quake_Add(Camera* cam, u32 callbackIdx); +u32 Quake_RemoveFromIdx(s16 idx); +s16 Quake_Calc(Camera* camera, QuakeCamCalc* camData); +Gfx* Gfx_SetFog(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far); +Gfx* Gfx_SetFogWithSync(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far); +Gfx* Gfx_SetFog2(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far); +Gfx* Gfx_CallSetupDL(Gfx* gfx, u32 i); +Gfx* func_800937C0(Gfx* gfx); +Gfx* func_80093808(Gfx* gfx); +void func_800938B4(GraphicsContext* gfxCtx); +void func_8009398C(GraphicsContext* gfxCtx); +void func_80093AD0(GraphicsContext* gfxCtx); +void func_80093BA8(GraphicsContext* gfxCtx); +void func_80093C14(GraphicsContext* gfxCtx); +void func_80093C80(GlobalContext* globalCtx); +void func_80093D18(GraphicsContext* gfxCtx); +void func_80093D84(GraphicsContext* gfxCtx); +Gfx* func_80093F34(Gfx* gfx); +Gfx* func_80093F58(Gfx* gfx); +void func_80094044(GraphicsContext* gfxCtx); +void func_800940B0(GraphicsContext* gfxCtx); +void func_80094140(GraphicsContext* gfxCtx); +Gfx* func_8009411C(Gfx* gfx); +void func_800942F0(GraphicsContext* gfxCtx); +void func_8009435C(GraphicsContext* gfxCtx); +void func_800943C8(GraphicsContext* gfxCtx); +Gfx* func_800944A0(Gfx* gfx); +void func_800944C4(GraphicsContext* gfxCtx); +void func_80094520(GraphicsContext* gfxCtx); +void func_8009457C(Gfx** gfxp); +void func_800945A0(GraphicsContext* gfxCtx); +void func_8009460C(GraphicsContext* gfxCtx); +void func_80094678(GraphicsContext* gfxCtx); +Gfx* func_80094968(Gfx* gfx); +Gfx* func_800946E4(Gfx* gfx); +Gfx* func_800947AC(Gfx* gfx); +void func_800949A8(GraphicsContext* gfxCtx); +void func_80094A14(GraphicsContext* gfxCtx); +void func_80094B58(GraphicsContext* gfxCtx); +void func_80094BC4(GraphicsContext* gfxCtx); +void func_80094C50(GraphicsContext* gfxCtx); +void func_80094D28(Gfx** gfxp); +Gfx* Gfx_BranchTexScroll(Gfx** gfxp, u32 x, u32 y, s32 width, s32 height); +Gfx* func_80094E78(GraphicsContext* gfxCtx, u32 x, u32 y); +Gfx* Gfx_TexScroll(GraphicsContext* gfxCtx, u32 x, u32 y, s32 width, s32 height); +Gfx* Gfx_TwoTexScroll(GraphicsContext* gfxCtx, s32 tile1, u32 x1, u32 y1, s32 width1, s32 height1, s32 tile2, u32 x2, + u32 y2, s32 width2, s32 height2); +Gfx* Gfx_TwoTexScrollEnvColor(GraphicsContext* gfxCtx, s32 tile1, u32 x1, u32 y1, s32 width1, s32 height1, s32 tile2, + u32 x2, u32 y2, s32 width2, s32 height2, s32 r, s32 g, s32 b, s32 a); +Gfx* Gfx_EnvColor(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a); +void func_80095248(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b); +void func_80095974(GraphicsContext* gfxCtx); +void func_80095AA0(GlobalContext* globalCtx, Room* room, Input* arg2, UNK_TYPE arg3); +void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 mode0, + u16 tlutCount, f32 frameX, f32 frameY); +void func_80096FD4(GlobalContext* globalCtx, Room* room); +u32 func_80096FE8(GlobalContext* globalCtx, RoomContext* roomCtx); +s32 func_8009728C(GlobalContext* globalCtx, RoomContext* roomCtx, s32 roomNum); +s32 func_800973FC(GlobalContext* globalCtx, RoomContext* roomCtx); +void Room_Draw(GlobalContext* globalCtx, Room* room, u32 flags); +void func_80097534(GlobalContext* globalCtx, RoomContext* roomCtx); +void Sample_Destroy(GameState* thisx); +void Sample_Init(GameState* thisx); +void Inventory_ChangeEquipment(s16 equipment, u16 value); +u8 Inventory_DeleteEquipment(GlobalContext* globalCtx, s16 equipment); +void Inventory_ChangeUpgrade(s16 upgrade, s16 value); +void Object_InitBank(GlobalContext* globalCtx, ObjectContext* objectCtx); +void Object_UpdateBank(ObjectContext* objectCtx); +s32 Object_GetIndex(ObjectContext* objectCtx, s16 objectId); +s32 Object_IsLoaded(ObjectContext* objectCtx, s32 bankIndex); +void func_800981B8(ObjectContext* objectCtx); +s32 Scene_ExecuteCommands(GlobalContext* globalCtx, SceneCmd* sceneCmd); +void TransitionActor_InitContext(GameState* state, TransitionActorContext* transiActorCtx); +void func_800994A0(GlobalContext* globalCtx); +void Scene_Draw(GlobalContext* globalCtx); +void SkelAnime_DrawLod(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 dListIndex); +void SkelAnime_DrawFlexLod(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, + s32 dListIndex); +void SkelAnime_DrawOpa(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg); +void SkelAnime_DrawFlexOpa(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg); +s16 Animation_GetLength(void* animation); +s16 Animation_GetLastFrame(void* animation); +s32 SkelAnime_GetFrameDataLegacy(LegacyAnimationHeader* animation, s32 frame, Vec3s* frameTable); +s16 Animation_GetLimbCountLegacy(LegacyAnimationHeader* animation); +s16 Animation_GetLengthLegacy(LegacyAnimationHeader* animation); +s16 Animation_GetLastFrameLegacy(LegacyAnimationHeader* animation); +Gfx* SkelAnime_Draw(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, void* arg, Gfx* gfx); +Gfx* SkelAnime_DrawFlex(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, void* arg, Gfx* gfx); +void SkelAnime_InterpFrameTable(s32 limbCount, Vec3s* dst, Vec3s* start, Vec3s* target, f32 weight); +void AnimationContext_Reset(AnimationContext* animationCtx); +void AnimationContext_SetNextQueue(GlobalContext* globalCtx); +void AnimationContext_DisableQueue(GlobalContext* globalCtx); +void AnimationContext_SetLoadFrame(GlobalContext* globalCtx, LinkAnimationHeader* animation, s32 frame, s32 limbCount, + Vec3s* frameTable); +void AnimationContext_SetCopyAll(GlobalContext* globalCtx, s32 vecCount, Vec3s* dst, Vec3s* src); +void AnimationContext_SetInterp(GlobalContext* globalCtx, s32 vecCount, Vec3s* base, Vec3s* mod, f32 weight); +void AnimationContext_SetCopyTrue(GlobalContext* globalCtx, s32 vecCount, Vec3s* dst, Vec3s* src, u8* copyFlag); +void AnimationContext_SetCopyFalse(GlobalContext* globalCtx, s32 vecCount, Vec3s* dst, Vec3s* src, u8* copyFlag); +void AnimationContext_SetMoveActor(GlobalContext* globalCtx, Actor* actor, SkelAnime* skelAnime, f32 arg3); +void AnimationContext_Update(GlobalContext* globalCtx, AnimationContext* animationCtx); +void SkelAnime_InitLink(GlobalContext* globalCtx, SkelAnime* skelAnime, FlexSkeletonHeader* skeletonHeaderSeg, + LinkAnimationHeader* animation, s32 initFlags, Vec3s* jointTable, Vec3s* morphTable, + s32 limbCount); +void LinkAnimation_SetUpdateFunction(SkelAnime* skelAnime); +s32 LinkAnimation_Update(GlobalContext* globalCtx, SkelAnime* skelAnime); +void LinkAnimation_AnimateFrame(GlobalContext* globalCtx, SkelAnime* skelAnime); +void Animation_SetMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, f32 morphFrames); +void LinkAnimation_Change(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, f32 playSpeed, + f32 startFrame, f32 endFrame, u8 mode, f32 morphFrames); +void LinkAnimation_PlayOnce(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation); +void LinkAnimation_PlayOnceSetSpeed(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 playSpeed); +void LinkAnimation_PlayLoop(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation); +void LinkAnimation_PlayLoopSetSpeed(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 playSpeed); +void LinkAnimation_CopyJointToMorph(GlobalContext* globalCtx, SkelAnime* skelAnime); +void LinkAnimation_CopyMorphToJoint(GlobalContext* globalCtx, SkelAnime* skelAnime); +void LinkAnimation_LoadToMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 frame); +void LinkAnimation_LoadToJoint(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 frame); +void LinkAnimation_InterpJointMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, f32 frame); +void LinkAnimation_BlendToJoint(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation1, + f32 frame1, LinkAnimationHeader* animation2, f32 frame2, f32 weight, Vec3s* blendTable); +void LinkAnimation_BlendToMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation1, + f32 frame1, LinkAnimationHeader* animation2, f32 frame2, f32 weight, Vec3s* blendTable); +void LinkAnimation_EndLoop(SkelAnime* skelAnime); +s32 LinkAnimation_OnFrame(SkelAnime* skelAnime, f32 frame); +s32 SkelAnime_Init(GlobalContext* globalCtx, SkelAnime* skelAnime, SkeletonHeader* skeletonHeaderSeg, + AnimationHeader* animation, Vec3s* jointTable, Vec3s* morphTable, s32 limbCount); +s32 SkelAnime_InitFlex(GlobalContext* globalCtx, SkelAnime* skelAnime, FlexSkeletonHeader* skeletonHeaderSeg, + AnimationHeader* animation, Vec3s* jointTable, Vec3s* morphTable, s32 limbCount); +s32 SkelAnime_InitSkin(GlobalContext* globalCtx, SkelAnime* skelAnime, SkeletonHeader* skeletonHeaderSeg, + AnimationHeader* animation); +s32 SkelAnime_Update(SkelAnime* skelAnime); +void Animation_ChangeImpl(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed, f32 startFrame, f32 endFrame, + u8 mode, f32 morphFrames, s8 taper); +void Animation_Change(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed, f32 startFrame, f32 endFrame, + u8 mode, f32 morphFrames); +void Animation_PlayOnce(SkelAnime* skelAnime, AnimationHeader* animation); +void Animation_MorphToPlayOnce(SkelAnime* skelAnime, AnimationHeader* animation, f32 morphFrames); +void Animation_PlayOnceSetSpeed(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed); +void Animation_PlayLoop(SkelAnime* skelAnime, AnimationHeader* animation); +void Animation_MorphToLoop(SkelAnime* skelAnime, AnimationHeader* animation, f32 morphFrames); +void Animation_PlayLoopSetSpeed(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed); +void Animation_EndLoop(SkelAnime* skelAnime); +void Animation_Reverse(SkelAnime* skelAnime); +void SkelAnime_CopyFrameTableTrue(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src, u8* copyFlag); +void SkelAnime_CopyFrameTableFalse(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src, u8* copyFlag); +void SkelAnime_UpdateTranslation(SkelAnime* skelAnime, Vec3f* pos, s16 angle); +s32 Animation_OnFrame(SkelAnime* skelAnime, f32 frame); +void SkelAnime_Free(SkelAnime* skelAnime, GlobalContext* globalCtx); +void SkelAnime_CopyFrameTable(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src); + +void Skin_UpdateVertices(MtxF* mtx, SkinVertex* skinVertices, SkinLimbModif* modifEntry, Vtx* vtxBuf, Vec3f* pos); +void Skin_DrawAnimatedLimb(GraphicsContext* gfxCtx, Skin* skin, s32 limbIndex, s32 arg3, s32 drawFlags); +void Skin_DrawLimb(GraphicsContext* gfxCtx, Skin* skin, s32 limbIndex, Gfx* dlistOverride, s32 drawFlags); +void func_800A6330(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, s32 setTranslation); +void func_800A6360(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation); +void func_800A6394(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6); +void func_800A63CC(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6, s32 drawFlags); +void Skin_GetLimbPos(Skin* skin, s32 limbIndex, Vec3f* arg2, Vec3f* dst); +void Skin_Init(GlobalContext* globalCtx, Skin* skin, SkeletonHeader* skeletonHeader, AnimationHeader* animationHeader); +void Skin_Free(GlobalContext* globalCtx, Skin* skin); +s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* mf, Actor* actor, s32 setTranslation); + +void SkinMatrix_Vec3fMtxFMultXYZW(MtxF* mf, Vec3f* src, Vec3f* xyzDest, f32* wDest); +void SkinMatrix_Vec3fMtxFMultXYZ(MtxF* mf, Vec3f* src, Vec3f* dest); +void SkinMatrix_MtxFMtxFMult(MtxF* mfA, MtxF* mfB, MtxF* dest); +void SkinMatrix_GetClear(MtxF** mf); +void SkinMatrix_MtxFCopy(MtxF* src, MtxF* dest); +s32 SkinMatrix_Invert(MtxF* src, MtxF* dest); +void SkinMatrix_SetScale(MtxF* mf, f32 x, f32 y, f32 z); +void SkinMatrix_SetRotateZYX(MtxF* mf, s16 x, s16 y, s16 z); +void SkinMatrix_SetTranslate(MtxF* mf, f32 x, f32 y, f32 z); +void SkinMatrix_SetTranslateRotateYXZScale(MtxF* dest, f32 scaleX, f32 scaleY, f32 scaleZ, s16 rotX, s16 rotY, s16 rotZ, + f32 translateX, f32 translateY, f32 translateZ); +void SkinMatrix_SetTranslateRotateZYX(MtxF* dest, s16 rotX, s16 rotY, s16 rotZ, f32 translateX, f32 translateY, + f32 translateZ); +Mtx* SkinMatrix_MtxFToNewMtx(GraphicsContext* gfxCtx, MtxF* src); +void SkinMatrix_SetRotateAxis(MtxF* mf, s16 angle, f32 axisX, f32 axisY, f32 axisZ); +void Sram_InitNewSave(void); +void Sram_InitDebugSave(void); +void Sram_OpenSave(SramContext* sramCtx); +void Sram_WriteSave(SramContext* sramCtx); +void Sram_VerifyAndLoadAllSaves(FileChooseContext* fileChoose, SramContext* sramCtx); +void Sram_InitSave(FileChooseContext* fileChoose, SramContext* sramCtx); +void Sram_EraseSave(FileChooseContext* fileChoose, SramContext* sramCtx); +void Sram_CopySave(FileChooseContext* fileChoose, SramContext* sramCtx); +void Sram_WriteSramHeader(SramContext* sramCtx); +void Sram_InitSram(GameState* gameState, SramContext* sramCtx); +void Sram_Alloc(GameState* gameState, SramContext* sramCtx); +void Sram_Init(GlobalContext* globalCtx, SramContext* sramCtx); +void SsSram_ReadWrite(uintptr_t addr, void* dramAddr, size_t size, s32 direction); +void func_800A9F30(PadMgr*, s32); +void func_800A9F6C(f32, u8, u8, u8); +void func_800AA000(f32, u8, u8, u8); +void func_800AA0B4(); +void func_800AA0F0(void); +u32 func_800AA148(); +void func_800AA15C(); +void func_800AA16C(); +void func_800AA178(u32); +View* View_New(GraphicsContext* gfxCtx); +void View_Free(View* view); +void View_Init(View*, GraphicsContext*); +void func_800AA358(View* view, Vec3f* eye, Vec3f* lookAt, Vec3f* up); +void func_800AA3F0(View* view, Vec3f* eye, Vec3f* lookAt, Vec3f* up); +void View_SetScale(View* view, f32 scale); +void View_GetScale(View* view, f32* scale); +void func_800AA460(View* view, f32 fovy, f32 near, f32 far); +void func_800AA48C(View* view, f32* fovy, f32* near, f32* far); +void func_800AA4A8(View* view, f32 fovy, f32 near, f32 far); +void func_800AA4E0(View* view, f32* fovy, f32* near, f32* far); +void View_SetViewport(View* view, Viewport* viewport); +void View_GetViewport(View* view, Viewport* viewport); +void func_800AA76C(View* view, f32 arg1, f32 arg2, f32 arg3); +void func_800AA78C(View* view, f32 arg1, f32 arg2, f32 arg3); +s32 func_800AA7AC(View* view, f32 arg1); +void func_800AA7B8(View* view); +void func_800AA814(View* view); +void func_800AA840(View* view, Vec3f vec1, Vec3f vec2, f32 arg3); +s32 func_800AA890(View* view, Mtx* mtx); +void func_800AAA50(View* view, s32 arg1); +s32 func_800AAA9C(View* view); +s32 func_800AB0A8(View* view); +s32 func_800AB2C4(View* view); +s32 func_800AB560(View* view); +s32 func_800AB944(View* view); +s32 func_800AB9EC(View* view, s32 arg1, Gfx** p); +s32 func_800ABE74(f32 eyeX, f32 eyeY, f32 eyeZ); +void ViMode_LogPrint(OSViMode* viMode); +void ViMode_Configure(ViMode* viMode, s32 mode, s32 type, s32 unk_70, s32 unk_74, s32 unk_78, s32 unk_7C, s32 width, + s32 height, s32 unk_left, s32 unk_right, s32 unk_top, s32 unk_bottom); +void ViMode_Save(ViMode* viMode); +void ViMode_Load(ViMode* viMode); +void ViMode_Init(ViMode* viMode); +void ViMode_Destroy(ViMode* viMode); +void ViMode_ConfigureFeatures(ViMode* viMode, s32 viFeatures); +void ViMode_Update(ViMode* viMode, Input* input); +void func_800ACE70(struct_801664F0* this); +void func_800ACE90(struct_801664F0* this); +void func_800ACE98(struct_801664F0* this, Gfx** gfxp); +void VisMono_Init(VisMono* this); +void VisMono_Destroy(VisMono* this); +void VisMono_UpdateTexture(VisMono* this, u16* tex); +Gfx* VisMono_DrawTexture(VisMono* this, Gfx* gfx); +void VisMono_Draw(VisMono* this, Gfx** gfxp); +void VisMono_DrawOld(VisMono* this); +void func_800AD920(struct_80166500* this); +void func_800AD950(struct_80166500* this); +void func_800AD958(struct_80166500* this, Gfx** gfxp); +void Skybox_Init(GameState* state, SkyboxContext* skyboxCtx, s16 skyboxId); +Mtx* SkyboxDraw_UpdateMatrix(SkyboxContext* skyboxCtx, f32 x, f32 y, f32 z); +void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z); +void SkyboxDraw_Update(SkyboxContext* skyboxCtx); +void PlayerCall_InitFuncPtrs(void); +void TransitionUnk_InitGraphics(TransitionUnk* this); +void TransitionUnk_InitData(TransitionUnk* this); +void TransitionUnk_Destroy(TransitionUnk* this); +TransitionUnk* TransitionUnk_Init(TransitionUnk* this, s32 row, s32 col); +void TransitionUnk_SetData(TransitionUnk* this); +void TransitionUnk_Draw(TransitionUnk* this, Gfx**); +void func_800B23E8(TransitionUnk* this); +void TransitionTriforce_Start(void* this); +void* TransitionTriforce_Init(void* this); +void TransitionTriforce_Destroy(void* this); +void TransitionTriforce_Update(void* this, s32 updateRate); +void TransitionTriforce_SetColor(void* this, u32 color); +void TransitionTriforce_SetType(void* this, s32 type); +void TransitionTriforce_Draw(void* this, Gfx** gfxP); +s32 TransitionTriforce_IsDone(void* this); +void TransitionWipe_Start(void* this); +void* TransitionWipe_Init(void* this); +void TransitionWipe_Destroy(void* this); +void TransitionWipe_Update(void* this, s32 updateRate); +void TransitionWipe_Draw(void* this, Gfx** gfxP); +s32 TransitionWipe_IsDone(void* this); +void TransitionWipe_SetType(void* this, s32 type); +void TransitionWipe_SetColor(void* this, u32 color); +void TransitionCircle_Start(void* thisx); +void* TransitionCircle_Init(void* thisx); +void TransitionCircle_Destroy(void* thisx); +void TransitionCircle_Update(void* thisx, s32 updateRate); +void TransitionCircle_Draw(void* thisx, Gfx** gfxP); +s32 TransitionCircle_IsDone(void* thisx); +void TransitionCircle_SetType(void* thisx, s32 type); +void TransitionCircle_SetColor(void* thisx, u32 color); +void TransitionCircle_SetEnvColor(void* thisx, u32 color); +void TransitionFade_Start(void* this); +void* TransitionFade_Init(void* this); +void TransitionFade_Destroy(void* this); +void TransitionFade_Update(void* this, s32 updateRate); +void TransitionFade_Draw(void* this, Gfx** gfxP); +s32 TransitionFade_IsDone(void* this); +void TransitionFade_SetColor(void* this, u32 color); +void TransitionFade_SetType(void* this, s32 type); +void ShrinkWindow_SetVal(s32 value); +u32 ShrinkWindow_GetVal(void); +void ShrinkWindow_SetCurrentVal(s32 nowVal); +u32 ShrinkWindow_GetCurrentVal(void); +void ShrinkWindow_Init(void); +void ShrinkWindow_Destroy(void); +void ShrinkWindow_Update(s32 updateRate); +// ? DbCamera_AddVecSph(?); +// ? DbCamera_CalcUpFromPitchYawRoll(?); +// ? DbCamera_SetTextValue(?); +// ? DbCamera_Vec3SToF(?); +// ? DbCamera_Vec3FToS(?); +// ? DbCamera_CopyVec3f(?); +// ? DbCamera_Vec3SToF2(?); +// ? func_800B3F94(?); +// ? func_800B3FF4(?); +// ? func_800B404C(?); +// ? func_800B4088(?); +// ? func_800B41DC(?); +// ? func_800B42C0(?); +// ? func_800B4370(?); +// ? func_800B44E0(?); +// ? DbCamera_PrintPoints(?); +// ? DbCamera_PrintF32Bytes(?); +// ? DbCamera_PrintU16Bytes(?); +// ? DbCamera_PrintS16Bytes(?); +// ? DbCamera_PrintCutBytes(?); +void DbCamera_Init(DbCamera* dbCamera, Camera* cameraPtr); +void DbgCamera_Enable(DbCamera* dbCamera, Camera* cam); +void DbCamera_Update(DbCamera* dbCamera, Camera* cam); +// ? DbCamera_GetFirstAvailableLetter(?); +// ? DbCamera_InitCut(?); +// ? DbCamera_ResetCut(?); +// ? DbCamera_CalcMempakAllocSize(?); +// ? DbCamera_GetMempakAllocSize(?); +// ? DbCamera_DrawSlotLetters(?); +// ? DbCamera_PrintAllCuts(?); +// ? func_800B91B0(?); +void DbCamera_Reset(Camera* cam, DbCamera* dbCam); +// ? DbCamera_UpdateDemoControl(?); +void func_800BB0A0(f32 u, Vec3f* pos, f32* roll, f32* viewAngle, f32* point0, f32* point1, f32* point2, f32* point3); +s32 func_800BB2B4(Vec3f* pos, f32* roll, f32* fov, CutsceneCameraPoint* point, s16* keyframe, f32* curFrame); +s32 Mempak_Init(s32 controllerNb); +s32 Mempak_GetFreeBytes(s32 controllerNb); +s32 Mempak_FindFile(s32 controllerNb, char start, char end); +s32 Mempak_Write(s32 controllerNb, char idx, void* buffer, s32 offset, ptrdiff_t size); +s32 Mempak_Read(s32 controllerNb, char idx, void* buffer, s32 offset, ptrdiff_t size); +s32 Mempak_Alloc(s32 controllerNb, char* idx, ptrdiff_t size); +s32 Mempak_DeleteFile(s32 controllerNb, char idx); +s32 Mempak_GetFileSize(s32 controllerNb, char idx); +void KaleidoManager_LoadOvl(KaleidoMgrOverlay* ovl); +void KaleidoManager_ClearOvl(KaleidoMgrOverlay* ovl); +void KaleidoManager_Init(GlobalContext* globalCtx); +void KaleidoManager_Destroy(); +void* KaleidoManager_GetRamAddr(void* vram); +void KaleidoScopeCall_LoadPlayer(); +void KaleidoScopeCall_Init(GlobalContext* globalCtx); +void KaleidoScopeCall_Destroy(GlobalContext* globalCtx); +void KaleidoScopeCall_Update(GlobalContext* globalCtx); +void KaleidoScopeCall_Draw(GlobalContext* globalCtx); +void func_800BC490(GlobalContext* globalCtx, s16 point); +s32 func_800BC56C(GlobalContext* globalCtx, s16 arg1); +void func_800BC590(GlobalContext* globalCtx); +void func_800BC5E0(GlobalContext* globalCtx, s32 arg1); +Gfx* Gameplay_SetFog(GlobalContext* globalCtx, Gfx* gfx); +void Gameplay_Destroy(GameState* thisx); +void Gameplay_Init(GameState* thisx); +void Gameplay_Main(GameState* thisx); +s32 Gameplay_InCsMode(GlobalContext* globalCtx); +f32 func_800BFCB8(GlobalContext* globalCtx, MtxF* mf, Vec3f* vec); +void* Gameplay_LoadFile(GlobalContext* globalCtx, RomFile* file); +void Gameplay_SpawnScene(GlobalContext* globalCtx, s32 sceneNum, s32 spawn); +void func_800C016C(GlobalContext* globalCtx, Vec3f* src, Vec3f* dest); +s16 Gameplay_CreateSubCamera(GlobalContext* globalCtx); +s16 Gameplay_GetActiveCamId(GlobalContext* globalCtx); +s16 Gameplay_ChangeCameraStatus(GlobalContext* globalCtx, s16 camId, s16 status); +void Gameplay_ClearCamera(GlobalContext* globalCtx, s16 camId); +void Gameplay_ClearAllSubCameras(GlobalContext* globalCtx); +Camera* Gameplay_GetCamera(GlobalContext* globalCtx, s16 camId); +s32 Gameplay_CameraSetAtEye(GlobalContext* globalCtx, s16 camId, Vec3f* at, Vec3f* eye); +s32 Gameplay_CameraSetAtEyeUp(GlobalContext* globalCtx, s16 camId, Vec3f* at, Vec3f* eye, Vec3f* up); +s32 Gameplay_CameraSetFov(GlobalContext* globalCtx, s16 camId, f32 fov); +s32 Gameplay_SetCameraRoll(GlobalContext* globalCtx, s16 camId, s16 roll); +void Gameplay_CopyCamera(GlobalContext* globalCtx, s16 camId1, s16 camId2); +s32 func_800C0808(GlobalContext* globalCtx, s16 camId, Player* player, s16 arg3); +s32 Gameplay_CameraChangeSetting(GlobalContext* globalCtx, s16 camId, s16 arg2); +void func_800C08AC(GlobalContext* globalCtx, s16 camId, s16 arg2); +void Gameplay_SaveSceneFlags(GlobalContext* globalCtx); +void Gameplay_SetupRespawnPoint(GlobalContext* globalCtx, s32 respawnMode, s32 playerParams); +void Gameplay_TriggerVoidOut(GlobalContext* globalCtx); +void Gameplay_TriggerRespawn(GlobalContext* globalCtx); +s32 func_800C0CB8(GlobalContext* globalCtx); +s32 FrameAdvance_IsEnabled(GlobalContext* globalCtx); +s32 func_800C0D34(GlobalContext* globalCtx, Actor* actor, s16* yaw); +s32 func_800C0DB4(GlobalContext* globalCtx, Vec3f* pos); +void PreRender_SetValuesSave(PreRender* this, u32 width, u32 height, void* fbuf, void* zbuf, void* cvg); +void PreRender_Init(PreRender* this); +void PreRender_SetValues(PreRender* this, u32 width, u32 height, void* fbuf, void* zbuf); +void PreRender_Destroy(PreRender* this); +void func_800C0F28(PreRender* this, Gfx** gfxp, void* buf, void* bufSave); +void func_800C1258(PreRender* this, Gfx** gfxp); +void func_800C170C(PreRender* this, Gfx** gfxp, void* fbuf, void* fbufSave, u32 r, u32 g, u32 b, u32 a); +void func_800C1AE8(PreRender* this, Gfx** gfxp, void* fbuf, void* fbufSave); +void func_800C1B24(PreRender* this, Gfx** gfxp, void* fbuf, void* cvgSave); +void func_800C1E9C(PreRender* this, Gfx** gfxp); +void func_800C1F20(PreRender* this, Gfx** gfxp); +void func_800C1FA4(PreRender* this, Gfx** gfxp); +void func_800C20B4(PreRender* this, Gfx** gfxp); +void func_800C2118(PreRender* this, Gfx** gfxp); +void func_800C213C(PreRender* this, Gfx** gfxp); +void func_800C24BC(PreRender* this, Gfx** gfxp); +void func_800C24E0(PreRender* this, Gfx** gfxp); +void func_800C2500(PreRender* this, s32 x, s32 y); +void func_800C2FE4(PreRender* this); +void PreRender_Calc(PreRender* this); +void THGA_Ct(TwoHeadGfxArena* thga, Gfx* start, size_t size); +void THGA_Dt(TwoHeadGfxArena* thga); +u32 THGA_IsCrash(TwoHeadGfxArena* thga); +void THGA_Init(TwoHeadGfxArena* thga); +s32 THGA_GetSize(TwoHeadGfxArena* thga); +Gfx* THGA_GetHead(TwoHeadGfxArena* thga); +void THGA_SetHead(TwoHeadGfxArena* thga, Gfx* start); +Gfx* THGA_GetTail(TwoHeadGfxArena* thga); +Gfx* THGA_AllocStartArray8(TwoHeadGfxArena* thga, u32 count); +Gfx* THGA_AllocStart8(TwoHeadGfxArena* thga); +Gfx* THGA_AllocStart8Wrapper(TwoHeadGfxArena* thga); +Gfx* THGA_AllocEnd(TwoHeadGfxArena* thga, size_t size); +Gfx* THGA_AllocEndArray64(TwoHeadGfxArena* thga, u32 count); +Gfx* THGA_AllocEnd64(TwoHeadGfxArena* thga); +Gfx* THGA_AllocEndArray16(TwoHeadGfxArena* thga, u32 count); +Gfx* THGA_AllocEnd16(TwoHeadGfxArena* thga); +void* THA_GetHead(TwoHeadArena* tha); +void THA_SetHead(TwoHeadArena* tha, void* start); +void* THA_GetTail(TwoHeadArena* tha); +void* THA_AllocStart(TwoHeadArena* tha, size_t size); +void* THA_AllocStart1(TwoHeadArena* tha); +void* THA_AllocEnd(TwoHeadArena* tha, size_t size); +void* THA_AllocEndAlign16(TwoHeadArena* tha, size_t size); +void* THA_AllocEndAlign(TwoHeadArena* tha, size_t size, size_t mask); +s32 THA_GetSize(TwoHeadArena* tha); +u32 THA_IsCrash(TwoHeadArena* tha); +void THA_Init(TwoHeadArena* tha); +void THA_Ct(TwoHeadArena* tha, void* ptr, size_t size); +void THA_Dt(TwoHeadArena* tha); +void func_800C3C20(void); +void func_800C3C80(AudioMgr* audioMgr); +void AudioMgr_HandleRetrace(AudioMgr* audioMgr); +void AudioMgr_HandlePRENMI(AudioMgr* audioMgr); +void AudioMgr_ThreadEntry(void* arg0); +void AudioMgr_Unlock(AudioMgr* audioMgr); +void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, SchedContext* sched, IrqMgr* irqMgr); +void TitleSetup_InitImpl(GameState* gameState); +void TitleSetup_Destroy(GameState* gameState); +void TitleSetup_Init(GameState* gameState); +void GameState_FaultPrint(void); +void GameState_SetFBFilter(Gfx** gfx); +// ? func_800C4344(?); +void GameState_DrawInputDisplay(u16 input, Gfx** gfx); +void GameState_Draw(GameState* gameState, GraphicsContext* gfxCtx); +void GameState_SetFrameBuffer(GraphicsContext* gfxCtx); +// ? func_800C49F4(?); +void GameState_ReqPadData(GameState* gameState); +void GameState_Update(GameState* gameState); +void GameState_InitArena(GameState* gameState, size_t size); +void GameState_Realloc(GameState* gameState, size_t size); +void GameState_Init(GameState* gameState, GameStateFunc init, GraphicsContext* gfxCtx); +void GameState_Destroy(GameState* gameState); +GameStateFunc GameState_GetInit(GameState* gameState); +u32 GameState_IsRunning(GameState* gameState); +void* GameState_Alloc(GameState* gameState, size_t size, char* file, s32 line); +void func_800C55D0(GameAlloc* this); +void* GameAlloc_MallocDebug(GameAlloc* this, size_t size, const char* file, s32 line); +void* GameAlloc_Malloc(GameAlloc* this, size_t size); +void GameAlloc_Free(GameAlloc* this, void* data); +void GameAlloc_Cleanup(GameAlloc* this); +void GameAlloc_Init(GameAlloc* this); +void Graph_FaultClient(); +void Graph_DisassembleUCode(Gfx* workBuf); +void Graph_UCodeFaultClient(Gfx* workBuf); +void Graph_InitTHGA(GraphicsContext* gfxCtx); +GameStateOverlay* Graph_GetNextGameState(GameState* gameState); +void Graph_Init(GraphicsContext* gfxCtx); +void Graph_Destroy(GraphicsContext* gfxCtx); +void Graph_TaskSet00(GraphicsContext* gfxCtx); +void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState); +void Graph_ThreadEntry(void*); +void* Graph_Alloc(GraphicsContext* gfxCtx, size_t size); +void* Graph_Alloc2(GraphicsContext* gfxCtx, size_t size); +void Graph_OpenDisps(Gfx** dispRefs, GraphicsContext* gfxCtx, const char* file, s32 line); +void Graph_CloseDisps(Gfx** dispRefs, GraphicsContext* gfxCtx, const char* file, s32 line); +Gfx* Graph_GfxPlusOne(Gfx* gfx); +Gfx* Graph_BranchDlist(Gfx* gfx, Gfx* dst); +void* Graph_DlistAlloc(Gfx** gfx, size_t size); +ListAlloc* ListAlloc_Init(ListAlloc* this); +void* ListAlloc_Alloc(ListAlloc* this, size_t size); +void ListAlloc_Free(ListAlloc* this, void* data); +void ListAlloc_FreeAll(ListAlloc* this); +void Main_LogSystemHeap(void); +void Main(void* arg); +OSMesgQueue* PadMgr_LockSerialMesgQueue(PadMgr* padmgr); +void PadMgr_UnlockSerialMesgQueue(PadMgr* padmgr, OSMesgQueue* ctrlrqueue); +void PadMgr_LockPadData(PadMgr* padmgr); +void PadMgr_UnlockPadData(PadMgr* padmgr); +void PadMgr_RumbleControl(PadMgr* padmgr); +void PadMgr_RumbleStop(PadMgr* padmgr); +void PadMgr_RumbleReset(PadMgr* padmgr); +void PadMgr_RumbleSet(PadMgr* padmgr, u8* ctrlrRumbles); +void PadMgr_ProcessInputs(PadMgr* padmgr); +void PadMgr_HandleRetraceMsg(PadMgr* padmgr); +void PadMgr_HandlePreNMI(PadMgr* padmgr); +// This function must remain commented out, because it is called incorrectly in +// fault.c (actual bug in game), and the compiler notices and won't compile it +// void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode); +void PadMgr_Init(PadMgr* padmgr, OSMesgQueue* siIntMsgQ, IrqMgr* irqMgr, OSId id, OSPri priority, void* stack); +void Sched_SwapFrameBuffer(CfbInfo* cfbInfo); +void func_800C84E4(SchedContext* sc, CfbInfo* cfbInfo); +void Sched_HandleReset(SchedContext* sc); +void Sched_HandleStart(SchedContext* sc); +void Sched_QueueTask(SchedContext* sc, OSScTask* task); +void Sched_Yield(SchedContext* sc); +OSScTask* func_800C89D4(SchedContext* sc, OSScTask* task); +s32 Sched_Schedule(SchedContext* sc, OSScTask** sp, OSScTask** dp, s32 state); +void func_800C8BC4(SchedContext* sc, OSScTask* task); +u32 Sched_IsComplete(SchedContext* sc, OSScTask* task); +void Sched_RunTask(SchedContext* sc, OSScTask* spTask, OSScTask* dpTask); +void Sched_HandleEntry(SchedContext* sc); +void Sched_HandleRetrace(SchedContext* sc); +void Sched_HandleRSPDone(SchedContext* sc); +void Sched_HandleRDPDone(SchedContext* sc); +void Sched_SendEntryMsg(SchedContext* sc); +void Sched_ThreadEntry(void* arg); +void Sched_Init(SchedContext* sc, void* stack, OSPri priority, UNK_TYPE arg3, UNK_TYPE arg4, IrqMgr* irqMgr); +void SpeedMeter_InitImpl(SpeedMeter* this, u32 arg1, u32 y); +void SpeedMeter_Init(SpeedMeter* this); +void SpeedMeter_Destroy(SpeedMeter* this); +void SpeedMeter_DrawTimeEntries(SpeedMeter* this, GraphicsContext* gfxCtx); +void SpeedMeter_InitAllocEntry(SpeedMeterAllocEntry* entry, u32 maxval, u32 val, u16 backColor, u16 foreColor, u32 ulx, + u32 lrx, u32 uly, u32 lry); +void SpeedMeter_DrawAllocEntry(SpeedMeterAllocEntry* this, GraphicsContext* gfxCtx); +void SpeedMeter_DrawAllocEntries(SpeedMeter* meter, GraphicsContext* gfxCtx, GameState* state); +void SysCfb_Init(s32 n64dd); +uintptr_t SysCfb_GetFbPtr(s32 idx); +uintptr_t SysCfb_GetFbEnd(); +f32 Math_FactorialF(f32 n); +f32 Math_Factorial(s32 n); +f32 Math_PowF(f32 base, s32 exp); +f32 Math_SinF(f32 angle); +f32 Math_CosF(f32 angle); +s32 Math3D_PlaneVsLineSegClosestPoint(f32 planeAA, f32 planeAB, f32 planeAC, f32 planeADist, f32 planeBA, f32 planeBB, + f32 planeBC, f32 planeBDist, Vec3f* linePointA, Vec3f* linePointB, + Vec3f* closestPoint); +void Math3D_LineClosestToPoint(Linef* line, Vec3f* pos, Vec3f* closestPoint); +s32 Math3D_PlaneVsPlaneVsLineClosestPoint(f32 planeAA, f32 planeAB, f32 planeAC, f32 planeADist, f32 planeBA, + f32 planeBB, f32 planeBC, f32 planeBDist, Vec3f* point, Vec3f* closestPoint); +void Math3D_LineSplitRatio(Vec3f* v0, Vec3f* v1, f32 ratio, Vec3f* ret); +f32 Math3D_Cos(Vec3f* a, Vec3f* b); +s32 Math3D_CosOut(Vec3f* a, Vec3f* b, f32* dst); +void Math3D_Vec3fReflect(Vec3f* vec, Vec3f* normal, Vec3f* reflVec); +s32 Math3D_PointInSquare2D(f32 upperLeftX, f32 lowerRightX, f32 upperLeftY, f32 lowerRightY, f32 x, f32 y); +f32 Math3D_Dist1DSq(f32 a, f32 b); +f32 Math3D_Dist2DSq(f32 x0, f32 y0, f32 x1, f32 y1); +f32 Math3D_Vec3fMagnitudeSq(Vec3f* vec); +f32 Math3D_Vec3fMagnitude(Vec3f* vec); +f32 Math3D_Vec3fDistSq(Vec3f* a, Vec3f* b); +void Math3D_Vec3f_Cross(Vec3f* a, Vec3f* b, Vec3f* ret); +void Math3D_SurfaceNorm(Vec3f* va, Vec3f* vb, Vec3f* vc, Vec3f* normal); +f32 Math3D_Vec3f_DistXYZ(Vec3f* a, Vec3f* b); +s32 Math3D_PointRelativeToCubeFaces(Vec3f* point, Vec3f* min, Vec3f* max); +s32 Math3D_PointRelativeToCubeEdges(Vec3f* point, Vec3f* min, Vec3f* max); +s32 Math3D_PointRelativeToCubeVertices(Vec3f* point, Vec3f* min, Vec3f* max); +s32 Math3D_LineVsCube(Vec3f* min, Vec3f* max, Vec3f* a, Vec3f* b); +void Math3D_RotateXZPlane(Vec3f* pointOnPlane, s16 angle, f32* a, f32* c, f32* d); +void Math3D_DefPlane(Vec3f* va, Vec3f* vb, Vec3f* vc, f32* nx, f32* ny, f32* nz, f32* originDist); +f32 Math3D_UDistPlaneToPos(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* p); +f32 Math3D_DistPlaneToPos(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* p); +s32 Math3D_TriChkPointParaYSlopedY(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 z, f32 x); +s32 Math3D_TriChkPointParaYIntersectDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 z, + f32 x, f32* yIntersect, f32 chkDist); +s32 Math3D_TriChkPointParaYIntersectInsideTri(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, + f32 z, f32 x, f32* yIntersect, f32 chkDist); +s32 Math3D_TriChkLineSegParaYIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 z, + f32 x, f32* yIntersect, f32 y0, f32 y1); +s32 Math3D_TriChkPointParaYDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, Plane* plane, f32 z, f32 x, f32 chkDist); +s32 Math3D_TriChkPointParaXIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 y, + f32 z, f32* xIntersect); +s32 Math3D_TriChkLineSegParaXIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 y, + f32 z, f32* xIntersect, f32 x0, f32 x1); +s32 Math3D_TriChkPointParaXDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, Plane* plane, f32 y, f32 z, f32 chkDist); +s32 Math3D_TriChkPointParaZIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 x, + f32 y, f32* zIntersect); +s32 Math3D_TriChkLineSegParaZIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 x, + f32 y, f32* zIntersect, f32 z0, f32 z1); +s32 Math3D_TriChkLineSegParaZDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, Plane* plane, f32 x, f32 y, f32 chkDist); +s32 Math3D_LineSegVsPlane(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* linePointA, Vec3f* linePointB, + Vec3f* intersect, s32 fromFront); +void Math3D_TriNorm(TriNorm* tri, Vec3f* va, Vec3f* vb, Vec3f* vc); +s32 Math3D_PointDistToLine2D(f32 x0, f32 y0, f32 x1, f32 y1, f32 x2, f32 y2, f32* lineLenSq); +s32 Math3D_LineVsSph(Sphere16* sphere, Linef* line); +s32 Math3D_TriVsSphIntersect(Sphere16* sphere, TriNorm* tri, Vec3f* intersectPoint); +s32 Math3D_CylVsLineSeg(Cylinder16* cyl, Vec3f* linePointA, Vec3f* linePointB, Vec3f* intersectA, Vec3f* intersectB); +s32 Math3D_CylVsTri(Cylinder16* cyl, TriNorm* tri); +s32 Math3D_CylTriVsIntersect(Cylinder16* cyl, TriNorm* tri, Vec3f* intersect); +s32 Math3D_SphVsSph(Sphere16* sphereA, Sphere16* sphereB); +s32 Math3D_SphVsSphOverlap(Sphere16* sphereA, Sphere16* sphereB, f32* overlapSize); +s32 Math3D_SphVsSphOverlapCenter(Sphere16* sphereA, Sphere16* sphereB, f32* overlapSize, f32* centerDist); +s32 Math3D_SphVsCylOverlapDist(Sphere16* sph, Cylinder16* cyl, f32* overlapSize); +s32 Math3D_SphVsCylOverlapCenterDist(Sphere16* sph, Cylinder16* cyl, f32* overlapSize, f32* centerDist); +s32 Math3D_CylOutsideCyl(Cylinder16* ca, Cylinder16* cb, f32* deadSpace); +s32 Math3D_CylOutsideCylDist(Cylinder16* ca, Cylinder16* cb, f32* deadSpace, f32* xzDist); +s32 Math3D_TriVsTriIntersect(TriNorm* ta, TriNorm* tb, Vec3f* intersect); +s32 Math3D_XZInSphere(Sphere16* sphere, f32 x, f32 z); +s32 Math3D_XYInSphere(Sphere16* sphere, f32 x, f32 y); +s32 Math3D_YZInSphere(Sphere16* sphere, f32 y, f32 z); +void Math3D_DrawSphere(GlobalContext* globalCtx, Sphere16* sph); +void Math3D_DrawCylinder(GlobalContext* globalCtx, Cylinder16* cyl); +s16 Math_Atan2S(f32 x, f32 y); +f32 Math_Atan2F(f32 x, f32 y); +void Matrix_Init(GameState* gameState); +void Matrix_Push(void); +void Matrix_Pop(void); +void Matrix_Get(MtxF* dest); +void Matrix_Put(MtxF* src); +void Matrix_Mult(MtxF* mf, u8 mode); +void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode); +void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode); +void Matrix_RotateX(f32 x, u8 mode); +void Matrix_RotateY(f32 y, u8 mode); +void Matrix_RotateZ(f32 z, u8 mode); +void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode); +void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rotation); +void Matrix_SetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot); +Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest); +Mtx* Matrix_ToMtx(Mtx* dest, char* file, s32 line); +Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx, char* file, s32 line); +Mtx* Matrix_MtxFToNewMtx(MtxF* src, GraphicsContext* gfxCtx); +void Matrix_MultVec3f(Vec3f* src, Vec3f* dest); +void Matrix_MtxFCopy(MtxF* dest, MtxF* src); +void Matrix_MtxToMtxF(Mtx* src, MtxF* dest); +void Matrix_MultVec3fExt(Vec3f* src, Vec3f* dest, MtxF* mf); +void Matrix_Transpose(MtxF* mf); +void Matrix_ReplaceRotation(MtxF* mf); +void Matrix_MtxFToYXZRotS(MtxF* mf, Vec3s* rotDest, s32 flag); +void Matrix_MtxFToZYXRotS(MtxF* mf, Vec3s* rotDest, s32 flag); +void Matrix_RotateAxis(f32 angle, Vec3f* axis, u8 mode); +MtxF* Matrix_CheckFloats(MtxF* mf, char* file, s32 line); +void Matrix_SetTranslateScaleMtx2(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY, + f32 translateZ); +uintptr_t SysUcode_GetUCodeBoot(void); +uintptr_t SysUcode_GetUCodeBootSize(void); +uintptr_t SysUcode_GetUCode(void); +uintptr_t SysUcode_GetUCodeData(void); +void func_800D2E30(UnkRumbleStruct* arg0); +void func_800D3140(UnkRumbleStruct* arg0); +void func_800D3178(UnkRumbleStruct* arg0); +void func_800D31A0(void); +void func_800D31F0(void); +void func_800D3210(void); +void IrqMgr_AddClient(IrqMgr* this, IrqMgrClient* c, OSMesgQueue* msgQ); +void IrqMgr_RemoveClient(IrqMgr* this, IrqMgrClient* c); +void IrqMgr_SendMesgForClient(IrqMgr* this, OSMesg msg); +void IrqMgr_JamMesgForClient(IrqMgr* this, OSMesg msg); +void IrqMgr_HandlePreNMI(IrqMgr* this); +void IrqMgr_CheckStack(); +void IrqMgr_HandlePRENMI450(IrqMgr* this); +void IrqMgr_HandlePRENMI480(IrqMgr* this); +void IrqMgr_HandlePRENMI500(IrqMgr* this); +void IrqMgr_HandleRetrace(IrqMgr* this); +void IrqMgr_ThreadEntry(void* arg0); +void IrqMgr_Init(IrqMgr* this, void* stack, OSPri pri, u8 retraceCount); +void DebugArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action); +void* DebugArena_Malloc(size_t size); +void* DebugArena_MallocDebug(size_t size, const char* file, s32 line); +void* DebugArena_MallocR(size_t size); +void* DebugArena_MallocRDebug(size_t size, const char* file, s32 line); +void* DebugArena_Realloc(void* ptr, size_t newSize); +void* DebugArena_ReallocDebug(void* ptr, size_t newSize, const char* file, s32 line); +void DebugArena_Free(void* ptr); +void DebugArena_FreeDebug(void* ptr, const char* file, s32 line); +void* DebugArena_Calloc(size_t num, size_t size); +void DebugArena_Display(void); +void DebugArena_GetSizes(u32* outMaxFree, u32* outFree, u32* outAlloc); +void DebugArena_Check(void); +void DebugArena_Init(void* start, size_t size); +void DebugArena_Cleanup(void); +u8 DebugArena_IsInitalized(void); +void Fault_SleepImpl(u32); +void Fault_ClientProcessThread(void* arg); +void Fault_ProcessClientContext(FaultClientContext*); +u32 Fault_ProcessClient(u32, u32, u32); +void Fault_AddClient(FaultClient*, void*, void*, void*); +void Fault_RemoveClient(FaultClient*); +void Fault_AddAddrConvClient(FaultAddrConvClient*, void*, void*); +void Fault_RemoveAddrConvClient(FaultAddrConvClient*); +u32 Fault_ConvertAddress(FaultAddrConvClient*); +void Fault_Sleep(u32); +void Fault_PadCallback(Input*); +void Fault_UpdatePadImpl(); +u32 Fault_WaitForInputImpl(); +void Fault_WaitForInput(); +void Fault_DrawRec(s32, s32, s32, s32, u16); +void Fault_FillScreenBlack(); +void Fault_FillScreenRed(); +void Fault_DrawCornerRec(u16); +void Fault_PrintFReg(s32, f32*); +void Fault_LogFReg(s32, f32*); +void Fault_PrintFPCR(u32); +void Fault_LogFPCR(u32); +void Fault_PrintThreadContext(OSThread*); +void Fault_LogThreadContext(OSThread*); +OSThread* Fault_FindFaultedThread(); +void Fault_Wait5Seconds(); +void Fault_WaitForButtonCombo(); +void Fault_DrawMemDumpPage(const char*, u32*, u32); +void Fault_DrawMemDump(u32, u32, u32, u32); +void Fault_WalkStack(u32* spPtr, u32* pcPtr, u32* raPtr); +void Fault_DrawStackTrace(OSThread* thread, s32 x, s32 y, s32 height); +void Fault_LogStackTrace(OSThread* thread, s32 height); +void Fault_ResumeThread(OSThread*); +void Fault_CommitFB(); +void Fault_ProcessClients(); +void Fault_UpdatePad(); +void Fault_ThreadEntry(void*); +void Fault_SetFB(void*, u16, u16); +void Fault_Init(void); +void Fault_HangupFaultClient(const char*, const char*); +void Fault_AddHungupAndCrashImpl(const char*, const char*); +void Fault_AddHungupAndCrash(const char*, u32); +void FaultDrawer_SetOsSyncPrintfEnabled(u32); +void FaultDrawer_DrawRecImpl(s32, s32, s32, s32, u16); +void FaultDrawer_DrawChar(char); +s32 FaultDrawer_ColorToPrintColor(u16); +void FaultDrawer_UpdatePrintColor(); +void FaultDrawer_SetForeColor(u16); +void FaultDrawer_SetBackColor(u16); +void FaultDrawer_SetFontColor(u16); +void FaultDrawer_SetCharPad(s8, s8); +void FaultDrawer_SetCursor(s32, s32); +void FaultDrawer_FillScreen(); +void* FaultDrawer_FormatStringFunc(void*, const char*, u32); +void FaultDrawer_VPrintf(const char*, char*); +void FaultDrawer_Printf(const char*, ...); +void FaultDrawer_DrawText(s32, s32, const char*, ...); +void FaultDrawer_SetDrawerFB(void*, u16, u16); +void FaultDrawer_SetInputCallback(void (*)()); +void FaultDrawer_SetDefault(); +// ? UCodeDisas_TranslateAddr(?); +// ? UCodeDisas_ParseCombineColor(?); +// ? UCodeDisas_ParseCombineAlpha(?); +void UCodeDisas_Init(UCodeDisas*); +void UCodeDisas_Destroy(UCodeDisas*); +// ? UCodeDisas_SetCurUCodeImpl(?); +// ? UCodeDisas_ParseGeometryMode(?); +// ? UCodeDisas_ParseRenderMode(?); +// ? UCodeDisas_PrintVertices(?); +//void UCodeDisas_Disassemble(UCodeDisas*, Gfx*); +void UCodeDisas_RegisterUCode(UCodeDisas* this, s32 count, UCodeInfo* ucodeArray); +void UCodeDisas_SetCurUCode(UCodeDisas*, void*); +Acmd* AudioSynth_Update(Acmd* cmdStart, s32* cmdCnt, s16* aiStart, s32 aiBufLen); +void AudioHeap_DiscardFont(s32 fontId); +void AudioHeap_DiscardSequence(s32 seqId); +void AudioHeap_WritebackDCache(void* mem, size_t size); +void* AudioHeap_AllocZeroedAttemptExternal(AudioAllocPool* pool, size_t size); +void* AudioHeap_AllocAttemptExternal(AudioAllocPool* pool, size_t size); +void* AudioHeap_AllocDmaMemory(AudioAllocPool* pool, size_t size); +void* AudioHeap_AllocDmaMemoryZeroed(AudioAllocPool* pool, size_t size); +void* AudioHeap_AllocZeroed(AudioAllocPool* pool, size_t size); +void* AudioHeap_Alloc(AudioAllocPool* pool, size_t size); +void AudioHeap_AllocPoolInit(AudioAllocPool* pool, void* mem, size_t size); +void AudioHeap_PersistentCacheClear(AudioPersistentCache* persistent); +void AudioHeap_TemporaryCacheClear(AudioTemporaryCache* temporary); +void AudioHeap_PopCache(s32 tableType); +void AudioHeap_InitMainPools(ptrdiff_t sizeForAudioInitPool); +void* AudioHeap_AllocCached(s32 tableType, ptrdiff_t size, s32 cache, s32 id); +void* AudioHeap_SearchCaches(s32 tableType, s32 arg1, s32 id); +void* AudioHeap_SearchRegularCaches(s32 tableType, s32 cache, s32 id); +void AudioHeap_LoadFilter(s16* filter, s32 filter1, s32 filter2); +s32 AudioHeap_ResetStep(void); +void AudioHeap_Init(void); +void* AudioHeap_SearchPermanentCache(s32 tableType, s32 id); +void* AudioHeap_AllocPermanent(s32 tableType, s32 id, size_t size); +void* AudioHeap_AllocSampleCache(size_t size, s32 fontId, void* sampleAddr, s8 medium, s32 cache); +void AudioHeap_ApplySampleBankCache(s32 sampleBankId); +void AudioLoad_DecreaseSampleDmaTtls(void); +void* AudioLoad_DmaSampleData(u32 devAddr, size_t size, s32 arg2, u8* dmaIndexRef, s32 medium); +void AudioLoad_InitSampleDmaBuffers(s32 arg0); +s32 AudioLoad_IsFontLoadComplete(s32 fontId); +s32 AudioLoad_IsSeqLoadComplete(s32 seqId); +void AudioLoad_SetFontLoadStatus(s32 fontId, s32 status); +void AudioLoad_SetSeqLoadStatus(s32 seqId, s32 status); +void AudioLoad_SyncLoadSeqParts(s32 seqId, s32 arg1); +s32 AudioLoad_SyncLoadInstrument(s32 fontId, s32 instId, s32 drumId); +void AudioLoad_AsyncLoadSeq(s32 seqId, s32 arg1, s32 retData, OSMesgQueue* retQueue); +void AudioLoad_AsyncLoadSampleBank(s32 sampleBankId, s32 arg1, s32 retData, OSMesgQueue* retQueue); +void AudioLoad_AsyncLoadFont(s32 fontId, s32 arg1, s32 retData, OSMesgQueue* retQueue); +u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* arg1); +void AudioLoad_DiscardSeqFonts(s32 seqId); +s32 AudioLoad_SyncInitSeqPlayer(s32 playerIdx, s32 seqId, s32 arg2); +s32 AudioLoad_SyncInitSeqPlayerSkipTicks(s32 playerIdx, s32 seqId, s32 arg2); +void AudioLoad_ProcessLoads(s32 resetStatus); +void AudioLoad_SetDmaHandler(DmaHandler callback); +void AudioLoad_Init(void* heap, u32 heapSize); +void AudioLoad_InitSlowLoads(void); +s32 AudioLoad_SlowLoadSample(s32 arg0, s32 arg1, s8* arg2); +s32 AudioLoad_SlowLoadSeq(s32 playerIdx, u8* ramAddr, s8* arg2); +void AudioLoad_InitAsyncLoads(void); +void AudioLoad_LoadPermanentSamples(void); +void AudioLoad_ScriptLoad(s32 tableType, s32 arg1, s8* arg2); +void AudioLoad_ProcessScriptLoads(void); +void AudioLoad_InitScriptLoads(void); +AudioTask* func_800E4FE0(void); +void Audio_QueueCmdF32(u32 arg0, f32 arg1); +void Audio_QueueCmdS32(u32 arg0, s32 arg1); +void Audio_QueueCmdS8(u32 arg0, s8 arg1); +void Audio_QueueCmdU16(u32 arg0, u16 arg1); +s32 Audio_ScheduleProcessCmds(void); +u32 func_800E5E20(u32* arg0); +u8* func_800E5E84(s32 arg0, u32* arg1); +s32 func_800E5EDC(void); +s32 func_800E5F88(s32 arg0); +void Audio_PreNMIInternal(void); +s32 func_800E6680(void); +u32 Audio_NextRandom(void); +void Audio_InitMesgQueues(void); +void Audio_InvalDCache(void* buf, size_t size); +void Audio_WritebackDCache(void* mem, size_t size); +s32 osAiSetNextBuffer(void* buf, size_t size); +void Audio_InitNoteSub(Note* note, NoteSubEu* sub, NoteSubAttributes* attrs); +void Audio_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput); +void Audio_NoteInit(Note* note); +void Audio_NoteDisable(Note* note); +void Audio_ProcessNotes(void); +SoundFontSound* Audio_InstrumentGetSound(Instrument* instrument, s32 semitone); +Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId); +Drum* Audio_GetDrum(s32 fontId, s32 drumId); +SoundFontSound* Audio_GetSfx(s32 fontId, s32 sfxId); +s32 Audio_SetFontInstrument(s32 instrumentType, s32 fontId, s32 index, void* value); +void Audio_SeqLayerDecayRelease(SequenceLayer* layer, s32 target); +void Audio_SeqLayerNoteDecay(SequenceLayer* layer); +void Audio_SeqLayerNoteRelease(SequenceLayer* layer); +s32 Audio_BuildSyntheticWave(Note* note, SequenceLayer* layer, s32 waveId); +void Audio_InitSyntheticWave(Note* note, SequenceLayer* layer); +void Audio_InitNoteList(AudioListItem* list); +void Audio_InitNoteLists(NotePool* pool); +void Audio_InitNoteFreeList(void); +void Audio_NotePoolClear(NotePool* pool); +void Audio_NotePoolFill(NotePool* pool, s32 count); +void Audio_AudioListPushFront(AudioListItem* list, AudioListItem* item); +void Audio_AudioListRemove(AudioListItem* item); +Note* Audio_FindNodeWithPrioLessThan(AudioListItem* list, s32 limit); +void Audio_NoteInitForLayer(Note* note, SequenceLayer* layer); +void func_800E82C0(Note* note, SequenceLayer* layer); +void Audio_NoteReleaseAndTakeOwnership(Note* note, SequenceLayer* layer); +Note* Audio_AllocNoteFromDisabled(NotePool* pool, SequenceLayer* layer); +Note* Audio_AllocNoteFromDecaying(NotePool* pool, SequenceLayer* layer); +Note* Audio_AllocNoteFromActive(NotePool* pool, SequenceLayer* layer); +Note* Audio_AllocNote(SequenceLayer* layer); +void Audio_NoteInitAll(void); +void Audio_SequenceChannelProcessSound(SequenceChannel* channel, s32 recalculateVolume, s32 b); +void Audio_SequencePlayerProcessSound(SequencePlayer* seqPlayer); +f32 Audio_GetPortamentoFreqScale(Portamento* p); +s16 Audio_GetVibratoPitchChange(VibratoState* vib); +f32 Audio_GetVibratoFreqScale(VibratoState* vib); +void Audio_NoteVibratoUpdate(Note* note); +void Audio_NoteVibratoInit(Note* note); +void Audio_NotePortamentoInit(Note* note); +void Audio_AdsrInit(AdsrState* adsr, AdsrEnvelope* envelope, s16* volOut); +f32 Audio_AdsrUpdate(AdsrState* adsr); +void AudioSeq_SequenceChannelDisable(SequenceChannel* channel); +void AudioSeq_SequencePlayerDisableAsFinished(SequencePlayer* seqPlayer); +void AudioSeq_SequencePlayerDisable(SequencePlayer* seqPlayer); +void AudioSeq_AudioListPushBack(AudioListItem* list, AudioListItem* item); +void* AudioSeq_AudioListPopBack(AudioListItem* list); +void AudioSeq_ProcessSequences(s32 arg0); +void AudioSeq_SkipForwardSequence(SequencePlayer* seqPlayer); +void AudioSeq_ResetSequencePlayer(SequencePlayer* seqPlayer); +void AudioSeq_InitSequencePlayerChannels(s32 playerIdx); +void AudioSeq_InitSequencePlayers(void); +void func_800ECC04(u16); +void Audio_OcaSetInstrument(u8); +void Audio_OcaSetSongPlayback(s8 songIdxPlusOne, s8 playbackState); +void Audio_OcaSetRecordingState(u8); +OcarinaStaff* Audio_OcaGetRecordingStaff(void); +OcarinaStaff* Audio_OcaGetPlayingStaff(void); +OcarinaStaff* Audio_OcaGetDisplayingStaff(void); +void Audio_OcaMemoryGameStart(u8 minigameIdx); +s32 Audio_OcaMemoryGameGenNote(void); +void func_800EE824(void); +void AudioDebug_Draw(GfxPrint* printer); +void AudioDebug_ScrPrt(const s8* str, u16 num); +void func_800F3054(void); +void Audio_SetSoundProperties(u8 bankId, u8 entryIdx, u8 channelIdx); +void func_800F3F3C(u8); +void func_800F4010(Vec3f* pos, u16 sfxId, f32); +void Audio_PlaySoundRandom(Vec3f* pos, u16 baseSfxId, u8 randLim); +void func_800F4138(Vec3f* pos, u16 sfxId, f32); +void func_800F4190(Vec3f* pos, u16 sfxId); +void func_800F436C(Vec3f* pos, u16 sfxId, f32 arg2); +void func_800F4414(Vec3f* pos, u16 sfxId, f32); +void func_800F44EC(s8 arg0, s8 arg1); +void func_800F4524(Vec3f* pos, u16 sfxId, s8 arg2); +void func_800F4254(Vec3f* pos, u8 arg1); +void func_800F436C(Vec3f*, u16 sfxId, f32 arg2); +void func_800F4414(Vec3f*, u16 sfxId, f32 arg2); +void Audio_PlaySoundRiver(Vec3f* pos, f32 freqScale); +void Audio_PlaySoundWaterfall(Vec3f* pos, f32 freqScale); +void func_800F47BC(void); +void func_800F47FC(void); +void func_800F483C(u8 targetVol, u8 volFadeTimer); +void func_800F4870(u8); +void func_800F4A54(u8); +void Audio_PlaySoundIncreasinglyTransposed(Vec3f* pos, s16 sfxId, u8* semitones); +void Audio_ResetIncreasingTranspose(void); +void Audio_PlaySoundTransposed(Vec3f* pos, u16 sfxId, s8 semitone); +void func_800F4C58(Vec3f* pos, u16 sfxId, u8); +void func_800F4E30(Vec3f* pos, f32); +void Audio_ClearSariaBgm(void); +void Audio_ClearSariaBgmAtPos(Vec3f* pos); +void Audio_PlaySariaBgm(Vec3f* pos, u16 seqId, u16 distMax); +void Audio_ClearSariaBgm2(void); +void func_800F5510(u16 seqId); +void func_800F5550(u16 seqId); +void func_800F574C(f32 arg0, u8 arg2); +void func_800F5718(void); +void func_800F5918(void); +void func_800F595C(u16); +void func_800F59E8(u16); +s32 func_800F5A58(u8); +void func_800F5ACC(u16 seqId); +void func_800F5B58(void); +void func_800F5BF0(u8 natureAmbienceId); +void Audio_PlayFanfare(u16); +void func_800F5C2C(void); +void func_800F5E18(u8 playerIdx, u16 seqId, u8 fadeTimer, s8 arg3, s8 arg4); +void Audio_SetSequenceMode(u8 seqMode); +void Audio_SetBgmEnemyVolume(f32 dist); +void func_800F6268(f32 dist, u16); +void func_800F64E0(u8 arg0); +void func_800F6584(u8 arg0); +void Audio_SetEnvReverb(s8 reverb); +void Audio_SetCodeReverb(s8 reverb); +void func_800F6700(s8 outputMode); +void Audio_SetBaseFilter(u8); +void Audio_SetExtraFilter(u8); +void Audio_SetCutsceneFlag(s8 flag); +void Audio_PlaySoundIfNotInCutscene(u16 sfxId); +void func_800F6964(u16); +void func_800F6AB0(u16); +// ? Audio_DisableAllSeq(?); +// ? func_800F6BB8(?); +void Audio_PreNMI(); +// ? func_800F6C34(?); +void Audio_SetNatureAmbienceChannelIO(u8 channelIdxRange, u8 port, u8 val); +void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId); +void Audio_Init(); +void Audio_InitSound(); +void func_800F7170(void); +void func_800F71BC(s32 arg0); +void Audio_SetSoundBanksMute(u16 muteMask); +void Audio_QueueSeqCmdMute(u8 channelIdx); +void Audio_ClearBGMMute(u8 channelIdx); +void Audio_PlaySoundGeneral(u16 sfxId, Vec3f* pos, u8 token, f32* freqScale, f32* a4, s8* reverbAdd); +void Audio_ProcessSoundRequest(void); +void Audio_ChooseActiveSounds(u8 bankId); +void Audio_PlayActiveSounds(u8 bankId); +void Audio_StopSfxByBank(u8 bankId); +void func_800F8884(u8, Vec3f*); +void Audio_StopSfxByPosAndBank(u8, Vec3f*); +void Audio_StopSfxByPos(Vec3f*); +void func_800F9280(u8 playerIdx, u8 seqId, u8 arg2, u16 fadeTimer); +void Audio_QueueSeqCmd(u32 bgmID); +void Audio_StopSfxByPosAndId(Vec3f* pos, u16 sfxId); +void Audio_StopSfxByTokenAndId(u8, u16); +void Audio_StopSfxById(u32 sfxId); +void Audio_ProcessSoundRequests(void); +void func_800F8F88(void); +u8 Audio_IsSfxPlaying(u32 sfxId); +void Audio_ResetSounds(void); +void func_800F9474(u8, u16); +void func_800F94FC(u32); +void Audio_ProcessSeqCmd(u32); +void Audio_ProcessSeqCmds(void); +u16 func_800FA0B4(u8 a0); +s32 func_800FA11C(u32 arg0, u32 arg1); +void func_800FA174(u8); +void func_800FA18C(u8, u8); +void Audio_SetVolScale(u8 playerIdx, u8 scaleIdx, u8 targetVol, u8 volFadeTimer); +void func_800FA3DC(void); +u8 func_800FAD34(void); +void func_800FADF8(void); +void func_800FAEB4(void); +void GfxPrint_SetColor(GfxPrint* this, u32 r, u32 g, u32 b, u32 a); +void GfxPrint_SetPosPx(GfxPrint* this, s32 x, s32 y); +void GfxPrint_SetPos(GfxPrint* this, s32 x, s32 y); +void GfxPrint_SetBasePosPx(GfxPrint* this, s32 x, s32 y); +void GfxPrint_Init(GfxPrint* this); +void GfxPrint_Destroy(GfxPrint* this); +void GfxPrint_Open(GfxPrint* this, Gfx* dList); +Gfx* GfxPrint_Close(GfxPrint* this); +s32 GfxPrint_Printf(GfxPrint* this, const char* fmt, ...); +void func_800FBCE0(); +void func_800FBFD8(void); +void* Overlay_AllocateAndLoad(uintptr_t vRomStart, uintptr_t vRomEnd, void* vRamStart, void* vRamEnd); +void MtxConv_F2L(Mtx* m1, MtxF* m2); +void MtxConv_L2F(MtxF* m1, Mtx* m2); +void Overlay_Relocate(void* allocatedVRamAddress, OverlayRelocationSection* overlayInfo, void* vRamAddress); +s32 Overlay_Load(uintptr_t vRomStart, uintptr_t vRomEnd, void* vRamStart, void* vRamEnd, void* allocatedVRamAddress); +// ? func_800FC800(?); +// ? func_800FC83C(?); +// ? func_800FCAB4(?); +void SystemHeap_Init(void* start, size_t size); +void PadUtils_Init(Input* input); +void func_800FCB70(void); +void PadUtils_ResetPressRel(Input* input); +u32 PadUtils_CheckCurExact(Input* input, u16 value); +u32 PadUtils_CheckCur(Input* input, u16 key); +u32 PadUtils_CheckPressed(Input* input, u16 key); +u32 PadUtils_CheckReleased(Input* input, u16 key); +u16 PadUtils_GetCurButton(Input* input); +u16 PadUtils_GetPressButton(Input* input); +s8 PadUtils_GetCurX(Input* input); +s8 PadUtils_GetCurY(Input* input); +void PadUtils_SetRelXY(Input* input, s32 x, s32 y); +s8 PadUtils_GetRelXImpl(Input* input); +s8 PadUtils_GetRelYImpl(Input* input); +s8 PadUtils_GetRelX(Input* input); +s8 PadUtils_GetRelY(Input* input); +void PadUtils_UpdateRelXY(Input* input); +s32 PadSetup_Init(OSMesgQueue* mq, u8* outMask, OSContStatus* status); +f32 Math_FTanF(f32 x); +f32 Math_FFloorF(f32 x); +f32 Math_FCeilF(f32 x); +f32 Math_FRoundF(f32 x); +f32 Math_FNearbyIntF(f32 x); +f32 Math_FTruncF(f32 x); +f32 Math_FAtanF(f32 x); +f32 Math_FAtan2F(f32 y, f32 x); +f32 Math_FAsinF(f32 x); +f32 Math_FAcosF(f32 x); +f32 ceilf(f32 x); +f32 truncf(f32 x); +f32 roundf(f32 x); +f32 nearbyintf(f32 x); +void SystemArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action); +void* SystemArena_Malloc(size_t size); +void* SystemArena_MallocDebug(size_t size, const char* file, s32 line); +void* SystemArena_MallocR(size_t size); +void* SystemArena_MallocRDebug(size_t size, const char* file, s32 line); +void* SystemArena_Realloc(void* ptr, size_t newSize); +void* SystemArena_ReallocDebug(void* ptr, size_t newSize, const char* file, s32 line); +void SystemArena_Free(void* ptr); +void SystemArena_FreeDebug(void* ptr, const char* file, s32 line); +void* SystemArena_Calloc(size_t num, size_t size); +void SystemArena_Display(void); +void SystemArena_GetSizes(u32* outMaxFree, u32* outFree, u32* outAlloc); +void SystemArena_Check(void); +void SystemArena_Init(void* start, size_t size); +void SystemArena_Cleanup(void); +u8 SystemArena_IsInitalized(void); +u32 Rand_Next(void); +void Rand_Seed(u32 seed); +f32 Rand_ZeroOne(void); +f32 Rand_Centered(void); +void Rand_Seed_Variable(u32* rndNum, u32 seed); +u32 Rand_Next_Variable(u32* rndNum); +f32 Rand_ZeroOne_Variable(u32* rndNum); +f32 Rand_Centered_Variable(u32* rndNum); +u32 ArenaImpl_GetFillAllocBlock(Arena* arena); +u32 ArenaImpl_GetFillFreeBlock(Arena* arena); +u32 ArenaImpl_GetCheckFreeBlock(Arena* arena); +void ArenaImpl_SetFillAllocBlock(Arena* arena); +void ArenaImpl_SetFillFreeBlock(Arena* arena); +void ArenaImpl_SetCheckFreeBlock(Arena* arena); +void ArenaImpl_UnsetFillAllocBlock(Arena* arena); +void ArenaImpl_UnsetFillFreeBlock(Arena* arena); +void ArenaImpl_UnsetCheckFreeBlock(Arena* arena); +void ArenaImpl_SetDebugInfo(ArenaNode* node, const char* file, s32 line, Arena* arena); +void ArenaImpl_LockInit(Arena* arena); +void ArenaImpl_Lock(Arena* arena); +void ArenaImpl_Unlock(Arena* arena); +ArenaNode* ArenaImpl_GetNextBlock(ArenaNode* node); +ArenaNode* ArenaImpl_GetPrevBlock(ArenaNode* node); +ArenaNode* ArenaImpl_GetLastBlock(Arena* arena); +void __osMallocInit(Arena* arena, void* start, size_t size); +void __osMallocAddBlock(Arena* arena, void* start, ptrdiff_t size); +void ArenaImpl_RemoveAllBlocks(Arena* arena); +void __osMallocCleanup(Arena* arena); +u8 __osMallocIsInitalized(Arena* arena); +void __osMalloc_FreeBlockTest(Arena* arena, ArenaNode* node); +void* __osMalloc_NoLockDebug(Arena* arena, size_t size, const char* file, s32 line); +void* __osMallocDebug(Arena* arena, size_t size, const char* file, s32 line); +void* __osMallocRDebug(Arena* arena, size_t size, const char* file, s32 line); +void* __osMalloc_NoLock(Arena* arena, size_t size); +void* __osMalloc(Arena* arena, size_t size); +void* __osMallocR(Arena* arena, size_t size); +void __osFree_NoLock(Arena* arena, void* ptr); +void __osFree(Arena* arena, void* ptr); +void __osFree_NoLockDebug(Arena* arena, void* ptr, const char* file, s32 line); +void __osFreeDebug(Arena* arena, void* ptr, const char* file, s32 line); +void* __osRealloc(Arena* arena, void* ptr, size_t newSize); +void* __osReallocDebug(Arena* arena, void* ptr, size_t newSize, const char* file, s32 line); +void ArenaImpl_GetSizes(Arena* arena, u32* outMaxFree, u32* outFree, u32* outAlloc); +void __osDisplayArena(Arena* arena); +void ArenaImpl_FaultClient(Arena* arena); +u32 __osCheckArena(Arena* arena); +u8 func_800FF334(Arena* arena); +s32 PrintUtils_VPrintf(PrintCallback* pfn, const char* fmt, va_list args); +s32 PrintUtils_Printf(PrintCallback* pfn, const char* fmt, ...); +void Sleep_Cycles(OSTime cycles); +void Sleep_Nsec(u32 nsec); +void Sleep_Usec(u32 usec); +void Sleep_Msec(u32 ms); +void Sleep_Sec(u32 sec); +void JpegUtils_ProcessQuantizationTable(u8* dqt, JpegQuantizationTable* qt, u8 count); +s32 JpegUtils_ParseHuffmanCodesLengths(u8* ptr, u8* codesLengths); +s32 JpegUtils_GetHuffmanCodes(u8* codesLengths, u16* codes); +s32 JpegUtils_SetHuffmanTable(u8* data, JpegHuffmanTable* ht, u16* codes); +u32 JpegUtils_ProcessHuffmanTableImpl(u8* data, JpegHuffmanTable* ht, u8* codesLengths, u16* codes, u8 isAc); +u32 JpegUtils_ProcessHuffmanTable(u8* dht, JpegHuffmanTable* ht, u8* codesLengths, u16* codes, u8 count); +void JpegUtils_SetHuffmanTableOld(u8* data, JpegHuffmanTableOld* ht, u8* codesLengths, u16* codes, s16 count, u8 isAc); +u32 JpegUtils_ProcessHuffmanTableImplOld(u8* dht, JpegHuffmanTableOld* ht, u8* codesLengths, u16* codes); +s32 JpegDecoder_Decode(JpegDecoder* decoder, u16* mcuBuff, s32 count, u8 isFollowing, JpegDecoderState* state); +s32 JpegDecoder_ProcessMcu(JpegHuffmanTable* hTable0, JpegHuffmanTable* hTable1, u16* mcu, s16* unk); +s32 JpegDecoder_ParseNextSymbol(JpegHuffmanTable* hTable, s16* outCoeff, s8* outZeroCount); +u16 JpegDecoder_ReadBits(u8 len); +s32 osPfsFreeBlocks(OSPfs* pfs, s32* leftoverBytes); +void guScale(Mtx* m, f32 x, f32 y, f32 z); +f32 sinf(f32); +s16 sins(u16); +OSTask* _VirtualToPhysicalTask(OSTask* intp); +void osSpTaskLoad(OSTask* task); +void osSpTaskStartGo(OSTask* task); +void __osSiCreateAccessQueue(void); +void __osSiGetAccess(void); +void __osSiRelAccess(void); +s32 osContInit(OSMesgQueue* mq, u8* ctl_present_bitfield, OSContStatus* status); +void __osContGetInitData(u8* ctl_present_bitfield, OSContStatus* status); +void __osPackRequestData(u8 poll); +s32 osContStartReadData(OSMesgQueue* mq); +void osContGetReadData(OSContPad* pad); +void __osPackReadData(void); +void guPerspectiveF(f32 mf[4][4], u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 far, f32 scale); +void guPerspective(Mtx* m, u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 far, f32 scale); +s32 __osSpRawStartDma(s32 direction, void* devAddr, void* dramAddr, size_t size); +s32 __osSiRawStartDma(s32 dir, void* addr); +void osSpTaskYield(void); +s32 __osPfsGetNextPage(OSPfs* pfs, u8* bank, __OSInode* inode, __OSInodeUnit* page); +s32 osPfsReadWriteFile(OSPfs* pfs, s32 fileNo, u8 flag, s32 offset, ptrdiff_t size, u8* data); +s32 __osPfsGetStatus(OSMesgQueue* queue, s32 channel); +void __osPfsRequestOneChannel(s32 channel, u8 poll); +void __osPfsGetOneChannelData(s32 channel, OSContStatus* contData); +void guMtxIdentF(f32 mf[4][4]); +void guLookAtF(f32 mf[4][4], f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, f32 yUp, f32 zUp); +void guLookAt(Mtx*, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, f32 yUp, f32 zUp); +s32 osPfsAllocateFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName, s32 length, s32* fileNo); +s32 __osPfsDeclearPage(OSPfs* pfs, __OSInode* inode, s32 fileSizeInPages, s32* startPage, u8 bank, s32* decleared, + s32* finalPage); +s32 osStopTimer(OSTimer* timer); +u16 __osSumcalc(u8* ptr, s32 length); +s32 __osIdCheckSum(u16* ptr, u16* csum, u16* icsum); +s32 __osRepairPackId(OSPfs* pfs, __OSPackId* badid, __OSPackId* newid); +s32 __osCheckPackId(OSPfs* pfs, __OSPackId* check); +s32 __osGetId(OSPfs* pfs); +s32 __osCheckId(OSPfs* pfs); +s32 __osPfsRWInode(OSPfs* pfs, __OSInode* inode, u8 flag, u8 bank); +void guMtxL2F(MtxF* m1, Mtx* m2); +s32 osPfsFindFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName, s32* fileNo); +s32 osAfterPreNMI(void); +s32 osContStartQuery(OSMesgQueue* mq); +void osContGetQuery(OSContStatus* data); +void guLookAtHiliteF(f32 mf[4][4], LookAt* l, Hilite* h, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, + f32 xUp, f32 yUp, f32 zUp, f32 xl1, f32 yl1, f32 zl1, f32 xl2, f32 yl2, f32 zl2, s32 hiliteWidth, + s32 hiliteHeight); +void guLookAtHilite(Mtx* m, LookAt* l, Hilite* h, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, + f32 yUp, f32 zUp, f32 xl1, f32 yl1, f32 zl1, f32 xl2, f32 yl2, f32 zl2, s32 hiliteWidth, + s32 hiliteHeight); +u32 __osSpDeviceBusy(void); +void guMtxIdent(Mtx*); +void guPositionF(f32 mf[4][4], f32 rot, f32 pitch, f32 yaw, f32 scale, f32 x, f32 y, f32 z); +void guPosition(Mtx*, f32, f32, f32, f32, f32, f32, f32); +OSYieldResult osSpTaskYielded(OSTask* task); +void guRotateF(f32 m[4][4], f32 a, f32 x, f32 y, f32 z); +void guRotate(Mtx*, f32 angle, f32 x, f32 y, f32 z); +s32 osAiSetFrequency(u32 frequency); +OSThread* __osGetActiveQueue(void); +void guNormalize(f32* x, f32* y, f32* z); +u32 osDpGetStatus(void); +void osDpSetStatus(u32 status); +s32 osPfsDeleteFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName); +s32 __osPfsReleasePages(OSPfs* pfs, __OSInode* inode, u8 initialPage, u8 bank, __OSInodeUnit* finalPage); +void guOrthoF(f32[4][4], f32, f32, f32, f32, f32, f32, f32); +void guOrtho(Mtx*, f32, f32, f32, f32, f32, f32, f32); +f32 cosf(f32); +s16 coss(u16); +void osViSetEvent(OSMesgQueue* mq, OSMesg m, u32 retraceCount); +s32 osPfsIsPlug(OSMesgQueue* mq, u8* pattern); +void __osPfsRequestData(u8 poll); +void __osPfsGetInitData(u8* pattern, OSContStatus* contData); +#ifndef __cplusplus +void guS2DInitBg(uObjBg* bg); +#endif +s32 __osPfsSelectBank(OSPfs* pfs, u8 bank); +s32 osContSetCh(u8 ch); +s32 osPfsFileState(OSPfs* pfs, s32 fileNo, OSPfsState* state); +s32 osPfsInitPak(OSMesgQueue* mq, OSPfs* pfs, s32 channel); +s32 __osPfsCheckRamArea(OSPfs* pfs); +s32 osPfsChecker(OSPfs* pfs); +s32 func_80105788(OSPfs* pfs, __OSInodeCache* cache); +s32 func_80105A60(OSPfs* pfs, __OSInodeUnit fpage, __OSInodeCache* cache); +u32 osAiGetLength(void); +void guTranslate(Mtx* m, f32 x, f32 y, f32 z); +s32 __osContRamWrite(OSMesgQueue* mq, s32 channel, u16 address, u8* buffer, s32 force); +s32 __osContRamRead(OSMesgQueue* ctrlrqueue, s32 channel, u16 addr, u8* data); +u8 __osContAddressCrc(u16 addr); +u8 __osContDataCrc(u8* data); +s32 osSetTimer(OSTimer* timer, OSTime countdown, OSTime interval, OSMesgQueue* mq, OSMesg msg); +u32 __osSpGetStatus(void); +void __osSpSetStatus(u32 status); +void osWritebackDCacheAll(void); +OSThread* __osGetCurrFaultedThread(void); +void guMtxF2L(MtxF* m1, Mtx* m2); +// ? __d_to_ll(?); +// ? __f_to_ll(?); +// ? __d_to_ull(?); +// ? __f_to_ull(?); +// ? __ll_to_d(?); +// ? __ll_to_f(?); +// ? __ull_to_d(?); +// ? __ull_to_f(?); +u32* osViGetCurrentFramebuffer(void); +s32 __osSpSetPc(void* pc); +f32 absf(f32); +void* func_801068B0(void* dst, void* src, size_t size); +void Message_UpdateOcarinaGame(GlobalContext* globalCtx); +u8 Message_ShouldAdvance(GlobalContext* globalCtx); +void Message_CloseTextbox(GlobalContext*); +void Message_StartTextbox(GlobalContext* globalCtx, u16 textId, Actor* actor); +void Message_ContinueTextbox(GlobalContext* globalCtx, u16 textId); +void func_8010BD58(GlobalContext* globalCtx, u16 arg1); +void func_8010BD88(GlobalContext* globalCtx, u16 arg1); +u8 Message_GetState(MessageContext* msgCtx); +void Message_Draw(GlobalContext* globalCtx); +void Message_Update(GlobalContext* globalCtx); +void Message_SetTables(void); +void GameOver_Init(GlobalContext* globalCtx); +void GameOver_FadeInLights(GlobalContext* globalCtx); +void GameOver_Update(GlobalContext* globalCtx); +void func_80110990(GlobalContext* globalCtx); +void func_801109B0(GlobalContext* globalCtx); +void Message_Init(GlobalContext* globalCtx); +void func_80112098(GlobalContext* globalCtx); + +void Title_Init(GameState* thisx); +void Title_PrintBuildInfo(Gfx** gfxp); +void Title_Destroy(GameState* thisx); +void Select_Init(GameState* thisx); +void Select_Destroy(GameState* thisx); +void Opening_Init(GameState* thisx); +void Opening_Destroy(GameState* thisx); +void FileChoose_Init(GameState* thisx); +void FileChoose_Destroy(GameState* thisx); + +char* SetQuote(); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/soh/include/global.h b/soh/include/global.h new file mode 100644 index 000000000..4845728dd --- /dev/null +++ b/soh/include/global.h @@ -0,0 +1,49 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include "functions.h" +#include "variables.h" +#include "macros.h" +#include "soh\OTRGlobals.h" +#include "soh\Enhancements\gameconsole.h" +#include "Cvar.h" + + + + + + +#define _AudioseqSegmentRomStart "Audioseq" +#define _AudiobankSegmentRomStart "Audiobank" +#define _AudiotableSegmentRomStart "Audiotable" + + + + + +#define _icon_item_gameover_staticSegmentRomStart 0 +#define _icon_item_gameover_staticSegmentRomEnd 0 +#define _icon_item_staticSegmentRomStart 0 +#define _icon_item_staticSegmentRomEnd 0 +#define _map_i_staticSegmentRomStart 0 +#define _map_i_staticSegmentRomEnd 0 +#define _message_staticSegmentRomStart 0 +#define _message_staticSegmentRomEnd 0 +#define _do_action_staticSegmentRomStart 0 +#define _do_action_staticSegmentRomEnd 0 +#define _nes_font_staticSegmentRomStart 0 +#define _nes_font_staticSegmentRomEnd 0 +#define _nintendo_rogo_staticSegmentRomStart 0 +#define _nintendo_rogo_staticSegmentRomEnd 0 +#define _dmadataSegmentStart 0 +#define _dmadataSegmentEnd 0 +#define _parameter_staticSegmentRomStart 0 +#define _parameter_staticSegmentRomEnd 0 +#define _map_name_staticSegmentRomStart 0 +#define _map_name_staticSegmentRomEnd 0 +#define _title_staticSegmentRomStart 0 +#define _title_staticSegmentRomEnd 0 +#define _z_select_staticSegmentRomStart 0 +#define _z_select_staticSegmentRomEnd 0 + +#endif diff --git a/soh/include/ichain.h b/soh/include/ichain.h new file mode 100644 index 000000000..37af91057 --- /dev/null +++ b/soh/include/ichain.h @@ -0,0 +1,56 @@ +#ifndef ICHAIN_H +#define ICHAIN_H + +typedef struct { + u32 cont: 1; + u32 type: 4; + u32 offset: 11; + s32 value: 16; +} InitChainEntry; + +typedef enum { + /* 0x0 */ ICHAINTYPE_U8, // sets byte + /* 0x1 */ ICHAINTYPE_S8, + /* 0x2 */ ICHAINTYPE_U16, // sets short + /* 0x3 */ ICHAINTYPE_S16, + /* 0x4 */ ICHAINTYPE_U32, // sets word + /* 0x5 */ ICHAINTYPE_S32, + /* 0x6 */ ICHAINTYPE_F32, // sets float + /* 0x7 */ ICHAINTYPE_F32_DIV1000, // sets float divided by 1000 + /* 0x8 */ ICHAINTYPE_VEC3F, // sets Vec3f members + /* 0x9 */ ICHAINTYPE_VEC3F_DIV1000, // sets Vec3f members divided by 1000 + /* 0xA */ ICHAINTYPE_VEC3S // sets Vec3s members +} InitChainType; + +/** + * ICHAIN macros generate an init chain entry of the following form: + * * (e >> 31) & 0x0001 == Continue Parsing after this entry + * * (e >> 27) & 0x000F == Type + * * (e >> 16) & 0x07FF == Offset from start of instance to write initial value + * * e & 0xFFFF == Initial Value + * + * Arguments: + * * type ----- value from enum `InitChainType` + * * member --- name of member inside `Actor` structure to use as the offset + * * value ---- s16 value to use + * * cont ----- ICHAIN_CONTINUE (or ICHAIN_STOP) to continue (or stop) parsing + */ +#define ICHAIN(type, member, value, cont) \ + { cont, type, OFFSETOF(Actor, member), value } + +#define ICHAIN_U8(member, val, cont) ICHAIN(ICHAINTYPE_U8, member, val, cont) +#define ICHAIN_S8(member, val, cont) ICHAIN(ICHAINTYPE_S8, member, val, cont) +#define ICHAIN_U16(member, val, cont) ICHAIN(ICHAINTYPE_U16, member, val, cont) +#define ICHAIN_S16(member, val, cont) ICHAIN(ICHAINTYPE_S16, member, val, cont) +#define ICHAIN_U32(member, val, cont) ICHAIN(ICHAINTYPE_U32, member, val, cont) +#define ICHAIN_S32(member, val, cont) ICHAIN(ICHAINTYPE_S32, member, val, cont) +#define ICHAIN_F32(member, val, cont) ICHAIN(ICHAINTYPE_F32, member, val, cont) +#define ICHAIN_F32_DIV1000(member, val, cont) ICHAIN(ICHAINTYPE_F32_DIV1000, member, val, cont) +#define ICHAIN_VEC3F(member, val, cont) ICHAIN(ICHAINTYPE_VEC3F, member, val, cont) +#define ICHAIN_VEC3F_DIV1000(member, val, cont) ICHAIN(ICHAINTYPE_VEC3F_DIV1000, member, val, cont) +#define ICHAIN_VEC3S(member, val, cont) ICHAIN(ICHAINTYPE_VEC3S, member, val, cont) + +#define ICHAIN_CONTINUE 1 +#define ICHAIN_STOP 0 + +#endif diff --git a/soh/include/libc/math.h b/soh/include/libc/math.h new file mode 100644 index 000000000..bceffad22 --- /dev/null +++ b/soh/include/libc/math.h @@ -0,0 +1,29 @@ +#ifndef MATH_H +#define MATH_H + +#include "ultra64/types.h" + +#define M_PI 3.14159265358979323846f +#define M_SQRT2 1.41421356237309504880f +#define FLT_MAX 340282346638528859811704183484516925440.0f +#define SHT_MAX 32767.0f +#define SHT_MINV (1.0f / SHT_MAX) +#define DEGTORAD(x) (x * M_PI / 180.0f) + +typedef union { + struct { + u32 hi; + u32 lo; + } word; + + f64 d; +} du; + +typedef union { + u32 i; + f32 f; +} fu; + +extern f32 __libm_qnan_f; + +#endif diff --git a/soh/include/libc/stdarg.h b/soh/include/libc/stdarg.h new file mode 100644 index 000000000..daf456bb3 --- /dev/null +++ b/soh/include/libc/stdarg.h @@ -0,0 +1,43 @@ +#ifndef STDARG_H +#define STDARG_H + +// When building with GCC, use the official vaarg macros to avoid warnings +// and possibly bad codegen. +#ifdef __GNUC__ +#define va_list __builtin_va_list +#define va_start __builtin_va_start +#define va_arg __builtin_va_arg +#define va_end __builtin_va_end +#else + +typedef char* va_list; +#define _FP 1 +#define _INT 0 +#define _STRUCT 2 + +#define _VA_FP_SAVE_AREA 0x10 +#define _VA_ALIGN(p, a) (((uintptr_t)(((char*)p) + ((a) > 4 ? (a) : 4) - 1)) & -((a) > 4 ? (a) : 4)) +#define va_start(vp, parmN) (vp = ((va_list)&parmN + sizeof(parmN))) + +#define __va_stack_arg(list, mode) \ + ( \ + ((list) = (char*)_VA_ALIGN(list, __builtin_alignof(mode)) + \ + _VA_ALIGN(sizeof(mode), 4)), \ + (((char*)list) - (_VA_ALIGN(sizeof(mode), 4) - sizeof(mode)))) + +#define __va_double_arg(list, mode) \ + ( \ + (((s32)list & 0x1) /* 1 byte aligned? */ \ + ? (list = (char*)((s32)list + 7), (char*)((s32)list - 6 - _VA_FP_SAVE_AREA)) \ + : (((s32)list & 0x2) /* 2 byte aligned? */ \ + ? (list = (char*)((s32)list + 10), (char*)((s32)list - 24 - _VA_FP_SAVE_AREA)) \ + : __va_stack_arg(list, mode)))) + +#define va_arg(list, mode) ((mode*)(((__builtin_classof(mode) == _FP && \ + __builtin_alignof(mode) == sizeof(f64)) \ + ? __va_double_arg(list, mode) \ + : __va_stack_arg(list, mode))))[-1] +#define va_end(__list) + +#endif +#endif diff --git a/soh/include/libc/stdbool.h b/soh/include/libc/stdbool.h new file mode 100644 index 000000000..4495977a9 --- /dev/null +++ b/soh/include/libc/stdbool.h @@ -0,0 +1,14 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +#define __bool_true_false_are_defined 1 + +#ifndef __cplusplus + +#define bool u32 +#define false 0 +#define true 1 + +#endif /* __cplusplus */ + +#endif /* STDBOOL */ diff --git a/soh/include/libc/stddef.h b/soh/include/libc/stddef.h new file mode 100644 index 000000000..c30e392f8 --- /dev/null +++ b/soh/include/libc/stddef.h @@ -0,0 +1,14 @@ +#ifndef STDDEF_H +#define STDDEF_H + +#define NULL ((void*)0) +#if 0 +#define size_t unsigned long +#define ssize_t long + +#endif + +//typedef unsigned long size_t; +//typedef long ssize_t; + +#endif diff --git a/soh/include/libc/stdlib.h b/soh/include/libc/stdlib.h new file mode 100644 index 000000000..00418278a --- /dev/null +++ b/soh/include/libc/stdlib.h @@ -0,0 +1,21 @@ +#ifndef STDLIB_H +#define STDLIB_H + +#include "ultra64.h" + +#if 0 + +#ifndef __cplusplus +typedef struct lldiv_t { + s64 quot; + s64 rem; +} lldiv_t; + +typedef struct ldiv_t { + s32 quot; + s32 rem; +} ldiv_t; +#endif +#endif + +#endif diff --git a/soh/include/macro.inc b/soh/include/macro.inc new file mode 100644 index 000000000..1adda2564 --- /dev/null +++ b/soh/include/macro.inc @@ -0,0 +1,4 @@ +.macro glabel label + .global \label + \label: +.endm diff --git a/soh/include/macros.h b/soh/include/macros.h new file mode 100644 index 000000000..0d86c56c3 --- /dev/null +++ b/soh/include/macros.h @@ -0,0 +1,209 @@ +#ifndef MACROS_H +#define MACROS_H + +#define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0])) +#define ARRAY_COUNTU(arr) (u32)(sizeof(arr) / sizeof(arr[0])) + +#define PHYSICAL_TO_VIRTUAL(addr) (void*)((uintptr_t)(addr) + 0x80000000) +#define VIRTUAL_TO_PHYSICAL(addr) (uintptr_t)((u8*)(addr) - 0x80000000) +//#define SEGMENTED_TO_VIRTUAL(addr) PHYSICAL_TO_VIRTUAL(gSegments[SEGMENT_NUMBER(addr)] + SEGMENT_OFFSET(addr)) + +#define SEGMENTED_TO_VIRTUAL(addr) addr + +#define ALIGN16(val) (((val) + 0xF) & ~0xF) +#define ALIGN32(val) (((val) + 0x1F) & ~0x1F) +#define ALIGN64(val) (((val) + 0x3F) & ~0x3F) +#define ALIGN256(val) (((val) + 0xFF) & ~0xFF) + +#define OFFSETOF(structure, member) ((size_t)&(((structure*)0)->member)) + +#define SQ(x) ((x)*(x)) +#define ABS(x) ((x) >= 0 ? (x) : -(x)) +#define DECR(x) ((x) == 0 ? 0 : --(x)) +#define CLAMP(x, min, max) ((x) < (min) ? (min) : (x) > (max) ? (max) : (x)) +#define CLAMP_MAX(x, max) ((x) > (max) ? (max) : (x)) +#define CLAMP_MIN(x, min) ((x) < (min) ? (min) : (x)) +#define MEDIAN3(a1, a2, a3) \ + (((a2) >= (a1)) ? (((a3) >= (a2)) ? (a2) : (((a1) >= (a3)) ? (a1) : (a3))) \ + : (((a2) >= (a3)) ? (a2) : (((a3) >= (a1)) ? (a1) : (a3)))) + +#define RGBA8(r, g, b, a) ((((r) & 0xFF) << 24) | (((g) & 0xFF) << 16) | (((b) & 0xFF) << 8) | (((a) & 0xFF) << 0)) + +#define GET_PLAYER(globalCtx) ((Player*)(globalCtx)->actorCtx.actorLists[ACTORCAT_PLAYER].head) + +#define GET_ACTIVE_CAM(globalCtx) ((globalCtx)->cameraPtrs[(globalCtx)->activeCamera]) + +#define LINK_IS_ADULT (gSaveContext.linkAge == 0) +#define LINK_IS_CHILD (gSaveContext.linkAge == 1) + +#define YEARS_CHILD 5 +#define YEARS_ADULT 17 +#define LINK_AGE_IN_YEARS (!LINK_IS_ADULT ? YEARS_CHILD : YEARS_ADULT) + +#define IS_DAY (gSaveContext.nightFlag == 0) +#define IS_NIGHT (gSaveContext.nightFlag == 1) + +#define SLOT(item) gItemSlots[item] +#define INV_CONTENT(item) gSaveContext.inventory.items[SLOT(item)] +#define AMMO(item) gSaveContext.inventory.ammo[SLOT(item)] +#define BEANS_BOUGHT AMMO(ITEM_BEAN + 1) + +#define ALL_EQUIP_VALUE(equip) ((s32)(gSaveContext.inventory.equipment & gEquipMasks[equip]) >> gEquipShifts[equip]) +#define CUR_EQUIP_VALUE(equip) ((s32)(gSaveContext.equips.equipment & gEquipMasks[equip]) >> gEquipShifts[equip]) +#define CHECK_OWNED_EQUIP(equip, value) ((gBitFlags[value] << gEquipShifts[equip]) & gSaveContext.inventory.equipment) + +#define CUR_UPG_VALUE(upg) ((s32)(gSaveContext.inventory.upgrades & gUpgradeMasks[upg]) >> gUpgradeShifts[upg]) +#define CAPACITY(upg, value) gUpgradeCapacities[upg][value] +#define CUR_CAPACITY(upg) CAPACITY(upg, CUR_UPG_VALUE(upg)) + +#define CHECK_QUEST_ITEM(item) (gBitFlags[item] & gSaveContext.inventory.questItems) +#define CHECK_DUNGEON_ITEM(item, dungeonIndex) (gSaveContext.inventory.dungeonItems[dungeonIndex] & gBitFlags[item]) + +#define GET_GS_FLAGS(index) \ + ((gSaveContext.gsFlags[(index) >> 2] & gGsFlagsMasks[(index) & 3]) >> gGsFlagsShifts[(index) & 3]) +#define SET_GS_FLAGS(index, value) \ + (gSaveContext.gsFlags[(index) >> 2] |= (value) << gGsFlagsShifts[(index) & 3]) + +#define HIGH_SCORE(score) (gSaveContext.highScores[score]) + +#define B_BTN_ITEM ((gSaveContext.buttonStatus[0] == ITEM_NONE) \ + ? ITEM_NONE \ + : (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KNIFE) \ + ? ITEM_SWORD_BGS \ + : gSaveContext.equips.buttonItems[0]) + +#define C_BTN_ITEM(button) ((gSaveContext.buttonStatus[(button) + 1] != BTN_DISABLED) \ + ? gSaveContext.equips.buttonItems[(button) + 1] \ + : ITEM_NONE) + +#define CHECK_BTN_ALL(state, combo) (~((state) | ~(combo)) == 0) +#define CHECK_BTN_ANY(state, combo) (((state) & (combo)) != 0) + +#define CHECK_FLAG_ALL(flags, mask) (((flags) & (mask)) == (mask)) + +#ifndef NDEBUG +#define LOG(exp, value, format, file, line) \ + do { \ + LogUtils_LogThreadId(file, line); \ + osSyncPrintf(exp " = " format "\n", value); \ + } while (0) +#else +#define LOG(exp, value, format, file, line) ((void)0) +#endif + +#ifndef NDEBUG +#define LOG_STRING(string, file, line) LOG(#string, string, "%s", file, line) +#define LOG_ADDRESS(exp, value, file, line) LOG(exp, value, "%08x", file, line) +#define LOG_TIME(exp, value, file, line) LOG(exp, value, "%lld", file, line) +#define LOG_NUM(exp, value, file, line) LOG(exp, value, "%d", file, line) +#define LOG_HEX(exp, value, file, line) LOG(exp, value, "%x", file, line) +#define LOG_FLOAT(exp, value, file, line) LOG(exp, value, "%f", file, line) +#else +#define LOG_STRING(string, file, line) ((void)0) +#define LOG_ADDRESS(exp, value, file, line) ((void)0) +#define LOG_TIME(exp, value, file, line) ((void)0) +#define LOG_NUM(exp, value, file, line) ((void)0) +#define LOG_HEX(exp, value, file, line) ((void)0) +#define LOG_FLOAT(exp, value, file, line) ((void)0) +#endif + +#define SET_NEXT_GAMESTATE(curState, newInit, newStruct) \ + do { \ + (curState)->init = newInit; \ + (curState)->size = sizeof(newStruct); \ + } while (0) + +#define SET_FULLSCREEN_VIEWPORT(view) \ + { \ + Viewport viewport; \ + viewport.bottomY = SCREEN_HEIGHT; \ + viewport.rightX = SCREEN_WIDTH; \ + viewport.topY = 0; \ + viewport.leftX = 0; \ + View_SetViewport(view, &viewport); \ + } \ + (void)0 + +extern GraphicsContext* __gfxCtx; + +#define WORK_DISP __gfxCtx->work.p +#define POLY_OPA_DISP __gfxCtx->polyOpa.p +#define POLY_XLU_DISP __gfxCtx->polyXlu.p +#define POLY_KAL_DISP __gfxCtx->polyKal.p +#define OVERLAY_DISP __gfxCtx->overlay.p + +// __gfxCtx shouldn't be used directly. +// Use the DISP macros defined above when writing to display buffers. +#ifndef NDEBUG +#define OPEN_DISPS(gfxCtx, file, line) \ + { \ + GraphicsContext* __gfxCtx; \ + Gfx* dispRefs[4]; \ + __gfxCtx = gfxCtx; \ + (void)__gfxCtx; \ + Graph_OpenDisps(dispRefs, gfxCtx, file, line) +#else +#define OPEN_DISPS(gfxCtx, file, line) \ + { \ + GraphicsContext* __gfxCtx; \ + __gfxCtx = gfxCtx; \ + (void)__gfxCtx; +#endif + +#ifndef NDEBUG +#define CLOSE_DISPS(gfxCtx, file, line) \ + Graph_CloseDisps(dispRefs, gfxCtx, file, line); \ + } \ + (void)0 +#else +#define CLOSE_DISPS(gfxCtx, file, line) \ + (void)0; \ + } \ + (void)0 +#endif + +/** + * `x` vertex x + * `y` vertex y + * `z` vertex z + * `s` texture s coordinate + * `t` texture t coordinate + * `crnx` red component of color vertex, or x component of normal vertex + * `cgny` green component of color vertex, or y component of normal vertex + * `cbnz` blue component of color vertex, or z component of normal vertex + * `a` alpha + */ +#define VTX(x,y,z,s,t,crnx,cgny,cbnz,a) { { { x, y, z }, 0, { s, t }, { crnx, cgny, cbnz, a } } } + +#define VTX_T(x,y,z,s,t,cr,cg,cb,a) { { x, y, z }, 0, { s, t }, { cr, cg, cb, a } } + +#ifdef NDEBUG +#define ASSERT(cond, msg, file, line) ((void)0) +#elif defined(REAL_ASSERT_MACRO) +#define ASSERT(cond, msg, file, line) ((cond) ? ((void)0) : __assert(#cond, __FILE__, __LINE__)) +#else +#define ASSERT(cond, msg, file, line) ((cond) ? ((void)0) : __assert(msg, file, line)) +#endif + +#define gDPSetTileCustom(pkt, fmt, siz, width, height, pal, cms, cmt, masks, maskt, shifts, shiftt) \ + do { \ + gDPPipeSync(pkt); \ + gDPTileSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_TILE_BYTES) + 7) >> 3, 0, G_TX_LOADTILE, 0, cmt, maskt, shiftt, cms, \ + masks, shifts); \ + gDPTileSync(pkt); \ + gDPSetTile(pkt, fmt, siz, (((width)*siz##_TILE_BYTES) + 7) >> 3, 0, G_TX_RENDERTILE, pal, cmt, maskt, shiftt, \ + cms, masks, shifts); \ + gDPSetTileSize(pkt, G_TX_RENDERTILE, 0, 0, ((width)-1) << G_TEXTURE_IMAGE_FRAC, \ + ((height)-1) << G_TEXTURE_IMAGE_FRAC); \ + } while (0) + +#ifdef __GNUC__ +#define ALIGNED8 __attribute__ ((aligned (8))) +#else +#define ALIGNED8 +#endif + +#define SEG_ADDR(seg, addr) (addr | (seg << 24) | 0xF0000000) + +#endif diff --git a/soh/include/message_data_fmt.h b/soh/include/message_data_fmt.h new file mode 100644 index 000000000..fffac116c --- /dev/null +++ b/soh/include/message_data_fmt.h @@ -0,0 +1,178 @@ +#ifndef MESSAGE_DATA_FMT_H +#define MESSAGE_DATA_FMT_H + +/* + * Macros to create both a constant and a string literal from a magic value + * The constants are used in code files when parsing text for various purposes + * The strings are used in the message_data_static files themselves, as you can only concat strings with other strings + */ + +#define GLUE(a, b) a##b + +#define STRINGIFY(s) #s +#define EXPAND_AND_STRINGIFY(s) STRINGIFY(s) + +#define HEX(N) GLUE(0x, N) +#define STR(N) EXPAND_AND_STRINGIFY(GLUE(\x, N)) + +/* + * Text control characters + */ + +// Control character magic values, in 2-digit hex without prefix + +#define CTRL_NEWLINE 01 +#define CTRL_END 02 +#define CTRL_BOX_BREAK 04 +#define CTRL_COLOR 05 +#define CTRL_SHIFT 06 +#define CTRL_TEXTID 07 +#define CTRL_QUICKTEXT_ENABLE 08 +#define CTRL_QUICKTEXT_DISABLE 09 +#define CTRL_PERSISTENT 0A +#define CTRL_EVENT 0B +#define CTRL_BOX_BREAK_DELAYED 0C +#define CTRL_AWAIT_BUTTON_PRESS 0D +#define CTRL_FADE 0E +#define CTRL_NAME 0F +#define CTRL_OCARINA 10 +#define CTRL_FADE2 11 +#define CTRL_SFX 12 +#define CTRL_ITEM_ICON 13 +#define CTRL_TEXT_SPEED 14 +#define CTRL_BACKGROUND 15 +#define CTRL_MARATHON_TIME 16 +#define CTRL_RACE_TIME 17 +#define CTRL_POINTS 18 +#define CTRL_TOKENS 19 +#define CTRL_UNSKIPPABLE 1A +#define CTRL_TWO_CHOICE 1B +#define CTRL_THREE_CHOICE 1C +#define CTRL_FISH_INFO 1D +#define CTRL_HIGHSCORE 1E +#define CTRL_TIME 1F + +/* + * Colors + */ + +#define COLOR_STR(N) EXPAND_AND_STRINGIFY(GLUE(\x4, N)) + +// Color magic values, in single-digit hex without prefix + +#define CTRL_DEFAULT 0 +#define CTRL_RED 1 +#define CTRL_ADJUSTABLE 2 +#define CTRL_BLUE 3 +#define CTRL_LIGHTBLUE 4 +#define CTRL_PURPLE 5 +#define CTRL_YELLOW 6 +#define CTRL_BLACK 7 + +#ifdef MESSAGE_DATA_STATIC +// For use in message_data_static files + +#define ARG(x) x + +// while a control character, newlines are handled in the charmap conversion +// stage to allow normal newline \n usage in message_data_static files +#define NEWLINE STR(CTRL_NEWLINE) +#define END STR(CTRL_END) +#define BOX_BREAK STR(CTRL_BOX_BREAK) +#define COLOR(x) STR(CTRL_COLOR) ARG(x) // 1 +#define SHIFT(x) STR(CTRL_SHIFT) ARG(x) // 1 +#define TEXTID(x) STR(CTRL_TEXTID) ARG(x) // 2 +#define QUICKTEXT_ENABLE STR(CTRL_QUICKTEXT_ENABLE) +#define QUICKTEXT_DISABLE STR(CTRL_QUICKTEXT_DISABLE) +#define PERSISTENT STR(CTRL_PERSISTENT) +#define EVENT STR(CTRL_EVENT) +#define BOX_BREAK_DELAYED(x) STR(CTRL_BOX_BREAK_DELAYED) ARG(x) // 1 +#define AWAIT_BUTTON_PRESS STR(CTRL_AWAIT_BUTTON_PRESS) +#define FADE(x) STR(CTRL_FADE) ARG(x) // 1 +#define NAME STR(CTRL_NAME) +#define OCARINA STR(CTRL_OCARINA) +#define FADE2(x) STR(CTRL_FADE2) ARG(x) // 2 +#define SFX(x) STR(CTRL_SFX) ARG(x) // 2 +#define ITEM_ICON(x) STR(CTRL_ITEM_ICON) ARG(x) // 1 +#define TEXT_SPEED(x) STR(CTRL_TEXT_SPEED) ARG(x) // 1 +#define BACKGROUND(x,y,z) STR(CTRL_BACKGROUND) ARG(x) ARG(y) ARG(z) +#define MARATHON_TIME STR(CTRL_MARATHON_TIME) +#define RACE_TIME STR(CTRL_RACE_TIME) +#define POINTS STR(CTRL_POINTS) +#define TOKENS STR(CTRL_TOKENS) +#define UNSKIPPABLE STR(CTRL_UNSKIPPABLE) +#define TWO_CHOICE STR(CTRL_TWO_CHOICE) +#define THREE_CHOICE STR(CTRL_THREE_CHOICE) +#define FISH_INFO STR(CTRL_FISH_INFO) +#define HIGHSCORE(x) STR(CTRL_HIGHSCORE) ARG(x) // 1 +#define TIME STR(CTRL_TIME) + +/* + * Highscore values as strings, for code references the HighScores + * enum should be used. + */ + +#define HS_HORSE_ARCHERY "\x00" +#define HS_POE_POINTS "\x01" +#define HS_LARGEST_FISH "\x02" +#define HS_HORSE_RACE "\x03" +#define HS_MARATHON "\x04" +#define HS_DAMPE_RACE "\x06" + +/* + * Color values as strings + */ + +#define DEFAULT COLOR_STR(CTRL_DEFAULT) +#define RED COLOR_STR(CTRL_RED) +#define ADJUSTABLE COLOR_STR(CTRL_ADJUSTABLE) +#define BLUE COLOR_STR(CTRL_BLUE) +#define LIGHTBLUE COLOR_STR(CTRL_LIGHTBLUE) +#define PURPLE COLOR_STR(CTRL_PURPLE) +#define YELLOW COLOR_STR(CTRL_YELLOW) +#define BLACK COLOR_STR(CTRL_BLACK) + +#else +// For use in code files +#define MSGCOL_DEFAULT HEX(CTRL_DEFAULT) +#define MSGCOL_RED HEX(CTRL_RED) +#define MSGCOL_ADJUSTABLE HEX(CTRL_ADJUSTABLE) +#define MSGCOL_BLUE HEX(CTRL_BLUE) +#define MSGCOL_LIGHTBLUE HEX(CTRL_LIGHTBLUE) +#define MSGCOL_PURPLE HEX(CTRL_PURPLE) +#define MSGCOL_YELLOW HEX(CTRL_YELLOW) +#define MSGCOL_BLACK HEX(CTRL_BLACK) + +#define MESSAGE_NEWLINE HEX(CTRL_NEWLINE) +#define MESSAGE_END HEX(CTRL_END) +#define MESSAGE_BOX_BREAK HEX(CTRL_BOX_BREAK) +#define MESSAGE_COLOR HEX(CTRL_COLOR) +#define MESSAGE_SHIFT HEX(CTRL_SHIFT) +#define MESSAGE_TEXTID HEX(CTRL_TEXTID) +#define MESSAGE_QUICKTEXT_ENABLE HEX(CTRL_QUICKTEXT_ENABLE) +#define MESSAGE_QUICKTEXT_DISABLE HEX(CTRL_QUICKTEXT_DISABLE) +#define MESSAGE_PERSISTENT HEX(CTRL_PERSISTENT) +#define MESSAGE_EVENT HEX(CTRL_EVENT) +#define MESSAGE_BOX_BREAK_DELAYED HEX(CTRL_BOX_BREAK_DELAYED) +#define MESSAGE_AWAIT_BUTTON_PRESS HEX(CTRL_AWAIT_BUTTON_PRESS) +#define MESSAGE_FADE HEX(CTRL_FADE) +#define MESSAGE_NAME HEX(CTRL_NAME) +#define MESSAGE_OCARINA HEX(CTRL_OCARINA) +#define MESSAGE_FADE2 HEX(CTRL_FADE2) +#define MESSAGE_SFX HEX(CTRL_SFX) +#define MESSAGE_ITEM_ICON HEX(CTRL_ITEM_ICON) +#define MESSAGE_TEXT_SPEED HEX(CTRL_TEXT_SPEED) +#define MESSAGE_BACKGROUND HEX(CTRL_BACKGROUND) +#define MESSAGE_MARATHON_TIME HEX(CTRL_MARATHON_TIME) +#define MESSAGE_RACE_TIME HEX(CTRL_RACE_TIME) +#define MESSAGE_POINTS HEX(CTRL_POINTS) +#define MESSAGE_TOKENS HEX(CTRL_TOKENS) +#define MESSAGE_UNSKIPPABLE HEX(CTRL_UNSKIPPABLE) +#define MESSAGE_TWO_CHOICE HEX(CTRL_TWO_CHOICE) +#define MESSAGE_THREE_CHOICE HEX(CTRL_THREE_CHOICE) +#define MESSAGE_FISH_INFO HEX(CTRL_FISH_INFO) +#define MESSAGE_HIGHSCORE HEX(CTRL_HIGHSCORE) +#define MESSAGE_TIME HEX(CTRL_TIME) +#endif + +#endif diff --git a/soh/include/message_data_static.h b/soh/include/message_data_static.h new file mode 100644 index 000000000..baa21c286 --- /dev/null +++ b/soh/include/message_data_static.h @@ -0,0 +1,64 @@ +#ifndef MESSAGE_DATA_STATIC_H +#define MESSAGE_DATA_STATIC_H + +#include "global.h" +#include "message_data_fmt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + /* 0 */ TEXTBOX_TYPE_BLACK, + /* 1 */ TEXTBOX_TYPE_WOODEN, + /* 2 */ TEXTBOX_TYPE_BLUE, + /* 3 */ TEXTBOX_TYPE_OCARINA, + /* 4 */ TEXTBOX_TYPE_NONE_BOTTOM, + /* 5 */ TEXTBOX_TYPE_NONE_NO_SHADOW, + /* 11 */ TEXTBOX_TYPE_CREDITS = 11 +} TextBoxType; + +typedef enum { + /* 0 */ TEXTBOX_BG_CROSS +} TextBoxBackground; + +typedef enum { + /* 0 */ TEXTBOX_POS_VARIABLE, + /* 1 */ TEXTBOX_POS_TOP, + /* 2 */ TEXTBOX_POS_BOTTOM, + /* 3 */ TEXTBOX_POS_MIDDLE +} TextBoxPosition; + +typedef struct { + u16 textId; + u8 typePos; + const char* segment; + u32 msgSize; +} MessageTableEntry; + +/* + * Message Symbol Declarations + */ + +#define DEFINE_MESSAGE(textId, type, yPos, staffMessage) \ +extern const char _message_##textId##_staff[]; + +//#include "text/message_data_staff.h" + +#undef DEFINE_MESSAGE + +#define DEFINE_MESSAGE(textId, type, yPos, nesMessage, gerMessage, fraMessage) \ +extern const char _message_##textId##_nes[]; \ +extern const char _message_##textId##_ger[]; \ +extern const char _message_##textId##_fra[]; + +//#include "text/message_data.h" +extern char* _message_0xFFFC_nes; + +#undef DEFINE_MESSAGE + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/soh/include/regs.h b/soh/include/regs.h new file mode 100644 index 000000000..74b8a43df --- /dev/null +++ b/soh/include/regs.h @@ -0,0 +1,165 @@ +#ifndef REGS_H +#define REGS_H + +#define REG_GROUPS 29 // number of REG groups, i.e. REG, SREG, OREG, etc. +#define REG_PAGES 6 +#define REG_PER_PAGE 16 +#define REG_PER_GROUP REG_PAGES * REG_PER_PAGE + +#define BASE_REG(n, r) gGameInfo->data[n * REG_PER_GROUP + r] + +#define REG(r) BASE_REG(0, r) +#define SREG(r) BASE_REG(1, r) +#define OREG(r) BASE_REG(2, r) +#define PREG(r) BASE_REG(3, r) +#define QREG(r) BASE_REG(4, r) +#define MREG(r) BASE_REG(5, r) +#define YREG(r) BASE_REG(6, r) +#define DREG(r) BASE_REG(7, r) +#define UREG(r) BASE_REG(8, r) +#define IREG(r) BASE_REG(9, r) +#define ZREG(r) BASE_REG(10, r) +#define CREG(r) BASE_REG(11, r) +#define NREG(r) BASE_REG(12, r) +#define KREG(r) BASE_REG(13, r) +#define XREG(r) BASE_REG(14, r) +#define cREG(r) BASE_REG(15, r) +#define sREG(r) BASE_REG(16, r) +#define iREG(r) BASE_REG(17, r) +#define WREG(r) BASE_REG(18, r) +#define AREG(r) BASE_REG(19, r) +#define VREG(r) BASE_REG(20, r) +#define HREG(r) BASE_REG(21, r) +#define GREG(r) BASE_REG(22, r) +#define mREG(r) BASE_REG(23, r) +#define nREG(r) BASE_REG(24, r) +#define BREG(r) BASE_REG(25, r) +#define dREG(r) BASE_REG(26, r) +#define kREG(r) BASE_REG(27, r) +#define bREG(r) BASE_REG(28, r) + +#define R_ENV_AMBIENT_COLOR(i) REG(0 + i) +#define R_ENV_LIGHT1_COLOR(i) REG(3 + i) +#define R_ENV_LIGHT2_COLOR(i) REG(6 + i) +#define R_ENV_DISABLE_DBG REG(9) +#define R_ENV_FOG_COLOR(i) REG(10 + i) +#define R_ENV_FOG_FAR REG(13) +#define R_ENV_FOG_NEAR REG(14) +#define R_ENV_TIME_INCREMENT REG(15) +#define R_RUN_SPEED_LIMIT REG(45) +#define R_ENABLE_ARENA_DBG SREG(0) +#define R_UPDATE_RATE SREG(30) +#define R_ENABLE_AUDIO_DBG SREG(36) +#define R_FB_FILTER_TYPE SREG(80) +#define R_FB_FILTER_PRIM_COLOR(c) SREG(81 + c) +#define R_FB_FILTER_A SREG(84) +#define R_FB_FILTER_ENV_COLOR(c) SREG(85 + c) +#define R_ENABLE_FB_FILTER SREG(88) +#define R_PAUSE_MENU_MODE SREG(94) +#define R_CAM_MAX_PHI OREG(5) +#define R_CAM_DEFA_PHI_UPDRATE OREG(7) +#define R_DEFA_CAM_ANIM_TIME OREG(23) +#define R_CAM_MIN_PHI OREG(34) +#define R_CAM_MIN_PHI2 OREG(35) +#define R_AT_LERP_MIN OREG(41) +#define R_AT_LERP_SCALE OREG(42) +#define R_CAM_YOFFSET_NORM OREG(46) +#define R_CAM_DATA(type) PREG(type) +#define R_DBG_CAM_UPDATE PREG(80) +#define R_DBG_REG_UPDATE PREG(82) +#define R_RELOAD_CAM_PARAMS QREG(0) +#define R_C_UP_ICON_X YREG(88) +#define R_C_UP_ICON_Y YREG(89) +#define R_MAGIC_FILL_COLOR(i) ZREG(0 + i) +#define R_C_BTN_COLOR(i) ZREG(39 + i) +#define R_B_BTN_COLOR(i) ZREG(43 + i) +#define R_START_LABEL_DD(i) ZREG(48 + i) +#define R_START_LABEL_Y(i) ZREG(51 + i) +#define R_START_LABEL_X(i) ZREG(54 + i) +#define R_C_UP_BTN_X ZREG(62) +#define R_C_UP_BTN_Y ZREG(63) +#define R_START_BTN_X ZREG(68) +#define R_START_BTN_Y ZREG(69) +#define R_ITEM_BTN_X(i) ZREG(70 + i) +#define R_ITEM_BTN_Y(i) ZREG(74 + i) +#define R_ITEM_BTN_DD(i) ZREG(78 + i) +#define R_ITEM_ICON_X(i) ZREG(82 + i) +#define R_ITEM_ICON_Y(i) ZREG(86 + i) +#define R_ITEM_ICON_DD(i) ZREG(90 + i) +#define R_ENV_WIND_DIR(i) CREG(16 + i) +#define R_ENV_WIND_SPEED CREG(19) +#define R_A_BTN_Y XREG(16) +#define R_A_BTN_X XREG(17) +#define R_A_ICON_Y XREG(19) +#define R_A_ICON_X XREG(20) +#define R_A_BTN_COLOR(i) XREG(22 + i) +#define R_MAGIC_BAR_SMALL_Y XREG(48) +#define R_MAGIC_BAR_X XREG(49) +#define R_MAGIC_BAR_LARGE_Y XREG(50) +#define R_MAGIC_FILL_X XREG(51) +#define R_ENV_LIGHT1_DIR(i) cREG(3 + i) +#define R_ENV_LIGHT2_DIR(i) cREG(6 + i) +#define R_B_LABEL_DD WREG(0) +#define R_OW_MINIMAP_X WREG(29) +#define R_OW_MINIMAP_Y WREG(30) +#define R_MINIMAP_DISABLED WREG(31) +#define R_B_LABEL_X(i) WREG(40 + i) +#define R_B_LABEL_Y(i) WREG(43 + i) +#define R_DGN_MINIMAP_X WREG(68) +#define R_DGN_MINIMAP_Y WREG(69) +#define R_MAP_INDEX VREG(11) +#define R_MAP_TEX_INDEX_BASE VREG(12) +#define R_MAP_TEX_INDEX VREG(13) +#define R_COMPASS_SCALE_X VREG(14) +#define R_COMPASS_SCALE_Y VREG(15) +#define R_COMPASS_OFFSET_X VREG(16) +#define R_COMPASS_OFFSET_Y VREG(17) +#define R_MINIMAP_COLOR(i) VREG(18 + i) +#define R_ITEM_AMMO_X(i) VREG(64 + i) +#define R_ITEM_AMMO_Y(i) VREG(68 + i) +#define R_ITEM_ICON_WIDTH(i) VREG(76 + i) +#define R_ITEM_BTN_WIDTH(i) VREG(80 + i) +#define R_DISABLE_INPUT_DISPLAY HREG(47) +#define R_EN_GOROIWA_SPEED mREG(12) +#define R_NAVI_MSG_REGION_ALPHA nREG(87) +#define R_TEXT_DROP_SHADOW_OFFSET XREG(60) +#define R_TEXTBOX_X_TARGET XREG(72) +#define R_TEXTBOX_Y_TARGET XREG(73) +#define R_TEXTBOX_TEXWIDTH YREG(16) +#define R_TEXTBOX_TEXHEIGHT YREG(17) +#define R_TEXTBOX_WIDTH YREG(22) +#define R_TEXTBOX_HEIGHT YREG(23) +#define R_MESSAGE_DEBUGGER_SELECT YREG(78) +#define R_MESSAGE_DEBUGGER_TEXTID YREG(79) +#define R_TEXT_LINE_SPACING XREG(56) +#define R_TEXT_CHAR_SCALE XREG(57) +#define R_TEXTBOX_ICON_XPOS YREG(71) +#define R_TEXTBOX_ICON_YPOS YREG(72) +#define R_TEXTBOX_ICON_SIZE YREG(75) +#define R_TEXTBOX_X VREG(0) +#define R_TEXTBOX_Y VREG(1) +#define R_TEXTBOX_END_XPOS XREG(64) +#define R_TEXTBOX_END_YPOS XREG(65) +#define R_TEXTBOX_WIDTH_TARGET XREG(74) +#define R_TEXTBOX_HEIGHT_TARGET XREG(75) +#define R_TEXTBOX_TEXWIDTH_TARGET XREG(76) +#define R_TEXTBOX_TEXHEIGHT_TARGET XREG(77) +#define R_TEXT_ADJUST_COLOR_1_R VREG(33) +#define R_TEXT_ADJUST_COLOR_1_G VREG(34) +#define R_TEXT_ADJUST_COLOR_1_B VREG(35) +#define R_TEXT_ADJUST_COLOR_2_R VREG(36) +#define R_TEXT_ADJUST_COLOR_2_G VREG(37) +#define R_TEXT_ADJUST_COLOR_2_B VREG(38) +#define R_TEXT_CHOICE_XPOS XREG(66) +#define R_TEXT_CHOICE_YPOS(choice) XREG(67 + (choice)) +#define R_TEXT_INIT_XPOS XREG(54) +#define R_TEXT_INIT_YPOS XREG(55) +#define R_TEXTBOX_BG_YPOS XREG(61) +#define R_TEXTBOX_CLEF_XPOS VREG(7) +#define R_TEXTBOX_CLEF_YPOS VREG(8) +#define R_OCARINA_NOTES_XPOS VREG(28) +#define R_OCARINA_NOTES_YPOS(note) VREG(45 + (note)) +#define R_OCARINA_NOTES_XPOS_OFFSET VREG(29) +#define R_OCARINA_NOTES_YPOS_OFFSET VREG(51) + +#endif diff --git a/soh/include/segment_symbols.h b/soh/include/segment_symbols.h new file mode 100644 index 000000000..81fe34801 --- /dev/null +++ b/soh/include/segment_symbols.h @@ -0,0 +1,827 @@ +#ifndef SEGMENT_SYMBOLS_H +#define SEGMENT_SYMBOLS_H + +#include "z64.h" + +#define DECLARE_SEGMENT(name) \ + //extern u8 _##name##SegmentStart[]; \ + //extern u8 _##name##SegmentEnd[]; + +#define DECLARE_ROM_SEGMENT(name) \ + //extern u8 _##name##SegmentRomStart[]; \ + //extern u8 _##name##SegmentRomEnd[]; + +#define DECLARE_BSS_SEGMENT(name) \ + extern u8 _##name##SegmentBssStart[]; \ + extern u8 _##name##SegmentBssEnd[]; + +#define DECLARE_OVERLAY_SEGMENT(name) \ + //DECLARE_SEGMENT(ovl_##name) \ + //DECLARE_ROM_SEGMENT(ovl_##name) + +DECLARE_SEGMENT(boot) +DECLARE_ROM_SEGMENT(boot) + +DECLARE_SEGMENT(dmadata) +DECLARE_ROM_SEGMENT(dmadata) + +DECLARE_ROM_SEGMENT(Audiobank) +DECLARE_ROM_SEGMENT(Audioseq) +DECLARE_ROM_SEGMENT(Audiotable) + +DECLARE_SEGMENT(link_animetion) +DECLARE_ROM_SEGMENT(link_animetion) + +DECLARE_ROM_SEGMENT(icon_item_static) +DECLARE_ROM_SEGMENT(icon_item_24_static) +DECLARE_ROM_SEGMENT(icon_item_field_static) +DECLARE_ROM_SEGMENT(icon_item_dungeon_static) +DECLARE_ROM_SEGMENT(icon_item_gameover_static) +DECLARE_ROM_SEGMENT(icon_item_nes_static) +DECLARE_ROM_SEGMENT(icon_item_ger_static) +DECLARE_ROM_SEGMENT(icon_item_fra_static) +DECLARE_ROM_SEGMENT(item_name_static) +DECLARE_ROM_SEGMENT(map_name_static) +DECLARE_ROM_SEGMENT(do_action_static) +DECLARE_ROM_SEGMENT(message_static) +DECLARE_ROM_SEGMENT(message_texture_static) +DECLARE_ROM_SEGMENT(nes_font_static) + +DECLARE_SEGMENT(nes_message_data_static) +DECLARE_ROM_SEGMENT(nes_message_data_static) +DECLARE_SEGMENT(ger_message_data_static) +DECLARE_ROM_SEGMENT(ger_message_data_static) +DECLARE_SEGMENT(fra_message_data_static) +DECLARE_ROM_SEGMENT(fra_message_data_static) +DECLARE_SEGMENT(staff_message_data_static) +DECLARE_ROM_SEGMENT(staff_message_data_static) + +DECLARE_ROM_SEGMENT(map_grand_static) +DECLARE_ROM_SEGMENT(map_i_static) +DECLARE_ROM_SEGMENT(map_48x85_static) + +DECLARE_SEGMENT(code) +DECLARE_ROM_SEGMENT(code) +DECLARE_BSS_SEGMENT(code) + +DECLARE_OVERLAY_SEGMENT(title) +DECLARE_OVERLAY_SEGMENT(select) +DECLARE_OVERLAY_SEGMENT(opening) +DECLARE_OVERLAY_SEGMENT(file_choose) +DECLARE_OVERLAY_SEGMENT(kaleido_scope) +DECLARE_OVERLAY_SEGMENT(player_actor) +DECLARE_OVERLAY_SEGMENT(map_mark_data) + +DECLARE_ROM_SEGMENT(g_pn_01) +DECLARE_ROM_SEGMENT(g_pn_02) +DECLARE_ROM_SEGMENT(g_pn_03) +DECLARE_ROM_SEGMENT(g_pn_04) +DECLARE_ROM_SEGMENT(g_pn_05) +DECLARE_ROM_SEGMENT(g_pn_06) +DECLARE_ROM_SEGMENT(g_pn_07) +DECLARE_ROM_SEGMENT(g_pn_08) +DECLARE_ROM_SEGMENT(g_pn_09) +DECLARE_ROM_SEGMENT(g_pn_10) +DECLARE_ROM_SEGMENT(g_pn_11) +DECLARE_ROM_SEGMENT(g_pn_12) +DECLARE_ROM_SEGMENT(g_pn_13) +DECLARE_ROM_SEGMENT(g_pn_14) +DECLARE_ROM_SEGMENT(g_pn_15) +DECLARE_ROM_SEGMENT(g_pn_16) +DECLARE_ROM_SEGMENT(g_pn_17) +DECLARE_ROM_SEGMENT(g_pn_18) +DECLARE_ROM_SEGMENT(g_pn_19) +DECLARE_ROM_SEGMENT(g_pn_20) +DECLARE_ROM_SEGMENT(g_pn_21) +DECLARE_ROM_SEGMENT(g_pn_22) +DECLARE_ROM_SEGMENT(g_pn_23) +DECLARE_ROM_SEGMENT(g_pn_24) +DECLARE_ROM_SEGMENT(g_pn_25) +DECLARE_ROM_SEGMENT(g_pn_26) +DECLARE_ROM_SEGMENT(g_pn_27) +DECLARE_ROM_SEGMENT(g_pn_28) +DECLARE_ROM_SEGMENT(g_pn_29) +DECLARE_ROM_SEGMENT(g_pn_30) +DECLARE_ROM_SEGMENT(g_pn_31) +DECLARE_ROM_SEGMENT(g_pn_32) +DECLARE_ROM_SEGMENT(g_pn_33) +DECLARE_ROM_SEGMENT(g_pn_34) +DECLARE_ROM_SEGMENT(g_pn_35) +DECLARE_ROM_SEGMENT(g_pn_36) +DECLARE_ROM_SEGMENT(g_pn_37) +DECLARE_ROM_SEGMENT(g_pn_38) +DECLARE_ROM_SEGMENT(g_pn_39) +DECLARE_ROM_SEGMENT(g_pn_40) +DECLARE_ROM_SEGMENT(g_pn_41) +DECLARE_ROM_SEGMENT(g_pn_42) +DECLARE_ROM_SEGMENT(g_pn_43) +DECLARE_ROM_SEGMENT(g_pn_44) +DECLARE_ROM_SEGMENT(g_pn_45) +DECLARE_ROM_SEGMENT(g_pn_46) +DECLARE_ROM_SEGMENT(g_pn_47) +DECLARE_ROM_SEGMENT(g_pn_48) +DECLARE_ROM_SEGMENT(g_pn_49) +DECLARE_ROM_SEGMENT(g_pn_50) +DECLARE_ROM_SEGMENT(g_pn_51) +DECLARE_ROM_SEGMENT(g_pn_52) +DECLARE_ROM_SEGMENT(g_pn_53) +DECLARE_ROM_SEGMENT(g_pn_54) +DECLARE_ROM_SEGMENT(g_pn_55) +DECLARE_ROM_SEGMENT(g_pn_56) +DECLARE_ROM_SEGMENT(g_pn_57) + +DECLARE_ROM_SEGMENT(z_select_static) +DECLARE_ROM_SEGMENT(nintendo_rogo_static) +DECLARE_ROM_SEGMENT(title_static) +DECLARE_ROM_SEGMENT(parameter_static) +DECLARE_ROM_SEGMENT(vr_fine0_static) +DECLARE_ROM_SEGMENT(vr_fine0_pal_static) +DECLARE_ROM_SEGMENT(vr_fine1_static) +DECLARE_ROM_SEGMENT(vr_fine1_pal_static) +DECLARE_ROM_SEGMENT(vr_fine2_static) +DECLARE_ROM_SEGMENT(vr_fine2_pal_static) +DECLARE_ROM_SEGMENT(vr_fine3_static) +DECLARE_ROM_SEGMENT(vr_fine3_pal_static) +DECLARE_ROM_SEGMENT(vr_cloud0_static) +DECLARE_ROM_SEGMENT(vr_cloud0_pal_static) +DECLARE_ROM_SEGMENT(vr_cloud1_static) +DECLARE_ROM_SEGMENT(vr_cloud1_pal_static) +DECLARE_ROM_SEGMENT(vr_cloud2_static) +DECLARE_ROM_SEGMENT(vr_cloud2_pal_static) +DECLARE_ROM_SEGMENT(vr_cloud3_static) +DECLARE_ROM_SEGMENT(vr_cloud3_pal_static) +DECLARE_ROM_SEGMENT(vr_holy0_static) +DECLARE_ROM_SEGMENT(vr_holy0_pal_static) +DECLARE_ROM_SEGMENT(vr_holy1_static) +DECLARE_ROM_SEGMENT(vr_holy1_pal_static) +DECLARE_ROM_SEGMENT(vr_MDVR_static) +DECLARE_ROM_SEGMENT(vr_MDVR_pal_static) +DECLARE_ROM_SEGMENT(vr_MNVR_static) +DECLARE_ROM_SEGMENT(vr_MNVR_pal_static) +DECLARE_ROM_SEGMENT(vr_RUVR_static) +DECLARE_ROM_SEGMENT(vr_RUVR_pal_static) +DECLARE_ROM_SEGMENT(vr_LHVR_static) +DECLARE_ROM_SEGMENT(vr_LHVR_pal_static) +DECLARE_ROM_SEGMENT(vr_KHVR_static) +DECLARE_ROM_SEGMENT(vr_KHVR_pal_static) +DECLARE_ROM_SEGMENT(vr_K3VR_static) +DECLARE_ROM_SEGMENT(vr_K3VR_pal_static) +DECLARE_ROM_SEGMENT(vr_K4VR_static) +DECLARE_ROM_SEGMENT(vr_K4VR_pal_static) +DECLARE_ROM_SEGMENT(vr_K5VR_static) +DECLARE_ROM_SEGMENT(vr_K5VR_pal_static) +DECLARE_ROM_SEGMENT(vr_SP1a_static) +DECLARE_ROM_SEGMENT(vr_SP1a_pal_static) +DECLARE_ROM_SEGMENT(vr_MLVR_static) +DECLARE_ROM_SEGMENT(vr_MLVR_pal_static) +DECLARE_ROM_SEGMENT(vr_KKRVR_static) +DECLARE_ROM_SEGMENT(vr_KKRVR_pal_static) +DECLARE_ROM_SEGMENT(vr_KR3VR_static) +DECLARE_ROM_SEGMENT(vr_KR3VR_pal_static) +DECLARE_ROM_SEGMENT(vr_IPVR_static) +DECLARE_ROM_SEGMENT(vr_IPVR_pal_static) +DECLARE_ROM_SEGMENT(vr_KSVR_static) +DECLARE_ROM_SEGMENT(vr_KSVR_pal_static) +DECLARE_ROM_SEGMENT(vr_GLVR_static) +DECLARE_ROM_SEGMENT(vr_GLVR_pal_static) +DECLARE_ROM_SEGMENT(vr_ZRVR_static) +DECLARE_ROM_SEGMENT(vr_ZRVR_pal_static) +DECLARE_ROM_SEGMENT(vr_DGVR_static) +DECLARE_ROM_SEGMENT(vr_DGVR_pal_static) +DECLARE_ROM_SEGMENT(vr_ALVR_static) +DECLARE_ROM_SEGMENT(vr_ALVR_pal_static) +DECLARE_ROM_SEGMENT(vr_NSVR_static) +DECLARE_ROM_SEGMENT(vr_NSVR_pal_static) +DECLARE_ROM_SEGMENT(vr_LBVR_static) +DECLARE_ROM_SEGMENT(vr_LBVR_pal_static) +DECLARE_ROM_SEGMENT(vr_TTVR_static) +DECLARE_ROM_SEGMENT(vr_TTVR_pal_static) +DECLARE_ROM_SEGMENT(vr_FCVR_static) +DECLARE_ROM_SEGMENT(vr_FCVR_pal_static) +DECLARE_ROM_SEGMENT(elf_message_field) +DECLARE_ROM_SEGMENT(elf_message_ydan) + +// SCENES AND ROOMS +DECLARE_ROM_SEGMENT(ydan_scene) +DECLARE_ROM_SEGMENT(ydan_room_0) +DECLARE_ROM_SEGMENT(ydan_room_1) +DECLARE_ROM_SEGMENT(ydan_room_2) +DECLARE_ROM_SEGMENT(ydan_room_3) +DECLARE_ROM_SEGMENT(ydan_room_4) +DECLARE_ROM_SEGMENT(ydan_room_5) +DECLARE_ROM_SEGMENT(ydan_room_6) +DECLARE_ROM_SEGMENT(ydan_room_7) +DECLARE_ROM_SEGMENT(ydan_room_8) +DECLARE_ROM_SEGMENT(ydan_room_9) +DECLARE_ROM_SEGMENT(ydan_room_10) +DECLARE_ROM_SEGMENT(ydan_room_11) + +DECLARE_ROM_SEGMENT(ddan_scene) +DECLARE_ROM_SEGMENT(ddan_room_0) +DECLARE_ROM_SEGMENT(ddan_room_1) +DECLARE_ROM_SEGMENT(ddan_room_2) +DECLARE_ROM_SEGMENT(ddan_room_3) +DECLARE_ROM_SEGMENT(ddan_room_4) +DECLARE_ROM_SEGMENT(ddan_room_5) +DECLARE_ROM_SEGMENT(ddan_room_6) +DECLARE_ROM_SEGMENT(ddan_room_7) +DECLARE_ROM_SEGMENT(ddan_room_8) +DECLARE_ROM_SEGMENT(ddan_room_9) +DECLARE_ROM_SEGMENT(ddan_room_10) +DECLARE_ROM_SEGMENT(ddan_room_11) +DECLARE_ROM_SEGMENT(ddan_room_12) +DECLARE_ROM_SEGMENT(ddan_room_13) +DECLARE_ROM_SEGMENT(ddan_room_14) +DECLARE_ROM_SEGMENT(ddan_room_15) +DECLARE_ROM_SEGMENT(ddan_room_16) + +DECLARE_ROM_SEGMENT(bdan_scene) +DECLARE_ROM_SEGMENT(bdan_room_0) +DECLARE_ROM_SEGMENT(bdan_room_1) +DECLARE_ROM_SEGMENT(bdan_room_2) +DECLARE_ROM_SEGMENT(bdan_room_3) +DECLARE_ROM_SEGMENT(bdan_room_4) +DECLARE_ROM_SEGMENT(bdan_room_5) +DECLARE_ROM_SEGMENT(bdan_room_6) +DECLARE_ROM_SEGMENT(bdan_room_7) +DECLARE_ROM_SEGMENT(bdan_room_8) +DECLARE_ROM_SEGMENT(bdan_room_9) +DECLARE_ROM_SEGMENT(bdan_room_10) +DECLARE_ROM_SEGMENT(bdan_room_11) +DECLARE_ROM_SEGMENT(bdan_room_12) +DECLARE_ROM_SEGMENT(bdan_room_13) +DECLARE_ROM_SEGMENT(bdan_room_14) +DECLARE_ROM_SEGMENT(bdan_room_15) + +DECLARE_ROM_SEGMENT(Bmori1_scene) +DECLARE_ROM_SEGMENT(Bmori1_room_0) +DECLARE_ROM_SEGMENT(Bmori1_room_1) +DECLARE_ROM_SEGMENT(Bmori1_room_2) +DECLARE_ROM_SEGMENT(Bmori1_room_3) +DECLARE_ROM_SEGMENT(Bmori1_room_4) +DECLARE_ROM_SEGMENT(Bmori1_room_5) +DECLARE_ROM_SEGMENT(Bmori1_room_6) +DECLARE_ROM_SEGMENT(Bmori1_room_7) +DECLARE_ROM_SEGMENT(Bmori1_room_8) +DECLARE_ROM_SEGMENT(Bmori1_room_9) +DECLARE_ROM_SEGMENT(Bmori1_room_10) +DECLARE_ROM_SEGMENT(Bmori1_room_11) +DECLARE_ROM_SEGMENT(Bmori1_room_12) +DECLARE_ROM_SEGMENT(Bmori1_room_13) +DECLARE_ROM_SEGMENT(Bmori1_room_14) +DECLARE_ROM_SEGMENT(Bmori1_room_15) +DECLARE_ROM_SEGMENT(Bmori1_room_16) +DECLARE_ROM_SEGMENT(Bmori1_room_17) +DECLARE_ROM_SEGMENT(Bmori1_room_18) +DECLARE_ROM_SEGMENT(Bmori1_room_19) +DECLARE_ROM_SEGMENT(Bmori1_room_20) +DECLARE_ROM_SEGMENT(Bmori1_room_21) +DECLARE_ROM_SEGMENT(Bmori1_room_22) + +DECLARE_ROM_SEGMENT(HIDAN_scene) +DECLARE_ROM_SEGMENT(HIDAN_room_0) +DECLARE_ROM_SEGMENT(HIDAN_room_1) +DECLARE_ROM_SEGMENT(HIDAN_room_2) +DECLARE_ROM_SEGMENT(HIDAN_room_3) +DECLARE_ROM_SEGMENT(HIDAN_room_4) +DECLARE_ROM_SEGMENT(HIDAN_room_5) +DECLARE_ROM_SEGMENT(HIDAN_room_6) +DECLARE_ROM_SEGMENT(HIDAN_room_7) +DECLARE_ROM_SEGMENT(HIDAN_room_8) +DECLARE_ROM_SEGMENT(HIDAN_room_9) +DECLARE_ROM_SEGMENT(HIDAN_room_10) +DECLARE_ROM_SEGMENT(HIDAN_room_11) +DECLARE_ROM_SEGMENT(HIDAN_room_12) +DECLARE_ROM_SEGMENT(HIDAN_room_13) +DECLARE_ROM_SEGMENT(HIDAN_room_14) +DECLARE_ROM_SEGMENT(HIDAN_room_15) +DECLARE_ROM_SEGMENT(HIDAN_room_16) +DECLARE_ROM_SEGMENT(HIDAN_room_17) +DECLARE_ROM_SEGMENT(HIDAN_room_18) +DECLARE_ROM_SEGMENT(HIDAN_room_19) +DECLARE_ROM_SEGMENT(HIDAN_room_20) +DECLARE_ROM_SEGMENT(HIDAN_room_21) +DECLARE_ROM_SEGMENT(HIDAN_room_22) +DECLARE_ROM_SEGMENT(HIDAN_room_23) +DECLARE_ROM_SEGMENT(HIDAN_room_24) +DECLARE_ROM_SEGMENT(HIDAN_room_25) +DECLARE_ROM_SEGMENT(HIDAN_room_26) + +DECLARE_ROM_SEGMENT(MIZUsin_scene) +DECLARE_ROM_SEGMENT(MIZUsin_room_0) +DECLARE_ROM_SEGMENT(MIZUsin_room_1) +DECLARE_ROM_SEGMENT(MIZUsin_room_2) +DECLARE_ROM_SEGMENT(MIZUsin_room_3) +DECLARE_ROM_SEGMENT(MIZUsin_room_4) +DECLARE_ROM_SEGMENT(MIZUsin_room_5) +DECLARE_ROM_SEGMENT(MIZUsin_room_6) +DECLARE_ROM_SEGMENT(MIZUsin_room_7) +DECLARE_ROM_SEGMENT(MIZUsin_room_8) +DECLARE_ROM_SEGMENT(MIZUsin_room_9) +DECLARE_ROM_SEGMENT(MIZUsin_room_10) +DECLARE_ROM_SEGMENT(MIZUsin_room_11) +DECLARE_ROM_SEGMENT(MIZUsin_room_12) +DECLARE_ROM_SEGMENT(MIZUsin_room_13) +DECLARE_ROM_SEGMENT(MIZUsin_room_14) +DECLARE_ROM_SEGMENT(MIZUsin_room_15) +DECLARE_ROM_SEGMENT(MIZUsin_room_16) +DECLARE_ROM_SEGMENT(MIZUsin_room_17) +DECLARE_ROM_SEGMENT(MIZUsin_room_18) +DECLARE_ROM_SEGMENT(MIZUsin_room_19) +DECLARE_ROM_SEGMENT(MIZUsin_room_20) +DECLARE_ROM_SEGMENT(MIZUsin_room_21) +DECLARE_ROM_SEGMENT(MIZUsin_room_22) + +DECLARE_ROM_SEGMENT(jyasinzou_scene) +DECLARE_ROM_SEGMENT(jyasinzou_room_0) +DECLARE_ROM_SEGMENT(jyasinzou_room_1) +DECLARE_ROM_SEGMENT(jyasinzou_room_2) +DECLARE_ROM_SEGMENT(jyasinzou_room_3) +DECLARE_ROM_SEGMENT(jyasinzou_room_4) +DECLARE_ROM_SEGMENT(jyasinzou_room_5) +DECLARE_ROM_SEGMENT(jyasinzou_room_6) +DECLARE_ROM_SEGMENT(jyasinzou_room_7) +DECLARE_ROM_SEGMENT(jyasinzou_room_8) +DECLARE_ROM_SEGMENT(jyasinzou_room_9) +DECLARE_ROM_SEGMENT(jyasinzou_room_10) +DECLARE_ROM_SEGMENT(jyasinzou_room_11) +DECLARE_ROM_SEGMENT(jyasinzou_room_12) +DECLARE_ROM_SEGMENT(jyasinzou_room_13) +DECLARE_ROM_SEGMENT(jyasinzou_room_14) +DECLARE_ROM_SEGMENT(jyasinzou_room_15) +DECLARE_ROM_SEGMENT(jyasinzou_room_16) +DECLARE_ROM_SEGMENT(jyasinzou_room_17) +DECLARE_ROM_SEGMENT(jyasinzou_room_18) +DECLARE_ROM_SEGMENT(jyasinzou_room_19) +DECLARE_ROM_SEGMENT(jyasinzou_room_20) +DECLARE_ROM_SEGMENT(jyasinzou_room_21) +DECLARE_ROM_SEGMENT(jyasinzou_room_22) +DECLARE_ROM_SEGMENT(jyasinzou_room_23) +DECLARE_ROM_SEGMENT(jyasinzou_room_24) +DECLARE_ROM_SEGMENT(jyasinzou_room_25) +DECLARE_ROM_SEGMENT(jyasinzou_room_26) +DECLARE_ROM_SEGMENT(jyasinzou_room_27) +DECLARE_ROM_SEGMENT(jyasinzou_room_28) + +DECLARE_ROM_SEGMENT(HAKAdan_scene) +DECLARE_ROM_SEGMENT(HAKAdan_room_0) +DECLARE_ROM_SEGMENT(HAKAdan_room_1) +DECLARE_ROM_SEGMENT(HAKAdan_room_2) +DECLARE_ROM_SEGMENT(HAKAdan_room_3) +DECLARE_ROM_SEGMENT(HAKAdan_room_4) +DECLARE_ROM_SEGMENT(HAKAdan_room_5) +DECLARE_ROM_SEGMENT(HAKAdan_room_6) +DECLARE_ROM_SEGMENT(HAKAdan_room_7) +DECLARE_ROM_SEGMENT(HAKAdan_room_8) +DECLARE_ROM_SEGMENT(HAKAdan_room_9) +DECLARE_ROM_SEGMENT(HAKAdan_room_10) +DECLARE_ROM_SEGMENT(HAKAdan_room_11) +DECLARE_ROM_SEGMENT(HAKAdan_room_12) +DECLARE_ROM_SEGMENT(HAKAdan_room_13) +DECLARE_ROM_SEGMENT(HAKAdan_room_14) +DECLARE_ROM_SEGMENT(HAKAdan_room_15) +DECLARE_ROM_SEGMENT(HAKAdan_room_16) +DECLARE_ROM_SEGMENT(HAKAdan_room_17) +DECLARE_ROM_SEGMENT(HAKAdan_room_18) +DECLARE_ROM_SEGMENT(HAKAdan_room_19) +DECLARE_ROM_SEGMENT(HAKAdan_room_20) +DECLARE_ROM_SEGMENT(HAKAdan_room_21) +DECLARE_ROM_SEGMENT(HAKAdan_room_22) + +DECLARE_ROM_SEGMENT(HAKAdanCH_scene) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_0) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_1) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_2) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_3) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_4) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_5) +DECLARE_ROM_SEGMENT(HAKAdanCH_room_6) + +DECLARE_ROM_SEGMENT(ice_doukutu_scene) +DECLARE_ROM_SEGMENT(ice_doukutu_room_0) +DECLARE_ROM_SEGMENT(ice_doukutu_room_1) +DECLARE_ROM_SEGMENT(ice_doukutu_room_2) +DECLARE_ROM_SEGMENT(ice_doukutu_room_3) +DECLARE_ROM_SEGMENT(ice_doukutu_room_4) +DECLARE_ROM_SEGMENT(ice_doukutu_room_5) +DECLARE_ROM_SEGMENT(ice_doukutu_room_6) +DECLARE_ROM_SEGMENT(ice_doukutu_room_7) +DECLARE_ROM_SEGMENT(ice_doukutu_room_8) +DECLARE_ROM_SEGMENT(ice_doukutu_room_9) +DECLARE_ROM_SEGMENT(ice_doukutu_room_10) +DECLARE_ROM_SEGMENT(ice_doukutu_room_11) + +DECLARE_ROM_SEGMENT(ganon_scene) +DECLARE_ROM_SEGMENT(ganon_room_0) +DECLARE_ROM_SEGMENT(ganon_room_1) +DECLARE_ROM_SEGMENT(ganon_room_2) +DECLARE_ROM_SEGMENT(ganon_room_3) +DECLARE_ROM_SEGMENT(ganon_room_4) +DECLARE_ROM_SEGMENT(ganon_room_5) +DECLARE_ROM_SEGMENT(ganon_room_6) +DECLARE_ROM_SEGMENT(ganon_room_7) +DECLARE_ROM_SEGMENT(ganon_room_8) +DECLARE_ROM_SEGMENT(ganon_room_9) + +DECLARE_ROM_SEGMENT(men_scene) +DECLARE_ROM_SEGMENT(men_room_0) +DECLARE_ROM_SEGMENT(men_room_1) +DECLARE_ROM_SEGMENT(men_room_2) +DECLARE_ROM_SEGMENT(men_room_3) +DECLARE_ROM_SEGMENT(men_room_4) +DECLARE_ROM_SEGMENT(men_room_5) +DECLARE_ROM_SEGMENT(men_room_6) +DECLARE_ROM_SEGMENT(men_room_7) +DECLARE_ROM_SEGMENT(men_room_8) +DECLARE_ROM_SEGMENT(men_room_9) +DECLARE_ROM_SEGMENT(men_room_10) + +DECLARE_ROM_SEGMENT(gerudoway_scene) +DECLARE_ROM_SEGMENT(gerudoway_room_0) +DECLARE_ROM_SEGMENT(gerudoway_room_1) +DECLARE_ROM_SEGMENT(gerudoway_room_2) +DECLARE_ROM_SEGMENT(gerudoway_room_3) +DECLARE_ROM_SEGMENT(gerudoway_room_4) +DECLARE_ROM_SEGMENT(gerudoway_room_5) + +DECLARE_ROM_SEGMENT(ganontika_scene) +DECLARE_ROM_SEGMENT(ganontika_room_0) +DECLARE_ROM_SEGMENT(ganontika_room_1) +DECLARE_ROM_SEGMENT(ganontika_room_2) +DECLARE_ROM_SEGMENT(ganontika_room_3) +DECLARE_ROM_SEGMENT(ganontika_room_4) +DECLARE_ROM_SEGMENT(ganontika_room_5) +DECLARE_ROM_SEGMENT(ganontika_room_6) +DECLARE_ROM_SEGMENT(ganontika_room_7) +DECLARE_ROM_SEGMENT(ganontika_room_8) +DECLARE_ROM_SEGMENT(ganontika_room_9) +DECLARE_ROM_SEGMENT(ganontika_room_10) +DECLARE_ROM_SEGMENT(ganontika_room_11) +DECLARE_ROM_SEGMENT(ganontika_room_12) +DECLARE_ROM_SEGMENT(ganontika_room_13) +DECLARE_ROM_SEGMENT(ganontika_room_14) +DECLARE_ROM_SEGMENT(ganontika_room_15) +DECLARE_ROM_SEGMENT(ganontika_room_16) +DECLARE_ROM_SEGMENT(ganontika_room_17) +DECLARE_ROM_SEGMENT(ganontika_room_18) +DECLARE_ROM_SEGMENT(ganontika_room_19) + +DECLARE_ROM_SEGMENT(ganon_sonogo_scene) +DECLARE_ROM_SEGMENT(ganon_sonogo_room_0) +DECLARE_ROM_SEGMENT(ganon_sonogo_room_1) +DECLARE_ROM_SEGMENT(ganon_sonogo_room_2) +DECLARE_ROM_SEGMENT(ganon_sonogo_room_3) +DECLARE_ROM_SEGMENT(ganon_sonogo_room_4) + +DECLARE_ROM_SEGMENT(ganontikasonogo_scene) +DECLARE_ROM_SEGMENT(ganontikasonogo_room_0) +DECLARE_ROM_SEGMENT(ganontikasonogo_room_1) + +DECLARE_ROM_SEGMENT(takaraya_scene) +DECLARE_ROM_SEGMENT(takaraya_room_0) +DECLARE_ROM_SEGMENT(takaraya_room_1) +DECLARE_ROM_SEGMENT(takaraya_room_2) +DECLARE_ROM_SEGMENT(takaraya_room_3) +DECLARE_ROM_SEGMENT(takaraya_room_4) +DECLARE_ROM_SEGMENT(takaraya_room_5) +DECLARE_ROM_SEGMENT(takaraya_room_6) + +DECLARE_ROM_SEGMENT(ydan_boss_scene) +DECLARE_ROM_SEGMENT(ydan_boss_room_0) +DECLARE_ROM_SEGMENT(ydan_boss_room_1) + +DECLARE_ROM_SEGMENT(ddan_boss_scene) +DECLARE_ROM_SEGMENT(ddan_boss_room_0) +DECLARE_ROM_SEGMENT(ddan_boss_room_1) + +DECLARE_ROM_SEGMENT(bdan_boss_scene) +DECLARE_ROM_SEGMENT(bdan_boss_room_0) +DECLARE_ROM_SEGMENT(bdan_boss_room_1) + +DECLARE_ROM_SEGMENT(moribossroom_scene) +DECLARE_ROM_SEGMENT(moribossroom_room_0) +DECLARE_ROM_SEGMENT(moribossroom_room_1) + +DECLARE_ROM_SEGMENT(FIRE_bs_scene) +DECLARE_ROM_SEGMENT(FIRE_bs_room_0) +DECLARE_ROM_SEGMENT(FIRE_bs_room_1) + +DECLARE_ROM_SEGMENT(MIZUsin_bs_scene) +DECLARE_ROM_SEGMENT(MIZUsin_bs_room_0) +DECLARE_ROM_SEGMENT(MIZUsin_bs_room_1) + +DECLARE_ROM_SEGMENT(jyasinboss_scene) +DECLARE_ROM_SEGMENT(jyasinboss_room_0) +DECLARE_ROM_SEGMENT(jyasinboss_room_1) +DECLARE_ROM_SEGMENT(jyasinboss_room_2) +DECLARE_ROM_SEGMENT(jyasinboss_room_3) + +DECLARE_ROM_SEGMENT(HAKAdan_bs_scene) +DECLARE_ROM_SEGMENT(HAKAdan_bs_room_0) +DECLARE_ROM_SEGMENT(HAKAdan_bs_room_1) + +DECLARE_ROM_SEGMENT(ganon_boss_scene) +DECLARE_ROM_SEGMENT(ganon_boss_room_0) + +DECLARE_ROM_SEGMENT(ganon_final_scene) +DECLARE_ROM_SEGMENT(ganon_final_room_0) + +DECLARE_ROM_SEGMENT(entra_scene) +DECLARE_ROM_SEGMENT(entra_room_0) + +DECLARE_ROM_SEGMENT(entra_n_scene) +DECLARE_ROM_SEGMENT(entra_n_room_0) + +DECLARE_ROM_SEGMENT(enrui_scene) +DECLARE_ROM_SEGMENT(enrui_room_0) + +DECLARE_ROM_SEGMENT(market_alley_scene) +DECLARE_ROM_SEGMENT(market_alley_room_0) + +DECLARE_ROM_SEGMENT(market_alley_n_scene) +DECLARE_ROM_SEGMENT(market_alley_n_room_0) + +DECLARE_ROM_SEGMENT(market_day_scene) +DECLARE_ROM_SEGMENT(market_day_room_0) + +DECLARE_ROM_SEGMENT(market_night_scene) +DECLARE_ROM_SEGMENT(market_night_room_0) + +DECLARE_ROM_SEGMENT(market_ruins_scene) +DECLARE_ROM_SEGMENT(market_ruins_room_0) + +DECLARE_ROM_SEGMENT(shrine_scene) +DECLARE_ROM_SEGMENT(shrine_room_0) + +DECLARE_ROM_SEGMENT(shrine_n_scene) +DECLARE_ROM_SEGMENT(shrine_n_room_0) + +DECLARE_ROM_SEGMENT(shrine_r_scene) +DECLARE_ROM_SEGMENT(shrine_r_room_0) + +DECLARE_ROM_SEGMENT(kokiri_home_scene) +DECLARE_ROM_SEGMENT(kokiri_home_room_0) + +DECLARE_ROM_SEGMENT(kokiri_home3_scene) +DECLARE_ROM_SEGMENT(kokiri_home3_room_0) + +DECLARE_ROM_SEGMENT(kokiri_home4_scene) +DECLARE_ROM_SEGMENT(kokiri_home4_room_0) + +DECLARE_ROM_SEGMENT(kokiri_home5_scene) +DECLARE_ROM_SEGMENT(kokiri_home5_room_0) + +DECLARE_ROM_SEGMENT(kakariko_scene) +DECLARE_ROM_SEGMENT(kakariko_room_0) + +DECLARE_ROM_SEGMENT(kakariko3_scene) +DECLARE_ROM_SEGMENT(kakariko3_room_0) + +DECLARE_ROM_SEGMENT(shop1_scene) +DECLARE_ROM_SEGMENT(shop1_room_0) + +DECLARE_ROM_SEGMENT(kokiri_shop_scene) +DECLARE_ROM_SEGMENT(kokiri_shop_room_0) + +DECLARE_ROM_SEGMENT(golon_scene) +DECLARE_ROM_SEGMENT(golon_room_0) + +DECLARE_ROM_SEGMENT(zoora_scene) +DECLARE_ROM_SEGMENT(zoora_room_0) + +DECLARE_ROM_SEGMENT(drag_scene) +DECLARE_ROM_SEGMENT(drag_room_0) + +DECLARE_ROM_SEGMENT(alley_shop_scene) +DECLARE_ROM_SEGMENT(alley_shop_room_0) + +DECLARE_ROM_SEGMENT(night_shop_scene) +DECLARE_ROM_SEGMENT(night_shop_room_0) + +DECLARE_ROM_SEGMENT(face_shop_scene) +DECLARE_ROM_SEGMENT(face_shop_room_0) + +DECLARE_ROM_SEGMENT(link_home_scene) +DECLARE_ROM_SEGMENT(link_home_room_0) + +DECLARE_ROM_SEGMENT(impa_scene) +DECLARE_ROM_SEGMENT(impa_room_0) + +DECLARE_ROM_SEGMENT(malon_stable_scene) +DECLARE_ROM_SEGMENT(malon_stable_room_0) + +DECLARE_ROM_SEGMENT(labo_scene) +DECLARE_ROM_SEGMENT(labo_room_0) + +DECLARE_ROM_SEGMENT(hylia_labo_scene) +DECLARE_ROM_SEGMENT(hylia_labo_room_0) + +DECLARE_ROM_SEGMENT(tent_scene) +DECLARE_ROM_SEGMENT(tent_room_0) + +DECLARE_ROM_SEGMENT(hut_scene) +DECLARE_ROM_SEGMENT(hut_room_0) + +DECLARE_ROM_SEGMENT(daiyousei_izumi_scene) +DECLARE_ROM_SEGMENT(daiyousei_izumi_room_0) + +DECLARE_ROM_SEGMENT(yousei_izumi_tate_scene) +DECLARE_ROM_SEGMENT(yousei_izumi_tate_room_0) + +DECLARE_ROM_SEGMENT(yousei_izumi_yoko_scene) +DECLARE_ROM_SEGMENT(yousei_izumi_yoko_room_0) + +DECLARE_ROM_SEGMENT(kakusiana_scene) +DECLARE_ROM_SEGMENT(kakusiana_room_0) +DECLARE_ROM_SEGMENT(kakusiana_room_1) +DECLARE_ROM_SEGMENT(kakusiana_room_2) +DECLARE_ROM_SEGMENT(kakusiana_room_3) +DECLARE_ROM_SEGMENT(kakusiana_room_4) +DECLARE_ROM_SEGMENT(kakusiana_room_5) +DECLARE_ROM_SEGMENT(kakusiana_room_6) +DECLARE_ROM_SEGMENT(kakusiana_room_7) +DECLARE_ROM_SEGMENT(kakusiana_room_8) +DECLARE_ROM_SEGMENT(kakusiana_room_9) +DECLARE_ROM_SEGMENT(kakusiana_room_10) +DECLARE_ROM_SEGMENT(kakusiana_room_11) +DECLARE_ROM_SEGMENT(kakusiana_room_12) +DECLARE_ROM_SEGMENT(kakusiana_room_13) + +DECLARE_ROM_SEGMENT(hakaana_scene) +DECLARE_ROM_SEGMENT(hakaana_room_0) + +DECLARE_ROM_SEGMENT(hakaana2_scene) +DECLARE_ROM_SEGMENT(hakaana2_room_0) + +DECLARE_ROM_SEGMENT(hakaana_ouke_scene) +DECLARE_ROM_SEGMENT(hakaana_ouke_room_0) +DECLARE_ROM_SEGMENT(hakaana_ouke_room_1) +DECLARE_ROM_SEGMENT(hakaana_ouke_room_2) + +DECLARE_ROM_SEGMENT(syatekijyou_scene) +DECLARE_ROM_SEGMENT(syatekijyou_room_0) + +DECLARE_ROM_SEGMENT(tokinoma_scene) +DECLARE_ROM_SEGMENT(tokinoma_room_0) +DECLARE_ROM_SEGMENT(tokinoma_room_1) + +DECLARE_ROM_SEGMENT(kenjyanoma_scene) +DECLARE_ROM_SEGMENT(kenjyanoma_room_0) + +DECLARE_ROM_SEGMENT(hairal_niwa_scene) +DECLARE_ROM_SEGMENT(hairal_niwa_room_0) + +DECLARE_ROM_SEGMENT(hairal_niwa_n_scene) +DECLARE_ROM_SEGMENT(hairal_niwa_n_room_0) + +DECLARE_ROM_SEGMENT(hiral_demo_scene) +DECLARE_ROM_SEGMENT(hiral_demo_room_0) + +DECLARE_ROM_SEGMENT(hakasitarelay_scene) +DECLARE_ROM_SEGMENT(hakasitarelay_room_0) +DECLARE_ROM_SEGMENT(hakasitarelay_room_1) +DECLARE_ROM_SEGMENT(hakasitarelay_room_2) +DECLARE_ROM_SEGMENT(hakasitarelay_room_3) +DECLARE_ROM_SEGMENT(hakasitarelay_room_4) +DECLARE_ROM_SEGMENT(hakasitarelay_room_5) +DECLARE_ROM_SEGMENT(hakasitarelay_room_6) + +DECLARE_ROM_SEGMENT(turibori_scene) +DECLARE_ROM_SEGMENT(turibori_room_0) + +DECLARE_ROM_SEGMENT(nakaniwa_scene) +DECLARE_ROM_SEGMENT(nakaniwa_room_0) + +DECLARE_ROM_SEGMENT(bowling_scene) +DECLARE_ROM_SEGMENT(bowling_room_0) + +DECLARE_ROM_SEGMENT(souko_scene) +DECLARE_ROM_SEGMENT(souko_room_0) +DECLARE_ROM_SEGMENT(souko_room_1) +DECLARE_ROM_SEGMENT(souko_room_2) + +DECLARE_ROM_SEGMENT(miharigoya_scene) +DECLARE_ROM_SEGMENT(miharigoya_room_0) + +DECLARE_ROM_SEGMENT(mahouya_scene) +DECLARE_ROM_SEGMENT(mahouya_room_0) + +DECLARE_ROM_SEGMENT(ganon_demo_scene) +DECLARE_ROM_SEGMENT(ganon_demo_room_0) + +DECLARE_ROM_SEGMENT(kinsuta_scene) +DECLARE_ROM_SEGMENT(kinsuta_room_0) + +DECLARE_ROM_SEGMENT(spot00_scene) +DECLARE_ROM_SEGMENT(spot00_room_0) + +DECLARE_ROM_SEGMENT(spot01_scene) +DECLARE_ROM_SEGMENT(spot01_room_0) + +DECLARE_ROM_SEGMENT(spot02_scene) +DECLARE_ROM_SEGMENT(spot02_room_0) +DECLARE_ROM_SEGMENT(spot02_room_1) + +DECLARE_ROM_SEGMENT(spot03_scene) +DECLARE_ROM_SEGMENT(spot03_room_0) +DECLARE_ROM_SEGMENT(spot03_room_1) + +DECLARE_ROM_SEGMENT(spot04_scene) +DECLARE_ROM_SEGMENT(spot04_room_0) +DECLARE_ROM_SEGMENT(spot04_room_1) +DECLARE_ROM_SEGMENT(spot04_room_2) + +DECLARE_ROM_SEGMENT(spot05_scene) +DECLARE_ROM_SEGMENT(spot05_room_0) + +DECLARE_ROM_SEGMENT(spot06_scene) +DECLARE_ROM_SEGMENT(spot06_room_0) + +DECLARE_ROM_SEGMENT(spot07_scene) +DECLARE_ROM_SEGMENT(spot07_room_0) +DECLARE_ROM_SEGMENT(spot07_room_1) + +DECLARE_ROM_SEGMENT(spot08_scene) +DECLARE_ROM_SEGMENT(spot08_room_0) + +DECLARE_ROM_SEGMENT(spot09_scene) +DECLARE_ROM_SEGMENT(spot09_room_0) + +DECLARE_ROM_SEGMENT(spot10_scene) +DECLARE_ROM_SEGMENT(spot10_room_0) +DECLARE_ROM_SEGMENT(spot10_room_1) +DECLARE_ROM_SEGMENT(spot10_room_2) +DECLARE_ROM_SEGMENT(spot10_room_3) +DECLARE_ROM_SEGMENT(spot10_room_4) +DECLARE_ROM_SEGMENT(spot10_room_5) +DECLARE_ROM_SEGMENT(spot10_room_6) +DECLARE_ROM_SEGMENT(spot10_room_7) +DECLARE_ROM_SEGMENT(spot10_room_8) +DECLARE_ROM_SEGMENT(spot10_room_9) + +DECLARE_ROM_SEGMENT(spot11_scene) +DECLARE_ROM_SEGMENT(spot11_room_0) + +DECLARE_ROM_SEGMENT(spot12_scene) +DECLARE_ROM_SEGMENT(spot12_room_0) +DECLARE_ROM_SEGMENT(spot12_room_1) + +DECLARE_ROM_SEGMENT(spot13_scene) +DECLARE_ROM_SEGMENT(spot13_room_0) +DECLARE_ROM_SEGMENT(spot13_room_1) + +DECLARE_ROM_SEGMENT(spot15_scene) +DECLARE_ROM_SEGMENT(spot15_room_0) + +DECLARE_ROM_SEGMENT(spot16_scene) +DECLARE_ROM_SEGMENT(spot16_room_0) + +DECLARE_ROM_SEGMENT(spot17_scene) +DECLARE_ROM_SEGMENT(spot17_room_0) +DECLARE_ROM_SEGMENT(spot17_room_1) + +DECLARE_ROM_SEGMENT(spot18_scene) +DECLARE_ROM_SEGMENT(spot18_room_0) +DECLARE_ROM_SEGMENT(spot18_room_1) +DECLARE_ROM_SEGMENT(spot18_room_2) +DECLARE_ROM_SEGMENT(spot18_room_3) + +DECLARE_ROM_SEGMENT(spot20_scene) +DECLARE_ROM_SEGMENT(spot20_room_0) + +DECLARE_ROM_SEGMENT(ganon_tou_scene) +DECLARE_ROM_SEGMENT(ganon_tou_room_0) + +DECLARE_ROM_SEGMENT(test01_scene) +DECLARE_ROM_SEGMENT(test01_room_0) + +DECLARE_ROM_SEGMENT(besitu_scene) +DECLARE_ROM_SEGMENT(besitu_room_0) + +DECLARE_ROM_SEGMENT(depth_test_scene) +DECLARE_ROM_SEGMENT(depth_test_room_0) + +DECLARE_ROM_SEGMENT(syotes_scene) +DECLARE_ROM_SEGMENT(syotes_room_0) + +DECLARE_ROM_SEGMENT(syotes2_scene) +DECLARE_ROM_SEGMENT(syotes2_room_0) + +DECLARE_ROM_SEGMENT(sutaru_scene) +DECLARE_ROM_SEGMENT(sutaru_room_0) + +DECLARE_ROM_SEGMENT(hairal_niwa2_scene) +DECLARE_ROM_SEGMENT(hairal_niwa2_room_0) + +DECLARE_ROM_SEGMENT(sasatest_scene) +DECLARE_ROM_SEGMENT(sasatest_room_0) + +DECLARE_ROM_SEGMENT(testroom_scene) +DECLARE_ROM_SEGMENT(testroom_room_0) +DECLARE_ROM_SEGMENT(testroom_room_1) +DECLARE_ROM_SEGMENT(testroom_room_2) +DECLARE_ROM_SEGMENT(testroom_room_3) +DECLARE_ROM_SEGMENT(testroom_room_4) + + +#endif diff --git a/soh/include/sequence.h b/soh/include/sequence.h new file mode 100644 index 000000000..4112f7468 --- /dev/null +++ b/soh/include/sequence.h @@ -0,0 +1,260 @@ +#ifndef SEQUENCE_H +#define SEQUENCE_H + +#define NA_BGM_STOP 0x100000FF + +#define NA_BGM_GENERAL_SFX 0x0 // General Sound Effects +#define NA_BGM_NATURE_AMBIENCE 0x1 // Environmental nature background sounds +#define NA_BGM_FIELD_LOGIC 0x2 // Hyrule Field +#define NA_BGM_FIELD_INIT 0x3 // Hyrule Field Initial Segment From Loading Area +#define NA_BGM_FIELD_DEFAULT_1 0x4 // Hyrule Field Moving Segment 1 +#define NA_BGM_FIELD_DEFAULT_2 0x5 // Hyrule Field Moving Segment 2 +#define NA_BGM_FIELD_DEFAULT_3 0x6 // Hyrule Field Moving Segment 3 +#define NA_BGM_FIELD_DEFAULT_4 0x7 // Hyrule Field Moving Segment 4 +#define NA_BGM_FIELD_DEFAULT_5 0x8 // Hyrule Field Moving Segment 5 +#define NA_BGM_FIELD_DEFAULT_6 0x9 // Hyrule Field Moving Segment 6 +#define NA_BGM_FIELD_DEFAULT_7 0x0A // Hyrule Field Moving Segment 7 +#define NA_BGM_FIELD_DEFAULT_8 0x0B // Hyrule Field Moving Segment 8 +#define NA_BGM_FIELD_DEFAULT_9 0x0C // Hyrule Field Moving Segment 9 +#define NA_BGM_FIELD_DEFAULT_A 0x0D // Hyrule Field Moving Segment 10 +#define NA_BGM_FIELD_DEFAULT_B 0x0E // Hyrule Field Moving Segment 11 +#define NA_BGM_FIELD_ENEMY_INIT 0x0F // Hyrule Field Enemy Approaches +#define NA_BGM_FIELD_ENEMY_1 0x10 // Hyrule Field Enemy Near Segment 1 +#define NA_BGM_FIELD_ENEMY_2 0x11 // Hyrule Field Enemy Near Segment 2 +#define NA_BGM_FIELD_ENEMY_3 0x12 // Hyrule Field Enemy Near Segment 3 +#define NA_BGM_FIELD_ENEMY_4 0x13 // Hyrule Field Enemy Near Segment 4 +#define NA_BGM_FIELD_STILL_1 0x14 // Hyrule Field Standing Still Segment 1 +#define NA_BGM_FIELD_STILL_2 0x15 // Hyrule Field Standing Still Segment 2 +#define NA_BGM_FIELD_STILL_3 0x16 // Hyrule Field Standing Still Segment 3 +#define NA_BGM_FIELD_STILL_4 0x17 // Hyrule Field Standing Still Segment 4 +#define NA_BGM_DUNGEON 0x18 // Dodongo's Cavern +#define NA_BGM_KAKARIKO_ADULT 0x19 // Kakariko Village (Adult) +#define NA_BGM_ENEMY 0x1A // Battle +#define NA_BGM_BOSS 0x1B // Boss Battle "NA_BGM_BOSS00" +#define NA_BGM_INSIDE_DEKU_TREE 0x1C // Inside the Deku Tree "NA_BGM_FAIRY_DUNGEON" +#define NA_BGM_MARKET 0x1D // Market +#define NA_BGM_TITLE 0x1E // Title Theme +#define NA_BGM_LINK_HOUSE 0x1F // House +#define NA_BGM_GAME_OVER 0x20 // Game Over +#define NA_BGM_BOSS_CLEAR 0x21 // Boss Clear +#define NA_BGM_ITEM_GET 0x22 // Obtain Item +#define NA_BGM_OPENING_GANON 0x23 // Enter Ganondorf +#define NA_BGM_HEART_GET 0x24 // Obtain Heart Container +#define NA_BGM_OCA_LIGHT 0x25 // Prelude of Light +#define NA_BGM_JABU_JABU 0x26 // Inside Jabu-Jabu's Belly "NA_BGM_BUYO_DUNGEON" +#define NA_BGM_KAKARIKO_KID 0x27 // Kakariko Village (Child) +#define NA_BGM_GREAT_FAIRY 0x28 // Great Fairy's Fountain "NA_BGM_GODESS" +#define NA_BGM_ZELDA_THEME 0x29 // Zelda's Theme "NA_BGM_HIME" +#define NA_BGM_FIRE_TEMPLE 0x2A // Fire Temple "NA_BGM_FIRE_DUNGEON" +#define NA_BGM_OPEN_TRE_BOX 0x2B // Open Treasure Chest +#define NA_BGM_FOREST_TEMPLE 0x2C // Forest Temple "NA_BGM_FORST_DUNGEON" +#define NA_BGM_COURTYARD 0x2D // Hyrule Castle Courtyard "NA_BGM_HIRAL_GARDEN" +#define NA_BGM_GANON_TOWER 0x2E // Ganondorf's Theme +#define NA_BGM_LONLON 0x2F // Lon Lon Ranch "NA_BGM_RONRON" +#define NA_BGM_GORON_CITY 0x30 // Goron City "NA_BGM_GORON" +#define NA_BGM_FIELD_MORNING 0x31 // Hyrule Field Morning Theme +#define NA_BGM_SPIRITUAL_STONE 0x32 // Spiritual Stone Get "NA_BGM_SPIRIT_STONE" +#define NA_BGM_OCA_BOLERO 0x33 // Bolero of Fire "NA_BGM_OCA_FLAME" +#define NA_BGM_OCA_MINUET 0x34 // Minuet of Forest "NA_BGM_OCA_WIND" +#define NA_BGM_OCA_SERENADE 0x35 // Serenade of Water "NA_BGM_OCA_WATER" +#define NA_BGM_OCA_REQUIEM 0x36 // Requiem of Spirit "NA_BGM_OCA_SOUL" +#define NA_BGM_OCA_NOCTURNE 0x37 // Nocturne of Shadow "NA_BGM_OCA_DARKNESS" +#define NA_BGM_MINI_BOSS 0x38 // Mini-Boss Battle "NA_BGM_MIDDLE_BOSS" +#define NA_BGM_SMALL_ITEM_GET 0x39 // Obtain Small Item "NA_BGM_S_ITEM_GET" +#define NA_BGM_TEMPLE_OF_TIME 0x3A // Temple of Time "NA_BGM_SHRINE_OF_TIME" +#define NA_BGM_EVENT_CLEAR 0x3B // Escape from Lon Lon Ranch +#define NA_BGM_KOKIRI 0x3C // Kokiri Forest +#define NA_BGM_OCA_FAIRY_GET 0x3D // Obtain Fairy Ocarina "NA_BGM_OCA_YOUSEI" +#define NA_BGM_SARIA_THEME 0x3E // Lost Woods "NA_BGM_MAYOIMORI" +#define NA_BGM_SPIRIT_TEMPLE 0x3F // Spirit Temple "NA_BGM_SOUL_DUNGEON" +#define NA_BGM_HORSE 0x40 // Horse Race +#define NA_BGM_HORSE_GOAL 0x41 // Horse Race Goal +#define NA_BGM_INGO 0x42 // Ingo's Theme +#define NA_BGM_MEDALLION_GET 0x43 // Obtain Medallion "NA_BGM_MEDAL_GET" +#define NA_BGM_OCA_SARIA 0x44 // Ocarina Saria's Song +#define NA_BGM_OCA_EPONA 0x45 // Ocarina Epona's Song +#define NA_BGM_OCA_ZELDA 0x46 // Ocarina Zelda's Lullaby +#define NA_BGM_OCA_SUNS 0x47 // Ocarina Sun's Song "NA_BGM_OCA_SUNMOON" +#define NA_BGM_OCA_TIME 0x48 // Ocarina Song of Time +#define NA_BGM_OCA_STORM 0x49 // Ocarina Song of Storms +#define NA_BGM_NAVI_OPENING 0x4A // Fairy Flying "NA_BGM_NAVI" +#define NA_BGM_DEKU_TREE_CS 0x4B // Deku Tree "NA_BGM_DEKUNOKI" +#define NA_BGM_WINDMILL 0x4C // Windmill Hut "NA_BGM_FUSHA" +#define NA_BGM_HYRULE_CS 0x4D // Legend of Hyrule "NA_BGM_HIRAL_DEMO" +#define NA_BGM_MINI_GAME 0x4E // Shooting Gallery +#define NA_BGM_SHEIK 0x4F // Sheik's Theme "NA_BGM_SEAK" +#define NA_BGM_ZORA_DOMAIN 0x50 // Zora's Domain "NA_BGM_ZORA" +#define NA_BGM_APPEAR 0x51 // Enter Zelda +#define NA_BGM_ADULT_LINK 0x52 // Goodbye to Zelda +#define NA_BGM_MASTER_SWORD 0x53 // Master Sword +#define NA_BGM_INTRO_GANON 0x54 +#define NA_BGM_SHOP 0x55 // Shop +#define NA_BGM_CHAMBER_OF_SAGES 0x56 // Chamber of the Sages "NA_BGM_KENJA" +#define NA_BGM_FILE_SELECT 0x57 // File Select +#define NA_BGM_ICE_CAVERN 0x58 // Ice Cavern "NA_BGM_ICE_DUNGEON" +#define NA_BGM_DOOR_OF_TIME 0x59 // Open Door of Temple of Time "NA_BGM_GATE_OPEN" +#define NA_BGM_OWL 0x5A // Kaepora Gaebora's Theme +#define NA_BGM_SHADOW_TEMPLE 0x5B // Shadow Temple "NA_BGM_DARKNESS_DUNGEON" +#define NA_BGM_WATER_TEMPLE 0x5C // Water Temple "NA_BGM_AQUA_DUNGEON" +#define NA_BGM_BRIDGE_TO_GANONS 0x5D // Ganon's Castle Bridge "NA_BGM_BRIDGE" +#define NA_BGM_OCARINA_OF_TIME 0x5E // Ocarina of Time "NA_BGM_SARIA" +#define NA_BGM_GERUDO_VALLEY 0x5F // Gerudo Valley "NA_BGM_GERUDO" +#define NA_BGM_POTION_SHOP 0x60 // Potion Shop "NA_BGM_DRUGSTORE" +#define NA_BGM_KOTAKE_KOUME 0x61 // Kotake & Koume's Theme +#define NA_BGM_ESCAPE 0x62 // Escape from Ganon's Castle +#define NA_BGM_UNDERGROUND 0x63 // Ganon's Castle Under Ground +#define NA_BGM_GANONDORF_BOSS 0x64 // Ganondorf Battle +#define NA_BGM_GANON_BOSS 0x65 // Ganon Battle +#define NA_BGM_END_DEMO 0x66 // Seal of Six Sages +#define NA_BGM_STAFF_1 0x67 // End Credits I +#define NA_BGM_STAFF_2 0x68 // End Credits II +#define NA_BGM_STAFF_3 0x69 // End Credits III +#define NA_BGM_STAFF_4 0x6A // End Credits IV +#define NA_BGM_FIRE_BOSS 0x6B // King Dodongo & Volvagia Boss Battle "NA_BGM_BOSS01" +#define NA_BGM_TIMED_MINI_GAME 0x6C // Mini-Game +#define NA_BGM_VARIOUS_SFX 0x6D // A small collection of various sound effects +#define NA_BGM_NO_MUSIC 0x7F // No bgm music is played +#define NA_BGM_NATURE_SFX_RAIN 0x80 // Related to rain +#define NA_BGM_DISABLED 0xFFFF + +typedef enum { + /* 0 */ SEQ_PLAYER_BGM_MAIN, + /* 1 */ SEQ_PLAYER_FANFARE, + /* 2 */ SEQ_PLAYER_SFX, + /* 3 */ SEQ_PLAYER_BGM_SUB +} SequencePlayerId; + +typedef enum { + /* 0 */ SEQ_MODE_DEFAULT, + /* 1 */ SEQ_MODE_ENEMY, + /* 2 */ SEQ_MODE_STILL, // Not moving or first-person view + /* 3 */ SEQ_MODE_IGNORE +} SequenceMode; + +typedef enum { + /* 0x0 */ CHANNEL_IO_PORT_0, + /* 0x1 */ CHANNEL_IO_PORT_1, + /* 0x2 */ CHANNEL_IO_PORT_2, + /* 0x3 */ CHANNEL_IO_PORT_3, + /* 0x4 */ CHANNEL_IO_PORT_4, + /* 0x5 */ CHANNEL_IO_PORT_5, + /* 0x6 */ CHANNEL_IO_PORT_6, + /* 0x7 */ CHANNEL_IO_PORT_7 +} ChannelIOPort; + +typedef enum { + /* 0x0 */ NATURE_CHANNEL_STREAM_0, + /* 0x1 */ NATURE_CHANNEL_CRITTER_0, + /* 0x2 */ NATURE_CHANNEL_CRITTER_1, + /* 0x3 */ NATURE_CHANNEL_CRITTER_2, + /* 0x4 */ NATURE_CHANNEL_CRITTER_3, + /* 0x5 */ NATURE_CHANNEL_CRITTER_4, + /* 0x6 */ NATURE_CHANNEL_CRITTER_5, + /* 0x7 */ NATURE_CHANNEL_CRITTER_6, + /* 0x8 */ NATURE_CHANNEL_CRITTER_7, + /* 0xC */ NATURE_CHANNEL_STREAM_1 = 12, + /* 0xD */ NATURE_CHANNEL_UNK, + /* 0xE */ NATURE_CHANNEL_RAIN, + /* 0xF */ NATURE_CHANNEL_LIGHTNING +} NatureChannelIdx; // playerIdx = 0 (Overlaps with main bgm) + +typedef enum { + /* 0x00 */ NATURE_ID_GENERAL_NIGHT, + /* 0x01 */ NATURE_ID_MARKET_ENTRANCE, + /* 0x02 */ NATURE_ID_KAKARIKO_REGION, + /* 0x03 */ NATURE_ID_MARKET_RUINS, + /* 0x04 */ NATURE_ID_KOKIRI_REGION, + /* 0x05 */ NATURE_ID_MARKET_NIGHT, + /* 0x06 */ NATURE_ID_06, + /* 0x07 */ NATURE_ID_GANONS_LAIR, + /* 0x08 */ NATURE_ID_08, + /* 0x09 */ NATURE_ID_09, + /* 0x0A */ NATURE_ID_WASTELAND, + /* 0x0B */ NATURE_ID_COLOSSUS, + /* 0x0C */ NATURE_ID_DEATH_MOUNTAIN_TRAIL, + /* 0x0D */ NATURE_ID_0D, + /* 0x0E */ NATURE_ID_0E, + /* 0x0F */ NATURE_ID_0F, + /* 0x10 */ NATURE_ID_10, + /* 0x11 */ NATURE_ID_11, + /* 0x12 */ NATURE_ID_12, + /* 0x13 */ NATURE_ID_NONE, + /* 0xFF */ NATURE_ID_DISABLED = 0xFF +} NatureAmbienceId; + +typedef enum { + /* 0x00 */ NATURE_STREAM_RUSHING_WATER, + /* 0x01 */ NATURE_STREAM_HOWLING_WIND, + /* 0x02 */ NATURE_STREAM_SCREECHING_WIND, + /* 0x03 */ NATURE_STREAM_SCREECHING_WIND_ALT1 +} NatureStreamId; + +typedef enum { + /* 0x00 */ NATURE_CRITTER_BIRD_CHIRP_1, + /* 0x01 */ NATURE_CRITTER_TAP, + /* 0x02 */ NATURE_CRITTER_BIRD_CHIRP_2, + /* 0x03 */ NATURE_CRITTER_BIRD_CHIRP_1_ALT1, + /* 0x04 */ NATURE_CRITTER_CRICKETS, + /* 0x05 */ NATURE_CRITTER_BIRD_CHIRP_1_ALT2, + /* 0x06 */ NATURE_CRITTER_LOUD_CHIRPING, + /* 0x07 */ NATURE_CRITTER_BIRD_CHIRP_1_ALT3, + /* 0x08 */ NATURE_CRITTER_BIRD_CHIRP_1_ALT4, + /* 0x09 */ NATURE_CRITTER_CROWS_CAWS, + /* 0x0A */ NATURE_CRITTER_SMALL_BIRD_CHIRPS, + /* 0x0B */ NATURE_CRITTER_BIRD_SCREECH, + /* 0x0C */ NATURE_CRITTER_BIRD_SONG, + /* 0x0D */ NATURE_CRITTER_OWL_HOOT, + /* 0x0E */ NATURE_CRITTER_HAWK_SCREECH, + /* 0x0F */ NATURE_CRITTER_BIRD_CALL, + /* 0x10 */ NATURE_CRITTER_CAWING_BIRD, + /* 0x11 */ NATURE_CRITTER_CUCCO_CROWS, + /* 0x12 */ NATURE_CRITTER_BIRD_CHIRP_2_ALT1, + /* 0x13 */ NATURE_CRITTER_BIRD_CHIRP_1_ALT5 +} NatureAmimalId; + +#define NATURE_IO_CRITTER_0_TYPE(type) NATURE_CHANNEL_CRITTER_0, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_0_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_0, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_0_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_0, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_0_PORT5(reverb) NATURE_CHANNEL_CRITTER_0, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_CRITTER_1_TYPE(type) NATURE_CHANNEL_CRITTER_1, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_1_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_1, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_1_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_1, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_1_PORT5(reverb) NATURE_CHANNEL_CRITTER_1, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_CRITTER_2_TYPE(type) NATURE_CHANNEL_CRITTER_2, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_2_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_2, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_2_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_2, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_2_PORT5(reverb) NATURE_CHANNEL_CRITTER_2, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_CRITTER_3_TYPE(type) NATURE_CHANNEL_CRITTER_3, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_3_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_3, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_3_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_3, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_3_PORT5(reverb) NATURE_CHANNEL_CRITTER_3, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_CRITTER_4_TYPE(type) NATURE_CHANNEL_CRITTER_4, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_4_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_4, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_4_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_4, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_4_PORT5(reverb) NATURE_CHANNEL_CRITTER_4, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_CRITTER_5_TYPE(type) NATURE_CHANNEL_CRITTER_5, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_5_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_5, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_5_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_5, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_5_PORT5(reverb) NATURE_CHANNEL_CRITTER_5, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_CRITTER_6_TYPE(type) NATURE_CHANNEL_CRITTER_6, CHANNEL_IO_PORT_2, type +#define NATURE_IO_CRITTER_6_BEND_PITCH(bend) NATURE_CHANNEL_CRITTER_6, CHANNEL_IO_PORT_3, bend +#define NATURE_IO_CRITTER_6_NUM_LAYERS(num) NATURE_CHANNEL_CRITTER_6, CHANNEL_IO_PORT_4, num +#define NATURE_IO_CRITTER_6_PORT5(reverb) NATURE_CHANNEL_CRITTER_6, CHANNEL_IO_PORT_5, reverb + +#define NATURE_IO_STREAM_0_TYPE(type) NATURE_CHANNEL_STREAM_0, CHANNEL_IO_PORT_2, type +#define NATURE_IO_STREAM_0_PORT3(data) NATURE_CHANNEL_STREAM_0, CHANNEL_IO_PORT_3, data +#define NATURE_IO_STREAM_0_PORT4(data) NATURE_CHANNEL_STREAM_0, CHANNEL_IO_PORT_4, data + +#define NATURE_IO_STREAM_1_TYPE(type) NATURE_CHANNEL_STREAM_1, CHANNEL_IO_PORT_2, type +#define NATURE_IO_STREAM_1_PORT3(data) NATURE_CHANNEL_STREAM_1, CHANNEL_IO_PORT_3, data +#define NATURE_IO_STREAM_1_PORT4(data) NATURE_CHANNEL_STREAM_1, CHANNEL_IO_PORT_4, data + +#define NATURE_IO_ENTRIES_END 0xFF + +#endif diff --git a/soh/include/sfx.h b/soh/include/sfx.h new file mode 100644 index 000000000..25da99ea4 --- /dev/null +++ b/soh/include/sfx.h @@ -0,0 +1,1423 @@ +#ifndef SFX_H +#define SFX_H + +#define SFX_FLAG 0x800 + +// ------------ PLAYER ------------ + +#define NA_SE_PL_WALK_GROUND 0x800 +#define NA_SE_PL_WALK_SAND 0x801 +#define NA_SE_PL_WALK_CONCRETE 0x802 +#define NA_SE_PL_WALK_DIRT 0x803 +#define NA_SE_PL_WALK_WATER0 0x804 +#define NA_SE_PL_WALK_WATER1 0x805 +#define NA_SE_PL_WALK_WATER2 0x806 +#define NA_SE_PL_WALK_MAGMA 0x807 +#define NA_SE_PL_WALK_GRASS 0x808 +#define NA_SE_PL_WALK_IRON 0x809 +#define NA_SE_PL_WALK_LADDER 0x80A +#define NA_SE_PL_WALK_GLASS 0x80B +#define NA_SE_PL_WALK_WALL 0x80C +#define NA_SE_PL_WALK_HEAVYBOOTS 0x80D +#define NA_SE_PL_DUMMY_14 0x80E +#define NA_SE_PL_WALK_ICE 0x80F +#define NA_SE_PL_JUMP 0x810 +#define NA_SE_PL_JUMP_SAND 0x811 +#define NA_SE_PL_JUMP_CONCRETE 0x812 +#define NA_SE_PL_JUMP_DIRT 0x813 +#define NA_SE_PL_JUMP_WATER0 0x814 +#define NA_SE_PL_JUMP_WATER1 0x815 +#define NA_SE_PL_JUMP_WATER2 0x816 +#define NA_SE_PL_JUMP_MAGMA 0x817 +#define NA_SE_PL_JUMP_GRASS 0x818 +#define NA_SE_PL_JUMP_IRON 0x819 +#define NA_SE_PL_JUMP_LADDER 0x81A +#define NA_SE_PL_JUMP_GLASS 0x81B +#define NA_SE_PL_DUMMY28 0x81C +#define NA_SE_PL_JUMP_HEAVYBOOTS 0x81D +#define NA_SE_PL_DUMMY30 0x81E +#define NA_SE_PL_JUMP_ICE 0x81F +#define NA_SE_PL_LAND 0x820 +#define NA_SE_PL_LAND_SAND 0x821 +#define NA_SE_PL_LAND_CONCRETE 0x822 +#define NA_SE_PL_LAND_DIRT 0x823 +#define NA_SE_PL_LAND_WATER0 0x824 +#define NA_SE_PL_LAND_WATER1 0x825 +#define NA_SE_PL_LAND_WATER2 0x826 +#define NA_SE_PL_LAND_MAGMA 0x827 +#define NA_SE_PL_LAND_GRASS 0x828 +#define NA_SE_PL_LAND_IRON 0x829 +#define NA_SE_PL_LAND_LADDER 0x82A +#define NA_SE_PL_LAND_GLASS 0x82B +#define NA_SE_PL_DUMMY_44 0x82C +#define NA_SE_PL_LAND_HEAVYBOOTS 0x82D +#define NA_SE_PL_DUMMY_46 0x82E +#define NA_SE_PL_LAND_ICE 0x82F +#define NA_SE_PL_SLIPDOWN 0x830 +#define NA_SE_PL_CLIMB_CLIFF 0x831 +#define NA_SE_PL_SIT_ON_HORSE 0x832 +#define NA_SE_PL_GET_OFF_HORSE 0x833 +#define NA_SE_PL_TAKE_OUT_SHIELD 0x834 +#define NA_SE_PL_CHANGE_ARMS 0x835 +#define NA_SE_PL_CATCH_BOOMERANG 0x836 +#define NA_SE_PL_DUMMY_55 0x837 +#define NA_SE_PL_DUMMY_56 0x838 +#define NA_SE_PL_SWIM 0x839 +#define NA_SE_PL_THROW 0x83A +#define NA_SE_PL_BODY_BOUND 0x83B +#define NA_SE_PL_ROLL 0x83C +#define NA_SE_PL_SKIP 0x83D +#define NA_SE_PL_BODY_HIT 0x83E +#define NA_SE_PL_DAMAGE 0x83F +#define NA_SE_PL_SLIP 0x840 +#define NA_SE_PL_SLIP_SAND 0x841 +#define NA_SE_PL_SLIP_CONCRETE 0x842 +#define NA_SE_PL_SLIP_DIRT 0x843 +#define NA_SE_PL_SLIP_WATER0 0x844 +#define NA_SE_PL_SLIP_WATER1 0x845 +#define NA_SE_PL_SLIP_WATER2 0x846 +#define NA_SE_PL_SLIP_MAGMA 0x847 +#define NA_SE_PL_SLIP_GRASS 0x848 +#define NA_SE_PL_SLIP_IRON 0x849 +#define NA_SE_PL_SLIP_LADDER 0x84A +#define NA_SE_PL_SLIP_GLASS 0x84B +#define NA_SE_PL_DUMMY76 0x84C +#define NA_SE_PL_SLIP_HEAVYBOOTS 0x84D +#define NA_SE_PL_DUMMY78 0x84E +#define NA_SE_PL_SLIP_ICE 0x84F +#define NA_SE_PL_BOUND 0x850 +#define NA_SE_PL_BOUND_SAND 0x851 +#define NA_SE_PL_BOUND_CONCRETE 0x852 +#define NA_SE_PL_BOUND_DIRT 0x853 +#define NA_SE_PL_BOUND_WATER0 0x854 +#define NA_SE_PL_BOUND_WATER1 0x855 +#define NA_SE_PL_BOUND_WATER2 0x856 +#define NA_SE_PL_BOUND_MAGMA 0x857 +#define NA_SE_PL_BOUND_GRASS 0x858 +#define NA_SE_PL_BOUND_IRON 0x859 +#define NA_SE_PL_BOUND_LADDER 0x85A +#define NA_SE_PL_BOUND_WOOD 0x85B +#define NA_SE_PL_DUMMY_92 0x85C +#define NA_SE_PL_BOUND_HEAVYBOOTS 0x85D +#define NA_SE_PL_DUMMY_94 0x85E +#define NA_SE_PL_BOUND_ICE 0x85F +#define NA_SE_PL_DUMMY_96 0x860 +#define NA_SE_PL_DUMMY_97 0x861 +#define NA_SE_PL_DUMMY_98 0x862 +#define NA_SE_PL_FACE_UP 0x863 +#define NA_SE_PL_DIVE_BUBBLE 0x864 +#define NA_SE_PL_MOVE_BUBBLE 0x865 +#define NA_SE_PL_METALEFFECT_KID 0x866 +#define NA_SE_PL_METALEFFECT_ADULT 0x867 +#define NA_SE_PL_SPARK 0x868 +#define NA_SE_PL_PULL_UP_PLANT 0x869 +#define NA_SE_PL_PULL_UP_ROCK 0x86A +#define NA_SE_PL_IN_BUBBLE 0x86B +#define NA_SE_PL_PULL_UP_BIGROCK 0x86C +#define NA_SE_PL_SWORD_CHARGE 0x86D +#define NA_SE_PL_FREEZE 0x86E +#define NA_SE_PL_PULL_UP_POT 0x86F +#define NA_SE_PL_KNOCK 0x870 +#define NA_SE_PL_CALM_HIT 0x871 +#define NA_SE_PL_CALM_PAT 0x872 +#define NA_SE_PL_SUBMERGE 0x873 +#define NA_SE_PL_FREEZE_S 0x874 +#define NA_SE_PL_ICE_BROKEN 0x875 +#define NA_SE_PL_SLIP_ICE_LELEL 0x876 +#define NA_SE_PL_PUT_OUT_ITEM 0x877 +#define NA_SE_PL_PULL_UP_WOODBOX 0x878 +#define NA_SE_PL_MAGIC_FIRE 0x879 +#define NA_SE_PL_MAGIC_WIND_NORMAL 0x87A +#define NA_SE_PL_MAGIC_WIND_WARP 0x87B +#define NA_SE_PL_MAGIC_SOUL_NORMAL 0x87C +#define NA_SE_PL_ARROW_CHARGE_FIRE 0x87D +#define NA_SE_PL_ARROW_CHARGE_ICE 0x87E +#define NA_SE_PL_ARROW_CHARGE_LIGHT 0x87F +#define NA_SE_PL_DUMMY_128 0x880 +#define NA_SE_PL_DUMMY_129 0x881 +#define NA_SE_PL_DUMMY_130 0x882 +#define NA_SE_PL_PULL_UP_RUTO 0x883 +#define NA_SE_PL_DUMMY_132 0x884 +#define NA_SE_PL_DUMMY_133 0x885 +#define NA_SE_PL_DUMMY_134 0x886 +#define NA_SE_PL_DUMMY_135 0x887 +#define NA_SE_PL_DUMMY_136 0x888 +#define NA_SE_PL_DUMMY_137 0x889 +#define NA_SE_PL_DUMMY_138 0x88A +#define NA_SE_PL_DUMMY_139 0x88B +#define NA_SE_PL_DUMMY_140 0x88C +#define NA_SE_PL_DUMMY_141 0x88D +#define NA_SE_PL_DUMMY_142 0x88E +#define NA_SE_PL_DUMMY_143 0x88F +#define NA_SE_PL_DUMMY_144 0x890 +#define NA_SE_PL_DUMMY_145 0x891 +#define NA_SE_PL_DUMMY_146 0x892 +#define NA_SE_PL_DUMMY_147 0x893 +#define NA_SE_PL_DUMMY_148 0x894 +#define NA_SE_PL_DUMMY_149 0x895 +#define NA_SE_PL_DUMMY_150 0x896 +#define NA_SE_PL_DUMMY_151 0x897 +#define NA_SE_PL_DUMMY_152 0x898 +#define NA_SE_PL_DUMMY_153 0x899 +#define NA_SE_PL_DUMMY_154 0x89A +#define NA_SE_PL_DUMMY_155 0x89B +#define NA_SE_PL_DUMMY_156 0x89C +#define NA_SE_PL_DUMMY_157 0x89D +#define NA_SE_PL_DUMMY_158 0x89E +#define NA_SE_PL_DUMMY_159 0x89F +#define NA_SE_PL_DUMMY_160 0x8A0 +#define NA_SE_PL_DUMMY_161 0x8A1 +#define NA_SE_PL_DUMMY_162 0x8A2 +#define NA_SE_PL_DUMMY_163 0x8A3 +#define NA_SE_PL_DUMMY_164 0x8A4 +#define NA_SE_PL_DUMMY_165 0x8A5 +#define NA_SE_PL_DUMMY_166 0x8A6 +#define NA_SE_PL_DUMMY_167 0x8A7 +#define NA_SE_PL_DUMMY_168 0x8A8 +#define NA_SE_PL_DUMMY_169 0x8A9 +#define NA_SE_PL_DUMMY_170 0x8AA +#define NA_SE_PL_DUMMY_171 0x8AB +#define NA_SE_PL_DUMMY_172 0x8AC +#define NA_SE_PL_DUMMY_173 0x8AD +#define NA_SE_PL_DUMMY_174 0x8AE +#define NA_SE_PL_DUMMY_175 0x8AF +#define NA_SE_PL_CRAWL 0x8B0 +#define NA_SE_PL_CRAWL_SAND 0x8B1 +#define NA_SE_PL_CRAWL_CONCRETE 0x8B2 +#define NA_SE_PL_CRAWL_DIRT 0x8B3 +#define NA_SE_PL_CRAWL_WATER0 0x8B4 +#define NA_SE_PL_DUMMY_181 0x8B5 +#define NA_SE_PL_DUMMY_182 0x8B6 +#define NA_SE_PL_DUMMY_183 0x8B7 +#define NA_SE_PL_DUMMY_184 0x8B8 +#define NA_SE_PL_DUMMY_185 0x8B9 +#define NA_SE_PL_DUMMY_186 0x8BA +#define NA_SE_PL_CRAWL_WOOD 0x8BB +#define NA_SE_PL_CRAWL_ICE 0x8BC +#define NA_SE_PL_DUMMY_189 0x8BD +#define NA_SE_PL_DUMMY_190 0x8BE +#define NA_SE_PL_DUMMY_191 0x8BF +#define NA_SE_PL_MAGIC_SOUL_FLASH 0x8C0 +#define NA_SE_PL_ROLL_DUST 0x8C1 +#define NA_SE_PL_DUMMY_192 0x8C2 +#define NA_SE_PL_MAGIC_SOUL_BALL 0x8C3 +#define NA_SE_PL_SPIRAL_HEAL_BEAM 0x8C4 +#define NA_SE_PL_BOUND_NOWEAPON 0x8C5 +#define NA_SE_PL_PLANT_GROW_UP 0x8C6 +#define NA_SE_PL_PLANT_TALLER 0x8C7 +#define NA_SE_PL_MAGIC_WIND_VANISH 0x8C8 +#define NA_SE_PL_HOBBERBOOTS_LV 0x8C9 +#define NA_SE_PL_PLANT_MOVE 0x8CA +#define NA_SE_EV_WALL_MOVE_SP 0x8CB // EV sound inside PL? +#define NA_SE_PL_DUMMY_204 0x8CC +#define NA_SE_PL_DUMMY_205 0x8CD 0xCC +#define NA_SE_PL_DUMMY_206 0x8CE +#define NA_SE_PL_DUMMY_207 0x8CF +#define NA_SE_PL_SLIP_LEVEL 0x8D0 +#define NA_SE_PL_SLIP_SAND_LEVEL 0x8D1 +#define NA_SE_PL_SLIP_CONCRETE_LEVEL 0x8D2 +#define NA_SE_PL_SLIP_DIRT_LEVEL 0x8D3 +#define NA_SE_PL_SLIP_WATER0_LEVEL 0x8D4 +#define NA_SE_PL_SLIP_WATER1_LEVEL 0x8D5 +#define NA_SE_PL_SLIP_WATER2_LEVEL 0x8D6 +#define NA_SE_PL_SLIP_MAGMA_LEVEL 0x8D7 +#define NA_SE_PL_SLIP_GRASS_LEVEL 0x8D8 +#define NA_SE_PL_SLIP_IRON_LEVEL 0x8D9 +#define NA_SE_PL_SLIP_GLASS_LEVEL 0x8DA +#define NA_SE_PL_SLIP_WOOD_LEVEL 0x8DB +#define NA_SE_PL_DUMMY_220 0x8DC +#define NA_SE_PL_DUMMY_221 0x8DD +#define NA_SE_PL_SLIP_HEAVYBOOTS_LEVEL 0x8DE +#define NA_SE_PL_SLIP_ICE_LEVEL 0x8DF +#define NA_SE_PL_JUMP_METAL 0x8E0 +#define NA_SE_PL_LAND_METAL 0x8E1 +#define NA_SE_PL_WALK_RUNNINGMAN 0x8E2 +#define NA_SE_PL_WALK_ZELDA_DEMO 0x8E3 +#define NA_SE_PL_YOBI_DATA02 0x8E4 +#define NA_SE_PL_YOBI_DATA03 0x8E5 +#define NA_SE_PL_YOBI_DATA04 0x8E6 +#define NA_SE_PL_YOBI_DATA05 0x8E7 +#define NA_SE_PL_YOBI_DATA06 0x8E8 +#define NA_SE_PL_YOBI_DATA07 0x8E9 +#define NA_SE_PL_YOBI_DATA08 0x8EA +#define NA_SE_PL_YOBI_DATA09 0x8EB +#define NA_SE_PL_YOBI_DATA10 0x8EC +#define NA_SE_PL_YOBI_DATA11 0x8ED +#define NA_SE_PL_YOBI_DATA12 0x8EE +#define NA_SE_PL_YOBI_DATA13 0x8EF +#define NA_SE_PL_YOBI_DATA14 0x8F0 + +// ------------ ITEM ------------ + +#define NA_SE_IT_SWORD_IMPACT 0x1800 +#define NA_SE_IT_SWORD_SWING 0x1801 +#define NA_SE_IT_SWORD_PUTAWAY 0x1802 +#define NA_SE_IT_SWORD_PICKOUT 0x1803 +#define NA_SE_IT_ARROW_SHOT 0x1804 +#define NA_SE_IT_BOOMERANG_THROW 0x1805 +#define NA_SE_IT_SHIELD_BOUND 0x1806 +#define NA_SE_IT_BOW_DRAW 0x1807 +#define NA_SE_IT_SHIELD_REFLECT_SW 0x1808 +#define NA_SE_IT_ARROW_STICK_HRAD 0x1809 +#define NA_SE_IT_HAMMER_HIT 0x180A +#define NA_SE_IT_HOOKSHOT_CHAIN 0x180B +#define NA_SE_IT_SHIELD_REFLECT_MG 0x180C +#define NA_SE_IT_BOMB_IGNIT 0x180D +#define NA_SE_IT_BOMB_EXPLOSION 0x180E +#define NA_SE_IT_BOMB_UNEXPLOSION 0x180F +#define NA_SE_IT_BOOMERANG_FLY 0x1810 +#define NA_SE_IT_SWORD_STRIKE 0x1811 +#define NA_SE_IT_HAMMER_SWING 0x1812 +#define NA_SE_IT_HOOKSHOT_REFLECT 0x1813 +#define NA_SE_IT_ARROW_STICK_CRE 0x1814 +#define NA_SE_IT_ARROW_STICK_OBJ 0x1815 +#define NA_SE_IT_DUMMY 0x1816 +#define NA_SE_IT_DUMMY2 0x1817 +#define NA_SE_IT_SWORD_SWING_HARD 0x1818 +#define NA_SE_IT_DUMMY3 0x1819 +#define NA_SE_IT_WALL_HIT_HARD 0x181A +#define NA_SE_IT_WALL_HIT_SOFT 0x181B +#define NA_SE_IT_STONE_HIT 0x181C +#define NA_SE_IT_WOODSTICK_BROKEN 0x181D +#define NA_SE_IT_LASH 0x181E +#define NA_SE_IT_SHIELD_POSTURE 0x181F +#define NA_SE_IT_SLING_SHOT 0x1820 +#define NA_SE_IT_SLING_DRAW 0x1821 +#define NA_SE_IT_SWORD_CHARGE 0x1822 +#define NA_SE_IT_ROLLING_CUT 0x1823 +#define NA_SE_IT_SWORD_STRIKE_HARD 0x1824 +#define NA_SE_IT_SLING_REFLECT 0x1825 +#define NA_SE_IT_SHIELD_REMOVE 0x1826 +#define NA_SE_IT_HOOKSHOT_READY 0x1827 +#define NA_SE_IT_HOOKSHOT_RECEIVE 0x1828 +#define NA_SE_IT_HOOKSHOT_STICK_OBJ 0x1829 +#define NA_SE_IT_SWORD_REFLECT_MG 0x182A +#define NA_SE_IT_DEKU 0x182B +#define NA_SE_IT_WALL_HIT_BUYO 0x182C +#define NA_SE_IT_SWORD_PUTAWAY_STN 0x182D +#define NA_SE_IT_ROLLING_CUT_LV1 0x182E +#define NA_SE_IT_ROLLING_CUT_LV2 0x182F +#define NA_SE_IT_BOW_FLICK 0x1830 +#define NA_SE_IT_BOMBCHU_MOVE 0x1831 +#define NA_SE_IT_SHIELD_CHARGE_LV1 0x1832 +#define NA_SE_IT_SHIELD_CHARGE_LV2 0x1833 +#define NA_SE_IT_SHIELD_CHARGE_LV3 0x1834 +#define NA_SE_IT_SLING_FLICK 0x1835 +#define NA_SE_IT_SWORD_STICK_STN 0x1836 +#define NA_SE_IT_REFLECTION_WOOD 0x1837 +#define NA_SE_IT_SHIELD_REFLECT_MG2 0x1838 +#define NA_SE_IT_MAGIC_ARROW_SHOT 0x1839 +#define NA_SE_IT_EXPLOSION_FRAME 0x183A +#define NA_SE_IT_EXPLOSION_ICE 0x183B +#define NA_SE_IT_EXPLOSION_LIGHT 0x183C +#define NA_SE_IT_FISHING_REEL_SLOW 0x183D +#define NA_SE_IT_FISHING_REEL_HIGH 0x183E +#define NA_SE_IT_PULL_FISHING_ROD 0x183F +#define NA_SE_IT_DM_FLYING_GOD_PASS 0x1840 +#define NA_SE_IT_DM_FLYING_GOD_DASH 0x1841 +#define NA_SE_IT_DM_RING_EXPLOSION 0x1842 +#define NA_SE_IT_DM_RING_GATHER 0x1843 +#define NA_SE_IT_INGO_HORSE_NEIGH 0x1844 +#define NA_SE_IT_EARTHQUAKE 0x1845 +#define NA_SE_IT_DUMMY4 0x1846 +#define NA_SE_IT_KAKASHI_JUMP 0x1847 +#define NA_SE_IT_FLAME 0x1848 +#define NA_SE_IT_SHIELD_BEAM 0x1849 +#define NA_SE_IT_FISHING_HIT 0x184A +#define NA_SE_IT_GOODS_APPEAR 0x184B +#define NA_SE_IT_MAJIN_SWORD_BROKEN 0x184C +#define NA_SE_IT_HAND_CLAP 0x184D +#define NA_SE_IT_MASTER_SWORD_SWING 0x184E +#define NA_SE_IT_DUMMY5 0x184F +#define NA_SE_IT_YOBI19 0x1850 +#define NA_SE_FISHING_REEL_SLOW2 0x1851 // not IT? +#define NA_SE_IT_SPIDERNET_HIT1 0x1852 +#define NA_SE_IT_LURE_LAND1 0x1853 +#define NA_SE_IT_HOOKSHOT_STICK_OBJ_WATER 0x1854 +#define NA_SE_IT_SWORD_PICKOUT_GANON 0x1855 +#define NA_SE_IT_BOMB_IGNIT_DODO_M 0x1856 +#define NA_SE_IT_YOBI05 0x1857 +#define NA_SE_IT_YOBI06 0x1858 +#define NA_SE_IT_YOBI07 0x1859 +#define NA_SE_IT_YOBI08 0x185A +#define NA_SE_IT_YOBI09 0x185B +#define NA_SE_IT_YOBI10 0x185C +#define NA_SE_IT_YOBI11 0x185D +#define NA_SE_IT_YOBI12 0x185E +#define NA_SE_IT_YOBI13 0x185F +#define NA_SE_IT_YOBI14 0x1860 +#define NA_SE_IT_YOBI15 0x1861 +#define NA_SE_IT_YOBI16 0x1862 +#define NA_SE_IT_YOBI17 0x1863 +#define NA_SE_IT_YOBI18 0x1864 + +// ------------ ENVIRONMENT ------------ + +#define NA_SE_EV_DOOR_OPEN 0x2800 +#define NA_SE_EV_DOOR_CLOSE 0x2801 +#define NA_SE_EV_EXPLOSION 0x2802 +#define NA_SE_EV_HORSE_WALK 0x2803 +#define NA_SE_EV_HORSE_RUN 0x2804 +#define NA_SE_EV_HORSE_NEIGH 0x2805 +#define NA_SE_EV_RIVER_STREAM 0x2806 +#define NA_SE_EV_WATER_WALL_BIG 0x2807 +#define NA_SE_EV_OUT_OF_WATER 0x2808 +#define NA_SE_EV_DIVE_WATER 0x2809 +#define NA_SE_EV_ROCK_SLIDE 0x280A +#define NA_SE_EV_MAGMA_LEVEL 0x280B +#define NA_SE_EV_BRIDGE_OPEN 0x280C +#define NA_SE_EV_BRIDGE_CLOSE 0x280D +#define NA_SE_EV_BRIDGE_OPEN_STOP 0x280E +#define NA_SE_EV_BRIDGE_CLOSE_STOP 0x280F +#define NA_SE_EV_WALL_BROKEN 0x2810 +#define NA_SE_EV_CHICKEN_CRY_N 0x2811 +#define NA_SE_EV_CHICKEN_CRY_A 0x2812 +#define NA_SE_EV_CHICKEN_CRY_M 0x2813 +#define NA_SE_EV_SLIDE_DOOR_OPEN 0x2814 +#define NA_SE_EV_FOOT_SWITCH 0x2815 +#define NA_SE_EV_HORSE_GROAN 0x2816 +#define NA_SE_EV_BOMB_DROP_WATER 0x2817 +#define NA_SE_EV_HORSE_JUMP 0x2818 +#define NA_SE_EV_HORSE_LAND 0x2819 +#define NA_SE_EV_HORSE_SLIP 0x281A +#define NA_SE_EV_FAIRY_DASH 0x281B +#define NA_SE_EV_SLIDE_DOOR_CLOSE 0x281C +#define NA_SE_EV_STONE_BOUND 0x281D +#define NA_SE_EV_STONE_STATUE_OPEN 0x281E +#define NA_SE_EV_TBOX_UNLOCK 0x281F +#define NA_SE_EV_TBOX_OPEN 0x2820 +#define NA_SE_SY_TIMER 0x2821 // SY sound inside EV? +#define NA_SE_EV_FLAME_IGNITION 0x2822 +#define NA_SE_EV_SPEAR_HIT 0x2823 +#define NA_SE_EV_ELEVATOR_MOVE 0x2824 +#define NA_SE_EV_WARP_HOLE 0x2825 +#define NA_SE_EV_LINK_WARP 0x2826 +#define NA_SE_EV_PILLAR_SINK 0x2827 +#define NA_SE_EV_WATER_WALL 0x2828 +#define NA_SE_EV_RIVER_STREAM_S 0x2829 +#define NA_SE_EV_RIVER_STREAM_F 0x282A +#define NA_SE_EV_HORSE_LAND2 0x282B +#define NA_SE_EV_HORSE_SANDDUST 0x282C +#define NA_SE_EV_DUMMY45 0x282D +#define NA_SE_EV_LIGHTNING 0x282E +#define NA_SE_EV_BOMB_BOUND 0x282F +#define NA_SE_EV_WATERDROP 0x2830 +#define NA_SE_EV_TORCH 0x2831 +#define NA_SE_EV_MAGMA_LEVEL_M 0x2832 +#define NA_SE_EV_FIRE_PILLAR 0x2833 +#define NA_SE_EV_FIRE_PLATE 0x2834 +#define NA_SE_EV_BLOCK_BOUND 0x2835 +#define NA_SE_EV_METALDOOR_SLIDE 0x2836 +#define NA_SE_EV_METALDOOR_STOP 0x2837 +#define NA_SE_EV_BLOCK_SHAKE 0x2838 +#define NA_SE_EV_BOX_BREAK 0x2839 +#define NA_SE_EV_HAMMER_SWITCH 0x283A +#define NA_SE_EV_MAGMA_LEVEL_L 0x283B +#define NA_SE_EV_SPEAR_FENCE 0x283C +#define NA_SE_EV_GANON_HORSE_NEIGH 0x283D +#define NA_SE_EV_GANON_HORSE_GROAN 0x283E +#define NA_SE_EV_FANTOM_WARP_S 0x283F +#define NA_SE_EV_FANTOM_WARP_L 0x2840 +#define NA_SE_EV_FOUNTAIN 0x2841 +#define NA_SE_EV_KID_HORSE_WALK 0x2842 +#define NA_SE_EV_KID_HORSE_RUN 0x2843 +#define NA_SE_EV_KID_HORSE_NEIGH 0x2844 +#define NA_SE_EV_KID_HORSE_GROAN 0x2845 +#define NA_SE_EV_WHITE_OUT 0x2846 +#define NA_SE_EV_LIGHT_GATHER 0x2847 +#define NA_SE_EV_TREE_CUT 0x2848 +#define NA_SE_EV_VOLCANO 0x2849 +#define NA_SE_EV_GUILLOTINE_UP 0x284A +#define NA_SE_EV_GUILLOTINE_BOUND 0x284B +#define NA_SE_EV_ROLLCUTTER_MOTOR 0x284C +#define NA_SE_EV_CHINETRAP_DOWN 0x284D +#define NA_SE_EV_PLANT_BROKEN 0x284E +#define NA_SE_EV_SHIP_BELL 0x284F +#define NA_SE_EV_FLUTTER_FLAG 0x2850 +#define NA_SE_EV_TRAP_BOUND 0x2851 +#define NA_SE_EV_ROCK_BROKEN 0x2852 +#define NA_SE_EV_FANTOM_WARP_S2 0x2853 +#define NA_SE_EV_FANTOM_WARP_L2 0x2854 +#define NA_SE_EV_COFFIN_CAP_OPEN 0x2855 +#define NA_SE_EV_COFFIN_CAP_BOUND 0x2856 +#define NA_SE_EV_WIND_TRAP 0x2857 +#define NA_SE_EV_TRAP_OBJ_SLIDE 0x2858 +#define NA_SE_EV_METALDOOR_OPEN 0x2859 +#define NA_SE_EV_METALDOOR_CLOSE 0x285A +#define NA_SE_EV_BURN_OUT 0x285B +#define NA_SE_EV_BLOCKSINK 0x285C +#define NA_SE_EV_CROWD 0x285D +#define NA_SE_EV_WATER_LEVEL_DOWN 0x285E +#define NA_SE_EV_NAVY_VANISH 0x285F +#define NA_SE_EV_LADDER_DOUND 0x2860 +#define NA_SE_EV_WEB_VIBRATION 0x2861 +#define NA_SE_EV_WEB_BROKEN 0x2862 +#define NA_SE_EV_ROLL_STAND 0x2863 +#define NA_SE_EV_BUYODOOR_OPEN 0x2864 +#define NA_SE_EV_BUYODOOR_CLOSE 0x2865 +#define NA_SE_EV_WOODDOOR_OPEN 0x2866 +#define NA_SE_EV_METALGATE_OPEN 0x2867 +#define NA_SE_IT_SCOOP_UP_WATER 0x2868 +#define NA_SE_EV_FISH_LEAP 0x2869 +#define NA_SE_EV_KAKASHI_SWING 0x286A +#define NA_SE_EV_KAKASHI_ROLL 0x286B +#define NA_SE_EV_BOTTLE_CAP_OPEN 0x286C +#define NA_SE_EV_JABJAB_BREATHE 0x286D +#define NA_SE_EV_SPIRIT_STONE 0x286E +#define NA_SE_EV_TRIFORCE_FLASH 0x286F +#define NA_SE_EV_FALL_DOWN_DIRT 0x2870 +#define NA_SE_EV_NAVY_FLY 0x2871 +#define NA_SE_EV_NAVY_CRASH 0x2872 +#define NA_SE_EV_WOOD_HIT 0x2873 +#define NA_SE_EV_SCOOPUP_WATER 0x2874 +#define NA_SE_EV_DROP_FALL 0x2875 +#define NA_SE_EV_WOOD_GEAR 0x2876 +#define NA_SE_EV_TREE_SWING 0x2877 +#define NA_SE_EV_HORSE_RUN_LEVEL 0x2878 +#define NA_SE_EV_ELEVATOR_MOVE2 0x2879 +#define NA_SE_EV_ELEVATOR_STOP 0x287A +#define NA_SE_EV_TRE_BOX_APPEAR 0x287B +#define NA_SE_EV_CHAIN_KEY_UNLOCK 0x287C +#define NA_SE_EV_SPINE_TRAP_MOVE 0x287D +#define NA_SE_EV_HEALING 0x287E +#define NA_SE_EV_GREAT_FAIRY_APPEAR 0x287F +#define NA_SE_EV_GREAT_FAIRY_VANISH 0x2880 +#define NA_SE_EV_RED_EYE 0x2881 +#define NA_SE_EV_ROLL_STAND_2 0x2882 +#define NA_SE_EV_WALL_SLIDE 0x2883 +#define NA_SE_EV_TRE_BOX_FLASH 0x2884 +#define NA_SE_EV_WINDMILL_LEVEL 0x2885 +#define NA_SE_EV_GOTO_HEAVEN 0x2886 +#define NA_SE_EV_POT_BROKEN 0x2887 +#define NA_SE_PL_PUT_DOWN_POT 0x2888 // PL sound inside EV? +#define NA_SE_EV_DIVE_INTO_WATER 0x2889 +#define NA_SE_EV_JUMP_OUT_WATER 0x288A +#define NA_SE_EV_GOD_FLYING 0x288B +#define NA_SE_EV_TRIFORCE 0x288C +#define NA_SE_EV_AURORA 0x288D +#define NA_SE_EV_DEKU_DEATH 0x288E +#define NA_SE_EV_BUYOSTAND_RISING 0x288F +#define NA_SE_EV_BUYOSTAND_FALL 0x2890 +#define NA_SE_EV_BUYOSHUTTER_OPEN 0x2891 +#define NA_SE_EV_BUYOSHUTTER_CLOSE 0x2892 +#define NA_SE_EV_STONEDOOR_STOP 0x2893 +#define NA_SE_EV_S_STONE_REVIVAL 0x2894 +#define NA_SE_EV_MEDAL_APPEAR_S 0x2895 +#define NA_SE_EV_HUMAN_BOUND 0x2896 +#define NA_SE_EV_MEDAL_APPEAR_L 0x2897 +#define NA_SE_EV_EARTHQUAKE 0x2898 +#define NA_SE_EV_SHUT_BY_CRYSTAL 0x2899 +#define NA_SE_EV_GOD_LIGHTBALL_2 0x289A +#define NA_SE_EV_RUN_AROUND 0x289B +#define NA_SE_EV_CONSENTRATION 0x289C +#define NA_SE_EV_TIMETRIP_LIGHT 0x289D +#define NA_SE_EV_BUYOSTAND_STOP_A 0x289E +#define NA_SE_EV_BUYOSTAND_STOP_U 0x289F +#define NA_SE_EV_OBJECT_FALL 0x28A0 +#define NA_SE_EV_JUMP_CONC 0x28A1 +#define NA_SE_EV_ICE_MELT 0x28A2 +#define NA_SE_EV_FIRE_PILLAR_S 0x28A3 +#define NA_SE_EV_BLOCK_RISING 0x28A4 +#define NA_SE_EV_NABALL_VANISH 0x28A5 +#define NA_SE_EV_SARIA_MELODY 0x28A6 +#define NA_SE_EV_LINK_WARP_OUT 0x28A7 +#define NA_SE_EV_FIATY_HEAL 0x28A8 +#define NA_SE_EV_CHAIN_KEY_UNLOCK_B 0x28A9 +#define NA_SE_EV_WOODBOX_BREAK 0x28AA +#define NA_SE_EV_PUT_DOWN_WOODBOX 0x28AB +#define NA_SE_EV_LAND_DIRT 0x28AC +#define NA_SE_EV_FLOOR_ROLLING 0x28AD +#define NA_SE_EV_DOG_CRY_EVENING 0x28AE +#define NA_SE_EV_JABJAB_HICCUP 0x28AF +#define NA_SE_EV_NALE_MAGIC 0x28B0 +#define NA_SE_EV_FROG_JUMP 0x28B1 +#define NA_SE_EV_ICE_FREEZE 0x28B2 +#define NA_SE_EV_BURNING 0x28B3 +#define NA_SE_EV_WOODPLATE_BOUND 0x28B4 +#define NA_SE_EV_GORON_WATER_DROP 0x28B5 +#define NA_SE_EV_JABJAB_GROAN 0x28B6 +#define NA_SE_EV_DARUMA_VANISH 0x28B7 +#define NA_SE_EV_BIGBALL_ROLL 0x28B8 +#define NA_SE_EV_ELEVATOR_MOVE3 0x28B9 +#define NA_SE_EV_DIAMOND_SWITCH 0x28BA +#define NA_SE_EV_FLAME_OF_FIRE 0x28BB +#define NA_SE_EV_RAINBOW_SHOWER 0x28BC +#define NA_SE_EV_FLYING_AIR 0x28BD +#define NA_SE_EV_PASS_AIR 0x28BE +#define NA_SE_EV_COME_UP_DEKU_JR 0x28BF +#define NA_SE_EV_SAND_STORM 0x28C0 +#define NA_SE_EV_TRIFORCE_MARK 0x28C1 +#define NA_SE_EV_GRAVE_EXPLOSION 0x28C2 +#define NA_SE_EV_LURE_MOVE_W 0x28C3 +#define NA_SE_EV_POT_MOVE_START 0x28C4 +#define NA_SE_EV_DIVE_INTO_WATER_L 0x28C5 +#define NA_SE_EV_OUT_OF_WATER_L 0x28C6 +#define NA_SE_EV_GANON_MANTLE 0x28C7 +#define NA_SE_EV_DIG_UP 0x28C8 +#define NA_SE_EV_WOOD_BOUND 0x28C9 +#define NA_SE_EV_WATER_BUBBLE 0x28CA +#define NA_SE_EV_ICE_BROKEN 0x28CB +#define NA_SE_EV_FROG_GROW_UP 0x28CC +#define NA_SE_EV_WATER_CONVECTION 0x28CD +#define NA_SE_EV_GROUND_GATE_OPEN 0x28CE +#define NA_SE_EV_FACE_BREAKDOWN 0x28CF +#define NA_SE_EV_FACE_EXPLOSION 0x28D0 +#define NA_SE_EV_FACE_CRUMBLE_SLOW 0x28D1 +#define NA_SE_EV_ROUND_TRAP_MOVE 0x28D2 +#define NA_SE_EV_HIT_SOUND 0x28D3 +#define NA_SE_EV_ICE_SWING 0x28D4 +#define NA_SE_EV_DOWN_TO_GROUND 0x28D5 +#define NA_SE_EV_KENJA_ENVIROMENT_0 0x28D6 +#define NA_SE_EV_KENJA_ENVIROMENT_1 0x28D7 +#define NA_SE_EV_SMALL_DOG_BARK 0x28D8 +#define NA_SE_EV_ZELDA_POWER 0x28D9 +#define NA_SE_EV_RAIN 0x28DA +#define NA_SE_EV_IRON_DOOR_OPEN 0x28DB +#define NA_SE_EV_IRON_DOOR_CLOSE 0x28DC +#define NA_SE_EV_WHIRLPOOL 0x28DD +#define NA_SE_EV_TOWER_PARTS_BROKEN 0x28DE +#define NA_SE_EV_COW_CRY 0x28DF +#define NA_SE_EV_METAL_BOX_BOUND 0x28E0 +#define NA_SE_EV_ELECTRIC_EXPLOSION 0x28E1 +#define NA_SE_EV_HEAVY_THROW 0x28E2 +#define NA_SE_EV_FROG_CRY_0 0x28E3 +#define NA_SE_EV_FROG_CRY_1 0x28E4 +#define NA_SE_EV_COW_CRY_LV 0x28E5 +#define NA_SE_EV_RONRON_DOOR_CLOSE 0x28E6 +#define NA_SE_EV_BUTTERFRY_TO_FAIRY 0x28E7 +#define NA_SE_EV_FIVE_COUNT_LUPY 0x28E8 +#define NA_SE_EV_STONE_GROW_UP 0x28E9 +#define NA_SE_EV_STONE_LAUNCH 0x28EA +#define NA_SE_EV_STONE_ROLLING 0x28EB +#define NA_SE_EV_TOGE_STICK_ROLLING 0x28EC +#define NA_SE_EV_TOWER_ENERGY 0x28ED +#define NA_SE_EV_TOWER_BARRIER 0x28EE +#define NA_SE_EV_CHIBI_WALK 0x28EF +#define NA_SE_EV_KNIGHT_WALK 0x28F0 +#define NA_SE_EV_PILLAR_MOVE_STOP 0x28F1 +#define NA_SE_EV_ERUPTION_CLOUD 0x28F2 +#define NA_SE_EV_LINK_WARP_OUT_LV 0x28F3 +#define NA_SE_EV_LINK_WARP_IN 0x28F4 +#define NA_SE_EV_OCARINA_BMELO_0 0x28F5 +#define NA_SE_EV_OCARINA_BMELO_1 0x28F6 +#define NA_SE_EV_EXPLOSION_FOR_RENZOKU 0x28F7 +#define NA_SE_EV_ELEVATOR_MOVE_KABE1 0x28F8 +#define NA_SE_EV_RIVER_STREAM_F_IDO 0x28F9 +#define NA_SE_EV_GUILLOTINE_BOUND_copyOrigin 0x28FA +#define NA_SE_EV_HEALING_TOU 0x28FB +#define NA_SE_EV_RUMUBLE_KEMURI 0x28FC +#define NA_SE_EV_GANON_HADOU 0x28FD +#define NA_SE_EV_KANOKE_OPEN 0x28FE +#define NA_SE_EV_KANOKE_CLOSE 0x28FF +#define NA_SE_EV_SEEK_CLOTH1 0x2900 +#define NA_SE_EV_SEEK_CLOTH2 0x2901 +#define NA_SE_EV_BOTTLE_CAP_CLOSE 0x2902 +#define NA_SE_EV_ELEVATOR_MOVE_KABE2 0x2903 +#define NA_SE_EV_WATER_LEVEL_DOWN_STOP 0x2904 +#define NA_SE_EV_DAIKU_CLOTH1 0x2905 +#define NA_SE_EV_DEMO_EPONA_LAND 0x2906 +#define NA_SE_EV_DIVE_INTO_WATER_BLOCK 0x2907 +#define NA_SE_EV_TORCH2 0x2908 +#define NA_SE_EV_TORCH3 0x2909 +#define NA_SE_EV_TORCH4 0x290A +#define NA_SE_EV_TORCH5 0x290B +#define NA_SE_EV_EARTHQUAKE_LAST 0x290C +#define NA_SE_EV_YAMI_TRAP_CHAIN 0x290D +#define NA_SE_EV_FLAME_IGNITION_GANON 0x290E +#define NA_SE_EV_MGANON_DOWN2 0x290F +#define NA_SE_EV_EXPLOSION_HOUKAI 0x2910 +#define NA_SE_EV_BLOCKSINK_GANON 0x2911 +#define NA_SE_EV_DOG_WALK 0x2912 +#define NA_SE_EV_GANON_HOUKAI_KEMURI1 0x2913 +#define NA_SE_EV_YOBI21 0x2914 +#define NA_SE_EV_YOBI22 0x2915 +#define NA_SE_EV_YOBI23 0x2916 +#define NA_SE_EV_YOBI24 0x2917 +#define NA_SE_EV_YOBI25 0x2918 + +// ------------ ENEMY ------------ + +#define NA_SE_EN_FLOORMASTER_SLIDING 0x3034 +#define NA_SE_EN_FLOORMASTER_SM_STICK 0x3136 +#define NA_SE_EN_DODO_J_WALK 0x3800 +#define NA_SE_EN_DODO_J_CRY 0x3801 +#define NA_SE_EN_DODO_J_FIRE 0x3802 +#define NA_SE_EN_DODO_J_DAMAGE 0x3803 +#define NA_SE_EN_DODO_J_DEAD 0x3804 +#define NA_SE_EN_DODO_K_CRY 0x3805 +#define NA_SE_EN_DODO_K_DAMAGE 0x3806 +#define NA_SE_EN_DODO_K_DEAD 0x3807 +#define NA_SE_EN_DODO_K_WALK 0x3808 +#define NA_SE_EN_DODO_K_FIRE 0x3809 +#define NA_SE_EN_GOMA_WALK 0x380A +#define NA_SE_EN_GOMA_HIGH 0x380B +#define NA_SE_EN_GOMA_CLIM 0x380C +#define NA_SE_EN_GOMA_DOWN 0x380D +#define NA_SE_EN_GOMA_CRY1 0x380E +#define NA_SE_EN_GOMA_CRY2 0x380F +#define NA_SE_EN_GOMA_DAM1 0x3810 +#define NA_SE_EN_GOMA_DAM2 0x3811 +#define NA_SE_EN_GOMA_DEAD 0x3812 +#define NA_SE_EN_GOMA_UNARI 0x3813 +#define NA_SE_EN_GOMA_BJR_EGG1 0x3814 +#define NA_SE_EN_GOMA_BJR_EGG2 0x3815 +#define NA_SE_EN_GOMA_BJR_WALK 0x3816 +#define NA_SE_EN_GOMA_BJR_CRY 0x3817 +#define NA_SE_EN_GOMA_BJR_DAM1 0x3818 +#define NA_SE_EN_GOMA_BJR_DAM2 0x3819 +#define NA_SE_EN_GOMA_BJR_DEAD 0x381A +#define NA_SE_EN_GOMA_DEMO_EYE 0x381B +#define NA_SE_EN_GOMA_LAST 0x381C +#define NA_SE_EN_GOMA_UNARI2 0x381D +#define NA_SE_EN_GOMA_FAINT 0x381E +#define NA_SE_EN_GOMA_BJR_FREEZE 0x381F +#define NA_SE_EN_DODO_M_CRY 0x3820 +#define NA_SE_EN_DODO_M_DEAD 0x3821 +#define NA_SE_EN_DODO_M_MOVE 0x3822 +#define NA_SE_EN_DODO_M_DOWN 0x3823 +#define NA_SE_EN_DODO_M_UP 0x3824 +#define NA_SE_EN_GANON_THROW_MASIC 0x3825 +#define NA_SE_EN_DODO_M_EAT 0x3826 +#define NA_SE_EN_GANON_DD_THUNDER 0x3827 +#define NA_SE_EN_RIZA_ONGND 0x3828 +#define NA_SE_EN_RIZA_CRY 0x3829 +#define NA_SE_EN_RIZA_ATTACK 0x382A +#define NA_SE_EN_RIZA_DAMAGE 0x382B +#define NA_SE_EN_RIZA_WARAU 0x382C +#define NA_SE_EN_RIZA_DEAD 0x382D +#define NA_SE_EN_RIZA_WALK 0x382E +#define NA_SE_EN_RIZA_JUMP 0x382F +#define NA_SE_EN_STALKID_WALK 0x3830 +#define NA_SE_EN_STALKID_ATTACK 0x3831 +#define NA_SE_EN_STALKID_DAMAGE 0x3832 +#define NA_SE_EN_STALKID_DEAD 0x3833 +#define NA_SE_EN_TEKU_WALK_WATER 0x3835 +#define NA_SE_EN_LIGHT_ARROW_HIT 0x3836 +#define NA_SE_EN_TUBOOCK_FLY 0x3837 +#define NA_SE_EN_STAL_WARAU 0x3838 +#define NA_SE_EN_STAL_SAKEBI 0x3839 +#define NA_SE_EN_STAL_DAMAGE 0x383A +#define NA_SE_EN_STAL_DEAD 0x383B +#define NA_SE_EN_WOLFOS_APPEAR 0x383C +#define NA_SE_EN_STAL_WALK 0x383D +#define NA_SE_EN_WOLFOS_CRY 0x383E +#define NA_SE_EN_WOLFOS_ATTACK 0x383F +#define NA_SE_EN_FFLY_ATTACK 0x3840 +#define NA_SE_EN_FFLY_FLY 0x3841 +#define NA_SE_EN_FFLY_DEAD 0x3842 +#define NA_SE_EN_WOLFOS_DAMAGE 0x3843 +#define NA_SE_EN_AMOS_WALK 0x3844 +#define NA_SE_EN_AMOS_WAVE 0x3845 +#define NA_SE_EN_AMOS_DEAD 0x3846 +#define NA_SE_EN_AMOS_DAMAGE 0x3847 +#define NA_SE_EN_AMOS_VOICE 0x3848 +#define NA_SE_EN_SHELL_MOUTH 0x3849 +#define NA_SE_EN_SHELL_DEAD 0x384A +#define NA_SE_EN_WOLFOS_DEAD 0x384B +#define NA_SE_EN_DODO_K_COLI 0x384C +#define NA_SE_EN_DODO_K_COLI2 0x384D +#define NA_SE_EN_DODO_K_ROLL 0x384E +#define NA_SE_EN_DODO_K_BREATH 0x384F +#define NA_SE_EN_DODO_K_DRINK 0x3850 +#define NA_SE_EN_DODO_K_DOWN 0x3851 +#define NA_SE_EN_DODO_K_OTAKEBI 0x3852 +#define NA_SE_EN_DODO_K_END 0x3853 +#define NA_SE_EN_DODO_K_LAST 0x3854 +#define NA_SE_EN_DODO_K_LAVA 0x3855 +#define NA_SE_EN_GANON_FLOAT 0x3856 +#define NA_SE_EN_GANON_DARKWAVE_M 0x3857 +#define NA_SE_EN_DODO_J_BREATH 0x3858 +#define NA_SE_EN_DODO_J_TAIL 0x3859 +#define NA_SE_EN_WOLFOS_WALK 0x385A +#define NA_SE_EN_DODO_J_EAT 0x385B +#define NA_SE_EN_DEKU_MOUTH 0x385C +#define NA_SE_EN_DEKU_ATTACK 0x385D +#define NA_SE_EN_DEKU_DAMAGE 0x385E +#define NA_SE_EN_DEKU_DEAD 0x385F +#define NA_SE_EN_DEKU_JR_MOUTH 0x3860 +#define NA_SE_EN_DEKU_JR_ATTACK 0x3861 +#define NA_SE_EN_DEKU_JR_DEAD 0x3862 +#define NA_SE_EN_DEKU_SCRAPE 0x3863 +#define NA_SE_EN_TAIL_FLY 0x3864 +#define NA_SE_EN_TAIL_CRY 0x3865 +#define NA_SE_EN_TAIL_DEAD 0x3866 +#define NA_SE_EN_GANON_SPARK 0x3867 +#define NA_SE_EN_STALTU_DOWN 0x3868 +#define NA_SE_EN_STALTU_UP 0x3869 +#define NA_SE_EN_STALTU_LAUGH 0x386A +#define NA_SE_EN_STALTU_DAMAGE 0x386B +#define NA_SE_EN_STAL_JUMP 0x386C +#define NA_SE_EN_TEKU_DAMAGE 0x386D +#define NA_SE_EN_TEKU_DEAD 0x386E +#define NA_SE_EN_TEKU_WALK 0x386F +#define NA_SE_EN_PO_KANTERA 0x3870 +#define NA_SE_EN_PO_FLY 0x3871 +#define NA_SE_EN_PO_AWAY 0x3872 +#define NA_SE_EN_PO_APPEAR 0x3873 +#define NA_SE_EN_PO_DISAPPEAR 0x3874 +#define NA_SE_EN_PO_DAMAGE 0x3875 +#define NA_SE_EN_PO_DEAD 0x3876 +#define NA_SE_EN_PO_DEAD2 0x3877 +#define NA_SE_EN_EXTINCT 0x3878 +#define NA_SE_EN_GOLON_LAND_BIG 0x3879 +#define NA_SE_EN_RIZA_DOWN 0x387A +#define NA_SE_EN_DODO_M_GND 0x387B +#define NA_SE_EN_NUTS_UP 0x387C +#define NA_SE_EN_NUTS_DOWN 0x387D +#define NA_SE_EN_NUTS_THROW 0x387E +#define NA_SE_EN_NUTS_WALK 0x387F +#define NA_SE_EN_NUTS_DAMAGE 0x3880 +#define NA_SE_EN_NUTS_DEAD 0x3881 +#define NA_SE_EN_NUTS_FAINT 0x3882 +#define NA_SE_EN_PO_BIG_GET 0x3883 +#define NA_SE_EN_STALTU_ROLL 0x3884 +#define NA_SE_EN_STALWALL_DEAD 0x3885 +#define NA_SE_EN_PO_SISTER_DEAD 0x3886 +#define NA_SE_EN_BARI_SPLIT 0x3887 +#define NA_SE_EN_TEKU_REVERSE 0x3888 +#define NA_SE_EN_VALVAISA_LAND2 0x3889 +#define NA_SE_EN_TEKU_LAND_WATER 0x388A +#define NA_SE_EN_LAST_DAMAGE 0x388B +#define NA_SE_EN_STALWALL_ROLL 0x388C +#define NA_SE_EN_STALWALL_DASH 0x388D +#define NA_SE_EN_TEKU_JUMP_WATER 0x388E +#define NA_SE_EN_TEKU_LAND_WATER2 0x388F +#define NA_SE_EN_FALL_AIM 0x3890 +#define NA_SE_EN_FALL_UP 0x3891 +#define NA_SE_EN_FALL_CATCH 0x3892 +#define NA_SE_EN_FALL_LAND 0x3893 +#define NA_SE_EN_FALL_WALK 0x3894 +#define NA_SE_EN_FALL_DAMAGE 0x3895 +#define NA_SE_EN_FALL_DEAD 0x3896 +#define NA_SE_EN_KAICHO_FLUTTER 0x3897 +#define NA_SE_EN_BIRI_FLY 0x3898 +#define NA_SE_EN_BIRI_JUMP 0x3899 +#define NA_SE_EN_BIRI_SPARK 0x389A +#define NA_SE_EN_BIRI_DEAD 0x389B +#define NA_SE_EN_BIRI_BUBLE 0x389C +#define NA_SE_EN_BARI_ROLL 0x389D +#define NA_SE_EN_GOMA_JR_FREEZE 0x389E +#define NA_SE_EN_BARI_DEAD 0x389F +#define NA_SE_EN_GANON_FIRE 0x38A0 +#define NA_SE_EN_FANTOM_TRANSFORM 0x38A1 +#define NA_SE_EN_FANTOM_THUNDER 0x38A2 +#define NA_SE_EN_FANTOM_SPARK 0x38A3 +#define NA_SE_EN_FANTOM_FLOAT 0x38A4 +#define NA_SE_EN_FANTOM_MASIC1 0x38A5 +#define NA_SE_EN_FANTOM_MASIC2 0x38A6 +#define NA_SE_EN_FANTOM_FIRE 0x38A7 +#define NA_SE_EN_FANTOM_HIT_THUNDER 0x38A8 +#define NA_SE_EN_FANTOM_ATTACK 0x38A9 +#define NA_SE_EN_FANTOM_STICK 0x38AA +#define NA_SE_EN_FANTOM_EYE 0x38AB +#define NA_SE_EN_FANTOM_LAST 0x38AC +#define NA_SE_EN_FANTOM_THUNDER_GND 0x38AD +#define NA_SE_EN_FANTOM_DAMAGE 0x38AE +#define NA_SE_EN_FANTOM_DEAD 0x38AF +#define NA_SE_EN_FANTOM_LAUGH 0x38B0 +#define NA_SE_EN_FANTOM_DAMAGE2 0x38B1 +#define NA_SE_EN_FANTOM_VOICE 0x38B2 +#define NA_SE_EN_KAICHO_DAMAGE 0x38B3 +#define NA_SE_EN_GANON_ATTACK_DEMO 0x38B4 +#define NA_SE_EN_GANON_FIRE_DEMO 0x38B5 +#define NA_SE_EN_KAICHO_CRY 0x38B6 +#define NA_SE_EN_KAICHO_ATTACK 0x38B7 +#define NA_SE_EN_MORIBLIN_WALK 0x38B8 +#define NA_SE_EN_MORIBLIN_SLIDE 0x38B9 +#define NA_SE_EN_MORIBLIN_ATTACK 0x38BA +#define NA_SE_EN_MORIBLIN_VOICE 0x38BB +#define NA_SE_EN_MORIBLIN_SPEAR_AT 0x38BC +#define NA_SE_EN_MORIBLIN_SPEAR_NORM 0x38BD +#define NA_SE_EN_MORIBLIN_DEAD 0x38BE +#define NA_SE_EN_MORIBLIN_DASH 0x38BF +#define NA_SE_EN_OCTAROCK_ROCK 0x38C0 +#define NA_SE_EN_OCTAROCK_FLOAT 0x38C1 +#define NA_SE_EN_OCTAROCK_JUMP 0x38C2 +#define NA_SE_EN_OCTAROCK_LAND 0x38C3 +#define NA_SE_EN_OCTAROCK_SINK 0x38C4 +#define NA_SE_EN_OCTAROCK_BUBLE 0x38C5 +#define NA_SE_EN_OCTAROCK_DEAD1 0x38C6 +#define NA_SE_EN_OCTAROCK_DEAD2 0x38C7 +#define NA_SE_EN_BUBLE_WING 0x38C8 +#define NA_SE_EN_BUBLE_MOUTH 0x38C9 +#define NA_SE_EN_BUBLE_LAUGH 0x38CA +#define NA_SE_EN_BUBLE_BITE 0x38CB +#define NA_SE_EN_BUBLE_UP 0x38CC +#define NA_SE_EN_BUBLE_DOWN 0x38CD +#define NA_SE_EN_BUBLE_DEAD 0x38CE +#define NA_SE_EN_BUBLEFALL_FIRE 0x38CF +#define NA_SE_EN_VALVAISA_APPEAR 0x38D0 +#define NA_SE_EN_VALVAISA_ROAR 0x38D1 +#define NA_SE_EN_VALVAISA_MAHI1 0x38D2 +#define NA_SE_EN_VALVAISA_MAHI2 0x38D3 +#define NA_SE_EN_VALVAISA_KNOCKOUT 0x38D4 +#define NA_SE_EN_VALVAISA_DAMAGE1 0x38D5 +#define NA_SE_EN_VALVAISA_DAMAGE2 0x38D6 +#define NA_SE_EN_VALVAISA_ROCK 0x38D7 +#define NA_SE_EN_VALVAISA_SW_NAIL 0x38D8 +#define NA_SE_EN_VALVAISA_DEAD 0x38D9 +#define NA_SE_EN_VALVAISA_BURN 0x38DA +#define NA_SE_EN_VALVAISA_FIRE 0x38DB +#define NA_SE_EN_BARI_DAMAGE 0x38DC +#define NA_SE_EN_MOFER_CORE_LAND 0x38DD +#define NA_SE_EN_MOFER_CORE_MOVE_WT 0x38DE +#define NA_SE_EN_MOFER_CORE_SMJUMP 0x38DF +#define NA_SE_EN_MONBLIN_GNDWAVE 0x38E0 +#define NA_SE_EN_MONBLIN_HAM_DOWN 0x38E1 +#define NA_SE_EN_MONBLIN_HAM_UP 0x38E2 +#define NA_SE_EN_BUBLE_DAMAGE 0x38E3 +#define NA_SE_EN_REDEAD_CRY 0x38E4 +#define NA_SE_EN_REDEAD_AIM 0x38E5 +#define NA_SE_EN_REDEAD_DAMAGE 0x38E6 +#define NA_SE_EN_REDEAD_DEAD 0x38E7 +#define NA_SE_EN_REDEAD_ATTACK 0x38E8 +#define NA_SE_EN_NYU_MOVE 0x38E9 +#define NA_SE_EN_NYU_HIT_STOP 0x38EA +#define NA_SE_EN_KAICHO_DEAD 0x38EB +#define NA_SE_EN_PO_LAUGH 0x38EC +#define NA_SE_EN_PO_CRY 0x38ED +#define NA_SE_EN_PO_ROLL 0x38EE +#define NA_SE_EN_PO_LAUGH2 0x38EF +#define NA_SE_EN_MOFER_APPEAR 0x38F0 +#define NA_SE_EN_MOFER_ATTACK 0x38F1 +#define NA_SE_EN_MOFER_WAVE 0x38F2 +#define NA_SE_EN_MOFER_CATCH 0x38F3 +#define NA_SE_EN_MOFER_CUT 0x38F4 +#define NA_SE_EN_MOFER_MOVE_DEMO 0x38F5 +#define NA_SE_EN_MOFER_BUBLE_DEMO 0x38F6 +#define NA_SE_EN_MOFER_CORE_JUMP 0x38F7 +#define NA_SE_EN_MOFER_DEAD 0x38F8 +#define NA_SE_EN_MOFER_LASTVOICE 0x38F9 +#define NA_SE_EN_MOFER_CORE_ROLL 0x38FA +#define NA_SE_EN_MOFER_CORE_FLY 0x38FB +#define NA_SE_EN_GOLON_WAKE_UP 0x38FC +#define NA_SE_EN_GOLON_SIT_DOWN 0x38FD +#define NA_SE_EN_CHICKEN_FLUTTER 0x38FE +#define NA_SE_EN_DEKU_WAKEUP 0x38FF +#define NA_SE_EN_DEADHAND_BITE 0x3900 +#define NA_SE_EN_DEADHAND_WALK 0x3901 +#define NA_SE_EN_DEADHAND_GRIP 0x3902 +#define NA_SE_EN_DEADHAND_HAND_AT 0x3903 +#define NA_SE_EN_DAIOCTA_MAHI 0x3904 +#define NA_SE_EN_DAIOCTA_SPLASH 0x3905 +#define NA_SE_EN_DAIOCTA_VOICE 0x3906 +#define NA_SE_EN_DAIOCTA_DAMAGE 0x3907 +#define NA_SE_EN_DAIOCTA_SINK 0x3908 +#define NA_SE_EN_DAIOCTA_DEAD 0x3909 +#define NA_SE_EN_DAIOCTA_DEAD2 0x390A +#define NA_SE_EN_GANON_HIT_THUNDER 0x390B +#define NA_SE_EN_TWINROBA_APPEAR_MS 0x390C +#define NA_SE_EN_TWINROBA_TRANSFORM 0x390D +#define NA_SE_EN_TWINROBA_MS_FIRE 0x390E +#define NA_SE_EN_TWINROBA_FIRE_EXP 0x390F +#define NA_SE_EN_TWINROBA_POWERUP 0x3910 +#define NA_SE_EN_TWINROBA_SHOOT_FREEZE 0x3911 +#define NA_SE_EN_TWINROBA_MS_FREEZE 0x3912 +#define NA_SE_EN_TWINROBA_MASIC_SET 0x3913 +#define NA_SE_EN_TWINROBA_CUTBODY 0x3914 +#define NA_SE_EN_GANON_HIT_GND_IMP 0x3915 +#define NA_SE_EN_TWINROBA_DAMAGE_VOICE 0x3916 +#define NA_SE_EN_TWINROBA_REFL_FIRE 0x3917 +#define NA_SE_EN_TWINROBA_REFL_FREEZE 0x3918 +#define NA_SE_EN_GANON_CUTBODY 0x3919 +#define NA_SE_EN_TWINROBA_YOUNG_DAMAGE 0x391A +#define NA_SE_EN_TWINROBA_YOUNG_DEAD 0x391B +#define NA_SE_EN_GOLON_EYE_BIG 0x391C +#define NA_SE_EN_GOLON_GOOD_BIG 0x391D +#define NA_SE_EN_TWINROBA_FB_FLY 0x391E +#define NA_SE_EN_TWINROBA_FLY 0x391F +#define NA_SE_EN_TWINROBA_UNARI 0x3920 +#define NA_SE_EN_TWINROBA_ROLL 0x3921 +#define NA_SE_EN_TWINROBA_SHOOT_FIRE 0x3922 +#define NA_SE_EN_TWINROBA_THROW_MASIC 0x3923 +#define NA_SE_EN_DARUNIA_HIT_BREAST 0x3924 +#define NA_SE_EN_DARUNIA_HIT_LINK 0x3925 +#define NA_SE_EN_OWL_FLUTTER 0x3926 +#define NA_SE_EN_VALVAISA_LAND 0x3927 +#define NA_SE_EN_IRONNACK_WALK 0x3928 +#define NA_SE_EN_IRONNACK_SWING_AXE 0x3929 +#define NA_SE_EN_IRONNACK_ARMOR_DEMO 0x392A +#define NA_SE_EN_IRONNACK_STAGGER_DEMO 0x392B +#define NA_SE_EN_IRONNACK_ARMOR_OFF_DEMO 0x392C +#define NA_SE_EN_IRONNACK_ARMOR_LAND1_DEMO 0x392D +#define NA_SE_EN_IRONNACK_ARMOR_LAND2_DEMO 0x392E +#define NA_SE_EN_IRONNACK_ARMOR_LAND3_DEMO 0x392F +#define NA_SE_EN_FLOORMASTER_ATTACK 0x3930 +#define NA_SE_EN_FLOORMASTER_SM_WALK 0x3931 +#define NA_SE_EN_FLOORMASTER_SM_DEAD 0x3932 +#define NA_SE_EN_FLOORMASTER_RESTORE 0x3933 +#define NA_SE_EN_FLOORMASTER_EXPAND 0x3934 +#define NA_SE_EN_FLOORMASTER_SPLIT 0x3935 +#define NA_SE_EN_FLOORMASTER_SM_LAND 0x3937 +#define NA_SE_EN_IRONNACK_WAVE_DEMO 0x3938 +#define NA_SE_EN_IRONNACK_FINGER_DEMO 0x3939 +#define NA_SE_EN_IRONNACK_ARMOR_HIT 0x393A +#define NA_SE_EN_NUTS_CUTBODY 0x393B +#define NA_SE_EN_BALINADE_LEVEL 0x393C +#define NA_SE_EN_BALINADE_DAMAGE 0x393D +#define NA_SE_EN_BALINADE_FAINT 0x393E +#define NA_SE_EN_BALINADE_BREAK 0x393F +#define NA_SE_EN_BALINADE_DEAD 0x3940 +#define NA_SE_EN_BALINADE_STICK 0x3941 +#define NA_SE_EN_BALINADE_THUNDER 0x3942 +#define NA_SE_EN_BALINADE_BL_SPARK 0x3943 +#define NA_SE_EN_BALINADE_BL_DEAD 0x3944 +#define NA_SE_EN_BALINADE_BREAK2 0x3945 +#define NA_SE_EN_BALINADE_HIT_RINK 0x3946 +#define NA_SE_EN_GANON_WAVE_GND 0x3947 +#define NA_SE_EN_AWA_BOUND 0x3948 +#define NA_SE_EN_AWA_BREAK 0x3949 +#define NA_SE_EN_BROB_WAVE 0x394A +#define NA_SE_EN_NYU_DEAD 0x394B +#define NA_SE_EN_EIER_DAMAGE 0x394C +#define NA_SE_EN_EIER_DEAD 0x394D +#define NA_SE_EN_EIER_FLUTTER 0x394E +#define NA_SE_EN_EIER_FLY 0x394F +#define NA_SE_EN_SHADEST_TAIKO_LOW 0x3950 +#define NA_SE_EN_SHADEST_TAIKO_HIGH 0x3951 +#define NA_SE_EN_SHADEST_CLAP 0x3952 +#define NA_SE_EN_SHADEST_FLY_ATTACK 0x3953 +#define NA_SE_EN_PIHAT_UP 0x3954 +#define NA_SE_EN_PIHAT_FLY 0x3955 +#define NA_SE_EN_PIHAT_DAMAGE 0x3956 +#define NA_SE_EN_PIHAT_LAND 0x3957 +#define NA_SE_EN_BALINADE_HAND_DOWN 0x3958 +#define NA_SE_EN_BALINADE_HAND_UP 0x3959 +#define NA_SE_EN_BALINADE_HAND_DAMAGE 0x395A +#define NA_SE_EN_BALINADE_HAND_DEAD 0x395B +#define NA_SE_EN_GOMA_JR_WALK 0x395C +#define NA_SE_EN_GOMA_JR_CRY 0x395D +#define NA_SE_EN_GOMA_JR_DAM1 0x395E +#define NA_SE_EN_GOMA_JR_DAM2 0x395F +#define NA_SE_EN_GOMA_JR_DEAD 0x3960 +#define NA_SE_EN_GOMA_EGG1 0x3961 +#define NA_SE_EN_GOMA_EGG2 0x3962 +#define NA_SE_EN_GANON_BODY_SPARK 0x3963 +#define NA_SE_EN_SHADEST_HAND_WAVE 0x3964 +#define NA_SE_EN_SHADEST_CATCH 0x3965 +#define NA_SE_EN_SHADEST_LAND 0x3966 +#define NA_SE_EN_SHADEST_HAND_FLY 0x3967 +#define NA_SE_EN_SHADEST_SHAKEHAND 0x3968 +#define NA_SE_EN_SHADEST_DAMAGE 0x3969 +#define NA_SE_EN_SHADEST_DAMAGE_HAND 0x396A +#define NA_SE_EN_SHADEST_DISAPPEAR 0x396B +#define NA_SE_EN_GANON_CHARGE_MASIC 0x396C +#define NA_SE_EN_GANON_THROW_BIG 0x396D +#define NA_SE_EN_SHADEST_FREEZE 0x396E +#define NA_SE_EN_SHADEST_DEAD 0x396F +#define NA_SE_EN_BIMOS_ROLL_HEAD 0x3970 +#define NA_SE_EN_BIMOS_LAZER 0x3971 +#define NA_SE_EN_BIMOS_LAZER_GND 0x3972 +#define NA_SE_EN_BIMOS_AIM 0x3973 +#define NA_SE_EN_BUBLEWALK_WALK 0x3974 +#define NA_SE_EN_BUBLEWALK_AIM 0x3975 +#define NA_SE_EN_BUBLEWALK_REVERSE 0x3976 +#define NA_SE_EN_BUBLEWALK_DAMAGE 0x3977 +#define NA_SE_EN_BUBLEWALK_DEAD 0x3978 +#define NA_SE_EN_YUKABYUN_FLY 0x3979 +#define NA_SE_EN_FLAME_DAMAGE 0x397A +#define NA_SE_EN_TWINROBA_FLY_DEMO 0x397B +#define NA_SE_EN_FLAME_KICK 0x397C +#define NA_SE_EN_FLAME_RUN 0x397D +#define NA_SE_EN_FLAME_ROLL 0x397E +#define NA_SE_EN_FLAME_MAN_RUN 0x397F +#define NA_SE_EN_FLAME_MAN_DAMAGE 0x3980 +#define NA_SE_EN_FLAME_LAUGH 0x3981 +#define NA_SE_EN_FLAME_MAN_SLIDE 0x3982 +#define NA_SE_EN_FLAME_FIRE_ATTACK 0x3983 +#define NA_SE_EN_PIHAT_SM_FLY 0x3984 +#define NA_SE_EN_PIHAT_SM_DEAD 0x3985 +#define NA_SE_EN_RIVA_APPEAR 0x3986 +#define NA_SE_EN_AKINDONUTS_HIDE 0x3987 +#define NA_SE_EN_RIVA_DAMAGE 0x3988 +#define NA_SE_EN_RIVA_DEAD 0x3989 +#define NA_SE_EN_RIVA_MOVE 0x398A +#define NA_SE_EN_FLAME_MAN_SURP 0x398B +#define NA_SE_EN_SHADEST_LAST 0x398C +#define NA_SE_EN_SHADEST_MOVE 0x398D +#define NA_SE_EN_SHADEST_PRAY 0x398E +#define NA_SE_EN_MGANON_ROAR 0x398F +#define NA_SE_EN_LIKE_WALK 0x3990 +#define NA_SE_EN_LIKE_UNARI 0x3991 +#define NA_SE_EN_LIKE_DRINK 0x3992 +#define NA_SE_EN_LIKE_EAT 0x3993 +#define NA_SE_EN_LIKE_THROW 0x3994 +#define NA_SE_EN_LIKE_DAMAGE 0x3995 +#define NA_SE_EN_LIKE_DEAD 0x3996 +#define NA_SE_EN_MGANON_SWORD 0x3997 +#define NA_SE_EN_GERUDOFT_ATTACK 0x3998 +#define NA_SE_EN_GERUDOFT_DAMAGE 0x3999 +#define NA_SE_EN_GERUDOFT_DEAD 0x399A +#define NA_SE_EN_MGANON_DAMAGE 0x399B +#define NA_SE_EN_ANUBIS_FIRE 0x399C +#define NA_SE_EN_ANUBIS_FIREBOMB 0x399D +#define NA_SE_EN_MGANON_DEAD1 0x399E +#define NA_SE_EN_ANUBIS_DEAD 0x399F +#define NA_SE_EN_MUSI_LAND 0x39A0 +#define NA_SE_EN_MGANON_DEAD2 0x39A1 +#define NA_SE_EN_EIER_ATTACK 0x39A2 +#define NA_SE_EN_EIER_CRY 0x39A3 +#define NA_SE_EN_FREEZAD_BREATH 0x39A4 +#define NA_SE_EN_FREEZAD_DAMAGE 0x39A5 +#define NA_SE_EN_FREEZAD_DEAD 0x39A6 +#define NA_SE_EN_DEADHAND_LAUGH 0x39A7 +#define NA_SE_EN_DEADHAND_HIDE 0x39A8 +#define NA_SE_EN_DEADHAND_DAMAGE 0x39A9 +#define NA_SE_EN_DEADHAND_HAND_DEAD 0x39AA +#define NA_SE_EN_DEADHAND_DEAD 0x39AB +#define NA_SE_EN_IRONNACK_BREAK_PILLAR2 0x39AC +#define NA_SE_EN_IRONNACK_BREAK_PILLAR 0x39AD +#define NA_SE_EN_IRONNACK_HIT_GND 0x39AE +#define NA_SE_EN_MGANON_BREATH 0x39AF +#define NA_SE_EN_TWINROBA_LAUGH 0x39B0 +#define NA_SE_EN_TWINROBA_LAUGH2 0x39B1 +#define NA_SE_EN_DUMMY434 0x39B2 +#define NA_SE_EN_TWINROBA_SHOOT_VOICE 0x39B3 +#define NA_SE_EN_TWINROBA_SENSE 0x39B4 +#define NA_SE_EN_TWINROBA_DIE 0x39B5 +#define NA_SE_EN_DUMMY438 0x39B6 +#define NA_SE_EN_TWINROBA_YOUNG_DAMAGE2 0x39B7 +#define NA_SE_EN_TWINROBA_YOUNG_SHOOTVC 0x39B8 +#define NA_SE_EN_TWINROBA_YOUNG_LAUGH 0x39B9 +#define NA_SE_EN_DUMMY442 0x39BA +#define NA_SE_EN_TWINROBA_YOUNG_WINK 0x39BB +#define NA_SE_EN_DUMMY444 0x39BC +#define NA_SE_EN_DUMMY445 0x39BD +#define NA_SE_EN_IRONNACK_DAMAGE 0x39BE +#define NA_SE_EN_IRONNACK_DASH 0x39BF +#define NA_SE_EN_IRONNACK_DEAD 0x39C0 +#define NA_SE_EN_IRONNACK_PULLOUT 0x39C1 +#define NA_SE_EN_IRONNACK_WAKEUP 0x39C2 +#define NA_SE_EN_DUMMY451 0x39C3 +#define NA_SE_EN_DUMMY452 0x39C4 +#define NA_SE_EN_DUMMY453 0x39C5 +#define NA_SE_EN_GERUDOFT_BREATH 0x39C6 +#define NA_SE_EN_GANON_LAUGH 0x39C7 +#define NA_SE_EN_GANON_VOICE_DEMO 0x39C8 +#define NA_SE_EN_GANON_THROW 0x39C9 +#define NA_SE_EN_GANON_AT_RETURN 0x39CA +#define NA_SE_EN_GANON_HIT_GND 0x39CB +#define NA_SE_EN_GANON_DAMAGE1 0x39CC +#define NA_SE_EN_GANON_DAMAGE2 0x39CD +#define NA_SE_EN_GANON_DOWN 0x39CE +#define NA_SE_EN_GANON_RESTORE 0x39CF +#define NA_SE_EN_GANON_DEAD 0x39D0 +#define NA_SE_EN_GANON_BREATH 0x39D1 +#define NA_SE_EN_GANON_TOKETU 0x39D2 +#define NA_SE_EN_GANON_CASBREAK 0x39D3 +#define NA_SE_EN_GANON_BIGMASIC 0x39D4 +#define NA_SE_EN_GANON_DARKWAVE 0x39D5 +#define NA_SE_EN_FANTOM_ST_LAUGH 0x39D6 +#define NA_SE_EN_MGANON_WALK 0x39D7 +#define NA_SE_EN_MGANON_STAND 0x39D8 +#define NA_SE_EN_MGANON_UNARI 0x39D9 +#define NA_SE_EN_STALGOLD_ROLL 0x39DA +#define NA_SE_EN_KDOOR_WAVE 0x39DB +#define NA_SE_EN_KDOOR_HIT 0x39DC +#define NA_SE_EN_KDOOR_BREAK 0x39DD +#define NA_SE_EN_KDOOR_HIT_GND 0x39DE +#define NA_SE_EN_MGANON_SWDIMP 0x39DF +#define NA_SE_EN_STALTU_WAVE 0x39E0 +#define NA_SE_EN_STALTU_DOWN_SET 0x39E1 +#define NA_SE_EN_DUMMY482 0x39E2 +#define NA_SE_EN_GOMA_BJR_LAND 0x39E3 +#define NA_SE_EN_GOMA_BJR_LAND2 0x39E4 +#define NA_SE_EN_GOMA_JR_LAND 0x39E5 +#define NA_SE_EN_GOMA_JR_LAND2 0x39E6 +#define NA_SE_EN_TWINROBA_FIGHT 0x39E7 +#define NA_SE_EN_PO_BIG_CRY 0x39E8 +#define NA_SE_EN_MUSI_SINK 0x39E9 +#define NA_SE_EN_STALGOLD_UP_CRY 0x39EA +#define NA_SE_EN_GOLON_CRY 0x39EB +#define NA_SE_EN_MOFER_CORE_DAMAGE 0x39EC +#define NA_SE_EN_DAIOCTA_LAND_WATER 0x39ED +#define NA_SE_EN_RIVA_BIG_APPEAR 0x39EE +#define NA_SE_EN_MONBLIN_HAM_LAND 0x39EF +#define NA_SE_EN_MUSI_WALK 0x39F0 +#define NA_SE_EN_MIMICK_BREATH 0x39F1 +#define NA_SE_EN_STALWALL_LAUGH 0x39F2 +#define NA_SE_EN_TWINROBA_TRANSFORM2 0x39F3 +#define NA_SE_EN_KAICHO_PIYORI 0x39F4 +#define NA_SE_EN_DODO_K_WALK_APPEAR 0x39F5 +#define NA_SE_EN_DODO_K_STOP 0x39F6 +#define NA_SE_EN_TEKU_GND993 0x39F7 +#define NA_SE_EN_TEKU_JUMP993 0x39F8 +#define NA_SE_EN_MORIBLIN_DEMO 0x39F9 +#define NA_SE_EN_LEADED_WHITE 0x39FA +#define NA_SE_EN_RIVA_HIDE_NEW 0x39FB +#define NA_SE_EN_STALBABY_HIDE_NEW 0x39FC +#define NA_SE_EN_STAL_REBORN 0x39FD +#define NA_SE_EN_DAIKU_FOOT 0x39FE +#define NA_SE_EN_STALKID_DAMAGE_NEW 0x39FF +#define NA_SE_EN_STALKID_DOWN_NEW 0x3A00 +#define NA_SE_EN_BALINADE_ARM_LAND 0x3A01 +#define NA_SE_EN_BALINADE_ARM_DEAD 0x3A02 +#define NA_SE_EN_READED_WALK 0x3A03 +#define NA_SE_EN_YOBI14 0x3A04 +#define NA_SE_EN_YOBI15 0x3A05 +#define NA_SE_EN_YOBI16 0x3A06 +#define NA_SE_EN_YOBI17 0x3A07 +#define NA_SE_EN_YOBI18 0x3A08 +#define NA_SE_EN_YOBI19 0x3A09 +#define NA_SE_EN_YOBI20 0x3A0A +#define NA_SE_EN_YOBI21 0x3A0B +#define NA_SE_EN_YOBI22 0x3A0C +#define NA_SE_EN_YOBI23 0x3A0D +#define NA_SE_EN_YOBI24 0x3A0E +#define NA_SE_EN_YOBI25 0x3A0F +#define NA_SE_EN_YOBI26 0x3A10 +#define NA_SE_EN_YOBI27 0x3A11 +#define NA_SE_EN_YOBI28 0x3A12 +#define NA_SE_EN_YOBI29 0x3A13 +#define NA_SE_EN_YOBI30 0x3A14 + +// ------------ SYSTEM ------------ + +#define NA_SE_SY_WIN_OPEN 0x4800 +#define NA_SE_SY_WIN_CLOSE 0x4801 +#define NA_SE_SY_CORRECT_CHIME 0x4802 +#define NA_SE_SY_GET_RUPY 0x4803 +#define NA_SE_SY_MESSAGE_WOMAN 0x4804 +#define NA_SE_SY_MESSAGE_MAN 0x4805 +#define NA_SE_SY_ERROR 0x4806 +#define NA_SE_SY_TRE_BOX_APPEAR 0x4807 +#define NA_SE_SY_DECIDE 0x4808 +#define NA_SE_SY_CURSOR 0x4809 +#define NA_SE_SY_CANCEL 0x480A +#define NA_SE_SY_HP_RECOVER 0x480B +#define NA_SE_SY_ATTENTION_ON 0x480C +#define NA_SE_SY_DUMMY_13 0x480D +#define NA_SE_SY_DUMMY_14 0x480E +#define NA_SE_SY_LOCK_OFF 0x480F +#define NA_SE_SY_LOCK_ON_HUMAN 0x4810 +#define NA_SE_SY_DUMMY_17 0x4811 +#define NA_SE_SY_DUMMY_18 0x4812 +#define NA_SE_SY_CAMERA_ZOOM_UP 0x4813 +#define NA_SE_SY_CAMERA_ZOOM_DOWN 0x4814 +#define NA_SE_SY_DUMMY_21 0x4815 +#define NA_SE_SY_DUMMY_22 0x4816 +#define NA_SE_SY_ATTENTION_ON_OLD 0x4817 +#define NA_SE_SY_MESSAGE_PASS 0x4818 +#define NA_SE_SY_WARNING_COUNT_N 0x4819 +#define NA_SE_SY_WARNING_COUNT_E 0x481A +#define NA_SE_SY_HITPOINT_ALARM 0x481B +#define NA_SE_SY_DUMMY_28 0x481C +#define NA_SE_SY_DEMO_CUT 0x481D +#define NA_SE_SY_NAVY_CALL 0x481E +#define NA_SE_SY_GAUGE_UP 0x481F +#define NA_SE_SY_DUMMY_32 0x4820 +#define NA_SE_SY_DUMMY_33 0x4821 +#define NA_SE_SY_DUMMY_34 0x4822 +#define NA_SE_SY_PIECE_OF_HEART 0x4823 +#define NA_SE_SY_GET_ITEM 0x4824 +#define NA_SE_SY_WIN_SCROLL_LEFT 0x4825 +#define NA_SE_SY_WIN_SCROLL_RIGHT 0x4826 +#define NA_SE_SY_OCARINA_ERROR 0x4827 +#define NA_SE_SY_CAMERA_ZOOM_UP_2 0x4828 +#define NA_SE_SY_CAMERA_ZOOM_DOWN_2 0x4829 +#define NA_SE_SY_GLASSMODE_ON 0x482A +#define NA_SE_SY_GLASSMODE_OFF 0x482B +#define NA_SE_SY_FOUND 0x482C +#define NA_SE_SY_HIT_SOUND 0x482D +#define NA_SE_SY_MESSAGE_END 0x482E +#define NA_SE_SY_RUPY_COUNT 0x482F +#define NA_SE_SY_LOCK_ON 0x4830 +#define NA_SE_SY_GET_BOXITEM 0x4831 +#define NA_SE_SY_WHITE_OUT_L 0x4832 +#define NA_SE_SY_WHITE_OUT_S 0x4833 +#define NA_SE_SY_WHITE_OUT_T 0x4834 +#define NA_SE_SY_START_SHOT 0x4835 +#define NA_SE_SY_METRONOME 0x4836 +#define NA_SE_SY_ATTENTION_URGENCY 0x4837 +#define NA_SE_SY_METRONOME_LV 0x4838 +#define NA_SE_SY_FSEL_CURSOR 0x4839 +#define NA_SE_SY_FSEL_DECIDE_S 0x483A +#define NA_SE_SY_FSEL_DECIDE_L 0x483B +#define NA_SE_SY_FSEL_CLOSE 0x483C +#define NA_SE_SY_FSEL_ERROR 0x483D +#define NA_SE_SY_SET_FIRE_ARROW 0x483E +#define NA_SE_SY_SET_ICE_ARROW 0x483F +#define NA_SE_SY_SET_LIGHT_ARROW 0x4840 +#define NA_SE_SY_SYNTH_MAGIC_ARROW 0x4841 +#define NA_SE_SY_METRONOME_2 0x4842 +#define NA_SE_SY_KINSTA_MARK_APPEAR 0x4843 +#define NA_SE_SY_FIVE_COUNT_LUPY 0x4844 +#define NA_SE_SY_CARROT_RECOVER 0x4845 +#define NA_SE_EV_FAIVE_LUPY_COUNT 0x4846 // EV sound inside SY? +#define NA_SE_SY_DUMMY_71 0x4847 +#define NA_SE_SY_KANADE_ISHI 0x4848 +#define NA_SE_SY_NA_HELLO_2 0x4849 +#define NA_SE_SY_MAGIC_SOUL_NORMAL 0x484A +#define NA_SE_SY_MAGIC_SOUL_FLASH 0x484B +#define NA_SE_SY_CANCEL_CHALLENGE 0x484C +#define NA_SE_SY_DECIDE_CHALLENGE 0x484D +#define NA_SE_SY_YOBI05 0x484E +#define NA_SE_SY_YOBI06 0x484F +#define NA_SE_SY_YOBI07 0x4850 +#define NA_SE_SY_YOBI08 0x4851 +#define NA_SE_SY_YOBI09 0x4852 +#define NA_SE_SY_YOBI10 0x4853 +#define NA_SE_SY_YOBI11 0x4854 +#define NA_SE_SY_YOBI12 0x4855 +#define NA_SE_SY_YOBI13 0x4856 +#define NA_SE_SY_YOBI14 0x4857 +#define NA_SE_SY_YOBI15 0x4858 +#define NA_SE_SY_YOBI16 0x4859 +#define NA_SE_SY_YOBI17 0x485A + +// ------------ OCARINA ------------ + +#define NA_SE_OC_OCARINA 0x5800 +#define NA_SE_OC_ABYSS 0x5801 +#define NA_SE_OC_DOOR_OPEN 0x5802 +#define NA_SE_OC_SECRET_WARP_IN 0x5803 +#define NA_SE_OC_SECRET_WARP_OUT 0x5804 +#define NA_SE_OC_SECRET_HOLE_OUT 0x5805 +#define NA_SE_OC_REVENGE 0x5806 +#define NA_SE_OC_HINT_MOVIE 0x5807 +#define NA_SE_OC_HINT_MOVIE2_WHITE 0x5808 +#define NA_SE_OC_HINT_MOVIE_ZOOMIN 0x5809 +#define NA_SE_OC_HIBIKI_ISHI 0x580A + +// ------------ VOICE ------------ + +#define NA_SE_VO_LI_SWORD_N 0x6800 +#define NA_SE_VO_LI_SWORD_L 0x6801 +#define NA_SE_VO_LI_LASH 0x6802 +#define NA_SE_VO_LI_HANG 0x6803 +#define NA_SE_VO_LI_CLIMB_END 0x6804 +#define NA_SE_VO_LI_DAMAGE_S 0x6805 +#define NA_SE_VO_LI_FREEZE 0x6806 +#define NA_SE_VO_LI_FALL_S 0x6807 +#define NA_SE_VO_LI_FALL_L 0x6808 +#define NA_SE_VO_LI_BREATH_REST 0x6809 +#define NA_SE_VO_LI_BREATH_DRINK 0x680A +#define NA_SE_VO_LI_DOWN 0x680B +#define NA_SE_VO_LI_TAKEN_AWAY 0x680C +#define NA_SE_VO_LI_HELD 0x680D +#define NA_SE_VO_LI_SNEEZE 0x680E +#define NA_SE_VO_LI_SWEAT 0x680F +#define NA_SE_VO_LI_DRINK 0x6810 +#define NA_SE_VO_LI_RELAX 0x6811 +#define NA_SE_VO_LI_SWORD_PUTAWAY 0x6812 +#define NA_SE_VO_LI_GROAN 0x6813 +#define NA_SE_VO_LI_AUTO_JUMP 0x6814 +#define NA_SE_VO_LI_MAGIC_NALE 0x6815 +#define NA_SE_VO_LI_SURPRISE 0x6816 +#define NA_SE_VO_LI_MAGIC_FROL 0x6817 +#define NA_SE_VO_LI_PUSH 0x6818 +#define NA_SE_VO_LI_HOOKSHOT_HANG 0x6819 +#define NA_SE_VO_LI_LAND_DAMAGE_S 0x681A +#define NA_SE_VO_LI_NULL_0x1b 0x681B +#define NA_SE_VO_LI_MAGIC_ATTACK 0x681C +#define NA_SE_VO_BL_DOWN 0x681D +#define NA_SE_VO_LI_DEMO_DAMAGE 0x681E +#define NA_SE_VO_LI_ELECTRIC_SHOCK_LV 0x681F +#define NA_SE_VO_LI_SWORD_N_KID 0x6820 +#define NA_SE_VO_LI_ROLLING_CUT_KID 0x6821 +#define NA_SE_VO_LI_LASH_KID 0x6822 +#define NA_SE_VO_LI_HANG_KID 0x6823 +#define NA_SE_VO_LI_CLIMB_END_KID 0x6824 +#define NA_SE_VO_LI_DAMAGE_S_KID 0x6825 +#define NA_SE_VO_LI_FREEZE_KID 0x6826 +#define NA_SE_VO_LI_FALL_S_KID 0x6827 +#define NA_SE_VO_LI_FALL_L_KID 0x6828 +#define NA_SE_VO_LI_BREATH_REST_KID 0x6829 +#define NA_SE_VO_LI_BREATH_DRINK_KID 0x682A +#define NA_SE_VO_LI_DOWN_KID 0x682B +#define NA_SE_VO_LI_TAKEN_AWAY_KID 0x682C +#define NA_SE_VO_LI_HELD_KID 0x682D +#define NA_SE_VO_LI_SNEEZE_KID 0x682E +#define NA_SE_VO_LI_SWEAT_KID 0x682F +#define NA_SE_VO_LI_DRINK_KID 0x6830 +#define NA_SE_VO_LI_RELAX_KID 0x6831 +#define NA_SE_VO_LI_SWORD_PUTAWAY_KID 0x6832 +#define NA_SE_VO_LI_GROAN_KID 0x6833 +#define NA_SE_VO_LI_AUTO_JUMP_KID 0x6834 +#define NA_SE_VO_LI_MAGIC_NALE_KID 0x6835 +#define NA_SE_VO_LI_SURPRISE_KID 0x6836 +#define NA_SE_VO_LI_MAGIC_FROL_KID 0x6837 +#define NA_SE_VO_LI_PUSH_KID 0x6838 +#define NA_SE_VO_LI_HOOKSHOT_HANG_KID 0x6839 +#define NA_SE_VO_LI_LAND_DAMAGE_S_KID 0x683A +#define NA_SE_VO_LI_NULL_0x1b_KID 0x683B +#define NA_SE_VO_LI_MAGIC_ATTACK_KID 0x683C +#define NA_SE_VO_BL_DOWN_KID 0x683D +#define NA_SE_VO_LI_DEMO_DAMAGE_KID 0x683E +#define NA_SE_VO_LI_ELECTRIC_SHOCK_LV_KID 0x683F +#define NA_SE_VO_NAVY_ENEMY 0x6840 +#define NA_SE_VO_NAVY_HELLO 0x6841 +#define NA_SE_VO_NAVY_HEAR 0x6842 +#define NA_SE_VO_NAVY_CALL 0x6843 +#define NA_SE_VO_NA_HELLO_3 0x6844 +#define NA_SE_VO_DUMMY_0x45 0x6845 +#define NA_SE_VO_DUMMY_0x46 0x6846 +#define NA_SE_VO_DUMMY_0x47 0x6847 +#define NA_SE_VO_DUMMY_0x48 0x6848 +#define NA_SE_VO_DUMMY_0x49 0x6849 +#define NA_SE_VO_DUMMY_0x4a 0x684A +#define NA_SE_VO_DUMMY_0x4b 0x684B +#define NA_SE_VO_DUMMY_0x4c 0x684C +#define NA_SE_VO_DUMMY_0x4d 0x684D +#define NA_SE_VO_DUMMY_0x4e 0x684E +#define NA_SE_VO_DUMMY_0x4f 0x684F +#define NA_SE_VO_TA_SLEEP 0x6850 +#define NA_SE_VO_TA_SURPRISE 0x6851 +#define NA_SE_VO_TA_CRY_0 0x6852 +#define NA_SE_VO_TA_CRY_1 0x6853 +#define NA_SE_VO_IN_CRY_0 0x6854 +#define NA_SE_VO_IN_LOST 0x6855 +#define NA_SE_VO_IN_LASH_0 0x6856 +#define NA_SE_VO_IN_LASH_1 0x6857 +#define NA_SE_VO_FR_LAUGH_0 0x6858 +#define NA_SE_VO_FR_SMILE_0 0x6859 +#define NA_SE_VO_NB_AGONY 0x685A +#define NA_SE_VO_NB_CRY_0 0x685B +#define NA_SE_VO_NB_NOTICE 0x685C +#define NA_SE_VO_NA_HELLO_0 0x685D +#define NA_SE_VO_NA_HELLO_1 0x685E +#define NA_SE_VO_NA_HELLO_2 0x685F +#define NA_SE_VO_RT_CRASH 0x6860 +#define NA_SE_VO_RT_DISCOVER 0x6861 +#define NA_SE_VO_RT_FALL 0x6862 +#define NA_SE_VO_RT_LAUGH_0 0x6863 +#define NA_SE_VO_RT_LIFT 0x6864 +#define NA_SE_VO_RT_THROW 0x6865 +#define NA_SE_VO_RT_UNBALLANCE 0x6866 +#define NA_SE_VO_ST_DAMAGE 0x6867 +#define NA_SE_VO_ST_ATTACK 0x6868 +#define NA_SE_VO_Z0_HURRY 0x6869 +#define NA_SE_VO_Z0_MEET 0x686A +#define NA_SE_VO_Z0_QUESTION 0x686B +#define NA_SE_VO_Z0_SIGH_0 0x686C +#define NA_SE_VO_Z0_SMILE_0 0x686D +#define NA_SE_VO_Z0_SURPRISE 0x686E +#define NA_SE_VO_Z0_THROW 0x686F +#define NA_SE_VO_SK_CRY_0 0x6870 +#define NA_SE_VO_SK_CRY_1 0x6871 +#define NA_SE_VO_SK_CRASH 0x6872 +#define NA_SE_VO_SK_LAUGH 0x6873 +#define NA_SE_VO_SK_SHOUT 0x6874 +#define NA_SE_VO_Z1_CRY_0 0x6875 +#define NA_SE_VO_Z1_CRY_1 0x6876 +#define NA_SE_VO_Z1_OPENDOOR 0x6877 +#define NA_SE_VO_Z1_SURPRISE 0x6878 +#define NA_SE_VO_Z1_PAIN 0x6879 +#define NA_SE_VO_KZ_MOVE 0x687A +#define NA_SE_VO_NB_LAUGH 0x687B +#define NA_SE_VO_IN_LAUGH 0x687C +#define NA_SE_VO_DUMMY_0x7d 0x687D +#define NA_SE_VO_DUMMY_0x7e 0x687E +#define NA_SE_VO_DUMMY_0x7f 0x687F +#define NA_SE_VO_DUMMY_0x80 0x6880 +#define NA_SE_VO_LI_AUTO_JUMP_DARKLINK 0x6881 +#define NA_SE_VO_DUMMY_0x82_YOBI 0x6882 +#define NA_SE_VO_DUMMY_0x83_YOBI 0x6883 +#define NA_SE_VO_DUMMY_0x84_YOBI 0x6884 +#define NA_SE_VO_DUMMY_0x85_YOBI 0x6885 +#define NA_SE_VO_DUMMY_0x86_YOBI 0x6886 +#define NA_SE_VO_DUMMY_0x87_YOBI 0x6887 +#define NA_SE_VO_DUMMY_0x88_YOBI 0x6888 +#define NA_SE_VO_DUMMY_0x89_YOBI 0x6889 + +#endif diff --git a/soh/include/tables/actor_table.h b/soh/include/tables/actor_table.h new file mode 100644 index 000000000..cdbebbc63 --- /dev/null +++ b/soh/include/tables/actor_table.h @@ -0,0 +1,483 @@ +/** + * Actor Table + * + * DEFINE_ACTOR should be used for normal actors + * - Argument 1: Name of the actor (without the ovl_ part) + * - Argument 2: Enum value for this actor + * - Argument 3: Allocation type (normal, permanent or absolute) + * + * DEFINE_ACTOR_INTERNAL should be used for actors that aren't an overlay, with the same arguments as DEFINE_ACTOR + * + * DEFINE_ACTOR_UNSET is needed to define empty entries from the original game + */ +/* 0x0000 */ DEFINE_ACTOR_INTERNAL(Player, ACTOR_PLAYER, ALLOCTYPE_NORMAL) +/* 0x0001 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_1) +/* 0x0002 */ DEFINE_ACTOR(En_Test, ACTOR_EN_TEST, ALLOCTYPE_NORMAL) +/* 0x0003 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_3) +/* 0x0004 */ DEFINE_ACTOR(En_GirlA, ACTOR_EN_GIRLA, ALLOCTYPE_NORMAL) +/* 0x0005 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_5) +/* 0x0006 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_6) +/* 0x0007 */ DEFINE_ACTOR(En_Part, ACTOR_EN_PART, ALLOCTYPE_NORMAL) +/* 0x0008 */ DEFINE_ACTOR(En_Light, ACTOR_EN_LIGHT, ALLOCTYPE_NORMAL) +/* 0x0009 */ DEFINE_ACTOR(En_Door, ACTOR_EN_DOOR, ALLOCTYPE_PERMANENT) +/* 0x000A */ DEFINE_ACTOR(En_Box, ACTOR_EN_BOX, ALLOCTYPE_NORMAL) +/* 0x000B */ DEFINE_ACTOR(Bg_Dy_Yoseizo, ACTOR_BG_DY_YOSEIZO, ALLOCTYPE_NORMAL) +/* 0x000C */ DEFINE_ACTOR(Bg_Hidan_Firewall, ACTOR_BG_HIDAN_FIREWALL, ALLOCTYPE_NORMAL) +/* 0x000D */ DEFINE_ACTOR(En_Poh, ACTOR_EN_POH, ALLOCTYPE_NORMAL) +/* 0x000E */ DEFINE_ACTOR(En_Okuta, ACTOR_EN_OKUTA, ALLOCTYPE_NORMAL) +/* 0x000F */ DEFINE_ACTOR(Bg_Ydan_Sp, ACTOR_BG_YDAN_SP, ALLOCTYPE_NORMAL) +/* 0x0010 */ DEFINE_ACTOR(En_Bom, ACTOR_EN_BOM, ALLOCTYPE_PERMANENT) +/* 0x0011 */ DEFINE_ACTOR(En_Wallmas, ACTOR_EN_WALLMAS, ALLOCTYPE_NORMAL) +/* 0x0012 */ DEFINE_ACTOR(En_Dodongo, ACTOR_EN_DODONGO, ALLOCTYPE_NORMAL) +/* 0x0013 */ DEFINE_ACTOR(En_Firefly, ACTOR_EN_FIREFLY, ALLOCTYPE_NORMAL) +/* 0x0014 */ DEFINE_ACTOR(En_Horse, ACTOR_EN_HORSE, ALLOCTYPE_NORMAL) +/* 0x0015 */ DEFINE_ACTOR_INTERNAL(En_Item00, ACTOR_EN_ITEM00, ALLOCTYPE_NORMAL) +/* 0x0016 */ DEFINE_ACTOR(En_Arrow, ACTOR_EN_ARROW, ALLOCTYPE_PERMANENT) +/* 0x0017 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_17) +/* 0x0018 */ DEFINE_ACTOR(En_Elf, ACTOR_EN_ELF, ALLOCTYPE_NORMAL) +/* 0x0019 */ DEFINE_ACTOR(En_Niw, ACTOR_EN_NIW, ALLOCTYPE_NORMAL) +/* 0x001A */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_1A) +/* 0x001B */ DEFINE_ACTOR(En_Tite, ACTOR_EN_TITE, ALLOCTYPE_NORMAL) +/* 0x001C */ DEFINE_ACTOR(En_Reeba, ACTOR_EN_REEBA, ALLOCTYPE_NORMAL) +/* 0x001D */ DEFINE_ACTOR(En_Peehat, ACTOR_EN_PEEHAT, ALLOCTYPE_NORMAL) +/* 0x001E */ DEFINE_ACTOR(En_Butte, ACTOR_EN_BUTTE, ALLOCTYPE_NORMAL) +/* 0x001F */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_1F) +/* 0x0020 */ DEFINE_ACTOR(En_Insect, ACTOR_EN_INSECT, ALLOCTYPE_NORMAL) +/* 0x0021 */ DEFINE_ACTOR(En_Fish, ACTOR_EN_FISH, ALLOCTYPE_NORMAL) +/* 0x0022 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_22) +/* 0x0023 */ DEFINE_ACTOR(En_Holl, ACTOR_EN_HOLL, ALLOCTYPE_PERMANENT) +/* 0x0024 */ DEFINE_ACTOR(En_Scene_Change, ACTOR_EN_SCENE_CHANGE, ALLOCTYPE_NORMAL) +/* 0x0025 */ DEFINE_ACTOR(En_Zf, ACTOR_EN_ZF, ALLOCTYPE_NORMAL) +/* 0x0026 */ DEFINE_ACTOR(En_Hata, ACTOR_EN_HATA, ALLOCTYPE_NORMAL) +/* 0x0027 */ DEFINE_ACTOR(Boss_Dodongo, ACTOR_BOSS_DODONGO, ALLOCTYPE_NORMAL) +/* 0x0028 */ DEFINE_ACTOR(Boss_Goma, ACTOR_BOSS_GOMA, ALLOCTYPE_NORMAL) +/* 0x0029 */ DEFINE_ACTOR(En_Zl1, ACTOR_EN_ZL1, ALLOCTYPE_NORMAL) +/* 0x002A */ DEFINE_ACTOR(En_Viewer, ACTOR_EN_VIEWER, ALLOCTYPE_NORMAL) +/* 0x002B */ DEFINE_ACTOR(En_Goma, ACTOR_EN_GOMA, ALLOCTYPE_NORMAL) +/* 0x002C */ DEFINE_ACTOR(Bg_Pushbox, ACTOR_BG_PUSHBOX, ALLOCTYPE_NORMAL) +/* 0x002D */ DEFINE_ACTOR(En_Bubble, ACTOR_EN_BUBBLE, ALLOCTYPE_NORMAL) +/* 0x002E */ DEFINE_ACTOR(Door_Shutter, ACTOR_DOOR_SHUTTER, ALLOCTYPE_PERMANENT) +/* 0x002F */ DEFINE_ACTOR(En_Dodojr, ACTOR_EN_DODOJR, ALLOCTYPE_NORMAL) +/* 0x0030 */ DEFINE_ACTOR(En_Bdfire, ACTOR_EN_BDFIRE, ALLOCTYPE_NORMAL) +/* 0x0031 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_31) +/* 0x0032 */ DEFINE_ACTOR(En_Boom, ACTOR_EN_BOOM, ALLOCTYPE_PERMANENT) +/* 0x0033 */ DEFINE_ACTOR(En_Torch2, ACTOR_EN_TORCH2, ALLOCTYPE_NORMAL) +/* 0x0034 */ DEFINE_ACTOR(En_Bili, ACTOR_EN_BILI, ALLOCTYPE_NORMAL) +/* 0x0035 */ DEFINE_ACTOR(En_Tp, ACTOR_EN_TP, ALLOCTYPE_NORMAL) +/* 0x0036 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_36) +/* 0x0037 */ DEFINE_ACTOR(En_St, ACTOR_EN_ST, ALLOCTYPE_NORMAL) +/* 0x0038 */ DEFINE_ACTOR(En_Bw, ACTOR_EN_BW, ALLOCTYPE_NORMAL) +/* 0x0039 */ DEFINE_ACTOR_INTERNAL(En_A_Obj, ACTOR_EN_A_OBJ, ALLOCTYPE_NORMAL) +/* 0x003A */ DEFINE_ACTOR(En_Eiyer, ACTOR_EN_EIYER, ALLOCTYPE_NORMAL) +/* 0x003B */ DEFINE_ACTOR(En_River_Sound, ACTOR_EN_RIVER_SOUND, ALLOCTYPE_NORMAL) +/* 0x003C */ DEFINE_ACTOR(En_Horse_Normal, ACTOR_EN_HORSE_NORMAL, ALLOCTYPE_NORMAL) +/* 0x003D */ DEFINE_ACTOR(En_Ossan, ACTOR_EN_OSSAN, ALLOCTYPE_NORMAL) +/* 0x003E */ DEFINE_ACTOR(Bg_Treemouth, ACTOR_BG_TREEMOUTH, ALLOCTYPE_NORMAL) +/* 0x003F */ DEFINE_ACTOR(Bg_Dodoago, ACTOR_BG_DODOAGO, ALLOCTYPE_NORMAL) +/* 0x0040 */ DEFINE_ACTOR(Bg_Hidan_Dalm, ACTOR_BG_HIDAN_DALM, ALLOCTYPE_NORMAL) +/* 0x0041 */ DEFINE_ACTOR(Bg_Hidan_Hrock, ACTOR_BG_HIDAN_HROCK, ALLOCTYPE_NORMAL) +/* 0x0042 */ DEFINE_ACTOR(En_Horse_Ganon, ACTOR_EN_HORSE_GANON, ALLOCTYPE_NORMAL) +/* 0x0043 */ DEFINE_ACTOR(Bg_Hidan_Rock, ACTOR_BG_HIDAN_ROCK, ALLOCTYPE_NORMAL) +/* 0x0044 */ DEFINE_ACTOR(Bg_Hidan_Rsekizou, ACTOR_BG_HIDAN_RSEKIZOU, ALLOCTYPE_NORMAL) +/* 0x0045 */ DEFINE_ACTOR(Bg_Hidan_Sekizou, ACTOR_BG_HIDAN_SEKIZOU, ALLOCTYPE_NORMAL) +/* 0x0046 */ DEFINE_ACTOR(Bg_Hidan_Sima, ACTOR_BG_HIDAN_SIMA, ALLOCTYPE_NORMAL) +/* 0x0047 */ DEFINE_ACTOR(Bg_Hidan_Syoku, ACTOR_BG_HIDAN_SYOKU, ALLOCTYPE_NORMAL) +/* 0x0048 */ DEFINE_ACTOR(En_Xc, ACTOR_EN_XC, ALLOCTYPE_NORMAL) +/* 0x0049 */ DEFINE_ACTOR(Bg_Hidan_Curtain, ACTOR_BG_HIDAN_CURTAIN, ALLOCTYPE_NORMAL) +/* 0x004A */ DEFINE_ACTOR(Bg_Spot00_Hanebasi, ACTOR_BG_SPOT00_HANEBASI, ALLOCTYPE_NORMAL) +/* 0x004B */ DEFINE_ACTOR(En_Mb, ACTOR_EN_MB, ALLOCTYPE_NORMAL) +/* 0x004C */ DEFINE_ACTOR(En_Bombf, ACTOR_EN_BOMBF, ALLOCTYPE_NORMAL) +/* 0x004D */ DEFINE_ACTOR(En_Zl2, ACTOR_EN_ZL2, ALLOCTYPE_NORMAL) +/* 0x004E */ DEFINE_ACTOR(Bg_Hidan_Fslift, ACTOR_BG_HIDAN_FSLIFT, ALLOCTYPE_NORMAL) +/* 0x004F */ DEFINE_ACTOR(En_OE2, ACTOR_EN_OE2, ALLOCTYPE_NORMAL) +/* 0x0050 */ DEFINE_ACTOR(Bg_Ydan_Hasi, ACTOR_BG_YDAN_HASI, ALLOCTYPE_NORMAL) +/* 0x0051 */ DEFINE_ACTOR(Bg_Ydan_Maruta, ACTOR_BG_YDAN_MARUTA, ALLOCTYPE_NORMAL) +/* 0x0052 */ DEFINE_ACTOR(Boss_Ganondrof, ACTOR_BOSS_GANONDROF, ALLOCTYPE_NORMAL) +/* 0x0053 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_53) +/* 0x0054 */ DEFINE_ACTOR(En_Am, ACTOR_EN_AM, ALLOCTYPE_NORMAL) +/* 0x0055 */ DEFINE_ACTOR(En_Dekubaba, ACTOR_EN_DEKUBABA, ALLOCTYPE_NORMAL) +/* 0x0056 */ DEFINE_ACTOR(En_M_Fire1, ACTOR_EN_M_FIRE1, ALLOCTYPE_PERMANENT) +/* 0x0057 */ DEFINE_ACTOR(En_M_Thunder, ACTOR_EN_M_THUNDER, ALLOCTYPE_PERMANENT) +/* 0x0058 */ DEFINE_ACTOR(Bg_Ddan_Jd, ACTOR_BG_DDAN_JD, ALLOCTYPE_NORMAL) +/* 0x0059 */ DEFINE_ACTOR(Bg_Breakwall, ACTOR_BG_BREAKWALL, ALLOCTYPE_NORMAL) +/* 0x005A */ DEFINE_ACTOR(En_Jj, ACTOR_EN_JJ, ALLOCTYPE_NORMAL) +/* 0x005B */ DEFINE_ACTOR(En_Horse_Zelda, ACTOR_EN_HORSE_ZELDA, ALLOCTYPE_NORMAL) +/* 0x005C */ DEFINE_ACTOR(Bg_Ddan_Kd, ACTOR_BG_DDAN_KD, ALLOCTYPE_NORMAL) +/* 0x005D */ DEFINE_ACTOR(Door_Warp1, ACTOR_DOOR_WARP1, ALLOCTYPE_NORMAL) +/* 0x005E */ DEFINE_ACTOR(Obj_Syokudai, ACTOR_OBJ_SYOKUDAI, ALLOCTYPE_NORMAL) +/* 0x005F */ DEFINE_ACTOR(Item_B_Heart, ACTOR_ITEM_B_HEART, ALLOCTYPE_NORMAL) +/* 0x0060 */ DEFINE_ACTOR(En_Dekunuts, ACTOR_EN_DEKUNUTS, ALLOCTYPE_NORMAL) +/* 0x0061 */ DEFINE_ACTOR(Bg_Menkuri_Kaiten, ACTOR_BG_MENKURI_KAITEN, ALLOCTYPE_NORMAL) +/* 0x0062 */ DEFINE_ACTOR(Bg_Menkuri_Eye, ACTOR_BG_MENKURI_EYE, ALLOCTYPE_NORMAL) +/* 0x0063 */ DEFINE_ACTOR(En_Vali, ACTOR_EN_VALI, ALLOCTYPE_NORMAL) +/* 0x0064 */ DEFINE_ACTOR(Bg_Mizu_Movebg, ACTOR_BG_MIZU_MOVEBG, ALLOCTYPE_NORMAL) +/* 0x0065 */ DEFINE_ACTOR(Bg_Mizu_Water, ACTOR_BG_MIZU_WATER, ALLOCTYPE_NORMAL) +/* 0x0066 */ DEFINE_ACTOR(Arms_Hook, ACTOR_ARMS_HOOK, ALLOCTYPE_PERMANENT) +/* 0x0067 */ DEFINE_ACTOR(En_fHG, ACTOR_EN_FHG, ALLOCTYPE_NORMAL) +/* 0x0068 */ DEFINE_ACTOR(Bg_Mori_Hineri, ACTOR_BG_MORI_HINERI, ALLOCTYPE_NORMAL) +/* 0x0069 */ DEFINE_ACTOR(En_Bb, ACTOR_EN_BB, ALLOCTYPE_NORMAL) +/* 0x006A */ DEFINE_ACTOR(Bg_Toki_Hikari, ACTOR_BG_TOKI_HIKARI, ALLOCTYPE_NORMAL) +/* 0x006B */ DEFINE_ACTOR(En_Yukabyun, ACTOR_EN_YUKABYUN, ALLOCTYPE_NORMAL) +/* 0x006C */ DEFINE_ACTOR(Bg_Toki_Swd, ACTOR_BG_TOKI_SWD, ALLOCTYPE_NORMAL) +/* 0x006D */ DEFINE_ACTOR(En_Fhg_Fire, ACTOR_EN_FHG_FIRE, ALLOCTYPE_NORMAL) +/* 0x006E */ DEFINE_ACTOR(Bg_Mjin, ACTOR_BG_MJIN, ALLOCTYPE_NORMAL) +/* 0x006F */ DEFINE_ACTOR(Bg_Hidan_Kousi, ACTOR_BG_HIDAN_KOUSI, ALLOCTYPE_NORMAL) +/* 0x0070 */ DEFINE_ACTOR(Door_Toki, ACTOR_DOOR_TOKI, ALLOCTYPE_NORMAL) +/* 0x0071 */ DEFINE_ACTOR(Bg_Hidan_Hamstep, ACTOR_BG_HIDAN_HAMSTEP, ALLOCTYPE_NORMAL) +/* 0x0072 */ DEFINE_ACTOR(En_Bird, ACTOR_EN_BIRD, ALLOCTYPE_NORMAL) +/* 0x0073 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_73) +/* 0x0074 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_74) +/* 0x0075 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_75) +/* 0x0076 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_76) +/* 0x0077 */ DEFINE_ACTOR(En_Wood02, ACTOR_EN_WOOD02, ALLOCTYPE_NORMAL) +/* 0x0078 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_78) +/* 0x0079 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_79) +/* 0x007A */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_7A) +/* 0x007B */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_7B) +/* 0x007C */ DEFINE_ACTOR(En_Lightbox, ACTOR_EN_LIGHTBOX, ALLOCTYPE_NORMAL) +/* 0x007D */ DEFINE_ACTOR(En_Pu_box, ACTOR_EN_PU_BOX, ALLOCTYPE_NORMAL) +/* 0x007E */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_7E) +/* 0x007F */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_7F) +/* 0x0080 */ DEFINE_ACTOR(En_Trap, ACTOR_EN_TRAP, ALLOCTYPE_NORMAL) +/* 0x0081 */ DEFINE_ACTOR(En_Arow_Trap, ACTOR_EN_AROW_TRAP, ALLOCTYPE_NORMAL) +/* 0x0082 */ DEFINE_ACTOR(En_Vase, ACTOR_EN_VASE, ALLOCTYPE_NORMAL) +/* 0x0083 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_83) +/* 0x0084 */ DEFINE_ACTOR(En_Ta, ACTOR_EN_TA, ALLOCTYPE_NORMAL) +/* 0x0085 */ DEFINE_ACTOR(En_Tk, ACTOR_EN_TK, ALLOCTYPE_NORMAL) +/* 0x0086 */ DEFINE_ACTOR(Bg_Mori_Bigst, ACTOR_BG_MORI_BIGST, ALLOCTYPE_NORMAL) +/* 0x0087 */ DEFINE_ACTOR(Bg_Mori_Elevator, ACTOR_BG_MORI_ELEVATOR, ALLOCTYPE_NORMAL) +/* 0x0088 */ DEFINE_ACTOR(Bg_Mori_Kaitenkabe, ACTOR_BG_MORI_KAITENKABE, ALLOCTYPE_NORMAL) +/* 0x0089 */ DEFINE_ACTOR(Bg_Mori_Rakkatenjo, ACTOR_BG_MORI_RAKKATENJO, ALLOCTYPE_NORMAL) +/* 0x008A */ DEFINE_ACTOR(En_Vm, ACTOR_EN_VM, ALLOCTYPE_NORMAL) +/* 0x008B */ DEFINE_ACTOR(Demo_Effect, ACTOR_DEMO_EFFECT, ALLOCTYPE_NORMAL) +/* 0x008C */ DEFINE_ACTOR(Demo_Kankyo, ACTOR_DEMO_KANKYO, ALLOCTYPE_NORMAL) +/* 0x008D */ DEFINE_ACTOR(Bg_Hidan_Fwbig, ACTOR_BG_HIDAN_FWBIG, ALLOCTYPE_NORMAL) +/* 0x008E */ DEFINE_ACTOR(En_Floormas, ACTOR_EN_FLOORMAS, ALLOCTYPE_NORMAL) +/* 0x008F */ DEFINE_ACTOR(En_Heishi1, ACTOR_EN_HEISHI1, ALLOCTYPE_NORMAL) +/* 0x0090 */ DEFINE_ACTOR(En_Rd, ACTOR_EN_RD, ALLOCTYPE_NORMAL) +/* 0x0091 */ DEFINE_ACTOR(En_Po_Sisters, ACTOR_EN_PO_SISTERS, ALLOCTYPE_NORMAL) +/* 0x0092 */ DEFINE_ACTOR(Bg_Heavy_Block, ACTOR_BG_HEAVY_BLOCK, ALLOCTYPE_NORMAL) +/* 0x0093 */ DEFINE_ACTOR(Bg_Po_Event, ACTOR_BG_PO_EVENT, ALLOCTYPE_NORMAL) +/* 0x0094 */ DEFINE_ACTOR(Obj_Mure, ACTOR_OBJ_MURE, ALLOCTYPE_NORMAL) +/* 0x0095 */ DEFINE_ACTOR(En_Sw, ACTOR_EN_SW, ALLOCTYPE_NORMAL) +/* 0x0096 */ DEFINE_ACTOR(Boss_Fd, ACTOR_BOSS_FD, ALLOCTYPE_NORMAL) +/* 0x0097 */ DEFINE_ACTOR(Object_Kankyo, ACTOR_OBJECT_KANKYO, ALLOCTYPE_NORMAL) +/* 0x0098 */ DEFINE_ACTOR(En_Du, ACTOR_EN_DU, ALLOCTYPE_NORMAL) +/* 0x0099 */ DEFINE_ACTOR(En_Fd, ACTOR_EN_FD, ALLOCTYPE_NORMAL) +/* 0x009A */ DEFINE_ACTOR(En_Horse_Link_Child, ACTOR_EN_HORSE_LINK_CHILD, ALLOCTYPE_NORMAL) +/* 0x009B */ DEFINE_ACTOR(Door_Ana, ACTOR_DOOR_ANA, ALLOCTYPE_NORMAL) +/* 0x009C */ DEFINE_ACTOR(Bg_Spot02_Objects, ACTOR_BG_SPOT02_OBJECTS, ALLOCTYPE_NORMAL) +/* 0x009D */ DEFINE_ACTOR(Bg_Haka, ACTOR_BG_HAKA, ALLOCTYPE_NORMAL) +/* 0x009E */ DEFINE_ACTOR(Magic_Wind, ACTOR_MAGIC_WIND, ALLOCTYPE_ABSOLUTE) +/* 0x009F */ DEFINE_ACTOR(Magic_Fire, ACTOR_MAGIC_FIRE, ALLOCTYPE_ABSOLUTE) +/* 0x00A0 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_A0) +/* 0x00A1 */ DEFINE_ACTOR(En_Ru1, ACTOR_EN_RU1, ALLOCTYPE_NORMAL) +/* 0x00A2 */ DEFINE_ACTOR(Boss_Fd2, ACTOR_BOSS_FD2, ALLOCTYPE_NORMAL) +/* 0x00A3 */ DEFINE_ACTOR(En_Fd_Fire, ACTOR_EN_FD_FIRE, ALLOCTYPE_NORMAL) +/* 0x00A4 */ DEFINE_ACTOR(En_Dh, ACTOR_EN_DH, ALLOCTYPE_NORMAL) +/* 0x00A5 */ DEFINE_ACTOR(En_Dha, ACTOR_EN_DHA, ALLOCTYPE_NORMAL) +/* 0x00A6 */ DEFINE_ACTOR(En_Rl, ACTOR_EN_RL, ALLOCTYPE_NORMAL) +/* 0x00A7 */ DEFINE_ACTOR(En_Encount1, ACTOR_EN_ENCOUNT1, ALLOCTYPE_NORMAL) +/* 0x00A8 */ DEFINE_ACTOR(Demo_Du, ACTOR_DEMO_DU, ALLOCTYPE_NORMAL) +/* 0x00A9 */ DEFINE_ACTOR(Demo_Im, ACTOR_DEMO_IM, ALLOCTYPE_NORMAL) +/* 0x00AA */ DEFINE_ACTOR(Demo_Tre_Lgt, ACTOR_DEMO_TRE_LGT, ALLOCTYPE_NORMAL) +/* 0x00AB */ DEFINE_ACTOR(En_Fw, ACTOR_EN_FW, ALLOCTYPE_NORMAL) +/* 0x00AC */ DEFINE_ACTOR(Bg_Vb_Sima, ACTOR_BG_VB_SIMA, ALLOCTYPE_NORMAL) +/* 0x00AD */ DEFINE_ACTOR(En_Vb_Ball, ACTOR_EN_VB_BALL, ALLOCTYPE_NORMAL) +/* 0x00AE */ DEFINE_ACTOR(Bg_Haka_Megane, ACTOR_BG_HAKA_MEGANE, ALLOCTYPE_NORMAL) +/* 0x00AF */ DEFINE_ACTOR(Bg_Haka_MeganeBG, ACTOR_BG_HAKA_MEGANEBG, ALLOCTYPE_NORMAL) +/* 0x00B0 */ DEFINE_ACTOR(Bg_Haka_Ship, ACTOR_BG_HAKA_SHIP, ALLOCTYPE_NORMAL) +/* 0x00B1 */ DEFINE_ACTOR(Bg_Haka_Sgami, ACTOR_BG_HAKA_SGAMI, ALLOCTYPE_NORMAL) +/* 0x00B2 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_B2) +/* 0x00B3 */ DEFINE_ACTOR(En_Heishi2, ACTOR_EN_HEISHI2, ALLOCTYPE_NORMAL) +/* 0x00B4 */ DEFINE_ACTOR(En_Encount2, ACTOR_EN_ENCOUNT2, ALLOCTYPE_NORMAL) +/* 0x00B5 */ DEFINE_ACTOR(En_Fire_Rock, ACTOR_EN_FIRE_ROCK, ALLOCTYPE_NORMAL) +/* 0x00B6 */ DEFINE_ACTOR(En_Brob, ACTOR_EN_BROB, ALLOCTYPE_NORMAL) +/* 0x00B7 */ DEFINE_ACTOR(Mir_Ray, ACTOR_MIR_RAY, ALLOCTYPE_NORMAL) +/* 0x00B8 */ DEFINE_ACTOR(Bg_Spot09_Obj, ACTOR_BG_SPOT09_OBJ, ALLOCTYPE_NORMAL) +/* 0x00B9 */ DEFINE_ACTOR(Bg_Spot18_Obj, ACTOR_BG_SPOT18_OBJ, ALLOCTYPE_NORMAL) +/* 0x00BA */ DEFINE_ACTOR(Boss_Va, ACTOR_BOSS_VA, ALLOCTYPE_NORMAL) +/* 0x00BB */ DEFINE_ACTOR(Bg_Haka_Tubo, ACTOR_BG_HAKA_TUBO, ALLOCTYPE_NORMAL) +/* 0x00BC */ DEFINE_ACTOR(Bg_Haka_Trap, ACTOR_BG_HAKA_TRAP, ALLOCTYPE_NORMAL) +/* 0x00BD */ DEFINE_ACTOR(Bg_Haka_Huta, ACTOR_BG_HAKA_HUTA, ALLOCTYPE_NORMAL) +/* 0x00BE */ DEFINE_ACTOR(Bg_Haka_Zou, ACTOR_BG_HAKA_ZOU, ALLOCTYPE_NORMAL) +/* 0x00BF */ DEFINE_ACTOR(Bg_Spot17_Funen, ACTOR_BG_SPOT17_FUNEN, ALLOCTYPE_NORMAL) +/* 0x00C0 */ DEFINE_ACTOR(En_Syateki_Itm, ACTOR_EN_SYATEKI_ITM, ALLOCTYPE_NORMAL) +/* 0x00C1 */ DEFINE_ACTOR(En_Syateki_Man, ACTOR_EN_SYATEKI_MAN, ALLOCTYPE_NORMAL) +/* 0x00C2 */ DEFINE_ACTOR(En_Tana, ACTOR_EN_TANA, ALLOCTYPE_NORMAL) +/* 0x00C3 */ DEFINE_ACTOR(En_Nb, ACTOR_EN_NB, ALLOCTYPE_NORMAL) +/* 0x00C4 */ DEFINE_ACTOR(Boss_Mo, ACTOR_BOSS_MO, ALLOCTYPE_NORMAL) +/* 0x00C5 */ DEFINE_ACTOR(En_Sb, ACTOR_EN_SB, ALLOCTYPE_NORMAL) +/* 0x00C6 */ DEFINE_ACTOR(En_Bigokuta, ACTOR_EN_BIGOKUTA, ALLOCTYPE_NORMAL) +/* 0x00C7 */ DEFINE_ACTOR(En_Karebaba, ACTOR_EN_KAREBABA, ALLOCTYPE_NORMAL) +/* 0x00C8 */ DEFINE_ACTOR(Bg_Bdan_Objects, ACTOR_BG_BDAN_OBJECTS, ALLOCTYPE_NORMAL) +/* 0x00C9 */ DEFINE_ACTOR(Demo_Sa, ACTOR_DEMO_SA, ALLOCTYPE_NORMAL) +/* 0x00CA */ DEFINE_ACTOR(Demo_Go, ACTOR_DEMO_GO, ALLOCTYPE_NORMAL) +/* 0x00CB */ DEFINE_ACTOR(En_In, ACTOR_EN_IN, ALLOCTYPE_NORMAL) +/* 0x00CC */ DEFINE_ACTOR(En_Tr, ACTOR_EN_TR, ALLOCTYPE_NORMAL) +/* 0x00CD */ DEFINE_ACTOR(Bg_Spot16_Bombstone, ACTOR_BG_SPOT16_BOMBSTONE, ALLOCTYPE_NORMAL) +/* 0x00CE */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_CE) +/* 0x00CF */ DEFINE_ACTOR(Bg_Hidan_Kowarerukabe, ACTOR_BG_HIDAN_KOWARERUKABE, ALLOCTYPE_NORMAL) +/* 0x00D0 */ DEFINE_ACTOR(Bg_Bombwall, ACTOR_BG_BOMBWALL, ALLOCTYPE_NORMAL) +/* 0x00D1 */ DEFINE_ACTOR(Bg_Spot08_Iceblock, ACTOR_BG_SPOT08_ICEBLOCK, ALLOCTYPE_NORMAL) +/* 0x00D2 */ DEFINE_ACTOR(En_Ru2, ACTOR_EN_RU2, ALLOCTYPE_NORMAL) +/* 0x00D3 */ DEFINE_ACTOR(Obj_Dekujr, ACTOR_OBJ_DEKUJR, ALLOCTYPE_NORMAL) +/* 0x00D4 */ DEFINE_ACTOR(Bg_Mizu_Uzu, ACTOR_BG_MIZU_UZU, ALLOCTYPE_NORMAL) +/* 0x00D5 */ DEFINE_ACTOR(Bg_Spot06_Objects, ACTOR_BG_SPOT06_OBJECTS, ALLOCTYPE_NORMAL) +/* 0x00D6 */ DEFINE_ACTOR(Bg_Ice_Objects, ACTOR_BG_ICE_OBJECTS, ALLOCTYPE_NORMAL) +/* 0x00D7 */ DEFINE_ACTOR(Bg_Haka_Water, ACTOR_BG_HAKA_WATER, ALLOCTYPE_NORMAL) +/* 0x00D8 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_D8) +/* 0x00D9 */ DEFINE_ACTOR(En_Ma2, ACTOR_EN_MA2, ALLOCTYPE_NORMAL) +/* 0x00DA */ DEFINE_ACTOR(En_Bom_Chu, ACTOR_EN_BOM_CHU, ALLOCTYPE_NORMAL) +/* 0x00DB */ DEFINE_ACTOR(En_Horse_Game_Check, ACTOR_EN_HORSE_GAME_CHECK, ALLOCTYPE_NORMAL) +/* 0x00DC */ DEFINE_ACTOR(Boss_Tw, ACTOR_BOSS_TW, ALLOCTYPE_NORMAL) +/* 0x00DD */ DEFINE_ACTOR(En_Rr, ACTOR_EN_RR, ALLOCTYPE_NORMAL) +/* 0x00DE */ DEFINE_ACTOR(En_Ba, ACTOR_EN_BA, ALLOCTYPE_NORMAL) +/* 0x00DF */ DEFINE_ACTOR(En_Bx, ACTOR_EN_BX, ALLOCTYPE_NORMAL) +/* 0x00E0 */ DEFINE_ACTOR(En_Anubice, ACTOR_EN_ANUBICE, ALLOCTYPE_NORMAL) +/* 0x00E1 */ DEFINE_ACTOR(En_Anubice_Fire, ACTOR_EN_ANUBICE_FIRE, ALLOCTYPE_NORMAL) +/* 0x00E2 */ DEFINE_ACTOR(Bg_Mori_Hashigo, ACTOR_BG_MORI_HASHIGO, ALLOCTYPE_NORMAL) +/* 0x00E3 */ DEFINE_ACTOR(Bg_Mori_Hashira4, ACTOR_BG_MORI_HASHIRA4, ALLOCTYPE_NORMAL) +/* 0x00E4 */ DEFINE_ACTOR(Bg_Mori_Idomizu, ACTOR_BG_MORI_IDOMIZU, ALLOCTYPE_NORMAL) +/* 0x00E5 */ DEFINE_ACTOR(Bg_Spot16_Doughnut, ACTOR_BG_SPOT16_DOUGHNUT, ALLOCTYPE_NORMAL) +/* 0x00E6 */ DEFINE_ACTOR(Bg_Bdan_Switch, ACTOR_BG_BDAN_SWITCH, ALLOCTYPE_NORMAL) +/* 0x00E7 */ DEFINE_ACTOR(En_Ma1, ACTOR_EN_MA1, ALLOCTYPE_NORMAL) +/* 0x00E8 */ DEFINE_ACTOR(Boss_Ganon, ACTOR_BOSS_GANON, ALLOCTYPE_NORMAL) +/* 0x00E9 */ DEFINE_ACTOR(Boss_Sst, ACTOR_BOSS_SST, ALLOCTYPE_NORMAL) +/* 0x00EA */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_EA) +/* 0x00EB */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_EB) +/* 0x00EC */ DEFINE_ACTOR(En_Ny, ACTOR_EN_NY, ALLOCTYPE_NORMAL) +/* 0x00ED */ DEFINE_ACTOR(En_Fr, ACTOR_EN_FR, ALLOCTYPE_NORMAL) +/* 0x00EE */ DEFINE_ACTOR(Item_Shield, ACTOR_ITEM_SHIELD, ALLOCTYPE_NORMAL) +/* 0x00EF */ DEFINE_ACTOR(Bg_Ice_Shelter, ACTOR_BG_ICE_SHELTER, ALLOCTYPE_NORMAL) +/* 0x00F0 */ DEFINE_ACTOR(En_Ice_Hono, ACTOR_EN_ICE_HONO, ALLOCTYPE_NORMAL) +/* 0x00F1 */ DEFINE_ACTOR(Item_Ocarina, ACTOR_ITEM_OCARINA, ALLOCTYPE_NORMAL) +/* 0x00F2 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_F2) +/* 0x00F3 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_F3) +/* 0x00F4 */ DEFINE_ACTOR(Magic_Dark, ACTOR_MAGIC_DARK, ALLOCTYPE_ABSOLUTE) +/* 0x00F5 */ DEFINE_ACTOR(Demo_6K, ACTOR_DEMO_6K, ALLOCTYPE_NORMAL) +/* 0x00F6 */ DEFINE_ACTOR(En_Anubice_Tag, ACTOR_EN_ANUBICE_TAG, ALLOCTYPE_NORMAL) +/* 0x00F7 */ DEFINE_ACTOR(Bg_Haka_Gate, ACTOR_BG_HAKA_GATE, ALLOCTYPE_NORMAL) +/* 0x00F8 */ DEFINE_ACTOR(Bg_Spot15_Saku, ACTOR_BG_SPOT15_SAKU, ALLOCTYPE_NORMAL) +/* 0x00F9 */ DEFINE_ACTOR(Bg_Jya_Goroiwa, ACTOR_BG_JYA_GOROIWA, ALLOCTYPE_NORMAL) +/* 0x00FA */ DEFINE_ACTOR(Bg_Jya_Zurerukabe, ACTOR_BG_JYA_ZURERUKABE, ALLOCTYPE_NORMAL) +/* 0x00FB */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_FB) +/* 0x00FC */ DEFINE_ACTOR(Bg_Jya_Cobra, ACTOR_BG_JYA_COBRA, ALLOCTYPE_NORMAL) +/* 0x00FD */ DEFINE_ACTOR(Bg_Jya_Kanaami, ACTOR_BG_JYA_KANAAMI, ALLOCTYPE_NORMAL) +/* 0x00FE */ DEFINE_ACTOR(Fishing, ACTOR_FISHING, ALLOCTYPE_NORMAL) +/* 0x00FF */ DEFINE_ACTOR(Obj_Oshihiki, ACTOR_OBJ_OSHIHIKI, ALLOCTYPE_NORMAL) +/* 0x0100 */ DEFINE_ACTOR(Bg_Gate_Shutter, ACTOR_BG_GATE_SHUTTER, ALLOCTYPE_NORMAL) +/* 0x0101 */ DEFINE_ACTOR(Eff_Dust, ACTOR_EFF_DUST, ALLOCTYPE_NORMAL) +/* 0x0102 */ DEFINE_ACTOR(Bg_Spot01_Fusya, ACTOR_BG_SPOT01_FUSYA, ALLOCTYPE_NORMAL) +/* 0x0103 */ DEFINE_ACTOR(Bg_Spot01_Idohashira, ACTOR_BG_SPOT01_IDOHASHIRA, ALLOCTYPE_NORMAL) +/* 0x0104 */ DEFINE_ACTOR(Bg_Spot01_Idomizu, ACTOR_BG_SPOT01_IDOMIZU, ALLOCTYPE_NORMAL) +/* 0x0105 */ DEFINE_ACTOR(Bg_Po_Syokudai, ACTOR_BG_PO_SYOKUDAI, ALLOCTYPE_NORMAL) +/* 0x0106 */ DEFINE_ACTOR(Bg_Ganon_Otyuka, ACTOR_BG_GANON_OTYUKA, ALLOCTYPE_NORMAL) +/* 0x0107 */ DEFINE_ACTOR(Bg_Spot15_Rrbox, ACTOR_BG_SPOT15_RRBOX, ALLOCTYPE_NORMAL) +/* 0x0108 */ DEFINE_ACTOR(Bg_Umajump, ACTOR_BG_UMAJUMP, ALLOCTYPE_NORMAL) +/* 0x0109 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_109) +/* 0x010A */ DEFINE_ACTOR(Arrow_Fire, ACTOR_ARROW_FIRE, ALLOCTYPE_ABSOLUTE) +/* 0x010B */ DEFINE_ACTOR(Arrow_Ice, ACTOR_ARROW_ICE, ALLOCTYPE_ABSOLUTE) +/* 0x010C */ DEFINE_ACTOR(Arrow_Light, ACTOR_ARROW_LIGHT, ALLOCTYPE_ABSOLUTE) +/* 0x010D */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_10D) +/* 0x010E */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_10E) +/* 0x010F */ DEFINE_ACTOR(Item_Etcetera, ACTOR_ITEM_ETCETERA, ALLOCTYPE_NORMAL) +/* 0x0110 */ DEFINE_ACTOR(Obj_Kibako, ACTOR_OBJ_KIBAKO, ALLOCTYPE_NORMAL) +/* 0x0111 */ DEFINE_ACTOR(Obj_Tsubo, ACTOR_OBJ_TSUBO, ALLOCTYPE_NORMAL) +/* 0x0112 */ DEFINE_ACTOR(En_Wonder_Item, ACTOR_EN_WONDER_ITEM, ALLOCTYPE_NORMAL) +/* 0x0113 */ DEFINE_ACTOR(En_Ik, ACTOR_EN_IK, ALLOCTYPE_NORMAL) +/* 0x0114 */ DEFINE_ACTOR(Demo_Ik, ACTOR_DEMO_IK, ALLOCTYPE_NORMAL) +/* 0x0115 */ DEFINE_ACTOR(En_Skj, ACTOR_EN_SKJ, ALLOCTYPE_NORMAL) +/* 0x0116 */ DEFINE_ACTOR(En_Skjneedle, ACTOR_EN_SKJNEEDLE, ALLOCTYPE_NORMAL) +/* 0x0117 */ DEFINE_ACTOR(En_G_Switch, ACTOR_EN_G_SWITCH, ALLOCTYPE_NORMAL) +/* 0x0118 */ DEFINE_ACTOR(Demo_Ext, ACTOR_DEMO_EXT, ALLOCTYPE_NORMAL) +/* 0x0119 */ DEFINE_ACTOR(Demo_Shd, ACTOR_DEMO_SHD, ALLOCTYPE_NORMAL) +/* 0x011A */ DEFINE_ACTOR(En_Dns, ACTOR_EN_DNS, ALLOCTYPE_NORMAL) +/* 0x011B */ DEFINE_ACTOR(Elf_Msg, ACTOR_ELF_MSG, ALLOCTYPE_NORMAL) +/* 0x011C */ DEFINE_ACTOR(En_Honotrap, ACTOR_EN_HONOTRAP, ALLOCTYPE_NORMAL) +/* 0x011D */ DEFINE_ACTOR(En_Tubo_Trap, ACTOR_EN_TUBO_TRAP, ALLOCTYPE_NORMAL) +/* 0x011E */ DEFINE_ACTOR(Obj_Ice_Poly, ACTOR_OBJ_ICE_POLY, ALLOCTYPE_NORMAL) +/* 0x011F */ DEFINE_ACTOR(Bg_Spot03_Taki, ACTOR_BG_SPOT03_TAKI, ALLOCTYPE_NORMAL) +/* 0x0120 */ DEFINE_ACTOR(Bg_Spot07_Taki, ACTOR_BG_SPOT07_TAKI, ALLOCTYPE_NORMAL) +/* 0x0121 */ DEFINE_ACTOR(En_Fz, ACTOR_EN_FZ, ALLOCTYPE_NORMAL) +/* 0x0122 */ DEFINE_ACTOR(En_Po_Relay, ACTOR_EN_PO_RELAY, ALLOCTYPE_NORMAL) +/* 0x0123 */ DEFINE_ACTOR(Bg_Relay_Objects, ACTOR_BG_RELAY_OBJECTS, ALLOCTYPE_NORMAL) +/* 0x0124 */ DEFINE_ACTOR(En_Diving_Game, ACTOR_EN_DIVING_GAME, ALLOCTYPE_NORMAL) +/* 0x0125 */ DEFINE_ACTOR(En_Kusa, ACTOR_EN_KUSA, ALLOCTYPE_NORMAL) +/* 0x0126 */ DEFINE_ACTOR(Obj_Bean, ACTOR_OBJ_BEAN, ALLOCTYPE_NORMAL) +/* 0x0127 */ DEFINE_ACTOR(Obj_Bombiwa, ACTOR_OBJ_BOMBIWA, ALLOCTYPE_NORMAL) +/* 0x0128 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_128) +/* 0x0129 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_129) +/* 0x012A */ DEFINE_ACTOR(Obj_Switch, ACTOR_OBJ_SWITCH, ALLOCTYPE_NORMAL) +/* 0x012B */ DEFINE_ACTOR(Obj_Elevator, ACTOR_OBJ_ELEVATOR, ALLOCTYPE_NORMAL) +/* 0x012C */ DEFINE_ACTOR(Obj_Lift, ACTOR_OBJ_LIFT, ALLOCTYPE_NORMAL) +/* 0x012D */ DEFINE_ACTOR(Obj_Hsblock, ACTOR_OBJ_HSBLOCK, ALLOCTYPE_NORMAL) +/* 0x012E */ DEFINE_ACTOR(En_Okarina_Tag, ACTOR_EN_OKARINA_TAG, ALLOCTYPE_NORMAL) +/* 0x012F */ DEFINE_ACTOR(En_Yabusame_Mark, ACTOR_EN_YABUSAME_MARK, ALLOCTYPE_NORMAL) +/* 0x0130 */ DEFINE_ACTOR(En_Goroiwa, ACTOR_EN_GOROIWA, ALLOCTYPE_NORMAL) +/* 0x0131 */ DEFINE_ACTOR(En_Ex_Ruppy, ACTOR_EN_EX_RUPPY, ALLOCTYPE_NORMAL) +/* 0x0132 */ DEFINE_ACTOR(En_Toryo, ACTOR_EN_TORYO, ALLOCTYPE_NORMAL) +/* 0x0133 */ DEFINE_ACTOR(En_Daiku, ACTOR_EN_DAIKU, ALLOCTYPE_NORMAL) +/* 0x0134 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_134) +/* 0x0135 */ DEFINE_ACTOR(En_Nwc, ACTOR_EN_NWC, ALLOCTYPE_NORMAL) +/* 0x0136 */ DEFINE_ACTOR(En_Blkobj, ACTOR_EN_BLKOBJ, ALLOCTYPE_NORMAL) +/* 0x0137 */ DEFINE_ACTOR(Item_Inbox, ACTOR_ITEM_INBOX, ALLOCTYPE_NORMAL) +/* 0x0138 */ DEFINE_ACTOR(En_Ge1, ACTOR_EN_GE1, ALLOCTYPE_NORMAL) +/* 0x0139 */ DEFINE_ACTOR(Obj_Blockstop, ACTOR_OBJ_BLOCKSTOP, ALLOCTYPE_NORMAL) +/* 0x013A */ DEFINE_ACTOR(En_Sda, ACTOR_EN_SDA, ALLOCTYPE_NORMAL) +/* 0x013B */ DEFINE_ACTOR(En_Clear_Tag, ACTOR_EN_CLEAR_TAG, ALLOCTYPE_NORMAL) +/* 0x013C */ DEFINE_ACTOR(En_Niw_Lady, ACTOR_EN_NIW_LADY, ALLOCTYPE_NORMAL) +/* 0x013D */ DEFINE_ACTOR(En_Gm, ACTOR_EN_GM, ALLOCTYPE_NORMAL) +/* 0x013E */ DEFINE_ACTOR(En_Ms, ACTOR_EN_MS, ALLOCTYPE_NORMAL) +/* 0x013F */ DEFINE_ACTOR(En_Hs, ACTOR_EN_HS, ALLOCTYPE_NORMAL) +/* 0x0140 */ DEFINE_ACTOR(Bg_Ingate, ACTOR_BG_INGATE, ALLOCTYPE_NORMAL) +/* 0x0141 */ DEFINE_ACTOR(En_Kanban, ACTOR_EN_KANBAN, ALLOCTYPE_NORMAL) +/* 0x0142 */ DEFINE_ACTOR(En_Heishi3, ACTOR_EN_HEISHI3, ALLOCTYPE_NORMAL) +/* 0x0143 */ DEFINE_ACTOR(En_Syateki_Niw, ACTOR_EN_SYATEKI_NIW, ALLOCTYPE_NORMAL) +/* 0x0144 */ DEFINE_ACTOR(En_Attack_Niw, ACTOR_EN_ATTACK_NIW, ALLOCTYPE_NORMAL) +/* 0x0145 */ DEFINE_ACTOR(Bg_Spot01_Idosoko, ACTOR_BG_SPOT01_IDOSOKO, ALLOCTYPE_NORMAL) +/* 0x0146 */ DEFINE_ACTOR(En_Sa, ACTOR_EN_SA, ALLOCTYPE_NORMAL) +/* 0x0147 */ DEFINE_ACTOR(En_Wonder_Talk, ACTOR_EN_WONDER_TALK, ALLOCTYPE_NORMAL) +/* 0x0148 */ DEFINE_ACTOR(Bg_Gjyo_Bridge, ACTOR_BG_GJYO_BRIDGE, ALLOCTYPE_NORMAL) +/* 0x0149 */ DEFINE_ACTOR(En_Ds, ACTOR_EN_DS, ALLOCTYPE_NORMAL) +/* 0x014A */ DEFINE_ACTOR(En_Mk, ACTOR_EN_MK, ALLOCTYPE_NORMAL) +/* 0x014B */ DEFINE_ACTOR(En_Bom_Bowl_Man, ACTOR_EN_BOM_BOWL_MAN, ALLOCTYPE_NORMAL) +/* 0x014C */ DEFINE_ACTOR(En_Bom_Bowl_Pit, ACTOR_EN_BOM_BOWL_PIT, ALLOCTYPE_NORMAL) +/* 0x014D */ DEFINE_ACTOR(En_Owl, ACTOR_EN_OWL, ALLOCTYPE_NORMAL) +/* 0x014E */ DEFINE_ACTOR(En_Ishi, ACTOR_EN_ISHI, ALLOCTYPE_NORMAL) +/* 0x014F */ DEFINE_ACTOR(Obj_Hana, ACTOR_OBJ_HANA, ALLOCTYPE_NORMAL) +/* 0x0150 */ DEFINE_ACTOR(Obj_Lightswitch, ACTOR_OBJ_LIGHTSWITCH, ALLOCTYPE_NORMAL) +/* 0x0151 */ DEFINE_ACTOR(Obj_Mure2, ACTOR_OBJ_MURE2, ALLOCTYPE_NORMAL) +/* 0x0152 */ DEFINE_ACTOR(En_Go, ACTOR_EN_GO, ALLOCTYPE_NORMAL) +/* 0x0153 */ DEFINE_ACTOR(En_Fu, ACTOR_EN_FU, ALLOCTYPE_NORMAL) +/* 0x0154 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_154) +/* 0x0155 */ DEFINE_ACTOR(En_Changer, ACTOR_EN_CHANGER, ALLOCTYPE_NORMAL) +/* 0x0156 */ DEFINE_ACTOR(Bg_Jya_Megami, ACTOR_BG_JYA_MEGAMI, ALLOCTYPE_NORMAL) +/* 0x0157 */ DEFINE_ACTOR(Bg_Jya_Lift, ACTOR_BG_JYA_LIFT, ALLOCTYPE_NORMAL) +/* 0x0158 */ DEFINE_ACTOR(Bg_Jya_Bigmirror, ACTOR_BG_JYA_BIGMIRROR, ALLOCTYPE_NORMAL) +/* 0x0159 */ DEFINE_ACTOR(Bg_Jya_Bombchuiwa, ACTOR_BG_JYA_BOMBCHUIWA, ALLOCTYPE_NORMAL) +/* 0x015A */ DEFINE_ACTOR(Bg_Jya_Amishutter, ACTOR_BG_JYA_AMISHUTTER, ALLOCTYPE_NORMAL) +/* 0x015B */ DEFINE_ACTOR(Bg_Jya_Bombiwa, ACTOR_BG_JYA_BOMBIWA, ALLOCTYPE_NORMAL) +/* 0x015C */ DEFINE_ACTOR(Bg_Spot18_Basket, ACTOR_BG_SPOT18_BASKET, ALLOCTYPE_NORMAL) +/* 0x015D */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_15D) +/* 0x015E */ DEFINE_ACTOR(En_Ganon_Organ, ACTOR_EN_GANON_ORGAN, ALLOCTYPE_NORMAL) +/* 0x015F */ DEFINE_ACTOR(En_Siofuki, ACTOR_EN_SIOFUKI, ALLOCTYPE_NORMAL) +/* 0x0160 */ DEFINE_ACTOR(En_Stream, ACTOR_EN_STREAM, ALLOCTYPE_NORMAL) +/* 0x0161 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_161) +/* 0x0162 */ DEFINE_ACTOR(En_Mm, ACTOR_EN_MM, ALLOCTYPE_NORMAL) +/* 0x0163 */ DEFINE_ACTOR(En_Ko, ACTOR_EN_KO, ALLOCTYPE_NORMAL) +/* 0x0164 */ DEFINE_ACTOR(En_Kz, ACTOR_EN_KZ, ALLOCTYPE_NORMAL) +/* 0x0165 */ DEFINE_ACTOR(En_Weather_Tag, ACTOR_EN_WEATHER_TAG, ALLOCTYPE_NORMAL) +/* 0x0166 */ DEFINE_ACTOR(Bg_Sst_Floor, ACTOR_BG_SST_FLOOR, ALLOCTYPE_NORMAL) +/* 0x0167 */ DEFINE_ACTOR(En_Ani, ACTOR_EN_ANI, ALLOCTYPE_NORMAL) +/* 0x0168 */ DEFINE_ACTOR(En_Ex_Item, ACTOR_EN_EX_ITEM, ALLOCTYPE_NORMAL) +/* 0x0169 */ DEFINE_ACTOR(Bg_Jya_Ironobj, ACTOR_BG_JYA_IRONOBJ, ALLOCTYPE_NORMAL) +/* 0x016A */ DEFINE_ACTOR(En_Js, ACTOR_EN_JS, ALLOCTYPE_NORMAL) +/* 0x016B */ DEFINE_ACTOR(En_Jsjutan, ACTOR_EN_JSJUTAN, ALLOCTYPE_NORMAL) +/* 0x016C */ DEFINE_ACTOR(En_Cs, ACTOR_EN_CS, ALLOCTYPE_NORMAL) +/* 0x016D */ DEFINE_ACTOR(En_Md, ACTOR_EN_MD, ALLOCTYPE_NORMAL) +/* 0x016E */ DEFINE_ACTOR(En_Hy, ACTOR_EN_HY, ALLOCTYPE_NORMAL) +/* 0x016F */ DEFINE_ACTOR(En_Ganon_Mant, ACTOR_EN_GANON_MANT, ALLOCTYPE_NORMAL) +/* 0x0170 */ DEFINE_ACTOR(En_Okarina_Effect, ACTOR_EN_OKARINA_EFFECT, ALLOCTYPE_NORMAL) +/* 0x0171 */ DEFINE_ACTOR(En_Mag, ACTOR_EN_MAG, ALLOCTYPE_NORMAL) +/* 0x0172 */ DEFINE_ACTOR(Door_Gerudo, ACTOR_DOOR_GERUDO, ALLOCTYPE_NORMAL) +/* 0x0173 */ DEFINE_ACTOR(Elf_Msg2, ACTOR_ELF_MSG2, ALLOCTYPE_NORMAL) +/* 0x0174 */ DEFINE_ACTOR(Demo_Gt, ACTOR_DEMO_GT, ALLOCTYPE_NORMAL) +/* 0x0175 */ DEFINE_ACTOR(En_Po_Field, ACTOR_EN_PO_FIELD, ALLOCTYPE_NORMAL) +/* 0x0176 */ DEFINE_ACTOR(Efc_Erupc, ACTOR_EFC_ERUPC, ALLOCTYPE_NORMAL) +/* 0x0177 */ DEFINE_ACTOR(Bg_Zg, ACTOR_BG_ZG, ALLOCTYPE_NORMAL) +/* 0x0178 */ DEFINE_ACTOR(En_Heishi4, ACTOR_EN_HEISHI4, ALLOCTYPE_NORMAL) +/* 0x0179 */ DEFINE_ACTOR(En_Zl3, ACTOR_EN_ZL3, ALLOCTYPE_NORMAL) +/* 0x017A */ DEFINE_ACTOR(Boss_Ganon2, ACTOR_BOSS_GANON2, ALLOCTYPE_NORMAL) +/* 0x017B */ DEFINE_ACTOR(En_Kakasi, ACTOR_EN_KAKASI, ALLOCTYPE_NORMAL) +/* 0x017C */ DEFINE_ACTOR(En_Takara_Man, ACTOR_EN_TAKARA_MAN, ALLOCTYPE_NORMAL) +/* 0x017D */ DEFINE_ACTOR(Obj_Makeoshihiki, ACTOR_OBJ_MAKEOSHIHIKI, ALLOCTYPE_NORMAL) +/* 0x017E */ DEFINE_ACTOR(Oceff_Spot, ACTOR_OCEFF_SPOT, ALLOCTYPE_ABSOLUTE) +/* 0x017F */ DEFINE_ACTOR(End_Title, ACTOR_END_TITLE, ALLOCTYPE_NORMAL) +/* 0x0180 */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_180) +/* 0x0181 */ DEFINE_ACTOR(En_Torch, ACTOR_EN_TORCH, ALLOCTYPE_NORMAL) +/* 0x0182 */ DEFINE_ACTOR(Demo_Ec, ACTOR_DEMO_EC, ALLOCTYPE_NORMAL) +/* 0x0183 */ DEFINE_ACTOR(Shot_Sun, ACTOR_SHOT_SUN, ALLOCTYPE_NORMAL) +/* 0x0184 */ DEFINE_ACTOR(En_Dy_Extra, ACTOR_EN_DY_EXTRA, ALLOCTYPE_NORMAL) +/* 0x0185 */ DEFINE_ACTOR(En_Wonder_Talk2, ACTOR_EN_WONDER_TALK2, ALLOCTYPE_NORMAL) +/* 0x0186 */ DEFINE_ACTOR(En_Ge2, ACTOR_EN_GE2, ALLOCTYPE_NORMAL) +/* 0x0187 */ DEFINE_ACTOR(Obj_Roomtimer, ACTOR_OBJ_ROOMTIMER, ALLOCTYPE_NORMAL) +/* 0x0188 */ DEFINE_ACTOR(En_Ssh, ACTOR_EN_SSH, ALLOCTYPE_NORMAL) +/* 0x0189 */ DEFINE_ACTOR(En_Sth, ACTOR_EN_STH, ALLOCTYPE_NORMAL) +/* 0x018A */ DEFINE_ACTOR(Oceff_Wipe, ACTOR_OCEFF_WIPE, ALLOCTYPE_ABSOLUTE) +/* 0x018B */ DEFINE_ACTOR(Oceff_Storm, ACTOR_OCEFF_STORM, ALLOCTYPE_ABSOLUTE) +/* 0x018C */ DEFINE_ACTOR(En_Weiyer, ACTOR_EN_WEIYER, ALLOCTYPE_NORMAL) +/* 0x018D */ DEFINE_ACTOR(Bg_Spot05_Soko, ACTOR_BG_SPOT05_SOKO, ALLOCTYPE_NORMAL) +/* 0x018E */ DEFINE_ACTOR(Bg_Jya_1flift, ACTOR_BG_JYA_1FLIFT, ALLOCTYPE_NORMAL) +/* 0x018F */ DEFINE_ACTOR(Bg_Jya_Haheniron, ACTOR_BG_JYA_HAHENIRON, ALLOCTYPE_NORMAL) +/* 0x0190 */ DEFINE_ACTOR(Bg_Spot12_Gate, ACTOR_BG_SPOT12_GATE, ALLOCTYPE_NORMAL) +/* 0x0191 */ DEFINE_ACTOR(Bg_Spot12_Saku, ACTOR_BG_SPOT12_SAKU, ALLOCTYPE_NORMAL) +/* 0x0192 */ DEFINE_ACTOR(En_Hintnuts, ACTOR_EN_HINTNUTS, ALLOCTYPE_NORMAL) +/* 0x0193 */ DEFINE_ACTOR(En_Nutsball, ACTOR_EN_NUTSBALL, ALLOCTYPE_NORMAL) +/* 0x0194 */ DEFINE_ACTOR(Bg_Spot00_Break, ACTOR_BG_SPOT00_BREAK, ALLOCTYPE_NORMAL) +/* 0x0195 */ DEFINE_ACTOR(En_Shopnuts, ACTOR_EN_SHOPNUTS, ALLOCTYPE_NORMAL) +/* 0x0196 */ DEFINE_ACTOR(En_It, ACTOR_EN_IT, ALLOCTYPE_NORMAL) +/* 0x0197 */ DEFINE_ACTOR(En_GeldB, ACTOR_EN_GELDB, ALLOCTYPE_NORMAL) +/* 0x0198 */ DEFINE_ACTOR(Oceff_Wipe2, ACTOR_OCEFF_WIPE2, ALLOCTYPE_ABSOLUTE) +/* 0x0199 */ DEFINE_ACTOR(Oceff_Wipe3, ACTOR_OCEFF_WIPE3, ALLOCTYPE_ABSOLUTE) +/* 0x019A */ DEFINE_ACTOR(En_Niw_Girl, ACTOR_EN_NIW_GIRL, ALLOCTYPE_NORMAL) +/* 0x019B */ DEFINE_ACTOR(En_Dog, ACTOR_EN_DOG, ALLOCTYPE_NORMAL) +/* 0x019C */ DEFINE_ACTOR(En_Si, ACTOR_EN_SI, ALLOCTYPE_NORMAL) +/* 0x019D */ DEFINE_ACTOR(Bg_Spot01_Objects2, ACTOR_BG_SPOT01_OBJECTS2, ALLOCTYPE_NORMAL) +/* 0x019E */ DEFINE_ACTOR(Obj_Comb, ACTOR_OBJ_COMB, ALLOCTYPE_NORMAL) +/* 0x019F */ DEFINE_ACTOR(Bg_Spot11_Bakudankabe, ACTOR_BG_SPOT11_BAKUDANKABE, ALLOCTYPE_NORMAL) +/* 0x01A0 */ DEFINE_ACTOR(Obj_Kibako2, ACTOR_OBJ_KIBAKO2, ALLOCTYPE_NORMAL) +/* 0x01A1 */ DEFINE_ACTOR(En_Dnt_Demo, ACTOR_EN_DNT_DEMO, ALLOCTYPE_NORMAL) +/* 0x01A2 */ DEFINE_ACTOR(En_Dnt_Jiji, ACTOR_EN_DNT_JIJI, ALLOCTYPE_NORMAL) +/* 0x01A3 */ DEFINE_ACTOR(En_Dnt_Nomal, ACTOR_EN_DNT_NOMAL, ALLOCTYPE_NORMAL) +/* 0x01A4 */ DEFINE_ACTOR(En_Guest, ACTOR_EN_GUEST, ALLOCTYPE_NORMAL) +/* 0x01A5 */ DEFINE_ACTOR(Bg_Bom_Guard, ACTOR_BG_BOM_GUARD, ALLOCTYPE_NORMAL) +/* 0x01A6 */ DEFINE_ACTOR(En_Hs2, ACTOR_EN_HS2, ALLOCTYPE_NORMAL) +/* 0x01A7 */ DEFINE_ACTOR(Demo_Kekkai, ACTOR_DEMO_KEKKAI, ALLOCTYPE_NORMAL) +/* 0x01A8 */ DEFINE_ACTOR(Bg_Spot08_Bakudankabe, ACTOR_BG_SPOT08_BAKUDANKABE, ALLOCTYPE_NORMAL) +/* 0x01A9 */ DEFINE_ACTOR(Bg_Spot17_Bakudankabe, ACTOR_BG_SPOT17_BAKUDANKABE, ALLOCTYPE_NORMAL) +/* 0x01AA */ DEFINE_ACTOR_UNSET(ACTOR_UNSET_1AA) +/* 0x01AB */ DEFINE_ACTOR(Obj_Mure3, ACTOR_OBJ_MURE3, ALLOCTYPE_NORMAL) +/* 0x01AC */ DEFINE_ACTOR(En_Tg, ACTOR_EN_TG, ALLOCTYPE_NORMAL) +/* 0x01AD */ DEFINE_ACTOR(En_Mu, ACTOR_EN_MU, ALLOCTYPE_NORMAL) +/* 0x01AE */ DEFINE_ACTOR(En_Go2, ACTOR_EN_GO2, ALLOCTYPE_NORMAL) +/* 0x01AF */ DEFINE_ACTOR(En_Wf, ACTOR_EN_WF, ALLOCTYPE_NORMAL) +/* 0x01B0 */ DEFINE_ACTOR(En_Skb, ACTOR_EN_SKB, ALLOCTYPE_NORMAL) +/* 0x01B1 */ DEFINE_ACTOR(Demo_Gj, ACTOR_DEMO_GJ, ALLOCTYPE_NORMAL) +/* 0x01B2 */ DEFINE_ACTOR(Demo_Geff, ACTOR_DEMO_GEFF, ALLOCTYPE_NORMAL) +/* 0x01B3 */ DEFINE_ACTOR(Bg_Gnd_Firemeiro, ACTOR_BG_GND_FIREMEIRO, ALLOCTYPE_NORMAL) +/* 0x01B4 */ DEFINE_ACTOR(Bg_Gnd_Darkmeiro, ACTOR_BG_GND_DARKMEIRO, ALLOCTYPE_NORMAL) +/* 0x01B5 */ DEFINE_ACTOR(Bg_Gnd_Soulmeiro, ACTOR_BG_GND_SOULMEIRO, ALLOCTYPE_NORMAL) +/* 0x01B6 */ DEFINE_ACTOR(Bg_Gnd_Nisekabe, ACTOR_BG_GND_NISEKABE, ALLOCTYPE_NORMAL) +/* 0x01B7 */ DEFINE_ACTOR(Bg_Gnd_Iceblock, ACTOR_BG_GND_ICEBLOCK, ALLOCTYPE_NORMAL) +/* 0x01B8 */ DEFINE_ACTOR(En_Gb, ACTOR_EN_GB, ALLOCTYPE_NORMAL) +/* 0x01B9 */ DEFINE_ACTOR(En_Gs, ACTOR_EN_GS, ALLOCTYPE_NORMAL) +/* 0x01BA */ DEFINE_ACTOR(Bg_Mizu_Bwall, ACTOR_BG_MIZU_BWALL, ALLOCTYPE_NORMAL) +/* 0x01BB */ DEFINE_ACTOR(Bg_Mizu_Shutter, ACTOR_BG_MIZU_SHUTTER, ALLOCTYPE_NORMAL) +/* 0x01BC */ DEFINE_ACTOR(En_Daiku_Kakariko, ACTOR_EN_DAIKU_KAKARIKO, ALLOCTYPE_NORMAL) +/* 0x01BD */ DEFINE_ACTOR(Bg_Bowl_Wall, ACTOR_BG_BOWL_WALL, ALLOCTYPE_NORMAL) +/* 0x01BE */ DEFINE_ACTOR(En_Wall_Tubo, ACTOR_EN_WALL_TUBO, ALLOCTYPE_NORMAL) +/* 0x01BF */ DEFINE_ACTOR(En_Po_Desert, ACTOR_EN_PO_DESERT, ALLOCTYPE_NORMAL) +/* 0x01C0 */ DEFINE_ACTOR(En_Crow, ACTOR_EN_CROW, ALLOCTYPE_NORMAL) +/* 0x01C1 */ DEFINE_ACTOR(Door_Killer, ACTOR_DOOR_KILLER, ALLOCTYPE_NORMAL) +/* 0x01C2 */ DEFINE_ACTOR(Bg_Spot11_Oasis, ACTOR_BG_SPOT11_OASIS, ALLOCTYPE_NORMAL) +/* 0x01C3 */ DEFINE_ACTOR(Bg_Spot18_Futa, ACTOR_BG_SPOT18_FUTA, ALLOCTYPE_NORMAL) +/* 0x01C4 */ DEFINE_ACTOR(Bg_Spot18_Shutter, ACTOR_BG_SPOT18_SHUTTER, ALLOCTYPE_NORMAL) +/* 0x01C5 */ DEFINE_ACTOR(En_Ma3, ACTOR_EN_MA3, ALLOCTYPE_NORMAL) +/* 0x01C6 */ DEFINE_ACTOR(En_Cow, ACTOR_EN_COW, ALLOCTYPE_NORMAL) +/* 0x01C7 */ DEFINE_ACTOR(Bg_Ice_Turara, ACTOR_BG_ICE_TURARA, ALLOCTYPE_NORMAL) +/* 0x01C8 */ DEFINE_ACTOR(Bg_Ice_Shutter, ACTOR_BG_ICE_SHUTTER, ALLOCTYPE_NORMAL) +/* 0x01C9 */ DEFINE_ACTOR(En_Kakasi2, ACTOR_EN_KAKASI2, ALLOCTYPE_NORMAL) +/* 0x01CA */ DEFINE_ACTOR(En_Kakasi3, ACTOR_EN_KAKASI3, ALLOCTYPE_NORMAL) +/* 0x01CB */ DEFINE_ACTOR(Oceff_Wipe4, ACTOR_OCEFF_WIPE4, ALLOCTYPE_ABSOLUTE) +/* 0x01CC */ DEFINE_ACTOR(En_Eg, ACTOR_EN_EG, ALLOCTYPE_NORMAL) +/* 0x01CD */ DEFINE_ACTOR(Bg_Menkuri_Nisekabe, ACTOR_BG_MENKURI_NISEKABE, ALLOCTYPE_NORMAL) +/* 0x01CE */ DEFINE_ACTOR(En_Zo, ACTOR_EN_ZO, ALLOCTYPE_NORMAL) +/* 0x01CF */ DEFINE_ACTOR(Obj_Makekinsuta, ACTOR_OBJ_MAKEKINSUTA, ALLOCTYPE_NORMAL) +/* 0x01D0 */ DEFINE_ACTOR(En_Ge3, ACTOR_EN_GE3, ALLOCTYPE_NORMAL) +/* 0x01D1 */ DEFINE_ACTOR(Obj_Timeblock, ACTOR_OBJ_TIMEBLOCK, ALLOCTYPE_NORMAL) +/* 0x01D2 */ DEFINE_ACTOR(Obj_Hamishi, ACTOR_OBJ_HAMISHI, ALLOCTYPE_NORMAL) +/* 0x01D3 */ DEFINE_ACTOR(En_Zl4, ACTOR_EN_ZL4, ALLOCTYPE_NORMAL) +/* 0x01D4 */ DEFINE_ACTOR(En_Mm2, ACTOR_EN_MM2, ALLOCTYPE_NORMAL) +/* 0x01D5 */ DEFINE_ACTOR(Bg_Jya_Block, ACTOR_BG_JYA_BLOCK, ALLOCTYPE_NORMAL) +/* 0x01D6 */ DEFINE_ACTOR(Obj_Warp2block, ACTOR_OBJ_WARP2BLOCK, ALLOCTYPE_NORMAL) diff --git a/soh/include/tables/dmadata_table.h b/soh/include/tables/dmadata_table.h new file mode 100644 index 000000000..09ba92c00 --- /dev/null +++ b/soh/include/tables/dmadata_table.h @@ -0,0 +1,9 @@ +/** + * Select dmadata table for version + */ +#ifdef NON_MATCHING +// For non-matching builds, dmadata is generated from the specfile segments +#include "dmadata_table_spec.h" +#else +#include "tables/dmadata_table_mqdbg.h" +#endif diff --git a/soh/include/tables/dmadata_table_mqdbg.h b/soh/include/tables/dmadata_table_mqdbg.h new file mode 100644 index 000000000..a2c5279be --- /dev/null +++ b/soh/include/tables/dmadata_table_mqdbg.h @@ -0,0 +1,1535 @@ +/** + * Matching dmadata layout for PAL MQ Debug + */ +DEFINE_DMA_ENTRY(makerom) +DEFINE_DMA_ENTRY(boot) +DEFINE_DMA_ENTRY(dmadata) +DEFINE_DMA_ENTRY(Audiobank) +DEFINE_DMA_ENTRY(Audioseq) +DEFINE_DMA_ENTRY(Audiotable) +DEFINE_DMA_ENTRY(link_animetion) +DEFINE_DMA_ENTRY(icon_item_static) +DEFINE_DMA_ENTRY(icon_item_24_static) +DEFINE_DMA_ENTRY(icon_item_field_static) +DEFINE_DMA_ENTRY(icon_item_dungeon_static) +DEFINE_DMA_ENTRY(icon_item_gameover_static) +DEFINE_DMA_ENTRY(icon_item_nes_static) +DEFINE_DMA_ENTRY(icon_item_ger_static) +DEFINE_DMA_ENTRY(icon_item_fra_static) +DEFINE_DMA_ENTRY(item_name_static) +DEFINE_DMA_ENTRY(map_name_static) +DEFINE_DMA_ENTRY(do_action_static) +DEFINE_DMA_ENTRY(message_static) +DEFINE_DMA_ENTRY(message_texture_static) +DEFINE_DMA_ENTRY(nes_font_static) +DEFINE_DMA_ENTRY(nes_message_data_static) +DEFINE_DMA_ENTRY(ger_message_data_static) +DEFINE_DMA_ENTRY(fra_message_data_static) +DEFINE_DMA_ENTRY(staff_message_data_static) +DEFINE_DMA_ENTRY(map_grand_static) +DEFINE_DMA_ENTRY(map_i_static) +DEFINE_DMA_ENTRY(map_48x85_static) +DEFINE_DMA_ENTRY(code) +DEFINE_DMA_ENTRY(ovl_title) +DEFINE_DMA_ENTRY(ovl_select) +DEFINE_DMA_ENTRY(ovl_opening) +DEFINE_DMA_ENTRY(ovl_file_choose) +DEFINE_DMA_ENTRY(ovl_kaleido_scope) +DEFINE_DMA_ENTRY(ovl_player_actor) +DEFINE_DMA_ENTRY(ovl_map_mark_data) +DEFINE_DMA_ENTRY(ovl_En_Test) +DEFINE_DMA_ENTRY(ovl_Arms_Hook) +DEFINE_DMA_ENTRY(ovl_Arrow_Fire) +DEFINE_DMA_ENTRY(ovl_Arrow_Ice) +DEFINE_DMA_ENTRY(ovl_Arrow_Light) +DEFINE_DMA_ENTRY(ovl_Bg_Bdan_Objects) +DEFINE_DMA_ENTRY(ovl_Bg_Bdan_Switch) +DEFINE_DMA_ENTRY(ovl_Bg_Bom_Guard) +DEFINE_DMA_ENTRY(ovl_Bg_Bombwall) +DEFINE_DMA_ENTRY(ovl_Bg_Bowl_Wall) +DEFINE_DMA_ENTRY(ovl_Bg_Breakwall) +DEFINE_DMA_ENTRY(ovl_Bg_Ddan_Jd) +DEFINE_DMA_ENTRY(ovl_Bg_Ddan_Kd) +DEFINE_DMA_ENTRY(ovl_Bg_Dodoago) +DEFINE_DMA_ENTRY(ovl_Bg_Dy_Yoseizo) +DEFINE_DMA_ENTRY(ovl_Bg_Ganon_Otyuka) +DEFINE_DMA_ENTRY(ovl_Bg_Gate_Shutter) +DEFINE_DMA_ENTRY(ovl_Bg_Gjyo_Bridge) +DEFINE_DMA_ENTRY(ovl_Bg_Gnd_Darkmeiro) +DEFINE_DMA_ENTRY(ovl_Bg_Gnd_Firemeiro) +DEFINE_DMA_ENTRY(ovl_Bg_Gnd_Iceblock) +DEFINE_DMA_ENTRY(ovl_Bg_Gnd_Nisekabe) +DEFINE_DMA_ENTRY(ovl_Bg_Gnd_Soulmeiro) +DEFINE_DMA_ENTRY(ovl_Bg_Haka) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Gate) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Huta) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Megane) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_MeganeBG) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Sgami) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Ship) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Trap) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Tubo) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Water) +DEFINE_DMA_ENTRY(ovl_Bg_Haka_Zou) +DEFINE_DMA_ENTRY(ovl_Bg_Heavy_Block) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Curtain) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Dalm) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Firewall) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Fslift) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Fwbig) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Hamstep) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Hrock) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Kousi) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Kowarerukabe) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Rock) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Rsekizou) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Sekizou) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Sima) +DEFINE_DMA_ENTRY(ovl_Bg_Hidan_Syoku) +DEFINE_DMA_ENTRY(ovl_Bg_Ice_Objects) +DEFINE_DMA_ENTRY(ovl_Bg_Ice_Shelter) +DEFINE_DMA_ENTRY(ovl_Bg_Ice_Shutter) +DEFINE_DMA_ENTRY(ovl_Bg_Ice_Turara) +DEFINE_DMA_ENTRY(ovl_Bg_Ingate) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_1flift) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Amishutter) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Bigmirror) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Block) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Bombchuiwa) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Bombiwa) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Cobra) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Goroiwa) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Haheniron) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Ironobj) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Kanaami) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Lift) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Megami) +DEFINE_DMA_ENTRY(ovl_Bg_Jya_Zurerukabe) +DEFINE_DMA_ENTRY(ovl_Bg_Menkuri_Eye) +DEFINE_DMA_ENTRY(ovl_Bg_Menkuri_Kaiten) +DEFINE_DMA_ENTRY(ovl_Bg_Menkuri_Nisekabe) +DEFINE_DMA_ENTRY(ovl_Bg_Mizu_Bwall) +DEFINE_DMA_ENTRY(ovl_Bg_Mizu_Movebg) +DEFINE_DMA_ENTRY(ovl_Bg_Mizu_Shutter) +DEFINE_DMA_ENTRY(ovl_Bg_Mizu_Uzu) +DEFINE_DMA_ENTRY(ovl_Bg_Mizu_Water) +DEFINE_DMA_ENTRY(ovl_Bg_Mjin) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Bigst) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Elevator) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Hashigo) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Hashira4) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Hineri) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Idomizu) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Kaitenkabe) +DEFINE_DMA_ENTRY(ovl_Bg_Mori_Rakkatenjo) +DEFINE_DMA_ENTRY(ovl_Bg_Po_Event) +DEFINE_DMA_ENTRY(ovl_Bg_Po_Syokudai) +DEFINE_DMA_ENTRY(ovl_Bg_Pushbox) +DEFINE_DMA_ENTRY(ovl_Bg_Relay_Objects) +DEFINE_DMA_ENTRY(ovl_Bg_Spot00_Break) +DEFINE_DMA_ENTRY(ovl_Bg_Spot00_Hanebasi) +DEFINE_DMA_ENTRY(ovl_Bg_Spot01_Fusya) +DEFINE_DMA_ENTRY(ovl_Bg_Spot01_Idohashira) +DEFINE_DMA_ENTRY(ovl_Bg_Spot01_Idomizu) +DEFINE_DMA_ENTRY(ovl_Bg_Spot01_Idosoko) +DEFINE_DMA_ENTRY(ovl_Bg_Spot01_Objects2) +DEFINE_DMA_ENTRY(ovl_Bg_Spot02_Objects) +DEFINE_DMA_ENTRY(ovl_Bg_Spot03_Taki) +DEFINE_DMA_ENTRY(ovl_Bg_Spot05_Soko) +DEFINE_DMA_ENTRY(ovl_Bg_Spot06_Objects) +DEFINE_DMA_ENTRY(ovl_Bg_Spot07_Taki) +DEFINE_DMA_ENTRY(ovl_Bg_Spot08_Bakudankabe) +DEFINE_DMA_ENTRY(ovl_Bg_Spot08_Iceblock) +DEFINE_DMA_ENTRY(ovl_Bg_Spot09_Obj) +DEFINE_DMA_ENTRY(ovl_Bg_Spot11_Bakudankabe) +DEFINE_DMA_ENTRY(ovl_Bg_Spot11_Oasis) +DEFINE_DMA_ENTRY(ovl_Bg_Spot12_Gate) +DEFINE_DMA_ENTRY(ovl_Bg_Spot12_Saku) +DEFINE_DMA_ENTRY(ovl_Bg_Spot15_Rrbox) +DEFINE_DMA_ENTRY(ovl_Bg_Spot15_Saku) +DEFINE_DMA_ENTRY(ovl_Bg_Spot16_Bombstone) +DEFINE_DMA_ENTRY(ovl_Bg_Spot16_Doughnut) +DEFINE_DMA_ENTRY(ovl_Bg_Spot17_Bakudankabe) +DEFINE_DMA_ENTRY(ovl_Bg_Spot17_Funen) +DEFINE_DMA_ENTRY(ovl_Bg_Spot18_Basket) +DEFINE_DMA_ENTRY(ovl_Bg_Spot18_Futa) +DEFINE_DMA_ENTRY(ovl_Bg_Spot18_Obj) +DEFINE_DMA_ENTRY(ovl_Bg_Spot18_Shutter) +DEFINE_DMA_ENTRY(ovl_Bg_Sst_Floor) +DEFINE_DMA_ENTRY(ovl_Bg_Toki_Hikari) +DEFINE_DMA_ENTRY(ovl_Bg_Toki_Swd) +DEFINE_DMA_ENTRY(ovl_Bg_Treemouth) +DEFINE_DMA_ENTRY(ovl_Bg_Umajump) +DEFINE_DMA_ENTRY(ovl_Bg_Vb_Sima) +DEFINE_DMA_ENTRY(ovl_Bg_Ydan_Hasi) +DEFINE_DMA_ENTRY(ovl_Bg_Ydan_Maruta) +DEFINE_DMA_ENTRY(ovl_Bg_Ydan_Sp) +DEFINE_DMA_ENTRY(ovl_Bg_Zg) +DEFINE_DMA_ENTRY(ovl_Boss_Dodongo) +DEFINE_DMA_ENTRY(ovl_Boss_Fd) +DEFINE_DMA_ENTRY(ovl_Boss_Fd2) +DEFINE_DMA_ENTRY(ovl_Boss_Ganon) +DEFINE_DMA_ENTRY(ovl_Boss_Ganon2) +DEFINE_DMA_ENTRY(ovl_Boss_Ganondrof) +DEFINE_DMA_ENTRY(ovl_Boss_Goma) +DEFINE_DMA_ENTRY(ovl_Boss_Mo) +DEFINE_DMA_ENTRY(ovl_Boss_Sst) +DEFINE_DMA_ENTRY(ovl_Boss_Tw) +DEFINE_DMA_ENTRY(ovl_Boss_Va) +DEFINE_DMA_ENTRY(ovl_Demo_6K) +DEFINE_DMA_ENTRY(ovl_Demo_Du) +DEFINE_DMA_ENTRY(ovl_Demo_Ec) +DEFINE_DMA_ENTRY(ovl_Demo_Effect) +DEFINE_DMA_ENTRY(ovl_Demo_Ext) +DEFINE_DMA_ENTRY(ovl_Demo_Geff) +DEFINE_DMA_ENTRY(ovl_Demo_Gj) +DEFINE_DMA_ENTRY(ovl_Demo_Go) +DEFINE_DMA_ENTRY(ovl_Demo_Gt) +DEFINE_DMA_ENTRY(ovl_Demo_Ik) +DEFINE_DMA_ENTRY(ovl_Demo_Im) +DEFINE_DMA_ENTRY(ovl_Demo_Kankyo) +DEFINE_DMA_ENTRY(ovl_Demo_Kekkai) +DEFINE_DMA_ENTRY(ovl_Demo_Sa) +DEFINE_DMA_ENTRY(ovl_Demo_Shd) +DEFINE_DMA_ENTRY(ovl_Demo_Tre_Lgt) +DEFINE_DMA_ENTRY(ovl_Door_Ana) +DEFINE_DMA_ENTRY(ovl_Door_Gerudo) +DEFINE_DMA_ENTRY(ovl_Door_Killer) +DEFINE_DMA_ENTRY(ovl_Door_Shutter) +DEFINE_DMA_ENTRY(ovl_Door_Toki) +DEFINE_DMA_ENTRY(ovl_Door_Warp1) +DEFINE_DMA_ENTRY(ovl_Efc_Erupc) +DEFINE_DMA_ENTRY(ovl_Eff_Dust) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Blast) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Bomb) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Bomb2) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Bubble) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_D_Fire) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Dead_Db) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Dead_Dd) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Dead_Ds) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Dead_Sound) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Dt_Bubble) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Dust) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_En_Fire) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_En_Ice) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Extra) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Fcircle) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Fhg_Flash) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Fire_Tail) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_G_Fire) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_G_Magma) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_G_Magma2) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_G_Ripple) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_G_Spk) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_G_Splash) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Hahen) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_HitMark) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Ice_Piece) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Ice_Smoke) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_K_Fire) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Kakera) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_KiraKira) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Lightning) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Sibuki) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Sibuki2) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Solder_Srch_Ball) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Stick) +DEFINE_DMA_ENTRY(ovl_Effect_Ss_Stone1) +DEFINE_DMA_ENTRY(ovl_Elf_Msg) +DEFINE_DMA_ENTRY(ovl_Elf_Msg2) +DEFINE_DMA_ENTRY(ovl_En_Am) +DEFINE_DMA_ENTRY(ovl_En_Ani) +DEFINE_DMA_ENTRY(ovl_En_Anubice) +DEFINE_DMA_ENTRY(ovl_En_Anubice_Fire) +DEFINE_DMA_ENTRY(ovl_En_Anubice_Tag) +DEFINE_DMA_ENTRY(ovl_En_Arow_Trap) +DEFINE_DMA_ENTRY(ovl_En_Arrow) +DEFINE_DMA_ENTRY(ovl_En_Attack_Niw) +DEFINE_DMA_ENTRY(ovl_En_Ba) +DEFINE_DMA_ENTRY(ovl_En_Bb) +DEFINE_DMA_ENTRY(ovl_En_Bdfire) +DEFINE_DMA_ENTRY(ovl_En_Bigokuta) +DEFINE_DMA_ENTRY(ovl_En_Bili) +DEFINE_DMA_ENTRY(ovl_En_Bird) +DEFINE_DMA_ENTRY(ovl_En_Blkobj) +DEFINE_DMA_ENTRY(ovl_En_Bom) +DEFINE_DMA_ENTRY(ovl_En_Bom_Bowl_Man) +DEFINE_DMA_ENTRY(ovl_En_Bom_Bowl_Pit) +DEFINE_DMA_ENTRY(ovl_En_Bom_Chu) +DEFINE_DMA_ENTRY(ovl_En_Bombf) +DEFINE_DMA_ENTRY(ovl_En_Boom) +DEFINE_DMA_ENTRY(ovl_En_Box) +DEFINE_DMA_ENTRY(ovl_En_Brob) +DEFINE_DMA_ENTRY(ovl_En_Bubble) +DEFINE_DMA_ENTRY(ovl_En_Butte) +DEFINE_DMA_ENTRY(ovl_En_Bw) +DEFINE_DMA_ENTRY(ovl_En_Bx) +DEFINE_DMA_ENTRY(ovl_En_Changer) +DEFINE_DMA_ENTRY(ovl_En_Clear_Tag) +DEFINE_DMA_ENTRY(ovl_En_Cow) +DEFINE_DMA_ENTRY(ovl_En_Crow) +DEFINE_DMA_ENTRY(ovl_En_Cs) +DEFINE_DMA_ENTRY(ovl_En_Daiku) +DEFINE_DMA_ENTRY(ovl_En_Daiku_Kakariko) +DEFINE_DMA_ENTRY(ovl_En_Dekubaba) +DEFINE_DMA_ENTRY(ovl_En_Dekunuts) +DEFINE_DMA_ENTRY(ovl_En_Dh) +DEFINE_DMA_ENTRY(ovl_En_Dha) +DEFINE_DMA_ENTRY(ovl_En_Diving_Game) +DEFINE_DMA_ENTRY(ovl_En_Dns) +DEFINE_DMA_ENTRY(ovl_En_Dnt_Demo) +DEFINE_DMA_ENTRY(ovl_En_Dnt_Jiji) +DEFINE_DMA_ENTRY(ovl_En_Dnt_Nomal) +DEFINE_DMA_ENTRY(ovl_En_Dodojr) +DEFINE_DMA_ENTRY(ovl_En_Dodongo) +DEFINE_DMA_ENTRY(ovl_En_Dog) +DEFINE_DMA_ENTRY(ovl_En_Door) +DEFINE_DMA_ENTRY(ovl_En_Ds) +DEFINE_DMA_ENTRY(ovl_En_Du) +DEFINE_DMA_ENTRY(ovl_En_Dy_Extra) +DEFINE_DMA_ENTRY(ovl_En_Eg) +DEFINE_DMA_ENTRY(ovl_En_Eiyer) +DEFINE_DMA_ENTRY(ovl_En_Elf) +DEFINE_DMA_ENTRY(ovl_En_Encount1) +DEFINE_DMA_ENTRY(ovl_En_Encount2) +DEFINE_DMA_ENTRY(ovl_En_Ex_Item) +DEFINE_DMA_ENTRY(ovl_En_Ex_Ruppy) +DEFINE_DMA_ENTRY(ovl_En_Fd) +DEFINE_DMA_ENTRY(ovl_En_Fd_Fire) +DEFINE_DMA_ENTRY(ovl_En_Fhg_Fire) +DEFINE_DMA_ENTRY(ovl_En_Fire_Rock) +DEFINE_DMA_ENTRY(ovl_En_Firefly) +DEFINE_DMA_ENTRY(ovl_En_Fish) +DEFINE_DMA_ENTRY(ovl_En_Floormas) +DEFINE_DMA_ENTRY(ovl_En_Fr) +DEFINE_DMA_ENTRY(ovl_En_Fu) +DEFINE_DMA_ENTRY(ovl_En_Fw) +DEFINE_DMA_ENTRY(ovl_En_Fz) +DEFINE_DMA_ENTRY(ovl_En_G_Switch) +DEFINE_DMA_ENTRY(ovl_En_Ganon_Mant) +DEFINE_DMA_ENTRY(ovl_En_Ganon_Organ) +DEFINE_DMA_ENTRY(ovl_En_Gb) +DEFINE_DMA_ENTRY(ovl_En_Ge1) +DEFINE_DMA_ENTRY(ovl_En_Ge2) +DEFINE_DMA_ENTRY(ovl_En_Ge3) +DEFINE_DMA_ENTRY(ovl_En_GeldB) +DEFINE_DMA_ENTRY(ovl_En_GirlA) +DEFINE_DMA_ENTRY(ovl_En_Gm) +DEFINE_DMA_ENTRY(ovl_En_Go) +DEFINE_DMA_ENTRY(ovl_En_Go2) +DEFINE_DMA_ENTRY(ovl_En_Goma) +DEFINE_DMA_ENTRY(ovl_En_Goroiwa) +DEFINE_DMA_ENTRY(ovl_En_Gs) +DEFINE_DMA_ENTRY(ovl_En_Guest) +DEFINE_DMA_ENTRY(ovl_En_Hata) +DEFINE_DMA_ENTRY(ovl_En_Heishi1) +DEFINE_DMA_ENTRY(ovl_En_Heishi2) +DEFINE_DMA_ENTRY(ovl_En_Heishi3) +DEFINE_DMA_ENTRY(ovl_En_Heishi4) +DEFINE_DMA_ENTRY(ovl_En_Hintnuts) +DEFINE_DMA_ENTRY(ovl_En_Holl) +DEFINE_DMA_ENTRY(ovl_En_Honotrap) +DEFINE_DMA_ENTRY(ovl_En_Horse) +DEFINE_DMA_ENTRY(ovl_En_Horse_Game_Check) +DEFINE_DMA_ENTRY(ovl_En_Horse_Ganon) +DEFINE_DMA_ENTRY(ovl_En_Horse_Link_Child) +DEFINE_DMA_ENTRY(ovl_En_Horse_Normal) +DEFINE_DMA_ENTRY(ovl_En_Horse_Zelda) +DEFINE_DMA_ENTRY(ovl_En_Hs) +DEFINE_DMA_ENTRY(ovl_En_Hs2) +DEFINE_DMA_ENTRY(ovl_En_Hy) +DEFINE_DMA_ENTRY(ovl_En_Ice_Hono) +DEFINE_DMA_ENTRY(ovl_En_Ik) +DEFINE_DMA_ENTRY(ovl_En_In) +DEFINE_DMA_ENTRY(ovl_En_Insect) +DEFINE_DMA_ENTRY(ovl_En_Ishi) +DEFINE_DMA_ENTRY(ovl_En_It) +DEFINE_DMA_ENTRY(ovl_En_Jj) +DEFINE_DMA_ENTRY(ovl_En_Js) +DEFINE_DMA_ENTRY(ovl_En_Jsjutan) +DEFINE_DMA_ENTRY(ovl_En_Kakasi) +DEFINE_DMA_ENTRY(ovl_En_Kakasi2) +DEFINE_DMA_ENTRY(ovl_En_Kakasi3) +DEFINE_DMA_ENTRY(ovl_En_Kanban) +DEFINE_DMA_ENTRY(ovl_En_Karebaba) +DEFINE_DMA_ENTRY(ovl_En_Ko) +DEFINE_DMA_ENTRY(ovl_En_Kusa) +DEFINE_DMA_ENTRY(ovl_En_Kz) +DEFINE_DMA_ENTRY(ovl_En_Light) +DEFINE_DMA_ENTRY(ovl_En_Lightbox) +DEFINE_DMA_ENTRY(ovl_En_M_Fire1) +DEFINE_DMA_ENTRY(ovl_En_M_Thunder) +DEFINE_DMA_ENTRY(ovl_En_Ma1) +DEFINE_DMA_ENTRY(ovl_En_Ma2) +DEFINE_DMA_ENTRY(ovl_En_Ma3) +DEFINE_DMA_ENTRY(ovl_En_Mag) +DEFINE_DMA_ENTRY(ovl_En_Mb) +DEFINE_DMA_ENTRY(ovl_En_Md) +DEFINE_DMA_ENTRY(ovl_En_Mk) +DEFINE_DMA_ENTRY(ovl_En_Mm) +DEFINE_DMA_ENTRY(ovl_En_Mm2) +DEFINE_DMA_ENTRY(ovl_En_Ms) +DEFINE_DMA_ENTRY(ovl_En_Mu) +DEFINE_DMA_ENTRY(ovl_En_Nb) +DEFINE_DMA_ENTRY(ovl_En_Niw) +DEFINE_DMA_ENTRY(ovl_En_Niw_Girl) +DEFINE_DMA_ENTRY(ovl_En_Niw_Lady) +DEFINE_DMA_ENTRY(ovl_En_Nutsball) +DEFINE_DMA_ENTRY(ovl_En_Nwc) +DEFINE_DMA_ENTRY(ovl_En_Ny) +DEFINE_DMA_ENTRY(ovl_En_OE2) +DEFINE_DMA_ENTRY(ovl_En_Okarina_Effect) +DEFINE_DMA_ENTRY(ovl_En_Okarina_Tag) +DEFINE_DMA_ENTRY(ovl_En_Okuta) +DEFINE_DMA_ENTRY(ovl_En_Ossan) +DEFINE_DMA_ENTRY(ovl_En_Owl) +DEFINE_DMA_ENTRY(ovl_En_Part) +DEFINE_DMA_ENTRY(ovl_En_Peehat) +DEFINE_DMA_ENTRY(ovl_En_Po_Desert) +DEFINE_DMA_ENTRY(ovl_En_Po_Field) +DEFINE_DMA_ENTRY(ovl_En_Po_Relay) +DEFINE_DMA_ENTRY(ovl_En_Po_Sisters) +DEFINE_DMA_ENTRY(ovl_En_Poh) +DEFINE_DMA_ENTRY(ovl_En_Pu_box) +DEFINE_DMA_ENTRY(ovl_En_Rd) +DEFINE_DMA_ENTRY(ovl_En_Reeba) +DEFINE_DMA_ENTRY(ovl_En_River_Sound) +DEFINE_DMA_ENTRY(ovl_En_Rl) +DEFINE_DMA_ENTRY(ovl_En_Rr) +DEFINE_DMA_ENTRY(ovl_En_Ru1) +DEFINE_DMA_ENTRY(ovl_En_Ru2) +DEFINE_DMA_ENTRY(ovl_En_Sa) +DEFINE_DMA_ENTRY(ovl_En_Sb) +DEFINE_DMA_ENTRY(ovl_En_Scene_Change) +DEFINE_DMA_ENTRY(ovl_En_Sda) +DEFINE_DMA_ENTRY(ovl_En_Shopnuts) +DEFINE_DMA_ENTRY(ovl_En_Si) +DEFINE_DMA_ENTRY(ovl_En_Siofuki) +DEFINE_DMA_ENTRY(ovl_En_Skb) +DEFINE_DMA_ENTRY(ovl_En_Skj) +DEFINE_DMA_ENTRY(ovl_En_Skjneedle) +DEFINE_DMA_ENTRY(ovl_En_Ssh) +DEFINE_DMA_ENTRY(ovl_En_St) +DEFINE_DMA_ENTRY(ovl_En_Sth) +DEFINE_DMA_ENTRY(ovl_En_Stream) +DEFINE_DMA_ENTRY(ovl_En_Sw) +DEFINE_DMA_ENTRY(ovl_En_Syateki_Itm) +DEFINE_DMA_ENTRY(ovl_En_Syateki_Man) +DEFINE_DMA_ENTRY(ovl_En_Syateki_Niw) +DEFINE_DMA_ENTRY(ovl_En_Ta) +DEFINE_DMA_ENTRY(ovl_En_Takara_Man) +DEFINE_DMA_ENTRY(ovl_En_Tana) +DEFINE_DMA_ENTRY(ovl_En_Tg) +DEFINE_DMA_ENTRY(ovl_En_Tite) +DEFINE_DMA_ENTRY(ovl_En_Tk) +DEFINE_DMA_ENTRY(ovl_En_Torch) +DEFINE_DMA_ENTRY(ovl_En_Torch2) +DEFINE_DMA_ENTRY(ovl_En_Toryo) +DEFINE_DMA_ENTRY(ovl_En_Tp) +DEFINE_DMA_ENTRY(ovl_En_Tr) +DEFINE_DMA_ENTRY(ovl_En_Trap) +DEFINE_DMA_ENTRY(ovl_En_Tubo_Trap) +DEFINE_DMA_ENTRY(ovl_En_Vali) +DEFINE_DMA_ENTRY(ovl_En_Vase) +DEFINE_DMA_ENTRY(ovl_En_Vb_Ball) +DEFINE_DMA_ENTRY(ovl_En_Viewer) +DEFINE_DMA_ENTRY(ovl_En_Vm) +DEFINE_DMA_ENTRY(ovl_En_Wall_Tubo) +DEFINE_DMA_ENTRY(ovl_En_Wallmas) +DEFINE_DMA_ENTRY(ovl_En_Weather_Tag) +DEFINE_DMA_ENTRY(ovl_En_Weiyer) +DEFINE_DMA_ENTRY(ovl_En_Wf) +DEFINE_DMA_ENTRY(ovl_En_Wonder_Item) +DEFINE_DMA_ENTRY(ovl_En_Wonder_Talk) +DEFINE_DMA_ENTRY(ovl_En_Wonder_Talk2) +DEFINE_DMA_ENTRY(ovl_En_Wood02) +DEFINE_DMA_ENTRY(ovl_En_Xc) +DEFINE_DMA_ENTRY(ovl_En_Yabusame_Mark) +DEFINE_DMA_ENTRY(ovl_En_Yukabyun) +DEFINE_DMA_ENTRY(ovl_En_Zf) +DEFINE_DMA_ENTRY(ovl_En_Zl1) +DEFINE_DMA_ENTRY(ovl_En_Zl2) +DEFINE_DMA_ENTRY(ovl_En_Zl3) +DEFINE_DMA_ENTRY(ovl_En_Zl4) +DEFINE_DMA_ENTRY(ovl_En_Zo) +DEFINE_DMA_ENTRY(ovl_En_fHG) +DEFINE_DMA_ENTRY(ovl_End_Title) +DEFINE_DMA_ENTRY(ovl_Fishing) +DEFINE_DMA_ENTRY(ovl_Item_B_Heart) +DEFINE_DMA_ENTRY(ovl_Item_Etcetera) +DEFINE_DMA_ENTRY(ovl_Item_Inbox) +DEFINE_DMA_ENTRY(ovl_Item_Ocarina) +DEFINE_DMA_ENTRY(ovl_Item_Shield) +DEFINE_DMA_ENTRY(ovl_Magic_Dark) +DEFINE_DMA_ENTRY(ovl_Magic_Fire) +DEFINE_DMA_ENTRY(ovl_Magic_Wind) +DEFINE_DMA_ENTRY(ovl_Mir_Ray) +DEFINE_DMA_ENTRY(ovl_Obj_Bean) +DEFINE_DMA_ENTRY(ovl_Obj_Blockstop) +DEFINE_DMA_ENTRY(ovl_Obj_Bombiwa) +DEFINE_DMA_ENTRY(ovl_Obj_Comb) +DEFINE_DMA_ENTRY(ovl_Obj_Dekujr) +DEFINE_DMA_ENTRY(ovl_Obj_Elevator) +DEFINE_DMA_ENTRY(ovl_Obj_Hamishi) +DEFINE_DMA_ENTRY(ovl_Obj_Hana) +DEFINE_DMA_ENTRY(ovl_Obj_Hsblock) +DEFINE_DMA_ENTRY(ovl_Obj_Ice_Poly) +DEFINE_DMA_ENTRY(ovl_Obj_Kibako) +DEFINE_DMA_ENTRY(ovl_Obj_Kibako2) +DEFINE_DMA_ENTRY(ovl_Obj_Lift) +DEFINE_DMA_ENTRY(ovl_Obj_Lightswitch) +DEFINE_DMA_ENTRY(ovl_Obj_Makekinsuta) +DEFINE_DMA_ENTRY(ovl_Obj_Makeoshihiki) +DEFINE_DMA_ENTRY(ovl_Obj_Mure) +DEFINE_DMA_ENTRY(ovl_Obj_Mure2) +DEFINE_DMA_ENTRY(ovl_Obj_Mure3) +DEFINE_DMA_ENTRY(ovl_Obj_Oshihiki) +DEFINE_DMA_ENTRY(ovl_Obj_Roomtimer) +DEFINE_DMA_ENTRY(ovl_Obj_Switch) +DEFINE_DMA_ENTRY(ovl_Obj_Syokudai) +DEFINE_DMA_ENTRY(ovl_Obj_Timeblock) +DEFINE_DMA_ENTRY(ovl_Obj_Tsubo) +DEFINE_DMA_ENTRY(ovl_Obj_Warp2block) +DEFINE_DMA_ENTRY(ovl_Object_Kankyo) +DEFINE_DMA_ENTRY(ovl_Oceff_Spot) +DEFINE_DMA_ENTRY(ovl_Oceff_Storm) +DEFINE_DMA_ENTRY(ovl_Oceff_Wipe) +DEFINE_DMA_ENTRY(ovl_Oceff_Wipe2) +DEFINE_DMA_ENTRY(ovl_Oceff_Wipe3) +DEFINE_DMA_ENTRY(ovl_Oceff_Wipe4) +DEFINE_DMA_ENTRY(ovl_Shot_Sun) +DEFINE_DMA_ENTRY(gameplay_keep) +DEFINE_DMA_ENTRY(gameplay_field_keep) +DEFINE_DMA_ENTRY(gameplay_dangeon_keep) +DEFINE_DMA_ENTRY(gameplay_object_exchange_static) +DEFINE_DMA_ENTRY(object_link_boy) +DEFINE_DMA_ENTRY(object_link_child) +DEFINE_DMA_ENTRY(object_box) +DEFINE_DMA_ENTRY(object_human) +DEFINE_DMA_ENTRY(object_okuta) +DEFINE_DMA_ENTRY(object_poh) +DEFINE_DMA_ENTRY(object_wallmaster) +DEFINE_DMA_ENTRY(object_dy_obj) +DEFINE_DMA_ENTRY(object_firefly) +DEFINE_DMA_ENTRY(object_dodongo) +DEFINE_DMA_ENTRY(object_fire) +DEFINE_DMA_ENTRY(object_niw) +DEFINE_DMA_ENTRY(object_tite) +DEFINE_DMA_ENTRY(object_reeba) +DEFINE_DMA_ENTRY(object_peehat) +DEFINE_DMA_ENTRY(object_kingdodongo) +DEFINE_DMA_ENTRY(object_horse) +DEFINE_DMA_ENTRY(object_zf) +DEFINE_DMA_ENTRY(object_goma) +DEFINE_DMA_ENTRY(object_zl1) +DEFINE_DMA_ENTRY(object_gol) +DEFINE_DMA_ENTRY(object_bubble) +DEFINE_DMA_ENTRY(object_dodojr) +DEFINE_DMA_ENTRY(object_torch2) +DEFINE_DMA_ENTRY(object_bl) +DEFINE_DMA_ENTRY(object_tp) +DEFINE_DMA_ENTRY(object_oA1) +DEFINE_DMA_ENTRY(object_st) +DEFINE_DMA_ENTRY(object_bw) +DEFINE_DMA_ENTRY(object_ei) +DEFINE_DMA_ENTRY(object_horse_normal) +DEFINE_DMA_ENTRY(object_oB1) +DEFINE_DMA_ENTRY(object_o_anime) +DEFINE_DMA_ENTRY(object_spot04_objects) +DEFINE_DMA_ENTRY(object_ddan_objects) +DEFINE_DMA_ENTRY(object_hidan_objects) +DEFINE_DMA_ENTRY(object_horse_ganon) +DEFINE_DMA_ENTRY(object_oA2) +DEFINE_DMA_ENTRY(object_spot00_objects) +DEFINE_DMA_ENTRY(object_mb) +DEFINE_DMA_ENTRY(object_bombf) +DEFINE_DMA_ENTRY(object_sk2) +DEFINE_DMA_ENTRY(object_oE1) +DEFINE_DMA_ENTRY(object_oE_anime) +DEFINE_DMA_ENTRY(object_oE2) +DEFINE_DMA_ENTRY(object_ydan_objects) +DEFINE_DMA_ENTRY(object_gnd) +DEFINE_DMA_ENTRY(object_am) +DEFINE_DMA_ENTRY(object_dekubaba) +DEFINE_DMA_ENTRY(object_oA3) +DEFINE_DMA_ENTRY(object_oA4) +DEFINE_DMA_ENTRY(object_oA5) +DEFINE_DMA_ENTRY(object_oA6) +DEFINE_DMA_ENTRY(object_oA7) +DEFINE_DMA_ENTRY(object_jj) +DEFINE_DMA_ENTRY(object_oA8) +DEFINE_DMA_ENTRY(object_oA9) +DEFINE_DMA_ENTRY(object_oB2) +DEFINE_DMA_ENTRY(object_oB3) +DEFINE_DMA_ENTRY(object_oB4) +DEFINE_DMA_ENTRY(object_horse_zelda) +DEFINE_DMA_ENTRY(object_opening_demo1) +DEFINE_DMA_ENTRY(object_warp1) +DEFINE_DMA_ENTRY(object_b_heart) +DEFINE_DMA_ENTRY(object_dekunuts) +DEFINE_DMA_ENTRY(object_oE3) +DEFINE_DMA_ENTRY(object_oE4) +DEFINE_DMA_ENTRY(object_menkuri_objects) +DEFINE_DMA_ENTRY(object_oE5) +DEFINE_DMA_ENTRY(object_oE6) +DEFINE_DMA_ENTRY(object_oE7) +DEFINE_DMA_ENTRY(object_oE8) +DEFINE_DMA_ENTRY(object_oE9) +DEFINE_DMA_ENTRY(object_oE10) +DEFINE_DMA_ENTRY(object_oE11) +DEFINE_DMA_ENTRY(object_oE12) +DEFINE_DMA_ENTRY(object_vali) +DEFINE_DMA_ENTRY(object_oA10) +DEFINE_DMA_ENTRY(object_oA11) +DEFINE_DMA_ENTRY(object_mizu_objects) +DEFINE_DMA_ENTRY(object_fhg) +DEFINE_DMA_ENTRY(object_ossan) +DEFINE_DMA_ENTRY(object_mori_hineri1) +DEFINE_DMA_ENTRY(object_Bb) +DEFINE_DMA_ENTRY(object_toki_objects) +DEFINE_DMA_ENTRY(object_yukabyun) +DEFINE_DMA_ENTRY(object_zl2) +DEFINE_DMA_ENTRY(object_mjin) +DEFINE_DMA_ENTRY(object_mjin_flash) +DEFINE_DMA_ENTRY(object_mjin_dark) +DEFINE_DMA_ENTRY(object_mjin_flame) +DEFINE_DMA_ENTRY(object_mjin_ice) +DEFINE_DMA_ENTRY(object_mjin_soul) +DEFINE_DMA_ENTRY(object_mjin_wind) +DEFINE_DMA_ENTRY(object_mjin_oka) +DEFINE_DMA_ENTRY(object_haka_objects) +DEFINE_DMA_ENTRY(object_spot06_objects) +DEFINE_DMA_ENTRY(object_ice_objects) +DEFINE_DMA_ENTRY(object_relay_objects) +DEFINE_DMA_ENTRY(object_mori_hineri1a) +DEFINE_DMA_ENTRY(object_mori_hineri2) +DEFINE_DMA_ENTRY(object_mori_hineri2a) +DEFINE_DMA_ENTRY(object_mori_objects) +DEFINE_DMA_ENTRY(object_mori_tex) +DEFINE_DMA_ENTRY(object_spot08_obj) +DEFINE_DMA_ENTRY(object_warp2) +DEFINE_DMA_ENTRY(object_hata) +DEFINE_DMA_ENTRY(object_bird) +DEFINE_DMA_ENTRY(object_wood02) +DEFINE_DMA_ENTRY(object_lightbox) +DEFINE_DMA_ENTRY(object_pu_box) +DEFINE_DMA_ENTRY(object_trap) +DEFINE_DMA_ENTRY(object_vase) +DEFINE_DMA_ENTRY(object_im) +DEFINE_DMA_ENTRY(object_ta) +DEFINE_DMA_ENTRY(object_tk) +DEFINE_DMA_ENTRY(object_xc) +DEFINE_DMA_ENTRY(object_vm) +DEFINE_DMA_ENTRY(object_bv) +DEFINE_DMA_ENTRY(object_hakach_objects) +DEFINE_DMA_ENTRY(object_efc_crystal_light) +DEFINE_DMA_ENTRY(object_efc_fire_ball) +DEFINE_DMA_ENTRY(object_efc_flash) +DEFINE_DMA_ENTRY(object_efc_lgt_shower) +DEFINE_DMA_ENTRY(object_efc_star_field) +DEFINE_DMA_ENTRY(object_god_lgt) +DEFINE_DMA_ENTRY(object_light_ring) +DEFINE_DMA_ENTRY(object_triforce_spot) +DEFINE_DMA_ENTRY(object_medal) +DEFINE_DMA_ENTRY(object_bdan_objects) +DEFINE_DMA_ENTRY(object_sd) +DEFINE_DMA_ENTRY(object_rd) +DEFINE_DMA_ENTRY(object_po_sisters) +DEFINE_DMA_ENTRY(object_heavy_object) +DEFINE_DMA_ENTRY(object_gndd) +DEFINE_DMA_ENTRY(object_fd) +DEFINE_DMA_ENTRY(object_du) +DEFINE_DMA_ENTRY(object_fw) +DEFINE_DMA_ENTRY(object_horse_link_child) +DEFINE_DMA_ENTRY(object_spot02_objects) +DEFINE_DMA_ENTRY(object_haka) +DEFINE_DMA_ENTRY(object_ru1) +DEFINE_DMA_ENTRY(object_syokudai) +DEFINE_DMA_ENTRY(object_fd2) +DEFINE_DMA_ENTRY(object_dh) +DEFINE_DMA_ENTRY(object_rl) +DEFINE_DMA_ENTRY(object_efc_tw) +DEFINE_DMA_ENTRY(object_demo_tre_lgt) +DEFINE_DMA_ENTRY(object_gi_key) +DEFINE_DMA_ENTRY(object_mir_ray) +DEFINE_DMA_ENTRY(object_brob) +DEFINE_DMA_ENTRY(object_gi_jewel) +DEFINE_DMA_ENTRY(object_spot09_obj) +DEFINE_DMA_ENTRY(object_spot18_obj) +DEFINE_DMA_ENTRY(object_bdoor) +DEFINE_DMA_ENTRY(object_spot17_obj) +DEFINE_DMA_ENTRY(object_shop_dungen) +DEFINE_DMA_ENTRY(object_nb) +DEFINE_DMA_ENTRY(object_mo) +DEFINE_DMA_ENTRY(object_sb) +DEFINE_DMA_ENTRY(object_gi_melody) +DEFINE_DMA_ENTRY(object_gi_heart) +DEFINE_DMA_ENTRY(object_gi_compass) +DEFINE_DMA_ENTRY(object_gi_bosskey) +DEFINE_DMA_ENTRY(object_gi_medal) +DEFINE_DMA_ENTRY(object_gi_nuts) +DEFINE_DMA_ENTRY(object_sa) +DEFINE_DMA_ENTRY(object_gi_hearts) +DEFINE_DMA_ENTRY(object_gi_arrowcase) +DEFINE_DMA_ENTRY(object_gi_bombpouch) +DEFINE_DMA_ENTRY(object_in) +DEFINE_DMA_ENTRY(object_tr) +DEFINE_DMA_ENTRY(object_spot16_obj) +DEFINE_DMA_ENTRY(object_oE1s) +DEFINE_DMA_ENTRY(object_oE4s) +DEFINE_DMA_ENTRY(object_os_anime) +DEFINE_DMA_ENTRY(object_gi_bottle) +DEFINE_DMA_ENTRY(object_gi_stick) +DEFINE_DMA_ENTRY(object_gi_map) +DEFINE_DMA_ENTRY(object_oF1d_map) +DEFINE_DMA_ENTRY(object_ru2) +DEFINE_DMA_ENTRY(object_gi_shield_1) +DEFINE_DMA_ENTRY(object_dekujr) +DEFINE_DMA_ENTRY(object_gi_magicpot) +DEFINE_DMA_ENTRY(object_gi_bomb_1) +DEFINE_DMA_ENTRY(object_oF1s) +DEFINE_DMA_ENTRY(object_ma2) +DEFINE_DMA_ENTRY(object_gi_purse) +DEFINE_DMA_ENTRY(object_hni) +DEFINE_DMA_ENTRY(object_tw) +DEFINE_DMA_ENTRY(object_rr) +DEFINE_DMA_ENTRY(object_bxa) +DEFINE_DMA_ENTRY(object_anubice) +DEFINE_DMA_ENTRY(object_gi_gerudo) +DEFINE_DMA_ENTRY(object_gi_arrow) +DEFINE_DMA_ENTRY(object_gi_bomb_2) +DEFINE_DMA_ENTRY(object_gi_egg) +DEFINE_DMA_ENTRY(object_gi_scale) +DEFINE_DMA_ENTRY(object_gi_shield_2) +DEFINE_DMA_ENTRY(object_gi_hookshot) +DEFINE_DMA_ENTRY(object_gi_ocarina) +DEFINE_DMA_ENTRY(object_gi_milk) +DEFINE_DMA_ENTRY(object_ma1) +DEFINE_DMA_ENTRY(object_ganon) +DEFINE_DMA_ENTRY(object_sst) +DEFINE_DMA_ENTRY(object_ny) +DEFINE_DMA_ENTRY(object_fr) +DEFINE_DMA_ENTRY(object_gi_pachinko) +DEFINE_DMA_ENTRY(object_gi_boomerang) +DEFINE_DMA_ENTRY(object_gi_bow) +DEFINE_DMA_ENTRY(object_gi_glasses) +DEFINE_DMA_ENTRY(object_gi_liquid) +DEFINE_DMA_ENTRY(object_ani) +DEFINE_DMA_ENTRY(object_demo_6k) +DEFINE_DMA_ENTRY(object_gi_shield_3) +DEFINE_DMA_ENTRY(object_gi_letter) +DEFINE_DMA_ENTRY(object_spot15_obj) +DEFINE_DMA_ENTRY(object_jya_obj) +DEFINE_DMA_ENTRY(object_gi_clothes) +DEFINE_DMA_ENTRY(object_gi_bean) +DEFINE_DMA_ENTRY(object_gi_fish) +DEFINE_DMA_ENTRY(object_gi_saw) +DEFINE_DMA_ENTRY(object_gi_hammer) +DEFINE_DMA_ENTRY(object_gi_grass) +DEFINE_DMA_ENTRY(object_gi_longsword) +DEFINE_DMA_ENTRY(object_spot01_objects) +DEFINE_DMA_ENTRY(object_md) +DEFINE_DMA_ENTRY(object_km1) +DEFINE_DMA_ENTRY(object_kw1) +DEFINE_DMA_ENTRY(object_zo) +DEFINE_DMA_ENTRY(object_kz) +DEFINE_DMA_ENTRY(object_umajump) +DEFINE_DMA_ENTRY(object_masterkokiri) +DEFINE_DMA_ENTRY(object_masterkokirihead) +DEFINE_DMA_ENTRY(object_mastergolon) +DEFINE_DMA_ENTRY(object_masterzoora) +DEFINE_DMA_ENTRY(object_aob) +DEFINE_DMA_ENTRY(object_ik) +DEFINE_DMA_ENTRY(object_ahg) +DEFINE_DMA_ENTRY(object_cne) +DEFINE_DMA_ENTRY(object_gi_niwatori) +DEFINE_DMA_ENTRY(object_skj) +DEFINE_DMA_ENTRY(object_gi_bottle_letter) +DEFINE_DMA_ENTRY(object_bji) +DEFINE_DMA_ENTRY(object_bba) +DEFINE_DMA_ENTRY(object_gi_ocarina_0) +DEFINE_DMA_ENTRY(object_ds) +DEFINE_DMA_ENTRY(object_ane) +DEFINE_DMA_ENTRY(object_boj) +DEFINE_DMA_ENTRY(object_spot03_object) +DEFINE_DMA_ENTRY(object_spot07_object) +DEFINE_DMA_ENTRY(object_fz) +DEFINE_DMA_ENTRY(object_bob) +DEFINE_DMA_ENTRY(object_ge1) +DEFINE_DMA_ENTRY(object_yabusame_point) +DEFINE_DMA_ENTRY(object_gi_boots_2) +DEFINE_DMA_ENTRY(object_gi_seed) +DEFINE_DMA_ENTRY(object_gnd_magic) +DEFINE_DMA_ENTRY(object_d_elevator) +DEFINE_DMA_ENTRY(object_d_hsblock) +DEFINE_DMA_ENTRY(object_d_lift) +DEFINE_DMA_ENTRY(object_mamenoki) +DEFINE_DMA_ENTRY(object_goroiwa) +DEFINE_DMA_ENTRY(object_toryo) +DEFINE_DMA_ENTRY(object_daiku) +DEFINE_DMA_ENTRY(object_nwc) +DEFINE_DMA_ENTRY(object_blkobj) +DEFINE_DMA_ENTRY(object_gm) +DEFINE_DMA_ENTRY(object_ms) +DEFINE_DMA_ENTRY(object_hs) +DEFINE_DMA_ENTRY(object_ingate) +DEFINE_DMA_ENTRY(object_lightswitch) +DEFINE_DMA_ENTRY(object_kusa) +DEFINE_DMA_ENTRY(object_tsubo) +DEFINE_DMA_ENTRY(object_gi_gloves) +DEFINE_DMA_ENTRY(object_gi_coin) +DEFINE_DMA_ENTRY(object_kanban) +DEFINE_DMA_ENTRY(object_gjyo_objects) +DEFINE_DMA_ENTRY(object_owl) +DEFINE_DMA_ENTRY(object_mk) +DEFINE_DMA_ENTRY(object_fu) +DEFINE_DMA_ENTRY(object_gi_ki_tan_mask) +DEFINE_DMA_ENTRY(object_gi_redead_mask) +DEFINE_DMA_ENTRY(object_gi_skj_mask) +DEFINE_DMA_ENTRY(object_gi_rabit_mask) +DEFINE_DMA_ENTRY(object_gi_truth_mask) +DEFINE_DMA_ENTRY(object_ganon_objects) +DEFINE_DMA_ENTRY(object_siofuki) +DEFINE_DMA_ENTRY(object_stream) +DEFINE_DMA_ENTRY(object_mm) +DEFINE_DMA_ENTRY(object_fa) +DEFINE_DMA_ENTRY(object_os) +DEFINE_DMA_ENTRY(object_gi_eye_lotion) +DEFINE_DMA_ENTRY(object_gi_powder) +DEFINE_DMA_ENTRY(object_gi_mushroom) +DEFINE_DMA_ENTRY(object_gi_ticketstone) +DEFINE_DMA_ENTRY(object_gi_brokensword) +DEFINE_DMA_ENTRY(object_js) +DEFINE_DMA_ENTRY(object_cs) +DEFINE_DMA_ENTRY(object_gi_prescription) +DEFINE_DMA_ENTRY(object_gi_bracelet) +DEFINE_DMA_ENTRY(object_gi_soldout) +DEFINE_DMA_ENTRY(object_gi_frog) +DEFINE_DMA_ENTRY(object_mag) +DEFINE_DMA_ENTRY(object_door_gerudo) +DEFINE_DMA_ENTRY(object_gt) +DEFINE_DMA_ENTRY(object_efc_erupc) +DEFINE_DMA_ENTRY(object_zl2_anime1) +DEFINE_DMA_ENTRY(object_zl2_anime2) +DEFINE_DMA_ENTRY(object_gi_golonmask) +DEFINE_DMA_ENTRY(object_gi_zoramask) +DEFINE_DMA_ENTRY(object_gi_gerudomask) +DEFINE_DMA_ENTRY(object_ganon2) +DEFINE_DMA_ENTRY(object_ka) +DEFINE_DMA_ENTRY(object_ts) +DEFINE_DMA_ENTRY(object_zg) +DEFINE_DMA_ENTRY(object_gi_hoverboots) +DEFINE_DMA_ENTRY(object_gi_m_arrow) +DEFINE_DMA_ENTRY(object_ds2) +DEFINE_DMA_ENTRY(object_ec) +DEFINE_DMA_ENTRY(object_fish) +DEFINE_DMA_ENTRY(object_gi_sutaru) +DEFINE_DMA_ENTRY(object_gi_goddess) +DEFINE_DMA_ENTRY(object_ssh) +DEFINE_DMA_ENTRY(object_bigokuta) +DEFINE_DMA_ENTRY(object_bg) +DEFINE_DMA_ENTRY(object_spot05_objects) +DEFINE_DMA_ENTRY(object_spot12_obj) +DEFINE_DMA_ENTRY(object_bombiwa) +DEFINE_DMA_ENTRY(object_hintnuts) +DEFINE_DMA_ENTRY(object_rs) +DEFINE_DMA_ENTRY(object_spot00_break) +DEFINE_DMA_ENTRY(object_gla) +DEFINE_DMA_ENTRY(object_shopnuts) +DEFINE_DMA_ENTRY(object_geldb) +DEFINE_DMA_ENTRY(object_gr) +DEFINE_DMA_ENTRY(object_dog) +DEFINE_DMA_ENTRY(object_jya_iron) +DEFINE_DMA_ENTRY(object_jya_door) +DEFINE_DMA_ENTRY(object_spot01_objects2) +DEFINE_DMA_ENTRY(object_spot11_obj) +DEFINE_DMA_ENTRY(object_kibako2) +DEFINE_DMA_ENTRY(object_dns) +DEFINE_DMA_ENTRY(object_dnk) +DEFINE_DMA_ENTRY(object_gi_fire) +DEFINE_DMA_ENTRY(object_gi_insect) +DEFINE_DMA_ENTRY(object_gi_butterfly) +DEFINE_DMA_ENTRY(object_gi_ghost) +DEFINE_DMA_ENTRY(object_gi_soul) +DEFINE_DMA_ENTRY(object_bowl) +DEFINE_DMA_ENTRY(object_po_field) +DEFINE_DMA_ENTRY(object_demo_kekkai) +DEFINE_DMA_ENTRY(object_efc_doughnut) +DEFINE_DMA_ENTRY(object_gi_dekupouch) +DEFINE_DMA_ENTRY(object_ganon_anime1) +DEFINE_DMA_ENTRY(object_ganon_anime2) +DEFINE_DMA_ENTRY(object_ganon_anime3) +DEFINE_DMA_ENTRY(object_gi_rupy) +DEFINE_DMA_ENTRY(object_spot01_matoya) +DEFINE_DMA_ENTRY(object_spot01_matoyab) +DEFINE_DMA_ENTRY(object_po_composer) +DEFINE_DMA_ENTRY(object_mu) +DEFINE_DMA_ENTRY(object_wf) +DEFINE_DMA_ENTRY(object_skb) +DEFINE_DMA_ENTRY(object_gj) +DEFINE_DMA_ENTRY(object_geff) +DEFINE_DMA_ENTRY(object_haka_door) +DEFINE_DMA_ENTRY(object_gs) +DEFINE_DMA_ENTRY(object_ps) +DEFINE_DMA_ENTRY(object_bwall) +DEFINE_DMA_ENTRY(object_crow) +DEFINE_DMA_ENTRY(object_cow) +DEFINE_DMA_ENTRY(object_cob) +DEFINE_DMA_ENTRY(object_gi_sword_1) +DEFINE_DMA_ENTRY(object_door_killer) +DEFINE_DMA_ENTRY(object_ouke_haka) +DEFINE_DMA_ENTRY(object_timeblock) +DEFINE_DMA_ENTRY(object_zl4) +DEFINE_DMA_ENTRY(g_pn_01) +DEFINE_DMA_ENTRY(g_pn_02) +DEFINE_DMA_ENTRY(g_pn_03) +DEFINE_DMA_ENTRY(g_pn_04) +DEFINE_DMA_ENTRY(g_pn_05) +DEFINE_DMA_ENTRY(g_pn_06) +DEFINE_DMA_ENTRY(g_pn_07) +DEFINE_DMA_ENTRY(g_pn_08) +DEFINE_DMA_ENTRY(g_pn_09) +DEFINE_DMA_ENTRY(g_pn_10) +DEFINE_DMA_ENTRY(g_pn_11) +DEFINE_DMA_ENTRY(g_pn_12) +DEFINE_DMA_ENTRY(g_pn_13) +DEFINE_DMA_ENTRY(g_pn_14) +DEFINE_DMA_ENTRY(g_pn_15) +DEFINE_DMA_ENTRY(g_pn_16) +DEFINE_DMA_ENTRY(g_pn_17) +DEFINE_DMA_ENTRY(g_pn_18) +DEFINE_DMA_ENTRY(g_pn_19) +DEFINE_DMA_ENTRY(g_pn_20) +DEFINE_DMA_ENTRY(g_pn_21) +DEFINE_DMA_ENTRY(g_pn_22) +DEFINE_DMA_ENTRY(g_pn_23) +DEFINE_DMA_ENTRY(g_pn_24) +DEFINE_DMA_ENTRY(g_pn_25) +DEFINE_DMA_ENTRY(g_pn_26) +DEFINE_DMA_ENTRY(g_pn_27) +DEFINE_DMA_ENTRY(g_pn_28) +DEFINE_DMA_ENTRY(g_pn_29) +DEFINE_DMA_ENTRY(g_pn_30) +DEFINE_DMA_ENTRY(g_pn_31) +DEFINE_DMA_ENTRY(g_pn_32) +DEFINE_DMA_ENTRY(g_pn_33) +DEFINE_DMA_ENTRY(g_pn_34) +DEFINE_DMA_ENTRY(g_pn_35) +DEFINE_DMA_ENTRY(g_pn_36) +DEFINE_DMA_ENTRY(g_pn_37) +DEFINE_DMA_ENTRY(g_pn_38) +DEFINE_DMA_ENTRY(g_pn_39) +DEFINE_DMA_ENTRY(g_pn_40) +DEFINE_DMA_ENTRY(g_pn_41) +DEFINE_DMA_ENTRY(g_pn_42) +DEFINE_DMA_ENTRY(g_pn_43) +DEFINE_DMA_ENTRY(g_pn_44) +DEFINE_DMA_ENTRY(g_pn_45) +DEFINE_DMA_ENTRY(g_pn_46) +DEFINE_DMA_ENTRY(g_pn_47) +DEFINE_DMA_ENTRY(g_pn_48) +DEFINE_DMA_ENTRY(g_pn_49) +DEFINE_DMA_ENTRY(g_pn_50) +DEFINE_DMA_ENTRY(g_pn_51) +DEFINE_DMA_ENTRY(g_pn_52) +DEFINE_DMA_ENTRY(g_pn_53) +DEFINE_DMA_ENTRY(g_pn_54) +DEFINE_DMA_ENTRY(g_pn_55) +DEFINE_DMA_ENTRY(g_pn_56) +DEFINE_DMA_ENTRY(g_pn_57) +DEFINE_DMA_ENTRY(z_select_static) +DEFINE_DMA_ENTRY(nintendo_rogo_static) +DEFINE_DMA_ENTRY(title_static) +DEFINE_DMA_ENTRY(parameter_static) +DEFINE_DMA_ENTRY(vr_fine0_static) +DEFINE_DMA_ENTRY(vr_fine0_pal_static) +DEFINE_DMA_ENTRY(vr_fine1_static) +DEFINE_DMA_ENTRY(vr_fine1_pal_static) +DEFINE_DMA_ENTRY(vr_fine2_static) +DEFINE_DMA_ENTRY(vr_fine2_pal_static) +DEFINE_DMA_ENTRY(vr_fine3_static) +DEFINE_DMA_ENTRY(vr_fine3_pal_static) +DEFINE_DMA_ENTRY(vr_cloud0_static) +DEFINE_DMA_ENTRY(vr_cloud0_pal_static) +DEFINE_DMA_ENTRY(vr_cloud1_static) +DEFINE_DMA_ENTRY(vr_cloud1_pal_static) +DEFINE_DMA_ENTRY(vr_cloud2_static) +DEFINE_DMA_ENTRY(vr_cloud2_pal_static) +DEFINE_DMA_ENTRY(vr_cloud3_static) +DEFINE_DMA_ENTRY(vr_cloud3_pal_static) +DEFINE_DMA_ENTRY(vr_holy0_static) +DEFINE_DMA_ENTRY(vr_holy0_pal_static) +DEFINE_DMA_ENTRY(vr_holy1_static) +DEFINE_DMA_ENTRY(vr_holy1_pal_static) +DEFINE_DMA_ENTRY(vr_MDVR_static) +DEFINE_DMA_ENTRY(vr_MDVR_pal_static) +DEFINE_DMA_ENTRY(vr_MNVR_static) +DEFINE_DMA_ENTRY(vr_MNVR_pal_static) +DEFINE_DMA_ENTRY(vr_RUVR_static) +DEFINE_DMA_ENTRY(vr_RUVR_pal_static) +DEFINE_DMA_ENTRY(vr_LHVR_static) +DEFINE_DMA_ENTRY(vr_LHVR_pal_static) +DEFINE_DMA_ENTRY(vr_KHVR_static) +DEFINE_DMA_ENTRY(vr_KHVR_pal_static) +DEFINE_DMA_ENTRY(vr_K3VR_static) +DEFINE_DMA_ENTRY(vr_K3VR_pal_static) +DEFINE_DMA_ENTRY(vr_K4VR_static) +DEFINE_DMA_ENTRY(vr_K4VR_pal_static) +DEFINE_DMA_ENTRY(vr_K5VR_static) +DEFINE_DMA_ENTRY(vr_K5VR_pal_static) +DEFINE_DMA_ENTRY(vr_SP1a_static) +DEFINE_DMA_ENTRY(vr_SP1a_pal_static) +DEFINE_DMA_ENTRY(vr_MLVR_static) +DEFINE_DMA_ENTRY(vr_MLVR_pal_static) +DEFINE_DMA_ENTRY(vr_KKRVR_static) +DEFINE_DMA_ENTRY(vr_KKRVR_pal_static) +DEFINE_DMA_ENTRY(vr_KR3VR_static) +DEFINE_DMA_ENTRY(vr_KR3VR_pal_static) +DEFINE_DMA_ENTRY(vr_IPVR_static) +DEFINE_DMA_ENTRY(vr_IPVR_pal_static) +DEFINE_DMA_ENTRY(vr_KSVR_static) +DEFINE_DMA_ENTRY(vr_KSVR_pal_static) +DEFINE_DMA_ENTRY(vr_GLVR_static) +DEFINE_DMA_ENTRY(vr_GLVR_pal_static) +DEFINE_DMA_ENTRY(vr_ZRVR_static) +DEFINE_DMA_ENTRY(vr_ZRVR_pal_static) +DEFINE_DMA_ENTRY(vr_DGVR_static) +DEFINE_DMA_ENTRY(vr_DGVR_pal_static) +DEFINE_DMA_ENTRY(vr_ALVR_static) +DEFINE_DMA_ENTRY(vr_ALVR_pal_static) +DEFINE_DMA_ENTRY(vr_NSVR_static) +DEFINE_DMA_ENTRY(vr_NSVR_pal_static) +DEFINE_DMA_ENTRY(vr_LBVR_static) +DEFINE_DMA_ENTRY(vr_LBVR_pal_static) +DEFINE_DMA_ENTRY(vr_TTVR_static) +DEFINE_DMA_ENTRY(vr_TTVR_pal_static) +DEFINE_DMA_ENTRY(vr_FCVR_static) +DEFINE_DMA_ENTRY(vr_FCVR_pal_static) +DEFINE_DMA_ENTRY(elf_message_field) +DEFINE_DMA_ENTRY(elf_message_ydan) +DEFINE_DMA_ENTRY(syotes_scene) +DEFINE_DMA_ENTRY(syotes_room_0) +DEFINE_DMA_ENTRY(syotes2_scene) +DEFINE_DMA_ENTRY(syotes2_room_0) +DEFINE_DMA_ENTRY(depth_test_scene) +DEFINE_DMA_ENTRY(depth_test_room_0) +DEFINE_DMA_ENTRY(spot00_scene) +DEFINE_DMA_ENTRY(spot00_room_0) +DEFINE_DMA_ENTRY(spot01_scene) +DEFINE_DMA_ENTRY(spot01_room_0) +DEFINE_DMA_ENTRY(spot02_scene) +DEFINE_DMA_ENTRY(spot02_room_0) +DEFINE_DMA_ENTRY(spot02_room_1) +DEFINE_DMA_ENTRY(spot03_scene) +DEFINE_DMA_ENTRY(spot03_room_0) +DEFINE_DMA_ENTRY(spot03_room_1) +DEFINE_DMA_ENTRY(spot04_scene) +DEFINE_DMA_ENTRY(spot04_room_0) +DEFINE_DMA_ENTRY(spot04_room_1) +DEFINE_DMA_ENTRY(spot04_room_2) +DEFINE_DMA_ENTRY(spot05_scene) +DEFINE_DMA_ENTRY(spot05_room_0) +DEFINE_DMA_ENTRY(spot06_scene) +DEFINE_DMA_ENTRY(spot06_room_0) +DEFINE_DMA_ENTRY(spot07_scene) +DEFINE_DMA_ENTRY(spot07_room_0) +DEFINE_DMA_ENTRY(spot07_room_1) +DEFINE_DMA_ENTRY(spot08_scene) +DEFINE_DMA_ENTRY(spot08_room_0) +DEFINE_DMA_ENTRY(spot09_scene) +DEFINE_DMA_ENTRY(spot09_room_0) +DEFINE_DMA_ENTRY(spot10_scene) +DEFINE_DMA_ENTRY(spot10_room_0) +DEFINE_DMA_ENTRY(spot10_room_1) +DEFINE_DMA_ENTRY(spot10_room_2) +DEFINE_DMA_ENTRY(spot10_room_3) +DEFINE_DMA_ENTRY(spot10_room_4) +DEFINE_DMA_ENTRY(spot10_room_5) +DEFINE_DMA_ENTRY(spot10_room_6) +DEFINE_DMA_ENTRY(spot10_room_7) +DEFINE_DMA_ENTRY(spot10_room_8) +DEFINE_DMA_ENTRY(spot10_room_9) +DEFINE_DMA_ENTRY(spot11_scene) +DEFINE_DMA_ENTRY(spot11_room_0) +DEFINE_DMA_ENTRY(spot12_scene) +DEFINE_DMA_ENTRY(spot12_room_0) +DEFINE_DMA_ENTRY(spot12_room_1) +DEFINE_DMA_ENTRY(spot13_scene) +DEFINE_DMA_ENTRY(spot13_room_0) +DEFINE_DMA_ENTRY(spot13_room_1) +DEFINE_DMA_ENTRY(spot15_scene) +DEFINE_DMA_ENTRY(spot15_room_0) +DEFINE_DMA_ENTRY(spot16_scene) +DEFINE_DMA_ENTRY(spot16_room_0) +DEFINE_DMA_ENTRY(spot17_scene) +DEFINE_DMA_ENTRY(spot17_room_0) +DEFINE_DMA_ENTRY(spot17_room_1) +DEFINE_DMA_ENTRY(spot18_scene) +DEFINE_DMA_ENTRY(spot18_room_0) +DEFINE_DMA_ENTRY(spot18_room_1) +DEFINE_DMA_ENTRY(spot18_room_2) +DEFINE_DMA_ENTRY(spot18_room_3) +DEFINE_DMA_ENTRY(ydan_scene) +DEFINE_DMA_ENTRY(ydan_room_0) +DEFINE_DMA_ENTRY(ydan_room_1) +DEFINE_DMA_ENTRY(ydan_room_2) +DEFINE_DMA_ENTRY(ydan_room_3) +DEFINE_DMA_ENTRY(ydan_room_4) +DEFINE_DMA_ENTRY(ydan_room_5) +DEFINE_DMA_ENTRY(ydan_room_6) +DEFINE_DMA_ENTRY(ydan_room_7) +DEFINE_DMA_ENTRY(ydan_room_8) +DEFINE_DMA_ENTRY(ydan_room_9) +DEFINE_DMA_ENTRY(ydan_room_10) +DEFINE_DMA_ENTRY(ydan_room_11) +DEFINE_DMA_ENTRY(ddan_scene) +DEFINE_DMA_ENTRY(ddan_room_0) +DEFINE_DMA_ENTRY(ddan_room_1) +DEFINE_DMA_ENTRY(ddan_room_2) +DEFINE_DMA_ENTRY(ddan_room_3) +DEFINE_DMA_ENTRY(ddan_room_4) +DEFINE_DMA_ENTRY(ddan_room_5) +DEFINE_DMA_ENTRY(ddan_room_6) +DEFINE_DMA_ENTRY(ddan_room_7) +DEFINE_DMA_ENTRY(ddan_room_8) +DEFINE_DMA_ENTRY(ddan_room_9) +DEFINE_DMA_ENTRY(ddan_room_10) +DEFINE_DMA_ENTRY(ddan_room_11) +DEFINE_DMA_ENTRY(ddan_room_12) +DEFINE_DMA_ENTRY(ddan_room_13) +DEFINE_DMA_ENTRY(ddan_room_14) +DEFINE_DMA_ENTRY(ddan_room_15) +DEFINE_DMA_ENTRY(ddan_room_16) +DEFINE_DMA_ENTRY(bdan_scene) +DEFINE_DMA_ENTRY(bdan_room_0) +DEFINE_DMA_ENTRY(bdan_room_1) +DEFINE_DMA_ENTRY(bdan_room_2) +DEFINE_DMA_ENTRY(bdan_room_3) +DEFINE_DMA_ENTRY(bdan_room_4) +DEFINE_DMA_ENTRY(bdan_room_5) +DEFINE_DMA_ENTRY(bdan_room_6) +DEFINE_DMA_ENTRY(bdan_room_7) +DEFINE_DMA_ENTRY(bdan_room_8) +DEFINE_DMA_ENTRY(bdan_room_9) +DEFINE_DMA_ENTRY(bdan_room_10) +DEFINE_DMA_ENTRY(bdan_room_11) +DEFINE_DMA_ENTRY(bdan_room_12) +DEFINE_DMA_ENTRY(bdan_room_13) +DEFINE_DMA_ENTRY(bdan_room_14) +DEFINE_DMA_ENTRY(bdan_room_15) +DEFINE_DMA_ENTRY(Bmori1_scene) +DEFINE_DMA_ENTRY(Bmori1_room_0) +DEFINE_DMA_ENTRY(Bmori1_room_1) +DEFINE_DMA_ENTRY(Bmori1_room_2) +DEFINE_DMA_ENTRY(Bmori1_room_3) +DEFINE_DMA_ENTRY(Bmori1_room_4) +DEFINE_DMA_ENTRY(Bmori1_room_5) +DEFINE_DMA_ENTRY(Bmori1_room_6) +DEFINE_DMA_ENTRY(Bmori1_room_7) +DEFINE_DMA_ENTRY(Bmori1_room_8) +DEFINE_DMA_ENTRY(Bmori1_room_9) +DEFINE_DMA_ENTRY(Bmori1_room_10) +DEFINE_DMA_ENTRY(Bmori1_room_11) +DEFINE_DMA_ENTRY(Bmori1_room_12) +DEFINE_DMA_ENTRY(Bmori1_room_13) +DEFINE_DMA_ENTRY(Bmori1_room_14) +DEFINE_DMA_ENTRY(Bmori1_room_15) +DEFINE_DMA_ENTRY(Bmori1_room_16) +DEFINE_DMA_ENTRY(Bmori1_room_17) +DEFINE_DMA_ENTRY(Bmori1_room_18) +DEFINE_DMA_ENTRY(Bmori1_room_19) +DEFINE_DMA_ENTRY(Bmori1_room_20) +DEFINE_DMA_ENTRY(Bmori1_room_21) +DEFINE_DMA_ENTRY(Bmori1_room_22) +DEFINE_DMA_ENTRY(HIDAN_scene) +DEFINE_DMA_ENTRY(HIDAN_room_0) +DEFINE_DMA_ENTRY(HIDAN_room_1) +DEFINE_DMA_ENTRY(HIDAN_room_2) +DEFINE_DMA_ENTRY(HIDAN_room_3) +DEFINE_DMA_ENTRY(HIDAN_room_4) +DEFINE_DMA_ENTRY(HIDAN_room_5) +DEFINE_DMA_ENTRY(HIDAN_room_6) +DEFINE_DMA_ENTRY(HIDAN_room_7) +DEFINE_DMA_ENTRY(HIDAN_room_8) +DEFINE_DMA_ENTRY(HIDAN_room_9) +DEFINE_DMA_ENTRY(HIDAN_room_10) +DEFINE_DMA_ENTRY(HIDAN_room_11) +DEFINE_DMA_ENTRY(HIDAN_room_12) +DEFINE_DMA_ENTRY(HIDAN_room_13) +DEFINE_DMA_ENTRY(HIDAN_room_14) +DEFINE_DMA_ENTRY(HIDAN_room_15) +DEFINE_DMA_ENTRY(HIDAN_room_16) +DEFINE_DMA_ENTRY(HIDAN_room_17) +DEFINE_DMA_ENTRY(HIDAN_room_18) +DEFINE_DMA_ENTRY(HIDAN_room_19) +DEFINE_DMA_ENTRY(HIDAN_room_20) +DEFINE_DMA_ENTRY(HIDAN_room_21) +DEFINE_DMA_ENTRY(HIDAN_room_22) +DEFINE_DMA_ENTRY(HIDAN_room_23) +DEFINE_DMA_ENTRY(HIDAN_room_24) +DEFINE_DMA_ENTRY(HIDAN_room_25) +DEFINE_DMA_ENTRY(HIDAN_room_26) +DEFINE_DMA_ENTRY(MIZUsin_scene) +DEFINE_DMA_ENTRY(MIZUsin_room_0) +DEFINE_DMA_ENTRY(MIZUsin_room_1) +DEFINE_DMA_ENTRY(MIZUsin_room_2) +DEFINE_DMA_ENTRY(MIZUsin_room_3) +DEFINE_DMA_ENTRY(MIZUsin_room_4) +DEFINE_DMA_ENTRY(MIZUsin_room_5) +DEFINE_DMA_ENTRY(MIZUsin_room_6) +DEFINE_DMA_ENTRY(MIZUsin_room_7) +DEFINE_DMA_ENTRY(MIZUsin_room_8) +DEFINE_DMA_ENTRY(MIZUsin_room_9) +DEFINE_DMA_ENTRY(MIZUsin_room_10) +DEFINE_DMA_ENTRY(MIZUsin_room_11) +DEFINE_DMA_ENTRY(MIZUsin_room_12) +DEFINE_DMA_ENTRY(MIZUsin_room_13) +DEFINE_DMA_ENTRY(MIZUsin_room_14) +DEFINE_DMA_ENTRY(MIZUsin_room_15) +DEFINE_DMA_ENTRY(MIZUsin_room_16) +DEFINE_DMA_ENTRY(MIZUsin_room_17) +DEFINE_DMA_ENTRY(MIZUsin_room_18) +DEFINE_DMA_ENTRY(MIZUsin_room_19) +DEFINE_DMA_ENTRY(MIZUsin_room_20) +DEFINE_DMA_ENTRY(MIZUsin_room_21) +DEFINE_DMA_ENTRY(MIZUsin_room_22) +DEFINE_DMA_ENTRY(jyasinzou_scene) +DEFINE_DMA_ENTRY(jyasinzou_room_0) +DEFINE_DMA_ENTRY(jyasinzou_room_1) +DEFINE_DMA_ENTRY(jyasinzou_room_2) +DEFINE_DMA_ENTRY(jyasinzou_room_3) +DEFINE_DMA_ENTRY(jyasinzou_room_4) +DEFINE_DMA_ENTRY(jyasinzou_room_5) +DEFINE_DMA_ENTRY(jyasinzou_room_6) +DEFINE_DMA_ENTRY(jyasinzou_room_7) +DEFINE_DMA_ENTRY(jyasinzou_room_8) +DEFINE_DMA_ENTRY(jyasinzou_room_9) +DEFINE_DMA_ENTRY(jyasinzou_room_10) +DEFINE_DMA_ENTRY(jyasinzou_room_11) +DEFINE_DMA_ENTRY(jyasinzou_room_12) +DEFINE_DMA_ENTRY(jyasinzou_room_13) +DEFINE_DMA_ENTRY(jyasinzou_room_14) +DEFINE_DMA_ENTRY(jyasinzou_room_15) +DEFINE_DMA_ENTRY(jyasinzou_room_16) +DEFINE_DMA_ENTRY(jyasinzou_room_17) +DEFINE_DMA_ENTRY(jyasinzou_room_18) +DEFINE_DMA_ENTRY(jyasinzou_room_19) +DEFINE_DMA_ENTRY(jyasinzou_room_20) +DEFINE_DMA_ENTRY(jyasinzou_room_21) +DEFINE_DMA_ENTRY(jyasinzou_room_22) +DEFINE_DMA_ENTRY(jyasinzou_room_23) +DEFINE_DMA_ENTRY(jyasinzou_room_24) +DEFINE_DMA_ENTRY(jyasinzou_room_25) +DEFINE_DMA_ENTRY(jyasinzou_room_26) +DEFINE_DMA_ENTRY(jyasinzou_room_27) +DEFINE_DMA_ENTRY(jyasinzou_room_28) +DEFINE_DMA_ENTRY(HAKAdan_scene) +DEFINE_DMA_ENTRY(HAKAdan_room_0) +DEFINE_DMA_ENTRY(HAKAdan_room_1) +DEFINE_DMA_ENTRY(HAKAdan_room_2) +DEFINE_DMA_ENTRY(HAKAdan_room_3) +DEFINE_DMA_ENTRY(HAKAdan_room_4) +DEFINE_DMA_ENTRY(HAKAdan_room_5) +DEFINE_DMA_ENTRY(HAKAdan_room_6) +DEFINE_DMA_ENTRY(HAKAdan_room_7) +DEFINE_DMA_ENTRY(HAKAdan_room_8) +DEFINE_DMA_ENTRY(HAKAdan_room_9) +DEFINE_DMA_ENTRY(HAKAdan_room_10) +DEFINE_DMA_ENTRY(HAKAdan_room_11) +DEFINE_DMA_ENTRY(HAKAdan_room_12) +DEFINE_DMA_ENTRY(HAKAdan_room_13) +DEFINE_DMA_ENTRY(HAKAdan_room_14) +DEFINE_DMA_ENTRY(HAKAdan_room_15) +DEFINE_DMA_ENTRY(HAKAdan_room_16) +DEFINE_DMA_ENTRY(HAKAdan_room_17) +DEFINE_DMA_ENTRY(HAKAdan_room_18) +DEFINE_DMA_ENTRY(HAKAdan_room_19) +DEFINE_DMA_ENTRY(HAKAdan_room_20) +DEFINE_DMA_ENTRY(HAKAdan_room_21) +DEFINE_DMA_ENTRY(HAKAdan_room_22) +DEFINE_DMA_ENTRY(HAKAdanCH_scene) +DEFINE_DMA_ENTRY(HAKAdanCH_room_0) +DEFINE_DMA_ENTRY(HAKAdanCH_room_1) +DEFINE_DMA_ENTRY(HAKAdanCH_room_2) +DEFINE_DMA_ENTRY(HAKAdanCH_room_3) +DEFINE_DMA_ENTRY(HAKAdanCH_room_4) +DEFINE_DMA_ENTRY(HAKAdanCH_room_5) +DEFINE_DMA_ENTRY(HAKAdanCH_room_6) +DEFINE_DMA_ENTRY(ice_doukutu_scene) +DEFINE_DMA_ENTRY(ice_doukutu_room_0) +DEFINE_DMA_ENTRY(ice_doukutu_room_1) +DEFINE_DMA_ENTRY(ice_doukutu_room_2) +DEFINE_DMA_ENTRY(ice_doukutu_room_3) +DEFINE_DMA_ENTRY(ice_doukutu_room_4) +DEFINE_DMA_ENTRY(ice_doukutu_room_5) +DEFINE_DMA_ENTRY(ice_doukutu_room_6) +DEFINE_DMA_ENTRY(ice_doukutu_room_7) +DEFINE_DMA_ENTRY(ice_doukutu_room_8) +DEFINE_DMA_ENTRY(ice_doukutu_room_9) +DEFINE_DMA_ENTRY(ice_doukutu_room_10) +DEFINE_DMA_ENTRY(ice_doukutu_room_11) +DEFINE_DMA_ENTRY(men_scene) +DEFINE_DMA_ENTRY(men_room_0) +DEFINE_DMA_ENTRY(men_room_1) +DEFINE_DMA_ENTRY(men_room_2) +DEFINE_DMA_ENTRY(men_room_3) +DEFINE_DMA_ENTRY(men_room_4) +DEFINE_DMA_ENTRY(men_room_5) +DEFINE_DMA_ENTRY(men_room_6) +DEFINE_DMA_ENTRY(men_room_7) +DEFINE_DMA_ENTRY(men_room_8) +DEFINE_DMA_ENTRY(men_room_9) +DEFINE_DMA_ENTRY(men_room_10) +DEFINE_DMA_ENTRY(ganontika_scene) +DEFINE_DMA_ENTRY(ganontika_room_0) +DEFINE_DMA_ENTRY(ganontika_room_1) +DEFINE_DMA_ENTRY(ganontika_room_2) +DEFINE_DMA_ENTRY(ganontika_room_3) +DEFINE_DMA_ENTRY(ganontika_room_4) +DEFINE_DMA_ENTRY(ganontika_room_5) +DEFINE_DMA_ENTRY(ganontika_room_6) +DEFINE_DMA_ENTRY(ganontika_room_7) +DEFINE_DMA_ENTRY(ganontika_room_8) +DEFINE_DMA_ENTRY(ganontika_room_9) +DEFINE_DMA_ENTRY(ganontika_room_10) +DEFINE_DMA_ENTRY(ganontika_room_11) +DEFINE_DMA_ENTRY(ganontika_room_12) +DEFINE_DMA_ENTRY(ganontika_room_13) +DEFINE_DMA_ENTRY(ganontika_room_14) +DEFINE_DMA_ENTRY(ganontika_room_15) +DEFINE_DMA_ENTRY(ganontika_room_16) +DEFINE_DMA_ENTRY(ganontika_room_17) +DEFINE_DMA_ENTRY(ganontika_room_18) +DEFINE_DMA_ENTRY(ganontika_room_19) +DEFINE_DMA_ENTRY(market_day_scene) +DEFINE_DMA_ENTRY(market_day_room_0) +DEFINE_DMA_ENTRY(market_night_scene) +DEFINE_DMA_ENTRY(market_night_room_0) +DEFINE_DMA_ENTRY(testroom_scene) +DEFINE_DMA_ENTRY(testroom_room_0) +DEFINE_DMA_ENTRY(testroom_room_1) +DEFINE_DMA_ENTRY(testroom_room_2) +DEFINE_DMA_ENTRY(testroom_room_3) +DEFINE_DMA_ENTRY(testroom_room_4) +DEFINE_DMA_ENTRY(kenjyanoma_scene) +DEFINE_DMA_ENTRY(kenjyanoma_room_0) +DEFINE_DMA_ENTRY(tokinoma_scene) +DEFINE_DMA_ENTRY(tokinoma_room_0) +DEFINE_DMA_ENTRY(tokinoma_room_1) +DEFINE_DMA_ENTRY(sutaru_scene) +DEFINE_DMA_ENTRY(sutaru_room_0) +DEFINE_DMA_ENTRY(link_home_scene) +DEFINE_DMA_ENTRY(link_home_room_0) +DEFINE_DMA_ENTRY(kokiri_shop_scene) +DEFINE_DMA_ENTRY(kokiri_shop_room_0) +DEFINE_DMA_ENTRY(kokiri_home_scene) +DEFINE_DMA_ENTRY(kokiri_home_room_0) +DEFINE_DMA_ENTRY(kakusiana_scene) +DEFINE_DMA_ENTRY(kakusiana_room_0) +DEFINE_DMA_ENTRY(kakusiana_room_1) +DEFINE_DMA_ENTRY(kakusiana_room_2) +DEFINE_DMA_ENTRY(kakusiana_room_3) +DEFINE_DMA_ENTRY(kakusiana_room_4) +DEFINE_DMA_ENTRY(kakusiana_room_5) +DEFINE_DMA_ENTRY(kakusiana_room_6) +DEFINE_DMA_ENTRY(kakusiana_room_7) +DEFINE_DMA_ENTRY(kakusiana_room_8) +DEFINE_DMA_ENTRY(kakusiana_room_9) +DEFINE_DMA_ENTRY(kakusiana_room_10) +DEFINE_DMA_ENTRY(kakusiana_room_11) +DEFINE_DMA_ENTRY(kakusiana_room_12) +DEFINE_DMA_ENTRY(kakusiana_room_13) +DEFINE_DMA_ENTRY(entra_scene) +DEFINE_DMA_ENTRY(entra_room_0) +DEFINE_DMA_ENTRY(moribossroom_scene) +DEFINE_DMA_ENTRY(moribossroom_room_0) +DEFINE_DMA_ENTRY(moribossroom_room_1) +DEFINE_DMA_ENTRY(syatekijyou_scene) +DEFINE_DMA_ENTRY(syatekijyou_room_0) +DEFINE_DMA_ENTRY(shop1_scene) +DEFINE_DMA_ENTRY(shop1_room_0) +DEFINE_DMA_ENTRY(hairal_niwa_scene) +DEFINE_DMA_ENTRY(hairal_niwa_room_0) +DEFINE_DMA_ENTRY(ganon_tou_scene) +DEFINE_DMA_ENTRY(ganon_tou_room_0) +DEFINE_DMA_ENTRY(sasatest_scene) +DEFINE_DMA_ENTRY(sasatest_room_0) +DEFINE_DMA_ENTRY(market_alley_scene) +DEFINE_DMA_ENTRY(market_alley_room_0) +DEFINE_DMA_ENTRY(spot20_scene) +DEFINE_DMA_ENTRY(spot20_room_0) +DEFINE_DMA_ENTRY(market_ruins_scene) +DEFINE_DMA_ENTRY(market_ruins_room_0) +DEFINE_DMA_ENTRY(entra_n_scene) +DEFINE_DMA_ENTRY(entra_n_room_0) +DEFINE_DMA_ENTRY(enrui_scene) +DEFINE_DMA_ENTRY(enrui_room_0) +DEFINE_DMA_ENTRY(market_alley_n_scene) +DEFINE_DMA_ENTRY(market_alley_n_room_0) +DEFINE_DMA_ENTRY(hiral_demo_scene) +DEFINE_DMA_ENTRY(hiral_demo_room_0) +DEFINE_DMA_ENTRY(kokiri_home3_scene) +DEFINE_DMA_ENTRY(kokiri_home3_room_0) +DEFINE_DMA_ENTRY(malon_stable_scene) +DEFINE_DMA_ENTRY(malon_stable_room_0) +DEFINE_DMA_ENTRY(kakariko_scene) +DEFINE_DMA_ENTRY(kakariko_room_0) +DEFINE_DMA_ENTRY(bdan_boss_scene) +DEFINE_DMA_ENTRY(bdan_boss_room_0) +DEFINE_DMA_ENTRY(bdan_boss_room_1) +DEFINE_DMA_ENTRY(FIRE_bs_scene) +DEFINE_DMA_ENTRY(FIRE_bs_room_0) +DEFINE_DMA_ENTRY(FIRE_bs_room_1) +DEFINE_DMA_ENTRY(hut_scene) +DEFINE_DMA_ENTRY(hut_room_0) +DEFINE_DMA_ENTRY(daiyousei_izumi_scene) +DEFINE_DMA_ENTRY(daiyousei_izumi_room_0) +DEFINE_DMA_ENTRY(hakaana_scene) +DEFINE_DMA_ENTRY(hakaana_room_0) +DEFINE_DMA_ENTRY(yousei_izumi_tate_scene) +DEFINE_DMA_ENTRY(yousei_izumi_tate_room_0) +DEFINE_DMA_ENTRY(yousei_izumi_yoko_scene) +DEFINE_DMA_ENTRY(yousei_izumi_yoko_room_0) +DEFINE_DMA_ENTRY(golon_scene) +DEFINE_DMA_ENTRY(golon_room_0) +DEFINE_DMA_ENTRY(zoora_scene) +DEFINE_DMA_ENTRY(zoora_room_0) +DEFINE_DMA_ENTRY(drag_scene) +DEFINE_DMA_ENTRY(drag_room_0) +DEFINE_DMA_ENTRY(alley_shop_scene) +DEFINE_DMA_ENTRY(alley_shop_room_0) +DEFINE_DMA_ENTRY(night_shop_scene) +DEFINE_DMA_ENTRY(night_shop_room_0) +DEFINE_DMA_ENTRY(impa_scene) +DEFINE_DMA_ENTRY(impa_room_0) +DEFINE_DMA_ENTRY(labo_scene) +DEFINE_DMA_ENTRY(labo_room_0) +DEFINE_DMA_ENTRY(tent_scene) +DEFINE_DMA_ENTRY(tent_room_0) +DEFINE_DMA_ENTRY(nakaniwa_scene) +DEFINE_DMA_ENTRY(nakaniwa_room_0) +DEFINE_DMA_ENTRY(ddan_boss_scene) +DEFINE_DMA_ENTRY(ddan_boss_room_0) +DEFINE_DMA_ENTRY(ddan_boss_room_1) +DEFINE_DMA_ENTRY(ydan_boss_scene) +DEFINE_DMA_ENTRY(ydan_boss_room_0) +DEFINE_DMA_ENTRY(ydan_boss_room_1) +DEFINE_DMA_ENTRY(HAKAdan_bs_scene) +DEFINE_DMA_ENTRY(HAKAdan_bs_room_0) +DEFINE_DMA_ENTRY(HAKAdan_bs_room_1) +DEFINE_DMA_ENTRY(MIZUsin_bs_scene) +DEFINE_DMA_ENTRY(MIZUsin_bs_room_0) +DEFINE_DMA_ENTRY(MIZUsin_bs_room_1) +DEFINE_DMA_ENTRY(ganon_scene) +DEFINE_DMA_ENTRY(ganon_room_0) +DEFINE_DMA_ENTRY(ganon_room_1) +DEFINE_DMA_ENTRY(ganon_room_2) +DEFINE_DMA_ENTRY(ganon_room_3) +DEFINE_DMA_ENTRY(ganon_room_4) +DEFINE_DMA_ENTRY(ganon_room_5) +DEFINE_DMA_ENTRY(ganon_room_6) +DEFINE_DMA_ENTRY(ganon_room_7) +DEFINE_DMA_ENTRY(ganon_room_8) +DEFINE_DMA_ENTRY(ganon_room_9) +DEFINE_DMA_ENTRY(ganon_boss_scene) +DEFINE_DMA_ENTRY(ganon_boss_room_0) +DEFINE_DMA_ENTRY(jyasinboss_scene) +DEFINE_DMA_ENTRY(jyasinboss_room_0) +DEFINE_DMA_ENTRY(jyasinboss_room_1) +DEFINE_DMA_ENTRY(jyasinboss_room_2) +DEFINE_DMA_ENTRY(jyasinboss_room_3) +DEFINE_DMA_ENTRY(kokiri_home4_scene) +DEFINE_DMA_ENTRY(kokiri_home4_room_0) +DEFINE_DMA_ENTRY(kokiri_home5_scene) +DEFINE_DMA_ENTRY(kokiri_home5_room_0) +DEFINE_DMA_ENTRY(ganon_final_scene) +DEFINE_DMA_ENTRY(ganon_final_room_0) +DEFINE_DMA_ENTRY(kakariko3_scene) +DEFINE_DMA_ENTRY(kakariko3_room_0) +DEFINE_DMA_ENTRY(hairal_niwa2_scene) +DEFINE_DMA_ENTRY(hairal_niwa2_room_0) +DEFINE_DMA_ENTRY(hakasitarelay_scene) +DEFINE_DMA_ENTRY(hakasitarelay_room_0) +DEFINE_DMA_ENTRY(hakasitarelay_room_1) +DEFINE_DMA_ENTRY(hakasitarelay_room_2) +DEFINE_DMA_ENTRY(hakasitarelay_room_3) +DEFINE_DMA_ENTRY(hakasitarelay_room_4) +DEFINE_DMA_ENTRY(hakasitarelay_room_5) +DEFINE_DMA_ENTRY(hakasitarelay_room_6) +DEFINE_DMA_ENTRY(shrine_scene) +DEFINE_DMA_ENTRY(shrine_room_0) +DEFINE_DMA_ENTRY(turibori_scene) +DEFINE_DMA_ENTRY(turibori_room_0) +DEFINE_DMA_ENTRY(shrine_n_scene) +DEFINE_DMA_ENTRY(shrine_n_room_0) +DEFINE_DMA_ENTRY(shrine_r_scene) +DEFINE_DMA_ENTRY(shrine_r_room_0) +DEFINE_DMA_ENTRY(hakaana2_scene) +DEFINE_DMA_ENTRY(hakaana2_room_0) +DEFINE_DMA_ENTRY(gerudoway_scene) +DEFINE_DMA_ENTRY(gerudoway_room_0) +DEFINE_DMA_ENTRY(gerudoway_room_1) +DEFINE_DMA_ENTRY(gerudoway_room_2) +DEFINE_DMA_ENTRY(gerudoway_room_3) +DEFINE_DMA_ENTRY(gerudoway_room_4) +DEFINE_DMA_ENTRY(gerudoway_room_5) +DEFINE_DMA_ENTRY(hairal_niwa_n_scene) +DEFINE_DMA_ENTRY(hairal_niwa_n_room_0) +DEFINE_DMA_ENTRY(bowling_scene) +DEFINE_DMA_ENTRY(bowling_room_0) +DEFINE_DMA_ENTRY(hakaana_ouke_scene) +DEFINE_DMA_ENTRY(hakaana_ouke_room_0) +DEFINE_DMA_ENTRY(hakaana_ouke_room_1) +DEFINE_DMA_ENTRY(hakaana_ouke_room_2) +DEFINE_DMA_ENTRY(hylia_labo_scene) +DEFINE_DMA_ENTRY(hylia_labo_room_0) +DEFINE_DMA_ENTRY(souko_scene) +DEFINE_DMA_ENTRY(souko_room_0) +DEFINE_DMA_ENTRY(souko_room_1) +DEFINE_DMA_ENTRY(souko_room_2) +DEFINE_DMA_ENTRY(miharigoya_scene) +DEFINE_DMA_ENTRY(miharigoya_room_0) +DEFINE_DMA_ENTRY(mahouya_scene) +DEFINE_DMA_ENTRY(mahouya_room_0) +DEFINE_DMA_ENTRY(takaraya_scene) +DEFINE_DMA_ENTRY(takaraya_room_0) +DEFINE_DMA_ENTRY(takaraya_room_1) +DEFINE_DMA_ENTRY(takaraya_room_2) +DEFINE_DMA_ENTRY(takaraya_room_3) +DEFINE_DMA_ENTRY(takaraya_room_4) +DEFINE_DMA_ENTRY(takaraya_room_5) +DEFINE_DMA_ENTRY(takaraya_room_6) +DEFINE_DMA_ENTRY(ganon_sonogo_scene) +DEFINE_DMA_ENTRY(ganon_sonogo_room_0) +DEFINE_DMA_ENTRY(ganon_sonogo_room_1) +DEFINE_DMA_ENTRY(ganon_sonogo_room_2) +DEFINE_DMA_ENTRY(ganon_sonogo_room_3) +DEFINE_DMA_ENTRY(ganon_sonogo_room_4) +DEFINE_DMA_ENTRY(ganon_demo_scene) +DEFINE_DMA_ENTRY(ganon_demo_room_0) +DEFINE_DMA_ENTRY(besitu_scene) +DEFINE_DMA_ENTRY(besitu_room_0) +DEFINE_DMA_ENTRY(face_shop_scene) +DEFINE_DMA_ENTRY(face_shop_room_0) +DEFINE_DMA_ENTRY(kinsuta_scene) +DEFINE_DMA_ENTRY(kinsuta_room_0) +DEFINE_DMA_ENTRY(ganontikasonogo_scene) +DEFINE_DMA_ENTRY(ganontikasonogo_room_0) +DEFINE_DMA_ENTRY(ganontikasonogo_room_1) +DEFINE_DMA_ENTRY(test01_scene) +DEFINE_DMA_ENTRY(test01_room_0) +DEFINE_DMA_ENTRY(bump_texture_static) +DEFINE_DMA_ENTRY(anime_model_1_static) +DEFINE_DMA_ENTRY(anime_model_2_static) +DEFINE_DMA_ENTRY(anime_model_3_static) +DEFINE_DMA_ENTRY(anime_model_4_static) +DEFINE_DMA_ENTRY(anime_model_5_static) +DEFINE_DMA_ENTRY(anime_model_6_static) +DEFINE_DMA_ENTRY(anime_texture_1_static) +DEFINE_DMA_ENTRY(anime_texture_2_static) +DEFINE_DMA_ENTRY(anime_texture_3_static) +DEFINE_DMA_ENTRY(anime_texture_4_static) +DEFINE_DMA_ENTRY(anime_texture_5_static) +DEFINE_DMA_ENTRY(anime_texture_6_static) +DEFINE_DMA_ENTRY(softsprite_matrix_static) diff --git a/soh/include/tables/effect_ss_table.h b/soh/include/tables/effect_ss_table.h new file mode 100644 index 000000000..5df91784a --- /dev/null +++ b/soh/include/tables/effect_ss_table.h @@ -0,0 +1,46 @@ +/** + * Effect Soft Sprite Table + * + * DEFINE_EFFECT_SS should be used for normal effects soft sprites + * - Argument 1: Name of the effect (without the ovl_ part) + * - Argument 2: Enum value for this effect + * + * DEFINE_EFFECT_SS_UNSET is needed to define empty entries from the original game + */ +/* 0x00 */ DEFINE_EFFECT_SS(Effect_Ss_Dust, EFFECT_SS_DUST) +/* 0x01 */ DEFINE_EFFECT_SS(Effect_Ss_KiraKira, EFFECT_SS_KIRAKIRA) +/* 0x02 */ DEFINE_EFFECT_SS(Effect_Ss_Bomb, EFFECT_SS_BOMB) +/* 0x03 */ DEFINE_EFFECT_SS(Effect_Ss_Bomb2, EFFECT_SS_BOMB2) +/* 0x04 */ DEFINE_EFFECT_SS(Effect_Ss_Blast, EFFECT_SS_BLAST) +/* 0x05 */ DEFINE_EFFECT_SS(Effect_Ss_G_Spk, EFFECT_SS_G_SPK) +/* 0x06 */ DEFINE_EFFECT_SS(Effect_Ss_D_Fire, EFFECT_SS_D_FIRE) +/* 0x07 */ DEFINE_EFFECT_SS(Effect_Ss_Bubble, EFFECT_SS_BUBBLE) +/* 0x08 */ DEFINE_EFFECT_SS_UNSET(EFFECT_SS_UNSET) +/* 0x09 */ DEFINE_EFFECT_SS(Effect_Ss_G_Ripple, EFFECT_SS_G_RIPPLE) +/* 0x0A */ DEFINE_EFFECT_SS(Effect_Ss_G_Splash, EFFECT_SS_G_SPLASH) +/* 0x0B */ DEFINE_EFFECT_SS(Effect_Ss_G_Magma, EFFECT_SS_G_MAGMA) +/* 0x0C */ DEFINE_EFFECT_SS(Effect_Ss_G_Fire, EFFECT_SS_G_FIRE) +/* 0x0D */ DEFINE_EFFECT_SS(Effect_Ss_Lightning, EFFECT_SS_LIGHTNING) +/* 0x0E */ DEFINE_EFFECT_SS(Effect_Ss_Dt_Bubble, EFFECT_SS_DT_BUBBLE) +/* 0x0F */ DEFINE_EFFECT_SS(Effect_Ss_Hahen, EFFECT_SS_HAHEN) +/* 0x10 */ DEFINE_EFFECT_SS(Effect_Ss_Stick, EFFECT_SS_STICK) +/* 0x11 */ DEFINE_EFFECT_SS(Effect_Ss_Sibuki, EFFECT_SS_SIBUKI) +/* 0x12 */ DEFINE_EFFECT_SS(Effect_Ss_Sibuki2, EFFECT_SS_SIBUKI2) +/* 0x13 */ DEFINE_EFFECT_SS(Effect_Ss_G_Magma2, EFFECT_SS_G_MAGMA2) +/* 0x14 */ DEFINE_EFFECT_SS(Effect_Ss_Stone1, EFFECT_SS_STONE1) +/* 0x15 */ DEFINE_EFFECT_SS(Effect_Ss_HitMark, EFFECT_SS_HITMARK) +/* 0x16 */ DEFINE_EFFECT_SS(Effect_Ss_Fhg_Flash, EFFECT_SS_FHG_FLASH) +/* 0x17 */ DEFINE_EFFECT_SS(Effect_Ss_K_Fire, EFFECT_SS_K_FIRE) +/* 0x18 */ DEFINE_EFFECT_SS(Effect_Ss_Solder_Srch_Ball, EFFECT_SS_SOLDER_SRCH_BALL) +/* 0x19 */ DEFINE_EFFECT_SS(Effect_Ss_Kakera, EFFECT_SS_KAKERA) +/* 0x1A */ DEFINE_EFFECT_SS(Effect_Ss_Ice_Piece, EFFECT_SS_ICE_PIECE) +/* 0x1B */ DEFINE_EFFECT_SS(Effect_Ss_En_Ice, EFFECT_SS_EN_ICE) +/* 0x1C */ DEFINE_EFFECT_SS(Effect_Ss_Fire_Tail, EFFECT_SS_FIRE_TAIL) +/* 0x1D */ DEFINE_EFFECT_SS(Effect_Ss_En_Fire, EFFECT_SS_EN_FIRE) +/* 0x1E */ DEFINE_EFFECT_SS(Effect_Ss_Extra, EFFECT_SS_EXTRA) +/* 0x1F */ DEFINE_EFFECT_SS(Effect_Ss_Fcircle, EFFECT_SS_FCIRCLE) +/* 0x20 */ DEFINE_EFFECT_SS(Effect_Ss_Dead_Db, EFFECT_SS_DEAD_DB) +/* 0x21 */ DEFINE_EFFECT_SS(Effect_Ss_Dead_Dd, EFFECT_SS_DEAD_DD) +/* 0x22 */ DEFINE_EFFECT_SS(Effect_Ss_Dead_Ds, EFFECT_SS_DEAD_DS) +/* 0x23 */ DEFINE_EFFECT_SS(Effect_Ss_Dead_Sound, EFFECT_SS_DEAD_SOUND) +/* 0x24 */ DEFINE_EFFECT_SS(Effect_Ss_Ice_Smoke, EFFECT_SS_ICE_SMOKE) diff --git a/soh/include/tables/object_table.h b/soh/include/tables/object_table.h new file mode 100644 index 000000000..97e6aa255 --- /dev/null +++ b/soh/include/tables/object_table.h @@ -0,0 +1,411 @@ +/** + * Object Table + * + * DEFINE_OBJECT should be used for normal objects + * - Argument 1: Name of the object segment in spec + * - Argument 2: Enum value for this object + * + * DEFINE_OBJECT_UNSET and DEFINE_OBJECT_NULL are needed to define empty entries from the original game + */ +/* 0x0000 */ DEFINE_OBJECT_UNSET(OBJECT_INVALID) // Object ID 0 isn't usable and should remain unset +/* 0x0001 */ DEFINE_OBJECT(gameplay_keep, OBJECT_GAMEPLAY_KEEP) +/* 0x0002 */ DEFINE_OBJECT(gameplay_field_keep, OBJECT_GAMEPLAY_FIELD_KEEP) +/* 0x0003 */ DEFINE_OBJECT(gameplay_dangeon_keep, OBJECT_GAMEPLAY_DANGEON_KEEP) +/* 0x0004 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_4) +/* 0x0005 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_5) +/* 0x0006 */ DEFINE_OBJECT(object_human, OBJECT_HUMAN) +/* 0x0007 */ DEFINE_OBJECT(object_okuta, OBJECT_OKUTA) +/* 0x0008 */ DEFINE_OBJECT(object_crow, OBJECT_CROW) +/* 0x0009 */ DEFINE_OBJECT(object_poh, OBJECT_POH) +/* 0x000A */ DEFINE_OBJECT(object_dy_obj, OBJECT_DY_OBJ) +/* 0x000B */ DEFINE_OBJECT(object_wallmaster, OBJECT_WALLMASTER) +/* 0x000C */ DEFINE_OBJECT(object_dodongo, OBJECT_DODONGO) +/* 0x000D */ DEFINE_OBJECT(object_firefly, OBJECT_FIREFLY) +/* 0x000E */ DEFINE_OBJECT(object_box, OBJECT_BOX) +/* 0x000F */ DEFINE_OBJECT(object_fire, OBJECT_FIRE) +/* 0x0010 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_10) +/* 0x0011 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_11) +/* 0x0012 */ DEFINE_OBJECT(object_bubble, OBJECT_BUBBLE) +/* 0x0013 */ DEFINE_OBJECT(object_niw, OBJECT_NIW) +/* 0x0014 */ DEFINE_OBJECT(object_link_boy, OBJECT_LINK_BOY) +/* 0x0015 */ DEFINE_OBJECT(object_link_child, OBJECT_LINK_CHILD) +/* 0x0016 */ DEFINE_OBJECT(object_tite, OBJECT_TITE) +/* 0x0017 */ DEFINE_OBJECT(object_reeba, OBJECT_REEBA) +/* 0x0018 */ DEFINE_OBJECT(object_peehat, OBJECT_PEEHAT) +/* 0x0019 */ DEFINE_OBJECT(object_kingdodongo, OBJECT_KINGDODONGO) +/* 0x001A */ DEFINE_OBJECT(object_horse, OBJECT_HORSE) +/* 0x001B */ DEFINE_OBJECT(object_zf, OBJECT_ZF) +/* 0x001C */ DEFINE_OBJECT(object_goma, OBJECT_GOMA) +/* 0x001D */ DEFINE_OBJECT(object_zl1, OBJECT_ZL1) +/* 0x001E */ DEFINE_OBJECT(object_gol, OBJECT_GOL) +/* 0x001F */ DEFINE_OBJECT(object_dodojr, OBJECT_DODOJR) +/* 0x0020 */ DEFINE_OBJECT(object_torch2, OBJECT_TORCH2) +/* 0x0021 */ DEFINE_OBJECT(object_bl, OBJECT_BL) +/* 0x0022 */ DEFINE_OBJECT(object_tp, OBJECT_TP) +/* 0x0023 */ DEFINE_OBJECT(object_oA1, OBJECT_OA1) +/* 0x0024 */ DEFINE_OBJECT(object_st, OBJECT_ST) +/* 0x0025 */ DEFINE_OBJECT(object_bw, OBJECT_BW) +/* 0x0026 */ DEFINE_OBJECT(object_ei, OBJECT_EI) +/* 0x0027 */ DEFINE_OBJECT(object_horse_normal, OBJECT_HORSE_NORMAL) +/* 0x0028 */ DEFINE_OBJECT(object_oB1, OBJECT_OB1) +/* 0x0029 */ DEFINE_OBJECT(object_o_anime, OBJECT_O_ANIME) +/* 0x002A */ DEFINE_OBJECT(object_spot04_objects, OBJECT_SPOT04_OBJECTS) +/* 0x002B */ DEFINE_OBJECT(object_ddan_objects, OBJECT_DDAN_OBJECTS) +/* 0x002C */ DEFINE_OBJECT(object_hidan_objects, OBJECT_HIDAN_OBJECTS) +/* 0x002D */ DEFINE_OBJECT(object_horse_ganon, OBJECT_HORSE_GANON) +/* 0x002E */ DEFINE_OBJECT(object_oA2, OBJECT_OA2) +/* 0x002F */ DEFINE_OBJECT(object_spot00_objects, OBJECT_SPOT00_OBJECTS) +/* 0x0030 */ DEFINE_OBJECT(object_mb, OBJECT_MB) +/* 0x0031 */ DEFINE_OBJECT(object_bombf, OBJECT_BOMBF) +/* 0x0032 */ DEFINE_OBJECT(object_sk2, OBJECT_SK2) +/* 0x0033 */ DEFINE_OBJECT(object_oE1, OBJECT_OE1) +/* 0x0034 */ DEFINE_OBJECT(object_oE_anime, OBJECT_OE_ANIME) +/* 0x0035 */ DEFINE_OBJECT(object_oE2, OBJECT_OE2) +/* 0x0036 */ DEFINE_OBJECT(object_ydan_objects, OBJECT_YDAN_OBJECTS) +/* 0x0037 */ DEFINE_OBJECT(object_gnd, OBJECT_GND) +/* 0x0038 */ DEFINE_OBJECT(object_am, OBJECT_AM) +/* 0x0039 */ DEFINE_OBJECT(object_dekubaba, OBJECT_DEKUBABA) +/* 0x003A */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_3A) +/* 0x003B */ DEFINE_OBJECT(object_oA3, OBJECT_OA3) +/* 0x003C */ DEFINE_OBJECT(object_oA4, OBJECT_OA4) +/* 0x003D */ DEFINE_OBJECT(object_oA5, OBJECT_OA5) +/* 0x003E */ DEFINE_OBJECT(object_oA6, OBJECT_OA6) +/* 0x003F */ DEFINE_OBJECT(object_oA7, OBJECT_OA7) +/* 0x0040 */ DEFINE_OBJECT(object_jj, OBJECT_JJ) +/* 0x0041 */ DEFINE_OBJECT(object_oA8, OBJECT_OA8) +/* 0x0042 */ DEFINE_OBJECT(object_oA9, OBJECT_OA9) +/* 0x0043 */ DEFINE_OBJECT(object_oB2, OBJECT_OB2) +/* 0x0044 */ DEFINE_OBJECT(object_oB3, OBJECT_OB3) +/* 0x0045 */ DEFINE_OBJECT(object_oB4, OBJECT_OB4) +/* 0x0046 */ DEFINE_OBJECT(object_horse_zelda, OBJECT_HORSE_ZELDA) +/* 0x0047 */ DEFINE_OBJECT(object_opening_demo1, OBJECT_OPENING_DEMO1) +/* 0x0048 */ DEFINE_OBJECT(object_warp1, OBJECT_WARP1) +/* 0x0049 */ DEFINE_OBJECT(object_b_heart, OBJECT_B_HEART) +/* 0x004A */ DEFINE_OBJECT(object_dekunuts, OBJECT_DEKUNUTS) +/* 0x004B */ DEFINE_OBJECT(object_oE3, OBJECT_OE3) +/* 0x004C */ DEFINE_OBJECT(object_oE4, OBJECT_OE4) +/* 0x004D */ DEFINE_OBJECT(object_menkuri_objects, OBJECT_MENKURI_OBJECTS) +/* 0x004E */ DEFINE_OBJECT(object_oE5, OBJECT_OE5) +/* 0x004F */ DEFINE_OBJECT(object_oE6, OBJECT_OE6) +/* 0x0050 */ DEFINE_OBJECT(object_oE7, OBJECT_OE7) +/* 0x0051 */ DEFINE_OBJECT(object_oE8, OBJECT_OE8) +/* 0x0052 */ DEFINE_OBJECT(object_oE9, OBJECT_OE9) +/* 0x0053 */ DEFINE_OBJECT(object_oE10, OBJECT_OE10) +/* 0x0054 */ DEFINE_OBJECT(object_oE11, OBJECT_OE11) +/* 0x0055 */ DEFINE_OBJECT(object_oE12, OBJECT_OE12) +/* 0x0056 */ DEFINE_OBJECT(object_vali, OBJECT_VALI) +/* 0x0057 */ DEFINE_OBJECT(object_oA10, OBJECT_OA10) +/* 0x0058 */ DEFINE_OBJECT(object_oA11, OBJECT_OA11) +/* 0x0059 */ DEFINE_OBJECT(object_mizu_objects, OBJECT_MIZU_OBJECTS) +/* 0x005A */ DEFINE_OBJECT(object_fhg, OBJECT_FHG) +/* 0x005B */ DEFINE_OBJECT(object_ossan, OBJECT_OSSAN) +/* 0x005C */ DEFINE_OBJECT(object_mori_hineri1, OBJECT_MORI_HINERI1) +/* 0x005D */ DEFINE_OBJECT(object_Bb, OBJECT_BB) +/* 0x005E */ DEFINE_OBJECT(object_toki_objects, OBJECT_TOKI_OBJECTS) +/* 0x005F */ DEFINE_OBJECT(object_yukabyun, OBJECT_YUKABYUN) +/* 0x0060 */ DEFINE_OBJECT(object_zl2, OBJECT_ZL2) +/* 0x0061 */ DEFINE_OBJECT(object_mjin, OBJECT_MJIN) +/* 0x0062 */ DEFINE_OBJECT(object_mjin_flash, OBJECT_MJIN_FLASH) +/* 0x0063 */ DEFINE_OBJECT(object_mjin_dark, OBJECT_MJIN_DARK) +/* 0x0064 */ DEFINE_OBJECT(object_mjin_flame, OBJECT_MJIN_FLAME) +/* 0x0065 */ DEFINE_OBJECT(object_mjin_ice, OBJECT_MJIN_ICE) +/* 0x0066 */ DEFINE_OBJECT(object_mjin_soul, OBJECT_MJIN_SOUL) +/* 0x0067 */ DEFINE_OBJECT(object_mjin_wind, OBJECT_MJIN_WIND) +/* 0x0068 */ DEFINE_OBJECT(object_mjin_oka, OBJECT_MJIN_OKA) +/* 0x0069 */ DEFINE_OBJECT(object_haka_objects, OBJECT_HAKA_OBJECTS) +/* 0x006A */ DEFINE_OBJECT(object_spot06_objects, OBJECT_SPOT06_OBJECTS) +/* 0x006B */ DEFINE_OBJECT(object_ice_objects, OBJECT_ICE_OBJECTS) +/* 0x006C */ DEFINE_OBJECT(object_relay_objects, OBJECT_RELAY_OBJECTS) +/* 0x006D */ DEFINE_OBJECT(object_po_field, OBJECT_PO_FIELD) +/* 0x006E */ DEFINE_OBJECT(object_po_composer, OBJECT_PO_COMPOSER) +/* 0x006F */ DEFINE_OBJECT(object_mori_hineri1a, OBJECT_MORI_HINERI1A) +/* 0x0070 */ DEFINE_OBJECT(object_mori_hineri2, OBJECT_MORI_HINERI2) +/* 0x0071 */ DEFINE_OBJECT(object_mori_hineri2a, OBJECT_MORI_HINERI2A) +/* 0x0072 */ DEFINE_OBJECT(object_mori_objects, OBJECT_MORI_OBJECTS) +/* 0x0073 */ DEFINE_OBJECT(object_mori_tex, OBJECT_MORI_TEX) +/* 0x0074 */ DEFINE_OBJECT(object_spot08_obj, OBJECT_SPOT08_OBJ) +/* 0x0075 */ DEFINE_OBJECT(object_warp2, OBJECT_WARP2) +/* 0x0076 */ DEFINE_OBJECT(object_hata, OBJECT_HATA) +/* 0x0077 */ DEFINE_OBJECT(object_bird, OBJECT_BIRD) +/* 0x0078 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_78) +/* 0x0079 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_79) +/* 0x007A */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_7A) +/* 0x007B */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_7B) +/* 0x007C */ DEFINE_OBJECT(object_wood02, OBJECT_WOOD02) +/* 0x007D */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_7D) +/* 0x007E */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_7E) +/* 0x007F */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_7F) +/* 0x0080 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_80) +/* 0x0081 */ DEFINE_OBJECT(object_lightbox, OBJECT_LIGHTBOX) +/* 0x0082 */ DEFINE_OBJECT(object_pu_box, OBJECT_PU_BOX) +/* 0x0083 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_83) +/* 0x0084 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_84) +/* 0x0085 */ DEFINE_OBJECT(object_trap, OBJECT_TRAP) +/* 0x0086 */ DEFINE_OBJECT(object_vase, OBJECT_VASE) +/* 0x0087 */ DEFINE_OBJECT(object_im, OBJECT_IM) +/* 0x0088 */ DEFINE_OBJECT(object_ta, OBJECT_TA) +/* 0x0089 */ DEFINE_OBJECT(object_tk, OBJECT_TK) +/* 0x008A */ DEFINE_OBJECT(object_xc, OBJECT_XC) +/* 0x008B */ DEFINE_OBJECT(object_vm, OBJECT_VM) +/* 0x008C */ DEFINE_OBJECT(object_bv, OBJECT_BV) +/* 0x008D */ DEFINE_OBJECT(object_hakach_objects, OBJECT_HAKACH_OBJECTS) +/* 0x008E */ DEFINE_OBJECT(object_efc_crystal_light, OBJECT_EFC_CRYSTAL_LIGHT) +/* 0x008F */ DEFINE_OBJECT(object_efc_fire_ball, OBJECT_EFC_FIRE_BALL) +/* 0x0090 */ DEFINE_OBJECT(object_efc_flash, OBJECT_EFC_FLASH) +/* 0x0091 */ DEFINE_OBJECT(object_efc_lgt_shower, OBJECT_EFC_LGT_SHOWER) +/* 0x0092 */ DEFINE_OBJECT(object_efc_star_field, OBJECT_EFC_STAR_FIELD) +/* 0x0093 */ DEFINE_OBJECT(object_god_lgt, OBJECT_GOD_LGT) +/* 0x0094 */ DEFINE_OBJECT(object_light_ring, OBJECT_LIGHT_RING) +/* 0x0095 */ DEFINE_OBJECT(object_triforce_spot, OBJECT_TRIFORCE_SPOT) +/* 0x0096 */ DEFINE_OBJECT(object_bdan_objects, OBJECT_BDAN_OBJECTS) +/* 0x0097 */ DEFINE_OBJECT(object_sd, OBJECT_SD) +/* 0x0098 */ DEFINE_OBJECT(object_rd, OBJECT_RD) +/* 0x0099 */ DEFINE_OBJECT(object_po_sisters, OBJECT_PO_SISTERS) +/* 0x009A */ DEFINE_OBJECT(object_heavy_object, OBJECT_HEAVY_OBJECT) +/* 0x009B */ DEFINE_OBJECT(object_gndd, OBJECT_GNDD) +/* 0x009C */ DEFINE_OBJECT(object_fd, OBJECT_FD) +/* 0x009D */ DEFINE_OBJECT(object_du, OBJECT_DU) +/* 0x009E */ DEFINE_OBJECT(object_fw, OBJECT_FW) +/* 0x009F */ DEFINE_OBJECT(object_medal, OBJECT_MEDAL) +/* 0x00A0 */ DEFINE_OBJECT(object_horse_link_child, OBJECT_HORSE_LINK_CHILD) +/* 0x00A1 */ DEFINE_OBJECT(object_spot02_objects, OBJECT_SPOT02_OBJECTS) +/* 0x00A2 */ DEFINE_OBJECT(object_haka, OBJECT_HAKA) +/* 0x00A3 */ DEFINE_OBJECT(object_ru1, OBJECT_RU1) +/* 0x00A4 */ DEFINE_OBJECT(object_syokudai, OBJECT_SYOKUDAI) +/* 0x00A5 */ DEFINE_OBJECT(object_fd2, OBJECT_FD2) +/* 0x00A6 */ DEFINE_OBJECT(object_dh, OBJECT_DH) +/* 0x00A7 */ DEFINE_OBJECT(object_rl, OBJECT_RL) +/* 0x00A8 */ DEFINE_OBJECT(object_efc_tw, OBJECT_EFC_TW) +/* 0x00A9 */ DEFINE_OBJECT(object_demo_tre_lgt, OBJECT_DEMO_TRE_LGT) +/* 0x00AA */ DEFINE_OBJECT(object_gi_key, OBJECT_GI_KEY) +/* 0x00AB */ DEFINE_OBJECT(object_mir_ray, OBJECT_MIR_RAY) +/* 0x00AC */ DEFINE_OBJECT(object_brob, OBJECT_BROB) +/* 0x00AD */ DEFINE_OBJECT(object_gi_jewel, OBJECT_GI_JEWEL) +/* 0x00AE */ DEFINE_OBJECT(object_spot09_obj, OBJECT_SPOT09_OBJ) +/* 0x00AF */ DEFINE_OBJECT(object_spot18_obj, OBJECT_SPOT18_OBJ) +/* 0x00B0 */ DEFINE_OBJECT(object_bdoor, OBJECT_BDOOR) +/* 0x00B1 */ DEFINE_OBJECT(object_spot17_obj, OBJECT_SPOT17_OBJ) +/* 0x00B2 */ DEFINE_OBJECT(object_shop_dungen, OBJECT_SHOP_DUNGEN) +/* 0x00B3 */ DEFINE_OBJECT(object_nb, OBJECT_NB) +/* 0x00B4 */ DEFINE_OBJECT(object_mo, OBJECT_MO) +/* 0x00B5 */ DEFINE_OBJECT(object_sb, OBJECT_SB) +/* 0x00B6 */ DEFINE_OBJECT(object_gi_melody, OBJECT_GI_MELODY) +/* 0x00B7 */ DEFINE_OBJECT(object_gi_heart, OBJECT_GI_HEART) +/* 0x00B8 */ DEFINE_OBJECT(object_gi_compass, OBJECT_GI_COMPASS) +/* 0x00B9 */ DEFINE_OBJECT(object_gi_bosskey, OBJECT_GI_BOSSKEY) +/* 0x00BA */ DEFINE_OBJECT(object_gi_medal, OBJECT_GI_MEDAL) +/* 0x00BB */ DEFINE_OBJECT(object_gi_nuts, OBJECT_GI_NUTS) +/* 0x00BC */ DEFINE_OBJECT(object_sa, OBJECT_SA) +/* 0x00BD */ DEFINE_OBJECT(object_gi_hearts, OBJECT_GI_HEARTS) +/* 0x00BE */ DEFINE_OBJECT(object_gi_arrowcase, OBJECT_GI_ARROWCASE) +/* 0x00BF */ DEFINE_OBJECT(object_gi_bombpouch, OBJECT_GI_BOMBPOUCH) +/* 0x00C0 */ DEFINE_OBJECT(object_in, OBJECT_IN) +/* 0x00C1 */ DEFINE_OBJECT(object_tr, OBJECT_TR) +/* 0x00C2 */ DEFINE_OBJECT(object_spot16_obj, OBJECT_SPOT16_OBJ) +/* 0x00C3 */ DEFINE_OBJECT(object_oE1s, OBJECT_OE1S) +/* 0x00C4 */ DEFINE_OBJECT(object_oE4s, OBJECT_OE4S) +/* 0x00C5 */ DEFINE_OBJECT(object_os_anime, OBJECT_OS_ANIME) +/* 0x00C6 */ DEFINE_OBJECT(object_gi_bottle, OBJECT_GI_BOTTLE) +/* 0x00C7 */ DEFINE_OBJECT(object_gi_stick, OBJECT_GI_STICK) +/* 0x00C8 */ DEFINE_OBJECT(object_gi_map, OBJECT_GI_MAP) +/* 0x00C9 */ DEFINE_OBJECT(object_oF1d_map, OBJECT_OF1D_MAP) +/* 0x00CA */ DEFINE_OBJECT(object_ru2, OBJECT_RU2) +/* 0x00CB */ DEFINE_OBJECT(object_gi_shield_1, OBJECT_GI_SHIELD_1) +/* 0x00CC */ DEFINE_OBJECT(object_dekujr, OBJECT_DEKUJR) +/* 0x00CD */ DEFINE_OBJECT(object_gi_magicpot, OBJECT_GI_MAGICPOT) +/* 0x00CE */ DEFINE_OBJECT(object_gi_bomb_1, OBJECT_GI_BOMB_1) +/* 0x00CF */ DEFINE_OBJECT(object_oF1s, OBJECT_OF1S) +/* 0x00D0 */ DEFINE_OBJECT(object_ma2, OBJECT_MA2) +/* 0x00D1 */ DEFINE_OBJECT(object_gi_purse, OBJECT_GI_PURSE) +/* 0x00D2 */ DEFINE_OBJECT(object_hni, OBJECT_HNI) +/* 0x00D3 */ DEFINE_OBJECT(object_tw, OBJECT_TW) +/* 0x00D4 */ DEFINE_OBJECT(object_rr, OBJECT_RR) +/* 0x00D5 */ DEFINE_OBJECT(object_bxa, OBJECT_BXA) +/* 0x00D6 */ DEFINE_OBJECT(object_anubice, OBJECT_ANUBICE) +/* 0x00D7 */ DEFINE_OBJECT(object_gi_gerudo, OBJECT_GI_GERUDO) +/* 0x00D8 */ DEFINE_OBJECT(object_gi_arrow, OBJECT_GI_ARROW) +/* 0x00D9 */ DEFINE_OBJECT(object_gi_bomb_2, OBJECT_GI_BOMB_2) +/* 0x00DA */ DEFINE_OBJECT(object_gi_egg, OBJECT_GI_EGG) +/* 0x00DB */ DEFINE_OBJECT(object_gi_scale, OBJECT_GI_SCALE) +/* 0x00DC */ DEFINE_OBJECT(object_gi_shield_2, OBJECT_GI_SHIELD_2) +/* 0x00DD */ DEFINE_OBJECT(object_gi_hookshot, OBJECT_GI_HOOKSHOT) +/* 0x00DE */ DEFINE_OBJECT(object_gi_ocarina, OBJECT_GI_OCARINA) +/* 0x00DF */ DEFINE_OBJECT(object_gi_milk, OBJECT_GI_MILK) +/* 0x00E0 */ DEFINE_OBJECT(object_ma1, OBJECT_MA1) +/* 0x00E1 */ DEFINE_OBJECT(object_ganon, OBJECT_GANON) +/* 0x00E2 */ DEFINE_OBJECT(object_sst, OBJECT_SST) +/* 0x00E3 */ DEFINE_OBJECT_NULL(object_ny, OBJECT_NY_UNUSED) // unused duplicate with size 0 +/* 0x00E4 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_E4) +/* 0x00E5 */ DEFINE_OBJECT(object_ny, OBJECT_NY) +/* 0x00E6 */ DEFINE_OBJECT(object_fr, OBJECT_FR) +/* 0x00E7 */ DEFINE_OBJECT(object_gi_pachinko, OBJECT_GI_PACHINKO) +/* 0x00E8 */ DEFINE_OBJECT(object_gi_boomerang, OBJECT_GI_BOOMERANG) +/* 0x00E9 */ DEFINE_OBJECT(object_gi_bow, OBJECT_GI_BOW) +/* 0x00EA */ DEFINE_OBJECT(object_gi_glasses, OBJECT_GI_GLASSES) +/* 0x00EB */ DEFINE_OBJECT(object_gi_liquid, OBJECT_GI_LIQUID) +/* 0x00EC */ DEFINE_OBJECT(object_ani, OBJECT_ANI) +/* 0x00ED */ DEFINE_OBJECT(object_demo_6k, OBJECT_DEMO_6K) +/* 0x00EE */ DEFINE_OBJECT(object_gi_shield_3, OBJECT_GI_SHIELD_3) +/* 0x00EF */ DEFINE_OBJECT(object_gi_letter, OBJECT_GI_LETTER) +/* 0x00F0 */ DEFINE_OBJECT(object_spot15_obj, OBJECT_SPOT15_OBJ) +/* 0x00F1 */ DEFINE_OBJECT(object_jya_obj, OBJECT_JYA_OBJ) +/* 0x00F2 */ DEFINE_OBJECT(object_gi_clothes, OBJECT_GI_CLOTHES) +/* 0x00F3 */ DEFINE_OBJECT(object_gi_bean, OBJECT_GI_BEAN) +/* 0x00F4 */ DEFINE_OBJECT(object_gi_fish, OBJECT_GI_FISH) +/* 0x00F5 */ DEFINE_OBJECT(object_gi_saw, OBJECT_GI_SAW) +/* 0x00F6 */ DEFINE_OBJECT(object_gi_hammer, OBJECT_GI_HAMMER) +/* 0x00F7 */ DEFINE_OBJECT(object_gi_grass, OBJECT_GI_GRASS) +/* 0x00F8 */ DEFINE_OBJECT(object_gi_longsword, OBJECT_GI_LONGSWORD) +/* 0x00F9 */ DEFINE_OBJECT(object_spot01_objects, OBJECT_SPOT01_OBJECTS) +/* 0x00FA */ DEFINE_OBJECT_NULL(object_md, OBJECT_MD_UNUSED) // unused duplicate with size 0 +/* 0x00FB */ DEFINE_OBJECT(object_md, OBJECT_MD) +/* 0x00FC */ DEFINE_OBJECT(object_km1, OBJECT_KM1) +/* 0x00FD */ DEFINE_OBJECT(object_kw1, OBJECT_KW1) +/* 0x00FE */ DEFINE_OBJECT(object_zo, OBJECT_ZO) +/* 0x00FF */ DEFINE_OBJECT(object_kz, OBJECT_KZ) +/* 0x0100 */ DEFINE_OBJECT(object_umajump, OBJECT_UMAJUMP) +/* 0x0101 */ DEFINE_OBJECT(object_masterkokiri, OBJECT_MASTERKOKIRI) +/* 0x0102 */ DEFINE_OBJECT(object_masterkokirihead, OBJECT_MASTERKOKIRIHEAD) +/* 0x0103 */ DEFINE_OBJECT(object_mastergolon, OBJECT_MASTERGOLON) +/* 0x0104 */ DEFINE_OBJECT(object_masterzoora, OBJECT_MASTERZOORA) +/* 0x0105 */ DEFINE_OBJECT(object_aob, OBJECT_AOB) +/* 0x0106 */ DEFINE_OBJECT(object_ik, OBJECT_IK) +/* 0x0107 */ DEFINE_OBJECT(object_ahg, OBJECT_AHG) +/* 0x0108 */ DEFINE_OBJECT(object_cne, OBJECT_CNE) +/* 0x0109 */ DEFINE_OBJECT(object_gi_niwatori, OBJECT_GI_NIWATORI) +/* 0x010A */ DEFINE_OBJECT(object_skj, OBJECT_SKJ) +/* 0x010B */ DEFINE_OBJECT(object_gi_bottle_letter, OBJECT_GI_BOTTLE_LETTER) +/* 0x010C */ DEFINE_OBJECT(object_bji, OBJECT_BJI) +/* 0x010D */ DEFINE_OBJECT(object_bba, OBJECT_BBA) +/* 0x010E */ DEFINE_OBJECT(object_gi_ocarina_0, OBJECT_GI_OCARINA_0) +/* 0x010F */ DEFINE_OBJECT(object_ds, OBJECT_DS) +/* 0x0110 */ DEFINE_OBJECT(object_ane, OBJECT_ANE) +/* 0x0111 */ DEFINE_OBJECT(object_boj, OBJECT_BOJ) +/* 0x0112 */ DEFINE_OBJECT(object_spot03_object, OBJECT_SPOT03_OBJECT) +/* 0x0113 */ DEFINE_OBJECT(object_spot07_object, OBJECT_SPOT07_OBJECT) +/* 0x0114 */ DEFINE_OBJECT(object_fz, OBJECT_FZ) +/* 0x0115 */ DEFINE_OBJECT(object_bob, OBJECT_BOB) +/* 0x0116 */ DEFINE_OBJECT(object_ge1, OBJECT_GE1) +/* 0x0117 */ DEFINE_OBJECT(object_yabusame_point, OBJECT_YABUSAME_POINT) +/* 0x0118 */ DEFINE_OBJECT(object_gi_boots_2, OBJECT_GI_BOOTS_2) +/* 0x0119 */ DEFINE_OBJECT(object_gi_seed, OBJECT_GI_SEED) +/* 0x011A */ DEFINE_OBJECT(object_gnd_magic, OBJECT_GND_MAGIC) +/* 0x011B */ DEFINE_OBJECT(object_d_elevator, OBJECT_D_ELEVATOR) +/* 0x011C */ DEFINE_OBJECT(object_d_hsblock, OBJECT_D_HSBLOCK) +/* 0x011D */ DEFINE_OBJECT(object_d_lift, OBJECT_D_LIFT) +/* 0x011E */ DEFINE_OBJECT(object_mamenoki, OBJECT_MAMENOKI) +/* 0x011F */ DEFINE_OBJECT(object_goroiwa, OBJECT_GOROIWA) +/* 0x0120 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_120) +/* 0x0121 */ DEFINE_OBJECT(object_toryo, OBJECT_TORYO) +/* 0x0122 */ DEFINE_OBJECT(object_daiku, OBJECT_DAIKU) +/* 0x0123 */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_123) +/* 0x0124 */ DEFINE_OBJECT(object_nwc, OBJECT_NWC) +/* 0x0125 */ DEFINE_OBJECT(object_blkobj, OBJECT_BLKOBJ) +/* 0x0126 */ DEFINE_OBJECT(object_gm, OBJECT_GM) +/* 0x0127 */ DEFINE_OBJECT(object_ms, OBJECT_MS) +/* 0x0128 */ DEFINE_OBJECT(object_hs, OBJECT_HS) +/* 0x0129 */ DEFINE_OBJECT(object_ingate, OBJECT_INGATE) +/* 0x012A */ DEFINE_OBJECT(object_lightswitch, OBJECT_LIGHTSWITCH) +/* 0x012B */ DEFINE_OBJECT(object_kusa, OBJECT_KUSA) +/* 0x012C */ DEFINE_OBJECT(object_tsubo, OBJECT_TSUBO) +/* 0x012D */ DEFINE_OBJECT(object_gi_gloves, OBJECT_GI_GLOVES) +/* 0x012E */ DEFINE_OBJECT(object_gi_coin, OBJECT_GI_COIN) +/* 0x012F */ DEFINE_OBJECT(object_kanban, OBJECT_KANBAN) +/* 0x0130 */ DEFINE_OBJECT(object_gjyo_objects, OBJECT_GJYO_OBJECTS) +/* 0x0131 */ DEFINE_OBJECT(object_owl, OBJECT_OWL) +/* 0x0132 */ DEFINE_OBJECT(object_mk, OBJECT_MK) +/* 0x0133 */ DEFINE_OBJECT(object_fu, OBJECT_FU) +/* 0x0134 */ DEFINE_OBJECT(object_gi_ki_tan_mask, OBJECT_GI_KI_TAN_MASK) +/* 0x0135 */ DEFINE_OBJECT(object_gi_redead_mask, OBJECT_GI_REDEAD_MASK) +/* 0x0136 */ DEFINE_OBJECT(object_gi_skj_mask, OBJECT_GI_SKJ_MASK) +/* 0x0137 */ DEFINE_OBJECT(object_gi_rabit_mask, OBJECT_GI_RABIT_MASK) +/* 0x0138 */ DEFINE_OBJECT(object_gi_truth_mask, OBJECT_GI_TRUTH_MASK) +/* 0x0139 */ DEFINE_OBJECT(object_ganon_objects, OBJECT_GANON_OBJECTS) +/* 0x013A */ DEFINE_OBJECT(object_siofuki, OBJECT_SIOFUKI) +/* 0x013B */ DEFINE_OBJECT(object_stream, OBJECT_STREAM) +/* 0x013C */ DEFINE_OBJECT(object_mm, OBJECT_MM) +/* 0x013D */ DEFINE_OBJECT(object_fa, OBJECT_FA) +/* 0x013E */ DEFINE_OBJECT(object_os, OBJECT_OS) +/* 0x013F */ DEFINE_OBJECT(object_gi_eye_lotion, OBJECT_GI_EYE_LOTION) +/* 0x0140 */ DEFINE_OBJECT(object_gi_powder, OBJECT_GI_POWDER) +/* 0x0141 */ DEFINE_OBJECT(object_gi_mushroom, OBJECT_GI_MUSHROOM) +/* 0x0142 */ DEFINE_OBJECT(object_gi_ticketstone, OBJECT_GI_TICKETSTONE) +/* 0x0143 */ DEFINE_OBJECT(object_gi_brokensword, OBJECT_GI_BROKENSWORD) +/* 0x0144 */ DEFINE_OBJECT(object_js, OBJECT_JS) +/* 0x0145 */ DEFINE_OBJECT(object_cs, OBJECT_CS) +/* 0x0146 */ DEFINE_OBJECT(object_gi_prescription, OBJECT_GI_PRESCRIPTION) +/* 0x0147 */ DEFINE_OBJECT(object_gi_bracelet, OBJECT_GI_BRACELET) +/* 0x0148 */ DEFINE_OBJECT(object_gi_soldout, OBJECT_GI_SOLDOUT) +/* 0x0149 */ DEFINE_OBJECT(object_gi_frog, OBJECT_GI_FROG) +/* 0x014A */ DEFINE_OBJECT(object_mag, OBJECT_MAG) +/* 0x014B */ DEFINE_OBJECT(object_door_gerudo, OBJECT_DOOR_GERUDO) +/* 0x014C */ DEFINE_OBJECT(object_gt, OBJECT_GT) +/* 0x014D */ DEFINE_OBJECT(object_efc_erupc, OBJECT_EFC_ERUPC) +/* 0x014E */ DEFINE_OBJECT(object_zl2_anime1, OBJECT_ZL2_ANIME1) +/* 0x014F */ DEFINE_OBJECT(object_zl2_anime2, OBJECT_ZL2_ANIME2) +/* 0x0150 */ DEFINE_OBJECT(object_gi_golonmask, OBJECT_GI_GOLONMASK) +/* 0x0151 */ DEFINE_OBJECT(object_gi_zoramask, OBJECT_GI_ZORAMASK) +/* 0x0152 */ DEFINE_OBJECT(object_gi_gerudomask, OBJECT_GI_GERUDOMASK) +/* 0x0153 */ DEFINE_OBJECT(object_ganon2, OBJECT_GANON2) +/* 0x0154 */ DEFINE_OBJECT(object_ka, OBJECT_KA) +/* 0x0155 */ DEFINE_OBJECT(object_ts, OBJECT_TS) +/* 0x0156 */ DEFINE_OBJECT(object_zg, OBJECT_ZG) +/* 0x0157 */ DEFINE_OBJECT(object_gi_hoverboots, OBJECT_GI_HOVERBOOTS) +/* 0x0158 */ DEFINE_OBJECT(object_gi_m_arrow, OBJECT_GI_M_ARROW) +/* 0x0159 */ DEFINE_OBJECT(object_ds2, OBJECT_DS2) +/* 0x015A */ DEFINE_OBJECT(object_ec, OBJECT_EC) +/* 0x015B */ DEFINE_OBJECT(object_fish, OBJECT_FISH) +/* 0x015C */ DEFINE_OBJECT(object_gi_sutaru, OBJECT_GI_SUTARU) +/* 0x015D */ DEFINE_OBJECT(object_gi_goddess, OBJECT_GI_GODDESS) +/* 0x015E */ DEFINE_OBJECT(object_ssh, OBJECT_SSH) +/* 0x015F */ DEFINE_OBJECT(object_bigokuta, OBJECT_BIGOKUTA) +/* 0x0160 */ DEFINE_OBJECT(object_bg, OBJECT_BG) +/* 0x0161 */ DEFINE_OBJECT(object_spot05_objects, OBJECT_SPOT05_OBJECTS) +/* 0x0162 */ DEFINE_OBJECT(object_spot12_obj, OBJECT_SPOT12_OBJ) +/* 0x0163 */ DEFINE_OBJECT(object_bombiwa, OBJECT_BOMBIWA) +/* 0x0164 */ DEFINE_OBJECT(object_hintnuts, OBJECT_HINTNUTS) +/* 0x0165 */ DEFINE_OBJECT(object_rs, OBJECT_RS) +/* 0x0166 */ DEFINE_OBJECT(object_spot00_break, OBJECT_SPOT00_BREAK) +/* 0x0167 */ DEFINE_OBJECT(object_gla, OBJECT_GLA) +/* 0x0168 */ DEFINE_OBJECT(object_shopnuts, OBJECT_SHOPNUTS) +/* 0x0169 */ DEFINE_OBJECT(object_geldb, OBJECT_GELDB) +/* 0x016A */ DEFINE_OBJECT(object_gr, OBJECT_GR) +/* 0x016B */ DEFINE_OBJECT(object_dog, OBJECT_DOG) +/* 0x016C */ DEFINE_OBJECT(object_jya_iron, OBJECT_JYA_IRON) +/* 0x016D */ DEFINE_OBJECT(object_jya_door, OBJECT_JYA_DOOR) +/* 0x016E */ DEFINE_OBJECT_UNSET(OBJECT_UNSET_16E) +/* 0x016F */ DEFINE_OBJECT(object_spot11_obj, OBJECT_SPOT11_OBJ) +/* 0x0170 */ DEFINE_OBJECT(object_kibako2, OBJECT_KIBAKO2) +/* 0x0171 */ DEFINE_OBJECT(object_dns, OBJECT_DNS) +/* 0x0172 */ DEFINE_OBJECT(object_dnk, OBJECT_DNK) +/* 0x0173 */ DEFINE_OBJECT(object_gi_fire, OBJECT_GI_FIRE) +/* 0x0174 */ DEFINE_OBJECT(object_gi_insect, OBJECT_GI_INSECT) +/* 0x0175 */ DEFINE_OBJECT(object_gi_butterfly, OBJECT_GI_BUTTERFLY) +/* 0x0176 */ DEFINE_OBJECT(object_gi_ghost, OBJECT_GI_GHOST) +/* 0x0177 */ DEFINE_OBJECT(object_gi_soul, OBJECT_GI_SOUL) +/* 0x0178 */ DEFINE_OBJECT(object_bowl, OBJECT_BOWL) +/* 0x0179 */ DEFINE_OBJECT(object_demo_kekkai, OBJECT_DEMO_KEKKAI) +/* 0x017A */ DEFINE_OBJECT(object_efc_doughnut, OBJECT_EFC_DOUGHNUT) +/* 0x017B */ DEFINE_OBJECT(object_gi_dekupouch, OBJECT_GI_DEKUPOUCH) +/* 0x017C */ DEFINE_OBJECT(object_ganon_anime1, OBJECT_GANON_ANIME1) +/* 0x017D */ DEFINE_OBJECT(object_ganon_anime2, OBJECT_GANON_ANIME2) +/* 0x017E */ DEFINE_OBJECT(object_ganon_anime3, OBJECT_GANON_ANIME3) +/* 0x017F */ DEFINE_OBJECT(object_gi_rupy, OBJECT_GI_RUPY) +/* 0x0180 */ DEFINE_OBJECT(object_spot01_matoya, OBJECT_SPOT01_MATOYA) +/* 0x0181 */ DEFINE_OBJECT(object_spot01_matoyab, OBJECT_SPOT01_MATOYAB) +/* 0x0182 */ DEFINE_OBJECT(object_mu, OBJECT_MU) +/* 0x0183 */ DEFINE_OBJECT(object_wf, OBJECT_WF) +/* 0x0184 */ DEFINE_OBJECT(object_skb, OBJECT_SKB) +/* 0x0185 */ DEFINE_OBJECT(object_gj, OBJECT_GJ) +/* 0x0186 */ DEFINE_OBJECT(object_geff, OBJECT_GEFF) +/* 0x0187 */ DEFINE_OBJECT(object_haka_door, OBJECT_HAKA_DOOR) +/* 0x0188 */ DEFINE_OBJECT(object_gs, OBJECT_GS) +/* 0x0189 */ DEFINE_OBJECT(object_ps, OBJECT_PS) +/* 0x018A */ DEFINE_OBJECT(object_bwall, OBJECT_BWALL) +/* 0x018B */ DEFINE_OBJECT(object_cow, OBJECT_COW) +/* 0x018C */ DEFINE_OBJECT(object_cob, OBJECT_COB) +/* 0x018D */ DEFINE_OBJECT(object_gi_sword_1, OBJECT_GI_SWORD_1) +/* 0x018E */ DEFINE_OBJECT(object_door_killer, OBJECT_DOOR_KILLER) +/* 0x018F */ DEFINE_OBJECT(object_ouke_haka, OBJECT_OUKE_HAKA) +/* 0x0190 */ DEFINE_OBJECT(object_timeblock, OBJECT_TIMEBLOCK) +/* 0x0191 */ DEFINE_OBJECT(object_zl4, OBJECT_ZL4) diff --git a/soh/include/ultra64.h b/soh/include/ultra64.h new file mode 100644 index 000000000..65ef6d147 --- /dev/null +++ b/soh/include/ultra64.h @@ -0,0 +1,32 @@ +#ifndef ULTRA64_H +#define ULTRA64_H + +#include "ultra64/types.h" +#include "unk.h" + +#include "libc/stdarg.h" +#include "libc/stdbool.h" +#include "libc/stddef.h" +#include "libc/stdlib.h" +#include "libc/math.h" + +#include "ultra64/exception.h" +#include "ultra64/rcp.h" +#include "ultra64/rdp.h" +#include "ultra64/rsp.h" +#include "ultra64/thread.h" +#include "ultra64/convert.h" +#include "ultra64/time.h" +#include "ultra64/message.h" +#include "ultra64/sptask.h" +#include "ultra64/gu.h" +#include "ultra64/vi.h" +#include "ultra64/pi.h" +#include "ultra64/controller.h" +#include "ultra64/printf.h" +#include "ultra64/mbi.h" +#include "ultra64/pfs.h" +#include "ultra64/motor.h" +#include "ultra64/r4300.h" + +#endif diff --git a/soh/include/unk.h b/soh/include/unk.h new file mode 100644 index 000000000..e28f2bd8c --- /dev/null +++ b/soh/include/unk.h @@ -0,0 +1,16 @@ +#ifndef UNK_H +#define UNK_H + +#define UNK_TYPE s32 +#define UNK_TYPE1 s8 +#define UNK_TYPE2 s16 +#define UNK_TYPE4 s32 +#define UNK_PTR void* +#define UNK_RET void +#define UNK_FUN_ARG void(*)(void) +#define UNK_FUN_PTR(name) void(*name)(void) +#define UNK_ARGS +#define UNK_SIZE 1 +#define UNK_LINE 1 + +#endif diff --git a/soh/include/variables.h b/soh/include/variables.h new file mode 100644 index 000000000..80e3b792d --- /dev/null +++ b/soh/include/variables.h @@ -0,0 +1,247 @@ +#ifndef VARIABLES_H +#define VARIABLES_H + +#include "z64.h" +#include "segment_symbols.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + extern u32 osTvType; + extern u32 osRomBase; + extern u32 osResetType; + extern u32 osMemSize; + extern u8 osAppNmiBuffer[0x40]; + + extern u8 D_80009320[]; + extern u8 D_800093F0[]; + extern s8 D_80009430; + extern u32 D_80009460; + extern u32 gDmaMgrDmaBuffSize; + extern vu8 gViConfigUseDefault; + extern u8 gViConfigAdditionalScanLines; + extern u32 gViConfigFeatures; + extern f32 gViConfigXScale; + extern f32 gViConfigYScale; + extern OSPiHandle* gCartHandle; + extern u32 __osPiAccessQueueEnabled; + extern OSViMode osViModePalLan1; + extern s32 osViClock; + extern u32 __osShutdown; + extern OSHWIntr __OSGlobalIntMask; + extern OSThread* __osThreadTail[]; + extern OSThread* __osRunQueue; + extern OSThread* __osActiveQueue; + extern OSThread* __osRunningThread; + extern OSThread* __osFaultedThread; + extern OSPiHandle* __osPiTable; + extern OSPiHandle* __osCurrentHandle[]; + extern OSTimer* __osTimerList; + extern OSViMode osViModeNtscLan1; + extern OSViMode osViModeMpalLan1; + extern OSViContext* __osViCurr; + extern OSViContext* __osViNext; + extern OSViMode osViModeFpalLan1; + extern u32 __additional_scanline; + extern u8 gBuildVersion[]; + extern u8 gBuildTeam[]; + extern u8 gBuildDate[]; + extern u8 gBuildMakeOption[]; + extern OSMesgQueue gPiMgrCmdQ; + extern OSViMode gViConfigMode; + extern u8 D_80013960; + extern OSMesgQueue __osPiAccessQueue; + extern OSPiHandle __Dom1SpeedParam; + extern OSPiHandle __Dom2SpeedParam; + extern OSTime __osCurrentTime; + extern u32 __osBaseCounter; + extern u32 __osViIntrCount; + extern u32 __osTimerCounter; + extern DmaEntry gDmaDataTable[0x60C]; + extern u64 D_801120C0[]; + extern u8 D_80113070[]; + extern u64 gJpegUCode[]; + extern EffectSsOverlay gEffectSsOverlayTable[EFFECT_SS_TYPE_MAX]; + extern Gfx D_80116280[]; + extern ActorOverlay gActorOverlayTable[ACTOR_ID_MAX]; // original name: "actor_dlftbls" 801162A0 + extern s32 gMaxActorId; // original name: "MaxProfile" + extern s32 gDbgCamEnabled; + extern GameStateOverlay gGameStateOverlayTable[6]; + extern u8 gWeatherMode; + extern u8 D_8011FB34; + extern u8 D_8011FB38; + extern u8 gSkyboxBlendingEnabled; + extern u16 gTimeIncrement; + extern struct_8011FC1C D_8011FC1C[][9]; + extern SkyboxFile gSkyboxFiles[]; + extern s32 gZeldaArenaLogSeverity; + extern MapData gMapDataTable; + extern s16 gSpoilingItems[3]; + extern s16 gSpoilingItemReverts[3]; + extern FlexSkeletonHeader* gPlayerSkelHeaders[2]; + extern u8 gPlayerModelTypes[][5]; + extern Gfx* D_80125DE8[]; + extern Gfx* D_80125E08[]; + extern Gfx* D_80125E18[]; + extern Gfx* D_80125EF8[]; + extern Gfx gCullBackDList[]; + extern Gfx gCullFrontDList[]; + extern Gfx gEmptyDL[]; + extern u32 gBitFlags[32]; + extern u16 gEquipMasks[4]; + extern u16 gEquipNegMasks[4]; + extern u32 gUpgradeMasks[8]; + extern u8 gEquipShifts[4]; + extern u8 gUpgradeShifts[8]; + extern u16 gUpgradeCapacities[8][4]; + extern u32 gGsFlagsMasks[4]; + extern u32 gGsFlagsShifts[4]; + extern void* gItemIcons[0x82]; + extern u8 gItemSlots[56]; + extern void (*gSceneCmdHandlers[26])(GlobalContext*, SceneCmd*); + extern s16 gLinkObjectIds[2]; + extern u32 gObjectTableSize; + extern RomFile gObjectTable[OBJECT_ID_MAX]; + extern EntranceInfo gEntranceTable[1556]; + extern SceneTableEntry gSceneTable[SCENE_ID_MAX]; + extern u16 gSramSlotOffsets[]; + // 4 16-colors palettes + extern u64 gMojiFontTLUTs[4][4]; // original name: "moji_tlut" + extern u64 gMojiFontTex[]; // original name: "font_ff" + extern KaleidoMgrOverlay gKaleidoMgrOverlayTable[KALEIDO_OVL_MAX]; + extern KaleidoMgrOverlay* gKaleidoMgrCurOvl; + extern u8 gBossMarkState; + extern void* D_8012D1F0; + extern s32 gScreenWidth; + extern s32 gScreenHeight; + extern Mtx gMtxClear; + extern MtxF gMtxFClear; + extern u32 gIsCtrlr2Valid; + extern vu32 gIrqMgrResetStatus; + extern volatile OSTime gIrqMgrRetraceTime; + extern s16* gWaveSamples[9]; + extern f32 gBendPitchOneOctaveFrequencies[256]; + extern f32 gBendPitchTwoSemitonesFrequencies[256]; + extern f32 gNoteFrequencies[]; + extern u8 gDefaultShortNoteVelocityTable[16]; + extern u8 gDefaultShortNoteGateTimeTable[16]; + extern AdsrEnvelope gDefaultEnvelope[4]; + extern NoteSubEu gZeroNoteSub; + extern NoteSubEu gDefaultNoteSub; + extern u16 gHeadsetPanQuantization[64]; + extern s16 D_8012FBA8[]; + extern f32 gHeadsetPanVolume[128]; + extern f32 gStereoPanVolume[128]; + extern f32 gDefaultPanVolume[128]; + extern s16 sLowPassFilterData[16 * 8]; + extern s16 sHighPassFilterData[15 * 8]; + extern s32 gAudioContextInitalized; + extern u8 gIsLargeSoundBank[7]; + extern u8 gChannelsPerBank[4][7]; + extern u8 gUsedChannelsPerBank[4][7]; + extern u8 gMorphaTransposeTable[16]; + extern u8* gFrogsSongPtr; + extern OcarinaNote* gScarecrowCustomSongPtr; + extern u8* gScarecrowSpawnSongPtr; + extern OcarinaSongInfo gOcarinaSongNotes[]; + extern SoundParams* gSoundParams[7]; + extern char D_80133390[]; + extern char D_80133398[]; + extern SoundBankEntry* gSoundBanks[7]; + extern u8 gSfxChannelLayout; + extern Vec3f D_801333D4; + extern f32 D_801333E0; + extern s8 D_801333E8; + extern u8 D_801333F0; + extern u8 gAudioSfxSwapOff; + extern u8 D_80133408; + extern u8 D_8013340C; + extern u8 gAudioSpecId; + extern u8 D_80133418; + extern AudioSpec gAudioSpecs[18]; + extern s32 gOverlayLogSeverity; + extern s32 gSystemArenaLogSeverity; + extern u8 __osPfsInodeCacheBank; + extern s32 __osPfsLastChannel; + + extern const s16 D_8014A6C0[]; +#define gTatumsPerBeat (D_8014A6C0[1]) + extern const AudioContextInitSizes D_8014A6C4; + extern s16 gOcarinaSongItemMap[]; + extern u8 gSoundFontTable[]; + extern u8 gSequenceFontTable[]; + extern u8 gSequenceTable[]; + extern u8 gSampleBankTable[]; + extern u8 D_80155F50[]; + extern u8 D_80157580[]; + extern u8 D_801579A0[]; + extern u64 gJpegUCodeData[]; + + extern SaveContext gSaveContext; + extern GameInfo* gGameInfo; + extern u16 D_8015FCC0; + extern u16 D_8015FCC2; + extern u16 D_8015FCC4; + extern u8 D_8015FCC8; + extern u8 gCustomLensFlareOn; + extern Vec3f gCustomLensFlarePos; + extern s16 gLensFlareScale; + extern f32 gLensFlareColorIntensity; + extern s16 gLensFlareScreenFillAlpha; + extern LightningStrike gLightningStrike; + extern MapData* gMapData; + extern f32 gBossMarkScale; + extern PauseMapMarksData* gLoadedPauseMarkDataTable; + extern s32 gTrnsnUnkState; + extern Color_RGBA8_u32 D_801614B0; + extern PreNmiBuff* gAppNmiBufferPtr; + extern SchedContext gSchedContext; + extern PadMgr gPadMgr; + extern uintptr_t gSegments[NUM_SEGMENTS]; + extern volatile OSTime D_8016A520; + extern volatile OSTime D_8016A528; + extern volatile OSTime D_8016A530; + extern volatile OSTime D_8016A538; + extern volatile OSTime D_8016A540; + extern volatile OSTime D_8016A548; + extern volatile OSTime D_8016A550; + extern volatile OSTime D_8016A558; + extern volatile OSTime gRSPAudioTotalTime; + extern volatile OSTime gRSPGFXTotalTime; + extern volatile OSTime gRSPOtherTotalTime; + extern volatile OSTime gRDPTotalTime; + extern FaultThreadStruct gFaultStruct; + +extern ActiveSound gActiveSounds[7][MAX_CHANNELS_PER_BANK]; // total size = 0xA8 +extern u8 gSoundBankMuted[]; +extern u8 D_801333F0; +extern u8 gAudioSfxSwapOff; +extern u16 gAudioSfxSwapSource[10]; +extern u16 gAudioSfxSwapTarget[10]; +extern u8 gAudioSfxSwapMode[10]; +extern unk_D_8016E750 D_8016E750[4]; +extern AudioContext gAudioContext; +extern void(*D_801755D0)(void); + + extern u32 __osMalloc_FreeBlockTest_Enable; + extern Arena gSystemArena; + extern OSPifRam __osPifInternalBuff; + extern u8 __osContLastPoll; + extern u8 __osMaxControllers; + extern __OSInode __osPfsInodeCache; + extern OSPifRam gPifMempakBuf; + extern u16 gZBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; // 0x25800 bytes + extern u64 gGfxSPTaskOutputBuffer[0x3000]; // 0x18000 bytes + extern u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE]; // 0xC00 bytes + extern u8 gGfxSPTaskStack[0x400]; // 0x400 bytes + extern GfxPool gGfxPools[2]; // 0x24820 bytes + extern u8 gAudioHeap[0x38000]; // 0x38000 bytes + extern u8 gSystemHeap[]; + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/soh/include/vt.h b/soh/include/vt.h new file mode 100644 index 000000000..46cbf05e7 --- /dev/null +++ b/soh/include/vt.h @@ -0,0 +1,35 @@ +#ifndef VT_H +#define VT_H + +#define VT_COLOR_BLACK 0 +#define VT_COLOR_RED 1 +#define VT_COLOR_GREEN 2 +#define VT_COLOR_YELLOW 3 +#define VT_COLOR_BLUE 4 +#define VT_COLOR_PURPLE 5 +#define VT_COLOR_CYAN 6 +#define VT_COLOR_WHITE 7 +#define VT_COLOR_LIGHTGRAY 8 +#define VT_COLOR_DARKGRAY 9 + +#define VT_COLOR_FOREGROUND 3 +#define VT_COLOR_BACKGROUND 4 + +#define VT_COLOR_EXPAND0(type, color) #type #color +#define VT_COLOR_EXPAND1(type, color) VT_COLOR_EXPAND0(type, color) +#define VT_COLOR(type, color) VT_COLOR_EXPAND1(VT_COLOR_##type, VT_COLOR_##color) + +#define VT_ESC "\x1b" +#define VT_CSI "[" +#define VT_CUP(x, y) VT_ESC VT_CSI y ";" x "H" +#define VT_ED(n) VT_ESC VT_CSI #n "J" +#define VT_SGR(n) VT_ESC VT_CSI n "m" + +// Add more macros if necessary +#define VT_COL(back, fore) VT_SGR(VT_COLOR(BACKGROUND, back) ";" VT_COLOR(FOREGROUND, fore)) +#define VT_FGCOL(color) VT_SGR(VT_COLOR(FOREGROUND, color)) +#define VT_BGCOL(color) VT_SGR(VT_COLOR(BACKGROUND, color)) +#define VT_RST VT_SGR("") +#define VT_CLS VT_ED(2) + +#endif diff --git a/soh/include/z64.h b/soh/include/z64.h new file mode 100644 index 000000000..6e783eeb9 --- /dev/null +++ b/soh/include/z64.h @@ -0,0 +1,2096 @@ +#ifndef Z64_H +#define Z64_H + +#include "ultra64.h" +#include "ultra64/gs2dex.h" +#include "z64save.h" +#include "z64light.h" +#include "z64bgcheck.h" +#include "z64actor.h" +#include "z64player.h" +#include "z64audio.h" +#include "z64object.h" +#include "z64camera.h" +#include "z64environment.h" +#include "z64cutscene.h" +#include "z64collision_check.h" +#include "z64scene.h" +#include "z64effect.h" +#include "z64item.h" +#include "z64animation.h" +#include "z64dma.h" +#include "z64math.h" +#include "z64map_mark.h" +#include "z64skin.h" +#include "z64transition.h" +#include "z64interface.h" +#include "sequence.h" +#include "sfx.h" +#include "color.h" +#include "ichain.h" +#include "regs.h" + +#ifdef __cplusplus +namespace Ship +{ + class Resource; + class Scene; + class DisplayList; +}; +#endif + +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 + +#define REGION_NULL 0 +#define REGION_US 1 +#define REGION_JP 2 +#define REGION_EU 3 + +#define Z_PRIORITY_MAIN 10 +#define Z_PRIORITY_GRAPH 11 +#define Z_PRIORITY_AUDIOMGR 12 +#define Z_PRIORITY_PADMGR 14 +#define Z_PRIORITY_SCHED 15 +#define Z_PRIORITY_DMAMGR 16 +#define Z_PRIORITY_IRQMGR 17 + +// NOTE: Once we start supporting other builds, this can be changed with an ifdef +#define REGION_NATIVE REGION_EU + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct{ + /* 0x00 */ char unk[0x4]; + /* 0x04 */ MtxF mf; +} HorseStruct; + +// Game Info aka. Static Context (dbg ram start: 80210A10) +// Data normally accessed through REG macros (see regs.h) +typedef struct { + /* 0x00 */ s32 regPage; // 1 is first page + /* 0x04 */ s32 regGroup; // "register" group (R, RS, RO, RP etc.) + /* 0x08 */ s32 regCur; // selected register within page + /* 0x0C */ s32 dpadLast; + /* 0x10 */ s32 repeat; + /* 0x14 */ s16 data[REG_GROUPS * REG_PER_GROUP]; // 0xAE0 entries +} GameInfo; // size = 0x15D4 + +typedef struct { + /* 0x00000 */ u16 headMagic; // GFXPOOL_HEAD_MAGIC + /* 0x00008 */ Gfx polyOpaBuffer[0x2FC0]; + /* 0x0BF08 */ Gfx polyXluBuffer[0x1000]; + /* 0x0BF08 */ Gfx polyKalBuffer[0x1000]; + /* 0x0FF08 */ Gfx overlayBuffer[0x800]; + /* 0x11F08 */ Gfx workBuffer[0x100]; + /* 0x11308 */ Gfx unusedBuffer[0x40]; + /* 0x12408 */ u16 tailMagic; // GFXPOOL_TAIL_MAGIC +} GfxPool; // size = 0x24820 + +typedef struct { + /* 0x0000 */ u32 size; + /* 0x0004 */ void* bufp; + /* 0x0008 */ void* head; + /* 0x000C */ void* tail; +} TwoHeadArena; // size = 0x10 + +typedef struct { + /* 0x0000 */ u32 size; + /* 0x0004 */ Gfx* bufp; + /* 0x0008 */ Gfx* p; + /* 0x000C */ Gfx* d; +} TwoHeadGfxArena; // size = 0x10 + +typedef struct { + /* 0x00 */ u16* fb1; + /* 0x04 */ u16* swapBuffer; + /* 0x08 */ OSViMode* viMode; + /* 0x0C */ u32 features; + /* 0x10 */ u8 unk_10; + /* 0x11 */ s8 updateRate; + /* 0x12 */ s8 updateRate2; + /* 0x13 */ u8 unk_13; + /* 0x14 */ f32 xScale; + /* 0x18 */ f32 yScale; +} CfbInfo; // size = 0x1C + +typedef struct OSScTask { + /* 0x00 */ struct OSScTask* next; + /* 0x04 */ u32 state; + /* 0x08 */ u32 flags; + /* 0x0C */ CfbInfo* framebuffer; + /* 0x10 */ OSTask list; + /* 0x50 */ OSMesgQueue* msgQ; + /* 0x54 */ OSMesg msg; +} OSScTask; + +typedef struct GraphicsContext { + /* 0x0000 */ Gfx* polyOpaBuffer; // Pointer to "Zelda 0" + /* 0x0004 */ Gfx* polyXluBuffer; // Pointer to "Zelda 1" + /* 0xXXX */ Gfx* polyKalBuffer; // Pointer to "Rome" + /* 0x0008 */ char unk_008[0x08]; // Unused, could this be pointers to "Zelda 2" / "Zelda 3" + /* 0x0010 */ Gfx* overlayBuffer; // Pointer to "Zelda 4" + /* 0x0014 */ u32 unk_014; + /* 0x0018 */ char unk_018[0x20]; + /* 0x0038 */ OSMesg msgBuff[0x08]; + /* 0x0058 */ OSMesgQueue* schedMsgQ; + /* 0x005C */ OSMesgQueue queue; + /* 0x0074 */ char unk_074[0x04]; + /* 0x0078 */ OSScTask task; // size of OSScTask might be wrong + /* 0x00D0 */ char unk_0D0[0xE0]; + /* 0x01B0 */ Gfx* workBuffer; + /* 0x01B4 */ TwoHeadGfxArena work; + /* 0x01C4 */ char unk_01C4[0xC0]; + /* 0x0284 */ OSViMode* viMode; + /* 0x0288 */ char unk_0288[0x20]; // Unused, could this be Zelda 2/3 ? + /* 0x02A8 */ TwoHeadGfxArena overlay; // "Zelda 4" + /* 0x02B8 */ TwoHeadGfxArena polyOpa; // "Zelda 0" + /* 0x02C8 */ TwoHeadGfxArena polyXlu; // "Zelda 1" + /* 0x0XXX */ TwoHeadGfxArena polyKal; // When in Rome... + /* 0x02D8 */ u32 gfxPoolIdx; + /* 0x02DC */ u16* curFrameBuffer; + /* 0x02E0 */ char unk_2E0[0x04]; + /* 0x02E4 */ u32 viFeatures; + /* 0x02E8 */ s32 fbIdx; + /* 0x02EC */ void (*callback)(struct GraphicsContext*, void*); + /* 0x02F0 */ void* callbackParam; + /* 0x02F4 */ f32 xScale; + /* 0x02F8 */ f32 yScale; + /* 0x02FC */ char unk_2FC[0x04]; +} GraphicsContext; // size = 0x300 + +typedef struct { + /* 0x00 */ OSContPad cur; + /* 0x06 */ OSContPad prev; + /* 0x0C */ OSContPad press; // X/Y store delta from last frame + /* 0x12 */ OSContPad rel; // X/Y store adjusted +} Input; // size = 0x18 + +typedef struct { + /* 0x0000 */ s32 topY; // uly (upper left y) + /* 0x0004 */ s32 bottomY; // lry (lower right y) + /* 0x0008 */ s32 leftX; // ulx (upper left x) + /* 0x000C */ s32 rightX; // lrx (lower right x) +} Viewport; // size = 0x10 + +typedef struct { + /* 0x0000 */ s32 magic; // string literal "VIEW" / 0x56494557 + /* 0x0004 */ GraphicsContext* gfxCtx; + /* 0x0008 */ Viewport viewport; + /* 0x0018 */ f32 fovy; // vertical field of view in degrees + /* 0x001C */ f32 zNear; // distance to near clipping plane + /* 0x0020 */ f32 zFar; // distance to far clipping plane + /* 0x0024 */ f32 scale; // scale for matrix elements + /* 0x0028 */ Vec3f eye; + /* 0x0034 */ Vec3f lookAt; + /* 0x0040 */ Vec3f up; + /* 0x0050 */ Vp vp; + /* 0x0060 */ Mtx projection; + /* 0x00A0 */ Mtx viewing; + /* 0x00E0 */ Mtx* projectionPtr; + /* 0x00E4 */ Mtx* viewingPtr; + /* 0x00E8 */ Vec3f unk_E8; + /* 0x00F4 */ Vec3f unk_F4; + /* 0x0100 */ f32 unk_100; + /* 0x0104 */ Vec3f unk_104; + /* 0x0110 */ Vec3f unk_110; + /* 0x011C */ u16 normal; // used to normalize the projection matrix + /* 0x0120 */ s32 flags; + /* 0x0124 */ s32 unk_124; +} View; // size = 0x128 + +typedef struct { + /* 0x00 */ u8 seqId; + /* 0x01 */ u8 natureAmbienceId; +} SequenceContext; // size = 0x2 + +typedef struct { + /* 0x00 */ s32 enabled; + /* 0x04 */ s32 timer; +} FrameAdvanceContext; // size = 0x8 + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ f32 unk_0C; // radius? + /* 0x10 */ Color_RGB8 color; +} TargetContextEntry; // size = 0x14 + +typedef struct { + /* 0x00 */ Vec3f naviRefPos; // possibly wrong + /* 0x0C */ Vec3f targetCenterPos; + /* 0x18 */ Color_RGBAf naviInner; + /* 0x28 */ Color_RGBAf naviOuter; + /* 0x38 */ Actor* arrowPointedActor; + /* 0x3C */ Actor* targetedActor; + /* 0x40 */ f32 unk_40; + /* 0x44 */ f32 unk_44; + /* 0x48 */ s16 unk_48; + /* 0x4A */ u8 activeCategory; + /* 0x4B */ u8 unk_4B; + /* 0x4C */ s8 unk_4C; + /* 0x4D */ char unk_4D[0x03]; + /* 0x50 */ TargetContextEntry arr_50[3]; + /* 0x8C */ Actor* unk_8C; + /* 0x90 */ Actor* bgmEnemy; // The nearest enemy to player with the right flags that will trigger NA_BGM_ENEMY + /* 0x94 */ Actor* unk_94; +} TargetContext; // size = 0x98 + +typedef struct { + /* 0x00 */ void* texture; + /* 0x04 */ s16 x; + /* 0x06 */ s16 y; + /* 0x08 */ u8 width; + /* 0x09 */ u8 height; + /* 0x0A */ u8 durationTimer; // how long the title card appears for before fading + /* 0x0B */ u8 delayTimer; // how long the title card waits to appear + /* 0x0C */ s16 alpha; + /* 0x0E */ s16 intensity; +} TitleCardContext; // size = 0x10 + +typedef struct { + /* 0x00 */ s32 length; // number of actors loaded of this category + /* 0x04 */ Actor* head; // pointer to head of the linked list of this category (most recent actor added) +} ActorListEntry; // size = 0x08 + +typedef struct { + /* 0x0000 */ u8 freezeFlashTimer; + /* 0x0001 */ char unk_01[0x01]; + /* 0x0002 */ u8 unk_02; + /* 0x0003 */ u8 unk_03; + /* 0x0004 */ char unk_04[0x04]; + /* 0x0008 */ u8 total; // total number of actors loaded + /* 0x0009 */ char unk_09[0x03]; + /* 0x000C */ ActorListEntry actorLists[12]; + /* 0x006C */ TargetContext targetCtx; + struct { + /* 0x0104 */ u32 swch; + /* 0x0108 */ u32 tempSwch; + /* 0x010C */ u32 unk0; + /* 0x0110 */ u32 unk1; + /* 0x0114 */ u32 chest; + /* 0x0118 */ u32 clear; + /* 0x011C */ u32 tempClear; + /* 0x0120 */ u32 collect; + /* 0x0124 */ u32 tempCollect; + } flags; + /* 0x0128 */ TitleCardContext titleCtx; + /* 0x0138 */ char unk_138[0x04]; + /* 0x013C */ void* absoluteSpace; // Space used to allocate actor overlays of alloc type 1 +} ActorContext; // size = 0x140 + +typedef struct { + /* 0x00 */ char unk_00[0x4]; + /* 0x04 */ void* segment; + /* 0x08 */ u8 state; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ u16 frames; + /* 0x12 */ u16 unk_12; + /* 0x14 */ s32 unk_14; + /* 0x18 */ u16 unk_18; + /* 0x1A */ u8 unk_1A; + /* 0x1B */ u8 unk_1B; + /* 0x1C */ CutsceneCameraPoint* cameraFocus; + /* 0x20 */ CutsceneCameraPoint* cameraPosition; + /* 0x24 */ CsCmdActorAction* linkAction; + /* 0x28 */ CsCmdActorAction* npcActions[10]; // "npcdemopnt" +} CutsceneContext; // size = 0x50 + +typedef struct { + /* 0x00 */ u16 countdown; + /* 0x04 */ Vec3f worldPos; + /* 0x10 */ Vec3f projectedPos; +} SoundSource; // size = 0x1C + +typedef enum { + /* 0x00 */ SKYBOX_NONE, + /* 0x01 */ SKYBOX_NORMAL_SKY, + /* 0x02 */ SKYBOX_BAZAAR, + /* 0x03 */ SKYBOX_OVERCAST_SUNSET, + /* 0x04 */ SKYBOX_MARKET_ADULT, + /* 0x05 */ SKYBOX_CUTSCENE_MAP, + /* 0x07 */ SKYBOX_HOUSE_LINK = 7, + /* 0x09 */ SKYBOX_MARKET_CHILD_DAY = 9, + /* 0x0A */ SKYBOX_MARKET_CHILD_NIGHT, + /* 0x0B */ SKYBOX_HAPPY_MASK_SHOP, + /* 0x0C */ SKYBOX_HOUSE_KNOW_IT_ALL_BROTHERS, + /* 0x0E */ SKYBOX_HOUSE_OF_TWINS = 14, + /* 0x0F */ SKYBOX_STABLES, + /* 0x10 */ SKYBOX_HOUSE_KAKARIKO, + /* 0x11 */ SKYBOX_KOKIRI_SHOP, + /* 0x13 */ SKYBOX_GORON_SHOP = 19, + /* 0x14 */ SKYBOX_ZORA_SHOP, + /* 0x16 */ SKYBOX_POTION_SHOP_KAKARIKO = 22, + /* 0x17 */ SKYBOX_POTION_SHOP_MARKET, + /* 0x18 */ SKYBOX_BOMBCHU_SHOP, + /* 0x1A */ SKYBOX_HOUSE_RICHARD = 26, + /* 0x1B */ SKYBOX_HOUSE_IMPA, + /* 0x1C */ SKYBOX_TENT, + /* 0x1D */ SKYBOX_UNSET_1D, + /* 0x20 */ SKYBOX_HOUSE_MIDO = 32, + /* 0x21 */ SKYBOX_HOUSE_SARIA, + /* 0x22 */ SKYBOX_HOUSE_ALLEY, + /* 0x27 */ SKYBOX_UNSET_27 = 39 +} SkyboxId; + +typedef struct { + /* 0x000 */ char unk_00[0x128]; + /* 0x128 */ void* staticSegments[2]; + /* 0x130 */ u16 (*palettes)[256]; + /* 0x134 */ Gfx (*dListBuf)[150]; + /* 0x138 */ Gfx* unk_138; + /* 0x13C */ Vtx* roomVtx; + /* 0x140 */ s16 unk_140; + /* 0x144 */ Vec3f rot; + /* 0x150 */ char unk_150[0x10]; +} SkyboxContext; // size = 0x160 + +typedef enum { + /* 0 */ OCARINA_SONG_MINUET, + /* 1 */ OCARINA_SONG_BOLERO, + /* 2 */ OCARINA_SONG_SERENADE, + /* 3 */ OCARINA_SONG_REQUIEM, + /* 4 */ OCARINA_SONG_NOCTURNE, + /* 5 */ OCARINA_SONG_PRELUDE, + /* 6 */ OCARINA_SONG_SARIAS, + /* 7 */ OCARINA_SONG_EPONAS, + /* 8 */ OCARINA_SONG_LULLABY, + /* 9 */ OCARINA_SONG_SUNS, + /* 10 */ OCARINA_SONG_TIME, + /* 11 */ OCARINA_SONG_STORMS, + /* 12 */ OCARINA_SONG_SCARECROW, + /* 13 */ OCARINA_SONG_MEMORY_GAME, + /* 14 */ OCARINA_SONG_MAX, + /* 14 */ OCARINA_SONG_SCARECROW_LONG = OCARINA_SONG_MAX // anything larger than 13 is considered the long scarecrow's song +} OcarinaSongId; + +typedef enum { + /* 0x00 */ OCARINA_ACTION_UNK_0, // acts like free play but never set + /* 0x01 */ OCARINA_ACTION_FREE_PLAY, + /* 0x02 */ OCARINA_ACTION_TEACH_MINUET, // Song demonstrations by teachers + /* 0x03 */ OCARINA_ACTION_TEACH_BOLERO, + /* 0x04 */ OCARINA_ACTION_TEACH_SERENADE, + /* 0x05 */ OCARINA_ACTION_TEACH_REQUIEM, + /* 0x06 */ OCARINA_ACTION_TEACH_NOCTURNE, + /* 0x07 */ OCARINA_ACTION_TEACH_PRELUDE, + /* 0x08 */ OCARINA_ACTION_TEACH_SARIA, + /* 0x09 */ OCARINA_ACTION_TEACH_EPONA, + /* 0x0A */ OCARINA_ACTION_TEACH_LULLABY, + /* 0x0B */ OCARINA_ACTION_TEACH_SUNS, + /* 0x0C */ OCARINA_ACTION_TEACH_TIME, + /* 0x0D */ OCARINA_ACTION_TEACH_STORMS, + /* 0x0E */ OCARINA_ACTION_UNK_E, + /* 0x0F */ OCARINA_ACTION_PLAYBACK_MINUET, // Playing back a particular song + /* 0x10 */ OCARINA_ACTION_PLAYBACK_BOLERO, + /* 0x11 */ OCARINA_ACTION_PLAYBACK_SERENADE, + /* 0x12 */ OCARINA_ACTION_PLAYBACK_REQUIEM, + /* 0x13 */ OCARINA_ACTION_PLAYBACK_NOCTURNE, + /* 0x14 */ OCARINA_ACTION_PLAYBACK_PRELUDE, + /* 0x15 */ OCARINA_ACTION_PLAYBACK_SARIA, + /* 0x16 */ OCARINA_ACTION_PLAYBACK_EPONA, + /* 0x17 */ OCARINA_ACTION_PLAYBACK_LULLABY, + /* 0x18 */ OCARINA_ACTION_PLAYBACK_SUNS, + /* 0x19 */ OCARINA_ACTION_PLAYBACK_TIME, + /* 0x1A */ OCARINA_ACTION_PLAYBACK_STORMS, + /* 0x1B */ OCARINA_ACTION_UNK_1B, + /* 0x1C */ OCARINA_ACTION_CHECK_MINUET, // Playing songs for check spots + /* 0x1D */ OCARINA_ACTION_CHECK_BOLERO, + /* 0x1E */ OCARINA_ACTION_CHECK_SERENADE, + /* 0x1F */ OCARINA_ACTION_CHECK_REQUIEM, + /* 0020 */ OCARINA_ACTION_CHECK_NOCTURNE, + /* 0x21 */ OCARINA_ACTION_CHECK_PRELUDE, + /* 0x22 */ OCARINA_ACTION_CHECK_SARIA, + /* 0x23 */ OCARINA_ACTION_CHECK_EPONA, + /* 0x24 */ OCARINA_ACTION_CHECK_LULLABY, + /* 0x25 */ OCARINA_ACTION_CHECK_SUNS, + /* 0x26 */ OCARINA_ACTION_CHECK_TIME, + /* 0x27 */ OCARINA_ACTION_CHECK_STORMS, + /* 0x28 */ OCARINA_ACTION_CHECK_SCARECROW, // Playing back the song as adult that was set as child + /* 0x29 */ OCARINA_ACTION_FREE_PLAY_DONE, + /* 0x2A */ OCARINA_ACTION_SCARECROW_LONG_RECORDING, + /* 0x2B */ OCARINA_ACTION_SCARECROW_LONG_PLAYBACK, + /* 0x2C */ OCARINA_ACTION_SCARECROW_RECORDING, + /* 0x2D */ OCARINA_ACTION_SCARECROW_PLAYBACK, + /* 0x2E */ OCARINA_ACTION_MEMORY_GAME, + /* 0x2F */ OCARINA_ACTION_FROGS, + /* 0x30 */ OCARINA_ACTION_CHECK_NOWARP, // Check for any of sarias - storms + /* 0x31 */ OCARINA_ACTION_CHECK_NOWARP_DONE +} OcarinaSongActionIDs; + +typedef enum { + /* 0x00 */ OCARINA_MODE_00, + /* 0x01 */ OCARINA_MODE_01, + /* 0x02 */ OCARINA_MODE_02, + /* 0x03 */ OCARINA_MODE_03, + /* 0x04 */ OCARINA_MODE_04, + /* 0x05 */ OCARINA_MODE_05, + /* 0x06 */ OCARINA_MODE_06, + /* 0x07 */ OCARINA_MODE_07, + /* 0x08 */ OCARINA_MODE_08, + /* 0x09 */ OCARINA_MODE_09, + /* 0x0A */ OCARINA_MODE_0A, + /* 0x0B */ OCARINA_MODE_0B, + /* 0x0C */ OCARINA_MODE_0C, + /* 0x0D */ OCARINA_MODE_0D, + /* 0x0E */ OCARINA_MODE_0E, + /* 0x0F */ OCARINA_MODE_0F +} OcarinaMode; + +typedef enum { + TEXTBOX_ICON_TRIANGLE, + TEXTBOX_ICON_SQUARE, + TEXTBOX_ICON_ARROW +} TextBoxIcon; + +typedef enum { + LANGUAGE_ENG, + LANGUAGE_GER, + LANGUAGE_FRA, + LANGUAGE_MAX +} Language; + +// TODO get these properties from the textures themselves +#define FONT_CHAR_TEX_WIDTH 16 +#define FONT_CHAR_TEX_HEIGHT 16 +#define FONT_CHAR_TEX_SIZE ((16 * 16) / 2) // 16x16 I4 texture + +// TODO get these properties from the textures themselves +#define MESSAGE_STATIC_TEX_SIZE 0x1000 + +typedef enum { + /* 0x00 */ MSGMODE_NONE, + /* 0x01 */ MSGMODE_TEXT_START, + /* 0x02 */ MSGMODE_TEXT_BOX_GROWING, + /* 0x03 */ MSGMODE_TEXT_STARTING, + /* 0x04 */ MSGMODE_TEXT_NEXT_MSG, + /* 0x05 */ MSGMODE_TEXT_CONTINUING, + /* 0x06 */ MSGMODE_TEXT_DISPLAYING, + /* 0x07 */ MSGMODE_TEXT_AWAIT_INPUT, + /* 0x08 */ MSGMODE_TEXT_DELAYED_BREAK, + /* 0x09 */ MSGMODE_OCARINA_STARTING, + /* 0x0A */ MSGMODE_SONG_DEMONSTRATION_STARTING, + /* 0x0B */ MSGMODE_SONG_PLAYBACK_STARTING, + /* 0x0C */ MSGMODE_OCARINA_PLAYING, + /* 0x0D */ MSGMODE_OCARINA_CORRECT_PLAYBACK, + /* 0x0E */ MSGMODE_OCARINA_FAIL, // Failed to play a valid song after entering 8 notes + /* 0x0F */ MSGMODE_OCARINA_FAIL_NO_TEXT, // Never set, only compared against + /* 0x10 */ MSGMODE_OCARINA_NOTES_DROP, + /* 0x11 */ MSGMODE_SONG_PLAYED, // Played a full named song correctly + /* 0x12 */ MSGMODE_SETUP_DISPLAY_SONG_PLAYED, + /* 0x13 */ MSGMODE_DISPLAY_SONG_PLAYED, + /* 0x14 */ MSGMODE_DISPLAY_SONG_PLAYED_TEXT_BEGIN, + /* 0x15 */ MSGMODE_DISPLAY_SONG_PLAYED_TEXT, + /* 0x16 */ MSGMODE_SONG_PLAYED_ACT_BEGIN, + /* 0x17 */ MSGMODE_SONG_PLAYED_ACT, // Act on a played song + /* 0x18 */ MSGMODE_SONG_DEMONSTRATION_SELECT_INSTRUMENT, + /* 0x19 */ MSGMODE_SONG_DEMONSTRATION, + /* 0x1A */ MSGMODE_SONG_DEMONSTRATION_DONE, + /* 0x1B */ MSGMODE_SONG_PLAYBACK, + /* 0x1C */ MSGMODE_SONG_PLAYBACK_SUCCESS, + /* 0x1D */ MSGMODE_SONG_PLAYBACK_FAIL, + /* 0x1E */ MSGMODE_SONG_PLAYBACK_NOTES_DROP, + /* 0x1F */ MSGMODE_OCARINA_AWAIT_INPUT, + /* 0x20 */ MSGMODE_UNK_20, // Never set and does nothing + /* 0x21 */ MSGMODE_SCARECROW_LONG_RECORDING_START, + /* 0x22 */ MSGMODE_SCARECROW_LONG_RECORDING_ONGOING, + /* 0x23 */ MSGMODE_SCARECROW_LONG_PLAYBACK, + /* 0x24 */ MSGMODE_SCARECROW_RECORDING_START, + /* 0x25 */ MSGMODE_SCARECROW_RECORDING_ONGOING, + /* 0x26 */ MSGMODE_SCARECROW_RECORDING_FAILED, + /* 0x27 */ MSGMODE_SCARECROW_RECORDING_DONE, + /* 0x28 */ MSGMODE_SCARECROW_PLAYBACK, + /* 0x29 */ MSGMODE_MEMORY_GAME_START, + /* 0x2A */ MSGMODE_MEMORY_GAME_LEFT_SKULLKID_PLAYING, + /* 0x2B */ MSGMODE_MEMORY_GAME_LEFT_SKULLKID_WAIT, + /* 0x2C */ MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_PLAYING, + /* 0x2D */ MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_WAIT, + /* 0x2E */ MSGMODE_MEMORY_GAME_PLAYER_PLAYING, + /* 0x2F */ MSGMODE_MEMORY_GAME_ROUND_SUCCESS, + /* 0x30 */ MSGMODE_MEMORY_GAME_START_NEXT_ROUND, + /* 0x31 */ MSGMODE_FROGS_START, + /* 0x32 */ MSGMODE_FROGS_PLAYING, + /* 0x33 */ MSGMODE_FROGS_WAITING, + /* 0x34 */ MSGMODE_TEXT_AWAIT_NEXT, + /* 0x35 */ MSGMODE_TEXT_DONE, + /* 0x36 */ MSGMODE_TEXT_CLOSING, + /* 0x37 */ MSGMODE_PAUSED // Causes the message system to do nothing until external code sets a new message mode or calls a public function +} MessageMode; + +typedef enum { + /* 0 */ TEXT_STATE_NONE, + /* 1 */ TEXT_STATE_DONE_HAS_NEXT, + /* 2 */ TEXT_STATE_CLOSING, + /* 3 */ TEXT_STATE_DONE_FADING, + /* 4 */ TEXT_STATE_CHOICE, + /* 5 */ TEXT_STATE_EVENT, + /* 6 */ TEXT_STATE_DONE, + /* 7 */ TEXT_STATE_SONG_DEMO_DONE, + /* 8 */ TEXT_STATE_8, + /* 9 */ TEXT_STATE_9, + /* 10 */ TEXT_STATE_AWAITING_NEXT +} TextState; + +typedef struct { + /* 0x0000 */ uintptr_t msgOffset; + /* 0x0004 */ u32 msgLength; + /* 0x0008 */ u8 charTexBuf[FONT_CHAR_TEX_SIZE * 120]; + /* 0x3C08 */ u8 iconBuf[FONT_CHAR_TEX_SIZE]; + /* 0x3C88 */ u8 fontBuf[FONT_CHAR_TEX_SIZE * 320]; + union { + /* 0xDC88 */ char msgBuf[1280]; + /* 0xDC88 */ u16 msgBufWide[640]; + }; +} Font; // size = 0xE188 + +#define TEXTBOX_ENDTYPE_DEFAULT 0x00 +#define TEXTBOX_ENDTYPE_2_CHOICE 0x10 +#define TEXTBOX_ENDTYPE_3_CHOICE 0x20 +#define TEXTBOX_ENDTYPE_HAS_NEXT 0x30 +#define TEXTBOX_ENDTYPE_PERSISTENT 0x40 +#define TEXTBOX_ENDTYPE_EVENT 0x50 +#define TEXTBOX_ENDTYPE_FADING 0x60 + +typedef struct { + /* 0x0000 */ View view; + /* 0x0128 */ Font font; + /* 0xE2B0 */ void* textboxSegment; // original name: "fukidashiSegment" + /* 0xE2B4 */ char unk_E2B4[0x4]; + /* 0xE2B8 */ OcarinaStaff* ocarinaStaff; // original name : "info" + /* 0xE2BC */ char unk_E2BC[0x3C]; + /* 0xE2F8 */ u16 textId; + /* 0xE2FA */ u16 choiceTextId; + /* 0xE2FC */ u8 textBoxProperties; // original name : "msg_disp_type" + /* 0xE2FD */ u8 textBoxType; // "Text Box Type" + /* 0xE2FE */ u8 textBoxPos; // text box position + /* 0xE300 */ s32 msgLength; // original name : "msg_data" + /* 0xE304 */ u8 msgMode; // original name: "msg_mode" + /* 0xE305 */ char unk_E305[0x1]; + /* 0xE306 */ u8 msgBufDecoded[200]; // decoded message buffer, may be smaller than this + /* 0xE3CE */ u16 msgBufPos; // original name : "rdp" + /* 0xE3D0 */ u16 unk_E3D0; // unused, only ever set to 0 + /* 0xE3D2 */ u16 textDrawPos; // draw all decoded characters up to this buffer position + /* 0xE3D4 */ u16 decodedTextLen; // decoded message buffer length + /* 0xE3D6 */ u16 textUnskippable; + /* 0xE3D8 */ s16 textPosX; + /* 0xE3DA */ s16 textPosY; + /* 0xE3DC */ s16 textColorR; + /* 0xE3DE */ s16 textColorG; + /* 0xE3E0 */ s16 textColorB; + /* 0xE3E2 */ s16 textColorAlpha; + /* 0xE3E4 */ u8 textboxEndType; // original name : "select" + /* 0xE3E5 */ u8 choiceIndex; + /* 0xE3E6 */ u8 choiceNum; // textboxes that are not choice textboxes have a choiceNum of 1 + /* 0xE3E7 */ u8 stateTimer; + /* 0xE3E8 */ u16 textDelayTimer; + /* 0xE3EA */ u16 textDelay; + /* 0xE3EA */ u16 lastPlayedSong; // original references : "Ocarina_Flog" , "Ocarina_Free" + /* 0xE3EE */ u16 ocarinaMode; // original name : "ocarina_mode" + /* 0xE3F0 */ u16 ocarinaAction; // original name : "ocarina_no" + /* 0xE3F2 */ u16 unk_E3F2; // this is like "lastPlayedSong" but set less often, original name : "chk_ocarina_no" + /* 0xE3F4 */ u16 unk_E3F4; // unused, only set to 0 in z_actor + /* 0xE3F6 */ u16 textboxBackgroundIdx; + /* 0xE3F8 */ u8 textboxBackgroundForeColorIdx; + /* 0xE3F8 */ u8 textboxBackgroundBackColorIdx; + /* 0xE3F8 */ u8 textboxBackgroundYOffsetIdx; + /* 0xE3F8 */ u8 textboxBackgroundUnkArg; // unused, set by the textbox background control character arguments + /* 0xE3FC */ char unk_E3FC[0x2]; + /* 0xE3FE */ s16 textboxColorRed; + /* 0xE400 */ s16 textboxColorGreen; + /* 0xE402 */ s16 textboxColorBlue; + /* 0xE404 */ s16 textboxColorAlphaTarget; + /* 0xE406 */ s16 textboxColorAlphaCurrent; + /* 0xE408 */ Actor* talkActor; + /* 0xE40C */ s16 disableWarpSongs; // warp song flag set by scene commands + /* 0xE40E */ s16 unk_E40E; // ocarina related + /* 0xE410 */ u8 lastOcaNoteIdx; +} MessageContext; // size = 0xE418 + +typedef enum { + /* 0x00 */ DO_ACTION_ATTACK, + /* 0x01 */ DO_ACTION_CHECK, + /* 0x02 */ DO_ACTION_ENTER, + /* 0x03 */ DO_ACTION_RETURN, + /* 0x04 */ DO_ACTION_OPEN, + /* 0x05 */ DO_ACTION_JUMP, + /* 0x06 */ DO_ACTION_DECIDE, + /* 0x07 */ DO_ACTION_DIVE, + /* 0x08 */ DO_ACTION_FASTER, + /* 0x09 */ DO_ACTION_THROW, + /* 0x0A */ DO_ACTION_NONE, // in do_action_static, the texture at this position is NAVI, however this value is in practice the "No Action" value + /* 0x0B */ DO_ACTION_CLIMB, + /* 0x0C */ DO_ACTION_DROP, + /* 0x0D */ DO_ACTION_DOWN, + /* 0x0E */ DO_ACTION_SAVE, + /* 0x0F */ DO_ACTION_SPEAK, + /* 0x10 */ DO_ACTION_NEXT, + /* 0x11 */ DO_ACTION_GRAB, + /* 0x12 */ DO_ACTION_STOP, + /* 0x13 */ DO_ACTION_PUTAWAY, + /* 0x14 */ DO_ACTION_REEL, + /* 0x15 */ DO_ACTION_1, + /* 0x16 */ DO_ACTION_2, + /* 0x17 */ DO_ACTION_3, + /* 0x18 */ DO_ACTION_4, + /* 0x19 */ DO_ACTION_5, + /* 0x1A */ DO_ACTION_6, + /* 0x1B */ DO_ACTION_7, + /* 0x1C */ DO_ACTION_8, + /* 0x1D */ DO_ACTION_MAX +} DoAction; + +typedef struct { + /* 0x0000 */ View view; + /* 0x0128 */ Vtx* actionVtx; + /* 0x012C */ Vtx* beatingHeartVtx; + /* 0x0130 */ u8* parameterSegment; + /* 0x0134 */ u8* doActionSegment; + /* 0x0138 */ u8* iconItemSegment; + /* 0x013C */ u8* mapSegment; + /* 0x0140 */ u8 mapPalette[32]; + /* 0x0160 */ DmaRequest dmaRequest_160; + /* 0x0180 */ DmaRequest dmaRequest_180; + /* 0x01A0 */ char unk_1A0[0x20]; + /* 0x01C0 */ OSMesgQueue loadQueue; + /* 0x01D8 */ OSMesg loadMsg; + /* 0x01DC */ Viewport viewport; + /* 0x01EC */ s16 unk_1EC; + /* 0x01EE */ u16 unk_1EE; + /* 0x01F0 */ u16 unk_1F0; + /* 0x01F4 */ f32 unk_1F4; + /* 0x01F8 */ s16 naviCalling; + /* 0x01FA */ s16 unk_1FA; + /* 0x01FC */ s16 unk_1FC; + /* 0x01FE */ s16 unk_1FE; + /* 0x0200 */ s16 unk_200; + /* 0x0202 */ s16 beatingHeartPrim[3]; + /* 0x0208 */ s16 beatingHeartEnv[3]; + /* 0x020E */ s16 heartsPrimR[2]; + /* 0x0212 */ s16 heartsPrimG[2]; + /* 0x0216 */ s16 heartsPrimB[2]; + /* 0x021A */ s16 heartsEnvR[2]; + /* 0x021E */ s16 heartsEnvG[2]; + /* 0x0222 */ s16 heartsEnvB[2]; + /* 0x0226 */ s16 unk_226; + /* 0x0228 */ s16 unk_228; + /* 0x022A */ s16 unk_22A; + /* 0x022C */ s16 unk_22C; + /* 0x022E */ s16 unk_22E; + /* 0x0230 */ s16 unk_230; + /* 0x0232 */ s16 counterDigits[4]; // used for key and rupee counters + /* 0x023A */ u8 numHorseBoosts; + /* 0x023C */ u16 unk_23C; + /* 0x023E */ u16 hbaAmmo; // ammo while playing the horseback archery minigame + /* 0x0240 */ u16 unk_240; + /* 0x0242 */ u16 unk_242; + /* 0x0224 */ u16 unk_244; // screen fill alpha? + /* 0x0246 */ u16 aAlpha; // also carrots alpha + /* 0x0248 */ u16 bAlpha; // also HBA score alpha + /* 0x024A */ u16 cLeftAlpha; + /* 0x024C */ u16 cDownAlpha; + /* 0x024E */ u16 cRightAlpha; + /* 0x0250 */ u16 healthAlpha; // also max C-Up alpha + /* 0x0252 */ u16 magicAlpha; // also Rupee and Key counters alpha + /* 0x0254 */ u16 minimapAlpha; + /* 0x0256 */ s16 startAlpha; + /* 0x0258 */ s16 unk_258; + /* 0x025A */ s16 unk_25A; + /* 0x025C */ s16 mapRoomNum; + /* 0x025E */ s16 mapPaletteIndex; // "map_palete_no" + /* 0x0260 */ u8 unk_260; + /* 0x0261 */ u8 unk_261; + struct { + /* 0x0262 */ u8 hGauge; // "h_gage"; unknown? + /* 0x0263 */ u8 bButton; // "b_button" + /* 0x0264 */ u8 aButton; // "a_button" + /* 0x0265 */ u8 bottles; // "c_bottle" + /* 0x0266 */ u8 tradeItems; // "c_warasibe" + /* 0x0267 */ u8 hookshot; // "c_hook" + /* 0x0268 */ u8 ocarina; // "c_ocarina" + /* 0x0269 */ u8 warpSongs; // "c_warp" + /* 0x026A */ u8 sunsSong; // "m_sunmoon" + /* 0x026B */ u8 farores; // "m_wind" + /* 0x026C */ u8 dinsNayrus; // "m_magic"; din's fire and nayru's love + /* 0x026D */ u8 all; // "another"; enables all item restrictions + } restrictions; +} InterfaceContext; // size = 0x270 + +typedef struct { + /* 0x00 */ void* loadedRamAddr; + /* 0x04 */ uintptr_t vromStart; + /* 0x08 */ uintptr_t vromEnd; + /* 0x0C */ void* vramStart; + /* 0x10 */ void* vramEnd; + /* 0x14 */ u32 offset; // loadedRamAddr - vramStart + /* 0x18 */ const char* name; +} KaleidoMgrOverlay; // size = 0x1C + +typedef enum { + /* 0x00 */ KALEIDO_OVL_KALEIDO_SCOPE, + /* 0x01 */ KALEIDO_OVL_PLAYER_ACTOR, + /* 0x02 */ KALEIDO_OVL_MAX +} KaleidoOverlayType; + +#define PAUSE_ITEM_NONE 999 + +#define PAUSE_CURSOR_PAGE_LEFT 10 +#define PAUSE_CURSOR_PAGE_RIGHT 11 + +typedef enum { + /* 0x00 */ PAUSE_ITEM, + /* 0x01 */ PAUSE_MAP, + /* 0x02 */ PAUSE_QUEST, + /* 0x03 */ PAUSE_EQUIP, + /* 0x04 */ PAUSE_WORLD_MAP +} PauseMenuPage; + +typedef struct { + /* 0x0000 */ View view; + /* 0x0128 */ u8* iconItemSegment; + /* 0x012C */ u8* iconItem24Segment; + /* 0x0130 */ u8* iconItemAltSegment; + /* 0x0134 */ u8* iconItemLangSegment; + /* 0x0138 */ u8* nameSegment; + /* 0x013C */ u8* playerSegment; + /* 0x0140 */ char unk_140[0x04]; + /* 0x0144 */ Vtx* itemPageVtx; + /* 0x0148 */ Vtx* equipPageVtx; + /* 0x014C */ Vtx* mapPageVtx; + /* 0x0150 */ Vtx* questPageVtx; + /* 0x0154 */ Vtx* infoPanelVtx; + /* 0x0158 */ Vtx* itemVtx; + /* 0x015C */ Vtx* equipVtx; + /* 0x0160 */ char unk_160[0x04]; + /* 0x0164 */ Vtx* questVtx; + /* 0x0168 */ Vtx* cursorVtx; + /* 0x016C */ Vtx* saveVtx; + /* 0x0170 */ char unk_170[0x24]; + /* 0x0194 */ OcarinaStaff* ocarinaStaff; + /* 0x0198 */ char unk_198[0x20]; + /* 0x01B8 */ OSMesgQueue loadQueue; + /* 0x01D0 */ OSMesg loadMsg; + /* 0x01D4 */ u16 state; + /* 0x01D6 */ u16 debugState; + /* 0x01D8 */ Vec3f eye; + /* 0x01E4 */ u16 unk_1E4; + /* 0x01E6 */ u16 mode; + /* 0x01E8 */ u16 pageIndex; // "kscp_pos" + /* 0x01EA */ u16 unk_1EA; + /* 0x01EC */ u16 unk_1EC; + /* 0x01F0 */ f32 unk_1F0; + /* 0x01F4 */ f32 unk_1F4; + /* 0x01F8 */ f32 unk_1F8; + /* 0x01FC */ f32 unk_1FC; + /* 0x0200 */ f32 unk_200; + /* 0x0204 */ f32 unk_204; // "angle_s" + /* 0x0208 */ u16 alpha; + /* 0x020A */ s16 offsetY; + /* 0x020C */ char unk_20C[0x08]; + /* 0x0214 */ s16 stickRelX; + /* 0x0216 */ s16 stickRelY; + /* 0x0218 */ s16 cursorPoint[5]; // "cursor_point" + /* 0x0222 */ s16 cursorX[5]; // "cur_xpt" + /* 0x022C */ s16 cursorY[5]; // "cur_ypt" + /* 0x0236 */ s16 dungeonMapSlot; + /* 0x0238 */ s16 cursorSpecialPos; // "key_angle" + /* 0x023A */ s16 pageSwitchTimer; + /* 0x023C */ u16 namedItem; // "zoom_name" + /* 0x023E */ u16 cursorItem[4]; // "select_name" + /* 0x0246 */ u16 cursorSlot[4]; + /* 0x024E */ u16 equipTargetItem; // "sl_item_no" + /* 0x0250 */ u16 equipTargetSlot; // "sl_number" + /* 0x0252 */ u16 equipTargetCBtn; + /* 0x0254 */ s16 equipAnimX; + /* 0x0256 */ s16 equipAnimY; + /* 0x0258 */ s16 equipAnimAlpha; + /* 0x025A */ s16 infoPanelOffsetY; + /* 0x025C */ u16 nameDisplayTimer; + /* 0x025E */ u16 nameColorSet; // 0 = white; 1 = grey + /* 0x0260 */ s16 cursorColorSet; // 0 = white; 4 = yellow; 8 = green + /* 0x0262 */ s16 promptChoice; // save/continue choice: 0 = yes; 4 = no + /* 0x0264 */ s16 ocarinaSongIdx; + /* 0x0266 */ u8 worldMapPoints[20]; // 0 = hidden; 1 = displayed; 2 = highlighted + /* 0x027A */ u8 tradeQuestLocation; + /* 0x027C */ SkelAnime playerSkelAnime; +} PauseContext; // size = 0x2C0 + +typedef enum { + /* 00 */ GAMEOVER_INACTIVE, + /* 01 */ GAMEOVER_DEATH_START, + /* 02 */ GAMEOVER_DEATH_WAIT_GROUND, // wait for link to fall and hit the ground + /* 03 */ GAMEOVER_DEATH_DELAY_MENU, // wait for 1 second before showing the game over menu + /* 04 */ GAMEOVER_DEATH_MENU, // do nothing while kaliedoscope handles the game over menu + /* 20 */ GAMEOVER_REVIVE_START = 20, + /* 21 */ GAMEOVER_REVIVE_RUMBLE, + /* 22 */ GAMEOVER_REVIVE_WAIT_GROUND, // wait for link to fall and hit the ground + /* 23 */ GAMEOVER_REVIVE_WAIT_FAIRY, // wait for the fairy to rise all the way up out of links body + /* 24 */ GAMEOVER_REVIVE_FADE_OUT // fade out the game over lights as link is revived and gets back up +} GameOverState; + +typedef struct { + /* 0x00 */ u16 state; +} GameOverContext; // size = 0x2 + +typedef struct { + /* 0x00 */ s16 id; + /* 0x04 */ void* segment; + /* 0x08 */ DmaRequest dmaRequest; + /* 0x28 */ OSMesgQueue loadQueue; + /* 0x40 */ OSMesg loadMsg; +} ObjectStatus; // size = 0x44 + +typedef struct { + /* 0x0000 */ void* spaceStart; + /* 0x0004 */ void* spaceEnd; // original name: "endSegment" + /* 0x0008 */ u8 num; // number of objects in bank + /* 0x0009 */ u8 unk_09; + /* 0x000A */ u8 mainKeepIndex; // "gameplay_keep" index in bank + /* 0x000B */ u8 subKeepIndex; // "gameplay_field_keep" or "gameplay_dangeon_keep" index in bank + /* 0x000C */ ObjectStatus status[OBJECT_EXCHANGE_BANK_MAX]; +} ObjectContext; // size = 0x518 + +typedef struct { + /* 0x00 */ Gfx* opa; + /* 0x04 */ Gfx* xlu; + +#ifdef __cplusplus + Ship::DisplayList* opaDL; + Ship::DisplayList* xluDL; +#else +void* opaDL; +void* xluDL; +#endif +} PolygonDlist; // size = 0x8 + + +#ifdef __cplusplus +#define Polygon _Polygon +#endif + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 num; // number of dlist entries + /* 0x04 */ void* start; + /* 0x08 */ void* end; +} Polygon; // size = 0xC + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 num; // number of dlist entries + /* 0x04 */ void* start; + /* 0x08 */ void* end; +} PolygonType0; // size = 0xC + +typedef struct { + /* 0x00 */ u16 unk_00; + /* 0x02 */ u8 id; + /* 0x04 */ void* source; + /* 0x08 */ u32 unk_0C; + /* 0x0C */ u32 tlut; + /* 0x10 */ u16 width; + /* 0x12 */ u16 height; + /* 0x14 */ u8 fmt; + /* 0x15 */ u8 siz; + /* 0x16 */ u16 mode0; + /* 0x18 */ u16 tlutCount; +} BgImage; // size = 0x1C + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 format; // 1 = single, 2 = multi + /* 0x04 */ Gfx* dlist; + union { + struct { + /* 0x08 */ void* source; + /* 0x0C */ u32 unk_0C; + /* 0x10 */ void* tlut; + /* 0x14 */ u16 width; + /* 0x16 */ u16 height; + /* 0x18 */ u8 fmt; + /* 0x19 */ u8 siz; + /* 0x1A */ u16 mode0; + /* 0x1C */ u16 tlutCount; + } single; + struct { + /* 0x08 */ u8 count; + /* 0x0C */ BgImage* list; + } multi; + }; +} PolygonType1; + +typedef struct { + /* 0x00 */ Vec3s pos; + /* 0x06 */ s16 unk_06; + /* 0x08 */ Gfx* opa; + /* 0x0C */ Gfx* xlu; + +#ifdef __cplusplus + Ship::DisplayList* opaDL; + Ship::DisplayList* xluDL; +#else + void* opaDL; + void* xluDL; +#endif +} PolygonDlist2; // size = 0x8 + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 num; // number of dlist entries + /* 0x04 */ void* start; + /* 0x08 */ void* end; +} PolygonType2; // size = 0xC + +typedef union { + Polygon polygon; + PolygonType0 polygon0; + PolygonType1 polygon1; + PolygonType2 polygon2; +} Mesh; // "Ground Shape" + +typedef struct { + /* 0x00 */ s8 num; + /* 0x01 */ u8 unk_01; + /* 0x02 */ u8 unk_02; + /* 0x03 */ u8 unk_03; + /* 0x04 */ s8 echo; + /* 0x05 */ u8 showInvisActors; + /* 0x08 */ Mesh* mesh; // original name: "ground_shape" + /* 0x0C */ void* segment; + /* 0x10 */ char unk_10[0x4]; +} Room; // size = 0x14 + +typedef struct { + /* 0x00 */ Room curRoom; + /* 0x14 */ Room prevRoom; + /* 0x28 */ void* bufPtrs[2]; + /* 0x30 */ u8 unk_30; + /* 0x31 */ s8 status; + /* 0x34 */ void* unk_34; + /* 0x38 */ DmaRequest dmaRequest; + /* 0x58 */ OSMesgQueue loadQueue; + /* 0x70 */ OSMesg loadMsg; + /* 0x74 */ s16 unk_74[2]; // context-specific data used by the current scene draw config + +#ifdef __cplusplus + Ship::Scene* roomToLoad; +#else + void* roomToLoad; +#endif +} RoomContext; // size = 0x78 + +typedef struct { + /* 0x000 */ s16 colATCount; + /* 0x002 */ u16 sacFlags; + /* 0x004 */ Collider* colAT[COLLISION_CHECK_AT_MAX]; + /* 0x0CC */ s32 colACCount; + /* 0x0D0 */ Collider* colAC[COLLISION_CHECK_AC_MAX]; + /* 0x1C0 */ s32 colOCCount; + /* 0x1C4 */ Collider* colOC[COLLISION_CHECK_OC_MAX]; + /* 0x28C */ s32 colLineCount; + /* 0x290 */ OcLine* colLine[COLLISION_CHECK_OC_LINE_MAX]; +} CollisionCheckContext; // size = 0x29C + +typedef struct ListAlloc { + /* 0x00 */ struct ListAlloc* prev; + /* 0x04 */ struct ListAlloc* next; +} ListAlloc; // size = 0x8 + +typedef struct { + /* 0x00 */ s32 width; + /* 0x04 */ s32 height; + /* 0x08 */ s32 widthSave; + /* 0x0C */ s32 heightSave; + /* 0x10 */ u16* fbuf; + /* 0x14 */ u16* fbufSave; + /* 0x18 */ u8* cvgSave; + /* 0x1C */ u16* zbuf; + /* 0x20 */ u16* zbufSave; + /* 0x24 */ s32 ulxSave; + /* 0x28 */ s32 ulySave; + /* 0x2C */ s32 lrxSave; + /* 0x30 */ s32 lrySave; + /* 0x34 */ s32 ulx; + /* 0x38 */ s32 uly; + /* 0x3C */ s32 lrx; + /* 0x40 */ s32 lry; + /* 0x44 */ ListAlloc alloc; + /* 0x4C */ u32 unk_4C; +} PreRender; // size = 0x50 + +typedef struct { + union { + TransitionFade fade; + TransitionCircle circle; + TransitionTriforce triforce; + TransitionWipe wipe; + char data[0x228]; + }; + /* 0x228 */ s32 transitionType; + /* 0x22C */ void* (*init)(void* transition); + /* 0x230 */ void (*destroy)(void* transition); + /* 0x234 */ void (*update)(void* transition, s32 updateRate); + /* 0x238 */ void (*draw)(void* transition, Gfx** gfxP); + /* 0x23C */ void (*start)(void* transition); + /* 0x240 */ void (*setType)(void* transition, s32 type); + /* 0x244 */ void (*setColor)(void* transition, u32 color); + /* 0x248 */ void (*setEnvColor)(void* transition, u32 color); + /* 0x24C */ s32 (*isDone)(void* transition); +} TransitionContext; // size = 0x250 + +typedef struct { + /* 0x00 */ s16 id; + /* 0x02 */ Vec3s pos; + /* 0x08 */ Vec3s rot; + /* 0x0E */ s16 params; +} ActorEntry; // size = 0x10 + +typedef struct { + struct { + s8 room; // Room to switch to + s8 effects; // How the camera reacts during the transition + } /* 0x00 */ sides[2]; // 0 = front, 1 = back + /* 0x04 */ s16 id; + /* 0x06 */ Vec3s pos; + /* 0x0C */ s16 rotY; + /* 0x0E */ s16 params; +} TransitionActorEntry; // size = 0x10 + +typedef struct { + /* 0x00 */ u8 spawn; + /* 0x01 */ u8 room; +} EntranceEntry; + +typedef struct { + /* 0x00 */ u8* readBuff; +} SramContext; // size = 0x4 + +#define SRAM_SIZE 0x8000 +#define SRAM_HEADER_SIZE 0x10 + +typedef enum { + /* 0x00 */ SRAM_HEADER_SOUND, + /* 0x01 */ SRAM_HEADER_ZTARGET, + /* 0x02 */ SRAM_HEADER_LANGUAGE, + /* 0x03 */ SRAM_HEADER_MAGIC // must be the value of `sZeldaMagic` for save to be considered valid +} SramHeaderField; + +typedef struct GameAllocEntry { + /* 0x00 */ struct GameAllocEntry* next; + /* 0x04 */ struct GameAllocEntry* prev; + /* 0x08 */ size_t size; + /* 0x0C */ u32 unk_0C; +} GameAllocEntry; // size = 0x10 + +typedef struct { + /* 0x00 */ GameAllocEntry base; + /* 0x10 */ GameAllocEntry* head; +} GameAlloc; // size = 0x14 + +struct GameState; + +typedef void (*GameStateFunc)(struct GameState* gameState); + +typedef struct GameState { + /* 0x00 */ GraphicsContext* gfxCtx; + /* 0x04 */ GameStateFunc main; + /* 0x08 */ GameStateFunc destroy; // "cleanup" + /* 0x0C */ GameStateFunc init; + /* 0x10 */ size_t size; + /* 0x14 */ Input input[4]; + /* 0x74 */ TwoHeadArena tha; + /* 0x84 */ GameAlloc alloc; + /* 0x98 */ u32 running; + /* 0x9C */ u32 frames; + /* 0xA0 */ u32 unk_A0; +} GameState; // size = 0xA4 + +typedef struct { + /* 0x0000 */ GameState state; + /* 0x00A4 */ u8* staticSegment; + /* 0x00A8 */ View view; + /* 0x01D0 */ SramContext sramCtx; + /* 0x01D4 */ u16 unk_1D4; // not used in mq dbg (some sort of timer that doesn't seem to affect anything) + /* 0x01D6 */ s16 coverAlpha; + /* 0x01D8 */ s16 addAlpha; // not used in mq dbg + /* 0x01DA */ u16 visibleDuration; // not used in mq dbg + /* 0x01DC */ s16 ult; + /* 0x01DE */ s16 uls; + /* 0x01E0 */ char unk_1E0[0x01]; + /* 0x01E1 */ u8 exit; + /* 0x01E2 */ char unk_1E2[0x06]; +} TitleContext; // size = 0x1E8 + +struct SelectContext; + +typedef struct { + /* 0x00 */ char* name; + /* 0x04 */ void (*loadFunc)(struct SelectContext*, s32); + /* 0x08 */ s32 entranceIndex; +} SceneSelectEntry; // size = 0xC + +typedef struct SelectContext { + /* 0x0000 */ GameState state; + /* 0x00A8 */ View view; + /* 0x01D0 */ s32 count; + /* 0x01D4 */ SceneSelectEntry* scenes; + /* 0x01D8 */ s32 currentScene; + /* 0x01DC */ s32 pageDownIndex; // Index of pageDownStops + /* 0x01E0 */ s32 pageDownStops[7]; + /* 0x01FC */ char unk_1FC[0x0C]; + /* 0x0208 */ s32 opt; + /* 0x020C */ s32 topDisplayedScene; // The scene which is currently at the top of the screen + /* 0x0210 */ char unk_210[0x0C]; + /* 0x021C */ s32 verticalInputAccumulator; + /* 0x0220 */ s32 verticalInput; + /* 0x0224 */ s32 timerUp; + /* 0x0228 */ s32 timerDown; + /* 0x022C */ s32 lockUp; + /* 0x0230 */ s32 lockDown; + /* 0x0234 */ s32 unk_234; // unused + /* 0x0238 */ u8* staticSegment; +} SelectContext; // size = 0x240 + +typedef struct { + /* 0x0000 */ GameState state; + /* 0x00A4 */ u8* staticSegment; + /* 0x00A8 */ View view; +} SampleContext; // size = 0x1D0 + +typedef struct { + /* 0x00 */ u8 byte0; + /* 0x01 */ u8 byte1; + /* 0x02 */ u8 byte2; + /* 0x03 */ u8 byte3; +} ElfMessage; // size = 0x4 + +typedef struct { + /* 0x00 */ u8 numActors; + /* 0x04 */ TransitionActorEntry* list; +} TransitionActorContext; + +// Global Context (dbg ram start: 80212020) +typedef struct GlobalContext { + /* 0x00000 */ GameState state; + /* 0x000A4 */ s16 sceneNum; + /* 0x000A6 */ u8 sceneConfig; + /* 0x000A7 */ char unk_A7[0x9]; + +#ifdef __cplusplus + Ship::Scene* sceneSegment; +#else + /* 0x000B0 */ void* sceneSegment; +#endif + + /* 0x000B8 */ View view; + /* 0x001E0 */ Camera mainCamera; + /* 0x0034C */ Camera subCameras[NUM_CAMS - SUBCAM_FIRST]; + /* 0x00790 */ Camera* cameraPtrs[NUM_CAMS]; + /* 0x007A0 */ s16 activeCamera; + /* 0x007A2 */ s16 nextCamera; + /* 0x007A4 */ SequenceContext sequenceCtx; + /* 0x007A8 */ LightContext lightCtx; + /* 0x007B8 */ FrameAdvanceContext frameAdvCtx; + /* 0x007C0 */ CollisionContext colCtx; + /* 0x01C24 */ ActorContext actorCtx; + /* 0x01D64 */ CutsceneContext csCtx; // "demo_play" + /* 0x01DB4 */ SoundSource soundSources[16]; + /* 0x01F74 */ SramContext sramCtx; + /* 0x01F78 */ SkyboxContext skyboxCtx; + /* 0x020D8 */ MessageContext msgCtx; // "message" + /* 0x104F0 */ InterfaceContext interfaceCtx; // "parameter" + /* 0x10760 */ PauseContext pauseCtx; + /* 0x10A20 */ GameOverContext gameOverCtx; + /* 0x10A24 */ EnvironmentContext envCtx; + /* 0x10B20 */ AnimationContext animationCtx; + /* 0x117A4 */ ObjectContext objectCtx; + /* 0x11CBC */ RoomContext roomCtx; + /* 0x11D34 */ TransitionActorContext transiActorCtx; + /* 0x11D3C */ void (*playerInit)(Player* player, struct GlobalContext* globalCtx, FlexSkeletonHeader* skelHeader); + /* 0x11D40 */ void (*playerUpdate)(Player* player, struct GlobalContext* globalCtx, Input* input); + /* 0x11D44 */ s32 (*isPlayerDroppingFish)(struct GlobalContext* globalCtx); + /* 0x11D48 */ s32 (*startPlayerFishing)(struct GlobalContext* globalCtx); + /* 0x11D4C */ s32 (*grabPlayer)(struct GlobalContext* globalCtx, Player* player); + /* 0x11D50 */ s32 (*startPlayerCutscene)(struct GlobalContext* globalCtx, Actor* actor, s32 mode); + /* 0x11D54 */ void (*func_11D54)(Player* player, struct GlobalContext* globalCtx); + /* 0x11D58 */ s32 (*damagePlayer)(struct GlobalContext* globalCtx, s32 damage); + /* 0x11D5C */ void (*talkWithPlayer)(struct GlobalContext* globalCtx, Actor* actor); + /* 0x11D60 */ MtxF viewProjectionMtxF; + /* 0x11DA0 */ MtxF billboardMtxF; + /* 0x11DE0 */ Mtx* billboardMtx; + /* 0x11DE4 */ u32 gameplayFrames; + /* 0x11DE8 */ u8 linkAgeOnLoad; + /* 0x11DE9 */ u8 unk_11DE9; + /* 0x11DEA */ u8 curSpawn; + /* 0x11DEB */ u8 numSetupActors; + /* 0x11DEC */ u8 numRooms; + /* 0x11DF0 */ RomFile* roomList; + /* 0x11DF4 */ ActorEntry* linkActorEntry; + /* 0x11DF8 */ ActorEntry* setupActorList; + /* 0x11DFC */ UNK_PTR unk_11DFC; + /* 0x11E00 */ EntranceEntry* setupEntranceList; + /* 0x11E04 */ s16* setupExitList; + /* 0x11E08 */ Path* setupPathList; + /* 0x11E0C */ ElfMessage* cUpElfMsgs; + /* 0x11E10 */ void* specialEffects; + /* 0x11E14 */ u8 skyboxId; + /* 0x11E15 */ s8 sceneLoadFlag; // "fade_direction" + /* 0x11E16 */ s16 unk_11E16; + /* 0x11E18 */ s16 unk_11E18; + /* 0x11E1A */ s16 nextEntranceIndex; + /* 0x11E1C */ char unk_11E1C[0x40]; + /* 0x11E5C */ s8 shootingGalleryStatus; + /* 0x11E5D */ s8 bombchuBowlingStatus; // "bombchu_game_flag" + /* 0x11E5E */ u8 fadeTransition; + /* 0x11E60 */ CollisionCheckContext colChkCtx; + /* 0x120FC */ u16 envFlags[20]; + /* 0x12124 */ PreRender pauseBgPreRender; + /* 0x12174 */ char unk_12174[0x53]; + /* 0x121C7 */ s8 unk_121C7; + /* 0x121C8 */ TransitionContext transitionCtx; + /* 0x12418 */ char unk_12418[0x3]; + /* 0x1241B */ u8 transitionMode; // "fbdemo_wipe_modem" + /* 0x1241C */ TransitionFade transitionFade; + /* 0x12428 */ char unk_12428[0x3]; + /* 0x1242B */ u8 unk_1242B; + /* 0x1242C */ SceneTableEntry* loadedScene; + /* 0x12430 */ char unk_12430[0xE8]; +} GlobalContext; // size = 0x12518 + +typedef struct { + /* 0x0000 */ GameState state; + /* 0x00A8 */ View view; +} OpeningContext; // size = 0x1D0 + +typedef struct { + /* 0x00000 */ GameState state; + /* 0x000A4 */ Vtx* windowVtx; + /* 0x000A8 */ u8* staticSegment; + /* 0x000AC */ u8* parameterSegment; + /* 0x000B0 */ char unk_B0[0x8]; + /* 0x000B8 */ View view; + /* 0x001E0 */ SramContext sramCtx; + /* 0x001E4 */ char unk_1E4[0x4]; + /* 0x001E8 */ SkyboxContext skyboxCtx; + /* 0x00348 */ MessageContext msgCtx; + /* 0x0E760 */ Font font; + /* 0x1C8E8 */ EnvironmentContext envCtx; + /* 0x1C9E4 */ char unk_1C9E4[0x4]; + /* 0x1C9E8 */ Vtx* windowContentVtx; + /* 0x1C9EC */ Vtx* keyboardVtx; + /* 0x1C9F0 */ Vtx* nameEntryVtx; + /* 0x1C9F4 */ u8 n64ddFlag; + /* 0x1C9F6 */ u16 deaths[3]; + /* 0x1C9FC */ u8 fileNames[3][8]; + /* 0x1CA14 */ u16 healthCapacities[3]; + /* 0x1CA1C */ u32 questItems[3]; + /* 0x1CA28 */ s16 n64ddFlags[3]; + /* 0x1CA2E */ s8 defense[3]; + /* 0x1CA32 */ u16 health[3]; + /* 0x1CA38 */ s16 buttonIndex; + /* 0x1CA3A */ s16 confirmButtonIndex; // 0: yes, 1: quit + /* 0x1CA3C */ s16 menuMode; + /* 0x1CA3E */ s16 configMode; + /* 0x1CA40 */ s16 prevConfigMode; + /* 0x1CA42 */ s16 nextConfigMode; + /* 0x1CA44 */ s16 selectMode; + /* 0x1CA46 */ s16 selectedFileIndex; + /* 0x1CA48 */ char unk_1CA48[0x2]; + /* 0x1CA4A */ s16 fileNamesY[3]; + /* 0x1CA50 */ s16 actionTimer; + /* 0x1CA52 */ s16 buttonYOffsets[6]; + /* 0x1CA5E */ s16 copyDestFileIndex; + /* 0x1CA60 */ s16 warningLabel; + /* 0x1CA62 */ s16 warningButtonIndex; + /* 0x1CA64 */ s16 titleLabel; + /* 0x1CA66 */ s16 nextTitleLabel; + /* 0x1CA68 */ s16 windowColor[3]; + /* 0x1CA6E */ s16 titleAlpha[2]; + /* 0x1CA72 */ s16 windowAlpha; + /* 0x1CA74 */ s16 fileButtonAlpha[3]; + /* 0x1CA7A */ s16 nameBoxAlpha[3]; + /* 0x1CA80 */ s16 nameAlpha[3]; + /* 0x1CA86 */ s16 connectorAlpha[3]; + /* 0x1CA8C */ s16 fileInfoAlpha[3]; + /* 0x1CA92 */ s16 actionButtonAlpha[2]; + /* 0x1CA96 */ s16 confirmButtonAlpha[2]; + /* 0x1CA9A */ s16 optionButtonAlpha; + /* 0x1CA9C */ s16 nameEntryBoxAlpha; + /* 0x1CA9E */ s16 controlsAlpha; + /* 0x1CAA0 */ s16 emptyFileTextAlpha; + /* 0x1CAA2 */ s16 highlightColor[4]; + /* 0x1CAAA */ s16 highlightPulseDir; // 0 fade out, 1 fade in + /* 0x1CAAC */ s16 unk_1CAAC; // initialized but never used + /* 0x1CAAE */ s16 confirmButtonTexIndices[2]; + /* 0x1CAB2 */ s16 inputTimerX; + /* 0x1CAB4 */ s16 inputTimerY; + /* 0x1CAB6 */ s16 stickXDir; + /* 0x1CAB8 */ s16 stickYDir; + /* 0x1CABA */ s16 stickRelX; + /* 0x1CABC */ s16 stickRelY; + /* 0x1CABE */ s16 nameEntryBoxPosX; + /* 0x1CAC0 */ s16 windowPosX; + /* 0x1CAC4 */ f32 windowRot; + /* 0x1CAC8 */ s16 kbdButton; // only for buttons, not characters + /* 0x1CACA */ s16 charPage; // 0: hiragana, 1: katakana, 2: alphabet + /* 0x1CACC */ s16 charBgAlpha; // square shape the letter sits in + /* 0x1CACE */ s16 charIndex; // 0 - 64, top left to bottom right + /* 0x1CAD0 */ s16 kbdX; // (0, 0) is top left character + /* 0x1CAD2 */ s16 kbdY; + /* 0x1CAD4 */ s16 newFileNameCharCount; + /* 0x1CAD6 */ s16 unk_1CAD6[5]; +} FileChooseContext; // size = 0x1CAE0 + +typedef enum { + DPM_UNK = 0, + DPM_PLAYER = 1, + DPM_ENEMY = 2, + DPM_UNK3 = 3 +} DynaPolyMoveFlag; + +typedef struct { + /* 0x00 */ AnimationHeader* animation; + /* 0x04 */ f32 playSpeed; + /* 0x08 */ f32 startFrame; + /* 0x0C */ f32 frameCount; + /* 0x10 */ u8 mode; + /* 0x14 */ f32 morphFrames; +} AnimationInfo; // size = 0x18 + +typedef struct { + /* 0x00 */ AnimationHeader* animation; + /* 0x04 */ f32 frameCount; + /* 0x08 */ u8 mode; + /* 0x0C */ f32 morphFrames; +} AnimationFrameCountInfo; // size = 0x10 + +typedef struct { + /* 0x00 */ AnimationHeader* animation; + /* 0x04 */ f32 playSpeed; + /* 0x08 */ u8 mode; + /* 0x0C */ f32 morphFrames; +} AnimationSpeedInfo; // size = 0x10 + +typedef struct { + /* 0x00 */ AnimationHeader* animation; + /* 0x04 */ u8 mode; + /* 0x08 */ f32 morphFrames; +} AnimationMinimalInfo; // size = 0xC + +typedef struct { + /* 0x00 */ s16 unk_00; + /* 0x02 */ s16 unk_02; + /* 0x04 */ s16 unk_04; + /* 0x06 */ s16 unk_06; + /* 0x08 */ Vec3s unk_08; + /* 0x0E */ Vec3s unk_0E; + /* 0x14 */ f32 unk_14; + /* 0x18 */ Vec3f unk_18; + /* 0x24 */ s16 unk_24; +} struct_80034A14_arg1; // size = 0x28 + +typedef struct { + /* 0x00 */ s8 scene; + /* 0x01 */ s8 spawn; + /* 0x02 */ u16 field; +} EntranceInfo; // size = 0x4 + +typedef struct { + /* 0x00 */ void* loadedRamAddr; + /* 0x04 */ u32 vromStart; // if applicable + /* 0x08 */ u32 vromEnd; // if applicable + /* 0x0C */ void* vramStart; // if applicable + /* 0x10 */ void* vramEnd; // if applicable + /* 0x14 */ UNK_PTR unk_14; + /* 0x18 */ void* init; // initializes and executes the given context + /* 0x1C */ void* destroy; // deconstructs the context, and sets the next context to load + /* 0x20 */ UNK_PTR unk_20; + /* 0x24 */ UNK_PTR unk_24; + /* 0x28 */ UNK_TYPE4 unk_28; + /* 0x2C */ u32 instanceSize; +} GameStateOverlay; // size = 0x30 + +typedef struct PreNMIContext { + /* 0x00 */ GameState state; + /* 0xA4 */ u32 timer; + /* 0xA8 */ UNK_TYPE4 unk_A8; +} PreNMIContext; // size = 0xAC + +typedef enum { + /* 1 */ F_8F = 1, + /* 2 */ F_7F, + /* 3 */ F_6F, + /* 4 */ F_5F, + /* 5 */ F_4F, + /* 6 */ F_3F, + /* 7 */ F_2F, + /* 8 */ F_1F, + /* 9 */ F_B1, + /* 10 */ F_B2, + /* 11 */ F_B3, + /* 12 */ F_B4, + /* 13 */ F_B5, + /* 14 */ F_B6, + /* 15 */ F_B7, + /* 16 */ F_B8 +} FloorID; + +// All arrays pointed in this struct are indexed by "map indexes" +// In dungeons, the map index corresponds to the dungeon index (which also indexes keys, items, etc) +// In overworld areas, the map index corresponds to the overworld area index (spot 00, 01, etc) +typedef struct { + /* 0x00 */ s16 (*floorTexIndexOffset)[8]; // dungeon texture index offset by floor + /* 0x04 */ s16* bossFloor; // floor the boss is on + /* 0x08 */ s16 (*roomPalette)[32]; // map palette by room + /* 0x0C */ s16* maxPaletteCount; // max number of palettes in a same floor + /* 0x10 */ s16 (*paletteRoom)[8][14]; // room by palette by floor + /* 0x14 */ s16 (*roomCompassOffsetX)[44]; // dungeon compass icon X offset by room + /* 0x18 */ s16 (*roomCompassOffsetY)[44]; // dungeon compass icon Y offset by room + /* 0x1C */ u8* dgnMinimapCount; // number of room minimaps + /* 0x20 */ u16* dgnMinimapTexIndexOffset; // dungeon minimap texture index offset + /* 0x24 */ u16* owMinimapTexSize; + /* 0x28 */ u16* owMinimapTexOffset; + /* 0x2C */ s16* owMinimapPosX; + /* 0x30 */ s16* owMinimapPosY; + /* 0x34 */ s16 (*owCompassInfo)[4]; // [X scale, Y scale, X offset, Y offset] + /* 0x38 */ s16* dgnMinimapTexIndexBase; // dungeon minimap texture index base + /* 0x3C */ s16 (*dgnCompassInfo)[4]; // [X scale, Y scale, X offset, Y offset] + /* 0x40 */ s16* owMinimapWidth; + /* 0x44 */ s16* owMinimapHeight; + /* 0x48 */ s16* owEntranceIconPosX; // "dungeon entrance" icon X pos + /* 0x4C */ s16* owEntranceIconPosY; // "dungeon entrance" icon Y pos + /* 0x50 */ u16* owEntranceFlag; // flag in inf_table[26] based on which entrance icons are shown (0xFFFF = always shown) + /* 0x54 */ f32 (*floorCoordY)[8]; // Y coordinate of each floor + /* 0x58 */ u16* switchEntryCount; // number of "room switch" entries, which correspond to the next 3 arrays + /* 0x5C */ u8 (*switchFromRoom)[51]; // room to come from + /* 0x60 */ u8 (*switchFromFloor)[51]; // floor to come from + /* 0x64 */ u8 (*switchToRoom)[51]; // room to go to + /* 0x68 */ u8 (*floorID)[8]; + /* 0x6C */ s16* skullFloorIconY; // dungeon big skull icon Y pos +} MapData; // size = 0x70 + +#define PAUSE_MAP_MARK_NONE -1 +#define PAUSE_MAP_MARK_CHEST 0 +#define PAUSE_MAP_MARK_BOSS 1 + +typedef struct { + /* 0x00 */ s16 chestFlag; // chest icon is only displayed if this flag is not set for the current room, -1 for no flag + /* 0x04 */ f32 x, y; // coordinates to place the icon (top-left corner) +} PauseMapMarkPoint; // size = 0x0C + +typedef struct { + /* 0x00 */ s16 markType; // 0 for the chest icon, 1 for the boss skull icon, -1 for none + /* 0x04 */ s32 unk_04; + /* 0x08 */ const Vtx* vtx; + /* 0x0C */ s32 vtxCount; + /* 0x10 */ s32 count; // number of icons to display + /* 0x14 */ PauseMapMarkPoint points[12]; +} PauseMapMarkData; // size = 0xA4 + +typedef PauseMapMarkData PauseMapMarksData[3]; + +typedef struct DebugDispObject { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3s rot; + /* 0x14 */ Vec3f scale; + /* 0x20 */ Color_RGBA8 color; + /* 0x24 */ s16 type; + /* 0x28 */ struct DebugDispObject* next; +} DebugDispObject; // size = 0x2C + +typedef enum { + MTXMODE_NEW, // generates a new matrix + MTXMODE_APPLY // applies transformation to the current matrix +} MatrixMode; + +typedef struct FaultClient { + /* 0x00 */ struct FaultClient* next; + /* 0x04 */ u32 callback; + /* 0x08 */ u32 param1; + /* 0x0C */ u32 param2; +} FaultClient; // size = 0x10 + +typedef struct FaultAddrConvClient { + /* 0x00 */ struct FaultAddrConvClient* next; + /* 0x04 */ u32 callback; + /* 0x08 */ u32 param; +} FaultAddrConvClient; // size = 0xC + + +typedef struct { + /* 0x00 */ u32 (*callback)(u32, u32); + /* 0x04 */ u32 param0; + /* 0x08 */ u32 param1; + /* 0x0C */ u32 ret; + /* 0x10 */ OSMesgQueue* queue; + /* 0x14 */ OSMesg msg; +} FaultClientContext; // size = 0x18 + +typedef struct FaultThreadStruct { + /* 0x000 */ OSThread thread; + /* 0x1B0 */ u8 unk_1B0[0x600]; + /* 0x7B0 */ OSMesgQueue queue; + /* 0x7C8 */ OSMesg msg; + /* 0x7CC */ u8 exitDebugger; + /* 0x7CD */ u8 msgId; + /* 0x7CE */ u8 faultHandlerEnabled; + /* 0x7CF */ u8 faultActive; + /* 0x7D0 */ OSThread* faultedThread; + /* 0x7D4 */ void(*padCallback)(Input*); + /* 0x7D8 */ FaultClient* clients; + /* 0x7DC */ FaultAddrConvClient* addrConvClients; + /* 0x7E0 */ u8 unk_7E0[4]; + /* 0x7E4 */ Input padInput; + /* 0x7FC */ u16 colors[36]; + /* 0x844 */ void* fb; + /* 0x848 */ u32 currClientThreadSp; + /* 0x84C */ u8 unk_84C[4]; +} FaultThreadStruct; // size = 0x850 + +typedef struct { + /* 0x00 */ u16* fb; + /* 0x04 */ u16 w; + /* 0x08 */ u16 h; + /* 0x0A */ u16 yStart; + /* 0x0C */ u16 yEnd; + /* 0x0E */ u16 xStart; + /* 0x10 */ u16 xEnd; + /* 0x12 */ u16 foreColor; + /* 0x14 */ u16 backColor; + /* 0x14 */ u16 cursorX; + /* 0x16 */ u16 cursorY; + /* 0x18 */ const u32* fontData; + /* 0x1C */ u8 charW; + /* 0x1D */ u8 charH; + /* 0x1E */ s8 charWPad; + /* 0x1F */ s8 charHPad; + /* 0x20 */ u16 printColors[10]; + /* 0x34 */ u8 escCode; // bool + /* 0x35 */ u8 osSyncPrintfEnabled; + /* 0x38 */ void(*inputCallback)(); +} FaultDrawer; // size = 0x3C + +typedef struct { + /* 0x00 */ PrintCallback callback; + /* 0x04 */ Gfx* dList; + /* 0x08 */ u16 posX; + /* 0x0A */ u16 posY; + /* 0x0C */ u16 baseX; + /* 0x0E */ u8 baseY; + /* 0x0F */ u8 flags; + /* 0x10 */ Color_RGBA8_u32 color; + /* 0x14 */ char unk_14[0x1C]; // unused +} GfxPrint; // size = 0x30 + +#define GFXP_UNUSED "\x8E" +#define GFXP_UNUSED_CHAR 0x8E +#define GFXP_HIRAGANA "\x8D" +#define GFXP_HIRAGANA_CHAR 0x8D +#define GFXP_KATAKANA "\x8C" +#define GFXP_KATAKANA_CHAR 0x8C +#define GFXP_RAINBOW_ON "\x8B" +#define GFXP_RAINBOW_ON_CHAR 0x8B +#define GFXP_RAINBOW_OFF "\x8A" +#define GFXP_RAINBOW_OFF_CHAR 0x8A + +#define GFXP_FLAG_HIRAGANA (1 << 0) +#define GFXP_FLAG_RAINBOW (1 << 1) +#define GFXP_FLAG_SHADOW (1 << 2) +#define GFXP_FLAG_UPDATE (1 << 3) +#define GFXP_FLAG_ENLARGE (1 << 6) +#define GFXP_FLAG_OPEN (1 << 7) + +typedef struct StackEntry { + /* 0x00 */ struct StackEntry* next; + /* 0x04 */ struct StackEntry* prev; + /* 0x08 */ uintptr_t head; + /* 0x0C */ uintptr_t tail; + /* 0x10 */ u32 initValue; + /* 0x14 */ s32 minSpace; + /* 0x18 */ const char* name; +} StackEntry; + +typedef enum { + STACK_STATUS_OK = 0, + STACK_STATUS_WARNING = 1, + STACK_STATUS_OVERFLOW = 2 +} StackStatus; + +typedef struct { + /* 0x00 */ u32 magic; // IS64 + /* 0x04 */ u32 get; + /* 0x08 */ u8 unk_08[0x14-0x08]; + /* 0x14 */ u32 put; + /* 0x18 */ u8 unk_18[0x20-0x18]; + /* 0x20 */ u8 data[0x10000-0x20]; +} ISVDbg; + +typedef struct { + /* 0x00 */ char name[0x18]; + /* 0x18 */ u32 mediaFormat; + /* 0x1C */ union { + struct { + u16 cartId; + u8 countryCode; + u8 version; + }; + u32 regionInfo; + }; +} LocaleCartInfo; // size = 0x20 + +typedef struct { + /* 0x00 */ char magic[4]; // Yaz0 + /* 0x04 */ u32 decSize; + /* 0x08 */ u32 compInfoOffset; // only used in mio0 + /* 0x0C */ u32 uncompDataOffset; // only used in mio0 + /* 0x10 */ u32 data[1]; +} Yaz0Header; // size = 0x10 ("data" is not part of the header) + +typedef struct { + /* 0x00 */ s16 type; + /* 0x02 */ char misc[0x1E]; +} OSScMsg; // size = 0x20 + +typedef struct IrqMgrClient { + /* 0x00 */ struct IrqMgrClient* prev; + /* 0x04 */ OSMesgQueue* queue; +} IrqMgrClient; + +typedef struct { + /* 0x000 */ OSScMsg retraceMsg; // this apparently got moved from OSSched + /* 0x020 */ OSScMsg prenmiMsg; // this apparently got moved from OSSched + /* 0x040 */ OSScMsg nmiMsg; + /* 0x060 */ OSMesgQueue queue; + /* 0x078 */ OSMesg msgBuf[8]; + /* 0x098 */ OSThread thread; + /* 0x248 */ IrqMgrClient* clients; + /* 0x24C */ u8 resetStatus; + /* 0x250 */ OSTime resetTime; + /* 0x258 */ OSTimer timer; + /* 0x278 */ OSTime retraceTime; +} IrqMgr; // size = 0x280 + +typedef struct PadMgr { + /* 0x0000 */ OSContStatus padStatus[4]; + /* 0x0010 */ OSMesg serialMsgBuf[1]; + /* 0x0014 */ OSMesg lockMsgBuf[1]; + /* 0x0018 */ OSMesg interruptMsgBuf[4]; + /* 0x0028 */ OSMesgQueue serialMsgQ; + /* 0x0040 */ OSMesgQueue lockMsgQ; + /* 0x0058 */ OSMesgQueue interruptMsgQ; + /* 0x0070 */ IrqMgrClient irqClient; + /* 0x0078 */ IrqMgr* irqMgr; + /* 0x0080 */ OSThread thread; + /* 0x0230 */ Input inputs[4]; + /* 0x0290 */ OSContPad pads[4]; + /* 0x02A8 */ vu8 validCtrlrsMask; + /* 0x02A9 */ u8 nControllers; + /* 0x02AA */ u8 ctrlrIsConnected[4]; // "Key_switch" originally + /* 0x02AE */ u8 pakType[4]; // 1 if rumble pack, 2 if mempak? + /* 0x02B2 */ vu8 rumbleEnable[4]; + /* 0x02B6 */ u8 rumbleCounter[4]; // not clear exact meaning + /* 0x02BC */ OSPfs pfs[4]; + /* 0x045C */ vu8 rumbleOffFrames; + /* 0x045D */ vu8 rumbleOnFrames; + /* 0x045E */ u8 preNMIShutdown; + /* 0x0460 */ void (*retraceCallback)(struct PadMgr* padmgr, s32 unk464); + /* 0x0464 */ u32 retraceCallbackValue; +} PadMgr; // size = 0x468 + +// == Previously sched.h + +#define OS_SC_NEEDS_RDP 0x0001 +#define OS_SC_NEEDS_RSP 0x0002 +#define OS_SC_DRAM_DLIST 0x0004 +#define OS_SC_PARALLEL_TASK 0x0010 +#define OS_SC_LAST_TASK 0x0020 +#define OS_SC_SWAPBUFFER 0x0040 + +#define OS_SC_RCP_MASK 0x0003 +#define OS_SC_TYPE_MASK 0x0007 + +typedef struct { + /* 0x0000 */ u16* curBuffer; + /* 0x0004 */ u16* nextBuffer; +} FrameBufferSwap; + +typedef struct { + /* 0x0000 */ OSMesgQueue interruptQ; + /* 0x0018 */ OSMesg intBuf[8]; + /* 0x0038 */ OSMesgQueue cmdQ; + /* 0x0050 */ OSMesg cmdMsgBuf[8]; + /* 0x0070 */ OSThread thread; + /* 0x0220 */ OSScTask* audioListHead; + /* 0x0224 */ OSScTask* gfxListHead; + /* 0x0228 */ OSScTask* audioListTail; + /* 0x022C */ OSScTask* gfxListTail; + /* 0x0230 */ OSScTask* curRSPTask; + /* 0x0234 */ OSScTask* curRDPTask; + /* 0x0238 */ s32 retraceCnt; + /* 0x023C */ s32 doAudio; + /* 0x0240 */ CfbInfo* curBuf; + /* 0x0244 */ CfbInfo* pendingSwapBuf1; + /* 0x0220 */ CfbInfo* pendingSwapBuf2; + /* 0x0220 */ UNK_TYPE4 unk_24C; + /* 0x0250 */ IrqMgrClient irqClient; +} SchedContext; // size = 0x258 + +// ======================== + +#define OS_SC_RETRACE_MSG 1 +#define OS_SC_DONE_MSG 2 +#define OS_SC_NMI_MSG 3 // name is made up, 3 is OS_SC_RDP_DONE_MSG in the original sched.c +#define OS_SC_PRE_NMI_MSG 4 + +#define OS_SC_DP 0x0001 +#define OS_SC_SP 0x0002 +#define OS_SC_YIELD 0x0010 +#define OS_SC_YIELDED 0x0020 + +typedef struct { + /* 0x0000 */ IrqMgr* irqMgr; + /* 0x0004 */ SchedContext* sched; + /* 0x0008 */ OSScTask audioTask; + /* 0x0060 */ char unk_60[0x10]; + /* 0x0070 */ AudioTask* rspTask; + /* 0x0074 */ OSMesgQueue unk_74; + /* 0x008C */ OSMesg unk_8C; + /* 0x0090 */ OSMesgQueue unk_90; + /* 0x00A8 */ OSMesg unk_A8; + /* 0x00AC */ OSMesgQueue unk_AC; + /* 0x00C4 */ OSMesg unk_C4; + /* 0x00C8 */ OSMesgQueue unk_C8; + /* 0x00E0 */ OSMesg unk_E0; + /* 0x00E4 */ char unk_E4[0x04]; + /* 0x00E8 */ OSThread unk_E8; +} AudioMgr; // size = 0x298 + +struct ArenaNode; + +typedef struct Arena { + /* 0x00 */ struct ArenaNode* head; + /* 0x04 */ void* start; + /* 0x08 */ OSMesgQueue lock; + /* 0x20 */ u8 unk_20; + /* 0x21 */ u8 isInit; + /* 0x22 */ u8 flag; +} Arena; // size = 0x24 + +typedef struct ArenaNode { + /* 0x00 */ s16 magic; + /* 0x02 */ s16 isFree; + /* 0x04 */ size_t size; + /* 0x08 */ struct ArenaNode* next; + /* 0x0C */ struct ArenaNode* prev; + ///* 0x10 */ const char* filename; + ///* 0x14 */ s32 line; + ///* 0x18 */ OSId threadId; + ///* 0x1C */ Arena* arena; + ///* 0x20 */ OSTime time; + ///* 0x28 */ u8 unk_28[0x30-0x28]; // probably padding +} ArenaNode; // size = 0x10 + +typedef struct OverlayRelocationSection { + /* 0x00 */ u32 textSize; + /* 0x04 */ u32 dataSize; + /* 0x08 */ u32 rodataSize; + /* 0x0C */ u32 bssSize; + /* 0x10 */ u32 nRelocations; + /* 0x14 */ u32 relocations[1]; +} OverlayRelocationSection; // size >= 0x18 + +typedef struct { + /* 0x00 */ u32 resetting; + /* 0x04 */ u32 resetCount; + /* 0x08 */ OSTime duration; + /* 0x10 */ OSTime resetTime; +} PreNmiBuff; // size = 0x18 (actually osAppNmiBuffer is 0x40 bytes large but the rest is unused) + +typedef struct { + /* 0x00 */ s16 unk_00; + /* 0x02 */ s16 unk_02; + /* 0x04 */ s16 unk_04; +} SubQuakeRequest14; + +typedef struct { + /* 0x00 */ s16 randIdx; + /* 0x02 */ s16 countdownMax; + /* 0x04 */ Camera* cam; + /* 0x08 */ u32 callbackIdx; + /* 0x0C */ s16 y; + /* 0x0E */ s16 x; + /* 0x10 */ s16 zoom; + /* 0x12 */ s16 rotZ; + /* 0x14 */ SubQuakeRequest14 unk_14; + /* 0x1A */ s16 speed; + /* 0x1C */ s16 unk_1C; + /* 0x1E */ s16 countdown; + /* 0x20 */ s16 camPtrIdx; +} QuakeRequest; // size = 0x24 + +typedef struct { + /* 0x00 */ Vec3f vec1; + /* 0x0C */ Vec3f vec2; + /* 0x18 */ s16 rotZ; + /* 0x1A */ s16 unk_1A; + /* 0x1C */ s16 zoom; +} ShakeInfo; // size = 0x1E + +typedef struct { + /* 0x00 */ Vec3f atOffset; + /* 0x0C */ Vec3f eyeOffset; + /* 0x18 */ s16 rotZ; + /* 0x1A */ s16 unk_1A; + /* 0x1C */ s16 zoom; + /* 0x20 */ f32 unk_20; +} QuakeCamCalc; // size = 0x24 + + +#define UCODE_NULL 0 +#define UCODE_F3DZEX 1 +#define UCODE_UNK 2 +#define UCODE_S2DEX 3 + +typedef struct { + /* 0x00 */ u32 type; + /* 0x04 */ void* ptr; +} UCodeInfo; // size = 0x8 + +typedef struct { + /* 0x00 */ u32 segments[NUM_SEGMENTS]; + /* 0x40 */ Gfx* dlStack[18]; + /* 0x88 */ s32 dlDepth; + /* 0x8C */ u32 dlCnt; + /* 0x90 */ u32 vtxCnt; + /* 0x94 */ u32 spvtxCnt; + /* 0x98 */ u32 tri1Cnt; + /* 0x9C */ u32 tri2Cnt; + /* 0xA0 */ u32 quadCnt; + /* 0xA4 */ u32 lineCnt; + /* 0xA8 */ u32 loaducodeCnt; + /* 0xAC */ u32 pipeSyncRequired; + /* 0xB0 */ u32 tileSyncRequired; + /* 0xB4 */ u32 loadSyncRequired; + /* 0xB8 */ u32 syncErr; + /* 0xBC */ s32 enableLog; + /* 0xC0 */ s32 ucodeType; + /* 0xC4 */ s32 ucodeInfoCount; + /* 0xC8 */ UCodeInfo* ucodeInfo; + /* 0xCC */ u32 modeH; + /* 0xD0 */ u32 modeL; + /* 0xD4 */ u32 geometryMode; +} UCodeDisas; // size = 0xD8 + +typedef struct { + /* 0x00 */ u16 table[8*8]; +} JpegQuantizationTable; // size = 0x80 + +typedef struct { + /* 0x00 */ u8 codeOffs[16]; + /* 0x10 */ u16 codesA[16]; + /* 0x30 */ u16 codesB[16]; + /* 0x50 */ u8* symbols; +} JpegHuffmanTable; // size = 0x54 + +// this struct might be inaccurate but it's not used outside jpegutils.c anyways +typedef struct { + /* 0x000 */ u8 codeOffs[16]; + /* 0x010 */ u16 dcCodes[120]; + /* 0x100 */ u16 acCodes[256]; +} JpegHuffmanTableOld; // size = 0x300 + +typedef struct { + /* 0x00 */ u32 address; + /* 0x04 */ u32 mbCount; + /* 0x08 */ u32 mode; + /* 0x0C */ u32 qTableYPtr; + /* 0x10 */ u32 qTableUPtr; + /* 0x14 */ u32 qTableVPtr; + /* 0x18 */ char unk_18[0x8]; +} JpegTaskData; // size = 0x20 + +typedef struct { + /* 0x000 */ JpegTaskData taskData; + /* 0x020 */ char yieldData[0x200]; + /* 0x220 */ JpegQuantizationTable qTableY; + /* 0x2A0 */ JpegQuantizationTable qTableU; + /* 0x320 */ JpegQuantizationTable qTableV; + /* 0x3A0 */ u8 codesLengths[0x110]; + /* 0x4B0 */ u16 codes[0x108]; + /* 0x6C0 */ u16 data[4][0x180]; +} JpegWork; // size = 0x12C0 + +typedef struct { + /* 0x00 */ void* imageData; + /* 0x04 */ u8 mode; + /* 0x05 */ u8 unk_05; + /* 0x08 */ JpegHuffmanTable* hTablePtrs[4]; + /* 0x18 */ u8 unk_18; +} JpegDecoder; // size = 0x1C + +typedef struct { + /* 0x00 */ u8 dqtCount; + /* 0x04 */ u8* dqtPtr[3]; + /* 0x10 */ u8 dhtCount; + /* 0x14 */ u8* dhtPtr[4]; + /* 0x24 */ void* imageData; + /* 0x28 */ u32 mode; // 0 if Y V0 is 1 and 2 if Y V0 is 2 + /* 0x2C */ char unk_2C[4]; + /* 0x30 */ OSScTask scTask; + /* 0x88 */ char unk_88[0x10]; + /* 0x98 */ OSMesgQueue mq; + /* 0xB0 */ OSMesg msg; + /* 0xB4 */ JpegWork* workBuf; +} JpegContext; // size = 0xB8 + +typedef struct { + /* 0x00 */ u32 byteIdx; + /* 0x04 */ u8 bitIdx; + /* 0x05 */ u8 dontSkip; + /* 0x08 */ u32 curWord; + /* 0x0C */ s16 unk_0C; + /* 0x0E */ s16 unk_0E; + /* 0x10 */ s16 unk_10; +} JpegDecoderState; // size = 0x14 + +typedef struct { + /* 0x0000 */ OSViMode customViMode; + /* 0x0050 */ s32 viHeight; + /* 0x0054 */ s32 viWidth; + /* 0x0058 */ s32 unk_58; // Right adjustment? + /* 0x005C */ s32 unk_5C; // Left adjustment? + /* 0x0060 */ s32 unk_60; // Bottom adjustment? + /* 0x0064 */ s32 unk_64; // Top adjustment? + /* 0x0068 */ s32 viModeBase; // enum: {0, 1, 2, 3} + /* 0x006C */ s32 viTvType; + /* 0x0070 */ u32 unk_70; // bool + /* 0x0074 */ u32 unk_74; // bool + /* 0x0078 */ u32 unk_78; // bool + /* 0x007C */ u32 unk_7C; // bool + /* 0x0080 */ u32 viFeatures; + /* 0x0084 */ u32 unk_84; +} ViMode; + +// Vis... +typedef struct { + /* 0x00 */ u32 type; + /* 0x04 */ u32 setScissor; + /* 0x08 */ Color_RGBA8_u32 color; + /* 0x0C */ Color_RGBA8_u32 envColor; +} struct_801664F0; // size = 0x10 + +typedef struct { + /* 0x00 */ u32 unk_00; + /* 0x04 */ u32 setScissor; + /* 0x08 */ Color_RGBA8_u32 primColor; + /* 0x0C */ Color_RGBA8_u32 envColor; + /* 0x10 */ u16* tlut; + /* 0x14 */ Gfx* monoDl; +} VisMono; // size = 0x18 + +// Vis... +typedef struct { + /* 0x00 */ u32 useRgba; + /* 0x04 */ u32 setScissor; + /* 0x08 */ Color_RGBA8_u32 primColor; + /* 0x08 */ Color_RGBA8_u32 envColor; +} struct_80166500; // size = 0x10 + +typedef struct { + /* 0x000 */ u8 rumbleEnable[4]; + /* 0x004 */ u8 unk_04[0x40]; + /* 0x044 */ u8 unk_44[0x40]; + /* 0x084 */ u8 unk_84[0x40]; + /* 0x0C4 */ u8 unk_C4[0x40]; + /* 0x104 */ u8 unk_104; + /* 0x105 */ u8 unk_105; + /* 0x106 */ u16 unk_106; + /* 0x108 */ u16 unk_108; + /* 0x10A */ u8 unk_10A; + /* 0x10B */ u8 unk_10B; + /* 0x10C */ u8 unk_10C; + /* 0x10D */ u8 unk_10D; +} UnkRumbleStruct; // size = 0x10E + +typedef struct { + /* 0x00 */ char unk_00[0x18]; + /* 0x18 */ s32 unk_18; + /* 0x1C */ s32 y; +} SpeedMeter; // size = 0x20 + +typedef struct { + /* 0x00 */ s32 maxval; + /* 0x04 */ s32 val; + /* 0x08 */ u16 backColor; + /* 0x0A */ u16 foreColor; + /* 0x0C */ s32 ulx; + /* 0x10 */ s32 lrx; + /* 0x14 */ s32 uly; + /* 0x18 */ s32 lry; +} SpeedMeterAllocEntry; // size = 0x1C + +typedef struct { + /* 0x00 */ volatile OSTime* time; + /* 0x04 */ u8 x; + /* 0x05 */ u8 y; + /* 0x06 */ u16 color; +} SpeedMeterTimeEntry; // size = 0x08 + +typedef struct { + /* 0x00 */ u32 value; + /* 0x04 */ const char* name; +} F3dzexConst; // size = 0x8 + +typedef struct { + /* 0x00 */ u32 value; + /* 0x04 */ const char* setName; + /* 0x08 */ const char* unsetName; +} F3dzexFlag; // size = 0x0C + +typedef struct { + /* 0x00 */ const char* name; + /* 0x04 */ u32 value; + /* 0x08 */ u32 mask; +} F3dzexRenderMode; // size = 0x0C + +typedef struct { + /* 0x00 */ const char* name; + /* 0x04 */ u32 value; +} F3dzexSetModeMacroValue; // size = 0x8 + +typedef struct { + /* 0x00 */ const char* name; + /* 0x04 */ u32 shift; + /* 0x08 */ u32 len; + /* 0x0C */ F3dzexSetModeMacroValue values[4]; +} F3dzexSetModeMacro; // size = 0x2C + +typedef struct { + /* 0x00 */ u16* value; + /* 0x04 */ const char* name; +} FlagSetEntry; // size = 0x08 + +typedef struct { + /* 0x00 */ RomFile file; + /* 0x08 */ RomFile palette; +} SkyboxFile; // size = 0x10 + +typedef struct { + const char** textures; + const char** palettes; +} SkyboxTableEntry; + +#define ROM_FILE(name) \ + { 0, 0, #name } + +#define ROM_FILE_EMPTY \ + { 0, 0, "" } + +#define ROM_FILE_UNSET \ + { 0 } + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/soh/include/z64actor.h b/soh/include/z64actor.h new file mode 100644 index 000000000..ab1de9fc6 --- /dev/null +++ b/soh/include/z64actor.h @@ -0,0 +1,363 @@ +#ifndef Z64ACTOR_H +#define Z64ACTOR_H + +#include "z64dma.h" +#include "z64animation.h" +#include "z64math.h" +#include "z64collision_check.h" + +#define ACTOR_NUMBER_MAX 200 +#define INVISIBLE_ACTOR_MAX 20 +#define AM_FIELD_SIZE 0x27A0 +#define MASS_IMMOVABLE 0xFF // Cannot be pushed by OC collisions +#define MASS_HEAVY 0xFE // Can only be pushed by OC collisions with IMMOVABLE and HEAVY objects. + +struct Actor; +struct GlobalContext; +struct Lights; + +typedef void (*ActorFunc)(struct Actor*, struct GlobalContext*); +typedef void (*ActorResetFunc)(void); +typedef void (*ActorShadowFunc)(struct Actor*, struct Lights*, struct GlobalContext*); +typedef u16 (*callback1_800343CC)(struct GlobalContext*, struct Actor*); +typedef s16 (*callback2_800343CC)(struct GlobalContext*, struct Actor*); + +typedef struct { + Vec3f pos; + Vec3s rot; +} PosRot; // size = 0x14 + +typedef struct { + /* 0x00 */ s16 id; + /* 0x02 */ u8 category; // Classifies actor and determines when it will update or draw + /* 0x04 */ u32 flags; + /* 0x08 */ s16 objectId; + /* 0x0C */ u32 instanceSize; + /* 0x10 */ ActorFunc init; // Constructor + /* 0x14 */ ActorFunc destroy; // Destructor + /* 0x18 */ ActorFunc update; // Update Function + /* 0x1C */ ActorFunc draw; // Draw function + /* 0x20 */ ActorResetFunc reset; +} ActorInit; // size = 0x20 + +typedef enum { + /* 0 */ ALLOCTYPE_NORMAL, + /* 1 */ ALLOCTYPE_ABSOLUTE, + /* 2 */ ALLOCTYPE_PERMANENT +} AllocType; + +typedef struct { + /* 0x00 */ uintptr_t vromStart; + /* 0x04 */ uintptr_t vromEnd; + /* 0x08 */ void* vramStart; + /* 0x0C */ void* vramEnd; + /* 0x10 */ void* loadedRamAddr; // original name: "allocp" + /* 0x14 */ ActorInit* initInfo; + /* 0x18 */ char* name; + /* 0x1C */ u16 allocType; + /* 0x1E */ s8 numLoaded; // original name: "clients" +} ActorOverlay; // size = 0x20 + +typedef struct { + u8 table[32]; +} DamageTable; + +typedef struct { + /* 0x00 */ u8 health; + /* 0x02 */ s16 cylRadius; + /* 0x04 */ s16 cylHeight; + /* 0x06 */ u8 mass; +} CollisionCheckInfoInit; + +typedef struct { + /* 0x00 */ u8 health; + /* 0x02 */ s16 cylRadius; + /* 0x04 */ s16 cylHeight; + /* 0x06 */ s16 cylYShift; + /* 0x08 */ u8 mass; +} CollisionCheckInfoInit2; + +typedef struct { + /* 0x00 */ DamageTable* damageTable; + /* 0x04 */ Vec3f displacement; // Amount to correct velocity (0x5C) by when colliding into a body + /* 0x10 */ s16 cylRadius; // Used for various purposes + /* 0x12 */ s16 cylHeight; // Used for various purposes + /* 0x14 */ s16 cylYShift; // Unused. Purpose inferred from Cylinder16 and CollisionCheck_CylSideVsLineSeg + /* 0x16 */ u8 mass; // Used to compute displacement for OC collisions + /* 0x17 */ u8 health; // Note: some actors may use their own health variable instead of this one + /* 0x18 */ u8 damage; // Amount to decrement health by + /* 0x19 */ u8 damageEffect; // Stores what effect should occur when hit by a weapon + /* 0x1A */ u8 atHitEffect; // Stores what effect should occur when AT connects with an AC + /* 0x1B */ u8 acHitEffect; // Stores what effect should occur when AC is touched by an AT +} CollisionCheckInfo; // size = 0x1C + +typedef struct { + /* 0x00 */ Vec3s rot; // Current actor shape rotation + /* 0x06 */ s16 face; // Used to index eyebrow/eye/mouth textures. Only used by player + /* 0x08 */ f32 yOffset; // Model y axis offset. Represents model space units + /* 0x0C */ ActorShadowFunc shadowDraw; // Shadow draw function + /* 0x10 */ f32 shadowScale; // Changes the size of the shadow + /* 0x14 */ u8 shadowAlpha; // Default is 255 + /* 0x15 */ u8 feetFloorFlags; // Set if the actor's foot is clipped under the floor. & 1 is right foot, & 2 is left + /* 0x18 */ Vec3f feetPos[2]; // Update by using `Actor_SetFeetPos` in PostLimbDraw +} ActorShape; // size = 0x30 + +#define ACTOR_FLAG_0 (1 << 0) +#define ACTOR_FLAG_2 (1 << 2) +#define ACTOR_FLAG_3 (1 << 3) +#define ACTOR_FLAG_4 (1 << 4) +#define ACTOR_FLAG_5 (1 << 5) +#define ACTOR_FLAG_6 (1 << 6) +#define ACTOR_FLAG_7 (1 << 7) +#define ACTOR_FLAG_8 (1 << 8) +#define ACTOR_FLAG_9 (1 << 9) +#define ACTOR_FLAG_10 (1 << 10) +#define ACTOR_FLAG_ENKUSA_CUT (1 << 11) +#define ACTOR_FLAG_12 (1 << 12) +#define ACTOR_FLAG_13 (1 << 13) +#define ACTOR_FLAG_14 (1 << 14) +#define ACTOR_FLAG_15 (1 << 15) +#define ACTOR_FLAG_16 (1 << 16) +#define ACTOR_FLAG_17 (1 << 17) +#define ACTOR_FLAG_18 (1 << 18) +#define ACTOR_FLAG_19 (1 << 19) +#define ACTOR_FLAG_20 (1 << 20) +#define ACTOR_FLAG_21 (1 << 21) +#define ACTOR_FLAG_22 (1 << 22) +#define ACTOR_FLAG_23 (1 << 23) +#define ACTOR_FLAG_24 (1 << 24) +#define ACTOR_FLAG_25 (1 << 25) +#define ACTOR_FLAG_26 (1 << 26) +#define ACTOR_FLAG_27 (1 << 27) +#define ACTOR_FLAG_28 (1 << 28) + +typedef struct Actor { + /* 0x000 */ s16 id; // Actor ID + /* 0x002 */ u8 category; // Actor category. Refer to the corresponding enum for values + /* 0x003 */ s8 room; // Room number the actor is in. -1 denotes that the actor won't despawn on a room change + /* 0x004 */ u32 flags; // Flags used for various purposes + /* 0x008 */ PosRot home; // Initial position/rotation when spawned. Can be used for other purposes + /* 0x01C */ s16 params; // Configurable variable set by the actor's spawn data; original name: "args_data" + /* 0x01E */ s8 objBankIndex; // Object bank index of the actor's object dependency; original name: "bank" + /* 0x01F */ s8 targetMode; // Controls how far the actor can be targeted from and how far it can stay locked on + /* 0x020 */ u16 sfx; // SFX ID to play. Sound plays when value is set, then is cleared the following update cycle + /* 0x024 */ PosRot world; // Position/rotation in the world + /* 0x038 */ PosRot focus; // Target reticle focuses on this position. For player this represents head pos and rot + /* 0x04C */ f32 targetArrowOffset; // Height offset of the target arrow relative to `focus` position + /* 0x050 */ Vec3f scale; // Scale of the actor in each axis + /* 0x05C */ Vec3f velocity; // Velocity of the actor in each axis + /* 0x068 */ f32 speedXZ; // How fast the actor is traveling along the XZ plane + /* 0x06C */ f32 gravity; // Acceleration due to gravity. Value is added to Y velocity every frame + /* 0x070 */ f32 minVelocityY; // Sets the lower bounds cap on velocity along the Y axis + /* 0x074 */ CollisionPoly* wallPoly; // Wall polygon the actor is touching + /* 0x078 */ CollisionPoly* floorPoly; // Floor polygon directly below the actor + /* 0x07C */ u8 wallBgId; // Bg ID of the wall polygon the actor is touching + /* 0x07D */ u8 floorBgId; // Bg ID of the floor polygon directly below the actor + /* 0x07E */ s16 wallYaw; // Y rotation of the wall polygon the actor is touching + /* 0x080 */ f32 floorHeight; // Y position of the floor polygon directly below the actor + /* 0x084 */ f32 yDistToWater; // Distance to the surface of active waterbox. Negative value means above water + /* 0x088 */ u16 bgCheckFlags; // See comments below actor struct for wip docs. TODO: macros for these flags + /* 0x08A */ s16 yawTowardsPlayer; // Y rotation difference between the actor and the player + /* 0x08C */ f32 xyzDistToPlayerSq; // Squared distance between the actor and the player in the x,y,z axis + /* 0x090 */ f32 xzDistToPlayer; // Distance between the actor and the player in the XZ plane + /* 0x094 */ f32 yDistToPlayer; // Dist is negative if the actor is above the player + /* 0x098 */ CollisionCheckInfo colChkInfo; // Variables related to the Collision Check system + /* 0x0B4 */ ActorShape shape; // Variables related to the physical shape of the actor + /* 0x0E4 */ Vec3f projectedPos; // Position of the actor in projected space + /* 0x0F0 */ f32 projectedW; // w component of the projected actor position + /* 0x0F4 */ f32 uncullZoneForward; // Amount to increase the uncull zone forward by (in projected space) + /* 0x0F8 */ f32 uncullZoneScale; // Amount to increase the uncull zone scale by (in projected space) + /* 0x0FC */ f32 uncullZoneDownward; // Amount to increase uncull zone downward by (in projected space) + /* 0x100 */ Vec3f prevPos; // World position from the previous update cycle + /* 0x10C */ u8 isTargeted; // Set to true if the actor is currently being targeted by the player + /* 0x10D */ u8 targetPriority; // Lower values have higher priority. Resets to 0 when player stops targeting + /* 0x10E */ u16 textId; // Text ID to pass to link/display when interacting with the actor + /* 0x110 */ u16 freezeTimer; // Actor does not update when set. Timer decrements automatically + /* 0x112 */ u16 colorFilterParams; // Set color filter to red, blue, or white. Toggle opa or xlu + /* 0x114 */ u8 colorFilterTimer; // A non-zero value enables the color filter. Decrements automatically + /* 0x115 */ u8 isDrawn; // Set to true if the actor is currently being drawn. Always stays false for lens actors + /* 0x116 */ u8 dropFlag; // Configures what item is dropped by the actor from `Item_DropCollectibleRandom` + /* 0x117 */ u8 naviEnemyId; // Sets what 0600 dialog to display when talking to navi. Default 0xFF + /* 0x118 */ struct Actor* parent; // Usage is actor specific. Set if actor is spawned via `Actor_SpawnAsChild` + /* 0x11C */ struct Actor* child; // Usage is actor specific. Set if actor is spawned via `Actor_SpawnAsChild` + /* 0x120 */ struct Actor* prev; // Previous actor of this category + /* 0x124 */ struct Actor* next; // Next actor of this category + /* 0x128 */ ActorFunc init; // Initialization Routine. Called by `Actor_Init` or `Actor_UpdateAll` + /* 0x12C */ ActorFunc destroy; // Destruction Routine. Called by `Actor_Destroy` + /* 0x130 */ ActorFunc update; // Update Routine. Called by `Actor_UpdateAll` + /* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw` + /* 0x138 */ ActorResetFunc reset; + /* 0x138 */ ActorOverlay* overlayEntry; // Pointer to the overlay table entry for this actor + /* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom +} Actor; // size = 0x14C + +typedef enum { + /* 0 */ FOOT_LEFT, + /* 1 */ FOOT_RIGHT +} ActorFootIndex; + +/* +BgCheckFlags WIP documentation: +& 0x001 : Standing on the ground +& 0x002 : Has touched the ground (only active for 1 frame) +& 0x004 : Has left the ground (only active for 1 frame) +& 0x008 : Touching a wall +& 0x010 : Touching a ceiling +& 0x020 : On or below water surface +& 0x040 : Has touched water (actor is responsible for unsetting this the frame it touches the water) +& 0x080 : Similar to & 0x1 but with no velocity check and is cleared every frame +& 0x100 : Crushed between a floor and ceiling (triggers a void for player) +& 0x200 : Unknown (only set/used by player so far) +*/ + +/* +colorFilterParams WIP documentation +& 0x8000 : white +& 0x4000 : red +if neither of the above are set : blue + +(& 0x1F00 >> 5) | 7 : color intensity +0x2000 : translucent, else opaque +*/ + +typedef struct DynaPolyActor { + /* 0x000 */ struct Actor actor; + /* 0x14C */ s32 bgId; + /* 0x150 */ f32 unk_150; + /* 0x154 */ f32 unk_154; + /* 0x158 */ s16 unk_158; // y rotation? + /* 0x15A */ u16 unk_15A; + /* 0x15C */ u32 unk_15C; + /* 0x160 */ u8 unk_160; + /* 0x162 */ s16 unk_162; +} DynaPolyActor; // size = 0x164 + +typedef struct { + /* 0x00 */ MtxF* matrices; + /* 0x04 */ s16* objectIds; + /* 0x08 */ s16 count; + /* 0x0C */ Gfx** dLists; + /* 0x10 */ s32 val; // used for various purposes: both a status indicator and counter + /* 0x14 */ s32 prevLimbIndex; +} BodyBreak; + +#define BODYBREAK_OBJECT_DEFAULT -1 // use the same object as the actor +#define BODYBREAK_STATUS_READY -1 +#define BODYBREAK_STATUS_FINISHED 0 + +typedef enum { + /* 0x00 */ ITEM00_RUPEE_GREEN, + /* 0x01 */ ITEM00_RUPEE_BLUE, + /* 0x02 */ ITEM00_RUPEE_RED, + /* 0x03 */ ITEM00_HEART, + /* 0x04 */ ITEM00_BOMBS_A, + /* 0x05 */ ITEM00_ARROWS_SINGLE, + /* 0x06 */ ITEM00_HEART_PIECE, + /* 0x07 */ ITEM00_HEART_CONTAINER, + /* 0x08 */ ITEM00_ARROWS_SMALL, + /* 0x09 */ ITEM00_ARROWS_MEDIUM, + /* 0x0A */ ITEM00_ARROWS_LARGE, + /* 0x0B */ ITEM00_BOMBS_B, + /* 0x0C */ ITEM00_NUTS, + /* 0x0D */ ITEM00_STICK, + /* 0x0E */ ITEM00_MAGIC_LARGE, + /* 0x0F */ ITEM00_MAGIC_SMALL, + /* 0x10 */ ITEM00_SEEDS, + /* 0x11 */ ITEM00_SMALL_KEY, + /* 0x12 */ ITEM00_FLEXIBLE, + /* 0x13 */ ITEM00_RUPEE_ORANGE, + /* 0x14 */ ITEM00_RUPEE_PURPLE, + /* 0x15 */ ITEM00_SHIELD_DEKU, + /* 0x16 */ ITEM00_SHIELD_HYLIAN, + /* 0x17 */ ITEM00_TUNIC_ZORA, + /* 0x18 */ ITEM00_TUNIC_GORON, + /* 0x19 */ ITEM00_BOMBS_SPECIAL +} Item00Type; + +struct EnItem00; + +typedef void (*EnItem00ActionFunc)(struct EnItem00*, struct GlobalContext*); + +typedef struct EnItem00 { + /* 0x000 */ Actor actor; + /* 0x14C */ EnItem00ActionFunc actionFunc; + /* 0x150 */ s16 collectibleFlag; + /* 0x152 */ s16 getItemId; + /* 0x154 */ s16 unk_154; + /* 0x156 */ s16 unk_156; + /* 0x158 */ s16 unk_158; + /* 0x15A */ s16 unk_15A; + /* 0x15C */ f32 scale; + /* 0x160 */ ColliderCylinder collider; +} EnItem00; // size = 0x1AC + +// Only A_OBJ_SIGNPOST_OBLONG and A_OBJ_SIGNPOST_ARROW are used in room files. +typedef enum { + /* 0x00 */ A_OBJ_BLOCK_SMALL, + /* 0x01 */ A_OBJ_BLOCK_LARGE, + /* 0x02 */ A_OBJ_BLOCK_HUGE, + /* 0x03 */ A_OBJ_BLOCK_SMALL_ROT, + /* 0x04 */ A_OBJ_BLOCK_LARGE_ROT, + /* 0x05 */ A_OBJ_CUBE_SMALL, + /* 0x06 */ A_OBJ_UNKNOWN_6, + /* 0x07 */ A_OBJ_GRASS_CLUMP, + /* 0x08 */ A_OBJ_TREE_STUMP, + /* 0x09 */ A_OBJ_SIGNPOST_OBLONG, + /* 0x0A */ A_OBJ_SIGNPOST_ARROW, + /* 0x0B */ A_OBJ_BOULDER_FRAGMENT, + /* 0x0C */ A_OBJ_MAX +} AObjType; + +struct EnAObj; + +typedef void (*EnAObjActionFunc)(struct EnAObj*, struct GlobalContext*); + +typedef struct EnAObj { + /* 0x000 */ DynaPolyActor dyna; + /* 0x164 */ EnAObjActionFunc actionFunc; + /* 0x168 */ s32 rotateWaitTimer; + /* 0x16C */ s16 textId; + /* 0x16E */ s16 rotateState; + /* 0x170 */ s16 rotateForTimer; + /* 0x172 */ s16 rotSpeedY; + /* 0x174 */ s16 rotSpeedX; + /* 0x178 */ f32 focusYoffset; + /* 0x17C */ ColliderCylinder collider; +} EnAObj; // size = 0x1C8 + +typedef enum { + /* 0x00 */ ACTORCAT_SWITCH, + /* 0x01 */ ACTORCAT_BG, + /* 0x02 */ ACTORCAT_PLAYER, + /* 0x03 */ ACTORCAT_EXPLOSIVE, + /* 0x04 */ ACTORCAT_NPC, + /* 0x05 */ ACTORCAT_ENEMY, + /* 0x06 */ ACTORCAT_PROP, + /* 0x07 */ ACTORCAT_ITEMACTION, + /* 0x08 */ ACTORCAT_MISC, + /* 0x09 */ ACTORCAT_BOSS, + /* 0x0A */ ACTORCAT_DOOR, + /* 0x0B */ ACTORCAT_CHEST +} ActorCategory; + +//#define DEFINE_ACTOR(_0, enum, _2) enum, +#define DEFINE_ACTOR_INTERNAL(_0, enum, _2) enum, +#define DEFINE_ACTOR_UNSET(enum) enum, +#define DEFINE_ACTOR(_0, enum, _2) DEFINE_ACTOR_INTERNAL(_0, enum, _2) + +typedef enum { + #include "tables/actor_table.h" + /* 0x0192 */ ACTOR_ID_MAX // originally "ACTOR_DLF_MAX" +} ActorID; + +#undef DEFINE_ACTOR +#undef DEFINE_ACTOR_INTERNAL +#undef DEFINE_ACTOR_UNSET + +typedef enum { + DOORLOCK_NORMAL, + DOORLOCK_BOSS, + DOORLOCK_NORMAL_SPIRIT +} DoorLockType; + +#endif diff --git a/soh/include/z64animation.h b/soh/include/z64animation.h new file mode 100755 index 000000000..899d389cc --- /dev/null +++ b/soh/include/z64animation.h @@ -0,0 +1,266 @@ +#ifndef Z64_ANIMATION_H +#define Z64_ANIMATION_H + +#include "ultra64.h" +#include "z64dma.h" +#include "z64math.h" + +struct GlobalContext; +struct Actor; +struct SkelAnime; + +#define LINK_ANIMATION_OFFSET(addr, offset) \ + (((uintptr_t)_link_animetionSegmentRomStart) + ((uintptr_t)addr) - ((uintptr_t)_link_animetionSegmentStart) + ((uintptr_t)offset)) +#define LIMB_DONE 0xFF +#define ANIMATION_ENTRY_MAX 50 +#define ANIM_FLAG_UPDATEY (1 << 1) +#define ANIM_FLAG_NOMOVE (1 << 4) + +typedef enum { + /* 0 */ ANIMMODE_LOOP, + /* 1 */ ANIMMODE_LOOP_INTERP, + /* 2 */ ANIMMODE_ONCE, + /* 3 */ ANIMMODE_ONCE_INTERP, + /* 4 */ ANIMMODE_LOOP_PARTIAL, + /* 5 */ ANIMMODE_LOOP_PARTIAL_INTERP +} AnimationModes; + +typedef enum { + /* -1 */ ANIMTAPER_DECEL = -1, + /* 0 */ ANIMTAPER_NONE, + /* 1 */ ANIMTAPER_ACCEL +} AnimationTapers; + +typedef struct { + /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent + /* 0x06 */ u8 child; + /* 0x07 */ u8 sibling; + /* 0x08 */ Gfx* dList; +} StandardLimb; // size = 0xC + +typedef struct { + /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent + /* 0x06 */ u8 child; + /* 0x07 */ u8 sibling; + /* 0x08 */ Gfx* dLists[2]; // Near and far +} LodLimb; // size = 0x10 + +typedef struct LegacyLimb { + /* 0x000 */ Gfx* dList; + /* 0x004 */ Vec3f trans; + /* 0x010 */ Vec3s rot; + /* 0x018 */ struct LegacyLimb* sibling; + /* 0x01C */ struct LegacyLimb* child; +} LegacyLimb; // size = 0x20 + +// Model has limbs with only rigid meshes +typedef struct { + /* 0x00 */ void** segment; + /* 0x04 */ u8 limbCount; +} SkeletonHeader; // size = 0x8 + +// Model has limbs with flexible meshes +typedef struct { + /* 0x00 */ SkeletonHeader sh; + /* 0x08 */ u8 dListCount; +} FlexSkeletonHeader; // size = 0xC + +// Index into the frame data table. +typedef struct { + /* 0x00 */ u16 x; + /* 0x02 */ u16 y; + /* 0x04 */ u16 z; +} JointIndex; // size = 0x06 + +typedef struct { + /* 0x00 */ s16 frameCount; +} AnimationHeaderCommon; + +typedef struct { + /* 0x00 */ AnimationHeaderCommon common; + /* 0x04 */ void* segment; +} LinkAnimationHeader; // size = 0x8 + +typedef struct { + /* 0x00 */ AnimationHeaderCommon common; + /* 0x04 */ s16* frameData; // "tbl" + /* 0x08 */ JointIndex* jointIndices; // "ref_tbl" + /* 0x0C */ u16 staticIndexMax; +} AnimationHeader; // size = 0x10 + +// Unused +typedef struct { + /* 0x00 */ s16 xMax; + /* 0x02 */ s16 x; + /* 0x04 */ s16 yMax; + /* 0x06 */ s16 y; + /* 0x08 */ s16 zMax; + /* 0x0A */ s16 z; +} JointKey; // size = 0x0C + +// Unused +typedef struct { + /* 0x00 */ s16 frameCount; + /* 0x02 */ s16 limbCount; + /* 0x04 */ s16* frameData; + /* 0x08 */ JointKey* jointKey; +} LegacyAnimationHeader; // size = 0xC + +typedef s32 (*OverrideLimbDrawOpa)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void*); + +typedef void (*PostLimbDrawOpa)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void*); + +typedef s32 (*OverrideLimbDraw)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void*, Gfx** gfx); + +typedef void (*PostLimbDraw)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void*, Gfx** gfx); + +typedef enum { + ANIMENTRY_LOADFRAME, + ANIMENTRY_COPYALL, + ANIMENTRY_INTERP, + ANIMENTRY_COPYTRUE, + ANIMENTRY_COPYFALSE, + ANIMENTRY_MOVEACTOR +} AnimationType; + +typedef struct { + /* 0x000 */ DmaRequest req; + /* 0x020 */ OSMesgQueue msgQueue; + /* 0x038 */ OSMesg msg; +} AnimEntryLoadFrame; // size = 0x3C + +typedef struct { + /* 0x000 */ u8 queueFlag; + /* 0x001 */ u8 vecCount; + /* 0x004 */ Vec3s* dst; + /* 0x008 */ Vec3s* src; +} AnimEntryCopyAll; // size = 0xC + +typedef struct { + /* 0x000 */ u8 queueFlag; + /* 0x001 */ u8 vecCount; + /* 0x004 */ Vec3s* base; + /* 0x008 */ Vec3s* mod; + /* 0x00C */ f32 weight; +} AnimEntryInterp; // size = 0x10 + +typedef struct { + /* 0x000 */ u8 queueFlag; + /* 0x001 */ u8 vecCount; + /* 0x004 */ Vec3s* dst; + /* 0x008 */ Vec3s* src; + /* 0x00C */ u8* copyFlag; +} AnimEntryCopyTrue; // size = 0x10 + +typedef struct { + /* 0x000 */ u8 queueFlag; + /* 0x001 */ u8 vecCount; + /* 0x004 */ Vec3s* dst; + /* 0x008 */ Vec3s* src; + /* 0x00C */ u8* copyFlag; +} AnimEntryCopyFalse; // size = 0x10 + +typedef struct { + /* 0x000 */ struct Actor* actor; + /* 0x004 */ struct SkelAnime* skelAnime; + /* 0x008 */ f32 unk_08; +} AnimEntryMoveActor; // size = 0xC + +typedef union { + AnimEntryLoadFrame load; + AnimEntryCopyAll copy; + AnimEntryInterp interp; + AnimEntryCopyTrue copy1; + AnimEntryCopyFalse copy0; + AnimEntryMoveActor move; +} AnimationEntryData; // size = 0x3C + +typedef struct { + /* 0x00 */ u8 type; + /* 0x04 */ AnimationEntryData data; +} AnimationEntry; // size = 0x40 + +typedef struct AnimationContext { + s16 animationCount; + AnimationEntry entries[ANIMATION_ENTRY_MAX]; +} AnimationContext; // size = 0xC84 + +typedef void (*AnimationEntryCallback)(struct GlobalContext* globalCtx, AnimationEntryData* data); + +// fcurve_skelanime structs +typedef struct { + /* 0x0000 */ u16 unk_00; // appears to be flags + /* 0x0002 */ s16 unk_02; + /* 0x0004 */ s16 unk_04; + /* 0x0006 */ s16 unk_06; + /* 0x0008 */ f32 unk_08; +} TransformData; // size = 0xC + +typedef struct { + /* 0x0000 */ u8* refIndex; + /* 0x0004 */ TransformData* transformData; + /* 0x0008 */ s16* copyValues; + /* 0x000C */ s16 unk_0C; + /* 0x000E */ s16 unk_0E; +} TransformUpdateIndex; // size = 0x10 + +typedef struct { + /* 0x0000 */ u8 firstChildIdx; + /* 0x0001 */ u8 nextLimbIdx; + /* 0x0004 */ Gfx* dList[2]; +} SkelCurveLimb; // size = 0xC + +typedef struct { + /* 0x0000 */ SkelCurveLimb** limbs; + /* 0x0004 */ u8 limbCount; +} SkelCurveLimbList; // size = 0x8 + +typedef struct { + /* 0x0000 */ Vec3s scale; + /* 0x0006 */ Vec3s rot; + /* 0x000C */ Vec3s pos; +} LimbTransform; // size = 0x12 + +typedef struct { + /* 0x0000 */ u8 limbCount; + /* 0x0004 */ SkelCurveLimb** limbList; + /* 0x0008 */ TransformUpdateIndex* transUpdIdx; + /* 0x000C */ f32 unk_0C; // seems to be unused + /* 0x0010 */ f32 animFinalFrame; + /* 0x0014 */ f32 animSpeed; + /* 0x0018 */ f32 animCurFrame; + /* 0x001C */ LimbTransform* transforms; +} SkelAnimeCurve; // size = 0x20 + +typedef s32 (*OverrideCurveLimbDraw)(struct GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void*); +typedef void (*PostCurveLimbDraw)(struct GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void*); + +typedef s32 (*AnimUpdateFunc)(); + +typedef struct SkelAnime { + /* 0x00 */ u8 limbCount; // Number of limbs in the skeleton + /* 0x01 */ u8 mode; // 0: loop, 2: play once, 4: partial loop. +1 to interpolate between frames. + /* 0x02 */ u8 dListCount; // Number of display lists in a flexible skeleton + /* 0x03 */ s8 taper; // Tapering to use when morphing between animations. Only used by Door_Warp1. + /* 0x04 */ void** skeleton; // An array of pointers to limbs. Can be StandardLimb, LodLimb, or SkinLimb. + /* 0x08 */ void* animation; // Can be an AnimationHeader or LinkAnimationHeader. + /* 0x0C */ f32 startFrame; // In mode 4, start of partial loop. + /* 0x10 */ f32 endFrame; // In mode 2, Update returns true when curFrame is equal to this. In mode 4, end of partial loop. + /* 0x14 */ f32 animLength; // Total number of frames in the current animation's file. + /* 0x18 */ f32 curFrame; // Current frame in the animation + /* 0x1C */ f32 playSpeed; // Multiplied by R_UPDATE_RATE / 3 to get the animation's frame rate. + /* 0x20 */ Vec3s* jointTable; // Current translation of model and rotations of all limbs + /* 0x24 */ Vec3s* morphTable; // Table of values used to morph between animations + /* 0x28 */ f32 morphWeight; // Weight of the current animation morph as a fraction in [0,1] + /* 0x2C */ f32 morphRate; // Reciprocal of the number of frames in the morph + /* 0x30 */ s32 (*update)(); // Can be Loop, Partial loop, Play once, Morph, or Tapered morph. Link only has Loop, Play once, and Morph + /* 0x34 */ s8 initFlags; // Flags used when initializing Link's skeleton + /* 0x35 */ u8 moveFlags; // Flags used for animations that move the actor in worldspace. + /* 0x36 */ s16 prevRot; // Previous rotation in worldspace. + /* 0x38 */ Vec3s prevTransl; // Previous modelspace translation. + /* 0x3E */ Vec3s baseTransl; // Base modelspace translation. +} SkelAnime; // size = 0x44 + +#endif diff --git a/soh/include/z64audio.h b/soh/include/z64audio.h new file mode 100644 index 000000000..78f262dbf --- /dev/null +++ b/soh/include/z64audio.h @@ -0,0 +1,1074 @@ +#ifndef Z64_AUDIO_H +#define Z64_AUDIO_H + +#define MK_CMD(b0,b1,b2,b3) ((((b0) & 0xFF) << 0x18) | (((b1) & 0xFF) << 0x10) | (((b2) & 0xFF) << 0x8) | (((b3) & 0xFF) << 0)) + +#define NO_LAYER ((SequenceLayer*)(-1)) + +#define TATUMS_PER_BEAT 48 + +#define IS_SEQUENCE_CHANNEL_VALID(ptr) ((uintptr_t)(ptr) != (uintptr_t)&gAudioContext.sequenceChannelNone) + +#define MAX_CHANNELS_PER_BANK 3 + +#define ADSR_DISABLE 0 +#define ADSR_HANG -1 +#define ADSR_GOTO -2 +#define ADSR_RESTART -3 + +#define AIBUF_LEN 0x580 + +typedef enum { + /* 0 */ ADSR_STATE_DISABLED, + /* 1 */ ADSR_STATE_INITIAL, + /* 2 */ ADSR_STATE_START_LOOP, + /* 3 */ ADSR_STATE_LOOP, + /* 4 */ ADSR_STATE_FADE, + /* 5 */ ADSR_STATE_HANG, + /* 6 */ ADSR_STATE_DECAY, + /* 7 */ ADSR_STATE_RELEASE, + /* 8 */ ADSR_STATE_SUSTAIN +} AdsrStatus; + +typedef enum { + /* 0 */ MEDIUM_RAM, + /* 1 */ MEDIUM_UNK, + /* 2 */ MEDIUM_CART, + /* 3 */ MEDIUM_DISK_DRIVE +} SampleMedium; + +typedef enum { + /* 0 */ CODEC_ADPCM, + /* 1 */ CODEC_S8, + /* 2 */ CODEC_S16_INMEMORY, + /* 3 */ CODEC_SMALL_ADPCM, + /* 4 */ CODEC_REVERB, + /* 5 */ CODEC_S16 +} SampleCodec; + +typedef enum { + /* 0 */ SEQUENCE_TABLE, + /* 1 */ FONT_TABLE, + /* 2 */ SAMPLE_TABLE +} SampleBankTableType; + +typedef enum { + /* 0 */ CACHE_TEMPORARY, + /* 1 */ CACHE_PERSISTENT, + /* 2 */ CACHE_EITHER, + /* 3 */ CACHE_PERMANENT +} AudioCacheType; + +typedef s32 (*DmaHandler)(OSPiHandle* handle, OSIoMesg* mb, s32 direction); + +struct Note; +struct NotePool; +struct SequenceChannel; +struct SequenceLayer; + +typedef struct AudioListItem { + // A node in a circularly linked list. Each node is either a head or an item: + // - Items can be either detached (prev = NULL), or attached to a list. + // 'value' points to something of interest. + // - List heads are always attached; if a list is empty, its head points + // to itself. 'count' contains the size of the list. + // If the list holds notes, 'pool' points back to the pool where it lives. + // Otherwise, that member is NULL. + /* 0x00 */ struct AudioListItem* prev; + /* 0x04 */ struct AudioListItem* next; + /* 0x08 */ union { + void* value; // either Note* or SequenceLayer* + s32 count; + } u; + /* 0x0C */ struct NotePool* pool; +} AudioListItem; // size = 0x10 + +typedef struct NotePool { + /* 0x00 */ AudioListItem disabled; + /* 0x10 */ AudioListItem decaying; + /* 0x20 */ AudioListItem releasing; + /* 0x30 */ AudioListItem active; +} NotePool; + +// Pitch sliding by up to one octave in the positive direction. Negative +// direction is "supported" by setting extent to be negative. The code +// exterpolates exponentially in the wrong direction in that case, but that +// doesn't prevent seqplayer from doing it, AFAICT. +typedef struct { + /* 0x00 */ u8 mode; // bit 0x80 denotes something; the rest are an index 0-5 + /* 0x02 */ u16 cur; + /* 0x04 */ u16 speed; + /* 0x08 */ f32 extent; +} Portamento; // size = 0xC + +typedef struct { + /* 0x0 */ s16 delay; + /* 0x2 */ s16 arg; +} AdsrEnvelope; // size = 0x4 + +typedef struct { + /* 0x00 */ u32 start; + /* 0x04 */ u32 end; + /* 0x08 */ u32 count; + /* 0x0C */ char unk_0C[0x4]; + /* 0x10 */ s16 state[16]; // only exists if count != 0. 8-byte aligned +} AdpcmLoop; // size = 0x30 (or 0x10) + +typedef struct { + /* 0x00 */ s32 order; + /* 0x04 */ s32 npredictors; + /* 0x08 */ s16 book[1]; // size 8 * order * npredictors. 8-byte aligned +} AdpcmBook; // size >= 0x8 + +typedef struct { + union { + struct { + /* 0x00 */ u32 codec : 4; + /* 0x00 */ u32 medium : 2; + /* 0x00 */ u32 unk_bit26 : 1; + /* 0x00 */ u32 unk_bit25 : 1; + /* 0x01 */ u32 size : 24; + }; + u32 asU32; + }; + + /* 0x04 */ u8* sampleAddr; + /* 0x08 */ AdpcmLoop* loop; + /* 0x0C */ AdpcmBook* book; +} SoundFontSample; // size = 0x10 + +typedef struct { + /* 0x00 */ SoundFontSample* sample; + /* 0x04 */ union { + u32 tuningAsU32; + f32 tuning;// frequency scale factor + }; +} SoundFontSound; // size = 0x8 + +typedef struct { + /* 0x00 */ s16 numSamplesAfterDownsampling; // never read + /* 0x02 */ s16 chunkLen; // never read + /* 0x04 */ s16* toDownsampleLeft; + /* 0x08 */ s16* toDownsampleRight; // data pointed to by left and right are adjacent in memory + /* 0x0C */ s32 startPos; // start pos in ring buffer + /* 0x10 */ s16 lengthA; // first length in ring buffer (from startPos, at most until end) + /* 0x12 */ s16 lengthB; // second length in ring buffer (from pos 0) + /* 0x14 */ u16 unk_14; + /* 0x16 */ u16 unk_16; + /* 0x18 */ u16 unk_18; +} ReverbRingBufferItem; // size = 0x1C + +typedef struct { + /* 0x000 */ u8 resampleFlags; + /* 0x001 */ u8 useReverb; + /* 0x002 */ u8 framesToIgnore; + /* 0x003 */ u8 curFrame; + /* 0x004 */ u8 downsampleRate; + /* 0x005 */ s8 unk_05; + /* 0x006 */ u16 windowSize; + /* 0x008 */ s16 unk_08; + /* 0x00A */ s16 unk_0A; + /* 0x00C */ u16 unk_0C; + /* 0x00E */ u16 unk_0E; + /* 0x010 */ s16 leakRtl; + /* 0x012 */ s16 leakLtr; + /* 0x014 */ u16 unk_14; + /* 0x016 */ s16 unk_16; + /* 0x018 */ u8 unk_18; + /* 0x019 */ u8 unk_19; + /* 0x01A */ u8 unk_1A; + /* 0x01B */ u8 unk_1B; + /* 0x01C */ s32 nextRingBufPos; + /* 0x020 */ s32 unk_20; + /* 0x024 */ s32 bufSizePerChan; + /* 0x028 */ s16* leftRingBuf; + /* 0x02C */ s16* rightRingBuf; + /* 0x030 */ void* unk_30; + /* 0x034 */ void* unk_34; + /* 0x038 */ void* unk_38; + /* 0x03C */ void* unk_3C; + /* 0x040 */ ReverbRingBufferItem items[2][5]; + /* 0x158 */ ReverbRingBufferItem items2[2][5]; + /* 0x270 */ s16* filterLeft; + /* 0x274 */ s16* filterRight; + /* 0x278 */ s16* filterLeftState; + /* 0x27C */ s16* filterRightState; + /* 0x280 */ SoundFontSound sound; + /* 0x288 */ SoundFontSample sample; + /* 0x298 */ AdpcmLoop loop; +} SynthesisReverb; // size = 0x2C8 + +typedef struct { + /* 0x00 */ u8 loaded; + /* 0x01 */ u8 normalRangeLo; + /* 0x02 */ u8 normalRangeHi; + /* 0x03 */ u8 releaseRate; + /* 0x04 */ AdsrEnvelope* envelope; + /* 0x08 */ SoundFontSound lowNotesSound; + /* 0x10 */ SoundFontSound normalNotesSound; + /* 0x18 */ SoundFontSound highNotesSound; +} Instrument; // size = 0x20 + +typedef struct { + /* 0x00 */ u8 releaseRate; + /* 0x01 */ u8 pan; + /* 0x02 */ u8 loaded; + /* 0x04 */ SoundFontSound sound; + /* 0x14 */ AdsrEnvelope* envelope; +} Drum; // size = 0x14 + +typedef struct { + /* 0x00 */ u8 numInstruments; + /* 0x01 */ u8 numDrums; + /* 0x02 */ u8 sampleBankId1; + /* 0x03 */ u8 sampleBankId2; + /* 0x04 */ u16 numSfx; + /* 0x08 */ Instrument** instruments; + /* 0x0C */ Drum** drums; + /* 0x10 */ SoundFontSound* soundEffects; +} SoundFont; // size = 0x14 + +typedef struct { + /* 0x00 */ u8* pc; + /* 0x04 */ u8* stack[4]; + /* 0x14 */ u8 remLoopIters[4]; + /* 0x18 */ u8 depth; + /* 0x19 */ s8 value; +} SeqScriptState; // size = 0x1C + +// Also known as a Group, according to debug strings. +typedef struct { + /* 0x000 */ u8 enabled : 1; + /* 0x000 */ u8 finished : 1; + /* 0x000 */ u8 muted : 1; + /* 0x000 */ u8 seqDmaInProgress : 1; + /* 0x000 */ u8 fontDmaInProgress : 1; + /* 0x000 */ u8 recalculateVolume : 1; + /* 0x000 */ u8 stopScript : 1; + /* 0x000 */ u8 unk_0b1 : 1; + /* 0x001 */ u8 state; + /* 0x002 */ u8 noteAllocPolicy; + /* 0x003 */ u8 muteBehavior; + /* 0x004 */ u8 seqId; + /* 0x005 */ u8 defaultFont; + /* 0x006 */ u8 unk_06[1]; + /* 0x007 */ s8 playerIdx; + /* 0x008 */ u16 tempo; // tatums per minute + /* 0x00A */ u16 tempoAcc; + /* 0x00C */ u16 unk_0C; + /* 0x00E */ s16 transposition; + /* 0x010 */ u16 delay; + /* 0x012 */ u16 fadeTimer; + /* 0x014 */ u16 fadeTimerUnkEu; + /* 0x018 */ u8* seqData; + /* 0x01C */ f32 fadeVolume; + /* 0x020 */ f32 fadeVelocity; + /* 0x024 */ f32 volume; + /* 0x028 */ f32 muteVolumeScale; + /* 0x02C */ f32 fadeVolumeScale; + f32 gameVolume; + /* 0x030 */ f32 appliedFadeVolume; + /* 0x034 */ f32 unk_34; + /* 0x038 */ struct SequenceChannel* channels[16]; + /* 0x078 */ SeqScriptState scriptState; + /* 0x094 */ u8* shortNoteVelocityTable; + /* 0x098 */ u8* shortNoteGateTimeTable; + /* 0x09C */ NotePool notePool; + /* 0x0DC */ s32 skipTicks; + /* 0x0E0 */ u32 scriptCounter; + /* 0x0E4 */ char unk_E4[0x74]; // unused struct members for sequence/sound font dma management, according to sm64 decomp + /* 0x158 */ s8 soundScriptIO[8]; +} SequencePlayer; // size = 0x160 + +typedef struct { + /* 0x0 */ u8 releaseRate; + /* 0x1 */ u8 sustain; + /* 0x4 */ AdsrEnvelope* envelope; +} AdsrSettings; // size = 0x8 + +typedef struct { + /* 0x00 */ union { + struct A { + /* 0x00 */ u8 unk_0b80 : 1; + /* 0x00 */ u8 hang : 1; + /* 0x00 */ u8 decay : 1; + /* 0x00 */ u8 release : 1; + /* 0x00 */ u8 state : 4; + } s; + /* 0x00 */ u8 asByte; + } action; + /* 0x01 */ u8 envIndex; + /* 0x02 */ s16 delay; + /* 0x04 */ f32 sustain; + /* 0x08 */ f32 velocity; + /* 0x0C */ f32 fadeOutVel; + /* 0x10 */ f32 current; + /* 0x14 */ f32 target; + /* 0x18 */ char unk_18[4]; + /* 0x1C */ AdsrEnvelope* envelope; +} AdsrState; + +typedef struct { + /* 0x00 */ u8 unused : 2; + /* 0x00 */ u8 bit2 : 2; + /* 0x00 */ u8 strongRight : 1; + /* 0x00 */ u8 strongLeft : 1; + /* 0x00 */ u8 stereoHeadsetEffects : 1; + /* 0x00 */ u8 usesHeadsetPanEffects : 1; +} StereoData; + +typedef union { + /* 0x00 */ StereoData s; + /* 0x00 */ u8 asByte; +} Stereo; + +typedef struct { + /* 0x00 */ u8 reverb; + /* 0x01 */ u8 unk_1; + /* 0x02 */ u8 pan; + /* 0x03 */ Stereo stereo; + /* 0x04 */ u8 unk_4; + /* 0x06 */ u16 unk_6; + /* 0x08 */ f32 freqScale; + /* 0x0C */ f32 velocity; + /* 0x10 */ s16* filter; + /* 0x14 */ s16 filterBuf[8]; +} NoteAttributes; // size = 0x24 + +// Also known as a SubTrack, according to sm64 debug strings. +typedef struct SequenceChannel { + /* 0x00 */ u8 enabled : 1; + /* 0x00 */ u8 finished : 1; + /* 0x00 */ u8 stopScript : 1; + /* 0x00 */ u8 stopSomething2 : 1; // sets SequenceLayer.stopSomething + /* 0x00 */ u8 hasInstrument : 1; + /* 0x00 */ u8 stereoHeadsetEffects : 1; + /* 0x00 */ u8 largeNotes : 1; // notes specify duration and velocity + /* 0x00 */ u8 unused : 1; + union { + struct { + /* 0x01 */ u8 freqScale : 1; + /* 0x01 */ u8 volume : 1; + /* 0x01 */ u8 pan : 1; + } s; + /* 0x01 */ u8 asByte; + } changes; + /* 0x02 */ u8 noteAllocPolicy; + /* 0x03 */ u8 muteBehavior; + /* 0x04 */ u8 reverb; // or dry/wet mix + /* 0x05 */ u8 notePriority; // 0-3 + /* 0x06 */ u8 someOtherPriority; + /* 0x07 */ u8 fontId; + /* 0x08 */ u8 reverbIndex; + /* 0x09 */ u8 bookOffset; + /* 0x0A */ u8 newPan; + /* 0x0B */ u8 panChannelWeight; // proportion of pan that comes from the channel (0..128) + /* 0x0C */ u8 unk_0C; + /* 0x0D */ u8 velocityRandomVariance; + /* 0x0E */ u8 gateTimeRandomVariance; + /* 0x0F */ u8 unk_0F; + /* 0x10 */ u16 vibratoRateStart; + /* 0x12 */ u16 vibratoExtentStart; + /* 0x14 */ u16 vibratoRateTarget; + /* 0x16 */ u16 vibratoExtentTarget; + /* 0x18 */ u16 vibratoRateChangeDelay; + /* 0x1A */ u16 vibratoExtentChangeDelay; + /* 0x1C */ u16 vibratoDelay; + /* 0x1E */ u16 delay; + /* 0x20 */ u16 unk_20; + /* 0x22 */ u16 unk_22; + /* 0x24 */ s16 instOrWave; // either 0 (none), instrument index + 1, or + // 0x80..0x83 for sawtooth/triangle/sine/square waves. + /* 0x26 */ s16 transposition; + /* 0x28 */ f32 volumeScale; + /* 0x2C */ f32 volume; + /* 0x30 */ s32 pan; + /* 0x34 */ f32 appliedVolume; + /* 0x38 */ f32 freqScale; + /* 0x3C */ u8 (*dynTable)[][2]; + /* 0x40 */ struct Note* noteUnused; + /* 0x44 */ struct SequenceLayer* layerUnused; + /* 0x48 */ Instrument* instrument; + /* 0x4C */ SequencePlayer* seqPlayer; + /* 0x50 */ struct SequenceLayer* layers[4]; + /* 0x60 */ SeqScriptState scriptState; + /* 0x7C */ AdsrSettings adsr; + /* 0x84 */ NotePool notePool; + /* 0xC4 */ s8 soundScriptIO[8]; // bridge between sound script and audio lib, "io ports" + /* 0xCC */ s16* filter; + /* 0xD0 */ Stereo stereo; +} SequenceChannel; // size = 0xD4 + +// Might also be known as a Track, according to sm64 debug strings (?). +typedef struct SequenceLayer { + /* 0x00 */ u8 enabled : 1; + /* 0x00 */ u8 finished : 1; + /* 0x00 */ u8 stopSomething : 1; + /* 0x00 */ u8 continuousNotes : 1; // keep the same note for consecutive notes with the same sound + /* 0x00 */ u8 bit3 : 1; // "loaded"? + /* 0x00 */ u8 ignoreDrumPan : 1; + /* 0x00 */ u8 bit1 : 1; // "has initialized continuous notes"? + /* 0x00 */ u8 notePropertiesNeedInit : 1; + /* 0x01 */ Stereo stereo; + /* 0x02 */ u8 instOrWave; + /* 0x03 */ u8 gateTime; + /* 0x04 */ u8 semitone; + /* 0x05 */ u8 portamentoTargetNote; + /* 0x06 */ u8 pan; // 0..128 + /* 0x07 */ u8 notePan; + /* 0x08 */ s16 delay; + /* 0x0A */ s16 gateDelay; + /* 0x0C */ s16 delay2; + /* 0x0E */ u16 portamentoTime; + /* 0x10 */ s16 transposition; // #semitones added to play commands + // (seq instruction encoding only allows referring to the limited range + // 0..0x3F; this makes 0x40..0x7F accessible as well) + /* 0x12 */ s16 shortNoteDefaultDelay; + /* 0x14 */ s16 lastDelay; + /* 0x18 */ AdsrSettings adsr; + /* 0x20 */ Portamento portamento; + /* 0x2C */ struct Note* note; + /* 0x30 */ f32 freqScale; + /* 0x34 */ f32 unk_34; + /* 0x38 */ f32 velocitySquare2; + /* 0x3C */ f32 velocitySquare; // not sure which one of those corresponds to the sm64 original + /* 0x40 */ f32 noteVelocity; + /* 0x44 */ f32 noteFreqScale; + /* 0x48 */ Instrument* instrument; + /* 0x4C */ SoundFontSound* sound; + /* 0x50 */ SequenceChannel* channel; + /* 0x54 */ SeqScriptState scriptState; + /* 0x70 */ AudioListItem listItem; +} SequenceLayer; // size = 0x80 + +typedef struct { + /* 0x0000 */ s16 adpcmdecState[0x10]; + /* 0x0020 */ s16 finalResampleState[0x10]; + /* 0x0040 */ s16 mixEnvelopeState[0x28]; + /* 0x0090 */ s16 panResampleState[0x10]; + /* 0x00B0 */ s16 panSamplesBuffer[0x20]; + /* 0x00F0 */ s16 dummyResampleState[0x10]; +} NoteSynthesisBuffers; // size = 0x110 + +typedef struct { + /* 0x00 */ u8 restart; + /* 0x01 */ u8 sampleDmaIndex; + /* 0x02 */ u8 prevHeadsetPanRight; + /* 0x03 */ u8 prevHeadsetPanLeft; + /* 0x04 */ u8 reverbVol; + /* 0x05 */ u8 numParts; + /* 0x06 */ u16 samplePosFrac; + /* 0x08 */ s32 samplePosInt; + /* 0x0C */ NoteSynthesisBuffers* synthesisBuffers; + /* 0x10 */ s16 curVolLeft; + /* 0x12 */ s16 curVolRight; + /* 0x14 */ u16 unk_14; + /* 0x16 */ u16 unk_16; + /* 0x18 */ u16 unk_18; + /* 0x1A */ u8 unk_1A; + /* 0x1C */ u16 unk_1C; + /* 0x1E */ u16 unk_1E; +} NoteSynthesisState; // size = 0x20 + +typedef struct { + /* 0x00 */ struct SequenceChannel* channel; + /* 0x04 */ u32 time; + /* 0x08 */ s16* curve; + /* 0x0C */ f32 extent; + /* 0x10 */ f32 rate; + /* 0x14 */ u8 active; + /* 0x16 */ u16 rateChangeTimer; + /* 0x18 */ u16 extentChangeTimer; + /* 0x1A */ u16 delay; +} VibratoState; // size = 0x1C + +typedef struct { + /* 0x00 */ u8 priority; + /* 0x01 */ u8 waveId; + /* 0x02 */ u8 sampleCountIndex; + /* 0x03 */ u8 fontId; + /* 0x04 */ u8 unk_04; + /* 0x05 */ u8 stereoHeadsetEffects; + /* 0x06 */ s16 adsrVolScaleUnused; + /* 0x08 */ f32 portamentoFreqScale; + /* 0x0C */ f32 vibratoFreqScale; + /* 0x10 */ SequenceLayer* prevParentLayer; + /* 0x14 */ SequenceLayer* parentLayer; + /* 0x18 */ SequenceLayer* wantedParentLayer; + /* 0x1C */ NoteAttributes attributes; + /* 0x40 */ AdsrState adsr; + // may contain portamento, vibratoState, if those are not part of Note itself +} NotePlaybackState; + +typedef struct { + struct { + /* 0x00 */ volatile u8 enabled : 1; + /* 0x00 */ u8 needsInit : 1; + /* 0x00 */ u8 finished : 1; // ? + /* 0x00 */ u8 unused : 1; + /* 0x00 */ u8 stereoStrongRight : 1; + /* 0x00 */ u8 stereoStrongLeft : 1; + /* 0x00 */ u8 stereoHeadsetEffects : 1; + /* 0x00 */ u8 usesHeadsetPanEffects : 1; // ? + } bitField0; + struct { + /* 0x01 */ u8 reverbIndex : 3; + /* 0x01 */ u8 bookOffset : 2; + /* 0x01 */ u8 isSyntheticWave : 1; + /* 0x01 */ u8 hasTwoParts : 1; + /* 0x01 */ u8 usesHeadsetPanEffects2 : 1; + } bitField1; + /* 0x02 */ u8 unk_2; + /* 0x03 */ u8 headsetPanRight; + /* 0x04 */ u8 headsetPanLeft; + /* 0x05 */ u8 reverbVol; + /* 0x06 */ u8 unk_06; + /* 0x07 */ u8 unk_07; + /* 0x08 */ u16 targetVolLeft; + /* 0x0A */ u16 targetVolRight; + /* 0x0C */ u16 resamplingRateFixedPoint; + /* 0x0E */ u16 unk_0E; + /* 0x10 */ union { + SoundFontSound* soundFontSound; + s16* samples; // used for synthetic waves + } sound; + /* 0x14 */ s16* filter; + /* 0x18 */ char pad_18[0x8]; +} NoteSubEu; // size = 0x20 + +typedef struct Note { + /* 0x00 */ AudioListItem listItem; + /* 0x10 */ NoteSynthesisState synthesisState; + /* 0x30 */ NotePlaybackState playbackState; + /* 0x90 */ Portamento portamento; + /* 0x9C */ VibratoState vibratoState; + /* 0xB8 */ char unk_B8[0x4]; + /* 0xBC */ u32 unk_BC; + /* 0xC0 */ NoteSubEu noteSubEu; +} Note; // size = 0xE0 + +typedef struct { + /* 0x00 */ u8 downsampleRate; + /* 0x02 */ u16 windowSize; + /* 0x04 */ u16 unk_4; + /* 0x06 */ u16 unk_6; + /* 0x08 */ u16 unk_8; + /* 0x0A */ u16 unk_A; + /* 0x0C */ u16 leakRtl; + /* 0x0E */ u16 leakLtr; + /* 0x10 */ s8 unk_10; + /* 0x12 */ u16 unk_12; + /* 0x14 */ s16 lowPassFilterCutoffLeft; + /* 0x16 */ s16 lowPassFilterCutoffRight; +} ReverbSettings; // size = 0x18 + +typedef struct { + /* 0x00 */ u32 frequency; + /* 0x04 */ u8 unk_04; + /* 0x05 */ u8 numNotes; + /* 0x06 */ u8 numSequencePlayers; + /* 0x07 */ u8 unk_07; // unused, set to zero + /* 0x08 */ u8 unk_08; // unused, set to zero + /* 0x09 */ u8 numReverbs; + /* 0x0C */ ReverbSettings* reverbSettings; + /* 0x10 */ u16 sampleDmaBufSize1; + /* 0x12 */ u16 sampleDmaBufSize2; + /* 0x14 */ u16 unk_14; + /* 0x18 */ u32 persistentSeqMem; + /* 0x1C */ u32 persistentFontMem; + /* 0x20 */ u32 persistentSampleMem; + /* 0x24 */ u32 temporarySeqMem; + /* 0x28 */ u32 temporaryFontMem; + /* 0x2C */ u32 temporarySampleMem; + /* 0x30 */ s32 persistentSampleCacheMem; + /* 0x34 */ s32 temporarySampleCacheMem; +} AudioSpec; // size = 0x38 + +typedef struct { + /* 0x00 */ s16 specUnk4; + /* 0x02 */ u16 frequency; + /* 0x04 */ u16 aiFrequency; + /* 0x06 */ s16 samplesPerFrameTarget; + /* 0x08 */ s16 maxAiBufferLength; + /* 0x0A */ s16 minAiBufferLength; + /* 0x0C */ s16 updatesPerFrame; + /* 0x0E */ s16 samplesPerUpdate; + /* 0x10 */ s16 samplesPerUpdateMax; + /* 0x12 */ s16 samplesPerUpdateMin; + /* 0x14 */ s16 numSequencePlayers; + /* 0x18 */ f32 resampleRate; + /* 0x1C */ f32 updatesPerFrameInv; + /* 0x20 */ f32 unkUpdatesPerFrameScaled; + /* 0x24 */ f32 unk_24; +} AudioBufferParameters; + +typedef struct { + /* 0x0 */ u8* start; + /* 0x4 */ u8* cur; + /* 0x8 */ ptrdiff_t size; + /* 0xC */ s32 count; +} AudioAllocPool; // size = 0x10 + +typedef struct { + /* 0x0 */ u8* ptr; + /* 0x4 */ size_t size; + /* 0x8 */ s16 tableType; + /* 0xA */ s16 id; +} AudioCacheEntry; // size = 0xC + +typedef struct { + /* 0x00 */ s8 inUse; + /* 0x01 */ s8 origMedium; + /* 0x02 */ s8 sampleBankId; + /* 0x03 */ char unk_03[0x5]; + /* 0x08 */ u8* allocatedAddr; + /* 0x0C */ void* sampleAddr; + /* 0x10 */ size_t size; +} SampleCacheEntry; // size = 0x14 + +typedef struct { + /* 0x000 */ AudioAllocPool pool; + /* 0x010 */ SampleCacheEntry entries[32]; + /* 0x290 */ ptrdiff_t size; +} AudioSampleCache; // size = 0x294 + +typedef struct { + /* 0x00*/ u32 numEntries; + /* 0x04*/ AudioAllocPool pool; + /* 0x14*/ AudioCacheEntry entries[16]; +} AudioPersistentCache; // size = 0xD4 + +typedef struct { + /* 0x00*/ u32 nextSide; + /* 0x04*/ AudioAllocPool pool; + /* 0x14*/ AudioCacheEntry entries[2]; +} AudioTemporaryCache; // size = 0x3C + +typedef struct { + /* 0x000*/ AudioPersistentCache persistent; + /* 0x0D4*/ AudioTemporaryCache temporary; + /* 0x100*/ u8 unk_100[0x10]; +} AudioCache; // size = 0x110 + +typedef struct { + u32 wantPersistent; + u32 wantTemporary; +} AudioPoolSplit2; // size = 0x8 + +typedef struct { + u32 wantSeq; + u32 wantFont; + u32 wantSample; +} AudioPoolSplit3; // size = 0xC + +typedef struct { + u32 wantSeq; + u32 wantFont; + u32 wantSample; + u32 wantCustom; +} AudioPoolSplit4; // size = 0x10 + +typedef struct { + /* 0x00 */ u32 endAndMediumKey; + /* 0x04 */ SoundFontSample* sample; + /* 0x08 */ u8* ramAddr; + /* 0x0C */ u32 encodedInfo; + /* 0x10 */ s32 isFree; +} AudioPreloadReq; // size = 0x14 + +typedef struct { + union{ + u32 opArgs; + struct { + // OTRTODO: struct members swapped for quick audio + u8 arg2; + u8 arg1; + u8 arg0; + u8 op; + }; + }; + union { + void* data; + f32 asFloat; + s32 asInt; + struct { + u8 pad2[2]; + u16 asUShort; + }; + struct { + u8 pad1[3]; + s8 asSbyte; + }; + struct { + u8 pad0[3]; + u8 asUbyte; + }; + u32 asUInt; + }; +} AudioCmd; + +typedef struct { + /* 0x00 */ s8 status; + /* 0x01 */ s8 delay; + /* 0x02 */ s8 medium; + /* 0x04 */ u8* ramAddr; + /* 0x08 */ u32 curDevAddr; + /* 0x0C */ u8* curRamAddr; + /* 0x10 */ u32 bytesRemaining; + /* 0x14 */ u32 chunkSize; + /* 0x18 */ s32 unkMediumParam; + /* 0x1C */ u32 retMsg; + /* 0x20 */ OSMesgQueue* retQueue; + /* 0x24 */ OSMesgQueue msgQueue; + /* 0x3C */ OSMesg msg; + /* 0x40 */ OSIoMesg ioMesg; +} AudioAsyncLoad; // size = 0x58 + +typedef struct { + /* 0x00 */ u8 medium; + /* 0x01 */ u8 seqOrFontId; + /* 0x02 */ u16 instId; + /* 0x04 */ s32 unkMediumParam; + /* 0x08 */ s32 curDevAddr; + /* 0x0C */ u8* curRamAddr; + /* 0x10 */ u8* ramAddr; + /* 0x14 */ s32 status; + /* 0x18 */ s32 bytesRemaining; + /* 0x1C */ s8* isDone; + /* 0x20 */ SoundFontSample sample; + /* 0x30 */ OSMesgQueue msgqueue; + /* 0x48 */ OSMesg msg; + /* 0x4C */ OSIoMesg ioMesg; +} AudioSlowLoad; // size = 0x64 + +typedef struct { + /* 0x00 */ uintptr_t romAddr; + /* 0x04 */ size_t size; + /* 0x08 */ s8 medium; + /* 0x09 */ s8 cachePolicy; + /* 0x0A */ s16 shortData1; + /* 0x0C */ s16 shortData2; + /* 0x0E */ s16 shortData3; +} AudioTableEntry; // size = 0x10 + +typedef struct { + /* 0x00 */ s16 numEntries; + /* 0x02 */ s16 unkMediumParam; + /* 0x04 */ uintptr_t romAddr; + /* 0x08 */ char pad[0x8]; + /* 0x10 */ AudioTableEntry entries[512]; // (dynamic size) +} AudioTable; // size >= 0x20 + +typedef struct { + /* 0x00 */ OSTask task; + /* 0x40 */ OSMesgQueue* taskQueue; + /* 0x44 */ void* unk_44; // probably a message that gets unused. + /* 0x48 */ char unk_48[0x8]; +} AudioTask; // size = 0x50 + +typedef struct { + /* 0x00 */ u8* ramAddr; + /* 0x04 */ u32 devAddr; + /* 0x08 */ u16 sizeUnused; + /* 0x0A */ u16 size; + /* 0x0C */ u8 unused; + /* 0x0D */ u8 reuseIndex; // position in sSampleDmaReuseQueue1/2, if ttl == 0 + /* 0x0E */ u8 ttl; // duration after which the DMA can be discarded +} SampleDma; // size = 0x10 + +typedef struct { + /* 0x0000 */ char unk_0000; + /* 0x0001 */ s8 numSynthesisReverbs; + /* 0x0002 */ u16 unk_2; + /* 0x0004 */ u16 unk_4; + /* 0x0006 */ char unk_0006[0x0A]; + /* 0x0010 */ s16* curLoadedBook; + /* 0x0014 */ NoteSubEu* noteSubsEu; + /* 0x0018 */ SynthesisReverb synthesisReverbs[4]; + /* 0x0B38 */ char unk_0B38[0x30]; + /* 0x0B68 */ SoundFontSample* usedSamples[128]; + /* 0x0D68 */ AudioPreloadReq preloadSampleStack[128]; + /* 0x1768 */ s32 numUsedSamples; + /* 0x176C */ s32 preloadSampleStackTop; + /* 0x1770 */ AudioAsyncLoad asyncLoads[0x10]; + /* 0x1CF0 */ OSMesgQueue asyncLoadUnkMediumQueue; + /* 0x1D08 */ char unk_1D08[0x40]; + /* 0x1D48 */ AudioAsyncLoad* curUnkMediumLoad; + /* 0x1D4C */ u32 slowLoadPos; + /* 0x1D50 */ AudioSlowLoad slowLoads[2]; + /* 0x1E18 */ OSPiHandle* cartHandle; + /* 0x1E1C */ OSPiHandle* driveHandle; + /* 0x1E20 */ OSMesgQueue externalLoadQueue; + /* 0x1E38 */ OSMesg externalLoadMesgBuf[0x10]; + /* 0x1E78 */ OSMesgQueue preloadSampleQueue; + /* 0x1E90 */ OSMesg preloadSampleMesgBuf[0x10]; + /* 0x1ED0 */ OSMesgQueue currAudioFrameDmaQueue; + /* 0x1EE8 */ OSMesg currAudioFrameDmaMesgBuf[0x40]; + /* 0x1FE8 */ OSIoMesg currAudioFrameDmaIoMesgBuf[0x40]; + /* 0x25E8 */ OSMesgQueue syncDmaQueue; + /* 0x2600 */ OSMesg syncDmaMesg; + /* 0x2604 */ OSIoMesg syncDmaIoMesg; + /* 0x261C */ SampleDma* sampleDmas; + /* 0x2620 */ u32 sampleDmaCount; + /* 0x2624 */ u32 sampleDmaListSize1; + /* 0x2628 */ s32 unused2628; + /* 0x262C */ u8 sampleDmaReuseQueue1[0x100]; // read pos <= write pos, wrapping mod 256 + /* 0x272C */ u8 sampleDmaReuseQueue2[0x100]; + /* 0x282C */ u8 sampleDmaReuseQueue1RdPos; + /* 0x282D */ u8 sampleDmaReuseQueue2RdPos; + /* 0x282E */ u8 sampleDmaReuseQueue1WrPos; + /* 0x282F */ u8 sampleDmaReuseQueue2WrPos; + /* 0x2830 */ AudioTable* sequenceTable; + /* 0x2834 */ AudioTable* soundFontTable; + /* 0x2838 */ AudioTable* sampleBankTable; + /* 0x283C */ u8* sequenceFontTable; + /* 0x2840 */ u16 numSequences; + /* 0x2844 */ SoundFont* soundFonts; + /* 0x2848 */ AudioBufferParameters audioBufferParameters; + /* 0x2870 */ f32 unk_2870; + /* 0x2874 */ s32 sampleDmaBufSize1; + /* 0x2874 */ s32 sampleDmaBufSize2; + /* 0x287C */ char unk_287C[0x10]; + /* 0x288C */ s32 sampleDmaBufSize; + /* 0x2890 */ s32 maxAudioCmds; + /* 0x2894 */ s32 numNotes; + /* 0x2898 */ s16 tempoInternalToExternal; + /* 0x289A */ s8 soundMode; + /* 0x289C */ s32 totalTaskCnt; + /* 0x28A0 */ s32 curAudioFrameDmaCount; + /* 0x28A4 */ s32 rspTaskIdx; + /* 0x28A8 */ s32 curAIBufIdx; + /* 0x28AC */ Acmd* abiCmdBufs[2]; + /* 0x28B4 */ Acmd* curAbiCmdBuf; + /* 0x28B8 */ AudioTask* currTask; + /* 0x28BC */ char unk_28BC[0x4]; + /* 0x28C0 */ AudioTask rspTask[2]; + /* 0x2960 */ f32 unk_2960; + /* 0x2964 */ s32 refreshRate; + /* 0x2968 */ s16* aiBuffers[3]; + /* 0x2974 */ s16 aiBufLengths[3]; + /* 0x297C */ u32 audioRandom; + /* 0x2980 */ s32 audioErrorFlags; + /* 0x2984 */ volatile u32 resetTimer; + /* 0x2988 */ char unk_2988[0x8]; + /* 0x2990 */ AudioAllocPool audioSessionPool; + /* 0x29A0 */ AudioAllocPool externalPool; + /* 0x29B0 */ AudioAllocPool audioInitPool; + /* 0x29C0 */ AudioAllocPool notesAndBuffersPool; + /* 0x29D0 */ char unk_29D0[0x20]; // probably two unused pools + /* 0x29F0 */ AudioAllocPool cachePool; + /* 0x2A00 */ AudioAllocPool persistentCommonPool; + /* 0x2A10 */ AudioAllocPool temporaryCommonPool; + /* 0x2A20 */ AudioCache seqCache; + /* 0x2B30 */ AudioCache fontCache; + /* 0x2C40 */ AudioCache sampleBankCache; + /* 0x2D50 */ AudioAllocPool permanentPool; + /* 0x2D60 */ AudioCacheEntry permanentCache[32]; + /* 0x2EE0 */ AudioSampleCache persistentSampleCache; + /* 0x3174 */ AudioSampleCache temporarySampleCache; + /* 0x3408 */ AudioPoolSplit4 sessionPoolSplit; + /* 0x3418 */ AudioPoolSplit2 cachePoolSplit; + /* 0x3420 */ AudioPoolSplit3 persistentCommonPoolSplit; + /* 0x342C */ AudioPoolSplit3 temporaryCommonPoolSplit; + /* 0x3438 */ u8 sampleFontLoadStatus[0x30]; + /* 0x3468 */ u8 fontLoadStatus[0x30]; + /* 0x3498 */ u8 seqLoadStatus[0x80]; + /* 0x3518 */ volatile u8 resetStatus; + /* 0x3519 */ u8 audioResetSpecIdToLoad; + /* 0x351C */ s32 audioResetFadeOutFramesLeft; + /* 0x3520 */ f32* unk_3520; + /* 0x3524 */ u8* audioHeap; + /* 0x3528 */ u32 audioHeapSize; + /* 0x352C */ Note* notes; + /* 0x3530 */ SequencePlayer seqPlayers[4]; + /* 0x3AB0 */ SequenceLayer sequenceLayers[64]; + /* 0x5AB0 */ SequenceChannel sequenceChannelNone; + /* 0x5B84 */ s32 noteSubEuOffset; + /* 0x5B88 */ AudioListItem layerFreeList; + /* 0x5B98 */ NotePool noteFreeLists; + /* 0x5BD8 */ u8 cmdWrPos; + /* 0x5BD9 */ u8 cmdRdPos; + /* 0x5BDA */ u8 cmdQueueFinished; + /* 0x5BDC */ u16 unk_5BDC[4]; + /* 0x5BE4 */ OSMesgQueue* audioResetQueueP; + /* 0x5BE8 */ OSMesgQueue* taskStartQueueP; + /* 0x5BEC */ OSMesgQueue* cmdProcQueueP; + /* 0x5BF0 */ OSMesgQueue taskStartQueue; + /* 0x5C08 */ OSMesgQueue cmdProcQueue; + /* 0x5C20 */ OSMesgQueue audioResetQueue; + /* 0x5C38 */ OSMesg taskStartMsgs[1]; + /* 0x5C3C */ OSMesg audioResetMesgs[1]; + /* 0x5C40 */ OSMesg cmdProcMsgs[4]; + /* 0x5C50 */ AudioCmd cmdBuf[0x100]; +} AudioContext; // size = 0x6450 + +typedef struct { + /* 0x00 */ u8 reverbVol; + /* 0x01 */ u8 unk_1; + /* 0x02 */ u8 pan; + /* 0x03 */ Stereo stereo; + /* 0x04 */ f32 frequency; + /* 0x08 */ f32 velocity; + /* 0x0C */ char unk_0C[0x4]; + /* 0x10 */ s16* filter; + /* 0x14 */ u8 unk_14; + /* 0x16 */ u16 unk_16; +} NoteSubAttributes; // size = 0x18 + +typedef struct { + /* 0x00 */ u32 heapSize; + /* 0x04 */ u32 initPoolSize; + /* 0x08 */ u32 permanentPoolSize; +} AudioContextInitSizes; // size = 0xC + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ u16 unk_0C; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 unk_18; + /* 0x1C */ u16 unk_1C; +} unk_50_s; // size = 0x20 + +typedef struct { + /* 0x000 */ f32 volCur; + /* 0x004 */ f32 volTarget; + /* 0x008 */ f32 unk_08; + /* 0x00C */ u16 unk_0C; + /* 0x00E */ u8 volScales[0x4]; + /* 0x012 */ u8 volFadeTimer; + /* 0x013 */ u8 fadeVolUpdate; + /* 0x014 */ u32 unk_14; + /* 0x018 */ u16 unk_18; + /* 0x01C */ f32 unk_1C; + /* 0x020 */ f32 unk_20; + /* 0x024 */ f32 unk_24; + /* 0x028 */ u16 unk_28; + /* 0x02C */ u32 unk_2C[8]; + /* 0x04C */ u8 unk_4C; + /* 0x04D */ u8 unk_4D; + /* 0x04E */ u8 unk_4E; + /* 0x050 */ unk_50_s unk_50[0x10]; + /* 0x250 */ u16 unk_250; + /* 0x252 */ u16 unk_252; + /* 0x254 */ u16 unk_254; + /* 0x256 */ u16 unk_256; + /* 0x258 */ u16 unk_258; + /* 0x25C */ u32 unk_25C; + /* 0x260 */ u8 unk_260; +} unk_D_8016E750; // size = 0x264 + +typedef enum { + /* 0 */ BANK_PLAYER, + /* 1 */ BANK_ITEM, + /* 2 */ BANK_ENV, + /* 3 */ BANK_ENEMY, + /* 4 */ BANK_SYSTEM, + /* 5 */ BANK_OCARINA, + /* 6 */ BANK_VOICE +} SoundBankTypes; + +typedef enum { + /* 0 */ SFX_STATE_EMPTY, + /* 1 */ SFX_STATE_QUEUED, + /* 2 */ SFX_STATE_READY, + /* 3 */ SFX_STATE_PLAYING_REFRESH, + /* 4 */ SFX_STATE_PLAYING_1, + /* 5 */ SFX_STATE_PLAYING_2 +} SfxState; + +typedef struct { + /* 0x00 */ f32* posX; + /* 0x04 */ f32* posY; + /* 0x08 */ f32* posZ; + /* 0x0C */ u8 token; + /* 0x10 */ f32* freqScale; + /* 0x14 */ f32* vol; + /* 0x18 */ s8* reverbAdd; + /* 0x1C */ f32 dist; + /* 0x20 */ u32 priority; // lower is more prioritized + /* 0x24 */ u8 sfxImportance; + /* 0x26 */ u16 sfxParams; + /* 0x28 */ u16 sfxId; + /* 0x2A */ u8 state; // uses SfxState enum + /* 0x2B */ u8 freshness; + /* 0x2C */ u8 prev; + /* 0x2D */ u8 next; + /* 0x2E */ u8 channelIdx; + /* 0x2F */ u8 unk_2F; +} SoundBankEntry; // size = 0x30 + +/* + * SFX IDs + * + * index 0000000111111111 observed in audio code + * & 200 0000001000000000 single bit + * & 400 0000010000000000 single bit + * & 800 0000100000000000 single bit, what we currently call SFX_FLAG + * & 600 0000011000000000 2 bits + * & A00 0000101000000000 2 bits + * & C00 0000110000000000 2 bits, observed in audio code + * & E00 0000111000000000 all 3 bits + * bank 1111000000000000 observed in audio code + */ + +#define SFX_BANK_SHIFT(sfxId) (((sfxId) >> 12) & 0xFF) + +#define SFX_BANK_MASK(sfxId) ((sfxId) & 0xF000) + +#define SFX_INDEX(sfxId) ((sfxId) & 0x01FF) +#define SFX_BANK(sfxId) SFX_BANK_SHIFT(SFX_BANK_MASK(sfxId)) + +typedef struct { + u32 priority; // lower is more prioritized + u8 entryIndex; +} ActiveSound; + +typedef struct { + u8 importance; + u16 params; +} SoundParams; + +typedef struct { + /* 0x0000 */ u8 noteIdx; + /* 0x0001 */ u8 unk_01; + /* 0x0002 */ u16 unk_02; + /* 0x0004 */ u8 volume; + /* 0x0005 */ u8 vibrato; + /* 0x0006 */ s8 tone; + /* 0x0007 */ u8 semitone; +} OcarinaNote; // size = 0x8 + +typedef struct { + u8 len; + u8 notesIdx[8]; +} OcarinaSongInfo; + +typedef struct { + u8 noteIdx; + u8 state; // original name: "status" + u8 pos; // original name: "locate" +} OcarinaStaff; + +typedef enum { + /* 0 */ OCARINA_NOTE_A, + /* 1 */ OCARINA_NOTE_C_DOWN, + /* 2 */ OCARINA_NOTE_C_RIGHT, + /* 3 */ OCARINA_NOTE_C_LEFT, + /* 4 */ OCARINA_NOTE_C_UP, + /* -1 */ OCARINA_NOTE_INVALID = 0xFF +} OcarinaNoteIdx; + +#ifdef __cplusplus +extern "C" { +#endif + +void Audio_SetGameVolume(int player_id, f32 volume); +float Audio_GetGameVolume(int player_id); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/soh/include/z64bgcheck.h b/soh/include/z64bgcheck.h new file mode 100644 index 000000000..87c8e246a --- /dev/null +++ b/soh/include/z64bgcheck.h @@ -0,0 +1,196 @@ +#ifndef Z_BGCHECK_H +#define Z_BGCHECK_H + +struct GlobalContext; +struct Actor; +struct DynaPolyActor; + +#define COLPOLY_NORMAL_FRAC (1.0f / SHT_MAX) +#define COLPOLY_SNORMAL(x) ((s16)((x) * SHT_MAX)) +#define COLPOLY_GET_NORMAL(n) ((n)*COLPOLY_NORMAL_FRAC) +#define COLPOLY_VIA_FLAG_TEST(vIA, flags) ((vIA) & (((flags)&7) << 13)) +#define COLPOLY_VTX_INDEX(vI) ((vI)&0x1FFF) + +#define DYNAPOLY_INVALIDATE_LOOKUP (1 << 0) + +#define BGACTOR_NEG_ONE -1 +#define BG_ACTOR_MAX 50 +#define BGCHECK_SCENE BG_ACTOR_MAX +#define BGCHECK_Y_MIN -32000.0f +#define BGCHECK_XYZ_ABSMAX 32760.0f +#define BGCHECK_SUBDIV_OVERLAP 50 +#define BGCHECK_SUBDIV_MIN 150.0f + +#define FUNC_80041EA4_RESPAWN 5 +#define FUNC_80041EA4_MOUNT_WALL 6 +#define FUNC_80041EA4_STOP 8 +#define FUNC_80041EA4_VOID_OUT 12 + +#define WATERBOX_ROOM(p) ((p >> 13) & 0x3F) + +typedef struct { + Vec3f scale; + Vec3s rot; + Vec3f pos; +} ScaleRotPos; + +typedef struct { + /* 0x00 */ u16 type; + union { + u16 vtxData[3]; + struct { + /* 0x02 */ u16 flags_vIA; // 0xE000 is poly exclusion flags (xpFlags), 0x1FFF is vtxId + /* 0x04 */ u16 flags_vIB; // 0xE000 is flags, 0x1FFF is vtxId + // 0x2000 = poly IsConveyor surface + /* 0x06 */ u16 vIC; + }; + }; + /* 0x08 */ Vec3s normal; // Unit normal vector + // Value ranges from -0x7FFF to 0x7FFF, representing -1.0 to 1.0; 0x8000 is invalid + + /* 0x0E */ s16 dist; // Plane distance from origin along the normal +} CollisionPoly; // size = 0x10 + +typedef struct { + /* 0x00 */ u16 cameraSType; + /* 0x02 */ s16 numCameras; + /* 0x04 */ Vec3s* camPosData; +} CamData; + +typedef struct { + /* 0x00 */ s16 xMin; + /* 0x02 */ s16 ySurface; + /* 0x04 */ s16 zMin; + /* 0x06 */ s16 xLength; + /* 0x08 */ s16 zLength; + /* 0x0C */ u32 properties; + + // 0x0008_0000 = ? + // 0x0007_E000 = Room Index, 0x3F = all rooms + // 0x0000_1F00 = Lighting Settings Index + // 0x0000_00FF = CamData index +} WaterBox; // size = 0x10 + +typedef struct { + u32 data[2]; + + // Type 1 + // 0x0800_0000 = wall damage +} SurfaceType; + +typedef struct { + /* 0x00 */ Vec3s minBounds; // minimum coordinates of poly bounding box + /* 0x06 */ Vec3s maxBounds; // maximum coordinates of poly bounding box + /* 0x0C */ u16 numVertices; + /* 0x10 */ Vec3s* vtxList; + /* 0x14 */ u16 numPolygons; + /* 0x18 */ CollisionPoly* polyList; + /* 0x1C */ SurfaceType* surfaceTypeList; + /* 0x20 */ CamData* cameraDataList; + /* 0x24 */ u16 numWaterBoxes; + /* 0x28 */ WaterBox* waterBoxes; +} CollisionHeader; // original name: BGDataInfo + +typedef struct { + s16 polyId; + u16 next; // next SSNode index +} SSNode; + +typedef struct { + u16 head; // first SSNode index +} SSList; + +typedef struct { + /* 0x00 */ u16 max; // original name: short_slist_node_size + /* 0x02 */ u16 count; // original name: short_slist_node_last_index + /* 0x04 */ SSNode* tbl; // original name: short_slist_node_tbl + /* 0x08 */ u8* polyCheckTbl; // points to an array of bytes, one per static poly. Zero initialized when starting a + // bg check, and set to 1 if that poly has already been tested. +} SSNodeList; + +typedef struct { + SSNode* tbl; + s32 count; + s32 max; +} DynaSSNodeList; + +typedef struct { + SSList floor; + SSList wall; + SSList ceiling; +} StaticLookup; + +typedef struct { + u16 polyStartIndex; + SSList ceiling; + SSList wall; + SSList floor; +} DynaLookup; + +typedef struct { + /* 0x00 */ struct Actor* actor; + /* 0x04 */ CollisionHeader* colHeader; + /* 0x08 */ DynaLookup dynaLookup; + /* 0x10 */ u16 vtxStartIndex; + /* 0x14 */ ScaleRotPos prevTransform; + /* 0x34 */ ScaleRotPos curTransform; + /* 0x54 */ Sphere16 boundingSphere; + /* 0x5C */ f32 minY; + /* 0x60 */ f32 maxY; +} BgActor; // size = 0x64 + +typedef struct { + /* 0x0000 */ u8 bitFlag; + /* 0x0004 */ BgActor bgActors[BG_ACTOR_MAX]; + /* 0x138C */ u16 bgActorFlags[BG_ACTOR_MAX]; // & 0x0008 = no dyna ceiling + /* 0x13F0 */ CollisionPoly* polyList; + /* 0x13F4 */ Vec3s* vtxList; + /* 0x13F8 */ DynaSSNodeList polyNodes; + /* 0x1404 */ s32 polyNodesMax; + /* 0x1408 */ s32 polyListMax; + /* 0x140C */ s32 vtxListMax; +} DynaCollisionContext; // size = 0x1410 + +typedef struct CollisionContext { + /* 0x00 */ CollisionHeader* colHeader; // scene's static collision + /* 0x04 */ Vec3f minBounds; // minimum coordinates of collision bounding box + /* 0x10 */ Vec3f maxBounds; // maximum coordinates of collision bounding box + /* 0x1C */ Vec3i subdivAmount; // x, y, z subdivisions of the scene's static collision + /* 0x28 */ Vec3f subdivLength; // x, y, z subdivision worldspace lengths + /* 0x34 */ Vec3f subdivLengthInv; // inverse of subdivision length + /* 0x40 */ StaticLookup* lookupTbl; // 3d array of length subdivAmount + /* 0x44 */ SSNodeList polyNodes; + /* 0x50 */ DynaCollisionContext dyna; + /* 0x1460 */ u32 memSize; // Size of all allocated memory plus CollisionContext +} CollisionContext; // size = 0x1464 + +typedef struct { + /* 0x00 */ struct GlobalContext* globalCtx; + /* 0x04 */ struct CollisionContext* colCtx; + /* 0x08 */ u16 xpFlags; + /* 0x0C */ CollisionPoly** resultPoly; + /* 0x10 */ f32 yIntersect; + /* 0x14 */ Vec3f* pos; + /* 0x18 */ s32* bgId; + /* 0x1C */ struct Actor* actor; + /* 0x20 */ u32 unk_20; + /* 0x24 */ f32 chkDist; + /* 0x28 */ DynaCollisionContext* dyna; + /* 0x2C */ SSList* ssList; +} DynaRaycast; + +typedef struct { + /* 0x00 */ struct CollisionContext* colCtx; + /* 0x04 */ u16 xpFlags; + /* 0x08 */ DynaCollisionContext* dyna; + /* 0x0C */ SSList* ssList; + /* 0x10 */ Vec3f* posA; + /* 0x14 */ Vec3f* posB; + /* 0x18 */ Vec3f* posResult; + /* 0x1C */ CollisionPoly** resultPoly; + /* 0x20 */ s32 chkOneFace; // bccFlags & 0x8 + /* 0x24 */ f32* distSq; // distance from posA to poly squared + /* 0x28 */ f32 chkDist; // distance from poly +} DynaLineTest; + +#endif diff --git a/soh/include/z64camera.h b/soh/include/z64camera.h new file mode 100644 index 000000000..f4f064045 --- /dev/null +++ b/soh/include/z64camera.h @@ -0,0 +1,1288 @@ +#ifndef Z64CAMERA_H +#define Z64CAMERA_H + +#include "ultra64.h" +#include "z64cutscene.h" + +#define CAM_STAT_CUT 0 +#define CAM_STAT_WAIT 1 +#define CAM_STAT_UNK3 3 +#define CAM_STAT_ACTIVE 7 +#define CAM_STAT_UNK100 0x100 + +#define NUM_CAMS 4 +#define MAIN_CAM 0 +#define SUBCAM_FIRST 1 +#define SUBCAM_FREE 0 +#define SUBCAM_NONE -1 +#define SUBCAM_ACTIVE -1 + +#define ONEPOINT_CS_INFO(camera) ((Unique9OnePointCs*)camera->paramData) +#define PARENT_CAM(cam) ((cam)->globalCtx->cameraPtrs[(cam)->parentCamIdx]) +#define CHILD_CAM(cam) ((cam)->globalCtx->cameraPtrs[(cam)->childCamIdx]) + +typedef enum { + /* 0x00 */ CAM_SET_NONE, + /* 0x01 */ CAM_SET_NORMAL0, + /* 0x02 */ CAM_SET_NORMAL1, + /* 0x03 */ CAM_SET_DUNGEON0, + /* 0x04 */ CAM_SET_DUNGEON1, + /* 0x05 */ CAM_SET_NORMAL3, + /* 0x06 */ CAM_SET_HORSE, // "HORSE0" + /* 0x07 */ CAM_SET_BOSS_GOHMA, // "BOSS_GOMA" (unused) + /* 0x08 */ CAM_SET_BOSS_DODONGO, // "BOSS_DODO" (unused) + /* 0x09 */ CAM_SET_BOSS_BARINADE, // "BOSS_BARI" (unused) + /* 0x0A */ CAM_SET_BOSS_PHANTOM_GANON, // "BOSS_FGANON" + /* 0x0B */ CAM_SET_BOSS_VOLVAGIA, // "BOSS_BAL" + /* 0x0C */ CAM_SET_BOSS_BONGO, // "BOSS_SHADES" + /* 0x0D */ CAM_SET_BOSS_MORPHA, // "BOSS_MOFA" (unused) + /* 0x0E */ CAM_SET_BOSS_TWINROVA_PLATFORM, // Upper main platform and 4 smaller platforms in the room of the Twinrova boss battle "TWIN0" + /* 0x0F */ CAM_SET_BOSS_TWINROVA_FLOOR, // The floor in the room of the Twinrova boss battle "TWIN1" + /* 0x10 */ CAM_SET_BOSS_GANONDORF, // "BOSS_GANON1" + /* 0x11 */ CAM_SET_BOSS_GANON, // "BOSS_GANON2" (unused) + /* 0x12 */ CAM_SET_TOWER_CLIMB, // Various climbing structures (collapse sequence stairs, spiral around sarias house, zora domain climb, etc...) "TOWER0" + /* 0x13 */ CAM_SET_TOWER_UNUSED, // Unused but data is in Phantom Ganon's Lair (no surface uses it) "TOWER1" + /* 0x14 */ CAM_SET_MARKET_BALCONY, // Activated in day child market by talking to NPC on balcony above bombchu bowling "FIXED0" + /* 0x15 */ CAM_SET_CHU_BOWLING, // Fixes the camera to the bombchu bowling targets while playing the minigame "FIXED1" + /* 0x16 */ CAM_SET_PIVOT_CRAWLSPACE, // Unknown. In scene data: closely associated with crawlspaces CIRCLE0" + /* 0x17 */ CAM_SET_PIVOT_SHOP_BROWSING, // Shopping and browsing for items "CIRCLE2" + /* 0x18 */ CAM_SET_PIVOT_IN_FRONT, // The camera used on Link's balcony in Kokiri forest. Data present in scene data for Deku Tree, GTG, Inside Ganon's Castle (TODO: may or may not be used) "CIRCLE3" + /* 0x19 */ CAM_SET_PREREND_FIXED, // Camera is fixed in position and rotation "PREREND0" + /* 0x1A */ CAM_SET_PREREND_PIVOT, // Camera is fixed in position with fixed pitch, but is free to rotate in the yaw direction 360 degrees "PREREND1" + /* 0x1B */ CAM_SET_PREREND_SIDE_SCROLL, // Camera side-scrolls position to follow link. Only used in castle courtyard with the guards "PREREND3" + /* 0x1C */ CAM_SET_DOOR0, // Custom room door transitions, used in fire and royal family tomb + /* 0x1D */ CAM_SET_DOORC, // Generic room door transitions, camera moves and follows player as the door is open and closed + /* 0x1E */ CAM_SET_CRAWLSPACE, // Used in all crawlspaces "RAIL3" + /* 0x1F */ CAM_SET_START0, // Data is given in Temple of Time, but no surface uses it + /* 0x20 */ CAM_SET_START1, // Scene/room door transitions that snap the camera to a fixed location (example: ganon's towers doors climbing up) + /* 0x21 */ CAM_SET_FREE0, // Full manual control is given over the camera + /* 0x22 */ CAM_SET_FREE2, // Various OnePoint Cutscenes, 10 total (example: falling chest) + /* 0x23 */ CAM_SET_PIVOT_CORNER, // Inside the carpenter jail cells from theives hideout "CIRCLE4" + /* 0x24 */ CAM_SET_PIVOT_WATER_SURFACE, // Player diving from the surface of the water to underwater "CIRCLE5" + /* 0x25 */ CAM_SET_CS_0, // Various cutscenes "DEMO0" + /* 0x26 */ CAM_SET_CS_TWISTED_HALLWAY, // Never set to, but checked in twisting hallway (Forest Temple) "DEMO1" + /* 0x27 */ CAM_SET_FOREST_BIRDS_EYE, // Used in the falling ceiling room in forest temple "MORI1" + /* 0x28 */ CAM_SET_SLOW_CHEST_CS, // Long cutscene when opening a big chest with a major item "ITEM0" + /* 0x29 */ CAM_SET_ITEM_UNUSED, // Unused "ITEM1" + /* 0x2A */ CAM_SET_CS_3, // Various cutscenes "DEMO3" + /* 0x2B */ CAM_SET_CS_ATTENTION, // Attention cutscenes and the actor siofuki (water spout/jet) "DEMO4" + /* 0x2C */ CAM_SET_BEAN_GENERIC, // All beans except lost woods "UFOBEAN" + /* 0x2D */ CAM_SET_BEAN_LOST_WOODS, // Lost woods bean "LIFTBEAN" + /* 0x2E */ CAM_SET_SCENE_UNUSED, // Unused "SCENE0" + /* 0x2F */ CAM_SET_SCENE_TRANSITION, // Scene Transitions "SCENE1" + /* 0x30 */ CAM_SET_FIRE_PLATFORM, // All the fire platforms that rise. Also used in non-mq spirit shortcut "HIDAN1" + /* 0x31 */ CAM_SET_FIRE_STAIRCASE, // Used on fire staircase actor cutscene in shortcut room connecting vanilla hammer chest to the final goron small key "HIDAN2" + /* 0x32 */ CAM_SET_FOREST_UNUSED, // Unused "MORI2" + /* 0x33 */ CAM_SET_FOREST_DEFEAT_POE, // Used when defeating a poe sister "MORI3" + /* 0x34 */ CAM_SET_BIG_OCTO, // Used by big octo miniboss in Jabu Jabu "TAKO" + /* 0x35 */ CAM_SET_MEADOW_BIRDS_EYE, // Used only as child in Sacred Forest Meadow Maze "SPOT05A" + /* 0x36 */ CAM_SET_MEADOW_UNUSED, // Unused from Sacred Forest Meadow "SPOT05B" + /* 0x37 */ CAM_SET_FIRE_BIRDS_EYE, // Used in lower-floor maze in non-mq fire temple "HIDAN3" + /* 0x38 */ CAM_SET_TURN_AROUND, // Put the camera in front of player and turn around to look at player from the front "ITEM2" + /* 0x39 */ CAM_SET_PIVOT_VERTICAL, // Lowering platforms (forest temple bow room, Jabu final shortcut) "CAM_SET_PIVOT_VERTICAL" + /* 0x3A */ CAM_SET_NORMAL2, + /* 0x3B */ CAM_SET_FISHING, // Fishing pond by the lake + /* 0x3C */ CAM_SET_CS_C, // Various cutscenes "DEMOC" + /* 0x3D */ CAM_SET_JABU_TENTACLE, // Jabu-Jabu Parasitic Tenticle Rooms "UO_FIBER" + /* 0x3E */ CAM_SET_DUNGEON2, + /* 0x3F */ CAM_SET_DIRECTED_YAW, // Does not auto-update yaw, tends to keep the camera pointed at a certain yaw (used by biggoron and final spirit lowering platform) "TEPPEN" + /* 0x40 */ CAM_SET_PIVOT_FROM_SIDE, // Fixed side view, allows rotation of camera (eg. Potion Shop, Meadow at fairy grotto) "CIRCLE7" + /* 0x41 */ CAM_SET_NORMAL4, + /* 0x42 */ CAM_SET_MAX +} CameraSettingType; + +typedef enum { + /* 0x00 */ CAM_MODE_NORMAL, + /* 0x01 */ CAM_MODE_TARGET, // "PARALLEL" + /* 0x02 */ CAM_MODE_FOLLOWTARGET, // "KEEPON" + /* 0x03 */ CAM_MODE_TALK, + /* 0x04 */ CAM_MODE_BATTLE, + /* 0x05 */ CAM_MODE_CLIMB, + /* 0x06 */ CAM_MODE_FIRSTPERSON, // "SUBJECT" + /* 0x07 */ CAM_MODE_BOWARROW, + /* 0x08 */ CAM_MODE_BOWARROWZ, + /* 0x09 */ CAM_MODE_HOOKSHOT, // "FOOKSHOT" + /* 0x0A */ CAM_MODE_BOOMERANG, + /* 0x0B */ CAM_MODE_SLINGSHOT, // "PACHINCO" + /* 0x0C */ CAM_MODE_CLIMBZ, + /* 0x0D */ CAM_MODE_JUMP, + /* 0x0E */ CAM_MODE_HANG, + /* 0x0F */ CAM_MODE_HANGZ, + /* 0x10 */ CAM_MODE_FREEFALL, + /* 0x11 */ CAM_MODE_CHARGE, + /* 0x12 */ CAM_MODE_STILL, + /* 0x13 */ CAM_MODE_PUSHPULL, + /* 0x14 */ CAM_MODE_FOLLOWBOOMERANG, // "BOOKEEPON" + /* 0x15 */ CAM_MODE_MAX +} CameraModeType; + +typedef enum { + /* 0x00 */ CAM_FUNC_NONE, + /* 0x01 */ CAM_FUNC_NORM0, + /* 0x02 */ CAM_FUNC_NORM1, + /* 0x03 */ CAM_FUNC_NORM2, + /* 0x04 */ CAM_FUNC_NORM3, + /* 0x05 */ CAM_FUNC_NORM4, + /* 0x06 */ CAM_FUNC_PARA0, + /* 0x07 */ CAM_FUNC_PARA1, + /* 0x08 */ CAM_FUNC_PARA2, + /* 0x09 */ CAM_FUNC_PARA3, + /* 0x0A */ CAM_FUNC_PARA4, + /* 0x0B */ CAM_FUNC_KEEP0, + /* 0x0C */ CAM_FUNC_KEEP1, + /* 0x0D */ CAM_FUNC_KEEP2, + /* 0x0E */ CAM_FUNC_KEEP3, + /* 0x0F */ CAM_FUNC_KEEP4, + /* 0x10 */ CAM_FUNC_SUBJ0, + /* 0x11 */ CAM_FUNC_SUBJ1, + /* 0x12 */ CAM_FUNC_SUBJ2, + /* 0x13 */ CAM_FUNC_SUBJ3, + /* 0x14 */ CAM_FUNC_SUBJ4, + /* 0x15 */ CAM_FUNC_JUMP0, + /* 0x16 */ CAM_FUNC_JUMP1, + /* 0x17 */ CAM_FUNC_JUMP2, + /* 0x18 */ CAM_FUNC_JUMP3, + /* 0x19 */ CAM_FUNC_JUMP4, + /* 0x1A */ CAM_FUNC_BATT0, + /* 0x1B */ CAM_FUNC_BATT1, + /* 0x1C */ CAM_FUNC_BATT2, + /* 0x1D */ CAM_FUNC_BATT3, + /* 0x1E */ CAM_FUNC_BATT4, + /* 0x1F */ CAM_FUNC_FIXD0, + /* 0x20 */ CAM_FUNC_FIXD1, + /* 0x21 */ CAM_FUNC_FIXD2, + /* 0x22 */ CAM_FUNC_FIXD3, + /* 0x23 */ CAM_FUNC_FIXD4, + /* 0x24 */ CAM_FUNC_DATA0, + /* 0x25 */ CAM_FUNC_DATA1, + /* 0x26 */ CAM_FUNC_DATA2, + /* 0x27 */ CAM_FUNC_DATA3, + /* 0x28 */ CAM_FUNC_DATA4, + /* 0x29 */ CAM_FUNC_UNIQ0, + /* 0x2A */ CAM_FUNC_UNIQ1, + /* 0x2B */ CAM_FUNC_UNIQ2, + /* 0x2C */ CAM_FUNC_UNIQ3, + /* 0x2D */ CAM_FUNC_UNIQ4, + /* 0x2E */ CAM_FUNC_UNIQ5, + /* 0x2F */ CAM_FUNC_UNIQ6, + /* 0x30 */ CAM_FUNC_UNIQ7, + /* 0x31 */ CAM_FUNC_UNIQ8, + /* 0x32 */ CAM_FUNC_UNIQ9, + /* 0x33 */ CAM_FUNC_DEMO0, + /* 0x34 */ CAM_FUNC_DEMO1, + /* 0x35 */ CAM_FUNC_DEMO2, + /* 0x36 */ CAM_FUNC_DEMO3, + /* 0x37 */ CAM_FUNC_DEMO4, + /* 0x38 */ CAM_FUNC_DEMO5, + /* 0x39 */ CAM_FUNC_DEMO6, + /* 0x3A */ CAM_FUNC_DEMO7, + /* 0x3B */ CAM_FUNC_DEMO8, + /* 0x3C */ CAM_FUNC_DEMO9, + /* 0x3D */ CAM_FUNC_SPEC0, + /* 0x3E */ CAM_FUNC_SPEC1, + /* 0x3F */ CAM_FUNC_SPEC2, + /* 0x40 */ CAM_FUNC_SPEC3, + /* 0x41 */ CAM_FUNC_SPEC4, + /* 0x42 */ CAM_FUNC_SPEC5, + /* 0x43 */ CAM_FUNC_SPEC6, + /* 0x44 */ CAM_FUNC_SPEC7, + /* 0x45 */ CAM_FUNC_SPEC8, + /* 0x46 */ CAM_FUNC_SPEC9, + /* 0x47 */ CAM_FUNC_MAX +} CameraFuncType; + +typedef enum { + /* 0x00 */ CAM_DATA_Y_OFFSET, + /* 0x01 */ CAM_DATA_EYE_DIST, + /* 0x02 */ CAM_DATA_EYE_DIST_NEXT, + /* 0x03 */ CAM_DATA_PITCH_TARGET, + /* 0x04 */ CAM_DATA_YAW_UPDATE_RATE_TARGET, + /* 0x05 */ CAM_DATA_XZ_UPDATE_RATE_TARGET, + /* 0x06 */ CAM_DATA_MAX_YAW_UPDATE, + /* 0x07 */ CAM_DATA_FOV, + /* 0x08 */ CAM_DATA_AT_LERP_STEP_SCALE, + /* 0x09 */ CAM_DATA_FLAGS, + /* 0x0A */ CAM_DATA_YAW_TARGET, + /* 0x0B */ CAM_DATA_GROUND_Y_OFFSET, + /* 0x0C */ CAM_DATA_GROUND_AT_LERP_STEP_SCALE, + /* 0x0D */ CAM_DATA_SWING_YAW_INIT, + /* 0x0E */ CAM_DATA_SWING_YAW_FINAL, + /* 0x0F */ CAM_DATA_SWING_PITCH_INIT, + /* 0x10 */ CAM_DATA_SWING_PITCH_FINAL, + /* 0x11 */ CAM_DATA_SWING_PITCH_ADJ, + /* 0x12 */ CAM_DATA_MIN_MAX_DIST_FACTOR, + /* 0x13 */ CAM_DATA_AT_OFFSET_X, + /* 0x14 */ CAM_DATA_AT_OFFSET_Y, + /* 0x15 */ CAM_DATA_AT_OFFSET_Z, + /* 0x16 */ CAM_DATA_UNK_22, + /* 0x17 */ CAM_DATA_UNK_23, + /* 0x18 */ CAM_DATA_FOV_SCALE, + /* 0x19 */ CAM_DATA_YAW_SCALE, + /* 0x1A */ CAM_DATA_UNK_26, + /* 0x1B */ CAM_DATA_MAX +} CameraDataType; + +#define CAM_FUNCDATA_FLAGS(flags) \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ Vec3f collisionClosePoint; + /* 0x0C */ CollisionPoly* atEyePoly; + /* 0x10 */ f32 swingUpdateRate; + /* 0x14 */ s16 unk_14; + /* 0x16 */ s16 unk_16; + /* 0x18 */ s16 unk_18; + /* 0x1A */ s16 swingUpdateRateTimer; +} SwingAnimation; // size = 0x1C + +typedef struct { + /* 0x00 */ SwingAnimation swing; + /* 0x1C */ f32 yOffset; + /* 0x20 */ f32 unk_20; + /* 0x24 */ s16 slopePitchAdj; + /* 0x26 */ s16 swingYawTarget; + /* 0x28 */ s16 unk_28; + /* 0x2A */ s16 startSwingTimer; +} Normal1Anim; // size = 0x2C + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distMin; + /* 0x08 */ f32 distMax; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 fovTarget; + /* 0x1C */ f32 atLERPScaleMax; + /* 0x20 */ s16 pitchTarget; + /* 0x22 */ s16 interfaceFlags; + /* 0x24 */ Normal1Anim anim; +} Normal1; // size = 0x50 + +#define CAM_FUNCDATA_NORM1(yOffset, eyeDist, eyeDistNext, pitchTarget, yawUpdateRateTarget, xzUpdateRateTarget, maxYawUpdate, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { maxYawUpdate, CAM_DATA_MAX_YAW_UPDATE }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +#define CAM_FUNCDATA_NORM1_ALT(yOffset, eyeDist, eyeDistNext, pitchTarget, yawUpdateRateTarget, xzUpdateRateTarget, maxYawUpdate, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_UNK_26 }, \ + { maxYawUpdate, CAM_DATA_MAX_YAW_UPDATE }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ Vec3f unk_00; + /* 0x0C */ Vec3f unk_0C; + /* 0x18 */ f32 unk_18; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ s16 unk_20; + /* 0x22 */ s16 unk_22; + /* 0x24 */ f32 unk_24; + /* 0x28 */ s16 unk_28; +} Normal2Anim; // size = 0x2A + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 unk_18; + /* 0x1C */ s16 unk_1C; + /* 0x1E */ s16 interfaceFlags; + /* 0x20 */ Normal2Anim anim; +} Normal2; // size = 0x4A + +#define CAM_FUNCDATA_NORM2(yOffset, eyeDist, eyeDistNext, unk_23, yawUpdateRateTarget, maxYawUpdate, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { unk_23, CAM_DATA_UNK_23 }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { maxYawUpdate, CAM_DATA_MAX_YAW_UPDATE }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ SwingAnimation swing; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ f32 unk_20; + /* 0x24 */ s16 curPitch; + /* 0x26 */ s16 yawUpdAmt; + /* 0x28 */ s16 yawTimer; + /* 0x2A */ s16 distTimer; +} Normal3Anim; // size = 0x2C + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distMin; + /* 0x08 */ f32 distMax; + /* 0x0C */ f32 yawUpdateSpeed; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 fovTarget; + /* 0x18 */ f32 maxAtLERPScale; + /* 0x1C */ s16 pitchTarget; + /* 0x1E */ s16 interfaceFlags; + /* 0x20 */ Normal3Anim anim; +} Normal3; // size = 0x4C + +#define CAM_FUNCDATA_NORM3(yOffset, eyeDist, eyeDistNext, pitchTarget, yawUpdateRateTarget, xzUpdateRateTarget, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ Vec3f unk_00; + /* 0x0C */ f32 yTarget; + /* 0x10 */ s16 unk_10; + /* 0x12 */ s16 yawTarget; + /* 0x14 */ s16 pitchTarget; + /* 0x16 */ s16 unk_16; + /* 0x18 */ s16 animTimer; +} Parallel1Anim; // size = 0x1A + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distTarget; + /* 0x08 */ f32 unk_08; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ f32 fovTarget; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 unk_18; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ s16 pitchTarget; + /* 0x22 */ s16 yawTarget; + /* 0x24 */ s16 interfaceFlags; + /* 0x28 */ Parallel1Anim anim; +} Parallel1; // size = 0x42 + +#define CAM_FUNCDATA_PARA1(yOffset, eyeDist, pitchTarget, yawTarget, yawUpdateRateTarget, xzUpdateRateTarget, fov, atLerpStepScale, flags, groundYOffset, groundAtLerpStepScale) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawTarget, CAM_DATA_YAW_TARGET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS }, \ + { groundYOffset, CAM_DATA_GROUND_Y_OFFSET }, \ + { groundAtLerpStepScale, CAM_DATA_GROUND_AT_LERP_STEP_SCALE } + +typedef struct { + + /* 0x00 */ SwingAnimation swing; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ VecSph unk_20; +} Jump1Anim; // size = 0x28 + +typedef struct { + /* 0x00 */ f32 atYOffset; + /* 0x04 */ f32 distMin; + /* 0x08 */ f32 distMax; + /* 0x0C */ f32 yawUpateRateTarget; + /* 0x10 */ f32 maxYawUpdate; + /* 0x14 */ f32 unk_14; // never used. + /* 0x18 */ f32 atLERPScaleMax; + /* 0x1C */ s16 interfaceFlags; + /* 0x20 */ Jump1Anim anim; +} Jump1; // size = 0x48 + +#define CAM_FUNCDATA_JUMP1(yOffset, eyeDist, eyeDistNext, yawUpdateRateTarget, maxYawUpdate, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { maxYawUpdate, CAM_DATA_MAX_YAW_UPDATE }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ f32 floorY; + /* 0x4 */ s16 yawTarget; + /* 0x6 */ s16 initYawDiff; // unused, set but not read. + /* 0x8 */ s16 yawAdj; + /* 0xA */ s16 onFloor; // unused, set but not read + /* 0xC */ s16 animTimer; +} Jump2Anim; // size = 0x10 + +typedef struct { + /* 0x00 */ f32 atYOffset; + /* 0x04 */ f32 minDist; + /* 0x08 */ f32 maxDist; + /* 0x0C */ f32 minMaxDistFactor; + /* 0x10 */ f32 yawUpdRateTarget; + /* 0x14 */ f32 xzUpdRateTarget; + /* 0x18 */ f32 fovTarget; + /* 0x1C */ f32 atLERPStepScale; + /* 0x20 */ s16 interfaceFlags; + /* 0x24 */ Jump2Anim anim; +} Jump2; // size = 0x34 + +#define CAM_FUNCDATA_JUMP2(yOffset, eyeDist, eyeDistNext, minMaxDistFactor, yawUpdateRateTarget, xzUpdateRateTarget, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { minMaxDistFactor, CAM_DATA_MIN_MAX_DIST_FACTOR }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ SwingAnimation swing; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ s16 animTimer; + /* 0x22 */ s16 mode; +} Jump3Anim; // size = 0x24 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distMin; + /* 0x08 */ f32 distMax; + /* 0x0C */ f32 swingUpdateRate; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 fovTarget; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ s16 pitchTarget; + /* 0x22 */ s16 interfaceFlags; + /* 0x24 */ Jump3Anim anim; +} Jump3; // size = 0x48 + +#define CAM_FUNCDATA_JUMP3(yOffset, eyeDist, eyeDistNext, pitchTarget, yawUpdateRateTarget, xzUpdateRateTarget, maxYawUpdate, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { maxYawUpdate, CAM_DATA_MAX_YAW_UPDATE }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ f32 initialEyeToAtDist; + /* 0x04 */ f32 roll; + /* 0x08 */ f32 yPosOffset; + /* 0x0C */ Actor* target; + /* 0x10 */ f32 unk_10; + /* 0x14 */ s16 unk_14; // unused + /* 0x16 */ s16 initialEyeToAtYaw; + /* 0x18 */ s16 initialEyeToAtPitch; + /* 0x1A */ s16 animTimer; + /* 0x1C */ s16 chargeTimer; +} Battle1Anim; // size = 0x1E + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distance; + /* 0x08 */ f32 swingYawInitial; + /* 0x0C */ f32 swingYawFinal; + /* 0x10 */ f32 swingPitchInitial; + /* 0x14 */ f32 swingPitchFinal; + /* 0x18 */ f32 swingPitchAdj; + /* 0x1C */ f32 fov; + /* 0x20 */ f32 atLERPScaleOnGround; + /* 0x24 */ f32 yOffsetOffGround; + /* 0x28 */ f32 atLERPScaleOffGround; + /* 0x2C */ s16 flags; + /* 0x30 */ Battle1Anim anim; +} Battle1; // size = 0x50 + +#define CAM_FUNCDATA_BATT1(yOffset, eyeDist, swingYawInit, swingYawFinal, swingPitchInit, swingPitchFinal, swingPitchAdj, fov, atLerpStepScale, flags, groundYOffset, groundAtLerpStepScale) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { swingYawInit, CAM_DATA_SWING_YAW_INIT }, \ + { swingYawFinal, CAM_DATA_SWING_YAW_FINAL }, \ + { swingPitchInit, CAM_DATA_SWING_PITCH_INIT }, \ + { swingPitchFinal, CAM_DATA_SWING_PITCH_FINAL }, \ + { swingPitchAdj, CAM_DATA_SWING_PITCH_ADJ }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS }, \ + { groundYOffset, CAM_DATA_GROUND_Y_OFFSET }, \ + { groundAtLerpStepScale, CAM_DATA_GROUND_AT_LERP_STEP_SCALE } + +typedef struct { + /* 0x0 */ s16 animTimer; +} Battle4Anim; // size = 0x2 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 rTarget; + /* 0x08 */ s16 pitchTarget; + /* 0x0C */ f32 lerpUpdateRate; + /* 0x10 */ f32 fovTarget; + /* 0x14 */ f32 atLERPTarget; + /* 0x18 */ s16 interfaceFlags; + /* 0x1A */ s16 unk_1A; + /* 0x1C */ Battle4Anim anim; +} Battle4; // size = 0x20 + +#define CAM_FUNCDATA_BATT4(yOffset, eyeDist, pitchTarget, yawUpdateRateTarget, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ Actor* unk_0C; + /* 0x10 */ s16 unk_10; + /* 0x12 */ s16 unk_12; + /* 0x14 */ s16 unk_14; + /* 0x16 */ s16 unk_16; +} Keep1Anim; // size = 0x18 + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 unk_18; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ f32 unk_20; + /* 0x24 */ f32 unk_24; + /* 0x28 */ f32 unk_28; + /* 0x2C */ f32 unk_2C; + /* 0x30 */ s16 interfaceFlags; + /* 0x34 */ Keep1Anim anim; +} KeepOn1; // size = 0x4C + +#define CAM_FUNCDATA_KEEP1(yOffset, eyeDist, eyeDistNext, swingYawInit, swingYawFinal, swingPitchInit, swingPitchFinal, swingPitchAdj, fov, atLerpStepScale, flags, groundYOffset, groundAtLerpStepScale) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { swingYawInit, CAM_DATA_SWING_YAW_INIT }, \ + { swingYawFinal, CAM_DATA_SWING_YAW_FINAL }, \ + { swingPitchInit, CAM_DATA_SWING_PITCH_INIT }, \ + { swingPitchFinal, CAM_DATA_SWING_PITCH_FINAL }, \ + { swingPitchAdj, CAM_DATA_SWING_PITCH_ADJ }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS }, \ + { groundYOffset, CAM_DATA_GROUND_Y_OFFSET }, \ + { groundAtLerpStepScale, CAM_DATA_GROUND_AT_LERP_STEP_SCALE } + +typedef struct { + /* 0x00 */ Vec3f eyeToAtTarget; // esentially a VecSph, but all floats. + /* 0x0C */ Actor* target; + /* 0x10 */ Vec3f atTarget; + /* 0x1C */ s16 animTimer; +} Keep3Anim; // size = 0x20 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 minDist; + /* 0x08 */ f32 maxDist; + /* 0x0C */ f32 swingYawInital; + /* 0x10 */ f32 swingYawFinal; + /* 0x14 */ f32 swingPitchInitial; + /* 0x18 */ f32 swingPitchFinal; + /* 0x1C */ f32 swingPitchAdj; + /* 0x20 */ f32 fovTarget; + /* 0x24 */ f32 atLERPScaleMax; + /* 0x28 */ s16 initTimer; + /* 0x2A */ s16 flags; + /* 0x2C */ Keep3Anim anim; +} KeepOn3; // size = 0x4C + +#define CAM_FUNCDATA_KEEP3(yOffset, eyeDist, eyeDistNext, swingYawInit, swingYawFinal, swingPitchInit, swingPitchFinal, swingPitchAdj, fov, atLerpStepScale, yawUpdateRateTarget, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { swingYawInit, CAM_DATA_SWING_YAW_INIT }, \ + { swingYawFinal, CAM_DATA_SWING_YAW_FINAL }, \ + { swingPitchInit, CAM_DATA_SWING_PITCH_INIT }, \ + { swingPitchFinal, CAM_DATA_SWING_PITCH_FINAL }, \ + { swingPitchAdj, CAM_DATA_SWING_PITCH_ADJ }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ s16 unk_0C; + /* 0x0E */ s16 unk_0E; + /* 0x10 */ s16 unk_10; + /* 0x12 */ s16 unk_12; + /* 0x14 */ s16 unk_14; +} KeepOn4_Unk20; // size = 0x14 + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 unk_18; + /* 0x1C */ s16 unk_1C; + /* 0x1E */ s16 unk_1E; + /* 0x20 */ KeepOn4_Unk20 unk_20; +} KeepOn4; // size = 0x34 + +#define CAM_FUNCDATA_KEEP4(yOffset, eyeDist, pitchTarget, yawTarget, atOffsetZ, fov, flags, yawUpdateRateTarget, unk_22) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { yawTarget, CAM_DATA_YAW_TARGET }, \ + { atOffsetZ, CAM_DATA_AT_OFFSET_Z }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { unk_22, CAM_DATA_UNK_22 } + +typedef struct { + /* 0x0 */ f32 fovTarget; + /* 0x4 */ s16 animTimer; +} KeepOn0Anim; // size = 0x8 + +typedef struct { + /* 0x00 */ f32 fovScale; + /* 0x04 */ f32 yawScale; + /* 0x08 */ s16 timerInit; + /* 0x0A */ s16 interfaceFlags; + /* 0x0C */ KeepOn0Anim anim; +} KeepOn0; // size = 0x14 + +#define CAM_FUNCDATA_KEEP0(fovScale, yawScale, yawUpdateRateTarget, flags) \ + { fovScale, CAM_DATA_FOV_SCALE }, \ + { yawScale, CAM_DATA_YAW_SCALE }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ PosRot eyePosRotTarget; + /* 0x14 */ s16 fov; +} Fixed1Anim; // size = 0x18 + +typedef struct { + /* 0x00 */ f32 unk_00; // seems to be unused? + /* 0x04 */ f32 lerpStep; + /* 0x08 */ f32 fov; + /* 0x0C */ s16 interfaceFlags; + /* 0x10 */ Fixed1Anim anim; +} Fixed1; // size = 0x28 + +#define CAM_FUNCDATA_FIXD1(yOffset, yawUpdateRateTarget, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ Vec3f eye; + /* 0xC */ s16 fov; +} Fixed2InitParams; // size = 0x10 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 eyeStepScale; + /* 0x08 */ f32 posStepScale; + /* 0x0C */ f32 fov; + /* 0x10 */ s16 interfaceFlags; + /* 0x14 */ Fixed2InitParams initParams; +} Fixed2; // size = 0x24 + +#define CAM_FUNCDATA_FIXD2(yOffset, yawUpdateRateTarget, xzUpdateRateTarget, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ Vec3s rot; + /* 0x6 */ s16 fov; + /* 0x8 */ s16 updDirTimer; + /* 0xA */ s16 jfifId; +} Fixed3Anim; // size = 0xC + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Fixed3Anim anim; +} Fixed3; // size = 0x10 + +typedef struct { + /* 0x0 */ Vec3f eyeTarget; + /* 0xC */ f32 followSpeed; +} Fixed4Anim; // size = 0x10 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 speedToEyePos; + /* 0x08 */ f32 followSpeed; + /* 0x0C */ f32 fov; + /* 0x10 */ s16 interfaceFlags; + /* 0x14 */ Fixed4Anim anim; +} Fixed4; // size = 0x24 + +#define CAM_FUNCDATA_FIXD4(yOffset, yawUpdateRateTarget, xzUpdateRateTarget, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { xzUpdateRateTarget, CAM_DATA_XZ_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ f32 r; + /* 0x4 */ s16 yaw; + /* 0x6 */ s16 pitch; + /* 0x8 */ s16 animTimer; +} Subj3Anim; // size = 0xC + +typedef struct { + /* 0x00 */ f32 eyeNextYOffset; + /* 0x04 */ f32 eyeDist; + /* 0x08 */ f32 eyeNextDist; + /* 0x0C */ f32 unk_0C; // unused + /* 0x10 */ Vec3f atOffset; + /* 0x1C */ f32 fovTarget; + /* 0x20 */ s16 interfaceFlags; + /* 0x24 */ Subj3Anim anim; +} Subj3; // size = 0x30 + +#define CAM_FUNCDATA_SUBJ3(yOffset, eyeDist, eyeDistNext, yawUpdateRateTarget, atOffsetX, atOffsetY, atOffsetZ, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { atOffsetX, CAM_DATA_AT_OFFSET_X }, \ + { atOffsetY, CAM_DATA_AT_OFFSET_Y }, \ + { atOffsetZ, CAM_DATA_AT_OFFSET_Z }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ Linef unk_00; + /* 0x18 */ f32 unk_18; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ f32 unk_20; + /* 0x24 */ f32 unk_24; + /* 0x28 */ f32 unk_28; + /* 0x2C */ s16 unk_2C; + /* 0x2E */ s16 unk_2E; + /* 0x30 */ s16 unk_30; + /* 0x32 */ s16 unk_32; +} Subj4Anim; // size = 0x34 + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Subj4Anim anim; +} Subj4; // size = 0x38 + +#define CAM_FUNCDATA_SUBJ4(yOffset, eyeDist, eyeDistNext, yawUpdateRateTarget, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ PosRot eyePosRot; + /* 0x14 */ char unk_14[0x8]; + /* 0x1C */ s16 fov; + /* 0x1E */ s16 jfifId; +} Data4InitParams; // size = 0x20 + +typedef struct { + /* 0x0 */ f32 yOffset; + /* 0x4 */ f32 fov; + /* 0x8 */ s16 interfaceFlags; + /* 0xC */ Data4InitParams initParams; +} Data4; // size = 0x2C + +#define CAM_FUNCDATA_DATA4(yOffset, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ f32 unk_00; // unused + /* 0x4 */ s16 yawTarget; + /* 0x6 */ s16 yawTargetAdj; + /* 0x8 */ s16 timer; +} Unique1Anim; // size = 0xC + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distMin; + /* 0x08 */ f32 distMax; + /* 0x0C */ char unk_0C[4]; // unused + /* 0x10 */ f32 fovTarget; + /* 0x14 */ f32 atLERPScaleMax; + /* 0x18 */ s16 pitchTarget; + /* 0x1A */ s16 interfaceFlags; + /* 0x1C */ Unique1Anim anim; +} Unique1; // size = 0x28 + +#define CAM_FUNCDATA_UNIQ1(yOffset, eyeDist, eyeDistNext, pitchTarget, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ f32 unk_00; + /* 0x4 */ s16 unk_04; +} Unique2Unk10; // size = 0x8 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 distTarget; + /* 0x08 */ f32 fovTarget; + /* 0x0C */ s16 interfaceFlags; + /* 0x10 */ Unique2Unk10 unk_10; // unused, values set but not read. +} Unique2; // size = 0x18 + +#define CAM_FUNCDATA_UNIQ2(yOffset, eyeDist, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ f32 initialFov; + /* 0x4 */ f32 initialDist; +} Unique3Anim; // size = 0x8 + +typedef struct { + /* 0x0 */ f32 yOffset; + /* 0x4 */ f32 fov; + /* 0x8 */ s16 interfaceFlags; +} Unique3Params; // size = 0xC + +typedef struct { + /* 0x0 */ struct Actor* doorActor; + /* 0x4 */ s16 camDataIdx; + /* 0x6 */ s16 timer1; + /* 0x8 */ s16 timer2; + /* 0xA */ s16 timer3; +} DoorParams; // size = 0xC + +typedef struct { + /* 0x00 */ DoorParams doorParams; + /* 0x0C */ Unique3Params params; + /* 0x18 */ Unique3Anim anim; +} Unique3; // size = 0x20 + +#define CAM_FUNCDATA_UNIQ3(yOffset, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ Vec3f initalPos; + /* 0x0C */ s16 animTimer; + /* 0x10 */ Linef sceneCamPosPlayerLine; +} Unique0Anim; // size = 0x28 + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Unique0Anim anim; +} Unique0Params; // size = 0x2C + +typedef struct { + /* 0x0 */ DoorParams doorParams; + /* 0xC */ Unique0Params uniq0; +} Unique0; // size = 0x38 + +typedef struct { + /* 0x0 */ s16 interfaceFlags; +} Unique6; // size = 0x4 + +typedef union { + /* 0x0 */ Vec3s unk_00; +} Unique7Unk8; // size = 0x8 + +typedef struct { + /* 0x0 */ f32 fov; + /* 0x4 */ s16 interfaceFlags; + /* 0x6 */ s16 align; + /* 0x8 */ Unique7Unk8 unk_08; // unk_08 goes unused. +} Unique7; // size = 0x10 + +#define CAM_FUNCDATA_UNIQ7(fov, flags) \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +/** initFlags + * & 0x00FF = atInitFlags + * & 0xFF00 = eyeInitFlags + * 0x1: Direct Copy of atTargetInit + * if initFlags & 0x6060: use head for focus point + * 0x2: Add atTargetInit to view's lookAt + * if initFlags & 0x6060: use world for focus point + * 0x3: Add atTargetInit to camera's at + * 0x4: Don't update targets? + * 0x8: flag to use atTagetInit as f32 pitch, yaw, r + * 0x10: ? unused + * 0x20: focus on player +*/ +typedef struct { + /* 0x00 */ u8 actionFlags; + /* 0x01 */ u8 unk_01; + /* 0x02 */ s16 initFlags; + /* 0x04 */ s16 timerInit; + /* 0x06 */ s16 rollTargetInit; + /* 0x08 */ f32 fovTargetInit; + /* 0x0C */ f32 lerpStepScale; + /* 0x10 */ Vec3f atTargetInit; + /* 0x1C */ Vec3f eyeTargetInit; +} OnePointCsFull; /* size = 0x28 */ + +typedef struct { + /* 0x00 */ OnePointCsFull* curKeyFrame; + /* 0x04 */ Vec3f atTarget; + /* 0x10 */ Vec3f eyeTarget; + /* 0x1C */ Vec3f playerPos; + /* 0x28 */ f32 fovTarget; + /* 0x2C */ VecSph atEyeOffsetTarget; + /* 0x34 */ s16 rollTarget; + /* 0x36 */ s16 curKeyFrameIdx; + /* 0x38 */ s16 unk_38; + /* 0x3A */ s16 isNewKeyFrame; + /* 0x3C */ s16 keyFrameTimer; +} Unique9Anim; // size = 0x3E + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Unique9Anim anim; +} Unique9; // size = 0x40 + +typedef struct { + /* 0x0 */ s32 keyFrameCnt; + /* 0x4 */ OnePointCsFull* keyFrames; + /* 0x8 */ Unique9 uniq9; +} Unique9OnePointCs; // size = 0x48 + +typedef struct { + /* 0x0 */ f32 curFrame; + /* 0x4 */ s16 keyframe; +} Demo1Anim; // size = 0x14 + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Demo1Anim anim; +} Demo1; // size = 0x18 + +typedef struct { + /* 0x00 */ Vec3f initialAt; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ s16 animFrame; + /* 0x12 */ s16 yawDir; +} Demo3Anim; // size = 0x14 + +typedef struct { + /* 0x0 */ f32 fov; + /* 0x4 */ f32 unk_04; // unused + /* 0x8 */ s16 interfaceFlags; + /* 0xC */ Demo3Anim anim; +} Demo3; // size = 0x20 + +#define CAM_FUNCDATA_DEMO3(fov, atLerpStepScale, flags) \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ s16 animTimer; + /* 0x4 */ Vec3f atTarget; +} Demo6Anim; // size = 0x10 + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x2 */ s16 unk_02; + /* 0x4 */ Demo6Anim anim; +} Demo6; // size = 0x14 + +typedef struct { + /* 0x0 */ f32 curFrame; + /* 0x4 */ s16 keyframe; + /* 0x6 */ s16 doLERPAt; + /* 0x8 */ s16 finishAction; + /* 0xA */ s16 animTimer; +} Demo9Anim; // size = 0xC + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Demo9Anim anim; +} Demo9; // size = 0x10 + +typedef struct { + /* 0x0 */ CutsceneCameraPoint* atPoints; + /* 0x4 */ CutsceneCameraPoint* eyePoints; + /* 0x8 */ s16 actionParameters; + /* 0xA */ s16 initTimer; +} OnePointCsCamera; // size = 0xC + +typedef struct { + /* 0x0 */ OnePointCsCamera onePointCs; + /* 0xC */ Demo9 demo9; +} Demo9OnePointCs; // size = 0x1C + +typedef struct { + /* 0x0 */ f32 lerpAtScale; + /* 0x4 */ s16 interfaceFlags; +} Special0; // size = 0x8 + +#define CAM_FUNCDATA_SPEC0(yawUpdateRateTarget, flags) \ + { yawUpdateRateTarget, CAM_DATA_YAW_UPDATE_RATE_TARGET }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ s16 initalTimer; +} Special4; // size = 0x4 + +typedef struct { + /* 0x0 */ s16 animTimer; +} Special5Anim; // size = 0x4 + +typedef struct { + /* 0x00 */ f32 yOffset; + /* 0x04 */ f32 eyeDist; + /* 0x08 */ f32 minDistForRot; + /* 0x0C */ f32 fovTarget; + /* 0x10 */ f32 atMaxLERPScale; + /* 0x14 */ s16 timerInit; + /* 0x16 */ s16 pitch; + /* 0x18 */ s16 interfaceFlags; + /* 0x1A */ s16 unk_1A; + /* 0x1C */ Special5Anim anim; +} Special5; // size = 0x20 + +#define CAM_FUNCDATA_SPEC5(yOffset, eyeDist, eyeDistNext, unk_22, pitchTarget, fov, atLerpStepScale, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { unk_22, CAM_DATA_UNK_22 }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { flags, CAM_DATA_FLAGS } + +// Uses incorrect CAM_DATA values +#define CAM_FUNCDATA_SPEC5_ALT(yOffset, eyeDist, eyeDistNext, pitchTarget, fov, atLerpStepScale, unk_22, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { eyeDist, CAM_DATA_EYE_DIST }, \ + { eyeDistNext, CAM_DATA_EYE_DIST_NEXT }, \ + { pitchTarget, CAM_DATA_PITCH_TARGET }, \ + { fov, CAM_DATA_FOV }, \ + { atLerpStepScale, CAM_DATA_AT_LERP_STEP_SCALE }, \ + { unk_22, CAM_DATA_UNK_22 }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x0 */ s16 idx; +} Special7; // size = 0x4 + +typedef struct { + /* 0x0 */ f32 initalPlayerY; + /* 0x4 */ s16 animTimer; +} Special6Anim; // size = 0x8 + +typedef struct { + /* 0x0 */ s16 interfaceFlags; + /* 0x4 */ Special6Anim anim; +} Special6; // size = 0xC + +typedef struct { + /* 0x0 */ s16 targetYaw; +} Special9Anim; // size = 0x2 + +typedef struct { + /* 0x0 */ f32 yOffset; + /* 0x4 */ f32 unk_04; + /* 0x8 */ s16 interfaceFlags; + /* 0xA */ s16 unk_0A; + /* 0xC */ Special9Anim anim; +} Special9Params; // size = 0x10 + +typedef struct { + /* 0x0 */ DoorParams doorParams; + /* 0xC */ Special9Params params; +} Special9; // size = 0x1C + +#define CAM_FUNCDATA_SPEC9(yOffset, fov, flags) \ + { yOffset, CAM_DATA_Y_OFFSET }, \ + { fov, CAM_DATA_FOV }, \ + { flags, CAM_DATA_FLAGS } + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f norm; + /* 0x18 */ CollisionPoly* poly; + /* 0x1C */ VecSph sphNorm; + /* 0x24 */ s32 bgId; +} CamColChk; // size = 0x28 + +typedef struct { + /* 0x000 */ char paramData[0x50]; + /* 0x050 */ Vec3f at; + /* 0x05C */ Vec3f eye; + /* 0x068 */ Vec3f up; + /* 0x074 */ Vec3f eyeNext; + /* 0x080 */ Vec3f skyboxOffset; + /* 0x08C */ struct GlobalContext* globalCtx; + /* 0x090 */ struct Player* player; + /* 0x094 */ PosRot playerPosRot; + /* 0x0A8 */ struct Actor* target; + /* 0x0AC */ PosRot targetPosRot; + /* 0x0C0 */ f32 rUpdateRateInv; + /* 0x0C4 */ f32 pitchUpdateRateInv; + /* 0x0C8 */ f32 yawUpdateRateInv; + /* 0x0CC */ f32 xzOffsetUpdateRate; + /* 0x0D0 */ f32 yOffsetUpdateRate; + /* 0x0D4 */ f32 fovUpdateRate; + /* 0x0D8 */ f32 xzSpeed; + /* 0x0DC */ f32 dist; + /* 0x0E0 */ f32 speedRatio; + /* 0x0E4 */ Vec3f posOffset; + /* 0x0F0 */ Vec3f playerPosDelta; + /* 0x0FC */ f32 fov; + /* 0x100 */ f32 atLERPStepScale; + /* 0x104 */ f32 playerGroundY; + /* 0x108 */ Vec3f floorNorm; + /* 0x114 */ f32 waterYPos; + /* 0x118 */ s32 waterPrevCamIdx; + /* 0x11C */ s32 waterPrevCamSetting; + /* 0x120 */ s32 waterQuakeId; + /* 0x124 */ void* data0; + /* 0x128 */ void* data1; + /* 0x12C */ s16 data2; + /* 0x12E */ s16 data3; + /* 0x130 */ s16 uid; + /* 0x132 */ char unk_132[2]; + /* 0x134 */ Vec3s inputDir; + /* 0x13A */ Vec3s camDir; + /* 0x140 */ s16 status; + /* 0x142 */ s16 setting; + /* 0x144 */ s16 mode; + /* 0x146 */ s16 bgCheckId; + /* 0x148 */ s16 camDataIdx; + /* 0x14A */ s16 unk_14A; + /* 0x14C */ s16 unk_14C; + /* 0x14E */ s16 childCamIdx; + /* 0x150 */ s16 unk_150; + /* 0x152 */ s16 unk_152; + /* 0x154 */ s16 prevSetting; + /* 0x156 */ s16 nextCamDataIdx; + /* 0x158 */ s16 nextBGCheckId; + /* 0x15A */ s16 roll; + /* 0x15C */ s16 paramFlags; + /* 0x15E */ s16 animState; + /* 0x160 */ s16 timer; + /* 0x162 */ s16 parentCamIdx; + /* 0x164 */ s16 thisIdx; + /* 0x166 */ s16 prevCamDataIdx; + /* 0x168 */ s16 csId; + /* 0x16A */ s16 unk_16A; +} Camera; // size = 0x16C + +/** + * Debug Camera +*/ + +typedef struct { + /* 0x0000 */ s16 mode; + /* 0x0002 */ s16 nFrames; + /* 0x0004 */ s16 nPoints; + /* 0x0006 */ s16 unkIdx; + /* 0x0008 */ s16 unk_08; + /* 0x000A */ s16 unk_0A; + /* 0x000C */ s32 unk_0C; // bool: indicates position vs lookAt? + /* 0x0010 */ char unk_10[0x14]; + /* 0x0024 */ CutsceneCameraPoint position[129]; + /* 0x0834 */ CutsceneCameraPoint lookAt[129]; + /* 0x1044 */ s16 demoCtrlMenu; + /* 0x1046 */ s16 demoCtrlActionIdx; // e (?), s (save), l (load), c (clear) + /* 0x1048 */ s16 demoCtrlToggleSwitch; + /* 0x104A */ Vec3s unk_104A; +} DbCameraSub; // size = 0x1050 + +typedef struct { + /* 0x00 */ s32 unk_00; + /* 0x04 */ Vec3f at; + /* 0x10 */ Vec3f eye; + /* 0x1C */ Vec3f unk_1C; + /* 0x28 */ char unk_28[0xC]; + /* 0x34 */ s32 unk_34; + /* 0x38 */ s32 unk_38; + /* 0x3C */ s32 unk_3C; // bool + /* 0x40 */ s32 unk_40; + /* 0x44 */ s32 unk_44; + /* 0x48 */ f32 fov; + /* 0x4C */ s16 roll; + /* 0x4E */ char unk_4E[0x2]; + /* 0x50 */ f32 rollDegrees; + /* 0x54 */ Vec3f unk_54; + /* 0x60 */ Vec3f unk_60; + /* 0x6C */ Vec3f unk_6C; + /* 0x78 */ s16 unk_78; + /* 0x7A */ s16 unk_7A; + /* 0x7C */ DbCameraSub sub; +} DbCamera; // size = 0x10CC + +typedef struct { + /* 0x00 */ char letter; + /* 0x01 */ u8 unk_01; + /* 0x02 */ s16 mode; + /* 0x04 */ CutsceneCameraPoint* position; + /* 0x08 */ CutsceneCameraPoint* lookAt; + /* 0x0C */ s16 nFrames; + /* 0x0E */ s16 nPoints; +} DbCameraCut; // size = 0x10 + +typedef struct { + /* 0x00 */ f32 curFrame; + /* 0x04 */ f32 unk_04; // frame count? + /* 0x08 */ s16 keyframe; + /* 0x0A */ s16 unk_0A; + /* 0x0C */ s16 unk_0C; + /* 0x10 */ Vec3f positionPos; // confusing name + /* 0x1C */ Vec3f lookAtPos; + /* 0x28 */ f32 roll; + /* 0x2C */ f32 fov; +} DbCameraAnim; // size = 0x30 + +#endif diff --git a/soh/include/z64collision_check.h b/soh/include/z64collision_check.h new file mode 100644 index 000000000..4dad0acc7 --- /dev/null +++ b/soh/include/z64collision_check.h @@ -0,0 +1,390 @@ +#ifndef Z_COLLISION_CHECK_H +#define Z_COLLISION_CHECK_H + +#define COLLISION_CHECK_AT_MAX 50 +#define COLLISION_CHECK_AC_MAX 60 +#define COLLISION_CHECK_OC_MAX 50 +#define COLLISION_CHECK_OC_LINE_MAX 3 + +// From z64.h +struct Actor; + +typedef struct { + /* 0x00 */ struct Actor* actor; // Attached actor + /* 0x04 */ struct Actor* at; // Actor attached to what it collided with as an AT collider. + /* 0x08 */ struct Actor* ac; // Actor attached to what it collided with as an AC collider. + /* 0x0C */ struct Actor* oc; // Actor attached to what it collided with as an OC collider. + /* 0x10 */ u8 atFlags; // Information flags for AT collisions. + /* 0x11 */ u8 acFlags; // Information flags for AC collisions. + /* 0x12 */ u8 ocFlags1; // Information flags for OC collisions. + /* 0x13 */ u8 ocFlags2; // Flags related to which colliders it can OC collide with. + /* 0x14 */ u8 colType; // Determines hitmarks and sound effects during AC collisions. + /* 0x15 */ u8 shape; // JntSph, Cylinder, Tris, or Quad +} Collider; // size = 0x18 + +typedef struct { + /* 0x00 */ u8 colType; // Determines hitmarks and sound effects during AC collisions. + /* 0x01 */ u8 atFlags; // Information flags for AT collisions. + /* 0x02 */ u8 acFlags; // Information flags for OC collisions. + /* 0x03 */ u8 ocFlags1; // Information flags for OC collisions. + /* 0x04 */ u8 ocFlags2; // Flags related to which colliders it can OC collide with. + /* 0x05 */ u8 shape; // JntSph, Cylinder, Tris, or Quad +} ColliderInit; // size = 0x06 + +typedef struct { + /* 0x00 */ u8 colType; // Determines hitmarks and sound effects during AC collisions. + /* 0x01 */ u8 atFlags; // Information flags for AT collisions. + /* 0x02 */ u8 acFlags; // Information flags for AC collisions. + /* 0x03 */ u8 ocFlags1; // Information flags for OC collisions. + /* 0x04 */ u8 shape; // JntSph, Cylinder, Tris, or Quad +} ColliderInitType1; // size = 0x05 + +typedef struct { + /* 0x00 */ struct Actor* actor; + /* 0x04 */ u8 atFlags; // Information flags for AT collisions. + /* 0x05 */ u8 acFlags; // Information flags for AC collisions. + /* 0x06 */ u8 ocFlags1; // Information flags for OC collisions. + /* 0x07 */ u8 shape; // JntSph, Cylinder, Tris, or Quad +} ColliderInitToActor; // size = 0x08 + +typedef struct { + /* 0x00 */ u32 dmgFlags; // Toucher damage type flags. + /* 0x04 */ u8 effect; // Damage Effect (Knockback, Fire, etc.) + /* 0x05 */ u8 damage; // Damage or Stun Timer +} ColliderTouch; // size = 0x08 + +typedef struct { + /* 0x00 */ u32 dmgFlags; // Bumper damage type flags. + /* 0x04 */ u8 effect; // Damage Effect (Knockback, Fire, etc.) + /* 0x05 */ u8 defense; // Damage Resistance + /* 0x06 */ Vec3s hitPos; // Point of contact +} ColliderBump; // size = 0x0C + +typedef struct { + /* 0x00 */ u32 dmgFlags; // Bumper exclusion mask + /* 0x04 */ u8 effect; // Damage Effect (Knockback, Fire, etc.) + /* 0x05 */ u8 defense; // Damage Resistance +} ColliderBumpInit; // size = 0x08 + +typedef struct ColliderInfo { + /* 0x00 */ ColliderTouch toucher; // Damage properties when acting as an AT collider + /* 0x08 */ ColliderBump bumper; // Damage properties when acting as an AC collider + /* 0x14 */ u8 elemType; // Affects sfx reaction when attacked by Link and hookability. Full purpose unknown. + /* 0x15 */ u8 toucherFlags; // Information flags for AT collisions + /* 0x16 */ u8 bumperFlags; // Information flags for AC collisions + /* 0x17 */ u8 ocElemFlags; // Information flags for OC collisions + /* 0x18 */ Collider* atHit; // object touching this element's AT collider + /* 0x1C */ Collider* acHit; // object touching this element's AC collider + /* 0x20 */ struct ColliderInfo* atHitInfo; // element that hit the AT collider + /* 0x24 */ struct ColliderInfo* acHitInfo; // element that hit the AC collider +} ColliderInfo; // size = 0x28 + +typedef struct { + /* 0x00 */ u8 elemType; // Affects sfx reaction when attacked by Link and hookability. Full purpose unknown. + /* 0x04 */ ColliderTouch toucher; // Damage properties when acting as an AT collider + /* 0x0C */ ColliderBumpInit bumper; // Damage properties when acting as an AC collider + /* 0x14 */ u8 toucherFlags; // Information flags for AT collisions + /* 0x15 */ u8 bumperFlags; // Information flags for AC collisions + /* 0x16 */ u8 ocElemFlags; // Information flags for OC collisions +} ColliderInfoInit; // size = 0x18 + +typedef struct { + /* 0x00 */ Sphere16 modelSphere; // model space sphere + /* 0x08 */ Sphere16 worldSphere; // world space sphere + /* 0x10 */ f32 scale; // world space sphere = model * scale * 0.01 + /* 0x14 */ u8 limb; // attached limb +} ColliderJntSphElementDim; // size = 0x18 + +typedef struct { + /* 0x00 */ u8 limb; // attached limb + /* 0x02 */ Sphere16 modelSphere; // model space sphere + /* 0x0A */ s16 scale; // world space sphere = model * scale * 0.01 +} ColliderJntSphElementDimInit; // size = 0x0C + +typedef struct { + /* 0x00 */ ColliderInfo info; + /* 0x28 */ ColliderJntSphElementDim dim; +} ColliderJntSphElement; // size = 0x40 + +typedef struct { + /* 0x00 */ ColliderInfoInit info; + /* 0x18 */ ColliderJntSphElementDimInit dim; +} ColliderJntSphElementInit; // size = 0x24 + +typedef struct { + /* 0x00 */ Collider base; + /* 0x18 */ s32 count; + /* 0x1C */ ColliderJntSphElement* elements; +} ColliderJntSph; // size = 0x20 + +typedef struct { + /* 0x00 */ ColliderInit base; + /* 0x08 */ s32 count; + /* 0x0C */ ColliderJntSphElementInit* elements; +} ColliderJntSphInit; // size = 0x10 + +typedef struct { + /* 0x00 */ ColliderInitType1 base; + /* 0x08 */ s32 count; + /* 0x0C */ ColliderJntSphElementInit* elements; +} ColliderJntSphInitType1; // size = 0x10 + +typedef struct { + /* 0x00 */ ColliderInitToActor base; + /* 0x08 */ s32 count; + /* 0x0C */ ColliderJntSphElementInit* elements; +} ColliderJntSphInitToActor; // size = 0x10 + +typedef struct { + /* 0x00 */ Collider base; + /* 0x18 */ ColliderInfo info; + /* 0x40 */ Cylinder16 dim; +} ColliderCylinder; // size = 0x4C + +typedef struct { + /* 0x00 */ ColliderInit base; + /* 0x08 */ ColliderInfoInit info; + /* 0x20 */ Cylinder16 dim; +} ColliderCylinderInit; // size = 0x2C + +typedef struct { + /* 0x00 */ ColliderInitType1 base; + /* 0x08 */ ColliderInfoInit info; + /* 0x20 */ Cylinder16 dim; +} ColliderCylinderInitType1; // size = 0x2C + +typedef struct { + /* 0x00 */ ColliderInitToActor base; + /* 0x08 */ ColliderInfoInit info; + /* 0x20 */ Cylinder16 dim; +} ColliderCylinderInitToActor; // size = 0x2C + +typedef struct { + /* 0x00 */ Vec3f vtx[3]; +} ColliderTrisElementDimInit; // size = 0x24 + +typedef struct { + /* 0x00 */ ColliderInfo info; + /* 0x28 */ TriNorm dim; +} ColliderTrisElement; // size = 0x5C + +typedef struct { + /* 0x00 */ ColliderInfoInit info; + /* 0x18 */ ColliderTrisElementDimInit dim; +} ColliderTrisElementInit; // size = 0x3C + +typedef struct { + /* 0x00 */ Collider base; + /* 0x18 */ s32 count; + /* 0x1C */ ColliderTrisElement* elements; +} ColliderTris; // size = 0x20 + +typedef struct { + /* 0x00 */ ColliderInit base; + /* 0x08 */ s32 count; + /* 0x0C */ ColliderTrisElementInit* elements; +} ColliderTrisInit; // size = 0x10 + +typedef struct { + /* 0x00 */ ColliderInitType1 base; + /* 0x08 */ s32 count; + /* 0x0C */ ColliderTrisElementInit* elements; +} ColliderTrisInitType1; // size = 0x10 + +typedef struct { + /* 0x00 */ Vec3f quad[4]; + /* 0x30 */ Vec3s dcMid; // midpoint of vectors d, c + /* 0x36 */ Vec3s baMid; // midpoint of vectors b, a + /* 0x3C */ f32 acDist; // distance to nearest AC collision this frame. +} ColliderQuadDim; // size = 0x40 + +typedef struct { + /* 0x00 */ Vec3f quad[4]; +} ColliderQuadDimInit; // size = 0x30 + +typedef struct { + /* 0x00 */ ColliderInfo info; + /* 0x24 */ ColliderQuadDim dim; +} ColliderQuadElement; // size = 0x68 + +typedef struct { + /* 0x00 */ Collider base; + /* 0x18 */ ColliderInfo info; + /* 0x40 */ ColliderQuadDim dim; +} ColliderQuad; // size = 0x80 + +typedef struct { + /* 0x00 */ ColliderInit base; + /* 0x08 */ ColliderInfoInit info; + /* 0x20 */ ColliderQuadDimInit dim; +} ColliderQuadInit; // size = 0x50 + +typedef struct { + /* 0x00 */ ColliderInitType1 base; + /* 0x08 */ ColliderInfoInit info; + /* 0x20 */ ColliderQuadDimInit dim; +} ColliderQuadInitType1; // size = 0x50 + +typedef struct { + /* 0x00 */ Linef line; + /* 0x18 */ u16 ocFlags; +} OcLine; // size = 0x1C + +typedef enum { + /* 0 */ COLTYPE_HIT0, // Blue blood, white hitmark + /* 1 */ COLTYPE_HIT1, // No blood, dust hitmark + /* 2 */ COLTYPE_HIT2, // Green blood, dust hitmark + /* 3 */ COLTYPE_HIT3, // No blood, white hitmark + /* 4 */ COLTYPE_HIT4, // Water burst, no hitmark + /* 5 */ COLTYPE_HIT5, // No blood, red hitmark + /* 6 */ COLTYPE_HIT6, // Green blood, white hitmark + /* 7 */ COLTYPE_HIT7, // Red blood, white hitmark + /* 8 */ COLTYPE_HIT8, // Blue blood, red hitmark + /* 9 */ COLTYPE_METAL, + /* 10 */ COLTYPE_NONE, + /* 11 */ COLTYPE_WOOD, + /* 12 */ COLTYPE_HARD, + /* 13 */ COLTYPE_TREE +} ColliderType; + +typedef enum { + /* 0 */ COLSHAPE_JNTSPH, + /* 1 */ COLSHAPE_CYLINDER, + /* 2 */ COLSHAPE_TRIS, + /* 3 */ COLSHAPE_QUAD, + /* 4 */ COLSHAPE_INVALID +} ColliderShape; + +/** + * Affects the sound Link's sword makes when hitting it, hookability, + * and possibly other things. It's definitely not flags, as all checks + * are == or !=. Will probably need more actors decomped to truly + * understand what this is. + */ +typedef enum { + /* 0 */ ELEMTYPE_UNK0, + /* 1 */ ELEMTYPE_UNK1, + /* 2 */ ELEMTYPE_UNK2, + /* 3 */ ELEMTYPE_UNK3, + /* 4 */ ELEMTYPE_UNK4, + /* 5 */ ELEMTYPE_UNK5, + /* 6 */ ELEMTYPE_UNK6, + /* 7 */ ELEMTYPE_UNK7 +} ElementType; + +#define AT_NONE 0 // No flags set. Cannot have AT collisions when set as AT +#define AT_ON (1 << 0) // Can have AT collisions when set as AT +#define AT_HIT (1 << 1) // Had an AT collision +#define AT_BOUNCED (1 << 2) // Had an AT collision with an AC_HARD collider +#define AT_TYPE_PLAYER (1 << 3) // Has player-aligned damage +#define AT_TYPE_ENEMY (1 << 4) // Has enemy-aligned damage +#define AT_TYPE_OTHER (1 << 5) // Has non-aligned damage +#define AT_SELF (1 << 6) // Can have AT collisions with colliders attached to the same actor +#define AT_TYPE_ALL (AT_TYPE_PLAYER | AT_TYPE_ENEMY | AT_TYPE_OTHER) // Has all three damage alignments + +#define AC_NONE 0 // No flags set. Cannot have AC collisions when set as AC +#define AC_ON (1 << 0) // Can have AC collisions when set as AC +#define AC_HIT (1 << 1) // Had an AC collision +#define AC_HARD (1 << 2) // Causes AT colliders to bounce off it +#define AC_TYPE_PLAYER AT_TYPE_PLAYER // Takes player-aligned damage +#define AC_TYPE_ENEMY AT_TYPE_ENEMY // Takes enemy-aligned damage +#define AC_TYPE_OTHER AT_TYPE_OTHER // Takes non-aligned damage +#define AC_NO_DAMAGE (1 << 6) // Collider does not take damage +#define AC_BOUNCED (1 << 7) // Caused an AT collider to bounce off it +#define AC_TYPE_ALL (AC_TYPE_PLAYER | AC_TYPE_ENEMY | AC_TYPE_OTHER) // Takes damage from all three alignments + +#define OC1_NONE 0 // No flags set. Cannot have OC collisions when set as OC +#define OC1_ON (1 << 0) // Can have OC collisions when set as OC +#define OC1_HIT (1 << 1) // Had an OC collision +#define OC1_NO_PUSH (1 << 2) // Does not push other colliders away during OC collisions +#define OC1_TYPE_PLAYER (1 << 3) // Can have OC collisions with OC type player +#define OC1_TYPE_1 (1 << 4) // Can have OC collisions with OC type 1 +#define OC1_TYPE_2 (1 << 5) // Can have OC collisions with OC type 2 +#define OC1_TYPE_ALL (OC1_TYPE_PLAYER | OC1_TYPE_1 | OC1_TYPE_2) // Can have collisions with all three OC types + +#define OC2_NONE 0 // No flags set. Has no OC type +#define OC2_HIT_PLAYER (1 << 0) // Had an OC collision with OC type player +#define OC2_UNK1 (1 << 1) // Prevents OC collisions with OC2_UNK2. Some horses and toki_sword have it. +#define OC2_UNK2 (1 << 2) // Prevents OC collisions with OC2_UNK1. Nothing has it. +#define OC2_TYPE_PLAYER OC1_TYPE_PLAYER // Has OC type player +#define OC2_TYPE_1 OC1_TYPE_1 // Has OC type 1 +#define OC2_TYPE_2 OC1_TYPE_2 // Has OC type 2 +#define OC2_FIRST_ONLY (1 << 6) // Skips AC checks on elements after the first collision. Only used by Ganon + +#define TOUCH_NONE 0 // No flags set. Cannot have AT collisions +#define TOUCH_ON (1 << 0) // Can have AT collisions +#define TOUCH_HIT (1 << 1) // Had an AT collision +#define TOUCH_NEAREST (1 << 2) // If a Quad, only collides with the closest bumper +#define TOUCH_SFX_NORMAL (0 << 3) // Hit sound effect based on AC collider's type +#define TOUCH_SFX_HARD (1 << 3) // Always uses hard deflection sound +#define TOUCH_SFX_WOOD (2 << 3) // Always uses wood deflection sound +#define TOUCH_SFX_NONE (3 << 3) // No hit sound effect +#define TOUCH_AT_HITMARK (1 << 5) // Draw hitmarks for every AT collision +#define TOUCH_DREW_HITMARK (1 << 6) // Already drew hitmark for this frame +#define TOUCH_UNK7 (1 << 7) // Unknown purpose. Used by some enemy quads + +#define BUMP_NONE 0 // No flags set. Cannot have AC collisions +#define BUMP_ON (1 << 0) // Can have AC collisions +#define BUMP_HIT (1 << 1) // Had an AC collision +#define BUMP_HOOKABLE (1 << 2) // Can be hooked if actor has hookability flags set. +#define BUMP_NO_AT_INFO (1 << 3) // Does not give its info to the AT collider that hit it. +#define BUMP_NO_DAMAGE (1 << 4) // Does not take damage. +#define BUMP_NO_SWORD_SFX (1 << 5) // Does not have a sound when hit by player-attached AT colliders. +#define BUMP_NO_HITMARK (1 << 6) // Skips hit effects. +#define BUMP_DRAW_HITMARK (1 << 7) // Draw hitmark for AC collision this frame. + +#define OCELEM_NONE 0 // No flags set. Cannot have OC collisions +#define OCELEM_ON (1 << 0) // Can have OC collisions +#define OCELEM_HIT (1 << 1) // Had an OC collision +#define OCELEM_UNK3 (1 << 3) // Unknown purpose. Used by Dead Hand element 0 and Dodongo element 5 + +#define OCLINE_NONE 0 // Did not have an OcLine collision +#define OCLINE_HIT (1 << 0) // Had an OcLine collision + +#define DMG_ENTRY(damage, effect) ((damage) | ((effect) << 4)) + +// These flags are not to be used in code until we figure out how we want to format them. They are only here for reference +#define DMG_DEKU_NUT (1 << 0x00) +#define DMG_DEKU_STICK (1 << 0x01) +#define DMG_SLINGSHOT (1 << 0x02) +#define DMG_EXPLOSIVE (1 << 0x03) +#define DMG_BOOMERANG (1 << 0x04) +#define DMG_ARROW_NORMAL (1 << 0x05) +#define DMG_HAMMER_SWING (1 << 0x06) +#define DMG_HOOKSHOT (1 << 0x07) +#define DMG_SLASH_KOKIRI (1 << 0x08) +#define DMG_SLASH_MASTER (1 << 0x09) +#define DMG_SLASH_GIANT (1 << 0x0A) +#define DMG_ARROW_FIRE (1 << 0x0B) +#define DMG_ARROW_ICE (1 << 0x0C) +#define DMG_ARROW_LIGHT (1 << 0x0D) +#define DMG_ARROW_UNK1 (1 << 0x0E) +#define DMG_ARROW_UNK2 (1 << 0x0F) +#define DMG_ARROW_UNK3 (1 << 0x10) +#define DMG_MAGIC_FIRE (1 << 0x11) +#define DMG_MAGIC_ICE (1 << 0x12) +#define DMG_MAGIC_LIGHT (1 << 0x13) +#define DMG_SHIELD (1 << 0x14) +#define DMG_MIR_RAY (1 << 0x15) +#define DMG_SPIN_KOKIRI (1 << 0x16) +#define DMG_SPIN_GIANT (1 << 0x17) +#define DMG_SPIN_MASTER (1 << 0x18) +#define DMG_JUMP_KOKIRI (1 << 0x19) +#define DMG_JUMP_GIANT (1 << 0x1A) +#define DMG_JUMP_MASTER (1 << 0x1B) +#define DMG_UNKNOWN_1 (1 << 0x1C) +#define DMG_UNBLOCKABLE (1 << 0x1D) +#define DMG_HAMMER_JUMP (1 << 0x1E) +#define DMG_UNKNOWN_2 (1 << 0x1F) + +#define DMG_SLASH (DMG_SLASH_KOKIRI | DMG_SLASH_MASTER | DMG_SLASH_GIANT) +#define DMG_SPIN_ATTACK (DMG_SPIN_KOKIRI | DMG_SPIN_MASTER | DMG_SPIN_GIANT) +#define DMG_JUMP_SLASH (DMG_JUMP_KOKIRI | DMG_JUMP_MASTER | DMG_JUMP_GIANT) +#define DMG_SWORD (DMG_SLASH | DMG_SPIN_ATTACK | DMG_JUMP_SLASH) +#define DMG_HAMMER (DMG_HAMMER_SWING | DMG_HAMMER_JUMP) +#define DMG_FIRE (DMG_ARROW_FIRE | DMG_MAGIC_FIRE) +#define DMG_ARROW (DMG_ARROW_NORMAL | DMG_ARROW_FIRE | DMG_ARROW_ICE | DMG_ARROW_LIGHT | DMG_ARROW_UNK1 | DMG_ARROW_UNK2 | DMG_ARROW_UNK3) +#define DMG_RANGED (DMG_ARROW | DMG_HOOKSHOT | DMG_SLINGSHOT) +#define DMG_DEFAULT ~(DMG_SHIELD | DMG_MIR_RAY) + +#endif diff --git a/soh/include/z64cutscene.h b/soh/include/z64cutscene.h new file mode 100644 index 000000000..562561a08 --- /dev/null +++ b/soh/include/z64cutscene.h @@ -0,0 +1,285 @@ +#ifndef Z64CUTSCENE_H +#define Z64CUTSCENE_H + +#include "ultra64.h" + +typedef struct { + /* 0x00 */ u16 entrance; // entrance index upon which the cutscene should trigger + /* 0x02 */ u8 ageRestriction; // 0 for adult only, 1 for child only, 2 for both ages + /* 0x03 */ u8 flag; // eventChkInf flag bound to the entrance cutscene + /* 0x04 */ void* segAddr; // segment offset location of the cutscene +} EntranceCutscene; // size = 0x8 + +typedef struct { + /* 0x00 */ s8 continueFlag; + /* 0x01 */ s8 cameraRoll; + /* 0x02 */ u16 nextPointFrame; + /* 0x04 */ f32 viewAngle; // in degrees + /* 0x08 */ Vec3s pos; +} CutsceneCameraPoint; // size = 0x10 + +typedef struct { + /* 0x00 */ Vec3f at; + /* 0x0C */ Vec3f eye; + /* 0x18 */ s16 roll; + /* 0x1A */ s16 fov; +} CutsceneCameraAngle; // size = 0x1C + +typedef struct { + /* 0x0 */ CutsceneCameraPoint* atPoints; + /* 0x4 */ CutsceneCameraPoint* eyePoints; + /* 0x8 */ s16 relativeToPlayer; +} CutsceneCameraMove; // size = 0xC + +typedef struct { + /* 0x00 */ u16 base; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdBase; // size = 0x6 + +typedef struct { + /* 0x00 */ u8 unk_00; + /* 0x01 */ u8 setting; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdEnvLighting; // size = 0x6 + +typedef struct { + /* 0x00 */ u16 sequence; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdMusicChange; // size = 0x6 + +typedef struct { + /* 0x00 */ u16 type; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; +} CsCmdMusicFade; // size = 0x6 + +typedef struct { + /* 0x00 */ u16 unk_00; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + /* 0x06 */ u8 unk_06; + /* 0x07 */ u8 unk_07; + /* 0x08 */ u8 unk_08; +} CsCmdUnknown9; // size = 0xA + +typedef struct { + /* 0x00 */ u16 unk_00; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + /* 0x06 */ u8 hour; + /* 0x07 */ u8 minute; +} CsCmdDayTime; // size = 0x8 + +typedef struct { + /* 0x00 */ u16 base; + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + /* 0x06 */ u16 type; + /* 0x08 */ u16 textId1; + /* 0x0A */ u16 textId2; +} CsCmdTextbox; // size = 0xC + +typedef struct { + /* 0x00 */ u16 action; // "dousa" + /* 0x02 */ u16 startFrame; + /* 0x04 */ u16 endFrame; + union { + /* 0x06 */ Vec3s rot; + /* 0x06 */ Vec3us urot; + }; + /* 0x0C */ Vec3i startPos; + /* 0x18 */ Vec3i endPos; + /* 0x24 */ Vec3i normal; +} CsCmdActorAction; // size = 0x30 + +typedef enum { + CS_STATE_IDLE, + CS_STATE_SKIPPABLE_INIT, + CS_STATE_SKIPPABLE_EXEC, + CS_STATE_UNSKIPPABLE_INIT, + CS_STATE_UNSKIPPABLE_EXEC +} CutsceneState; + +typedef enum { + CS_CMD_00 = 0x0000, + CS_CMD_CAM_EYE = 0x0001, + CS_CMD_CAM_AT = 0x0002, + CS_CMD_MISC = 0x0003, + CS_CMD_SET_LIGHTING = 0x0004, + CS_CMD_CAM_EYE_REL_TO_PLAYER = 0x0005, + CS_CMD_CAM_AT_REL_TO_PLAYER = 0x0006, + CS_CMD_07 = 0x0007, + CS_CMD_08 = 0x0008, + CS_CMD_09 = 0x0009, + CS_CMD_TEXTBOX = 0x0013, + CS_CMD_SET_PLAYER_ACTION = 0x000A, + CS_CMD_SET_ACTOR_ACTION_1 = 0x000F, + CS_CMD_SET_ACTOR_ACTION_2 = 0x000E, + CS_CMD_SET_ACTOR_ACTION_3 = 0x0019, + CS_CMD_SET_ACTOR_ACTION_4 = 0x001D, + CS_CMD_SET_ACTOR_ACTION_5 = 0x001E, + CS_CMD_SET_ACTOR_ACTION_6 = 0x002C, + CS_CMD_SET_ACTOR_ACTION_7 = 0x001F, + CS_CMD_SET_ACTOR_ACTION_8 = 0x0031, + CS_CMD_SET_ACTOR_ACTION_9 = 0x003E, + CS_CMD_SET_ACTOR_ACTION_10 = 0x008F, + CS_CMD_SCENE_TRANS_FX = 0x002D, + CS_CMD_NOP = 0x000B, + CS_CMD_PLAYBGM = 0x0056, + CS_CMD_STOPBGM = 0x0057, + CS_CMD_FADEBGM = 0x007C, + CS_CMD_SETTIME = 0x008C, + CS_CMD_TERMINATOR = 0x03E8, + CS_CMD_END = 0xFFFF +} CutsceneCmd; + +/** + * Special type for blocks of cutscene data, asm-processor checks + * arrays for CutsceneData type and converts floats within the array + * to their IEEE-754 representation. The array must close with }; + * on its own line. + * + * Files that contain this type that are included in other C files + * must include an 'EARLY' qualifier to inform asm-processor that it + * must recursively process that include. + * + * Example: #include "file.c" EARLY + */ +typedef union CutsceneData { + s32 i; + f32 f; + s16 s[2]; + s8 b[4]; +} CutsceneData; + +#define CS_CMD_CONTINUE 0 +#define CS_CMD_STOP -1 + +// TODO confirm correctness, clarify names +typedef enum { + /* 0x00 */ INVALID_DESTINATION_0, + /* 0x01 */ CUTSCENE_MAP_GANON_HORSE, + /* 0x02 */ CUTSCENE_MAP_THREE_GODESSES_POST_DEKU_TREE, + /* 0x03 */ GERUDO_VALLEY_DIN, + /* 0x04 */ DEATH_MOUNTAIN_TRAIL_NAYRU, + /* 0x05 */ KOKIRI_FOREST_FARORE, + /* 0x06 */ CUTSCENE_MAP_TRIFORCE_CREATION, + /* 0x07 */ KOKIRI_FOREST_RECEIVE_KOKIRI_EMERALD, + /* 0x08 */ TEMPLE_OF_TIME_AFTER_USE_MS, + /* 0x09 */ GERUDO_VALLEY_DIN_2, + /* 0x0A */ LINKS_HOUSE_INTRO, + /* 0x0B */ KOKIRI_FOREST_INTRO, + /* 0x0C */ DEATH_MOUNTAIN_TRAIL_AFTER_GORON_RUBY, + /* 0x0D */ ZORAS_FOUNTAIN_AFTER_ZORAS_SAPPHIRE, + /* 0x0E */ KOKIRI_FOREST_AFTER_KOKIRI_EMERALD, + /* 0x0F */ TEMPLE_OF_TIME_KOKIRI_EMERALD, //unused + /* 0x10 */ TEMPLE_OF_TIME_GORON_RUBY, //unused + /* 0x11 */ TEMPLE_OF_TIME_ZORAS_SAPPHIRE, //unused + /* 0x12 */ TEMPLE_OF_TIME_AFTER_USE_MS_FIRST, + /* 0x13 */ DEATH_MOUNTAIN_TRAIL_AFTER_INTRO, + /* 0x14 */ INVALID_DESTINATION_14, + /* 0x15 */ LAKE_HYLIA_WATER_RISES, + /* 0x16 */ DESERT_COLOSSUS_REQUIEM, + /* 0x17 */ CUTSCENE_MAP_CURSE_YOU, + /* 0x18 */ JABU_JABU_INTRO, + /* 0x19 */ CHAMBER_OF_SAGES_LIGHT_MEDALLION, + /* 0x1A */ TEMPLE_OF_TIME_KOKIRI_EMERALD_2, //duplicate of 0x000F + /* 0x1B */ TEMPLE_OF_TIME_GORON_RUBY_2, //duplicate of 0x0010 + /* 0x1C */ TEMPLE_OF_TIME_ZORAS_SAPPHIRE_2, //duplicate of 0x0011 + /* 0x1D */ CHAMBER_OF_SAGES_FOREST_MEDALLION, + /* 0x1E */ CHAMBER_OF_SAGES_FIRE_MEDALLION, + /* 0x1F */ CHAMBER_OF_SAGES_WATER_MEDALLION, + /* 0x20 */ HYRULE_FIELD_FLASHBACK, //lacs part 4 + /* 0x21 */ HYRULE_FIELD_AFTER_LAKE_HYLIA_OWL, + /* 0x22 */ CUTSCENE_MAP_GANON_AFTER_USE_MS, + /* 0x23 */ HYRULE_FIELD_INTRO_ZELDA_ESCAPE, + /* 0x24 */ INVALID_DESTINATION_24, + /* 0x25 */ INVALID_DESTINATION_25, + /* 0x26 */ CUTSCENE_MAP_SHEIKAH_LEGEND, //lacs part 2 + /* 0x27 */ TEMPLE_OF_TIME_ZELDA_REVEAL, //lacs part 3 + /* 0x28 */ TEMPLE_OF_TIME_GET_LIGHT_ARROWS, //lacs part 5 + /* 0x29 */ LAKE_HYLIA_AFTER_BLUE_WARP, + /* 0x2A */ KAKARIKO_VILLAGE_DRAIN_WELL, + /* 0x2B */ WINDMILL_AFTER_DRAIN_WELL, + /* 0x2C */ TEMPLE_OF_TIME_AFTER_DOOR_OF_TIME_OPENS, + /* 0x2D */ INVALID_DESTINATION_2D, + /* 0x2E */ TEMPLE_OF_TIME_AFTER_USE_MS_FIRST_2, // duplicate of 0x0012 + /* 0x2F */ KAKARIKO_VILLAGE_NOCTURNE_PART_2, + /* 0x30 */ DESERT_COLOSSUS_AFTER_REQUIEM, + /* 0x31 */ TEMPLE_OF_TIME_AFTER_LIGHT_ARROWS, + /* 0x32 */ KAKARIKO_VILLAGE_AFTER_NOCTURNE, + /* 0x33 */ HYRULE_FIELD_IMPA_ESCORT_CS, + /* 0x34 */ TEMPLE_OF_TIME_SONG_OF_TIME, + /* 0x35 */ HYRULE_FIELD_AFTER_SONG_OF_TIME, + /* 0x36 */ GERUDO_VALLEY_CREDITS, + /* 0x37 */ GERUDO_FORTRESS_CREDITS, + /* 0x38 */ KAKARIKO_VILLAGE_CREDITS, + /* 0x39 */ DEATH_MOUNTAIN_TRAIL_CREDITS_1, + /* 0x3A */ GORON_CITY_CREDITS, // unused? + /* 0x3B */ LAKE_HYLIA_CREDITS, + /* 0x3C */ ZORAS_FOUNTAIN_CREDITS, // unused + /* 0x3D */ ZORAS_DOMAIN_CREDITS, + /* 0x3E */ KOKIRI_FOREST_CREDITS_1, + /* 0x3F */ KOKIRI_FOREST_CREDITS_2, + /* 0x40 */ HYRULE_FIELD_CREDITS, + /* 0x41 */ LON_LON_RANCH_CREDITS_1, + /* 0x42 */ KAKARIKO_VILLAGE_AFTER_TRAIL_OWL, + /* 0x43 */ HTRULE_FIELD_UNUSED_ENTRANCE, + /* 0x44 */ CUTSCENE_MAP_FIRE, + /* 0x45 */ KOKIRI_FOREST_POST_FOREST_MEDALLION, + /* 0x46 */ DEATH_MOUNTAIN_TRAIL_CREDITS_2, + /* 0x47 */ TEMPLE_OF_TIME_CREDITS, + /* 0x48 */ ZELDAS_COURTYARD_CREDITS, + /* 0x49 */ LON_LON_RANCH_CREDITS_1_2, // duplicate of 0x0041 + /* 0x4A */ LON_LON_RANCH_CREDITS_2, + /* 0x4B */ LON_LON_RANCH_CREDITS_3, + /* 0x4C */ LON_LON_RANCH_CREDITS_4, + /* 0x4D */ LON_LON_RANCH_CREDITS_5, + /* 0x4E */ LON_LON_RANCH_CREDITS_6, + /* 0x4F */ LON_LON_RANCH_NO_CS_1, + /* 0x50 */ LON_LON_RANCH_NO_CS_2, + /* 0x51 */ LON_LON_RANCH_NO_CS_3, + /* 0x52 */ LON_LON_RANCH_NO_CS_4, + /* 0x53 */ LON_LON_RANCH_NO_CS_5, + /* 0x54 */ LON_LON_RANCH_NO_CS_6, + /* 0x55 */ LON_LON_RANCH_NO_CS_7, + /* 0x56 */ LON_LON_RANCH_NO_CS_8, + /* 0x57 */ LON_LON_RANCH_NO_CS_9, + /* 0x58 */ LON_LON_RANCH_NO_CS_10, + /* 0x59 */ LON_LON_RANCH_NO_CS_11, + /* 0x5A */ LON_LON_RANCH_NO_CS_12, + /* 0x5B */ LON_LON_RANCH_NO_CS_13, + /* 0x5C */ LON_LON_RANCH_NO_CS_14, + /* 0x5D */ LON_LON_RANCH_NO_CS_15, + /* 0x5E */ LON_LON_RANCH_NO_CS_EPONAS_SONG, + /* 0x5F */ CONDITIONAL_DESTINATION, // TODO more descriptive name? + /* 0x60 */ DESERT_COLOSSUS_SPIRIT_BLUE_WARP, + /* 0x61 */ GRAVEYARD_AFTER_SHADOW_BLUE_WARP, + /* 0x62 */ DEATH_MOUNTAIN_CRATER_AFTER_FIRE_BLUE_WARP, + /* 0x63 */ SACRED_FOREST_MEADOW_AFTER_FOREST_BLUE_WARP, + /* 0x64 */ KOKIRI_FOREST_AFTER_FOREST_BLUE_WARP, + /* 0x65 */ DESERT_COLOSSUS_AFTER_SILVER_GAUNTLETS, + /* 0x66 */ TEMPLE_OF_TIME_FRONT_OF_PEDESTAL, + /* 0x67 */ HYRULE_FIELD_TITLE_SCREEN, + /* 0x68 */ SPIRIT_TEMPLE_BOSS_TITLE_SCREEN, + /* 0x69 */ GRAVEYARD_SUNS_SONG, + /* 0x6A */ ROYAL_FAMILYS_TOMB_SUNS_SONG, + /* 0x6B */ GANONS_CASTLE_AFTER_FOREST_TRIAL, + /* 0x6C */ GANONS_CASTLE_AFTER_WATER_TRIAL, + /* 0x6D */ GANONS_CASTLE_AFTER_SHADOW_TRIAL, + /* 0x6E */ GANONS_CASTLE_AFTER_FIRE_TRIAL, + /* 0x6F */ GANONS_CASTLE_AFTER_LIGHT_TRIAL, + /* 0x70 */ GANONS_CASTLE_AFTER_SPIRIT_TRIAL, + /* 0x71 */ GANONS_CASTLE_DISPEL_BARRIER_IF_CONDITIONS, + /* 0x72 */ HYRULE_FIELD_INTRO, + /* 0x73 */ HYRULE_FIELD_AFTER_IMPA_ESCORT, + /* 0x74 */ DESERT_COLOSSUS_SPIRIT_BLUE_WARP_2, + /* 0x75 */ HYRULE_FIELD_SKY, + /* 0x76 */ GANON_BATTLE_TOWER_COLLAPSE, + /* 0x77 */ ZELDAS_COURTYARD_RECEIVE_LETTER +} CutsceneTerminatorDestination; + +#endif diff --git a/soh/include/z64cutscene_commands.h b/soh/include/z64cutscene_commands.h new file mode 100644 index 000000000..b9e62f7fe --- /dev/null +++ b/soh/include/z64cutscene_commands.h @@ -0,0 +1,448 @@ +#ifndef Z64CUTSCENE_COMMANDS_H +#define Z64CUTSCENE_COMMANDS_H + +#include "command_macros_base.h" +#include "z64cutscene.h" + +/** + * ARGS + * s32 totalEntries (e), s32 endFrame (n) + * FORMAT + * eeeeeeee nnnnnnnn + * size = 0x8 + */ +#define CS_BEGIN_CUTSCENE(totalEntries, endFrame) CMD_W(totalEntries), CMD_W(endFrame) + +/** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * 00000001 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_POS_LIST CS_CAM_EYE_LIST +#define CS_CAM_EYE_LIST(startFrame, endFrame) \ + CS_CMD_CAM_EYE, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + +/** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_POS CS_CAM_EYE +#define CS_CAM_EYE(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + +/** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * 00000002 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_FOCUS_POINT_LIST CS_CAM_AT_LIST +#define CS_CAM_AT_LIST(startFrame, endFrame) \ + CS_CMD_CAM_AT, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + +/** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_FOCUS_POINT CS_CAM_AT +#define CS_CAM_AT(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 00000003 eeeeeeee + * size = 0x8 + */ +#define CS_MISC_LIST(entries) CS_CMD_MISC, CMD_W(entries) + +/** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * uuuussss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU + * size = 0x30 + */ +#define CS_MISC(unk, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7, unused8, unused9, unused10) \ + CMD_HH(unk, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), CMD_W(unused8), CMD_W(unused9), CMD_W(unused10) + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 00000004 eeeeeeee + * size = 0x8 + */ +#define CS_LIGHTING_LIST(entries) CS_CMD_SET_LIGHTING, CMD_W(entries) + +/** + * ARGS + * s16 setting (m), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * mmmmssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_LIGHTING(setting, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(setting, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + +/** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , may be consistently zero + * 00000005 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_POS_PLAYER_LIST CS_CAM_EYE_REL_TO_PLAYER_LIST +#define CS_CAM_EYE_REL_TO_PLAYER_LIST(startFrame, endFrame) \ + CS_CMD_CAM_EYE_REL_TO_PLAYER, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) + +/** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_POS_PLAYER CS_CAM_EYE_REL_TO_PLAYER +#define CS_CAM_EYE_REL_TO_PLAYER(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + +/** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , may be consistently zero + * 00000006 0001ssss eeee0000 + * size = 0xC + */ +#define CS_CAM_FOCUS_POINT_PLAYER_LIST CS_CAM_AT_REL_TO_PLAYER_LIST +#define CS_CAM_AT_REL_TO_PLAYER_LIST(startFrame, endFrame) \ + CS_CMD_CAM_AT_REL_TO_PLAYER, CMD_HH(0x0001, startFrame), CMD_HH(endFrame, 0x0000) +/** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CAM_FOCUS_POINT_PLAYER CS_CAM_AT_REL_TO_PLAYER +#define CS_CAM_AT_REL_TO_PLAYER(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + +/** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * 00000007 uuuussss eeeeUUUU + * size = 0xC + */ +#define CS_CMD_07_LIST(unk, startFrame, endFrame, unused) \ + CS_CMD_07, CMD_HH(unk, startFrame), CMD_HH(endFrame, unused) + +/** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CMD_07(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + +/** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * 00000008 uuuussss eeeeUUUU + * size = 0xC + */ +#define CS_CMD_08_LIST(unk, startFrame, endFrame, unused) \ + CS_CMD_08, CMD_HH(unk, startFrame), CMD_HH(endFrame, unused) + +/** + * ARGS + * s8 continueFlag (c), s8 roll (r), s16 frame (f), f32 viewAngle (a), + * s16 xPos (x), s16 yPos (y), s16 zPos (z) + * FORMAT + * Capital U is Unused + * ccrrffff aaaaaaaa xxxxyyyy zzzzUUUU + * size = 0x10 + */ +#define CS_CMD_08(continueFlag, roll, frame, viewAngle, xPos, yPos, zPos, unused) \ + CMD_BBH(continueFlag, roll, frame), CMD_F(viewAngle), CMD_HH(xPos, yPos), CMD_HH(zPos, unused) + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 00000009 eeeeeeee + * size = 0x8 + */ +#define CS_CMD_09_LIST(entries) CS_CMD_09, CMD_W(entries) + +/** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e), s16 unk2 (v), s16 unk3 (w), s16 unk4 (x) + * FORMAT + * Capital U is Unused + * uuuussss eeeevvww xxUUUUUU + * size = 0xC + */ +#define CS_CMD_09(unk, startFrame, endFrame, unk2, unk3, unk4, unused0, unused1) \ + CMD_HH(unk, startFrame), CMD_HBB(endFrame, unk2, unk3), CMD_BBH(unk4, unused0, unused1) + +/** + * ARGS + * s32 cmdType (c), s32 entries (e) + * FORMAT + * cccccccc eeeeeeee + * size = 0x8 + */ +#define CS_UNK_DATA_LIST(cmdType, entries) CMD_W(cmdType), CMD_W(entries) + +/** + * ARGS + * s32 unk1 (a), s32 unk2 (b), s32 unk3 (c), s32 unk4 (d), s32 unk5 (e), s32 unk6 (f), + * s32 unk7 (g), s32 unk8 (h), s32 unk9 (i), s32 unk10 (j), s32 unk11 (k), s32 unk12 (l) + * FORMAT + * aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee ffffffff gggggggg hhhhhhhh iiiiiiii jjjjjjjj kkkkkkkk llllllll + * size = 0x30 + */ +#define CS_UNK_DATA(unk1, unk2, unk3, unk4, unk5, unk6, unk7, unk8, unk9, unk10, unk11, unk12) \ + CMD_W(unk1), CMD_W(unk2), CMD_W(unk3), CMD_W(unk4), CMD_W(unk5), CMD_W(unk6), \ + CMD_W(unk7), CMD_W(unk8), CMD_W(unk9), CMD_W(unk10), CMD_W(unk11), CMD_W(unk12) + +/** + * ARGS + * s32 cmdType (c), s32 entries (e) + * FORMAT + * cccccccc eeeeeeee + * size = 0x8 + */ +#define CS_NPC_ACTION_LIST(cmdType, entries) CMD_W(cmdType), CMD_W(entries) + +/** + * ARGS + * s16 npcAction (a), s16 startFrame (s), s16 endFrame (e), + * s16 rotX (u), s16 rotY (v), s16 rotZ (w), + * s32 startX (i), s32 startY (j), s32 startZ (k), + * s32 endX (l), s32 endY (m), s32 endZ (n), + * f32 normX (x), f32 normY (y), f32 normZ (z), + * FORMAT + * aaaassss eeeeuuuu vvvvwwww iiiiiiii jjjjjjjj kkkkkkkk llllllll mmmmmmmm nnnnnnnn xxxxxxxx yyyyyyyy zzzzzzzz + * size = 0x30 + */ +#define CS_NPC_ACTION(npcAction, startFrame, endFrame, rotX, rotY, rotZ, startX, startY, startZ, endX, endY, endZ, normX, normY, normZ) \ + CMD_HH(npcAction, startFrame), CMD_HH(endFrame, rotX), CMD_HH(rotY, rotZ), \ + CMD_W(startX), CMD_W(startY), CMD_W(startZ), \ + CMD_W(endX), CMD_W(endY), CMD_W(endZ), \ + CMD_F(normX), CMD_F(normY), CMD_F(normZ) + +/** + * ARGS + * s32 cmdType (c), s32 entries (e) + * FORMAT + * cccccccc eeeeeeee + * size = 0x8 + */ +#define CS_PLAYER_ACTION_LIST(entries) CS_CMD_SET_PLAYER_ACTION, CMD_W(entries) + +/** + * ARGS + * s16 linkAction (a), s16 startFrame (s), s16 endFrame (e), + * s16 rotX (u), s16 rotY (v), s16 rotZ (w), + * s32 startX (i), s32 startY (j), s32 startZ (k), + * s32 endX (l), s32 endY (m), s32 endZ (n), + * f32 normX (x), f32 normY (y), f32 normZ (z), + * FORMAT + * aaaassss eeeeuuuu vvvvwwww iiiiiiii jjjjjjjj kkkkkkkk llllllll mmmmmmmm nnnnnnnn xxxxxxxx yyyyyyyy zzzzzzzz + * size = 0x30 + */ +#define CS_PLAYER_ACTION(linkAction, startFrame, endFrame, rotX, rotY, rotZ, startX, startY, startZ, endX, endY, endZ, normX, normY, normZ) \ + CS_NPC_ACTION(linkAction, startFrame, endFrame, rotX, rotY, rotZ, startX, startY, startZ, endX, endY, endZ, normX, normY, normZ) + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 00000013 eeeeeeee + * size = 0x8 + */ +#define CS_TEXT_LIST(entries) CS_CMD_TEXTBOX, CMD_W(entries) + +/** + * ARGS + * s16 messageId (i), s16 startFrame (s), s16 endFrame (e), s16 type (o), + * s16 topOptionBranch (y), s16 bottomOptionBranch (n) + * FORMAT + * iiiissss eeeeoooo yyyynnnn + * size = 0xC + */ +#define CS_TEXT_DISPLAY_TEXTBOX(messageId, startFrame, endFrame, type, topOptionBranch, bottomOptionBranch) \ + CMD_HH(messageId, startFrame), CMD_HH(endFrame, type), CMD_HH(topOptionBranch, bottomOptionBranch) + +/** + * ARGS + * s16 startFrame (s), s16 endFrame (e) + * FORMAT + * FFFFssss eeeeFFFF FFFFFFFF + * size = 0xC + */ +#define CS_TEXT_NONE(startFrame, endFrame) \ + CS_TEXT_DISPLAY_TEXTBOX(0xFFFF, startFrame, endFrame, 0xFFFF, 0xFFFF, 0xFFFF) + +/** + * ARGS + * s16 ocarinaSongAction (o), s16 startFrame (s), s16 endFrame (e), s16 topOptionBranch (i) + * FORMAT + * oooossss eeee0002 iiiiFFFF + * size = 0xC + */ +#define CS_TEXT_LEARN_SONG(ocarinaSongAction, startFrame, endFrame, messageId) \ + CS_TEXT_DISPLAY_TEXTBOX(ocarinaSongAction, startFrame, endFrame, 0x0002, messageId, 0xFFFF) + +/** + * ARGS + * s16 transitionType (t), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , endFrame duplicate + * 0000002D 00000001 ttttssss eeeeUUUU + * size = 0x10 + */ +#define CS_SCENE_TRANS_FX(transitionType, startFrame, endFrame) \ + CS_CMD_SCENE_TRANS_FX, 0x00000001, CMD_HH(transitionType, startFrame), CMD_HH(endFrame, endFrame) + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 00000056 eeeeeeee + * size = 0x8 + */ +#define CS_PLAY_BGM_LIST(entries) CS_CMD_PLAYBGM, CMD_W(entries) + +/** + * ARGS + * s16 sequence (q), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * qqqqssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_PLAY_BGM(sequence, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(sequence, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 00000057 eeeeeeee + * size = 0x8 + */ +#define CS_STOP_BGM_LIST(entries) CS_CMD_STOPBGM, CMD_W(entries) + +/** + * ARGS + * s16 sequence (q), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * uuqqssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_STOP_BGM(sequence, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(sequence, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 0000007C eeeeeeee + * size = 0x8 + */ +#define CS_FADE_BGM_LIST(entries) CS_CMD_FADEBGM, CMD_W(entries) + +/** + * ARGS + * s16 fadeType (t), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused + * ttttssss eeeeUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU UUUUUUUU 00000000 00000000 00000000 + * size = 0x30 + */ +#define CS_FADE_BGM(fadeType, startFrame, endFrame, unused0, unused1, unused2, unused3, unused4, unused5, unused6, unused7) \ + CMD_HH(fadeType, startFrame), CMD_HH(endFrame, unused0), \ + CMD_W(unused1), CMD_W(unused2), CMD_W(unused3), CMD_W(unused4), CMD_W(unused5), \ + CMD_W(unused6), CMD_W(unused7), 0x00000000, 0x00000000, 0x00000000 + +/** + * ARGS + * s32 entries (e) + * FORMAT + * 0000008C eeeeeeee + * size = 0x8 + */ +#define CS_TIME_LIST(entries) CS_CMD_SETTIME, CMD_W(entries) + +/** + * ARGS + * s16 unk (u), s16 startFrame (s), s16 endFrame (e), s8 hour (h), s8 min (m) + * FORMAT + * Capital U is Unused + * uuuussss eeeehhmm UUUUUUUU + * size = 0xC + */ +#define CS_TIME(unk, startFrame, endFrame, hour, min, unused) \ + CMD_HH(unk, startFrame), \ + CMD_HBB(endFrame, hour, min), \ + CMD_W(unused) + +/** + * ARGS + * CutsceneTerminatorDestination dest (d), s16 startFrame (s), s16 endFrame (e) + * FORMAT + * Capital U is Unused , endFrame duplicate + * 000003E8 00000001 ddddssss eeeeUUUU + * size = 0x10 + */ +#define CS_TERMINATOR(dest, startFrame, endFrame) \ + CS_CMD_TERMINATOR, 0x00000001, CMD_HH(dest, startFrame), CMD_HH(endFrame, endFrame) + +/** + * Marks the end of a cutscene + */ +#define CS_END() 0xFFFFFFFF, 0x00000000 + +#endif diff --git a/soh/include/z64dma.h b/soh/include/z64dma.h new file mode 100755 index 000000000..df5703375 --- /dev/null +++ b/soh/include/z64dma.h @@ -0,0 +1,24 @@ +#ifndef Z64_DMA_H +#define Z64_DMA_H + +#include "ultra64.h" + +typedef struct { + /* 0x00 */ uintptr_t vromAddr; // VROM address (source) + /* 0x04 */ void* dramAddr; // DRAM address (destination) + /* 0x08 */ u32 size; // File Transfer size + /* 0x0C */ const char* filename; // Filename for debugging + /* 0x10 */ s32 line; // Line for debugging + /* 0x14 */ s32 unk_14; + /* 0x18 */ OSMesgQueue* notifyQueue; // Message queue for the notification message + /* 0x1C */ OSMesg notifyMsg; // Completion notification message +} DmaRequest; // size = 0x20 + +typedef struct { + /* 0x00 */ uintptr_t vromStart; + /* 0x04 */ uintptr_t vromEnd; + /* 0x08 */ uintptr_t romStart; + /* 0x0C */ uintptr_t romEnd; +} DmaEntry; + +#endif diff --git a/soh/include/z64effect.h b/soh/include/z64effect.h new file mode 100644 index 000000000..c85c9d617 --- /dev/null +++ b/soh/include/z64effect.h @@ -0,0 +1,262 @@ +#ifndef Z64EFFECT_H +#define Z64EFFECT_H + +#include "color.h" + +struct GraphicsContext; +struct GlobalContext; + +/* Effects */ + +#define SPARK_COUNT 3 +#define BLURE_COUNT 25 +#define SHIELD_PARTICLE_COUNT 3 + +#define TOTAL_EFFECT_COUNT SPARK_COUNT + BLURE_COUNT + SHIELD_PARTICLE_COUNT + +typedef struct { + /* 0x00 */ u8 active; + /* 0x01 */ u8 unk_01; + /* 0x02 */ u8 unk_02; +} EffectStatus; // size = 0x03 + +typedef struct { + /* 0x00 */ Vec3f velocity; + /* 0x0C */ Vec3f position; + /* 0x18 */ Vec3s unkVelocity; + /* 0x1E */ Vec3s unkPosition; +} EffectSparkElement; // size = 0x24 + +typedef struct { + /* 0x000 */ Vec3s position; + /* 0x008 */ s32 numElements; // "table_size"; calculated as uDiv * vDiv + 2 + /* 0x00C */ EffectSparkElement elements[32]; + /* 0x48C */ f32 speed; + /* 0x490 */ f32 gravity; + /* 0x494 */ u32 uDiv; // "u_div" + /* 0x498 */ u32 vDiv; // "v_div" + /* 0x49C */ Color_RGBA8 colorStart[4]; + /* 0x4AC */ Color_RGBA8 colorEnd[4]; + /* 0x4BC */ s32 timer; + /* 0x4C0 */ s32 duration; +} EffectSparkInit; // size = 0x4C4 + +typedef struct { + /* 0x000 */ Vec3s position; + /* 0x008 */ s32 numElements; // "table_size"; calculated as uDiv * vDiv + 2 + /* 0x00C */ EffectSparkElement elements[32]; + /* 0x48C */ f32 speed; + /* 0x490 */ f32 gravity; + /* 0x494 */ u32 uDiv; // "u_div" + /* 0x498 */ u32 vDiv; // "v_div" + /* 0x49C */ Color_RGBA8 colorStart[4]; + /* 0x4AC */ Color_RGBA8 colorEnd[4]; + /* 0x4BC */ s32 timer; + /* 0x4C0 */ s32 duration; +} EffectSpark; // size = 0x4C4 + +typedef struct { + /* 0x00 */ s32 state; + /* 0x04 */ s32 timer; + /* 0x08 */ Vec3s p1; + /* 0x0E */ Vec3s p2; + /* 0x14 */ u16 flags; +} EffectBlureElement; // size = 0x18 + +typedef struct { + /* 0x000 */ char unk_00[0x184]; + /* 0x184 */ u8 p1StartColor[4]; + /* 0x188 */ u8 p2StartColor[4]; + /* 0x18C */ u8 p1EndColor[4]; + /* 0x190 */ u8 p2EndColor[4]; + /* 0x194 */ s32 elemDuration; + /* 0x198 */ s32 unkFlag; + /* 0x19C */ s32 calcMode; +} EffectBlureInit1; // size = 0x1A0 + +typedef struct { + /* 0x00 */ s32 calcMode; + /* 0x04 */ u16 flags; + /* 0x06 */ s16 addAngleChange; + /* 0x08 */ u8 p1StartColor[4]; + /* 0x0C */ u8 p2StartColor[4]; + /* 0x10 */ u8 p1EndColor[4]; + /* 0x14 */ u8 p2EndColor[4]; + /* 0x18 */ u8 elemDuration; + /* 0x19 */ u8 unkFlag; + /* 0x1A */ u8 drawMode; // 0: simple; 1: simple with alt colors; 2+: smooth + /* 0x1B */ u8 mode4Param; + /* 0x1C */ Color_RGBA8 altPrimColor; // used with drawMode 1 + /* 0x20 */ Color_RGBA8 altEnvColor; // used with drawMode 1 +} EffectBlureInit2; // size = 0x24 + +typedef struct { + /* 0x000 */ EffectBlureElement elements[16]; + /* 0x180 */ s32 calcMode; + /* 0x184 */ f32 mode4Param; + /* 0x188 */ u16 flags; + /* 0x18A */ s16 addAngleChange; + /* 0x18C */ s16 addAngle; + /* 0x18E */ Color_RGBA8 p1StartColor; + /* 0x192 */ Color_RGBA8 p2StartColor; + /* 0x196 */ Color_RGBA8 p1EndColor; + /* 0x19A */ Color_RGBA8 p2EndColor; + /* 0x19E */ u8 numElements; // "now_edge_num" + /* 0x19F */ u8 elemDuration; + /* 0x1A0 */ u8 unkFlag; + /* 0x1A1 */ u8 drawMode; // 0: simple; 1: simple with alt colors; 2+: smooth + /* 0x1A2 */ Color_RGBA8 altPrimColor; // used with drawMode 1 + /* 0x1A6 */ Color_RGBA8 altEnvColor; // used with drawMode 1 +} EffectBlure; // size = 0x1AC + +typedef struct { + /* 0x00 */ f32 initialSpeed; + /* 0x04 */ f32 endXChange; + /* 0x08 */ f32 endX; + /* 0x0C */ f32 startXChange; + /* 0x10 */ f32 startX; + /* 0x14 */ s16 yaw; + /* 0x16 */ s16 pitch; +} EffectShieldParticleElement; // size = 0x18 + +typedef struct { + /* 0x00 */ u8 numElements; + /* 0x02 */ Vec3s position; + /* 0x08 */ Color_RGBA8 primColorStart; + /* 0x0C */ Color_RGBA8 envColorStart; + /* 0x10 */ Color_RGBA8 primColorMid; + /* 0x14 */ Color_RGBA8 envColorMid; + /* 0x18 */ Color_RGBA8 primColorEnd; + /* 0x1C */ Color_RGBA8 envColorEnd; + /* 0x20 */ f32 deceleration; + /* 0x24 */ f32 maxInitialSpeed; + /* 0x28 */ f32 lengthCutoff; + /* 0x2C */ u8 duration; + /* 0x2E */ LightPoint lightPoint; + /* 0x3C */ s32 lightDecay; // halves light radius every frame when set to 1 +} EffectShieldParticleInit; // size = 0x40 + +typedef struct { + /* 0x000 */ EffectShieldParticleElement elements[16]; + /* 0x180 */ u8 numElements; + /* 0x182 */ Vec3s position; + /* 0x188 */ Color_RGBA8 primColorStart; + /* 0x18C */ Color_RGBA8 envColorStart; + /* 0x190 */ Color_RGBA8 primColorMid; + /* 0x194 */ Color_RGBA8 envColorMid; + /* 0x198 */ Color_RGBA8 primColorEnd; + /* 0x19C */ Color_RGBA8 envColorEnd; + /* 0x1A0 */ f32 deceleration; + /* 0x1A4 */ char unk_1A4[0x04]; + /* 0x1A8 */ f32 maxInitialSpeed; + /* 0x1AC */ f32 lengthCutoff; + /* 0x1B0 */ u8 duration; + /* 0x1B1 */ u8 timer; + /* 0x1B2 */ LightInfo lightInfo; + /* 0x1C0 */ LightNode* lightNode; + /* 0x1C4 */ s32 lightDecay; // halves light radius every frame when set to 1 +} EffectShieldParticle; // size = 0x1C8 + +typedef struct { + /* 0x0000 */ struct GlobalContext* globalCtx; + struct { + EffectStatus status; + EffectSpark effect; + } /* 0x0004 */ sparks[SPARK_COUNT]; + struct { + EffectStatus status; + EffectBlure effect; + } /* 0x0E5C */ blures[BLURE_COUNT]; + struct { + EffectStatus status; + EffectShieldParticle effect; + } /* 0x388C */ shieldParticles[SHIELD_PARTICLE_COUNT]; +} EffectContext; // size = 0x3DF0 + +typedef struct { + /* 0x00 */ size_t size; + /* 0x04 */ void (*init)(void* effect, void* initParams); + /* 0x08 */ void (*destroy)(void* effect); + /* 0x0C */ s32 (*update)(void* effect); + /* 0x10 */ void (*draw)(void* effect, struct GraphicsContext* gfxCtx); +} EffectInfo; // size = 0x14 + +typedef enum { + /* 0x00 */ EFFECT_SPARK, + /* 0x01 */ EFFECT_BLURE1, + /* 0x02 */ EFFECT_BLURE2, + /* 0x03 */ EFFECT_SHIELD_PARTICLE +} EffectType; + +/* Effect Soft Sprites */ + +struct EffectSs; + +typedef u32 (*EffectSsInitFunc)(struct GlobalContext* globalCtx, u32 index, struct EffectSs* effectSs, void* initParams); +typedef void (*EffectSsUpdateFunc)(struct GlobalContext* globalCtx, u32 index, struct EffectSs* effectSs); +typedef void (*EffectSsDrawFunc)(struct GlobalContext* globalCtx, u32 index, struct EffectSs* effectSs); + +typedef struct { + /* 0x00 */ u32 type; + /* 0x04 */ EffectSsInitFunc init; +} EffectSsInit; // size = 0x08 + +typedef struct { + /* 0x00 */ uintptr_t vromStart; + /* 0x04 */ uintptr_t vromEnd; + /* 0x08 */ void* vramStart; + /* 0x0C */ void* vramEnd; + /* 0x10 */ void* loadedRamAddr; + /* 0x14 */ EffectSsInit* initInfo; + /* 0x18 */ u8 unk_18; +} EffectSsOverlay; // size = 0x1C + +typedef struct EffectSs { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ EffectSsUpdateFunc update; + /* 0x28 */ EffectSsDrawFunc draw; + /* 0x2C */ Vec3f vec; // usage specific per effect + /* 0x38 */ void* gfx; // mostly used for display lists, sometimes textures + /* 0x3C */ Actor* actor; // interfacing actor, usually the actor that spawned the effect + /* 0x40 */ s16 regs[13]; // specific per effect + /* 0x5A */ u16 flags; + /* 0x5C */ s16 life; // -1 means this entry is free + /* 0x5E */ u8 priority; // Lower value means higher priority + /* 0x5F */ u8 type; +} EffectSs; // size = 0x60 + +typedef struct { + /* 0x00 */ EffectSs* table; // "data_table" + /* 0x04 */ s32 searchStartIndex; + /* 0x08 */ s32 tableSize; +} EffectSsInfo; // size = 0x0C + +/* G Effect Regs */ + +#define rgTexIdx regs[0] +#define rgScale regs[1] +#define rgTexIdxStep regs[2] +#define rgPrimColorR regs[3] +#define rgPrimColorG regs[4] +#define rgPrimColorB regs[5] +#define rgPrimColorA regs[6] +#define rgEnvColorR regs[7] +#define rgEnvColorG regs[8] +#define rgEnvColorB regs[9] +#define rgEnvColorA regs[10] +#define rgObjBankIdx regs[11] + +#define DEFINE_EFFECT_SS(_0, enum) enum, +#define DEFINE_EFFECT_SS_UNSET(enum) enum, + +typedef enum { + #include "tables/effect_ss_table.h" + /* 0x25 */ EFFECT_SS_TYPE_MAX // originally "EFFECT_SS2_TYPE_LAST_LABEL" +} EffectSsType; + +#undef DEFINE_EFFECT_SS +#undef DEFINE_EFFECT_SS_UNSET + +#endif diff --git a/soh/include/z64elf_message.h b/soh/include/z64elf_message.h new file mode 100644 index 000000000..d6f545b3f --- /dev/null +++ b/soh/include/z64elf_message.h @@ -0,0 +1,129 @@ +#ifndef Z64ELF_MESSAGE_H +#define Z64ELF_MESSAGE_H + +#include "ultra64.h" + +// Checks the condition and exits the script if the check passes +#define ELF_MSG_TYPE_CHECK 0 +// ? (unused) +#define ELF_MSG_TYPE_UNK_1 1 +// ? (unused) +#define ELF_MSG_TYPE_UNK_2 2 +// Checks the condition and skips forward by some number of commands if the check passes +#define ELF_MSG_TYPE_SKIP 3 +// Always ends the script, returning the text id for this command +#define ELF_MSG_TYPE_END 7 + +// Check an eventChkInf flag +#define ELF_MSG_CONDITION_FLAG 0 +// Check a dungeon item (map, compass, boss key) +#define ELF_MSG_CONDITION_DUNGEON_ITEM 1 +// Check if an item is in an item slot +#define ELF_MSG_CONDITION_ITEM 2 +// "Other" conditions described below +#define ELF_MSG_CONDITION_OTHER 3 + +// Check what strength upgrade has been obtained so far +#define ELF_MSG_CONDITION_STRENGTH_UPG 0 +// Check if specific boots have been obtained so far +#define ELF_MSG_CONDITION_BOOTS 1 +// Check if a particular song has been obtained +#define ELF_MSG_CONDITION_SONG 2 +// Check if a particular medallion has been obtained +#define ELF_MSG_CONDITION_MEDALLION 3 +// Check if the magic meter has been obtained +#define ELF_MSG_CONDITION_MAGIC 4 + +/* + * Bitpack byte 0 + */ +#define ELF_MSG_B0(type, cond_type, tf) \ + _SHIFTL(ELF_MSG_TYPE_##type, 5, 3) | \ + _SHIFTL(ELF_MSG_CONDITION_##cond_type, 1, 4) | \ + _SHIFTL(tf, 0, 1) + +/* + * Bitpack byte 1 + */ +#define ELF_MSG_B1(cond_type, data) \ + _SHIFTL(ELF_MSG_CONDITION_##cond_type, 4, 4) | \ + _SHIFTL(data, 0, 4) + +/* + * Other bytes + */ +#define ELF_MSG_B(data) \ + _SHIFTL(data, 0, 8) + +/* + * Command macros + */ + +#define ELF_MSG_FLAG(type, textId, tf, flag) \ + { \ + ELF_MSG_B0(type, FLAG, tf), \ + ELF_MSG_B(flag), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(0), \ + } + +#define ELF_MSG_END(textId) \ + ELF_MSG_FLAG(END, textId, false, 0) + +#define ELF_MSG_DUNGEON_ITEM(type, textId, tf, itemId) \ + { \ + ELF_MSG_B0(type, DUNGEON_ITEM, tf), \ + ELF_MSG_B(itemId), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(0), \ + } + +#define ELF_MSG_ITEM(type, textId, tf, slotItemId, itemId) \ + { \ + ELF_MSG_B0(type, ITEM, tf), \ + ELF_MSG_B(slotItemId), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(itemId), \ + } + +#define ELF_MSG_STRENGTH_UPG(type, textId, tf, strUpg) \ + { \ + ELF_MSG_B0(type, OTHER, tf), \ + ELF_MSG_B1(STRENGTH_UPG, strUpg), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(0), \ + } + +#define ELF_MSG_BOOTS(type, textId, tf, itemId) \ + { \ + ELF_MSG_B0(type, OTHER, tf), \ + ELF_MSG_B1(BOOTS, 0), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(itemId), \ + } + +#define ELF_MSG_SONG(type, textId, tf, itemId) \ + { \ + ELF_MSG_B0(type, OTHER, tf), \ + ELF_MSG_B1(SONG, 0), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(itemId), \ + } + +#define ELF_MSG_MEDALLION(type, textId, tf, itemId) \ + { \ + ELF_MSG_B0(type, OTHER, tf), \ + ELF_MSG_B1(MEDALLION, 0), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(itemId), \ + } + +#define ELF_MSG_MAGIC(type, textId, tf) \ + { \ + ELF_MSG_B0(type, OTHER, tf), \ + ELF_MSG_B1(MAGIC, 0), \ + ELF_MSG_B(textId), \ + ELF_MSG_B(0), \ + } + +#endif diff --git a/soh/include/z64environment.h b/soh/include/z64environment.h new file mode 100644 index 000000000..b2badccd3 --- /dev/null +++ b/soh/include/z64environment.h @@ -0,0 +1,129 @@ +#ifndef _Z64ENVIRONMENT_H_ +#define _Z64ENVIRONMENT_H_ + +#include "z64math.h" +#include "z64light.h" +#include "z64dma.h" + +#define FILL_SCREEN_OPA (1 << 0) +#define FILL_SCREEN_XLU (1 << 1) + +typedef enum { + /* 0 */ LIGHTNING_MODE_OFF, // no lightning + /* 1 */ LIGHTNING_MODE_ON, // request ligtning strikes at random intervals + /* 2 */ LIGHTNING_MODE_LAST // request one lightning strike before turning off +} LightningMode; + +typedef enum { + /* 0 */ LIGHTNING_STRIKE_WAIT, // wait between lightning strikes. request bolts when timer hits 0 + /* 1 */ LIGHTNING_STRIKE_START, // fade in the flash. note: bolts are requested in the previous state + /* 2 */ LIGHTNING_STRIKE_END // fade out the flash and go back to wait +} LightningStrikeState; + +typedef enum { + /* 0 */ SKYBOX_DMA_INACTIVE, + /* 1 */ SKYBOX_DMA_FILE1_START, + /* 2 */ SKYBOX_DMA_FILE1_DONE, + /* 3 */ SKYBOX_DMA_PAL1_START, + /* 11 */ SKYBOX_DMA_FILE2_START = 11, + /* 12 */ SKYBOX_DMA_FILE2_DONE, + /* 13 */ SKYBOX_DMA_PAL2_START +} SkyboxDmaState; + +typedef struct { + /* 0x00 */ u8 state; + /* 0x01 */ u8 flashRed; + /* 0x02 */ u8 flashGreen; + /* 0x03 */ u8 flashBlue; + /* 0x04 */ u8 flashAlphaTarget; + /* 0x08 */ f32 delayTimer; +} LightningStrike; // size = 0xC + +// describes what skybox files and blending modes to use depending on time of day +typedef struct { + /* 0x00 */ u16 startTime; + /* 0x02 */ u16 endTime; + /* 0x04 */ u8 blend; // if true, blend between.. skyboxes? palettes? + /* 0x05 */ u8 skybox1Index; // whats the difference between _pal and non _pal files? + /* 0x06 */ u8 skybox2Index; +} struct_8011FC1C; // size = 0x8 + +typedef struct { + /* 0x00 */ u8 ambientColor[3]; + /* 0x03 */ s8 light1Dir[3]; + /* 0x06 */ u8 light1Color[3]; + /* 0x09 */ s8 light2Dir[3]; + /* 0x0C */ u8 light2Color[3]; + /* 0x0F */ u8 fogColor[3]; + /* 0x12 */ s16 fogNear; + /* 0x14 */ s16 fogFar; +} EnvLightSettings; // size = 0x16 + +// 1.0: 801D8EC4 +// dbg: 80222A44 +typedef struct { + /* 0x00 */ char unk_00[0x02]; + /* 0x02 */ u16 timeIncrement; // how many units of time that pass every update + /* 0x04 */ Vec3f sunPos; // moon position can be found by negating the sun position + /* 0x10 */ u8 skybox1Index; + /* 0x11 */ u8 skybox2Index; + /* 0x12 */ char unk_12[0x01]; + /* 0x13 */ u8 skyboxBlend; + /* 0x14 */ char unk_14[0x01]; + /* 0x15 */ u8 skyboxDisabled; + /* 0x16 */ u8 sunMoonDisabled; + /* 0x17 */ u8 unk_17; // currentWeatherMode for skybox? (prev called gloomySky) + /* 0x18 */ u8 unk_18; // nextWeatherMode for skybox? + /* 0x19 */ u8 unk_19; + /* 0x1A */ u16 unk_1A; + /* 0x1C */ char unk_1C[0x02]; + /* 0x1E */ u8 indoors; // when set, day time has no effect on lighting + /* 0x1F */ u8 unk_1F; // outdoor light index + /* 0x20 */ u8 unk_20; // prev outdoor light index? + /* 0x21 */ u8 unk_21; + /* 0x22 */ u16 unk_22; + /* 0x24 */ u16 unk_24; + /* 0x26 */ char unk_26[0x02]; + /* 0x28 */ LightInfo dirLight1; // used for sunlight outdoors + /* 0x36 */ LightInfo dirLight2; // used for moonlight outdoors + /* 0x44 */ s8 skyboxDmaState; + /* 0x48 */ DmaRequest dmaRequest; + /* 0x68 */ OSMesgQueue loadQueue; + /* 0x80 */ OSMesg loadMsg; + /* 0x84 */ f32 unk_84; + /* 0x88 */ f32 unk_88; + /* 0x8C */ s16 adjAmbientColor[3]; + /* 0x92 */ s16 adjLight1Color[3]; + /* 0x98 */ s16 adjFogColor[3]; + /* 0x9E */ s16 adjFogNear; + /* 0xA0 */ s16 adjFogFar; + /* 0xA2 */ char unk_A2[0x06]; + /* 0xA8 */ Vec3s windDirection; + /* 0xB0 */ f32 windSpeed; + /* 0xB4 */ u8 numLightSettings; + /* 0xB8 */ EnvLightSettings* lightSettingsList; // list of light settings from the scene file + /* 0xBC */ u8 blendIndoorLights; // when true, blend between indoor light settings when switching + /* 0xBD */ u8 unk_BD; // indoor light index + /* 0xBE */ u8 unk_BE; // prev indoor light index? + /* 0xBF */ u8 unk_BF; + /* 0xC0 */ EnvLightSettings lightSettings; + /* 0xD6 */ u16 unk_D6; + /* 0xD8 */ f32 unk_D8; // indoor light blend weight? + /* 0xDC */ u8 unk_DC; + /* 0xDD */ u8 gloomySkyMode; + /* 0xDE */ u8 unk_DE; // gloomy sky state + /* 0xDF */ u8 lightningMode; + /* 0xE0 */ u8 unk_E0; // env sounds state + /* 0xE1 */ u8 fillScreen; + /* 0xE2 */ u8 screenFillColor[4]; + /* 0xE6 */ u8 sandstormState; + /* 0xE7 */ u8 sandstormPrimA; + /* 0xE8 */ u8 sandstormEnvA; + /* 0xE9 */ u8 customSkyboxFilter; + /* 0xEA */ u8 skyboxFilterColor[4]; + /* 0xEE */ u8 unk_EE[4]; + /* 0xF2 */ u8 unk_F2[4]; + /* 0xF6 */ char unk_F6[0x06]; +} EnvironmentContext; // size = 0xFC + +#endif diff --git a/soh/include/z64interface.h b/soh/include/z64interface.h new file mode 100644 index 000000000..de3913aba --- /dev/null +++ b/soh/include/z64interface.h @@ -0,0 +1,71 @@ +#ifndef Z64INTERFACE_H +#define Z64INTERFACE_H + +/** + * Button HUD Positions (Upper Left) + */ +#define A_BUTTON_X 186 +#define A_BUTTON_Y 9 + +#define B_BUTTON_X 160 +#define B_BUTTON_Y 17 + +#define C_LEFT_BUTTON_X 227 +#define C_LEFT_BUTTON_Y 18 + +#define C_DOWN_BUTTON_X 249 +#define C_DOWN_BUTTON_Y 34 + +#define C_RIGHT_BUTTON_X 271 +#define C_RIGHT_BUTTON_Y 18 + +#define C_UP_BUTTON_X 254 +#define C_UP_BUTTON_Y 16 + +/** + * These are the colors for the hearts in the interface. The prim color is the red color of the heart + * for the base hearts, while the prim color for the double defense hearts is the white outline. The + * env color for the base hearts is the purple-ish outline, while the env color for the double defense + * hearts is the red color of the hearts. + */ + +#define HEARTS_PRIM_R 255 +#define HEARTS_PRIM_G 70 +#define HEARTS_PRIM_B 50 + +#define HEARTS_ENV_R 50 +#define HEARTS_ENV_G 40 +#define HEARTS_ENV_B 60 + +#define HEARTS_DD_PRIM_R 255 +#define HEARTS_DD_PRIM_G 255 +#define HEARTS_DD_PRIM_B 255 + +#define HEARTS_DD_ENV_R 200 +#define HEARTS_DD_ENV_G 0 +#define HEARTS_DD_ENV_B 0 + +/** + * The burn and drown colors listed here are unused. Prerelease footage of the game confirms that at one + * point in development the orange color was to be used while taking damage from hot environments. + * Based on this, we can assume that the blue heart color was to be used while drowning. + * In the final game these environments only have a timer and do not damage you continuously. + */ + +#define HEARTS_BURN_PRIM_R 255 +#define HEARTS_BURN_PRIM_G 190 +#define HEARTS_BURN_PRIM_B 0 + +#define HEARTS_BURN_ENV_R 255 +#define HEARTS_BURN_ENV_G 0 +#define HEARTS_BURN_ENV_B 0 + +#define HEARTS_DROWN_PRIM_R 100 +#define HEARTS_DROWN_PRIM_G 100 +#define HEARTS_DROWN_PRIM_B 255 + +#define HEARTS_DROWN_ENV_R 0 +#define HEARTS_DROWN_ENV_G 0 +#define HEARTS_DROWN_ENV_B 255 + +#endif diff --git a/soh/include/z64item.h b/soh/include/z64item.h new file mode 100644 index 000000000..05b2a24b2 --- /dev/null +++ b/soh/include/z64item.h @@ -0,0 +1,535 @@ +#ifndef Z64ITEM_H +#define Z64ITEM_H + +typedef enum { + /* 0x00 */ EQUIP_SWORD, + /* 0x01 */ EQUIP_SHIELD, + /* 0x02 */ EQUIP_TUNIC, + /* 0x03 */ EQUIP_BOOTS +} EquipmentType; + +typedef enum { + /* 0x00 */ UPG_QUIVER, + /* 0x01 */ UPG_BOMB_BAG, + /* 0x02 */ UPG_STRENGTH, + /* 0x03 */ UPG_SCALE, + /* 0x04 */ UPG_WALLET, + /* 0x05 */ UPG_BULLET_BAG, + /* 0x06 */ UPG_STICKS, + /* 0x07 */ UPG_NUTS +} UpgradeType; + +typedef enum { + /* 0x00 */ QUEST_MEDALLION_FOREST, + /* 0x01 */ QUEST_MEDALLION_FIRE, + /* 0x02 */ QUEST_MEDALLION_WATER, + /* 0x03 */ QUEST_MEDALLION_SPIRIT, + /* 0x04 */ QUEST_MEDALLION_SHADOW, + /* 0x05 */ QUEST_MEDALLION_LIGHT, + /* 0x06 */ QUEST_SONG_MINUET, + /* 0x07 */ QUEST_SONG_BOLERO, + /* 0x08 */ QUEST_SONG_SERENADE, + /* 0x09 */ QUEST_SONG_REQUIEM, + /* 0x0A */ QUEST_SONG_NOCTURNE, + /* 0x0B */ QUEST_SONG_PRELUDE, + /* 0x0C */ QUEST_SONG_LULLABY, + /* 0x0D */ QUEST_SONG_EPONA, + /* 0x0E */ QUEST_SONG_SARIA, + /* 0x0F */ QUEST_SONG_SUN, + /* 0x10 */ QUEST_SONG_TIME, + /* 0x11 */ QUEST_SONG_STORMS, + /* 0x12 */ QUEST_KOKIRI_EMERALD, + /* 0x13 */ QUEST_GORON_RUBY, + /* 0x14 */ QUEST_ZORA_SAPPHIRE, + /* 0x15 */ QUEST_STONE_OF_AGONY, + /* 0x16 */ QUEST_GERUDO_CARD, + /* 0x17 */ QUEST_SKULL_TOKEN, + /* 0x18 */ QUEST_HEART_PIECE +} QuestItem; + +typedef enum { + /* 0x00 */ DUNGEON_KEY_BOSS, + /* 0x01 */ DUNGEON_COMPASS, + /* 0x02 */ DUNGEON_MAP +} DungeonItem; + +typedef enum { + /* 0x00 */ SLOT_STICK, + /* 0x01 */ SLOT_NUT, + /* 0x02 */ SLOT_BOMB, + /* 0x03 */ SLOT_BOW, + /* 0x04 */ SLOT_ARROW_FIRE, + /* 0x05 */ SLOT_DINS_FIRE, + /* 0x06 */ SLOT_SLINGSHOT, + /* 0x07 */ SLOT_OCARINA, + /* 0x08 */ SLOT_BOMBCHU, + /* 0x09 */ SLOT_HOOKSHOT, + /* 0x0A */ SLOT_ARROW_ICE, + /* 0x0B */ SLOT_FARORES_WIND, + /* 0x0C */ SLOT_BOOMERANG, + /* 0x0D */ SLOT_LENS, + /* 0x0E */ SLOT_BEAN, + /* 0x0F */ SLOT_HAMMER, + /* 0x10 */ SLOT_ARROW_LIGHT, + /* 0x11 */ SLOT_NAYRUS_LOVE, + /* 0x12 */ SLOT_BOTTLE_1, + /* 0x13 */ SLOT_BOTTLE_2, + /* 0x14 */ SLOT_BOTTLE_3, + /* 0x15 */ SLOT_BOTTLE_4, + /* 0x16 */ SLOT_TRADE_ADULT, + /* 0x17 */ SLOT_TRADE_CHILD, + /* 0xFF */ SLOT_NONE = 0xFF +} InventorySlot; + +typedef enum { + /* 0x00 */ ITEM_STICK, + /* 0x01 */ ITEM_NUT, + /* 0x02 */ ITEM_BOMB, + /* 0x03 */ ITEM_BOW, + /* 0x04 */ ITEM_ARROW_FIRE, + /* 0x05 */ ITEM_DINS_FIRE, + /* 0x06 */ ITEM_SLINGSHOT, + /* 0x07 */ ITEM_OCARINA_FAIRY, + /* 0x08 */ ITEM_OCARINA_TIME, + /* 0x09 */ ITEM_BOMBCHU, + /* 0x0A */ ITEM_HOOKSHOT, + /* 0x0B */ ITEM_LONGSHOT, + /* 0x0C */ ITEM_ARROW_ICE, + /* 0x0D */ ITEM_FARORES_WIND, + /* 0x0E */ ITEM_BOOMERANG, + /* 0x0F */ ITEM_LENS, + /* 0x10 */ ITEM_BEAN, + /* 0x11 */ ITEM_HAMMER, + /* 0x12 */ ITEM_ARROW_LIGHT, + /* 0x13 */ ITEM_NAYRUS_LOVE, + /* 0x14 */ ITEM_BOTTLE, + /* 0x15 */ ITEM_POTION_RED, + /* 0x16 */ ITEM_POTION_GREEN, + /* 0x17 */ ITEM_POTION_BLUE, + /* 0x18 */ ITEM_FAIRY, + /* 0x19 */ ITEM_FISH, + /* 0x1A */ ITEM_MILK_BOTTLE, + /* 0x1B */ ITEM_LETTER_RUTO, + /* 0x1C */ ITEM_BLUE_FIRE, + /* 0x1D */ ITEM_BUG, + /* 0x1E */ ITEM_BIG_POE, + /* 0x1F */ ITEM_MILK_HALF, + /* 0x20 */ ITEM_POE, + /* 0x21 */ ITEM_WEIRD_EGG, + /* 0x22 */ ITEM_CHICKEN, + /* 0x23 */ ITEM_LETTER_ZELDA, + /* 0x24 */ ITEM_MASK_KEATON, + /* 0x25 */ ITEM_MASK_SKULL, + /* 0x26 */ ITEM_MASK_SPOOKY, + /* 0x27 */ ITEM_MASK_BUNNY, + /* 0x28 */ ITEM_MASK_GORON, + /* 0x29 */ ITEM_MASK_ZORA, + /* 0x2A */ ITEM_MASK_GERUDO, + /* 0x2B */ ITEM_MASK_TRUTH, + /* 0x2C */ ITEM_SOLD_OUT, + /* 0x2D */ ITEM_POCKET_EGG, + /* 0x2E */ ITEM_POCKET_CUCCO, + /* 0x2F */ ITEM_COJIRO, + /* 0x30 */ ITEM_ODD_MUSHROOM, + /* 0x31 */ ITEM_ODD_POTION, + /* 0x32 */ ITEM_SAW, + /* 0x33 */ ITEM_SWORD_BROKEN, + /* 0x34 */ ITEM_PRESCRIPTION, + /* 0x35 */ ITEM_FROG, + /* 0x36 */ ITEM_EYEDROPS, + /* 0x37 */ ITEM_CLAIM_CHECK, + /* 0x38 */ ITEM_BOW_ARROW_FIRE, + /* 0x39 */ ITEM_BOW_ARROW_ICE, + /* 0x3A */ ITEM_BOW_ARROW_LIGHT, + /* 0x3B */ ITEM_SWORD_KOKIRI, + /* 0x3C */ ITEM_SWORD_MASTER, + /* 0x3D */ ITEM_SWORD_BGS, + /* 0x3E */ ITEM_SHIELD_DEKU, + /* 0x3F */ ITEM_SHIELD_HYLIAN, + /* 0x40 */ ITEM_SHIELD_MIRROR, + /* 0x41 */ ITEM_TUNIC_KOKIRI, + /* 0x42 */ ITEM_TUNIC_GORON, + /* 0x43 */ ITEM_TUNIC_ZORA, + /* 0x44 */ ITEM_BOOTS_KOKIRI, + /* 0x45 */ ITEM_BOOTS_IRON, + /* 0x46 */ ITEM_BOOTS_HOVER, + /* 0x47 */ ITEM_BULLET_BAG_30, + /* 0x48 */ ITEM_BULLET_BAG_40, + /* 0x49 */ ITEM_BULLET_BAG_50, + /* 0x4A */ ITEM_QUIVER_30, + /* 0x4B */ ITEM_QUIVER_40, + /* 0x4C */ ITEM_QUIVER_50, + /* 0x4D */ ITEM_BOMB_BAG_20, + /* 0x4E */ ITEM_BOMB_BAG_30, + /* 0x4F */ ITEM_BOMB_BAG_40, + /* 0x50 */ ITEM_BRACELET, + /* 0x51 */ ITEM_GAUNTLETS_SILVER, + /* 0x52 */ ITEM_GAUNTLETS_GOLD, + /* 0x53 */ ITEM_SCALE_SILVER, + /* 0x54 */ ITEM_SCALE_GOLDEN, + /* 0x55 */ ITEM_SWORD_KNIFE, + /* 0x56 */ ITEM_WALLET_ADULT, + /* 0x57 */ ITEM_WALLET_GIANT, + /* 0x58 */ ITEM_SEEDS, + /* 0x59 */ ITEM_FISHING_POLE, + /* 0x5A */ ITEM_SONG_MINUET, + /* 0x5B */ ITEM_SONG_BOLERO, + /* 0x5C */ ITEM_SONG_SERENADE, + /* 0x5D */ ITEM_SONG_REQUIEM, + /* 0x5E */ ITEM_SONG_NOCTURNE, + /* 0x5F */ ITEM_SONG_PRELUDE, + /* 0x60 */ ITEM_SONG_LULLABY, + /* 0x61 */ ITEM_SONG_EPONA, + /* 0x62 */ ITEM_SONG_SARIA, + /* 0x63 */ ITEM_SONG_SUN, + /* 0x64 */ ITEM_SONG_TIME, + /* 0x65 */ ITEM_SONG_STORMS, + /* 0x66 */ ITEM_MEDALLION_FOREST, + /* 0x67 */ ITEM_MEDALLION_FIRE, + /* 0x68 */ ITEM_MEDALLION_WATER, + /* 0x69 */ ITEM_MEDALLION_SPIRIT, + /* 0x6A */ ITEM_MEDALLION_SHADOW, + /* 0x6B */ ITEM_MEDALLION_LIGHT, + /* 0x6C */ ITEM_KOKIRI_EMERALD, + /* 0x6D */ ITEM_GORON_RUBY, + /* 0x6E */ ITEM_ZORA_SAPPHIRE, + /* 0x6F */ ITEM_STONE_OF_AGONY, + /* 0x70 */ ITEM_GERUDO_CARD, + /* 0x71 */ ITEM_SKULL_TOKEN, + /* 0x72 */ ITEM_HEART_CONTAINER, + /* 0x73 */ ITEM_HEART_PIECE, + /* 0x74 */ ITEM_KEY_BOSS, + /* 0x75 */ ITEM_COMPASS, + /* 0x76 */ ITEM_DUNGEON_MAP, + /* 0x77 */ ITEM_KEY_SMALL, + /* 0x78 */ ITEM_MAGIC_SMALL, + /* 0x79 */ ITEM_MAGIC_LARGE, + /* 0x7A */ ITEM_HEART_PIECE_2, + /* 0x7B */ ITEM_INVALID_1, + /* 0x7C */ ITEM_INVALID_2, + /* 0x7D */ ITEM_INVALID_3, + /* 0x7E */ ITEM_INVALID_4, + /* 0x7F */ ITEM_INVALID_5, + /* 0x80 */ ITEM_INVALID_6, + /* 0x81 */ ITEM_INVALID_7, + /* 0x82 */ ITEM_MILK, + /* 0x83 */ ITEM_HEART, + /* 0x84 */ ITEM_RUPEE_GREEN, + /* 0x85 */ ITEM_RUPEE_BLUE, + /* 0x86 */ ITEM_RUPEE_RED, + /* 0x87 */ ITEM_RUPEE_PURPLE, + /* 0x88 */ ITEM_RUPEE_GOLD, + /* 0x89 */ ITEM_INVALID_8, + /* 0x8A */ ITEM_STICKS_5, + /* 0x8B */ ITEM_STICKS_10, + /* 0x8C */ ITEM_NUTS_5, + /* 0x8D */ ITEM_NUTS_10, + /* 0x8E */ ITEM_BOMBS_5, + /* 0x8F */ ITEM_BOMBS_10, + /* 0x90 */ ITEM_BOMBS_20, + /* 0x91 */ ITEM_BOMBS_30, + /* 0x92 */ ITEM_ARROWS_SMALL, + /* 0x93 */ ITEM_ARROWS_MEDIUM, + /* 0x94 */ ITEM_ARROWS_LARGE, + /* 0x95 */ ITEM_SEEDS_30, + /* 0x96 */ ITEM_BOMBCHUS_5, + /* 0x97 */ ITEM_BOMBCHUS_20, + /* 0x98 */ ITEM_STICK_UPGRADE_20, + /* 0x99 */ ITEM_STICK_UPGRADE_30, + /* 0x9A */ ITEM_NUT_UPGRADE_30, + /* 0x9B */ ITEM_NUT_UPGRADE_40, + /* 0xFC */ ITEM_LAST_USED = 0xFC, + /* 0xFE */ ITEM_NONE_FE = 0xFE, + /* 0xFF */ ITEM_NONE = 0xFF +} ItemID; + +#define ITEM_TRADE_CHILD ITEM_WEIRD_EGG +#define ITEM_TRADE_ADULT ITEM_POCKET_EGG + +// Get Item result may vary depending on context (chest/shop/scrub/drop) +typedef enum { + /* 0x00 */ GI_NONE, + /* 0x01 */ GI_BOMBS_5, + /* 0x02 */ GI_NUTS_5, + /* 0x03 */ GI_BOMBCHUS_10, + /* 0x04 */ GI_BOW, + /* 0x05 */ GI_SLINGSHOT, + /* 0x06 */ GI_BOOMERANG, + /* 0x07 */ GI_STICKS_1, + /* 0x08 */ GI_HOOKSHOT, + /* 0x09 */ GI_LONGSHOT, + /* 0x0A */ GI_LENS, + /* 0x0B */ GI_LETTER_ZELDA, + /* 0x0C */ GI_OCARINA_OOT, + /* 0x0D */ GI_HAMMER, + /* 0x0E */ GI_COJIRO, + /* 0x0F */ GI_BOTTLE, + /* 0x10 */ GI_POTION_RED, + /* 0x11 */ GI_POTION_GREEN, + /* 0x12 */ GI_POTION_BLUE, + /* 0x13 */ GI_FAIRY, + /* 0x14 */ GI_MILK_BOTTLE, + /* 0x15 */ GI_LETTER_RUTO, + /* 0x16 */ GI_BEAN, + /* 0x17 */ GI_MASK_SKULL, + /* 0x18 */ GI_MASK_SPOOKY, + /* 0x19 */ GI_CHICKEN, // uses bean message ID + /* 0x1A */ GI_MASK_KEATON, + /* 0x1B */ GI_MASK_BUNNY, + /* 0x1C */ GI_MASK_TRUTH, + /* 0x1D */ GI_POCKET_EGG, + /* 0x1E */ GI_POCKET_CUCCO, // uses bean message ID + /* 0x1F */ GI_ODD_MUSHROOM, + /* 0x20 */ GI_ODD_POTION, + /* 0x21 */ GI_SAW, + /* 0x22 */ GI_SWORD_BROKEN, + /* 0x23 */ GI_PRESCRIPTION, + /* 0x24 */ GI_FROG, + /* 0x25 */ GI_EYEDROPS, + /* 0x26 */ GI_CLAIM_CHECK, + /* 0x27 */ GI_SWORD_KOKIRI, + /* 0x28 */ GI_SWORD_KNIFE, + /* 0x29 */ GI_SHIELD_DEKU, // or blue rupee if you have the shield + /* 0x2A */ GI_SHIELD_HYLIAN, // or blue rupee if you have the shield + /* 0x2B */ GI_SHIELD_MIRROR, + /* 0x2C */ GI_TUNIC_GORON, // or blue rupee if you have the tunic + /* 0x2D */ GI_TUNIC_ZORA, // or blue rupee if you have the tunic + /* 0x2E */ GI_BOOTS_IRON, + /* 0x2F */ GI_BOOTS_HOVER, + /* 0x30 */ GI_QUIVER_40, + /* 0x31 */ GI_QUIVER_50, + /* 0x32 */ GI_BOMB_BAG_20, + /* 0x33 */ GI_BOMB_BAG_30, + /* 0x34 */ GI_BOMB_BAG_40, + /* 0x35 */ GI_GAUNTLETS_SILVER, + /* 0x36 */ GI_GAUNTLETS_GOLD, + /* 0x37 */ GI_SCALE_SILVER, + /* 0x38 */ GI_SCALE_GOLD, + /* 0x39 */ GI_STONE_OF_AGONY, + /* 0x3A */ GI_GERUDO_CARD, + /* 0x3B */ GI_OCARINA_FAIRY, // uses Ocarina of Time message ID + /* 0x3C */ GI_SEEDS_5, + /* 0x3D */ GI_HEART_CONTAINER, + /* 0x3E */ GI_HEART_PIECE, + /* 0x3F */ GI_KEY_BOSS, + /* 0x40 */ GI_COMPASS, + /* 0x41 */ GI_MAP, + /* 0x42 */ GI_KEY_SMALL, + /* 0x43 */ GI_MAGIC_SMALL, // or blue rupee if not from a drop + /* 0x44 */ GI_MAGIC_LARGE, // or blue rupee if not from a drop + /* 0x45 */ GI_WALLET_ADULT, + /* 0x46 */ GI_WALLET_GIANT, + /* 0x47 */ GI_WEIRD_EGG, + /* 0x48 */ GI_HEART, + /* 0x49 */ GI_ARROWS_SMALL, // amount changes depending on context + /* 0x4A */ GI_ARROWS_MEDIUM, // amount changes depending on context + /* 0x4B */ GI_ARROWS_LARGE, // amount changes depending on context + /* 0x4C */ GI_RUPEE_GREEN, + /* 0x4D */ GI_RUPEE_BLUE, + /* 0x4E */ GI_RUPEE_RED, + /* 0x4F */ GI_HEART_CONTAINER_2, + /* 0x50 */ GI_MILK, + /* 0x51 */ GI_MASK_GORON, + /* 0x52 */ GI_MASK_ZORA, + /* 0x53 */ GI_MASK_GERUDO, + /* 0x54 */ GI_BRACELET, + /* 0x55 */ GI_RUPEE_PURPLE, + /* 0x56 */ GI_RUPEE_GOLD, + /* 0x57 */ GI_SWORD_BGS, + /* 0x58 */ GI_ARROW_FIRE, + /* 0x59 */ GI_ARROW_ICE, + /* 0x5A */ GI_ARROW_LIGHT, + /* 0x5B */ GI_SKULL_TOKEN, + /* 0x5C */ GI_DINS_FIRE, + /* 0x5D */ GI_FARORES_WIND, + /* 0x5E */ GI_NAYRUS_LOVE, + /* 0x5F */ GI_BULLET_BAG_30, + /* 0x60 */ GI_BULLET_BAG_40, + /* 0x61 */ GI_STICKS_5, + /* 0x62 */ GI_STICKS_10, + /* 0x63 */ GI_NUTS_5_2, + /* 0x64 */ GI_NUTS_10, + /* 0x65 */ GI_BOMBS_1, + /* 0x66 */ GI_BOMBS_10, + /* 0x67 */ GI_BOMBS_20, + /* 0x68 */ GI_BOMBS_30, + /* 0x69 */ GI_SEEDS_30, + /* 0x6A */ GI_BOMBCHUS_5, + /* 0x6B */ GI_BOMBCHUS_20, + /* 0x6C */ GI_FISH, + /* 0x6D */ GI_BUGS, + /* 0x6E */ GI_BLUE_FIRE, + /* 0x6F */ GI_POE, + /* 0x70 */ GI_BIG_POE, + /* 0x71 */ GI_DOOR_KEY, // specific to chest minigame + /* 0x72 */ GI_RUPEE_GREEN_LOSE, // specific to chest minigame + /* 0x73 */ GI_RUPEE_BLUE_LOSE, // specific to chest minigame + /* 0x74 */ GI_RUPEE_RED_LOSE, // specific to chest minigame + /* 0x75 */ GI_RUPEE_PURPLE_LOSE, // specific to chest minigame + /* 0x76 */ GI_HEART_PIECE_WIN, // specific to chest minigame + /* 0x77 */ GI_STICK_UPGRADE_20, + /* 0x78 */ GI_STICK_UPGRADE_30, + /* 0x79 */ GI_NUT_UPGRADE_30, + /* 0x7A */ GI_NUT_UPGRADE_40, + /* 0x7B */ GI_BULLET_BAG_50, + /* 0x7C */ GI_ICE_TRAP, // freezes link when opened from a chest + /* 0x7D */ GI_TEXT_0, // no model appears over Link, shows text id 0 (pocket egg) + /* 0x7E */ GI_MAX +} GetItemID; + +typedef enum { + /* 0x00 */ GID_BOTTLE, + /* 0x01 */ GID_KEY_SMALL, + /* 0x02 */ GID_SONG_MINUET, + /* 0x03 */ GID_SONG_BOLERO, + /* 0x04 */ GID_SONG_SERENADE, + /* 0x05 */ GID_SONG_REQUIEM, + /* 0x06 */ GID_SONG_NOCTURNE, + /* 0x07 */ GID_SONG_PRELUDE, + /* 0x08 */ GID_HEART, + /* 0x09 */ GID_KEY_BOSS, + /* 0x0A */ GID_COMPASS, + /* 0x0B */ GID_MEDALLION_FOREST, + /* 0x0C */ GID_MEDALLION_FIRE, + /* 0x0D */ GID_MEDALLION_WATER, + /* 0x0E */ GID_MEDALLION_SPIRIT, + /* 0x0F */ GID_MEDALLION_SHADOW, + /* 0x10 */ GID_MEDALLION_LIGHT, + /* 0x11 */ GID_NUTS, + /* 0x12 */ GID_HEART_CONTAINER, + /* 0x13 */ GID_HEART_PIECE, + /* 0x14 */ GID_QUIVER_30, + /* 0x15 */ GID_QUIVER_40, + /* 0x16 */ GID_QUIVER_50, + /* 0x17 */ GID_BOMB_BAG_20, + /* 0x18 */ GID_BOMB_BAG_30, + /* 0x19 */ GID_BOMB_BAG_40, + /* 0x1A */ GID_STICK, + /* 0x1B */ GID_DUNGEON_MAP, + /* 0x1C */ GID_SHIELD_DEKU, + /* 0x1D */ GID_MAGIC_SMALL, + /* 0x1E */ GID_MAGIC_LARGE, + /* 0x1F */ GID_BOMB, + /* 0x20 */ GID_STONE_OF_AGONY, + /* 0x21 */ GID_WALLET_ADULT, + /* 0x22 */ GID_WALLET_GIANT, + /* 0x23 */ GID_GERUDO_CARD, + /* 0x24 */ GID_ARROWS_SMALL, + /* 0x25 */ GID_ARROWS_MEDIUM, + /* 0x26 */ GID_ARROWS_LARGE, + /* 0x27 */ GID_BOMBCHU, + /* 0x28 */ GID_EGG, + /* 0x29 */ GID_SCALE_SILVER, + /* 0x2A */ GID_SCALE_GOLDEN, + /* 0x2B */ GID_SHIELD_HYLIAN, + /* 0x2C */ GID_HOOKSHOT, + /* 0x2D */ GID_LONGSHOT, + /* 0x2E */ GID_OCARINA_TIME, + /* 0x2F */ GID_MILK, + /* 0x30 */ GID_MASK_KEATON, + /* 0x31 */ GID_MASK_SPOOKY, + /* 0x32 */ GID_SLINGSHOT, + /* 0x33 */ GID_BOOMERANG, + /* 0x34 */ GID_BOW, + /* 0x35 */ GID_LENS, + /* 0x36 */ GID_POTION_GREEN, + /* 0x37 */ GID_POTION_RED, + /* 0x38 */ GID_POTION_BLUE, + /* 0x39 */ GID_SHIELD_MIRROR, + /* 0x3A */ GID_LETTER_ZELDA, + /* 0x3B */ GID_TUNIC_GORON, + /* 0x3C */ GID_TUNIC_ZORA, + /* 0x3D */ GID_BEAN, + /* 0x3E */ GID_FISH, + /* 0x3F */ GID_SAW, + /* 0x40 */ GID_HAMMER, + /* 0x41 */ GID_GRASS, + /* 0x42 */ GID_SWORD_BGS, + /* 0x43 */ GID_CHICKEN, + /* 0x44 */ GID_LETTER_RUTO, + /* 0x45 */ GID_OCARINA_FAIRY, + /* 0x46 */ GID_BOOTS_IRON, + /* 0x47 */ GID_SEEDS, + /* 0x48 */ GID_GAUNTLETS_SILVER, + /* 0x49 */ GID_GAUNTLETS_GOLD, + /* 0x4A */ GID_NCOIN_YELLOW, + /* 0x4B */ GID_NCOIN_RED, + /* 0x4C */ GID_NCOIN_GREEN, + /* 0x4D */ GID_NCOIN_BLUE, + /* 0x4E */ GID_MASK_SKULL, + /* 0x4F */ GID_MASK_BUNNY, + /* 0x50 */ GID_MASK_TRUTH, + /* 0x51 */ GID_EYEDROPS, + /* 0x52 */ GID_ODD_POTION, + /* 0x53 */ GID_ODD_MUSHROOM, + /* 0x54 */ GID_CLAIM_CHECK, + /* 0x55 */ GID_SWORD_BROKEN, + /* 0x56 */ GID_PRESCRIPTION, + /* 0x57 */ GID_BRACELET, + /* 0x58 */ GID_SOLDOUT, + /* 0x59 */ GID_FROG, + /* 0x5A */ GID_MASK_GORON, + /* 0x5B */ GID_MASK_ZORA, + /* 0x5C */ GID_MASK_GERUDO, + /* 0x5D */ GID_COJIRO, + /* 0x5E */ GID_BOOTS_HOVER, + /* 0x5F */ GID_ARROW_FIRE, + /* 0x60 */ GID_ARROW_ICE, + /* 0x61 */ GID_ARROW_LIGHT, + /* 0x62 */ GID_SKULL_TOKEN, + /* 0x63 */ GID_DINS_FIRE, + /* 0x64 */ GID_FARORES_WIND, + /* 0x65 */ GID_NAYRUS_LOVE, + /* 0x66 */ GID_BLUE_FIRE, + /* 0x67 */ GID_BUG, + /* 0x68 */ GID_BUTTERFLY, + /* 0x69 */ GID_POE, + /* 0x6A */ GID_FAIRY, + /* 0x6B */ GID_BULLET_BAG, + /* 0x6C */ GID_RUPEE_GREEN, + /* 0x6D */ GID_RUPEE_BLUE, + /* 0x6E */ GID_RUPEE_RED, + /* 0x6F */ GID_BIG_POE, + /* 0x70 */ GID_RUPEE_PURPLE, + /* 0x71 */ GID_RUPEE_GOLD, + /* 0x72 */ GID_BULLET_BAG_50, + /* 0x73 */ GID_SWORD_KOKIRI, + /* 0x74 */ GID_SKULL_TOKEN_2, + /* 0x75 */ GID_MAX +} GetItemDrawID; + +typedef enum { + /* 0x00 */ EXCH_ITEM_NONE, + /* 0x01 */ EXCH_ITEM_LETTER_ZELDA, + /* 0x02 */ EXCH_ITEM_WEIRD_EGG, + /* 0x03 */ EXCH_ITEM_CHICKEN, + /* 0x04 */ EXCH_ITEM_BEAN, + /* 0x05 */ EXCH_ITEM_POCKET_EGG, + /* 0x06 */ EXCH_ITEM_POCKET_CUCCO, + /* 0x07 */ EXCH_ITEM_COJIRO, + /* 0x08 */ EXCH_ITEM_ODD_MUSHROOM, + /* 0x09 */ EXCH_ITEM_ODD_POTION, + /* 0x0A */ EXCH_ITEM_SAW, + /* 0x0B */ EXCH_ITEM_SWORD_BROKEN, + /* 0x0C */ EXCH_ITEM_PRESCRIPTION, + /* 0x0D */ EXCH_ITEM_FROG, + /* 0x0E */ EXCH_ITEM_EYEDROPS, + /* 0x0F */ EXCH_ITEM_CLAIM_CHECK, + /* 0x10 */ EXCH_ITEM_MASK_SKULL, + /* 0x11 */ EXCH_ITEM_MASK_SPOOKY, + /* 0x12 */ EXCH_ITEM_MASK_KEATON, + /* 0x13 */ EXCH_ITEM_MASK_BUNNY, + /* 0x14 */ EXCH_ITEM_MASK_TRUTH, + /* 0x15 */ EXCH_ITEM_MASK_GORON, + /* 0x16 */ EXCH_ITEM_MASK_ZORA, + /* 0x17 */ EXCH_ITEM_MASK_GERUDO, + /* 0x18 */ EXCH_ITEM_FISH, + /* 0x19 */ EXCH_ITEM_BLUE_FIRE, + /* 0x1A */ EXCH_ITEM_BUG, + /* 0x1B */ EXCH_ITEM_POE, + /* 0x1C */ EXCH_ITEM_BIG_POE, + /* 0x1D */ EXCH_ITEM_LETTER_RUTO, + /* 0x1E */ EXCH_ITEM_MAX +} ExchangeItemID; + +#endif diff --git a/soh/include/z64light.h b/soh/include/z64light.h new file mode 100644 index 000000000..639490022 --- /dev/null +++ b/soh/include/z64light.h @@ -0,0 +1,62 @@ +#ifndef Z64LIGHT_H +#define Z64LIGHT_H + +#include "ultra64.h" +#include "ultra64/gbi.h" +#include "z64math.h" +#include "color.h" + +typedef struct { + /* 0x0 */ s16 x; + /* 0x2 */ s16 y; + /* 0x4 */ s16 z; + /* 0x6 */ u8 color[3]; + /* 0x9 */ u8 drawGlow; + /* 0xA */ s16 radius; +} LightPoint; // size = 0xC + +typedef struct { + /* 0x0 */ s8 x; + /* 0x1 */ s8 y; + /* 0x2 */ s8 z; + /* 0x3 */ u8 color[3]; +} LightDirectional; // size = 0x6 + +typedef union { + LightPoint point; + LightDirectional dir; +} LightParams; // size = 0xC + +typedef struct { + /* 0x0 */ u8 type; + /* 0x2 */ LightParams params; +} LightInfo; // size = 0xE + +typedef struct Lights { + /* 0x00 */ u8 numLights; + /* 0x08 */ Lightsn l; +} Lights; // size = 0x80 + +typedef struct LightNode { + /* 0x0 */ LightInfo* info; + /* 0x4 */ struct LightNode* prev; + /* 0x8 */ struct LightNode* next; +} LightNode; // size = 0xC + +typedef struct { + /* 0x0 */ LightNode* listHead; + /* 0x4 */ u8 ambientColor[3]; + /* 0x7 */ u8 fogColor[3]; + /* 0xA */ s16 fogNear; // how close until fog starts taking effect. range 0 - 1000 + /* 0xC */ s16 fogFar; // how far until fog starts to saturate. range 0 - 1000 +} LightContext; // size = 0x10 + +typedef enum { + /* 0x00 */ LIGHT_POINT_NOGLOW, + /* 0x01 */ LIGHT_DIRECTIONAL, + /* 0x02 */ LIGHT_POINT_GLOW +} LightType; + +typedef void (*LightsBindFunc)(Lights* lights, LightParams* params, Vec3f* vec); + +#endif diff --git a/soh/include/z64map_mark.h b/soh/include/z64map_mark.h new file mode 100644 index 000000000..d86219784 --- /dev/null +++ b/soh/include/z64map_mark.h @@ -0,0 +1,25 @@ +#ifndef Z64MAP_MARK_H +#define Z64MAP_MARK_H + +#include "ultra64.h" + +#define MAP_MARK_NONE -1 +#define MAP_MARK_CHEST 0 +#define MAP_MARK_BOSS 1 + +typedef struct { + /* 0x00 */ s8 chestFlag; // chest icon is only displayed if this flag is not set for the current room + /* 0x01 */ u8 x, y; // coordinates to place the icon (top-left corner), relative to the minimap texture +} MapMarkPoint; // size = 0x3 + +typedef struct { + /* 0x00 */ s8 markType; // 0 for the chest icon, 1 for the boss skull icon, -1 for none + /* 0x01 */ u8 count; // number of icons to display + /* 0x02 */ MapMarkPoint points[12]; +} MapMarkIconData; // size = 0x26 + +typedef MapMarkIconData MapMarkData[3]; // size = 0x72 + +extern MapMarkData* gMapMarkDataTable[]; + +#endif diff --git a/soh/include/z64math.h b/soh/include/z64math.h new file mode 100644 index 000000000..3e7813ada --- /dev/null +++ b/soh/include/z64math.h @@ -0,0 +1,119 @@ +#ifndef Z64MATH_H +#define Z64MATH_H + +#include "ultra64.h" + +#define VEC_SET(V,X,Y,Z) (V).x=(X);(V).y=(Y);(V).z=(Z) + +#ifdef __cplusplus +#define Vec2f _Vec2f +#define Vec3f _Vec3f +#define Vec3s _Vec3s +#endif + +typedef struct { + f32 x, y; +} Vec2f; // size = 0x08 + +typedef struct { + f32 x, y, z; +} Vec3f; // size = 0x0C + +typedef struct { + u16 x, y, z; +} Vec3us; // size = 0x06 + +typedef struct { + s16 x, y, z; +} Vec3s; // size = 0x06 + +typedef struct { + s32 x, y, z; +} Vec3i; // size = 0x0C + +typedef struct { + Vec3s center; + s16 radius; +} Sphere16; // size = 0x08 + +typedef struct { + Vec3f center; + f32 radius; +} Spheref; // size = 0x10 + +typedef struct { + Vec3f normal; + f32 originDist; +} Plane; // size = 0x10 + +typedef struct { + Vec3f vtx[3]; + Plane plane; +} TriNorm; // size = 0x34 + +typedef struct { + /* 0x0000 */ s16 radius; + /* 0x0002 */ s16 height; + /* 0x0004 */ s16 yShift; + /* 0x0006 */ Vec3s pos; +} Cylinder16; // size = 0x0C + +typedef struct { + /* 0x00 */ f32 radius; + /* 0x04 */ f32 height; + /* 0x08 */ f32 yShift; + /* 0x0C */ Vec3f pos; +} Cylinderf; // size = 0x18 + +typedef struct { + /* 0x0000 */ Vec3f point; + /* 0x000C */ Vec3f dir; +} InfiniteLine; // size = 0x18 + +typedef struct { + /* 0x0000 */ Vec3f a; + /* 0x000C */ Vec3f b; +} Linef; // size = 0x18 + +// Defines a point in the spherical coordinate system +typedef struct { + /* 0x00 */ f32 r; // radius + /* 0x04 */ s16 pitch; // polar (zenith) angle + /* 0x06 */ s16 yaw; // azimuthal angle +} VecSph; // size = 0x08 + +#define LERP(x, y, scale) (((y) - (x)) * (scale) + (x)) +#define LERP32(x, y, scale) ((s32)(((y) - (x)) * (scale)) + (x)) +#define LERP16(x, y, scale) ((s16)(((y) - (x)) * (scale)) + (x)) +#define F32_LERP(v0,v1,t) ((v0) * (1.0f - (t)) + (v1) * (t)) +#define F32_LERPIMP(v0, v1, t) (v0 + ((v1 - v0) * t)) +#define F32_LERPIMPINV(v0, v1, t) ((v0) + (((v1) - (v0)) / (t))) +#define BINANG_LERPIMP(v0, v1, t) ((v0) + (s16)(BINANG_SUB((v1), (v0)) * (t))) +#define BINANG_LERPIMPINV(v0, v1, t) ((v0) + BINANG_SUB((v1), (v0)) / (t)) + +#define VEC3F_LERPIMPDST(dst, v0, v1, t){ \ + (dst)->x = (v0)->x + (((v1)->x - (v0)->x) * t); \ + (dst)->y = (v0)->y + (((v1)->y - (v0)->y) * t); \ + (dst)->z = (v0)->z + (((v1)->z - (v0)->z) * t); \ +} + +#define IS_ZERO(f) (fabsf(f) < 0.008f) + +// Trig macros +#define DEGF_TO_BINANG(degreesf) (s16)(degreesf * 182.04167f + .5f) +#define RADF_TO_BINANG(radf) (s16)(radf * (32768.0f / M_PI)) +#define RADF_TO_DEGF(radf) (radf * (180.0f / M_PI)) +#define DEGF_TO_RADF(degf) (degf * (M_PI / 180.0f)) +#define BINANG_ROT180(angle) ((s16)(angle - 0x7FFF)) +#define BINANG_SUB(a, b) ((s16)(a - b)) +#define DEG_TO_RAD(degrees) ((degrees) * (M_PI / 180.0f)) +#define BINANG_TO_DEGF(binang) ((f32)binang * (360.0001525f / 65535.0f)) +#define BINANG_TO_RAD(binang) (((f32)binang / 32768.0f) * M_PI) + +// Vector macros +#define SQXZ(vec) ((vec.x) * (vec.x) + (vec.z) * (vec.z)) +#define DOTXZ(vec1, vec2) ((vec1.x) * (vec2.x) + (vec1.z) * (vec2.z)) +#define SQXYZ(vec) ((vec.x) * (vec.x) + (vec.y) * (vec.y) + (vec.z) * (vec.z)) +#define DOTXYZ(vec1, vec2) ((vec1.x) * (vec2.x) + (vec1.y) * (vec2.y) + (vec1.z) * (vec2.z)) + +#endif diff --git a/soh/include/z64object.h b/soh/include/z64object.h new file mode 100644 index 000000000..bf7871431 --- /dev/null +++ b/soh/include/z64object.h @@ -0,0 +1,20 @@ +#ifndef Z64OBJECT_H +#define Z64OBJECT_H + +//#define OBJECT_EXCHANGE_BANK_MAX 19 +#define OBJECT_EXCHANGE_BANK_MAX 128 + +#define DEFINE_OBJECT(_0, enum) enum, +#define DEFINE_OBJECT_NULL(_0, enum) enum, +#define DEFINE_OBJECT_UNSET(enum) enum, + +typedef enum { + #include "tables/object_table.h" + /* 0x0192 */ OBJECT_ID_MAX +} ObjectID; + +#undef DEFINE_OBJECT +#undef DEFINE_OBJECT_NULL +#undef DEFINE_OBJECT_UNSET + +#endif diff --git a/soh/include/z64player.h b/soh/include/z64player.h new file mode 100644 index 000000000..6771ef5c6 --- /dev/null +++ b/soh/include/z64player.h @@ -0,0 +1,608 @@ +#ifndef Z64PLAYER_H +#define Z64PLAYER_H + +#include "z64actor.h" + +struct Player; + +typedef enum { + /* 0 */ PLAYER_SWORD_NONE, + /* 1 */ PLAYER_SWORD_KOKIRI, + /* 2 */ PLAYER_SWORD_MASTER, + /* 3 */ PLAYER_SWORD_BGS, + /* 4 */ PLAYER_SWORD_MAX +} PlayerSword; + +typedef enum { + /* 0x00 */ PLAYER_SHIELD_NONE, + /* 0x01 */ PLAYER_SHIELD_DEKU, + /* 0x02 */ PLAYER_SHIELD_HYLIAN, + /* 0x03 */ PLAYER_SHIELD_MIRROR, + /* 0x04 */ PLAYER_SHIELD_MAX +} PlayerShield; + +typedef enum { + /* 0x00 */ PLAYER_TUNIC_KOKIRI, + /* 0x01 */ PLAYER_TUNIC_GORON, + /* 0x02 */ PLAYER_TUNIC_ZORA, + /* 0x03 */ PLAYER_TUNIC_MAX +} PlayerTunic; + +typedef enum { + /* 0x00 */ PLAYER_BOOTS_KOKIRI, + /* 0x01 */ PLAYER_BOOTS_IRON, + /* 0x02 */ PLAYER_BOOTS_HOVER, + /* Values below are only relevant when setting regs in Player_SetBootData */ + /* 0x03 */ PLAYER_BOOTS_INDOOR, + /* 0x04 */ PLAYER_BOOTS_IRON_UNDERWATER, + /* 0x05 */ PLAYER_BOOTS_KOKIRI_CHILD, + /* 0x06 */ PLAYER_BOOTS_MAX +} PlayerBoots; + +typedef enum { + /* 0x00 */ PLAYER_STR_NONE, + /* 0x01 */ PLAYER_STR_BRACELET, + /* 0x02 */ PLAYER_STR_SILVER_G, + /* 0x03 */ PLAYER_STR_GOLD_G, + /* 0x04 */ PLAYER_STR_MAX +} PlayerStrength; + +typedef enum { + /* 0x00 */ PLAYER_MASK_NONE, + /* 0x01 */ PLAYER_MASK_KEATON, + /* 0x02 */ PLAYER_MASK_SKULL, + /* 0x03 */ PLAYER_MASK_SPOOKY, + /* 0x04 */ PLAYER_MASK_BUNNY, + /* 0x05 */ PLAYER_MASK_GORON, + /* 0x06 */ PLAYER_MASK_ZORA, + /* 0x07 */ PLAYER_MASK_GERUDO, + /* 0x08 */ PLAYER_MASK_TRUTH, + /* 0x09 */ PLAYER_MASK_MAX +} PlayerMask; + +typedef enum { + /* 0x00 */ PLAYER_AP_NONE, + /* 0x01 */ PLAYER_AP_LAST_USED, + /* 0x02 */ PLAYER_AP_FISHING_POLE, + /* 0x03 */ PLAYER_AP_SWORD_MASTER, + /* 0x04 */ PLAYER_AP_SWORD_KOKIRI, + /* 0x05 */ PLAYER_AP_SWORD_BGS, + /* 0x06 */ PLAYER_AP_STICK, + /* 0x07 */ PLAYER_AP_HAMMER, + /* 0x08 */ PLAYER_AP_BOW, + /* 0x09 */ PLAYER_AP_BOW_FIRE, + /* 0x0A */ PLAYER_AP_BOW_ICE, + /* 0x0B */ PLAYER_AP_BOW_LIGHT, + /* 0x0C */ PLAYER_AP_BOW_0C, + /* 0x0D */ PLAYER_AP_BOW_0D, + /* 0x0E */ PLAYER_AP_BOW_0E, + /* 0x0F */ PLAYER_AP_SLINGSHOT, + /* 0x10 */ PLAYER_AP_HOOKSHOT, + /* 0x11 */ PLAYER_AP_LONGSHOT, + /* 0x12 */ PLAYER_AP_BOMB, + /* 0x13 */ PLAYER_AP_BOMBCHU, + /* 0x14 */ PLAYER_AP_BOOMERANG, + /* 0x15 */ PLAYER_AP_MAGIC_SPELL_15, + /* 0x16 */ PLAYER_AP_MAGIC_SPELL_16, + /* 0x17 */ PLAYER_AP_MAGIC_SPELL_17, + /* 0x18 */ PLAYER_AP_FARORES_WIND, + /* 0x19 */ PLAYER_AP_NAYRUS_LOVE, + /* 0x1A */ PLAYER_AP_DINS_FIRE, + /* 0x1B */ PLAYER_AP_NUT, + /* 0x1C */ PLAYER_AP_OCARINA_FAIRY, + /* 0x1D */ PLAYER_AP_OCARINA_TIME, + /* 0x1E */ PLAYER_AP_BOTTLE, + /* 0x1F */ PLAYER_AP_BOTTLE_FISH, + /* 0x20 */ PLAYER_AP_BOTTLE_FIRE, + /* 0x21 */ PLAYER_AP_BOTTLE_BUG, + /* 0x22 */ PLAYER_AP_BOTTLE_POE, + /* 0x23 */ PLAYER_AP_BOTTLE_BIG_POE, + /* 0x24 */ PLAYER_AP_BOTTLE_LETTER, + /* 0x25 */ PLAYER_AP_BOTTLE_POTION_RED, + /* 0x26 */ PLAYER_AP_BOTTLE_POTION_BLUE, + /* 0x27 */ PLAYER_AP_BOTTLE_POTION_GREEN, + /* 0x28 */ PLAYER_AP_BOTTLE_MILK, + /* 0x29 */ PLAYER_AP_BOTTLE_MILK_HALF, + /* 0x2A */ PLAYER_AP_BOTTLE_FAIRY, + /* 0x2B */ PLAYER_AP_LETTER_ZELDA, + /* 0x2C */ PLAYER_AP_WEIRD_EGG, + /* 0x2D */ PLAYER_AP_CHICKEN, + /* 0x2E */ PLAYER_AP_BEAN, + /* 0x2F */ PLAYER_AP_POCKET_EGG, + /* 0x30 */ PLAYER_AP_POCKET_CUCCO, + /* 0x31 */ PLAYER_AP_COJIRO, + /* 0x32 */ PLAYER_AP_ODD_MUSHROOM, + /* 0x33 */ PLAYER_AP_ODD_POTION, + /* 0x34 */ PLAYER_AP_SAW, + /* 0x35 */ PLAYER_AP_SWORD_BROKEN, + /* 0x36 */ PLAYER_AP_PRESCRIPTION, + /* 0x37 */ PLAYER_AP_FROG, + /* 0x38 */ PLAYER_AP_EYEDROPS, + /* 0x39 */ PLAYER_AP_CLAIM_CHECK, + /* 0x3A */ PLAYER_AP_MASK_KEATON, + /* 0x3B */ PLAYER_AP_MASK_SKULL, + /* 0x3C */ PLAYER_AP_MASK_SPOOKY, + /* 0x3D */ PLAYER_AP_MASK_BUNNY, + /* 0x3E */ PLAYER_AP_MASK_GORON, + /* 0x3F */ PLAYER_AP_MASK_ZORA, + /* 0x40 */ PLAYER_AP_MASK_GERUDO, + /* 0x41 */ PLAYER_AP_MASK_TRUTH, + /* 0x42 */ PLAYER_AP_LENS, + /* 0x43 */ PLAYER_AP_MAX +} PlayerActionParam; + +typedef enum { + /* 0x00 */ PLAYER_LIMB_NONE, + /* 0x01 */ PLAYER_LIMB_ROOT, + /* 0x02 */ PLAYER_LIMB_WAIST, + /* 0x03 */ PLAYER_LIMB_LOWER, + /* 0x04 */ PLAYER_LIMB_R_THIGH, + /* 0x05 */ PLAYER_LIMB_R_SHIN, + /* 0x06 */ PLAYER_LIMB_R_FOOT, + /* 0x07 */ PLAYER_LIMB_L_THIGH, + /* 0x08 */ PLAYER_LIMB_L_SHIN, + /* 0x09 */ PLAYER_LIMB_L_FOOT, + /* 0x0A */ PLAYER_LIMB_UPPER, + /* 0x0B */ PLAYER_LIMB_HEAD, + /* 0x0C */ PLAYER_LIMB_HAT, + /* 0x0D */ PLAYER_LIMB_COLLAR, + /* 0x0E */ PLAYER_LIMB_L_SHOULDER, + /* 0x0F */ PLAYER_LIMB_L_FOREARM, + /* 0x10 */ PLAYER_LIMB_L_HAND, + /* 0x11 */ PLAYER_LIMB_R_SHOULDER, + /* 0x12 */ PLAYER_LIMB_R_FOREARM, + /* 0x13 */ PLAYER_LIMB_R_HAND, + /* 0x14 */ PLAYER_LIMB_SHEATH, + /* 0x15 */ PLAYER_LIMB_TORSO, + /* 0x16 */ PLAYER_LIMB_MAX +} PlayerLimb; + +typedef enum { + /* 0 */ PLAYER_BODYPART_WAIST, // PLAYER_LIMB_WAIST + /* 1 */ PLAYER_BODYPART_R_THIGH, // PLAYER_LIMB_R_THIGH + /* 2 */ PLAYER_BODYPART_R_SHIN, // PLAYER_LIMB_R_SHIN + /* 3 */ PLAYER_BODYPART_R_FOOT, // PLAYER_LIMB_R_FOOT + /* 4 */ PLAYER_BODYPART_L_THIGH, // PLAYER_LIMB_L_THIGH + /* 5 */ PLAYER_BODYPART_L_SHIN, // PLAYER_LIMB_L_SHIN + /* 6 */ PLAYER_BODYPART_L_FOOT, // PLAYER_LIMB_L_FOOT + /* 7 */ PLAYER_BODYPART_HEAD, // PLAYER_LIMB_HEAD + /* 8 */ PLAYER_BODYPART_HAT, // PLAYER_LIMB_HAT + /* 9 */ PLAYER_BODYPART_COLLAR, // PLAYER_LIMB_COLLAR + /* 10 */ PLAYER_BODYPART_L_SHOULDER, // PLAYER_LIMB_L_SHOULDER + /* 11 */ PLAYER_BODYPART_L_FOREARM, // PLAYER_LIMB_L_FOREARM + /* 12 */ PLAYER_BODYPART_L_HAND, // PLAYER_LIMB_L_HAND + /* 13 */ PLAYER_BODYPART_R_SHOULDER, // PLAYER_LIMB_R_SHOULDER + /* 14 */ PLAYER_BODYPART_R_FOREARM, // PLAYER_LIMB_R_FOREARM + /* 15 */ PLAYER_BODYPART_R_HAND, // PLAYER_LIMB_R_HAND + /* 16 */ PLAYER_BODYPART_SHEATH, // PLAYER_LIMB_SHEATH + /* 17 */ PLAYER_BODYPART_TORSO, // PLAYER_LIMB_TORSO + /* 18 */ PLAYER_BODYPART_MAX +} PlayerBodyPart; + +typedef enum { + /* -1 */ PLAYER_DOORTYPE_AJAR = -1, + /* 0 */ PLAYER_DOORTYPE_NONE, + /* 1 */ PLAYER_DOORTYPE_HANDLE, + /* 2 */ PLAYER_DOORTYPE_SLIDING, + /* 3 */ PLAYER_DOORTYPE_FAKE +} PlayerDoorType; + +typedef enum { + /* 0 */ PLAYER_MODELGROUP_0, // unused (except with the `Player_OverrideLimbDrawPause` bug) + /* 1 */ PLAYER_MODELGROUP_CHILD_HYLIAN_SHIELD, // kokiri/master sword, shield not in hand + /* 2 */ PLAYER_MODELGROUP_SWORD, // kokiri/master sword and possibly shield + /* 3 */ PLAYER_MODELGROUP_DEFAULT, // non-specific models, for items that don't have particular link models + /* 4 */ PLAYER_MODELGROUP_4, // unused, same as PLAYER_MODELGROUP_DEFAULT + /* 5 */ PLAYER_MODELGROUP_BGS, // biggoron sword + /* 6 */ PLAYER_MODELGROUP_BOW_SLINGSHOT, // bow/slingshot + /* 7 */ PLAYER_MODELGROUP_EXPLOSIVES, // bombs, bombchus, same as PLAYER_MODELGROUP_DEFAULT + /* 8 */ PLAYER_MODELGROUP_BOOMERANG, + /* 9 */ PLAYER_MODELGROUP_HOOKSHOT, + /* 10 */ PLAYER_MODELGROUP_10, // stick/fishing pole (which are drawn separately) + /* 11 */ PLAYER_MODELGROUP_HAMMER, + /* 12 */ PLAYER_MODELGROUP_OCARINA, // ocarina + /* 13 */ PLAYER_MODELGROUP_OOT, // ocarina of time + /* 14 */ PLAYER_MODELGROUP_BOTTLE, // bottles (drawn separately) + /* 15 */ PLAYER_MODELGROUP_15, // "last used" + /* 16 */ PLAYER_MODELGROUP_MAX +} PlayerModelGroup; + +typedef enum { + /* 0 */ PLAYER_MODELGROUPENTRY_ANIM, + /* 1 */ PLAYER_MODELGROUPENTRY_LEFT_HAND, + /* 2 */ PLAYER_MODELGROUPENTRY_RIGHT_HAND, + /* 3 */ PLAYER_MODELGROUPENTRY_SHEATH, + /* 4 */ PLAYER_MODELGROUPENTRY_WAIST, + /* 5 */ PLAYER_MODELGROUPENTRY_MAX +} PlayerModelGroupEntry; + +typedef enum { + // left hand + /* 0 */ PLAYER_MODELTYPE_LH_OPEN, // empty open hand + /* 1 */ PLAYER_MODELTYPE_LH_CLOSED, // empty closed hand + /* 2 */ PLAYER_MODELTYPE_LH_SWORD, // holding kokiri/master sword + /* 3 */ PLAYER_MODELTYPE_3, // unused, same as PLAYER_MODELTYPE_LH_SWORD + /* 4 */ PLAYER_MODELTYPE_LH_BGS, // holding bgs/broken giant knife (child: master sword) + /* 5 */ PLAYER_MODELTYPE_LH_HAMMER, // holding hammer (child: empty hand) + /* 6 */ PLAYER_MODELTYPE_LH_BOOMERANG, // holding boomerang (adult: empty hand) + /* 7 */ PLAYER_MODELTYPE_LH_BOTTLE, // holding bottle (bottle drawn separately) + // right hand + /* 8 */ PLAYER_MODELTYPE_RH_OPEN, // empty open hand + /* 9 */ PLAYER_MODELTYPE_RH_CLOSED, // empty closed hand + /* 10 */ PLAYER_MODELTYPE_RH_SHIELD, // holding a shield (including no shield) + /* 11 */ PLAYER_MODELTYPE_RH_BOW_SLINGSHOT, // holding bow/slingshot + /* 12 */ PLAYER_MODELTYPE_12, // unused, same as PLAYER_MODELTYPE_RH_BOW_SLINGSHOT + /* 13 */ PLAYER_MODELTYPE_RH_OCARINA, // holding ocarina (child: fairy ocarina, adult: OoT) + /* 14 */ PLAYER_MODELTYPE_RH_OOT, // holding OoT + /* 15 */ PLAYER_MODELTYPE_RH_HOOKSHOT, // holding hookshot (child: empty hand) + // sheath + /* 16 */ PLAYER_MODELTYPE_SHEATH_16, // sheathed kokiri/master sword? + /* 17 */ PLAYER_MODELTYPE_SHEATH_17, // empty sheath? + /* 18 */ PLAYER_MODELTYPE_SHEATH_18, // sword sheathed and shield on back? + /* 19 */ PLAYER_MODELTYPE_SHEATH_19, // empty sheath and shield on back? + // waist + /* 20 */ PLAYER_MODELTYPE_WAIST, + /* 21 */ PLAYER_MODELTYPE_MAX, + /* 0xFF */ PLAYER_MODELTYPE_RH_FF = 0xFF // disable shield collider, cutscene-specific +} PlayerModelType; + +typedef enum { + /* 0 */ PLAYER_ANIMTYPE_0, + /* 1 */ PLAYER_ANIMTYPE_1, + /* 2 */ PLAYER_ANIMTYPE_2, + /* 3 */ PLAYER_ANIMTYPE_3, + /* 4 */ PLAYER_ANIMTYPE_4, + /* 5 */ PLAYER_ANIMTYPE_5, + /* 6 */ PLAYER_ANIMTYPE_MAX +} PlayerAnimType; + +typedef enum { + /* 0 */ PLAYER_ANIMGROUP_0, + /* 1 */ PLAYER_ANIMGROUP_1, + /* 2 */ PLAYER_ANIMGROUP_2, + /* 3 */ PLAYER_ANIMGROUP_3, + /* 4 */ PLAYER_ANIMGROUP_4, + /* 5 */ PLAYER_ANIMGROUP_5, + /* 6 */ PLAYER_ANIMGROUP_6, + /* 7 */ PLAYER_ANIMGROUP_7, + /* 8 */ PLAYER_ANIMGROUP_8, + /* 9 */ PLAYER_ANIMGROUP_9, + /* 10 */ PLAYER_ANIMGROUP_10, + /* 11 */ PLAYER_ANIMGROUP_11, + /* 12 */ PLAYER_ANIMGROUP_12, + /* 13 */ PLAYER_ANIMGROUP_13, + /* 14 */ PLAYER_ANIMGROUP_14, + /* 15 */ PLAYER_ANIMGROUP_15, + /* 16 */ PLAYER_ANIMGROUP_16, + /* 17 */ PLAYER_ANIMGROUP_17, + /* 18 */ PLAYER_ANIMGROUP_18, + /* 19 */ PLAYER_ANIMGROUP_19, + /* 20 */ PLAYER_ANIMGROUP_20, + /* 21 */ PLAYER_ANIMGROUP_21, + /* 22 */ PLAYER_ANIMGROUP_22, + /* 23 */ PLAYER_ANIMGROUP_23, + /* 24 */ PLAYER_ANIMGROUP_24, + /* 25 */ PLAYER_ANIMGROUP_25, + /* 26 */ PLAYER_ANIMGROUP_26, + /* 27 */ PLAYER_ANIMGROUP_27, + /* 28 */ PLAYER_ANIMGROUP_28, + /* 29 */ PLAYER_ANIMGROUP_29, + /* 30 */ PLAYER_ANIMGROUP_30, + /* 31 */ PLAYER_ANIMGROUP_31, + /* 32 */ PLAYER_ANIMGROUP_32, + /* 33 */ PLAYER_ANIMGROUP_33, + /* 34 */ PLAYER_ANIMGROUP_34, + /* 35 */ PLAYER_ANIMGROUP_35, + /* 36 */ PLAYER_ANIMGROUP_36, + /* 37 */ PLAYER_ANIMGROUP_37, + /* 38 */ PLAYER_ANIMGROUP_38, + /* 39 */ PLAYER_ANIMGROUP_39, + /* 40 */ PLAYER_ANIMGROUP_40, + /* 41 */ PLAYER_ANIMGROUP_41, + /* 42 */ PLAYER_ANIMGROUP_42, + /* 43 */ PLAYER_ANIMGROUP_43, + /* 44 */ PLAYER_ANIMGROUP_44, + /* 45 */ PLAYER_ANIMGROUP_MAX +} PlayerAnimGroup; + +#define PLAYER_LIMB_BUF_COUNT PLAYER_LIMB_MAX + 2 // 2 extra entries in limb buffers? + +typedef struct { + /* 0x00 */ f32 unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ f32 unk_08; + /* 0x0C */ f32 unk_0C; + /* 0x10 */ f32 unk_10; + /* 0x14 */ f32 unk_14; + /* 0x18 */ f32 unk_18; + /* 0x1C */ f32 unk_1C; + /* 0x20 */ f32 unk_20; + /* 0x24 */ f32 unk_24; + /* 0x28 */ f32 unk_28; + /* 0x2C */ f32 unk_2C; + /* 0x30 */ f32 unk_30; + /* 0x34 */ f32 unk_34; + /* 0x38 */ f32 unk_38; + /* 0x3C */ f32 unk_3C; + /* 0x40 */ f32 unk_40; + /* 0x44 */ Vec3s unk_44; + /* 0x4A */ Vec3s unk_4A[4]; + /* 0x62 */ Vec3s unk_62[4]; + /* 0x7A */ Vec3s unk_7A[2]; + /* 0x86 */ Vec3s unk_86[2]; + /* 0x92 */ u16 unk_92; + /* 0x94 */ u16 unk_94; + /* 0x98 */ LinkAnimationHeader* unk_98; + /* 0x9C */ LinkAnimationHeader* unk_9C; + /* 0xA0 */ LinkAnimationHeader* unk_A0; + /* 0xA4 */ LinkAnimationHeader* unk_A4; + /* 0xA8 */ LinkAnimationHeader* unk_A8; + /* 0xAC */ LinkAnimationHeader* unk_AC[4]; + /* 0xBC */ LinkAnimationHeader* unk_BC[2]; + /* 0xC4 */ LinkAnimationHeader* unk_C4[2]; + /* 0xCC */ LinkAnimationHeader* unk_CC[2]; +} PlayerAgeProperties; // size = 0xD4 + +typedef struct { + /* 0x00 */ s32 active; + /* 0x04 */ Vec3f tip; + /* 0x10 */ Vec3f base; +} WeaponInfo; // size = 0x1C + +#define PLAYER_STATE1_0 (1 << 0) +#define PLAYER_STATE1_1 (1 << 1) +#define PLAYER_STATE1_2 (1 << 2) +#define PLAYER_STATE1_3 (1 << 3) +#define PLAYER_STATE1_4 (1 << 4) +#define PLAYER_STATE1_5 (1 << 5) +#define PLAYER_STATE1_6 (1 << 6) +#define PLAYER_STATE1_7 (1 << 7) +#define PLAYER_STATE1_8 (1 << 8) +#define PLAYER_STATE1_9 (1 << 9) +#define PLAYER_STATE1_10 (1 << 10) +#define PLAYER_STATE1_11 (1 << 11) +#define PLAYER_STATE1_12 (1 << 12) +#define PLAYER_STATE1_13 (1 << 13) +#define PLAYER_STATE1_14 (1 << 14) +#define PLAYER_STATE1_15 (1 << 15) +#define PLAYER_STATE1_16 (1 << 16) +#define PLAYER_STATE1_17 (1 << 17) +#define PLAYER_STATE1_18 (1 << 18) +#define PLAYER_STATE1_19 (1 << 19) +#define PLAYER_STATE1_20 (1 << 20) +#define PLAYER_STATE1_21 (1 << 21) +#define PLAYER_STATE1_22 (1 << 22) +#define PLAYER_STATE1_23 (1 << 23) +#define PLAYER_STATE1_24 (1 << 24) +#define PLAYER_STATE1_25 (1 << 25) +#define PLAYER_STATE1_26 (1 << 26) +#define PLAYER_STATE1_27 (1 << 27) +#define PLAYER_STATE1_28 (1 << 28) +#define PLAYER_STATE1_29 (1 << 29) +#define PLAYER_STATE1_30 (1 << 30) +#define PLAYER_STATE1_31 (1 << 31) + +#define PLAYER_STATE2_0 (1 << 0) +#define PLAYER_STATE2_1 (1 << 1) +#define PLAYER_STATE2_2 (1 << 2) +#define PLAYER_STATE2_3 (1 << 3) +#define PLAYER_STATE2_4 (1 << 4) +#define PLAYER_STATE2_5 (1 << 5) +#define PLAYER_STATE2_6 (1 << 6) +#define PLAYER_STATE2_7 (1 << 7) +#define PLAYER_STATE2_8 (1 << 8) +#define PLAYER_STATE2_9 (1 << 9) +#define PLAYER_STATE2_10 (1 << 10) +#define PLAYER_STATE2_11 (1 << 11) +#define PLAYER_STATE2_12 (1 << 12) +#define PLAYER_STATE2_13 (1 << 13) +#define PLAYER_STATE2_14 (1 << 14) +#define PLAYER_STATE2_15 (1 << 15) +#define PLAYER_STATE2_16 (1 << 16) +#define PLAYER_STATE2_17 (1 << 17) +#define PLAYER_STATE2_18 (1 << 18) +#define PLAYER_STATE2_19 (1 << 19) +#define PLAYER_STATE2_20 (1 << 20) +#define PLAYER_STATE2_21 (1 << 21) +#define PLAYER_STATE2_22 (1 << 22) +#define PLAYER_STATE2_23 (1 << 23) +#define PLAYER_STATE2_24 (1 << 24) +#define PLAYER_STATE2_25 (1 << 25) +#define PLAYER_STATE2_26 (1 << 26) +#define PLAYER_STATE2_27 (1 << 27) +#define PLAYER_STATE2_28 (1 << 28) +#define PLAYER_STATE2_29 (1 << 29) +#define PLAYER_STATE2_30 (1 << 30) +#define PLAYER_STATE2_31 (1 << 31) + +#define PLAYER_STATE3_0 (1 << 0) +#define PLAYER_STATE3_1 (1 << 1) +#define PLAYER_STATE3_2 (1 << 2) +#define PLAYER_STATE3_3 (1 << 3) +#define PLAYER_STATE3_4 (1 << 4) +#define PLAYER_STATE3_5 (1 << 5) +#define PLAYER_STATE3_6 (1 << 6) +#define PLAYER_STATE3_7 (1 << 7) + +typedef void (*PlayerFunc674)(struct Player*, struct GlobalContext*); +typedef s32(*PlayerFunc82C)(struct Player*, struct GlobalContext*); +typedef void (*PlayerFuncA74)(struct GlobalContext*, struct Player*); + +typedef struct Player { + /* 0x0000 */ Actor actor; + /* 0x014C */ s8 currentTunic; // current tunic from `PlayerTunic` + /* 0x014D */ s8 currentSwordItem; // current sword Item ID + /* 0x014E */ s8 currentShield; // current shield from `PlayerShield` + /* 0x014F */ s8 currentBoots; // current boots from `PlayerBoots` + /* 0x0150 */ s8 heldItemButton; // Button index for the item currently used + /* 0x0151 */ s8 heldItemActionParam; // Action param for the item currently used + /* 0x0152 */ u8 heldItemId; // Item id for the item currently used + /* 0x0153 */ s8 prevBoots; // previous boots from `PlayerBoots` + /* 0x0154 */ s8 itemActionParam; // the difference between this and heldItemActionParam is unclear + /* 0x0155 */ char unk_155[0x003]; + /* 0x0158 */ u8 modelGroup; + /* 0x0159 */ u8 nextModelGroup; + /* 0x015A */ s8 unk_15A; + /* 0x015B */ u8 modelAnimType; + /* 0x015C */ u8 leftHandType; + /* 0x015D */ u8 rightHandType; + /* 0x015E */ u8 sheathType; + /* 0x015F */ u8 currentMask; // current mask equipped from `PlayerMask` + /* 0x0160 */ Gfx** rightHandDLists; + /* 0x0164 */ Gfx** leftHandDLists; + /* 0x0168 */ Gfx** sheathDLists; + /* 0x016C */ Gfx** waistDLists; + /* 0x0170 */ u8 giObjectLoading; + /* 0x0174 */ DmaRequest giObjectDmaRequest; + /* 0x0194 */ OSMesgQueue giObjectLoadQueue; + /* 0x01AC */ OSMesg giObjectLoadMsg; + /* 0x01B0 */ void* giObjectSegment; // also used for title card textures + /* 0x01B4 */ SkelAnime skelAnime; + /* 0x01F8 */ Vec3s jointTable[PLAYER_LIMB_BUF_COUNT]; + /* 0x0288 */ Vec3s morphTable[PLAYER_LIMB_BUF_COUNT]; + /* 0x0318 */ Vec3s blendTable[PLAYER_LIMB_BUF_COUNT]; + /* 0x03A8 */ s16 unk_3A8[2]; + /* 0x03AC */ Actor* heldActor; + /* 0x03B0 */ Vec3f leftHandPos; + /* 0x03BC */ Vec3s unk_3BC; + /* 0x03C4 */ Actor* unk_3C4; + /* 0x03C8 */ Vec3f unk_3C8; + /* 0x03D4 */ char unk_3D4[0x058]; + /* 0x042C */ s8 doorType; + /* 0x042D */ s8 doorDirection; + /* 0x042E */ s16 doorTimer; + /* 0x0430 */ Actor* doorActor; + /* 0x0434 */ s8 getItemId; + /* 0x0436 */ u16 getItemDirection; + /* 0x0438 */ Actor* interactRangeActor; + /* 0x043C */ s8 mountSide; + /* 0x043D */ char unk_43D[0x003]; + /* 0x0440 */ Actor* rideActor; + /* 0x0444 */ u8 csMode; + /* 0x0445 */ u8 prevCsMode; + /* 0x0446 */ u8 unk_446; + /* 0x0447 */ u8 unk_447; + /* 0x0448 */ Actor* unk_448; + /* 0x044C */ char unk_44C[0x004]; + /* 0x0450 */ Vec3f unk_450; + /* 0x045C */ Vec3f unk_45C; + /* 0x0468 */ char unk_468[0x002]; + /* 0x046A */ s16 unk_46A; + /* 0x046C */ s16 unk_46C; + /* 0x046E */ char unk_46E[0x02A]; + /* 0x0498 */ ColliderCylinder cylinder; + /* 0x04E4 */ ColliderQuad swordQuads[2]; + /* 0x05E4 */ ColliderQuad shieldQuad; + /* 0x0664 */ Actor* unk_664; + /* 0x0668 */ char unk_668[0x004]; + /* 0x066C */ s32 unk_66C; + /* 0x0670 */ s32 swordEffectIndex; + /* 0x0674 */ PlayerFunc674 func_674; + /* 0x0678 */ PlayerAgeProperties* ageProperties; + /* 0x067C */ u32 stateFlags1; + /* 0x0680 */ u32 stateFlags2; + /* 0x0684 */ Actor* unk_684; + /* 0x0688 */ Actor* boomerangActor; + /* 0x068C */ Actor* naviActor; + /* 0x0690 */ s16 naviTextId; + /* 0x0692 */ u8 stateFlags3; + /* 0x0693 */ s8 exchangeItemId; + /* 0x0694 */ Actor* targetActor; + /* 0x0698 */ f32 targetActorDistance; + /* 0x069C */ char unk_69C[0x004]; + /* 0x06A0 */ f32 unk_6A0; + /* 0x06A4 */ f32 unk_6A4; + /* 0x06A8 */ Actor* unk_6A8; + /* 0x06AC */ s8 unk_6AC; + /* 0x06AD */ u8 unk_6AD; + /* 0x06AE */ u16 unk_6AE; + /* 0x06B0 */ s16 unk_6B0; + /* 0x06B2 */ char unk_6B4[0x004]; + /* 0x06B6 */ s16 unk_6B6; + /* 0x06B8 */ s16 unk_6B8; + /* 0x06BA */ s16 unk_6BA; + /* 0x06BC */ s16 unk_6BC; + /* 0x06BE */ s16 unk_6BE; + /* 0x06C0 */ s16 unk_6C0; + /* 0x06C2 */ s16 unk_6C2; + /* 0x06C4 */ f32 unk_6C4; + /* 0x06C8 */ SkelAnime skelAnime2; + /* 0x070C */ Vec3s jointTable2[PLAYER_LIMB_BUF_COUNT]; + /* 0x079C */ Vec3s morphTable2[PLAYER_LIMB_BUF_COUNT]; + /* 0x082C */ PlayerFunc82C func_82C; + /* 0x0830 */ f32 unk_830; + /* 0x0834 */ s16 unk_834; + /* 0x0836 */ s8 unk_836; + /* 0x0837 */ u8 unk_837; + /* 0x0838 */ f32 linearVelocity; + /* 0x083C */ s16 currentYaw; + /* 0x083E */ s16 targetYaw; + /* 0x0840 */ u16 unk_840; + /* 0x0842 */ s8 swordAnimation; + /* 0x0843 */ s8 swordState; + /* 0x0844 */ s8 unk_844; + /* 0x0845 */ u8 unk_845; + /* 0x0846 */ u8 unk_846; + /* 0x0847 */ s8 unk_847[4]; + /* 0x084B */ s8 unk_84B[4]; + /* 0x084F */ s8 unk_84F; + /* 0x0850 */ s16 unk_850; // multipurpose timer + /* 0x0854 */ f32 unk_854; + /* 0x0858 */ f32 unk_858; + /* 0x085C */ f32 unk_85C; // stick length among other things + /* 0x0860 */ s16 unk_860; // stick flame timer among other things + /* 0x0862 */ s8 unk_862; // get item draw ID + 1 + /* 0x0864 */ f32 unk_864; + /* 0x0868 */ f32 unk_868; + /* 0x086C */ f32 unk_86C; + /* 0x0870 */ f32 unk_870; + /* 0x0874 */ f32 unk_874; + /* 0x0878 */ f32 unk_878; + /* 0x087C */ s16 unk_87C; + /* 0x087E */ s16 unk_87E; + /* 0x0880 */ f32 unk_880; + /* 0x0884 */ f32 wallHeight; // height used to determine whether link can climb or grab a ledge at the top + /* 0x0888 */ f32 wallDistance; // distance to the colliding wall plane + /* 0x088C */ u8 unk_88C; + /* 0x088D */ u8 unk_88D; + /* 0x088E */ u8 unk_88E; + /* 0x088F */ u8 unk_88F; + /* 0x0890 */ u8 unk_890; + /* 0x0891 */ u8 shockTimer; + /* 0x0892 */ u8 unk_892; + /* 0x0893 */ u8 hoverBootsTimer; + /* 0x0894 */ s16 fallStartHeight; // last truncated Y position before falling + /* 0x0896 */ s16 fallDistance; // truncated Y distance the player has fallen so far (positive is down) + /* 0x0898 */ s16 unk_898; + /* 0x089A */ s16 unk_89A; + /* 0x089C */ s16 unk_89C; + /* 0x089E */ u16 unk_89E; + /* 0x08A0 */ u8 unk_8A0; + /* 0x08A1 */ u8 unk_8A1; + /* 0x08A2 */ s16 unk_8A2; + /* 0x08A4 */ f32 unk_8A4; + /* 0x08A8 */ f32 unk_8A8; + /* 0x08AC */ f32 windSpeed; + /* 0x08B0 */ s16 windDirection; + /* 0x08B4 */ WeaponInfo swordInfo[3]; + /* 0x0908 */ Vec3f bodyPartsPos[18]; + /* 0x09E0 */ MtxF mf_9E0; + /* 0x0A20 */ MtxF shieldMf; + /* 0x0A60 */ u8 isBurning; + /* 0x0A61 */ u8 flameTimers[18]; // one flame per body part + /* 0x0A73 */ u8 unk_A73; + /* 0x0A74 */ PlayerFuncA74 func_A74; + /* 0x0A78 */ s8 invincibilityTimer; // prevents damage when nonzero (positive = visible, counts towards zero each frame) + /* 0x0A79 */ u8 unk_A79; + /* 0x0A7A */ u8 unk_A7A; + /* 0x0A7B */ u8 unk_A7B; + /* 0x0A7C */ f32 unk_A7C; + /* 0x0A80 */ s16 unk_A80; + /* 0x0A82 */ u16 unk_A82; + /* 0x0A84 */ s16 unk_A84; + /* 0x0A86 */ s8 unk_A86; + /* 0x0A87 */ u8 unk_A87; + /* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position +} Player; // size = 0xA94 + +#endif \ No newline at end of file diff --git a/soh/include/z64save.h b/soh/include/z64save.h new file mode 100644 index 000000000..982da9d5c --- /dev/null +++ b/soh/include/z64save.h @@ -0,0 +1,215 @@ +#ifndef Z64SAVE_H +#define Z64SAVE_H + +#include "ultra64.h" +#include "z64math.h" + +typedef struct { + /* 0x00 */ u8 buttonItems[4]; + /* 0x04 */ u8 cButtonSlots[3]; + /* 0x08 */ u16 equipment; +} ItemEquips; // size = 0x0A + +typedef struct { + /* 0x00 */ u8 items[24]; + /* 0x18 */ s8 ammo[16]; + /* 0x28 */ u16 equipment; + /* 0x2C */ u32 upgrades; + /* 0x30 */ u32 questItems; + /* 0x34 */ u8 dungeonItems[20]; + /* 0x48 */ s8 dungeonKeys[19]; + /* 0x5B */ s8 defenseHearts; + /* 0x5C */ s16 gsTokens; +} Inventory; // size = 0x5E + +typedef struct { + /* 0x00 */ u32 chest; + /* 0x04 */ u32 swch; + /* 0x08 */ u32 clear; + /* 0x0C */ u32 collect; + /* 0x10 */ u32 unk; + /* 0x14 */ u32 rooms; + /* 0x18 */ u32 floors; +} SavedSceneFlags; // size = 0x1C + +typedef struct { + /* 0x00 */ s16 scene; + /* 0x02 */ Vec3s pos; + /* 0x08 */ s16 angle; +} HorseData; // size = 0x0A + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 yaw; + /* 0x0E */ s16 playerParams; + /* 0x10 */ s16 entranceIndex; + /* 0x12 */ u8 roomIndex; + /* 0x13 */ s8 data; + /* 0x14 */ u32 tempSwchFlags; + /* 0x18 */ u32 tempCollectFlags; +} RespawnData; // size = 0x1C + +typedef struct { + /* 0x00 */ Vec3i pos; + /* 0x0C */ s32 yaw; + /* 0x10 */ s32 playerParams; + /* 0x14 */ s32 entranceIndex; + /* 0x18 */ s32 roomIndex; + /* 0x1C */ s32 set; + /* 0x20 */ s32 tempSwchFlags; + /* 0x24 */ s32 tempCollectFlags; +} FaroresWindData; // size = 0x28 + +typedef struct { + /* 0x0000 */ s32 entranceIndex; // start of `save` substruct, originally called "memory" + /* 0x0004 */ s32 linkAge; // 0: Adult; 1: Child + /* 0x0008 */ s32 cutsceneIndex; + /* 0x000C */ u16 dayTime; // "zelda_time" + /* 0x0010 */ s32 nightFlag; + /* 0x0014 */ s32 totalDays; + /* 0x0018 */ s32 bgsDayCount; // increments with totalDays, can be cleared with `Environment_ClearBgsDayCount` + /* 0x001C */ char newf[6]; // string "ZELDAZ". start of `info` substruct, originally called "information" + /* 0x0022 */ u16 deaths; + /* 0x0024 */ char playerName[8]; + /* 0x002C */ s16 n64ddFlag; + /* 0x002E */ s16 healthCapacity; // "max_life" + /* 0x0030 */ s16 health; // "now_life" + /* 0x0032 */ s8 magicLevel; + /* 0x0033 */ s8 magic; + /* 0x0034 */ s16 rupees; + /* 0x0036 */ u16 swordHealth; + /* 0x0038 */ u16 naviTimer; + /* 0x003A */ u8 magicAcquired; + /* 0x003B */ char unk_3B[0x01]; + /* 0x003C */ u8 doubleMagic; + /* 0x003D */ u8 doubleDefense; + /* 0x003E */ u8 bgsFlag; + /* 0x003F */ u8 ocarinaGameRoundNum; + /* 0x0040 */ ItemEquips childEquips; + /* 0x004A */ ItemEquips adultEquips; + /* 0x0054 */ u32 unk_54; // this may be incorrect, currently used for alignement + /* 0x0058 */ char unk_58[0x0E]; + /* 0x0066 */ s16 savedSceneNum; + /* 0x0068 */ ItemEquips equips; + /* 0x0074 */ Inventory inventory; + /* 0x00D4 */ SavedSceneFlags sceneFlags[124]; + /* 0x0E64 */ FaroresWindData fw; + /* 0x0E8C */ char unk_E8C[0x10]; + /* 0x0E9C */ s32 gsFlags[6]; + /* 0x0EB4 */ char unk_EB4[0x4]; + /* 0x0EB8 */ s32 highScores[7]; + /* 0x0ED4 */ u16 eventChkInf[14]; // "event_chk_inf" + /* 0x0EF0 */ u16 itemGetInf[4]; // "item_get_inf" + /* 0x0EF8 */ u16 infTable[30]; // "inf_table" + /* 0x0F34 */ char unk_F34[0x04]; + /* 0x0F38 */ u32 worldMapAreaData; // "area_arrival" + /* 0x0F3C */ char unk_F3C[0x4]; + /* 0x0F40 */ u8 scarecrowCustomSongSet; + /* 0x0F41 */ u8 scarecrowCustomSong[0x360]; + /* 0x12A1 */ char unk_12A1[0x24]; + /* 0x12C5 */ u8 scarecrowSpawnSongSet; + /* 0x12C6 */ u8 scarecrowSpawnSong[0x80]; + /* 0x1346 */ char unk_1346[0x02]; + /* 0x1348 */ HorseData horseData; + /* 0x1352 */ u16 checksum; // "check_sum" + /* 0x1354 */ s32 fileNum; // "file_no" + /* 0x1358 */ char unk_1358[0x0004]; + /* 0x135C */ s32 gameMode; + /* 0x1360 */ s32 sceneSetupIndex; + /* 0x1364 */ s32 respawnFlag; // "restart_flag" + /* 0x1368 */ RespawnData respawn[3]; // "restart_data" + /* 0x13BC */ f32 entranceSpeed; + /* 0x13C0 */ u16 entranceSound; + /* 0x13C2 */ char unk_13C2[0x0001]; + /* 0x13C3 */ u8 unk_13C3; + /* 0x13C4 */ s16 dogParams; + /* 0x13C6 */ u8 textTriggerFlags; + /* 0x13C7 */ u8 showTitleCard; + /* 0x13C8 */ s16 nayrusLoveTimer; + /* 0x13CA */ char unk_13CA[0x0002]; + /* 0x13CC */ s16 rupeeAccumulator; + /* 0x13CE */ s16 timer1State; + /* 0x13D0 */ s16 timer1Value; + /* 0x13D2 */ s16 timer2State; + /* 0x13D4 */ s16 timer2Value; + /* 0x13D6 */ s16 timerX[2]; + /* 0x13DA */ s16 timerY[2]; + /* 0x13DE */ char unk_13DE[0x0002]; + /* 0x13E0 */ u8 seqId; + /* 0x13E1 */ u8 natureAmbienceId; + /* 0x13E2 */ u8 buttonStatus[5]; + /* 0x13E7 */ u8 unk_13E7; // alpha related + /* 0x13E8 */ u16 unk_13E8; // alpha type? + /* 0x13EA */ u16 unk_13EA; // also alpha type? + /* 0x13EC */ u16 unk_13EC; // alpha type counter? + /* 0x13EE */ u16 unk_13EE; // previous alpha type? + /* 0x13F0 */ s16 unk_13F0; // magic related + /* 0x13F2 */ s16 unk_13F2; // magic related + /* 0x13F4 */ s16 unk_13F4; // magic related + /* 0x13F6 */ s16 unk_13F6; // magic related + /* 0x13F8 */ s16 unk_13F8; // magic related + /* 0x13FA */ u16 eventInf[4]; // "event_inf" + /* 0x1402 */ u16 mapIndex; // intended for maps/minimaps but commonly used as the dungeon index + /* 0x1404 */ u16 minigameState; + /* 0x1406 */ u16 minigameScore; // "yabusame_total" + /* 0x1408 */ char unk_1408[0x0001]; + /* 0x1409 */ u8 language; // NTSC 0: Japanese; 1: English | PAL 0: English; 1: German; 2: French + /* 0x140A */ u8 audioSetting; + /* 0x140B */ char unk_140B[0x0001]; + /* 0x140C */ u8 zTargetSetting; // 0: Switch; 1: Hold + /* 0x140E */ u16 forcedSeqId; // immediately start playing the sequence if set + /* 0x1410 */ u8 unk_1410; // transition related + /* 0x1411 */ char unk_1411[0x0001]; + /* 0x1412 */ u16 nextCutsceneIndex; + /* 0x1414 */ u8 cutsceneTrigger; + /* 0x1415 */ u8 chamberCutsceneNum; + /* 0x1416 */ u16 nextDayTime; // "next_zelda_time" + /* 0x1418 */ u8 fadeDuration; + /* 0x1419 */ u8 unk_1419; // transition related + /* 0x141A */ u16 skyboxTime; + /* 0x141C */ u8 dogIsLost; + /* 0x141D */ u8 nextTransition; + /* 0x141E */ char unk_141E[0x0002]; + /* 0x1420 */ s16 worldMapArea; + /* 0x1422 */ s16 sunsSongState; // controls the effects of suns song + /* 0x1424 */ s16 healthAccumulator; +} SaveContext; // size = 0x1428 + +typedef enum { + /* 0x00 */ RESPAWN_MODE_DOWN, /* Normal Void Outs */ + /* 0x01 */ RESPAWN_MODE_RETURN, /* Grotto Returnpoints */ + /* 0x02 */ RESPAWN_MODE_TOP /* Farore's Wind */ +} RespawnMode; + +typedef enum { + /* 0x00 */ BTN_ENABLED, + /* 0xFF */ BTN_DISABLED = 0xFF +} ButtonStatus; + +typedef enum { + /* 0x00 */ CHAMBER_CS_FOREST, + /* 0x01 */ CHAMBER_CS_FIRE, + /* 0x02 */ CHAMBER_CS_WATER, + /* 0x03 */ CHAMBER_CS_SPIRIT, + /* 0x04 */ CHAMBER_CS_SHADOW, + /* 0x05 */ CHAMBER_CS_LIGHT +} ChamberCutsceneNum; + +typedef enum { + /* 0x00 */ HS_HBA, // horseback archery + /* 0x01 */ HS_POE_POINTS, + /* 0x02 */ HS_FISHING, + /* 0x03 */ HS_HORSE_RACE, + /* 0x04 */ HS_MARATHON, + /* 0x05 */ HS_UNK_05, + /* 0x06 */ HS_DAMPE_RACE +} HighScores; + +typedef enum { + /* 0 */ SUNSSONG_INACTIVE, + /* 1 */ SUNSSONG_START, // the suns ocarina effect signals that the song has finished playing + /* 2 */ SUNSSONG_SPEED_TIME, // suns was played where time passes, speed up the advancement of time + /* 3 */ SUNSSONG_SPECIAL // time does not advance, but signals the song was played. used for freezing redeads +} SunsSongState; + +#endif diff --git a/soh/include/z64scene.h b/soh/include/z64scene.h new file mode 100644 index 000000000..400ecfee8 --- /dev/null +++ b/soh/include/z64scene.h @@ -0,0 +1,532 @@ +#ifndef Z64SCENE_H +#define Z64SCENE_H + +#include "command_macros_base.h" + +typedef struct { + /* 0x00 */ uintptr_t vromStart; + /* 0x04 */ uintptr_t vromEnd; + char* fileName; +} RomFile; // size = 0x8 + +typedef struct { + /* 0x00 */ RomFile sceneFile; + /* 0x08 */ RomFile titleFile; + /* 0x10 */ u8 unk_10; + /* 0x11 */ u8 config; + /* 0x12 */ u8 unk_12; + /* 0x13 */ u8 unk_13; +} SceneTableEntry; // size = 0x14 + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ u32 data2; +} SCmdBase; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdSpawnList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 num; + /* 0x04 */ void* segment; +} SCmdActorList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdUnused02; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdColHeader; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 num; + /* 0x04 */ void* segment; +} SCmdRoomList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x02 */ char pad[2]; + /* 0x04 */ u8 x; + /* 0x05 */ u8 y; + /* 0x06 */ u8 z; + /* 0x07 */ u8 unk_07; +} SCmdWindSettings; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdEntranceList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 cUpElfMsgNum; + /* 0x04 */ u32 keepObjectId; +} SCmdSpecialFiles; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 gpFlag1; + /* 0x04 */ u32 gpFlag2; +} SCmdRoomBehavior; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdMesh; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 num; + /* 0x04 */ void* segment; +} SCmdObjectList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 num; + /* 0x04 */ void* segment; +} SCmdLightList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdPathList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 num; + /* 0x04 */ void* segment; +} SCmdTransiActorList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 num; + /* 0x04 */ void* segment; +} SCmdLightSettingList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x02 */ char pad[2]; + /* 0x04 */ u8 hour; + /* 0x05 */ u8 min; + /* 0x06 */ u8 unk_06; +} SCmdTimeSettings; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x02 */ char pad[2]; + /* 0x04 */ u8 skyboxId; + /* 0x05 */ u8 unk_05; + /* 0x06 */ u8 unk_06; +} SCmdSkyboxSettings; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x02 */ char pad[2]; + /* 0x04 */ u8 unk_04; + /* 0x05 */ u8 unk_05; +} SCmdSkyboxDisables; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ u32 data2; +} SCmdEndMarker; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdExitList; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 specId; + /* 0x02 */ char pad[4]; + /* 0x06 */ u8 natureAmbienceId; + /* 0x07 */ u8 seqId; +} SCmdSoundSettings; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x02 */ char pad[5]; + /* 0x07 */ u8 echo; +} SCmdEchoSettings; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdCutsceneData; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 data1; + /* 0x04 */ void* segment; +} SCmdAltHeaders; + +typedef struct { + /* 0x00 */ u8 code; + /* 0x01 */ u8 cameraMovement; + /* 0x04 */ u32 area; +} SCmdMiscSettings; + +typedef struct { + u8 headerType; +} MeshHeaderBase; + +typedef struct { + MeshHeaderBase base; + u8 numEntries; + Gfx* dListStart; + Gfx* dListEnd; +} MeshHeader0; + +typedef struct { + Gfx* opaqueDList; + Gfx* translucentDList; +} MeshEntry0; + +typedef struct { + MeshHeaderBase base; + u8 format; + u32 entryRecord; +} MeshHeader1Base; + +typedef struct { + MeshHeader1Base base; + void* imagePtr; // 0x08 + u32 unknown; // 0x0C + u32 unknown2; // 0x10 + u16 bgWidth; // 0x14 + u16 bgHeight; // 0x16 + u8 imageFormat; // 0x18 + u8 imageSize; // 0x19 + u16 imagePal; // 0x1A + u16 imageFlip; // 0x1C +} MeshHeader1Single; + +typedef struct { + MeshHeader1Base base; + u8 bgCnt; + void* bgRecordPtr; +} MeshHeader1Multi; + +typedef struct { + u16 unknown; // 0x00 + s8 bgID; // 0x02 + void* imagePtr; // 0x04 + u32 unknown2; // 0x08 + u32 unknown3; // 0x0C + u16 bgWidth; // 0x10 + u16 bgHeight; // 0x12 + u8 imageFmt; // 0x14 + u8 imageSize; // 0x15 + u16 imagePal; // 0x16 + u16 imageFlip; // 0x18 +} BackgroundRecord; + +typedef struct { + s16 playerXMax, playerZMax; + s16 playerXMin, playerZMin; + Gfx* opaqueDList; + Gfx* translucentDList; +} MeshEntry2; + +typedef struct { + MeshHeaderBase base; + u8 numEntries; + Gfx* dListStart; + Gfx* dListEnd; +} MeshHeader2; + +typedef struct { + /* 0x00 */ u8 ambientColor[3]; + /* 0x03 */ s8 diffuseDir1[3]; + /* 0x06 */ u8 diffuseColor1[3]; + /* 0x09 */ s8 diffuseDir2[3]; + /* 0x0C */ u8 diffuseColor2[3]; + /* 0x0F */ u8 fogColor[3]; + /* 0x12 */ u16 fogNear; + /* 0x14 */ u16 fogFar; +} LightSettings; // size = 0x16 + +typedef struct { + /* 0x00 */ u8 count; // number of points in the path + /* 0x04 */ Vec3s* points; // Segment Address to the array of points +} Path; // size = 0x8 + +typedef union { + SCmdBase base; + SCmdSpawnList spawnList; + SCmdActorList actorList; + SCmdUnused02 unused02; + SCmdRoomList roomList; + SCmdEntranceList entranceList; + SCmdObjectList objectList; + SCmdLightList lightList; + SCmdPathList pathList; + SCmdTransiActorList transiActorList; + SCmdLightSettingList lightSettingList; + SCmdExitList exitList; + SCmdColHeader colHeader; + SCmdMesh mesh; + SCmdSpecialFiles specialFiles; + SCmdCutsceneData cutsceneData; + SCmdRoomBehavior roomBehavior; + SCmdWindSettings windSettings; + SCmdTimeSettings timeSettings; + SCmdSkyboxSettings skyboxSettings; + SCmdSkyboxDisables skyboxDisables; + SCmdEndMarker endMarker; + SCmdSoundSettings soundSettings; + SCmdEchoSettings echoSettings; + SCmdMiscSettings miscSettings; + SCmdAltHeaders altHeaders; +} SceneCmd; // size = 0x8 + +typedef enum { + /* 0x00 */ SCENE_YDAN, + /* 0x01 */ SCENE_DDAN, + /* 0x02 */ SCENE_BDAN, + /* 0x03 */ SCENE_BMORI1, + /* 0x04 */ SCENE_HIDAN, + /* 0x05 */ SCENE_MIZUSIN, + /* 0x06 */ SCENE_JYASINZOU, + /* 0x07 */ SCENE_HAKADAN, + /* 0x08 */ SCENE_HAKADANCH, + /* 0x09 */ SCENE_ICE_DOUKUTO, + /* 0x0A */ SCENE_GANON, + /* 0x0B */ SCENE_MEN, + /* 0x0C */ SCENE_GERUDOWAY, + /* 0x0D */ SCENE_GANONTIKA, + /* 0x0E */ SCENE_GANON_SONOGO, + /* 0x0F */ SCENE_GANONTIKA_SONOGO, + /* 0x10 */ SCENE_TAKARAYA, + /* 0x11 */ SCENE_YDAN_BOSS, + /* 0x12 */ SCENE_DDAN_BOSS, + /* 0x13 */ SCENE_BDAN_BOSS, + /* 0x14 */ SCENE_MORIBOSSROOM, + /* 0x15 */ SCENE_FIRE_BS, + /* 0x16 */ SCENE_MIZUSIN_BS, + /* 0x17 */ SCENE_JYASINBOSS, + /* 0x18 */ SCENE_HAKADAN_BS, + /* 0x19 */ SCENE_GANON_BOSS, + /* 0x1A */ SCENE_GANON_FINAL, + /* 0x1B */ SCENE_ENTRA, + /* 0x1C */ SCENE_ENTRA_N, + /* 0x1D */ SCENE_ENRUI, + /* 0x1E */ SCENE_MARKET_ALLEY, + /* 0x1F */ SCENE_MARKET_ALLEY_N, + /* 0x20 */ SCENE_MARKET_DAY, + /* 0x21 */ SCENE_MARKET_NIGHT, + /* 0x22 */ SCENE_MARKET_RUINS, + /* 0x23 */ SCENE_SHRINE, + /* 0x24 */ SCENE_SHRINE_N, + /* 0x25 */ SCENE_SHRINE_R, + /* 0x26 */ SCENE_KOKIRI_HOME, + /* 0x27 */ SCENE_KOKIRI_HOME3, + /* 0x28 */ SCENE_KOKIRI_HOME4, + /* 0x29 */ SCENE_KOKIRI_HOME5, + /* 0x2A */ SCENE_KAKARIKO, + /* 0x2B */ SCENE_KAKARIKO3, + /* 0x2C */ SCENE_SHOP1, + /* 0x2D */ SCENE_KOKIRI_SHOP, + /* 0x2E */ SCENE_GOLON, + /* 0x2F */ SCENE_ZOORA, + /* 0x30 */ SCENE_DRAG, + /* 0x31 */ SCENE_ALLEY_SHOP, + /* 0x32 */ SCENE_NIGHT_SHOP, + /* 0x33 */ SCENE_FACE_SHOP, + /* 0x34 */ SCENE_LINK_HOME, + /* 0x35 */ SCENE_IMPA, + /* 0x36 */ SCENE_MALON_STABLE, + /* 0x37 */ SCENE_LABO, + /* 0x38 */ SCENE_HYLIA_LABO, + /* 0x39 */ SCENE_TENT, + /* 0x3A */ SCENE_HUT, + /* 0x3B */ SCENE_DAIYOUSEI_IZUMI, + /* 0x3C */ SCENE_YOUSEI_IZUMI_TATE, + /* 0x3D */ SCENE_YOUSEI_IZUMI_YOKO, + /* 0x3E */ SCENE_KAKUSIANA, + /* 0x3F */ SCENE_HAKAANA, + /* 0x40 */ SCENE_HAKAANA2, + /* 0x41 */ SCENE_HAKAANA_OUKE, + /* 0x42 */ SCENE_SYATEKIJYOU, + /* 0x43 */ SCENE_TOKINOMA, + /* 0x44 */ SCENE_KENJYANOMA, + /* 0x45 */ SCENE_HAIRAL_NIWA, + /* 0x46 */ SCENE_HAIRAL_NIWA_N, + /* 0x47 */ SCENE_HIRAL_DEMO, + /* 0x48 */ SCENE_HAKASITARELAY, + /* 0x49 */ SCENE_TURIBORI, + /* 0x4A */ SCENE_NAKANIWA, + /* 0x4B */ SCENE_BOWLING, + /* 0x4C */ SCENE_SOUKO, + /* 0x4D */ SCENE_MIHARIGOYA, + /* 0x4E */ SCENE_MAHOUYA, + /* 0x4F */ SCENE_GANON_DEMO, + /* 0x50 */ SCENE_KINSUTA, + /* 0x51 */ SCENE_SPOT00, + /* 0x52 */ SCENE_SPOT01, + /* 0x53 */ SCENE_SPOT02, + /* 0x54 */ SCENE_SPOT03, + /* 0x55 */ SCENE_SPOT04, + /* 0x56 */ SCENE_SPOT05, + /* 0x57 */ SCENE_SPOT06, + /* 0x58 */ SCENE_SPOT07, + /* 0x59 */ SCENE_SPOT08, + /* 0x5A */ SCENE_SPOT09, + /* 0x5B */ SCENE_SPOT10, + /* 0x5C */ SCENE_SPOT11, + /* 0x5D */ SCENE_SPOT12, + /* 0x5E */ SCENE_SPOT13, + /* 0x5F */ SCENE_SPOT15, + /* 0x60 */ SCENE_SPOT16, + /* 0x61 */ SCENE_SPOT17, + /* 0x62 */ SCENE_SPOT18, + /* 0x63 */ SCENE_SPOT20, + /* 0x64 */ SCENE_GANON_TOU, + // Debug only scenes + /* 0x65 */ SCENE_TEST01, + /* 0x66 */ SCENE_BESITU, + /* 0x67 */ SCENE_DEPTH_TEST, + /* 0x68 */ SCENE_SYOTES, + /* 0x69 */ SCENE_SYOTES2, + /* 0x6A */ SCENE_SUTARU, + /* 0x6B */ SCENE_HAIRAL_NIWA2, + /* 0x6C */ SCENE_SASATEST, + /* 0x6D */ SCENE_TESTROOM, + /* 0x6E */ SCENE_ID_MAX +} SceneID; + +// Scene commands + +typedef enum { + /* 0x00 */ SCENE_CMD_ID_SPAWN_LIST, + /* 0x01 */ SCENE_CMD_ID_ACTOR_LIST, + /* 0x02 */ SCENE_CMD_ID_UNUSED_02, + /* 0x03 */ SCENE_CMD_ID_COL_HEADER, + /* 0x04 */ SCENE_CMD_ID_ROOM_LIST, + /* 0x05 */ SCENE_CMD_ID_WIND_SETTINGS, + /* 0x06 */ SCENE_CMD_ID_ENTRANCE_LIST, + /* 0x07 */ SCENE_CMD_ID_SPECIAL_FILES, + /* 0x08 */ SCENE_CMD_ID_ROOM_BEHAVIOR, + /* 0x09 */ SCENE_CMD_ID_UNK_09, + /* 0x0A */ SCENE_CMD_ID_MESH, + /* 0x0B */ SCENE_CMD_ID_OBJECT_LIST, + /* 0x0C */ SCENE_CMD_ID_LIGHT_LIST, + /* 0x0D */ SCENE_CMD_ID_PATH_LIST, + /* 0x0E */ SCENE_CMD_ID_TRANSI_ACTOR_LIST, + /* 0x0F */ SCENE_CMD_ID_ENV_LIGHT_SETTINGS, + /* 0x10 */ SCENE_CMD_ID_TIME_SETTINGS, + /* 0x11 */ SCENE_CMD_ID_SKYBOX_SETTINGS, + /* 0x12 */ SCENE_CMD_ID_SKYBOX_DISABLES, + /* 0x13 */ SCENE_CMD_ID_EXIT_LIST, + /* 0x14 */ SCENE_CMD_ID_END, + /* 0x15 */ SCENE_CMD_ID_SOUND_SETTINGS, + /* 0x16 */ SCENE_CMD_ID_ECHO_SETTINGS, + /* 0x17 */ SCENE_CMD_ID_CUTSCENE_DATA, + /* 0x18 */ SCENE_CMD_ID_ALTERNATE_HEADER_LIST, + /* 0x19 */ SCENE_CMD_ID_MISC_SETTINGS +} SceneCommandTypeID; + +#define SCENE_CMD_SPAWN_LIST(numSpawns, spawnList) \ + { SCENE_CMD_ID_SPAWN_LIST, numSpawns, CMD_PTR(spawnList) } + +#define SCENE_CMD_ACTOR_LIST(numActors, actorList) \ + { SCENE_CMD_ID_ACTOR_LIST, numActors, CMD_PTR(actorList) } + +#define SCENE_CMD_UNUSED_02(unk, data) \ + { SCENE_CMD_ID_UNUSED_02, unk, CMD_PTR(data) } + +#define SCENE_CMD_COL_HEADER(colHeader) \ + { SCENE_CMD_ID_COL_HEADER, 0, CMD_PTR(colHeader) } + +#define SCENE_CMD_ROOM_LIST(numRooms, roomList) \ + { SCENE_CMD_ID_ROOM_LIST, numRooms, CMD_PTR(roomList) } + +#define SCENE_CMD_WIND_SETTINGS(xDir, yDir, zDir, strength) \ + { SCENE_CMD_ID_WIND_SETTINGS, 0, CMD_BBBB(xDir, yDir, zDir, strength) } + +#define SCENE_CMD_ENTRANCE_LIST(entranceList) \ + { SCENE_CMD_ID_ENTRANCE_LIST, 0, CMD_PTR(entranceList) } + +#define SCENE_CMD_SPECIAL_FILES(elfMessageFile, keepObjectId) \ + { SCENE_CMD_ID_SPECIAL_FILES, elfMessageFile, CMD_W(keepObjectId) } + +#define SCENE_CMD_ROOM_BEHAVIOR(curRoomUnk3, curRoomUnk2, showInvisActors, disableWarpSongs) \ + { SCENE_CMD_ID_ROOM_BEHAVIOR, curRoomUnk3, \ + curRoomUnk2 | _SHIFTL(showInvisActors, 8, 1) | _SHIFTL(disableWarpSongs, 10, 1) } + +#define SCENE_CMD_UNK_09() \ + { SCENE_CMD_ID_UNK_09, 0, CMD_W(0) } + +#define SCENE_CMD_MESH(meshHeader) \ + { SCENE_CMD_ID_MESH, 0, CMD_PTR(meshHeader) } + +#define SCENE_CMD_OBJECT_LIST(numObjects, objectList) \ + { SCENE_CMD_ID_OBJECT_LIST, numObjects, CMD_PTR(objectList) } + +#define SCENE_CMD_LIGHT_LIST(numLights, lightList) \ + { SCENE_CMD_ID_POS_LIGHT_LIST, numLights, CMD_PTR(lightList) } + +#define SCENE_CMD_PATH_LIST(pathList) \ + { SCENE_CMD_ID_PATH_LIST, 0, CMD_PTR(pathList) } + +#define SCENE_CMD_TRANSITION_ACTOR_LIST(numActors, list) \ + { SCENE_CMD_ID_TRANSI_ACTOR_LIST, numActors, CMD_PTR(list) } + +#define SCENE_CMD_ENV_LIGHT_SETTINGS(numLightSettings, lightSettingsList) \ + { SCENE_CMD_ID_ENV_LIGHT_SETTINGS, numLightSettings, CMD_PTR(lightSettingsList) } + +#define SCENE_CMD_TIME_SETTINGS(hour, min, speed) \ + { SCENE_CMD_ID_TIME_SETTINGS, 0, CMD_BBBB(hour, min, speed, 0) } + +#define SCENE_CMD_SKYBOX_SETTINGS(skyboxId, weather, isIndoors) \ + { SCENE_CMD_ID_SKYBOX_SETTINGS, 0, CMD_BBBB(skyboxId, weather, isIndoors, 0) } + +#define SCENE_CMD_SKYBOX_DISABLES(disableSky, disableSunMoon) \ + { SCENE_CMD_ID_SKYBOX_DISABLES, 0, CMD_BBBB(disableSky, disableSunMoon, 0, 0) } + +#define SCENE_CMD_EXIT_LIST(exitList) \ + { SCENE_CMD_ID_EXIT_LIST, 0, CMD_PTR(exitList) } + +#define SCENE_CMD_END() \ + { SCENE_CMD_ID_END, 0, CMD_W(0) } + +#define SCENE_CMD_SOUND_SETTINGS(specId, natureAmbienceId, seqId) \ + { SCENE_CMD_ID_SOUND_SETTINGS, specId, CMD_BBBB(0, 0, natureAmbienceId, seqId) } + +#define SCENE_CMD_ECHO_SETTINGS(echo) \ + { SCENE_CMD_ID_ECHO_SETTINGS, 0, CMD_BBBB(0, 0, 0, echo) } + +#define SCENE_CMD_CUTSCENE_DATA(cutsceneData) \ + { SCENE_CMD_ID_CUTSCENE_DATA, 0, CMD_PTR(cutsceneData) } + +#define SCENE_CMD_ALTERNATE_HEADER_LIST(alternateHeaderList) \ + { SCENE_CMD_ID_ALTERNATE_HEADER_LIST, 0, CMD_PTR(alternateHeaderList) } + +#define SCENE_CMD_MISC_SETTINGS(camMode, worldMapLocation) \ + { SCENE_CMD_ID_MISC_SETTINGS, camMode, CMD_W(worldMapLocation) } + + +#endif diff --git a/soh/include/z64skin.h b/soh/include/z64skin.h new file mode 100644 index 000000000..3d75f4835 --- /dev/null +++ b/soh/include/z64skin.h @@ -0,0 +1,94 @@ +#ifndef Z64_SKIN_H +#define Z64_SKIN_H + +#include "z64animation.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Holds a compact version of a vertex used in the Skin system + * It is used to initialise the Vtx used by an animated limb + */ +typedef struct { + /* 0x00 */ u16 index; + /* 0x02 */ s16 s; // s and t are texture coordinates (also known as u and v) + /* 0x04 */ s16 t; + /* 0x06 */ s8 normX; + /* 0x07 */ s8 normY; + /* 0x08 */ s8 normZ; + /* 0x09 */ u8 alpha; +} SkinVertex; // size = 0xA + +/** + * Describes a position displacement and a scale to be applied to a limb at index `limbIndex` + */ +typedef struct { + /* 0x00 */ u8 limbIndex; + /* 0x02 */ s16 x; + /* 0x04 */ s16 y; + /* 0x06 */ s16 z; + /* 0x08 */ u8 scale; +} SkinTransformation; // size = 0xA + +typedef struct { + /* 0x00 */ u16 vtxCount; // number of vertices in this modif entry + /* 0x02 */ u16 transformCount; + /* 0x04 */ u16 unk_4; // index of limbTransformations? + /* 0x08 */ SkinVertex* skinVertices; + /* 0x0C */ SkinTransformation* limbTransformations; +} SkinLimbModif; // size = 0x10 + +typedef struct { + /* 0x00 */ u16 totalVtxCount; // total vertex count for all modif entries + /* 0x02 */ u16 limbModifCount; + /* 0x04 */ SkinLimbModif* limbModifications; + /* 0x08 */ Gfx* dlist; +} SkinAnimatedLimbData; // size = 0xC + +// ZAPD compatibility typedefs +// TODO: Remove when ZAPD adds support for them +typedef SkinVertex Struct_800A57C0; +typedef SkinTransformation Struct_800A598C_2; +typedef SkinAnimatedLimbData Struct_800A5E28; +typedef SkinLimbModif Struct_800A598C; + +#define SKIN_LIMB_TYPE_ANIMATED 4 +#define SKIN_LIMB_TYPE_NORMAL 11 + +typedef struct { + /* 0x00 */ Vec3s jointPos; // Root is position in model space, children are relative to parent + /* 0x06 */ u8 child; + /* 0x07 */ u8 sibling; + /* 0x08 */ s32 segmentType; // Type of data contained in segment + /* 0x0C */ void* segment; // Gfx* if segmentType is SKIN_LIMB_TYPE_NORMAL, SkinAnimatedLimbData* if segmentType is SKIN_LIMB_TYPE_ANIMATED, NULL otherwise +} SkinLimb; // size = 0x10 + +typedef struct { + /* 0x000 */ u8 index; // alternates every draw cycle + /* 0x004 */ Vtx* buf[2]; // number of vertices in buffer determined by `totalVtxCount` +} SkinLimbVtx; // size = 0xC + +typedef struct { + /* 0x000 */ SkeletonHeader* skeletonHeader; + /* 0x004 */ MtxF mtx; + /* 0x044 */ s32 limbCount; + /* 0x048 */ SkinLimbVtx* vtxTable; // double buffered list of vertices for each limb + /* 0x04C */ SkelAnime skelAnime; +} Skin; // size = 0x90 + +typedef void (*SkinPostDraw)(struct Actor*, struct GlobalContext*, Skin*); +typedef s32 (*SkinOverrideLimbDraw)(struct Actor*, struct GlobalContext*, s32, Skin*); + +#define SKIN_DRAW_FLAG_CUSTOM_TRANSFORMS (1 << 0) +#define SKIN_DRAW_FLAG_CUSTOM_MATRIX (1 << 1) + +#define SKIN_TRANSFORM_IS_FHG 0x23 + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/soh/include/z64transition.h b/soh/include/z64transition.h new file mode 100644 index 000000000..2adb6f604 --- /dev/null +++ b/soh/include/z64transition.h @@ -0,0 +1,78 @@ +#ifndef Z64TRANSITION_H +#define Z64TRANSITION_H + +#include "ultra64.h" +#include "color.h" + +typedef struct { + f32 unk_0; + f32 unk_4; +} TransitionUnkData; + +typedef struct { + /* 0x00 */ s32 row; + /* 0x04 */ s32 col; + /* 0x08 */ s32 frame; + /* 0x0C */ TransitionUnkData* unk_0C; + /* 0x10 */ Vtx* vtxFrame1; + /* 0x14 */ Vtx* vtxFrame2; + /* 0x18 */ Mtx projection; + /* 0x58 */ Mtx modelView; + /* 0x98 */ Mtx unk_98; + /* 0xD8 */ Gfx* gfx; // "gfxtbl" + /* 0xDC */ u16* zBuffer; +} TransitionUnk; // size = 0xE0 + +typedef struct { + /* 0x000 */ Color_RGBA8_u32 color; + /* 0x004 */ Color_RGBA8_u32 envColor; + /* 0x008 */ u8 direction; + /* 0x009 */ u8 frame; + /* 0x00A */ u8 isDone; + /* 0x00C */ u16 texX; + /* 0x00E */ u16 texY; + /* 0x010 */ u16 normal; + /* 0x018 */ Mtx projection; + /* 0x058 */ Mtx lookAt; + /* 0x098 */ Mtx modelView[2][3]; +} TransitionWipe; // size = 0x218 + +typedef struct { + /* 0x000 */ u8 fadeType; + /* 0x001 */ u8 isDone; + /* 0x002 */ u8 fadeDirection; + /* 0x004 */ Color_RGBA8_u32 fadeColor; + /* 0x008 */ u16 fadeTimer; +} TransitionFade; // size = 0xC + +typedef struct { + /* 0x000 */ Color_RGBA8_u32 color; + /* 0x004 */ Color_RGBA8_u32 envColor; + /* 0x008 */ s32 texX; + /* 0x00C */ s32 texY; + /* 0x010 */ s32 step; + /* 0x014 */ u8 unk_14; + /* 0x015 */ u8 typeColor; + /* 0x016 */ u8 speed; + /* 0x017 */ u8 effect; + /* 0x018 */ u8 isDone; + /* 0x019 */ u8 frame; + /* 0x01A */ u16 normal; + /* 0x020 */ Mtx projection; + /* 0x060 */ Mtx lookAt; + /* 0x0A0 */ void* texture; + /* 0x0A8 */ Mtx modelView[2][3]; +} TransitionCircle; // size = 0x228; + +typedef struct { + /* 0x000 */ Color_RGBA8_u32 color; + /* 0x004 */ f32 transPos; + /* 0x008 */ f32 step; + /* 0x00C */ s32 state; + /* 0x010 */ s32 fadeDirection; + /* 0x018 */ Mtx projection; + /* 0x058 */ s32 frame; + /* 0x060 */ Mtx modelView[2][3]; +} TransitionTriforce; // size = 0x1E0; + +#endif diff --git a/soh/resource.h b/soh/resource.h new file mode 100644 index 000000000..8a193b3e8 --- /dev/null +++ b/soh/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Resource.rc +// +#define IDI_ICON1 111 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/soh/soh.sln b/soh/soh.sln new file mode 100644 index 000000000..4c870692f --- /dev/null +++ b/soh/soh.sln @@ -0,0 +1,55 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31112.23 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "soh", "soh.vcxproj", "{31348AA7-8DC5-4FA7-955F-E80855CADE9E}" + ProjectSection(ProjectDependencies) = postProject + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8} = {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8} + {A2E01C3E-D647-45D1-9788-043DEBC1A908} = {A2E01C3E-D647-45D1-9788-043DEBC1A908} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libultraship", "..\libultraship\libultraship\libultraship.vcxproj", "{6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZAPDUtils", "..\ZAPDTR\ZAPDUtils\ZAPDUtils.vcxproj", "{A2E01C3E-D647-45D1-9788-043DEBC1A908}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Debug|x64.ActiveCfg = Debug|x64 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Debug|x64.Build.0 = Debug|x64 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Debug|x86.ActiveCfg = Debug|Win32 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Debug|x86.Build.0 = Debug|Win32 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Release|x64.ActiveCfg = Release|x64 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Release|x64.Build.0 = Release|x64 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Release|x86.ActiveCfg = Release|Win32 + {31348AA7-8DC5-4FA7-955F-E80855CADE9E}.Release|x86.Build.0 = Release|Win32 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Debug|x64.ActiveCfg = Debug|x64 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Debug|x64.Build.0 = Debug|x64 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Debug|x86.ActiveCfg = Debug|Win32 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Debug|x86.Build.0 = Debug|Win32 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Release|x64.ActiveCfg = Release|x64 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Release|x64.Build.0 = Release|x64 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Release|x86.ActiveCfg = Release|Win32 + {6DA9B521-65B7-41E2-8F8A-F0451CC18ED8}.Release|x86.Build.0 = Release|Win32 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x64.ActiveCfg = Debug|x64 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x64.Build.0 = Debug|x64 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x86.ActiveCfg = Debug|Win32 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Debug|x86.Build.0 = Debug|Win32 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x64.ActiveCfg = Release|x64 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x64.Build.0 = Release|x64 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x86.ActiveCfg = Release|Win32 + {A2E01C3E-D647-45D1-9788-043DEBC1A908}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7659B687-4D92-4865-A037-045115E1C783} + EndGlobalSection +EndGlobal diff --git a/soh/soh.vcxproj b/soh/soh.vcxproj new file mode 100644 index 000000000..9d5b509f2 --- /dev/null +++ b/soh/soh.vcxproj @@ -0,0 +1,1404 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {31348aa7-8dc5-4fa7-955f-e80855cade9e} + soh + 10.0 + + + + Application + true + v142 + Unicode + false + + + Application + false + v142 + true + Unicode + false + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)assets;$(ProjectDir)build;$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir);$(ProjectDir)..\libultraship\libultraship\Lib\libjpeg\include;$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\spdlog\include;$(ProjectDir)..\ZAPDTR\ZAPDUtils;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64\PR;$(ProjectDir)..\libultraship\libultraship\Lib\SDL;$(IncludePath) + $(OutDir);$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\SDL\lib\x86;$(ProjectDir)..\libultraship\libultraship\Lib\GLEW\x86;$(ProjectDir)..\libultraship\libultraship\Lib\GLFW;$(LibraryPath) + $(Configuration)Obj\ + + + false + $(ProjectDir)assets;$(ProjectDir)build;$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir);$(ProjectDir)..\libultraship\libultraship\Lib\libjpeg\include;$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\spdlog\include;$(ProjectDir)..\ZAPDTR\ZAPDUtils;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64\PR;$(ProjectDir)..\libultraship\libultraship\Lib\SDL;$(IncludePath) + $(OutDir);$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\SDL\lib\x86;$(ProjectDir)..\libultraship\libultraship\Lib\GLEW\x86;$(ProjectDir)..\libultraship\libultraship\Lib\GLFW;$(LibraryPath) + $(Configuration)Obj\ + + + true + $(ProjectDir)assets;$(ProjectDir)build;$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir);$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\spdlog\include;$(ProjectDir)..\ZAPDTR\ZAPDUtils;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64\PR;$(IncludePath) + $(OutDir);$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\SDL\lib\x64;$(ProjectDir)..\libultraship\libultraship\Lib\GLEW\x64;$(ProjectDir)..\libultraship\libultraship\Lib\GLFW;$(LibraryPath) + + + false + $(ProjectDir)assets;$(ProjectDir)build;$(ProjectDir)include;$(ProjectDir)src;$(ProjectDir);$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\spdlog\include;$(ProjectDir)..\ZAPDTR\ZAPDUtils;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64;$(ProjectDir)..\libultraship\libultraship\Lib\Fast3D\U64\PR;$(IncludePath) + $(OutDir);$(ProjectDir)..\libultraship\libultraship;$(ProjectDir)..\libultraship\libultraship\Lib\SDL\lib\x64;$(ProjectDir)..\libultraship\libultraship\Lib\GLEW\x64;$(ProjectDir)..\libultraship\libultraship\Lib\GLFW;$(LibraryPath) + + + + TurnOffAllWarnings + false + NOINCLUDE_GAME_PRINTF;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;ENABLE_OPENGL;%(PreprocessorDefinitions) + true + stdcpp20 + MultiThreadedDebug + true + StackFrameRuntimeCheck + + + Console + true + libultraship.lib;ZAPDUtils.lib;opengl32.lib;glu32.lib;SDL2.lib;SDL2main.lib;glfw3dll.lib;glew32s.lib;winmm.lib;imm32.lib;version.lib;setupapi.lib;%(AdditionalDependencies) + 8777216 + true + true + + + copy /b $(ProjectDir)src\boot\build.c +,, + + + + + TurnOffAllWarnings + true + true + false + INCLUDE_GAME_PRINTF;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)GLEW_STATIC + true + stdcpp20 + MultiThreaded + MaxSpeed + true + + + Console + true + true + true + libultraship.lib;ZAPDUtils.lib;opengl32.lib;glu32.lib;SDL2.lib;SDL2main.lib;glfw3dll.lib;glew32s.lib;winmm.lib;imm32.lib;version.lib;setupapi.lib;%(AdditionalDependencies) + /FORCE:MULTIPLE %(AdditionalOptions) + + + + + TurnOffAllWarnings + false + INCLUDE_GAME_PRINTF;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;ENABLE_DX11;%(PreprocessorDefinitions)GLEW_STATIC + true + stdcpp17 + MultiThreadedDebug + true + + + Console + true + libultraship.lib;ZAPDUtils.lib;opengl32.lib;glu32.lib;SDL2.lib;SDL2main.lib;glfw3dll.lib;glew32s.lib;winmm.lib;imm32.lib;version.lib;setupapi.lib;%(AdditionalDependencies) + + + /FORCE:MULTIPLE %(AdditionalOptions) + + + + + Level3 + true + true + true + INCLUDE_GAME_PRINTF;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)GLEW_STATIC + true + true + + + Console + true + true + true + /FORCE:MULTIPLE %(AdditionalOptions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/soh/soh.vcxproj.filters b/soh/soh.vcxproj.filters new file mode 100644 index 000000000..30ca9d4ed --- /dev/null +++ b/soh/soh.vcxproj.filters @@ -0,0 +1,3729 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8d479b58-9306-40c3-9728-2cf5b185f5c4} + + + {8ebc52ba-5b1d-4bb3-9848-ba9ea57fc49f} + + + {54c2fe99-a68e-4ce2-813d-bce0ab1ac24f} + + + {0b91ecc8-6f20-4da1-87ca-6c7263aab3db} + + + {2104d3ab-707c-4ab2-bb1a-5cdce5c3d07d} + + + {022552c8-3bb8-4ba5-904c-e2c3eb28ce10} + + + {3ab8c311-cbfb-4eca-88af-2446ce3c89b0} + + + {8f2da1b8-973e-4be0-a036-c79a63bc3067} + + + {1a584bc2-7081-4251-a953-cdc7b611d62b} + + + {49bb84e4-a0b9-4733-9720-1ea120795083} + + + {d4794b4c-ae5d-4bf0-96f4-077f03807f5e} + + + {981df102-b362-4e2b-8efc-62756c28aa60} + + + {f8b2165f-9b32-4be1-a285-49349e09bef5} + + + {fcaae731-4e17-448b-b6c7-36e4c8413838} + + + {264ed8f8-f42b-475f-82ca-c264225ef56c} + + + {5fde6633-a728-4eea-a616-59d413bec476} + + + {3aeae6ac-1340-4a7b-bf34-cd196667fe37} + + + {fe745190-d4ec-4dc3-a9fd-ea553a971b14} + + + {06ba0ec4-2ee7-4454-93b5-5fd804723b6d} + + + {ffe200b9-a955-4843-a1ce-3603c132e591} + + + {18b9727f-30de-4ab8-a317-916090d4a110} + + + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\boot + + + Source Files\src\buffers + + + Source Files\src\buffers + + + Source Files\src\buffers + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\gamestates\ovl_title + + + Source Files\src\overlays\gamestates\ovl_select + + + Source Files\src\overlays\gamestates\ovl_opening + + + Source Files\src\overlays\gamestates\ovl_file_choose + + + Source Files\src\overlays\gamestates\ovl_file_choose + + + Source Files\soh + + + Source Files\soh + + + Source Files\soh + + + Source Files\soh + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\libultra + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Source Files\src\overlays\gamestates\ovl_file_choose + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\code + + + Source Files\src\overlays\gamestates\ovl_file_choose + + + Source Files\src\overlays\misc\ovl_map_mark_data + + + Source Files\soh + + + Source Files\soh\Enhancements + + + Source Files\soh\Enhancements + + + Source Files\soh\Enhancements + + + Source Files\soh + + + Source Files\soh + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Header Files\include + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Source Files\src\overlays\effects + + + Header Files\include + + + Source Files\soh + + + Source Files\src\overlays\misc\ovl_kaleido_scope + + + Header Files\soh\Enhancements + + + Header Files\soh\Enhancements + + + Header Files\soh\Enhancements + + + Header Files\soh\Enhancements + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Source Files\src\overlays\actors + + + Header Files + + + + + Header Files\include + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/soh/soh.vcxproj.user b/soh/soh.vcxproj.user new file mode 100644 index 000000000..f6a27bc57 --- /dev/null +++ b/soh/soh.vcxproj.user @@ -0,0 +1,22 @@ + + + + $(TargetDir) + WindowsLocalDebugger + + + + + $(TargetDir) + WindowsLocalDebugger + + + + + $(TargetDir) + WindowsLocalDebugger + + + false + + \ No newline at end of file diff --git a/soh/soh/Enhancements/bootcommands.c b/soh/soh/Enhancements/bootcommands.c new file mode 100644 index 000000000..6901c4644 --- /dev/null +++ b/soh/soh/Enhancements/bootcommands.c @@ -0,0 +1,71 @@ +#include "bootcommands.h" +#include "gameconsole.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +uint8_t gLoadFileSelect = 0, gSkipLogoTest = 0; + +extern BootCommandFunc BootCommands_Command_SkipLogo(char** argv, s32 argc); +extern BootCommandFunc BootCommands_Command_LoadFileSelect(char** argv, s32 argc); + +static BootCommand sCommands[] = { { "--skiplogo", BootCommands_Command_SkipLogo }, + { "--loadfileselect", BootCommands_Command_LoadFileSelect } }; + +void BootCommands_Init() +{ + CVar_RegisterS32("gDisableLOD", 0); + CVar_RegisterS32("gDebugEnabled", 0); + CVar_RegisterS32("gPauseLiveLink", 0); +} + +//void BootCommands_ParseBootArgs(char* str) +void BootCommands_ParseBootArgs(s32 argc, char** argv) +{ + s32 i; + + // Parse the commands + for (i = 0; i < argc; i++) { + s32 j; + + for (j = 0; j < ARRAY_COUNT(sCommands); j++) { + if (!strcmp(argv[i], sCommands[j].name)) { + s32 numArgsProcessed = sCommands[j].func(&argv[i], argc - i); + i += numArgsProcessed; + break; + } + } + } + + for (i = 0; i < argc; i++) + DebugArena_Free(argv[i]); + + DebugArena_Free(argv); +} + +/* + * Command Name: --skiplogo + * Description: Skips the N64 Logo Screen + * Arguments: None + */ +BootCommandFunc BootCommands_Command_SkipLogo(char** argv, s32 argc) { + gSkipLogoTest = 1; + return 0; +} + +/* + * Command Name: --loadfileselect + * Description: Loads the file select screen on bootup. + * Arguments: None + */ +BootCommandFunc BootCommands_Command_LoadFileSelect(char** argv, s32 argc) { + gLoadFileSelect = 1; + return 0; +} diff --git a/soh/soh/Enhancements/bootcommands.h b/soh/soh/Enhancements/bootcommands.h new file mode 100644 index 000000000..42877a499 --- /dev/null +++ b/soh/soh/Enhancements/bootcommands.h @@ -0,0 +1,21 @@ +#ifndef _BOOTCOMMANDS_H_ +#define _BOOTCOMMANDS_H_ +#include +#include + +typedef s32 (*BootCommandFunc)(char** argv, s32 argc); // Returns the number of arguments it read + +typedef struct BootCommand +{ + char* name; + BootCommandFunc func; +} BootCommand; + +extern uint8_t gLoadFileSelect; +extern uint8_t gSkipLogoTest; + +void BootCommands_Init(); +//void BootCommands_ParseBootArgs(char* str); +void BootCommands_ParseBootArgs(s32 argc, char** argv); + +#endif \ No newline at end of file diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp new file mode 100644 index 000000000..06126281f --- /dev/null +++ b/soh/soh/Enhancements/debugconsole.cpp @@ -0,0 +1,449 @@ +#include "debugconsole.h" +#include "../libultraship/SohImGuiImpl.h" +#include +#include + +#define Path _Path +#define PATH_HACK +#include +#include +#undef PATH_HACK +#undef Path + +extern "C" { +#include +#include "variables.h" +#include "functions.h" +#include "macros.h" +extern GlobalContext* gGlobalCtx; +} + +#include "cvar.h" + +#define CMD_REGISTER SohImGui::BindCmd + +static bool ActorSpawnHandler(const std::vector& args) { + if ((args.size() != 9) && (args.size() != 3) && (args.size() != 6)) { + ERROR("Not enough arguments passed to actorspawn"); + return CMD_FAILED; + } + + if (gGlobalCtx == nullptr) { + ERROR("GlobalCtx == nullptr"); + return CMD_FAILED; + } + + Player* player = GET_PLAYER(gGlobalCtx); + PosRot spawnPoint; + const s16 actorId = std::stoi(args[1]); + const s16 params = std::stoi(args[2]); + + spawnPoint = player->actor.world; + + switch (args.size()) { + case 9: + if (args[6][0] != ',') { + spawnPoint.rot.x = std::stoi(args[6]); + } + if (args[7][0] != ',') { + spawnPoint.rot.y = std::stoi(args[7]); + } + if (args[8][0] != ',') { + spawnPoint.rot.z = std::stoi(args[8]); + } + case 5: + if (args[3][0] != ',') { + spawnPoint.pos.x = std::stoi(args[3]); + } + if (args[4][0] != ',') { + spawnPoint.pos.y = std::stoi(args[4]); + } + if (args[5][0] != ',') { + spawnPoint.pos.z = std::stoi(args[5]); + } + } + + if (Actor_Spawn(&gGlobalCtx->actorCtx, gGlobalCtx, actorId, spawnPoint.pos.x, spawnPoint.pos.y, spawnPoint.pos.z, + spawnPoint.rot.x, spawnPoint.rot.y, spawnPoint.rot.z, params) == NULL) { + ERROR("Failed to spawn actor. Actor_Spawn returned NULL"); + return CMD_FAILED; + } + return CMD_SUCCESS; +} + + +static bool KillPlayerHandler([[maybe_unused]] const std::vector&) { + gSaveContext.health = 0; + + INFO("[SOH] You've met with a terrible fate, haven't you?"); + return CMD_SUCCESS; +} + +static bool SetPlayerHealthHandler(const std::vector& args) { + if (args.size() != 2) { + ERROR("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + int health; + + try { + health = std::stoi(args[1]); + } catch (std::invalid_argument const& ex) { + ERROR("[SOH] Health value must be an integer."); + return CMD_FAILED; + } + + if (health < 0) { + ERROR("[SOH] Health value must be a positive integer"); + return CMD_SUCCESS; + } + + gSaveContext.health = health * 0x10; + + INFO("[SOH] Player health updated to %d", health); + return CMD_SUCCESS; +} + + +static bool LoadSceneHandler(const std::vector&) { + gSaveContext.respawnFlag = 0; + gSaveContext.seqId = 0xFF; + gSaveContext.gameMode = 0; + + return CMD_SUCCESS; +} + +static bool RuppeHandler(const std::vector& args) { + if (args.size() < 2) + return CMD_FAILED; + + int rupeeAmount; + try { + rupeeAmount = std::stoi(args[1]); + } + catch (std::invalid_argument const& ex) { + ERROR("[SOH] Rupee count must be an integer."); + return CMD_FAILED; + } + + if (rupeeAmount < 0) { + ERROR("[SOH] Rupee count must be positive"); + return CMD_FAILED; + } + + gSaveContext.rupees = rupeeAmount; + + INFO("Set rupee count to %u", rupeeAmount); + return CMD_SUCCESS; +} + +static bool SetPosHandler(const std::vector args) { + if (gGlobalCtx == nullptr) { + ERROR("GlobalCtx == nullptr"); + return CMD_FAILED; + } + + Player* player = GET_PLAYER(gGlobalCtx); + + if (args.size() == 1) { + INFO("Player position is [ %.2f, %.2f, %.2f ]", player->actor.world.pos.x, player->actor.world.pos.y, + player->actor.world.pos.z); + return CMD_SUCCESS; + } + if (args.size() < 4) + return CMD_FAILED; + + player->actor.world.pos.x = std::stof(args[1]); + player->actor.world.pos.y = std::stof(args[2]); + player->actor.world.pos.z = std::stof(args[3]); + + INFO("Set player position to [ %.2f, %.2f, %.2f ]", player->actor.world.pos.x, player->actor.world.pos.y, + player->actor.world.pos.z); + return CMD_SUCCESS; +} + +static bool ResetHandler(std::vector args) { + if (gGlobalCtx == nullptr) { + ERROR("GlobalCtx == nullptr"); + return CMD_FAILED; + } + + SET_NEXT_GAMESTATE(&gGlobalCtx->state, TitleSetup_Init, GameState); + gGlobalCtx->state.running = false; + return CMD_SUCCESS; +} + +const static std::map ammoItems{ + { "sticks", ITEM_STICK }, { "deku_sticks", ITEM_STICK }, + { "nuts", ITEM_NUT }, { "deku_nuts", ITEM_NUT }, + { "bombs", ITEM_BOMB }, { "arrows", ITEM_BOW }, + { "bombchus", ITEM_BOMBCHU }, { "chus", ITEM_BOMBCHU }, + { "beans", ITEM_BEAN }, + { "seeds", ITEM_SLINGSHOT }, { "deku_seeds", ITEM_SLINGSHOT }, + { "magic_beans", ITEM_BEAN }, +}; + +static bool AmmoHandler(const std::vector& args) { + if (args.size() != 3) { + ERROR("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + int count; + + try { + count = std::stoi(args[2]); + } catch (std::invalid_argument const& ex) { + ERROR("Ammo count must be an integer"); + return CMD_FAILED; + } + + if (count < 0) { + ERROR("Ammo count must be positive"); + return CMD_FAILED; + } + + const auto& it = ammoItems.find(args[1]); + + if (it == ammoItems.end()) { + ERROR("Invalid item passed"); + return CMD_FAILED; + } + + // I dont think you can do OOB with just this + AMMO(it->second) = count; + + //To use a change by uncomment this + //Inventory_ChangeAmmo(it->second, count); +} + +const static std::map bottleItems{ + { "green_potion", ITEM_POTION_GREEN }, + { "red_potion", ITEM_POTION_RED }, + { "blue_potion", ITEM_POTION_BLUE }, + { "milk", ITEM_MILK }, { "half_milk", ITEM_MILK_HALF }, { "fairy", ITEM_FAIRY }, + { "bugs", ITEM_BUG }, { "fish", ITEM_FISH }, { "poe", ITEM_POE }, + { "big_poe", ITEM_BIG_POE }, { "blue_fire", ITEM_BLUE_FIRE }, { "rutos_letter", ITEM_LETTER_RUTO }, +}; + +static bool BottleHandler(const std::vector& args) { + if (args.size() != 3) { + ERROR("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + unsigned int slot; + try { + slot = std::stoi(args[2]); + } catch (std::invalid_argument const& ex) { + ERROR("[SOH] Bottle slot must be an integer."); + return CMD_FAILED; + } + + if ((slot < 1) || (slot > 4)) { + ERROR("Invalid slot passed"); + return CMD_FAILED; + } + + const auto& it = bottleItems.find(args[1]); + + if (it == bottleItems.end()) { + ERROR("Invalid item passed"); + return CMD_FAILED; + } + + // I dont think you can do OOB with just this + gSaveContext.inventory.items[0x11 + slot] = it->second; + + return CMD_SUCCESS; +} + +static bool BHandler(const std::vector& args) { + if (args.size() != 2) { + ERROR("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + gSaveContext.equips.buttonItems[0] = std::stoi(args[1]); + return CMD_SUCCESS; +} + +static bool ItemHandler(const std::vector& args) { + if (args.size() != 3) { + ERROR("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + gSaveContext.inventory.items[std::stoi(args[1])] = std::stoi(args[2]); + + return CMD_SUCCESS; +} + +static bool EntranceHandler(const std::vector& args) { + if (args.size() != 2) { + ERROR("[SOH] Unexpected arguments passed"); + return CMD_FAILED; + } + + unsigned int entrance; + + try { + entrance = std::stoi(args[1], nullptr, 16); + } catch (std::invalid_argument const& ex) { + ERROR("[SOH] Entrance value must be a Hex number."); + return CMD_FAILED; + } + gGlobalCtx->nextEntranceIndex = entrance; + + gGlobalCtx->sceneLoadFlag = 0x14; + gGlobalCtx->fadeTransition = 11; + gSaveContext.nextTransition = 11; +} + +#define VARTYPE_INTEGER 0 +#define VARTYPE_FLOAT 1 +#define VARTYPE_STRING 2 + +static int CheckVarType(const std::string& input) +{ + int result = VARTYPE_STRING; + + for (size_t i = 0; i < input.size(); i++) + { + if (!(std::isdigit(input[i]) || input[i] == '.')) + { + break; + } + else + { + if (input[i] == '.') + result = VARTYPE_FLOAT; + else if (std::isdigit(input[i]) && result != VARTYPE_FLOAT) + result = VARTYPE_INTEGER; + } + } + + return result; +} + +void DebugConsole_LoadCVars(); +void DebugConsole_SaveCVars(); + +static bool SetCVarHandler(const std::vector& args) { + if (args.size() < 3) + return CMD_FAILED; + + int vType = CheckVarType(args[2]); + + if (vType == VARTYPE_STRING) + CVar_SetString((char*)args[1].c_str(), (char*)args[2].c_str()); + else if (vType == VARTYPE_FLOAT) + CVar_SetFloat((char*)args[1].c_str(), std::stof(args[2])); + else + CVar_SetS32((char*)args[1].c_str(), std::stoi(args[2])); + + DebugConsole_SaveCVars(); + + //INFO("[SOH] Updated player position to [ %.2f, %.2f, %.2f ]", pos.x, pos.y, pos.z); + return CMD_SUCCESS; +} + + +static bool GetCVarHandler(const std::vector& args) { + if (args.size() < 2) + return CMD_FAILED; + + CVar* cvar = CVar_GetVar((char*) args[1].c_str()); + + if (cvar != nullptr) + { + if (cvar->type == CVAR_TYPE_S32) + INFO("[SOH] Variable %s is %i", args[1].c_str(), cvar->value.valueS32); + else if (cvar->type == CVAR_TYPE_FLOAT) + INFO("[SOH] Variable %s is %f", args[1].c_str(), cvar->value.valueFloat); + else if (cvar->type == CVAR_TYPE_STRING) + INFO("[SOH] Variable %s is %s", args[1].c_str(), cvar->value.valueStr); + } + else + { + INFO("[SOH] Could not find variable %s", args[1].c_str()); + } + + + return CMD_SUCCESS; +} + +void DebugConsole_Init(void) { + CMD_REGISTER("kill", { KillPlayerHandler, "Commit suicide." }); + CMD_REGISTER("map", { LoadSceneHandler, "Load up kak?" }); + CMD_REGISTER("rupee", { RuppeHandler, "Set your rupee counter.", { + {"amount", ArgumentType::NUMBER } + }}); + CMD_REGISTER("bItem", { BHandler, "Set an item to the B button.", { { "Item ID", ArgumentType::NUMBER } } }); + CMD_REGISTER("health", { SetPlayerHealthHandler, "Set the health of the player.", { + {"health", ArgumentType::NUMBER } + }}); + CMD_REGISTER("spawn", { ActorSpawnHandler, "Spawn an actor.", { + { "actor_id", ArgumentType::NUMBER }, + { "data", ArgumentType::NUMBER }, + { "x", ArgumentType::PLAYER_POS, true }, + { "y", ArgumentType::PLAYER_POS, true }, + { "z", ArgumentType::PLAYER_POS, true }, + { "rx", ArgumentType::PLAYER_ROT, true }, + { "ry", ArgumentType::PLAYER_ROT, true }, + { "rz", ArgumentType::PLAYER_ROT, true } + }}); + CMD_REGISTER("pos", { SetPosHandler, "Sets the position of the player.", { + { "x", ArgumentType::PLAYER_POS, true }, + { "y", ArgumentType::PLAYER_POS, true }, + { "z", ArgumentType::PLAYER_POS, true } + }}); + CMD_REGISTER("set", { SetCVarHandler, + "Sets a console variable.", + { { "varName", ArgumentType::TEXT }, { "varValue", ArgumentType::TEXT } } }); + CMD_REGISTER("get", { GetCVarHandler, "Gets a console variable.", { { "varName", ArgumentType::TEXT } } }); + CMD_REGISTER("reset", { ResetHandler, "Resets the game." }); + CMD_REGISTER("ammo", { AmmoHandler, "Changes ammo of an item.", + { { "item", ArgumentType::TEXT }, + { "count", ArgumentType::NUMBER } } }); + + CMD_REGISTER("bottle", { BottleHandler, + "Changes item in a bottle slot.", + { { "item", ArgumentType::TEXT }, { "slot", ArgumentType::NUMBER } } }); + + CMD_REGISTER("item", { ItemHandler, + "Sets item ID in arg 1 into slot arg 2. No boundary checks. Use with caution.", + { { "slot", ArgumentType::NUMBER }, { "item id", ArgumentType::NUMBER } } }); + CMD_REGISTER("entrance", + { EntranceHandler, "Sends player to the entered entrance (hex)", { { "entrance", ArgumentType::NUMBER } } }); + + DebugConsole_LoadCVars(); +} + +void DebugConsole_LoadCVars() +{ + if (File::Exists("cvars.cfg")) { + const auto lines = File::ReadAllLines("cvars.cfg"); + + for (const std::string& line : lines) { + SohImGui::console->Dispatch(line); + } + } +} + +void DebugConsole_SaveCVars() +{ + std::string output; + + for (const auto &cvar : cvars) { + if (cvar.second->type == CVAR_TYPE_STRING) + output += StringHelper::Sprintf("set %s %s\n", cvar.first.c_str(), cvar.second->value.valueStr); + else if (cvar.second->type == CVAR_TYPE_S32) + output += StringHelper::Sprintf("set %s %i\n", cvar.first.c_str(), cvar.second->value.valueS32); + else if (cvar.second->type == CVAR_TYPE_FLOAT) + output += StringHelper::Sprintf("set %s %f\n", cvar.first.c_str(), cvar.second->value.valueFloat); + } + + File::WriteAllText("cvars.cfg", output); +} \ No newline at end of file diff --git a/soh/soh/Enhancements/debugconsole.h b/soh/soh/Enhancements/debugconsole.h new file mode 100644 index 000000000..21e0b51f1 --- /dev/null +++ b/soh/soh/Enhancements/debugconsole.h @@ -0,0 +1,3 @@ +#pragma once + +void DebugConsole_Init(void); \ No newline at end of file diff --git a/soh/soh/Enhancements/gamecommand.h b/soh/soh/Enhancements/gamecommand.h new file mode 100644 index 000000000..6f353e9a3 --- /dev/null +++ b/soh/soh/Enhancements/gamecommand.h @@ -0,0 +1,10 @@ +#ifndef _GAMECOMMAND_H_ +#define _GAMECOMMAND_H_ +#include +#include + +#define MAX_COMMAND_LEN 0x100 + +s32 GameCommand_ExecuteCommand(); + +#endif diff --git a/soh/soh/Enhancements/gameconsole.c b/soh/soh/Enhancements/gameconsole.c new file mode 100644 index 000000000..58cb5f750 --- /dev/null +++ b/soh/soh/Enhancements/gameconsole.c @@ -0,0 +1,20 @@ +#include "gameconsole.h" +#include "../OTRGlobals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* memcpy */ +#include +#include + +extern GlobalContext* gGlobalCtx; + +void GameConsole_Init() {} \ No newline at end of file diff --git a/soh/soh/Enhancements/gameconsole.h b/soh/soh/Enhancements/gameconsole.h new file mode 100644 index 000000000..d7052fce3 --- /dev/null +++ b/soh/soh/Enhancements/gameconsole.h @@ -0,0 +1,42 @@ +#ifndef _GAMECONSOLE_H_ +#define _GAMECONSOLE_H_ + +#include +#include +#include "cvar.h" + +#define MAX_CVARS 2048 + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum +{ + CONSOLECOMMAND_RESULT_SUCCESS, + CONSOLECOMMAND_RESULT_INVALIDARGS, + CONSOLECOMMAND_RESULT_INVALIDCOMMAND, + CONSOLECOMMAND_RESULT_FAILURE +} ConsoleCommandResult; + +typedef ConsoleCommandResult(*ConsoleCommandFunc)(char** argv, s32 argc); // Returns the number of arguments it read + +typedef struct ConsoleCommand +{ + char* name; + ConsoleCommandFunc func; + char* description; +} ConsoleCommand; + +extern GlobalContext* gGlobalCtx; + +void GameConsole_Init(); + +s32 GameConsole_Split(char* str, char** argv); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/soh/soh/GbiWrap.cpp b/soh/soh/GbiWrap.cpp new file mode 100644 index 000000000..1f6a8003c --- /dev/null +++ b/soh/soh/GbiWrap.cpp @@ -0,0 +1,86 @@ +#include "z64.h" + +//OTRTODO - this is awful + +extern "C" { +void InitOTR(); +void Graph_ProcessFrame(void (*run_one_game_iter)(void)); +void Graph_ProcessGfxCommands(Gfx* commands); +void OTRLogString(const char* src); +void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char)); +void OTRSetFrameDivisor(int divisor); +uint16_t OTRGetPixelDepth(float x, float y); +int32_t OTRGetLastScancode(); +void ResourceMgr_CacheDirectory(const char* resName); +void ResourceMgr_LoadFile(const char* resName); +char* ResourceMgr_LoadFileFromDisk(const char* filePath); +char* ResourceMgr_LoadTexByName(char* texPath); +char* ResourceMgr_LoadTexOrDListByName(char* filePath); +char* ResourceMgr_LoadPlayerAnimByName(char* animPath); +char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc); +Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc); +Gfx* ResourceMgr_LoadGfxByName(char* path); +Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc); +Vtx* ResourceMgr_LoadVtxByName(char* path); +CollisionHeader* ResourceMgr_LoadColByName(char* path); +uint64_t GetPerfCounter(); +int ResourceMgr_OTRSigCheck(char* imgData); + +} + + +extern "C" void gSPSegment(void* value, int segNum, uintptr_t target) { + char* imgData = (char*)target; + + int res = ResourceMgr_OTRSigCheck(imgData); + + if (res) + target = (uintptr_t)ResourceMgr_LoadTexOrDListByName(imgData); + + __gSPSegment(value, segNum, target); +} + +extern "C" void gDPSetTextureImage(Gfx* pkt, u32 format, u32 size, u32 width, uintptr_t i) { + __gDPSetTextureImage(pkt, format, size, width, i); +} + +extern "C" void gDPSetTextureImageFB(Gfx* pkt, u32 format, u32 size, u32 width, int fb) +{ + __gDPSetTextureImageFB(pkt, format, size, width, fb); +} + +extern "C" void gSPDisplayList(Gfx* pkt, Gfx* dl) { + char* imgData = (char*)dl; + + if (ResourceMgr_OTRSigCheck(imgData) == 1) + dl = ResourceMgr_LoadGfxByName(imgData); + + __gSPDisplayList(pkt, dl); +} + +extern "C" void gSPDisplayListOffset(Gfx* pkt, Gfx* dl, int offset) { + char* imgData = (char*)dl; + + if (ResourceMgr_OTRSigCheck(imgData) == 1) + dl = ResourceMgr_LoadGfxByName(imgData); + + __gSPDisplayList(pkt, dl + offset); +} + +extern "C" void gSPVertex(Gfx* pkt, uintptr_t v, int n, int v0) { + + if (ResourceMgr_OTRSigCheck((char*)v) == 1) + v = (uintptr_t)ResourceMgr_LoadVtxByName((char*)v); + + __gSPVertex(pkt, v, n, v0); +} + +extern "C" void gSPInvalidateTexCache(Gfx* pkt, uintptr_t texAddr) +{ + char* imgData = (char*)texAddr; + + if (texAddr != 0 && ResourceMgr_OTRSigCheck(imgData)) + texAddr = (uintptr_t)ResourceMgr_LoadTexByName(imgData); + + __gSPInvalidateTexCache(pkt, texAddr); + } \ No newline at end of file diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp new file mode 100644 index 000000000..0732a80e1 --- /dev/null +++ b/soh/soh/OTRGlobals.cpp @@ -0,0 +1,799 @@ +#include "OTRGlobals.h" +#include +#include +#include +#include "GlobalCtx2.h" +#include "ResourceMgr.h" +#include "DisplayList.h" +#include "PlayerAnimation.h" +#include "Skeleton.h" +#include "Window.h" +#include "z64animation.h" +#include "z64bgcheck.h" +#include "../soh/enhancements/gameconsole.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "Lib/stb/stb_image.h" +#include "AudioPlayer.h" +#include "../soh/Enhancements/debugconsole.h" + +OTRGlobals* OTRGlobals::Instance; + +OTRGlobals::OTRGlobals() { + context = Ship::GlobalCtx2::CreateInstance("Ship of Harkinian"); + context->GetWindow()->Init(); +} + +OTRGlobals::~OTRGlobals() { +} + +extern uintptr_t clearMtx; +extern "C" Mtx gMtxClear; +extern "C" MtxF gMtxFClear; +extern "C" void OTRMessage_Init(); + +// C->C++ Bridge +extern "C" void InitOTR() { + OTRGlobals::Instance = new OTRGlobals(); + clearMtx = (uintptr_t)&gMtxClear; + OTRMessage_Init(); + DebugConsole_Init(); +} + +extern "C" uint64_t GetFrequency() { + LARGE_INTEGER nFreq; + + QueryPerformanceFrequency(&nFreq); + + return nFreq.QuadPart; +} + +extern "C" uint64_t GetPerfCounter() { + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + + return ticks.QuadPart; +} + +// C->C++ Bridge +extern "C" void Graph_ProcessFrame(void (*run_one_game_iter)(void)) { + OTRGlobals::Instance->context->GetWindow()->MainLoop(run_one_game_iter); +} + +// C->C++ Bridge +extern "C" void Graph_ProcessGfxCommands(Gfx* commands) { + OTRGlobals::Instance->context->GetWindow()->RunCommands(commands); + + // OTRTODO: FIGURE OUT END FRAME POINT + /* if (OTRGlobals::Instance->context->GetWindow()->lastScancode != -1) + OTRGlobals::Instance->context->GetWindow()->lastScancode = -1;*/ + +} + +float divisor_num = 0.0f; + +extern "C" void OTRSetFrameDivisor(int divisor) { + OTRGlobals::Instance->context->GetWindow()->SetFrameDivisor(divisor); +} + +extern "C" uint16_t OTRGetPixelDepth(float x, float y) { + return OTRGlobals::Instance->context->GetWindow()->GetPixelDepth(x, y); +} + +extern "C" int32_t OTRGetLastScancode() +{ + return OTRGlobals::Instance->context->GetWindow()->lastScancode; +} + +extern "C" void OTRResetScancode() +{ + OTRGlobals::Instance->context->GetWindow()->lastScancode = -1; +} + +extern "C" void ResourceMgr_CacheDirectory(const char* resName) { + OTRGlobals::Instance->context->GetResourceManager()->CacheDirectory(resName); +} +extern "C" void ResourceMgr_DirtyDirectory(const char* resName) { + OTRGlobals::Instance->context->GetResourceManager()->DirtyDirectory(resName); +} + +extern "C" void ResourceMgr_InvalidateCache() { + OTRGlobals::Instance->context->GetResourceManager()->InvalidateResourceCache(); +} + + +extern "C" void ResourceMgr_LoadFile(const char* resName) { + OTRGlobals::Instance->context->GetResourceManager()->LoadResource(resName); +} + +extern "C" char* ResourceMgr_LoadFileRaw(const char* resName) { + return OTRGlobals::Instance->context->GetResourceManager()->LoadFile(resName)->buffer.get(); +} + +extern "C" char* ResourceMgr_LoadFileFromDisk(const char* filePath) { + FILE* file = fopen(filePath, "r"); + fseek(file, 0, SEEK_END); + int fSize = ftell(file); + fseek(file, 0, SEEK_SET); + + char* data = (char*)malloc(fSize); + fread(data, 1, fSize, file); + + fclose(file); + + return data; +} + +extern "C" char* ResourceMgr_LoadJPEG(char* data, int dataSize) +{ + static char* finalBuffer = 0; + + if (finalBuffer == 0) + finalBuffer = (char*)malloc(dataSize); + + int w; + int h; + int comp; + + unsigned char* pixels = stbi_load_from_memory((const unsigned char*)data, 320 * 240 * 2, &w, &h, &comp, STBI_rgb_alpha); + //unsigned char* pixels = stbi_load_from_memory((const unsigned char*)data, 480 * 240 * 2, &w, &h, &comp, STBI_rgb_alpha); + int idx = 0; + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + uint16_t* bufferTest = (uint16_t*)finalBuffer; + int pixelIdx = ((y * w) + x) * 4; + + uint8_t r = pixels[pixelIdx + 0] / 8; + uint8_t g = pixels[pixelIdx + 1] / 8; + uint8_t b = pixels[pixelIdx + 2] / 8; + + uint8_t alphaBit = pixels[pixelIdx + 3] != 0; + + uint16_t data = (r << 11) + (g << 6) + (b << 1) + alphaBit; + + finalBuffer[idx++] = (data & 0xFF00) >> 8; + finalBuffer[idx++] = (data & 0x00FF); + } + } + + return (char*)finalBuffer; +} + +extern "C" char* ResourceMgr_LoadTexByName(char* texPath); + +extern "C" char* ResourceMgr_LoadTexOrDListByName(char* filePath) { + auto res = OTRGlobals::Instance->context->GetResourceManager()->LoadResource(filePath); + + if (res->resType == Ship::ResourceType::DisplayList) + return (char*)&((std::static_pointer_cast(res))->instructions[0]); + else if (res->resType == Ship::ResourceType::Array) + return (char*)(std::static_pointer_cast(res))->vertices.data(); + else + return ResourceMgr_LoadTexByName(filePath); +} + +extern "C" char* ResourceMgr_LoadPlayerAnimByName(char* animPath) { + auto anim = std::static_pointer_cast( + OTRGlobals::Instance->context->GetResourceManager()->LoadResource(animPath)); + + return (char*)&anim->limbRotData[0]; +} + +extern "C" Gfx* ResourceMgr_LoadGfxByName(char* path) +{ + auto res = std::static_pointer_cast( + OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + return (Gfx*)&res->instructions[0]; +} + +extern "C" char* ResourceMgr_LoadArrayByName(char* path) +{ + auto res = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + + return (char*)res->scalars.data(); +} + +extern "C" char* ResourceMgr_LoadArrayByNameAsVec3s(char* path) { + auto res = + std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + + if (res->cachedGameAsset != nullptr) + return (char*)res->cachedGameAsset; + else + { + Vec3s* data = (Vec3s*)malloc(sizeof(Vec3s) * res->scalars.size()); + + for (int i = 0; i < res->scalars.size(); i += 3) { + data[(i / 3)].x = res->scalars[i + 0].s16; + data[(i / 3)].y = res->scalars[i + 1].s16; + data[(i / 3)].z = res->scalars[i + 2].s16; + } + + res->cachedGameAsset = data; + + return (char*)data; + } +} + +extern "C" CollisionHeader* ResourceMgr_LoadColByName(char* path) +{ + auto colRes = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + + if (colRes->cachedGameAsset != nullptr) + return (CollisionHeader*)colRes->cachedGameAsset; + + CollisionHeader* colHeader = (CollisionHeader*)malloc(sizeof(CollisionHeader)); + + colHeader->minBounds.x = colRes->absMinX; + colHeader->minBounds.y = colRes->absMinY; + colHeader->minBounds.z = colRes->absMinZ; + + colHeader->maxBounds.x = colRes->absMaxX; + colHeader->maxBounds.y = colRes->absMaxY; + colHeader->maxBounds.z = colRes->absMaxZ; + + colHeader->vtxList = (Vec3s*)malloc(sizeof(Vec3s) * colRes->vertices.size()); + colHeader->numVertices = colRes->vertices.size(); + + for (int i = 0; i < colRes->vertices.size(); i++) + { + colHeader->vtxList[i].x = colRes->vertices[i].x; + colHeader->vtxList[i].y = colRes->vertices[i].y; + colHeader->vtxList[i].z = colRes->vertices[i].z; + } + + colHeader->polyList = (CollisionPoly*)malloc(sizeof(CollisionPoly) * colRes->polygons.size()); + colHeader->numPolygons = colRes->polygons.size(); + + for (int i = 0; i < colRes->polygons.size(); i++) + { + colHeader->polyList[i].type = colRes->polygons[i].type; + colHeader->polyList[i].flags_vIA = colRes->polygons[i].vtxA; + colHeader->polyList[i].flags_vIB = colRes->polygons[i].vtxB; + colHeader->polyList[i].vIC = colRes->polygons[i].vtxC; + colHeader->polyList[i].normal.x = colRes->polygons[i].a; + colHeader->polyList[i].normal.y = colRes->polygons[i].b; + colHeader->polyList[i].normal.z = colRes->polygons[i].c; + colHeader->polyList[i].dist = colRes->polygons[i].d; + } + + colHeader->surfaceTypeList = (SurfaceType*)malloc(colRes->polygonTypes.size() * sizeof(SurfaceType)); + + for (int i = 0; i < colRes->polygonTypes.size(); i++) + { + colHeader->surfaceTypeList[i].data[0] = colRes->polygonTypes[i] >> 32; + colHeader->surfaceTypeList[i].data[1] = colRes->polygonTypes[i] & 0xFFFFFFFF; + } + + colHeader->cameraDataList = (CamData*)malloc(sizeof(CamData) * colRes->camData->entries.size()); + + for (int i = 0; i < colRes->camData->entries.size(); i++) + { + colHeader->cameraDataList[i].cameraSType = colRes->camData->entries[i]->cameraSType; + colHeader->cameraDataList[i].numCameras = colRes->camData->entries[i]->numData; + + int idx = colRes->camData->entries[i]->cameraPosDataIdx; + + colHeader->cameraDataList[i].camPosData = (Vec3s*)malloc(sizeof(Vec3s)); + + if (colRes->camData->cameraPositionData.size() > 0) + { + colHeader->cameraDataList[i].camPosData->x = colRes->camData->cameraPositionData[idx]->x; + colHeader->cameraDataList[i].camPosData->y = colRes->camData->cameraPositionData[idx]->y; + colHeader->cameraDataList[i].camPosData->z = colRes->camData->cameraPositionData[idx]->z; + } + else + { + colHeader->cameraDataList[i].camPosData->x = 0; + colHeader->cameraDataList[i].camPosData->y = 0; + colHeader->cameraDataList[i].camPosData->z = 0; + } + } + + colHeader->numWaterBoxes = colRes->waterBoxes.size(); + colHeader->waterBoxes = (WaterBox*)malloc(sizeof(WaterBox) * colHeader->numWaterBoxes); + + for (int i = 0; i < colHeader->numWaterBoxes; i++) + { + colHeader->waterBoxes[i].xLength = colRes->waterBoxes[i].xLength; + colHeader->waterBoxes[i].ySurface = colRes->waterBoxes[i].ySurface; + colHeader->waterBoxes[i].xMin = colRes->waterBoxes[i].xMin; + colHeader->waterBoxes[i].zMin = colRes->waterBoxes[i].zMin; + colHeader->waterBoxes[i].xLength = colRes->waterBoxes[i].xLength; + colHeader->waterBoxes[i].zLength = colRes->waterBoxes[i].zLength; + colHeader->waterBoxes[i].properties = colRes->waterBoxes[i].properties; + } + + colRes->cachedGameAsset = colHeader; + + return (CollisionHeader*)colHeader; +} + +extern "C" Vtx * ResourceMgr_LoadVtxByName(char* path) +{ + auto res = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + return (Vtx*)res->vertices.data(); +} + +extern "C" int ResourceMgr_OTRSigCheck(char* imgData) +{ + uintptr_t i = (uintptr_t)(imgData); + + if (i == 0xD9000000 || i == 0xE7000000 || (i & 0xF0000000) == 0xF0000000) + return 0; + + if ((i & 0xFF000000) != 0xAB000000 && (i & 0xFF000000) != 0xCD000000 && i != 0) { + if (imgData[0] == '_' && imgData[1] == '_' && imgData[2] == 'O' && imgData[3] == 'T' && imgData[4] == 'R' && + imgData[5] == '_' && imgData[6] == '_') + return 1; + } + + return 0; +} + +extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(char* path) { + auto res = std::static_pointer_cast( + OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + + if (res->cachedGameAsset != nullptr) + return (AnimationHeaderCommon*)res->cachedGameAsset; + + AnimationHeaderCommon* anim = nullptr; + + if (res->type == Ship::AnimationType::Normal) { + AnimationHeader* animNormal = (AnimationHeader*)malloc(sizeof(AnimationHeader)); + + animNormal->common.frameCount = res->frameCount; + animNormal->frameData = (int16_t*)malloc(res->rotationValues.size() * sizeof(int16_t)); + + for (int i = 0; i < res->rotationValues.size(); i++) + animNormal->frameData[i] = res->rotationValues[i]; + + animNormal->jointIndices = (JointIndex*)malloc(res->rotationIndices.size() * sizeof(Vec3s)); + + for (int i = 0; i < res->rotationIndices.size(); i++) { + animNormal->jointIndices[i].x = res->rotationIndices[i].x; + animNormal->jointIndices[i].y = res->rotationIndices[i].y; + animNormal->jointIndices[i].z = res->rotationIndices[i].z; + } + + animNormal->staticIndexMax = res->limit; + + anim = (AnimationHeaderCommon*)animNormal; + } + else if (res->type == Ship::AnimationType::Curve) + { + TransformUpdateIndex* animCurve = (TransformUpdateIndex*)malloc(sizeof(TransformUpdateIndex)); + + animCurve->copyValues = (s16*)malloc(res->copyValuesArr.size() * sizeof(s16)); + + for (int i = 0; i < res->copyValuesArr.size(); i++) + animCurve->copyValues[i] = res->copyValuesArr[i]; + + animCurve->transformData = (TransformData*)malloc(res->transformDataArr.size() * sizeof(TransformData)); + + for (int i = 0; i < res->transformDataArr.size(); i++) + { + animCurve->transformData[i].unk_00 = res->transformDataArr[i].unk_00; + animCurve->transformData[i].unk_02 = res->transformDataArr[i].unk_02; + animCurve->transformData[i].unk_04 = res->transformDataArr[i].unk_04; + animCurve->transformData[i].unk_06 = res->transformDataArr[i].unk_06; + animCurve->transformData[i].unk_08 = res->transformDataArr[i].unk_08; + } + + animCurve->refIndex = (u8*)malloc(res->refIndexArr.size()); + for (int i = 0; i < res->refIndexArr.size(); i++) + animCurve->refIndex[i] = res->refIndexArr[i]; + + anim = (AnimationHeaderCommon*)animCurve; + } + else { + LinkAnimationHeader* animLink = (LinkAnimationHeader*)malloc(sizeof(LinkAnimationHeader)); + animLink->common.frameCount = res->frameCount; + animLink->segment = (void*)res->segPtr; + + anim = (AnimationHeaderCommon*)animLink; + } + + res->cachedGameAsset = anim; + + return anim; +} + +extern "C" SkeletonHeader* ResourceMgr_LoadSkeletonByName(char* path) { + auto res = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + + if (res->cachedGameAsset != nullptr) + return (SkeletonHeader*)res->cachedGameAsset; + + SkeletonHeader* baseHeader = nullptr; + + if (res->type == Ship::SkeletonType::Normal) + { + baseHeader = (SkeletonHeader*)malloc(sizeof(SkeletonHeader)); + } + else if (res->type == Ship::SkeletonType::Curve) + { + SkelCurveLimbList* curve = (SkelCurveLimbList*)malloc(sizeof(SkelCurveLimbList)); + curve->limbCount = res->limbCount; + curve->limbs = (SkelCurveLimb**)malloc(res->limbCount * sizeof(SkelCurveLimb*)); + baseHeader = (SkeletonHeader*)curve; + } + else { + FlexSkeletonHeader* flex = (FlexSkeletonHeader*)malloc(sizeof(FlexSkeletonHeader)); + flex->dListCount = res->dListCount; + + baseHeader = (SkeletonHeader*)flex; + } + + if (res->type != Ship::SkeletonType::Curve) + { + baseHeader->limbCount = res->limbCount; + baseHeader->segment = (void**)malloc(sizeof(StandardLimb*) * res->limbTable.size()); + } + + for (int i = 0; i < res->limbTable.size(); i++) { + std::string limbStr = res->limbTable[i]; + auto limb = std::static_pointer_cast( + OTRGlobals::Instance->context->GetResourceManager()->LoadResource(limbStr.c_str())); + + if (limb->limbType == Ship::LimbType::LOD) { + LodLimb* limbC = (LodLimb*)malloc(sizeof(LodLimb)); + limbC->jointPos.x = limb->transX; + limbC->jointPos.y = limb->transY; + limbC->jointPos.z = limb->transZ; + limbC->child = limb->childIndex; + limbC->sibling = limb->siblingIndex; + + if (limb->dListPtr != "") { + auto dList = ResourceMgr_LoadGfxByName((char*)limb->dListPtr.c_str()); + limbC->dLists[0] = dList; + } else { + limbC->dLists[0] = nullptr; + } + + if (limb->dList2Ptr != "") { + auto dList = ResourceMgr_LoadGfxByName((char*)limb->dList2Ptr.c_str()); + limbC->dLists[1] = dList; + } else { + limbC->dLists[1] = nullptr; + } + + baseHeader->segment[i] = limbC; + } + else if (limb->limbType == Ship::LimbType::Standard) + { + const auto limbC = new StandardLimb; + limbC->jointPos.x = limb->transX; + limbC->jointPos.y = limb->transY; + limbC->jointPos.z = limb->transZ; + limbC->child = limb->childIndex; + limbC->sibling = limb->siblingIndex; + limbC->dList = nullptr; + + if (!limb->dListPtr.empty()) { + const auto dList = ResourceMgr_LoadGfxByName(const_cast(limb->dListPtr.c_str())); + limbC->dList = dList; + } + + baseHeader->segment[i] = limbC; + } + else if (limb->limbType == Ship::LimbType::Curve) + { + const auto limbC = new SkelCurveLimb; + + limbC->firstChildIdx = limb->childIndex; + limbC->nextLimbIdx = limb->siblingIndex; + limbC->dList[0] = nullptr; + limbC->dList[1] = nullptr; + + if (!limb->dListPtr.empty()) { + const auto dList = ResourceMgr_LoadGfxByName(const_cast(limb->dListPtr.c_str())); + limbC->dList[0] = dList; + } + + if (!limb->dList2Ptr.empty()) { + const auto dList = ResourceMgr_LoadGfxByName(const_cast(limb->dList2Ptr.c_str())); + limbC->dList[1] = dList; + } + + const auto curve = reinterpret_cast(baseHeader); + curve->limbs[i] = limbC; + } + else if (limb->limbType == Ship::LimbType::Skin) + { + const auto limbC = new SkinLimb; + limbC->jointPos.x = limb->transX; + limbC->jointPos.y = limb->transY; + limbC->jointPos.z = limb->transZ; + limbC->child = limb->childIndex; + limbC->sibling = limb->siblingIndex; + + if (limb->skinSegmentType == Ship::ZLimbSkinType::SkinType_DList) + limbC->segmentType = static_cast(limb->skinSegmentType); + else if (limb->skinSegmentType == Ship::ZLimbSkinType::SkinType_4) + limbC->segmentType = 4; + else if (limb->skinSegmentType == Ship::ZLimbSkinType::SkinType_5) + limbC->segmentType = 5; + else + limbC->segmentType = 0; + + if (limb->skinSegmentType == Ship::ZLimbSkinType::SkinType_DList) + limbC->segment = ResourceMgr_LoadGfxByName(const_cast(limb->skinDList.c_str())); + else if (limb->skinSegmentType == Ship::ZLimbSkinType::SkinType_4) { + const auto animData = new SkinAnimatedLimbData; + const int skinDataSize = limb->skinData.size(); + + animData->totalVtxCount = limb->skinVtxCnt; + animData->limbModifCount = skinDataSize; + animData->limbModifications = new SkinLimbModif[animData->limbModifCount]; + animData->dlist = ResourceMgr_LoadGfxByName(const_cast(limb->skinDList2.c_str())); + + for (int i = 0; i < skinDataSize; i++) + { + animData->limbModifications[i].vtxCount = limb->skinData[i].unk_8_arr.size(); + animData->limbModifications[i].transformCount = limb->skinData[i].unk_C_arr.size(); + animData->limbModifications[i].unk_4 = limb->skinData[i].unk_4; + + animData->limbModifications[i].skinVertices = new SkinVertex[limb->skinData[i].unk_8_arr.size()]; + + for (int k = 0; k < limb->skinData[i].unk_8_arr.size(); k++) + { + animData->limbModifications[i].skinVertices[k].index = limb->skinData[i].unk_8_arr[k].unk_0; + animData->limbModifications[i].skinVertices[k].s = limb->skinData[i].unk_8_arr[k].unk_2; + animData->limbModifications[i].skinVertices[k].t = limb->skinData[i].unk_8_arr[k].unk_4; + animData->limbModifications[i].skinVertices[k].normX = limb->skinData[i].unk_8_arr[k].unk_6; + animData->limbModifications[i].skinVertices[k].normY = limb->skinData[i].unk_8_arr[k].unk_7; + animData->limbModifications[i].skinVertices[k].normZ = limb->skinData[i].unk_8_arr[k].unk_8; + animData->limbModifications[i].skinVertices[k].alpha = limb->skinData[i].unk_8_arr[k].unk_9; + } + + animData->limbModifications[i].limbTransformations = + new SkinTransformation[limb->skinData[i].unk_C_arr.size()]; + + for (int k = 0; k < limb->skinData[i].unk_C_arr.size(); k++) + { + animData->limbModifications[i].limbTransformations[k].limbIndex = limb->skinData[i].unk_C_arr[k].unk_0; + animData->limbModifications[i].limbTransformations[k].x = limb->skinData[i].unk_C_arr[k].x; + animData->limbModifications[i].limbTransformations[k].y = limb->skinData[i].unk_C_arr[k].y; + animData->limbModifications[i].limbTransformations[k].z = limb->skinData[i].unk_C_arr[k].z; + animData->limbModifications[i].limbTransformations[k].scale = limb->skinData[i].unk_C_arr[k].unk_8; + } + } + + limbC->segment = animData; + + //limbC->segment = nullptr; + } + + /*if (limb->dListPtr != "") { + auto dList = ResourceMgr_LoadGfxByName((char*)limb->dListPtr.c_str()); + limbC->unk_8 = dList; + } + else { + limbC->unk_8 = nullptr; + }*/ + + baseHeader->segment[i] = limbC; + } + else + { + // OTRTODO: Print error here... + } + } + + res->cachedGameAsset = baseHeader; + + return baseHeader; +} + +extern "C" s32* ResourceMgr_LoadCSByName(char* path) +{ + auto res = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); + return (s32*)res->commands.data(); +} + +std::wstring StringToU16(const std::string& s) { + std::vector result; + size_t i = 0; + while (i < s.size()) { + unsigned long uni; + size_t nbytes; + bool error = false; + unsigned char c = s[i++]; + if (c < 0x80) { // ascii + uni = c; + nbytes = 0; + } else if (c <= 0xBF) { // assuming kata/hiragana delimiter + nbytes = 0; + uni = '\1'; + } else if (c <= 0xDF) { + uni = c & 0x1F; + nbytes = 1; + } else if (c <= 0xEF) { + uni = c & 0x0F; + nbytes = 2; + } else if (c <= 0xF7) { + uni = c & 0x07; + nbytes = 3; + } + for (size_t j = 0; j < nbytes; ++j) { + unsigned char c = s[i++]; + uni <<= 6; + uni += c & 0x3F; + } + if (uni != '\1') + result.push_back(uni); + } + std::wstring utf16; + for (size_t i = 0; i < result.size(); ++i) { + unsigned long uni = result[i]; + if (uni <= 0xFFFF) { + utf16 += (wchar_t)uni; + } else { + uni -= 0x10000; + utf16 += (wchar_t)((uni >> 10) + 0xD800); + utf16 += (wchar_t)((uni & 0x3FF) + 0xDC00); + } + } + return utf16; +} + +extern "C" void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char)) { + const std::vector hira1 = { + u'を', u'ぁ', u'ぃ', u'ぅ', u'ぇ', u'ぉ', u'ゃ', u'ゅ', u'ょ', u'っ', u'-', u'あ', u'い', + u'う', u'え', u'お', u'か', u'き', u'く', u'け', u'こ', u'さ', u'し', u'す', u'せ', u'そ', + }; + + const std::vector hira2 = { + u'た', u'ち', u'つ', u'て', u'と', u'な', u'に', u'ぬ', u'ね', u'の', u'は', u'ひ', u'ふ', u'へ', u'ほ', u'ま', + u'み', u'む', u'め', u'も', u'や', u'ゆ', u'よ', u'ら', u'り', u'る', u'れ', u'ろ', u'わ', u'ん', u'゛', u'゜', + }; + + std::wstring wstr = StringToU16(str); + + for (const auto& c : wstr) { + unsigned char convt = ' '; + if (c < 0x80) { + printImpl(printer, c); + } else if (c >= u'。' && c <= u'゚') { // katakana + printImpl(printer, c - 0xFEC0); + } else { + auto it = std::find(hira1.begin(), hira1.end(), c); + if (it != hira1.end()) { // hiragana block 1 + printImpl(printer, 0x88 + std::distance(hira1.begin(), it)); + } + + auto it2 = std::find(hira2.begin(), hira2.end(), c); + if (it2 != hira2.end()) { // hiragana block 2 + printImpl(printer, 0xe0 + std::distance(hira2.begin(), it2)); + } + } + } +} + +extern "C" uint32_t OTRGetCurrentWidth() { + return OTRGlobals::Instance->context->GetWindow()->GetCurrentWidth(); +} + +extern "C" uint32_t OTRGetCurrentHeight() { + return OTRGlobals::Instance->context->GetWindow()->GetCurrentHeight(); +} + +extern "C" void OTRControllerCallback(ControllerCallback* controller) { + auto controllers = OTRGlobals::Instance->context->GetWindow()->Controllers; + for (int i = 0; i < controllers.size(); i++) { + for (int j = 0; j < controllers[i].size(); j++) { + OTRGlobals::Instance->context->GetWindow()->Controllers[i][j]->WriteToSource(controller); + } + } +} + +extern "C" float OTRGetAspectRatio() { + return gfx_current_dimensions.aspect_ratio; +} + +extern "C" float OTRGetDimensionFromLeftEdge(float v) { + return (SCREEN_WIDTH / 2 - SCREEN_HEIGHT / 2 * OTRGetAspectRatio() + (v)); +} + +extern "C" float OTRGetDimensionFromRightEdge(float v) { + return (SCREEN_WIDTH / 2 + SCREEN_HEIGHT / 2 * OTRGetAspectRatio() - (SCREEN_WIDTH - v)); +} + +f32 floorf(f32 x); +f32 ceilf(f32 x); + +extern "C" int16_t OTRGetRectDimensionFromLeftEdge(float v) { + return ((int)floorf(OTRGetDimensionFromLeftEdge(v))); +} + +extern "C" int16_t OTRGetRectDimensionFromRightEdge(float v) { + return ((int)ceilf(OTRGetDimensionFromRightEdge(v))); +} + +extern "C" void bswapSoundFontSound(SoundFontSound* swappable) { + swappable->sample = (SoundFontSample*)_byteswap_ulong((u32)swappable->sample); + swappable->tuningAsU32 = _byteswap_ulong((u32)swappable->tuningAsU32); +} + +extern "C" void bswapDrum(Drum* swappable) { + bswapSoundFontSound(&swappable->sound); + swappable->envelope = (AdsrEnvelope*)_byteswap_ulong((u32)swappable->envelope); +} + +extern "C" void bswapInstrument(Instrument* swappable) { + swappable->envelope = (AdsrEnvelope*)_byteswap_ulong((u32)swappable->envelope); + bswapSoundFontSound(&swappable->lowNotesSound); + bswapSoundFontSound(&swappable->normalNotesSound); + bswapSoundFontSound(&swappable->highNotesSound); +} + +extern "C" void bswapSoundFontSample(SoundFontSample* swappable) { + u32 origBitfield = _byteswap_ulong(swappable->asU32); + + swappable->codec = (origBitfield >> 28) & 0x0F; + swappable->medium = (origBitfield >> 24) & 0x03; + swappable->unk_bit26 = (origBitfield >> 22) & 0x01; + swappable->unk_bit25 = (origBitfield >> 21) & 0x01; + swappable->size = (origBitfield) & 0x00FFFFFF; + + swappable->sampleAddr = (u8*)_byteswap_ulong((u32)swappable->sampleAddr); + swappable->loop = (AdpcmLoop*)_byteswap_ulong((u32)swappable->loop); + swappable->book = (AdpcmBook*)_byteswap_ulong((u32)swappable->book); +} + +extern "C" void bswapAdpcmLoop(AdpcmLoop* swappable) { + swappable->start = (u32)_byteswap_ulong((u32)swappable->start); + swappable->end = (u32)_byteswap_ulong((u32)swappable->end); + swappable->count = (u32)_byteswap_ulong((u32)swappable->count); + + if (swappable->count != 0) { + for (int i = 0; i < 16; i++) { + swappable->state[i] = (s16)_byteswap_ushort(swappable->state[i]); + } + } +} + +extern "C" void bswapAdpcmBook(AdpcmBook* swappable) { + swappable->order = (u32)_byteswap_ulong((u32)swappable->order); + swappable->npredictors = (u32)_byteswap_ulong((u32)swappable->npredictors); + + for (int i = 0; i < swappable->npredictors * swappable->order * sizeof(s16) * 4; i++) + swappable->book[i] = (s16)_byteswap_ushort(swappable->book[i]); +} + +extern "C" bool AudioPlayer_Init(void) { + if (OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer() != nullptr) { + return OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer()->Init(); + } + + return false; +} + +extern "C" int AudioPlayer_Buffered(void) { + if (OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer() != nullptr) { + return OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer()->Buffered(); + } +} + +extern "C" int AudioPlayer_GetDesiredBuffered(void) { + if (OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer() != nullptr) { + return OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer()->GetDesiredBuffered(); + } +} + +extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len) { + if (OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer() != nullptr) { + OTRGlobals::Instance->context->GetWindow()->GetAudioPlayer()->Play(buf, len); + } +} diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h new file mode 100644 index 000000000..f7e525834 --- /dev/null +++ b/soh/soh/OTRGlobals.h @@ -0,0 +1,66 @@ +#pragma once + +#include "GlobalCtx2.h" + +#ifdef __cplusplus +class OTRGlobals +{ +public: + static OTRGlobals* Instance; + + std::shared_ptr context; + + OTRGlobals(); + ~OTRGlobals(); + +private: + +}; +#endif + +#ifndef __cplusplus +void InitOTR(); +void Graph_ProcessFrame(void (*run_one_game_iter)(void)); +void Graph_ProcessGfxCommands(Gfx* commands); +void OTRLogString(const char* src); +void OTRGfxPrint(const char* str, void* printer, void (*printImpl)(void*, char)); +void OTRSetFrameDivisor(int divisor); +uint16_t OTRGetPixelDepth(float x, float y); +int32_t OTRGetLastScancode(); +void ResourceMgr_CacheDirectory(const char* resName); +void ResourceMgr_LoadFile(const char* resName); +char* ResourceMgr_LoadFileFromDisk(const char* filePath); +char* ResourceMgr_LoadTexByName(char* texPath); +char* ResourceMgr_LoadTexOrDListByName(char* filePath); +char* ResourceMgr_LoadPlayerAnimByName(char* animPath); +char* ResourceMgr_GetNameByCRC(uint64_t crc, char* alloc); +Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc); +Gfx* ResourceMgr_LoadGfxByName(char* path); +Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc); +Vtx* ResourceMgr_LoadVtxByName(char* path); +CollisionHeader* ResourceMgr_LoadColByName(char* path); +uint64_t GetPerfCounter(); +struct SkeletonHeader* ResourceMgr_LoadSkeletonByName(char* path); +int ResourceMgr_OTRSigCheck(char* imgData); +uint64_t osGetTime(void); +uint32_t osGetCount(void); +uint32_t OTRGetCurrentWidth(void); +uint32_t OTRGetCurrentHeight(void); +float OTRGetAspectRatio(void); +float OTRGetDimensionFromLeftEdge(float v); +float OTRGetDimensionFromRightEdge(float v); +int16_t OTRGetRectDimensionFromLeftEdge(float v); +int16_t OTRGetRectDimensionFromRightEdge(float v); +void bswapDrum(Drum* swappable); +void bswapInstrument(Instrument* swappable); +bool bswapSoundFontSound(SoundFontSound* swappable); +void bswapSoundFontSample(SoundFontSample* swappable); +void bswapAdpcmLoop(AdpcmLoop* swappable); +void bswapAdpcmBook(AdpcmBook* swappable); +char* ResourceMgr_LoadFileRaw(const char* resName); +bool AudioPlayer_Init(void); +int AudioPlayer_Buffered(void); +int AudioPlayer_GetDesiredBuffered(void); +void AudioPlayer_Play(const uint8_t* buf, uint32_t len); +void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples); +#endif diff --git a/soh/soh/OTRInit.h b/soh/soh/OTRInit.h new file mode 100644 index 000000000..7b9637ef9 --- /dev/null +++ b/soh/soh/OTRInit.h @@ -0,0 +1 @@ +#pragma once \ No newline at end of file diff --git a/soh/soh/gu_pc.c b/soh/soh/gu_pc.c new file mode 100644 index 000000000..7bdcded98 --- /dev/null +++ b/soh/soh/gu_pc.c @@ -0,0 +1,87 @@ +#include "z64.h" + +void guMtxF2L(float mf[4][4], Mtx* m) { + unsigned int r, c; + s32 tmp1; + s32 tmp2; + s32* m1 = &m->m[0][0]; + s32* m2 = &m->m[2][0]; + for (r = 0; r < 4; r++) { + for (c = 0; c < 2; c++) { + tmp1 = mf[r][2 * c] * 65536.0f; + tmp2 = mf[r][2 * c + 1] * 65536.0f; + *m1++ = (tmp1 & 0xffff0000) | ((tmp2 >> 0x10) & 0xffff); + *m2++ = ((tmp1 << 0x10) & 0xffff0000) | (tmp2 & 0xffff); + } + } +} + +void guMtxL2F(float mf[4][4], Mtx* m) { + unsigned int r, c; + u32 tmp1; + u32 tmp2; + u32* m1; + u32* m2; + s32 stmp1, stmp2; + m1 = (u32*)&m->m[0][0]; + m2 = (u32*)&m->m[2][0]; + for (r = 0; r < 4; r++) { + for (c = 0; c < 2; c++) { + tmp1 = (*m1 & 0xffff0000) | ((*m2 >> 0x10) & 0xffff); + tmp2 = ((*m1++ << 0x10) & 0xffff0000) | (*m2++ & 0xffff); + stmp1 = *(s32*)&tmp1; + stmp2 = *(s32*)&tmp2; + mf[r][c * 2 + 0] = stmp1 / 65536.0f; + mf[r][c * 2 + 1] = stmp2 / 65536.0f; + } + } +} + +void guMtxIdentF(f32 mf[4][4]) { + unsigned int r, c; + for (r = 0; r < 4; r++) { + for (c = 0; c < 4; c++) { + if (r == c) { + mf[r][c] = 1.0f; + } else { + mf[r][c] = 0.0f; + } + } + } +} + +void guMtxIdent(Mtx* m) { + guMtxIdentF(m->m); +} + +void guTranslateF(float m[4][4], float x, float y, float z) { + guMtxIdentF(m); + m[3][0] = x; + m[3][1] = y; + m[3][2] = z; +} +void guTranslate(Mtx* m, float x, float y, float z) { + float mf[4][4]; + guTranslateF(mf, x, y, z); + guMtxF2L(mf, m); +} + +void guScaleF(float mf[4][4], float x, float y, float z) { + guMtxIdentF(mf); + mf[0][0] = x; + mf[1][1] = y; + mf[2][2] = z; + mf[3][3] = 1.0; +} +void guScale(Mtx* m, float x, float y, float z) { + float mf[4][4]; + guScaleF(mf, x, y, z); + guMtxF2L(mf, m); +} + +void guNormalize(f32* x, f32* y, f32* z) { + f32 tmp = 1.0f / sqrtf(*x * *x + *y * *y + *z * *z); + *x = *x * tmp; + *y = *y * tmp; + *z = *z * tmp; +} \ No newline at end of file diff --git a/soh/soh/stubs.c b/soh/soh/stubs.c new file mode 100644 index 000000000..871d5ca22 --- /dev/null +++ b/soh/soh/stubs.c @@ -0,0 +1,2102 @@ +#include "ultra64.h" +#include +#include +#include "z64.h" +#include "OTRGlobals.h" +//#include + +u32 osResetType; +u32 osTvType = OS_TV_NTSC; +//u32 osTvType = OS_TV_PAL; +OSViMode osViModeNtscLan1; +OSViMode osViModeMpalLan1; +OSViMode osViModeFpalLan1; +OSViMode osViModePalLan1; +AudioContext gAudioContext; +unk_D_8016E750 D_8016E750[4]; +u8 gLetterTLUT[4][32]; +u8 gFontFF[999]; +DmaEntry gDmaDataTable[0x60C]; +u8 D_80133418; +u16 gAudioSEFlagSwapSource[64]; +u16 gAudioSEFlagSwapTarget[64]; +u8 gAudioSEFlagSwapMode[64]; + +u8 osAppNmiBuffer[2048]; + +f32 qNaN0x10000 = 0x7F810000; + +//void gSPTextureRectangle(Gfx* pkt, s32 xl, s32 yl, s32 xh, s32 yh, u32 tile, u32 s, s32 t, u32 dsdx, u32 dtdy) +//{ +// __gSPTextureRectangle(pkt, xl, yl, xh, yh, tile, s, t, dsdx, dtdy); +//} + +void osCreateMesgQueue(OSMesgQueue* mq, OSMesg* msgBuf, s32 count) { + mq->validCount = 0; + mq->first = 0; + mq->msgCount = count; + mq->msg = msgBuf; + return; +} + +s32 osSendMesg(OSMesgQueue* mq, OSMesg msg, s32 flag) { + s32 index; + if (mq->validCount >= mq->msgCount) { + return -1; + } + index = (mq->first + mq->validCount) % mq->msgCount; + mq->msg[index] = msg; + mq->validCount++; + return 0; +} +s32 osRecvMesg(OSMesgQueue* mq, OSMesg* msg, s32 flag) { + if (mq->validCount == 0) { + return -1; + } + if (msg != NULL) { + *msg = *(mq->first + mq->msg); + } + mq->first = (mq->first + 1) % mq->msgCount; + mq->validCount--; + return 0; +} + +s32 osJamMesg(OSMesgQueue* mq, OSMesg msg, s32 flag) +{ +} + +void osSetEventMesg(OSEvent e, OSMesgQueue* mq, OSMesg msg) +{ +} + +void osViSetEvent(OSMesgQueue* mq, OSMesg msg, u32 retraceCount) +{ +} + +OSId osGetThreadId(OSThread* thread) +{ +} + +OSPri osGetThreadPri(OSThread* thread) +{ +} + +void osSetThreadPri(OSThread* thread, OSPri pri) +{ +} + +s32 osSetTimer(OSTimer* timer, OSTime countdown, OSTime interval, OSMesgQueue* mq, OSMesg msg) +{ + +} + +void osCreatePiManager(OSPri pri, OSMesgQueue* cmdQ, OSMesg* cmdBuf, s32 cmdMsgCnt) +{ + +} + +void osCreateViManager(OSPri pri) +{ + +} + +s32 osMotorInit(OSMesgQueue* ctrlrqueue, OSPfs* pfs, s32 channel) +{ + +} + +u32 osAiGetLength(void) +{ + +} + +s32 osPfsFreeBlocks(OSPfs* pfs, s32* leftoverBytes) +{ + +} + +s32 osEPiWriteIo(OSPiHandle* handle, u32 devAddr, u32 data) +{ + +} + +s32 osPfsReadWriteFile(OSPfs* pfs, s32 fileNo, u8 flag, s32 offset, ptrdiff_t size, u8* data) +{ + +} + +s32 osPfsDeleteFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName) +{ + +} + +s32 osPfsFileState(OSPfs* pfs, s32 fileNo, OSPfsState* state) +{ + +} + +s32 osPfsInitPak(OSMesgQueue* mq, OSPfs* pfs, s32 channel) +{ + +} + +s32 __osPfsCheckRamArea(OSPfs* pfs) +{ + +} + +s32 osPfsChecker(OSPfs* pfs) +{ + +} + +s32 osPfsFindFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName, s32* fileNo) +{ + +} + +s32 osPfsAllocateFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName, s32 length, s32* fileNo) +{ + +} + +s32 osAiSetNextBuffer(void* buf, size_t size) +{ + +} + +s32 __osMotorAccess(OSPfs* pfs, u32 vibrate) +{ + +} + +OSIntMask osSetIntMask(OSIntMask a) +{ + return 0; +} + +s32 osAfterPreNMI(void) +{ + return 0; +} + +s32 osProbeRumblePak(OSMesgQueue* ctrlrqueue, OSPfs* pfs, u32 channel) +{ + +} + +s32 osSetRumble(OSPfs* pfs, u32 vibrate) +{ + return 0; +} + +void osCreateThread(OSThread* thread, OSId id, void (*entry)(void*), void* arg, void* sp, OSPri pri) +{ + +} + +void osStartThread(OSThread* thread) +{ + +} + +void osStopThread(OSThread* thread) +{ + +} + +void osDestroyThread(OSThread* thread) +{ + +} + +void osWritebackDCache(void* vaddr, s32 nbytes) +{ + +} + +void osInvalICache(void* vaddr, s32 nbytes) +{ + +} + +s32 osContStartQuery(OSMesgQueue* mq) +{ + +} + +void osContGetQuery(OSContStatus* data) +{ + +} + +void osViSwapBuffer(void* vaddr) +{ + +} + +void* osViGetNextFramebuffer() +{ + +} + +u32 __osGetFpcCsr() +{ + return 0; +} + +void __osSetFpcCsr(u32 a0) +{ + +} + +s32 __osDisableInt(void) +{ + +} + +void __osRestoreInt(s32 a0) +{ + +} + +OSThread* __osGetActiveQueue(void) +{ + +} + +OSThread* __osGetCurrFaultedThread(void) +{ + +} + +void osCartRomInit() +{ + +} + +u32 osMemSize = 1024 * 1024 * 1024; + +void Audio_osInvalDCache(void* buf, s32 size) +{ + +} + +void Audio_osWritebackDCache(void* mem, s32 size) +{ + +} + + +s32 osAiSetFrequency(u32 freq) +{ + +} + +s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction) +{ + +} + +void osInvalDCache(void* vaddr, s32 nbytes) +{ + +} + +void osWritebackDCacheAll(void) +{ + +} + +void Audio_SetBGM(u32 bgmId) +{ + +} + +s32 osContSetCh(u8 ch) +{ + +} + +u32 osDpGetStatus(void) +{ + +} + +void osDpSetStatus(u32 status) +{ + +} + +u32 __osSpGetStatus() +{ + +} + +void __osSpSetStatus(u32 status) +{ + +} + +OSPiHandle* osDriveRomInit() +{ + +} + +void osViSetMode(OSViMode* mode) +{ + +} + +void osViBlack(u8 active) +{ + +} + +void osViSetYScale(f32 scale) +{ + +} + +void osViSetXScale(f32 value) +{ + +} + +void osViSetSpecialFeatures(u32 func) +{ + +} + +void __osInitialize_common(void) +{ + +} + +void __osInitialize_autodetect(void) +{ + +} + +void __osExceptionPreamble() +{ + +} + +void __osCleanupThread(void) +{ + +} + +s32 _Printf(PrintCallback a, void* arg, const char* fmt, va_list ap) { + unsigned char buffer[4096]; + + vsnprintf(buffer, sizeof(buffer), fmt, ap); + a(arg, buffer, strlen(buffer)); +} + +void osSpTaskLoad(OSTask* task) +{ + +} + +void osSpTaskStartGo(OSTask* task) +{ + +} + +void osSetUpMempakWrite(s32 channel, OSPifRam* buf) +{ + +} + +u32 osGetMemSize(void) +{ + return 1024 * 1024 * 1024; +} + +s32 osEPiReadIo(OSPiHandle* handle, u32 devAddr, u32* data) +{ + return 0; +} + +u32* osViGetCurrentFramebuffer(void) +{ + +} + +void osSpTaskYield(void) +{ +} + +s32 osStopTimer(OSTimer* timer) +{ + +} + +OSYieldResult osSpTaskYielded(OSTask* task) +{ + +} + + +void osViExtendVStart(u32 arg0) +{ + +} + + +AudioTable gSoundFontTable = { 0x0026, + 0x0000, + 0x00000000, + { 0 }, + { + { + 0x00000000, + 0x00003AA0, + 0x02, + 0x00, + 0x00FF, + 0x5C04, + 0x0088, + }, + { + 0x00003AA0, + 0x000017B0, + 0x02, + 0x00, + 0x00FF, + 0x3301, + 0x0029, + }, + { + 0x00005250, + 0x00000CE0, + 0x02, + 0x02, + 0x00FF, + 0x1500, + 0x0000, + }, + { + 0x00005F30, + 0x000015D0, + 0x02, + 0x02, + 0x01FF, + 0x1040, + 0x0000, + }, + { + 0x00007500, + 0x00000100, + 0x02, + 0x02, + 0x02FF, + 0x0200, + 0x0000, + }, + { + 0x00007600, + 0x00000B60, + 0x02, + 0x02, + 0x01FF, + 0x0D40, + 0x0000, + }, + { + 0x00008160, + 0x00000520, + 0x02, + 0x02, + 0x01FF, + 0x0F00, + 0x0000, + }, + { + 0x00008680, + 0x00000840, + 0x02, + 0x02, + 0x03FF, + 0x0A40, + 0x0000, + }, + { + 0x00008EC0, + 0x00000B20, + 0x02, + 0x02, + 0x01FF, + 0x0A40, + 0x0000, + }, + { + 0x000099E0, + 0x00000FC0, + 0x02, + 0x02, + 0x01FF, + 0x0D40, + 0x0000, + }, + { + 0x0000A9A0, + 0x00000B00, + 0x02, + 0x02, + 0x01FF, + 0x0E40, + 0x0000, + }, + { + 0x0000B4A0, + 0x00000390, + 0x02, + 0x02, + 0x01FF, + 0x0400, + 0x0000, + }, + { + 0x0000B830, + 0x00000320, + 0x02, + 0x02, + 0x01FF, + 0x0300, + 0x0000, + }, + { + 0x0000BB50, + 0x000006F0, + 0x02, + 0x02, + 0x01FF, + 0x1000, + 0x0000, + }, + { + 0x0000C240, + 0x00000390, + 0x02, + 0x02, + 0x05FF, + 0x0500, + 0x0000, + }, + { + 0x0000C5D0, + 0x00000B40, + 0x02, + 0x02, + 0x01FF, + 0x1000, + 0x0000, + }, + { + 0x0000D110, + 0x000009E0, + 0x02, + 0x02, + 0x06FF, + 0x0A40, + 0x0000, + }, + { + 0x0000DAF0, + 0x00000560, + 0x02, + 0x02, + 0x01FF, + 0x0F00, + 0x0000, + }, + { + 0x0000E050, + 0x00000CC0, + 0x02, + 0x01, + 0x01FF, + 0x0D40, + 0x0000, + }, + { + 0x0000ED10, + 0x000003A0, + 0x02, + 0x02, + 0x01FF, + 0x0400, + 0x0000, + }, + { + 0x0000F0B0, + 0x00000AA0, + 0x02, + 0x02, + 0x01FF, + 0x0A40, + 0x0000, + }, + { + 0x0000FB50, + 0x00000A60, + 0x02, + 0x02, + 0x01FF, + 0x0A40, + 0x0000, + }, + { + 0x000105B0, + 0x00000BF0, + 0x02, + 0x02, + 0x01FF, + 0x0B40, + 0x0000, + }, + { + 0x000111A0, + 0x000001F0, + 0x02, + 0x02, + 0x01FF, + 0x0400, + 0x0000, + }, + { + 0x00011390, + 0x00000860, + 0x02, + 0x02, + 0x01FF, + 0x0900, + 0x0000, + }, + { + 0x00011BF0, + 0x000005B0, + 0x02, + 0x02, + 0x01FF, + 0x0800, + 0x0000, + }, + { + 0x000121A0, + 0x00000250, + 0x02, + 0x02, + 0x01FF, + 0x0F00, + 0x0000, + }, + { + 0x000123F0, + 0x000004E0, + 0x02, + 0x02, + 0x01FF, + 0x0C00, + 0x0000, + }, + { + 0x000128D0, + 0x000004C0, + 0x02, + 0x02, + 0x01FF, + 0x0500, + 0x0000, + }, + { + 0x00012D90, + 0x00000C00, + 0x02, + 0x02, + 0x01FF, + 0x0C40, + 0x0000, + }, + { + 0x00013990, + 0x00000270, + 0x02, + 0x02, + 0x01FF, + 0x0500, + 0x0000, + }, + { + 0x00013C00, + 0x00000640, + 0x02, + 0x02, + 0x01FF, + 0x0800, + 0x0000, + }, + { + 0x00014240, + 0x00001300, + 0x02, + 0x02, + 0x01FF, + 0x1040, + 0x0000, + }, + { + 0x00015540, + 0x000011A0, + 0x02, + 0x02, + 0x01FF, + 0x1040, + 0x0000, + }, + { + 0x000166E0, + 0x00001720, + 0x02, + 0x02, + 0x01FF, + 0x1040, + 0x0000, + }, + { + 0x00017E00, + 0x00000DE0, + 0x02, + 0x01, + 0x01FF, + 0x0F40, + 0x0000, + }, + { + 0x00018BE0, + 0x00000660, + 0x02, + 0x01, + 0x01FF, + 0x0F00, + 0x0000, + }, + { + 0x00019240, + 0x00012B60, + 0x02, + 0x02, + 0x00FF, + 0x0200, + 0x0000, + }, + } + }; + +AudioTable gSequenceTable = { 0x006E, + 0x0000, + 0x00000000, + { 0 }, + { + { + 0x00000000, + 0x00006A90, + 0x02, + 0x00, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00006A90, + 0x00001040, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00007AD0, + 0x00002500, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00009FD0, + 0x00000AB0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000AA80, + 0x00000890, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000B310, + 0x00000980, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000BC90, + 0x00000B80, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000C810, + 0x000007A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000CFB0, + 0x000005C0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000D570, + 0x00000730, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000DCA0, + 0x000006A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000E340, + 0x00000740, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000EA80, + 0x00000900, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000F380, + 0x000006F0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0000FA70, + 0x00000820, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00010290, + 0x000006B0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00010940, + 0x000007C0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00011100, + 0x00000800, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00011900, + 0x000009F0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000122F0, + 0x00000960, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00012C50, + 0x000005F0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00013240, + 0x00000650, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00013890, + 0x00000580, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00013E10, + 0x00000670, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00014480, + 0x00000170, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000145F0, + 0x00001AB0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000160A0, + 0x000010F0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00017190, + 0x00000F30, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000180C0, + 0x00000310, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000183D0, + 0x00000710, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00018AE0, + 0x00001100, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00019BE0, + 0x00000570, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001A150, + 0x000001B0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001A300, + 0x00000630, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001A930, + 0x00000150, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001AA80, + 0x00000560, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001AFE0, + 0x00000230, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001B210, + 0x000004C0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001B6D0, + 0x000009A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001C070, + 0x000028D0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001E940, + 0x000004D0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001EE10, + 0x00000A00, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0001F810, + 0x00001240, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00020A50, + 0x00000450, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00020EA0, + 0x00001720, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000225C0, + 0x000005A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00022B60, + 0x000012E0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00023E40, + 0x000025A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000263E0, + 0x00000A20, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00026E00, + 0x000011A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00027FA0, + 0x00000540, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000284E0, + 0x00000490, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00028970, + 0x00000510, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00028E80, + 0x00000790, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00029610, + 0x00000590, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00029BA0, + 0x000005B0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002A150, + 0x00001310, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002B460, + 0x000001C0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002B620, + 0x00000980, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002BFA0, + 0x00000390, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002C330, + 0x00000F90, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002D2C0, + 0x000003E0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002D6A0, + 0x00000960, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002E000, + 0x00001A00, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0002FA00, + 0x00001A80, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00031480, + 0x00000660, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00031AE0, + 0x00001AD0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000335B0, + 0x000005B0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00033B60, + 0x000000B0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00033C10, + 0x00000140, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00033D50, + 0x00000170, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00033EC0, + 0x00000080, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00033F40, + 0x00000130, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00034070, + 0x000000D0, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00034140, + 0x00000480, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000345C0, + 0x00000410, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000349D0, + 0x00001020, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000359F0, + 0x00000B70, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00036560, + 0x00000E10, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00037370, + 0x000004A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00037810, + 0x000011A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000389B0, + 0x00000210, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00038BC0, + 0x00000A30, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000395F0, + 0x00000300, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000398F0, + 0x00000310, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00039C00, + 0x00000EC0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003AAC0, + 0x00000470, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00000028, + 0x00000000, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003AF30, + 0x000003A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003B2D0, + 0x00000220, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003B4F0, + 0x00000720, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003BC10, + 0x00001130, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003CD40, + 0x00001890, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003E5D0, + 0x00000490, + 0x02, + 0x01, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003EA60, + 0x00000160, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0003EBC0, + 0x00001DC0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00040980, + 0x00000360, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00040CE0, + 0x00000AC0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000417A0, + 0x00000CD0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00042470, + 0x00000580, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000429F0, + 0x00001730, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00044120, + 0x000011B0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x000452D0, + 0x00000640, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00045910, + 0x00002610, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00047F20, + 0x000034A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0004B3C0, + 0x000018B0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0004CC70, + 0x000008A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0004D510, + 0x00001370, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0004E880, + 0x000008A0, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0004F120, + 0x00000960, + 0x02, + 0x02, + 0x0000, + 0x0000, + 0x0000, + }, + } + }; + +AudioTable gSampleBankTable = { 0x0007, + 0x0000, + 0x00000000, + { 0 }, + { + { + 0x00000000, + 0x003EB2A0, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x00000000, + 0x00000000, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x003EB2A0, + 0x00005CD0, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x003F0F70, + 0x0001D0B0, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x0040E020, + 0x0000A5D0, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x004185F0, + 0x0000FAB0, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + { + 0x004280A0, + 0x000292F0, + 0x02, + 0x04, + 0x0000, + 0x0000, + 0x0000, + }, + } + }; + +u8 gSequenceFontTable[0x1C0] = { + 0xDC, + 0x00, + 0xDF, + 0x00, + 0xE1, + 0x00, + 0xE3, + 0x00, + 0xE5, + 0x00, + 0xE7, + 0x00, + 0xE9, + 0x00, + 0xEB, + 0x00, + 0xED, + 0x00, + 0xEF, + 0x00, + 0xF1, + 0x00, + 0xF3, + 0x00, + 0xF5, + 0x00, + 0xF7, + 0x00, + 0xF9, + 0x00, + 0xFB, + 0x00, + 0xFD, + 0x00, + 0xFF, + 0x00, + 0x01, + 0x01, + 0x03, + 0x01, + 0x05, + 0x01, + 0x07, + 0x01, + 0x09, + 0x01, + 0x0B, + 0x01, + 0x0D, + 0x01, + 0x0F, + 0x01, + 0x11, + 0x01, + 0x13, + 0x01, + 0x15, + 0x01, + 0x17, + 0x01, + 0x19, + 0x01, + 0x1B, + 0x01, + 0x1D, + 0x01, + 0x1F, + 0x01, + 0x21, + 0x01, + 0x23, + 0x01, + 0x25, + 0x01, + 0x27, + 0x01, + 0x29, + 0x01, + 0x2B, + 0x01, + 0x2D, + 0x01, + 0x2F, + 0x01, + 0x31, + 0x01, + 0x33, + 0x01, + 0x35, + 0x01, + 0x37, + 0x01, + 0x39, + 0x01, + 0x3B, + 0x01, + 0x3D, + 0x01, + 0x3F, + 0x01, + 0x41, + 0x01, + 0x43, + 0x01, + 0x45, + 0x01, + 0x47, + 0x01, + 0x49, + 0x01, + 0x4B, + 0x01, + 0x4D, + 0x01, + 0x4F, + 0x01, + 0x51, + 0x01, + 0x53, + 0x01, + 0x55, + 0x01, + 0x57, + 0x01, + 0x59, + 0x01, + 0x5B, + 0x01, + 0x5D, + 0x01, + 0x5F, + 0x01, + 0x61, + 0x01, + 0x63, + 0x01, + 0x65, + 0x01, + 0x67, + 0x01, + 0x69, + 0x01, + 0x6B, + 0x01, + 0x6D, + 0x01, + 0x6F, + 0x01, + 0x71, + 0x01, + 0x73, + 0x01, + 0x75, + 0x01, + 0x77, + 0x01, + 0x79, + 0x01, + 0x7B, + 0x01, + 0x7D, + 0x01, + 0x7F, + 0x01, + 0x81, + 0x01, + 0x83, + 0x01, + 0x85, + 0x01, + 0x87, + 0x01, + 0x89, + 0x01, + 0x8B, + 0x01, + 0x8D, + 0x01, + 0x8F, + 0x01, + 0x91, + 0x01, + 0x93, + 0x01, + 0x95, + 0x01, + 0x97, + 0x01, + 0x99, + 0x01, + 0x9B, + 0x01, + 0x9D, + 0x01, + 0x9F, + 0x01, + 0xA1, + 0x01, + 0xA3, + 0x01, + 0xA5, + 0x01, + 0xA7, + 0x01, + 0xA9, + 0x01, + 0xAB, + 0x01, + 0xAD, + 0x01, + 0xAF, + 0x01, + 0xB1, + 0x01, + 0xB3, + 0x01, + 0xB5, + 0x01, + 0xB7, + 0x01, + 0x02, 0x01, 0x00, 0x01, 0x02, 0x01, 0x03, 0x01, + 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, + 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, + 0x03, 0x01, 0x03, 0x01, 0x0B, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, + 0x01, 0x23, 0x01, 0x03, 0x01, 0x23, 0x01, 0x03, 0x01, 0x23, 0x01, 0x12, 0x01, 0x07, 0x01, 0x08, 0x01, 0x09, 0x01, + 0x09, 0x01, 0x0A, 0x01, 0x03, 0x01, 0x0C, 0x01, 0x03, 0x01, 0x1E, 0x01, 0x0D, 0x01, 0x0E, 0x01, 0x03, 0x01, 0x03, + 0x01, 0x12, 0x01, 0x12, 0x01, 0x12, 0x01, 0x12, 0x01, 0x12, 0x01, 0x03, 0x01, 0x23, 0x01, 0x09, 0x01, 0x03, 0x01, + 0x0F, 0x01, 0x09, 0x01, 0x05, 0x01, 0x10, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x01, 0x03, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x03, 0x01, 0x09, 0x01, 0x08, 0x01, 0x13, 0x01, 0x14, 0x01, + 0x09, 0x01, 0x15, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x03, 0x01, 0x16, 0x01, 0x13, 0x01, 0x09, 0x01, 0x17, + 0x01, 0x12, 0x01, 0x24, 0x01, 0x18, 0x01, 0x19, 0x01, 0x13, 0x01, 0x20, 0x01, 0x1B, 0x01, 0x1C, 0x01, 0x1D, 0x01, + 0x03, 0x01, 0x1F, 0x01, 0x20, 0x01, 0x20, 0x01, 0x09, 0x01, 0x21, 0x01, 0x22, 0x01, 0x21, 0x01, 0x09, 0x01, 0x20, + 0x01, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; \ No newline at end of file diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp new file mode 100644 index 000000000..354997e16 --- /dev/null +++ b/soh/soh/z_message_OTR.cpp @@ -0,0 +1,44 @@ +#include "OTRGlobals.h" +#include "ResourceMgr.h" +#include "Scene.h" +#include "message_data_static.h" +#include "Utils/StringHelper.h" +#include "global.h" +#include "vt.h" +#include + +extern "C" MessageTableEntry* sNesMessageEntryTablePtr; +extern "C" MessageTableEntry* sStaffMessageEntryTablePtr; +//extern "C" MessageTableEntry* _message_0xFFFC_nes; + +extern "C" void OTRMessage_Init() +{ + auto file = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource("text/nes_message_data_static/nes_message_data_static")); + + sNesMessageEntryTablePtr = (MessageTableEntry*)malloc(sizeof(MessageTableEntry) * file->messages.size()); + + for (int i = 0; i < file->messages.size(); i++) + { + sNesMessageEntryTablePtr[i].textId = file->messages[i].id; + sNesMessageEntryTablePtr[i].typePos = (file->messages[i].textboxType << 4) | file->messages[i].textboxYPos; + sNesMessageEntryTablePtr[i].segment = file->messages[i].msg.c_str(); + sNesMessageEntryTablePtr[i].msgSize = file->messages[i].msg.size(); + + if (file->messages[i].id == 0xFFFC) + { + _message_0xFFFC_nes = (char*)file->messages[i].msg.c_str(); + } + } + + auto file2 = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource("text/staff_message_data_static/staff_message_data_static")); + + sStaffMessageEntryTablePtr = (MessageTableEntry*)malloc(sizeof(MessageTableEntry) * file2->messages.size()); + + for (int i = 0; i < file2->messages.size(); i++) + { + sStaffMessageEntryTablePtr[i].textId = file2->messages[i].id; + sStaffMessageEntryTablePtr[i].typePos = (file2->messages[i].textboxType << 4) | file2->messages[i].textboxYPos; + sStaffMessageEntryTablePtr[i].segment = file2->messages[i].msg.c_str(); + sStaffMessageEntryTablePtr[i].msgSize = file2->messages[i].msg.size(); + } +} \ No newline at end of file diff --git a/soh/soh/z_play_otr.cpp b/soh/soh/z_play_otr.cpp new file mode 100644 index 000000000..6d6c4bb30 --- /dev/null +++ b/soh/soh/z_play_otr.cpp @@ -0,0 +1,69 @@ +#include "OTRGlobals.h" +#include "ResourceMgr.h" +#include "Scene.h" +#include "Utils/StringHelper.h" +#include "global.h" +#include "vt.h" +#include "Vertex.h" + +extern "C" void Gameplay_InitScene(GlobalContext * globalCtx, s32 spawn); +extern "C" void Gameplay_InitEnvironment(GlobalContext * globalCtx, s16 skyboxId); +void OTRGameplay_InitScene(GlobalContext* globalCtx, s32 spawn); +s32 OTRScene_ExecuteCommands(GlobalContext* globalCtx, Ship::Scene* sceneCmd); + +//Ship::OTRResource* OTRGameplay_LoadFile(GlobalContext* globalCtx, RomFile* file) { +Ship::Resource* OTRGameplay_LoadFile(GlobalContext* globalCtx, const char* fileName) +{ + auto res = OTRGlobals::Instance->context->GetResourceManager()->LoadResource(fileName); + return res.get(); +} + +extern "C" void OTRGameplay_SpawnScene(GlobalContext* globalCtx, s32 sceneNum, s32 spawn) { + SceneTableEntry* scene = &gSceneTable[sceneNum]; + + scene->unk_13 = 0; + globalCtx->loadedScene = scene; + globalCtx->sceneNum = sceneNum; + globalCtx->sceneConfig = scene->config; + + //osSyncPrintf("\nSCENE SIZE %fK\n", (scene->sceneFile.vromEnd - scene->sceneFile.vromStart) / 1024.0f); + + std::string scenePath = StringHelper::Sprintf("scenes\\%s\\%s", scene->sceneFile.fileName, scene->sceneFile.fileName); + + globalCtx->sceneSegment = (Ship::Scene*)OTRGameplay_LoadFile(globalCtx, scenePath.c_str()); + scene->unk_13 = 0; + + //ASSERT(globalCtx->sceneSegment != NULL, "this->sceneSegment != NULL", "../z_play.c", 4960); + //gSegments[2] = VIRTUAL_TO_PHYSICAL(globalCtx->sceneSegment); + + OTRGameplay_InitScene(globalCtx, spawn); + + osSyncPrintf("ROOM SIZE=%fK\n", func_80096FE8(globalCtx, &globalCtx->roomCtx) / 1024.0f); +} + +void OTRGameplay_InitScene(GlobalContext* globalCtx, s32 spawn) { + globalCtx->curSpawn = spawn; + globalCtx->linkActorEntry = nullptr; + globalCtx->unk_11DFC = nullptr; + globalCtx->setupEntranceList = nullptr; + globalCtx->setupExitList = nullptr; + globalCtx->cUpElfMsgs = nullptr; + globalCtx->setupPathList = nullptr; + globalCtx->numSetupActors = 0; + Object_InitBank(globalCtx, &globalCtx->objectCtx); + LightContext_Init(globalCtx, &globalCtx->lightCtx); + TransitionActor_InitContext(&globalCtx->state, &globalCtx->transiActorCtx); + func_80096FD4(globalCtx, &globalCtx->roomCtx.curRoom); + YREG(15) = 0; + gSaveContext.worldMapArea = 0; + OTRScene_ExecuteCommands(globalCtx, globalCtx->sceneSegment); + Gameplay_InitEnvironment(globalCtx, globalCtx->skyboxId); + /* auto data = static_cast(Ship::GlobalCtx2::GetInstance() + ->GetResourceManager() + ->LoadResource("object_link_child\\object_link_childVtx_01FE08") + .get()); + + auto data2 = ResourceMgr_LoadVtxByCRC(0x68d4ea06044e228f);*/ + + volatile int a = 0; +} \ No newline at end of file diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp new file mode 100644 index 000000000..f63df81e4 --- /dev/null +++ b/soh/soh/z_scene_otr.cpp @@ -0,0 +1,932 @@ +#include "OTRGlobals.h" +#include "ResourceMgr.h" +#include "Scene.h" +#include "Utils/StringHelper.h" +#include "global.h" +#include "vt.h" +#include +#include +#include +#include +#include +#include + +extern Ship::Resource* OTRGameplay_LoadFile(GlobalContext* globalCtx, const char* fileName); +extern "C" s32 Object_Spawn(ObjectContext* objectCtx, s16 objectId); +extern "C" RomFile sNaviMsgFiles[]; +s32 OTRScene_ExecuteCommands(GlobalContext* globalCtx, Ship::Scene* scene); + +bool func_80098508(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetStartPositionList* cmdStartPos = (Ship::SetStartPositionList*)cmd; + + ActorEntry* linkSpawnEntry = nullptr; + + if (cmdStartPos->cachedGameData != nullptr) + { + ActorEntry* entries = (ActorEntry*)cmdStartPos->cachedGameData; + linkSpawnEntry = &entries[globalCtx->setupEntranceList[globalCtx->curSpawn].spawn]; + } + else + { + ActorEntry* entries = (ActorEntry*)malloc(sizeof(ActorEntry) * cmdStartPos->entries.size()); + + for (int i = 0; i < cmdStartPos->entries.size(); i++) + { + entries[i].id = cmdStartPos->entries[i].actorNum; + entries[i].params = cmdStartPos->entries[i].initVar; + entries[i].pos.x = cmdStartPos->entries[i].posX; + entries[i].pos.y = cmdStartPos->entries[i].posY; + entries[i].pos.z = cmdStartPos->entries[i].posZ; + entries[i].rot.x = cmdStartPos->entries[i].rotX; + entries[i].rot.y = cmdStartPos->entries[i].rotY; + entries[i].rot.z = cmdStartPos->entries[i].rotZ; + } + + linkSpawnEntry = &entries[globalCtx->setupEntranceList[globalCtx->curSpawn].spawn]; + cmdStartPos->cachedGameData = entries; + } + + ActorEntry* linkEntry = globalCtx->linkActorEntry = linkSpawnEntry; + + s16 linkObjectId; + + globalCtx->linkAgeOnLoad = ((void)0, gSaveContext.linkAge); + + linkObjectId = gLinkObjectIds[((void)0, gSaveContext.linkAge)]; + + //gActorOverlayTable[linkEntry->id].initInfo->objectId = linkObjectId; + Object_Spawn(&globalCtx->objectCtx, linkObjectId); + + return false; +} + +// Scene Command 0x01: Actor List +bool func_800985DC(GlobalContext* globalCtx, Ship::SceneCommand* cmd) { + Ship::SetActorList* cmdActor = (Ship::SetActorList*)cmd; + + globalCtx->numSetupActors = cmdActor->entries.size(); + + if (cmdActor->cachedGameData != nullptr) + globalCtx->setupActorList = (ActorEntry*)cmdActor->cachedGameData; + else + { + ActorEntry* entries = (ActorEntry*)malloc(cmdActor->entries.size() * sizeof(ActorEntry)); + + for (int i = 0; i < cmdActor->entries.size(); i++) + { + entries[i].id = cmdActor->entries[i].actorNum; + entries[i].pos.x = cmdActor->entries[i].posX; + entries[i].pos.y = cmdActor->entries[i].posY; + entries[i].pos.z = cmdActor->entries[i].posZ; + entries[i].rot.x = cmdActor->entries[i].rotX; + entries[i].rot.y = cmdActor->entries[i].rotY; + entries[i].rot.z = cmdActor->entries[i].rotZ; + entries[i].params = cmdActor->entries[i].initVar; + } + + cmdActor->cachedGameData = entries; + globalCtx->setupActorList = entries; + } + + return false; +} + +// Scene Command 0x02: Unused 02 +bool func_80098630(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + // Do we need to implement this? + //globalCtx->unk_11DFC = SEGMENTED_TO_VIRTUAL(cmd->unused02.segment); + + return false; +} + +// Scene Command 0x03: Collision Header +bool func_80098674(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetCollisionHeader* cmdCol = (Ship::SetCollisionHeader*)cmd; + + auto colRes = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(cmdCol->filePath)); + + CollisionHeader* colHeader = nullptr; + + if (colRes->cachedGameAsset != nullptr) + colHeader = (CollisionHeader*)colRes->cachedGameAsset; + else + { + colHeader = (CollisionHeader*)malloc(sizeof(CollisionHeader)); + + colHeader->minBounds.x = colRes->absMinX; + colHeader->minBounds.y = colRes->absMinY; + colHeader->minBounds.z = colRes->absMinZ; + + colHeader->maxBounds.x = colRes->absMaxX; + colHeader->maxBounds.y = colRes->absMaxY; + colHeader->maxBounds.z = colRes->absMaxZ; + + colHeader->vtxList = (Vec3s*)malloc(sizeof(Vec3s) * colRes->vertices.size()); + colHeader->numVertices = colRes->vertices.size(); + + for (int i = 0; i < colRes->vertices.size(); i++) + { + colHeader->vtxList[i].x = colRes->vertices[i].x; + colHeader->vtxList[i].y = colRes->vertices[i].y; + colHeader->vtxList[i].z = colRes->vertices[i].z; + } + + colHeader->polyList = (CollisionPoly*)malloc(sizeof(CollisionPoly) * colRes->polygons.size()); + colHeader->numPolygons = colRes->polygons.size(); + + for (int i = 0; i < colRes->polygons.size(); i++) + { + colHeader->polyList[i].type = colRes->polygons[i].type; + colHeader->polyList[i].flags_vIA = colRes->polygons[i].vtxA; + colHeader->polyList[i].flags_vIB = colRes->polygons[i].vtxB; + colHeader->polyList[i].vIC = colRes->polygons[i].vtxC; + colHeader->polyList[i].normal.x = colRes->polygons[i].a; + colHeader->polyList[i].normal.y = colRes->polygons[i].b; + colHeader->polyList[i].normal.z = colRes->polygons[i].c; + colHeader->polyList[i].dist = colRes->polygons[i].d; + } + + colHeader->surfaceTypeList = (SurfaceType*)malloc(colRes->polygonTypes.size() * sizeof(SurfaceType)); + + for (int i = 0; i < colRes->polygonTypes.size(); i++) + { + colHeader->surfaceTypeList[i].data[0] = colRes->polygonTypes[i] >> 32; + colHeader->surfaceTypeList[i].data[1] = colRes->polygonTypes[i] & 0xFFFFFFFF; + } + + colHeader->cameraDataList = (CamData*)malloc(sizeof(CamData) * colRes->camData->entries.size()); + + for (int i = 0; i < colRes->camData->entries.size(); i++) + { + colHeader->cameraDataList[i].cameraSType = colRes->camData->entries[i]->cameraSType; + colHeader->cameraDataList[i].numCameras = colRes->camData->entries[i]->numData; + + int idx = colRes->camData->entries[i]->cameraPosDataIdx; + + colHeader->cameraDataList[i].camPosData = (Vec3s*)malloc(sizeof(Vec3s) * colRes->camData->entries[i]->numData); + + for (int j = 0; j < colRes->camData->entries[i]->numData; j++) + { + if (colRes->camData->cameraPositionData.size() > 0) + { + colHeader->cameraDataList[i].camPosData[j].x = colRes->camData->cameraPositionData[idx + j]->x; + colHeader->cameraDataList[i].camPosData[j].y = colRes->camData->cameraPositionData[idx + j]->y; + colHeader->cameraDataList[i].camPosData[j].z = colRes->camData->cameraPositionData[idx + j]->z; + } + else + { + colHeader->cameraDataList[i].camPosData->x = 0; + colHeader->cameraDataList[i].camPosData->y = 0; + colHeader->cameraDataList[i].camPosData->z = 0; + } + } + } + + colHeader->numWaterBoxes = colRes->waterBoxes.size(); + colHeader->waterBoxes = (WaterBox*)malloc(sizeof(WaterBox) * colHeader->numWaterBoxes); + + for (int i = 0; i < colHeader->numWaterBoxes; i++) + { + colHeader->waterBoxes[i].xLength = colRes->waterBoxes[i].xLength; + colHeader->waterBoxes[i].ySurface = colRes->waterBoxes[i].ySurface; + colHeader->waterBoxes[i].xMin = colRes->waterBoxes[i].xMin; + colHeader->waterBoxes[i].zMin = colRes->waterBoxes[i].zMin; + colHeader->waterBoxes[i].xLength = colRes->waterBoxes[i].xLength; + colHeader->waterBoxes[i].zLength = colRes->waterBoxes[i].zLength; + colHeader->waterBoxes[i].properties = colRes->waterBoxes[i].properties; + } + + colRes->cachedGameAsset = colHeader; + } + + BgCheck_Allocate(&globalCtx->colCtx, globalCtx, colHeader); + + return false; +} + +// Scene Command 0x04: Room List +bool func_800987A4(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetRoomList* cmdRoomList = (Ship::SetRoomList*)cmd; + + globalCtx->numRooms = cmdRoomList->rooms.size(); + globalCtx->roomList = (RomFile*)malloc(globalCtx->numRooms * sizeof(RomFile)); + + for (int i = 0; i < cmdRoomList->rooms.size(); i++) + { + globalCtx->roomList[i].fileName = (char*)cmdRoomList->rooms[i].name.c_str(); + globalCtx->roomList[i].vromStart = cmdRoomList->rooms[i].vromStart; + globalCtx->roomList[i].vromEnd = cmdRoomList->rooms[i].vromEnd; + } + + return false; +} + +// Scene Command 0x06: Entrance List +bool func_800987F8(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetEntranceList* otrEntrance = (Ship::SetEntranceList*)cmd; + + if (otrEntrance->cachedGameData != nullptr) + globalCtx->setupEntranceList = (EntranceEntry*)otrEntrance->cachedGameData; + else + { + globalCtx->setupEntranceList = (EntranceEntry*)malloc(otrEntrance->entrances.size() * sizeof(EntranceEntry)); + + for (int i = 0; i < otrEntrance->entrances.size(); i++) + { + globalCtx->setupEntranceList[i].room = otrEntrance->entrances[i].roomToLoad; + globalCtx->setupEntranceList[i].spawn = otrEntrance->entrances[i].startPositionIndex; + } + + otrEntrance->cachedGameData = globalCtx->setupEntranceList; + } + + return false; +} + +// Scene Command 0x07: Special Files +bool func_8009883C(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetSpecialObjects* otrSpecial = (Ship::SetSpecialObjects*)cmd; + + if (otrSpecial->globalObject != 0) + globalCtx->objectCtx.subKeepIndex = Object_Spawn(&globalCtx->objectCtx, otrSpecial->globalObject); + + if (otrSpecial->elfMessage != 0) + { + auto res = (Ship::Blob*)OTRGameplay_LoadFile(globalCtx, sNaviMsgFiles[otrSpecial->elfMessage - 1].fileName); + globalCtx->cUpElfMsgs = (ElfMessage*)res->data.data(); + } + + return false; +} + +// Scene Command 0x08: Room Behavior +bool func_80098904(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetRoomBehavior* cmdRoom = (Ship::SetRoomBehavior*)cmd; + + globalCtx->roomCtx.curRoom.unk_03 = cmdRoom->gameplayFlags; + globalCtx->roomCtx.curRoom.unk_02 = cmdRoom->gameplayFlags2 & 0xFF; + globalCtx->roomCtx.curRoom.showInvisActors = (cmdRoom->gameplayFlags2 >> 8) & 1; + globalCtx->msgCtx.disableWarpSongs = (cmdRoom->gameplayFlags2 >> 0xA) & 1; + + return false; +} + +// Scene Command 0x0A: Mesh Header +bool func_80098958(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetMesh* otrMesh = (Ship::SetMesh*)cmd; + + if (otrMesh->cachedGameData != nullptr) + globalCtx->roomCtx.curRoom.mesh = (Mesh*)otrMesh->cachedGameData; + else + { + globalCtx->roomCtx.curRoom.mesh = (Mesh*)malloc(sizeof(Mesh)); + globalCtx->roomCtx.curRoom.mesh->polygon.type = otrMesh->meshHeaderType; + globalCtx->roomCtx.curRoom.mesh->polygon.num = otrMesh->meshes.size(); + + if (otrMesh->meshHeaderType == 2) + globalCtx->roomCtx.curRoom.mesh->polygon.start = malloc(sizeof(PolygonDlist2) * globalCtx->roomCtx.curRoom.mesh->polygon.num); + else + globalCtx->roomCtx.curRoom.mesh->polygon.start = malloc(sizeof(PolygonDlist) * globalCtx->roomCtx.curRoom.mesh->polygon.num); + + for (int i = 0; i < globalCtx->roomCtx.curRoom.mesh->polygon.num; i++) + { + if (otrMesh->meshHeaderType == 2) + { + PolygonDlist2* arr = (PolygonDlist2*)globalCtx->roomCtx.curRoom.mesh->polygon.start; + PolygonDlist2* dlist = &arr[i]; + + if (otrMesh->meshes[i].opa != "") + { + auto opaFile = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].opa)); + + dlist->opaDL = opaFile.get(); + dlist->opa = (Gfx*)&dlist->opaDL->instructions[0]; + } + else + { + dlist->opa = 0; + } + + if (otrMesh->meshes[i].xlu != "") + { + auto xluFile = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].xlu)); + + dlist->xluDL = xluFile.get(); + dlist->xlu = (Gfx*)&dlist->xluDL->instructions[0]; + } + else + dlist->xlu = 0; + + dlist->pos.x = otrMesh->meshes[i].x; + dlist->pos.y = otrMesh->meshes[i].y; + dlist->pos.z = otrMesh->meshes[i].z; + dlist->unk_06 = otrMesh->meshes[i].unk_06; + + //globalCtx->roomCtx.curRoom.mesh->polygon.start = dlist; + } + else if (otrMesh->meshHeaderType == 1) + { + PolygonDlist* pType = (PolygonDlist*)malloc(sizeof(PolygonDlist)); + + if (otrMesh->meshes[0].imgOpa != "") + pType->opa = (Gfx*)&std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[0].imgOpa))->instructions[0]; + else + pType->opa = 0; + + if (otrMesh->meshes[0].imgXlu != "") + pType->xlu = (Gfx*)&std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[0].imgXlu))->instructions[0]; + else + pType->xlu = 0; + + globalCtx->roomCtx.curRoom.mesh->polygon1.dlist = (Gfx*)pType; + + globalCtx->roomCtx.curRoom.mesh->polygon1.format = otrMesh->meshes[0].imgFmt; + + if (otrMesh->meshes[0].imgFmt == 1) + { + globalCtx->roomCtx.curRoom.mesh->polygon1.single.fmt = otrMesh->meshes[0].images[0].fmt; + globalCtx->roomCtx.curRoom.mesh->polygon1.single.source = + (void*)(OTRGlobals::Instance->context->GetResourceManager()->LoadFile( + otrMesh->meshes[0].images[0].sourceBackground)) + .get() + ->buffer.get(); + globalCtx->roomCtx.curRoom.mesh->polygon1.single.siz = otrMesh->meshes[0].images[0].siz; + globalCtx->roomCtx.curRoom.mesh->polygon1.single.width = otrMesh->meshes[0].images[0].width; + globalCtx->roomCtx.curRoom.mesh->polygon1.single.height = otrMesh->meshes[0].images[0].height; + globalCtx->roomCtx.curRoom.mesh->polygon1.single.fmt = otrMesh->meshes[0].images[0].fmt; + globalCtx->roomCtx.curRoom.mesh->polygon1.single.mode0 = otrMesh->meshes[0].images[0].mode0; + globalCtx->roomCtx.curRoom.mesh->polygon1.single.tlutCount = otrMesh->meshes[0].images[0].tlutCount; + } + else + { + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.count = otrMesh->meshes[0].images.size(); + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list = + (BgImage*)calloc(sizeof(BgImage), globalCtx->roomCtx.curRoom.mesh->polygon1.multi.count); + + for (size_t i = 0; i < otrMesh->meshes[0].images.size(); i++) + { + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].fmt = otrMesh->meshes[0].images[i].fmt; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].source = + (void*)(OTRGlobals::Instance->context->GetResourceManager()->LoadFile( + otrMesh->meshes[0].images[i].sourceBackground)) + .get() + ->buffer.get(); + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].siz = otrMesh->meshes[0].images[i].siz; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].width = otrMesh->meshes[0].images[i].width; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].height = + otrMesh->meshes[0].images[i].height; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].fmt = otrMesh->meshes[0].images[i].fmt; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].mode0 = otrMesh->meshes[0].images[i].mode0; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].tlutCount = + otrMesh->meshes[0].images[i].tlutCount; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].unk_00 = + otrMesh->meshes[0].images[i].unk_00; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].unk_0C = + otrMesh->meshes[0].images[i].unk_0C; + globalCtx->roomCtx.curRoom.mesh->polygon1.multi.list[i].id = otrMesh->meshes[0].images[i].id; + } + } + } + else + { + PolygonDlist* arr = (PolygonDlist*)globalCtx->roomCtx.curRoom.mesh->polygon.start; + PolygonDlist* dlist = &arr[i]; + + if (otrMesh->meshes[i].opa != "") + { + auto opaFile = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].opa)); + + dlist->opaDL = opaFile.get(); + dlist->opa = (Gfx*)&dlist->opaDL->instructions[0]; + } + else + dlist->opa = 0; + + if (otrMesh->meshes[i].xlu != "") + { + auto xluFile = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(otrMesh->meshes[i].xlu)); + + dlist->xluDL = xluFile.get(); + dlist->xlu = (Gfx*)&dlist->xluDL->instructions[0]; + } + else + dlist->xlu = 0; + + //globalCtx->roomCtx.curRoom.mesh->polygon.start = dlist; + } + } + + otrMesh->cachedGameData = globalCtx->roomCtx.curRoom.mesh; + } + + return false; +} + +extern "C" void* func_800982FC(ObjectContext * objectCtx, s32 bankIndex, s16 objectId); + +// Scene Command 0x0B: Object List +bool func_8009899C(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetObjectList* cmdObj = (Ship::SetObjectList*)cmd; + + s32 i; + s32 j; + s32 k; + ObjectStatus* status; + ObjectStatus* status2; + ObjectStatus* firstStatus; + //s16* objectEntry = SEGMENTED_TO_VIRTUAL(cmd->objectList.segment); + s16* objectEntry = (s16*)cmdObj->objects.data(); + void* nextPtr; + + k = 0; + //i = globalCtx->objectCtx.unk_09; + firstStatus = &globalCtx->objectCtx.status[0]; + status = &globalCtx->objectCtx.status[i]; + + for (int i = 0; i < cmdObj->objects.size(); i++) { + bool alreadyIncluded = false; + + for (int j = 0; j < globalCtx->objectCtx.num; j++) { + if (globalCtx->objectCtx.status[j].id == cmdObj->objects[i]) { + alreadyIncluded = true; + break; + } + } + + if (!alreadyIncluded) { + globalCtx->objectCtx.status[globalCtx->objectCtx.num++].id = cmdObj->objects[i]; + func_80031A28(globalCtx, &globalCtx->actorCtx); + } + } + + /* + while (i < globalCtx->objectCtx.num) { + if (status->id != *objectEntry) { + status2 = &globalCtx->objectCtx.status[i]; + for (j = i; j < globalCtx->objectCtx.num; j++) { + status2->id = OBJECT_INVALID; + status2++; + } + globalCtx->objectCtx.num = i; + func_80031A28(globalCtx, &globalCtx->actorCtx); + + continue; + } + + i++; + k++; + objectEntry++; + status++; + } + + globalCtx->objectCtx.num = i; + */ + + return false; +} + +// Scene Command 0x0C: Light List +bool func_80098B74(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetLightList* cmdLight = (Ship::SetLightList*)cmd; + + LightInfo* lightInfo = (LightInfo*)malloc(cmdLight->lights.size() * sizeof(LightInfo)); + + for (int i = 0; i < cmdLight->lights.size(); i++) + { + lightInfo[i].type = cmdLight->lights[i].type; + lightInfo[i].params.point.x = cmdLight->lights[i].x; + lightInfo[i].params.point.y = cmdLight->lights[i].y; + lightInfo[i].params.point.z = cmdLight->lights[i].z; + lightInfo[i].params.point.radius = cmdLight->lights[i].radius; + lightInfo[i].params.point.drawGlow = cmdLight->lights[i].drawGlow; + lightInfo[i].params.point.color[0] = cmdLight->lights[i].r; + lightInfo[i].params.point.color[1] = cmdLight->lights[i].g; + lightInfo[i].params.point.color[2] = cmdLight->lights[i].b; + + LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &lightInfo[i]); + } + + return false; +} + +// Scene Command 0x0D: Path +bool func_80098C24(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetPathways* cmdPath = (Ship::SetPathways*)cmd; + + Ship::Path* path = (Ship::Path*)OTRGlobals::Instance->context->GetResourceManager()->LoadResource(cmdPath->paths[0]).get(); + globalCtx->setupPathList = (Path*)malloc(path->paths.size() * sizeof(Path)); + + //for (int i = 0; i < cmdPath->paths.size(); i++) + { + for (int j = 0; j < path->paths.size(); j++) + { + globalCtx->setupPathList[j].count = path->paths[j].size(); + globalCtx->setupPathList[j].points = (Vec3s*)malloc(sizeof(Vec3s) * path->paths[j].size()); + + for (int k = 0; k < path->paths[j].size(); k++) + { + globalCtx->setupPathList[j].points[k].x = path->paths[j][k].x; + globalCtx->setupPathList[j].points[k].y = path->paths[j][k].y; + globalCtx->setupPathList[j].points[k].z = path->paths[j][k].z; + } + } + } + + return false; +} + +// Scene Command 0x0E: Transition Actor List +bool func_80098C68(GlobalContext* globalCtx, Ship::SceneCommand* cmd) { + Ship::SetTransitionActorList* cmdActor = (Ship::SetTransitionActorList*)cmd; + + globalCtx->transiActorCtx.numActors = cmdActor->entries.size(); + globalCtx->transiActorCtx.list = (TransitionActorEntry*)malloc(cmdActor->entries.size() * sizeof(TransitionActorEntry)); + + for (int i = 0; i < cmdActor->entries.size(); i++) + { + globalCtx->transiActorCtx.list[i].sides[0].room = cmdActor->entries[i].frontObjectRoom; + globalCtx->transiActorCtx.list[i].sides[0].effects = cmdActor->entries[i].frontTransitionReaction; + globalCtx->transiActorCtx.list[i].sides[1].room = cmdActor->entries[i].backObjectRoom; + globalCtx->transiActorCtx.list[i].sides[1].effects = cmdActor->entries[i].backTransitionReaction; + globalCtx->transiActorCtx.list[i].id = cmdActor->entries[i].actorNum; + globalCtx->transiActorCtx.list[i].pos.x = cmdActor->entries[i].posX; + globalCtx->transiActorCtx.list[i].pos.y = cmdActor->entries[i].posY; + globalCtx->transiActorCtx.list[i].pos.z = cmdActor->entries[i].posZ; + globalCtx->transiActorCtx.list[i].rotY = cmdActor->entries[i].rotY; + globalCtx->transiActorCtx.list[i].params = cmdActor->entries[i].initVar; + } + + return false; +} + +//void TransitionActor_InitContext(GameState* state, TransitionActorContext* transiActorCtx) { +// transiActorCtx->numActors = 0; +//} + +// Scene Command 0x0F: Light Setting List +bool func_80098CC8(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetLightingSettings* otrLight = (Ship::SetLightingSettings*)cmd; + + globalCtx->envCtx.numLightSettings = otrLight->settings.size(); + globalCtx->envCtx.lightSettingsList = (EnvLightSettings*)malloc(globalCtx->envCtx.numLightSettings * sizeof(EnvLightSettings)); + + for (int i = 0; i < otrLight->settings.size(); i++) + { + globalCtx->envCtx.lightSettingsList[i].ambientColor[0] = otrLight->settings[i].ambientClrR; + globalCtx->envCtx.lightSettingsList[i].ambientColor[1] = otrLight->settings[i].ambientClrG; + globalCtx->envCtx.lightSettingsList[i].ambientColor[2] = otrLight->settings[i].ambientClrB; + + globalCtx->envCtx.lightSettingsList[i].light1Color[0] = otrLight->settings[i].diffuseClrA_R; + globalCtx->envCtx.lightSettingsList[i].light1Color[1] = otrLight->settings[i].diffuseClrA_G; + globalCtx->envCtx.lightSettingsList[i].light1Color[2] = otrLight->settings[i].diffuseClrA_B; + + globalCtx->envCtx.lightSettingsList[i].light1Dir[0] = otrLight->settings[i].diffuseDirA_X; + globalCtx->envCtx.lightSettingsList[i].light1Dir[1] = otrLight->settings[i].diffuseDirA_Y; + globalCtx->envCtx.lightSettingsList[i].light1Dir[2] = otrLight->settings[i].diffuseDirA_Z; + + globalCtx->envCtx.lightSettingsList[i].light2Color[0] = otrLight->settings[i].diffuseClrB_R; + globalCtx->envCtx.lightSettingsList[i].light2Color[1] = otrLight->settings[i].diffuseClrB_G; + globalCtx->envCtx.lightSettingsList[i].light2Color[2] = otrLight->settings[i].diffuseClrB_B; + + globalCtx->envCtx.lightSettingsList[i].light2Dir[0] = otrLight->settings[i].diffuseDirB_X; + globalCtx->envCtx.lightSettingsList[i].light2Dir[1] = otrLight->settings[i].diffuseDirB_Y; + globalCtx->envCtx.lightSettingsList[i].light2Dir[2] = otrLight->settings[i].diffuseDirB_Z; + + globalCtx->envCtx.lightSettingsList[i].fogColor[0] = otrLight->settings[i].fogClrR; + globalCtx->envCtx.lightSettingsList[i].fogColor[1] = otrLight->settings[i].fogClrG; + globalCtx->envCtx.lightSettingsList[i].fogColor[2] = otrLight->settings[i].fogClrB; + + globalCtx->envCtx.lightSettingsList[i].fogNear = otrLight->settings[i].fogNear; + globalCtx->envCtx.lightSettingsList[i].fogFar = otrLight->settings[i].fogFar; + } + + return false; +} + +// Scene Command 0x11: Skybox Settings +bool func_80098D1C(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetSkyboxSettings* cmdSky = (Ship::SetSkyboxSettings*)cmd; + + globalCtx->skyboxId = cmdSky->skyboxNumber; + globalCtx->envCtx.unk_17 = globalCtx->envCtx.unk_18 = cmdSky->cloudsType; + globalCtx->envCtx.indoors = cmdSky->isIndoors; + + return false; +} + +// Scene Command 0x12: Skybox Disables +bool func_80098D5C(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetSkyboxModifier* cmdSky = (Ship::SetSkyboxModifier*)cmd; + + globalCtx->envCtx.sunMoonDisabled = cmdSky->disableSunMoon; + globalCtx->envCtx.skyboxDisabled = cmdSky->disableSky; + + return false; +} + +// Scene Command 0x10: Time Settings +bool func_80098D80(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetTimeSettings* cmdTime = (Ship::SetTimeSettings*)cmd; + + if ((cmdTime->hour != 0xFF) && (cmdTime->min != 0xFF)) { + gSaveContext.skyboxTime = gSaveContext.dayTime = + ((cmdTime->hour + (cmdTime->min / 60.0f)) * 60.0f) / ((f32)(24 * 60) / 0x10000); + } + + if (cmdTime->unk != 0xFF) { + globalCtx->envCtx.timeIncrement = cmdTime->unk; + } + else { + globalCtx->envCtx.timeIncrement = 0; + } + + if (gSaveContext.sunsSongState == SUNSSONG_INACTIVE) { + gTimeIncrement = globalCtx->envCtx.timeIncrement; + } + + globalCtx->envCtx.sunPos.x = -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + globalCtx->envCtx.sunPos.y = (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + globalCtx->envCtx.sunPos.z = (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f) * 25.0f; + + if (((globalCtx->envCtx.timeIncrement == 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) || + (gSaveContext.entranceIndex == 0x0604)) { + gSaveContext.skyboxTime = ((void)0, gSaveContext.dayTime); + if ((gSaveContext.skyboxTime >= 0x2AAC) && (gSaveContext.skyboxTime < 0x4555)) { + gSaveContext.skyboxTime = 0x3556; + } + else if ((gSaveContext.skyboxTime >= 0x4555) && (gSaveContext.skyboxTime < 0x5556)) { + gSaveContext.skyboxTime = 0x5556; + } + else if ((gSaveContext.skyboxTime >= 0xAAAB) && (gSaveContext.skyboxTime < 0xB556)) { + gSaveContext.skyboxTime = 0xB556; + } + else if ((gSaveContext.skyboxTime >= 0xC001) && (gSaveContext.skyboxTime < 0xCAAC)) { + gSaveContext.skyboxTime = 0xCAAC; + } + } + + return false; +} + +// Scene Command 0x05: Wind Settings +bool func_80099090(GlobalContext* globalCtx, Ship::SceneCommand* cmd) { + Ship::SetWind* cmdWind = (Ship::SetWind*)cmd; + + s8 x = cmdWind->windWest; + s8 y = cmdWind->windVertical; + s8 z = cmdWind->windSouth; + + globalCtx->envCtx.windDirection.x = x; + globalCtx->envCtx.windDirection.y = y; + globalCtx->envCtx.windDirection.z = z; + + globalCtx->envCtx.windSpeed = cmdWind->clothFlappingStrength; + + return false; +} + +// Scene Command 0x13: Exit List +bool func_800990F0(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::ExitList* cmdExit = (Ship::ExitList*)cmd; + + globalCtx->setupExitList = (int16_t*)malloc(cmdExit->exits.size() * sizeof(int16_t)); + + for (int i = 0; i < cmdExit->exits.size(); i++) + globalCtx->setupExitList[i] = cmdExit->exits[i]; + + return false; +} + +// Scene Command 0x09: Undefined +bool func_80099134(GlobalContext* globalCtx, Ship::SceneCommand* cmd) { + return false; +} + +// Scene Command 0x15: Sound Settings +bool func_80099140(GlobalContext* globalCtx, Ship::SceneCommand* cmd) { + Ship::SetSoundSettings* cmdSnd = (Ship::SetSoundSettings*)cmd; + + globalCtx->sequenceCtx.seqId = cmdSnd->musicSequence; + globalCtx->sequenceCtx.natureAmbienceId = cmdSnd->nightTimeSFX; + + if (gSaveContext.seqId == 0xFF) { + Audio_QueueSeqCmd(cmdSnd->reverb | 0xF0000000); + } + + return false; +} + +// Scene Command 0x16: Echo Setting +bool func_8009918C(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetEchoSettings* cmdEcho = (Ship::SetEchoSettings*)cmd; + + globalCtx->roomCtx.curRoom.echo = cmdEcho->echo; + + return false; +} + +// Scene Command 0x18: Alternate Headers +bool func_800991A0(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetAlternateHeaders* cmdHeaders = (Ship::SetAlternateHeaders*)cmd; + + //s32 pad; + //SceneCmd* altHeader; + + //osSyncPrintf("\n[ZU]sceneset age =[%X]", ((void)0, gSaveContext.linkAge)); + //osSyncPrintf("\n[ZU]sceneset time =[%X]", ((void)0, gSaveContext.cutsceneIndex)); + //osSyncPrintf("\n[ZU]sceneset counter=[%X]", ((void)0, gSaveContext.sceneSetupIndex)); + + if (gSaveContext.sceneSetupIndex != 0) + { + std::string desiredHeader = cmdHeaders->headers[gSaveContext.sceneSetupIndex - 1]; + Ship::Scene* headerData = nullptr; + if (desiredHeader != "") { + headerData = + (Ship::Scene*)OTRGlobals::Instance->context->GetResourceManager()->LoadResource(desiredHeader).get(); + } + + if (headerData != nullptr) + { + OTRScene_ExecuteCommands(globalCtx, headerData); + return true; + } + else + { + // "Coughh! There is no specified dataaaaa!" + osSyncPrintf("\nげぼはっ! 指定されたデータがないでええっす!"); + + if (gSaveContext.sceneSetupIndex == 3) + { + std::string desiredHeader = cmdHeaders->headers[gSaveContext.sceneSetupIndex - 2]; + Ship::Scene* headerData = nullptr; + if (desiredHeader != "") { + headerData = (Ship::Scene*)OTRGlobals::Instance->context->GetResourceManager() + ->LoadResource(desiredHeader) + .get(); + } + + // "Using adult day data there!" + osSyncPrintf("\nそこで、大人の昼データを使用するでええっす!!"); + + if (headerData != nullptr) + { + OTRScene_ExecuteCommands(globalCtx, headerData); + return true; + } + } + } + } + return false; +} + +// Scene Command 0x17: Cutscene Data +bool func_8009934C(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetCutscenes* cmdCS = (Ship::SetCutscenes*)cmd; + + Ship::Cutscene* csData = (Ship::Cutscene*)OTRGlobals::Instance->context->GetResourceManager()->LoadResource(cmdCS->cutscenePath).get(); + globalCtx->csCtx.segment = csData->commands.data(); + + //osSyncPrintf("\ngame_play->demo_play.data=[%x]", globalCtx->csCtx.segment); + return false; +} + +// Scene Command 0x19: Misc. Settings (Camera & World Map Area) +bool func_800993C0(GlobalContext* globalCtx, Ship::SceneCommand* cmd) +{ + Ship::SetCameraSettings* cmdCam = (Ship::SetCameraSettings*)cmd; + + YREG(15) = cmdCam->cameraMovement; + gSaveContext.worldMapArea = cmdCam->mapHighlights; + + if ((globalCtx->sceneNum == SCENE_SHOP1) || (globalCtx->sceneNum == SCENE_SYATEKIJYOU)) { + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + gSaveContext.worldMapArea = 1; + } + } + + if (((globalCtx->sceneNum >= SCENE_SPOT00) && (globalCtx->sceneNum <= SCENE_GANON_TOU)) || + ((globalCtx->sceneNum >= SCENE_ENTRA) && (globalCtx->sceneNum <= SCENE_SHRINE_R))) { + if (gSaveContext.cutsceneIndex < 0xFFF0) { + gSaveContext.worldMapAreaData |= gBitFlags[gSaveContext.worldMapArea]; + osSyncPrintf("000 area_arrival=%x (%d)\n", gSaveContext.worldMapAreaData, + gSaveContext.worldMapArea); + } + } + return false; +} + +bool (*sceneCommands[])(GlobalContext*, Ship::SceneCommand*) = +{ + func_80098508, func_800985DC, func_80098630, func_80098674, func_800987A4, func_80099090, func_800987F8, + func_8009883C, func_80098904, func_80099134, func_80098958, func_8009899C, func_80098B74, func_80098C24, + func_80098C68, func_80098CC8, func_80098D80, func_80098D1C, func_80098D5C, func_800990F0, 0, + func_80099140, func_8009918C, func_8009934C, func_800991A0, func_800993C0, +}; + +s32 OTRScene_ExecuteCommands(GlobalContext* globalCtx, Ship::Scene* scene) +{ + Ship::SceneCommandID cmdCode; + + for (int i = 0; i < scene->commands.size(); i++) + { + auto sceneCmd = scene->commands[i]; + + if (sceneCmd == nullptr) // UH OH + continue; + + + cmdCode = sceneCmd->cmdID; + //osSyncPrintf("*** Scene_Word = { code=%d, data1=%02x, data2=%04x } ***\n", cmdCode, sceneCmd->base.data1, sceneCmd->base.data2); + + if ((int)cmdCode == 0x14) { + break; + } + + if ((int)cmdCode <= 0x19) { + if (sceneCommands[(int)cmdCode](globalCtx, sceneCmd)) + break; + } + else { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("code の値が異常です\n"); // "code variable is abnormal" + osSyncPrintf(VT_RST); + } + + //sceneCmd++; + } + return 0; +} + +extern "C" s32 OTRfunc_800973FC(GlobalContext* globalCtx, RoomContext* roomCtx) { + if (roomCtx->status == 1) { + //if (!osRecvMesg(&roomCtx->loadQueue, NULL, OS_MESG_NOBLOCK)) { + if (1) + { + roomCtx->status = 0; + roomCtx->curRoom.segment = roomCtx->unk_34; + gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->unk_34); + + OTRScene_ExecuteCommands(globalCtx, roomCtx->roomToLoad); + Player_SetBootData(globalCtx, GET_PLAYER(globalCtx)); + Actor_SpawnTransitionActors(globalCtx, &globalCtx->actorCtx); + + return 1; + } + + return 0; + } + + return 1; +} + +extern "C" s32 OTRfunc_8009728C(GlobalContext* globalCtx, RoomContext* roomCtx, s32 roomNum) { + u32 size; + + if (roomCtx->status == 0) { + roomCtx->prevRoom = roomCtx->curRoom; + roomCtx->curRoom.num = roomNum; + roomCtx->curRoom.segment = NULL; + roomCtx->status = 1; + + ASSERT(roomNum < globalCtx->numRooms, "read_room_ID < game_play->room_rom_address.num", "../z_room.c", 1009); + + if (roomNum >= globalCtx->numRooms) + return 0; // UH OH + + size = globalCtx->roomList[roomNum].vromEnd - globalCtx->roomList[roomNum].vromStart; + roomCtx->unk_34 = (void*)ALIGN16((u32)roomCtx->bufPtrs[roomCtx->unk_30] - ((size + 8) * roomCtx->unk_30 + 7)); + + osCreateMesgQueue(&roomCtx->loadQueue, &roomCtx->loadMsg, 1); + //DmaMgr_SendRequest2(&roomCtx->dmaRequest, roomCtx->unk_34, globalCtx->roomList[roomNum].vromStart, size, 0, + //&roomCtx->loadQueue, NULL, "../z_room.c", 1036); + + auto roomData = OTRGlobals::Instance->context->GetResourceManager()->LoadResource(globalCtx->roomList[roomNum].fileName); + roomCtx->status = 1; + roomCtx->roomToLoad = (Ship::Scene*)roomData.get(); + + roomCtx->unk_30 ^= 1; + + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/soh/src/boot/assert.c b/soh/src/boot/assert.c new file mode 100644 index 000000000..b7895dddd --- /dev/null +++ b/soh/src/boot/assert.c @@ -0,0 +1,9 @@ +#include "global.h" + +void __assert(const char* exp, const char* file, s32 line) { + char msg[256]; + + osSyncPrintf("Assertion failed: %s, file %s, line %d, thread %d\n", exp, file, line, osGetThreadId(NULL)); + sprintf(msg, "ASSERT: %s:%d(%d)", file, line, osGetThreadId(NULL)); + Fault_AddHungupAndCrashImpl(msg, exp); +} diff --git a/soh/src/boot/boot_main.c b/soh/src/boot/boot_main.c new file mode 100644 index 000000000..7b8b88fed --- /dev/null +++ b/soh/src/boot/boot_main.c @@ -0,0 +1,30 @@ +#include "global.h" + +StackEntry sBootThreadInfo; +OSThread sIdleThread; +u8 sIdleThreadStack[0x400]; +StackEntry sIdleThreadInfo; +u8 sBootThreadStack[0x400]; + +void cleararena(void) { + //bzero(_dmadataSegmentStart, osMemSize - OS_K0_TO_PHYSICAL(_dmadataSegmentStart)); +} + +void bootproc(void) { + StackCheck_Init(&sBootThreadInfo, sBootThreadStack, sBootThreadStack + sizeof(sBootThreadStack), 0, -1, "boot"); + + osMemSize = osGetMemSize(); + cleararena(); + __osInitialize_common(); + __osInitialize_autodetect(); + + gCartHandle = osCartRomInit(); + osDriveRomInit(); + isPrintfInit(); + Locale_Init(); + + StackCheck_Init(&sIdleThreadInfo, sIdleThreadStack, sIdleThreadStack + sizeof(sIdleThreadStack), 0, 256, "idle"); + osCreateThread(&sIdleThread, 1, Idle_ThreadEntry, NULL, sIdleThreadStack + sizeof(sIdleThreadStack), + Z_PRIORITY_MAIN); + osStartThread(&sIdleThread); +} diff --git a/soh/src/boot/build.c b/soh/src/boot/build.c new file mode 100644 index 000000000..f3f9bdae6 --- /dev/null +++ b/soh/src/boot/build.c @@ -0,0 +1,8 @@ +const char gBuildVersion[] = "DECKARD ALFA (1.0.0)"; +const char gBuildTeam[] = "github.com/harbourmasters"; +#ifdef __TIMESTAMP__ +const char gBuildDate[] = __TIMESTAMP__; +#else +const char gBuildDate[] = __DATE__ " " __TIME__; +#endif +const char gBuildMakeOption[] = ""; diff --git a/soh/src/boot/idle.c b/soh/src/boot/idle.c new file mode 100644 index 000000000..9799229e4 --- /dev/null +++ b/soh/src/boot/idle.c @@ -0,0 +1,90 @@ +#include "global.h" +#include "vt.h" + +OSThread gMainThread; +u8 sMainStack[0x900]; +StackEntry sMainStackInfo; +OSMesg sPiMgrCmdBuff[50]; +OSMesgQueue gPiMgrCmdQ; +OSViMode gViConfigMode; +u8 D_80013960; + +s8 D_80009430 = 1; +vu8 gViConfigUseDefault = 1; +u8 gViConfigAdditionalScanLines = 0; +u32 gViConfigFeatures = OS_VI_DITHER_FILTER_ON | OS_VI_GAMMA_OFF; +f32 gViConfigXScale = 1.0; +f32 gViConfigYScale = 1.0; + +void Main_ThreadEntry(void* arg) { + OSTime time; + + osSyncPrintf("mainx 実行開始\n"); + DmaMgr_Init(); + osSyncPrintf("codeセグメントロード中..."); + time = osGetTime(); + //DmaMgr_SendRequest1(_codeSegmentStart, (uintptr_t)_codeSegmentRomStart, _codeSegmentRomEnd - _codeSegmentRomStart, + //"../idle.c", 238); + time -= osGetTime(); + osSyncPrintf("\rcodeセグメントロード中...完了\n"); + osSyncPrintf("転送時間 %6.3f\n"); + //bzero(_codeSegmentBssStart, _codeSegmentBssEnd - _codeSegmentBssStart); + osSyncPrintf("codeセグメントBSSクリア完了\n"); + Main(arg); + osSyncPrintf("mainx 実行終了\n"); +} + +void Idle_ThreadEntry(void* arg) { + osSyncPrintf("アイドルスレッド(idleproc)実行開始\n"); + osSyncPrintf("作製者 : %s\n", gBuildTeam); + osSyncPrintf("作成日時 : %s\n", gBuildDate); + osSyncPrintf("MAKEOPTION: %s\n", gBuildMakeOption); + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("RAMサイズは %d キロバイトです(osMemSize/osGetMemSize)\n", (s32)osMemSize / 1024); + //osSyncPrintf("_bootSegmentEnd(%08x) 以降のRAM領域はクリアされました(boot)\n", _bootSegmentEnd); + osSyncPrintf("Zバッファのサイズは %d キロバイトです\n", 0x96); + osSyncPrintf("ダイナミックバッファのサイズは %d キロバイトです\n", 0x92); + osSyncPrintf("FIFOバッファのサイズは %d キロバイトです\n", 0x60); + osSyncPrintf("YIELDバッファのサイズは %d キロバイトです\n", 3); + osSyncPrintf("オーディオヒープのサイズは %d キロバイトです\n", ((s32)gSystemHeap - (s32)gAudioHeap) / 1024); + osSyncPrintf(VT_RST); + + osCreateViManager(OS_PRIORITY_VIMGR); + + gViConfigFeatures = OS_VI_GAMMA_OFF | OS_VI_DITHER_FILTER_ON; + gViConfigXScale = 1.0f; + gViConfigYScale = 1.0f; + + switch (osTvType) { + case OS_TV_NTSC: + D_80013960 = 2; + gViConfigMode = osViModeNtscLan1; + break; + + case OS_TV_MPAL: + D_80013960 = 0x1E; + gViConfigMode = osViModeMpalLan1; + break; + + case OS_TV_PAL: + D_80013960 = 0x2C; + gViConfigMode = osViModeFpalLan1; + gViConfigYScale = 0.833f; + break; + } + + D_80009430 = 1; + osViSetMode(&gViConfigMode); + ViConfig_UpdateVi(1); + osViBlack(1); + osViSwapBuffer(0x803DA80); //! @bug Invalid vram address (probably intended to be 0x803DA800) + osCreatePiManager(OS_PRIORITY_PIMGR, &gPiMgrCmdQ, sPiMgrCmdBuff, 50); + StackCheck_Init(&sMainStackInfo, sMainStack, sMainStack + sizeof(sMainStack), 0, 0x400, "main"); + osCreateThread(&gMainThread, 3, Main_ThreadEntry, arg, sMainStack + sizeof(sMainStack), Z_PRIORITY_MAIN); + osStartThread(&gMainThread); + osSetThreadPri(NULL, OS_PRIORITY_IDLE); + + while (1) { + ; + } +} diff --git a/soh/src/boot/is_debug.c b/soh/src/boot/is_debug.c new file mode 100644 index 000000000..4ece8c36e --- /dev/null +++ b/soh/src/boot/is_debug.c @@ -0,0 +1,90 @@ +#include "global.h" + +OSPiHandle* sISVHandle; // official name : is_Handle + +#define gISVDbgPrnAdrs ((ISVDbg*)0xB3FF0000) +#define ASCII_TO_U32(a, b, c, d) ((u32)((a << 24) | (b << 16) | (c << 8) | (d << 0))) + +void isPrintfInit(void) { + sISVHandle = osCartRomInit(); + osEPiWriteIo(sISVHandle, (u32)&gISVDbgPrnAdrs->put, 0); + osEPiWriteIo(sISVHandle, (u32)&gISVDbgPrnAdrs->get, 0); + osEPiWriteIo(sISVHandle, (u32)&gISVDbgPrnAdrs->magic, ASCII_TO_U32('I', 'S', '6', '4')); +} + +void osSyncPrintfUnused(const char* fmt, ...) { +} + +/*void osSyncPrintf(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + _Printf(is_proutSyncPrintf, NULL, fmt, args); + + va_end(args); +}*/ + +// assumption +void rmonPrintf(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + _Printf(is_proutSyncPrintf, NULL, fmt, args); + + va_end(args); +} + +void* is_proutSyncPrintf(void* arg, const char* str, u32 count) { + u32 data; + s32 pos; + s32 start; + s32 end; + + //OTRLogString(str); + + osEPiReadIo(sISVHandle, (u32)&gISVDbgPrnAdrs->magic, &data); + if (data != ASCII_TO_U32('I', 'S', '6', '4')) { + return 1; + } + osEPiReadIo(sISVHandle, (u32)&gISVDbgPrnAdrs->get, &data); + pos = data; + osEPiReadIo(sISVHandle, (u32)&gISVDbgPrnAdrs->put, &data); + start = data; + end = start + count; + if (end >= 0xFFE0) { + end -= 0xFFE0; + if (pos < end || start < pos) { + return 1; + } + } else { + if (start < pos && pos < end) { + return 1; + } + } + while (count) { + u32 addr = (u32)&gISVDbgPrnAdrs->data + (start & 0xFFFFFFC); + s32 shift = ((3 - (start & 3)) * 8); + + if (*str) { + osEPiReadIo(sISVHandle, addr, &data); + osEPiWriteIo(sISVHandle, addr, (*str << shift) | (data & ~(0xFF << shift))); + + start++; + if (start >= 0xFFE0) { + start -= 0xFFE0; + } + } + count--; + str++; + } + osEPiWriteIo(sISVHandle, (u32)&gISVDbgPrnAdrs->put, start); + + return 1; +} + +void func_80002384(const char* exp, const char* file, u32 line) { + osSyncPrintf("File:%s Line:%d %s \n", file, line, exp); + while (true) { + ; + } +} diff --git a/soh/src/boot/logutils.c b/soh/src/boot/logutils.c new file mode 100644 index 000000000..92dac9f75 --- /dev/null +++ b/soh/src/boot/logutils.c @@ -0,0 +1,112 @@ +#include "global.h" +#include "vt.h" + +f32 LogUtils_CheckFloatRange(const char* exp, s32 line, const char* valueName, f32 value, const char* minName, f32 min, + const char* maxName, f32 max) { + if (value < min || max < value) { + osSyncPrintf("%s %d: range error %s(%f) < %s(%f) < %s(%f)\n", exp, line, minName, min, valueName, value, + maxName, max); + } + return value; +} + +s32 LogUtils_CheckIntRange(const char* exp, s32 line, const char* valueName, s32 value, const char* minName, s32 min, + const char* maxName, s32 max) { + if (value < min || max < value) { + osSyncPrintf("%s %d: range error %s(%d) < %s(%d) < %s(%d)\n", exp, line, minName, min, valueName, value, + maxName, max); + } + return value; +} + +void LogUtils_LogHexDump(void* ptr, ptrdiff_t size0) { + u8* addr = (u8*)ptr; + ptrdiff_t size = size0; + s32 rest; + s32 i; + u32 off; + + osSyncPrintf("dump(%08x, %u)\n", addr, size); + osSyncPrintf("address off +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f 0123456789abcdef\n"); + + off = 0; + while (size > 0) { + + osSyncPrintf("%08x %04x", addr, off); + rest = (size < 0x10) ? size : 0x10; + + i = 0; + while (true) { + if (i < rest) { + osSyncPrintf(" %02x", *((u8*)addr + i)); + } else { + osSyncPrintf(" "); + } + + i++; + if (i > 0xF) { + break; + } + } + osSyncPrintf(" "); + + i = 0; + while (true) { + if (i < rest) { + u8 a = *(addr + i); + + osSyncPrintf("%c", (a >= 0x20 && a < 0x7F) ? a : '.'); + } else { + osSyncPrintf(" "); + } + + i++; + if (i > 0xF) { + break; + } + } + osSyncPrintf("\n"); + size -= rest; + addr += rest; + off += rest; + } +} + +void LogUtils_LogPointer(s32 value, u32 max, void* ptr, const char* name, const char* file, s32 line) { + osSyncPrintf(VT_COL(RED, WHITE) "%s %d %s[%d] max=%u ptr=%08x\n" VT_RST, file, line, name, value, max, ptr); +} + +void LogUtils_CheckBoundary(const char* name, s32 value, s32 unk, const char* file, s32 line) { + u32 mask = (unk - 1); + + if (value & mask) { + osSyncPrintf(VT_COL(RED, WHITE) "%s %d:%s(%08x) は バウンダリ(%d)違反です\n" VT_RST, file, line, name, value, + unk); + } +} + +void LogUtils_CheckNullPointer(const char* exp, void* ptr, const char* file, s32 line) { + if (ptr == NULL) { + osSyncPrintf(VT_COL(RED, WHITE) "%s %d:%s は はヌルポインタです\n" VT_RST, file, line, exp); + } +} + +void LogUtils_CheckValidPointer(const char* exp, void* ptr, const char* file, s32 line) { + if (ptr == NULL || (uintptr_t)ptr < 0x80000000 || (0x80000000 + osMemSize) <= (uintptr_t)ptr) { + osSyncPrintf(VT_COL(RED, WHITE) "%s %d:ポインタ %s(%08x) が異常です\n" VT_RST, file, line, exp, ptr); + } +} + +void LogUtils_LogThreadId(const char* name, s32 line) { + osSyncPrintf("<%d %s %d>", osGetThreadId(NULL), name, line); +} + +void LogUtils_HungupThread(const char* name, s32 line) { + osSyncPrintf("*** HungUp in thread %d, [%s:%d] ***\n", osGetThreadId(NULL), name, line); + Fault_AddHungupAndCrash(name, line); +} + +void LogUtils_ResetHungup(void) { + osSyncPrintf("*** Reset ***\n"); + Fault_AddHungupAndCrash("Reset", 0); +} diff --git a/soh/src/boot/missing_gcc_functions.c b/soh/src/boot/missing_gcc_functions.c new file mode 100644 index 000000000..d1d71201f --- /dev/null +++ b/soh/src/boot/missing_gcc_functions.c @@ -0,0 +1,13 @@ +#include "global.h" + +// Define functions needed for the GCC build here. + + + +f32 __floatundisf(u32 c) { + return (f32)c; +} + +f64 __floatundidf(u32 c) { + return (f64)c; +} diff --git a/soh/src/boot/stackcheck.c b/soh/src/boot/stackcheck.c new file mode 100644 index 000000000..2b216f0bd --- /dev/null +++ b/soh/src/boot/stackcheck.c @@ -0,0 +1,134 @@ +#include "global.h" +#include "vt.h" + +StackEntry* sStackInfoListStart = NULL; +StackEntry* sStackInfoListEnd = NULL; + +void StackCheck_Init(StackEntry* entry, void* stackTop, void* stackBottom, u32 initValue, s32 minSpace, + const char* name) { + StackEntry* iter; + u32* addr; + + if (entry == NULL) { + sStackInfoListStart = NULL; + } else { + entry->head = (uintptr_t)stackTop; + entry->tail = (uintptr_t)stackBottom; + entry->initValue = initValue; + entry->minSpace = minSpace; + entry->name = name; + iter = sStackInfoListStart; + while (iter) { + if (iter == entry) { + osSyncPrintf(VT_COL(RED, WHITE) "stackcheck_init: %08x は既にリスト中にある\n" VT_RST, entry); + return; + } + iter = iter->next; + } + + entry->prev = sStackInfoListEnd; + entry->next = NULL; + + if (sStackInfoListEnd) { + sStackInfoListEnd->next = entry; + } + + sStackInfoListEnd = entry; + if (!sStackInfoListStart) { + sStackInfoListStart = entry; + } + + if (entry->minSpace != -1) { + addr = (u32*)entry->head; + while ((uintptr_t)addr < entry->tail) { + *addr++ = entry->initValue; + } + } + } +} + +void StackCheck_Cleanup(StackEntry* entry) { + u32 inconsistency = false; + + if (!entry->prev) { + if (entry == sStackInfoListStart) { + sStackInfoListStart = entry->next; + } else { + inconsistency = true; + } + } else { + entry->prev->next = entry->next; + } + + if (!entry->next) { + if (entry == sStackInfoListEnd) { + sStackInfoListEnd = entry->prev; + } else { + inconsistency = true; + } + } + if (inconsistency) { + osSyncPrintf(VT_COL(RED, WHITE) "stackcheck_cleanup: %08x リスト不整合です\n" VT_RST, entry); + } +} + +StackStatus StackCheck_GetState(StackEntry* entry) { + u32* last; + size_t used; + size_t free; + s32 ret; + + for (last = (uintptr_t*)entry->head; (uintptr_t)last < entry->tail; last++) { + if (entry->initValue != *last) { + break; + } + } + + used = entry->tail - (uintptr_t)last; + free = (uintptr_t)last - entry->head; + + if (free == 0) { + ret = STACK_STATUS_OVERFLOW; + osSyncPrintf(VT_FGCOL(RED)); + } else if (free < (u32)entry->minSpace && entry->minSpace != -1) { + ret = STACK_STATUS_WARNING; + osSyncPrintf(VT_FGCOL(YELLOW)); + } else { + osSyncPrintf(VT_FGCOL(GREEN)); + ret = STACK_STATUS_OK; + } + + osSyncPrintf("head=%08x tail=%08x last=%08x used=%08x free=%08x [%s]\n", entry->head, entry->tail, last, used, free, + entry->name != NULL ? entry->name : "(null)"); + osSyncPrintf(VT_RST); + + if (ret != STACK_STATUS_OK) { + LogUtils_LogHexDump(entry->head, entry->tail - entry->head); + } + + return ret; +} + +u32 StackCheck_CheckAll(void) { + u32 ret = 0; + StackEntry* iter = sStackInfoListStart; + + while (iter) { + u32 state = StackCheck_GetState(iter); + + if (state != STACK_STATUS_OK) { + ret = 1; + } + iter = iter->next; + } + + return ret; +} + +u32 StackCheck_Check(StackEntry* entry) { + if (entry == NULL) { + return StackCheck_CheckAll(); + } else { + return StackCheck_GetState(entry); + } +} diff --git a/soh/src/boot/viconfig.c b/soh/src/boot/viconfig.c new file mode 100644 index 000000000..4c6830c99 --- /dev/null +++ b/soh/src/boot/viconfig.c @@ -0,0 +1,46 @@ +#include "global.h" +#include "vt.h" + +// this should probably go elsewhere but right now viconfig.o is the only object between idle and z_std_dma +OSPiHandle* gCartHandle = 0; + +void ViConfig_UpdateVi(u32 mode) { + if (mode != 0) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "osViSetYScale1(%f);\n" VT_RST, 1.0f); + + if (osTvType == OS_TV_PAL) { + osViSetMode(&osViModePalLan1); + } + + osViSetYScale(1.0f); + } else { + osViSetMode(&gViConfigMode); + + if (gViConfigAdditionalScanLines != 0) { + osViExtendVStart(gViConfigAdditionalScanLines); + } + + if (gViConfigFeatures != 0) { + osViSetSpecialFeatures(gViConfigFeatures); + } + + if (gViConfigXScale != 1.0f) { + osViSetXScale(gViConfigXScale); + } + + if (gViConfigYScale != 1.0f) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "osViSetYScale3(%f);\n" VT_RST, gViConfigYScale); + osViSetYScale(gViConfigYScale); + } + } + + gViConfigUseDefault = mode; +} + +void ViConfig_UpdateBlack(void) { + if (gViConfigUseDefault != 0) { + osViBlack(1); + } else { + osViBlack(0); + } +} diff --git a/soh/src/boot/yaz0.c b/soh/src/boot/yaz0.c new file mode 100644 index 000000000..25829ad5b --- /dev/null +++ b/soh/src/boot/yaz0.c @@ -0,0 +1,102 @@ +#include "global.h" +#include + +u8 sYaz0DataBuffer[0x400]; +uintptr_t sYaz0CurDataEnd; +uintptr_t sYaz0CurRomStart; +u32 sYaz0CurSize; +uintptr_t sYaz0MaxPtr; + +void* Yaz0_FirstDMA(void) { + u32 pad0; + u32 pad1; + u32 dmaSize; + u32 curSize; + + sYaz0MaxPtr = sYaz0CurDataEnd - 0x19; + + curSize = sYaz0CurDataEnd - (uintptr_t)sYaz0DataBuffer; + dmaSize = (curSize > sYaz0CurSize) ? sYaz0CurSize : curSize; + + DmaMgr_DmaRomToRam(sYaz0CurRomStart, sYaz0DataBuffer, dmaSize); + sYaz0CurRomStart += dmaSize; + sYaz0CurSize -= dmaSize; + return sYaz0DataBuffer; +} + +void* Yaz0_NextDMA(void* curSrcPos) { + u8* dst; + u32 restSize; + u32 dmaSize; + + restSize = sYaz0CurDataEnd - (uintptr_t)curSrcPos; + dst = (restSize & 7) ? (sYaz0DataBuffer - (restSize & 7)) + 8 : sYaz0DataBuffer; + + memcpy(dst, curSrcPos, restSize); + dmaSize = (sYaz0CurDataEnd - (uintptr_t)dst) - restSize; + if (sYaz0CurSize < dmaSize) { + dmaSize = sYaz0CurSize; + } + + if (dmaSize != 0) { + DmaMgr_DmaRomToRam(sYaz0CurRomStart, (uintptr_t)dst + restSize, dmaSize); + sYaz0CurRomStart += dmaSize; + sYaz0CurSize -= dmaSize; + if (!sYaz0CurSize) { + sYaz0MaxPtr = (uintptr_t)dst + restSize + dmaSize; + } + } + + return dst; +} + +void Yaz0_DecompressImpl(Yaz0Header* hdr, u8* dst) { + u32 bitIdx = 0; + u8* src = (u8*)hdr->data; + u8* dstEnd = dst + hdr->decSize; + u32 chunkHeader; + u32 nibble; + u8* backPtr; + u32 chunkSize; + u32 off; + + do { + if (bitIdx == 0) { + if ((sYaz0MaxPtr < (uintptr_t)src) && (sYaz0CurSize != 0)) { + src = Yaz0_NextDMA(src); + } + + chunkHeader = *src++; + bitIdx = 8; + } + + if (chunkHeader & (1 << 7)) { // uncompressed + *dst = *src; + dst++; + src++; + } else { // compressed + off = ((*src & 0xF) << 8 | *(src + 1)); + nibble = *src >> 4; + backPtr = dst - off; + src += 2; + + chunkSize = (nibble == 0) // N = chunkSize; B = back offset + ? (u32)(*src++ + 0x12) // 3 bytes 0B BB NN + : nibble + 2; // 2 bytes NB BB + + do { + *dst++ = *(backPtr++ - 1); + chunkSize--; + } while (chunkSize != 0); + } + chunkHeader <<= 1; + bitIdx--; + } while (dst != dstEnd); +} + +void Yaz0_Decompress(uintptr_t romStart, void* dst, size_t size) { + sYaz0CurRomStart = romStart; + sYaz0CurSize = size; + sYaz0CurDataEnd = sYaz0DataBuffer + sizeof(sYaz0DataBuffer); + Yaz0_DecompressImpl(Yaz0_FirstDMA(), dst); +} diff --git a/soh/src/boot/z_locale.c b/soh/src/boot/z_locale.c new file mode 100644 index 000000000..0fc68958e --- /dev/null +++ b/soh/src/boot/z_locale.c @@ -0,0 +1,63 @@ +#include "global.h" +#include "vt.h" + +u32 gCurrentRegion = 0; +LocaleCartInfo sCartInfo; + +void Locale_Init(void) { + osEPiReadIo(gCartHandle, 0x38, &sCartInfo.mediaFormat); + osEPiReadIo(gCartHandle, 0x3C, &sCartInfo.regionInfo); + + switch (sCartInfo.countryCode) { + case 'J': // "NTSC-U (North America)" + gCurrentRegion = REGION_US; + break; + case 'E': // "NTSC-J (Japan)" + gCurrentRegion = REGION_JP; + break; + case 'P': // "PAL (Europe)" + gCurrentRegion = REGION_EU; + break; + default: + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("z_locale_init: 日本用かアメリカ用か判別できません\n"); + LogUtils_HungupThread("../z_locale.c", 118); + osSyncPrintf(VT_RST); + break; + } + + osSyncPrintf("z_locale_init:日本用かアメリカ用か3コンで判断させる\n"); +} + +void Locale_ResetRegion(void) { + gCurrentRegion = REGION_NULL; +} + +u32 func_80001F48(void) { + if (gCurrentRegion == REGION_NATIVE) { + return 0; + } + + if (gPadMgr.validCtrlrsMask & 4) { + return 0; + } + + return 1; +} + +u32 func_80001F8C(void) { + if (gCurrentRegion == REGION_NATIVE) { + return 0; + } + + if (gPadMgr.validCtrlrsMask & 4) { + return 1; + } + + return 0; +} + +// This function appears to be unused? +u32 Locale_IsRegionNative(void) { + return gCurrentRegion == REGION_NATIVE; +} diff --git a/soh/src/boot/z_std_dma.c b/soh/src/boot/z_std_dma.c new file mode 100644 index 000000000..f91d7cbe1 --- /dev/null +++ b/soh/src/boot/z_std_dma.c @@ -0,0 +1,461 @@ +#include "global.h" +#include "vt.h" + +StackEntry sDmaMgrStackInfo; +OSMesgQueue sDmaMgrMsgQueue; +OSMesg sDmaMgrMsgs[0x20]; +OSThread sDmaMgrThread; +u8 sDmaMgrStack[0x500]; +const char* sDmaMgrCurFileName; +s32 sDmaMgrCurFileLine; + +u32 D_80009460 = 0; +u32 gDmaMgrDmaBuffSize = 0x2000; +u32 sDmaMgrDataExistError = 0; + +// dmadata filenames +#define DEFINE_DMA_ENTRY(name) #name, + +const char* sDmaMgrFileNames[] = { +#include "tables/dmadata_table.h" +}; + +#undef DEFINE_DMA_ENTRY + +s32 DmaMgr_CompareName(const char* name1, const char* name2) { + while (*name1 != 0u) { + if (*name1 > *name2) { + return 1; + } + + if (*name1 < *name2) { + return -1; + } + + name1++; + name2++; + } + + if (*name2 > 0) { + return -1; + } + + return 0; +} + +s32 DmaMgr_DmaRomToRam(uintptr_t rom, uintptr_t ram, size_t size) { + OSIoMesg ioMsg; + OSMesgQueue queue; + OSMesg msg; + s32 ret; + u32 buffSize = gDmaMgrDmaBuffSize; + s32 pad[2]; + + if (buffSize == 0) { + buffSize = 0x2000; + } + + osInvalICache((void*)ram, size); + osInvalDCache((void*)ram, size); + osCreateMesgQueue(&queue, &msg, 1); + + while (size > buffSize) { + if (1) {} // Necessary to match + + ioMsg.hdr.pri = OS_MESG_PRI_NORMAL; + ioMsg.hdr.retQueue = &queue; + ioMsg.devAddr = rom; + ioMsg.dramAddr = (void*)ram; + ioMsg.size = buffSize; + + if (D_80009460 == 10) { + osSyncPrintf("%10lld ノーマルDMA %08x %08x %08x (%d)\n", OS_CYCLES_TO_USEC(osGetTime()), ioMsg.dramAddr, + ioMsg.devAddr, ioMsg.size, gPiMgrCmdQ.validCount); + } + + ret = osEPiStartDma(gCartHandle, &ioMsg, OS_READ); + if (ret) { + goto end; + } + + if (D_80009460 == 10) { + osSyncPrintf("%10lld ノーマルDMA START (%d)\n", OS_CYCLES_TO_USEC(osGetTime()), gPiMgrCmdQ.validCount); + } + + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); + if (D_80009460 == 10) { + osSyncPrintf("%10lld ノーマルDMA END (%d)\n", OS_CYCLES_TO_USEC(osGetTime()), gPiMgrCmdQ.validCount); + } + + size -= buffSize; + rom += buffSize; + ram += buffSize; + } + + if (1) {} // Also necessary to match + + ioMsg.hdr.pri = OS_MESG_PRI_NORMAL; + ioMsg.hdr.retQueue = &queue; + ioMsg.devAddr = rom; + ioMsg.dramAddr = (void*)ram; + ioMsg.size = size; + + if (D_80009460 == 10) { + osSyncPrintf("%10lld ノーマルDMA %08x %08x %08x (%d)\n", OS_CYCLES_TO_USEC(osGetTime()), ioMsg.dramAddr, + ioMsg.devAddr, ioMsg.size, gPiMgrCmdQ.validCount); + } + + ret = osEPiStartDma(gCartHandle, &ioMsg, OS_READ); + if (ret) { + goto end; + } + + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); + if (D_80009460 == 10) { + osSyncPrintf("%10lld ノーマルDMA END (%d)\n", OS_CYCLES_TO_USEC(osGetTime()), gPiMgrCmdQ.validCount); + } + +end: + osInvalICache((void*)ram, size); + osInvalDCache((void*)ram, size); + + return ret; +} + +s32 DmaMgr_DmaHandler(OSPiHandle* pihandle, OSIoMesg* mb, s32 direction) { + s32 ret; + + ASSERT(pihandle == gCartHandle, "pihandle == carthandle", "../z_std_dma.c", 530); + ASSERT(direction == OS_READ, "direction == OS_READ", "../z_std_dma.c", 531); + ASSERT(mb != NULL, "mb != NULL", "../z_std_dma.c", 532); + + if (D_80009460 == 10) { + osSyncPrintf("%10lld サウンドDMA %08x %08x %08x (%d)\n", OS_CYCLES_TO_USEC(osGetTime()), mb->dramAddr, + mb->devAddr, mb->size, gPiMgrCmdQ.validCount); + } + + ret = osEPiStartDma(pihandle, mb, direction); + if (ret) { + osSyncPrintf("OOPS!!\n"); + } + return ret; +} + +void DmaMgr_DmaFromDriveRom(uintptr_t ram, uintptr_t rom, size_t size) { + OSPiHandle* handle = osDriveRomInit(); + OSMesgQueue queue; + OSMesg msg; + OSIoMesg ioMsg; + + osInvalICache((void*)ram, size); + osInvalDCache((void*)ram, size); + osCreateMesgQueue(&queue, &msg, 1); + + ioMsg.hdr.retQueue = &queue; + ioMsg.hdr.pri = OS_MESG_PRI_NORMAL; + ioMsg.devAddr = rom; + ioMsg.dramAddr = (void*)ram; + ioMsg.size = size; + handle->transferInfo.cmdType = 2; + + osEPiStartDma(handle, &ioMsg, OS_READ); + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); + return; +} + +void DmaMgr_Error(DmaRequest* req, const char* file, const char* errorName, const char* errorDesc) { + uintptr_t vrom = req->vromAddr; + uintptr_t ram = (uintptr_t)req->dramAddr; + size_t size = req->size; + char buff1[80]; + char buff2[80]; + + osSyncPrintf("%c", 7); + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("DMA致命的エラー(%s)\nROM:%X RAM:%X SIZE:%X %s\n", + errorDesc != NULL ? errorDesc : (errorName != NULL ? errorName : "???"), vrom, ram, size, + file != NULL ? file : "???"); + + if (req->filename) { + osSyncPrintf("DMA ERROR: %s %d", req->filename, req->line); + } else if (sDmaMgrCurFileName) { + osSyncPrintf("DMA ERROR: %s %d", sDmaMgrCurFileName, sDmaMgrCurFileLine); + } + + osSyncPrintf(VT_RST); + + if (req->filename) { + sprintf(buff1, "DMA ERROR: %s %d", req->filename, req->line); + } else if (sDmaMgrCurFileName) { + sprintf(buff1, "DMA ERROR: %s %d", sDmaMgrCurFileName, sDmaMgrCurFileLine); + } else { + sprintf(buff1, "DMA ERROR: %s", errorName != NULL ? errorName : "???"); + } + + sprintf(buff2, "%07X %08X %X %s", vrom, ram, size, file != NULL ? file : "???"); + Fault_AddHungupAndCrashImpl(buff1, buff2); +} + +const char* DmaMgr_GetFileNameImpl(uintptr_t vrom) { + DmaEntry* iter = gDmaDataTable; + const char** name = sDmaMgrFileNames; + + while (iter->vromEnd) { + if (vrom >= iter->vromStart && vrom < iter->vromEnd) { + return *name; + } + + iter++; + name++; + } + //! @bug Since there is no return, in case the file isn't found, the return value will be a pointer to the end + // of gDmaDataTable +} + +const char* DmaMgr_GetFileName(uintptr_t vrom) { + const char* ret = DmaMgr_GetFileNameImpl(vrom); + + if (ret == NULL) { + return "(unknown)"; + } + + if (DmaMgr_CompareName(ret, "kanji") == 0 || DmaMgr_CompareName(ret, "link_animetion") == 0) { + return NULL; + } + + return ret; +} + +void DmaMgr_ProcessMsg(DmaRequest* req) { + uintptr_t vrom = req->vromAddr; + void* ram = req->dramAddr; + size_t size = req->size; + uintptr_t romStart; + uintptr_t romSize; + u8 found = false; + DmaEntry* iter; + const char* filename; + + if (0) { + // the string is defined in .rodata but not used, suggesting + // a debug print is here but optimized out in some way + osSyncPrintf("DMA ROM:%08X RAM:%08X SIZE:%08X %s\n"); + // the last arg of this print looks like it may be filename, but + // filename above this block does not match + } + + filename = DmaMgr_GetFileName(vrom); + iter = gDmaDataTable; + + while (iter->vromEnd) { + if (vrom >= iter->vromStart && vrom < iter->vromEnd) { + if (1) {} // Necessary to match + + if (iter->romEnd == 0) { + if (iter->vromEnd < vrom + size) { + DmaMgr_Error(req, filename, "Segment Alignment Error", + "セグメント境界をまたがってDMA転送することはできません"); + } + + DmaMgr_DmaRomToRam(iter->romStart + (vrom - iter->vromStart), ram, size); + found = true; + + if (0) { + osSyncPrintf("No Press ROM:%08X RAM:%08X SIZE:%08X\n", vrom, ram, size); + } + } else { + romStart = iter->romStart; + romSize = iter->romEnd - iter->romStart; + + if (vrom != iter->vromStart) { + DmaMgr_Error(req, filename, "Can't Transfer Segment", + "圧縮されたセグメントの途中からはDMA転送することはできません"); + } + + if (size != iter->vromEnd - iter->vromStart) { + DmaMgr_Error(req, filename, "Can't Transfer Segment", + "圧縮されたセグメントの一部だけをDMA転送することはできません"); + } + + osSetThreadPri(NULL, Z_PRIORITY_MAIN); + Yaz0_Decompress(romStart, ram, romSize); + osSetThreadPri(NULL, Z_PRIORITY_DMAMGR); + found = true; + + if (0) { + osSyncPrintf(" Press ROM:%X RAM:%X SIZE:%X\n", vrom, ram, size); + } + } + break; + } + iter++; + } + + if (!found) { + if (sDmaMgrDataExistError) { + DmaMgr_Error(req, NULL, "DATA DON'T EXIST", "該当するデータが存在しません"); + return; + } + + DmaMgr_DmaRomToRam(vrom, (uintptr_t)ram, size); + + if (0) { + osSyncPrintf("No Press ROM:%08X RAM:%08X SIZE:%08X (非公式)\n", vrom, ram, size); + } + } +} + +void DmaMgr_ThreadEntry(void* arg0) { + OSMesg msg; + DmaRequest* req; + + osSyncPrintf("DMAマネージャスレッド実行開始\n"); + while (true) { + osRecvMesg(&sDmaMgrMsgQueue, &msg, OS_MESG_BLOCK); + req = (DmaRequest*)msg; + if (req == NULL) { + break; + } + + if (0) { + osSyncPrintf("DMA登録受付 dmap=%08x\n", req); + } + + DmaMgr_ProcessMsg(req); + if (req->notifyQueue) { + osSendMesg(req->notifyQueue, req->notifyMsg, OS_MESG_NOBLOCK); + if (0) { + osSyncPrintf("osSendMesg: dmap=%08x, mq=%08x, m=%08x \n", req, req->notifyQueue, req->notifyMsg); + } + } + } + osSyncPrintf("DMAマネージャスレッド実行終了\n"); +} + +s32 DmaMgr_SendRequestImpl(DmaRequest* req, uintptr_t ram, uintptr_t vrom, size_t size, u32 unk, OSMesgQueue* queue, OSMesg msg) { + static s32 sDmaMgrQueueFullLogged = 0; + + if ((1 && (ram == 0)) || (osMemSize < ram + size + 0x80000000) || (vrom & 1) || (vrom > 0x4000000) || (size == 0) || + (size & 1)) { + //! @bug `req` is passed to `DmaMgr_Error` without rom, ram and size being set + DmaMgr_Error(req, NULL, "ILLIGAL DMA-FUNCTION CALL", "パラメータ異常です"); + } + + req->vromAddr = vrom; + req->dramAddr = (void*)ram; + req->size = size; + req->unk_14 = 0; + req->notifyQueue = queue; + req->notifyMsg = msg; + + if (1) { + if ((sDmaMgrQueueFullLogged == 0) && (sDmaMgrMsgQueue.validCount >= sDmaMgrMsgQueue.msgCount)) { + sDmaMgrQueueFullLogged++; + osSyncPrintf("%c", 7); + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("dmaEntryMsgQが一杯です。キューサイズの再検討をおすすめします。"); + LOG_NUM("(sizeof(dmaEntryMsgBufs) / sizeof(dmaEntryMsgBufs[0]))", ARRAY_COUNT(sDmaMgrMsgs), + "../z_std_dma.c", 952); + osSyncPrintf(VT_RST); + } + } + + osSendMesg(&sDmaMgrMsgQueue, req, OS_MESG_BLOCK); + return 0; +} + +s32 DmaMgr_SendRequest0(uintptr_t ram, uintptr_t vrom, size_t size) { + DmaRequest req; + OSMesgQueue queue; + OSMesg msg; + s32 ret; + + osCreateMesgQueue(&queue, &msg, 1); + ret = DmaMgr_SendRequestImpl(&req, ram, vrom, size, 0, &queue, NULL); + if (ret == -1) { + return ret; + } + + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); + return 0; +} + +void DmaMgr_Init(void) { + const char** name; + s32 idx; + DmaEntry* iter; + + //DmaMgr_DmaRomToRam((uintptr_t)_dmadataSegmentRomStart, (uintptr_t)_dmadataSegmentStart, + //(uintptr_t)(_dmadataSegmentRomEnd - _dmadataSegmentRomStart)); + osSyncPrintf("dma_rom_ad[]\n"); + + sDmaMgrDataExistError = 0; + name = sDmaMgrFileNames; + iter = gDmaDataTable; + idx = 0; + + while (iter->vromEnd != 0) { + if (iter->romEnd != 0) { + sDmaMgrDataExistError = 1; + } + + osSyncPrintf( + "%3d %08x %08x %08x %08x %08x %c %s\n", idx, iter->vromStart, iter->vromEnd, iter->romStart, iter->romEnd, + (iter->romEnd != 0) ? iter->romEnd - iter->romStart : iter->vromEnd - iter->vromStart, + (((iter->romEnd != 0) ? iter->romEnd - iter->romStart : 0) > 0x10000) ? '*' : ' ', name ? *name : ""); + + idx++; + iter++; + + if (name) { + name++; + } + } + +#if 0 + if ((uintptr_t)_bootSegmentRomStart != gDmaDataTable[0].vromEnd) + { + osSyncPrintf("_bootSegmentRomStart(%08x) != dma_rom_ad[0].rom_b(%08x)\n", _bootSegmentRomStart, + gDmaDataTable[0].vromEnd); + Fault_AddHungupAndCrash("../z_std_dma.c", 1055); + } +#endif + + osCreateMesgQueue(&sDmaMgrMsgQueue, sDmaMgrMsgs, ARRAY_COUNT(sDmaMgrMsgs)); + StackCheck_Init(&sDmaMgrStackInfo, sDmaMgrStack, sDmaMgrStack + sizeof(sDmaMgrStack), 0, 0x100, "dmamgr"); + osCreateThread(&sDmaMgrThread, 0x12, DmaMgr_ThreadEntry, 0, sDmaMgrStack + sizeof(sDmaMgrStack), Z_PRIORITY_DMAMGR); + osStartThread(&sDmaMgrThread); +} + +s32 DmaMgr_SendRequest2(DmaRequest* req, uintptr_t ram, uintptr_t vrom, size_t size, u32 unk5, OSMesgQueue* queue, OSMesg msg, + const char* file, s32 line) { +#if 0 + req->filename = file; + req->line = line; + DmaMgr_SendRequestImpl(req, ram, vrom, size, unk5, queue, msg); +#endif +} + +s32 DmaMgr_SendRequest1(void* ram0, uintptr_t vrom, size_t size, const char* file, s32 line) { + //printf("DmaMgr_SendRequest1 called...\n"); + return 0; + +#if 0 + DmaRequest req; + s32 ret; + OSMesgQueue queue; + OSMesg msg; + uintptr_t ram = (uintptr_t)ram0; + + req.filename = file; + req.line = line; + osCreateMesgQueue(&queue, &msg, 1); + ret = DmaMgr_SendRequestImpl(&req, ram, vrom, size, 0, &queue, 0); + if (ret == -1) { + return ret; + } + + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); + return 0; +#endif +} diff --git a/soh/src/buffers/gfxbuffers.c b/soh/src/buffers/gfxbuffers.c new file mode 100644 index 000000000..13206fe2b --- /dev/null +++ b/soh/src/buffers/gfxbuffers.c @@ -0,0 +1,13 @@ +#include "z64.h" + +// 0x18000 bytes +u64 gGfxSPTaskOutputBuffer[0x3000]; + +// 0xC00 bytes +u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE]; + +// 0x400 bytes +u8 gGfxSPTaskStack[0x400]; + +// 0x12410 bytes each; 0x24820 bytes total +GfxPool gGfxPools[2]; diff --git a/soh/src/buffers/heaps.c b/soh/src/buffers/heaps.c new file mode 100644 index 000000000..4309acd18 --- /dev/null +++ b/soh/src/buffers/heaps.c @@ -0,0 +1,7 @@ +#include "z64.h" + +// 0x38000 bytes +u8 gAudioHeap[0x38000]; + +//u8 gSystemHeap[UNK_SIZE]; +u8 gSystemHeap[1024 * 1024 * 128]; diff --git a/soh/src/buffers/zbuffer.c b/soh/src/buffers/zbuffer.c new file mode 100644 index 000000000..555d9fdd7 --- /dev/null +++ b/soh/src/buffers/zbuffer.c @@ -0,0 +1,4 @@ +#include "z64.h" + +// 0x25800 bytes +u16 gZBuffer[SCREEN_HEIGHT][SCREEN_WIDTH]; diff --git a/soh/src/code/PreRender.c b/soh/src/code/PreRender.c new file mode 100644 index 000000000..0f29ca8dd --- /dev/null +++ b/soh/src/code/PreRender.c @@ -0,0 +1,569 @@ +#include "global.h" +#include "alloca.h" + +#include + +void PreRender_SetValuesSave(PreRender* this, u32 width, u32 height, void* fbuf, void* zbuf, void* cvg) { + this->widthSave = width; + this->heightSave = height; + this->fbufSave = fbuf; + this->cvgSave = cvg; + this->zbufSave = zbuf; + this->ulxSave = 0; + this->ulySave = 0; + this->lrxSave = width - 1; + this->lrySave = height - 1; +} + +void PreRender_Init(PreRender* this) { + memset(this,0, sizeof(PreRender)); + ListAlloc_Init(&this->alloc); +} + +void PreRender_SetValues(PreRender* this, u32 width, u32 height, void* fbuf, void* zbuf) { + this->width = width; + this->height = height; + this->fbuf = fbuf; + this->zbuf = zbuf; + this->ulx = 0; + this->uly = 0; + this->lrx = width - 1; + this->lry = height - 1; +} + +void PreRender_Destroy(PreRender* this) { + ListAlloc_FreeAll(&this->alloc); +} + +void func_800C0F28(PreRender* this, Gfx** gfxp, void* buf, void* bufSave) { + Gfx* gfx; + s32 x; + s32 x2; + s32 dx; + + LogUtils_CheckNullPointer("this", this, "../PreRender.c", 215); + LogUtils_CheckNullPointer("glistpp", gfxp, "../PreRender.c", 216); + gfx = *gfxp; + LogUtils_CheckNullPointer("glistp", gfx, "../PreRender.c", 218); + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_COPY | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, bufSave); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, this->width, this->height); + + dx = 0x1000 / (this->width * 2); + + x = this->height; + x2 = 0; + while (x > 0) { + s32 uls = 0; + s32 lrs = this->width - 1; + s32 ult; + s32 lrt; + + dx = CLAMP_MAX(dx, x); + ult = x2; + lrt = (ult + dx) - 1; + + gDPLoadTextureTile(gfx++, buf, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->height, uls, ult, lrs, lrt, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSPTextureRectangle(gfx++, uls << 2, ult << 2, lrs << 2, lrt << 2, G_TX_RENDERTILE, uls << 5, ult << 5, 4 << 10, + 1 << 10); + + x -= dx; + x2 += dx; + } + + gDPPipeSync(gfx++); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->fbuf); + *gfxp = gfx; +} + +void func_800C1258(PreRender* this, Gfx** gfxp) { + Gfx* gfx; + s32 y; + s32 y2; + s32 dy; + + LogUtils_CheckNullPointer("this", this, "../PreRender.c", 278); + LogUtils_CheckNullPointer("glistpp", gfxp, "../PreRender.c", 279); + gfx = *gfxp; + LogUtils_CheckNullPointer("glistp", gfx, "../PreRender.c", 281); + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_COPY | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->fbuf); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, this->ulx, this->uly, this->lrx + 1, this->lry + 1); + + dy = 0x1000 / ((this->lrxSave - this->ulxSave + 1) * 2); + + y = (this->lrySave - this->ulySave) + 1; + y2 = 0; + while (y > 0) { + s32 ult; + s32 lrt; + s32 uly; + + dy = CLAMP_MAX(dy, y); + + ult = this->ulySave + y2; + lrt = (ult + dy) - 1; + uly = this->uly + y2; + + gDPLoadTextureTile(gfx++, this->fbufSave, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->widthSave, this->height - 1, + this->ulxSave, ult, this->lrxSave, lrt, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle(gfx++, this->ulx << 2, uly << 2, this->lrx << 2, (uly + dy - 1) << 2, G_TX_RENDERTILE, + this->ulxSave << 5, ult << 5, 4 << 10, 1 << 10); + + y -= dy; + y2 += dy; + } + + gDPPipeSync(gfx++); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->fbuf); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, this->width, this->height); + *gfxp = gfx; +} + +void func_800C170C(PreRender* this, Gfx** gfxp, void* fbuf, void* fbufSave, u32 r, u32 g, u32 b, u32 a) { + Gfx* gfx; + s32 x; + s32 x2; + s32 dx; + + LogUtils_CheckNullPointer("this", this, "../PreRender.c", 343); + LogUtils_CheckNullPointer("glistpp", gfxp, "../PreRender.c", 344); + gfx = *gfxp; + LogUtils_CheckNullPointer("glistp", gfx, "../PreRender.c", 346); + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_OPA_SURF | G_RM_OPA_SURF2); + gDPSetEnvColor(gfx++, r, g, b, a); + gDPSetCombineLERP(gfx++, 0, 0, 0, TEXEL0, 0, 0, 0, 1, 0, 0, 0, TEXEL0, 0, 0, 0, 1); + gDPSetCombineLERP(gfx++, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, 1, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, 1); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, fbufSave); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, this->width, this->height); + + dx = 0x1000 / (this->width * 2); + + x = this->height; + x2 = 0; + while (x > 0) { + s32 uls = 0; + s32 lrs = this->width - 1; + s32 ult; + s32 lrt; + + dx = CLAMP_MAX(dx, x); + ult = x2; + lrt = x2 + dx - 1; + + gDPLoadTextureTile(gfx++, fbuf, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->height, uls, ult, lrs, lrt, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPTextureRectangle(gfx++, uls << 2, ult << 2, (lrs + 1) << 2, (lrt + 1) << 2, G_TX_RENDERTILE, uls << 5, + ult << 5, 1 << 10, 1 << 10); + + x -= dx; + x2 += dx; + } + + gDPPipeSync(gfx++); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->fbuf); + *gfxp = gfx; +} + +void func_800C1AE8(PreRender* this, Gfx** gfxp, void* fbuf, void* fbufSave) { + func_800C170C(this, gfxp, fbuf, fbufSave, 255, 255, 255, 255); +} + +void func_800C1B24(PreRender* this, Gfx** gfxp, void* fbuf, void* cvgSave) { + Gfx* gfx; + s32 x; + s32 x2; + s32 dx; + + LogUtils_CheckNullPointer("this", this, "../PreRender.c", 422); + LogUtils_CheckNullPointer("glistpp", gfxp, "../PreRender.c", 423); + gfx = *gfxp; + LogUtils_CheckNullPointer("glistp", gfx, "../PreRender.c", 425); + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_PASS | G_RM_OPA_CI2); + gDPSetCombineLERP(gfx++, 0, 0, 0, TEXEL0, 0, 0, 0, 0, 0, 0, 0, TEXEL0, 0, 0, 0, 0); + gDPSetColorImage(gfx++, G_IM_FMT_I, G_IM_SIZ_8b, this->width, cvgSave); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, this->width, this->height); + + dx = 0x1000 / (this->width * 2); + + x = this->height; + x2 = 0; + while (x > 0) { + s32 uls = 0; + s32 lrs = this->width - 1; + s32 ult; + s32 lrt; + + dx = CLAMP_MAX(dx, x); + ult = x2; + lrt = x2 + dx - 1; + + gDPLoadTextureTile(gfx++, fbuf, G_IM_FMT_IA, G_IM_SIZ_16b, this->width, this->height, uls, ult, lrs, lrt, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPTextureRectangle(gfx++, uls << 2, ult << 2, (lrs + 1) << 2, (lrt + 1) << 2, G_TX_RENDERTILE, uls << 5, + ult << 5, 1 << 10, 1 << 10); + x -= dx; + x2 += dx; + } + + gDPPipeSync(gfx++); + gDPSetColorImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, this->fbuf); + *gfxp = gfx; +} + +void func_800C1E9C(PreRender* this, Gfx** gfxp) { + LogUtils_CheckNullPointer("this->zbuf_save", this->zbufSave, "../PreRender.c", 481); + LogUtils_CheckNullPointer("this->zbuf", this->zbuf, "../PreRender.c", 482); + + if ((this->zbufSave != NULL) && (this->zbuf != NULL)) { + func_800C0F28(this, gfxp, this->zbuf, this->zbufSave); + } +} + +void func_800C1F20(PreRender* this, Gfx** gfxp) { + LogUtils_CheckNullPointer("this->fbuf_save", this->fbufSave, "../PreRender.c", 495); + LogUtils_CheckNullPointer("this->fbuf", this->fbuf, "../PreRender.c", 496); + + if ((this->fbufSave != NULL) && (this->fbuf != NULL)) { + func_800C1AE8(this, gfxp, this->fbuf, this->fbufSave); + } +} + +void func_800C1FA4(PreRender* this, Gfx** gfxp) { + Gfx* gfx = *gfxp; + + gDPPipeSync(gfx++); + gDPSetBlendColor(gfx++, 255, 255, 255, 8); + gDPSetPrimDepth(gfx++, -1, -1); + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_VISCVG | G_RM_VISCVG2); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, this->width, this->height); + gDPFillRectangle(gfx++, 0, 0, this->width, this->height); + gDPPipeSync(gfx++); + + *gfxp = gfx; +} + +void func_800C20B4(PreRender* this, Gfx** gfxp) { + func_800C1FA4(this, gfxp); + LogUtils_CheckNullPointer("this->cvg_save", this->cvgSave, "../PreRender.c", 532); + if (this->cvgSave != NULL) { + func_800C1B24(this, gfxp, this->fbuf, this->cvgSave); + } +} + +void func_800C2118(PreRender* this, Gfx** gfxp) { + func_800C0F28(this, gfxp, this->zbufSave, this->zbuf); +} + +void func_800C213C(PreRender* this, Gfx** gfxp) { + Gfx* gfx; + s32 y; + s32 y2; + s32 dy; + s32 rtile = 1; + + if (this->cvgSave != NULL) { + LogUtils_CheckNullPointer("this", this, "../PreRender.c", 563); + LogUtils_CheckNullPointer("glistpp", gfxp, "../PreRender.c", 564); + gfx = *gfxp; + LogUtils_CheckNullPointer("glistp", gfx, "../PreRender.c", 566); + + gDPPipeSync(gfx++); + gDPSetEnvColor(gfx++, 255, 255, 255, 32); + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) | + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)); + gDPSetCombineLERP(gfx++, 0, 0, 0, TEXEL0, 1, 0, TEXEL1, ENVIRONMENT, 0, 0, 0, COMBINED, 0, 0, 0, COMBINED); + + dy = 4; + + y = this->height; + y2 = 0; + while (y > 0) { + s32 uls = 0; + s32 lrs = this->width - 1; + s32 ult; + s32 lrt; + + dy = CLAMP_MAX(dy, y); + + ult = y2; + lrt = (y2 + dy - 1); + + gDPLoadMultiTile(gfx++, this->fbufSave, 0x0000, G_TX_RENDERTILE, G_IM_FMT_RGBA, G_IM_SIZ_16b, this->width, + this->height, uls, ult, lrs, lrt, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gDPLoadMultiTile(gfx++, this->cvgSave, 0x0160, rtile, G_IM_FMT_I, G_IM_SIZ_8b, this->width, this->height, + uls, ult, lrs, lrt, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, uls << 2, ult << 2, (lrs + 1) << 2, (lrt + 1) << 2, G_TX_RENDERTILE, uls << 5, + ult << 5, 1 << 10, 1 << 10); + + y -= dy; + y2 += dy; + } + + gDPPipeSync(gfx++); + *gfxp = gfx; + } +} + +void func_800C24BC(PreRender* this, Gfx** gfxp) { + func_800C0F28(this, gfxp, this->fbufSave, this->fbuf); +} + +void func_800C24E0(PreRender* this, Gfx** gfxp) { + func_800C1258(this, gfxp); +} + +void func_800C2500(PreRender* this, s32 x, s32 y) { + s32 i; + s32 j; + s32 buffA[3 * 5]; + s32 buffR[3 * 5]; + s32 buffG[3 * 5]; + s32 buffB[3 * 5]; + s32 x1; + s32 y1; + s32 pad; + s32 pxR; + s32 pxG; + s32 pxB; + s32 pxR2; + s32 pxG2; + s32 pxB2; + Color_RGBA16 pxIn; + Color_RGBA16 pxOut; + u32 pxR3; + u32 pxG3; + u32 pxB3; + + /* + Picture this as a 3x5 rectangle where the middle pixel (index 7) correspond to (x, y) + _ _ _ _ _ + | 0 1 2 3 4 | + | 5 6 7 8 9 | + | A B C D E | + ‾ ‾ ‾ ‾ ‾ + */ + for (i = 0; i < 3 * 5; i++) { + x1 = (i % 5) + x - 2; + y1 = (i / 5) + y - 1; + + if (x1 < 0) { + x1 = 0; + } else if (x1 > (this->width - 1)) { + x1 = this->width - 1; + } + if (y1 < 0) { + y1 = 0; + } else if (y1 > (this->height - 1)) { + y1 = this->height - 1; + } + + pxIn.rgba = this->fbufSave[x1 + y1 * this->width]; + buffR[i] = (pxIn.r << 3) | (pxIn.r >> 2); + buffG[i] = (pxIn.g << 3) | (pxIn.g >> 2); + buffB[i] = (pxIn.b << 3) | (pxIn.b >> 2); + buffA[i] = this->cvgSave[x1 + y1 * this->width] >> 5; // A + } + + if (buffA[7] == 7) { + osSyncPrintf("Error, should not be in here \n"); + return; + } + + pxR = pxR2 = buffR[7]; + pxG = pxG2 = buffG[7]; + pxB = pxB2 = buffB[7]; + + for (i = 1; i < 3 * 5; i += 2) { + if (buffA[i] == 7) { + if (pxR < buffR[i]) { + for (j = 1; j < 15; j += 2) { + if ((i != j) && (buffR[j] >= buffR[i]) && (buffA[j] == 7)) { + pxR = buffR[i]; + } + } + } + if (pxG < buffG[i]) { + for (j = 1; j < 15; j += 2) { + if ((i != j) && (buffG[j] >= buffG[i]) && (buffA[j] == 7)) { + pxG = buffG[i]; + } + } + } + if (pxB < buffB[i]) { + for (j = 1; j < 15; j += 2) { + if ((i != j) && (buffB[j] >= buffB[i]) && (buffA[j] == 7)) { + pxB = buffB[i]; + } + } + } + if (1) {} + if (pxR2 > buffR[i]) { + for (j = 1; j < 15; j += 2) { + if ((i != j) && (buffR[j] <= buffR[i]) && (buffA[j] == 7)) { + pxR2 = buffR[i]; + } + } + } + if (pxG2 > buffG[i]) { + for (j = 1; j < 15; j += 2) { + if ((i != j) && (buffG[j] <= buffG[i]) && (buffA[j] == 7)) { + pxG2 = buffG[i]; + } + } + } + if (pxB2 > buffB[i]) { + for (j = 1; j < 15; j += 2) { + if ((i != j) && (buffB[j] <= buffB[i]) && (buffA[j] == 7)) { + pxB2 = buffB[i]; + } + } + } + } + } + + pxR3 = buffR[7] + ((s32)((7 - buffA[7]) * ((pxR + pxR2) - (buffR[7] << 1)) + 4) >> 3); + pxG3 = buffG[7] + ((s32)((7 - buffA[7]) * ((pxG + pxG2) - (buffG[7] << 1)) + 4) >> 3); + pxB3 = buffB[7] + ((s32)((7 - buffA[7]) * ((pxB + pxB2) - (buffB[7] << 1)) + 4) >> 3); + + pxOut.r = pxR3 >> 3; + pxOut.g = pxG3 >> 3; + pxOut.b = pxB3 >> 3; + pxOut.a = 1; + this->fbufSave[x + y * this->width] = pxOut.rgba; +} + +void func_800C2FE4(PreRender* this) { + s32 x; + s32 y; + s32 phi_v0; + u8* buffR = alloca(this->width); + u8* buffG = alloca(this->width); + u8* buffB = alloca(this->width); + s32 pad[3]; + s32 pxR; + s32 pxG; + s32 pxB; + + for (y = 0; y < this->height; y++) { + for (x = 0; x < this->width; x++) { + Color_RGBA16 pxIn; + + pxIn.rgba = this->fbufSave[x + y * this->width]; + buffR[x] = pxIn.r; + buffG[x] = pxIn.g; + buffB[x] = pxIn.b; + } + + for (x = 1; x < this->width - 1; x++) { + Color_RGBA16 pxOut; + s32 a = this->cvgSave[x + y * this->width]; + + a >>= 5; + if (a == 7) { + continue; + } + + if (((HREG(80) == 0xF) ? HREG(81) : 0) != 0) { + if (((HREG(80) == 0xF) ? HREG(81) : 0) != 0) {} + + if (((HREG(80) == 0xF) ? HREG(81) : 0) == 5) { + pxR = 31; + pxG = 0; + pxB = 0; + } else { + u8* temp_s0 = &buffR[x - 1]; + u8* temp_s1 = &buffG[x - 1]; + u8* temp_s2 = &buffB[x - 1]; + + if (((HREG(80) == 0xF) ? HREG(81) : 0) == 3) { + osSyncPrintf("red=%3d %3d %3d %3d grn=%3d %3d %3d %3d blu=%3d %3d %3d %3d \n", temp_s0[0], + temp_s0[1], temp_s0[2], MEDIAN3(temp_s0[0], temp_s0[1], temp_s0[2]), temp_s1[0], + temp_s1[1], temp_s1[2], MEDIAN3(temp_s1[0], temp_s1[1], temp_s1[2]), temp_s2[0], + temp_s2[1], temp_s2[2], MEDIAN3(temp_s2[0], temp_s2[1], temp_s2[2])); + } + + if (((HREG(80) == 0xF) ? HREG(81) : 0) == 1) { + pxR = MEDIAN3(temp_s0[0], temp_s0[1], temp_s0[2]); + pxG = MEDIAN3(temp_s1[0], temp_s1[1], temp_s1[2]); + pxB = MEDIAN3(temp_s2[0], temp_s2[1], temp_s2[2]); + } else { + pxR = MEDIAN3(temp_s0[0], temp_s0[1], temp_s0[2]); + pxG = MEDIAN3(temp_s1[0], temp_s1[1], temp_s1[2]); + pxB = MEDIAN3(temp_s2[0], temp_s2[1], temp_s2[2]); + } + } + pxOut.r = pxR; + pxOut.g = pxG; + pxOut.b = pxB; + pxOut.a = 1; + } + this->fbufSave[x + y * this->width] = pxOut.rgba; + } + } +} + +void PreRender_Calc(PreRender* this) { + s32 x; + s32 y; + + // OTRTODO: I think this is currently causing some kind of buffer overflow...? + return; + + if ((this->cvgSave != NULL) && (this->fbufSave != NULL)) { + + for (y = 0; y < this->height; y++) { + for (x = 0; x < this->width; x++) { + s32 a = this->cvgSave[x + y * this->width]; + + a >>= 5; + a++; + if (a != 8) { + func_800C2500(this, x, y); + } + } + } + + if (HREG(80) == 0xF ? HREG(81) : 0) { + func_800C2FE4(this); + } + } +} diff --git a/soh/src/code/TwoHeadArena.c b/soh/src/code/TwoHeadArena.c new file mode 100644 index 000000000..08f2c7758 --- /dev/null +++ b/soh/src/code/TwoHeadArena.c @@ -0,0 +1,142 @@ +#include "global.h" + +#include + +void THGA_Ct(TwoHeadGfxArena* thga, Gfx* start, size_t size) { + THA_Ct((TwoHeadArena*)thga, start, size); +} + +void THGA_Dt(TwoHeadGfxArena* thga) { + THA_Dt((TwoHeadArena*)thga); +} + +u32 THGA_IsCrash(TwoHeadGfxArena* thga) { + return THA_IsCrash((TwoHeadArena*)thga); +} + +void THGA_Init(TwoHeadGfxArena* thga) { + THA_Init((TwoHeadArena*)thga); +} + +s32 THGA_GetSize(TwoHeadGfxArena* thga) { + return THA_GetSize((TwoHeadArena*)thga); +} + +Gfx* THGA_GetHead(TwoHeadGfxArena* thga) { + return THA_GetHead((TwoHeadArena*)thga); +} + +void THGA_SetHead(TwoHeadGfxArena* thga, Gfx* start) { + THA_SetHead((TwoHeadArena*)thga, start); +} + +Gfx* THGA_GetTail(TwoHeadGfxArena* thga) { + return THA_GetTail((TwoHeadArena*)thga); +} + +Gfx* THGA_AllocStartArray8(TwoHeadGfxArena* thga, u32 count) { + return THA_AllocStart((TwoHeadArena*)thga, count * 8); +} + +Gfx* THGA_AllocStart8(TwoHeadGfxArena* thga) { + return THGA_AllocStartArray8(thga, 1); +} + +Gfx* THGA_AllocStart8Wrapper(TwoHeadGfxArena* thga) { + return THGA_AllocStart8(thga); +} + +Gfx* THGA_AllocEnd(TwoHeadGfxArena* thga, size_t size) { + return THA_AllocEnd((TwoHeadArena*)thga, size); +} + +Gfx* THGA_AllocEndArray64(TwoHeadGfxArena* thga, u32 count) { + return THGA_AllocEnd(thga, count * 0x40); +} + +Gfx* THGA_AllocEnd64(TwoHeadGfxArena* thga) { + return THGA_AllocEnd(thga, 0x40); +} + +Gfx* THGA_AllocEndArray16(TwoHeadGfxArena* thga, u32 count) { + return THGA_AllocEnd(thga, count * 0x10); +} + +Gfx* THGA_AllocEnd16(TwoHeadGfxArena* thga) { + return THGA_AllocEnd(thga, 0x10); +} + +void* THA_GetHead(TwoHeadArena* tha) { + return tha->head; +} + +void THA_SetHead(TwoHeadArena* tha, void* start) { + tha->head = start; +} + +void* THA_GetTail(TwoHeadArena* tha) { + return tha->tail; +} + +void* THA_AllocStart(TwoHeadArena* tha, size_t size) { + void* start = tha->head; + + tha->head = (uintptr_t)tha->head + size; + return start; +} + +void* THA_AllocStart1(TwoHeadArena* tha) { + return THA_AllocStart(tha, 1); +} + +void* THA_AllocEnd(TwoHeadArena* tha, size_t size) { + size_t mask; + + if (size == 8) { + mask = ~7; + } else if (size == 4 || size == 12) { + mask = ~3; + } else if (size == 2 || size == 6 || size == 10 || size == 12 || size == 14) { + mask = ~1; + } else { + mask = (size >= 0x10) ? ~0xF : 0; + } + + tha->tail = (((uintptr_t)tha->tail & mask) - size) & mask; + return tha->tail; +} + +void* THA_AllocEndAlign16(TwoHeadArena* tha, size_t size) { + size_t mask = ~0xF; + + tha->tail = (((uintptr_t)tha->tail & mask) - size) & mask; + return tha->tail; +} + +void* THA_AllocEndAlign(TwoHeadArena* tha, size_t size, size_t mask) { + tha->tail = (((uintptr_t)tha->tail & mask) - size) & mask; + return tha->tail; +} + +s32 THA_GetSize(TwoHeadArena* tha) { + return (uintptr_t)tha->tail - (uintptr_t)tha->head; +} + +u32 THA_IsCrash(TwoHeadArena* tha) { + return THA_GetSize(tha) < 0; +} + +void THA_Init(TwoHeadArena* tha) { + tha->head = tha->bufp; + tha->tail = (uintptr_t)tha->bufp + tha->size; +} + +void THA_Ct(TwoHeadArena* tha, void* ptr, size_t size) { + tha->bufp = ptr; + tha->size = size; + THA_Init(tha); +} + +void THA_Dt(TwoHeadArena* tha) { + memset(tha,0, sizeof(TwoHeadArena)); +} diff --git a/soh/src/code/__osMalloc.c b/soh/src/code/__osMalloc.c new file mode 100644 index 000000000..bba1e6735 --- /dev/null +++ b/soh/src/code/__osMalloc.c @@ -0,0 +1,823 @@ +#include "global.h" +#include "vt.h" + +#include + +#define FILL_ALLOCBLOCK (1 << 0) +#define FILL_FREEBLOCK (1 << 1) +#define CHECK_FREE_BLOCK (1 << 2) + +#define NODE_MAGIC (0x7373) + +#define BLOCK_UNINIT_MAGIC (0xAB) +#define BLOCK_UNINIT_MAGIC_32 (0xABABABAB) +#define BLOCK_ALLOC_MAGIC (0xCD) +#define BLOCK_ALLOC_MAGIC_32 (0xCDCDCDCD) +#define BLOCK_FREE_MAGIC (0xEF) +#define BLOCK_FREE_MAGIC_32 (0xEFEFEFEF) + +OSMesg sArenaLockMsg; +u32 __osMalloc_FreeBlockTest_Enable; + +u32 ArenaImpl_GetFillAllocBlock(Arena* arena) { + return (arena->flag & FILL_ALLOCBLOCK) != 0; +} +u32 ArenaImpl_GetFillFreeBlock(Arena* arena) { + return (arena->flag & FILL_FREEBLOCK) != 0; +} +u32 ArenaImpl_GetCheckFreeBlock(Arena* arena) { + return (arena->flag & CHECK_FREE_BLOCK) != 0; +} + +void ArenaImpl_SetFillAllocBlock(Arena* arena) { + arena->flag |= FILL_ALLOCBLOCK; +} +void ArenaImpl_SetFillFreeBlock(Arena* arena) { + arena->flag |= FILL_FREEBLOCK; +} +void ArenaImpl_SetCheckFreeBlock(Arena* arena) { + arena->flag |= CHECK_FREE_BLOCK; +} + +void ArenaImpl_UnsetFillAllocBlock(Arena* arena) { + arena->flag &= ~FILL_ALLOCBLOCK; +} +void ArenaImpl_UnsetFillFreeBlock(Arena* arena) { + arena->flag &= ~FILL_FREEBLOCK; +} +void ArenaImpl_UnsetCheckFreeBlock(Arena* arena) { + arena->flag &= ~CHECK_FREE_BLOCK; +} + +#if 0 +void ArenaImpl_SetDebugInfo(ArenaNode* node, const char* file, s32 line, Arena* arena) { + node->filename = file; + node->line = line; + node->threadId = osGetThreadId(NULL); + node->arena = arena; + node->time = osGetTime(); +} +#endif +void ArenaImpl_LockInit(Arena* arena) { + osCreateMesgQueue(&arena->lock, &sArenaLockMsg, 1); +} + +void ArenaImpl_Lock(Arena* arena) { + osSendMesg(&arena->lock, NULL, OS_MESG_BLOCK); +} + +void ArenaImpl_Unlock(Arena* arena) { + osRecvMesg(&arena->lock, NULL, OS_MESG_BLOCK); +} + +ArenaNode* ArenaImpl_GetNextBlock(ArenaNode* node) { + ArenaNode* next = node->next; + + if (next != NULL && (next == NULL || (next->magic != NODE_MAGIC))) { + osSyncPrintf(VT_COL(RED, WHITE) "緊急事態!メモリリーク発見! (block=%08x)\n" VT_RST, next); + next = NULL; + node->next = NULL; + } + return next; +} + +ArenaNode* ArenaImpl_GetPrevBlock(ArenaNode* node) { + ArenaNode* prev = node->prev; + + if (prev != NULL && (prev == NULL || (prev->magic != NODE_MAGIC))) { + osSyncPrintf(VT_COL(RED, WHITE) "緊急事態!メモリリーク発見! (block=%08x)\n" VT_RST, prev); + prev = NULL; + node->prev = NULL; + } + return prev; +} + +ArenaNode* ArenaImpl_GetLastBlock(Arena* arena) { + ArenaNode* last = NULL; + ArenaNode* iter; + + if (arena != NULL && arena->head != NULL && arena->head->magic == NODE_MAGIC) { + iter = arena->head; + while (iter != NULL) { + last = iter; + iter = ArenaImpl_GetNextBlock(iter); + } + } + return last; +} + +void __osMallocInit(Arena* arena, void* start, size_t size) { + memset(arena,0, sizeof(Arena)); + ArenaImpl_LockInit(arena); + __osMallocAddBlock(arena, start, size); + arena->isInit = true; +} + +void __osMallocAddBlock(Arena* arena, void* start, ptrdiff_t size) { + s32 diff; + ptrdiff_t size2; + ArenaNode* firstNode; + ArenaNode* lastNode; + + if (start != NULL) { + firstNode = (ArenaNode*)ALIGN16((uintptr_t)start); + diff = (uintptr_t)firstNode - (uintptr_t)start; + size2 = (size - diff) & ~0xF; + + if (size2 > (ptrdiff_t)sizeof(ArenaNode)) { + memset(firstNode, BLOCK_UNINIT_MAGIC, size2); // memset + firstNode->next = NULL; + firstNode->prev = NULL; + firstNode->size = size2 - sizeof(ArenaNode); + firstNode->isFree = true; + firstNode->magic = NODE_MAGIC; + ArenaImpl_Lock(arena); + lastNode = ArenaImpl_GetLastBlock(arena); + if (lastNode == NULL) { + arena->head = firstNode; + arena->start = start; + } else { + firstNode->prev = lastNode; + lastNode->next = firstNode; + } + ArenaImpl_Unlock(arena); + } + } +} + +void ArenaImpl_RemoveAllBlocks(Arena* arena) { + ArenaNode* iter; + ArenaNode* next; + + ArenaImpl_Lock(arena); + + iter = arena->head; + while (iter != NULL) { + next = ArenaImpl_GetNextBlock(iter); + memset(iter, BLOCK_UNINIT_MAGIC, iter->size + sizeof(ArenaNode)); // memset + iter = next; + } + + ArenaImpl_Unlock(arena); +} + +void __osMallocCleanup(Arena* arena) { + ArenaImpl_RemoveAllBlocks(arena); + memset(arena, 0, sizeof(*arena)); +} + +u8 __osMallocIsInitalized(Arena* arena) { + return arena->isInit; +} + +void __osMalloc_FreeBlockTest(Arena* arena, ArenaNode* node) { + ArenaNode* node2 = node; + u32* start; + u32* end; + u32* iter; + + if (__osMalloc_FreeBlockTest_Enable) { + start = (u32*)((uintptr_t)node + sizeof(ArenaNode)); + end = (u32*)((uintptr_t)start + node2->size); + iter = start; + + while (iter < end) { + if (*iter != BLOCK_UNINIT_MAGIC_32 && *iter != BLOCK_FREE_MAGIC_32) { + osSyncPrintf( + VT_COL(RED, WHITE) "緊急事態!メモリリーク検出! (block=%08x s=%08x e=%08x p=%08x)\n" VT_RST, node, + start, end, iter); + __osDisplayArena(arena); + return; + } + iter++; + } + } +} + +void* __osMalloc_NoLockDebug(Arena* arena, size_t size, const char* file, s32 line) { + ArenaNode* iter; + u32 blockSize; + ArenaNode* newNode; + void* alloc = NULL; + ArenaNode* next; + + iter = arena->head; + size = ALIGN16(size); + blockSize = ALIGN16(size) + sizeof(ArenaNode); + + while (iter != NULL) { + if (iter->isFree && iter->size >= size) { + if (arena->flag & CHECK_FREE_BLOCK) { + __osMalloc_FreeBlockTest(arena, iter); + } + + if (blockSize < iter->size) { + newNode = (ArenaNode*)((uintptr_t)iter + blockSize); + newNode->next = ArenaImpl_GetNextBlock(iter); + newNode->prev = iter; + newNode->size = iter->size - blockSize; + newNode->isFree = true; + newNode->magic = NODE_MAGIC; + + iter->next = newNode; + iter->size = size; + next = ArenaImpl_GetNextBlock(newNode); + if (next) { + next->prev = newNode; + } + } + + iter->isFree = false; + //ArenaImpl_SetDebugInfo(iter, file, line, arena); + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); + if (arena->flag & FILL_ALLOCBLOCK) { + memset(alloc, BLOCK_ALLOC_MAGIC, size); + } + + break; + } + + iter = ArenaImpl_GetNextBlock(iter); + } + + return alloc; +} + +void* __osMallocDebug(Arena* arena, size_t size, const char* file, s32 line) { + void* alloc; + + ArenaImpl_Lock(arena); + alloc = __osMalloc_NoLockDebug(arena, size, file, line); + ArenaImpl_Unlock(arena); + + return alloc; +} + +void* __osMallocRDebug(Arena* arena, size_t size, const char* file, s32 line) { + ArenaNode* iter; + ArenaNode* newNode; + u32 blockSize; + ArenaNode* next; + void* allocR = NULL; + + size = ALIGN16(size); + ArenaImpl_Lock(arena); + iter = ArenaImpl_GetLastBlock(arena); + + while (iter != NULL) { + if (iter->isFree && iter->size >= size) { + if (arena->flag & CHECK_FREE_BLOCK) { + __osMalloc_FreeBlockTest(arena, iter); + } + + blockSize = ALIGN16(size) + sizeof(ArenaNode); + if (blockSize < iter->size) { + newNode = (ArenaNode*)((uintptr_t)iter + (iter->size - size)); + newNode->next = ArenaImpl_GetNextBlock(iter); + newNode->prev = iter; + newNode->size = size; + newNode->magic = NODE_MAGIC; + + iter->next = newNode; + iter->size -= blockSize; + next = ArenaImpl_GetNextBlock(newNode); + if (next) { + next->prev = newNode; + } + iter = newNode; + } + + iter->isFree = false; + //ArenaImpl_SetDebugInfo(iter, file, line, arena); + allocR = (void*)((uintptr_t)iter + sizeof(ArenaNode)); + if (arena->flag & FILL_ALLOCBLOCK) { + memset(allocR, BLOCK_ALLOC_MAGIC, size); + } + + break; + } + + iter = ArenaImpl_GetPrevBlock(iter); + } + ArenaImpl_Unlock(arena); + + return allocR; +} + +void* __osMalloc_NoLock(Arena* arena, size_t size) { + ArenaNode* iter; + u32 blockSize; + ArenaNode* newNode; + void* alloc = NULL; + ArenaNode* next; + + iter = arena->head; + size = ALIGN16(size); + blockSize = ALIGN16(size) + sizeof(ArenaNode); + + while (iter != NULL) { + + if (iter->isFree && iter->size >= size) { + if (arena->flag & CHECK_FREE_BLOCK) { + __osMalloc_FreeBlockTest(arena, iter); + } + + if (blockSize < iter->size) { + newNode = (ArenaNode*)((uintptr_t)iter + blockSize); + newNode->next = ArenaImpl_GetNextBlock(iter); + newNode->prev = iter; + newNode->size = iter->size - blockSize; + newNode->isFree = true; + newNode->magic = NODE_MAGIC; + + iter->next = newNode; + iter->size = size; + next = ArenaImpl_GetNextBlock(newNode); + if (next) { + next->prev = newNode; + } + } + + iter->isFree = false; + //ArenaImpl_SetDebugInfo(iter, NULL, 0, arena); + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); + if (arena->flag & FILL_ALLOCBLOCK) { + memset(alloc, BLOCK_ALLOC_MAGIC, size); + } + break; + } + + iter = ArenaImpl_GetNextBlock(iter); + } + + return alloc; +} + +void* __osMalloc(Arena* arena, size_t size) { + void* alloc; + + ArenaImpl_Lock(arena); + alloc = __osMalloc_NoLock(arena, size); + ArenaImpl_Unlock(arena); + + return alloc; +} + +void* __osMallocR(Arena* arena, size_t size) { + ArenaNode* iter; + ArenaNode* newNode; + u32 blockSize; + ArenaNode* next; + void* alloc = NULL; + + size = ALIGN16(size); + ArenaImpl_Lock(arena); + iter = ArenaImpl_GetLastBlock(arena); + + while (iter != NULL) { + if (iter->isFree && iter->size >= size) { + if (arena->flag & CHECK_FREE_BLOCK) { + __osMalloc_FreeBlockTest(arena, iter); + } + + blockSize = ALIGN16(size) + sizeof(ArenaNode); + if (blockSize < iter->size) { + newNode = (ArenaNode*)((uintptr_t)iter + (iter->size - size)); + newNode->next = ArenaImpl_GetNextBlock(iter); + newNode->prev = iter; + newNode->size = size; + newNode->magic = NODE_MAGIC; + + iter->next = newNode; + iter->size -= blockSize; + next = ArenaImpl_GetNextBlock(newNode); + if (next) { + next->prev = newNode; + } + iter = newNode; + } + + iter->isFree = false; + //ArenaImpl_SetDebugInfo(iter, NULL, 0, arena); + alloc = (void*)((uintptr_t)iter + sizeof(ArenaNode)); + if (arena->flag & FILL_ALLOCBLOCK) { + memset(alloc, BLOCK_ALLOC_MAGIC, size); + } + break; + } + iter = ArenaImpl_GetPrevBlock(iter); + } + ArenaImpl_Unlock(arena); + + return alloc; +} + +void __osFree_NoLock(Arena* arena, void* ptr) { + ArenaNode* node; + ArenaNode* next; + ArenaNode* prev; + ArenaNode* newNext; + + if (ptr == NULL) { + return; + } + + node = (ArenaNode*)((uintptr_t)ptr - sizeof(ArenaNode)); + if (node == NULL || node->magic != NODE_MAGIC) { + // "__osFree: Unauthorized release (%08x)" + osSyncPrintf(VT_COL(RED, WHITE) "__osFree:不正解放(%08x)\n" VT_RST, ptr); + return; + } + if (node->isFree) { + osSyncPrintf(VT_COL(RED, WHITE) "__osFree:二重解放(%08x)\n" VT_RST, ptr); // "__osFree: Double release (%08x)" + return; + } + #if 0 + if (arena != node->arena && arena != NULL) { + // "__osFree:Tried to release in a different way than when it was secured (%08x:%08x)" + osSyncPrintf(VT_COL(RED, WHITE) "__osFree:確保時と違う方法で解放しようとした (%08x:%08x)\n" VT_RST, arena, + node->arena); + return; + } +#endif + next = ArenaImpl_GetNextBlock(node); + prev = ArenaImpl_GetPrevBlock(node); + node->isFree = true; + //ArenaImpl_SetDebugInfo(node, NULL, 0, arena); + + if (arena->flag & FILL_FREEBLOCK) { + memset((uintptr_t)node + sizeof(ArenaNode), BLOCK_FREE_MAGIC, node->size); + } + + newNext = next; + if ((uintptr_t)next == (uintptr_t)node + sizeof(ArenaNode) + node->size && next->isFree) { + newNext = ArenaImpl_GetNextBlock(next); + if (newNext != NULL) { + newNext->prev = node; + } + + node->size += next->size + sizeof(ArenaNode); + if (arena->flag & FILL_FREEBLOCK) { + memset(next, BLOCK_FREE_MAGIC, sizeof(ArenaNode)); + } + node->next = newNext; + next = newNext; + } + + if (prev != NULL && prev->isFree && (uintptr_t)node == (uintptr_t)prev + sizeof(ArenaNode) + prev->size) { + if (next) { + next->prev = prev; + } + prev->next = next; + prev->size += node->size + sizeof(ArenaNode); + if (arena->flag & FILL_FREEBLOCK) { + memset(node, BLOCK_FREE_MAGIC, sizeof(ArenaNode)); + } + } +} + +void __osFree(Arena* arena, void* ptr) { + ArenaImpl_Lock(arena); + __osFree_NoLock(arena, ptr); + ArenaImpl_Unlock(arena); +} + +void __osFree_NoLockDebug(Arena* arena, void* ptr, const char* file, s32 line) { + ArenaNode* node; + ArenaNode* next; + ArenaNode* prev; + ArenaNode* newNext; + + if (ptr == NULL) { + return; + } + + node = (ArenaNode*)((uintptr_t)ptr - sizeof(ArenaNode)); + if (node == NULL || node->magic != NODE_MAGIC) { + // "__osFree: Unauthorized release (%08x)" + osSyncPrintf(VT_COL(RED, WHITE) "__osFree:不正解放(%08x) [%s:%d ]\n" VT_RST, ptr, file, line); + return; + } + if (node->isFree) { + // "__osFree: Double release (%08x)" + osSyncPrintf(VT_COL(RED, WHITE) "__osFree:二重解放(%08x) [%s:%d ]\n" VT_RST, ptr, file, line); + return; + } + #if 0 + if (arena != node->arena && arena != NULL) { + // "__osFree:Tried to release in a different way than when it was secured (%08x:%08x)" + osSyncPrintf(VT_COL(RED, WHITE) "__osFree:確保時と違う方法で解放しようとした (%08x:%08x)\n" VT_RST, arena, + node->arena); + return; + } + #endif + next = ArenaImpl_GetNextBlock(node); + prev = ArenaImpl_GetPrevBlock(node); + node->isFree = true; + //ArenaImpl_SetDebugInfo(node, file, line, arena); + + if (arena->flag & FILL_FREEBLOCK) { + memset((uintptr_t)node + sizeof(ArenaNode), BLOCK_FREE_MAGIC, node->size); + } + + newNext = node->next; + if ((uintptr_t)next == (uintptr_t)node + sizeof(ArenaNode) + node->size && next->isFree) { + newNext = ArenaImpl_GetNextBlock(next); + if (newNext != NULL) { + newNext->prev = node; + } + + node->size += next->size + sizeof(ArenaNode); + if (arena->flag & FILL_FREEBLOCK) { + memset(next, BLOCK_FREE_MAGIC, sizeof(ArenaNode)); + } + node->next = newNext; + next = newNext; + } + + if (prev != NULL && prev->isFree && (uintptr_t)node == (uintptr_t)prev + sizeof(ArenaNode) + prev->size) { + if (next != NULL) { + next->prev = prev; + } + prev->next = next; + prev->size += node->size + sizeof(ArenaNode); + if (arena->flag & FILL_FREEBLOCK) { + memset(node, BLOCK_FREE_MAGIC, sizeof(ArenaNode)); + } + } +} + +void __osFreeDebug(Arena* arena, void* ptr, const char* file, s32 line) { + ArenaImpl_Lock(arena); + __osFree_NoLockDebug(arena, ptr, file, line); + ArenaImpl_Unlock(arena); +} + +void* __osRealloc(Arena* arena, void* ptr, size_t newSize) { + void* newAlloc; + ArenaNode* node; + ArenaNode* next; + ArenaNode* newNext; + ArenaNode* overNext; + ArenaNode* newNext2; + ArenaNode* next2; + size_t sizeDiff; + ArenaNode* overNext2; + ArenaNode localCopy; + u32 blockSize; + s32 pad; + + newSize = ALIGN16(newSize); + osSyncPrintf("__osRealloc(%08x, %d)\n", ptr, newSize); + ArenaImpl_Lock(arena); + + if (ptr == NULL) { + ptr = __osMalloc_NoLock(arena, newSize); + } else if (newSize == 0) { + __osFree_NoLock(arena, ptr); + ptr = NULL; + } else { + node = (ArenaNode*)((uintptr_t)ptr - sizeof(ArenaNode)); + if (newSize == node->size) { + // "Does nothing because the memory block size does not change" + osSyncPrintf("メモリブロックサイズが変わらないためなにもしません\n"); + } else if (node->size < newSize) { + next = ArenaImpl_GetNextBlock(node); + sizeDiff = newSize - node->size; + if ((uintptr_t)next == ((uintptr_t)node + node->size + sizeof(ArenaNode)) && next->isFree && next->size >= sizeDiff) { + // "Merge because there is a free block after the current memory block" + osSyncPrintf("現メモリブロックの後ろにフリーブロックがあるので結合します\n"); + next->size -= sizeDiff; + overNext = ArenaImpl_GetNextBlock(next); + newNext = (ArenaNode*)((uintptr_t)next + sizeDiff); + if (overNext != NULL) { + overNext->prev = newNext; + } + node->next = newNext; + node->size = newSize; + func_801068B0(newNext, next, sizeof(ArenaNode)); // memcpy + } else { + // "Allocate a new memory block and move the contents" + osSyncPrintf("新たにメモリブロックを確保して内容を移動します\n"); + newAlloc = __osMalloc_NoLock(arena, newSize); + if (newAlloc != NULL) { + memcpy(newAlloc, ptr, node->size); + __osFree_NoLock(arena, ptr); + } + ptr = newAlloc; + } + } else if (newSize < node->size) { + next2 = ArenaImpl_GetNextBlock(node); + if (next2 != NULL && next2->isFree) { + blockSize = ALIGN16(newSize) + sizeof(ArenaNode); + // "Increased free block behind current memory block" + osSyncPrintf("現メモリブロックの後ろのフリーブロックを大きくしました\n"); + newNext2 = (ArenaNode*)((uintptr_t)node + blockSize); + localCopy = *next2; + *newNext2 = localCopy; + newNext2->size += node->size - newSize; + node->next = newNext2; + node->size = newSize; + overNext2 = ArenaImpl_GetNextBlock(newNext2); + if (overNext2 != NULL) { + overNext2->prev = newNext2; + } + } else if (newSize + sizeof(ArenaNode) < node->size) { + blockSize = ALIGN16(newSize) + sizeof(ArenaNode); + // "Generated because there is no free block after the current memory block" + osSyncPrintf("現メモリブロックの後ろにフリーブロックがないので生成します\n"); + newNext2 = (ArenaNode*)((uintptr_t)node + blockSize); + newNext2->next = ArenaImpl_GetNextBlock(node); + newNext2->prev = node; + newNext2->size = node->size - blockSize; + newNext2->isFree = true; + newNext2->magic = NODE_MAGIC; + node->next = newNext2; + node->size = newSize; + overNext2 = ArenaImpl_GetNextBlock(newNext2); + if (overNext2 != NULL) { + overNext2->prev = newNext2; + } + } else { + // "There is no room to generate free blocks" + osSyncPrintf("フリーブロック生成するだけの空きがありません\n"); + ptr = NULL; + } + } + } + ArenaImpl_Unlock(arena); + + return ptr; +} + +void* __osReallocDebug(Arena* arena, void* ptr, size_t newSize, const char* file, s32 line) { + return __osRealloc(arena, ptr, newSize); +} + +void ArenaImpl_GetSizes(Arena* arena, u32* outMaxFree, u32* outFree, u32* outAlloc) { + ArenaNode* iter; + + ArenaImpl_Lock(arena); + + *outMaxFree = 0; + *outFree = 0; + *outAlloc = 0; + + iter = arena->head; + while (iter != NULL) { + if (iter->isFree) { + *outFree += iter->size; + if (*outMaxFree < iter->size) { + *outMaxFree = iter->size; + } + } else { + *outAlloc += iter->size; + } + + iter = ArenaImpl_GetNextBlock(iter); + } + + ArenaImpl_Unlock(arena); +} + +void __osDisplayArena(Arena* arena) { + size_t freeSize; + size_t allocatedSize; + size_t maxFree; + ArenaNode* iter; + ArenaNode* next; + + if (!__osMallocIsInitalized(arena)) { + osSyncPrintf("アリーナは初期化されていません\n"); // "Arena is not initalized" + return; + } + + ArenaImpl_Lock(arena); + + maxFree = 0; + freeSize = 0; + allocatedSize = 0; + + osSyncPrintf("アリーナの内容 (0x%08x)\n", arena); // "Arena contents (0x%08x)" + // "Memory node range status size [time s ms us ns: TID: src: line]" + osSyncPrintf("メモリブロック範囲 status サイズ [時刻 s ms us ns: TID:src:行]\n"); + + iter = arena->head; + while (iter != NULL) { + if (iter != NULL && iter->magic == NODE_MAGIC) { + next = iter->next; + osSyncPrintf("%08x-%08x%c %s %08x", iter, ((uintptr_t)iter + sizeof(ArenaNode) + iter->size), + (next == NULL) ? '$' : (iter != next->prev ? '!' : ' '), + iter->isFree ? "空き" : "確保", //? "Free" : "Secure" + iter->size); + #if 0 + if (!iter->isFree) { + osSyncPrintf(" [%016llu:%2d:%s:%d]", OS_CYCLES_TO_NSEC(iter->time), iter->threadId, + iter->filename != NULL ? iter->filename : "**NULL**", iter->line); + } + #endif + osSyncPrintf("\n"); + + if (iter->isFree) { + freeSize += iter->size; + if (maxFree < iter->size) { + maxFree = iter->size; + } + } else { + allocatedSize += iter->size; + } + } else { + osSyncPrintf("%08x Block Invalid\n", iter); + next = NULL; + } + iter = next; + } + + // "Total reserved node size 0x%08x bytes" + osSyncPrintf("確保ブロックサイズの合計 0x%08x バイト\n", allocatedSize); + // "Total free node size 0x%08x bytes" + osSyncPrintf("空きブロックサイズの合計 0x%08x バイト\n", freeSize); + // "Maximum free node size 0x%08x bytes" + osSyncPrintf("最大空きブロックサイズ 0x%08x バイト\n", maxFree); + + ArenaImpl_Unlock(arena); +} + +void ArenaImpl_FaultClient(Arena* arena) { + size_t freeSize; + size_t allocatedSize; + size_t maxFree; + ArenaNode* iter; + ArenaNode* next; + + FaultDrawer_Printf("ARENA INFO (0x%08x)\n", arena); + if (!__osMallocIsInitalized(arena)) { + FaultDrawer_Printf("Arena is uninitalized\n", arena); + return; + } + + maxFree = 0; + freeSize = 0; + allocatedSize = 0; + + FaultDrawer_Printf("Memory Block Region status size\n"); + + iter = arena->head; + while (iter != NULL) { + if (iter != NULL && iter->magic == NODE_MAGIC) { + next = iter->next; + FaultDrawer_Printf("%08x-%08x%c %s %08x", iter, ((uintptr_t)iter + sizeof(ArenaNode) + iter->size), + (!next) ? '$' : (iter != next->prev ? '!' : ' '), iter->isFree ? "F" : "A", iter->size); + + FaultDrawer_Printf("\n"); + + if (iter->isFree) { + freeSize += iter->size; + if (maxFree < iter->size) { + maxFree = iter->size; + } + } else { + allocatedSize += iter->size; + } + } else { + FaultDrawer_SetFontColor(0xF801); + FaultDrawer_Printf("%08x Block Invalid\n", iter); + next = NULL; + } + iter = next; + } + + FaultDrawer_SetFontColor(0x7F1); + FaultDrawer_Printf("Total Alloc Block Size %08x\n", allocatedSize); + FaultDrawer_Printf("Total Free Block Size %08x\n", freeSize); + FaultDrawer_Printf("Largest Free Block Size %08x\n", maxFree); +} + +u32 __osCheckArena(Arena* arena) { + ArenaNode* iter; + u32 error = 0; + + ArenaImpl_Lock(arena); + // "Checking the contents of the arena. . . (%08x)" + osSyncPrintf("アリーナの内容をチェックしています... (%08x)\n", arena); + iter = arena->head; + while (iter != NULL) { + if (iter && iter->magic == NODE_MAGIC) { + // "Oops!! (%08x %08x)" + osSyncPrintf(VT_COL(RED, WHITE) "おおっと!! (%08x %08x)\n" VT_RST, iter, iter->magic); + error = 1; + break; + } + iter = ArenaImpl_GetNextBlock(iter); + } + if (error == 0) { + osSyncPrintf("アリーナはまだ、いけそうです\n"); // "The arena is still going well" + } + ArenaImpl_Unlock(arena); + + return error; +} + +u8 func_800FF334(Arena* arena) { + return arena->unk_20; +} diff --git a/soh/src/code/audioMgr.c b/soh/src/code/audioMgr.c new file mode 100644 index 000000000..5b46ad8eb --- /dev/null +++ b/soh/src/code/audioMgr.c @@ -0,0 +1,121 @@ +#include "global.h" +#include "SohHooks.h" +#include + +void func_800C3C80(AudioMgr* audioMgr) { + AudioTask* task; + + task = audioMgr->rspTask; + if (audioMgr->rspTask->taskQueue != NULL) { + osSendMesg(task->taskQueue, NULL, OS_MESG_BLOCK); + } +} + +void AudioMgr_HandleRetrace(AudioMgr* audioMgr) { + AudioTask* rspTask; + + if (SREG(20) > 0) { + audioMgr->rspTask = NULL; + } + if (audioMgr->rspTask != NULL) { + audioMgr->audioTask.next = NULL; + audioMgr->audioTask.flags = 2; + audioMgr->audioTask.framebuffer = NULL; + + audioMgr->audioTask.list = audioMgr->rspTask->task; + audioMgr->audioTask.msgQ = &audioMgr->unk_AC; + + audioMgr->audioTask.msg = NULL; + osSendMesg(&audioMgr->sched->cmdQ, &audioMgr->audioTask, OS_MESG_BLOCK); + Sched_SendEntryMsg(audioMgr->sched); + } + + D_8016A550 = osGetTime(); + if (SREG(20) >= 2) { + rspTask = NULL; + } else { + rspTask = func_800E4FE0(); + } + D_8016A558 += osGetTime() - D_8016A550; + D_8016A550 = 0; + if (audioMgr->rspTask != NULL) { + osRecvMesg(&audioMgr->unk_AC, NULL, OS_MESG_BLOCK); + func_800C3C80(audioMgr); + } + audioMgr->rspTask = rspTask; +} + +void AudioMgr_HandlePRENMI(AudioMgr* audioMgr) { + // "Audio manager received OS_SC_PRE_NMI_MSG" + osSyncPrintf("オーディオマネージャが OS_SC_PRE_NMI_MSG を受け取りました\n"); + Audio_PreNMI(); +} + +void AudioMgr_ThreadEntry(void* arg0) { + AudioMgr* audioMgr = (AudioMgr*)arg0; + s16* msg = NULL; + + //while (true) { + osRecvMesg(&audioMgr->unk_74, (OSMesg*)&msg, OS_MESG_BLOCK); + AudioMgr_HandleRetrace(audioMgr); + /*switch (*msg) { + case OS_SC_RETRACE_MSG: + AudioMgr_HandleRetrace(audioMgr); + while (audioMgr->unk_74.validCount != 0) { + osRecvMesg(&audioMgr->unk_74, (OSMesg*)&msg, OS_MESG_BLOCK); + switch (*msg) { + case OS_SC_RETRACE_MSG: + break; + case OS_SC_PRE_NMI_MSG: + AudioMgr_HandlePRENMI(audioMgr); + break; + } + } + break; + case OS_SC_PRE_NMI_MSG: + AudioMgr_HandlePRENMI(audioMgr); + break; + }*/ + //} +} + +void AudioMgr_Unlock(AudioMgr* audioMgr) { + osRecvMesg(&audioMgr->unk_C8, NULL, OS_MESG_BLOCK); +} + +void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, SchedContext* sched, IrqMgr* irqMgr) { + AudioPlayer_Init(); + + memset(audioMgr,0, sizeof(AudioMgr)); + + audioMgr->sched = sched; + audioMgr->irqMgr = irqMgr; + audioMgr->rspTask = NULL; + + osCreateMesgQueue(&audioMgr->unk_AC, &audioMgr->unk_C4, 1); + osCreateMesgQueue(&audioMgr->unk_74, &audioMgr->unk_8C, 8); + osCreateMesgQueue(&audioMgr->unk_C8, &audioMgr->unk_E0, 1); + + osSendMesg(&audioMgr->unk_AC, NULL, OS_MESG_BLOCK); + + static bool hasInitialized = false; + + if (!hasInitialized) { + IrqMgrClient irqClient; + + osSyncPrintf("オーディオマネージャスレッド実行開始\n"); // "Start running audio manager thread" + Audio_Init(); + AudioLoad_SetDmaHandler(DmaMgr_DmaHandler); + Audio_InitSound(); + osSendMesg(&audioMgr->unk_C8, NULL, OS_MESG_BLOCK); + bind_hook(AUDIO_INIT); + init_hook(0); + call_hook(0); + // Removed due to crash + //IrqMgr_AddClient(audioMgr->irqMgr, &irqClient, &audioMgr->unk_74); + hasInitialized = true; + } + + //osCreateThread(&audioMgr->unk_E8, id, AudioMgr_ThreadEntry, audioMgr, stack, pri); + //osStartThread(&audioMgr->unk_E8); +} diff --git a/soh/src/code/audio_data.c b/soh/src/code/audio_data.c new file mode 100644 index 000000000..540eedca8 --- /dev/null +++ b/soh/src/code/audio_data.c @@ -0,0 +1,680 @@ +#include "global.h" + +// clang-format off +s16 gSawtoothWaveSample[] = { + // Frequency of 1 + 0, 1023, 2047, 3071, 4095, 5119, 6143, 7167, + 8191, 9215, 10239, 11263, 12287, 13311, 14335, 15359, + 16383, 17407, 18431, 19455, 20479, 21503, 22527, 23551, + 24575, 25599, 26623, 27647, 28671, 29695, 30719, 31743, + -32767, -31743, -30719, -29695, -28671, -27647, -26623, -25599, + -24575, -23551, -22527, -21503, -20479, -19455, -18431, -17407, + -16383, -15359, -14335, -13311, -12287, -11263, -10239, -9215, + -8191, -7167, -6143, -5119, -4095, -3071, -2047, -1023, + + // Frequency of 2 + 0, 2047, 4095, 6143, 8191, 10239, 12287, 14335, + 16383, 18431, 20479, 22527, 24575, 26623, 28671, 30719, + -32767, -30719, -28671, -26623, -24575, -22527, -20479, -18431, + -16383, -14335, -12287, -10239, -8191, -6143, -4095, -2047, + 0, 2047, 4095, 6143, 8191, 10239, 12287, 14335, + 16383, 18431, 20479, 22527, 24575, 26623, 28671, 30719, + -32767, -30719, -28671, -26623, -24575, -22527, -20479, -18431, + -16383, -14335, -12287, -10239, -8191, -6143, -4095, -2047, + + // Frequency of 4 + 0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, + -32767, -28671, -24575, -20479, -16383, -12287, -8191, -4095, + 0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, + -32767, -28671, -24575, -20479, -16383, -12287, -8191, -4095, + 0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, + -32767, -28671, -24575, -20479, -16383, -12287, -8191, -4095, + 0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, + -32767, -28671, -24575, -20479, -16383, -12287, -8191, -4095, + + // Frequency of 8 + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, -32767, -24575, -16383, -8191, +}; + +s16 gTriangleWaveSample[] = { + // Frequency of 1 + 0, 2047, 4095, 6143, 8191, 10239, 12287, 14335, + 16383, 18431, 20479, 22527, 24575, 26623, 28671, 30719, + 32767, 30719, 28671, 26623, 24575, 22527, 20479, 18431, + 16383, 14335, 12287, 10239, 8191, 6143, 4095, 2047, + 0, -2047, -4095, -6143, -8191, -10239, -12287, -14335, + -16383, -18431, -20479, -22527, -24575, -26623, -28671, -30719, + -32767, -30719, -28671, -26623, -24575, -22527, -20479, -18431, + -16383, -14335, -12287, -10239, -8191, -6143, -4095, -2047, + + // Frequency of 2 + 0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, + 32767, 28671, 24575, 20479, 16383, 12287, 8191, 4095, + 0, -4095, -8191, -12287, -16383, -20479, -24575, -28671, + -32767, -28671, -24575, -20479, -16383, -12287, -8191, -4095, + 0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, + 32767, 28671, 24575, 20479, 16383, 12287, 8191, 4095, + 0, -4095, -8191, -12287, -16383, -20479, -24575, -28671, + -32767, -28671, -24575, -20479, -16383, -12287, -8191, -4095, + + // Frequency of 4 + 0, 8191, 16383, 24575, 32767, 24575, 16383, 8191, + 0, -8191, -16383, -24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, 32767, 24575, 16383, 8191, + 0, -8191, -16383, -24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, 32767, 24575, 16383, 8191, + 0, -8191, -16383, -24575, -32767, -24575, -16383, -8191, + 0, 8191, 16383, 24575, 32767, 24575, 16383, 8191, + 0, -8191, -16383, -24575, -32767, -24575, -16383, -8191, + + // Frequency of 8 + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, + 0, 16383, 32767, 16383, 0, -16383, -32767, -16383, +}; + +s16 gSineWaveSample[] = { + // Frequency of 1 + 0, 3211, 6392, 9511, 12539, 15446, 18204, 20787, + 23169, 25329, 27244, 28897, 30272, 31356, 32137, 32609, + 32767, 32609, 32137, 31356, 30272, 28897, 27244, 25329, + 23169, 20787, 18204, 15446, 12539, 9511, 6392, 3211, + 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, + -23169, -25329, -27244, -28897, -30272, -31356, -32137, -32609, + -32767, -32609, -32137, -31356, -30272, -28897, -27244, -25329, + -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, + + // Frequency of 2 + 0, 6392, 12539, 18204, 23169, 27244, 30272, 32137, + 32767, 32137, 30272, 27244, 23169, 18204, 12539, 6392, + 0, -6392, -12539, -18204, -23169, -27244, -30272, -32137, + -32767, -32137, -30272, -27244, -23169, -18204, -12539, -6392, + 0, 6392, 12539, 18204, 23169, 27244, 30272, 32137, + 32767, 32137, 30272, 27244, 23169, 18204, 12539, 6392, + 0, -6392, -12539, -18204, -23169, -27244, -30272, -32137, + -32767, -32137, -30272, -27244, -23169, -18204, -12539, -6392, + + // Frequency of 4 + 0, 12539, 23169, 30272, 32767, 30272, 23169, 12539, + 0, -12539, -23169, -30272, -32767, -30272, -23169, -12539, + 0, 12539, 23169, 30272, 32767, 30272, 23169, 12539, + 0, -12539, -23169, -30272, -32767, -30272, -23169, -12539, + 0, 12539, 23169, 30272, 32767, 30272, 23169, 12539, + 0, -12539, -23169, -30272, -32767, -30272, -23169, -12539, + 0, 12539, 23169, 30272, 32767, 30272, 23169, 12539, + 0, -12539, -23169, -30272, -32767, -30272, -23169, -12539, + + // Frequency of 8 + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, + 0, 23169, 32767, 23169, 0, -23169, -32767, -23169, +}; + +s16 gSquareWaveSample[] = { + // Frequency of 1 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, -32767, -32767, -32767, -32767, -32767, -32767, + -32767, -32767, -32767, -32767, -32767, -32767, -32767, -32767, + + // Frequency of 2 + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, -32767, -32767, -32767, -32767, -32767, -32767, + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, 0, 0, 0, 0, + + // Frequency of 4 + -32767, -32767, -32767, -32767, -32767, -32767, -32767, -32767, + 0, 0, 0, 0, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, -32767, -32767, -32767, -32767, + 0, 0, 0, 0, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, -32767, -32767, -32767, -32767, + 0, 0, 0, 0, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, -32767, -32767, -32767, -32767, + 0, 0, 0, 0, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, -32767, -32767, -32767, -32767, + + // Frequency of 8 + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, + 0, 0, 32767, 32767, 0, 0, -32767, -32767, +}; + +s16 gWhiteNoiseSample[] = { + // Frequency of 1 + 0, -25689, -25791, 27803, -27568, -21030, 22174, 6298, + 27071, -18531, 28649, 2284, 3380, 6890, -12682, -21114, + 10000, -24520, 32296, 12248, 15096, 15354, -12021, -31545, + -12929, 6388, -11064, 30456, -30316, -21999, 29691, 27649, + 0, -27649, -29691, 21999, 30316, -30457, 11064, -6387, + 12929, 31544, 12021, -15353, -15096, -12249, -32296, 24521, + -10000, 21113, 12682, -6889, -3380, -2285, -28649, 18532, + -27071, -6299, -22174, 21031, 27568, -27804, 25791, 25690, + + // Frequency of 2 + 0, -25791, -27568, 22174, 27071, 28649, 3380, -12682, + 10000, 32296, 15096, -12021, -12929, -11064, -30316, 29691, + 0, -29691, 30316, 11064, 12929, 12021, -15096, -32296, + -10000, 12682, -3380, -28649, -27071, -22174, 27568, 25791, + 0, -25791, -27568, 22174, 27071, 28649, 3380, -12682, + 10000, 32296, 15096, -12021, -12929, -11064, -30316, 29691, + 0, -29691, 30316, 11064, 12929, 12021, -15096, -32296, + -10000, 12682, -3380, -28649, -27071, -22174, 27568, 25791, + + // Frequency of 4 + 0, -27568, 27071, 3380, 10000, 15096, -12929, -30316, + 0, 30316, 12929, -15096, -10000, -3380, -27071, 27568, + 0, -27568, 27071, 3380, 10000, 15096, -12929, -30316, + 0, 30316, 12929, -15096, -10000, -3380, -27071, 27568, + 0, -27568, 27071, 3380, 10000, 15096, -12929, -30316, + 0, 30316, 12929, -15096, -10000, -3380, -27071, 27568, + 0, -27568, 27071, 3380, 10000, 15096, -12929, -30316, + 0, 30316, 12929, -15096, -10000, -3380, -27071, 27568, + + // Frequency of 8 + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, + 0, 27071, 10000, -12929, 0, 12929, -10000, -27071, +}; + +// Sine White Noise? +s16 D_8012EA90[] = { + // Frequency of 1 + 0, 16316, 20148, 20257, 27209, -32657, 29264, 27259, + -29394, -21494, -26410, 30770, 30033, 29130, 20206, 14129, + 20000, 25677, 19024, 9146, 6921, 4506, -5868, -13122, + -7858, -1885, -7042, -14025, -11903, -8647, -12346, -12396, + 0, 12396, 12346, 8647, 11903, 14024, 7042, 1886, + 7858, 13121, 5868, -4505, -6921, -9147, -19024, -25676, + -20000, -14130, -20206, -29129, -30033, -30771, 26410, 21495, + 29394, -27260, -29264, 32658, -27209, -20258, -20148, -16315, + + // Frequency of 2 + 0, 20148, 27209, 29264, -29394, -26410, 30033, 20206, + 20000, 19024, 6921, -5868, -7858, -7042, -11903, -12346, + 0, 12346, 11903, 7042, 7858, 5868, -6921, -19024, + -20000, -20206, -30033, 26410, 29394, -29264, -27209, -20148, + 0, 20148, 27209, 29264, -29394, -26410, 30033, 20206, + 20000, 19024, 6921, -5868, -7858, -7042, -11903, -12346, + 0, 12346, 11903, 7042, 7858, 5868, -6921, -19024, + -20000, -20206, -30033, 26410, 29394, -29264, -27209, -20148, + + // Frequency of 4 + 0, 27209, -29394, 30033, 20000, 6921, -7858, -11903, + 0, 11903, 7858, -6921, -20000, -30033, 29394, -27209, + 0, 27209, -29394, 30033, 20000, 6921, -7858, -11903, + 0, 11903, 7858, -6921, -20000, -30033, 29394, -27209, + 0, 27209, -29394, 30033, 20000, 6921, -7858, -11903, + 0, 11903, 7858, -6921, -20000, -30033, 29394, -27209, + 0, 27209, -29394, 30033, 20000, 6921, -7858, -11903, + 0, 11903, 7858, -6921, -20000, -30033, 29394, -27209, + + // Frequency of 8 + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, + 0, -29394, 20000, -7858, 0, 7858, -20000, 29394, +}; + +// Pulse Wave (duty cycle = 12.5%) +s16 gEighthPulseWaveSample[] = { + // Frequency of 1 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, -32767, -32767, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + // Frequency of 2 + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, 0, 0, 0, 0, 0, 0, + + // Frequency of 4 + 0, 0, 0, 0, 32767, 0, 0, 0, + 0, 0, 0, 0, -32767, 0, 0, 0, + 0, 0, 0, 0, 32767, 0, 0, 0, + 0, 0, 0, 0, -32767, 0, 0, 0, + 0, 0, 0, 0, 32767, 0, 0, 0, + 0, 0, 0, 0, -32767, 0, 0, 0, + 0, 0, 0, 0, 32767, 0, 0, 0, + 0, 0, 0, 0, -32767, 0, 0, 0, + + // Frequency of 8 + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, +}; + +// Pulse Wave (duty cycle = 25%) +s16 gQuarterPulseWaveSample[] = { + // Frequency of 1 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 32767, 32767, 32767, 32767, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, -32767, -32767, -32767, -32767, -32767, -32767, + 0, 0, 0, 0, 0, 0, 0, 0, + + // Frequency of 2 + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, -32767, -32767, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 32767, 32767, 32767, 32767, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + -32767, -32767, -32767, -32767, 0, 0, 0, 0, + + // Frequency of 4 + 0, 0, 0, 0, 32767, 32767, 0, 0, + 0, 0, 0, 0, -32767, -32767, 0, 0, + 0, 0, 0, 0, 32767, 32767, 0, 0, + 0, 0, 0, 0, -32767, -32767, 0, 0, + 0, 0, 0, 0, 32767, 32767, 0, 0, + 0, 0, 0, 0, -32767, -32767, 0, 0, + 0, 0, 0, 0, 32767, 32767, 0, 0, + 0, 0, 0, 0, -32767, -32767, 0, 0, + + // Frequency of 8 + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, + 0, 0, 32767, 0, 0, 0, -32767, 0, +}; +// clang-format on + +s16* gWaveSamples[] = { + gSawtoothWaveSample, gTriangleWaveSample, gSineWaveSample, gSquareWaveSample, gWhiteNoiseSample, + D_8012EA90, gEighthPulseWaveSample, gQuarterPulseWaveSample, gQuarterPulseWaveSample, +}; + +f32 gBendPitchOneOctaveFrequencies[] = { + 0.5f, 0.5f, 0.502736f, 0.505488f, 0.508254f, 0.511036f, 0.513833f, 0.516645f, 0.519472f, 0.522315f, + 0.525174f, 0.528048f, 0.530938f, 0.533843f, 0.536765f, 0.539702f, 0.542656f, 0.545626f, 0.548612f, 0.551614f, + 0.554633f, 0.557669f, 0.560721f, 0.563789f, 0.566875f, 0.569977f, 0.573097f, 0.576233f, 0.579387f, 0.582558f, + 0.585746f, 0.588951f, 0.592175f, 0.595415f, 0.598674f, 0.60195f, 0.605245f, 0.608557f, 0.611888f, 0.615236f, + 0.618603f, 0.621989f, 0.625393f, 0.628815f, 0.632257f, 0.635717f, 0.639196f, 0.642694f, 0.646212f, 0.649748f, + 0.653304f, 0.65688f, 0.660475f, 0.664089f, 0.667724f, 0.671378f, 0.675052f, 0.678747f, 0.682461f, 0.686196f, + 0.689952f, 0.693727f, 0.697524f, 0.701341f, 0.70518f, 0.709039f, 0.712919f, 0.716821f, 0.720744f, 0.724689f, + 0.728655f, 0.732642f, 0.736652f, 0.740684f, 0.744737f, 0.748813f, 0.752911f, 0.757031f, 0.761175f, 0.76534f, + 0.769529f, 0.77374f, 0.777975f, 0.782232f, 0.786513f, 0.790818f, 0.795146f, 0.799497f, 0.803873f, 0.808272f, + 0.812696f, 0.817144f, 0.821616f, 0.826112f, 0.830633f, 0.835179f, 0.83975f, 0.844346f, 0.848966f, 0.853613f, + 0.858284f, 0.862982f, 0.867704f, 0.872453f, 0.877228f, 0.882029f, 0.886856f, 0.891709f, 0.89659f, 0.901496f, + 0.90643f, 0.911391f, 0.916379f, 0.921394f, 0.926436f, 0.931507f, 0.936604f, 0.94173f, 0.946884f, 0.952066f, + 0.957277f, 0.962516f, 0.967783f, 0.97308f, 0.978405f, 0.98376f, 0.989144f, 0.994557f, 1.0f, 1.005473f, + 1.010975f, 1.016508f, 1.022071f, 1.027665f, 1.033289f, 1.038944f, 1.04463f, 1.050347f, 1.056095f, 1.061875f, + 1.067687f, 1.07353f, 1.079405f, 1.085312f, 1.091252f, 1.097224f, 1.103229f, 1.109267f, 1.115337f, 1.121441f, + 1.127579f, 1.13375f, 1.139955f, 1.146193f, 1.152466f, 1.158773f, 1.165115f, 1.171491f, 1.177903f, 1.184349f, + 1.190831f, 1.197348f, 1.203901f, 1.210489f, 1.217114f, 1.223775f, 1.230473f, 1.237207f, 1.243978f, 1.250786f, + 1.257631f, 1.264514f, 1.271434f, 1.278392f, 1.285389f, 1.292423f, 1.299497f, 1.306608f, 1.313759f, 1.320949f, + 1.328178f, 1.335447f, 1.342756f, 1.350104f, 1.357493f, 1.364922f, 1.372392f, 1.379903f, 1.387455f, 1.395048f, + 1.402683f, 1.41036f, 1.418078f, 1.425839f, 1.433642f, 1.441488f, 1.449377f, 1.457309f, 1.465285f, 1.473304f, + 1.481367f, 1.489474f, 1.497626f, 1.505822f, 1.514063f, 1.522349f, 1.530681f, 1.539058f, 1.547481f, 1.55595f, + 1.564465f, 1.573027f, 1.581636f, 1.590292f, 1.598995f, 1.607746f, 1.616545f, 1.625392f, 1.634287f, 1.643231f, + 1.652224f, 1.661266f, 1.670358f, 1.6795f, 1.688691f, 1.697933f, 1.707225f, 1.716569f, 1.725963f, 1.735409f, + 1.744906f, 1.754456f, 1.764058f, 1.773712f, 1.783419f, 1.793179f, 1.802993f, 1.81286f, 1.822782f, 1.832757f, + 1.842788f, 1.852873f, 1.863013f, 1.873209f, 1.883461f, 1.893768f, 1.904132f, 1.914553f, 1.925031f, 1.935567f, + 1.946159f, 1.95681f, 1.96752f, 1.978287f, 1.989114f, 2.0f, +}; + +f32 gBendPitchTwoSemitonesFrequencies[] = { + 0.890899f, 0.890899f, 0.89171f, 0.892521f, 0.893333f, 0.894146f, 0.89496f, 0.895774f, 0.89659f, 0.897406f, + 0.898222f, 0.89904f, 0.899858f, 0.900677f, 0.901496f, 0.902317f, 0.903138f, 0.90396f, 0.904783f, 0.905606f, + 0.90643f, 0.907255f, 0.908081f, 0.908907f, 0.909734f, 0.910562f, 0.911391f, 0.91222f, 0.91305f, 0.913881f, + 0.914713f, 0.915545f, 0.916379f, 0.917213f, 0.918047f, 0.918883f, 0.919719f, 0.920556f, 0.921394f, 0.922232f, + 0.923072f, 0.923912f, 0.924752f, 0.925594f, 0.926436f, 0.927279f, 0.928123f, 0.928968f, 0.929813f, 0.93066f, + 0.931507f, 0.932354f, 0.933203f, 0.934052f, 0.934902f, 0.935753f, 0.936604f, 0.937457f, 0.93831f, 0.939164f, + 0.940019f, 0.940874f, 0.94173f, 0.942587f, 0.943445f, 0.944304f, 0.945163f, 0.946023f, 0.946884f, 0.947746f, + 0.948608f, 0.949472f, 0.950336f, 0.951201f, 0.952066f, 0.952933f, 0.9538f, 0.954668f, 0.955537f, 0.956406f, + 0.957277f, 0.958148f, 0.95902f, 0.959893f, 0.960766f, 0.961641f, 0.962516f, 0.963392f, 0.964268f, 0.965146f, + 0.966024f, 0.966903f, 0.967783f, 0.968664f, 0.969546f, 0.970428f, 0.971311f, 0.972195f, 0.97308f, 0.973965f, + 0.974852f, 0.975739f, 0.976627f, 0.977516f, 0.978405f, 0.979296f, 0.980187f, 0.981079f, 0.981972f, 0.982865f, + 0.98376f, 0.984655f, 0.985551f, 0.986448f, 0.987346f, 0.988244f, 0.989144f, 0.990044f, 0.990945f, 0.991847f, + 0.992749f, 0.993653f, 0.994557f, 0.995462f, 0.996368f, 0.997275f, 0.998182f, 0.999091f, 1.0f, 1.00091f, + 1.001821f, 1.002733f, 1.003645f, 1.004559f, 1.005473f, 1.006388f, 1.007304f, 1.00822f, 1.009138f, 1.010056f, + 1.010975f, 1.011896f, 1.012816f, 1.013738f, 1.014661f, 1.015584f, 1.016508f, 1.017433f, 1.018359f, 1.019286f, + 1.020214f, 1.021142f, 1.022071f, 1.023002f, 1.023933f, 1.024864f, 1.025797f, 1.026731f, 1.027665f, 1.0286f, + 1.029536f, 1.030473f, 1.031411f, 1.03235f, 1.033289f, 1.03423f, 1.035171f, 1.036113f, 1.037056f, 1.038f, + 1.038944f, 1.03989f, 1.040836f, 1.041783f, 1.042731f, 1.04368f, 1.04463f, 1.045581f, 1.046532f, 1.047485f, + 1.048438f, 1.049392f, 1.050347f, 1.051303f, 1.05226f, 1.053217f, 1.054176f, 1.055135f, 1.056095f, 1.057056f, + 1.058018f, 1.058981f, 1.059945f, 1.06091f, 1.061875f, 1.062842f, 1.063809f, 1.064777f, 1.065746f, 1.066716f, + 1.067687f, 1.068658f, 1.069631f, 1.070604f, 1.071578f, 1.072554f, 1.07353f, 1.074507f, 1.075485f, 1.076463f, + 1.077443f, 1.078424f, 1.079405f, 1.080387f, 1.08137f, 1.082355f, 1.08334f, 1.084325f, 1.085312f, 1.0863f, + 1.087289f, 1.088278f, 1.089268f, 1.09026f, 1.091252f, 1.092245f, 1.093239f, 1.094234f, 1.09523f, 1.096226f, + 1.097224f, 1.098223f, 1.099222f, 1.100222f, 1.101224f, 1.102226f, 1.103229f, 1.104233f, 1.105238f, 1.106244f, + 1.10725f, 1.108258f, 1.109267f, 1.110276f, 1.111287f, 1.112298f, 1.11331f, 1.114323f, 1.115337f, 1.116352f, + 1.117368f, 1.118385f, 1.119403f, 1.120422f, 1.121441f, 1.122462f, +}; + +f32 gNoteFrequencies[] = { + /* 0x00 */ 0.105112f, // NOTE_A0 + /* 0x01 */ 0.111362f, // NOTE_BFLAT0 + /* 0x02 */ 0.117984f, // NOTE_B0 + /* 0x03 */ 0.125f, // NOTE_C1 + /* 0x04 */ 0.132433f, // NOTE_DFLAT1 + /* 0x05 */ 0.140308f, // NOTE_D1 + /* 0x06 */ 0.148651f, // NOTE_EFLAT1 + /* 0x07 */ 0.15749f, // NOTE_E1 + /* 0x08 */ 0.166855f, // NOTE_F1 + /* 0x09 */ 0.176777f, // NOTE_GFLAT1 + /* 0x0A */ 0.187288f, // NOTE_G1 + /* 0x0B */ 0.198425f, // NOTE_AFLAT1 + /* 0x0C */ 0.210224f, // NOTE_A1 + /* 0x0D */ 0.222725f, // NOTE_BFLAT1 + /* 0x0E */ 0.235969f, // NOTE_B1 + /* 0x0F */ 0.25f, // NOTE_C2 + /* 0x10 */ 0.264866f, // NOTE_DFLAT2 + /* 0x11 */ 0.280616f, // NOTE_D2 + /* 0x12 */ 0.297302f, // NOTE_EFLAT2 + /* 0x13 */ 0.31498f, // NOTE_E2 + /* 0x14 */ 0.33371f, // NOTE_F2 + /* 0x15 */ 0.353553f, // NOTE_GFLAT2 + /* 0x16 */ 0.374577f, // NOTE_G2 + /* 0x17 */ 0.39685f, // NOTE_AFLAT2 + /* 0x18 */ 0.420448f, // NOTE_A2 + /* 0x19 */ 0.445449f, // NOTE_BFLAT2 + /* 0x1A */ 0.471937f, // NOTE_B2 + /* 0x1B */ 0.5f, // NOTE_C3 + /* 0x1C */ 0.529732f, // NOTE_DFLAT3 + /* 0x1D */ 0.561231f, // NOTE_D3 + /* 0x1E */ 0.594604f, // NOTE_EFLAT3 + /* 0x1F */ 0.629961f, // NOTE_E3 + /* 0x20 */ 0.66742f, // NOTE_F3 + /* 0x21 */ 0.707107f, // NOTE_GFLAT3 + /* 0x22 */ 0.749154f, // NOTE_G3 + /* 0x23 */ 0.793701f, // NOTE_AFLAT3 + /* 0x24 */ 0.840897f, // NOTE_A3 + /* 0x25 */ 0.890899f, // NOTE_BFLAT3 + /* 0x26 */ 0.943875f, // NOTE_B3 + /* 0x27 */ 1.0f, // NOTE_C4 (Middle C) + /* 0x28 */ 1.059463f, // NOTE_DFLAT4 + /* 0x29 */ 1.122462f, // NOTE_D4 + /* 0x2A */ 1.189207f, // NOTE_EFLAT4 + /* 0x2B */ 1.259921f, // NOTE_E4 + /* 0x2C */ 1.33484f, // NOTE_F4 + /* 0x2D */ 1.414214f, // NOTE_GFLAT4 + /* 0x2E */ 1.498307f, // NOTE_G4 + /* 0x2F */ 1.587401f, // NOTE_AFLAT4 + /* 0x30 */ 1.681793f, // NOTE_A4 + /* 0x31 */ 1.781798f, // NOTE_BFLAT4 + /* 0x32 */ 1.887749f, // NOTE_B4 + /* 0x33 */ 2.0f, // NOTE_C5 + /* 0x34 */ 2.118926f, // NOTE_DFLAT5 + /* 0x35 */ 2.244924f, // NOTE_D5 + /* 0x36 */ 2.378414f, // NOTE_EFLAT5 + /* 0x37 */ 2.519842f, // NOTE_E5 + /* 0x38 */ 2.66968f, // NOTE_F5 + /* 0x39 */ 2.828428f, // NOTE_GFLAT5 + /* 0x3A */ 2.996615f, // NOTE_G5 + /* 0x3B */ 3.174803f, // NOTE_AFLAT5 + /* 0x3C */ 3.363586f, // NOTE_A5 + /* 0x3D */ 3.563596f, // NOTE_BFLAT5 + /* 0x3E */ 3.775498f, // NOTE_B5 + /* 0x3F */ 4.0f, // NOTE_C6 + /* 0x40 */ 4.237853f, // NOTE_DFLAT6 + /* 0x41 */ 4.489849f, // NOTE_D6 + /* 0x42 */ 4.756829f, // NOTE_EFLAT6 + /* 0x43 */ 5.039685f, // NOTE_E6 + /* 0x44 */ 5.33936f, // NOTE_F6 + /* 0x45 */ 5.656855f, // NOTE_GFLAT6 + /* 0x46 */ 5.993229f, // NOTE_G6 + /* 0x47 */ 6.349606f, // NOTE_AFLAT6 + /* 0x48 */ 6.727173f, // NOTE_A6 + /* 0x49 */ 7.127192f, // NOTE_BFLAT6 + /* 0x4A */ 7.550996f, // NOTE_B6 + /* 0x4B */ 8.0f, // NOTE_C7 + /* 0x4C */ 8.475705f, // NOTE_DFLAT7 + /* 0x4D */ 8.979697f, // NOTE_D7 + /* 0x4E */ 9.513658f, // NOTE_EFLAT7 + /* 0x4F */ 10.07937f, // NOTE_E7 + /* 0x50 */ 10.6787205f, // NOTE_F7 + /* 0x51 */ 11.31371f, // NOTE_GFLAT7 + /* 0x52 */ 11.986459f, // NOTE_G7 + /* 0x53 */ 12.699211f, // NOTE_AFLAT7 + /* 0x54 */ 13.454346f, // NOTE_A7 + /* 0x55 */ 14.254383f, // NOTE_BFLAT7 + /* 0x56 */ 15.101993f, // NOTE_B7 + /* 0x57 */ 16.0f, // NOTE_C8 + /* 0x58 */ 16.95141f, // NOTE_DFLAT8 + /* 0x59 */ 17.959395f, // NOTE_D8 + /* 0x5A */ 19.027315f, // NOTE_EFLAT8 + /* 0x5B */ 20.15874f, // NOTE_E8 + /* 0x5C */ 21.35744f, // NOTE_F8 + /* 0x5D */ 22.62742f, // NOTE_GFLAT8 + /* 0x5E */ 23.972918f, // NOTE_G8 + /* 0x5F */ 25.398422f, // NOTE_AFLAT8 + /* 0x60 */ 26.908691f, // NOTE_A8 + /* 0x61 */ 28.508766f, // NOTE_BFLAT8 + /* 0x62 */ 30.203985f, // NOTE_B8 + /* 0x63 */ 32.0f, // NOTE_C9 + /* 0x64 */ 33.90282f, // NOTE_DFLAT9 + /* 0x65 */ 35.91879f, // NOTE_D9 + /* 0x66 */ 38.05463f, // NOTE_EFLAT9 + /* 0x67 */ 40.31748f, // NOTE_E9 + /* 0x68 */ 42.71488f, // NOTE_F9 + /* 0x69 */ 45.25484f, // NOTE_GFLAT9 + /* 0x6A */ 47.945835f, // NOTE_G9 + /* 0x6B */ 50.796845f, // NOTE_AFLAT9 + /* 0x6C */ 53.817383f, // NOTE_A9 + /* 0x6D */ 57.017532f, // NOTE_BFLAT9 + /* 0x6E */ 60.40797f, // NOTE_B9 + /* 0x6F */ 64.0f, // NOTE_C10 + /* 0x70 */ 67.80564f, // NOTE_DFLAT10 + /* 0x71 */ 71.83758f, // NOTE_D10 + /* 0x72 */ 76.10926f, // NOTE_EFLAT10 + /* 0x73 */ 80.63496f, // NOTE_E10 + /* 0x74 */ 85.42976f, // NOTE_F10 + /* 0x75 */ 0.055681f, // NOTE_BFLATNEG1 + /* 0x76 */ 0.058992f, // NOTE_BNEG1 + /* 0x77 */ 0.0625f, // NOTE_C0 + /* 0x78 */ 0.066216f, // NOTE_DFLAT0 + /* 0x79 */ 0.070154f, // NOTE_D0 + /* 0x7A */ 0.074325f, // NOTE_EFLAT0 + /* 0x7B */ 0.078745f, // NOTE_E0 + /* 0x7C */ 0.083427f, // NOTE_F0 + /* 0x7D */ 0.088388f, // NOTE_GFLAT0 + /* 0x7E */ 0.093644f, // NOTE_G0 + /* 0x7F */ 0.099213f, // NOTE_AFLAT0 +}; + +u8 gDefaultShortNoteVelocityTable[] = { + 12, 25, 38, 51, 57, 64, 71, 76, 83, 89, 96, 102, 109, 115, 121, 127, +}; + +u8 gDefaultShortNoteGateTimeTable[] = { + 229, 203, 177, 151, 139, 126, 113, 100, 87, 74, 61, 48, 36, 23, 10, 0, +}; + +AdsrEnvelope gDefaultEnvelope[] = { + // OTRTODO: Byteswapped manually for quick audio support. + { 0x0100, 0x007D }, + { 0xE803, 0x007D }, + { 0xFFFF, 0x0000 }, + { 0x0000, 0x0000 }, + /* { 1, 32000 }, + { 1000, 32000 }, + { -1, 0 }, + { 0, 0 }, + */ +}; + +NoteSubEu gZeroNoteSub = { 0 }; + +NoteSubEu gDefaultNoteSub = { + { 1, 1, 0, 0, 0, 0, 0, 0 }, { 0 }, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +u16 gHeadsetPanQuantization[64] = { + 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, + 16, 14, 12, 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +s32 D_8012FBA4 = 0; + +// clang-format off +s16 D_8012FBA8[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 500, 0, 0, 0, 0, + 0, 0, 0, 500, 0, 0, 0, 0, + 0, 0, 0, 500, 0, 0, 0, 0, + 0, 0, 0, 500, 0, 0, 0, 0, +}; +// clang-format on + +f32 gHeadsetPanVolume[] = { + 1.0f, 0.995386f, 0.990772f, 0.986157f, 0.981543f, 0.976929f, 0.972315f, 0.967701f, 0.963087f, 0.958472f, + 0.953858f, 0.949244f, 0.94463f, 0.940016f, 0.935402f, 0.930787f, 0.926173f, 0.921559f, 0.916945f, 0.912331f, + 0.907717f, 0.903102f, 0.898488f, 0.893874f, 0.88926f, 0.884646f, 0.880031f, 0.875417f, 0.870803f, 0.866189f, + 0.861575f, 0.856961f, 0.852346f, 0.847732f, 0.843118f, 0.838504f, 0.83389f, 0.829276f, 0.824661f, 0.820047f, + 0.815433f, 0.810819f, 0.806205f, 0.801591f, 0.796976f, 0.792362f, 0.787748f, 0.783134f, 0.77852f, 0.773906f, + 0.769291f, 0.764677f, 0.760063f, 0.755449f, 0.750835f, 0.74622f, 0.741606f, 0.736992f, 0.732378f, 0.727764f, + 0.72315f, 0.718535f, 0.713921f, 0.709307f, 0.70537f, 0.70211f, 0.69885f, 0.695591f, 0.692331f, 0.689071f, + 0.685811f, 0.682551f, 0.679291f, 0.676031f, 0.672772f, 0.669512f, 0.666252f, 0.662992f, 0.659732f, 0.656472f, + 0.653213f, 0.649953f, 0.646693f, 0.643433f, 0.640173f, 0.636913f, 0.633654f, 0.630394f, 0.627134f, 0.623874f, + 0.620614f, 0.617354f, 0.614094f, 0.610835f, 0.607575f, 0.604315f, 0.601055f, 0.597795f, 0.594535f, 0.591276f, + 0.588016f, 0.584756f, 0.581496f, 0.578236f, 0.574976f, 0.571717f, 0.568457f, 0.565197f, 0.561937f, 0.558677f, + 0.555417f, 0.552157f, 0.548898f, 0.545638f, 0.542378f, 0.539118f, 0.535858f, 0.532598f, 0.529339f, 0.526079f, + 0.522819f, 0.519559f, 0.516299f, 0.513039f, 0.50978f, 0.50652f, 0.50326f, 0.5f, +}; + +f32 gStereoPanVolume[] = { + 0.707f, 0.716228f, 0.725457f, 0.734685f, 0.743913f, 0.753142f, 0.76237f, 0.771598f, 0.780827f, 0.790055f, + 0.799283f, 0.808512f, 0.81774f, 0.826968f, 0.836197f, 0.845425f, 0.854654f, 0.863882f, 0.87311f, 0.882339f, + 0.891567f, 0.900795f, 0.910024f, 0.919252f, 0.92848f, 0.937709f, 0.946937f, 0.956165f, 0.965394f, 0.974622f, + 0.98385f, 0.993079f, 0.997693f, 0.988465f, 0.979236f, 0.970008f, 0.960779f, 0.951551f, 0.942323f, 0.933095f, + 0.923866f, 0.914638f, 0.905409f, 0.896181f, 0.886953f, 0.877724f, 0.868496f, 0.859268f, 0.850039f, 0.840811f, + 0.831583f, 0.822354f, 0.813126f, 0.803898f, 0.794669f, 0.785441f, 0.776213f, 0.766984f, 0.757756f, 0.748528f, + 0.739299f, 0.730071f, 0.720843f, 0.711614f, 0.695866f, 0.673598f, 0.651331f, 0.629063f, 0.606795f, 0.584528f, + 0.56226f, 0.539992f, 0.517724f, 0.495457f, 0.473189f, 0.450921f, 0.428654f, 0.406386f, 0.384118f, 0.36185f, + 0.339583f, 0.317315f, 0.295047f, 0.27278f, 0.250512f, 0.228244f, 0.205976f, 0.183709f, 0.161441f, 0.139173f, + 0.116905f, 0.094638f, 0.07237f, 0.050102f, 0.027835f, 0.005567f, 0.00835f, 0.019484f, 0.030618f, 0.041752f, + 0.052886f, 0.06402f, 0.075154f, 0.086287f, 0.097421f, 0.108555f, 0.119689f, 0.130823f, 0.141957f, 0.153091f, + 0.164224f, 0.175358f, 0.186492f, 0.197626f, 0.20876f, 0.219894f, 0.231028f, 0.242161f, 0.253295f, 0.264429f, + 0.275563f, 0.286697f, 0.297831f, 0.308965f, 0.320098f, 0.331232f, 0.342366f, 0.3535f, +}; + +f32 gDefaultPanVolume[] = { + 1.0f, 0.999924f, 0.999694f, 0.999312f, 0.998776f, 0.998088f, 0.997248f, 0.996254f, 0.995109f, 0.993811f, + 0.992361f, 0.990759f, 0.989006f, 0.987101f, 0.985045f, 0.982839f, 0.980482f, 0.977976f, 0.97532f, 0.972514f, + 0.96956f, 0.966457f, 0.963207f, 0.959809f, 0.956265f, 0.952574f, 0.948737f, 0.944755f, 0.940629f, 0.936359f, + 0.931946f, 0.92739f, 0.922692f, 0.917853f, 0.912873f, 0.907754f, 0.902497f, 0.897101f, 0.891567f, 0.885898f, + 0.880093f, 0.874153f, 0.868079f, 0.861873f, 0.855535f, 0.849066f, 0.842467f, 0.835739f, 0.828884f, 0.821901f, + 0.814793f, 0.807561f, 0.800204f, 0.792725f, 0.785125f, 0.777405f, 0.769566f, 0.76161f, 0.753536f, 0.745348f, + 0.737045f, 0.72863f, 0.720103f, 0.711466f, 0.70272f, 0.693867f, 0.684908f, 0.675843f, 0.666676f, 0.657406f, + 0.648036f, 0.638567f, 0.629f, 0.619337f, 0.609579f, 0.599728f, 0.589785f, 0.579752f, 0.56963f, 0.559421f, + 0.549126f, 0.538748f, 0.528287f, 0.517745f, 0.507124f, 0.496425f, 0.485651f, 0.474802f, 0.46388f, 0.452888f, + 0.441826f, 0.430697f, 0.419502f, 0.408243f, 0.396921f, 0.385538f, 0.374097f, 0.362598f, 0.351044f, 0.339436f, + 0.327776f, 0.316066f, 0.304308f, 0.292503f, 0.280653f, 0.268761f, 0.256827f, 0.244854f, 0.232844f, 0.220798f, + 0.208718f, 0.196606f, 0.184465f, 0.172295f, 0.160098f, 0.147877f, 0.135634f, 0.12337f, 0.111087f, 0.098786f, + 0.086471f, 0.074143f, 0.061803f, 0.049454f, 0.037097f, 0.024734f, 0.012368f, 0.0f, +}; + +s16 sLowPassFilterData[16 * 8] = { + /* 0x0 */ 0, 0, 0, 32767, 0, 0, 0, 0, // Identity filter (delta function) + /* 0x1 */ 3854, 4188, 4398, 4469, 4398, 4188, 3854, 3416, + /* 0x2 */ 3415, 4314, 4915, 5126, 4915, 4314, 3415, 2351, + /* 0x3 */ 2636, 4433, 5762, 6252, 5762, 4433, 2636, 849, + /* 0x4 */ 1334, 4196, 6646, 7609, 6646, 4196, 1334, -802, + /* 0x5 */ -265, 3421, 7292, 8944, 7292, 3421, -265, -1863, + /* 0x6 */ -1558, 2065, 7146, 9546, 7146, 2065, -1558, -1682, + /* 0x7 */ -2353, 726, 7441, 11028, 7441, 726, -2353, -697, + /* 0x8 */ -2252, -693, 7121, 11962, 7121, -693, -2252, 668, + /* 0x9 */ -1373, -1819, 6299, 12298, 6299, -1819, -1373, 1484, + /* 0xA */ -213, -2740, 5843, 13680, 5843, -2740, -213, 1494, + /* 0xB */ 980, -3081, 4883, 14286, 4883, -3081, 980, 590, + /* 0xC */ 1769, -2973, 3866, 14981, 3866, -2973, 1769, -568, + /* 0xD */ 2023, -2554, 2911, 16397, 2911, -2554, 2023, -1391, + /* 0xE */ 1766, -1918, 2016, 19800, 2016, -1918, 1766, -1564, + /* 0xF */ 841, -853, 863, 26829, 863, -853, 841, -820, +}; + +s16 sHighPassFilterData[15 * 8] = { + /* 0x0 */ -289, -291, -289, 30736, -289, -291, -289, -290, + /* 0x1 */ -464, -467, -467, 29506, -467, -467, -464, -463, + /* 0x2 */ -662, -670, -672, 28101, -672, -670, -662, -656, + /* 0x3 */ -839, -855, -861, 26830, -861, -855, -839, -822, + /* 0x4 */ -996, -1024, -1038, 25685, -1038, -1024, -996, -963, + /* 0x5 */ -1184, -1236, -1266, 24272, -1266, -1236, -1184, -1118, + /* 0x6 */ -1357, -1450, -1506, 22900, -1506, -1450, -1357, -1238, + /* 0x7 */ -1514, -1680, -1784, 21498, -1784, -1680, -1514, -1307, + /* 0x8 */ -1613, -1877, -2048, 20390, -2048, -1877, -1613, -1298, + /* 0x9 */ -1657, -2185, -2559, 18869, -2559, -2185, -1657, -1093, + /* 0xA */ -1524, -2395, -3078, 18030, -3078, -2395, -1524, -739, + /* 0xB */ -1253, -2504, -3621, 17642, -3621, -2504, -1253, -367, + /* 0xC */ -525, -2367, -4732, 17517, -4732, -2367, -525, 0, + /* 0xD */ -34, -1762, -5706, 17503, -5706, -1762, -34, -258, + /* 0xE */ -772, -3, -6985, 17240, -6985, -3, -772, -3, +}; + +s16 D_80130418[8 * 8] = { + /* 0x0 */ 0, 6392, 12539, 18204, 23169, 27244, 30272, 32137, + /* 0x1 */ 32767, 32137, 30272, 27244, 23169, 18204, 12539, 6392, + /* 0x2 */ 0, -3211, -6392, -9511, -12539, -15446, -18204, -20787, + /* 0x3 */ -23169, -25329, -27244, -28897, -30272, -31356, -32137, -32609, + /* 0x4 */ -32767, -32609, -32137, -31356, -30272, -28897, -27244, -25329, + /* 0x5 */ -23169, -25329, -27244, -28897, -30272, -31356, -32137, -32609, + /* 0x6 */ -32767, -32609, -32137, -31356, -30272, -28897, -27244, -25329, + /* 0x7 */ -23169, -20787, -18204, -15446, -12539, -9511, -6392, -3211, +}; diff --git a/soh/src/code/audio_effects.c b/soh/src/code/audio_effects.c new file mode 100644 index 000000000..4813d1c63 --- /dev/null +++ b/soh/src/code/audio_effects.c @@ -0,0 +1,335 @@ +#include +#include "ultra64.h" +#include "global.h" + +#define ROUND(num) floorf((num) * 100) / 100; + +void Audio_SequenceChannelProcessSound(SequenceChannel* channel, s32 recalculateVolume, s32 b) { + f32 channelVolume; + f32 chanFreqScale; + s32 i; + + if (channel->changes.s.volume || recalculateVolume) { + channelVolume = ROUND((channel->volume * (channel->volumeScale * channel->seqPlayer->appliedFadeVolume)) * + channel->seqPlayer->gameVolume); + if (channel->seqPlayer->muted && (channel->muteBehavior & 0x20)) { + channelVolume = channel->seqPlayer->muteVolumeScale * channelVolume; + } + channel->appliedVolume = (channelVolume * channelVolume); + } + + if (channel->changes.s.pan) { + channel->pan = channel->newPan * channel->panChannelWeight; + } + + chanFreqScale = channel->freqScale; + if (b != 0) { + chanFreqScale *= channel->seqPlayer->unk_34; + channel->changes.s.freqScale = true; + } + + for (i = 0; i < 4; i++) { + SequenceLayer* layer = channel->layers[i]; + if (layer != NULL && layer->enabled && layer->note != NULL) { + if (layer->notePropertiesNeedInit) { + layer->noteFreqScale = layer->freqScale * chanFreqScale; + layer->noteVelocity = layer->velocitySquare2 * channel->appliedVolume; + layer->notePan = (channel->pan + layer->pan * (0x80 - channel->panChannelWeight)) >> 7; + layer->notePropertiesNeedInit = false; + } else { + if (channel->changes.s.freqScale) { + layer->noteFreqScale = layer->freqScale * chanFreqScale; + } + if (channel->changes.s.volume || recalculateVolume) { + layer->noteVelocity = layer->velocitySquare2 * channel->appliedVolume; + } + if (channel->changes.s.pan) { + layer->notePan = (channel->pan + layer->pan * (0x80 - channel->panChannelWeight)) >> 7; + } + } + } + } + channel->changes.asByte = 0; +} + +void Audio_SequencePlayerProcessSound(SequencePlayer* seqPlayer) { + s32 i; + + if (seqPlayer->fadeTimer != 0) { + seqPlayer->fadeVolume += seqPlayer->fadeVelocity; + seqPlayer->recalculateVolume = true; + + if (seqPlayer->fadeVolume > 1.0f) { + seqPlayer->fadeVolume = 1.0f; + } + if (seqPlayer->fadeVolume < 0) { + seqPlayer->fadeVolume = 0; + } + + if (--seqPlayer->fadeTimer == 0 && seqPlayer->state == 2) { + AudioSeq_SequencePlayerDisable(seqPlayer); + return; + } + } + + if (seqPlayer->recalculateVolume) { + seqPlayer->appliedFadeVolume = seqPlayer->fadeVolume * seqPlayer->fadeVolumeScale; + } + + for (i = 0; i < 16; i++) { + if (seqPlayer->channels[i]->enabled == 1) { + Audio_SequenceChannelProcessSound(seqPlayer->channels[i], seqPlayer->recalculateVolume, seqPlayer->unk_0b1); + } + } + + seqPlayer->recalculateVolume = false; +} + +f32 Audio_GetPortamentoFreqScale(Portamento* p) { + u32 loResCur; + f32 result; + + p->cur += p->speed; + loResCur = (p->cur >> 8) & 0xFF; + + if (loResCur >= 127) { + loResCur = 127; + p->mode = 0; + } + + result = 1.0f + p->extent * (gBendPitchOneOctaveFrequencies[loResCur + 128] - 1.0f); + return result; +} + +s16 Audio_GetVibratoPitchChange(VibratoState* vib) { + s32 index; + vib->time += (s32)vib->rate; + index = (vib->time >> 10) & 0x3F; + return vib->curve[index]; +} + +f32 Audio_GetVibratoFreqScale(VibratoState* vib) { + static f32 D_80130510 = 0.0f; + static s32 D_80130514 = 0; + f32 pitchChange; + f32 extent; + f32 invExtent; + f32 result; + f32 temp; + SequenceChannel* channel = vib->channel; + + if (vib->delay != 0) { + vib->delay--; + return 1; + } + + //! @bug this probably meant to compare with gAudioContext.sequenceChannelNone. + //! -1 isn't used as a channel pointer anywhere else. + if (channel != ((SequenceChannel*)(-1))) { + if (vib->extentChangeTimer) { + if (vib->extentChangeTimer == 1) { + vib->extent = (s32)channel->vibratoExtentTarget; + } else { + vib->extent += ((s32)channel->vibratoExtentTarget - vib->extent) / (s32)vib->extentChangeTimer; + } + + vib->extentChangeTimer--; + } else if (channel->vibratoExtentTarget != (s32)vib->extent) { + if ((vib->extentChangeTimer = channel->vibratoExtentChangeDelay) == 0) { + vib->extent = (s32)channel->vibratoExtentTarget; + } + } + + if (vib->rateChangeTimer) { + if (vib->rateChangeTimer == 1) { + vib->rate = (s32)channel->vibratoRateTarget; + } else { + vib->rate += ((s32)channel->vibratoRateTarget - vib->rate) / (s32)vib->rateChangeTimer; + } + + vib->rateChangeTimer--; + } else if (channel->vibratoRateTarget != (s32)vib->rate) { + if ((vib->rateChangeTimer = channel->vibratoRateChangeDelay) == 0) { + vib->rate = (s32)channel->vibratoRateTarget; + } + } + } + + if (vib->extent == 0) { + return 1.0f; + } + + pitchChange = Audio_GetVibratoPitchChange(vib) + 32768.0f; + temp = vib->extent / 4096.0f; + extent = temp + 1.0f; + invExtent = 1.0f / extent; + + result = 1.0f / ((extent - invExtent) * pitchChange / 65536.0f + invExtent); + + D_80130510 += result; + D_80130514++; + + return result; +} + +void Audio_NoteVibratoUpdate(Note* note) { + if (note->portamento.mode != 0) { + note->playbackState.portamentoFreqScale = Audio_GetPortamentoFreqScale(¬e->portamento); + } + if (note->vibratoState.active) { + note->playbackState.vibratoFreqScale = Audio_GetVibratoFreqScale(¬e->vibratoState); + } +} + +void Audio_NoteVibratoInit(Note* note) { + VibratoState* vib; + SequenceChannel* channel; + + note->playbackState.vibratoFreqScale = 1.0f; + + vib = ¬e->vibratoState; + + vib->active = 1; + vib->time = 0; + + vib->curve = gWaveSamples[2]; + vib->channel = note->playbackState.parentLayer->channel; + channel = vib->channel; + if ((vib->extentChangeTimer = channel->vibratoExtentChangeDelay) == 0) { + vib->extent = (s32)channel->vibratoExtentTarget; + } else { + vib->extent = (s32)channel->vibratoExtentStart; + } + + if ((vib->rateChangeTimer = channel->vibratoRateChangeDelay) == 0) { + vib->rate = (s32)channel->vibratoRateTarget; + } else { + vib->rate = (s32)channel->vibratoRateStart; + } + vib->delay = channel->vibratoDelay; +} + +void Audio_NotePortamentoInit(Note* note) { + note->playbackState.portamentoFreqScale = 1.0f; + note->portamento = note->playbackState.parentLayer->portamento; +} + +void Audio_AdsrInit(AdsrState* adsr, AdsrEnvelope* envelope, s16* volOut) { + adsr->action.asByte = 0; + adsr->delay = 0; + adsr->envelope = envelope; + adsr->sustain = 0.0f; + adsr->current = 0.0f; + // (An older versions of the audio engine used in Super Mario 64 did + // adsr->volOut = volOut. That line and associated struct member were + // removed, but the function parameter was forgotten and remains.) +} + +f32 Audio_AdsrUpdate(AdsrState* adsr) { + u8 state = adsr->action.s.state; + switch (state) { + case ADSR_STATE_DISABLED: + return 0.0f; + + case ADSR_STATE_INITIAL: { + if (adsr->action.s.hang) { + adsr->action.s.state = ADSR_STATE_HANG; + break; + } + // fallthrough + } + + case ADSR_STATE_START_LOOP: + adsr->envIndex = 0; + adsr->action.s.state = ADSR_STATE_LOOP; + // fallthrough + + retry: + case ADSR_STATE_LOOP: + adsr->delay = (s16)_byteswap_ushort(adsr->envelope[adsr->envIndex].delay); + switch (adsr->delay) { + case ADSR_DISABLE: + adsr->action.s.state = ADSR_STATE_DISABLED; + break; + case ADSR_HANG: + adsr->action.s.state = ADSR_STATE_HANG; + break; + case ADSR_GOTO: + adsr->envIndex = (s16)_byteswap_ushort(adsr->envelope[adsr->envIndex].arg); + goto retry; + case ADSR_RESTART: + adsr->action.s.state = ADSR_STATE_INITIAL; + break; + + default: + adsr->delay *= gAudioContext.audioBufferParameters.unk_24; + if (adsr->delay == 0) { + adsr->delay = 1; + } + adsr->target = (s16)_byteswap_ushort(adsr->envelope[adsr->envIndex].arg) / 32767.0f; + adsr->target = adsr->target * adsr->target; + adsr->velocity = (adsr->target - adsr->current) / adsr->delay; + adsr->action.s.state = ADSR_STATE_FADE; + adsr->envIndex++; + break; + } + if (adsr->action.s.state != ADSR_STATE_FADE) { + break; + } + // fallthrough + + case ADSR_STATE_FADE: + adsr->current += adsr->velocity; + if (--adsr->delay <= 0) { + adsr->action.s.state = ADSR_STATE_LOOP; + } + // fallthrough + + case ADSR_STATE_HANG: + break; + + case ADSR_STATE_DECAY: + case ADSR_STATE_RELEASE: { + adsr->current -= adsr->fadeOutVel; + if (adsr->sustain != 0.0f && state == ADSR_STATE_DECAY) { + if (adsr->current < adsr->sustain) { + adsr->current = adsr->sustain; + adsr->delay = 128; + adsr->action.s.state = ADSR_STATE_SUSTAIN; + } + break; + } + + if (adsr->current < 0.00001f) { + adsr->current = 0.0f; + adsr->action.s.state = ADSR_STATE_DISABLED; + } + break; + } + + case ADSR_STATE_SUSTAIN: + adsr->delay -= 1; + if (adsr->delay == 0) { + adsr->action.s.state = ADSR_STATE_RELEASE; + } + break; + } + + if (adsr->action.s.decay) { + adsr->action.s.state = ADSR_STATE_DECAY; + adsr->action.s.decay = false; + } + + if (adsr->action.s.release) { + adsr->action.s.state = ADSR_STATE_RELEASE; + adsr->action.s.release = false; + } + + if (adsr->current < 0.0f) { + return 0.0f; + } + if (adsr->current > 1.0f) { + return 1.0f; + } + return adsr->current; +} diff --git a/soh/src/code/audio_heap.c b/soh/src/code/audio_heap.c new file mode 100644 index 000000000..765fd8b09 --- /dev/null +++ b/soh/src/code/audio_heap.c @@ -0,0 +1,1405 @@ +#include "ultra64.h" +#include "global.h" + +void AudioHeap_InitSampleCaches(u32 persistentSize, u32 temporarySize); +SampleCacheEntry* AudioHeap_AllocTemporarySampleCacheEntry(size_t size); +SampleCacheEntry* AudioHeap_AllocPersistentSampleCacheEntry(size_t size); +void AudioHeap_DiscardSampleCacheEntry(SampleCacheEntry* entry); +void AudioHeap_UnapplySampleCache(SampleCacheEntry* entry, SoundFontSample* sample); +void AudioHeap_DiscardSampleCaches(void); +void AudioHeap_DiscardSampleBank(s32 sampleBankId); +void AudioHeap_DiscardSampleBanks(void); + +f32 func_800DDE20(f32 arg0) { + return 256.0f * gAudioContext.audioBufferParameters.unkUpdatesPerFrameScaled / arg0; +} + +void func_800DDE3C(void) { + s32 i; + + gAudioContext.unk_3520[255] = func_800DDE20(0.25f); + gAudioContext.unk_3520[254] = func_800DDE20(0.33f); + gAudioContext.unk_3520[253] = func_800DDE20(0.5f); + gAudioContext.unk_3520[252] = func_800DDE20(0.66f); + gAudioContext.unk_3520[251] = func_800DDE20(0.75f); + + for (i = 128; i < 251; i++) { + gAudioContext.unk_3520[i] = func_800DDE20(251 - i); + } + + for (i = 16; i < 128; i++) { + gAudioContext.unk_3520[i] = func_800DDE20(4 * (143 - i)); + } + + for (i = 1; i < 16; i++) { + gAudioContext.unk_3520[i] = func_800DDE20(60 * (23 - i)); + } + + gAudioContext.unk_3520[0] = 0.0f; +} + +void AudioHeap_ResetLoadStatus(void) { + s32 i; + + for (i = 0; i < 0x30; i++) { + if (gAudioContext.fontLoadStatus[i] != 5) { + gAudioContext.fontLoadStatus[i] = 0; + } + } + + for (i = 0; i < 0x30; i++) { + if (gAudioContext.sampleFontLoadStatus[i] != 5) { + gAudioContext.sampleFontLoadStatus[i] = 0; + } + } + + for (i = 0; i < 0x80; i++) { + if (gAudioContext.seqLoadStatus[i] != 5) { + gAudioContext.seqLoadStatus[i] = 0; + } + } +} + +void AudioHeap_DiscardFont(s32 fontId) { + s32 i; + + for (i = 0; i < gAudioContext.numNotes; i++) { + Note* note = &gAudioContext.notes[i]; + + if (note->playbackState.fontId == fontId) { + if (note->playbackState.unk_04 == 0 && note->playbackState.priority != 0) { + note->playbackState.parentLayer->enabled = false; + note->playbackState.parentLayer->finished = true; + } + Audio_NoteDisable(note); + Audio_AudioListRemove(¬e->listItem); + AudioSeq_AudioListPushBack(&gAudioContext.noteFreeLists.disabled, ¬e->listItem); + } + } +} + +void AudioHeap_ReleaseNotesForFont(s32 fontId) { + s32 i; + + for (i = 0; i < gAudioContext.numNotes; i++) { + Note* note = &gAudioContext.notes[i]; + NotePlaybackState* state = ¬e->playbackState; + + if (state->fontId == fontId) { + if (state->priority != 0 && state->adsr.action.s.state == ADSR_STATE_DECAY) { + state->priority = 1; + state->adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; + state->adsr.action.s.release = true; + } + } + } +} + +void AudioHeap_DiscardSequence(s32 seqId) { + s32 i; + + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + if (gAudioContext.seqPlayers[i].enabled && gAudioContext.seqPlayers[i].seqId == seqId) { + AudioSeq_SequencePlayerDisable(&gAudioContext.seqPlayers[i]); + } + } +} + +void AudioHeap_WritebackDCache(void* mem, size_t size) { + Audio_WritebackDCache(mem, size); +} + +void* AudioHeap_AllocZeroedAttemptExternal(AudioAllocPool* pool, size_t size) { + void* ret = NULL; + + if (gAudioContext.externalPool.start != 0) { + ret = AudioHeap_AllocZeroed(&gAudioContext.externalPool, size); + } + if (ret == NULL) { + ret = AudioHeap_AllocZeroed(pool, size); + } + return ret; +} + +void* AudioHeap_AllocAttemptExternal(AudioAllocPool* pool, size_t size) { + void* ret = NULL; + + if (gAudioContext.externalPool.start != NULL) { + ret = AudioHeap_Alloc(&gAudioContext.externalPool, size); + } + if (ret == NULL) { + ret = AudioHeap_Alloc(pool, size); + } + return ret; +} + +void* AudioHeap_AllocDmaMemory(AudioAllocPool* pool, size_t size) { + void* ret; + + ret = AudioHeap_Alloc(pool, size); + if (ret != NULL) { + AudioHeap_WritebackDCache(ret, size); + } + return ret; +} + +void* AudioHeap_AllocDmaMemoryZeroed(AudioAllocPool* pool, size_t size) { + void* ret; + + ret = AudioHeap_AllocZeroed(pool, size); + if (ret != NULL) { + AudioHeap_WritebackDCache(ret, size); + } + return ret; +} + +void* AudioHeap_AllocZeroed(AudioAllocPool* pool, size_t size) { + u8* ret = AudioHeap_Alloc(pool, size); + u8* ptr; + + if (ret != NULL) { + for (ptr = ret; ptr < pool->cur; ptr++) { + *ptr = 0; + } + } + + return ret; +} + +void* AudioHeap_Alloc(AudioAllocPool* pool, size_t size) { + u32 aligned = ALIGN16(size); + u8* ret = pool->cur; + + if (pool->start + pool->size >= pool->cur + aligned) { + pool->cur += aligned; + } else { + return NULL; + } + pool->count++; + return ret; +} + +void AudioHeap_AllocPoolInit(AudioAllocPool* pool, void* mem, size_t size) { + pool->cur = pool->start = (u8*)ALIGN16((uintptr_t)mem); + pool->size = size - ((uintptr_t)mem & 0xF); + pool->count = 0; +} + +void AudioHeap_PersistentCacheClear(AudioPersistentCache* persistent) { + persistent->pool.count = 0; + persistent->numEntries = 0; + persistent->pool.cur = persistent->pool.start; +} + +void AudioHeap_TemporaryCacheClear(AudioTemporaryCache* temporary) { + temporary->pool.count = 0; + temporary->pool.cur = temporary->pool.start; + temporary->nextSide = 0; + temporary->entries[0].ptr = temporary->pool.start; + temporary->entries[1].ptr = temporary->pool.start + temporary->pool.size; + temporary->entries[0].id = -1; + temporary->entries[1].id = -1; +} + +void AudioHeap_ResetPool(AudioAllocPool* pool) { + pool->count = 0; + pool->cur = pool->start; +} + +void AudioHeap_PopCache(s32 tableType) { + AudioCache* loadedPool; + AudioAllocPool* persistentPool; + AudioPersistentCache* persistent; + void* entryPtr; + u8* table; + + switch (tableType) { + case SEQUENCE_TABLE: + loadedPool = &gAudioContext.seqCache; + table = gAudioContext.seqLoadStatus; + break; + case FONT_TABLE: + loadedPool = &gAudioContext.fontCache; + table = gAudioContext.fontLoadStatus; + break; + case SAMPLE_TABLE: + loadedPool = &gAudioContext.sampleBankCache; + table = gAudioContext.sampleFontLoadStatus; + break; + } + + persistent = &loadedPool->persistent; + persistentPool = &persistent->pool; + + if (persistent->numEntries == 0) { + return; + } + + entryPtr = persistent->entries[persistent->numEntries - 1].ptr; + persistentPool->cur = entryPtr; + persistentPool->count--; + + if (tableType == SAMPLE_TABLE) { + AudioHeap_DiscardSampleBank(persistent->entries[persistent->numEntries - 1].id); + } + if (tableType == FONT_TABLE) { + AudioHeap_DiscardFont(persistent->entries[persistent->numEntries - 1].id); + } + table[persistent->entries[persistent->numEntries - 1].id] = 0; + persistent->numEntries--; +} + +void AudioHeap_InitMainPools(ptrdiff_t initPoolSize) { + AudioHeap_AllocPoolInit(&gAudioContext.audioInitPool, gAudioContext.audioHeap, initPoolSize); + AudioHeap_AllocPoolInit(&gAudioContext.audioSessionPool, gAudioContext.audioHeap + initPoolSize, + gAudioContext.audioHeapSize - initPoolSize); + gAudioContext.externalPool.start = NULL; +} + +void AudioHeap_SessionPoolsInit(AudioPoolSplit4* split) { + gAudioContext.audioSessionPool.cur = gAudioContext.audioSessionPool.start; + AudioHeap_AllocPoolInit(&gAudioContext.notesAndBuffersPool, + AudioHeap_Alloc(&gAudioContext.audioSessionPool, split->wantSeq), split->wantSeq); + AudioHeap_AllocPoolInit(&gAudioContext.cachePool, + AudioHeap_Alloc(&gAudioContext.audioSessionPool, split->wantCustom), split->wantCustom); +} + +void AudioHeap_CachePoolInit(AudioPoolSplit2* split) { + gAudioContext.cachePool.cur = gAudioContext.cachePool.start; + AudioHeap_AllocPoolInit(&gAudioContext.persistentCommonPool, + AudioHeap_Alloc(&gAudioContext.cachePool, split->wantPersistent), split->wantPersistent); + AudioHeap_AllocPoolInit(&gAudioContext.temporaryCommonPool, + AudioHeap_Alloc(&gAudioContext.cachePool, split->wantTemporary), split->wantTemporary); +} + +void AudioHeap_PersistentCachesInit(AudioPoolSplit3* split) { + gAudioContext.persistentCommonPool.cur = gAudioContext.persistentCommonPool.start; + AudioHeap_AllocPoolInit(&gAudioContext.seqCache.persistent.pool, + AudioHeap_Alloc(&gAudioContext.persistentCommonPool, split->wantSeq), split->wantSeq); + AudioHeap_AllocPoolInit(&gAudioContext.fontCache.persistent.pool, + AudioHeap_Alloc(&gAudioContext.persistentCommonPool, split->wantFont), split->wantFont); + AudioHeap_AllocPoolInit(&gAudioContext.sampleBankCache.persistent.pool, + AudioHeap_Alloc(&gAudioContext.persistentCommonPool, split->wantSample), split->wantSample); + AudioHeap_PersistentCacheClear(&gAudioContext.seqCache.persistent); + AudioHeap_PersistentCacheClear(&gAudioContext.fontCache.persistent); + AudioHeap_PersistentCacheClear(&gAudioContext.sampleBankCache.persistent); +} + +void AudioHeap_TemporaryCachesInit(AudioPoolSplit3* split) { + gAudioContext.temporaryCommonPool.cur = gAudioContext.temporaryCommonPool.start; + AudioHeap_AllocPoolInit(&gAudioContext.seqCache.temporary.pool, + AudioHeap_Alloc(&gAudioContext.temporaryCommonPool, split->wantSeq), split->wantSeq); + AudioHeap_AllocPoolInit(&gAudioContext.fontCache.temporary.pool, + AudioHeap_Alloc(&gAudioContext.temporaryCommonPool, split->wantFont), split->wantFont); + AudioHeap_AllocPoolInit(&gAudioContext.sampleBankCache.temporary.pool, + AudioHeap_Alloc(&gAudioContext.temporaryCommonPool, split->wantSample), split->wantSample); + AudioHeap_TemporaryCacheClear(&gAudioContext.seqCache.temporary); + AudioHeap_TemporaryCacheClear(&gAudioContext.fontCache.temporary); + AudioHeap_TemporaryCacheClear(&gAudioContext.sampleBankCache.temporary); +} + +void* AudioHeap_AllocCached(s32 tableType, ptrdiff_t size, s32 cache, s32 id) { + AudioCache* loadedPool; + AudioTemporaryCache* tp; + AudioAllocPool* pool; + void* mem; + void* ret; + u8 firstVal; + u8 secondVal; + s32 i; + u8* table; + s32 side; + + switch (tableType) { + case SEQUENCE_TABLE: + loadedPool = &gAudioContext.seqCache; + table = gAudioContext.seqLoadStatus; + break; + case FONT_TABLE: + loadedPool = &gAudioContext.fontCache; + table = gAudioContext.fontLoadStatus; + break; + case SAMPLE_TABLE: + loadedPool = &gAudioContext.sampleBankCache; + table = gAudioContext.sampleFontLoadStatus; + break; + } + + if (cache == CACHE_TEMPORARY) { + tp = &loadedPool->temporary; + pool = &tp->pool; + + if (pool->size < size) { + return NULL; + } + + firstVal = (tp->entries[0].id == -1) ? 0 : table[tp->entries[0].id]; + secondVal = (tp->entries[1].id == -1) ? 0 : table[tp->entries[1].id]; + + if (tableType == FONT_TABLE) { + if (firstVal == 4) { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.notes[i].playbackState.fontId == tp->entries[0].id && + gAudioContext.notes[i].noteSubEu.bitField0.enabled != 0) { + break; + } + } + + if (i == gAudioContext.numNotes) { + AudioLoad_SetFontLoadStatus(tp->entries[0].id, 3); + firstVal = 3; + } + } + + if (secondVal == 4) { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.notes[i].playbackState.fontId == tp->entries[1].id && + gAudioContext.notes[i].noteSubEu.bitField0.enabled != 0) { + break; + } + } + + if (i == gAudioContext.numNotes) { + AudioLoad_SetFontLoadStatus(tp->entries[1].id, 3); + secondVal = 3; + } + } + } + + if (firstVal == 0) { + tp->nextSide = 0; + } else if (secondVal == 0) { + tp->nextSide = 1; + } else if (firstVal == 3 && secondVal == 3) { + // Use the opposite side from last time. + } else if (firstVal == 3) { + tp->nextSide = 0; + } else if (secondVal == 3) { + tp->nextSide = 1; + } else { + // Check if there is a side which isn't in active use, if so, evict that one. + if (tableType == SEQUENCE_TABLE) { + if (firstVal == 2) { + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + if (gAudioContext.seqPlayers[i].enabled != 0 && + gAudioContext.seqPlayers[i].seqId == tp->entries[0].id) { + break; + } + } + + if (i == gAudioContext.audioBufferParameters.numSequencePlayers) { + tp->nextSide = 0; + goto done; + } + } + + if (secondVal == 2) { + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + if (gAudioContext.seqPlayers[i].enabled != 0 && + gAudioContext.seqPlayers[i].seqId == tp->entries[1].id) { + break; + } + } + + if (i == gAudioContext.audioBufferParameters.numSequencePlayers) { + tp->nextSide = 1; + goto done; + } + } + } else if (tableType == FONT_TABLE) { + if (firstVal == 2) { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.notes[i].playbackState.fontId == tp->entries[0].id && + gAudioContext.notes[i].noteSubEu.bitField0.enabled != 0) { + break; + } + } + if (i == gAudioContext.numNotes) { + tp->nextSide = 0; + goto done; + } + } + + if (secondVal == 2) { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.notes[i].playbackState.fontId == tp->entries[1].id && + gAudioContext.notes[i].noteSubEu.bitField0.enabled != 0) { + break; + } + } + if (i == gAudioContext.numNotes) { + tp->nextSide = 1; + goto done; + } + } + } + + // No such luck. Evict the side that wasn't chosen last time, except + // if it is being loaded into. + if (tp->nextSide == 0) { + if (firstVal == 1) { + if (secondVal == 1) { + goto fail; + } + tp->nextSide = 1; + } + } else { + if (secondVal == 1) { + if (firstVal == 1) { + goto fail; + } + tp->nextSide = 0; + } + } + + if (0) { + fail: + // Both sides are being loaded into. + return NULL; + } + } + done: + + side = tp->nextSide; + + if (tp->entries[side].id != -1) { + if (tableType == SAMPLE_TABLE) { + AudioHeap_DiscardSampleBank(tp->entries[side].id); + } + table[tp->entries[side].id] = 0; + if (tableType == FONT_TABLE) { + AudioHeap_DiscardFont(tp->entries[side].id); + } + } + + switch (side) { + case 0: + tp->entries[0].ptr = pool->start; + tp->entries[0].id = id; + tp->entries[0].size = size; + pool->cur = pool->start + size; + + if (tp->entries[1].id != -1 && tp->entries[1].ptr < pool->cur) { + if (tableType == SAMPLE_TABLE) { + AudioHeap_DiscardSampleBank(tp->entries[1].id); + } + + table[tp->entries[1].id] = 0; + switch (tableType) { + case SEQUENCE_TABLE: + AudioHeap_DiscardSequence((s32)tp->entries[1].id); + break; + case FONT_TABLE: + AudioHeap_DiscardFont((s32)tp->entries[1].id); + break; + } + + tp->entries[1].id = -1; + tp->entries[1].ptr = pool->start + pool->size; + } + + ret = tp->entries[0].ptr; + break; + + case 1: + tp->entries[1].ptr = (u8*)((uintptr_t)(pool->start + pool->size - size) & ~0xF); + tp->entries[1].id = id; + tp->entries[1].size = size; + if (tp->entries[0].id != -1 && tp->entries[1].ptr < pool->cur) { + if (tableType == SAMPLE_TABLE) { + AudioHeap_DiscardSampleBank(tp->entries[0].id); + } + + table[tp->entries[0].id] = 0; + switch (tableType) { + case SEQUENCE_TABLE: + AudioHeap_DiscardSequence(tp->entries[0].id); + break; + case FONT_TABLE: + AudioHeap_DiscardFont(tp->entries[0].id); + break; + } + + tp->entries[0].id = -1; + pool->cur = pool->start; + } + ret = tp->entries[1].ptr; + break; + + default: + return NULL; + } + + tp->nextSide ^= 1; + return ret; + } + + mem = AudioHeap_Alloc(&loadedPool->persistent.pool, size); + loadedPool->persistent.entries[loadedPool->persistent.numEntries].ptr = mem; + + if (mem == NULL) { + switch (cache) { + case CACHE_EITHER: + return AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, id); + + case CACHE_TEMPORARY: + case CACHE_PERSISTENT: + return NULL; + } + } + + loadedPool->persistent.entries[loadedPool->persistent.numEntries].id = id; + loadedPool->persistent.entries[loadedPool->persistent.numEntries].size = size; + return loadedPool->persistent.entries[loadedPool->persistent.numEntries++].ptr; +} + +void* AudioHeap_SearchCaches(s32 tableType, s32 cache, s32 id) { + void* ret; + + // Always search the permanent cache in addition to the regular ones. + ret = AudioHeap_SearchPermanentCache(tableType, id); + if (ret != NULL) { + return ret; + } + if (cache == CACHE_PERMANENT) { + return NULL; + } + return AudioHeap_SearchRegularCaches(tableType, cache, id); +} + +void* AudioHeap_SearchRegularCaches(s32 tableType, s32 cache, s32 id) { + u32 i; + AudioCache* loadedPool; + AudioTemporaryCache* temporary; + AudioPersistentCache* persistent; + + switch (tableType) { + case SEQUENCE_TABLE: + loadedPool = &gAudioContext.seqCache; + break; + case FONT_TABLE: + loadedPool = &gAudioContext.fontCache; + break; + case SAMPLE_TABLE: + loadedPool = &gAudioContext.sampleBankCache; + break; + } + + temporary = &loadedPool->temporary; + if (cache == CACHE_TEMPORARY) { + if (temporary->entries[0].id == id) { + temporary->nextSide = 1; + return temporary->entries[0].ptr; + } else if (temporary->entries[1].id == id) { + temporary->nextSide = 0; + return temporary->entries[1].ptr; + } else { + return NULL; + } + } + + persistent = &loadedPool->persistent; + for (i = 0; i < persistent->numEntries; i++) { + if (persistent->entries[i].id == id) { + return persistent->entries[i].ptr; + } + } + + if (cache == CACHE_EITHER) { + return AudioHeap_SearchCaches(tableType, CACHE_TEMPORARY, id); + } + return NULL; +} + +void func_800DF1D8(f32 p, f32 q, u16* out) { + // With the bug below fixed, this mysterious unused function computes two recurrences + // out[0..7] = a_i, out[8..15] = b_i, where + // a_{-2} = b_{-1} = 262159 = 2^18 + 15 + // a_{-1} = b_{-2} = 0 + // a_i = q * a_{i-1} + p * a_{i-2} + // b_i = q * b_{i-1} + p * b_{i-2} + // These grow exponentially if p < -1 or p + |q| > 1. + s32 i; + f32 tmp[16]; + + tmp[0] = (f32)(q * 262159.0f); + tmp[8] = (f32)(p * 262159.0f); + tmp[1] = (f32)((q * p) * 262159.0f); + tmp[9] = (f32)(((p * p) + q) * 262159.0f); + + for (i = 2; i < 8; i++) { + //! @bug value should be stored to tmp[i] and tmp[8 + i], otherwise we read + //! garbage in later loop iterations. + out[i] = q * tmp[i - 2] + p * tmp[i - 1]; + out[8 + i] = q * tmp[6 + i] + p * tmp[7 + i]; + } + + for (i = 0; i < 16; i++) { + out[i] = tmp[i]; + } +} + +void AudioHeap_ClearFilter(s16* filter) { + s32 i; + + for (i = 0; i < 8; i++) { + filter[i] = 0; + } +} + +void AudioHeap_LoadLowPassFilter(s16* filter, s32 cutoff) { + s32 i; + s16* ptr = &sLowPassFilterData[8 * cutoff]; + + for (i = 0; i < 8; i++) { + filter[i] = ptr[i]; + } +} + +void AudioHeap_LoadHighPassFilter(s16* filter, s32 cutoff) { + s32 i; + s16* ptr = &sHighPassFilterData[8 * (cutoff - 1)]; + + for (i = 0; i < 8; i++) { + filter[i] = ptr[i]; + } +} + +void AudioHeap_LoadFilter(s16* filter, s32 lowPassCutoff, s32 highPassCutoff) { + s32 i; + + if (lowPassCutoff == 0 && highPassCutoff == 0) { + // Identity filter + AudioHeap_LoadLowPassFilter(filter, 0); + } else if (highPassCutoff == 0) { + AudioHeap_LoadLowPassFilter(filter, lowPassCutoff); + } else if (lowPassCutoff == 0) { + AudioHeap_LoadHighPassFilter(filter, highPassCutoff); + } else { + s16* ptr1 = &sLowPassFilterData[8 * lowPassCutoff]; + s16* ptr2 = &sHighPassFilterData[8 * (highPassCutoff - 1)]; + for (i = 0; i < 8; i++) { + filter[i] = (ptr1[i] + ptr2[i]) / 2; + } + } +} + +void AudioHeap_UpdateReverb(SynthesisReverb* reverb) { +} + +void AudioHeap_UpdateReverbs(void) { + s32 count; + s32 i; + s32 j; + + if (gAudioContext.audioBufferParameters.specUnk4 == 2) { + count = 2; + } else { + count = 1; + } + + for (i = 0; i < gAudioContext.numSynthesisReverbs; i++) { + for (j = 0; j < count; j++) { + AudioHeap_UpdateReverb(&gAudioContext.synthesisReverbs[i]); + } + } +} + +void AudioHeap_ClearAiBuffers(void) { + s32 ind; + s32 i; + + ind = gAudioContext.curAIBufIdx; + gAudioContext.aiBufLengths[ind] = gAudioContext.audioBufferParameters.minAiBufferLength; + + for (i = 0; i < AIBUF_LEN; i++) { + gAudioContext.aiBuffers[ind][i] = 0; + } +} + +s32 AudioHeap_ResetStep(void) { + s32 i; + s32 j; + s32 sp24; + + if (gAudioContext.audioBufferParameters.specUnk4 == 2) { + sp24 = 2; + } else { + sp24 = 1; + } + + switch (gAudioContext.resetStatus) { + case 5: + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + AudioSeq_SequencePlayerDisableAsFinished(&gAudioContext.seqPlayers[i]); + } + gAudioContext.audioResetFadeOutFramesLeft = 2 / sp24; + gAudioContext.resetStatus--; + break; + + case 4: + if (gAudioContext.audioResetFadeOutFramesLeft != 0) { + gAudioContext.audioResetFadeOutFramesLeft--; + AudioHeap_UpdateReverbs(); + } else { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.notes[i].noteSubEu.bitField0.enabled && + gAudioContext.notes[i].playbackState.adsr.action.s.state != ADSR_STATE_DISABLED) { + gAudioContext.notes[i].playbackState.adsr.fadeOutVel = + gAudioContext.audioBufferParameters.updatesPerFrameInv; + gAudioContext.notes[i].playbackState.adsr.action.s.release = true; + } + } + gAudioContext.audioResetFadeOutFramesLeft = 8 / sp24; + gAudioContext.resetStatus--; + } + break; + + case 3: + if (gAudioContext.audioResetFadeOutFramesLeft != 0) { + gAudioContext.audioResetFadeOutFramesLeft--; + AudioHeap_UpdateReverbs(); + } else { + gAudioContext.audioResetFadeOutFramesLeft = 2 / sp24; + gAudioContext.resetStatus--; + } + break; + + case 2: + AudioHeap_ClearAiBuffers(); + if (gAudioContext.audioResetFadeOutFramesLeft != 0) { + gAudioContext.audioResetFadeOutFramesLeft--; + } else { + gAudioContext.resetStatus--; + AudioHeap_DiscardSampleCaches(); + AudioHeap_DiscardSampleBanks(); + } + break; + + case 1: + AudioHeap_Init(); + gAudioContext.resetStatus = 0; + for (i = 0; i < 3; i++) { + gAudioContext.aiBufLengths[i] = gAudioContext.audioBufferParameters.maxAiBufferLength; + for (j = 0; j < AIBUF_LEN; j++) { + gAudioContext.aiBuffers[i][j] = 0; + } + } + break; + } + + if (gAudioContext.resetStatus < 3) { + return 0; + } + + return 1; +} + +void AudioHeap_Init(void) { + s32 pad1[4]; + s16* mem; + s32 persistentMem; + s32 temporaryMem; + s32 totalMem; + s32 wantMisc; + OSIntMask intMask; + s32 i; + s32 j; + s32 pad2; + AudioSpec* spec; + + spec = &gAudioSpecs[gAudioContext.audioResetSpecIdToLoad]; + gAudioContext.sampleDmaCount = 0; + gAudioContext.audioBufferParameters.frequency = spec->frequency; + gAudioContext.audioBufferParameters.aiFrequency = osAiSetFrequency(gAudioContext.audioBufferParameters.frequency); + gAudioContext.audioBufferParameters.samplesPerFrameTarget = + ((gAudioContext.audioBufferParameters.frequency / gAudioContext.refreshRate) + 0xF) & 0xFFF0; + gAudioContext.audioBufferParameters.minAiBufferLength = + gAudioContext.audioBufferParameters.samplesPerFrameTarget - 0x10; + gAudioContext.audioBufferParameters.maxAiBufferLength = + gAudioContext.audioBufferParameters.samplesPerFrameTarget + 0x10; + gAudioContext.audioBufferParameters.updatesPerFrame = + ((gAudioContext.audioBufferParameters.samplesPerFrameTarget + 0x10) / 0xD0) + 1; + gAudioContext.audioBufferParameters.samplesPerUpdate = (gAudioContext.audioBufferParameters.samplesPerFrameTarget / + gAudioContext.audioBufferParameters.updatesPerFrame) & + ~7; + gAudioContext.audioBufferParameters.samplesPerUpdateMax = gAudioContext.audioBufferParameters.samplesPerUpdate + 8; + gAudioContext.audioBufferParameters.samplesPerUpdateMin = gAudioContext.audioBufferParameters.samplesPerUpdate - 8; + gAudioContext.audioBufferParameters.resampleRate = 32000.0f / (s32)gAudioContext.audioBufferParameters.frequency; + gAudioContext.audioBufferParameters.unkUpdatesPerFrameScaled = + (1.0f / 256.0f) / gAudioContext.audioBufferParameters.updatesPerFrame; + gAudioContext.audioBufferParameters.unk_24 = gAudioContext.audioBufferParameters.updatesPerFrame * 0.25f; + gAudioContext.audioBufferParameters.updatesPerFrameInv = 1.0f / gAudioContext.audioBufferParameters.updatesPerFrame; + gAudioContext.sampleDmaBufSize1 = spec->sampleDmaBufSize1; + gAudioContext.sampleDmaBufSize2 = spec->sampleDmaBufSize2; + + gAudioContext.numNotes = spec->numNotes; + gAudioContext.audioBufferParameters.numSequencePlayers = spec->numSequencePlayers; + if (gAudioContext.audioBufferParameters.numSequencePlayers > 4) { + gAudioContext.audioBufferParameters.numSequencePlayers = 4; + } + gAudioContext.unk_2 = spec->unk_14; + gAudioContext.tempoInternalToExternal = (u32)(gAudioContext.audioBufferParameters.updatesPerFrame * 2880000.0f / + gTatumsPerBeat / gAudioContext.unk_2960); + + gAudioContext.unk_2870 = gAudioContext.refreshRate; + gAudioContext.unk_2870 *= gAudioContext.audioBufferParameters.updatesPerFrame; + gAudioContext.unk_2870 /= gAudioContext.audioBufferParameters.aiFrequency; + gAudioContext.unk_2870 /= gAudioContext.tempoInternalToExternal; + + gAudioContext.audioBufferParameters.specUnk4 = spec->unk_04; + gAudioContext.audioBufferParameters.samplesPerFrameTarget *= gAudioContext.audioBufferParameters.specUnk4; + gAudioContext.audioBufferParameters.maxAiBufferLength *= gAudioContext.audioBufferParameters.specUnk4; + gAudioContext.audioBufferParameters.minAiBufferLength *= gAudioContext.audioBufferParameters.specUnk4; + gAudioContext.audioBufferParameters.updatesPerFrame *= gAudioContext.audioBufferParameters.specUnk4; + + if (gAudioContext.audioBufferParameters.specUnk4 >= 2) { + gAudioContext.audioBufferParameters.maxAiBufferLength -= 0x10; + } + + gAudioContext.maxAudioCmds = gAudioContext.numNotes * 0x10 * gAudioContext.audioBufferParameters.updatesPerFrame + + spec->numReverbs * 0x18 + 0x140; + + persistentMem = spec->persistentSeqMem + spec->persistentFontMem + spec->persistentSampleMem + 0x10; + temporaryMem = spec->temporarySeqMem + spec->temporaryFontMem + spec->temporarySampleMem + 0x10; + totalMem = persistentMem + temporaryMem; + wantMisc = gAudioContext.audioSessionPool.size - totalMem - 0x100; + + if (gAudioContext.externalPool.start != NULL) { + gAudioContext.externalPool.cur = gAudioContext.externalPool.start; + } + + gAudioContext.sessionPoolSplit.wantSeq = wantMisc; + gAudioContext.sessionPoolSplit.wantCustom = totalMem; + AudioHeap_SessionPoolsInit(&gAudioContext.sessionPoolSplit); + gAudioContext.cachePoolSplit.wantPersistent = persistentMem; + gAudioContext.cachePoolSplit.wantTemporary = temporaryMem; + AudioHeap_CachePoolInit(&gAudioContext.cachePoolSplit); + gAudioContext.persistentCommonPoolSplit.wantSeq = spec->persistentSeqMem; + gAudioContext.persistentCommonPoolSplit.wantFont = spec->persistentFontMem; + gAudioContext.persistentCommonPoolSplit.wantSample = spec->persistentSampleMem; + AudioHeap_PersistentCachesInit(&gAudioContext.persistentCommonPoolSplit); + gAudioContext.temporaryCommonPoolSplit.wantSeq = spec->temporarySeqMem; + gAudioContext.temporaryCommonPoolSplit.wantFont = spec->temporaryFontMem; + gAudioContext.temporaryCommonPoolSplit.wantSample = spec->temporarySampleMem; + AudioHeap_TemporaryCachesInit(&gAudioContext.temporaryCommonPoolSplit); + + AudioHeap_ResetLoadStatus(); + gAudioContext.notes = + AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, gAudioContext.numNotes * sizeof(Note)); + Audio_NoteInitAll(); + Audio_InitNoteFreeList(); + gAudioContext.noteSubsEu = + AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, gAudioContext.audioBufferParameters.updatesPerFrame * + gAudioContext.numNotes * sizeof(NoteSubEu)); + + for (i = 0; i != 2; i++) { + gAudioContext.abiCmdBufs[i] = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.notesAndBuffersPool, + gAudioContext.maxAudioCmds * sizeof(u64)); + } + + gAudioContext.unk_3520 = AudioHeap_Alloc(&gAudioContext.notesAndBuffersPool, 0x100 * sizeof(f32)); + func_800DDE3C(); + for (i = 0; i < 4; i++) { + gAudioContext.synthesisReverbs[i].useReverb = 0; + } + + gAudioContext.numSynthesisReverbs = spec->numReverbs; + for (i = 0; i < gAudioContext.numSynthesisReverbs; i++) { + ReverbSettings* settings = &spec->reverbSettings[i]; + SynthesisReverb* reverb = &gAudioContext.synthesisReverbs[i]; + reverb->downsampleRate = settings->downsampleRate; + reverb->windowSize = settings->windowSize * 64; + reverb->windowSize /= reverb->downsampleRate; + reverb->unk_0C = settings->unk_4; + reverb->unk_0A = settings->unk_A; + reverb->unk_14 = settings->unk_6 * 64; + reverb->unk_16 = settings->unk_8; + reverb->unk_18 = 0; + reverb->leakRtl = settings->leakRtl; + reverb->leakLtr = settings->leakLtr; + reverb->unk_05 = settings->unk_10; + reverb->unk_08 = settings->unk_12; + reverb->useReverb = 8; + reverb->leftRingBuf = + AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.notesAndBuffersPool, reverb->windowSize * sizeof(s16)); + reverb->rightRingBuf = + AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.notesAndBuffersPool, reverb->windowSize * sizeof(s16)); + reverb->nextRingBufPos = 0; + reverb->unk_20 = 0; + reverb->curFrame = 0; + reverb->bufSizePerChan = reverb->windowSize; + reverb->framesToIgnore = 2; + reverb->resampleFlags = 1; + reverb->sound.sample = &reverb->sample; + reverb->sample.loop = &reverb->loop; + reverb->sound.tuning = 1.0f; + reverb->sample.codec = CODEC_REVERB; + reverb->sample.medium = MEDIUM_RAM; + reverb->sample.size = reverb->windowSize * 2; + reverb->sample.sampleAddr = (u8*)reverb->leftRingBuf; + reverb->loop.start = 0; + reverb->loop.count = 1; + reverb->loop.end = reverb->windowSize; + + if (reverb->downsampleRate != 1) { + reverb->unk_0E = 0x8000 / reverb->downsampleRate; + reverb->unk_30 = AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, 0x20); + reverb->unk_34 = AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, 0x20); + reverb->unk_38 = AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, 0x20); + reverb->unk_3C = AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, 0x20); + for (j = 0; j < gAudioContext.audioBufferParameters.updatesPerFrame; j++) { + mem = AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.notesAndBuffersPool, 0x340); + reverb->items[0][j].toDownsampleLeft = mem; + reverb->items[0][j].toDownsampleRight = mem + 0x1A0 / sizeof(s16); + mem = AudioHeap_AllocZeroedAttemptExternal(&gAudioContext.notesAndBuffersPool, 0x340); + reverb->items[1][j].toDownsampleLeft = mem; + reverb->items[1][j].toDownsampleRight = mem + 0x1A0 / sizeof(s16); + } + } + + if (settings->lowPassFilterCutoffLeft != 0) { + reverb->filterLeftState = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.notesAndBuffersPool, 0x40); + reverb->filterLeft = AudioHeap_AllocDmaMemory(&gAudioContext.notesAndBuffersPool, 8 * sizeof(s16)); + AudioHeap_LoadLowPassFilter(reverb->filterLeft, settings->lowPassFilterCutoffLeft); + } else { + reverb->filterLeft = NULL; + } + + if (settings->lowPassFilterCutoffRight != 0) { + reverb->filterRightState = AudioHeap_AllocDmaMemoryZeroed(&gAudioContext.notesAndBuffersPool, 0x40); + reverb->filterRight = AudioHeap_AllocDmaMemory(&gAudioContext.notesAndBuffersPool, 8 * sizeof(s16)); + AudioHeap_LoadLowPassFilter(reverb->filterRight, settings->lowPassFilterCutoffRight); + } else { + reverb->filterRight = NULL; + } + } + + AudioSeq_InitSequencePlayers(); + for (j = 0; j < gAudioContext.audioBufferParameters.numSequencePlayers; j++) { + AudioSeq_InitSequencePlayerChannels(j); + AudioSeq_ResetSequencePlayer(&gAudioContext.seqPlayers[j]); + } + + AudioHeap_InitSampleCaches(spec->persistentSampleCacheMem, spec->temporarySampleCacheMem); + AudioLoad_InitSampleDmaBuffers(gAudioContext.numNotes); + gAudioContext.preloadSampleStackTop = 0; + AudioLoad_InitSlowLoads(); + AudioLoad_InitScriptLoads(); + AudioLoad_InitAsyncLoads(); + gAudioContext.unk_4 = 0x1000; + AudioLoad_LoadPermanentSamples(); + intMask = osSetIntMask(1); + osWritebackDCacheAll(); + osSetIntMask(intMask); +} + +void* AudioHeap_SearchPermanentCache(s32 tableType, s32 id) { + s32 i; + + for (i = 0; i < gAudioContext.permanentPool.count; i++) { + if (gAudioContext.permanentCache[i].tableType == tableType && gAudioContext.permanentCache[i].id == id) { + return gAudioContext.permanentCache[i].ptr; + } + } + return NULL; +} + +void* AudioHeap_AllocPermanent(s32 tableType, s32 id, size_t size) { + void* ret; + s32 index; + + index = gAudioContext.permanentPool.count; + + ret = AudioHeap_Alloc(&gAudioContext.permanentPool, size); + gAudioContext.permanentCache[index].ptr = ret; + if (ret == NULL) { + return NULL; + } + gAudioContext.permanentCache[index].tableType = tableType; + gAudioContext.permanentCache[index].id = id; + gAudioContext.permanentCache[index].size = size; + //! @bug UB: missing return. "ret" is in v0 at this point, but doing an + // explicit return uses an additional register. + return ret; +} + +void* AudioHeap_AllocSampleCache(size_t size, s32 fontId, void* sampleAddr, s8 medium, s32 cache) { + SampleCacheEntry* entry; + + if (cache == CACHE_TEMPORARY) { + entry = AudioHeap_AllocTemporarySampleCacheEntry(size); + } else { + entry = AudioHeap_AllocPersistentSampleCacheEntry(size); + } + if (entry != NULL) { + //! @bug Should use sampleBankId, not fontId + entry->sampleBankId = fontId; + entry->sampleAddr = sampleAddr; + entry->origMedium = medium; + return entry->allocatedAddr; + } + return NULL; +} + +void AudioHeap_InitSampleCaches(u32 persistentSize, u32 temporarySize) { + void* mem; + + mem = AudioHeap_AllocAttemptExternal(&gAudioContext.notesAndBuffersPool, persistentSize); + if (mem == NULL) { + gAudioContext.persistentSampleCache.pool.size = 0; + } else { + AudioHeap_AllocPoolInit(&gAudioContext.persistentSampleCache.pool, mem, persistentSize); + } + mem = AudioHeap_AllocAttemptExternal(&gAudioContext.notesAndBuffersPool, temporarySize); + if (mem == NULL) { + gAudioContext.temporarySampleCache.pool.size = 0; + } else { + AudioHeap_AllocPoolInit(&gAudioContext.temporarySampleCache.pool, mem, temporarySize); + } + gAudioContext.persistentSampleCache.size = 0; + gAudioContext.temporarySampleCache.size = 0; +} + +SampleCacheEntry* AudioHeap_AllocTemporarySampleCacheEntry(size_t size) { + u8* allocAfter; + u8* allocBefore; + void* mem; + s32 index; + s32 i; + SampleCacheEntry* ret; + AudioPreloadReq* preload; + AudioSampleCache* pool; + u8* start; + u8* end; + + pool = &gAudioContext.temporarySampleCache; + allocBefore = pool->pool.cur; + mem = AudioHeap_Alloc(&pool->pool, size); + if (mem == NULL) { + // Reset the pool and try again. We still keep pointers to within the + // pool, so we have to be careful to discard existing overlapping + // allocations further down. + u8* old = pool->pool.cur; + pool->pool.cur = pool->pool.start; + mem = AudioHeap_Alloc(&pool->pool, size); + if (mem == NULL) { + pool->pool.cur = old; + return NULL; + } + allocBefore = pool->pool.start; + } + + allocAfter = pool->pool.cur; + + index = -1; + for (i = 0; i < gAudioContext.preloadSampleStackTop; i++) { + preload = &gAudioContext.preloadSampleStack[i]; + if (preload->isFree == false) { + start = preload->ramAddr; + end = preload->ramAddr + preload->sample->size - 1; + + if (end < allocBefore && start < allocBefore) { + continue; + } + if (end >= allocAfter && start >= allocAfter) { + continue; + } + + // Overlap, skip this preload. + preload->isFree = true; + } + } + + for (i = 0; i < pool->size; i++) { + if (pool->entries[i].inUse == false) { + continue; + } + + start = pool->entries[i].allocatedAddr; + end = start + pool->entries[i].size - 1; + + if (end < allocBefore && start < allocBefore) { + continue; + } + if (end >= allocAfter && start >= allocAfter) { + continue; + } + + // Overlap, discard existing entry. + AudioHeap_DiscardSampleCacheEntry(&pool->entries[i]); + if (index == -1) { + index = i; + } + } + + if (index == -1) { + index = pool->size++; + } + + ret = &pool->entries[index]; + ret->inUse = true; + ret->allocatedAddr = mem; + ret->size = size; + return ret; +} + +void AudioHeap_UnapplySampleCacheForFont(SampleCacheEntry* entry, s32 fontId) { + Drum* drum; + Instrument* inst; + SoundFontSound* sfx; + s32 instId; + s32 drumId; + s32 sfxId; + + for (instId = 0; instId < gAudioContext.soundFonts[fontId].numInstruments; instId++) { + inst = Audio_GetInstrumentInner(fontId, instId); + if (inst != NULL) { + if (inst->normalRangeLo != 0) { + AudioHeap_UnapplySampleCache(entry, inst->lowNotesSound.sample); + } + if (inst->normalRangeHi != 0x7F) { + AudioHeap_UnapplySampleCache(entry, inst->highNotesSound.sample); + } + AudioHeap_UnapplySampleCache(entry, inst->normalNotesSound.sample); + } + } + + for (drumId = 0; drumId < gAudioContext.soundFonts[fontId].numDrums; drumId++) { + drum = Audio_GetDrum(fontId, drumId); + if (drum != NULL) { + AudioHeap_UnapplySampleCache(entry, drum->sound.sample); + } + } + + for (sfxId = 0; sfxId < gAudioContext.soundFonts[fontId].numSfx; sfxId++) { + sfx = Audio_GetSfx(fontId, sfxId); + if (sfx != NULL) { + AudioHeap_UnapplySampleCache(entry, sfx->sample); + } + } +} + +void AudioHeap_DiscardSampleCacheEntry(SampleCacheEntry* entry) { + s32 numFonts; + s32 sampleBankId1; + s32 sampleBankId2; + s32 fontId; + + numFonts = gAudioContext.soundFontTable->numEntries; + for (fontId = 0; fontId < numFonts; fontId++) { + sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; + sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + if (((sampleBankId1 != 0xFF) && (entry->sampleBankId == sampleBankId1)) || + ((sampleBankId2 != 0xFF) && (entry->sampleBankId == sampleBankId2)) || entry->sampleBankId == 0) { + if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, fontId) != NULL) { + if (AudioLoad_IsFontLoadComplete(fontId) != 0) { + AudioHeap_UnapplySampleCacheForFont(entry, fontId); + } + } + } + } +} + +void AudioHeap_UnapplySampleCache(SampleCacheEntry* entry, SoundFontSample* sample) { + if (sample != NULL) { + if (sample->sampleAddr == entry->allocatedAddr) { + sample->sampleAddr = entry->sampleAddr; + sample->medium = entry->origMedium; + } + } +} + +SampleCacheEntry* AudioHeap_AllocPersistentSampleCacheEntry(size_t size) { + AudioSampleCache* pool; + SampleCacheEntry* entry; + void* mem; + + pool = &gAudioContext.persistentSampleCache; + mem = AudioHeap_Alloc(&pool->pool, size); + if (mem == NULL) { + return NULL; + } + entry = &pool->entries[pool->size]; + entry->inUse = true; + entry->allocatedAddr = mem; + entry->size = size; + pool->size++; + return entry; +} + +void AudioHeap_DiscardSampleCacheForFont(SampleCacheEntry* entry, s32 sampleBankId1, s32 sampleBankId2, s32 fontId) { + if ((entry->sampleBankId == sampleBankId1) || (entry->sampleBankId == sampleBankId2) || + (entry->sampleBankId == 0)) { + AudioHeap_UnapplySampleCacheForFont(entry, fontId); + } +} + +void AudioHeap_DiscardSampleCaches(void) { + s32 numFonts; + s32 sampleBankId1; + s32 sampleBankId2; + s32 fontId; + s32 j; + + numFonts = gAudioContext.soundFontTable->numEntries; + for (fontId = 0; fontId < numFonts; fontId++) { + sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; + sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + if ((sampleBankId1 == 0xFF) && (sampleBankId2 == 0xFF)) { + continue; + } + if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_PERMANENT, fontId) == NULL || + !AudioLoad_IsFontLoadComplete(fontId)) { + continue; + } + + for (j = 0; j < gAudioContext.persistentSampleCache.size; j++) { + AudioHeap_DiscardSampleCacheForFont(&gAudioContext.persistentSampleCache.entries[j], sampleBankId1, + sampleBankId2, fontId); + } + for (j = 0; j < gAudioContext.temporarySampleCache.size; j++) { + AudioHeap_DiscardSampleCacheForFont(&gAudioContext.temporarySampleCache.entries[j], sampleBankId1, + sampleBankId2, fontId); + } + } +} + +typedef struct { + u32 oldAddr; + u32 newAddr; + size_t size; + u8 newMedium; +} StorageChange; + +void AudioHeap_ChangeStorage(StorageChange* change, SoundFontSample* sample) { + if (sample != NULL) { + u32 start = change->oldAddr; + u32 end = change->oldAddr + change->size; + + if (start <= (u32)sample->sampleAddr && (u32)sample->sampleAddr < end) { + sample->sampleAddr = sample->sampleAddr - start + change->newAddr; + sample->medium = change->newMedium; + } + } +} + +void AudioHeap_ApplySampleBankCacheInternal(s32 apply, s32 id); + +void AudioHeap_DiscardSampleBank(s32 sampleBankId) { + AudioHeap_ApplySampleBankCacheInternal(false, sampleBankId); +} + +void AudioHeap_ApplySampleBankCache(s32 sampleBankId) { + AudioHeap_ApplySampleBankCacheInternal(true, sampleBankId); +} + +void AudioHeap_ApplySampleBankCacheInternal(s32 apply, s32 sampleBankId) { + AudioTable* sampleBankTable; + AudioTableEntry* entry; + s32 numFonts; + s32 instId; + s32 drumId; + s32 sfxId; + StorageChange change; + s32 sampleBankId1; + s32 sampleBankId2; + s32 fontId; + Drum* drum; + Instrument* inst; + SoundFontSound* sfx; + u32* fakematch; + s32 pad[4]; + + sampleBankTable = gAudioContext.sampleBankTable; + numFonts = gAudioContext.soundFontTable->numEntries; + change.oldAddr = AudioHeap_SearchCaches(SAMPLE_TABLE, CACHE_EITHER, sampleBankId); + if (change.oldAddr == 0) { + return; + } + + entry = &sampleBankTable->entries[sampleBankId]; + change.size = entry->size; + change.newMedium = entry->medium; + + if ((change.newMedium == MEDIUM_CART) || (change.newMedium == MEDIUM_DISK_DRIVE)) { + change.newAddr = entry->romAddr; + } else { + change.newAddr = 0; + } + + fakematch = &change.oldAddr; + if ((apply != false) && (apply == true)) { + u32 temp = change.newAddr; + change.newAddr = *fakematch; // = change.oldAddr + change.oldAddr = temp; + change.newMedium = MEDIUM_RAM; + } + + for (fontId = 0; fontId < numFonts; fontId++) { + sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; + sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + if ((sampleBankId1 != 0xFF) || (sampleBankId2 != 0xFF)) { + if (!AudioLoad_IsFontLoadComplete(fontId) || + AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, fontId) == NULL) { + continue; + } + + if (sampleBankId1 == sampleBankId) { + } else if (sampleBankId2 == sampleBankId) { + } else { + continue; + } + + for (instId = 0; instId < gAudioContext.soundFonts[fontId].numInstruments; instId++) { + inst = Audio_GetInstrumentInner(fontId, instId); + if (inst != NULL) { + if (inst->normalRangeLo != 0) { + AudioHeap_ChangeStorage(&change, inst->lowNotesSound.sample); + } + if (inst->normalRangeHi != 0x7F) { + AudioHeap_ChangeStorage(&change, inst->highNotesSound.sample); + } + AudioHeap_ChangeStorage(&change, inst->normalNotesSound.sample); + } + } + + for (drumId = 0; drumId < gAudioContext.soundFonts[fontId].numDrums; drumId++) { + drum = Audio_GetDrum(fontId, drumId); + if (drum != NULL) { + AudioHeap_ChangeStorage(&change, drum->sound.sample); + } + } + + for (sfxId = 0; sfxId < gAudioContext.soundFonts[fontId].numSfx; sfxId++) { + sfx = Audio_GetSfx(fontId, sfxId); + if (sfx != NULL) { + AudioHeap_ChangeStorage(&change, sfx->sample); + } + } + } + } +} + +void AudioHeap_DiscardSampleBanks(void) { + AudioCache* pool; + AudioPersistentCache* persistent; + AudioTemporaryCache* temporary; + u32 i; + + pool = &gAudioContext.sampleBankCache; + temporary = &pool->temporary; + + if (temporary->entries[0].id != -1) { + AudioHeap_DiscardSampleBank(temporary->entries[0].id); + } + + if (temporary->entries[1].id != -1) { + AudioHeap_DiscardSampleBank(temporary->entries[1].id); + } + + persistent = &pool->persistent; + for (i = 0; i < persistent->numEntries; i++) { + AudioHeap_DiscardSampleBank(persistent->entries[i].id); + } +} diff --git a/soh/src/code/audio_init_params.c b/soh/src/code/audio_init_params.c new file mode 100644 index 000000000..0cebdf596 --- /dev/null +++ b/soh/src/code/audio_init_params.c @@ -0,0 +1,88 @@ +#include "global.h" + +u8 D_8016F0E0[0xA0]; // unused +AudioContext gAudioContext; +void (*D_801755D0)(void); +s32 D_801755D8[3]; // unused + +const s16 D_8014A6C0[] = { + 0x1C00, // unused + 0x0030, // gTatumsPerBeat +}; + +const AudioContextInitSizes D_8014A6C4 = { 0x37F00, 0xE0E0, 0xBCE0 }; + +ReverbSettings D_80133420[][3] = { + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x20, 0x0800, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x0000, 0x0, 0x0 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x30, 0x1800, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x0000, 0xB, 0xB }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x38, 0x2800, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x0000, 0x7, 0x7 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x50, 0x5000, 0, 0, 0x7FFF, 0x1000, 0x1000, 0xFF, 0x3000, 0x7, 0x7 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x40, 0x5000, 0, 0, 0x7FFF, 0x1800, 0x1800, 0xFF, 0x3000, 0x7, 0x7 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x40, 0x5C00, 0, 0, 0x7FFF, 0x2000, 0x2000, 0xFF, 0x3000, 0x4, 0x4 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x30, 0x6000, 0, 0, 0x7FFF, 0x1000, 0x1000, 0xFF, 0x3000, 0xA, 0xA }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x30, 0x6800, 0, 0, 0x7FFF, 0x1400, 0x1400, 0xFF, 0x3000, 0x6, 0x6 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 2, 0x50, 0x5000, 0, 0, 0x7FFF, 0xD000, 0x3000, 0xFF, 0x3000, 0x0, 0x0 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x20, 0x0000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x0000, 0x0, 0x0 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x30, 0x1800, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x0000, 0xB, 0xB }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + }, + { + { 1, 0x30, 0x3000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + { 1, 0x40, 0x5000, 0, 0, 0x7FFF, 0x0000, 0x0000, 0xFF, 0x3000, 0x0, 0x0 }, + }, +}; + +AudioSpec gAudioSpecs[18] = { + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x4000, 0x2880, 0, 0, 0 }, + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[1], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 23, 4, 0, 0, 2, D_80133420[4], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 23, 4, 0, 0, 2, D_80133420[5], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[6], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[7], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[9], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 28, 3, 0, 0, 2, D_80133420[10], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x2800, 0x2880, 0, 0, 0 }, + { 32000, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0x4800, 0, 0x4000, 0, 0, 0, 0 }, + { 32000, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0, 0, 0x4000, 0x4800, 0, 0, 0 }, + { 32000, 1, 22, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 22, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 16, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 22050, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, + { 32000, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3600, 0x2600, 0, 0, 0 }, +}; diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c new file mode 100644 index 000000000..ec8b7be86 --- /dev/null +++ b/soh/src/code/audio_load.c @@ -0,0 +1,2157 @@ +#include +#include +#include + +#include "ultra64.h" +#include "global.h" + +#define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status)) +#define ASYNC_TBLTYPE(v) ((u8)(v >> 16)) +#define ASYNC_ID(v) ((u8)(v >> 8)) +#define ASYNC_STATUS(v) ((u8)(v >> 0)) + +typedef enum { + /* 0 */ LOAD_STATUS_WAITING, + /* 1 */ LOAD_STATUS_START, + /* 2 */ LOAD_STATUS_LOADING, + /* 3 */ LOAD_STATUS_DONE +} SlowLoadStatus; + +typedef struct { + /* 0x00 */ s32 sampleBankId1; + /* 0x04 */ s32 sampleBankId2; + /* 0x08 */ s32 baseAddr1; + /* 0x0C */ s32 baseAddr2; + /* 0x10 */ u32 medium1; + /* 0x14 */ u32 medium2; +} RelocInfo; // size = 0x18 + +// opaque type for unpatched sound font data (should maybe get rid of this?) +typedef void SoundFontData; + +/* forward declarations */ +s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 skipTicks); +SoundFontData* AudioLoad_SyncLoadFont(u32 fontId); +SoundFontSample* AudioLoad_GetFontSample(s32 fontId, s32 instId); +void AudioLoad_ProcessAsyncLoads(s32 resetStatus); +void AudioLoad_ProcessAsyncLoadUnkMedium(AudioAsyncLoad* asyncLoad, s32 resetStatus); +void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus); +void AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo, s32 temporary); +void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocInfo* relocInfo); +void AudioLoad_DiscardFont(s32 fontId); +u32 AudioLoad_TrySyncLoadSampleBank(u32 sampleBankId, u32* outMedium, s32 noLoad); +void* AudioLoad_SyncLoad(u32 tableType, u32 tableId, s32* didAllocate); +u32 AudioLoad_GetRealTableIndex(s32 tableType, u32 tableId); +void* AudioLoad_SearchCaches(s32 tableType, s32 id); +AudioTable* AudioLoad_GetLoadTable(s32 tableType); +void AudioLoad_SyncDma(u32 devAddr, u8* addr, size_t size, s32 medium); +void AudioLoad_SyncDmaUnkMedium(u32 devAddr, u8* addr, size_t size, s32 unkMediumParam); +s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, u32 devAddr, void* ramAddr, size_t size, + OSMesgQueue* reqQueue, s32 medium, const char* dmaFuncType); +void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, OSMesgQueue* retQueue); +AudioAsyncLoad* AudioLoad_StartAsyncLoadUnkMedium(s32 unkMediumParam, u32 devAddr, void* ramAddr, ptrdiff_t size, s32 medium, + s32 nChunks, OSMesgQueue* retQueue, s32 retMsg); +AudioAsyncLoad* AudioLoad_StartAsyncLoad(u32 devAddr, void* ramAddr, size_t size, s32 medium, s32 nChunks, + OSMesgQueue* retQueue, s32 retMsg); +void AudioLoad_AsyncDma(AudioAsyncLoad* asyncLoad, size_t size); +void AudioLoad_AsyncDmaUnkMedium(u32 devAddr, void* ramAddr, size_t size, s16 arg3); +u8* AudioLoad_SyncLoadSeq(s32 seqId); +s32 AudioLoad_ProcessSamplePreloads(s32 resetStatus); +void AudioLoad_DmaSlowCopy(AudioSlowLoad* slowLoad, ptrdiff_t size); +void AudioLoad_ProcessSlowLoads(s32 resetStatus); +void AudioLoad_DmaSlowCopyUnkMedium(s32 devAddr, u8* ramAddr, ptrdiff_t size, s32 arg3); + +OSMesgQueue sScriptLoadQueue; +OSMesg sScriptLoadMesgBuf[0x10]; +s8* sScriptLoadDonePointers[0x10]; +s32 sAudioLoadPad1[2]; // file padding + +s32 D_8016B780; +s32 sAudioLoadPad2[4]; // double file padding? + +DmaHandler sDmaHandler = osEPiStartDma; +void* sUnusedHandler = NULL; + +s32 gAudioContextInitalized = false; + +void AudioLoad_DecreaseSampleDmaTtls(void) { + u32 i; + + for (i = 0; i < gAudioContext.sampleDmaListSize1; i++) { + SampleDma* dma = &gAudioContext.sampleDmas[i]; + + if (dma->ttl != 0) { + dma->ttl--; + if (dma->ttl == 0) { + dma->reuseIndex = gAudioContext.sampleDmaReuseQueue1WrPos; + gAudioContext.sampleDmaReuseQueue1[gAudioContext.sampleDmaReuseQueue1WrPos] = i; + gAudioContext.sampleDmaReuseQueue1WrPos++; + } + } + } + + for (i = gAudioContext.sampleDmaListSize1; i < gAudioContext.sampleDmaCount; i++) { + SampleDma* dma = &gAudioContext.sampleDmas[i]; + + if (dma->ttl != 0) { + dma->ttl--; + if (dma->ttl == 0) { + dma->reuseIndex = gAudioContext.sampleDmaReuseQueue2WrPos; + gAudioContext.sampleDmaReuseQueue2[gAudioContext.sampleDmaReuseQueue2WrPos] = i; + gAudioContext.sampleDmaReuseQueue2WrPos++; + } + } + } + + gAudioContext.unused2628 = 0; +} + +void* AudioLoad_DmaSampleData(u32 devAddr, size_t size, s32 arg2, u8* dmaIndexRef, s32 medium) { + s32 pad1; + SampleDma* dma; + s32 hasDma = false; + u32 dmaDevAddr; + u32 pad2; + u32 dmaIndex; + u32 transfer; + s32 bufferPos; + u32 i; + + if (arg2 != 0 || *dmaIndexRef >= gAudioContext.sampleDmaListSize1) { + for (i = gAudioContext.sampleDmaListSize1; i < gAudioContext.sampleDmaCount; i++) { + dma = &gAudioContext.sampleDmas[i]; + bufferPos = devAddr - dma->devAddr; + if (0 <= bufferPos && (u32)bufferPos <= dma->size - size) { + // We already have a DMA request for this memory range. + if (dma->ttl == 0 && + gAudioContext.sampleDmaReuseQueue2RdPos != gAudioContext.sampleDmaReuseQueue2WrPos) { + // Move the DMA out of the reuse queue, by swapping it with the + // read pos, and then incrementing the read pos. + if (dma->reuseIndex != gAudioContext.sampleDmaReuseQueue2RdPos) { + gAudioContext.sampleDmaReuseQueue2[dma->reuseIndex] = + gAudioContext.sampleDmaReuseQueue2[gAudioContext.sampleDmaReuseQueue2RdPos]; + gAudioContext + .sampleDmas[gAudioContext.sampleDmaReuseQueue2[gAudioContext.sampleDmaReuseQueue2RdPos]] + .reuseIndex = dma->reuseIndex; + } + gAudioContext.sampleDmaReuseQueue2RdPos++; + } + dma->ttl = 32; + *dmaIndexRef = (u8)i; + return &dma->ramAddr[devAddr - dma->devAddr]; + } + } + + if (arg2 == 0) { + goto search_short_lived; + } + + if (gAudioContext.sampleDmaReuseQueue2RdPos != gAudioContext.sampleDmaReuseQueue2WrPos && arg2 != 0) { + // Allocate a DMA from reuse queue 2, unless full. + dmaIndex = gAudioContext.sampleDmaReuseQueue2[gAudioContext.sampleDmaReuseQueue2RdPos]; + gAudioContext.sampleDmaReuseQueue2RdPos++; + dma = gAudioContext.sampleDmas + dmaIndex; + hasDma = true; + } + } else { + search_short_lived: + dma = gAudioContext.sampleDmas + *dmaIndexRef; + i = 0; + again: + bufferPos = devAddr - dma->devAddr; + if (0 <= bufferPos && (u32)bufferPos <= dma->size - size) { + // We already have DMA for this memory range. + if (dma->ttl == 0) { + // Move the DMA out of the reuse queue, by swapping it with the + // read pos, and then incrementing the read pos. + if (dma->reuseIndex != gAudioContext.sampleDmaReuseQueue1RdPos) { + gAudioContext.sampleDmaReuseQueue1[dma->reuseIndex] = + gAudioContext.sampleDmaReuseQueue1[gAudioContext.sampleDmaReuseQueue1RdPos]; + gAudioContext + .sampleDmas[gAudioContext.sampleDmaReuseQueue1[gAudioContext.sampleDmaReuseQueue1RdPos]] + .reuseIndex = dma->reuseIndex; + } + gAudioContext.sampleDmaReuseQueue1RdPos++; + } + dma->ttl = 2; + return dma->ramAddr + (devAddr - dma->devAddr); + } + dma = gAudioContext.sampleDmas + i++; + if (i <= gAudioContext.sampleDmaListSize1) { + goto again; + } + } + + if (!hasDma) { + if (gAudioContext.sampleDmaReuseQueue1RdPos == gAudioContext.sampleDmaReuseQueue1WrPos) { + return NULL; + } + // Allocate a DMA from reuse queue 1. + dmaIndex = gAudioContext.sampleDmaReuseQueue1[gAudioContext.sampleDmaReuseQueue1RdPos++]; + dma = gAudioContext.sampleDmas + dmaIndex; + hasDma = true; + } + + transfer = dma->size; + dmaDevAddr = devAddr & ~0xF; + dma->ttl = 3; + dma->devAddr = dmaDevAddr; + dma->sizeUnused = transfer; + AudioLoad_Dma(&gAudioContext.currAudioFrameDmaIoMesgBuf[gAudioContext.curAudioFrameDmaCount++], OS_MESG_PRI_NORMAL, + OS_READ, dmaDevAddr, dma->ramAddr, transfer, &gAudioContext.currAudioFrameDmaQueue, medium, + "SUPERDMA"); + *dmaIndexRef = dmaIndex; + + return (devAddr - dmaDevAddr) + dma->ramAddr; +} + +void AudioLoad_InitSampleDmaBuffers(s32 arg0) { + SampleDma* dma; + s32 i; + s32 t2; + s32 j; + + gAudioContext.sampleDmaBufSize = gAudioContext.sampleDmaBufSize1; + gAudioContext.sampleDmas = + AudioHeap_Alloc(&gAudioContext.notesAndBuffersPool, + 4 * gAudioContext.numNotes * sizeof(SampleDma) * gAudioContext.audioBufferParameters.specUnk4); + t2 = 3 * gAudioContext.numNotes * gAudioContext.audioBufferParameters.specUnk4; + for (i = 0; i < t2; i++) { + dma = &gAudioContext.sampleDmas[gAudioContext.sampleDmaCount]; + dma->ramAddr = + AudioHeap_AllocAttemptExternal(&gAudioContext.notesAndBuffersPool, gAudioContext.sampleDmaBufSize); + if (dma->ramAddr == NULL) { + break; + } else { + AudioHeap_WritebackDCache(dma->ramAddr, gAudioContext.sampleDmaBufSize); + dma->size = gAudioContext.sampleDmaBufSize; + dma->devAddr = 0; + dma->sizeUnused = 0; + dma->unused = 0; + dma->ttl = 0; + gAudioContext.sampleDmaCount++; + } + } + + for (i = 0; (u32)i < gAudioContext.sampleDmaCount; i++) { + gAudioContext.sampleDmaReuseQueue1[i] = i; + gAudioContext.sampleDmas[i].reuseIndex = i; + } + + for (i = gAudioContext.sampleDmaCount; i < 0x100; i++) { + gAudioContext.sampleDmaReuseQueue1[i] = 0; + } + + gAudioContext.sampleDmaReuseQueue1RdPos = 0; + gAudioContext.sampleDmaReuseQueue1WrPos = gAudioContext.sampleDmaCount; + gAudioContext.sampleDmaListSize1 = gAudioContext.sampleDmaCount; + gAudioContext.sampleDmaBufSize = gAudioContext.sampleDmaBufSize2; + + for (j = 0; j < gAudioContext.numNotes; j++) { + dma = &gAudioContext.sampleDmas[gAudioContext.sampleDmaCount]; + dma->ramAddr = + AudioHeap_AllocAttemptExternal(&gAudioContext.notesAndBuffersPool, gAudioContext.sampleDmaBufSize); + if (dma->ramAddr == NULL) { + break; + } else { + AudioHeap_WritebackDCache(dma->ramAddr, gAudioContext.sampleDmaBufSize); + dma->size = gAudioContext.sampleDmaBufSize; + dma->devAddr = 0U; + dma->sizeUnused = 0; + dma->unused = 0; + dma->ttl = 0; + gAudioContext.sampleDmaCount++; + } + } + + for (i = gAudioContext.sampleDmaListSize1; (u32)i < gAudioContext.sampleDmaCount; i++) { + gAudioContext.sampleDmaReuseQueue2[i - gAudioContext.sampleDmaListSize1] = i; + gAudioContext.sampleDmas[i].reuseIndex = i - gAudioContext.sampleDmaListSize1; + } + + for (i = gAudioContext.sampleDmaCount; i < 0x100; i++) { + gAudioContext.sampleDmaReuseQueue2[i] = gAudioContext.sampleDmaListSize1; + } + + gAudioContext.sampleDmaReuseQueue2RdPos = 0; + gAudioContext.sampleDmaReuseQueue2WrPos = gAudioContext.sampleDmaCount - gAudioContext.sampleDmaListSize1; +} + +s32 AudioLoad_IsFontLoadComplete(s32 fontId) { + if (fontId == 0xFF) { + return true; + } else if (gAudioContext.fontLoadStatus[fontId] >= 2) { + return true; + } else if (gAudioContext.fontLoadStatus[AudioLoad_GetRealTableIndex(FONT_TABLE, fontId)] >= 2) { + return true; + } else { + return false; + } +} + +s32 AudioLoad_IsSeqLoadComplete(s32 seqId) { + if (seqId == 0xFF) { + return true; + } else if (gAudioContext.seqLoadStatus[seqId] >= 2) { + return true; + } else if (gAudioContext.seqLoadStatus[AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId)] >= 2) { + return true; + } else { + return false; + } +} + +s32 AudioLoad_IsSampleLoadComplete(s32 sampleBankId) { + if (sampleBankId == 0xFF) { + return true; + } else if (gAudioContext.sampleFontLoadStatus[sampleBankId] >= 2) { + return true; + } else if (gAudioContext.sampleFontLoadStatus[AudioLoad_GetRealTableIndex(SAMPLE_TABLE, sampleBankId)] >= 2) { + return true; + } else { + return false; + } +} + +void AudioLoad_SetFontLoadStatus(s32 fontId, s32 status) { + if ((fontId != 0xFF) && (gAudioContext.fontLoadStatus[fontId] != 5)) { + gAudioContext.fontLoadStatus[fontId] = status; + } +} + +void AudioLoad_SetSeqLoadStatus(s32 seqId, s32 status) { + if ((seqId != 0xFF) && (gAudioContext.seqLoadStatus[seqId] != 5)) { + gAudioContext.seqLoadStatus[seqId] = status; + } +} + +void AudioLoad_SetSampleFontLoadStatusAndApplyCaches(s32 sampleBankId, s32 status) { + if (sampleBankId != 0xFF) { + if (gAudioContext.sampleFontLoadStatus[sampleBankId] != 5) { + gAudioContext.sampleFontLoadStatus[sampleBankId] = status; + } + + if ((gAudioContext.sampleFontLoadStatus[sampleBankId] == 5) || + (gAudioContext.sampleFontLoadStatus[sampleBankId] == 2)) { + AudioHeap_ApplySampleBankCache(sampleBankId); + } + } +} + +void AudioLoad_SetSampleFontLoadStatus(s32 sampleBankId, s32 status) { + if ((sampleBankId != 0xFF) && (gAudioContext.sampleFontLoadStatus[sampleBankId] != 5)) { + gAudioContext.sampleFontLoadStatus[sampleBankId] = status; + } +} + +void AudioLoad_InitTable(AudioTable* table, uintptr_t romAddr, u16 unkMediumParam) { + s32 i; + + table->unkMediumParam = unkMediumParam; + table->romAddr = romAddr; + + for (i = 0; i < table->numEntries; i++) { + if ((table->entries[i].size != 0) && (table->entries[i].medium == MEDIUM_CART)) { + table->entries[i].romAddr += romAddr; + } + } +} + +SoundFontData* AudioLoad_SyncLoadSeqFonts(s32 seqId, u32* outDefaultFontId) { + char pad[0x8]; + s32 index; + SoundFontData* font; + s32 numFonts; + s32 fontId; + s32 i; + + if (seqId >= gAudioContext.numSequences) { + return NULL; + } + + fontId = 0xFF; + index = ((u16*)gAudioContext.sequenceFontTable)[seqId]; + numFonts = gAudioContext.sequenceFontTable[index++]; + + while (numFonts > 0) { + fontId = gAudioContext.sequenceFontTable[index++]; + font = AudioLoad_SyncLoadFont(fontId); + numFonts--; + } + + *outDefaultFontId = fontId; + return font; +} + +void AudioLoad_SyncLoadSeqParts(s32 seqId, s32 arg1) { + s32 pad; + u32 defaultFontId; + + if (seqId < gAudioContext.numSequences) { + if (arg1 & 2) { + AudioLoad_SyncLoadSeqFonts(seqId, &defaultFontId); + } + if (arg1 & 1) { + AudioLoad_SyncLoadSeq(seqId); + } + } +} + +s32 AudioLoad_SyncLoadSample(SoundFontSample* sample, s32 fontId) { + void* sampleAddr; + + if (sample->unk_bit25 == 1) { + if (sample->medium != MEDIUM_RAM) { + sampleAddr = AudioHeap_AllocSampleCache(sample->size, fontId, (void*)sample->sampleAddr, sample->medium, + CACHE_PERSISTENT); + if (sampleAddr == NULL) { + return -1; + } + + if (sample->medium == MEDIUM_UNK) { + AudioLoad_SyncDmaUnkMedium(sample->sampleAddr, sampleAddr, sample->size, + gAudioContext.sampleBankTable->unkMediumParam); + } else { + AudioLoad_SyncDma(sample->sampleAddr, sampleAddr, sample->size, sample->medium); + } + sample->medium = MEDIUM_RAM; + sample->sampleAddr = sampleAddr; + } + } +} + +s32 AudioLoad_SyncLoadInstrument(s32 fontId, s32 instId, s32 drumId) { + if (instId < 0x7F) { + Instrument* instrument = Audio_GetInstrumentInner(fontId, instId); + + if (instrument == NULL) { + return -1; + } + if (instrument->normalRangeLo != 0) { + AudioLoad_SyncLoadSample(instrument->lowNotesSound.sample, fontId); + } + AudioLoad_SyncLoadSample(instrument->normalNotesSound.sample, fontId); + if (instrument->normalRangeHi != 0x7F) { + return AudioLoad_SyncLoadSample(instrument->highNotesSound.sample, fontId); + } + } else if (instId == 0x7F) { + Drum* drum = Audio_GetDrum(fontId, drumId); + + if (drum == NULL) { + return -1; + } + AudioLoad_SyncLoadSample(drum->sound.sample, fontId); + return 0; + } +} + +void AudioLoad_AsyncLoad(s32 tableType, s32 id, s32 nChunks, s32 retData, OSMesgQueue* retQueue) { + if (AudioLoad_AsyncLoadInner(tableType, id, nChunks, retData, retQueue) == NULL) { + osSendMesg(retQueue, 0xFFFFFFFF, OS_MESG_NOBLOCK); + } +} + +void AudioLoad_AsyncLoadSeq(s32 seqId, s32 arg1, s32 retData, OSMesgQueue* retQueue) { + AudioLoad_AsyncLoad(SEQUENCE_TABLE, seqId, 0, retData, retQueue); +} + +void AudioLoad_AsyncLoadSampleBank(s32 sampleBankId, s32 arg1, s32 retData, OSMesgQueue* retQueue) { + AudioLoad_AsyncLoad(SAMPLE_TABLE, sampleBankId, 0, retData, retQueue); +} + +void AudioLoad_AsyncLoadFont(s32 fontId, s32 arg1, s32 retData, OSMesgQueue* retQueue) { + AudioLoad_AsyncLoad(FONT_TABLE, fontId, 0, retData, retQueue); +} + +u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) { + s32 index; + + index = ((u16*)gAudioContext.sequenceFontTable)[seqId]; + + *outNumFonts = gAudioContext.sequenceFontTable[index++]; + if (*outNumFonts == 0) { + return NULL; + } + return &gAudioContext.sequenceFontTable[index]; +} + +void AudioLoad_DiscardSeqFonts(s32 seqId) { + s32 fontId; + s32 index; + s32 numFonts; + + index = ((u16*)gAudioContext.sequenceFontTable)[seqId]; + numFonts = gAudioContext.sequenceFontTable[index++]; + + while (numFonts > 0) { + numFonts--; + fontId = AudioLoad_GetRealTableIndex(FONT_TABLE, gAudioContext.sequenceFontTable[index++]); + if (AudioHeap_SearchPermanentCache(FONT_TABLE, fontId) == NULL) { + AudioLoad_DiscardFont(fontId); + AudioLoad_SetFontLoadStatus(fontId, 0); + } + } +} + +void AudioLoad_DiscardFont(s32 fontId) { + u32 i; + AudioCache* pool = &gAudioContext.fontCache; + AudioPersistentCache* persistent; + + if (fontId == pool->temporary.entries[0].id) { + pool->temporary.entries[0].id = -1; + } else if (fontId == pool->temporary.entries[1].id) { + pool->temporary.entries[1].id = -1; + } + + persistent = &pool->persistent; + for (i = 0; i < persistent->numEntries; i++) { + if (fontId == persistent->entries[i].id) { + persistent->entries[i].id = -1; + } + } + + AudioHeap_DiscardFont(fontId); +} + +s32 AudioLoad_SyncInitSeqPlayer(s32 playerIdx, s32 seqId, s32 arg2) { + if (gAudioContext.resetTimer != 0) { + return 0; + } + + gAudioContext.seqPlayers[playerIdx].skipTicks = 0; + AudioLoad_SyncInitSeqPlayerInternal(playerIdx, seqId, arg2); + // Intentionally missing return. Returning the result of the above function + // call matches but is UB because it too is missing a return, and using the + // result of a non-void function that has failed to return a value is UB. + // The callers of this function do not use the return value, so it's fine. +} + +s32 AudioLoad_SyncInitSeqPlayerSkipTicks(s32 playerIdx, s32 seqId, s32 skipTicks) { + if (gAudioContext.resetTimer != 0) { + return 0; + } + + gAudioContext.seqPlayers[playerIdx].skipTicks = skipTicks; + AudioLoad_SyncInitSeqPlayerInternal(playerIdx, seqId, 0); + // Missing return, see above. +} + +s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[playerIdx]; + u8* seqData; + s32 index; + s32 numFonts; + s32 fontId; + + if (seqId >= gAudioContext.numSequences) { + return 0; + } + + AudioSeq_SequencePlayerDisable(seqPlayer); + + fontId = 0xFF; + index = ((u16*)gAudioContext.sequenceFontTable)[seqId]; + numFonts = gAudioContext.sequenceFontTable[index++]; + + while (numFonts > 0) { + fontId = gAudioContext.sequenceFontTable[index++]; + AudioLoad_SyncLoadFont(fontId); + numFonts--; + } + + seqData = AudioLoad_SyncLoadSeq(seqId); + if (seqData == NULL) { + return 0; + } + + AudioSeq_ResetSequencePlayer(seqPlayer); + seqPlayer->seqId = seqId; + seqPlayer->defaultFont = AudioLoad_GetRealTableIndex(FONT_TABLE, fontId); + seqPlayer->seqData = seqData; + seqPlayer->enabled = 1; + seqPlayer->scriptState.pc = seqData; + seqPlayer->scriptState.depth = 0; + seqPlayer->delay = 0; + seqPlayer->finished = 0; + seqPlayer->playerIdx = playerIdx; + AudioSeq_SkipForwardSequence(seqPlayer); + //! @bug missing return (but the return value is not used so it's not UB) +} + +u8* AudioLoad_SyncLoadSeq(s32 seqId) { + s32 pad; + s32 didAllocate; + + if (gAudioContext.seqLoadStatus[AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId)] == 1) { + return NULL; + } + + return AudioLoad_SyncLoad(SEQUENCE_TABLE, seqId, &didAllocate); +} + +u32 AudioLoad_GetSampleBank(u32 sampleBankId, u32* outMedium) { + return AudioLoad_TrySyncLoadSampleBank(sampleBankId, outMedium, true); +} + +u32 AudioLoad_TrySyncLoadSampleBank(u32 sampleBankId, u32* outMedium, s32 noLoad) { + void* ret; + AudioTable* sampleBankTable; + u32 realTableId = AudioLoad_GetRealTableIndex(SAMPLE_TABLE, sampleBankId); + s8 cachePolicy; + + sampleBankTable = AudioLoad_GetLoadTable(SAMPLE_TABLE); + ret = AudioLoad_SearchCaches(SAMPLE_TABLE, realTableId); + if (ret != NULL) { + if (gAudioContext.sampleFontLoadStatus[realTableId] != 1) { + AudioLoad_SetSampleFontLoadStatus(realTableId, 2); + } + *outMedium = MEDIUM_RAM; + return ret; + } + + cachePolicy = sampleBankTable->entries[sampleBankId].cachePolicy; + if (cachePolicy == 4 || noLoad == true) { + *outMedium = sampleBankTable->entries[sampleBankId].medium; + return sampleBankTable->entries[realTableId].romAddr; + } + + ret = AudioLoad_SyncLoad(SAMPLE_TABLE, sampleBankId, &noLoad); + if (ret != NULL) { + *outMedium = MEDIUM_RAM; + return ret; + } + + *outMedium = sampleBankTable->entries[sampleBankId].medium; + return sampleBankTable->entries[realTableId].romAddr; +} + +SoundFontData* AudioLoad_SyncLoadFont(u32 fontId) { + SoundFontData* ret; + s32 sampleBankId1; + s32 sampleBankId2; + s32 didAllocate; + RelocInfo relocInfo; + s32 realFontId = AudioLoad_GetRealTableIndex(FONT_TABLE, fontId); + + if (gAudioContext.fontLoadStatus[realFontId] == 1) { + return NULL; + } + sampleBankId1 = gAudioContext.soundFonts[realFontId].sampleBankId1; + sampleBankId2 = gAudioContext.soundFonts[realFontId].sampleBankId2; + + relocInfo.sampleBankId1 = sampleBankId1; + relocInfo.sampleBankId2 = sampleBankId2; + if (sampleBankId1 != 0xFF) { + relocInfo.baseAddr1 = AudioLoad_TrySyncLoadSampleBank(sampleBankId1, &relocInfo.medium1, false); + } else { + relocInfo.baseAddr1 = 0; + } + + if (sampleBankId2 != 0xFF) { + relocInfo.baseAddr2 = AudioLoad_TrySyncLoadSampleBank(sampleBankId2, &relocInfo.medium2, false); + } else { + relocInfo.baseAddr2 = 0; + } + + ret = AudioLoad_SyncLoad(FONT_TABLE, fontId, &didAllocate); + if (ret == NULL) { + return NULL; + } + if (didAllocate == true) { + AudioLoad_RelocateFontAndPreloadSamples(realFontId, ret, &relocInfo, false); + } + + return ret; +} + +void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { + size_t size; + AudioTable* table; + s32 pad; + u32 medium; + s32 status; + uintptr_t romAddr; + s32 cachePolicy; + void* ret; + u32 realId; + + realId = AudioLoad_GetRealTableIndex(tableType, id); + ret = AudioLoad_SearchCaches(tableType, realId); + if (ret != NULL) { + *didAllocate = false; + status = 2; + } else { + table = AudioLoad_GetLoadTable(tableType); + size = table->entries[realId].size; + size = ALIGN16(size); + medium = table->entries[id].medium; + cachePolicy = table->entries[id].cachePolicy; + romAddr = table->entries[realId].romAddr; + switch (cachePolicy) { + case 0: + ret = AudioHeap_AllocPermanent(tableType, realId, size); + if (ret == NULL) { + return ret; + } + break; + case 1: + ret = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, realId); + if (ret == NULL) { + return ret; + } + break; + case 2: + ret = AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, realId); + if (ret == NULL) { + return ret; + } + break; + case 3: + case 4: + ret = AudioHeap_AllocCached(tableType, size, CACHE_EITHER, realId); + if (ret == NULL) { + return ret; + } + break; + } + + *didAllocate = true; + if (medium == MEDIUM_UNK) { + AudioLoad_SyncDmaUnkMedium(romAddr, ret, size, (s16)table->unkMediumParam); + } else { + AudioLoad_SyncDma(romAddr, ret, size, medium); + } + + status = cachePolicy == 0 ? 5 : 2; + } + + switch (tableType) { + case SEQUENCE_TABLE: + AudioLoad_SetSeqLoadStatus(realId, status); + break; + case FONT_TABLE: + AudioLoad_SetFontLoadStatus(realId, status); + break; + case SAMPLE_TABLE: + AudioLoad_SetSampleFontLoadStatusAndApplyCaches(realId, status); + break; + default: + break; + } + + return ret; +} + +u32 AudioLoad_GetRealTableIndex(s32 tableType, u32 id) { + AudioTable* table = AudioLoad_GetLoadTable(tableType); + + if (table->entries[id].size == 0) { + id = table->entries[id].romAddr; + } + + return id; +} + +void* AudioLoad_SearchCaches(s32 tableType, s32 id) { + void* ret; + + ret = AudioHeap_SearchPermanentCache(tableType, id); + if (ret != NULL) { + return ret; + } + + ret = AudioHeap_SearchCaches(tableType, CACHE_EITHER, id); + if (ret != NULL) { + return ret; + } + + return NULL; +} + +AudioTable* AudioLoad_GetLoadTable(s32 tableType) { + AudioTable* ret; + + switch (tableType) { + case SEQUENCE_TABLE: + ret = gAudioContext.sequenceTable; + break; + case FONT_TABLE: + ret = gAudioContext.soundFontTable; + break; + default: + ret = NULL; + break; + case SAMPLE_TABLE: + ret = gAudioContext.sampleBankTable; + break; + } + return ret; +} + +void AudioLoad_RelocateFont(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo) { + u32 reloc; + u32 reloc2; + Instrument* inst; + Drum* drum; + SoundFontSound* sfx; + s32 i; + s32 numDrums = gAudioContext.soundFonts[fontId].numDrums; + s32 numInstruments = gAudioContext.soundFonts[fontId].numInstruments; + s32 numSfx = gAudioContext.soundFonts[fontId].numSfx; + void** ptrs = (void**)mem; + +#define BASE_OFFSET(x) (void*)((u32)(x) + (u32)(mem)) + + reloc2 = ptrs[0]; + if (1) {} + if ((reloc2 != 0) && (numDrums != 0)) { + ptrs[0] = BASE_OFFSET(reloc2); + for (i = 0; i < numDrums; i++) { + reloc = ((Drum**)ptrs[0])[i]; + + if (reloc != 0) { + reloc = BASE_OFFSET(reloc); + + ((Drum**)ptrs[0])[i] = drum = reloc; + if (!drum->loaded) { + AudioLoad_RelocateSample(&drum->sound, mem, relocInfo); + reloc = drum->envelope; + drum->envelope = BASE_OFFSET(reloc); + drum->loaded = 1; + } + } + } + } + + reloc2 = ptrs[1]; + if (1) {} + if ((reloc2 != 0) && (numSfx != 0)) { + ptrs[1] = BASE_OFFSET(reloc2); + for (i = 0; i < numSfx; i++) { + reloc = (SoundFontSound*)ptrs[1] + i; + if (reloc != 0) { + sfx = reloc; + if (sfx->sample != NULL) { + AudioLoad_RelocateSample(sfx, mem, relocInfo); + } + } + } + } + + if (numInstruments > 0x7E) { + numInstruments = 0x7E; + } + + for (i = 2; i <= 2 + numInstruments - 1; i++) { + if (ptrs[i] != NULL) { + ptrs[i] = BASE_OFFSET(ptrs[i]); + + inst = ptrs[i]; + if (!inst->loaded) { + if (inst->normalRangeLo != 0) { + AudioLoad_RelocateSample(&inst->lowNotesSound, mem, relocInfo); + } + AudioLoad_RelocateSample(&inst->normalNotesSound, mem, relocInfo); + if (inst->normalRangeHi != 0x7F) { + AudioLoad_RelocateSample(&inst->highNotesSound, mem, relocInfo); + } + + reloc = inst->envelope; + inst->envelope = BASE_OFFSET(reloc); + inst->loaded = 1; + } + } + } + +#undef BASE_OFFSET + + gAudioContext.soundFonts[fontId].drums = ptrs[0]; + gAudioContext.soundFonts[fontId].soundEffects = ptrs[1]; + gAudioContext.soundFonts[fontId].instruments = (Instrument**)(ptrs + 2); +} + +void AudioLoad_SyncDma(u32 devAddr, u8* addr, size_t size, s32 medium) { + OSMesgQueue* msgQueue = &gAudioContext.syncDmaQueue; + OSIoMesg* ioMesg = &gAudioContext.syncDmaIoMesg; + size = ALIGN16(size); + + Audio_InvalDCache(addr, size); + + while (true) { + if (size < 0x400) { + break; + } + AudioLoad_Dma(ioMesg, OS_MESG_PRI_HIGH, OS_READ, devAddr, addr, 0x400, msgQueue, medium, "FastCopy"); + osRecvMesg(msgQueue, NULL, OS_MESG_BLOCK); + size -= 0x400; + devAddr += 0x400; + addr += 0x400; + } + + if (size != 0) { + AudioLoad_Dma(ioMesg, OS_MESG_PRI_HIGH, OS_READ, devAddr, addr, size, msgQueue, medium, "FastCopy"); + osRecvMesg(msgQueue, NULL, OS_MESG_BLOCK); + } +} + +void AudioLoad_SyncDmaUnkMedium(u32 devAddr, u8* addr, size_t size, s32 unkMediumParam) { +} + +s32 AudioLoad_Dma(OSIoMesg* mesg, u32 priority, s32 direction, u32 devAddr, void* ramAddr, size_t size, + OSMesgQueue* reqQueue, s32 medium, const char* dmaFuncType) { + if (gAudioContext.resetTimer > 0x10) { + return -1; + } + + memcpy(ramAddr, devAddr, size); + + return 0; +} + +void AudioLoad_Unused1(void) { +} + +void AudioLoad_SyncLoadSimple(u32 tableType, u32 fontId) { + s32 didAllocate; + + AudioLoad_SyncLoad(tableType, fontId, &didAllocate); +} + +void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, OSMesgQueue* retQueue) { + size_t size; + AudioTable* sp50; + void* ret; + s32 medium; + s8 cachePolicy; + u32 devAddr; + s32 status; + u32 temp_v0; + u32 realId; + + realId = AudioLoad_GetRealTableIndex(tableType, id); + switch (tableType) { + case SEQUENCE_TABLE: + if (gAudioContext.seqLoadStatus[realId] == 1) { + return NULL; + } + break; + case FONT_TABLE: + if (gAudioContext.fontLoadStatus[realId] == 1) { + return NULL; + } + break; + case SAMPLE_TABLE: + if (gAudioContext.sampleFontLoadStatus[realId] == 1) { + return NULL; + } + break; + } + + ret = AudioLoad_SearchCaches(tableType, realId); + if (ret != NULL) { + status = 2; + osSendMesg(retQueue, MK_ASYNC_MSG(retData, 0, 0, 0), OS_MESG_NOBLOCK); + } else { + sp50 = AudioLoad_GetLoadTable(tableType); + size = sp50->entries[realId].size; + size = ALIGN16(size); + medium = sp50->entries[id].medium; + cachePolicy = sp50->entries[id].cachePolicy; + devAddr = sp50->entries[realId].romAddr; + status = 2; + switch (cachePolicy) { + case 0: + ret = AudioHeap_AllocPermanent(tableType, realId, size); + if (ret == NULL) { + return ret; + } + status = 5; + break; + case 1: + ret = AudioHeap_AllocCached(tableType, size, CACHE_PERSISTENT, realId); + if (ret == NULL) { + return ret; + } + break; + case 2: + ret = AudioHeap_AllocCached(tableType, size, CACHE_TEMPORARY, realId); + if (ret == NULL) { + return ret; + } + break; + case 3: + case 4: + ret = AudioHeap_AllocCached(tableType, size, CACHE_EITHER, realId); + if (ret == NULL) { + return ret; + } + break; + } + + if (medium == MEDIUM_UNK) { + AudioLoad_StartAsyncLoadUnkMedium((s16)sp50->unkMediumParam, devAddr, ret, size, medium, nChunks, retQueue, + MK_ASYNC_MSG(retData, tableType, id, status)); + } else { + AudioLoad_StartAsyncLoad(devAddr, ret, size, medium, nChunks, retQueue, + MK_ASYNC_MSG(retData, tableType, realId, status)); + } + status = 1; + } + + switch (tableType) { + case SEQUENCE_TABLE: + AudioLoad_SetSeqLoadStatus(realId, status); + break; + case FONT_TABLE: + AudioLoad_SetFontLoadStatus(realId, status); + break; + case SAMPLE_TABLE: + AudioLoad_SetSampleFontLoadStatusAndApplyCaches(realId, status); + break; + default: + break; + } + + return ret; +} + +void AudioLoad_ProcessLoads(s32 resetStatus) { + AudioLoad_ProcessSlowLoads(resetStatus); + AudioLoad_ProcessSamplePreloads(resetStatus); + AudioLoad_ProcessAsyncLoads(resetStatus); +} + +void AudioLoad_SetDmaHandler(DmaHandler callback) { + sDmaHandler = callback; +} + +void AudioLoad_SetUnusedHandler(void* callback) { + sUnusedHandler = callback; +} + +void AudioLoad_InitSoundFontMeta(s32 fontId) { + SoundFont* font = &gAudioContext.soundFonts[fontId]; + AudioTableEntry* entry = &gAudioContext.soundFontTable->entries[fontId]; + + font->sampleBankId1 = (entry->shortData1 >> 8) & 0xFF; + font->sampleBankId2 = (entry->shortData1) & 0xFF; + font->numInstruments = (entry->shortData2 >> 8) & 0xFF; + font->numDrums = entry->shortData2 & 0xFF; + font->numSfx = entry->shortData3; +} + +s32 AudioLoad_AssertValidAddr(uintptr_t ramAddr, uintptr_t startAddr, size_t size) { + if (ramAddr >= startAddr && ramAddr < startAddr + size) { + // Valid + return 0; + } else { + // Not Valid, Breakpoint + return -1; + } +} + +#define BASE_ROM_OFFSET(x) (void*)((u32)(x) + (u32)(romAddr)) + +void AudioLoad_InitSwapFontSampleHeaders(SoundFontSample* sample, uintptr_t romAddr) { + // OTRTODO: This will be removed when we actually extract the data. + size_t maxSoundFontSize = 0x3AA0; // soundFont 0 is the largest size at 0x3AA0 + AdpcmLoop* loop; + AdpcmBook* book; + + if (((uintptr_t)sample->loop > maxSoundFontSize) || ((uintptr_t)sample->book > maxSoundFontSize) ) { + bswapSoundFontSample(sample); + + loop = (AdpcmLoop*)BASE_ROM_OFFSET(sample->loop); + if ((uint32_t)loop->end > (uint32_t)0xFFFF) { + bswapAdpcmLoop(loop); + } + + book = (AdpcmBook*)BASE_ROM_OFFSET(sample->book); + if (book->order > 0xFFFF) { + bswapAdpcmBook(book); + } + } + return; +} + +void AudioLoad_InitSwapFont(void) { + s32 i; + s32 fontId; + s32 numFonts = gAudioContext.soundFontTable->numEntries; + AudioTableEntry* fontEntry; + uintptr_t romAddr; + size_t size; + void** ptrs; + SoundFont* font; + s32 numDrums; + s32 numSfxs; + s32 numInstruments; + SoundFontSample* sample; + Drum** drumList; + Drum* drum; + SoundFontSound* sfxList; + SoundFontSound* sfx; + Instrument** instList; + Instrument* inst; + + // Only up to (numFonts - 1) as final font has garbage data to prevent corruption and is never used + for (fontId = 0; fontId < (numFonts - 1); fontId++) { + // extract meta-data from soundFontTable + fontEntry = &gAudioContext.soundFontTable->entries[fontId]; + romAddr = fontEntry->romAddr; + size = fontEntry->size; + ptrs = (void**)romAddr; + + // extract meta-data from soundFonts + font = &gAudioContext.soundFonts[fontId]; + numDrums = font->numDrums; + numSfxs = font->numSfx; + numInstruments = font->numInstruments; + + // drums + ptrs[0] = (void*)_byteswap_ulong((uintptr_t)ptrs[0]); + if ((ptrs[0] != NULL) && (numDrums != 0)) { + drumList = (Drum**)BASE_ROM_OFFSET(ptrs[0]); + + for (i = 0; i < numDrums; i++) { + drumList[i] = (Drum*)_byteswap_ulong((uintptr_t)drumList[i]); + + if (drumList[i] != NULL) { + drum = (Drum*)BASE_ROM_OFFSET(drumList[i]); + bswapDrum(drum); + + sample = (SoundFontSample*)BASE_ROM_OFFSET(drum->sound.sample); + AudioLoad_InitSwapFontSampleHeaders(sample, romAddr); + } + } + } + + // sfxs + ptrs[1] = (void*)_byteswap_ulong((u32)ptrs[1]); + if ((ptrs[1] != NULL) && (numSfxs != 0)) { + sfxList = (SoundFontSound*)BASE_ROM_OFFSET(ptrs[1]); + + for (i = 0; i < numSfxs; i++) { + + sfx = &sfxList[i]; + bswapSoundFontSound(sfx); + + if (sfx->sample != NULL) { + SoundFontSample* sample = (SoundFontSample*)BASE_ROM_OFFSET(sfx->sample); + AudioLoad_InitSwapFontSampleHeaders(sample, romAddr); + } + } + } + + // instruments + if (numInstruments > 0x7E) { + numInstruments = 0x7E; + } + + instList = (Instrument**)(&ptrs[2]); + for (i = 0; i < numInstruments; i++) { + instList[i] = (Instrument*)_byteswap_ulong((uintptr_t)instList[i]); + + if (instList[i] != NULL) { + inst = BASE_ROM_OFFSET(instList[i]); + bswapInstrument(inst); + + if (inst->normalRangeLo != 0) { + sample = (SoundFontSample*)BASE_ROM_OFFSET(inst->lowNotesSound.sample); + AudioLoad_InitSwapFontSampleHeaders(sample, romAddr); + } + + sample = (SoundFontSample*)BASE_ROM_OFFSET(inst->normalNotesSound.sample); + AudioLoad_InitSwapFontSampleHeaders(sample, romAddr); + + if (inst->normalRangeHi != 0x7F) { + sample = (SoundFontSample*)BASE_ROM_OFFSET(inst->highNotesSound.sample); + AudioLoad_InitSwapFontSampleHeaders(sample, romAddr); + } + } + } + } + + return; +} + +#undef BASE_ROM_OFFSET + +void AudioLoad_Init(void* heap, u32 heapSize) { + char pad[0x48]; + s32 numFonts; + void* temp_v0_3; + s32 i; + u64* heapP; + u8* ctxP; + s16* u2974p; + + D_801755D0 = NULL; + gAudioContext.resetTimer = 0; + + { + s32 i; + u8* ctxP = (u8*)&gAudioContext; + for (i = sizeof(gAudioContext); i >= 0; i--) { + *ctxP++ = 0; + } + } + + switch (osTvType) { + case OS_TV_PAL: + gAudioContext.unk_2960 = 20.03042f; + gAudioContext.refreshRate = 50; + break; + case OS_TV_MPAL: + gAudioContext.unk_2960 = 16.546f; + gAudioContext.refreshRate = 60; + break; + case OS_TV_NTSC: + default: + gAudioContext.unk_2960 = 16.713f; + gAudioContext.refreshRate = 60; + } + + Audio_InitMesgQueues(); + + for (i = 0; i < 3; i++) { + gAudioContext.aiBufLengths[i] = 0xA0; + } + + gAudioContext.totalTaskCnt = 0; + gAudioContext.rspTaskIdx = 0; + gAudioContext.curAIBufIdx = 0; + gAudioContext.soundMode = 0; + gAudioContext.currTask = NULL; + gAudioContext.rspTask[0].task.t.data_size = 0; + gAudioContext.rspTask[1].task.t.data_size = 0; + osCreateMesgQueue(&gAudioContext.syncDmaQueue, &gAudioContext.syncDmaMesg, 1); + osCreateMesgQueue(&gAudioContext.currAudioFrameDmaQueue, gAudioContext.currAudioFrameDmaMesgBuf, 0x40); + osCreateMesgQueue(&gAudioContext.externalLoadQueue, gAudioContext.externalLoadMesgBuf, + ARRAY_COUNT(gAudioContext.externalLoadMesgBuf)); + osCreateMesgQueue(&gAudioContext.preloadSampleQueue, gAudioContext.preloadSampleMesgBuf, + ARRAY_COUNT(gAudioContext.externalLoadMesgBuf)); + gAudioContext.curAudioFrameDmaCount = 0; + gAudioContext.sampleDmaCount = 0; + gAudioContext.cartHandle = osCartRomInit(); + + if (heap == NULL) { + gAudioContext.audioHeap = gAudioHeap; + gAudioContext.audioHeapSize = D_8014A6C4.heapSize; + } else { + void** hp = &heap; + gAudioContext.audioHeap = *hp; + gAudioContext.audioHeapSize = heapSize; + } + + for (i = 0; i < (s32)gAudioContext.audioHeapSize / 8; i++) { + ((u64*)gAudioContext.audioHeap)[i] = 0; + } + + AudioHeap_InitMainPools(D_8014A6C4.initPoolSize); + + for (i = 0; i < 3; i++) { + gAudioContext.aiBuffers[i] = AudioHeap_AllocZeroed(&gAudioContext.audioInitPool, AIBUF_LEN * sizeof(s16)); + } + + gAudioContext.sequenceTable = (AudioTable*)gSequenceTable; + gAudioContext.soundFontTable = (AudioTable*)gSoundFontTable; + gAudioContext.sampleBankTable = (AudioTable*)gSampleBankTable; + gAudioContext.sequenceFontTable = gSequenceFontTable; + gAudioContext.numSequences = gAudioContext.sequenceTable->numEntries; + + gAudioContext.audioResetSpecIdToLoad = 0; + gAudioContext.resetStatus = 1; + + AudioHeap_ResetStep(); + + uintptr_t seqStart = ResourceMgr_LoadFileRaw(_AudioseqSegmentRomStart); + uintptr_t bankStart = ResourceMgr_LoadFileRaw(_AudiobankSegmentRomStart); + uintptr_t tableStart = ResourceMgr_LoadFileRaw(_AudiotableSegmentRomStart); + + AudioLoad_InitTable(gAudioContext.sequenceTable, seqStart, 0); + AudioLoad_InitTable(gAudioContext.soundFontTable, bankStart, 0); + AudioLoad_InitTable(gAudioContext.sampleBankTable, tableStart, 0); + numFonts = gAudioContext.soundFontTable->numEntries; + gAudioContext.soundFonts = AudioHeap_Alloc(&gAudioContext.audioInitPool, numFonts * sizeof(SoundFont)); + + for (i = 0; i < numFonts; i++) { + AudioLoad_InitSoundFontMeta(i); + } + + AudioLoad_InitSwapFont(); + + if (temp_v0_3 = AudioHeap_Alloc(&gAudioContext.audioInitPool, D_8014A6C4.permanentPoolSize), temp_v0_3 == NULL) { + // cast away const from D_8014A6C4 + *((u32*)&D_8014A6C4.permanentPoolSize) = 0; + } + + AudioHeap_AllocPoolInit(&gAudioContext.permanentPool, temp_v0_3, D_8014A6C4.permanentPoolSize); + gAudioContextInitalized = true; + osSendMesg(gAudioContext.taskStartQueueP, (void*)gAudioContext.totalTaskCnt, OS_MESG_NOBLOCK); +} + +void AudioLoad_InitSlowLoads(void) { + gAudioContext.slowLoads[0].status = 0; + gAudioContext.slowLoads[1].status = 0; +} + +s32 AudioLoad_SlowLoadSample(s32 fontId, s32 instId, s8* isDone) { + SoundFontSample* sample; + AudioSlowLoad* slowLoad; + + sample = AudioLoad_GetFontSample(fontId, instId); + if (sample == NULL) { + *isDone = 0; + return -1; + } + + if (sample->medium == MEDIUM_RAM) { + *isDone = 2; + return 0; + } + + slowLoad = &gAudioContext.slowLoads[gAudioContext.slowLoadPos]; + if (slowLoad->status == LOAD_STATUS_DONE) { + slowLoad->status = LOAD_STATUS_WAITING; + } + + slowLoad->sample = *sample; + slowLoad->isDone = isDone; + slowLoad->curRamAddr = + AudioHeap_AllocSampleCache(sample->size, fontId, sample->sampleAddr, sample->medium, CACHE_TEMPORARY); + + if (slowLoad->curRamAddr == NULL) { + if (sample->medium == MEDIUM_UNK || sample->codec == CODEC_S16_INMEMORY) { + *isDone = 0; + return -1; + } else { + *isDone = 3; + return -1; + } + } + + slowLoad->status = LOAD_STATUS_START; + slowLoad->bytesRemaining = ALIGN16(sample->size); + slowLoad->ramAddr = slowLoad->curRamAddr; + slowLoad->curDevAddr = sample->sampleAddr; + slowLoad->medium = sample->medium; + slowLoad->seqOrFontId = fontId; + slowLoad->instId = instId; + if (slowLoad->medium == MEDIUM_UNK) { + slowLoad->unkMediumParam = gAudioContext.sampleBankTable->unkMediumParam; + } + + gAudioContext.slowLoadPos ^= 1; + return 0; +} + +SoundFontSample* AudioLoad_GetFontSample(s32 fontId, s32 instId) { + SoundFontSample* ret; + + if (instId < 0x80) { + Instrument* instrument = Audio_GetInstrumentInner(fontId, instId); + if (instrument == NULL) { + return NULL; + } + ret = instrument->normalNotesSound.sample; + } else if (instId < 0x100) { + Drum* drum = Audio_GetDrum(fontId, instId - 0x80); + if (drum == NULL) { + return NULL; + } + ret = drum->sound.sample; + } else { + SoundFontSound* sound = Audio_GetSfx(fontId, instId - 0x100); + if (sound == NULL) { + return NULL; + } + ret = sound->sample; + } + return ret; +} + +void AudioLoad_Unused2(void) { +} + +void AudioLoad_FinishSlowLoad(AudioSlowLoad* slowLoad) { + SoundFontSample* sample; + + if (slowLoad->sample.sampleAddr == NULL) { + return; + } + + sample = AudioLoad_GetFontSample(slowLoad->seqOrFontId, slowLoad->instId); + if (sample == NULL) { + return; + } + + slowLoad->sample = *sample; + sample->sampleAddr = slowLoad->ramAddr; + sample->medium = MEDIUM_RAM; +} + +void AudioLoad_ProcessSlowLoads(s32 resetStatus) { + AudioSlowLoad* slowLoad; + s32 i; + + for (i = 0; i < ARRAY_COUNT(gAudioContext.slowLoads); i++) { + slowLoad = &gAudioContext.slowLoads[i]; + switch (gAudioContext.slowLoads[i].status) { + case LOAD_STATUS_LOADING: + if (slowLoad->medium != MEDIUM_UNK) { + osRecvMesg(&slowLoad->msgqueue, NULL, OS_MESG_BLOCK); + } + + if (resetStatus != 0) { + slowLoad->status = LOAD_STATUS_DONE; + continue; + } + case LOAD_STATUS_START: + slowLoad->status = LOAD_STATUS_LOADING; + if (slowLoad->bytesRemaining == 0) { + AudioLoad_FinishSlowLoad(slowLoad); + slowLoad->status = LOAD_STATUS_DONE; + *slowLoad->isDone = 1; + } else if (slowLoad->bytesRemaining < 0x400) { + if (slowLoad->medium == MEDIUM_UNK) { + size_t size = slowLoad->bytesRemaining; + AudioLoad_DmaSlowCopyUnkMedium(slowLoad->curDevAddr, slowLoad->curRamAddr, size, + slowLoad->unkMediumParam); + } else { + AudioLoad_DmaSlowCopy(slowLoad, slowLoad->bytesRemaining); + } + slowLoad->bytesRemaining = 0; + } else { + if (slowLoad->medium == MEDIUM_UNK) { + AudioLoad_DmaSlowCopyUnkMedium(slowLoad->curDevAddr, slowLoad->curRamAddr, 0x400, + slowLoad->unkMediumParam); + } else { + AudioLoad_DmaSlowCopy(slowLoad, 0x400); + } + slowLoad->bytesRemaining -= 0x400; + slowLoad->curRamAddr += 0x400; + slowLoad->curDevAddr += 0x400; + } + break; + } + } +} + +void AudioLoad_DmaSlowCopy(AudioSlowLoad* slowLoad, ptrdiff_t size) { + Audio_InvalDCache(slowLoad->curRamAddr, size); + osCreateMesgQueue(&slowLoad->msgqueue, &slowLoad->msg, 1); + AudioLoad_Dma(&slowLoad->ioMesg, OS_MESG_PRI_NORMAL, 0, slowLoad->curDevAddr, slowLoad->curRamAddr, size, + &slowLoad->msgqueue, slowLoad->medium, "SLOWCOPY"); +} + +void AudioLoad_DmaSlowCopyUnkMedium(s32 devAddr, u8* ramAddr, ptrdiff_t size, s32 arg3) { +} + +s32 AudioLoad_SlowLoadSeq(s32 seqId, u8* ramAddr, s8* isDone) { + AudioSlowLoad* slowLoad; + AudioTable* seqTable; + size_t size; + + if (seqId >= gAudioContext.numSequences) { + *isDone = 0; + return -1; + } + + seqId = AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId); + seqTable = AudioLoad_GetLoadTable(SEQUENCE_TABLE); + slowLoad = &gAudioContext.slowLoads[gAudioContext.slowLoadPos]; + if (slowLoad->status == LOAD_STATUS_DONE) { + slowLoad->status = LOAD_STATUS_WAITING; + } + + slowLoad->sample.sampleAddr = NULL; + slowLoad->isDone = isDone; + size = seqTable->entries[seqId].size; + size = ALIGN16(size); + slowLoad->curRamAddr = ramAddr; + slowLoad->status = LOAD_STATUS_START; + slowLoad->bytesRemaining = size; + slowLoad->ramAddr = ramAddr; + slowLoad->curDevAddr = seqTable->entries[seqId].romAddr; + slowLoad->medium = seqTable->entries[seqId].medium; + slowLoad->seqOrFontId = seqId; + + if (slowLoad->medium == MEDIUM_UNK) { + slowLoad->unkMediumParam = seqTable->unkMediumParam; + } + + gAudioContext.slowLoadPos ^= 1; + return 0; +} + +void AudioLoad_InitAsyncLoads(void) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(gAudioContext.asyncLoads); i++) { + gAudioContext.asyncLoads[i].status = 0; + } +} + +AudioAsyncLoad* AudioLoad_StartAsyncLoadUnkMedium(s32 unkMediumParam, u32 devAddr, void* ramAddr, ptrdiff_t size, s32 medium, + s32 nChunks, OSMesgQueue* retQueue, s32 retMsg) { + AudioAsyncLoad* asyncLoad; + + asyncLoad = AudioLoad_StartAsyncLoad(devAddr, ramAddr, size, medium, nChunks, retQueue, retMsg); + + if (asyncLoad == NULL) { + return NULL; + } + + osSendMesg(&gAudioContext.asyncLoadUnkMediumQueue, asyncLoad, OS_MESG_NOBLOCK); + asyncLoad->unkMediumParam = unkMediumParam; + return asyncLoad; +} + +AudioAsyncLoad* AudioLoad_StartAsyncLoad(u32 devAddr, void* ramAddr, size_t size, s32 medium, s32 nChunks, + OSMesgQueue* retQueue, s32 retMsg) { + AudioAsyncLoad* asyncLoad; + s32 i; + + for (i = 0; i < ARRAY_COUNT(gAudioContext.asyncLoads); i++) { + if (gAudioContext.asyncLoads[i].status == 0) { + asyncLoad = &gAudioContext.asyncLoads[i]; + break; + } + } + + // no more available async loads + if (i == ARRAY_COUNT(gAudioContext.asyncLoads)) { + return NULL; + } + + asyncLoad->status = LOAD_STATUS_START; + asyncLoad->curDevAddr = devAddr; + asyncLoad->ramAddr = ramAddr; + asyncLoad->curRamAddr = ramAddr; + asyncLoad->bytesRemaining = size; + + if (nChunks == 0) { + asyncLoad->chunkSize = 0x1000; + } else if (nChunks == 1) { + asyncLoad->chunkSize = size; + } else { + asyncLoad->chunkSize = ALIGN256((s32)size / nChunks); + if (asyncLoad->chunkSize < 0x100) { + asyncLoad->chunkSize = 0x100; + } + } + + asyncLoad->retQueue = retQueue; + asyncLoad->delay = 3; + asyncLoad->medium = medium; + asyncLoad->retMsg = retMsg; + osCreateMesgQueue(&asyncLoad->msgQueue, &asyncLoad->msg, 1); + return asyncLoad; +} + +void AudioLoad_ProcessAsyncLoads(s32 resetStatus) { + AudioAsyncLoad* asyncLoad; + s32 i; + + if (gAudioContext.resetTimer == 1) { + return; + } + + if (gAudioContext.curUnkMediumLoad == NULL) { + if (resetStatus != 0) { + // Clear and ignore queue if resetting. + do { + } while (osRecvMesg(&gAudioContext.asyncLoadUnkMediumQueue, (OSMesg*)&asyncLoad, OS_MESG_NOBLOCK) != -1); + } else if (osRecvMesg(&gAudioContext.asyncLoadUnkMediumQueue, (OSMesg*)&asyncLoad, OS_MESG_NOBLOCK) == -1) { + gAudioContext.curUnkMediumLoad = NULL; + } else { + gAudioContext.curUnkMediumLoad = asyncLoad; + } + } + + if (gAudioContext.curUnkMediumLoad != NULL) { + AudioLoad_ProcessAsyncLoadUnkMedium(gAudioContext.curUnkMediumLoad, resetStatus); + } + + for (i = 0; i < ARRAY_COUNT(gAudioContext.asyncLoads); i++) { + if (gAudioContext.asyncLoads[i].status == 1) { + asyncLoad = &gAudioContext.asyncLoads[i]; + if (asyncLoad->medium != MEDIUM_UNK) { + AudioLoad_ProcessAsyncLoad(asyncLoad, resetStatus); + } + } + } +} + +void AudioLoad_ProcessAsyncLoadUnkMedium(AudioAsyncLoad* asyncLoad, s32 resetStatus) { +} + +void AudioLoad_FinishAsyncLoad(AudioAsyncLoad* asyncLoad) { + u32 retMsg = asyncLoad->retMsg; + u32 fontId; + u32 pad; + OSMesg doneMsg; + u32 sampleBankId1; + u32 sampleBankId2; + RelocInfo relocInfo; + + if (1) {} + switch (ASYNC_TBLTYPE(retMsg)) { + case SEQUENCE_TABLE: + AudioLoad_SetSeqLoadStatus(ASYNC_ID(retMsg), ASYNC_STATUS(retMsg)); + break; + case SAMPLE_TABLE: + AudioLoad_SetSampleFontLoadStatusAndApplyCaches(ASYNC_ID(retMsg), ASYNC_STATUS(retMsg)); + break; + case FONT_TABLE: + fontId = ASYNC_ID(retMsg); + sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; + sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + relocInfo.sampleBankId1 = sampleBankId1; + relocInfo.sampleBankId2 = sampleBankId2; + relocInfo.baseAddr1 = + sampleBankId1 != 0xFF ? AudioLoad_GetSampleBank(sampleBankId1, &relocInfo.medium1) : 0; + relocInfo.baseAddr2 = + sampleBankId2 != 0xFF ? AudioLoad_GetSampleBank(sampleBankId2, &relocInfo.medium2) : 0; + AudioLoad_SetFontLoadStatus(fontId, ASYNC_STATUS(retMsg)); + AudioLoad_RelocateFontAndPreloadSamples(fontId, asyncLoad->ramAddr, &relocInfo, true); + break; + } + + doneMsg = asyncLoad->retMsg; + if (1) {} + asyncLoad->status = LOAD_STATUS_WAITING; + osSendMesg(asyncLoad->retQueue, doneMsg, OS_MESG_NOBLOCK); +} + +void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus) { + AudioTable* sampleBankTable = gAudioContext.sampleBankTable; + + if (asyncLoad->delay >= 2) { + asyncLoad->delay--; + return; + } + + if (asyncLoad->delay == 1) { + asyncLoad->delay = 0; + } else if (resetStatus != 0) { + // Await the previous DMA response synchronously, then return. + osRecvMesg(&asyncLoad->msgQueue, NULL, OS_MESG_BLOCK); + asyncLoad->status = LOAD_STATUS_WAITING; + return; + } else if (osRecvMesg(&asyncLoad->msgQueue, NULL, OS_MESG_NOBLOCK) == -1) { + // If the previous DMA step isn't done, return. + return; + } + + if (asyncLoad->bytesRemaining == 0) { + AudioLoad_FinishAsyncLoad(asyncLoad); + return; + } + + if (asyncLoad->bytesRemaining < asyncLoad->chunkSize) { + if (asyncLoad->medium == MEDIUM_UNK) { + AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->bytesRemaining, + sampleBankTable->unkMediumParam); + } else { + AudioLoad_AsyncDma(asyncLoad, asyncLoad->bytesRemaining); + } + asyncLoad->bytesRemaining = 0; + return; + } + + if (asyncLoad->medium == MEDIUM_UNK) { + AudioLoad_AsyncDmaUnkMedium(asyncLoad->curDevAddr, asyncLoad->curRamAddr, asyncLoad->chunkSize, + sampleBankTable->unkMediumParam); + } else { + AudioLoad_AsyncDma(asyncLoad, asyncLoad->chunkSize); + } + + asyncLoad->bytesRemaining -= asyncLoad->chunkSize; + asyncLoad->curDevAddr += asyncLoad->chunkSize; + asyncLoad->curRamAddr += asyncLoad->chunkSize; +} + +void AudioLoad_AsyncDma(AudioAsyncLoad* asyncLoad, size_t size) { + size = ALIGN16(size); + Audio_InvalDCache(asyncLoad->curRamAddr, size); + osCreateMesgQueue(&asyncLoad->msgQueue, &asyncLoad->msg, 1); + AudioLoad_Dma(&asyncLoad->ioMesg, 0, 0, asyncLoad->curDevAddr, asyncLoad->curRamAddr, size, &asyncLoad->msgQueue, + asyncLoad->medium, "BGCOPY"); +} + +void AudioLoad_AsyncDmaUnkMedium(u32 devAddr, void* ramAddr, size_t size, s16 arg3) { +} + +#define RELOC(v, base) (reloc = (void*)((u32)(v) + (u32)(base))) + +void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocInfo* relocInfo) { + // OTRTODO: This is hack to detect whether or not the sample has been relocated. + size_t maxSoundBankSize = 0x3EB2A0; // sample bank 0 is largest size at 0x3EB2A0 + if ((uintptr_t)mem <= maxSoundBankSize) { + // OTRTODO: This can be removed once we have properly byteswapped files on the disk. + assert("mem for sound font bank is too low."); + } + + SoundFontSample* sample; + void* reloc; + + // OTRTODO: Seems precarious to assume the RAM is never <= 0x3EB2A0, but it largely works. + if ((uintptr_t)sound->sample < maxSoundBankSize) { + sample = sound->sample = RELOC(sound->sample, mem); + if (sample->size != 0 && sample->unk_bit25 != 1) { + sample->loop = RELOC(sample->loop, mem); + sample->book = RELOC(sample->book, mem); + + // Resolve the sample medium 2-bit bitfield into a real value based on relocInfo. + switch (sample->medium) { + case 0: + sample->sampleAddr = RELOC(sample->sampleAddr, relocInfo->baseAddr1); + sample->medium = relocInfo->medium1; + break; + case 1: + sample->sampleAddr = RELOC(sample->sampleAddr, relocInfo->baseAddr2); + sample->medium = relocInfo->medium2; + break; + case 2: + case 3: + // Invalid? This leaves sample->medium as MEDIUM_CART and MEDIUM_DISK_DRIVE + // respectively, and the sampleAddr unrelocated. + break; + } + + sample->unk_bit25 = 1; + if (sample->unk_bit26 && (sample->medium != MEDIUM_RAM)) { + gAudioContext.usedSamples[gAudioContext.numUsedSamples++] = sample; + } + } + } +} + +#undef RELOC + +void AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo, s32 async) { + AudioPreloadReq* preload; + AudioPreloadReq* topPreload; + SoundFontSample* sample; + ptrdiff_t size; + s32 nChunks; + u8* addr; + s32 preloadInProgress; + s32 i; + + preloadInProgress = false; + if (gAudioContext.preloadSampleStackTop != 0) { + preloadInProgress = true; + } else { + D_8016B780 = 0; + } + + gAudioContext.numUsedSamples = 0; + AudioLoad_RelocateFont(fontId, mem, relocInfo); + + size = 0; + for (i = 0; i < gAudioContext.numUsedSamples; i++) { + size += ALIGN16(gAudioContext.usedSamples[i]->size); + } + if (size && size) {} + + for (i = 0; i < gAudioContext.numUsedSamples; i++) { + if (gAudioContext.preloadSampleStackTop == 120) { + break; + } + + sample = gAudioContext.usedSamples[i]; + addr = NULL; + switch (async) { + case false: + if (sample->medium == relocInfo->medium1) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId1, sample->sampleAddr, + sample->medium, CACHE_PERSISTENT); + } else if (sample->medium == relocInfo->medium2) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId2, sample->sampleAddr, + sample->medium, CACHE_PERSISTENT); + } else if (sample->medium == MEDIUM_DISK_DRIVE) { + addr = AudioHeap_AllocSampleCache(sample->size, 0xFE, sample->sampleAddr, sample->medium, + CACHE_PERSISTENT); + } + break; + + case true: + if (sample->medium == relocInfo->medium1) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId1, sample->sampleAddr, + sample->medium, CACHE_TEMPORARY); + } else if (sample->medium == relocInfo->medium2) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId2, sample->sampleAddr, + sample->medium, CACHE_TEMPORARY); + } else if (sample->medium == MEDIUM_DISK_DRIVE) { + addr = AudioHeap_AllocSampleCache(sample->size, 0xFE, sample->sampleAddr, sample->medium, + CACHE_TEMPORARY); + } + break; + } + if (addr == NULL) { + continue; + } + + switch (async) { + case false: + if (sample->medium == MEDIUM_UNK) { + AudioLoad_SyncDmaUnkMedium((u32)sample->sampleAddr, addr, sample->size, + gAudioContext.sampleBankTable->unkMediumParam); + sample->sampleAddr = addr; + sample->medium = MEDIUM_RAM; + } else { + AudioLoad_SyncDma((u32)sample->sampleAddr, addr, sample->size, sample->medium); + sample->sampleAddr = addr; + sample->medium = MEDIUM_RAM; + } + if (sample->medium == MEDIUM_DISK_DRIVE) {} + break; + + case true: + preload = &gAudioContext.preloadSampleStack[gAudioContext.preloadSampleStackTop]; + preload->sample = sample; + preload->ramAddr = addr; + preload->encodedInfo = (gAudioContext.preloadSampleStackTop << 24) | 0xFFFFFF; + preload->isFree = false; + preload->endAndMediumKey = (u32)sample->sampleAddr + sample->size + sample->medium; + gAudioContext.preloadSampleStackTop++; + break; + } + } + gAudioContext.numUsedSamples = 0; + + if (gAudioContext.preloadSampleStackTop != 0 && !preloadInProgress) { + topPreload = &gAudioContext.preloadSampleStack[gAudioContext.preloadSampleStackTop - 1]; + sample = topPreload->sample; + nChunks = (sample->size >> 12) + 1; + AudioLoad_StartAsyncLoad((u32)sample->sampleAddr, topPreload->ramAddr, sample->size, sample->medium, nChunks, + &gAudioContext.preloadSampleQueue, topPreload->encodedInfo); + } +} + +s32 AudioLoad_ProcessSamplePreloads(s32 resetStatus) { + SoundFontSample* sample; + AudioPreloadReq* preload; + u32 preloadIndex; + u32 key; + u32 nChunks; + s32 pad; + + if (gAudioContext.preloadSampleStackTop > 0) { + if (resetStatus != 0) { + // Clear result queue and preload stack and return. + osRecvMesg(&gAudioContext.preloadSampleQueue, (OSMesg*)&preloadIndex, OS_MESG_NOBLOCK); + gAudioContext.preloadSampleStackTop = 0; + return 0; + } + if (osRecvMesg(&gAudioContext.preloadSampleQueue, (OSMesg*)&preloadIndex, OS_MESG_NOBLOCK) == -1) { + // Previous preload is not done yet. + return 0; + } + + preloadIndex >>= 24; + preload = &gAudioContext.preloadSampleStack[preloadIndex]; + + if (preload->isFree == false) { + sample = preload->sample; + key = (u32)sample->sampleAddr + sample->size + sample->medium; + if (key == preload->endAndMediumKey) { + // Change storage for sample to the preloaded version. + sample->sampleAddr = preload->ramAddr; + sample->medium = MEDIUM_RAM; + } + preload->isFree = true; + } + + // Pop requests with isFree = true off the stack, as far as possible, + // and dispatch the next DMA. + for (;;) { + if (gAudioContext.preloadSampleStackTop <= 0) { + break; + } + preload = &gAudioContext.preloadSampleStack[gAudioContext.preloadSampleStackTop - 1]; + if (preload->isFree == true) { + gAudioContext.preloadSampleStackTop--; + continue; + } + + sample = preload->sample; + nChunks = (sample->size >> 12) + 1; + key = (u32)sample->sampleAddr + sample->size + sample->medium; + if (key != preload->endAndMediumKey) { + preload->isFree = true; + gAudioContext.preloadSampleStackTop--; + } else { + AudioLoad_StartAsyncLoad((u32)sample->sampleAddr, preload->ramAddr, sample->size, sample->medium, + nChunks, &gAudioContext.preloadSampleQueue, preload->encodedInfo); + break; + } + } + } + return 1; +} + +s32 AudioLoad_AddToSampleSet(SoundFontSample* sample, s32 numSamples, SoundFontSample** sampleSet) { + s32 i; + + for (i = 0; i < numSamples; i++) { + if (sample->sampleAddr == sampleSet[i]->sampleAddr) { + break; + } + } + + if (i == numSamples) { + sampleSet[numSamples] = sample; + numSamples++; + } + + return numSamples; +} + +s32 AudioLoad_GetSamplesForFont(s32 fontId, SoundFontSample** sampleSet) { + s32 i; + s32 numDrums; + s32 numInstruments; + s32 numSamples = 0; + + numDrums = gAudioContext.soundFonts[fontId].numDrums; + numInstruments = gAudioContext.soundFonts[fontId].numInstruments; + + for (i = 0; i < numDrums; i++) { + Drum* drum = Audio_GetDrum(fontId, i); + if (1) {} + if (drum != NULL) { + numSamples = AudioLoad_AddToSampleSet(drum->sound.sample, numSamples, sampleSet); + } + } + + for (i = 0; i < numInstruments; i++) { + Instrument* instrument = Audio_GetInstrumentInner(fontId, i); + if (instrument != NULL) { + if (instrument->normalRangeLo != 0) { + numSamples = AudioLoad_AddToSampleSet(instrument->lowNotesSound.sample, numSamples, sampleSet); + } + if (instrument->normalRangeHi != 0x7F) { + numSamples = AudioLoad_AddToSampleSet(instrument->highNotesSound.sample, numSamples, sampleSet); + } + numSamples = AudioLoad_AddToSampleSet(instrument->normalNotesSound.sample, numSamples, sampleSet); + } + } + + // Should really also process sfx, but this method is never called, so whatever. + return numSamples; +} + +void AudioLoad_AddUsedSample(SoundFontSound* sound) { + SoundFontSample* sample = sound->sample; + + if ((sample->size != 0) && (sample->unk_bit26) && (sample->medium != MEDIUM_RAM)) { + gAudioContext.usedSamples[gAudioContext.numUsedSamples++] = sample; + } +} + +void AudioLoad_PreloadSamplesForFont(s32 fontId, s32 async, RelocInfo* relocInfo) { + s32 numDrums; + s32 numInstruments; + s32 numSfx; + Drum* drum; + Instrument* instrument; + SoundFontSound* sound; + AudioPreloadReq* preload; + AudioPreloadReq* topPreload; + u8* addr = NULL; + ptrdiff_t size; + s32 i; + SoundFontSample* sample; + s32 preloadInProgress; + s32 nChunks; + + preloadInProgress = false; + if (gAudioContext.preloadSampleStackTop != 0) { + preloadInProgress = true; + } + + gAudioContext.numUsedSamples = 0; + + numDrums = gAudioContext.soundFonts[fontId].numDrums; + numInstruments = gAudioContext.soundFonts[fontId].numInstruments; + numSfx = gAudioContext.soundFonts[fontId].numSfx; + + for (i = 0; i < numInstruments; i++) { + instrument = Audio_GetInstrumentInner(fontId, i); + if (instrument != NULL) { + if (instrument->normalRangeLo != 0) { + AudioLoad_AddUsedSample(&instrument->lowNotesSound); + } + if (instrument->normalRangeHi != 0x7F) { + AudioLoad_AddUsedSample(&instrument->highNotesSound); + } + AudioLoad_AddUsedSample(&instrument->normalNotesSound); + } + } + + for (i = 0; i < numDrums; i++) { + drum = Audio_GetDrum(fontId, i); + if (drum != NULL) { + AudioLoad_AddUsedSample(&drum->sound); + } + } + + for (i = 0; i < numSfx; i++) { + sound = Audio_GetSfx(fontId, i); + if (sound != NULL) { + AudioLoad_AddUsedSample(sound); + } + } + + if (gAudioContext.numUsedSamples == 0) { + return; + } + + size = 0; + for (i = 0; i < gAudioContext.numUsedSamples; i++) { + size += ALIGN16(gAudioContext.usedSamples[i]->size); + } + if (size) {} + + for (i = 0; i < gAudioContext.numUsedSamples; i++) { + if (gAudioContext.preloadSampleStackTop == 120) { + break; + } + + sample = gAudioContext.usedSamples[i]; + if (sample->medium == MEDIUM_RAM) { + continue; + } + + switch (async) { + case false: + if (sample->medium == relocInfo->medium1) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId1, sample->sampleAddr, + sample->medium, CACHE_PERSISTENT); + } else if (sample->medium == relocInfo->medium2) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId2, sample->sampleAddr, + sample->medium, CACHE_PERSISTENT); + } + break; + + case true: + if (sample->medium == relocInfo->medium1) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId1, sample->sampleAddr, + sample->medium, CACHE_TEMPORARY); + } else if (sample->medium == relocInfo->medium2) { + addr = AudioHeap_AllocSampleCache(sample->size, relocInfo->sampleBankId2, sample->sampleAddr, + sample->medium, CACHE_TEMPORARY); + } + break; + } + if (addr == NULL) { + continue; + } + + switch (async) { + case false: + if (sample->medium == MEDIUM_UNK) { + AudioLoad_SyncDmaUnkMedium((u32)sample->sampleAddr, addr, sample->size, + gAudioContext.sampleBankTable->unkMediumParam); + sample->sampleAddr = addr; + sample->medium = MEDIUM_RAM; + } else { + AudioLoad_SyncDma((u32)sample->sampleAddr, addr, sample->size, sample->medium); + sample->sampleAddr = addr; + sample->medium = MEDIUM_RAM; + } + break; + + case true: + preload = &gAudioContext.preloadSampleStack[gAudioContext.preloadSampleStackTop]; + preload->sample = sample; + preload->ramAddr = addr; + preload->encodedInfo = (gAudioContext.preloadSampleStackTop << 24) | 0xFFFFFF; + preload->isFree = false; + preload->endAndMediumKey = (u32)sample->sampleAddr + sample->size + sample->medium; + gAudioContext.preloadSampleStackTop++; + break; + } + } + gAudioContext.numUsedSamples = 0; + + if (gAudioContext.preloadSampleStackTop != 0 && !preloadInProgress) { + topPreload = &gAudioContext.preloadSampleStack[gAudioContext.preloadSampleStackTop - 1]; + sample = topPreload->sample; + nChunks = (sample->size >> 12) + 1; + AudioLoad_StartAsyncLoad((u32)sample->sampleAddr, topPreload->ramAddr, sample->size, sample->medium, nChunks, + &gAudioContext.preloadSampleQueue, topPreload->encodedInfo); + } +} + +void AudioLoad_LoadPermanentSamples(void) { + s32 pad; + u32 fontId; + AudioTable* sampleBankTable; + s32 pad2; + s32 i; + + sampleBankTable = AudioLoad_GetLoadTable(SAMPLE_TABLE); + for (i = 0; i < gAudioContext.permanentPool.count; i++) { + RelocInfo relocInfo; + + if (gAudioContext.permanentCache[i].tableType == FONT_TABLE) { + fontId = AudioLoad_GetRealTableIndex(FONT_TABLE, gAudioContext.permanentCache[i].id); + relocInfo.sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; + relocInfo.sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + + if (relocInfo.sampleBankId1 != 0xFF) { + relocInfo.sampleBankId1 = AudioLoad_GetRealTableIndex(SAMPLE_TABLE, relocInfo.sampleBankId1); + relocInfo.medium1 = sampleBankTable->entries[relocInfo.sampleBankId1].medium; + } + + if (relocInfo.sampleBankId2 != 0xFF) { + relocInfo.sampleBankId2 = AudioLoad_GetRealTableIndex(SAMPLE_TABLE, relocInfo.sampleBankId2); + relocInfo.medium2 = sampleBankTable->entries[relocInfo.sampleBankId2].medium; + } + AudioLoad_PreloadSamplesForFont(fontId, false, &relocInfo); + } + } +} + +void AudioLoad_Unused3(void) { +} + +void AudioLoad_Unused4(void) { +} + +void AudioLoad_Unused5(void) { +} + +void AudioLoad_ScriptLoad(s32 tableType, s32 id, s8* isDone) { + static u32 sLoadIndex = 0; + sScriptLoadDonePointers[sLoadIndex] = isDone; + AudioLoad_AsyncLoad(tableType, id, 0, sLoadIndex, &sScriptLoadQueue); + sLoadIndex++; + if (sLoadIndex == 0x10) { + sLoadIndex = 0; + } +} + +void AudioLoad_ProcessScriptLoads(void) { + u32 temp; + u32 sp20; + s8* isDone; + + if (osRecvMesg(&sScriptLoadQueue, (OSMesg*)&sp20, OS_MESG_NOBLOCK) != -1) { + temp = sp20 >> 24; + isDone = sScriptLoadDonePointers[temp]; + if (isDone != NULL) { + *isDone = 0; + } + } +} + +void AudioLoad_InitScriptLoads(void) { + osCreateMesgQueue(&sScriptLoadQueue, sScriptLoadMesgBuf, ARRAY_COUNT(sScriptLoadMesgBuf)); +} diff --git a/soh/src/code/audio_playback.c b/soh/src/code/audio_playback.c new file mode 100644 index 000000000..c116b4ddb --- /dev/null +++ b/soh/src/code/audio_playback.c @@ -0,0 +1,929 @@ +#include "global.h" +#include "Cvar.h" + +void Audio_InitNoteSub(Note* note, NoteSubEu* sub, NoteSubAttributes* attrs) { + f32 volRight, volLeft; + s32 smallPanIndex; + u64 pad; + u8 strongLeft; + u8 strongRight; + f32 vel; + u8 pan; + u8 reverbVol; + StereoData sp24; + s32 stereoHeadsetEffects = note->playbackState.stereoHeadsetEffects; + + vel = attrs->velocity; + pan = attrs->pan; + reverbVol = attrs->reverbVol; + sp24 = attrs->stereo.s; + + sub->bitField0 = note->noteSubEu.bitField0; + sub->bitField1 = note->noteSubEu.bitField1; + sub->sound.samples = note->noteSubEu.sound.samples; + sub->unk_06 = note->noteSubEu.unk_06; + + Audio_NoteSetResamplingRate(sub, attrs->frequency); + + pan &= 0x7F; + + sub->bitField0.stereoStrongRight = false; + sub->bitField0.stereoStrongLeft = false; + sub->bitField0.stereoHeadsetEffects = sp24.stereoHeadsetEffects; + sub->bitField0.usesHeadsetPanEffects = sp24.usesHeadsetPanEffects; + if (stereoHeadsetEffects && gAudioContext.soundMode == 1) { + smallPanIndex = pan >> 1; + if (smallPanIndex > 0x3F) { + smallPanIndex = 0x3F; + } + + sub->headsetPanLeft = gHeadsetPanQuantization[smallPanIndex]; + sub->headsetPanRight = gHeadsetPanQuantization[0x3F - smallPanIndex]; + sub->bitField1.usesHeadsetPanEffects2 = true; + + volLeft = gHeadsetPanVolume[pan]; + volRight = gHeadsetPanVolume[0x7F - pan]; + } else if (stereoHeadsetEffects && gAudioContext.soundMode == 0) { + strongLeft = strongRight = 0; + sub->headsetPanRight = 0; + sub->headsetPanLeft = 0; + sub->bitField1.usesHeadsetPanEffects2 = false; + + volLeft = gStereoPanVolume[pan]; + volRight = gStereoPanVolume[0x7F - pan]; + if (pan < 0x20) { + strongLeft = 1; + } else if (pan > 0x60) { + strongRight = 1; + } + + sub->bitField0.stereoStrongRight = strongRight; + sub->bitField0.stereoStrongLeft = strongLeft; + + switch (sp24.bit2) { + case 0: + break; + case 1: + sub->bitField0.stereoStrongRight = sp24.strongRight; + sub->bitField0.stereoStrongLeft = sp24.strongLeft; + break; + case 2: + sub->bitField0.stereoStrongRight = sp24.strongRight | strongRight; + sub->bitField0.stereoStrongLeft = sp24.strongLeft | strongLeft; + break; + case 3: + sub->bitField0.stereoStrongRight = sp24.strongRight ^ strongRight; + sub->bitField0.stereoStrongLeft = sp24.strongLeft ^ strongLeft; + break; + } + + } else if (gAudioContext.soundMode == 3) { + sub->bitField0.stereoHeadsetEffects = false; + sub->bitField0.usesHeadsetPanEffects = false; + volLeft = 0.707f; // approx 1/sqrt(2) + volRight = 0.707f; + } else { + sub->bitField0.stereoStrongRight = sp24.strongRight; + sub->bitField0.stereoStrongLeft = sp24.strongLeft; + volLeft = gDefaultPanVolume[pan]; + volRight = gDefaultPanVolume[0x7F - pan]; + } + + vel = 0.0f > vel ? 0.0f : vel; + vel = 1.0f < vel ? 1.0f : vel; + + float master_vol = CVar_GetFloat("gGameMasterVolume", 1.0f); + sub->targetVolLeft = (s32)((vel * volLeft) * (0x1000 - 0.001f)) * master_vol; + sub->targetVolRight = (s32)((vel * volRight) * (0x1000 - 0.001f)) * master_vol; + + sub->unk_2 = attrs->unk_1; + sub->filter = attrs->filter; + sub->unk_07 = attrs->unk_14; + sub->unk_0E = attrs->unk_16; + sub->reverbVol = reverbVol; +} + +void Audio_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput) { + f32 resamplingRate = 0.0f; + + if (resamplingRateInput < 2.0f) { + noteSubEu->bitField1.hasTwoParts = false; + + if (1.99998f < resamplingRateInput) { + resamplingRate = 1.99998f; + } else { + resamplingRate = resamplingRateInput; + } + + } else { + noteSubEu->bitField1.hasTwoParts = true; + if (3.99996f < resamplingRateInput) { + resamplingRate = 1.99998f; + } else { + resamplingRate = resamplingRateInput * 0.5f; + } + } + noteSubEu->resamplingRateFixedPoint = (s32)(resamplingRate * 32768.0f); +} + +void Audio_NoteInit(Note* note) { + if (note->playbackState.parentLayer->adsr.releaseRate == 0) { + Audio_AdsrInit(¬e->playbackState.adsr, note->playbackState.parentLayer->channel->adsr.envelope, + ¬e->playbackState.adsrVolScaleUnused); + } else { + Audio_AdsrInit(¬e->playbackState.adsr, note->playbackState.parentLayer->adsr.envelope, + ¬e->playbackState.adsrVolScaleUnused); + } + + note->playbackState.unk_04 = 0; + note->playbackState.adsr.action.s.state = ADSR_STATE_INITIAL; + note->noteSubEu = gDefaultNoteSub; +} + +void Audio_NoteDisable(Note* note) { + if (note->noteSubEu.bitField0.needsInit == true) { + note->noteSubEu.bitField0.needsInit = false; + } + note->playbackState.priority = 0; + note->noteSubEu.bitField0.enabled = false; + note->playbackState.unk_04 = 0; + note->noteSubEu.bitField0.finished = false; + note->playbackState.parentLayer = NO_LAYER; + note->playbackState.prevParentLayer = NO_LAYER; + note->playbackState.adsr.action.s.state = ADSR_STATE_DISABLED; + note->playbackState.adsr.current = 0; +} + +void Audio_ProcessNotes(void) { + s32 pad[2]; + NoteAttributes* attrs; + NoteSubEu* noteSubEu2; + NoteSubEu* noteSubEu; + Note* note; + NotePlaybackState* playbackState; + NoteSubAttributes subAttrs; + u8 bookOffset; + f32 scale; + s32 i; + + for (i = 0; i < gAudioContext.numNotes; i++) { + note = &gAudioContext.notes[i]; + noteSubEu2 = &gAudioContext.noteSubsEu[gAudioContext.noteSubEuOffset + i]; + playbackState = ¬e->playbackState; + if (playbackState->parentLayer != NO_LAYER) { + if (note != playbackState->parentLayer->note && playbackState->unk_04 == 0) { + playbackState->adsr.action.s.release = true; + playbackState->adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; + playbackState->priority = 1; + playbackState->unk_04 = 2; + goto out; + } else if (!playbackState->parentLayer->enabled && playbackState->unk_04 == 0 && + playbackState->priority >= 1) { + // do nothing + } else if (playbackState->parentLayer->channel->seqPlayer == NULL) { + AudioSeq_SequenceChannelDisable(playbackState->parentLayer->channel); + playbackState->priority = 1; + playbackState->unk_04 = 1; + continue; + } else if (playbackState->parentLayer->channel->seqPlayer->muted && + (playbackState->parentLayer->channel->muteBehavior & 0x40)) { + // do nothing + } else { + goto out; + } + + Audio_SeqLayerNoteRelease(playbackState->parentLayer); + Audio_AudioListRemove(¬e->listItem); + Audio_AudioListPushFront(¬e->listItem.pool->decaying, ¬e->listItem); + playbackState->priority = 1; + playbackState->unk_04 = 2; + } else if (playbackState->unk_04 == 0 && playbackState->priority >= 1) { + continue; + } + + out: + if (playbackState->priority != 0) { + if (1) {} + noteSubEu = ¬e->noteSubEu; + if (playbackState->unk_04 >= 1 || noteSubEu->bitField0.finished) { + if (playbackState->adsr.action.s.state == ADSR_STATE_DISABLED || noteSubEu->bitField0.finished) { + if (playbackState->wantedParentLayer != NO_LAYER) { + Audio_NoteDisable(note); + if (playbackState->wantedParentLayer->channel != NULL) { + Audio_NoteInitForLayer(note, playbackState->wantedParentLayer); + Audio_NoteVibratoInit(note); + Audio_NotePortamentoInit(note); + Audio_AudioListRemove(¬e->listItem); + AudioSeq_AudioListPushBack(¬e->listItem.pool->active, ¬e->listItem); + playbackState->wantedParentLayer = NO_LAYER; + // don't skip + } else { + Audio_NoteDisable(note); + Audio_AudioListRemove(¬e->listItem); + AudioSeq_AudioListPushBack(¬e->listItem.pool->disabled, ¬e->listItem); + playbackState->wantedParentLayer = NO_LAYER; + goto skip; + } + } else { + if (playbackState->parentLayer != NO_LAYER) { + playbackState->parentLayer->bit1 = true; + } + Audio_NoteDisable(note); + Audio_AudioListRemove(¬e->listItem); + AudioSeq_AudioListPushBack(¬e->listItem.pool->disabled, ¬e->listItem); + continue; + } + } + } else if (playbackState->adsr.action.s.state == ADSR_STATE_DISABLED) { + if (playbackState->parentLayer != NO_LAYER) { + playbackState->parentLayer->bit1 = true; + } + Audio_NoteDisable(note); + Audio_AudioListRemove(¬e->listItem); + AudioSeq_AudioListPushBack(¬e->listItem.pool->disabled, ¬e->listItem); + continue; + } + + scale = Audio_AdsrUpdate(&playbackState->adsr); + Audio_NoteVibratoUpdate(note); + attrs = &playbackState->attributes; + if (playbackState->unk_04 == 1 || playbackState->unk_04 == 2) { + subAttrs.frequency = attrs->freqScale; + subAttrs.velocity = attrs->velocity; + subAttrs.pan = attrs->pan; + subAttrs.reverbVol = attrs->reverb; + subAttrs.stereo = attrs->stereo; + subAttrs.unk_1 = attrs->unk_1; + subAttrs.filter = attrs->filter; + subAttrs.unk_14 = attrs->unk_4; + subAttrs.unk_16 = attrs->unk_6; + bookOffset = noteSubEu->bitField1.bookOffset; + } else { + SequenceLayer* layer = playbackState->parentLayer; + SequenceChannel* channel = layer->channel; + + subAttrs.frequency = layer->noteFreqScale; + subAttrs.velocity = layer->noteVelocity; + subAttrs.pan = layer->notePan; + if (layer->stereo.asByte == 0) { + subAttrs.stereo = channel->stereo; + } else { + subAttrs.stereo = layer->stereo; + } + subAttrs.reverbVol = channel->reverb; + subAttrs.unk_1 = channel->unk_0C; + subAttrs.filter = channel->filter; + subAttrs.unk_14 = channel->unk_0F; + subAttrs.unk_16 = channel->unk_20; + bookOffset = channel->bookOffset & 0x7; + + if (channel->seqPlayer->muted && (channel->muteBehavior & 8)) { + subAttrs.frequency = 0.0f; + subAttrs.velocity = 0.0f; + } + } + + subAttrs.frequency *= playbackState->vibratoFreqScale * playbackState->portamentoFreqScale; + subAttrs.frequency *= gAudioContext.audioBufferParameters.resampleRate; + subAttrs.velocity *= scale; + Audio_InitNoteSub(note, noteSubEu2, &subAttrs); + noteSubEu->bitField1.bookOffset = bookOffset; + skip:; + } + } +} + +SoundFontSound* Audio_InstrumentGetSound(Instrument* instrument, s32 semitone) { + SoundFontSound* sound; + if (semitone < instrument->normalRangeLo) { + sound = &instrument->lowNotesSound; + } else if (semitone <= instrument->normalRangeHi) { + sound = &instrument->normalNotesSound; + } else { + sound = &instrument->highNotesSound; + } + return sound; +} + +Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) { + Instrument* inst; + + if (fontId == 0xFF) { + return NULL; + } + + if (!AudioLoad_IsFontLoadComplete(fontId)) { + gAudioContext.audioErrorFlags = fontId + 0x10000000; + return NULL; + } + + if (instId >= gAudioContext.soundFonts[fontId].numInstruments) { + gAudioContext.audioErrorFlags = ((fontId << 8) + instId) + 0x3000000; + return NULL; + } + + inst = gAudioContext.soundFonts[fontId].instruments[instId]; + if (inst == NULL) { + gAudioContext.audioErrorFlags = ((fontId << 8) + instId) + 0x1000000; + return inst; + } + + return inst; +} + +Drum* Audio_GetDrum(s32 fontId, s32 drumId) { + Drum* drum; + + if (fontId == 0xFF) { + return NULL; + } + + if (!AudioLoad_IsFontLoadComplete(fontId)) { + gAudioContext.audioErrorFlags = fontId + 0x10000000; + return NULL; + } + + if (drumId >= gAudioContext.soundFonts[fontId].numDrums) { + gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x4000000; + return NULL; + } + + drum = gAudioContext.soundFonts[fontId].drums[drumId]; + + if (drum == NULL) { + gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x5000000; + } + + return drum; +} + +SoundFontSound* Audio_GetSfx(s32 fontId, s32 sfxId) { + SoundFontSound* sfx; + + if (fontId == 0xFF) { + return NULL; + } + + if (!AudioLoad_IsFontLoadComplete(fontId)) { + gAudioContext.audioErrorFlags = fontId + 0x10000000; + return NULL; + } + + if (sfxId >= gAudioContext.soundFonts[fontId].numSfx) { + gAudioContext.audioErrorFlags = ((fontId << 8) + sfxId) + 0x4000000; + return NULL; + } + + sfx = &gAudioContext.soundFonts[fontId].soundEffects[sfxId]; + + if (sfx == NULL) { + gAudioContext.audioErrorFlags = ((fontId << 8) + sfxId) + 0x5000000; + } + + if (sfx->sample == NULL) { + return NULL; + } + + return sfx; +} + +s32 Audio_SetFontInstrument(s32 instrumentType, s32 fontId, s32 index, void* value) { + if (fontId == 0xFF) { + return -1; + } + + if (!AudioLoad_IsFontLoadComplete(fontId)) { + return -2; + } + + switch (instrumentType) { + case 0: + if (index >= gAudioContext.soundFonts[fontId].numDrums) { + return -3; + } + gAudioContext.soundFonts[fontId].drums[index] = value; + break; + + case 1: + if (index >= gAudioContext.soundFonts[fontId].numSfx) { + return -3; + } + gAudioContext.soundFonts[fontId].soundEffects[index] = *(SoundFontSound*)value; + break; + + default: + if (index >= gAudioContext.soundFonts[fontId].numInstruments) { + return -3; + } + gAudioContext.soundFonts[fontId].instruments[index] = value; + break; + } + + return 0; +} + +void Audio_SeqLayerDecayRelease(SequenceLayer* layer, s32 target) { + Note* note; + NoteAttributes* attrs; + SequenceChannel* chan; + s32 i; + + if (layer == NO_LAYER) { + return; + } + + layer->bit3 = false; + + if (layer->note == NULL) { + return; + } + + note = layer->note; + attrs = ¬e->playbackState.attributes; + + if (note->playbackState.wantedParentLayer == layer) { + note->playbackState.wantedParentLayer = NO_LAYER; + } + + if (note->playbackState.parentLayer != layer) { + if (note->playbackState.parentLayer == NO_LAYER && note->playbackState.wantedParentLayer == NO_LAYER && + note->playbackState.prevParentLayer == layer && target != ADSR_STATE_DECAY) { + note->playbackState.adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; + note->playbackState.adsr.action.s.release = true; + } + return; + } + + if (note->playbackState.adsr.action.s.state != ADSR_STATE_DECAY) { + attrs->freqScale = layer->noteFreqScale; + attrs->velocity = layer->noteVelocity; + attrs->pan = layer->notePan; + + if (layer->channel != NULL) { + chan = layer->channel; + attrs->reverb = chan->reverb; + attrs->unk_1 = chan->unk_0C; + attrs->filter = chan->filter; + + if (attrs->filter != NULL) { + for (i = 0; i < 8; i++) { + attrs->filterBuf[i] = attrs->filter[i]; + } + attrs->filter = attrs->filterBuf; + } + + attrs->unk_6 = chan->unk_20; + attrs->unk_4 = chan->unk_0F; + if (chan->seqPlayer->muted && (chan->muteBehavior & 8)) { + note->noteSubEu.bitField0.finished = true; + } + + if (layer->stereo.asByte == 0) { + attrs->stereo = chan->stereo; + } else { + attrs->stereo = layer->stereo; + } + note->playbackState.priority = chan->someOtherPriority; + } else { + attrs->stereo = layer->stereo; + note->playbackState.priority = 1; + } + + note->playbackState.prevParentLayer = note->playbackState.parentLayer; + note->playbackState.parentLayer = NO_LAYER; + if (target == ADSR_STATE_RELEASE) { + note->playbackState.adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; + note->playbackState.adsr.action.s.release = true; + note->playbackState.unk_04 = 2; + } else { + note->playbackState.unk_04 = 1; + note->playbackState.adsr.action.s.decay = true; + if (layer->adsr.releaseRate == 0) { + note->playbackState.adsr.fadeOutVel = gAudioContext.unk_3520[layer->channel->adsr.releaseRate]; + } else { + note->playbackState.adsr.fadeOutVel = gAudioContext.unk_3520[layer->adsr.releaseRate]; + } + note->playbackState.adsr.sustain = + ((f32)(s32)(layer->channel->adsr.sustain) * note->playbackState.adsr.current) / 256.0f; + } + } + + if (target == ADSR_STATE_DECAY) { + Audio_AudioListRemove(¬e->listItem); + Audio_AudioListPushFront(¬e->listItem.pool->decaying, ¬e->listItem); + } +} + +void Audio_SeqLayerNoteDecay(SequenceLayer* layer) { + Audio_SeqLayerDecayRelease(layer, ADSR_STATE_DECAY); +} + +void Audio_SeqLayerNoteRelease(SequenceLayer* layer) { + Audio_SeqLayerDecayRelease(layer, ADSR_STATE_RELEASE); +} + +s32 Audio_BuildSyntheticWave(Note* note, SequenceLayer* layer, s32 waveId) { + f32 freqScale; + f32 ratio; + u8 sampleCountIndex; + + if (waveId < 128) { + waveId = 128; + } + + freqScale = layer->freqScale; + if (layer->portamento.mode != 0 && 0.0f < layer->portamento.extent) { + freqScale *= (layer->portamento.extent + 1.0f); + } + if (freqScale < 0.99999f) { + sampleCountIndex = 0; + ratio = 1.0465f; + } else if (freqScale < 1.99999f) { + sampleCountIndex = 1; + ratio = 0.52325f; + } else if (freqScale < 3.99999f) { + sampleCountIndex = 2; + ratio = 0.26263f; + } else { + sampleCountIndex = 3; + ratio = 0.13081f; + } + layer->freqScale *= ratio; + note->playbackState.waveId = waveId; + note->playbackState.sampleCountIndex = sampleCountIndex; + + note->noteSubEu.sound.samples = &gWaveSamples[waveId - 128][sampleCountIndex * 64]; + + return sampleCountIndex; +} + +void Audio_InitSyntheticWave(Note* note, SequenceLayer* layer) { + s32 sampleCountIndex; + s32 waveSampleCountIndex; + s32 waveId = layer->instOrWave; + + if (waveId == 0xFF) { + waveId = layer->channel->instOrWave; + } + + sampleCountIndex = note->playbackState.sampleCountIndex; + waveSampleCountIndex = Audio_BuildSyntheticWave(note, layer, waveId); + + if (waveSampleCountIndex != sampleCountIndex) { + note->noteSubEu.unk_06 = waveSampleCountIndex * 4 + sampleCountIndex; + } +} + +void Audio_InitNoteList(AudioListItem* list) { + list->prev = list; + list->next = list; + list->u.count = 0; +} + +void Audio_InitNoteLists(NotePool* pool) { + Audio_InitNoteList(&pool->disabled); + Audio_InitNoteList(&pool->decaying); + Audio_InitNoteList(&pool->releasing); + Audio_InitNoteList(&pool->active); + pool->disabled.pool = pool; + pool->decaying.pool = pool; + pool->releasing.pool = pool; + pool->active.pool = pool; +} + +void Audio_InitNoteFreeList(void) { + s32 i; + + Audio_InitNoteLists(&gAudioContext.noteFreeLists); + for (i = 0; i < gAudioContext.numNotes; i++) { + gAudioContext.notes[i].listItem.u.value = &gAudioContext.notes[i]; + gAudioContext.notes[i].listItem.prev = NULL; + AudioSeq_AudioListPushBack(&gAudioContext.noteFreeLists.disabled, &gAudioContext.notes[i].listItem); + } +} + +void Audio_NotePoolClear(NotePool* pool) { + s32 i; + AudioListItem* source; + AudioListItem* cur; + AudioListItem* dest; + + for (i = 0; i < 4; i++) { + switch (i) { + case 0: + source = &pool->disabled; + dest = &gAudioContext.noteFreeLists.disabled; + break; + + case 1: + source = &pool->decaying; + dest = &gAudioContext.noteFreeLists.decaying; + break; + + case 2: + source = &pool->releasing; + dest = &gAudioContext.noteFreeLists.releasing; + break; + + case 3: + source = &pool->active; + dest = &gAudioContext.noteFreeLists.active; + break; + } + + for (;;) { + cur = source->next; + if (cur == source || cur == NULL) { + break; + } + Audio_AudioListRemove(cur); + AudioSeq_AudioListPushBack(dest, cur); + } + } +} + +void Audio_NotePoolFill(NotePool* pool, s32 count) { + s32 i; + s32 j; + Note* note; + AudioListItem* source; + AudioListItem* dest; + + Audio_NotePoolClear(pool); + + for (i = 0, j = 0; j < count; i++) { + if (i == 4) { + return; + } + + switch (i) { + case 0: + source = &gAudioContext.noteFreeLists.disabled; + dest = &pool->disabled; + break; + + case 1: + source = &gAudioContext.noteFreeLists.decaying; + dest = &pool->decaying; + break; + + case 2: + source = &gAudioContext.noteFreeLists.releasing; + dest = &pool->releasing; + break; + + case 3: + source = &gAudioContext.noteFreeLists.active; + dest = &pool->active; + break; + } + + while (j < count) { + note = AudioSeq_AudioListPopBack(source); + if (note == NULL) { + break; + } + AudioSeq_AudioListPushBack(dest, ¬e->listItem); + j++; + } + } +} + +void Audio_AudioListPushFront(AudioListItem* list, AudioListItem* item) { + // add 'item' to the front of the list given by 'list', if it's not in any list + if (item->prev == NULL) { + item->prev = list; + item->next = list->next; + list->next->prev = item; + list->next = item; + list->u.count++; + item->pool = list->pool; + } +} + +void Audio_AudioListRemove(AudioListItem* item) { + // remove 'item' from the list it's in, if any + if (item->prev != NULL) { + item->prev->next = item->next; + item->next->prev = item->prev; + item->prev = NULL; + } +} + +Note* Audio_FindNodeWithPrioLessThan(AudioListItem* list, s32 limit) { + AudioListItem* cur = list->next; + AudioListItem* best; + + if (cur == list) { + return NULL; + } + + for (best = cur; cur != list; cur = cur->next) { + if (((Note*)best->u.value)->playbackState.priority >= ((Note*)cur->u.value)->playbackState.priority) { + best = cur; + } + } + + if (best == NULL) { + return NULL; + } + + if (limit <= ((Note*)best->u.value)->playbackState.priority) { + return NULL; + } + + return best->u.value; +} + +void Audio_NoteInitForLayer(Note* note, SequenceLayer* layer) { + s32 pad[3]; + s16 instId; + NotePlaybackState* playback = ¬e->playbackState; + NoteSubEu* sub = ¬e->noteSubEu; + + note->playbackState.prevParentLayer = NO_LAYER; + note->playbackState.parentLayer = layer; + playback->priority = layer->channel->notePriority; + layer->notePropertiesNeedInit = true; + layer->bit3 = true; + layer->note = note; + layer->channel->noteUnused = note; + layer->channel->layerUnused = layer; + layer->noteVelocity = 0.0f; + Audio_NoteInit(note); + instId = layer->instOrWave; + + if (instId == 0xFF) { + instId = layer->channel->instOrWave; + } + sub->sound.soundFontSound = layer->sound; + + if (instId >= 0x80 && instId < 0xC0) { + sub->bitField1.isSyntheticWave = true; + } else { + sub->bitField1.isSyntheticWave = false; + } + + if (sub->bitField1.isSyntheticWave) { + Audio_BuildSyntheticWave(note, layer, instId); + } + + playback->fontId = layer->channel->fontId; + playback->stereoHeadsetEffects = layer->channel->stereoHeadsetEffects; + sub->bitField1.reverbIndex = layer->channel->reverbIndex & 3; +} + +void func_800E82C0(Note* note, SequenceLayer* layer) { + // similar to Audio_NoteReleaseAndTakeOwnership, hard to say what the difference is + Audio_SeqLayerNoteRelease(note->playbackState.parentLayer); + note->playbackState.wantedParentLayer = layer; +} + +void Audio_NoteReleaseAndTakeOwnership(Note* note, SequenceLayer* layer) { + note->playbackState.wantedParentLayer = layer; + note->playbackState.priority = layer->channel->notePriority; + + note->playbackState.adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; + note->playbackState.adsr.action.s.release = true; +} + +Note* Audio_AllocNoteFromDisabled(NotePool* pool, SequenceLayer* layer) { + Note* note = AudioSeq_AudioListPopBack(&pool->disabled); + if (note != NULL) { + Audio_NoteInitForLayer(note, layer); + Audio_AudioListPushFront(&pool->active, ¬e->listItem); + } + return note; +} + +Note* Audio_AllocNoteFromDecaying(NotePool* pool, SequenceLayer* layer) { + Note* note = AudioSeq_AudioListPopBack(&pool->decaying); + if (note != NULL) { + Audio_NoteReleaseAndTakeOwnership(note, layer); + AudioSeq_AudioListPushBack(&pool->releasing, ¬e->listItem); + } + return note; +} + +Note* Audio_AllocNoteFromActive(NotePool* pool, SequenceLayer* layer) { + Note* rNote; + Note* aNote; + s32 rPriority; + s32 aPriority; + + rPriority = aPriority = 0x10; + rNote = Audio_FindNodeWithPrioLessThan(&pool->releasing, layer->channel->notePriority); + + if (rNote != NULL) { + rPriority = rNote->playbackState.priority; + } + + aNote = Audio_FindNodeWithPrioLessThan(&pool->active, layer->channel->notePriority); + + if (aNote != NULL) { + aPriority = aNote->playbackState.priority; + } + + if (rNote == NULL && aNote == NULL) { + return NULL; + } + + if (aPriority < rPriority) { + Audio_AudioListRemove(&aNote->listItem); + func_800E82C0(aNote, layer); + AudioSeq_AudioListPushBack(&pool->releasing, &aNote->listItem); + aNote->playbackState.priority = layer->channel->notePriority; + return aNote; + } + rNote->playbackState.wantedParentLayer = layer; + rNote->playbackState.priority = layer->channel->notePriority; + return rNote; +} + +Note* Audio_AllocNote(SequenceLayer* layer) { + Note* ret; + u32 policy = layer->channel->noteAllocPolicy; + + if (policy & 1) { + ret = layer->note; + if (ret != NULL && ret->playbackState.prevParentLayer == layer && + ret->playbackState.wantedParentLayer == NO_LAYER) { + Audio_NoteReleaseAndTakeOwnership(ret, layer); + Audio_AudioListRemove(&ret->listItem); + AudioSeq_AudioListPushBack(&ret->listItem.pool->releasing, &ret->listItem); + return ret; + } + } + + if (policy & 2) { + if (!(ret = Audio_AllocNoteFromDisabled(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromActive(&layer->channel->notePool, layer))) { + goto null_return; + } + return ret; + } + + if (policy & 4) { + if (!(ret = Audio_AllocNoteFromDisabled(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromDisabled(&layer->channel->seqPlayer->notePool, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&layer->channel->seqPlayer->notePool, layer)) && + !(ret = Audio_AllocNoteFromActive(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromActive(&layer->channel->seqPlayer->notePool, layer))) { + goto null_return; + } + return ret; + } + + if (policy & 8) { + if (!(ret = Audio_AllocNoteFromDisabled(&gAudioContext.noteFreeLists, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&gAudioContext.noteFreeLists, layer)) && + !(ret = Audio_AllocNoteFromActive(&gAudioContext.noteFreeLists, layer))) { + goto null_return; + } + return ret; + } + + if (!(ret = Audio_AllocNoteFromDisabled(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromDisabled(&layer->channel->seqPlayer->notePool, layer)) && + !(ret = Audio_AllocNoteFromDisabled(&gAudioContext.noteFreeLists, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&layer->channel->seqPlayer->notePool, layer)) && + !(ret = Audio_AllocNoteFromDecaying(&gAudioContext.noteFreeLists, layer)) && + !(ret = Audio_AllocNoteFromActive(&layer->channel->notePool, layer)) && + !(ret = Audio_AllocNoteFromActive(&layer->channel->seqPlayer->notePool, layer)) && + !(ret = Audio_AllocNoteFromActive(&gAudioContext.noteFreeLists, layer))) { + goto null_return; + } + return ret; + +null_return: + layer->bit3 = true; + return NULL; +} + +void Audio_NoteInitAll(void) { + Note* note; + s32 i; + + for (i = 0; i < gAudioContext.numNotes; i++) { + note = &gAudioContext.notes[i]; + note->noteSubEu = gZeroNoteSub; + note->playbackState.priority = 0; + note->playbackState.unk_04 = 0; + note->playbackState.parentLayer = NO_LAYER; + note->playbackState.wantedParentLayer = NO_LAYER; + note->playbackState.prevParentLayer = NO_LAYER; + note->playbackState.waveId = 0; + note->playbackState.attributes.velocity = 0.0f; + note->playbackState.adsrVolScaleUnused = 0; + note->playbackState.adsr.action.asByte = 0; + note->vibratoState.active = 0; + note->portamento.cur = 0; + note->portamento.speed = 0; + note->playbackState.stereoHeadsetEffects = false; + note->unk_BC = 0; + note->synthesisState.synthesisBuffers = AudioHeap_AllocDmaMemory(&gAudioContext.notesAndBuffersPool, 0x1E0); + } +} diff --git a/soh/src/code/audio_seqplayer.c b/soh/src/code/audio_seqplayer.c new file mode 100644 index 000000000..03e8842e3 --- /dev/null +++ b/soh/src/code/audio_seqplayer.c @@ -0,0 +1,1864 @@ +#include + +#include "ultra64.h" +#include "global.h" + +#define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80) +#define PORTAMENTO_MODE(x) ((x).mode & ~0x80) +#define PORTAMENTO_MODE_1 1 +#define PORTAMENTO_MODE_2 2 +#define PORTAMENTO_MODE_3 3 +#define PORTAMENTO_MODE_4 4 +#define PORTAMENTO_MODE_5 5 + +u8 AudioSeq_ScriptReadU8(SeqScriptState* state); +s16 AudioSeq_ScriptReadS16(SeqScriptState* state); +u16 AudioSeq_ScriptReadCompressedU16(SeqScriptState* state); + +u8 AudioSeq_GetInstrument(SequenceChannel* channel, u8 instId, Instrument** instOut, AdsrSettings* adsr); + +u8 D_80130520[] = { + 0x81, 0x00, 0x81, 0x01, 0x00, 0x00, 0x00, 0x81, 0x01, 0x01, 0x01, 0x42, 0x81, 0xC2, 0x00, 0x00, + 0x00, 0x01, 0x81, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x01, 0x81, 0x01, 0x01, 0x81, 0x81, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x81, 0x01, 0x01, 0x01, 0x81, 0x01, + 0x01, 0x03, 0x03, 0x01, 0x00, 0x01, 0x01, 0x81, 0x03, 0x01, 0x00, 0x02, 0x00, 0x01, 0x01, 0x82, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x81, 0x00, 0x00, 0x01, 0x81, 0x81, 0x81, 0x81, 0x00, 0x00, 0x00, +}; + +u16 AudioSeq_GetScriptControlFlowArgument(SeqScriptState* state, u8 arg1) { + u8 temp_v0 = D_80130520[arg1 - 0xB0]; + u8 loBits = temp_v0 & 3; + u16 ret = 0; + + if (loBits == 1) { + if ((temp_v0 & 0x80) == 0) { + ret = AudioSeq_ScriptReadU8(state); + } else { + ret = AudioSeq_ScriptReadS16(state); + } + } + return ret; +} + +s32 AudioSeq_HandleScriptFlowControl(SequencePlayer* seqPlayer, SeqScriptState* state, s32 cmd, s32 arg) { + switch (cmd) { + case 0xFF: + if (state->depth == 0) { + return -1; + } + state->pc = state->stack[--state->depth]; + break; + + case 0xFD: + return AudioSeq_ScriptReadCompressedU16(state); + + case 0xFE: + return 1; + + case 0xFC: + state->stack[state->depth++] = state->pc; + state->pc = seqPlayer->seqData + (u16)arg; + break; + + case 0xF8: + state->remLoopIters[state->depth] = arg; + state->stack[state->depth++] = state->pc; + break; + + case 0xF7: + state->remLoopIters[state->depth - 1]--; + if (state->remLoopIters[state->depth - 1] != 0) { + state->pc = state->stack[state->depth - 1]; + } else { + state->depth--; + } + break; + + case 0xF6: + state->depth--; + break; + + case 0xF5: + case 0xF9: + case 0xFA: + case 0xFB: + if (cmd == 0xFA && state->value != 0) { + break; + } + if (cmd == 0xF9 && state->value >= 0) { + break; + } + if (cmd == 0xF5 && state->value < 0) { + break; + } + state->pc = seqPlayer->seqData + (u16)arg; + break; + + case 0xF2: + case 0xF3: + case 0xF4: + if (cmd == 0xF3 && state->value != 0) { + break; + } + if (cmd == 0xF2 && state->value >= 0) { + break; + } + state->pc += (s8)(arg & 0xFF); + break; + } + + return 0; +} + +void AudioSeq_InitSequenceChannel(SequenceChannel* channel) { + s32 i; + + if (channel == &gAudioContext.sequenceChannelNone) { + return; + } + + channel->enabled = false; + channel->finished = false; + channel->stopScript = false; + channel->stopSomething2 = false; + channel->hasInstrument = false; + channel->stereoHeadsetEffects = false; + channel->transposition = 0; + channel->largeNotes = false; + channel->bookOffset = 0; + channel->stereo.asByte = 0; + channel->changes.asByte = 0xFF; + channel->scriptState.depth = 0; + channel->newPan = 0x40; + channel->panChannelWeight = 0x80; + channel->velocityRandomVariance = 0; + channel->gateTimeRandomVariance = 0; + channel->noteUnused = NULL; + channel->reverbIndex = 0; + channel->reverb = 0; + channel->unk_0C = 0; + channel->notePriority = 3; + channel->someOtherPriority = 1; + channel->delay = 0; + channel->adsr.envelope = gDefaultEnvelope; + channel->adsr.releaseRate = 0xF0; + channel->adsr.sustain = 0; + channel->vibratoRateTarget = 0x800; + channel->vibratoRateStart = 0x800; + channel->vibratoExtentTarget = 0; + channel->vibratoExtentStart = 0; + channel->vibratoRateChangeDelay = 0; + channel->vibratoExtentChangeDelay = 0; + channel->vibratoDelay = 0; + channel->filter = NULL; + channel->unk_20 = 0; + channel->unk_0F = 0; + channel->volume = 1.0f; + channel->volumeScale = 1.0f; + channel->freqScale = 1.0f; + + for (i = 0; i < 8; i++) { + channel->soundScriptIO[i] = -1; + } + + channel->unused = false; + Audio_InitNoteLists(&channel->notePool); +} + +s32 AudioSeq_SeqChannelSetLayer(SequenceChannel* channel, s32 layerIdx) { + SequenceLayer* layer; + + if (channel->layers[layerIdx] == NULL) { + SequenceLayer* layer; + layer = AudioSeq_AudioListPopBack(&gAudioContext.layerFreeList); + channel->layers[layerIdx] = layer; + if (layer == NULL) { + channel->layers[layerIdx] = NULL; + return -1; + } + } else { + Audio_SeqLayerNoteDecay(channel->layers[layerIdx]); + } + + layer = channel->layers[layerIdx]; + layer->channel = channel; + layer->adsr = channel->adsr; + layer->adsr.releaseRate = 0; + layer->enabled = true; + layer->finished = false; + layer->stopSomething = false; + layer->continuousNotes = false; + layer->bit3 = false; + layer->ignoreDrumPan = false; + layer->bit1 = false; + layer->notePropertiesNeedInit = false; + layer->stereo.asByte = 0; + layer->portamento.mode = 0; + layer->scriptState.depth = 0; + layer->gateTime = 0x80; + layer->pan = 0x40; + layer->transposition = 0; + layer->delay = 0; + layer->gateDelay = 0; + layer->delay2 = 0; + layer->note = NULL; + layer->instrument = NULL; + layer->freqScale = 1.0f; + layer->unk_34 = 1.0f; + layer->velocitySquare2 = 0.0f; + layer->instOrWave = 0xFF; + return 0; +} + +void AudioSeq_SeqLayerDisable(SequenceLayer* layer) { + if (layer != NULL) { + if (layer->channel != &gAudioContext.sequenceChannelNone && layer->channel->seqPlayer->finished == 1) { + Audio_SeqLayerNoteRelease(layer); + } else { + Audio_SeqLayerNoteDecay(layer); + } + layer->enabled = false; + layer->finished = true; + } +} + +void AudioSeq_SeqLayerFree(SequenceChannel* channel, s32 layerIdx) { + SequenceLayer* layer = channel->layers[layerIdx]; + + if (layer != NULL) { + AudioSeq_AudioListPushBack(&gAudioContext.layerFreeList, &layer->listItem); + AudioSeq_SeqLayerDisable(layer); + channel->layers[layerIdx] = NULL; + } +} + +void AudioSeq_SequenceChannelDisable(SequenceChannel* channel) { + s32 i; + + for (i = 0; i < 4; i++) { + AudioSeq_SeqLayerFree(channel, i); + } + + Audio_NotePoolClear(&channel->notePool); + channel->enabled = false; + channel->finished = true; +} + +void AudioSeq_SequencePlayerSetupChannels(SequencePlayer* seqPlayer, u16 channelBits) { + SequenceChannel* channel; + s32 i; + + for (i = 0; i < 0x10; i++) { + if (channelBits & 1) { + channel = seqPlayer->channels[i]; + channel->fontId = seqPlayer->defaultFont; + channel->muteBehavior = seqPlayer->muteBehavior; + channel->noteAllocPolicy = seqPlayer->noteAllocPolicy; + } + channelBits = channelBits >> 1; + } +} + +void AudioSeq_SequencePlayerDisableChannels(SequencePlayer* seqPlayer, u16 channelBitsUnused) { + SequenceChannel* channel; + s32 i; + + for (i = 0; i < 0x10; i++) { + channel = seqPlayer->channels[i]; + if (IS_SEQUENCE_CHANNEL_VALID(channel) == 1) { + AudioSeq_SequenceChannelDisable(channel); + } + } +} + +void AudioSeq_SequenceChannelEnable(SequencePlayer* seqPlayer, u8 channelIdx, void* script) { + SequenceChannel* channel = seqPlayer->channels[channelIdx]; + s32 i; + + channel->enabled = true; + channel->finished = false; + channel->scriptState.depth = 0; + channel->scriptState.pc = script; + channel->delay = 0; + for (i = 0; i < 4; i++) { + if (channel->layers[i] != NULL) { + AudioSeq_SeqLayerFree(channel, i); + } + } +} + +void AudioSeq_SequencePlayerDisableAsFinished(SequencePlayer* seqPlayer) { + seqPlayer->finished = true; + AudioSeq_SequencePlayerDisable(seqPlayer); +} + +void AudioSeq_SequencePlayerDisable(SequencePlayer* seqPlayer) { + AudioSeq_SequencePlayerDisableChannels(seqPlayer, 0xFFFF); + Audio_NotePoolClear(&seqPlayer->notePool); + if (!seqPlayer->enabled) { + return; + } + + seqPlayer->enabled = false; + seqPlayer->finished = true; + + if (AudioLoad_IsSeqLoadComplete(seqPlayer->seqId)) { + AudioLoad_SetSeqLoadStatus(seqPlayer->seqId, 3); + } + if (AudioLoad_IsFontLoadComplete(seqPlayer->defaultFont)) { + AudioLoad_SetFontLoadStatus(seqPlayer->defaultFont, 4); + } + + if (seqPlayer->defaultFont == gAudioContext.fontCache.temporary.entries[0].id) { + gAudioContext.fontCache.temporary.nextSide = 0; + } else if (seqPlayer->defaultFont == gAudioContext.fontCache.temporary.entries[1].id) { + gAudioContext.fontCache.temporary.nextSide = 1; + } +} + +void AudioSeq_AudioListPushBack(AudioListItem* list, AudioListItem* item) { + if (item->prev == NULL) { + list->prev->next = item; + item->prev = list->prev; + item->next = list; + list->prev = item; + list->u.count++; + item->pool = list->pool; + } +} + +void* AudioSeq_AudioListPopBack(AudioListItem* list) { + AudioListItem* item = list->prev; + + if (item == list) { + return NULL; + } + + item->prev->next = list; + list->prev = item->prev; + item->prev = NULL; + list->u.count--; + return item->u.value; +} + +void AudioSeq_InitLayerFreelist(void) { + s32 i; + + gAudioContext.layerFreeList.prev = &gAudioContext.layerFreeList; + gAudioContext.layerFreeList.next = &gAudioContext.layerFreeList; + gAudioContext.layerFreeList.u.count = 0; + gAudioContext.layerFreeList.pool = NULL; + + for (i = 0; i < ARRAY_COUNT(gAudioContext.sequenceLayers); i++) { + gAudioContext.sequenceLayers[i].listItem.u.value = &gAudioContext.sequenceLayers[i]; + gAudioContext.sequenceLayers[i].listItem.prev = NULL; + AudioSeq_AudioListPushBack(&gAudioContext.layerFreeList, &gAudioContext.sequenceLayers[i].listItem); + } +} + +u8 AudioSeq_ScriptReadU8(SeqScriptState* state) { + return *(state->pc++); +} + +s16 AudioSeq_ScriptReadS16(SeqScriptState* state) { + s16 ret = *(state->pc++) << 8; + + ret = *(state->pc++) | ret; + return ret; +} + +u16 AudioSeq_ScriptReadCompressedU16(SeqScriptState* state) { + u16 ret = *(state->pc++); + + if (ret & 0x80) { + ret = (ret << 8) & 0x7F00; + ret = *(state->pc++) | ret; + } + return ret; +} + +void AudioSeq_SeqLayerProcessScriptStep1(SequenceLayer* layer); +s32 AudioSeq_SeqLayerProcessScriptStep2(SequenceLayer* layer); +s32 AudioSeq_SeqLayerProcessScriptStep3(SequenceLayer* layer, s32 cmd); +s32 AudioSeq_SeqLayerProcessScriptStep4(SequenceLayer* layer, s32 cmd); +s32 AudioSeq_SeqLayerProcessScriptStep5(SequenceLayer* layer, s32 sameSound); + +void AudioSeq_SeqLayerProcessScript(SequenceLayer* layer) { + s32 val; + + if (!layer->enabled) { + return; + } + + if (layer->delay > 1) { + layer->delay--; + if (!layer->stopSomething && layer->delay <= layer->gateDelay) { + Audio_SeqLayerNoteDecay(layer); + layer->stopSomething = true; + } + return; + } + + AudioSeq_SeqLayerProcessScriptStep1(layer); + val = AudioSeq_SeqLayerProcessScriptStep2(layer); + if (val == -1) { + return; + } + + val = AudioSeq_SeqLayerProcessScriptStep3(layer, val); + if (val != -1) { + val = AudioSeq_SeqLayerProcessScriptStep4(layer, val); + } + if (val != -1) { + AudioSeq_SeqLayerProcessScriptStep5(layer, val); + } + + if (layer->stopSomething == true) { + if ((layer->note != NULL) || layer->continuousNotes) { + Audio_SeqLayerNoteDecay(layer); + } + } +} + +void AudioSeq_SeqLayerProcessScriptStep1(SequenceLayer* layer) { + if (!layer->continuousNotes) { + Audio_SeqLayerNoteDecay(layer); + } else if (layer->note != NULL && layer->note->playbackState.wantedParentLayer == layer) { + Audio_SeqLayerNoteDecay(layer); + } + + if (PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_1 || + PORTAMENTO_MODE(layer->portamento) == PORTAMENTO_MODE_2) { + layer->portamento.mode = 0; + } + layer->notePropertiesNeedInit = true; +} + +s32 AudioSeq_SeqLayerProcessScriptStep5(SequenceLayer* layer, s32 sameSound) { + if (!layer->stopSomething && layer->sound != NULL && layer->sound->sample->codec == CODEC_S16_INMEMORY && + layer->sound->sample->medium != MEDIUM_RAM) { + layer->stopSomething = true; + return -1; + } + + if (layer->continuousNotes == true && layer->bit1 == 1) { + return 0; + } + + if (layer->continuousNotes == true && layer->note != NULL && layer->bit3 && sameSound == true && + layer->note->playbackState.parentLayer == layer) { + if (layer->sound == NULL) { + Audio_InitSyntheticWave(layer->note, layer); + } + } else { + if (sameSound == false) { + Audio_SeqLayerNoteDecay(layer); + } + layer->note = Audio_AllocNote(layer); + if (layer->note != NULL && layer->note->playbackState.parentLayer == layer) { + Audio_NoteVibratoInit(layer->note); + } + } + + if (layer->note != NULL && layer->note->playbackState.parentLayer == layer) { + Note* note = layer->note; + Audio_NotePortamentoInit(note); + } + return 0; +} + +s32 AudioSeq_SeqLayerProcessScriptStep2(SequenceLayer* layer) { + SequenceChannel* channel = layer->channel; + SeqScriptState* state = &layer->scriptState; + SequencePlayer* seqPlayer = channel->seqPlayer; + u16 sp3A; + u8 cmd; + + for (;;) { + cmd = AudioSeq_ScriptReadU8(state); + if (cmd < 0xC1) { + return cmd; + } + if (cmd >= 0xF2) { + u16 arg = AudioSeq_GetScriptControlFlowArgument(state, cmd); + + if (AudioSeq_HandleScriptFlowControl(seqPlayer, state, cmd, arg) == 0) { + continue; + } + AudioSeq_SeqLayerDisable(layer); + return -1; + } + + switch (cmd) { + case 0xC1: // layer_setshortnotevelocity + case 0xCA: // layer_setpan + { + u8 tempByte = *(state->pc++); + + if (cmd == 0xC1) { + layer->velocitySquare = (f32)(tempByte * tempByte) / 16129.0f; + } else { + layer->pan = tempByte; + } + break; + } + + case 0xC9: // layer_setshortnotegatetime + case 0xC2: // layer_transpose; set transposition in semitones + { + u8 tempByte = *(state->pc++); + + if (cmd == 0xC9) { + layer->gateTime = tempByte; + } else { + layer->transposition = tempByte; + } + break; + } + + case 0xC4: // layer_continuousnoteson + case 0xC5: // layer_continuousnotesoff + if (cmd == 0xC4) { + layer->continuousNotes = true; + } else { + layer->continuousNotes = false; + } + layer->bit1 = false; + Audio_SeqLayerNoteDecay(layer); + break; + + case 0xC3: // layer_setshortnotedefaultdelay + sp3A = AudioSeq_ScriptReadCompressedU16(state); + layer->shortNoteDefaultDelay = sp3A; + break; + + case 0xC6: // layer_setinstr + cmd = AudioSeq_ScriptReadU8(state); + if (cmd >= 0x7E) { + if (cmd == 0x7E) { + layer->instOrWave = 1; + } else if (cmd == 0x7F) { + layer->instOrWave = 0; + } else { + layer->instOrWave = cmd; + layer->instrument = NULL; + } + + if (cmd == 0xFF) { + layer->adsr.releaseRate = 0; + } + + break; + } + + if ((layer->instOrWave = AudioSeq_GetInstrument(channel, cmd, &layer->instrument, &layer->adsr)) == 0) { + layer->instOrWave = 0xFF; + } + break; + + case 0xC7: // layer_portamento + layer->portamento.mode = AudioSeq_ScriptReadU8(state); + + cmd = AudioSeq_ScriptReadU8(state); + cmd += channel->transposition; + cmd += layer->transposition; + cmd += seqPlayer->transposition; + + if (cmd >= 0x80) { + cmd = 0; + } + + layer->portamentoTargetNote = cmd; + + // If special, the next param is u8 instead of var + if (PORTAMENTO_IS_SPECIAL(layer->portamento)) { + layer->portamentoTime = *(state->pc++); + break; + } + + sp3A = AudioSeq_ScriptReadCompressedU16(state); + layer->portamentoTime = sp3A; + break; + + case 0xC8: // layer_disableportamento + layer->portamento.mode = 0; + break; + + case 0xCB: + sp3A = AudioSeq_ScriptReadS16(state); + layer->adsr.envelope = (AdsrEnvelope*)(seqPlayer->seqData + sp3A); + // fallthrough + + case 0xCF: + layer->adsr.releaseRate = AudioSeq_ScriptReadU8(state); + break; + + case 0xCC: + layer->ignoreDrumPan = true; + break; + + case 0xCD: + layer->stereo.asByte = AudioSeq_ScriptReadU8(state); + break; + + case 0xCE: { + u8 tempByte = AudioSeq_ScriptReadU8(state); + layer->unk_34 = gBendPitchTwoSemitonesFrequencies[(tempByte + 0x80) & 0xFF]; + break; + } + + default: + switch (cmd & 0xF0) { + case 0xD0: // layer_setshortnotevelocityfromtable + sp3A = seqPlayer->shortNoteVelocityTable[cmd & 0xF]; + layer->velocitySquare = (f32)(sp3A * sp3A) / 16129.0f; + break; + case 0xE0: // layer_setshortnotegatetimefromtable + layer->gateTime = (u8)seqPlayer->shortNoteGateTimeTable[cmd & 0xF]; + break; + } + } + } +} + +s32 AudioSeq_SeqLayerProcessScriptStep4(SequenceLayer* layer, s32 cmd) { + s32 sameSound = true; + s32 instOrWave; + s32 speed; + f32 temp_f14; + f32 temp_f2; + Portamento* portamento; + f32 freqScale; + f32 freqScale2; + SoundFontSound* sound; + Instrument* instrument; + Drum* drum; + s32 pad; + SequenceChannel* channel; + SequencePlayer* seqPlayer; + u8 semitone = cmd; + u16 sfxId; + s32 semitone2; + s32 vel; + f32 time; + f32 tuning; + + instOrWave = layer->instOrWave; + channel = layer->channel; + seqPlayer = channel->seqPlayer; + + if (instOrWave == 0xFF) { + if (!channel->hasInstrument) { + return -1; + } + instOrWave = channel->instOrWave; + } + + switch (instOrWave) { + case 0: + semitone += channel->transposition + layer->transposition; + layer->semitone = semitone; + drum = Audio_GetDrum(channel->fontId, semitone); + if (drum == NULL) { + layer->stopSomething = true; + layer->delay2 = layer->delay; + return -1; + } + sound = &drum->sound; + layer->adsr.envelope = drum->envelope; + layer->adsr.releaseRate = (u8)drum->releaseRate; + if (!layer->ignoreDrumPan) { + layer->pan = drum->pan; + } + layer->sound = sound; + layer->freqScale = sound->tuning; + break; + + case 1: + layer->semitone = semitone; + sfxId = (layer->transposition << 6) + semitone; + sound = Audio_GetSfx(channel->fontId, sfxId); + if (sound == NULL) { + layer->stopSomething = true; + layer->delay2 = layer->delay + 1; + return -1; + } + layer->sound = sound; + layer->freqScale = sound->tuning; + break; + + default: + semitone += seqPlayer->transposition + channel->transposition + layer->transposition; + semitone2 = semitone; + layer->semitone = semitone; + if (semitone >= 0x80) { + layer->stopSomething = true; + return -1; + } + if (layer->instOrWave == 0xFF) { + instrument = channel->instrument; + } else { + instrument = layer->instrument; + } + + if (layer->portamento.mode != 0) { + portamento = &layer->portamento; + vel = (semitone > layer->portamentoTargetNote) ? semitone : layer->portamentoTargetNote; + + if (instrument != NULL) { + sound = Audio_InstrumentGetSound(instrument, vel); + sameSound = (layer->sound == sound); + layer->sound = sound; + tuning = sound->tuning; + } else { + layer->sound = NULL; + tuning = 1.0f; + if (instOrWave >= 0xC0) { + layer->sound = &gAudioContext.synthesisReverbs[instOrWave - 0xC0].sound; + } + } + + temp_f2 = gNoteFrequencies[semitone2] * tuning; + temp_f14 = gNoteFrequencies[layer->portamentoTargetNote] * tuning; + + switch (PORTAMENTO_MODE(*portamento)) { + case PORTAMENTO_MODE_1: + case PORTAMENTO_MODE_3: + case PORTAMENTO_MODE_5: + freqScale2 = temp_f2; + freqScale = temp_f14; + break; + case PORTAMENTO_MODE_2: + case PORTAMENTO_MODE_4: + freqScale = temp_f2; + freqScale2 = temp_f14; + break; + default: + freqScale = temp_f2; + freqScale2 = temp_f2; + break; + } + + portamento->extent = (freqScale2 / freqScale) - 1.0f; + + if (PORTAMENTO_IS_SPECIAL(*portamento)) { + speed = seqPlayer->tempo * 0x8000 / gAudioContext.tempoInternalToExternal; + if (layer->delay != 0) { + speed = speed * 0x100 / (layer->delay * layer->portamentoTime); + } + } else { + speed = 0x20000 / (layer->portamentoTime * gAudioContext.audioBufferParameters.updatesPerFrame); + } + + if (speed >= 0x7FFF) { + speed = 0x7FFF; + } else if (speed < 1) { + speed = 1; + } + + portamento->speed = speed; + portamento->cur = 0; + layer->freqScale = freqScale; + if (PORTAMENTO_MODE(*portamento) == PORTAMENTO_MODE_5) { + layer->portamentoTargetNote = semitone; + } + break; + } + + if (instrument != NULL) { + sound = Audio_InstrumentGetSound(instrument, semitone); + sameSound = (sound == layer->sound); + layer->sound = sound; + layer->freqScale = gNoteFrequencies[semitone2] * sound->tuning; + } else { + layer->sound = NULL; + layer->freqScale = gNoteFrequencies[semitone2]; + if (instOrWave >= 0xC0) { + layer->sound = &gAudioContext.synthesisReverbs[instOrWave - 0xC0].sound; + } + } + break; + } + + layer->delay2 = layer->delay; + layer->freqScale *= layer->unk_34; + if (layer->delay == 0) { + if (layer->sound != NULL) { + time = (f32)layer->sound->sample->loop->end; + } else { + time = 0.0f; + } + time *= seqPlayer->tempo; + time *= gAudioContext.unk_2870; + time /= layer->freqScale; + if (time > 32766.0f) { + time = 32766.0f; + } + layer->gateDelay = 0; + layer->delay = (u16)(s32)time + 1; + if (layer->portamento.mode != 0) { + // (It's a bit unclear if 'portamento' has actually always been + // set when this is reached...) + if (PORTAMENTO_IS_SPECIAL(*portamento)) { + s32 speed2; + speed2 = seqPlayer->tempo * 0x8000 / gAudioContext.tempoInternalToExternal; + speed2 = speed2 * 0x100 / (layer->delay * layer->portamentoTime); + if (speed2 >= 0x7FFF) { + speed2 = 0x7FFF; + } else if (speed2 < 1) { + speed2 = 1; + } + portamento->speed = speed2; + } + } + } + return sameSound; +} + +s32 AudioSeq_SeqLayerProcessScriptStep3(SequenceLayer* layer, s32 cmd) { + SeqScriptState* state = &layer->scriptState; + u16 delay; + s32 velocity; + SequenceChannel* channel = layer->channel; + SequencePlayer* seqPlayer = channel->seqPlayer; + s32 intDelta; + f32 floatDelta; + + if (cmd == 0xC0) { + layer->delay = AudioSeq_ScriptReadCompressedU16(state); + layer->stopSomething = true; + layer->bit1 = false; + return -1; + } + + layer->stopSomething = false; + if (channel->largeNotes == 1) { + switch (cmd & 0xC0) { + case 0x00: + delay = AudioSeq_ScriptReadCompressedU16(state); + velocity = *(state->pc++); + layer->gateTime = *(state->pc++); + layer->lastDelay = delay; + break; + + case 0x40: + delay = AudioSeq_ScriptReadCompressedU16(state); + velocity = *(state->pc++); + layer->gateTime = 0; + layer->lastDelay = delay; + break; + + case 0x80: + delay = layer->lastDelay; + velocity = *(state->pc++); + layer->gateTime = *(state->pc++); + break; + } + + if (velocity > 0x7F || velocity < 0) { + velocity = 0x7F; + } + layer->velocitySquare = (f32)velocity * (f32)velocity / 16129.0f; + cmd -= (cmd & 0xC0); + } else { + switch (cmd & 0xC0) { + case 0x00: + delay = AudioSeq_ScriptReadCompressedU16(state); + layer->lastDelay = delay; + break; + + case 0x40: + delay = layer->shortNoteDefaultDelay; + break; + + case 0x80: + delay = layer->lastDelay; + break; + } + cmd -= (cmd & 0xC0); + } + + if (channel->velocityRandomVariance != 0) { + floatDelta = + layer->velocitySquare * (f32)(gAudioContext.audioRandom % channel->velocityRandomVariance) / 100.0f; + if ((gAudioContext.audioRandom & 0x8000) != 0) { + floatDelta = -floatDelta; + } + layer->velocitySquare2 = layer->velocitySquare + floatDelta; + if (layer->velocitySquare2 < 0.0f) { + layer->velocitySquare2 = 0.0f; + } else if (layer->velocitySquare2 > 1.0f) { + layer->velocitySquare2 = 1.0f; + } + } else { + layer->velocitySquare2 = layer->velocitySquare; + } + + layer->delay = delay; + layer->gateDelay = (layer->gateTime * delay) >> 8; + if (channel->gateTimeRandomVariance != 0) { + //! @bug should probably be gateTimeRandomVariance + intDelta = (layer->gateDelay * (gAudioContext.audioRandom % channel->velocityRandomVariance)) / 100; + if ((gAudioContext.audioRandom & 0x4000) != 0) { + intDelta = -intDelta; + } + layer->gateDelay += intDelta; + if (layer->gateDelay < 0) { + layer->gateDelay = 0; + } else if (layer->gateDelay > layer->delay) { + layer->gateDelay = layer->delay; + } + } + + if ((seqPlayer->muted && (channel->muteBehavior & (0x40 | 0x10)) != 0) || channel->stopSomething2) { + layer->stopSomething = true; + return -1; + } + if (seqPlayer->skipTicks != 0) { + layer->stopSomething = true; + return -1; + } + return cmd; +} + +void AudioSeq_SetChannelPriorities(SequenceChannel* channel, u8 arg1) { + if ((arg1 & 0xF) != 0) { + channel->notePriority = arg1 & 0xF; + } + arg1 = arg1 >> 4; + if (arg1 != 0) { + channel->someOtherPriority = arg1; + } +} + +u8 AudioSeq_GetInstrument(SequenceChannel* channel, u8 instId, Instrument** instOut, AdsrSettings* adsr) { + Instrument* inst = Audio_GetInstrumentInner(channel->fontId, instId); + + if (inst == NULL) { + *instOut = NULL; + return 0; + } + adsr->envelope = inst->envelope; + adsr->releaseRate = inst->releaseRate; + *instOut = inst; + instId += 2; + return instId; +} + +void AudioSeq_SetInstrument(SequenceChannel* channel, u8 instId) { + if (instId >= 0x80) { + channel->instOrWave = instId; + channel->instrument = NULL; + } else if (instId == 0x7F) { + channel->instOrWave = 0; + channel->instrument = (Instrument*)1; + } else if (instId == 0x7E) { + channel->instOrWave = 1; + channel->instrument = (Instrument*)2; + } else if ((channel->instOrWave = AudioSeq_GetInstrument(channel, instId, &channel->instrument, &channel->adsr)) == + 0) { + channel->hasInstrument = false; + return; + } + channel->hasInstrument = true; +} + +void AudioSeq_SequenceChannelSetVolume(SequenceChannel* channel, u8 volume) { + channel->volume = (f32)(s32)volume / 127.0f; +} + +void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { + s32 i; + u8* data; + u8* test; + SequencePlayer* seqPlayer; + + if (channel->stopScript) { + goto exit_loop; + } + seqPlayer = channel->seqPlayer; + if (seqPlayer->muted && (channel->muteBehavior & 0x80)) { + return; + } + + if (channel->delay >= 2) { + channel->delay--; + goto exit_loop; + } + + while (true) { + SeqScriptState* scriptState = &channel->scriptState; + s32 param; + s16 pad1; + u16 offset; + u32 parameters[3]; + s8 signedParam; + u8 command = AudioSeq_ScriptReadU8(scriptState); + u8 lowBits; + u8 highBits; + s32 result; + s32 pad2; + + if (command >= 0xB0) { + highBits = D_80130520[(s32)command - 0xB0]; + lowBits = highBits & 3; + + for (i = 0; i < lowBits; i++, highBits <<= 1) { + if (!(highBits & 0x80)) { + parameters[i] = AudioSeq_ScriptReadU8(scriptState); + } else { + parameters[i] = AudioSeq_ScriptReadS16(scriptState); + } + } + if (command >= 0xF2) { + result = AudioSeq_HandleScriptFlowControl(seqPlayer, scriptState, command, parameters[0]); + + if (result != 0) { + if (result == -1) { + AudioSeq_SequenceChannelDisable(channel); + } else { + channel->delay = result; + } + break; + } + } else { + switch (command) { + case 0xEA: + channel->stopScript = true; + goto exit_loop; + case 0xF1: + Audio_NotePoolClear(&channel->notePool); + command = (u8)parameters[0]; + Audio_NotePoolFill(&channel->notePool, command); + break; + case 0xF0: + Audio_NotePoolClear(&channel->notePool); + break; + case 0xC2: + offset = (u16)parameters[0]; + channel->dynTable = (void*)&seqPlayer->seqData[offset]; + break; + case 0xC5: + if (scriptState->value != -1) { + + data = (*channel->dynTable)[scriptState->value]; + offset = (u16)((data[0] << 8) + data[1]); + + channel->dynTable = (void*)&seqPlayer->seqData[offset]; + } + break; + case 0xEB: + result = (u8)parameters[0]; + command = (u8)parameters[0]; + + if (seqPlayer->defaultFont != 0xFF) { + offset = ((u16*)gAudioContext.sequenceFontTable)[seqPlayer->seqId]; + lowBits = gAudioContext.sequenceFontTable[offset]; + command = gAudioContext.sequenceFontTable[offset + lowBits - result]; + } + + if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, command)) { + channel->fontId = command; + } + + parameters[0] = parameters[1]; + // NOTE: Intentional fallthrough + case 0xC1: + command = (u8)parameters[0]; + AudioSeq_SetInstrument(channel, command); + break; + case 0xC3: + channel->largeNotes = false; + break; + case 0xC4: + channel->largeNotes = true; + break; + case 0xDF: + command = (u8)parameters[0]; + AudioSeq_SequenceChannelSetVolume(channel, command); + channel->changes.s.volume = true; + break; + case 0xE0: + command = (u8)parameters[0]; + channel->volumeScale = (f32)(s32)command / 128.0f; + channel->changes.s.volume = true; + break; + case 0xDE: + offset = (u16)parameters[0]; + channel->freqScale = (f32)(s32)offset / 32768.0f; + channel->changes.s.freqScale = true; + break; + case 0xD3: + command = (u8)parameters[0]; + command += 0x80; + channel->freqScale = gBendPitchOneOctaveFrequencies[command]; + channel->changes.s.freqScale = true; + break; + case 0xEE: + command = (u8)parameters[0]; + command += 0x80; + channel->freqScale = gBendPitchTwoSemitonesFrequencies[command]; + channel->changes.s.freqScale = true; + break; + case 0xDD: + command = (u8)parameters[0]; + channel->newPan = command; + channel->changes.s.pan = true; + break; + case 0xDC: + command = (u8)parameters[0]; + channel->panChannelWeight = command; + channel->changes.s.pan = true; + break; + case 0xDB: + signedParam = (s8)parameters[0]; + channel->transposition = signedParam; + break; + case 0xDA: + offset = (u16)parameters[0]; + channel->adsr.envelope = (AdsrEnvelope*)&seqPlayer->seqData[offset]; + break; + case 0xD9: + command = (u8)parameters[0]; + channel->adsr.releaseRate = command; + break; + case 0xD8: + command = (u8)parameters[0]; + channel->vibratoExtentTarget = command * 8; + channel->vibratoExtentStart = 0; + channel->vibratoExtentChangeDelay = 0; + break; + case 0xD7: + command = (u8)parameters[0]; + channel->vibratoRateChangeDelay = 0; + channel->vibratoRateTarget = command * 32; + channel->vibratoRateStart = command * 32; + break; + case 0xE2: + command = (u8)parameters[0]; + channel->vibratoExtentStart = command * 8; + command = (u8)parameters[1]; + channel->vibratoExtentTarget = command * 8; + command = (u8)parameters[2]; + channel->vibratoExtentChangeDelay = command * 16; + break; + case 0xE1: + command = (u8)parameters[0]; + channel->vibratoRateStart = command * 32; + command = (u8)parameters[1]; + channel->vibratoRateTarget = command * 32; + command = (u8)parameters[2]; + channel->vibratoRateChangeDelay = command * 16; + break; + case 0xE3: + command = (u8)parameters[0]; + channel->vibratoDelay = command * 16; + break; + case 0xD4: + command = (u8)parameters[0]; + channel->reverb = command; + break; + case 0xC6: + result = (u8)parameters[0]; + command = (u8)parameters[0]; + + if (seqPlayer->defaultFont != 0xFF) { + offset = ((u16*)gAudioContext.sequenceFontTable)[seqPlayer->seqId]; + lowBits = gAudioContext.sequenceFontTable[offset]; + command = gAudioContext.sequenceFontTable[offset + lowBits - result]; + } + + if (AudioHeap_SearchCaches(FONT_TABLE, CACHE_EITHER, command)) { + channel->fontId = command; + } + + break; + case 0xC7: + command = (u8)parameters[0]; + offset = (u16)parameters[1]; + test = &seqPlayer->seqData[offset]; + test[0] = (u8)scriptState->value + command; + break; + case 0xC8: + case 0xCC: + case 0xC9: + signedParam = (s8)parameters[0]; + + if (command == 0xC8) { + scriptState->value -= signedParam; + } else if (command == 0xCC) { + scriptState->value = signedParam; + } else { + scriptState->value &= signedParam; + } + break; + case 0xCD: + command = (u8)parameters[0]; + AudioSeq_SequenceChannelDisable(seqPlayer->channels[command]); + break; + case 0xCA: + command = (u8)parameters[0]; + channel->muteBehavior = command; + channel->changes.s.volume = true; + break; + case 0xCB: + offset = (u16)parameters[0]; + + scriptState->value = *(seqPlayer->seqData + (uintptr_t)(offset + scriptState->value)); + break; + case 0xCE: + offset = (u16)parameters[0]; + channel->unk_22 = offset; + break; + case 0xCF: + offset = (u16)parameters[0]; + test = &seqPlayer->seqData[offset]; + test[0] = (channel->unk_22 >> 8) & 0xFF; + test[1] = channel->unk_22 & 0xFF; + break; + case 0xD0: + command = (u8)parameters[0]; + if (command & 0x80) { + channel->stereoHeadsetEffects = true; + } else { + channel->stereoHeadsetEffects = false; + } + channel->stereo.asByte = command & 0x7F; + break; + case 0xD1: + command = (u8)parameters[0]; + channel->noteAllocPolicy = command; + break; + case 0xD2: + command = (u8)parameters[0]; + channel->adsr.sustain = command; + break; + case 0xE5: + command = (u8)parameters[0]; + channel->reverbIndex = command; + break; + case 0xE4: + if (scriptState->value != -1) { + data = (*channel->dynTable)[scriptState->value]; + //! @bug: Missing a stack depth check here + scriptState->stack[scriptState->depth++] = scriptState->pc; + offset = (u16)((data[0] << 8) + data[1]); + scriptState->pc = seqPlayer->seqData + offset; + } + break; + case 0xE6: + command = (u8)parameters[0]; + channel->bookOffset = command; + break; + case 0xE7: + offset = (u16)parameters[0]; + data = &seqPlayer->seqData[offset]; + channel->muteBehavior = data[0]; + data += 3; + channel->noteAllocPolicy = data[-2]; + AudioSeq_SetChannelPriorities(channel, data[-1]); + channel->transposition = (s8)data[0]; + data += 4; + channel->newPan = data[-3]; + channel->panChannelWeight = data[-2]; + channel->reverb = data[-1]; + channel->reverbIndex = data[0]; + //! @bug: Not marking reverb state as changed + channel->changes.s.pan = true; + break; + case 0xE8: + channel->muteBehavior = parameters[0]; + channel->noteAllocPolicy = parameters[1]; + command = (u8)parameters[2]; + AudioSeq_SetChannelPriorities(channel, command); + channel->transposition = (s8)AudioSeq_ScriptReadU8(scriptState); + channel->newPan = AudioSeq_ScriptReadU8(scriptState); + channel->panChannelWeight = AudioSeq_ScriptReadU8(scriptState); + channel->reverb = AudioSeq_ScriptReadU8(scriptState); + channel->reverbIndex = AudioSeq_ScriptReadU8(scriptState); + //! @bug: Not marking reverb state as changed + channel->changes.s.pan = true; + break; + case 0xEC: + channel->vibratoExtentTarget = 0; + channel->vibratoExtentStart = 0; + channel->vibratoExtentChangeDelay = 0; + channel->vibratoRateTarget = 0; + channel->vibratoRateStart = 0; + channel->vibratoRateChangeDelay = 0; + channel->filter = NULL; + channel->unk_0C = 0; + channel->adsr.sustain = 0; + channel->velocityRandomVariance = 0; + channel->gateTimeRandomVariance = 0; + channel->unk_0F = 0; + channel->unk_20 = 0; + channel->bookOffset = 0; + channel->freqScale = 1.0f; + break; + case 0xE9: + AudioSeq_SetChannelPriorities(channel, (u8)parameters[0]); + break; + case 0xED: + command = (u8)parameters[0]; + channel->unk_0C = command; + break; + case 0xB0: + offset = (u16)parameters[0]; + data = seqPlayer->seqData + offset; + channel->filter = (s16*)data; + break; + case 0xB1: + channel->filter = NULL; + break; + case 0xB3: + command = parameters[0]; + + if (channel->filter != NULL) { + lowBits = (command >> 4) & 0xF; + command &= 0xF; + AudioHeap_LoadFilter(channel->filter, lowBits, command); + } + break; + case 0xB2: + offset = (u16)parameters[0]; + // OTRTODO: Byteswap added for quick audio + channel->unk_22 = _byteswap_ushort(*(u16*)(seqPlayer->seqData + (uintptr_t)(offset + scriptState->value * 2))); + break; + case 0xB4: + channel->dynTable = (void*)&seqPlayer->seqData[channel->unk_22]; + break; + case 0xB5: + // OTRTODO: Byteswap added for quick audio + channel->unk_22 = _byteswap_ushort(((u16*)(channel->dynTable))[scriptState->value]); + break; + case 0xB6: + scriptState->value = (*channel->dynTable)[0][scriptState->value]; + break; + case 0xB7: + channel->unk_22 = (parameters[0] == 0) ? gAudioContext.audioRandom & 0xFFFF + : gAudioContext.audioRandom % parameters[0]; + break; + case 0xB8: + scriptState->value = (parameters[0] == 0) ? gAudioContext.audioRandom & 0xFFFF + : gAudioContext.audioRandom % parameters[0]; + break; + case 0xBD: { + result = Audio_NextRandom(); + channel->unk_22 = (parameters[0] == 0) ? (u32)result & 0xFFFF : (u32)result % parameters[0]; + channel->unk_22 += parameters[1]; + pad2 = (channel->unk_22 / 0x100) + 0x80; // i is wrong here + param = channel->unk_22 % 0x100; + channel->unk_22 = (pad2 << 8) | param; + } break; + case 0xB9: + channel->velocityRandomVariance = parameters[0]; + break; + case 0xBA: + channel->gateTimeRandomVariance = parameters[0]; + break; + case 0xBB: + channel->unk_0F = parameters[0]; + channel->unk_20 = parameters[1]; + break; + case 0xBC: + channel->unk_22 += parameters[0]; + break; + } + } + } else if (command >= 0x70) { + lowBits = command & 0x7; + + if ((command & 0xF8) != 0x70 && lowBits >= 4) { + lowBits = 0; + } + + switch (command & 0xF8) { + case 0x80: + if (channel->layers[lowBits] != NULL) { + scriptState->value = channel->layers[lowBits]->finished; + } else { + scriptState->value = -1; + } + break; + case 0x88: + offset = AudioSeq_ScriptReadS16(scriptState); + if (!AudioSeq_SeqChannelSetLayer(channel, lowBits)) { + channel->layers[lowBits]->scriptState.pc = &seqPlayer->seqData[offset]; + } + break; + case 0x90: + AudioSeq_SeqLayerFree(channel, lowBits); + break; + case 0x98: + if (scriptState->value == -1 || AudioSeq_SeqChannelSetLayer(channel, lowBits) == -1) { + break; + } + + data = (*channel->dynTable)[scriptState->value]; + offset = (data[0] << 8) + data[1]; + channel->layers[lowBits]->scriptState.pc = &seqPlayer->seqData[offset]; + break; + case 0x70: + channel->soundScriptIO[lowBits] = scriptState->value; + break; + case 0x78: + pad1 = AudioSeq_ScriptReadS16(scriptState); + if (!AudioSeq_SeqChannelSetLayer(channel, lowBits)) { + channel->layers[lowBits]->scriptState.pc = &scriptState->pc[pad1]; + } + break; + } + } else { + lowBits = command & 0xF; + + switch (command & 0xF0) { + case 0x00: { + channel->delay = lowBits; + goto exit_loop; + } + case 0x10: + if (lowBits < 8) { + channel->soundScriptIO[lowBits] = -1; + if (AudioLoad_SlowLoadSample(channel->fontId, scriptState->value, + &channel->soundScriptIO[lowBits]) == -1) { + break; + } + } else { + lowBits -= 8; + channel->soundScriptIO[lowBits] = -1; + if (AudioLoad_SlowLoadSample(channel->fontId, channel->unk_22 + 0x100, + &channel->soundScriptIO[lowBits]) == -1) { + break; + } + } + break; + case 0x60: + scriptState->value = channel->soundScriptIO[lowBits]; + if (lowBits < 2) { + channel->soundScriptIO[lowBits] = -1; + } + break; + case 0x50: + scriptState->value -= channel->soundScriptIO[lowBits]; + break; + case 0x20: + offset = AudioSeq_ScriptReadS16(scriptState); + AudioSeq_SequenceChannelEnable(seqPlayer, lowBits, &seqPlayer->seqData[offset]); + break; + case 0x30: + command = AudioSeq_ScriptReadU8(scriptState); + seqPlayer->channels[lowBits]->soundScriptIO[command] = scriptState->value; + break; + case 0x40: + command = AudioSeq_ScriptReadU8(scriptState); + scriptState->value = seqPlayer->channels[lowBits]->soundScriptIO[command]; + break; + } + } + } +exit_loop: + + for (i = 0; i < ARRAY_COUNT(channel->layers); i++) { + if (channel->layers[i] != NULL) { + AudioSeq_SeqLayerProcessScript(channel->layers[i]); + } + } +} + +void AudioSeq_SequencePlayerProcessSequence(SequencePlayer* seqPlayer) { + u8 command; + u8 commandLow; + SeqScriptState* seqScript = &seqPlayer->scriptState; + s16 tempS; + u16 temp; + s32 i; + s32 value; + u8* data; + u8* data2; + u8* data3; + s32 pad3; + s32 dummy; + + if (!seqPlayer->enabled) { + return; + } + + if (!AudioLoad_IsSeqLoadComplete(seqPlayer->seqId) || !AudioLoad_IsFontLoadComplete(seqPlayer->defaultFont)) { + AudioSeq_SequencePlayerDisable(seqPlayer); + return; + } + + AudioLoad_SetSeqLoadStatus(seqPlayer->seqId, 2); + AudioLoad_SetFontLoadStatus(seqPlayer->defaultFont, 2); + + if (seqPlayer->muted && (seqPlayer->muteBehavior & 0x80)) { + return; + } + + seqPlayer->scriptCounter++; + seqPlayer->tempoAcc += seqPlayer->tempo; + seqPlayer->tempoAcc += (s16)seqPlayer->unk_0C; + + if (seqPlayer->tempoAcc < gAudioContext.tempoInternalToExternal) { + return; + } + + seqPlayer->tempoAcc -= (u16)gAudioContext.tempoInternalToExternal; + + if (seqPlayer->stopScript == true) { + return; + } + + if (seqPlayer->delay > 1) { + seqPlayer->delay--; + } else { + seqPlayer->recalculateVolume = true; + + while (true) { + command = AudioSeq_ScriptReadU8(seqScript); + + // 0xF2 and above are "flow control" commands, including termination. + if (command >= 0xF2) { + s32 scriptHandled = AudioSeq_HandleScriptFlowControl( + seqPlayer, seqScript, command, + AudioSeq_GetScriptControlFlowArgument(&seqPlayer->scriptState, command)); + + if (scriptHandled != 0) { + if (scriptHandled == -1) { + AudioSeq_SequencePlayerDisable(seqPlayer); + } else { + seqPlayer->delay = (u16)scriptHandled; + } + break; + } + } else if (command >= 0xC0) { + switch (command) { + case 0xF1: + Audio_NotePoolClear(&seqPlayer->notePool); + command = AudioSeq_ScriptReadU8(seqScript); + Audio_NotePoolFill(&seqPlayer->notePool, command); + // Fake-match: the asm has two breaks in a row here, + // which the compiler normally optimizes out. + dummy = -1; + if (dummy < 0) { + dummy = 0; + } + if (dummy > 1) { + dummy = 1; + } + if (dummy) {} + break; + case 0xF0: + Audio_NotePoolClear(&seqPlayer->notePool); + break; + case 0xDF: + seqPlayer->transposition = 0; + // Note: intentional fallthrough, also executes below command + case 0xDE: + seqPlayer->transposition += (s8)AudioSeq_ScriptReadU8(seqScript); + break; + case 0xDD: + seqPlayer->tempo = AudioSeq_ScriptReadU8(seqScript) * 48; + if (seqPlayer->tempo > gAudioContext.tempoInternalToExternal) { + seqPlayer->tempo = (u16)gAudioContext.tempoInternalToExternal; + } + if ((s16)seqPlayer->tempo <= 0) { + seqPlayer->tempo = 1; + } + break; + case 0xDC: + seqPlayer->unk_0C = (s8)AudioSeq_ScriptReadU8(seqScript) * 48; + break; + case 0xDA: + command = AudioSeq_ScriptReadU8(seqScript); + temp = AudioSeq_ScriptReadS16(seqScript); + switch (command) { + case 0: + case 1: + if (seqPlayer->state != 2) { + seqPlayer->fadeTimerUnkEu = temp; + seqPlayer->state = command; + } + break; + case 2: + seqPlayer->fadeTimer = temp; + seqPlayer->state = command; + seqPlayer->fadeVelocity = (0 - seqPlayer->fadeVolume) / (s32)seqPlayer->fadeTimer; + break; + } + break; + case 0xDB: + value = AudioSeq_ScriptReadU8(seqScript); + switch (seqPlayer->state) { + case 1: + seqPlayer->state = 0; + seqPlayer->fadeVolume = 0.0f; + // NOTE: Intentional fallthrough + case 0: + seqPlayer->fadeTimer = seqPlayer->fadeTimerUnkEu; + if (seqPlayer->fadeTimerUnkEu != 0) { + seqPlayer->fadeVelocity = + ((value / 127.0f) - seqPlayer->fadeVolume) / (s32)(seqPlayer->fadeTimer); + } else { + seqPlayer->fadeVolume = (s32)value / 127.0f; + } + break; + case 2: + break; + } + break; + case 0xD9: + seqPlayer->fadeVolumeScale = (s8)AudioSeq_ScriptReadU8(seqScript) / 127.0f; + break; + case 0xD7: + temp = AudioSeq_ScriptReadS16(seqScript); + AudioSeq_SequencePlayerSetupChannels(seqPlayer, temp); + break; + case 0xD6: + AudioSeq_ScriptReadS16(seqScript); + break; + case 0xD5: + seqPlayer->muteVolumeScale = (s8)AudioSeq_ScriptReadU8(seqScript) / 127.0f; + break; + case 0xD4: + seqPlayer->muted = true; + break; + case 0xD3: + seqPlayer->muteBehavior = AudioSeq_ScriptReadU8(seqScript); + break; + case 0xD1: + case 0xD2: + temp = AudioSeq_ScriptReadS16(seqScript); + data3 = &seqPlayer->seqData[temp]; + if (command == 0xD2) { + seqPlayer->shortNoteVelocityTable = data3; + } else { + seqPlayer->shortNoteGateTimeTable = data3; + } + break; + case 0xD0: + seqPlayer->noteAllocPolicy = AudioSeq_ScriptReadU8(seqScript); + break; + case 0xCE: + command = AudioSeq_ScriptReadU8(seqScript); + if (command == 0) { + seqScript->value = (gAudioContext.audioRandom >> 2) & 0xFF; + } else { + seqScript->value = (gAudioContext.audioRandom >> 2) % command; + } + break; + case 0xCD: { + temp = AudioSeq_ScriptReadS16(seqScript); + + if ((seqScript->value != -1) && (seqScript->depth != 3)) { + data = seqPlayer->seqData + (u32)(temp + (seqScript->value << 1)); + seqScript->stack[seqScript->depth] = seqScript->pc; + seqScript->depth++; + + temp = (data[0] << 8) + data[1]; + seqScript->pc = &seqPlayer->seqData[temp]; + } + break; + } + case 0xCC: + seqScript->value = AudioSeq_ScriptReadU8(seqScript); + break; + case 0xC9: + seqScript->value &= AudioSeq_ScriptReadU8(seqScript); + break; + case 0xC8: + seqScript->value -= AudioSeq_ScriptReadU8(seqScript); + break; + case 0xC7: + command = AudioSeq_ScriptReadU8(seqScript); + temp = AudioSeq_ScriptReadS16(seqScript); + data2 = &seqPlayer->seqData[temp]; + *data2 = (u8)seqScript->value + command; + break; + case 0xC6: + seqPlayer->stopScript = true; + return; + case 0xC5: + seqPlayer->scriptCounter = (u16)AudioSeq_ScriptReadS16(seqScript); + break; + case 0xEF: + AudioSeq_ScriptReadS16(seqScript); + AudioSeq_ScriptReadU8(seqScript); + break; + case 0xC4: + command = AudioSeq_ScriptReadU8(seqScript); + if (command == 0xFF) { + command = seqPlayer->playerIdx; + } + commandLow = AudioSeq_ScriptReadU8(seqScript); + AudioLoad_SyncInitSeqPlayer(command, commandLow, 0); + if (command == (u8)seqPlayer->playerIdx) { + return; + } + break; + } + } else { + commandLow = command & 0x0F; + + switch (command & 0xF0) { + case 0x00: + seqScript->value = seqPlayer->channels[commandLow]->enabled ^ 1; + break; + case 0x50: + seqScript->value -= seqPlayer->soundScriptIO[commandLow]; + break; + case 0x70: + seqPlayer->soundScriptIO[commandLow] = seqScript->value; + break; + case 0x80: + seqScript->value = seqPlayer->soundScriptIO[commandLow]; + if (commandLow < 2) { + seqPlayer->soundScriptIO[commandLow] = -1; + } + break; + case 0x40: + AudioSeq_SequenceChannelDisable(seqPlayer->channels[commandLow]); + break; + case 0x90: + temp = AudioSeq_ScriptReadS16(seqScript); + AudioSeq_SequenceChannelEnable(seqPlayer, commandLow, (void*)&seqPlayer->seqData[temp]); + break; + case 0xA0: + tempS = AudioSeq_ScriptReadS16(seqScript); + AudioSeq_SequenceChannelEnable(seqPlayer, commandLow, (void*)&seqScript->pc[tempS]); + break; + case 0xB0: + command = AudioSeq_ScriptReadU8(seqScript); + temp = AudioSeq_ScriptReadS16(seqScript); + data2 = &seqPlayer->seqData[temp]; + AudioLoad_SlowLoadSeq(command, data2, &seqPlayer->soundScriptIO[commandLow]); + break; + case 0x60: { + command = AudioSeq_ScriptReadU8(seqScript); + value = command; + temp = AudioSeq_ScriptReadU8(seqScript); + + AudioLoad_ScriptLoad(value, temp, &seqPlayer->soundScriptIO[commandLow]); + break; + } + } + } + } + } + + for (i = 0; i < ARRAY_COUNT(seqPlayer->channels); i++) { + if (seqPlayer->channels[i]->enabled) { + AudioSeq_SequenceChannelProcessScript(seqPlayer->channels[i]); + } + } +} + +void AudioSeq_ProcessSequences(s32 arg0) { + SequencePlayer* seqPlayer; + u32 i; + + gAudioContext.noteSubEuOffset = + (gAudioContext.audioBufferParameters.updatesPerFrame - arg0 - 1) * gAudioContext.numNotes; + for (i = 0; i < (u32)gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + seqPlayer = &gAudioContext.seqPlayers[i]; + if (seqPlayer->enabled == 1) { + AudioSeq_SequencePlayerProcessSequence(seqPlayer); + Audio_SequencePlayerProcessSound(seqPlayer); + } + } + Audio_ProcessNotes(); +} + +void AudioSeq_SkipForwardSequence(SequencePlayer* seqPlayer) { + while (seqPlayer->skipTicks > 0) { + AudioSeq_SequencePlayerProcessSequence(seqPlayer); + Audio_SequencePlayerProcessSound(seqPlayer); + seqPlayer->skipTicks--; + } +} + +void AudioSeq_ResetSequencePlayer(SequencePlayer* seqPlayer) { + s32 i; + + AudioSeq_SequencePlayerDisable(seqPlayer); + seqPlayer->stopScript = false; + seqPlayer->delay = 0; + seqPlayer->state = 1; + seqPlayer->fadeTimer = 0; + seqPlayer->fadeTimerUnkEu = 0; + seqPlayer->tempoAcc = 0; + seqPlayer->tempo = 120 * TATUMS_PER_BEAT; // 120 BPM + seqPlayer->unk_0C = 0; + seqPlayer->transposition = 0; + seqPlayer->noteAllocPolicy = 0; + seqPlayer->shortNoteVelocityTable = gDefaultShortNoteVelocityTable; + seqPlayer->shortNoteGateTimeTable = gDefaultShortNoteGateTimeTable; + seqPlayer->scriptCounter = 0; + seqPlayer->fadeVolume = 1.0f; + seqPlayer->fadeVelocity = 0.0f; + seqPlayer->volume = 0.0f; + seqPlayer->muteVolumeScale = 0.5f; + + for (i = 0; i < 0x10; i++) { + AudioSeq_InitSequenceChannel(seqPlayer->channels[i]); + } +} + +void AudioSeq_InitSequencePlayerChannels(s32 playerIdx) { + SequenceChannel* channel; + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[playerIdx]; + s32 i, j; + + for (i = 0; i < 0x10; i++) { + seqPlayer->channels[i] = AudioHeap_AllocZeroed(&gAudioContext.notesAndBuffersPool, sizeof(SequenceChannel)); + if (seqPlayer->channels[i] == NULL) { + seqPlayer->channels[i] = &gAudioContext.sequenceChannelNone; + } else { + channel = seqPlayer->channels[i]; + channel->seqPlayer = seqPlayer; + channel->enabled = false; + for (j = 0; j < 4; j++) { + channel->layers[j] = NULL; + } + } + AudioSeq_InitSequenceChannel(seqPlayer->channels[i]); + } +} + +void AudioSeq_InitSequencePlayer(SequencePlayer* seqPlayer) { + s32 i, j; + + for (i = 0; i < 0x10; i++) { + seqPlayer->channels[i] = &gAudioContext.sequenceChannelNone; + } + + seqPlayer->enabled = false; + seqPlayer->muted = false; + seqPlayer->fontDmaInProgress = false; + seqPlayer->seqDmaInProgress = false; + seqPlayer->unk_0b1 = false; + + for (j = 0; j < 8; j++) { + seqPlayer->soundScriptIO[j] = -1; + } + seqPlayer->muteBehavior = 0x40 | 0x20; + seqPlayer->fadeVolumeScale = 1.0f; + seqPlayer->unk_34 = 1.0f; + Audio_InitNoteLists(&seqPlayer->notePool); + AudioSeq_ResetSequencePlayer(seqPlayer); +} + +void AudioSeq_InitSequencePlayers(void) { + s32 i; + + AudioSeq_InitLayerFreelist(); + for (i = 0; i < 64; i++) { + gAudioContext.sequenceLayers[i].channel = NULL; + gAudioContext.sequenceLayers[i].enabled = false; + } + + for (i = 0; i < 4; i++) { + AudioSeq_InitSequencePlayer(&gAudioContext.seqPlayers[i]); + } +} diff --git a/soh/src/code/audio_sound_params.c b/soh/src/code/audio_sound_params.c new file mode 100644 index 000000000..2b90e5b98 --- /dev/null +++ b/soh/src/code/audio_sound_params.c @@ -0,0 +1,241 @@ +#include "ultra64.h" +#include "global.h" + +SoundParams sEnemyBankParams[] = { + { 0x18, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x30, 0x3 }, + { 0x38, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x2 }, { 0x20, 0x81 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, + { 0x40, 0x3 }, { 0x20, 0x2000 }, { 0x28, 0x3 }, { 0x28, 0x3 }, { 0x20, 0x2 }, { 0x28, 0x3 }, + { 0x38, 0x3 }, { 0x30, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x40, 0x1 }, { 0x18, 0x0 }, { 0x14, 0x0 }, + { 0x14, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x44, 0x3 }, { 0x18, 0x0 }, { 0x30, 0x2 }, + { 0x32, 0x2 }, { 0x38, 0x1 }, { 0x20, 0x0 }, { 0x40, 0x1 }, { 0x18, 0x0 }, { 0x28, 0x0 }, + { 0x18, 0x0 }, { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x14, 0x0 }, { 0x18, 0x80 }, + { 0x38, 0x2 }, { 0x30, 0x0 }, { 0x28, 0x1 }, { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, + { 0x30, 0x0 }, { 0x18, 0x0 }, { 0x20, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x1 }, + { 0x40, 0x1 }, { 0x38, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x1 }, { 0x38, 0x1 }, + { 0x20, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x1 }, { 0x40, 0x1 }, { 0x30, 0x3 }, { 0x30, 0x2000 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x18, 0x3 }, { 0x30, 0x3 }, { 0x28, 0x0 }, { 0x30, 0x0 }, + { 0x18, 0x0 }, { 0x30, 0x0 }, { 0x28, 0x0 }, { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, + { 0x28, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x1 }, { 0x14, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x0 }, + { 0x40, 0x1 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x0 }, { 0x38, 0x1 }, + { 0x20, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x14, 0x0 }, { 0x30, 0x3 }, { 0x20, 0x1 }, + { 0x20, 0x1 }, { 0x30, 0x2 }, { 0x30, 0x2 }, { 0x38, 0x2 }, { 0x40, 0x2 }, { 0x40, 0x2 }, + { 0x14, 0x81 }, { 0x34, 0x0 }, { 0x40, 0x0 }, { 0x20, 0x0 }, { 0x28, 0x0 }, { 0x28, 0x0 }, + { 0x30, 0x0 }, { 0x14, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x20, 0x0 }, { 0x30, 0x3 }, + { 0x30, 0x0 }, { 0x40, 0x1 }, { 0x40, 0x3 }, { 0x40, 0x1 }, { 0x28, 0x1 }, { 0x30, 0x3 }, + { 0x20, 0x0 }, { 0x38, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x0 }, { 0x20, 0x0 }, + { 0x38, 0x2000 }, { 0x30, 0x3 }, { 0x30, 0x2000 }, { 0x30, 0x0 }, { 0x14, 0x0 }, { 0x38, 0x1 }, + { 0x40, 0x1 }, { 0x14, 0x0 }, { 0x20, 0x0 }, { 0x20, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x1 }, + { 0x40, 0x1 }, { 0x30, 0x0 }, { 0x34, 0x1 }, { 0x40, 0x1 }, { 0x30, 0x3 }, { 0x30, 0x2 }, + { 0x30, 0x2000 }, { 0x20, 0x43 }, { 0x20, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x38, 0x3 }, { 0x30, 0x2000 }, { 0x30, 0x3 }, { 0x30, 0x2 }, { 0x30, 0x2000 }, { 0x30, 0x3 }, + { 0x38, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x403 }, { 0x38, 0x1 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x20, 0x0 }, { 0x34, 0x0 }, { 0x18, 0x1 }, { 0x20, 0x2000 }, + { 0x30, 0x2000 }, { 0x14, 0x3 }, { 0x28, 0x3 }, { 0x28, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, + { 0x20, 0x0 }, { 0x14, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x28, 0x0 }, { 0x28, 0x0 }, + { 0x40, 0x1 }, { 0x40, 0x1 }, { 0x20, 0x0 }, { 0x20, 0x0 }, { 0x14, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x38, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x1 }, { 0x28, 0x3 }, + { 0x28, 0x83 }, { 0x28, 0x82 }, { 0x30, 0x3 }, { 0x30, 0x2000 }, { 0x30, 0x2000 }, { 0x38, 0x1 }, + { 0x20, 0x0 }, { 0x34, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x34, 0x2000 }, { 0x20, 0x0 }, + { 0x38, 0x0 }, { 0x40, 0x1 }, { 0x30, 0x3 }, { 0x30, 0x2 }, { 0x30, 0x2 }, { 0x38, 0x3 }, + { 0x30, 0x3 }, { 0x32, 0x3 }, { 0x34, 0x3 }, { 0x34, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x28, 0x82 }, { 0x40, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x20, 0x0 }, { 0x20, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x80 }, { 0x30, 0x3 }, { 0x18, 0x3 }, + { 0x34, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x18, 0x3 }, { 0x30, 0x2000 }, { 0x38, 0x3 }, + { 0x30, 0x3 }, { 0x40, 0x3 }, { 0x40, 0x2000 }, { 0x38, 0x3 }, { 0x30, 0x2000 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x2 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x2000 }, { 0x38, 0x3 }, { 0x38, 0x3 }, { 0x38, 0x3 }, + { 0x38, 0x2000 }, { 0x40, 0x2000 }, { 0x18, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x2 }, { 0x20, 0x2 }, + { 0x24, 0x3 }, { 0x28, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x18, 0x1 }, { 0x34, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x34, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x1 }, { 0x14, 0x0 }, + { 0x40, 0x1 }, { 0x30, 0x1 }, { 0x30, 0x1 }, { 0x30, 0x1 }, { 0x38, 0x3 }, { 0x30, 0x0 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x38, 0x3 }, { 0x30, 0x2 }, { 0x38, 0x3 }, + { 0x38, 0x3 }, { 0x30, 0x83 }, { 0x38, 0x3 }, { 0x30, 0x3 }, { 0x34, 0x3 }, { 0x20, 0x2 }, + { 0x34, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x20, 0x3 }, { 0x14, 0x0 }, { 0x20, 0x1 }, + { 0x30, 0x3 }, { 0x40, 0x1 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x20, 0x0 }, { 0x20, 0x0 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x28, 0x2 }, { 0x30, 0x0 }, + { 0x38, 0x1 }, { 0x28, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x40, 0x3 }, + { 0x14, 0x0 }, { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x30, 0x1 }, { 0x40, 0x1 }, { 0x28, 0x0 }, + { 0x28, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x2000 }, { 0x38, 0x3 }, { 0x38, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x34, 0x3 }, + { 0x38, 0x3 }, { 0x40, 0x3 }, { 0x10, 0x0 }, { 0x34, 0x0 }, { 0x18, 0x0 }, { 0x30, 0x0 }, + { 0x14, 0x0 }, { 0x34, 0x0 }, { 0x28, 0x1 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x30, 0x0 }, + { 0x38, 0x3 }, { 0x20, 0x0 }, { 0x20, 0x2 }, { 0x30, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x38, 0x3 }, { 0x30, 0x3 }, { 0x20, 0x2000 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x40, 0x1 }, + { 0x30, 0x0 }, { 0x20, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x2000 }, + { 0x30, 0x3 }, { 0x30, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x18, 0x0 }, { 0x28, 0x0 }, + { 0x34, 0x0 }, { 0x34, 0x0 }, { 0x34, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x30, 0x3 }, + { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x38, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x40, 0x3 }, { 0x40, 0x2 }, { 0x18, 0x0 }, { 0x44, 0x3 }, { 0x34, 0x0 }, { 0x18, 0x0 }, + { 0x30, 0x0 }, { 0x38, 0x1 }, { 0x40, 0x1 }, { 0x18, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, + { 0x38, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, { 0x36, 0x3 }, { 0x34, 0x3 }, { 0x28, 0x82 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x0 }, { 0x30, 0x2000 }, { 0x30, 0x2000 }, { 0x30, 0x2000 }, { 0x30, 0x0 }, { 0x30, 0x2000 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x28, 0x3 }, { 0x30, 0x3 }, { 0x40, 0x3 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x0 }, { 0x20, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x38, 0x3 }, { 0x38, 0x3 }, + { 0x20, 0x3 }, { 0x30, 0x3 }, { 0x44, 0x3 }, { 0x30, 0x83 }, { 0x30, 0x3 }, { 0x30, 0x3 }, + { 0x34, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x20, 0x2 }, { 0x30, 0x3 }, { 0x20, 0x3 }, + { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x40, 0x2 }, { 0x40, 0x3 }, { 0x34, 0x2 }, { 0x30, 0x3 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x1 }, { 0x34, 0x3 }, { 0x24, 0x3 }, { 0x34, 0x1 }, + { 0x20, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x8, 0x1 }, { 0x30, 0x1 }, { 0x30, 0x3 }, + { 0x38, 0x3 }, { 0x20, 0x2000 }, { 0x34, 0x3 }, { 0x34, 0x2000 }, { 0x8, 0x0 }, { 0x40, 0x3 }, + { 0x34, 0x3 }, +}; + +SoundParams sPlayerBankParams[] = { + { 0x20, 0x480 }, { 0x20, 0x480 }, { 0x20, 0x480 }, { 0x20, 0x480 }, { 0x20, 0x440 }, { 0x20, 0x440 }, + { 0x20, 0x440 }, { 0x20, 0x440 }, { 0x20, 0x480 }, { 0x20, 0x440 }, { 0x20, 0x480 }, { 0x20, 0x400 }, + { 0x20, 0x400 }, { 0x20, 0x400 }, { 0x20, 0x400 }, { 0x20, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, + { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, + { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x400 }, + { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, + { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, + { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, { 0x40, 0x440 }, + { 0x30, 0x80 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x400 }, { 0x30, 0x400 }, + { 0x30, 0x400 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x80 }, { 0x30, 0x400 }, { 0x30, 0x400 }, + { 0x40, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x402 }, { 0x30, 0x400 }, { 0x30, 0x40 }, { 0x30, 0x40 }, + { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x40 }, + { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, + { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, + { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, { 0x80, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x80 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x2000 }, { 0x30, 0xC00 }, { 0x30, 0x400 }, + { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x20, 0x80 }, { 0x20, 0x80 }, { 0x20, 0x80 }, { 0x20, 0x80 }, + { 0x20, 0x40 }, { 0x20, 0x40 }, { 0x20, 0x40 }, { 0x20, 0x40 }, { 0x20, 0x80 }, { 0x20, 0x80 }, + { 0x20, 0x80 }, { 0x20, 0x0 }, { 0x20, 0x0 }, { 0x20, 0x0 }, { 0x20, 0x0 }, { 0x20, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, + { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, + { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x40, 0x0 }, + { 0x40, 0x0 }, { 0x40, 0x0 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, + { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, + { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, { 0x30, 0x440 }, + { 0x30, 0xC00 }, { 0x30, 0x80 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x60, 0x2 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x800 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, +}; + +SoundParams sItemBankParams[] = { + { 0x30, 0x8040 }, { 0x30, 0x40 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x440 }, { 0x30, 0x440 }, + { 0x60, 0x83 }, { 0x30, 0x440 }, { 0x80, 0x43 }, { 0x30, 0x0 }, { 0x30, 0x40 }, { 0x30, 0x400 }, + { 0x30, 0x401 }, { 0x50, 0x0 }, { 0x90, 0x2 }, { 0x50, 0x2 }, { 0x30, 0x400 }, { 0x40, 0x2 }, + { 0x30, 0x40 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x34, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x80 }, { 0x30, 0x40 }, { 0x30, 0x400 }, { 0x20, 0x400 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x60, 0x43 }, + { 0x30, 0x1 }, { 0x30, 0x401 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0xA0, 0x2 }, { 0xA0, 0x2 }, + { 0x30, 0x400 }, { 0x30, 0x0 }, { 0x60, 0x0 }, { 0x60, 0x0 }, { 0x60, 0x0 }, { 0x30, 0x400 }, + { 0x30, 0x0 }, { 0x60, 0x81 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x60, 0x8003 }, { 0x60, 0x8003 }, + { 0x60, 0x8003 }, { 0x30, 0x4000 }, { 0x30, 0x4000 }, { 0x30, 0x40 }, { 0x80, 0x3 }, { 0x80, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x40 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x80, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, +}; + +SoundParams sEnvBankParams[] = { + { 0x70, 0x640 }, { 0x80, 0x40 }, { 0x30, 0x0 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x40, 0x40 }, + { 0x30, 0x480 }, { 0x38, 0x2 }, { 0x30, 0x40 }, { 0x30, 0x40 }, { 0x80, 0x2 }, { 0xA0, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x2 }, { 0x30, 0x40 }, + { 0x30, 0x40 }, { 0x30, 0x0 }, { 0x60, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x82 }, + { 0x30, 0x0 }, { 0x40, 0x0 }, { 0x38, 0x0 }, { 0x28, 0x0 }, { 0x60, 0x0 }, { 0x70, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0xA0, 0x2008 }, { 0x20, 0x2 }, { 0x30, 0x0 }, + { 0x30, 0x800 }, { 0x30, 0x8800 }, { 0x30, 0x8000 }, { 0x30, 0x2 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x400 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x80 }, + { 0x60, 0x42 }, { 0x10, 0x0 }, { 0xA0, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x10 }, { 0x30, 0x3 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x0 }, { 0xA0, 0x3 }, + { 0x30, 0x0 }, { 0x30, 0x400 }, { 0x30, 0x400 }, { 0x70, 0x13 }, { 0x60, 0x8000 }, { 0x30, 0x8000 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x2003 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x2010 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x40, 0x0 }, { 0x30, 0xC2 }, { 0x70, 0x2 }, + { 0x60, 0x2 }, { 0x30, 0x0 }, { 0x60, 0x1 }, { 0x30, 0x2 }, { 0x30, 0x0 }, { 0x90, 0x3 }, + { 0x90, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x3800 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x803 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x2 }, { 0x30, 0x2 }, { 0x40, 0x0 }, { 0x1C, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x60, 0x200 }, { 0x30, 0x800 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x3 }, + { 0x30, 0x3 }, { 0x30, 0x2000 }, { 0x30, 0x2000 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x80 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x800 }, { 0x30, 0x800 }, { 0x30, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x8000 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x60, 0x3 }, + { 0x30, 0x80 }, { 0x30, 0x2000 }, { 0x30, 0x0 }, { 0x30, 0x1 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x2 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0xA0, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0xC0 }, { 0x30, 0x2 }, + { 0x30, 0x2 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x0 }, { 0x30, 0x4083 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x80, 0x0 }, { 0x60, 0x0 }, { 0x90, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x60, 0xC3 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x30, 0x3 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0xA0, 0x800 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x2 }, { 0x30, 0x0 }, { 0x30, 0x3 }, { 0x20, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, +}; + +SoundParams sSystemBankParams[] = { + { 0xC0, 0x0 }, { 0xC0, 0x0 }, { 0xB0, 0x20 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x50, 0x0 }, + { 0x30, 0x20 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x28, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x18, 0x0 }, { 0x2C, 0x0 }, { 0x2C, 0x0 }, { 0x20, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x20, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x60, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, +}; + +SoundParams sOcarinaBankParams[] = { + { 0x30, 0x0 }, { 0x30, 0x20 }, { 0x30, 0x642 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, +}; + +SoundParams sVoiceBankParams[] = { + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x20, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x442 }, { 0x30, 0x442 }, { 0x30, 0x402 }, + { 0x30, 0x402 }, { 0x50, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, + { 0x30, 0x482 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x80, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x20, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x442 }, + { 0x30, 0x442 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x50, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x481 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, + { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x30, 0x402 }, { 0x60, 0x20 }, { 0x30, 0x20 }, + { 0x30, 0x20 }, { 0x60, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x0 }, + { 0x30, 0x0 }, { 0x30, 0x0 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8043 }, { 0x30, 0x8043 }, { 0x30, 0x8043 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, { 0x30, 0x8041 }, + { 0x30, 0x8041 }, { 0x30, 0x8041 }, +}; + +SoundParams* gSoundParams[7] = { + sPlayerBankParams, sItemBankParams, sEnvBankParams, sEnemyBankParams, + sSystemBankParams, sOcarinaBankParams, sVoiceBankParams, +}; diff --git a/soh/src/code/audio_synthesis.c b/soh/src/code/audio_synthesis.c new file mode 100644 index 000000000..e71c6b441 --- /dev/null +++ b/soh/src/code/audio_synthesis.c @@ -0,0 +1,1230 @@ +#include "ultra64.h" +#include "global.h" +#include "mixer.h" + +#define DEFAULT_LEN_1CH 0x1A0 +#define DEFAULT_LEN_2CH 0x340 + +#define DMEM_TEMP 0x3C0 +#define DMEM_UNCOMPRESSED_NOTE 0x580 +#define DMEM_NOTE_PAN_TEMP 0x5C0 +#define DMEM_SCRATCH2 0x760 // = DMEM_TEMP + DEFAULT_LEN_2CH + a bit more +#define DMEM_COMPRESSED_ADPCM_DATA 0x940 // = DMEM_LEFT_CH +#define DMEM_LEFT_CH 0x940 +#define DMEM_RIGHT_CH 0xAE0 +#define DMEM_WET_TEMP 0x3E0 +#define DMEM_WET_SCRATCH 0x720 // = DMEM_WET_TEMP + DEFAULT_LEN_2CH +#define DMEM_WET_LEFT_CH 0xC80 +#define DMEM_WET_RIGHT_CH 0xE20 // = DMEM_WET_LEFT_CH + DEFAULT_LEN_1CH + +Acmd* AudioSynth_LoadRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 length, SynthesisReverb* reverb); +Acmd* AudioSynth_SaveBufferOffset(Acmd* cmd, u16 dmem, u16 offset, s32 length, s16* buf); +Acmd* AudioSynth_SaveRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 length, SynthesisReverb* reverb); +Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updateIndex); +Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s16* aiBuf, + s32 aiBufLen, Acmd* cmd, s32 updateIndex); +Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 nSamplesToLoad); +Acmd* AudioSynth_NoteApplyHeadsetPanEffects(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 bufLen, + s32 flags, s32 side); +Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 aiBufLen, + u16 inBuf, s32 headsetPanSettings, s32 flags); +Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 count, u16 pitch, u16 inpDmem, + s32 resampleFlags); + +u32 D_801304A0 = 0x13000000; +u32 D_801304A4 = 0x5CAEC8E2; +u32 D_801304A8 = 0x945CC8E2; +u32 D_801304AC = 0x94AEC8E2; + +u16 D_801304B0[] = { + 0x7FFF, 0xD001, 0x3FFF, 0xF001, 0x5FFF, 0x9001, 0x7FFF, 0x8001, +}; + +u8 D_801304C0[] = { 0x40, 0x20, 0x10, 0x8 }; + +void AudioSynth_InitNextRingBuf(s32 chunkLen, s32 bufIndex, s32 reverbIndex) { + ReverbRingBufferItem* bufItem; + s32 pad[3]; + SynthesisReverb* reverb = &gAudioContext.synthesisReverbs[reverbIndex]; + s32 temp_a0_2; + s32 temp_a0_4; + s32 sampleCnt; + s32 extraSamples; + s32 i; + s32 j; + + if (reverb->downsampleRate >= 2) { + if (reverb->framesToIgnore == 0) { + bufItem = &reverb->items[reverb->curFrame][bufIndex]; + Audio_InvalDCache(bufItem->toDownsampleLeft, DEFAULT_LEN_2CH); + + for (j = 0, i = 0; i < bufItem->lengthA / 2; j += reverb->downsampleRate, i++) { + reverb->leftRingBuf[bufItem->startPos + i] = bufItem->toDownsampleLeft[j]; + reverb->rightRingBuf[bufItem->startPos + i] = bufItem->toDownsampleRight[j]; + } + + for (i = 0; i < bufItem->lengthB / 2; j += reverb->downsampleRate, i++) { + reverb->leftRingBuf[i] = bufItem->toDownsampleLeft[j]; + reverb->rightRingBuf[i] = bufItem->toDownsampleRight[j]; + } + } + } + + bufItem = &reverb->items[reverb->curFrame][bufIndex]; + sampleCnt = chunkLen / reverb->downsampleRate; + extraSamples = (sampleCnt + reverb->nextRingBufPos) - reverb->bufSizePerChan; + temp_a0_2 = reverb->nextRingBufPos; + if (extraSamples < 0) { + bufItem->lengthA = sampleCnt * 2; + bufItem->lengthB = 0; + bufItem->startPos = reverb->nextRingBufPos; + reverb->nextRingBufPos += sampleCnt; + } else { + bufItem->lengthA = (sampleCnt - extraSamples) * 2; + bufItem->lengthB = extraSamples * 2; + bufItem->startPos = reverb->nextRingBufPos; + reverb->nextRingBufPos = extraSamples; + } + + bufItem->numSamplesAfterDownsampling = sampleCnt; + bufItem->chunkLen = chunkLen; + + if (reverb->unk_14 != 0) { + temp_a0_4 = reverb->unk_14 + temp_a0_2; + if (temp_a0_4 >= reverb->bufSizePerChan) { + temp_a0_4 -= reverb->bufSizePerChan; + } + bufItem = &reverb->items2[reverb->curFrame][bufIndex]; + sampleCnt = chunkLen / reverb->downsampleRate; + extraSamples = (temp_a0_4 + sampleCnt) - reverb->bufSizePerChan; + if (extraSamples < 0) { + bufItem->lengthA = sampleCnt * 2; + bufItem->lengthB = 0; + bufItem->startPos = temp_a0_4; + } else { + bufItem->lengthA = (sampleCnt - extraSamples) * 2; + bufItem->lengthB = extraSamples * 2; + bufItem->startPos = temp_a0_4; + } + bufItem->numSamplesAfterDownsampling = sampleCnt; + bufItem->chunkLen = chunkLen; + } +} + +void func_800DB03C(s32 arg0) { + NoteSubEu* subEu; + NoteSubEu* subEu2; + s32 baseIndex; + s32 i; + + baseIndex = gAudioContext.numNotes * arg0; + for (i = 0; i < gAudioContext.numNotes; i++) { + subEu = &gAudioContext.notes[i].noteSubEu; + subEu2 = &gAudioContext.noteSubsEu[baseIndex + i]; + if (subEu->bitField0.enabled) { + subEu->bitField0.needsInit = false; + } else { + subEu2->bitField0.enabled = false; + } + + subEu->unk_06 = 0; + } +} + +Acmd* AudioSynth_Update(Acmd* cmdStart, s32* cmdCnt, s16* aiStart, s32 aiBufLen) { + s32 chunkLen; + s16* aiBufP; + Acmd* cmdP; + s32 i; + s32 j; + SynthesisReverb* reverb; + + cmdP = cmdStart; + for (i = gAudioContext.audioBufferParameters.updatesPerFrame; i > 0; i--) { + AudioSeq_ProcessSequences(i - 1); + func_800DB03C(gAudioContext.audioBufferParameters.updatesPerFrame - i); + } + + aiBufP = aiStart; + gAudioContext.curLoadedBook = NULL; + for (i = gAudioContext.audioBufferParameters.updatesPerFrame; i > 0; i--) { + if (i == 1) { + chunkLen = aiBufLen; + } else if ((aiBufLen / i) >= gAudioContext.audioBufferParameters.samplesPerUpdateMax) { + chunkLen = gAudioContext.audioBufferParameters.samplesPerUpdateMax; + } else if (gAudioContext.audioBufferParameters.samplesPerUpdateMin >= (aiBufLen / i)) { + chunkLen = gAudioContext.audioBufferParameters.samplesPerUpdateMin; + } else { + chunkLen = gAudioContext.audioBufferParameters.samplesPerUpdate; + } + + for (j = 0; j < gAudioContext.numSynthesisReverbs; j++) { + if (gAudioContext.synthesisReverbs[j].useReverb) { + AudioSynth_InitNextRingBuf(chunkLen, gAudioContext.audioBufferParameters.updatesPerFrame - i, j); + } + } + + cmdP = AudioSynth_DoOneAudioUpdate(aiBufP, chunkLen, cmdP, gAudioContext.audioBufferParameters.updatesPerFrame - i); + aiBufLen -= chunkLen; + aiBufP += chunkLen * 2; + } + + for (j = 0; j < gAudioContext.numSynthesisReverbs; j++) { + if (gAudioContext.synthesisReverbs[j].framesToIgnore != 0) { + gAudioContext.synthesisReverbs[j].framesToIgnore--; + } + gAudioContext.synthesisReverbs[j].curFrame ^= 1; + } + + *cmdCnt = cmdP - cmdStart; + return cmdP; +} + +void func_800DB2C0(s32 updateIndexStart, s32 noteIndex) { + NoteSubEu* temp_v1; + s32 i; + + for (i = updateIndexStart + 1; i < gAudioContext.audioBufferParameters.updatesPerFrame; i++) { + temp_v1 = &gAudioContext.noteSubsEu[(gAudioContext.numNotes * i) + noteIndex]; + if (!temp_v1->bitField0.needsInit) { + temp_v1->bitField0.enabled = 0; + } else { + break; + } + } +} + +Acmd* AudioSynth_LoadRingBuffer1AtTemp(Acmd* cmd, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][bufIndex]; + + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP, bufItem->startPos, bufItem->lengthA, reverb); + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP + bufItem->lengthA, 0, bufItem->lengthB, reverb); + } + return cmd; +} + +Acmd* AudioSynth_SaveRingBuffer1AtTemp(Acmd* cmd, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][bufIndex]; + + cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_TEMP, bufItem->startPos, bufItem->lengthA, reverb); + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_TEMP + bufItem->lengthA, 0, bufItem->lengthB, reverb); + } + return cmd; +} + +Acmd* AudioSynth_LeakReverb(Acmd* cmd, SynthesisReverb* reverb) { + // Leak some audio from the left reverb channel into the right reverb channel and vice versa (pan) + aDMEMMove(cmd++, DMEM_WET_LEFT_CH, DMEM_WET_SCRATCH, DEFAULT_LEN_1CH); + aMix(cmd++, 0x1A, reverb->leakRtl, DMEM_WET_RIGHT_CH, DMEM_WET_LEFT_CH); + aMix(cmd++, 0x1A, reverb->leakLtr, DMEM_WET_SCRATCH, DMEM_WET_RIGHT_CH); + return cmd; +} + +Acmd* func_800DB4E4(Acmd* cmd, s32 arg1, SynthesisReverb* reverb, s16 arg3) { + ReverbRingBufferItem* item = &reverb->items[reverb->curFrame][arg3]; + s16 offsetA; + s16 offsetB; + + offsetA = (item->startPos & 7) * 2; + offsetB = ALIGN16(offsetA + item->lengthA); + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP, item->startPos - (offsetA / 2), DEFAULT_LEN_1CH, reverb); + if (item->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP + offsetB, 0, DEFAULT_LEN_1CH - offsetB, reverb); + } + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + offsetA, DMEM_WET_LEFT_CH, arg1 * 2); + aResample(cmd++, reverb->resampleFlags, reverb->unk_0E, reverb->unk_30); + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + DEFAULT_LEN_1CH + offsetA, DMEM_WET_RIGHT_CH, arg1 * 2); + aResample(cmd++, reverb->resampleFlags, reverb->unk_0E, reverb->unk_34); + return cmd; +} + +Acmd* func_800DB680(Acmd* cmd, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][bufIndex]; + + aSetBuffer(cmd++, 0, DMEM_WET_LEFT_CH, DMEM_WET_SCRATCH, bufItem->unk_18 * 2); + aResample(cmd++, reverb->resampleFlags, bufItem->unk_16, reverb->unk_38); + + cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH, bufItem->startPos, bufItem->lengthA, reverb->leftRingBuf); + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH + bufItem->lengthA, 0, bufItem->lengthB, + reverb->leftRingBuf); + } + aSetBuffer(cmd++, 0, DMEM_WET_RIGHT_CH, DMEM_WET_SCRATCH, bufItem->unk_18 * 2); + aResample(cmd++, reverb->resampleFlags, bufItem->unk_16, reverb->unk_3C); + cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH, bufItem->startPos, bufItem->lengthA, reverb->rightRingBuf); + + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveBufferOffset(cmd, DMEM_WET_SCRATCH + bufItem->lengthA, 0, bufItem->lengthB, + reverb->rightRingBuf); + } + + return cmd; +} + +Acmd* func_800DB828(Acmd* cmd, s32 arg1, SynthesisReverb* reverb, s16 arg3) { + ReverbRingBufferItem* item = &reverb->items[reverb->curFrame][arg3]; + s16 offsetA; + s16 offsetB; + + item->unk_14 = (item->unk_18 << 0xF) / arg1; + offsetA = (item->startPos & 7) * 2; + item->unk_16 = (arg1 << 0xF) / item->unk_18; + offsetB = ALIGN16(offsetA + item->lengthA); + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP, item->startPos - (offsetA / 2), DEFAULT_LEN_1CH, reverb); + if (item->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_TEMP + offsetB, 0, DEFAULT_LEN_1CH - offsetB, reverb); + } + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + offsetA, DMEM_WET_LEFT_CH, arg1 * 2); + aResample(cmd++, reverb->resampleFlags, item->unk_14, reverb->unk_30); + aSetBuffer(cmd++, 0, DMEM_WET_TEMP + DEFAULT_LEN_1CH + offsetA, DMEM_WET_RIGHT_CH, arg1 * 2); + aResample(cmd++, reverb->resampleFlags, item->unk_14, reverb->unk_34); + return cmd; +} + +Acmd* AudioSynth_FilterReverb(Acmd* cmd, s32 count, SynthesisReverb* reverb) { + // Apply a filter (convolution) to each reverb channel. + if (reverb->filterLeft != NULL) { + aFilter(cmd++, 2, count, reverb->filterLeft); + aFilter(cmd++, reverb->resampleFlags, DMEM_WET_LEFT_CH, reverb->filterLeftState); + } + + if (reverb->filterRight != NULL) { + aFilter(cmd++, 2, count, reverb->filterRight); + aFilter(cmd++, reverb->resampleFlags, DMEM_WET_RIGHT_CH, reverb->filterRightState); + } + return cmd; +} + +Acmd* AudioSynth_MaybeMixRingBuffer1(Acmd* cmd, SynthesisReverb* reverb, s32 arg2) { + SynthesisReverb* temp_a3; + + temp_a3 = &gAudioContext.synthesisReverbs[reverb->unk_05]; + if (temp_a3->downsampleRate == 1) { + cmd = AudioSynth_LoadRingBuffer1AtTemp(cmd, temp_a3, arg2); + aMix(cmd++, 0x34, reverb->unk_08, DMEM_WET_LEFT_CH, DMEM_WET_TEMP); + cmd = AudioSynth_SaveRingBuffer1AtTemp(cmd, temp_a3, arg2); + } + return cmd; +} + +void func_800DBB94(void) { +} + +void AudioSynth_ClearBuffer(Acmd* cmd, s32 arg1, s32 arg2) { + aClearBuffer(cmd, arg1, arg2); +} + +void func_800DBBBC(void) { +} + +void func_800DBBC4(void) { +} + +void func_800DBBCC(void) { +} + +void AudioSynth_Mix(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + aMix(cmd, arg1, arg2, arg3, arg4); +} + +void func_800DBC08(void) { +} + +void func_800DBC10(void) { +} + +void func_800DBC18(void) { +} + +void AudioSynth_SetBuffer(Acmd* cmd, s32 flags, s32 dmemIn, s32 dmemOut, s32 count) { + aSetBuffer(cmd, flags, dmemIn, dmemOut, count); +} + +void func_800DBC54(void) { +} + +void func_800DBC5C(void) { +} + +// possible fake match? +void AudioSynth_DMemMove(Acmd* cmd, s32 dmemIn, s32 dmemOut, s32 count) { + aDMEMMove(cmd, dmemIn, dmemOut, count); +} + +void func_800DBC90(void) { +} + +void func_800DBC98(void) { +} + +void func_800DBCA0(void) { +} + +void func_800DBCA8(void) { +} + +void AudioSynth_InterL(Acmd* cmd, s32 dmemIn, s32 dmemOut, s32 count) { + aInterl(cmd, dmemIn, dmemOut, count); +} + +void AudioSynth_EnvSetup1(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + aEnvSetup1(cmd, arg1, arg2, arg3, arg4); +} + +void func_800DBD08(void) { +} + +void AudioSynth_LoadBuffer(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3) { + aLoadBuffer(cmd, arg3, arg1, arg2); +} + +void AudioSynth_SaveBuffer(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3) { + aSaveBuffer(cmd, arg1, arg3, arg2); +} + +void AudioSynth_EnvSetup2(Acmd* cmd, s32 volLeft, s32 volRight) { + aEnvSetup2(cmd, volLeft, volRight); +} + +void func_800DBD7C(void) { +} + +void func_800DBD84(void) { +} + +void func_800DBD8C(void) { +} + +void AudioSynth_S8Dec(Acmd* cmd, s32 flags, s16* state) { + aS8Dec(cmd, flags, state); +} + +void AudioSynth_HiLoGain(Acmd* cmd, s32 gain, s32 dmemIn, s32 dmemOut, s32 count) { + aHiLoGain(cmd, gain, count, dmemIn, dmemOut); +} + +void AudioSynth_UnkCmd19(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + aUnkCmd19(cmd, arg1, arg2, arg3, arg4); +} + +void func_800DBE18(void) { +} + +void func_800DBE20(void) { +} + +void func_800DBE28(void) { +} + +void func_800DBE30(void) { +} + +void AudioSynth_UnkCmd3(Acmd* cmd, s32 arg1, s32 arg2, s32 arg3) { + aUnkCmd3(cmd, arg1, arg2, arg3); +} + +void func_800DBE5C(void) { +} + +void func_800DBE64(void) { +} + +void func_800DBE6C(void) { +} + +void AudioSynth_LoadFilter(Acmd* cmd, s32 flags, s32 countOrBuf, s32 addr) { + aFilter(cmd, flags, countOrBuf, addr); +} + +void AudioSynth_LoadFilterCount(Acmd* cmd, s32 count, s32 addr) { + aFilter(cmd, 2, count, addr); +} + +Acmd* AudioSynth_LoadRingBuffer1(Acmd* cmd, s32 arg1, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* ringBufferItem = &reverb->items[reverb->curFrame][bufIndex]; + + cmd = + AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH, ringBufferItem->startPos, ringBufferItem->lengthA, reverb); + if (ringBufferItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH + ringBufferItem->lengthA, 0, ringBufferItem->lengthB, + reverb); + } + + return cmd; +} + +Acmd* AudioSynth_LoadRingBuffer2(Acmd* cmd, s32 arg1, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* bufItem = &reverb->items2[reverb->curFrame][bufIndex]; + + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH, bufItem->startPos, bufItem->lengthA, reverb); + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_LoadRingBufferPart(cmd, DMEM_WET_LEFT_CH + bufItem->lengthA, 0, bufItem->lengthB, reverb); + } + return cmd; +} + +Acmd* AudioSynth_LoadRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 length, SynthesisReverb* reverb) { + aLoadBuffer(cmd++, &reverb->leftRingBuf[startPos], dmem, length); + aLoadBuffer(cmd++, &reverb->rightRingBuf[startPos], dmem + DEFAULT_LEN_1CH, length); + return cmd; +} + +Acmd* AudioSynth_SaveRingBufferPart(Acmd* cmd, u16 dmem, u16 startPos, s32 length, SynthesisReverb* reverb) { + aSaveBuffer(cmd++, dmem, &reverb->leftRingBuf[startPos], length); + aSaveBuffer(cmd++, dmem + DEFAULT_LEN_1CH, &reverb->rightRingBuf[startPos], length); + return cmd; +} + +Acmd* AudioSynth_SaveBufferOffset(Acmd* cmd, u16 dmem, u16 offset, s32 length, s16* buf) { + aSaveBuffer(cmd++, dmem, &buf[offset], length); + return cmd; +} + +Acmd* AudioSynth_MaybeLoadRingBuffer2(Acmd* cmd, s32 arg1, SynthesisReverb* reverb, s16 bufIndex) { + if (reverb->downsampleRate == 1) { + cmd = AudioSynth_LoadRingBuffer2(cmd, arg1, reverb, bufIndex); + } + + return cmd; +} + +Acmd* func_800DC164(Acmd* cmd, s32 arg1, SynthesisReverb* reverb, s16 arg3) { + // Sets DMEM_WET_{LEFT,RIGHT}_CH, clobbers DMEM_TEMP + if (reverb->downsampleRate == 1) { + if (reverb->unk_18 != 0) { + cmd = func_800DB828(cmd, arg1, reverb, arg3); + } else { + cmd = AudioSynth_LoadRingBuffer1(cmd, arg1, reverb, arg3); + } + } else { + cmd = func_800DB4E4(cmd, arg1, reverb, arg3); + } + return cmd; +} + +Acmd* AudioSynth_SaveReverbSamples(Acmd* cmd, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* bufItem = &reverb->items[reverb->curFrame][bufIndex]; + + if (reverb->downsampleRate == 1) { + if (reverb->unk_18 != 0) { + cmd = func_800DB680(cmd, reverb, bufIndex); + } else { + // Put the oldest samples in the ring buffer into the wet channels + cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH, bufItem->startPos, bufItem->lengthA, reverb); + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH + bufItem->lengthA, 0, bufItem->lengthB, + reverb); + } + } + } else { + // Downsampling is done later by CPU when RSP is done, therefore we need to have + // double buffering. Left and right buffers are adjacent in memory. + AudioSynth_SaveBuffer(cmd++, DMEM_WET_LEFT_CH, DEFAULT_LEN_2CH, + reverb->items[reverb->curFrame][bufIndex].toDownsampleLeft); + } + + reverb->resampleFlags = 0; + return cmd; +} + +Acmd* AudioSynth_SaveRingBuffer2(Acmd* cmd, SynthesisReverb* reverb, s16 bufIndex) { + ReverbRingBufferItem* bufItem = &reverb->items2[reverb->curFrame][bufIndex]; + + cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH, bufItem->startPos, bufItem->lengthA, reverb); + if (bufItem->lengthB != 0) { + // Ring buffer wrapped + cmd = AudioSynth_SaveRingBufferPart(cmd, DMEM_WET_LEFT_CH + bufItem->lengthA, 0, bufItem->lengthB, reverb); + } + return cmd; +} + +Acmd* AudioSynth_DoOneAudioUpdate(s16* aiBuf, s32 aiBufLen, Acmd* cmd, s32 updateIndex) { + u8 noteIndices[0x5C]; + s16 count; + s16 reverbIndex; + SynthesisReverb* reverb; + s32 useReverb; + s32 t; + s32 i; + NoteSubEu* noteSubEu; + NoteSubEu* noteSubEu2; + s32 unk14; + + t = gAudioContext.numNotes * updateIndex; + count = 0; + if (gAudioContext.numSynthesisReverbs == 0) { + for (i = 0; i < gAudioContext.numNotes; i++) { + if (gAudioContext.noteSubsEu[t + i].bitField0.enabled) { + noteIndices[count++] = i; + } + } + } else { + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + for (i = 0; i < gAudioContext.numNotes; i++) { + noteSubEu = &gAudioContext.noteSubsEu[t + i]; + if (noteSubEu->bitField0.enabled && noteSubEu->bitField1.reverbIndex == reverbIndex) { + noteIndices[count++] = i; + } + } + } + + for (i = 0; i < gAudioContext.numNotes; i++) { + noteSubEu = &gAudioContext.noteSubsEu[t + i]; + if (noteSubEu->bitField0.enabled && noteSubEu->bitField1.reverbIndex >= gAudioContext.numSynthesisReverbs) { + noteIndices[count++] = i; + } + } + } + + aClearBuffer(cmd++, DMEM_LEFT_CH, DEFAULT_LEN_2CH); + i = 0; + for (reverbIndex = 0; reverbIndex < gAudioContext.numSynthesisReverbs; reverbIndex++) { + reverb = &gAudioContext.synthesisReverbs[reverbIndex]; + useReverb = reverb->useReverb; + if (useReverb) { + cmd = func_800DC164(cmd, aiBufLen, reverb, updateIndex); + aMix(cmd++, 0x34, reverb->unk_0A, DMEM_WET_LEFT_CH, DMEM_LEFT_CH); + unk14 = reverb->unk_14; + if (unk14) { + aDMEMMove(cmd++, DMEM_WET_LEFT_CH, DMEM_WET_TEMP, DEFAULT_LEN_2CH); + } + + aMix(cmd++, 0x34, reverb->unk_0C + 0x8000, DMEM_WET_LEFT_CH, DMEM_WET_LEFT_CH); + if (reverb->leakRtl != 0 || reverb->leakLtr != 0) { + cmd = AudioSynth_LeakReverb(cmd, reverb); + } + + if (unk14) { + cmd = AudioSynth_SaveReverbSamples(cmd, reverb, updateIndex); + if (reverb->unk_05 != -1) { + cmd = AudioSynth_MaybeMixRingBuffer1(cmd, reverb, updateIndex); + } + cmd = AudioSynth_MaybeLoadRingBuffer2(cmd, aiBufLen, reverb, updateIndex); + aMix(cmd++, 0x34, reverb->unk_16, DMEM_WET_TEMP, DMEM_WET_LEFT_CH); + } + } + + while (i < count) { + noteSubEu2 = &gAudioContext.noteSubsEu[noteIndices[i] + t]; + if (noteSubEu2->bitField1.reverbIndex == reverbIndex) { + cmd = AudioSynth_ProcessNote(noteIndices[i], noteSubEu2, + &gAudioContext.notes[noteIndices[i]].synthesisState, aiBuf, aiBufLen, cmd, + updateIndex); + } else { + break; + } + i++; + } + + if (useReverb) { + if (reverb->filterLeft != NULL || reverb->filterRight != NULL) { + cmd = AudioSynth_FilterReverb(cmd, aiBufLen * 2, reverb); + } + if (unk14) { + cmd = AudioSynth_SaveRingBuffer2(cmd, reverb, updateIndex); + } else { + cmd = AudioSynth_SaveReverbSamples(cmd, reverb, updateIndex); + if (reverb->unk_05 != -1) { + cmd = AudioSynth_MaybeMixRingBuffer1(cmd, reverb, updateIndex); + } + } + } + } + + while (i < count) { + cmd = AudioSynth_ProcessNote(noteIndices[i], &gAudioContext.noteSubsEu[t + noteIndices[i]], + &gAudioContext.notes[noteIndices[i]].synthesisState, aiBuf, aiBufLen, cmd, + updateIndex); + i++; + } + + updateIndex = aiBufLen * 2; + aInterleave(cmd++, DMEM_TEMP, DMEM_LEFT_CH, DMEM_RIGHT_CH, updateIndex); + aSaveBuffer(cmd++, DMEM_TEMP, aiBuf, updateIndex * 2); + + return cmd; +} + +Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s16* aiBuf, + s32 aiBufLen, Acmd* cmd, s32 updateIndex) { + s32 pad1[3]; + SoundFontSample* audioFontSample; + AdpcmLoop* loopInfo; + s32 nSamplesUntilLoopEnd; + s32 nSamplesInThisIteration; + s32 noteFinished; + s32 restart; + s32 flags; + u16 resamplingRateFixedPoint; + s32 nSamplesInFirstFrame; + s32 nTrailingSamplesToIgnore; + s32 phi_a1_2; + s32 frameIndex; + s32 skipBytes; + s32 temp_v1_6; + void* buf; + s32 nSamplesToDecode; + u32 sampleAddr; + u32 samplesLenFixedPoint; + s32 samplesLenAdjusted; + s32 nSamplesProcessed; + s32 loopEndPos; + s32 nSamplesToProcess; + s32 phi_s4; + s32 nFirstFrameSamplesToIgnore; + s32 pad2[7]; + s32 frameSize; + s32 nFramesToDecode; + s32 skipInitialSamples; + s32 sampleDataStart; + u8* sampleData; + s32 nParts; + s32 curPart; + s32 sampleDataStartPad; + s32 side; + s32 resampledTempLen; + u16 noteSamplesDmemAddrBeforeResampling; + s32 sampleDataOffset; + s32 thing; + s32 s5; + Note* note; + u32 nSamplesToLoad; + u16 unk7; + u16 unkE; + s16* filter; + s32 bookOffset; + s32 finished; + s32 aligned; + s16 addr; + u16 unused; + + bookOffset = noteSubEu->bitField1.bookOffset; + finished = noteSubEu->bitField0.finished; + note = &gAudioContext.notes[noteIndex]; + flags = A_CONTINUE; + + if (noteSubEu->bitField0.needsInit == true) { + flags = A_INIT; + synthState->restart = 0; + synthState->samplePosInt = note->unk_BC; + synthState->samplePosFrac = 0; + synthState->curVolLeft = 0; + synthState->curVolRight = 0; + synthState->prevHeadsetPanRight = 0; + synthState->prevHeadsetPanLeft = 0; + synthState->reverbVol = noteSubEu->reverbVol; + synthState->numParts = 0; + synthState->unk_1A = 1; + note->noteSubEu.bitField0.finished = false; + finished = false; + } + + resamplingRateFixedPoint = noteSubEu->resamplingRateFixedPoint; + nParts = noteSubEu->bitField1.hasTwoParts + 1; + samplesLenFixedPoint = (resamplingRateFixedPoint * aiBufLen * 2) + synthState->samplePosFrac; + nSamplesToLoad = samplesLenFixedPoint >> 16; + synthState->samplePosFrac = samplesLenFixedPoint & 0xFFFF; + + // Partially-optimized out no-op ifs required for matching. SM64 decomp + // makes it clear that this is how it should look. + if (synthState->numParts == 1 && nParts == 2) { + } else if (synthState->numParts == 2 && nParts == 1) { + } else { + } + + synthState->numParts = nParts; + + if (noteSubEu->bitField1.isSyntheticWave) { + cmd = AudioSynth_LoadWaveSamples(cmd, noteSubEu, synthState, nSamplesToLoad); + noteSamplesDmemAddrBeforeResampling = DMEM_UNCOMPRESSED_NOTE + (synthState->samplePosInt * 2); + synthState->samplePosInt += nSamplesToLoad; + } else { + audioFontSample = noteSubEu->sound.soundFontSound->sample; + loopInfo = audioFontSample->loop; + loopEndPos = loopInfo->end; + sampleAddr = audioFontSample->sampleAddr; + resampledTempLen = 0; + + for (curPart = 0; curPart < nParts; curPart++) { + nSamplesProcessed = 0; + s5 = 0; + + if (nParts == 1) { + samplesLenAdjusted = nSamplesToLoad; + } else if (nSamplesToLoad & 1) { + samplesLenAdjusted = (nSamplesToLoad & ~1) + (curPart * 2); + } else { + samplesLenAdjusted = nSamplesToLoad; + } + + if (audioFontSample->codec == CODEC_ADPCM || audioFontSample->codec == CODEC_SMALL_ADPCM) { + if (gAudioContext.curLoadedBook != audioFontSample->book->book) { + u32 nEntries; + switch (bookOffset) { + case 1: + gAudioContext.curLoadedBook = &D_8012FBA8[1]; + break; + case 2: + case 3: + default: + gAudioContext.curLoadedBook = audioFontSample->book->book; + break; + } + if (1) {} + if (1) {} + if (1) {} + nEntries = 16 * audioFontSample->book->order * audioFontSample->book->npredictors; + aLoadADPCM(cmd++, nEntries, gAudioContext.curLoadedBook); + } + } + + while (nSamplesProcessed != samplesLenAdjusted) { + noteFinished = false; + restart = false; + phi_s4 = 0; + + nFirstFrameSamplesToIgnore = synthState->samplePosInt & 0xF; + nSamplesUntilLoopEnd = loopEndPos - synthState->samplePosInt; + nSamplesToProcess = samplesLenAdjusted - nSamplesProcessed; + + if (nFirstFrameSamplesToIgnore == 0 && !synthState->restart) { + nFirstFrameSamplesToIgnore = 16; + } + nSamplesInFirstFrame = 16 - nFirstFrameSamplesToIgnore; + + if (nSamplesToProcess < nSamplesUntilLoopEnd) { + nFramesToDecode = (s32)(nSamplesToProcess - nSamplesInFirstFrame + 15) / 16; + nSamplesToDecode = nFramesToDecode * 16; + nTrailingSamplesToIgnore = nSamplesInFirstFrame + nSamplesToDecode - nSamplesToProcess; + } else { + nSamplesToDecode = nSamplesUntilLoopEnd - nSamplesInFirstFrame; + nTrailingSamplesToIgnore = 0; + if (nSamplesToDecode <= 0) { + nSamplesToDecode = 0; + nSamplesInFirstFrame = nSamplesUntilLoopEnd; + } + nFramesToDecode = (nSamplesToDecode + 15) / 16; + if (loopInfo->count != 0) { + // Loop around and restart + restart = true; + } else { + noteFinished = true; + } + } + + switch (audioFontSample->codec) { + case CODEC_ADPCM: + frameSize = 9; + skipInitialSamples = 16; + sampleDataStart = 0; + break; + case CODEC_SMALL_ADPCM: + frameSize = 5; + skipInitialSamples = 16; + sampleDataStart = 0; + break; + case CODEC_S8: + frameSize = 16; + skipInitialSamples = 16; + sampleDataStart = 0; + break; + case CODEC_S16_INMEMORY: + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, (samplesLenAdjusted * 2) + 0x20); + flags = A_CONTINUE; + skipBytes = 0; + nSamplesProcessed = samplesLenAdjusted; + s5 = samplesLenAdjusted; + goto skip; + case CODEC_S16: + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, (samplesLenAdjusted * 2) + 0x20); + flags = A_CONTINUE; + skipBytes = 0; + nSamplesProcessed = samplesLenAdjusted; + s5 = samplesLenAdjusted; + goto skip; + case CODEC_REVERB: + break; + } + + if (nFramesToDecode != 0) { + frameIndex = (synthState->samplePosInt + skipInitialSamples - nFirstFrameSamplesToIgnore) / 16; + sampleDataOffset = frameIndex * frameSize; + if (audioFontSample->medium == MEDIUM_RAM) { + sampleData = (u8*)(sampleDataStart + sampleDataOffset + sampleAddr); + } else if (audioFontSample->medium == MEDIUM_UNK) { + return cmd; + } else { + sampleData = AudioLoad_DmaSampleData(sampleDataStart + sampleDataOffset + sampleAddr, + ALIGN16((nFramesToDecode * frameSize) + 0x10), flags, + &synthState->sampleDmaIndex, audioFontSample->medium); + } + + if (sampleData == NULL) { + return cmd; + } + + sampleDataStartPad = (uintptr_t)sampleData & 0xF; + aligned = ALIGN16((nFramesToDecode * frameSize) + 16); + addr = DMEM_COMPRESSED_ADPCM_DATA - aligned; + aLoadBuffer(cmd++, sampleData - sampleDataStartPad, addr, aligned); + } else { + nSamplesToDecode = 0; + sampleDataStartPad = 0; + } + + if (synthState->restart) { + aSetLoop(cmd++, audioFontSample->loop->state); + flags = A_LOOP; + synthState->restart = false; + } + + nSamplesInThisIteration = nSamplesToDecode + nSamplesInFirstFrame - nTrailingSamplesToIgnore; + if (nSamplesProcessed == 0) { + if (1) {} + skipBytes = nFirstFrameSamplesToIgnore * 2; + } else { + phi_s4 = ALIGN16(s5 + 16); + } + switch (audioFontSample->codec) { + case CODEC_ADPCM: + aligned = ALIGN16((nFramesToDecode * frameSize) + 0x10); + addr = DMEM_COMPRESSED_ADPCM_DATA - aligned; + aSetBuffer(cmd++, 0, addr + sampleDataStartPad, DMEM_UNCOMPRESSED_NOTE + phi_s4, + nSamplesToDecode * 2); + aADPCMdec(cmd++, flags, synthState->synthesisBuffers->adpcmdecState); + break; + case CODEC_SMALL_ADPCM: + aligned = ALIGN16((nFramesToDecode * frameSize) + 0x10); + addr = DMEM_COMPRESSED_ADPCM_DATA - aligned; + aSetBuffer(cmd++, 0, addr + sampleDataStartPad, DMEM_UNCOMPRESSED_NOTE + phi_s4, + nSamplesToDecode * 2); + aADPCMdec(cmd++, flags | 4, synthState->synthesisBuffers->adpcmdecState); + break; + case CODEC_S8: + aligned = ALIGN16((nFramesToDecode * frameSize) + 0x10); + addr = DMEM_COMPRESSED_ADPCM_DATA - aligned; + AudioSynth_SetBuffer(cmd++, 0, addr + sampleDataStartPad, DMEM_UNCOMPRESSED_NOTE + phi_s4, + nSamplesToDecode * 2); + AudioSynth_S8Dec(cmd++, flags, synthState->synthesisBuffers->adpcmdecState); + break; + } + + if (nSamplesProcessed != 0) { + aDMEMMove(cmd++, DMEM_UNCOMPRESSED_NOTE + phi_s4 + (nFirstFrameSamplesToIgnore * 2), + DMEM_UNCOMPRESSED_NOTE + s5, nSamplesInThisIteration * 2); + } + + nSamplesProcessed += nSamplesInThisIteration; + + switch (flags) { + case A_INIT: + skipBytes = 0x20; + s5 = (nSamplesToDecode + 0x10) * 2; + break; + case A_LOOP: + s5 = nSamplesInThisIteration * 2 + s5; + break; + default: + if (s5 != 0) { + s5 = nSamplesInThisIteration * 2 + s5; + } else { + s5 = (nFirstFrameSamplesToIgnore + nSamplesInThisIteration) * 2; + } + break; + } + + flags = A_CONTINUE; + + skip: + if (noteFinished) { + AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE + s5, (samplesLenAdjusted - nSamplesProcessed) * 2); + finished = true; + note->noteSubEu.bitField0.finished = true; + func_800DB2C0(updateIndex, noteIndex); + break; + } else { + if (restart) { + synthState->restart = true; + synthState->samplePosInt = loopInfo->start; + } else { + synthState->samplePosInt += nSamplesToProcess; + } + } + } + + switch (nParts) { + case 1: + noteSamplesDmemAddrBeforeResampling = DMEM_UNCOMPRESSED_NOTE + skipBytes; + break; + case 2: + switch (curPart) { + case 0: + AudioSynth_InterL(cmd++, DMEM_UNCOMPRESSED_NOTE + skipBytes, DMEM_TEMP + 0x20, + ((samplesLenAdjusted / 2) + 7) & ~7); + resampledTempLen = samplesLenAdjusted; + noteSamplesDmemAddrBeforeResampling = DMEM_TEMP + 0x20; + if (finished) { + AudioSynth_ClearBuffer(cmd++, noteSamplesDmemAddrBeforeResampling + resampledTempLen, + samplesLenAdjusted + 0x10); + } + break; + case 1: + AudioSynth_InterL(cmd++, DMEM_UNCOMPRESSED_NOTE + skipBytes, + DMEM_TEMP + 0x20 + resampledTempLen, ((samplesLenAdjusted / 2) + 7) & ~7); + break; + } + } + if (finished) { + break; + } + } + } + + flags = A_CONTINUE; + if (noteSubEu->bitField0.needsInit == true) { + noteSubEu->bitField0.needsInit = false; + flags = A_INIT; + } + + cmd = AudioSynth_FinalResample(cmd, synthState, aiBufLen * 2, resamplingRateFixedPoint, + noteSamplesDmemAddrBeforeResampling, flags); + if (bookOffset == 3) { + AudioSynth_UnkCmd19(cmd++, DMEM_TEMP, DMEM_TEMP, aiBufLen * 2, 0); + } + + if (bookOffset == 2) { + AudioSynth_UnkCmd3(cmd++, DMEM_TEMP, DMEM_TEMP, aiBufLen * 2); + } + + phi_a1_2 = noteSubEu->unk_2; + if (phi_a1_2 != 0) { + if (phi_a1_2 < 0x10) { + phi_a1_2 = 0x10; + } + AudioSynth_HiLoGain(cmd++, phi_a1_2, DMEM_TEMP, 0, (aiBufLen * 2) + 0x20); + } + + filter = noteSubEu->filter; + if (filter != 0) { + AudioSynth_LoadFilterCount(cmd++, aiBufLen * 2, filter); + AudioSynth_LoadFilter(cmd++, flags, DMEM_TEMP, synthState->synthesisBuffers->mixEnvelopeState); + } + + unk7 = noteSubEu->unk_07; + unkE = noteSubEu->unk_0E; + buf = &synthState->synthesisBuffers->panSamplesBuffer[0x18]; + if (unk7 != 0 && noteSubEu->unk_0E != 0) { + AudioSynth_DMemMove(cmd++, DMEM_TEMP, DMEM_SCRATCH2, aiBufLen * 2); + thing = DMEM_SCRATCH2 - unk7; + if (synthState->unk_1A != 0) { + AudioSynth_ClearBuffer(cmd++, thing, unk7); + synthState->unk_1A = 0; + } else { + AudioSynth_LoadBuffer(cmd++, thing, unk7, buf); + } + AudioSynth_SaveBuffer(cmd++, DMEM_TEMP + (aiBufLen * 2) - unk7, unk7, buf); + AudioSynth_Mix(cmd++, (aiBufLen * 2) >> 4, unkE, DMEM_SCRATCH2, thing); + AudioSynth_DMemMove(cmd++, thing, DMEM_TEMP, aiBufLen * 2); + } else { + synthState->unk_1A = 1; + } + + if (noteSubEu->headsetPanRight != 0 || synthState->prevHeadsetPanRight != 0) { + side = 1; + } else if (noteSubEu->headsetPanLeft != 0 || synthState->prevHeadsetPanLeft != 0) { + side = 2; + } else { + side = 0; + } + cmd = AudioSynth_ProcessEnvelope(cmd, noteSubEu, synthState, aiBufLen, DMEM_TEMP, side, flags); + if (noteSubEu->bitField1.usesHeadsetPanEffects2) { + if (!(flags & A_INIT)) { + flags = A_CONTINUE; + } + cmd = AudioSynth_NoteApplyHeadsetPanEffects(cmd, noteSubEu, synthState, aiBufLen * 2, flags, side); + } + return cmd; +} + +Acmd* AudioSynth_FinalResample(Acmd* cmd, NoteSynthesisState* synthState, s32 count, u16 pitch, u16 inpDmem, + s32 resampleFlags) { + if (pitch == 0) { + AudioSynth_ClearBuffer(cmd++, DMEM_TEMP, count); + } else { + aSetBuffer(cmd++, 0, inpDmem, DMEM_TEMP, count); + aResample(cmd++, resampleFlags, pitch, synthState->synthesisBuffers->finalResampleState); + } + return cmd; +} + +Acmd* AudioSynth_ProcessEnvelope(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 aiBufLen, + u16 inBuf, s32 headsetPanSettings, s32 flags) { + u32 phi_a1; + u16 curVolLeft; + u16 targetVolLeft; + s32 phi_t1; + s16 reverbVol; + u16 curVolRight; + s16 rampLeft; + s16 rampRight; + s16 rampReverb; + s16 sourceReverbVol; + u16 targetVolRight; + s32 pad; + + curVolLeft = synthState->curVolLeft; + targetVolLeft = noteSubEu->targetVolLeft; + targetVolLeft <<= 4; + reverbVol = noteSubEu->reverbVol; + curVolRight = synthState->curVolRight; + targetVolRight = noteSubEu->targetVolRight; + targetVolRight <<= 4; + + if (targetVolLeft != curVolLeft) { + rampLeft = (targetVolLeft - curVolLeft) / (aiBufLen >> 3); + } else { + rampLeft = 0; + } + if (targetVolRight != curVolRight) { + rampRight = (targetVolRight - curVolRight) / (aiBufLen >> 3); + } else { + rampRight = 0; + } + + sourceReverbVol = synthState->reverbVol; + phi_t1 = sourceReverbVol & 0x7F; + + if (sourceReverbVol != reverbVol) { + rampReverb = (((reverbVol & 0x7F) - phi_t1) << 9) / (aiBufLen >> 3); + synthState->reverbVol = reverbVol; + } else { + rampReverb = 0; + } + + synthState->curVolLeft = curVolLeft + (rampLeft * (aiBufLen >> 3)); + synthState->curVolRight = curVolRight + (rampRight * (aiBufLen >> 3)); + + if (noteSubEu->bitField1.usesHeadsetPanEffects2) { + AudioSynth_ClearBuffer(cmd++, DMEM_NOTE_PAN_TEMP, DEFAULT_LEN_1CH); + AudioSynth_EnvSetup1(cmd++, phi_t1 * 2, rampReverb, rampLeft, rampRight); + AudioSynth_EnvSetup2(cmd++, curVolLeft, curVolRight); + switch (headsetPanSettings) { + case 1: + phi_a1 = D_801304A4; + break; + case 2: + phi_a1 = D_801304A8; + break; + default: + phi_a1 = D_801304AC; + break; + } + } else { + aEnvSetup1(cmd++, phi_t1 * 2, rampReverb, rampLeft, rampRight); + aEnvSetup2(cmd++, curVolLeft, curVolRight); + phi_a1 = D_801304AC; + } + + aEnvMixer(cmd++, inBuf, aiBufLen, (sourceReverbVol & 0x80) >> 7, noteSubEu->bitField0.stereoHeadsetEffects, + noteSubEu->bitField0.usesHeadsetPanEffects, noteSubEu->bitField0.stereoStrongRight, + noteSubEu->bitField0.stereoStrongLeft, phi_a1, D_801304A0); + return cmd; +} + +Acmd* AudioSynth_LoadWaveSamples(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 nSamplesToLoad) { + s32 temp_v0; + s32 unk6 = noteSubEu->unk_06; + s32 samplePosInt = synthState->samplePosInt; + s32 repeats; + + if (noteSubEu->bitField1.bookOffset != 0) { + AudioSynth_LoadBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, ALIGN16(nSamplesToLoad * 2), gWaveSamples[8]); + gWaveSamples[8] += nSamplesToLoad * 2; + return cmd; + } else { + aLoadBuffer(cmd++, noteSubEu->sound.samples, DMEM_UNCOMPRESSED_NOTE, 0x80); + if (unk6 != 0) { + samplePosInt = (samplePosInt * D_801304C0[unk6 >> 2]) / D_801304C0[unk6 & 3]; + } + samplePosInt &= 0x3F; + temp_v0 = 0x40 - samplePosInt; + if (temp_v0 < nSamplesToLoad) { + repeats = ((nSamplesToLoad - temp_v0 + 0x3F) / 0x40); + if (repeats != 0) { + aDuplicate(cmd++, repeats, DMEM_UNCOMPRESSED_NOTE, DMEM_UNCOMPRESSED_NOTE + 0x80, 0x80); + } + } + synthState->samplePosInt = samplePosInt; + } + return cmd; +} + +Acmd* AudioSynth_NoteApplyHeadsetPanEffects(Acmd* cmd, NoteSubEu* noteSubEu, NoteSynthesisState* synthState, s32 bufLen, + s32 flags, s32 side) { + u16 dest; + u16 pitch; + u8 prevPanShift; + u8 panShift; + + switch (side) { + case 1: + dest = DMEM_LEFT_CH; + panShift = noteSubEu->headsetPanRight; + prevPanShift = synthState->prevHeadsetPanRight; + synthState->prevHeadsetPanLeft = 0; + synthState->prevHeadsetPanRight = panShift; + break; + case 2: + dest = DMEM_RIGHT_CH; + panShift = noteSubEu->headsetPanLeft; + prevPanShift = synthState->prevHeadsetPanLeft; + synthState->prevHeadsetPanLeft = panShift; + synthState->prevHeadsetPanRight = 0; + break; + default: + return cmd; + } + + if (flags != A_INIT) { + // Slightly adjust the sample rate in order to fit a change in pan shift + if (panShift != prevPanShift) { + pitch = (((bufLen << 0xF) / 2) - 1) / ((bufLen + panShift - prevPanShift - 2) / 2); + aSetBuffer(cmd++, 0, DMEM_NOTE_PAN_TEMP, DMEM_TEMP, bufLen + panShift - prevPanShift); + aResampleZoh(cmd++, pitch, 0); + } else { + aDMEMMove(cmd++, DMEM_NOTE_PAN_TEMP, DMEM_TEMP, bufLen); + } + + if (prevPanShift != 0) { + aLoadBuffer(cmd++, &synthState->synthesisBuffers->panResampleState[0x8], DMEM_NOTE_PAN_TEMP, + ALIGN16(prevPanShift)); + aDMEMMove(cmd++, DMEM_TEMP, DMEM_NOTE_PAN_TEMP + prevPanShift, bufLen + panShift - prevPanShift); + } else { + aDMEMMove(cmd++, DMEM_TEMP, DMEM_NOTE_PAN_TEMP, bufLen + panShift); + } + } else { + // Just shift right + aDMEMMove(cmd++, DMEM_NOTE_PAN_TEMP, DMEM_TEMP, bufLen); + aClearBuffer(cmd++, DMEM_NOTE_PAN_TEMP, panShift); + aDMEMMove(cmd++, DMEM_TEMP, DMEM_NOTE_PAN_TEMP + panShift, bufLen); + } + + if (panShift) { + // Save excessive samples for next iteration + aSaveBuffer(cmd++, DMEM_NOTE_PAN_TEMP + bufLen, &synthState->synthesisBuffers->panResampleState[0x8], + ALIGN16(panShift)); + } + aAddMixer(cmd++, ALIGN64(bufLen), DMEM_NOTE_PAN_TEMP, dest, 0x7FFF); + return cmd; +} diff --git a/soh/src/code/code_800430A0.c b/soh/src/code/code_800430A0.c new file mode 100644 index 000000000..95c7c4fc5 --- /dev/null +++ b/soh/src/code/code_800430A0.c @@ -0,0 +1,105 @@ +#include "global.h" +#include "vt.h" + +void func_800430A0(CollisionContext* colCtx, s32 bgId, Actor* actor) { + MtxF prevTransform; + MtxF prevTransformInv; + MtxF curTransform; + Vec3f pos; + Vec3f tempPos; + + if (DynaPoly_IsBgIdBgActor(bgId)) { + SkinMatrix_SetTranslateRotateYXZScale( + &prevTransform, colCtx->dyna.bgActors[bgId].prevTransform.scale.x, + colCtx->dyna.bgActors[bgId].prevTransform.scale.y, colCtx->dyna.bgActors[bgId].prevTransform.scale.z, + colCtx->dyna.bgActors[bgId].prevTransform.rot.x, colCtx->dyna.bgActors[bgId].prevTransform.rot.y, + colCtx->dyna.bgActors[bgId].prevTransform.rot.z, colCtx->dyna.bgActors[bgId].prevTransform.pos.x, + colCtx->dyna.bgActors[bgId].prevTransform.pos.y, colCtx->dyna.bgActors[bgId].prevTransform.pos.z); + if (SkinMatrix_Invert(&prevTransform, &prevTransformInv) != 2) { + SkinMatrix_SetTranslateRotateYXZScale( + &curTransform, colCtx->dyna.bgActors[bgId].curTransform.scale.x, + colCtx->dyna.bgActors[bgId].curTransform.scale.y, colCtx->dyna.bgActors[bgId].curTransform.scale.z, + colCtx->dyna.bgActors[bgId].curTransform.rot.x, colCtx->dyna.bgActors[bgId].curTransform.rot.y, + colCtx->dyna.bgActors[bgId].curTransform.rot.z, colCtx->dyna.bgActors[bgId].curTransform.pos.x, + colCtx->dyna.bgActors[bgId].curTransform.pos.y, colCtx->dyna.bgActors[bgId].curTransform.pos.z); + SkinMatrix_Vec3fMtxFMultXYZ(&prevTransformInv, &actor->world.pos, &tempPos); + SkinMatrix_Vec3fMtxFMultXYZ(&curTransform, &tempPos, &pos); + actor->world.pos = pos; + if (BGCHECK_XYZ_ABSMAX <= pos.x || pos.x <= -BGCHECK_XYZ_ABSMAX || BGCHECK_XYZ_ABSMAX <= pos.y || + pos.y <= -BGCHECK_XYZ_ABSMAX || BGCHECK_XYZ_ABSMAX <= pos.z || pos.z <= -BGCHECK_XYZ_ABSMAX) { + + osSyncPrintf(VT_FGCOL(RED)); + //! @bug file and line are not passed to osSyncPrintf + // "Position is not valid" + osSyncPrintf( + "BGCheckCollection_typicalActorPos():位置が妥当ではありません。\npos (%f,%f,%f) file:%s line:%d\n", + pos.x, pos.y, pos.z); + osSyncPrintf(VT_RST); + } + } + } +} + +/** + * Rotate actor + */ +void func_800432A0(CollisionContext* colCtx, s32 bgId, Actor* actor) { + if (DynaPoly_IsBgIdBgActor(bgId)) { + s16 rot = colCtx->dyna.bgActors[bgId].curTransform.rot.y - colCtx->dyna.bgActors[bgId].prevTransform.rot.y; + + if (actor->id == ACTOR_PLAYER) { + ((Player*)actor)->currentYaw += rot; + } + + actor->shape.rot.y += rot; + actor->world.rot.y += rot; + } +} + +void func_80043334(CollisionContext* colCtx, Actor* actor, s32 bgId) { + if (DynaPoly_IsBgIdBgActor(bgId)) { + DynaPolyActor* dynaActor = DynaPoly_GetActor(colCtx, bgId); + if (dynaActor != NULL) { + func_800434A8(dynaActor); + + if (CHECK_FLAG_ALL(actor->flags, ACTOR_FLAG_26)) { + func_80043538(dynaActor); + } + } + } +} + +/** + * Transform actor's position + * `actor` is the actor to update + */ +s32 func_800433A4(CollisionContext* colCtx, s32 bgId, Actor* actor) { + s32 result = false; + DynaPolyActor* dynaActor; + + if (DynaPoly_IsBgIdBgActor(bgId) == false) { + return false; + } + + if ((colCtx->dyna.bgActorFlags[bgId] & 2) || !(colCtx->dyna.bgActorFlags[bgId] & 1)) { + return false; + } + + dynaActor = DynaPoly_GetActor(colCtx, bgId); + + if (dynaActor == NULL) { + return false; + } + + if (dynaActor->unk_15C & 1) { + func_800430A0(colCtx, bgId, actor); + result = true; + } + + if (dynaActor->unk_15C & 2) { + func_800432A0(colCtx, bgId, actor); + result = true; + } + + return result; +} diff --git a/soh/src/code/code_80043480.c b/soh/src/code/code_80043480.c new file mode 100644 index 000000000..eda615d35 --- /dev/null +++ b/soh/src/code/code_80043480.c @@ -0,0 +1,114 @@ +#include "global.h" + +void DynaPolyActor_Init(DynaPolyActor* dynaActor, s32 flags) { + dynaActor->bgId = -1; + dynaActor->unk_15C = flags; + dynaActor->unk_160 = 0; + dynaActor->unk_150 = 0.0f; + dynaActor->unk_154 = 0.0f; +} + +void func_800434A0(DynaPolyActor* dynaActor) { + dynaActor->unk_160 = 0; +} + +void func_800434A8(DynaPolyActor* dynaActor) { + dynaActor->unk_160 |= 1; +} + +void func_800434B8(DynaPolyActor* dynaActor) { + dynaActor->unk_160 |= 2; +} + +void func_800434C8(CollisionContext* colCtx, s32 floorBgId) { + DynaPolyActor* dynaActor = DynaPoly_GetActor(colCtx, floorBgId); + + if (dynaActor != NULL) { + func_800434B8(dynaActor); + } +} + +void func_800434F8(DynaPolyActor* dynaActor) { + dynaActor->unk_160 |= 4; +} + +void func_80043508(CollisionContext* colCtx, s32 floorBgId) { + DynaPolyActor* dynaActor = DynaPoly_GetActor(colCtx, floorBgId); + + if (dynaActor != NULL) { + func_800434F8(dynaActor); + } +} + +void func_80043538(DynaPolyActor* dynaActor) { + dynaActor->unk_160 |= 8; +} + +s32 func_80043548(DynaPolyActor* dynaActor) { + if (dynaActor->unk_160 & 1) { + return true; + } else { + return false; + } +} + +s32 func_8004356C(DynaPolyActor* dynaActor) { + if (dynaActor->unk_160 & 2) { + return true; + } else { + return false; + } +} + +s32 func_80043590(DynaPolyActor* dynaActor) { + if (dynaActor->unk_160 & 4) { + return true; + } else { + return false; + } +} + +s32 func_800435B4(DynaPolyActor* dynaActor) { + if (dynaActor->unk_160 & 8) { + return true; + } else { + return false; + } +} + +s32 func_800435D8(GlobalContext* globalCtx, DynaPolyActor* dynaActor, s16 arg2, s16 arg3, s16 arg4) { + Vec3f posA; + Vec3f posB; + Vec3f posResult; + f32 sin = Math_SinS(dynaActor->unk_158); + f32 cos = Math_CosS(dynaActor->unk_158); + s32 bgId; + CollisionPoly* poly; + f32 a2; + f32 a3; + f32 sign = (0.0f <= dynaActor->unk_150) ? 1.0f : -1.0f; + + a2 = (f32)arg2 - 0.1f; + posA.x = dynaActor->actor.world.pos.x + (a2 * cos); + posA.y = dynaActor->actor.world.pos.y + arg4; + posA.z = dynaActor->actor.world.pos.z - (a2 * sin); + + a3 = (f32)arg3 - 0.1f; + posB.x = sign * a3 * sin + posA.x; + posB.y = posA.y; + posB.z = sign * a3 * cos + posA.z; + + if (BgCheck_EntityLineTest3(&globalCtx->colCtx, &posA, &posB, &posResult, &poly, true, false, false, true, &bgId, + &dynaActor->actor, 0.0f)) { + return false; + } + posA.x = (dynaActor->actor.world.pos.x * 2) - posA.x; + posA.z = (dynaActor->actor.world.pos.z * 2) - posA.z; + posB.x = sign * a3 * sin + posA.x; + posB.z = sign * a3 * cos + posA.z; + if (BgCheck_EntityLineTest3(&globalCtx->colCtx, &posA, &posB, &posResult, &poly, true, false, false, true, &bgId, + &dynaActor->actor, 0.0f)) { + return false; + } + return true; +} diff --git a/soh/src/code/code_8006C3A0.c b/soh/src/code/code_8006C3A0.c new file mode 100644 index 000000000..341a44595 --- /dev/null +++ b/soh/src/code/code_8006C3A0.c @@ -0,0 +1,33 @@ +#include "global.h" + +void Flags_UnsetAllEnv(GlobalContext* globalCtx) { + u8 i; + + for (i = 0; i < 20; i++) { + globalCtx->envFlags[i] = 0; + } +} + +void Flags_SetEnv(GlobalContext* globalCtx, s16 flag) { + s16 index = flag / 16; + s16 bit = flag % 16; + s16 mask = 1 << bit; + + globalCtx->envFlags[index] |= mask; +} + +void Flags_UnsetEnv(GlobalContext* globalCtx, s16 flag) { + s16 index = flag / 16; + s16 bit = flag % 16; + s16 mask = (1 << bit) ^ 0xFFFF; + + globalCtx->envFlags[index] &= mask; +} + +s32 Flags_GetEnv(GlobalContext* globalCtx, s16 flag) { + s16 index = flag / 16; + s16 bit = flag % 16; + s16 mask = 1 << bit; + + return globalCtx->envFlags[index] & mask; +} diff --git a/soh/src/code/code_8006C510.c b/soh/src/code/code_8006C510.c new file mode 100644 index 000000000..833009e81 --- /dev/null +++ b/soh/src/code/code_8006C510.c @@ -0,0 +1,40 @@ +#include "global.h" + +f32 func_8006C510(f32 arg0, f32 arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5) { + char pad[0x1C]; + f32 sq = SQ(arg0); + f32 cube = sq * arg0; + + return (((cube + cube) - sq * 3.0f) + 1.0f) * arg2 + (sq * 3.0f - (cube + cube)) * arg3 + + ((cube - (sq + sq)) + arg0) * arg4 * arg1 + (cube - sq) * arg5 * arg1; +} + +f32 func_8006C5A8(f32 target, TransformData* transData, s32 refIdx) { + s32 i; + s32 j; + + if (target <= transData->unk_02) { + return transData->unk_08; + } + if (target >= transData[refIdx - 1].unk_02) { + return transData[refIdx - 1].unk_08; + } + + for (i = 0;; i++) { + j = i + 1; + if (transData[j].unk_02 > target) { + if (transData[i].unk_00 & 1) { + return transData[i].unk_08; + } else if (transData[i].unk_00 & 2) { + return transData[i].unk_08 + + ((target - (f32)transData[i].unk_02) / ((f32)transData[j].unk_02 - (f32)transData[i].unk_02)) * + (transData[j].unk_08 - transData[i].unk_08); + } else { + f32 diff = (f32)transData[j].unk_02 - (f32)transData[i].unk_02; + return func_8006C510((target - transData[i].unk_02) / ((f32)transData[j].unk_02 - transData[i].unk_02), + diff * (1.0f / 30.0f), transData[i].unk_08, transData[j].unk_08, + transData[i].unk_06, transData[j].unk_04); + } + } + } +} diff --git a/soh/src/code/code_80097A00.c b/soh/src/code/code_80097A00.c new file mode 100644 index 000000000..f07102b0f --- /dev/null +++ b/soh/src/code/code_80097A00.c @@ -0,0 +1,224 @@ +#include "global.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/parameter_static/parameter_static.h" + +// Bit Flag array in which gBitFlags[n] is literally (1 << n) +u32 gBitFlags[] = { + (1 << 0), (1 << 1), (1 << 2), (1 << 3), (1 << 4), (1 << 5), (1 << 6), (1 << 7), + (1 << 8), (1 << 9), (1 << 10), (1 << 11), (1 << 12), (1 << 13), (1 << 14), (1 << 15), + (1 << 16), (1 << 17), (1 << 18), (1 << 19), (1 << 20), (1 << 21), (1 << 22), (1 << 23), + (1 << 24), (1 << 25), (1 << 26), (1 << 27), (1 << 28), (1 << 29), (1 << 30), (1 << 31), +}; + +u16 gEquipMasks[] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; +u16 gEquipNegMasks[] = { 0xFFF0, 0xFF0F, 0xF0FF, 0x0FFF }; +u32 gUpgradeMasks[] = { + 0x00000007, 0x00000038, 0x000001C0, 0x00000E00, 0x00003000, 0x0001C000, 0x000E0000, 0x00700000, +}; +u32 gUpgradeNegMasks[] = { + 0xFFFFFFF8, 0xFFFFFFC7, 0xFFFFFE3F, 0xFFFFF1FF, 0xFFFFCFFF, 0xFFFE3FFF, 0xFFF1FFFF, 0xFF8FFFFF, +}; +u8 gEquipShifts[] = { 0, 4, 8, 12 }; +u8 gUpgradeShifts[] = { 0, 3, 6, 9, 12, 14, 17, 20 }; + +u16 gUpgradeCapacities[][4] = { + { 0, 30, 40, 50 }, // Quivers + { 0, 20, 30, 40 }, // Bomb Bags + { 0, 0, 0, 0 }, // Unused (Scale) + { 0, 0, 0, 0 }, // Unused (Strength) + { 99, 200, 500, 500 }, // Wallets + { 0, 30, 40, 50 }, // Deku Seed Bullet Bags + { 0, 10, 20, 30 }, // Deku Stick Upgrades + { 0, 20, 30, 40 }, // Deku Nut Upgrades +}; + +u32 gGsFlagsMasks[] = { 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }; +u32 gGsFlagsShifts[] = { 0, 8, 16, 24 }; + +void* gItemIcons[] = { + gDekuStickIconTex, + gDekuNutIconTex, + gBombIconTex, + gFairyBowIconTex, + gFireArrowIconTex, + gDinsFireIconTex, + gFairySlingshotIconTex, + gFairyOcarinaIconTex, + gOcarinaofTimeIconTex, + gBombchuIconTex, + gHookshotIconTex, + gLongshotIconTex, + gIceArrowIconTex, + gFaroresWindIconTex, + gBoomerangIconTex, + gLensofTruthIconTex, + gMagicBeansIconTex, + gMegatonHammerIconTex, + gLightArrowIconTex, + gNayrusLoveIconTex, + gEmptyBottleIconTex, + gRedPotionIconTex, + gGreenPotionIconTex, + gBluePotionIconTex, + gBottledFairyIconTex, + gFishIconTex, + gMilkFullIconTex, + gRutosLetterIconTex, + gBlueFireIconTex, + gBugIconTex, + gBigPoeIconTex, + gMilkhalfIconTex, + gPoeIconTex, + gWeirdEggIconTex, + gCuccoIconTex, + gZeldasLetterIconTex, + gKeatonMaskIconTex, + gSkullMaskIconTex, + gSpookyMaskIconTex, + gBunnyHoodIconTex, + gGoronMaskIconTex, + gZoraMaskIconTex, + gGerudoMaskIconTex, + gMaskofTruthIconTex, + gSoldOutIconTex, + gPocketEggIconTex, + gPocketCuccoIconTex, + gCojiroIconTex, + gOddMushroomIconTex, + gOddPotionIconTex, + gPoachersSawIconTex, + gBrokenBiggoronSwordIconTex, + gPrescriptionIconTex, + gEyeBallFrogIconTex, + gEyeDropsIconTex, + gClaimCheckIconTex, + gFairyBowFireIconTex, + gFairyBowIceIconTex, + gFairyBowLightIconTex, + gKokiriSwordIconTex, + gMasterSwordIconTex, + gBiggoronSwordIconTex, + gDekuShieldIconTex, + gHylianShieldIconTex, + gMirrorShieldIconTex, + gKokiriTunicIconTex, + gGoronTunicIconTex, + gZoraTunicIconTex, + gKokiriBootsIconTex, + gIronBootsIconTex, + gHoverBootsIconTex, + gBulletBag30IconTex, + gBulletBag40IconTex, + gBulletBag50IconTex, + gQuiver30IconTex, + gQuiver40IconTex, + gQuiver50IconTex, + gBombBag20IconTex, + gBombBag30IconTex, + gBombBag40IconTex, + gGoronsBraceletIconTex, + gSilverGauntletsIconTex, + gGoldenGauntletsIconTex, + gSilverScaleIconTex, + gGoldenScaleIconTex, + gBrokenGiantsKnifeIconTex, + gAdultsWalletIconTex, + gGiantsWalletIconTex, + gDekuSeedsIconTex, + gFishingPoleIconTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gSongNoteTex, + gForestMedallionIconTex, + gFireMedallionIconTex, + gWaterMedallionIconTex, + gSpiritMedallionIconTex, + gShadowMedallionIconTex, + gLightMedallionIconTex, + gKokiriEmeraldIconTex, + gGoronRubyIconTex, + gZoraSapphireIconTex, + gStoneOfAgonyIconTex, + gGerudosCardIconTex, + gGoldSkulltulaIconTex, + gHeartContainerIconTex, + gUnusedPieceOfHeartIconTex, + gBossKeyIconTex, + gCompassIconTex, + gDungeonMapIconTex, + gSmallKeyIconTex, + gSmallMagicJarIconTex, + gBigMagicJarIconTex, + gHeartPieceIcon1Tex, + gHeartPieceIcon2Tex, + gHeartPieceIcon3Tex, + gOcarinaCUpTex, + gOcarinaCDownTex, + gOcarinaCLeftTex, + gOcarinaCRightTex, + gOcarinaATex, +}; + +// Used to map item IDs to inventory slots +u8 gItemSlots[] = { + SLOT_STICK, SLOT_NUT, SLOT_BOMB, SLOT_BOW, SLOT_ARROW_FIRE, SLOT_DINS_FIRE, + SLOT_SLINGSHOT, SLOT_OCARINA, SLOT_OCARINA, SLOT_BOMBCHU, SLOT_HOOKSHOT, SLOT_HOOKSHOT, + SLOT_ARROW_ICE, SLOT_FARORES_WIND, SLOT_BOOMERANG, SLOT_LENS, SLOT_BEAN, SLOT_HAMMER, + SLOT_ARROW_LIGHT, SLOT_NAYRUS_LOVE, SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, + SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, + SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_BOTTLE_1, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, + SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, + SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_CHILD, SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, + SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, + SLOT_TRADE_ADULT, SLOT_TRADE_ADULT, +}; + +void Inventory_ChangeEquipment(s16 equipment, u16 value) { + gSaveContext.equips.equipment &= gEquipNegMasks[equipment]; + gSaveContext.equips.equipment |= value << gEquipShifts[equipment]; +} + +u8 Inventory_DeleteEquipment(GlobalContext* globalCtx, s16 equipment) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + u16 sp26 = gSaveContext.equips.equipment & gEquipMasks[equipment]; + + // "Erasing equipment item = %d zzz=%d" + osSyncPrintf("装備アイテム抹消 = %d zzz=%d\n", equipment, sp26); + + if (sp26) { + sp26 >>= gEquipShifts[equipment]; + + gSaveContext.equips.equipment &= gEquipNegMasks[equipment]; + gSaveContext.inventory.equipment ^= gBitFlags[sp26 - 1] << gEquipShifts[equipment]; + + if (equipment == EQUIP_TUNIC) { + gSaveContext.equips.equipment |= 0x0100; + } + + if (equipment == EQUIP_SWORD) { + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + gSaveContext.infTable[29] = 1; + } + + Player_SetEquipmentData(globalCtx, player); + globalCtx->pauseCtx.cursorSpecialPos = PAUSE_CURSOR_PAGE_LEFT; + } + + return sp26; +} + +void Inventory_ChangeUpgrade(s16 upgrade, s16 value) { + gSaveContext.inventory.upgrades &= gUpgradeNegMasks[upgrade]; + gSaveContext.inventory.upgrades |= value << gUpgradeShifts[upgrade]; +} diff --git a/soh/src/code/code_800A9F30.c b/soh/src/code/code_800A9F30.c new file mode 100644 index 000000000..b8f4e1ce8 --- /dev/null +++ b/soh/src/code/code_800A9F30.c @@ -0,0 +1,91 @@ +#include "global.h" + +UnkRumbleStruct D_80160FD0; + +void func_800A9F30(PadMgr* a, s32 b) { + func_800D2E30(&D_80160FD0); + PadMgr_RumbleSet(a, D_80160FD0.rumbleEnable); +} + +void func_800A9F6C(f32 a, u8 b, u8 c, u8 d) { + s32 temp1; + s32 temp2; + + if (1000000.0f < a) { + temp1 = 1000; + } else { + temp1 = sqrtf(a); + } + + if ((temp1 < 1000) && (b != 0) && (d != 0)) { + temp2 = b - (temp1 * 255) / 1000; + if (temp2 > 0) { + D_80160FD0.unk_10A = temp2; + D_80160FD0.unk_10B = c; + D_80160FD0.unk_10C = d; + } + } +} + +void func_800AA000(f32 a, u8 b, u8 c, u8 d) { + s32 temp1; + s32 temp2; + s32 i; + + if (1000000.0f < a) { + temp1 = 1000; + } else { + temp1 = sqrtf(a); + } + + if (temp1 < 1000 && b != 0 && d != 0) { + temp2 = b - (temp1 * 255) / 1000; + + for (i = 0; i < 0x40; i++) { + if (D_80160FD0.unk_04[i] == 0) { + if (temp2 > 0) { + D_80160FD0.unk_04[i] = temp2; + D_80160FD0.unk_44[i] = c; + D_80160FD0.unk_84[i] = d; + } + break; + } + } + } +} + +void func_800AA0B4(void) { + func_800D3140(&D_80160FD0); + + gPadMgr.retraceCallback = func_800A9F30; + gPadMgr.retraceCallbackValue = 0; + + if (1) {} +} + +void func_800AA0F0(void) { + PadMgr* padmgr = &gPadMgr; + + if ((padmgr->retraceCallback == func_800A9F30) && (padmgr->retraceCallbackValue == 0)) { + padmgr->retraceCallback = NULL; + padmgr->retraceCallbackValue = 0; + } + + func_800D3178(&D_80160FD0); +} + +u32 func_800AA148(void) { + return gPadMgr.pakType[0] == 1; +} + +void func_800AA15C(void) { + D_80160FD0.unk_104 = 2; +} + +void func_800AA16C(void) { + D_80160FD0.unk_104 = 0; +} + +void func_800AA178(u32 a) { + D_80160FD0.unk_105 = !!a; +} diff --git a/soh/src/code/code_800ACE70.c b/soh/src/code/code_800ACE70.c new file mode 100644 index 000000000..e22162777 --- /dev/null +++ b/soh/src/code/code_800ACE70.c @@ -0,0 +1,97 @@ +#include "global.h" + +// Note : This file is related to z_vismono, the original name was probably z_vis + +Gfx D_8012AC00[] = { + gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_VISCVG | G_RM_VISCVG2), + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsDPPipeSync(), + gsDPSetBlendColor(0, 0, 0, 8), + gsSPEndDisplayList(), +}; + +Gfx D_8012AC28[] = { + gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL | + GBL_c1(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_A_MEM) | + GBL_c2(G_BL_CLR_FOG, G_BL_A_FOG, G_BL_CLR_MEM, G_BL_A_MEM)), + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsSPEndDisplayList(), +}; + +Gfx D_8012AC40[] = { + gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL | + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM) | + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM)), + + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsSPEndDisplayList(), +}; + +Gfx D_8012AC58[] = { + gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_DISABLE | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsDPSetOtherMode(G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | FORCE_BL | + GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM) | + GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_MEM, G_BL_A_MEM)), + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsSPEndDisplayList(), +}; + +// Init +void func_800ACE70(struct_801664F0* this) { + this->type = 0; + this->setScissor = false; + this->color.r = 255; + this->color.g = 255; + this->color.b = 255; + this->color.a = 255; +} + +// Destroy +void func_800ACE90(struct_801664F0* this) { +} + +// Draw +void func_800ACE98(struct_801664F0* this, Gfx** gfxp) { + Gfx* gfx = *gfxp; + + gDPPipeSync(gfx++); + gDPSetPrimDepth(gfx++, -1, -1); + + if (this->setScissor == true) { + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + } + + switch (this->type) { + case 1: + gSPDisplayList(gfx++, D_8012AC40); + break; + case 2: + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->color.rgba); + gSPDisplayList(gfx++, D_8012AC58); + break; + case 3: + gDPSetColor(gfx++, G_SETBLENDCOLOR, this->color.rgba); + gSPDisplayList(gfx++, D_8012AC00); + break; + case 4: + gDPSetColor(gfx++, G_SETFOGCOLOR, this->color.rgba); + gSPDisplayList(gfx++, D_8012AC28); + break; + } + + gDPPipeSync(gfx++); + *gfxp = gfx; +} diff --git a/soh/src/code/code_800AD920.c b/soh/src/code/code_800AD920.c new file mode 100644 index 000000000..2ef7cf0bb --- /dev/null +++ b/soh/src/code/code_800AD920.c @@ -0,0 +1,64 @@ +#include "global.h" + +// Note : This file is related to z_vismono, the original name was probably z_vis + +// z-buffer +extern u16 D_0E000000[]; + +// Init +void func_800AD920(struct_80166500* this) { + this->useRgba = false; + this->setScissor = false; + this->primColor.r = 255; + this->primColor.g = 255; + this->primColor.b = 255; + this->primColor.a = 255; + this->envColor.a = 255; + this->envColor.r = 0; + this->envColor.g = 0; + this->envColor.b = 0; +} + +// Destroy +void func_800AD950(struct_80166500* this) { +} + +// Draw +void func_800AD958(struct_80166500* this, Gfx** gfxp) { + Gfx* gfx = *gfxp; + + // OTRTODO +#if 0 + u16* tex = D_0E000000; + s32 fmt = this->useRgba == false ? G_IM_FMT_IA : G_IM_FMT_RGBA; + s32 y; + s32 height = 6; + + gDPPipeSync(gfx++); + if (this->setScissor == true) { + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + } + + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_OPA_SURF | G_RM_OPA_SURF2); + gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT); + + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->primColor.rgba); + gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba); + + for (y = 0; y <= SCREEN_HEIGHT - height; y += height) { + gDPLoadTextureBlock(gfx++, tex, fmt, G_IM_SIZ_16b, SCREEN_WIDTH, height, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, 0, (y) << 2, (SCREEN_WIDTH << 2), (y + height) << 2, G_TX_RENDERTILE, 0, 0, + (1 << 10), (1 << 10)); + tex += SCREEN_WIDTH * height; + } + + gDPPipeSync(gfx++); + *gfxp = gfx; +#endif +} diff --git a/soh/src/code/code_800BB0A0.c b/soh/src/code/code_800BB0A0.c new file mode 100644 index 000000000..0f2ee7d74 --- /dev/null +++ b/soh/src/code/code_800BB0A0.c @@ -0,0 +1,71 @@ +#include "global.h" + +// The code in this file is very similar to a spline system used in Super Mario 64 for cutscene camera movement + +void func_800BB0A0(f32 u, Vec3f* pos, f32* roll, f32* viewAngle, f32* point0, f32* point1, f32* point2, f32* point3) { + f32 coeff[4]; + + u = CLAMP_MAX(u, 1.0f); + + coeff[0] = (1.0f - u) * (1.0f - u) * (1.0f - u) / 6.0f; + coeff[1] = u * u * u / 2.0f - u * u + 2.0f / 3.0f; + coeff[2] = -u * u * u / 2.0f + u * u / 2.0f + u / 2.0f + 1.0f / 6.0f; + coeff[3] = u * u * u / 6.0f; + + pos->x = (coeff[0] * point0[0]) + (coeff[1] * point1[0]) + (coeff[2] * point2[0]) + (coeff[3] * point3[0]); + pos->y = (coeff[0] * point0[1]) + (coeff[1] * point1[1]) + (coeff[2] * point2[1]) + (coeff[3] * point3[1]); + pos->z = (coeff[0] * point0[2]) + (coeff[1] * point1[2]) + (coeff[2] * point2[2]) + (coeff[3] * point3[2]); + *roll = (coeff[0] * point0[3]) + (coeff[1] * point1[3]) + (coeff[2] * point2[3]) + (coeff[3] * point3[3]); + *viewAngle = (coeff[0] * point0[4]) + (coeff[1] * point1[4]) + (coeff[2] * point2[4]) + (coeff[3] * point3[4]); +} + +s32 func_800BB2B4(Vec3f* pos, f32* roll, f32* fov, CutsceneCameraPoint* point, s16* keyFrame, f32* curFrame) { + s32 ret = false; + f32 pointData[4][5]; + s32 i; + f32 progress = *curFrame; + s32 key = *keyFrame; + f32 speed1 = 0.0f; + f32 speed2 = 0.0f; + f32 advance; + + if (key < 0) { + progress = 0.0f; + } + + if ((point[key].continueFlag == -1) || (point[key + 1].continueFlag == -1) || (point[key + 2].continueFlag == -1)) { + return true; + } + + for (i = 0; i < 4; i++) { + pointData[i][0] = point[key + i].pos.x; + pointData[i][1] = point[key + i].pos.y; + pointData[i][2] = point[key + i].pos.z; + pointData[i][3] = point[key + i].cameraRoll; + pointData[i][4] = point[key + i].viewAngle; + } + + func_800BB0A0(progress, pos, roll, fov, pointData[0], pointData[1], pointData[2], pointData[3]); + + if (point[*keyFrame + 1].nextPointFrame != 0) { + speed1 = 1.0f / point[*keyFrame + 1].nextPointFrame; + } + + if (point[*keyFrame + 2].nextPointFrame != 0) { + speed2 = 1.0f / point[*keyFrame + 2].nextPointFrame; + } + advance = (*curFrame * (speed2 - speed1)) + speed1; + if (advance < 0.0f) { + advance = 0; + } + *curFrame += advance; + if (*curFrame >= 1) { + if (point[++*keyFrame + 3].continueFlag == -1) { + *keyFrame = 0; + ret = true; + } + *curFrame -= 1; + } + + return ret; +} diff --git a/soh/src/code/code_800C3C20.c b/soh/src/code/code_800C3C20.c new file mode 100644 index 000000000..bd3b37850 --- /dev/null +++ b/soh/src/code/code_800C3C20.c @@ -0,0 +1,13 @@ +#include "global.h" + +u8 D_8012D200[] = { + 0, 1, 2, 3, 4, 5, 6, +}; + +void func_800C3C20(void) { + s32 i; + + for (i = 0; (i < ARRAY_COUNT(D_8012D200)) & 0xFFFFFFFF; i++) { + Audio_StopSfxByBank(D_8012D200[i]); + } +} diff --git a/soh/src/code/code_800D2E30.c b/soh/src/code/code_800D2E30.c new file mode 100644 index 000000000..2742e7232 --- /dev/null +++ b/soh/src/code/code_800D2E30.c @@ -0,0 +1,120 @@ +#include "global.h" +#include + +void func_800D2E30(UnkRumbleStruct* arg0) { + static u8 D_8012DBB0 = 1; + s32 i; + s32 unk_a3; + s32 index = -1; + + for (i = 0; i < 4; i++) { + arg0->rumbleEnable[i] = 0; + } + + if (arg0->unk_105 == 0) { + if (D_8012DBB0 != 0) { + for (i = 0; i < 4; i++) { + gPadMgr.pakType[i] = 0; + } + } + D_8012DBB0 = arg0->unk_105; + return; + } + + D_8012DBB0 = arg0->unk_105; + + if (arg0->unk_104 == 2) { + for (i = 0; i < 4; ++i) { + gPadMgr.pakType[i] = 0; + } + + for (i = 0; i < 0x40; i++) { + arg0->unk_C4[i] = 0; + arg0->unk_84[i] = 0; + arg0->unk_44[i] = 0; + arg0->unk_04[i] = 0; + } + arg0->unk_106 = arg0->unk_108 = arg0->unk_10A = arg0->unk_10B = arg0->unk_10C = arg0->unk_10D = 0; + arg0->unk_104 = 1; + } + if (arg0->unk_104 != 0) { + for (i = 0; i < 0x40; i++) { + if (arg0->unk_04[i] != 0) { + if (arg0->unk_44[i] > 0) { + arg0->unk_44[i]--; + } else { + unk_a3 = arg0->unk_04[i] - arg0->unk_84[i]; + if (unk_a3 > 0) { + arg0->unk_04[i] = unk_a3; + } else { + arg0->unk_04[i] = 0; + } + } + + unk_a3 = arg0->unk_C4[i] + arg0->unk_04[i]; + arg0->unk_C4[i] = unk_a3; + if (index == -1) { + index = i; + arg0->rumbleEnable[0] = (unk_a3 >= 0x100); + } else if (arg0->unk_04[index] < arg0->unk_04[i]) { + index = i; + arg0->rumbleEnable[0] = (unk_a3 >= 0x100); + } + } + } + if (arg0->unk_10A != 0) { + if (arg0->unk_10B > 0) { + arg0->unk_10B--; + } else { + unk_a3 = arg0->unk_10A - arg0->unk_10C; + if (unk_a3 > 0) { + arg0->unk_10A = unk_a3; + } else { + arg0->unk_10A = 0; + } + } + unk_a3 = arg0->unk_10D + arg0->unk_10A; + arg0->unk_10D = unk_a3; + arg0->rumbleEnable[0] = (unk_a3 >= 0x100); + } + if (arg0->unk_10A != 0) { + unk_a3 = arg0->unk_10A; + } else { + if (index == -1) { + unk_a3 = 0; + } else { + unk_a3 = arg0->unk_04[index]; + } + } + if (unk_a3 == 0) { + if ((++arg0->unk_108) >= 6) { + arg0->unk_106 = 0; + arg0->unk_108 = 5; + } + } else { + arg0->unk_108 = 0; + if ((++arg0->unk_106) >= 0x1C21) { + arg0->unk_104 = 0; + } + } + } else { + for (i = 0; i < 0x40; i++) { + arg0->unk_C4[i] = 0; + arg0->unk_84[i] = 0; + arg0->unk_44[i] = 0; + arg0->unk_04[i] = 0; + } + + arg0->unk_106 = arg0->unk_108 = arg0->unk_10A = arg0->unk_10B = arg0->unk_10C = arg0->unk_10D = 0; + } +} + +void func_800D3140(UnkRumbleStruct* arg0) { + memset(arg0, 0,sizeof(UnkRumbleStruct)); + arg0->unk_104 = 2; + arg0->unk_105 = 1; +} + +void func_800D3178(UnkRumbleStruct* arg0) { + memset(arg0, 0, sizeof(UnkRumbleStruct)); +} diff --git a/soh/src/code/code_800D31A0.c b/soh/src/code/code_800D31A0.c new file mode 100644 index 000000000..8e8a438c6 --- /dev/null +++ b/soh/src/code/code_800D31A0.c @@ -0,0 +1,19 @@ +#include "global.h" +#include "vt.h" + +u32 gIsCtrlr2Valid = false; + +void func_800D31A0(void) { + osSyncPrintf(VT_FGCOL(RED) "\n**** Freeze!! ****\n" VT_RST); + while (true) { + Sleep_Msec(1000); + } +} + +void func_800D31F0(void) { + gIsCtrlr2Valid = (gPadMgr.validCtrlrsMask & 2) != 0; +} + +void func_800D3210(void) { + gIsCtrlr2Valid = false; +} diff --git a/soh/src/code/code_800E4FE0.c b/soh/src/code/code_800E4FE0.c new file mode 100644 index 000000000..c46ae7a07 --- /dev/null +++ b/soh/src/code/code_800E4FE0.c @@ -0,0 +1,865 @@ +#include "global.h" + +#define SAMPLES_TO_OVERPRODUCE 0x10 +#define EXTRA_BUFFERED_AI_SAMPLES_TARGET 0x80 + +typedef enum { + CHAN_UPD_UNK_0, // 0 + CHAN_UPD_VOL_SCALE, // 1 + CHAN_UPD_VOL, // 2 + CHAN_UPD_PAN_SIGNED, // 3 + CHAN_UPD_FREQ_SCALE, // 4 + CHAN_UPD_REVERB, // 5 + CHAN_UPD_SCRIPT_IO, // 6 + CHAN_UPD_PAN_UNSIGNED, // 7 + CHAN_UPD_STOP_SOMETHING2, // 8 + CHAN_UPD_MUTE_BEHAVE, // 9 + CHAN_UPD_VIBE_X8, // 10 + CHAN_UPD_VIBE_X32, // 11 + CHAN_UPD_UNK_0F, // 12 + CHAN_UPD_UNK_20, // 13 + CHAN_UPD_STEREO // 14 +} ChannelUpdateType; + +void func_800E6300(SequenceChannel* channel, AudioCmd* arg1); +void func_800E59AC(s32 playerIdx, s32 fadeTimer); +void Audio_InitMesgQueues(void); +AudioTask* func_800E5000(void); +void Audio_ProcessCmds(u32); +void func_800E6128(SequencePlayer* seqPlayer, AudioCmd* arg1); +void func_800E5958(s32 playerIdx, s32 fadeTimer); +s32 func_800E66C0(s32 arg0); + +// AudioMgr_Retrace +AudioTask* func_800E4FE0(void) { + return func_800E5000(); +} + +extern u64 rspAspMainDataStart[]; +extern u64 rspAspMainDataEnd[]; + +void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples) { + // OTRTODO: uintptr_t? + u32 sp4C; + + gAudioContext.totalTaskCnt++; + + AudioLoad_DecreaseSampleDmaTtls(); + AudioLoad_ProcessLoads(gAudioContext.resetStatus); + AudioLoad_ProcessScriptLoads(); + + if (gAudioContext.resetStatus != 0) { + if (AudioHeap_ResetStep() == 0) { + if (gAudioContext.resetStatus == 0) { + osSendMesg(gAudioContext.audioResetQueueP, gAudioContext.audioResetSpecIdToLoad, OS_MESG_NOBLOCK); + } + } + } + + int j = 0; + if (gAudioContext.resetStatus == 0) { + // msg = 0000RREE R = read pos, E = End Pos + while (osRecvMesg(gAudioContext.cmdProcQueueP, (OSMesg*)&sp4C, OS_MESG_NOBLOCK) != -1) { + Audio_ProcessCmds(sp4C); + j++; + } + if ((j == 0) && (gAudioContext.cmdQueueFinished)) { + Audio_ScheduleProcessCmds(); + } + } + s32 writtenCmds; + AudioSynth_Update(gAudioContext.curAbiCmdBuf, &writtenCmds, samples, num_samples); + gAudioContext.audioRandom = (gAudioContext.audioRandom + gAudioContext.totalTaskCnt) * osGetCount(); +} + +AudioTask* func_800E5000(void) { + return NULL; + + static s32 sMaxAbiCmdCnt = 0x80; + static AudioTask* sWaitingAudioTask = NULL; + u32 samplesRemainingInAi; + s32 abiCmdCnt; + s32 pad; + s32 j; + s32 sp5C; + s16* currAiBuffer; + OSTask_t* task; + s32 index; + u32 sp4C; + s32 sp48; + s32 i; + + gAudioContext.totalTaskCnt++; + if (gAudioContext.totalTaskCnt % (gAudioContext.audioBufferParameters.specUnk4) != 0) { + if (D_801755D0 != NULL) { + D_801755D0(); + } + + if ((gAudioContext.totalTaskCnt % gAudioContext.audioBufferParameters.specUnk4) + 1 == + gAudioContext.audioBufferParameters.specUnk4) { + return sWaitingAudioTask; + } else { + return NULL; + } + } + + osSendMesg(gAudioContext.taskStartQueueP, gAudioContext.totalTaskCnt, OS_MESG_NOBLOCK); + gAudioContext.rspTaskIdx ^= 1; + gAudioContext.curAIBufIdx++; + gAudioContext.curAIBufIdx %= 3; + index = (gAudioContext.curAIBufIdx - 2 + 3) % 3; + samplesRemainingInAi = osAiGetLength() / 4; + + if (gAudioContext.resetTimer < 16) { + if (gAudioContext.aiBufLengths[index] != 0) { + osAiSetNextBuffer(gAudioContext.aiBuffers[index], gAudioContext.aiBufLengths[index] * 4); + if (gAudioContext.aiBuffers[index]) {} + if (gAudioContext.aiBufLengths[index]) {} + } + } + + if (D_801755D0 != NULL) { + D_801755D0(); + } + + sp5C = gAudioContext.curAudioFrameDmaCount; + for (i = 0; i < gAudioContext.curAudioFrameDmaCount; i++) { + if (osRecvMesg(&gAudioContext.currAudioFrameDmaQueue, NULL, OS_MESG_NOBLOCK) == 0) { + sp5C--; + } + } + + if (sp5C != 0) { + for (i = 0; i < sp5C; i++) { + osRecvMesg(&gAudioContext.currAudioFrameDmaQueue, NULL, OS_MESG_BLOCK); + } + } + + sp48 = gAudioContext.currAudioFrameDmaQueue.validCount; + if (sp48 != 0) { + for (i = 0; i < sp48; i++) { + osRecvMesg(&gAudioContext.currAudioFrameDmaQueue, NULL, OS_MESG_NOBLOCK); + } + } + + gAudioContext.curAudioFrameDmaCount = 0; + AudioLoad_DecreaseSampleDmaTtls(); + AudioLoad_ProcessLoads(gAudioContext.resetStatus); + AudioLoad_ProcessScriptLoads(); + + if (gAudioContext.resetStatus != 0) { + if (AudioHeap_ResetStep() == 0) { + if (gAudioContext.resetStatus == 0) { + osSendMesg(gAudioContext.audioResetQueueP, gAudioContext.audioResetSpecIdToLoad, OS_MESG_NOBLOCK); + } + + sWaitingAudioTask = NULL; + return NULL; + } + } + + if (gAudioContext.resetTimer > 16) { + return NULL; + } + if (gAudioContext.resetTimer != 0) { + gAudioContext.resetTimer++; + } + + gAudioContext.currTask = &gAudioContext.rspTask[gAudioContext.rspTaskIdx]; + gAudioContext.curAbiCmdBuf = gAudioContext.abiCmdBufs[gAudioContext.rspTaskIdx]; + + index = gAudioContext.curAIBufIdx; + currAiBuffer = gAudioContext.aiBuffers[index]; + + gAudioContext.aiBufLengths[index] = + (s16)((((gAudioContext.audioBufferParameters.samplesPerFrameTarget - samplesRemainingInAi) + + EXTRA_BUFFERED_AI_SAMPLES_TARGET) & + ~0xF) + + SAMPLES_TO_OVERPRODUCE); + if (gAudioContext.aiBufLengths[index] < gAudioContext.audioBufferParameters.minAiBufferLength) { + gAudioContext.aiBufLengths[index] = gAudioContext.audioBufferParameters.minAiBufferLength; + } + + if (gAudioContext.aiBufLengths[index] > gAudioContext.audioBufferParameters.maxAiBufferLength) { + gAudioContext.aiBufLengths[index] = gAudioContext.audioBufferParameters.maxAiBufferLength; + } + + j = 0; + if (gAudioContext.resetStatus == 0) { + // msg = 0000RREE R = read pos, E = End Pos + while (osRecvMesg(gAudioContext.cmdProcQueueP, (OSMesg*)&sp4C, OS_MESG_NOBLOCK) != -1) { + if (1) {} + if (1) {} + if (1) {} + Audio_ProcessCmds(sp4C); + j++; + } + if ((j == 0) && (gAudioContext.cmdQueueFinished)) { + Audio_ScheduleProcessCmds(); + } + } + + gAudioContext.curAbiCmdBuf = + AudioSynth_Update(gAudioContext.curAbiCmdBuf, &abiCmdCnt, currAiBuffer, gAudioContext.aiBufLengths[index]); + gAudioContext.audioRandom = (gAudioContext.audioRandom + gAudioContext.totalTaskCnt) * osGetCount(); + gAudioContext.audioRandom = + gAudioContext.aiBuffers[index][gAudioContext.totalTaskCnt & 0xFF] + gAudioContext.audioRandom; + gWaveSamples[8] = (s16*)(((u8*)func_800E4FE0) + (gAudioContext.audioRandom & 0xFFF0)); + + index = gAudioContext.rspTaskIdx; + gAudioContext.currTask->taskQueue = NULL; + gAudioContext.currTask->unk_44 = NULL; + + task = &gAudioContext.currTask->task.t; + task->type = M_AUDTASK; + task->flags = 0; + //task->ucode_boot = D_801120C0; + task->ucode_boot_size = 0x1000; + //task->ucode_data_size = ((rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64)) - 1; + //task->ucode = D_801120C0; + //task->ucode_data = rspAspMainDataStart; + task->ucode_size = 0x1000; + task->dram_stack = NULL; + task->dram_stack_size = 0; + task->output_buff = NULL; + task->output_buff_size = NULL; + if (1) {} + task->data_ptr = (u64*)gAudioContext.abiCmdBufs[index]; + task->data_size = abiCmdCnt * sizeof(Acmd); + task->yield_data_ptr = NULL; + task->yield_data_size = 0; + + if (sMaxAbiCmdCnt < abiCmdCnt) { + sMaxAbiCmdCnt = abiCmdCnt; + } + + if (gAudioContext.audioBufferParameters.specUnk4 == 1) { + return gAudioContext.currTask; + } else { + sWaitingAudioTask = gAudioContext.currTask; + return NULL; + } +} + +#define ACMD_SND_MDE ((u32)0xF0000000) +#define ACMD_MUTE ((u32)0xF1000000) + +void func_800E5584(AudioCmd* cmd) { + s32 i; + s32 pad; + s32 pad2; + u32 temp_a1_5; + u32 temp_t7; + + switch (cmd->op) { + case 0x81: + AudioLoad_SyncLoadSeqParts(cmd->arg1, cmd->arg2); + return; + case 0x82: + AudioLoad_SyncInitSeqPlayer(cmd->arg0, cmd->arg1, cmd->arg2); + func_800E59AC(cmd->arg0, cmd->data); + return; + case 0x85: + AudioLoad_SyncInitSeqPlayerSkipTicks(cmd->arg0, cmd->arg1, cmd->data); + return; + case 0x83: + if (gAudioContext.seqPlayers[cmd->arg0].enabled) { + if (cmd->asInt == 0) { + AudioSeq_SequencePlayerDisableAsFinished(&gAudioContext.seqPlayers[cmd->arg0]); + } else { + func_800E5958(cmd->arg0, cmd->asInt); + } + } + return; + case 0xF0: + gAudioContext.soundMode = cmd->asUInt; + return; + case 0xF1: + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[i]; + seqPlayer->muted = 1; + seqPlayer->recalculateVolume = 1; + } + return; + case 0xF2: + if (cmd->asUInt == 1) { + for (i = 0; i < gAudioContext.numNotes; i++) { + Note* note = &gAudioContext.notes[i]; + NoteSubEu* subEu = ¬e->noteSubEu; + if (subEu->bitField0.enabled && note->playbackState.unk_04 == 0) { + if (note->playbackState.parentLayer->channel->muteBehavior & 8) { + subEu->bitField0.finished = 1; + } + } + } + } + + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[i]; + seqPlayer->muted = 0; + seqPlayer->recalculateVolume = 1; + } + + return; + case 0xF3: + AudioLoad_SyncLoadInstrument(cmd->arg0, cmd->arg1, cmd->arg2); + return; + case 0xF4: + AudioLoad_AsyncLoadSampleBank(cmd->arg0, cmd->arg1, cmd->arg2, &gAudioContext.externalLoadQueue); + return; + case 0xF5: + AudioLoad_AsyncLoadFont(cmd->arg0, cmd->arg1, cmd->arg2, &gAudioContext.externalLoadQueue); + return; + case 0xFC: + AudioLoad_AsyncLoadSeq(cmd->arg0, cmd->arg1, cmd->arg2, &gAudioContext.externalLoadQueue); + return; + case 0xF6: + AudioLoad_DiscardSeqFonts(cmd->arg1); + return; + case 0x90: + gAudioContext.unk_5BDC[cmd->arg0] = cmd->asUShort; + return; + case 0xF9: + gAudioContext.resetStatus = 5; + gAudioContext.audioResetSpecIdToLoad = cmd->asUInt; + return; + case 0xFB: + D_801755D0 = (void (*)(void))cmd->asUInt; + return; + case 0xE0: + case 0xE1: + case 0xE2: + Audio_SetFontInstrument(cmd->op - 0xE0, cmd->arg0, cmd->arg1, cmd->data); + return; + case 0xFE: + temp_t7 = cmd->asUInt; + if (temp_t7 == 1) { + for (i = 0; i < gAudioContext.audioBufferParameters.numSequencePlayers; i++) { + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[i]; + if (seqPlayer->enabled) { + AudioSeq_SequencePlayerDisableAsFinished(seqPlayer); + } + } + } + func_800E66C0(temp_t7); + return; + case 0xE3: + AudioHeap_PopCache(cmd->asInt); + return; + default: + return; + } +} + +// SetFadeOutTimer +void func_800E5958(s32 playerIdx, s32 fadeTimer) { + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[playerIdx]; + + if (fadeTimer == 0) { + fadeTimer = 1; + } + + seqPlayer->fadeVelocity = -(seqPlayer->fadeVolume / fadeTimer); + seqPlayer->state = 2; + seqPlayer->fadeTimer = fadeTimer; +} + +// SetFadeInTimer +void func_800E59AC(s32 playerIdx, s32 fadeTimer) { + SequencePlayer* seqPlayer; + if (fadeTimer != 0) { + seqPlayer = &gAudioContext.seqPlayers[playerIdx]; + seqPlayer->state = 1; + seqPlayer->fadeTimerUnkEu = fadeTimer; + seqPlayer->fadeTimer = fadeTimer; + seqPlayer->fadeVolume = 0.0f; + seqPlayer->fadeVelocity = 0.0f; + } +} + +void Audio_InitMesgQueuesInternal(void) { + gAudioContext.cmdWrPos = 0; + gAudioContext.cmdRdPos = 0; + gAudioContext.cmdQueueFinished = 0; + gAudioContext.taskStartQueueP = &gAudioContext.taskStartQueue; + gAudioContext.cmdProcQueueP = &gAudioContext.cmdProcQueue; + gAudioContext.audioResetQueueP = &gAudioContext.audioResetQueue; + osCreateMesgQueue(gAudioContext.taskStartQueueP, gAudioContext.taskStartMsgs, + ARRAY_COUNT(gAudioContext.taskStartMsgs)); + osCreateMesgQueue(gAudioContext.cmdProcQueueP, gAudioContext.cmdProcMsgs, ARRAY_COUNT(gAudioContext.cmdProcMsgs)); + osCreateMesgQueue(gAudioContext.audioResetQueueP, gAudioContext.audioResetMesgs, + ARRAY_COUNT(gAudioContext.audioResetMesgs)); +} + +void Audio_QueueCmd(u32 opArgs, void** data) { + AudioCmd* cmd = &gAudioContext.cmdBuf[gAudioContext.cmdWrPos & 0xFF]; + + cmd->opArgs = opArgs; + cmd->data = *data; + + gAudioContext.cmdWrPos++; + + if (gAudioContext.cmdWrPos == gAudioContext.cmdRdPos) { + gAudioContext.cmdWrPos--; + } +} + +void Audio_QueueCmdF32(u32 opArgs, f32 data) { + Audio_QueueCmd(opArgs, (void**)&data); +} + +void Audio_QueueCmdS32(u32 opArgs, s32 data) { + Audio_QueueCmd(opArgs, (void**)&data); +} + +void Audio_QueueCmdS8(u32 opArgs, s8 data) { + u32 uData = data << 0x18; + + Audio_QueueCmd(opArgs, (void**)&uData); +} + +void Audio_QueueCmdU16(u32 opArgs, u16 data) { + u32 uData = data << 0x10; + + Audio_QueueCmd(opArgs, (void**)&uData); +} + +s32 Audio_ScheduleProcessCmds(void) { + static s32 D_801304E8 = 0; + s32 ret; + + if (D_801304E8 < (u8)((gAudioContext.cmdWrPos - gAudioContext.cmdRdPos) + 0x100)) { + D_801304E8 = (u8)((gAudioContext.cmdWrPos - gAudioContext.cmdRdPos) + 0x100); + } + + ret = + osSendMesg(gAudioContext.cmdProcQueueP, + (void*)(((gAudioContext.cmdRdPos & 0xFF) << 8) | (gAudioContext.cmdWrPos & 0xFF)), OS_MESG_NOBLOCK); + if (ret != -1) { + gAudioContext.cmdRdPos = gAudioContext.cmdWrPos; + ret = 0; + } else { + return -1; + } + + return ret; +} + +void Audio_ResetCmdQueue(void) { + gAudioContext.cmdQueueFinished = 0; + gAudioContext.cmdRdPos = gAudioContext.cmdWrPos; +} + +void Audio_ProcessCmd(AudioCmd* cmd) { + SequencePlayer* seqPlayer; + u16 phi_v0; + s32 i; + + if ((cmd->op & 0xF0) == 0xF0) { + func_800E5584(cmd); + return; + } + + if (cmd->arg0 < gAudioContext.audioBufferParameters.numSequencePlayers) { + seqPlayer = &gAudioContext.seqPlayers[cmd->arg0]; + if (cmd->op & 0x80) { + func_800E5584(cmd); + return; + } + if (cmd->op & 0x40) { + func_800E6128(seqPlayer, cmd); + return; + } + + if (cmd->arg1 < 0x10) { + func_800E6300(seqPlayer->channels[cmd->arg1], cmd); + return; + } + if (cmd->arg1 == 0xFF) { + phi_v0 = gAudioContext.unk_5BDC[cmd->arg0]; + for (i = 0; i < 0x10; i++) { + if (phi_v0 & 1) { + func_800E6300(seqPlayer->channels[i], cmd); + } + phi_v0 = phi_v0 >> 1; + } + } + } +} + +void Audio_ProcessCmds(u32 msg) { + static u8 curCmdRdPos = 0; + AudioCmd* cmd; + u8 endPos; + + if (!gAudioContext.cmdQueueFinished) { + curCmdRdPos = msg >> 8; + } + + while (true) { + endPos = msg & 0xFF; + if (curCmdRdPos == endPos) { + gAudioContext.cmdQueueFinished = 0; + return; + } + + cmd = &gAudioContext.cmdBuf[curCmdRdPos++ & 0xFF]; + if (cmd->op == 0xF8) { + gAudioContext.cmdQueueFinished = 1; + return; + } + + Audio_ProcessCmd(cmd); + cmd->op = 0; + } +} + +u32 func_800E5E20(u32* out) { + u32 sp1C; + + if (osRecvMesg(&gAudioContext.externalLoadQueue, (OSMesg*)&sp1C, OS_MESG_NOBLOCK) == -1) { + *out = 0; + return 0; + } + *out = sp1C & 0xFFFFFF; + return sp1C >> 0x18; +} + +u8* func_800E5E84(s32 arg0, u32* arg1) { + return AudioLoad_GetFontsForSequence(arg0, arg1); +} + +void func_800E5EA4(s32 arg0, u32* arg1, u32* arg2) { + *arg1 = gAudioContext.soundFonts[arg0].sampleBankId1; + *arg2 = gAudioContext.soundFonts[arg0].sampleBankId2; +} + +s32 func_800E5EDC(void) { + s32 pad; + s32 sp18; + + if (osRecvMesg(gAudioContext.audioResetQueueP, (OSMesg*)&sp18, OS_MESG_NOBLOCK) == -1) { + return 0; + } else if (gAudioContext.audioResetSpecIdToLoad != sp18) { + return -1; + } else { + return 1; + } +} + +void func_800E5F34(void) { + // macro? + // clang-format off + s32 chk = -1; s32 sp28; do {} while (osRecvMesg(gAudioContext.audioResetQueueP, (OSMesg*)&sp28, OS_MESG_NOBLOCK) != chk); + // clang-format on +} + +s32 func_800E5F88(s32 resetPreloadID) { + s32 resetStatus; + OSMesg msg; + s32 pad; + + func_800E5F34(); + resetStatus = gAudioContext.resetStatus; + if (resetStatus != 0) { + Audio_ResetCmdQueue(); + if (gAudioContext.audioResetSpecIdToLoad == resetPreloadID) { + return -2; + } else if (resetStatus > 2) { + gAudioContext.audioResetSpecIdToLoad = resetPreloadID; + return -3; + } else { + osRecvMesg(gAudioContext.audioResetQueueP, &msg, OS_MESG_BLOCK); + } + } + + func_800E5F34(); + Audio_QueueCmdS32(0xF9000000, resetPreloadID); + + return Audio_ScheduleProcessCmds(); +} + +void Audio_PreNMIInternal(void) { + gAudioContext.resetTimer = 1; + if (gAudioContextInitalized) { + func_800E5F88(0); + gAudioContext.resetStatus = 0; + } +} + +s8 func_800E6070(s32 playerIdx, s32 channelIdx, s32 scriptIdx) { + SequencePlayer* seqPlayer = &gAudioContext.seqPlayers[playerIdx]; + SequenceChannel* channel; + if (seqPlayer->enabled) { + channel = seqPlayer->channels[channelIdx]; + return channel->soundScriptIO[scriptIdx]; + } else { + return -1; + } +} + +s8 func_800E60C4(s32 playerIdx, s32 arg1) { + return gAudioContext.seqPlayers[playerIdx].soundScriptIO[arg1]; +} + +void Audio_InitExternalPool(void* mem, size_t size) { + AudioHeap_AllocPoolInit(&gAudioContext.externalPool, mem, size); +} + +void Audio_DestroyExternalPool(void) { + gAudioContext.externalPool.start = NULL; +} + +void Audio_SetGameVolume(int player_id, f32 volume) { + gAudioContext.seqPlayers[player_id].gameVolume = volume; + gAudioContext.seqPlayers[player_id].recalculateVolume = true; +} + +float Audio_GetGameVolume(int player_id) { + return gAudioContext.seqPlayers[player_id].gameVolume; +} + +void func_800E6128(SequencePlayer* seqPlayer, AudioCmd* cmd) { + f32 fadeVolume; + switch (cmd->op) { + case 0x41: + if (seqPlayer->fadeVolumeScale != cmd->asFloat) { + seqPlayer->fadeVolumeScale = cmd->asFloat; + seqPlayer->recalculateVolume = 1; + } + return; + case 0x47: + seqPlayer->tempo = cmd->asInt * 0x30; + return; + case 0x49: + seqPlayer->unk_0C = cmd->asInt * 0x30; + return; + case 0x4E: + seqPlayer->unk_0C = cmd->asInt; + return; + case 0x48: + seqPlayer->transposition = cmd->asSbyte; + return; + case 0x46: + seqPlayer->soundScriptIO[cmd->arg2] = cmd->asSbyte; + return; + case 0x4A: + fadeVolume = (s32)cmd->arg1 / 127.0f; + goto block_11; + case 0x4B: + fadeVolume = ((s32)cmd->arg1 / 100.0f) * seqPlayer->fadeVolume; + block_11: + if (seqPlayer->state != 2) { + seqPlayer->volume = seqPlayer->fadeVolume; + if (cmd->asInt == 0) { + seqPlayer->fadeVolume = fadeVolume; + } else { + s32 tmp = cmd->asInt; + seqPlayer->state = 0; + seqPlayer->fadeTimer = tmp; + seqPlayer->fadeVelocity = (fadeVolume - seqPlayer->fadeVolume) / tmp; + } + } + return; + case 0x4C: + if (seqPlayer->state != 2) { + if (cmd->asInt == 0) { + seqPlayer->fadeVolume = seqPlayer->volume; + } else { + s32 tmp = cmd->asInt; + seqPlayer->state = 0; + seqPlayer->fadeTimer = tmp; + seqPlayer->fadeVelocity = (seqPlayer->volume - seqPlayer->fadeVolume) / tmp; + } + } + return; + case 0x4D: + seqPlayer->unk_34 = cmd->asFloat; + if (seqPlayer->unk_34 == 1.0f) { + seqPlayer->unk_0b1 = 0; + } else { + seqPlayer->unk_0b1 = 1; + } + } +} + +void func_800E6300(SequenceChannel* channel, AudioCmd* cmd) { + switch (cmd->op) { + case CHAN_UPD_VOL_SCALE: + if (channel->volumeScale != cmd->asFloat) { + channel->volumeScale = cmd->asFloat; + channel->changes.s.volume = 1; + } + return; + case CHAN_UPD_VOL: + if (channel->volume != cmd->asFloat) { + channel->volume = cmd->asFloat; + channel->changes.s.volume = 1; + } + return; + case CHAN_UPD_PAN_SIGNED: + if (channel->newPan != cmd->asSbyte) { + channel->newPan = cmd->asSbyte; + channel->changes.s.pan = 1; + } + return; + case CHAN_UPD_PAN_UNSIGNED: + if (channel->newPan != cmd->asSbyte) { + channel->panChannelWeight = cmd->asSbyte; + channel->changes.s.pan = 1; + } + return; + case CHAN_UPD_FREQ_SCALE: + if (channel->freqScale != cmd->asFloat) { + channel->freqScale = cmd->asFloat; + channel->changes.s.freqScale = 1; + } + return; + case CHAN_UPD_REVERB: + if (channel->reverb != cmd->asSbyte) { + channel->reverb = cmd->asSbyte; + } + return; + case CHAN_UPD_SCRIPT_IO: + if (cmd->arg2 < 8) { + channel->soundScriptIO[cmd->arg2] = cmd->asSbyte; + } + return; + case CHAN_UPD_STOP_SOMETHING2: + channel->stopSomething2 = cmd->asSbyte; + return; + case CHAN_UPD_MUTE_BEHAVE: + channel->muteBehavior = cmd->asSbyte; + return; + case CHAN_UPD_VIBE_X8: + channel->vibratoExtentTarget = cmd->asUbyte * 8; + channel->vibratoExtentChangeDelay = 1; + return; + case CHAN_UPD_VIBE_X32: + channel->vibratoRateTarget = cmd->asUbyte * 32; + channel->vibratoRateChangeDelay = 1; + return; + case CHAN_UPD_UNK_0F: + channel->unk_0F = cmd->asUbyte; + return; + case CHAN_UPD_UNK_20: + channel->unk_20 = cmd->asUShort; + return; + case CHAN_UPD_STEREO: + channel->stereo.asByte = cmd->asUbyte; + return; + } +} + +void func_800E64B0(s32 arg0, s32 arg1, s32 arg2) { + Audio_QueueCmdS32(((arg0 & 0xFF) << 0x10) | 0xFA000000 | ((arg1 & 0xFF) << 8) | (arg2 & 0xFF), 1); +} + +void func_800E64F8(void) { + Audio_QueueCmdS32(0xFA000000, 0); +} + +void func_800E651C(u32 arg0, s32 arg1) { + Audio_QueueCmdS32((arg1 & 0xFF) | 0xFD000000, arg0); +} + +void Audio_WaitForAudioTask(void) { + osRecvMesg(gAudioContext.taskStartQueueP, NULL, OS_MESG_NOBLOCK); + osRecvMesg(gAudioContext.taskStartQueueP, NULL, OS_MESG_BLOCK); +} + +s32 func_800E6590(s32 playerIdx, s32 arg1, s32 arg2) { + SequencePlayer* seqPlayer; + SequenceLayer* layer; + Note* note; + SoundFontSound* sound; + s32 loopEnd; + s32 samplePos; + + seqPlayer = &gAudioContext.seqPlayers[playerIdx]; + if (seqPlayer->enabled && seqPlayer->channels[arg1]->enabled) { + layer = seqPlayer->channels[arg1]->layers[arg2]; + if (layer == NULL) { + return 0; + } + + if (layer->enabled) { + if (layer->note == NULL) { + return 0; + } + + if (!layer->bit3) { + return 0; + } + + note = layer->note; + if (layer == note->playbackState.parentLayer) { + sound = note->noteSubEu.sound.soundFontSound; + if (sound == NULL) { + return 0; + } + loopEnd = sound->sample->loop->end; + samplePos = note->synthesisState.samplePosInt; + return loopEnd - samplePos; + } + return 0; + } + } + return 0; +} + +s32 func_800E6680(void) { + return func_800E66C0(0); +} + +void func_800E66A0(void) { + func_800E66C0(2); +} + +s32 func_800E66C0(s32 arg0) { + s32 phi_v1; + NotePlaybackState* temp_a2; + NoteSubEu* temp_a3; + s32 i; + Note* note; + SoundFontSound* sound; + + phi_v1 = 0; + for (i = 0; i < gAudioContext.numNotes; i++) { + note = &gAudioContext.notes[i]; + temp_a2 = ¬e->playbackState; + if (note->noteSubEu.bitField0.enabled) { + temp_a3 = ¬e->noteSubEu; + if (temp_a2->adsr.action.s.state != 0) { + if (arg0 >= 2) { + sound = temp_a3->sound.soundFontSound; + if (sound == NULL || temp_a3->bitField1.isSyntheticWave) { + continue; + } + if (sound->sample->medium == MEDIUM_RAM) { + continue; + } + } + + phi_v1++; + if ((arg0 & 1) == 1) { + temp_a2->adsr.fadeOutVel = gAudioContext.audioBufferParameters.updatesPerFrameInv; + temp_a2->adsr.action.s.release = 1; + } + } + } + } + return phi_v1; +} + +u32 Audio_NextRandom(void) { + static u32 audRand = 0x12345678; + + audRand = ((osGetCount() + 0x1234567) * (audRand + gAudioContext.totalTaskCnt)); + audRand += gAudioContext.audioRandom; + return audRand; +} + +void Audio_InitMesgQueues(void) { + Audio_InitMesgQueuesInternal(); +} diff --git a/soh/src/code/code_800E6840.c b/soh/src/code/code_800E6840.c new file mode 100644 index 000000000..27f019fef --- /dev/null +++ b/soh/src/code/code_800E6840.c @@ -0,0 +1,15 @@ +#include "global.h" + +void Audio_InvalDCache(void* buf, size_t size) { + OSIntMask prevMask = osSetIntMask(1); + + osInvalDCache(buf, size); + osSetIntMask(prevMask); +} + +void Audio_WritebackDCache(void* buf, size_t size) { + OSIntMask prevMask = osSetIntMask(1); + + osWritebackDCache(buf, size); + osSetIntMask(prevMask); +} diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c new file mode 100644 index 000000000..5efa93167 --- /dev/null +++ b/soh/src/code/code_800EC960.c @@ -0,0 +1,5056 @@ +#include "ultra64.h" +#include "global.h" + +// TODO: can these macros be shared between files? code_800F9280 seems to use +// versions without any casts... +#define Audio_DisableSeq(playerIdx, fadeOut) Audio_QueueCmdS32(0x83000000 | ((u8)playerIdx << 16), fadeOut) +#define Audio_StartSeq(playerIdx, fadeTimer, seqId) \ + Audio_QueueSeqCmd(0x00000000 | ((u8)playerIdx << 24) | ((u8)(fadeTimer) << 0x10) | (u16)seqId) +#define Audio_SeqCmd7(playerIdx, a, b) \ + Audio_QueueSeqCmd(0x70000000 | ((u8)playerIdx << 0x18) | ((u8)a << 0x10) | (u8)(b)) +#define Audio_SeqCmdC(playerIdx, a, b, c) \ + Audio_QueueSeqCmd(0xC0000000 | ((u8)playerIdx << 24) | ((u8)a << 16) | ((u8)b << 8) | ((u8)(c))) +#define Audio_SeqCmdA(playerIdx, a) Audio_QueueSeqCmd(0xA0000000 | ((u8)playerIdx << 24) | ((u16)(a))) +#define Audio_SeqCmd1(playerIdx, a) Audio_QueueSeqCmd(0x100000FF | ((u8)playerIdx << 24) | ((u8)(a) << 16)) +#define Audio_SeqCmdB(playerIdx, a, b, c) \ + Audio_QueueSeqCmd(0xB0000000 | ((u8)playerIdx << 24) | ((u8)a << 16) | ((u8)b << 8) | ((u8)c)) +#define Audio_SeqCmdB40(playerIdx, a, b) Audio_QueueSeqCmd(0xB0004000 | ((u8)playerIdx << 24) | ((u8)a << 16) | ((u8)b)) +#define Audio_SeqCmd6(playerIdx, a, b, c) \ + Audio_QueueSeqCmd(0x60000000 | ((u8)playerIdx << 24) | ((u8)(a) << 16) | ((u8)b << 8) | ((u8)c)) +#define Audio_SeqCmdE0(playerIdx, a) Audio_QueueSeqCmd(0xE0000000 | ((u8)playerIdx << 24) | ((u8)a)) +#define Audio_SeqCmdE01(playerIdx, a) Audio_QueueSeqCmd(0xE0000100 | ((u8)playerIdx << 24) | ((u16)a)) +#define Audio_SeqCmd8(playerIdx, a, b, c) \ + Audio_QueueSeqCmd(0x80000000 | ((u8)playerIdx << 24) | ((u8)a << 16) | ((u8)b << 8) | ((u8)c)) +#define Audio_SeqCmdF(playerIdx, a) Audio_QueueSeqCmd(0xF0000000 | ((u8)playerIdx << 24) | ((u8)a)) + +typedef struct { + /* 0x0 */ f32 vol; + /* 0x4 */ f32 freqScale; + /* 0x8 */ s8 reverb; + /* 0x9 */ s8 panSigned; + /* 0xA */ s8 stereoBits; + /* 0xB */ u8 filter; + /* 0xC */ u8 unk_0C; +} SfxPlayerState; + +typedef struct { + /* 0x0 */ f32 value; + /* 0x4 */ f32 target; + /* 0x8 */ f32 step; + /* 0xC */ s32 remainingFrames; +} FreqLerp; + +typedef struct { + /* 0x0 */ u16 playerIO; + /* 0x2 */ u16 channelMask; + /* 0x4 */ u8 channelIO[3 * 33 + 1]; +} NatureAmbienceDataIO; // size = 0x68 + +typedef enum { + /* 0x0 */ PAGE_NON, + /* 0x1 */ PAGE_SOUND_CONTROL, + /* 0x2 */ PAGE_SPEC_INFO, // unused + /* 0x3 */ PAGE_HEAP_INFO, + /* 0x4 */ PAGE_GROUP_TRACK_INFO, // unused + /* 0x5 */ PAGE_SUB_TRACK_INFO, + /* 0x6 */ PAGE_CHANNEL_INFO, // unused + /* 0x7 */ PAGE_INTERFACE_INFO, + /* 0x8 */ PAGE_SFX_SWAP, + /* 0x9 */ PAGE_BLOCK_CHANGE_BGM, + /* 0xA */ PAGE_NATURAL_SOUND_CONTROL, // unused + /* 0xB */ PAGE_OCARINA_TEST, + /* 0xC */ PAGE_SFX_PARAMETER_CHANGE, + /* 0xD */ PAGE_SCROLL_PRINT, + /* 0xE */ PAGE_FREE_AREA, + /* 0xF */ PAGE_MAX +} AudioDebugPage; + +/** bit field of songs that can be played + * 0x0800 storms + * 0x0400 song of time + * 0x0200 suns + * 0x0100 lullaby + * 0x0080 epona + * 0x0040 sarias + * 0x0020 prelude + * 0x0010 nocturne + * 0x0008 requiem + * 0x0004 serenade + * 0x0002 bolero + * 0x0001 minuet + */ + +#define SCROLL_PRINT_BUF_SIZE 25 + +#define SFX_PLAYER_CHANNEL_OCARINA 13 + +u8 gIsLargeSoundBank[7] = { 0, 0, 0, 1, 0, 0, 0 }; + +// Only the first row of these is supported by sequence 0. (gSfxChannelLayout is always 0.) +u8 gChannelsPerBank[4][7] = { + { 3, 2, 3, 3, 2, 1, 2 }, + { 3, 2, 2, 2, 2, 2, 2 }, + { 3, 2, 2, 2, 2, 2, 2 }, + { 4, 1, 0, 0, 2, 2, 2 }, +}; +u8 gUsedChannelsPerBank[4][7] = { + { 3, 2, 3, 2, 2, 1, 1 }, + { 3, 1, 1, 1, 2, 1, 1 }, + { 3, 1, 1, 1, 2, 1, 1 }, + { 2, 1, 0, 0, 1, 1, 1 }, +}; + +f32 D_801305B0 = 0.7950898f; +s8 D_801305B4 = 35; +s8 D_801305B8 = 20; +s8 D_801305BC = 30; +s8 D_801305C0 = 20; +f32 sBehindScreenZ[2] = { -15.0f, -65.0f }; +u8 sAudioIncreasingTranspose = 0; +u8 gMorphaTransposeTable[16] = { 0, 0, 0, 1, 1, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8 }; +u8 sPrevChargeLevel = 0; +f32 D_801305E4[4] = { 1.0f, 1.12246f, 1.33484f, 1.33484f }; // 2**({0, 2, 5, 5}/12) +f32 D_801305F4 = 1.0f; +u8 D_801305F8[8] = { 127, 80, 75, 73, 70, 68, 65, 60 }; +u8 D_80130600 = 0; +s8 D_80130604 = 2; +s8 D_80130608 = 0; +s8 sAudioCutsceneFlag = 0; +s8 sSpecReverb = 0; +s8 sAudioEnvReverb = 0; +s8 sAudioCodeReverb = 0; +u8 sPrevSeqMode = 0; +f32 sAudioEnemyDist = 0.0f; +s8 sAudioEnemyVol = 127; +u16 sPrevMainBgmSeqId = NA_BGM_DISABLED; +u8 D_8013062C = 0; +u8 D_80130630 = NA_BGM_GENERAL_SFX; +u32 sNumFramesStill = 0; +u32 sNumFramesMoving = 0; +u8 sAudioBaseFilter = 0; +u8 sAudioExtraFilter = 0; +u8 sAudioBaseFilter2 = 0; +u8 sAudioExtraFilter2 = 0; +Vec3f* sSariaBgmPtr = NULL; +f32 D_80130650 = 2000.0f; +u8 sSeqModeInput = 0; +u8 sSeqFlags[0x6E] = { + 0x2, // NA_BGM_GENERAL_SFX + 0x1, // NA_BGM_NATURE_BACKGROUND + 0, // NA_BGM_FIELD_LOGIC + 0, // NA_BGM_FIELD_INIT + 0, // NA_BGM_FIELD_DEFAULT_1 + 0, // NA_BGM_FIELD_DEFAULT_2 + 0, // NA_BGM_FIELD_DEFAULT_3 + 0, // NA_BGM_FIELD_DEFAULT_4 + 0, // NA_BGM_FIELD_DEFAULT_5 + 0, // NA_BGM_FIELD_DEFAULT_6 + 0, // NA_BGM_FIELD_DEFAULT_7 + 0, // NA_BGM_FIELD_DEFAULT_8 + 0, // NA_BGM_FIELD_DEFAULT_9 + 0, // NA_BGM_FIELD_DEFAULT_A + 0, // NA_BGM_FIELD_DEFAULT_B + 0, // NA_BGM_FIELD_ENEMY_INIT + 0, // NA_BGM_FIELD_ENEMY_1 + 0, // NA_BGM_FIELD_ENEMY_2 + 0, // NA_BGM_FIELD_ENEMY_3 + 0, // NA_BGM_FIELD_ENEMY_4 + 0, // NA_BGM_FIELD_STILL_1 + 0, // NA_BGM_FIELD_STILL_2 + 0, // NA_BGM_FIELD_STILL_3 + 0, // NA_BGM_FIELD_STILL_4 + 0x21, // NA_BGM_DUNGEON + 0x10, // NA_BGM_KAKARIKO_ADULT + 0, // NA_BGM_ENEMY + 0x88, // NA_BGM_BOSS + 0x1, // NA_BGM_INSIDE_DEKU_TREE + 0, // NA_BGM_MARKET + 0, // NA_BGM_TITLE + 0x20, // NA_BGM_LINK_HOUSE + 0, // NA_BGM_GAME_OVER + 0, // NA_BGM_BOSS_CLEAR + 0x2, // NA_BGM_ITEM_GET + 0x4, // NA_BGM_OPENING_GANON + 0x2, // NA_BGM_HEART_GET + 0x2, // NA_BGM_OCA_LIGHT + 0x1, // NA_BGM_JABU_JABU + 0x10, // NA_BGM_KAKARIKO_KID + 0, // NA_BGM_GREAT_FAIRY + 0, // NA_BGM_ZELDA_THEME + 0x1, // NA_BGM_FIRE_TEMPLE + 0x2, // NA_BGM_OPEN_TRE_BOX + 0x1, // NA_BGM_FOREST_TEMPLE + 0, // NA_BGM_COURTYARD + 0x80, // NA_BGM_GANON_TOWER + 0, // NA_BGM_LONLON + 0x80, // NA_BGM_GORON_CITY + 0, // NA_BGM_FIELD_MORNING + 0x2, // NA_BGM_SPIRITUAL_STONE + 0x2, // NA_BGM_OCA_BOLERO + 0x2, // NA_BGM_OCA_MINUET + 0x2, // NA_BGM_OCA_SERENADE + 0x2, // NA_BGM_OCA_REQUIEM + 0x2, // NA_BGM_OCA_NOCTURNE + 0x88, // NA_BGM_MINI_BOSS + 0x2, // NA_BGM_SMALL_ITEM_GET + 0, // NA_BGM_TEMPLE_OF_TIME + 0x2, // NA_BGM_EVENT_CLEAR + 0x11, // NA_BGM_KOKIRI + 0x2, // NA_BGM_OCA_FAIRY_GET + 0x1, // NA_BGM_SARIA_THEME + 0x1, // NA_BGM_SPIRIT_TEMPLE + 0, // NA_BGM_HORSE + 0, // NA_BGM_HORSE_GOAL + 0, // NA_BGM_INGO + 0x2, // NA_BGM_MEDALLION_GET + 0x2, // NA_BGM_OCA_SARIA + 0x2, // NA_BGM_OCA_EPONA + 0x2, // NA_BGM_OCA_ZELDA + 0x2, // NA_BGM_OCA_SUNS + 0x2, // NA_BGM_OCA_TIME + 0x2, // NA_BGM_OCA_STORM + 0, // NA_BGM_NAVI_OPENING + 0, // NA_BGM_DEKU_TREE_CS + 0, // NA_BGM_WINDMILL + 0, // NA_BGM_HYRULE_CS + 0x20, // NA_BGM_MINI_GAME + 0, // NA_BGM_SHEIK + 0x10, // NA_BGM_ZORA_DOMAIN + 0x2, // NA_BGM_APPEAR + 0, // NA_BGM_ADULT_LINK + 0, // NA_BGM_MASTER_SWORD + 0x4, // NA_BGM_INTRO_GANON + 0x20, // NA_BGM_SHOP + 0x40, // NA_BGM_CHAMBER_OF_SAGES + 0x40, // NA_BGM_FILE_SELECT + 0x1, // NA_BGM_ICE_CAVERN + 0x2, // NA_BGM_DOOR_OF_TIME + 0x2, // NA_BGM_OWL + 0x1, // NA_BGM_SHADOW_TEMPLE + 0x1, // NA_BGM_WATER_TEMPLE + 0x2, // NA_BGM_BRIDGE_TO_GANONS + 0, // NA_BGM_OCARINA_OF_TIME + 0x11, // NA_BGM_GERUDO_VALLEY + 0, // NA_BGM_POTION_SHOP + 0, // NA_BGM_KOTAKE_KOUME + 0x80, // NA_BGM_ESCAPE + 0, // NA_BGM_UNDERGROUND + 0x80, // NA_BGM_GANON_BATTLE_1 + 0x80, // NA_BGM_GANON_BATTLE_2 + 0, // NA_BGM_END_DEMO + 0, // NA_BGM_STAFF_1 + 0, // NA_BGM_STAFF_2 + 0, // NA_BGM_STAFF_3 + 0, // NA_BGM_STAFF_4 + 0, // NA_BGM_FIRE_BOSS + 0x8, // NA_BGM_TIMED_MINI_GAME + 0, // NA_BGM_VARIOUS_SFX +}; + +s8 sSpecReverbs[20] = { 0, 0, 0, 0, 0, 0, 0, 40, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +NatureAmbienceDataIO sNatureAmbienceDataIO[20] = { + // NATURE_ID_GENERAL_NIGHT + { + 0xC0FF, // PlayerIO Data + 0xC0FE, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CROWS_CAWS), + NATURE_IO_CRITTER_0_BEND_PITCH(64), + NATURE_IO_CRITTER_0_NUM_LAYERS(0), + NATURE_IO_CRITTER_0_PORT5(32), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_1_BEND_PITCH(0), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(16), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_SMALL_BIRD_CHIRPS), + NATURE_IO_CRITTER_2_BEND_PITCH(112), + NATURE_IO_CRITTER_2_NUM_LAYERS(1), + NATURE_IO_CRITTER_2_PORT5(48), + + // Channel 4 + NATURE_IO_CRITTER_3_TYPE(NATURE_CRITTER_HAWK_SCREECH), + NATURE_IO_CRITTER_3_BEND_PITCH(127), + NATURE_IO_CRITTER_3_NUM_LAYERS(0), + NATURE_IO_CRITTER_3_PORT5(16), + + // Channel 5 + NATURE_IO_CRITTER_4_TYPE(NATURE_CRITTER_BIRD_CHIRP_1), + NATURE_IO_CRITTER_4_BEND_PITCH(127), + NATURE_IO_CRITTER_4_NUM_LAYERS(1), + NATURE_IO_CRITTER_4_PORT5(16), + + // Channel 6 + NATURE_IO_CRITTER_5_TYPE(NATURE_CRITTER_TAP), + NATURE_IO_CRITTER_5_BEND_PITCH(127), + NATURE_IO_CRITTER_5_NUM_LAYERS(3), + NATURE_IO_CRITTER_5_PORT5(16), + + // Channel 7 + NATURE_IO_CRITTER_6_TYPE(NATURE_CRITTER_CUCCO_CROWS), + NATURE_IO_CRITTER_6_BEND_PITCH(127), + NATURE_IO_CRITTER_6_NUM_LAYERS(1), + NATURE_IO_CRITTER_6_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_MARKET_ENTRANCE + { + 0xC0FB, // PlayerIO Data + 0xC0FA, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_2_BEND_PITCH(112), + NATURE_IO_CRITTER_2_NUM_LAYERS(1), + NATURE_IO_CRITTER_2_PORT5(48), + + // Channel 4 + NATURE_IO_CRITTER_3_TYPE(NATURE_CRITTER_HAWK_SCREECH), + NATURE_IO_CRITTER_3_BEND_PITCH(127), + NATURE_IO_CRITTER_3_NUM_LAYERS(0), + NATURE_IO_CRITTER_3_PORT5(16), + + // Channel 5 + NATURE_IO_CRITTER_4_TYPE(NATURE_CRITTER_BIRD_CHIRP_1), + NATURE_IO_CRITTER_4_BEND_PITCH(127), + NATURE_IO_CRITTER_4_NUM_LAYERS(1), + NATURE_IO_CRITTER_4_PORT5(16), + + // Channel 6 + NATURE_IO_CRITTER_5_TYPE(NATURE_CRITTER_TAP), + NATURE_IO_CRITTER_5_BEND_PITCH(127), + NATURE_IO_CRITTER_5_NUM_LAYERS(3), + NATURE_IO_CRITTER_5_PORT5(16), + + // Channel 7 + NATURE_IO_CRITTER_6_TYPE(NATURE_CRITTER_CUCCO_CROWS), + NATURE_IO_CRITTER_6_BEND_PITCH(127), + NATURE_IO_CRITTER_6_NUM_LAYERS(1), + NATURE_IO_CRITTER_6_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_KAKARIKO_REGION + { + 0xC001, // PlayerIO Data + 0x4000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_1_BEND_PITCH(48), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(32), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_MARKET_RUINS + { + 0xC005, // PlayerIO Data + 0x4000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_HOWLING_WIND), + NATURE_IO_STREAM_0_PORT3(32), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_1_BEND_PITCH(48), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(32), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_KOKIRI_REGION + { + 0xC01F, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(47), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_OWL_HOOT), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_CAWING_BIRD), + NATURE_IO_CRITTER_1_BEND_PITCH(0), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(32), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_HAWK_SCREECH), + NATURE_IO_CRITTER_2_BEND_PITCH(0), + NATURE_IO_CRITTER_2_NUM_LAYERS(0), + NATURE_IO_CRITTER_2_PORT5(44), + + // Channel 4 + NATURE_IO_CRITTER_3_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_3_BEND_PITCH(63), + NATURE_IO_CRITTER_3_NUM_LAYERS(1), + NATURE_IO_CRITTER_3_PORT5(44), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_MARKET_NIGHT + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_06 + { + 0xC0FB, // PlayerIO Data + 0xC0FA, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_2_BEND_PITCH(112), + NATURE_IO_CRITTER_2_NUM_LAYERS(1), + NATURE_IO_CRITTER_2_PORT5(48), + + // Channel 4 + NATURE_IO_CRITTER_3_TYPE(NATURE_CRITTER_HAWK_SCREECH), + NATURE_IO_CRITTER_3_BEND_PITCH(127), + NATURE_IO_CRITTER_3_NUM_LAYERS(0), + NATURE_IO_CRITTER_3_PORT5(16), + + // Channel 5 + NATURE_IO_CRITTER_4_TYPE(NATURE_CRITTER_BIRD_CHIRP_1), + NATURE_IO_CRITTER_4_BEND_PITCH(127), + NATURE_IO_CRITTER_4_NUM_LAYERS(1), + NATURE_IO_CRITTER_4_PORT5(16), + + // Channel 6 + NATURE_IO_CRITTER_5_TYPE(NATURE_CRITTER_TAP), + NATURE_IO_CRITTER_5_BEND_PITCH(127), + NATURE_IO_CRITTER_5_NUM_LAYERS(3), + NATURE_IO_CRITTER_5_PORT5(16), + + // Channel 7 + NATURE_IO_CRITTER_6_TYPE(NATURE_CRITTER_CUCCO_CROWS), + NATURE_IO_CRITTER_6_BEND_PITCH(127), + NATURE_IO_CRITTER_6_NUM_LAYERS(1), + NATURE_IO_CRITTER_6_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_GANONS_LAIR + { + 0x8001, // PlayerIO Data + 0x0, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_HOWLING_WIND), + NATURE_IO_STREAM_0_PORT3(32), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_08 + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_09 + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_WASTELAND + { + 0xC001, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_SCREECHING_WIND), + NATURE_IO_STREAM_0_PORT3(0), + NATURE_IO_STREAM_0_PORT4(0), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_COLOSSUS + { + 0xC02F, // PlayerIO Data + 0xC02E, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_SCREECHING_WIND), + NATURE_IO_STREAM_0_PORT3(0), + NATURE_IO_STREAM_0_PORT4(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_SMALL_BIRD_CHIRPS), + NATURE_IO_CRITTER_0_BEND_PITCH(64), + NATURE_IO_CRITTER_0_NUM_LAYERS(0), + NATURE_IO_CRITTER_0_PORT5(32), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_BIRD_CALL), + NATURE_IO_CRITTER_1_BEND_PITCH(112), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(48), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_HAWK_SCREECH), + NATURE_IO_CRITTER_2_BEND_PITCH(127), + NATURE_IO_CRITTER_2_NUM_LAYERS(0), + NATURE_IO_CRITTER_2_PORT5(16), + + // Channel 5 + NATURE_IO_CRITTER_4_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_4_BEND_PITCH(127), + NATURE_IO_CRITTER_4_NUM_LAYERS(0), + NATURE_IO_CRITTER_4_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_DEATH_MOUNTAIN_TRAIL + { + 0xC07F, // PlayerIO Data + 0xC07E, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + NATURE_IO_STREAM_0_PORT4(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_SMALL_BIRD_CHIRPS), + NATURE_IO_CRITTER_0_BEND_PITCH(64), + NATURE_IO_CRITTER_0_NUM_LAYERS(0), + NATURE_IO_CRITTER_0_PORT5(32), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_1_BEND_PITCH(112), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(48), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_BIRD_SONG), + NATURE_IO_CRITTER_2_BEND_PITCH(127), + NATURE_IO_CRITTER_2_NUM_LAYERS(0), + NATURE_IO_CRITTER_2_PORT5(16), + + // Channel 4 + NATURE_IO_CRITTER_3_TYPE(NATURE_CRITTER_LOUD_CHIRPING), + NATURE_IO_CRITTER_3_BEND_PITCH(0), + NATURE_IO_CRITTER_3_NUM_LAYERS(0), + NATURE_IO_CRITTER_3_PORT5(16), + + // Channel 5 + NATURE_IO_CRITTER_4_TYPE(NATURE_CRITTER_BIRD_CHIRP_1), + NATURE_IO_CRITTER_4_BEND_PITCH(0), + NATURE_IO_CRITTER_4_NUM_LAYERS(0), + NATURE_IO_CRITTER_4_PORT5(16), + + // Channel 6 + NATURE_IO_CRITTER_5_TYPE(NATURE_CRITTER_TAP), + NATURE_IO_CRITTER_5_BEND_PITCH(0), + NATURE_IO_CRITTER_5_NUM_LAYERS(0), + NATURE_IO_CRITTER_5_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_0D + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_0E + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_0F + { + 0xC01F, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_BIRD_CHIRP_1), + NATURE_IO_CRITTER_0_BEND_PITCH(80), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(8), + + // Channel 2 + NATURE_IO_CRITTER_1_TYPE(NATURE_CRITTER_SMALL_BIRD_CHIRPS), + NATURE_IO_CRITTER_1_BEND_PITCH(80), + NATURE_IO_CRITTER_1_NUM_LAYERS(1), + NATURE_IO_CRITTER_1_PORT5(48), + + // Channel 3 + NATURE_IO_CRITTER_2_TYPE(NATURE_CRITTER_LOUD_CHIRPING), + NATURE_IO_CRITTER_2_BEND_PITCH(0), + NATURE_IO_CRITTER_2_NUM_LAYERS(0), + NATURE_IO_CRITTER_2_PORT5(0), + + // Channel 4 + NATURE_IO_CRITTER_3_TYPE(NATURE_CRITTER_BIRD_SCREECH), + NATURE_IO_CRITTER_3_BEND_PITCH(96), + NATURE_IO_CRITTER_3_NUM_LAYERS(0), + NATURE_IO_CRITTER_3_PORT5(32), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_10 + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_11 + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_12 + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, + + // NATURE_ID_NONE + // While there is data for this natureAmbienceId, it is identical to previous entries + // and the game treats it as no nature ambience + { + 0xC003, // PlayerIO Data + 0xC000, // Channel Mask + { + // Channel 0 + NATURE_IO_STREAM_0_TYPE(NATURE_STREAM_RUSHING_WATER), + NATURE_IO_STREAM_0_PORT3(0), + + // Channel 1 + NATURE_IO_CRITTER_0_TYPE(NATURE_CRITTER_CRICKETS), + NATURE_IO_CRITTER_0_BEND_PITCH(0), + NATURE_IO_CRITTER_0_NUM_LAYERS(1), + NATURE_IO_CRITTER_0_PORT5(16), + + // End + NATURE_IO_ENTRIES_END, + }, + }, +}; + +u32 sOcarinaAllowedBtnMask = 0x800F; +s32 sOcarinaABtnMap = 0x8000; +s32 sOcarinaCUPBtnMap = 8; +s32 sOcarinaCDownBtnMap = 4; +u8 sOcarinaInpEnabled = 0; +s8 D_80130F10 = 0; // "OCA", ocarina active? +u8 sCurOcarinaBtnVal = 0xFF; +u8 sPrevOcarinaNoteVal = 0; +u8 sCurOcarinaBtnIdx = 0; // note index? +u8 sLearnSongLastBtn = 0; +f32 D_80130F24 = 1.0f; +f32 D_80130F28 = 87.0f / 127.0f; +s8 D_80130F2C = 0; // pitch? +s8 D_80130F30 = 0x57; +s8 D_80130F34 = 0; +u8 sPlaybackState = 0; // 80130F38 +u32 D_80130F3C = 0; // "SEQ" +u32 sNotePlaybackTimer = 0; +u16 sPlaybackNotePos = 0; +u16 sStaffPlaybackPos = 0; +u16 D_80130F4C = 0; +u8 sDisplayedNoteValue = 0xFF; // Note to display on screen? +u8 sNotePlaybackVolume = 0; +u8 sNotePlaybackVibrato = 0; +s8 sNotePlaybackTone = 0; +f32 sNormalizedNotePlaybackTone = 1.0f; +f32 sNormalizedNotePlaybackVolume = 1.0f; +s32 D_80130F68 = 0; +u8 sOcarinaNoteValues[5] = { 2, 5, 9, 11, 14 }; +u8 sOcaMinigameAppendPos = 0; +u8 sOcaMinigameEndPos = 0; +u8 sOcaMinigameNoteCnts[] = { 5, 6, 8 }; + +OcarinaNote sOcarinaSongs[OCARINA_SONG_MAX][20] = { + // Minuet + { + { 2, 0, 18, 86, 0, 0, 0 }, + { 14, 0, 18, 92, 0, 0, 0 }, + { 11, 0, 72, 86, 0, 0, 0 }, + { 9, 0, 18, 80, 0, 0, 0 }, + { 11, 0, 18, 88, 0, 0, 0 }, + { 9, 0, 144, 86, 0, 0, 0 }, + { 0xFF, 0, 0, 86, 0, 0, 0 }, + }, + + // Bolero + { + { 5, 0, 15, 80, 0, 0, 0 }, + { 2, 0, 15, 72, 0, 0, 0 }, + { 5, 0, 15, 84, 0, 0, 0 }, + { 2, 0, 15, 76, 0, 0, 0 }, + { 9, 0, 15, 84, 0, 0, 0 }, + { 5, 0, 15, 74, 0, 0, 0 }, + { 9, 0, 15, 78, 0, 0, 0 }, + { 5, 0, 135, 66, 0, 0, 0 }, + { 0xFF, 0, 0, 66, 0, 0, 0 }, + }, + + // Serenade + { + { 2, 0, 36, 60, 0, 0, 0 }, + { 5, 0, 36, 78, 0, 0, 0 }, + { 9, 0, 33, 82, 0, 0, 0 }, + { 0xFF, 0, 3, 82, 0, 0, 0 }, + { 9, 0, 36, 84, 0, 0, 0 }, + { 11, 0, 144, 90, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Requiem + { + { 2, 0, 45, 88, 0, 0, 0 }, + { 5, 0, 23, 86, 0, 0, 0 }, + { 2, 0, 22, 84, 0, 0, 0 }, + { 9, 0, 45, 86, 0, 0, 0 }, + { 5, 0, 45, 94, 0, 0, 0 }, + { 2, 0, 180, 94, 0, 0, 0 }, + { 0xFF, 0, 0, 94, 0, 0, 0 }, + }, + + // Nocturne + { + { 11, 0, 36, 88, 0, 0, 0 }, + { 9, 0, 33, 84, 0, 0, 0 }, + { 0xFF, 0, 3, 84, 0, 0, 0 }, + { 9, 0, 18, 82, 0, 0, 0 }, + { 2, 0, 18, 60, 0, 0, 0 }, + { 11, 0, 18, 90, 0, 0, 0 }, + { 9, 0, 18, 88, 0, 0, 0 }, + { 5, 0, 144, 96, 0, 0, 0 }, + { 0xFF, 0, 0, 96, 0, 0, 0 }, + }, + + // Prelude + { + { 14, 0, 15, 84, 0, 0, 0 }, + { 9, 0, 45, 88, 0, 0, 0 }, + { 14, 0, 15, 88, 0, 0, 0 }, + { 9, 0, 15, 82, 0, 0, 0 }, + { 11, 0, 15, 86, 0, 0, 0 }, + { 14, 0, 60, 90, 0, 0, 0 }, + { 0xFF, 0, 75, 90, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Sarias + { + { 5, 0, 17, 84, 0, 0, 0 }, + { 9, 0, 17, 88, 0, 0, 0 }, + { 11, 0, 34, 80, 0, 0, 0 }, + { 5, 0, 17, 84, 0, 0, 0 }, + { 9, 0, 17, 88, 0, 0, 0 }, + { 11, 0, 136, 80, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Epona + { + { 14, 0, 18, 84, 0, 0, 0 }, + { 11, 0, 18, 88, 0, 0, 0 }, + { 9, 0, 72, 80, 0, 0, 0 }, + { 14, 0, 18, 84, 0, 0, 0 }, + { 11, 0, 18, 88, 0, 0, 0 }, + { 9, 0, 144, 80, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Lullaby + { + { 11, 0, 51, 84, 0, 0, 0 }, + { 14, 0, 25, 88, 0, 0, 0 }, + { 9, 0, 78, 80, 0, 0, 0 }, + { 11, 0, 51, 84, 0, 0, 0 }, + { 14, 0, 25, 88, 0, 0, 0 }, + { 9, 0, 100, 80, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Suns + { + { 9, 0, 12, 84, 0, 0, 0 }, + { 5, 0, 13, 88, 0, 0, 0 }, + { 14, 0, 29, 80, 2, 0, 0 }, + { 0xFF, 0, 9, 84, 0, 0, 0 }, + { 9, 0, 12, 84, 0, 0, 0 }, + { 5, 0, 13, 88, 0, 0, 0 }, + { 14, 0, 120, 80, 3, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Song of Time + { + { 9, 0, 32, 84, 0, 0, 0 }, + { 2, 0, 65, 88, 0, 0, 0 }, + { 5, 0, 33, 80, 0, 0, 0 }, + { 9, 0, 32, 84, 0, 0, 0 }, + { 2, 0, 65, 88, 0, 0, 0 }, + { 5, 0, 99, 80, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Storms + { + { 2, 0, 11, 84, 0, 0, 0 }, + { 5, 0, 11, 88, 0, 0, 0 }, + { 14, 0, 45, 80, 0, 0, 0 }, + { 2, 0, 11, 84, 0, 0, 0 }, + { 5, 0, 11, 88, 0, 0, 0 }, + { 14, 0, 90, 80, 0, 0, 0 }, + { 0xFF, 0, 0, 90, 0, 0, 0 }, + }, + + // Scarecrow + { + { 2, 0, 3, 0, 0, 0, 0 }, + { 0xFF, 0, 0, 255, 0, 0, 0 }, + }, + + // Lost Woods Memory Game + { + { 2, 0, 3, 0, 0, 0, 0 }, + { 0xFF, 0, 0, 0, 0, 0, 0 }, + }, +}; + +OcarinaNote* sPlaybackSong = sOcarinaSongs[0]; +u8 sFrogsSongNotes[14] = { + OCARINA_NOTE_A, OCARINA_NOTE_C_LEFT, OCARINA_NOTE_C_RIGHT, OCARINA_NOTE_C_DOWN, OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, OCARINA_NOTE_C_DOWN, OCARINA_NOTE_A, OCARINA_NOTE_C_DOWN, OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, OCARINA_NOTE_C_RIGHT, OCARINA_NOTE_C_LEFT, OCARINA_NOTE_A, +}; +u8* gFrogsSongPtr = sFrogsSongNotes; +u8 sRecordingState = 0; +u8 sRecordSongPos = 0; +u32 D_80131860 = 0; +u8 D_80131864 = 0; +u8 D_80131868 = 0; +u8 D_8013186C = 0; +s8 D_80131870 = 0; +u8 D_80131874 = 0; +u8 D_80131878 = 0; +u8 D_8013187C = 0; +u8 D_80131880 = 0; + +OcarinaNote sPierresSong[108] = { + { 0xFF, 0, 0, 0, 0, 0, 0 }, + { 0xFF, 0, 0, 0, 0, 0, 0 }, +}; +OcarinaNote* gScarecrowCustomSongPtr = sPierresSong; + +u8* gScarecrowSpawnSongPtr = (u8*)&sOcarinaSongs[OCARINA_SONG_SCARECROW]; +OcarinaNote* D_80131BEC = sOcarinaSongs[OCARINA_SONG_MEMORY_GAME]; +u8 sNoteValueIndexMap[16] = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 5, 3, 3, 4, 4, 4 }; + +OcarinaSongInfo gOcarinaSongNotes[OCARINA_SONG_MAX] = { + // Minuet + { 6, + { + OCARINA_NOTE_A, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, + } }, + // Bolero + { 8, + { + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_A, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_DOWN, + } }, + // Serenade + { 5, + { + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_LEFT, + } }, + // Requiem + { 6, + { + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_A, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_A, + } }, + // Nocturne + { 7, + { + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_A, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_DOWN, + } }, + // Prelude + { 6, + { + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_UP, + } }, + // Sarias + { 6, + { + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_LEFT, + } }, + // Epona + { 6, + { + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_RIGHT, + } }, + // Lullaby + { 6, + { + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_LEFT, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_RIGHT, + } }, + // Suns + { 6, + { + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_UP, + } }, + // Song of Time + { 6, + { + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_RIGHT, + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + } }, + // Storms + { 6, + { + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_UP, + OCARINA_NOTE_A, + OCARINA_NOTE_C_DOWN, + OCARINA_NOTE_C_UP, + } }, + // Scarecrow + { 8, { 0, 0, 0, 0, 0, 0, 0, 0 } }, + // Lost Woods Memory Game + { 0, { 0, 0, 0, 0, 0, 0, 0, 0 } }, +}; +// clang-format on + +/** + * BSS + */ +u32 sAudioUpdateStartTime; // 8016B7A0 +u32 sAudioUpdateEndTime; +f32 D_8016B7A8; +f32 D_8016B7AC; +f32 D_8016B7B0; +f32 D_8016B7B4; +FreqLerp sRiverFreqScaleLerp; +FreqLerp sWaterfallFreqScaleLerp; +f32 D_8016B7D8; +s8 D_8016B7DC; +f32 D_8016B7E0; +u16 D_8016B7E4; +struct { + s8 str[5]; + u16 num; +} sAudioScrPrtBuf[SCROLL_PRINT_BUF_SIZE]; +u8 D_8016B8B0; +u8 D_8016B8B1; +u8 D_8016B8B2; +u8 D_8016B8B3; +u8 sAudioGanonDistVol; +SfxPlayerState sSfxChannelState[0x10]; + +char sBinToStrBuf[0x20]; +u8 D_8016B9D8; +u8 sAudioSpecPeakNumNotes[0x12]; +u8 D_8016B9F2; +u8 D_8016B9F3; +u8 D_8016B9F4; +u16 D_8016B9F6; + +OcarinaStaff sPlayingStaff; +OcarinaStaff sDisplayedStaff; +OcarinaStaff sRecordingStaff; +u32 D_8016BA04; +typedef struct { + s8 x; + s8 y; +} OcarinaStick; +OcarinaStick sCurOcaStick; +u32 sCurOcarinaBtnPress; +u32 D_8016BA10; +u32 sPrevOcarinaBtnPress; +s32 D_8016BA18; +s32 D_8016BA1C; +u8 sCurOcarinaSong[8]; +u8 sOcarinaSongAppendPos; +u8 sOcarinaHasStartedSong; +u8 sOcarinaSongNoteStartIdx; +u8 sOcarinaSongCnt; +u16 sOcarinaAvailSongs; +u8 sStaffPlayingPos; +u16 sLearnSongPos[0x10]; +u16 D_8016BA50[0x10]; +u16 D_8016BA70[0x10]; +u8 sLearnSongExpectedNote[0x10]; +OcarinaNote D_8016BAA0; +u8 sAudioHasMalonBgm; +f32 sAudioMalonBgmDist; + +// Start debug bss +u32 sDebugPadHold; +u32 sDebugPadBtnLast; +u32 sDebugPadPress; +s32 sAudioUpdateTaskStart; +s32 sAudioUpdateTaskEnd; + +void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode); + +void Audio_StepFreqLerp(FreqLerp* lerp); +void func_800F56A8(void); +void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId); +s32 Audio_SetGanonDistVol(u8 targetVol); + +void func_800EC960(u8 custom) { + if (!custom) { + osSyncPrintf("AUDIO : Ocarina Control Assign Normal\n"); + sOcarinaAllowedBtnMask = (BTN_A | BTN_CUP | BTN_CDOWN | BTN_CLEFT | BTN_CRIGHT); + sOcarinaABtnMap = BTN_A; + sOcarinaCUPBtnMap = BTN_CUP; + sOcarinaCDownBtnMap = BTN_CDOWN; + } else { + osSyncPrintf("AUDIO : Ocarina Control Assign Custom\n"); + sOcarinaAllowedBtnMask = (BTN_A | BTN_B | BTN_CDOWN | BTN_CLEFT | BTN_CRIGHT); + sOcarinaABtnMap = BTN_B; + sOcarinaCUPBtnMap = BTN_CDOWN; + sOcarinaCDownBtnMap = BTN_A; + } +} + +void Audio_GetOcaInput(void) { + Input inputs[4]; + Input* input = &inputs[0]; + u32 sp18; + + sp18 = sCurOcarinaBtnPress; + PadMgr_RequestPadData(&gPadMgr, inputs, 0); + sCurOcarinaBtnPress = input->cur.button; + sPrevOcarinaBtnPress = sp18; + sCurOcaStick.x = input->rel.stick_x; + sCurOcaStick.y = input->rel.stick_y; +} + +f32 Audio_OcaAdjStick(s8 inp) { + s8 inpAdj; + f32 ret; + + if (inp > 0x40) { + inpAdj = 127; + } else if (inp < -0x40) { + inpAdj = -128; + } else if (inp >= 0) { + inpAdj = (inp * 127) / 64; + } else { + inpAdj = (inp * 128) / 64; + } + ret = gBendPitchTwoSemitonesFrequencies[inpAdj + 128]; + return ret; +} + +u8 Audio_OcaGetPlayingState(void) { + u8 ret; + + if (D_80131878 != 0) { + ret = D_80131878 - 1; + D_80131878 = 0; + } else if (D_80130F3C != 0) { + ret = 0xFE; + } else { + ret = 0xFF; + } + + return ret; +} + +u8 Audio_OcaMapNoteValue(u8 arg0) { + u8 temp_v1; + + temp_v1 = sNoteValueIndexMap[arg0 & 0x3F]; + if (temp_v1 == 5) { + if (arg0 & 0x80) { + return 2; + } + return 3; + } + return temp_v1; +} + +void func_800ECB7C(u8 songIdx) { + u8 savedSongIdx; + u8 scarecrowSongIdx; + u8 noteIdx; + + savedSongIdx = 0; + scarecrowSongIdx = 0; + while (savedSongIdx < 8 && scarecrowSongIdx < 0x10) { + noteIdx = sOcarinaSongs[songIdx][scarecrowSongIdx++].noteIdx; + if (noteIdx != 0xFF) { + gOcarinaSongNotes[OCARINA_SONG_SCARECROW].notesIdx[savedSongIdx++] = sNoteValueIndexMap[noteIdx]; + } + } +} + +// start ocarina. +void func_800ECC04(u16 flg) { + u8 i; + + if ((sOcarinaSongs[OCARINA_SONG_SCARECROW][1].volume != 0xFF) && ((flg & 0xFFF) == 0xFFF)) { + flg |= 0x1000; + } + + if ((flg == 0xCFFF) && (sOcarinaSongs[OCARINA_SONG_SCARECROW][1].volume != 0xFF)) { + flg = 0xDFFF; + } + + if ((flg == 0xFFF) && (sOcarinaSongs[OCARINA_SONG_SCARECROW][1].volume != 0xFF)) { + flg = 0x1FFF; + } + + if (flg != 0xFFFF) { + D_80130F3C = 0x80000000 + (u32)flg; + sOcarinaSongNoteStartIdx = 0; + sOcarinaSongCnt = 0xE; + if (flg != 0xA000) { + sOcarinaSongCnt--; + } + sOcarinaAvailSongs = flg & 0x3FFF; + D_8013187C = 8; + sOcarinaHasStartedSong = 0; + D_80131878 = 0; + sStaffPlayingPos = 0; + sPlayingStaff.state = Audio_OcaGetPlayingState(); + sOcarinaInpEnabled = 1; + D_80130F4C = 0; + for (i = 0; i < 0xE; i++) { + sLearnSongPos[i] = 0; + D_8016BA50[i] = 0; + D_8016BA70[i] = 0; + sLearnSongExpectedNote[i] = 0; + } + + if (flg & 0x8000) { + D_8013187C = 0; + } + + if (flg & 0x4000) { + sOcarinaSongAppendPos = 0; + } + + if (flg & 0xD000) { + func_800ECB7C(OCARINA_SONG_SCARECROW); + } + } else { + D_80130F3C = 0; + sOcarinaInpEnabled = 0; + } +} + +void func_800ECDBC(void) { + if (sCurOcarinaBtnVal != 0xFF && sOcarinaHasStartedSong == 0) { + sOcarinaHasStartedSong = 1; + sLearnSongLastBtn = 0xFF; + } +} + +void func_800ECDF8(void) { + u16 sh; + u16 pad; + u8 inputChanged = 0; + u16 pad2; + s8 sp57 = 0; + u8 i; + OcarinaNote* prevNote; + OcarinaNote* note; + + func_800ECDBC(); + + if (sOcarinaHasStartedSong) { + if ((D_80130F2C < 0 ? -D_80130F2C : D_80130F2C) >= 0x15) { + D_80130F3C = 0; + return; + } + + // clang-format off + if (sPrevOcarinaNoteVal == sCurOcarinaBtnVal || sCurOcarinaBtnVal == 0xFF) { inputChanged = 1; } + // clang-format on + + for (i = sOcarinaSongNoteStartIdx; i < sOcarinaSongCnt; i++) { + sh = 1 << i; + if (sOcarinaAvailSongs & sh) { + D_8016BA50[i] = D_8016BA70[i] + 0x12; + if (inputChanged) { + // (pointless if check, this is always true) + if ((D_8016BA50[i] >= D_8016BA70[i] - 0x12) && (D_8016BA50[i] >= D_8016BA70[i] + 0x12) && + (sOcarinaSongs[i][sLearnSongPos[i]].unk_02 == 0) && + (sLearnSongLastBtn == sLearnSongExpectedNote[i])) { + D_80131878 = i + 1; + sOcarinaInpEnabled = 0; + D_80130F3C = 0; + } + } else if (D_8016BA50[i] >= (D_8016BA70[i] - 0x12)) { + if (sLearnSongLastBtn != 0xFF) { + if (sLearnSongLastBtn == sLearnSongExpectedNote[i]) { + if (i == 12) { + D_8016BA50[i] = 0; + } + } else { + sOcarinaAvailSongs ^= sh; + } + } + + prevNote = &sOcarinaSongs[i][sLearnSongPos[i]]; + note = &sOcarinaSongs[i][++sLearnSongPos[i]]; + D_8016BA70[i] = prevNote->unk_02; + sLearnSongExpectedNote[i] = prevNote->noteIdx; + + if (sCurOcarinaBtnVal != sLearnSongExpectedNote[i]) { + sOcarinaAvailSongs ^= sh; + } + while (prevNote->noteIdx == note->noteIdx || + (note->noteIdx == OCARINA_NOTE_INVALID && note->unk_02 != 0)) { + D_8016BA70[i] += note->unk_02; + prevNote = &sOcarinaSongs[i][sLearnSongPos[i]]; + note = &sOcarinaSongs[i][sLearnSongPos[i] + 1]; + sLearnSongPos[i]++; + } + } else if (D_8016BA50[i] < 0xA) { + sp57 = -1; + D_8016BA50[i] = 0; + sLearnSongLastBtn = sCurOcarinaBtnVal; + } else { + sOcarinaAvailSongs ^= sh; + } + } + + if (sOcarinaAvailSongs == 0 && sStaffPlayingPos >= D_8013187C) { + sOcarinaInpEnabled = 0; + if (CHECK_BTN_ANY(D_80130F3C, BTN_B) && sCurOcarinaBtnVal == sOcarinaSongs[i][0].noteIdx) { + D_80130F4C = D_80130F3C; + } + D_80130F3C = 0; + return; + } + } + + if (!inputChanged) { + sLearnSongLastBtn = sCurOcarinaBtnVal; + sStaffPlayingPos += sp57 + 1; + } + } +} + +void func_800ED200(void) { + u32 temp_v0; + u8 i; + u8 j; + u8 k; + + if (CHECK_BTN_ANY(sCurOcarinaBtnPress, BTN_L) && CHECK_BTN_ANY(sCurOcarinaBtnPress, sOcarinaAllowedBtnMask)) { + func_800ECC04((u16)D_80130F3C); + return; + } + + func_800ECDBC(); + + if (sOcarinaHasStartedSong) { + if ((sPrevOcarinaNoteVal != sCurOcarinaBtnVal) && (sCurOcarinaBtnVal != 0xFF)) { + sStaffPlayingPos++; + if (sStaffPlayingPos >= 9) { + sStaffPlayingPos = 1; + } + + if (sOcarinaSongAppendPos == 8) { + for (i = 0; i < 7; i++) { + sCurOcarinaSong[i] = sCurOcarinaSong[i + 1]; + } + } else { + sOcarinaSongAppendPos++; + } + + if ((D_80130F2C < 0 ? -D_80130F2C : D_80130F2C) >= 0x15) { + sCurOcarinaSong[sOcarinaSongAppendPos - 1] = 0xFF; + } else { + sCurOcarinaSong[sOcarinaSongAppendPos - 1] = sCurOcarinaBtnVal; + } + + for (i = sOcarinaSongNoteStartIdx; i < sOcarinaSongCnt; i++) { + if (sOcarinaAvailSongs & (u16)(1 << i)) { + for (j = 0, k = 0; + j < gOcarinaSongNotes[i].len && k == 0 && sOcarinaSongAppendPos >= gOcarinaSongNotes[i].len;) { + temp_v0 = sCurOcarinaSong[(sOcarinaSongAppendPos - gOcarinaSongNotes[i].len) + j]; + if (temp_v0 == sOcarinaNoteValues[gOcarinaSongNotes[i].notesIdx[j]]) { + j++; + } else { + k++; + } + } + + if (j == gOcarinaSongNotes[i].len) { + D_80131878 = i + 1; + sOcarinaInpEnabled = 0; + D_80130F3C = 0; + } + } + } + } + } +} + +void func_800ED458(s32 arg0) { + u32 phi_v1_2; + + if (D_80130F3C != 0 && D_80131880 != 0) { + D_80131880--; + return; + } + + if ((D_8016BA10 == 0) || + ((D_8016BA10 & sOcarinaAllowedBtnMask) != (sCurOcarinaBtnPress & sOcarinaAllowedBtnMask))) { + D_8016BA10 = 0; + if (1) {} + sCurOcarinaBtnVal = 0xFF; + sCurOcarinaBtnIdx = 0xFF; + phi_v1_2 = (sCurOcarinaBtnPress & sOcarinaAllowedBtnMask) & (sPrevOcarinaBtnPress & sOcarinaAllowedBtnMask); + if (!(D_8016BA18 & phi_v1_2) && (sCurOcarinaBtnPress != 0)) { + D_8016BA18 = sCurOcarinaBtnPress; + } else { + D_8016BA18 &= phi_v1_2; + } + + if (D_8016BA18 & sOcarinaABtnMap) { + osSyncPrintf("Presss NA_KEY_D4 %08x\n", sOcarinaABtnMap); + sCurOcarinaBtnVal = 2; + sCurOcarinaBtnIdx = 0; + } else if (D_8016BA18 & sOcarinaCDownBtnMap) { + osSyncPrintf("Presss NA_KEY_F4 %08x\n", sOcarinaCDownBtnMap); + sCurOcarinaBtnVal = 5; + sCurOcarinaBtnIdx = 1; + } else if (D_8016BA18 & 1) { + osSyncPrintf("Presss NA_KEY_A4 %08x\n", 1); + sCurOcarinaBtnVal = 9; + sCurOcarinaBtnIdx = 2; + } else if (D_8016BA18 & 2) { + osSyncPrintf("Presss NA_KEY_B4 %08x\n", 2); + sCurOcarinaBtnVal = 0xB; + sCurOcarinaBtnIdx = 3; + } else if (D_8016BA18 & sOcarinaCUPBtnMap) { + osSyncPrintf("Presss NA_KEY_D5 %08x\n", sOcarinaCUPBtnMap); + sCurOcarinaBtnVal = 0xE; + sCurOcarinaBtnIdx = 4; + } + + if (sCurOcarinaBtnVal != 0xFF && sCurOcarinaBtnPress & 0x10 && sRecordingState != 2) { + sCurOcarinaBtnIdx += 0x80; + sCurOcarinaBtnVal++; + } + + if ((sCurOcarinaBtnVal != 0xFF) && (sCurOcarinaBtnPress & 0x2000) && (sRecordingState != 2)) { + sCurOcarinaBtnIdx += 0x40; + sCurOcarinaBtnVal--; + } + + if (sRecordingState != 2) { + D_80130F2C = sCurOcaStick.y; + D_80130F24 = Audio_OcaAdjStick(D_80130F2C); + + D_80130F34 = (sCurOcaStick.x < 0 ? -sCurOcaStick.x : sCurOcaStick.x) >> 2; + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD06, D_80130F34); + } else { + D_80130F2C = 0; + D_80130F24 = 1.0f; + } + + if ((sCurOcarinaBtnVal != 0xFF) && (sPrevOcarinaNoteVal != sCurOcarinaBtnVal)) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD07, D_80130F10 - 1); + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD05, sCurOcarinaBtnVal); + Audio_PlaySoundGeneral(NA_SE_OC_OCARINA, &D_801333D4, 4, &D_80130F24, &D_80130F28, &D_801333E8); + } else if ((sPrevOcarinaNoteVal != 0xFF) && (sCurOcarinaBtnVal == 0xFF)) { + Audio_StopSfxById(NA_SE_OC_OCARINA); + } + } +} + +void func_800ED848(u8 inputEnabled) { + sOcarinaInpEnabled = inputEnabled; +} + +void Audio_OcaSetInstrument(u8 arg0) { + if (D_80130F10 == arg0) { + return; + } + + Audio_SeqCmd8(SEQ_PLAYER_SFX, 1, SFX_PLAYER_CHANNEL_OCARINA, arg0); + D_80130F10 = arg0; + if (arg0 == 0) { + sCurOcarinaBtnPress = 0; + sPrevOcarinaBtnPress = 0; + D_8016BA18 = 0; + D_8016BA10 = 0xFFFF; + func_800ED458(0); + Audio_StopSfxById(NA_SE_OC_OCARINA); + Audio_SetSoundBanksMute(0); + sPlaybackState = 0; + sStaffPlaybackPos = 0; + sOcarinaInpEnabled = 0; + D_80130F3C = 0; + Audio_ClearBGMMute(SFX_PLAYER_CHANNEL_OCARINA); + } else { + sCurOcarinaBtnPress = 0; + Audio_GetOcaInput(); + D_8016BA10 = sCurOcarinaBtnPress; + Audio_QueueSeqCmdMute(SFX_PLAYER_CHANNEL_OCARINA); + } +} + +void Audio_OcaSetSongPlayback(s8 songIdxPlusOne, s8 playbackState) { + if (songIdxPlusOne == 0) { + sPlaybackState = 0; + Audio_StopSfxById(NA_SE_OC_OCARINA); + return; + } + + if (songIdxPlusOne < 0xF) { + sPlaybackSong = sOcarinaSongs[songIdxPlusOne - 1]; + } else { + sPlaybackSong = sPierresSong; + } + + sPlaybackState = playbackState; + sNotePlaybackTimer = 0; + sDisplayedNoteValue = 0xFF; + sPlaybackNotePos = 0; + sStaffPlaybackPos = 0; + while (sPlaybackSong[sPlaybackNotePos].noteIdx == OCARINA_NOTE_INVALID) { + sPlaybackNotePos++; + } +} + +void Audio_OcaPlayback(void) { + u32 noteTimerStep; + u32 nextNoteTimerStep; + + if (sPlaybackState != 0) { + if (sStaffPlaybackPos == 0) { + noteTimerStep = 3; + } else { + noteTimerStep = D_8016BA04 - D_80130F68; + } + + if (noteTimerStep < sNotePlaybackTimer) { + sNotePlaybackTimer -= noteTimerStep; + } else { + nextNoteTimerStep = noteTimerStep - sNotePlaybackTimer; + sNotePlaybackTimer = 0; + } + + if (sNotePlaybackTimer == 0) { + + sNotePlaybackTimer = sPlaybackSong[sPlaybackNotePos].unk_02; + + if (sPlaybackNotePos == 1) { + sNotePlaybackTimer++; + } + + if (sNotePlaybackTimer == 0) { + sPlaybackState--; + if (sPlaybackState != 0) { + sPlaybackNotePos = 0; + sStaffPlaybackPos = 0; + sDisplayedNoteValue = 0xFF; + } else { + Audio_StopSfxById(NA_SE_OC_OCARINA); + } + return; + } else { + sNotePlaybackTimer -= nextNoteTimerStep; + } + + if (sNotePlaybackVolume != sPlaybackSong[sPlaybackNotePos].volume) { + sNotePlaybackVolume = sPlaybackSong[sPlaybackNotePos].volume; + sNormalizedNotePlaybackVolume = sNotePlaybackVolume / 127.0f; + } + + if (sNotePlaybackVibrato != sPlaybackSong[sPlaybackNotePos].vibrato) { + sNotePlaybackVibrato = sPlaybackSong[sPlaybackNotePos].vibrato; + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD06, sNotePlaybackVibrato); + } + + if (sNotePlaybackTone != sPlaybackSong[sPlaybackNotePos].tone) { + sNotePlaybackTone = sPlaybackSong[sPlaybackNotePos].tone; + sNormalizedNotePlaybackTone = Audio_OcaAdjStick(sNotePlaybackTone); + } + + if ((sPlaybackSong[sPlaybackNotePos].volume == sPlaybackSong[sPlaybackNotePos - 1].volume && + (sPlaybackSong[sPlaybackNotePos].vibrato == sPlaybackSong[sPlaybackNotePos - 1].vibrato) && + (sPlaybackSong[sPlaybackNotePos].tone == sPlaybackSong[sPlaybackNotePos - 1].tone))) { + sDisplayedNoteValue = 0xFE; + } + + if (sDisplayedNoteValue != sPlaybackSong[sPlaybackNotePos].noteIdx) { + u8 tmp = sPlaybackSong[sPlaybackNotePos].noteIdx; + + if (tmp == 0xA) { + sDisplayedNoteValue = tmp + sPlaybackSong[sPlaybackNotePos].semitone; + } else { + sDisplayedNoteValue = tmp; + } + + if (sDisplayedNoteValue != 0xFF) { + sStaffPlaybackPos++; + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD07, D_80130F10 - 1); + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | 0xD05, sDisplayedNoteValue & 0x3F); + Audio_PlaySoundGeneral(NA_SE_OC_OCARINA, &D_801333D4, 4, &sNormalizedNotePlaybackTone, + &sNormalizedNotePlaybackVolume, &D_801333E8); + } else { + Audio_StopSfxById(NA_SE_OC_OCARINA); + } + } + sPlaybackNotePos++; + } + } +} + +void func_800EDD68(u8 arg0) { + u16 i; + u16 i2; + u16 pad; + u8 lastNote; + OcarinaNote* note; + u8 j; + u8 k; + s32 t; + OcarinaNote* song; + + if (sRecordingState == 1) { + song = gScarecrowCustomSongPtr; + } else { + song = D_80131BEC; + } + song[sRecordSongPos].noteIdx = D_80131864; + song[sRecordSongPos].unk_02 = D_8016BA04 - D_80131860; + song[sRecordSongPos].volume = D_80131868; + song[sRecordSongPos].vibrato = D_8013186C; + song[sRecordSongPos].tone = D_80131870; + song[sRecordSongPos].semitone = D_80131874 & 0xC0; + D_80131864 = sCurOcarinaBtnVal; + D_80131868 = D_80130F30; + D_8013186C = D_80130F34; + D_80131870 = D_80130F2C; + D_80131874 = sCurOcarinaBtnIdx; + sRecordSongPos++; + + if ((sRecordSongPos != 107) && (arg0 == 0)) { + return; + } + + i = sRecordSongPos; + lastNote = 0xFF; + while (i != 0 && lastNote == 0xFF) { + i--; + lastNote = song[i].noteIdx; + } + + if (1) {} + + if (sRecordSongPos != (i + 1)) { + sRecordSongPos = i + 2; + song[sRecordSongPos - 1].unk_02 = 0; + } + + song[sRecordSongPos].unk_02 = 0; + + if (sRecordingState == 2) { + if (sStaffPlayingPos >= 8) { + for (i = 0; i < sRecordSongPos; i++) { + song[i] = song[i + 1]; + } + + func_800ECB7C(OCARINA_SONG_MEMORY_GAME); + + for (i = 0; i < OCARINA_SONG_SCARECROW; i++) { + for (j = 0; j < 9 - gOcarinaSongNotes[i].len; j++) { + for (k = 0; + k < gOcarinaSongNotes[i].len && k + j < 8 && + gOcarinaSongNotes[i].notesIdx[k] == gOcarinaSongNotes[OCARINA_SONG_SCARECROW].notesIdx[k + j]; + k++) { + ; + } + + if (k == gOcarinaSongNotes[i].len) { + sRecordingState = 0xFF; + sOcarinaSongs[OCARINA_SONG_SCARECROW][1].volume = 0xFF; + return; + } + } + } + + i = 1; + while (i < 8) { + if (gOcarinaSongNotes[OCARINA_SONG_SCARECROW].notesIdx[0] != + gOcarinaSongNotes[OCARINA_SONG_SCARECROW].notesIdx[i]) { + i = 9; + } else { + i++; + } + } + + if (i == 8) { + sRecordingState = 0xFF; + sOcarinaSongs[OCARINA_SONG_SCARECROW][1].volume = 0xFF; + return; + } + + for (i = 0; i < sRecordSongPos; i++) { + sOcarinaSongs[OCARINA_SONG_SCARECROW][i] = sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][i]; + } + + sOcarinaInpEnabled = 0; + } else { + sOcarinaSongs[OCARINA_SONG_SCARECROW][1].volume = 0xFF; + } + } + sRecordingState = 0; +} + +// start custom song? +/** + * recordingState = 1, start long scarecrows song + * recordingState = 0, end + * recordingState = 2, also scarecrows song + */ +void Audio_OcaSetRecordingState(u8 recordingState) { + if ((u32)recordingState == sRecordingState) { + return; + } + + if (recordingState != 0) { + D_80131860 = D_8016BA04; + D_80131864 = 0xFF; + D_80131868 = 0x57; + D_8013186C = 0; + D_80131870 = 0; + D_80131874 = 0; + sRecordSongPos = 0; + sOcarinaInpEnabled = 1; + sStaffPlayingPos = 0; + D_8016BAA0 = sPierresSong[1]; + } else { + if (sRecordSongPos == 0) { + sPierresSong[1] = D_8016BAA0; + } else { + if (sRecordingState == 2) { + sStaffPlayingPos = 1; + } + + func_800EDD68(1); + } + + sOcarinaInpEnabled = 0; + sStaffPlayingPos = 0; + } + + sRecordingState = recordingState; +} + +void Audio_OcaUpdateRecordingStaff(void) { + sRecordingStaff.state = sRecordingState; + sRecordingStaff.pos = sStaffPlayingPos; + if (sRecordingState == 0xFF) { + sRecordingState = 0; + } +} + +void Audio_OcaUpdatePlayingStaff(void) { + sPlayingStaff.noteIdx = sCurOcarinaBtnIdx & 0x3F; + sPlayingStaff.state = Audio_OcaGetPlayingState(); + sPlayingStaff.pos = sStaffPlayingPos; +} + +void Audio_OcaUpdateDisplayedStaff(void) { + if ((sDisplayedNoteValue & 0x3F) < 0x10) { + sDisplayedStaff.noteIdx = Audio_OcaMapNoteValue(sDisplayedNoteValue); + } + + sDisplayedStaff.state = sPlaybackState; + + if (sPlaybackSong != sPierresSong) { + sDisplayedStaff.pos = sStaffPlaybackPos; + } else if (sStaffPlaybackPos == 0) { + sDisplayedStaff.pos = 0; + } else { + sDisplayedStaff.pos = ((sStaffPlaybackPos - 1) % 8) + 1; + } +} + +OcarinaStaff* Audio_OcaGetRecordingStaff(void) { + return &sRecordingStaff; +} + +OcarinaStaff* Audio_OcaGetPlayingStaff(void) { + if (sPlayingStaff.state < 0xFE) { + D_80130F3C = 0; + } + return &sPlayingStaff; +} + +OcarinaStaff* Audio_OcaGetDisplayingStaff(void) { + return &sDisplayedStaff; +} + +void func_800EE404(void) { + s32 noteChanged; + + if ((sRecordingState != 0) && ((D_8016BA04 - D_80131860) >= 3)) { + noteChanged = false; + if (D_80131864 != sCurOcarinaBtnVal) { + if (sCurOcarinaBtnVal != 0xFF) { + sRecordingStaff.noteIdx = sCurOcarinaBtnIdx & 0x3F; + sStaffPlayingPos++; + } else if ((sRecordingState == 2) && (sStaffPlayingPos == 8)) { + func_800EDD68(1); + return; + } + + if (sStaffPlayingPos > 8) { + if (sRecordingState == 2) { + // notes played are over 8 and in recording mode. + func_800EDD68(1); + return; + } + sStaffPlayingPos = true; + } + + noteChanged = true; + } else if (D_80131868 != D_80130F30) { + noteChanged = true; + } else if (D_8013186C != D_80130F34) { + noteChanged = true; + } else if (D_80131870 != D_80130F2C) { + noteChanged = true; + } + + if (noteChanged) { + func_800EDD68(0); + D_80131860 = D_8016BA04; + } + } +} + +void Audio_OcaMemoryGameStart(u8 minigameRound) { + u8 i; + + if (minigameRound > 2) { + minigameRound = 2; + } + + sOcaMinigameAppendPos = 0; + sOcaMinigameEndPos = sOcaMinigameNoteCnts[minigameRound]; + + for (i = 0; i < 3; i++) { + Audio_OcaMemoryGameGenNote(); + } +} + +s32 Audio_OcaMemoryGameGenNote(void) { + u32 rnd; + u8 rndNote; + + if (sOcaMinigameAppendPos == sOcaMinigameEndPos) { + return 1; + } + + rnd = Audio_NextRandom(); + rndNote = sOcarinaNoteValues[rnd % 5]; + + if (sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos - 1].noteIdx == rndNote) { + rndNote = sOcarinaNoteValues[(rnd + 1) % 5]; + } + + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].noteIdx = rndNote; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].unk_02 = 0x2D; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].volume = 0x50; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].vibrato = 0; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].tone = 0; + + sOcaMinigameAppendPos++; + + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].noteIdx = 0xFF; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos].unk_02 = 0; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos + 1].noteIdx = 0xFF; + sOcarinaSongs[OCARINA_SONG_MEMORY_GAME][sOcaMinigameAppendPos + 1].unk_02 = 0; + if (1) {} + return 0; +} + +// input update? +void func_800EE6F4(void) { + D_8016BA04 = gAudioContext.totalTaskCnt; + if (D_80130F10 != 0) { + if (sOcarinaInpEnabled == 1) { + Audio_GetOcaInput(); + } + if ((sPlaybackState == 0) && (sOcarinaInpEnabled == 1)) { + func_800ED458(0); + } + if (D_80130F3C != 0) { + if (D_80130F3C & 0x4000) { + func_800ED200(); + } else { + func_800ECDF8(); + } + } + + Audio_OcaPlayback(); + D_80130F68 = D_8016BA04; + + if (sPlaybackState == 0) { + func_800EE404(); + } + + if ((D_80130F3C != 0) && (sPrevOcarinaNoteVal != sCurOcarinaBtnVal)) { + D_80131880 = 1; + } + + sPrevOcarinaNoteVal = sCurOcarinaBtnVal; + } + + Audio_OcaUpdatePlayingStaff(); + Audio_OcaUpdateDisplayedStaff(); + Audio_OcaUpdateRecordingStaff(); +} + +void func_800EE824(void) { + static u8 D_80131C80 = 0; + static u8 D_80131C84 = 1; + static u16 D_80131C88 = 1200; + + switch (D_80131C80) { + case 0: + if (D_80131C88-- == 0) { + if (D_80131C84 < 7) { + D_80131C80++; + } else { + D_80131C80 = 3; + Audio_OcaSetInstrument(0); + } + D_80131C88 = 1200; + } + break; + case 1: + Audio_SetSoundBanksMute(0); + Audio_OcaSetInstrument(D_80131C84); + Audio_OcaSetSongPlayback(OCARINA_SONG_SCARECROW_LONG + 1, 1); + D_80131C84++; + D_80131C80++; + break; + case 2: + if (Audio_OcaGetDisplayingStaff()->state == 0) { + D_80131C80 = 0; + } + break; + } +} + +void func_800EE930(void) { + sPlayingStaff.noteIdx = OCARINA_NOTE_INVALID; + sPlayingStaff.state = 0xFF; + sPlayingStaff.pos = 0; + sDisplayedStaff.noteIdx = OCARINA_NOTE_INVALID; + sDisplayedStaff.state = 0; + sDisplayedStaff.pos = 0; + sRecordingStaff.noteIdx = OCARINA_NOTE_INVALID; + sRecordingStaff.state = 0xFF; + sRecordingStaff.pos = 0; + D_80131880 = 0; +} + +f32 D_80131C8C = 0.0f; + +// === Audio Debugging === + +// These variables come between in-function statics in func_800EE824 and Audio_SplitBgmChannels + +f32 sAudioUpdateDuration = 0.0f; +f32 sAudioUpdateDurationMax = 0.0f; +u8 sAudioDebugEverOpened = 0; +u8 sAudioSfxMuted = 0; +u8 sAudioDebugPage = 0; +u8 sAudioSndContSel = 0; +u8 sAudioDebugTextColor = 7; +char sAudioDebugPageNames[15][23] = { + "Non", + "Sound Control", + "Spec Info", + "Heap Info", + "Grp Track Info", + "Sub Track Info", + "Channel Info", + "Interface Info", + "SE Flag Swap", + "Block Change BGM", + "Natural Sound Control", + "Ocarina Test", + "SE Parameter Change", + "Scroll Print", + "Free Area", +}; +u16 sAudioSndContWork[11] = { 0 }; +u16 sAudioSndContWorkLims[11] = { 128, 128, 7, 512, 4, 2, 16, 32, 2, 2, 2 }; +char sSoundBankNames[7][11] = { "PLAYER", "ITEM", "ENVIROMENT", "ENEMY", "SYSTEM", "OCARINA", "VOICE" }; +char sSoundModeNames[5][10] = { "W-STEREO", "HEADPHONE", "3D SOUND", "MONO", "" }; +s8 sAudioIntInfoX = 0; +s8 sAudioIntInfoY = 0; +s8 sAudioIntInfoSel = 0; +s8 sAudioIntInfoBankPage[7] = { 0, 0, 2, 2, 0, 0, 0 }; +u8 sAudioScrPrtSel = 0; +u8 sAudioScrPrtInd = 0; +u8 sAudioScrPrtOverflow = 0; +s8 sAudioScrPrtX = 26; +s8 sAudioScrPrtY = 1; +u8 sAudioScrPrtWork[11] = { 1, 19, 6, 0, 0, 0, 0, 0, 0, 0, 1 }; +u8 sAudioScrPrtWorkLims[11] = { 2, SCROLL_PRINT_BUF_SIZE, 8, 2, 2, 2, 2, 2, 2, 2, 2 }; +u8 sAudioSubTrackInfoSpec = 0; +u8 sAudioSfxSwapIsEditing = 0; +u8 sAudioSfxSwapSel = 0; +u8 sAudioSfxSwapNibbleSel = 0; +char sAudioSfxSwapModeNames[2][5] = { "SWAP", "ADD" }; +u8 sAudioSfxParamChgSel = 0; +u8 sAudioSfxParamChgBitSel = 0; +u16 sAudioSfxParamChgWork[4] = { 0 }; +u8 sAudioSubTrackInfoPlayerSel = SEQ_PLAYER_BGM_MAIN; +u8 sAudioSubTrackInfoChannelSel = 0; +u8 sSeqPlayerPeakNumLayers[20] = { 0 }; +char sAudioSceneNames[3][2] = { "A", "S", "X" }; +u8 sAudioBlkChgBgmWork[2] = { 0 }; +u8 sAudioBlkChgBgmSel = 0; +char sBoolStrs[3][5] = { "OFF", "ON", "STBY" }; +u8 sAudioNatureFailed = false; +u8 sPeakNumNotes = 0; + +void AudioDebug_SetInput(void) { + Input inputs[4]; + u32 btn; + + PadMgr_RequestPadData(&gPadMgr, inputs, 0); + btn = inputs[3].cur.button; + sDebugPadHold = btn & 0xFFFF; + sDebugPadPress = (btn ^ sDebugPadBtnLast) & btn; + sDebugPadBtnLast = btn; +} + +char* AudioDebug_ToStringBinary(u32 num, u8 bits) { + u8 i; + u32 flg = 1; + + for (i = 0; i < bits; flg *= 2, i++) { + if (num & flg) { + sBinToStrBuf[bits - i - 1] = '1'; + } else { + sBinToStrBuf[bits - i - 1] = '0'; + } + } + + sBinToStrBuf[bits] = '\0'; + return sBinToStrBuf; +} + +void AudioDebug_Draw(GfxPrint* printer) { + s32 pad[3]; + u8 i; + u8 j; + u8 ctr; + u8 ctr2; + s8 k; + s8 k2; + s8 ind; + u8 numEnabledNotes = 0; + char digitStr[2] = "1"; + +#define SETCOL_COMMON(v, r, g, b) \ + GfxPrint_SetColor(printer, ((v & 4) >> 2) * (r), ((v & 2) >> 1) * (g), (v & 1) * (b), 255) +#define SETCOL(r, g, b) SETCOL_COMMON(sAudioDebugTextColor, r, g, b) +#define SETCOL_SCROLLPRINT(r, g, b) SETCOL_COMMON(sAudioScrPrtWork[2], r, g, b) + + sAudioDebugEverOpened = true; + GfxPrint_SetPos(printer, 3, 2); + SETCOL(255, 255, 255); + GfxPrint_Printf(printer, "Audio Debug Mode"); + + GfxPrint_SetPos(printer, 3, 3); + GfxPrint_Printf(printer, "- %s -", sAudioDebugPageNames[sAudioDebugPage]); + + for (i = 0; i < gAudioSpecs[gAudioSpecId].numNotes; i++) { + if (gAudioContext.notes[i].noteSubEu.bitField0.enabled == 1) { + numEnabledNotes++; + } + } + + if (sPeakNumNotes < numEnabledNotes) { + sPeakNumNotes = numEnabledNotes; + } + if (sAudioSpecPeakNumNotes[gAudioSpecId] < numEnabledNotes) { + sAudioSpecPeakNumNotes[gAudioSpecId] = numEnabledNotes; + } + + if (sAudioScrPrtWork[0] != 0) { + GfxPrint_SetPos(printer, sAudioScrPrtX, sAudioScrPrtY); + SETCOL_SCROLLPRINT(200, 200, 200); + GfxPrint_Printf(printer, "Audio ScrPrt"); + + ind = sAudioScrPrtInd; + for (k = 0; k < sAudioScrPrtWork[1] + 1; k++) { + if (ind == 0) { + if (sAudioScrPrtOverflow == 1) { + ind = SCROLL_PRINT_BUF_SIZE - 1; + } else { + k = sAudioScrPrtWork[1] + 1; // "break;" + } + } else { + ind--; + } + if (k != sAudioScrPrtWork[1] + 1) { + if ((ind % 5) != 0) { + SETCOL_SCROLLPRINT(180, 180, 180); + } else { + SETCOL_SCROLLPRINT(120, 120, 120); + } + GfxPrint_SetPos(printer, 2 + sAudioScrPrtX, sAudioScrPrtY + sAudioScrPrtWork[1] + 1 - k); + GfxPrint_Printf(printer, "%s", sAudioScrPrtBuf[ind].str); + + GfxPrint_SetPos(printer, 7 + sAudioScrPrtX, sAudioScrPrtY + sAudioScrPrtWork[1] + 1 - k); + GfxPrint_Printf(printer, "%04X", sAudioScrPrtBuf[ind].num); + } + } + } + + switch (sAudioDebugPage) { + case PAGE_NON: + GfxPrint_SetPos(printer, 3, 4); + SETCOL(255, 64, 64); + GfxPrint_Printf(printer, "BGM CANCEL:%s", sBoolStrs[sAudioSndContWork[5]]); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "SE MUTE:%s", sBoolStrs[sAudioSfxMuted]); + + GfxPrint_SetPos(printer, 18, 4); + SETCOL(255, 255, 255); + GfxPrint_Printf(printer, "PUSH CONT-4 A-BTN"); + + ind = (s8)sAudioSndContWork[2]; + i = gSoundBanks[ind][0].next; + j = 0; + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf(printer, "SE HANDLE:%s", sSoundBankNames[ind]); + + while (i != 0xFF) { + GfxPrint_SetPos(printer, 3, 7 + j++); + GfxPrint_Printf(printer, "%02x %04x %02x %08x", i, gSoundBanks[ind][i].sfxId, gSoundBanks[ind][i].state, + gSoundBanks[ind][i].priority); + i = gSoundBanks[ind][i].next; + } + break; + + case PAGE_SOUND_CONTROL: + GfxPrint_SetPos(printer, 2, 4 + sAudioSndContSel); + SETCOL(127, 255, 127); + GfxPrint_Printf(printer, "*"); + + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 4); + GfxPrint_Printf(printer, "Seq 0 : %2x", sAudioSndContWork[0]); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "Seq 1 : %2x", sAudioSndContWork[1]); + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf(printer, "SE HD : %2x %s", sAudioSndContWork[2], sSoundBankNames[sAudioSndContWork[2]]); + + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf(printer, "SE No. :%3x", sAudioSndContWork[3]); + + GfxPrint_SetPos(printer, 3, 8); + GfxPrint_Printf(printer, "S-Out : %2x %s", sAudioSndContWork[4], sSoundModeNames[sAudioSndContWork[4]]); + + GfxPrint_SetPos(printer, 3, 9); + GfxPrint_Printf(printer, "BGM Ent: %2x", sAudioSndContWork[5]); + + GfxPrint_SetPos(printer, 3, 10); + GfxPrint_Printf(printer, "Spec : %2x", sAudioSndContWork[6]); + + GfxPrint_SetPos(printer, 3, 11); + GfxPrint_Printf(printer, "Na Snd : %2x", sAudioSndContWork[7]); + + GfxPrint_SetPos(printer, 3, 12); + GfxPrint_Printf(printer, "Cam Wt : %s", sBoolStrs[sAudioSndContWork[8]]); + + GfxPrint_SetPos(printer, 3, 13); + GfxPrint_Printf(printer, "Lnk Wt : %s", sBoolStrs[sAudioSndContWork[9]]); + + GfxPrint_SetPos(printer, 3, 14); + GfxPrint_Printf(printer, "SE Ent : %2x", sAudioSndContWork[10]); + break; + + case PAGE_INTERFACE_INFO: + ind = 0; + for (k = 0; k < 7; k++) { + if (k == sAudioIntInfoSel) { + SETCOL(255, 127, 127); + } else { + SETCOL(255, 255, 255); + } + GfxPrint_SetPos(printer, 2 + sAudioIntInfoX, 4 + ind + sAudioIntInfoY); + GfxPrint_Printf(printer, "%s <%d>", sSoundBankNames[k], sAudioIntInfoBankPage[k]); + + for (k2 = 0; k2 < gChannelsPerBank[gSfxChannelLayout][k]; k2++) { +#define entryIndex (gActiveSounds[k][k2].entryIndex) +#define entry (&gSoundBanks[k][entryIndex]) +#define chan (gAudioContext.seqPlayers[SEQ_PLAYER_SFX].channels[entry->channelIdx]) + GfxPrint_SetPos(printer, 2 + sAudioIntInfoX, 5 + ind + sAudioIntInfoY); + if (sAudioIntInfoBankPage[k] == 1) { + if ((entryIndex != 0xFF) && + ((entry->state == SFX_STATE_PLAYING_1) || (entry->state == SFX_STATE_PLAYING_2))) { + GfxPrint_Printf(printer, "%2X %5d %5d %5d %02X %04X %04X", entryIndex, (s32)*entry->posX, + (s32)*entry->posY, (s32)*entry->posZ, entry->sfxImportance, + entry->sfxParams, entry->sfxId); + } else { + GfxPrint_Printf(printer, "FF ----- ----- ----- -- ---- ----"); + } + } else if (sAudioIntInfoBankPage[k] == 2) { + if ((entryIndex != 0xFF) && + ((entry->state == SFX_STATE_PLAYING_1) || (entry->state == SFX_STATE_PLAYING_2))) { + GfxPrint_Printf(printer, "%2X %5d %5d %5d %3d %3d %04X", entryIndex, (s32)*entry->posX, + (s32)*entry->posY, (s32)*entry->posZ, (s32)(chan->volume * 127.1f), + chan->newPan, entry->sfxId); + } else { + GfxPrint_Printf(printer, "FF ----- ----- ----- --- --- ----"); + } + } else if (sAudioIntInfoBankPage[k] == 3) { + if ((entryIndex != 0xFF) && + ((entry->state == SFX_STATE_PLAYING_1) || (entry->state == SFX_STATE_PLAYING_2))) { + GfxPrint_Printf(printer, "%2X %5d %5d %5d %3d %3d %04X", entryIndex, (s32)*entry->posX, + (s32)*entry->posY, (s32)*entry->posZ, (s32)(chan->freqScale * 100.0f), + chan->reverb, entry->sfxId); + } else { + GfxPrint_Printf(printer, "FF ----- ----- ----- --- --- ----"); + } + } else if (sAudioIntInfoBankPage[k] == 4) { + if ((entryIndex != 0xFF) && + ((entry->state == SFX_STATE_PLAYING_1) || (entry->state == SFX_STATE_PLAYING_2))) { + GfxPrint_Printf(printer, "%2X %04X", entryIndex, entry->sfxId); + } else { + GfxPrint_Printf(printer, "FF ----"); + } + } +#undef entryIndex +#undef entry +#undef chan + + if (sAudioIntInfoBankPage[k] != 0) { + ind++; + } + } + ind++; + } + break; + + case PAGE_SCROLL_PRINT: + GfxPrint_SetPos(printer, 2, 4 + sAudioScrPrtSel); + SETCOL(255, 255, 255); + GfxPrint_Printf(printer, "*"); + + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 4); + GfxPrint_Printf(printer, "Swicth : %d", sAudioScrPrtWork[0]); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "Lines : %d", sAudioScrPrtWork[1] + 1); + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf(printer, "Color : %d", sAudioScrPrtWork[2]); + + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf(printer, "%s : %d", sSoundBankNames[0], sAudioScrPrtWork[3]); + + GfxPrint_SetPos(printer, 3, 8); + GfxPrint_Printf(printer, "%s : %d", sSoundBankNames[1], sAudioScrPrtWork[4]); + + GfxPrint_SetPos(printer, 3, 9); + GfxPrint_Printf(printer, "ENVRONM : %d", sAudioScrPrtWork[5]); + + GfxPrint_SetPos(printer, 3, 10); + GfxPrint_Printf(printer, "%s : %d", sSoundBankNames[3], sAudioScrPrtWork[6]); + + GfxPrint_SetPos(printer, 3, 11); + GfxPrint_Printf(printer, "%s : %d", sSoundBankNames[4], sAudioScrPrtWork[7]); + + GfxPrint_SetPos(printer, 3, 12); + GfxPrint_Printf(printer, "%s : %d", sSoundBankNames[5], sAudioScrPrtWork[8]); + + GfxPrint_SetPos(printer, 3, 13); + GfxPrint_Printf(printer, "%s : %d", sSoundBankNames[6], sAudioScrPrtWork[9]); + + GfxPrint_SetPos(printer, 3, 14); + GfxPrint_Printf(printer, "SEQ ENT : %d", sAudioScrPrtWork[10]); + break; + + case PAGE_SFX_SWAP: + GfxPrint_SetPos(printer, 3, 4); + SETCOL(255, 255, 255); + if (gAudioSfxSwapOff) { + GfxPrint_Printf(printer, "SWAP OFF"); + } + + if (sAudioSfxSwapIsEditing == 0) { + SETCOL(255, 255, 255); + } else { + SETCOL(127, 127, 127); + } + GfxPrint_SetPos(printer, 2, 6 + sAudioSfxSwapSel); + GfxPrint_Printf(printer, "*"); + + ctr = sAudioSfxSwapNibbleSel; + if (sAudioSfxSwapNibbleSel >= 4) { + ctr++; + } + if (sAudioSfxSwapIsEditing == 1) { + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3 + ctr, 5); + GfxPrint_Printf(printer, "V"); + } + + for (i = 0; i < 10; i++) { + if (i == sAudioSfxSwapSel) { + if (sAudioSfxSwapIsEditing == 0) { + SETCOL(192, 192, 192); + } else { + SETCOL(255, 255, 255); + } + } else if (sAudioSfxSwapIsEditing == 0) { + SETCOL(144, 144, 144); + } else { + SETCOL(96, 96, 96); + } + GfxPrint_SetPos(printer, 3, 6 + i); + GfxPrint_Printf(printer, "%04x %04x %s", gAudioSfxSwapSource[i], gAudioSfxSwapTarget[i], + sAudioSfxSwapModeNames[gAudioSfxSwapMode[i]]); + } + break; + + case PAGE_SUB_TRACK_INFO: + GfxPrint_SetPos(printer, 3, 4); + SETCOL(255, 255, 255); + GfxPrint_Printf(printer, "Group Track:%d", sAudioSubTrackInfoPlayerSel); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "Sub Track :%d", sAudioSubTrackInfoChannelSel); + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf(printer, "TRK NO. "); + + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf(printer, "ENTRY "); + + GfxPrint_SetPos(printer, 3, 8); + GfxPrint_Printf(printer, "MUTE "); + + GfxPrint_SetPos(printer, 3, 9); + GfxPrint_Printf(printer, "OPENNOTE"); + + ctr2 = 0; + for (i = 0; i < 16; i++) { + if (i == sAudioSubTrackInfoChannelSel) { + SETCOL(255, 255, 255); + } else { + SETCOL(200, 200, 200); + } + GfxPrint_SetPos(printer, 15 + i, 6); + GfxPrint_Printf(printer, "%1X", i); + + GfxPrint_SetPos(printer, 15 + i, 7); + if (gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel].channels[i]->enabled) { + GfxPrint_Printf(printer, "O"); + } else { + GfxPrint_Printf(printer, "X"); + } + + GfxPrint_SetPos(printer, 15 + i, 8); + if (gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel].channels[i]->stopSomething2) { + GfxPrint_Printf(printer, "O"); + } else { + GfxPrint_Printf(printer, "X"); + } + + GfxPrint_SetPos(printer, 15 + i, 9); + ctr = 0; + for (j = 0; j < 4; j++) { + if (gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel].channels[i]->layers[j] != NULL) { + ctr++; + } + } + + GfxPrint_Printf(printer, "%1X", ctr); + ctr2 += ctr; + } + + SETCOL(255, 255, 255); + if (sSeqPlayerPeakNumLayers[sAudioSubTrackInfoPlayerSel] < ctr2) { + sSeqPlayerPeakNumLayers[sAudioSubTrackInfoPlayerSel] = ctr2; + } + GfxPrint_SetPos(printer, 16 + i, 9); + GfxPrint_Printf(printer, "%2d,%2d", ctr2, sSeqPlayerPeakNumLayers[sAudioSubTrackInfoPlayerSel]); + + GfxPrint_SetPos(printer, 3, 11); + GfxPrint_Printf(printer, "VOL "); + + GfxPrint_SetPos(printer, 3, 12); + GfxPrint_Printf(printer, "E VOL "); + + GfxPrint_SetPos(printer, 3, 13); + GfxPrint_Printf(printer, "BANK ID "); + + GfxPrint_SetPos(printer, 3, 14); + GfxPrint_Printf(printer, "PROG "); + + GfxPrint_SetPos(printer, 3, 15); + GfxPrint_Printf(printer, "PAN "); + + GfxPrint_SetPos(printer, 3, 16); + GfxPrint_Printf(printer, "PANPOW "); + + GfxPrint_SetPos(printer, 3, 17); + GfxPrint_Printf(printer, "FXMIX "); + + GfxPrint_SetPos(printer, 3, 18); + GfxPrint_Printf(printer, "PRIO "); + + GfxPrint_SetPos(printer, 3, 19); + GfxPrint_Printf(printer, "VIB PIT "); + + GfxPrint_SetPos(printer, 3, 20); + GfxPrint_Printf(printer, "VIB DEP "); + + GfxPrint_SetPos(printer, 3, 21); + GfxPrint_Printf(printer, "TUNE "); + + GfxPrint_SetPos(printer, 3, 22); + GfxPrint_Printf(printer, "TUNE "); + + for (i = 0; i < 8; i++) { + GfxPrint_SetPos(printer, 15 + 3 * i, 22); + GfxPrint_Printf(printer, "%02X ", + (u8)gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->soundScriptIO[i]); + } + + if (gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel].channels[sAudioSubTrackInfoChannelSel]->enabled) { + GfxPrint_SetPos(printer, 15, 11); + GfxPrint_Printf(printer, "%d", + (u8)(gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->volume * + 127.1)); + + GfxPrint_SetPos(printer, 15, 12); + GfxPrint_Printf(printer, "%d", + (u8)(gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->volumeScale * + 127.1)); + + GfxPrint_SetPos(printer, 15, 13); + GfxPrint_Printf(printer, "%X", + gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->fontId); + + ctr = (u8)(gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->instOrWave); + + if (ctr == 0) { + ctr2 = 0x7F; + } else if (ctr < 0x80) { + ctr2 = ctr - 1; + } else { + ctr2 = ctr; + } + + GfxPrint_SetPos(printer, 15, 14); + GfxPrint_Printf(printer, "%d", ctr2); + + GfxPrint_SetPos(printer, 15, 15); + GfxPrint_Printf(printer, "%d", + gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->newPan); + + GfxPrint_SetPos(printer, 15, 16); + GfxPrint_Printf(printer, "%d", + gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->panChannelWeight); + + GfxPrint_SetPos(printer, 15, 17); + GfxPrint_Printf(printer, "%d", + gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->reverb); + + GfxPrint_SetPos(printer, 15, 18); + GfxPrint_Printf(printer, "%d", + gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->notePriority); + + GfxPrint_SetPos(printer, 15, 19); + GfxPrint_Printf(printer, "%d", + (u8)(gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->vibratoRateTarget / + 32)); + + GfxPrint_SetPos(printer, 15, 20); + GfxPrint_Printf(printer, "%d", + (u8)(gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->vibratoExtentTarget / + 8)); + + GfxPrint_SetPos(printer, 15, 21); + GfxPrint_Printf(printer, "%d", + (u16)(gAudioContext.seqPlayers[sAudioSubTrackInfoPlayerSel] + .channels[sAudioSubTrackInfoChannelSel] + ->freqScale * + 100)); + } + break; + + case PAGE_HEAP_INFO: + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 4); + GfxPrint_Printf(printer, "TOTAL %d", D_8014A6C4.heapSize); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "DRIVER %05X / %05X", + gAudioContext.notesAndBuffersPool.cur - gAudioContext.notesAndBuffersPool.start, + gAudioContext.notesAndBuffersPool.size); + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf( + printer, "AT-SEQ %02X-%02X (%05X-%05X / %05X)", (u8)gAudioContext.seqCache.temporary.entries[0].id, + (u8)gAudioContext.seqCache.temporary.entries[1].id, gAudioContext.seqCache.temporary.entries[0].size, + gAudioContext.seqCache.temporary.entries[1].size, gAudioContext.seqCache.temporary.pool.size); + + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf( + printer, "AT-BNK %02X-%02X (%05X-%05X / %05X)", (u8)gAudioContext.fontCache.temporary.entries[0].id, + (u8)gAudioContext.fontCache.temporary.entries[1].id, gAudioContext.fontCache.temporary.entries[0].size, + gAudioContext.fontCache.temporary.entries[1].size, gAudioContext.fontCache.temporary.pool.size); + + GfxPrint_SetPos(printer, 3, 8); + GfxPrint_Printf(printer, "ST-SEQ %02Xseqs (%05X / %06X)", gAudioContext.seqCache.persistent.numEntries, + gAudioContext.seqCache.persistent.pool.cur - gAudioContext.seqCache.persistent.pool.start, + gAudioContext.seqCache.persistent.pool.size); + + for (k = 0; (u32)k < gAudioContext.seqCache.persistent.numEntries; k++) { + GfxPrint_SetPos(printer, 3 + 3 * k, 9); + GfxPrint_Printf(printer, "%02x", gAudioContext.seqCache.persistent.entries[k].id); + } + + GfxPrint_SetPos(printer, 3, 10); + GfxPrint_Printf(printer, "ST-BNK %02Xbanks (%05X / %06X)", gAudioContext.fontCache.persistent.numEntries, + gAudioContext.fontCache.persistent.pool.cur - gAudioContext.fontCache.persistent.pool.start, + gAudioContext.fontCache.persistent.pool.size); + + for (k = 0; (u32)k < gAudioContext.fontCache.persistent.numEntries; k++) { + GfxPrint_SetPos(printer, 3 + 3 * k, 11); + GfxPrint_Printf(printer, "%02x", gAudioContext.fontCache.persistent.entries[k].id); + } + + GfxPrint_SetPos(printer, 3, 12); + GfxPrint_Printf(printer, "E-MEM %05X / %05X", + gAudioContext.permanentPool.cur - gAudioContext.permanentPool.start, + gAudioContext.permanentPool.size); + break; + + case PAGE_BLOCK_CHANGE_BGM: + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 4); + GfxPrint_Printf(printer, "BGM No. %02X", sAudioBlkChgBgmWork[0]); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "SCENE SET %02X %s", sAudioBlkChgBgmWork[1], + sAudioSceneNames[sAudioBlkChgBgmWork[1]]); + + SETCOL(0x64, 255, 0x64); + GfxPrint_SetPos(printer, 2, 4 + sAudioBlkChgBgmSel); + GfxPrint_Printf(printer, "*"); + + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf(printer, "NEXT SCENE %02X %s", + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[2], + sAudioSceneNames[(u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[2]]); + + GfxPrint_SetPos(printer, 3, 8); + GfxPrint_Printf(printer, "NOW SCENE %02X %s", + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[4], + sAudioSceneNames[(u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[4]]); + + GfxPrint_SetPos(printer, 3, 9); + GfxPrint_Printf(printer, "NOW BLOCK %02X", + (gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[5] + 1) & 0xFF); + + GfxPrint_SetPos(printer, 3, 11); + GfxPrint_Printf(printer, "PORT"); + + GfxPrint_SetPos(printer, 3, 12); + GfxPrint_Printf(printer, "%02X %02X %02X %02X", + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[0], + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[1], + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[2], + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[3]); + + GfxPrint_SetPos(printer, 3, 13); + GfxPrint_Printf(printer, "%02X %02X %02X %02X", + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[4], + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[5], + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[6], + (u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[7]); + break; + + case PAGE_OCARINA_TEST: + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 4); + GfxPrint_Printf(printer, "SEQ INFO : %2d %02x %d", sDisplayedStaff.noteIdx, sDisplayedStaff.state, + sDisplayedStaff.pos); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "PLAY INFO : %2d %02x %d", sPlayingStaff.noteIdx, sPlayingStaff.state, + sPlayingStaff.pos); + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf(printer, "8note REC POINTER : %08x", gScarecrowSpawnSongPtr); + + ctr = 0; + for (j = 0; j < 4; j++) { + for (i = 0; i < 8; i++) { + GfxPrint_SetPos(printer, 3 + 3 * i, 7 + j); + GfxPrint_Printf(printer, "%02x", gScarecrowSpawnSongPtr[ctr++]); + } + } + + GfxPrint_SetPos(printer, 3, 24); + GfxPrint_Printf(printer, "OCA:%02x SEQ:%04x PLAY:%02x REC:%02x", D_80130F10, D_80130F3C, sPlaybackState, + sRecordingState); + break; + + case PAGE_SFX_PARAMETER_CHANGE: + GfxPrint_SetPos(printer, 2, 4 + sAudioSfxParamChgSel); + SETCOL(127, 255, 127); + GfxPrint_Printf(printer, "*"); + + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 4); + GfxPrint_Printf(printer, "SE HD : %02x %s", sAudioSfxParamChgWork[0], + sSoundBankNames[sAudioSfxParamChgWork[0]]); + + GfxPrint_SetPos(printer, 3, 5); + GfxPrint_Printf(printer, "SE No. : %02x", sAudioSfxParamChgWork[1]); + + GfxPrint_SetPos(printer, 20, 6); + GfxPrint_Printf(printer, " : %04x", + gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].params); + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf( + printer, "SE SW %s", + AudioDebug_ToStringBinary(gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].params, 16)); + + SETCOL(127, 255, 127); + digitStr[0] = (char)('0' + ((gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].params >> + (15 - sAudioSfxParamChgBitSel)) & + 1)); + GfxPrint_SetPos(printer, 12 + sAudioSfxParamChgBitSel, 6); + GfxPrint_Printf(printer, "%s", digitStr); + + SETCOL(255, 255, 255); + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf(printer, "SE PR : %02x", + gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].importance); + break; + + case PAGE_FREE_AREA: + GfxPrint_SetPos(printer, 3, 4); + SETCOL(255, 255, 255); + GfxPrint_Printf(printer, "env_fx %d code_fx %d SPEC %d", sAudioEnvReverb, sAudioCodeReverb, gAudioSpecId); + + if (sAudioUpdateTaskStart == sAudioUpdateTaskEnd) { + sAudioUpdateDuration = OS_CYCLES_TO_NSEC(sAudioUpdateEndTime - sAudioUpdateStartTime) / (1e9f / 20); + if (sAudioUpdateDurationMax < sAudioUpdateDuration) { + sAudioUpdateDurationMax = sAudioUpdateDuration; + } + } + + GfxPrint_SetPos(printer, 3, 6); + GfxPrint_Printf(printer, "SOUND GAME FRAME NOW %f", sAudioUpdateDuration); + + GfxPrint_SetPos(printer, 3, 7); + GfxPrint_Printf(printer, "SOUND GAME FRAME MAX %f", sAudioUpdateDurationMax); + + GfxPrint_SetPos(printer, 3, 9); + GfxPrint_Printf(printer, "SWITCH BGM MODE %d %d %d (FLAG %d)", sPrevSeqMode, sNumFramesStill, + sNumFramesMoving, sSeqModeInput); + + GfxPrint_SetPos(printer, 3, 10); + GfxPrint_Printf(printer, "ENEMY DIST %f VOL %3d", sAudioEnemyDist, sAudioEnemyVol); + + GfxPrint_SetPos(printer, 3, 11); + GfxPrint_Printf(printer, "GANON DIST VOL %3d", sAudioGanonDistVol); + + GfxPrint_SetPos(printer, 3, 12); + GfxPrint_Printf(printer, "DEMO FLAG %d", sAudioCutsceneFlag); + + GfxPrint_SetPos(printer, 3, 12); + if (sAudioHasMalonBgm == true) { + GfxPrint_Printf(printer, "MARON BGM DIST %f", sAudioMalonBgmDist); + sAudioHasMalonBgm = false; + } + + GfxPrint_SetPos(printer, 3, 23); + if (sAudioNatureFailed != false) { + GfxPrint_Printf(printer, "NATURE FAILED %01x", sAudioNatureFailed); + } + + GfxPrint_SetPos(printer, 3, 24); + if (sSariaBgmPtr != 0) { + GfxPrint_Printf(printer, "SARIA BGM PTR %08x", sSariaBgmPtr); + } + + GfxPrint_SetPos(printer, 3, 25); + GfxPrint_Printf(printer, "POLI %d(%d)", sPeakNumNotes, numEnabledNotes); + + for (i = 0; i < 11; i++) { + GfxPrint_SetPos(printer, 3 + 3 * i, 26); + GfxPrint_Printf(printer, "%d", sAudioSpecPeakNumNotes[i]); + } + break; + } +#undef SETCOL_COMMON +#undef SETCOL +#undef SETCOL_SCROLLPRINT +} + +void AudioDebug_ProcessInput_SndCont(void) { + u16 step = 1; + + if (CHECK_BTN_ANY(sDebugPadHold, BTN_CDOWN)) { + if (sAudioSndContWorkLims[sAudioSndContSel] >= 16) { + step = 16; + } + } else if (CHECK_BTN_ANY(sDebugPadHold, BTN_CLEFT)) { + if (sAudioSndContWorkLims[sAudioSndContSel] >= 16) { + step = 8; + } + } else if (CHECK_BTN_ANY(sDebugPadHold, BTN_CUP)) { + sAudioSndContWork[sAudioSndContSel] = 0; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + if (sAudioSndContSel > 0) { + sAudioSndContSel--; + } else { + sAudioSndContSel = 10; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (sAudioSndContSel < 10) { + sAudioSndContSel++; + } else { + sAudioSndContSel = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT)) { + if (sAudioSndContWork[sAudioSndContSel] >= step) { + if (1) { + sAudioSndContWork[sAudioSndContSel] -= step; + } + } else { + sAudioSndContWork[sAudioSndContSel] += sAudioSndContWorkLims[sAudioSndContSel] - step; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT)) { + if (sAudioSndContWork[sAudioSndContSel] + step < sAudioSndContWorkLims[sAudioSndContSel]) { + sAudioSndContWork[sAudioSndContSel] += step; + } else { + sAudioSndContWork[sAudioSndContSel] += step - sAudioSndContWorkLims[sAudioSndContSel]; + } + } + + if (sAudioSndContSel == 8) { + if (sAudioSndContWork[sAudioSndContSel] != 0) { + Audio_SetExtraFilter(0x20); + } else { + Audio_SetExtraFilter(0); + } + } + + if (sAudioSndContSel == 9) { + if (sAudioSndContWork[sAudioSndContSel] != 0) { + Audio_SetBaseFilter(0x20); + } else { + Audio_SetBaseFilter(0); + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + switch (sAudioSndContSel) { + case 0: + case 1: + Audio_StartSeq(sAudioSndContSel, 0, sAudioSndContWork[sAudioSndContSel]); + break; + case 2: + case 3: + Audio_PlaySoundGeneral(((sAudioSndContWork[2] << 12) & 0xFFFF) + sAudioSndContWork[3] + SFX_FLAG, + &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + break; + case 4: + func_800F6700(sAudioSndContWork[sAudioSndContSel]); + break; + case 5: + Audio_SeqCmdE01(SEQ_PLAYER_BGM_MAIN, sAudioSndContWork[sAudioSndContSel]); + break; + case 6: + Audio_SeqCmdF(SEQ_PLAYER_BGM_MAIN, sAudioSndContWork[sAudioSndContSel]); + sAudioSubTrackInfoSpec = sAudioSndContWork[6]; + if (sAudioSubTrackInfoPlayerSel > gAudioSpecs[sAudioSubTrackInfoSpec].numSequencePlayers - 1) { + sAudioSubTrackInfoPlayerSel = gAudioSpecs[sAudioSubTrackInfoSpec].numSequencePlayers - 1; + } + break; + case 7: + Audio_PlayNatureAmbienceSequence(sAudioSndContWork[sAudioSndContSel]); + break; + case 8: + case 9: + break; + case 10: + Audio_SetSoundBanksMute(sAudioSndContWork[sAudioSndContSel] * 0x7F); + break; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + switch (sAudioSndContSel) { + case 0: + case 1: + Audio_SeqCmd1(sAudioSndContSel, 0); + break; + case 7: + Audio_SeqCmd1(SEQ_PLAYER_BGM_MAIN, 0); + break; + case 2: + case 3: + Audio_StopSfxByBank(sAudioSndContWork[2]); + break; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CDOWN)) { + if (sAudioSndContSel == 0) { + if (1) {} + func_800F595C(sAudioSndContWork[sAudioSndContSel]); + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CRIGHT)) { + if (sAudioSndContSel == 0) { + if (1) {} + func_800F5ACC(sAudioSndContWork[sAudioSndContSel]); + } + } +} + +void AudioDebug_ProcessInput_IntInfo(void) { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CUP)) { + sAudioIntInfoY--; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CDOWN)) { + sAudioIntInfoY++; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CLEFT)) { + sAudioIntInfoX--; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CRIGHT)) { + sAudioIntInfoX++; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + sAudioIntInfoX = 0; + sAudioIntInfoY = 0; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP) && sAudioIntInfoSel > 0) { + sAudioIntInfoSel--; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN) && sAudioIntInfoSel < 6) { + sAudioIntInfoSel++; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT) && sAudioIntInfoBankPage[sAudioIntInfoSel] > 0) { + sAudioIntInfoBankPage[sAudioIntInfoSel]--; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT) && sAudioIntInfoBankPage[sAudioIntInfoSel] < 4) { + sAudioIntInfoBankPage[sAudioIntInfoSel]++; + } +} + +void AudioDebug_ProcessInput_ScrPrt(void) { + if (sAudioScrPrtWork[0] != 0) { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CUP)) { + sAudioScrPrtY--; + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CDOWN)) { + sAudioScrPrtY++; + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CLEFT)) { + sAudioScrPrtX--; + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CRIGHT)) { + sAudioScrPrtX++; + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + sAudioScrPrtX = 26; + sAudioScrPrtY = 1; + sAudioScrPrtWork[2] = 6; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + sAudioScrPrtInd = 0; + sAudioScrPrtOverflow = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + if (sAudioScrPrtSel > 0) { + sAudioScrPrtSel--; + } else { + sAudioScrPrtSel = 10; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (sAudioScrPrtSel < 10) { + sAudioScrPrtSel++; + } else { + sAudioScrPrtSel = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT)) { + if (sAudioScrPrtWork[sAudioScrPrtSel] > 0) { + sAudioScrPrtWork[sAudioScrPrtSel]--; + } else { + sAudioScrPrtWork[sAudioScrPrtSel] = sAudioScrPrtWorkLims[sAudioScrPrtSel] - 1; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT)) { + if (sAudioScrPrtWork[sAudioScrPrtSel] < sAudioScrPrtWorkLims[sAudioScrPrtSel] - 1) { + sAudioScrPrtWork[sAudioScrPrtSel]++; + } else { + sAudioScrPrtWork[sAudioScrPrtSel] = 0; + } + } + + D_801333F0 = sAudioScrPrtWork[3] + (sAudioScrPrtWork[4] * 2) + (sAudioScrPrtWork[5] * 4) + + (sAudioScrPrtWork[6] * 8) + (sAudioScrPrtWork[7] * 0x10) + (sAudioScrPrtWork[8] * 0x20); +} + +void AudioDebug_ProcessInput_SfxSwap(void) { + s16 step; + u16 val; + u8 prev; + + if (!sAudioSfxSwapIsEditing) { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + if (sAudioSfxSwapSel > 0) { + sAudioSfxSwapSel--; + } else { + sAudioSfxSwapSel = 9; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (sAudioSfxSwapSel < 9) { + sAudioSfxSwapSel++; + } else { + sAudioSfxSwapSel = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + sAudioSfxSwapIsEditing = true; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + gAudioSfxSwapSource[sAudioSfxSwapSel] = 0; + gAudioSfxSwapTarget[sAudioSfxSwapSel] = 0; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_START)) { + if (sAudioSfxSwapSel != 0) { + prev = sAudioSfxSwapSel - 1; + } else { + prev = 9; + } + gAudioSfxSwapSource[sAudioSfxSwapSel] = gAudioSfxSwapSource[prev]; + gAudioSfxSwapTarget[sAudioSfxSwapSel] = gAudioSfxSwapTarget[prev]; + } + } else { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT)) { + if (sAudioSfxSwapNibbleSel > 0) { + sAudioSfxSwapNibbleSel--; + } else { + sAudioSfxSwapNibbleSel = 7; + } + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT)) { + if (sAudioSfxSwapNibbleSel < 7) { + sAudioSfxSwapNibbleSel++; + } else { + sAudioSfxSwapNibbleSel = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP) || CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + step = CHECK_BTN_ANY(sDebugPadHold, BTN_CUP) ? 8 : 1; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + step = CHECK_BTN_ANY(sDebugPadHold, BTN_CUP) ? -8 : -1; + } + + if (sAudioSfxSwapNibbleSel < 4) { + val = gAudioSfxSwapSource[sAudioSfxSwapSel] >> ((3 - sAudioSfxSwapNibbleSel) * 4); + val = (val + step) & 0xF; + gAudioSfxSwapSource[sAudioSfxSwapSel] = + (gAudioSfxSwapSource[sAudioSfxSwapSel] & ((0xF << ((3 - sAudioSfxSwapNibbleSel) * 4)) ^ 0xFFFF)) + + (val << ((3 - sAudioSfxSwapNibbleSel) * 4)); + } else { + val = gAudioSfxSwapTarget[sAudioSfxSwapSel] >> ((7 - sAudioSfxSwapNibbleSel) * 4); + val = (val + step) & 0xF; + gAudioSfxSwapTarget[sAudioSfxSwapSel] = + (gAudioSfxSwapTarget[sAudioSfxSwapSel] & ((0xF << ((7 - sAudioSfxSwapNibbleSel) * 4)) ^ 0xFFFF)) + + (val << ((7 - sAudioSfxSwapNibbleSel) * 4)); + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + sAudioSfxSwapIsEditing = false; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + if (sAudioSfxSwapNibbleSel < 4) { + gAudioSfxSwapSource[sAudioSfxSwapSel] = 0; + } else { + gAudioSfxSwapTarget[sAudioSfxSwapSel] = 0; + } + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CLEFT)) { + gAudioSfxSwapOff ^= 1; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CDOWN)) { + gAudioSfxSwapMode[sAudioSfxSwapSel] ^= 1; + } +} + +void AudioDebug_ProcessInput_SubTrackInfo(void) { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (sAudioSubTrackInfoPlayerSel != 0) { + sAudioSubTrackInfoPlayerSel--; + } else { + sAudioSubTrackInfoPlayerSel = gAudioSpecs[sAudioSubTrackInfoSpec].numSequencePlayers - 1; + } + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + if (sAudioSubTrackInfoPlayerSel < gAudioSpecs[sAudioSubTrackInfoSpec].numSequencePlayers - 1) { + sAudioSubTrackInfoPlayerSel++; + } else { + sAudioSubTrackInfoPlayerSel = SEQ_PLAYER_BGM_MAIN; + } + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT)) { + sAudioSubTrackInfoChannelSel = (sAudioSubTrackInfoChannelSel - 1) & 0xF; + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT)) { + sAudioSubTrackInfoChannelSel = (sAudioSubTrackInfoChannelSel + 1) & 0xF; + } + if (CHECK_BTN_ANY(sDebugPadPress, BTN_START)) { + sSeqPlayerPeakNumLayers[sAudioSubTrackInfoPlayerSel] = SEQ_PLAYER_BGM_MAIN; + } +} + +void AudioDebug_ProcessInput_HeapInfo(void) { +} + +void AudioDebug_ProcessInput_BlkChgBgm(void) { + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + if (sAudioBlkChgBgmSel > 0) { + sAudioBlkChgBgmSel--; + } else { + sAudioBlkChgBgmSel = 1; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (sAudioBlkChgBgmSel <= 0) { + sAudioBlkChgBgmSel++; + } else { + sAudioBlkChgBgmSel = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT)) { + sAudioBlkChgBgmWork[sAudioBlkChgBgmSel]--; + if (sAudioBlkChgBgmSel == 1) { + Audio_SetSequenceMode(sAudioBlkChgBgmWork[1]); + ; // might be a fake match? + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT)) { + sAudioBlkChgBgmWork[sAudioBlkChgBgmSel]++; + if (sAudioBlkChgBgmSel == 1) { + Audio_SetSequenceMode(sAudioBlkChgBgmWork[1]); + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + Audio_QueueCmdS8(MK_CMD(0x46, SEQ_PLAYER_BGM_MAIN, 0x00, 0x00), sAudioBlkChgBgmWork[1]); + Audio_QueueSeqCmd(sAudioBlkChgBgmWork[0] | 0x10000); + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + Audio_QueueSeqCmd(0x100100FF); + } +} + +void AudioDebug_ProcessInput_OcaTest(void) { +} + +void AudioDebug_ProcessInput_SfxParamChg(void) { + s32 step; + u16 sfx; + + if (CHECK_BTN_ANY(sDebugPadHold, BTN_CLEFT)) { + step = 8; + } else { + step = 1; + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DUP)) { + if (sAudioSfxParamChgSel > 0) { + sAudioSfxParamChgSel--; + } else { + sAudioSfxParamChgSel = 3; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DDOWN)) { + if (sAudioSfxParamChgSel < 3) { + sAudioSfxParamChgSel++; + } else { + sAudioSfxParamChgSel = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DLEFT)) { + if (sAudioSfxParamChgSel < 2) { + if (sAudioSfxParamChgSel == 0) { + if (sAudioSfxParamChgWork[sAudioSfxParamChgSel] > 0) { + sAudioSfxParamChgWork[sAudioSfxParamChgSel]--; + } else { + sAudioSfxParamChgWork[sAudioSfxParamChgSel] = sAudioSndContWorkLims[2] - 1; + } + } else { + sAudioSfxParamChgWork[sAudioSfxParamChgSel] -= step; + sAudioSfxParamChgWork[sAudioSfxParamChgSel] &= 0x1FF; + } + } else if (sAudioSfxParamChgSel == 3) { + gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].importance -= step; + } else { + sAudioSfxParamChgBitSel = (sAudioSfxParamChgBitSel - 1) & 0xF; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_DRIGHT)) { + if (sAudioSfxParamChgSel < 2) { + if (sAudioSfxParamChgSel == 0) { + if (sAudioSfxParamChgWork[sAudioSfxParamChgSel] < (sAudioSndContWorkLims[2] - 1)) { + sAudioSfxParamChgWork[sAudioSfxParamChgSel]++; + } else { + sAudioSfxParamChgWork[sAudioSfxParamChgSel] = 0; + } + } else { + sAudioSfxParamChgWork[sAudioSfxParamChgSel] += step; + sAudioSfxParamChgWork[sAudioSfxParamChgSel] &= 0x1FF; + } + } else if (sAudioSfxParamChgSel == 3) { + gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].importance += step; + } else { + sAudioSfxParamChgBitSel = (sAudioSfxParamChgBitSel + 1) & 0xF; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + sfx = (u16)(sAudioSfxParamChgWork[0] << 12) + sAudioSfxParamChgWork[1] + SFX_FLAG; + Audio_PlaySoundGeneral(sfx, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + Audio_StopSfxByBank(sAudioSfxParamChgWork[0]); + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CDOWN)) { + if (sAudioSfxParamChgSel == 2) { + gSoundParams[sAudioSfxParamChgWork[0]][sAudioSfxParamChgWork[1]].params ^= + (1 << (0xF - sAudioSfxParamChgBitSel)); + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_CUP)) { + if (sAudioSfxParamChgSel < 2) { + sAudioSfxParamChgWork[sAudioSfxParamChgSel] = 0; + } + } +} + +void AudioDebug_ScrPrt(const s8* str, u16 num) { + u8 i = 0; + + sAudioScrPrtBuf[sAudioScrPrtInd].num = num; + + while (str[i] != 0) { + sAudioScrPrtBuf[sAudioScrPrtInd].str[i] = str[i]; + i++; + } + + while (i < 5) { + sAudioScrPrtBuf[sAudioScrPrtInd].str[i] = 0; + i++; + } + + if (sAudioScrPrtInd < 25 - 1) { + sAudioScrPrtInd++; + } else { + sAudioScrPrtInd = 0; + sAudioScrPrtOverflow = 1; + } +} + +void AudioDebug_ProcessInput(void) { + if (!sAudioDebugEverOpened) { + return; + } + + if (sAudioSfxMuted) { + Audio_SetSoundBanksMute(0x6F); + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_L)) { + if (sAudioDebugPage < PAGE_MAX - 1) { + sAudioDebugPage++; + } else { + sAudioDebugPage = 0; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_R)) { + if (sAudioDebugPage > 0) { + sAudioDebugPage--; + } else { + sAudioDebugPage = PAGE_MAX - 1; + } + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_Z)) { + sAudioDebugTextColor++; + sAudioDebugTextColor &= 7; + } + + switch (sAudioDebugPage) { + case PAGE_NON: + if (CHECK_BTN_ANY(sDebugPadPress, BTN_A)) { + sAudioSndContWork[5] ^= 1; + Audio_SeqCmdE01(SEQ_PLAYER_BGM_MAIN, sAudioSndContWork[5]); + if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) != NA_BGM_NATURE_AMBIENCE) { + Audio_SeqCmd1(SEQ_PLAYER_BGM_MAIN, 0); + } + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, 0); + Audio_SeqCmd1(SEQ_PLAYER_BGM_SUB, 0); + } + + if (CHECK_BTN_ANY(sDebugPadPress, BTN_B)) { + sAudioSfxMuted ^= 1; + if (!sAudioSfxMuted) { + Audio_SetSoundBanksMute(0); + } + } + break; + case PAGE_SOUND_CONTROL: + AudioDebug_ProcessInput_SndCont(); + break; + case PAGE_INTERFACE_INFO: + AudioDebug_ProcessInput_IntInfo(); + break; + case PAGE_SCROLL_PRINT: + AudioDebug_ProcessInput_ScrPrt(); + break; + case PAGE_SFX_SWAP: + AudioDebug_ProcessInput_SfxSwap(); + break; + case PAGE_SUB_TRACK_INFO: + AudioDebug_ProcessInput_SubTrackInfo(); + break; + case PAGE_HEAP_INFO: + AudioDebug_ProcessInput_HeapInfo(); + break; + case PAGE_BLOCK_CHANGE_BGM: + AudioDebug_ProcessInput_BlkChgBgm(); + break; + case PAGE_OCARINA_TEST: + AudioDebug_ProcessInput_OcaTest(); + break; + case PAGE_SFX_PARAMETER_CHANGE: + AudioDebug_ProcessInput_SfxParamChg(); + break; + case PAGE_FREE_AREA: + default: + break; + } + + D_8013340C = sAudioScrPrtWork[10]; +} + +void func_800F4A70(void); +void func_800F5CF8(void); + +void func_800F3054(void) { + if (func_800FAD34() == 0) { + sAudioUpdateTaskStart = gAudioContext.totalTaskCnt; + sAudioUpdateStartTime = osGetTime(); + func_800EE6F4(); + Audio_StepFreqLerp(&sRiverFreqScaleLerp); + Audio_StepFreqLerp(&sWaterfallFreqScaleLerp); + func_800F4A70(); + func_800F56A8(); + func_800F5CF8(); + if (gAudioSpecId == 7) { + Audio_ClearSariaBgm(); + } + Audio_ProcessSoundRequests(); + Audio_ProcessSeqCmds(); + func_800F8F88(); + func_800FA3DC(); + AudioDebug_SetInput(); + AudioDebug_ProcessInput(); + Audio_ScheduleProcessCmds(); + sAudioUpdateTaskEnd = gAudioContext.totalTaskCnt; + sAudioUpdateEndTime = osGetTime(); + } +} + +void func_800F3138(UNK_TYPE arg0) { +} + +void func_800F3140(UNK_TYPE arg0, UNK_TYPE arg1) { +} + +void func_800F314C(s8 arg0) { + Audio_QueueCmdS32(0x82 << 24 | SEQ_PLAYER_BGM_MAIN << 16 | (((u8)arg0 & 0xFF) << 8), 1); +} + +f32 Audio_ComputeSoundVolume(u8 bankId, u8 entryIdx) { + SoundBankEntry* bankEntry = &gSoundBanks[bankId][entryIdx]; + f32 minDist; + f32 baseDist; + f32 ret; + + if (bankEntry->sfxParams & 0x2000) { + return 1.0f; + } + + if (bankEntry->dist > 10000.0f) { + ret = 0.0f; + } else { + switch (bankEntry->sfxParams & 3) { + case 1: + baseDist = 10000.0f / 15.0f; + break; + case 2: + baseDist = 10000.0f / 10.5f; + break; + case 3: + baseDist = 10000.0f / 2.6f; + break; + default: + baseDist = 10000.0f / 20.0f; + break; + } + + minDist = baseDist / 5.0f; + + // Volume grows as inverse square of distance. Linearly approximate + // the inverse part, then square. + if (bankEntry->dist < minDist) { + ret = 1.0f; + } else if (bankEntry->dist < baseDist) { + ret = ((((baseDist - minDist) - (bankEntry->dist - minDist)) / (baseDist - minDist)) * 0.19f) + 0.81f; + } else { + ret = (1.0f - ((bankEntry->dist - baseDist) / (10000.0f - baseDist))) * 0.81f; + } + ret = SQ(ret); + } + + return ret; +} + +s8 Audio_ComputeSoundReverb(u8 bankId, u8 entryIdx, u8 channelIdx) { + s8 distAdd = 0; + s32 scriptAdd = 0; + SoundBankEntry* entry = &gSoundBanks[bankId][entryIdx]; + s32 reverb; + + if (!(entry->sfxParams & 0x1000)) { + if (entry->dist < 2500.0f) { + distAdd = *entry->posZ > 0.0f ? (entry->dist / 2500.0f) * 70.0f : (entry->dist / 2500.0f) * 91.0f; + } else { + distAdd = 70; + } + } + + if (IS_SEQUENCE_CHANNEL_VALID(gAudioContext.seqPlayers[SEQ_PLAYER_SFX].channels[channelIdx])) { + scriptAdd = gAudioContext.seqPlayers[SEQ_PLAYER_SFX].channels[channelIdx]->soundScriptIO[1]; + if (gAudioContext.seqPlayers[SEQ_PLAYER_SFX].channels[channelIdx]->soundScriptIO[1] < 0) { + scriptAdd = 0; + } + } + + reverb = *entry->reverbAdd + distAdd + scriptAdd; + if ((bankId != BANK_OCARINA) || !((entry->sfxId & 0x1FF) < 2)) { + reverb += sAudioEnvReverb + sAudioCodeReverb + sSpecReverb; + } + + if (reverb > 0x7F) { + reverb = 0x7F; + } + + return reverb; +} + +s8 Audio_ComputeSoundPanSigned(f32 x, f32 z, u8 token) { + f32 absX; + f32 absZ; + f32 pan; + + if (x < 0) { + absX = -x; + } else { + absX = x; + } + if (z < 0) { + absZ = -z; + } else { + absZ = z; + } + + if (absX > 8000.0f) { + absX = 8000.0f; + } + + if (absZ > 8000.0f) { + absZ = 8000.0f; + } + + if ((x == 0.0f) && (z == 0.0f)) { + pan = 0.5f; + } else if (absZ <= absX) { + pan = (16000.0f - absX) / (3.3f * (16000.0f - absZ)); + if (x >= 0.0f) { + pan = 1.0f - pan; + } + } else { + pan = (x / (5.0769234f * absZ)) + 0.5f; // about 66 / 13 + } + + if (absZ < 50.0f) { + if (absX < 50.0f) { + pan = ((pan - 0.5f) * SQ(absX / 50.0f)) + 0.5f; + } + } + return (s8)((pan * 127.0f) + 0.5f); +} + +f32 Audio_ComputeSoundFreqScale(u8 bankId, u8 entryIdx) { + s32 phi_v0 = 0; + SoundBankEntry* entry = &gSoundBanks[bankId][entryIdx]; + f32 unk1C; + f32 freq = 1.0f; + + if (entry->sfxParams & 0x4000) { + freq = 1.0f - ((gAudioContext.audioRandom & 0xF) / 192.0f); + } + + switch (bankId) { + case BANK_PLAYER: + case BANK_ITEM: + case BANK_VOICE: + if (sAudioBaseFilter2 != 0) { + phi_v0 = 1; + } + break; + case BANK_ENV: + case BANK_ENEMY: + if (sAudioExtraFilter2 != 0) { + phi_v0 = 1; + } + break; + case BANK_SYSTEM: + case BANK_OCARINA: + break; + } + + if (phi_v0 == 1) { + if (!(entry->sfxParams & 0x800)) { + freq *= (1.0293 - ((gAudioContext.audioRandom & 0xF) / 144.0f)); + } + } + + unk1C = entry->dist; + if (!(entry->sfxParams & 0x2000)) { + if (!(entry->sfxParams & 0x8000)) { + if (unk1C >= 10000.0f) { + freq += 0.2f; + } else { + freq += (0.2f * (unk1C / 10000.0f)); + } + } + } + + if (entry->sfxParams & 0xC0) { + freq += (entry->unk_2F / 192.0f); + } + + return freq; +} + +u8 func_800F37B8(f32 behindScreenZ, SoundBankEntry* arg1, s8 arg2) { + s8 phi_v0; + u8 phi_v1; + f32 phi_f0; + f32 phi_f12; + + if (*arg1->posZ < behindScreenZ) { + phi_v0 = arg2 < 65 ? arg2 : 0x7F - arg2; + + if (phi_v0 < 30) { + phi_v1 = 0; + } else { + phi_v1 = (((phi_v0 & 0xFFFF) * 10) - 300) / 34; + if (phi_v1 != 0) { + phi_v1 = 0x10 - phi_v1; + } + } + } else { + phi_v1 = 0; + } + + if (phi_v1 == 0) { + if (arg1->sfxParams & 0x200) { + phi_v1 = 0xF; + } + } + + switch (arg1->sfxParams & 3) { + case 1: + phi_f0 = 12.0f; + break; + case 2: + phi_f0 = 9.0f; + break; + case 3: + phi_f0 = 6.0f; + break; + default: + phi_f0 = 15.0f; + break; + } + + phi_f12 = CLAMP_MAX(arg1->dist, 10000.0f / 5.2f); + + return (phi_v1 * 0x10) + (u8)((phi_f0 * phi_f12) / (10000.0f / 5.2f)); +} + +s8 func_800F3990(f32 arg0, u16 sfxParams) { + s8 ret = 0; + + if (arg0 >= 0.0f) { + if (arg0 > 625.0f) { + ret = 127; + } else { + ret = (arg0 / 625.0f) * 126.0f; + } + } + return ret | 1; +} + +void Audio_SetSoundProperties(u8 bankId, u8 entryIdx, u8 channelIdx) { + f32 vol = 1.0f; + s8 volS8; + s8 reverb = 0; + f32 freqScale = 1.0f; + s8 panSigned = 0x40; + u8 stereoBits = 0; + u8 filter = 0; + s8 sp38 = 0; + f32 behindScreenZ; + u8 baseFilter = 0; + SoundBankEntry* entry = &gSoundBanks[bankId][entryIdx]; + + switch (bankId) { + case BANK_PLAYER: + case BANK_ITEM: + case BANK_ENV: + case BANK_ENEMY: + case BANK_VOICE: + if (D_80130604 == 2) { + sp38 = func_800F3990(*entry->posY, entry->sfxParams); + } + // fallthrough + case BANK_OCARINA: + entry->dist = sqrtf(entry->dist); + vol = Audio_ComputeSoundVolume(bankId, entryIdx) * *entry->vol; + reverb = Audio_ComputeSoundReverb(bankId, entryIdx, channelIdx); + panSigned = Audio_ComputeSoundPanSigned(*entry->posX, *entry->posZ, entry->token); + freqScale = Audio_ComputeSoundFreqScale(bankId, entryIdx) * *entry->freqScale; + if (D_80130604 == 2) { + behindScreenZ = sBehindScreenZ[(entry->sfxParams & 0x400) >> 10]; + if (!(entry->sfxParams & 0x800)) { + if (*entry->posZ < behindScreenZ) { + stereoBits = 0x10; + } + + if ((sSfxChannelState[channelIdx].stereoBits ^ stereoBits) & 0x10) { + if (panSigned < 0x40) { + stereoBits = sSfxChannelState[channelIdx].stereoBits ^ 0x14; + } else { + stereoBits = sSfxChannelState[channelIdx].stereoBits ^ 0x18; + } + } else { + stereoBits = sSfxChannelState[channelIdx].stereoBits; + } + } + } + if (sAudioBaseFilter != 0) { + if ((bankId == BANK_ITEM) || (bankId == BANK_PLAYER) || (bankId == BANK_VOICE)) { + baseFilter = sAudioBaseFilter; + } + } + + if ((baseFilter | sAudioExtraFilter) != 0) { + filter = (baseFilter | sAudioExtraFilter); + } else if (D_80130604 == 2 && (entry->sfxParams & 0x2000) == 0) { + filter = func_800F37B8(behindScreenZ, entry, panSigned); + } + break; + case BANK_SYSTEM: + break; + } + + if (sSfxChannelState[channelIdx].vol != vol) { + volS8 = (u8)(vol * 127.0f); + sSfxChannelState[channelIdx].vol = vol; + } else { + volS8 = -1; + } + + // CHAN_UPD_SCRIPT_IO (slot 2, sets volume) + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8) | 2, volS8); + if (reverb != sSfxChannelState[channelIdx].reverb) { + Audio_QueueCmdS8(0x5 << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8), reverb); + sSfxChannelState[channelIdx].reverb = reverb; + } + if (freqScale != sSfxChannelState[channelIdx].freqScale) { + Audio_QueueCmdF32(0x4 << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8), freqScale); + sSfxChannelState[channelIdx].freqScale = freqScale; + } + if (stereoBits != sSfxChannelState[channelIdx].stereoBits) { + Audio_QueueCmdS8(0xE << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8), stereoBits | 0x10); + sSfxChannelState[channelIdx].stereoBits = stereoBits; + } + if (filter != sSfxChannelState[channelIdx].filter) { + // CHAN_UPD_SCRIPT_IO (slot 3, sets filter) + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8) | 3, filter); + sSfxChannelState[channelIdx].filter = filter; + } + if (sp38 != sSfxChannelState[channelIdx].unk_0C) { + // CHAN_UPD_UNK_0F + Audio_QueueCmdS8(0xC << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8), 0x10); + // CHAN_UPD_UNK_20 + Audio_QueueCmdU16(0xD << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8), ((u16)(sp38) << 8) + 0xFF); + sSfxChannelState[channelIdx].unk_0C = sp38; + } + if (panSigned != sSfxChannelState[channelIdx].panSigned) { + Audio_QueueCmdS8(0x3 << 24 | SEQ_PLAYER_SFX << 16 | (channelIdx << 8), panSigned); + sSfxChannelState[channelIdx].panSigned = panSigned; + } +} + +void Audio_ResetSfxChannelState(void) { + u8 i; + SfxPlayerState* state; + + for (i = 0; i < 16; i++) { + state = &sSfxChannelState[i]; + state->vol = 1.0f; + state->freqScale = 1.0f; + state->reverb = 0; + state->panSigned = 0x40; + state->stereoBits = 0; + state->filter = 0xFF; + state->unk_0C = 0xFF; + } + + sSfxChannelState[SFX_PLAYER_CHANNEL_OCARINA].unk_0C = 0; + sPrevSeqMode = 0; + sAudioCodeReverb = 0; +} + +void func_800F3F3C(u8 arg0) { + if (gSoundBankMuted[0] != 1) { + Audio_StartSeq(SEQ_PLAYER_BGM_SUB, 0, NA_BGM_VARIOUS_SFX); + Audio_SeqCmd8(SEQ_PLAYER_BGM_SUB, 0, 0, arg0); + } +} + +f32 func_800F3F84(f32 arg0) { + f32 ret = 1.0f; + + if (arg0 > 6.0f) { + D_8016B7A8 = 1.0f; + D_8016B7B0 = 1.1f; + } else { + ret = arg0 / 6.0f; + D_8016B7A8 = (ret * 0.22500002f) + 0.775f; + D_8016B7B0 = (ret * 0.2f) + 0.9f; + } + return ret; +} + +void func_800F4010(Vec3f* pos, u16 sfxId, f32 arg2) { + f32 sp24; + f32 phi_f0; + u8 phi_v0; + u16 sfxId2; + + D_80131C8C = arg2; + sp24 = func_800F3F84(arg2); + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_8016B7B0, &D_8016B7A8, &D_801333E8); + + if ((sfxId & 0xF0) == 0xB0) { + phi_f0 = 0.3f; + phi_v0 = 1; + sp24 = 1.0f; + } else { + phi_f0 = 1.1f; + phi_v0 = gAudioContext.audioRandom % 2; + } + + if ((phi_f0 < arg2) && (phi_v0 != 0)) { + if ((sfxId & 0x80) != 0) { + sfxId2 = NA_SE_PL_METALEFFECT_ADULT; + } else { + sfxId2 = NA_SE_PL_METALEFFECT_KID; + } + D_8016B7AC = (sp24 * 0.7) + 0.3; + Audio_PlaySoundGeneral(sfxId2, pos, 4, &D_8016B7B0, &D_8016B7AC, &D_801333E8); + } +} + +void func_800F4138(Vec3f* pos, u16 sfxId, f32 arg2) { + func_800F3F84(arg2); + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_8016B7B0, &D_8016B7A8, &D_801333E8); +} + +void func_800F4190(Vec3f* pos, u16 sfxId) { + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_801305B0, &D_801333E0, &D_801305B4); +} +void Audio_PlaySoundRandom(Vec3f* pos, u16 baseSfxId, u8 randLim) { + u8 offset = Audio_NextRandom() % randLim; + + Audio_PlaySoundGeneral(baseSfxId + offset, pos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void func_800F4254(Vec3f* pos, u8 level) { + level &= 3; + if (level != sPrevChargeLevel) { + D_801305F4 = D_801305E4[level]; + switch (level) { + case 1: + Audio_PlaySoundGeneral(NA_SE_PL_SWORD_CHARGE, pos, 4, &D_801305F4, &D_801333E0, &D_801333E8); + break; + case 2: + Audio_PlaySoundGeneral(NA_SE_PL_SWORD_CHARGE, pos, 4, &D_801305F4, &D_801333E0, &D_801333E8); + break; + } + + sPrevChargeLevel = level; + } + + if (level != 0) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_CHARGE - SFX_FLAG, pos, 4, &D_801305F4, &D_801333E0, &D_801333E8); + } +} + +void func_800F436C(Vec3f* pos, u16 sfxId, f32 arg2) { + if (arg2 < 0.75f) { + D_8016B7D8 = ((arg2 / 0.75f) * 0.25f) + 0.5f; + } else { + D_8016B7D8 = arg2; + } + + if (D_8016B7D8 > 0.5f) { + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_8016B7D8, &D_801333E0, &D_801333E8); + } +} + +void func_800F4414(Vec3f* pos, u16 sfxId, f32 arg2) { + D_801305B8--; + if (D_801305B8 == 0) { + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_8016B7D8, &D_801333E0, &D_801333E8); + + if (arg2 > 2.0f) { + arg2 = 2.0f; + } + D_801305B8 = (s8)((D_801305C0 - D_801305BC) * (1.0f - arg2)) + D_801305C0; + } +} + +void func_800F44EC(s8 arg0, s8 arg1) { + D_801305B8 = 1; + D_801305BC = arg1; + D_801305C0 = arg0; +} + +void func_800F4524(Vec3f* pos, u16 sfxId, s8 arg2) { + D_8016B7DC = arg2; + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_801333E0, &D_801333E0, &D_8016B7DC); +} + +void func_800F4578(Vec3f* pos, u16 sfxId, f32 arg2) { + D_8016B7E0 = arg2; + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_801333E0, &D_8016B7E0, &D_801333E8); +} + +void func_800F45D0(f32 arg0) { + func_800F4414(&D_801333D4, NA_SE_IT_FISHING_REEL_SLOW - SFX_FLAG, arg0); + func_800F436C(&D_801333D4, 0, (0.15f * arg0) + 1.4f); +} + +void Audio_PlaySoundRiver(Vec3f* pos, f32 freqScale) { + if (!Audio_IsSfxPlaying(NA_SE_EV_RIVER_STREAM - SFX_FLAG)) { + sRiverFreqScaleLerp.value = freqScale; + } else if (freqScale != sRiverFreqScaleLerp.value) { + sRiverFreqScaleLerp.target = freqScale; + sRiverFreqScaleLerp.remainingFrames = 40; + sRiverFreqScaleLerp.step = (sRiverFreqScaleLerp.target - sRiverFreqScaleLerp.value) / 40; + } + Audio_PlaySoundGeneral(NA_SE_EV_RIVER_STREAM - SFX_FLAG, pos, 4, &sRiverFreqScaleLerp.value, &D_801333E0, + &D_801333E8); +} + +void Audio_PlaySoundWaterfall(Vec3f* pos, f32 freqScale) { + if (!Audio_IsSfxPlaying(NA_SE_EV_WATER_WALL_BIG - SFX_FLAG)) { + sWaterfallFreqScaleLerp.value = freqScale; + } else if (freqScale != sWaterfallFreqScaleLerp.value) { + sWaterfallFreqScaleLerp.target = freqScale; + sWaterfallFreqScaleLerp.remainingFrames = 40; + sWaterfallFreqScaleLerp.step = (sWaterfallFreqScaleLerp.target - sWaterfallFreqScaleLerp.value) / 40; + } + Audio_PlaySoundGeneral(NA_SE_EV_WATER_WALL_BIG - SFX_FLAG, pos, 4, &sWaterfallFreqScaleLerp.value, + &sWaterfallFreqScaleLerp.value, &D_801333E8); +} + +void Audio_StepFreqLerp(FreqLerp* lerp) { + if (lerp->remainingFrames != 0) { + lerp->remainingFrames--; + if (lerp->remainingFrames != 0) { + lerp->value += lerp->step; + } else { + lerp->value = lerp->target; + } + } +} + +void func_800F47BC(void) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 1, 0, 10); + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 1, 0, 10); +} + +void func_800F47FC(void) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 1, 0x7F, 3); + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 1, 0x7F, 3); +} + +void func_800F483C(u8 targetVol, u8 volFadeTimer) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, targetVol, volFadeTimer); +} + +void func_800F4870(u8 arg0) { + s8 pan; + u8 i; + + pan = 0; + if (arg0 == 0) { + pan = 0x7F; + } + + for (i = 0; i < 16; i++) { + // CHAN_UPD_PAN_UNSIGNED + Audio_QueueCmdS8( + _SHIFTL(0x7, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) | _SHIFTL(i, 8, 8) | _SHIFTL(0, 0, 8), pan); + } + + if (arg0 == 7) { + D_80130600 = 2; + } else { + Audio_SetGanonDistVol(D_801305F8[arg0 & 7]); + } +} + +// (name derived from debug strings, should probably update. used in ganon/ganon_boss scenes) +s32 Audio_SetGanonDistVol(u8 targetVol) +{ + u8 phi_v0; + u16 phi_v0_2; + u8 i; + + if (sAudioGanonDistVol != targetVol) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, targetVol, 2); + if (targetVol < 0x40) { + phi_v0 = 0x10; + } else { + phi_v0 = (((targetVol - 0x40) >> 2) + 1) << 4; + } + + Audio_SeqCmd8(SEQ_PLAYER_BGM_MAIN, 4, 15, phi_v0); + for (i = 0; i < 0x10; i++) { + if (gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[i] != &gAudioContext.sequenceChannelNone) { + if ((u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[i]->soundScriptIO[5] != 0xFF) { + // this looks like some kind of macro? + phi_v0_2 = + ((u16)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[i]->soundScriptIO[5] - targetVol) + + 0x7F; + if (phi_v0_2 >= 0x80) { + phi_v0_2 = 0x7F; + } + // CHAN_UPD_REVERB + Audio_QueueCmdS8(_SHIFTL(0x5, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) | _SHIFTL(i, 8, 8) | + _SHIFTL(0, 0, 8), + (u8)phi_v0_2); + } + } + } + sAudioGanonDistVol = targetVol; + } + return -1; +} + +void func_800F4A54(u8 arg0) { + D_8016B8B0 = arg0; + D_8016B8B2 = 1; +} + +void func_800F4A70(void) { + if (D_8016B8B2 == 1) { + if (D_8016B8B1 != D_8016B8B0) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, D_8016B8B0, 0xA); + D_8016B8B1 = D_8016B8B0; + D_8016B8B3 = 1; + } + D_8016B8B2 = 0; + } else if (D_8016B8B3 == 1 && D_80130608 == 0) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, 0x7F, 0xA); + D_8016B8B1 = 0x7F; + D_8016B8B3 = 0; + } + + if (D_80130600 != 0) { + D_80130600--; + if (D_80130600 == 0) { + Audio_SetGanonDistVol(D_801305F8[7]); + } + } +} + +void Audio_PlaySoundIncreasinglyTransposed(Vec3f* pos, s16 sfxId, u8* semitones) { + Audio_PlaySoundGeneral(sfxId, pos, 4, &gNoteFrequencies[semitones[sAudioIncreasingTranspose] + 39], &D_801333E0, + &D_801333E8); + + if (sAudioIncreasingTranspose < 15) { + sAudioIncreasingTranspose++; + } +} + +void Audio_ResetIncreasingTranspose(void) { + sAudioIncreasingTranspose = 0; +} + +void Audio_PlaySoundTransposed(Vec3f* pos, u16 sfxId, s8 semitone) { + Audio_PlaySoundGeneral(sfxId, pos, 4, &gNoteFrequencies[semitone + 39], &D_801333E0, &D_801333E8); +} + +void func_800F4C58(Vec3f* pos, u16 sfxId, u8 arg2) { + u8 phi_s1 = 0; + u8 i; + u8 bankId; + + bankId = SFX_BANK_SHIFT(sfxId); + for (i = 0; i < bankId; i++) { + phi_s1 += gChannelsPerBank[gSfxChannelLayout][i]; + } + + for (i = 0; i < gChannelsPerBank[gSfxChannelLayout][bankId]; i++) { + if ((gActiveSounds[bankId][i].entryIndex != 0xFF) && + (sfxId == gSoundBanks[bankId][gActiveSounds[bankId][i].entryIndex].sfxId)) { + Audio_QueueCmdS8( + _SHIFTL(0x6, 24, 8) | _SHIFTL(SEQ_PLAYER_SFX, 16, 8) | _SHIFTL(phi_s1, 8, 8) | _SHIFTL(6, 0, 8), arg2); + } + phi_s1++; + } + Audio_PlaySoundGeneral(sfxId, pos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void func_800F4E30(Vec3f* pos, f32 arg1) { + f32 phi_f22; + s8 phi_s4; + u8 i; + + if (sSariaBgmPtr == NULL) { + sSariaBgmPtr = pos; + D_80130650 = arg1; + } else if (pos != sSariaBgmPtr) { + if (arg1 < D_80130650) { + sSariaBgmPtr = pos; + D_80130650 = arg1; + } + } else { + D_80130650 = arg1; + } + + if (sSariaBgmPtr->x > 100.0f) { + phi_s4 = 0x7F; + } else if (sSariaBgmPtr->x < -100.0f) { + phi_s4 = 0; + } else { + phi_s4 = ((sSariaBgmPtr->x / 100.0f) * 64.0f) + 64.0f; + } + + if (D_80130650 > 400.0f) { + phi_f22 = 0.1f; + } else if (D_80130650 < 120.0f) { + phi_f22 = 1.0f; + } else { + phi_f22 = ((1.0f - ((D_80130650 - 120.0f) / 280.0f)) * 0.9f) + 0.1f; + } + + for (i = 0; i < 0x10; i++) { + if (i != 9) { + Audio_SeqCmd6(SEQ_PLAYER_BGM_MAIN, 2, i, (127.0f * phi_f22)); + Audio_QueueCmdS8(0x3 << 24 | SEQ_PLAYER_BGM_MAIN << 16 | ((u8)((u32)i) << 8), phi_s4); + } + } +} + +void Audio_ClearSariaBgm(void) { + if (sSariaBgmPtr != NULL) { + sSariaBgmPtr = NULL; + } +} + +void Audio_ClearSariaBgmAtPos(Vec3f* pos) { + if (sSariaBgmPtr == pos) { + sSariaBgmPtr = NULL; + } +} + +/** + * Turns on and off channels from both bgm players in a way that splits + * equally between the two bgm channels. Split based on note priority + */ +void Audio_SplitBgmChannels(s8 volSplit) { + u8 volume; + u8 notePriority; + u16 channelBits; + u8 bgmPlayers[2] = { SEQ_PLAYER_BGM_MAIN, SEQ_PLAYER_BGM_SUB }; + u8 channelIdx; + u8 i; + + if ((func_800FA0B4(SEQ_PLAYER_FANFARE) == NA_BGM_DISABLED) && + (func_800FA0B4(SEQ_PLAYER_BGM_SUB) != NA_BGM_LONLON)) { + for (i = 0; i < ARRAY_COUNT(bgmPlayers); i++) { + if (i == 0) { + // Main Bgm SeqPlayer + volume = volSplit; + } else { + // Sub Bgm SeqPlayer + volume = 0x7F - volSplit; + } + + if (volume > 100) { + notePriority = 11; + } else if (volume < 20) { + notePriority = 2; + } else { + notePriority = ((volume - 20) / 10) + 2; + } + + channelBits = 0; + for (channelIdx = 0; channelIdx < 16; channelIdx++) { + if (notePriority > gAudioContext.seqPlayers[bgmPlayers[i]].channels[channelIdx]->notePriority) { + // If the note currently playing in the channel is a high enough priority, + // then keep the channel on by setting a channelBit + // If this condition fails, then the channel will be shut off + channelBits += (1 << channelIdx); + } + } + + Audio_SeqCmdA(bgmPlayers[i], channelBits); + } + } +} + +void Audio_PlaySariaBgm(Vec3f* pos, u16 seqId, u16 distMax) { + f32 absY; + f32 dist; + u8 vol; + f32 prevDist; + + if (D_8016B9F3 != 0) { + D_8016B9F3--; + return; + } + + dist = sqrtf(SQ(pos->z) + SQ(pos->x)); + if (sSariaBgmPtr == NULL) { + sSariaBgmPtr = pos; + func_800F5E18(SEQ_PLAYER_BGM_SUB, seqId, 0, 7, 2); + } else { + prevDist = sqrtf(SQ(sSariaBgmPtr->z) + SQ(sSariaBgmPtr->x)); + if (dist < prevDist) { + sSariaBgmPtr = pos; + } else { + dist = prevDist; + } + } + + if (pos->y < 0.0f) { + absY = -pos->y; + } else { + absY = pos->y; + } + + if ((distMax / 15.0f) < absY) { + vol = 0; + } else if (dist < distMax) { + vol = (1.0f - (dist / distMax)) * 127.0f; + } else { + vol = 0; + } + + if (seqId != NA_BGM_GREAT_FAIRY) { + Audio_SplitBgmChannels(vol); + } + + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 3, vol, 0); + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 3, 0x7F - vol, 0); +} + +void Audio_ClearSariaBgm2(void) { + sSariaBgmPtr = NULL; +} + +void func_800F5510(u16 seqId) { + func_800F5550(seqId); + func_800F5E18(SEQ_PLAYER_BGM_MAIN, seqId, 0, 0, 1); +} + +void func_800F5550(u16 seqId) { + u8 sp27 = 0; + u16 nv; + + if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) != NA_BGM_WINDMILL) { + if (func_800FA0B4(SEQ_PLAYER_BGM_SUB) == NA_BGM_LONLON) { + func_800F9474(SEQ_PLAYER_BGM_SUB, 0); + Audio_QueueCmdS32(0xF8000000, 0); + } + + if ((sSeqFlags[D_80130630] & 0x20) && sSeqFlags[(seqId & 0xFF) & 0xFF] & 0x10) { + + if ((D_8013062C & 0x3F) != 0) { + sp27 = 0x1E; + } + + func_800F5E18(SEQ_PLAYER_BGM_MAIN, seqId, sp27, 7, D_8013062C); + + D_8013062C = 0; + } else { + nv = (sSeqFlags[(seqId & 0xFF) & 0xFF] & 0x40) ? 1 : 0xFF; + func_800F5E18(SEQ_PLAYER_BGM_MAIN, seqId, 0, 7, nv); + if (!(sSeqFlags[seqId] & 0x20)) { + D_8013062C = 0xC0; + } + } + D_80130630 = seqId & 0xFF; + } +} + +void func_800F56A8(void) { + u16 temp_v0; + u8 bvar; + + temp_v0 = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + bvar = temp_v0 & 0xFF; + if ((temp_v0 != NA_BGM_DISABLED) && (sSeqFlags[bvar] & 0x10)) { + if (D_8013062C != 0xC0) { + D_8013062C = gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].soundScriptIO[3]; + } else { + D_8013062C = 0; + } + } +} + +void func_800F5718(void) { + if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) != NA_BGM_WINDMILL) { + Audio_StartSeq(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_WINDMILL); + } +} + +void func_800F574C(f32 arg0, u8 arg2) { + if (arg0 == 1.0f) { + Audio_SeqCmdB40(SEQ_PLAYER_BGM_MAIN, arg2, 0); + } else { + Audio_SeqCmdC(SEQ_PLAYER_FANFARE, 0x30, arg2, arg0 * 100.0f); + } + Audio_SeqCmdC(SEQ_PLAYER_FANFARE, 0xA0, arg2, arg0 * 100.0f); +} + +void func_800F5918(void) { + if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) == NA_BGM_TIMED_MINI_GAME && func_800FA11C(0, 0xF0000000)) { + Audio_SeqCmdB(SEQ_PLAYER_BGM_MAIN, 5, 0, 0xD2); + } +} + +void func_800F595C(u16 arg0) { + u8 arg0b = arg0 & 0xFF; + + if (sSeqFlags[arg0b] & 2) { + Audio_PlayFanfare(arg0); + } else if (sSeqFlags[arg0b] & 4) { + Audio_StartSeq(SEQ_PLAYER_FANFARE, 0, arg0); + + } else { + func_800F5E18(SEQ_PLAYER_BGM_MAIN, arg0, 0, 7, -1); + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, 0); + } +} + +void func_800F59E8(u16 arg0) { + u8 arg0b = arg0 & 0xFF; + + if (sSeqFlags[arg0b] & 2) { + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, 0); + } else if (sSeqFlags[arg0b] & 4) { + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, 0); + } else { + Audio_SeqCmd1(SEQ_PLAYER_BGM_MAIN, 0); + } +} + +s32 func_800F5A58(u8 arg0) { + u8 phi_a1 = 0; + + if (sSeqFlags[arg0 & 0xFF] & 2) { + phi_a1 = 1; + } else if (sSeqFlags[arg0 & 0xFF] & 4) { + phi_a1 = 1; + } + + if (arg0 == (u8)func_800FA0B4(phi_a1)) { + return 1; + } else { + return 0; + } +} + +/** + * Plays a sequence on the main bgm player, but stores the previous sequence to return to later + * Designed for the mini-boss sequence, but also used by mini-game 2 sequence + */ +void func_800F5ACC(u16 seqId) { + u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + + if ((curSeqId & 0xFF) != NA_BGM_GANON_TOWER && (curSeqId & 0xFF) != NA_BGM_ESCAPE && curSeqId != seqId) { + Audio_SetSequenceMode(SEQ_MODE_IGNORE); + if (curSeqId != NA_BGM_DISABLED) { + sPrevMainBgmSeqId = curSeqId; + } else { + osSyncPrintf("Middle Boss BGM Start not stack \n"); + } + + Audio_StartSeq(SEQ_PLAYER_BGM_MAIN, 0, seqId); + } +} + +/** + * Restores the previous sequence to the main bgm player before func_800F5ACC was called + */ +void func_800F5B58(void) { + if ((func_800FA0B4(SEQ_PLAYER_BGM_MAIN) != NA_BGM_DISABLED) && (sPrevMainBgmSeqId != NA_BGM_DISABLED) && + (sSeqFlags[func_800FA0B4(SEQ_PLAYER_BGM_MAIN) & 0xFF] & 8)) { + if (sPrevMainBgmSeqId == NA_BGM_DISABLED) { + Audio_SeqCmd1(SEQ_PLAYER_BGM_MAIN, 0); + } else { + Audio_StartSeq(SEQ_PLAYER_BGM_MAIN, 0, sPrevMainBgmSeqId); + } + + sPrevMainBgmSeqId = NA_BGM_DISABLED; + } +} + +/** + * Plays the nature ambience sequence on the main bgm player, but stores the previous sequence to return to later + */ +void func_800F5BF0(u8 natureAmbienceId) { + u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + + if (curSeqId != NA_BGM_NATURE_AMBIENCE) { + sPrevMainBgmSeqId = curSeqId; + } + + Audio_PlayNatureAmbienceSequence(natureAmbienceId); +} + +/** + * Restores the previous sequence to the main bgm player before func_800F5BF0 was called + */ +void func_800F5C2C(void) { + if (sPrevMainBgmSeqId != NA_BGM_DISABLED) { + Audio_StartSeq(SEQ_PLAYER_BGM_MAIN, 0, sPrevMainBgmSeqId); + } + sPrevMainBgmSeqId = NA_BGM_DISABLED; +} + +void Audio_PlayFanfare(u16 seqId) +{ + u16 sp26; + u32 sp20; + u8* sp1C; + u8* sp18; + + sp26 = func_800FA0B4(SEQ_PLAYER_FANFARE); + sp1C = func_800E5E84(sp26 & 0xFF, &sp20); + sp18 = func_800E5E84(seqId & 0xFF, &sp20); + if ((sp26 == NA_BGM_DISABLED) || (*sp1C == *sp18)) { + D_8016B9F4 = 1; + } else { + D_8016B9F4 = 5; + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, 0); + } + D_8016B9F6 = seqId; +} + +void func_800F5CF8(void) { + u16 sp26; + u16 pad; + u16 sp22; + + if (D_8016B9F4 != 0) { + D_8016B9F4--; + if (D_8016B9F4 == 0) { + Audio_QueueCmdS32(0xE3000000, SEQUENCE_TABLE); + Audio_QueueCmdS32(0xE3000000, FONT_TABLE); + func_800FA0B4(SEQ_PLAYER_BGM_MAIN); + sp26 = func_800FA0B4(SEQ_PLAYER_FANFARE); + sp22 = func_800FA0B4(SEQ_PLAYER_BGM_SUB); + if (sp26 == NA_BGM_DISABLED) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 1, 0, 5); + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 1, 0, 5); + Audio_SeqCmdC(SEQ_PLAYER_FANFARE, 0x80, 1, 0xA); + Audio_SeqCmdC(SEQ_PLAYER_FANFARE, 0x83, 1, 0xA); + Audio_SeqCmdC(SEQ_PLAYER_FANFARE, 0x90, 0, 0); + if (sp22 != NA_BGM_LONLON) { + Audio_SeqCmdC(SEQ_PLAYER_FANFARE, 0x93, 0, 0); + } + } + Audio_StartSeq(SEQ_PLAYER_FANFARE, 1, D_8016B9F6); + Audio_SeqCmdA(0, 0xFFFF); + if (sp22 != NA_BGM_LONLON) { + Audio_SeqCmdA(SEQ_PLAYER_BGM_SUB, 0xFFFF); + } + } + } +} + +void func_800F5E18(u8 playerIdx, u16 seqId, u8 fadeTimer, s8 arg3, s8 arg4) { + Audio_SeqCmd7(playerIdx, arg3, arg4); + Audio_StartSeq(playerIdx, fadeTimer, seqId); +} + +void Audio_SetSequenceMode(u8 seqMode) { + s32 volumeFadeInTimer; + u16 seqId; + u8 volumeFadeOutTimer; + + sSeqModeInput = seqMode; + if (sPrevMainBgmSeqId == NA_BGM_DISABLED) { + if (sAudioCutsceneFlag) { + seqMode = SEQ_MODE_IGNORE; + } + + seqId = D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254; + + if (seqId == NA_BGM_FIELD_LOGIC && func_800FA0B4(SEQ_PLAYER_BGM_SUB) == (NA_BGM_ENEMY | 0x800)) { + seqMode = SEQ_MODE_IGNORE; + } + + if ((seqId == NA_BGM_DISABLED) || (sSeqFlags[(u8)(seqId & 0xFF)] & 1) || + ((sPrevSeqMode & 0x7F) == SEQ_MODE_ENEMY)) { + if (seqMode != (sPrevSeqMode & 0x7F)) { + if (seqMode == SEQ_MODE_ENEMY) { + // Start playing enemy bgm + if (D_8016E750[SEQ_PLAYER_BGM_SUB].volScales[1] - sAudioEnemyVol < 0) { + volumeFadeInTimer = -(D_8016E750[SEQ_PLAYER_BGM_SUB].volScales[1] - sAudioEnemyVol); + } else { + volumeFadeInTimer = D_8016E750[SEQ_PLAYER_BGM_SUB].volScales[1] - sAudioEnemyVol; + } + + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 3, sAudioEnemyVol, volumeFadeInTimer); + Audio_StartSeq(SEQ_PLAYER_BGM_SUB, 10, NA_BGM_ENEMY | 0x800); + + if (seqId != NA_BGM_NATURE_AMBIENCE) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 3, (0x7F - sAudioEnemyVol) & 0xFF, 0xA); + Audio_SplitBgmChannels(sAudioEnemyVol); + } + } else if ((sPrevSeqMode & 0x7F) == SEQ_MODE_ENEMY) { + // Stop playing enemy bgm + Audio_SeqCmd1(SEQ_PLAYER_BGM_SUB, 10); + if (seqMode == SEQ_MODE_IGNORE) { + volumeFadeOutTimer = 0; + } else { + volumeFadeOutTimer = 10; + } + + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 3, 0x7F, volumeFadeOutTimer); + Audio_SplitBgmChannels(0); + } + + sPrevSeqMode = seqMode + 0x80; + } + } else { + // Hyrule Field will play slightly different bgm music depending on whether player is standing + // still or moving. This is the logic to determine the transition between those two states + if (seqMode == SEQ_MODE_DEFAULT) { + if (sPrevSeqMode == SEQ_MODE_STILL) { + sNumFramesMoving = 0; + } + sNumFramesStill = 0; + sNumFramesMoving++; + } else { + sNumFramesStill++; + } + + if (seqMode == SEQ_MODE_STILL && sNumFramesStill < 30 && sNumFramesMoving > 20) { + seqMode = SEQ_MODE_DEFAULT; + } + + sPrevSeqMode = seqMode; + Audio_SeqCmd7(SEQ_PLAYER_BGM_MAIN, 2, seqMode); + } + } +} + +void Audio_SetBgmEnemyVolume(f32 dist) { + f32 adjDist; + + if (sPrevSeqMode == (0x80 | SEQ_MODE_ENEMY)) { + if (dist != sAudioEnemyDist) { + if (dist < 150.0f) { + adjDist = 0.0f; + } else if (dist > 500.0f) { + adjDist = 350.0f; + } else { + adjDist = dist - 150.0f; + } + + sAudioEnemyVol = ((350.0f - adjDist) * 127.0f) / 350.0f; + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 3, sAudioEnemyVol, 10); + if (D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254 != NA_BGM_NATURE_AMBIENCE) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 3, (0x7F - sAudioEnemyVol), 10); + } + } + if (D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254 != NA_BGM_NATURE_AMBIENCE) { + Audio_SplitBgmChannels(sAudioEnemyVol); + } + } + sAudioEnemyDist = dist; +} + +void func_800F6268(f32 dist, u16 arg1) { + s8 pad; + s8 phi_v1; + s16 temp_a0; + + sAudioHasMalonBgm = true; + sAudioMalonBgmDist = dist; + if (D_8016B9F2 == 0) { + temp_a0 = (s8)(func_800FA0B4(SEQ_PLAYER_BGM_MAIN) & 0xFF); + if (temp_a0 == (arg1 & 0xFF)) { + if ((arg1 & 0xFF) == NA_BGM_LONLON) { + + if (dist > 2000.0f) { + phi_v1 = 127; + } else if (dist < 200.0f) { + phi_v1 = 0; + } else { + phi_v1 = (s8)(((dist - 200.0f) * 127.0f) / 1800.0f); + } + // Transition volume of channels 0, 1 and 13 on seq player 0 over 3 frames + Audio_SeqCmd6(SEQ_PLAYER_BGM_MAIN, 3, 0, 127 - phi_v1); + Audio_SeqCmd6(SEQ_PLAYER_BGM_MAIN, 3, 1, 127 - phi_v1); + Audio_SeqCmd6(SEQ_PLAYER_BGM_MAIN, 3, 13, phi_v1); + if (D_8016B9D8 == 0) { + D_8016B9D8++; + } + } + } else if ((temp_a0 == NA_BGM_NATURE_AMBIENCE) && ((arg1 & 0xFF) == NA_BGM_LONLON)) { + temp_a0 = (s8)(func_800FA0B4(SEQ_PLAYER_BGM_SUB) & 0xFF); + if ((temp_a0 != (arg1 & 0xFF)) && (D_8016B9D8 < 10)) { + func_800F5E18(SEQ_PLAYER_BGM_SUB, NA_BGM_LONLON, 0, 0, 0); + Audio_SeqCmdA(SEQ_PLAYER_BGM_SUB, 0xFFFC); + D_8016B9D8 = 10; + } + + if (dist > 2000.0f) { + phi_v1 = 127; + } else if (dist < 200.0f) { + phi_v1 = 0; + } else { + phi_v1 = (s8)(((dist - 200.0f) * 127.0f) / 1800.0f); + } + // Transition volume of channels 0 and 1 on seq player 0 over 3 frames + Audio_SeqCmd6(SEQ_PLAYER_BGM_SUB, 3, 0, 127 - phi_v1); + Audio_SeqCmd6(SEQ_PLAYER_BGM_SUB, 3, 1, 127 - phi_v1); + } + + if (D_8016B9D8 < 10) { + D_8016B9D8++; + } + } +} + +void func_800F64E0(u8 arg0) { + D_80130608 = arg0; + if (arg0 != 0) { + Audio_PlaySoundGeneral(NA_SE_SY_WIN_OPEN, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Audio_QueueCmdS32(0xF1000000, 0); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_WIN_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Audio_QueueCmdS32(0xF2000000, 0); + } +} + +void func_800F6584(u8 arg0) { + u8 playerIdx; + u16 sp34; + + D_8016B9F2 = arg0; + if ((func_800FA0B4(SEQ_PLAYER_BGM_MAIN) & 0xFF) == NA_BGM_LONLON) { + playerIdx = SEQ_PLAYER_BGM_MAIN; + sp34 = 0; + } else if ((func_800FA0B4(SEQ_PLAYER_BGM_SUB) & 0xFF) == NA_BGM_LONLON) { + playerIdx = SEQ_PLAYER_BGM_SUB; + sp34 = 0xFFFC; + } else { + return; + } + + if (arg0 != 0) { + Audio_SeqCmd6(playerIdx, 1, 0, 0); + Audio_SeqCmd6(playerIdx, 1, 1, 0); + if (playerIdx == SEQ_PLAYER_BGM_SUB) { + Audio_SeqCmdA(playerIdx, sp34 | 3); + } + } else { + if (playerIdx == SEQ_PLAYER_BGM_SUB) { + func_800F5E18(SEQ_PLAYER_BGM_SUB, NA_BGM_LONLON, 0, 0, 0); + } + Audio_SeqCmd6(playerIdx, 1, 0, 0x7F); + Audio_SeqCmd6(playerIdx, 1, 1, 0x7F); + if (playerIdx == SEQ_PLAYER_BGM_SUB) { + Audio_SeqCmdA(playerIdx, sp34); + } + } +} + +void Audio_SetEnvReverb(s8 reverb) { + sAudioEnvReverb = reverb & 0x7F; +} + +void Audio_SetCodeReverb(s8 reverb) { + if (reverb != 0) { + sAudioCodeReverb = reverb & 0x7F; + } +} + +void func_800F6700(s8 arg0) { + s8 sp1F; + + switch (arg0) { + case 0: + sp1F = 0; + D_80130604 = 0; + break; + case 1: + sp1F = 3; + D_80130604 = 3; + break; + case 2: + sp1F = 1; + D_80130604 = 1; + break; + case 3: + sp1F = 0; + D_80130604 = 2; + break; + } + + Audio_SeqCmdE0(SEQ_PLAYER_BGM_MAIN, sp1F); +} + +void Audio_SetBaseFilter(u8 filter) { + if (sAudioBaseFilter != filter) { + if (filter == 0) { + Audio_StopSfxById(NA_SE_PL_IN_BUBBLE); + } else if (sAudioBaseFilter == 0) { + Audio_PlaySoundGeneral(NA_SE_PL_IN_BUBBLE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + sAudioBaseFilter = filter; + sAudioBaseFilter2 = filter; +} + +void Audio_SetExtraFilter(u8 filter) { + u32 t; + u8 i; + + sAudioExtraFilter2 = filter; + sAudioExtraFilter = filter; + if (D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254 == NA_BGM_NATURE_AMBIENCE) { + for (i = 0; i < 16; i++) { + t = i; + // CHAN_UPD_SCRIPT_IO (seq player 0, all channels, slot 6) + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_BGM_MAIN << 16 | ((t & 0xFF) << 8) | 6, filter); + } + } +} + +void Audio_SetCutsceneFlag(s8 flag) { + sAudioCutsceneFlag = flag; +} + +void Audio_PlaySoundGeneralIfNotInCutscene(u16 sfxId, Vec3f* pos, u8 arg2, f32* freqScale, f32* arg4, s8* reverbAdd) { + if (!sAudioCutsceneFlag) { + Audio_PlaySoundGeneral(sfxId, pos, arg2, freqScale, arg4, reverbAdd); + } +} + +void Audio_PlaySoundIfNotInCutscene(u16 sfxId) { + Audio_PlaySoundGeneralIfNotInCutscene(sfxId, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void func_800F6964(u16 arg0) { + s32 skip; + u8 i; + + Audio_SeqCmd1(SEQ_PLAYER_BGM_MAIN, (arg0 * 3) / 2); + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, (arg0 * 3) / 2); + for (i = 0; i < 0x10; i++) { + skip = false; + switch (i) { + case 11: + case 12: + if (gAudioSpecId == 10) { + skip = true; + } + break; + case 13: + skip = true; + break; + } + + if (!skip) { + Audio_SeqCmd6(SEQ_PLAYER_SFX, arg0 >> 1, i, 0); + } + } + + Audio_SeqCmd1(SEQ_PLAYER_BGM_SUB, (arg0 * 3) / 2); +} + +void func_800F6AB0(u16 arg0) { + Audio_SeqCmd1(SEQ_PLAYER_BGM_MAIN, arg0); + Audio_SeqCmd1(SEQ_PLAYER_FANFARE, arg0); + Audio_SeqCmd1(SEQ_PLAYER_BGM_SUB, arg0); + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 3, 0x7F, 0); + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 1, 0x7F, 0); +} + +void func_800F6B3C(void) { + func_800F9280(SEQ_PLAYER_SFX, 0, 0xFF, 5); +} + +void Audio_DisableAllSeq(void) { + Audio_DisableSeq(SEQ_PLAYER_BGM_MAIN, 0); + Audio_DisableSeq(SEQ_PLAYER_FANFARE, 0); + Audio_DisableSeq(SEQ_PLAYER_SFX, 0); + Audio_DisableSeq(SEQ_PLAYER_BGM_SUB, 0); + Audio_ScheduleProcessCmds(); +} + +s8 func_800F6BB8(void) { + return func_800E6680(); +} + +void func_800F6BDC(void) { + Audio_DisableAllSeq(); + Audio_ScheduleProcessCmds(); + while (true) { + if (!func_800F6BB8()) { + return; + } + } +} + +void Audio_PreNMI(void) { + Audio_PreNMIInternal(); +} + +void func_800F6C34(void) { + sPrevSeqMode = 0; + D_8016B7A8 = 1.0f; + D_8016B7B0 = 1.0f; + sAudioBaseFilter = 0; + sAudioExtraFilter = 0; + sAudioBaseFilter2 = 0; + sAudioExtraFilter2 = 0; + Audio_OcaSetInstrument(0); + sRiverFreqScaleLerp.remainingFrames = 0; + sWaterfallFreqScaleLerp.remainingFrames = 0; + sRiverFreqScaleLerp.value = 1.0f; + sWaterfallFreqScaleLerp.value = 1.0f; + D_8016B7D8 = 1.0f; + D_8016B8B0 = 0x7F; + D_8016B8B1 = 0x7F; + D_8016B8B2 = 0; + D_8016B8B3 = 0; + sAudioGanonDistVol = 0xFF; + D_8016B9D8 = 0; + sSpecReverb = sSpecReverbs[gAudioSpecId]; + D_80130608 = 0; + sPrevMainBgmSeqId = NA_BGM_DISABLED; + Audio_QueueCmdS8(0x46 << 24 | SEQ_PLAYER_BGM_MAIN << 16, -1); + sSariaBgmPtr = NULL; + D_8016B9F4 = 0; + D_8016B9F3 = 1; + D_8016B9F2 = 0; +} + +void Audio_SetNatureAmbienceChannelIO(u8 channelIdxRange, u8 port, u8 val) { + u8 firstChannelIdx; + u8 lastChannelIdx; + u8 channelIdx; + + if ((D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254 != NA_BGM_NATURE_AMBIENCE) && func_800FA11C(1, 0xF00000FF)) { + sAudioNatureFailed = true; + return; + } + + // channelIdxRange = 01 on port 1 + if (((channelIdxRange << 8) + port) == ((NATURE_CHANNEL_CRITTER_0 << 8) + CHANNEL_IO_PORT_1)) { + if (func_800FA0B4(SEQ_PLAYER_BGM_SUB) != NA_BGM_LONLON) { + D_8016B9D8 = 0; + } + } + + firstChannelIdx = channelIdxRange >> 4; + lastChannelIdx = channelIdxRange & 0xF; + + if (firstChannelIdx == 0) { + firstChannelIdx = channelIdxRange & 0xF; + } + + for (channelIdx = firstChannelIdx; channelIdx <= lastChannelIdx; channelIdx++) { + Audio_SeqCmd8(SEQ_PLAYER_BGM_MAIN, port, channelIdx, val); + } +} + +void Audio_StartNatureAmbienceSequence(u16 playerIO, u16 channelMask) { + u8 channelIdx; + + if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) == NA_BGM_WINDMILL) { + func_800F3F3C(0xF); + return; + } + + Audio_SeqCmd7(SEQ_PLAYER_BGM_MAIN, 0, 1); + Audio_SeqCmd7(SEQ_PLAYER_BGM_MAIN, 4, playerIO >> 8); + Audio_SeqCmd7(SEQ_PLAYER_BGM_MAIN, 5, playerIO & 0xFF); + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, 0x7F, 1); + + channelIdx = false; + if (D_80133408 != 0) { + channelIdx = true; + Audio_SeqCmdE01(SEQ_PLAYER_BGM_MAIN, 0); + } + + Audio_StartSeq(SEQ_PLAYER_BGM_MAIN, 0, NA_BGM_NATURE_AMBIENCE); + + if (channelIdx) { + Audio_SeqCmdE01(SEQ_PLAYER_BGM_MAIN, 1); + } + + for (channelIdx = 0; channelIdx < 16; channelIdx++) { + if (!(channelMask & (1 << channelIdx)) && (playerIO & (1 << channelIdx))) { + Audio_SeqCmd8(SEQ_PLAYER_BGM_MAIN, CHANNEL_IO_PORT_1, channelIdx, 1); + } + } +} + +void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId) { + u8 i = 0; + u8 channelIdx; + u8 port; + u8 val; + + if ((D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254 == NA_BGM_DISABLED) || + !(sSeqFlags[((u8)D_8016E750[SEQ_PLAYER_BGM_MAIN].unk_254) & 0xFF] & 0x80)) { + + Audio_StartNatureAmbienceSequence(sNatureAmbienceDataIO[natureAmbienceId].playerIO, + sNatureAmbienceDataIO[natureAmbienceId].channelMask); + + while ((sNatureAmbienceDataIO[natureAmbienceId].channelIO[i] != 0xFF) && (i < 100)) { + // Probably a fake match, using Audio_SeqCmd8 doesn't work. + channelIdx = sNatureAmbienceDataIO[natureAmbienceId].channelIO[i++]; + port = sNatureAmbienceDataIO[natureAmbienceId].channelIO[i++]; + val = sNatureAmbienceDataIO[natureAmbienceId].channelIO[i++]; + Audio_QueueSeqCmd(0x80000000 | (SEQ_PLAYER_BGM_MAIN << 24) | (port << 0x10) | (channelIdx << 8) | val); + } + + Audio_SeqCmd8(SEQ_PLAYER_BGM_MAIN, CHANNEL_IO_PORT_7, NATURE_CHANNEL_UNK, D_80130604); + } +} + +void Audio_Init(void) { + AudioLoad_Init(NULL, 0); +} + +void Audio_InitSound(void) { + func_800F6C34(); + func_800EE930(); + Audio_ResetSfxChannelState(); + func_800FAEB4(); + Audio_ResetSounds(); + func_800F9280(SEQ_PLAYER_SFX, 0, 0x70, 10); +} + +void func_800F7170(void) { + func_800F9280(SEQ_PLAYER_SFX, 0, 0x70, 1); + Audio_QueueCmdS32(0xF2000000, 1); + Audio_ScheduleProcessCmds(); + Audio_QueueCmdS32(0xF8000000, 0); +} + +void func_800F71BC(s32 arg0) { + D_80133418 = 1; + func_800F6C34(); + func_800EE930(); + Audio_ResetSfxChannelState(); + func_800FADF8(); + Audio_ResetSounds(); +} + +void func_800F7208(void) { + func_800FADF8(); + Audio_QueueCmdS32(0xF2000000, 1); + func_800F6C34(); + Audio_ResetSfxChannelState(); + func_800F9280(SEQ_PLAYER_SFX, 0, 0x70, 1); +} diff --git a/soh/src/code/code_800F7260.c b/soh/src/code/code_800F7260.c new file mode 100644 index 000000000..bd8e4091f --- /dev/null +++ b/soh/src/code/code_800F7260.c @@ -0,0 +1,792 @@ +#include "ultra64.h" +#include "global.h" +#include "vt.h" + +typedef struct { + /* 0x00 */ u16 sfxId; + /* 0x04 */ Vec3f* pos; + /* 0x08 */ u8 token; + /* 0x0C */ f32* freqScale; + /* 0x10 */ f32* vol; + /* 0x14 */ s8* reverbAdd; +} SoundRequest; // size = 0x18 + +typedef struct { + /* 0x00 */ f32 value; + /* 0x04 */ f32 target; + /* 0x08 */ f32 step; + /* 0x0C */ u16 remainingFrames; +} UnusedBankLerp; // size = 0x10 + +// rodata for Audio_ProcessSoundRequest (this file) +// (probably moved to .data due to -use_readwrite_const) +char D_80133340[] = "SE"; + +// rodata for Audio_ChooseActiveSounds (this file) +char D_80133344[] = VT_COL(RED, WHITE) " dist over! flag:%04X ptr:%08X pos:%f-%f-%f" VT_RST "\n"; + +// file padding +s32 D_8013338C = 0; + +// rodata for Audio_ProcessSeqCmd (code_800F9280.c) +char D_80133390[] = "SEQ H"; +char D_80133398[] = " L"; + +SoundBankEntry D_8016BAD0[9]; +SoundBankEntry D_8016BC80[12]; +SoundBankEntry D_8016BEC0[22]; +SoundBankEntry D_8016C2E0[20]; +SoundBankEntry D_8016C6A0[8]; +SoundBankEntry D_8016C820[3]; +SoundBankEntry D_8016C8B0[5]; +SoundRequest sSoundRequests[0x100]; +u8 sSoundBankListEnd[7]; +u8 sSoundBankFreeListStart[7]; +u8 sSoundBankUnused[7]; +ActiveSound gActiveSounds[7][3]; +u8 sCurSfxPlayerChannelIdx; +u8 gSoundBankMuted[7]; +UnusedBankLerp sUnusedBankLerp[7]; +u16 gAudioSfxSwapSource[10]; +u16 gAudioSfxSwapTarget[10]; +u8 gAudioSfxSwapMode[10]; + +// sSoundRequests ring buffer endpoints. read index <= write index, wrapping around mod 256. +u8 sSoundRequestWriteIndex = 0; +u8 sSoundRequestReadIndex = 0; + +/** + * Array of pointers to arrays of SoundBankEntry of sizes: 9, 12, 22, 20, 8, 3, 5 + * + * 0 : Player Bank size 9 + * 1 : Item Bank size 12 + * 2 : Environment Bank size 22 + * 3 : Enemy Bank size 20 + * 4 : System Bank size 8 + * 5 : Ocarina Bank size 3 + * 6 : Voice Bank size 5 + */ +SoundBankEntry* gSoundBanks[7] = { + D_8016BAD0, D_8016BC80, D_8016BEC0, D_8016C2E0, D_8016C6A0, D_8016C820, D_8016C8B0, +}; + +u8 sBankSizes[ARRAY_COUNT(gSoundBanks)] = { + ARRAY_COUNT(D_8016BAD0), ARRAY_COUNT(D_8016BC80), ARRAY_COUNT(D_8016BEC0), ARRAY_COUNT(D_8016C2E0), + ARRAY_COUNT(D_8016C6A0), ARRAY_COUNT(D_8016C820), ARRAY_COUNT(D_8016C8B0), +}; + +u8 gSfxChannelLayout = 0; + +u16 D_801333D0 = 0; + +Vec3f D_801333D4 = { 0.0f, 0.0f, 0.0f }; // default pos + +f32 D_801333E0 = 1.0f; // default freqScale + +s32 D_801333E4 = 0; // unused + +s8 D_801333E8 = 0; // default reverbAdd + +s32 D_801333EC = 0; // unused + +u8 D_801333F0 = 0; + +u8 gAudioSfxSwapOff = 0; + +u8 D_801333F8 = 0; + +void Audio_SetSoundBanksMute(u16 muteMask) { + u8 bankId; + + for (bankId = 0; bankId < ARRAY_COUNT(gSoundBanks); bankId++) { + if (muteMask & 1) { + gSoundBankMuted[bankId] = true; + } else { + gSoundBankMuted[bankId] = false; + } + muteMask = muteMask >> 1; + } +} + +void Audio_QueueSeqCmdMute(u8 channelIdx) { + D_801333D0 |= (1 << channelIdx); + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 2, 0x40, 0xF); + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 2, 0x40, 0xF); +} + +void Audio_ClearBGMMute(u8 channelIdx) { + D_801333D0 &= ((1 << channelIdx) ^ 0xFFFF); + if (D_801333D0 == 0) { + Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 2, 0x7F, 0xF); + Audio_SetVolScale(SEQ_PLAYER_BGM_SUB, 2, 0x7F, 0xF); + } +} + +void Audio_PlaySoundGeneral(u16 sfxId, Vec3f* pos, u8 token, f32* freqScale, f32* vol, s8* reverbAdd) +{ + u8 i; + SoundRequest* req; + + if (!gSoundBankMuted[SFX_BANK_SHIFT(sfxId)]) { + req = &sSoundRequests[sSoundRequestWriteIndex]; + if (!gAudioSfxSwapOff) { + for (i = 0; i < 10; i++) { + if (sfxId == gAudioSfxSwapSource[i]) { + if (gAudioSfxSwapMode[i] == 0) { // "SWAP" + sfxId = gAudioSfxSwapTarget[i]; + } else { // "ADD" + req->sfxId = gAudioSfxSwapTarget[i]; + req->pos = pos; + req->token = token; + req->freqScale = freqScale; + req->vol = vol; + req->reverbAdd = reverbAdd; + sSoundRequestWriteIndex++; + req = &sSoundRequests[sSoundRequestWriteIndex]; + } + i = 10; // "break;" + } + } + } + req->sfxId = sfxId; + req->pos = pos; + req->token = token; + req->freqScale = freqScale; + req->vol = vol; + req->reverbAdd = reverbAdd; + sSoundRequestWriteIndex++; + } +} + +void Audio_RemoveMatchingSoundRequests(u8 aspect, SoundBankEntry* cmp) { + SoundRequest* req; + s32 remove; + u8 i = sSoundRequestReadIndex; + + for (; i != sSoundRequestWriteIndex; i++) { + remove = false; + req = &sSoundRequests[i]; + switch (aspect) { + case 0: + if (SFX_BANK_MASK(req->sfxId) == SFX_BANK_MASK(cmp->sfxId)) { + remove = true; + } + break; + case 1: + if (SFX_BANK_MASK(req->sfxId) == SFX_BANK_MASK(cmp->sfxId) && (&req->pos->x == cmp->posX)) { + remove = true; + } + break; + case 2: + if (&req->pos->x == cmp->posX) { + remove = true; + } + break; + case 3: + if (&req->pos->x == cmp->posX && req->sfxId == cmp->sfxId) { + remove = true; + } + break; + case 4: + if (req->token == cmp->token && req->sfxId == cmp->sfxId) { + remove = true; + } + break; + case 5: + if (req->sfxId == cmp->sfxId) { + remove = true; + } + break; + } + if (remove) { + req->sfxId = 0; + } + } +} + +void Audio_ProcessSoundRequest(void) +{ + u16 sfxId; + u8 count; + u8 index; + SoundRequest* req; + SoundBankEntry* entry; + SoundParams* soundParams; + s32 bankId; + u8 evictImportance; + u8 evictIndex; + + req = &sSoundRequests[sSoundRequestReadIndex]; + evictIndex = 0x80; + if (req->sfxId == 0) { + return; + } + bankId = SFX_BANK(req->sfxId); + if ((1 << bankId) & D_801333F0) { + AudioDebug_ScrPrt((const s8*)D_80133340, req->sfxId); + bankId = SFX_BANK(req->sfxId); + } + count = 0; + index = gSoundBanks[bankId][0].next; + while (index != 0xFF && index != 0) { + if (gSoundBanks[bankId][index].posX == &req->pos->x) { + if ((gSoundParams[SFX_BANK_SHIFT(req->sfxId)][SFX_INDEX(req->sfxId)].params & 0x20) && + gSoundParams[SFX_BANK_SHIFT(req->sfxId)][SFX_INDEX(req->sfxId)].importance == + gSoundBanks[bankId][index].sfxImportance) { + return; + } + if (gSoundBanks[bankId][index].sfxId == req->sfxId) { + count = gUsedChannelsPerBank[gSfxChannelLayout][bankId]; + } else { + if (count == 0) { + evictIndex = index; + sfxId = gSoundBanks[bankId][index].sfxId & 0xFFFF; + evictImportance = gSoundParams[SFX_BANK_SHIFT(sfxId)][SFX_INDEX(sfxId)].importance; + } else if (gSoundBanks[bankId][index].sfxImportance < evictImportance) { + evictIndex = index; + sfxId = gSoundBanks[bankId][index].sfxId & 0xFFFF; + evictImportance = gSoundParams[SFX_BANK_SHIFT(sfxId)][SFX_INDEX(sfxId)].importance; + } + count++; + if (count == gUsedChannelsPerBank[gSfxChannelLayout][bankId]) { + if (gSoundParams[SFX_BANK_SHIFT(req->sfxId)][SFX_INDEX(req->sfxId)].importance >= evictImportance) { + index = evictIndex; + } else { + index = 0; + } + } + } + if (count == gUsedChannelsPerBank[gSfxChannelLayout][bankId]) { + soundParams = &gSoundParams[SFX_BANK_SHIFT(req->sfxId)][SFX_INDEX(req->sfxId)]; + if ((req->sfxId & 0xC00) || (soundParams->params & 4) || (index == evictIndex)) { + if ((gSoundBanks[bankId][index].sfxParams & 8) && + gSoundBanks[bankId][index].state != SFX_STATE_QUEUED) { + Audio_ClearBGMMute(gSoundBanks[bankId][index].channelIdx); + } + gSoundBanks[bankId][index].token = req->token; + gSoundBanks[bankId][index].sfxId = req->sfxId; + gSoundBanks[bankId][index].state = SFX_STATE_QUEUED; + gSoundBanks[bankId][index].freshness = 2; + gSoundBanks[bankId][index].freqScale = req->freqScale; + gSoundBanks[bankId][index].vol = req->vol; + gSoundBanks[bankId][index].reverbAdd = req->reverbAdd; + gSoundBanks[bankId][index].sfxParams = soundParams->params; + gSoundBanks[bankId][index].sfxImportance = soundParams->importance; + } else if (gSoundBanks[bankId][index].state == SFX_STATE_PLAYING_2) { + gSoundBanks[bankId][index].state = SFX_STATE_PLAYING_1; + } + index = 0; + } + } + if (index != 0) { + index = gSoundBanks[bankId][index].next; + } + } + if (gSoundBanks[bankId][sSoundBankFreeListStart[bankId]].next != 0xFF && index != 0) { + index = sSoundBankFreeListStart[bankId]; + entry = &gSoundBanks[bankId][index]; + entry->posX = &req->pos->x; + entry->posY = &req->pos->y; + entry->posZ = &req->pos->z; + entry->token = req->token; + entry->freqScale = req->freqScale; + entry->vol = req->vol; + entry->reverbAdd = req->reverbAdd; + soundParams = &gSoundParams[SFX_BANK_SHIFT(req->sfxId)][SFX_INDEX(req->sfxId)]; + entry->sfxParams = soundParams->params; + entry->sfxImportance = soundParams->importance; + entry->sfxId = req->sfxId; + entry->state = SFX_STATE_QUEUED; + entry->freshness = 2; + entry->prev = sSoundBankListEnd[bankId]; + gSoundBanks[bankId][sSoundBankListEnd[bankId]].next = sSoundBankFreeListStart[bankId]; + sSoundBankListEnd[bankId] = sSoundBankFreeListStart[bankId]; + sSoundBankFreeListStart[bankId] = gSoundBanks[bankId][sSoundBankFreeListStart[bankId]].next; + gSoundBanks[bankId][sSoundBankFreeListStart[bankId]].prev = 0xFF; + entry->next = 0xFF; + } +} + +void Audio_RemoveSoundBankEntry(u8 bankId, u8 entryIndex) +{ + SoundBankEntry* entry = &gSoundBanks[bankId][entryIndex]; + u8 i; + + if (entry->sfxParams & 8) { + Audio_ClearBGMMute(entry->channelIdx); + } + if (entryIndex == sSoundBankListEnd[bankId]) { + sSoundBankListEnd[bankId] = entry->prev; + } else { + gSoundBanks[bankId][entry->next].prev = entry->prev; + } + gSoundBanks[bankId][entry->prev].next = entry->next; + entry->next = sSoundBankFreeListStart[bankId]; + entry->prev = 0xFF; + gSoundBanks[bankId][sSoundBankFreeListStart[bankId]].prev = entryIndex; + sSoundBankFreeListStart[bankId] = entryIndex; + entry->state = SFX_STATE_EMPTY; + + for (i = 0; i < gChannelsPerBank[gSfxChannelLayout][bankId]; i++) { + if (gActiveSounds[bankId][i].entryIndex == entryIndex) { + gActiveSounds[bankId][i].entryIndex = 0xFF; + i = gChannelsPerBank[gSfxChannelLayout][bankId]; + } + } +} + +void Audio_ChooseActiveSounds(u8 bankId) +{ + u8 numChosenSounds; + u8 numChannels; + u8 entryIndex; + u8 i; + u8 j; + u8 k; + u8 sfxImportance; + u8 needNewSound; + u8 chosenEntryIndex; + u16 temp3; + f32 tempf1; + SoundBankEntry* entry; + ActiveSound chosenSounds[MAX_CHANNELS_PER_BANK]; + ActiveSound* activeSound; + s32 pad; + + numChosenSounds = 0; + for (i = 0; i < MAX_CHANNELS_PER_BANK; i++) { + chosenSounds[i].priority = 0x7FFFFFFF; + chosenSounds[i].entryIndex = 0xFF; + } + entryIndex = gSoundBanks[bankId][0].next; + k = 0; + while (entryIndex != 0xFF) { + if ((gSoundBanks[bankId][entryIndex].state == SFX_STATE_QUEUED) && + (gSoundBanks[bankId][entryIndex].sfxId & 0xC00)) { + gSoundBanks[bankId][entryIndex].freshness--; + } else if (!(gSoundBanks[bankId][entryIndex].sfxId & 0xC00) && + (gSoundBanks[bankId][entryIndex].state == SFX_STATE_PLAYING_2)) { + Audio_QueueCmdS8((gSoundBanks[bankId][entryIndex].channelIdx << 8) | 0x6020000, 0); + Audio_RemoveSoundBankEntry(bankId, entryIndex); + } + if (gSoundBanks[bankId][entryIndex].freshness == 0) { + Audio_RemoveSoundBankEntry(bankId, entryIndex); + } else if (gSoundBanks[bankId][entryIndex].state != SFX_STATE_EMPTY) { + entry = &gSoundBanks[bankId][entryIndex]; + + if (&D_801333D4.x == entry[0].posX) { + entry->dist = 0.0f; + } else { + tempf1 = *entry->posY * 1; + entry->dist = (SQ(*entry->posX) + SQ(tempf1) + SQ(*entry->posZ)) * 1; + } + sfxImportance = entry->sfxImportance; + if (entry->sfxParams & 0x10) { + entry->priority = SQ(0xFF - sfxImportance) * SQ(76); + } else { + if (entry->dist > 0x7FFFFFD0) { + entry->dist = 0x70000008; + osSyncPrintf(D_80133344, entry->sfxId, entry->posX, entry->posZ, *entry->posX, *entry->posY, + *entry->posZ); + } + temp3 = entry->sfxId; // fake + entry->priority = (u32)entry->dist + (SQ(0xFF - sfxImportance) * SQ(76)) + temp3 - temp3; + if (*entry->posZ < 0.0f) { + entry->priority += (s32)(-*entry->posZ * 6.0f); + } + } + if (entry->dist > SQ(1e5f)) { + if (entry->state == SFX_STATE_PLAYING_1) { + Audio_QueueCmdS8((entry->channelIdx << 8) | 0x6020000, 0); + if (entry->sfxId & 0xC00) { + Audio_RemoveSoundBankEntry(bankId, entryIndex); + entryIndex = k; + } + } + } else { + numChannels = gChannelsPerBank[gSfxChannelLayout][bankId]; + for (i = 0; i < numChannels; i++) { + if (chosenSounds[i].priority >= entry->priority) { + if (numChosenSounds < gChannelsPerBank[gSfxChannelLayout][bankId]) { + numChosenSounds++; + } + for (j = numChannels - 1; j > i; j--) { + chosenSounds[j].priority = chosenSounds[j - 1].priority; + chosenSounds[j].entryIndex = chosenSounds[j - 1].entryIndex; + } + chosenSounds[i].priority = entry->priority; + chosenSounds[i].entryIndex = entryIndex; + i = numChannels; // "break;" + } + } + } + k = entryIndex; + } + entryIndex = gSoundBanks[bankId][k].next; + } + for (i = 0; i < numChosenSounds; i++) { + entry = &gSoundBanks[bankId][chosenSounds[i].entryIndex]; + if (entry->state == SFX_STATE_QUEUED) { + entry->state = SFX_STATE_READY; + } else if (entry->state == SFX_STATE_PLAYING_1) { + entry->state = SFX_STATE_PLAYING_REFRESH; + } + } + + // Pick something to play for all channels. + numChannels = gChannelsPerBank[gSfxChannelLayout][bankId]; + for (i = 0; i < numChannels; i++) { + needNewSound = false; + activeSound = &gActiveSounds[bankId][i]; + + if (activeSound->entryIndex == 0xFF) { + needNewSound = true; + } else { + entry = &gSoundBanks[bankId][activeSound[0].entryIndex]; + if (entry->state == SFX_STATE_PLAYING_1) { + if (entry->sfxId & 0xC00) { + Audio_RemoveSoundBankEntry(bankId, activeSound->entryIndex); + } else { + entry->state = SFX_STATE_QUEUED; + } + needNewSound = true; + } else if (entry->state == SFX_STATE_EMPTY) { + activeSound->entryIndex = 0xFF; + needNewSound = true; + } else { + // Sound is already playing as it should, nothing to do. + for (j = 0; j < numChannels; j++) { + if (activeSound->entryIndex == chosenSounds[j].entryIndex) { + chosenSounds[j].entryIndex = 0xFF; + j = numChannels; + } + } + numChosenSounds--; + } + } + + if (needNewSound == true) { + for (j = 0; j < numChannels; j++) { + chosenEntryIndex = chosenSounds[j].entryIndex; + if ((chosenEntryIndex != 0xFF) && + (gSoundBanks[bankId][chosenEntryIndex].state != SFX_STATE_PLAYING_REFRESH)) { + for (k = 0; k < numChannels; k++) { + if (chosenEntryIndex == gActiveSounds[bankId][k].entryIndex) { + needNewSound = false; + k = numChannels; // "break;" + } + } + if (needNewSound == true) { + activeSound->entryIndex = chosenEntryIndex; + chosenSounds[j].entryIndex = 0xFF; + j = numChannels + 1; + numChosenSounds--; + } + } + } + if (j == numChannels) { + // nothing found + activeSound->entryIndex = 0xFF; + } + } + } +} + +void Audio_PlayActiveSounds(u8 bankId) +{ + u8 entryIndex; + SequenceChannel* channel; + SoundBankEntry* entry; + u8 i; + + for (i = 0; i < gChannelsPerBank[gSfxChannelLayout][bankId]; i++) { + entryIndex = gActiveSounds[bankId][i].entryIndex; + if (entryIndex != 0xFF) { + entry = &gSoundBanks[bankId][entryIndex]; + channel = gAudioContext.seqPlayers[SEQ_PLAYER_SFX].channels[sCurSfxPlayerChannelIdx]; + if (entry->state == SFX_STATE_READY) { + entry->channelIdx = sCurSfxPlayerChannelIdx; + if (entry->sfxParams & 8) { + Audio_QueueSeqCmdMute(sCurSfxPlayerChannelIdx); + } + if (entry->sfxParams & 0xC0) { + switch (entry->sfxParams & 0xC0) { + case 0x40: + entry->unk_2F = Audio_NextRandom() & 0xF; + break; + case 0x80: + entry->unk_2F = Audio_NextRandom() & 0x1F; + break; + case 0xC0: + entry->unk_2F = Audio_NextRandom() & 0x3F; + break; + default: + entry->unk_2F = 0; + break; + } + } + Audio_SetSoundProperties(bankId, entryIndex, sCurSfxPlayerChannelIdx); + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((sCurSfxPlayerChannelIdx & 0xFF) << 8), 1); + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((sCurSfxPlayerChannelIdx & 0xFF) << 8) | 4, + entry->sfxId & 0xFF); + if (gIsLargeSoundBank[bankId]) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((sCurSfxPlayerChannelIdx & 0xFF) << 8) | 5, + (entry->sfxId & 0x100) >> 8); + } + if (entry->sfxId & 0xC00) { + entry->state = SFX_STATE_PLAYING_1; + } else { + entry->state = SFX_STATE_PLAYING_2; + } + } else if ((u8)channel->soundScriptIO[1] == 0xFF) { + Audio_RemoveSoundBankEntry(bankId, entryIndex); + } else if (entry->state == SFX_STATE_PLAYING_REFRESH) { + Audio_SetSoundProperties(bankId, entryIndex, sCurSfxPlayerChannelIdx); + if (entry->sfxId & 0xC00) { + entry->state = SFX_STATE_PLAYING_1; + } else { + entry->state = SFX_STATE_PLAYING_2; + } + } + } + sCurSfxPlayerChannelIdx++; + } +} + +void Audio_StopSfxByBank(u8 bankId) +{ + SoundBankEntry* entry; + s32 pad; + SoundBankEntry cmp; + u8 entryIndex = gSoundBanks[bankId][0].next; + + while (entryIndex != 0xFF) { + entry = &gSoundBanks[bankId][entryIndex]; + if (entry->state >= SFX_STATE_PLAYING_REFRESH) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((entry->channelIdx & 0xFF) << 8), 0); + } + if (entry->state != SFX_STATE_EMPTY) { + Audio_RemoveSoundBankEntry(bankId, entryIndex); + } + entryIndex = gSoundBanks[bankId][0].next; + } + cmp.sfxId = bankId << 12; + Audio_RemoveMatchingSoundRequests(0, &cmp); +} + +void func_800F8884(u8 bankId, Vec3f* pos) { + SoundBankEntry* entry; + u8 entryIndex = gSoundBanks[bankId][0].next; + u8 prevEntryIndex = 0; + + while (entryIndex != 0xFF) { + entry = &gSoundBanks[bankId][entryIndex]; + if (entry->posX == &pos->x) { + if (entry->state >= SFX_STATE_PLAYING_REFRESH) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((entry->channelIdx & 0xFF) << 8), 0); + } + if (entry->state != SFX_STATE_EMPTY) { + Audio_RemoveSoundBankEntry(bankId, entryIndex); + } + } else { + prevEntryIndex = entryIndex; + } + entryIndex = gSoundBanks[bankId][prevEntryIndex].next; + } +} + +void Audio_StopSfxByPosAndBank(u8 bankId, Vec3f* pos) { + SoundBankEntry cmp; + + func_800F8884(bankId, pos); + cmp.sfxId = bankId << 12; + cmp.posX = &pos->x; + Audio_RemoveMatchingSoundRequests(1, &cmp); +} + +void Audio_StopSfxByPos(Vec3f* pos) { + u8 i; + SoundBankEntry cmp; + + for (i = 0; i < ARRAY_COUNT(gSoundBanks); i++) { + func_800F8884(i, pos); + } + cmp.posX = &pos->x; + Audio_RemoveMatchingSoundRequests(2, &cmp); +} + +void Audio_StopSfxByPosAndId(Vec3f* pos, u16 sfxId) +{ + SoundBankEntry* entry; + u8 entryIndex = gSoundBanks[SFX_BANK(sfxId)][0].next; + u8 prevEntryIndex = 0; + SoundBankEntry cmp; + + while (entryIndex != 0xFF) { + entry = &gSoundBanks[SFX_BANK(sfxId)][entryIndex]; + if (entry->posX == &pos->x && entry->sfxId == sfxId) { + if (entry->state >= SFX_STATE_PLAYING_REFRESH) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((entry->channelIdx & 0xFF) << 8), 0); + } + if (entry->state != SFX_STATE_EMPTY) { + Audio_RemoveSoundBankEntry(SFX_BANK(sfxId), entryIndex); + } + entryIndex = 0xFF; + } else { + prevEntryIndex = entryIndex; + } + if (entryIndex != 0xFF) { + entryIndex = gSoundBanks[SFX_BANK(sfxId)][prevEntryIndex].next; + } + } + cmp.posX = &pos->x; + cmp.sfxId = sfxId; + Audio_RemoveMatchingSoundRequests(3, &cmp); +} + +void Audio_StopSfxByTokenAndId(u8 token, u16 sfxId) { + SoundBankEntry* entry; + u8 entryIndex = gSoundBanks[SFX_BANK(sfxId)][0].next; + u8 prevEntryIndex = 0; + SoundBankEntry cmp; + + while (entryIndex != 0xFF) { + entry = &gSoundBanks[SFX_BANK(sfxId)][entryIndex]; + if (entry->token == token && entry->sfxId == sfxId) { + if (entry->state >= SFX_STATE_PLAYING_REFRESH) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((entry->channelIdx & 0xFF) << 8), 0); + } + if (entry->state != SFX_STATE_EMPTY) { + Audio_RemoveSoundBankEntry(SFX_BANK(sfxId), entryIndex); + } + } else { + prevEntryIndex = entryIndex; + } + if (entryIndex != 0xFF) { + entryIndex = gSoundBanks[SFX_BANK(sfxId)][prevEntryIndex].next; + } + } + cmp.token = token; + cmp.sfxId = sfxId; + Audio_RemoveMatchingSoundRequests(4, &cmp); +} + +void Audio_StopSfxById(u32 sfxId) { + SoundBankEntry* entry; + u8 entryIndex = gSoundBanks[SFX_BANK(sfxId)][0].next; + u8 prevEntryIndex = 0; + SoundBankEntry cmp; + + while (entryIndex != 0xFF) { + entry = &gSoundBanks[SFX_BANK(sfxId)][entryIndex]; + if (entry->sfxId == sfxId) { + if (entry->state >= SFX_STATE_PLAYING_REFRESH) { + Audio_QueueCmdS8(0x6 << 24 | SEQ_PLAYER_SFX << 16 | ((entry->channelIdx & 0xFF) << 8), 0); + } + if (entry->state != SFX_STATE_EMPTY) { + Audio_RemoveSoundBankEntry(SFX_BANK(sfxId), entryIndex); + } + } else { + prevEntryIndex = entryIndex; + } + entryIndex = gSoundBanks[SFX_BANK(sfxId)][prevEntryIndex].next; + } + cmp.sfxId = sfxId; + Audio_RemoveMatchingSoundRequests(5, &cmp); +} + +void Audio_ProcessSoundRequests(void) { + while (sSoundRequestWriteIndex != sSoundRequestReadIndex) { + Audio_ProcessSoundRequest(); + sSoundRequestReadIndex++; + } +} + +void Audio_SetUnusedBankLerp(u8 bankId, u8 target, u16 delay) { + if (delay == 0) { + delay++; + } + sUnusedBankLerp[bankId].target = target / 127.0f; + sUnusedBankLerp[bankId].remainingFrames = delay; + sUnusedBankLerp[bankId].step = ((sUnusedBankLerp[bankId].value - sUnusedBankLerp[bankId].target) / delay); +} + +void Audio_StepUnusedBankLerp(u8 bankId) { + if (sUnusedBankLerp[bankId].remainingFrames != 0) { + sUnusedBankLerp[bankId].remainingFrames--; + if (sUnusedBankLerp[bankId].remainingFrames != 0) { + sUnusedBankLerp[bankId].value -= sUnusedBankLerp[bankId].step; + } else { + sUnusedBankLerp[bankId].value = sUnusedBankLerp[bankId].target; + } + } +} + +void func_800F8F88(void) { + u8 bankId; + + if (IS_SEQUENCE_CHANNEL_VALID(gAudioContext.seqPlayers[SEQ_PLAYER_SFX].channels[0])) { + sCurSfxPlayerChannelIdx = 0; + for (bankId = 0; bankId < ARRAY_COUNT(gSoundBanks); bankId++) { + Audio_ChooseActiveSounds(bankId); + Audio_PlayActiveSounds(bankId); + Audio_StepUnusedBankLerp(bankId); + } + } +} + +u8 Audio_IsSfxPlaying(u32 sfxId) +{ + SoundBankEntry* entry; + u8 entryIndex = gSoundBanks[SFX_BANK(sfxId)][0].next; + + while (entryIndex != 0xFF) { + entry = &gSoundBanks[SFX_BANK(sfxId)][entryIndex]; + if (entry->sfxId == sfxId) { + return true; + } + entryIndex = entry->next; + } + return false; +} + +void Audio_ResetSounds(void) { + u8 bankId; + u8 i; + u8 entryIndex; + + sSoundRequestWriteIndex = 0; + sSoundRequestReadIndex = 0; + D_801333D0 = 0; + for (bankId = 0; bankId < ARRAY_COUNT(gSoundBanks); bankId++) { + sSoundBankListEnd[bankId] = 0; + sSoundBankFreeListStart[bankId] = 1; + sSoundBankUnused[bankId] = 0; + gSoundBankMuted[bankId] = false; + sUnusedBankLerp[bankId].value = 1.0f; + sUnusedBankLerp[bankId].remainingFrames = 0; + } + for (bankId = 0; bankId < ARRAY_COUNT(gSoundBanks); bankId++) { + for (i = 0; i < MAX_CHANNELS_PER_BANK; i++) { + gActiveSounds[bankId][i].entryIndex = 0xFF; + } + } + for (bankId = 0; bankId < ARRAY_COUNT(gSoundBanks); bankId++) { + gSoundBanks[bankId][0].prev = 0xFF; + gSoundBanks[bankId][0].next = 0xFF; + for (i = 1; i < sBankSizes[bankId] - 1; i++) { + gSoundBanks[bankId][i].prev = i - 1; + gSoundBanks[bankId][i].next = i + 1; + } + gSoundBanks[bankId][i].prev = i - 1; + gSoundBanks[bankId][i].next = 0xFF; + } + if (D_801333F8 == 0) { + for (bankId = 0; bankId < 10; bankId++) { + gAudioSfxSwapSource[bankId] = 0; + gAudioSfxSwapTarget[bankId] = 0; + gAudioSfxSwapMode[bankId] = 0; + } + D_801333F8++; + } +} diff --git a/soh/src/code/code_800F9280.c b/soh/src/code/code_800F9280.c new file mode 100644 index 000000000..e23ede0e9 --- /dev/null +++ b/soh/src/code/code_800F9280.c @@ -0,0 +1,710 @@ +#include "ultra64.h" +#include "global.h" +#include "ultra64/abi.h" +#include "mixer.h" + +typedef struct { + u8 unk_0; + u8 unk_1; // importance? +} Struct_8016E320; + +Struct_8016E320 D_8016E320[4][5]; +u8 D_8016E348[4]; +u32 sAudioSeqCmds[0x100]; +unk_D_8016E750 D_8016E750[4]; + +u8 sSeqCmdWrPos = 0; +u8 sSeqCmdRdPos = 0; +u8 D_80133408 = 0; +u8 D_8013340C = 1; +u8 D_80133410[] = { 0, 1, 2, 3 }; +u8 gAudioSpecId = 0; +u8 D_80133418 = 0; + +// TODO: clean up these macros. They are similar to ones in code_800EC960.c but without casts. +#define Audio_StartSeq(playerIdx, fadeTimer, seqId) \ + Audio_QueueSeqCmd(0x00000000 | ((playerIdx) << 24) | ((fadeTimer) << 16) | (seqId)) +#define Audio_SeqCmdA(playerIdx, a) Audio_QueueSeqCmd(0xA0000000 | ((playerIdx) << 24) | (a)) +#define Audio_SeqCmdB30(playerIdx, a, b) Audio_QueueSeqCmd(0xB0003000 | ((playerIdx) << 24) | ((a) << 16) | (b)) +#define Audio_SeqCmdB40(playerIdx, a, b) Audio_QueueSeqCmd(0xB0004000 | ((playerIdx) << 24) | ((a) << 16) | (b)) +#define Audio_SeqCmd3(playerIdx, a) Audio_QueueSeqCmd(0x30000000 | ((playerIdx) << 24) | (a)) +#define Audio_SeqCmd5(playerIdx, a, b) Audio_QueueSeqCmd(0x50000000 | ((playerIdx) << 24) | ((a) << 16) | (b)) +#define Audio_SeqCmd4(playerIdx, a, b) Audio_QueueSeqCmd(0x40000000 | ((playerIdx) << 24) | ((a) << 16) | (b)) +#define Audio_SetVolScaleNow(playerIdx, volFadeTimer, volScale) \ + Audio_ProcessSeqCmd(0x40000000 | ((u8)playerIdx << 24) | ((u8)volFadeTimer << 16) | ((u8)(volScale * 127.0f))); + +void func_800F9280(u8 playerIdx, u8 seqId, u8 arg2, u16 fadeTimer) { + u8 i; + u16 dur; + s32 pad; + + if (D_80133408 == 0 || playerIdx == SEQ_PLAYER_SFX) { + arg2 &= 0x7F; + if (arg2 == 0x7F) { + dur = (fadeTimer >> 3) * 60 * gAudioContext.audioBufferParameters.updatesPerFrame; + Audio_QueueCmdS32(0x85000000 | _SHIFTL(playerIdx, 16, 8) | _SHIFTL(seqId, 8, 8), dur); + } else { + Audio_QueueCmdS32(0x82000000 | _SHIFTL(playerIdx, 16, 8) | _SHIFTL(seqId, 8, 8), + (fadeTimer * (u16)gAudioContext.audioBufferParameters.updatesPerFrame) / 4); + } + + D_8016E750[playerIdx].unk_254 = seqId | (arg2 << 8); + D_8016E750[playerIdx].unk_256 = seqId | (arg2 << 8); + + if (D_8016E750[playerIdx].volCur != 1.0f) { + Audio_QueueCmdF32(0x41000000 | _SHIFTL(playerIdx, 16, 8), D_8016E750[playerIdx].volCur); + } + + D_8016E750[playerIdx].unk_28 = 0; + D_8016E750[playerIdx].unk_18 = 0; + D_8016E750[playerIdx].unk_14 = 0; + + for (i = 0; i < 0x10; i++) { + D_8016E750[playerIdx].unk_50[i].unk_00 = 1.0f; + D_8016E750[playerIdx].unk_50[i].unk_0C = 0; + D_8016E750[playerIdx].unk_50[i].unk_10 = 1.0f; + D_8016E750[playerIdx].unk_50[i].unk_1C = 0; + } + + D_8016E750[playerIdx].unk_250 = 0; + D_8016E750[playerIdx].unk_252 = 0; + } +} + +void func_800F9474(u8 playerIdx, u16 arg1) { + Audio_QueueCmdS32(0x83000000 | ((u8)playerIdx << 16), + (arg1 * (u16)gAudioContext.audioBufferParameters.updatesPerFrame) / 4); + D_8016E750[playerIdx].unk_254 = NA_BGM_DISABLED; +} + +typedef enum { + SEQ_START, + CMD1, + CMD2, + CMD3, + SEQ_VOL_UPD, + CMD5, + CMD6, + CMD7, + CMD8, + CMD9, + CMDA, + CMDB, + CMDC, + CMDD, + CMDE, + CMDF +} SeqCmdType; + +void Audio_ProcessSeqCmd(u32 cmd) { + s32 pad[2]; + u16 fadeTimer; + u16 channelMask; + u16 val; + u8 oldSpec; + u8 spec; + u8 op; + u8 subOp; + u8 playerIdx; + u8 seqId; + u8 seqArgs; + u8 found; + u8 port; + u8 duration; + u8 chanIdx; + u8 i; + s32 new_var; + f32 freqScale; + + if (D_8013340C && (cmd & 0xF0000000) != 0x70000000) { + AudioDebug_ScrPrt((const s8*)D_80133390, (cmd >> 16) & 0xFFFF); // "SEQ H" + AudioDebug_ScrPrt((const s8*)D_80133398, cmd & 0xFFFF); // " L" + } + + op = cmd >> 28; + playerIdx = (cmd & 0xF000000) >> 24; + + switch (op) { + case 0x0: + // play sequence immediately + seqId = cmd & 0xFF; + seqArgs = (cmd & 0xFF00) >> 8; + fadeTimer = (cmd & 0xFF0000) >> 13; + if ((D_8016E750[playerIdx].unk_260 == 0) && (seqArgs < 0x80)) { + func_800F9280(playerIdx, seqId, seqArgs, fadeTimer); + } + break; + + case 0x1: + // disable seq player + fadeTimer = (cmd & 0xFF0000) >> 13; + func_800F9474(playerIdx, fadeTimer); + break; + + case 0x2: + // queue sequence + seqId = cmd & 0xFF; + seqArgs = (cmd & 0xFF00) >> 8; + fadeTimer = (cmd & 0xFF0000) >> 13; + new_var = seqArgs; + for (i = 0; i < D_8016E348[playerIdx]; i++) { + if (D_8016E320[playerIdx][i].unk_0 == seqId) { + if (i == 0) { + func_800F9280(playerIdx, seqId, seqArgs, fadeTimer); + } + return; + } + } + + found = D_8016E348[playerIdx]; + for (i = 0; i < D_8016E348[playerIdx]; i++) { + if (D_8016E320[playerIdx][i].unk_1 <= new_var) { + found = i; + i = D_8016E348[playerIdx]; // "break;" + } + } + + if (D_8016E348[playerIdx] < 5) { + D_8016E348[playerIdx]++; + } + for (i = D_8016E348[playerIdx] - 1; i != found; i--) { + D_8016E320[playerIdx][i].unk_1 = D_8016E320[playerIdx][i - 1].unk_1; + D_8016E320[playerIdx][i].unk_0 = D_8016E320[playerIdx][i - 1].unk_0; + } + D_8016E320[playerIdx][found].unk_1 = seqArgs; + D_8016E320[playerIdx][found].unk_0 = seqId; + + if (found == 0) { + func_800F9280(playerIdx, seqId, seqArgs, fadeTimer); + } + break; + + case 0x3: + // unqueue/stop sequence + seqId = cmd & 0xFF; + fadeTimer = (cmd & 0xFF0000) >> 13; + + found = D_8016E348[playerIdx]; + for (i = 0; i < D_8016E348[playerIdx]; i++) { + if (D_8016E320[playerIdx][i].unk_0 == seqId) { + found = i; + i = D_8016E348[playerIdx]; // "break;" + } + } + + if (found != D_8016E348[playerIdx]) { + for (i = found; i < D_8016E348[playerIdx] - 1; i++) { + D_8016E320[playerIdx][i].unk_1 = D_8016E320[playerIdx][i + 1].unk_1; + D_8016E320[playerIdx][i].unk_0 = D_8016E320[playerIdx][i + 1].unk_0; + } + D_8016E348[playerIdx]--; + } + + if (found == 0) { + func_800F9474(playerIdx, fadeTimer); + if (D_8016E348[playerIdx] != 0) { + func_800F9280(playerIdx, D_8016E320[playerIdx][0].unk_0, D_8016E320[playerIdx][0].unk_1, fadeTimer); + } + } + break; + + case 0x4: + // transition seq volume + duration = (cmd & 0xFF0000) >> 15; + val = cmd & 0xFF; + if (duration == 0) { + duration++; + } + D_8016E750[playerIdx].volTarget = (f32)val / 127.0f; + if (D_8016E750[playerIdx].volCur != D_8016E750[playerIdx].volTarget) { + D_8016E750[playerIdx].unk_08 = + (D_8016E750[playerIdx].volCur - D_8016E750[playerIdx].volTarget) / (f32)duration; + D_8016E750[playerIdx].unk_0C = duration; + } + break; + + case 0x5: + // transition freq scale for all channels + duration = (cmd & 0xFF0000) >> 15; + val = cmd & 0xFFFF; + if (duration == 0) { + duration++; + } + freqScale = (f32)val / 1000.0f; + for (i = 0; i < 16; i++) { + D_8016E750[playerIdx].unk_50[i].unk_14 = freqScale; + D_8016E750[playerIdx].unk_50[i].unk_1C = duration; + D_8016E750[playerIdx].unk_50[i].unk_18 = + (D_8016E750[playerIdx].unk_50[i].unk_10 - freqScale) / (f32)duration; + } + D_8016E750[playerIdx].unk_250 = 0xFFFF; + break; + + case 0xD: + // transition freq scale + duration = (cmd & 0xFF0000) >> 15; + chanIdx = (cmd & 0xF000) >> 12; + val = cmd & 0xFFF; + if (duration == 0) { + duration++; + } + freqScale = (f32)val / 1000.0f; + D_8016E750[playerIdx].unk_50[chanIdx].unk_14 = freqScale; + D_8016E750[playerIdx].unk_50[chanIdx].unk_18 = + (D_8016E750[playerIdx].unk_50[chanIdx].unk_10 - freqScale) / (f32)duration; + D_8016E750[playerIdx].unk_50[chanIdx].unk_1C = duration; + D_8016E750[playerIdx].unk_250 |= 1 << chanIdx; + break; + + case 0x6: + // transition vol scale + duration = (cmd & 0xFF0000) >> 15; + chanIdx = (cmd & 0xF00) >> 8; + val = cmd & 0xFF; + if (duration == 0) { + duration++; + } + D_8016E750[playerIdx].unk_50[chanIdx].unk_04 = (f32)val / 127.0f; + if (D_8016E750[playerIdx].unk_50[chanIdx].unk_00 != D_8016E750[playerIdx].unk_50[chanIdx].unk_04) { + D_8016E750[playerIdx].unk_50[chanIdx].unk_08 = + (D_8016E750[playerIdx].unk_50[chanIdx].unk_00 - D_8016E750[playerIdx].unk_50[chanIdx].unk_04) / + (f32)duration; + D_8016E750[playerIdx].unk_50[chanIdx].unk_0C = duration; + D_8016E750[playerIdx].unk_252 |= 1 << chanIdx; + } + break; + + case 0x7: + // set global io port + port = (cmd & 0xFF0000) >> 16; + val = cmd & 0xFF; + Audio_QueueCmdS8(0x46000000 | _SHIFTL(playerIdx, 16, 8) | _SHIFTL(port, 0, 8), val); + break; + + case 0x8: + // set io port if channel masked + chanIdx = (cmd & 0xF00) >> 8; + port = (cmd & 0xFF0000) >> 16; + val = cmd & 0xFF; + if ((D_8016E750[playerIdx].unk_258 & (1 << chanIdx)) == 0) { + Audio_QueueCmdS8(0x06000000 | _SHIFTL(playerIdx, 16, 8) | _SHIFTL(chanIdx, 8, 8) | _SHIFTL(port, 0, 8), + val); + } + break; + + case 0x9: + // set channel mask for command 0x8 + D_8016E750[playerIdx].unk_258 = cmd & 0xFFFF; + break; + + case 0xA: + // set channel stop mask + channelMask = cmd & 0xFFFF; + if (channelMask != 0) { + // with channel mask channelMask... + Audio_QueueCmdU16(0x90000000 | _SHIFTL(playerIdx, 16, 8), channelMask); + // stop channels + Audio_QueueCmdS8(0x08000000 | _SHIFTL(playerIdx, 16, 8) | 0xFF00, 1); + } + if ((channelMask ^ 0xFFFF) != 0) { + // with channel mask ~channelMask... + Audio_QueueCmdU16(0x90000000 | _SHIFTL(playerIdx, 16, 8), (channelMask ^ 0xFFFF)); + // unstop channels + Audio_QueueCmdS8(0x08000000 | _SHIFTL(playerIdx, 16, 8) | 0xFF00, 0); + } + break; + + case 0xB: + // update tempo + D_8016E750[playerIdx].unk_14 = cmd; + break; + + case 0xC: + // start sequence with setup commands + subOp = (cmd & 0xF00000) >> 20; + if (subOp != 0xF) { + if (D_8016E750[playerIdx].unk_4D < 7) { + found = D_8016E750[playerIdx].unk_4D++; + if (found < 8) { + D_8016E750[playerIdx].unk_2C[found] = cmd; + D_8016E750[playerIdx].unk_4C = 2; + } + } + } else { + D_8016E750[playerIdx].unk_4D = 0; + } + break; + + case 0xE: + subOp = (cmd & 0xF00) >> 8; + val = cmd & 0xFF; + switch (subOp) { + case 0: + // set sound mode + Audio_QueueCmdS32(0xF0000000, D_80133410[val]); + break; + case 1: + // set sequence starting disabled? + D_80133408 = val & 1; + break; + } + break; + + case 0xF: + // change spec + spec = cmd & 0xFF; + gSfxChannelLayout = (cmd & 0xFF00) >> 8; + oldSpec = gAudioSpecId; + gAudioSpecId = spec; + func_800E5F88(spec); + func_800F71BC(oldSpec); + Audio_QueueCmdS32(0xF8000000, 0); + break; + } +} + +void Audio_QueueSeqCmd(u32 cmd) { + sAudioSeqCmds[sSeqCmdWrPos++] = cmd; +} + +void Audio_ProcessSeqCmds(void) { + while (sSeqCmdWrPos != sSeqCmdRdPos) { + Audio_ProcessSeqCmd(sAudioSeqCmds[sSeqCmdRdPos++]); + } +} + +u16 func_800FA0B4(u8 playerIdx) { + if (!gAudioContext.seqPlayers[playerIdx].enabled) { + return NA_BGM_DISABLED; + } + return D_8016E750[playerIdx].unk_254; +} + +s32 func_800FA11C(u32 arg0, u32 arg1) { + u8 i; + + for (i = sSeqCmdRdPos; i != sSeqCmdWrPos; i++) { + if (arg0 == (sAudioSeqCmds[i] & arg1)) { + return false; + } + } + + return true; +} + +void func_800FA174(u8 playerIdx) { + D_8016E348[playerIdx] = 0; +} + +void func_800FA18C(u8 playerIdx, u8 arg1) { + u8 i; + + for (i = 0; i < D_8016E750[playerIdx].unk_4D; i++) { + u8 unkb = (D_8016E750[playerIdx].unk_2C[i] & 0xF00000) >> 20; + + if (unkb == arg1) { + D_8016E750[playerIdx].unk_2C[i] = 0xFF000000; + } + } +} + +void Audio_SetVolScale(u8 playerIdx, u8 scaleIdx, u8 targetVol, u8 volFadeTimer) { + f32 volScale; + u8 i; + + D_8016E750[playerIdx].volScales[scaleIdx] = targetVol & 0x7F; + + if (volFadeTimer != 0) { + D_8016E750[playerIdx].fadeVolUpdate = 1; + D_8016E750[playerIdx].volFadeTimer = volFadeTimer; + } else { + for (i = 0, volScale = 1.0f; i < 4; i++) { + volScale *= D_8016E750[playerIdx].volScales[i] / 127.0f; + } + + Audio_SetVolScaleNow(playerIdx, volFadeTimer, volScale); + } +} + +void func_800FA3DC(void) { + u32 temp_a1; + u16 temp_lo; + u16 temp_v1; + u16 phi_a2; + u8 temp_v0_4; + u8 temp_a0; + u8 temp_s1; + u8 temp_s0_3; + u8 temp_a3_3; + s32 pad[3]; + u32 dummy; + f32 phi_f0; + u8 phi_t0; + u8 playerIdx; + u8 j; + u8 k; + + for (playerIdx = 0; playerIdx < 4; playerIdx++) { + if (D_8016E750[playerIdx].unk_260 != 0) { + switch (func_800E5E20(&dummy)) { + case 1: + case 2: + case 3: + case 4: + D_8016E750[playerIdx].unk_260 = 0; + Audio_ProcessSeqCmd(D_8016E750[playerIdx].unk_25C); + break; + } + } + + if (D_8016E750[playerIdx].fadeVolUpdate) { + phi_f0 = 1.0f; + for (j = 0; j < 4; j++) { + phi_f0 *= (D_8016E750[playerIdx].volScales[j] / 127.0f); + } + Audio_SeqCmd4(playerIdx, D_8016E750[playerIdx].volFadeTimer, (u8)(phi_f0 * 127.0f)); + D_8016E750[playerIdx].fadeVolUpdate = 0; + } + + if (D_8016E750[playerIdx].unk_0C != 0) { + D_8016E750[playerIdx].unk_0C--; + + if (D_8016E750[playerIdx].unk_0C != 0) { + D_8016E750[playerIdx].volCur = D_8016E750[playerIdx].volCur - D_8016E750[playerIdx].unk_08; + } else { + D_8016E750[playerIdx].volCur = D_8016E750[playerIdx].volTarget; + } + + Audio_QueueCmdF32(0x41000000 | _SHIFTL(playerIdx, 16, 8), D_8016E750[playerIdx].volCur); + } + + if (D_8016E750[playerIdx].unk_14 != 0) { + temp_a1 = D_8016E750[playerIdx].unk_14; + phi_t0 = (temp_a1 & 0xFF0000) >> 15; + phi_a2 = temp_a1 & 0xFFF; + if (phi_t0 == 0) { + phi_t0++; + } + + if (gAudioContext.seqPlayers[playerIdx].enabled) { + temp_lo = gAudioContext.seqPlayers[playerIdx].tempo / 0x30; + temp_v0_4 = (temp_a1 & 0xF000) >> 12; + switch (temp_v0_4) { + case 1: + phi_a2 += temp_lo; + break; + case 2: + if (phi_a2 < temp_lo) { + phi_a2 = temp_lo - phi_a2; + } + break; + case 3: + phi_a2 = temp_lo * (phi_a2 / 100.0f); + break; + case 4: + if (D_8016E750[playerIdx].unk_18) { + phi_a2 = D_8016E750[playerIdx].unk_18; + } else { + phi_a2 = temp_lo; + } + break; + } + + if (phi_a2 > 300) { + phi_a2 = 300; + } + + if (D_8016E750[playerIdx].unk_18 == 0) { + D_8016E750[playerIdx].unk_18 = temp_lo; + } + + D_8016E750[playerIdx].unk_20 = phi_a2; + D_8016E750[playerIdx].unk_1C = gAudioContext.seqPlayers[playerIdx].tempo / 0x30; + D_8016E750[playerIdx].unk_24 = (D_8016E750[playerIdx].unk_1C - D_8016E750[playerIdx].unk_20) / phi_t0; + D_8016E750[playerIdx].unk_28 = phi_t0; + D_8016E750[playerIdx].unk_14 = 0; + } + } + + if (D_8016E750[playerIdx].unk_28 != 0) { + D_8016E750[playerIdx].unk_28--; + if (D_8016E750[playerIdx].unk_28 != 0) { + D_8016E750[playerIdx].unk_1C = D_8016E750[playerIdx].unk_1C - D_8016E750[playerIdx].unk_24; + } else { + D_8016E750[playerIdx].unk_1C = D_8016E750[playerIdx].unk_20; + } + // set tempo + Audio_QueueCmdS32(0x47000000 | _SHIFTL(playerIdx, 16, 8), D_8016E750[playerIdx].unk_1C); + } + + if (D_8016E750[playerIdx].unk_252 != 0) { + for (k = 0; k < 0x10; k++) { + if (D_8016E750[playerIdx].unk_50[k].unk_0C != 0) { + D_8016E750[playerIdx].unk_50[k].unk_0C--; + if (D_8016E750[playerIdx].unk_50[k].unk_0C != 0) { + D_8016E750[playerIdx].unk_50[k].unk_00 -= D_8016E750[playerIdx].unk_50[k].unk_08; + } else { + D_8016E750[playerIdx].unk_50[k].unk_00 = D_8016E750[playerIdx].unk_50[k].unk_04; + D_8016E750[playerIdx].unk_252 ^= (1 << k); + } + // CHAN_UPD_VOL_SCALE (playerIdx = seq, k = chan) + Audio_QueueCmdF32(0x01000000 | _SHIFTL(playerIdx, 16, 8) | _SHIFTL(k, 8, 8), + D_8016E750[playerIdx].unk_50[k].unk_00); + } + } + } + + if (D_8016E750[playerIdx].unk_250 != 0) { + for (k = 0; k < 0x10; k++) { + if (D_8016E750[playerIdx].unk_50[k].unk_1C != 0) { + D_8016E750[playerIdx].unk_50[k].unk_1C--; + if (D_8016E750[playerIdx].unk_50[k].unk_1C != 0) { + D_8016E750[playerIdx].unk_50[k].unk_10 -= D_8016E750[playerIdx].unk_50[k].unk_18; + } else { + D_8016E750[playerIdx].unk_50[k].unk_10 = D_8016E750[playerIdx].unk_50[k].unk_14; + D_8016E750[playerIdx].unk_250 ^= (1 << k); + } + // CHAN_UPD_FREQ_SCALE + Audio_QueueCmdF32(0x04000000 | _SHIFTL(playerIdx, 16, 8) | _SHIFTL(k, 8, 8), + D_8016E750[playerIdx].unk_50[k].unk_10); + } + } + } + + if (D_8016E750[playerIdx].unk_4D != 0) { + if (func_800FA11C(0xF0000000, 0xF0000000) == 0) { + D_8016E750[playerIdx].unk_4D = 0; + return; + } + + if (D_8016E750[playerIdx].unk_4C != 0) { + D_8016E750[playerIdx].unk_4C--; + continue; + } + + if (gAudioContext.seqPlayers[playerIdx].enabled) { + continue; + } + + for (j = 0; j < D_8016E750[playerIdx].unk_4D; j++) { + temp_a0 = (D_8016E750[playerIdx].unk_2C[j] & 0x00F00000) >> 20; + temp_s1 = (D_8016E750[playerIdx].unk_2C[j] & 0x000F0000) >> 16; + temp_s0_3 = (D_8016E750[playerIdx].unk_2C[j] & 0xFF00) >> 8; + temp_a3_3 = D_8016E750[playerIdx].unk_2C[j] & 0xFF; + + switch (temp_a0) { + case 0: + Audio_SetVolScale(temp_s1, 1, 0x7F, temp_a3_3); + break; + case 7: + if (D_8016E348[playerIdx] == temp_a3_3) { + Audio_SetVolScale(temp_s1, 1, 0x7F, temp_s0_3); + } + break; + case 1: + Audio_SeqCmd3(playerIdx, D_8016E750[playerIdx].unk_254); + break; + case 2: + Audio_StartSeq(temp_s1, 1, D_8016E750[temp_s1].unk_254); + D_8016E750[temp_s1].fadeVolUpdate = 1; + D_8016E750[temp_s1].volScales[1] = 0x7F; + break; + case 3: + Audio_SeqCmdB30(temp_s1, temp_s0_3, temp_a3_3); + break; + case 4: + Audio_SeqCmdB40(temp_s1, temp_a3_3, 0); + break; + case 5: + temp_v1 = D_8016E750[playerIdx].unk_2C[j] & 0xFFFF; + Audio_StartSeq(temp_s1, D_8016E750[temp_s1].unk_4E, temp_v1); + Audio_SetVolScale(temp_s1, 1, 0x7F, 0); + D_8016E750[temp_s1].unk_4E = 0; + break; + case 6: + D_8016E750[playerIdx].unk_4E = temp_s0_3; + break; + case 8: + Audio_SetVolScale(temp_s1, temp_s0_3, 0x7F, temp_a3_3); + break; + case 14: + if (temp_a3_3 & 1) { + Audio_QueueCmdS32(0xE3000000, SEQUENCE_TABLE); + } + if (temp_a3_3 & 2) { + Audio_QueueCmdS32(0xE3000000, FONT_TABLE); + } + if (temp_a3_3 & 4) { + Audio_QueueCmdS32(0xE3000000, SAMPLE_TABLE); + } + break; + case 9: + temp_v1 = D_8016E750[playerIdx].unk_2C[j] & 0xFFFF; + Audio_SeqCmdA(temp_s1, temp_v1); + break; + case 10: + Audio_SeqCmd5(temp_s1, temp_s0_3, (temp_a3_3 * 10) & 0xFFFF); + break; + } + } + + D_8016E750[playerIdx].unk_4D = 0; + } + } +} + +u8 func_800FAD34(void) { + if (D_80133418 != 0) { + if (D_80133418 == 1) { + if (func_800E5EDC() == 1) { + D_80133418 = 0; + Audio_QueueCmdS8(0x46020000, gSfxChannelLayout); + func_800F7170(); + } + } else if (D_80133418 == 2) { + while (func_800E5EDC() != 1) {} + D_80133418 = 0; + Audio_QueueCmdS8(0x46020000, gSfxChannelLayout); + func_800F7170(); + } + } + + return D_80133418; +} + +void func_800FADF8(void) { + u8 playerIdx, j; + + for (playerIdx = 0; playerIdx < 4; playerIdx++) { + D_8016E348[playerIdx] = 0; + D_8016E750[playerIdx].unk_254 = NA_BGM_DISABLED; + D_8016E750[playerIdx].unk_256 = NA_BGM_DISABLED; + D_8016E750[playerIdx].unk_28 = 0; + D_8016E750[playerIdx].unk_18 = 0; + D_8016E750[playerIdx].unk_14 = 0; + D_8016E750[playerIdx].unk_258 = 0; + D_8016E750[playerIdx].unk_4D = 0; + D_8016E750[playerIdx].unk_4E = 0; + D_8016E750[playerIdx].unk_250 = 0; + D_8016E750[playerIdx].unk_252 = 0; + for (j = 0; j < 4; j++) { + D_8016E750[playerIdx].volScales[j] = 0x7F; + } + D_8016E750[playerIdx].volFadeTimer = 1; + D_8016E750[playerIdx].fadeVolUpdate = 1; + } +} + +void func_800FAEB4(void) { + u8 playerIdx, j; + + for (playerIdx = 0; playerIdx < 4; playerIdx++) { + D_8016E750[playerIdx].volCur = 1.0f; + D_8016E750[playerIdx].unk_0C = 0; + D_8016E750[playerIdx].fadeVolUpdate = 0; + for (j = 0; j < 4; j++) { + D_8016E750[playerIdx].volScales[j] = 0x7F; + } + } + func_800FADF8(); +} diff --git a/soh/src/code/code_800FBCE0.c b/soh/src/code/code_800FBCE0.c new file mode 100644 index 000000000..65156eda0 --- /dev/null +++ b/soh/src/code/code_800FBCE0.c @@ -0,0 +1,52 @@ +#include "global.h" + +#define printSpStatus(x, name) \ + if (x & SP_STATUS_##name) \ + osSyncPrintf(#name " ") +#define printDpStatus(x, name) \ + if (x & DPC_STATUS_##name) \ + osSyncPrintf(#name " ") + +void func_800FBCE0() { + u32 spStatus = __osSpGetStatus(); + u32 dpStatus = osDpGetStatus(); + + osSyncPrintf("osSpGetStatus=%08x: ", spStatus); + printSpStatus(spStatus, HALT); + printSpStatus(spStatus, BROKE); + printSpStatus(spStatus, DMA_BUSY); + printSpStatus(spStatus, DMA_FULL); + printSpStatus(spStatus, IO_FULL); + printSpStatus(spStatus, SSTEP); + printSpStatus(spStatus, INTR_BREAK); + printSpStatus(spStatus, YIELD); + printSpStatus(spStatus, YIELDED); + printSpStatus(spStatus, TASKDONE); + printSpStatus(spStatus, SIG3); + printSpStatus(spStatus, SIG4); + printSpStatus(spStatus, SIG5); + printSpStatus(spStatus, SIG6); + printSpStatus(spStatus, SIG7); + osSyncPrintf("\n"); + + osSyncPrintf("osDpGetStatus=%08x:", dpStatus); + printDpStatus(dpStatus, XBUS_DMEM_DMA); + printDpStatus(dpStatus, FREEZE); + printDpStatus(dpStatus, FLUSH); + printDpStatus(dpStatus, START_GCLK); + printDpStatus(dpStatus, TMEM_BUSY); + printDpStatus(dpStatus, PIPE_BUSY); + printDpStatus(dpStatus, CMD_BUSY); + printDpStatus(dpStatus, CBUF_READY); + printDpStatus(dpStatus, DMA_BUSY); + printDpStatus(dpStatus, END_VALID); + printDpStatus(dpStatus, START_VALID); + osSyncPrintf("\n"); +} + +void func_800FBFD8() { + func_800FBCE0(); + osDpSetStatus(DPC_SET_FREEZE | DPC_SET_FLUSH); + __osSpSetStatus(SP_SET_HALT | SP_SET_SIG2 | SP_CLR_INTR_BREAK); + func_800FBCE0(); +} diff --git a/soh/src/code/code_800FC620.c b/soh/src/code/code_800FC620.c new file mode 100644 index 000000000..75a6081e5 --- /dev/null +++ b/soh/src/code/code_800FC620.c @@ -0,0 +1,201 @@ +#include "global.h" + +typedef void (*arg3_800FC868)(void*); +typedef void (*arg3_800FC8D8)(void*, u32); +typedef void (*arg3_800FC948)(void*, u32, u32, u32, u32, u32, u32, u32, u32); +typedef void (*arg3_800FCA18)(void*, u32); + +typedef struct InitFunc { + s32 nextOffset; + void (*func)(void); +} InitFunc; + +// .data +void* sInitFuncs = NULL; + +char sNew[] = { 'n', 'e', 'w' }; + +char D_80134488[0x18] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, + 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, +}; + +s32 Overlay_Load(uintptr_t vRomStart, uintptr_t vRomEnd, void* vRamStart, void* vRamEnd, void* allocatedVRamAddr) +{ + return 0; + +#if 0 + s32 pad; + uintptr_t end; + u32 bssSize; + OverlayRelocationSection* ovl; + u32 relocCnt; + u32 ovlOffset; + size_t size; + + if (gOverlayLogSeverity >= 3) { + // "Start loading dynamic link function" + osSyncPrintf("\nダイナミックリンクファンクションのロードを開始します\n"); + } + + if (gOverlayLogSeverity >= 3) { + size = vRomEnd - vRomStart; + // "DMA transfer of TEXT, DATA, RODATA + rel (%08x-%08x)" + osSyncPrintf("TEXT,DATA,RODATA+relをDMA転送します(%08x-%08x)\n", allocatedVRamAddr, + (uintptr_t)allocatedVRamAddr + size); + } + + size = vRomEnd - vRomStart; + end = (uintptr_t)allocatedVRamAddr + size; + DmaMgr_SendRequest0(allocatedVRamAddr, vRomStart, size); + + ovlOffset = ((s32*)end)[-1]; + + ovl = (OverlayRelocationSection*)((uintptr_t)end - ovlOffset); + if (gOverlayLogSeverity >= 3) { + osSyncPrintf("TEXT(%08x), DATA(%08x), RODATA(%08x), BSS(%08x)\n", ovl->textSize, ovl->dataSize, ovl->rodataSize, + ovl->bssSize); + } + + if (gOverlayLogSeverity >= 3) { + osSyncPrintf("リロケーションします\n"); // "Relocate" + } + + Overlay_Relocate(allocatedVRamAddr, ovl, vRamStart); + + bssSize = ovl->bssSize; + if (bssSize != 0) { + if (gOverlayLogSeverity >= 3) { + // "Clear BSS area (% 08x-% 08x)" + osSyncPrintf("BSS領域をクリアします(%08x-%08x)\n", end, end + ovl->bssSize); + } + + size = ovl->bssSize; + bssSize = size; + bzero((void*)end, bssSize); + relocCnt = ovl->nRelocations; + (void)relocCnt; // suppresses set but unused warning + } + + size = (uintptr_t)&ovl->relocations[ovl->nRelocations] - (uintptr_t)ovl; + if (gOverlayLogSeverity >= 3) { + // "Clear REL area (%08x-%08x)" + osSyncPrintf("REL領域をクリアします(%08x-%08x)\n", ovl, (uintptr_t)ovl + size); + } + + bzero(ovl, size); + + size = (uintptr_t)vRamEnd - (uintptr_t)vRamStart; + osWritebackDCache(allocatedVRamAddr, size); + osInvalICache(allocatedVRamAddr, size); + + if (gOverlayLogSeverity >= 3) { + // "Finish loading dynamic link function" + osSyncPrintf("ダイナミックリンクファンクションのロードを終了します\n\n"); + } + return size; +#endif +} + +// possibly some kind of new() function +void* func_800FC800(size_t size) { + if (size == 0) { + size = 1; + } + + return __osMallocDebug(&gSystemArena, size, sNew, 0); +} + +// possible some kind of delete() function +void func_800FC83C(void* ptr) { + if (ptr != NULL) { + __osFree(&gSystemArena, ptr); + } +} + +void func_800FC868(void* blk, u32 nBlk, u32 blkSize, arg3_800FC868 arg3) { + uintptr_t pos; + + for (pos = (uintptr_t)blk; pos < (uintptr_t)blk + (nBlk * blkSize); pos = (uintptr_t)pos + (blkSize & ~0)) { + arg3((void*)pos); + } +} + +void func_800FC8D8(void* blk, u32 nBlk, s32 blkSize, arg3_800FC8D8 arg3) { + uintptr_t pos; + + for (pos = (uintptr_t)blk; pos < (uintptr_t)blk + (nBlk * blkSize); pos = (uintptr_t)pos + (blkSize & ~0)) { + arg3((void*)pos, 2); + } +} + +void* func_800FC948(void* blk, u32 nBlk, u32 blkSize, arg3_800FC948 arg3) { + uintptr_t pos; + + if (blk == NULL) { + blk = func_800FC800(nBlk * blkSize); + } + + if (blk != NULL && arg3 != NULL) { + pos = (uintptr_t)blk; + while (pos < (uintptr_t)blk + (nBlk * blkSize)) { + arg3((void*)pos, 0, 0, 0, 0, 0, 0, 0, 0); + pos = (uintptr_t)pos + (blkSize & ~0); + } + } + return blk; +} + +void func_800FCA18(void* blk, u32 nBlk, u32 blkSize, arg3_800FCA18 arg3, s32 arg4) { + uintptr_t pos; + uintptr_t end; + s32 masked_arg2; + + if (blk == 0) { + return; + } + if (arg3 != 0) { + end = (uintptr_t)blk; + masked_arg2 = (s32)(blkSize & ~0); + pos = (uintptr_t)end + (nBlk * blkSize); + + if (masked_arg2) {} + + while (pos > end) { + pos -= masked_arg2; + arg3((void*)pos, 2); + if (1) {} + } + + if (!masked_arg2) {} + } + + if (arg4 != 0) { + func_800FC83C(blk); + } +} + +void func_800FCB34(void) { + InitFunc* initFunc = (InitFunc*)&sInitFuncs; + u32 nextOffset = initFunc->nextOffset; + InitFunc* prev = NULL; + + while (nextOffset != 0) { + initFunc = (InitFunc*)((s32)initFunc + nextOffset); + + if (initFunc->func != NULL) { + (*initFunc->func)(); + } + + nextOffset = initFunc->nextOffset; + initFunc->nextOffset = (s32)prev; + prev = initFunc; + } + + sInitFuncs = prev; +} + +void SystemHeap_Init(void* start, size_t size) { + SystemArena_Init(start, size); + func_800FCB34(); +} diff --git a/soh/src/code/code_800FCE80.c b/soh/src/code/code_800FCE80.c new file mode 100644 index 000000000..500e2c01d --- /dev/null +++ b/soh/src/code/code_800FCE80.c @@ -0,0 +1,161 @@ +#include "global.h" +#include "fp.h" + +s32 gUseAtanContFrac; + +f32 Math_FTanF(f32 x) { + f32 sin = sinf(x); + f32 cos = cosf(x); + + return sin / cos; +} + +f32 Math_FFloorF(f32 x) { + return floorf(x); +} + +f32 Math_FCeilF(f32 x) { + return ceilf(x); +} + +f32 Math_FRoundF(f32 x) { + return roundf(x); +} + +f32 Math_FTruncF(f32 x) { + return truncf(x); +} + +f32 Math_FNearbyIntF(f32 x) { + return nearbyintf(x); +} + +/* Arctangent approximation using a Taylor series (one quadrant) */ +f32 Math_FAtanTaylorQF(f32 x) { + static const f32 coeffs[] = { + -1.0f / 3, +1.0f / 5, -1.0f / 7, +1.0f / 9, -1.0f / 11, +1.0f / 13, -1.0f / 15, +1.0f / 17, 0.0f, + }; + + f32 poly = x; + f32 sq = SQ(x); + f32 exp = x * sq; + const f32* c = coeffs; + f32 term; + + while (1) { + term = *c++ * exp; + if (poly + term == poly) { + break; + } + poly = poly + term; + exp = exp * sq; + } + + return poly; +} + +/* Ditto for two quadrants */ +f32 Math_FAtanTaylorF(f32 x) { + f32 t; + f32 q; + + if (x > 0.0f) { + t = x; + } else if (x < 0.0f) { + t = -x; + } else if (x == 0.0f) { + return 0.0f; + } else { + return qNaN0x10000; + } + + if (t <= M_SQRT2 - 1.0f) { + return Math_FAtanTaylorQF(x); + } + + if (t >= M_SQRT2 + 1.0f) { + q = M_PI / 2 - Math_FAtanTaylorQF(1.0f / t); + } else { + q = M_PI / 4 - Math_FAtanTaylorQF((1.0f - t) / (1.0f + t)); + } + + if (x > 0.0f) { + return q; + } else { + return -q; + } +} + +/* Arctangent approximation using a continued fraction */ +f32 Math_FAtanContFracF(f32 x) { + s32 sector; + f32 z; + f32 conv; + f32 sq; + s32 i; + + if (x >= -1.0f && x <= 1.0f) { + sector = 0; + } else if (x > 1.0f) { + sector = 1; + x = 1.0f / x; + } else if (x < -1.0f) { + sector = -1; + x = 1.0f / x; + } else { + return qNaN0x10000; + } + + sq = SQ(x); + conv = 0.0f; + z = 8.0f; + for (i = 8; i != 0; i--) { + conv = SQ(z) * sq / (2.0f * z + 1.0f + conv); + z -= 1.0f; + } + conv = x / (1.0f + conv); + + if (sector == 0) { + return conv; + } else if (sector > 0) { + return M_PI / 2 - conv; + } else { + return -M_PI / 2 - conv; + } +} + +f32 Math_FAtanF(f32 x) { + if (!gUseAtanContFrac) { + return Math_FAtanTaylorF(x); + } else { + return Math_FAtanContFracF(x); + } +} + +f32 Math_FAtan2F(f32 y, f32 x) { + if (x == 0.0f) { + if (y == 0.0f) { + return 0.0f; + } else if (y > 0.0f) { + return M_PI / 2; + } else if (y < 0.0f) { + return -M_PI / 2; + } else { + return qNaN0x10000; + } + } else if (x >= 0.0f) { + return Math_FAtanF(y / x); + } else if (y < 0.0f) { + return Math_FAtanF(y / x) - M_PI; + } else { + return M_PI - Math_FAtanF(-(y / x)); + } +} + +f32 Math_FAsinF(f32 x) { + return Math_FAtan2F(x, sqrtf(1.0f - SQ(x))); +} + +f32 Math_FAcosF(f32 x) { + return M_PI / 2 - Math_FAsinF(x); +} diff --git a/soh/src/code/code_800FD970.c b/soh/src/code/code_800FD970.c new file mode 100644 index 000000000..d1efcdc3a --- /dev/null +++ b/soh/src/code/code_800FD970.c @@ -0,0 +1,82 @@ +#include "global.h" + +// The latest generated random number, used to generate the next number in the sequence. +static u32 sRandInt = 1; + +// Space to store a value to be re-interpreted as a float. +static u32 sRandFloat; + +/** + * Gets the next integer in the sequence of pseudo-random numbers. + */ +u32 Rand_Next(void) { + return sRandInt = (sRandInt * 1664525) + 1013904223; +} + +/** + * Seeds the pseudo-random number generator by providing a starting value. + */ +void Rand_Seed(u32 seed) { + sRandInt = seed; +} + +/** + * Returns a pseudo-random floating-point number between 0.0f and 1.0f, by generating + * the next integer and masking it to an IEEE-754 compliant floating-point number + * between 1.0f and 2.0f, returning the result subtract 1.0f. + */ +f32 Rand_ZeroOne(void) { + sRandInt = (sRandInt * 1664525) + 1013904223; + sRandFloat = ((sRandInt >> 9) | 0x3F800000); + return *((f32*)&sRandFloat) - 1.0f; +} + +/** + * Returns a pseudo-random floating-point number between -0.5f and 0.5f by the same + * manner in which Rand_ZeroOne generates its result. + */ +f32 Rand_Centered(void) { + sRandInt = (sRandInt * 1664525) + 1013904223; + sRandFloat = ((sRandInt >> 9) | 0x3F800000); + return *((f32*)&sRandFloat) - 1.5f; +} + +/** + * Seeds a pseudo-random number at rndNum with a provided seed. + */ +void Rand_Seed_Variable(u32* rndNum, u32 seed) { + *rndNum = seed; +} + +/** + * Generates the next pseudo-random integer from the provided rndNum. + */ +u32 Rand_Next_Variable(u32* rndNum) { + return *rndNum = (*rndNum * 1664525) + 1013904223; +} + +/** + * Generates the next pseudo-random floating-point number between 0.0f and + * 1.0f from the provided rndNum. + */ +f32 Rand_ZeroOne_Variable(u32* rndNum) { + u32 next = (*rndNum * 1664525) + 1013904223; + + // clang-format off + *rndNum = next; sRandFloat = (next >> 9) | 0x3F800000; + // clang-format on + return *((f32*)&sRandFloat) - 1.0f; +} + +/** + * Generates the next pseudo-random floating-point number between -0.5f and + * 0.5f from the provided rndNum. + */ +f32 Rand_Centered_Variable(u32* rndNum) { + u32 next = (*rndNum * 1664525) + 1013904223; + + // clang-format off + *rndNum = next; sRandFloat = (next >> 9) | 0x3F800000; + // clang-format on + return *((f32*)&sRandFloat) - 1.5f; +} diff --git a/soh/src/code/code_801067F0.c b/soh/src/code/code_801067F0.c new file mode 100644 index 000000000..ae65e522b --- /dev/null +++ b/soh/src/code/code_801067F0.c @@ -0,0 +1,12 @@ +#include "global.h" + +// fmodf? +f32 func_801067F0(f32 arg0, f32 arg1) { + s32 sp4; + + if (arg1 == 0.0f) { + return 0.0f; + } + sp4 = arg0 / arg1; + return arg0 - (sp4 * arg1); +} diff --git a/soh/src/code/code_801068B0.c b/soh/src/code/code_801068B0.c new file mode 100644 index 000000000..d673cc5e0 --- /dev/null +++ b/soh/src/code/code_801068B0.c @@ -0,0 +1,24 @@ +#include "global.h" + +// memmove used in __osMalloc.c +void* func_801068B0(void* dst, void* src, size_t size) { + u8* spC = dst; + u8* sp8 = src; + register s32 a3; + + if (spC == sp8) { + return dst; + } + if (spC < sp8) { + for (a3 = size--; a3 != 0; a3 = size--) { + *spC++ = *sp8++; + } + } else { + spC += size - 1; + sp8 += size - 1; + for (a3 = size--; a3 != 0; a3 = size--) { + *spC-- = *sp8--; + } + } + return dst; +} diff --git a/soh/src/code/db_camera.c b/soh/src/code/db_camera.c new file mode 100644 index 000000000..53d5c41bf --- /dev/null +++ b/soh/src/code/db_camera.c @@ -0,0 +1,2312 @@ +#include "ultra64.h" +#include "global.h" + +static GlobalContext* sGlobalCtx; + +// TODO: cleanup these arrays and UB access +char* D_8012CEE0[] = { GFXP_KATAKANA "キ-フレ-ム" GFXP_HIRAGANA "ガ" }; +char* D_8012CEE4 = GFXP_HIRAGANA "タリマセン。"; +char* D_8012CEE8 = GFXP_HIRAGANA "サイセイデキマセン"; +char* D_8012CEEC = GFXP_HIRAGANA "サイセイシュウリョウ"; +char* D_8012CEF0 = GFXP_HIRAGANA "サイセイチュウ!"; + +char* D_8012CEF4 = "DEMO CAMERA TOOL"; +char* D_8012CEF8[] = { GFXP_HIRAGANA "モウハイリマセン", GFXP_HIRAGANA "トウロク テンメ", GFXP_HIRAGANA "ヘンコウ / " }; +char* D_8012CF04 = "> >"; +char* D_8012CF08 = "< <"; +char* D_8012CF0C = "< >"; +char* D_8012CF10 = GFXP_KATAKANA "*プレイヤ-*"; +char* D_8012CF14 = GFXP_KATAKANA "Eモ-ド" GFXP_HIRAGANA " ソウタイ"; +char* D_8012CF18[] = { GFXP_KATAKANA "Eモ-ド" GFXP_HIRAGANA "ゼッタイ", GFXP_HIRAGANA "ガメン" GFXP_KATAKANA " デモ", + GFXP_HIRAGANA "ガメン フツウ" }; +char* D_8012CF24[] = { GFXP_HIRAGANA "Pジカン MAX", GFXP_KATAKANA "リンク" GFXP_HIRAGANA " キオク", + GFXP_KATAKANA "リンク" GFXP_HIRAGANA " ムシ" }; +char* D_8012CF30 = GFXP_HIRAGANA "*ミテルイチ*"; +char* D_8012CF34 = GFXP_KATAKANA "*カメラ" GFXP_HIRAGANA "イチ*"; +char* D_8012CF38 = "DEBUG CAMERA"; +char* D_8012CF3C = GFXP_KATAKANA "センタ-/ロック"; +char* D_8012CF40 = GFXP_KATAKANA "センタ-/フリ-"; + +char* D_8012CF44 = "DEMO CONTROL"; +char* D_8012CF48 = GFXP_KATAKANA "メモリ" GFXP_HIRAGANA "ガタリマセン"; +char* D_8012CF4C = "p"; +char* D_8012CF50[] = { "e", "s", "l", "c" }; + +char* D_8012CF60[] = { GFXP_KATAKANA "メモリパック" }; // "Mempak" +char* D_8012CF64 = GFXP_KATAKANA "セーブ"; // "Save" +char* D_8012CF68 = GFXP_KATAKANA "ロード"; // "Load" +char* D_8012CF6C = GFXP_KATAKANA "クリア-"; // "Clear" +char* D_8012CF70 = GFXP_HIRAGANA "ヲヌカナイデネ"; + +char* D_8012CF74 = "FREE BYTE"; +char* D_8012CF78 = "NEED BYTE"; +char* D_8012CF7C = GFXP_KATAKANA "*メモリ-パック*"; +char* D_8012CF80 = GFXP_HIRAGANA "ヲミツケラレマセン"; +char* D_8012CF84 = GFXP_KATAKANA "ファイル " GFXP_HIRAGANA "ヲ"; +char* D_8012CF88[] = { GFXP_HIRAGANA "シテモイイデスカ?", GFXP_HIRAGANA "ゲンザイヘンシュウチュウノ", + GFXP_KATAKANA "ファイル" GFXP_HIRAGANA "ハハキサレマス" }; +char* D_8012CF94 = GFXP_HIRAGANA "ハイ"; +char* D_8012CF98 = GFXP_HIRAGANA "イイエ"; +char* D_8012CF9C[] = { GFXP_HIRAGANA "シテイマス", GFXP_HIRAGANA "ウワガキ" }; +char* D_8012CFA4 = GFXP_HIRAGANA "シマシタ"; +char* D_8012CFA8 = "USE BYTE"; +char* D_8012CFAC = GFXP_HIRAGANA "ニシッパイ"; + +char* D_8012CFB0 = GFXP_KATAKANA "Eモ-ド" GFXP_HIRAGANA " コテイ"; +char D_8012CFB4[] = GFXP_KATAKANA "フレ-ム "; +char D_8012CFC4[] = GFXP_KATAKANA "キ- / "; +char D_8012CFD0[] = GFXP_HIRAGANA "(センタ-テン)\0\0\0\0\0"; +char D_8012CFE4[] = GFXP_HIRAGANA "(ゲンテン)\0\0\0\0\0\0"; +char D_8012CFF8[] = GFXP_KATAKANA "(プレイヤ-)\0\0\0\0\0"; +char D_8012D00C[] = GFXP_HIRAGANA "(イチアワセ)\0\0\0\0\0\0"; +char D_8012D020[] = GFXP_HIRAGANA "(セッテイ)\0\0\0\0\0\0\0\0"; +char D_8012D034[] = GFXP_HIRAGANA "(キャッカン)\0\0\0\0\0\0"; +char D_8012D048[] = GFXP_KATAKANA "ポイントNo. \0\0\0"; +char D_8012D05C[] = GFXP_HIRAGANA "ガカク \0\0\0"; +char D_8012D070[] = GFXP_KATAKANA "Nフレ-ム \0\0\0\0"; +char D_8012D084[] = GFXP_HIRAGANA "Zカイテン \0\0\0"; +char D_8012D098[] = GFXP_KATAKANA "モ-ド \0\0\0\0"; +char D_8012D0AC[] = " R" GFXP_HIRAGANA "チュウシン \0\0\0"; + +char D_8012D0C0[] = GFXP_HIRAGANA "Pジカン \0\0"; +char D_8012D0D4[] = GFXP_HIRAGANA "キョリ \0"; +char D_8012D0E4[] = GFXP_HIRAGANA "Xカイテン \0\0"; +char D_8012D0F8[] = GFXP_HIRAGANA "Yカイテン \0\0"; + +#define ACTION_E 0 +#define ACTION_SAVE 1 +#define ACTION_LOAD 2 +#define ACTION_CLEAR 3 + +#define MENU_INFO 0 +#define MENU_CALLBACK 1 +#define MENU_SUCCESS 2 +#define MENU_ERROR 9 + +#define DEMO_CTRL_MENU(actionIdx, menuIdx) (actionIdx * 100 + menuIdx) + +s32 DbCamera_SaveCallback(char* c); +s32 DbCamera_LoadCallback(char* c); +s32 DbCamera_ClearCallback(char* c); +s32 DbCamera_UpdateDemoControl(DbCamera* dbCamera, Camera* cam); + +static DbCamera* sDbCamPtr; +static s16 D_8016110C; +static DbCameraAnim sDbCamAnim; + +Vec3f* DbCamera_AddVecSph(Vec3f* out, Vec3f* in, VecSph* sph) { + Vec3f ret; + Vec3f vec; + + OLib_VecSphGeoToVec3f(&vec, sph); + + ret.x = in->x + vec.x; + ret.y = in->y + vec.y; + ret.z = in->z + vec.z; + *out = ret; + return out; +} + +Vec3f* DbCamera_CalcUpFromPitchYawRoll(Vec3f* dest, s16 pitch, s16 yaw, s16 roll) { + f32 sinPitch; + f32 cosPitch; + f32 sinYaw; + f32 cosYaw; + f32 sinNegRoll; + f32 cosNegRoll; + Vec3f spA4; + f32 sp54; + f32 sp4C; + f32 cosPitchCosYawSinRoll; + f32 negSinPitch; + f32 temp_f10_2; + f32 cosPitchcosYaw; + f32 temp_f14; + f32 negSinPitchSinYaw; + f32 negSinPitchCosYaw; + f32 cosPitchSinYaw; + f32 temp_f4_2; + f32 temp_f6; + f32 temp_f8; + f32 temp_f8_2; + f32 temp_f8_3; + + sinPitch = Math_SinS(pitch); + cosPitch = Math_CosS(pitch); + sinYaw = Math_SinS(yaw); + cosYaw = Math_CosS(yaw); + sinNegRoll = Math_SinS(-roll); + cosNegRoll = Math_CosS(-roll); + negSinPitch = -sinPitch; + negSinPitchSinYaw = negSinPitch * sinYaw; + negSinPitchCosYaw = negSinPitch * cosYaw; + temp_f14 = 1.0f - cosNegRoll; + cosPitchSinYaw = cosPitch * sinYaw; + sp54 = SQ(cosPitchSinYaw); + sp4C = (cosPitchSinYaw * sinPitch) * ((void)0, temp_f14); + cosPitchcosYaw = cosPitch * cosYaw; + temp_f4_2 = ((1.0f - sp54) * cosNegRoll) + sp54; + cosPitchCosYawSinRoll = cosPitchcosYaw * sinNegRoll; + temp_f6 = (cosPitchcosYaw * cosPitchSinYaw) * ((void)0, temp_f14); + temp_f10_2 = sinPitch * sinNegRoll; + spA4.x = ((negSinPitchSinYaw * temp_f4_2) + (cosPitch * (sp4C - cosPitchCosYawSinRoll))) + + (negSinPitchCosYaw * (temp_f6 + temp_f10_2)); + sp54 = SQ(sinPitch); + temp_f4_2 = (sinPitch * cosPitchcosYaw) * ((void)0, temp_f14); + temp_f8_3 = cosPitchSinYaw * sinNegRoll; + temp_f8 = sp4C + cosPitchCosYawSinRoll; + spA4.y = ((negSinPitchSinYaw * temp_f8) + (cosPitch * (((1.0f - sp54) * cosNegRoll) + sp54))) + + (negSinPitchCosYaw * (temp_f4_2 - temp_f8_3)); + temp_f8_2 = temp_f6 - temp_f10_2; + spA4.z = ((negSinPitchSinYaw * temp_f8_2) + (cosPitch * (temp_f4_2 + temp_f8_3))) + + (negSinPitchCosYaw * (((1.0f - SQ(cosPitchcosYaw)) * cosNegRoll) + SQ(cosPitchcosYaw))); + *dest = spA4; + return dest; +} + +char* DbCamera_SetTextValue(s16 value, char* str, u8 endIdx) { + char* strIter; + char sign; + + // OTRTODO - This crashes... + return; + + strIter = str + (s32)endIdx - 1; + str[endIdx] = '\0'; + + if (value >= 0) { + sign = ' '; + } else { + sign = '-'; + value = -value; + } + + do { + + // clang-format off + *strIter-- = (value % 10) + '0'; value /= 10; + // clang-format on + + } while (value != 0); + + if (sign == '-') { + *strIter-- = sign; + } + + while (strIter >= str) { + *str++ = ' '; + } + + return strIter; +} + +void DbCamera_Vec3SToF(Vec3s* in, Vec3f* out) { + out->x = in->x; + out->y = in->y; + out->z = in->z; +} + +void DbCamera_Vec3FToS(Vec3f* in, Vec3s* out) { + out->x = in->x; + out->y = in->y; + out->z = in->z; +} + +void DbCamera_CopyVec3f(Vec3f* in, Vec3f* out) { + out->x = in->x; + out->y = in->y; + out->z = in->z; +} + +void DbCamera_Vec3SToF2(Vec3s* in, Vec3f* out) { + out->x = in->x; + out->y = in->y; + out->z = in->z; +} + +void func_800B3F94(PosRot* posRot, Vec3f* vec, Vec3s* out) { + VecSph sph; + Vec3f tempVec; + OLib_Vec3fDiffToVecSphGeo(&sph, &posRot->pos, vec); + sph.yaw -= posRot->rot.y; + OLib_VecSphGeoToVec3f(&tempVec, &sph); + DbCamera_Vec3FToS(&tempVec, out); +} + +void func_800B3FF4(PosRot* posRot, Vec3f* vec, Vec3f* out) { + VecSph sph; + Vec3f tempVec; + DbCamera_CopyVec3f(vec, &tempVec); + OLib_Vec3fToVecSphGeo(&sph, &tempVec); + sph.yaw += posRot->rot.y; + DbCamera_AddVecSph(out, &posRot->pos, &sph); +} + +void func_800B404C(PosRot* posRot, Vec3s* vec, Vec3f* out) { + Vec3f tempVec; + DbCamera_Vec3SToF(vec, &tempVec); + func_800B3FF4(posRot, &tempVec, out); +} + +s32 func_800B4088(DbCamera* dbCamera, Camera* cam) { + CutsceneCameraPoint* position; + CutsceneCameraPoint* lookAt; + s32 i; + + position = &dbCamera->sub.position[dbCamera->sub.unkIdx]; + lookAt = &dbCamera->sub.lookAt[dbCamera->sub.unkIdx]; + + position->continueFlag = -1; + lookAt->continueFlag = position->continueFlag; + position->nextPointFrame = 0; + lookAt->nextPointFrame = 30; + lookAt->cameraRoll = position->cameraRoll = dbCamera->roll * (360.0f / 256.0f); + lookAt->viewAngle = position->viewAngle = dbCamera->fov; + + if (dbCamera->sub.mode != 1) { + DbCamera_Vec3FToS(&dbCamera->eye, &position->pos); + DbCamera_Vec3FToS(&dbCamera->at, &lookAt->pos); + } else { + func_800B3F94(&cam->playerPosRot, &dbCamera->at, &lookAt->pos); + func_800B3F94(&cam->playerPosRot, &dbCamera->eye, &position->pos); + } + + for (i = 0; i < (dbCamera->sub.nPoints - 2); i++) { + dbCamera->sub.position[i].continueFlag = dbCamera->sub.lookAt[i].continueFlag = 0; + } + + dbCamera->sub.position[i].continueFlag = dbCamera->sub.lookAt[i].continueFlag = -1; + + return dbCamera->sub.unkIdx; +} + +s16 func_800B41DC(DbCamera* dbCamera, s16 idx, Camera* cameraPtr) { + CutsceneCameraPoint* position = &dbCamera->sub.position[idx]; + CutsceneCameraPoint* lookAt = &dbCamera->sub.lookAt[idx]; + + if (dbCamera->sub.mode != 1) { + DbCamera_Vec3SToF2(&position->pos, &dbCamera->eye); + DbCamera_Vec3SToF2(&lookAt->pos, &dbCamera->at); + } else { + func_800B404C(&cameraPtr->playerPosRot, &lookAt->pos, &dbCamera->at); + func_800B404C(&cameraPtr->playerPosRot, &position->pos, &dbCamera->eye); + } + + dbCamera->roll = lookAt->cameraRoll; + dbCamera->rollDegrees = dbCamera->roll * (360.0f / 256.0f); + dbCamera->fov = lookAt->viewAngle; + return idx; +} + +s32 func_800B42C0(DbCamera* dbCamera, Camera* cameraPtr) { + + CutsceneCameraPoint* position = &dbCamera->sub.position[dbCamera->sub.unkIdx]; + CutsceneCameraPoint* lookAt = &dbCamera->sub.lookAt[dbCamera->sub.unkIdx]; + + position->continueFlag = lookAt->continueFlag = 0; + + if (dbCamera->sub.mode != 1) { + DbCamera_Vec3FToS(&dbCamera->eye, &position->pos); + DbCamera_Vec3FToS(&dbCamera->at, &lookAt->pos); + } else { + func_800B3F94(&cameraPtr->playerPosRot, &dbCamera->at, &lookAt->pos); + func_800B3F94(&cameraPtr->playerPosRot, &dbCamera->eye, &position->pos); + } + + return dbCamera->sub.unkIdx; +} + +s32 func_800B4370(DbCamera* dbCamera, s16 idx, Camera* cam) { + CutsceneCameraPoint* lookAt = &dbCamera->sub.lookAt[idx]; + CutsceneCameraPoint* position = &dbCamera->sub.position[idx]; + VecSph sph; + Vec3f at; + + if (dbCamera->sub.mode != 1) { + if (dbCamera->sub.unk_0C) { + DbCamera_Vec3SToF2(&position->pos, &dbCamera->at); + } else { + DbCamera_Vec3SToF2(&lookAt->pos, &dbCamera->at); + } + } else { + if (dbCamera->sub.unk_0C) { + func_800B404C(&cam->playerPosRot, &position->pos, &at); + } else { + func_800B404C(&cam->playerPosRot, &lookAt->pos, &at); + } + dbCamera->at = at; + } + sph.pitch = 0x2000; + sph.yaw -= 0x7FFF; + sph.r = 250.0f; + DbCamera_AddVecSph(&dbCamera->eye, &dbCamera->at, &sph); + dbCamera->roll = lookAt->cameraRoll; + dbCamera->rollDegrees = dbCamera->roll * (360.0f / 256.0f); + dbCamera->fov = lookAt->viewAngle; + return idx; +} + +void func_800B44E0(DbCamera* dbCamera, Camera* cam) { + s32 i; + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CRIGHT)) { + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_0A = 1; + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.unk_04 = 0; + + for (i = 0; i < (dbCamera->sub.nPoints - 2); i++) { + dbCamera->sub.position[i].continueFlag = dbCamera->sub.lookAt[i].continueFlag = 0; + } + + dbCamera->sub.position[i].continueFlag = dbCamera->sub.lookAt[i].continueFlag = -1; + } + + if (dbCamera->sub.nPoints < 6) { + if (sDbCamAnim.unk_0A != 0) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sDbCamAnim.unk_0A = 0; + } + func_8006376C(0x11, 0x17, 3, D_8012CEE0[0]); + func_8006376C(0x12, 0x18, 3, D_8012CEE4); + func_8006376C(0x10, 0x1A, 1, D_8012CEE8); + return; + } + + if (!func_800BB2B4(&sDbCamAnim.positionPos, &sDbCamAnim.roll, &sDbCamAnim.fov, dbCamera->sub.position, + &sDbCamAnim.keyframe, &sDbCamAnim.curFrame) && + !func_800BB2B4(&sDbCamAnim.lookAtPos, &sDbCamAnim.roll, &sDbCamAnim.fov, dbCamera->sub.lookAt, + &sDbCamAnim.keyframe, &sDbCamAnim.curFrame) && + sDbCamAnim.unk_0A == 1) { + Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sDbCamAnim.unk_04++; + + if (dbCamera->sub.nFrames > 0 && dbCamera->sub.nFrames < sDbCamAnim.unk_04) { + sDbCamAnim.unk_0A = 0; + func_8006376C(0xF, 0x1A, 1, D_8012CEEC); + } + + if (dbCamera->sub.mode != 1) { + DbCamera_CopyVec3f(&sDbCamAnim.positionPos, &dbCamera->eye); + DbCamera_CopyVec3f(&sDbCamAnim.lookAtPos, &dbCamera->at); + } else { + func_800B3FF4(&cam->playerPosRot, &sDbCamAnim.lookAtPos, &dbCamera->at); + func_800B3FF4(&cam->playerPosRot, &sDbCamAnim.positionPos, &dbCamera->eye); + } + + dbCamera->fov = sDbCamAnim.fov; + dbCamera->roll = sDbCamAnim.roll; + dbCamera->rollDegrees = sDbCamAnim.roll * (360.0f / 256.0f); + + DbCamera_SetTextValue(sDbCamAnim.unk_04, &D_8012CFB4[8], 4); + func_8006376C(0x10, 0x17, 3, D_8012CFB4); + D_8012CFC4[5] = ((sDbCamAnim.keyframe + 1) / 10) + '0'; + D_8012CFC4[6] = ((sDbCamAnim.keyframe + 1) % 10) + '0'; + D_8012CFC4[8] = ((dbCamera->sub.nPoints - 5) / 10) + '0'; + D_8012CFC4[9] = ((dbCamera->sub.nPoints - 5) % 10) + '0'; + func_8006376C(0x10, 0x18, 3, D_8012CFC4); + func_8006376C(0x10, 0x1A, 1, D_8012CEF0); + return; + } + + sDbCamAnim.unk_0A = 0; + func_8006376C(0xF, 0x1A, 1, D_8012CEEC); +} + +void DbCamera_PrintPoints(const char* name, s16 count, CutsceneCameraPoint* points) { + s32 i; + + osSyncPrintf("@@@static SplinedatZ %s[] = {\n", name); + for (i = 0; i < count; i++) { + osSyncPrintf("@@@ /* key frame %2d */ {\n", i); + osSyncPrintf("@@@ /* code */ %d,\n", points[i].continueFlag); + osSyncPrintf("@@@ /* z */ %d,\n", points[i].cameraRoll); + osSyncPrintf("@@@ /* T */ %d,\n", points[i].nextPointFrame); + osSyncPrintf("@@@ /* zoom */ %f,\n", points[i].viewAngle); + osSyncPrintf("@@@ /* pos */ { %d, %d, %d }\n", points[i].pos.x, points[i].pos.y, points[i].pos.z); + osSyncPrintf("@@@ },\n"); + } + osSyncPrintf("@@@};\n@@@\n"); +} + +void DbCamera_PrintF32Bytes(f32 value) { + f32 b = value; + char* a = (char*)&b; + + osSyncPrintf("\n@@@%d,%d,%d,%d,", a[0], a[1], a[2], a[3]); +} + +void DbCamera_PrintU16Bytes(u16 value) { + u16 pad; + u16 b = value; + char* a = (char*)&b; + + osSyncPrintf("\n@@@%d,%d,", a[0], a[1]); +} + +void DbCamera_PrintS16Bytes(s16 value) { + u16 pad; + s16 b = value; + char* a = (char*)&b; + + osSyncPrintf("\n@@@%d,%d,", a[0], a[1]); +} + +void DbCamera_PrintCutBytes(DbCameraCut* cut) { + CutsceneCameraPoint* point; + CutsceneCameraPoint* points; + s32 i; + + points = cut->lookAt; + osSyncPrintf("\n@@@ 0,0,0,2,\t/* Look Camera\t*/"); + osSyncPrintf("\n@@@ 0,1,\t/* dousa\t*/"); + + osSyncPrintf("\n@@@ 0,0,\t/* Start Flame\t*/"); + DbCamera_PrintU16Bytes(cut->nFrames); + osSyncPrintf("\t/* End Flame\t*/"); + + osSyncPrintf("\n@@@0,0,\t/* Dammy\t*/\n@@@ "); + for (i = 0; i < cut->nPoints; i++) { + point = points + i; + osSyncPrintf("\n@@@ %d, /* code */", point->continueFlag); + osSyncPrintf("\n@@@ %d, /* z */", point->cameraRoll); + DbCamera_PrintU16Bytes(point->nextPointFrame); + osSyncPrintf("\t/* sokudo\t*/"); + DbCamera_PrintF32Bytes(point->viewAngle); + osSyncPrintf("\t/* zoom\t*/"); + DbCamera_PrintS16Bytes(point->pos.x); + osSyncPrintf("\t/* x pos\t*/"); + DbCamera_PrintS16Bytes(point->pos.y); + osSyncPrintf("\t/* y pos\t*/"); + DbCamera_PrintS16Bytes(point->pos.z); + osSyncPrintf("\t/* z pos\t*/\n"); + osSyncPrintf("\n@@@0,0,\t/* Dammy\t*/\n@@@ "); + } + + points = cut->position; + osSyncPrintf("\n@@@ 0,0,0,1,\t/* Position Camera */"); + osSyncPrintf("\n@@@ 0,1,\t/* dousa\t*/"); + + osSyncPrintf("\n@@@ 0,0,\t/* Start Flame\t*/"); + DbCamera_PrintU16Bytes(cut->nFrames); + osSyncPrintf("\t/* End Flame\t*/"); + + osSyncPrintf("\n@@@0,0,\t/* Dammy\t*/\n@@@ "); + for (i = 0; i < cut->nPoints; i++) { + point = points + i; + osSyncPrintf("\n@@@ %d, /* code */", point->continueFlag); + osSyncPrintf("\n@@@ %d, /* z */", point->cameraRoll); + DbCamera_PrintU16Bytes(point->nextPointFrame); + osSyncPrintf("\t/* sokudo\t*/"); + DbCamera_PrintF32Bytes(point->viewAngle); + osSyncPrintf("\t/* zoom\t*/"); + DbCamera_PrintS16Bytes(point->pos.x); + osSyncPrintf("\t/* x pos\t*/"); + DbCamera_PrintS16Bytes(point->pos.y); + osSyncPrintf("\t/* y pos\t*/"); + DbCamera_PrintS16Bytes(point->pos.z); + osSyncPrintf("\t/* z pos\t*/"); + osSyncPrintf("\n@@@0,0,\t/* Dammy\t*/\n@@@ "); + } +} + +void DbCamera_Init(DbCamera* dbCamera, Camera* cameraPtr) { + dbCamera->sub.unk_104A.z = 0; + dbCamera->unk_44 = 0; + dbCamera->unk_00 = 0; + dbCamera->unk_34 = 0; + dbCamera->unk_3C = false; + dbCamera->unk_38 = -1; + dbCamera->unk_40 = -1; + dbCamera->roll = 0; + dbCamera->sub.unk_104A.y = dbCamera->sub.unk_104A.z; + dbCamera->sub.unk_104A.x = dbCamera->sub.unk_104A.z; + dbCamera->fov = 0.0f; + dbCamera->rollDegrees = 0.0f; + sGlobalCtx = cameraPtr->globalCtx; + dbCamera->sub.mode = 0; + dbCamera->sub.nFrames = -1; + dbCamera->sub.nPoints = 1; + dbCamera->sub.unkIdx = 0; + dbCamera->sub.unk_08 = 0; + dbCamera->sub.unk_0A = 0; + dbCamera->unk_78 = 0; + dbCamera->unk_7A = 0; + dbCamera->sub.demoCtrlMenu = DEMO_CTRL_MENU(ACTION_E, MENU_INFO); + dbCamera->sub.demoCtrlActionIdx = ACTION_E; + dbCamera->sub.demoCtrlToggleSwitch = 0; + dbCamera->unk_6C.x = 0; + dbCamera->unk_6C.y = 0; + dbCamera->unk_6C.z = 0; +} + +void DbgCamera_Enable(DbCamera* dbCamera, Camera* cam) { + dbCamera->at = cam->at; + dbCamera->eye = cam->eye; + dbCamera->unk_1C = cam->up; + dbCamera->fov = cam->fov; + dbCamera->roll = 0; + dbCamera->sub.nPoints = 1; + dbCamera->sub.unkIdx = 0; + dbCamera->sub.unk_08 = 0; + dbCamera->sub.unk_0A = 1; + dbCamera->sub.unk_0C = true; + dbCamera->unk_78 = 0; + dbCamera->unk_7A = 0; + dbCamera->rollDegrees = 0.0f; + func_800B4088(dbCamera, cam); +} + +void DbCamera_Update(DbCamera* dbCamera, Camera* cam) { + static s32 D_8012D10C = 100; + static s32 D_8012D110 = 0; + static s32 D_80161140; // bool + static s32 D_80161144; // bool + Vec3f* sp124; + f32 temp_f0_5; + s16 yaw; + f32 new_var2; + f32 temp_f2; + s16 pitch; + char sp111; + char sp110; + f32 temp_f2_2; + VecSph sp104; + VecSph spFC; + VecSph spF4; + PosRot* temp_s6; + Vec3f* eye; + Vec3f* at; + Vec3f* phi_s0; + Vec3f spD8; + s32 pad; + Vec3f* sp90; + Vec3f* sp80; + Vec3f* sp7C; + s32 i; + Vec3f spB8; + Vec3f spAC; + s16 spAA; + VecSph spA0; + + sp90 = &dbCamera->unk_54; + temp_s6 = &cam->playerPosRot; + at = &cam->at; + eye = &cam->eye; + *sp90 = temp_s6->pos; + dbCamera->unk_60 = cam->at; + sp80 = &dbCamera->eye; + sp7C = &dbCamera->at; + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_Z)) { + dbCamera->unk_00++; + dbCamera->unk_00 %= 3; + dbCamera->unk_38 = 1; + dbCamera->unk_44 = 0; + dbCamera->unk_40 = -1; + dbCamera->sub.demoCtrlActionIdx = 0; + sDbCamAnim.unk_0A = 0; + Audio_PlaySoundGeneral(NA_SE_SY_LOCK_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (dbCamera->unk_38 == -1) { + dbCamera->unk_38 = 1; + } else { + dbCamera->unk_38 = 0; + } + + switch (dbCamera->unk_00) { + case 0: + switch (dbCamera->unk_78) { + case 0: + sp124 = &dbCamera->unk_60; + D_80161144 = false; + D_80161140 = false; + break; + case 1: + sp124 = &dbCamera->unk_6C; + D_80161144 = false; + D_80161140 = false; + break; + case 2: + sp124 = &dbCamera->unk_54; + D_80161144 = false; + D_80161140 = true; + break; + } + break; + case 1: + switch (dbCamera->sub.unk_08) { + case 0: + D_80161144 = dbCamera->sub.unk_0C; + if (D_80161144) { + sp124 = sp80; + } else { + sp124 = sp7C; + } + D_80161140 = false; + break; + case 1: + D_80161144 = dbCamera->sub.unk_0C; + if (D_80161144) { + sp124 = sp80; + } else { + sp124 = sp7C; + } + D_80161140 = false; + break; + case 2: + D_80161144 = false; + D_80161140 = true; + sp124 = sp7C; + break; + } + break; + case 2: + DbCamera_UpdateDemoControl(dbCamera, cam); + return; + default: + break; + } + phi_s0 = sp124; + + if (!D_80161144) { + OLib_Vec3fDiffToVecSphGeo(&sp104, sp7C, sp80); + } else { + OLib_Vec3fDiffToVecSphGeo(&sp104, sp80, sp7C); + } + + if (dbCamera->unk_44 > 100) { + dbCamera->unk_44 = 100; + } + + new_var2 = ((dbCamera->unk_44 * 0.15f) + 0.2f); + temp_f2 = new_var2 * (sp104.r / 100.0f); + if ((dbCamera->unk_38 != 0) || dbCamera->unk_3C) { + if (D_80161144) { + *sp80 = *phi_s0; + } else { + *sp7C = *phi_s0; + } + + dbCamera->unk_3C = D_80161140; + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_B | BTN_L)) { + sp104.r += temp_f2; + + if (sp104.r > 30000.0f) { + sp104.r = 30000.0f; + } + + if (dbCamera->unk_40 == 7) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + + dbCamera->unk_40 = 7; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_B)) { + spFC = sp104; + spFC.r = temp_f2; + if (!D_80161144) { + spFC.yaw = sp104.yaw; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.pitch = -spFC.pitch; + spFC.yaw = BINANG_ROT180(sp104.yaw); + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 0xB) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 0xB; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_A | BTN_L)) { + sp104.r -= temp_f2; + if (sp104.r < 10.0f) { + sp104.r = 10.0f; + } + if (dbCamera->unk_40 == 8) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 8; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_A)) { + spFC = sp104; + spFC.r = -temp_f2; + if (!D_80161144) { + spFC.yaw = sp104.yaw; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.pitch = -spFC.pitch; + spFC.yaw = BINANG_ROT180(sp104.yaw); + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 0xC) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 0xC; + } else { + dbCamera->unk_44 = 0; + dbCamera->unk_40 = -1; + } + + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DDOWN | BTN_L)) { + spFC = sp104; + spFC.r = temp_f2; + spFC.pitch = 0; + if (!D_80161144) { + spFC.yaw = sp104.yaw; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.yaw = BINANG_ROT180(sp104.yaw); + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + + if (dbCamera->unk_40 == 1) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 1; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DUP | BTN_L)) { + spFC = sp104; + spFC.r = -temp_f2; + spFC.pitch = 0; + if (!D_80161144) { + spFC.yaw = sp104.yaw; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.yaw = BINANG_ROT180(sp104.yaw); + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 2) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 2; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DUP)) { + spFC = sp104; + spFC.r = temp_f2; + spFC.pitch = 0x3FFF; + spFC.yaw = sp104.yaw; + if (!D_80161144) { + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 3) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 3; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DDOWN)) { + spFC = sp104; + spFC.r = temp_f2; + spFC.pitch = -0x3FFF; + spFC.yaw = sp104.yaw; + if (!D_80161144) { + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 4) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 4; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, (BTN_DRIGHT | BTN_L)) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DRIGHT)) { + spFC = sp104; + spFC.r = temp_f2; + spFC.pitch = 0; + if (!D_80161144) { + spFC.yaw = sp104.yaw + 0x3FFF; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.yaw = sp104.yaw - 0x3FFF; + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 5) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 5; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, (BTN_DLEFT | BTN_L)) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DLEFT)) { + spFC = sp104; + spFC.r = temp_f2; + spFC.pitch = 0; + if (!D_80161144) { + spFC.yaw = sp104.yaw - 0x3FFF; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.yaw = sp104.yaw + 0x3FFF; + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 6) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 6; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_B | BTN_L)) { + sp104.r = sp104.r + temp_f2; + if (sp104.r > 30000.0f) { + sp104.r = 30000.0f; + } + if (dbCamera->unk_40 == 7) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 7; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_B)) { + spFC = sp104; + spFC.r = temp_f2; + if (!D_80161144) { + spFC.yaw = sp104.yaw; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.pitch = -spFC.pitch; + spFC.yaw = BINANG_ROT180(sp104.yaw); + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 0xB) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 0xB; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_A | BTN_L)) { + + sp104.r -= temp_f2; + if (sp104.r < 10.0f) { + sp104.r = 10.0f; + } + if (dbCamera->unk_40 == 8) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 8; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_A)) { + spFC = sp104; + spFC.r = -temp_f2; + if (!D_80161144) { + spFC.yaw = sp104.yaw; + DbCamera_AddVecSph(sp7C, sp7C, &spFC); + } else { + spFC.pitch = -spFC.pitch; + spFC.yaw = BINANG_ROT180(sp104.yaw); + DbCamera_AddVecSph(sp80, sp80, &spFC); + } + if (dbCamera->unk_40 == 0xC) { + dbCamera->unk_44++; + } else { + dbCamera->unk_44 = 0; + } + dbCamera->unk_40 = 0xC; + } else { + dbCamera->unk_44 = 0; + dbCamera->unk_40 = -1; + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_R)) { + if (dbCamera->unk_00 == 0) { + dbCamera->sub.unk_104A = cam->inputDir; + *sp7C = cam->at; + *sp80 = cam->eye; + dbCamera->unk_1C.x = 0.0f; + dbCamera->unk_1C.z = 0.0f; + dbCamera->unk_1C.y = 1.0f; + } else if (dbCamera->sub.unk_08 == 2) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.unk_08 = 0; + func_800B41DC(dbCamera, dbCamera->sub.unkIdx, cam); + } else { + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_R) && + CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.nPoints = dbCamera->sub.unkIdx + 1; + func_800B4088(dbCamera, cam); + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_R)) { + if (dbCamera->sub.unkIdx == 0x80) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_PUTAWAY, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_800B42C0(dbCamera, cam); + if (dbCamera->sub.unkIdx == (dbCamera->sub.nPoints - 1)) { + dbCamera->sub.unkIdx++; + dbCamera->sub.nPoints++; + func_800B4088(dbCamera, cam); + } + } + } + } + } else { + temp_f0_5 = sGlobalCtx->state.input[2].rel.stick_y; + temp_f2_2 = sGlobalCtx->state.input[2].rel.stick_x; + pitch = DEGF_TO_BINANG((SQ(temp_f0_5) / 600.0f) * 0.8f); + yaw = DEGF_TO_BINANG((SQ(temp_f2_2) / 600.0f) * 0.8f); + if (!D_80161144) { + sp104.pitch += (s16)((temp_f0_5 >= 0.0f) ? pitch : -pitch); + sp104.yaw += (s16)((temp_f2_2 >= 0.0f) ? yaw : -yaw); + DbCamera_AddVecSph(sp80, sp7C, &sp104); + dbCamera->sub.unk_104A.x = -sp104.pitch; + dbCamera->sub.unk_104A.y = BINANG_ROT180(sp104.yaw); + } else { + sp104.pitch += (s16)((temp_f0_5 >= 0.0f) ? -pitch : pitch); + sp104.yaw += (s16)((temp_f2_2 >= 0.0f) ? -yaw : yaw); + DbCamera_AddVecSph(sp7C, sp80, &sp104); + dbCamera->sub.unk_104A.x = sp104.pitch; + dbCamera->sub.unk_104A.y = sp104.yaw; + } + + OLib_Vec3fDiffToVecSphGeo(&spF4, sp80, sp7C); + DbCamera_CalcUpFromPitchYawRoll(&dbCamera->unk_1C, spF4.pitch, spF4.yaw, DEGF_TO_BINANG(dbCamera->rollDegrees)); + if (dbCamera->unk_00 == 1) { + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_CRIGHT)) { + cam->inputDir = dbCamera->sub.unk_104A; + new_var2 = OLib_Vec3fDist(&cam->at, &cam->eye); + cam->at = *sp7C; + spFC = sp104; + spFC.r = new_var2; + DbCamera_AddVecSph(&cam->eye, &cam->at, &spFC); + } + } + } + + if (dbCamera->unk_00 == 1) { + OREG(0) = 8; + func_8006376C(0xC, 5, 0, D_8012CEF4); + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_CRIGHT) && + !CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + func_800B44E0(dbCamera, cam); + } else { + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CRIGHT) && + CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + Audio_PlaySoundGeneral(NA_SE_SY_GET_RUPY, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + osSyncPrintf("@@@\n@@@\n@@@/* *** spline point data ** start here *** */\n@@@\n"); + DbCamera_PrintPoints("Lookat", dbCamera->sub.nPoints, dbCamera->sub.lookAt); + DbCamera_PrintPoints("Position", dbCamera->sub.nPoints, dbCamera->sub.position); + osSyncPrintf("@@@static short nPoints = %d;\n@@@\n", dbCamera->sub.nPoints); + osSyncPrintf("@@@static short nFrames = %d;\n@@@\n", dbCamera->sub.nFrames); + osSyncPrintf("@@@static short Mode = %d;\n@@@\n", dbCamera->sub.mode); + osSyncPrintf("@@@\n@@@\n@@@/* *** spline point data ** finish! *** */\n@@@\n"); + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.unk_08 = (dbCamera->sub.unk_08 + 1) % 3; + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CUP) && + CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (dbCamera->sub.unkIdx > 0) { + dbCamera->sub.unkIdx--; + } else { + dbCamera->sub.unkIdx = dbCamera->sub.nPoints - 1; + } + } else { + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CUP)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (dbCamera->sub.unkIdx > 0) { + dbCamera->sub.unkIdx--; + } else { + dbCamera->sub.unkIdx = dbCamera->sub.nPoints - 1; + } + if ((dbCamera->sub.unk_08 == 2) && (dbCamera->sub.unkIdx != (dbCamera->sub.nPoints - 1))) { + func_800B4370(dbCamera, dbCamera->sub.unkIdx, cam); + dbCamera->roll = 0; + dbCamera->fov = 60.0f; + dbCamera->rollDegrees = 0; + } else { + func_800B41DC(dbCamera, dbCamera->sub.unkIdx, cam); + dbCamera->fov = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle; + dbCamera->roll = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll; + dbCamera->rollDegrees = dbCamera->roll * 1.40625f; + } + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L) && + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CDOWN)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (dbCamera->sub.unkIdx < (dbCamera->sub.nPoints - 1)) { + dbCamera->sub.unkIdx++; + } else { + dbCamera->sub.unkIdx = 0; + } + } else { + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CDOWN)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (dbCamera->sub.unkIdx < (dbCamera->sub.nPoints - 1)) { + dbCamera->sub.unkIdx++; + } else { + dbCamera->sub.unkIdx = 0; + } + + if ((dbCamera->sub.unk_08 == 2) && (dbCamera->sub.unkIdx != (dbCamera->sub.nPoints - 1))) { + func_800B4370(dbCamera, dbCamera->sub.unkIdx, cam); + dbCamera->roll = 0; + dbCamera->fov = 60.0f; + dbCamera->rollDegrees = 0; + } else { + func_800B41DC(dbCamera, dbCamera->sub.unkIdx, cam); + dbCamera->fov = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle; + dbCamera->roll = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll; + dbCamera->rollDegrees = dbCamera->roll * 1.40625f; + } + } + } + + func_8006376C(0xA, 6, (dbCamera->sub.unk_08 == 0) ? 7 : 4, D_8012D00C); + func_8006376C(0x11, 6, (dbCamera->sub.unk_08 == 1) ? 7 : 4, D_8012D020); + func_8006376C(0x17, 6, (dbCamera->sub.unk_08 == 2) ? 7 : 4, D_8012D034); + if (dbCamera->sub.unkIdx == 0x80) { + func_8006376C(0x10, 0x1A, 1, D_8012CEF8[0]); + } else if (dbCamera->sub.unkIdx == (dbCamera->sub.nPoints - 1)) { + D_8012CEE0[7][10] = (dbCamera->sub.nPoints / 10) + '0'; + D_8012CEE0[7][11] = (dbCamera->sub.nPoints % 10) + '0'; + func_8006376C(0xF, 0x1A, 1, D_8012CEE0[7]); + } else { + D_8012CEE0[8][10] = ((dbCamera->sub.unkIdx + 1) / 10) + '0'; + D_8012CEE0[8][11] = ((dbCamera->sub.unkIdx + 1) % 10) + '0'; + D_8012CEE0[8][13] = ((dbCamera->sub.nPoints - 1) / 10) + '0'; + D_8012CEE0[8][14] = ((dbCamera->sub.nPoints - 1) % 10) + '0'; + func_8006376C(0xF, 0x1A, 1, D_8012CEE0[8]); + } + + switch (dbCamera->sub.unk_08) { + case 2: + dbCamera->unk_3C = false; + break; + case 0: + dbCamera->unk_3C = false; + if (dbCamera->sub.mode != 1) { + func_8006376C(0xD, 0x18, 3, !D_80161144 ? D_8012CF04 : D_8012CF08); + DbCamera_SetTextValue(sp104.pitch * 0.00549325f, &D_8012D0E4[11], 4); + func_8006376C(0xF, 0x17, 3, D_8012D0E4); + DbCamera_SetTextValue(sp104.yaw * 0.00549325f, &D_8012D0F8[11], 4); + func_8006376C(0xF, 0x18, 3, D_8012D0F8); + DbCamera_SetTextValue(sp104.r, &D_8012D0D4[8], 6); + func_8006376C(0xF, 0x19, 3, D_8012D0D4); + } else { + func_8006376C(0xE, 0x18, 3, D_8012CF0C); + func_8006376C(0x10, 0x16, 3, D_8012CF10); + sp110 = 'X'; + DbCamera_SetTextValue(temp_s6->pos.x, &sp111, 7); + func_8006376C(0x10, 0x17, 3, &sp110); + sp110 = 'Y'; + DbCamera_SetTextValue(temp_s6->pos.y, &sp111, 7); + func_8006376C(0x10, 0x18, 3, &sp110); + sp110 = 'Z'; + DbCamera_SetTextValue(temp_s6->pos.z, &sp111, 7); + func_8006376C(0x10, 0x19, 3, &sp110); + } + break; + case 1: + dbCamera->unk_3C = true; + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DUP)) { + Audio_PlaySoundGeneral(NA_SE_SY_ATTENTION_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (dbCamera->sub.unk_0A == 0) { + dbCamera->sub.unk_0A = 5; + } else { + dbCamera->sub.unk_0A--; + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DDOWN)) { + Audio_PlaySoundGeneral(NA_SE_SY_ATTENTION_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (dbCamera->sub.unk_0A == 5) { + dbCamera->sub.unk_0A = 0; + } else { + dbCamera->sub.unk_0A++; + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_ATTENTION_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + switch (dbCamera->sub.unk_0A) { + case 1: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].nextPointFrame -= 5; + } else { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].nextPointFrame--; + } + break; + case 3: + dbCamera->sub.mode--; + if (dbCamera->sub.mode == -1) { + dbCamera->sub.mode = 2; + } + if (dbCamera->sub.mode == 1) { + dbCamera->unk_78 = 2; + for (i = 0; i < dbCamera->sub.nPoints; i++) { + DbCamera_Vec3SToF2(&dbCamera->sub.lookAt[i].pos, &spD8); + func_800B3F94(temp_s6, &spD8, &dbCamera->sub.lookAt[i].pos); + DbCamera_Vec3SToF2(&dbCamera->sub.position[i].pos, &spD8); + func_800B3F94(temp_s6, &spD8, &dbCamera->sub.position[i].pos); + } + } else { + for (i = 0; i < dbCamera->sub.nPoints; i++) { + func_800B404C(temp_s6, &dbCamera->sub.lookAt[i].pos, &spD8); + DbCamera_Vec3FToS(&spD8, &dbCamera->sub.lookAt[i].pos); + func_800B404C(temp_s6, &dbCamera->sub.position[i].pos, &spD8); + DbCamera_Vec3FToS(&spD8, &dbCamera->sub.position[i].pos); + } + } + break; + case 4: + dbCamera->sub.unk_0C = false; + break; + case 2: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll -= 5; + dbCamera->roll = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll; + } else { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll--; + dbCamera->roll = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll; + } + dbCamera->rollDegrees = dbCamera->roll * 1.40625f; + break; + } + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DLEFT)) { + if ((D_8012D10C++ % 5) == 0) { + Audio_PlaySoundGeneral(NA_SE_SY_ATTENTION_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + switch (dbCamera->sub.unk_0A) { + case 0: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle -= 1.0f; + dbCamera->fov = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle; + } else { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle -= 0.2f; + dbCamera->fov = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle; + } + break; + case 5: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.nFrames -= 10; + } else { + dbCamera->sub.nFrames--; + } + + if (dbCamera->sub.nFrames < -1) { + if (dbCamera->sub.nPoints < 5) { + dbCamera->sub.nFrames = -1; + } else { + dbCamera->sub.nFrames = 0; + for (i = 0; i < dbCamera->sub.nPoints; i++) { + dbCamera->sub.nFrames += dbCamera->sub.lookAt[i].nextPointFrame; + } + i = dbCamera->sub.nFrames / dbCamera->sub.nPoints; + dbCamera->sub.nFrames -= (s16)(i * 5); + } + } + break; + } + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DRIGHT)) { + Audio_PlaySoundGeneral(NA_SE_SY_ATTENTION_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + switch (dbCamera->sub.unk_0A) { + case 1: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].nextPointFrame += 5; + } else { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].nextPointFrame++; + } + break; + case 3: + dbCamera->sub.mode++; + if (dbCamera->sub.mode == 3) { + dbCamera->sub.mode = 0; + } + if (dbCamera->sub.mode == 1) { + dbCamera->unk_78 = 2; + for (i = 0; i < dbCamera->sub.nPoints; i++) { + DbCamera_Vec3SToF2(&dbCamera->sub.lookAt[i].pos, &spD8); + func_800B3F94(temp_s6, &spD8, &dbCamera->sub.lookAt[i].pos); + DbCamera_Vec3SToF2(&dbCamera->sub.position[i].pos, &spD8); + func_800B3F94(temp_s6, &spD8, &dbCamera->sub.position[i].pos); + } + } else { + for (i = 0; i < dbCamera->sub.nPoints; i++) { + func_800B404C(temp_s6, &dbCamera->sub.lookAt[i].pos, &spD8); + DbCamera_Vec3FToS(&spD8, &dbCamera->sub.lookAt[i].pos); + func_800B404C(temp_s6, &dbCamera->sub.position[i].pos, &spD8); + DbCamera_Vec3FToS(&spD8, &dbCamera->sub.position[i].pos); + } + } + case 4: + dbCamera->sub.unk_0C = true; + break; + case 2: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll += 5; + dbCamera->roll = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll; + } else { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll++; + dbCamera->roll = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll; + } + dbCamera->rollDegrees = dbCamera->roll * 1.40625f; + break; + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_DRIGHT)) { + if ((D_8012D10C++ % 5) == 0) { + Audio_PlaySoundGeneral(NA_SE_SY_ATTENTION_ON, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + switch (dbCamera->sub.unk_0A) { + case 0: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle += 1.0f; + dbCamera->fov = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle; + } else { + dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle += 0.2f; + dbCamera->fov = dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle; + } + break; + case 5: + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + dbCamera->sub.nFrames += 10; + } else { + dbCamera->sub.nFrames++; + } + if (&dbCamera->at) {} + break; + } + } + + DbCamera_SetTextValue(dbCamera->sub.lookAt[dbCamera->sub.unkIdx].viewAngle, &D_8012D05C[10], 3); + func_8006376C(0x10, 0x14, (dbCamera->sub.unk_0A == 0) ? 7 : 4, D_8012D05C); + DbCamera_SetTextValue(dbCamera->sub.lookAt[dbCamera->sub.unkIdx].nextPointFrame, &D_8012D070[9], 3); + func_8006376C(0x10, 0x15, (dbCamera->sub.unk_0A == 1) ? 7 : 4, D_8012D070); + DbCamera_SetTextValue(dbCamera->sub.lookAt[dbCamera->sub.unkIdx].cameraRoll, &D_8012D084[10], 3); + func_8006376C(0x10, 0x16, (dbCamera->sub.unk_0A == 2) ? 7 : 4, D_8012D084); + func_8006376C(0xF, 0x17, (dbCamera->sub.unk_0A == 3) ? 7 : 4, + (dbCamera->sub.mode == 1) ? D_8012CF14 + : (dbCamera->sub.mode == 0) ? *D_8012CF18 + : D_8012CFB0); + if (dbCamera->sub.unk_0C) { + D_8012D05C[80] = '>'; + } else { + D_8012D05C[80] = '<'; + } + + D_8012D05C[81] = ' '; + D_8012D05C[94] = ' '; + + if (dbCamera->sub.unk_0C) { + D_8012D05C[95] = '>'; + } else { + D_8012D05C[95] = '<'; + } + D_8012D05C[96] = '\0'; + func_8006376C(0xF, 0x18, (dbCamera->sub.unk_0A == 4) ? 7 : 4, D_8012D0AC); + DbCamera_SetTextValue(dbCamera->sub.nFrames, &D_8012D0C0[10], 5); + func_8006376C(0xF, 0x19, (dbCamera->sub.unk_0A == 5) ? 7 : 4, + (dbCamera->sub.nFrames == -1) ? *D_8012CF24 : D_8012D0C0); + break; + } + + if (dbCamera->sub.mode != 1) { + func_8006376C(3, 0x16, + ((dbCamera->sub.unk_08 == 1) && (dbCamera->sub.unk_0A == 4) && !D_80161144) ? 7 + : !D_80161144 ? 4 + : 3, + D_8012CF30); + sp110 = 'X'; + DbCamera_SetTextValue(dbCamera->at.x, &sp111, 6); + func_8006376C(3, 0x17, 2, &sp110); + sp110 = 'Y'; + DbCamera_SetTextValue(dbCamera->at.y, &sp111, 6); + func_8006376C(3, 0x18, 2, &sp110); + sp110 = 'Z'; + DbCamera_SetTextValue(dbCamera->at.z, &sp111, 6); + func_8006376C(3, 0x19, 2, &sp110); + func_8006376C(0x1E, 0x16, + ((dbCamera->sub.unk_08 == 1) && (dbCamera->sub.unk_0A == 4) && D_80161144) ? 7 + : D_80161144 ? 4 + : 3, + D_8012CF34); + sp110 = 'X'; + DbCamera_SetTextValue(dbCamera->eye.x, &sp111, 6); + func_8006376C(0x1E, 0x17, 2, &sp110); + sp110 = 'Y'; + DbCamera_SetTextValue(dbCamera->eye.y, &sp111, 6); + func_8006376C(0x1E, 0x18, 2, &sp110); + sp110 = 'Z'; + DbCamera_SetTextValue(dbCamera->eye.z, &sp111, 6); + func_8006376C(0x1E, 0x19, 2, &sp110); + } else { + if (D_8012CEE0[0]) {} + OLib_Vec3fDiffToVecSphGeo(&spFC, sp90, sp7C); + spFC.yaw -= cam->playerPosRot.rot.y; + func_8006376C(3, 0x16, + ((dbCamera->sub.unk_08 == 1) && (dbCamera->sub.unk_0A == 4) && !D_80161144) ? 7 + : !D_80161144 ? 4 + : 3, + D_8012CF30); + DbCamera_SetTextValue(spFC.pitch * 0.00549325f, &D_8012D0E4[10], 4); + func_8006376C(3, 0x17, 3, D_8012D0E4); + DbCamera_SetTextValue(spFC.yaw * 0.00549325f, &D_8012D0F8[10], 4); + func_8006376C(3, 0x18, 3, D_8012D0F8); + DbCamera_SetTextValue(spFC.r, &D_8012D0D4[7], 6); + func_8006376C(3, 0x19, 3, D_8012D0D4); + OLib_Vec3fDiffToVecSphGeo(&spFC, sp90, sp80); + spFC.yaw -= cam->playerPosRot.rot.y; + func_8006376C(0x1E, 0x16, + ((dbCamera->sub.unk_08 == 1) && (dbCamera->sub.unk_0A == 4) && D_80161144) ? 7 + : D_80161144 ? 4 + : 3, + D_8012CF34); + DbCamera_SetTextValue(spFC.pitch * 0.00549325f, &D_8012D0E4[10], 4); + func_8006376C(0x1C, 0x17, 3, D_8012D0E4); + DbCamera_SetTextValue(spFC.yaw * 0.00549325f, &D_8012D0F8[10], 4); + func_8006376C(0x1C, 0x18, 3, D_8012D0F8); + DbCamera_SetTextValue(spFC.r, &D_8012D0D4[7], 6); + func_8006376C(0x1C, 0x19, 3, D_8012D0D4); + } + + DebugDisplay_AddObject(dbCamera->at.x, dbCamera->at.y + 1.0f, dbCamera->at.z, 0, 0, 0, 0.02f, 2.0f, 0.02f, + 0xFF, 0xFF, 0x7F, 0x40, 0, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(dbCamera->at.x, dbCamera->at.y + 1.0f, dbCamera->at.z, 0, 0, 0, 2.0f, 0.02f, 0.02f, + 0x7F, 0xFF, 0xFF, 0x40, 0, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(dbCamera->at.x, dbCamera->at.y + 1.0f, dbCamera->at.z, 0, 0, 0, 0.02f, 0.02f, 2.0f, + 0xFF, 0x7F, 0xFF, 0x40, 0, cam->globalCtx->view.gfxCtx); + if (dbCamera->sub.unk_08 == 2) { + for (i = 0; i < (dbCamera->sub.nPoints - 1); i++) { + if (dbCamera->sub.mode != 1) { + DbCamera_Vec3SToF2(&(dbCamera->sub.position + i)->pos, &spAC); + DbCamera_Vec3SToF2(&(dbCamera->sub.lookAt + i)->pos, &spB8); + } else { + func_800B404C(temp_s6, &(dbCamera->sub.lookAt + i)->pos, &spB8); + func_800B404C(temp_s6, &(dbCamera->sub.position + i)->pos, &spAC); + } + OLib_Vec3fDiffToVecSphGeo(&spFC, &spAC, &spB8); + spAA = dbCamera->sub.lookAt[i].cameraRoll * 0xB6; + if (i == dbCamera->sub.unkIdx) { + DebugDisplay_AddObject(spAC.x, spAC.y, spAC.z, spFC.pitch * -1, spFC.yaw, spAA, .5f, .5f, .5f, + 0x7F, 0xFF, 0x7F, 0x80, 5, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(spB8.x, spB8.y, spB8.z, spFC.pitch * -1, spFC.yaw, spAA, 1.5f, 2.0f, + 1.0f, 0x7F, 0xFF, 0x7F, 0x80, 4, cam->globalCtx->view.gfxCtx); + } else { + DebugDisplay_AddObject(spAC.x, spAC.y, spAC.z, spFC.pitch * -1, spFC.yaw, spAA, .5f, .5f, .5f, + 0xFF, 0x7F, 0x7F, 0x80, 5, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(spB8.x, spB8.y, spB8.z, spFC.pitch * -1, spFC.yaw, spAA, 1.5f, 2.0f, + 1.0f, 0xFF, 0x7F, 0x7F, 0x80, 4, cam->globalCtx->view.gfxCtx); + } + } + } + } + } else { + OREG(0) = 8; + dbCamera->roll = 0; + dbCamera->fov = 60.0f; + dbCamera->rollDegrees = dbCamera->roll * 1.40625f; + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->unk_78 = (dbCamera->unk_78 + 1) % 3; + dbCamera->unk_38 = -1; + } + + func_8006376C(0xE, 5, 0, D_8012CF38); + func_8006376C(9, 6, (dbCamera->unk_78 == 0) ? 7 : 4, D_8012CFD0); + func_8006376C(0x11, 6, (dbCamera->unk_78 == 1) ? 7 : 4, D_8012CFE4); + func_8006376C(0x18, 6, (dbCamera->unk_78 == 2) ? 7 : 4, D_8012CFF8); + func_8006376C(3, 0x16, D_80161144 ? 3 : 4, D_8012CF30); + sp110 = 'X'; + DbCamera_SetTextValue(dbCamera->at.x, &sp111, 6); + func_8006376C(3, 0x17, 2, &sp110); + sp110 = 'Y'; + DbCamera_SetTextValue(dbCamera->at.y, &sp111, 6); + func_8006376C(3, 0x18, 2, &sp110); + sp110 = 'Z'; + DbCamera_SetTextValue(dbCamera->at.z, &sp111, 6); + func_8006376C(3, 0x19, 2, &sp110); + func_8006376C(0x1E, 0x16, D_80161144 ? 4 : 3, D_8012CF34); + sp110 = 'X'; + DbCamera_SetTextValue(dbCamera->eye.x, &sp111, 6); + func_8006376C(0x1E, 0x17, 2, &sp110); + sp110 = 'Y'; + DbCamera_SetTextValue(dbCamera->eye.y, &sp111, 6); + func_8006376C(0x1E, 0x18, 2, &sp110); + sp110 = 'Z'; + DbCamera_SetTextValue(dbCamera->eye.z, &sp111, 6); + func_8006376C(0x1E, 0x19, 2, &sp110); + func_8006376C(0xD, 0x18, 3, !D_80161144 ? D_8012CF04 : D_8012CF08); + DbCamera_SetTextValue(sp104.pitch * 0.00549325f, &D_8012D0E4[11], 4); + func_8006376C(0xF, 0x17, 3, D_8012D0E4); + DbCamera_SetTextValue(sp104.yaw * 0.00549325f, &D_8012D0F8[11], 4); + func_8006376C(0xF, 0x18, 3, D_8012D0F8); + DbCamera_SetTextValue(sp104.r, &D_8012D0D4[8], 6); + func_8006376C(0xF, 0x19, 3, D_8012D0D4); + if (dbCamera->unk_3C) { + func_8006376C(0x10, 0x1A, 1, D_8012CF3C); + } else { + func_8006376C(0x10, 0x1A, 1, D_8012CF40); + } + + D_8012D110++; + D_8012D110 %= 50; + + OLib_Vec3fDiffToVecSphGeo(&spA0, &cam->eye, &cam->at); + DebugDisplay_AddObject(dbCamera->at.x, dbCamera->at.y + 1.0f, dbCamera->at.z, 0, 0, 0, 0.02f, 2.0f, 0.02f, 0xFF, + 0xFF, 0x7F, 0x2D, 0, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(dbCamera->at.x, dbCamera->at.y + 1.0f, dbCamera->at.z, 0, 0, 0, 2.0f, 0.02f, 0.02f, 0x7F, + 0xFF, 0xFF, 0x2D, 0, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(dbCamera->at.x, dbCamera->at.y + 1.0f, dbCamera->at.z, 0, 0, 0, 0.02f, 0.02f, 2.0f, 0xFF, + 0x7F, 0xFF, 0x2D, 0, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(cam->eye.x, cam->eye.y, cam->eye.z, spA0.pitch * -1, spA0.yaw, 0, .5f, .5f, .5f, 0xFF, + 0x7F, 0x7F, 0x80, 5, cam->globalCtx->view.gfxCtx); + DebugDisplay_AddObject(cam->at.x, cam->at.y, cam->at.z, spA0.pitch * -1, spA0.yaw, 0, 1.5f, 2.0f, 1.0f, 0xFF, + 0x7F, 0x7F, 0x80, 4, cam->globalCtx->view.gfxCtx); + OLib_Vec3fDiffToVecSphGeo(&spA0, &cam->eyeNext, &cam->at); + DebugDisplay_AddObject(cam->eyeNext.x, cam->eyeNext.y, cam->eyeNext.z, spA0.pitch * -1, spA0.yaw, 0, .5f, .5f, + .5f, 0xFF, 0xC0, 0x7F, 0x50, 5, cam->globalCtx->view.gfxCtx); + } +} + +static s16 sCurFileIdx; +static s16 sLastFileIdx; // holds the file index of the slot to move +// is the size correct? todo: add ALIGN32 for sizeof in Mempak functions, replace 0xF with sizeof() +static DbCameraCut sDbCameraCuts[16]; +static char D_80161250[0x80]; +static char sLetters[26]; +static char D_801612EA; +static s32 sAllocSize; + +s32 DbCamera_GetFirstAvailableLetter(void) { + s32 i; + for (i = 0; i < ARRAY_COUNT(sLetters); i++) { + switch (sLetters[i]) { + case 'O': + break; + default: + return 'A' + i; + } + } + + return '?'; +} + +char DbCamera_InitCut(s32 idx, DbCameraSub* sub) { + s32 i; + + sDbCameraCuts[idx].unk_01 = 0x61; + sDbCameraCuts[idx].letter = DbCamera_GetFirstAvailableLetter(); + D_80161250[0x3F + sDbCameraCuts[idx].letter] = 'O'; + + i = sub->nPoints * sizeof(CutsceneCameraPoint); + sDbCameraCuts[idx].lookAt = DebugArena_MallocDebug(i, "../db_camera.c", 2748); + if (sDbCameraCuts[idx].lookAt == NULL) { + // "Debug camera memory allocation failure" + osSyncPrintf("%s: %d: デバッグカメラ メモリ確保失敗!!\n", "../db_camera.c", 2751); + return '?'; + } + + sDbCameraCuts[idx].position = DebugArena_MallocDebug(i, "../db_camera.c", 2754); + if (sDbCameraCuts[idx].position == NULL) { + // "Debug camera memory allocation failure" + osSyncPrintf("%s: %d: デバッグカメラ メモリ確保失敗!!\n", "../db_camera.c", 2757); + DebugArena_FreeDebug(sDbCameraCuts[idx].lookAt, "../db_camera.c", 2758); + sDbCameraCuts[idx].lookAt = NULL; + return '?'; + } + + sDbCameraCuts[idx].mode = sub->mode; + sDbCameraCuts[idx].nFrames = sub->nFrames; + sDbCameraCuts[idx].nPoints = sub->nPoints; + + for (i = 0; i < sub->nPoints; i++) { + sDbCameraCuts[idx].lookAt[i] = sub->lookAt[i]; + sDbCameraCuts[idx].position[i] = sub->position[i]; + } + + return sDbCameraCuts[idx].letter; +} + +void DbCamera_ResetCut(s32 idx, s32 shouldFree) { + if (sDbCameraCuts[idx].letter != '?') { + D_80161250[0x3F + sDbCameraCuts[idx].letter] = 'X'; + } + + if (shouldFree) { + DebugArena_FreeDebug(sDbCameraCuts[idx].lookAt, "../db_camera.c", 2784); + DebugArena_FreeDebug(sDbCameraCuts[idx].position, "../db_camera.c", 2785); + } + + sDbCameraCuts[idx].letter = '?'; + sDbCameraCuts[idx].lookAt = NULL; + sDbCameraCuts[idx].position = NULL; + sDbCameraCuts[idx].mode = 0; + sDbCameraCuts[idx].nFrames = 0; + sDbCameraCuts[idx].nPoints = 0; +} + +s32 DbCamera_CalcMempakAllocSize(void) { + s32 i; + + sAllocSize = 0; + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + if (sDbCameraCuts[i].letter != '?') { + sAllocSize += ALIGN32(sDbCameraCuts[i].nPoints * sizeof(CutsceneCameraPoint)) * 2; + } + } + sAllocSize += 0x100; + sAllocSize = ALIGN256(sAllocSize); + return sAllocSize; +} + +s32 DbCamera_GetMempakAllocSize(void) { + return sAllocSize; +} + +s32 DbCamera_LoadCallback(char* c) { + s32 i; + ptrdiff_t size; + s32 off; + + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + if (sDbCameraCuts[i].letter != '?') { + DbCamera_ResetCut(i, true); + sLetters[i] = 'X'; + } + } + + if (!Mempak_Read(2, *c, sDbCameraCuts, 0, sizeof(sDbCameraCuts))) { + return false; + } + + off = sizeof(sDbCameraCuts); + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + if (sDbCameraCuts[i].letter != '?') { + size = sDbCameraCuts[i].nPoints * sizeof(CutsceneCameraPoint); + + sDbCameraCuts[i].lookAt = DebugArena_MallocDebug(ALIGN32(size), "../db_camera.c", 2844); + if (sDbCameraCuts[i].lookAt == NULL) { + // "Debug camera memory allocation failure" + osSyncPrintf("%s: %d: デバッグカメラ メモリ確保失敗!!\n", "../db_camera.c", 2847); + return false; + } + if (!Mempak_Read(2, *c, sDbCameraCuts[i].lookAt, off, ALIGN32(size))) { + return false; + } + off += ALIGN32(size); + + sDbCameraCuts[i].position = DebugArena_MallocDebug(ALIGN32(size), "../db_camera.c", 2855); + if (sDbCameraCuts[i].position == NULL) { + // "Debug camera memory allocation failure" + osSyncPrintf("%s: %d: デバッグカメラ メモリ確保失敗!!\n", "../db_camera.c", 2858); + return false; + } + if (!Mempak_Read(2, *c, sDbCameraCuts[i].position, off, ALIGN32(size))) { + return false; + } + off += ALIGN32(size); + + D_80161250[0x3F + sDbCameraCuts[i].letter] = 'O'; + } + } + + return true; +} + +s32 DbCamera_SaveCallback(char* c) { + s32 pad[2]; + s32 ret; + size_t freeSize; + ptrdiff_t off; + ptrdiff_t size; + s32 i; + + ret = Mempak_GetFileSize(2, *c); + freeSize = Mempak_GetFreeBytes(2); + + if ((u32)sAllocSize < (freeSize + ret)) { + if (!Mempak_Alloc(2, c, sAllocSize)) { + return false; + } + + if (!Mempak_Write(2, *c, sDbCameraCuts, 0, sizeof(sDbCameraCuts))) { + Mempak_DeleteFile(2, *c); + return false; + } + + off = sizeof(sDbCameraCuts); + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + if (sDbCameraCuts[i].letter != '?') { + size = sDbCameraCuts[i].nPoints * sizeof(CutsceneCameraPoint); + + ret = Mempak_Write(2, *c, sDbCameraCuts[i].lookAt, off, ALIGN32(size)); + if (!ret) { + break; + } + off += ALIGN32(size); + + ret = Mempak_Write(2, *c, sDbCameraCuts[i].position, off, ALIGN32(size)); + if (!ret) { + break; + } + + off += ALIGN32(size); + } + ret = true; + } + + if (ret) { + return *c; + } else { + Mempak_DeleteFile(2, *c); + return false; + } + } + + return false; +} + +s32 DbCamera_ClearCallback(char* c) { + return Mempak_DeleteFile(2, *c); +} + +void DbCamera_DrawSlotLetters(char* str, s16 y, s16 x, s32 colorId) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + str[i * 2 + 1] = sDbCameraCuts[i].letter; + str[i * 2 + 0] = '-'; + } + + str[0x14] = str[i * 2 + 1] = '\0'; + func_8006376C(x, y, colorId, str); + str[0x14] = str[i * 2 + 0] = '-'; + func_8006376C(x + 0x14, y, colorId, str + 0x14); +} + +void DbCamera_PrintAllCuts(Camera* cam) { + s32 i; + + Audio_PlaySoundGeneral(NA_SE_SY_GET_RUPY, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + osSyncPrintf("@@@\n@@@\n@@@/* ****** spline point data ** start here ***** */\n@@@\n"); + + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + DbCameraCut* cut = &sDbCameraCuts[i]; + if (cut->nPoints != 0) { + if (i != 0) { + osSyncPrintf("@@@\n@@@/* ** %d ** */\n@@@\n", i); + } + + DbCamera_PrintPoints("Lookat", cut->nPoints, cut->lookAt); + DbCamera_PrintPoints("Position", cut->nPoints, cut->position); + osSyncPrintf("@@@static short nPoints = %d;\n@@@\n", cut->nPoints); + osSyncPrintf("@@@static short nFrames = %d;\n@@@\n", cut->nFrames); + osSyncPrintf("@@@static short Mode = %d;\n@@@\n", cut->mode); + } + } + + osSyncPrintf("@@@\n@@@\n@@@/* ****** spline point data ** finish! ***** */\n@@@\n"); +} + +char D_8012D114[] = GFXP_KATAKANA "フレ-ム "; +char D_8012D128[] = GFXP_KATAKANA "ト-タル "; +char D_8012D13C[] = GFXP_KATAKANA "キ- / "; + +s32 func_800B91B0(Camera* cam, DbCamera* dbCamera) { + s32 pointCount; + s32 curPoint; + + while (sDbCameraCuts[D_8016110C].letter == '?') { + D_8016110C++; + if (D_8016110C >= ARRAY_COUNT(sDbCameraCuts) - 1) { + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.unk_04 = 0; + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_0A = 0; + D_8016110C = 0; + return 0; + } + } + + if (!func_800BB2B4(&sDbCamAnim.positionPos, &sDbCamAnim.roll, &sDbCamAnim.fov, sDbCameraCuts[D_8016110C].position, + &sDbCamAnim.keyframe, &sDbCamAnim.curFrame) && + !func_800BB2B4(&sDbCamAnim.lookAtPos, &sDbCamAnim.roll, &sDbCamAnim.fov, sDbCameraCuts[D_8016110C].lookAt, + &sDbCamAnim.keyframe, &sDbCamAnim.curFrame)) { + + D_8012D13C[7] = ((sDbCamAnim.keyframe + 1) / 10) + '0'; + D_8012D13C[8] = ((sDbCamAnim.keyframe + 1) % 10) + '0'; + D_8012D13C[10] = ((sDbCameraCuts[D_8016110C].nPoints - 5) / 10) + '0'; + D_8012D13C[11] = ((sDbCameraCuts[D_8016110C].nPoints - 5) % 10) + '0'; + DbCamera_SetTextValue(sDbCamAnim.unk_04, &D_8012D114[10], 4); + func_8006376C(0xF, 0x16, 3, D_8012D114); + DbCamera_SetTextValue(sDbCamAnim.unk_0C, &D_8012D128[10], 4); + func_8006376C(0xF, 0x17, 3, D_8012D128); + func_8006376C(0xF, 0x18, 3, D_8012D13C); + func_8006376C(0x10, 0x1A, 1, D_8012CEF0); + + sDbCamAnim.unk_04++; + sDbCamAnim.unk_0C++; + + if (sDbCameraCuts[D_8016110C].nFrames > 0 && sDbCameraCuts[D_8016110C].nFrames < sDbCamAnim.unk_04) { + D_8016110C++; + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.unk_04 = 0; + sDbCamAnim.keyframe = 0; + return D_8016110C | 0x8000; + } + + if (sDbCameraCuts[D_8016110C].mode != 1) { + DbCamera_CopyVec3f(&sDbCamAnim.positionPos, &dbCamera->eye); + DbCamera_CopyVec3f(&sDbCamAnim.lookAtPos, &dbCamera->at); + } else { + func_800B3FF4(&cam->playerPosRot, &sDbCamAnim.lookAtPos, &dbCamera->at); + func_800B3FF4(&cam->playerPosRot, &sDbCamAnim.positionPos, &dbCamera->eye); + } + + dbCamera->fov = sDbCamAnim.fov; + dbCamera->roll = sDbCamAnim.roll; + dbCamera->rollDegrees = sDbCamAnim.roll * (360.0f / 256.0f); + + } else { + D_8016110C++; + sDbCamAnim.keyframe = 0; + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.unk_04 = 0; + if (D_8016110C == ARRAY_COUNT(sDbCameraCuts) - 1) { + D_8016110C = 0; + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_0A = 0; + return -1; + } + } + + return D_8016110C | 0x8000; +} + +void DbCamera_Reset(Camera* cam, DbCamera* dbCam) { + s32 i; + + D_801612EA = '*'; + for (i = 0; i < ARRAY_COUNT(sLetters); i++) { + sLetters[i] = 'X'; + } + + for (i = 0; i < 0xF; i++) { + DbCamera_ResetCut(i, false); + } + sDbCamPtr = dbCam; + D_8016110C = 0; + sCurFileIdx = 0; + sLastFileIdx = -1; + sDbCamAnim.unk_0A = 0; +} + +s32 DbCamera_UpdateDemoControl(DbCamera* dbCamera, Camera* cam) { + static s32 sMempakFiles; + static u32 sDbCameraColors[] = { + 4, 4, 4, 7, 4, 4, + }; + static s32 sMempakFilesize = 0; + s32 i; + s32 idx1; + s32 idx2; + s16 idx3; + char sp74[(ARRAY_COUNT(sDbCameraCuts) - 1 + 4) * 2]; + DbCameraCut sp64; + VecSph sp5C; + s32 (*callbacks[])(char*) = { DbCamera_SaveCallback, DbCamera_LoadCallback, DbCamera_ClearCallback }; + + func_8006376C(0xE, 5, 0, D_8012CF44); // DEMO CONTROL + + idx1 = sCurFileIdx >> 1; + idx2 = sLastFileIdx >> 1; + + switch (dbCamera->sub.demoCtrlActionIdx) { + case ACTION_SAVE: + case ACTION_LOAD: + case ACTION_CLEAR: + switch (dbCamera->sub.demoCtrlMenu) { + + case DEMO_CTRL_MENU(ACTION_SAVE, MENU_INFO): + case DEMO_CTRL_MENU(ACTION_LOAD, MENU_INFO): + case DEMO_CTRL_MENU(ACTION_CLEAR, MENU_INFO): { + if ((1 << sCurFileIdx) & sMempakFiles) { + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DLEFT) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DRIGHT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlToggleSwitch ^= 1; + } + D_8012CEE0[41][9] = sCurFileIdx + 'A'; + func_8006376C(0xA, 7, 5, D_8012CEE0[41]); + func_8006376C(0x10, 7, 5, D_8012CF60[dbCamera->sub.demoCtrlActionIdx]); + func_8006376C(0x14, 7, 5, D_8012CF88[0]); + + func_8006376C(0x11, 8, dbCamera->sub.demoCtrlToggleSwitch ? 4 : 7, D_8012CF94); + func_8006376C(0x15, 8, dbCamera->sub.demoCtrlToggleSwitch ? 7 : 4, D_8012CF98); + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_A)) { + if (dbCamera->sub.demoCtrlToggleSwitch == 0) { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlMenu++; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlMenu = 0; + } + } + } else { + if (dbCamera->sub.demoCtrlMenu == 100) { + dbCamera->sub.demoCtrlMenu++; + } else { + dbCamera->sub.demoCtrlToggleSwitch ^= 1; + D_8012CF84[9] = sCurFileIdx + 'A'; + func_8006376C(0xD, 7, 5, D_8012CF88[-1]); // todo: find something better + func_8006376C(0x12, 7, 5, D_8012CF80); + func_8006376C(0xD, 9, dbCamera->sub.demoCtrlToggleSwitch ? 1 : 6, "PRESS B BUTTON"); + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_B)) { + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.demoCtrlMenu = 0; + return 1; + } + goto block_2; + } + + case DEMO_CTRL_MENU(ACTION_SAVE, MENU_CALLBACK): + case DEMO_CTRL_MENU(ACTION_LOAD, MENU_CALLBACK): + case DEMO_CTRL_MENU(ACTION_CLEAR, MENU_CALLBACK): { + D_8012CEE0[41][9] = sCurFileIdx + 'A'; + func_8006376C(0xC, 7, 5, D_8012CEE0[41]); + func_8006376C(0x12, 7, 5, D_8012CF60[dbCamera->sub.demoCtrlActionIdx]); + func_8006376C(0x16, 7, 5, D_8012CF9C[0]); + + if (callbacks[dbCamera->sub.demoCtrlActionIdx - 1](&D_8012CF84[9])) { + dbCamera->sub.demoCtrlMenu++; + return 1; + } else { + dbCamera->sub.demoCtrlMenu += 8; + return 1; + } + } + + case DEMO_CTRL_MENU(ACTION_SAVE, MENU_SUCCESS): + case DEMO_CTRL_MENU(ACTION_LOAD, MENU_SUCCESS): + case DEMO_CTRL_MENU(ACTION_CLEAR, MENU_SUCCESS): { + dbCamera->sub.demoCtrlToggleSwitch ^= 1; + D_8012CEE0[41][9] = sCurFileIdx + 'A'; + func_8006376C(0xD, 7, 5, D_8012CEE0[41]); + func_8006376C(0x13, 7, 5, D_8012CF60[dbCamera->sub.demoCtrlMenu / 100]); + func_8006376C(0x17, 7, 5, D_8012CFA4); + func_8006376C(0xD, 9, (dbCamera->sub.demoCtrlToggleSwitch != 0) ? 1 : 6, "PRESS B BUTTON"); + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_A) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_B)) { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (dbCamera->sub.demoCtrlMenu == DEMO_CTRL_MENU(ACTION_LOAD, MENU_SUCCESS)) { + dbCamera->sub.demoCtrlActionIdx = ACTION_E; + } + dbCamera->sub.demoCtrlMenu = DEMO_CTRL_MENU(ACTION_E, MENU_INFO); + return 1; + } + goto block_2; + } + + case DEMO_CTRL_MENU(ACTION_SAVE, MENU_ERROR): + case DEMO_CTRL_MENU(ACTION_LOAD, MENU_ERROR): + case DEMO_CTRL_MENU(ACTION_CLEAR, MENU_ERROR): { + dbCamera->sub.demoCtrlToggleSwitch ^= 1; + D_8012CEE0[41][9] = sCurFileIdx + 'A'; + func_8006376C(0xD, 7, 5, D_8012CEE0[(dbCamera->sub.demoCtrlMenu / 100) + 32]); + func_8006376C(0x11, 7, 5, D_8012CFAC); + func_8006376C(0x17, 7, 5, D_8012CFA4); + func_8006376C(0xD, 9, (dbCamera->sub.demoCtrlToggleSwitch != 0) ? 1 : 6, "PRESS B BUTTON"); + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_A) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_B)) { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.demoCtrlMenu -= 9; + } + block_2: + return 1; + } + + case 1: + goto block_1; + + default: { + if (Mempak_Init(2)) { + sMempakFiles = Mempak_FindFile(2, 'A', 'E'); + dbCamera->sub.demoCtrlMenu = DEMO_CTRL_MENU(ACTION_E, MENU_CALLBACK); + DbCamera_CalcMempakAllocSize(); + if ((1 << sCurFileIdx) & sMempakFiles) { + sMempakFilesize = Mempak_GetFileSize(2, sCurFileIdx + 'A'); + dbCamera->sub.demoCtrlActionIdx = ACTION_LOAD; + } else { + sMempakFilesize = 0; + dbCamera->sub.demoCtrlActionIdx = ACTION_SAVE; + } + block_1: + idx2 = 1; + for (i = 0; i < 5; i++) { + sp74[i * 2 + 1] = (sMempakFiles & idx2) ? i + 'A' : '?'; + sp74[i * 2 + 0] = '-'; + + idx2 <<= 1; + } + sp74[i * 2 + 0] = '-'; + sp74[i * 2 + 1] = '\0'; + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DRIGHT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (sCurFileIdx >= 4) { + sCurFileIdx = 0; + } else { + sCurFileIdx++; + } + + if ((1 << sCurFileIdx) & sMempakFiles) { + sMempakFilesize = Mempak_GetFileSize(2, sCurFileIdx + 'A'); + dbCamera->sub.demoCtrlActionIdx = ACTION_LOAD; + } else { + sMempakFilesize = 0; + dbCamera->sub.demoCtrlActionIdx = ACTION_SAVE; + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (sCurFileIdx <= 0) { + sCurFileIdx = 4; + } else { + sCurFileIdx--; + } + + if ((1 << sCurFileIdx) & sMempakFiles) { + sMempakFilesize = Mempak_GetFileSize(2, sCurFileIdx + 'A'); + dbCamera->sub.demoCtrlActionIdx = ACTION_LOAD; + } else { + sMempakFilesize = 0; + dbCamera->sub.demoCtrlActionIdx = ACTION_SAVE; + } + } + idx3 = dbCamera->sub.demoCtrlActionIdx; + func_8006376C(0xE, 7, 5, D_8012CF50[idx3]); + func_8006376C(0xF, 7, 4, sp74); + + func_8006376C((sCurFileIdx * 2) + 0x10, 7, 7, "_"); // cursor + DbCamera_SetTextValue(DbCamera_GetMempakAllocSize(), sp74, 6); + func_8006376C(0xD, 9, 6, D_8012CF78); // NEED BYTE + func_8006376C(0x11, 9, 4, sp74); + DbCamera_SetTextValue(Mempak_GetFreeBytes(2), sp74, 6); + func_8006376C(0xD, 0xA, 6, D_8012CF74); // FREE BYTE + func_8006376C(0x11, 0xA, 4, sp74); + if (sMempakFilesize != 0) { + DbCamera_SetTextValue(sMempakFilesize, sp74, 6); + func_8006376C(0xD, 0xB, 7, D_8012CFA8); + func_8006376C(0x11, 0xB, 4, sp74); + } + + idx1 = (dbCamera->sub.demoCtrlActionIdx + 2); + func_8006376C(0xF, 0x16, 1, D_8012CF7C); + func_8006376C(0x12, 0x17, sDbCameraColors[idx1], D_8012CF64); + func_8006376C(0x12, 0x18, sDbCameraColors[idx1 - 1], D_8012CF68); + func_8006376C(0x12, 0x19, sDbCameraColors[idx1 - 2], D_8012CF6C); + func_8006376C(0xE, dbCamera->sub.demoCtrlActionIdx + 0x16, 7, D_8012CF0C); // current selection + func_8006376C(0xD, 0x1A, 5, D_8012CF60[0]); + func_8006376C(0x14, 0x1A, 5, D_8012CF70); + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DUP)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlActionIdx = (dbCamera->sub.demoCtrlActionIdx - 1) % 4u; + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DDOWN)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlActionIdx = (dbCamera->sub.demoCtrlActionIdx + 1) % 4u; + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_A)) { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlToggleSwitch = 0; + dbCamera->sub.demoCtrlMenu = DEMO_CTRL_MENU(dbCamera->sub.demoCtrlActionIdx, MENU_INFO); + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_B)) { + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlActionIdx = ACTION_E; + return 1; + } + goto block_2; + } else { + func_8006376C(0xC, 0x1A, 4, D_8012CF60[0]); + func_8006376C(0x13, 0x1A, 4, D_8012CF80); + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_B) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DUP) || + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DDOWN)) { + + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + dbCamera->sub.demoCtrlActionIdx = ACTION_E; + } + return 2; + } + break; + } + } + break; + + default: { + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DUP)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.demoCtrlMenu = DEMO_CTRL_MENU(ACTION_E, MENU_INFO); + dbCamera->sub.demoCtrlActionIdx = (dbCamera->sub.demoCtrlActionIdx - 1) % 4u; + sCurFileIdx = 0; + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DDOWN)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + dbCamera->sub.demoCtrlMenu = DEMO_CTRL_MENU(ACTION_E, MENU_INFO); + dbCamera->sub.demoCtrlActionIdx = (dbCamera->sub.demoCtrlActionIdx + 1) % 4u; + sCurFileIdx = 0; + } + + DbCamera_DrawSlotLetters(sp74, 7, 5, 4); + + if (sDbCamAnim.unk_0A != 0) { + func_8006376C(4, 7, 5, D_8012CF4C); + func_8006376C(D_8016110C * 2 + 6, 7, 7, ">"); + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CUP)) { + if (D_8016110C > 0) { + D_8016110C--; + } + + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_04 = 0; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CDOWN)) { + if (D_8016110C < 14) { + D_8016110C++; + } + + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_04 = 0; + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CLEFT)) { + sDbCamAnim.unk_0A = 0; + Interface_ChangeAlpha(2); + ShrinkWindow_SetVal(0); + D_8016110C = 0; + return 2; + } + + if (func_800B91B0(cam, dbCamera) == 0) { + Interface_ChangeAlpha(2); + ShrinkWindow_SetVal(0); + Audio_PlaySoundGeneral(NA_SE_SY_GET_RUPY, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + OLib_Vec3fDiffToVecSphGeo(&sp5C, &dbCamera->eye, &dbCamera->at); + DbCamera_CalcUpFromPitchYawRoll(&dbCamera->unk_1C, sp5C.pitch, sp5C.yaw, + DEGF_TO_BINANG(dbCamera->rollDegrees)); + return 2; + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[1].press.button, BTN_CRIGHT)) { + D_8015FCC8 = 0; + gSaveContext.cutsceneIndex = 0xFFFD; + gSaveContext.cutsceneTrigger = 1; + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_04 = 0; + sDbCamAnim.unk_0A = 1; + sDbCamAnim.unk_0C = 0; + D_8016110C = 0; + Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_L)) { + if (sp74[sCurFileIdx] == '?') { + sLastFileIdx = -1; + D_801612EA = '*'; + } else { + sLastFileIdx = sCurFileIdx; + D_801612EA = sDbCameraCuts[idx1].letter; + } + if (1) {} + } else if (!CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L)) { + if (sLastFileIdx != -1) { + switch (sp74[sCurFileIdx]) { + case '?': + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + sDbCameraCuts[idx1] = sDbCameraCuts[idx2]; + sp74[sCurFileIdx] = '?'; // useless + DbCamera_ResetCut(idx2, false); + break; + case '-': + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + sp64 = sDbCameraCuts[idx2]; + if (sLastFileIdx < sCurFileIdx) { + // rotate right + for (i = idx2; i < idx1 - 1 && i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + sDbCameraCuts[i] = sDbCameraCuts[i + 1]; + } + sDbCameraCuts[idx1 - 1] = sp64; + } else if (sCurFileIdx < sLastFileIdx) { + // rotate left + for (i = idx2; idx1 < i && i > 0; i--) { + sDbCameraCuts[i] = sDbCameraCuts[i - 1]; + } + sDbCameraCuts[idx1] = sp64; + } + + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + sp74[i * 2 + 1] = sDbCameraCuts[i].letter; + } + break; + default: + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + break; + } + } + sLastFileIdx = -1; + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_A)) { + if (sp74[sCurFileIdx] == '?') { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sp74[sCurFileIdx] = DbCamera_InitCut(idx1, &dbCamera->sub); + if (sp74[sCurFileIdx] == '?') { + func_8006376C(0xF, 0x18, 7, D_8012CF48); + } + } + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_B)) { + if (sp74[sCurFileIdx] != '?' && sp74[sCurFileIdx] != '-') { + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sp74[sCurFileIdx] = '?'; + DbCamera_ResetCut(idx1, true); + } + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_R)) { + if (sp74[sCurFileIdx] != '?' && sp74[sCurFileIdx] != '-') { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + for (i = 0; i < sDbCameraCuts[idx1].nPoints; i++) { + dbCamera->sub.lookAt[i] = sDbCameraCuts[idx1].lookAt[i]; + } + // why use another loop for that... + for (i = 0; i < sDbCameraCuts[idx1].nPoints; i++) { + dbCamera->sub.position[i] = sDbCameraCuts[idx1].position[i]; + } + + dbCamera->sub.mode = sDbCameraCuts[idx1].mode; + dbCamera->sub.nFrames = sDbCameraCuts[idx1].nFrames; + dbCamera->sub.nPoints = sDbCameraCuts[idx1].nPoints; + dbCamera->sub.unkIdx = 0; + func_800B41DC(dbCamera, dbCamera->sub.unkIdx, cam); + sp74[sCurFileIdx] = '?'; + DbCamera_ResetCut(idx1, true); + dbCamera->unk_00 = 1; + } + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DRIGHT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (sCurFileIdx == 0x1E) { + sCurFileIdx = 0; + } else { + sCurFileIdx++; + } + } + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_DLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sCurFileIdx = (sCurFileIdx == 0) ? 0x1E : sCurFileIdx - 1; + } + + if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L) && + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CRIGHT)) { + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + osSyncPrintf("###%2d:(%c) (%d %d) %d %d %d\n", i, sDbCameraCuts[i].letter, + sDbCameraCuts[i].position, sDbCameraCuts[i].lookAt, sDbCameraCuts[i].nFrames, + sDbCameraCuts[i].nPoints, sDbCameraCuts[i].mode); + } + DbCamera_PrintAllCuts(cam); + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].cur.button, BTN_L) && + CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_GET_RUPY, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + for (i = 0; i < ARRAY_COUNT(sDbCameraCuts) - 1; i++) { + if (sDbCameraCuts[i].nPoints != 0) { + osSyncPrintf("\n@@@ /* CUT [%d]\t*/", i); + DbCamera_PrintCutBytes(&sDbCameraCuts[i]); + } + } + } else if (CHECK_BTN_ALL(sGlobalCtx->state.input[2].press.button, BTN_CRIGHT)) { + sDbCamAnim.curFrame = 0.0f; + sDbCamAnim.keyframe = 0; + sDbCamAnim.unk_04 = 0.0f; + sDbCamAnim.unk_0A = 1; + sDbCamAnim.unk_0C = 0; + Interface_ChangeAlpha(50); + ShrinkWindow_SetVal(0x20); + D_8016110C = 0; + Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + func_8006376C(4, 7, 5, D_8012CF50[0]); + sp74[1] = 0; + if (sLastFileIdx != -1) { + sp74[0] = D_801612EA; + func_8006376C(sLastFileIdx + 5, 7, 2, sp74); + } else { + sp74[0] = '_'; + } + func_8006376C(sCurFileIdx + 5, 7, 7, sp74); + + break; + } + } + + return 1; +} + +void func_800BB03C(Camera* cam) { + func_800B91B0(cam, sDbCamPtr); +} + +void func_800BB060(void) { + sDbCamAnim.unk_0A = 0; +} + +s32 func_800BB06C(void) { + return sDbCamPtr->unk_00 == 2 && sDbCamAnim.unk_0A != 0; +} diff --git a/soh/src/code/debug_malloc.c b/soh/src/code/debug_malloc.c new file mode 100644 index 000000000..c1c4f99d0 --- /dev/null +++ b/soh/src/code/debug_malloc.c @@ -0,0 +1,113 @@ +#include "global.h" + +#include + +#define LOG_SEVERITY_NOLOG 0 +#define LOG_SEVERITY_ERROR 2 +#define LOG_SEVERITY_VERBOSE 3 + +s32 gDebugArenaLogSeverity = LOG_SEVERITY_ERROR; +Arena sDebugArena; + +void DebugArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action) { + if (ptr == NULL) { + if (gDebugArenaLogSeverity >= LOG_SEVERITY_ERROR) { + // "%s: %u bytes %s failed\n" + osSyncPrintf("%s: %u バイトの%sに失敗しました\n", name, size, action); + __osDisplayArena(&sDebugArena); + return; + } + } else if (gDebugArenaLogSeverity >= LOG_SEVERITY_VERBOSE) { + // "%s: %u bytes %s succeeded\n" + osSyncPrintf("%s: %u バイトの%sに成功しました\n", name, size, action); + } +} + +void* DebugArena_Malloc(size_t size) { + void* ptr = __osMalloc(&sDebugArena, size); + + DebugArena_CheckPointer(ptr, size, "debug_malloc", "確保"); // "Secure" + return ptr; +} + +void* DebugArena_MallocDebug(size_t size, const char* file, s32 line) { + void* ptr = __osMallocDebug(&sDebugArena, size, file, line); + + DebugArena_CheckPointer(ptr, size, "debug_malloc_DEBUG", "確保"); // "Secure" + return ptr; +} + +void* DebugArena_MallocR(size_t size) { + void* ptr = __osMallocR(&sDebugArena, size); + + DebugArena_CheckPointer(ptr, size, "debug_malloc_r", "確保"); // "Secure" + return ptr; +} + +void* DebugArena_MallocRDebug(size_t size, const char* file, s32 line) { + void* ptr = __osMallocRDebug(&sDebugArena, size, file, line); + + DebugArena_CheckPointer(ptr, size, "debug_malloc_r_DEBUG", "確保"); // "Secure" + return ptr; +} + +void* DebugArena_Realloc(void* ptr, size_t newSize) { + ptr = __osRealloc(&sDebugArena, ptr, newSize); + DebugArena_CheckPointer(ptr, newSize, "debug_realloc", "再確保"); // "Re-securing" + return ptr; +} + +void* DebugArena_ReallocDebug(void* ptr, size_t newSize, const char* file, s32 line) { + ptr = __osReallocDebug(&sDebugArena, ptr, newSize, file, line); + DebugArena_CheckPointer(ptr, newSize, "debug_realloc_DEBUG", "再確保"); // "Re-securing" + return ptr; +} + +void DebugArena_Free(void* ptr) { + __osFree(&sDebugArena, ptr); +} + +void DebugArena_FreeDebug(void* ptr, const char* file, s32 line) { + __osFreeDebug(&sDebugArena, ptr, file, line); +} + +void* DebugArena_Calloc(size_t num, size_t size) { + void* ret; + size_t n = num * size; + + ret = __osMalloc(&sDebugArena, n); + if (ret != NULL) { + memset(ret, 0, n); + } + + DebugArena_CheckPointer(ret, n, "debug_calloc", "確保"); + return ret; +} + +void DebugArena_Display(void) { + // "Zelda heap display" ("Zelda" should probably have been changed to "Debug") + osSyncPrintf("ゼルダヒープ表示\n"); + __osDisplayArena(&sDebugArena); +} + +void DebugArena_GetSizes(u32* outMaxFree, u32* outFree, u32* outAlloc) { + ArenaImpl_GetSizes(&sDebugArena, outMaxFree, outFree, outAlloc); +} + +void DebugArena_Check(void) { + __osCheckArena(&sDebugArena); +} + +void DebugArena_Init(void* start, size_t size) { + gDebugArenaLogSeverity = LOG_SEVERITY_NOLOG; + __osMallocInit(&sDebugArena, start, size); +} + +void DebugArena_Cleanup(void) { + gDebugArenaLogSeverity = LOG_SEVERITY_NOLOG; + __osMallocCleanup(&sDebugArena); +} + +u8 DebugArena_IsInitalized(void) { + return __osMallocIsInitalized(&sDebugArena); +} diff --git a/soh/src/code/fault.c b/soh/src/code/fault.c new file mode 100644 index 000000000..c4d6d746e --- /dev/null +++ b/soh/src/code/fault.c @@ -0,0 +1,1132 @@ +#include "global.h" +#include "vt.h" +#include "alloca.h" + +// data +const char* sExceptionNames[] = { + "Interrupt", + "TLB modification", + "TLB exception on load", + "TLB exception on store", + "Address error on load", + "Address error on store", + "Bus error on inst.", + "Bus error on data", + "System call exception", + "Breakpoint exception", + "Reserved instruction", + "Coprocessor unusable", + "Arithmetic overflow", + "Trap exception", + "Virtual coherency on inst.", + "Floating point exception", + "Watchpoint exception", + "Virtual coherency on data", + "Unimplemented operation", + "Invalid operation", + "Division by zero", + "Overflow", + "Underflow", + "Inexact operation", +}; + +// bss +extern FaultThreadStruct* sFaultStructPtr; +extern u8 sFaultIsWaitingForInput; +extern char sFaultStack[0x600]; +extern StackEntry sFaultThreadInfo; +extern FaultThreadStruct gFaultStruct; + +void Fault_SleepImpl(u32 duration) { + u64 value = (duration * OS_CPU_COUNTER) / 1000ull; + + Sleep_Cycles(value); +} + +void Fault_ClientProcessThread(void* arg) { + FaultClientContext* ctx = (FaultClientContext*)arg; + + if (ctx->callback != 0) { + ctx->ret = ctx->callback(ctx->param0, ctx->param1); + } + + if (ctx->queue != NULL) { + osSendMesg(ctx->queue, ctx->msg, OS_MESG_BLOCK); + } +} + +void Fault_ProcessClientContext(FaultClientContext* ctx) +{ +#if 0 + OSMesgQueue queue; + OSMesg msg; + OSMesg recMsg; + OSThread* thread; + OSTimer timer; + u32 timerMsgVal; + + timerMsgVal = 666; + thread = NULL; + + osCreateMesgQueue(&queue, &msg, 1); + ctx->queue = &queue; + ctx->msg = NULL; + + if (sFaultStructPtr->currClientThreadSp != 0) { + thread = alloca(sizeof(OSThread)); + osCreateThread(thread, 2, Fault_ClientProcessThread, ctx, sFaultStructPtr->currClientThreadSp, + OS_PRIORITY_APPMAX - 1); + osStartThread(thread); + } else { + Fault_ClientProcessThread(ctx); + } + + while (true) { + osSetTimer(&timer, OS_USEC_TO_CYCLES(1000000), 0, &queue, (OSMesg)timerMsgVal); + osRecvMesg(&queue, &recMsg, OS_MESG_BLOCK); + + if (recMsg != (OSMesg)666) { + break; + } + + if (!sFaultIsWaitingForInput) { + ctx->ret = -1; + break; + } + } + + osStopTimer(&timer); + + if (thread != NULL) { + osStopThread(thread); + osDestroyThread(thread); + } +#endif +} + +u32 Fault_ProcessClient(u32 callback, u32 param0, u32 param1) { + FaultClientContext a; + + a.callback = callback; + a.param0 = param0; + a.param1 = param1; + a.ret = 0; + Fault_ProcessClientContext(&a); + return a.ret; +} + +void Fault_AddClient(FaultClient* client, void* callback, void* param0, void* param1) +{ +#if 0 + OSIntMask mask; + s32 alreadyExists = false; + + mask = osSetIntMask(1); + + { + FaultClient* iter = sFaultStructPtr->clients; + + while (iter != NULL) { + if (iter == client) { + alreadyExists = true; + goto end; + } + iter = iter->next; + } + } + + client->callback = callback; + client->param1 = param0; + client->param2 = param1; + client->next = sFaultStructPtr->clients; + sFaultStructPtr->clients = client; + +end: + osSetIntMask(mask); + if (alreadyExists) { + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddClient: %08x は既にリスト中にある\n" VT_RST, client); + } +#endif +} + +void Fault_RemoveClient(FaultClient* client) +{ +#if 0 + FaultClient* iter; + FaultClient* lastIter; + OSIntMask mask; + u32 listIsEmpty; + + iter = sFaultStructPtr->clients; + listIsEmpty = 0; + lastIter = NULL; + + mask = osSetIntMask(1); + + while (iter != NULL) { + if (iter == client) { + if (lastIter != NULL) { + lastIter->next = client->next; + } else { + sFaultStructPtr->clients = client; + if (sFaultStructPtr->clients) { + sFaultStructPtr->clients = client->next; + } else { + listIsEmpty = 1; + } + } + break; + } + + lastIter = iter; + iter = iter->next; + } + + osSetIntMask(mask); + + if (listIsEmpty) { + osSyncPrintf(VT_COL(RED, WHITE) "fault_RemoveClient: %08x リスト不整合です\n" VT_RST, client); + } +#endif +} + +void Fault_AddAddrConvClient(FaultAddrConvClient* client, void* callback, void* param) +{ +#if 0 + OSIntMask mask; + u32 alreadyExists = false; + + mask = osSetIntMask(1); + + { + FaultAddrConvClient* iter = sFaultStructPtr->addrConvClients; + + while (iter != NULL) { + if (iter == client) { + alreadyExists = true; + goto end; + } + iter = iter->next; + } + } + + client->callback = callback; + client->param = param; + client->next = sFaultStructPtr->addrConvClients; + sFaultStructPtr->addrConvClients = client; + +end: + osSetIntMask(mask); + if (alreadyExists) { + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddressConverterAddClient: %08x は既にリスト中にある\n" VT_RST, client); + } +#endif +} + +void Fault_RemoveAddrConvClient(FaultAddrConvClient* client) +{ +#if 0 + FaultAddrConvClient* iter; + FaultAddrConvClient* lastIter; + OSIntMask mask; + u32 listIsEmpty; + + iter = sFaultStructPtr->addrConvClients; + listIsEmpty = 0; + lastIter = NULL; + + mask = osSetIntMask(1); + + while (iter != NULL) { + if (iter == client) { + if (lastIter != NULL) { + lastIter->next = client->next; + } else { + sFaultStructPtr->addrConvClients = client; + if (sFaultStructPtr->addrConvClients != NULL) { + sFaultStructPtr->addrConvClients = client->next; + } else { + listIsEmpty = 1; + } + } + break; + } + + lastIter = iter; + iter = iter->next; + } + + osSetIntMask(mask); + + if (listIsEmpty) { + osSyncPrintf(VT_COL(RED, WHITE) "fault_AddressConverterRemoveClient: %08x は既にリスト中にある\n" VT_RST, + client); + } +#endif +} + +u32 Fault_ConvertAddress(FaultAddrConvClient* client) +{ +#if 0 + u32 ret; + FaultAddrConvClient* iter = sFaultStructPtr->addrConvClients; + + while (iter != NULL) { + if (iter->callback != 0) { + ret = Fault_ProcessClient(iter->callback, client, iter->param); + if ((s32)ret == -1) { + Fault_RemoveAddrConvClient(iter); + } else if (ret != 0) { + return ret; + } + } + iter = iter->next; + } + +#endif + return 0; +} + +void Fault_Sleep(u32 duration) { + Fault_SleepImpl(duration); +} + +void Fault_PadCallback(Input* input) { + //! @bug This function is not called correctly and thus will crash from reading a bad pointer at 0x800C7E4C + PadMgr_RequestPadData(input, 0); +} + +void Fault_UpdatePadImpl() +{ +#if 0 + sFaultStructPtr->padCallback(&sFaultStructPtr->padInput); +#endif +} + +u32 Fault_WaitForInputImpl() +{ +#if 0 + Input* input = &sFaultStructPtr->padInput; + s32 count = 600; + u32 kDown; + + while (true) { + Fault_Sleep(0x10); + Fault_UpdatePadImpl(); + + kDown = input->press.button; + + if (kDown == BTN_L) { + sFaultStructPtr->faultActive = !sFaultStructPtr->faultActive; + } + + if (sFaultStructPtr->faultActive) { + if (count-- < 1) { + return false; + } + } else { + if (kDown == BTN_A || kDown == BTN_DRIGHT) { + return false; + } + + if (kDown == BTN_DLEFT) { + return true; + } + + if (kDown == BTN_DUP) { + FaultDrawer_SetOsSyncPrintfEnabled(true); + } + + if (kDown == BTN_DDOWN) { + FaultDrawer_SetOsSyncPrintfEnabled(false); + } + } + } +#endif +} + +void Fault_WaitForInput() { +#if 0 + sFaultIsWaitingForInput = 1; + Fault_WaitForInputImpl(); + sFaultIsWaitingForInput = 0; +#endif +} + +void Fault_DrawRec(s32 x, s32 y, s32 w, s32 h, u16 color) { + FaultDrawer_DrawRecImpl(x, y, x + w - 1, y + h - 1, color); +} + +void Fault_FillScreenBlack() { + FaultDrawer_SetForeColor(0xFFFF); + FaultDrawer_SetBackColor(1); + FaultDrawer_FillScreen(); + FaultDrawer_SetBackColor(0); +} + +void Fault_FillScreenRed() { + FaultDrawer_SetForeColor(0xFFFF); + FaultDrawer_SetBackColor(0xF001); + FaultDrawer_FillScreen(); + FaultDrawer_SetBackColor(0); +} + +void Fault_DrawCornerRec(u16 color) { + Fault_DrawRec(0x16, 0x10, 8, 1, color); +} + +void Fault_PrintFReg(s32 idx, f32* value) { + u32 raw = *(u32*)value; + s32 v0 = ((raw & 0x7F800000) >> 0x17) - 0x7F; + + if ((v0 >= -0x7E && v0 < 0x80) || raw == 0) { + FaultDrawer_Printf("F%02d:%14.7e ", idx, *value); + } else { + FaultDrawer_Printf("F%02d: %08x(16) ", idx, raw); + } +} + +void Fault_LogFReg(s32 idx, f32* value) { + u32 raw = *(u32*)value; + s32 v0 = ((raw & 0x7F800000) >> 0x17) - 0x7F; + + if ((v0 >= -0x7E && v0 < 0x80) || raw == 0) { + osSyncPrintf("F%02d:%14.7e ", idx, *value); + } else { + osSyncPrintf("F%02d: %08x(16) ", idx, *(u32*)value); + } +} + +void Fault_PrintFPCR(u32 value) { + s32 i; + u32 flag = 0x20000; + + FaultDrawer_Printf("FPCSR:%08xH ", value); + for (i = 0; i < 6; i++) { + if (value & flag) { + FaultDrawer_Printf("(%s)", sExceptionNames[i + 18]); + break; + } + flag >>= 1; + } + FaultDrawer_Printf("\n"); +} + +void Fault_LogFPCR(u32 value) { + s32 i; + u32 flag = 0x20000; + + osSyncPrintf("FPCSR:%08xH ", value); + for (i = 0; i < 6; i++) { + if (value & flag) { + osSyncPrintf("(%s)\n", sExceptionNames[i + 18]); + break; + } + flag >>= 1; + } +} + +void Fault_PrintThreadContext(OSThread* t) { + __OSThreadContext* ctx; + s32 causeStrIdx = (s32)((((u32)t->context.cause >> 2) & 0x1F) << 0x10) >> 0x10; + + if (causeStrIdx == 0x17) { + causeStrIdx = 0x10; + } + if (causeStrIdx == 0x1F) { + causeStrIdx = 0x11; + } + + FaultDrawer_FillScreen(); + FaultDrawer_SetCharPad(-2, 4); + FaultDrawer_SetCursor(0x16, 0x14); + + ctx = &t->context; + FaultDrawer_Printf("THREAD:%d (%d:%s)\n", t->id, causeStrIdx, sExceptionNames[causeStrIdx]); + FaultDrawer_SetCharPad(-1, 0); + + FaultDrawer_Printf("PC:%08xH SR:%08xH VA:%08xH\n", (u32)ctx->pc, (u32)ctx->sr, (u32)ctx->badvaddr); + FaultDrawer_Printf("AT:%08xH V0:%08xH V1:%08xH\n", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1); + FaultDrawer_Printf("A0:%08xH A1:%08xH A2:%08xH\n", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2); + FaultDrawer_Printf("A3:%08xH T0:%08xH T1:%08xH\n", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1); + FaultDrawer_Printf("T2:%08xH T3:%08xH T4:%08xH\n", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4); + FaultDrawer_Printf("T5:%08xH T6:%08xH T7:%08xH\n", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7); + FaultDrawer_Printf("S0:%08xH S1:%08xH S2:%08xH\n", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2); + FaultDrawer_Printf("S3:%08xH S4:%08xH S5:%08xH\n", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5); + FaultDrawer_Printf("S6:%08xH S7:%08xH T8:%08xH\n", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8); + FaultDrawer_Printf("T9:%08xH GP:%08xH SP:%08xH\n", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp); + //FaultDrawer_Printf("S8:%08xH RA:%08xH LO:%08xH\n\n", (u32)ctx->s8, (u32)ctx->ra, (u32)ctx->lo); + + Fault_PrintFPCR(ctx->fpcsr); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(0, &ctx->fp0.f.f_even); + Fault_PrintFReg(2, &ctx->fp2.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(4, &ctx->fp4.f.f_even); + Fault_PrintFReg(6, &ctx->fp6.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(8, &ctx->fp8.f.f_even); + Fault_PrintFReg(0xA, &ctx->fp10.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(0xC, &ctx->fp12.f.f_even); + Fault_PrintFReg(0xE, &ctx->fp14.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(0x10, &ctx->fp16.f.f_even); + Fault_PrintFReg(0x12, &ctx->fp18.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(0x14, &ctx->fp20.f.f_even); + Fault_PrintFReg(0x16, &ctx->fp22.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(0x18, &ctx->fp24.f.f_even); + Fault_PrintFReg(0x1A, &ctx->fp26.f.f_even); + FaultDrawer_Printf("\n"); + Fault_PrintFReg(0x1C, &ctx->fp28.f.f_even); + Fault_PrintFReg(0x1E, &ctx->fp30.f.f_even); + FaultDrawer_Printf("\n"); + FaultDrawer_SetCharPad(0, 0); +} + +void Fault_LogThreadContext(OSThread* t) { + __OSThreadContext* ctx; + s32 causeStrIdx = (s32)((((u32)t->context.cause >> 2) & 0x1F) << 0x10) >> 0x10; + + if (causeStrIdx == 0x17) { + causeStrIdx = 0x10; + } + if (causeStrIdx == 0x1F) { + causeStrIdx = 0x11; + } + + ctx = &t->context; + osSyncPrintf("\n"); + osSyncPrintf("THREAD ID:%d (%d:%s)\n", t->id, causeStrIdx, sExceptionNames[causeStrIdx]); + + osSyncPrintf("PC:%08xH SR:%08xH VA:%08xH\n", (u32)ctx->pc, (u32)ctx->sr, (u32)ctx->badvaddr); + osSyncPrintf("AT:%08xH V0:%08xH V1:%08xH\n", (u32)ctx->at, (u32)ctx->v0, (u32)ctx->v1); + osSyncPrintf("A0:%08xH A1:%08xH A2:%08xH\n", (u32)ctx->a0, (u32)ctx->a1, (u32)ctx->a2); + osSyncPrintf("A3:%08xH T0:%08xH T1:%08xH\n", (u32)ctx->a3, (u32)ctx->t0, (u32)ctx->t1); + osSyncPrintf("T2:%08xH T3:%08xH T4:%08xH\n", (u32)ctx->t2, (u32)ctx->t3, (u32)ctx->t4); + osSyncPrintf("T5:%08xH T6:%08xH T7:%08xH\n", (u32)ctx->t5, (u32)ctx->t6, (u32)ctx->t7); + osSyncPrintf("S0:%08xH S1:%08xH S2:%08xH\n", (u32)ctx->s0, (u32)ctx->s1, (u32)ctx->s2); + osSyncPrintf("S3:%08xH S4:%08xH S5:%08xH\n", (u32)ctx->s3, (u32)ctx->s4, (u32)ctx->s5); + osSyncPrintf("S6:%08xH S7:%08xH T8:%08xH\n", (u32)ctx->s6, (u32)ctx->s7, (u32)ctx->t8); + osSyncPrintf("T9:%08xH GP:%08xH SP:%08xH\n", (u32)ctx->t9, (u32)ctx->gp, (u32)ctx->sp); + //osSyncPrintf("S8:%08xH RA:%08xH LO:%08xH\n", (u32)ctx->s8, (u32)ctx->ra, (u32)ctx->lo); + osSyncPrintf("\n"); + Fault_LogFPCR(ctx->fpcsr); + osSyncPrintf("\n"); + Fault_LogFReg(0, &ctx->fp0.f.f_even); + Fault_LogFReg(2, &ctx->fp2.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(4, &ctx->fp4.f.f_even); + Fault_LogFReg(6, &ctx->fp6.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(8, &ctx->fp8.f.f_even); + Fault_LogFReg(0xA, &ctx->fp10.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(0xC, &ctx->fp12.f.f_even); + Fault_LogFReg(0xE, &ctx->fp14.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(0x10, &ctx->fp16.f.f_even); + Fault_LogFReg(0x12, &ctx->fp18.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(0x14, &ctx->fp20.f.f_even); + Fault_LogFReg(0x16, &ctx->fp22.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(0x18, &ctx->fp24.f.f_even); + Fault_LogFReg(0x1A, &ctx->fp26.f.f_even); + osSyncPrintf("\n"); + Fault_LogFReg(0x1C, &ctx->fp28.f.f_even); + Fault_LogFReg(0x1E, &ctx->fp30.f.f_even); + osSyncPrintf("\n"); +} + +OSThread* Fault_FindFaultedThread() { + OSThread* iter = __osGetActiveQueue(); + + while (iter->priority != -1) { + if (iter->priority > 0 && iter->priority < 0x7F && (iter->flags & 3)) { + return iter; + } + iter = iter->tlnext; + } + return NULL; +} + +void Fault_Wait5Seconds(void) +{ +#if 0 + OSTime start[2]; // to make the function allocate 0x28 bytes of stack instead of 0x20 + + start[0] = osGetTime(); + do { + Fault_Sleep(0x10); + } while ((osGetTime() - start[0]) < OS_USEC_TO_CYCLES(5000000) + 1); + + sFaultStructPtr->faultActive = true; +#endif +} + +void Fault_WaitForButtonCombo() +{ +#if 0 + Input* input = &sFaultStructPtr->padInput; + s32 state; + u32 s1; + u32 s2; + u32 kDown; + u32 kCur; + + if (1) {} + if (1) {} + + osSyncPrintf( + VT_FGCOL(WHITE) "KeyWaitB (LRZ " VT_FGCOL(WHITE) "上" VT_FGCOL(YELLOW) "下 " VT_FGCOL(YELLOW) "上" VT_FGCOL(WHITE) "下 " VT_FGCOL(WHITE) "左" VT_FGCOL( + YELLOW) "左 " VT_FGCOL(YELLOW) "右" VT_FGCOL(WHITE) "右 " VT_FGCOL(GREEN) "B" VT_FGCOL(BLUE) "A" VT_FGCOL(RED) "START" VT_FGCOL(WHITE) ")" VT_RST + "\n"); + osSyncPrintf(VT_FGCOL(WHITE) "KeyWaitB'(LR左" VT_FGCOL(YELLOW) "右 +" VT_FGCOL(RED) "START" VT_FGCOL( + WHITE) ")" VT_RST "\n"); + + FaultDrawer_SetForeColor(0xFFFF); + FaultDrawer_SetBackColor(1); + + state = 0; + s1 = 0; + s2 = 1; + + while (state != 11) { + Fault_Sleep(0x10); + Fault_UpdatePadImpl(); + + kDown = input->press.button; + kCur = input->cur.button; + + if ((kCur == 0) && (s1 == s2)) { + s1 = 0; + } else if (kDown != 0) { + if (s1 == s2) { + state = 0; + } + + switch (state) { + case 0: + if (kCur == (BTN_Z | BTN_L | BTN_R) && kDown == BTN_Z) { + state = s2; + s1 = s2; + } + break; + case 1: + if (kDown == BTN_DUP) { + state = 2; + } else { + state = 0; + } + break; + case 2: + if (kDown == BTN_CDOWN) { + state = 3; + s1 = s2; + } else { + state = 0; + } + break; + case 3: + if (kDown == BTN_CUP) { + state = 4; + } else { + state = 0; + } + break; + case 4: + if (kDown == BTN_DDOWN) { + state = 5; + s1 = s2; + } else { + state = 0; + } + break; + case 5: + if (kDown == BTN_DLEFT) { + state = 6; + } else { + state = 0; + } + break; + case 6: + if (kDown == BTN_CLEFT) { + state = 7; + s1 = s2; + } else { + state = 0; + } + break; + case 7: + if (kDown == BTN_CRIGHT) { + state = 8; + } else { + state = 0; + } + break; + case 8: + if (kDown == BTN_DRIGHT) { + state = 9; + s1 = s2; + } else { + state = 0; + } + break; + case 9: + if (kDown == (BTN_A | BTN_B)) { + state = 10; + } else if (kDown == BTN_A) { + state = 0x5B; + } else if (kDown == BTN_B) { + state = 0x5C; + } else { + state = 0; + } + break; + case 0x5B: + if (kDown == BTN_B) { + state = 10; + } else { + state = 0; + } + break; + case 0x5C: + if (kDown == BTN_A) { + state = 10; + } else { + state = 0; + } + break; + case 10: + if (kDown == BTN_START) { + state = 11; + } else { + state = 0; + } + break; + } + } + + osWritebackDCacheAll(); + } +#endif +} + +void Fault_DrawMemDumpPage(const char* title, u32* addr, u32 param_3) { + u32* alignedAddr; + u32* writeAddr; + s32 y; + s32 x; + + alignedAddr = addr; + + if (alignedAddr < (u32*)0x80000000) { + alignedAddr = (u32*)0x80000000; + } + if (alignedAddr > (u32*)0x807FFF00) { + alignedAddr = (u32*)0x807FFF00; + } + + alignedAddr = (u32*)((u32)alignedAddr & ~3); + writeAddr = alignedAddr; + Fault_FillScreenBlack(); + FaultDrawer_SetCharPad(-2, 0); + + FaultDrawer_DrawText(0x24, 0x12, "%s %08x", title != NULL ? title : "PrintDump", alignedAddr); + if (alignedAddr >= (u32*)0x80000000 && alignedAddr < (u32*)0xC0000000) { + for (y = 0x1C; y != 0xE2; y += 9) { + FaultDrawer_DrawText(0x18, y, "%06x", writeAddr); + for (x = 0x52; x != 0x122; x += 0x34) { + FaultDrawer_DrawText(x, y, "%08x", *writeAddr++); + } + } + } + + FaultDrawer_SetCharPad(0, 0); +} + +void Fault_DrawMemDump(u32 pc, u32 sp, u32 unk0, u32 unk1) +{ +#if 0 + Input* input = &sFaultStructPtr->padInput; + u32 addr = pc; + s32 count; + u32 off; + + do { + count = 0; + if (addr < 0x80000000) { + addr = 0x80000000; + } + if (addr > 0x807FFF00) { + addr = 0x807FFF00; + } + + addr &= ~0xF; + Fault_DrawMemDumpPage("Dump", (u32*)addr, 0); + count = 600; + + while (sFaultStructPtr->faultActive) { + if (count == 0) { + return; + } + + count--; + Fault_Sleep(0x10); + Fault_UpdatePadImpl(); + if (CHECK_BTN_ALL(input->press.button, BTN_L)) { + sFaultStructPtr->faultActive = false; + } + } + + do { + Fault_Sleep(0x10); + Fault_UpdatePadImpl(); + } while (input->press.button == 0); + + if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->cur.button, BTN_A)) { + return; + } + + off = 0x10; + if (CHECK_BTN_ALL(input->cur.button, BTN_Z)) { + off = 0x100; + } + + if (CHECK_BTN_ALL(input->cur.button, BTN_B)) { + off <<= 8; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + addr -= off; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + addr += off; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + addr = pc; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + addr = sp; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + addr = unk0; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + addr = unk1; + } + } while (!CHECK_BTN_ALL(input->press.button, BTN_L)); + + sFaultStructPtr->faultActive = true; +#endif +} + +void Fault_WalkStack(u32* spPtr, u32* pcPtr, u32* raPtr) { + u32 sp = *spPtr; + u32 pc = *pcPtr; + u32 ra = *raPtr; + s32 count = 0x10000; + u32 lastOpc; + u32 opc; + u16 opcHi; + s16 opcLo; + u32 imm; + + if (sp & 3 || sp < 0x80000000 || sp >= 0xA0000000 || ra & 3 || ra < 0x80000000 || ra >= 0xA0000000) { + *spPtr = 0; + *pcPtr = 0; + *raPtr = 0; + return; + } + + if (pc & 3 || pc < 0x80000000 || pc >= 0xA0000000) { + *pcPtr = ra; + return; + } + + lastOpc = 0; + while (true) { + opc = HW_REG(pc, u32); + opcHi = opc >> 16; + opcLo = opc & 0xFFFF; + imm = opcLo; + if (opcHi == 0x8FBF) { + ra = HW_REG(sp + imm, u32); + } else if (opcHi == 0x27BD) { + sp += imm; + } else if (opc == 0x42000018) { + sp = 0; + pc = 0; + ra = 0; + goto end; + } + if (lastOpc == 0x3E00008) { + pc = ra; + goto end; + } else if (lastOpc >> 26 == 2) { + pc = pc >> 28 << 28 | lastOpc << 6 >> 4; + goto end; + } + lastOpc = opc; + pc += 4; + if (count == 0) { + break; + } + count--; + } + sp = 0; + pc = 0; + ra = 0; + +end: + *spPtr = sp; + *pcPtr = pc; + *raPtr = ra; +} + +void Fault_DrawStackTrace(OSThread* thread, s32 x, s32 y, s32 height) { + s32 line; + u32 sp = thread->context.sp; + u32 ra = thread->context.ra; + u32 pc = thread->context.pc; + u32 addr; + + FaultDrawer_DrawText(x, y, "SP PC (VPC)"); + for (line = 1; line < height && (ra != 0 || sp != 0) && pc != (u32)__osCleanupThread; line++) { + FaultDrawer_DrawText(x, y + line * 8, "%08x %08x", sp, pc); + addr = Fault_ConvertAddress(pc); + if (addr != 0) { + FaultDrawer_Printf(" -> %08x", addr); + } + Fault_WalkStack(&sp, &pc, &ra); + } +} + +void Fault_LogStackTrace(OSThread* thread, s32 height) { + s32 line; + u32 sp = thread->context.sp; + u32 ra = thread->context.ra; + u32 pc = thread->context.pc; + u32 addr; + s32 pad; + + osSyncPrintf("STACK TRACE\nSP PC (VPC)\n"); + for (line = 1; line < height && (ra != 0 || sp != 0) && pc != (u32)__osCleanupThread; line++) { + osSyncPrintf("%08x %08x", sp, pc); + addr = Fault_ConvertAddress(pc); + if (addr != 0) { + osSyncPrintf(" -> %08x", addr); + } + osSyncPrintf("\n"); + Fault_WalkStack(&sp, &pc, &ra); + } +} + +void Fault_ResumeThread(OSThread* t) { + t->context.cause = 0; + t->context.fpcsr = 0; + t->context.pc += 4; + *(u32*)t->context.pc = 0xD; + osWritebackDCache(t->context.pc, 4); + osInvalICache(t->context.pc, 4); + osStartThread(t); +} + +void Fault_CommitFB() { + u16* fb; + +#if 0 + osViSetYScale(1.0f); + osViSetMode(&osViModeNtscLan1); + osViSetSpecialFeatures(OS_VI_GAMMA_OFF | OS_VI_DITHER_FILTER_ON); + osViBlack(false); + + if (sFaultStructPtr->fb) { + fb = sFaultStructPtr->fb; + } else { + fb = (u16*)osViGetNextFramebuffer(); + if ((u32)fb == 0x80000000) { + fb = (u16*)((osMemSize | 0x80000000) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])); + } + } + + osViSwapBuffer(fb); + FaultDrawer_SetDrawerFB(fb, SCREEN_WIDTH, SCREEN_HEIGHT); +#endif +} + +void Fault_ProcessClients(void) +{ +#if 0 + FaultClient* iter = sFaultStructPtr->clients; + s32 idx = 0; + + while (iter != NULL) { + if (iter->callback != 0) { + Fault_FillScreenBlack(); + FaultDrawer_SetCharPad(-2, 0); + FaultDrawer_Printf("\x1a" + "8CallBack (%d) %08x %08x %08x\n" + "\x1a" + "7", + idx++, iter, iter->param1, iter->param2); + FaultDrawer_SetCharPad(0, 0); + Fault_ProcessClient(iter->callback, iter->param1, iter->param2); + Fault_WaitForInput(); + Fault_CommitFB(); + } + iter = iter->next; + } +#endif +} + +void Fault_UpdatePad() { + Fault_UpdatePadImpl(); +} + +void Fault_ThreadEntry(void* arg) { + OSMesg msg; + OSThread* faultedThread; + s32 pad; + +#if 0 + osSetEventMesg(OS_EVENT_CPU_BREAK, &sFaultStructPtr->queue, 1); + osSetEventMesg(OS_EVENT_FAULT, &sFaultStructPtr->queue, 2); + + while (true) { + do { + osRecvMesg(&sFaultStructPtr->queue, &msg, OS_MESG_BLOCK); + + if (msg == (OSMesg)1) { + sFaultStructPtr->msgId = 1; + osSyncPrintf("フォルトマネージャ:OS_EVENT_CPU_BREAKを受信しました\n"); + } else if (1 && msg == (OSMesg)2) { + sFaultStructPtr->msgId = 2; + osSyncPrintf("フォルトマネージャ:OS_EVENT_FAULTを受信しました\n"); + } else if (msg == (OSMesg)3) { + Fault_UpdatePad(); + faultedThread = NULL; + continue; + } else { + sFaultStructPtr->msgId = 3; + osSyncPrintf("フォルトマネージャ:不明なメッセージを受信しました\n"); + } + + faultedThread = __osGetCurrFaultedThread(); + osSyncPrintf("__osGetCurrFaultedThread()=%08x\n", faultedThread); + + if (faultedThread == NULL) { + faultedThread = Fault_FindFaultedThread(); + osSyncPrintf("FindFaultedThread()=%08x\n", faultedThread); + } + } while (faultedThread == NULL); + + __osSetFpcCsr(__osGetFpcCsr() & -0xF81); + sFaultStructPtr->faultedThread = faultedThread; + + while (!sFaultStructPtr->faultHandlerEnabled) { + Fault_Sleep(1000); + } + + Fault_Sleep(500); + Fault_CommitFB(); + + if (sFaultStructPtr->faultActive) { + Fault_Wait5Seconds(); + } else { + Fault_DrawCornerRec(0xF801); + Fault_WaitForButtonCombo(); + } + + sFaultStructPtr->faultActive = true; + FaultDrawer_SetForeColor(0xFFFF); + FaultDrawer_SetBackColor(0); + + do { + Fault_PrintThreadContext(faultedThread); + Fault_LogThreadContext(faultedThread); + Fault_WaitForInput(); + Fault_FillScreenBlack(); + FaultDrawer_DrawText(0x78, 0x10, "STACK TRACE"); + Fault_DrawStackTrace(faultedThread, 0x24, 0x18, 0x16); + Fault_LogStackTrace(faultedThread, 0x32); + Fault_WaitForInput(); + Fault_ProcessClients(); + Fault_DrawMemDump(faultedThread->context.pc - 0x100, (u32)faultedThread->context.sp, 0, 0); + Fault_FillScreenRed(); + FaultDrawer_DrawText(0x40, 0x50, " CONGRATURATIONS! "); + FaultDrawer_DrawText(0x40, 0x5A, "All Pages are displayed."); + FaultDrawer_DrawText(0x40, 0x64, " THANK YOU! "); + FaultDrawer_DrawText(0x40, 0x6E, " You are great debugger!"); + Fault_WaitForInput(); + } while (!sFaultStructPtr->exitDebugger); + + while (!sFaultStructPtr->exitDebugger) {} + + Fault_ResumeThread(faultedThread); + } +#endif +} + +void Fault_SetFB(void* fb, u16 w, u16 h) +{ +#if 0 + sFaultStructPtr->fb = fb; + FaultDrawer_SetDrawerFB(fb, w, h); +#endif +} + +void Fault_Init(void) +{ +#if 0 + sFaultStructPtr = &gFaultStruct; + bzero(sFaultStructPtr, sizeof(FaultThreadStruct)); + FaultDrawer_SetDefault(); + FaultDrawer_SetInputCallback(Fault_WaitForInput); + sFaultStructPtr->exitDebugger = false; + sFaultStructPtr->msgId = 0; + sFaultStructPtr->faultHandlerEnabled = false; + sFaultStructPtr->faultedThread = NULL; + sFaultStructPtr->padCallback = Fault_PadCallback; + sFaultStructPtr->clients = NULL; + sFaultStructPtr->faultActive = false; + gFaultStruct.faultHandlerEnabled = true; + osCreateMesgQueue(&sFaultStructPtr->queue, &sFaultStructPtr->msg, 1); + StackCheck_Init(&sFaultThreadInfo, &sFaultStack, sFaultStack + sizeof(sFaultStack), 0, 0x100, "fault"); + osCreateThread(&sFaultStructPtr->thread, 2, Fault_ThreadEntry, 0, sFaultStack + sizeof(sFaultStack), + OS_PRIORITY_APPMAX); + osStartThread(&sFaultStructPtr->thread); +#endif +} + +void Fault_HangupFaultClient(const char* arg0, const char* arg1) +{ +#if 0 + osSyncPrintf("HungUp on Thread %d\n", osGetThreadId(0)); + osSyncPrintf("%s\n", arg0 != NULL ? arg0 : "(NULL)"); + osSyncPrintf("%s\n", arg1 != NULL ? arg1 : "(NULL)"); + FaultDrawer_Printf("HungUp on Thread %d\n", osGetThreadId(0)); + FaultDrawer_Printf("%s\n", arg0 != NULL ? arg0 : "(NULL)"); + FaultDrawer_Printf("%s\n", arg1 != NULL ? arg1 : "(NULL)"); +#endif +} + +void Fault_AddHungupAndCrashImpl(const char* arg0, const char* arg1) { + FaultClient client; + s32 pad; + + // TODO: DO A PROPER EXCEPTION HANDLER HERE + + Fault_AddClient(&client, Fault_HangupFaultClient, (void*)arg0, (void*)arg1); + *(u32*)0x11111111 = 0; // trigger an exception +} + +void Fault_AddHungupAndCrash(const char* filename, u32 line) { + char msg[256]; + + sprintf(msg, "HungUp %s:%d", filename, line); + Fault_AddHungupAndCrashImpl(msg, NULL); +} diff --git a/soh/src/code/fault_drawer.c b/soh/src/code/fault_drawer.c new file mode 100644 index 000000000..9759bfe72 --- /dev/null +++ b/soh/src/code/fault_drawer.c @@ -0,0 +1,308 @@ +#include "global.h" +#include "vt.h" +#include +// rodata +const u32 sFaultDrawerFont[] = { + 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, + 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x0EFFFFC0, + 0x0BF00FB0, 0x0FF00330, 0x0FF00FF0, 0x0FF00220, 0x0CFBBF60, 0x0FFCCE20, 0x0DD44FF0, 0x0FF00220, 0x0FF00FF0, + 0x0FF00330, 0x0CFBBF40, 0x0EF77740, 0x00000000, 0x00000000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, 0x06611DC0, + 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, 0x04DFFD40, + 0x026EF700, 0x00000000, 0x00000000, 0x08BFFB00, 0x000DE000, 0x0BF00FB0, 0x005DE600, 0x0FF00FF0, 0x055CC660, + 0x0CFBBF60, 0x773FF377, 0x0DD44FF0, 0xBB3FF3BB, 0x0FF00FF0, 0x099CCAA0, 0x0CFBBF40, 0x009DEA00, 0x00000000, + 0x000DE000, 0x04C22C40, 0x028D5020, 0x0CCAACC0, 0x21F91710, 0x04C22C40, 0x12493400, 0x00820800, 0x01975110, + 0x088A8880, 0x04615241, 0x00800800, 0x43117530, 0x00A20800, 0x60055600, 0x00000000, 0x04400040, 0x00221100, + 0x00000080, 0x000FB000, 0x00000880, 0x040DA400, 0x00008800, 0x08CDE880, 0x022AA220, 0x08CDE880, 0x02AA2220, + 0x040DA400, 0x0CD10000, 0x000FB000, 0x8C510000, 0x00221100, 0x81100000, 0x00DFFD00, 0x0AEEFFA0, 0x0DF22DD0, + 0x06611DC0, 0x01122DD0, 0x06719900, 0x011EED10, 0x077EF700, 0x01562990, 0x05589760, 0x0DD22990, 0x05599770, + 0x04DFFD40, 0x026EF700, 0x00000000, 0x00000000, 0x00333300, 0x04489980, 0x033CC330, 0x00CD1088, 0x033CC330, + 0x02BF62A8, 0x00333320, 0x01104C80, 0x01100330, 0x0015C800, 0x033CC330, 0x02673220, 0x003FF300, 0x04409900, + 0x00880000, 0x00000000, 0x05DFFD10, 0x07FFFF60, 0x1CE00EC1, 0x0FF00990, 0x1EE11661, 0x0FF00110, 0x1EF45621, + 0x0FF66710, 0x1EF23661, 0x0FF08990, 0x1EF10FE1, 0x0FF00990, 0x16ECCE21, 0x07FBBB20, 0x01111110, 0x00000000, + 0x09B66FD0, 0x27D88E60, 0x0992ED10, 0x2FF02EE0, 0x099AE510, 0x2FF62EE0, 0x099B7510, 0x2FD64EE0, 0x0DDAE510, + 0x2FD04EE0, 0x0DD2ED10, 0x2FD00EE0, 0x09F66F90, 0x27D99F70, 0x00000000, 0x00000000, 0x07FFFF00, 0x8F711FF0, + 0x2FD00FF0, 0x8F711FF0, 0x2FD00770, 0x8E611EE0, 0x27DDDF60, 0x8E691EE0, 0x27764AA0, 0x8EE99EE0, 0x2FD06E80, + 0x8AE7FEA0, 0x07FA8E60, 0x88277A80, 0x00000000, 0x00000000, 0x077CCFF0, 0x13266011, 0x077CCFF0, 0x03766510, + 0x0239D720, 0x04533540, 0x002FF200, 0x01133110, 0x005FB100, 0x00033000, 0x055EE550, 0x01133110, 0x055EEDD0, + 0x02233000, 0x00088880, 0x8AABB888, 0x00001100, 0x00044510, 0x04623320, 0x00440110, 0x04C89AA0, 0x00EEAB10, + 0x0CE66720, 0x0EF55FB0, 0x0EE00660, 0x0BF62B90, 0x0EE00660, 0x03FC8990, 0x04EEEEA0, 0x00773BB0, 0x00000000, + 0x08888800, 0x09900000, 0x00111000, 0x09922440, 0x00011000, 0x09908800, 0x26EFDE20, 0x099BB540, 0x2EC33CE2, + 0x0D9A2550, 0x2EC33CE2, 0x0DDAA550, 0x2EC33CE2, 0x09D6ED10, 0x26CBBC62, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00011000, 0x05FBFFE0, 0x8E6116E8, 0x0FF40330, 0x8F7117F8, 0x07FC8B30, 0x8E6996E8, + 0x05733BA0, 0x8A6DD6A8, 0x0DD88A20, 0x08A779B2, 0x01100220, 0x00000000, 0x00000080, 0x8A011000, 0x00000800, + 0x80A11000, 0x07744F70, 0x80A99000, 0x0231DF20, 0x84E60004, 0x0027DA20, 0xC8AA4C40, 0x00573B20, 0x00A11800, + 0x05546F50, 0x00A99800, 0x02222080, 0x02001888, +}; + +FaultDrawer sFaultDrawerDefault = { + (u16*)(0x80400000 - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])), // fb + SCREEN_WIDTH, // w + SCREEN_HEIGHT, // h + 16, // yStart + 223, // yEnd + 22, // xStart + 297, // xEnd + GPACK_RGBA5551(255, 255, 255, 255), // foreColor + GPACK_RGBA5551(0, 0, 0, 0), // backColor + 22, // cursorX + 16, // cursorY + sFaultDrawerFont, // font + 8, + 8, + 0, + 0, + { + // printColors + GPACK_RGBA5551(0, 0, 0, 1), + GPACK_RGBA5551(255, 0, 0, 1), + GPACK_RGBA5551(0, 255, 0, 1), + GPACK_RGBA5551(255, 255, 0, 1), + GPACK_RGBA5551(0, 0, 255, 1), + GPACK_RGBA5551(255, 0, 255, 1), + GPACK_RGBA5551(0, 255, 255, 1), + GPACK_RGBA5551(255, 255, 255, 1), + GPACK_RGBA5551(120, 120, 120, 1), + GPACK_RGBA5551(176, 176, 176, 1), + }, + 0, // escCode + 0, // osSyncPrintfEnabled + NULL, // inputCallback +}; + +FaultDrawer sFaultDrawerStruct; +char D_8016B6C0[0x20]; + +void FaultDrawer_SetOsSyncPrintfEnabled(u32 enabled) { + sFaultDrawerStruct.osSyncPrintfEnabled = enabled; +} + +void FaultDrawer_DrawRecImpl(s32 xStart, s32 yStart, s32 xEnd, s32 yEnd, u16 color) { + u16* fb; + s32 x, y; + s32 xDiff = sFaultDrawerStruct.w - xStart; + s32 yDiff = sFaultDrawerStruct.h - yStart; + s32 xSize = xEnd - xStart + 1; + s32 ySize = yEnd - yStart + 1; + + if (xDiff > 0 && yDiff > 0) { + if (xDiff < xSize) { + xSize = xDiff; + } + + if (yDiff < ySize) { + ySize = yDiff; + } + + fb = sFaultDrawerStruct.fb + sFaultDrawerStruct.w * yStart + xStart; + for (y = 0; y < ySize; y++) { + for (x = 0; x < xSize; x++) { + *fb++ = color; + } + fb += sFaultDrawerStruct.w - xSize; + } + + osWritebackDCacheAll(); + } +} + +void FaultDrawer_DrawChar(char c) { + u16* fb; + s32 x, y; + const u32* dataPtr; + u32 data; + s32 cursorX = sFaultDrawerStruct.cursorX; + s32 cursorY = sFaultDrawerStruct.cursorY; + const u32** fontData = &sFaultDrawerStruct.fontData; + s32 shift = c % 4; + + dataPtr = &fontData[0][(((c / 8) * 16) + ((c & 4) >> 2))]; + fb = sFaultDrawerStruct.fb + (sFaultDrawerStruct.w * cursorY) + cursorX; + + if ((sFaultDrawerStruct.xStart <= cursorX) && + ((sFaultDrawerStruct.charW + cursorX - 1) <= sFaultDrawerStruct.xEnd) && + (sFaultDrawerStruct.yStart <= cursorY) && + ((sFaultDrawerStruct.charH + cursorY - 1) <= sFaultDrawerStruct.yEnd)) { + for (y = 0; y < sFaultDrawerStruct.charH; y++) { + u32 mask = 0x10000000 << shift; + + data = *dataPtr; + for (x = 0; x < sFaultDrawerStruct.charW; x++) { + if (mask & data) { + fb[x] = sFaultDrawerStruct.foreColor; + } else if (sFaultDrawerStruct.backColor & 1) { + fb[x] = sFaultDrawerStruct.backColor; + } + mask >>= 4; + } + fb += sFaultDrawerStruct.w; + dataPtr += 2; + } + } +} + +s32 FaultDrawer_ColorToPrintColor(u16 color) { + s32 i; + for (i = 0; i < 10; i++) { + if (color == sFaultDrawerStruct.printColors[i]) { + return i; + } + } + return -1; +} + +void FaultDrawer_UpdatePrintColor() { + s32 idx; + + if (sFaultDrawerStruct.osSyncPrintfEnabled) { + osSyncPrintf(VT_RST); + idx = FaultDrawer_ColorToPrintColor(sFaultDrawerStruct.foreColor); + if (idx >= 0 && idx < 8) { + osSyncPrintf(VT_SGR("3%d"), idx); + } + idx = FaultDrawer_ColorToPrintColor(sFaultDrawerStruct.backColor); + if (idx >= 0 && idx < 8) { + osSyncPrintf(VT_SGR("4%d"), idx); + } + } +} + +void FaultDrawer_SetForeColor(u16 color) { + sFaultDrawerStruct.foreColor = color; + FaultDrawer_UpdatePrintColor(); +} + +void FaultDrawer_SetBackColor(u16 color) { + sFaultDrawerStruct.backColor = color; + FaultDrawer_UpdatePrintColor(); +} + +void FaultDrawer_SetFontColor(u16 color) { + FaultDrawer_SetForeColor(color | 1); // force alpha to be set +} + +void FaultDrawer_SetCharPad(s8 padW, s8 padH) { + sFaultDrawerStruct.charWPad = padW; + sFaultDrawerStruct.charHPad = padH; +} + +void FaultDrawer_SetCursor(s32 x, s32 y) { + if (sFaultDrawerStruct.osSyncPrintfEnabled) { + osSyncPrintf(VT_CUP("%d", "%d"), + (y - sFaultDrawerStruct.yStart) / (sFaultDrawerStruct.charH + sFaultDrawerStruct.charHPad), + (x - sFaultDrawerStruct.xStart) / (sFaultDrawerStruct.charW + sFaultDrawerStruct.charWPad)); + } + sFaultDrawerStruct.cursorX = x; + sFaultDrawerStruct.cursorY = y; +} + +void FaultDrawer_FillScreen() { + if (sFaultDrawerStruct.osSyncPrintfEnabled) { + osSyncPrintf(VT_CLS); + } + + FaultDrawer_DrawRecImpl(sFaultDrawerStruct.xStart, sFaultDrawerStruct.yStart, sFaultDrawerStruct.xEnd, + sFaultDrawerStruct.yEnd, sFaultDrawerStruct.backColor | 1); + FaultDrawer_SetCursor(sFaultDrawerStruct.xStart, sFaultDrawerStruct.yStart); +} + +void* FaultDrawer_FormatStringFunc(void* arg, const char* str, u32 count) { + for (; count != 0; count--, str++) { + s32 curXStart; + s32 curXEnd; + + if (sFaultDrawerStruct.escCode) { + sFaultDrawerStruct.escCode = false; + if (*str > 0x30 && *str < 0x3A) { + FaultDrawer_SetForeColor(sFaultDrawerStruct.printColors[*str - 0x30]); + } + + curXStart = sFaultDrawerStruct.cursorX; + curXEnd = sFaultDrawerStruct.xEnd - sFaultDrawerStruct.charW; + } else { + switch (*str) { + case '\n': + if (sFaultDrawerStruct.osSyncPrintfEnabled) { + osSyncPrintf("\n"); + } + + sFaultDrawerStruct.cursorX = sFaultDrawerStruct.w; + curXStart = sFaultDrawerStruct.cursorX; + curXEnd = sFaultDrawerStruct.xEnd - sFaultDrawerStruct.charW; + break; + case '\x1A': + sFaultDrawerStruct.escCode = true; + curXStart = sFaultDrawerStruct.cursorX; + curXEnd = sFaultDrawerStruct.xEnd - sFaultDrawerStruct.charW; + break; + default: + if (sFaultDrawerStruct.osSyncPrintfEnabled) { + osSyncPrintf("%c", *str); + } + + FaultDrawer_DrawChar(*str); + sFaultDrawerStruct.cursorX += sFaultDrawerStruct.charW + sFaultDrawerStruct.charWPad; + + curXStart = sFaultDrawerStruct.cursorX; + curXEnd = sFaultDrawerStruct.xEnd - sFaultDrawerStruct.charW; + } + } + + if (curXEnd <= curXStart) { + sFaultDrawerStruct.cursorX = sFaultDrawerStruct.xStart; + sFaultDrawerStruct.cursorY += sFaultDrawerStruct.charH + sFaultDrawerStruct.charHPad; + if (sFaultDrawerStruct.yEnd - sFaultDrawerStruct.charH <= sFaultDrawerStruct.cursorY) { + if (sFaultDrawerStruct.inputCallback) { + sFaultDrawerStruct.inputCallback(); + FaultDrawer_FillScreen(); + } + sFaultDrawerStruct.cursorY = sFaultDrawerStruct.yStart; + } + } + } + + osWritebackDCacheAll(); + + return arg; +} + +void FaultDrawer_VPrintf(const char* str, char* args) { // va_list + _Printf(FaultDrawer_FormatStringFunc, (char*)&sFaultDrawerStruct, str, args); +} + +void FaultDrawer_Printf(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + FaultDrawer_VPrintf(fmt, args); + + va_end(args); +} + +void FaultDrawer_DrawText(s32 x, s32 y, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + FaultDrawer_SetCursor(x, y); + FaultDrawer_VPrintf(fmt, args); + + va_end(args); +} + +void FaultDrawer_SetDrawerFB(void* fb, u16 w, u16 h) { + sFaultDrawerStruct.fb = fb; + sFaultDrawerStruct.w = w; + sFaultDrawerStruct.h = h; +} + +void FaultDrawer_SetInputCallback(void (*callback)()) { + sFaultDrawerStruct.inputCallback = callback; +} + +void FaultDrawer_WritebackFBDCache() { + osWritebackDCache(sFaultDrawerStruct.fb, sFaultDrawerStruct.w * sFaultDrawerStruct.h * 2); +} + +void FaultDrawer_SetDefault() { + memcpy(&sFaultDrawerStruct, &sFaultDrawerDefault, sizeof(FaultDrawer)); + sFaultDrawerStruct.fb = (u16*)((osMemSize | 0x80000000) - sizeof(u16[SCREEN_HEIGHT][SCREEN_WIDTH])); +} diff --git a/soh/src/code/flg_set.c b/soh/src/code/flg_set.c new file mode 100644 index 000000000..55850d5b0 --- /dev/null +++ b/soh/src/code/flg_set.c @@ -0,0 +1,152 @@ +#include "global.h" + +void FlagSet_Update(GlobalContext* globalCtx) { + static s32 entryIdx = 0; + static u32 curBit = 0; + static s32 timer = 0; + static s32 bitIdx; // ? this doesn't need to be static + + FlagSetEntry entries[53] = { + { &gSaveContext.eventChkInf[0], "event_chk_inf[0]" }, { &gSaveContext.eventChkInf[1], "event_chk_inf[1]" }, + { &gSaveContext.eventChkInf[2], "event_chk_inf[2]" }, { &gSaveContext.eventChkInf[3], "event_chk_inf[3]" }, + { &gSaveContext.eventChkInf[4], "event_chk_inf[4]" }, { &gSaveContext.eventChkInf[5], "event_chk_inf[5]" }, + { &gSaveContext.eventChkInf[6], "event_chk_inf[6]" }, { &gSaveContext.eventChkInf[7], "event_chk_inf[7]" }, + { &gSaveContext.eventChkInf[8], "event_chk_inf[8]" }, { &gSaveContext.eventChkInf[9], "event_chk_inf[9]" }, + { &gSaveContext.eventChkInf[10], "event_chk_inf[10]" }, { &gSaveContext.eventChkInf[11], "event_chk_inf[11]" }, + { &gSaveContext.eventChkInf[12], "event_chk_inf[12]" }, { &gSaveContext.eventChkInf[13], "event_chk_inf[13]" }, + { &gSaveContext.itemGetInf[0], "item_get_inf[0]" }, { &gSaveContext.itemGetInf[1], "item_get_inf[1]" }, + { &gSaveContext.itemGetInf[2], "item_get_inf[2]" }, { &gSaveContext.itemGetInf[3], "item_get_inf[3]" }, + { &gSaveContext.infTable[0], "inf_table[0]" }, { &gSaveContext.infTable[1], "inf_table[1]" }, + { &gSaveContext.infTable[2], "inf_table[2]" }, { &gSaveContext.infTable[3], "inf_table[3]" }, + { &gSaveContext.infTable[4], "inf_table[4]" }, { &gSaveContext.infTable[5], "inf_table[5]" }, + { &gSaveContext.infTable[6], "inf_table[6]" }, { &gSaveContext.infTable[7], "inf_table[7]" }, + { &gSaveContext.infTable[8], "inf_table[8]" }, { &gSaveContext.infTable[9], "inf_table[9]" }, + { &gSaveContext.infTable[10], "inf_table[10]" }, { &gSaveContext.infTable[11], "inf_table[11]" }, + { &gSaveContext.infTable[12], "inf_table[12]" }, { &gSaveContext.infTable[13], "inf_table[13]" }, + { &gSaveContext.infTable[14], "inf_table[14]" }, { &gSaveContext.infTable[15], "inf_table[15]" }, + { &gSaveContext.infTable[16], "inf_table[16]" }, { &gSaveContext.infTable[17], "inf_table[17]" }, + { &gSaveContext.infTable[18], "inf_table[18]" }, { &gSaveContext.infTable[19], "inf_table[19]" }, + { &gSaveContext.infTable[20], "inf_table[20]" }, { &gSaveContext.infTable[21], "inf_table[21]" }, + { &gSaveContext.infTable[22], "inf_table[22]" }, { &gSaveContext.infTable[23], "inf_table[23]" }, + { &gSaveContext.infTable[24], "inf_table[24]" }, { &gSaveContext.infTable[25], "inf_table[25]" }, + { &gSaveContext.infTable[26], "inf_table[26]" }, { &gSaveContext.infTable[27], "inf_table[27]" }, + { &gSaveContext.infTable[28], "inf_table[28]" }, { &gSaveContext.infTable[29], "inf_table[29]" }, + { &gSaveContext.eventInf[0], "event_inf[0]" }, { &gSaveContext.eventInf[1], "event_inf[1]" }, + { &gSaveContext.eventInf[2], "event_inf[2]" }, { &gSaveContext.eventInf[3], "event_inf[3]" }, + }; + + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + Input* input = &globalCtx->state.input[0]; + Gfx* gfx; + Gfx* polyOpa; + + OPEN_DISPS(gfxCtx, "../flg_set.c", 131); + + { + GfxPrint printer; + s32 pad; + + polyOpa = POLY_OPA_DISP; + gfx = Graph_GfxPlusOne(polyOpa); + gSPDisplayList(OVERLAY_DISP++, gfx); + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, gfx); + GfxPrint_SetColor(&printer, 250, 50, 50, 255); + GfxPrint_SetPos(&printer, 4, 13); + GfxPrint_Printf(&printer, entries[entryIdx].name); + GfxPrint_SetPos(&printer, 4, 15); + + for (bitIdx = 15; bitIdx >= 0; bitIdx--) { + if ((u32)bitIdx == curBit) { + GfxPrint_SetColor(&printer, 200, 200, 200, 255); + } else { + GfxPrint_SetColor(&printer, 100, 100, 100, 255); + } + + if (*entries[entryIdx].value & (1 << bitIdx)) { + GfxPrint_Printf(&printer, "1"); + } else { + GfxPrint_Printf(&printer, "0"); + } + + if ((bitIdx % 4) == 0) { + GfxPrint_Printf(&printer, " "); + } + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + timer = 10; + curBit++; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + curBit--; + timer = 10; + } + + if (timer == 0) { + if (CHECK_BTN_ALL(input->cur.button, BTN_DLEFT)) { + curBit++; + timer = 2; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT)) { + curBit--; + timer = 2; + } + } + + curBit %= 16; + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + entryIdx--; + if (entryIdx < 0) { + entryIdx = 0; + } + timer = 10; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + timer = 10; + entryIdx++; + if (!entries[entryIdx].value) { + entryIdx--; + } + } + + if (timer == 0) { + if (CHECK_BTN_ALL(input->cur.button, BTN_DUP)) { + entryIdx--; + timer = 2; + if (entryIdx < 0) { + entryIdx = 0; + } + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DDOWN)) { + timer = 2; + entryIdx++; + if (!entries[entryIdx].value) { + entryIdx--; + } + } + } + + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + *entries[entryIdx].value ^= (1 << curBit); + } + + if (timer != 0) { + timer--; + } + + gfx = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); + + gSPEndDisplayList(gfx++); + Graph_BranchDlist(polyOpa, gfx); + POLY_OPA_DISP = gfx; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_L)) { + globalCtx->pauseCtx.debugState = 0; + } + + CLOSE_DISPS(gfxCtx, "../flg_set.c", 241); +} diff --git a/soh/src/code/game.c b/soh/src/code/game.c new file mode 100644 index 000000000..ec8a0e9fa --- /dev/null +++ b/soh/src/code/game.c @@ -0,0 +1,505 @@ +#include "global.h" +#include "vt.h" + +SpeedMeter D_801664D0; +struct_801664F0 D_801664F0; +struct_80166500 D_80166500; +VisMono sMonoColors; +ViMode sViMode; +FaultClient sGameFaultClient; +u16 sLastButtonPressed; + +void GameState_FaultPrint(void) { + static char sBtnChars[] = "ABZSuldr*+LRudlr"; + s32 i; + + osSyncPrintf("last_button=%04x\n", sLastButtonPressed); + FaultDrawer_DrawText(120, 180, "%08x", sLastButtonPressed); + for (i = 0; i < ARRAY_COUNT(sBtnChars); i++) { + if (sLastButtonPressed & (1 << i)) { + FaultDrawer_DrawText((i * 8) + 0x78, 0xBE, "%c", sBtnChars[i]); + } + } +} + +void GameState_SetFBFilter(Gfx** gfx) { + Gfx* gfxP; + gfxP = *gfx; + + if ((R_FB_FILTER_TYPE > 0) && (R_FB_FILTER_TYPE < 5)) { + D_801664F0.type = R_FB_FILTER_TYPE; + D_801664F0.color.r = R_FB_FILTER_PRIM_COLOR(0); + D_801664F0.color.g = R_FB_FILTER_PRIM_COLOR(1); + D_801664F0.color.b = R_FB_FILTER_PRIM_COLOR(2); + D_801664F0.color.a = R_FB_FILTER_A; + func_800ACE98(&D_801664F0, &gfxP); + } else if ((R_FB_FILTER_TYPE == 5) || (R_FB_FILTER_TYPE == 6)) { + D_80166500.useRgba = (R_FB_FILTER_TYPE == 6); + D_80166500.primColor.r = R_FB_FILTER_PRIM_COLOR(0); + D_80166500.primColor.g = R_FB_FILTER_PRIM_COLOR(1); + D_80166500.primColor.b = R_FB_FILTER_PRIM_COLOR(2); + D_80166500.primColor.a = R_FB_FILTER_A; + D_80166500.envColor.r = R_FB_FILTER_ENV_COLOR(0); + D_80166500.envColor.g = R_FB_FILTER_ENV_COLOR(1); + D_80166500.envColor.b = R_FB_FILTER_ENV_COLOR(2); + D_80166500.envColor.a = R_FB_FILTER_A; + func_800AD958(&D_80166500, &gfxP); + } else if (R_FB_FILTER_TYPE == 7) { + sMonoColors.unk_00 = 0; + sMonoColors.primColor.r = R_FB_FILTER_PRIM_COLOR(0); + sMonoColors.primColor.g = R_FB_FILTER_PRIM_COLOR(1); + sMonoColors.primColor.b = R_FB_FILTER_PRIM_COLOR(2); + sMonoColors.primColor.a = R_FB_FILTER_A; + sMonoColors.envColor.r = R_FB_FILTER_ENV_COLOR(0); + sMonoColors.envColor.g = R_FB_FILTER_ENV_COLOR(1); + sMonoColors.envColor.b = R_FB_FILTER_ENV_COLOR(2); + sMonoColors.envColor.a = R_FB_FILTER_A; + VisMono_Draw(&sMonoColors, &gfxP); + } + *gfx = gfxP; +} + +void func_800C4344(GameState* gameState) { + Input* selectedInput; + s32 hexDumpSize; + u16 hReg82; + + if (HREG(80) == 0x14) { + __osMalloc_FreeBlockTest_Enable = HREG(82); + } + + if (HREG(80) == 0xC) { + selectedInput = &gameState->input[(u32)HREG(81) < 4U ? HREG(81) : 0]; + hReg82 = HREG(82); + HREG(83) = selectedInput->cur.button; + HREG(84) = selectedInput->press.button; + HREG(85) = selectedInput->rel.stick_x; + HREG(86) = selectedInput->rel.stick_y; + HREG(87) = selectedInput->rel.stick_x; + HREG(88) = selectedInput->rel.stick_y; + HREG(89) = selectedInput->cur.stick_x; + HREG(90) = selectedInput->cur.stick_y; + HREG(93) = (selectedInput->cur.button == hReg82); + HREG(94) = CHECK_BTN_ALL(selectedInput->cur.button, hReg82); + HREG(95) = CHECK_BTN_ALL(selectedInput->press.button, hReg82); + } + + if (gIsCtrlr2Valid) { + func_8006390C(&gameState->input[1]); + } + + D_80009460 = HREG(60); + gDmaMgrDmaBuffSize = SREG(21) != 0 ? ALIGN16(SREG(21)) : 0x2000; + gSystemArenaLogSeverity = HREG(61); + gZeldaArenaLogSeverity = HREG(62); + if (HREG(80) == 8) { + if (HREG(94) != 8) { + HREG(94) = 8; + HREG(81) = 0; + HREG(82) = 0; + HREG(83) = 0; + } + if (HREG(81) < 0) { + HREG(81) = 0; + // & 0xFFFFFFFF necessary for matching. + hexDumpSize = (HREG(83) == 0 ? 0x100 : HREG(83) * 0x10) & 0xFFFFFFFF; + LogUtils_LogHexDump(PHYSICAL_TO_VIRTUAL(HREG(82) << 8), hexDumpSize); + } + } +} + +void GameState_DrawInputDisplay(u16 input, Gfx** gfx) { + static const u16 sInpDispBtnColors[] = { + GPACK_RGBA5551(255, 255, 0, 1), GPACK_RGBA5551(255, 255, 0, 1), GPACK_RGBA5551(255, 255, 0, 1), + GPACK_RGBA5551(255, 255, 0, 1), GPACK_RGBA5551(120, 120, 120, 1), GPACK_RGBA5551(120, 120, 120, 1), + GPACK_RGBA5551(0, 255, 255, 1), GPACK_RGBA5551(255, 0, 255, 1), GPACK_RGBA5551(120, 120, 120, 1), + GPACK_RGBA5551(120, 120, 120, 1), GPACK_RGBA5551(120, 120, 120, 1), GPACK_RGBA5551(120, 120, 120, 1), + GPACK_RGBA5551(255, 0, 0, 1), GPACK_RGBA5551(120, 120, 120, 1), GPACK_RGBA5551(0, 255, 0, 1), + GPACK_RGBA5551(0, 0, 255, 1), + }; + s32 i, j, k; + Gfx* gfxP = *gfx; + + gDPPipeSync(gfxP++); + gDPSetOtherMode(gfxP++, + G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + + for (i = 0; i < 16; i++) { + j = i; + if (input & (1 << i)) { + gDPSetFillColor(gfxP++, (sInpDispBtnColors[i] << 0x10) | sInpDispBtnColors[i]); + k = i + 1; + gDPFillRectangle(gfxP++, (j * 4) + 226, 220, (k * 4) + 225, 223); + gDPPipeSync(gfxP++); + } + } + + *gfx = gfxP; +} + +void GameState_Draw(GameState* gameState, GraphicsContext* gfxCtx) { + Gfx* newDList; + Gfx* polyOpaP; + + OPEN_DISPS(gfxCtx, "../game.c", 746); + + newDList = Graph_GfxPlusOne(polyOpaP = POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, newDList); + + if (R_ENABLE_FB_FILTER == 1) { + GameState_SetFBFilter(&newDList); + } + + sLastButtonPressed = gameState->input[0].press.button | gameState->input[0].cur.button; + if (R_DISABLE_INPUT_DISPLAY == 0 && CVar_GetS32("gDebugEnabled", 0)) { + GameState_DrawInputDisplay(sLastButtonPressed, &newDList); + } + + if (R_ENABLE_AUDIO_DBG & 1) { + s32 pad; + GfxPrint printer; + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, newDList); + AudioDebug_Draw(&printer); + newDList = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); + } + + if (R_ENABLE_ARENA_DBG < 0) { + s32 pad; + + DebugArena_Display(); + SystemArena_Display(); + // "%08x bytes left until the death of Hyrule (game_alloc)" + osSyncPrintf("ハイラル滅亡まであと %08x バイト(game_alloc)\n", THA_GetSize(&gameState->tha)); + R_ENABLE_ARENA_DBG = 0; + } + + gSPEndDisplayList(newDList++); + Graph_BranchDlist(polyOpaP, newDList); + POLY_OPA_DISP = newDList; + + if (1) {} + + CLOSE_DISPS(gfxCtx, "../game.c", 800); + + func_80063D7C(gfxCtx); + + if (R_ENABLE_ARENA_DBG != 0) { + SpeedMeter_DrawTimeEntries(&D_801664D0, gfxCtx); + SpeedMeter_DrawAllocEntries(&D_801664D0, gfxCtx, gameState); + } +} + +void GameState_SetFrameBuffer(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../game.c", 814); + + gSPSegment(POLY_OPA_DISP++, 0, 0); + gSPSegment(POLY_OPA_DISP++, 0xF, gfxCtx->curFrameBuffer); + gSPSegment(POLY_OPA_DISP++, 0xE, gZBuffer); + gSPSegment(POLY_XLU_DISP++, 0, 0); + gSPSegment(POLY_XLU_DISP++, 0xF, gfxCtx->curFrameBuffer); + gSPSegment(POLY_XLU_DISP++, 0xE, gZBuffer); + gSPSegment(OVERLAY_DISP++, 0, 0); + gSPSegment(OVERLAY_DISP++, 0xF, gfxCtx->curFrameBuffer); + gSPSegment(OVERLAY_DISP++, 0xE, gZBuffer); + + CLOSE_DISPS(gfxCtx, "../game.c", 838); +} + +void func_800C49F4(GraphicsContext* gfxCtx) { + Gfx* newDlist; + Gfx* polyOpaP; + + OPEN_DISPS(gfxCtx, "../game.c", 846); + + newDlist = Graph_GfxPlusOne(polyOpaP = POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, newDlist); + + gSPEndDisplayList(newDlist++); + Graph_BranchDlist(polyOpaP, newDlist); + POLY_OPA_DISP = newDlist; + + if (1) {} + + CLOSE_DISPS(gfxCtx, "../game.c", 865); +} + +void PadMgr_RequestPadData(PadMgr*, Input*, s32); + +void GameState_ReqPadData(GameState* gameState) { + PadMgr_RequestPadData(&gPadMgr, &gameState->input[0], 1); +} + +// OTRTODO +int fbTest = -1; + +void GameState_Update(GameState* gameState) { + GraphicsContext* gfxCtx = gameState->gfxCtx; + + if (fbTest == -1) + { + fbTest = gfx_create_framebuffer(64, 112); + //fbTest = gfx_create_framebuffer(256, 512); + } + + GameState_SetFrameBuffer(gfxCtx); + + gameState->main(gameState); + + func_800C4344(gameState); + + if (SREG(63) == 1u) { + if (SREG(48) < 0) { + SREG(48) = 0; + gfxCtx->viMode = &gViConfigMode; + gfxCtx->viFeatures = gViConfigFeatures; + gfxCtx->xScale = gViConfigXScale; + gfxCtx->yScale = gViConfigYScale; + } else if (SREG(48) > 0) { + ViMode_Update(&sViMode, gameState->input); + gfxCtx->viMode = &sViMode.customViMode; + gfxCtx->viFeatures = sViMode.viFeatures; + gfxCtx->xScale = 1.0f; + gfxCtx->yScale = 1.0f; + } + } else if (SREG(63) >= 2) { + gfxCtx->viMode = &gViConfigMode; + gfxCtx->viFeatures = gViConfigFeatures; + gfxCtx->xScale = gViConfigXScale; + gfxCtx->yScale = gViConfigYScale; + if (SREG(63) == 6 || (SREG(63) == 2u && osTvType == OS_TV_NTSC)) { + gfxCtx->viMode = &osViModeNtscLan1; + gfxCtx->yScale = 1.0f; + } + + if (SREG(63) == 5 || (SREG(63) == 2u && osTvType == OS_TV_MPAL)) { + gfxCtx->viMode = &osViModeMpalLan1; + gfxCtx->yScale = 1.0f; + } + + if (SREG(63) == 4 || (SREG(63) == 2u && osTvType == OS_TV_PAL)) { + gfxCtx->viMode = &osViModePalLan1; + gfxCtx->yScale = 1.0f; + } + + if (SREG(63) == 3 || (SREG(63) == 2u && osTvType == OS_TV_PAL)) { + gfxCtx->viMode = &osViModeFpalLan1; + gfxCtx->yScale = 0.833f; + } + } else { + gfxCtx->viMode = NULL; + } + + if (HREG(80) == 0x15) { + if (HREG(95) != 0x15) { + HREG(95) = 0x15; + HREG(81) = 0; + HREG(82) = gViConfigAdditionalScanLines; + HREG(83) = 0; + HREG(84) = 0; + } + + if (HREG(82) < 0) { + HREG(82) = 0; + } + if (HREG(82) > 0x30) { + HREG(82) = 0x30; + } + + if ((HREG(83) != HREG(82)) || HREG(84) != HREG(81)) { + HREG(83) = HREG(82); + HREG(84) = HREG(81); + gViConfigAdditionalScanLines = HREG(82); + gViConfigYScale = HREG(81) == 0 ? 240.0f / (gViConfigAdditionalScanLines + 240.0f) : 1.0f; + D_80009430 = 1; + } + } + + if (R_PAUSE_MENU_MODE != 2u) { + GameState_Draw(gameState, gfxCtx); + func_800C49F4(gfxCtx); + } + + gameState->frames++; +} + +void GameState_InitArena(GameState* gameState, size_t size) { + void* arena; + + osSyncPrintf("ハイラル確保 サイズ=%u バイト\n"); // "Hyrule reserved size = %u bytes" + arena = GameAlloc_MallocDebug(&gameState->alloc, size, "../game.c", 992); + if (arena != NULL) { + THA_Ct(&gameState->tha, arena, size); + osSyncPrintf("ハイラル確保成功\n"); // "Successful Hyral" + } else { + THA_Ct(&gameState->tha, NULL, 0); + osSyncPrintf("ハイラル確保失敗\n"); // "Failure to secure Hyrule" + Fault_AddHungupAndCrash("../game.c", 999); + } +} + +void GameState_Realloc(GameState* gameState, size_t size) { + GameAlloc* alloc = &gameState->alloc; + void* gameArena; + u32 systemMaxFree; + u32 systemFree; + u32 systemAlloc; + void* thaBufp = gameState->tha.bufp; + + THA_Dt(&gameState->tha); + GameAlloc_Free(alloc, thaBufp); + osSyncPrintf("ハイラル一時解放!!\n"); // "Hyrule temporarily released!!" + SystemArena_GetSizes(&systemMaxFree, &systemFree, &systemAlloc); + if ((systemMaxFree - 0x10) < size) { + osSyncPrintf("%c", 7); + osSyncPrintf(VT_FGCOL(RED)); + + // "Not enough memory. Change the hyral size to the largest possible value" + osSyncPrintf("メモリが足りません。ハイラルサイズを可能な最大値に変更します\n"); + osSyncPrintf("(hyral=%08x max=%08x free=%08x alloc=%08x)\n", size, systemMaxFree, systemFree, systemAlloc); + osSyncPrintf(VT_RST); + size = systemMaxFree - 0x10; + } + + osSyncPrintf("ハイラル再確保 サイズ=%u バイト\n", size); // "Hyral reallocate size = %u bytes" + gameArena = GameAlloc_MallocDebug(alloc, size, "../game.c", 1033); + if (gameArena != NULL) { + THA_Ct(&gameState->tha, gameArena, size); + osSyncPrintf("ハイラル再確保成功\n"); // "Successful reacquisition of Hyrule" + } else { + THA_Ct(&gameState->tha, NULL, 0); + osSyncPrintf("ハイラル再確保失敗\n"); // "Failure to secure Hyral" + SystemArena_Display(); + Fault_AddHungupAndCrash("../game.c", 1044); + } +} + +void GameState_Init(GameState* gameState, GameStateFunc init, GraphicsContext* gfxCtx) { + OSTime startTime; + OSTime endTime; + + osSyncPrintf("game コンストラクタ開始\n"); // "game constructor start" + gameState->gfxCtx = gfxCtx; + gameState->frames = 0; + gameState->main = NULL; + gameState->destroy = NULL; + gameState->running = 1; + startTime = osGetTime(); + gameState->size = 0; + gameState->init = NULL; + endTime = osGetTime(); + + // "game_set_next_game_null processing time %d us" + osSyncPrintf("game_set_next_game_null 処理時間 %d us\n", OS_CYCLES_TO_USEC(endTime - startTime)); + startTime = endTime; + GameAlloc_Init(&gameState->alloc); + + endTime = osGetTime(); + // "gamealloc_init processing time %d us" + osSyncPrintf("gamealloc_init 処理時間 %d us\n", OS_CYCLES_TO_USEC(endTime - startTime)); + + startTime = endTime; + GameState_InitArena(gameState, 0x100000); + R_UPDATE_RATE = 3; + init(gameState); + + endTime = osGetTime(); + // "init processing time %d us" + osSyncPrintf("init 処理時間 %d us\n", OS_CYCLES_TO_USEC(endTime - startTime)); + + startTime = endTime; + LogUtils_CheckNullPointer("this->cleanup", gameState->destroy, "../game.c", 1088); + func_800ACE70(&D_801664F0); + func_800AD920(&D_80166500); + VisMono_Init(&sMonoColors); + if (SREG(48) == 0) { + ViMode_Init(&sViMode); + } + SpeedMeter_Init(&D_801664D0); + func_800AA0B4(); + osSendMesg(&gameState->gfxCtx->queue, NULL, OS_MESG_BLOCK); + + endTime = osGetTime(); + // "Other initialization processing time %d us" + osSyncPrintf("その他初期化 処理時間 %d us\n", OS_CYCLES_TO_USEC(endTime - startTime)); + + Fault_AddClient(&sGameFaultClient, GameState_FaultPrint, NULL, NULL); + + osSyncPrintf("game コンストラクタ終了\n"); // "game constructor end" +} + +void GameState_Destroy(GameState* gameState) { + osSyncPrintf("game デストラクタ開始\n"); // "game destructor start" + func_800C3C20(); + func_800F3054(); + osRecvMesg(&gameState->gfxCtx->queue, NULL, OS_MESG_BLOCK); + LogUtils_CheckNullPointer("this->cleanup", gameState->destroy, "../game.c", 1139); + if (gameState->destroy != NULL) { + gameState->destroy(gameState); + } + func_800AA0F0(); + SpeedMeter_Destroy(&D_801664D0); + func_800ACE90(&D_801664F0); + func_800AD950(&D_80166500); + VisMono_Destroy(&sMonoColors); + if (SREG(48) == 0) { + ViMode_Destroy(&sViMode); + } + THA_Dt(&gameState->tha); + GameAlloc_Cleanup(&gameState->alloc); + SystemArena_Display(); + Fault_RemoveClient(&sGameFaultClient); + + osSyncPrintf("game デストラクタ終了\n"); // "game destructor end" +} + +GameStateFunc GameState_GetInit(GameState* gameState) { + return gameState->init; +} + +size_t GameState_GetSize(GameState* gameState) { + return gameState->size; +} + +u32 GameState_IsRunning(GameState* gameState) { + return gameState->running; +} + +void* GameState_Alloc(GameState* gameState, size_t size, char* file, s32 line) +{ + void* ret; + + if (THA_IsCrash(&gameState->tha)) { + osSyncPrintf("ハイラルは滅亡している\n"); + ret = NULL; + } else if ((uintptr_t)THA_GetSize(&gameState->tha) < size) { + // "Hyral on the verge of extinction does not have %d bytes left (%d bytes until extinction)" + osSyncPrintf("滅亡寸前のハイラルには %d バイトの余力もない(滅亡まであと %d バイト)\n", size, + THA_GetSize(&gameState->tha)); + ret = NULL; + } else { + ret = THA_AllocEndAlign16(&gameState->tha, size); + if (THA_IsCrash(&gameState->tha)) { + osSyncPrintf("ハイラルは滅亡してしまった\n"); // "Hyrule has been destroyed" + ret = NULL; + } + } + if (ret != NULL) { + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("game_alloc(%08x) %08x-%08x [%s:%d]\n", size, ret, (uintptr_t)ret + size, file, line); + osSyncPrintf(VT_RST); + } + return ret; +} + +void* GameState_AllocEndAlign16(GameState* gameState, size_t size) { + return THA_AllocEndAlign16(&gameState->tha, size); +} + +s32 GameState_GetArenaSize(GameState* gameState) { + return THA_GetSize(&gameState->tha); +} diff --git a/soh/src/code/gamealloc.c b/soh/src/code/gamealloc.c new file mode 100644 index 000000000..451c56de2 --- /dev/null +++ b/soh/src/code/gamealloc.c @@ -0,0 +1,80 @@ +#include "global.h" + +void GameAlloc_Log(GameAlloc* this) { + GameAllocEntry* iter; + + osSyncPrintf("this = %08x\n", this); + + iter = this->base.next; + while (iter != &this->base) { + osSyncPrintf("ptr = %08x size = %d\n", iter, iter->size); + iter = iter->next; + } +} + +void* GameAlloc_MallocDebug(GameAlloc* this, size_t size, const char* file, s32 line) { + GameAllocEntry* ptr = SystemArena_MallocDebug(size + sizeof(GameAllocEntry), file, line); + + if (ptr != NULL) { + ptr->size = size; + ptr->prev = this->head; + this->head->next = ptr; + this->head = ptr; + ptr->next = &this->base; + this->base.prev = this->head; + return ptr + 1; + } else { + return NULL; + } +} + +void* GameAlloc_Malloc(GameAlloc* this, size_t size) { + GameAllocEntry* ptr = SystemArena_MallocDebug(size + sizeof(GameAllocEntry), "../gamealloc.c", 93); + + if (ptr != NULL) { + ptr->size = size; + ptr->prev = this->head; + this->head->next = ptr; + this->head = ptr; + ptr->next = &this->base; + this->base.prev = this->head; + return ptr + 1; + } else { + return NULL; + } +} + +void GameAlloc_Free(GameAlloc* this, void* data) { + GameAllocEntry* ptr; + + if (data != NULL) { + ptr = &((GameAllocEntry*)data)[-1]; + LogUtils_CheckNullPointer("ptr->prev", ptr->prev, "../gamealloc.c", 120); + LogUtils_CheckNullPointer("ptr->next", ptr->next, "../gamealloc.c", 121); + ptr->prev->next = ptr->next; + ptr->next->prev = ptr->prev; + this->head = this->base.prev; + SystemArena_FreeDebug(ptr, "../gamealloc.c", 125); + } +} + +void GameAlloc_Cleanup(GameAlloc* this) { + GameAllocEntry* next = this->base.next; + GameAllocEntry* cur; + + while (&this->base != next) { + cur = next; + next = next->next; + SystemArena_FreeDebug(cur, "../gamealloc.c", 145); + } + + this->head = &this->base; + this->base.next = &this->base; + this->base.prev = &this->base; +} + +void GameAlloc_Init(GameAlloc* this) { + this->head = &this->base; + this->base.next = &this->base; + this->base.prev = &this->base; +} diff --git a/soh/src/code/gfxprint.c b/soh/src/code/gfxprint.c new file mode 100644 index 000000000..2dfc21da9 --- /dev/null +++ b/soh/src/code/gfxprint.c @@ -0,0 +1,313 @@ +#include "global.h" + +u16 sGfxPrintFontTLUT[64] = { + 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0xFFFF, 0x0000, + 0xFFFF, 0x0000, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0x0000, 0x0000, + 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, +}; + +u16 sGfxPrintRainbowTLUT[16] = { + 0xF801, 0xFBC1, 0xFFC1, 0x07C1, 0x0421, 0x003F, 0x803F, 0xF83F, + 0xF801, 0xFBC1, 0xFFC1, 0x07C1, 0x0421, 0x003F, 0x803F, 0xF83F, +}; + +u8 sGfxPrintRainbowData[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + +u8 sGfxPrintFontData[(16 * 256) / 2] = { + 0x00, 0xDF, 0xFD, 0x00, 0x0A, 0xEE, 0xFF, 0xA0, 0x0D, 0xF2, 0x2D, 0xD0, 0x06, 0x61, 0x1D, 0xC0, 0x01, 0x12, 0x2D, + 0xD0, 0x06, 0x71, 0x99, 0x00, 0x01, 0x1E, 0xED, 0x10, 0x07, 0x7E, 0xF7, 0x00, 0x01, 0x56, 0x29, 0x90, 0x05, 0x58, + 0x97, 0x60, 0x0D, 0xD2, 0x29, 0x90, 0x05, 0x59, 0x97, 0x70, 0x04, 0xDF, 0xFD, 0x40, 0x02, 0x6E, 0xF7, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xBF, 0xFB, 0x00, 0x0E, 0xFF, 0xFF, 0xC0, 0x0B, 0xF0, 0x0F, 0xB0, + 0x0F, 0xF0, 0x03, 0x30, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x02, 0x20, 0x0C, 0xFB, 0xBF, 0x60, 0x0F, 0xFC, 0xCE, + 0x20, 0x0D, 0xD4, 0x4F, 0xF0, 0x0F, 0xF0, 0x02, 0x20, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0xF0, 0x03, 0x30, 0x0C, 0xFB, + 0xBF, 0x40, 0x0E, 0xF7, 0x77, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDF, 0xFD, 0x00, 0x0A, + 0xEE, 0xFF, 0xA0, 0x0D, 0xF2, 0x2D, 0xD0, 0x06, 0x61, 0x1D, 0xC0, 0x01, 0x12, 0x2D, 0xD0, 0x06, 0x71, 0x99, 0x00, + 0x01, 0x1E, 0xED, 0x10, 0x07, 0x7E, 0xF7, 0x00, 0x01, 0x56, 0x29, 0x90, 0x05, 0x58, 0x97, 0x60, 0x0D, 0xD2, 0x29, + 0x90, 0x05, 0x59, 0x97, 0x70, 0x04, 0xDF, 0xFD, 0x40, 0x02, 0x6E, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xBF, 0xFB, 0x00, 0x00, 0x0D, 0xE0, 0x00, 0x0B, 0xF0, 0x0F, 0xB0, 0x00, 0x5D, 0xE6, 0x00, 0x0F, + 0xF0, 0x0F, 0xF0, 0x05, 0x5C, 0xC6, 0x60, 0x0C, 0xFB, 0xBF, 0x60, 0x77, 0x3F, 0xF3, 0x77, 0x0D, 0xD4, 0x4F, 0xF0, + 0xBB, 0x3F, 0xF3, 0xBB, 0x0F, 0xF0, 0x0F, 0xF0, 0x09, 0x9C, 0xCA, 0xA0, 0x0C, 0xFB, 0xBF, 0x40, 0x00, 0x9D, 0xEA, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0xE0, 0x00, 0x04, 0xC2, 0x2C, 0x40, 0x02, 0x8D, 0x50, 0x20, 0x0C, 0xCA, + 0xAC, 0xC0, 0x21, 0xF9, 0x17, 0x10, 0x04, 0xC2, 0x2C, 0x40, 0x12, 0x49, 0x34, 0x00, 0x00, 0x82, 0x08, 0x00, 0x01, + 0x97, 0x51, 0x10, 0x08, 0x8A, 0x88, 0x80, 0x04, 0x61, 0x52, 0x41, 0x00, 0x80, 0x08, 0x00, 0x43, 0x11, 0x75, 0x30, + 0x00, 0xA2, 0x08, 0x00, 0x60, 0x05, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x40, 0x00, 0x22, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x0F, 0xB0, 0x00, 0x00, 0x00, 0x08, 0x80, 0x04, 0x0D, 0xA4, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x08, 0xCD, 0xE8, 0x80, 0x02, 0x2A, 0xA2, 0x20, 0x08, 0xCD, 0xE8, 0x80, 0x02, 0xAA, 0x22, 0x20, 0x04, + 0x0D, 0xA4, 0x00, 0x0C, 0xD1, 0x00, 0x00, 0x00, 0x0F, 0xB0, 0x00, 0x8C, 0x51, 0x00, 0x00, 0x00, 0x22, 0x11, 0x00, + 0x81, 0x10, 0x00, 0x00, 0x00, 0xDF, 0xFD, 0x00, 0x0A, 0xEE, 0xFF, 0xA0, 0x0D, 0xF2, 0x2D, 0xD0, 0x06, 0x61, 0x1D, + 0xC0, 0x01, 0x12, 0x2D, 0xD0, 0x06, 0x71, 0x99, 0x00, 0x01, 0x1E, 0xED, 0x10, 0x07, 0x7E, 0xF7, 0x00, 0x01, 0x56, + 0x29, 0x90, 0x05, 0x58, 0x97, 0x60, 0x0D, 0xD2, 0x29, 0x90, 0x05, 0x59, 0x97, 0x70, 0x04, 0xDF, 0xFD, 0x40, 0x02, + 0x6E, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x00, 0x04, 0x48, 0x99, 0x80, + 0x03, 0x3C, 0xC3, 0x30, 0x00, 0xCD, 0x10, 0x88, 0x03, 0x3C, 0xC3, 0x30, 0x02, 0xBF, 0x62, 0xA8, 0x00, 0x33, 0x33, + 0x20, 0x01, 0x10, 0x4C, 0x80, 0x01, 0x10, 0x03, 0x30, 0x00, 0x15, 0xC8, 0x00, 0x03, 0x3C, 0xC3, 0x30, 0x02, 0x67, + 0x32, 0x20, 0x00, 0x3F, 0xF3, 0x00, 0x04, 0x40, 0x99, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0xDF, 0xFD, 0x10, 0x07, 0xFF, 0xFF, 0x60, 0x1C, 0xE0, 0x0E, 0xC1, 0x0F, 0xF0, 0x09, 0x90, 0x1E, 0xE1, 0x16, 0x61, + 0x0F, 0xF0, 0x01, 0x10, 0x1E, 0xF4, 0x56, 0x21, 0x0F, 0xF6, 0x67, 0x10, 0x1E, 0xF2, 0x36, 0x61, 0x0F, 0xF0, 0x89, + 0x90, 0x1E, 0xF1, 0x0F, 0xE1, 0x0F, 0xF0, 0x09, 0x90, 0x16, 0xEC, 0xCE, 0x21, 0x07, 0xFB, 0xBB, 0x20, 0x01, 0x11, + 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x09, 0xB6, 0x6F, 0xD0, 0x27, 0xD8, 0x8E, 0x60, 0x09, 0x92, 0xED, 0x10, 0x2F, + 0xF0, 0x2E, 0xE0, 0x09, 0x9A, 0xE5, 0x10, 0x2F, 0xF6, 0x2E, 0xE0, 0x09, 0x9B, 0x75, 0x10, 0x2F, 0xD6, 0x4E, 0xE0, + 0x0D, 0xDA, 0xE5, 0x10, 0x2F, 0xD0, 0x4E, 0xE0, 0x0D, 0xD2, 0xED, 0x10, 0x2F, 0xD0, 0x0E, 0xE0, 0x09, 0xF6, 0x6F, + 0x90, 0x27, 0xD9, 0x9F, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x8F, 0x71, + 0x1F, 0xF0, 0x2F, 0xD0, 0x0F, 0xF0, 0x8F, 0x71, 0x1F, 0xF0, 0x2F, 0xD0, 0x07, 0x70, 0x8E, 0x61, 0x1E, 0xE0, 0x27, + 0xDD, 0xDF, 0x60, 0x8E, 0x69, 0x1E, 0xE0, 0x27, 0x76, 0x4A, 0xA0, 0x8E, 0xE9, 0x9E, 0xE0, 0x2F, 0xD0, 0x6E, 0x80, + 0x8A, 0xE7, 0xFE, 0xA0, 0x07, 0xFA, 0x8E, 0x60, 0x88, 0x27, 0x7A, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x7C, 0xCF, 0xF0, 0x13, 0x26, 0x60, 0x11, 0x07, 0x7C, 0xCF, 0xF0, 0x03, 0x76, 0x65, 0x10, 0x02, 0x39, + 0xD7, 0x20, 0x04, 0x53, 0x35, 0x40, 0x00, 0x2F, 0xF2, 0x00, 0x01, 0x13, 0x31, 0x10, 0x00, 0x5F, 0xB1, 0x00, 0x00, + 0x03, 0x30, 0x00, 0x05, 0x5E, 0xE5, 0x50, 0x01, 0x13, 0x31, 0x10, 0x05, 0x5E, 0xED, 0xD0, 0x02, 0x23, 0x30, 0x00, + 0x00, 0x08, 0x88, 0x80, 0x8A, 0xAB, 0xB8, 0x88, 0x00, 0x00, 0x11, 0x00, 0x00, 0x04, 0x45, 0x10, 0x04, 0x62, 0x33, + 0x20, 0x00, 0x44, 0x01, 0x10, 0x04, 0xC8, 0x9A, 0xA0, 0x00, 0xEE, 0xAB, 0x10, 0x0C, 0xE6, 0x67, 0x20, 0x0E, 0xF5, + 0x5F, 0xB0, 0x0E, 0xE0, 0x06, 0x60, 0x0B, 0xF6, 0x2B, 0x90, 0x0E, 0xE0, 0x06, 0x60, 0x03, 0xFC, 0x89, 0x90, 0x04, + 0xEE, 0xEE, 0xA0, 0x00, 0x77, 0x3B, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x08, 0x88, 0x88, 0x00, 0x09, 0x90, 0x00, 0x00, + 0x00, 0x11, 0x10, 0x00, 0x09, 0x92, 0x24, 0x40, 0x00, 0x01, 0x10, 0x00, 0x09, 0x90, 0x88, 0x00, 0x26, 0xEF, 0xDE, + 0x20, 0x09, 0x9B, 0xB5, 0x40, 0x2E, 0xC3, 0x3C, 0xE2, 0x0D, 0x9A, 0x25, 0x50, 0x2E, 0xC3, 0x3C, 0xE2, 0x0D, 0xDA, + 0xA5, 0x50, 0x2E, 0xC3, 0x3C, 0xE2, 0x09, 0xD6, 0xED, 0x10, 0x26, 0xCB, 0xBC, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, + 0x05, 0xFB, 0xFF, 0xE0, 0x8E, 0x61, 0x16, 0xE8, 0x0F, 0xF4, 0x03, 0x30, 0x8F, 0x71, 0x17, 0xF8, 0x07, 0xFC, 0x8B, + 0x30, 0x8E, 0x69, 0x96, 0xE8, 0x05, 0x73, 0x3B, 0xA0, 0x8A, 0x6D, 0xD6, 0xA8, 0x0D, 0xD8, 0x8A, 0x20, 0x08, 0xA7, + 0x79, 0xB2, 0x01, 0x10, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x8A, 0x01, 0x10, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x80, 0xA1, 0x10, 0x00, 0x07, 0x74, 0x4F, 0x70, 0x80, 0xA9, 0x90, 0x00, 0x02, 0x31, 0xDF, 0x20, + 0x84, 0xE6, 0x00, 0x04, 0x00, 0x27, 0xDA, 0x20, 0xC8, 0xAA, 0x4C, 0x40, 0x00, 0x57, 0x3B, 0x20, 0x00, 0xA1, 0x18, + 0x00, 0x05, 0x54, 0x6F, 0x50, 0x00, 0xA9, 0x98, 0x00, 0x02, 0x22, 0x20, 0x80, 0x02, 0x00, 0x18, 0x88, 0x00, 0x04, + 0x44, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x44, 0x40, 0x0C, 0x44, 0x44, 0x00, 0x00, 0x04, 0x40, 0x00, 0x88, + 0xC0, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x0C, 0x46, 0xA4, 0x40, 0x00, 0x0C, 0xC0, 0x00, 0x08, 0x8E, 0xE0, 0x00, + 0x02, 0x08, 0x80, 0x00, 0x80, 0xD0, 0x88, 0x00, 0x28, 0xA8, 0x80, 0x00, 0x88, 0xCD, 0x4C, 0x40, 0x0A, 0x88, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xE0, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x88, 0x00, 0x80, 0x01, 0x06, 0x10, 0x00, 0x56, 0xE7, 0x50, 0x80, 0x02, 0x1F, 0xF1, 0x00, 0x38, + 0x8C, 0xB8, 0x00, 0x0B, 0xF6, 0x0B, 0x00, 0x94, 0xC0, 0x28, 0x00, 0x06, 0x07, 0x6A, 0x00, 0xCB, 0xA6, 0xC8, 0x00, + 0x00, 0x47, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x80, 0x00, 0x00, 0x39, 0x14, + 0x20, 0x02, 0x22, 0x24, 0x00, 0x08, 0xAE, 0xA8, 0x60, 0x04, 0x28, 0x99, 0x70, 0x07, 0x75, 0xD1, 0x04, 0x0F, 0xB3, + 0x33, 0xD0, 0x00, 0xAE, 0xBE, 0xA4, 0x25, 0x15, 0x20, 0xA0, 0x02, 0x61, 0x0C, 0x02, 0x20, 0x42, 0x08, 0x20, 0x2C, + 0x30, 0x14, 0x02, 0x02, 0x28, 0x82, 0x00, 0x03, 0xAC, 0xC1, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x12, 0x00, 0x08, 0x00, 0x28, 0x00, 0x0A, 0xCF, 0xEE, 0x20, 0x0B, 0x62, 0x2E, 0x20, 0x02, 0x10, 0x82, + 0x40, 0x01, 0x44, 0xE4, 0x40, 0x03, 0x00, 0x0E, 0x00, 0x8D, 0xEA, 0xAC, 0x00, 0x02, 0x10, 0x0A, 0x00, 0x01, 0xE0, + 0x24, 0x00, 0x0C, 0x21, 0x02, 0x00, 0x09, 0x42, 0x21, 0x00, 0x00, 0xCC, 0xF4, 0x40, 0x02, 0xBF, 0xD4, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x44, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x0C, 0xCC, 0xC4, 0x40, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x02, 0xA0, + 0x40, 0x00, 0x0C, 0xC0, 0x00, 0x04, 0xCE, 0x64, 0x40, 0x02, 0x08, 0x80, 0x00, 0x00, 0x90, 0x00, 0x40, 0x28, 0xA8, + 0x80, 0x00, 0x08, 0x01, 0x04, 0x00, 0x0A, 0x88, 0x80, 0x00, 0x04, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x54, 0x44, 0x00, + 0xEE, 0xFE, 0xE0, 0x00, 0x09, 0x3B, 0x3F, 0x00, 0x21, 0xD8, 0x20, 0x00, 0x00, 0x54, 0x4F, 0x00, 0x18, 0x58, 0x20, + 0x00, 0x00, 0x01, 0x86, 0x00, 0xC6, 0x7E, 0x40, 0x00, 0x00, 0xEF, 0x66, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0xC0, 0x20, 0x00, 0xAA, 0xAA, 0xEA, 0x20, 0xEF, 0xFF, 0xFF, 0x00, 0x80, + 0x44, 0x19, 0x30, 0x00, 0x49, 0x24, 0x00, 0xC5, 0x35, 0x1B, 0x10, 0x00, 0x4B, 0x24, 0x00, 0x01, 0x35, 0xA0, 0x00, + 0x8C, 0xA9, 0xAC, 0x80, 0x00, 0x2C, 0x00, 0x00, 0x04, 0x21, 0xA4, 0x00, 0x2A, 0x84, 0x00, 0x00, 0x73, 0x11, 0xF1, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x11, 0x19, 0x00, 0x00, 0x40, 0x00, 0x00, 0x8F, 0xEE, + 0xEF, 0xE0, 0x0B, 0x76, 0x66, 0xD0, 0x1A, 0x00, 0x0B, 0x40, 0x4C, 0x40, 0x02, 0xD0, 0x28, 0x00, 0x1A, 0x40, 0x01, + 0xD0, 0x2C, 0x10, 0x00, 0x00, 0x38, 0x40, 0x00, 0x40, 0x28, 0x10, 0x00, 0x01, 0xA0, 0x40, 0x00, 0x42, 0x83, 0x00, + 0x05, 0xFE, 0x44, 0x40, 0x03, 0xFD, 0x54, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x99, 0x9B, + 0x00, 0x00, 0x10, 0x20, 0x00, 0x07, 0x26, 0x21, 0x40, 0x2A, 0xFE, 0xEE, 0xA0, 0x8D, 0x8C, 0xA9, 0xC0, 0x00, 0x10, + 0x20, 0x80, 0x32, 0x33, 0xB3, 0x60, 0x00, 0x19, 0x28, 0x00, 0x00, 0x00, 0xA1, 0x40, 0x00, 0x10, 0xB1, 0x00, 0x00, + 0x08, 0x34, 0x00, 0x00, 0x1A, 0x08, 0x00, 0x05, 0xF7, 0x40, 0x00, 0x8E, 0xF4, 0x44, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x02, 0x80, 0x00, 0x04, 0x00, 0x00, 0x1D, 0x11, 0xDB, 0x00, 0xDD, 0xFD, 0xDD, + 0xD0, 0x0C, 0x88, 0x07, 0x00, 0x02, 0x06, 0x00, 0x90, 0x48, 0x00, 0x34, 0x00, 0x2C, 0x04, 0x2C, 0x10, 0x48, 0x11, + 0x21, 0x40, 0x04, 0x84, 0x83, 0x40, 0x59, 0x03, 0x00, 0x50, 0x40, 0x0C, 0x10, 0x60, 0x42, 0xA9, 0x88, 0xC0, 0x40, + 0x15, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x02, 0x00, 0x40, 0x08, 0x98, 0x88, 0x80, + 0x08, 0xF9, 0x98, 0xC0, 0x06, 0x77, 0x75, 0x50, 0x02, 0x0C, 0x05, 0x00, 0x19, 0x98, 0xA8, 0xD0, 0x0B, 0x99, 0xCA, + 0x80, 0x04, 0x54, 0x65, 0xC0, 0x20, 0x08, 0x50, 0x20, 0x00, 0x10, 0x20, 0xC0, 0x31, 0x1C, 0x04, 0x20, 0x00, 0x01, + 0x28, 0x40, 0x26, 0x63, 0xBB, 0xE0, 0x26, 0xEF, 0xE6, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x01, 0x00, 0xC8, 0xC0, 0x00, 0x00, 0x0F, 0x8A, 0x89, 0x80, 0xC3, 0xF3, 0x11, 0x30, 0x0F, 0x02, 0x01, 0x80, + 0xC9, 0xC0, 0x00, 0x30, 0x0F, 0x02, 0x05, 0xA0, 0x00, 0x00, 0x00, 0x30, 0x0E, 0x02, 0x05, 0xA0, 0x00, 0x00, 0x00, + 0x30, 0x0E, 0x02, 0x52, 0x80, 0x00, 0x00, 0x03, 0x00, 0x2C, 0xDF, 0xA8, 0x80, 0x02, 0x33, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x88, 0x00, 0x01, 0x02, 0x80, 0x00, 0x03, 0xFF, 0xF7, 0x00, 0x0F, + 0x26, 0xE4, 0x72, 0xCC, 0x38, 0x00, 0x40, 0x0C, 0x38, 0x99, 0x00, 0x03, 0x0A, 0x31, 0x50, 0x0C, 0xB1, 0x82, 0x80, + 0x03, 0x28, 0x06, 0x00, 0x87, 0x88, 0x2A, 0xA0, 0x01, 0x05, 0xC2, 0x00, 0x85, 0x82, 0xC2, 0x80, 0x10, 0x00, 0x39, + 0x10, 0x08, 0x51, 0xBF, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x48, 0x9D, + 0xCC, 0x40, 0xC9, 0xE6, 0x7F, 0x40, 0x40, 0x00, 0x94, 0x00, 0x5B, 0x21, 0x0C, 0xB0, 0x48, 0xAE, 0xCC, 0x40, 0xE1, + 0x30, 0x0C, 0x30, 0x43, 0x01, 0xA4, 0x00, 0xE1, 0x24, 0x5D, 0x30, 0x78, 0x8C, 0xD6, 0x10, 0xF1, 0x60, 0x94, 0x70, + 0xD0, 0x40, 0x9C, 0x70, 0x0B, 0x8C, 0x53, 0x00, 0x0C, 0x9D, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x39, 0x50, 0x00, 0x00, 0x88, 0xF0, 0x00, 0x2E, 0xAF, 0xC6, 0x00, 0x03, 0x01, 0x77, 0x60, 0x04, 0xF0, + 0x41, 0x60, 0x03, 0x92, 0xF8, 0x12, 0x0F, 0xBD, 0x91, 0x40, 0x1B, 0x28, 0x60, 0x92, 0x70, 0xF4, 0x01, 0xF0, 0x0A, + 0xD4, 0x65, 0x82, 0x53, 0xE0, 0x01, 0xE0, 0x04, 0x10, 0x68, 0x60, 0x04, 0x2A, 0xBE, 0x00, 0x00, 0x4F, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x3A, 0xEE, 0x00, 0xC8, 0xC0, 0x00, 0x00, 0x0D, 0x84, 0xA5, + 0x00, 0xC1, 0xC2, 0x11, 0x00, 0x45, 0x0E, 0x27, 0x00, 0xD9, 0xC3, 0x00, 0x10, 0x07, 0xF8, 0x8D, 0x20, 0x01, 0x30, + 0x00, 0x10, 0xAC, 0x02, 0x25, 0xA0, 0x01, 0x22, 0x00, 0x10, 0x44, 0x20, 0x16, 0xA0, 0x13, 0x02, 0x00, 0x30, 0x04, + 0x1B, 0xAA, 0x40, 0x21, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +// Can be used to set GFXP_FLAG_ENLARGE by default +static u8 sDefaultSpecialFlags; + +void GfxPrint_Setup(GfxPrint* this) { + s32 width = 16; + s32 height = 256; + s32 i; + + gDPPipeSync(this->dList++); + gDPSetOtherMode(this->dList++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_IA16 | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_XLU_SURF | G_RM_XLU_SURF2); + gDPSetCombineMode(this->dList++, G_CC_DECALRGBA, G_CC_DECALRGBA); + gDPLoadTextureBlock_4b(this->dList++, sGfxPrintFontData, G_IM_FMT_CI, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gDPLoadTLUT(this->dList++, 64, 256, sGfxPrintFontTLUT); + + for (i = 1; i < 4; i++) { + gDPSetTile(this->dList++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0, i * 2, i, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); + gDPSetTileSize(this->dList++, i * 2, 0, 0, 60, 1020); + } + + gDPSetColor(this->dList++, G_SETPRIMCOLOR, this->color.rgba); + + /*gDPLoadMultiTile_4b(this->dList++, sGfxPrintRainbowData, 0, 1, G_IM_FMT_CI, 2, 8, 0, 0, 1, 7, 4, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 1, 3, G_TX_NOLOD, G_TX_NOLOD); + + gDPLoadTLUT(this->dList++, 16, 320, sGfxPrintRainbowTLUT); + + for (i = 1; i < 4; i++) { + gDPSetTile(this->dList++, G_IM_FMT_CI, G_IM_SIZ_4b, 1, 0, i * 2 + 1, 4, G_TX_NOMIRROR | G_TX_WRAP, 3, + G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, 1, G_TX_NOLOD); + gDPSetTileSize(this->dList++, i * 2 + 1, 0, 0, 4, 28); + }*/ +} + +void GfxPrint_SetColor(GfxPrint* this, u32 r, u32 g, u32 b, u32 a) { + this->color.r = r; + this->color.g = g; + this->color.b = b; + this->color.a = a; + gDPPipeSync(this->dList++); + gDPSetColor(this->dList++, G_SETPRIMCOLOR, this->color.rgba); +} + +void GfxPrint_SetPosPx(GfxPrint* this, s32 x, s32 y) { + this->posX = this->baseX + (x * 4); + this->posY = this->baseY + (y * 4); +} + +void GfxPrint_SetPos(GfxPrint* this, s32 x, s32 y) { + GfxPrint_SetPosPx(this, x * 8, y * 8); +} + +void GfxPrint_SetBasePosPx(GfxPrint* this, s32 x, s32 y) { + this->baseX = x * 4; + this->baseY = y * 4; +} + +void GfxPrint_PrintCharImpl(GfxPrint* this, u8 c) { + u32 tile = (c & 0xFF) * 2; + + if (this->flags & GFXP_FLAG_UPDATE) { + this->flags &= ~GFXP_FLAG_UPDATE; + + gDPPipeSync(this->dList++); + if (this->flags & GFXP_FLAG_RAINBOW) { + gDPSetTextureLUT(this->dList++, G_TT_RGBA16); + gDPSetCycleType(this->dList++, G_CYC_2CYCLE); + gDPSetRenderMode(this->dList++, G_RM_OPA_CI, G_RM_XLU_SURF2); + gDPSetCombineMode(this->dList++, G_CC_INTERFERENCE, G_CC_PASS2); + } else { + gDPSetTextureLUT(this->dList++, G_TT_IA16); + gDPSetCycleType(this->dList++, G_CYC_1CYCLE); + gDPSetRenderMode(this->dList++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetCombineMode(this->dList++, G_CC_MODULATEIDECALA_PRIM, G_CC_MODULATEIDECALA_PRIM); + } + } + + if (this->flags & GFXP_FLAG_SHADOW) { + gDPSetColor(this->dList++, G_SETPRIMCOLOR, 0); + + if (this->flags & GFXP_FLAG_ENLARGE) { + gSPTextureRectangle(this->dList++, (this->posX + 4) << 1, (this->posY + 4) << 1, (this->posX + 4 + 32) << 1, + (this->posY + 4 + 32) << 1, tile, (u16)(c & 4) * 64, (u16)(c >> 3) * 256, 1 << 9, + 1 << 9); + } else { + gSPTextureRectangle(this->dList++, this->posX + 4, this->posY + 4, this->posX + 4 + 32, this->posY + 4 + 32, + tile, (u16)(c & 4) * 64, (u16)(c >> 3) * 256, 1 << 10, 1 << 10); + } + + gDPSetColor(this->dList++, G_SETPRIMCOLOR, this->color.rgba); + } + + if (this->flags & GFXP_FLAG_ENLARGE) { + gSPTextureRectangle(this->dList++, (this->posX) << 1, (this->posY) << 1, (this->posX + 32) << 1, + (this->posY + 32) << 1, tile, (u16)(c & 4) * 64, (u16)(c >> 3) * 256, 1 << 9, 1 << 9); + } else { + gSPTextureRectangle(this->dList++, this->posX, this->posY, this->posX + 32, this->posY + 32, tile, + (u16)(c & 4) * 64, (u16)(c >> 3) * 256, 1 << 10, 1 << 10); + } + + this->posX += 32; +} + +void GfxPrint_PrintStringWithSize(GfxPrint* this, const void* buffer, u32 charSize, u32 charCount) { + OTRGfxPrint((const char*)buffer, this, GfxPrint_PrintCharImpl); +} + +void GfxPrint_PrintString(GfxPrint* this, const char* str) { + OTRGfxPrint(str, this, GfxPrint_PrintCharImpl); +} + +void* GfxPrint_Callback(void* arg, const char* str, size_t size) { + GfxPrint* this = arg; + + GfxPrint_PrintStringWithSize(this, str, sizeof(char), size); + + return this; +} + +void GfxPrint_Init(GfxPrint* this) { + this->flags &= ~GFXP_FLAG_OPEN; + + this->callback = GfxPrint_Callback; + this->dList = NULL; + this->posX = 0; + this->posY = 0; + this->baseX = 0; + this->baseY = 0; + this->color.rgba = 0; + + this->flags &= ~GFXP_FLAG_HIRAGANA; + this->flags &= ~GFXP_FLAG_RAINBOW; + this->flags |= GFXP_FLAG_SHADOW; + this->flags |= GFXP_FLAG_UPDATE; + + if (sDefaultSpecialFlags & GFXP_FLAG_ENLARGE) { + this->flags |= GFXP_FLAG_ENLARGE; + } else { + this->flags &= ~GFXP_FLAG_ENLARGE; + } +} + +void GfxPrint_Destroy(GfxPrint* this) { +} + +void GfxPrint_Open(GfxPrint* this, Gfx* dList) { + if (!(this->flags & GFXP_FLAG_OPEN)) { + this->flags |= GFXP_FLAG_OPEN; + this->dList = dList; + GfxPrint_Setup(this); + } else { + osSyncPrintf("gfxprint_open:2重オープンです\n"); + } +} + +Gfx* GfxPrint_Close(GfxPrint* this) { + Gfx* ret; + + this->flags &= ~GFXP_FLAG_OPEN; + gDPPipeSync(this->dList++); + ret = this->dList; + this->dList = NULL; + + return ret; +} + +s32 GfxPrint_VPrintf(GfxPrint* this, const char* fmt, va_list args) { + return PrintUtils_VPrintf(&this->callback, fmt, args); +} + +s32 GfxPrint_Printf(GfxPrint* this, const char* fmt, ...) { + s32 ret; + va_list args; + va_start(args, fmt); + + ret = GfxPrint_VPrintf(this, fmt, args); + + va_end(args); + + return ret; +} diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c new file mode 100644 index 000000000..9d2886ef1 --- /dev/null +++ b/soh/src/code/graph.c @@ -0,0 +1,610 @@ +#include "global.h" +#include "vt.h" +#include "regs.h" + +#include + +#include "soh/Enhancements/gameconsole.h" + +#define GFXPOOL_HEAD_MAGIC 0x1234 +#define GFXPOOL_TAIL_MAGIC 0x5678 + +OSTime sGraphUpdateTime; +OSTime sGraphSetTaskTime; +FaultClient sGraphFaultClient; +CfbInfo sGraphCfbInfos[3]; +FaultClient sGraphUcodeFaultClient; + +// clang-format off +UCodeInfo D_8012D230[3] = { + //{ UCODE_F3DZEX, D_80155F50 }, + { UCODE_UNK, NULL }, + //{ UCODE_S2DEX, D_80113070 }, +}; + +UCodeInfo D_8012D248[3] = { + //{ UCODE_F3DZEX, D_80155F50 }, + { UCODE_UNK, NULL }, + //{ UCODE_S2DEX, D_80113070 }, +}; +// clang-format on + +void Graph_FaultClient() { + void* nextFb = osViGetNextFramebuffer(); + void* newFb = ((uintptr_t)SysCfb_GetFbPtr(0) != (uintptr_t)nextFb) ? SysCfb_GetFbPtr(0) : SysCfb_GetFbPtr(1); + + osViSwapBuffer(newFb); + Fault_WaitForInput(); + osViSwapBuffer(nextFb); +} + +void Graph_DisassembleUCode(Gfx* workBuf) { + #if 0 + UCodeDisas disassembler; + + if (HREG(80) == 7 && HREG(81) != 0) { + UCodeDisas_Init(&disassembler); + disassembler.enableLog = HREG(83); + UCodeDisas_RegisterUCode(&disassembler, ARRAY_COUNT(D_8012D230), D_8012D230); + //UCodeDisas_SetCurUCode(&disassembler, D_80155F50); + UCodeDisas_Disassemble(&disassembler, workBuf); + HREG(93) = disassembler.dlCnt; + HREG(84) = disassembler.tri2Cnt * 2 + disassembler.tri1Cnt + (disassembler.quadCnt * 2) + disassembler.lineCnt; + HREG(85) = disassembler.vtxCnt; + HREG(86) = disassembler.spvtxCnt; + HREG(87) = disassembler.tri1Cnt; + HREG(88) = disassembler.tri2Cnt; + HREG(89) = disassembler.quadCnt; + HREG(90) = disassembler.lineCnt; + HREG(91) = disassembler.syncErr; + HREG(92) = disassembler.loaducodeCnt; + if (HREG(82) == 1 || HREG(82) == 2) { + osSyncPrintf("vtx_cnt=%d\n", disassembler.vtxCnt); + osSyncPrintf("spvtx_cnt=%d\n", disassembler.spvtxCnt); + osSyncPrintf("tri1_cnt=%d\n", disassembler.tri1Cnt); + osSyncPrintf("tri2_cnt=%d\n", disassembler.tri2Cnt); + osSyncPrintf("quad_cnt=%d\n", disassembler.quadCnt); + osSyncPrintf("line_cnt=%d\n", disassembler.lineCnt); + osSyncPrintf("sync_err=%d\n", disassembler.syncErr); + osSyncPrintf("loaducode_cnt=%d\n", disassembler.loaducodeCnt); + osSyncPrintf("dl_depth=%d\n", disassembler.dlDepth); + osSyncPrintf("dl_cnt=%d\n", disassembler.dlCnt); + } + UCodeDisas_Destroy(&disassembler); + } + #endif +} + +void Graph_UCodeFaultClient(Gfx* workBuf) { + #if 0 + UCodeDisas disassembler; + + UCodeDisas_Init(&disassembler); + disassembler.enableLog = true; + UCodeDisas_RegisterUCode(&disassembler, ARRAY_COUNT(D_8012D248), D_8012D248); + //UCodeDisas_SetCurUCode(&disassembler, D_80155F50); + UCodeDisas_Disassemble(&disassembler, workBuf); + UCodeDisas_Destroy(&disassembler); + #endif +} + +void Graph_InitTHGA(GraphicsContext* gfxCtx) { + GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx & 1]; + + pool->headMagic = GFXPOOL_HEAD_MAGIC; + pool->tailMagic = GFXPOOL_TAIL_MAGIC; + THGA_Ct(&gfxCtx->polyOpa, pool->polyOpaBuffer, sizeof(pool->polyOpaBuffer)); + THGA_Ct(&gfxCtx->polyXlu, pool->polyXluBuffer, sizeof(pool->polyXluBuffer)); + THGA_Ct(&gfxCtx->polyKal, pool->polyKalBuffer, sizeof(pool->polyKalBuffer)); + THGA_Ct(&gfxCtx->overlay, pool->overlayBuffer, sizeof(pool->overlayBuffer)); + THGA_Ct(&gfxCtx->work, pool->workBuffer, sizeof(pool->workBuffer)); + + gfxCtx->polyOpaBuffer = pool->polyOpaBuffer; + gfxCtx->polyXluBuffer = pool->polyXluBuffer; + gfxCtx->polyKalBuffer = pool->polyKalBuffer; + gfxCtx->overlayBuffer = pool->overlayBuffer; + gfxCtx->workBuffer = pool->workBuffer; + + gfxCtx->curFrameBuffer = (u16*)SysCfb_GetFbPtr(gfxCtx->fbIdx % 2); + gfxCtx->unk_014 = 0; +} + +GameStateOverlay* Graph_GetNextGameState(GameState* gameState) { + void* gameStateInitFunc = GameState_GetInit(gameState); + + if (gameStateInitFunc == TitleSetup_Init) { + return &gGameStateOverlayTable[0]; + } + if (gameStateInitFunc == Select_Init) { + return &gGameStateOverlayTable[1]; + } + if (gameStateInitFunc == Title_Init) { + return &gGameStateOverlayTable[2]; + } + if (gameStateInitFunc == Gameplay_Init) { + return &gGameStateOverlayTable[3]; + } + if (gameStateInitFunc == Opening_Init) { + return &gGameStateOverlayTable[4]; + } + if (gameStateInitFunc == FileChoose_Init) { + return &gGameStateOverlayTable[5]; + } + + LOG_ADDRESS("game_init_func", gameStateInitFunc, "../graph.c", 696); + return NULL; +} + +void Graph_Init(GraphicsContext* gfxCtx) { + memset(gfxCtx,0, sizeof(GraphicsContext)); + gfxCtx->gfxPoolIdx = 0; + gfxCtx->fbIdx = 0; + gfxCtx->viMode = NULL; + gfxCtx->viFeatures = gViConfigFeatures; + gfxCtx->xScale = gViConfigXScale; + gfxCtx->yScale = gViConfigYScale; + osCreateMesgQueue(&gfxCtx->queue, gfxCtx->msgBuff, ARRAY_COUNT(gfxCtx->msgBuff)); + func_800D31F0(); + Fault_AddClient(&sGraphFaultClient, Graph_FaultClient, 0, 0); +} + +void Graph_Destroy(GraphicsContext* gfxCtx) { + func_800D3210(); + Fault_RemoveClient(&sGraphFaultClient); +} + +void Graph_TaskSet00(GraphicsContext* gfxCtx) { + static Gfx* D_8012D260 = NULL; + static s32 sGraphCfbInfoIdx = 0; + + OSTime time; + OSTimer timer; + OSMesg msg; + OSTask_t* task = &gfxCtx->task.list.t; + OSScTask* scTask = &gfxCtx->task; + CfbInfo* cfb; + s32 pad1; + + D_8016A528 = osGetTime() - sGraphSetTaskTime - D_8016A558; + + osSetTimer(&timer, OS_USEC_TO_CYCLES(3000000), 0, &gfxCtx->queue, (OSMesg)666); + + osRecvMesg(&gfxCtx->queue, &msg, OS_MESG_BLOCK); + osStopTimer(&timer); + //OTRTODO - Proper GFX crash handler + #if 0 + if (msg == (OSMesg)666) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("RCPが帰ってきませんでした。"); // "RCP did not return." + osSyncPrintf(VT_RST); + LogUtils_LogHexDump((void*)&HW_REG(SP_MEM_ADDR_REG, u32), 0x20); + LogUtils_LogHexDump((void*)&DPC_START_REG, 0x20); + LogUtils_LogHexDump(gGfxSPTaskYieldBuffer, sizeof(gGfxSPTaskYieldBuffer)); + + SREG(6) = -1; + if (D_8012D260 != NULL) { + HREG(80) = 7; + HREG(81) = 1; + HREG(83) = 2; + D_8012D260 = D_8012D260; + Graph_DisassembleUCode(D_8012D260); + } + Fault_AddHungupAndCrashImpl("RCP is HUNG UP!!", "Oh! MY GOD!!"); + } + #endif + osRecvMesg(&gfxCtx->queue, &msg, OS_MESG_NOBLOCK); + + D_8012D260 = gfxCtx->workBuffer; + if (gfxCtx->callback != NULL) { + gfxCtx->callback(gfxCtx, gfxCtx->callbackParam); + } + + time = osGetTime(); + if (D_8016A550 != 0) { + D_8016A558 = (D_8016A558 + time) - D_8016A550; + D_8016A550 = time; + } + D_8016A520 = D_8016A558; + D_8016A558 = 0; + sGraphSetTaskTime = osGetTime(); + + task->type = M_GFXTASK; + task->flags = OS_SC_DRAM_DLIST; + task->ucode_boot = SysUcode_GetUCodeBoot(); + task->ucode_boot_size = SysUcode_GetUCodeBootSize(); + task->ucode = SysUcode_GetUCode(); + task->ucode_data = SysUcode_GetUCodeData(); + task->ucode_size = 0x1000; + task->ucode_data_size = 0x800; + task->dram_stack = (u64*)gGfxSPTaskStack; + task->dram_stack_size = sizeof(gGfxSPTaskStack); + task->output_buff = gGfxSPTaskOutputBuffer; + task->output_buff_size = (u64*)((u8*)gGfxSPTaskOutputBuffer + sizeof(gGfxSPTaskOutputBuffer)); + task->data_ptr = (u64*)gfxCtx->workBuffer; + + OPEN_DISPS(gfxCtx, "../graph.c", 828); + task->data_size = (uintptr_t)WORK_DISP - (uintptr_t)gfxCtx->workBuffer; + CLOSE_DISPS(gfxCtx, "../graph.c", 830); + + { s32 pad2; } // Necessary to match stack usage + + task->yield_data_ptr = (u64*)gGfxSPTaskYieldBuffer; + task->yield_data_size = sizeof(gGfxSPTaskYieldBuffer); + + scTask->next = NULL; + scTask->flags = OS_SC_RCP_MASK | OS_SC_SWAPBUFFER | OS_SC_LAST_TASK; + if (SREG(33) & 1) { + SREG(33) &= ~1; + scTask->flags &= ~OS_SC_SWAPBUFFER; + gfxCtx->fbIdx--; + } + + scTask->msgQ = &gfxCtx->queue; + scTask->msg = NULL; + + cfb = &sGraphCfbInfos[sGraphCfbInfoIdx++]; + cfb->fb1 = gfxCtx->curFrameBuffer; + cfb->swapBuffer = gfxCtx->curFrameBuffer; + cfb->viMode = gfxCtx->viMode; + cfb->features = gfxCtx->viFeatures; + cfb->xScale = gfxCtx->xScale; + cfb->yScale = gfxCtx->yScale; + cfb->unk_10 = 0; + cfb->updateRate = R_UPDATE_RATE; + + scTask->framebuffer = cfb; + sGraphCfbInfoIdx = sGraphCfbInfoIdx % ARRAY_COUNT(sGraphCfbInfos); + + if (1) {} + + gfxCtx->schedMsgQ = &gSchedContext.cmdQ; + + osSendMesg(&gSchedContext.cmdQ, scTask, OS_MESG_BLOCK); + Sched_SendEntryMsg(&gSchedContext); +} + +void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState) { + u32 problem; + + gameState->unk_A0 = 0; + Graph_InitTHGA(gfxCtx); + + OPEN_DISPS(gfxCtx, "../graph.c", 966); + + gDPNoOpString(WORK_DISP++, "WORK_DISP 開始", 0); + gDPNoOpString(POLY_OPA_DISP++, "POLY_OPA_DISP 開始", 0); + gDPNoOpString(POLY_XLU_DISP++, "POLY_XLU_DISP 開始", 0); + gDPNoOpString(OVERLAY_DISP++, "OVERLAY_DISP 開始", 0); + + CLOSE_DISPS(gfxCtx, "../graph.c", 975); + + GameState_ReqPadData(gameState); + GameState_Update(gameState); + + OPEN_DISPS(gfxCtx, "../graph.c", 987); + + gDPNoOpString(WORK_DISP++, "WORK_DISP 終了", 0); + gDPNoOpString(POLY_OPA_DISP++, "POLY_OPA_DISP 終了", 0); + gDPNoOpString(POLY_XLU_DISP++, "POLY_XLU_DISP 終了", 0); + gDPNoOpString(OVERLAY_DISP++, "OVERLAY_DISP 終了", 0); + + CLOSE_DISPS(gfxCtx, "../graph.c", 996); + + OPEN_DISPS(gfxCtx, "../graph.c", 999); + + gSPBranchList(WORK_DISP++, gfxCtx->polyOpaBuffer); + gSPBranchList(POLY_OPA_DISP++, gfxCtx->polyXluBuffer); + gSPBranchList(POLY_XLU_DISP++, gfxCtx->polyKalBuffer); + gSPBranchList(POLY_KAL_DISP++, gfxCtx->overlayBuffer); + gDPPipeSync(OVERLAY_DISP++); + gDPFullSync(OVERLAY_DISP++); + gSPEndDisplayList(OVERLAY_DISP++); + + CLOSE_DISPS(gfxCtx, "../graph.c", 1028); + + if (HREG(80) == 10 && HREG(93) == 2) { + HREG(80) = 7; + HREG(81) = -1; + HREG(83) = HREG(92); + } + + if (HREG(80) == 7 && HREG(81) != 0) { + if (HREG(82) == 3) { + Fault_AddClient(&sGraphUcodeFaultClient, Graph_UCodeFaultClient, gfxCtx->workBuffer, "do_count_fault"); + } + + Graph_DisassembleUCode(gfxCtx->workBuffer); + + if (HREG(82) == 3) { + Fault_RemoveClient(&sGraphUcodeFaultClient); + } + + if (HREG(81) < 0) { + LogUtils_LogHexDump((void*)&HW_REG(SP_MEM_ADDR_REG, u32), 0x20); + LogUtils_LogHexDump((void*)&DPC_START_REG, 0x20); + } + + if (HREG(81) < 0) { + HREG(81) = 0; + } + } + + problem = false; + + { + GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx & 1]; + + if (pool->headMagic != GFXPOOL_HEAD_MAGIC) { + //! @bug (?) : "problem = true;" may be missing + osSyncPrintf("%c", 7); + // "Dynamic area head is destroyed" + osSyncPrintf(VT_COL(RED, WHITE) "ダイナミック領域先頭が破壊されています\n" VT_RST); + Fault_AddHungupAndCrash("../graph.c", 1070); + } + if (pool->tailMagic != GFXPOOL_TAIL_MAGIC) { + problem = true; + osSyncPrintf("%c", 7); + // "Dynamic region tail is destroyed" + osSyncPrintf(VT_COL(RED, WHITE) "ダイナミック領域末尾が破壊されています\n" VT_RST); + Fault_AddHungupAndCrash("../graph.c", 1076); + } + } + + if (THGA_IsCrash(&gfxCtx->polyOpa)) { + problem = true; + osSyncPrintf("%c", 7); + // "Zelda 0 is dead" + osSyncPrintf(VT_COL(RED, WHITE) "ゼルダ0は死んでしまった(graph_alloc is empty)\n" VT_RST); + } + if (THGA_IsCrash(&gfxCtx->polyXlu)) { + problem = true; + osSyncPrintf("%c", 7); + // "Zelda 1 is dead" + osSyncPrintf(VT_COL(RED, WHITE) "ゼルダ1は死んでしまった(graph_alloc is empty)\n" VT_RST); + } + if (THGA_IsCrash(&gfxCtx->overlay)) { + problem = true; + osSyncPrintf("%c", 7); + // "Zelda 4 is dead" + osSyncPrintf(VT_COL(RED, WHITE) "ゼルダ4は死んでしまった(graph_alloc is empty)\n" VT_RST); + } + + if (!problem) { + Graph_TaskSet00(gfxCtx); + gfxCtx->gfxPoolIdx++; + gfxCtx->fbIdx++; + } + + func_800F3054(); + + { + OSTime time = osGetTime(); + s32 pad[4]; + + D_8016A538 = gRSPGFXTotalTime; + D_8016A530 = gRSPAudioTotalTime; + D_8016A540 = gRDPTotalTime; + gRSPGFXTotalTime = 0; + gRSPAudioTotalTime = 0; + gRDPTotalTime = 0; + + if (sGraphUpdateTime != 0) { + D_8016A548 = time - sGraphUpdateTime; + } + sGraphUpdateTime = time; + } + + if (CVar_GetS32("gDebugEnabled", 0)) + { + if (CHECK_BTN_ALL(gameState->input[0].press.button, BTN_Z) && + CHECK_BTN_ALL(gameState->input[0].cur.button, BTN_L | BTN_R)) { + gSaveContext.gameMode = 0; + SET_NEXT_GAMESTATE(gameState, Select_Init, SelectContext); + gameState->running = false; + } + } + + if (gIsCtrlr2Valid && PreNmiBuff_IsResetting(gAppNmiBufferPtr) && !gameState->unk_A0) { + // "To reset mode" + osSyncPrintf(VT_COL(YELLOW, BLACK) "PRE-NMIによりリセットモードに移行します\n" VT_RST); + SET_NEXT_GAMESTATE(gameState, PreNMI_Init, PreNMIContext); + gameState->running = false; + } +} + +uint64_t GetFrequency(); +uint64_t GetPerfCounter(); + +static struct RunFrameContext { + GraphicsContext gfxCtx; + GameState* gameState; + GameStateOverlay* nextOvl; + GameStateOverlay* ovl; + int state; +} runFrameContext; + + +extern AudioMgr gAudioMgr; + +static void RunFrame() +{ + u32 size; + char faultMsg[0x50]; + + switch (runFrameContext.state) { + case 0: + break; + case 1: + goto nextFrame; + } + + runFrameContext.nextOvl = &gGameStateOverlayTable[0]; + + osSyncPrintf("グラフィックスレッド実行開始\n"); // "Start graphic thread execution" + Graph_Init(&runFrameContext.gfxCtx); + + while (runFrameContext.nextOvl) + { + runFrameContext.ovl = runFrameContext.nextOvl; + Overlay_LoadGameState(runFrameContext.ovl); + + size = runFrameContext.ovl->instanceSize; + osSyncPrintf("クラスサイズ=%dバイト\n", size); // "Class size = %d bytes" + + runFrameContext.gameState = SystemArena_MallocDebug(size, "../graph.c", 1196); + + if (!runFrameContext.gameState) + { + osSyncPrintf("確保失敗\n"); // "Failure to secure" + + sprintf(faultMsg, "CLASS SIZE= %d bytes", size); + Fault_AddHungupAndCrashImpl("GAME CLASS MALLOC FAILED", faultMsg); + } + GameState_Init(runFrameContext.gameState, runFrameContext.ovl->init, &runFrameContext.gfxCtx); + + uint64_t freq = GetFrequency(); + + while (GameState_IsRunning(runFrameContext.gameState)) + { + uint64_t ticksA, ticksB; + ticksA = GetPerfCounter(); + + OTRSetFrameDivisor(R_UPDATE_RATE); + //OTRSetFrameDivisor(0); + + + //AudioMgr_ThreadEntry(&gAudioMgr); + // 528 and 544 relate to 60 fps at 32 kHz 32000/60 = 533.333.. + // in an ideal world, one third of the calls should use num_samples=544 and two thirds num_samples=528 + #define SAMPLES_HIGH 560 + #define SAMPLES_LOW 528 + // PAL values + //#define SAMPLES_HIGH 656 + //#define SAMPLES_LOW 624 + #define AUDIO_FRAMES_PER_UPDATE (R_UPDATE_RATE > 0 ? R_UPDATE_RATE : 1 ) + #define NUM_AUDIO_CHANNELS 2 + int samples_left = AudioPlayer_Buffered(); + u32 num_audio_samples = samples_left < AudioPlayer_GetDesiredBuffered() ? SAMPLES_HIGH : SAMPLES_LOW; + // printf("Audio samples: %d %u\n", samples_left, num_audio_samples); + + // 3 is the maximum authentic frame divisor. + s16 audio_buffer[SAMPLES_HIGH * NUM_AUDIO_CHANNELS * 3]; + for (int i = 0; i < AUDIO_FRAMES_PER_UPDATE; i++) { + AudioMgr_CreateNextAudioBuffer(audio_buffer + i * (num_audio_samples * NUM_AUDIO_CHANNELS), num_audio_samples); + } + //for (uint32_t i = 0; i < 2 * num_audio_samples; i++) { + // audio_buffer[i] = Rand_Next() & 0xFF; + //} + // printf("Audio samples before submitting: %d\n", audio_api->buffered()); + AudioPlayer_Play((u8*)audio_buffer, num_audio_samples * (sizeof(int16_t) * NUM_AUDIO_CHANNELS * AUDIO_FRAMES_PER_UPDATE)); + + + PadMgr_ThreadEntry(&gPadMgr); + + Graph_Update(&runFrameContext.gfxCtx, runFrameContext.gameState); + ticksB = GetPerfCounter(); + + Graph_ProcessGfxCommands(runFrameContext.gfxCtx.workBuffer); + + + //uint64_t diff = (ticksB - ticksA) / (freq / 1000); + //printf("Frame simulated in %ims\n", diff); + runFrameContext.state = 1; + return; + nextFrame:; + } + + runFrameContext.nextOvl = Graph_GetNextGameState(runFrameContext.gameState); + GameState_Destroy(runFrameContext.gameState); + SystemArena_FreeDebug(runFrameContext.gameState, "../graph.c", 1227); + Overlay_FreeGameState(runFrameContext.ovl); + } + Graph_Destroy(&runFrameContext.gfxCtx); + osSyncPrintf("グラフィックスレッド実行終了\n"); // "End of graphic thread execution" + + //Graph_Update(gfxCtxTest, gameStateTest); + exit(0); +} + +void Graph_ThreadEntry(void* arg0) { + + Graph_ProcessFrame(RunFrame); +} + +void* Graph_Alloc(GraphicsContext* gfxCtx, size_t size) { + TwoHeadGfxArena* thga = &gfxCtx->polyOpa; + + if (HREG(59) == 1) { + osSyncPrintf("graph_alloc siz=%d thga size=%08x bufp=%08x head=%08x tail=%08x\n", size, thga->size, thga->bufp, + thga->p, thga->d); + } + return THGA_AllocEnd(&gfxCtx->polyOpa, ALIGN16(size)); +} + +void* Graph_Alloc2(GraphicsContext* gfxCtx, size_t size) { + TwoHeadGfxArena* thga = &gfxCtx->polyOpa; + + if (HREG(59) == 1) { + osSyncPrintf("graph_alloc siz=%d thga size=%08x bufp=%08x head=%08x tail=%08x\n", size, thga->size, thga->bufp, + thga->p, thga->d); + } + return THGA_AllocEnd(&gfxCtx->polyOpa, ALIGN16(size)); +} + +void Graph_OpenDisps(Gfx** dispRefs, GraphicsContext* gfxCtx, const char* file, s32 line) { + if (HREG(80) == 7 && HREG(82) != 4) { + dispRefs[0] = gfxCtx->polyOpa.p; + dispRefs[1] = gfxCtx->polyXlu.p; + dispRefs[2] = gfxCtx->overlay.p; + + gDPNoOpOpenDisp(gfxCtx->polyOpa.p++, file, line); + gDPNoOpOpenDisp(gfxCtx->polyXlu.p++, file, line); + gDPNoOpOpenDisp(gfxCtx->overlay.p++, file, line); + } +} + +void Graph_CloseDisps(Gfx** dispRefs, GraphicsContext* gfxCtx, const char* file, s32 line) { + if (HREG(80) == 7 && HREG(82) != 4) { + if (dispRefs[0] + 1 == gfxCtx->polyOpa.p) { + gfxCtx->polyOpa.p = dispRefs[0]; + } else { + gDPNoOpCloseDisp(gfxCtx->polyOpa.p++, file, line); + } + + if (dispRefs[1] + 1 == gfxCtx->polyXlu.p) { + gfxCtx->polyXlu.p = dispRefs[1]; + } else { + gDPNoOpCloseDisp(gfxCtx->polyXlu.p++, file, line); + } + + if (dispRefs[2] + 1 == gfxCtx->overlay.p) { + gfxCtx->overlay.p = dispRefs[2]; + } else { + gDPNoOpCloseDisp(gfxCtx->overlay.p++, file, line); + } + } +} + +Gfx* Graph_GfxPlusOne(Gfx* gfx) { + return gfx + 1; +} + +Gfx* Graph_BranchDlist(Gfx* gfx, Gfx* dst) { + gSPBranchList(gfx, dst); + return dst; +} + +void* Graph_DlistAlloc(Gfx** gfx, size_t size) { + u8* ptr; + Gfx* dst; + + size = ((size + 7) & ~7), + + ptr = (u8*)(*gfx + 1); + + dst = (Gfx*)(ptr + size); + gSPBranchList(*gfx, dst); + + *gfx = dst; + return ptr; +} diff --git a/soh/src/code/irqmgr.c b/soh/src/code/irqmgr.c new file mode 100644 index 000000000..6e7118bff --- /dev/null +++ b/soh/src/code/irqmgr.c @@ -0,0 +1,232 @@ +#include "global.h" +#include "vt.h" + +vu32 gIrqMgrResetStatus = 0; +volatile OSTime sIrqMgrResetTime = 0; +volatile OSTime gIrqMgrRetraceTime = 0; +u32 sIrqMgrRetraceCount = 0; + +#define RETRACE_MSG 666 +#define PRE_NMI_MSG 669 +#define PRENMI450_MSG 671 +#define PRENMI480_MSG 672 +#define PRENMI500_MSG 673 + +#define STATUS_IDLE 0 +#define STATUS_PRENMI 1 +#define STATUS_NMI 2 + +void IrqMgr_AddClient(IrqMgr* this, IrqMgrClient* c, OSMesgQueue* msgQ) { + OSIntMask prevInt; + + LogUtils_CheckNullPointer("this", this, "../irqmgr.c", 96); + LogUtils_CheckNullPointer("c", c, "../irqmgr.c", 97); + LogUtils_CheckNullPointer("msgQ", msgQ, "../irqmgr.c", 98); + + prevInt = osSetIntMask(1); + + c->queue = msgQ; + c->prev = this->clients; + this->clients = c; + + osSetIntMask(prevInt); + + if (this->resetStatus > STATUS_IDLE) { + osSendMesg(c->queue, (OSMesg) & this->prenmiMsg, OS_MESG_NOBLOCK); + } + + if (this->resetStatus >= STATUS_NMI) { + osSendMesg(c->queue, (OSMesg) & this->nmiMsg, OS_MESG_NOBLOCK); + } +} + +void IrqMgr_RemoveClient(IrqMgr* this, IrqMgrClient* c) { + IrqMgrClient* iter = this->clients; + IrqMgrClient* lastIter = NULL; + OSIntMask prevInt; + + LogUtils_CheckNullPointer("this", this, "../irqmgr.c", 129); + LogUtils_CheckNullPointer("c", c, "../irqmgr.c", 130); + + prevInt = osSetIntMask(1); + + while (iter != NULL) { + if (iter == c) { + if (lastIter) { + lastIter->prev = c->prev; + } else { + this->clients = c->prev; + } + break; + } + lastIter = iter; + iter = iter->prev; + } + + osSetIntMask(prevInt); +} + +void IrqMgr_SendMesgForClient(IrqMgr* this, OSMesg msg) { + IrqMgrClient* iter = this->clients; + + while (iter != NULL) { + if (iter->queue->validCount >= iter->queue->msgCount) { + // "irqmgr_SendMesgForClient: Message queue is overflowing mq=%08x cnt=%d" + osSyncPrintf( + VT_COL(RED, WHITE) "irqmgr_SendMesgForClient:メッセージキューがあふれています mq=%08x cnt=%d\n" VT_RST, + iter->queue, iter->queue->validCount); + } else { + osSendMesg(iter->queue, msg, OS_MESG_NOBLOCK); + } + + iter = iter->prev; + } +} + +void IrqMgr_JamMesgForClient(IrqMgr* this, OSMesg msg) { + IrqMgrClient* iter = this->clients; + + while (iter != NULL) { + if (iter->queue->validCount >= iter->queue->msgCount) { + // "irqmgr_JamMesgForClient: Message queue is overflowing mq=%08x cnt=%d" + osSyncPrintf( + VT_COL(RED, WHITE) "irqmgr_JamMesgForClient:メッセージキューがあふれています mq=%08x cnt=%d\n" VT_RST, + iter->queue, iter->queue->validCount); + } else { + // mistake? the function's name suggests it would use osJamMesg + osSendMesg(iter->queue, msg, OS_MESG_NOBLOCK); + } + iter = iter->prev; + } +} + +void IrqMgr_HandlePreNMI(IrqMgr* this) { + u64 temp = STATUS_PRENMI; // required to match + + gIrqMgrResetStatus = temp; + this->resetStatus = STATUS_PRENMI; + sIrqMgrResetTime = this->resetTime = osGetTime(); + + osSetTimer(&this->timer, OS_USEC_TO_CYCLES(450000), 0ull, &this->queue, (OSMesg)PRENMI450_MSG); + IrqMgr_JamMesgForClient(this, (OSMesg) & this->prenmiMsg); +} + +void IrqMgr_CheckStack() { + osSyncPrintf("irqmgr.c: PRENMIから0.5秒経過\n"); // "0.5 seconds after PRENMI" + if (StackCheck_Check(NULL) == 0) { + osSyncPrintf("スタックは大丈夫みたいです\n"); // "The stack looks ok" + } else { + osSyncPrintf("%c", 7); + osSyncPrintf(VT_FGCOL(RED)); + // "Stack overflow or dangerous" + osSyncPrintf("スタックがオーバーフローしたか危険な状態です\n"); + // "Increase stack size early or don't consume stack" + osSyncPrintf("早々にスタックサイズを増やすか、スタックを消費しないようにしてください\n"); + osSyncPrintf(VT_RST); + } +} + +void IrqMgr_HandlePRENMI450(IrqMgr* this) { + u64 temp = STATUS_NMI; // required to match + gIrqMgrResetStatus = temp; + this->resetStatus = STATUS_NMI; + + osSetTimer(&this->timer, OS_USEC_TO_CYCLES(30000), 0ull, &this->queue, (OSMesg)PRENMI480_MSG); + IrqMgr_SendMesgForClient(this, (OSMesg) & this->nmiMsg); +} + +void IrqMgr_HandlePRENMI480(IrqMgr* this) { + u32 ret; + + osSetTimer(&this->timer, OS_USEC_TO_CYCLES(20000), 0ull, &this->queue, (OSMesg)PRENMI500_MSG); + ret = osAfterPreNMI(); + if (ret) { + // "osAfterPreNMI returned %d !?" + osSyncPrintf("osAfterPreNMIが %d を返しました!?\n", ret); + osSetTimer(&this->timer, OS_USEC_TO_CYCLES(1000), 0ull, &this->queue, (OSMesg)PRENMI480_MSG); + } +} + +void IrqMgr_HandlePRENMI500(IrqMgr* this) { + IrqMgr_CheckStack(); +} + +void IrqMgr_HandleRetrace(IrqMgr* this) { + if (gIrqMgrRetraceTime == 0ull) { + if (this->retraceTime == 0) { + this->retraceTime = osGetTime(); + } else { + gIrqMgrRetraceTime = osGetTime() - this->retraceTime; + } + } + sIrqMgrRetraceCount++; + IrqMgr_SendMesgForClient(this, (OSMesg) & this->retraceMsg); +} + +void IrqMgr_ThreadEntry(void* arg0) { + OSMesg msg; + IrqMgr* this = (IrqMgr*)arg0; + u8 exit; + + msg = 0; + osSyncPrintf("IRQマネージャスレッド実行開始\n"); // "Start IRQ manager thread execution" + exit = false; + + while (!exit) { + osRecvMesg(&this->queue, &msg, OS_MESG_BLOCK); + switch ((u32)msg) { + case RETRACE_MSG: + IrqMgr_HandleRetrace(this); + break; + case PRE_NMI_MSG: + osSyncPrintf("PRE_NMI_MSG\n"); + // "Scheduler: Receives PRE_NMI message" + osSyncPrintf("スケジューラ:PRE_NMIメッセージを受信\n"); + IrqMgr_HandlePreNMI(this); + break; + case PRENMI450_MSG: + osSyncPrintf("PRENMI450_MSG\n"); + // "Scheduler: Receives PRENMI450 message" + osSyncPrintf("スケジューラ:PRENMI450メッセージを受信\n"); + IrqMgr_HandlePRENMI450(this); + break; + case PRENMI480_MSG: + osSyncPrintf("PRENMI480_MSG\n"); + // "Scheduler: Receives PRENMI480 message" + osSyncPrintf("スケジューラ:PRENMI480メッセージを受信\n"); + IrqMgr_HandlePRENMI480(this); + break; + case PRENMI500_MSG: + osSyncPrintf("PRENMI500_MSG\n"); + // "Scheduler: Receives PRENMI500 message" + osSyncPrintf("スケジューラ:PRENMI500メッセージを受信\n"); + exit = true; + IrqMgr_HandlePRENMI500(this); + break; + default: + // "Unexpected message received" + osSyncPrintf("irqmgr.c:予期しないメッセージを受け取りました(%08x)\n", msg); + break; + } + } + + osSyncPrintf("IRQマネージャスレッド実行終了\n"); // "End of IRQ manager thread execution" +} + +void IrqMgr_Init(IrqMgr* this, void* stack, OSPri pri, u8 retraceCount) { + LogUtils_CheckNullPointer("this", this, "../irqmgr.c", 346); + LogUtils_CheckNullPointer("stack", stack, "../irqmgr.c", 347); + + this->clients = NULL; + this->retraceMsg.type = OS_SC_RETRACE_MSG; + this->prenmiMsg.type = OS_SC_PRE_NMI_MSG; + this->nmiMsg.type = OS_SC_NMI_MSG; + this->resetStatus = STATUS_IDLE; + this->resetTime = 0; + + osCreateMesgQueue(&this->queue, this->msgBuf, ARRAY_COUNT(this->msgBuf)); + osSetEventMesg(OS_EVENT_PRENMI, &this->queue, (OSMesg)PRE_NMI_MSG); + osViSetEvent(&this->queue, (OSMesg)RETRACE_MSG, retraceCount); + osCreateThread(&this->thread, 0x13, IrqMgr_ThreadEntry, this, stack, pri); + osStartThread(&this->thread); +} diff --git a/soh/src/code/jpegdecoder.c b/soh/src/code/jpegdecoder.c new file mode 100644 index 000000000..5261264c7 --- /dev/null +++ b/soh/src/code/jpegdecoder.c @@ -0,0 +1,192 @@ +#include "global.h" + +u8* sJpegBitStreamPtr; +u32 sJpegBitStreamByteIdx; +u8 sJpegBitStreamBitIdx; +u8 sJpegBitStreamDontSkip; +u32 sJpegBitStreamCurWord; + +s32 JpegDecoder_Decode(JpegDecoder* decoder, u16* mcuBuff, s32 count, u8 isFollowing, JpegDecoderState* state) { + s16 pad; + s16 unk0; + s16 unk1; + s16 unk2; + u32 idx; + s32 inc; + u16 unkCount; + + JpegHuffmanTable* hTable0; + JpegHuffmanTable* hTable1; + JpegHuffmanTable* hTable2; + JpegHuffmanTable* hTable3; + + inc = 0; + sJpegBitStreamPtr = decoder->imageData; + if (decoder->mode == 0) { + unkCount = 2; + } else { + unkCount = 4; + if (decoder->unk_05 == 1) { + inc = 8 * 8 * 2; + } + } + + hTable0 = decoder->hTablePtrs[0]; + hTable1 = decoder->hTablePtrs[1]; + hTable2 = decoder->hTablePtrs[2]; + hTable3 = decoder->hTablePtrs[3]; + + if (!isFollowing) { + sJpegBitStreamByteIdx = 0; + sJpegBitStreamBitIdx = 32; + sJpegBitStreamCurWord = 0; + sJpegBitStreamDontSkip = 0; + unk0 = 0; + unk1 = 0; + unk2 = 0; + } else { + sJpegBitStreamByteIdx = state->byteIdx; + sJpegBitStreamBitIdx = state->bitIdx; + sJpegBitStreamCurWord = state->curWord; + sJpegBitStreamDontSkip = state->dontSkip; + unk0 = state->unk_0C; + unk1 = state->unk_0E; + unk2 = state->unk_10; + } + + while (count != 0) { + for (idx = 0; idx < unkCount; idx++) { + if (JpegDecoder_ProcessMcu(hTable0, hTable1, mcuBuff, &unk0)) { + return 2; + } + mcuBuff += 8 * 8; + } + + if (JpegDecoder_ProcessMcu(hTable2, hTable3, mcuBuff, &unk1)) { + return 2; + } + mcuBuff += 8 * 8; + + if (JpegDecoder_ProcessMcu(hTable2, hTable3, mcuBuff, &unk2)) { + return 2; + } + + count--; + mcuBuff += 8 * 8; + mcuBuff += inc; + } + + state->byteIdx = sJpegBitStreamByteIdx; + state->bitIdx = sJpegBitStreamBitIdx; + state->curWord = sJpegBitStreamCurWord; + state->dontSkip = sJpegBitStreamDontSkip; + state->unk_0C = unk0; + state->unk_0E = unk1; + state->unk_10 = unk2; + return 0; +} + +s32 JpegDecoder_ProcessMcu(JpegHuffmanTable* hTable0, JpegHuffmanTable* hTable1, u16* mcu, s16* unk) { + s8 i = 0; + s8 zeroCount; + s16 coeff; + + if (JpegDecoder_ParseNextSymbol(hTable0, &coeff, &zeroCount)) { + return 1; + } + + *unk += coeff; + mcu[i++] = *unk; + while (i < 8 * 8) { + if (JpegDecoder_ParseNextSymbol(hTable1, &coeff, &zeroCount) != 0) { + return 1; + } + + if (coeff == 0) { + if (zeroCount == 0xF) { + while (zeroCount-- >= 0) { + mcu[i++] = 0; + } + } else { + while (i < 8 * 8) { + mcu[i++] = 0; + } + break; + } + } else { + while (0 < zeroCount--) { + mcu[i++] = 0; + } + mcu[i++] = coeff; + } + } + + return 0; +} + +s32 JpegDecoder_ParseNextSymbol(JpegHuffmanTable* hTable, s16* outCoeff, s8* outZeroCount) { + u8 codeIdx; + u8 sym; + u16 codeOff = 0; + u16 buff = JpegDecoder_ReadBits(16); + + for (codeIdx = 0; codeIdx < 16; codeIdx++) { + if (hTable->codesB[codeIdx] == 0xFFFF) { + continue; + } + + codeOff = buff >> (15 - codeIdx); + if (codeOff <= hTable->codesB[codeIdx]) { + break; + } + } + + if (codeIdx >= 16) { + return 1; + } + + sym = hTable->symbols[hTable->codeOffs[codeIdx] + codeOff - hTable->codesA[codeIdx]]; + *outZeroCount = sym >> 4; + sym &= 0xF; + + sJpegBitStreamBitIdx += codeIdx - 15; + *outCoeff = 0; + if (sym) { + *outCoeff = JpegDecoder_ReadBits(sym); + if (*outCoeff < (1 << (sym - 1))) { + *outCoeff += (-1 << sym) + 1; + } + } + + return 0; +} + +u16 JpegDecoder_ReadBits(u8 len) { + u8 byteCount; + u8 data; + s32 ret; + u32 temp; + + ret = 0; // this is required for some reason + + for (byteCount = sJpegBitStreamBitIdx >> 3; byteCount > 0; byteCount--) { + data = sJpegBitStreamPtr[sJpegBitStreamByteIdx++]; + if (sJpegBitStreamDontSkip) { + if (data == 0) { + data = sJpegBitStreamPtr[sJpegBitStreamByteIdx++]; + } + } + + sJpegBitStreamDontSkip = (data == 0xFF) ? 1 : 0; + + sJpegBitStreamCurWord <<= 8; + sJpegBitStreamCurWord |= data; + sJpegBitStreamBitIdx -= 8; + } + + ret = (sJpegBitStreamCurWord << (sJpegBitStreamBitIdx)); + temp = ret; + ret = temp >> -len; + sJpegBitStreamBitIdx += len; + return ret; +} diff --git a/soh/src/code/jpegutils.c b/soh/src/code/jpegutils.c new file mode 100644 index 000000000..00c040609 --- /dev/null +++ b/soh/src/code/jpegutils.c @@ -0,0 +1,153 @@ +#include "global.h" + +void JpegUtils_ProcessQuantizationTable(u8* dqt, JpegQuantizationTable* qt, u8 count) { + u8 i; + + for (i = 0; i < count; i++) { + u8 j; + + dqt++; + for (j = 0; j < 64; j++) { + qt[i].table[j] = *dqt++; + } + } +} + +s32 JpegUtils_ParseHuffmanCodesLengths(u8* ptr, u8* codesLengths) { + u8 off = 1; + s16 count = 0; + s16 idx = 1; + + while (off < 0x11) { + while (idx <= ptr[off - 1]) { + codesLengths[count++] = off; + idx++; + } + idx = 1; + off++; + } + + codesLengths[count] = 0; + return count; +} + +s32 JpegUtils_GetHuffmanCodes(u8* codesLengths, u16* codes) { + s16 idx = 0; + u16 code = 0; + u8 lastLen = codesLengths[0]; + + while (true) { + while (true) { + codes[idx++] = code++; + + if (codesLengths[idx] != lastLen) { + break; + } + } + + if (codesLengths[idx] == 0) { + break; + } + + while (true) { + if (code <<= 1, codesLengths[idx] == ++lastLen) { + break; + } + } + } + + return idx; +} + +s32 JpegUtils_SetHuffmanTable(u8* data, JpegHuffmanTable* ht, u16* codes) { + u8 idx; + u16 codeOff = 0; + + for (idx = 0; idx < 0x10; idx++) { + if (data[idx]) { + ht->codeOffs[idx] = codeOff; + ht->codesA[idx] = codes[codeOff]; + codeOff += data[idx] - 1; + ht->codesB[idx] = codes[codeOff]; + codeOff++; + } else { + ht->codesB[idx] = 0xFFFF; + } + } + + return codeOff; +} + +u32 JpegUtils_ProcessHuffmanTableImpl(u8* data, JpegHuffmanTable* ht, u8* codesLengths, u16* codes, u8 isAc) { + s16 ret; + s32 count = JpegUtils_ParseHuffmanCodesLengths(data, codesLengths); + s32 temp; + + ret = count; + if (count == 0 || (isAc && count > 0x100) || (!isAc && count > 0x10)) { + return 0; + } + if (ret != JpegUtils_GetHuffmanCodes(codesLengths, codes)) { + return 0; + } + if (temp = JpegUtils_SetHuffmanTable(data, ht, codes), temp != ret) { + return 0; + } + + return ret; +} + +u32 JpegUtils_ProcessHuffmanTable(u8* dht, JpegHuffmanTable* ht, u8* codesLengths, u16* codes, u8 count) { + u8 idx; + u32 codeCount; + + for (idx = 0; idx < count; idx++) { + u32 ac = (*dht++ >> 4); + + codeCount = JpegUtils_ProcessHuffmanTableImpl(dht, &ht[idx], codesLengths, codes, ac); + if (codeCount == 0) { + return 1; + } + + dht += 0x10; + ht[idx].symbols = dht; + dht += codeCount; + } + return 0; +} + +void JpegUtils_SetHuffmanTableOld(u8* data, JpegHuffmanTableOld* ht, u8* codesLengths, u16* codes, s16 count, u8 isAc) { + s16 idx; + u8 a; + + for (idx = 0; idx < count; idx++) { + a = data[idx]; + if (isAc) { + ht->acCodes[a] = codes[idx]; + ht->codeOffs[a] = codesLengths[idx]; + } else { + ht->dcCodes[a] = codes[idx]; + ht->codeOffs[a] = codesLengths[idx]; + } + } +} + +u32 JpegUtils_ProcessHuffmanTableImplOld(u8* dht, JpegHuffmanTableOld* ht, u8* codesLengths, u16* codes) { + u8 isAc = *dht++ >> 4; + s16 count2; + s32 count; + + count2 = count = JpegUtils_ParseHuffmanCodesLengths(dht, codesLengths); + + if (count == 0 || (isAc && count > 0x100) || (!isAc && count > 0x10)) { + return 1; + } + + if (JpegUtils_GetHuffmanCodes(codesLengths, codes) != count2) { + return 1; + } + + JpegUtils_SetHuffmanTableOld(dht + 0x10, ht, codesLengths, codes, count2, isAc); + + return 0; +} diff --git a/soh/src/code/listalloc.c b/soh/src/code/listalloc.c new file mode 100644 index 000000000..18d43328d --- /dev/null +++ b/soh/src/code/listalloc.c @@ -0,0 +1,62 @@ +#include "global.h" + +ListAlloc* ListAlloc_Init(ListAlloc* this) { + this->prev = NULL; + this->next = NULL; + return this; +} + +void* ListAlloc_Alloc(ListAlloc* this, size_t size) { + ListAlloc* ptr = SystemArena_MallocDebug(size + sizeof(ListAlloc), "../listalloc.c", 40); + ListAlloc* next; + + if (ptr == NULL) { + return NULL; + } + + next = this->next; + if (next != NULL) { + next->next = ptr; + } + + ptr->prev = next; + ptr->next = NULL; + this->next = ptr; + + if (this->prev == NULL) { + this->prev = ptr; + } + + return (u8*)ptr + sizeof(ListAlloc); +} + +void ListAlloc_Free(ListAlloc* this, void* data) { + ListAlloc* ptr = &((ListAlloc*)data)[-1]; + + if (ptr->prev != NULL) { + ptr->prev->next = ptr->next; + } + + if (ptr->next != NULL) { + ptr->next->prev = ptr->prev; + } + + if (this->prev == ptr) { + this->prev = ptr->next; + } + + if (this->next == ptr) { + this->next = ptr->prev; + } + + SystemArena_FreeDebug(ptr, "../listalloc.c", 72); +} + +void ListAlloc_FreeAll(ListAlloc* this) { + ListAlloc* iter = this->prev; + + while (iter != NULL) { + ListAlloc_Free(this, (u8*)iter + sizeof(ListAlloc)); + iter = this->prev; + } +} diff --git a/soh/src/code/loadfragment2.c b/soh/src/code/loadfragment2.c new file mode 100644 index 000000000..dd413ca49 --- /dev/null +++ b/soh/src/code/loadfragment2.c @@ -0,0 +1,16 @@ +#include "global.h" + +void* Overlay_AllocateAndLoad(uintptr_t vRomStart, uintptr_t vRomEnd, void* vRamStart, void* vRamEnd) { + void* allocatedVRamAddr = SystemArena_MallocRDebug((intptr_t)vRamEnd - (intptr_t)vRamStart, "../loadfragment2.c", 31); + + if (gOverlayLogSeverity >= 3) { + osSyncPrintf("OVL:SPEC(%08x-%08x) REAL(%08x-%08x) OFFSET(%08x)\n", vRamStart, vRamEnd, allocatedVRamAddr, + ((uintptr_t)vRamEnd - (uintptr_t)vRamStart) + (uintptr_t)allocatedVRamAddr, (uintptr_t)vRamStart - (uintptr_t)allocatedVRamAddr); + } + + if (allocatedVRamAddr != NULL) { + Overlay_Load(vRomStart, vRomEnd, vRamStart, vRamEnd, allocatedVRamAddr); + } + + return allocatedVRamAddr; +} diff --git a/soh/src/code/logseverity.c b/soh/src/code/logseverity.c new file mode 100644 index 000000000..940aa87cc --- /dev/null +++ b/soh/src/code/logseverity.c @@ -0,0 +1,3 @@ +#include "global.h" + +s32 gOverlayLogSeverity = 2; diff --git a/soh/src/code/main.c b/soh/src/code/main.c new file mode 100644 index 000000000..eaaa7b388 --- /dev/null +++ b/soh/src/code/main.c @@ -0,0 +1,134 @@ +#include "global.h" +#include "vt.h" +#include +#include "soh/OTRGlobals.h" + + +s32 gScreenWidth = SCREEN_WIDTH; +s32 gScreenHeight = SCREEN_HEIGHT; +u32 gSystemHeapSize = 0; + +PreNmiBuff* gAppNmiBufferPtr; +SchedContext gSchedContext; +PadMgr gPadMgr; +IrqMgr gIrqMgr; +uintptr_t gSegments[NUM_SEGMENTS]; +OSThread sGraphThread; +u8 sGraphStack[0x1800]; +u8 sSchedStack[0x600]; +u8 sAudioStack[0x800]; +u8 sPadMgrStack[0x500]; +u8 sIrqMgrStack[0x500]; +StackEntry sGraphStackInfo; +StackEntry sSchedStackInfo; +StackEntry sAudioStackInfo; +StackEntry sPadMgrStackInfo; +StackEntry sIrqMgrStackInfo; +AudioMgr gAudioMgr; +OSMesgQueue sSiIntMsgQ; +OSMesg sSiIntMsgBuf[1]; + +void Main_LogSystemHeap(void) { + osSyncPrintf(VT_FGCOL(GREEN)); + // "System heap size% 08x (% dKB) Start address% 08x" + osSyncPrintf("システムヒープサイズ %08x(%dKB) 開始アドレス %08x\n", gSystemHeapSize, gSystemHeapSize / 1024, + gSystemHeap); + osSyncPrintf(VT_RST); +} + +void main(int argc, char** argv) +{ + GameConsole_Init(); + InitOTR(); + BootCommands_Init(); + + BootCommands_ParseBootArgs(argc - 1, (char**)&argv[1]); + Main(0); +} + +void Main(void* arg) { + IrqMgrClient irqClient; + OSMesgQueue irqMgrMsgQ; + OSMesg irqMgrMsgBuf[60]; + uintptr_t sysHeap; + uintptr_t fb; + void* debugHeap; + s32 debugHeapSize; + s16* msg; + + osSyncPrintf("mainproc 実行開始\n"); // "Start running" + gScreenWidth = SCREEN_WIDTH; + gScreenHeight = SCREEN_HEIGHT; + gAppNmiBufferPtr = (PreNmiBuff*)osAppNmiBuffer; + PreNmiBuff_Init(gAppNmiBufferPtr); + Fault_Init(); + SysCfb_Init(0); + sysHeap = gSystemHeap; + fb = SysCfb_GetFbPtr(0); + gSystemHeapSize = 1024 * 1024 * 4; + // "System heap initalization" + osSyncPrintf("システムヒープ初期化 %08x-%08x %08x\n", sysHeap, fb, gSystemHeapSize); + SystemHeap_Init(sysHeap, gSystemHeapSize); // initializes the system heap + if (osMemSize >= 0x800000) { + debugHeap = SysCfb_GetFbEnd(); + debugHeapSize = (0x80600000 - (uintptr_t)debugHeap); + } else { + debugHeapSize = 0x400; + debugHeap = SystemArena_MallocDebug(debugHeapSize, "../main.c", 565); + } + + debugHeapSize = 1024 * 64; + + osSyncPrintf("debug_InitArena(%08x, %08x)\n", debugHeap, debugHeapSize); + DebugArena_Init(debugHeap, debugHeapSize); + func_800636C0(); + + R_ENABLE_ARENA_DBG = 0; + + osCreateMesgQueue(&sSiIntMsgQ, sSiIntMsgBuf, 1); + osSetEventMesg(5, &sSiIntMsgQ, 0); + + Main_LogSystemHeap(); + + osCreateMesgQueue(&irqMgrMsgQ, irqMgrMsgBuf, 0x3C); + StackCheck_Init(&sIrqMgrStackInfo, sIrqMgrStack, sIrqMgrStack + sizeof(sIrqMgrStack), 0, 0x100, "irqmgr"); + IrqMgr_Init(&gIrqMgr, &sGraphStackInfo, Z_PRIORITY_IRQMGR, 1); + + osSyncPrintf("タスクスケジューラの初期化\n"); // "Initialize the task scheduler" + StackCheck_Init(&sSchedStackInfo, sSchedStack, sSchedStack + sizeof(sSchedStack), 0, 0x100, "sched"); + Sched_Init(&gSchedContext, &sAudioStack, Z_PRIORITY_SCHED, D_80013960, 1, &gIrqMgr); + + IrqMgr_AddClient(&gIrqMgr, &irqClient, &irqMgrMsgQ); + + StackCheck_Init(&sAudioStackInfo, sAudioStack, sAudioStack + sizeof(sAudioStack), 0, 0x100, "audio"); + AudioMgr_Init(&gAudioMgr, sAudioStack + sizeof(sAudioStack), Z_PRIORITY_AUDIOMGR, 0xA, &gSchedContext, &gIrqMgr); + + StackCheck_Init(&sPadMgrStackInfo, sPadMgrStack, sPadMgrStack + sizeof(sPadMgrStack), 0, 0x100, "padmgr"); + PadMgr_Init(&gPadMgr, &sSiIntMsgQ, &gIrqMgr, 7, Z_PRIORITY_PADMGR, &sIrqMgrStack); + + AudioMgr_Unlock(&gAudioMgr); + + StackCheck_Init(&sGraphStackInfo, sGraphStack, sGraphStack + sizeof(sGraphStack), 0, 0x100, "graph"); + osCreateThread(&sGraphThread, 4, Graph_ThreadEntry, arg, sGraphStack + sizeof(sGraphStack), Z_PRIORITY_GRAPH); + osStartThread(&sGraphThread); + osSetThreadPri(0, Z_PRIORITY_SCHED); + + Graph_ThreadEntry(0); + + while (true) { + msg = NULL; + osRecvMesg(&irqMgrMsgQ, (OSMesg)&msg, OS_MESG_BLOCK); + if (msg == NULL) { + break; + } + if (*msg == OS_SC_PRE_NMI_MSG) { + osSyncPrintf("main.c: リセットされたみたいだよ\n"); // "Looks like it's been reset" + PreNmiBuff_SetReset(gAppNmiBufferPtr); + } + } + + osSyncPrintf("mainproc 後始末\n"); // "Cleanup" + osDestroyThread(&sGraphThread); + func_800FBFD8(); + osSyncPrintf("mainproc 実行終了\n"); // "End of execution" +} diff --git a/soh/src/code/mempak.c b/soh/src/code/mempak.c new file mode 100644 index 000000000..1b9d95ee8 --- /dev/null +++ b/soh/src/code/mempak.c @@ -0,0 +1,187 @@ +#include "global.h" + +OSPfs sMempakPfsHandle; +s32 sMempakFreeBytes; +s32 sMempakFiles[10]; + +u16 sMempakCompanyCode = 1; +u32 sMempakGameCode = 1; + +// "ZELDA DEMO TOOL " +u8 sMempakGameName[0x10] = { 0x33, 0x1E, 0x25, 0x1D, 0x1A, 0x0F, 0x1D, 0x1E, + 0x26, 0x28, 0x0F, 0x2D, 0x28, 0x28, 0x25, 0x0F }; +u8 sMempakExtName[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +s32 Mempak_Init(s32 controllerNb) { + OSMesgQueue* mq; + s32 pad; + s32 ret = false; + + mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + + if (!osPfsInitPak(mq, &sMempakPfsHandle, controllerNb)) { + ret = true; + } + + osPfsFreeBlocks(&sMempakPfsHandle, &sMempakFreeBytes); + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + + return ret; +} + +s32 Mempak_GetFreeBytes(s32 controllerNb) { + return sMempakFreeBytes; +} + +s32 Mempak_FindFile(s32 controllerNb, char start, char end) { + OSMesgQueue* mq; + s32 error; + char idx; + u32 bit = 1; + s32 flag = 0; + + mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + + for (idx = start; idx <= end; idx++) { + sMempakExtName[0] = idx - 0x27; + + error = osPfsFindFile(&sMempakPfsHandle, sMempakCompanyCode, sMempakGameCode, sMempakGameName, sMempakExtName, + &sMempakFiles[idx - 'A']); + if (error == 0) { + flag |= bit; + } else { + sMempakFiles[idx - 'A'] = -1; + } + + bit <<= 1; + osSyncPrintf("mempak: find '%c' (%d)\n", idx, error); + } + + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + osSyncPrintf("mempak: find '%c' - '%c' %02x\n", start, end, flag); + + return flag; +} + +s32 Mempak_Write(s32 controllerNb, char idx, void* buffer, s32 offset, ptrdiff_t size) { + OSMesgQueue* mq; + s32 error; + s32 ret = false; + s32 pad; + + mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + + if (size < sMempakFreeBytes) { + error = osPfsReadWriteFile(&sMempakPfsHandle, sMempakFiles[idx - 'A'], 1, offset, size, buffer); + if (error == 0) { + ret = true; + } + osSyncPrintf("mempak: write %d byte '%c' (%d)->%d\n", size, idx, sMempakFiles[idx - 'A'], error); + } + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + + return ret; +} + +s32 Mempak_Read(s32 controllerNb, char idx, void* buffer, s32 offset, ptrdiff_t size) { + OSMesgQueue* mq; + s32 error; + s32 ret = false; + s32 pad; + + mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + + if (size < sMempakFreeBytes) { + error = osPfsReadWriteFile(&sMempakPfsHandle, sMempakFiles[idx - 'A'], 0, offset, size, buffer); + if (error == 0) { + ret = true; + } + osSyncPrintf("mempak: read %d byte '%c' (%d)<-%d\n", size, idx, sMempakFiles[idx - 'A'], error); + } + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + return ret; +} + +s32 Mempak_Alloc(s32 controllerNb, char* idx, ptrdiff_t size) { + OSMesgQueue* mq; + s32 error; + s32 ret = 0; + s32 i; + s32 pad; + + mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + + if (*idx >= 'A' && *idx < 'L') { + sMempakExtName[0] = *idx - 0x27; + if (-1 == sMempakFiles[*idx - 'A']) { + error = osPfsAllocateFile(&sMempakPfsHandle, sMempakCompanyCode, sMempakGameCode, sMempakGameName, + sMempakExtName, size, &sMempakFiles[*idx - 'A']); + if (error == 0) { + ret = 1; + } + osSyncPrintf("mempak: alloc %d byte '%c' (%d)\n", size, *idx, error); + } else { + sMempakExtName[0] = *idx - 0x27; + if (osPfsDeleteFile(&sMempakPfsHandle, sMempakCompanyCode, sMempakGameCode, sMempakGameName, + sMempakExtName) == 0) { + ret = 1; + } + error = osPfsAllocateFile(&sMempakPfsHandle, sMempakCompanyCode, sMempakGameCode, sMempakGameName, + sMempakExtName, size, &sMempakFiles[*idx - 'A']); + if (error == 0) { + ret |= 1; + } + osSyncPrintf("mempak: resize %d byte '%c' (%d)\n", size, *idx, error); + } + } else { + for (i = 0; i < ARRAY_COUNT(sMempakFiles); i++) { + if (sMempakFiles[i] == -1) { + break; + } + } + + *idx = i + 'A'; + sMempakExtName[0] = *idx - 0x27; + error = osPfsAllocateFile(&sMempakPfsHandle, sMempakCompanyCode, sMempakGameCode, sMempakGameName, + sMempakExtName, size, &sMempakFiles[i]); + osSyncPrintf("mempak: alloc %d byte '%c' (%d) with search\n", size, *idx, error); + if (error == 0) { + ret = 1; + } + } + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + + return ret; +} + +s32 Mempak_DeleteFile(s32 controllerNb, char idx) { + OSMesgQueue* mq; + s32 error; + s32 ret = false; + + mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + + sMempakExtName[0] = idx - 0x27; + error = osPfsDeleteFile(&sMempakPfsHandle, sMempakCompanyCode, sMempakGameCode, sMempakGameName, sMempakExtName); + if (error == 0) { + ret = true; + } + osSyncPrintf("mempak: delete '%c' (%d)\n", idx, error); + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + + return ret; +} + +s32 Mempak_GetFileSize(s32 controllerNb, char idx) { + OSMesgQueue* mq = PadMgr_LockSerialMesgQueue(&gPadMgr); + OSPfsState state; + s32 error = osPfsFileState(&sMempakPfsHandle, sMempakFiles[idx - 'A'], &state); + s32 pad; + + PadMgr_UnlockSerialMesgQueue(&gPadMgr, mq); + + if (error != 0) { + return 0; + } + return state.file_size; +} diff --git a/soh/src/code/mtxuty-cvt.c b/soh/src/code/mtxuty-cvt.c new file mode 100644 index 000000000..18c7a36f3 --- /dev/null +++ b/soh/src/code/mtxuty-cvt.c @@ -0,0 +1,24 @@ +#include "global.h" + +void MtxConv_F2L(Mtx* m1, MtxF* m2) { + s32 i; + s32 j; + + LogUtils_CheckNullPointer("m1", m1, "../mtxuty-cvt.c", 31); + LogUtils_CheckNullPointer("m2", m2, "../mtxuty-cvt.c", 32); + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + s32 value = (m2->mf[i][j] * 0x10000); + + m1->intPart[i][j] = value >> 16; + m1->fracPart[i][j] = value; + } + } +} + +void MtxConv_L2F(MtxF* m1, Mtx* m2) { + LogUtils_CheckNullPointer("m1", m1, "../mtxuty-cvt.c", 55); + LogUtils_CheckNullPointer("m2", m2, "../mtxuty-cvt.c", 56); + guMtxL2F(m1, m2); +} diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c new file mode 100644 index 000000000..fb4ceb8e6 --- /dev/null +++ b/soh/src/code/padmgr.c @@ -0,0 +1,441 @@ +#include "global.h" +#include "vt.h" + +//#include + +extern void* __cdecl memset(_Out_writes_bytes_all_(_Size) void* _Dst, _In_ int _Val, _In_ size_t _Size); + +s32 D_8012D280 = 1; + +static ControllerCallback controllerCallback; + +OSMesgQueue* PadMgr_LockSerialMesgQueue(PadMgr* padMgr) { + OSMesgQueue* ctrlrQ = NULL; + + if (D_8012D280 > 2) { + // "serialMsgQ Waiting for lock" + osSyncPrintf("%2d %d serialMsgQロック待ち %08x %08x %08x\n", osGetThreadId(NULL), + padMgr->serialMsgQ.validCount, padMgr, &padMgr->serialMsgQ, &ctrlrQ); + } + + osRecvMesg(&padMgr->serialMsgQ, (OSMesg)&ctrlrQ, OS_MESG_BLOCK); + + if (D_8012D280 > 2) { + // "serialMsgQ Locked" + osSyncPrintf("%2d %d serialMsgQをロックしました %08x\n", osGetThreadId(NULL), + padMgr->serialMsgQ.validCount, ctrlrQ); + } + + return ctrlrQ; +} + +void PadMgr_UnlockSerialMesgQueue(PadMgr* padMgr, OSMesgQueue* ctrlrQ) { + if (D_8012D280 > 2) { + // "serialMsgQ Unlock" + osSyncPrintf("%2d %d serialMsgQロック解除します %08x %08x %08x\n", osGetThreadId(NULL), + padMgr->serialMsgQ.validCount, padMgr, &padMgr->serialMsgQ, ctrlrQ); + } + + osSendMesg(&padMgr->serialMsgQ, ctrlrQ, OS_MESG_BLOCK); + + if (D_8012D280 > 2) { + // "serialMsgQ Unlocked" + osSyncPrintf("%2d %d serialMsgQロック解除しました %08x %08x %08x\n", osGetThreadId(NULL), + padMgr->serialMsgQ.validCount, padMgr, &padMgr->serialMsgQ, ctrlrQ); + } +} + +void PadMgr_LockPadData(PadMgr* padMgr) { + osRecvMesg(&padMgr->lockMsgQ, NULL, OS_MESG_BLOCK); +} + +void PadMgr_UnlockPadData(PadMgr* padMgr) { + osSendMesg(&padMgr->lockMsgQ, NULL, OS_MESG_BLOCK); +} + +void PadMgr_RumbleControl(PadMgr* padMgr) { + static u32 errcnt = 0; + static u32 frame; + s32 temp = 1; + s32 triedRumbleComm; + OSMesgQueue* ctrlrQ = PadMgr_LockSerialMesgQueue(padMgr); + s32 var4; + s32 i; + + triedRumbleComm = 0; + + for (i = 0; i < 4; i++) { + if (padMgr->ctrlrIsConnected[i]) { + if (padMgr->padStatus[i].status & 1) { + if (padMgr->pakType[i] == temp) { + if (padMgr->rumbleEnable[i] != 0) { + if (padMgr->rumbleCounter[i] < 3) { + // clang-format off + if (1) {} osSyncPrintf(VT_FGCOL(YELLOW)); + // clang-format on + + // "Vibration pack jumble jumble"? + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パック ぶるぶるぶるぶる"); + osSyncPrintf(VT_RST); + + if (__osMotorAccess(&padMgr->pfs[i], temp) != 0) { + padMgr->pakType[i] = 0; + osSyncPrintf(VT_FGCOL(YELLOW)); + // "A communication error has occurred with the vibration pack" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パックで通信エラーが発生しました"); + osSyncPrintf(VT_RST); + } else { + padMgr->rumbleCounter[i] = 3; + } + + triedRumbleComm = 1; + } + } else { + if (padMgr->rumbleCounter[i] != 0) { + // clang-format off + if (1) {} osSyncPrintf(VT_FGCOL(YELLOW)); + // clang-format on + + // "Stop vibration pack" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パック 停止"); + osSyncPrintf(VT_RST); + + if (osMotorStop(&padMgr->pfs[i]) != 0) { + padMgr->pakType[i] = 0; + osSyncPrintf(VT_FGCOL(YELLOW)); + // "A communication error has occurred with the vibration pack" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パックで通信エラーが発生しました"); + osSyncPrintf(VT_RST); + } else { + padMgr->rumbleCounter[i]--; + } + + triedRumbleComm = 1; + } + } + } + } else { + if (padMgr->pakType[i] != 0) { + if (padMgr->pakType[i] == 1) { + osSyncPrintf(VT_FGCOL(YELLOW)); + // "It seems that a vibration pack was pulled out" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パックが抜かれたようです"); + osSyncPrintf(VT_RST); + padMgr->pakType[i] = 0; + } else { + osSyncPrintf(VT_FGCOL(YELLOW)); + // "It seems that a controller pack that is not a vibration pack was pulled out" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, + "振動パックではないコントローラパックが抜かれたようです"); + osSyncPrintf(VT_RST); + padMgr->pakType[i] = 0; + } + } + } + } + } + + if (!triedRumbleComm) { + i = frame % 4; + + if (padMgr->ctrlrIsConnected[i] && (padMgr->padStatus[i].status & 1) && (padMgr->pakType[i] != 1)) { + var4 = osMotorInit(ctrlrQ, &padMgr->pfs[i], i); + + if (var4 == 0) { + padMgr->pakType[i] = 1; + osMotorStart(&padMgr->pfs[i]); + osMotorStop(&padMgr->pfs[i]); + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Recognized vibration pack" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パックを認識しました"); + osSyncPrintf(VT_RST); + } else if (var4 == 11) { + padMgr->pakType[i] = 2; + } else if (var4 == 4) { + LOG_NUM("++errcnt", ++errcnt, "../padmgr.c", 282); + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Controller pack communication error" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "コントローラパックの通信エラー"); + osSyncPrintf(VT_RST); + } + } + } + + frame++; + PadMgr_UnlockSerialMesgQueue(padMgr, ctrlrQ); +} + +void PadMgr_RumbleStop(PadMgr* padMgr) { + s32 i; + OSMesgQueue* ctrlrQ = PadMgr_LockSerialMesgQueue(padMgr); + + for (i = 0; i < 4; i++) { + if (osMotorInit(ctrlrQ, &padMgr->pfs[i], i) == 0) { +#if 0 + if ((gFaultStruct.msgId == 0) && (padMgr->rumbleOnFrames != 0)) + { + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Stop vibration pack" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "振動パック 停止"); + osSyncPrintf(VT_RST); + } +#endif + + osMotorStop(&padMgr->pfs[i]); + } + } + + PadMgr_UnlockSerialMesgQueue(padMgr, ctrlrQ); +} + +void PadMgr_RumbleReset(PadMgr* padMgr) { + padMgr->rumbleOffFrames = 3; +} + +void PadMgr_RumbleSetSingle(PadMgr* padMgr, u32 ctrlr, u32 rumble) { + padMgr->rumbleEnable[ctrlr] = rumble; + padMgr->rumbleOnFrames = 240; +} + +void PadMgr_RumbleSet(PadMgr* padMgr, u8* ctrlrRumbles) { + s32 i; + + for (i = 0; i < 4; i++) { + padMgr->rumbleEnable[i] = ctrlrRumbles[i]; + } + + padMgr->rumbleOnFrames = 240; +} + +void PadMgr_ProcessInputs(PadMgr* padMgr) { + s32 i; + Input* input; + OSContPad* padnow1; // original name + s32 buttonDiff; + + PadMgr_LockPadData(padMgr); + + input = &padMgr->inputs[0]; + padnow1 = &padMgr->pads[0]; + + for (i = 0; i < padMgr->nControllers; i++, input++, padnow1++) { + input->prev = input->cur; + + if (1) {} // Necessary to match + + switch (padnow1->err_no) { + case 0: + input->cur = *padnow1; + if (!padMgr->ctrlrIsConnected[i]) { + padMgr->ctrlrIsConnected[i] = true; + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "認識しました"); // "Recognized" + osSyncPrintf(VT_RST); + } + break; + case 4: + input->cur = input->prev; + LOG_NUM("this->Key_switch[i]", padMgr->ctrlrIsConnected[i], "../padmgr.c", 380); + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Overrun error occurred" + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "オーバーランエラーが発生"); + osSyncPrintf(VT_RST); + break; + case 8: + input->cur.button = 0; + input->cur.stick_x = 0; + input->cur.stick_y = 0; + input->cur.err_no = padnow1->err_no; + if (padMgr->ctrlrIsConnected[i]) { + padMgr->ctrlrIsConnected[i] = false; + padMgr->pakType[i] = 0; + padMgr->rumbleCounter[i] = 0xFF; + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Do not respond"? + osSyncPrintf("padmgr: %dコン: %s\n", i + 1, "応答しません"); + osSyncPrintf(VT_RST); + } + break; + default: + LOG_HEX("padnow1->errno", padnow1->err_no, "../padmgr.c", 396); + Fault_AddHungupAndCrash("../padmgr.c", 397); + } + + buttonDiff = input->prev.button ^ input->cur.button; + input->press.button |= (u16)(buttonDiff & input->cur.button); + input->rel.button |= (u16)(buttonDiff & input->prev.button); + PadUtils_UpdateRelXY(input); + input->press.stick_x += (s8)(input->cur.stick_x - input->prev.stick_x); + input->press.stick_y += (s8)(input->cur.stick_y - input->prev.stick_y); + } + + controllerCallback.rumble = padMgr->rumbleEnable[0] > 0 ? 1 : 0; + + if (HealthMeter_IsCritical()) { + controllerCallback.ledColor = 1; + } else { + controllerCallback.ledColor = 0; + } + + OTRControllerCallback(&controllerCallback); + + PadMgr_UnlockPadData(padMgr); +} + +void PadMgr_HandleRetraceMsg(PadMgr* padMgr) { + s32 i; + OSMesgQueue* queue = PadMgr_LockSerialMesgQueue(padMgr); + u32 mask; + + osContStartReadData(queue); + if (padMgr->retraceCallback) { + padMgr->retraceCallback(padMgr, padMgr->retraceCallbackValue); + } + osRecvMesg(queue, NULL, OS_MESG_BLOCK); + osContGetReadData(padMgr->pads); + if (padMgr->preNMIShutdown) { + memset(padMgr->pads, 0, sizeof(padMgr->pads)); + } + PadMgr_ProcessInputs(padMgr); + osContStartQuery(queue); + osRecvMesg(queue, NULL, OS_MESG_BLOCK); + osContGetQuery(padMgr->padStatus); + PadMgr_UnlockSerialMesgQueue(padMgr, queue); + + mask = 0; + for (i = 0; i < 4; i++) { + if (padMgr->padStatus[i].err_no == 0) { + if (padMgr->padStatus[i].type == CONT_TYPE_NORMAL) { + mask |= 1 << i; + } else { + //LOG_HEX("this->pad_status[i].type", padMgr->padStatus[i].type, "../padmgr.c", 458); + // "An unknown type of controller is connected" + //osSyncPrintf("知らない種類のコントローラが接続されています\n"); + } + } + } + padMgr->validCtrlrsMask = mask; + + /* if (gFaultStruct.msgId) { + PadMgr_RumbleStop(padMgr); + } else */ if (padMgr->rumbleOffFrames > 0) { + --padMgr->rumbleOffFrames; + PadMgr_RumbleStop(padMgr); + } else if (padMgr->rumbleOnFrames == 0) { + PadMgr_RumbleStop(padMgr); + } else if (!padMgr->preNMIShutdown) { + PadMgr_RumbleControl(padMgr); + --padMgr->rumbleOnFrames; + } +} + +void PadMgr_HandlePreNMI(PadMgr* padMgr) { + osSyncPrintf("padmgr_HandlePreNMI()\n"); + padMgr->preNMIShutdown = true; + PadMgr_RumbleReset(padMgr); +} + +void PadMgr_RequestPadData(PadMgr* padMgr, Input* inputs, s32 mode) { + s32 i; + Input* ogInput; + Input* newInput; + s32 buttonDiff; + + PadMgr_LockPadData(padMgr); + + ogInput = &padMgr->inputs[0]; + newInput = &inputs[0]; + for (i = 0; i < 4; i++) { + if (mode != 0) { + *newInput = *ogInput; + ogInput->press.button = 0; + ogInput->press.stick_x = 0; + ogInput->press.stick_y = 0; + ogInput->rel.button = 0; + } else { + newInput->prev = newInput->cur; + newInput->cur = ogInput->cur; + buttonDiff = newInput->prev.button ^ newInput->cur.button; + newInput->press.button = newInput->cur.button & buttonDiff; + newInput->rel.button = newInput->prev.button & buttonDiff; + PadUtils_UpdateRelXY(newInput); + newInput->press.stick_x += (s8)(newInput->cur.stick_x - newInput->prev.stick_x); + newInput->press.stick_y += (s8)(newInput->cur.stick_y - newInput->prev.stick_y); + } + ogInput++; + newInput++; + } + + PadMgr_UnlockPadData(padMgr); +} + +void PadMgr_ThreadEntry(PadMgr* padMgr) { + s16* mesg = NULL; + s32 exit; + + //osSyncPrintf("コントローラスレッド実行開始\n"); // "Controller thread execution start" + + exit = false; + while (!exit) { + if ((D_8012D280 > 2) && (padMgr->interruptMsgQ.validCount == 0)) { + // "Waiting for controller thread event" + osSyncPrintf("コントローラスレッドイベント待ち %lld\n", OS_CYCLES_TO_USEC(osGetTime())); + } + + osRecvMesg(&padMgr->interruptMsgQ, (OSMesg)&mesg, OS_MESG_BLOCK); + //LogUtils_CheckNullPointer("msg", mesg, "../padmgr.c", 563); + + PadMgr_HandleRetraceMsg(padMgr); + break; + +#if 0 + switch (*mesg) { + case OS_SC_RETRACE_MSG: + if (D_8012D280 > 2) { + osSyncPrintf("padmgr_HandleRetraceMsg START %lld\n", OS_CYCLES_TO_USEC(osGetTime())); + } + + PadMgr_HandleRetraceMsg(padMgr); + + if (D_8012D280 > 2) { + osSyncPrintf("padmgr_HandleRetraceMsg END %lld\n", OS_CYCLES_TO_USEC(osGetTime())); + } + + break; + case OS_SC_PRE_NMI_MSG: + PadMgr_HandlePreNMI(padMgr); + break; + case OS_SC_NMI_MSG: + exit = true; + break; + } +#endif + } + + // OTRTODO: Removed due to crash + //IrqMgr_RemoveClient(padMgr->irqMgr, &padMgr->irqClient); + + //osSyncPrintf("コントローラスレッド実行終了\n"); // "Controller thread execution end" +} + +void PadMgr_Init(PadMgr* padMgr, OSMesgQueue* siIntMsgQ, IrqMgr* irqMgr, OSId id, OSPri priority, void* stack) { + osSyncPrintf("パッドマネージャ作成 padmgr_Create()\n"); // "Pad Manager creation" + + memset(padMgr, 0, sizeof(PadMgr)); + padMgr->irqMgr = irqMgr; + + osCreateMesgQueue(&padMgr->interruptMsgQ, padMgr->interruptMsgBuf, 4); + // OTRTODO: Removed due to crash + //IrqMgr_AddClient(padMgr->irqMgr, &padMgr->irqClient, &padMgr->interruptMsgQ); + osCreateMesgQueue(&padMgr->serialMsgQ, padMgr->serialMsgBuf, 1); + PadMgr_UnlockSerialMesgQueue(padMgr, siIntMsgQ); + osCreateMesgQueue(&padMgr->lockMsgQ, padMgr->lockMsgBuf, 1); + PadMgr_UnlockPadData(padMgr); + PadSetup_Init(siIntMsgQ, (u8*)&padMgr->validCtrlrsMask, padMgr->padStatus); + + padMgr->nControllers = 4; + osContSetCh(padMgr->nControllers); + + osCreateThread(&padMgr->thread, id, (void (*)(void*))PadMgr_ThreadEntry, padMgr, stack, priority); + osStartThread(&padMgr->thread); +} diff --git a/soh/src/code/padsetup.c b/soh/src/code/padsetup.c new file mode 100644 index 000000000..f3145a434 --- /dev/null +++ b/soh/src/code/padsetup.c @@ -0,0 +1,34 @@ +#include "global.h" + +s32 PadSetup_Init(OSMesgQueue* mq, u8* outMask, OSContStatus* status) { + s32 ret; + s32 i; + + *outMask = 0xFF; + ret = osContInit(mq, outMask, status); + if (ret != 0) { + return ret; + } + if (*outMask == 0xFF) { + if (osContStartQuery(mq) != 0) { + return 1; + } + + osRecvMesg(mq, NULL, OS_MESG_BLOCK); + osContGetQuery(status); + + *outMask = 0; + for (i = 0; i < 4; i++) { + switch (status[i].err_no) { + case 0: + if (status[i].type == CONT_TYPE_NORMAL) { + *outMask |= 1 << i; + } + break; + default: + break; + } + } + } + return 0; +} diff --git a/soh/src/code/padutils.c b/soh/src/code/padutils.c new file mode 100644 index 000000000..6a58b14f6 --- /dev/null +++ b/soh/src/code/padutils.c @@ -0,0 +1,94 @@ +#include "global.h" + +#include + +void PadUtils_Init(Input* input) { + memset(input,0, sizeof(Input)); +} + +void func_800FCB70(void) { +} + +void PadUtils_ResetPressRel(Input* input) { + input->press.button = 0; + input->rel.button = 0; +} + +u32 PadUtils_CheckCurExact(Input* input, u16 value) { + return value == input->cur.button; +} + +u32 PadUtils_CheckCur(Input* input, u16 key) { + return key == (input->cur.button & key); +} + +u32 PadUtils_CheckPressed(Input* input, u16 key) { + return key == (input->press.button & key); +} + +u32 PadUtils_CheckReleased(Input* input, u16 key) { + return key == (input->rel.button & key); +} + +u16 PadUtils_GetCurButton(Input* input) { + return input->cur.button; +} + +u16 PadUtils_GetPressButton(Input* input) { + return input->press.button; +} + +s8 PadUtils_GetCurX(Input* input) { + return input->cur.stick_x; +} + +s8 PadUtils_GetCurY(Input* input) { + return input->cur.stick_y; +} + +void PadUtils_SetRelXY(Input* input, s32 x, s32 y) { + input->rel.stick_x = x; + input->rel.stick_y = y; +} + +s8 PadUtils_GetRelXImpl(Input* input) { + return input->rel.stick_x; +} + +s8 PadUtils_GetRelYImpl(Input* input) { + return input->rel.stick_y; +} + +s8 PadUtils_GetRelX(Input* input) { + return PadUtils_GetRelXImpl(input); +} + +s8 PadUtils_GetRelY(Input* input) { + return PadUtils_GetRelYImpl(input); +} + +void PadUtils_UpdateRelXY(Input* input) { + s32 curX = PadUtils_GetCurX(input); + s32 curY = PadUtils_GetCurY(input); + s32 relX; + s32 relY; + + if (curX > 7) { + relX = (curX < 0x43) ? curX - 7 : 0x43 - 7; + } else if (curX < -7) { + relX = (curX > -0x43) ? curX + 7 : -0x43 + 7; + } else { + relX = 0; + } + + if (curY > 7) { + relY = (curY < 0x43) ? curY - 7 : 0x43 - 7; + + } else if (curY < -7) { + relY = (curY > -0x43) ? curY + 7 : -0x43 + 7; + } else { + relY = 0; + } + + PadUtils_SetRelXY(input, relX, relY); +} diff --git a/soh/src/code/printutils.c b/soh/src/code/printutils.c new file mode 100644 index 000000000..3fb8cf367 --- /dev/null +++ b/soh/src/code/printutils.c @@ -0,0 +1,17 @@ +#include "global.h" + +s32 PrintUtils_VPrintf(PrintCallback* pfn, const char* fmt, va_list args) { + return _Printf(*pfn, pfn, fmt, args); +} + +s32 PrintUtils_Printf(PrintCallback* pfn, const char* fmt, ...) { + s32 ret; + va_list args; + va_start(args, fmt); + + ret = PrintUtils_VPrintf(pfn, fmt, args); + + va_end(args); + + return ret; +} diff --git a/soh/src/code/relocation.c b/soh/src/code/relocation.c new file mode 100644 index 000000000..a62ba3239 --- /dev/null +++ b/soh/src/code/relocation.c @@ -0,0 +1,113 @@ +#include "global.h" + +void Overlay_Relocate(void* allocatedVRamAddress, OverlayRelocationSection* overlayInfo, void* vRamAddress) { + u32 sections[4]; + u32 relocatedValue; + u32 dbg; + u32 relocOffset; + u32 relocData; + uintptr_t unrelocatedAddress; + u32 i; + uintptr_t* relocDataP; + u32* luiRefs[32]; + u32 luiVals[32]; + uintptr_t relocatedAddress; + u32 reloc; + uintptr_t vaddr; + u32* luiInstRef; + uintptr_t allocu32 = (uintptr_t)allocatedVRamAddress; + u32* regValP; + u32 isLoNeg; + + relocOffset = 0; + relocatedValue = 0; + unrelocatedAddress = 0; + relocatedAddress = 0; + + if (gOverlayLogSeverity >= 3) { + osSyncPrintf("DoRelocation(%08x, %08x, %08x)\n", allocatedVRamAddress, overlayInfo, vRamAddress); + osSyncPrintf("text=%08x, data=%08x, rodata=%08x, bss=%08x\n", overlayInfo->textSize, overlayInfo->dataSize, + overlayInfo->rodataSize, overlayInfo->bssSize); + } + + sections[0] = 0; + sections[1] = allocu32; + sections[2] = allocu32 + overlayInfo->textSize; + sections[3] = sections[2] + overlayInfo->dataSize; + + for (i = 0; i < overlayInfo->nRelocations; i++) { + reloc = overlayInfo->relocations[i]; + relocDataP = (u32*)(sections[reloc >> 0x1E] + (reloc & 0xFFFFFF)); + relocData = *relocDataP; + switch (reloc & 0x3F000000) { + case 0x2000000: + /* R_MIPS_32 + * Handles 32-bit address relocation. Used in things such as + * jump tables. + */ + if ((*relocDataP & 0xF000000) == 0) { + luiInstRef = vRamAddress; + relocOffset = *relocDataP - (uintptr_t)luiInstRef; + relocatedValue = relocOffset + allocu32; + relocatedAddress = relocatedValue; + unrelocatedAddress = relocData; + *relocDataP = relocatedAddress; + } + break; + case 0x4000000: + /* R_MIPS_26 + * Handles 26-bit address relocation, used for jumps and jals + */ + unrelocatedAddress = ((*relocDataP & 0x3FFFFFF) << 2) | 0x80000000; + relocOffset = unrelocatedAddress - (uintptr_t)vRamAddress; + relocatedValue = (*relocDataP & 0xFC000000) | (((allocu32 + relocOffset) & 0xFFFFFFF) >> 2); + relocatedAddress = ((relocatedValue & 0x3FFFFFF) << 2) | 0x80000000; + *relocDataP = relocatedValue; + break; + case 0x5000000: + /* R_MIPS_HI16 + * Handles relocation for a lui instruciton, store the reference to + * the instruction, and will update it in the R_MIPS_LO16 section. + */ + luiRefs[(*relocDataP >> 0x10) & 0x1F] = relocDataP; + luiVals[(*relocDataP >> 0x10) & 0x1F] = *relocDataP; + break; + case 0x6000000: + /* R_MIPS_LO16 + * Updates the LUI instruction to reflect the relocated address. + * The full address is calculated from the LUI and lo parts, and then updated. + * if the lo part is negative, add 1 to the lui. + */ + regValP = &luiVals[((*relocDataP >> 0x15) & 0x1F)]; + vaddr = (*regValP << 0x10) + (s16)*relocDataP; + luiInstRef = luiRefs[((*relocDataP >> 0x15) & 0x1F)]; + if ((vaddr & 0xF000000) == 0) { + relocOffset = vaddr - (uintptr_t)vRamAddress; + vaddr = (s16)relocData; + isLoNeg = (((relocOffset + allocu32) & 0x8000) ? 1 : 0); + unrelocatedAddress = (*luiInstRef << 0x10) + vaddr; + *luiInstRef = + (*luiInstRef & 0xFFFF0000) | ((((relocOffset + allocu32) >> 0x10) & 0xFFFF) + isLoNeg); + relocatedValue = (*relocDataP & 0xFFFF0000) | ((relocOffset + allocu32) & 0xFFFF); + + relocatedAddress = (*luiInstRef << 0x10) + (s16)relocatedValue; + *relocDataP = relocatedValue; + } + break; + } + + dbg = 0x10; + switch (reloc & 0x3F000000) { + case 0x2000000: + dbg = 0x16; + case 0x4000000: + dbg += 0xA; + case 0x6000000: + if (gOverlayLogSeverity >= 3) { + osSyncPrintf("%02d %08x %08x %08x ", dbg, relocDataP, relocatedValue, relocatedAddress); + osSyncPrintf(" %08x %08x %08x %08x\n", ((uintptr_t)relocDataP + (uintptr_t)vRamAddress) - allocu32, relocData, + unrelocatedAddress, relocOffset); + } + } + } +} diff --git a/soh/src/code/sched.c b/soh/src/code/sched.c new file mode 100644 index 000000000..38b51bcde --- /dev/null +++ b/soh/src/code/sched.c @@ -0,0 +1,493 @@ +#include "global.h" + +#include + +#define RSP_DONE_MSG 667 +#define RDP_DONE_MSG 668 +#define ENTRY_MSG 670 + +// data +vs32 sLogScheduler = false; + +// bss +OSTime sRSPGFXStartTime; +OSTime sRSPAudioStartTime; +OSTime sRSPOtherStartTime; +OSTime sRDPStartTime; + +void Sched_SwapFrameBuffer(CfbInfo* cfbInfo) { + u16 width; + + LogUtils_CheckValidPointer("cfbinfo->swapbuffer", cfbInfo->swapBuffer, "../sched.c", 340); + if (cfbInfo->swapBuffer != NULL) { + osViSwapBuffer(cfbInfo->swapBuffer); + cfbInfo->updateRate2 = cfbInfo->updateRate; + + if (sLogScheduler) { + osSyncPrintf("osViSwapBuffer %08x %08x %08x\n", osViGetCurrentFramebuffer(), osViGetNextFramebuffer(), + (cfbInfo != NULL ? cfbInfo->swapBuffer : NULL)); + } + width = cfbInfo->viMode != NULL ? cfbInfo->viMode->comRegs.width : (u32)gScreenWidth; + Fault_SetFB(cfbInfo->swapBuffer, width, 0x10); + + if (HREG(80) == 0xD && HREG(95) != 0xD) { + HREG(81) = 0; + HREG(82) = 0; + HREG(83) = 1; + HREG(84) = 0; + HREG(85) = 1; + HREG(86) = 0; + HREG(87) = 0; + HREG(88) = 0; + HREG(89) = 0; + HREG(90) = 0; + HREG(91) = 0; + HREG(92) = 0; + HREG(93) = 0; + HREG(94) = 0; + HREG(95) = 0xD; + } + if (HREG(80) == 0xD && HREG(81) == 2) { + osViSetSpecialFeatures(HREG(82) != 0 ? OS_VI_GAMMA_ON : OS_VI_GAMMA_OFF); + osViSetSpecialFeatures(HREG(83) != 0 ? OS_VI_DITHER_FILTER_ON : OS_VI_DITHER_FILTER_OFF); + osViSetSpecialFeatures(HREG(84) != 0 ? OS_VI_GAMMA_DITHER_ON : OS_VI_GAMMA_DITHER_OFF); + osViSetSpecialFeatures(HREG(85) != 0 ? OS_VI_DIVOT_ON : OS_VI_DIVOT_OFF); + } + } + cfbInfo->unk_10 = 0; +} + +void func_800C84E4(SchedContext* sc, CfbInfo* cfbInfo) { + if (sc->unk_24C != 0) { + sc->unk_24C = 0; + + if (gIrqMgrResetStatus == 0) { + ViConfig_UpdateVi(0); + } + } + + Sched_SwapFrameBuffer(cfbInfo); +} + +void Sched_HandleReset(SchedContext* sc) { + OSTime now; + + if (sc->curRSPTask != NULL) { + now = osGetTime(); + + if (sc->curRSPTask->framebuffer == NULL) { + LOG_TIME("(((u64)(now - audio_rsp_start_time)*(1000000LL/15625LL))/((62500000LL*3/4)/15625LL))", + OS_CYCLES_TO_USEC(now - sRSPAudioStartTime), "../sched.c", 421); + } else if (OS_CYCLES_TO_USEC(now - sRSPGFXStartTime) > 1000000 || + OS_CYCLES_TO_USEC(now - sRDPStartTime) > 1000000) { + func_800FBFD8(); + if (sc->curRSPTask != NULL) { + LOG_TIME("(((u64)(now - graph_rsp_start_time)*(1000000LL/15625LL))/((62500000LL*3/4)/15625LL))", + OS_CYCLES_TO_USEC(now - sRSPGFXStartTime), "../sched.c", 427); + osSendMesg(&sc->interruptQ, RSP_DONE_MSG, OS_MESG_NOBLOCK); + } + if (sc->curRDPTask != NULL) { + LOG_TIME("(((u64)(now - rdp_start_time)*(1000000LL/15625LL))/((62500000LL*3/4)/15625LL))", + OS_CYCLES_TO_USEC(now - sRDPStartTime), "../sched.c", 431); + osSendMesg(&sc->interruptQ, RDP_DONE_MSG, OS_MESG_NOBLOCK); + } + } + } +} + +void Sched_HandleStart(SchedContext* sc) { + ViConfig_UpdateVi(1); +} + +void Sched_QueueTask(SchedContext* sc, OSScTask* task) { + s32 type = task->list.t.type; + + ASSERT((type == M_AUDTASK) || (type == M_GFXTASK) || (type == M_NJPEGTASK) || (type == M_NULTASK), + "(type == M_AUDTASK) || (type == M_GFXTASK) || (type == M_NJPEGTASK) || (type == M_NULTASK)", "../sched.c", + 463); + + if (type == M_AUDTASK) { + if (sLogScheduler) { + // "You have entered an audio task" + osSyncPrintf("オーディオタスクをエントリしました\n"); + } + if (sc->audioListTail != NULL) { + sc->audioListTail->next = task; + } else { + sc->audioListHead = task; + } + sc->audioListTail = task; + sc->doAudio = 1; + } else { + if (sLogScheduler) { + osSyncPrintf("グラフタスクをエントリしました\n"); // "Entered graph task" + } + + if (sc->gfxListTail != NULL) { + sc->gfxListTail->next = task; + } else { + sc->gfxListHead = task; + } + sc->gfxListTail = task; + } + task->next = NULL; + task->state = task->flags & (OS_SC_NEEDS_RDP | OS_SC_NEEDS_RSP); +} + +void Sched_Yield(SchedContext* sc) { + if (!(sc->curRSPTask->state & OS_SC_YIELD)) { + ASSERT(sc->curRSPTask->list.t.type != M_AUDTASK, "sc->curRSPTask->list.t.type != M_AUDTASK", "../sched.c", 496); + + sc->curRSPTask->state |= OS_SC_YIELD; + + osSpTaskYield(); + + if (sLogScheduler) { + osSyncPrintf("%08d:osSpTaskYield\n", (u32)(OS_CYCLES_TO_USEC(osGetTime()))); + } + } +} + +OSScTask* func_800C89D4(SchedContext* sc, OSScTask* task) { + if (task == NULL) { + return NULL; + } + + if (sc->pendingSwapBuf1 != NULL) { + if (0) { + ASSERT(sc->pendingSwapBuf1 != NULL, "sc->pending_swapbuffer1", "../sched.c", UNK_LINE); + } + return NULL; + } + + if (sc->pendingSwapBuf2 != NULL) { + if (0) { + ASSERT(sc->pendingSwapBuf2 != NULL, "sc->pending_swapbuffer2", "../sched.c", UNK_LINE); + } + return NULL; + } + + if ((sc->pendingSwapBuf2 != NULL ? sc->pendingSwapBuf2->swapBuffer : NULL) == task->framebuffer->fb1) { + return NULL; + } + + if ((sc->pendingSwapBuf1 != NULL ? sc->pendingSwapBuf1->swapBuffer : NULL) == task->framebuffer->fb1) { + return NULL; + } + + if (osViGetCurrentFramebuffer() == (u32*)task->framebuffer->fb1) { + return NULL; + } + + return task; +} + +s32 Sched_Schedule(SchedContext* sc, OSScTask** sp, OSScTask** dp, s32 state) { + s32 ret = state; + OSScTask* gfxTask = sc->gfxListHead; + OSScTask* audioTask = sc->audioListHead; + + if (sc->doAudio && (ret & OS_SC_SP)) { + *sp = audioTask; + ret &= ~OS_SC_SP; + sc->doAudio = 0; + sc->audioListHead = sc->audioListHead->next; + if (sc->audioListHead == NULL) { + sc->audioListTail = NULL; + } + } else if (gfxTask != NULL) { + if (gfxTask->state & OS_SC_YIELDED || !(gfxTask->flags & OS_SC_NEEDS_RDP)) { + if (ret & OS_SC_SP) { + *sp = gfxTask; + ret &= ~OS_SC_SP; + sc->gfxListHead = sc->gfxListHead->next; + if (sc->gfxListHead == NULL) { + sc->gfxListTail = NULL; + } + } + } else if (ret == (OS_SC_SP | OS_SC_DP)) { + if (gfxTask->framebuffer == NULL || func_800C89D4(sc, gfxTask) != NULL) { + *sp = *dp = gfxTask; + ret &= ~(OS_SC_SP | OS_SC_DP); + sc->gfxListHead = sc->gfxListHead->next; + if (sc->gfxListHead == NULL) { + sc->gfxListTail = NULL; + } + } + } + } + return ret; +} + +void func_800C8BC4(SchedContext* sc, OSScTask* task) { + if (sc->pendingSwapBuf1 == NULL) { + sc->pendingSwapBuf1 = task->framebuffer; + + LogUtils_CheckValidPointer("sc->pending_swapbuffer1", sc->pendingSwapBuf1, "../sched.c", 618); + + if ((sc->curBuf == NULL) || (sc->curBuf->updateRate2 < 1)) { + func_800C84E4(sc, task->framebuffer); + } + } +} + +u32 Sched_IsComplete(SchedContext* sc, OSScTask* task) { + if (!(task->state & (OS_SC_DP | OS_SC_SP))) { + if (task->msgQ != NULL) { + osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK); + } + + if (task->flags & OS_SC_SWAPBUFFER) { + func_800C8BC4(sc, task); + } + + return 1; + } + + return 0; +} + +void Sched_RunTask(SchedContext* sc, OSScTask* spTask, OSScTask* dpTask) { + ASSERT(sc->curRSPTask == NULL, "sc->curRSPTask == NULL", "../sched.c", 663); + if (spTask != NULL) { + if (spTask->list.t.type == M_NULTASK) { + if (spTask->flags & OS_SC_NEEDS_RSP) { + spTask->state &= ~OS_SC_SP; + sc->curRSPTask = NULL; + } + if (spTask->flags & OS_SC_NEEDS_RDP) { + spTask->state &= ~OS_SC_DP; + sc->curRDPTask = NULL; + } + Sched_IsComplete(sc, spTask); + return; + } + + spTask->state &= ~(OS_SC_YIELD | OS_SC_YIELDED); + osWritebackDCacheAll(); + osSpTaskLoad(&spTask->list); + + if (spTask->list.t.type == M_AUDTASK) { + sRSPAudioStartTime = osGetTime(); + } else if (spTask->list.t.type == M_GFXTASK) { + sRSPGFXStartTime = osGetTime(); + } else { + sRSPOtherStartTime = osGetTime(); + } + + osSpTaskStartGo(&spTask->list); + if (sLogScheduler) { + osSyncPrintf( + "%08d:osSpTaskStartGo(%08x) %s\n", (u32)OS_CYCLES_TO_USEC(osGetTime()), &spTask->list, + (spTask->list.t.type == M_AUDTASK ? "AUDIO" : (spTask->list.t.type == M_GFXTASK ? "GRAPH" : "OTHER"))); + } + sc->curRSPTask = spTask; + + if (spTask == dpTask && sc->curRDPTask == NULL) { + sc->curRDPTask = dpTask; + sRDPStartTime = sRSPGFXStartTime; + } + } +} + +void Sched_HandleEntry(SchedContext* sc) { + OSScTask* nextRSP = NULL; + OSScTask* nextRDP = NULL; + s32 state; + OSMesg msg = NULL; + + while (osRecvMesg(&sc->cmdQ, &msg, OS_MESG_NOBLOCK) != -1) { + Sched_QueueTask(sc, msg); + } + + if (sc->doAudio != 0 && sc->curRSPTask != NULL) { + if (sLogScheduler) { + osSyncPrintf("[YIELD B]"); + } + Sched_Yield(sc); + return; + } + + state = ((sc->curRSPTask == 0) * 2) | (sc->curRDPTask == 0); + if (Sched_Schedule(sc, &nextRSP, &nextRDP, state) != state) { + Sched_RunTask(sc, nextRSP, nextRDP); + } + if (sLogScheduler) { + osSyncPrintf("EN sc:%08x sp:%08x dp:%08x state:%x\n", sc, nextRSP, nextRDP, state); + } +} + +void Sched_HandleRetrace(SchedContext* sc) { + if (sLogScheduler) { + osSyncPrintf("%08d:scHandleRetrace %08x\n", (u32)OS_CYCLES_TO_USEC(osGetTime()), osViGetCurrentFramebuffer()); + } + ViConfig_UpdateBlack(); + sc->retraceCnt++; + + if (osViGetCurrentFramebuffer() == (u32*)(sc->pendingSwapBuf1 != NULL ? sc->pendingSwapBuf1->swapBuffer : NULL)) { + if (sc->curBuf != NULL) { + sc->curBuf->unk_10 = 0; + } + + if (sc->pendingSwapBuf1 != NULL) { + sc->pendingSwapBuf1->unk_10 = 0; + } + sc->curBuf = sc->pendingSwapBuf1; + sc->pendingSwapBuf1 = sc->pendingSwapBuf2; + sc->pendingSwapBuf2 = NULL; + } + if (sc->curBuf != NULL) { + if (sc->curBuf->updateRate2 > 0) { + sc->curBuf->updateRate2--; + } + if ((sc->curBuf->updateRate2 <= 0) && (sc->pendingSwapBuf1 != NULL)) { + func_800C84E4(sc, sc->pendingSwapBuf1); + } + } + if (sLogScheduler) { + osSyncPrintf("%08x %08x %08x %d\n", osViGetCurrentFramebuffer(), osViGetNextFramebuffer(), + sc->pendingSwapBuf1 != NULL ? sc->pendingSwapBuf1->swapBuffer : NULL, + sc->curBuf != NULL ? sc->curBuf->updateRate2 : 0); + } + Sched_HandleEntry(sc); +} + +void Sched_HandleRSPDone(SchedContext* sc) { + OSScTask* curRSPTask; + OSScTask* nextRSP = NULL; + OSScTask* nextRDP = NULL; + s32 state; + + ASSERT(sc->curRSPTask != NULL, "sc->curRSPTask", "../sched.c", 819); + + if (sc->curRSPTask->list.t.type == M_AUDTASK) { + gRSPAudioTotalTime += osGetTime() - sRSPAudioStartTime; + } else if (sc->curRSPTask->list.t.type == M_GFXTASK) { + gRSPGFXTotalTime += osGetTime() - sRSPGFXStartTime; + } else { + gRSPOtherTotalTime += osGetTime() - sRSPOtherStartTime; + } + + curRSPTask = sc->curRSPTask; + sc->curRSPTask = NULL; + if (sLogScheduler) { + osSyncPrintf("RSP DONE %d %d", curRSPTask->state & 0x10, osSpTaskYielded(&curRSPTask->list)); + } + if (curRSPTask->state & OS_SC_YIELD && osSpTaskYielded(&curRSPTask->list)) { + if (sLogScheduler) { + osSyncPrintf("[YIELDED]\n"); + } + curRSPTask->state |= OS_SC_YIELDED; + curRSPTask->next = sc->gfxListHead; + sc->gfxListHead = curRSPTask; + if (sc->gfxListTail == NULL) { + sc->gfxListTail = curRSPTask; + } + } else { + if (sLogScheduler) { + osSyncPrintf("[NOT YIELDED]\n"); + } + curRSPTask->state &= ~OS_SC_SP; + Sched_IsComplete(sc, curRSPTask); + } + + state = ((sc->curRSPTask == NULL) << 1) | (sc->curRDPTask == NULL); + if (Sched_Schedule(sc, &nextRSP, &nextRDP, state) != state) { + Sched_RunTask(sc, nextRSP, nextRDP); + } + if (sLogScheduler) { + osSyncPrintf("SP sc:%08x sp:%08x dp:%08x state:%x\n", sc, nextRSP, nextRDP, state); + } +} + +void Sched_HandleRDPDone(SchedContext* sc) { + OSScTask* curTask; + OSScTask* nextRSP = NULL; + OSScTask* nextRDP = NULL; + s32 state; + + gRDPTotalTime = osGetTime() - sRDPStartTime; + ASSERT(sc->curRDPTask != NULL, "sc->curRDPTask", "../sched.c", 878); + ASSERT(sc->curRDPTask->list.t.type == M_GFXTASK, "sc->curRDPTask->list.t.type == M_GFXTASK", "../sched.c", 879); + curTask = sc->curRDPTask; + sc->curRDPTask = NULL; + curTask->state &= ~OS_SC_DP; + Sched_IsComplete(sc, curTask); + state = ((sc->curRSPTask == NULL) << 1) | (sc->curRDPTask == NULL); + if (Sched_Schedule(sc, &nextRSP, &nextRDP, state) != state) { + Sched_RunTask(sc, nextRSP, nextRDP); + } + if (sLogScheduler) { + osSyncPrintf("DP sc:%08x sp:%08x dp:%08x state:%x\n", sc, nextRSP, nextRDP, state); + } +} + +void Sched_SendEntryMsg(SchedContext* sc) { + if (sLogScheduler) { + osSyncPrintf("osScKickEntryMsg\n"); + } + + osSendMesg(&sc->interruptQ, ENTRY_MSG, OS_MESG_BLOCK); +} + +void Sched_ThreadEntry(void* arg) { + void* msg; + SchedContext* sc = (SchedContext*)arg; + + msg = NULL; + + while (true) { + if (sLogScheduler) { + // "%08d: standby" + osSyncPrintf("%08d:待機中\n", (u32)OS_CYCLES_TO_USEC(osGetTime())); + } + + osRecvMesg(&sc->interruptQ, &msg, OS_MESG_BLOCK); + + switch ((s32)msg) { + case ENTRY_MSG: + if (sLogScheduler) { + osSyncPrintf("%08d:ENTRY_MSG\n", (u32)OS_CYCLES_TO_USEC(osGetTime())); + } + Sched_HandleEntry(sc); + continue; + case RSP_DONE_MSG: + if (sLogScheduler) { + osSyncPrintf("%08d:RSP_DONE_MSG\n", (u32)OS_CYCLES_TO_USEC(osGetTime())); + } + Sched_HandleRSPDone(sc); + continue; + case RDP_DONE_MSG: + if (sLogScheduler) { + osSyncPrintf("%08d:RDP_DONE_MSG\n", (u32)OS_CYCLES_TO_USEC(osGetTime())); + } + Sched_HandleRDPDone(sc); + continue; + default: + break; + } + switch (((OSScMsg*)msg)->type) { + case 1: + Sched_HandleRetrace(sc); + continue; + case 4: + Sched_HandleReset(sc); + continue; + case 3: + Sched_HandleStart(sc); + continue; + } + } +} + +void Sched_Init(SchedContext* sc, void* stack, OSPri priority, UNK_TYPE arg3, UNK_TYPE arg4, IrqMgr* irqMgr) { + memset(sc,0, sizeof(SchedContext)); + sc->unk_24C = 1; + osCreateMesgQueue(&sc->interruptQ, sc->intBuf, 8); + osCreateMesgQueue(&sc->cmdQ, sc->cmdMsgBuf, 8); + osSetEventMesg(OS_EVENT_SP, &sc->interruptQ, RSP_DONE_MSG); + osSetEventMesg(OS_EVENT_DP, &sc->interruptQ, RDP_DONE_MSG); + IrqMgr_AddClient(irqMgr, &sc->irqClient, &sc->interruptQ); + osCreateThread(&sc->thread, 5, Sched_ThreadEntry, sc, stack, priority); + osStartThread(&sc->thread); +} diff --git a/soh/src/code/shrink_window.c b/soh/src/code/shrink_window.c new file mode 100644 index 000000000..cc68129e2 --- /dev/null +++ b/soh/src/code/shrink_window.c @@ -0,0 +1,97 @@ +#include "global.h" + +s32 D_8012CED0 = 0; + +s32 sShrinkWindowVal = 0; +s32 sShrinkWindowCurrentVal = 0; + +void ShrinkWindow_SetVal(s32 value) { + if (HREG(80) == 0x13 && HREG(81) == 1) { + osSyncPrintf("shrink_window_setval(%d)\n", value); + } + sShrinkWindowVal = value; +} + +u32 ShrinkWindow_GetVal(void) { + return sShrinkWindowVal; +} + +void ShrinkWindow_SetCurrentVal(s32 currentVal) { + if (HREG(80) == 0x13 && HREG(81) == 1) { + osSyncPrintf("shrink_window_setnowval(%d)\n", currentVal); + } + sShrinkWindowCurrentVal = currentVal; +} + +u32 ShrinkWindow_GetCurrentVal(void) { + return sShrinkWindowCurrentVal; +} + +void ShrinkWindow_Init(void) { + if (HREG(80) == 0x13 && HREG(81) == 1) { + osSyncPrintf("shrink_window_init()\n"); + } + D_8012CED0 = 0; + sShrinkWindowVal = 0; + sShrinkWindowCurrentVal = 0; +} + +void ShrinkWindow_Destroy(void) { + if (HREG(80) == 0x13 && HREG(81) == 1) { + osSyncPrintf("shrink_window_cleanup()\n"); + } + sShrinkWindowCurrentVal = 0; +} + +void ShrinkWindow_Update(s32 updateRate) { + s32 off; + + if (updateRate == 3) { + off = 10; + } else { + off = 30 / updateRate; + } + + if (sShrinkWindowCurrentVal < sShrinkWindowVal) { + if (D_8012CED0 != 1) { + D_8012CED0 = 1; + } + + if (sShrinkWindowCurrentVal + off < sShrinkWindowVal) { + sShrinkWindowCurrentVal += off; + } else { + sShrinkWindowCurrentVal = sShrinkWindowVal; + } + } else if (sShrinkWindowVal < sShrinkWindowCurrentVal) { + if (D_8012CED0 != 2) { + D_8012CED0 = 2; + } + + if (sShrinkWindowVal < sShrinkWindowCurrentVal - off) { + sShrinkWindowCurrentVal -= off; + } else { + sShrinkWindowCurrentVal = sShrinkWindowVal; + } + } else { + D_8012CED0 = 0; + } + + if (HREG(80) == 0x13) { + if (HREG(94) != 0x13) { + HREG(94) = 0x13; + HREG(81) = 0; + HREG(82) = 0; + HREG(83) = 0; + HREG(84) = 0; + HREG(85) = 0; + HREG(86) = 0; + HREG(87) = 0; + HREG(88) = 0; + HREG(89) = 0; + } + HREG(83) = D_8012CED0; + HREG(84) = sShrinkWindowCurrentVal; + HREG(85) = sShrinkWindowVal; + HREG(86) = off; + } +} diff --git a/soh/src/code/sleep.c b/soh/src/code/sleep.c new file mode 100644 index 000000000..f08129701 --- /dev/null +++ b/soh/src/code/sleep.c @@ -0,0 +1,28 @@ +#include "global.h" + +void Sleep_Cycles(OSTime cycles) { + OSMesgQueue mq; + OSMesg msg; + OSTimer timer; + + osCreateMesgQueue(&mq, &msg, OS_MESG_BLOCK); + osSetTimer(&timer, cycles, 0, &mq, NULL); + osRecvMesg(&mq, NULL, OS_MESG_BLOCK); +} + +void Sleep_Nsec(u32 nsec) { + Sleep_Cycles(OS_NSEC_TO_CYCLES(nsec)); +} + +void Sleep_Usec(u32 usec) { + Sleep_Cycles(OS_USEC_TO_CYCLES(usec)); +} + +// originally "msleep" +void Sleep_Msec(u32 ms) { + Sleep_Cycles((ms * OS_CPU_COUNTER) / 1000ull); +} + +void Sleep_Sec(u32 sec) { + Sleep_Cycles(sec * OS_CPU_COUNTER); +} diff --git a/soh/src/code/speed_meter.c b/soh/src/code/speed_meter.c new file mode 100644 index 000000000..7d768d090 --- /dev/null +++ b/soh/src/code/speed_meter.c @@ -0,0 +1,211 @@ +#include "global.h" +#include "vt.h" + +volatile OSTime D_8016A520; +volatile OSTime D_8016A528; +volatile OSTime D_8016A530; +volatile OSTime D_8016A538; +volatile OSTime D_8016A540; +volatile OSTime D_8016A548; +volatile OSTime D_8016A550; +volatile OSTime D_8016A558; +volatile OSTime gRSPAudioTotalTime; +volatile OSTime gRSPGFXTotalTime; +volatile OSTime gRSPOtherTotalTime; +volatile OSTime D_8016A578; +volatile OSTime gRDPTotalTime; +SpeedMeterTimeEntry* sSpeedMeterTimeEntryPtr; + +SpeedMeterTimeEntry sSpeedMeterTimeEntryArray[] = { + { &D_8016A520, 0, 0, GPACK_RGBA5551(255, 0, 0, 1) }, { &D_8016A528, 0, 2, GPACK_RGBA5551(255, 255, 0, 1) }, + { &D_8016A530, 0, 4, GPACK_RGBA5551(0, 0, 255, 1) }, { &D_8016A538, 0, 6, GPACK_RGBA5551(255, 128, 128, 1) }, + { &D_8016A540, 0, 8, GPACK_RGBA5551(0, 255, 0, 1) }, { &D_8016A548, 0, 10, GPACK_RGBA5551(255, 0, 255, 1) }, +}; + +#define DrawRec(gfx, color, ulx, uly, lrx, lry) \ + gDPPipeSync(gfx); \ + gDPSetFillColor(gfx, ((color) << 16) | (color)); \ + gDPFillRectangle(gfx, (ulx), (uly), (lrx), (lry)); \ + gDPPipeSync(gfx); + +void SpeedMeter_InitImpl(SpeedMeter* this, u32 arg1, u32 y) { + LogUtils_CheckNullPointer("this", this, "../speed_meter.c", 181); + this->unk_18 = arg1; + this->y = y; +} + +void SpeedMeter_Init(SpeedMeter* this) { + SpeedMeter_InitImpl(this, 32, 22); +} + +void SpeedMeter_Destroy(SpeedMeter* this) { +} + +void SpeedMeter_DrawTimeEntries(SpeedMeter* this, GraphicsContext* gfxCtx) { + s32 pad[2]; + u32 baseX = 32; + s32 temp; + s32 i; + s32 uly; + s32 lry; + View view; + u32 pad2[3]; + Gfx* gfx; + + uly = this->y; + lry = this->y + 2; + + OPEN_DISPS(gfxCtx, "../speed_meter.c", 225); + + /*! @bug if gIrqMgrRetraceTime is 0, CLOSE_DISPS will never be reached */ + if (gIrqMgrRetraceTime == 0) { + return; + } + + sSpeedMeterTimeEntryPtr = &sSpeedMeterTimeEntryArray[0]; + for (i = 0; i < ARRAY_COUNT(sSpeedMeterTimeEntryArray); i++) { + temp = ((f64) * (sSpeedMeterTimeEntryPtr->time) / gIrqMgrRetraceTime) * 64.0; + sSpeedMeterTimeEntryPtr->x = temp + baseX; + sSpeedMeterTimeEntryPtr++; + } + + View_Init(&view, gfxCtx); + view.flags = 0xA; + + SET_FULLSCREEN_VIEWPORT(&view); + + gfx = OVERLAY_DISP; + func_800AB9EC(&view, 0xF, &gfx); + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + + DrawRec(gfx++, GPACK_RGBA5551(0, 0, 255, 1), baseX + 64 * 0, uly, baseX + 64 * 1, lry); + DrawRec(gfx++, GPACK_RGBA5551(0, 255, 0, 1), baseX + 64 * 1, uly, baseX + 64 * 2, lry); + DrawRec(gfx++, GPACK_RGBA5551(255, 0, 0, 1), baseX + 64 * 2, uly, baseX + 64 * 3, lry); + DrawRec(gfx++, GPACK_RGBA5551(255, 0, 255, 1), baseX + 64 * 3, uly, baseX + 64 * 4, lry); + + sSpeedMeterTimeEntryPtr = &sSpeedMeterTimeEntryArray[0]; + for (i = 0; i < ARRAY_COUNT(sSpeedMeterTimeEntryArray); i++) { + DrawRec(gfx++, sSpeedMeterTimeEntryPtr->color, baseX, lry + sSpeedMeterTimeEntryPtr->y, + sSpeedMeterTimeEntryPtr->x, lry + sSpeedMeterTimeEntryPtr->y + 1); + sSpeedMeterTimeEntryPtr++; + } + gDPPipeSync(gfx++); + + OVERLAY_DISP = gfx; + + CLOSE_DISPS(gfxCtx, "../speed_meter.c", 276); +} + +void SpeedMeter_InitAllocEntry(SpeedMeterAllocEntry* this, u32 maxval, u32 val, u16 backColor, u16 foreColor, u32 ulx, + u32 lrx, u32 uly, u32 lry) { + this->maxval = maxval; + this->val = val; + this->backColor = backColor; + this->foreColor = foreColor; + this->ulx = ulx; + this->lrx = lrx; + this->uly = uly; + this->lry = lry; +} + +void SpeedMeter_DrawAllocEntry(SpeedMeterAllocEntry* this, GraphicsContext* gfxCtx) { + s32 usedOff; + View view; + Gfx* gfx; + + if (this->maxval == 0) { + osSyncPrintf(VT_FGCOL(RED)); + LOG_NUM("this->maxval", this->maxval, "../speed_meter.c", 313); + osSyncPrintf(VT_RST); + } else { + OPEN_DISPS(gfxCtx, "../speed_meter.c", 318); + + View_Init(&view, gfxCtx); + view.flags = 0xA; + + SET_FULLSCREEN_VIEWPORT(&view); + + gfx = OVERLAY_DISP; + func_800AB9EC(&view, 0xF, &gfx); + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_PATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_CONV | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + + usedOff = ((this->lrx - this->ulx) * this->val) / this->maxval + this->ulx; + DrawRec(gfx++, this->backColor, usedOff, this->uly, this->lrx, this->lry); + DrawRec(gfx++, this->foreColor, this->ulx, this->uly, usedOff, this->lry); + + gDPPipeSync(gfx++); + + OVERLAY_DISP = gfx; + CLOSE_DISPS(gfxCtx, "../speed_meter.c", 339); + } +} + +void SpeedMeter_DrawAllocEntries(SpeedMeter* meter, GraphicsContext* gfxCtx, GameState* state) { + s32 pad[2]; + u32 ulx = 30; + u32 lrx = 290; + SpeedMeterAllocEntry entry; + u32 temp; + s32 y; + TwoHeadGfxArena* thga; + u32 zeldaFreeMax; + u32 zeldaFree; + u32 zeldaAlloc; + s32 sysFreeMax; + s32 sysFree; + s32 sysAlloc; + + y = 212; + if (SREG(0) > 2) { + if (ZeldaArena_IsInitalized()) { + ZeldaArena_GetSizes(&zeldaFreeMax, &zeldaFree, &zeldaAlloc); + SpeedMeter_InitAllocEntry(&entry, zeldaFree + zeldaAlloc, zeldaAlloc, GPACK_RGBA5551(0, 0, 255, 1), + GPACK_RGBA5551(255, 255, 255, 1), ulx, lrx, y, y + 1); + SpeedMeter_DrawAllocEntry(&entry, gfxCtx); + y++; + y++; + } + } + + if (SREG(0) > 1) { + SystemArena_GetSizes((u32*)&sysFreeMax, (u32*)&sysFree, (u32*)&sysAlloc); + SpeedMeter_InitAllocEntry(&entry, sysFree + sysAlloc - state->tha.size, sysAlloc - state->tha.size, + GPACK_RGBA5551(0, 0, 255, 1), GPACK_RGBA5551(255, 128, 128, 1), ulx, lrx, y, y); + SpeedMeter_DrawAllocEntry(&entry, gfxCtx); + y++; + } + + thga = (TwoHeadGfxArena*)&state->tha; + SpeedMeter_InitAllocEntry(&entry, thga->size, thga->size - THA_GetSize((TwoHeadArena*)thga), + GPACK_RGBA5551(0, 0, 255, 1), GPACK_RGBA5551(0, 255, 0, 1), ulx, lrx, y, y); + SpeedMeter_DrawAllocEntry(&entry, gfxCtx); + y++; + + thga = &gfxCtx->polyOpa; + SpeedMeter_InitAllocEntry(&entry, thga->size, thga->size - THGA_GetSize(thga), GPACK_RGBA5551(0, 0, 255, 1), + GPACK_RGBA5551(255, 0, 255, 1), ulx, lrx, y, y); + SpeedMeter_DrawAllocEntry(&entry, gfxCtx); + y++; + + thga = &gfxCtx->polyXlu; + SpeedMeter_InitAllocEntry(&entry, thga->size, thga->size - THGA_GetSize(thga), GPACK_RGBA5551(0, 0, 255, 1), + GPACK_RGBA5551(255, 255, 0, 1), ulx, lrx, y, y); + SpeedMeter_DrawAllocEntry(&entry, gfxCtx); + y++; + + thga = &gfxCtx->overlay; + SpeedMeter_InitAllocEntry(&entry, thga->size, thga->size - THGA_GetSize(thga), GPACK_RGBA5551(0, 0, 255, 1), + GPACK_RGBA5551(255, 0, 0, 1), ulx, lrx, y, y); + SpeedMeter_DrawAllocEntry(&entry, gfxCtx); + y++; +} diff --git a/soh/src/code/sys_cfb.c b/soh/src/code/sys_cfb.c new file mode 100644 index 000000000..9412c3035 --- /dev/null +++ b/soh/src/code/sys_cfb.c @@ -0,0 +1,60 @@ +#include "global.h" +#include + +uintptr_t sSysCfbFbPtr[2]; +uintptr_t sSysCfbEnd; + +void SysCfb_Init(s32 n64dd) { + u32 screenSize; + uintptr_t tmpFbEnd; + + /* + if (osMemSize >= 0x800000) { + // "8MB or more memory is installed" + osSyncPrintf("8Mバイト以上のメモリが搭載されています\n"); + tmpFbEnd = 0x8044BE80; + if (n64dd == 1) { + osSyncPrintf("RAM 8M mode (N64DD対応)\n"); // "RAM 8M mode (N64DD compatible)" + sSysCfbEnd = 0x805FB000; + } else { + // "The margin for this version is %dK bytes" + osSyncPrintf("このバージョンのマージンは %dK バイトです\n", (0x4BC00 / 1024)); + sSysCfbEnd = tmpFbEnd; + } + } else if (osMemSize >= 0x400000) { + osSyncPrintf("RAM4M mode\n"); + sSysCfbEnd = 0x80400000; + } else { + LogUtils_HungupThread("../sys_cfb.c", 354); + } + */ + + screenSize = SCREEN_WIDTH * SCREEN_HEIGHT; + //sSysCfbEnd &= ~0x3F; + // "The final address used by the system is %08x" + osSyncPrintf("システムが使用する最終アドレスは %08x です\n", sSysCfbEnd); + //sSysCfbFbPtr[0] = sSysCfbEnd - (screenSize * 4); + //sSysCfbFbPtr[1] = sSysCfbEnd - (screenSize * 2); + sSysCfbFbPtr[0] = malloc(screenSize * 4); + sSysCfbFbPtr[1] = malloc(screenSize * 4); + + // "Frame buffer addresses are %08x and %08x" + //osSyncPrintf("フレームバッファのアドレスは %08x と %08x です\n", sSysCfbFbPtr[0], sSysCfbFbPtr[1]); +} + +void SysCfb_Reset() { + sSysCfbFbPtr[0] = 0; + sSysCfbFbPtr[1] = 0; + sSysCfbEnd = 0; +} + +uintptr_t SysCfb_GetFbPtr(s32 idx) { + if (idx < 2) { + return sSysCfbFbPtr[idx]; + } + return 0; +} + +uintptr_t SysCfb_GetFbEnd() { + return sSysCfbEnd; +} diff --git a/soh/src/code/sys_math.c b/soh/src/code/sys_math.c new file mode 100644 index 000000000..9e65cdaa3 --- /dev/null +++ b/soh/src/code/sys_math.c @@ -0,0 +1,47 @@ +#include "global.h" + +f32 sFactorialTbl[] = { 1.0f, 1.0f, 2.0f, 6.0f, 24.0f, 120.0f, 720.0f, + 5040.0f, 40320.0f, 362880.0f, 3628800.0f, 39916800.0f, 479001600.0f }; + +f32 Math_FactorialF(f32 n) { + f32 ret = 1.0f; + s32 i; + + for (i = n; i > 1; i--) { + ret *= i; + } + return ret; +} + +f32 Math_Factorial(s32 n) { + f32 ret; + s32 i; + + if ((u32)n > 12U) { + ret = sFactorialTbl[12]; + for (i = 13; i <= n; i++) { + ret *= i; + } + } else { + ret = sFactorialTbl[n]; + } + return ret; +} + +f32 Math_PowF(f32 base, s32 exp) { + f32 ret = 1.0f; + + while (exp > 0) { + exp--; + ret *= base; + } + return ret; +} + +f32 Math_SinF(f32 angle) { + return sins((s16)(angle * (32767.0f / M_PI))) * SHT_MINV; +} + +f32 Math_CosF(f32 angle) { + return coss((s16)(angle * (32767.0f / M_PI))) * SHT_MINV; +} diff --git a/soh/src/code/sys_math3d.c b/soh/src/code/sys_math3d.c new file mode 100644 index 000000000..d442b7c4a --- /dev/null +++ b/soh/src/code/sys_math3d.c @@ -0,0 +1,2154 @@ +#include "global.h" +#include "vt.h" + +s32 Math3D_LineVsLineClosestTwoPoints(Vec3f* lineAPointA, Vec3f* lineAPointB, Vec3f* lineBPointA, Vec3f* lineBPointB, + Vec3f* lineAClosestToB, Vec3f* lineBClosestToA); +s32 Math3D_TriLineIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* linePointA, + Vec3f* linePointB, Vec3f* intersect, s32 fromFront); +s32 Math3D_PlaneVsPlaneNewLine(f32 planeAA, f32 planeAB, f32 planeAC, f32 planeADist, f32 planeBA, f32 planeBB, + f32 planeBC, f32 planeBDist, InfiniteLine* intersect); +s32 Math3D_CirSquareVsTriSquare(f32 x0, f32 y0, f32 x1, f32 y1, f32 x2, f32 y2, f32 centerX, f32 centerY, f32 radius); +s32 Math3D_SphCubeVsTriCube(Vec3f* v0, Vec3f* v1, Vec3f* v2, Vec3f* center, f32 radius); + +/** + * Creates an infinite line along the intersection of the plane defined from `planeAA`x + `planeAB`y + `planeAB`z + + * `planeADist` = 0 and `planeBA`x + `planeBB`y + `planeBC`z + `planeBDist` = 0, and finds the closest point on that + * intersection to the line segment `linePointA and linePointB`, outputs the intersection to `closestPoint` + */ +s32 Math3D_PlaneVsLineSegClosestPoint(f32 planeAA, f32 planeAB, f32 planeAC, f32 planeADist, f32 planeBA, f32 planeBB, + f32 planeBC, f32 planeBDist, Vec3f* linePointA, Vec3f* linePointB, + Vec3f* closestPoint) { + static InfiniteLine planeIntersectLine; + static Linef planeIntersectSeg; + + Vec3f sp34; // unused + + if (!Math3D_PlaneVsPlaneNewLine(planeAA, planeAB, planeAC, planeADist, planeBA, planeBB, planeBC, planeBDist, + &planeIntersectLine)) { + // The planes are parallel + return false; + } + + // create a line segment on the plane. + Math_Vec3f_Copy(&planeIntersectSeg.a, &planeIntersectLine.point); + planeIntersectSeg.b.x = (planeIntersectLine.dir.x * 100.0f) + planeIntersectLine.point.x; + planeIntersectSeg.b.y = (planeIntersectLine.dir.y * 100.0f) + planeIntersectLine.point.y; + planeIntersectSeg.b.z = (planeIntersectLine.dir.z * 100.0f) + planeIntersectLine.point.z; + + // closestPoint is a point on planeIntersect, sp34 is a point on linePointA, linePointB + if (!Math3D_LineVsLineClosestTwoPoints(&planeIntersectSeg.a, &planeIntersectSeg.b, linePointA, linePointB, + closestPoint, &sp34)) { + return false; + } + return true; +} + +/** + * Finds the two points on lines A and B where the lines are closest together. + */ +s32 Math3D_LineVsLineClosestTwoPoints(Vec3f* lineAPointA, Vec3f* lineAPointB, Vec3f* lineBPointA, Vec3f* lineBPointB, + Vec3f* lineAClosestToB, Vec3f* lineBClosestToA) { + f32 sqMag; + f32 scaleB; + f32 lineAx; + f32 lineAy; + f32 lineAz; + f32 lineBx; + f32 lineBy; + f32 lineBz; + f32 compAAlongB; + f32 compBAAlongB; + Vec3f lineAPerpB; + Vec3f lineBAPerpB; + f32 tA; + f32 tB; + + lineAx = lineAPointB->x - lineAPointA->x; + lineAy = lineAPointB->y - lineAPointA->y; + lineAz = lineAPointB->z - lineAPointA->z; + + lineBx = lineBPointB->x - lineBPointA->x; + lineBy = lineBPointB->y - lineBPointA->y; + lineBz = lineBPointB->z - lineBPointA->z; + + sqMag = SQ(lineBx) + SQ(lineBy) + SQ(lineBz); + if (IS_ZERO(sqMag)) { + return false; + } + + scaleB = 1.0f / sqMag; + + compAAlongB = ((lineAx * lineBx) + (lineAy * lineBy) + (lineAz * lineBz)) * scaleB; + + compBAAlongB = ((lineBx * (lineAPointA->x - lineBPointA->x)) + (lineBy * (lineAPointA->y - lineBPointA->y)) + + (lineBz * (lineAPointA->z - lineBPointA->z))) * + scaleB; + + lineAPerpB.x = lineAx - (lineBx * compAAlongB); + lineAPerpB.y = lineAy - (lineBy * compAAlongB); + lineAPerpB.z = lineAz - (lineBz * compAAlongB); + + sqMag = SQXYZ(lineAPerpB); + if (IS_ZERO(sqMag)) { + return false; + } + + lineBAPerpB.x = (lineAPointA->x - lineBPointA->x) - (lineBx * compBAAlongB); + lineBAPerpB.y = (lineAPointA->y - lineBPointA->y) - (lineBy * compBAAlongB); + lineBAPerpB.z = (lineAPointA->z - lineBPointA->z) - (lineBz * compBAAlongB); + + tA = -DOTXYZ(lineAPerpB, lineBAPerpB) / sqMag; + lineAClosestToB->x = (lineAx * tA) + lineAPointA->x; + lineAClosestToB->y = (lineAy * tA) + lineAPointA->y; + lineAClosestToB->z = (lineAz * tA) + lineAPointA->z; + + tB = (compAAlongB * tA) + compBAAlongB; + lineBClosestToA->x = (lineBx * tB) + lineBPointA->x; + lineBClosestToA->y = (lineBy * tB) + lineBPointA->y; + lineBClosestToA->z = (lineBz * tB) + lineBPointA->z; + + return true; +} + +/** + * Determines the closest point on the line `line` to `pos`, by forming a line perpendicular from + * `point` to `line` closest point is placed in `closestPoint` + */ +void Math3D_LineClosestToPoint(Linef* line, Vec3f* pos, Vec3f* closestPoint) { + f32 dirVectorSize; + f32 t; + + dirVectorSize = Math3D_Vec3fMagnitudeSq(&line->b); + if (IS_ZERO(dirVectorSize)) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "Math3D_lineVsPosSuisenCross(): No straight line length" + osSyncPrintf("Math3D_lineVsPosSuisenCross():直線の長さがありません\n"); + osSyncPrintf("cross = pos を返します。\n"); // "Returns cross = pos." + osSyncPrintf(VT_RST); + Math_Vec3f_Copy(closestPoint, pos); + } + + t = (((pos->x - line->a.x) * line->b.x) + ((pos->y - line->a.y) * line->b.y) + ((pos->z - line->a.z) * line->b.z)) / + dirVectorSize; + closestPoint->x = (line->b.x * t) + line->a.x; + closestPoint->y = (line->b.y * t) + line->a.y; + closestPoint->z = (line->b.z * t) + line->a.z; +} + +void Math3D_FindPointOnPlaneIntersect(f32 planeAAxis1Norm, f32 planeAAxis2Norm, f32 planeBAxis1Norm, + f32 planeBAxis2Norm, f32 axis3Direction, f32 planeADist, f32 planeBDist, + f32* axis1Point, f32* axis2Point) { + *axis1Point = ((planeAAxis2Norm * planeBDist) - (planeBAxis2Norm * planeADist)) / axis3Direction; + *axis2Point = ((planeBAxis1Norm * planeADist) - (planeAAxis1Norm * planeBDist)) / axis3Direction; +} + +/** + * Creates a line between the intersections of two planes defined from `planeAA`x + `planeAB`y + `planeAC`z + + * `planeADist` = 0 and `planeBA`x + `planeBB`y + `planeBC`z + `planeBDist` = 0, and outputs the line to `intersect`. + * Returns false if the planes are parallel. + */ +s32 Math3D_PlaneVsPlaneNewLine(f32 planeAA, f32 planeAB, f32 planeAC, f32 planeADist, f32 planeBA, f32 planeBB, + f32 planeBC, f32 planeBDist, InfiniteLine* intersect) { + char pad[4]; + Vec3f planeANormal; + Vec3f planeBNormal; + f32 dirX; + f32 dirY; + f32 dirZ; + + VEC_SET(planeANormal, planeAA, planeAB, planeAC); + VEC_SET(planeBNormal, planeBA, planeBB, planeBC); + + Math3D_Vec3f_Cross(&planeANormal, &planeBNormal, &intersect->dir); + + if (IS_ZERO(intersect->dir.x) && IS_ZERO(intersect->dir.y) && IS_ZERO(intersect->dir.z)) { + // planes are parallel + return false; + } + + dirX = fabsf(intersect->dir.x); + dirY = fabsf(intersect->dir.y); + dirZ = fabsf(intersect->dir.z); + + if ((dirX >= dirY) && (dirX >= dirZ)) { + Math3D_FindPointOnPlaneIntersect(planeAB, planeAC, planeBB, planeBC, intersect->dir.x, planeADist, planeBDist, + &intersect->point.y, &intersect->point.z); + intersect->point.x = 0.0f; + } else if ((dirY >= dirX) && (dirY >= dirZ)) { + Math3D_FindPointOnPlaneIntersect(planeAC, planeAA, planeBC, planeBA, intersect->dir.y, planeADist, planeBDist, + &intersect->point.z, &intersect->point.x); + intersect->point.y = 0.0f; + } else { + Math3D_FindPointOnPlaneIntersect(planeAA, planeAB, planeBA, planeBB, intersect->dir.z, planeADist, planeBDist, + &intersect->point.x, &intersect->point.y); + intersect->point.z = 0.0f; + } + return true; +} + +/** + * Gets the closest point on the line formed from the intersection of of the planes defined from + * `planeAA`x + `planeAB`y + `planeAC`z + `planeADist` = 0 and + * `planeBA`x + `planeBB`y + `planeBC`z + `planeBDist` = 0 + * the point on the intersection line closest to `point` is placed in `closestPoint` + * returns false if the planes are parallel. + */ +s32 Math3D_PlaneVsPlaneVsLineClosestPoint(f32 planeAA, f32 planeAB, f32 planeAC, f32 planeADist, f32 planeBA, + f32 planeBB, f32 planeBC, f32 planeBDist, Vec3f* point, Vec3f* closestPoint) { + static Linef planeIntersect; + + if (!Math3D_PlaneVsPlaneNewLine(planeAA, planeAB, planeAC, planeADist, planeBA, planeBB, planeBC, planeBDist, + (InfiniteLine*)&planeIntersect)) { + return false; + } + Math3D_LineClosestToPoint(&planeIntersect, point, closestPoint); + return true; +} + +/** + * Finds a point on the line from starting point `v0`, and directional vector `dir` + * which is `dist` length from the starting point. Result is placed in `ret` + */ +void Math3D_PointOnInfiniteLine(Vec3f* v0, Vec3f* dir, f32 dist, Vec3f* ret) { + ret->x = (dir->x * dist) + v0->x; + ret->y = (dir->y * dist) + v0->y; + ret->z = (dir->z * dist) + v0->z; +} + +/** + * Splits the line segment from end points `v0` and `v1`, and splits that segment + * by `ratio` of `v0`:`v1`, places the resulting point on the line in `ret` + */ +void Math3D_LineSplitRatio(Vec3f* v0, Vec3f* v1, f32 ratio, Vec3f* ret) { + Vec3f diff; + + Math_Vec3f_Diff(v1, v0, &diff); + Math3D_PointOnInfiniteLine(v0, &diff, ratio, ret); +} + +/** + * Calculates the cosine between vectors `a` and `b` + */ +f32 Math3D_Cos(Vec3f* a, Vec3f* b) { + f32 ret; + + Math3D_CosOut(a, b, &ret); + return ret; +} + +/** + * Calculates the cosine between bectors `a` and `b` and places the result in `ret` + * returns true if the cosine cannot be calculated because the product of the magnitudes is zero + */ +s32 Math3D_CosOut(Vec3f* a, Vec3f* b, f32* dst) { + f32 magProduct; + + magProduct = Math3D_Vec3fMagnitude(a) * Math3D_Vec3fMagnitude(b); + if (IS_ZERO(magProduct)) { + *dst = 0.0f; + return true; + } + *dst = ((a->x * b->x) + (a->y * b->y) + (a->z * b->z)) / magProduct; + return false; +} + +/** + * Reflects vector `vec` across the normal vector `normal`, reflection vector is placed in + * `reflVec` + */ +void Math3D_Vec3fReflect(Vec3f* vec, Vec3f* normal, Vec3f* reflVec) { + + f32 normScaleY; + Vec3f negVec; + f32 normScaleZ; + f32 normScaleX; + f32 vecDotNorm; + + negVec.x = vec->x * -1.0f; + negVec.y = vec->y * -1.0f; + negVec.z = vec->z * -1.0f; + + vecDotNorm = Math3D_Cos(&negVec, normal); + + normScaleX = normal->x * vecDotNorm; + normScaleY = normal->y * vecDotNorm; + normScaleZ = normal->z * vecDotNorm; + + reflVec->x = ((normScaleX + vec->x) + (normScaleX + vec->x)) + negVec.x; + reflVec->y = ((normScaleY + vec->y) + (normScaleY + vec->y)) + negVec.y; + reflVec->z = ((normScaleZ + vec->z) + (normScaleZ + vec->z)) + negVec.z; +} + +/** + * Checks if the point (`x`,`y`) is contained within the square formed from (`upperLeftX`,`upperLeftY`) to + * (`lowerRightX`,`lowerRightY`) + */ +s32 Math3D_PointInSquare2D(f32 upperLeftX, f32 lowerRightX, f32 upperLeftY, f32 lowerRightY, f32 x, f32 y) { + if (x >= upperLeftX && x <= lowerRightX && y >= upperLeftY && y <= lowerRightY) { + return true; + } + return false; +} + +/** + * Checks if the square formed around the circle with center (`centerX`,`centerY`) with radius `radius` + * touches any portion of the square formed around the triangle with vertices (`x0`,`y0`), (`x1`,`y1`), + * and (`x2`,`y2`) + */ +s32 Math3D_CirSquareVsTriSquare(f32 x0, f32 y0, f32 x1, f32 y1, f32 x2, f32 y2, f32 centerX, f32 centerY, f32 radius) { + f32 minX; + f32 maxX; + f32 minY; + f32 maxY; + + minX = maxX = x0; + minY = maxY = y0; + + if (x1 < minX) { + minX = x1; + } else if (maxX < x1) { + maxX = x1; + } + + if (y1 < minY) { + minY = y1; + } else if (maxY < y1) { + maxY = y1; + } + + if (x2 < minX) { + minX = x2; + } else if (maxX < x2) { + maxX = x2; + } + + if (y2 < minY) { + minY = y2; + } else if (maxY < y2) { + maxY = y2; + } + + if ((minX - radius) <= centerX && (maxX + radius) >= centerX && (minY - radius) <= centerY && + (maxY + radius) >= centerY) { + return true; + } + return false; +} + +/** + * Checks if the cube formed around the triangle formed from `v0`, `v1`, and `v2` + * has any portion touching the cube formed around the sphere with center `center` + * and radius of `radius` + */ +s32 Math3D_SphCubeVsTriCube(Vec3f* v0, Vec3f* v1, Vec3f* v2, Vec3f* center, f32 radius) { + f32 minX; + f32 maxX; + f32 minY; + f32 maxY; + f32 minZ; + f32 maxZ; + + minX = maxX = v0->x; + minY = maxY = v0->y; + minZ = maxZ = v0->z; + + if (v1->x < minX) { + minX = v1->x; + } else if (maxX < v1->x) { + maxX = v1->x; + } + + if (v1->y < minY) { + minY = v1->y; + } else if (maxY < v1->y) { + maxY = v1->y; + } + + if (v1->z < minZ) { + minZ = v1->z; + } else if (maxZ < v1->z) { + maxZ = v1->z; + } + + if (v2->x < minX) { + minX = v2->x; + } else if (maxX < v2->x) { + maxX = v2->x; + } + + if (v2->y < minY) { + minY = v2->y; + } else if (maxY < v2->y) { + maxY = v2->y; + } + + if (v2->z < minZ) { + minZ = v2->z; + } else if (maxZ < v2->z) { + maxZ = v2->z; + } + + if ((center->x >= (minX - radius)) && (center->x <= (maxX + radius)) && (center->y >= (minY - radius)) && + (center->y <= (maxY + radius)) && (center->z >= (minZ - radius)) && (center->z <= (maxZ + radius))) { + return true; + } + + return false; +} + +/** + * Returns the distance squared between `a` and `b` on a single axis + */ +f32 Math3D_Dist1DSq(f32 a, f32 b) { + return SQ(a) + SQ(b); +} + +/** + * Returns the distance between `a` and `b` on a single axis + */ +f32 Math3D_Dist1D(f32 a, f32 b) { + return sqrtf(Math3D_Dist1DSq(a, b)); +} + +/** + * Returns the distance squared between (`x0`,`y0`) and (`x1`,`x2`) + */ +f32 Math3D_Dist2DSq(f32 x0, f32 y0, f32 x1, f32 y1) { + return Math3D_Dist1DSq(x0 - x1, y0 - y1); +} + +/** + * Returns the distance between points (`x0`,`y0`) and (`x1`,`y1`) + */ +f32 Math3D_Dist2D(f32 x0, f32 y0, f32 x1, f32 y1) { + return sqrtf(Math3D_Dist2DSq(x0, y0, x1, y1)); +} + +/** + * Returns the magntiude (length) squared of `vec` + */ +f32 Math3D_Vec3fMagnitudeSq(Vec3f* vec) { + return SQ(vec->x) + SQ(vec->y) + SQ(vec->z); +} + +/** + * Returns the magnitude(length) of `vec` + */ +f32 Math3D_Vec3fMagnitude(Vec3f* vec) { + return sqrt(Math3D_Vec3fMagnitudeSq(vec)); +} + +/** + * Returns the distance between `a` and `b` squared. + */ +f32 Math3D_Vec3fDistSq(Vec3f* a, Vec3f* b) { + Vec3f diff; + + Math_Vec3f_Diff(a, b, &diff); + return Math3D_Vec3fMagnitudeSq(&diff); +} + +/* + * Calculates the distance between points `a` and `b` + */ +f32 Math3D_Vec3f_DistXYZ(Vec3f* a, Vec3f* b) { + return Math_Vec3f_DistXYZ(a, b); +} + +/* + * Calculates the distance between `a` and `b`. + */ +f32 Math3D_DistXYZ16toF(Vec3s* a, Vec3f* b) { + Vec3f diff; + + diff.x = a->x - b->x; + diff.y = a->y - b->y; + diff.z = a->z - b->z; + return Math3D_Vec3fMagnitude(&diff); +} + +/** + * Gets the Z portion of the cross product of vectors `a - (`dx`,`dy`,z) and `b` - (`dx`,`dy`,z) + */ +f32 Math3D_Vec3fDiff_CrossZ(Vec3f* a, Vec3f* b, f32 dx, f32 dy) { + return ((a->x - dx) * (b->y - dy)) - ((a->y - dy) * (b->x - dx)); +} + +/** + * Gets the X portion of the cross product of vectors `a - (x,`dy`,`dz`) and `b` - (x,`dy`,`dz`) + */ +f32 Math3D_Vec3fDiff_CrossX(Vec3f* a, Vec3f* b, f32 dy, f32 dz) { + return ((a->y - dy) * (b->z - dz)) - ((a->z - dz) * (b->y - dy)); +} + +/** + * Gets the Y portion of the cross product of vectors `a - (`dx`,y,`dz`) and `b` - (`dx`,y,`dz`) + */ +f32 Math3D_Vec3fDiff_CrossY(Vec3f* a, Vec3f* b, f32 dz, f32 dx) { + return ((a->z - dz) * (b->x - dx)) - ((a->x - dx) * (b->z - dz)); +} + +/** + * Gets the Cross Product of vectors `a` and `b` and places the result in `ret` + */ +void Math3D_Vec3f_Cross(Vec3f* a, Vec3f* b, Vec3f* ret) { + ret->x = (a->y * b->z) - (a->z * b->y); + ret->y = (a->z * b->x) - (a->x * b->z); + ret->z = (a->x * b->y) - (a->y * b->x); +} + +/* + * Calculates the normal vector to a surface with sides `vb` - `va` and `vc` - `va` + * outputs the normal to `normal` + */ +void Math3D_SurfaceNorm(Vec3f* va, Vec3f* vb, Vec3f* vc, Vec3f* normal) { + static Vec3f abDiff; + static Vec3f acDiff; + + Math_Vec3f_Diff(vb, va, &abDiff); + Math_Vec3f_Diff(vc, va, &acDiff); + Math3D_Vec3f_Cross(&abDiff, &acDiff, normal); +} + +/** + * Creates flags relative to the faces of a cube. + */ +s32 Math3D_PointRelativeToCubeFaces(Vec3f* point, Vec3f* min, Vec3f* max) { + s32 ret = 0; + + if (point->x > max->x) { + ret = 1; + } + + if (point->x < min->x) { + ret |= 2; + } + + if (point->y > max->y) { + ret |= 4; + } + + if (point->y < min->y) { + ret |= 8; + } + + if (point->z > max->z) { + ret |= 0x10; + } + + if (point->z < min->z) { + ret |= 0x20; + } + + return ret; +} + +/** + * Creates flags of `point` relative to the edges of a cube + */ +s32 Math3D_PointRelativeToCubeEdges(Vec3f* point, Vec3f* min, Vec3f* max) { + s32 ret = 0; + + if ((-min->x + max->y) < (-point->x + point->y)) { + ret |= 1; + } + + if ((-point->x + point->y) < (-max->x + min->y)) { + ret |= 2; + } + + if ((max->x + max->y) < (point->x + point->y)) { + ret |= 4; + } + + if ((point->x + point->y) < (min->x + min->y)) { + ret |= 8; + } + + if ((-min->z + max->y) < (-point->z + point->y)) { + ret |= 0x10; + } + + if ((-point->z + point->y) < (-max->z + min->y)) { + ret |= 0x20; + } + + if ((max->z + max->y) < (point->z + point->y)) { + ret |= 0x40; + } + + if ((point->z + point->y) < (min->z + min->y)) { + ret |= 0x80; + } + + if ((-min->z + max->x) < (-point->z + point->x)) { + ret |= 0x100; + } + + if ((-point->z + point->x) < (-max->z + min->x)) { + ret |= 0x200; + } + + if ((max->z + max->x) < (point->z + point->x)) { + ret |= 0x400; + } + + if ((point->z + point->x) < (min->z + min->x)) { + ret |= 0x800; + } + return ret; +} + +/** + * Creates flags for `point` relative to the vertices of a cube + */ +s32 Math3D_PointRelativeToCubeVertices(Vec3f* point, Vec3f* min, Vec3f* max) { + s32 ret = 0; + + if ((max->x + max->y + max->z) < (point->x + point->y + point->z)) { + ret = 1; + } + + if ((-min->x + max->y + max->z) < (-point->x + point->y + point->z)) { + ret |= 2; + } + + if ((-min->x + max->y - min->z) < (-point->x + point->y - point->z)) { + ret |= 4; + } + + if ((max->x + max->y - min->z) < (point->x + point->y - point->z)) { + ret |= 8; + } + + if ((max->x - min->y + max->z) < (point->x - point->y + point->z)) { + ret |= 0x10; + } + + //! @bug: The next 2 conditions are the same check. + if ((-min->x - min->y + max->z) < (-point->x - point->y + point->z)) { + ret |= 0x20; + } + + if ((-min->x - min->y + max->z) < (-point->x - point->y + point->z)) { + ret |= 0x40; + } + + if ((-min->x - min->y - min->z) < (-point->x - point->y - point->z)) { + ret |= 0x80; + } + return ret; +} + +/** + * Checks if a line segment with endpoints `a` and `b` intersect a cube + */ +s32 Math3D_LineVsCube(Vec3f* min, Vec3f* max, Vec3f* a, Vec3f* b) { + static Vec3f triVtx0; + static Vec3f triVtx1; + static Vec3f triVtx2; + static Vec3f intersectPoint; + + s32 flags[2]; + + flags[0] = flags[1] = 0; + flags[0] = Math3D_PointRelativeToCubeFaces(a, min, max); + if (!flags[0]) { + return true; + } + + flags[1] = Math3D_PointRelativeToCubeFaces(b, min, max); + if (!flags[1]) { + return true; + } + + if (flags[0] & flags[1]) { + return false; + } + + flags[0] |= (Math3D_PointRelativeToCubeEdges(a, min, max) << 8); + flags[1] |= (Math3D_PointRelativeToCubeEdges(b, min, max) << 8); + if (flags[0] & flags[1]) { + return false; + } + + flags[0] |= (Math3D_PointRelativeToCubeVertices(a, min, max) << 0x18); + flags[1] |= (Math3D_PointRelativeToCubeVertices(b, min, max) << 0x18); + if (flags[0] & flags[1]) { + return false; + } + + // face 1 + triVtx0.x = min->x; + triVtx0.y = min->y; + triVtx0.z = min->z; + triVtx1.x = min->x; + triVtx1.y = min->y; + triVtx1.z = max->z; + triVtx2.x = min->x; + triVtx2.y = max->y; + triVtx2.z = max->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, -1.0f, 0.0f, 0.0f, min->x, a, b, &intersectPoint, 0)) { + return true; + } + + triVtx0.x = min->x; + triVtx0.y = min->y; + triVtx0.z = min->z; + triVtx1.x = min->x; + triVtx1.y = max->y; + triVtx1.z = max->z; + triVtx2.x = min->x; + triVtx2.y = max->y; + triVtx2.z = min->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, -1.0f, 0.0f, 0.0f, min->x, a, b, &intersectPoint, 0)) { + return true; + } + + // face 2 + triVtx0.x = min->x; + triVtx0.y = max->y; + triVtx0.z = max->z; + triVtx1.x = min->x; + triVtx1.y = min->y; + triVtx1.z = max->z; + triVtx2.x = max->x; + triVtx2.y = max->y; + triVtx2.z = max->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, 0.0f, 1.0f, -max->z, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = max->x; + triVtx0.y = max->y; + triVtx0.z = max->z; + triVtx1.x = min->x; + triVtx1.y = min->y; + triVtx1.z = max->z; + triVtx2.x = max->x; + //! @bug trVtx1.y should be triVtx2.y, prevents a tri on the cube from being checked. + triVtx1.y = min->y; + triVtx2.z = max->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, 0.0f, 1.0f, -max->z, a, b, &intersectPoint, 0)) { + return true; + } + + // face 3 + triVtx0.x = max->x; + triVtx0.y = max->y; + triVtx0.z = max->z; + triVtx1.x = min->x; + triVtx1.y = max->y; + triVtx1.z = min->z; + triVtx2.x = min->x; + triVtx2.y = max->y; + triVtx2.z = max->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, 1.0f, 0.0f, -max->y, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = max->x; + triVtx0.y = max->y; + triVtx0.z = max->z; + triVtx1.x = max->x; + triVtx1.y = max->y; + triVtx1.z = min->z; + triVtx2.x = min->x; + triVtx2.y = max->y; + triVtx2.z = min->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, 1.0f, 0.0f, -max->y, a, b, &intersectPoint, 0)) { + return true; + } + + // face 4 + triVtx0.x = min->x; + triVtx0.y = min->y; + triVtx0.z = min->z; + triVtx1.x = min->x; + triVtx1.y = max->y; + triVtx1.z = min->z; + triVtx2.x = max->x; + triVtx2.y = max->y; + triVtx2.z = min->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, 0.0f, -1.0f, min->z, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = min->x; + triVtx0.y = min->y; + triVtx0.z = min->z; + triVtx1.x = max->x; + triVtx1.y = max->y; + triVtx1.z = min->z; + triVtx2.x = max->x; + triVtx2.y = min->y; + triVtx2.z = min->z; + + // face 5 + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, 0.0f, -1.0f, min->z, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = min->x; + triVtx0.y = min->y; + triVtx0.z = min->z; + triVtx1.x = max->x; + triVtx1.y = min->y; + triVtx1.z = min->z; + triVtx2.x = max->x; + triVtx2.y = min->y; + triVtx2.z = max->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, -1.0f, 0.0f, min->y, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = min->x; + triVtx0.y = min->y; + triVtx0.z = min->z; + triVtx1.x = max->x; + triVtx1.y = min->y; + triVtx1.z = max->z; + triVtx2.x = min->x; + triVtx2.y = min->y; + triVtx2.z = max->z; + + // face 6 + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 0.0f, -1.0f, 0.0f, min->y, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = max->x; + triVtx0.y = max->y; + triVtx0.z = max->z; + triVtx1.x = max->x; + triVtx1.y = min->y; + triVtx1.z = min->z; + triVtx2.x = max->x; + triVtx2.y = max->y; + triVtx2.z = min->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 1.0f, 0.0f, 0.0f, -max->x, a, b, &intersectPoint, 0)) { + return true; + } + triVtx0.x = max->x; + triVtx0.y = max->y; + triVtx0.z = max->z; + triVtx1.x = max->x; + triVtx1.y = min->y; + triVtx1.z = max->z; + triVtx2.x = max->x; + triVtx2.y = min->y; + triVtx2.z = min->z; + if (Math3D_TriLineIntersect(&triVtx0, &triVtx1, &triVtx2, 1.0f, 0.0f, 0.0f, -max->x, a, b, &intersectPoint, 0)) { + return true; + } + + return false; +} + +/** + * Checks if a line segment with endpoints `a` and `b` intersect a cube + */ +s32 Math3D_LineVsCubeShort(Vec3s* min, Vec3s* max, Vec3s* a, Vec3s* b) { + static Vec3f minF; + static Vec3f maxF; + static Vec3f aF; + static Vec3f bF; + + minF.x = min->x; + minF.y = min->y; + minF.z = min->z; + maxF.x = max->x; + maxF.y = max->y; + maxF.z = max->z; + aF.x = a->x; + aF.y = a->y; + aF.z = a->z; + bF.x = b->x; + bF.y = b->y; + bF.z = b->z; + return Math3D_LineVsCube(&minF, &maxF, &aF, &bF); +} + +/** + * Rotates the xz plane around the y axis `angle` degrees. + * outputs the plane equation `a``pointOnPlane->x` + 0y + `c``pointOnPlane->z`+`d` = 0 + */ +void Math3D_RotateXZPlane(Vec3f* pointOnPlane, s16 angle, f32* a, f32* c, f32* d) { + *a = Math_SinS(angle) * 32767.0f; + *c = Math_CosS(angle) * 32767.0f; + *d = -((*a * pointOnPlane->x) + (*c * pointOnPlane->z)); +} + +/* + * Defines a plane from verticies `va`, `vb`, and `vc`. Normal components are output to + * `nx`, `ny`, and `nz`. Distance from the origin is output to `originDist` + * Satisifes the plane equation NxVx + NyVy + NzVz + D = 0 + */ +void Math3D_DefPlane(Vec3f* va, Vec3f* vb, Vec3f* vc, f32* nx, f32* ny, f32* nz, f32* originDist) { + static Vec3f normal; + + f32 normMagnitude; + f32 normMagInv; + + Math3D_SurfaceNorm(va, vb, vc, &normal); + normMagnitude = sqrtf(SQ(normal.x) + SQ(normal.y) + SQ(normal.z)); + if (!IS_ZERO(normMagnitude)) { + normMagInv = 1.0f / normMagnitude; + *nx = normal.x * normMagInv; + *ny = normal.y * normMagInv; + *nz = normal.z * normMagInv; + *originDist = -((*nx * va->x) + (*ny * va->y) + (*nz * va->z)); + } else { + *originDist = 0.0f; + *nz = 0.0f; + *ny = 0.0f; + *nx = 0.0f; + } +} + +/* + * Returns the answer to the plane equation with elements specified by arguments. + */ +f32 Math3D_Planef(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* pointOnPlane) { + return (nx * pointOnPlane->x) + (ny * pointOnPlane->y) + (nz * pointOnPlane->z) + originDist; +} + +/* + * Returns the answer to the plane equation + */ +f32 Math3D_Plane(Plane* plane, Vec3f* pointOnPlane) { + return (plane->normal.x * pointOnPlane->x) + (plane->normal.y * pointOnPlane->y) + + (plane->normal.z * pointOnPlane->z) + plane->originDist; +} + +/* + * Calculates the absolute distance from a point `p` to the plane defined as + * `nx`, `ny`, `nz`, and `originDist` + */ +f32 Math3D_UDistPlaneToPos(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* p) { + + if (IS_ZERO(sqrtf(SQ(nx) + SQ(ny) + SQ(nz)))) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "Math3DLengthPlaneAndPos(): Normal size is near zero %f %f %f" + osSyncPrintf("Math3DLengthPlaneAndPos():法線size がゼロ近いです%f %f %f\n", nx, ny, nz); + osSyncPrintf(VT_RST); + return 0.0f; + } + return fabsf(Math3D_DistPlaneToPos(nx, ny, nz, originDist, p)); +} + +/* + * Calculates the signed distance from a point `p` to a plane defined as + * `nx`, `ny`, `nz`, and `originDist` + */ +f32 Math3D_DistPlaneToPos(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* p) { + f32 normMagnitude; + + normMagnitude = sqrtf(SQ(nx) + SQ(ny) + SQ(nz)); + if (IS_ZERO(normMagnitude)) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "Math3DSignedLengthPlaneAndPos(): Normal size is close to zero %f %f %f" + osSyncPrintf("Math3DSignedLengthPlaneAndPos():法線size がゼロ近いです%f %f %f\n", nx, ny, nz); + osSyncPrintf(VT_RST); + return 0.0f; + } + return Math3D_Planef(nx, ny, nz, originDist, p) / normMagnitude; +} + +/** + * Checks if the point defined by (`z`,`x`) is within distance of the triangle defined from `v0`,`v1`, and `v2` + */ +s32 Math3D_TriChkPointParaYImpl(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 z, f32 x, f32 detMax, f32 chkDist, f32 ny) { + f32 detv0v1; + f32 detv1v2; + f32 detv2v0; + f32 distToEdgeSq; + f32 chkDistSq; + + // first check if the point is within range of the triangle. + if (!Math3D_CirSquareVsTriSquare(v0->z, v0->x, v1->z, v1->x, v2->z, v2->x, z, x, chkDist)) { + return false; + } + + // check if the point is within `chkDist` units of any vertex of the triangle. + chkDistSq = SQ(chkDist); + if (((SQ(v0->z - z) + SQ(v0->x - x)) < chkDistSq) || ((SQ(v1->z - z) + SQ(v1->x - x)) < chkDistSq) || + ((SQ(v2->z - z) + SQ(v2->x - x)) < chkDistSq)) { + + return true; + } + + // Calculate the determinant of each face of the triangle to the point. + // If all the of determinants are within abs(`detMax`), return true. + detv0v1 = ((v0->z - z) * (v1->x - x)) - ((v0->x - x) * (v1->z - z)); + detv1v2 = ((v1->z - z) * (v2->x - x)) - ((v1->x - x) * (v2->z - z)); + detv2v0 = ((v2->z - z) * (v0->x - x)) - ((v2->x - x) * (v0->z - z)); + + if (((detMax >= detv0v1) && (detMax >= detv1v2) && (detMax >= detv2v0)) || + ((-detMax <= detv0v1) && (-detMax <= detv1v2) && (-detMax <= detv2v0))) { + return true; + } + + if (fabsf(ny) > 0.5f) { + // Do a check on each face of the triangle, if the point is within `chkDist` units return true. + if (Math3D_PointDistToLine2D(z, x, v0->z, v0->x, v1->z, v1->x, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + + if (Math3D_PointDistToLine2D(z, x, v1->z, v1->x, v2->z, v2->x, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + + if (Math3D_PointDistToLine2D(z, x, v2->z, v2->x, v0->z, v0->x, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + } + return false; +} + +s32 Math3D_TriChkPointParaYDeterminate(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 z, f32 x, f32 detMax, f32 ny) { + return Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, detMax, 1.0f, ny); +} + +s32 Math3D_TriChkPointParaYSlopedY(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 z, f32 x) { + return Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, 300.0f, 1.0f, 0.6f); +} + +/** + * Performs the triangle and point check parallel to the Y axis, outputs the y coordinate of the point to `yIntersect` + */ +s32 Math3D_TriChkPointParaYIntersectDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 z, + f32 x, f32* yIntersect, f32 chkDist) { + if (IS_ZERO(ny)) { + return false; + } + + if (Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, 300.0f, chkDist, ny)) { + *yIntersect = (f32)((((-nx * x) - (nz * z)) - originDist) / ny); + return true; + } + + return false; +} + +s32 Math3D_TriChkPointParaYIntersectInsideTri(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, + f32 z, f32 x, f32* yIntersect, f32 chkDist) { + if (IS_ZERO(ny)) { + return false; + } + + if (Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, 0.0f, chkDist, ny)) { + *yIntersect = (f32)((((-nx * x) - (nz * z)) - originDist) / ny); + return true; + } + + return false; +} + +s32 Math3D_TriChkPointParaY(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 ny, f32 z, f32 x) { + if (IS_ZERO(ny)) { + return false; + } + if (Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, 300.0f, 1.0f, ny)) { + return true; + } + return false; +} + +s32 Math3D_TriChkLineSegParaYIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 z, + f32 x, f32* yIntersect, f32 y0, f32 y1) { + f32 pointADist; + f32 pointBDist; + Vec3f planePos; + + if (IS_ZERO(ny)) { + return false; + } + + planePos.x = x; + planePos.y = y0; + planePos.z = z; + + pointADist = Math3D_Planef(nx, ny, nz, originDist, &planePos); + planePos.y = y1; + pointBDist = Math3D_Planef(nx, ny, nz, originDist, &planePos); + if (((pointADist > 0.0f) && (pointBDist > 0.0f)) || ((pointADist < 0.0f) && (pointBDist < 0.0f))) { + return false; + } + + if (Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, 300.0f, 1.0f, ny)) { + *yIntersect = (((-nx * x) - (nz * z)) - originDist) / ny; + return true; + } + + return false; +} + +s32 Math3D_TriChkPointParaYDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, Plane* plane, f32 z, f32 x, f32 chkDist) { + if (IS_ZERO(plane->normal.y)) { + return false; + } + + if (Math3D_TriChkPointParaYImpl(v0, v1, v2, z, x, 0.0f, chkDist, plane->normal.y)) { + return true; + } + + return false; +} + +s32 Math3D_TriChkPointParaXImpl(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 y, f32 z, f32 detMax, f32 chkDist, f32 nx) { + f32 detv0v1; + f32 detv1v2; + f32 detv2v0; + f32 distToEdgeSq; + f32 chkDistSq; + + if (!Math3D_CirSquareVsTriSquare(v0->y, v0->z, v1->y, v1->z, v2->y, v2->z, y, z, chkDist)) { + return false; + } + + chkDistSq = SQ(chkDist); + + if (((SQ(v0->y - y) + SQ(v0->z - z)) < chkDistSq) || ((SQ(v1->y - y) + SQ(v1->z - z)) < chkDistSq) || + ((SQ(v2->y - y) + SQ(v2->z - z)) < chkDistSq)) { + return true; + } + + detv0v1 = ((v0->y - y) * (v1->z - z)) - ((v0->z - z) * (v1->y - y)); + detv1v2 = ((v1->y - y) * (v2->z - z)) - ((v1->z - z) * (v2->y - y)); + detv2v0 = ((v2->y - y) * (v0->z - z)) - ((v2->z - z) * (v0->y - y)); + + if (((detv0v1 <= detMax) && (detv1v2 <= detMax) && (detv2v0 <= detMax)) || + ((-detMax <= detv0v1) && (-detMax <= detv1v2) && (-detMax <= detv2v0))) { + return true; + } + + if (fabsf(nx) > 0.5f) { + + if (Math3D_PointDistToLine2D(y, z, v0->y, v0->z, v1->y, v1->z, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + + if (Math3D_PointDistToLine2D(y, z, v1->y, v1->z, v2->y, v2->z, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + + if (Math3D_PointDistToLine2D(y, z, v2->y, v2->z, v0->y, v0->z, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + } + return false; +} + +s32 Math3D_TriChkPointParaXDeterminate(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 y, f32 z, f32 detMax, f32 nx) { + return Math3D_TriChkPointParaXImpl(v0, v1, v2, y, z, detMax, 1.0f, nx); +} + +s32 Math3D_TriChkPointParaXIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 y, + f32 z, f32* xIntersect) { + if (IS_ZERO(nx)) { + return false; + } + + if (Math3D_TriChkPointParaXImpl(v0, v1, v2, y, z, 300.0f, 1.0f, nx)) { + *xIntersect = (((-ny * y) - (nz * z)) - originDist) / nx; + return true; + } + return false; +} + +s32 Math3D_TriChkPointParaX(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 y, f32 z) { + if (IS_ZERO(nx)) { + return false; + } + if (Math3D_TriChkPointParaXImpl(v0, v1, v2, y, z, 300.0f, 1.0f, nx)) { + return true; + } + return false; +} + +s32 Math3D_TriChkLineSegParaXIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 y, + f32 z, f32* xIntersect, f32 x0, f32 x1) { + static Vec3f planePos; + + f32 pointADist; + f32 pointBDist; + + if (IS_ZERO(nx)) { + return false; + } + + planePos.x = x0; + planePos.y = y; + planePos.z = z; + pointADist = Math3D_Planef(nx, ny, nz, originDist, &planePos); + + planePos.x = x1; + pointBDist = Math3D_Planef(nx, ny, nz, originDist, &planePos); + + if (((pointADist > 0.0f) && (pointBDist > 0.0f)) || ((pointADist < 0.0f) && (pointBDist < 0.0f))) { + return false; + } + + if (Math3D_TriChkPointParaXImpl(v0, v1, v2, y, z, 300.0f, 1.0f, nx)) { + *xIntersect = (((-ny * y) - (nz * z)) - originDist) / nx; + return true; + } + return false; +} + +s32 Math3D_TriChkPointParaXDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, Plane* plane, f32 y, f32 z, f32 chkDist) { + if (IS_ZERO(plane->normal.x)) { + return false; + } + if (Math3D_TriChkPointParaXImpl(v0, v1, v2, y, z, 0.0f, chkDist, plane->normal.x)) { + return true; + } + return false; +} + +s32 Math3D_TriChkPointParaZImpl(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 x, f32 y, f32 detMax, f32 chkDist, f32 nz) { + f32 detv0v1; + f32 detv1v2; + f32 detv2v0; + f32 distToEdgeSq; + f32 chkDistSq; + + if (!Math3D_CirSquareVsTriSquare(v0->x, v0->y, v1->x, v1->y, v2->x, v2->y, x, y, chkDist)) { + return false; + } + + chkDistSq = SQ(chkDist); + + if (((SQ(x - v0->x) + SQ(y - v0->y)) < chkDistSq) || ((SQ(x - v1->x) + SQ(y - v1->y)) < chkDistSq) || + ((SQ(x - v2->x) + SQ(y - v2->y)) < chkDistSq)) { + // Distance from any vertex to a point is less than chkDist + return true; + } + + detv0v1 = ((v0->x - x) * (v1->y - y)) - ((v0->y - y) * (v1->x - x)); + detv1v2 = ((v1->x - x) * (v2->y - y)) - ((v1->y - y) * (v2->x - x)); + detv2v0 = ((v2->x - x) * (v0->y - y)) - ((v2->y - y) * (v0->x - x)); + + if (((detMax >= detv0v1) && (detMax >= detv1v2) && (detMax >= detv2v0)) || + ((-detMax <= detv0v1) && (-detMax <= detv1v2) && (-detMax <= detv2v0))) { + return true; + } + + if (fabsf(nz) > 0.5f) { + + if (Math3D_PointDistToLine2D(x, y, v0->x, v0->y, v1->x, v1->y, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + + if (Math3D_PointDistToLine2D(x, y, v1->x, v1->y, v2->x, v2->y, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + + if (Math3D_PointDistToLine2D(x, y, v2->x, v2->y, v0->x, v0->y, &distToEdgeSq) && (distToEdgeSq < chkDistSq)) { + return true; + } + } + + return false; +} + +s32 Math3D_TriChkPointParaZDeterminate(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 x, f32 y, f32 detMax, f32 nz) { + return Math3D_TriChkPointParaZImpl(v0, v1, v2, x, y, detMax, 1.0f, nz); +} + +s32 Math3D_TriChkPointParaZIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 x, + f32 y, f32* zIntersect) { + + if (IS_ZERO(nz)) { + return false; + } + + if (Math3D_TriChkPointParaZImpl(v0, v1, v2, x, y, 300.0f, 1.0f, nz)) { + *zIntersect = (f32)((((-nx * x) - (ny * y)) - originDist) / nz); + return true; + } + return false; +} + +s32 Math3D_TriChkPointParaZ(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nz, f32 x, f32 y) { + if (IS_ZERO(nz)) { + return false; + } + if (Math3D_TriChkPointParaZImpl(v0, v1, v2, x, y, 300.0f, 1.0f, nz)) { + return true; + } + return false; +} + +s32 Math3D_TriChkLineSegParaZIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, f32 x, + f32 y, f32* zIntersect, f32 z0, f32 z1) { + static Vec3f planePos; + + f32 pointADist; + f32 pointBDist; + + if (IS_ZERO(nz)) { + return false; + } + planePos.x = x; + planePos.y = y; + planePos.z = z0; + pointADist = Math3D_Planef(nx, ny, nz, originDist, &planePos); + + planePos.z = z1; + pointBDist = Math3D_Planef(nx, ny, nz, originDist, &planePos); + if (((pointADist > 0.0f) && (pointBDist > 0.0f)) || ((pointADist < 0.0f) && (pointBDist < 0.0f))) { + // points on the line segment are on the same side of the plane + return false; + } + + if (Math3D_TriChkPointParaZImpl(v0, v1, v2, x, y, 300.0f, 1.0f, nz)) { + *zIntersect = (((-nx * x) - (ny * y)) - originDist) / nz; + return true; + } + return false; +} + +s32 Math3D_TriChkLineSegParaZDist(Vec3f* v0, Vec3f* v1, Vec3f* v2, Plane* plane, f32 x, f32 y, f32 chkDist) { + if (IS_ZERO(plane->normal.z)) { + return false; + } + if (Math3D_TriChkPointParaZImpl(v0, v1, v2, x, y, 0.0f, chkDist, plane->normal.z)) { + return true; + } + return false; +} + +s32 Math3D_LineSegFindPlaneIntersect(f32 pointADist, f32 pointBDist, Vec3f* pointA, Vec3f* pointB, Vec3f* intersect) { + f32 distDiff; + + distDiff = pointADist - pointBDist; + if (IS_ZERO(distDiff)) { + // both points lie on the plane. + *intersect = *pointB; + return false; + } + + if (pointADist == 0.0f) { + // pointA is on the plane + *intersect = *pointA; + } else if (pointBDist == 0.0f) { + // pointB is on the plane + *intersect = *pointB; + } else { + // place the point at the intersection point. + Math3D_LineSplitRatio(pointA, pointB, pointADist / distDiff, intersect); + } + return true; +} + +/** + * Determines if the line segement from `linePointA` to `linePointB` crosses the plane + * from `nx` + `ny` + `nz` + `originDist` = 0. If fromFront is set, then detection will only + * be true if point A crosses from the front of the plane + */ +s32 Math3D_LineSegVsPlane(f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* linePointA, Vec3f* linePointB, + Vec3f* intersect, s32 fromFront) { + f32 pointADist; + f32 pointBDist; + + pointADist = Math3D_Planef(nx, ny, nz, originDist, linePointA); + pointBDist = Math3D_Planef(nx, ny, nz, originDist, linePointB); + + if ((pointADist * pointBDist) > 0.0f) { + *intersect = *linePointB; + return false; + } + + if (fromFront && (pointADist < 0.0f) && (pointBDist > 0.0f)) { + *intersect = *linePointB; + return false; + } + + return Math3D_LineSegFindPlaneIntersect(pointADist, pointBDist, linePointA, linePointB, intersect); +} + +/* + * Determines if the line formed by `linePiontA` and `linePointB` intersect with Triangle formed from + * vertices `v0`, `v1`, and `v2` with normal vector `nx`, `ny`, and `nz` with plane distance from origin + * `originDist` Outputs the intersection point at to `intersect` + * Returns 1 if the line intersects with the triangle, 0 otherwise + */ +s32 Math3D_TriLineIntersect(Vec3f* v0, Vec3f* v1, Vec3f* v2, f32 nx, f32 ny, f32 nz, f32 originDist, Vec3f* linePointA, + Vec3f* linePointB, Vec3f* intersect, s32 fromFront) { + + if (!Math3D_LineSegVsPlane(nx, ny, nz, originDist, linePointA, linePointB, intersect, fromFront)) { + return false; + } + + if (((nx == 0.0f) || (Math3D_TriChkPointParaX(v0, v1, v2, nx, intersect->y, intersect->z))) && + ((ny == 0.0f) || (Math3D_TriChkPointParaY(v0, v1, v2, ny, intersect->z, intersect->x))) && + ((nz == 0.0f) || (Math3D_TriChkPointParaZ(v0, v1, v2, nz, intersect->x, intersect->y)))) { + return true; + } + + *intersect = *linePointB; + return false; +} + +/* + * Creates a TriNorm output to `tri`, and calculates the normal vector and plane from vertices + * `va`, `vb`, and `vc` + */ +void Math3D_TriNorm(TriNorm* tri, Vec3f* va, Vec3f* vb, Vec3f* vc) { + tri->vtx[0] = *va; + tri->vtx[1] = *vb; + tri->vtx[2] = *vc; + Math3D_DefPlane(va, vb, vc, &tri->plane.normal.x, &tri->plane.normal.y, &tri->plane.normal.z, + &tri->plane.originDist); +} + +/* + * Determines if point `point` lies within `sphere` + */ +s32 Math3D_PointInSph(Sphere16* sphere, Vec3f* point) { + + if (Math3D_DistXYZ16toF(&sphere->center, point) < sphere->radius) { + return true; + } + return false; +} + +/** + * Determines the distance from point (`x0`,`y0`) to the line fromed from (`x1`,`y1`) and (`x2`,`y2`) + * Distance squared is output to `lineLenSq`, returns true if the point perpendicular from (`x0`,`y0`) + * is contained within the segement between (`x1`,`y1`) and (`x2`,`y2`) + */ +s32 Math3D_PointDistToLine2D(f32 x0, f32 y0, f32 x1, f32 y1, f32 x2, f32 y2, f32* lineLenSq) { + static Vec3f perpendicularPoint; + + f32 perpendicularRatio; + f32 xDiff; + f32 distSq; + f32 yDiff; + s32 ret = false; + + xDiff = x2 - x1; + yDiff = y2 - y1; + distSq = SQ(xDiff) + SQ(yDiff); + if (IS_ZERO(distSq)) { + *lineLenSq = 0.0f; + return false; + } + + perpendicularRatio = (((x0 - x1) * xDiff) + (y0 - y1) * yDiff) / distSq; + if (perpendicularRatio >= 0.0f && perpendicularRatio <= 1.0f) { + ret = true; + } + perpendicularPoint.x = (xDiff * perpendicularRatio) + x1; + perpendicularPoint.y = (yDiff * perpendicularRatio) + y1; + *lineLenSq = SQ(perpendicularPoint.x - x0) + SQ(perpendicularPoint.y - y0); + return ret; +} + +/** + * Determines if the line `line` is touching the sphere `sphere` at any point in the line. + */ +s32 Math3D_LineVsSph(Sphere16* sphere, Linef* line) { + static Vec3f sphLinePerpendicularPoint; + + Vec3f lineDiff; + f32 temp_f0_2; + f32 lineLenSq; + + if ((Math3D_PointInSph(sphere, &line->a)) || (Math3D_PointInSph(sphere, &line->b))) { + // either point of the line is in the sphere. + return true; + } + lineDiff.x = line->b.x - line->a.x; + lineDiff.y = line->b.y - line->a.y; + lineDiff.z = line->b.z - line->a.z; + + lineLenSq = SQ(lineDiff.x) + SQ(lineDiff.y) + SQ(lineDiff.z); + if (IS_ZERO(lineLenSq)) { + // line length is "0" + return false; + } + temp_f0_2 = ((((sphere->center.x - line->a.x) * lineDiff.x) + ((sphere->center.y - line->a.y) * lineDiff.y)) + + ((sphere->center.z - line->a.z) * lineDiff.z)) / + lineLenSq; + if ((temp_f0_2 < 0.0f) || (temp_f0_2 > 1.0f)) { + return false; + } + + sphLinePerpendicularPoint.x = (lineDiff.x * temp_f0_2) + line->a.x; + sphLinePerpendicularPoint.y = (lineDiff.y * temp_f0_2) + line->a.y; + sphLinePerpendicularPoint.z = (lineDiff.z * temp_f0_2) + line->a.z; + + if (SQ(sphLinePerpendicularPoint.x - sphere->center.x) + SQ(sphLinePerpendicularPoint.y - sphere->center.y) + + SQ(sphLinePerpendicularPoint.z - sphere->center.z) <= + SQ((f32)sphere->radius)) { + return true; + } + return false; +} + +/** + * Gets the surface point of `sphere` intersecting with `tri` generated from the line formed from the + * sphere's surface to the midpoint of the line formed from the first two vertices of the tri + */ +void Math3D_GetSphVsTriIntersectPoint(Sphere16* sphere, TriNorm* tri, Vec3f* intersectPoint) { + static Vec3f v0v1Center; + static Vec3f sphereCenter; + + f32 dist; + f32 splitRatio; + + v0v1Center.x = ((tri->vtx[0].x + tri->vtx[1].x) * 0.5f); + v0v1Center.y = ((tri->vtx[0].y + tri->vtx[1].y) * 0.5f); + v0v1Center.z = ((tri->vtx[0].z + tri->vtx[1].z) * 0.5f); + sphereCenter.x = sphere->center.x; + sphereCenter.y = sphere->center.y; + sphereCenter.z = sphere->center.z; + dist = Math3D_Vec3f_DistXYZ(&v0v1Center, &sphereCenter); + // Distance from the sphere's center to the center of the line formed from v0->v1 + if (IS_ZERO(dist)) { + intersectPoint->x = sphereCenter.x; + intersectPoint->y = sphereCenter.y; + intersectPoint->z = sphereCenter.z; + return; + } + splitRatio = sphere->radius / dist; + Math3D_LineSplitRatio(&sphereCenter, &v0v1Center, splitRatio, intersectPoint); +} + +/** + * Determines if `sphere` and `tri` and touching, and outputs the intersection point to `intersectPoint` + */ +s32 Math3D_TriVsSphIntersect(Sphere16* sphere, TriNorm* tri, Vec3f* intersectPoint) { + static Linef triTestLine; + static Vec3f sphereCenter; + static Vec3f sphPlanePos; + + f32 radius; + f32 nx; + f32 ny; + f32 nz; + f32 planeDist; + + sphereCenter.x = sphere->center.x; + sphereCenter.y = sphere->center.y; + sphereCenter.z = sphere->center.z; + radius = sphere->radius; + + if (!Math3D_SphCubeVsTriCube(&tri->vtx[0], &tri->vtx[1], &tri->vtx[2], &sphereCenter, radius)) { + return false; + } + + planeDist = Math3D_UDistPlaneToPos(tri->plane.normal.x, tri->plane.normal.y, tri->plane.normal.z, + tri->plane.originDist, &sphereCenter); + if (radius < planeDist) { + // the point that lies within the plane of the triangle which is perpendicular to the sphere's center is more + // than the radius of the sphere, the plane never crosses the sphere. + return false; + } + + // tests if any of the edges of the triangle are intersecting the sphere + triTestLine.a = tri->vtx[0]; + triTestLine.b = tri->vtx[1]; + if (Math3D_LineVsSph(sphere, &triTestLine)) { + Math3D_GetSphVsTriIntersectPoint(sphere, tri, intersectPoint); + return true; + } + + triTestLine.a = tri->vtx[1]; + triTestLine.b = tri->vtx[2]; + if (Math3D_LineVsSph(sphere, &triTestLine)) { + Math3D_GetSphVsTriIntersectPoint(sphere, tri, intersectPoint); + return true; + } + + triTestLine.a = tri->vtx[2]; + triTestLine.b = tri->vtx[0]; + if (Math3D_LineVsSph(sphere, &triTestLine)) { + Math3D_GetSphVsTriIntersectPoint(sphere, tri, intersectPoint); + return true; + } + + nx = tri->plane.normal.x * planeDist; + ny = tri->plane.normal.y * planeDist; + nz = tri->plane.normal.z * planeDist; + + if (Math3D_Planef(tri->plane.normal.x, tri->plane.normal.y, tri->plane.normal.z, tri->plane.originDist, + &sphereCenter) > 0.0f) { + sphPlanePos.x = sphereCenter.x - nx; + sphPlanePos.y = sphereCenter.y - ny; + sphPlanePos.z = sphereCenter.z - nz; + } else { + sphPlanePos.x = sphereCenter.x + nx; + sphPlanePos.y = sphereCenter.y + ny; + sphPlanePos.z = sphereCenter.z + nz; + } + + if (fabsf(tri->plane.normal.y) > 0.5f) { + if (Math3D_TriChkPointParaYDeterminate(&tri->vtx[0], &tri->vtx[1], &tri->vtx[2], sphPlanePos.z, sphPlanePos.x, + 0.0f, tri->plane.normal.y)) { + Math3D_GetSphVsTriIntersectPoint(sphere, tri, intersectPoint); + return true; + } + } else if (fabsf(tri->plane.normal.x) > 0.5f) { + if (Math3D_TriChkPointParaXDeterminate(&tri->vtx[0], &tri->vtx[1], &tri->vtx[2], sphPlanePos.y, sphPlanePos.z, + 0.0f, tri->plane.normal.x)) { + Math3D_GetSphVsTriIntersectPoint(sphere, tri, intersectPoint); + return true; + } + } else if (Math3D_TriChkPointParaZDeterminate(&tri->vtx[0], &tri->vtx[1], &tri->vtx[2], sphPlanePos.x, + sphPlanePos.y, 0.0f, tri->plane.normal.z)) { + Math3D_GetSphVsTriIntersectPoint(sphere, tri, intersectPoint); + return true; + } + return false; +} + +/* + * Checks if point `point` is within cylinder `cyl` + * Returns 1 if the point is inside the cylinder, 0 otherwise. + */ +s32 Math3D_PointInCyl(Cylinder16* cyl, Vec3f* point) { + f32 bottom; + f32 top; + f32 x; + f32 z; + + x = cyl->pos.x - point->x; + z = cyl->pos.z - point->z; + bottom = (f32)cyl->pos.y + cyl->yShift; + top = cyl->height + bottom; + + if ((SQ(x) + SQ(z)) < SQ(cyl->radius) && (bottom < point->y) && (point->y < top)) { + return true; + } else { + return false; + } +} + +s32 Math3D_CylVsLineSeg(Cylinder16* cyl, Vec3f* linePointA, Vec3f* linePointB, Vec3f* intersectA, Vec3f* intersectB) { + Vec3f cylToPtA; + Vec3f cylToPtB; + Vec3f ptAToPtB; + f32 fracA; + f32 fracB; + f32 fracBase; + f32 zero = 0.0f; + f32 pad; + f32 cylRadiusSq; + f32 radSqDiff; + f32 distCent2; + f32 dot2AB; + s32 sideIntA; + s32 sideIntB; + s32 intBeyondA; + s32 intBeyondB; + s32 intFlags = 0; + Vec3f intPts[4]; + s32 count; + s32 i; + + if (Math3D_PointInCyl(cyl, linePointA) && Math3D_PointInCyl(cyl, linePointB)) { + // both points are in the cylinder + *intersectA = *linePointA; + *intersectB = *linePointB; + return 2; + } + + cylToPtA.x = linePointA->x - cyl->pos.x; + cylToPtA.y = linePointA->y - cyl->pos.y - cyl->yShift; + cylToPtA.z = linePointA->z - cyl->pos.z; + cylToPtB.x = linePointB->x - cyl->pos.x; + cylToPtB.y = linePointB->y - cyl->pos.y - cyl->yShift; + cylToPtB.z = linePointB->z - cyl->pos.z; + Math_Vec3f_Diff(&cylToPtB, &cylToPtA, &ptAToPtB); + cylRadiusSq = SQ(cyl->radius); + + /** + * This section checks for intersections with the cylinder's base and top + */ + if (!IS_ZERO(ptAToPtB.y)) { + // fraction of length along AB to reach y = 0 + fracBase = -cylToPtA.y / ptAToPtB.y; + if ((0.0f <= fracBase) && (fracBase <= 1.0f)) { + f32 baseIntX = (ptAToPtB.x * fracBase) + cylToPtA.x; + f32 baseIntZ = (ptAToPtB.z * fracBase) + cylToPtA.z; + + if (SQ(baseIntX) + SQ(baseIntZ) < cylRadiusSq) { + // adds base intersection point to intPts and sets its flag + intPts[0].x = cyl->pos.x + baseIntX; + intPts[0].y = (f32)cyl->pos.y + cyl->yShift; + intPts[0].z = cyl->pos.z + baseIntZ; + intFlags |= 1; + } + } + // fraction of length along AB to reach y = cyl->height + fracA = (cyl->height - cylToPtA.y) / ptAToPtB.y; + if ((0.0f <= fracA) && (fracA <= 1.0f)) { + f32 topIntX = ptAToPtB.x * fracA + cylToPtA.x; + f32 topIntZ = ptAToPtB.z * fracA + cylToPtA.z; + + if (SQ(topIntX) + SQ(topIntZ) < cylRadiusSq) { + // adds top intersection point to intPts and sets its flag + intPts[1].x = cyl->pos.x + topIntX; + intPts[1].y = (f32)cyl->pos.y + cyl->yShift + cyl->height; + intPts[1].z = cyl->pos.z + topIntZ; + intFlags |= 2; + } + } + } + /** + * This section finds the points of intersection of the infinite line containing AB with the side of the infinite + * cylinder containing cyl. Intersection points beyond the bounds of the segment and cylinder are filtered out + * afterward. + */ + radSqDiff = SQXZ(cylToPtA) - cylRadiusSq; + if (!IS_ZERO(2.0f * SQXZ(ptAToPtB))) { + dot2AB = 2.0f * DOTXZ(ptAToPtB, cylToPtA); + if (SQ(dot2AB) < 4.0f * SQXZ(ptAToPtB) * radSqDiff) { + // Line's closest xz-approach is outside cylinder. No intersections. + return 0; + } + if (SQ(dot2AB) - (4.0f * SQXZ(ptAToPtB) * radSqDiff) > zero) { + sideIntA = sideIntB = 1; + } else { + // Line is tangent in xz-plane. At most 1 side intersection. + sideIntA = 1; + sideIntB = 0; + } + distCent2 = sqrtf(SQ(dot2AB) - (4.0f * SQXZ(ptAToPtB) * radSqDiff)); + if (sideIntA == 1) { + // fraction of length along AB for side intersection closer to A + fracA = (distCent2 - dot2AB) / (2.0f * SQXZ(ptAToPtB)); + } + if (sideIntB == 1) { + // fraction of length along AB for side intersection closer to B + fracB = (-dot2AB - distCent2) / (2.0f * SQXZ(ptAToPtB)); + } + } else if (!IS_ZERO(2.0f * DOTXZ(ptAToPtB, cylToPtA))) { + // Used if the line segment is nearly vertical. Unclear what it's calculating. + fracA = -radSqDiff / (2.0f * DOTXZ(ptAToPtB, cylToPtA)); + sideIntA = 1; + sideIntB = 0; + } else { + return 0; + } + // checks for intersection points outside the bounds of the segment + if (!sideIntB) { + if (fracA < 0.0f || 1.0f < fracA) { + return 0; + } + } else { + intBeyondA = fracA < 0.0f || 1.0f < fracA; + intBeyondB = fracB < 0.0f || 1.0f < fracB; + if (intBeyondA && intBeyondB) { + return 0; + } + if (intBeyondA) { + sideIntA = 0; + } + if (intBeyondB) { + sideIntB = 0; + } + } + // checks for intersection points outside the bounds of the cylinder + if ((sideIntA == 1) && + ((fracA * ptAToPtB.y + cylToPtA.y) < 0.0f || cyl->height < (fracA * ptAToPtB.y + cylToPtA.y))) { + sideIntA = 0; + } + if ((sideIntB == 1) && + ((fracB * ptAToPtB.y + cylToPtA.y) < 0.0f || cyl->height < (fracB * ptAToPtB.y + cylToPtA.y))) { + sideIntB = 0; + } + if (sideIntA == 0 && sideIntB == 0) { + return 0; + } + // Adds intersection points to intPts and sets side A and side B flags + if (sideIntA == 1 && sideIntB == 1) { + intPts[2].x = (fracA * ptAToPtB.x + cylToPtA.x) + cyl->pos.x; + intPts[2].y = (fracA * ptAToPtB.y + cylToPtA.y) + cyl->pos.y + cyl->yShift; + intPts[2].z = (fracA * ptAToPtB.z + cylToPtA.z) + cyl->pos.z; + intFlags |= 4; + intPts[3].x = (fracB * ptAToPtB.x + cylToPtA.x) + cyl->pos.x; + intPts[3].y = (fracB * ptAToPtB.y + cylToPtA.y) + cyl->pos.y + cyl->yShift; + intPts[3].z = (fracB * ptAToPtB.z + cylToPtA.z) + cyl->pos.z; + intFlags |= 8; + } else if (sideIntA == 1) { + intPts[2].x = (fracA * ptAToPtB.x + cylToPtA.x) + cyl->pos.x; + intPts[2].y = (fracA * ptAToPtB.y + cylToPtA.y) + cyl->pos.y + cyl->yShift; + intPts[2].z = (fracA * ptAToPtB.z + cylToPtA.z) + cyl->pos.z; + intFlags |= 4; + } else if (sideIntB == 1) { + intPts[2].x = (fracB * ptAToPtB.x + cylToPtA.x) + cyl->pos.x; + intPts[2].y = (fracB * ptAToPtB.y + cylToPtA.y) + cyl->pos.y + cyl->yShift; + intPts[2].z = (fracB * ptAToPtB.z + cylToPtA.z) + cyl->pos.z; + intFlags |= 4; + } + + /** + * Places the found intersection points into intersectA and intersectB. IntersectA is always closer to point A + */ + for (count = 0, i = 0; i < 4; i++) { + if (intFlags & (1 << i)) { + if (count == 0) { + *intersectA = intPts[i]; + } else if (count == 1) { + if (Math3D_Vec3fDistSq(intersectA, linePointA) < Math3D_Vec3fDistSq(intersectA, &intPts[i])) { + *intersectB = intPts[i]; + } else { + *intersectB = *intersectA; + *intersectA = intPts[i]; + } + break; + } + count++; + } + } + return count; +} + +/* + * Determines if `cyl` and `tri` are touching. The point of intersection + * is placed in `intersect` Returns 1 if they are touching, 0 otherwise. + */ +s32 Math3D_CylTriVsIntersect(Cylinder16* cyl, TriNorm* tri, Vec3f* intersect) { + static Sphere16 topSphere; + static Sphere16 bottomSphere; + static Vec3f cylIntersectA; + static Vec3f cylIntersectB; + + f32 yIntersect; + f32 cylTop; + f32 cylBottom; + f32 minDistSq; + f32 radiusTodistFromCylYIntersectTov0v1; + f32 distFromPointAToIntersectASq; + Vec3f cylIntersectCenter; + Vec3f midpointv0v1; + Vec3f diffMidpointIntersect; + f32 distFromCylYIntersectTov0v1; + s32 pad; + + cylBottom = (f32)cyl->pos.y + cyl->yShift; + cylTop = cyl->height + cylBottom; + + if (((tri->vtx[0].y < cylBottom) && (tri->vtx[1].y < cylBottom) && (tri->vtx[2].y < cylBottom)) || + ((cylTop < tri->vtx[0].y) && (cylTop < tri->vtx[1].y) && (cylTop < tri->vtx[2].y))) { + // If all of the verticies are below or all of the verticies are above the cylinder. + return false; + } + + minDistSq = 1.e38f; + if (Math3D_CylVsLineSeg(cyl, &tri->vtx[0], &tri->vtx[1], &cylIntersectA, &cylIntersectB)) { + distFromPointAToIntersectASq = Math3D_Vec3fDistSq(&cylIntersectA, &tri->vtx[0]); + minDistSq = distFromPointAToIntersectASq; + *intersect = cylIntersectA; + } + + if (Math3D_CylVsLineSeg(cyl, &tri->vtx[2], &tri->vtx[1], &cylIntersectA, &cylIntersectB)) { + distFromPointAToIntersectASq = Math3D_Vec3fDistSq(&cylIntersectA, &tri->vtx[2]); + if (distFromPointAToIntersectASq < minDistSq) { + *intersect = cylIntersectA; + minDistSq = distFromPointAToIntersectASq; + } + } + + if (Math3D_CylVsLineSeg(cyl, &tri->vtx[0], &tri->vtx[2], &cylIntersectA, &cylIntersectB)) { + distFromPointAToIntersectASq = Math3D_Vec3fDistSq(&cylIntersectA, &tri->vtx[0]); + if (distFromPointAToIntersectASq < minDistSq) { + *intersect = cylIntersectA; + minDistSq = distFromPointAToIntersectASq; + } + } + + if (minDistSq != 1.e38f) { + return true; + } + + if (Math3D_TriChkLineSegParaYIntersect(&tri->vtx[0], &tri->vtx[1], &tri->vtx[2], tri->plane.normal.x, + tri->plane.normal.y, tri->plane.normal.z, tri->plane.originDist, cyl->pos.z, + cyl->pos.x, &yIntersect, cylBottom, cylTop)) { + + cylIntersectCenter.x = cyl->pos.x; + cylIntersectCenter.y = yIntersect; + cylIntersectCenter.z = cyl->pos.z; + + midpointv0v1.x = (tri->vtx[0].x + tri->vtx[1].x) * 0.5f; + midpointv0v1.y = (tri->vtx[0].y + tri->vtx[1].y) * 0.5f; + midpointv0v1.z = (tri->vtx[0].z + tri->vtx[1].z) * 0.5f; + + Math_Vec3f_Diff(&midpointv0v1, &cylIntersectCenter, &diffMidpointIntersect); + distFromCylYIntersectTov0v1 = sqrtf(SQ(diffMidpointIntersect.x) + SQ(diffMidpointIntersect.z)); + + if (IS_ZERO(distFromCylYIntersectTov0v1)) { + Math_Vec3f_Copy(intersect, &midpointv0v1); + return true; + } + + radiusTodistFromCylYIntersectTov0v1 = cyl->radius / distFromCylYIntersectTov0v1; + Math3D_PointOnInfiniteLine(&cylIntersectCenter, &diffMidpointIntersect, radiusTodistFromCylYIntersectTov0v1, + intersect); + return true; + } + + topSphere.center.x = bottomSphere.center.x = cyl->pos.x; + topSphere.center.z = bottomSphere.center.z = cyl->pos.z; + topSphere.center.y = cylTop; + bottomSphere.center.y = cylBottom; + topSphere.radius = bottomSphere.radius = cyl->radius; + + if ((Math3D_TriVsSphIntersect(&topSphere, tri, intersect)) || + (Math3D_TriVsSphIntersect(&bottomSphere, tri, intersect))) { + return true; + } + return false; +} + +/* + * Determines if `cyl` and `tri` are touching. + */ +s32 Math3D_CylVsTri(Cylinder16* cyl, TriNorm* tri) { + Vec3f intersect; + + return Math3D_CylTriVsIntersect(cyl, tri, &intersect); +} + +/* + * Deteremines if two spheres are touching. + */ +s32 Math3D_SphVsSph(Sphere16* sphereA, Sphere16* sphereB) { + f32 overlapSize; + + return Math3D_SphVsSphOverlap(sphereA, sphereB, &overlapSize); +} + +/* + * Determines if two spheres are touching. The amount that they're overlapping is placed in `overlapSize` + */ +s32 Math3D_SphVsSphOverlap(Sphere16* sphereA, Sphere16* sphereB, f32* overlapSize) { + f32 centerDist; + + return Math3D_SphVsSphOverlapCenter(sphereA, sphereB, overlapSize, ¢erDist); +} + +/* + * Determines if two spheres are touching The distance from the centers is placed in `centerDist`, + * and the amount that they're overlapping is placed in `overlapSize` + */ +s32 Math3D_SphVsSphOverlapCenter(Sphere16* sphereA, Sphere16* sphereB, f32* overlapSize, f32* centerDist) { + Vec3f diff; + + diff.x = (f32)sphereA->center.x - (f32)sphereB->center.x; + diff.y = (f32)sphereA->center.y - (f32)sphereB->center.y; + diff.z = (f32)sphereA->center.z - (f32)sphereB->center.z; + + *centerDist = sqrt(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)); + + *overlapSize = (((f32)sphereA->radius + (f32)sphereB->radius) - *centerDist); + if (*overlapSize > 0.008f) { + return true; + } + + *overlapSize = 0.0f; + return false; +} + +/** + * Checks if `sph` and `cyl` are touching, output the amount of overlap to `overlapSize` + */ +s32 Math3D_SphVsCylOverlapDist(Sphere16* sph, Cylinder16* cyl, f32* overlapSize) { + f32 centerDist; + + return Math3D_SphVsCylOverlapCenterDist(sph, cyl, overlapSize, ¢erDist); +} + +/** + * Checks if `sph` and `cyl` are touching, output the xz distance of the centers to `centerDist`, and the amount of + * overlap to `overlapSize` + */ +s32 Math3D_SphVsCylOverlapCenterDist(Sphere16* sph, Cylinder16* cyl, f32* overlapSize, f32* centerDist) { + static Cylinderf cylf; + static Spheref sphf; + + f32 x; + f32 z; + f32 combinedRadius; + f32 cylBottom; + f32 cylTop; + f32 sphBottom; + f32 sphTop; + + if (sph->radius <= 0 || cyl->radius <= 0) { + // either radius is 0 + return false; + } + sphf.center.y = sph->center.y; + sphf.radius = sph->radius; + cylf.pos.y = cyl->pos.y; + cylf.yShift = cyl->yShift; + cylf.height = cyl->height; + x = (f32)sph->center.x - cyl->pos.x; + z = (f32)sph->center.z - cyl->pos.z; + combinedRadius = (f32)sph->radius + cyl->radius; + *centerDist = sqrtf(SQ(x) + SQ(z)); + if (combinedRadius < *centerDist) { + // if the combined radii is less than the distance to the centers, they cannot be touching. + return false; + } + + cylBottom = (cylf.pos.y + cylf.yShift); + cylTop = cylBottom + cylf.height; + sphBottom = sphf.center.y - sphf.radius; + sphTop = sphf.center.y + sphf.radius; + + if ((sphTop >= cylBottom) && (sphBottom <= cylTop)) { + // if the cylinder and sphere are intersecting on the xz plane, check if they're intersecting on + // the y axis. + *overlapSize = combinedRadius - *centerDist; + return true; + } + return false; +} + +/* + * returns 1 if cylinder `ca` is outside cylinder `cb`. + * Sets `deadSpace` to the mininum space between the cylinders not occupied by the other. + */ +s32 Math3D_CylOutsideCyl(Cylinder16* ca, Cylinder16* cb, f32* deadSpace) { + f32 xzDist; + + return Math3D_CylOutsideCylDist(ca, cb, deadSpace, &xzDist); +} + +/* + * returns 1 if cylinder `ca` is outside cylinder `cb`. + * Sets `xzDist` to the xz distance between the centers of the cylinders. + * Sets `deadSpace` to the mininum space between the cylinders not occupied by the other. + */ +s32 Math3D_CylOutsideCylDist(Cylinder16* ca, Cylinder16* cb, f32* deadSpace, f32* xzDist) { + static Cylinderf caf; + static Cylinderf cbf; + + Math_Vec3s_ToVec3f(&caf.pos, &ca->pos); + caf.radius = ca->radius; + caf.yShift = ca->yShift; + caf.height = ca->height; + + Math_Vec3s_ToVec3f(&cbf.pos, &cb->pos); + cbf.radius = cb->radius; + cbf.yShift = cb->yShift; + cbf.height = cb->height; + + *xzDist = sqrtf(SQ(caf.pos.x - cbf.pos.x) + SQ(caf.pos.z - cbf.pos.z)); + + // The combined radix are within the xz distance + if ((caf.radius + cbf.radius) < *xzDist) { + return false; + } + + // top of ca < bottom of cb or top of cb < bottom of ca + if (((caf.pos.y + caf.yShift) + caf.height) < (cbf.pos.y + cbf.yShift) || + (((cbf.pos.y + cbf.yShift) + cbf.height) < (caf.pos.y + caf.yShift))) { + return false; + } + + *deadSpace = caf.radius + cbf.radius - *xzDist; + return true; +} + +/* + * Determines if triangle `ta` intersects with triangle `tb` the point of + * intersection is output to `intersect. + * Returns 1 is the triangles intersect, 0 otherwise + */ + +s32 Math3D_TriVsTriIntersect(TriNorm* ta, TriNorm* tb, Vec3f* intersect) { + f32 dist0; + f32 dist1; + f32 dist2; + + dist0 = Math3D_Plane(&ta->plane, &tb->vtx[0]); + dist1 = Math3D_Plane(&ta->plane, &tb->vtx[1]); + dist2 = Math3D_Plane(&ta->plane, &tb->vtx[2]); + + if (((dist0 > 0.0f) && (dist1 > 0.0f) && (dist2 > 0.0f)) || + (((dist0 < 0.0f) && (dist1 < 0.0f)) && (dist2 < 0.0f))) { + return false; + } + + dist0 = Math3D_Plane(&tb->plane, &ta->vtx[0]); + dist1 = Math3D_Plane(&tb->plane, &ta->vtx[1]); + dist2 = Math3D_Plane(&tb->plane, &ta->vtx[2]); + + if ((((dist0 > 0.0f) && (dist1 > 0.0f)) && (dist2 > 0.0f)) || + ((dist0 < 0.0f) && (dist1 < 0.0f) && (dist2 < 0.0f))) { + return false; + } + + if (Math3D_TriLineIntersect(&tb->vtx[0], &tb->vtx[1], &tb->vtx[2], tb->plane.normal.x, tb->plane.normal.y, + tb->plane.normal.z, tb->plane.originDist, &ta->vtx[0], &ta->vtx[1], intersect, 0)) { + return true; + } + if (Math3D_TriLineIntersect(&tb->vtx[0], &tb->vtx[1], &tb->vtx[2], tb->plane.normal.x, tb->plane.normal.y, + tb->plane.normal.z, tb->plane.originDist, &ta->vtx[1], &ta->vtx[2], intersect, 0)) { + return true; + } + if (Math3D_TriLineIntersect(&tb->vtx[0], &tb->vtx[1], &tb->vtx[2], tb->plane.normal.x, tb->plane.normal.y, + tb->plane.normal.z, tb->plane.originDist, &ta->vtx[2], &ta->vtx[0], intersect, 0)) { + return true; + } + if (Math3D_TriLineIntersect(&ta->vtx[0], &ta->vtx[1], &ta->vtx[2], ta->plane.normal.x, ta->plane.normal.y, + ta->plane.normal.z, ta->plane.originDist, &tb->vtx[0], &tb->vtx[1], intersect, + 0) == 1) { + return true; + } + if (Math3D_TriLineIntersect(&ta->vtx[0], &ta->vtx[1], &ta->vtx[2], ta->plane.normal.x, ta->plane.normal.y, + ta->plane.normal.z, ta->plane.originDist, &tb->vtx[1], &tb->vtx[2], intersect, + 0) == 1) { + return true; + } + if (Math3D_TriLineIntersect(&ta->vtx[0], &ta->vtx[1], &ta->vtx[2], ta->plane.normal.x, ta->plane.normal.y, + ta->plane.normal.z, ta->plane.originDist, &tb->vtx[2], &tb->vtx[0], intersect, + 0) == 1) { + return true; + } + return false; +} + +s32 Math3D_XZInSphere(Sphere16* sphere, f32 x, f32 z) { + f32 xDiff; + f32 zDiff; + + xDiff = sphere->center.x - x; + zDiff = sphere->center.z - z; + if ((SQ(xDiff) + SQ(zDiff)) <= SQ(sphere->radius)) { + return true; + } + return false; +} + +s32 Math3D_XYInSphere(Sphere16* sphere, f32 x, f32 y) { + f32 xDiff; + f32 yDiff; + + xDiff = sphere->center.x - x; + yDiff = sphere->center.y - y; + if ((SQ(xDiff) + SQ(yDiff)) <= SQ(sphere->radius)) { + return true; + } + return false; +} + +s32 Math3D_YZInSphere(Sphere16* sphere, f32 y, f32 z) { + f32 yDiff; + f32 zDiff; + + yDiff = sphere->center.y - y; + zDiff = sphere->center.z - z; + if ((SQ(yDiff) + SQ(zDiff)) <= SQ(sphere->radius)) { + return true; + } + return false; +} + +void Math3D_DrawSphere(GlobalContext* globalCtx, Sphere16* sph) { +} + +void Math3D_DrawCylinder(GlobalContext* globalCtx, Cylinder16* cyl) { +} diff --git a/soh/src/code/sys_math_atan.c b/soh/src/code/sys_math_atan.c new file mode 100644 index 000000000..02f515327 --- /dev/null +++ b/soh/src/code/sys_math_atan.c @@ -0,0 +1,131 @@ +#include "global.h" + +static u16 sATan2Tbl[] = { + 0x0000, 0x000A, 0x0014, 0x001F, 0x0029, 0x0033, 0x003D, 0x0047, 0x0051, 0x005C, 0x0066, 0x0070, 0x007A, 0x0084, + 0x008F, 0x0099, 0x00A3, 0x00AD, 0x00B7, 0x00C2, 0x00CC, 0x00D6, 0x00E0, 0x00EA, 0x00F4, 0x00FF, 0x0109, 0x0113, + 0x011D, 0x0127, 0x0131, 0x013C, 0x0146, 0x0150, 0x015A, 0x0164, 0x016F, 0x0179, 0x0183, 0x018D, 0x0197, 0x01A1, + 0x01AC, 0x01B6, 0x01C0, 0x01CA, 0x01D4, 0x01DE, 0x01E9, 0x01F3, 0x01FD, 0x0207, 0x0211, 0x021B, 0x0226, 0x0230, + 0x023A, 0x0244, 0x024E, 0x0258, 0x0262, 0x026D, 0x0277, 0x0281, 0x028B, 0x0295, 0x029F, 0x02A9, 0x02B4, 0x02BE, + 0x02C8, 0x02D2, 0x02DC, 0x02E6, 0x02F0, 0x02FB, 0x0305, 0x030F, 0x0319, 0x0323, 0x032D, 0x0337, 0x0341, 0x034C, + 0x0356, 0x0360, 0x036A, 0x0374, 0x037E, 0x0388, 0x0392, 0x039C, 0x03A7, 0x03B1, 0x03BB, 0x03C5, 0x03CF, 0x03D9, + 0x03E3, 0x03ED, 0x03F7, 0x0401, 0x040C, 0x0416, 0x0420, 0x042A, 0x0434, 0x043E, 0x0448, 0x0452, 0x045C, 0x0466, + 0x0470, 0x047A, 0x0484, 0x048E, 0x0499, 0x04A3, 0x04AD, 0x04B7, 0x04C1, 0x04CB, 0x04D5, 0x04DF, 0x04E9, 0x04F3, + 0x04FD, 0x0507, 0x0511, 0x051B, 0x0525, 0x052F, 0x0539, 0x0543, 0x054D, 0x0557, 0x0561, 0x056B, 0x0575, 0x057F, + 0x0589, 0x0593, 0x059D, 0x05A7, 0x05B1, 0x05BB, 0x05C5, 0x05CF, 0x05D9, 0x05E3, 0x05ED, 0x05F7, 0x0601, 0x060B, + 0x0615, 0x061F, 0x0629, 0x0633, 0x063D, 0x0647, 0x0651, 0x065B, 0x0665, 0x066E, 0x0678, 0x0682, 0x068C, 0x0696, + 0x06A0, 0x06AA, 0x06B4, 0x06BE, 0x06C8, 0x06D2, 0x06DC, 0x06E5, 0x06EF, 0x06F9, 0x0703, 0x070D, 0x0717, 0x0721, + 0x072B, 0x0735, 0x073E, 0x0748, 0x0752, 0x075C, 0x0766, 0x0770, 0x077A, 0x0783, 0x078D, 0x0797, 0x07A1, 0x07AB, + 0x07B5, 0x07BE, 0x07C8, 0x07D2, 0x07DC, 0x07E6, 0x07EF, 0x07F9, 0x0803, 0x080D, 0x0817, 0x0820, 0x082A, 0x0834, + 0x083E, 0x0848, 0x0851, 0x085B, 0x0865, 0x086F, 0x0878, 0x0882, 0x088C, 0x0896, 0x089F, 0x08A9, 0x08B3, 0x08BD, + 0x08C6, 0x08D0, 0x08DA, 0x08E3, 0x08ED, 0x08F7, 0x0901, 0x090A, 0x0914, 0x091E, 0x0927, 0x0931, 0x093B, 0x0944, + 0x094E, 0x0958, 0x0961, 0x096B, 0x0975, 0x097E, 0x0988, 0x0992, 0x099B, 0x09A5, 0x09AE, 0x09B8, 0x09C2, 0x09CB, + 0x09D5, 0x09DE, 0x09E8, 0x09F2, 0x09FB, 0x0A05, 0x0A0E, 0x0A18, 0x0A22, 0x0A2B, 0x0A35, 0x0A3E, 0x0A48, 0x0A51, + 0x0A5B, 0x0A64, 0x0A6E, 0x0A77, 0x0A81, 0x0A8B, 0x0A94, 0x0A9E, 0x0AA7, 0x0AB1, 0x0ABA, 0x0AC4, 0x0ACD, 0x0AD7, + 0x0AE0, 0x0AE9, 0x0AF3, 0x0AFC, 0x0B06, 0x0B0F, 0x0B19, 0x0B22, 0x0B2C, 0x0B35, 0x0B3F, 0x0B48, 0x0B51, 0x0B5B, + 0x0B64, 0x0B6E, 0x0B77, 0x0B80, 0x0B8A, 0x0B93, 0x0B9D, 0x0BA6, 0x0BAF, 0x0BB9, 0x0BC2, 0x0BCB, 0x0BD5, 0x0BDE, + 0x0BE7, 0x0BF1, 0x0BFA, 0x0C03, 0x0C0D, 0x0C16, 0x0C1F, 0x0C29, 0x0C32, 0x0C3B, 0x0C45, 0x0C4E, 0x0C57, 0x0C60, + 0x0C6A, 0x0C73, 0x0C7C, 0x0C86, 0x0C8F, 0x0C98, 0x0CA1, 0x0CAB, 0x0CB4, 0x0CBD, 0x0CC6, 0x0CCF, 0x0CD9, 0x0CE2, + 0x0CEB, 0x0CF4, 0x0CFD, 0x0D07, 0x0D10, 0x0D19, 0x0D22, 0x0D2B, 0x0D34, 0x0D3E, 0x0D47, 0x0D50, 0x0D59, 0x0D62, + 0x0D6B, 0x0D74, 0x0D7D, 0x0D87, 0x0D90, 0x0D99, 0x0DA2, 0x0DAB, 0x0DB4, 0x0DBD, 0x0DC6, 0x0DCF, 0x0DD8, 0x0DE1, + 0x0DEA, 0x0DF3, 0x0DFC, 0x0E05, 0x0E0F, 0x0E18, 0x0E21, 0x0E2A, 0x0E33, 0x0E3C, 0x0E45, 0x0E4E, 0x0E56, 0x0E5F, + 0x0E68, 0x0E71, 0x0E7A, 0x0E83, 0x0E8C, 0x0E95, 0x0E9E, 0x0EA7, 0x0EB0, 0x0EB9, 0x0EC2, 0x0ECB, 0x0ED4, 0x0EDC, + 0x0EE5, 0x0EEE, 0x0EF7, 0x0F00, 0x0F09, 0x0F12, 0x0F1B, 0x0F23, 0x0F2C, 0x0F35, 0x0F3E, 0x0F47, 0x0F50, 0x0F58, + 0x0F61, 0x0F6A, 0x0F73, 0x0F7C, 0x0F84, 0x0F8D, 0x0F96, 0x0F9F, 0x0FA7, 0x0FB0, 0x0FB9, 0x0FC2, 0x0FCA, 0x0FD3, + 0x0FDC, 0x0FE5, 0x0FED, 0x0FF6, 0x0FFF, 0x1007, 0x1010, 0x1019, 0x1021, 0x102A, 0x1033, 0x103B, 0x1044, 0x104D, + 0x1055, 0x105E, 0x1067, 0x106F, 0x1078, 0x1080, 0x1089, 0x1092, 0x109A, 0x10A3, 0x10AB, 0x10B4, 0x10BC, 0x10C5, + 0x10CE, 0x10D6, 0x10DF, 0x10E7, 0x10F0, 0x10F8, 0x1101, 0x1109, 0x1112, 0x111A, 0x1123, 0x112B, 0x1134, 0x113C, + 0x1145, 0x114D, 0x1156, 0x115E, 0x1166, 0x116F, 0x1177, 0x1180, 0x1188, 0x1191, 0x1199, 0x11A1, 0x11AA, 0x11B2, + 0x11BB, 0x11C3, 0x11CB, 0x11D4, 0x11DC, 0x11E4, 0x11ED, 0x11F5, 0x11FD, 0x1206, 0x120E, 0x1216, 0x121F, 0x1227, + 0x122F, 0x1237, 0x1240, 0x1248, 0x1250, 0x1259, 0x1261, 0x1269, 0x1271, 0x127A, 0x1282, 0x128A, 0x1292, 0x129A, + 0x12A3, 0x12AB, 0x12B3, 0x12BB, 0x12C3, 0x12CC, 0x12D4, 0x12DC, 0x12E4, 0x12EC, 0x12F4, 0x12FC, 0x1305, 0x130D, + 0x1315, 0x131D, 0x1325, 0x132D, 0x1335, 0x133D, 0x1345, 0x134D, 0x1355, 0x135E, 0x1366, 0x136E, 0x1376, 0x137E, + 0x1386, 0x138E, 0x1396, 0x139E, 0x13A6, 0x13AE, 0x13B6, 0x13BE, 0x13C6, 0x13CE, 0x13D6, 0x13DE, 0x13E6, 0x13ED, + 0x13F5, 0x13FD, 0x1405, 0x140D, 0x1415, 0x141D, 0x1425, 0x142D, 0x1435, 0x143D, 0x1444, 0x144C, 0x1454, 0x145C, + 0x1464, 0x146C, 0x1473, 0x147B, 0x1483, 0x148B, 0x1493, 0x149B, 0x14A2, 0x14AA, 0x14B2, 0x14BA, 0x14C1, 0x14C9, + 0x14D1, 0x14D9, 0x14E0, 0x14E8, 0x14F0, 0x14F8, 0x14FF, 0x1507, 0x150F, 0x1516, 0x151E, 0x1526, 0x152D, 0x1535, + 0x153D, 0x1544, 0x154C, 0x1554, 0x155B, 0x1563, 0x156B, 0x1572, 0x157A, 0x1581, 0x1589, 0x1591, 0x1598, 0x15A0, + 0x15A7, 0x15AF, 0x15B7, 0x15BE, 0x15C6, 0x15CD, 0x15D5, 0x15DC, 0x15E4, 0x15EB, 0x15F3, 0x15FA, 0x1602, 0x1609, + 0x1611, 0x1618, 0x1620, 0x1627, 0x162F, 0x1636, 0x163E, 0x1645, 0x164C, 0x1654, 0x165B, 0x1663, 0x166A, 0x1671, + 0x1679, 0x1680, 0x1688, 0x168F, 0x1696, 0x169E, 0x16A5, 0x16AC, 0x16B4, 0x16BB, 0x16C2, 0x16CA, 0x16D1, 0x16D8, + 0x16E0, 0x16E7, 0x16EE, 0x16F6, 0x16FD, 0x1704, 0x170B, 0x1713, 0x171A, 0x1721, 0x1728, 0x1730, 0x1737, 0x173E, + 0x1745, 0x174C, 0x1754, 0x175B, 0x1762, 0x1769, 0x1770, 0x1778, 0x177F, 0x1786, 0x178D, 0x1794, 0x179B, 0x17A2, + 0x17AA, 0x17B1, 0x17B8, 0x17BF, 0x17C6, 0x17CD, 0x17D4, 0x17DB, 0x17E2, 0x17E9, 0x17F0, 0x17F7, 0x17FE, 0x1806, + 0x180D, 0x1814, 0x181B, 0x1822, 0x1829, 0x1830, 0x1837, 0x183E, 0x1845, 0x184C, 0x1853, 0x185A, 0x1860, 0x1867, + 0x186E, 0x1875, 0x187C, 0x1883, 0x188A, 0x1891, 0x1898, 0x189F, 0x18A6, 0x18AD, 0x18B3, 0x18BA, 0x18C1, 0x18C8, + 0x18CF, 0x18D6, 0x18DD, 0x18E3, 0x18EA, 0x18F1, 0x18F8, 0x18FF, 0x1906, 0x190C, 0x1913, 0x191A, 0x1921, 0x1928, + 0x192E, 0x1935, 0x193C, 0x1943, 0x1949, 0x1950, 0x1957, 0x195D, 0x1964, 0x196B, 0x1972, 0x1978, 0x197F, 0x1986, + 0x198C, 0x1993, 0x199A, 0x19A0, 0x19A7, 0x19AE, 0x19B4, 0x19BB, 0x19C2, 0x19C8, 0x19CF, 0x19D5, 0x19DC, 0x19E3, + 0x19E9, 0x19F0, 0x19F6, 0x19FD, 0x1A04, 0x1A0A, 0x1A11, 0x1A17, 0x1A1E, 0x1A24, 0x1A2B, 0x1A31, 0x1A38, 0x1A3E, + 0x1A45, 0x1A4B, 0x1A52, 0x1A58, 0x1A5F, 0x1A65, 0x1A6C, 0x1A72, 0x1A79, 0x1A7F, 0x1A86, 0x1A8C, 0x1A93, 0x1A99, + 0x1A9F, 0x1AA6, 0x1AAC, 0x1AB3, 0x1AB9, 0x1AC0, 0x1AC6, 0x1ACC, 0x1AD3, 0x1AD9, 0x1ADF, 0x1AE6, 0x1AEC, 0x1AF2, + 0x1AF9, 0x1AFF, 0x1B05, 0x1B0C, 0x1B12, 0x1B18, 0x1B1F, 0x1B25, 0x1B2B, 0x1B32, 0x1B38, 0x1B3E, 0x1B44, 0x1B4B, + 0x1B51, 0x1B57, 0x1B5D, 0x1B64, 0x1B6A, 0x1B70, 0x1B76, 0x1B7D, 0x1B83, 0x1B89, 0x1B8F, 0x1B95, 0x1B9C, 0x1BA2, + 0x1BA8, 0x1BAE, 0x1BB4, 0x1BBA, 0x1BC1, 0x1BC7, 0x1BCD, 0x1BD3, 0x1BD9, 0x1BDF, 0x1BE5, 0x1BEB, 0x1BF2, 0x1BF8, + 0x1BFE, 0x1C04, 0x1C0A, 0x1C10, 0x1C16, 0x1C1C, 0x1C22, 0x1C28, 0x1C2E, 0x1C34, 0x1C3A, 0x1C40, 0x1C46, 0x1C4C, + 0x1C52, 0x1C58, 0x1C5E, 0x1C64, 0x1C6A, 0x1C70, 0x1C76, 0x1C7C, 0x1C82, 0x1C88, 0x1C8E, 0x1C94, 0x1C9A, 0x1CA0, + 0x1CA6, 0x1CAC, 0x1CB2, 0x1CB8, 0x1CBE, 0x1CC3, 0x1CC9, 0x1CCF, 0x1CD5, 0x1CDB, 0x1CE1, 0x1CE7, 0x1CED, 0x1CF3, + 0x1CF8, 0x1CFE, 0x1D04, 0x1D0A, 0x1D10, 0x1D16, 0x1D1B, 0x1D21, 0x1D27, 0x1D2D, 0x1D33, 0x1D38, 0x1D3E, 0x1D44, + 0x1D4A, 0x1D4F, 0x1D55, 0x1D5B, 0x1D61, 0x1D66, 0x1D6C, 0x1D72, 0x1D78, 0x1D7D, 0x1D83, 0x1D89, 0x1D8E, 0x1D94, + 0x1D9A, 0x1DA0, 0x1DA5, 0x1DAB, 0x1DB1, 0x1DB6, 0x1DBC, 0x1DC2, 0x1DC7, 0x1DCD, 0x1DD3, 0x1DD8, 0x1DDE, 0x1DE3, + 0x1DE9, 0x1DEF, 0x1DF4, 0x1DFA, 0x1DFF, 0x1E05, 0x1E0B, 0x1E10, 0x1E16, 0x1E1B, 0x1E21, 0x1E26, 0x1E2C, 0x1E32, + 0x1E37, 0x1E3D, 0x1E42, 0x1E48, 0x1E4D, 0x1E53, 0x1E58, 0x1E5E, 0x1E63, 0x1E69, 0x1E6E, 0x1E74, 0x1E79, 0x1E7F, + 0x1E84, 0x1E8A, 0x1E8F, 0x1E94, 0x1E9A, 0x1E9F, 0x1EA5, 0x1EAA, 0x1EB0, 0x1EB5, 0x1EBA, 0x1EC0, 0x1EC5, 0x1ECB, + 0x1ED0, 0x1ED5, 0x1EDB, 0x1EE0, 0x1EE6, 0x1EEB, 0x1EF0, 0x1EF6, 0x1EFB, 0x1F00, 0x1F06, 0x1F0B, 0x1F10, 0x1F16, + 0x1F1B, 0x1F20, 0x1F26, 0x1F2B, 0x1F30, 0x1F36, 0x1F3B, 0x1F40, 0x1F45, 0x1F4B, 0x1F50, 0x1F55, 0x1F5A, 0x1F60, + 0x1F65, 0x1F6A, 0x1F6F, 0x1F75, 0x1F7A, 0x1F7F, 0x1F84, 0x1F8A, 0x1F8F, 0x1F94, 0x1F99, 0x1F9E, 0x1FA4, 0x1FA9, + 0x1FAE, 0x1FB3, 0x1FB8, 0x1FBD, 0x1FC3, 0x1FC8, 0x1FCD, 0x1FD2, 0x1FD7, 0x1FDC, 0x1FE1, 0x1FE6, 0x1FEC, 0x1FF1, + 0x1FF6, 0x1FFB, 0x2000, +}; + +u16 Math_GetAtan2Tbl(f32 x, f32 y) { + s32 tblIdx = ((x / y) * 1024.0f) + 0.5f; + u16 ret; + + if (y == 0.0f) { + ret = sATan2Tbl[0]; + } else if (tblIdx >= ARRAY_COUNT(sATan2Tbl)) { + ret = sATan2Tbl[0]; + } else { + ret = sATan2Tbl[tblIdx]; + } + return ret; +} + +s16 Math_Atan2S(f32 x, f32 y) { + s32 ret; + + if (y >= 0.0f) { + if (x >= 0.0f) { + if (y <= x) { + ret = Math_GetAtan2Tbl(y, x); + } else { + ret = 0x4000 - Math_GetAtan2Tbl(x, y); + } + } else { + if (-x < y) { + ret = Math_GetAtan2Tbl(-x, y) + 0x4000; + } else { + ret = 0x8000 - Math_GetAtan2Tbl(y, -x); + } + } + } else { + if (x < 0.0f) { + if (-y <= -x) { + ret = Math_GetAtan2Tbl(-y, -x) + 0x8000; + } else { + ret = 0xC000 - Math_GetAtan2Tbl(-x, -y); + } + } else { + if (x < -y) { + ret = Math_GetAtan2Tbl(x, -y) + 0xC000; + } else { + ret = -Math_GetAtan2Tbl(-y, x); + } + } + } + return ret; +} + +f32 Math_Atan2F(f32 x, f32 y) { + return Math_Atan2S(x, y) * (M_PI / 32768.0f); +} diff --git a/soh/src/code/sys_matrix.c b/soh/src/code/sys_matrix.c new file mode 100644 index 000000000..9d7c73967 --- /dev/null +++ b/soh/src/code/sys_matrix.c @@ -0,0 +1,1035 @@ +#include "global.h" + +// clang-format off +Mtx gMtxClear = { + 65536, 0, 1, 0, + 0, 65536, 0, 1, + 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +MtxF gMtxFClear = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; +// clang-format on + +MtxF* sMatrixStack; // "Matrix_stack" +MtxF* sCurrentMatrix; // "Matrix_now" + +void Matrix_Init(GameState* gameState) { + sCurrentMatrix = GameState_Alloc(gameState, 20 * sizeof(MtxF), "../sys_matrix.c", 153); + sMatrixStack = sCurrentMatrix; +} + +void Matrix_Push(void) { + Matrix_MtxFCopy(sCurrentMatrix + 1, sCurrentMatrix); + sCurrentMatrix++; +} + +void Matrix_Pop(void) { + sCurrentMatrix--; + ASSERT(sCurrentMatrix >= sMatrixStack, "Matrix_now >= Matrix_stack", "../sys_matrix.c", 176); +} + +void Matrix_Get(MtxF* dest) { + Matrix_MtxFCopy(dest, sCurrentMatrix); +} + +void Matrix_Put(MtxF* src) { + Matrix_MtxFCopy(sCurrentMatrix, src); +} + +MtxF* Matrix_GetCurrent(void) { + return sCurrentMatrix; +} + +void Matrix_Mult(MtxF* mf, u8 mode) { + MtxF* cmf = Matrix_GetCurrent(); + + if (mode == MTXMODE_APPLY) { + SkinMatrix_MtxFMtxFMult(cmf, mf, cmf); + } else { + Matrix_MtxFCopy(sCurrentMatrix, mf); + } +} + +void Matrix_Translate(f32 x, f32 y, f32 z, u8 mode) { + MtxF* cmf = sCurrentMatrix; + f32 tx; + f32 ty; + + if (mode == MTXMODE_APPLY) { + tx = cmf->xx; + ty = cmf->xy; + cmf->xw += tx * x + ty * y + cmf->xz * z; + tx = cmf->yx; + ty = cmf->yy; + cmf->yw += tx * x + ty * y + cmf->yz * z; + tx = cmf->zx; + ty = cmf->zy; + cmf->zw += tx * x + ty * y + cmf->zz * z; + tx = cmf->wx; + ty = cmf->wy; + cmf->ww += tx * x + ty * y + cmf->wz * z; + } else { + SkinMatrix_SetTranslate(cmf, x, y, z); + } +} + +void Matrix_Scale(f32 x, f32 y, f32 z, u8 mode) { + MtxF* cmf = sCurrentMatrix; + + if (mode == MTXMODE_APPLY) { + cmf->xx *= x; + cmf->yx *= x; + cmf->zx *= x; + cmf->xy *= y; + cmf->yy *= y; + cmf->zy *= y; + cmf->xz *= z; + cmf->yz *= z; + cmf->zz *= z; + cmf->wx *= x; + cmf->wy *= y; + cmf->wz *= z; + } else { + SkinMatrix_SetScale(cmf, x, y, z); + } +} + +void Matrix_RotateX(f32 x, u8 mode) { + MtxF* cmf; + f32 sin; + f32 cos; + f32 temp1; + f32 temp2; + + if (mode == MTXMODE_APPLY) { + if (x != 0) { + cmf = sCurrentMatrix; + + sin = sinf(x); + cos = cosf(x); + + temp1 = cmf->xy; + temp2 = cmf->xz; + cmf->xy = temp1 * cos + temp2 * sin; + cmf->xz = temp2 * cos - temp1 * sin; + + temp1 = cmf->yy; + temp2 = cmf->yz; + cmf->yy = temp1 * cos + temp2 * sin; + cmf->yz = temp2 * cos - temp1 * sin; + + temp1 = cmf->zy; + temp2 = cmf->zz; + cmf->zy = temp1 * cos + temp2 * sin; + cmf->zz = temp2 * cos - temp1 * sin; + + temp1 = cmf->wy; + temp2 = cmf->wz; + cmf->wy = temp1 * cos + temp2 * sin; + cmf->wz = temp2 * cos - temp1 * sin; + } + } else { + cmf = sCurrentMatrix; + + if (x != 0) { + sin = sinf(x); + cos = cosf(x); + } else { + sin = 0.0f; + cos = 1.0f; + } + + cmf->yx = 0.0f; + cmf->zx = 0.0f; + cmf->wx = 0.0f; + cmf->xy = 0.0f; + cmf->wy = 0.0f; + cmf->xz = 0.0f; + cmf->wz = 0.0f; + cmf->xw = 0.0f; + cmf->yw = 0.0f; + cmf->zw = 0.0f; + cmf->xx = 1.0f; + cmf->ww = 1.0f; + cmf->yy = cos; + cmf->zz = cos; + cmf->zy = sin; + cmf->yz = -sin; + } +} + +void Matrix_RotateY(f32 y, u8 mode) { + MtxF* cmf; + f32 sin; + f32 cos; + f32 temp1; + f32 temp2; + + if (mode == MTXMODE_APPLY) { + if (y != 0) { + cmf = sCurrentMatrix; + + sin = sinf(y); + cos = cosf(y); + + temp1 = cmf->xx; + temp2 = cmf->xz; + cmf->xx = temp1 * cos - temp2 * sin; + cmf->xz = temp1 * sin + temp2 * cos; + + temp1 = cmf->yx; + temp2 = cmf->yz; + cmf->yx = temp1 * cos - temp2 * sin; + cmf->yz = temp1 * sin + temp2 * cos; + + temp1 = cmf->zx; + temp2 = cmf->zz; + cmf->zx = temp1 * cos - temp2 * sin; + cmf->zz = temp1 * sin + temp2 * cos; + + temp1 = cmf->wx; + temp2 = cmf->wz; + cmf->wx = temp1 * cos - temp2 * sin; + cmf->wz = temp1 * sin + temp2 * cos; + } + } else { + cmf = sCurrentMatrix; + + if (y != 0) { + sin = sinf(y); + cos = cosf(y); + } else { + sin = 0.0f; + cos = 1.0f; + } + + cmf->yx = 0.0f; + cmf->wx = 0.0f; + cmf->xy = 0.0f; + cmf->zy = 0.0f; + cmf->wy = 0.0f; + cmf->yz = 0.0f; + cmf->wz = 0.0f; + cmf->xw = 0.0f; + cmf->yw = 0.0f; + cmf->zw = 0.0f; + cmf->yy = 1.0f; + cmf->ww = 1.0f; + cmf->xx = cos; + cmf->zz = cos; + cmf->zx = -sin; + cmf->xz = sin; + } +} + +void Matrix_RotateZ(f32 z, u8 mode) { + MtxF* cmf; + f32 sin; + f32 cos; + f32 temp1; + f32 temp2; + + if (mode == MTXMODE_APPLY) { + if (z != 0) { + cmf = sCurrentMatrix; + + sin = sinf(z); + cos = cosf(z); + + temp1 = cmf->xx; + temp2 = cmf->xy; + cmf->xx = temp1 * cos + temp2 * sin; + cmf->xy = temp2 * cos - temp1 * sin; + + temp1 = cmf->yx; + temp2 = cmf->yy; + cmf->yx = temp1 * cos + temp2 * sin; + cmf->yy = temp2 * cos - temp1 * sin; + + temp1 = cmf->zx; + temp2 = cmf->zy; + cmf->zx = temp1 * cos + temp2 * sin; + cmf->zy = temp2 * cos - temp1 * sin; + + temp1 = cmf->wx; + temp2 = cmf->wy; + cmf->wx = temp1 * cos + temp2 * sin; + cmf->wy = temp2 * cos - temp1 * sin; + } + } else { + cmf = sCurrentMatrix; + + if (z != 0) { + sin = sinf(z); + cos = cosf(z); + } else { + sin = 0.0f; + cos = 1.0f; + } + + cmf->zx = 0.0f; + cmf->wx = 0.0f; + cmf->zy = 0.0f; + cmf->wy = 0.0f; + cmf->xz = 0.0f; + cmf->yz = 0.0f; + cmf->wz = 0.0f; + cmf->xw = 0.0f; + cmf->yw = 0.0f; + cmf->zw = 0.0f; + cmf->zz = 1.0f; + cmf->ww = 1.0f; + cmf->xx = cos; + cmf->yy = cos; + cmf->yx = sin; + cmf->xy = -sin; + } +} + +/** + * Rotate using ZYX Tait-Bryan angles. + * This means a (column) vector is first rotated around X, then around Y, then around Z, then (if `mode` is + * `MTXMODE_APPLY`) gets transformed according to whatever the matrix was before adding the ZYX rotation. + * Original Name: Matrix_RotateXYZ, changed to reflect rotation order. + */ +void Matrix_RotateZYX(s16 x, s16 y, s16 z, u8 mode) { + MtxF* cmf = sCurrentMatrix; + f32 temp1; + f32 temp2; + f32 sin; + f32 cos; + + if (mode == MTXMODE_APPLY) { + sin = Math_SinS(z); + cos = Math_CosS(z); + + temp1 = cmf->xx; + temp2 = cmf->xy; + cmf->xx = temp1 * cos + temp2 * sin; + cmf->xy = temp2 * cos - temp1 * sin; + + temp1 = cmf->yx; + temp2 = cmf->yy; + cmf->yx = temp1 * cos + temp2 * sin; + cmf->yy = temp2 * cos - temp1 * sin; + + temp1 = cmf->zx; + temp2 = cmf->zy; + cmf->zx = temp1 * cos + temp2 * sin; + cmf->zy = temp2 * cos - temp1 * sin; + + temp1 = cmf->wx; + temp2 = cmf->wy; + cmf->wx = temp1 * cos + temp2 * sin; + cmf->wy = temp2 * cos - temp1 * sin; + + if (y != 0) { + sin = Math_SinS(y); + cos = Math_CosS(y); + + temp1 = cmf->xx; + temp2 = cmf->xz; + cmf->xx = temp1 * cos - temp2 * sin; + cmf->xz = temp1 * sin + temp2 * cos; + + temp1 = cmf->yx; + temp2 = cmf->yz; + cmf->yx = temp1 * cos - temp2 * sin; + cmf->yz = temp1 * sin + temp2 * cos; + + temp1 = cmf->zx; + temp2 = cmf->zz; + cmf->zx = temp1 * cos - temp2 * sin; + cmf->zz = temp1 * sin + temp2 * cos; + + temp1 = cmf->wx; + temp2 = cmf->wz; + cmf->wx = temp1 * cos - temp2 * sin; + cmf->wz = temp1 * sin + temp2 * cos; + } + + if (x != 0) { + sin = Math_SinS(x); + cos = Math_CosS(x); + + temp1 = cmf->xy; + temp2 = cmf->xz; + cmf->xy = temp1 * cos + temp2 * sin; + cmf->xz = temp2 * cos - temp1 * sin; + + temp1 = cmf->yy; + temp2 = cmf->yz; + cmf->yy = temp1 * cos + temp2 * sin; + cmf->yz = temp2 * cos - temp1 * sin; + + temp1 = cmf->zy; + temp2 = cmf->zz; + cmf->zy = temp1 * cos + temp2 * sin; + cmf->zz = temp2 * cos - temp1 * sin; + + temp1 = cmf->wy; + temp2 = cmf->wz; + cmf->wy = temp1 * cos + temp2 * sin; + cmf->wz = temp2 * cos - temp1 * sin; + } + } else { + SkinMatrix_SetRotateZYX(cmf, x, y, z); + } +} + +/** + * Translate and rotate using ZYX Tait-Bryan angles. + * This means a (column) vector is first rotated around X, then around Y, then around Z, then translated, then gets + * transformed according to whatever the matrix was previously. + */ +void Matrix_TranslateRotateZYX(Vec3f* translation, Vec3s* rotation) { + MtxF* cmf = sCurrentMatrix; + f32 sin = Math_SinS(rotation->z); + f32 cos = Math_CosS(rotation->z); + f32 temp1; + f32 temp2; + + temp1 = cmf->xx; + temp2 = cmf->xy; + cmf->xw += temp1 * translation->x + temp2 * translation->y + cmf->xz * translation->z; + cmf->xx = temp1 * cos + temp2 * sin; + cmf->xy = temp2 * cos - temp1 * sin; + + temp1 = cmf->yx; + temp2 = cmf->yy; + cmf->yw += temp1 * translation->x + temp2 * translation->y + cmf->yz * translation->z; + cmf->yx = temp1 * cos + temp2 * sin; + cmf->yy = temp2 * cos - temp1 * sin; + + temp1 = cmf->zx; + temp2 = cmf->zy; + cmf->zw += temp1 * translation->x + temp2 * translation->y + cmf->zz * translation->z; + cmf->zx = temp1 * cos + temp2 * sin; + cmf->zy = temp2 * cos - temp1 * sin; + + temp1 = cmf->wx; + temp2 = cmf->wy; + cmf->ww += temp1 * translation->x + temp2 * translation->y + cmf->wz * translation->z; + cmf->wx = temp1 * cos + temp2 * sin; + cmf->wy = temp2 * cos - temp1 * sin; + + if (rotation->y != 0) { + sin = Math_SinS(rotation->y); + cos = Math_CosS(rotation->y); + + temp1 = cmf->xx; + temp2 = cmf->xz; + cmf->xx = temp1 * cos - temp2 * sin; + cmf->xz = temp1 * sin + temp2 * cos; + + temp1 = cmf->yx; + temp2 = cmf->yz; + cmf->yx = temp1 * cos - temp2 * sin; + cmf->yz = temp1 * sin + temp2 * cos; + + temp1 = cmf->zx; + temp2 = cmf->zz; + cmf->zx = temp1 * cos - temp2 * sin; + cmf->zz = temp1 * sin + temp2 * cos; + + temp1 = cmf->wx; + temp2 = cmf->wz; + cmf->wx = temp1 * cos - temp2 * sin; + cmf->wz = temp1 * sin + temp2 * cos; + } + + if (rotation->x != 0) { + sin = Math_SinS(rotation->x); + cos = Math_CosS(rotation->x); + + temp1 = cmf->xy; + temp2 = cmf->xz; + cmf->xy = temp1 * cos + temp2 * sin; + cmf->xz = temp2 * cos - temp1 * sin; + + temp1 = cmf->yy; + temp2 = cmf->yz; + cmf->yy = temp1 * cos + temp2 * sin; + cmf->yz = temp2 * cos - temp1 * sin; + + temp1 = cmf->zy; + temp2 = cmf->zz; + cmf->zy = temp1 * cos + temp2 * sin; + cmf->zz = temp2 * cos - temp1 * sin; + + temp1 = cmf->wy; + temp2 = cmf->wz; + cmf->wy = temp1 * cos + temp2 * sin; + cmf->wz = temp2 * cos - temp1 * sin; + } +} + +/** + * Set the current matrix to translate and rotate using YXZ Tait-Bryan angles. + * This means a (column) vector is first rotated around Z, then around X, then around Y, then translated. + */ +void Matrix_SetTranslateRotateYXZ(f32 translateX, f32 translateY, f32 translateZ, Vec3s* rot) { + MtxF* cmf = sCurrentMatrix; + f32 temp1 = Math_SinS(rot->y); + f32 temp2 = Math_CosS(rot->y); + f32 cos; + f32 sin; + + cmf->xx = temp2; + cmf->zx = -temp1; + cmf->xw = translateX; + cmf->yw = translateY; + cmf->zw = translateZ; + cmf->wx = 0.0f; + cmf->wy = 0.0f; + cmf->wz = 0.0f; + cmf->ww = 1.0f; + + if (rot->x != 0) { + sin = Math_SinS(rot->x); + cos = Math_CosS(rot->x); + + cmf->zz = temp2 * cos; + cmf->zy = temp2 * sin; + cmf->xz = temp1 * cos; + cmf->xy = temp1 * sin; + cmf->yz = -sin; + cmf->yy = cos; + } else { + cmf->zz = temp2; + cmf->xz = temp1; + cmf->yz = 0.0f; + cmf->zy = 0.0f; + cmf->xy = 0.0f; + cmf->yy = 1.0f; + } + + if (rot->z != 0) { + sin = Math_SinS(rot->z); + cos = Math_CosS(rot->z); + + temp1 = cmf->xx; + temp2 = cmf->xy; + cmf->xx = temp1 * cos + temp2 * sin; + cmf->xy = temp2 * cos - temp1 * sin; + + temp1 = cmf->zx; + temp2 = cmf->zy; + cmf->zx = temp1 * cos + temp2 * sin; + cmf->zy = temp2 * cos - temp1 * sin; + + temp2 = cmf->yy; + cmf->yx = temp2 * sin; + cmf->yy = temp2 * cos; + } else { + cmf->yx = 0.0f; + } +} + +Mtx* Matrix_MtxFToMtx(MtxF* src, Mtx* dest) { + guMtxF2L(src, dest); + return dest; +} + +Mtx* Matrix_ToMtx(Mtx* dest, char* file, s32 line) { + return Matrix_MtxFToMtx(Matrix_CheckFloats(sCurrentMatrix, file, line), dest); +} + +Mtx* Matrix_NewMtx(GraphicsContext* gfxCtx, char* file, s32 line) { + return Matrix_ToMtx(Graph_Alloc(gfxCtx, sizeof(Mtx)), file, line); +} + +Mtx* Matrix_MtxFToNewMtx(MtxF* src, GraphicsContext* gfxCtx) { + return Matrix_MtxFToMtx(src, Graph_Alloc(gfxCtx, sizeof(Mtx))); +} + +void Matrix_MultVec3f(Vec3f* src, Vec3f* dest) { + MtxF* cmf = sCurrentMatrix; + + dest->x = cmf->xw + (cmf->xx * src->x + cmf->xy * src->y + cmf->xz * src->z); + dest->y = cmf->yw + (cmf->yx * src->x + cmf->yy * src->y + cmf->yz * src->z); + dest->z = cmf->zw + (cmf->zx * src->x + cmf->zy * src->y + cmf->zz * src->z); +} + +void Matrix_MtxFCopy(MtxF* dest, MtxF* src) { + dest->xx = src->xx; + dest->yx = src->yx; + dest->zx = src->zx; + dest->wx = src->wx; + dest->xy = src->xy; + dest->yy = src->yy; + dest->zy = src->zy; + dest->wy = src->wy; + dest->xx = src->xx; + dest->yx = src->yx; + dest->zx = src->zx; + dest->wx = src->wx; + dest->xy = src->xy; + dest->yy = src->yy; + dest->zy = src->zy; + dest->wy = src->wy; + dest->xz = src->xz; + dest->yz = src->yz; + dest->zz = src->zz; + dest->wz = src->wz; + dest->xw = src->xw; + dest->yw = src->yw; + dest->zw = src->zw; + dest->ww = src->ww; + dest->xz = src->xz; + dest->yz = src->yz; + dest->zz = src->zz; + dest->wz = src->wz; + dest->xw = src->xw; + dest->yw = src->yw; + dest->zw = src->zw; + dest->ww = src->ww; +} + +void Matrix_MtxToMtxF(Mtx* src, MtxF* dest) { + guMtxL2F(dest, src); +} + +void Matrix_MultVec3fExt(Vec3f* src, Vec3f* dest, MtxF* mf) { + dest->x = mf->xw + (mf->xx * src->x + mf->xy * src->y + mf->xz * src->z); + dest->y = mf->yw + (mf->yx * src->x + mf->yy * src->y + mf->yz * src->z); + dest->z = mf->zw + (mf->zx * src->x + mf->zy * src->y + mf->zz * src->z); +} + +void Matrix_Transpose(MtxF* mf) { + f32 temp; + + temp = mf->yx; + mf->yx = mf->xy; + mf->xy = temp; + + temp = mf->zx; + mf->zx = mf->xz; + mf->xz = temp; + + temp = mf->zy; + mf->zy = mf->yz; + mf->yz = temp; +} + +/** + * Changes the 3x3 part of the current matrix to `mf` * S, where S is the scale in the current matrix. + * + * In details, S is a diagonal where each coefficient is the norm of the column in the 3x3 current matrix. + * The 3x3 part can then be written as R * S where R has its columns normalized. + * Since R is typically a rotation matrix, and the 3x3 part is changed from R * S to `mf` * S, this operation can be + * seen as replacing the R rotation with `mf`, hence the function name. + */ +void Matrix_ReplaceRotation(MtxF* mf) { + MtxF* cmf = sCurrentMatrix; + f32 acc; + f32 temp; + f32 curColNorm; + + // compute the Euclidean norm of the first column of the current matrix + acc = cmf->xx; + acc *= acc; + temp = cmf->yx; + acc += SQ(temp); + temp = cmf->zx; + acc += SQ(temp); + curColNorm = sqrtf(acc); + + cmf->xx = mf->xx * curColNorm; + cmf->yx = mf->yx * curColNorm; + cmf->zx = mf->zx * curColNorm; + + // second column + acc = cmf->xy; + acc *= acc; + temp = cmf->yy; + acc += SQ(temp); + temp = cmf->zy; + acc += SQ(temp); + curColNorm = sqrtf(acc); + + cmf->xy = mf->xy * curColNorm; + cmf->yy = mf->yy * curColNorm; + cmf->zy = mf->zy * curColNorm; + + // third column + acc = cmf->xz; + acc *= acc; + temp = cmf->yz; + acc += SQ(temp); + temp = cmf->zz; + acc += SQ(temp); + curColNorm = sqrtf(acc); + + cmf->xz = mf->xz * curColNorm; + cmf->yz = mf->yz * curColNorm; + cmf->zz = mf->zz * curColNorm; +} + +/** + * Gets the rotation the specified matrix represents, using Tait-Bryan YXZ angles. + * The flag value doesn't matter for a rotation matrix. Not 0 does extra calculation. + */ +void Matrix_MtxFToYXZRotS(MtxF* mf, Vec3s* rotDest, s32 flag) { + f32 temp; + f32 temp2; + f32 temp3; + f32 temp4; + + temp = mf->xz; + temp *= temp; + temp += SQ(mf->zz); + rotDest->x = Math_FAtan2F(-mf->yz, sqrtf(temp)) * (0x8000 / M_PI); + + if ((rotDest->x == 0x4000) || (rotDest->x == -0x4000)) { + rotDest->z = 0; + + rotDest->y = Math_FAtan2F(-mf->zx, mf->xx) * (0x8000 / M_PI); + } else { + rotDest->y = Math_FAtan2F(mf->xz, mf->zz) * (0x8000 / M_PI); + + if (!flag) { + rotDest->z = Math_FAtan2F(mf->yx, mf->yy) * (0x8000 / M_PI); + } else { + temp = mf->xx; + temp2 = mf->zx; + temp3 = mf->zy; + + temp *= temp; + temp += SQ(temp2); + temp2 = mf->yx; + temp += SQ(temp2); + /* temp = xx^2+zx^2+yx^2 == 1 for a rotation matrix */ + temp = sqrtf(temp); + temp = temp2 / temp; + + temp2 = mf->xy; + temp2 *= temp2; + temp2 += SQ(temp3); + temp3 = mf->yy; + temp2 += SQ(temp3); + /* temp2 = xy^2+zy^2+yy^2 == 1 for a rotation matrix */ + temp2 = sqrtf(temp2); + temp2 = temp3 / temp2; + + /* for a rotation matrix, temp == yx and temp2 == yy + * which is the same as in the !flag branch */ + rotDest->z = Math_FAtan2F(temp, temp2) * (0x8000 / M_PI); + } + } +} + +/** + * Gets the rotation the specified matrix represents, using Tait-Bryan ZYX angles. + * The flag value doesn't matter for a rotation matrix. Not 0 does extra calculation. + */ +void Matrix_MtxFToZYXRotS(MtxF* mf, Vec3s* rotDest, s32 flag) { + f32 temp; + f32 temp2; + f32 temp3; + f32 temp4; + + temp = mf->xx; + temp *= temp; + temp += SQ(mf->yx); + rotDest->y = Math_FAtan2F(-mf->zx, sqrtf(temp)) * (0x8000 / M_PI); + + if ((rotDest->y == 0x4000) || (rotDest->y == -0x4000)) { + rotDest->x = 0; + rotDest->z = Math_FAtan2F(-mf->xy, mf->yy) * (0x8000 / M_PI); + } else { + rotDest->z = Math_FAtan2F(mf->yx, mf->xx) * (0x8000 / M_PI); + + if (!flag) { + rotDest->x = Math_FAtan2F(mf->zy, mf->zz) * (0x8000 / M_PI); + } else { + // see Matrix_MtxFToYXZRotS + temp = mf->xy; + temp2 = mf->yy; + temp3 = mf->yz; + + temp *= temp; + temp += SQ(temp2); + temp2 = mf->zy; + temp += SQ(temp2); + temp = sqrtf(temp); + temp = temp2 / temp; + + temp2 = mf->xz; + temp2 *= temp2; + temp2 += SQ(temp3); + temp3 = mf->zz; + temp2 += SQ(temp3); + temp2 = sqrtf(temp2); + temp2 = temp3 / temp2; + + rotDest->x = Math_FAtan2F(temp, temp2) * (0x8000 / M_PI); + } + } +} + +/* + * Rotate the matrix by `angle` radians around a unit vector `axis`. + * NB: `axis` is assumed to be a unit vector. + */ +void Matrix_RotateAxis(f32 angle, Vec3f* axis, u8 mode) { + MtxF* cmf; + f32 sin; + f32 cos; + f32 rCos; + f32 temp1; + f32 temp2; + f32 temp3; + f32 temp4; + + if (mode == MTXMODE_APPLY) { + if (angle != 0) { + cmf = sCurrentMatrix; + + sin = sinf(angle); + cos = cosf(angle); + + temp1 = cmf->xx; + temp2 = cmf->xy; + temp3 = cmf->xz; + temp4 = (axis->x * temp1 + axis->y * temp2 + axis->z * temp3) * (1.0f - cos); + cmf->xx = temp1 * cos + axis->x * temp4 + sin * (temp2 * axis->z - temp3 * axis->y); + cmf->xy = temp2 * cos + axis->y * temp4 + sin * (temp3 * axis->x - temp1 * axis->z); + cmf->xz = temp3 * cos + axis->z * temp4 + sin * (temp1 * axis->y - temp2 * axis->x); + + temp1 = cmf->yx; + temp2 = cmf->yy; + temp3 = cmf->yz; + temp4 = (axis->x * temp1 + axis->y * temp2 + axis->z * temp3) * (1.0f - cos); + cmf->yx = temp1 * cos + axis->x * temp4 + sin * (temp2 * axis->z - temp3 * axis->y); + cmf->yy = temp2 * cos + axis->y * temp4 + sin * (temp3 * axis->x - temp1 * axis->z); + cmf->yz = temp3 * cos + axis->z * temp4 + sin * (temp1 * axis->y - temp2 * axis->x); + + temp1 = cmf->zx; + temp2 = cmf->zy; + temp3 = cmf->zz; + temp4 = (axis->x * temp1 + axis->y * temp2 + axis->z * temp3) * (1.0f - cos); + cmf->zx = temp1 * cos + axis->x * temp4 + sin * (temp2 * axis->z - temp3 * axis->y); + cmf->zy = temp2 * cos + axis->y * temp4 + sin * (temp3 * axis->x - temp1 * axis->z); + cmf->zz = temp3 * cos + axis->z * temp4 + sin * (temp1 * axis->y - temp2 * axis->x); + } + } else { + cmf = sCurrentMatrix; + + if (angle != 0) { + sin = sinf(angle); + cos = cosf(angle); + rCos = 1.0f - cos; + + cmf->xx = axis->x * axis->x * rCos + cos; + cmf->yy = axis->y * axis->y * rCos + cos; + cmf->zz = axis->z * axis->z * rCos + cos; + + if (0) {} + + temp2 = axis->x * rCos * axis->y; + temp3 = axis->z * sin; + cmf->yx = temp2 + temp3; + cmf->xy = temp2 - temp3; + + temp2 = axis->x * rCos * axis->z; + temp3 = axis->y * sin; + cmf->zx = temp2 - temp3; + cmf->xz = temp2 + temp3; + + temp2 = axis->y * rCos * axis->z; + temp3 = axis->x * sin; + cmf->zy = temp2 + temp3; + cmf->yz = temp2 - temp3; + + cmf->wx = cmf->wy = cmf->wz = cmf->xw = cmf->yw = cmf->zw = 0.0f; + cmf->ww = 1.0f; + } else { + cmf->yx = 0.0f; + cmf->zx = 0.0f; + cmf->wx = 0.0f; + cmf->xy = 0.0f; + cmf->zy = 0.0f; + cmf->wy = 0.0f; + cmf->xz = 0.0f; + cmf->yz = 0.0f; + cmf->wz = 0.0f; + cmf->xw = 0.0f; + cmf->yw = 0.0f; + cmf->zw = 0.0f; + cmf->xx = 1.0f; + cmf->yy = 1.0f; + cmf->zz = 1.0f; + cmf->ww = 1.0f; + } + } +} + +MtxF* Matrix_CheckFloats(MtxF* mf, char* file, s32 line) { + s32 i, j; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + if (!(-32768.0f <= mf->mf[i][j]) || !(mf->mf[i][j] < 32768.0f)) { + osSyncPrintf("%s %d: [%s] =\n" + "/ %12.6f %12.6f %12.6f %12.6f \\\n" + "| %12.6f %12.6f %12.6f %12.6f |\n" + "| %12.6f %12.6f %12.6f %12.6f |\n" + "\\ %12.6f %12.6f %12.6f %12.6f /\n", + file, line, "mf", mf->xx, mf->xy, mf->xz, mf->xw, mf->yx, mf->yy, mf->yz, mf->yw, mf->zx, + mf->zy, mf->zz, mf->zw, mf->wx, mf->wy, mf->wz, mf->ww); + //Fault_AddHungupAndCrash(file, line); + } + } + } + + return mf; +} + +void Matrix_SetTranslateUniformScaleMtxF(MtxF* mf, f32 scale, f32 translateX, f32 translateY, f32 translateZ) { + mf->yx = 0.0f; + mf->zx = 0.0f; + mf->wx = 0.0f; + mf->xy = 0.0f; + mf->zy = 0.0f; + mf->wy = 0.0f; + mf->xz = 0.0f; + mf->yz = 0.0f; + mf->wz = 0.0f; + mf->xx = scale; + mf->yy = scale; + mf->zz = scale; + mf->xw = translateX; + mf->yw = translateY; + mf->zw = translateZ; + mf->ww = 1.0f; +} + +void Matrix_SetTranslateUniformScaleMtx(Mtx* mtx, f32 scale, f32 translateX, f32 translateY, f32 translateZ) { + MtxF mf; + + Matrix_SetTranslateUniformScaleMtxF(&mf, scale, translateX, translateY, translateZ); + guMtxF2L(&mf, mtx); +} + +void Matrix_SetTranslateUniformScaleMtx2(Mtx* mtx, f32 scale, f32 translateX, f32 translateY, f32 translateZ) { + u16* intPart = (u16*)&mtx->m[0][0]; + u16* fracPart = (u16*)&mtx->m[2][0]; + u32 fixedPoint; + + fixedPoint = (s32)(scale * 0x10000); + fracPart[0] = fixedPoint & 0xFFFF; + intPart[0] = (fixedPoint >> 16) & 0xFFFF; + + fixedPoint = (s32)(scale * 0x10000); + intPart[5] = (fixedPoint >> 16) & 0xFFFF; + fracPart[5] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(scale * 0x10000); + intPart[10] = (fixedPoint >> 16) & 0xFFFF; + fracPart[10] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(translateX * 0x10000); + intPart[12] = (fixedPoint >> 16) & 0xFFFF; + fracPart[12] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(translateY * 0x10000); + intPart[13] = (fixedPoint >> 16) & 0xFFFF; + fracPart[13] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(translateZ * 0x10000); + intPart[14] = (fixedPoint >> 16) & 0xFFFF; + fracPart[14] = fixedPoint & 0xFFFF; + + intPart[1] = 0; + intPart[2] = 0; + intPart[3] = 0; + intPart[4] = 0; + intPart[6] = 0; + intPart[7] = 0; + intPart[8] = 0; + intPart[9] = 0; + intPart[11] = 0; + intPart[15] = 1; + + fracPart[1] = 0; + fracPart[2] = 0; + fracPart[3] = 0; + fracPart[4] = 0; + fracPart[6] = 0; + fracPart[7] = 0; + fracPart[8] = 0; + fracPart[9] = 0; + fracPart[11] = 0; + fracPart[15] = 0; +} + +void Matrix_SetTranslateScaleMtx1(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY, + f32 translateZ) { + u16* intPart = (u16*)&mtx->m[0][0]; + u16* fracPart = (u16*)&mtx->m[2][0]; + u32 fixedPoint; + + fixedPoint = (s32)(scaleX * 0x10000); + intPart[0] = (fixedPoint >> 16) & 0xFFFF; + fracPart[0] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(scaleY * 0x10000); + intPart[5] = (fixedPoint >> 16) & 0xFFFF; + fracPart[5] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(scaleZ * 0x10000); + intPart[10] = (fixedPoint >> 16) & 0xFFFF; + fracPart[10] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(translateX * 0x10000); + intPart[12] = (fixedPoint >> 16) & 0xFFFF; + fracPart[12] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(translateY * 0x10000); + intPart[13] = (fixedPoint >> 16) & 0xFFFF; + fracPart[13] = fixedPoint & 0xFFFF; + + fixedPoint = (s32)(translateZ * 0x10000); + intPart[14] = (fixedPoint >> 16) & 0xFFFF; + fracPart[14] = fixedPoint & 0xFFFF; + + intPart[1] = 0; + intPart[2] = 0; + intPart[3] = 0; + intPart[4] = 0; + intPart[6] = 0; + intPart[7] = 0; + intPart[8] = 0; + intPart[9] = 0; + intPart[11] = 0; + intPart[15] = 1; + + fracPart[1] = 0; + fracPart[2] = 0; + fracPart[3] = 0; + fracPart[4] = 0; + fracPart[6] = 0; + fracPart[7] = 0; + fracPart[8] = 0; + fracPart[9] = 0; + fracPart[11] = 0; + fracPart[15] = 0; +} + +void Matrix_SetTranslateScaleMtx2(Mtx* mtx, f32 scaleX, f32 scaleY, f32 scaleZ, f32 translateX, f32 translateY, + f32 translateZ) { + MtxF mtxf = { { + { scaleX, 0, 0, 0 }, + { 0, scaleY, 0, 0 }, + { 0, 0, scaleZ, 0 }, + { translateX, translateY, translateZ, 1 } + } }; + guMtxF2L(&mtxf, mtx); +} diff --git a/soh/src/code/sys_ucode.c b/soh/src/code/sys_ucode.c new file mode 100644 index 000000000..b2a5ef108 --- /dev/null +++ b/soh/src/code/sys_ucode.c @@ -0,0 +1,20 @@ +#include "global.h" + +//uintptr_t D_8012DBA0 = (uintptr_t)&D_80155F50; +//uintptr_t D_8012DBA4 = (uintptr_t)&D_80157580; + +uintptr_t SysUcode_GetUCodeBoot(void) { + //return &D_80009320; +} + +uintptr_t SysUcode_GetUCodeBootSize(void) { + //return (uintptr_t)&D_800093F0 - (uintptr_t)&D_80009320; +} + +uintptr_t SysUcode_GetUCode(void) { + //return D_8012DBA0; +} + +uintptr_t SysUcode_GetUCodeData(void) { + //return D_8012DBA4; +} diff --git a/soh/src/code/system_malloc.c b/soh/src/code/system_malloc.c new file mode 100644 index 000000000..e8bfefed3 --- /dev/null +++ b/soh/src/code/system_malloc.c @@ -0,0 +1,111 @@ +#include "global.h" +#include + +#define LOG_SEVERITY_NOLOG 0 +#define LOG_SEVERITY_ERROR 2 +#define LOG_SEVERITY_VERBOSE 3 + +s32 gSystemArenaLogSeverity = LOG_SEVERITY_NOLOG; +Arena gSystemArena; + +void SystemArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action) { + if (ptr == NULL) { + if (gSystemArenaLogSeverity >= LOG_SEVERITY_ERROR) { + // "%s: %u bytes %s failed\n" + osSyncPrintf("%s: %u バイトの%sに失敗しました\n", name, size, action); + __osDisplayArena(&gSystemArena); + return; + } + } else if (gSystemArenaLogSeverity >= LOG_SEVERITY_VERBOSE) { + // "%s: %u bytes %s succeeded\n" + osSyncPrintf("%s: %u バイトの%sに成功しました\n", name, size, action); + } +} + +void* SystemArena_Malloc(size_t size) { + void* ptr = __osMalloc(&gSystemArena, size); + + SystemArena_CheckPointer(ptr, size, "malloc", "確保"); // "Secure" + return ptr; +} + +void* SystemArena_MallocDebug(size_t size, const char* file, s32 line) { + void* ptr = __osMallocDebug(&gSystemArena, size, file, line); + + SystemArena_CheckPointer(ptr, size, "malloc_DEBUG", "確保"); // "Secure" + return ptr; +} + +void* SystemArena_MallocR(size_t size) { + void* ptr = __osMallocR(&gSystemArena, size); + + SystemArena_CheckPointer(ptr, size, "malloc_r", "確保"); // "Secure" + return ptr; +} + +void* SystemArena_MallocRDebug(size_t size, const char* file, s32 line) { + void* ptr = __osMallocRDebug(&gSystemArena, size, file, line); + + SystemArena_CheckPointer(ptr, size, "malloc_r_DEBUG", "確保"); // "Secure" + return ptr; +} + +void* SystemArena_Realloc(void* ptr, size_t newSize) { + ptr = __osRealloc(&gSystemArena, ptr, newSize); + SystemArena_CheckPointer(ptr, newSize, "realloc", "再確保"); // "Re-securing" + return ptr; +} + +void* SystemArena_ReallocDebug(void* ptr, size_t newSize, const char* file, s32 line) { + ptr = __osReallocDebug(&gSystemArena, ptr, newSize, file, line); + SystemArena_CheckPointer(ptr, newSize, "realloc_DEBUG", "再確保"); // "Re-securing" + return ptr; +} + +void SystemArena_Free(void* ptr) { + __osFree(&gSystemArena, ptr); +} + +void SystemArena_FreeDebug(void* ptr, const char* file, s32 line) { + __osFreeDebug(&gSystemArena, ptr, file, line); +} + +void* SystemArena_Calloc(size_t num, size_t size) { + void* ret; + size_t n = num * size; + + ret = __osMalloc(&gSystemArena, n); + if (ret != NULL) { + memset(ret, 0, n); + } + + SystemArena_CheckPointer(ret, n, "calloc", "確保"); + return ret; +} + +void SystemArena_Display(void) { + osSyncPrintf("システムヒープ表示\n"); // "System heap display" + __osDisplayArena(&gSystemArena); +} + +void SystemArena_GetSizes(u32* outMaxFree, u32* outFree, u32* outAlloc) { + ArenaImpl_GetSizes(&gSystemArena, outMaxFree, outFree, outAlloc); +} + +void SystemArena_Check(void) { + __osCheckArena(&gSystemArena); +} + +void SystemArena_Init(void* start, size_t size) { + gSystemArenaLogSeverity = LOG_SEVERITY_NOLOG; + __osMallocInit(&gSystemArena, start, size); +} + +void SystemArena_Cleanup(void) { + gSystemArenaLogSeverity = LOG_SEVERITY_NOLOG; + __osMallocCleanup(&gSystemArena); +} + +u8 SystemArena_IsInitalized(void) { + return __osMallocIsInitalized(&gSystemArena); +} diff --git a/soh/src/code/title_setup.c b/soh/src/code/title_setup.c new file mode 100644 index 000000000..1e709dc12 --- /dev/null +++ b/soh/src/code/title_setup.c @@ -0,0 +1,16 @@ +#include "global.h" + +void TitleSetup_InitImpl(GameState* gameState) { + osSyncPrintf("ゼルダ共通データ初期化\n"); // "Zelda common data initalization" + SaveContext_Init(); + gameState->running = false; + SET_NEXT_GAMESTATE(gameState, Title_Init, TitleContext); +} + +void TitleSetup_Destroy(GameState* gameState) { +} + +void TitleSetup_Init(GameState* gameState) { + gameState->destroy = TitleSetup_Destroy; + TitleSetup_InitImpl(gameState); +} diff --git a/soh/src/code/ucode_disas.c b/soh/src/code/ucode_disas.c new file mode 100644 index 000000000..4ca61f3c5 --- /dev/null +++ b/soh/src/code/ucode_disas.c @@ -0,0 +1,1365 @@ +#include "global.h" +#include +#if 0 +#define F3DZEX_CONST(name) \ + { name, #name } +#define F3DZEX_FLAG(set, unset) \ + { set, #set, #unset } +#define F3DZEX_RENDERMODE(name, mask) \ + { #name, name, mask } +#define F3DZEX_SETRENDERMACRO(name, shift, len, value0, value1, value2, value3) \ + { \ + name, shift, len, { \ + { #value0, value0 }, { #value1, value1 }, { #value2, value2 }, { #value3, value3 }, \ + } \ + } + +#define DISAS_LOG \ + if (this->enableLog) \ + osSyncPrintf + +u32 UCodeDisas_TranslateAddr(UCodeDisas* this, u32 addr) { + u32 physical = this->segments[SEGMENT_NUMBER(addr)] + SEGMENT_OFFSET(addr); + + return PHYSICAL_TO_VIRTUAL(physical); +} + +F3dzexConst sUCodeDisasGeometryModes[] = { + F3DZEX_CONST(G_ZBUFFER), F3DZEX_CONST(G_TEXTURE_ENABLE), + F3DZEX_CONST(G_SHADE), F3DZEX_CONST(G_SHADING_SMOOTH), + F3DZEX_CONST(G_CULL_FRONT), F3DZEX_CONST(G_CULL_BACK), + F3DZEX_CONST(G_FOG), F3DZEX_CONST(G_LIGHTING), + F3DZEX_CONST(G_TEXTURE_GEN), F3DZEX_CONST(G_TEXTURE_GEN_LINEAR), + F3DZEX_CONST(G_LOD), +}; + +F3dzexFlag sUCodeDisasMtxFlags[] = { + F3DZEX_FLAG(G_MTX_PROJECTION, G_MTX_MODELVIEW), + F3DZEX_FLAG(G_MTX_LOAD, G_MTX_MUL), + F3DZEX_FLAG(G_MTX_PUSH, G_MTX_NOPUSH), +}; + +const char* UCodeDisas_ParseCombineColor(u32 value, u32 idx) { + const char* ret = "?"; + + switch (value) { + case G_CCMUX_COMBINED: + ret = "COMBINED"; + break; + case G_CCMUX_TEXEL0: + ret = "TEXEL0"; + break; + case G_CCMUX_TEXEL1: + ret = "TEXEL1"; + break; + case G_CCMUX_PRIMITIVE: + ret = "PRIMITIVE"; + break; + case G_CCMUX_SHADE: + ret = "SHADE"; + break; + case G_CCMUX_ENVIRONMENT: + ret = "ENVIRONMENT"; + break; + case 6: + ret = (idx == 2) ? "CENTER" : (idx == 3) ? "SCALE" : "1"; + break; + case 7: + ret = (idx == 1) ? "NOISE" : (idx == 2) ? "K4" : (idx == 3) ? "COMBINED_ALPHA" : "0"; + break; + default: + if (idx == 3) { + switch (value) { + case G_CCMUX_TEXEL0_ALPHA: + ret = "TEXEL0_ALPHA"; + break; + case G_CCMUX_TEXEL1_ALPHA: + ret = "TEXEL1_ALPHA"; + break; + case G_CCMUX_PRIMITIVE_ALPHA: + ret = "PRIMITIVE_ALPHA"; + break; + case G_CCMUX_SHADE_ALPHA: + ret = "SHADE_ALPHA"; + break; + case G_CCMUX_ENV_ALPHA: + ret = "ENV_ALPHA"; + break; + case G_CCMUX_LOD_FRACTION: + ret = "LOD_FRACTION"; + break; + case G_CCMUX_PRIM_LOD_FRAC: + ret = "PRIM_LOD_FRAC"; + break; + case G_CCMUX_K5: + ret = "K5"; + break; + default: + ret = "0"; + break; + } + } else { + ret = "0"; + } + } + return ret; +} + +const char* UCodeDisas_ParseCombineAlpha(u32 value, u32 idx) { + const char* ret = "?"; + switch (value) { + case 0: + ret = (idx == 3) ? "LOD_FRACTION" : "COMBINED"; + break; + case G_ACMUX_TEXEL0: + ret = "TEXEL0"; + break; + case G_ACMUX_TEXEL1: + ret = "TEXEL1"; + break; + case G_ACMUX_PRIMITIVE: + ret = "PRIMITIVE"; + break; + case G_ACMUX_SHADE: + ret = "SHADE"; + break; + case G_ACMUX_ENVIRONMENT: + ret = "ENVIRONMENT"; + break; + case 6: + ret = (idx == 3) ? "PRIM_LOD_FRAC" : "1"; + break; + case G_ACMUX_0: + ret = "0"; + break; + } + return ret; +} + +void UCodeDisas_Init(UCodeDisas* this) { + u32 i; + + memset(this,0, sizeof(UCodeDisas)); + for (i = 0; i < NUM_SEGMENTS; i++) { + this->segments[i] = gSegments[i]; + } +} + +void UCodeDisas_Destroy(UCodeDisas* this) { +} + +void UCodeDisas_SetCurUCodeImpl(UCodeDisas* this, void* ptr) { + s32 i; + + for (i = 0; i < this->ucodeInfoCount; i++) { + if (ptr == this->ucodeInfo[i].ptr) { + this->ucodeType = this->ucodeInfo[i].type; + break; + } + } + if (i >= this->ucodeInfoCount) { + DISAS_LOG("マイクロコードが一致しなかった\n"); // "Microcode did not match" + this->ucodeType = UCODE_NULL; + } +} + +void UCodeDisas_ParseGeometryMode(UCodeDisas* this, u32 mode) { + u32 first = true; + s32 i; + + for (i = 0; i < ARRAY_COUNT(sUCodeDisasGeometryModes); i++) { + if ((sUCodeDisasGeometryModes[i].value & mode) == 0) { + continue; + } + + if (first) { + } else { + DISAS_LOG("|"); + } + first = false; + + DISAS_LOG("%s", sUCodeDisasGeometryModes[i].name); + } +} + +void UCodeDisas_ParseRenderMode(UCodeDisas* this, u32 mode) { + static F3dzexRenderMode sUCodeDisasRenderModeFlags[] = { + F3DZEX_RENDERMODE(AA_EN, 0x8), + F3DZEX_RENDERMODE(Z_CMP, 0x10), + F3DZEX_RENDERMODE(Z_UPD, 0x20), + F3DZEX_RENDERMODE(IM_RD, 0x40), + F3DZEX_RENDERMODE(CLR_ON_CVG, 0x80), + F3DZEX_RENDERMODE(CVG_DST_CLAMP, 0x300), + F3DZEX_RENDERMODE(CVG_DST_WRAP, 0x300), + F3DZEX_RENDERMODE(CVG_DST_FULL, 0x300), + F3DZEX_RENDERMODE(CVG_DST_SAVE, 0x300), + F3DZEX_RENDERMODE(ZMODE_OPA, 0xC00), + F3DZEX_RENDERMODE(ZMODE_INTER, 0xC00), + F3DZEX_RENDERMODE(ZMODE_XLU, 0xC00), + F3DZEX_RENDERMODE(ZMODE_DEC, 0xC00), + F3DZEX_RENDERMODE(CVG_X_ALPHA, 0x1000), + F3DZEX_RENDERMODE(ALPHA_CVG_SEL, 0x2000), + F3DZEX_RENDERMODE(FORCE_BL, 0x4000), + }; + static const char* D_8012DDDC[4][4] = { + { "G_BL_CLR_IN", "G_BL_CLR_MEM", "G_BL_CLR_BL", "G_BL_CLR_FOG" }, + { "G_BL_A_IN", "G_BL_A_FOG", "G_BL_A_SHADE", "G_BL_0" }, + { "G_BL_CLR_IN", "G_BL_CLR_MEM", "G_BL_CLR_BL", "G_BL_CLR_FOG" }, + { "G_BL_1MA", "G_BL_A_MEM", "G_BL_1", "G_BL_0" }, + }; + + s32 i; + s32 a; + s32 b; + + for (i = 0; i < ARRAY_COUNT(sUCodeDisasRenderModeFlags); i++) { + if ((mode & sUCodeDisasRenderModeFlags[i].mask) != sUCodeDisasRenderModeFlags[i].value) { + continue; + } + + DISAS_LOG("%s|", sUCodeDisasRenderModeFlags[i].name); + } + + a = (mode >> 18) & 0x3333; + b = (mode >> 16) & 0x3333; + + // clang-format off + if (this->enableLog == 0) {} else { osSyncPrintf("\nGBL_c1(%s, %s, %s, %s)|", + D_8012DDDC[0][a >> 12 & 3], D_8012DDDC[1][a >> 8 & 3], D_8012DDDC[2][a >> 4 & 3], D_8012DDDC[3][a >> 0 & 3]); } + // clang-format on + + if (this->enableLog) { + osSyncPrintf("\nGBL_c2(%s, %s, %s, %s)", D_8012DDDC[0][b >> 12 & 3], D_8012DDDC[1][b >> 8 & 3], + D_8012DDDC[2][b >> 4 & 3], D_8012DDDC[3][b >> 0 & 3]); + } +} + +void UCodeDisas_PrintVertices(UCodeDisas* this, Vtx* vtx, s32 count, s32 start) { + s32 i; + for (i = 0; i < count; i++) { + if (this->geometryMode & G_LIGHTING) { + DISAS_LOG("\n{{%6d, %6d, %6d, %d, %6d, %6d, %3d, %3d, %3d, %3d}}, /* vc%d */", vtx->n.ob[0], vtx->n.ob[1], + vtx->n.ob[2], vtx->n.flag, vtx->n.tc[0], vtx->n.tc[1], vtx->n.n[0], vtx->n.n[1], vtx->n.n[2], + vtx->n.a, start + i); + } else { + DISAS_LOG("\n{{%6d, %6d, %6d, %d, %6d, %6d, %3d, %3d, %3d, %3d}}, /* vn%d */", vtx->v.ob[0], vtx->v.ob[1], + vtx->v.ob[2], vtx->v.flag, vtx->v.tc[0], vtx->v.tc[1], vtx->v.cn[0], vtx->v.cn[1], vtx->v.cn[2], + vtx->v.cn[3], start + i); + } + vtx++; + + if (1) {} + } +} + +// Todo: clean this up + +typedef struct { + s8 cmd; + u8 v0; + u8 v1; + u8 wd; + u32 pad; +} Gline3DFix; + +typedef struct { + int cmd : 8; + u32 pad : 4; + u32 numv : 8; + s32 pad2 : 4; + u8 vbidx; +} Gvtx; + +typedef struct { + u8 pad : 8; + u8 v0 : 8; + u8 v1 : 8; + u8 v2 : 8; +} Gtrimod; + +typedef struct { + int cmd : 8; + int pad : 24; + Gtrimod tri; +} Gtri1; + +typedef struct { + Gtrimod tri1; + Gtrimod tri2; +} Gtri2; + +typedef struct { + u8 pad : 8; + u8 v0 : 8; + u8 v1 : 8; + u8 v2 : 8; + u8 pad1 : 8; + u8 pad2 : 8; + u8 pad3 : 8; + u8 v3 : 8; +} Gquadmod; + +typedef struct { + u16 pad; + u16 vstart; + u16 pad2; + u16 vend; +} Gcull; + +typedef struct { + u16 pad; + u16 vstart; + u16 pad2; + u16 vend; +} Gbranchz; + +typedef struct { + int cmd : 8; + u8 pad; + u8 prim_min_level; + u8 prim_level; + u8 r; + u8 g; + u8 b; + u8 a; +} GsetcolorMod; + +typedef struct { + u8 cmd; + char pad[3]; + u16 z; + u16 d; +} Gsetprimdepth; + +typedef struct { + u8 cmd; + u8 type; + u16 len; + union { + u32 u32; + f32 f32; + } value; +} Gnoop; + +typedef struct { + u8 cmd; + u8 pad[2]; + u8 params; + u32 addr; +} Gmatrix; + +typedef struct { + u8 cmd; + u32 a : 4; + u32 c : 5; + u32 z : 3; + u32 x : 3; + u32 e : 4; + u32 g : 5; + u32 b : 4; + u32 f : 4; + u32 v : 3; + u32 t : 3; + u32 d : 3; + u32 y : 3; + u32 w : 3; + u32 h : 3; + u32 u : 3; + u32 s : 3; +} GsetcombineMod; + +typedef struct { + u32 cmd : 8; + u32 pad0 : 8; + u32 sft : 8; + u32 len : 8; + u32 data : 32; +} GsetothermodeMod; + +typedef struct { + s32 cmd : 8; + u32 offset : 16; + u32 index : 8; + u32 data; +} Gmovewd; + +typedef struct { + s32 cmd : 8; + size_t size : 8; + u32 offset : 8; + u32 index : 8; + u32 data; +} Gmovemem; + +typedef struct { + u8 cmd : 8; + u8 lodscale : 8; + u8 pad : 2; + u8 level : 3; + u8 tile : 3; + + unsigned char on; + unsigned short s; + unsigned short t; +} Gtexturemod; + +typedef struct { + int cmd : 8; + int pad1 : 24; + u32 param : 26; + unsigned char pad3 : 6; +} Gpopmtxmod; + +typedef union { + Gwords words; + Gnoop noop; + Gmatrix matrix; + Gdma dma; + Gtri1 tri1; + Gtri2 tri2; + Gquadmod quad; + Gcull cull; + Gline3D line; + Gline3DFix linefix; + Gmovewd movewd; + Gmovemem movemem; + Gpopmtx popmtx; + Gpopmtxmod popmtxmod; + Gsegment segment; + GsetothermodeH setothermodeH; + GsetothermodeL setothermodeL; + GsetothermodeMod setothermode; + Gtexture texture; + Gtexturemod texmod; + Gperspnorm perspnorm; + Gsetimg setimg; + GsetcombineMod setcombine; + GsetcolorMod setcolor; // mod + Gfillrect fillrect; /* use for setscissor also */ + Gsettile settile; + Gloadtile loadtile; /* use for loadblock also, th is dxt */ + Gsettilesize settilesize; + Gloadtlut loadtlut; + Gsetprimdepth setprimdepth; + Gvtx vtx; + long long int force_structure_alignment; +} GfxMod; + +void UCodeDisas_Disassemble(UCodeDisas* this, GfxMod* ptr) { + u32 pad; + u32 addr; + u32 rdpHalf; + u16 linkDlLow; + u8 sid; + u8 cmd; + s32 i0; + u32 exit; + GfxMod curGfx[1]; + + exit = false; + + while (!exit) { + this->dlCnt++; + + ptr = UCodeDisas_TranslateAddr(this, ptr); + DISAS_LOG("%08x:", ptr); + + *curGfx = *ptr; + cmd = curGfx->dma.cmd; + addr = UCodeDisas_TranslateAddr(this, curGfx->dma.addr); + + DISAS_LOG("%08x-%08x:", curGfx->words.w0, curGfx->words.w1); + + for (i0 = 0; i0 < this->dlDepth; i0++) { + DISAS_LOG(" "); + } + + switch (cmd) { + case G_SPNOOP: { + DISAS_LOG("gsSPNoOp(),"); + } break; + + case G_DL: { + Gdma dma = ptr->dma; + + switch (dma.par) { + case 0: { + DISAS_LOG("gsSPDisplayList(0x%08x),", dma.addr); + this->dlStack[this->dlDepth++] = (Gfx*)(ptr + 1); + ptr = (GfxMod*)addr - 1; + } break; + + case 1: { + DISAS_LOG("gsSPBranchList(0x%08x),", dma.addr); + ptr = (GfxMod*)addr - 1; + } break; + } + } break; + + case G_RDPHALF_1: { + DISAS_LOG("RDPHALF_1(0x%08x),", curGfx->dma.addr); + rdpHalf = curGfx->dma.addr; + } break; + + case G_TEXRECT: { + Gtexrect rect = *(Gtexrect*)ptr; + + DISAS_LOG("gsSPTextureRectangle(%d,%d,%d,%d,%d,%d,%d,%d,%d),", rect.xl, rect.yl, rect.xh, rect.yh, + rect.tile, ptr[1].words.w1 >> 16, ptr[1].words.w1 & 0xFFFF, ptr[2].words.w1 >> 16, + ptr[2].words.w1 & 0xFFFF); + + ptr += 3 - 1; + this->pipeSyncRequired = true; + } break; + + case G_LOAD_UCODE: { + if (curGfx->dma.len == 0x7FF) { + DISAS_LOG("gsSPLoadUcode(0x%08x, 0x%08x),", curGfx->dma.addr, rdpHalf); + } else { + DISAS_LOG("gsSPLoadUcodeEx(0x%08x, 0x%08x, 0x%05x),", curGfx->dma.addr, rdpHalf, + curGfx->dma.len + 1); + } + UCodeDisas_SetCurUCodeImpl(this, (void*)UCodeDisas_TranslateAddr(this, curGfx->dma.addr)); + this->loaducodeCnt++; + } break; + + case G_ENDDL: { + DISAS_LOG("gsSPEndDisplayList(),"); + if (this->dlDepth <= 0) { + exit = true; + } else { + ptr = (GfxMod*)this->dlStack[--this->dlDepth] - 1; + } + } break; + + case G_SETTILE: { + Gsettile settile = ptr->settile; + + DISAS_LOG("gsDPSetTile(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d),", settile.fmt, settile.siz, settile.line, + settile.tmem, settile.tile, settile.palette, (settile.ct << 1) + settile.mt, settile.maskt, + settile.shiftt, (settile.cs << 1) + settile.ms, settile.masks, settile.shifts); + + if (this->tileSyncRequired) { + DISAS_LOG("### TileSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_LOADTILE: { + Gloadtile loadtile = ptr->loadtile; + + DISAS_LOG("gsDPLoadTile(%d,%d,%d,%d,%d),", loadtile.tile, loadtile.sl, loadtile.tl, loadtile.sh, + loadtile.th); + } break; + + case G_LOADBLOCK: { + Gloadtile loadtile = ptr->loadtile; + + DISAS_LOG("gsDPLoadBlock(%d,%d,%d,%d,%d),", loadtile.tile, loadtile.sl, loadtile.tl, loadtile.sh, + loadtile.th); + + if (this->loadSyncRequired) { + DISAS_LOG("### LoadSyncが必要です。\n"); + this->syncErr++; + } + this->pipeSyncRequired = true; + } break; + + case G_SETTILESIZE: { + Gloadtile loadtile = ptr->loadtile; + + DISAS_LOG("gsDPSetTileSize(%d,%d,%d,%d,%d),", loadtile.tile, loadtile.sl, loadtile.tl, loadtile.sh, + loadtile.th); + } break; + + case G_LOADTLUT: { + Gloadtlut loadtlut = ptr->loadtlut; + + DISAS_LOG("gsDPLoadTLUTCmd(%d,%d),", loadtlut.tile, loadtlut.sh >> 2); + } break; + + case G_SETCOMBINE: { + GsetcombineMod setcombine = ptr->setcombine; + + DISAS_LOG("gsDPSetCombineLERP(%s,%s,%s,%s, %s,%s,%s,%s, %s,%s,%s,%s, %s,%s,%s,%s),", + UCodeDisas_ParseCombineColor(setcombine.a, 1), UCodeDisas_ParseCombineColor(setcombine.b, 2), + UCodeDisas_ParseCombineColor(setcombine.c, 3), UCodeDisas_ParseCombineColor(setcombine.d, 4), + + UCodeDisas_ParseCombineAlpha(setcombine.z, 1), UCodeDisas_ParseCombineAlpha(setcombine.y, 2), + UCodeDisas_ParseCombineAlpha(setcombine.x, 3), UCodeDisas_ParseCombineAlpha(setcombine.w, 4), + + UCodeDisas_ParseCombineColor(setcombine.e, 1), UCodeDisas_ParseCombineColor(setcombine.f, 2), + UCodeDisas_ParseCombineColor(setcombine.g, 3), UCodeDisas_ParseCombineColor(setcombine.h, 4), + + UCodeDisas_ParseCombineAlpha(setcombine.v, 1), UCodeDisas_ParseCombineAlpha(setcombine.u, 2), + UCodeDisas_ParseCombineAlpha(setcombine.t, 3), UCodeDisas_ParseCombineAlpha(setcombine.s, 4)); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETOTHERMODE_H: { + static F3dzexSetModeMacro sUCodeDisasModeHMacros[] = { + F3DZEX_SETRENDERMACRO("SetAlphaDither", G_MDSFT_ALPHADITHER, 2, G_AD_PATTERN, G_AD_NOTPATTERN, + G_AD_NOISE, G_AD_DISABLE), + F3DZEX_SETRENDERMACRO("SetColorDither", G_MDSFT_RGBDITHER, 2, G_CD_MAGICSQ, G_CD_BAYER, G_CD_NOISE, + -1), + F3DZEX_SETRENDERMACRO("SetCombineKey", G_MDSFT_COMBKEY, 1, G_CK_NONE, G_CK_KEY, -1, -1), + F3DZEX_SETRENDERMACRO("SetTextureConvert", G_MDSFT_TEXTCONV, 3, G_TC_CONV, G_TC_FILTCONV, G_TC_FILT, + -1), + F3DZEX_SETRENDERMACRO("SetTextureFilter", G_MDSFT_TEXTFILT, 2, G_TF_POINT, G_TF_AVERAGE, + G_TF_BILERP, -1), + F3DZEX_SETRENDERMACRO("SetTextureLUT", G_MDSFT_TEXTLUT, 2, G_TT_NONE, G_TT_RGBA16, G_TT_IA16, -1), + F3DZEX_SETRENDERMACRO("SetTextureLOD", G_MDSFT_TEXTLOD, 1, G_TL_TILE, G_TL_LOD, -1, -1), + F3DZEX_SETRENDERMACRO("SetTextureDetail", G_MDSFT_TEXTDETAIL, 2, G_TD_CLAMP, G_TD_SHARPEN, + G_TD_DETAIL, -1), + F3DZEX_SETRENDERMACRO("SetTexturePersp", G_MDSFT_TEXTPERSP, 1, G_TP_PERSP, G_TP_NONE, -1, -1), + F3DZEX_SETRENDERMACRO("SetCycleType", G_MDSFT_CYCLETYPE, 2, G_CYC_1CYCLE, G_CYC_2CYCLE, G_CYC_COPY, + G_CYC_FILL), + F3DZEX_SETRENDERMACRO("SetColorDither", G_MDSFT_COLORDITHER, 2, G_CD_MAGICSQ, G_CD_BAYER, + G_CD_NOISE, -1), + F3DZEX_SETRENDERMACRO("PipelineMode", G_MDSFT_PIPELINE, 1, G_PM_1PRIMITIVE, G_PM_NPRIMITIVE, -1, + -1), + }; + + u32 len = curGfx->setothermode.len + 1; + u32 sft = (-curGfx->setothermode.sft - len) + 32; + u32 s2 = curGfx->setothermode.data * 1; + u32 i1; + u32 i2; + + for (i1 = 0; i1 < ARRAY_COUNTU(sUCodeDisasModeHMacros); i1++) { + if (sft == sUCodeDisasModeHMacros[i1].shift) { + for (i2 = 0; i2 < 4; i2++) { + if (s2 == sUCodeDisasModeHMacros[i1].values[i2].value) { + DISAS_LOG("gsDP%s(%s),", sUCodeDisasModeHMacros[i1].name, + sUCodeDisasModeHMacros[i1].values[i2].name); + goto block_1; + } + } + } + } + DISAS_LOG("gsSPSetOtherModeH(%d, %d, 0x%08x),", sft, len, s2); + + block_1: + this->modeH &= (((1 - (1 << len)) << sft) - 1); + this->modeH |= s2; + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + + } break; + + case G_SETOTHERMODE_L: { + static F3dzexSetModeMacro sUCodeDisasModeLMacros[] = { + F3DZEX_SETRENDERMACRO("gsDPSetAlphaCompare", G_MDSFT_ALPHACOMPARE, 2, G_AC_NONE, G_AC_THRESHOLD, + G_AC_DITHER, -1), + F3DZEX_SETRENDERMACRO("gsDPSetDepthSource", G_MDSFT_ZSRCSEL, 1, G_ZS_PIXEL, G_ZS_PRIM, -1, -1), + }; + + u32 len = curGfx->setothermode.len + 1; + u32 sft = (-curGfx->setothermode.sft - len) + 32; + u32 s2 = curGfx->setothermode.data * 1; + u32 i1; + u32 i2; + + if (sft == G_MDSFT_RENDERMODE) { + DISAS_LOG("\ngsDPSetRenderBlender("); + UCodeDisas_ParseRenderMode(this, s2); + DISAS_LOG("\n),"); + } else { + for (i1 = 0; i1 * 1 < ARRAY_COUNTU(sUCodeDisasModeLMacros); i1++) { + if (sft == sUCodeDisasModeLMacros[i1].shift) { + for (i2 = 0; i2 < 4; i2++) { + if (s2 == sUCodeDisasModeLMacros[i1].values[i2].value) { + DISAS_LOG("gsDP%s(%s),", sUCodeDisasModeLMacros[i1].name, + sUCodeDisasModeLMacros[i1].values[i2].name); + goto block_2; + } + } + } + } + DISAS_LOG("gsSPSetOtherModeL(%d, %d, 0x%08x),", sft, len, s2); + } + + block_2: + this->modeL &= (((1 - (1 << len)) << sft) - 1); + this->modeL |= s2; + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_RDPSETOTHERMODE: { + DISAS_LOG("gsDPSetOtherMode(0x%08x, 0x%08x),", curGfx->words.w0 & 0xFFFFFF, curGfx->words.w1); + this->modeH = curGfx->words.w0 & 0xFFF; + this->modeL = curGfx->words.w1; + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETSCISSOR: { + Gfillrect setscissor = ptr->fillrect; + const char* modeStr; + + modeStr = (setscissor.pad == G_SC_NON_INTERLACE) ? "G_SC_NON_INTERLACE" + : (setscissor.pad == G_SC_ODD_INTERLACE) ? "G_SC_ODD_INTERLACE" + : (setscissor.pad == G_SC_EVEN_INTERLACE) ? "G_SC_EVEN_INTERLACE" + : "???"; + + if ((setscissor.x0frac | setscissor.y0frac | setscissor.x1frac | setscissor.y1frac)) { + if (1) {} + DISAS_LOG("gsDPSetScissorFrac(%s, %d, %d, %d, %d),", modeStr, + (setscissor.x0 << 2) + setscissor.x0frac, (setscissor.y0 << 2) + setscissor.y0frac, + (setscissor.x1 << 2) + setscissor.x1frac, (setscissor.y1 << 2) + setscissor.y1frac); + } else { + DISAS_LOG("gsDPSetScissor(%s, %d, %d, %d, %d),", modeStr, setscissor.x0, setscissor.y0, + setscissor.x1, setscissor.y1); + } + } break; + + case G_FILLRECT: { + Gfillrect fillrect = ptr->fillrect; + + DISAS_LOG("gsDPFillRectangle(%d, %d, %d, %d),", fillrect.x1, fillrect.y1, fillrect.x0, fillrect.y0); + + this->pipeSyncRequired = true; + } break; + + case G_SETCIMG: { + u32 fmt = ((curGfx->words.w0 & 0xE00000) >> 0x15) & 0xFF; + u32 siz = ((curGfx->words.w0 & 0x180000) >> 0x13) & 0xFF; + + DISAS_LOG("gsDPSetColorImage(G_IM_FMT_%s, G_IM_SIZ_%s, %d, 0x%08x(0x%08x) ),", + (fmt == G_IM_FMT_RGBA) ? "RGBA" + : (fmt == G_IM_FMT_YUV) ? "YUV" + : (fmt == G_IM_FMT_CI) ? "CI" + : (fmt == G_IM_FMT_IA) ? "IA" + : "I", + (siz == G_IM_SIZ_4b) ? "4b" + : (siz == G_IM_SIZ_8b) ? "8b" + : (siz == G_IM_SIZ_16b) ? "16b" + : "32b", + (curGfx->dma.len & 0xFFF) + 1, curGfx->setimg.dram, addr); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETZIMG: { + DISAS_LOG("gsDPSetDepthImage(0x%08x(0x%08x)),", curGfx->words.w1, addr); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETTIMG: { + u32 fmt = ((curGfx->words.w0 & 0xE00000) >> 0x15) & 0xFF; + u32 siz = ((curGfx->words.w0 & 0x180000) >> 0x13) & 0xFF; + + DISAS_LOG("gsDPSetTextureImage(G_IM_FMT_%s, G_IM_SIZ_%s, %d, 0x%08x(0x%08x)),", + (fmt == G_IM_FMT_RGBA) ? "RGBA" + : (fmt == G_IM_FMT_YUV) ? "YUV" + : (fmt == G_IM_FMT_CI) ? "CI" + : (fmt == G_IM_FMT_IA) ? "IA" + : "I", + (siz == G_IM_SIZ_4b) ? "4b" + : (siz == G_IM_SIZ_8b) ? "8b" + : (siz == G_IM_SIZ_16b) ? "16b" + : "32b", + (curGfx->dma.len & 0xFFF) + 1, curGfx->setimg.dram, addr); + } break; + + case G_SETENVCOLOR: { + DISAS_LOG("gsDPSetEnvColor(%d, %d, %d, %d),", curGfx->setcolor.r, curGfx->setcolor.g, + curGfx->setcolor.b, curGfx->setcolor.a); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETBLENDCOLOR: { + DISAS_LOG("gsDPSetBlendColor(%d, %d, %d, %d),", curGfx->setcolor.r, curGfx->setcolor.g, + curGfx->setcolor.b, curGfx->setcolor.a); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETFOGCOLOR: { + DISAS_LOG("gsDPSetFogColor(%d, %d, %d, %d),", curGfx->setcolor.r, curGfx->setcolor.g, + curGfx->setcolor.b, curGfx->setcolor.a); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETFILLCOLOR: { + DISAS_LOG("gsDPSetFillColor(0x%08x),", curGfx->words.w1); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETPRIMDEPTH: { + DISAS_LOG("gsDPSetPrimDepth(%d, %d),", curGfx->setprimdepth.z, curGfx->setprimdepth.d); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_SETPRIMCOLOR: { + DISAS_LOG("gsDPSetPrimColor(%d, %d, %d, %d, %d, %d),", curGfx->setcolor.prim_min_level, + curGfx->setcolor.prim_level, curGfx->setcolor.r, curGfx->setcolor.g, curGfx->setcolor.b, + curGfx->setcolor.a); + } break; + + case G_RDPFULLSYNC: { + DISAS_LOG("gsDPFullSync(),"); + + if (this->pipeSyncRequired) { + DISAS_LOG("### PipeSyncが必要です。\n"); + this->syncErr++; + } + } break; + + case G_RDPTILESYNC: { + DISAS_LOG("gsDPTileSync(),"); + this->tileSyncRequired = false; + } break; + + case G_RDPPIPESYNC: { + DISAS_LOG("gsDPPipeSync(),"); + this->pipeSyncRequired = false; + } break; + + case G_RDPLOADSYNC: { + DISAS_LOG("gsDPLoadSync(),"); + this->loadSyncRequired = false; + } break; + + case G_NOOP: { + switch (curGfx->noop.type) { + case 0: { + if (curGfx->noop.value.u32 == 0) { + DISAS_LOG("gsDPNoOp(),"); + } else { + DISAS_LOG("gsDPNoOpTag(%08x),", curGfx->noop.value.u32); + } + } break; + + case 1: { + DISAS_LOG("count_gsDPNoOpHere([%s:%d]),", curGfx->noop.value.u32, curGfx->noop.len); + } break; + + case 7: { + DISAS_LOG("count_gsDPNoOpOpenDisp([%s:%d]),", curGfx->noop.value.u32, curGfx->noop.len); + } break; + + case 8: { + DISAS_LOG("count_gsDPNoOpCloseDisp([%s:%d]),", curGfx->noop.value.u32, curGfx->noop.len); + } break; + + case 2: { + DISAS_LOG("count_gsDPNoOpString(%c%s%c, %d),", '"', curGfx->noop.value.u32, '"', + curGfx->noop.len); + } break; + + case 3: { + DISAS_LOG("count_gsDPNoOpWord(0x%08x, %d),", curGfx->noop.value.u32, curGfx->noop.len); + } break; + + case 4: { + DISAS_LOG("count_gsDPNoOpFloat(%8.3f, %d),", curGfx->noop.value.f32, curGfx->noop.len); + } break; + + case 5: { + if (curGfx->noop.len == 0) { + DISAS_LOG("count_gsDPNoOpQuiet(),"); + } else { + DISAS_LOG("count_gsDPNoOpVerbose(),"); + } + this->enableLog = curGfx->noop.len; + } break; + + case 6: { + /*! @bug arguments are not printed */ + DISAS_LOG("count_gsDPNoOpCallBack(%08x,%d),"); + ((void (*)(UCodeDisas*, u32))curGfx->noop.value.u32)(this, curGfx->noop.len); + } break; + + default: { + DISAS_LOG("gsDPNoOpTag3(%02x, %08x, %04x),", curGfx->noop.type, curGfx->noop.value.u32, + curGfx->noop.len); + } break; + } + } break; + + default: { + switch (this->ucodeType) { + case UCODE_F3DZEX: + case UCODE_UNK: { + switch (cmd) { + case G_MTX: { + Gmatrix gmtx = ptr->matrix; + u32 params; + MtxF mtx; + s32 i1 = 0; + + DISAS_LOG("gsSPMatrix(0x%08x(%08x), 0", gmtx.addr, addr); + + params = (gmtx.params ^ G_MTX_PUSH); + + for (; i1 != ARRAY_COUNT(sUCodeDisasMtxFlags); i1++) { + DISAS_LOG("|%s", (sUCodeDisasMtxFlags[i1].value & params) + ? sUCodeDisasMtxFlags[i1].setName + : sUCodeDisasMtxFlags[i1].unsetName); + } + DISAS_LOG("),", gmtx.addr); /*! @bug gmtx.addr shouldn't be here*/ + + if (this->enableLog >= 2) { + MtxConv_L2F(&mtx, (Mtx*)addr); + DISAS_LOG("\n"); + + /*! @bug %.04x.%04x is a typo, should be %04x.%04x */ + // clang-format off + DISAS_LOG( + "/ %04x.%04x %04x.%04x %04x.%04x %.04x.%04x \\/ %12.6f %12.6f %12.6f %12.6f \\\n" + "| %04x.%04x %04x.%04x %04x.%04x %.04x.%04x || %12.6f %12.6f %12.6f %12.6f |\n" + "| %04x.%04x %04x.%04x %04x.%04x %.04x.%04x || %12.6f %12.6f %12.6f %12.6f |\n" + "\\ %04x.%04x %04x.%04x %04x.%04x %.04x.%04x /\\ %12.6f %12.6f %12.6f %12.6f /\n", + ((Mtx*)addr)->intPart[0][0], ((Mtx*)addr)->fracPart[0][0], + ((Mtx*)addr)->intPart[1][0], ((Mtx*)addr)->fracPart[1][0], + ((Mtx*)addr)->intPart[2][0], ((Mtx*)addr)->fracPart[2][0], + ((Mtx*)addr)->intPart[3][0], ((Mtx*)addr)->fracPart[3][0], + mtx.mf[0][0], mtx.mf[1][0], mtx.mf[2][0], mtx.mf[3][0], + ((Mtx*)addr)->intPart[0][1], ((Mtx*)addr)->fracPart[0][1], + ((Mtx*)addr)->intPart[1][1], ((Mtx*)addr)->fracPart[1][1], + ((Mtx*)addr)->intPart[2][1], ((Mtx*)addr)->fracPart[2][1], + ((Mtx*)addr)->intPart[3][1], ((Mtx*)addr)->fracPart[3][1], + mtx.mf[0][1], mtx.mf[1][1], mtx.mf[2][1], mtx.mf[3][1], + ((Mtx*)addr)->intPart[0][2], ((Mtx*)addr)->fracPart[0][2], + ((Mtx*)addr)->intPart[1][2], ((Mtx*)addr)->fracPart[1][2], + ((Mtx*)addr)->intPart[2][2], ((Mtx*)addr)->fracPart[2][2], + ((Mtx*)addr)->intPart[3][2], ((Mtx*)addr)->fracPart[3][2], + mtx.mf[0][2], mtx.mf[1][2], mtx.mf[2][2], mtx.mf[3][2], + ((Mtx*)addr)->intPart[0][3], ((Mtx*)addr)->fracPart[0][3], + ((Mtx*)addr)->intPart[1][3], ((Mtx*)addr)->fracPart[1][3], + ((Mtx*)addr)->intPart[2][3], ((Mtx*)addr)->fracPart[2][3], + ((Mtx*)addr)->intPart[3][3], ((Mtx*)addr)->fracPart[3][3], + mtx.mf[0][3], mtx.mf[1][3], mtx.mf[2][3], mtx.mf[3][3]); + // clang-format on + } + } break; + + case G_VTX: { + u32 numv = curGfx->words.w0; + u32 vbidx; + + numv >>= 12; + numv &= 0xFF; + vbidx = (curGfx->vtx.vbidx >> 1) - numv; + + DISAS_LOG("gsSPVertex(0x%08x(0x%08x), %d, %d),", curGfx->words.w1, addr, numv, vbidx); + + this->vtxCnt += numv; + this->spvtxCnt++; + + if (this->enableLog >= 2) { + UCodeDisas_PrintVertices(this, addr, numv, vbidx); + } + } break; + + case G_MODIFYVTX: { + DISAS_LOG("gsSPModifyVertex(%d, %s, %08x),", curGfx->dma.par, + (curGfx->dma.len == G_MWO_POINT_RGBA) ? "G_MWO_POINT_RGBA" + : (curGfx->dma.len == G_MWO_POINT_ST) ? "G_MWO_POINT_ST" + : (curGfx->dma.len == G_MWO_POINT_XYSCREEN) ? "G_MWO_POINT_XYSCREEN" + : (curGfx->dma.len == G_MWO_POINT_ZSCREEN) ? "G_MWO_POINT_ZSCREEN" + : "G_MWO_POINT_????", + curGfx->dma.addr); + this->vtxCnt += curGfx->dma.par; + this->spvtxCnt++; + } break; + + case G_TRI1: { + Gtri1 gtri = ptr->tri1; + Gtrimod tri = gtri.tri; + + DISAS_LOG("gsSP1Triangle(%d, %d, %d),", tri.v0 / 2, tri.v1 / 2, tri.v2 / 2); + + this->tri1Cnt++; + this->pipeSyncRequired = true; + } break; + + case G_LINE3D: { + if (curGfx->linefix.wd == 0) { + DISAS_LOG("gsSPLine3D(%d, %d),", curGfx->linefix.v0, curGfx->linefix.v1); + } else { + DISAS_LOG("gsSPLineW3D(%d, %d, %d),", curGfx->linefix.v0, curGfx->linefix.v1, + curGfx->linefix.wd); + } + + this->lineCnt++; + this->pipeSyncRequired = true; + } break; + + case G_TRI2: { + Gtri2 tri2 = ptr->tri2; + u32 v0, v1, v2; + u32 v3, v4, v5; + + v0 = tri2.tri1.v0 / 2; + v1 = tri2.tri1.v1 / 2; + v2 = tri2.tri1.v2 / 2; + + v3 = tri2.tri2.v0 / 2; + v4 = tri2.tri2.v1 / 2; + v5 = tri2.tri2.v2 / 2; + + DISAS_LOG("gsSP2Triangles(%d, %d, %d, 0, %d, %d, %d, 0),", v0, v1, v2, v3, v4, v5); + + this->tri2Cnt++; + this->pipeSyncRequired = true; + } break; + + case G_QUAD: { + Gquadmod quad = ptr->quad; + u32 v0, v1, v2, v3; + + v0 = quad.v0 / 2; + v1 = quad.v1 / 2; + v2 = quad.v2 / 2; + v3 = quad.v3 / 2; + + DISAS_LOG("gsSP1Quadrangle(%d, %d, %d, %d, 0),", v0, v1, v2, v3); + + this->quadCnt++; + this->pipeSyncRequired = true; + } break; + + case G_CULLDL: { + DISAS_LOG("gsSPCullDisplayList(%d, %d),", (curGfx->cull.vstart) / 2, + (curGfx->cull.vend) / 2); + } break; + + case G_BRANCH_Z: { + addr = UCodeDisas_TranslateAddr(this, rdpHalf); + DISAS_LOG("gsSPBranchLessZraw(0x%08x(0x%08x), %d, 0x%08x),", rdpHalf, addr, + (curGfx->words.w0 & 0xFFF) / 2, curGfx->words.w1); + ptr = (GfxMod*)addr - 1; + } break; + + case G_TEXTURE: { + Gtexturemod texture = ptr->texmod; + + if (texture.lodscale == 0) { + DISAS_LOG("gsSPTexture(%d, %d, %d, %d, %s),", texture.s, texture.t, texture.level, + texture.tile, texture.on ? "G_ON" : "G_OFF"); + } else { + DISAS_LOG("gsSPTextureL(%d, %d, %d, %d, %d, %s),", texture.s, texture.t, + texture.level, texture.lodscale, texture.tile, + texture.on ? "G_ON" : "G_OFF"); + } + } break; + + case G_POPMTX: { + Gpopmtxmod popmtx = ptr->popmtxmod; + + if (popmtx.param == 1) { + DISAS_LOG("gsSPPopMatrix(G_MTX_MODELVIEW),"); + } else { + DISAS_LOG("gsSPPopMatrixN(G_MTX_MODELVIEW, %d),", popmtx.param); + } + } break; + + case G_GEOMETRYMODE: { + u32 clearbits = curGfx->words.w0 & 0xFFFFFF; + u32 setbits = curGfx->words.w1 & 0xFFFFFF; + + if (clearbits == 0) { + DISAS_LOG("gsSPLoadGeometryMode("); + UCodeDisas_ParseGeometryMode(this, setbits); + DISAS_LOG("),"); + } else if (setbits == 0) { + DISAS_LOG("gsSPClearGeometryMode("); + UCodeDisas_ParseGeometryMode(this, ~clearbits); + DISAS_LOG("),"); + } else if (clearbits == 0xFFFFFF) { + DISAS_LOG("gsSPSetGeometryMode("); + UCodeDisas_ParseGeometryMode(this, setbits); + DISAS_LOG("),"); + } else { + DISAS_LOG("gsSPGeometryMode("); + UCodeDisas_ParseGeometryMode(this, ~clearbits); + DISAS_LOG(", "); + UCodeDisas_ParseGeometryMode(this, setbits); + DISAS_LOG("),"); + } + + this->geometryMode &= clearbits; + this->geometryMode |= setbits; + break; // this break needs to be inside, but the other cases need their breaks to be + // outside... + } + + case G_MOVEWORD: { + u32 pad; + Gdma dma = ptr->dma; + Gmovewd movewd = ptr->movewd; + + movewd.index = dma.par; + movewd.offset = dma.len; + movewd.data = dma.addr; + switch (movewd.index) { + case G_MW_SEGMENT: { + DISAS_LOG("gsSPSegment(%d, 0x%08x),", movewd.offset / 4, movewd.data); + this->segments[movewd.offset / 4] = movewd.data & 0xFFFFFF; + } break; + + case G_MW_CLIP: { + DISAS_LOG("gsSPClipRatio(FRUSTRATIO_%d), ", + (movewd.data != 0) ? movewd.data : -movewd.data); + ptr += 4 - 1; + } break; + + case G_MW_NUMLIGHT: { + DISAS_LOG("gsSPNumLights(%d), ", movewd.data / 24); + } break; + + case G_MW_LIGHTCOL: { + DISAS_LOG("gsSPLightColor(%d, %d), ", ((movewd.offset & 0xF0) >> 5) + 1, + movewd.data); + ptr += 2 - 1; + } break; + + case G_MW_FOG: { + DISAS_LOG("gsSPFogFactor(%d, %d),", movewd.data >> 16, movewd.data & 0xFFFF); + } break; + + case G_MW_PERSPNORM: { + DISAS_LOG("gsSPPerspNormalize(%d),", movewd.data); + } break; + + default: { + DISAS_LOG("gsMoveWd(%d, %d, %d), ", movewd.index, movewd.offset, movewd.data); + } break; + } + } break; + + case G_MOVEMEM: { + Gmovemem movemem = ptr->movemem; + Vp_t* vp = (Vp_t*)addr; + + switch (movemem.index) { + case G_MV_VIEWPORT: { + DISAS_LOG("gsSPViewport(0x%08x(0x%08x)),", movemem.data, vp); + DISAS_LOG("\t# vscale=[%d %d %d %d], ", vp->vscale[0], vp->vscale[1], + vp->vscale[2], vp->vscale[3]); + DISAS_LOG("vtrans=[%d %d %d %d] ", vp->vtrans[0], vp->vtrans[1], vp->vtrans[2], + vp->vtrans[3]); + } break; + + case G_MV_MATRIX: { + DISAS_LOG("gsSPForceMatrix(0x%08x),", movemem.data); + ptr += 1; + } break; + + case G_MV_LIGHT: { + switch ((movemem.offset * 8)) { + case G_MVO_LOOKATX: { + DISAS_LOG("gsSPLookAtX(0x%08x),", movemem.data); + } break; + + case G_MVO_LOOKATY: { + DISAS_LOG("gsSPLookAtY(0x%08x),", movemem.data); + } break; + + default: { + DISAS_LOG("gsSPLight(0x%08x,%d),", movemem.data, + (movemem.offset * 8 - 24) / 24); + } break; + } + } break; + + default: { + DISAS_LOG("gsMoveMem(0x%08x, %d, %d, %d),", movemem.data, + ((movemem.size >> 3) + 1) * 8, movemem.index, movemem.offset * 8); + } break; + } + } break; + + default: { + DISAS_LOG("AnyDisplayList(),"); + } break; + } + } break; + + case UCODE_S2DEX: { + switch (cmd) { + case G_BG_COPY: { + Gwords words = ptr->words; + + DISAS_LOG("gsSPBgRectCopy(0x%08x(0x%08x)),", words.w1, addr); + this->pipeSyncRequired = true; + } break; + + case G_BG_1CYC: { + Gwords words = ptr->words; + + DISAS_LOG("gsSPBgRect1Cyc(0x%08x(0x%08x)),", words.w1, addr); + this->pipeSyncRequired = true; + } break; + + case G_OBJ_SPRITE: { + Gwords words = ptr->words; + + DISAS_LOG("gsSPObjSprite(0x%08x(0x%08x)),", words.w1, addr); + this->pipeSyncRequired = true; + } break; + + case G_OBJ_RECTANGLE: { + Gwords words = ptr->words; + + DISAS_LOG("gsSPObjRectangle(0x%08x(0x%08x)),", words.w1, addr); + this->pipeSyncRequired = true; + } break; + + case G_OBJ_RECTANGLE_R: { + Gwords words = ptr->words; + + DISAS_LOG("gsSPObjRectangleR(0x%08x(0x%08x)),", words.w1, addr); + this->pipeSyncRequired = true; + } break; + + case G_RDPHALF_0: { + DISAS_LOG("RDPHALF_0(0x%02x, 0x%08x, 0x%04x),", curGfx->dma.par, curGfx->dma.addr, + curGfx->dma.len); + sid = curGfx->dma.par; + rdpHalf = curGfx->dma.addr; + linkDlLow = curGfx->dma.len; + } break; + + case G_OBJ_MOVEMEM: { + Gdma dma = ptr->dma; + + if (dma.par == 23) { + DISAS_LOG("gsSPObjMatrix(0x%08x(0x%08x)),", dma.addr, addr); + } else { + DISAS_LOG("gsSPObjSubMatrix(0x%08x(0x%08x)),", dma.addr, addr); + } + } break; + + case G_OBJ_LOADTXTR: { + Gdma dma = ptr->dma; + + DISAS_LOG("gsSPObjLoadTxtr(0x%08x(0x%08x)),", dma.addr, addr); + } break; + + case G_OBJ_LDTX_SPRITE: { + Gdma dma = ptr->dma; + + DISAS_LOG("gsSPObjLoadTxSprite(0x%08x(0x%08x)),", dma.addr, addr); + } break; + + case G_OBJ_LDTX_RECT: { + Gdma dma = ptr->dma; + + DISAS_LOG("gsSPObjLoadTxRect(0x%08x(0x%08x)),", dma.addr, addr); + } break; + + case G_OBJ_LDTX_RECT_R: { + Gdma dma = ptr->dma; + + DISAS_LOG("gsSPObjLoadTxRectR(0x%08x(0x%08x)),", dma.addr, addr); + } break; + + case G_SELECT_DL: { + Gdma dma = ptr->dma; + u32 dlAddr = UCodeDisas_TranslateAddr(this, (dma.len << 16) | (linkDlLow)); + u32 dmaAddr = dma.addr; + + if (dma.par == 0) { + DISAS_LOG("gsSPSelectDL(0x%08x, %d, 0x%08x, 0x%08x),", dlAddr, sid, rdpHalf, + dmaAddr); + } else { + DISAS_LOG("gsSPSelectBranchDL(0x%08x, %d, 0x%08x, 0x%08x),", dlAddr, sid, rdpHalf, + dmaAddr); + } + } break; + + case G_MOVEWORD: { + u32 pad[2]; + Gmovewd movewd = ptr->movewd; + + switch (movewd.index) { + case 6: { + u32 segId = movewd.offset / 4; + + DISAS_LOG("gsSPSegment(%d, 0x%08x),", segId, movewd.data); + this->segments[segId] = movewd.data & 0xFFFFFF; + } break; + + case 8: { + DISAS_LOG("gsSPSetStatus(0x%08x, 0x%08x),", movewd.offset, movewd.data); + } break; + + default: { + DISAS_LOG("gsMoveWd(%d, %d, %d), ", movewd.index, movewd.offset, movewd.data); + } break; + } + } break; + + case G_OBJ_RENDERMODE: { + Gdma dma = ptr->dma; + + DISAS_LOG("gsSPObjRenderMode(0x%08x),", dma.addr); + } break; + + default: { + DISAS_LOG("AnyDisplayList(),"); + } break; + } + } + } + } break; + } + + DISAS_LOG("\n"); + + ptr++; + } +} + +void UCodeDisas_RegisterUCode(UCodeDisas* this, s32 count, UCodeInfo* ucodeArray) { + this->ucodeInfoCount = count; + this->ucodeInfo = ucodeArray; +} + +void UCodeDisas_SetCurUCode(UCodeDisas* this, void* ptr) { + UCodeDisas_SetCurUCodeImpl(this, ptr); +} +#endif diff --git a/soh/src/code/z_DLF.c b/soh/src/code/z_DLF.c new file mode 100644 index 000000000..3409cc779 --- /dev/null +++ b/soh/src/code/z_DLF.c @@ -0,0 +1,111 @@ +#include "global.h" +#include "vt.h" + +void Overlay_LoadGameState(GameStateOverlay* overlayEntry) { + if (overlayEntry->loadedRamAddr != NULL) { + osSyncPrintf("既にリンクされています\n"); // "Already linked" + return; + } + + if (overlayEntry->vramStart == 0) { + overlayEntry->unk_28 = 0; + } else { + overlayEntry->loadedRamAddr = Overlay_AllocateAndLoad(overlayEntry->vromStart, overlayEntry->vromEnd, + overlayEntry->vramStart, overlayEntry->vramEnd); + + if (overlayEntry->loadedRamAddr == NULL) { + osSyncPrintf("ロードに失敗しました\n"); // "Loading failed" + return; + } + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("OVL(d):Seg:%08x-%08x Ram:%08x-%08x Off:%08x %s\n", overlayEntry->vramStart, overlayEntry->vramEnd, + overlayEntry->loadedRamAddr, + (uintptr_t)overlayEntry->loadedRamAddr + (uintptr_t)overlayEntry->vramEnd - (uintptr_t)overlayEntry->vramStart, + (uintptr_t)overlayEntry->vramStart - (uintptr_t)overlayEntry->loadedRamAddr, ""); + osSyncPrintf(VT_RST); + + if (overlayEntry->unk_14 != NULL) { + overlayEntry->unk_14 = (void*)((uintptr_t)overlayEntry->unk_14 - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->unk_14 = NULL; + } + + if (overlayEntry->init != NULL) { + overlayEntry->init = (void*)((uintptr_t)overlayEntry->init - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->init = NULL; + } + + if (overlayEntry->destroy != NULL) { + overlayEntry->destroy = (void*)((uintptr_t)overlayEntry->destroy - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->destroy = NULL; + } + + if (overlayEntry->unk_20 != NULL) { + overlayEntry->unk_20 = (void*)((uintptr_t)overlayEntry->unk_20 - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->unk_20 = NULL; + } + + if (overlayEntry->unk_24 != NULL) { + overlayEntry->unk_24 = (void*)((uintptr_t)overlayEntry->unk_24 - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->unk_24 = NULL; + } + + overlayEntry->unk_28 = 0; + } +} + +void Overlay_FreeGameState(GameStateOverlay* overlayEntry) { + if (overlayEntry->loadedRamAddr != NULL) { + s32 temp = overlayEntry->unk_28 != 0 ? -1 : 0; + + if (temp == 0) { + if (overlayEntry->unk_14 != NULL) { + overlayEntry->unk_14 = (void*)((uintptr_t)overlayEntry->unk_14 + + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->unk_14 = NULL; + } + + if (overlayEntry->init != NULL) { + overlayEntry->init = (void*)((uintptr_t)overlayEntry->init + + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->init = NULL; + } + + if (overlayEntry->destroy != NULL) { + overlayEntry->destroy = (void*)((uintptr_t)overlayEntry->destroy + + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->destroy = NULL; + } + + if (overlayEntry->unk_20 != NULL) { + overlayEntry->unk_20 = (void*)((uintptr_t)overlayEntry->unk_20 + + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->unk_20 = NULL; + } + + if (overlayEntry->unk_24 != NULL) { + overlayEntry->unk_24 = (void*)((uintptr_t)overlayEntry->unk_24 + + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)); + } else { + overlayEntry->unk_24 = NULL; + } + + SystemArena_FreeDebug(overlayEntry->loadedRamAddr, "../z_DLF.c", 149); + overlayEntry->loadedRamAddr = NULL; + } + } +} diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c new file mode 100644 index 000000000..92702f866 --- /dev/null +++ b/soh/src/code/z_actor.c @@ -0,0 +1,5833 @@ +#include "global.h" +#include "vt.h" + +#include "overlays/actors/ovl_Arms_Hook/z_arms_hook.h" +#include "overlays/actors/ovl_En_Part/z_en_part.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "objects/object_bdoor/object_bdoor.h" + +#ifdef _MSC_VER +#include +#include +#include +#endif + +#ifdef _MSC_VER +#include "textures/place_title_cards/g_pn_49.h" +#include "textures/place_title_cards/g_pn_01.h" +#include "textures/place_title_cards/g_pn_02.h" +#include "textures/place_title_cards/g_pn_03.h" +#include "textures/place_title_cards/g_pn_04.h" +#include "textures/place_title_cards/g_pn_05.h" +#include "textures/place_title_cards/g_pn_06.h" +#include "textures/place_title_cards/g_pn_07.h" +#include "textures/place_title_cards/g_pn_08.h" +#include "textures/place_title_cards/g_pn_09.h" +#include "textures/place_title_cards/g_pn_10.h" +#include "textures/place_title_cards/g_pn_11.h" +#include "textures/place_title_cards/g_pn_12.h" +#include "textures/place_title_cards/g_pn_13.h" +#include "textures/place_title_cards/g_pn_14.h" +#include "textures/place_title_cards/g_pn_15.h" +#include "textures/place_title_cards/g_pn_16.h" +#include "textures/place_title_cards/g_pn_17.h" +#include "textures/place_title_cards/g_pn_18.h" +#include "textures/place_title_cards/g_pn_19.h" +#include "textures/place_title_cards/g_pn_20.h" +#include "textures/place_title_cards/g_pn_21.h" +#include "textures/place_title_cards/g_pn_22.h" +#include "textures/place_title_cards/g_pn_23.h" +#include "textures/place_title_cards/g_pn_24.h" +#include "textures/place_title_cards/g_pn_25.h" +#include "textures/place_title_cards/g_pn_26.h" +#include "textures/place_title_cards/g_pn_27.h" +#include "textures/place_title_cards/g_pn_28.h" +#include "textures/place_title_cards/g_pn_29.h" +#include "textures/place_title_cards/g_pn_30.h" +#include "textures/place_title_cards/g_pn_31.h" +#include "textures/place_title_cards/g_pn_32.h" +#include "textures/place_title_cards/g_pn_33.h" +#include "textures/place_title_cards/g_pn_34.h" +#include "textures/place_title_cards/g_pn_35.h" +#include "textures/place_title_cards/g_pn_36.h" +#include "textures/place_title_cards/g_pn_37.h" +#include "textures/place_title_cards/g_pn_38.h" +#include "textures/place_title_cards/g_pn_39.h" +#include "textures/place_title_cards/g_pn_40.h" +#include "textures/place_title_cards/g_pn_41.h" +#include "textures/place_title_cards/g_pn_42.h" +#include "textures/place_title_cards/g_pn_43.h" +#include "textures/place_title_cards/g_pn_44.h" +#include "textures/place_title_cards/g_pn_45.h" +#include "textures/place_title_cards/g_pn_46.h" +#include "textures/place_title_cards/g_pn_47.h" +#include "textures/place_title_cards/g_pn_48.h" +#include "textures/place_title_cards/g_pn_50.h" +#include "textures/place_title_cards/g_pn_51.h" +#include "textures/place_title_cards/g_pn_52.h" +#include "textures/place_title_cards/g_pn_53.h" +#include "textures/place_title_cards/g_pn_54.h" +#include "textures/place_title_cards/g_pn_55.h" +#include "textures/place_title_cards/g_pn_56.h" +#include "textures/place_title_cards/g_pn_57.h" +#endif + +static CollisionPoly* sCurCeilingPoly; +static s32 sCurCeilingBgId; + +void ActorShape_Init(ActorShape* shape, f32 yOffset, ActorShadowFunc shadowDraw, f32 shadowScale) { + shape->yOffset = yOffset; + shape->shadowDraw = shadowDraw; + shape->shadowScale = shadowScale; + shape->shadowAlpha = 255; +} + +void ActorShadow_Draw(Actor* actor, Lights* lights, GlobalContext* globalCtx, Gfx* dlist, Color_RGBA8* color) { + f32 temp1; + f32 temp2; + MtxF sp60; + + if (actor->floorPoly != NULL) { + temp1 = actor->world.pos.y - actor->floorHeight; + + if (temp1 >= -50.0f && temp1 < 500.0f) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 1553); + + POLY_OPA_DISP = Gfx_CallSetupDL(POLY_OPA_DISP, 0x2C); + + gDPSetCombineLERP(POLY_OPA_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, COMBINED, 0, 0, 0, + COMBINED); + + temp1 = (temp1 < 0.0f) ? 0.0f : ((temp1 > 150.0f) ? 150.0f : temp1); + temp2 = 1.0f - (temp1 * (1.0f / 350)); + + if (color != NULL) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, color->r, color->g, color->b, + (u32)(actor->shape.shadowAlpha * temp2) & 0xFF); + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, (u32)(actor->shape.shadowAlpha * temp2) & 0xFF); + } + + func_80038A28(actor->floorPoly, actor->world.pos.x, actor->floorHeight, actor->world.pos.z, &sp60); + Matrix_Put(&sp60); + + if (dlist != gCircleShadowDL) { + Matrix_RotateY(actor->shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + } + + temp2 = (1.0f - (temp1 * (1.0f / 350))) * actor->shape.shadowScale; + Matrix_Scale(actor->scale.x * temp2, 1.0f, actor->scale.z * temp2, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 1588), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dlist); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 1594); + } + } +} + +void ActorShadow_DrawCircle(Actor* actor, Lights* lights, GlobalContext* globalCtx) { + ActorShadow_Draw(actor, lights, globalCtx, gCircleShadowDL, NULL); +} + +void ActorShadow_DrawWhiteCircle(Actor* actor, Lights* lights, GlobalContext* globalCtx) { + static Color_RGBA8 white = { 255, 255, 255, 255 }; + + ActorShadow_Draw(actor, lights, globalCtx, gCircleShadowDL, &white); +} + +void ActorShadow_DrawHorse(Actor* actor, Lights* lights, GlobalContext* globalCtx) { + ActorShadow_Draw(actor, lights, globalCtx, gHorseShadowDL, NULL); +} + +void ActorShadow_DrawFoot(GlobalContext* globalCtx, Light* light, MtxF* arg2, s32 arg3, f32 arg4, f32 arg5, f32 arg6) { + s32 pad1; + f32 sp58; + s32 pad2[2]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 1661); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, + (u32)(((arg3 * 0.00005f) > 1.0f ? 1.0f : (arg3 * 0.00005f)) * arg4) & 0xFF); + + sp58 = Math_FAtan2F(light->l.dir[0], light->l.dir[2]); + arg6 *= (4.5f - (light->l.dir[1] * 0.035f)); + arg6 = (arg6 < 1.0f) ? 1.0f : arg6; + Matrix_Put(arg2); + Matrix_RotateY(sp58, MTXMODE_APPLY); + Matrix_Scale(arg5, 1.0f, arg5 * arg6, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 1687), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gFootShadowDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 1693); +} + +void ActorShadow_DrawFeet(Actor* actor, Lights* lights, GlobalContext* globalCtx) { + f32 distToFloor = actor->world.pos.y - actor->floorHeight; + + if (distToFloor > 20.0f) { + f32 shadowScale = actor->shape.shadowScale; + u8 shadowAlpha = actor->shape.shadowAlpha; + f32 alphaRatio; + + actor->shape.shadowScale *= 0.3f; + alphaRatio = (distToFloor - 20.0f) * 0.02f; + actor->shape.shadowAlpha = (f32)actor->shape.shadowAlpha * CLAMP_MAX(alphaRatio, 1.0f); + ActorShadow_DrawCircle(actor, lights, globalCtx); + actor->shape.shadowScale = shadowScale; + actor->shape.shadowAlpha = shadowAlpha; + } + + if (distToFloor < 200.0f) { + MtxF floorMtx; + f32 floorHeight[2]; // One for each foot + f32 distToFloor; + f32 shadowAlpha; + f32 shadowScaleX; + f32 shadowScaleZ; + Light* lightPtr; + s32 lightNum; + s32 lightNumMax; + s32 i; + s32 j; + s32 numLights = lights->numLights - 2; + Light* firstLightPtr = &lights->l.l[0]; + Vec3f* feetPosPtr = actor->shape.feetPos; + f32* floorHeightPtr = floorHeight; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 1741); + + POLY_OPA_DISP = Gfx_CallSetupDL(POLY_OPA_DISP, 0x2C); + + actor->shape.feetFloorFlags = 0; + + for (i = 0; i < 2; i++) { + feetPosPtr->y += 50.0f; + *floorHeightPtr = func_800BFCB8(globalCtx, &floorMtx, feetPosPtr); + feetPosPtr->y -= 50.0f; + actor->shape.feetFloorFlags <<= 1; + distToFloor = feetPosPtr->y - *floorHeightPtr; + + if ((-1.0f <= distToFloor) && (distToFloor < 500.0f)) { + if (distToFloor <= 0.0f) { + actor->shape.feetFloorFlags++; + } + distToFloor = CLAMP_MAX(distToFloor, 30.0f); + shadowAlpha = (f32)actor->shape.shadowAlpha * (1.0f - (distToFloor * (1.0f / 30.0f))); + distToFloor = CLAMP_MAX(distToFloor, 30.0f); + shadowScaleZ = 1.0f - (distToFloor * (1.0f / (30.0f + 40.0f))); + shadowScaleX = shadowScaleZ * actor->shape.shadowScale * actor->scale.x; + lightNumMax = 0; + lightPtr = firstLightPtr; + + for (j = 0; j < numLights; j++) { + if (lightPtr->l.dir[1] > 0) { + lightNum = + (lightPtr->l.col[0] + lightPtr->l.col[1] + lightPtr->l.col[2]) * ABS(lightPtr->l.dir[1]); + if (lightNum > 0) { + lightNumMax += lightNum; + ActorShadow_DrawFoot(globalCtx, lightPtr, &floorMtx, lightNum, shadowAlpha, shadowScaleX, + shadowScaleZ); + } + } + lightPtr++; + } + + for (j = 0; j < 2; j++) { + if (lightPtr->l.dir[1] > 0) { + lightNum = + ((lightPtr->l.col[0] + lightPtr->l.col[1] + lightPtr->l.col[2]) * ABS(lightPtr->l.dir[1])) - + (lightNumMax * 8); + if (lightNum > 0) { + ActorShadow_DrawFoot(globalCtx, lightPtr, &floorMtx, lightNum, shadowAlpha, shadowScaleX, + shadowScaleZ); + } + } + lightPtr++; + } + } + feetPosPtr++; + floorHeightPtr++; + } + + if (!(actor->bgCheckFlags & 1)) { + actor->shape.feetFloorFlags = 0; + } else if (actor->shape.feetFloorFlags == 3) { + f32 footDistY = actor->shape.feetPos[FOOT_LEFT].y - actor->shape.feetPos[FOOT_RIGHT].y; + + if ((floorHeight[0] + footDistY) < (floorHeight[1] - footDistY)) { + actor->shape.feetFloorFlags = 2; + } else { + actor->shape.feetFloorFlags = 1; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 1831); + } +} + +void Actor_SetFeetPos(Actor* actor, s32 limbIndex, s32 leftFootIndex, Vec3f* leftFootPos, s32 rightFootIndex, + Vec3f* rightFootPos) { + if (limbIndex == leftFootIndex) { + Matrix_MultVec3f(leftFootPos, &actor->shape.feetPos[FOOT_LEFT]); + } else if (limbIndex == rightFootIndex) { + Matrix_MultVec3f(rightFootPos, &actor->shape.feetPos[FOOT_RIGHT]); + } +} + +void func_8002BE04(GlobalContext* globalCtx, Vec3f* arg1, Vec3f* arg2, f32* arg3) { + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, arg1, arg2, arg3); + *arg3 = (*arg3 < 1.0f) ? 1.0f : (1.0f / *arg3); +} + +typedef struct { + /* 0x00 */ Color_RGBA8 inner; + /* 0x04 */ Color_RGBA8 outer; +} NaviColor; // size = 0x8 + +NaviColor sNaviColorList[] = { + { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, + { { 255, 255, 255, 255 }, { 0, 0, 255, 0 } }, { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, + { { 150, 150, 255, 255 }, { 150, 150, 255, 0 } }, { { 255, 255, 0, 255 }, { 200, 155, 0, 0 } }, + { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, + { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, { { 255, 255, 0, 255 }, { 200, 155, 0, 0 } }, + { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, + { { 0, 255, 0, 255 }, { 0, 255, 0, 0 } }, +}; + +// unused +Gfx D_80115FF0[] = { + gsSPEndDisplayList(), +}; + +void func_8002BE64(TargetContext* targetCtx, s32 index, f32 arg2, f32 arg3, f32 arg4) { + targetCtx->arr_50[index].pos.x = arg2; + targetCtx->arr_50[index].pos.y = arg3; + targetCtx->arr_50[index].pos.z = arg4; + targetCtx->arr_50[index].unk_0C = targetCtx->unk_44; +} + +void func_8002BE98(TargetContext* targetCtx, s32 actorCategory, GlobalContext* globalCtx) { + TargetContextEntry* entry; + NaviColor* naviColor; + s32 i; + + Math_Vec3f_Copy(&targetCtx->targetCenterPos, &globalCtx->view.eye); + targetCtx->unk_44 = 500.0f; + targetCtx->unk_48 = 0x100; + + naviColor = &sNaviColorList[actorCategory]; + + entry = &targetCtx->arr_50[0]; + for (i = 0; i < ARRAY_COUNT(targetCtx->arr_50); i++) { + func_8002BE64(targetCtx, i, 0.0f, 0.0f, 0.0f); + entry->color.r = naviColor->inner.r; + entry->color.g = naviColor->inner.g; + entry->color.b = naviColor->inner.b; + entry++; + } +} + +void func_8002BF60(TargetContext* targetCtx, Actor* actor, s32 actorCategory, GlobalContext* globalCtx) { + NaviColor* naviColor = &sNaviColorList[actorCategory]; + targetCtx->naviRefPos.x = actor->focus.pos.x; + targetCtx->naviRefPos.y = actor->focus.pos.y + (actor->targetArrowOffset * actor->scale.y); + targetCtx->naviRefPos.z = actor->focus.pos.z; + targetCtx->naviInner.r = naviColor->inner.r; + targetCtx->naviInner.g = naviColor->inner.g; + targetCtx->naviInner.b = naviColor->inner.b; + targetCtx->naviInner.a = naviColor->inner.a; + targetCtx->naviOuter.r = naviColor->outer.r; + targetCtx->naviOuter.g = naviColor->outer.g; + targetCtx->naviOuter.b = naviColor->outer.b; + targetCtx->naviOuter.a = naviColor->outer.a; +} + +void func_8002C0C0(TargetContext* targetCtx, Actor* actor, GlobalContext* globalCtx) { + targetCtx->arrowPointedActor = NULL; + targetCtx->targetedActor = NULL; + targetCtx->unk_40 = 0.0f; + targetCtx->unk_8C = NULL; + targetCtx->bgmEnemy = NULL; + targetCtx->unk_4B = 0; + targetCtx->unk_4C = 0; + func_8002BF60(targetCtx, actor, actor->category, globalCtx); + func_8002BE98(targetCtx, actor->category, globalCtx); +} + +void func_8002C124(TargetContext* targetCtx, GlobalContext* globalCtx) { + Actor* actor = targetCtx->targetedActor; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 2029); + + if (targetCtx->unk_48 != 0) { + TargetContextEntry* entry; + Player* player; + s16 spCE; + f32 temp1; + Vec3f spBC; + s32 spB8; + f32 spB4; + s32 spB0; + s32 spAC; + f32 var1; + f32 var2; + s32 i; + + player = GET_PLAYER(globalCtx); + + spCE = 0xFF; + var1 = 1.0f; + + if (targetCtx->unk_4B != 0) { + spB8 = 1; + } else { + spB8 = 3; + } + + if (actor != NULL) { + Math_Vec3f_Copy(&targetCtx->targetCenterPos, &actor->focus.pos); + var1 = (500.0f - targetCtx->unk_44) / 420.0f; + } else { + targetCtx->unk_48 -= 120; + if (targetCtx->unk_48 < 0) { + targetCtx->unk_48 = 0; + } + spCE = targetCtx->unk_48; + } + + func_8002BE04(globalCtx, &targetCtx->targetCenterPos, &spBC, &spB4); + + spBC.x = (160 * (spBC.x * spB4)) * var1; + spBC.x = CLAMP(spBC.x, -320.0f, 320.0f); + + spBC.y = (120 * (spBC.y * spB4)) * var1; + spBC.y = CLAMP(spBC.y, -240.0f, 240.0f); + + spBC.z = spBC.z * var1; + + targetCtx->unk_4C--; + if (targetCtx->unk_4C < 0) { + targetCtx->unk_4C = 2; + } + + func_8002BE64(targetCtx, targetCtx->unk_4C, spBC.x, spBC.y, spBC.z); + + if ((!(player->stateFlags1 & 0x40)) || (actor != player->unk_664)) { + OVERLAY_DISP = Gfx_CallSetupDL(OVERLAY_DISP, 0x39); + + for (spB0 = 0, spAC = targetCtx->unk_4C; spB0 < spB8; spB0++, spAC = (spAC + 1) % 3) { + entry = &targetCtx->arr_50[spAC]; + + if (entry->unk_0C < 500.0f) { + if (entry->unk_0C <= 120.0f) { + var2 = 0.15f; + } else { + var2 = ((entry->unk_0C - 120.0f) * 0.001f) + 0.15f; + } + + Matrix_Translate(entry->pos.x, entry->pos.y, 0.0f, MTXMODE_NEW); + Matrix_Scale(var2, 0.15f, 1.0f, MTXMODE_APPLY); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, entry->color.r, entry->color.g, entry->color.b, (u8)spCE); + + Matrix_RotateZ((targetCtx->unk_4B & 0x7F) * (M_PI / 64), MTXMODE_APPLY); + + for (i = 0; i < 4; i++) { + Matrix_RotateZ(M_PI / 2, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Translate(entry->unk_0C, entry->unk_0C, 0.0f, MTXMODE_APPLY); + gSPMatrix(OVERLAY_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 2116), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(OVERLAY_DISP++, gZTargetLockOnTriangleDL); + Matrix_Pop(); + } + } + + spCE -= 0xFF / 3; + if (spCE < 0) { + spCE = 0; + } + } + } + } + + actor = targetCtx->unk_94; + if ((actor != NULL) && !(actor->flags & ACTOR_FLAG_27)) { + NaviColor* naviColor = &sNaviColorList[actor->category]; + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x7); + + Matrix_Translate(actor->focus.pos.x, actor->focus.pos.y + (actor->targetArrowOffset * actor->scale.y) + 17.0f, + actor->focus.pos.z, MTXMODE_NEW); + Matrix_RotateY((f32)((u16)(globalCtx->gameplayFrames * 3000)) * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale((iREG(27) + 35) / 1000.0f, (iREG(28) + 60) / 1000.0f, (iREG(29) + 50) / 1000.0f, MTXMODE_APPLY); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, naviColor->inner.r, naviColor->inner.g, naviColor->inner.b, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 2153), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, gZTargetArrowDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 2158); +} + +void func_8002C7BC(TargetContext* targetCtx, Player* player, Actor* actorArg, GlobalContext* globalCtx) { + s32 pad; + Actor* unkActor; + s32 actorCategory; + Vec3f sp50; + f32 sp4C; + f32 temp1; + f32 temp2; + f32 temp3; + f32 temp4; + f32 temp5; + f32 temp6; + s32 lockOnSfxId; + + unkActor = NULL; + + if ((player->unk_664 != NULL) && (player->unk_84B[player->unk_846] == 2)) { + targetCtx->unk_94 = NULL; + } else { + func_80032AF0(globalCtx, &globalCtx->actorCtx, &unkActor, player); + targetCtx->unk_94 = unkActor; + } + + if (targetCtx->unk_8C != NULL) { + unkActor = targetCtx->unk_8C; + targetCtx->unk_8C = NULL; + } else if (actorArg != NULL) { + unkActor = actorArg; + } + + if (unkActor != NULL) { + actorCategory = unkActor->category; + } else { + actorCategory = player->actor.category; + } + + if ((unkActor != targetCtx->arrowPointedActor) || (actorCategory != targetCtx->activeCategory)) { + targetCtx->arrowPointedActor = unkActor; + targetCtx->activeCategory = actorCategory; + targetCtx->unk_40 = 1.0f; + } + + if (unkActor == NULL) { + unkActor = &player->actor; + } + + if (Math_StepToF(&targetCtx->unk_40, 0.0f, 0.25f) == 0) { + temp1 = 0.25f / targetCtx->unk_40; + temp2 = unkActor->world.pos.x - targetCtx->naviRefPos.x; + temp3 = (unkActor->world.pos.y + (unkActor->targetArrowOffset * unkActor->scale.y)) - targetCtx->naviRefPos.y; + temp4 = unkActor->world.pos.z - targetCtx->naviRefPos.z; + targetCtx->naviRefPos.x += temp2 * temp1; + targetCtx->naviRefPos.y += temp3 * temp1; + targetCtx->naviRefPos.z += temp4 * temp1; + } else { + func_8002BF60(targetCtx, unkActor, actorCategory, globalCtx); + } + + if ((actorArg != NULL) && (targetCtx->unk_4B == 0)) { + func_8002BE04(globalCtx, &actorArg->focus.pos, &sp50, &sp4C); + if (((sp50.z <= 0.0f) || (1.0f <= fabsf(sp50.x * sp4C))) || (1.0f <= fabsf(sp50.y * sp4C))) { + actorArg = NULL; + } + } + + if (actorArg != NULL) { + if (actorArg != targetCtx->targetedActor) { + func_8002BE98(targetCtx, actorArg->category, globalCtx); + targetCtx->targetedActor = actorArg; + + if (actorArg->id == ACTOR_EN_BOOM) { + targetCtx->unk_48 = 0; + } + + lockOnSfxId = CHECK_FLAG_ALL(actorArg->flags, ACTOR_FLAG_0 | ACTOR_FLAG_2) ? NA_SE_SY_LOCK_ON + : NA_SE_SY_LOCK_ON_HUMAN; + func_80078884(lockOnSfxId); + } + + targetCtx->targetCenterPos.x = actorArg->world.pos.x; + targetCtx->targetCenterPos.y = actorArg->world.pos.y - (actorArg->shape.yOffset * actorArg->scale.y); + targetCtx->targetCenterPos.z = actorArg->world.pos.z; + + if (targetCtx->unk_4B == 0) { + temp5 = (500.0f - targetCtx->unk_44) * 3.0f; + temp6 = (temp5 < 30.0f) ? 30.0f : ((100.0f < temp5) ? 100.0f : temp5); + if (Math_StepToF(&targetCtx->unk_44, 80.0f, temp6) != 0) { + targetCtx->unk_4B++; + } + } else { + targetCtx->unk_4B = (targetCtx->unk_4B + 3) | 0x80; + targetCtx->unk_44 = 120.0f; + } + } else { + targetCtx->targetedActor = NULL; + Math_StepToF(&targetCtx->unk_44, 500.0f, 80.0f); + } +} + +/** + * Tests if current scene switch flag is set. + */ +s32 Flags_GetSwitch(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + return globalCtx->actorCtx.flags.swch & (1 << flag); + } else { + return globalCtx->actorCtx.flags.tempSwch & (1 << (flag - 0x20)); + } +} + +/** + * Sets current scene switch flag. + */ +void Flags_SetSwitch(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + globalCtx->actorCtx.flags.swch |= (1 << flag); + } else { + globalCtx->actorCtx.flags.tempSwch |= (1 << (flag - 0x20)); + } +} + +/** + * Unsets current scene switch flag. + */ +void Flags_UnsetSwitch(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + globalCtx->actorCtx.flags.swch &= ~(1 << flag); + } else { + globalCtx->actorCtx.flags.tempSwch &= ~(1 << (flag - 0x20)); + } +} + +/** + * Tests if current scene unknown flag is set. + */ +s32 Flags_GetUnknown(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + return globalCtx->actorCtx.flags.unk0 & (1 << flag); + } else { + return globalCtx->actorCtx.flags.unk1 & (1 << (flag - 0x20)); + } +} + +/** + * Sets current scene unknown flag. + */ +void Flags_SetUnknown(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + globalCtx->actorCtx.flags.unk0 |= (1 << flag); + } else { + globalCtx->actorCtx.flags.unk1 |= (1 << (flag - 0x20)); + } +} + +/** + * Unsets current scene unknown flag. + */ +void Flags_UnsetUnknown(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + globalCtx->actorCtx.flags.unk0 &= ~(1 << flag); + } else { + globalCtx->actorCtx.flags.unk1 &= ~(1 << (flag - 0x20)); + } +} + +/** + * Tests if current scene chest flag is set. + */ +s32 Flags_GetTreasure(GlobalContext* globalCtx, s32 flag) { + return globalCtx->actorCtx.flags.chest & (1 << flag); +} + +/** + * Sets current scene chest flag. + */ +void Flags_SetTreasure(GlobalContext* globalCtx, s32 flag) { + globalCtx->actorCtx.flags.chest |= (1 << flag); +} + +/** + * Tests if current scene clear flag is set. + */ +s32 Flags_GetClear(GlobalContext* globalCtx, s32 flag) { + return globalCtx->actorCtx.flags.clear & (1 << flag); +} + +/** + * Sets current scene clear flag. + */ +void Flags_SetClear(GlobalContext* globalCtx, s32 flag) { + globalCtx->actorCtx.flags.clear |= (1 << flag); +} + +/** + * Unsets current scene clear flag. + */ +void Flags_UnsetClear(GlobalContext* globalCtx, s32 flag) { + globalCtx->actorCtx.flags.clear &= ~(1 << flag); +} + +/** + * Tests if current scene temp clear flag is set. + */ +s32 Flags_GetTempClear(GlobalContext* globalCtx, s32 flag) { + return globalCtx->actorCtx.flags.tempClear & (1 << flag); +} + +/** + * Sets current scene temp clear flag. + */ +void Flags_SetTempClear(GlobalContext* globalCtx, s32 flag) { + globalCtx->actorCtx.flags.tempClear |= (1 << flag); +} + +/** + * Unsets current scene temp clear flag. + */ +void Flags_UnsetTempClear(GlobalContext* globalCtx, s32 flag) { + globalCtx->actorCtx.flags.tempClear &= ~(1 << flag); +} + +/** + * Tests if current scene collectible flag is set. + */ +s32 Flags_GetCollectible(GlobalContext* globalCtx, s32 flag) { + if (flag < 0x20) { + return globalCtx->actorCtx.flags.collect & (1 << flag); + } else { + return globalCtx->actorCtx.flags.tempCollect & (1 << (flag - 0x20)); + } +} + +/** + * Sets current scene collectible flag. + */ +void Flags_SetCollectible(GlobalContext* globalCtx, s32 flag) { + if (flag != 0) { + if (flag < 0x20) { + globalCtx->actorCtx.flags.collect |= (1 << flag); + } else { + globalCtx->actorCtx.flags.tempCollect |= (1 << (flag - 0x20)); + } + } +} + +void func_8002CDE4(GlobalContext* globalCtx, TitleCardContext* titleCtx) { + titleCtx->durationTimer = titleCtx->delayTimer = titleCtx->intensity = titleCtx->alpha = 0; +} + +void TitleCard_InitBossName(GlobalContext* globalCtx, TitleCardContext* titleCtx, void* texture, s16 x, s16 y, u8 width, + u8 height) { + + if (ResourceMgr_OTRSigCheck(texture)) + texture = ResourceMgr_LoadTexByName(texture); + + titleCtx->texture = texture; + titleCtx->x = x; + titleCtx->y = y; + titleCtx->width = width; + titleCtx->height = height; + titleCtx->durationTimer = 80; + titleCtx->delayTimer = 0; +} + +void TitleCard_InitPlaceName(GlobalContext* globalCtx, TitleCardContext* titleCtx, char* texture, s32 x, s32 y, + s32 width, s32 height, s32 delay) { + SceneTableEntry* loadedScene = globalCtx->loadedScene; + + // size_t size = loadedScene->titleFile.vromEnd - loadedScene->titleFile.vromStart; + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + texture = gDekuTreeTitleCardENGTex; + break; + case SCENE_DDAN: + texture = gDodongosCavernTitleCardENGTex; + break; + case SCENE_BDAN: + texture = gJabuJabuTitleCardENGTex; + break; + case SCENE_BMORI1: + texture = gForestTempleTitleCardENGTex; + break; + case SCENE_HIDAN: + texture = gFireTempleTitleCardENGTex; + break; + case SCENE_MIZUSIN: + texture = gWaterTempleTitleCardENGTex; + break; + case SCENE_JYASINZOU: + texture = gSpiritTempleTitleCardENGTex; + break; + case SCENE_HAKADAN: + texture = gSpiritTempleTitleCardENGTex; + break; + case SCENE_HAKADANCH: + texture = gBottomOfTheWellTitleCardENGTex; + break; + case SCENE_ICE_DOUKUTO: + texture = gIceCavernTitleCardENGTex; + break; + case SCENE_MEN: + texture = gGERudoTrainingGroundTitleCardENGTex; + break; + case SCENE_GERUDOWAY: + texture = gThievesHideoutTitleCardENGTex; + break; + case SCENE_GANONTIKA: + texture = gGanonsCastleTitleCardENGTex; + break; + case SCENE_TAKARAYA: + texture = gTreasureBoxShopTitleCardENGTex; + break; + case SCENE_MARKET_ALLEY: + case SCENE_MARKET_ALLEY_N: + texture = gBackAlleyTitleCardENGTex; + break; + case SCENE_MARKET_DAY: + case SCENE_MARKET_NIGHT: + case SCENE_MARKET_RUINS: + texture = gMarketTitleCardENGTex; + break; + case SCENE_SHOP1: + texture = gBazaarTitleCardENGTex; + break; + case SCENE_KOKIRI_SHOP: + texture = gKokiriShopTitleCardENGTex; + break; + case SCENE_GOLON: + texture = gGoronShopTitleCardENGTex; + break; + case SCENE_ZOORA: + texture = gZoraShopTitleCardENGTex; + break; + case SCENE_ALLEY_SHOP: + texture = gBombchuShopTitleCardENGTex; + break; + case SCENE_DRAG: + case SCENE_MAHOUYA: + texture = gPotionShopTitleCardENGTex; + break; + case SCENE_FACE_SHOP: + texture = gHappyMaskShopTitleCardENGTex; + break; + case SCENE_MALON_STABLE: + texture = gStableTitleCardENGTex; + break; + case SCENE_HYLIA_LABO: + texture = gLakeHyliaTitleCardENGTex; + break; + case SCENE_HUT: + texture = gGravekeepersHutTitleCardENGTex; + break; + case SCENE_DAIYOUSEI_IZUMI: + case SCENE_YOUSEI_IZUMI_YOKO: + texture = gGreatFairysFountainTitleCardENGTex; + break; + case SCENE_YOUSEI_IZUMI_TATE: + texture = gFairysFountainTitleCardENGTex; + break; + case SCENE_HAKAANA_OUKE: + texture = gRoyalFamilysTumbTitleCardENGTex; + break; + case SCENE_SYATEKIJYOU: + texture = gShootingGalleryTitleCardENGTex; + break; + case SCENE_TOKINOMA: + texture = gTempleOfTimeTitleCardENGTex; + break; + case SCENE_KENJYANOMA: + texture = gChamberOfTheSagesTitleCardENGTex; + break; + case SCENE_HAIRAL_NIWA: + case SCENE_HAIRAL_NIWA_N: + case SCENE_NAKANIWA: + case SCENE_HAIRAL_NIWA2: + texture = gCastleCourtyardTitleCardENGTex; + break; + case SCENE_HAKASITARELAY: + texture = gQuestionMarkTitleCardENGTex; + break; + case SCENE_TURIBORI: + texture = gFishingPondTitleCardENGTex; + break; + case SCENE_BOWLING: + texture = gBombchuBowlingAlleyCardENGTex; + break; + case SCENE_KINSUTA: + texture = gHouseOfSkulltulaTitleCardENGTex; + break; + case SCENE_SPOT00: + texture = gHyruleFieldTitleCardENGTex; + break; + case SCENE_SPOT01: + texture = gKakarikoVillageTitleCardENGTex; + break; + case SCENE_SPOT02: + texture = gGraveyardTitleCardENGTex; + break; + case SCENE_SPOT03: + texture = gZorasRiverTitleCardENGTex; + break; + case SCENE_SPOT04: + texture = gKokiriForestTitleCardENGTex; + break; + case SCENE_SPOT05: + texture = gSacredForestMeadowTitleCardENGTex; + break; + case SCENE_SPOT06: + texture = gLakeHyliaTitleCardENGTex; + break; + case SCENE_SPOT07: + texture = gZorasDomainTitleCardENGTex; + break; + case SCENE_SPOT08: + texture = gZorasFountainTitleCardENGTex; + break; + case SCENE_SPOT09: + texture = gGERudoValleyTitleCardENGTex; + break; + case SCENE_SPOT10: + texture = gLostWoodsTitleCardENGTex; + break; + case SCENE_SPOT11: + texture = gDesertColossusTitleCardENGTex; + break; + case SCENE_SPOT12: + texture = gGERudosFortressTitleCardENGTex; + break; + case SCENE_SPOT13: + texture = gHauntedWastelandTitleCardENGTex; + break; + case SCENE_SPOT15: + texture = gHyruleCastleTitleCardENGTex; + break; + case SCENE_SPOT16: + texture = gDeathMountainTrailTitleCardENGTex; + break; + case SCENE_SPOT17: + texture = gDeathMountainCraterTitleCardENGTex; + break; + case SCENE_SPOT18: + texture = gGoronCityTitleCardENGTex; + break; + case SCENE_SPOT20: + texture = gLonLonRanchTitleCardENGTex; + break; + default: + titleCtx->texture = NULL; + return; + + } + + char newName[512]; + + if (gSaveContext.language != LANGUAGE_ENG) { + size_t length = strlen(texture); + strcpy(newName, texture); + if (gSaveContext.language == LANGUAGE_FRA) { + newName[length - 6] = 'F'; + newName[length - 5] = 'R'; + newName[length - 4] = 'A'; + } + else if (gSaveContext.language == LANGUAGE_GER) { + newName[length - 6] = 'G'; + newName[length - 5] = 'E'; + newName[length - 4] = 'R'; + } + texture = newName; + } + + titleCtx->texture = ResourceMgr_LoadTexByName(texture); + + //titleCtx->texture = texture; + titleCtx->x = x; + titleCtx->y = y; + titleCtx->width = width; + titleCtx->height = height; + titleCtx->durationTimer = 80; + titleCtx->delayTimer = delay; +} + +void TitleCard_Update(GlobalContext* globalCtx, TitleCardContext* titleCtx) { + if (DECR(titleCtx->delayTimer) == 0) { + if (DECR(titleCtx->durationTimer) == 0) { + Math_StepToS(&titleCtx->alpha, 0, 30); + Math_StepToS(&titleCtx->intensity, 0, 70); + } else { + Math_StepToS(&titleCtx->alpha, 255, 10); + Math_StepToS(&titleCtx->intensity, 255, 20); + } + } +} + +void TitleCard_Draw(GlobalContext* globalCtx, TitleCardContext* titleCtx) { + s32 width; + s32 height; + s32 unused; + s32 titleX; + s32 doubleWidth; + s32 titleY; + s32 titleSecondY; + s32 textureLanguageOffset; + + if (titleCtx->alpha != 0) { + width = titleCtx->width; + height = titleCtx->height; + titleX = (titleCtx->x * 4) - (width * 2); + titleY = (titleCtx->y * 4) - (height * 2); + doubleWidth = width * 2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 2824); + + textureLanguageOffset = width * height * gSaveContext.language; + height = (width * height > 0x1000) ? 0x1000 / width : height; + titleSecondY = titleY + (height * 4); + + OVERLAY_DISP = func_80093808(OVERLAY_DISP); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, (u8)titleCtx->intensity, (u8)titleCtx->intensity, (u8)titleCtx->intensity, + (u8)titleCtx->alpha); + + gDPLoadTextureBlock(OVERLAY_DISP++, (uintptr_t)titleCtx->texture + textureLanguageOffset, G_IM_FMT_IA, + G_IM_SIZ_8b, + width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(OVERLAY_DISP++, titleX, titleY, ((doubleWidth * 2) + titleX) - 4, titleY + (height * 4) - 1, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + height = titleCtx->height - height; + + // If texture is bigger than 0x1000, display the rest + if (height > 0) { + gDPLoadTextureBlock(OVERLAY_DISP++, (uintptr_t)titleCtx->texture + textureLanguageOffset + 0x1000, + G_IM_FMT_IA, + G_IM_SIZ_8b, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(OVERLAY_DISP++, titleX, titleSecondY, ((doubleWidth * 2) + titleX) - 4, + titleSecondY + (height * 4) - 1, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 2880); + } +} + +s32 func_8002D53C(GlobalContext* globalCtx, TitleCardContext* titleCtx) { + if ((globalCtx->actorCtx.titleCtx.delayTimer != 0) || (globalCtx->actorCtx.titleCtx.alpha != 0)) { + titleCtx->durationTimer = 0; + titleCtx->delayTimer = 0; + return false; + } + + return true; +} + +void Actor_Kill(Actor* actor) { + actor->draw = NULL; + actor->update = NULL; + actor->flags &= ~ACTOR_FLAG_0; +} + +void Actor_SetWorldToHome(Actor* actor) { + actor->world = actor->home; +} + +void Actor_SetFocus(Actor* actor, f32 yOffset) { + actor->focus.pos.x = actor->world.pos.x; + actor->focus.pos.y = actor->world.pos.y + yOffset; + actor->focus.pos.z = actor->world.pos.z; + + actor->focus.rot.x = actor->world.rot.x; + actor->focus.rot.y = actor->world.rot.y; + actor->focus.rot.z = actor->world.rot.z; +} + +void Actor_SetWorldRotToShape(Actor* actor) { + actor->world.rot = actor->shape.rot; +} + +void Actor_SetShapeRotToWorld(Actor* actor) { + actor->shape.rot = actor->world.rot; +} + +void Actor_SetScale(Actor* actor, f32 scale) { + actor->scale.z = scale; + actor->scale.y = scale; + actor->scale.x = scale; +} + +void Actor_SetObjectDependency(GlobalContext* globalCtx, Actor* actor) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[actor->objBankIndex].segment); +} + +void Actor_Init(Actor* actor, GlobalContext* globalCtx) { + Actor_SetWorldToHome(actor); + Actor_SetShapeRotToWorld(actor); + Actor_SetFocus(actor, 0.0f); + Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); + Actor_SetScale(actor, 0.01f); + actor->targetMode = 3; + actor->minVelocityY = -20.0f; + actor->xyzDistToPlayerSq = FLT_MAX; + actor->naviEnemyId = 0xFF; + actor->uncullZoneForward = 1000.0f; + actor->uncullZoneScale = 350.0f; + actor->uncullZoneDownward = 700.0f; + if (CVar_GetS32("gDisableDrawDistance", 0) != 0) { + actor->uncullZoneForward = 32767.0f; + actor->uncullZoneScale = 32767.0f; + actor->uncullZoneDownward = 32767.0f; + } + CollisionCheck_InitInfo(&actor->colChkInfo); + actor->floorBgId = BGCHECK_SCENE; + ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f); + //if (Object_IsLoaded(&globalCtx->objectCtx, actor->objBankIndex)) + { + //Actor_SetObjectDependency(globalCtx, actor); + actor->init(actor, globalCtx); + actor->init = NULL; + } +} + +void Actor_Destroy(Actor* actor, GlobalContext* globalCtx) { + ActorOverlay* overlayEntry; + char* name; + + if (actor->destroy != NULL) { + actor->destroy(actor, globalCtx); + actor->destroy = NULL; + } else { + overlayEntry = actor->overlayEntry; + name = overlayEntry->name != NULL ? overlayEntry->name : ""; + + // "No Actor class destruct [%s]" + osSyncPrintf("Actorクラス デストラクトがありません [%s]\n" VT_RST, name); + } +} + +void func_8002D7EC(Actor* actor) { + f32 speedRate = R_UPDATE_RATE * 0.5f; + + actor->world.pos.x += (actor->velocity.x * speedRate) + actor->colChkInfo.displacement.x; + actor->world.pos.y += (actor->velocity.y * speedRate) + actor->colChkInfo.displacement.y; + actor->world.pos.z += (actor->velocity.z * speedRate) + actor->colChkInfo.displacement.z; +} + +void func_8002D868(Actor* actor) { + actor->velocity.x = Math_SinS(actor->world.rot.y) * actor->speedXZ; + actor->velocity.z = Math_CosS(actor->world.rot.y) * actor->speedXZ; + + actor->velocity.y += actor->gravity; + if (actor->velocity.y < actor->minVelocityY) { + actor->velocity.y = actor->minVelocityY; + } +} + +void Actor_MoveForward(Actor* actor) { + func_8002D868(actor); + func_8002D7EC(actor); +} + +void func_8002D908(Actor* actor) { + f32 sp24 = Math_CosS(actor->world.rot.x) * actor->speedXZ; + + actor->velocity.x = Math_SinS(actor->world.rot.y) * sp24; + actor->velocity.y = Math_SinS(actor->world.rot.x) * actor->speedXZ; + actor->velocity.z = Math_CosS(actor->world.rot.y) * sp24; +} + +void func_8002D97C(Actor* actor) { + func_8002D908(actor); + func_8002D7EC(actor); +} + +void func_8002D9A4(Actor* actor, f32 arg1) { + actor->speedXZ = Math_CosS(actor->world.rot.x) * arg1; + actor->velocity.y = -Math_SinS(actor->world.rot.x) * arg1; +} + +void func_8002D9F8(Actor* actor, SkelAnime* skelAnime) { + Vec3f sp1C; + + SkelAnime_UpdateTranslation(skelAnime, &sp1C, actor->shape.rot.y); + actor->world.pos.x += sp1C.x * actor->scale.x; + actor->world.pos.y += sp1C.y * actor->scale.y; + actor->world.pos.z += sp1C.z * actor->scale.z; +} + +s16 Actor_WorldYawTowardActor(Actor* actorA, Actor* actorB) { + return Math_Vec3f_Yaw(&actorA->world.pos, &actorB->world.pos); +} + +s16 Actor_FocusYawTowardActor(Actor* actorA, Actor* actorB) { + return Math_Vec3f_Yaw(&actorA->focus.pos, &actorB->focus.pos); +} + +s16 Actor_WorldYawTowardPoint(Actor* actor, Vec3f* refPoint) { + return Math_Vec3f_Yaw(&actor->world.pos, refPoint); +} + +s16 Actor_WorldPitchTowardActor(Actor* actorA, Actor* actorB) { + return Math_Vec3f_Pitch(&actorA->world.pos, &actorB->world.pos); +} + +s16 Actor_FocusPitchTowardActor(Actor* actorA, Actor* actorB) { + return Math_Vec3f_Pitch(&actorA->focus.pos, &actorB->focus.pos); +} + +s16 Actor_WorldPitchTowardPoint(Actor* actor, Vec3f* refPoint) { + return Math_Vec3f_Pitch(&actor->world.pos, refPoint); +} + +f32 Actor_WorldDistXYZToActor(Actor* actorA, Actor* actorB) { + return Math_Vec3f_DistXYZ(&actorA->world.pos, &actorB->world.pos); +} + +f32 Actor_WorldDistXYZToPoint(Actor* actor, Vec3f* refPoint) { + return Math_Vec3f_DistXYZ(&actor->world.pos, refPoint); +} + +f32 Actor_WorldDistXZToActor(Actor* actorA, Actor* actorB) { + return Math_Vec3f_DistXZ(&actorA->world.pos, &actorB->world.pos); +} + +f32 Actor_WorldDistXZToPoint(Actor* actor, Vec3f* refPoint) { + return Math_Vec3f_DistXZ(&actor->world.pos, refPoint); +} + +void func_8002DBD0(Actor* actor, Vec3f* result, Vec3f* arg2) { + f32 cosRot2Y; + f32 sinRot2Y; + f32 deltaX; + f32 deltaZ; + + cosRot2Y = Math_CosS(actor->shape.rot.y); + sinRot2Y = Math_SinS(actor->shape.rot.y); + deltaX = arg2->x - actor->world.pos.x; + deltaZ = arg2->z - actor->world.pos.z; + + result->x = (deltaX * cosRot2Y) - (deltaZ * sinRot2Y); + result->z = (deltaX * sinRot2Y) + (deltaZ * cosRot2Y); + result->y = arg2->y - actor->world.pos.y; +} + +f32 Actor_HeightDiff(Actor* actorA, Actor* actorB) { + return actorB->world.pos.y - actorA->world.pos.y; +} + +f32 Player_GetHeight(Player* player) { + f32 offset = (player->stateFlags1 & 0x800000) ? 32.0f : 0.0f; + + if (LINK_IS_ADULT) { + return offset + 68.0f; + } else { + return offset + 44.0f; + } +} + +f32 func_8002DCE4(Player* player) { + if (player->stateFlags1 & 0x800000) { + return 8.0f; + } else if (player->stateFlags1 & 0x8000000) { + return (R_RUN_SPEED_LIMIT / 100.0f) * 0.6f; + } else { + return R_RUN_SPEED_LIMIT / 100.0f; + } +} + +s32 func_8002DD6C(Player* player) { + return player->stateFlags1 & 0x8; +} + +s32 func_8002DD78(Player* player) { + return func_8002DD6C(player) && player->unk_834; +} + +s32 func_8002DDA8(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + return (player->stateFlags1 & 0x800) || func_8002DD78(player); +} + +s32 func_8002DDE4(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + return player->stateFlags2 & 0x8; +} + +s32 func_8002DDF4(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + return player->stateFlags2 & 0x1000; +} + +void func_8002DE04(GlobalContext* globalCtx, Actor* actorA, Actor* actorB) { + ArmsHook* hookshot = (ArmsHook*)Actor_Find(&globalCtx->actorCtx, ACTOR_ARMS_HOOK, ACTORCAT_ITEMACTION); + + hookshot->grabbed = actorB; + hookshot->grabbedDistDiff.x = 0.0f; + hookshot->grabbedDistDiff.y = 0.0f; + hookshot->grabbedDistDiff.z = 0.0f; + actorB->flags |= ACTOR_FLAG_13; + actorA->flags &= ~ACTOR_FLAG_13; +} + +void func_8002DE74(GlobalContext* globalCtx, Player* player) { + if ((globalCtx->roomCtx.curRoom.unk_03 != 4) && func_800C0CB8(globalCtx)) { + Camera_ChangeSetting(Gameplay_GetCamera(globalCtx, MAIN_CAM), CAM_SET_HORSE); + } +} + +void Actor_MountHorse(GlobalContext* globalCtx, Player* player, Actor* horse) { + player->rideActor = horse; + player->stateFlags1 |= 0x800000; + horse->child = &player->actor; +} + +s32 func_8002DEEC(Player* player) { + return (player->stateFlags1 & 0x20000080) || (player->csMode != 0); +} + +void func_8002DF18(GlobalContext* globalCtx, Player* player) { + func_8006DC68(globalCtx, player); +} + +s32 func_8002DF38(GlobalContext* globalCtx, Actor* actor, u8 csMode) { + Player* player = GET_PLAYER(globalCtx); + + player->csMode = csMode; + player->unk_448 = actor; + player->unk_46A = 0; + + return true; +} + +s32 func_8002DF54(GlobalContext* globalCtx, Actor* actor, u8 csMode) { + Player* player = GET_PLAYER(globalCtx); + + func_8002DF38(globalCtx, actor, csMode); + player->unk_46A = 1; + + return true; +} + +void func_8002DF90(DynaPolyActor* dynaActor) { + dynaActor->unk_154 = 0.0f; + dynaActor->unk_150 = 0.0f; +} + +void func_8002DFA4(DynaPolyActor* dynaActor, f32 arg1, s16 arg2) { + dynaActor->unk_150 += arg1; + dynaActor->unk_158 = arg2; +} + +/** + * Chcek if the player is facing the specified actor. + * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. + */ +s32 Player_IsFacingActor(Actor* actor, s16 maxAngle, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff = (s16)(actor->yawTowardsPlayer + 0x8000) - player->actor.shape.rot.y; + + if (ABS(yawDiff) < maxAngle) { + return true; + } + + return false; +} + +/** + * Chcek if `actorB` is facing `actorA`. + * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. + * + * This function is unused in the original game. + */ +s32 Actor_ActorBIsFacingActorA(Actor* actorA, Actor* actorB, s16 maxAngle) { + s16 yawDiff = (s16)(Actor_WorldYawTowardActor(actorA, actorB) + 0x8000) - actorB->shape.rot.y; + + if (ABS(yawDiff) < maxAngle) { + return true; + } + + return false; +} + +/** + * Chcek if the specified actor is facing the player. + * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. + */ +s32 Actor_IsFacingPlayer(Actor* actor, s16 maxAngle) { + s16 yawDiff = actor->yawTowardsPlayer - actor->shape.rot.y; + + if (ABS(yawDiff) < maxAngle) { + return true; + } + + return false; +} + +/** + * Chcek if `actorA` is facing `actorB`. + * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. + * + * This function is unused in the original game. + */ +s32 Actor_ActorAIsFacingActorB(Actor* actorA, Actor* actorB, s16 maxAngle) { + s16 yawDiff = Actor_WorldYawTowardActor(actorA, actorB) - actorA->shape.rot.y; + + if (ABS(yawDiff) < maxAngle) { + return true; + } + + return false; +} + +/** + * Chcek if the specified actor is facing the player and is nearby. + * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. + * The minimum distance that qualifies as "nearby" is specified by `range`. + */ +s32 Actor_IsFacingAndNearPlayer(Actor* actor, f32 range, s16 maxAngle) { + s16 yawDiff = actor->yawTowardsPlayer - actor->shape.rot.y; + + if (ABS(yawDiff) < maxAngle) { + f32 xyzDistanceFromLink = sqrtf(SQ(actor->xzDistToPlayer) + SQ(actor->yDistToPlayer)); + + if (xyzDistanceFromLink < range) { + return true; + } + } + + return false; +} + +/** + * Chcek if `actorA` is facing `actorB` and is nearby. + * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. + * The minimum distance that qualifies as "nearby" is specified by `range`. + */ +s32 Actor_ActorAIsFacingAndNearActorB(Actor* actorA, Actor* actorB, f32 range, s16 maxAngle) { + if (Actor_WorldDistXYZToActor(actorA, actorB) < range) { + s16 yawDiff = Actor_WorldYawTowardActor(actorA, actorB) - actorA->shape.rot.y; + + if (ABS(yawDiff) < maxAngle) { + return true; + } + } + + return false; +} + +s32 func_8002E234(Actor* actor, f32 arg1, s32 arg2) { + if ((actor->bgCheckFlags & 0x1) && (arg1 < -11.0f)) { + actor->bgCheckFlags &= ~0x1; + actor->bgCheckFlags |= 0x4; + + if ((actor->velocity.y < 0.0f) && (arg2 & 0x10)) { + actor->velocity.y = 0.0f; + } + + return false; + } + + return true; +} + +s32 func_8002E2AC(GlobalContext* globalCtx, Actor* actor, Vec3f* arg2, s32 arg3) { + f32 floorHeightDiff; + s32 floorBgId; + + arg2->y += 50.0f; + + actor->floorHeight = + BgCheck_EntityRaycastFloor5(globalCtx, &globalCtx->colCtx, &actor->floorPoly, &floorBgId, actor, arg2); + actor->bgCheckFlags &= ~0x0086; + + if (actor->floorHeight <= BGCHECK_Y_MIN) { + return func_8002E234(actor, BGCHECK_Y_MIN, arg3); + } + + floorHeightDiff = actor->floorHeight - actor->world.pos.y; + actor->floorBgId = floorBgId; + + if (floorHeightDiff >= 0.0f) { // actor is on or below the ground + actor->bgCheckFlags |= 0x80; + + if (actor->bgCheckFlags & 0x10) { + if (floorBgId != sCurCeilingBgId) { + if (floorHeightDiff > 15.0f) { + actor->bgCheckFlags |= 0x100; + } + } else { + actor->world.pos.x = actor->prevPos.x; + actor->world.pos.z = actor->prevPos.z; + } + } + + actor->world.pos.y = actor->floorHeight; + + if (actor->velocity.y <= 0.0f) { + if (!(actor->bgCheckFlags & 0x1)) { + actor->bgCheckFlags |= 0x2; + } else if ((arg3 & 0x8) && (actor->gravity < 0.0f)) { + actor->velocity.y = -4.0f; + } else { + actor->velocity.y = 0.0f; + } + + actor->bgCheckFlags |= 0x1; + func_80043334(&globalCtx->colCtx, actor, actor->floorBgId); + } + } else { // actor is above ground + if ((actor->bgCheckFlags & 0x1) && (floorHeightDiff >= -11.0f)) { + func_80043334(&globalCtx->colCtx, actor, actor->floorBgId); + } + + return func_8002E234(actor, floorHeightDiff, arg3); + } + + return true; +} + +void Actor_UpdateBgCheckInfo(GlobalContext* globalCtx, Actor* actor, f32 wallCheckHeight, f32 wallCheckRadius, + f32 ceilingCheckHeight, s32 flags) { + f32 sp74; + s32 pad; + Vec3f sp64; + s32 bgId; + CollisionPoly* wallPoly; + f32 sp58; + WaterBox* waterBox; + f32 waterBoxYSurface; + Vec3f ripplePos; + + sp74 = actor->world.pos.y - actor->prevPos.y; + + if ((actor->floorBgId != BGCHECK_SCENE) && (actor->bgCheckFlags & 1)) { + func_800433A4(&globalCtx->colCtx, actor->floorBgId, actor); + } + + if (flags & 1) { + if ((!(flags & 0x80) && + BgCheck_EntitySphVsWall3(&globalCtx->colCtx, &sp64, &actor->world.pos, &actor->prevPos, wallCheckRadius, + &actor->wallPoly, &bgId, actor, wallCheckHeight)) || + ((flags & 0x80) && + BgCheck_EntitySphVsWall4(&globalCtx->colCtx, &sp64, &actor->world.pos, &actor->prevPos, wallCheckRadius, + &actor->wallPoly, &bgId, actor, wallCheckHeight))) { + wallPoly = actor->wallPoly; + Math_Vec3f_Copy(&actor->world.pos, &sp64); + actor->wallYaw = Math_Atan2S(wallPoly->normal.z, wallPoly->normal.x); + actor->bgCheckFlags |= 8; + actor->wallBgId = bgId; + } else { + actor->bgCheckFlags &= ~8; + } + } + + sp64.x = actor->world.pos.x; + sp64.z = actor->world.pos.z; + + if (flags & 2) { + sp64.y = actor->prevPos.y + 10.0f; + if (BgCheck_EntityCheckCeiling(&globalCtx->colCtx, &sp58, &sp64, (ceilingCheckHeight + sp74) - 10.0f, + &sCurCeilingPoly, &sCurCeilingBgId, actor)) { + actor->bgCheckFlags |= 0x10; + actor->world.pos.y = (sp58 + sp74) - 10.0f; + } else { + actor->bgCheckFlags &= ~0x10; + } + } + + if (flags & 4) { + sp64.y = actor->prevPos.y; + func_8002E2AC(globalCtx, actor, &sp64, flags); + waterBoxYSurface = actor->world.pos.y; + if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, actor->world.pos.x, actor->world.pos.z, + &waterBoxYSurface, &waterBox)) { + actor->yDistToWater = waterBoxYSurface - actor->world.pos.y; + if (actor->yDistToWater < 0.0f) { + actor->bgCheckFlags &= ~0x60; + } else { + if (!(actor->bgCheckFlags & 0x20)) { + actor->bgCheckFlags |= 0x40; + if (!(flags & 0x40)) { + ripplePos.x = actor->world.pos.x; + ripplePos.y = waterBoxYSurface; + ripplePos.z = actor->world.pos.z; + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 100, 500, 0); + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 100, 500, 4); + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 100, 500, 8); + } + } + actor->bgCheckFlags |= 0x20; + } + } else { + actor->bgCheckFlags &= ~0x60; + actor->yDistToWater = BGCHECK_Y_MIN; + } + } +} + +Mtx D_8015BBA8; + +Gfx* func_8002E830(Vec3f* object, Vec3f* eye, Vec3f* lightDir, GraphicsContext* gfxCtx, Gfx* gfx, Hilite** hilite) { + LookAt* lookAt; + f32 correctedEyeX; + + lookAt = Graph_Alloc(gfxCtx, sizeof(LookAt)); + + correctedEyeX = (eye->x == object->x) && (eye->z == object->z) ? eye->x + 0.001f : eye->x; + + *hilite = Graph_Alloc(gfxCtx, sizeof(Hilite)); + + if (HREG(80) == 6) { + osSyncPrintf("z_actor.c 3529 eye=[%f(%f) %f %f] object=[%f %f %f] light_direction=[%f %f %f]\n", correctedEyeX, + eye->x, eye->y, eye->z, object->x, object->y, object->z, lightDir->x, lightDir->y, lightDir->z); + } + + func_800ABE74(correctedEyeX, eye->y, eye->z); + guLookAtHilite(&D_8015BBA8, lookAt, *hilite, correctedEyeX, eye->y, eye->z, object->x, object->y, object->z, 0.0f, + 1.0f, 0.0f, lightDir->x, lightDir->y, lightDir->z, lightDir->x, lightDir->y, lightDir->z, 0x10, + 0x10); + + gSPLookAt(gfx++, lookAt); + gDPSetHilite1Tile(gfx++, 1, *hilite, 0x10, 0x10); + + return gfx; +} + +Hilite* func_8002EABC(Vec3f* object, Vec3f* eye, Vec3f* lightDir, GraphicsContext* gfxCtx) { + Hilite* hilite; + + OPEN_DISPS(gfxCtx, "../z_actor.c", 4306); + + POLY_OPA_DISP = func_8002E830(object, eye, lightDir, gfxCtx, POLY_OPA_DISP, &hilite); + + CLOSE_DISPS(gfxCtx, "../z_actor.c", 4313); + + return hilite; +} + +Hilite* func_8002EB44(Vec3f* object, Vec3f* eye, Vec3f* lightDir, GraphicsContext* gfxCtx) { + Hilite* hilite; + + OPEN_DISPS(gfxCtx, "../z_actor.c", 4332); + + POLY_XLU_DISP = func_8002E830(object, eye, lightDir, gfxCtx, POLY_XLU_DISP, &hilite); + + CLOSE_DISPS(gfxCtx, "../z_actor.c", 4339); + + return hilite; +} + +void func_8002EBCC(Actor* actor, GlobalContext* globalCtx, s32 flag) { + Hilite* hilite; + Vec3f lightDir; + Gfx* displayListHead; + Gfx* displayList; + + lightDir.x = globalCtx->envCtx.dirLight1.params.dir.x; + lightDir.y = globalCtx->envCtx.dirLight1.params.dir.y; + lightDir.z = globalCtx->envCtx.dirLight1.params.dir.z; + + if (HREG(80) == 6) { + osSyncPrintf("z_actor.c 3637 game_play->view.eye=[%f(%f) %f %f]\n", globalCtx->view.eye.x, + globalCtx->view.eye.y, globalCtx->view.eye.z); + } + + hilite = func_8002EABC(&actor->world.pos, &globalCtx->view.eye, &lightDir, globalCtx->state.gfxCtx); + + if (flag != 0) { + displayList = Graph_Alloc(globalCtx->state.gfxCtx, 2 * sizeof(Gfx)); + displayListHead = displayList; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 4384); + + gDPSetHilite1Tile(displayListHead++, 1, hilite, 0x10, 0x10); + gSPEndDisplayList(displayListHead); + gSPSegment(POLY_OPA_DISP++, 0x07, displayList); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 4394); + } +} + +void func_8002ED80(Actor* actor, GlobalContext* globalCtx, s32 flag) { + Hilite* hilite; + Vec3f lightDir; + Gfx* displayListHead; + Gfx* displayList; + + lightDir.x = globalCtx->envCtx.dirLight1.params.dir.x; + lightDir.y = globalCtx->envCtx.dirLight1.params.dir.y; + lightDir.z = globalCtx->envCtx.dirLight1.params.dir.z; + + hilite = func_8002EB44(&actor->world.pos, &globalCtx->view.eye, &lightDir, globalCtx->state.gfxCtx); + + if (flag != 0) { + displayList = Graph_Alloc(globalCtx->state.gfxCtx, 2 * sizeof(Gfx)); + displayListHead = displayList; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 4429); + + gDPSetHilite1Tile(displayListHead++, 1, hilite, 0x10, 0x10); + gSPEndDisplayList(displayListHead); + gSPSegment(POLY_XLU_DISP++, 0x07, displayList); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 4439); + } +} + +PosRot* Actor_GetFocus(PosRot* dest, Actor* actor) { + *dest = actor->focus; + + return dest; +} + +PosRot* Actor_GetWorld(PosRot* dest, Actor* actor) { + *dest = actor->world; + + return dest; +} + +PosRot* Actor_GetWorldPosShapeRot(PosRot* arg0, Actor* actor) { + PosRot sp1C; + + Math_Vec3f_Copy(&sp1C.pos, &actor->world.pos); + sp1C.rot = actor->shape.rot; + *arg0 = sp1C; + + return arg0; +} + +f32 func_8002EFC0(Actor* actor, Player* player, s16 arg2) { + s16 yawTemp = (s16)(actor->yawTowardsPlayer - 0x8000) - arg2; + s16 yawTempAbs = ABS(yawTemp); + + if (player->unk_664 != NULL) { + if ((yawTempAbs > 0x4000) || (actor->flags & ACTOR_FLAG_27)) { + return FLT_MAX; + } else { + f32 ret = + actor->xyzDistToPlayerSq - actor->xyzDistToPlayerSq * 0.8f * ((0x4000 - yawTempAbs) * (1.0f / 0x8000)); + + return ret; + } + } + + if (yawTempAbs > 0x2AAA) { + return FLT_MAX; + } + + return actor->xyzDistToPlayerSq; +} + +typedef struct { + /* 0x0 */ f32 rangeSq; + /* 0x4 */ f32 leashScale; +} TargetRangeParams; // size = 0x8 + +#define TARGET_RANGE(range, leash) \ + { SQ(range), (f32)range / leash } + +TargetRangeParams D_80115FF8[] = { + TARGET_RANGE(70, 140), TARGET_RANGE(170, 255), TARGET_RANGE(280, 5600), TARGET_RANGE(350, 525), + TARGET_RANGE(700, 1050), TARGET_RANGE(1000, 1500), TARGET_RANGE(100, 105.36842), TARGET_RANGE(140, 163.33333), + TARGET_RANGE(240, 576), TARGET_RANGE(280, 280000), +}; + +u32 func_8002F090(Actor* actor, f32 arg1) { + return arg1 < D_80115FF8[actor->targetMode].rangeSq; +} + +s32 func_8002F0C8(Actor* actor, Player* player, s32 flag) { + if ((actor->update == NULL) || !(actor->flags & ACTOR_FLAG_0)) { + return true; + } + + if (!flag) { + s16 var = (s16)(actor->yawTowardsPlayer - 0x8000) - player->actor.shape.rot.y; + s16 abs_var = ABS(var); + f32 dist; + + if ((player->unk_664 == NULL) && (abs_var > 0x2AAA)) { + dist = FLT_MAX; + } else { + dist = actor->xyzDistToPlayerSq; + } + + return !func_8002F090(actor, D_80115FF8[actor->targetMode].leashScale * dist); + } + + return false; +} + +u32 Actor_ProcessTalkRequest(Actor* actor, GlobalContext* globalCtx) { + if (actor->flags & ACTOR_FLAG_8) { + actor->flags &= ~ACTOR_FLAG_8; + return true; + } + + return false; +} + +s32 func_8002F1C4(Actor* actor, GlobalContext* globalCtx, f32 arg2, f32 arg3, u32 exchangeItemId) { + Player* player = GET_PLAYER(globalCtx); + + // This is convoluted but it seems like it must be a single if statement to match + if ((player->actor.flags & ACTOR_FLAG_8) || ((exchangeItemId != EXCH_ITEM_NONE) && Player_InCsMode(globalCtx)) || + (!actor->isTargeted && + ((arg3 < fabsf(actor->yDistToPlayer)) || (player->targetActorDistance < actor->xzDistToPlayer) || + (arg2 < actor->xzDistToPlayer)))) { + return false; + } + + player->targetActor = actor; + player->targetActorDistance = actor->xzDistToPlayer; + player->exchangeItemId = exchangeItemId; + + return true; +} + +s32 func_8002F298(Actor* actor, GlobalContext* globalCtx, f32 arg2, u32 exchangeItemId) { + return func_8002F1C4(actor, globalCtx, arg2, arg2, exchangeItemId); +} + +s32 func_8002F2CC(Actor* actor, GlobalContext* globalCtx, f32 arg2) { + return func_8002F298(actor, globalCtx, arg2, EXCH_ITEM_NONE); +} + +s32 func_8002F2F4(Actor* actor, GlobalContext* globalCtx) { + f32 var1 = 50.0f + actor->colChkInfo.cylRadius; + + return func_8002F2CC(actor, globalCtx, var1); +} + +u32 Actor_TextboxIsClosing(Actor* actor, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + return true; + } else { + return false; + } +} + +s8 func_8002F368(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + return player->exchangeItemId; +} + +void Actor_GetScreenPos(GlobalContext* globalCtx, Actor* actor, s16* x, s16* y) { + Vec3f projectedPos; + f32 w; + + func_8002BE04(globalCtx, &actor->focus.pos, &projectedPos, &w); + *x = projectedPos.x * w * (SCREEN_WIDTH / 2) + (SCREEN_WIDTH / 2); + *y = projectedPos.y * w * -(SCREEN_HEIGHT / 2) + (SCREEN_HEIGHT / 2); +} + +u32 Actor_HasParent(Actor* actor, GlobalContext* globalCtx) { + if (actor->parent != NULL) { + return true; + } else { + return false; + } +} + +s32 func_8002F434(Actor* actor, GlobalContext* globalCtx, s32 getItemId, f32 xzRange, f32 yRange) { + Player* player = GET_PLAYER(globalCtx); + + if (!(player->stateFlags1 & 0x3C7080) && Player_GetExplosiveHeld(player) < 0) { + if ((((player->heldActor != NULL) || (actor == player->targetActor)) && (getItemId > GI_NONE) && + (getItemId < GI_MAX)) || + (!(player->stateFlags1 & 0x20000800))) { + if ((actor->xzDistToPlayer < xzRange) && (fabsf(actor->yDistToPlayer) < yRange)) { + s16 yawDiff = actor->yawTowardsPlayer - player->actor.shape.rot.y; + s32 absYawDiff = ABS(yawDiff); + + if ((getItemId != GI_NONE) || (player->getItemDirection < absYawDiff)) { + player->getItemId = getItemId; + player->interactRangeActor = actor; + player->getItemDirection = absYawDiff; + return true; + } + } + } + } + + return false; +} + +void func_8002F554(Actor* actor, GlobalContext* globalCtx, s32 getItemId) { + func_8002F434(actor, globalCtx, getItemId, 50.0f, 10.0f); +} + +void func_8002F580(Actor* actor, GlobalContext* globalCtx) { + func_8002F554(actor, globalCtx, GI_NONE); +} + +u32 Actor_HasNoParent(Actor* actor, GlobalContext* globalCtx) { + if (actor->parent == NULL) { + return true; + } else { + return false; + } +} + +void func_8002F5C4(Actor* actorA, Actor* actorB, GlobalContext* globalCtx) { + Actor* parent = actorA->parent; + + if (parent->id == ACTOR_PLAYER) { + Player* player = (Player*)parent; + + player->heldActor = actorB; + player->interactRangeActor = actorB; + } + + parent->child = actorB; + actorB->parent = parent; + actorA->parent = NULL; +} + +void func_8002F5F0(Actor* actor, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (actor->xyzDistToPlayerSq < player->unk_6A4) { + player->unk_6A4 = actor->xyzDistToPlayerSq; + } +} + +s32 Actor_IsMounted(GlobalContext* globalCtx, Actor* horse) { + if (horse->child != NULL) { + return true; + } else { + return false; + } +} + +u32 Actor_SetRideActor(GlobalContext* globalCtx, Actor* horse, s32 mountSide) { + Player* player = GET_PLAYER(globalCtx); + + if (!(player->stateFlags1 & 0x003C7880)) { + player->rideActor = horse; + player->mountSide = mountSide; + return true; + } + + return false; +} + +s32 Actor_NotMounted(GlobalContext* globalCtx, Actor* horse) { + if (horse->child == NULL) { + return true; + } else { + return false; + } +} + +void func_8002F698(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6) { + Player* player = GET_PLAYER(globalCtx); + + player->unk_8A0 = arg6; + player->unk_8A1 = arg5; + player->unk_8A2 = arg3; + player->unk_8A4 = arg2; + player->unk_8A8 = arg4; +} + +void func_8002F6D4(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5) { + func_8002F698(globalCtx, actor, arg2, arg3, arg4, 2, arg5); +} + +void func_8002F71C(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4) { + func_8002F6D4(globalCtx, actor, arg2, arg3, arg4, 0); +} + +void func_8002F758(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5) { + func_8002F698(globalCtx, actor, arg2, arg3, arg4, 1, arg5); +} + +void func_8002F7A0(GlobalContext* globalCtx, Actor* actor, f32 arg2, s16 arg3, f32 arg4) { + func_8002F758(globalCtx, actor, arg2, arg3, arg4, 0); +} + +void func_8002F7DC(Actor* actor, u16 sfxId) { + Audio_PlaySoundGeneral(sfxId, &actor->projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void Audio_PlayActorSound2(Actor* actor, u16 sfxId) { + func_80078914(&actor->projectedPos, sfxId); +} + +void func_8002F850(GlobalContext* globalCtx, Actor* actor) { + s32 sfxId; + + if (actor->bgCheckFlags & 0x20) { + if (actor->yDistToWater < 20.0f) { + sfxId = NA_SE_PL_WALK_WATER0 - SFX_FLAG; + } else { + sfxId = NA_SE_PL_WALK_WATER1 - SFX_FLAG; + } + } else { + sfxId = SurfaceType_GetSfx(&globalCtx->colCtx, actor->floorPoly, actor->floorBgId); + } + + func_80078914(&actor->projectedPos, NA_SE_EV_BOMB_BOUND); + func_80078914(&actor->projectedPos, sfxId + SFX_FLAG); +} + +void func_8002F8F0(Actor* actor, u16 sfxId) { + actor->sfx = sfxId; + actor->flags |= ACTOR_FLAG_19; + actor->flags &= ~(ACTOR_FLAG_20 | ACTOR_FLAG_21 | ACTOR_FLAG_28); +} + +void func_8002F91C(Actor* actor, u16 sfxId) { + actor->sfx = sfxId; + actor->flags |= ACTOR_FLAG_20; + actor->flags &= ~(ACTOR_FLAG_19 | ACTOR_FLAG_21 | ACTOR_FLAG_28); +} + +void func_8002F948(Actor* actor, u16 sfxId) { + actor->sfx = sfxId; + actor->flags |= ACTOR_FLAG_21; + actor->flags &= ~(ACTOR_FLAG_19 | ACTOR_FLAG_20 | ACTOR_FLAG_28); +} + +void func_8002F974(Actor* actor, u16 sfxId) { + actor->flags &= ~(ACTOR_FLAG_19 | ACTOR_FLAG_20 | ACTOR_FLAG_21 | ACTOR_FLAG_28); + actor->sfx = sfxId; +} + +void func_8002F994(Actor* actor, s32 arg1) { + actor->flags |= ACTOR_FLAG_28; + actor->flags &= ~(ACTOR_FLAG_19 | ACTOR_FLAG_20 | ACTOR_FLAG_21); + if (arg1 < 40) { + actor->sfx = NA_SE_PL_WALK_DIRT - SFX_FLAG; + } else if (arg1 < 100) { + actor->sfx = NA_SE_PL_WALK_CONCRETE - SFX_FLAG; + } else { + actor->sfx = NA_SE_PL_WALK_SAND - SFX_FLAG; + } +} + +// Tests if something hit Jabu Jabu surface, displaying hit splash and playing sfx if true +s32 func_8002F9EC(GlobalContext* globalCtx, Actor* actor, CollisionPoly* poly, s32 bgId, Vec3f* pos) { + if (func_80041D4C(&globalCtx->colCtx, poly, bgId) == 8) { + globalCtx->roomCtx.unk_74[0] = 1; + CollisionCheck_BlueBlood(globalCtx, NULL, pos); + Audio_PlayActorSound2(actor, NA_SE_IT_WALL_HIT_BUYO); + return true; + } + + return false; +} + +// Local data used for Farore's Wind light (stored in BSS, possibly a struct?) +LightInfo D_8015BC00; +LightNode* D_8015BC10; +s32 D_8015BC14; +f32 D_8015BC18; + +void func_8002FA60(GlobalContext* globalCtx) { + Vec3f lightPos; + + if (gSaveContext.fw.set) { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0x28; + gSaveContext.respawn[RESPAWN_MODE_TOP].pos.x = gSaveContext.fw.pos.x; + gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y = gSaveContext.fw.pos.y; + gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z = gSaveContext.fw.pos.z; + gSaveContext.respawn[RESPAWN_MODE_TOP].yaw = gSaveContext.fw.yaw; + gSaveContext.respawn[RESPAWN_MODE_TOP].playerParams = gSaveContext.fw.playerParams; + gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex = gSaveContext.fw.entranceIndex; + gSaveContext.respawn[RESPAWN_MODE_TOP].roomIndex = gSaveContext.fw.roomIndex; + gSaveContext.respawn[RESPAWN_MODE_TOP].tempSwchFlags = gSaveContext.fw.tempSwchFlags; + gSaveContext.respawn[RESPAWN_MODE_TOP].tempCollectFlags = gSaveContext.fw.tempCollectFlags; + } else { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0; + gSaveContext.respawn[RESPAWN_MODE_TOP].pos.x = 0.0f; + gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y = 0.0f; + gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z = 0.0f; + } + + lightPos.x = gSaveContext.respawn[RESPAWN_MODE_TOP].pos.x; + lightPos.y = gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y + 80.0f; + lightPos.z = gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z; + + Lights_PointNoGlowSetInfo(&D_8015BC00, lightPos.x, lightPos.y, lightPos.z, 0xFF, 0xFF, 0xFF, -1); + + D_8015BC10 = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &D_8015BC00); + D_8015BC14 = 0; + D_8015BC18 = 0.0f; +} + +void Actor_DrawFaroresWindPointer(GlobalContext* globalCtx) { + s32 lightRadius = -1; + s32 params; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 5308); + + params = gSaveContext.respawn[RESPAWN_MODE_TOP].data; + + if (params) { + f32 yOffset = LINK_IS_ADULT ? 80.0f : 60.0f; + f32 ratio = 1.0f; + s32 alpha = 255; + s32 temp = params - 40; + + if (temp < 0) { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = ++params; + ratio = ABS(params) * 0.025f; + D_8015BC14 = 60; + D_8015BC18 = 1.0f; + } else if (D_8015BC14) { + D_8015BC14--; + } else if (D_8015BC18 > 0.0f) { + static Vec3f effectVel = { 0.0f, -0.05f, 0.0f }; + static Vec3f effectAccel = { 0.0f, -0.025f, 0.0f }; + static Color_RGBA8 effectPrimCol = { 255, 255, 255, 0 }; + static Color_RGBA8 effectEnvCol = { 100, 200, 0, 0 }; + Vec3f* curPos = &gSaveContext.respawn[RESPAWN_MODE_TOP].pos; + Vec3f* nextPos = &gSaveContext.respawn[RESPAWN_MODE_DOWN].pos; + f32 prevNum = D_8015BC18; + Vec3f dist; + f32 diff = Math_Vec3f_DistXYZAndStoreDiff(nextPos, curPos, &dist); + Vec3f effectPos; + f32 factor; + f32 length; + f32 dx; + f32 speed; + + if (diff < 20.0f) { + D_8015BC18 = 0.0f; + Math_Vec3f_Copy(curPos, nextPos); + } else { + length = diff * (1.0f / D_8015BC18); + speed = 20.0f / length; + speed = CLAMP_MIN(speed, 0.05f); + Math_StepToF(&D_8015BC18, 0.0f, speed); + factor = (diff * (D_8015BC18 / prevNum)) / diff; + curPos->x = nextPos->x + (dist.x * factor); + curPos->y = nextPos->y + (dist.y * factor); + curPos->z = nextPos->z + (dist.z * factor); + length *= 0.5f; + dx = diff - length; + yOffset += sqrtf(SQ(length) - SQ(dx)) * 0.2f; + osSyncPrintf("-------- DISPLAY Y=%f\n", yOffset); + } + + effectPos.x = curPos->x + Rand_CenteredFloat(6.0f); + effectPos.y = curPos->y + 80.0f + (6.0f * Rand_ZeroOne()); + effectPos.z = curPos->z + Rand_CenteredFloat(6.0f); + + EffectSsKiraKira_SpawnDispersed(globalCtx, &effectPos, &effectVel, &effectAccel, &effectPrimCol, + &effectEnvCol, 1000, 16); + + if (D_8015BC18 == 0.0f) { + gSaveContext.respawn[RESPAWN_MODE_TOP] = gSaveContext.respawn[RESPAWN_MODE_DOWN]; + gSaveContext.respawn[RESPAWN_MODE_TOP].playerParams = 0x06FF; + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 40; + } + + gSaveContext.respawn[RESPAWN_MODE_TOP].pos = *curPos; + } else if (temp > 0) { + Vec3f* curPos = &gSaveContext.respawn[RESPAWN_MODE_TOP].pos; + f32 nextRatio = 1.0f - temp * 0.1f; + f32 curRatio = 1.0f - (f32)(temp - 1) * 0.1f; + Vec3f eye; + Vec3f dist; + f32 diff; + + if (nextRatio > 0.0f) { + eye.x = globalCtx->view.eye.x; + eye.y = globalCtx->view.eye.y - yOffset; + eye.z = globalCtx->view.eye.z; + diff = Math_Vec3f_DistXYZAndStoreDiff(&eye, curPos, &dist); + diff = (diff * (nextRatio / curRatio)) / diff; + curPos->x = eye.x + (dist.x * diff); + curPos->y = eye.y + (dist.y * diff); + curPos->z = eye.z + (dist.z * diff); + gSaveContext.respawn[RESPAWN_MODE_TOP].pos = *curPos; + } + + alpha = 255 - (temp * 30); + + if (alpha < 0) { + gSaveContext.fw.set = 0; + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0; + alpha = 0; + } else { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = ++params; + } + + ratio = 1.0f + ((f32)temp * 0.2); // required to match + } + + lightRadius = 500.0f * ratio; + + if ((globalCtx->csCtx.state == CS_STATE_IDLE) && + (((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex) == + ((void)0, gSaveContext.entranceIndex)) && + (((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].roomIndex) == globalCtx->roomCtx.curRoom.num)) { + f32 scale = 0.025f * ratio; + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x19); + + Matrix_Translate(((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.x), + ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y) + yOffset, + ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z), MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_Push(); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 200, alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 200, 0, 255); + + Matrix_RotateZ(((globalCtx->gameplayFrames * 1500) & 0xFFFF) * M_PI / 32768.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 5458), + G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + + Matrix_Pop(); + Matrix_RotateZ(~((globalCtx->gameplayFrames * 1200) & 0xFFFF) * M_PI / 32768.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 5463), + G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + } + + Lights_PointNoGlowSetInfo(&D_8015BC00, ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.x), + ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.y) + yOffset, + ((void)0, gSaveContext.respawn[RESPAWN_MODE_TOP].pos.z), 255, 255, 255, lightRadius); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 5474); + } +} + +void func_80030488(GlobalContext* globalCtx) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, D_8015BC10); +} + +void func_800304B0(GlobalContext* globalCtx) { + if (globalCtx->actorCtx.unk_03 != 0) { + globalCtx->actorCtx.unk_03 = 0; + func_800876C8(globalCtx); + } +} + +// Actor_InitContext +void func_800304DC(GlobalContext* globalCtx, ActorContext* actorCtx, ActorEntry* actorEntry) { + ActorOverlay* overlayEntry; + SavedSceneFlags* savedSceneFlags; + s32 i; + + savedSceneFlags = &gSaveContext.sceneFlags[globalCtx->sceneNum]; + + memset(actorCtx, 0, sizeof(*actorCtx)); + + ActorOverlayTable_Init(); + Matrix_MtxFCopy(&globalCtx->billboardMtxF, &gMtxFClear); + Matrix_MtxFCopy(&globalCtx->viewProjectionMtxF, &gMtxFClear); + + overlayEntry = &gActorOverlayTable[0]; + for (i = 0; i < ARRAY_COUNT(gActorOverlayTable); i++) { + overlayEntry->loadedRamAddr = NULL; + overlayEntry->numLoaded = 0; + overlayEntry++; + } + + actorCtx->flags.chest = savedSceneFlags->chest; + actorCtx->flags.swch = savedSceneFlags->swch; + actorCtx->flags.clear = savedSceneFlags->clear; + actorCtx->flags.collect = savedSceneFlags->collect; + + func_8002CDE4(globalCtx, &actorCtx->titleCtx); + + actorCtx->absoluteSpace = NULL; + + Actor_SpawnEntry(actorCtx, actorEntry, globalCtx); + func_8002C0C0(&actorCtx->targetCtx, actorCtx->actorLists[ACTORCAT_PLAYER].head, globalCtx); + func_8002FA60(globalCtx); +} + +u32 D_80116068[] = { + 0x100000C0, 0x100000C0, 0x00000000, 0x100004C0, 0x00000080, 0x300000C0, + 0x10000080, 0x00000000, 0x300000C0, 0x100004C0, 0x00000000, 0x100000C0, +}; + +void Actor_UpdateAll(GlobalContext* globalCtx, ActorContext* actorCtx) { + Actor* refActor; + Actor* actor; + Player* player; + u32* sp80; + u32 unkFlag; + u32 unkCondition; + Actor* sp74; + ActorEntry* actorEntry; + s32 i; + + player = GET_PLAYER(globalCtx); + + if (0) { + // This ASSERT is optimized out but it exists due to its presence in rodata + ASSERT(gMaxActorId == ACTOR_ID_MAX, "MaxProfile == ACTOR_DLF_MAX", "../z_actor.c", UNK_LINE); + } + + sp74 = NULL; + unkFlag = 0; + + if (globalCtx->numSetupActors != 0) { + actorEntry = &globalCtx->setupActorList[0]; + for (i = 0; i < globalCtx->numSetupActors; i++) { + Actor_SpawnEntry(&globalCtx->actorCtx, actorEntry++, globalCtx); + } + globalCtx->numSetupActors = 0; + } + + if (actorCtx->unk_02 != 0) { + actorCtx->unk_02--; + } + + if (KREG(0) == -100) { + refActor = &GET_PLAYER(globalCtx)->actor; + KREG(0) = 0; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_CLEAR_TAG, refActor->world.pos.x, + refActor->world.pos.y + 100.0f, refActor->world.pos.z, 0, 0, 0, 1); + } + + sp80 = &D_80116068[0]; + + if (player->stateFlags2 & 0x8000000) { + unkFlag = ACTOR_FLAG_25; + } + + if ((player->stateFlags1 & 0x40) && ((player->actor.textId & 0xFF00) != 0x600)) { + sp74 = player->targetActor; + } + + for (i = 0; i < ARRAY_COUNT(actorCtx->actorLists); i++, sp80++) { + unkCondition = (*sp80 & player->stateFlags1); + + actor = actorCtx->actorLists[i].head; + while (actor != NULL) { + if (actor->world.pos.y < -25000.0f) { + actor->world.pos.y = -25000.0f; + } + + actor->sfx = 0; + + if (actor->init != NULL) { + if (Object_IsLoaded(&globalCtx->objectCtx, actor->objBankIndex)) + { + Actor_SetObjectDependency(globalCtx, actor); + actor->init(actor, globalCtx); + actor->init = NULL; + } + actor = actor->next; + } else if (!Object_IsLoaded(&globalCtx->objectCtx, actor->objBankIndex)) { + Actor_Kill(actor); + actor = actor->next; + } else if ((unkFlag && !(actor->flags & unkFlag)) || + (!unkFlag && unkCondition && (sp74 != actor) && (actor != player->naviActor) && + (actor != player->heldActor) && (&player->actor != actor->parent))) { + CollisionCheck_ResetDamage(&actor->colChkInfo); + actor = actor->next; + } else if (actor->update == NULL) { + if (!actor->isDrawn) { + actor = Actor_Delete(&globalCtx->actorCtx, actor, globalCtx); + } else { + Actor_Destroy(actor, globalCtx); + actor = actor->next; + } + } else { + Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); + actor->xzDistToPlayer = Actor_WorldDistXZToActor(actor, &player->actor); + actor->yDistToPlayer = Actor_HeightDiff(actor, &player->actor); + actor->xyzDistToPlayerSq = SQ(actor->xzDistToPlayer) + SQ(actor->yDistToPlayer); + + actor->yawTowardsPlayer = Actor_WorldYawTowardActor(actor, &player->actor); + actor->flags &= ~ACTOR_FLAG_24; + + if ((DECR(actor->freezeTimer) == 0) && (actor->flags & (ACTOR_FLAG_4 | ACTOR_FLAG_6))) { + if (actor == player->unk_664) { + actor->isTargeted = true; + } else { + actor->isTargeted = false; + } + + if ((actor->targetPriority != 0) && (player->unk_664 == NULL)) { + actor->targetPriority = 0; + } + + Actor_SetObjectDependency(globalCtx, actor); + if (actor->colorFilterTimer != 0) { + actor->colorFilterTimer--; + } + actor->update(actor, globalCtx); + func_8003F8EC(globalCtx, &globalCtx->colCtx.dyna, actor); + } + + CollisionCheck_ResetDamage(&actor->colChkInfo); + + actor = actor->next; + } + } + + if (i == ACTORCAT_BG) { + DynaPoly_Setup(globalCtx, &globalCtx->colCtx.dyna); + } + } + + actor = player->unk_664; + + if ((actor != NULL) && (actor->update == NULL)) { + actor = NULL; + func_8008EDF0(player); + } + + if ((actor == NULL) || (player->unk_66C < 5)) { + actor = NULL; + if (actorCtx->targetCtx.unk_4B != 0) { + actorCtx->targetCtx.unk_4B = 0; + func_80078884(NA_SE_SY_LOCK_OFF); + } + } + + func_8002C7BC(&actorCtx->targetCtx, player, actor, globalCtx); + TitleCard_Update(globalCtx, &actorCtx->titleCtx); + DynaPoly_UpdateBgActorTransforms(globalCtx, &globalCtx->colCtx.dyna); +} + +void Actor_FaultPrint(Actor* actor, char* command) { + ActorOverlay* overlayEntry; + char* name; + + if ((actor == NULL) || (actor->overlayEntry == NULL)) { + FaultDrawer_SetCursor(48, 24); + FaultDrawer_Printf("ACTOR NAME is NULL"); + } + + overlayEntry = actor->overlayEntry; + name = overlayEntry->name != NULL ? overlayEntry->name : ""; + + osSyncPrintf("アクターの名前(%08x:%s)\n", actor, name); // "Actor name (%08x:%s)" + + if (command != NULL) { + osSyncPrintf("コメント:%s\n", command); // "Command:%s" + } + + FaultDrawer_SetCursor(48, 24); + FaultDrawer_Printf("ACTOR NAME %08x:%s", actor, name); +} + +void Actor_Draw(GlobalContext* globalCtx, Actor* actor) { + FaultClient faultClient; + Lights* lights; + + Fault_AddClient(&faultClient, Actor_FaultPrint, actor, "Actor_draw"); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6035); + + lights = LightContext_NewLights(&globalCtx->lightCtx, globalCtx->state.gfxCtx); + + Lights_BindAll(lights, globalCtx->lightCtx.listHead, (actor->flags & ACTOR_FLAG_22) ? NULL : &actor->world.pos); + Lights_Draw(lights, globalCtx->state.gfxCtx); + + if (actor->flags & ACTOR_FLAG_12) { + Matrix_SetTranslateRotateYXZ( + actor->world.pos.x + globalCtx->mainCamera.skyboxOffset.x, + actor->world.pos.y + (f32)((actor->shape.yOffset * actor->scale.y) + globalCtx->mainCamera.skyboxOffset.y), + actor->world.pos.z + globalCtx->mainCamera.skyboxOffset.z, &actor->shape.rot); + } else { + Matrix_SetTranslateRotateYXZ(actor->world.pos.x, actor->world.pos.y + (actor->shape.yOffset * actor->scale.y), + actor->world.pos.z, &actor->shape.rot); + } + + Matrix_Scale(actor->scale.x, actor->scale.y, actor->scale.z, MTXMODE_APPLY); + Actor_SetObjectDependency(globalCtx, actor); + + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[actor->objBankIndex].segment); + gSPSegment(POLY_XLU_DISP++, 0x06, globalCtx->objectCtx.status[actor->objBankIndex].segment); + + if (actor->colorFilterTimer != 0) { + Color_RGBA8 color = { 0, 0, 0, 255 }; + + if (actor->colorFilterParams & 0x8000) { + color.r = color.g = color.b = ((actor->colorFilterParams & 0x1F00) >> 5) | 7; + } else if (actor->colorFilterParams & 0x4000) { + color.r = ((actor->colorFilterParams & 0x1F00) >> 5) | 7; + } else { + color.b = ((actor->colorFilterParams & 0x1F00) >> 5) | 7; + } + + if (actor->colorFilterParams & 0x2000) { + func_80026860(globalCtx, &color, actor->colorFilterTimer, actor->colorFilterParams & 0xFF); + } else { + func_80026400(globalCtx, &color, actor->colorFilterTimer, actor->colorFilterParams & 0xFF); + } + } + + actor->draw(actor, globalCtx); + + if (actor->colorFilterTimer != 0) { + if (actor->colorFilterParams & 0x2000) { + func_80026A6C(globalCtx); + } else { + func_80026608(globalCtx); + } + } + + if (actor->shape.shadowDraw != NULL) { + actor->shape.shadowDraw(actor, lights, globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6119); + + Fault_RemoveClient(&faultClient); +} + +void func_80030ED8(Actor* actor) { + if (actor->flags & ACTOR_FLAG_19) { + Audio_PlaySoundGeneral(actor->sfx, &actor->projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (actor->flags & ACTOR_FLAG_20) { + func_80078884(actor->sfx); + } else if (actor->flags & ACTOR_FLAG_21) { + func_800788CC(actor->sfx); + } else if (actor->flags & ACTOR_FLAG_28) { + func_800F4C58(&D_801333D4, NA_SE_SY_TIMER - SFX_FLAG, (s8)(actor->sfx - 1)); + } else { + func_80078914(&actor->projectedPos, actor->sfx); + } +} + +void func_80030FA8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_actor.c", 6161); + + gDPLoadTextureBlock(POLY_XLU_DISP++, gLensOfTruthMaskTex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, + G_TX_MIRROR | G_TX_CLAMP, G_TX_MIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetTileSize(POLY_XLU_DISP++, G_TX_RENDERTILE, 384, 224, 892, 732); + gSPTextureRectangle(POLY_XLU_DISP++, 0, 0, 1280, 960, G_TX_RENDERTILE, 2240, 1600, 576, 597); + gDPPipeSync(POLY_XLU_DISP++); + + CLOSE_DISPS(gfxCtx, "../z_actor.c", 6183); +} + +void func_8003115C(GlobalContext* globalCtx, s32 numInvisibleActors, Actor** invisibleActors) { + Actor** invisibleActor; + GraphicsContext* gfxCtx; + s32 i; + + gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_actor.c", 6197); + + gDPNoOpString(POLY_OPA_DISP++, "魔法のメガネ START", 0); // "Magic lens START" + + gDPPipeSync(POLY_XLU_DISP++); + + if (globalCtx->roomCtx.curRoom.showInvisActors == 0) { + gDPSetOtherMode(POLY_XLU_DISP++, + G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PRIM | Z_UPD | G_RM_CLD_SURF | G_RM_CLD_SURF2); + gDPSetCombineMode(POLY_XLU_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 0, 0, 255); + } else { + gDPSetOtherMode(POLY_XLU_DISP++, + G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PRIM | Z_UPD | IM_RD | CVG_DST_SAVE | ZMODE_OPA | FORCE_BL | + GBL_c1(G_BL_CLR_BL, G_BL_0, G_BL_CLR_MEM, G_BL_1MA) | + GBL_c2(G_BL_CLR_BL, G_BL_0, G_BL_CLR_MEM, G_BL_1MA)); + gDPSetCombineLERP(POLY_XLU_DISP++, PRIMITIVE, TEXEL0, PRIM_LOD_FRAC, 0, PRIMITIVE, TEXEL0, PRIM_LOD_FRAC, 0, + PRIMITIVE, TEXEL0, PRIM_LOD_FRAC, 0, PRIMITIVE, TEXEL0, PRIM_LOD_FRAC, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0xFF, 74, 74, 74, 74); + } + + gDPSetPrimDepth(POLY_XLU_DISP++, 0, 0); + + func_80030FA8(gfxCtx); + + // "Magic lens invisible Actor display START" + gDPNoOpString(POLY_OPA_DISP++, "魔法のメガネ 見えないActor表示 START", numInvisibleActors); + + invisibleActor = &invisibleActors[0]; + for (i = 0; i < numInvisibleActors; i++) { + // "Magic lens invisible Actor display" + gDPNoOpString(POLY_OPA_DISP++, "魔法のメガネ 見えないActor表示", i); + Actor_Draw(globalCtx, *(invisibleActor++)); + } + + // "Magic lens invisible Actor display END" + gDPNoOpString(POLY_OPA_DISP++, "魔法のメガネ 見えないActor表示 END", numInvisibleActors); + + if (globalCtx->roomCtx.curRoom.showInvisActors != 0) { + gDPNoOpString(POLY_OPA_DISP++, "青い眼鏡(外側)", 0); // "Blue spectacles (exterior)" + + gDPPipeSync(POLY_XLU_DISP++); + + gDPSetOtherMode(POLY_XLU_DISP++, + G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PRIM | G_RM_CLD_SURF | G_RM_CLD_SURF2); + gDPSetCombineMode(POLY_XLU_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 0, 0, 255); + + func_80030FA8(gfxCtx); + + gDPNoOpString(POLY_OPA_DISP++, "青い眼鏡(外側)", 1); // "Blue spectacles (exterior)" + } + + gDPNoOpString(POLY_OPA_DISP++, "魔法のメガネ END", 0); // "Magic lens END" + + CLOSE_DISPS(gfxCtx, "../z_actor.c", 6284); +} + +s32 func_800314B0(GlobalContext* globalCtx, Actor* actor) { + return func_800314D4(globalCtx, actor, &actor->projectedPos, actor->projectedW); +} + +s32 func_800314D4(GlobalContext* globalCtx, Actor* actor, Vec3f* arg2, f32 arg3) { + f32 var; + + if (CVar_GetS32("gDisableDrawDistance", 0) != 0) { + return true; + } + + if ((arg2->z > -actor->uncullZoneScale) && (arg2->z < (actor->uncullZoneForward + actor->uncullZoneScale))) { + var = (arg3 < 1.0f) ? 1.0f : 1.0f / arg3; + + if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < 2.0f) && + (((arg2->y + actor->uncullZoneDownward) * var) > -2.0f) && + (((arg2->y - actor->uncullZoneScale) * var) < 2.0f)) { + return true; + } + } + + return false; +} + +void func_800315AC(GlobalContext* globalCtx, ActorContext* actorCtx) { + s32 invisibleActorCounter; + Actor* invisibleActors[INVISIBLE_ACTOR_MAX]; + ActorListEntry* actorListEntry; + Actor* actor; + s32 i; + + invisibleActorCounter = 0; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6336); + + actorListEntry = &actorCtx->actorLists[0]; + + for (i = 0; i < ARRAY_COUNT(actorCtx->actorLists); i++, actorListEntry++) { + actor = actorListEntry->head; + + while (actor != NULL) { + ActorOverlay* overlayEntry = actor->overlayEntry; + char* actorName = overlayEntry->name != NULL ? overlayEntry->name : ""; + + gDPNoOpString(POLY_OPA_DISP++, actorName, i); + gDPNoOpString(POLY_XLU_DISP++, actorName, i); + + HREG(66) = i; + + if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(68) == 0)) { + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &actor->world.pos, &actor->projectedPos, + &actor->projectedW); + } + + if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(69) == 0)) { + if (actor->sfx != 0) { + func_80030ED8(actor); + } + } + + if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(70) == 0)) { + if (func_800314B0(globalCtx, actor)) { + actor->flags |= ACTOR_FLAG_6; + } else { + actor->flags &= ~ACTOR_FLAG_6; + } + } + + actor->isDrawn = false; + + if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(71) == 0)) { + if ((actor->init == NULL) && (actor->draw != NULL) && (actor->flags & (ACTOR_FLAG_5 | ACTOR_FLAG_6))) { + if ((actor->flags & ACTOR_FLAG_7) && + ((globalCtx->roomCtx.curRoom.showInvisActors == 0) || (globalCtx->actorCtx.unk_03 != 0) || + (actor->room != globalCtx->roomCtx.curRoom.num))) { + ASSERT(invisibleActorCounter < INVISIBLE_ACTOR_MAX, + "invisible_actor_counter < INVISIBLE_ACTOR_MAX", "../z_actor.c", 6464); + invisibleActors[invisibleActorCounter] = actor; + invisibleActorCounter++; + } else { + if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(72) == 0)) { + Actor_Draw(globalCtx, actor); + actor->isDrawn = true; + } + } + } + } + + actor = actor->next; + } + } + + if ((HREG(64) != 1) || (HREG(73) != 0)) { + Effect_DrawAll(globalCtx->state.gfxCtx); + } + + if ((HREG(64) != 1) || (HREG(74) != 0)) { + EffectSs_DrawAll(globalCtx); + } + + if ((HREG(64) != 1) || (HREG(72) != 0)) { + if (globalCtx->actorCtx.unk_03 != 0) { + func_8003115C(globalCtx, invisibleActorCounter, invisibleActors); + if ((globalCtx->csCtx.state != CS_STATE_IDLE) || Player_InCsMode(globalCtx)) { + func_800304B0(globalCtx); + } + } + } + + Actor_DrawFaroresWindPointer(globalCtx); + + if (IREG(32) == 0) { + Lights_DrawGlow(globalCtx); + } + + if ((HREG(64) != 1) || (HREG(75) != 0)) { + TitleCard_Draw(globalCtx, &actorCtx->titleCtx); + } + + if ((HREG(64) != 1) || (HREG(76) != 0)) { + CollisionCheck_DrawCollision(globalCtx, &globalCtx->colChkCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 6563); +} + +void func_80031A28(GlobalContext* globalCtx, ActorContext* actorCtx) { + Actor* actor; + s32 i; + + for (i = 0; i < ARRAY_COUNT(actorCtx->actorLists); i++) { + actor = actorCtx->actorLists[i].head; + while (actor != NULL) { + if (!Object_IsLoaded(&globalCtx->objectCtx, actor->objBankIndex)) { + Actor_Kill(actor); + } + actor = actor->next; + } + } +} + +u8 sEnemyActorCategories[] = { ACTORCAT_ENEMY, ACTORCAT_BOSS }; + +void Actor_FreezeAllEnemies(GlobalContext* globalCtx, ActorContext* actorCtx, s32 duration) { + Actor* actor; + s32 i; + + for (i = 0; i < ARRAY_COUNT(sEnemyActorCategories); i++) { + actor = actorCtx->actorLists[sEnemyActorCategories[i]].head; + while (actor != NULL) { + actor->freezeTimer = duration; + actor = actor->next; + } + } +} + +void func_80031B14(GlobalContext* globalCtx, ActorContext* actorCtx) { + Actor* actor; + s32 i; + + for (i = 0; i < ARRAY_COUNT(actorCtx->actorLists); i++) { + actor = actorCtx->actorLists[i].head; + while (actor != NULL) { + if ((actor->room >= 0) && (actor->room != globalCtx->roomCtx.curRoom.num) && + (actor->room != globalCtx->roomCtx.prevRoom.num)) { + if (!actor->isDrawn) { + actor = Actor_Delete(actorCtx, actor, globalCtx); + } else { + Actor_Kill(actor); + Actor_Destroy(actor, globalCtx); + actor = actor->next; + } + } else { + actor = actor->next; + } + } + } + + CollisionCheck_ClearContext(globalCtx, &globalCtx->colChkCtx); + actorCtx->flags.tempClear = 0; + actorCtx->flags.tempSwch &= 0xFFFFFF; + globalCtx->msgCtx.unk_E3F4 = 0; +} + +// Actor_CleanupContext +void func_80031C3C(ActorContext* actorCtx, GlobalContext* globalCtx) { + Actor* actor; + s32 i; + + for (i = 0; i < ARRAY_COUNT(actorCtx->actorLists); i++) { + actor = actorCtx->actorLists[i].head; + while (actor != NULL) { + Actor_Delete(actorCtx, actor, globalCtx); + actor = actorCtx->actorLists[i].head; + } + } + + if (HREG(20) != 0) { + osSyncPrintf("絶対魔法領域解放\n"); // "Absolute magic field deallocation" + } + + if (actorCtx->absoluteSpace != NULL) { + ZeldaArena_FreeDebug(actorCtx->absoluteSpace, "../z_actor.c", 6731); + actorCtx->absoluteSpace = NULL; + } + + Gameplay_SaveSceneFlags(globalCtx); + func_80030488(globalCtx); + ActorOverlayTable_Cleanup(); +} + +/** + * Adds a given actor instance at the front of the actor list of the specified category. + * Also sets the actor instance as being of that category. + */ +void Actor_AddToCategory(ActorContext* actorCtx, Actor* actorToAdd, u8 actorCategory) { + Actor* prevHead; + + actorToAdd->category = actorCategory; + + actorCtx->total++; + actorCtx->actorLists[actorCategory].length++; + prevHead = actorCtx->actorLists[actorCategory].head; + + if (prevHead != NULL) { + prevHead->prev = actorToAdd; + } + + actorCtx->actorLists[actorCategory].head = actorToAdd; + actorToAdd->next = prevHead; +} + +/** + * Removes a given actor instance from its actor list. + * Also sets the temp clear flag of the current room if the actor removed was the last enemy loaded. + */ +Actor* Actor_RemoveFromCategory(GlobalContext* globalCtx, ActorContext* actorCtx, Actor* actorToRemove) { + Actor* newHead; + + actorCtx->total--; + actorCtx->actorLists[actorToRemove->category].length--; + + if (actorToRemove->prev != NULL) { + actorToRemove->prev->next = actorToRemove->next; + } else { + actorCtx->actorLists[actorToRemove->category].head = actorToRemove->next; + } + + newHead = actorToRemove->next; + + if (newHead != NULL) { + newHead->prev = actorToRemove->prev; + } + + actorToRemove->next = NULL; + actorToRemove->prev = NULL; + + if ((actorToRemove->room == globalCtx->roomCtx.curRoom.num) && (actorToRemove->category == ACTORCAT_ENEMY) && + (actorCtx->actorLists[ACTORCAT_ENEMY].length == 0)) { + Flags_SetTempClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + + return newHead; +} + +void Actor_FreeOverlay(ActorOverlay* actorOverlay) { + osSyncPrintf(VT_FGCOL(CYAN)); + + if (actorOverlay->numLoaded == 0) { + + if (actorOverlay->initInfo->reset != NULL) { + actorOverlay->initInfo->reset(); + } + + if (HREG(20) != 0) { + osSyncPrintf("アクタークライアントが0になりました\n"); // "Actor client is now 0" + } + + if (actorOverlay->loadedRamAddr != NULL) { + if (actorOverlay->allocType & ALLOCTYPE_PERMANENT) { + if (HREG(20) != 0) { + osSyncPrintf("オーバーレイ解放しません\n"); // "Overlay will not be deallocated" + } + } else if (actorOverlay->allocType & ALLOCTYPE_ABSOLUTE) { + if (HREG(20) != 0) { + // "Absolute magic field reserved, so deallocation will not occur" + osSyncPrintf("絶対魔法領域確保なので解放しません\n"); + } + actorOverlay->loadedRamAddr = NULL; + } else { + if (HREG(20) != 0) { + osSyncPrintf("オーバーレイ解放します\n"); // "Overlay deallocated" + } + ZeldaArena_FreeDebug(actorOverlay->loadedRamAddr, "../z_actor.c", 6834); + actorOverlay->loadedRamAddr = NULL; + } + } + } else if (HREG(20) != 0) { + // "%d of actor client remains" + osSyncPrintf("アクタークライアントはあと %d 残っています\n", actorOverlay->numLoaded); + } + + osSyncPrintf(VT_RST); +} + +int gMapLoading = 0; + +Actor* Actor_Spawn(ActorContext* actorCtx, GlobalContext* globalCtx, s16 actorId, f32 posX, f32 posY, f32 posZ, + s16 rotX, s16 rotY, s16 rotZ, s16 params) { + s32 pad; + Actor* actor; + ActorInit* actorInit; + s32 objBankIndex; + ActorOverlay* overlayEntry; + u32 temp; + char* name; + u32 overlaySize; + + overlayEntry = &gActorOverlayTable[actorId]; + ASSERT(actorId < ACTOR_ID_MAX, "profile < ACTOR_DLF_MAX", "../z_actor.c", 6883); + + name = overlayEntry->name != NULL ? overlayEntry->name : ""; + overlaySize = (uintptr_t)overlayEntry->vramEnd - (uintptr_t)overlayEntry->vramStart; + + if (HREG(20) != 0) { + // "Actor class addition [%d:%s]" + osSyncPrintf("アクタークラス追加 [%d:%s]\n", actorId, name); + } + + if (actorCtx->total > ACTOR_NUMBER_MAX) { + // "Actor set number exceeded" + osSyncPrintf(VT_COL(YELLOW, BLACK) "Actorセット数オーバー\n" VT_RST); + return NULL; + } + + if (overlayEntry->vramStart == 0) { + if (HREG(20) != 0) { + osSyncPrintf("オーバーレイではありません\n"); // "Not an overlay" + } + + actorInit = overlayEntry->initInfo; + } else { + if (overlayEntry->loadedRamAddr != NULL) { + if (HREG(20) != 0) { + osSyncPrintf("既にロードされています\n"); // "Already loaded" + } + } else { + if (overlayEntry->allocType & ALLOCTYPE_ABSOLUTE) { + ASSERT(overlaySize <= AM_FIELD_SIZE, "actor_segsize <= AM_FIELD_SIZE", "../z_actor.c", 6934); + + if (actorCtx->absoluteSpace == NULL) { + // "AMF: absolute magic field" + actorCtx->absoluteSpace = ZeldaArena_MallocRDebug(AM_FIELD_SIZE, "AMF:絶対魔法領域", 0); + if (HREG(20) != 0) { + // "Absolute magic field reservation - %d bytes reserved" + osSyncPrintf("絶対魔法領域確保 %d バイト確保\n", AM_FIELD_SIZE); + } + } + + overlayEntry->loadedRamAddr = actorCtx->absoluteSpace; + } else if (overlayEntry->allocType & ALLOCTYPE_PERMANENT) { + overlayEntry->loadedRamAddr = ZeldaArena_MallocRDebug(overlaySize, name, 0); + } else { + overlayEntry->loadedRamAddr = ZeldaArena_MallocDebug(overlaySize, name, 0); + } + + if (overlayEntry->loadedRamAddr == NULL) { + // "Cannot reserve actor program memory" + osSyncPrintf(VT_COL(RED, WHITE) "Actorプログラムメモリが確保できません\n" VT_RST); + return NULL; + } + + Overlay_Load(overlayEntry->vromStart, overlayEntry->vromEnd, overlayEntry->vramStart, overlayEntry->vramEnd, + overlayEntry->loadedRamAddr); + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("OVL(a):Seg:%08x-%08x Ram:%08x-%08x Off:%08x %s\n", overlayEntry->vramStart, + overlayEntry->vramEnd, overlayEntry->loadedRamAddr, + (uintptr_t)overlayEntry->loadedRamAddr + (uintptr_t)overlayEntry->vramEnd - (uintptr_t)overlayEntry->vramStart, + (uintptr_t)overlayEntry->vramStart - (uintptr_t)overlayEntry->loadedRamAddr, name); + osSyncPrintf(VT_RST); + + overlayEntry->numLoaded = 0; + } + + actorInit = (void*)(uintptr_t)((overlayEntry->initInfo != NULL) + ? (void*)((uintptr_t)overlayEntry->initInfo - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)) + : NULL); + } + + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, actorInit->objectId); + + if (objBankIndex < 0 && !gMapLoading) + objBankIndex = 0; + + if ((objBankIndex < 0) || + ((actorInit->category == ACTORCAT_ENEMY) && Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num))) { + // "No data bank!! (profilep->bank=%d)" + osSyncPrintf(VT_COL(RED, WHITE) "データバンク無し!!<データバンク=%d>(profilep->bank=%d)\n" VT_RST, + objBankIndex, actorInit->objectId); + Actor_FreeOverlay(overlayEntry); + return NULL; + } + + actor = ZeldaArena_MallocDebug(actorInit->instanceSize, name, 1); + + if (actor == NULL) { + // "Actor class cannot be reserved! %s " + osSyncPrintf(VT_COL(RED, WHITE) "Actorクラス確保できません! %s <サイズ=%dバイト>\n", VT_RST, name, + actorInit->instanceSize); + Actor_FreeOverlay(overlayEntry); + return NULL; + } + + ASSERT(overlayEntry->numLoaded < 255, "actor_dlftbl->clients < 255", "../z_actor.c", 7031); + + overlayEntry->numLoaded++; + + if (HREG(20) != 0) { + // "Actor client No. %d" + osSyncPrintf("アクタークライアントは %d 個目です\n", overlayEntry->numLoaded); + } + + memset((u8*)actor, 0, actorInit->instanceSize); + actor->overlayEntry = overlayEntry; + actor->id = actorInit->id; + actor->flags = actorInit->flags; + + if (actorInit->id == ACTOR_EN_PART) { + actor->objBankIndex = rotZ; + rotZ = 0; + } else { + actor->objBankIndex = objBankIndex; + } + + actor->init = actorInit->init; + actor->destroy = actorInit->destroy; + actor->update = actorInit->update; + actor->draw = actorInit->draw; + actor->room = globalCtx->roomCtx.curRoom.num; + actor->home.pos.x = posX; + actor->home.pos.y = posY; + actor->home.pos.z = posZ; + actor->home.rot.x = rotX; + actor->home.rot.y = rotY; + actor->home.rot.z = rotZ; + actor->params = params; + + Actor_AddToCategory(actorCtx, actor, actorInit->category); + + temp = gSegments[6]; + Actor_Init(actor, globalCtx); + gSegments[6] = temp; + + return actor; +} + +Actor* Actor_SpawnAsChild(ActorContext* actorCtx, Actor* parent, GlobalContext* globalCtx, s16 actorId, f32 posX, + f32 posY, f32 posZ, s16 rotX, s16 rotY, s16 rotZ, s16 params) { + Actor* spawnedActor = Actor_Spawn(actorCtx, globalCtx, actorId, posX, posY, posZ, rotX, rotY, rotZ, params); + if (spawnedActor == NULL) { + return NULL; + } + + parent->child = spawnedActor; + spawnedActor->parent = parent; + + if (spawnedActor->room >= 0) { + spawnedActor->room = parent->room; + } + + return spawnedActor; +} + +void Actor_SpawnTransitionActors(GlobalContext* globalCtx, ActorContext* actorCtx) { + TransitionActorEntry* transitionActor; + u8 numActors; + s32 i; + + transitionActor = globalCtx->transiActorCtx.list; + numActors = globalCtx->transiActorCtx.numActors; + + for (i = 0; i < numActors; i++) { + if (transitionActor->id >= 0) { + if (((transitionActor->sides[0].room >= 0) && + ((transitionActor->sides[0].room == globalCtx->roomCtx.curRoom.num) || + (transitionActor->sides[0].room == globalCtx->roomCtx.prevRoom.num))) || + ((transitionActor->sides[1].room >= 0) && + ((transitionActor->sides[1].room == globalCtx->roomCtx.curRoom.num) || + (transitionActor->sides[1].room == globalCtx->roomCtx.prevRoom.num)))) { + Actor_Spawn(actorCtx, globalCtx, (s16)(transitionActor->id & 0x1FFF), transitionActor->pos.x, + transitionActor->pos.y, transitionActor->pos.z, 0, transitionActor->rotY, 0, + (i << 0xA) + transitionActor->params); + + transitionActor->id = -transitionActor->id; + numActors = globalCtx->transiActorCtx.numActors; + } + } + transitionActor++; + } +} + +Actor* Actor_SpawnEntry(ActorContext* actorCtx, ActorEntry* actorEntry, GlobalContext* globalCtx) { + gMapLoading = 1; + Actor* ret = Actor_Spawn(actorCtx, globalCtx, actorEntry->id, actorEntry->pos.x, actorEntry->pos.y, actorEntry->pos.z, + actorEntry->rot.x, actorEntry->rot.y, actorEntry->rot.z, actorEntry->params); + gMapLoading = 0; + + return ret; +} + +Actor* Actor_Delete(ActorContext* actorCtx, Actor* actor, GlobalContext* globalCtx) { + char* name; + Player* player; + Actor* newHead; + ActorOverlay* overlayEntry; + + player = GET_PLAYER(globalCtx); + + overlayEntry = actor->overlayEntry; + name = overlayEntry->name != NULL ? overlayEntry->name : ""; + + if (HREG(20) != 0) { + osSyncPrintf("アクタークラス削除 [%s]\n", name); // "Actor class deleted [%s]" + } + + if ((player != NULL) && (actor == player->unk_664)) { + func_8008EDF0(player); + Camera_ChangeMode(Gameplay_GetCamera(globalCtx, Gameplay_GetActiveCamId(globalCtx)), 0); + } + + if (actor == actorCtx->targetCtx.arrowPointedActor) { + actorCtx->targetCtx.arrowPointedActor = NULL; + } + + if (actor == actorCtx->targetCtx.unk_8C) { + actorCtx->targetCtx.unk_8C = NULL; + } + + if (actor == actorCtx->targetCtx.bgmEnemy) { + actorCtx->targetCtx.bgmEnemy = NULL; + } + + Audio_StopSfxByPos(&actor->projectedPos); + Actor_Destroy(actor, globalCtx); + + newHead = Actor_RemoveFromCategory(globalCtx, actorCtx, actor); + + ZeldaArena_FreeDebug(actor, "../z_actor.c", 7242); + + /* if (overlayEntry->vramStart == 0) { + if (HREG(20) != 0) { + osSyncPrintf("オーバーレイではありません\n"); // "Not an overlay" + } + } else { */ + //ASSERT(overlayEntry->loadedRamAddr != NULL, "actor_dlftbl->allocp != NULL", "../z_actor.c", 7251); + //ASSERT(overlayEntry->numLoaded > 0, "actor_dlftbl->clients > 0", "../z_actor.c", 7252); + overlayEntry->numLoaded--; + Actor_FreeOverlay(overlayEntry); + //} + + return newHead; +} + +s32 func_80032880(GlobalContext* globalCtx, Actor* actor) { + s16 sp1E; + s16 sp1C; + + Actor_GetScreenPos(globalCtx, actor, &sp1E, &sp1C); + + return (sp1E > -20) && (sp1E < 340) && (sp1C > -160) && (sp1C < 400); +} + +Actor* D_8015BBE8; +Actor* D_8015BBEC; +f32 D_8015BBF0; +f32 sbgmEnemyDistSq; +s32 D_8015BBF8; +s16 D_8015BBFC; + +void func_800328D4(GlobalContext* globalCtx, ActorContext* actorCtx, Player* player, u32 actorCategory) { + f32 var; + Actor* actor; + Actor* sp84; + CollisionPoly* sp80; + s32 sp7C; + Vec3f sp70; + + actor = actorCtx->actorLists[actorCategory].head; + sp84 = player->unk_664; + + while (actor != NULL) { + if ((actor->update != NULL) && ((Player*)actor != player) && CHECK_FLAG_ALL(actor->flags, ACTOR_FLAG_0)) { + + // This block below is for determining the closest actor to player in determining the volume + // used while playing enemy bgm music + if ((actorCategory == ACTORCAT_ENEMY) && CHECK_FLAG_ALL(actor->flags, ACTOR_FLAG_0 | ACTOR_FLAG_2) && + (actor->xyzDistToPlayerSq < SQ(500.0f)) && (actor->xyzDistToPlayerSq < sbgmEnemyDistSq)) { + actorCtx->targetCtx.bgmEnemy = actor; + sbgmEnemyDistSq = actor->xyzDistToPlayerSq; + } + + if (actor != sp84) { + var = func_8002EFC0(actor, player, D_8015BBFC); + if ((var < D_8015BBF0) && func_8002F090(actor, var) && func_80032880(globalCtx, actor) && + (!BgCheck_CameraLineTest1(&globalCtx->colCtx, &player->actor.focus.pos, &actor->focus.pos, &sp70, + &sp80, 1, 1, 1, 1, &sp7C) || + SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, sp80, sp7C))) { + if (actor->targetPriority != 0) { + if (actor->targetPriority < D_8015BBF8) { + D_8015BBEC = actor; + D_8015BBF8 = actor->targetPriority; + } + } else { + D_8015BBE8 = actor; + D_8015BBF0 = var; + } + } + } + } + + actor = actor->next; + } +} + +u8 D_801160A0[] = { + ACTORCAT_BOSS, ACTORCAT_ENEMY, ACTORCAT_BG, ACTORCAT_EXPLOSIVE, ACTORCAT_NPC, ACTORCAT_ITEMACTION, + ACTORCAT_CHEST, ACTORCAT_SWITCH, ACTORCAT_PROP, ACTORCAT_MISC, ACTORCAT_DOOR, ACTORCAT_SWITCH, +}; + +Actor* func_80032AF0(GlobalContext* globalCtx, ActorContext* actorCtx, Actor** actorPtr, Player* player) { + s32 i; + u8* entry; + + D_8015BBE8 = D_8015BBEC = NULL; + D_8015BBF0 = sbgmEnemyDistSq = FLT_MAX; + D_8015BBF8 = 0x7FFFFFFF; + + if (!Player_InCsMode(globalCtx)) { + entry = &D_801160A0[0]; + + actorCtx->targetCtx.bgmEnemy = NULL; + D_8015BBFC = player->actor.shape.rot.y; + + for (i = 0; i < 3; i++) { + func_800328D4(globalCtx, actorCtx, player, *entry); + entry++; + } + + if (D_8015BBE8 == NULL) { + for (; i < ARRAY_COUNT(D_801160A0); i++) { + func_800328D4(globalCtx, actorCtx, player, *entry); + entry++; + } + } + } + + if (D_8015BBE8 == 0) { + *actorPtr = D_8015BBEC; + } else { + *actorPtr = D_8015BBE8; + } + + return *actorPtr; +} + +/** + * Finds the first actor instance of a specified ID and category if there is one. + */ +Actor* Actor_Find(ActorContext* actorCtx, s32 actorId, s32 actorCategory) { + Actor* actor = actorCtx->actorLists[actorCategory].head; + + while (actor != NULL) { + if (actorId == actor->id) { + return actor; + } + actor = actor->next; + } + + return NULL; +} + +/** + * Play the death sound effect and flash the screen white for 4 frames. + * While the screen flashes, the game freezes. + */ +void Enemy_StartFinishingBlow(GlobalContext* globalCtx, Actor* actor) { + globalCtx->actorCtx.freezeFlashTimer = 5; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &actor->world.pos, 20, NA_SE_EN_LAST_DAMAGE); +} + +s16 func_80032CB4(s16* arg0, s16 arg1, s16 arg2, s16 arg3) { + if (DECR(arg0[1]) == 0) { + arg0[1] = Rand_S16Offset(arg1, arg2); + } + + if ((arg0[1] - arg3) > 0) { + arg0[0] = 0; + } else if (((arg0[1] - arg3) > -2) || (arg0[1] < 2)) { + arg0[0] = 1; + } else { + arg0[0] = 2; + } + + return arg0[0]; +} + +s16 func_80032D60(s16* arg0, s16 arg1, s16 arg2, s16 arg3) { + if (DECR(arg0[1]) == 0) { + arg0[1] = Rand_S16Offset(arg1, arg2); + arg0[0]++; + + if ((arg0[0] % 3) == 0) { + arg0[0] = (s32)(Rand_ZeroOne() * arg3) * 3; + } + } + + return arg0[0]; +} + +void BodyBreak_Alloc(BodyBreak* bodyBreak, s32 count, GlobalContext* globalCtx) { + u32 matricesSize; + u32 dListsSize; + u32 objectIdsSize; + + matricesSize = (count + 1) * sizeof(*bodyBreak->matrices); + bodyBreak->matrices = ZeldaArena_MallocDebug(matricesSize, "../z_actor.c", 7540); + + if (bodyBreak->matrices != NULL) { + dListsSize = (count + 1) * sizeof(*bodyBreak->dLists); + bodyBreak->dLists = ZeldaArena_MallocDebug(dListsSize, "../z_actor.c", 7543); + + if (bodyBreak->dLists != NULL) { + objectIdsSize = (count + 1) * sizeof(*bodyBreak->objectIds); + bodyBreak->objectIds = ZeldaArena_MallocDebug(objectIdsSize, "../z_actor.c", 7546); + + if (bodyBreak->objectIds != NULL) { + memset((u8*)bodyBreak->matrices,0, matricesSize); + memset((u8*)bodyBreak->dLists, 0, dListsSize); + memset((u8*)bodyBreak->objectIds, 0, objectIdsSize); + bodyBreak->val = 1; + return; + } + } + } + + if (bodyBreak->matrices != NULL) { + ZeldaArena_FreeDebug(bodyBreak->matrices, "../z_actor.c", 7558); + } + + if (bodyBreak->dLists != NULL) { + ZeldaArena_FreeDebug(bodyBreak->dLists, "../z_actor.c", 7561); + } + + if (bodyBreak->objectIds != NULL) { + ZeldaArena_FreeDebug(bodyBreak->objectIds, "../z_actor.c", 7564); + } +} + +void BodyBreak_SetInfo(BodyBreak* bodyBreak, s32 limbIndex, s32 minLimbIndex, s32 maxLimbIndex, u32 count, Gfx** dList, + s16 objectId) { + GlobalContext* globalCtx = Effect_GetGlobalCtx(); + + if ((globalCtx->actorCtx.freezeFlashTimer == 0) && (bodyBreak->val > 0)) { + if ((limbIndex >= minLimbIndex) && (limbIndex <= maxLimbIndex) && (*dList != NULL)) { + bodyBreak->dLists[bodyBreak->val] = *dList; + Matrix_Get(&bodyBreak->matrices[bodyBreak->val]); + bodyBreak->objectIds[bodyBreak->val] = objectId; + bodyBreak->val++; + } + + if (limbIndex != bodyBreak->prevLimbIndex) { + bodyBreak->count++; + } + + if ((u32)bodyBreak->count >= count) { + bodyBreak->count = bodyBreak->val - 1; + bodyBreak->val = BODYBREAK_STATUS_READY; + } + } + + bodyBreak->prevLimbIndex = limbIndex; +} + +s32 BodyBreak_SpawnParts(Actor* actor, BodyBreak* bodyBreak, GlobalContext* globalCtx, s16 type) { + EnPart* spawnedEnPart; + MtxF* mtx; + s16 objBankIndex; + + if (bodyBreak->val != BODYBREAK_STATUS_READY) { + return false; + } + + while (bodyBreak->count > 0) { + Matrix_Put(&bodyBreak->matrices[bodyBreak->count]); + Matrix_Scale(1.0f / actor->scale.x, 1.0f / actor->scale.y, 1.0f / actor->scale.z, MTXMODE_APPLY); + Matrix_Get(&bodyBreak->matrices[bodyBreak->count]); + + if (1) { + if (bodyBreak->objectIds[bodyBreak->count] >= 0) { + objBankIndex = bodyBreak->objectIds[bodyBreak->count]; + } else { + objBankIndex = actor->objBankIndex; + } + } + + mtx = &bodyBreak->matrices[bodyBreak->count]; + + spawnedEnPart = (EnPart*)Actor_SpawnAsChild(&globalCtx->actorCtx, actor, globalCtx, ACTOR_EN_PART, mtx->xw, + mtx->yw, mtx->zw, 0, 0, objBankIndex, type); + + if (spawnedEnPart != NULL) { + Matrix_MtxFToYXZRotS(&bodyBreak->matrices[bodyBreak->count], &spawnedEnPart->actor.shape.rot, 0); + spawnedEnPart->displayList = bodyBreak->dLists[bodyBreak->count]; + spawnedEnPart->actor.scale = actor->scale; + } + + bodyBreak->count--; + } + + bodyBreak->val = BODYBREAK_STATUS_FINISHED; + + ZeldaArena_FreeDebug(bodyBreak->matrices, "../z_actor.c", 7678); + ZeldaArena_FreeDebug(bodyBreak->dLists, "../z_actor.c", 7679); + ZeldaArena_FreeDebug(bodyBreak->objectIds, "../z_actor.c", 7680); + + return true; +} + +void Actor_SpawnFloorDustRing(GlobalContext* globalCtx, Actor* actor, Vec3f* posXZ, f32 radius, s32 amountMinusOne, + f32 randAccelWeight, s16 scale, s16 scaleStep, u8 useLighting) { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + f32 angle; + s32 i; + + angle = (Rand_ZeroOne() - 0.5f) * (2.0f * 3.14f); + pos.y = actor->floorHeight; + accel.y += (Rand_ZeroOne() - 0.5f) * 0.2f; + + for (i = amountMinusOne; i >= 0; i--) { + pos.x = Math_SinF(angle) * radius + posXZ->x; + pos.z = Math_CosF(angle) * radius + posXZ->z; + accel.x = (Rand_ZeroOne() - 0.5f) * randAccelWeight; + accel.z = (Rand_ZeroOne() - 0.5f) * randAccelWeight; + + if (scale == 0) { + func_8002857C(globalCtx, &pos, &velocity, &accel); + } else { + if (useLighting) { + func_800286CC(globalCtx, &pos, &velocity, &accel, scale, scaleStep); + } else { + func_8002865C(globalCtx, &pos, &velocity, &accel, scale, scaleStep); + } + } + + angle += (2.0f * 3.14f) / (amountMinusOne + 1.0f); + } +} + +void func_80033480(GlobalContext* globalCtx, Vec3f* posBase, f32 randRangeDiameter, s32 amountMinusOne, s16 scaleBase, + s16 scaleStep, u8 arg6) { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + s16 scale; + u32 var2; + s32 i; + + for (i = amountMinusOne; i >= 0; i--) { + pos.x = posBase->x + ((Rand_ZeroOne() - 0.5f) * randRangeDiameter); + pos.y = posBase->y + ((Rand_ZeroOne() - 0.5f) * randRangeDiameter); + pos.z = posBase->z + ((Rand_ZeroOne() - 0.5f) * randRangeDiameter); + + scale = (s16)((Rand_ZeroOne() * scaleBase) * 0.2f) + scaleBase; + var2 = arg6; + + if (var2 != 0) { + func_800286CC(globalCtx, &pos, &velocity, &accel, scale, scaleStep); + } else { + func_8002865C(globalCtx, &pos, &velocity, &accel, scale, scaleStep); + } + } +} + +Actor* Actor_GetCollidedExplosive(GlobalContext* globalCtx, Collider* collider) { + if ((collider->acFlags & AC_HIT) && (collider->ac->category == ACTORCAT_EXPLOSIVE)) { + collider->acFlags &= ~AC_HIT; + return collider->ac; + } + + return NULL; +} + +Actor* func_80033684(GlobalContext* globalCtx, Actor* explosiveActor) { + Actor* actor = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + + while (actor != NULL) { + if ((actor == explosiveActor) || (actor->params != 1)) { + actor = actor->next; + } else { + if (Actor_WorldDistXYZToActor(explosiveActor, actor) <= (actor->shape.rot.z * 10) + 80.0f) { + return actor; + } else { + actor = actor->next; + } + } + } + + return NULL; +} + +/** + * Dynamically changes the category of a given actor instance. + * This is done by moving it to the corresponding category list and setting its category variable accordingly. + */ +void Actor_ChangeCategory(GlobalContext* globalCtx, ActorContext* actorCtx, Actor* actor, u8 actorCategory) { + Actor_RemoveFromCategory(globalCtx, actorCtx, actor); + Actor_AddToCategory(actorCtx, actor, actorCategory); +} + +/** + * Checks if a hookshot or arrow actor is going to collide with the cylinder denoted by the + * actor's `cylRadius` and `cylHeight`. + * The check is only peformed if the projectile actor is within the provided sphere radius. + * + * Returns the actor if there will be collision, NULL otherwise. + */ +Actor* Actor_GetProjectileActor(GlobalContext* globalCtx, Actor* refActor, f32 radius) { + Actor* actor; + Vec3f spA8; + f32 deltaX; + f32 deltaY; + f32 deltaZ; + Vec3f sp90; + Vec3f sp84; + + actor = globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + while (actor != NULL) { + if (((actor->id != ACTOR_ARMS_HOOK) && (actor->id != ACTOR_EN_ARROW)) || (actor == refActor)) { + actor = actor->next; + } else { + //! @bug The projectile actor gets unsafely casted to a hookshot to check its timer, even though + // it can also be an arrow. + // Luckily, the field at the same offset in the arrow actor is the x component of a vector + // which will rarely ever be 0. So it's very unlikely for this bug to cause an issue. + if ((Math_Vec3f_DistXYZ(&refActor->world.pos, &actor->world.pos) > radius) || + (((ArmsHook*)actor)->timer == 0)) { + actor = actor->next; + } else { + deltaX = Math_SinS(actor->world.rot.y) * (actor->speedXZ * 10.0f); + deltaY = actor->velocity.y + (actor->gravity * 10.0f); + deltaZ = Math_CosS(actor->world.rot.y) * (actor->speedXZ * 10.0f); + + spA8.x = actor->world.pos.x + deltaX; + spA8.y = actor->world.pos.y + deltaY; + spA8.z = actor->world.pos.z + deltaZ; + + if (CollisionCheck_CylSideVsLineSeg(refActor->colChkInfo.cylRadius, refActor->colChkInfo.cylHeight, + 0.0f, &refActor->world.pos, &actor->world.pos, &spA8, &sp90, + &sp84)) { + return actor; + } else { + actor = actor->next; + } + } + } + } + + return NULL; +} + +/** + * Sets the actor's text id with a dynamic prefix based on the current scene. + */ +void Actor_SetTextWithPrefix(GlobalContext* globalCtx, Actor* actor, s16 baseTextId) { + s16 prefix; + + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_YDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_KOKIRI_HOME: + case SCENE_KOKIRI_HOME3: + case SCENE_KOKIRI_HOME4: + case SCENE_KOKIRI_HOME5: + case SCENE_KOKIRI_SHOP: + case SCENE_LINK_HOME: + case SCENE_SPOT04: + case SCENE_SPOT05: + case SCENE_SPOT10: + case 112: + prefix = 0x1000; + break; + case SCENE_MALON_STABLE: + case SCENE_SPOT00: + case SCENE_SPOT20: + prefix = 0x2000; + break; + case SCENE_HIDAN: + case SCENE_DDAN_BOSS: + case SCENE_FIRE_BS: + case SCENE_SPOT16: + case SCENE_SPOT17: + case SCENE_SPOT18: + prefix = 0x3000; + break; + case SCENE_BDAN: + case SCENE_BDAN_BOSS: + case SCENE_SPOT03: + case SCENE_SPOT07: + case SCENE_SPOT08: + prefix = 0x4000; + break; + case SCENE_HAKADAN: + case SCENE_HAKADAN_BS: + case SCENE_KAKARIKO: + case SCENE_KAKARIKO3: + case SCENE_IMPA: + case SCENE_HUT: + case SCENE_HAKAANA: + case SCENE_HAKASITARELAY: + case SCENE_SPOT01: + case SCENE_SPOT02: + prefix = 0x5000; + break; + case SCENE_JYASINZOU: + case SCENE_JYASINBOSS: + case SCENE_LABO: + case SCENE_TENT: + case SCENE_SPOT06: + case SCENE_SPOT09: + case SCENE_SPOT11: + prefix = 0x6000; + break; + case SCENE_ENTRA: + case SCENE_MARKET_ALLEY: + case SCENE_MARKET_ALLEY_N: + case SCENE_MARKET_DAY: + case SCENE_MARKET_NIGHT: + case SCENE_MARKET_RUINS: + case SCENE_SPOT15: + prefix = 0x7000; + break; + default: + prefix = 0x0000; + break; + } + + actor->textId = prefix | baseTextId; +} + +/** + * Checks if a given actor will be standing on the ground after being translated + * by the provided distance and angle. + * + * Returns true if the actor will be standing on ground. + */ +s16 Actor_TestFloorInDirection(Actor* actor, GlobalContext* globalCtx, f32 distance, s16 angle) { + s16 ret; + s16 prevBgCheckFlags; + f32 dx; + f32 dz; + Vec3f prevActorPos; + + Math_Vec3f_Copy(&prevActorPos, &actor->world.pos); + prevBgCheckFlags = actor->bgCheckFlags; + + dx = Math_SinS(angle) * distance; + dz = Math_CosS(angle) * distance; + actor->world.pos.x += dx; + actor->world.pos.z += dz; + + Actor_UpdateBgCheckInfo(globalCtx, actor, 0.0f, 0.0f, 0.0f, 4); + + Math_Vec3f_Copy(&actor->world.pos, &prevActorPos); + + ret = actor->bgCheckFlags & 1; + actor->bgCheckFlags = prevBgCheckFlags; + + return ret; +} + +/** + * Returns true if the player is targeting the provided actor + */ +s32 Actor_IsTargeted(GlobalContext* globalCtx, Actor* actor) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags1 & 0x10) && actor->isTargeted) { + return true; + } else { + return false; + } +} + +/** + * Returns true if the player is targeting an actor other than the provided actor + */ +s32 Actor_OtherIsTargeted(GlobalContext* globalCtx, Actor* actor) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags1 & 0x10) && !actor->isTargeted) { + return true; + } else { + return false; + } +} + +f32 func_80033AEC(Vec3f* arg0, Vec3f* arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5) { + f32 ret = 0.0f; + + if (arg4 <= Math_Vec3f_DistXYZ(arg0, arg1)) { + ret = Math_SmoothStepToF(&arg1->x, arg0->x, arg2, arg3, 0.0f); + ret += Math_SmoothStepToF(&arg1->y, arg0->y, arg2, arg3, 0.0f); + ret += Math_SmoothStepToF(&arg1->z, arg0->z, arg2, arg3, 0.0f); + } else if (arg5 < Math_Vec3f_DistXYZ(arg0, arg1)) { + ret = Math_SmoothStepToF(&arg1->x, arg0->x, arg2, arg3, 0.0f); + ret += Math_SmoothStepToF(&arg1->y, arg0->y, arg2, arg3, 0.0f); + ret += Math_SmoothStepToF(&arg1->z, arg0->z, arg2, arg3, 0.0f); + } + + return ret; +} + +void func_80033C30(Vec3f* arg0, Vec3f* arg1, u8 alpha, GlobalContext* globalCtx) { + MtxF sp60; + f32 var; + Vec3f sp50; + CollisionPoly* sp4C; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8120); + + if (0) {} // Necessary to match + + POLY_OPA_DISP = Gfx_CallSetupDL(POLY_OPA_DISP, 0x2C); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, alpha); + + sp50.x = arg0->x; + sp50.y = arg0->y + 1.0f; + sp50.z = arg0->z; + + var = BgCheck_EntityRaycastFloor2(globalCtx, &globalCtx->colCtx, &sp4C, &sp50); + + if (sp4C != NULL) { + func_80038A28(sp4C, arg0->x, var, arg0->z, &sp60); + Matrix_Put(&sp60); + } else { + Matrix_Translate(arg0->x, arg0->y, arg0->z, MTXMODE_NEW); + } + + Matrix_Scale(arg1->x, 1.0f, arg1->z, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 8149), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gCircleShadowDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8155); +} + +void func_80033DB8(GlobalContext* globalCtx, s16 arg1, s16 arg2) { + s16 var = Quake_Add(&globalCtx->mainCamera, 3); + + Quake_SetSpeed(var, 20000); + Quake_SetQuakeValues(var, arg1, 0, 0, 0); + Quake_SetCountdown(var, arg2); +} + +void func_80033E1C(GlobalContext* globalCtx, s16 arg1, s16 arg2, s16 arg3) { + s16 var = Quake_Add(&globalCtx->mainCamera, 3); + + Quake_SetSpeed(var, arg3); + Quake_SetQuakeValues(var, arg1, 0, 0, 0); + Quake_SetCountdown(var, arg2); +} + +void func_80033E88(Actor* actor, GlobalContext* globalCtx, s16 arg2, s16 arg3) { + if (arg2 >= 5) { + func_800AA000(actor->xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + } else { + func_800AA000(actor->xyzDistToPlayerSq, 0xB4, 0x14, 0x64); + } + + func_80033DB8(globalCtx, arg2, arg3); +} + +f32 Rand_ZeroFloat(f32 f) { + return Rand_ZeroOne() * f; +} + +f32 Rand_CenteredFloat(f32 f) { + return (Rand_ZeroOne() - 0.5f) * f; +} + +typedef struct { + /* 0x00 */ f32 chainAngle; + /* 0x04 */ f32 chainLength; + /* 0x08 */ f32 yShift; + /* 0x0C */ f32 chainsScale; + /* 0x10 */ f32 chainsRotZInit; + /* 0x14 */ Gfx* chainDL; + /* 0x18 */ Gfx* lockDL; +} DoorLockInfo; // size = 0x1C + +static DoorLockInfo sDoorLocksInfo[] = { + /* DOORLOCK_NORMAL */ { 0.54f, 6000.0f, 5000.0f, 1.0f, 0.0f, gDoorChainsDL, gDoorLockDL }, + /* DOORLOCK_BOSS */ { 0.644f, 12000.0f, 8000.0f, 1.0f, 0.0f, object_bdoor_DL_001530, object_bdoor_DL_001400 }, + /* DOORLOCK_NORMAL_SPIRIT */ { 0.64000005f, 8500.0f, 8000.0f, 1.75f, 0.1f, gDoorChainsDL, gDoorLockDL }, +}; + +/** + * Draws chains and lock of a locked door, of the specified `type` (see `DoorLockType`). + * `frame` can be 0 to 10, where 0 is "open" and 10 is "closed", the chains slide accordingly. + */ +void Actor_DrawDoorLock(GlobalContext* globalCtx, s32 frame, s32 type) { + DoorLockInfo* entry; + s32 i; + MtxF baseMtxF; + f32 chainRotZ; + f32 chainsTranslateX; + f32 chainsTranslateY; + f32 rotZStep; + + entry = &sDoorLocksInfo[type]; + chainRotZ = entry->chainsRotZInit; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8265); + + Matrix_Translate(0.0f, entry->yShift, 500.0f, MTXMODE_APPLY); + Matrix_Get(&baseMtxF); + + chainsTranslateX = sinf(entry->chainAngle - chainRotZ) * -(10 - frame) * 0.1f * entry->chainLength; + chainsTranslateY = cosf(entry->chainAngle - chainRotZ) * (10 - frame) * 0.1f * entry->chainLength; + + for (i = 0; i < 4; i++) { + Matrix_Put(&baseMtxF); + Matrix_RotateZ(chainRotZ, MTXMODE_APPLY); + Matrix_Translate(chainsTranslateX, chainsTranslateY, 0.0f, MTXMODE_APPLY); + + if (entry->chainsScale != 1.0f) { + Matrix_Scale(entry->chainsScale, entry->chainsScale, entry->chainsScale, MTXMODE_APPLY); + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 8299), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, entry->chainDL); + + if (i % 2) { + rotZStep = 2.0f * entry->chainAngle; + } else { + rotZStep = M_PI - (2.0f * entry->chainAngle); + } + + chainRotZ += rotZStep; + } + + Matrix_Put(&baseMtxF); + Matrix_Scale(frame * 0.1f, frame * 0.1f, frame * 0.1f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_actor.c", 8314), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, entry->lockDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8319); +} + +void func_8003424C(GlobalContext* globalCtx, Vec3f* arg1) { + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, arg1); +} + +void Actor_SetColorFilter(Actor* actor, s16 colorFlag, s16 colorIntensityMax, s16 xluFlag, s16 duration) { + if ((colorFlag == 0x8000) && !(colorIntensityMax & 0x8000)) { + Audio_PlayActorSound2(actor, NA_SE_EN_LIGHT_ARROW_HIT); + } + + actor->colorFilterParams = colorFlag | xluFlag | ((colorIntensityMax & 0xF8) << 5) | duration; + actor->colorFilterTimer = duration; +} + +Hilite* func_800342EC(Vec3f* object, GlobalContext* globalCtx) { + Vec3f lightDir; + + lightDir.x = globalCtx->envCtx.dirLight1.params.dir.x; + lightDir.y = globalCtx->envCtx.dirLight1.params.dir.y; + lightDir.z = globalCtx->envCtx.dirLight1.params.dir.z; + + return func_8002EABC(object, &globalCtx->view.eye, &lightDir, globalCtx->state.gfxCtx); +} + +Hilite* func_8003435C(Vec3f* object, GlobalContext* globalCtx) { + Vec3f lightDir; + + lightDir.x = globalCtx->envCtx.dirLight1.params.dir.x; + lightDir.y = globalCtx->envCtx.dirLight1.params.dir.y; + lightDir.z = globalCtx->envCtx.dirLight1.params.dir.z; + + return func_8002EB44(object, &globalCtx->view.eye, &lightDir, globalCtx->state.gfxCtx); +} + +s32 func_800343CC(GlobalContext* globalCtx, Actor* actor, s16* arg2, f32 interactRange, callback1_800343CC unkFunc1, + callback2_800343CC unkFunc2) { + s16 x; + s16 y; + + if (Actor_ProcessTalkRequest(actor, globalCtx)) { + *arg2 = 1; + return true; + } + + if (*arg2 != 0) { + *arg2 = unkFunc2(globalCtx, actor); + return false; + } + + Actor_GetScreenPos(globalCtx, actor, &x, &y); + + if ((x < 0) || (x > SCREEN_WIDTH) || (y < 0) || (y > SCREEN_HEIGHT)) { + return false; + } + + if (!func_8002F2CC(actor, globalCtx, interactRange)) { + return false; + } + + actor->textId = unkFunc1(globalCtx, actor); + + return false; +} + +typedef struct { + /* 0x00 */ s16 unk_00; + /* 0x02 */ s16 unk_02; + /* 0x04 */ s16 unk_04; + /* 0x06 */ s16 unk_06; + /* 0x08 */ s16 unk_08; + /* 0x0A */ s16 unk_0A; + /* 0x0C */ u8 unk_0C; +} struct_80116130_0; // size = 0x10 + +typedef struct { + /* 0x00 */ struct_80116130_0 sub_00; + /* 0x10 */ f32 unk_10; + /* 0x14 */ s16 unk_14; +} struct_80116130; // size = 0x18 + +static struct_80116130 D_80116130[] = { + { { 0x2AA8, 0xF1C8, 0x18E2, 0x1554, 0x0000, 0x0000, 1 }, 170.0f, 0x3FFC }, + { { 0x2AA8, 0xEAAC, 0x1554, 0x1554, 0xF8E4, 0x0E38, 1 }, 170.0f, 0x3FFC }, + { { 0x31C4, 0xE390, 0x0E38, 0x0E38, 0xF1C8, 0x071C, 1 }, 170.0f, 0x3FFC }, + { { 0x1554, 0xF1C8, 0x0000, 0x071C, 0xF8E4, 0x0000, 1 }, 170.0f, 0x3FFC }, + { { 0x2AA8, 0xF8E4, 0x071C, 0x0E38, 0xD558, 0x2AA8, 1 }, 170.0f, 0x3FFC }, + { { 0x0000, 0xE390, 0x2AA8, 0x3FFC, 0xF1C8, 0x0E38, 1 }, 170.0f, 0x3FFC }, + { { 0x2AA8, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 }, + { { 0x2AA8, 0xF1C8, 0x0000, 0x0E38, 0x0000, 0x1C70, 1 }, 0.0f, 0x0000 }, + { { 0x2AA8, 0xF1C8, 0xF1C8, 0x0000, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 }, + { { 0x071C, 0xF1C8, 0x0E38, 0x1C70, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 }, + { { 0x0E38, 0xF1C8, 0x0000, 0x1C70, 0x0000, 0x0E38, 1 }, 0.0f, 0x0000 }, + { { 0x2AA8, 0xE390, 0x1C70, 0x0E38, 0xF1C8, 0x0E38, 1 }, 0.0f, 0x0000 }, + { { 0x18E2, 0xF1C8, 0x0E38, 0x0E38, 0x0000, 0x0000, 1 }, 0.0f, 0x0000 }, +}; + +void func_800344BC(Actor* actor, struct_80034A14_arg1* arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5, s16 arg6, s16 arg7, + u8 arg8) { + s16 sp46; + s16 sp44; + s16 temp2; + s16 sp40; + s16 temp1; + Vec3f sp30; + + sp30.x = actor->world.pos.x; + sp30.y = actor->world.pos.y + arg1->unk_14; + sp30.z = actor->world.pos.z; + + sp46 = Math_Vec3f_Pitch(&sp30, &arg1->unk_18); + sp44 = Math_Vec3f_Yaw(&sp30, &arg1->unk_18); + sp40 = Math_Vec3f_Yaw(&actor->world.pos, &arg1->unk_18) - actor->shape.rot.y; + + temp1 = CLAMP(sp40, -arg2, arg2); + Math_SmoothStepToS(&arg1->unk_08.y, temp1, 6, 2000, 1); + + temp1 = (ABS(sp40) >= 0x8000) ? 0 : ABS(sp40); + arg1->unk_08.y = CLAMP(arg1->unk_08.y, -temp1, temp1); + + sp40 -= arg1->unk_08.y; + + temp1 = CLAMP(sp40, -arg5, arg5); + Math_SmoothStepToS(&arg1->unk_0E.y, temp1, 6, 2000, 1); + + temp1 = (ABS(sp40) >= 0x8000) ? 0 : ABS(sp40); + arg1->unk_0E.y = CLAMP(arg1->unk_0E.y, -temp1, temp1); + + if (arg8) { + Math_SmoothStepToS(&actor->shape.rot.y, sp44, 6, 2000, 1); + } + + temp1 = CLAMP(sp46, arg4, (s16)(u16)arg3); + Math_SmoothStepToS(&arg1->unk_08.x, temp1, 6, 2000, 1); + + temp2 = sp46 - arg1->unk_08.x; + + temp1 = CLAMP(temp2, arg7, arg6); + Math_SmoothStepToS(&arg1->unk_0E.x, temp1, 6, 2000, 1); +} + +s16 func_800347E8(s16 arg0) { + return D_80116130[arg0].unk_14; +} + +s16 func_80034810(Actor* actor, struct_80034A14_arg1* arg1, f32 arg2, s16 arg3, s16 arg4) { + s32 pad; + s16 var; + s16 abs_var; + + if (arg4 != 0) { + return arg4; + } + + if (arg1->unk_00 != 0) { + return 4; + } + + if (arg2 < Math_Vec3f_DistXYZ(&actor->world.pos, &arg1->unk_18)) { + arg1->unk_04 = 0; + arg1->unk_06 = 0; + return 1; + } + + var = Math_Vec3f_Yaw(&actor->world.pos, &arg1->unk_18); + abs_var = ABS((s16)((f32)var - actor->shape.rot.y)); + if (arg3 >= abs_var) { + arg1->unk_04 = 0; + arg1->unk_06 = 0; + return 2; + } + + if (DECR(arg1->unk_04) != 0) { + return arg1->unk_02; + } + + switch (arg1->unk_06) { + case 0: + case 2: + arg1->unk_04 = Rand_S16Offset(30, 30); + arg1->unk_06++; + return 1; + case 1: + arg1->unk_04 = Rand_S16Offset(10, 10); + arg1->unk_06++; + return 3; + } + + return 4; +} + +void func_80034A14(Actor* actor, struct_80034A14_arg1* arg1, s16 arg2, s16 arg3) { + struct_80116130_0 sp38; + + arg1->unk_02 = func_80034810(actor, arg1, D_80116130[arg2].unk_10, D_80116130[arg2].unk_14, arg3); + + sp38 = D_80116130[arg2].sub_00; + + switch (arg1->unk_02) { + case 1: + sp38.unk_00 = 0; + sp38.unk_04 = 0; + sp38.unk_02 = 0; + case 3: + sp38.unk_06 = 0; + sp38.unk_0A = 0; + sp38.unk_08 = 0; + case 2: + sp38.unk_0C = 0; + } + + func_800344BC(actor, arg1, sp38.unk_00, sp38.unk_04, sp38.unk_02, sp38.unk_06, sp38.unk_0A, sp38.unk_08, + sp38.unk_0C); +} + +Gfx* func_80034B28(GraphicsContext* gfxCtx) { + Gfx* displayList; + + displayList = Graph_Alloc(gfxCtx, sizeof(Gfx)); + gSPEndDisplayList(displayList); + + return displayList; +} + +Gfx* func_80034B54(GraphicsContext* gfxCtx) { + Gfx* displayListHead; + Gfx* displayList; + + displayList = displayListHead = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + + gDPSetRenderMode(displayListHead++, G_RM_FOG_SHADE_A, + AA_EN | Z_CMP | Z_UPD | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | ZMODE_XLU | FORCE_BL | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)); + + gSPEndDisplayList(displayListHead++); + + return displayList; +} + +void func_80034BA0(GlobalContext* globalCtx, SkelAnime* skelAnime, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, Actor* actor, s16 alpha) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8831); + + func_80093D18(globalCtx->state.gfxCtx); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, alpha); + gDPPipeSync(POLY_OPA_DISP++); + gSPSegment(POLY_OPA_DISP++, 0x0C, func_80034B28(globalCtx->state.gfxCtx)); + + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + overrideLimbDraw, postLimbDraw, actor, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8860); +} + +void func_80034CC4(GlobalContext* globalCtx, SkelAnime* skelAnime, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, Actor* actor, s16 alpha) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8876); + + func_80093D84(globalCtx->state.gfxCtx); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, func_80034B54(globalCtx->state.gfxCtx)); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + overrideLimbDraw, postLimbDraw, actor, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_actor.c", 8904); +} + +s16 func_80034DD4(Actor* actor, GlobalContext* globalCtx, s16 arg2, f32 arg3) { + Player* player = GET_PLAYER(globalCtx); + f32 var; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) || (gDbgCamEnabled)) { + var = Math_Vec3f_DistXYZ(&actor->world.pos, &globalCtx->view.eye) * 0.25f; + } else { + var = Math_Vec3f_DistXYZ(&actor->world.pos, &player->actor.world.pos); + } + + if (arg3 < var) { + actor->flags &= ~ACTOR_FLAG_0; + Math_SmoothStepToS(&arg2, 0, 6, 0x14, 1); + } else { + actor->flags |= ACTOR_FLAG_0; + Math_SmoothStepToS(&arg2, 0xFF, 6, 0x14, 1); + } + + return arg2; +} + +void Animation_ChangeByInfo(SkelAnime* skelAnime, AnimationInfo* animationInfo, s32 index) { + f32 frameCount; + + animationInfo += index; + + if (animationInfo->frameCount > 0.0f) { + frameCount = animationInfo->frameCount; + } else { + frameCount = Animation_GetLastFrame(animationInfo->animation); + } + + Animation_Change(skelAnime, animationInfo->animation, animationInfo->playSpeed, animationInfo->startFrame, + frameCount, animationInfo->mode, animationInfo->morphFrames); +} + +void func_80034F54(GlobalContext* globalCtx, s16* arg1, s16* arg2, s32 arg3) { + u32 frames = globalCtx->gameplayFrames; + s32 i; + + for (i = 0; i < arg3; i++) { + arg1[i] = (0x814 + 50 * i) * frames; + arg2[i] = (0x940 + 50 * i) * frames; + } +} + +void Actor_Noop(Actor* actor, GlobalContext* globalCtx) { +} + +s32 func_80035124(Actor* actor, GlobalContext* globalCtx) { + s32 ret = 0; + + switch (actor->params) { + case 0: + if (Actor_HasParent(actor, globalCtx)) { + actor->params = 1; + } else if (!(actor->bgCheckFlags & 1)) { + Actor_MoveForward(actor); + Math_SmoothStepToF(&actor->speedXZ, 0.0f, 1.0f, 0.1f, 0.0f); + } else if ((actor->bgCheckFlags & 2) && (actor->velocity.y < -4.0f)) { + ret = 1; + } else { + actor->shape.rot.x = actor->shape.rot.z = 0; + func_8002F580(actor, globalCtx); + } + break; + case 1: + if (Actor_HasNoParent(actor, globalCtx)) { + actor->params = 0; + } + break; + } + + Actor_UpdateBgCheckInfo(globalCtx, actor, actor->colChkInfo.cylHeight, actor->colChkInfo.cylRadius, + actor->colChkInfo.cylRadius, 0x1D); + + return ret; +} + +#include "z_cheap_proc.c" + +u8 func_800353E8(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + return player->unk_845; +} + +/** + * Finds the first actor instance of a specified ID and category within a given range from + * an actor if there is one. If the ID provided is -1, this will look for any actor of the + * specified category rather than a specific ID. + */ +Actor* Actor_FindNearby(GlobalContext* globalCtx, Actor* refActor, s16 actorId, u8 actorCategory, f32 range) { + Actor* actor = globalCtx->actorCtx.actorLists[actorCategory].head; + + while (actor != NULL) { + if (actor == refActor || ((actorId != -1) && (actorId != actor->id))) { + actor = actor->next; + } else { + if (Actor_WorldDistXYZToActor(refActor, actor) <= range) { + return actor; + } else { + actor = actor->next; + } + } + } + + return NULL; +} + +s32 func_800354B4(GlobalContext* globalCtx, Actor* actor, f32 range, s16 arg3, s16 arg4, s16 arg5) { + Player* player = GET_PLAYER(globalCtx); + s16 var1; + s16 var2; + + var1 = (s16)(actor->yawTowardsPlayer + 0x8000) - player->actor.shape.rot.y; + var2 = actor->yawTowardsPlayer - arg5; + + if ((actor->xzDistToPlayer <= range) && (player->swordState != 0) && (arg4 >= ABS(var1)) && (arg3 >= ABS(var2))) { + return true; + } else { + return false; + } +} + +void func_8003555C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + Color_RGBA8 color1; + Color_RGBA8 color2; + + color1.r = 200; + color1.g = 160; + color1.b = 120; + + color2.r = 130; + color2.g = 90; + color2.b = 50; + + //! @bug color1 and color2 alpha components not set before being passed on + EffectSsKiraKira_SpawnSmall(globalCtx, pos, velocity, accel, &color1, &color2); +} + +Vec3f D_80116268 = { 0.0f, -1.5f, 0.0f }; +Vec3f D_80116274 = { 0.0f, -0.2f, 0.0f }; + +Gfx D_80116280[] = { + gsDPSetRenderMode(G_RM_FOG_SHADE_A, AA_EN | Z_CMP | Z_UPD | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | ZMODE_XLU | + FORCE_BL | GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)), + gsDPSetAlphaCompare(G_AC_THRESHOLD), + gsSPEndDisplayList(), +}; + +void func_800355B8(GlobalContext* globalCtx, Vec3f* pos) { + func_8003555C(globalCtx, pos, &D_80116268, &D_80116274); +} + +u8 func_800355E4(GlobalContext* globalCtx, Collider* collider) { + Player* player = GET_PLAYER(globalCtx); + + if ((collider->acFlags & AC_TYPE_PLAYER) && (player->swordState != 0) && (player->swordAnimation == 0x16)) { + return true; + } else { + return false; + } +} + +u8 Actor_ApplyDamage(Actor* actor) { + if (actor->colChkInfo.damage >= actor->colChkInfo.health) { + actor->colChkInfo.health = 0; + } else { + actor->colChkInfo.health -= actor->colChkInfo.damage; + } + + return actor->colChkInfo.health; +} + +void Actor_SetDropFlag(Actor* actor, ColliderInfo* colInfo, s32 freezeFlag) { + if (colInfo->acHitInfo == NULL) { + actor->dropFlag = 0x00; + } else if (freezeFlag && (colInfo->acHitInfo->toucher.dmgFlags & 0x10060000)) { + actor->freezeTimer = colInfo->acHitInfo->toucher.damage; + actor->dropFlag = 0x00; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x0800) { + actor->dropFlag = 0x01; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x1000) { + actor->dropFlag = 0x02; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x4000) { + actor->dropFlag = 0x04; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x8000) { + actor->dropFlag = 0x08; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x10000) { + actor->dropFlag = 0x10; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x2000) { + actor->dropFlag = 0x20; + } else if (colInfo->acHitInfo->toucher.dmgFlags & 0x80000) { + if (freezeFlag) { + actor->freezeTimer = colInfo->acHitInfo->toucher.damage; + } + actor->dropFlag = 0x40; + } else { + actor->dropFlag = 0x00; + } +} + +void Actor_SetDropFlagJntSph(Actor* actor, ColliderJntSph* jntSph, s32 freezeFlag) { + ColliderInfo* curColInfo; + s32 flag; + s32 i; + + actor->dropFlag = 0x00; + + for (i = jntSph->count - 1; i >= 0; i--) { + curColInfo = &jntSph->elements[i].info; + if (curColInfo->acHitInfo == NULL) { + flag = 0x00; + } else if (freezeFlag && (curColInfo->acHitInfo->toucher.dmgFlags & 0x10060000)) { + actor->freezeTimer = curColInfo->acHitInfo->toucher.damage; + flag = 0x00; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x0800) { + flag = 0x01; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x1000) { + flag = 0x02; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x4000) { + flag = 0x04; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x8000) { + flag = 0x08; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x10000) { + flag = 0x10; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x2000) { + flag = 0x20; + } else if (curColInfo->acHitInfo->toucher.dmgFlags & 0x80000) { + if (freezeFlag) { + actor->freezeTimer = curColInfo->acHitInfo->toucher.damage; + } + flag = 0x40; + } else { + flag = 0x00; + } + actor->dropFlag |= flag; + } +} + +void func_80035844(Vec3f* arg0, Vec3f* arg1, Vec3s* arg2, s32 arg3) { + f32 dx = arg1->x - arg0->x; + f32 dz = arg1->z - arg0->z; + f32 dy = arg3 ? (arg1->y - arg0->y) : (arg0->y - arg1->y); + + arg2->y = Math_Atan2S(dz, dx); + arg2->x = Math_Atan2S(sqrtf(SQ(dx) + SQ(dz)), dy); +} + +/** + * Spawns En_Part (Dissipating Flames) actor as a child of the given actor. + */ +Actor* func_800358DC(Actor* actor, Vec3f* spawnPos, Vec3s* spawnRot, f32* arg3, s32 timer, s16* unused, + GlobalContext* globalCtx, s16 params, s32 arg8) { + EnPart* spawnedEnPart; + + spawnedEnPart = + (EnPart*)Actor_SpawnAsChild(&globalCtx->actorCtx, actor, globalCtx, ACTOR_EN_PART, spawnPos->x, spawnPos->y, + spawnPos->z, spawnRot->x, spawnRot->y, actor->objBankIndex, params); + if (spawnedEnPart != NULL) { + spawnedEnPart->actor.scale = actor->scale; + spawnedEnPart->actor.speedXZ = arg3[0]; + spawnedEnPart->displayList = arg8; + spawnedEnPart->action = 2; + spawnedEnPart->timer = timer; + spawnedEnPart->rotZ = arg3[1]; + spawnedEnPart->rotZSpeed = arg3[2]; + return &spawnedEnPart->actor; + } + + return NULL; +} + +void func_800359B8(Actor* actor, s16 arg1, Vec3s* arg2) { + f32 sp44; + f32 sp40; + f32 sp3C; + f32 sp38; + f32 sp34; + f32 sp30; + f32 sp2C; + f32 sp28; + f32 sp24; + CollisionPoly* floorPoly; + s32 pad; + + if (actor->floorPoly != NULL) { + floorPoly = actor->floorPoly; + sp44 = COLPOLY_GET_NORMAL(floorPoly->normal.x); + sp40 = COLPOLY_GET_NORMAL(floorPoly->normal.y); + sp3C = COLPOLY_GET_NORMAL(floorPoly->normal.z); + + sp38 = Math_SinS(arg1); + sp34 = Math_CosS(arg1); + sp28 = (-(sp44 * sp38) - (sp3C * sp34)); + arg2->x = -(s16)(Math_FAtan2F(sp28 * sp40, 1.0f) * (32768 / M_PI)); + + sp2C = Math_SinS(arg1 - 16375); + sp30 = Math_CosS(arg1 - 16375); + sp24 = (-(sp44 * sp2C) - (sp3C * sp30)); + arg2->z = -(s16)(Math_FAtan2F(sp24 * sp40, 1.0f) * (32768 / M_PI)); + } +} + +void func_80035B18(GlobalContext* globalCtx, Actor* actor, u16 textId) { + Message_ContinueTextbox(globalCtx, textId); + actor->textId = textId; +} + +/** + * Tests if event_chk_inf flag is set. + */ +s32 Flags_GetEventChkInf(s32 flag) { + return gSaveContext.eventChkInf[flag >> 4] & (1 << (flag & 0xF)); +} + +/** + * Sets event_chk_inf flag. + */ +void Flags_SetEventChkInf(s32 flag) { + gSaveContext.eventChkInf[flag >> 4] |= (1 << (flag & 0xF)); +} + +/** + * Tests if "inf_table flag is set. + */ +s32 Flags_GetInfTable(s32 flag) { + return gSaveContext.infTable[flag >> 4] & (1 << (flag & 0xF)); +} + +/** + * Sets "inf_table" flag. + */ +void Flags_SetInfTable(s32 flag) { + gSaveContext.infTable[flag >> 4] |= (1 << (flag & 0xF)); +} + +u32 func_80035BFC(GlobalContext* globalCtx, s16 arg1) { + u16 retTextId = 0; + + switch (arg1) { + case 0: + if (Flags_GetEventChkInf(0x9)) { + if (Flags_GetInfTable(0x5)) { + retTextId = 0x1048; + } else { + retTextId = 0x1047; + } + } else { + if (Flags_GetEventChkInf(0x2)) { + if (Flags_GetInfTable(0x3)) { + retTextId = 0x1032; + } else { + retTextId = 0x1031; + } + } else { + if (Flags_GetInfTable(0x0)) { + if (Flags_GetInfTable(0x1)) { + retTextId = 0x1003; + } else { + retTextId = 0x1002; + } + } else { + retTextId = 0x1001; + } + } + } + break; + case 1: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + if (Flags_GetInfTable(0x10)) { + retTextId = 0x1046; + } else { + retTextId = 0x1045; + } + } else { + if (Flags_GetEventChkInf(0x3)) { + if (Flags_GetInfTable(0xE)) { + retTextId = 0x1034; + } else { + retTextId = 0x1033; + } + } else { + if (Flags_GetInfTable(0xC)) { + retTextId = 0x1030; + } else { + retTextId = 0x102F; + } + } + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + if (Flags_GetInfTable(0x19)) { + retTextId = 0x1071; + } else { + retTextId = 0x1070; + } + } else { + if (Flags_GetEventChkInf(0xB)) { + if (Flags_GetInfTable(0x17)) { + retTextId = 0x1068; + } else { + retTextId = 0x1067; + } + } else { + if (Flags_GetInfTable(0x15)) { + retTextId = 0x1061; + } else { + retTextId = 0x1060; + } + } + } + } + break; + case 2: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1042; + } else { + retTextId = 0x1004; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1072; + } else if (Flags_GetInfTable(0x41)) { + retTextId = 0x1055; + } else { + retTextId = 0x1056; + } + } + break; + case 3: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1043; + } else { + if (Flags_GetInfTable(0x1E)) { + retTextId = 0x1006; + } else { + retTextId = 0x1005; + } + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1073; + } else { + retTextId = 0x105A; + } + } + break; + case 4: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1042; + } else { + retTextId = 0x1007; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1072; + } else if (Flags_GetInfTable(0x47)) { + retTextId = 0x105E; + } else { + retTextId = 0x105D; + } + } + break; + case 5: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1044; + } else if (Flags_GetInfTable(0x22)) { + retTextId = 0x1009; + } else { + retTextId = 0x1008; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1075; + } else { + retTextId = 0x105B; + } + } + break; + case 6: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1042; + } else if (Flags_GetInfTable(0x24)) { + retTextId = 0x100B; + } else { + retTextId = 0x100A; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1056; + } else { + retTextId = 0x105F; + } + } + break; + case 7: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1043; + } else if (Flags_GetInfTable(0x26)) { + retTextId = 0x100D; + } else { + retTextId = 0x100C; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1057; + } else { + retTextId = 0x1057; + } + } + break; + case 8: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1043; + } else if (Flags_GetInfTable(0x28)) { + retTextId = 0x1019; + } else { + retTextId = 0x100E; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1077; + } else if (Flags_GetInfTable(0x51)) { + retTextId = 0x1058; + } else { + retTextId = 0x1059; + } + } + break; + case 9: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x1049; + } else { + retTextId = 0x1035; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1079; + } else { + retTextId = 0x104E; + } + } + break; + case 10: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x104A; + } else { + retTextId = 0x1038; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1079; + } else if (Flags_GetInfTable(0x59)) { + retTextId = 0x1050; + } else { + retTextId = 0x104F; + } + } + break; + case 11: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x104B; + } else { + retTextId = 0x103C; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x107B; + } else { + retTextId = 0x1051; + } + } + break; + case 12: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x104C; + } else { + retTextId = 0x103D; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x107C; + } else { + retTextId = 0x1052; + } + } + break; + case 13: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x9)) { + retTextId = 0x104D; + } else { + retTextId = 0x103E; + } + } else { + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x106E; + } else if (Flags_GetInfTable(0x61)) { + retTextId = 0x1053; + } else { + retTextId = 0x1054; + } + } + break; + case 15: + if (Flags_GetEventChkInf(0x5C)) { + retTextId = 0x1078; + } else if (Flags_GetInfTable(0x66)) { + retTextId = 0x1066; + } else { + retTextId = 0x1062; + } + break; + case 16: + if (globalCtx->sceneNum == SCENE_SPOT15) { + retTextId = 0x7002; + } else if (Flags_GetInfTable(0x6A)) { + retTextId = 0x7004; + } else if ((gSaveContext.dayTime >= 0x4000) && (gSaveContext.dayTime < 0xC556)) { + retTextId = 0x7002; + } else { + retTextId = 0x7003; + } + break; + case 17: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + if (Flags_GetInfTable(0x6C)) { + retTextId = 0x7008; + } else { + retTextId = 0x7007; + } + } else { + retTextId = 0; + } + break; + case 19: + retTextId = 0x702D; + break; + case 18: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7006; + } else { + if (Flags_GetEventChkInf(0x12)) { + if (Flags_GetInfTable(0x71)) { + retTextId = 0x7072; + } else { + retTextId = 0x7071; + } + } else { + retTextId = 0x7029; + } + } + break; + case 20: + case 21: + if (Flags_GetEventChkInf(0x42)) { + retTextId = 0x2012; + } else if (Flags_GetEventChkInf(0x41)) { + if (Flags_GetInfTable(0x76)) { + retTextId = 0x2011; + } else { + retTextId = 0x2010; + } + } else if (Flags_GetEventChkInf(0x40)) { + retTextId = 0x200F; + } else { + retTextId = 0x200E; + } + break; + case 24: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7044; + } else { + retTextId = 0x7015; + } + break; + case 25: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7045; + } else { + Flags_GetInfTable(0xC2); + retTextId = 0x7016; + } + break; + case 26: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7046; + } else { + Flags_GetInfTable(0xC2); + retTextId = 0x7018; + } + break; + case 27: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7047; + } else if (Flags_GetEventChkInf(0x14)) { + retTextId = 0x701A; + } else if (Flags_GetEventChkInf(0x11)) { + if (Flags_GetInfTable(0xC6)) { + retTextId = 0x701C; + } else { + retTextId = 0x701B; + } + } else { + retTextId = 0x701A; + } + break; + case 28: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7048; + } else { + Flags_GetInfTable(0xCA); + retTextId = 0x701D; + } + break; + case 29: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x7049; + } else { + Flags_GetInfTable(0xCC); + retTextId = 0x701F; + } + break; + case 30: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x704A; + } else { + Flags_GetInfTable(0xCE); + retTextId = 0x7021; + } + break; + case 31: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x704B; + } else { + Flags_GetInfTable(0xD0); + retTextId = 0x7023; + } + break; + case 32: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x704C; + } else { + Flags_GetInfTable(0xD2); + retTextId = 0x7025; + } + break; + case 33: + if (Flags_GetEventChkInf(0x9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + retTextId = 0x704D; + } else { + Flags_GetInfTable(0xD4); + retTextId = 0x7027; + } + break; + case 34: + Flags_GetInfTable(0xD6); + retTextId = 0x403C; + break; + case 35: + if (Flags_GetInfTable(0xD8)) { + retTextId = 0x5029; + } else { + retTextId = 0x5028; + } + break; + case 37: + retTextId = 0x5002; + break; + case 38: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3027; + } else if (Flags_GetEventChkInf(0x23)) { + retTextId = 0x3021; + } else if (Flags_GetInfTable(0xE0)) { + retTextId = 0x302A; + } else { + retTextId = 0x3008; + } + } else { + if (Flags_GetEventChkInf(0x20)) { + retTextId = 0x4043; + } else { + retTextId = 0x302A; + } + } + break; + case 39: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3027; + } else if (Flags_GetEventChkInf(0x23)) { + retTextId = 0x3026; + } else { + retTextId = 0x3009; + } + } else { + if (Flags_GetEventChkInf(0x2A)) { + retTextId = 0x4043; + } else { + retTextId = 0x302A; + } + } + break; + case 40: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3027; + } else if (Flags_GetEventChkInf(0x23)) { + retTextId = 0x3026; + } else if (Flags_GetInfTable(0xEB)) { + retTextId = 0x302B; + } else { + retTextId = 0x300A; + } + } else { + if (Flags_GetEventChkInf(0x2B)) { + retTextId = 0x4043; + } else { + retTextId = 0x302A; + } + } + break; + case 41: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3027; + } else if (Flags_GetInfTable(0xF0)) { + retTextId = 0x3015; + } else { + retTextId = 0x3014; + } + } else { + if (Flags_GetEventChkInf(0x2C)) { + retTextId = 0x4043; + } else { + retTextId = 0x302A; + } + } + break; + case 42: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3027; + } else if (Flags_GetInfTable(0xF4)) { + retTextId = 0x3017; + } else { + retTextId = 0x3016; + } + } else { + if (Flags_GetEventChkInf(0x2C)) { + retTextId = 0x4043; + } else { + retTextId = 0x302A; + } + } + break; + case 43: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3027; + } else if (Flags_GetInfTable(0xF8)) { + retTextId = 0x3019; + } else { + retTextId = 0x3018; + } + } else { + if (Flags_GetEventChkInf(0x2D)) { + retTextId = 0x4043; + } else { + retTextId = 0x302A; + } + } + break; + case 48: + if (Flags_GetEventChkInf(0x25)) { + retTextId = 0x3029; + } else if (Flags_GetEventChkInf(0x20) && Flags_GetEventChkInf(0x21)) { + retTextId = 0x301B; + } else { + retTextId = 0x301A; + } + break; + case 49: + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402D; + } else if (Flags_GetEventChkInf(0x30)) { + retTextId = 0x4007; + } else { + retTextId = 0x4006; + } + break; + case 50: + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402E; + } else if (Flags_GetEventChkInf(0x30)) { + if (Flags_GetInfTable(0x124)) { + retTextId = 0x4009; + } else { + retTextId = 0x4008; + } + } else { + retTextId = 0x4006; + } + break; + case 51: + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402D; + } else if (Flags_GetEventChkInf(0x31)) { + if (Flags_GetInfTable(0x12A)) { + retTextId = 0x400B; + } else { + retTextId = 0x402F; + } + } else if (Flags_GetEventChkInf(0x30)) { + retTextId = 0x400A; + } else { + retTextId = 0x4006; + } + break; + case 52: + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402E; + } else if (Flags_GetEventChkInf(0x30)) { + retTextId = 0x400C; + } else { + retTextId = 0x4006; + } + break; + case 53: + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402D; + } else if (Flags_GetEventChkInf(0x33)) { + retTextId = 0x4010; + } else if (Flags_GetEventChkInf(0x30)) { + retTextId = 0x400F; + } else { + retTextId = 0x4006; + } + break; + case 54: + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402E; + } else if (Flags_GetEventChkInf(0x30)) { + retTextId = 0x4011; + } else { + retTextId = 0x4006; + } + break; + case 55: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x37)) { + retTextId = 0x402B; + } else if (Flags_GetEventChkInf(0x31)) { + if (Flags_GetInfTable(0x138)) { + retTextId = 0x401C; + } else { + retTextId = 0x401B; + } + } else { + retTextId = 0x401A; + } + } else { + retTextId = 0; + } + break; + case 58: + retTextId = 0x500F; + break; + case 59: + retTextId = 0x5010; + break; + case 60: + retTextId = 0x5012; + break; + case 61: + if (Flags_GetInfTable(0x166)) { + retTextId = 0x5001; + } else { + retTextId = 0x5000; + } + break; + case 62: + retTextId = 0x5012; + break; + case 63: + if (Flags_GetInfTable(0x16A)) { + retTextId = 0x5001; + } else { + retTextId = 0x5000; + } + break; + case 71: + if (Flags_GetEventChkInf(0x16)) { + retTextId = 0x2049; + } else if (Flags_GetEventChkInf(0x15)) { + retTextId = 0x2048; + } else if (Flags_GetEventChkInf(0x14)) { + retTextId = 0x2047; + } else if (Flags_GetEventChkInf(0x12) && !Flags_GetEventChkInf(0x14)) { + retTextId = 0x2044; + } else if (Flags_GetEventChkInf(0x10)) { + if (Flags_GetEventChkInf(0x11)) { + retTextId = 0x2043; + } else { + retTextId = 0x2042; + } + } else { + retTextId = 0x2041; + } + break; + case 72: + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x14)) { + retTextId = 0x2040; + } else if (Flags_GetInfTable(0x94)) { + retTextId = 0x2040; + } else { + retTextId = 0x203F; + } + } else { + if (!Flags_GetEventChkInf(0x18)) { + if (!IS_DAY) { + retTextId = 0x204E; + } else if (Flags_GetInfTable(0x9A)) { + retTextId = 0x2031; + } else { + retTextId = 0x2030; + } + } else { + retTextId = 0; + } + } + break; + } + + if (retTextId == 0) { + retTextId = 1; + } + + return retTextId; +} + +void func_80036E50(u16 textId, s16 arg1) { + switch (arg1) { + case 0: + switch (textId) { + case 0x1001: + Flags_SetInfTable(0x0); + return; + case 0x1002: + Flags_SetInfTable(0x1); + return; + case 0x1031: + Flags_SetEventChkInf(0x3); + Flags_SetInfTable(0x3); + return; + case 0x1047: + Flags_SetInfTable(0x5); + return; + } + return; + case 1: + switch (textId) { + case 0x102F: + Flags_SetEventChkInf(0x2); + Flags_SetInfTable(0xC); + return; + case 0x1033: + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Flags_SetEventChkInf(0x4); + Flags_SetInfTable(0xE); + return; + case 0x1045: + Flags_SetInfTable(0x10); + return; + case 0x1060: + Flags_SetInfTable(0x15); + return; + case 0x1067: + Flags_SetEventChkInf(0xA); + Flags_SetInfTable(0x17); + return; + case 0x1070: + Flags_SetInfTable(0x19); + return; + } + return; + case 2: + if (textId == 0x1056) { + Flags_SetInfTable(0x41); + } + return; + case 3: + if (textId == 0x1005) { + Flags_SetInfTable(0x1E); + } + return; + case 4: + if (textId == 0x105D) { + Flags_SetInfTable(0x47); + } + return; + case 5: + if (textId == 0x1008) { + Flags_SetInfTable(0x22); + } + return; + case 6: + if (textId == 0x100A) { + Flags_SetInfTable(0x24); + } + return; + case 7: + if (textId == 0x100C) { + Flags_SetInfTable(0x26); + } + return; + case 8: + if (textId == 0x100E) { + Flags_SetInfTable(0x28); + } + if (textId == 0x1059) { + Flags_SetInfTable(0x51); + } + return; + case 10: + if (textId == 0x104F) { + Flags_SetInfTable(0x59); + } + return; + case 13: + if (textId == 0x1054) { + Flags_SetInfTable(0x61); + } + return; + case 15: + if (textId == 0x1062) { + Flags_SetInfTable(0x66); + } + return; + case 16: + if (textId == 0x7002) { + Flags_SetInfTable(0x6A); + } + if (textId == 0x7003) { + Flags_SetInfTable(0x6A); + } + return; + case 17: + if (textId == 0x7007) { + Flags_SetInfTable(0x6C); + } + return; + case 18: + if (textId == 0x7071) { + Flags_SetInfTable(0x71); + } + return; + case 20: + case 21: + if (textId == 0x2010) { + Flags_SetInfTable(0x76); + } + return; + case 25: + if (textId == 0x7016) { + Flags_SetInfTable(0xC2); + } + return; + case 26: + if (textId == 0x7018) { + Flags_SetInfTable(0xC4); + } + return; + case 28: + if (textId == 0x701D) { + Flags_SetInfTable(0xCA); + } + return; + case 29: + if (textId == 0x701F) { + Flags_SetInfTable(0xCC); + } + return; + case 30: + if (textId == 0x7021) { + Flags_SetInfTable(0xCE); + } + return; + case 31: + if (textId == 0x7023) { + Flags_SetInfTable(0xD0); + } + return; + case 32: + if (textId == 0x7025) { + Flags_SetInfTable(0xD2); + } + return; + case 33: + if (textId == 0x7027) { + Flags_SetInfTable(0xD4); + } + return; + case 34: + if (textId == 0x403C) { + Flags_SetInfTable(0xD6); + } + return; + case 35: + if (textId == 0x5028) { + Flags_SetInfTable(0xD8); + } + return; + case 38: + if (textId == 0x3008) { + Flags_SetInfTable(0xE0); + } + return; + case 40: + if (textId == 0x300B) { + Flags_SetInfTable(0xEB); + } + return; + case 41: + if (textId == 0x3014) { + Flags_SetInfTable(0xF0); + } + return; + case 42: + if (textId == 0x3016) { + Flags_SetInfTable(0xF4); + } + return; + case 43: + if (textId == 0x3018) { + Flags_SetEventChkInf(0x20); + Flags_SetInfTable(0xF8); + } + return; + case 48: + if (textId == 0x3020) { + Flags_SetEventChkInf(0x22); + Flags_SetInfTable(0x113); + } + return; + case 49: + case 52: + case 53: + case 54: + if (textId == 0x4006) { + Flags_SetEventChkInf(0x30); + } + return; + case 50: + if (textId == 0x4006) { + Flags_SetEventChkInf(0x30); + } + if (textId == 0x4008) { + Flags_SetInfTable(0x124); + } + return; + case 51: + if (textId == 0x4006) { + Flags_SetEventChkInf(0x30); + } + if (textId == 0x400A) { + Flags_SetEventChkInf(0x32); + } + if (textId == 0x402F) { + Flags_SetInfTable(0x12A); + } + return; + case 55: + if (textId == 0x401B) { + Flags_SetEventChkInf(0x33); + Flags_SetInfTable(0x138); + } + return; + case 61: + if (textId == 0x5000) { + Flags_SetInfTable(0x166); + } + return; + case 63: + if (textId == 0x5013) { + Flags_SetInfTable(0x16A); + } + return; + case 71: + if (textId == 0x2041) { + Flags_SetEventChkInf(0x10); + } + if (textId == 0x2044) { + Flags_SetEventChkInf(0x12); + } + if (textId == 0x2047) { + Flags_SetEventChkInf(0x15); + } + if (textId == 0x2048) { + Flags_SetEventChkInf(0x16); + } + return; + case 72: + return; + } +} + +s32 func_800374E0(GlobalContext* globalCtx, Actor* actor, u16 textId) { + MessageContext* msgCtx = &globalCtx->msgCtx; + s32 ret = 1; + + switch (textId) { + case 0x1035: + if (msgCtx->choiceIndex == 0) { + if (Flags_GetInfTable(0x2A)) { + func_80035B18(globalCtx, actor, 0x1036); + } else { + func_80035B18(globalCtx, actor, 0x1041); + } + } + if (msgCtx->choiceIndex == 1) { + if (Flags_GetInfTable(0x2B)) { + func_80035B18(globalCtx, actor, 0x1037); + } else { + func_80035B18(globalCtx, actor, 0x1041); + } + } + ret = 0; + break; + case 0x1038: + if (msgCtx->choiceIndex == 0) { + if (Flags_GetInfTable(0x2E)) { + func_80035B18(globalCtx, actor, 0x1039); + } else { + func_80035B18(globalCtx, actor, 0x1041); + } + } + if (msgCtx->choiceIndex == 1) { + if (Flags_GetInfTable(0x2F)) { + func_80035B18(globalCtx, actor, 0x103A); + } else { + func_80035B18(globalCtx, actor, 0x1041); + } + } + if (msgCtx->choiceIndex == 2) { + if (Flags_GetInfTable(0x30)) { + func_80035B18(globalCtx, actor, 0x103B); + } else { + func_80035B18(globalCtx, actor, 0x1041); + } + } + ret = 0; + break; + case 0x103E: + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x103F); + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x1040); + } + ret = 0; + break; + case 0x1041: + if (msgCtx->choiceTextId == 0x1035) { + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x1036); + Flags_SetInfTable(0x2A); + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x1037); + Flags_SetInfTable(0x2B); + } + } + if (msgCtx->choiceTextId == 0x1038) { + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x1039); + Flags_SetInfTable(0x2E); + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x103A); + Flags_SetInfTable(0x2F); + } + if (msgCtx->choiceIndex == 2) { + func_80035B18(globalCtx, actor, 0x103B); + Flags_SetInfTable(0x30); + } + } + ret = 0; + break; + case 0x1062: + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x1063); + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x1064); + } + ret = 0; + break; + case 0x2030: + case 0x2031: + if (msgCtx->choiceIndex == 0) { + if (gSaveContext.rupees >= 10) { + func_80035B18(globalCtx, actor, 0x2034); + Rupees_ChangeBy(-10); + } else { + func_80035B18(globalCtx, actor, 0x2032); + } + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x2032); + } + Flags_SetInfTable(0x9A); + ret = 0; + break; + case 0x2036: + case 0x2037: + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x201F); + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x205A); + } + ret = 0; + break; + case 0x2038: + if (msgCtx->choiceIndex == 0) { + break; + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x205A); + } + ret = 0; + break; + case 0x2034: + if (msgCtx->choiceIndex != 0) { + break; + } + func_80035B18(globalCtx, actor, 0x2035); + ret = 0; + break; + case 0x2043: + if (Flags_GetEventChkInf(0x12)) { + break; + } + func_80035B18(globalCtx, actor, 0x2044); + ret = 0; + break; + case 0x205A: + break; + case 0x300A: + if (msgCtx->choiceIndex == 0) { + if (Flags_GetEventChkInf(0x22)) { + func_80035B18(globalCtx, actor, 0x300B); + } else { + func_80035B18(globalCtx, actor, 0x300C); + } + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x300D); + } + ret = 0; + break; + case 0x301B: + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x301D); + } + if (msgCtx->choiceIndex == 1) { + if (Flags_GetInfTable(0x113)) { + func_80035B18(globalCtx, actor, 0x301F); + } else { + func_80035B18(globalCtx, actor, 0x301E); + } + } + ret = 0; + break; + case 0x301E: + func_80035B18(globalCtx, actor, 0x3020); + ret = 0; + break; + case 0x400C: + if (msgCtx->choiceIndex == 0) { + func_80035B18(globalCtx, actor, 0x400D); + } + if (msgCtx->choiceIndex == 1) { + func_80035B18(globalCtx, actor, 0x400E); + } + ret = 0; + break; + case 0x7007: + func_80035B18(globalCtx, actor, 0x703E); + ret = 0; + break; + case 0x703E: + func_80035B18(globalCtx, actor, 0x703F); + ret = 0; + break; + case 0x703F: + func_80035B18(globalCtx, actor, 0x7042); + ret = 0; + break; + } + + return ret; +} + +u16 func_80037C30(GlobalContext* globalCtx, s16 arg1) { + return func_80035BFC(globalCtx, arg1); +} + +s32 func_80037C5C(GlobalContext* globalCtx, s16 arg1, u16 textId) { + func_80036E50(textId, arg1); + return false; +} + +s32 func_80037C94(GlobalContext* globalCtx, Actor* actor, s32 arg2) { + return func_800374E0(globalCtx, actor, actor->textId); +} + +s32 func_80037CB8(GlobalContext* globalCtx, Actor* actor, s16 arg2) { + MessageContext* msgCtx = &globalCtx->msgCtx; + s32 ret = false; + + switch (Message_GetState(msgCtx)) { + case TEXT_STATE_CLOSING: + func_80037C5C(globalCtx, arg2, actor->textId); + ret = true; + break; + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx) && func_80037C94(globalCtx, actor, arg2)) { + Audio_PlaySoundGeneral(NA_SE_SY_CANCEL, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + msgCtx->msgMode = MSGMODE_TEXT_CLOSING; + ret = true; + } + break; + } + + return ret; +} + +s32 func_80037D98(GlobalContext* globalCtx, Actor* actor, s16 arg2, s32* arg3) { + s16 var; + s16 sp2C; + s16 sp2A; + s16 abs_var; + + if (Actor_ProcessTalkRequest(actor, globalCtx)) { + *arg3 = 1; + return true; + } + + if (*arg3 == 1) { + if (func_80037CB8(globalCtx, actor, arg2)) { + *arg3 = 0; + } + return false; + } + + Actor_GetScreenPos(globalCtx, actor, &sp2C, &sp2A); + + if (0) {} // Necessary to match + + if ((sp2C < 0) || (sp2C > SCREEN_WIDTH) || (sp2A < 0) || (sp2A > SCREEN_HEIGHT)) { + return false; + } + + var = actor->yawTowardsPlayer - actor->shape.rot.y; + abs_var = ABS(var); + + if (abs_var >= 0x4300) { + return false; + } + + if ((actor->xyzDistToPlayerSq > SQ(160.0f)) && !actor->isTargeted) { + return false; + } + + if (actor->xyzDistToPlayerSq <= SQ(80.0f)) { + if (func_8002F2CC(actor, globalCtx, 80.0f)) { + actor->textId = func_80037C30(globalCtx, arg2); + } + } else { + if (func_8002F2F4(actor, globalCtx)) { + actor->textId = func_80037C30(globalCtx, arg2); + } + } + + return false; +} + +s32 func_80037F30(Vec3s* arg0, Vec3s* arg1) { + Math_SmoothStepToS(&arg0->y, 0, 6, 6200, 100); + Math_SmoothStepToS(&arg0->x, 0, 6, 6200, 100); + Math_SmoothStepToS(&arg1->y, 0, 6, 6200, 100); + Math_SmoothStepToS(&arg1->x, 0, 6, 6200, 100); + return true; +} + +s32 func_80037FC8(Actor* actor, Vec3f* arg1, Vec3s* arg2, Vec3s* arg3) { + s16 sp36; + s16 sp34; + s16 var; + + sp36 = Math_Vec3f_Pitch(&actor->focus.pos, arg1); + sp34 = Math_Vec3f_Yaw(&actor->focus.pos, arg1) - actor->world.rot.y; + + Math_SmoothStepToS(&arg2->x, sp36, 6, 2000, 1); + arg2->x = (arg2->x < -6000) ? -6000 : ((arg2->x > 6000) ? 6000 : arg2->x); + + var = Math_SmoothStepToS(&arg2->y, sp34, 6, 2000, 1); + arg2->y = (arg2->y < -8000) ? -8000 : ((arg2->y > 8000) ? 8000 : arg2->y); + + if (var && (ABS(arg2->y) < 8000)) { + return false; + } + + Math_SmoothStepToS(&arg3->y, sp34 - arg2->y, 4, 2000, 1); + arg3->y = (arg3->y < -12000) ? -12000 : ((arg3->y > 12000) ? 12000 : arg3->y); + + return true; +} + +s32 func_80038154(GlobalContext* globalCtx, Actor* actor, Vec3s* arg2, Vec3s* arg3, f32 arg4) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + Vec3f sp2C; + s16 var; + s16 abs_var; + + actor->focus.pos = actor->world.pos; + actor->focus.pos.y += arg4; + + if (!(((globalCtx->csCtx.state != CS_STATE_IDLE) || (gDbgCamEnabled)) && (gSaveContext.entranceIndex == 0x00EE))) { + var = actor->yawTowardsPlayer - actor->shape.rot.y; + abs_var = ABS(var); + if (abs_var >= 0x4300) { + func_80037F30(arg2, arg3); + return false; + } + } + + if (((globalCtx->csCtx.state != CS_STATE_IDLE) || (gDbgCamEnabled)) && (gSaveContext.entranceIndex == 0x00EE)) { + sp2C = globalCtx->view.eye; + } else { + sp2C = player->actor.focus.pos; + } + + func_80037FC8(actor, &sp2C, arg2, arg3); + + return true; +} + +s32 func_80038290(GlobalContext* globalCtx, Actor* actor, Vec3s* arg2, Vec3s* arg3, Vec3f arg4) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + Vec3f sp24; + s16 var; + s16 abs_var; + + actor->focus.pos = arg4; + + if (!(((globalCtx->csCtx.state != CS_STATE_IDLE) || (gDbgCamEnabled)) && (gSaveContext.entranceIndex == 0x00EE))) { + var = actor->yawTowardsPlayer - actor->shape.rot.y; + abs_var = ABS(var); + if (abs_var >= 0x4300) { + func_80037F30(arg2, arg3); + return false; + } + } + + if (((globalCtx->csCtx.state != CS_STATE_IDLE) || (gDbgCamEnabled)) && (gSaveContext.entranceIndex == 0x00EE)) { + sp24 = globalCtx->view.eye; + } else { + sp24 = player->actor.focus.pos; + } + + func_80037FC8(actor, &sp24, arg2, arg3); + + return true; +} diff --git a/soh/src/code/z_actor_dlftbls.c b/soh/src/code/z_actor_dlftbls.c new file mode 100644 index 000000000..ee18d9703 --- /dev/null +++ b/soh/src/code/z_actor_dlftbls.c @@ -0,0 +1,97 @@ +#include "global.h" + +// Linker symbol declarations (used in the table below) +#define DEFINE_ACTOR(name, _1, _2) DECLARE_OVERLAY_SEGMENT(name) +#define DEFINE_ACTOR_INTERNAL(_0, _1, _2) +#define DEFINE_ACTOR_UNSET(_0) + +#include "tables/actor_table.h" + +#undef DEFINE_ACTOR +#undef DEFINE_ACTOR_INTERNAL +#undef DEFINE_ACTOR_UNSET + +// Init Vars declarations (also used in the table below) +#define DEFINE_ACTOR(name, _1, _2) extern ActorInit name##_InitVars; +#define DEFINE_ACTOR_INTERNAL(name, _1, _2) extern ActorInit name##_InitVars; +#define DEFINE_ACTOR_UNSET(_0) + +#include "tables/actor_table.h" + +#undef DEFINE_ACTOR +#undef DEFINE_ACTOR_INTERNAL +#undef DEFINE_ACTOR_UNSET + +// Actor Overlay Table definition +//#define DEFINE_ACTOR(name, _1, allocType) \ +// { (uintptr_t)_ovl_##name##SegmentRomStart, \ +// (uintptr_t)_ovl_##name##SegmentRomEnd, \ +// _ovl_##name##SegmentStart, \ +// _ovl_##name##SegmentEnd, \ +// NULL, \ +// &name##_InitVars, \ +// #name, \ +// allocType, \ +// 0 }, + +#define DEFINE_ACTOR_INTERNAL(name, _1, allocType) { 0, 0, NULL, NULL, NULL, &name##_InitVars, #name, allocType, 0 }, + +#define DEFINE_ACTOR_UNSET(_0) { 0 }, + +#define DEFINE_ACTOR(name, _1, allocType) { 0, 0, NULL, NULL, NULL, &name##_InitVars, #name, allocType, 0 }, + +ActorOverlay gActorOverlayTable[] = { +#include "tables/actor_table.h" +}; + +#undef DEFINE_ACTOR +#undef DEFINE_ACTOR_INTERNAL +#undef DEFINE_ACTOR_UNSET + +s32 gMaxActorId = 0; + +static FaultClient sFaultClient; + +void ActorOverlayTable_LogPrint(void) { + ActorOverlay* overlayEntry; + u32 i; + + osSyncPrintf("actor_dlftbls %u\n", gMaxActorId); + osSyncPrintf("RomStart RomEnd SegStart SegEnd allocp profile segname\n"); + + for (i = 0, overlayEntry = &gActorOverlayTable[0]; i < (u32)gMaxActorId; i++, overlayEntry++) { + osSyncPrintf("%08x %08x %08x %08x %08x %08x %s\n", overlayEntry->vromStart, overlayEntry->vromEnd, + overlayEntry->vramStart, overlayEntry->vramEnd, overlayEntry->loadedRamAddr, + &overlayEntry->initInfo->id, overlayEntry->name != NULL ? overlayEntry->name : "?"); + } +} + +void ActorOverlayTable_FaultPrint(void* arg0, void* arg1) { + ActorOverlay* overlayEntry; + u32 overlaySize; + s32 i; + + FaultDrawer_SetCharPad(-2, 0); + + FaultDrawer_Printf("actor_dlftbls %u\n", gMaxActorId); + FaultDrawer_Printf("No. RamStart- RamEnd cn Name\n"); + + for (i = 0, overlayEntry = &gActorOverlayTable[0]; i < gMaxActorId; i++, overlayEntry++) { + overlaySize = (uintptr_t)overlayEntry->vramEnd - (uintptr_t)overlayEntry->vramStart; + if (overlayEntry->loadedRamAddr != NULL) { + FaultDrawer_Printf("%3d %08x-%08x %3d %s\n", i, overlayEntry->loadedRamAddr, + (uintptr_t)overlayEntry->loadedRamAddr + overlaySize, overlayEntry->numLoaded, + overlayEntry->name != NULL ? overlayEntry->name : ""); + } + } +} + +void ActorOverlayTable_Init(void) { + gMaxActorId = ACTOR_ID_MAX; + Fault_AddClient(&sFaultClient, ActorOverlayTable_FaultPrint, NULL, NULL); +} + +void ActorOverlayTable_Cleanup(void) { + Fault_RemoveClient(&sFaultClient); + gMaxActorId = 0; +} diff --git a/soh/src/code/z_bgcheck.c b/soh/src/code/z_bgcheck.c new file mode 100644 index 000000000..b736d0cf0 --- /dev/null +++ b/soh/src/code/z_bgcheck.c @@ -0,0 +1,4522 @@ +#include "global.h" +#include "vt.h" + +#include + +#define SS_NULL 0xFFFF + +// bccFlags +#define BGCHECK_CHECK_WALL (1 << 0) +#define BGCHECK_CHECK_FLOOR (1 << 1) +#define BGCHECK_CHECK_CEILING (1 << 2) +#define BGCHECK_CHECK_ONE_FACE (1 << 3) +#define BGCHECK_CHECK_DYNA (1 << 4) +#define BGCHECK_CHECK_ALL \ + (BGCHECK_CHECK_WALL | BGCHECK_CHECK_FLOOR | BGCHECK_CHECK_CEILING | BGCHECK_CHECK_ONE_FACE | BGCHECK_CHECK_DYNA) + +// bciFlags +#define BGCHECK_IGNORE_NONE 0 +#define BGCHECK_IGNORE_CEILING (1 << 0) +#define BGCHECK_IGNORE_WALL (1 << 1) +#define BGCHECK_IGNORE_FLOOR (1 << 2) + +// poly exclusion flags (xpFlags) +#define COLPOLY_IGNORE_NONE 0 +#define COLPOLY_IGNORE_CAMERA (1 << 0) +#define COLPOLY_IGNORE_ENTITY (1 << 1) +#define COLPOLY_IGNORE_PROJECTILES (1 << 2) + +// func_80041DB8, SurfaceType wall properties +s32 D_80119D90[32] = { + 0, 1, 3, 5, 8, 16, 32, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// SurfaceType_GetSfx +u16 D_80119E10[14] = { + NA_SE_PL_WALK_GROUND - SFX_FLAG, NA_SE_PL_WALK_SAND - SFX_FLAG, NA_SE_PL_WALK_CONCRETE - SFX_FLAG, + NA_SE_PL_WALK_DIRT - SFX_FLAG, NA_SE_PL_WALK_WATER0 - SFX_FLAG, NA_SE_PL_WALK_WATER1 - SFX_FLAG, + NA_SE_PL_WALK_WATER2 - SFX_FLAG, NA_SE_PL_WALK_MAGMA - SFX_FLAG, NA_SE_PL_WALK_GRASS - SFX_FLAG, + NA_SE_PL_WALK_GLASS - SFX_FLAG, NA_SE_PL_WALK_LADDER - SFX_FLAG, NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_ICE - SFX_FLAG, NA_SE_PL_WALK_IRON - SFX_FLAG, +}; + +/** + * original name: T_BGCheck_PosErrorCheck + */ +s32 BgCheck_PosErrorCheck(Vec3f* pos, char* file, s32 line) { + if (pos->x >= BGCHECK_XYZ_ABSMAX || pos->x <= -BGCHECK_XYZ_ABSMAX || pos->y >= BGCHECK_XYZ_ABSMAX || + pos->y <= -BGCHECK_XYZ_ABSMAX || pos->z >= BGCHECK_XYZ_ABSMAX || pos->z <= -BGCHECK_XYZ_ABSMAX) { + osSyncPrintf(VT_FGCOL(RED)); + // "Position is invalid." + osSyncPrintf("T_BGCheck_PosErrorCheck():位置が妥当ではありません。pos (%f,%f,%f) file:%s line:%d\n", pos->x, + pos->y, pos->z, file, line); + osSyncPrintf(VT_RST); + return true; + } + return false; +} + +/** + * Set SSNode + */ +void SSNode_SetValue(SSNode* node, s16* polyId, u16 next) { + node->polyId = *polyId; + node->next = next; +} + +/** + * Set SSList to SS_NULL + */ +void SSList_SetNull(SSList* ssList) { + ssList->head = SS_NULL; +} + +/** + * Insert `polyId` at the start of the static `ssList` list + */ +void SSNodeList_SetSSListHead(SSNodeList* nodeList, SSList* ssList, s16* polyId) { + u16 newNodeId = SSNodeList_GetNextNodeIdx(nodeList); + + SSNode_SetValue(&nodeList->tbl[newNodeId], polyId, ssList->head); + ssList->head = newNodeId; +} + +/** + * Insert `polyId` at the start of the dyna `ssList` list + */ +void DynaSSNodeList_SetSSListHead(DynaSSNodeList* nodeList, SSList* ssList, s16* polyId) { + u16 newNodeId = DynaSSNodeList_GetNextNodeIdx(nodeList); + + ASSERT(newNodeId != SS_NULL, "new_node != SS_NULL", "../z_bgcheck.c", 1776); + SSNode_SetValue(&nodeList->tbl[newNodeId], polyId, ssList->head); + ssList->head = newNodeId; +} + +/** + * Initialize DynaSSNodeList + */ +void DynaSSNodeList_Initialize(GlobalContext* globalCtx, DynaSSNodeList* nodeList) { + nodeList->tbl = NULL; + nodeList->count = 0; +} + +/** + * Initialize DynaSSNodeList tbl + */ +void DynaSSNodeList_Alloc(GlobalContext* globalCtx, DynaSSNodeList* nodeList, s32 max) { + nodeList->tbl = THA_AllocEndAlign(&globalCtx->state.tha, max * sizeof(SSNode), -2); + + ASSERT(nodeList->tbl != NULL, "psst->tbl != NULL", "../z_bgcheck.c", 1811); + + nodeList->max = max; + nodeList->count = 0; +} + +/** + * Reset DynaSSNodeList count + */ +void DynaSSNodeList_ResetCount(DynaSSNodeList* nodeList) { + nodeList->count = 0; +} + +/** + * Get next available node index in DynaSSNodeList + * returns SS_NULL if list is full + */ +u16 DynaSSNodeList_GetNextNodeIdx(DynaSSNodeList* nodeList) { + u16 idx = nodeList->count++; + + if (nodeList->max <= idx) { + return SS_NULL; + } + + return idx; +} + +/** + * original name: T_BGCheck_Vec3sToVec3f + */ +void BgCheck_Vec3sToVec3f(Vec3s* src, Vec3f* dst) { + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} + +/** + * original name: T_BGCheck_Vec3fToVec3s + */ +void BgCheck_Vec3fToVec3s(Vec3s* dst, Vec3f* src) { + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} + +/** + * Get CollisionPoly's lowest y point + */ +s16 CollisionPoly_GetMinY(CollisionPoly* poly, Vec3s* vtxList) { + s32 a; + s32 b; + s32 c; + s16 min; + + //! @bug Due to rounding errors, some polys with a slight slope have a y normal of 1.0f/-1.0f. As such, this + //! optimization returns the wrong minimum y for a subset of these polys. + if (poly->normal.y == COLPOLY_SNORMAL(1.0f) || poly->normal.y == COLPOLY_SNORMAL(-1.0f)) { + return vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)].y; + } + + a = COLPOLY_VTX_INDEX(poly->flags_vIA); + b = COLPOLY_VTX_INDEX(poly->flags_vIB); + c = poly->vIC; + + min = vtxList[a].y; + + if (min > vtxList[b].y) { + min = vtxList[b].y; + } + if (min < vtxList[c].y) { + return min; + } + return vtxList[c].y; +} + +/** + * CollisionPoly get unit normal + */ +void CollisionPoly_GetNormalF(CollisionPoly* poly, f32* nx, f32* ny, f32* nz) { + *nx = COLPOLY_GET_NORMAL(poly->normal.x); + *ny = COLPOLY_GET_NORMAL(poly->normal.y); + *nz = COLPOLY_GET_NORMAL(poly->normal.z); +} + +/** + * Compute transform matrix mapping +y (up) to the collision poly's normal + */ +void func_80038A28(CollisionPoly* poly, f32 tx, f32 ty, f32 tz, MtxF* dest) { + f32 nx; + f32 ny; + f32 nz; + s32 pad; + f32 phi_f2; + f32 phi_f14; + f32 phi_f12; + f32 inv_phi_f2; + f32 inv_phi_f14; + + if (poly == NULL) { + return; + } + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + + phi_f2 = sqrtf(1.0f - SQ(nx)); + if (!IS_ZERO(phi_f2)) { + inv_phi_f2 = 1.0f / phi_f2; + phi_f14 = ny * inv_phi_f2; + phi_f12 = -(nz * inv_phi_f2); + } else { + phi_f14 = sqrtf(1.0f - SQ(ny)); + if (1) {} + if (!IS_ZERO(phi_f14)) { + inv_phi_f14 = (1.0f / phi_f14); + phi_f12 = nx * inv_phi_f14; + phi_f2 = -(nz * inv_phi_f14); + } else { + phi_f12 = 0.0f; + phi_f2 = 0.0f; + } + } + dest->xx = phi_f2; + dest->yx = (-nx) * phi_f14; + dest->zx = nx * phi_f12; + dest->xy = nx; + dest->yy = ny; + dest->zy = nz; + dest->yz = phi_f12; + dest->zz = phi_f14; + dest->wx = 0.0f; + dest->wy = 0.0f; + dest->xz = 0.0f; + dest->wz = 0.0f; + dest->xw = tx; + dest->yw = ty; + dest->zw = tz; + dest->ww = 1.0f; +} + +/** + * Calculate point distance from plane along normal + */ +f32 CollisionPoly_GetPointDistanceFromPlane(CollisionPoly* poly, Vec3f* point) { + return (poly->normal.x * point->x + poly->normal.y * point->y + poly->normal.z * point->z) * COLPOLY_NORMAL_FRAC + + poly->dist; +} + +/** + * Get Poly Vertices + */ +void CollisionPoly_GetVertices(CollisionPoly* poly, Vec3s* vtxList, Vec3f* dest) { + BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)], &dest[0]); + BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)], &dest[1]); + BgCheck_Vec3sToVec3f(&vtxList[poly->vIC], &dest[2]); +} + +/** + * Get vertices by bgId + * original name: T_Polygon_GetVertex_bg_ai + */ +void CollisionPoly_GetVerticesByBgId(CollisionPoly* poly, s32 bgId, CollisionContext* colCtx, Vec3f* dest) { + Vec3s* vtxList; + + if (poly == NULL || bgId > BG_ACTOR_MAX || dest == NULL) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "Argument not appropriate. Processing terminated." + osSyncPrintf("T_Polygon_GetVertex_bg_ai(): Error %d %d %d 引数が適切ではありません。処理を終了します。\n", + poly == NULL, bgId > BG_ACTOR_MAX, dest == NULL); + osSyncPrintf(VT_RST); + + if (dest != NULL) { + //! @bug: dest[2] x and y are not set to 0 + dest[0].x = dest[0].y = dest[0].z = dest[1].x = dest[1].y = dest[1].z = dest[2].z = 0.0f; + } + } else { + if (bgId == BGCHECK_SCENE) { + vtxList = colCtx->colHeader->vtxList; + } else { + vtxList = colCtx->dyna.vtxList; + } + + CollisionPoly_GetVertices(poly, vtxList, dest); + } +} + +/** + * Checks if point (`x`,`z`) is within `chkDist` of `poly`, computing `yIntersect` if true + * Determinant max 300.0f + */ +s32 CollisionPoly_CheckYIntersectApprox1(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 z, f32* yIntersect, + f32 chkDist) { + static Vec3f polyVerts[3]; + f32 nx; + f32 ny; + f32 nz; + Vec3s* vA; + Vec3s* vB; + Vec3s* vC; + + vA = &vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)]; + Math_Vec3s_ToVec3f(&polyVerts[0], vA); + vB = &vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)]; + Math_Vec3s_ToVec3f(&polyVerts[1], vB); + vC = &vtxList[poly->vIC]; + Math_Vec3s_ToVec3f(&polyVerts[2], vC); + + nx = COLPOLY_GET_NORMAL(poly->normal.x); + ny = COLPOLY_GET_NORMAL(poly->normal.y); + nz = COLPOLY_GET_NORMAL(poly->normal.z); + + return Math3D_TriChkPointParaYIntersectDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, poly->dist, z, + x, yIntersect, chkDist); +} + +/** + * Checks if point (`x`,`z`) is within `chkDist` of `poly`, computing `yIntersect` if true + * Determinant max 0.0f (checks if on or within poly) + */ +s32 CollisionPoly_CheckYIntersect(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 z, f32* yIntersect, f32 chkDist) { + static Vec3f polyVerts[3]; + f32 nx; + f32 ny; + f32 nz; + + CollisionPoly_GetVertices(poly, vtxList, polyVerts); + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + return Math3D_TriChkPointParaYIntersectInsideTri(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, + poly->dist, z, x, yIntersect, chkDist); +} + +/** + * Checks if point (`x`,`z`) is within 1.0f of `poly`, computing `yIntersect` if true + * Determinant max 300.0f + */ +s32 CollisionPoly_CheckYIntersectApprox2(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 z, f32* yIntersect) { + return CollisionPoly_CheckYIntersectApprox1(poly, vtxList, x, z, yIntersect, 1.0f); +} + +/** + * Checks if point (`y`,`z`) is within 1.0f of `poly`, computing `xIntersect` if true + * Determinant max 300.0f + */ +s32 CollisionPoly_CheckXIntersectApprox(CollisionPoly* poly, Vec3s* vtxList, f32 y, f32 z, f32* xIntersect) { + static Vec3f polyVerts[3]; + f32 nx; + f32 ny; + f32 nz; + + CollisionPoly_GetVertices(poly, vtxList, polyVerts); + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + return Math3D_TriChkPointParaXIntersect(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, poly->dist, y, z, + xIntersect); +} + +/** + * Checks if point (`x`,`y`) is within 1.0f of `poly`, computing `zIntersect` if true + * Determinant max 300.0f + */ +s32 CollisionPoly_CheckZIntersectApprox(CollisionPoly* poly, Vec3s* vtxList, f32 x, f32 y, f32* zIntersect) { + static Vec3f polyVerts[3]; + f32 nx; + f32 ny; + f32 nz; + + CollisionPoly_GetVertices(poly, vtxList, polyVerts); + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + return Math3D_TriChkPointParaZIntersect(&polyVerts[0], &polyVerts[1], &polyVerts[2], nx, ny, nz, poly->dist, x, y, + zIntersect); +} + +/** + * Test if travelling from `posA` to `posB` intersects `poly` + * returns true if an intersection occurs, else false + * returns `planeIntersect`, which is the point at which the line from `posA` to `posB` crosses `poly`'s plane + * if `chkOneFace` is true, return false (no intersection) when going through the poly from A to B is done in the + * normal's direction + */ +s32 CollisionPoly_LineVsPoly(CollisionPoly* poly, Vec3s* vtxList, Vec3f* posA, Vec3f* posB, Vec3f* planeIntersect, + s32 chkOneFace, f32 chkDist) { + static Vec3f polyVerts[3]; + static Plane plane; + f32 planeDistA; + f32 planeDistB; + f32 planeDistDelta; + + plane.originDist = poly->dist; + planeDistA = + (poly->normal.x * posA->x + poly->normal.y * posA->y + poly->normal.z * posA->z) * COLPOLY_NORMAL_FRAC + + plane.originDist; + planeDistB = + (poly->normal.x * posB->x + poly->normal.y * posB->y + poly->normal.z * posB->z) * COLPOLY_NORMAL_FRAC + + plane.originDist; + + planeDistDelta = planeDistA - planeDistB; + if ((planeDistA >= 0.0f && planeDistB >= 0.0f) || (planeDistA < 0.0f && planeDistB < 0.0f) || + (chkOneFace && planeDistA < 0.0f && planeDistB > 0.0f) || IS_ZERO(planeDistDelta)) { + return false; + } + + CollisionPoly_GetNormalF(poly, &plane.normal.x, &plane.normal.y, &plane.normal.z); + CollisionPoly_GetVertices(poly, vtxList, polyVerts); + Math3D_LineSplitRatio(posA, posB, planeDistA / planeDistDelta, planeIntersect); + if ((fabsf(plane.normal.x) > 0.5f && + Math3D_TriChkPointParaXDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], &plane, planeIntersect->y, + planeIntersect->z, chkDist)) || + (fabsf(plane.normal.y) > 0.5f && + Math3D_TriChkPointParaYDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], &plane, planeIntersect->z, + planeIntersect->x, chkDist)) || + (fabsf(plane.normal.z) > 0.5f && + Math3D_TriChkLineSegParaZDist(&polyVerts[0], &polyVerts[1], &polyVerts[2], &plane, planeIntersect->x, + planeIntersect->y, chkDist))) { + return true; + } + return false; +} + +/** + * Tests if sphere `center` `radius` intersects `poly` + */ +s32 CollisionPoly_SphVsPoly(CollisionPoly* poly, Vec3s* vtxList, Vec3f* center, f32 radius) { + static Sphere16 sphere; + static TriNorm tri; + Vec3f intersect; + + CollisionPoly_GetVertices(poly, vtxList, tri.vtx); + CollisionPoly_GetNormalF(poly, &tri.plane.normal.x, &tri.plane.normal.y, &tri.plane.normal.z); + tri.plane.originDist = poly->dist; + sphere.center.x = center->x; + sphere.center.y = center->y; + sphere.center.z = center->z; + sphere.radius = radius; + return Math3D_TriVsSphIntersect(&sphere, &tri, &intersect); +} + +/** + * Add poly to StaticLookup table + * Table is sorted by poly's smallest y vertex component + * `ssList` is the list to append a new poly to + * `polyList` is the CollisionPoly lookup list + * `vtxList` is the vertex lookup list + * `polyId` is the index of the poly in polyList to insert into the lookup table + */ +void StaticLookup_AddPolyToSSList(CollisionContext* colCtx, SSList* ssList, CollisionPoly* polyList, Vec3s* vtxList, + s16 polyId) { + SSNode* curNode; + SSNode* nextNode; + s32 polyYMin; + u16 newNodeId; + s16 curPolyId; + + // if list is null + if (ssList->head == SS_NULL) { + SSNodeList_SetSSListHead(&colCtx->polyNodes, ssList, &polyId); + return; + } + + polyYMin = CollisionPoly_GetMinY(&polyList[polyId], vtxList); + curNode = &colCtx->polyNodes.tbl[ssList->head]; + curPolyId = curNode->polyId; + + // if the poly being inserted has a lower y than the first poly + if (polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIA)].y && + polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIB)].y && + polyYMin < vtxList[polyList[curPolyId].vIC].y) { + SSNodeList_SetSSListHead(&colCtx->polyNodes, ssList, &polyId); + return; + } + while (true) { + // if at the end of the list + if (curNode->next == SS_NULL) { + newNodeId = SSNodeList_GetNextNodeIdx(&colCtx->polyNodes); + SSNode_SetValue(&colCtx->polyNodes.tbl[newNodeId], &polyId, SS_NULL); + curNode->next = newNodeId; + return; + } + + nextNode = &colCtx->polyNodes.tbl[curNode->next]; + curPolyId = nextNode->polyId; + + // if the poly being inserted is lower than the next poly + if (polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIA)].y && + polyYMin < vtxList[COLPOLY_VTX_INDEX(polyList[curPolyId].flags_vIB)].y && + polyYMin < vtxList[polyList[curPolyId].vIC].y) { + newNodeId = SSNodeList_GetNextNodeIdx(&colCtx->polyNodes); + SSNode_SetValue(&colCtx->polyNodes.tbl[newNodeId], &polyId, curNode->next); + curNode->next = newNodeId; + return; + } + curNode = nextNode; + } +} + +/** + * Add CollisionPoly to StaticLookup list + */ +void StaticLookup_AddPoly(StaticLookup* lookup, CollisionContext* colCtx, CollisionPoly* polyList, Vec3s* vtxList, + s16 index) { + if (polyList[index].normal.y > COLPOLY_SNORMAL(0.5f)) { + StaticLookup_AddPolyToSSList(colCtx, &lookup->floor, polyList, vtxList, index); + } else if (polyList[index].normal.y < COLPOLY_SNORMAL(-0.8f)) { + StaticLookup_AddPolyToSSList(colCtx, &lookup->ceiling, polyList, vtxList, index); + } else { + StaticLookup_AddPolyToSSList(colCtx, &lookup->wall, polyList, vtxList, index); + } +} + +/** + * Locates the closest static poly directly underneath `pos`, starting at list `ssList` + * returns yIntersect of the closest poly, or `yIntersectMin` + * stores the pointer of the closest poly to `outPoly` + * if (flags & 1), ignore polys with a normal.y < 0 (from vertical walls to ceilings) + */ +f32 BgCheck_RaycastFloorStaticList(CollisionContext* colCtx, u16 xpFlags, SSList* ssList, CollisionPoly** outPoly, + Vec3f* pos, f32 yIntersectMin, f32 chkDist, s32 flags) { + SSNode* curNode; + s32 polyId; + f32 result; + f32 yIntersect; + + result = yIntersectMin; + if (ssList->head == SS_NULL) { + return result; + } + + curNode = &colCtx->polyNodes.tbl[ssList->head]; + + while (true) { + polyId = curNode->polyId; + + if (COLPOLY_VIA_FLAG_TEST(colCtx->colHeader->polyList[polyId].flags_vIA, xpFlags) || + ((flags & 1) && colCtx->colHeader->polyList[polyId].normal.y < 0)) { + if (curNode->next == SS_NULL) { + break; + } + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + + if (pos->y < colCtx->colHeader->vtxList[COLPOLY_VTX_INDEX(colCtx->colHeader->polyList[polyId].flags_vIA)].y && + pos->y < colCtx->colHeader->vtxList[COLPOLY_VTX_INDEX(colCtx->colHeader->polyList[polyId].flags_vIB)].y && + pos->y < colCtx->colHeader->vtxList[colCtx->colHeader->polyList[polyId].vIC].y) { + break; + } + + if (CollisionPoly_CheckYIntersect(&colCtx->colHeader->polyList[polyId], colCtx->colHeader->vtxList, pos->x, + pos->z, &yIntersect, chkDist) == true) { + // if poly is closer to pos without going over + if (yIntersect < pos->y && result < yIntersect) { + + result = yIntersect; + *outPoly = &colCtx->colHeader->polyList[polyId]; + } + } + + if (curNode->next == SS_NULL) { + break; + } + curNode = &colCtx->polyNodes.tbl[curNode->next]; + } + return result; +} + +/** + * Locates the closest static poly directly underneath `pos` within `lookup`. + * returns yIntersect of the closest poly, or `yIntersectMin` + * stores the pointer of the closest poly to `outPoly` + */ +f32 BgCheck_RaycastFloorStatic(StaticLookup* lookup, CollisionContext* colCtx, u16 xpFlags, CollisionPoly** poly, + Vec3f* pos, u32 arg5, f32 chkDist, f32 yIntersectMin) { + s32 flag; // skip polys with normal.y < 0 + f32 yIntersect = yIntersectMin; + + if (arg5 & 4) { + yIntersect = BgCheck_RaycastFloorStaticList(colCtx, xpFlags, &lookup->floor, poly, pos, yIntersect, chkDist, 0); + } + + if ((arg5 & 2) || (arg5 & 8)) { + flag = 0; + if (arg5 & 0x10) { + flag = 1; + } + yIntersect = + BgCheck_RaycastFloorStaticList(colCtx, xpFlags, &lookup->wall, poly, pos, yIntersect, chkDist, flag); + } + + if (arg5 & 1) { + flag = 0; + if (arg5 & 0x10) { + flag = 1; + } + yIntersect = + BgCheck_RaycastFloorStaticList(colCtx, xpFlags, &lookup->ceiling, poly, pos, yIntersect, chkDist, flag); + } + + return yIntersect; +} + +/** + * Compute wall displacement on `posX` and `posZ` + * sets `wallPolyPtr` to `poly` if `wallPolyPtr` is NULL or not a damage wall + * returns true if `wallPolyPtr` was changed + * `invXZlength` is 1 / sqrt( sq(poly.normal.x) + sq(poly.normal.z) ) + */ +s32 BgCheck_ComputeWallDisplacement(CollisionContext* colCtx, CollisionPoly* poly, f32* posX, f32* posZ, f32 nx, f32 ny, + f32 nz, f32 invXZlength, f32 planeDist, f32 radius, CollisionPoly** wallPolyPtr) { + CollisionPoly* wallPoly; + u32 surfaceData; + u32 wallDamage; + f32 displacement = (radius - planeDist) * invXZlength; + + *posX += displacement * nx; + *posZ += displacement * nz; + + wallPoly = *wallPolyPtr; + if (wallPoly == NULL) { + *wallPolyPtr = poly; + return true; + } + + surfaceData = colCtx->colHeader->surfaceTypeList[wallPoly->type].data[1]; + wallDamage = surfaceData & 0x08000000 ? 1 : 0; + + if (!wallDamage) { + *wallPolyPtr = poly; + return true; + } + return false; +} + +/** + * Performs collision detection on static poly walls within `lookup` on sphere `pos`, `radius` + * returns true if a collision was detected + * `outX` `outZ` return the displaced x,z coordinates, + * `outPoly` returns the pointer to the nearest poly collided with, or NULL + */ +s32 BgCheck_SphVsStaticWall(StaticLookup* lookup, CollisionContext* colCtx, u16 xpFlags, f32* outX, f32* outZ, + Vec3f* pos, f32 radius, CollisionPoly** outPoly) { + Vec3f resultPos; + f32 temp_f2; + f32 temp_f2_2; + f32 planeDist; + f32 intersect; + s32 result; + CollisionPoly* curPoly; + CollisionPoly* polyList; + SSNode* curNode; + f32 invNormalXZ; + f32 zTemp; + f32 xTemp; + s32 polyId; + f32 normalXZ; + f32 nx; + f32 ny; + f32 nz; + f32 temp_f16; + Vec3s* vtxList; + u16 pad; + + f32 zMin; + f32 zMax; + f32 xMin; + f32 xMax; + + result = false; + if (lookup->wall.head == SS_NULL) { + return result; + } + resultPos = *pos; + + polyList = colCtx->colHeader->polyList; + vtxList = colCtx->colHeader->vtxList; + curNode = &colCtx->polyNodes.tbl[lookup->wall.head]; + + while (true) { + polyId = curNode->polyId; + curPoly = &polyList[polyId]; + if (pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].y && + pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].y && pos->y < vtxList[curPoly->vIC].y) { + break; + } + + nx = COLPOLY_GET_NORMAL(curPoly->normal.x); + ny = COLPOLY_GET_NORMAL(curPoly->normal.y); + nz = COLPOLY_GET_NORMAL(curPoly->normal.z); + normalXZ = sqrtf(SQ(nx) + SQ(nz)); + planeDist = Math3D_DistPlaneToPos(nx, ny, nz, curPoly->dist, &resultPos); + if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + + ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 2854); + + invNormalXZ = 1.0f / normalXZ; + temp_f16 = fabsf(nz) * invNormalXZ; + if (temp_f16 < 0.4f) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + + // compute curPoly zMin/zMax + zTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].z; + zMax = zMin = zTemp; + zTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].z; + + if (zTemp < zMin) { + zMin = zTemp; + } else if (zMax < zTemp) { + zMax = zTemp; + } + zTemp = vtxList[curPoly->vIC].z; + if (zTemp < zMin) { + zMin = zTemp; + } else if (zTemp > zMax) { + zMax = zTemp; + } + + zMin -= radius; + zMax += radius; + + if (resultPos.z < zMin || resultPos.z > zMax) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + if (CollisionPoly_CheckZIntersectApprox(curPoly, vtxList, resultPos.x, pos->y, &intersect)) { + if (fabsf(intersect - resultPos.z) <= radius / temp_f16) { + if ((intersect - resultPos.z) * nz <= 4.0f) { + BgCheck_ComputeWallDisplacement(colCtx, curPoly, &resultPos.x, &resultPos.z, nx, ny, nz, + invNormalXZ, planeDist, radius, outPoly); + result = true; + } + } + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + } + } + + curNode = &colCtx->polyNodes.tbl[lookup->wall.head]; + + while (true) { + polyId = curNode->polyId; + curPoly = &polyList[polyId]; + if (pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].y && + pos->y < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].y && pos->y < vtxList[curPoly->vIC].y) { + break; + } + + nx = COLPOLY_GET_NORMAL(curPoly->normal.x); + ny = COLPOLY_GET_NORMAL(curPoly->normal.y); + nz = COLPOLY_GET_NORMAL(curPoly->normal.z); + normalXZ = sqrtf(SQ(nx) + SQ(nz)); + planeDist = Math3D_DistPlaneToPos(nx, ny, nz, curPoly->dist, &resultPos); + if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + + ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 2964); + + invNormalXZ = 1.0f / normalXZ; + temp_f16 = fabsf(nx) * invNormalXZ; + if (temp_f16 < 0.4f) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + + // compute curPoly xMin/xMax + xTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].x; + xMax = xMin = xTemp; + xTemp = vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].x; + + if (xTemp < xMin) { + xMin = xTemp; + } else if (xMax < xTemp) { + xMax = xTemp; + } + xTemp = vtxList[curPoly->vIC].x; + if (xTemp < xMin) { + xMin = xTemp; + } else if (xMax < xTemp) { + xMax = xTemp; + } + + xMin -= radius; + xMax += radius; + + if (resultPos.x < xMin || xMax < resultPos.x) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + if (CollisionPoly_CheckXIntersectApprox(curPoly, vtxList, pos->y, resultPos.z, &intersect)) { + if (fabsf(intersect - resultPos.x) <= radius / temp_f16) { + if ((intersect - resultPos.x) * nx <= 4.0f) { + BgCheck_ComputeWallDisplacement(colCtx, curPoly, &resultPos.x, &resultPos.z, nx, ny, nz, + invNormalXZ, planeDist, radius, outPoly); + result = true; + } + } + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + + *outX = resultPos.x; + *outZ = resultPos.z; + return result; +} + +/** + * Tests for collision with a static poly ceiling + * returns true if a collision occurs, else false + * `outPoly` returns the poly collided with + * `outY` returns the y coordinate needed to not collide with `outPoly` + */ +s32 BgCheck_CheckStaticCeiling(StaticLookup* lookup, u16 xpFlags, CollisionContext* colCtx, f32* outY, Vec3f* pos, + f32 checkHeight, CollisionPoly** outPoly) { + s32 result = false; + u16 nextId; + CollisionPoly* curPoly; + CollisionPoly* polyList; + f32 ceilingY; + Vec3s* vtxList; + SSNode* curNode; + s32 curPolyId; + + if (lookup->ceiling.head == SS_NULL) { + return false; + } + curNode = &colCtx->polyNodes.tbl[lookup->ceiling.head]; + polyList = colCtx->colHeader->polyList; + vtxList = colCtx->colHeader->vtxList; + + *outY = pos->y; + + while (true) { + curPolyId = curNode->polyId; + if (COLPOLY_VIA_FLAG_TEST(colCtx->colHeader->polyList[curPolyId].flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + curPoly = &polyList[curPolyId]; + + if (CollisionPoly_CheckYIntersectApprox2(curPoly, vtxList, pos->x, pos->z, &ceilingY)) { + f32 intersectDist = ceilingY - *outY; + f32 ny = COLPOLY_GET_NORMAL(curPoly->normal.y); + + if (intersectDist > 0.0f && intersectDist < checkHeight && intersectDist * ny <= 0) { + *outY = ceilingY - checkHeight; + *outPoly = curPoly; + result = true; + } + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + return result; +} + +/** + * Tests if line `posA` to `posB` intersects with a static poly in list `ssList`. Uses polyCheckTbl + * returns true if such a poly exists, else false + * `outPoly` returns the pointer of the poly intersected + * `posB` and `outPos` returns the point of intersection with `outPoly` + * `outDistSq` returns the squared distance from `posA` to the point of intersect + */ +s32 BgCheck_CheckLineAgainstSSList(SSList* ssList, CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, Vec3f* posA, + Vec3f* posB, Vec3f* outPos, CollisionPoly** outPoly, f32* outDistSq, f32 chkDist, + s32 bccFlags) { + SSNode* curNode; + u8* checkedPoly; + Vec3f polyIntersect; + CollisionPoly* polyList; + CollisionPoly* curPoly; + s32 result; + f32 minY; + f32 distSq; + s16 polyId; + + result = false; + polyList = colCtx->colHeader->polyList; + if (ssList->head == SS_NULL) { + return result; + } + + curNode = &colCtx->polyNodes.tbl[ssList->head]; + while (true) { + polyId = curNode->polyId; + checkedPoly = &colCtx->polyNodes.polyCheckTbl[polyId]; + + if (*checkedPoly == true || COLPOLY_VIA_FLAG_TEST(polyList[polyId].flags_vIA, xpFlags1) || + !(xpFlags2 == 0 || COLPOLY_VIA_FLAG_TEST(polyList[polyId].flags_vIA, xpFlags2))) { + + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &colCtx->polyNodes.tbl[curNode->next]; + continue; + } + } + *checkedPoly = true; + curPoly = &polyList[polyId]; + minY = CollisionPoly_GetMinY(curPoly, colCtx->colHeader->vtxList); + if (posA->y < minY && posB->y < minY) { + break; + } + if (CollisionPoly_LineVsPoly(curPoly, colCtx->colHeader->vtxList, posA, posB, &polyIntersect, + (bccFlags & BGCHECK_CHECK_ONE_FACE) != 0, chkDist)) { + distSq = Math3D_Vec3fDistSq(posA, &polyIntersect); + if (distSq < *outDistSq) { + + *outDistSq = distSq; + *outPos = polyIntersect; + *posB = polyIntersect; + *outPoly = curPoly; + result = true; + } + } + if (curNode->next == SS_NULL) { + break; + } + curNode = &colCtx->polyNodes.tbl[curNode->next]; + } + return result; +} + +/** + * Tests if line `posA` to `posB` intersects with a static poly in `lookup`. Uses polyCheckTbl + * returns true if such a poly exists, else false + * `outPoly` returns the pointer of the poly intersected + * `posB` and `outPos` returns the point of intersection with `outPoly` + * `outDistSq` returns the squared distance from `posA` to the point of intersect + */ +s32 BgCheck_CheckLineInSubdivision(StaticLookup* lookup, CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, + Vec3f* posA, Vec3f* posB, Vec3f* outPos, CollisionPoly** outPoly, f32 chkDist, + f32* outDistSq, u32 bccFlags) { + s32 result = false; + + if ((bccFlags & BGCHECK_CHECK_FLOOR) && lookup->floor.head != SS_NULL) { + if (BgCheck_CheckLineAgainstSSList(&lookup->floor, colCtx, xpFlags1, xpFlags2, posA, posB, outPos, outPoly, + outDistSq, chkDist, bccFlags)) { + result = true; + } + } + + if ((bccFlags & BGCHECK_CHECK_WALL) && lookup->wall.head != SS_NULL) { + if (BgCheck_CheckLineAgainstSSList(&lookup->wall, colCtx, xpFlags1, xpFlags2, posA, posB, outPos, outPoly, + outDistSq, chkDist, bccFlags)) { + result = true; + } + } + + if ((bccFlags & BGCHECK_CHECK_CEILING) && lookup->ceiling.head != SS_NULL) { + if (BgCheck_CheckLineAgainstSSList(&lookup->ceiling, colCtx, xpFlags1, xpFlags2, posA, posB, outPos, outPoly, + outDistSq, chkDist, bccFlags)) { + result = true; + } + } + return result; +} + +/** + * Get first static poly intersecting sphere `center` `radius` from list `node` + * returns true if any poly intersects the sphere, else returns false + * `outPoly` returns the pointer of the first poly found that intersects + */ +s32 BgCheck_SphVsFirstStaticPolyList(SSNode* node, u16 xpFlags, CollisionContext* colCtx, Vec3f* center, f32 radius, + CollisionPoly** outPoly) { + CollisionPoly* polyList = colCtx->colHeader->polyList; + Vec3s* vtxList = colCtx->colHeader->vtxList; + CollisionPoly* curPoly; + u16 nextId; + s16 curPolyId; + + while (true) { + curPolyId = node->polyId; + curPoly = &polyList[curPolyId]; + if (COLPOLY_VIA_FLAG_TEST(colCtx->colHeader->polyList[curPolyId].flags_vIA, xpFlags)) { + if (node->next == SS_NULL) { + break; + } else { + node = &colCtx->polyNodes.tbl[node->next]; + continue; + } + } + + if (center->y + radius < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIA)].y && + center->y + radius < vtxList[COLPOLY_VTX_INDEX(curPoly->flags_vIB)].y && + center->y + radius < vtxList[curPoly->vIC].y) { + break; + } + + if (CollisionPoly_SphVsPoly(curPoly, vtxList, center, radius)) { + *outPoly = curPoly; + return true; + } + if (node->next == SS_NULL) { + break; + } + node = &colCtx->polyNodes.tbl[node->next]; + } + return false; +} + +/** + * Get first static poly intersecting sphere `center` `radius` within `lookup` + * returns true if any poly intersects the sphere, else false + * `outPoly` returns the first poly found that intersects + */ +s32 BgCheck_SphVsFirstStaticPoly(StaticLookup* lookup, u16 xpFlags, CollisionContext* colCtx, Vec3f* center, f32 radius, + CollisionPoly** outPoly, u16 bciFlags) { + if (lookup->floor.head != SS_NULL && !(bciFlags & BGCHECK_IGNORE_FLOOR) && + BgCheck_SphVsFirstStaticPolyList(&colCtx->polyNodes.tbl[lookup->floor.head], xpFlags, colCtx, center, radius, + outPoly)) { + return true; + } + + if (lookup->wall.head != SS_NULL && !(bciFlags & BGCHECK_IGNORE_WALL) && + BgCheck_SphVsFirstStaticPolyList(&colCtx->polyNodes.tbl[lookup->wall.head], xpFlags, colCtx, center, radius, + outPoly)) { + return true; + } + + if (lookup->ceiling.head != SS_NULL && !(bciFlags & BGCHECK_IGNORE_CEILING) && + BgCheck_SphVsFirstStaticPolyList(&colCtx->polyNodes.tbl[lookup->ceiling.head], xpFlags, colCtx, center, radius, + outPoly)) { + return true; + } + + return false; +} + +/** + * Get StaticLookup from `pos` + * Does not return NULL + */ +StaticLookup* BgCheck_GetNearestStaticLookup(CollisionContext* colCtx, StaticLookup* lookupTbl, Vec3f* pos) { + Vec3i sector; + s32 subdivAmountX; + + BgCheck_GetStaticLookupIndicesFromPos(colCtx, pos, §or); + subdivAmountX = colCtx->subdivAmount.x; + return (sector.z * subdivAmountX) * colCtx->subdivAmount.y + lookupTbl + sector.x + sector.y * subdivAmountX; +} + +/** + * Get StaticLookup from `pos` + * Returns NULL if just outside the mesh bounding box + */ +StaticLookup* BgCheck_GetStaticLookup(CollisionContext* colCtx, StaticLookup* lookupTbl, Vec3f* pos) { + Vec3i sector; + s32 subdivAmountX; + + if (!BgCheck_PosInStaticBoundingBox(colCtx, pos)) { + return NULL; + } + BgCheck_GetStaticLookupIndicesFromPos(colCtx, pos, §or); + subdivAmountX = colCtx->subdivAmount.x; + return (sector.z * subdivAmountX) * colCtx->subdivAmount.y + lookupTbl + sector.x + sector.y * subdivAmountX; +} + +/** + * Get StaticLookup subdivision indices from `pos` + * `sector` returns the subdivision x,y,z indices containing or is nearest to `pos` + */ +void BgCheck_GetStaticLookupIndicesFromPos(CollisionContext* colCtx, Vec3f* pos, Vec3i* sector) { + sector->x = (pos->x - colCtx->minBounds.x) * colCtx->subdivLengthInv.x; + sector->y = (pos->y - colCtx->minBounds.y) * colCtx->subdivLengthInv.y; + sector->z = (pos->z - colCtx->minBounds.z) * colCtx->subdivLengthInv.z; + + if (sector->x < 0) { + sector->x = 0; + } else if (sector->x >= colCtx->subdivAmount.x) { + sector->x = colCtx->subdivAmount.x - 1; + } + + if (sector->y < 0) { + sector->y = 0; + } else if (sector->y >= colCtx->subdivAmount.y) { + sector->y = colCtx->subdivAmount.y - 1; + } + + if (sector->z < 0) { + sector->z = 0; + } else if (sector->z >= colCtx->subdivAmount.z) { + sector->z = colCtx->subdivAmount.z - 1; + } +} + +/** + * Get negative bias subdivision indices + * decrements indices if `pos` is within BGCHECK_SUBDIV_OVERLAP units of the negative subdivision boundary + * `sx`, `sy`, `sz` returns the subdivision x, y, z indices + */ +void BgCheck_GetSubdivisionMinBounds(CollisionContext* colCtx, Vec3f* pos, s32* sx, s32* sy, s32* sz) { + f32 dx = pos->x - colCtx->minBounds.x; + f32 dy = pos->y - colCtx->minBounds.y; + f32 dz = pos->z - colCtx->minBounds.z; + + *sx = dx * colCtx->subdivLengthInv.x; + *sy = dy * colCtx->subdivLengthInv.y; + *sz = dz * colCtx->subdivLengthInv.z; + + if (((s32)dx % (s32)colCtx->subdivLength.x < BGCHECK_SUBDIV_OVERLAP) && (*sx > 0)) { + *sx -= 1; + } + + if (((s32)dy % (s32)colCtx->subdivLength.y < BGCHECK_SUBDIV_OVERLAP) && (*sy > 0)) { + *sy -= 1; + } + + if (((s32)dz % (s32)colCtx->subdivLength.z < BGCHECK_SUBDIV_OVERLAP) && (*sz > 0)) { + *sz -= 1; + } +} + +/** + * Get positive bias subdivision indices + * increments indicies if `pos` is within BGCHECK_SUBDIV_OVERLAP units of the postive subdivision boundary + * `sx`, `sy`, `sz` returns the subdivision x, y, z indices + */ +void BgCheck_GetSubdivisionMaxBounds(CollisionContext* colCtx, Vec3f* pos, s32* sx, s32* sy, s32* sz) { + f32 dx = pos->x - colCtx->minBounds.x; + f32 dy = pos->y - colCtx->minBounds.y; + f32 dz = pos->z - colCtx->minBounds.z; + + *sx = dx * colCtx->subdivLengthInv.x; + *sy = dy * colCtx->subdivLengthInv.y; + *sz = dz * colCtx->subdivLengthInv.z; + + if (((s32)colCtx->subdivLength.x - BGCHECK_SUBDIV_OVERLAP < (s32)dx % (s32)colCtx->subdivLength.x) && + (*sx < colCtx->subdivAmount.x - 1)) { + *sx += 1; + } + + if (((s32)colCtx->subdivLength.y - BGCHECK_SUBDIV_OVERLAP < (s32)dy % (s32)colCtx->subdivLength.y) && + (*sy < colCtx->subdivAmount.y - 1)) { + *sy += 1; + } + + if (((s32)colCtx->subdivLength.z - BGCHECK_SUBDIV_OVERLAP < (s32)dz % (s32)colCtx->subdivLength.z) && + (*sz < colCtx->subdivAmount.z - 1)) { + *sz += 1; + } +} + +/** + * Calculate the subdivision index bounding box for CollisionPoly `polyId` + * `subdivMinX`, `subdivMinY`, `subdivMinZ` returns the minimum subdivision x, y, z indices + * `subdivMaxX`, `subdivMaxY`, `subdivMaxZ` returns the maximum subdivision x, y, z indices + */ +void BgCheck_GetPolySubdivisionBounds(CollisionContext* colCtx, Vec3s* vtxList, CollisionPoly* polyList, + s32* subdivMinX, s32* subdivMinY, s32* subdivMinZ, s32* subdivMaxX, + s32* subdivMaxY, s32* subdivMaxZ, s16 polyId) { + u16* vtxDataTemp; + Vec3f minVtx; + Vec3f maxVtx; + + f32 x; + f32 y; + f32 z; + + Vec3s* vtx; + s16 vtxId = COLPOLY_VTX_INDEX(polyList[polyId].vtxData[0]); + + Math_Vec3s_ToVec3f(&maxVtx, &vtxList[vtxId]); + Math_Vec3f_Copy(&minVtx, &maxVtx); + + for (vtxDataTemp = polyList[polyId].vtxData + 1; vtxDataTemp < polyList[polyId].vtxData + 3; vtxDataTemp++) { + vtxId = COLPOLY_VTX_INDEX(*vtxDataTemp); + vtx = &vtxList[vtxId]; + x = vtx->x; + y = vtx->y; + z = vtx->z; + + if (minVtx.x > x) { + minVtx.x = x; + } else if (maxVtx.x < x) { + maxVtx.x = x; + } + + if (minVtx.y > y) { + minVtx.y = y; + } else if (maxVtx.y < y) { + maxVtx.y = y; + } + + if (minVtx.z > z) { + minVtx.z = z; + } else if (maxVtx.z < z) { + maxVtx.z = z; + } + } + BgCheck_GetSubdivisionMinBounds(colCtx, &minVtx, subdivMinX, subdivMinY, subdivMinZ); + BgCheck_GetSubdivisionMaxBounds(colCtx, &maxVtx, subdivMaxX, subdivMaxY, subdivMaxZ); +} + +/** + * Test if poly `polyList`[`polyId`] intersects cube `min` `max` + * returns true if the poly intersects the cube, else false + */ +s32 BgCheck_PolyIntersectsSubdivision(Vec3f* min, Vec3f* max, CollisionPoly* polyList, Vec3s* vtxList, s16 polyId) { + f32 intersect; + Vec3f va2; + Vec3f vb2; + Vec3f vc2; + CollisionPoly* poly; + f32 nx; + f32 ny; + f32 nz; + f32 dist; + Vec3f va; + Vec3f vb; + Vec3f vc; + s32 flags[3]; + + flags[0] = flags[1] = 0; + poly = &polyList[polyId]; + + BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)], &va); + flags[0] = Math3D_PointRelativeToCubeFaces(&va, min, max); + if (flags[0] == 0) { + return true; + } + + BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)], &vb); + flags[1] = Math3D_PointRelativeToCubeFaces(&vb, min, max); + if (flags[1] == 0) { + return true; + } + + BgCheck_Vec3sToVec3f(&vtxList[poly->vIC], &vc); + flags[2] = Math3D_PointRelativeToCubeFaces(&vc, min, max); + if (flags[2] == 0) { + return true; + } + + if (flags[0] & flags[1] & flags[2]) { + return false; + } + + flags[0] |= Math3D_PointRelativeToCubeEdges(&va, min, max) << 8; + flags[1] |= Math3D_PointRelativeToCubeEdges(&vb, min, max) << 8; + flags[2] |= Math3D_PointRelativeToCubeEdges(&vc, min, max) << 8; + if (flags[0] & flags[1] & flags[2]) { + return false; + } + + flags[0] |= Math3D_PointRelativeToCubeVertices(&va, min, max) << 0x18; + flags[1] |= Math3D_PointRelativeToCubeVertices(&vb, min, max) << 0x18; + flags[2] |= Math3D_PointRelativeToCubeVertices(&vc, min, max) << 0x18; + if (flags[0] & flags[1] & flags[2]) { + return false; + } + + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + dist = poly->dist; + + if (Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->z, min->x, &intersect, min->y, + max->y) || + Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->z, min->x, &intersect, min->y, + max->y) || + Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->z, max->x, &intersect, min->y, + max->y) || + Math3D_TriChkLineSegParaYIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->z, max->x, &intersect, min->y, + max->y)) { + return true; + } + if (Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->x, min->y, &intersect, min->z, + max->z) || + Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->x, max->y, &intersect, min->z, + max->z) || + Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->x, min->y, &intersect, min->z, + max->z) || + Math3D_TriChkLineSegParaZIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->x, max->y, &intersect, min->z, + max->z)) { + return true; + } + if (Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->y, min->z, &intersect, min->x, + max->x) || + Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, min->y, max->z, &intersect, min->x, + max->x) || + Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->y, min->z, &intersect, min->x, + max->x) || + Math3D_TriChkLineSegParaXIntersect(&va, &vb, &vc, nx, ny, nz, dist, max->y, max->z, &intersect, min->x, + max->x)) { + return true; + } + + BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)], &va2); + BgCheck_Vec3sToVec3f(&vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)], &vb2); + BgCheck_Vec3sToVec3f(&vtxList[poly->vIC], &vc2); + if (Math3D_LineVsCube(min, max, &va2, &vb2) || Math3D_LineVsCube(min, max, &vb2, &vc2) || + Math3D_LineVsCube(min, max, &vc2, &va2)) { + return true; + } + return false; +} + +/** + * Initialize StaticLookup Table + * returns size of table, in bytes + */ +u32 BgCheck_InitializeStaticLookup(CollisionContext* colCtx, GlobalContext* globalCtx, StaticLookup* lookupTbl) { + Vec3s* vtxList; + CollisionPoly* polyList; + s32 polyMax; + s32 polyIdx; + s32 sx; + s32 sy; + s32 sz; + // subdivMin indices + s32 sxMin; + s32 syMin; + s32 szMin; + // subdivMax indices + s32 sxMax; + s32 syMax; + s32 szMax; + // subdiv min/max bounds for adding a poly + Vec3f curSubdivMin; + Vec3f curSubdivMax; + CollisionHeader* colHeader = colCtx->colHeader; + StaticLookup* spA4; + StaticLookup* phi_fp; + StaticLookup* phi_s0; + s32 sp98; + f32 subdivLengthX; + f32 subdivLengthY; + f32 subdivLengthZ; + + for (spA4 = lookupTbl; + spA4 < (colCtx->subdivAmount.x * colCtx->subdivAmount.y * colCtx->subdivAmount.z + lookupTbl); spA4++) { + spA4->floor.head = SS_NULL; + spA4->wall.head = SS_NULL; + spA4->ceiling.head = SS_NULL; + } + + polyMax = colHeader->numPolygons; + vtxList = colHeader->vtxList; + polyList = colHeader->polyList; + sp98 = colCtx->subdivAmount.x * colCtx->subdivAmount.y; + subdivLengthX = colCtx->subdivLength.x + (2 * BGCHECK_SUBDIV_OVERLAP); + subdivLengthY = colCtx->subdivLength.y + (2 * BGCHECK_SUBDIV_OVERLAP); + subdivLengthZ = colCtx->subdivLength.z + (2 * BGCHECK_SUBDIV_OVERLAP); + + for (polyIdx = 0; polyIdx < polyMax; polyIdx++) { + BgCheck_GetPolySubdivisionBounds(colCtx, vtxList, polyList, &sxMin, &syMin, &szMin, &sxMax, &syMax, &szMax, + polyIdx); + spA4 = szMin * sp98 + lookupTbl; + curSubdivMin.z = (colCtx->subdivLength.z * szMin + colCtx->minBounds.z) - BGCHECK_SUBDIV_OVERLAP; + curSubdivMax.z = curSubdivMin.z + subdivLengthZ; + + for (sz = szMin; sz < szMax + 1; sz++) { + phi_fp = (colCtx->subdivAmount.x * syMin) + spA4; + curSubdivMin.y = (colCtx->subdivLength.y * syMin + colCtx->minBounds.y) - BGCHECK_SUBDIV_OVERLAP; + curSubdivMax.y = curSubdivMin.y + subdivLengthY; + + for (sy = syMin; sy < syMax + 1; sy++) { + phi_s0 = sxMin + phi_fp; + curSubdivMin.x = (colCtx->subdivLength.x * sxMin + colCtx->minBounds.x) - BGCHECK_SUBDIV_OVERLAP; + curSubdivMax.x = curSubdivMin.x + subdivLengthX; + + for (sx = sxMin; sx < sxMax + 1; sx++) { + if (BgCheck_PolyIntersectsSubdivision(&curSubdivMin, &curSubdivMax, polyList, vtxList, polyIdx)) { + StaticLookup_AddPoly(phi_s0, colCtx, polyList, vtxList, polyIdx); + } + curSubdivMin.x += colCtx->subdivLength.x; + curSubdivMax.x += colCtx->subdivLength.x; + phi_s0++; + } + curSubdivMin.y += colCtx->subdivLength.y; + curSubdivMax.y += colCtx->subdivLength.y; + phi_fp += colCtx->subdivAmount.x; + } + curSubdivMin.z += colCtx->subdivLength.z; + curSubdivMax.z += colCtx->subdivLength.z; + spA4 += sp98; + } + } + return colCtx->polyNodes.count * sizeof(SSNode); +} + +/** + * Is current scene a SPOT scene + */ +s32 BgCheck_IsSpotScene(GlobalContext* globalCtx) { + static s16 spotScenes[] = { + SCENE_SPOT00, SCENE_SPOT01, SCENE_SPOT02, SCENE_SPOT03, SCENE_SPOT04, SCENE_SPOT05, SCENE_SPOT06, + SCENE_SPOT07, SCENE_SPOT08, SCENE_SPOT09, SCENE_SPOT10, SCENE_SPOT11, SCENE_SPOT12, SCENE_SPOT13, + SCENE_SPOT15, SCENE_SPOT16, SCENE_SPOT17, SCENE_SPOT18, SCENE_SPOT20, + }; + s16* i; + + for (i = spotScenes; i < spotScenes + ARRAY_COUNT(spotScenes); i++) { + if (globalCtx->sceneNum == *i) { + return true; + } + } + return false; +} + +typedef struct { + s16 sceneId; + u32 memSize; +} BgCheckSceneMemEntry; + +/** + * Get custom scene memSize + */ +s32 BgCheck_TryGetCustomMemsize(s32 sceneId, u32* memSize) { + static BgCheckSceneMemEntry sceneMemList[] = { + { SCENE_SPOT00, 0xB798 }, { SCENE_GANON_FINAL, 0x78C8 }, { SCENE_GANON_DEMO, 0x70C8 }, + { SCENE_JYASINBOSS, 0xACC8 }, { SCENE_KENJYANOMA, 0x70C8 }, { SCENE_JYASINZOU, 0x16CC8 }, + { SCENE_HIDAN, 0x198C8 }, { SCENE_GANON_BOSS, 0x84C8 }, + }; + s32 i; + + for (i = 0; i < ARRAY_COUNT(sceneMemList); i++) { + if (sceneId == sceneMemList[i].sceneId) { + *memSize = sceneMemList[i].memSize; + return true; + } + } + return false; +} + +/** + * Compute subdivLength for scene mesh lookup, for a single dimension + */ +void BgCheck_SetSubdivisionDimension(f32 min, s32 subdivAmount, f32* max, f32* subdivLength, f32* subdivLengthInv) { + f32 length = (*max - min); + + *subdivLength = (s32)(length / subdivAmount) + 1; + *subdivLength = CLAMP_MIN(*subdivLength, BGCHECK_SUBDIV_MIN); + *subdivLengthInv = 1.0f / *subdivLength; + + *max = *subdivLength * subdivAmount + min; +} + +typedef struct { + s16 sceneId; + Vec3s subdivAmount; + s32 nodeListMax; // if -1, dynamically compute max nodes +} BgCheckSceneSubdivisionEntry; + +/** + * Allocate CollisionContext + */ +void BgCheck_Allocate(CollisionContext* colCtx, GlobalContext* globalCtx, CollisionHeader* colHeader) { + static BgCheckSceneSubdivisionEntry sceneSubdivisionList[] = { + { SCENE_HAKADAN, { 23, 7, 14 }, -1 }, + { SCENE_BMORI1, { 38, 1, 38 }, -1 }, + }; + u32 tblMax; + u32 memSize; + u32 lookupTblMemSize; + SSNodeList* nodeList; + s32 useCustomSubdivisions; + u32 customMemSize; + s32 customNodeListMax; + s32 i; + + colCtx->colHeader = colHeader; + customNodeListMax = -1; + + // "/*---------------- BGCheck Buffer Memory Size -------------*/\n" + osSyncPrintf("/*---------------- BGCheck バッファーメモリサイズ -------------*/\n"); + + if (YREG(15) == 0x10 || YREG(15) == 0x20 || YREG(15) == 0x30 || YREG(15) == 0x40) { + if (globalCtx->sceneNum == SCENE_MALON_STABLE) { + // "/* BGCheck LonLon Size %dbyte */\n" + osSyncPrintf("/* BGCheck LonLonサイズ %dbyte */\n", 0x3520); + colCtx->memSize = 0x3520; + } else { + // "/* BGCheck Mini Size %dbyte */\n" + osSyncPrintf("/* BGCheck ミニサイズ %dbyte */\n", 0x4E20); + colCtx->memSize = 0x4E20; + } + colCtx->dyna.polyNodesMax = 500; + colCtx->dyna.polyListMax = 256; + colCtx->dyna.vtxListMax = 256; + colCtx->subdivAmount.x = 2; + colCtx->subdivAmount.y = 2; + colCtx->subdivAmount.z = 2; + } else if (BgCheck_IsSpotScene(globalCtx) == true) { + colCtx->memSize = 0xF000; + // "/* BGCheck Spot Size %dbyte */\n" + osSyncPrintf("/* BGCheck Spot用サイズ %dbyte */\n", 0xF000); + colCtx->dyna.polyNodesMax = 1000; + colCtx->dyna.polyListMax = 512; + colCtx->dyna.vtxListMax = 512; + colCtx->subdivAmount.x = 16; + colCtx->subdivAmount.y = 4; + colCtx->subdivAmount.z = 16; + } else { + if (BgCheck_TryGetCustomMemsize(globalCtx->sceneNum, &customMemSize)) { + colCtx->memSize = customMemSize; + } else { + colCtx->memSize = 0x1CC00; + } + // "/* BGCheck Normal Size %dbyte */\n" + osSyncPrintf("/* BGCheck ノーマルサイズ %dbyte */\n", colCtx->memSize); + colCtx->dyna.polyNodesMax = 1000; + colCtx->dyna.polyListMax = 512; + colCtx->dyna.vtxListMax = 512; + useCustomSubdivisions = false; + + for (i = 0; i < ARRAY_COUNT(sceneSubdivisionList); i++) { + if (globalCtx->sceneNum == sceneSubdivisionList[i].sceneId) { + colCtx->subdivAmount.x = sceneSubdivisionList[i].subdivAmount.x; + colCtx->subdivAmount.y = sceneSubdivisionList[i].subdivAmount.y; + colCtx->subdivAmount.z = sceneSubdivisionList[i].subdivAmount.z; + useCustomSubdivisions = true; + customNodeListMax = sceneSubdivisionList[i].nodeListMax; + } + } + if (useCustomSubdivisions == false) { + colCtx->subdivAmount.x = 16; + colCtx->subdivAmount.y = 4; + colCtx->subdivAmount.z = 16; + } + } + colCtx->lookupTbl = THA_AllocEndAlign( + &globalCtx->state.tha, + colCtx->subdivAmount.x * sizeof(StaticLookup) * colCtx->subdivAmount.y * colCtx->subdivAmount.z, ~1); + if (colCtx->lookupTbl == NULL) { + LogUtils_HungupThread("../z_bgcheck.c", 4176); + } + colCtx->minBounds.x = colCtx->colHeader->minBounds.x; + colCtx->minBounds.y = colCtx->colHeader->minBounds.y; + colCtx->minBounds.z = colCtx->colHeader->minBounds.z; + colCtx->maxBounds.x = colCtx->colHeader->maxBounds.x; + colCtx->maxBounds.y = colCtx->colHeader->maxBounds.y; + colCtx->maxBounds.z = colCtx->colHeader->maxBounds.z; + BgCheck_SetSubdivisionDimension(colCtx->minBounds.x, colCtx->subdivAmount.x, &colCtx->maxBounds.x, + &colCtx->subdivLength.x, &colCtx->subdivLengthInv.x); + BgCheck_SetSubdivisionDimension(colCtx->minBounds.y, colCtx->subdivAmount.y, &colCtx->maxBounds.y, + &colCtx->subdivLength.y, &colCtx->subdivLengthInv.y); + BgCheck_SetSubdivisionDimension(colCtx->minBounds.z, colCtx->subdivAmount.z, &colCtx->maxBounds.z, + &colCtx->subdivLength.z, &colCtx->subdivLengthInv.z); + memSize = colCtx->subdivAmount.x * sizeof(StaticLookup) * colCtx->subdivAmount.y * colCtx->subdivAmount.z + + colCtx->colHeader->numPolygons * sizeof(u8) + colCtx->dyna.polyNodesMax * sizeof(SSNode) + + colCtx->dyna.polyListMax * sizeof(CollisionPoly) + colCtx->dyna.vtxListMax * sizeof(Vec3s) + + sizeof(CollisionContext); + if (customNodeListMax > 0) { + // tblMax is set without checking if customNodeListMax will result in a memory overflow + // this is a non-issue as long as sceneSubdivisionList.nodeListMax is -1 + tblMax = customNodeListMax; + } else { + if (colCtx->memSize < memSize) { + LogUtils_HungupThread("../z_bgcheck.c", 4230); + } + tblMax = (colCtx->memSize - memSize) / sizeof(SSNode); + } + + SSNodeList_Initialize(&colCtx->polyNodes); + SSNodeList_Alloc(globalCtx, &colCtx->polyNodes, tblMax, colCtx->colHeader->numPolygons); + + lookupTblMemSize = BgCheck_InitializeStaticLookup(colCtx, globalCtx, colCtx->lookupTbl); + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("/*---結局 BG使用サイズ %dbyte---*/\n", memSize + lookupTblMemSize); + osSyncPrintf(VT_RST); + + DynaPoly_Init(globalCtx, &colCtx->dyna); + DynaPoly_Alloc(globalCtx, &colCtx->dyna); +} + +/** + * Get CollisionHeader + * original name: T_BGCheck_getBGDataInfo + */ +CollisionHeader* BgCheck_GetCollisionHeader(CollisionContext* colCtx, s32 bgId) { + if (bgId == BGCHECK_SCENE) { + return colCtx->colHeader; + } + if (bgId < 0 || bgId > BG_ACTOR_MAX) { + return NULL; + } + if (!(colCtx->dyna.bgActorFlags[bgId] & 1)) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + osSyncPrintf("T_BGCheck_getBGDataInfo():そのbg_actor_indexは使われておりません。index=%d\n"); + osSyncPrintf(VT_RST); + return NULL; + } + return colCtx->dyna.bgActors[bgId].colHeader; +} + +/** + * Test if pos is near collision boundaries + */ +s32 BgCheck_PosInStaticBoundingBox(CollisionContext* colCtx, Vec3f* pos) { + if (pos->x < (colCtx->minBounds.x - BGCHECK_SUBDIV_OVERLAP) || + (colCtx->maxBounds.x + BGCHECK_SUBDIV_OVERLAP) < pos->x || + pos->y < (colCtx->minBounds.y - BGCHECK_SUBDIV_OVERLAP) || + (colCtx->maxBounds.y + BGCHECK_SUBDIV_OVERLAP) < pos->y || + pos->z < (colCtx->minBounds.z - BGCHECK_SUBDIV_OVERLAP) || + (colCtx->maxBounds.z + BGCHECK_SUBDIV_OVERLAP) < pos->z) { + return false; + } + return true; +} + +/** + * Raycast Toward Floor + * returns the yIntersect of the nearest poly found directly below `pos`, or BGCHECK_Y_MIN if no floor detected + * returns the poly found in `outPoly`, and the bgId of the entity in `outBgId` + */ +f32 BgCheck_RaycastFloorImpl(GlobalContext* globalCtx, CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, + s32* outBgId, Vec3f* pos, Actor* actor, u32 arg7, f32 chkDist) { + + f32 yIntersectDyna; + s32* temp_a0; + StaticLookup* lookupTbl; + Vec3f checkPos; + StaticLookup* lookup; + DynaRaycast dynaRaycast; + f32 yIntersect; + + *outBgId = BGCHECK_SCENE; + *outPoly = NULL; + lookupTbl = colCtx->lookupTbl; + yIntersect = BGCHECK_Y_MIN; + checkPos = *pos; + + while (true) { + if (checkPos.y < colCtx->minBounds.y) { + break; + } + if (BgCheck_PosErrorCheck(&checkPos, "../z_bgcheck.c", 4410)) { + if (actor != NULL) { + osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id); + } + } + lookup = BgCheck_GetStaticLookup(colCtx, lookupTbl, &checkPos); + if (lookup == NULL) { + checkPos.y -= colCtx->subdivLength.y; + continue; + } + yIntersect = BgCheck_RaycastFloorStatic(lookup, colCtx, xpFlags, outPoly, pos, arg7, chkDist, BGCHECK_Y_MIN); + if (yIntersect > BGCHECK_Y_MIN) { + break; + } + checkPos.y -= colCtx->subdivLength.y; + } + + dynaRaycast.colCtx = colCtx; + dynaRaycast.xpFlags = xpFlags; + dynaRaycast.yIntersect = yIntersect; + dynaRaycast.pos = pos; + dynaRaycast.actor = actor; + dynaRaycast.unk_20 = arg7; + dynaRaycast.chkDist = chkDist; + dynaRaycast.globalCtx = globalCtx; + dynaRaycast.resultPoly = outPoly; + dynaRaycast.bgId = outBgId; + + yIntersectDyna = BgCheck_RaycastFloorDyna(&dynaRaycast); + + if (yIntersect < yIntersectDyna) { + yIntersect = yIntersectDyna; + } + + if (yIntersect != BGCHECK_Y_MIN && func_80041EC8(colCtx, *outPoly, *outBgId)) { + yIntersect -= 1.0f; + } + return yIntersect; +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_CameraRaycastFloor1(CollisionContext* colCtx, CollisionPoly** outPoly, Vec3f* pos) { + s32 bgId; + + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_CAMERA, outPoly, &bgId, pos, NULL, 0x1C, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor1(CollisionContext* colCtx, CollisionPoly** outPoly, Vec3f* pos) { + s32 bgId; + + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, &bgId, pos, NULL, 0x1C, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor2(GlobalContext* globalCtx, CollisionContext* colCtx, CollisionPoly** outPoly, + Vec3f* pos) { + s32 bgId; + + return BgCheck_RaycastFloorImpl(globalCtx, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, &bgId, pos, NULL, 0x1C, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor3(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, NULL, 0x1C, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor4(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, + Vec3f* pos) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor, 0x1C, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor5(GlobalContext* globalCtx, CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, + Actor* actor, Vec3f* pos) { + return BgCheck_RaycastFloorImpl(globalCtx, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor, 0x1C, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor6(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, Vec3f* pos, + f32 chkDist) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor, 0x1C, chkDist); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor7(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, + Vec3f* pos) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor, 0x06, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_AnyRaycastFloor1(CollisionContext* colCtx, CollisionPoly* outPoly, Vec3f* pos) { + CollisionPoly* tempPoly; + f32 result; + s32 bgId; + + result = BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_NONE, &tempPoly, &bgId, pos, NULL, 0x1C, 1.0f); + + if (tempPoly != NULL) { + *outPoly = *tempPoly; + } + return result; +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_AnyRaycastFloor2(CollisionContext* colCtx, CollisionPoly* outPoly, s32* bgId, Vec3f* pos) { + CollisionPoly* tempPoly; + f32 result = BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_NONE, &tempPoly, bgId, pos, NULL, 0x1C, 1.0f); + + if (tempPoly != NULL) { + *outPoly = *tempPoly; + } + return result; +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_CameraRaycastFloor2(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_CAMERA, outPoly, bgId, pos, NULL, 0x06, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor8(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Actor* actor, + Vec3f* pos) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, actor, 0x02, 1.0f); +} + +/** + * Public raycast toward floor + * returns yIntersect of the poly found, or BGCHECK_Y_MIN if no poly detected + */ +f32 BgCheck_EntityRaycastFloor9(CollisionContext* colCtx, CollisionPoly** outPoly, s32* bgId, Vec3f* pos) { + return BgCheck_RaycastFloorImpl(NULL, colCtx, COLPOLY_IGNORE_ENTITY, outPoly, bgId, pos, NULL, 0x06, 1.0f); +} + +/** + * Tests if moving from `posPrev` to `posNext` will collide with a "wall" + * `radius` is used to form a sphere for collision detection purposes + * `checkHeight` is the positive height above posNext to perform certain checks + * returns true if a collision is detected, else false + * `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner + */ +s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, + f32 radius, CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight, u8 argA) { + StaticLookup* lookupTbl; + f32 temp_f0; + s32 result; + CollisionPoly* poly; + f32 dx, dy, dz; // change between posPrev to posNext + Vec3f sphCenter; + s32 dynaPolyCollision; + Vec3f posIntersect; + s32 bgId; + f32 temp_f0_2; + f32 f32temp; + f32 nx2, nz2; + Vec3f checkLineNext; + Vec3f checkLinePrev; + f32 n2XZDist; + f32 n3XZDist; + f32 nx3, nz3; + s32 bccFlags; + Vec3f posIntersect2; + s32 bgId2; + f32 nx, ny, nz; // unit normal of polygon + + result = false; + *outBgId = BGCHECK_SCENE; + *outPoly = NULL; + lookupTbl = colCtx->lookupTbl; + *posResult = *posNext; + dx = posNext->x - posPrev->x; + dy = posNext->y - posPrev->y; + dz = posNext->z - posPrev->z; + + if (BgCheck_PosErrorCheck(posNext, "../z_bgcheck.c", 4831) == true || + BgCheck_PosErrorCheck(posPrev, "../z_bgcheck.c", 4832) == true) { + if (actor != NULL) { + osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id); + } + } + + // if there's movement on the xz plane, and argA flag is 0, + if ((dx != 0.0f || dz != 0.0f) && (argA & 1) == 0) { + if ((checkHeight + dy) < 5.0f) { + //! @bug checkHeight is not applied to posPrev/posNext + result = BgCheck_CheckLineImpl(colCtx, xpFlags, COLPOLY_IGNORE_NONE, posPrev, posNext, &posIntersect, &poly, + &bgId, actor, 1.0f, BGCHECK_CHECK_ALL & ~BGCHECK_CHECK_CEILING); + if (result) { + ny = COLPOLY_GET_NORMAL(poly->normal.y); + // if poly is floor, push result underneath the floor + if (ny > 0.5f) { + posResult->x = posIntersect.x; + if (checkHeight > 1.0f) { + posResult->y = posIntersect.y - 1.0f; + } else { + posResult->y = posIntersect.y - checkHeight; + } + posResult->z = posIntersect.z; + } + // poly is wall + else { + nx = COLPOLY_GET_NORMAL(poly->normal.x); + nz = COLPOLY_GET_NORMAL(poly->normal.z); + posResult->x = radius * nx + posIntersect.x; + posResult->y = radius * ny + posIntersect.y; + posResult->z = radius * nz + posIntersect.z; + } + *outPoly = poly; + *outBgId = bgId; + } + } else { + // if the radius is less than the distance travelled on the xz plane, also test for floor collisions + bccFlags = SQ(radius) < (SQ(dx) + SQ(dz)) + ? (BGCHECK_CHECK_ALL & ~BGCHECK_CHECK_CEILING) + : (BGCHECK_CHECK_ALL & ~BGCHECK_CHECK_FLOOR & ~BGCHECK_CHECK_CEILING); + + // perform a straight line test to see if a line at posNext.y + checkHeight from posPrev.xz to posNext.xz + // passes through any wall and possibly floor polys + checkLineNext = *posNext; + checkLineNext.y += checkHeight; + checkLinePrev = *posPrev; + checkLinePrev.y = checkLineNext.y; + result = BgCheck_CheckLineImpl(colCtx, xpFlags, COLPOLY_IGNORE_NONE, &checkLinePrev, &checkLineNext, + &posIntersect, &poly, &bgId, actor, 1.0f, bccFlags); + + if (result) { + nx2 = COLPOLY_GET_NORMAL(poly->normal.x); + nz2 = COLPOLY_GET_NORMAL(poly->normal.z); + n2XZDist = sqrtf(SQ(nx2) + SQ(nz2)); + + // if poly is not a "flat" floor or "flat" ceiling + if (!IS_ZERO(n2XZDist)) { + // normalize nx,nz and multiply each by the radius to go back to the other side of the wall + f32temp = 1.0f / n2XZDist; + temp_f0 = radius * f32temp; + posResult->x = temp_f0 * nx2 + posIntersect.x; + posResult->z = temp_f0 * nz2 + posIntersect.z; + *outPoly = poly; + *outBgId = bgId; + result = true; + } + } + } + } + + sphCenter = *posResult; + dynaPolyCollision = false; + sphCenter.y += checkHeight; + // test if sphere (sphCenter, radius) collides with a dynamic wall, displacing the x/z coordinates + if (BgCheck_SphVsDynaWall(colCtx, xpFlags, &posResult->x, &posResult->z, &sphCenter, radius, outPoly, outBgId, + actor)) { + result = true; + dynaPolyCollision = true; + sphCenter = *posResult; + sphCenter.y += checkHeight; + } + // test if sphere (sphCenter, radius) collides with a static wall, displacing the x/z coordinates + if (BgCheck_PosInStaticBoundingBox(colCtx, posNext) == true && + // possible bug? if the sphere's radius is smaller than the distance to a subdivision boundary, some static + // polys will be missed + BgCheck_SphVsStaticWall(BgCheck_GetNearestStaticLookup(colCtx, lookupTbl, posResult), colCtx, xpFlags, + &posResult->x, &posResult->z, &sphCenter, radius, outPoly)) { + *outBgId = BGCHECK_SCENE; + result = true; + } + // if a collision with a dyna poly was detected + if (dynaPolyCollision == true || *outBgId != BGCHECK_SCENE) { + if (BgCheck_CheckLineImpl(colCtx, xpFlags, COLPOLY_IGNORE_NONE, posPrev, posResult, &posIntersect2, &poly, + &bgId2, actor, 1.0f, BGCHECK_CHECK_ONE_FACE | BGCHECK_CHECK_WALL)) { + nx3 = COLPOLY_GET_NORMAL(poly->normal.x); + nz3 = COLPOLY_GET_NORMAL(poly->normal.z); + n3XZDist = sqrtf(SQ(nx3) + SQ(nz3)); + + // if poly is not a "flat" floor or "flat" ceiling + if (!IS_ZERO(n3XZDist)) { + // normalize nx,nz and multiply each by the radius to go back to the other side of the wall + f32temp = 1.0f / n3XZDist; + temp_f0_2 = radius * f32temp; + posResult->x = temp_f0_2 * nx3 + posIntersect2.x; + posResult->z = temp_f0_2 * nz3 + posIntersect2.z; + *outPoly = poly; + *outBgId = bgId2; + result = true; + } + } + } + return result; +} + +/** + * Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall" + * `radius` is used to form a sphere for collision detection purposes + * `checkHeight` is the positive height above posNext to perform certain checks + * returns true if a collision is detected, else false + * `outPoly` returns the closest poly detected + */ +s32 BgCheck_EntitySphVsWall1(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, f32 checkHeight) { + s32 bgId; + + return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, &bgId, + NULL, checkHeight, 0); +} + +/** + * Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall" + * `radius` is used to form a sphere for collision detection purposes + * `checkHeight` is the positive height above posNext to perform certain checks + * returns true if a collision is detected, else false + * `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner + */ +s32 BgCheck_EntitySphVsWall2(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, s32* outBgId, f32 checkHeight) { + return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, outBgId, + NULL, checkHeight, 0); +} + +/** + * Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall" + * `radius` is used to form a sphere for collision detection purposes + * `checkHeight` is the positive height above posNext to perform certain checks + * `actor` is the actor performing the check, allowing it to be skipped + * returns true if a collision is detected, else false + * `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner + */ +s32 BgCheck_EntitySphVsWall3(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight) { + return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, outBgId, + actor, checkHeight, 0); +} + +/*** + * Public. Tests if moving from `posPrev` to `posNext` will collide with a "wall" + * Skips a check that occurs only when moving on the xz plane + * `radius` is used to form a sphere for collision detection purposes + * `checkHeight` is the positive height above posNext to perform certain checks + * `actor` is the actor performing the check, allowing it to be skipped + * returns true if a collision is detected, else false + * `outPoly` returns the closest poly detected, while `outBgId` returns the poly owner + */ +s32 BgCheck_EntitySphVsWall4(CollisionContext* colCtx, Vec3f* posResult, Vec3f* posNext, Vec3f* posPrev, f32 radius, + CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 checkHeight) { + return BgCheck_CheckWallImpl(colCtx, COLPOLY_IGNORE_ENTITY, posResult, posNext, posPrev, radius, outPoly, outBgId, + actor, checkHeight, 1); +} + +/*** + * Tests for collision with a ceiling poly + * `checkHeight` should be a positive value + * returns true if a collision occurs, else false + * `outPoly` returns the poly collided with, while `outBgId` returns the owner of the poly + * `outY` returns the y coordinate of pos needed to not collide with `outPoly` + */ +s32 BgCheck_CheckCeilingImpl(CollisionContext* colCtx, u16 xpFlags, f32* outY, Vec3f* pos, f32 checkHeight, + CollisionPoly** outPoly, s32* outBgId, Actor* actor) { + StaticLookup* lookupTbl; + StaticLookup* lookup; + s32 result; + Vec3f posTemp; + f32 tempY; + + *outBgId = BGCHECK_SCENE; + *outY = pos->y; + if (BgCheck_PosErrorCheck(pos, "../z_bgcheck.c", 5206) == true) { + if (actor != NULL) { + osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id); + } + } + lookupTbl = colCtx->lookupTbl; + if (!BgCheck_PosInStaticBoundingBox(colCtx, pos)) { + return false; + } + + lookup = BgCheck_GetNearestStaticLookup(colCtx, lookupTbl, pos); + result = BgCheck_CheckStaticCeiling(lookup, xpFlags, colCtx, outY, pos, checkHeight, outPoly); + + posTemp = *pos; + posTemp.y = *outY; + tempY = *outY; + + if (BgCheck_CheckDynaCeiling(colCtx, xpFlags, &tempY, &posTemp, checkHeight, outPoly, outBgId, actor)) { + *outY = tempY; + result = true; + } + return result; +} + +/** + * Tests for collision with any ceiling poly + * `checkHeight` must be a positive value + * returns true if a collision occurs, else false + * `outY` returns the displaced y coordinate needed to not collide with the poly + */ +s32 BgCheck_AnyCheckCeiling(CollisionContext* colCtx, f32* outY, Vec3f* pos, f32 checkHeight) { + CollisionPoly* poly; + s32 bgId; + + return BgCheck_CheckCeilingImpl(colCtx, COLPOLY_IGNORE_NONE, outY, pos, checkHeight, &poly, &bgId, NULL); +} + +/** + * Tests for collision with any entity solid ceiling poly + * `checkHeight` must be a positive value + * returns true if a collision occurs, else false + * `outY` returns the displaced y coordinate needed to not collide with the poly + */ +s32 BgCheck_EntityCheckCeiling(CollisionContext* colCtx, f32* outY, Vec3f* pos, f32 checkHeight, + CollisionPoly** outPoly, s32* outBgId, Actor* actor) { + return BgCheck_CheckCeilingImpl(colCtx, COLPOLY_IGNORE_ENTITY, outY, pos, checkHeight, outPoly, outBgId, actor); +} + +/** + * Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + * `posB`? `posResult` returns the point of intersection + * `outPoly` returns the pointer to the intersected poly, while `outBgId` returns the entity the poly belongs to + */ +s32 BgCheck_CheckLineImpl(CollisionContext* colCtx, u16 xpFlags1, u16 xpFlags2, Vec3f* posA, Vec3f* posB, + Vec3f* posResult, CollisionPoly** outPoly, s32* outBgId, Actor* actor, f32 chkDist, + u32 bccFlags) { + StaticLookup* lookupTbl = colCtx->lookupTbl; + StaticLookup* iLookup; + s32 subdivMin[3]; + s32 subdivMax[3]; + s32 i; + s32 result; + f32 distSq; + Vec3f posBTemp = *posB; + Vec3f sectorMin; + Vec3f sectorMax; + s32 k; + StaticLookup* lookup; + s32 j; + StaticLookup* jLookup; + s32 temp_lo; + + *outBgId = BGCHECK_SCENE; + if (BgCheck_PosErrorCheck(posA, "../z_bgcheck.c", 5334) == true || + BgCheck_PosErrorCheck(posB, "../z_bgcheck.c", 5335) == true) { + if (actor != NULL) { + osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id); + } else { + osSyncPrintf("pself_actor == NULLで犯人不明\n"); + } + } + + BgCheck_ResetPolyCheckTbl(&colCtx->polyNodes, colCtx->colHeader->numPolygons); + BgCheck_GetStaticLookupIndicesFromPos(colCtx, posA, (Vec3i*)&subdivMin); + BgCheck_GetStaticLookupIndicesFromPos(colCtx, &posBTemp, (Vec3i*)&subdivMax); + *posResult = *posB; + result = false; + distSq = 1.0e38f; + *outPoly = NULL; + + if (subdivMin[0] != subdivMax[0] || subdivMin[1] != subdivMax[1] || subdivMin[2] != subdivMax[2]) { + for (i = 0; i < 3; i++) { + if (subdivMax[i] < subdivMin[i]) { + j = subdivMax[i]; + subdivMax[i] = subdivMin[i]; + subdivMin[i] = j; + } + } + temp_lo = colCtx->subdivAmount.x * colCtx->subdivAmount.y; + iLookup = lookupTbl + subdivMin[2] * temp_lo; + sectorMin.z = subdivMin[2] * colCtx->subdivLength.z + colCtx->minBounds.z; + sectorMax.z = colCtx->subdivLength.z + sectorMin.z; + + for (i = subdivMin[2]; i < subdivMax[2] + 1; i++) { + jLookup = iLookup + subdivMin[1] * colCtx->subdivAmount.x; + sectorMin.y = subdivMin[1] * colCtx->subdivLength.y + colCtx->minBounds.y; + sectorMax.y = colCtx->subdivLength.y + sectorMin.y; + + for (j = subdivMin[1]; j < subdivMax[1] + 1; j++) { + lookup = jLookup + subdivMin[0]; + sectorMin.x = subdivMin[0] * colCtx->subdivLength.x + colCtx->minBounds.x; + sectorMax.x = colCtx->subdivLength.x + sectorMin.x; + + for (k = subdivMin[0]; k < subdivMax[0] + 1; k++) { + if (Math3D_LineVsCube(§orMin, §orMax, posA, &posBTemp) == true && + BgCheck_CheckLineInSubdivision(lookup, colCtx, xpFlags1, xpFlags2, posA, &posBTemp, posResult, + outPoly, chkDist, &distSq, bccFlags)) { + result = true; + } + + lookup++; + sectorMin.x += colCtx->subdivLength.x; + sectorMax.x += colCtx->subdivLength.x; + } + + jLookup += colCtx->subdivAmount.x; + sectorMin.y += colCtx->subdivLength.y; + sectorMax.y += colCtx->subdivLength.y; + } + + iLookup += temp_lo; + sectorMin.z += colCtx->subdivLength.z; + sectorMax.z += colCtx->subdivLength.z; + } + } else if (BgCheck_PosInStaticBoundingBox(colCtx, posA) == false) { + return false; + } else { + result = + BgCheck_CheckLineInSubdivision(BgCheck_GetNearestStaticLookup(colCtx, lookupTbl, posA), colCtx, xpFlags1, + xpFlags2, posA, &posBTemp, posResult, outPoly, chkDist, &distSq, bccFlags); + if (result == true) { + distSq = Math3D_Vec3fDistSq(posResult, posA); + } + } + if ((bccFlags & BGCHECK_CHECK_DYNA) && + BgCheck_CheckLineAgainstDyna(colCtx, xpFlags1, posA, &posBTemp, posResult, outPoly, &distSq, outBgId, actor, + chkDist, bccFlags)) { + result = true; + } + return result; +} + +/** + * Get bccFlags + */ +u32 BgCheck_GetBccFlags(s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32 chkDyna) { + u32 result = 0; + + if (chkWall) { + result = BGCHECK_CHECK_WALL; + } + if (chkFloor) { + result |= BGCHECK_CHECK_FLOOR; + } + if (chkCeil) { + result |= BGCHECK_CHECK_CEILING; + } + if (chkOneFace) { + result |= BGCHECK_CHECK_ONE_FACE; + } + if (chkDyna) { + result |= BGCHECK_CHECK_DYNA; + } + return result; +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_CameraLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, + s32* bgId) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_CAMERA, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, + bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_CameraLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, + s32* bgId) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_NONE, COLPOLY_IGNORE_CAMERA, posA, posB, posResult, outPoly, + bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_EntityLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, + s32* bgId) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_ENTITY, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, + bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_EntityLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId, + Actor* actor) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_ENTITY, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, + bgId, actor, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_EntityLineTest3(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId, + Actor* actor, f32 chkDist) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_ENTITY, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, + bgId, actor, chkDist, + BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_ProjectileLineTest(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, + s32* bgId) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_PROJECTILES, COLPOLY_IGNORE_NONE, posA, posB, posResult, + outPoly, bgId, NULL, 1.0f, + BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_AnyLineTest1(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly, + s32 chkOneFace) { + return BgCheck_AnyLineTest2(colCtx, posA, posB, posResult, outPoly, true, true, true, chkOneFace); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_AnyLineTest2(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly, + s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace) { + s32 bgId; + + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_NONE, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, + &bgId, NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Public. Tests if a line from `posA` to `posB` intersects with a poly + * returns true if it does, else false + */ +s32 BgCheck_AnyLineTest3(CollisionContext* colCtx, Vec3f* posA, Vec3f* posB, Vec3f* posResult, CollisionPoly** outPoly, + s32 chkWall, s32 chkFloor, s32 chkCeil, s32 chkOneFace, s32* bgId) { + return BgCheck_CheckLineImpl(colCtx, COLPOLY_IGNORE_NONE, COLPOLY_IGNORE_NONE, posA, posB, posResult, outPoly, bgId, + NULL, 1.0f, BgCheck_GetBccFlags(chkWall, chkFloor, chkCeil, chkOneFace, true)); +} + +/** + * Get first poly intersecting sphere `center` `radius` + * ignores `actor` dyna poly + * returns true if any poly intersects the sphere, else false + * `outPoly` returns the pointer of the first poly found that intersects + * `outBgId` returns the bgId of the entity that owns `outPoly` + */ +s32 BgCheck_SphVsFirstPolyImpl(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, s32* outBgId, + Vec3f* center, f32 radius, Actor* actor, u16 bciFlags) { + StaticLookup* lookup; + + *outBgId = BGCHECK_SCENE; + if (BgCheck_PosErrorCheck(center, "../z_bgcheck.c", 5852) == true) { + if (actor != NULL) { + osSyncPrintf("こいつ,pself_actor->name %d\n", actor->id); + } + } + + lookup = BgCheck_GetStaticLookup(colCtx, colCtx->lookupTbl, center); + if (lookup == NULL) { + return false; + } else if (BgCheck_SphVsFirstStaticPoly(lookup, xpFlags, colCtx, center, radius, outPoly, bciFlags) || + BgCheck_SphVsFirstDynaPoly(colCtx, xpFlags, outPoly, outBgId, center, radius, actor, bciFlags)) { + return true; + } + return false; +} + +/** + * Public get first poly intersecting sphere `center` `radius` + */ +s32 BgCheck_SphVsFirstPoly(CollisionContext* colCtx, Vec3f* center, f32 radius) { + CollisionPoly* poly; + s32 bgId; + + return BgCheck_SphVsFirstPolyImpl(colCtx, COLPOLY_IGNORE_NONE, &poly, &bgId, center, radius, NULL, + BGCHECK_IGNORE_NONE); +} + +/** + * Public get first wall poly intersecting sphere `center` `radius` + */ +s32 BgCheck_SphVsFirstWall(CollisionContext* colCtx, Vec3f* center, f32 radius) { + CollisionPoly* poly; + s32 bgId; + + return BgCheck_SphVsFirstPolyImpl(colCtx, COLPOLY_IGNORE_NONE, &poly, &bgId, center, radius, NULL, + BGCHECK_IGNORE_FLOOR | BGCHECK_IGNORE_CEILING); +} + +/** + * Init SSNodeList + */ +void SSNodeList_Initialize(SSNodeList* this) { + this->max = 0; + this->count = 0; + this->tbl = NULL; + this->polyCheckTbl = NULL; +} + +/** + * Allocate SSNodeList + * tblMax is the number of SSNode records to allocate + * numPolys is the number of polygons defined within the CollisionHeader + */ +void SSNodeList_Alloc(GlobalContext* globalCtx, SSNodeList* this, s32 tblMax, s32 numPolys) { + this->max = tblMax; + this->count = 0; + this->tbl = THA_AllocEndAlign(&globalCtx->state.tha, tblMax * sizeof(SSNode), -2); + + ASSERT(this->tbl != NULL, "this->short_slist_node_tbl != NULL", "../z_bgcheck.c", 5975); + + this->polyCheckTbl = GameState_Alloc(&globalCtx->state, numPolys, "../z_bgcheck.c", 5979); + + ASSERT(this->polyCheckTbl != NULL, "this->polygon_check != NULL", "../z_bgcheck.c", 5981); +} + +/** + * Get next SSNodeList SSNode + */ +SSNode* SSNodeList_GetNextNode(SSNodeList* this) { + SSNode* result = &this->tbl[this->count]; + + this->count++; + ASSERT(this->count < this->max, "this->short_slist_node_last_index < this->short_slist_node_size", "../z_bgcheck.c", + 5998); + if (!(this->count < this->max)) { + return NULL; + } + return result; +} + +/** + * Get next SSNodeList SSNode index + */ +u16 SSNodeList_GetNextNodeIdx(SSNodeList* this) { + u16 new_index = this->count++; + + ASSERT(new_index < this->max, "new_index < this->short_slist_node_size", "../z_bgcheck.c", 6021); + return new_index; +} + +/** + * Initialize ScaleRotPos + */ +void ScaleRotPos_Initialize(ScaleRotPos* srp) { + srp->scale.x = srp->scale.y = srp->scale.z = 1.0f; + srp->pos.x = srp->pos.y = srp->pos.z = 0.0f; + srp->rot.x = srp->rot.y = srp->rot.z = 0; +} + +/** + * Set ScaleRotPos + */ +void ScaleRotPos_SetValue(ScaleRotPos* srp, Vec3f* scale, Vec3s* rot, Vec3f* pos) { + srp->scale = *scale; + srp->rot = *rot; + srp->pos = *pos; +} + +/** + * ScaleRotPos equality test + */ +s32 ScaleRotPos_Equals(ScaleRotPos* a, ScaleRotPos* b) { + if (a->scale.x != b->scale.x || a->scale.y != b->scale.y || a->scale.z != b->scale.z || a->rot.x != b->rot.x || + a->rot.y != b->rot.y || a->rot.z != b->rot.z || a->pos.x != b->pos.x || a->pos.y != b->pos.y || + a->pos.z != b->pos.z) { + return false; + } + return true; +} + +/** + * Reset DynaLookup lists + */ +void DynaLookup_ResetLists(DynaLookup* dynaLookup) { + SSList_SetNull(&dynaLookup->ceiling); + SSList_SetNull(&dynaLookup->wall); + SSList_SetNull(&dynaLookup->floor); +} + +/** + * Reset DynaLookup + */ +void DynaLookup_Reset(DynaLookup* dynaLookup) { + dynaLookup->polyStartIndex = 0; + DynaLookup_ResetLists(dynaLookup); +} + +/** + * Reset vtxStartIndex + */ +void DynaLookup_ResetVtxStartIndex(u16* vtxStartIndex) { + *vtxStartIndex = 0; +} + +/** + * Initialize BgActor + */ +void BgActor_Initialize(GlobalContext* globalCtx, BgActor* bgActor) { + bgActor->actor = NULL; + bgActor->colHeader = NULL; + ScaleRotPos_Initialize(&bgActor->prevTransform); + ScaleRotPos_Initialize(&bgActor->curTransform); + DynaLookup_Reset(&bgActor->dynaLookup); + DynaLookup_ResetVtxStartIndex(&bgActor->vtxStartIndex); + bgActor->boundingSphere.center.x = bgActor->boundingSphere.center.y = bgActor->boundingSphere.center.z = 0; + bgActor->boundingSphere.radius = 0; +} + +/** + * setActor internal + */ +void BgActor_SetActor(BgActor* bgActor, Actor* actor, CollisionHeader* colHeader) { + bgActor->actor = actor; + bgActor->colHeader = colHeader; + bgActor->prevTransform.scale = actor->scale; + bgActor->prevTransform.rot = actor->shape.rot; + bgActor->prevTransform.rot.x--; + bgActor->prevTransform.pos = actor->world.pos; + bgActor->curTransform.scale = actor->scale; + bgActor->curTransform.rot = actor->shape.rot; + bgActor->curTransform.pos = actor->world.pos; +} + +/** + * Test if the BgActor transform is the same + */ +s32 BgActor_IsTransformUnchanged(BgActor* bgActor) { + return ScaleRotPos_Equals(&bgActor->prevTransform, &bgActor->curTransform); +} + +/** + * NULL polyList + */ +void DynaPoly_NullPolyList(CollisionPoly** polyList) { + *polyList = NULL; +} + +/** + * Allocate dyna.polyList + */ +void DynaPoly_AllocPolyList(GlobalContext* globalCtx, CollisionPoly** polyList, s32 numPolys) { + *polyList = THA_AllocEndAlign(&globalCtx->state.tha, numPolys * sizeof(CollisionPoly), -2); + ASSERT(*polyList != NULL, "ptbl->pbuf != NULL", "../z_bgcheck.c", 6247); +} + +/** + * NULL vtxList + */ +void DynaPoly_NullVtxList(Vec3s** vtxList) { + *vtxList = NULL; +} + +/** + * Allocate dyna.vtxList + */ +void DynaPoly_AllocVtxList(GlobalContext* globalCtx, Vec3s** vtxList, s32 numVtx) { + *vtxList = THA_AllocEndAlign(&globalCtx->state.tha, numVtx * sizeof(Vec3s), -2); + ASSERT(*vtxList != NULL, "ptbl->pbuf != NULL", "../z_bgcheck.c", 6277); +} + +/** + * Update BgActor's prevTransform + */ +void DynaPoly_SetBgActorPrevTransform(GlobalContext* globalCtx, BgActor* bgActor) { + bgActor->prevTransform = bgActor->curTransform; +} + +/** + * Is BgActor Id + */ +s32 DynaPoly_IsBgIdBgActor(s32 bgId) { + if (bgId < 0 || bgId >= BG_ACTOR_MAX) { + return false; + } + return true; +} + +/** + * Init DynaCollisionContext + */ +void DynaPoly_Init(GlobalContext* globalCtx, DynaCollisionContext* dyna) { + dyna->bitFlag = DYNAPOLY_INVALIDATE_LOOKUP; + DynaPoly_NullPolyList(&dyna->polyList); + DynaPoly_NullVtxList(&dyna->vtxList); + DynaSSNodeList_Initialize(globalCtx, &dyna->polyNodes); +} + +/** + * Set DynaCollisionContext + */ +void DynaPoly_Alloc(GlobalContext* globalCtx, DynaCollisionContext* dyna) { + s32 i; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + BgActor_Initialize(globalCtx, &dyna->bgActors[i]); + dyna->bgActorFlags[i] = 0; + } + DynaPoly_NullPolyList(&dyna->polyList); + DynaPoly_AllocPolyList(globalCtx, &dyna->polyList, dyna->polyListMax); + + DynaPoly_NullVtxList(&dyna->vtxList); + DynaPoly_AllocVtxList(globalCtx, &dyna->vtxList, dyna->vtxListMax); + + DynaSSNodeList_Initialize(globalCtx, &dyna->polyNodes); + DynaSSNodeList_Alloc(globalCtx, &dyna->polyNodes, dyna->polyNodesMax); +} + +/** + * Set BgActor + * original name: DynaPolyInfo_setActor + */ +s32 DynaPoly_SetBgActor(GlobalContext* globalCtx, DynaCollisionContext* dyna, Actor* actor, + CollisionHeader* colHeader) { + s32 bgId; + s32 foundSlot = false; + + for (bgId = 0; bgId < BG_ACTOR_MAX; bgId++) { + if (!(dyna->bgActorFlags[bgId] & 1)) { + dyna->bgActorFlags[bgId] |= 1; + foundSlot = true; + break; + } + } + + if (foundSlot == false) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("DynaPolyInfo_setActor():ダイナミックポリゴン 空きインデックスはありません\n"); + osSyncPrintf(VT_RST); + return BG_ACTOR_MAX; + } + + BgActor_SetActor(&dyna->bgActors[bgId], actor, colHeader); + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + + dyna->bgActorFlags[bgId] &= ~2; + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("DynaPolyInfo_setActor():index %d\n", bgId); + osSyncPrintf(VT_RST); + return bgId; +} + +/** + * Gets the actor assigned to `bgId` + * possible orginal name: DynaPolyInfo_getActor + */ +DynaPolyActor* DynaPoly_GetActor(CollisionContext* colCtx, s32 bgId) { + if (!DynaPoly_IsBgIdBgActor(bgId) || !(colCtx->dyna.bgActorFlags[bgId] & 1) || + colCtx->dyna.bgActorFlags[bgId] & 2) { + return NULL; + } + return (DynaPolyActor*)colCtx->dyna.bgActors[bgId].actor; +} + +void func_8003EBF8(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId) { + if (DynaPoly_IsBgIdBgActor(bgId)) { + dyna->bgActorFlags[bgId] |= 4; + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + } +} + +void func_8003EC50(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId) { + if (DynaPoly_IsBgIdBgActor(bgId)) { + dyna->bgActorFlags[bgId] &= ~4; + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + } +} + +void func_8003ECA8(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId) { + if (DynaPoly_IsBgIdBgActor(bgId)) { + dyna->bgActorFlags[bgId] |= 8; + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + } +} + +void func_8003ED00(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId) { + if (DynaPoly_IsBgIdBgActor(bgId)) { + dyna->bgActorFlags[bgId] &= ~8; + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + } +} + +/** + * original name: DynaPolyInfo_delReserve + */ +void DynaPoly_DeleteBgActor(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId) { + DynaPolyActor* actor; + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("DynaPolyInfo_delReserve():index %d\n", bgId); + osSyncPrintf(VT_RST); + if (DynaPoly_IsBgIdBgActor(bgId) == false) { + + if (bgId == -1) { + osSyncPrintf(VT_FGCOL(GREEN)); + // "The index that should have been deleted(? ) was(== -1), processing aborted." + osSyncPrintf( + "DynaPolyInfo_delReserve():削除されているはずの(?)\nインデックス(== -1)のため,処理を中止します。\n"); + osSyncPrintf(VT_RST); + return; + } else { + osSyncPrintf(VT_FGCOL(RED)); + // "Unable to deallocate index / index unallocated, processing aborted." + osSyncPrintf("DynaPolyInfo_delReserve():" + "確保していない/出来なかったインデックスの解放のため、処理を中止します。index == %d\n", + bgId); + osSyncPrintf(VT_RST); + return; + } + } + actor = DynaPoly_GetActor(&globalCtx->colCtx, bgId); + if (actor != NULL) { + + actor->bgId = BGACTOR_NEG_ONE; + dyna->bgActors[bgId].actor = NULL; + dyna->bgActorFlags[bgId] |= 2; + } +} + +void func_8003EE6C(GlobalContext* globalCtx, DynaCollisionContext* dyna) { + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; +} + +/** + * original name: DynaPolyInfo_expandSRT + */ +void DynaPoly_ExpandSRT(GlobalContext* globalCtx, DynaCollisionContext* dyna, s32 bgId, s32* vtxStartIndex, + s32* polyStartIndex) { + MtxF mtx; + Actor* actor; + s32 pad; + s32 pad2; + f32 numVtxInverse; + s32 i; + Vec3f pos; + Sphere16* sphere; + Vec3s* dVtxList; + Vec3s* point; + Vec3f newCenterPoint; + f32 newRadiusSq; + CollisionHeader* pbgdata; + Vec3f newVtx; + Vec3f vtxA; + Vec3f vtxB; + Vec3f vtxC; + Vec3f newNormal; + + pbgdata = dyna->bgActors[bgId].colHeader; + sphere = &dyna->bgActors[bgId].boundingSphere; + actor = dyna->bgActors[bgId].actor; + dyna->bgActors[bgId].dynaLookup.polyStartIndex = *polyStartIndex; + dyna->bgActors[bgId].vtxStartIndex = *vtxStartIndex; + pos = actor->world.pos; + pos.y += actor->shape.yOffset * actor->scale.y; + + ScaleRotPos_SetValue(&dyna->bgActors[bgId].curTransform, &actor->scale, &actor->shape.rot, &pos); + + if (dyna->bgActorFlags[bgId] & 4) { + return; + } + + if (!(dyna->polyListMax >= *polyStartIndex + pbgdata->numPolygons)) { + osSyncPrintf(VT_FGCOL(RED)); + // "do not use if %d exceeds %d" + osSyncPrintf("DynaPolyInfo_expandSRT():polygon over %dが%dを越えるとダメ\n", + *polyStartIndex + pbgdata->numPolygons, dyna->polyListMax); + } + + if (!(dyna->vtxListMax >= *vtxStartIndex + pbgdata->numVertices)) { + osSyncPrintf(VT_FGCOL(RED)); + // "do not use if %d exceeds %d" + osSyncPrintf("DynaPolyInfo_expandSRT():vertex over %dが%dを越えるとダメ\n", + *vtxStartIndex + pbgdata->numVertices, dyna->vtxListMax); + } + + ASSERT(dyna->polyListMax >= *polyStartIndex + pbgdata->numPolygons, + "pdyna_poly_info->poly_num >= *pstart_poly_index + pbgdata->poly_num", "../z_bgcheck.c", 6687); + ASSERT(dyna->vtxListMax >= *vtxStartIndex + pbgdata->numVertices, + "pdyna_poly_info->vert_num >= *pstart_vert_index + pbgdata->vtx_num", "../z_bgcheck.c", 6688); + + if (!(dyna->bitFlag & DYNAPOLY_INVALIDATE_LOOKUP) && + (BgActor_IsTransformUnchanged(&dyna->bgActors[bgId]) == true)) { + s32 pi; + for (pi = *polyStartIndex; pi < *polyStartIndex + pbgdata->numPolygons; pi++) { + CollisionPoly* poly = &dyna->polyList[pi]; + s16 normalY = poly->normal.y; + + if (normalY > COLPOLY_SNORMAL(0.5f)) { + s16 polyIndex = pi; + DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.floor, &polyIndex); + } else if (normalY < COLPOLY_SNORMAL(-0.8f)) { + if (!(dyna->bgActorFlags[bgId] & 8)) { + s16 polyIndex = pi; + DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.ceiling, + &polyIndex); + } + } else { + s16 polyIndex = pi; + DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.wall, &polyIndex); + } + } + + *polyStartIndex += pbgdata->numPolygons; + *vtxStartIndex += pbgdata->numVertices; + } else { + SkinMatrix_SetTranslateRotateYXZScale( + &mtx, dyna->bgActors[bgId].curTransform.scale.x, dyna->bgActors[bgId].curTransform.scale.y, + dyna->bgActors[bgId].curTransform.scale.z, dyna->bgActors[bgId].curTransform.rot.x, + dyna->bgActors[bgId].curTransform.rot.y, dyna->bgActors[bgId].curTransform.rot.z, + dyna->bgActors[bgId].curTransform.pos.x, dyna->bgActors[bgId].curTransform.pos.y, + dyna->bgActors[bgId].curTransform.pos.z); + + numVtxInverse = 1.0f / pbgdata->numVertices; + newCenterPoint.x = newCenterPoint.y = newCenterPoint.z = 0.0f; + for (i = 0; i < pbgdata->numVertices; i++) { + Vec3f vtx; + Vec3f vtxT; // Vtx after mtx transform + Math_Vec3s_ToVec3f(&vtx, &pbgdata->vtxList[i]); + SkinMatrix_Vec3fMtxFMultXYZ(&mtx, &vtx, &vtxT); + BgCheck_Vec3fToVec3s(&dyna->vtxList[*vtxStartIndex + i], &vtxT); + + if (i == 0) { + dyna->bgActors[bgId].minY = dyna->bgActors[bgId].maxY = vtxT.y; + } else if (vtxT.y < dyna->bgActors[bgId].minY) { + dyna->bgActors[bgId].minY = vtxT.y; + } else if (dyna->bgActors[bgId].maxY < vtxT.y) { + dyna->bgActors[bgId].maxY = vtxT.y; + } + newCenterPoint.x += vtxT.x; + newCenterPoint.y += vtxT.y; + newCenterPoint.z += vtxT.z; + } + + newCenterPoint.x *= numVtxInverse; + newCenterPoint.y *= numVtxInverse; + newCenterPoint.z *= numVtxInverse; + sphere->center.x = newCenterPoint.x; + sphere->center.y = newCenterPoint.y; + sphere->center.z = newCenterPoint.z; + newRadiusSq = -100.0f; + + for (i = 0; i < pbgdata->numVertices; i++) { + f32 radiusSq; + + newVtx.x = dyna->vtxList[*vtxStartIndex + i].x; + newVtx.y = dyna->vtxList[*vtxStartIndex + i].y; + newVtx.z = dyna->vtxList[*vtxStartIndex + i].z; + radiusSq = Math3D_Vec3fDistSq(&newVtx, &newCenterPoint); + if (newRadiusSq < radiusSq) { + newRadiusSq = radiusSq; + } + } + + sphere->radius = sqrtf(newRadiusSq) * 1.1f; + + for (i = 0; i < pbgdata->numPolygons; i++) { + CollisionPoly* newPoly = &dyna->polyList[*polyStartIndex + i]; + f32 newNormMagnitude; + + *newPoly = pbgdata->polyList[i]; + + // Yeah, this is all kinds of fake, but my God, it matches. + newPoly->flags_vIA = + (COLPOLY_VTX_INDEX(newPoly->flags_vIA) + *vtxStartIndex) | ((*newPoly).flags_vIA & 0xE000); + newPoly->flags_vIB = + (COLPOLY_VTX_INDEX(newPoly->flags_vIB) + *vtxStartIndex) | ((*newPoly).flags_vIB & 0xE000); + newPoly->vIC = *vtxStartIndex + newPoly->vIC; + dVtxList = dyna->vtxList; + vtxA.x = dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIA)].x; + vtxA.y = dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIA)].y; + vtxA.z = dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIA)].z; + vtxB.x = dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIB)].x; + vtxB.y = dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIB)].y; + vtxB.z = dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIB)].z; + vtxC.x = dVtxList[newPoly->vIC].x; + vtxC.y = dVtxList[newPoly->vIC].y; + vtxC.z = dVtxList[newPoly->vIC].z; + Math3D_SurfaceNorm(&vtxA, &vtxB, &vtxC, &newNormal); + newNormMagnitude = Math3D_Vec3fMagnitude(&newNormal); + + if (!IS_ZERO(newNormMagnitude)) { + newNormal.x *= (1.0f / newNormMagnitude); + newNormal.y *= (1.0f / newNormMagnitude); + newNormal.z *= (1.0f / newNormMagnitude); + newPoly->normal.x = COLPOLY_SNORMAL(newNormal.x); + newPoly->normal.y = COLPOLY_SNORMAL(newNormal.y); + newPoly->normal.z = COLPOLY_SNORMAL(newNormal.z); + } + + newPoly->dist = -DOTXYZ(newNormal, dVtxList[(uintptr_t)COLPOLY_VTX_INDEX(newPoly->flags_vIA)]); + if (newNormal.y > 0.5f) { + s16 polyId = *polyStartIndex + i; + DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.floor, &polyId); + } else if (newNormal.y < -0.8f) { + s16 polyId = *polyStartIndex + i; + DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.ceiling, &polyId); + } else { + s16 polyId = *polyStartIndex + i; + DynaSSNodeList_SetSSListHead(&dyna->polyNodes, &dyna->bgActors[bgId].dynaLookup.wall, &polyId); + } + } + + *polyStartIndex += pbgdata->numPolygons; + *vtxStartIndex += pbgdata->numVertices; + } +} + +void func_8003F8EC(GlobalContext* globalCtx, DynaCollisionContext* dyna, Actor* actor) { + DynaPolyActor* dynaActor; + s32 i; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if ((dyna->bgActorFlags[i] & 1)) { + dynaActor = DynaPoly_GetActor(&globalCtx->colCtx, i); + if (dynaActor != NULL && &dynaActor->actor == actor) { + func_800434A0((DynaPolyActor*)actor); + return; + } + } + } +} + +/** + * DynaPolyInfo_setup + */ +void DynaPoly_Setup(GlobalContext* globalCtx, DynaCollisionContext* dyna) { + DynaPolyActor* actor; + s32 vtxStartIndex; + s32 polyStartIndex; + s32 i; + + DynaSSNodeList_ResetCount(&dyna->polyNodes); + + for (i = 0; i < BG_ACTOR_MAX; i++) { + DynaLookup_ResetLists(&dyna->bgActors[i].dynaLookup); + } + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (dyna->bgActorFlags[i] & 2) { + // Initialize BgActor + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("DynaPolyInfo_setup():削除 index=%d\n", i); + osSyncPrintf(VT_RST); + + dyna->bgActorFlags[i] = 0; + BgActor_Initialize(globalCtx, &dyna->bgActors[i]); + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + } + if (dyna->bgActors[i].actor != NULL && dyna->bgActors[i].actor->update == NULL) { + // Delete BgActor + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("DynaPolyInfo_setup():削除 index=%d\n", i); + osSyncPrintf(VT_RST); + actor = DynaPoly_GetActor(&globalCtx->colCtx, i); + if (actor == NULL) { + return; + } + actor->bgId = BGACTOR_NEG_ONE; + dyna->bgActorFlags[i] = 0; + + BgActor_Initialize(globalCtx, &dyna->bgActors[i]); + dyna->bitFlag |= DYNAPOLY_INVALIDATE_LOOKUP; + } + } + vtxStartIndex = 0; + polyStartIndex = 0; + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (dyna->bgActorFlags[i] & 1) { + DynaPoly_ExpandSRT(globalCtx, dyna, i, &vtxStartIndex, &polyStartIndex); + } + } + dyna->bitFlag &= ~DYNAPOLY_INVALIDATE_LOOKUP; +} + +/** + * Update all BgActor's previous ScaleRotPos + */ +void DynaPoly_UpdateBgActorTransforms(GlobalContext* globalCtx, DynaCollisionContext* dyna) { + s32 i; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (dyna->bgActorFlags[i] & 1) { + DynaPoly_SetBgActorPrevTransform(globalCtx, &dyna->bgActors[i]); + } + } +} + +#define DYNA_RAYCAST_FLOORS 1 +#define DYNA_RAYCAST_WALLS 2 +#define DYNA_RAYCAST_CEILINGS 4 + +/** + * Perform dyna poly raycast toward floor on a list of floor, wall, or ceiling polys + * `listType` specifies the poly list type (e.g. DYNA_RAYCAST_FLOORS) + */ +f32 BgCheck_RaycastFloorDynaList(DynaRaycast* dynaRaycast, u32 listType) { + CollisionPoly* polyList; + SSNode* curNode; + f32 result; + f32 yIntersect; + s16 id; + + result = dynaRaycast->yIntersect; + if (dynaRaycast->ssList->head == SS_NULL) { + return result; + } + polyList = dynaRaycast->dyna->polyList; + curNode = &dynaRaycast->dyna->polyNodes.tbl[dynaRaycast->ssList->head]; + + while (true) { + id = curNode->polyId; + if (COLPOLY_VIA_FLAG_TEST(polyList[id].flags_vIA, dynaRaycast->xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dynaRaycast->dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + if ((listType & (DYNA_RAYCAST_WALLS | DYNA_RAYCAST_CEILINGS)) && (dynaRaycast->unk_20 & 0x10) && + COLPOLY_GET_NORMAL(polyList[id].normal.y) < 0.0f) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dynaRaycast->dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + if (CollisionPoly_CheckYIntersectApprox1(&polyList[id], dynaRaycast->dyna->vtxList, dynaRaycast->pos->x, + dynaRaycast->pos->z, &yIntersect, dynaRaycast->chkDist) == true && + yIntersect < dynaRaycast->pos->y && result < yIntersect) { + result = yIntersect; + *dynaRaycast->resultPoly = &dynaRaycast->dyna->polyList[id]; + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dynaRaycast->dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + return result; +} + +/** + * Perform dyna poly raycast toward floor + * returns the yIntersect of the poly found, or BGCHECK_Y_MIN if no poly is found + */ +f32 BgCheck_RaycastFloorDyna(DynaRaycast* dynaRaycast) { + s32 i; + f32 result; + f32 intersect2; + s32 i2; + s32 pauseState; + DynaPolyActor* dynaActor; + s32 pad; + Vec3f polyVtx[3]; + Vec3f polyNorm; + u32 polyIndex; + CollisionPoly* polyMin; + MtxF srpMtx; + f32 magnitude; + Vec3s* vtxList; + f32 polyDist; + Vec3f vtx; + f32 intersect; + ScaleRotPos* curTransform; + CollisionPoly* poly; + + result = BGCHECK_Y_MIN; + *dynaRaycast->bgId = BGCHECK_SCENE; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (!(dynaRaycast->colCtx->dyna.bgActorFlags[i] & 1)) { + continue; + } + + if (dynaRaycast->actor == dynaRaycast->colCtx->dyna.bgActors[i].actor || + dynaRaycast->pos->y < dynaRaycast->colCtx->dyna.bgActors[i].minY || + Math3D_XZInSphere(&dynaRaycast->colCtx->dyna.bgActors[i].boundingSphere, dynaRaycast->pos->x, + dynaRaycast->pos->z) == false) { + continue; + } + + dynaRaycast->dyna = &dynaRaycast->colCtx->dyna; + if (dynaRaycast->unk_20 & BGCHECK_IGNORE_FLOOR) { + dynaRaycast->ssList = &dynaRaycast->colCtx->dyna.bgActors[i].dynaLookup.floor; + intersect2 = BgCheck_RaycastFloorDynaList(dynaRaycast, DYNA_RAYCAST_FLOORS); + + if (dynaRaycast->yIntersect < intersect2) { + + dynaRaycast->yIntersect = intersect2; + *dynaRaycast->bgId = i; + result = intersect2; + } + } + if ((dynaRaycast->unk_20 & BGCHECK_IGNORE_WALL) || + (*dynaRaycast->resultPoly == NULL && (dynaRaycast->unk_20 & 8))) { + dynaRaycast->ssList = &dynaRaycast->colCtx->dyna.bgActors[i].dynaLookup.wall; + intersect2 = BgCheck_RaycastFloorDynaList(dynaRaycast, DYNA_RAYCAST_WALLS); + if (dynaRaycast->yIntersect < intersect2) { + + dynaRaycast->yIntersect = intersect2; + *dynaRaycast->bgId = i; + result = intersect2; + } + } + + if (dynaRaycast->unk_20 & BGCHECK_IGNORE_CEILING) { + dynaRaycast->ssList = &dynaRaycast->colCtx->dyna.bgActors[i].dynaLookup.ceiling; + intersect2 = BgCheck_RaycastFloorDynaList(dynaRaycast, DYNA_RAYCAST_CEILINGS); + if (dynaRaycast->yIntersect < intersect2) { + + dynaRaycast->yIntersect = intersect2; + *dynaRaycast->bgId = i; + result = intersect2; + } + } + } + + dynaActor = DynaPoly_GetActor(dynaRaycast->colCtx, *dynaRaycast->bgId); + if ((result != BGCHECK_Y_MIN) && (dynaActor != NULL) && (dynaRaycast->globalCtx != NULL)) { + pauseState = dynaRaycast->globalCtx->pauseCtx.state != 0; + if (pauseState == 0) { + pauseState = dynaRaycast->globalCtx->pauseCtx.debugState != 0; + } + if (!pauseState && (dynaRaycast->colCtx->dyna.bgActorFlags[*dynaRaycast->bgId] & 2)) { + curTransform = &dynaRaycast->dyna->bgActors[*dynaRaycast->bgId].curTransform; + polyMin = + &dynaRaycast->dyna->polyList[dynaRaycast->dyna->bgActors[*dynaRaycast->bgId].dynaLookup.polyStartIndex]; + polyIndex = *dynaRaycast->resultPoly - polyMin; + poly = &dynaRaycast->dyna->bgActors[*dynaRaycast->bgId].colHeader->polyList[polyIndex]; + + SkinMatrix_SetTranslateRotateYXZScale(&srpMtx, curTransform->scale.x, curTransform->scale.y, + curTransform->scale.z, curTransform->rot.x, curTransform->rot.y, + curTransform->rot.z, curTransform->pos.x, curTransform->pos.y, + curTransform->pos.z); + + vtxList = dynaRaycast->dyna->bgActors[*dynaRaycast->bgId].colHeader->vtxList; + + for (i2 = 0; i2 < 3; i2++) { + Math_Vec3s_ToVec3f(&vtx, &vtxList[COLPOLY_VTX_INDEX(poly->vtxData[i2])]); + SkinMatrix_Vec3fMtxFMultXYZ(&srpMtx, &vtx, &polyVtx[i2]); + } + Math3D_SurfaceNorm(&polyVtx[0], &polyVtx[1], &polyVtx[2], &polyNorm); + magnitude = Math3D_Vec3fMagnitude(&polyNorm); + + if (!IS_ZERO(magnitude)) { + polyNorm.x *= 1.0f / magnitude; + polyNorm.y *= 1.0f / magnitude; + polyNorm.z *= 1.0f / magnitude; + polyDist = -DOTXYZ(polyNorm, polyVtx[0]); + if (Math3D_TriChkPointParaYIntersectInsideTri(&polyVtx[0], &polyVtx[1], &polyVtx[2], polyNorm.x, + polyNorm.y, polyNorm.z, polyDist, dynaRaycast->pos->z, + dynaRaycast->pos->x, &intersect, dynaRaycast->chkDist)) { + if (fabsf(intersect - result) < 1.0f) { + + result = intersect; + } + } + } + } + } + return result; +} + +/** + * Performs collision detection on a BgActor's wall polys on sphere `pos`, `radius` + * returns true if a collision was detected + * `outX` `outZ` return the displaced x,z coordinates + * `outPoly` returns the pointer to the nearest poly collided with, or NULL + * `outBgId` returns `bgId` if the poly SurfaceType's wall damage flag is not set, else ? + */ +s32 BgCheck_SphVsDynaWallInBgActor(CollisionContext* colCtx, u16 xpFlags, DynaCollisionContext* dyna, SSList* ssList, + f32* outX, f32* outZ, CollisionPoly** outPoly, s32* outBgId, Vec3f* pos, f32 radius, + s32 bgId) { + f32 temp; + f32 intersect; + s32 result = false; + CollisionPoly* poly; + SSNode* curNode; + f32 nx; + f32 ny; + f32 nz; + Vec3f resultPos; + s16 polyId; + f32 zTemp; + f32 xTemp; + f32 normalXZ; + f32 invNormalXZ; + f32 planeDist; + f32 temp_f18; + f32 zIntersectDist; + f32 xIntersectDist; + f32 zMin; + f32 zMax; + f32 xMin; + f32 xMax; + + if (ssList->head == SS_NULL) { + return result; + } + + resultPos = *pos; + curNode = &dyna->polyNodes.tbl[ssList->head]; + + while (true) { + polyId = curNode->polyId; + poly = &dyna->polyList[polyId]; + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + normalXZ = sqrtf(SQ(nx) + SQ(nz)); + ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 7382); + + planeDist = Math3D_DistPlaneToPos(nx, ny, nz, poly->dist, &resultPos); + if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(poly->flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + invNormalXZ = 1.0f / normalXZ; + temp_f18 = fabsf(nz) * invNormalXZ; + if (temp_f18 < 0.4f) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + + // compute poly zMin/zMax + zTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)].z; + zMax = zMin = zTemp; + + zTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)].z; + if (zTemp < zMin) { + zMin = zTemp; + } else if (zTemp > zMax) { + zMax = zTemp; + } + + zTemp = dyna->vtxList[poly->vIC].z; + if (zTemp < zMin) { + zMin = zTemp; + } else if (zMax < zTemp) { + zMax = zTemp; + } + + zMin -= radius; + zMax += radius; + if (resultPos.z < zMin || zMax < resultPos.z) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + if (CollisionPoly_CheckZIntersectApprox(poly, dyna->vtxList, resultPos.x, pos->y, &intersect)) { + if (fabsf(intersect - resultPos.z) <= radius / temp_f18) { + if ((intersect - resultPos.z) * nz <= 4.0f) { + if (BgCheck_ComputeWallDisplacement(colCtx, poly, &resultPos.x, &resultPos.z, nx, ny, nz, + invNormalXZ, planeDist, radius, outPoly)) { + *outBgId = bgId; + } + result = true; + } + } + } + if (curNode->next == SS_NULL) { + break; + } + curNode = &dyna->polyNodes.tbl[curNode->next]; + } + + curNode = &dyna->polyNodes.tbl[ssList->head]; + while (true) { + polyId = curNode->polyId; + poly = &dyna->polyList[polyId]; + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + normalXZ = sqrtf(SQ(nx) + SQ(nz)); + ASSERT(!IS_ZERO(normalXZ), "!IS_ZERO(ac_size)", "../z_bgcheck.c", 7489); + + planeDist = Math3D_DistPlaneToPos(nx, ny, nz, poly->dist, &resultPos); + if (radius < fabsf(planeDist) || COLPOLY_VIA_FLAG_TEST(poly->flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + + invNormalXZ = 1.0f / normalXZ; + temp_f18 = fabsf(nx) * invNormalXZ; + if (temp_f18 < 0.4f) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + + // compute poly xMin/xMax + xTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIA)].x; + xMax = xMin = xTemp; + xTemp = dyna->vtxList[COLPOLY_VTX_INDEX(poly->flags_vIB)].x; + + if (xTemp < xMin) { + xMin = xTemp; + } else if (xMax < xTemp) { + xMax = xTemp; + } + xTemp = dyna->vtxList[poly->vIC].x; + if (xTemp < xMin) { + xMin = xTemp; + } else if (xMax < xTemp) { + xMax = xTemp; + } + + xMin -= radius; + xMax += radius; + if (resultPos.x < xMin || xMax < resultPos.x) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + + if (CollisionPoly_CheckXIntersectApprox(poly, dyna->vtxList, pos->y, resultPos.z, &intersect)) { + xIntersectDist = intersect - resultPos.x; + if (fabsf(xIntersectDist) <= radius / temp_f18) { + if (xIntersectDist * nx <= 4.0f) { + if (BgCheck_ComputeWallDisplacement(colCtx, poly, &resultPos.x, &resultPos.z, nx, ny, nz, + invNormalXZ, planeDist, radius, outPoly)) { + *outBgId = bgId; + } + result = true; + } + } + } + if (curNode->next == SS_NULL) { + break; + } + curNode = &dyna->polyNodes.tbl[curNode->next]; + } + *outX = resultPos.x; + *outZ = resultPos.z; + return result; +} + +/** + * Performs collision detection on all dyna poly walls using sphere `pos`, `radius` + * returns true if a collision was detected + * `outX` `outZ` return the displaced x,z coordinates + * `outPoly` returns the pointer to the nearest poly collided with, or NULL + * `outBgId` returns the index of the BgActor that owns `outPoly` + * If `actor` is not NULL, an BgActor bound to that actor will be ignored + */ +s32 BgCheck_SphVsDynaWall(CollisionContext* colCtx, u16 xpFlags, f32* outX, f32* outZ, Vec3f* pos, f32 radius, + CollisionPoly** outPoly, s32* outBgId, Actor* actor) { + Vec3f resultPos; + s32 result; + f32 r; + f32 dz; + f32 dx; + BgActor* bgActor; + s32 i; + + result = false; + resultPos = *pos; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (!(colCtx->dyna.bgActorFlags[i] & 1)) { + continue; + } + if ((colCtx->dyna.bgActors + i)->actor == actor) { + continue; + } + bgActor = &colCtx->dyna.bgActors[i]; + + if (bgActor->minY > resultPos.y || bgActor->maxY < resultPos.y) { + continue; + } + + bgActor->boundingSphere.radius += (s16)radius; + + r = bgActor->boundingSphere.radius; + dx = bgActor->boundingSphere.center.x - resultPos.x; + dz = bgActor->boundingSphere.center.z - resultPos.z; + if (SQ(r) < (SQ(dx) + SQ(dz)) || (!Math3D_XYInSphere(&bgActor->boundingSphere, resultPos.x, resultPos.y) && + !Math3D_YZInSphere(&bgActor->boundingSphere, resultPos.y, resultPos.z))) { + bgActor->boundingSphere.radius -= (s16)radius; + continue; + } + bgActor->boundingSphere.radius -= (s16)radius; + if (BgCheck_SphVsDynaWallInBgActor(colCtx, xpFlags, &colCtx->dyna, + &(colCtx->dyna.bgActors + i)->dynaLookup.wall, outX, outZ, outPoly, outBgId, + &resultPos, radius, i)) { + resultPos.x = *outX; + resultPos.z = *outZ; + result = true; + } + } + return result; +} + +/** + * Tests for collision with a dyna poly ceiling, starting at `ssList` + * returns true if a collision occurs, else false + * `outPoly` returns the poly collided with + * `outY` returns the y coordinate needed to not collide with `outPoly` + */ +s32 BgCheck_CheckDynaCeilingList(CollisionContext* colCtx, u16 xpFlags, DynaCollisionContext* dyna, SSList* ssList, + f32* outY, Vec3f* pos, f32 checkHeight, CollisionPoly** outPoly) { + s16 polyId; + SSNode* curNode; + CollisionPoly* poly; + Vec3f testPos; + f32 ceilingY; + f32 sign; + f32 nx; + f32 ny; + f32 nz; + s32 result = false; + f32 intersectDist; + u16 padding; + + if (ssList->head == SS_NULL) { + return false; + } + curNode = &dyna->polyNodes.tbl[ssList->head]; + testPos = *pos; + + while (true) { + polyId = curNode->polyId; + poly = &dyna->polyList[polyId]; + if (COLPOLY_VIA_FLAG_TEST(poly->flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + CollisionPoly_GetNormalF(poly, &nx, &ny, &nz); + if (checkHeight < Math3D_UDistPlaneToPos(nx, ny, nz, poly->dist, &testPos)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + if (CollisionPoly_CheckYIntersectApprox2(poly, dyna->vtxList, testPos.x, testPos.z, &ceilingY)) { + intersectDist = ceilingY - testPos.y; + if (testPos.y < ceilingY && intersectDist < checkHeight && intersectDist * ny <= 0.0f) { + sign = (0.0f <= ny) ? 1.0f : -1.0f; + testPos.y = (sign * checkHeight) + ceilingY; + result = true; + *outPoly = poly; + } + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + *outY = testPos.y; + return result; +} + +/** + * Tests collision with a dyna poly ceiling + * returns true if a collision occurs, else false + * `outPoly` returns the poly collided with, while `outBgId` returns the id of the BgActor that owns the poly + * `outY` returns the y coordinate needed to not collide with `outPoly`, or `pos`.y + `chkDist` if no collision occurs + */ +s32 BgCheck_CheckDynaCeiling(CollisionContext* colCtx, u16 xpFlags, f32* outY, Vec3f* pos, f32 chkDist, + CollisionPoly** outPoly, s32* outBgId, Actor* actor) { + s32 i = 0; + s32 result = false; + f32 resultY; + f32 tempY = chkDist + pos->y; + BgActor* bgActor; + CollisionPoly* poly; + + resultY = tempY; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (!(colCtx->dyna.bgActorFlags[i] & 1)) { + continue; + } + if (actor == colCtx->dyna.bgActors[i].actor) { + continue; + } + if (!Math3D_XZInSphere(&colCtx->dyna.bgActors[i].boundingSphere, pos->x, pos->z)) { + continue; + } + if (BgCheck_CheckDynaCeilingList(colCtx, xpFlags, &colCtx->dyna, &colCtx->dyna.bgActors[i].dynaLookup.ceiling, + &tempY, pos, chkDist, &poly) == true && + tempY < resultY) { + + resultY = tempY; + *outPoly = poly; + *outBgId = i; + result = true; + } + } + *outY = resultY; + return result; +} + +/** + * Tests if DynaLineTest intersects with a poly + * returns true if a poly was intersected, else false + */ +s32 BgCheck_CheckLineAgainstBgActorSSList(DynaLineTest* dynaLineTest) { + f32 distSq; + s32 result; + CollisionPoly* curPoly; + SSNode* curNode; + Vec3f polyIntersect; + s16 polyId; + + if (dynaLineTest->ssList->head == SS_NULL) { + return false; + } + + curNode = &dynaLineTest->dyna->polyNodes.tbl[dynaLineTest->ssList->head]; + result = false; + + while (true) { + polyId = curNode->polyId; + curPoly = &dynaLineTest->dyna->polyList[polyId]; + if (COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, dynaLineTest->xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dynaLineTest->dyna->polyNodes.tbl[curNode->next]; + } + } else { + if (CollisionPoly_LineVsPoly(curPoly, dynaLineTest->dyna->vtxList, dynaLineTest->posA, dynaLineTest->posB, + &polyIntersect, dynaLineTest->chkOneFace, dynaLineTest->chkDist)) { + distSq = Math3D_Vec3fDistSq(dynaLineTest->posA, &polyIntersect); + if (distSq < *dynaLineTest->distSq) { + *dynaLineTest->distSq = distSq; + *dynaLineTest->posResult = polyIntersect; + *dynaLineTest->posB = polyIntersect; + *dynaLineTest->resultPoly = curPoly; + result = true; + } + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dynaLineTest->dyna->polyNodes.tbl[curNode->next]; + } + } + } + return result; +} + +/** + * Tests if line `posA` `posB` intersects with a dyna poly within BgActor `bgId` + * `distSq` is the maximum squared distance to check for a collision + * returns true if an intersection occurred, else false + * `posB`? and `posResult` return the point of intersection + * `outPoly` returns the poly intersected + * `distSq` returns the squared distance of the intersection + */ +s32 BgCheck_CheckLineAgainstBgActor(CollisionContext* colCtx, u16 xpFlags, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, f32* distSq, s32 bgId, f32 chkDist, s32 bccFlags) { + s32 result = false; + DynaLineTest dynaLineTest; + + dynaLineTest.colCtx = colCtx; + dynaLineTest.xpFlags = xpFlags; + dynaLineTest.dyna = &colCtx->dyna; + dynaLineTest.posA = posA; + dynaLineTest.posB = posB; + dynaLineTest.posResult = posResult; + dynaLineTest.resultPoly = outPoly; + dynaLineTest.chkOneFace = (bccFlags & BGCHECK_CHECK_ONE_FACE) != 0; + dynaLineTest.distSq = distSq; + dynaLineTest.chkDist = chkDist; + + dynaLineTest.ssList = &colCtx->dyna.bgActors[bgId].dynaLookup.wall; + if (bccFlags & BGCHECK_CHECK_WALL) { + if (BgCheck_CheckLineAgainstBgActorSSList(&dynaLineTest)) { + result = true; + } + } + dynaLineTest.ssList = &colCtx->dyna.bgActors[bgId].dynaLookup.floor; + if (bccFlags & BGCHECK_CHECK_FLOOR) { + if (BgCheck_CheckLineAgainstBgActorSSList(&dynaLineTest)) { + result = true; + } + } + dynaLineTest.ssList = &colCtx->dyna.bgActors[bgId].dynaLookup.ceiling; + if (bccFlags & BGCHECK_CHECK_CEILING) { + if (BgCheck_CheckLineAgainstBgActorSSList(&dynaLineTest)) { + result = true; + } + } + return result; +} + +/** + * Tests if line from `posA` to `posB` passes through a dyna poly. + * returns true if so, otherwise false + * `outPoly` returns the pointer of the poly intersected. + * `outBgId` returns the BgActor index of the poly + */ +s32 BgCheck_CheckLineAgainstDyna(CollisionContext* colCtx, u16 xpFlags, Vec3f* posA, Vec3f* posB, Vec3f* posResult, + CollisionPoly** outPoly, f32* distSq, s32* outBgId, Actor* actor, f32 chkDist, + s32 bccFlags) { + s32 pad; + s32 i; + s32 result = false; + Linef line; + f32 ay; + f32 by; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (colCtx->dyna.bgActorFlags[i] & 1) { + if (actor != colCtx->dyna.bgActors[i].actor) { + ay = posA->y; + by = posB->y; + if (!(ay < colCtx->dyna.bgActors[i].minY) || !(by < colCtx->dyna.bgActors[i].minY)) { + if (!(colCtx->dyna.bgActors[i].maxY < ay) || !(colCtx->dyna.bgActors[i].maxY < by)) { + line.a = *posA; + line.b = *posB; + if (Math3D_LineVsSph(&colCtx->dyna.bgActors[i].boundingSphere, &line) != 0) { + if (BgCheck_CheckLineAgainstBgActor(colCtx, xpFlags, posA, posB, posResult, outPoly, distSq, + i, chkDist, bccFlags) == true) { + *outBgId = i; + result = true; + } + } + } + } + } + } + } + return result; +} + +/** + * Get first dyna poly intersecting sphere `center` `radius` from list `ssList` + * returns true if any poly intersects the sphere, else returns false + * `outPoly` returns the pointer of the first poly found that intersects + */ +s32 BgCheck_SphVsFirstDynaPolyList(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, Vec3f* center, + f32 radius, SSList* ssList) { + CollisionPoly* curPoly; + DynaCollisionContext* dyna; + SSNode* curNode; + s32 curPolyId; + + if (ssList->head == SS_NULL) { + return false; + } + dyna = &colCtx->dyna; + curNode = &dyna->polyNodes.tbl[ssList->head]; + while (true) { + curPolyId = curNode->polyId; + curPoly = &dyna->polyList[curPolyId]; + if (COLPOLY_VIA_FLAG_TEST(curPoly->flags_vIA, xpFlags)) { + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + if (CollisionPoly_SphVsPoly(curPoly, dyna->vtxList, center, radius)) { + *outPoly = curPoly; + return true; + } + if (curNode->next == SS_NULL) { + break; + } else { + curNode = &dyna->polyNodes.tbl[curNode->next]; + continue; + } + } + return false; +} + +/** + * Get first dyna poly intersecting sphere `center` `radius` from BgActor `bgId` + * returns true if any poly intersects the sphere, else false + * `outPoly` returns the pointer of the first poly found that intersects + */ +s32 BgCheck_SphVsFirstDynaPolyInBgActor(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, Vec3f* center, + f32 radius, s32 bgId, u16 bciFlags) { + if ((bciFlags & BGCHECK_IGNORE_CEILING) == 0) { + if (BgCheck_SphVsFirstDynaPolyList(colCtx, xpFlags, outPoly, center, radius, + &colCtx->dyna.bgActors[bgId].dynaLookup.ceiling)) { + return true; + } + } + if ((bciFlags & BGCHECK_IGNORE_WALL) == 0) { + if (BgCheck_SphVsFirstDynaPolyList(colCtx, xpFlags, outPoly, center, radius, + &colCtx->dyna.bgActors[bgId].dynaLookup.wall)) { + return true; + } + } + if ((bciFlags & BGCHECK_IGNORE_FLOOR) == 0) { + if (BgCheck_SphVsFirstDynaPolyList(colCtx, xpFlags, outPoly, center, radius, + &colCtx->dyna.bgActors[bgId].dynaLookup.floor)) { + return true; + } + } + return false; +} + +/** + * Gets first dyna poly intersecting sphere `center` `radius` + * returns true if poly detected, else false + * `outPoly` returns the first intersecting poly, while `outBgId` returns the BgActor index of that poly + */ +s32 BgCheck_SphVsFirstDynaPoly(CollisionContext* colCtx, u16 xpFlags, CollisionPoly** outPoly, s32* outBgId, + Vec3f* center, f32 radius, Actor* actor, u16 bciFlags) { + s32 i = 0; + Sphere16 testSphere; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + if (!(colCtx->dyna.bgActorFlags[i] & 1)) { + continue; + } + if (colCtx->dyna.bgActors[i].actor == actor) { + continue; + } + testSphere.center.x = center->x; + testSphere.center.y = center->y; + testSphere.center.z = center->z; + testSphere.radius = radius; + if (!Math3D_SphVsSph(&testSphere, &colCtx->dyna.bgActors[i].boundingSphere)) { + continue; + } + if (BgCheck_SphVsFirstDynaPolyInBgActor(colCtx, xpFlags, outPoly, center, radius, i, bciFlags)) { + return true; + } + } + return false; +} + +/** + * SEGMENTED_TO_VIRTUAL CollisionHeader members + */ +void CollisionHeader_SegmentedToVirtual(CollisionHeader* colHeader) { + if (ResourceMgr_OTRSigCheck(colHeader)) + colHeader = ResourceMgr_LoadColByName(colHeader); + + colHeader->vtxList = SEGMENTED_TO_VIRTUAL(colHeader->vtxList); + colHeader->polyList = SEGMENTED_TO_VIRTUAL(colHeader->polyList); + colHeader->surfaceTypeList = SEGMENTED_TO_VIRTUAL(colHeader->surfaceTypeList); + colHeader->cameraDataList = SEGMENTED_TO_VIRTUAL(colHeader->cameraDataList); + colHeader->waterBoxes = SEGMENTED_TO_VIRTUAL(colHeader->waterBoxes); +} + +/** + * Convert CollisionHeader Segmented to Virtual addressing + */ +void CollisionHeader_GetVirtual(void* colHeader, CollisionHeader** dest) +{ + if (ResourceMgr_OTRSigCheck(colHeader)) + colHeader = ResourceMgr_LoadColByName(colHeader); + + *dest = SEGMENTED_TO_VIRTUAL(colHeader); + CollisionHeader_SegmentedToVirtual(*dest); +} + +/** + * SEGMENT_TO_VIRTUAL all active BgActor CollisionHeaders + */ +void func_800418D0(CollisionContext* colCtx, GlobalContext* globalCtx) { + DynaCollisionContext* dyna = &colCtx->dyna; + s32 i; + u16 flag; + + for (i = 0; i < BG_ACTOR_MAX; i++) { + flag = dyna->bgActorFlags[i]; + if ((flag & 1) && !(flag & 2)) { + Actor_SetObjectDependency(globalCtx, dyna->bgActors[i].actor); + CollisionHeader_SegmentedToVirtual(dyna->bgActors[i].colHeader); + } + } +} + +/** + * Reset SSNodeList polyCheckTbl + */ +void BgCheck_ResetPolyCheckTbl(SSNodeList* nodeList, s32 numPolys) { + u8* t; + + for (t = nodeList->polyCheckTbl; t < nodeList->polyCheckTbl + numPolys; t++) { + *t = 0; + } +} + +/** + * Get SurfaceType property set + */ +u32 SurfaceType_GetData(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId, s32 dataIdx) { + CollisionHeader* colHeader; + SurfaceType* surfaceTypes; + + colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + if (colHeader == NULL || poly == NULL) { + return 0; + } + surfaceTypes = colHeader->surfaceTypeList; + if (surfaceTypes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + return surfaceTypes[poly->type].data[dataIdx]; +} + +/** + * SurfaceType return CamData Index + */ +u32 SurfaceType_GetCamDataIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + + return SurfaceType_GetData(colCtx, poly, bgId, 0) & 0xFF; +} + +/** + * CamData return cameraSType + */ +u16 func_80041A4C(CollisionContext* colCtx, u32 camId, s32 bgId) { + u16 result; + CollisionHeader* colHeader; + CamData* camData; + + colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + if (colHeader == NULL) { + return 0; + } + camData = colHeader->cameraDataList; + result = camData[camId].cameraSType; + return result; +} + +/** + * SurfaceType return cameraSType + */ +u16 SurfaceType_GetCameraSType(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + CamData* camData; + SurfaceType* surfaceTypes; + + if (colHeader == NULL) { + return 0; + } + camData = colHeader->cameraDataList; + if (camData == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + surfaceTypes = colHeader->surfaceTypeList; + if (surfaceTypes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + return func_80041A4C(colCtx, SurfaceType_GetCamDataIndex(colCtx, poly, bgId), bgId); +} + +/** + * CamData Get number of cameras + */ +u16 func_80041B24(CollisionContext* colCtx, u32 camId, s32 bgId) { + CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + CamData* camData; + + if (colHeader == NULL) { + return 0; + } + + camData = colHeader->cameraDataList; + if (camData == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + return camData[camId].numCameras; +} + +/** + * SurfaceType Get number of cameras + */ +u16 SurfaceType_GetNumCameras(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + CamData* camData; + SurfaceType* surfaceTypes; + + if (colHeader == NULL) { + return 0; + } + camData = colHeader->cameraDataList; + if (camData == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + surfaceTypes = colHeader->surfaceTypeList; + if (surfaceTypes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + return func_80041B24(colCtx, SurfaceType_GetCamDataIndex(colCtx, poly, bgId), bgId); +} + +/** + * CamData Get camPosData + */ +Vec3s* func_80041C10(CollisionContext* colCtx, s32 camId, s32 bgId) { + CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + CamData* cameraDataList; + + if (colHeader == NULL) { + return NULL; + } + cameraDataList = colHeader->cameraDataList; + if (cameraDataList == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return NULL; + } + return (Vec3s*)SEGMENTED_TO_VIRTUAL(cameraDataList[camId].camPosData); +} + +/** + * SurfaceType Get camPosData + */ +Vec3s* SurfaceType_GetCamPosData(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + CollisionHeader* colHeader = BgCheck_GetCollisionHeader(colCtx, bgId); + CamData* camData; + SurfaceType* surfaceTypes; + + if (colHeader == NULL) { + return NULL; + } + camData = colHeader->cameraDataList; + if (camData == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return NULL; + } + surfaceTypes = colHeader->surfaceTypeList; + if (surfaceTypes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return NULL; + } + return func_80041C10(colCtx, SurfaceType_GetCamDataIndex(colCtx, poly, bgId), bgId); +} + +/** + * SurfaceType Get Scene Exit Index + */ +u32 SurfaceType_GetSceneExitIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 8 & 0x1F; +} + +/** + * SurfaceType Get ? Property (& 0x0003 E000) + */ +u32 func_80041D4C(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 13 & 0x1F; +} + +/** + * SurfaceType Get ? Property (& 0x001C 0000) + */ +u32 func_80041D70(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 18 & 7; +} + +/** + * SurfaceType Get Wall Property (Internal) + */ +u32 func_80041D94(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 21 & 0x1F; +} + +/** + * SurfaceType Get Wall Flags + */ +s32 func_80041DB8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return D_80119D90[func_80041D94(colCtx, poly, bgId)]; +} + +/** + * SurfaceType Is Wall Flag (1 << 0) Set + */ +s32 func_80041DE4(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return (func_80041DB8(colCtx, poly, bgId) & 1) ? true : false; +} + +/** + * SurfaceType Is Wall Flag (1 << 1) Set + */ +s32 func_80041E18(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return (func_80041DB8(colCtx, poly, bgId) & 2) ? true : false; +} + +/** + * SurfaceType Is Wall Flag (1 << 2) Set + */ +s32 func_80041E4C(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return (func_80041DB8(colCtx, poly, bgId) & 4) ? true : false; +} + +/** + * unused + */ +u32 func_80041E80(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 26 & 0xF; +} + +/** + * SurfaceType Get Floor Property + */ +u32 func_80041EA4(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 26 & 0xF; +} + +/** + * SurfaceType Is Floor Minus 1 + */ +u32 func_80041EC8(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 30 & 1; +} + +/** + * SurfaceType Is Horse Blocked + */ +u32 SurfaceType_IsHorseBlocked(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 0) >> 31 & 1; +} + +u32 func_80041F10(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) & 0xF; +} + +/** + * SurfaceType Get Poly Sfx + */ +u16 SurfaceType_GetSfx(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + s32 id = func_80041F10(colCtx, poly, bgId); + + if (id < 0 || id > 13) { + return NA_SE_PL_WALK_GROUND - SFX_FLAG; + } + return D_80119E10[id]; +} + +/** + * SurfaceType get terrain slope surface + */ +u32 SurfaceType_GetSlope(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 4 & 3; +} + +/** + * SurfaceType get surface lighting setting + */ +u32 SurfaceType_GetLightSettingIndex(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 6 & 0x1F; +} + +/** + * SurfaceType get echo + */ +u32 SurfaceType_GetEcho(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 11 & 0x3F; +} + +/** + * SurfaceType Is Hookshot Surface + */ +u32 SurfaceType_IsHookshotSurface(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 17 & 1; +} + +/** + * CollisionPoly is ignored by entities + * Returns true if poly is ignored by entities, else false + */ +s32 SurfaceType_IsIgnoredByEntities(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + u32 flags; + + if (BgCheck_GetCollisionHeader(colCtx, bgId) == NULL) { + return true; + } + flags = poly->flags_vIA & 0x4000; + return !!flags; +} + +/** + * CollisionPoly is ignored by projectiles + * Returns true if poly is ignored by projectiles, else false + */ +s32 SurfaceType_IsIgnoredByProjectiles(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + u32 flags; + + if (BgCheck_GetCollisionHeader(colCtx, bgId) == NULL) { + return true; + } + flags = poly->flags_vIA & 0x8000; + return !!flags; +} + +/** + * CollisionPoly is conveyor enabled + * Returns true if `poly` is a conveyor surface, else false + */ +s32 SurfaceType_IsConveyor(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + u32 flags; + + if (BgCheck_GetCollisionHeader(colCtx, bgId) == NULL) { + return true; + } + flags = poly->flags_vIB & 0x2000; + return !!flags; +} + +/** + * SurfaceType Get Conveyor Surface Speed + */ +u32 SurfaceType_GetConveyorSpeed(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 18 & 7; +} + +/** + * SurfaceType Get Conveyor Direction + * returns a value between 0-63, representing 360 / 64 degrees of rotation + */ +u32 SurfaceType_GetConveyorDirection(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return SurfaceType_GetData(colCtx, poly, bgId, 1) >> 21 & 0x3F; +} + +/** + * SurfaceType is Wall Damage + */ +u32 SurfaceType_IsWallDamage(CollisionContext* colCtx, CollisionPoly* poly, s32 bgId) { + return (SurfaceType_GetData(colCtx, poly, bgId, 1) & 0x8000000) ? 1 : 0; +} + +/** + * Zora's Domain WaterBox in King Zora's Room + */ +WaterBox zdWaterBox = { -348, 877, -1746, 553, 780, 0x2104 }; + +/** + * WaterBox's effective bounding box + */ +f32 zdWaterBoxMinX = -348.0f; +f32 zdWaterBoxMinY = 777.0f; +f32 zdWaterBoxMinZ = -1746.0f; +f32 zdWaterBoxMaxX = 205.0f; +f32 zdWaterBoxMaxY = 977.0f; +f32 zdWaterBoxMaxZ = -967.0f; + +/** + * Public. Get the water surface at point (`x`, `ySurface`, `z`). `ySurface` doubles as position y input + * returns true if point is within the xz boundaries of an active water box, else false + * `ySurface` returns the water box's surface, while `outWaterBox` returns a pointer to the WaterBox + */ +s32 WaterBox_GetSurface1(GlobalContext* globalCtx, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface, + WaterBox** outWaterBox) { + if (globalCtx->sceneNum == SCENE_SPOT07) { + if (zdWaterBoxMinX < x && x < zdWaterBoxMaxX && zdWaterBoxMinY < *ySurface && *ySurface < zdWaterBoxMaxY && + zdWaterBoxMinZ < z && z < zdWaterBoxMaxZ) { + *outWaterBox = &zdWaterBox; + *ySurface = zdWaterBox.ySurface; + return true; + } + } + return WaterBox_GetSurfaceImpl(globalCtx, colCtx, x, z, ySurface, outWaterBox); +} + +/** + * Internal. Get the water surface at point (`x`, `ySurface`, `z`). `ySurface` doubles as position y input + * returns true if point is within the xz boundaries of an active water box, else false + * `ySurface` returns the water box's surface, while `outWaterBox` returns a pointer to the WaterBox + */ +s32 WaterBox_GetSurfaceImpl(GlobalContext* globalCtx, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface, + WaterBox** outWaterBox) { + CollisionHeader* colHeader = colCtx->colHeader; + u32 room; + WaterBox* curWaterBox; + + if (colHeader->numWaterBoxes == 0 || colHeader->waterBoxes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return false; + } + + for (curWaterBox = colHeader->waterBoxes; curWaterBox < colHeader->waterBoxes + colHeader->numWaterBoxes; + curWaterBox++) { + room = (curWaterBox->properties >> 13) & 0x3F; + if (room == (u32)globalCtx->roomCtx.curRoom.num || room == 0x3F) { + if ((curWaterBox->properties & 0x80000) == 0) { + if (curWaterBox->xMin < x && x < curWaterBox->xMin + curWaterBox->xLength) { + if (curWaterBox->zMin < z && z < curWaterBox->zMin + curWaterBox->zLength) { + *outWaterBox = curWaterBox; + *ySurface = curWaterBox->ySurface; + return true; + } + } + } + } + } + return false; +} + +/** + * Gets the first active WaterBox at `pos` where WaterBox.properties & 0x80000 == 0 + * `surfaceChkDist` is the absolute y distance from the water surface to check + * returns the index of the waterbox found, or -1 if no waterbox is found + * `outWaterBox` returns the pointer to the waterbox found, or NULL if none is found + */ +s32 WaterBox_GetSurface2(GlobalContext* globalCtx, CollisionContext* colCtx, Vec3f* pos, f32 surfaceChkDist, + WaterBox** outWaterBox) { + CollisionHeader* colHeader = colCtx->colHeader; + s32 room; + s32 i; + WaterBox* waterBox; + WaterBox* waterBoxList = colHeader->waterBoxes; // unused, needed for matching + + if (colHeader->numWaterBoxes == 0 || colHeader->waterBoxes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + *outWaterBox = NULL; + return -1; + } + + for (i = 0; i < colHeader->numWaterBoxes; i++) { + waterBox = &colHeader->waterBoxes[i]; + + room = WATERBOX_ROOM(waterBox->properties); + if (!(room == globalCtx->roomCtx.curRoom.num || room == 0x3F)) { + continue; + } + if ((waterBox->properties & 0x80000)) { + continue; + } + if (!(waterBox->xMin < pos->x && pos->x < waterBox->xMin + waterBox->xLength)) { + continue; + } + if (!(waterBox->zMin < pos->z && pos->z < waterBox->zMin + waterBox->zLength)) { + continue; + } + if (pos->y - surfaceChkDist < waterBox->ySurface && waterBox->ySurface < pos->y + surfaceChkDist) { + *outWaterBox = waterBox; + return i; + } + } + + *outWaterBox = NULL; + return -1; +} + +/** + * WaterBox get CamData index + */ +u32 WaterBox_GetCamDataIndex(CollisionContext* colCtx, WaterBox* waterBox) { + u32 prop = waterBox->properties >> 0; + + return prop & 0xFF; +} + +/** + * WaterBox get CamData cameraSType + */ +u16 WaterBox_GetCameraSType(CollisionContext* colCtx, WaterBox* waterBox) { + s32 camId = WaterBox_GetCamDataIndex(colCtx, waterBox); + CamData* camData = colCtx->colHeader->cameraDataList; + + if (camData == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return 0; + } + + return colCtx->colHeader->cameraDataList[camId].cameraSType; +} + +/** + * WaterBox get lighting settings + */ +u32 WaterBox_GetLightSettingIndex(CollisionContext* colCtx, WaterBox* waterBox) { + u32 prop = waterBox->properties >> 8; + + return prop & 0x1F; +} + +/** + * Get the water surface at point (`x`, `ySurface`, `z`). `ySurface` doubles as position y input + * same as WaterBox_GetSurfaceImpl, but tests if WaterBox properties & 0x80000 != 0 + * returns true if point is within the xz boundaries of an active water box, else false + * `ySurface` returns the water box's surface, while `outWaterBox` returns a pointer to the WaterBox + */ +s32 func_800425B0(GlobalContext* globalCtx, CollisionContext* colCtx, f32 x, f32 z, f32* ySurface, + WaterBox** outWaterBox) { + CollisionHeader* colHeader = colCtx->colHeader; + u32 room; + WaterBox* curWaterBox; + + if (colHeader->numWaterBoxes == 0 || colHeader->waterBoxes == PHYSICAL_TO_VIRTUAL(gSegments[0])) { + return false; + } + + for (curWaterBox = colHeader->waterBoxes; curWaterBox < colHeader->waterBoxes + colHeader->numWaterBoxes; + curWaterBox++) { + room = (curWaterBox->properties >> 0xD) & 0x3F; + if ((room == (u32)globalCtx->roomCtx.curRoom.num) || (room == 0x3F)) { + if ((curWaterBox->properties & 0x80000) != 0) { + if (curWaterBox->xMin < x && x < (curWaterBox->xMin + curWaterBox->xLength)) { + if (curWaterBox->zMin < z && z < (curWaterBox->zMin + curWaterBox->zLength)) { + *outWaterBox = curWaterBox; + *ySurface = curWaterBox->ySurface; + return true; + } + } + } + } + } + return false; +} + +/** + * Gets the `closestPoint` to `point` on the line formed from the intesection of planes `polyA` and `polyB` + * returns true if the `closestPoint` exists, else returns false + */ +s32 func_80042708(CollisionPoly* polyA, CollisionPoly* polyB, Vec3f* point, Vec3f* closestPoint) { + f32 n1X; + f32 n1Y; + f32 n1Z; + f32 n2X; + f32 n2Y; + f32 n2Z; + + CollisionPoly_GetNormalF(polyA, &n1X, &n1Y, &n1Z); + CollisionPoly_GetNormalF(polyB, &n2X, &n2Y, &n2Z); + return Math3D_PlaneVsPlaneVsLineClosestPoint(n1X, n1Y, n1Z, polyA->dist, n2X, n2Y, n2Z, polyB->dist, point, + closestPoint); +} + +/** + * Get the `closestPoint` to line (`pointA`, `pointB`) formed from the intersection of planes `polyA` and `polyB` + * returns true if the `closestPoint` exists, else returns false + */ +s32 func_800427B4(CollisionPoly* polyA, CollisionPoly* polyB, Vec3f* pointA, Vec3f* pointB, Vec3f* closestPoint) { + f32 n1X; + f32 n1Y; + f32 n1Z; + f32 n2X; + f32 n2Y; + f32 n2Z; + s32 result; + + CollisionPoly_GetNormalF(polyA, &n1X, &n1Y, &n1Z); + CollisionPoly_GetNormalF(polyB, &n2X, &n2Y, &n2Z); + result = Math3D_PlaneVsLineSegClosestPoint(n1X, n1Y, n1Z, polyA->dist, n2X, n2Y, n2Z, polyB->dist, pointA, pointB, + closestPoint); + return result; +} + +/** + * Draw a list of dyna polys, specified by `ssList` + */ +void BgCheck_DrawDynaPolyList(GlobalContext* globalCtx, CollisionContext* colCtx, DynaCollisionContext* dyna, + SSList* ssList, u8 r, u8 g, u8 b) { + s16 curPolyId; + CollisionPoly* poly; + SSNode* curNode; + Vec3f vA; + Vec3f vB; + Vec3f vC; + f32 nx; + f32 ny; + f32 nz; + + if (ssList->head != SS_NULL) { + curNode = &dyna->polyNodes.tbl[ssList->head]; + while (true) { + curPolyId = curNode->polyId; + poly = &dyna->polyList[curPolyId]; + BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIA) + dyna->vtxList, &vA); + BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIB) + dyna->vtxList, &vB); + BgCheck_Vec3sToVec3f((s32)(poly->vIC) + dyna->vtxList, &vC); + if (AREG(26)) { + nx = COLPOLY_GET_NORMAL(poly->normal.x); + ny = COLPOLY_GET_NORMAL(poly->normal.y); + nz = COLPOLY_GET_NORMAL(poly->normal.z); + vA.x += AREG(26) * nx; + vA.y += AREG(26) * ny; + vA.z += AREG(26) * nz; + vB.x += AREG(26) * nx; + vB.y += AREG(26) * ny; + vB.z += AREG(26) * nz; + vC.x += AREG(26) * nx; + vC.y += AREG(26) * ny; + vC.z += AREG(26) * nz; + } + Collider_DrawPoly(globalCtx->state.gfxCtx, &vA, &vB, &vC, r, g, b); + if (curNode->next == SS_NULL) { + break; + } + curNode = &dyna->polyNodes.tbl[curNode->next]; + } + } +} + +/** + * Draw a BgActor's dyna polys + * `bgId` is the BgActor index that should be drawn + */ +void BgCheck_DrawBgActor(GlobalContext* globalCtx, CollisionContext* colCtx, s32 bgId) { + if (AREG(21)) { + BgCheck_DrawDynaPolyList(globalCtx, colCtx, &colCtx->dyna, &colCtx->dyna.bgActors[bgId].dynaLookup.ceiling, 255, + 0, 0); + } + if (AREG(22)) { + BgCheck_DrawDynaPolyList(globalCtx, colCtx, &colCtx->dyna, &colCtx->dyna.bgActors[bgId].dynaLookup.wall, 0, 255, + 0); + } + if (AREG(23)) { + BgCheck_DrawDynaPolyList(globalCtx, colCtx, &colCtx->dyna, &colCtx->dyna.bgActors[bgId].dynaLookup.floor, 0, 0, + 255); + } +} + +/** + * Draw all dyna polys + */ +void BgCheck_DrawDynaCollision(GlobalContext* globalCtx, CollisionContext* colCtx) { + s32 bgId; + + for (bgId = 0; bgId < BG_ACTOR_MAX; bgId++) { + + if (!(colCtx->dyna.bgActorFlags[bgId] & 1)) { + continue; + } + BgCheck_DrawBgActor(globalCtx, colCtx, bgId); + } +} + +/** + * Draw a static poly + */ +void BgCheck_DrawStaticPoly(GlobalContext* globalCtx, CollisionContext* colCtx, CollisionPoly* poly, u8 r, u8 g, u8 b) { + Vec3f vA; + Vec3f vB; + Vec3f vC; + f32 nx; + f32 ny; + f32 nz; + + BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIA) + colCtx->colHeader->vtxList, &vA); + BgCheck_Vec3sToVec3f(COLPOLY_VTX_INDEX(poly->flags_vIB) + colCtx->colHeader->vtxList, &vB); + BgCheck_Vec3sToVec3f(poly->vIC + colCtx->colHeader->vtxList, &vC); + if (AREG(26) != 0) { + nx = COLPOLY_GET_NORMAL(poly->normal.x); + ny = COLPOLY_GET_NORMAL(poly->normal.y); + nz = COLPOLY_GET_NORMAL(poly->normal.z); + vA.x += AREG(26) * nx; + vA.y += AREG(26) * ny; + vA.z += AREG(26) * nz; + vB.x += AREG(26) * nx; + vB.y += AREG(26) * ny; + vB.z += AREG(26) * nz; + vC.x += AREG(26) * nx; + vC.y += AREG(26) * ny; + vC.z += AREG(26) * nz; + } + Collider_DrawPoly(globalCtx->state.gfxCtx, &vA, &vB, &vC, r, g, b); +} + +/** + * Draw a list of static polys, specified by `ssList` + */ +void BgCheck_DrawStaticPolyList(GlobalContext* globalCtx, CollisionContext* colCtx, SSList* ssList, u8 r, u8 g, u8 b) { + SSNode* curNode; + CollisionPoly* polyList = colCtx->colHeader->polyList; + s16 curPolyId; + + if (ssList->head != SS_NULL) { + curNode = &colCtx->polyNodes.tbl[ssList->head]; + while (true) { + curPolyId = curNode->polyId; + BgCheck_DrawStaticPoly(globalCtx, colCtx, &polyList[curPolyId], r, g, b); + if (curNode->next == SS_NULL) { + break; + } + curNode = &colCtx->polyNodes.tbl[curNode->next]; + } + } +} + +/** + * Draw scene collision + */ +void BgCheck_DrawStaticCollision(GlobalContext* globalCtx, CollisionContext* colCtx) { + Player* player = GET_PLAYER(globalCtx); + StaticLookup* lookup = BgCheck_GetNearestStaticLookup(colCtx, colCtx->lookupTbl, &player->actor.world.pos); + + if (AREG(23) != 0) { + BgCheck_DrawStaticPolyList(globalCtx, colCtx, &lookup->floor, 0, 0, 255); + } + if (AREG(22) != 0) { + BgCheck_DrawStaticPolyList(globalCtx, colCtx, &lookup->wall, 0, 255, 0); + } + if (AREG(21) != 0) { + BgCheck_DrawStaticPolyList(globalCtx, colCtx, &lookup->ceiling, 255, 0, 0); + } +} diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c new file mode 100644 index 000000000..7302f9b21 --- /dev/null +++ b/soh/src/code/z_camera.c @@ -0,0 +1,8131 @@ +#include "ultra64.h" +#include "global.h" +#include "vt.h" + +#include + +#include "overlays/actors/ovl_En_Horse/z_en_horse.h" + +s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags); +s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags); +s32 Camera_QRegInit(void); +s32 Camera_CheckWater(Camera* camera); + +#define RELOAD_PARAMS \ + (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14 || R_RELOAD_CAM_PARAMS) + +#define PCT(x) ((x)*0.01f) +#define NEXTSETTING ((values++)->val) +#define NEXTPCT PCT(NEXTSETTING) + +#define BGCAM_POS(v) ((v)[0]) +#define BGCAM_ROT(v) ((v)[1]) +#define BGCAM_FOV(v) ((v)[2].x) +#define BGCAM_JFIFID(v) ((v)[2].y) + +#define FLG_ADJSLOPE (1 << 0) +#define FLG_OFFGROUND (1 << 7) + +#include "z_camera_data.c" + +/*===============================================================*/ + +/** + * Interpolates along a curve between 0 and 1 with a period of + * -a <= p <= a at time `b` + */ +f32 Camera_InterpolateCurve(f32 a, f32 b) { + f32 ret; + f32 absB; + f32 t = 0.4f; + f32 t2; + f32 t3; + f32 t4; + + absB = fabsf(b); + if (a < absB) { + ret = 1.0f; + } else { + t2 = 1.0f - t; + if ((a * t2) > absB) { + t3 = SQ(b) * (1.0f - t); + t4 = SQ(a * t2); + ret = t3 / t4; + } else { + t3 = SQ(a - absB) * t; + t4 = SQ(0.4f * a); + ret = 1.0f - (t3 / t4); + } + } + return ret; +} + +/* + * Performs linear interpoloation between `cur` and `target`. If `cur` is within + * `minDiff` units, The result is rounded up to `target` + */ +f32 Camera_LERPCeilF(f32 target, f32 cur, f32 stepScale, f32 minDiff) { + f32 diff = target - cur; + f32 step; + f32 ret; + + if (fabsf(diff) >= minDiff) { + step = diff * stepScale; + ret = cur + step; + } else { + ret = target; + } + + return ret; +} + +/* + * Performs linear interpoloation between `cur` and `target`. If `cur` is within + * `minDiff` units, The result is rounded down to `cur` + */ +f32 Camera_LERPFloorF(f32 target, f32 cur, f32 stepScale, f32 minDiff) { + f32 diff = target - cur; + f32 step; + f32 ret; + + if (fabsf(diff) >= minDiff) { + step = diff * stepScale; + ret = cur + step; + } else { + ret = cur; + } + + return ret; +} + +/* + * Performs linear interpoloation between `cur` and `target`. If `cur` is within + * `minDiff` units, The result is rounded up to `target` + */ +s16 Camera_LERPCeilS(s16 target, s16 cur, f32 stepScale, s16 minDiff) { + s16 diff = target - cur; + s16 step; + s32 ret; + + if (ABS(diff) >= minDiff) { + step = diff * stepScale + 0.5f; + ret = cur + step; + } else { + ret = target; + } + + return ret; +} + +/* + * Performs linear interpoloation between `cur` and `target`. If `cur` is within + * `minDiff` units, The result is rounded down to `cur` + */ +s16 Camera_LERPFloorS(s16 target, s16 cur, f32 stepScale, s16 minDiff) { + s16 diff = target - cur; + s16 step; + s32 ret; + + if (ABS(diff) >= minDiff) { + step = diff * stepScale + 0.5f; + ret = cur + step; + } else { + ret = cur; + } + + return ret; +} + +/* + * Performs linear interpoloation between `cur` and `target`. If `cur` is within + * `minDiff` units, The result is rounded up to `target` + */ +void Camera_LERPCeilVec3f(Vec3f* target, Vec3f* cur, f32 yStepScale, f32 xzStepScale, f32 minDiff) { + cur->x = Camera_LERPCeilF(target->x, cur->x, xzStepScale, minDiff); + cur->y = Camera_LERPCeilF(target->y, cur->y, yStepScale, minDiff); + cur->z = Camera_LERPCeilF(target->z, cur->z, xzStepScale, minDiff); +} + +void func_80043ABC(Camera* camera) { + camera->yawUpdateRateInv = 100.0f; + camera->pitchUpdateRateInv = R_CAM_DEFA_PHI_UPDRATE; + camera->rUpdateRateInv = OREG(6); + camera->xzOffsetUpdateRate = PCT(OREG(2)); + camera->yOffsetUpdateRate = PCT(OREG(3)); + camera->fovUpdateRate = PCT(OREG(4)); +} + +void func_80043B60(Camera* camera) { + camera->rUpdateRateInv = OREG(27); + camera->yawUpdateRateInv = OREG(27); + camera->pitchUpdateRateInv = OREG(27); + camera->xzOffsetUpdateRate = 0.001f; + camera->yOffsetUpdateRate = 0.001f; + camera->fovUpdateRate = 0.001f; +} + +Vec3f* Camera_Vec3sToVec3f(Vec3f* dest, Vec3s* src) { + Vec3f copy; + + copy.x = src->x; + copy.y = src->y; + copy.z = src->z; + + *dest = copy; + return dest; +} + +Vec3f* Camera_Vec3fVecSphGeoAdd(Vec3f* dest, Vec3f* a, VecSph* b) { + Vec3f copy; + Vec3f vecB; + OLib_VecSphGeoToVec3f(&vecB, b); + + copy.x = a->x + vecB.x; + copy.y = a->y + vecB.y; + copy.z = a->z + vecB.z; + + *dest = copy; + return dest; +} + +Vec3f* Camera_Vec3fTranslateByUnitVector(Vec3f* dest, Vec3f* src, Vec3f* unitVector, f32 uvScale) { + Vec3f copy; + + copy.x = src->x + (unitVector->x * uvScale); + copy.y = src->y + (unitVector->y * uvScale); + copy.z = src->z + (unitVector->z * uvScale); + + *dest = copy; + return dest; +} + +/** + * Detects the collision poly between `from` and `to`, places collision info in `to` + */ +s32 Camera_BGCheckInfo(Camera* camera, Vec3f* from, CamColChk* to) { + CollisionContext* colCtx = &camera->globalCtx->colCtx; + Vec3f toNewPos; + Vec3f toPoint; + Vec3f fromToNorm; + f32 floorPolyY; + CollisionPoly* floorPoly; + s32 floorBgId = 0; + VecSph fromToOffset; + + OLib_Vec3fDiffToVecSphGeo(&fromToOffset, from, &to->pos); + fromToOffset.r += 8.0f; + Camera_Vec3fVecSphGeoAdd(&toPoint, from, &fromToOffset); + + if (!BgCheck_CameraLineTest1(colCtx, from, &toPoint, &toNewPos, &to->poly, 1, 1, 1, -1, &to->bgId)) { + // no poly in path. + OLib_Vec3fDistNormalize(&fromToNorm, from, &to->pos); + + to->norm.x = -fromToNorm.x; + to->norm.y = -fromToNorm.y; + to->norm.z = -fromToNorm.z; + + toNewPos = to->pos; + toNewPos.y += 5.0f; + floorPolyY = BgCheck_CameraRaycastFloor2(colCtx, &floorPoly, &floorBgId, &toNewPos); + + if ((to->pos.y - floorPolyY) > 5.0f) { + // if the y distance from the check point to the floor is more than 5 units + // the point is not colliding with any collision. + to->pos.x += to->norm.x; + to->pos.y += to->norm.y; + to->pos.z += to->norm.z; + return 0; + } + + to->poly = floorPoly; + toNewPos.y = floorPolyY + 1.0f; + to->bgId = floorBgId; + } + + to->norm.x = COLPOLY_GET_NORMAL(to->poly->normal.x); + to->norm.y = COLPOLY_GET_NORMAL(to->poly->normal.y); + to->norm.z = COLPOLY_GET_NORMAL(to->poly->normal.z); + to->pos.x = to->norm.x + toNewPos.x; + to->pos.y = to->norm.y + toNewPos.y; + to->pos.z = to->norm.z + toNewPos.z; + + return floorBgId + 1; +} + +/** + * Detects if there is collision between `from` and `to` + */ +s32 Camera_BGCheck(Camera* camera, Vec3f* from, Vec3f* to) { + CamColChk toCol; + s32 bgId; + + toCol.pos = *to; + bgId = Camera_BGCheckInfo(camera, from, &toCol); + *to = toCol.pos; + return bgId; +} + +s32 func_80043F94(Camera* camera, Vec3f* from, CamColChk* to) { + CollisionContext* colCtx = &camera->globalCtx->colCtx; + Vec3f toNewPos; + Vec3f toPos; + Vec3f fromToNorm; + Vec3f playerFloorNormF; + f32 floorY; + CollisionPoly* floorPoly; + s32 bgId; + VecSph fromToGeo; + + OLib_Vec3fDiffToVecSphGeo(&fromToGeo, from, &to->pos); + fromToGeo.r += 8.0f; + Camera_Vec3fVecSphGeoAdd(&toPos, from, &fromToGeo); + if (!BgCheck_CameraLineTest1(colCtx, from, &toPos, &toNewPos, &to->poly, 1, 1, 1, -1, &to->bgId)) { + OLib_Vec3fDistNormalize(&fromToNorm, from, &to->pos); + to->norm.x = -fromToNorm.x; + to->norm.y = -fromToNorm.y; + to->norm.z = -fromToNorm.z; + toNewPos = to->pos; + toNewPos.y += 5.0f; + floorY = BgCheck_CameraRaycastFloor2(colCtx, &floorPoly, &bgId, &toNewPos); + if ((to->pos.y - floorY) > 5.0f) { + // to is not on the ground or below it. + to->pos.x += to->norm.x; + to->pos.y += to->norm.y; + to->pos.z += to->norm.z; + return 0; + } + // to is touching the ground, move it up 1 unit. + to->poly = floorPoly; + toNewPos.y = floorY + 1.0f; + to->bgId = bgId; + } + to->norm.x = COLPOLY_GET_NORMAL(to->poly->normal.x); + to->norm.y = COLPOLY_GET_NORMAL(to->poly->normal.y); + to->norm.z = COLPOLY_GET_NORMAL(to->poly->normal.z); + if ((to->norm.y > 0.5f) || (to->norm.y < -0.8f)) { + to->pos.x = to->norm.x + toNewPos.x; + to->pos.y = to->norm.y + toNewPos.y; + to->pos.z = to->norm.z + toNewPos.z; + } else if (playerFloorPoly != NULL) { + playerFloorNormF.x = COLPOLY_GET_NORMAL(playerFloorPoly->normal.x); + playerFloorNormF.y = COLPOLY_GET_NORMAL(playerFloorPoly->normal.y); + playerFloorNormF.z = COLPOLY_GET_NORMAL(playerFloorPoly->normal.z); + if (Math3D_LineSegVsPlane(playerFloorNormF.x, playerFloorNormF.y, playerFloorNormF.z, playerFloorPoly->dist, + from, &toPos, &toNewPos, 1)) { + // line is from->to is touching the poly the player is on. + to->norm = playerFloorNormF; + to->poly = playerFloorPoly; + to->bgId = camera->bgCheckId; + to->pos.x = to->norm.x + toNewPos.x; + to->pos.y = to->norm.y + toNewPos.y; + to->pos.z = to->norm.z + toNewPos.z; + } else { + OLib_Vec3fDistNormalize(&fromToNorm, from, &to->pos); + to->norm.x = -fromToNorm.x; + to->norm.y = -fromToNorm.y; + to->norm.z = -fromToNorm.z; + to->pos.x += to->norm.x; + to->pos.y += to->norm.y; + to->pos.z += to->norm.z; + return 0; + } + } + return 1; +} + +void func_80044340(Camera* camera, Vec3f* arg1, Vec3f* arg2) { + CamColChk sp20; + Vec3s unused; + + sp20.pos = *arg2; + func_80043F94(camera, arg1, &sp20); + *arg2 = sp20.pos; +} + +/** + * Checks if `from` to `to` is looking from the outside of a poly towards the front + */ +s32 Camera_CheckOOB(Camera* camera, Vec3f* from, Vec3f* to) { + s32 pad; + Vec3f intersect; + s32 pad2; + s32 bgId; + CollisionPoly* poly; + CollisionContext* colCtx = &camera->globalCtx->colCtx; + + poly = NULL; + if (BgCheck_CameraLineTest1(colCtx, from, to, &intersect, &poly, 1, 1, 1, 0, &bgId) && + (CollisionPoly_GetPointDistanceFromPlane(poly, from) < 0.0f)) { + // if there is a poly between `from` and `to` and the `from` is behind the poly. + return true; + } + + return false; +} + +/** + * Gets the floor position underneath `chkPos`, and returns the normal of the floor to `floorNorm`, + * and bgId to `bgId`. If no floor is found, then the normal is a flat surface pointing upwards. + */ +f32 Camera_GetFloorYNorm(Camera* camera, Vec3f* floorNorm, Vec3f* chkPos, s32* bgId) { + s32 pad; + CollisionPoly* floorPoly; + f32 floorY = BgCheck_EntityRaycastFloor3(&camera->globalCtx->colCtx, &floorPoly, bgId, chkPos); + + if (floorY == BGCHECK_Y_MIN) { + // no floor + floorNorm->x = 0.0f; + floorNorm->y = 1.0f; + floorNorm->z = 0.0f; + } else { + floorNorm->x = COLPOLY_GET_NORMAL(floorPoly->normal.x); + floorNorm->y = COLPOLY_GET_NORMAL(floorPoly->normal.y); + floorNorm->z = COLPOLY_GET_NORMAL(floorPoly->normal.z); + } + + return floorY; +} + +/** + * Gets the position of the floor from `pos` + */ +f32 Camera_GetFloorY(Camera* camera, Vec3f* pos) { + Vec3f posCheck; + Vec3f floorNorm; + s32 bgId; + + posCheck = *pos; + posCheck.y += 80.0f; + + return Camera_GetFloorYNorm(camera, &floorNorm, &posCheck, &bgId); +} + +/** + * Gets the position of the floor from `pos`, and if the floor is considered not solid, + * it checks the next floor below that up to 3 times. Returns the normal of the floor into `norm` + */ +f32 Camera_GetFloorYLayer(Camera* camera, Vec3f* norm, Vec3f* pos, s32* bgId) { + CollisionPoly* floorPoly; + CollisionContext* colCtx = &camera->globalCtx->colCtx; + f32 floorY; + s32 i; + + for (i = 3; i > 0; i--) { + floorY = BgCheck_CameraRaycastFloor2(colCtx, &floorPoly, bgId, pos); + if (floorY == BGCHECK_Y_MIN || + (camera->playerGroundY < floorY && !(COLPOLY_GET_NORMAL(floorPoly->normal.y) > 0.5f))) { + // no floor, or player is below the floor and floor is not considered steep + norm->x = 0.0f; + norm->y = 1.0f; + norm->z = 0.0f; + floorY = BGCHECK_Y_MIN; + break; + } else if (func_80041D4C(colCtx, floorPoly, *bgId) == 1) { + // floor is not solid, check below that floor. + pos->y = floorY - 10.0f; + continue; + } else { + norm->x = COLPOLY_GET_NORMAL(floorPoly->normal.x); + norm->y = COLPOLY_GET_NORMAL(floorPoly->normal.y); + norm->z = COLPOLY_GET_NORMAL(floorPoly->normal.z); + break; + } + } + if (i == 0) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: foward check: too many layer!\n" VT_RST); + } + return floorY; +} + +/** + * Returns the CameraSettingType of the camera at index `camDataIdx` + */ +s16 Camera_GetCamDataSetting(Camera* camera, s32 camDataIdx) { + return func_80041A4C(&camera->globalCtx->colCtx, camDataIdx, BGCHECK_SCENE); +} + +/** + * Returns the scene camera info for the current camera data index + */ +Vec3s* Camera_GetCamBGData(Camera* camera) { + return func_80041C10(&camera->globalCtx->colCtx, camera->camDataIdx, BGCHECK_SCENE); +} + +/** + * Gets the scene's camera index for the poly `poly`, returns -1 if + * there is no camera data for that poly. + */ +s32 Camera_GetDataIdxForPoly(Camera* camera, s32* bgId, CollisionPoly* poly) { + s32 camDataIdx; + PosRot playerPosRot; + s32 ret; + + Actor_GetWorldPosShapeRot(&playerPosRot, &camera->player->actor); // unused. + camDataIdx = SurfaceType_GetCamDataIndex(&camera->globalCtx->colCtx, poly, *bgId); + + if (func_80041A4C(&camera->globalCtx->colCtx, camDataIdx, *bgId) == CAM_SET_NONE) { + ret = -1; + } else { + ret = camDataIdx; + } + return ret; +} + +/** + * Returns the scene camera info for the floor under the player. + * If there is no floor then there is no camera data, returns the number of + * pieces of data there are in `dataCnt` + */ +Vec3s* Camera_GetCamBgDataUnderPlayer(Camera* camera, u16* dataCnt) { + CollisionPoly* floorPoly; + s32 pad; + s32 bgId; + PosRot playerPosShape; + + Actor_GetWorldPosShapeRot(&playerPosShape, &camera->player->actor); + playerPosShape.pos.y += Player_GetHeight(camera->player); + if (BgCheck_EntityRaycastFloor3(&camera->globalCtx->colCtx, &floorPoly, &bgId, &playerPosShape.pos) == + BGCHECK_Y_MIN) { + // no floor + return NULL; + } + *dataCnt = SurfaceType_GetNumCameras(&camera->globalCtx->colCtx, floorPoly, bgId); + return SurfaceType_GetCamPosData(&camera->globalCtx->colCtx, floorPoly, bgId); +} + +/** + * Gets the Camera information for the water box the player is in. + * Returns -1 if the player is not in a water box, or does not have a swimming state. + * Returns -2 if there is no camera index for the water box. + * Returns the camera data index otherwise. + */ +s32 Camera_GetWaterBoxDataIdx(Camera* camera, f32* waterY) { + PosRot playerPosShape; + WaterBox* waterBox; + s32 ret; + + Actor_GetWorldPosShapeRot(&playerPosShape, &camera->player->actor); + *waterY = playerPosShape.pos.y; + + if (!WaterBox_GetSurface1(camera->globalCtx, &camera->globalCtx->colCtx, playerPosShape.pos.x, playerPosShape.pos.z, + waterY, &waterBox)) { + // player's position is not in a water box. + *waterY = BGCHECK_Y_MIN; + return -1; + } + + if (!(camera->player->stateFlags1 & 0x8000000)) { + // player is not swimming + *waterY = BGCHECK_Y_MIN; + return -1; + } + + ret = WaterBox_GetCamDataIndex(&camera->globalCtx->colCtx, waterBox); + if ((ret <= 0) || (WaterBox_GetCameraSType(&camera->globalCtx->colCtx, waterBox) <= 0)) { + // no camera data idx, or no CameraSettingType + return -2; + } + + return ret; +} + +/** + * Checks if `chkPos` is inside a waterbox. If there is no water box below `chkPos` + * or if `chkPos` is above the water surface, return BGCHECK_Y_MIN, output + * environment properites to `envProp` if `chkPos` is inside the waterbox. + */ +f32 Camera_GetWaterSurface(Camera* camera, Vec3f* chkPos, s32* envProp) { + PosRot playerPosRot; + f32 waterY; + WaterBox* waterBox; + + Actor_GetWorldPosShapeRot(&playerPosRot, &camera->player->actor); + waterY = playerPosRot.pos.y; + + if (!WaterBox_GetSurface1(camera->globalCtx, &camera->globalCtx->colCtx, chkPos->x, chkPos->z, &waterY, + &waterBox)) { + // chkPos is not within the x/z boundaries of a water box. + return BGCHECK_Y_MIN; + } + + if (waterY < chkPos->y) { + // the water's y position is below the check position + // meaning the position is NOT in the water. + return BGCHECK_Y_MIN; + } + + *envProp = WaterBox_GetLightSettingIndex(&camera->globalCtx->colCtx, waterBox); + return waterY; +} + +/** + * Calculates the angle between points `from` and `to` + */ +s16 Camera_XZAngle(Vec3f* to, Vec3f* from) { + return DEGF_TO_BINANG(RADF_TO_DEGF(Math_FAtan2F(from->x - to->x, from->z - to->z))); +} + +s16 func_80044ADC(Camera* camera, s16 yaw, s16 arg2) { + static f32 D_8015CE50; + static f32 D_8015CE54; + static CamColChk D_8015CE58; + Vec3f playerPos; + Vec3f rotatedPos; + Vec3f floorNorm; + f32 temp_f2; + s16 temp_s0; + s16 temp_s1; + f32 phi_f18; + f32 sinYaw; + f32 cosYaw; + s32 bgId; + f32 sp30; + f32 sp2C; + f32 phi_f16; + f32 playerHeight; + + sinYaw = Math_SinS(yaw); + cosYaw = Math_CosS(yaw); + playerHeight = Player_GetHeight(camera->player); + temp_f2 = PCT(OREG(19)) * playerHeight; + sp30 = PCT(OREG(17)) * playerHeight; + sp2C = PCT(OREG(18)) * playerHeight; + playerPos.x = camera->playerPosRot.pos.x; + playerPos.y = camera->playerGroundY + temp_f2; + playerPos.z = camera->playerPosRot.pos.z; + rotatedPos.x = playerPos.x + (sp30 * sinYaw); + rotatedPos.y = playerPos.y; + rotatedPos.z = playerPos.z + (sp30 * cosYaw); + if (arg2 || (camera->globalCtx->state.frames % 2) == 0) { + D_8015CE58.pos.x = playerPos.x + (sp2C * sinYaw); + D_8015CE58.pos.y = playerPos.y; + D_8015CE58.pos.z = playerPos.z + (sp2C * cosYaw); + Camera_BGCheckInfo(camera, &playerPos, &D_8015CE58); + if (arg2) { + D_8015CE50 = D_8015CE54 = camera->playerGroundY; + } + } else { + sp2C = OLib_Vec3fDistXZ(&playerPos, &D_8015CE58.pos); + D_8015CE58.pos.x += D_8015CE58.norm.x * 5.0f; + D_8015CE58.pos.y += D_8015CE58.norm.y * 5.0f; + D_8015CE58.pos.z += D_8015CE58.norm.z * 5.0f; + if (sp2C < sp30) { + sp30 = sp2C; + D_8015CE50 = D_8015CE54 = Camera_GetFloorYLayer(camera, &floorNorm, &D_8015CE58.pos, &bgId); + } else { + D_8015CE50 = Camera_GetFloorYLayer(camera, &floorNorm, &rotatedPos, &bgId); + D_8015CE54 = Camera_GetFloorYLayer(camera, &floorNorm, &D_8015CE58.pos, &bgId); + } + + if (D_8015CE50 == BGCHECK_Y_MIN) { + D_8015CE50 = camera->playerGroundY; + } + + if (D_8015CE54 == BGCHECK_Y_MIN) { + D_8015CE54 = D_8015CE50; + } + } + phi_f16 = PCT(OREG(20)) * (D_8015CE50 - camera->playerGroundY); + phi_f18 = (1.0f - PCT(OREG(20))) * (D_8015CE54 - camera->playerGroundY); + temp_s0 = DEGF_TO_BINANG(RADF_TO_DEGF(Math_FAtan2F(phi_f16, sp30))); + temp_s1 = DEGF_TO_BINANG(RADF_TO_DEGF(Math_FAtan2F(phi_f18, sp2C))); + return temp_s0 + temp_s1; +} + +Vec3f* Camera_CalcUpFromPitchYawRoll(Vec3f* dest, s16 pitch, s16 yaw, s16 roll) { + f32 sinPitch; + f32 cosPitch; + f32 sinYaw; + f32 cosYaw; + f32 sinNegRoll; + f32 cosNegRoll; + Vec3f spA4; + f32 pad; + f32 sp54; + f32 sp4C; + f32 cosPitchCosYawSinRoll; + f32 negSinPitch; + f32 temp_f10_2; + f32 cosPitchcosYaw; + f32 temp_f14; + f32 negSinPitchSinYaw; + f32 negSinPitchCosYaw; + f32 cosPitchSinYaw; + f32 temp_f4_2; + f32 temp_f6; + f32 temp_f8; + f32 temp_f8_2; + f32 temp_f8_3; + + sinPitch = Math_SinS(pitch); + cosPitch = Math_CosS(pitch); + sinYaw = Math_SinS(yaw); + cosYaw = Math_CosS(yaw); + negSinPitch = -sinPitch; + sinNegRoll = Math_SinS(-roll); + cosNegRoll = Math_CosS(-roll); + negSinPitchSinYaw = negSinPitch * sinYaw; + temp_f14 = 1.0f - cosNegRoll; + cosPitchSinYaw = cosPitch * sinYaw; + sp54 = SQ(cosPitchSinYaw); + sp4C = (cosPitchSinYaw * sinPitch) * temp_f14; + cosPitchcosYaw = cosPitch * cosYaw; + temp_f4_2 = ((1.0f - sp54) * cosNegRoll) + sp54; + cosPitchCosYawSinRoll = cosPitchcosYaw * sinNegRoll; + negSinPitchCosYaw = negSinPitch * cosYaw; + temp_f6 = (cosPitchcosYaw * cosPitchSinYaw) * temp_f14; + temp_f10_2 = sinPitch * sinNegRoll; + spA4.x = ((negSinPitchSinYaw * temp_f4_2) + (cosPitch * (sp4C - cosPitchCosYawSinRoll))) + + (negSinPitchCosYaw * (temp_f6 + temp_f10_2)); + sp54 = SQ(sinPitch); + temp_f4_2 = (sinPitch * cosPitchcosYaw) * temp_f14; + temp_f8_3 = cosPitchSinYaw * sinNegRoll; + temp_f8 = sp4C + cosPitchCosYawSinRoll; + spA4.y = ((negSinPitchSinYaw * temp_f8) + (cosPitch * (((1.0f - sp54) * cosNegRoll) + sp54))) + + (negSinPitchCosYaw * (temp_f4_2 - temp_f8_3)); + temp_f8_2 = temp_f6 - temp_f10_2; + spA4.z = ((negSinPitchSinYaw * temp_f8_2) + (cosPitch * (temp_f4_2 + temp_f8_3))) + + (negSinPitchCosYaw * (((1.0f - SQ(cosPitchcosYaw)) * cosNegRoll) + SQ(cosPitchcosYaw))); + *dest = spA4; + return dest; +} + +f32 Camera_ClampLERPScale(Camera* camera, f32 maxLERPScale) { + f32 ret; + + if (camera->atLERPStepScale < PCT(R_AT_LERP_MIN)) { + ret = PCT(R_AT_LERP_MIN); + } else if (camera->atLERPStepScale >= maxLERPScale) { + ret = maxLERPScale; + } else { + ret = PCT(R_AT_LERP_SCALE) * camera->atLERPStepScale; + } + + return ret; +} + +void Camera_CopyDataToRegs(Camera* camera, s16 mode) { + CameraModeValue* values; + CameraModeValue* valueP; + s32 i; + + if (PREG(82)) { + osSyncPrintf("camera: res: stat (%d/%d/%d)\n", camera->thisIdx, camera->setting, mode); + } + + values = sCameraSettings[camera->setting].cameraModes[mode].values; + + for (i = 0; i < sCameraSettings[camera->setting].cameraModes[mode].valueCnt; i++) { + valueP = &values[i]; + PREG(valueP->dataType) = valueP->val; + if (PREG(82)) { + osSyncPrintf("camera: res: PREG(%02d) = %d\n", valueP->dataType, valueP->val); + } + } + camera->animState = 0; +} + +s32 Camera_CopyPREGToModeValues(Camera* camera) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + CameraModeValue* valueP; + s32 i; + + for (i = 0; i < sCameraSettings[camera->setting].cameraModes[camera->mode].valueCnt; i++) { + valueP = &values[i]; + valueP->val = R_CAM_DATA(valueP->dataType); + if (PREG(82)) { + osSyncPrintf("camera: res: %d = PREG(%02d)\n", valueP->val, valueP->dataType); + } + } + return true; +} + +#define SHRINKWIN_MASK (0xF000) +#define SHRINKWINVAL_MASK (0x7000) +#define SHRINKWIN_CURVAL (0x8000) +#define IFACE_ALPHA_MASK (0x0F00) + +void Camera_UpdateInterface(s16 flags) { + s16 interfaceAlpha; + + if ((flags & SHRINKWIN_MASK) != SHRINKWIN_MASK) { + switch (flags & SHRINKWINVAL_MASK) { + case 0x1000: + sCameraShrinkWindowVal = 0x1A; + break; + case 0x2000: + sCameraShrinkWindowVal = 0x1B; + break; + case 0x3000: + sCameraShrinkWindowVal = 0x20; + break; + default: + sCameraShrinkWindowVal = 0; + break; + } + + if (flags & SHRINKWIN_CURVAL) { + ShrinkWindow_SetCurrentVal(sCameraShrinkWindowVal); + } else { + ShrinkWindow_SetVal(sCameraShrinkWindowVal); + } + } + + if ((flags & IFACE_ALPHA_MASK) != IFACE_ALPHA_MASK) { + interfaceAlpha = (flags & IFACE_ALPHA_MASK) >> 8; + if (interfaceAlpha == 0) { + interfaceAlpha = 0x32; + } + if (interfaceAlpha != sCameraInterfaceAlpha) { + sCameraInterfaceAlpha = interfaceAlpha; + Interface_ChangeAlpha(sCameraInterfaceAlpha); + } + } +} + +Vec3f* Camera_BGCheckCorner(Vec3f* dst, Vec3f* linePointA, Vec3f* linePointB, CamColChk* pointAColChk, + CamColChk* pointBColChk) { + Vec3f closestPoint; + + if (!func_800427B4(pointAColChk->poly, pointBColChk->poly, linePointA, linePointB, &closestPoint)) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: corner check no cross point %x %x\n" VT_RST, pointAColChk, + pointBColChk); + *dst = pointAColChk->pos; + return dst; + } + + *dst = closestPoint; + return dst; +} + +/** + * Checks collision between at and eyeNext, if `checkEye` is set, if there is no collsion between + * eyeNext->at, then eye->at is also checked. + * Returns: + * 0 if no collsion is found between at->eyeNext + * 2 if the angle between the polys is between 60 degrees and 120 degrees + * 3 ? + * 6 if the angle between the polys is greater than 120 degrees + */ +s32 func_80045508(Camera* camera, VecSph* diffSph, CamColChk* eyeChk, CamColChk* atChk, s16 checkEye) { + Vec3f* at = &camera->at; + Vec3f* eye = &camera->eye; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f eyePos; + s32 atEyeBgId; + s32 eyeAtBgId; + s32 ret; + f32 cosEyeAt; + + eyeChk->pos = camera->eyeNext; + + ret = 0; + + atEyeBgId = Camera_BGCheckInfo(camera, at, eyeChk); + if (atEyeBgId != 0) { + // collision found between at->eye + atChk->pos = camera->at; + + OLib_Vec3fToVecSphGeo(&eyeChk->sphNorm, &eyeChk->norm); + + if (eyeChk->sphNorm.pitch >= 0x2EE1) { + eyeChk->sphNorm.yaw = diffSph->yaw; + } + + eyeAtBgId = Camera_BGCheckInfo(camera, eyeNext, atChk); + + if (eyeAtBgId == 0) { + // no collision from eyeNext->at + if (checkEye & 1) { + + atChk->pos = *at; + eyePos = *eye; + + if (Camera_BGCheckInfo(camera, &eyePos, atChk) == 0) { + // no collision from eye->at + return 3; + } else if (eyeChk->poly == atChk->poly) { + // at->eye and eye->at is the same poly + return 3; + } + } else { + return 3; + } + } else if (eyeChk->poly == atChk->poly) { + // at->eyeNext and eyeNext->at is the same poly + return 3; + } + + OLib_Vec3fToVecSphGeo(&atChk->sphNorm, &atChk->norm); + + if (atChk->sphNorm.pitch >= 0x2EE1) { + atChk->sphNorm.yaw = BINANG_ROT180(diffSph->yaw); + } + + if (atEyeBgId != eyeAtBgId) { + // different bgIds for at->eye[Next] and eye[Next]->at + ret = 3; + } else { + cosEyeAt = Math3D_Cos(&eyeChk->norm, &atChk->norm); + if (cosEyeAt < -0.5f) { + ret = 6; + } else if (cosEyeAt > 0.5f) { + ret = 3; + } else { + ret = 2; + } + } + } + return ret; +} + +/** + * Calculates how much to adjust the camera at's y value when on a slope. + */ +f32 Camera_CalcSlopeYAdj(Vec3f* floorNorm, s16 playerYRot, s16 eyeAtYaw, f32 adjAmt) { + f32 tmp; + VecSph floorNormSph; + + OLib_Vec3fToVecSphGeo(&floorNormSph, floorNorm); + + tmp = Math_CosS(floorNormSph.pitch) * Math_CosS(playerYRot - floorNormSph.yaw); + return (fabsf(tmp) * adjAmt) * Math_CosS(playerYRot - eyeAtYaw); +} + +/** + * Calculates new at vector for the camera pointing in `eyeAtDir` + */ +s32 Camera_CalcAtDefault(Camera* camera, VecSph* eyeAtDir, f32 extraYOffset, s16 calcSlope) { + Vec3f* at = &camera->at; + Vec3f posOffsetTarget; + Vec3f atTarget; + s32 pad2; + PosRot* playerPosRot = &camera->playerPosRot; + f32 yOffset; + + yOffset = Player_GetHeight(camera->player); + + posOffsetTarget.x = 0.f; + posOffsetTarget.y = yOffset + extraYOffset; + posOffsetTarget.z = 0.f; + + if (calcSlope) { + posOffsetTarget.y -= OLib_ClampMaxDist( + Camera_CalcSlopeYAdj(&camera->floorNorm, playerPosRot->rot.y, eyeAtDir->yaw, OREG(9)), yOffset); + } + + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, camera->yOffsetUpdateRate, camera->xzOffsetUpdateRate, + 0.1f); + + atTarget.x = playerPosRot->pos.x + camera->posOffset.x; + atTarget.y = playerPosRot->pos.y + camera->posOffset.y; + atTarget.z = playerPosRot->pos.z + camera->posOffset.z; + + Camera_LERPCeilVec3f(&atTarget, at, camera->atLERPStepScale, camera->atLERPStepScale, 0.2f); + + return true; +} + +s32 func_800458D4(Camera* camera, VecSph* eyeAtDir, f32 arg2, f32* arg3, s16 arg4) { + f32 phi_f2; + Vec3f posOffsetTarget; + Vec3f atTarget; + f32 eyeAtAngle; + PosRot* playerPosRot = &camera->playerPosRot; + f32 deltaY; + s32 pad[2]; + + posOffsetTarget.y = Player_GetHeight(camera->player) + arg2; + posOffsetTarget.x = 0.0f; + posOffsetTarget.z = 0.0f; + + if (arg4) { + posOffsetTarget.y -= Camera_CalcSlopeYAdj(&camera->floorNorm, playerPosRot->rot.y, eyeAtDir->yaw, OREG(9)); + } + + deltaY = playerPosRot->pos.y - *arg3; + eyeAtAngle = Math_FAtan2F(deltaY, OLib_Vec3fDistXZ(&camera->at, &camera->eye)); + + if (eyeAtAngle > DEGF_TO_RADF(OREG(32))) { + if (1) {} + phi_f2 = 1.0f - sinf(DEGF_TO_RADF(eyeAtAngle - OREG(32))); + } else if (eyeAtAngle < DEGF_TO_RADF(OREG(33))) { + phi_f2 = 1.0f - sinf(DEGF_TO_RADF(OREG(33)) - eyeAtAngle); + } else { + phi_f2 = 1.0f; + } + + posOffsetTarget.y -= deltaY * phi_f2; + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, PCT(OREG(29)), PCT(OREG(30)), 0.1f); + + atTarget.x = playerPosRot->pos.x + camera->posOffset.x; + atTarget.y = playerPosRot->pos.y + camera->posOffset.y; + atTarget.z = playerPosRot->pos.z + camera->posOffset.z; + + Camera_LERPCeilVec3f(&atTarget, &camera->at, camera->atLERPStepScale, camera->atLERPStepScale, 0.2f); + + return 1; +} + +s32 func_80045B08(Camera* camera, VecSph* eyeAtDir, f32 yExtra, s16 arg3) { + f32 phi_f2; + Vec3f posOffsetTarget; + Vec3f atTarget; + f32 sp38; // unused + f32 temp_ret; + PosRot* playerPosRot = &camera->playerPosRot; + + posOffsetTarget.y = Player_GetHeight(camera->player) + yExtra; + posOffsetTarget.x = 0.0f; + posOffsetTarget.z = 0.0f; + + temp_ret = Math_SinS(arg3); + + if (temp_ret < 0.0f) { + phi_f2 = Math_CosS(playerPosRot->rot.y - eyeAtDir->yaw); + } else { + phi_f2 = -Math_CosS(playerPosRot->rot.y - eyeAtDir->yaw); + } + + posOffsetTarget.y -= temp_ret * phi_f2 * OREG(9); + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, camera->yOffsetUpdateRate, camera->xzOffsetUpdateRate, + 0.1f); + + atTarget.x = playerPosRot->pos.x + camera->posOffset.x; + atTarget.y = playerPosRot->pos.y + camera->posOffset.y; + atTarget.z = playerPosRot->pos.z + camera->posOffset.z; + Camera_LERPCeilVec3f(&atTarget, &camera->at, camera->atLERPStepScale, camera->atLERPStepScale, 0.2f); + + return 1; +} + +/** + * Adjusts the camera's at position for Camera_Parallel1 + */ +s32 Camera_CalcAtForParallel(Camera* camera, VecSph* arg1, f32 yOffset, f32* arg3, s16 arg4) { + Vec3f* at = &camera->at; + Vec3f posOffsetTarget; + Vec3f atTarget; + Vec3f* eye = &camera->eye; + PosRot* playerPosRot = &camera->playerPosRot; + f32 temp_f2; + f32 phi_f16; + f32 sp54; + f32 phi_f20; + f32 temp_f0_4; + + temp_f0_4 = Player_GetHeight(camera->player); + posOffsetTarget.x = 0.0f; + posOffsetTarget.y = temp_f0_4 + yOffset; + posOffsetTarget.z = 0.0f; + + if (PREG(76) && arg4) { + posOffsetTarget.y -= Camera_CalcSlopeYAdj(&camera->floorNorm, playerPosRot->rot.y, arg1->yaw, OREG(9)); + } + + if (camera->playerGroundY == camera->playerPosRot.pos.y || camera->player->actor.gravity > -0.1f || + camera->player->stateFlags1 & 0x200000) { + *arg3 = Camera_LERPCeilF(playerPosRot->pos.y, *arg3, PCT(OREG(43)), 0.1f); + phi_f20 = playerPosRot->pos.y - *arg3; + posOffsetTarget.y -= phi_f20; + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, camera->yOffsetUpdateRate, + camera->xzOffsetUpdateRate, 0.1f); + } else { + if (!PREG(75)) { + phi_f20 = playerPosRot->pos.y - *arg3; + sp54 = OLib_Vec3fDistXZ(at, &camera->eye); + phi_f16 = sp54; + Math_FAtan2F(phi_f20, sp54); + temp_f2 = Math_FTanF(DEG_TO_RAD(camera->fov * 0.4f)) * phi_f16; + if (temp_f2 < phi_f20) { + *arg3 += phi_f20 - temp_f2; + phi_f20 = temp_f2; + } else if (phi_f20 < -temp_f2) { + *arg3 += phi_f20 + temp_f2; + phi_f20 = -temp_f2; + } + posOffsetTarget.y -= phi_f20; + } else { + phi_f20 = playerPosRot->pos.y - *arg3; + temp_f2 = Math_FAtan2F(phi_f20, OLib_Vec3fDistXZ(at, eye)); + if (DEG_TO_RAD(OREG(32)) < temp_f2) { + phi_f16 = 1 - sinf(temp_f2 - DEG_TO_RAD(OREG(32))); + } else if (temp_f2 < DEG_TO_RAD(OREG(33))) { + phi_f16 = 1 - sinf(DEG_TO_RAD(OREG(33)) - temp_f2); + } else { + phi_f16 = 1; + } + posOffsetTarget.y -= phi_f20 * phi_f16; + } + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, PCT(OREG(29)), PCT(OREG(30)), 0.1f); + camera->yOffsetUpdateRate = PCT(OREG(29)); + camera->xzOffsetUpdateRate = PCT(OREG(30)); + } + atTarget.x = playerPosRot->pos.x + camera->posOffset.x; + atTarget.y = playerPosRot->pos.y + camera->posOffset.y; + atTarget.z = playerPosRot->pos.z + camera->posOffset.z; + Camera_LERPCeilVec3f(&atTarget, at, camera->atLERPStepScale, camera->atLERPStepScale, 0.2f); + return 1; +} + +/** + * Adjusts at position for Camera_Battle1 and Camera_KeepOn1 + */ +s32 Camera_CalcAtForLockOn(Camera* camera, VecSph* eyeAtDir, Vec3f* targetPos, f32 yOffset, f32 distance, + f32* yPosOffset, VecSph* outPlayerToTargetDir, s16 flags) { + Vec3f* at = &camera->at; + Vec3f tmpPos0; + Vec3f tmpPos1; + Vec3f lookFromOffset; + Vec3f* floorNorm = &camera->floorNorm; + VecSph playerToTargetDir; + PosRot* playerPosRot = &camera->playerPosRot; + f32 yPosDelta; + f32 phi_f16; + f32 eyeAtDist; + f32 temp_f0_2; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + tmpPos0.x = 0.0f; + tmpPos0.y = playerHeight + yOffset; + tmpPos0.z = 0.0f; + if (PREG(76) && (flags & FLG_ADJSLOPE)) { + tmpPos0.y -= Camera_CalcSlopeYAdj(floorNorm, playerPosRot->rot.y, eyeAtDir->yaw, OREG(9)); + } + + // tmpPos1 is player's head + tmpPos1 = playerPosRot->pos; + tmpPos1.y += playerHeight; + OLib_Vec3fDiffToVecSphGeo(outPlayerToTargetDir, &tmpPos1, targetPos); + playerToTargetDir = *outPlayerToTargetDir; + if (distance < playerToTargetDir.r) { + playerToTargetDir.r = playerToTargetDir.r * PCT(OREG(38)); + } else { + // ratio of player's height off ground to player's height. + temp_f0_2 = OLib_ClampMaxDist((playerPosRot->pos.y - camera->playerGroundY) / playerHeight, 1.0f); + playerToTargetDir.r = + (playerToTargetDir.r * PCT(OREG(39))) - + (((PCT(OREG(39)) - PCT(OREG(38))) * playerToTargetDir.r) * (playerToTargetDir.r / distance)); + playerToTargetDir.r = playerToTargetDir.r - (playerToTargetDir.r * temp_f0_2) * temp_f0_2; + } + + if (flags & FLG_OFFGROUND) { + playerToTargetDir.r *= 0.2f; + camera->xzOffsetUpdateRate = camera->yOffsetUpdateRate = .01f; + } + + OLib_VecSphGeoToVec3f(&lookFromOffset, &playerToTargetDir); + + if (PREG(89)) { + osSyncPrintf("%f (%f %f %f) %f\n", playerToTargetDir.r / distance, lookFromOffset.x, lookFromOffset.y, + lookFromOffset.z, camera->atLERPStepScale); + } + + tmpPos0.x = tmpPos0.x + lookFromOffset.x; + tmpPos0.y = tmpPos0.y + lookFromOffset.y; + tmpPos0.z = tmpPos0.z + lookFromOffset.z; + + if (camera->playerGroundY == camera->playerPosRot.pos.y || camera->player->actor.gravity > -0.1f || + camera->player->stateFlags1 & 0x200000) { + *yPosOffset = Camera_LERPCeilF(playerPosRot->pos.y, *yPosOffset, PCT(OREG(43)), 0.1f); + yPosDelta = playerPosRot->pos.y - *yPosOffset; + tmpPos0.y -= yPosDelta; + Camera_LERPCeilVec3f(&tmpPos0, &camera->posOffset, camera->yOffsetUpdateRate, camera->xzOffsetUpdateRate, 0.1f); + } else { + if (!(flags & FLG_OFFGROUND)) { + yPosDelta = playerPosRot->pos.y - *yPosOffset; + eyeAtDist = OLib_Vec3fDistXZ(at, &camera->eye); + phi_f16 = eyeAtDist; + Math_FAtan2F(yPosDelta, eyeAtDist); + temp_f0_2 = Math_FTanF(DEG_TO_RAD(camera->fov * 0.4f)) * phi_f16; + if (temp_f0_2 < yPosDelta) { + *yPosOffset = *yPosOffset + (yPosDelta - temp_f0_2); + yPosDelta = temp_f0_2; + } else if (yPosDelta < -temp_f0_2) { + *yPosOffset = *yPosOffset + (yPosDelta + temp_f0_2); + yPosDelta = -temp_f0_2; + } + tmpPos0.y = tmpPos0.y - yPosDelta; + } else { + yPosDelta = playerPosRot->pos.y - *yPosOffset; + temp_f0_2 = Math_FAtan2F(yPosDelta, OLib_Vec3fDistXZ(at, &camera->eye)); + + if (temp_f0_2 > DEG_TO_RAD(OREG(32))) { + phi_f16 = 1.0f - sinf(temp_f0_2 - DEG_TO_RAD(OREG(32))); + } else if (temp_f0_2 < DEG_TO_RAD(OREG(33))) { + phi_f16 = 1.0f - sinf(DEG_TO_RAD(OREG(33)) - temp_f0_2); + } else { + phi_f16 = 1.0f; + } + tmpPos0.y -= (yPosDelta * phi_f16); + } + + Camera_LERPCeilVec3f(&tmpPos0, &camera->posOffset, PCT(OREG(29)), PCT(OREG(30)), 0.1f); + camera->yOffsetUpdateRate = PCT(OREG(29)); + camera->xzOffsetUpdateRate = PCT(OREG(30)); + } + + tmpPos1.x = playerPosRot->pos.x + camera->posOffset.x; + tmpPos1.y = playerPosRot->pos.y + camera->posOffset.y; + tmpPos1.z = playerPosRot->pos.z + camera->posOffset.z; + Camera_LERPCeilVec3f(&tmpPos1, at, camera->atLERPStepScale, camera->atLERPStepScale, 0.2f); + return 1; +} + +s32 Camera_CalcAtForHorse(Camera* camera, VecSph* eyeAtDir, f32 yOffset, f32* yPosOffset, s16 calcSlope) { + Vec3f* at = &camera->at; + Vec3f posOffsetTarget; + Vec3f atTarget; + s32 pad; + s32 pad2; + f32 playerHeight; + Player* player; + PosRot horsePosRot; + + playerHeight = Player_GetHeight(camera->player); + player = camera->player; + Actor_GetWorldPosShapeRot(&horsePosRot, player->rideActor); + + if (EN_HORSE_CHECK_JUMPING((EnHorse*)player->rideActor)) { + horsePosRot.pos.y -= 49.f; + *yPosOffset = Camera_LERPCeilF(horsePosRot.pos.y, *yPosOffset, 0.1f, 0.2f); + camera->atLERPStepScale = Camera_LERPCeilF(0.4f, camera->atLERPStepScale, 0.2f, 0.02f); + } else { + *yPosOffset = Camera_LERPCeilF(horsePosRot.pos.y, *yPosOffset, 0.5f, 0.2f); + } + + posOffsetTarget.x = 0.0f; + posOffsetTarget.y = playerHeight + yOffset; + posOffsetTarget.z = 0.0f; + + if (calcSlope != 0) { + posOffsetTarget.y -= + Camera_CalcSlopeYAdj(&camera->floorNorm, camera->playerPosRot.rot.y, eyeAtDir->yaw, OREG(9)); + } + + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, camera->yOffsetUpdateRate, camera->xzOffsetUpdateRate, + 0.1f); + + atTarget.x = camera->posOffset.x + horsePosRot.pos.x; + atTarget.y = camera->posOffset.y + horsePosRot.pos.y; + atTarget.z = camera->posOffset.z + horsePosRot.pos.z; + Camera_LERPCeilVec3f(&atTarget, at, camera->atLERPStepScale, camera->atLERPStepScale, 0.2f); + + return 1; +} + +f32 Camera_LERPClampDist(Camera* camera, f32 dist, f32 min, f32 max) { + f32 distTarget; + f32 rUpdateRateInvTarget; + + if (dist < min) { + distTarget = min; + rUpdateRateInvTarget = OREG(6); + } else if (dist > max) { + distTarget = max; + rUpdateRateInvTarget = OREG(6); + } else { + distTarget = dist; + rUpdateRateInvTarget = 1.0f; + } + + camera->rUpdateRateInv = Camera_LERPCeilF(rUpdateRateInvTarget, camera->rUpdateRateInv, PCT(OREG(25)), 0.1f); + return Camera_LERPCeilF(distTarget, camera->dist, 1.0f / camera->rUpdateRateInv, 0.2f); +} + +f32 Camera_ClampDist(Camera* camera, f32 dist, f32 minDist, f32 maxDist, s16 timer) { + f32 distTarget; + f32 rUpdateRateInvTarget; + + if (dist < minDist) { + distTarget = minDist; + + rUpdateRateInvTarget = timer != 0 ? OREG(6) * 0.5f : OREG(6); + } else if (maxDist < dist) { + distTarget = maxDist; + + rUpdateRateInvTarget = timer != 0 ? OREG(6) * 0.5f : OREG(6); + } else { + distTarget = dist; + + rUpdateRateInvTarget = timer != 0 ? OREG(6) : 1.0f; + } + + camera->rUpdateRateInv = Camera_LERPCeilF(rUpdateRateInvTarget, camera->rUpdateRateInv, PCT(OREG(25)), 0.1f); + return Camera_LERPCeilF(distTarget, camera->dist, 1.0f / camera->rUpdateRateInv, 0.2f); +} + +s16 Camera_CalcDefaultPitch(Camera* camera, s16 arg1, s16 arg2, s16 arg3) { + f32 pad; + f32 phi_a2; + f32 t; + s16 phi_v0; + s16 phi_v1; + s16 sp1C; + + phi_v1 = ABS(arg1); + phi_v0 = arg3 > 0 ? (s16)(Math_CosS(arg3) * arg3) : arg3; + sp1C = arg2 - phi_v0; + + if (ABS(sp1C) < phi_v1) { + phi_a2 = (1.0f / camera->pitchUpdateRateInv) * 3.0f; + } else { + t = phi_v1 * (1.0f / R_CAM_MAX_PHI); + pad = Camera_InterpolateCurve(0.8f, 1.0f - t); + phi_a2 = (1.0f / camera->pitchUpdateRateInv) * pad; + } + return Camera_LERPCeilS(sp1C, arg1, phi_a2, 0xA); +} + +s16 Camera_CalcDefaultYaw(Camera* camera, s16 cur, s16 target, f32 arg3, f32 accel) { + f32 velocity; + s16 angDelta; + f32 updSpeed; + f32 speedT; + f32 velFactor; + f32 yawUpdRate; + + if (camera->xzSpeed > 0.001f) { + angDelta = target - BINANG_ROT180(cur); + speedT = COLPOLY_GET_NORMAL(BINANG_ROT180(angDelta)); + } else { + angDelta = target - BINANG_ROT180(cur); + speedT = PCT(OREG(48)); + } + + updSpeed = Camera_InterpolateCurve(arg3, speedT); + + velocity = updSpeed + (1.0f - updSpeed) * accel; + + if (velocity < 0.0f) { + velocity = 0.0f; + } + + velFactor = Camera_InterpolateCurve(0.5f, camera->speedRatio); + yawUpdRate = 1.0f / camera->yawUpdateRateInv; + return cur + (s16)(angDelta * velocity * velFactor * yawUpdRate); +} + +void func_80046E20(Camera* camera, VecSph* eyeAdjustment, f32 minDist, f32 arg3, f32* arg4, SwingAnimation* anim) { + static CamColChk atEyeColChk; + static CamColChk eyeAtColChk; + static CamColChk newEyeColChk; + Vec3f* eye = &camera->eye; + s32 temp_v0; + Vec3f* at = &camera->at; + Vec3f peekAroundPoint; + Vec3f* eyeNext = &camera->eyeNext; + f32 temp_f0; + VecSph newEyeAdjustment; + VecSph sp40; + + temp_v0 = func_80045508(camera, eyeAdjustment, &atEyeColChk, &eyeAtColChk, !anim->unk_18); + + switch (temp_v0) { + case 1: + case 2: + // angle between polys is between 60 and 120 degrees. + Camera_BGCheckCorner(&anim->collisionClosePoint, at, eyeNext, &atEyeColChk, &eyeAtColChk); + peekAroundPoint.x = anim->collisionClosePoint.x + (atEyeColChk.norm.x + eyeAtColChk.norm.x); + peekAroundPoint.y = anim->collisionClosePoint.y + (atEyeColChk.norm.y + eyeAtColChk.norm.y); + peekAroundPoint.z = anim->collisionClosePoint.z + (atEyeColChk.norm.z + eyeAtColChk.norm.z); + + temp_f0 = OLib_Vec3fDist(at, &atEyeColChk.pos); + *arg4 = temp_f0 > minDist ? 1.0f : temp_f0 / minDist; + + anim->swingUpdateRate = PCT(OREG(10)); + anim->unk_18 = 1; + anim->atEyePoly = eyeAtColChk.poly; + OLib_Vec3fDiffToVecSphGeo(&newEyeAdjustment, at, &peekAroundPoint); + + newEyeAdjustment.r = eyeAdjustment->r; + Camera_Vec3fVecSphGeoAdd(eye, at, &newEyeAdjustment); + newEyeColChk.pos = *eye; + if (Camera_BGCheckInfo(camera, at, &newEyeColChk) == 0) { + // no collision found between at->newEyePos + newEyeAdjustment.yaw += BINANG_SUB(eyeAdjustment->yaw, newEyeAdjustment.yaw) >> 1; + newEyeAdjustment.pitch += BINANG_SUB(eyeAdjustment->pitch, newEyeAdjustment.pitch) >> 1; + Camera_Vec3fVecSphGeoAdd(eye, at, &newEyeAdjustment); + if (atEyeColChk.sphNorm.pitch < 0x2AA8) { + // ~ 60 degrees + anim->unk_16 = newEyeAdjustment.yaw; + anim->unk_14 = newEyeAdjustment.pitch; + } else { + anim->unk_16 = eyeAdjustment->yaw; + anim->unk_14 = eyeAdjustment->pitch; + } + peekAroundPoint.x = anim->collisionClosePoint.x - (atEyeColChk.norm.x + eyeAtColChk.norm.x); + peekAroundPoint.y = anim->collisionClosePoint.y - (atEyeColChk.norm.y + eyeAtColChk.norm.y); + peekAroundPoint.z = anim->collisionClosePoint.z - (atEyeColChk.norm.z + eyeAtColChk.norm.z); + OLib_Vec3fDiffToVecSphGeo(&newEyeAdjustment, at, &peekAroundPoint); + newEyeAdjustment.r = eyeAdjustment->r; + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &newEyeAdjustment); + break; + } + + camera->eye = newEyeColChk.pos; + atEyeColChk = newEyeColChk; + + case 3: + case 6: + if (anim->unk_18 != 0) { + anim->swingUpdateRateTimer = OREG(52); + anim->unk_18 = 0; + *eyeNext = *eye; + } + + temp_f0 = OLib_Vec3fDist(at, &atEyeColChk.pos); + *arg4 = temp_f0 > minDist ? 1.0f : temp_f0 / minDist; + + anim->swingUpdateRate = *arg4 * arg3; + + Camera_Vec3fTranslateByUnitVector(eye, &atEyeColChk.pos, &atEyeColChk.norm, 1.0f); + anim->atEyePoly = NULL; + if (temp_f0 < OREG(21)) { + sp40.yaw = eyeAdjustment->yaw; + sp40.pitch = Math_SinS(atEyeColChk.sphNorm.pitch + 0x3FFF) * 16380.0f; + sp40.r = (OREG(21) - temp_f0) * PCT(OREG(22)); + Camera_Vec3fVecSphGeoAdd(eye, eye, &sp40); + } + break; + default: + if (anim->unk_18 != 0) { + anim->swingUpdateRateTimer = OREG(52); + *eyeNext = *eye; + anim->unk_18 = 0; + } + anim->swingUpdateRate = arg3; + anim->atEyePoly = NULL; + eye->x = atEyeColChk.pos.x + atEyeColChk.norm.x; + eye->y = atEyeColChk.pos.y + atEyeColChk.norm.y; + eye->z = atEyeColChk.pos.z + atEyeColChk.norm.z; + break; + } +} + +s32 Camera_Noop(Camera* camera) { + return true; +} + +s32 Camera_Normal1(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + f32 spA0; + f32 sp9C; + f32 sp98; + f32 sp94; + Vec3f sp88; + s16 wiggleAdj; + s16 t; + VecSph eyeAdjustment; + VecSph atEyeGeo; + VecSph atEyeNextGeo; + PosRot* playerPosRot = &camera->playerPosRot; + Normal1* norm1 = (Normal1*)camera->paramData; + Normal1Anim* anim = &norm1->anim; + f32 playerHeight; + f32 rate = 0.1f; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM) - PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + sp94 = yNormal * PCT(playerHeight); + + norm1->yOffset = NEXTSETTING * sp94; + norm1->distMin = NEXTSETTING * sp94; + norm1->distMax = NEXTSETTING * sp94; + norm1->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + norm1->unk_0C = NEXTSETTING; + norm1->unk_10 = NEXTSETTING; + norm1->unk_14 = NEXTPCT; + norm1->fovTarget = NEXTSETTING; + norm1->atLERPScaleMax = NEXTPCT; + norm1->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = norm1->interfaceFlags; + + OLib_Vec3fDiffToVecSphGeo(&atEyeGeo, at, eye); + OLib_Vec3fDiffToVecSphGeo(&atEyeNextGeo, at, eyeNext); + + switch (camera->animState) { + case 0x14: + camera->yawUpdateRateInv = OREG(27); + camera->pitchUpdateRateInv = OREG(27); + case 0: + case 0xA: + case 0x19: + anim->swing.atEyePoly = NULL; + anim->slopePitchAdj = 0; + anim->unk_28 = 0xA; + anim->swing.unk_16 = anim->swing.unk_14 = anim->swing.unk_18 = 0; + anim->swing.swingUpdateRate = norm1->unk_0C; + anim->yOffset = camera->playerPosRot.pos.y; + anim->unk_20 = camera->xzSpeed; + anim->swing.swingUpdateRateTimer = 0; + anim->swingYawTarget = atEyeGeo.yaw; + sUpdateCameraDirection = 0; + anim->startSwingTimer = OREG(50) + OREG(51); + break; + default: + break; + } + + camera->animState = 1; + sUpdateCameraDirection = 1; + + if (anim->unk_28 != 0) { + anim->unk_28--; + } + + if (camera->xzSpeed > 0.001f) { + anim->startSwingTimer = OREG(50) + OREG(51); + } else if (anim->startSwingTimer > 0) { + if (anim->startSwingTimer > OREG(50)) { + anim->swingYawTarget = atEyeGeo.yaw + (BINANG_SUB(BINANG_ROT180(camera->playerPosRot.rot.y), atEyeGeo.yaw) / + anim->startSwingTimer); + } + anim->startSwingTimer--; + } + + spA0 = camera->speedRatio * PCT(OREG(25)); + sp9C = camera->speedRatio * PCT(OREG(26)); + sp98 = anim->swing.unk_18 != 0 ? PCT(OREG(25)) : spA0; + + sp94 = (camera->xzSpeed - anim->unk_20) * (0.333333f); + if (sp94 > 1.0f) { + sp94 = 1.0f; + } + if (sp94 > -1.0f) { + sp94 = -1.0f; + } + + anim->unk_20 = camera->xzSpeed; + + if (anim->swing.swingUpdateRateTimer != 0) { + camera->yawUpdateRateInv = + Camera_LERPCeilF(anim->swing.swingUpdateRate + (f32)(anim->swing.swingUpdateRateTimer * 2), + camera->yawUpdateRateInv, sp98, rate); + camera->pitchUpdateRateInv = + Camera_LERPCeilF((f32)R_CAM_DEFA_PHI_UPDRATE + (f32)(anim->swing.swingUpdateRateTimer * 2), + camera->pitchUpdateRateInv, sp9C, rate); + anim->swing.swingUpdateRateTimer--; + } else { + camera->yawUpdateRateInv = + Camera_LERPCeilF(anim->swing.swingUpdateRate - ((OREG(49) * 0.01f) * anim->swing.swingUpdateRate * sp94), + camera->yawUpdateRateInv, sp98, rate); + camera->pitchUpdateRateInv = Camera_LERPCeilF(R_CAM_DEFA_PHI_UPDRATE, camera->pitchUpdateRateInv, sp9C, rate); + } + + camera->pitchUpdateRateInv = Camera_LERPCeilF(R_CAM_DEFA_PHI_UPDRATE, camera->pitchUpdateRateInv, sp9C, rate); + camera->xzOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(2)), camera->xzOffsetUpdateRate, spA0, rate); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, sp9C, rate); + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->yOffsetUpdateRate, camera->speedRatio * 0.05f, rate); + + if (norm1->interfaceFlags & 1) { + t = func_80044ADC(camera, BINANG_ROT180(atEyeGeo.yaw), 0); + sp9C = ((1.0f / norm1->unk_10) * 0.5f) * (1.0f - camera->speedRatio); + anim->slopePitchAdj = Camera_LERPCeilS(t, anim->slopePitchAdj, ((1.0f / norm1->unk_10) * 0.5f) + sp9C, 0xF); + } else { + anim->slopePitchAdj = 0; + if (camera->playerGroundY == camera->playerPosRot.pos.y) { + anim->yOffset = camera->playerPosRot.pos.y; + } + } + + spA0 = ((anim->swing.unk_18 != 0) && (norm1->yOffset > -40.0f)) + ? (sp9C = Math_SinS(anim->swing.unk_14), ((-40.0f * sp9C) + (norm1->yOffset * (1.0f - sp9C)))) + : norm1->yOffset; + + if (norm1->interfaceFlags & 0x80) { + func_800458D4(camera, &atEyeNextGeo, spA0, &anim->yOffset, norm1->interfaceFlags & 1); + } else if (norm1->interfaceFlags & 0x20) { + func_80045B08(camera, &atEyeNextGeo, spA0, anim->slopePitchAdj); + } else { + Camera_CalcAtDefault(camera, &atEyeNextGeo, spA0, norm1->interfaceFlags & 1); + } + + OLib_Vec3fDiffToVecSphGeo(&eyeAdjustment, at, eyeNext); + + camera->dist = eyeAdjustment.r = + Camera_ClampDist(camera, eyeAdjustment.r, norm1->distMin, norm1->distMax, anim->unk_28); + + if (anim->startSwingTimer <= 0) { + eyeAdjustment.pitch = atEyeNextGeo.pitch; + eyeAdjustment.yaw = + Camera_LERPCeilS(anim->swingYawTarget, atEyeNextGeo.yaw, 1.0f / camera->yawUpdateRateInv, 0xA); + } else if (anim->swing.unk_18 != 0) { + eyeAdjustment.yaw = + Camera_LERPCeilS(anim->swing.unk_16, atEyeNextGeo.yaw, 1.0f / camera->yawUpdateRateInv, 0xA); + eyeAdjustment.pitch = + Camera_LERPCeilS(anim->swing.unk_14, atEyeNextGeo.pitch, 1.0f / camera->yawUpdateRateInv, 0xA); + } else { + // rotate yaw to follow player. + eyeAdjustment.yaw = + Camera_CalcDefaultYaw(camera, atEyeNextGeo.yaw, camera->playerPosRot.rot.y, norm1->unk_14, sp94); + eyeAdjustment.pitch = + Camera_CalcDefaultPitch(camera, atEyeNextGeo.pitch, norm1->pitchTarget, anim->slopePitchAdj); + } + + // set eyeAdjustment pitch from 79.65 degrees to -85 degrees + if (eyeAdjustment.pitch > 0x38A4) { + eyeAdjustment.pitch = 0x38A4; + } + if (eyeAdjustment.pitch < -0x3C8C) { + eyeAdjustment.pitch = -0x3C8C; + } + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeAdjustment); + if ((camera->status == CAM_STAT_ACTIVE) && (!(norm1->interfaceFlags & 0x10))) { + anim->swingYawTarget = BINANG_ROT180(camera->playerPosRot.rot.y); + if (anim->startSwingTimer > 0) { + func_80046E20(camera, &eyeAdjustment, norm1->distMin, norm1->unk_0C, &sp98, &anim->swing); + } else { + sp88 = *eyeNext; + anim->swing.swingUpdateRate = camera->yawUpdateRateInv = norm1->unk_0C * 2.0f; + if (Camera_BGCheck(camera, at, &sp88)) { + anim->swingYawTarget = atEyeNextGeo.yaw; + anim->startSwingTimer = -1; + } else { + *eye = *eyeNext; + } + anim->swing.unk_18 = 0; + } + + if (anim->swing.unk_18 != 0) { + camera->inputDir.y = + Camera_LERPCeilS(camera->inputDir.y + BINANG_SUB(BINANG_ROT180(anim->swing.unk_16), camera->inputDir.y), + camera->inputDir.y, 1.0f - (0.99f * sp98), 0xA); + } + + if (norm1->interfaceFlags & 4) { + camera->inputDir.x = -atEyeGeo.pitch; + camera->inputDir.y = BINANG_ROT180(atEyeGeo.yaw); + camera->inputDir.z = 0; + } else { + OLib_Vec3fDiffToVecSphGeo(&eyeAdjustment, eye, at); + camera->inputDir.x = eyeAdjustment.pitch; + camera->inputDir.y = eyeAdjustment.yaw; + camera->inputDir.z = 0; + } + + // crit wiggle + if (gSaveContext.health <= 16 && ((camera->globalCtx->state.frames % 256) == 0)) { + wiggleAdj = Rand_ZeroOne() * 10000.0f; + camera->inputDir.y = wiggleAdj + camera->inputDir.y; + } + } else { + anim->swing.swingUpdateRate = norm1->unk_0C; + anim->swing.unk_18 = 0; + sUpdateCameraDirection = 0; + *eye = *eyeNext; + } + + spA0 = (gSaveContext.health <= 16 ? 0.8f : 1.0f); + camera->fov = Camera_LERPCeilF(norm1->fovTarget * spA0, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, norm1->atLERPScaleMax); + return 1; +} + +s32 Camera_Normal2(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + CamColChk bgChk; + s16 phi_a0; + s16 phi_a1; + f32 spA4; + f32 spA0; + VecSph adjSph; + VecSph sp90; + VecSph sp88; + VecSph atToEyeDir; + VecSph atToEyeNextDir; + PosRot* playerPosRot = &camera->playerPosRot; + Normal2* norm2 = (Normal2*)camera->paramData; + Normal2Anim* anim = &norm2->anim; + s32 pad; + Vec3s* bgData; + f32 playerHeight; + f32 yNormal; + + playerHeight = Player_GetHeight(camera->player); + yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + norm2->unk_00 = NEXTPCT * playerHeight * yNormal; + norm2->unk_04 = NEXTPCT * playerHeight * yNormal; + norm2->unk_08 = NEXTPCT * playerHeight * yNormal; + norm2->unk_1C = DEGF_TO_BINANG(NEXTSETTING); + norm2->unk_0C = NEXTSETTING; + norm2->unk_10 = NEXTPCT; + norm2->unk_14 = NEXTSETTING; + norm2->unk_18 = NEXTPCT; + norm2->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = norm2->interfaceFlags; + + switch (camera->animState) { + case 0: + case 0xA: + case 0x14: + case 0x19: + bgData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(&anim->unk_00, &BGCAM_POS(bgData)); + anim->unk_20 = BGCAM_ROT(bgData).x; + anim->unk_22 = BGCAM_ROT(bgData).y; + anim->unk_24 = playerPosRot->pos.y; + anim->unk_1C = BGCAM_FOV(bgData) == -1 ? norm2->unk_14 + : BGCAM_FOV(bgData) >= 0x169 ? PCT(BGCAM_FOV(bgData)) + : BGCAM_FOV(bgData); + + anim->unk_28 = BGCAM_JFIFID(bgData) == -1 ? 0 : BGCAM_JFIFID(bgData); + + anim->unk_18 = 0.0f; + + if (norm2->interfaceFlags & 4) { + sp88.pitch = anim->unk_20; + sp88.yaw = anim->unk_22 + 0x3FFF; + sp88.r = 100.0f; + OLib_VecSphGeoToVec3f(&anim->unk_0C, &sp88); + } + + camera->animState = 1; + camera->yawUpdateRateInv = 50.0f; + break; + default: + if (camera->playerGroundY == playerPosRot->pos.y) { + anim->unk_24 = playerPosRot->pos.y; + } + break; + } + + OLib_Vec3fDiffToVecSphGeo(&atToEyeDir, at, eye); + OLib_Vec3fDiffToVecSphGeo(&atToEyeNextDir, at, eyeNext); + + camera->speedRatio *= 0.5f; + spA4 = PCT(OREG(25)) * camera->speedRatio; + spA0 = PCT(OREG(26)) * camera->speedRatio; + + camera->yawUpdateRateInv = + Camera_LERPCeilF(norm2->unk_0C, camera->yawUpdateRateInv * camera->speedRatio, PCT(OREG(25)), 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF(OREG(7), camera->pitchUpdateRateInv, spA0, 0.1f); + camera->xzOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(2)), camera->xzOffsetUpdateRate, spA4, 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, spA0, 0.1f); + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->yOffsetUpdateRate, camera->speedRatio * 0.05f, 0.1f); + + if (!(norm2->interfaceFlags & 0x80)) { + Camera_CalcAtDefault(camera, &atToEyeNextDir, norm2->unk_00, norm2->interfaceFlags & 1); + } else { + func_800458D4(camera, &atToEyeNextDir, norm2->unk_00, &anim->unk_24, norm2->interfaceFlags & 1); + } + + if (norm2->interfaceFlags & 4) { + anim->unk_00.x = playerPosRot->pos.x + anim->unk_0C.x; + anim->unk_00.z = playerPosRot->pos.z + anim->unk_0C.z; + } + + anim->unk_00.y = playerPosRot->pos.y; + + OLib_Vec3fDiffToVecSphGeo(&sp88, &anim->unk_00, at); + OLib_Vec3fDiffToVecSphGeo(&sp90, at, eyeNext); + + phi_a1 = (anim->unk_28 & 2 ? anim->unk_22 : norm2->unk_1C); + phi_a0 = BINANG_SUB(sp90.yaw, sp88.yaw); + if ((phi_a1 < 0x4000 && ABS(phi_a0) > phi_a1) || (phi_a1 >= 0x4000 && ABS(phi_a0) < phi_a1)) { + + phi_a0 = (phi_a0 < 0 ? -phi_a1 : phi_a1); + phi_a0 += sp88.yaw; + adjSph.yaw = + Camera_LERPCeilS(phi_a0, atToEyeDir.yaw, (1.0f / camera->yawUpdateRateInv) * camera->speedRatio, 0xA); + if (anim->unk_28 & 1) { + adjSph.pitch = Camera_CalcDefaultPitch(camera, atToEyeNextDir.pitch, anim->unk_20, 0); + } else { + adjSph.pitch = atToEyeDir.pitch; + } + } else { + adjSph = sp90; + } + + camera->dist = adjSph.r = Camera_ClampDist(camera, sp90.r, norm2->unk_04, norm2->unk_08, 0); + + if (!(anim->unk_28 & 1)) { + if (adjSph.pitch >= 0xE39) { + adjSph.pitch += (BINANG_SUB(0xE38, adjSph.pitch) >> 2); + } + + if (adjSph.pitch < 0) { + adjSph.pitch += (BINANG_SUB(-0x38E, adjSph.pitch) >> 2); + } + } + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &adjSph); + + if (camera->status == CAM_STAT_ACTIVE) { + bgChk.pos = *eyeNext; + if (!camera->globalCtx->envCtx.skyboxDisabled || norm2->interfaceFlags & 0x10) { + Camera_BGCheckInfo(camera, at, &bgChk); + *eye = bgChk.pos; + } else { + func_80043F94(camera, at, &bgChk); + *eye = bgChk.pos; + OLib_Vec3fDiffToVecSphGeo(&adjSph, eye, at); + camera->inputDir.x = adjSph.pitch; + camera->inputDir.y = adjSph.yaw; + camera->inputDir.z = 0; + } + } + + camera->fov = Camera_LERPCeilF(anim->unk_1C, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, norm2->unk_18); + return 1; +} + +// riding epona +s32 Camera_Normal3(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + f32 sp98; + f32 sp94; + f32 sp90; + f32 sp8C; + VecSph sp84; + VecSph sp7C; + VecSph sp74; + PosRot* playerPosRot = &camera->playerPosRot; + f32 temp_f0; + f32 temp_f6; + s16 phi_a0; + s16 t2; + Normal3* norm3 = (Normal3*)camera->paramData; + Normal3Anim* anim = &norm3->anim; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + norm3->yOffset = NEXTSETTING * PCT(playerHeight); + norm3->distMin = NEXTSETTING * PCT(playerHeight); + norm3->distMax = NEXTSETTING * PCT(playerHeight); + norm3->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + norm3->yawUpdateSpeed = NEXTSETTING; + norm3->unk_10 = NEXTSETTING; + norm3->fovTarget = NEXTSETTING; + norm3->maxAtLERPScale = NEXTPCT; + norm3->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&sp7C, at, eye); + OLib_Vec3fDiffToVecSphGeo(&sp74, at, eyeNext); + + sUpdateCameraDirection = true; + sCameraInterfaceFlags = norm3->interfaceFlags; + switch (camera->animState) { + case 0: + case 0xA: + case 0x14: + case 0x19: + anim->swing.atEyePoly = NULL; + anim->curPitch = 0; + anim->unk_1C = 0.0f; + anim->unk_20 = camera->playerGroundY; + anim->swing.unk_16 = anim->swing.unk_14 = anim->swing.unk_18 = 0; + anim->swing.swingUpdateRate = norm3->yawUpdateSpeed; + anim->yawUpdAmt = BINANG_SUB(BINANG_ROT180(playerPosRot->rot.y), sp7C.yaw) * (1.0f / OREG(23)); + anim->distTimer = 10; + anim->yawTimer = OREG(23); + camera->animState = 1; + anim->swing.swingUpdateRateTimer = 0; + } + + if (anim->distTimer != 0) { + anim->distTimer--; + } + + sp98 = PCT(OREG(25)) * camera->speedRatio; + sp94 = PCT(OREG(26)) * camera->speedRatio; + + if (anim->swing.swingUpdateRateTimer != 0) { + camera->yawUpdateRateInv = Camera_LERPCeilF(norm3->yawUpdateSpeed + (anim->swing.swingUpdateRateTimer * 2), + camera->yawUpdateRateInv, sp98, 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF((f32)OREG(7) + (anim->swing.swingUpdateRateTimer * 2), + camera->pitchUpdateRateInv, sp94, 0.1f); + if (1) {} + anim->swing.swingUpdateRateTimer--; + } else { + camera->yawUpdateRateInv = Camera_LERPCeilF(norm3->yawUpdateSpeed, camera->yawUpdateRateInv, sp98, 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF(OREG(7), camera->pitchUpdateRateInv, sp94, 0.1f); + } + + camera->xzOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(2)), camera->xzOffsetUpdateRate, sp98, 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, sp94, 0.1f); + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->fovUpdateRate, sp94, 0.1f); + + t2 = func_80044ADC(camera, BINANG_ROT180(sp7C.yaw), 1); + sp94 = ((1.0f / norm3->unk_10) * 0.5f); + temp_f0 = (((1.0f / norm3->unk_10) * 0.5f) * (1.0f - camera->speedRatio)); + anim->curPitch = Camera_LERPCeilS(t2, anim->curPitch, sp94 + temp_f0, 0xF); + + Camera_CalcAtForHorse(camera, &sp74, norm3->yOffset, &anim->unk_20, 1); + sp90 = (norm3->distMax + norm3->distMin) * 0.5f; + OLib_Vec3fDiffToVecSphGeo(&sp84, at, eyeNext); + camera->dist = sp84.r = Camera_ClampDist(camera, sp84.r, norm3->distMin, norm3->distMax, anim->distTimer); + if (camera->xzSpeed > 0.001f) { + sp84.r += (sp90 - sp84.r) * 0.002f; + } + phi_a0 = BINANG_SUB(norm3->pitchTarget, anim->curPitch); + sp84.pitch = Camera_LERPCeilS(phi_a0, sp74.pitch, 1.0f / camera->pitchUpdateRateInv, 0xA); + + if (OREG(5) < sp84.pitch) { + sp84.pitch = OREG(5); + } + if (sp84.pitch < OREG(34)) { + sp84.pitch = OREG(34); + } + + phi_a0 = BINANG_SUB(playerPosRot->rot.y, BINANG_ROT180(sp74.yaw)); + if (ABS(phi_a0) > 0x2AF8) { + if (phi_a0 > 0) { + phi_a0 = 0x2AF8; + } else { + phi_a0 = -0x2AF8; + } + } + + sp90 = 1.0f; + sp98 = 0.5; + sp94 = camera->speedRatio; + sp90 -= sp98; + sp98 = sp98 + (sp94 * sp90); + sp98 = (sp98 * phi_a0) / camera->yawUpdateRateInv; + + sp84.yaw = fabsf(sp98) > (150.0f * (1.0f - camera->speedRatio)) ? (s16)(sp74.yaw + sp98) : sp74.yaw; + + if (anim->yawTimer > 0) { + sp84.yaw += anim->yawUpdAmt; + anim->yawTimer--; + } + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &sp84); + + if (camera->status == CAM_STAT_ACTIVE) { + func_80046E20(camera, &sp84, norm3->distMin, norm3->yawUpdateSpeed, &sp8C, &anim->swing); + } else { + *eye = *eyeNext; + } + + camera->fov = Camera_LERPCeilF(norm3->fovTarget, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, norm3->maxAtLERPScale); + return 1; +} + +s32 Camera_Normal4(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Normal0(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Parallel1(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + f32 spB8; + f32 spB4; + s16 tangle; + VecSph spA8; + VecSph atToEyeDir; + VecSph atToEyeNextDir; + PosRot* playerPosRot = &camera->playerPosRot; + CamColChk sp6C; + s16 sp6A; + s16 phi_a0; + Parallel1* para1 = (Parallel1*)camera->paramData; + Parallel1Anim* anim = ¶1->anim; + f32 pad2; + f32 playerHeight; + s32 pad3; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(OREG(46))) - (PCT(OREG(46)) * (68.0f / playerHeight)); + + para1->yOffset = NEXTPCT * playerHeight * yNormal; + para1->distTarget = NEXTPCT * playerHeight * yNormal; + para1->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + para1->yawTarget = DEGF_TO_BINANG(NEXTSETTING); + para1->unk_08 = NEXTSETTING; + para1->unk_0C = NEXTSETTING; + para1->fovTarget = NEXTSETTING; + para1->unk_14 = NEXTPCT; + para1->interfaceFlags = NEXTSETTING; + para1->unk_18 = NEXTPCT * playerHeight * yNormal; + para1->unk_1C = NEXTPCT; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&atToEyeDir, at, eye); + OLib_Vec3fDiffToVecSphGeo(&atToEyeNextDir, at, eyeNext); + + switch (camera->animState) { + case 0: + case 0xA: + case 0x14: + case 0x19: + anim->unk_16 = 0; + anim->unk_10 = 0; + if (para1->interfaceFlags & 4) { + anim->animTimer = 20; + } else { + anim->animTimer = OREG(23); + } + anim->unk_00.x = 0.0f; + anim->yTarget = playerPosRot->pos.y - camera->playerPosDelta.y; + camera->animState++; + } + + if (anim->animTimer != 0) { + if (para1->interfaceFlags & 2) { + // Rotate para1->yawTarget degrees from behind the player. + anim->yawTarget = BINANG_ROT180(playerPosRot->rot.y) + para1->yawTarget; + } else if (para1->interfaceFlags & 4) { + // rotate to para1->yawTarget + anim->yawTarget = para1->yawTarget; + } else { + // leave the rotation alone. + anim->yawTarget = atToEyeNextDir.yaw; + } + } else { + if (para1->interfaceFlags & 0x20) { + anim->yawTarget = BINANG_ROT180(playerPosRot->rot.y) + para1->yawTarget; + } + sCameraInterfaceFlags = para1->interfaceFlags; + } + + anim->pitchTarget = para1->pitchTarget; + + if (camera->animState == 0x15) { + anim->unk_16 = 1; + camera->animState = 1; + } else if (camera->animState == 0xB) { + camera->animState = 1; + } + + spB8 = PCT(OREG(25)) * camera->speedRatio; + spB4 = PCT(OREG(26)) * camera->speedRatio; + + camera->rUpdateRateInv = Camera_LERPCeilF(OREG(6), camera->rUpdateRateInv, spB8, 0.1f); + camera->yawUpdateRateInv = Camera_LERPCeilF(para1->unk_08, camera->yawUpdateRateInv, spB8, 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF(2.0f, camera->pitchUpdateRateInv, spB4, 0.1f); + camera->xzOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(2)), camera->xzOffsetUpdateRate, spB8, 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, spB4, 0.1f); + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->fovUpdateRate, camera->speedRatio * 0.05f, 0.1f); + + if (para1->interfaceFlags & 1) { + tangle = func_80044ADC(camera, BINANG_ROT180(atToEyeDir.yaw), 1); + + spB8 = ((1.0f / para1->unk_0C) * 0.3f); + pad2 = (((1.0f / para1->unk_0C) * 0.7f) * (1.0f - camera->speedRatio)); + anim->unk_10 = Camera_LERPCeilS(tangle, anim->unk_10, spB8 + pad2, 0xF); + } else { + anim->unk_10 = 0; + } + + if (camera->playerGroundY == camera->playerPosRot.pos.y || camera->player->actor.gravity > -0.1f || + camera->player->stateFlags1 & 0x200000) { + anim->yTarget = playerPosRot->pos.y; + sp6A = 0; + } else { + sp6A = 1; + } + + if (!(para1->interfaceFlags & 0x80) && !sp6A) { + Camera_CalcAtForParallel(camera, &atToEyeNextDir, para1->yOffset, &anim->yTarget, para1->interfaceFlags & 1); + } else { + func_800458D4(camera, &atToEyeNextDir, para1->unk_18, &anim->yTarget, para1->interfaceFlags & 1); + } + + if (anim->animTimer != 0) { + camera->unk_14C |= 0x20; + tangle = (((anim->animTimer + 1) * anim->animTimer) >> 1); + spA8.yaw = atToEyeDir.yaw + ((BINANG_SUB(anim->yawTarget, atToEyeDir.yaw) / tangle) * anim->animTimer); + spA8.pitch = atToEyeDir.pitch; + spA8.r = atToEyeDir.r; + anim->animTimer--; + } else { + anim->unk_16 = 0; + camera->dist = Camera_LERPCeilF(para1->distTarget, camera->dist, 1.0f / camera->rUpdateRateInv, 2.0f); + OLib_Vec3fDiffToVecSphGeo(&spA8, at, eyeNext); + spA8.r = camera->dist; + + if (para1->interfaceFlags & 0x40) { + spA8.yaw = Camera_LERPCeilS(anim->yawTarget, atToEyeNextDir.yaw, 0.6f, 0xA); + } else { + spA8.yaw = Camera_LERPCeilS(anim->yawTarget, atToEyeNextDir.yaw, 0.8f, 0xA); + } + + if (para1->interfaceFlags & 1) { + phi_a0 = BINANG_SUB(anim->pitchTarget, anim->unk_10); + } else { + phi_a0 = anim->pitchTarget; + } + + spA8.pitch = Camera_LERPCeilS(phi_a0, atToEyeNextDir.pitch, 1.0f / camera->pitchUpdateRateInv, 4); + + if (spA8.pitch > OREG(5)) { + spA8.pitch = OREG(5); + } + + if (spA8.pitch < OREG(34)) { + spA8.pitch = OREG(34); + } + } + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &spA8); + if (camera->status == CAM_STAT_ACTIVE) { + sp6C.pos = *eyeNext; + if (!camera->globalCtx->envCtx.skyboxDisabled || para1->interfaceFlags & 0x10) { + Camera_BGCheckInfo(camera, at, &sp6C); + *eye = sp6C.pos; + } else { + func_80043F94(camera, at, &sp6C); + *eye = sp6C.pos; + OLib_Vec3fDiffToVecSphGeo(&spA8, eye, at); + camera->inputDir.x = spA8.pitch; + camera->inputDir.y = spA8.yaw; + camera->inputDir.z = 0; + } + } + camera->fov = Camera_LERPCeilF(para1->fovTarget, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, sp6A ? para1->unk_1C : para1->unk_14); + //! @bug No return +} + +s32 Camera_Parallel2(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Parallel3(Camera* camera) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + s16 val = NEXTSETTING; + + sCameraInterfaceFlags = val; + + if (val & 1) { + camera->unk_14C |= 0x400; + } + if (val & 2) { + camera->unk_14C |= 0x10; + } + //! @bug doesn't return +} + +s32 Camera_Parallel4(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Parallel0(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Generic jump, jumping off ledges + */ +s32 Camera_Jump1(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + s32 pad2; + f32 spA4; + Vec3f newEye; + VecSph eyeAtOffset; + VecSph eyeNextAtOffset; + VecSph eyeDiffSph; + VecSph eyeDiffTarget; + PosRot* playerPosRot = &camera->playerPosRot; + PosRot playerhead; + s16 tangle; + Jump1* jump1 = (Jump1*)camera->paramData; + Jump1Anim* anim = &jump1->anim; + s32 pad; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + + jump1->atYOffset = PCT(NEXTSETTING) * playerHeight * yNormal; + jump1->distMin = PCT(NEXTSETTING) * playerHeight * yNormal; + jump1->distMax = PCT(NEXTSETTING) * playerHeight * yNormal; + jump1->yawUpateRateTarget = NEXTSETTING; + jump1->maxYawUpdate = PCT(NEXTSETTING); + jump1->unk_14 = NEXTSETTING; + jump1->atLERPScaleMax = PCT(NEXTSETTING); + jump1->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + // playerhead never gets used. + Actor_GetFocus(&playerhead, &camera->player->actor); + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, at, eye); + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + + sCameraInterfaceFlags = jump1->interfaceFlags; + + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + anim->swing.unk_16 = anim->swing.unk_18 = 0; + anim->swing.atEyePoly = NULL; + anim->unk_20.pitch = 0; + anim->unk_20.yaw = 0xC8; + anim->swing.swingUpdateRateTimer = 0; + anim->swing.swingUpdateRate = jump1->yawUpateRateTarget; + anim->unk_1C = playerPosRot->pos.y - camera->playerPosDelta.y; + anim->unk_20.r = eyeAtOffset.r; + camera->posOffset.y -= camera->playerPosDelta.y; + camera->xzOffsetUpdateRate = (1.0f / 10000.0f); + camera->animState++; + } + + if (anim->swing.swingUpdateRateTimer != 0) { + camera->yawUpdateRateInv = Camera_LERPCeilF(jump1->yawUpateRateTarget + anim->swing.swingUpdateRateTimer, + camera->yawUpdateRateInv, PCT(OREG(26)), 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF((f32)R_CAM_DEFA_PHI_UPDRATE + anim->swing.swingUpdateRateTimer, + camera->pitchUpdateRateInv, PCT(OREG(26)), 0.1f); + anim->swing.swingUpdateRateTimer--; + } else { + camera->yawUpdateRateInv = + Camera_LERPCeilF(jump1->yawUpateRateTarget, camera->yawUpdateRateInv, PCT(OREG(26)), 0.1f); + camera->pitchUpdateRateInv = + Camera_LERPCeilF((f32)R_CAM_DEFA_PHI_UPDRATE, camera->pitchUpdateRateInv, PCT(OREG(26)), 0.1f); + } + + camera->xzOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(2)), camera->xzOffsetUpdateRate, PCT(OREG(25)), 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, PCT(OREG(26)), 0.1f); + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->yOffsetUpdateRate, 0.05f, 0.1f); + + func_800458D4(camera, &eyeNextAtOffset, jump1->atYOffset, &anim->unk_1C, 0); + + eyeDiffSph = eyeAtOffset; + + OLib_Vec3fDiffToVecSphGeo(&eyeDiffTarget, at, eye); + + eyeDiffSph.r = Camera_LERPCeilF(eyeDiffTarget.r, eyeAtOffset.r, PCT(OREG(29)), 1.0f); + eyeDiffSph.pitch = Camera_LERPCeilS(eyeDiffTarget.pitch, eyeAtOffset.pitch, PCT(OREG(29)), 0xA); + + if (anim->swing.unk_18) { + eyeDiffSph.yaw = + Camera_LERPCeilS(anim->swing.unk_16, eyeNextAtOffset.yaw, 1.0f / camera->yawUpdateRateInv, 0xA); + eyeDiffSph.pitch = + Camera_LERPCeilS(anim->swing.unk_14, eyeNextAtOffset.pitch, 1.0f / camera->yawUpdateRateInv, 0xA); + } else { + eyeDiffSph.yaw = + Camera_CalcDefaultYaw(camera, eyeNextAtOffset.yaw, camera->playerPosRot.rot.y, jump1->maxYawUpdate, 0.0f); + } + + // Clamp the eye->at distance to jump1->distMin < eyeDiffSph.r < jump1->distMax + if (eyeDiffSph.r < jump1->distMin) { + eyeDiffSph.r = jump1->distMin; + } else if (eyeDiffSph.r > jump1->distMax) { + eyeDiffSph.r = jump1->distMax; + } + + // Clamp the phi rotation at R_CAM_MAX_PHI AND R_CAM_MIN_PHI2 + if (eyeDiffSph.pitch > R_CAM_MAX_PHI) { + eyeDiffSph.pitch = R_CAM_MAX_PHI; + } else if (eyeDiffSph.pitch < R_CAM_MIN_PHI2) { + eyeDiffSph.pitch = R_CAM_MIN_PHI2; + } + + Camera_Vec3fVecSphGeoAdd(&newEye, at, &eyeDiffSph); + eyeNext->x = newEye.x; + eyeNext->z = newEye.z; + eyeNext->y += (newEye.y - eyeNext->y) * PCT(OREG(31)); + if ((camera->status == CAM_STAT_ACTIVE) && !(jump1->interfaceFlags & 0x10)) { + func_80046E20(camera, &eyeDiffSph, jump1->distMin, jump1->yawUpateRateTarget, &spA4, &anim->swing); + if (jump1->interfaceFlags & 4) { + camera->inputDir.x = -eyeAtOffset.pitch; + camera->inputDir.y = BINANG_ROT180(eyeAtOffset.yaw); + camera->inputDir.z = 0; + } else { + OLib_Vec3fDiffToVecSphGeo(&eyeDiffSph, eye, at); + camera->inputDir.x = eyeDiffSph.pitch; + camera->inputDir.y = eyeDiffSph.yaw; + camera->inputDir.z = 0; + } + if (anim->swing.unk_18) { + camera->inputDir.y = + Camera_LERPCeilS(camera->inputDir.y + BINANG_SUB(BINANG_ROT180(anim->swing.unk_16), camera->inputDir.y), + camera->inputDir.y, 1.0f - (0.99f * spA4), 0xA); + } + } else { + anim->swing.swingUpdateRate = jump1->yawUpateRateTarget; + anim->swing.unk_18 = 0; + sUpdateCameraDirection = 0; + *eye = *eyeNext; + } + + camera->dist = OLib_Vec3fDist(at, eye); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, jump1->atLERPScaleMax); + return true; +} + +// Climbing ladders/vines +s32 Camera_Jump2(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f bgChkPos; + Vec3f floorNorm; + VecSph adjAtToEyeDir; + VecSph bgChkPara; + VecSph atToEyeNextDir; + VecSph atToEyeDir; + f32 temp_f14; + f32 temp_f16; + f32 sp90; + f32 sp8C; + s32 bgId; + CamColChk camBgChk; + PosRot* playerPosRot = &camera->playerPosRot; + s16 yawDiff; + s16 playerYawRot180; + Jump2* jump2 = (Jump2*)camera->paramData; + Jump2Anim* anim = &jump2->anim; + CameraModeValue* values; + f32 playerHeight; + f32 yNormal; + + playerHeight = Player_GetHeight(camera->player); + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + yNormal = (1.0f + PCT(OREG(46))) - (PCT(OREG(46)) * (68.0f / playerHeight)); + jump2->atYOffset = + PCT((camera->playerPosDelta.y > 0.0f ? -10.0f : 10.0f) + NEXTSETTING) * playerHeight * yNormal; + jump2->minDist = NEXTPCT * playerHeight * yNormal; + jump2->maxDist = NEXTPCT * playerHeight * yNormal; + jump2->minMaxDistFactor = NEXTPCT; + jump2->yawUpdRateTarget = NEXTSETTING; + jump2->xzUpdRateTarget = NEXTPCT; + jump2->fovTarget = NEXTSETTING; + jump2->atLERPStepScale = NEXTPCT; + jump2->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&atToEyeDir, at, eye); + OLib_Vec3fDiffToVecSphGeo(&atToEyeNextDir, at, eyeNext); + + sCameraInterfaceFlags = jump2->interfaceFlags; + + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + bgChkPos = playerPosRot->pos; + anim->floorY = Camera_GetFloorY(camera, &bgChkPos); + anim->yawTarget = atToEyeNextDir.yaw; + anim->initYawDiff = 0; + if (anim->floorY == BGCHECK_Y_MIN) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: climb: no floor \n" VT_RST); + anim->onFloor = -1; + anim->floorY = playerPosRot->pos.y - 1000.0f; + } else if (playerPosRot->pos.y - anim->floorY < playerHeight) { + // player's model is within the height of the floor. + anim->onFloor = 1; + } else { + anim->onFloor = -1; + } + + yawDiff = BINANG_SUB(BINANG_ROT180(playerPosRot->rot.y), atToEyeNextDir.yaw); + anim->initYawDiff = ((yawDiff / OREG(23)) / 4) * 3; + if (jump2->interfaceFlags & 2) { + anim->yawAdj = 0xA; + } else { + anim->yawAdj = 0x2710; + } + + playerPosRot->pos.x -= camera->playerPosDelta.x; + playerPosRot->pos.y -= camera->playerPosDelta.y; + playerPosRot->pos.z -= camera->playerPosDelta.z; + anim->animTimer = OREG(23); + camera->animState++; + camera->atLERPStepScale = jump2->atLERPStepScale; + } + + sp90 = PCT(OREG(25)) * camera->speedRatio; + sp8C = PCT(OREG(26)) * camera->speedRatio; + camera->yawUpdateRateInv = Camera_LERPCeilF(jump2->yawUpdRateTarget, camera->yawUpdateRateInv, sp90, 0.1f); + camera->xzOffsetUpdateRate = Camera_LERPCeilF(jump2->xzUpdRateTarget, camera->xzOffsetUpdateRate, sp90, 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, sp8C, 0.1f); + + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->yOffsetUpdateRate, camera->speedRatio * 0.05f, 0.1f); + camera->rUpdateRateInv = OREG(27); + + Camera_CalcAtDefault(camera, &atToEyeNextDir, jump2->atYOffset, 0); + OLib_Vec3fDiffToVecSphGeo(&adjAtToEyeDir, at, eye); + + temp_f16 = jump2->minDist; + sp90 = jump2->maxDist + (jump2->maxDist * jump2->minMaxDistFactor); + temp_f14 = temp_f16 - (jump2->minDist * jump2->minMaxDistFactor); + + if (adjAtToEyeDir.r > sp90) { + adjAtToEyeDir.r = sp90; + } else if (adjAtToEyeDir.r < temp_f14) { + adjAtToEyeDir.r = temp_f14; + } + + yawDiff = BINANG_SUB(BINANG_ROT180(playerPosRot->rot.y), adjAtToEyeDir.yaw); + if (anim->animTimer != 0) { + anim->yawTarget = BINANG_ROT180(playerPosRot->rot.y); + anim->animTimer--; + adjAtToEyeDir.yaw = Camera_LERPCeilS(anim->yawTarget, atToEyeNextDir.yaw, 0.5f, 0xA); + } else if (anim->yawAdj < ABS(yawDiff)) { + playerYawRot180 = BINANG_ROT180(playerPosRot->rot.y); + adjAtToEyeDir.yaw = Camera_LERPFloorS( + ((yawDiff < 0) ? (s16)(playerYawRot180 + anim->yawAdj) : (s16)(playerYawRot180 - anim->yawAdj)), + atToEyeNextDir.yaw, 0.1f, 0xA); + } else { + adjAtToEyeDir.yaw = Camera_LERPCeilS(adjAtToEyeDir.yaw, atToEyeNextDir.yaw, 0.25f, 0xA); + } + + // Check the floor at the top of the climb + bgChkPos.x = playerPosRot->pos.x + (Math_SinS(playerPosRot->rot.y) * 25.0f); + bgChkPos.y = playerPosRot->pos.y + (playerHeight * 2.2f); + bgChkPos.z = playerPosRot->pos.z + (Math_CosS(playerPosRot->rot.y) * 25.0f); + + sp90 = Camera_GetFloorYNorm(camera, &floorNorm, &bgChkPos, &bgId); + if ((sp90 != BGCHECK_Y_MIN) && (playerPosRot->pos.y < sp90)) { + // top of the climb is within 2.2x of the player's height. + camera->pitchUpdateRateInv = Camera_LERPCeilF(20.0f, camera->pitchUpdateRateInv, PCT(OREG(26)), 0.1f); + camera->rUpdateRateInv = Camera_LERPCeilF(20.0f, camera->rUpdateRateInv, PCT(OREG(26)), 0.1f); + adjAtToEyeDir.pitch = Camera_LERPCeilS(0x1F4, atToEyeNextDir.pitch, 1.0f / camera->pitchUpdateRateInv, 0xA); + } else if ((playerPosRot->pos.y - anim->floorY) < playerHeight) { + // player is within his height of the ground. + camera->pitchUpdateRateInv = Camera_LERPCeilF(20.0f, camera->pitchUpdateRateInv, PCT(OREG(26)), 0.1f); + camera->rUpdateRateInv = Camera_LERPCeilF(20.0f, camera->rUpdateRateInv, PCT(OREG(26)), 0.1f); + adjAtToEyeDir.pitch = Camera_LERPCeilS(0x1F4, atToEyeNextDir.pitch, 1.0f / camera->pitchUpdateRateInv, 0xA); + } else { + camera->pitchUpdateRateInv = 100.0f; + camera->rUpdateRateInv = 100.0f; + } + + // max pitch to +/- ~ 60 degrees + if (adjAtToEyeDir.pitch > 0x2AF8) { + adjAtToEyeDir.pitch = 0x2AF8; + } + + if (adjAtToEyeDir.pitch < -0x2AF8) { + adjAtToEyeDir.pitch = -0x2AF8; + } + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &adjAtToEyeDir); + camBgChk.pos = *eyeNext; + if (Camera_BGCheckInfo(camera, at, &camBgChk)) { + // Collision detected between at->eyeNext, Check if collision between + // at->eyeNext, but parallel to at (pitch = 0). + bgChkPos = camBgChk.pos; + bgChkPara.r = adjAtToEyeDir.r; + bgChkPara.pitch = 0; + bgChkPara.yaw = adjAtToEyeDir.yaw; + Camera_Vec3fVecSphGeoAdd(&camBgChk.pos, at, &bgChkPara); + if (Camera_BGCheckInfo(camera, at, &camBgChk)) { + // Collision found between parallel at->eyeNext, set eye position to + // first collsion point. + *eye = bgChkPos; + } else { + // no collision found with the parallel at->eye, animate to be parallel + adjAtToEyeDir.pitch = Camera_LERPCeilS(0, adjAtToEyeDir.pitch, 0.2f, 0xA); + Camera_Vec3fVecSphGeoAdd(eye, at, &adjAtToEyeDir); + // useless? + Camera_BGCheck(camera, at, eye); + } + } else { + // no collision detected. + *eye = *eyeNext; + } + + camera->dist = adjAtToEyeDir.r; + camera->fov = Camera_LERPCeilF(jump2->fovTarget, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + return true; +} + +// swimming +s32 Camera_Jump3(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + s32 prevMode; + f32 spC4; + f32 spC0; + f32 spBC; + Vec3f spB0; // unused + VecSph eyeDiffSph; + PosRot* playerPosRot = &camera->playerPosRot; + Jump3* jump3 = (Jump3*)camera->paramData; + VecSph eyeAtOffset; + VecSph eyeNextAtOffset; + s32 pad; + s32 pad2; + CameraModeValue* values; + f32 t2; + f32 phi_f0; + f32 phi_f2; + f32 playerHeight; + PosRot playerhead; + f32 yNormal; + f32 temp_f18; + s32 modeSwitch; + f32 temp_f2_2; + Jump3Anim* anim = &jump3->anim; + + playerHeight = Player_GetHeight(camera->player); + Actor_GetFocus(&playerhead, &camera->player->actor); + + modeSwitch = false; + if (((camera->waterYPos - eye->y) < OREG(44) || (camera->animState == 0))) { + if (anim->mode != CAM_MODE_NORMAL) { + anim->mode = CAM_MODE_NORMAL; + modeSwitch = true; + } + } else if (((camera->waterYPos - eye->y) > OREG(45)) && (anim->mode != CAM_MODE_BOOMERANG)) { + anim->mode = CAM_MODE_BOOMERANG; + modeSwitch = true; + } + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, at, eye); + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14 || modeSwitch || + R_RELOAD_CAM_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[anim->mode].values; + yNormal = ((1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight))); + t2 = PCT(playerHeight) * yNormal; + jump3->yOffset = NEXTSETTING * t2; + jump3->distMin = NEXTSETTING * t2; + jump3->distMax = NEXTSETTING * t2; + jump3->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + jump3->swingUpdateRate = NEXTSETTING; + jump3->unk_10 = NEXTSETTING; + jump3->unk_14 = NEXTPCT; + jump3->fovTarget = NEXTSETTING; + jump3->unk_1C = NEXTPCT; + jump3->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + prevMode = camera->mode; + camera->mode = anim->mode; + Camera_CopyPREGToModeValues(camera); + camera->mode = prevMode; + } + + sCameraInterfaceFlags = jump3->interfaceFlags; + + switch (camera->animState) { + case 0: + case 0xA: + case 0x14: + case 0x19: + anim->swing.atEyePoly = NULL; + anim->unk_1C = camera->playerGroundY; + anim->swing.unk_16 = anim->swing.unk_14 = anim->swing.unk_18 = 0; + anim->animTimer = 0xA; + anim->swing.swingUpdateRate = jump3->swingUpdateRate; + camera->animState++; + anim->swing.swingUpdateRateTimer = 0; + break; + default: + if (anim->animTimer != 0) { + anim->animTimer--; + } + break; + } + + spB0 = *eye; // unused + (void)spB0; // suppresses set but unused warning + + spC4 = PCT(OREG(25)) * camera->speedRatio; + spC0 = camera->speedRatio * PCT(OREG(26)); + spBC = anim->swing.unk_18 != 0 ? PCT(OREG(25)) : spC4; + + if (anim->swing.swingUpdateRateTimer != 0) { + camera->yawUpdateRateInv = Camera_LERPCeilF( + anim->swing.swingUpdateRate + (anim->swing.swingUpdateRateTimer * 2), camera->yawUpdateRateInv, spC4, 0.1f); + camera->pitchUpdateRateInv = + Camera_LERPCeilF((anim->swing.swingUpdateRateTimer * 2) + 40.0f, camera->pitchUpdateRateInv, spC0, 0.1f); + anim->swing.swingUpdateRateTimer--; + } else { + camera->yawUpdateRateInv = Camera_LERPCeilF(anim->swing.swingUpdateRate, camera->yawUpdateRateInv, spBC, 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF(40.0f, camera->pitchUpdateRateInv, spC0, 0.1f); + } + + camera->xzOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(2)), camera->xzOffsetUpdateRate, spC4, 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, spC0, 0.1f); + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->yOffsetUpdateRate, camera->speedRatio * 0.05f, 0.1f); + + Camera_CalcAtDefault(camera, &eyeNextAtOffset, jump3->yOffset, jump3->interfaceFlags); + OLib_Vec3fDiffToVecSphGeo(&eyeDiffSph, at, eyeNext); + + camera->dist = eyeDiffSph.r = + Camera_ClampDist(camera, eyeDiffSph.r, jump3->distMin, jump3->distMax, anim->animTimer); + + if (camera->playerGroundY <= playerPosRot->pos.y) { + phi_f0 = playerPosRot->pos.y - camera->playerGroundY; + } else { + phi_f0 = -(playerPosRot->pos.y - camera->playerGroundY); + } + + if (!(phi_f0 < 10.0f)) { + if (camera->waterYPos <= playerhead.pos.y) { + phi_f2 = playerhead.pos.y - camera->waterYPos; + } else { + phi_f2 = -(playerhead.pos.y - camera->waterYPos); + } + if (!(phi_f2 < 50.0f)) { + camera->pitchUpdateRateInv = 100.0f; + } + } + if (anim->swing.unk_18 != 0) { + eyeDiffSph.yaw = + Camera_LERPCeilS(anim->swing.unk_16, eyeNextAtOffset.yaw, 1.0f / camera->yawUpdateRateInv, 0xA); + eyeDiffSph.pitch = + Camera_LERPCeilS(anim->swing.unk_14, eyeNextAtOffset.pitch, 1.0f / camera->yawUpdateRateInv, 0xA); + } else { + eyeDiffSph.yaw = Camera_CalcDefaultYaw(camera, eyeNextAtOffset.yaw, playerPosRot->rot.y, jump3->unk_14, 0.0f); + eyeDiffSph.pitch = Camera_CalcDefaultPitch(camera, eyeNextAtOffset.pitch, jump3->pitchTarget, 0); + } + + if (eyeDiffSph.pitch > OREG(5)) { + eyeDiffSph.pitch = OREG(5); + } + + if (eyeDiffSph.pitch < OREG(34)) { + eyeDiffSph.pitch = OREG(34); + } + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeDiffSph); + if ((camera->status == CAM_STAT_ACTIVE) && !(jump3->interfaceFlags & 0x10)) { + func_80046E20(camera, &eyeDiffSph, jump3->distMin, jump3->swingUpdateRate, &spBC, &anim->swing); + if (jump3->interfaceFlags & 4) { + camera->inputDir.x = -eyeAtOffset.pitch; + camera->inputDir.y = BINANG_ROT180(eyeAtOffset.yaw); + camera->inputDir.z = 0; + } else { + OLib_Vec3fDiffToVecSphGeo(&eyeDiffSph, eye, at); + camera->inputDir.x = eyeDiffSph.pitch; + camera->inputDir.y = eyeDiffSph.yaw; + camera->inputDir.z = 0; + } + + if (anim->swing.unk_18 != 0) { + camera->inputDir.y = + Camera_LERPCeilS(camera->inputDir.y + BINANG_SUB(BINANG_ROT180(anim->swing.unk_16), camera->inputDir.y), + camera->inputDir.y, 1.0f - (0.99f * spBC), 0xA); + } + } else { + anim->swing.swingUpdateRate = jump3->swingUpdateRate; + anim->swing.unk_18 = 0; + sUpdateCameraDirection = 0; + *eye = *eyeNext; + } + camera->fov = Camera_LERPCeilF(jump3->fovTarget, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, jump3->unk_1C); + return true; +} + +s32 Camera_Jump4(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Jump0(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Battle1(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f sp128; + Vec3f playerHead; + Vec3f targetPos; + f32 var3; + f32 var2; + f32 temp_f0_2; + f32 temp_f12_2; + f32 spFC; + f32 spF8; + f32 swingAngle; + f32 temp_f2_2; + f32 temp_f14; + s32 skipEyeAtCalc; + f32 distRatio; + CamColChk spBC; + VecSph spB4; + VecSph atToTargetDir; + VecSph playerToTargetDir; + VecSph atToEyeDir; + VecSph atToEyeNextDir; + PosRot* playerPosRot = &camera->playerPosRot; + s16 tmpAng1; + s16 tmpAng2; + Player* player; + s16 sp86; + s16 isOffGround; + f32 distance; + f32 sp7C; + f32 sp78; + f32 fov; + Battle1* batt1 = (Battle1*)camera->paramData; + Battle1Anim* anim = &batt1->anim; + s32 pad; + f32 playerHeight; + + skipEyeAtCalc = false; + player = camera->player; + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(OREG(46))) - (PCT(OREG(46)) * (68.0f / playerHeight)); + + batt1->yOffset = NEXTPCT * playerHeight * yNormal; + batt1->distance = NEXTSETTING; + batt1->swingYawInitial = NEXTSETTING; + batt1->swingYawFinal = NEXTSETTING; + batt1->swingPitchInitial = NEXTSETTING; + batt1->swingPitchFinal = NEXTSETTING; + batt1->swingPitchAdj = NEXTPCT; + batt1->fov = NEXTSETTING; + batt1->atLERPScaleOnGround = NEXTPCT; + batt1->flags = NEXTSETTING; + batt1->yOffsetOffGround = NEXTPCT * playerHeight * yNormal; + batt1->atLERPScaleOffGround = NEXTPCT; + anim->chargeTimer = 40; + anim->unk_10 = PCT(OREG(12)); + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + distance = batt1->distance; + sp7C = batt1->swingPitchInitial; + sp78 = batt1->swingPitchFinal; + fov = batt1->fov; + + if (camera->player->stateFlags1 & 0x1000) { + // charging sword. + anim->unk_10 = Camera_LERPCeilF(PCT(OREG(12)) * 0.5f, anim->unk_10, PCT(OREG(25)), 0.1f); + camera->xzOffsetUpdateRate = Camera_LERPCeilF(0.2f, camera->xzOffsetUpdateRate, PCT(OREG(25)), 0.1f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(0.2f, camera->yOffsetUpdateRate, PCT(OREG(25)), 0.1f); + if (anim->chargeTimer >= -19) { + anim->chargeTimer--; + } else { + distance = 250.0f; + sp7C = 50.0f; + sp78 = 40.0f; + fov = 60.0f; + } + } else if (anim->chargeTimer < 0) { + distance = 250.0f; + sp7C = 50.0f; + sp78 = 40.0f; + fov = 60.0f; + anim->chargeTimer++; + } else { + anim->chargeTimer = 40; + anim->unk_10 = Camera_LERPCeilF(PCT(OREG(12)), anim->unk_10, PCT(OREG(25)), 0.1f); + camera->xzOffsetUpdateRate = + Camera_LERPCeilF(PCT(OREG(40)), camera->xzOffsetUpdateRate, PCT(OREG(25)) * camera->speedRatio, 0.1f); + camera->yOffsetUpdateRate = + Camera_LERPCeilF(PCT(OREG(40)), camera->yOffsetUpdateRate, PCT(OREG(26)) * camera->speedRatio, 0.1f); + } + camera->fovUpdateRate = Camera_LERPCeilF(PCT(OREG(4)), camera->fovUpdateRate, camera->speedRatio * 0.05f, 0.1f); + playerHeight += batt1->yOffset; + OLib_Vec3fDiffToVecSphGeo(&atToEyeDir, at, eye); + OLib_Vec3fDiffToVecSphGeo(&atToEyeNextDir, at, eyeNext); + if (camera->target == NULL || camera->target->update == NULL) { + if (camera->target == NULL) { + osSyncPrintf( + VT_COL(YELLOW, BLACK) "camera: warning: battle: target is not valid, change parallel\n" VT_RST); + } + camera->target = NULL; + Camera_ChangeMode(camera, CAM_MODE_TARGET); + return true; + } + + sCameraInterfaceFlags = batt1->flags; + + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + anim->unk_14 = 0; + anim->roll = 0.0f; + anim->target = camera->target; + camera->animState++; + if (anim->target->id > 0) { + osSyncPrintf("camera: battle: target actor name " VT_FGCOL(BLUE) "%d" VT_RST "\n", anim->target->id); + } else { + osSyncPrintf("camera: battle: target actor name " VT_COL(RED, WHITE) "%d" VT_RST "\n", anim->target->id); + camera->target = NULL; + Camera_ChangeMode(camera, CAM_MODE_TARGET); + return true; + } + anim->animTimer = OREG(23) + OREG(24); + anim->initialEyeToAtYaw = atToEyeDir.yaw; + anim->initialEyeToAtPitch = atToEyeDir.pitch; + anim->initialEyeToAtDist = atToEyeDir.r; + anim->yPosOffset = playerPosRot->pos.y - camera->playerPosDelta.y; + } + + if (camera->status == CAM_STAT_ACTIVE) { + sUpdateCameraDirection = 1; + camera->inputDir.x = -atToEyeDir.pitch; + camera->inputDir.y = BINANG_ROT180(atToEyeDir.yaw); + camera->inputDir.z = 0; + } + + if (camera->playerGroundY == camera->playerPosRot.pos.y || camera->player->actor.gravity > -0.1f || + camera->player->stateFlags1 & 0x200000) { + isOffGround = false; + anim->yPosOffset = playerPosRot->pos.y; + } else { + isOffGround = true; + } + + if (anim->animTimer == 0) { + camera->atLERPStepScale = + Camera_ClampLERPScale(camera, isOffGround ? batt1->atLERPScaleOffGround : batt1->atLERPScaleOnGround); + } + Actor_GetFocus(&camera->targetPosRot, camera->target); + if (anim->target != camera->target) { + osSyncPrintf("camera: battle: change target %d -> " VT_FGCOL(BLUE) "%d" VT_RST "\n", anim->target->id, + camera->target->id); + camera->animState = 0; + return true; + } + + Camera_CalcAtForLockOn(camera, &atToEyeNextDir, &camera->targetPosRot.pos, + isOffGround ? batt1->yOffsetOffGround : batt1->yOffset, distance, &anim->yPosOffset, + &playerToTargetDir, (isOffGround ? 0x81 : 1) | batt1->flags); + tmpAng2 = playerToTargetDir.yaw; + playerHead = playerPosRot->pos; + playerHead.y += playerHeight; + OLib_Vec3fDiffToVecSphGeo(&playerToTargetDir, &playerHead, &camera->targetPosRot.pos); + distRatio = playerToTargetDir.r > distance ? 1 : playerToTargetDir.r / distance; + targetPos = camera->targetPosRot.pos; + OLib_Vec3fDiffToVecSphGeo(&atToTargetDir, at, &targetPos); + atToTargetDir.r = distance - ((atToTargetDir.r <= distance ? atToTargetDir.r : distance) * 0.5f); + swingAngle = batt1->swingYawInitial + ((batt1->swingYawFinal - batt1->swingYawInitial) * (1.1f - distRatio)); + spF8 = OREG(13) + swingAngle; + + spB4.r = camera->dist = Camera_LERPCeilF(distance, camera->dist, PCT(OREG(11)), 2.0f); + spB4.yaw = atToEyeNextDir.yaw; + tmpAng1 = BINANG_SUB(atToTargetDir.yaw, BINANG_ROT180(atToEyeNextDir.yaw)); + if (anim->animTimer != 0) { + if (anim->animTimer >= OREG(24)) { + sp86 = anim->animTimer - OREG(24); + OLib_Vec3fDiffToVecSphGeo(&playerToTargetDir, at, eye); + playerToTargetDir.yaw = BINANG_ROT180(tmpAng2); + + var2 = 1.0f / OREG(23); + var3 = (anim->initialEyeToAtDist - playerToTargetDir.r) * var2; + tmpAng1 = BINANG_SUB(anim->initialEyeToAtYaw, playerToTargetDir.yaw) * var2; + tmpAng2 = BINANG_SUB(anim->initialEyeToAtPitch, playerToTargetDir.pitch) * var2; + + spB4.r = Camera_LERPCeilF(playerToTargetDir.r + (var3 * sp86), atToEyeDir.r, PCT(OREG(28)), 1.0f); + spB4.yaw = Camera_LERPCeilS(playerToTargetDir.yaw + (tmpAng1 * sp86), atToEyeDir.yaw, PCT(OREG(28)), 0xA); + spB4.pitch = + Camera_LERPCeilS(playerToTargetDir.pitch + (tmpAng2 * sp86), atToEyeDir.pitch, PCT(OREG(28)), 0xA); + } else { + skipEyeAtCalc = true; + } + anim->animTimer--; + } else if (ABS(tmpAng1) > DEGF_TO_BINANG(swingAngle)) { + spFC = BINANG_TO_DEGF(tmpAng1); + temp_f2_2 = swingAngle + (spF8 - swingAngle) * (OLib_ClampMaxDist(atToTargetDir.r, spB4.r) / spB4.r); + temp_f12_2 = ((temp_f2_2 * temp_f2_2) - 2.0f) / (temp_f2_2 - 360.0f); + var2 = ((temp_f12_2 * spFC) + (2.0f - (360.0f * temp_f12_2))); + temp_f14 = SQ(spFC) / var2; + tmpAng2 = tmpAng1 >= 0 ? DEGF_TO_BINANG(temp_f14) : (-DEGF_TO_BINANG(temp_f14)); + spB4.yaw = BINANG_ROT180((s16)(BINANG_ROT180(atToEyeNextDir.yaw) + tmpAng2)); + } else { + spFC = 0.05f; + spFC = (1 - camera->speedRatio) * spFC; + tmpAng2 = tmpAng1 >= 0 ? DEGF_TO_BINANG(swingAngle) : -DEGF_TO_BINANG(swingAngle); + spB4.yaw = atToEyeNextDir.yaw - (s16)((tmpAng2 - tmpAng1) * spFC); + } + + if (!skipEyeAtCalc) { + var3 = atToTargetDir.pitch * batt1->swingPitchAdj; + var2 = F32_LERPIMP(sp7C, sp78, distRatio); + tmpAng1 = DEGF_TO_BINANG(var2) - (s16)(playerToTargetDir.pitch * (0.5f + distRatio * (1.0f - 0.5f))); + tmpAng1 += (s16)(var3); + + if (tmpAng1 < -0x2AA8) { + tmpAng1 = -0x2AA8; + } else if (tmpAng1 > 0x2AA8) { + tmpAng1 = 0x2AA8; + } + + spB4.pitch = Camera_LERPCeilS(tmpAng1, atToEyeNextDir.pitch, anim->unk_10, 0xA); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &spB4); + spBC.pos = *eyeNext; + if (camera->status == CAM_STAT_ACTIVE) { + if (!camera->globalCtx->envCtx.skyboxDisabled || batt1->flags & 1) { + Camera_BGCheckInfo(camera, at, &spBC); + } else if (batt1->flags & 2) { + func_80043F94(camera, at, &spBC); + } else { + OLib_Vec3fDistNormalize(&sp128, at, &spBC.pos); + spBC.pos.x -= sp128.x; + spBC.pos.y -= sp128.y; + spBC.pos.z -= sp128.z; + } + *eye = spBC.pos; + } else { + *eye = *eyeNext; + } + } + anim->roll += (((OREG(36) * camera->speedRatio) * (1.0f - distRatio)) - anim->roll) * PCT(OREG(37)); + camera->roll = DEGF_TO_BINANG(anim->roll); + camera->fov = Camera_LERPCeilF((player->swordState != 0 ? 0.8f + : gSaveContext.health <= 0x10 ? 0.8f + : 1.0f) * + (fov - ((fov * 0.05f) * distRatio)), + camera->fov, camera->fovUpdateRate, 1.0f); +} + +s32 Camera_Battle2(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Battle3(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Charging spin attack + * Camera zooms out slowly for 50 frames, then tilts up to a specified + * setting value. + */ +s32 Camera_Battle4(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + VecSph eyeNextOffset; + VecSph eyeAtOffset; + VecSph eyeNextAtOffset; + Battle4* batt4 = (Battle4*)camera->paramData; + Battle4Anim* anim = &batt4->anim; + s32 pad; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + + batt4->yOffset = NEXTPCT * playerHeight * yNormal; + batt4->rTarget = NEXTPCT * playerHeight * yNormal; + batt4->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + batt4->lerpUpdateRate = NEXTPCT; + batt4->fovTarget = NEXTSETTING; + batt4->atLERPTarget = NEXTPCT; + batt4->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, at, eye); + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + + sCameraInterfaceFlags = batt4->interfaceFlags; + + switch (camera->animState) { + case 0: + case 0xA: + case 0x14: + anim->animTimer = 50; + camera->animState++; + } + + camera->yawUpdateRateInv = + Camera_LERPCeilF(batt4->lerpUpdateRate, camera->yawUpdateRateInv, PCT(OREG(25)) * camera->speedRatio, 0.1f); + camera->rUpdateRateInv = 1000.0f; + camera->pitchUpdateRateInv = 1000.0f; + camera->xzOffsetUpdateRate = Camera_LERPCeilF(0.025f, camera->xzOffsetUpdateRate, PCT(OREG(25)), 0.1f); + camera->yOffsetUpdateRate = + Camera_LERPCeilF(PCT(OREG(3)), camera->yOffsetUpdateRate, PCT(OREG(26)) * camera->speedRatio, 0.1f); + camera->fovUpdateRate = 0.0001f; + Camera_CalcAtDefault(camera, &eyeNextAtOffset, batt4->yOffset, 1); + if (anim->animTimer != 0) { + eyeNextOffset.yaw = eyeAtOffset.yaw; + eyeNextOffset.pitch = eyeAtOffset.pitch; + eyeNextOffset.r = eyeAtOffset.r; + anim->animTimer--; + } else { + eyeNextOffset.yaw = eyeAtOffset.yaw; + eyeNextOffset.pitch = Camera_LERPCeilS(batt4->pitchTarget, eyeAtOffset.pitch, batt4->lerpUpdateRate, 2); + eyeNextOffset.r = Camera_LERPCeilF(batt4->rTarget, eyeAtOffset.r, batt4->lerpUpdateRate, 0.001f); + } + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeNextOffset); + *eye = *eyeNext; + camera->dist = eyeNextOffset.r; + camera->fov = Camera_LERPCeilF(batt4->fovTarget, camera->fov, batt4->lerpUpdateRate, 1.0f); + camera->roll = 0; + camera->atLERPStepScale = Camera_ClampLERPScale(camera, batt4->atLERPTarget); + return true; +} + +s32 Camera_Battle0(Camera* camera) { + return Camera_Noop(camera); +} + +// Targeting non-enemy +s32 Camera_KeepOn1(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f sp120; + Vec3f sp114; + Vec3f sp108; + f32 sp104; + f32 temp_f12_2; + f32 temp_f14; + f32 t1; + f32 spF4; + f32 spF0; + f32 spEC; + f32 spE8; + f32 t2; + s16 spE2; + s16 spE0; + VecSph spD8; + VecSph spD0; + VecSph spC8; + VecSph spC0; + VecSph spB8; + PosRot* playerPosRot = &camera->playerPosRot; + CamColChk sp8C; + s32 sp88; + f32 sp84; + s16 sp82; + s16 sp80; + KeepOn1* keep1 = (KeepOn1*)camera->paramData; + Keep1Anim* anim = &keep1->anim; + s16 t3; + f32 playerHeight; + + sp88 = 0; + playerHeight = Player_GetHeight(camera->player); + if ((camera->target == NULL) || (camera->target->update == NULL)) { + if (camera->target == NULL) { + osSyncPrintf( + VT_COL(YELLOW, BLACK) "camera: warning: keepon: target is not valid, change parallel\n" VT_RST); + } + camera->target = NULL; + Camera_ChangeMode(camera, CAM_MODE_TARGET); + return 1; + } + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(OREG(46))) - (PCT(OREG(46)) * (68.0f / playerHeight)); + + keep1->unk_00 = NEXTPCT * playerHeight * yNormal; + keep1->unk_04 = NEXTSETTING; + keep1->unk_08 = NEXTSETTING; + keep1->unk_0C = NEXTSETTING; + keep1->unk_10 = NEXTSETTING; + keep1->unk_14 = NEXTSETTING; + keep1->unk_18 = NEXTSETTING; + keep1->unk_1C = NEXTPCT; + keep1->unk_20 = NEXTSETTING; + keep1->unk_24 = NEXTPCT; + keep1->interfaceFlags = NEXTSETTING; + keep1->unk_28 = NEXTPCT * playerHeight * yNormal; + keep1->unk_2C = NEXTPCT; + } + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + playerHeight += keep1->unk_00; + OLib_Vec3fDiffToVecSphGeo(&spC0, at, eye); + OLib_Vec3fDiffToVecSphGeo(&spB8, at, eyeNext); + sCameraInterfaceFlags = keep1->interfaceFlags; + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + camera->animState++; + anim->unk_10 = 0; + anim->unk_04 = 0.0f; + anim->unk_0C = camera->target; + anim->unk_16 = OREG(23) + OREG(24); + anim->unk_12 = spC0.yaw; + anim->unk_14 = spC0.pitch; + anim->unk_00 = spC0.r; + anim->unk_08 = playerPosRot->pos.y - camera->playerPosDelta.y; + } + if (camera->status == 7) { + sUpdateCameraDirection = 1; + camera->inputDir.x = -spC0.pitch; + camera->inputDir.y = BINANG_ROT180(spC0.yaw); + camera->inputDir.z = 0; + } + + sp104 = keep1->unk_04; + sp84 = 1; + + switch (camera->paramFlags & 0x18) { + case 8: + if ((camera->player->actor.category == 2) && (camera->player->interactRangeActor == camera->target)) { + PosRot sp54; + Actor_GetFocus(&sp54, &camera->player->actor); + spC8.r = 60.0f; + spC8.yaw = camera->playerPosRot.rot.y; + spC8.pitch = 0x2EE0; + Camera_Vec3fVecSphGeoAdd(&camera->targetPosRot.pos, &sp54.pos, &spC8); + } else { + Actor_GetFocus(&camera->targetPosRot, camera->target); + } + Actor_GetFocus(&camera->targetPosRot, camera->target); + if (anim->unk_0C != camera->target) { + anim->unk_0C = camera->target; + camera->atLERPStepScale = 0.0f; + } + camera->xzOffsetUpdateRate = + Camera_LERPCeilF(1.0f, camera->xzOffsetUpdateRate, PCT(OREG(25)) * camera->speedRatio, 0.1f); + camera->yOffsetUpdateRate = + Camera_LERPCeilF(1.0f, camera->yOffsetUpdateRate, PCT(OREG(26)) * camera->speedRatio, 0.1f); + camera->fovUpdateRate = + Camera_LERPCeilF(PCT(OREG(4)), camera->fovUpdateRate, camera->speedRatio * 0.05f, 0.1f); + goto cont; + case 0x10: + anim->unk_0C = NULL; + cont: + if (camera->playerGroundY == camera->playerPosRot.pos.y || camera->player->actor.gravity > -0.1f || + camera->player->stateFlags1 & 0x200000) { + anim->unk_08 = playerPosRot->pos.y; + sp80 = 0; + } else { + sp80 = 1; + } + + Camera_CalcAtForLockOn(camera, &spB8, &camera->targetPosRot.pos, sp80 ? keep1->unk_28 : keep1->unk_00, + sp104, &anim->unk_08, &spC8, (sp80 ? 0x80 : 0) | keep1->interfaceFlags); + sp114 = playerPosRot->pos; + sp114.y += playerHeight; + OLib_Vec3fDiffToVecSphGeo(&spC8, &sp114, &camera->targetPosRot.pos); + sp84 = spC8.r > sp104 ? 1.0f : spC8.r / sp104; + break; + default: + *at = playerPosRot->pos; + at->y += playerHeight; + anim->unk_0C = NULL; + break; + } + OLib_Vec3fDiffToVecSphGeo(&spD8, at, eyeNext); + if (spD8.r < keep1->unk_04) { + sp104 = keep1->unk_04; + spE8 = OREG(6); + } else if (keep1->unk_08 < spD8.r) { + sp104 = keep1->unk_08; + spE8 = OREG(6); + } else { + sp104 = spD8.r; + spE8 = 1.0f; + } + + camera->rUpdateRateInv = Camera_LERPCeilF(spE8, camera->rUpdateRateInv, PCT(OREG(25)), 0.1f); + spD8.r = spE8 = camera->dist = Camera_LERPCeilF(sp104, camera->dist, 1.0f / camera->rUpdateRateInv, 0.2f); + sp108 = camera->targetPosRot.pos; + OLib_Vec3fDiffToVecSphGeo(&spD0, at, &sp108); + spD0.r = spE8 - ((spD0.r <= spE8 ? spD0.r : spE8) * 0.5f); + spEC = keep1->unk_0C + ((keep1->unk_10 - keep1->unk_0C) * (1.1f - sp84)); + spF0 = OREG(13) + spEC; + spD8.r = camera->dist = Camera_LERPCeilF(spE8, camera->dist, PCT(OREG(11)), 2.0f); + spD8.yaw = spB8.yaw; + spE2 = BINANG_SUB(spD0.yaw, BINANG_ROT180(spB8.yaw)); + if (anim->unk_16 != 0) { + if (anim->unk_16 >= OREG(24)) { + sp82 = anim->unk_16 - OREG(24); + spE2 = spC8.yaw; + OLib_Vec3fDiffToVecSphGeo(&spC8, at, eye); + spC8.yaw = BINANG_ROT180(spE2); + + t2 = 1.0f / OREG(23); + spE8 = (anim->unk_00 - spC8.r) * t2; + spE2 = BINANG_SUB(anim->unk_12, spC8.yaw) * t2; + spE0 = BINANG_SUB(anim->unk_14, spC8.pitch) * t2; + + spD8.r = Camera_LERPCeilF(spC8.r + (spE8 * sp82), spC0.r, PCT(OREG(28)), 1.0f); + spD8.yaw = Camera_LERPCeilS(spC8.yaw + (spE2 * sp82), spC0.yaw, PCT(OREG(28)), 0xA); + spD8.pitch = Camera_LERPCeilS(spC8.pitch + (spE0 * sp82), spC0.pitch, PCT(OREG(28)), 0xA); + } else { + sp88 = 1; + } + anim->unk_16--; + } else if (ABS(spE2) > DEGF_TO_BINANG(spEC)) { + spF4 = BINANG_TO_DEGF(spE2); + t2 = spEC + (spF0 - spEC) * (OLib_ClampMaxDist(spD0.r, spD8.r) / spD8.r); + temp_f12_2 = ((SQ(t2) - 2.0f) / (t2 - 360.0f)); + t1 = (temp_f12_2 * spF4) + (2.0f - (360.0f * temp_f12_2)); + temp_f14 = SQ(spF4) / t1; + spE0 = spE2 >= 0 ? (DEGF_TO_BINANG(temp_f14)) : (-DEGF_TO_BINANG(temp_f14)); + spD8.yaw = BINANG_ROT180((s16)(BINANG_ROT180(spB8.yaw) + spE0)); + } else { + spF4 = 0.02f; + spF4 = (1.0f - camera->speedRatio) * spF4; + spE0 = spE2 >= 0 ? DEGF_TO_BINANG(spEC) : -DEGF_TO_BINANG(spEC); + spD8.yaw = spB8.yaw - (s16)((spE0 - spE2) * spF4); + } + + if (sp88 == 0) { + spE2 = DEGF_TO_BINANG((f32)(keep1->unk_14 + ((keep1->unk_18 - keep1->unk_14) * sp84))); + spE2 -= (s16)(spC8.pitch * (0.5f + (sp84 * 0.5f))); + + spE8 = spD0.pitch * keep1->unk_1C; + spE2 += (s16)spE8; + if (spE2 < -0x3200) { + spE2 = -0x3200; + } else if (spE2 > 0x3200) { + spE2 = 0x3200; + } + + spD8.pitch = Camera_LERPCeilS(spE2, spB8.pitch, PCT(OREG(12)), 0xA); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &spD8); + sp8C.pos = *eyeNext; + if (camera->status == CAM_STAT_ACTIVE) { + if (!camera->globalCtx->envCtx.skyboxDisabled || keep1->interfaceFlags & 1) { + Camera_BGCheckInfo(camera, at, &sp8C); + } else if (keep1->interfaceFlags & 2) { + func_80043F94(camera, at, &sp8C); + } else { + OLib_Vec3fDistNormalize(&sp120, at, &sp8C.pos); + sp8C.pos.x -= sp120.x; + sp8C.pos.y -= sp120.y; + sp8C.pos.z -= sp120.z; + } + *eye = sp8C.pos; + } else { + *eye = *eyeNext; + } + OLib_Vec3fDistNormalize(&sp120, eye, at); + Camera_Vec3fTranslateByUnitVector(eye, eye, &sp120, OREG(1)); + } + camera->fov = Camera_LERPCeilF(keep1->unk_20, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, sp80 ? keep1->unk_2C : keep1->unk_24); + return 1; +} + +s32 Camera_KeepOn2(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Talking to an NPC + */ +s32 Camera_KeepOn3(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f playerHeadPos; + Vec3f lineChkPointB; + f32 temp_f0; + f32 spBC; + f32 prevTargetPlayerDist; + f32 swingAngle; + Actor* colChkActors[2]; + VecSph targetToPlayerDir; + VecSph atToEyeAdj; + VecSph atToEyeDir; + VecSph atToEyeNextDir; + s32 i; + s32 angleCnt; + s16 sp82; + s16 sp80; + PosRot playerPosRot; + PosRot* camPlayerPosRot = &camera->playerPosRot; + KeepOn3* keep3 = (KeepOn3*)camera->paramData; + Keep3Anim* anim = &keep3->anim; + s32 pad; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + if (camera->target == NULL || camera->target->update == NULL) { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: talk: target is not valid, change parallel\n" VT_RST); + } + camera->target = NULL; + Camera_ChangeMode(camera, CAM_MODE_TARGET); + return 1; + } + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + if (camera->globalCtx->view.unk_124 == 0) { + camera->unk_14C |= 0x20; + camera->globalCtx->view.unk_124 = camera->thisIdx | 0x50; + return 1; + } + camera->unk_14C &= ~0x20; + } + camera->unk_14C &= ~0x10; + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(OREG(46))) - (PCT(OREG(46)) * (68.0f / playerHeight)); + + keep3->yOffset = NEXTPCT * playerHeight * yNormal; + keep3->minDist = NEXTSETTING; + keep3->maxDist = NEXTSETTING; + keep3->swingYawInital = NEXTSETTING; + keep3->swingYawFinal = NEXTSETTING; + keep3->swingPitchInitial = NEXTSETTING; + keep3->swingPitchFinal = NEXTSETTING; + keep3->swingPitchAdj = NEXTPCT; + keep3->fovTarget = NEXTSETTING; + keep3->atLERPScaleMax = NEXTPCT; + keep3->initTimer = NEXTSETTING; + keep3->flags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + playerHeight += keep3->yOffset; + OLib_Vec3fDiffToVecSphGeo(&atToEyeDir, at, eye); + OLib_Vec3fDiffToVecSphGeo(&atToEyeNextDir, at, eyeNext); + Actor_GetFocus(&camera->targetPosRot, camera->target); + Actor_GetFocus(&playerPosRot, &camera->player->actor); + playerHeadPos = camPlayerPosRot->pos; + playerHeadPos.y += playerHeight; + OLib_Vec3fDiffToVecSphGeo(&targetToPlayerDir, &playerHeadPos, &camera->targetPosRot.pos); + sCameraInterfaceFlags = keep3->flags; + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + colChkActors[0] = camera->target; + colChkActors[1] = &camera->player->actor; + camera->animState++; + anim->target = camera->target; + temp_f0 = (keep3->maxDist < targetToPlayerDir.r ? 1.0f : targetToPlayerDir.r / keep3->maxDist); + anim->animTimer = keep3->initTimer; + spBC = ((1.0f - temp_f0) * targetToPlayerDir.r) / anim->animTimer; + swingAngle = F32_LERPIMP(keep3->swingPitchInitial, keep3->swingPitchFinal, temp_f0); + atToEyeAdj.pitch = DEGF_TO_BINANG(swingAngle) + ((s16)(-(targetToPlayerDir.pitch * keep3->swingPitchAdj))); + swingAngle = F32_LERPIMP(keep3->swingYawInital, keep3->swingYawFinal, temp_f0); + if (keep3->flags & 0x10) { + if (BINANG_SUB(targetToPlayerDir.yaw, atToEyeNextDir.yaw) < 0) { + atToEyeAdj.yaw = targetToPlayerDir.yaw + DEGF_TO_BINANG(swingAngle); + } else { + atToEyeAdj.yaw = targetToPlayerDir.yaw - DEGF_TO_BINANG(swingAngle); + } + } else if (keep3->flags & 0x20) { + if (BINANG_SUB(targetToPlayerDir.yaw, atToEyeNextDir.yaw) < 0) { + atToEyeAdj.yaw = BINANG_ROT180(targetToPlayerDir.yaw) - DEGF_TO_BINANG(swingAngle); + } else { + atToEyeAdj.yaw = BINANG_ROT180(targetToPlayerDir.yaw) + DEGF_TO_BINANG(swingAngle); + } + } else if (ABS(BINANG_SUB(targetToPlayerDir.yaw, atToEyeNextDir.yaw)) < 0x3FFF) { + if (BINANG_SUB(targetToPlayerDir.yaw, atToEyeNextDir.yaw) < 0) { + atToEyeAdj.yaw = targetToPlayerDir.yaw + DEGF_TO_BINANG(swingAngle); + } else { + atToEyeAdj.yaw = targetToPlayerDir.yaw - DEGF_TO_BINANG(swingAngle); + } + } else { + if (BINANG_SUB(targetToPlayerDir.yaw, atToEyeNextDir.yaw) < 0) { + atToEyeAdj.yaw = BINANG_ROT180(targetToPlayerDir.yaw) - DEGF_TO_BINANG(swingAngle); + } else { + atToEyeAdj.yaw = BINANG_ROT180(targetToPlayerDir.yaw) + DEGF_TO_BINANG(swingAngle); + } + } + prevTargetPlayerDist = targetToPlayerDir.r; + temp_f0 = 0.6f; + targetToPlayerDir.r = (spBC * 0.6f) + (prevTargetPlayerDist * (1.0f - temp_f0)); + sp80 = atToEyeAdj.yaw; + sp82 = atToEyeAdj.pitch; + playerHeadPos = camPlayerPosRot->pos; + playerHeadPos.y += playerHeight; + Camera_Vec3fVecSphGeoAdd(&anim->atTarget, &playerHeadPos, &targetToPlayerDir); + angleCnt = ARRAY_COUNT(D_8011D3B0); + i = 0; + targetToPlayerDir.r = prevTargetPlayerDist; + atToEyeAdj.r = ((keep3->minDist + (targetToPlayerDir.r * (1 - 0.5f))) - atToEyeNextDir.r) + atToEyeNextDir.r; + Camera_Vec3fVecSphGeoAdd(&lineChkPointB, &anim->atTarget, &atToEyeAdj); + if (!(keep3->flags & 0x80)) { + while (i < angleCnt) { + if (!CollisionCheck_LineOCCheck(camera->globalCtx, &camera->globalCtx->colChkCtx, &anim->atTarget, + &lineChkPointB, colChkActors, 2) && + !Camera_BGCheck(camera, &anim->atTarget, &lineChkPointB)) { + break; + } + atToEyeAdj.yaw = sp80 + D_8011D3B0[i]; + atToEyeAdj.pitch = sp82 + D_8011D3CC[i]; + Camera_Vec3fVecSphGeoAdd(&lineChkPointB, &anim->atTarget, &atToEyeAdj); + i++; + } + } + osSyncPrintf("camera: talk: BG&collision check %d time(s)\n", i); + camera->unk_14C &= ~0xC; + pad = ((anim->animTimer + 1) * anim->animTimer) >> 1; + anim->eyeToAtTarget.y = (f32)BINANG_SUB(atToEyeAdj.yaw, atToEyeNextDir.yaw) / pad; + anim->eyeToAtTarget.z = (f32)BINANG_SUB(atToEyeAdj.pitch, atToEyeNextDir.pitch) / pad; + anim->eyeToAtTarget.x = (atToEyeAdj.r - atToEyeNextDir.r) / pad; + return 1; + } + + if (anim->animTimer != 0) { + at->x += (anim->atTarget.x - at->x) / anim->animTimer; + at->y += (anim->atTarget.y - at->y) / anim->animTimer; + at->z += (anim->atTarget.z - at->z) / anim->animTimer; + // needed to match + //if (!prevTargetPlayerDist) {} + atToEyeAdj.r = ((anim->eyeToAtTarget.x * anim->animTimer) + atToEyeNextDir.r) + 1.0f; + atToEyeAdj.yaw = atToEyeNextDir.yaw + (s16)(anim->eyeToAtTarget.y * anim->animTimer); + atToEyeAdj.pitch = atToEyeNextDir.pitch + (s16)(anim->eyeToAtTarget.z * anim->animTimer); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &atToEyeAdj); + *eye = *eyeNext; + camera->fov = Camera_LERPCeilF(keep3->fovTarget, camera->fov, 0.5, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, keep3->atLERPScaleMax); + Camera_BGCheck(camera, at, eye); + anim->animTimer--; + } else { + camera->unk_14C |= 0x410; + } + + if (camera->unk_14C & 8) { + sCameraInterfaceFlags = 0; + func_80043B60(camera); + camera->atLERPStepScale = 0.0f; + + if (camera->xzSpeed > 0.001f || CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CLEFT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CDOWN) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CUP) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CRIGHT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_R) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_Z)) { + camera->unk_14C |= 4; + camera->unk_14C &= ~8; + } + } + return 1; +} + +s32 Camera_KeepOn4(Camera* camera) { + static Vec3f D_8015BD50; + static Vec3f D_8015BD60; + static Vec3f D_8015BD70; + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Actor* spCC[2]; + f32 t = -0.5f; + f32 temp_f0_2; + CollisionPoly* spC0; + VecSph spB8; + VecSph spB0; + VecSph spA8; + s16* temp_s0 = &camera->data2; + s16 spA2; + s16 spA0; + s16 sp9E; + s16 sp9C; + PosRot* playerPosRot = &camera->playerPosRot; + KeepOn4* keep4 = (KeepOn4*)camera->paramData; + KeepOn4_Unk20* unk20 = &keep4->unk_20; + s32 pad; + f32 playerHeight; + Player* player = GET_PLAYER(camera->globalCtx); + s16 angleCnt; + s32 i; + + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + if (camera->globalCtx->view.unk_124 == 0) { + camera->unk_14C |= 0x20; + camera->unk_14C &= ~(0x4 | 0x2); + camera->globalCtx->view.unk_124 = camera->thisIdx | 0x50; + return 1; + } + unk20->unk_14 = *temp_s0; + camera->unk_14C &= ~0x20; + } + + if (unk20->unk_14 != *temp_s0) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: item: item type changed %d -> %d\n" VT_RST, unk20->unk_14, + *temp_s0); + camera->animState = 0x14; + camera->unk_14C |= 0x20; + camera->unk_14C &= ~(0x4 | 0x2); + camera->globalCtx->view.unk_124 = camera->thisIdx | 0x50; + return 1; + } + + playerHeight = Player_GetHeight(camera->player); + camera->unk_14C &= ~0x10; + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + t) - ((68.0f / playerHeight) * t); + + keep4->unk_00 = NEXTPCT * playerHeight * yNormal; + keep4->unk_04 = NEXTPCT * playerHeight * yNormal; + keep4->unk_08 = NEXTSETTING; + keep4->unk_0C = NEXTSETTING; + keep4->unk_10 = NEXTSETTING; + keep4->unk_18 = NEXTSETTING; + keep4->unk_1C = NEXTSETTING; + keep4->unk_14 = NEXTPCT; + keep4->unk_1E = NEXTSETTING; + osSyncPrintf("camera: item: type %d\n", *temp_s0); + switch (*temp_s0) { + case 1: + keep4->unk_00 = playerHeight * -0.6f * yNormal; + keep4->unk_04 = playerHeight * 2.0f * yNormal; + keep4->unk_08 = 10.0f; + break; + case 2: + case 3: + keep4->unk_08 = -20.0f; + keep4->unk_18 = 80.0f; + break; + case 4: + keep4->unk_00 = playerHeight * -0.2f * yNormal; + keep4->unk_08 = 25.0f; + break; + case 8: + keep4->unk_00 = playerHeight * -0.2f * yNormal; + keep4->unk_04 = playerHeight * 0.8f * yNormal; + keep4->unk_08 = 50.0f; + keep4->unk_18 = 70.0f; + break; + case 9: + keep4->unk_00 = playerHeight * 0.1f * yNormal; + keep4->unk_04 = playerHeight * 0.5f * yNormal; + keep4->unk_08 = -20.0f; + keep4->unk_0C = 0.0f; + keep4->unk_1C = 0x2540; + break; + case 5: + keep4->unk_00 = playerHeight * -0.4f * yNormal; + keep4->unk_08 = -10.0f; + keep4->unk_0C = 45.0f; + keep4->unk_1C = 0x2002; + break; + case 10: + keep4->unk_00 = playerHeight * -0.5f * yNormal; + keep4->unk_04 = playerHeight * 1.5f * yNormal; + keep4->unk_08 = -15.0f; + keep4->unk_0C = 175.0f; + keep4->unk_18 = 70.0f; + keep4->unk_1C = 0x2202; + keep4->unk_1E = 0x3C; + break; + case 12: + keep4->unk_00 = playerHeight * -0.6f * yNormal; + keep4->unk_04 = playerHeight * 1.6f * yNormal; + keep4->unk_08 = -2.0f; + keep4->unk_0C = 120.0f; + keep4->unk_10 = player->stateFlags1 & 0x8000000 ? 0.0f : 20.0f; + keep4->unk_1C = 0x3212; + keep4->unk_1E = 0x1E; + keep4->unk_18 = 50.0f; + break; + case 0x5A: + keep4->unk_00 = playerHeight * -0.3f * yNormal; + keep4->unk_18 = 45.0f; + keep4->unk_1C = 0x2F02; + break; + case 0x5B: + keep4->unk_00 = playerHeight * -0.1f * yNormal; + keep4->unk_04 = playerHeight * 1.5f * yNormal; + keep4->unk_08 = -3.0f; + keep4->unk_0C = 10.0f; + keep4->unk_18 = 55.0f; + keep4->unk_1C = 0x2F08; + break; + case 0x51: + keep4->unk_00 = playerHeight * -0.3f * yNormal; + keep4->unk_04 = playerHeight * 1.5f * yNormal; + keep4->unk_08 = 2.0f; + keep4->unk_0C = 20.0f; + keep4->unk_10 = 20.0f; + keep4->unk_1C = 0x2280; + keep4->unk_1E = 0x1E; + keep4->unk_18 = 45.0f; + break; + case 11: + keep4->unk_00 = playerHeight * -0.19f * yNormal; + keep4->unk_04 = playerHeight * 0.7f * yNormal; + keep4->unk_0C = 130.0f; + keep4->unk_10 = 10.0f; + keep4->unk_1C = 0x2522; + break; + default: + break; + } + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sUpdateCameraDirection = 1; + sCameraInterfaceFlags = keep4->unk_1C; + OLib_Vec3fDiffToVecSphGeo(&spB0, at, eye); + OLib_Vec3fDiffToVecSphGeo(&spA8, at, eyeNext); + D_8015BD50 = playerPosRot->pos; + D_8015BD50.y += playerHeight; + temp_f0_2 = BgCheck_CameraRaycastFloor2(&camera->globalCtx->colCtx, &spC0, &i, &D_8015BD50); + if (temp_f0_2 > (keep4->unk_00 + D_8015BD50.y)) { + D_8015BD50.y = temp_f0_2 + 10.0f; + } else { + D_8015BD50.y += keep4->unk_00; + } + + sp9C = 0; + switch (camera->animState) { + case 0: + case 0x14: + spCC[sp9C] = &camera->player->actor; + sp9C++; + func_80043ABC(camera); + camera->unk_14C &= ~(0x4 | 0x2); + unk20->unk_10 = keep4->unk_1E; + unk20->unk_08 = playerPosRot->pos.y - camera->playerPosDelta.y; + if (keep4->unk_1C & 2) { + spA2 = DEGF_TO_BINANG(keep4->unk_08); + spA0 = BINANG_SUB(BINANG_ROT180(playerPosRot->rot.y), spA8.yaw) > 0 + ? BINANG_ROT180(playerPosRot->rot.y) + DEGF_TO_BINANG(keep4->unk_0C) + : BINANG_ROT180(playerPosRot->rot.y) - DEGF_TO_BINANG(keep4->unk_0C); + } else if (keep4->unk_1C & 4) { + spA2 = DEGF_TO_BINANG(keep4->unk_08); + spA0 = DEGF_TO_BINANG(keep4->unk_0C); + } else if ((keep4->unk_1C & 8) && camera->target != NULL) { + PosRot sp60; + + Actor_GetWorldPosShapeRot(&sp60, camera->target); + spA2 = DEGF_TO_BINANG(keep4->unk_08) - sp60.rot.x; + spA0 = BINANG_SUB(BINANG_ROT180(sp60.rot.y), spA8.yaw) > 0 + ? BINANG_ROT180(sp60.rot.y) + DEGF_TO_BINANG(keep4->unk_0C) + : BINANG_ROT180(sp60.rot.y) - DEGF_TO_BINANG(keep4->unk_0C); + spCC[1] = camera->target; + sp9C++; + } else if ((keep4->unk_1C & 0x80) && camera->target != NULL) { + PosRot sp4C; + + Actor_GetWorld(&sp4C, camera->target); + spA2 = DEGF_TO_BINANG(keep4->unk_08); + sp9E = Camera_XZAngle(&sp4C.pos, &playerPosRot->pos); + spA0 = (BINANG_SUB(sp9E, spA8.yaw) > 0) ? sp9E + DEGF_TO_BINANG(keep4->unk_0C) + : sp9E - DEGF_TO_BINANG(keep4->unk_0C); + spCC[1] = camera->target; + sp9C++; + } else if (keep4->unk_1C & 0x40) { + spA2 = DEGF_TO_BINANG(keep4->unk_08); + spA0 = spA8.yaw; + } else { + spA2 = spA8.pitch; + spA0 = spA8.yaw; + } + + spB8.pitch = spA2; + spB8.yaw = spA0; + spB8.r = keep4->unk_04; + Camera_Vec3fVecSphGeoAdd(&D_8015BD70, &D_8015BD50, &spB8); + if (!(keep4->unk_1C & 1)) { + angleCnt = ARRAY_COUNT(D_8011D3B0); + for (i = 0; i < angleCnt; i++) { + if (!CollisionCheck_LineOCCheck(camera->globalCtx, &camera->globalCtx->colChkCtx, &D_8015BD50, + &D_8015BD70, spCC, sp9C) && + !Camera_BGCheck(camera, &D_8015BD50, &D_8015BD70)) { + break; + } + spB8.yaw = D_8011D3B0[i] + spA0; + spB8.pitch = D_8011D3CC[i] + spA2; + Camera_Vec3fVecSphGeoAdd(&D_8015BD70, &D_8015BD50, &spB8); + } + osSyncPrintf("camera: item: BG&collision check %d time(s)\n", i); + } + unk20->unk_04 = BINANG_SUB(spB8.pitch, spA8.pitch) / (f32)unk20->unk_10; + unk20->unk_00 = BINANG_SUB(spB8.yaw, spA8.yaw) / (f32)unk20->unk_10; + unk20->unk_0C = spA8.yaw; + unk20->unk_0E = spA8.pitch; + camera->animState++; + unk20->unk_12 = 1; + break; + case 0xA: + unk20->unk_08 = playerPosRot->pos.y - camera->playerPosDelta.y; + default: + break; + } + camera->xzOffsetUpdateRate = 0.25f; + camera->yOffsetUpdateRate = 0.25f; + camera->atLERPStepScale = 0.75f; + Camera_LERPCeilVec3f(&D_8015BD50, at, 0.5f, 0.5f, 0.2f); + if (keep4->unk_10 != 0.0f) { + spB8.r = keep4->unk_10; + spB8.pitch = 0; + spB8.yaw = playerPosRot->rot.y; + Camera_Vec3fVecSphGeoAdd(at, at, &spB8); + } + camera->atLERPStepScale = 0.0f; + camera->dist = Camera_LERPCeilF(keep4->unk_04, camera->dist, 0.25f, 2.0f); + spB8.r = camera->dist; + if (unk20->unk_10 != 0) { + camera->unk_14C |= 0x20; + unk20->unk_0C += (s16)unk20->unk_00; + unk20->unk_0E += (s16)unk20->unk_04; + unk20->unk_10--; + } else if (keep4->unk_1C & 0x10) { + camera->unk_14C |= (0x400 | 0x10); + camera->unk_14C |= (0x4 | 0x2); + camera->unk_14C &= ~8; + if (camera->timer > 0) { + camera->timer--; + } + } else { + camera->unk_14C |= (0x400 | 0x10); + if (camera->unk_14C & 8 || keep4->unk_1C & 0x80) { + sCameraInterfaceFlags = 0; + camera->unk_14C |= (0x4 | 0x2); + camera->unk_14C &= ~8; + if (camera->prevCamDataIdx < 0) { + Camera_ChangeSettingFlags(camera, camera->prevSetting, 2); + } else { + Camera_ChangeDataIdx(camera, camera->prevCamDataIdx); + camera->prevCamDataIdx = -1; + } + } + } + spB8.yaw = Camera_LERPCeilS(unk20->unk_0C, spA8.yaw, keep4->unk_14, 4); + spB8.pitch = Camera_LERPCeilS(unk20->unk_0E, spA8.pitch, keep4->unk_14, 4); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &spB8); + *eye = *eyeNext; + Camera_BGCheck(camera, at, eye); + camera->fov = Camera_LERPCeilF(keep4->unk_18, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); +} + +/** + * Talking in a pre-rendered room + */ +s32 Camera_KeepOn0(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f* at = &camera->at; + VecSph eyeTargetPosOffset; + VecSph eyeAtOffset; + KeepOn0* keep0 = (KeepOn0*)camera->paramData; + KeepOn0Anim* anim = &keep0->anim; + s32 pad; + Vec3s* sceneCamData; + Vec3s sceneCamRot; + s16 fov; + + camera->unk_14C &= ~0x10; + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + keep0->fovScale = NEXTPCT; + keep0->yawScale = NEXTPCT; + keep0->timerInit = NEXTSETTING; + keep0->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + sceneCamData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(eyeNext, &BGCAM_POS(sceneCamData)); + *eye = *eyeNext; + + sceneCamRot = BGCAM_ROT(sceneCamData); // unused + (void)sceneCamRot; // suppresses set but unused warning + + fov = BGCAM_FOV(sceneCamData); + if (fov == -1) { + fov = 6000; + } + + if (camera->target == NULL || camera->target->update == NULL) { + if (camera->target == NULL) { + osSyncPrintf( + VT_COL(YELLOW, BLACK) "camera: warning: talk: target is not valid, change normal camera\n" VT_RST); + } + camera->target = NULL; + Camera_ChangeMode(camera, CAM_MODE_NORMAL); + return true; + } + + Actor_GetFocus(&camera->targetPosRot, camera->target); + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, eye, at); + OLib_Vec3fDiffToVecSphGeo(&eyeTargetPosOffset, eye, &camera->targetPosRot.pos); + + sCameraInterfaceFlags = keep0->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + camera->fov = PCT(fov); + camera->roll = 0; + camera->atLERPStepScale = 0.0f; + anim->animTimer = keep0->timerInit; + anim->fovTarget = camera->fov - (camera->fov * keep0->fovScale); + } + + if (anim->animTimer != 0) { + eyeAtOffset.yaw += (BINANG_SUB(eyeTargetPosOffset.yaw, eyeAtOffset.yaw) / anim->animTimer) * keep0->yawScale; + Camera_Vec3fVecSphGeoAdd(at, eye, &eyeAtOffset); + anim->animTimer--; + } else { + camera->unk_14C |= (0x400 | 0x10); + } + camera->fov = Camera_LERPCeilF(anim->fovTarget, camera->fov, 0.5f, 10.0f); + return true; +} + +s32 Camera_Fixed1(Camera* camera) { + Fixed1* fixd1 = (Fixed1*)camera->paramData; + Fixed1Anim* anim = &fixd1->anim; + s32 pad; + VecSph eyeOffset; + VecSph eyeAtOffset; + s32 pad2; + Vec3f adjustedPos; + Vec3s* scenePosData; + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + PosRot* playerPosRot = &camera->playerPosRot; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + scenePosData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(&anim->eyePosRotTarget.pos, &BGCAM_POS(scenePosData)); + anim->eyePosRotTarget.rot = BGCAM_ROT(scenePosData); + anim->fov = BGCAM_FOV(scenePosData); + fixd1->unk_00 = NEXTPCT * playerHeight; + fixd1->lerpStep = NEXTPCT; + fixd1->fov = NEXTSETTING; + fixd1->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + if (anim->fov == -1) { + anim->fov = fixd1->fov * 100.0f; + } else if (anim->fov < 361) { + anim->fov *= 100; + } + + sCameraInterfaceFlags = fixd1->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + func_80043B60(camera); + if (anim->fov != -1) { + fixd1->fov = PCT(anim->fov); + } + } + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, eye, at); + + Camera_LERPCeilVec3f(&anim->eyePosRotTarget.pos, eye, 0.1f, 0.1f, 0.2f); + adjustedPos = playerPosRot->pos; + adjustedPos.y += playerHeight; + camera->dist = OLib_Vec3fDist(&adjustedPos, eye); + + eyeOffset.r = camera->dist; + eyeOffset.pitch = Camera_LERPCeilS(-anim->eyePosRotTarget.rot.x, eyeAtOffset.pitch, fixd1->lerpStep, 5); + eyeOffset.yaw = Camera_LERPCeilS(anim->eyePosRotTarget.rot.y, eyeAtOffset.yaw, fixd1->lerpStep, 5); + + Camera_Vec3fVecSphGeoAdd(at, eye, &eyeOffset); + + camera->eyeNext = *eye; + + camera->fov = Camera_LERPCeilF(fixd1->fov, camera->fov, fixd1->lerpStep, 0.01f); + camera->roll = 0; + camera->atLERPStepScale = 0.0f; + + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + + return true; +} + +s32 Camera_Fixed2(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f atTarget; + Vec3f posOffsetTarget; + PosRot* playerPosRot = &camera->playerPosRot; + Vec3s* scenePosData; + Fixed2* fixd2 = (Fixed2*)camera->paramData; + Fixed2InitParams* initParams = &fixd2->initParams; + s32 pad; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + fixd2->yOffset = (NEXTPCT * playerHeight) * yNormal; + fixd2->eyeStepScale = NEXTPCT; + fixd2->posStepScale = NEXTPCT; + fixd2->fov = NEXTSETTING; + fixd2->interfaceFlags = NEXTSETTING; + initParams->fov = fixd2->fov * 100.0f; + scenePosData = Camera_GetCamBGData(camera); + if (scenePosData != NULL) { + Camera_Vec3sToVec3f(&initParams->eye, &BGCAM_POS(scenePosData)); + if (BGCAM_FOV(scenePosData) != -1) { + initParams->fov = BGCAM_FOV(scenePosData); + } + } else { + initParams->eye = *eye; + } + if (initParams->fov < 361) { + initParams->fov *= 100; + } + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = fixd2->interfaceFlags; + + posOffsetTarget.x = 0.0f; + posOffsetTarget.y = fixd2->yOffset + playerHeight; + posOffsetTarget.z = 0.0f; + + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, fixd2->posStepScale, fixd2->posStepScale, 0.1f); + atTarget.x = playerPosRot->pos.x + camera->posOffset.x; + atTarget.y = playerPosRot->pos.y + camera->posOffset.y; + atTarget.z = playerPosRot->pos.z + camera->posOffset.z; + if (camera->animState == 0) { + camera->animState++; + func_80043B60(camera); + if (!(fixd2->interfaceFlags & 1)) { + *eye = *eyeNext = initParams->eye; + camera->at = atTarget; + } + } + + Camera_LERPCeilVec3f(&atTarget, &camera->at, fixd2->posStepScale, fixd2->posStepScale, 10.0f); + Camera_LERPCeilVec3f(&initParams->eye, eyeNext, fixd2->eyeStepScale, fixd2->eyeStepScale, 0.1f); + + *eye = *eyeNext; + camera->dist = OLib_Vec3fDist(at, eye); + camera->roll = 0; + camera->xzSpeed = 0.0f; + camera->fov = PCT(initParams->fov); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, 1.0f); + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + return true; +} + +/** + * Camera's position is fixed, does not move, or rotate + */ +s32 Camera_Fixed3(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + VecSph atSph; + Vec3s* sceneCamData; + VecSph eyeAtOffset; + Fixed3* fixd3 = (Fixed3*)camera->paramData; + Fixed3Anim* anim = &fixd3->anim; + s32 pad; + + sceneCamData = Camera_GetCamBGData(camera); + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, eye, at); + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + fixd3->interfaceFlags = NEXTSETTING; + Camera_Vec3sToVec3f(eyeNext, &BGCAM_POS(sceneCamData)); + *eye = *eyeNext; + anim->rot = BGCAM_ROT(sceneCamData); + anim->fov = BGCAM_FOV(sceneCamData); + anim->jfifId = BGCAM_JFIFID(sceneCamData); + if (anim->fov == -1) { + anim->fov = 6000; + } + if (anim->fov <= 360) { + anim->fov *= 100; + } + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + if (camera->animState == 0) { + anim->updDirTimer = 5; + R_CAM_DATA(CAM_DATA_FOV) = anim->fov; + camera->animState++; + } + + if (BGCAM_JFIFID(sceneCamData) != anim->jfifId) { + osSyncPrintf("camera: position change %d \n", anim->jfifId); + anim->jfifId = BGCAM_JFIFID(sceneCamData); + anim->updDirTimer = 5; + } + + if (anim->updDirTimer > 0) { + anim->updDirTimer--; + sUpdateCameraDirection = true; + } else { + sUpdateCameraDirection = false; + } + + atSph.r = 150.0f; + atSph.yaw = anim->rot.y; + atSph.pitch = -anim->rot.x; + + Camera_Vec3fVecSphGeoAdd(at, eye, &atSph); + sCameraInterfaceFlags = fixd3->interfaceFlags; + anim->fov = R_CAM_DATA(CAM_DATA_FOV); + camera->roll = 0; + camera->fov = anim->fov * 0.01f; + camera->atLERPStepScale = 0.0f; + return true; +} + +/** + * camera follow player, eye is in a fixed offset of the previous eye, and a value + * specified in the scene. + */ +s32 Camera_Fixed4(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f playerPosWithCamOffset; + Vec3f atTarget; + Vec3f posOffsetTarget; + VecSph atEyeNextOffset; + VecSph atTargetEyeNextOffset; + PosRot* playerPosRot = &camera->playerPosRot; + Vec3s* camPosData; + Vec3f* posOffset = &camera->posOffset; + Fixed4* fixed4 = (Fixed4*)camera->paramData; + Fixed4Anim* anim = &fixed4->anim; + f32 playerYOffset; + + playerYOffset = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = ((1.0f + PCT(OREG(46))) - (PCT(OREG(46)) * (68.0f / playerYOffset))); + fixed4->yOffset = NEXTPCT * playerYOffset * yNormal; + fixed4->speedToEyePos = NEXTPCT; + fixed4->followSpeed = NEXTPCT; + fixed4->fov = NEXTSETTING; + fixed4->interfaceFlags = NEXTSETTING; + camPosData = Camera_GetCamBGData(camera); + if (camPosData != NULL) { + Camera_Vec3sToVec3f(&anim->eyeTarget, &BGCAM_POS(camPosData)); + } else { + anim->eyeTarget = *eye; + } + } + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + sCameraInterfaceFlags = fixed4->interfaceFlags; + if (camera->animState == 0) { + camera->animState++; + if (!(fixed4->interfaceFlags & 4)) { + func_80043B60(camera); + } + anim->followSpeed = fixed4->followSpeed; + } + + VEC3F_LERPIMPDST(eyeNext, eyeNext, &anim->eyeTarget, fixed4->speedToEyePos); + *eye = *eyeNext; + + posOffsetTarget.x = 0.0f; + posOffsetTarget.y = fixed4->yOffset + playerYOffset; + posOffsetTarget.z = 0.0f; + Camera_LERPCeilVec3f(&posOffsetTarget, &camera->posOffset, 0.1f, 0.1f, 0.1f); + + playerPosWithCamOffset.x = playerPosRot->pos.x + camera->posOffset.x; + playerPosWithCamOffset.y = playerPosRot->pos.y + camera->posOffset.y; + playerPosWithCamOffset.z = playerPosRot->pos.z + camera->posOffset.z; + VEC3F_LERPIMPDST(&atTarget, at, &playerPosWithCamOffset, 0.5f); + + OLib_Vec3fDiffToVecSphGeo(&atEyeNextOffset, eyeNext, at); + OLib_Vec3fDiffToVecSphGeo(&atTargetEyeNextOffset, eyeNext, &atTarget); + + atEyeNextOffset.r += (atTargetEyeNextOffset.r - atEyeNextOffset.r) * anim->followSpeed; + atEyeNextOffset.pitch = Camera_LERPCeilS(atTargetEyeNextOffset.pitch, atEyeNextOffset.pitch, + anim->followSpeed * camera->speedRatio, 0xA); + atEyeNextOffset.yaw = + Camera_LERPCeilS(atTargetEyeNextOffset.yaw, atEyeNextOffset.yaw, anim->followSpeed * camera->speedRatio, 0xA); + Camera_Vec3fVecSphGeoAdd(at, eyeNext, &atEyeNextOffset); + camera->dist = OLib_Vec3fDist(at, eye); + camera->roll = 0; + camera->fov = fixed4->fov; + camera->atLERPStepScale = Camera_ClampLERPScale(camera, 1.0f); + return true; +} + +s32 Camera_Fixed0(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Subj1(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Subj2(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * First person view + */ +s32 Camera_Subj3(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f sp98; + Vec3f sp8C; + VecSph sp84; + VecSph sp7C; + VecSph tsph; + PosRot sp60; + PosRot* playerPosRot = &camera->playerPosRot; + f32 sp58; + f32 temp_f0_3; + s16 sp52; + s16 sp50; + Subj3* subj3 = (Subj3*)camera->paramData; + Subj3Anim* anim = &subj3->anim; + CameraModeValue* values; + Vec3f* pad2; + f32 playerHeight; + + Actor_GetFocus(&sp60, &camera->player->actor); + playerHeight = Player_GetHeight(camera->player); + + if (camera->globalCtx->view.unk_124 == 0) { + camera->globalCtx->view.unk_124 = camera->thisIdx | 0x50; + return true; + } + + func_80043ABC(camera); + Camera_CopyPREGToModeValues(camera); + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + subj3->eyeNextYOffset = NEXTPCT * playerHeight; + subj3->eyeDist = NEXTSETTING; + subj3->eyeNextDist = NEXTSETTING; + subj3->unk_0C = NEXTSETTING; + subj3->atOffset.x = NEXTSETTING * 0.1f; + subj3->atOffset.y = NEXTSETTING * 0.1f; + subj3->atOffset.z = NEXTSETTING * 0.1f; + subj3->fovTarget = NEXTSETTING; + subj3->interfaceFlags = NEXTSETTING; + sp84.r = subj3->eyeNextDist; + sp84.yaw = BINANG_ROT180(sp60.rot.y); + sp84.pitch = sp60.rot.x; + sp98 = sp60.pos; + sp98.y += subj3->eyeNextYOffset; + + Camera_Vec3fVecSphGeoAdd(&sp8C, &sp98, &sp84); + OLib_Vec3fDiffToVecSphGeo(&sp7C, at, eye); + + sCameraInterfaceFlags = subj3->interfaceFlags; + if (camera->animState == 0 || camera->animState == 0xA || camera->animState == 0x14) { + anim->r = sp7C.r; + anim->yaw = sp7C.yaw; + anim->pitch = sp7C.pitch; + anim->animTimer = OREG(23); + camera->dist = subj3->eyeNextDist; + camera->animState++; + camera->rUpdateRateInv = 1.0f; + camera->dist = subj3->eyeNextDist; + } + + tsph.r = anim->r; + tsph.yaw = anim->yaw; + tsph.pitch = anim->pitch; + if (anim->animTimer != 0) { + temp_f0_3 = (1.0f / anim->animTimer); + pad2 = at; + at->x = at->x + (sp98.x - pad2->x) * temp_f0_3; + at->y = at->y + (sp98.y - pad2->y) * temp_f0_3; + at->z = at->z + (sp98.z - pad2->z) * temp_f0_3; + + temp_f0_3 = (1.0f / OREG(23)); + sp58 = (tsph.r - sp84.r) * temp_f0_3; + sp52 = BINANG_SUB(tsph.yaw, sp84.yaw) * temp_f0_3; + sp50 = BINANG_SUB(tsph.pitch, sp84.pitch) * temp_f0_3; + + sp7C.r = Camera_LERPCeilF(sp84.r + (sp58 * anim->animTimer), sp7C.r, PCT(OREG(28)), 1.0f); + sp7C.yaw = Camera_LERPCeilS(sp84.yaw + (sp52 * anim->animTimer), sp7C.yaw, PCT(OREG(28)), 0xA); + sp7C.pitch = Camera_LERPCeilS(sp84.pitch + (sp50 * anim->animTimer), sp7C.pitch, PCT(OREG(28)), 0xA); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &sp7C); + + *eye = *eyeNext; + anim->animTimer--; + + if (!camera->globalCtx->envCtx.skyboxDisabled) { + Camera_BGCheck(camera, at, eye); + } else { + func_80044340(camera, at, eye); + } + } else { + sp58 = Math_SinS(-sp60.rot.x); + temp_f0_3 = Math_CosS(-sp60.rot.x); + sp98.x = subj3->atOffset.x; + sp98.y = (subj3->atOffset.y * temp_f0_3) - (subj3->atOffset.z * sp58); + sp98.z = (subj3->atOffset.y * sp58) + (subj3->atOffset.z * temp_f0_3); + sp58 = Math_SinS(BINANG_ROT180(sp60.rot.y)); + temp_f0_3 = Math_CosS(BINANG_ROT180(sp60.rot.y)); + subj3->atOffset.x = (sp98.z * sp58) + (sp98.x * temp_f0_3); + subj3->atOffset.y = sp98.y; + subj3->atOffset.z = (sp98.z * temp_f0_3) - (sp98.x * sp58); + at->x = subj3->atOffset.x + sp60.pos.x; + at->y = subj3->atOffset.y + sp60.pos.y; + at->z = subj3->atOffset.z + sp60.pos.z; + sp7C.r = subj3->eyeNextDist; + sp7C.yaw = BINANG_ROT180(sp60.rot.y); + sp7C.pitch = sp60.rot.x; + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &sp7C); + sp7C.r = subj3->eyeDist; + Camera_Vec3fVecSphGeoAdd(eye, at, &sp7C); + } + + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + camera->fov = Camera_LERPCeilF(subj3->fovTarget, camera->fov, 0.25f, 1.0f); + camera->roll = 0; + camera->atLERPStepScale = 0.0f; + return 1; +} + +s32 Camera_Subj4(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f* at = &camera->at; + u16 spAA; + Vec3s* spA4; + Vec3f sp98; + Vec3f sp8C; + f32 sp88; + s16 pad2; + f32 temp_f16; + PosRot sp6C; + VecSph sp64; + VecSph sp5C; + s16 temp_a0; + f32 tx; + Player* player; + PosRot* playerPosRot = &camera->playerPosRot; + Subj4* subj4 = (Subj4*)camera->paramData; + Subj4Anim* anim = &subj4->anim; + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + subj4->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + if (camera->globalCtx->view.unk_124 == 0) { + camera->globalCtx->view.unk_124 = (camera->thisIdx | 0x50); + anim->unk_24 = camera->xzSpeed; + return true; + } + + Actor_GetWorldPosShapeRot(&sp6C, &camera->player->actor); + + OLib_Vec3fDiffToVecSphGeo(&sp5C, at, eye); + sCameraInterfaceFlags = subj4->interfaceFlags; + if (camera->animState == 0) { + spA4 = Camera_GetCamBgDataUnderPlayer(camera, &spAA); + Camera_Vec3sToVec3f(&anim->unk_00.a, &spA4[1]); + Camera_Vec3sToVec3f(&sp98, &spA4[spAA - 2]); + + sp64.r = 10.0f; + // 0x238C ~ 50 degrees + sp64.pitch = 0x238C; + sp64.yaw = Camera_XZAngle(&sp98, &anim->unk_00.a); + sp88 = OLib_Vec3fDist(&playerPosRot->pos, &anim->unk_00.a); + if (OLib_Vec3fDist(&playerPosRot->pos, &sp98) < sp88) { + anim->unk_00.b.x = anim->unk_00.a.x - sp98.x; + anim->unk_00.b.y = anim->unk_00.a.y - sp98.y; + anim->unk_00.b.z = anim->unk_00.a.z - sp98.z; + anim->unk_00.a = sp98; + } else { + anim->unk_00.b.x = sp98.x - anim->unk_00.a.x; + anim->unk_00.b.y = sp98.y - anim->unk_00.a.y; + anim->unk_00.b.z = sp98.z - anim->unk_00.a.z; + sp64.yaw = BINANG_ROT180(sp64.yaw); + } + anim->unk_30 = sp64.yaw; + anim->unk_32 = 0xA; + anim->unk_2C = 0; + anim->unk_2E = false; + anim->unk_28 = 0.0f; + camera->animState++; + } + + if (anim->unk_32 != 0) { + sp64.r = 10.0f; + sp64.pitch = 0x238C; + sp64.yaw = anim->unk_30; + Camera_Vec3fVecSphGeoAdd(&sp8C, &sp6C.pos, &sp64); + sp88 = (anim->unk_32 + 1.0f); + at->x += (sp8C.x - at->x) / sp88; + at->y += (sp8C.y - at->y) / sp88; + at->z += (sp8C.z - at->z) / sp88; + sp5C.r -= (sp5C.r / sp88); + sp5C.yaw = BINANG_LERPIMPINV(sp5C.yaw, BINANG_ROT180(sp6C.rot.y), anim->unk_32); + sp5C.pitch = BINANG_LERPIMPINV(sp5C.pitch, sp6C.rot.x, anim->unk_32); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &sp5C); + *eye = *eyeNext; + anim->unk_32--; + return false; + } else if (anim->unk_24 < 0.5f) { + return false; + } + + Actor_GetWorldPosShapeRot(&sp6C, &camera->player->actor); + Math3D_LineClosestToPoint(&anim->unk_00, &sp6C.pos, eyeNext); + at->x = eyeNext->x + anim->unk_00.b.x; + at->y = eyeNext->y + anim->unk_00.b.y; + at->z = eyeNext->z + anim->unk_00.b.z; + *eye = *eyeNext; + sp64.yaw = anim->unk_30; + sp64.r = 5.0f; + sp64.pitch = 0x238C; + Camera_Vec3fVecSphGeoAdd(&sp98, eyeNext, &sp64); + anim->unk_2C += 0xBB8; + temp_f16 = Math_CosS(anim->unk_2C); + eye->x += (sp98.x - eye->x) * fabsf(temp_f16); + eye->y += (sp98.y - eye->y) * fabsf(temp_f16); + eye->z += (sp98.z - eye->z) * fabsf(temp_f16); + + if ((anim->unk_28 < temp_f16) && !anim->unk_2E) { + player = camera->player; + anim->unk_2E = true; + func_800F4010(&player->actor.projectedPos, player->unk_89E + 0x8B0, 4.0f); + } else if (anim->unk_28 > temp_f16) { + anim->unk_2E = false; + } + + anim->unk_28 = temp_f16; + //camera->player->actor.world.pos = *eyeNext; + //camera->player->actor.world.pos.y = camera->playerGroundY; + //camera->player->actor.shape.rot.y = sp64.yaw; + temp_f16 = ((240.0f * temp_f16) * (anim->unk_24 * 0.416667f)); + temp_a0 = temp_f16 + anim->unk_30; + at->x = eye->x + (Math_SinS(temp_a0) * 10.0f); + at->y = eye->y; + at->z = eye->z + (Math_CosS(temp_a0) * 10.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + return 1; +} + +s32 Camera_Subj0(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Data0(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Data1(Camera* camera) { + osSyncPrintf("chau!chau!\n"); + return Camera_Normal1(camera); +} + +s32 Camera_Data2(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Data3(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Data4(Camera* camera) { + s32 pad2[2]; + Data4* data4 = (Data4*)camera->paramData; + VecSph eyeAtOffset; + VecSph atOffset; + VecSph eyeNextAtOffset; + f32 yNormal; + s16 fov; + Vec3f* eyeNext = &camera->eyeNext; + Vec3s* sceneCamData; + Vec3f lookAt; + CameraModeValue* values; + Data4InitParams* initParams = &data4->initParams; + Vec3f* eye = &camera->eye; + f32 playerHeight; + Vec3f* at = &camera->at; + s32 pad; + + playerHeight = Player_GetHeight(camera->player); + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + data4->yOffset = NEXTPCT * playerHeight * yNormal; + data4->fov = NEXTSETTING; + data4->interfaceFlags = NEXTSETTING; + sceneCamData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(&initParams->eyePosRot.pos, &BGCAM_POS(sceneCamData)); + initParams->eyePosRot.rot = BGCAM_ROT(sceneCamData); + fov = BGCAM_FOV(sceneCamData); + initParams->fov = fov; + if (fov != -1) { + data4->fov = initParams->fov < 361 ? initParams->fov : PCT(initParams->fov); + } + + initParams->jfifId = BGCAM_JFIFID(sceneCamData); + *eye = initParams->eyePosRot.pos; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = data4->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + func_80043B60(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + Camera_CalcAtDefault(camera, &eyeNextAtOffset, data4->yOffset, false); + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, eye, at); + + atOffset.r = eyeAtOffset.r; + atOffset.yaw = + (initParams->jfifId & 1) ? (DEGF_TO_BINANG(camera->data2) + initParams->eyePosRot.rot.y) : eyeAtOffset.yaw; + atOffset.pitch = + (initParams->jfifId & 2) ? (DEGF_TO_BINANG(camera->data3) + initParams->eyePosRot.rot.x) : eyeAtOffset.pitch; + + Camera_Vec3fVecSphGeoAdd(at, eye, &atOffset); + + lookAt = camera->playerPosRot.pos; + lookAt.y += playerHeight; + + camera->dist = OLib_Vec3fDist(&lookAt, eye); + camera->roll = 0; + camera->xzSpeed = 0.0f; + camera->fov = data4->fov; + camera->atLERPStepScale = 0; + return true; +} + +/** + * Hanging off of a ledge + */ +s32 Camera_Unique1(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f playerBodyPart0; + s16 phiTarget; + VecSph sp8C; + VecSph unk908PlayerPosOffset; + VecSph eyeAtOffset; + VecSph eyeNextAtOffset; + PosRot* playerPosRot = &camera->playerPosRot; + PosRot playerhead; + Unique1* uniq1 = (Unique1*)camera->paramData; + Unique1Anim* anim = &uniq1->anim; + s32 pad; + f32 playerHeight; + s32 pad2; + + playerHeight = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + uniq1->yOffset = NEXTPCT * playerHeight * yNormal; + uniq1->distMin = NEXTPCT * playerHeight * yNormal; + uniq1->distMax = NEXTPCT * playerHeight * yNormal; + uniq1->pitchTarget = DEGF_TO_BINANG(NEXTSETTING); + uniq1->fovTarget = NEXTSETTING; + uniq1->atLERPScaleMax = NEXTPCT; + uniq1->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS != 0) { + Camera_CopyPREGToModeValues(camera); + } + + sUpdateCameraDirection = 1; + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, at, eye); + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + + sCameraInterfaceFlags = uniq1->interfaceFlags; + + if (camera->animState == 0) { + camera->posOffset.y = camera->posOffset.y - camera->playerPosDelta.y; + anim->yawTarget = eyeNextAtOffset.yaw; + anim->unk_00 = 0.0f; + playerBodyPart0 = camera->player->bodyPartsPos[0]; + OLib_Vec3fDiffToVecSphGeo(&unk908PlayerPosOffset, &playerPosRot->pos, &playerBodyPart0); + anim->timer = R_DEFA_CAM_ANIM_TIME; + anim->yawTargetAdj = ABS(BINANG_SUB(unk908PlayerPosOffset.yaw, eyeAtOffset.yaw)) < 0x3A98 + ? 0 + : ((BINANG_SUB(unk908PlayerPosOffset.yaw, eyeAtOffset.yaw) / anim->timer) / 4) * 3; + camera->animState++; + } + + Actor_GetFocus(&playerhead, &camera->player->actor); // unused + + camera->yawUpdateRateInv = Camera_LERPCeilF(100.0f, camera->yawUpdateRateInv, OREG(25) * 0.01f, 0.1f); + camera->pitchUpdateRateInv = Camera_LERPCeilF(100.0f, camera->pitchUpdateRateInv, OREG(25) * 0.01f, 0.1f); + camera->xzOffsetUpdateRate = Camera_LERPCeilF(0.005f, camera->xzOffsetUpdateRate, OREG(25) * 0.01f, 0.01f); + camera->yOffsetUpdateRate = Camera_LERPCeilF(0.01f, camera->yOffsetUpdateRate, OREG(26) * 0.01f, 0.01f); + camera->fovUpdateRate = Camera_LERPCeilF(OREG(4) * 0.01f, camera->fovUpdateRate, 0.05f, 0.1f); + + Camera_CalcAtDefault(camera, &eyeNextAtOffset, uniq1->yOffset, 1); + OLib_Vec3fDiffToVecSphGeo(&sp8C, at, eyeNext); + + camera->dist = Camera_LERPClampDist(camera, sp8C.r, uniq1->distMin, uniq1->distMax); + + phiTarget = uniq1->pitchTarget; + sp8C.pitch = Camera_LERPCeilS(phiTarget, eyeNextAtOffset.pitch, 1.0f / camera->pitchUpdateRateInv, 0xA); + + if (sp8C.pitch > OREG(5)) { + sp8C.pitch = OREG(5); + } + if (sp8C.pitch < -OREG(5)) { + sp8C.pitch = -OREG(5); + } + + if (anim->timer != 0) { + anim->yawTarget += anim->yawTargetAdj; + anim->timer--; + } + + sp8C.yaw = Camera_LERPFloorS(anim->yawTarget, eyeNextAtOffset.yaw, 0.5f, 0x2710); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &sp8C); + *eye = *eyeNext; + Camera_BGCheck(camera, at, eye); + camera->fov = Camera_LERPCeilF(uniq1->fovTarget, camera->fov, camera->fovUpdateRate, 1.0f); + camera->roll = 0; + camera->atLERPStepScale = Camera_ClampLERPScale(camera, uniq1->atLERPScaleMax); + return true; +} + +s32 Camera_Unique2(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f playerPos; + VecSph eyeOffset; + VecSph eyeAtOffset; + s32 pad; + f32 lerpRateFactor; + Unique2* uniq2 = (Unique2*)camera->paramData; + Unique2Unk10* unk10 = &uniq2->unk_10; + s32 pad2; + f32 playerHeight; + + playerHeight = Player_GetHeight(camera->player); + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, at, eye); + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = ((1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight))); + uniq2->yOffset = NEXTPCT * playerHeight * yNormal; + uniq2->distTarget = NEXTSETTING; + uniq2->fovTarget = NEXTSETTING; + uniq2->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = uniq2->interfaceFlags; + + if ((camera->animState == 0) || (unk10->unk_04 != uniq2->interfaceFlags)) { + unk10->unk_04 = uniq2->interfaceFlags; + } + + if (camera->animState == 0) { + camera->animState = 1; + func_80043B60(camera); + unk10->unk_00 = 200.0f; + if (uniq2->interfaceFlags & 0x10) { + camera->unk_14C &= ~4; + } + } + + playerPos = camera->playerPosRot.pos; + lerpRateFactor = (uniq2->interfaceFlags & 1 ? 1.0f : camera->speedRatio); + at->x = F32_LERPIMP(at->x, playerPos.x, lerpRateFactor * 0.6f); + at->y = F32_LERPIMP(at->y, playerPos.y + playerHeight + uniq2->yOffset, 0.4f); + at->z = F32_LERPIMP(at->z, playerPos.z, lerpRateFactor * 0.6f); + unk10->unk_00 = F32_LERPIMP(unk10->unk_00, 2.0f, 0.05f); // unused. + + if (uniq2->interfaceFlags & 1) { + OLib_Vec3fDiffToVecSphGeo(&eyeOffset, at, eyeNext); + eyeOffset.r = uniq2->distTarget; + Camera_Vec3fVecSphGeoAdd(&playerPos, at, &eyeOffset); + Camera_LERPCeilVec3f(&playerPos, eye, 0.25f, 0.25f, 0.2f); + } else if (uniq2->interfaceFlags & 2) { + if (OLib_Vec3fDistXZ(at, eyeNext) < uniq2->distTarget) { + OLib_Vec3fDiffToVecSphGeo(&eyeOffset, at, eyeNext); + eyeOffset.yaw = Camera_LERPCeilS(eyeOffset.yaw, eyeAtOffset.yaw, 0.1f, 0xA); + eyeOffset.r = uniq2->distTarget; + eyeOffset.pitch = 0; + Camera_Vec3fVecSphGeoAdd(eye, at, &eyeOffset); + eye->y = eyeNext->y; + } else { + Camera_LERPCeilVec3f(eyeNext, eye, 0.25f, 0.25f, 0.2f); + } + } + + Camera_BGCheck(camera, at, eye); + camera->dist = OLib_Vec3fDist(at, eye); + camera->roll = 0; + camera->fov = Camera_LERPCeilF(uniq2->fovTarget, camera->fov, 0.2f, 0.1f); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, 1.0f); + return true; +} + +s32 Camera_Unique3(Camera* camera) { + VecSph sp60; + f32 playerHeight; + Unique3* uniq3 = (Unique3*)camera->paramData; + Vec3s* temp_v0_2; + Vec3s sp4C; + Unique3Anim* anim = &uniq3->anim; + Unique3Params* params = &uniq3->params; + Vec3f* at = &camera->at; + PosRot* cameraPlayerPosRot = &camera->playerPosRot; + + playerHeight = Player_GetHeight(camera->player); + camera->unk_14C &= ~0x10; + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerHeight)); + + params->yOffset = NEXTPCT * playerHeight * yNormal; + params->fov = NEXTSETTING; + params->interfaceFlags = NEXTSETTING; + } + if (R_RELOAD_CAM_PARAMS != 0) { + Camera_CopyPREGToModeValues(camera); + } + sCameraInterfaceFlags = params->interfaceFlags; + switch (camera->animState) { + case 0: + func_80043B60(camera); + camera->unk_14C &= ~(0x8 | 0x4); + anim->initialFov = camera->fov; + anim->initialDist = OLib_Vec3fDist(at, &camera->eye); + camera->animState++; + case 1: + if (uniq3->doorParams.timer1-- > 0) { + break; + } + temp_v0_2 = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(&camera->eyeNext, &BGCAM_POS(temp_v0_2)); + camera->eye = camera->eyeNext; + sp4C = BGCAM_ROT(temp_v0_2); + sp60.r = 100.0f; + sp60.yaw = sp4C.y; + sp60.pitch = -sp4C.x; + Camera_Vec3fVecSphGeoAdd(at, &camera->eye, &sp60); + camera->animState++; + + case 2: + if (params->interfaceFlags & 4) { + camera->at = cameraPlayerPosRot->pos; + camera->at.y += playerHeight + params->yOffset; + } + if (uniq3->doorParams.timer2-- > 0) { + break; + } + camera->animState++; + + case 3: + camera->unk_14C |= (0x400 | 0x10); + if ((camera->unk_14C & 8) != 0) { + camera->animState++; + } else { + break; + } + case 4: + if (params->interfaceFlags & 2) { + camera->unk_14C |= 4; + camera->unk_14C &= ~8; + Camera_ChangeSettingFlags(camera, CAM_SET_PIVOT_IN_FRONT, 2); + break; + } + uniq3->doorParams.timer3 = 5; + if (camera->xzSpeed > 0.001f || CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CLEFT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CDOWN) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CUP) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CRIGHT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_R) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_Z)) { + camera->animState++; + } else { + break; + } + case 5: + camera->fov = Camera_LERPCeilF(anim->initialFov, camera->fov, 0.4f, 0.1f); + OLib_Vec3fDiffToVecSphGeo(&sp60, at, &camera->eye); + sp60.r = Camera_LERPCeilF(100.0f, sp60.r, 0.4f, 0.1f); + Camera_Vec3fVecSphGeoAdd(&camera->eyeNext, at, &sp60); + camera->eye = camera->eyeNext; + if (uniq3->doorParams.timer3-- > 0) { + break; + } + camera->animState++; + default: + camera->unk_14C |= 4; + camera->unk_14C &= ~8; + camera->fov = params->fov; + Camera_ChangeSettingFlags(camera, camera->prevSetting, 2); + camera->atLERPStepScale = 0.0f; + camera->posOffset.x = camera->at.x - cameraPlayerPosRot->pos.x; + camera->posOffset.y = camera->at.y - cameraPlayerPosRot->pos.y; + camera->posOffset.z = camera->at.z - cameraPlayerPosRot->pos.z; + break; + } + + return true; +} + +/** + * Camera's eye is specified by scene camera data, at point is generated at the intersection + * of the eye to the player + */ +s32 Camera_Unique0(Camera* camera) { + f32 yOffset; + CameraModeValue* values; + Player* player; + Vec3f playerPosWithOffset; + VecSph atPlayerOffset; + Vec3s* sceneCamData; + Vec3s sceneCamRot; + PosRot* playerPosRot = &camera->playerPosRot; + Unique0* uniq0 = (Unique0*)camera->paramData; + Unique0Params* params = &uniq0->uniq0; + Unique0Anim* anim = ¶ms->anim; + Vec3f* eye = &camera->eye; + s16 fov; + + yOffset = Player_GetHeight(camera->player); + player = camera->player; + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + params->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + playerPosWithOffset = playerPosRot->pos; + playerPosWithOffset.y += yOffset; + + sCameraInterfaceFlags = params->interfaceFlags; + + if (camera->animState == 0) { + func_80043B60(camera); + camera->unk_14C &= ~4; + sceneCamData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(&anim->sceneCamPosPlayerLine.a, &BGCAM_POS(sceneCamData)); + + *eye = camera->eyeNext = anim->sceneCamPosPlayerLine.a; + sceneCamRot = BGCAM_ROT(sceneCamData); + fov = BGCAM_FOV(sceneCamData); + if (fov != -1) { + camera->fov = fov < 361 ? fov : PCT(fov); + } + anim->animTimer = BGCAM_JFIFID(sceneCamData); + if (anim->animTimer == -1) { + anim->animTimer = uniq0->doorParams.timer1 + uniq0->doorParams.timer2; + } + atPlayerOffset.r = OLib_Vec3fDist(&playerPosWithOffset, eye); + atPlayerOffset.yaw = sceneCamRot.y; + atPlayerOffset.pitch = -sceneCamRot.x; + OLib_VecSphGeoToVec3f(&anim->sceneCamPosPlayerLine.b, &atPlayerOffset); + Math3D_LineClosestToPoint(&anim->sceneCamPosPlayerLine, &playerPosRot->pos, &camera->at); + anim->initalPos = playerPosRot->pos; + camera->animState++; + } + + if (player->stateFlags1 & 0x20000000) { + anim->initalPos = playerPosRot->pos; + } + + if (params->interfaceFlags & 1) { + if (anim->animTimer > 0) { + anim->animTimer--; + anim->initalPos = playerPosRot->pos; + } else if ((!(player->stateFlags1 & 0x20000000)) && + ((OLib_Vec3fDistXZ(&playerPosRot->pos, &anim->initalPos) >= 10.0f) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CLEFT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CDOWN) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CUP) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CRIGHT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_R) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_Z))) { + camera->dist = OLib_Vec3fDist(&camera->at, eye); + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + camera->atLERPStepScale = 0.0f; + camera->unk_14C |= 4; + Camera_ChangeSettingFlags(camera, camera->prevSetting, 2); + } + } else { + if (anim->animTimer > 0) { + anim->animTimer--; + if (anim->animTimer == 0) { + sCameraInterfaceFlags = 0; + } + } else { + anim->initalPos = playerPosRot->pos; + } + + if ((!(player->stateFlags1 & 0x20000000)) && + ((0.001f < camera->xzSpeed) || CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CLEFT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CDOWN) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CUP) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CRIGHT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_R) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_Z))) { + camera->dist = OLib_Vec3fDist(&camera->at, &camera->eye); + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + camera->atLERPStepScale = 0.0f; + Camera_ChangeSettingFlags(camera, camera->prevSetting, 2); + camera->unk_14C |= 4; + } + } + return true; +} + +s32 Camera_Unique4(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Was setup to be used by the camera setting "FOREST_UNUSED" + */ +s32 Camera_Unique5(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * This function doesn't really update much. + * Eye/at positions are updated via Camera_SetParam + */ +s32 Camera_Unique6(Camera* camera) { + Unique6* uniq6 = (Unique6*)camera->paramData; + CameraModeValue* values; + Vec3f sp2C; + PosRot* playerPosRot = &camera->playerPosRot; + f32 offset; + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + uniq6->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = uniq6->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + func_80043ABC(camera); + } + + if (camera->player != NULL) { + offset = Player_GetHeight(camera->player); + sp2C = playerPosRot->pos; + sp2C.y += offset; + camera->dist = OLib_Vec3fDist(&sp2C, &camera->eye); + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + } else { + camera->dist = OLib_Vec3fDist(&camera->at, &camera->eye); + } + + if (uniq6->interfaceFlags & 1 && camera->timer > 0) { + camera->timer--; + } + + return true; +} + +/** + * Camera is at a fixed point specified by the scene's camera data, + * camera rotates to follow player + */ +s32 Camera_Unique7(Camera* camera) { + s32 pad; + Unique7* uniq7 = (Unique7*)camera->paramData; + CameraModeValue* values; + VecSph playerPosEyeOffset; + s16 fov; + Vec3s* sceneCamData; + Vec3s sceneCamRot; + Vec3f* at = &camera->at; + PosRot* playerPosRot = &camera->playerPosRot; + Vec3f* eye = &camera->eye; + Vec3f* eyeNext = &camera->eyeNext; + Unique7Unk8* unk08 = &uniq7->unk_08; + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + uniq7->fov = NEXTSETTING; + uniq7->interfaceFlags = (s16)NEXTSETTING; + } + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sceneCamData = Camera_GetCamBGData(camera); + + Camera_Vec3sToVec3f(eyeNext, &BGCAM_POS(sceneCamData)); + *eye = *eyeNext; + sceneCamRot = BGCAM_ROT(sceneCamData); // unused + (void)sceneCamRot; // suppresses set but unused warning + + OLib_Vec3fDiffToVecSphGeo(&playerPosEyeOffset, eye, &playerPosRot->pos); + + // fov actually goes unused since it's hard set later on. + fov = BGCAM_FOV(sceneCamData); + if (fov == -1) { + fov = uniq7->fov * 100.0f; + } + + if (fov < 361) { + fov *= 100; + } + + sCameraInterfaceFlags = uniq7->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + camera->fov = PCT(fov); + camera->atLERPStepScale = 0.0f; + camera->roll = 0; + unk08->unk_00.x = playerPosEyeOffset.yaw; + } + + camera->fov = 60.0f; + + // 0x7D0 ~ 10.98 degres. + unk08->unk_00.x = Camera_LERPFloorS(playerPosEyeOffset.yaw, unk08->unk_00.x, 0.4f, 0x7D0); + playerPosEyeOffset.pitch = + -BGCAM_ROT(sceneCamData).x * Math_CosS(playerPosEyeOffset.yaw - BGCAM_ROT(sceneCamData).y); + Camera_Vec3fVecSphGeoAdd(at, eye, &playerPosEyeOffset); + camera->unk_14C |= 0x400; + return true; +} + +s32 Camera_Unique8(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Unique9(Camera* camera) { + Vec3f atTarget; + Vec3f eyeTarget; + Unique9* uniq9 = &ONEPOINT_CS_INFO(camera)->uniq9; + Unique9Anim* anim = &uniq9->anim; + f32 invKeyFrameTimer; + VecSph eyeNextAtOffset; + VecSph scratchSph; + VecSph playerTargetOffset; + s16 action; + s16 atInitFlags; + s16 eyeInitFlags; + s16 pad2; + PosRot targethead; + PosRot playerhead; + PosRot playerPosRot; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f* at = &camera->at; + Vec3f* eye = &camera->eye; + Player* player = camera->player; + Actor* focusActor; + f32 spB4; + PosRot atFocusPosRot; + Vec3f eyeLookAtPos; + CameraModeValue* values; + PosRot eyeFocusPosRot; + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + uniq9->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = uniq9->interfaceFlags; + + Actor_GetWorld(&playerPosRot, &camera->player->actor); + + if (camera->animState == 0) { + camera->animState++; + anim->curKeyFrameIdx = -1; + anim->keyFrameTimer = 1; + anim->unk_38 = 0; + anim->playerPos.x = playerPosRot.pos.x; + anim->playerPos.y = playerPosRot.pos.y; + anim->playerPos.z = playerPosRot.pos.z; + camera->atLERPStepScale = 0.0f; + func_80043B60(camera); + } + + if (anim->unk_38 == 0 && anim->keyFrameTimer > 0) { + anim->keyFrameTimer--; + } + + if (anim->keyFrameTimer == 0) { + anim->isNewKeyFrame = true; + anim->curKeyFrameIdx++; + if (anim->curKeyFrameIdx < ONEPOINT_CS_INFO(camera)->keyFrameCnt) { + anim->curKeyFrame = &ONEPOINT_CS_INFO(camera)->keyFrames[anim->curKeyFrameIdx]; + anim->keyFrameTimer = anim->curKeyFrame->timerInit; + + if (anim->curKeyFrame->unk_01 != 0xFF) { + if ((anim->curKeyFrame->unk_01 & 0xF0) == 0x80) { + D_8011D3AC = anim->curKeyFrame->unk_01 & 0xF; + } else if ((anim->curKeyFrame->unk_01 & 0xF0) == 0xC0) { + Camera_UpdateInterface(0xF000 | ((anim->curKeyFrame->unk_01 & 0xF) << 8)); + } else if (camera->player->stateFlags1 & 0x8000000 && player->currentBoots != PLAYER_BOOTS_IRON) { + func_8002DF38(camera->globalCtx, camera->target, 8); + osSyncPrintf("camera: demo: player demo set WAIT\n"); + } else { + osSyncPrintf("camera: demo: player demo set %d\n", anim->curKeyFrame->unk_01); + func_8002DF38(camera->globalCtx, camera->target, anim->curKeyFrame->unk_01); + } + } + } else { + // We've gone through all the keyframes. + if (camera->thisIdx != MAIN_CAM) { + camera->timer = 0; + } + return true; + } + } else { + anim->isNewKeyFrame = false; + } + + atInitFlags = anim->curKeyFrame->initFlags & 0xFF; + if (atInitFlags == 1) { + anim->atTarget = anim->curKeyFrame->atTargetInit; + } else if (atInitFlags == 2) { + if (anim->isNewKeyFrame) { + anim->atTarget.x = camera->globalCtx->view.lookAt.x + anim->curKeyFrame->atTargetInit.x; + anim->atTarget.y = camera->globalCtx->view.lookAt.y + anim->curKeyFrame->atTargetInit.y; + anim->atTarget.z = camera->globalCtx->view.lookAt.z + anim->curKeyFrame->atTargetInit.z; + } + } else if (atInitFlags == 3) { + if (anim->isNewKeyFrame) { + anim->atTarget.x = camera->at.x + anim->curKeyFrame->atTargetInit.x; + anim->atTarget.y = camera->at.y + anim->curKeyFrame->atTargetInit.y; + anim->atTarget.z = camera->at.z + anim->curKeyFrame->atTargetInit.z; + } + } else if (atInitFlags == 4 || atInitFlags == 0x84) { + if (camera->target != NULL && camera->target->update != NULL) { + Actor_GetFocus(&targethead, camera->target); + Actor_GetFocus(&playerhead, &camera->player->actor); + playerhead.pos.x = playerPosRot.pos.x; + playerhead.pos.z = playerPosRot.pos.z; + OLib_Vec3fDiffToVecSphGeo(&playerTargetOffset, &targethead.pos, &playerhead.pos); + if (atInitFlags & (s16)0x8080) { + scratchSph.pitch = DEGF_TO_BINANG(anim->curKeyFrame->atTargetInit.x); + scratchSph.yaw = DEGF_TO_BINANG(anim->curKeyFrame->atTargetInit.y); + scratchSph.r = anim->curKeyFrame->atTargetInit.z; + } else { + OLib_Vec3fToVecSphGeo(&scratchSph, &anim->curKeyFrame->atTargetInit); + } + scratchSph.yaw += playerTargetOffset.yaw; + scratchSph.pitch += playerTargetOffset.pitch; + Camera_Vec3fVecSphGeoAdd(&anim->atTarget, &targethead.pos, &scratchSph); + } else { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: demo C: actor is not valid\n" VT_RST); + } + + camera->target = NULL; + anim->atTarget = camera->at; + } + } else if (atInitFlags & 0x6060) { + if (!(atInitFlags & 4) || anim->isNewKeyFrame) { + if (atInitFlags & 0x2020) { + focusActor = &camera->player->actor; + } else if (camera->target != NULL && camera->target->update != NULL) { + focusActor = camera->target; + } else { + camera->target = NULL; + focusActor = NULL; + } + + if (focusActor != NULL) { + if ((atInitFlags & 0xF) == 1) { + Actor_GetFocus(&atFocusPosRot, focusActor); + } else if ((atInitFlags & 0xF) == 2) { + Actor_GetWorld(&atFocusPosRot, focusActor); + } else { + Actor_GetWorldPosShapeRot(&atFocusPosRot, focusActor); + } + + if (atInitFlags & (s16)0x8080) { + scratchSph.pitch = DEGF_TO_BINANG(anim->curKeyFrame->atTargetInit.x); + scratchSph.yaw = DEGF_TO_BINANG(anim->curKeyFrame->atTargetInit.y); + scratchSph.r = anim->curKeyFrame->atTargetInit.z; + } else { + OLib_Vec3fToVecSphGeo(&scratchSph, &anim->curKeyFrame->atTargetInit); + } + + scratchSph.yaw += atFocusPosRot.rot.y; + scratchSph.pitch -= atFocusPosRot.rot.x; + Camera_Vec3fVecSphGeoAdd(&anim->atTarget, &atFocusPosRot.pos, &scratchSph); + } else { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: demo C: actor is not valid\n" VT_RST); + } + anim->atTarget = *at; + } + } + } else { + anim->atTarget = *at; + } + + eyeInitFlags = anim->curKeyFrame->initFlags & 0xFF00; + if (eyeInitFlags == 0x100) { + anim->eyeTarget = anim->curKeyFrame->eyeTargetInit; + } else if (eyeInitFlags == 0x200) { + if (anim->isNewKeyFrame) { + anim->eyeTarget.x = camera->globalCtx->view.eye.x + anim->curKeyFrame->eyeTargetInit.x; + anim->eyeTarget.y = camera->globalCtx->view.eye.y + anim->curKeyFrame->eyeTargetInit.y; + anim->eyeTarget.z = camera->globalCtx->view.eye.z + anim->curKeyFrame->eyeTargetInit.z; + } + } else if (eyeInitFlags == 0x300) { + if (anim->isNewKeyFrame) { + anim->eyeTarget.x = camera->eyeNext.x + anim->curKeyFrame->eyeTargetInit.x; + anim->eyeTarget.y = camera->eyeNext.y + anim->curKeyFrame->eyeTargetInit.y; + anim->eyeTarget.z = camera->eyeNext.z + anim->curKeyFrame->eyeTargetInit.z; + } + } else if (eyeInitFlags == 0x400 || eyeInitFlags == (s16)0x8400 || eyeInitFlags == 0x500 || + eyeInitFlags == (s16)0x8500) { + if (camera->target != NULL && camera->target->update != NULL) { + Actor_GetFocus(&targethead, camera->target); + Actor_GetFocus(&playerhead, &camera->player->actor); + playerhead.pos.x = playerPosRot.pos.x; + playerhead.pos.z = playerPosRot.pos.z; + OLib_Vec3fDiffToVecSphGeo(&playerTargetOffset, &targethead.pos, &playerhead.pos); + if (eyeInitFlags == 0x400 || eyeInitFlags == (s16)0x8400) { + eyeLookAtPos = targethead.pos; + } else { + eyeLookAtPos = anim->atTarget; + } + + if (eyeInitFlags & (s16)0x8080) { + scratchSph.pitch = DEGF_TO_BINANG(anim->curKeyFrame->eyeTargetInit.x); + scratchSph.yaw = DEGF_TO_BINANG(anim->curKeyFrame->eyeTargetInit.y); + scratchSph.r = anim->curKeyFrame->eyeTargetInit.z; + } else { + OLib_Vec3fToVecSphGeo(&scratchSph, &anim->curKeyFrame->eyeTargetInit); + } + + scratchSph.yaw += playerTargetOffset.yaw; + scratchSph.pitch += playerTargetOffset.pitch; + Camera_Vec3fVecSphGeoAdd(&anim->eyeTarget, &eyeLookAtPos, &scratchSph); + } else { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: demo C: actor is not valid\n" VT_RST); + } + camera->target = NULL; + anim->eyeTarget = *eyeNext; + } + } else if (eyeInitFlags & 0x6060) { + if (!(eyeInitFlags & 0x400) || anim->isNewKeyFrame) { + if (eyeInitFlags & 0x2020) { + focusActor = &camera->player->actor; + } else if (camera->target != NULL && camera->target->update != NULL) { + focusActor = camera->target; + } else { + camera->target = NULL; + focusActor = NULL; + } + + if (focusActor != NULL) { + if ((eyeInitFlags & 0xF00) == 0x100) { + // head + Actor_GetFocus(&eyeFocusPosRot, focusActor); + } else if ((eyeInitFlags & 0xF00) == 0x200) { + // world + Actor_GetWorld(&eyeFocusPosRot, focusActor); + } else { + // world, shapeRot + Actor_GetWorldPosShapeRot(&eyeFocusPosRot, focusActor); + } + + if (eyeInitFlags & (s16)0x8080) { + scratchSph.pitch = DEGF_TO_BINANG(anim->curKeyFrame->eyeTargetInit.x); + scratchSph.yaw = DEGF_TO_BINANG(anim->curKeyFrame->eyeTargetInit.y); + scratchSph.r = anim->curKeyFrame->eyeTargetInit.z; + } else { + OLib_Vec3fToVecSphGeo(&scratchSph, &anim->curKeyFrame->eyeTargetInit); + } + + scratchSph.yaw += eyeFocusPosRot.rot.y; + scratchSph.pitch -= eyeFocusPosRot.rot.x; + Camera_Vec3fVecSphGeoAdd(&anim->eyeTarget, &eyeFocusPosRot.pos, &scratchSph); + } else { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: demo C: actor is not valid\n" VT_RST); + } + camera->target = NULL; + anim->eyeTarget = *eyeNext; + } + } + } else { + anim->eyeTarget = *eyeNext; + } + + if (anim->curKeyFrame->initFlags == 2) { + anim->fovTarget = camera->globalCtx->view.fovy; + anim->rollTarget = 0; + } else if (anim->curKeyFrame->initFlags == 0) { + anim->fovTarget = camera->fov; + anim->rollTarget = camera->roll; + } else { + anim->fovTarget = anim->curKeyFrame->fovTargetInit; + anim->rollTarget = DEGF_TO_BINANG(anim->curKeyFrame->rollTargetInit); + } + + action = anim->curKeyFrame->actionFlags & 0x1F; + switch (action) { + case 15: + // static copy to at/eye/fov/roll + *at = anim->atTarget; + *eyeNext = anim->eyeTarget; + camera->fov = anim->fovTarget; + camera->roll = anim->rollTarget; + camera->unk_14C |= 0x400; + break; + case 21: + // same as 15, but with unk_38 ? + if (anim->unk_38 == 0) { + anim->unk_38 = 1; + } else if (camera->unk_14C & 8) { + anim->unk_38 = 0; + camera->unk_14C &= ~8; + } + *at = anim->atTarget; + *eyeNext = anim->eyeTarget; + camera->fov = anim->fovTarget; + camera->roll = anim->rollTarget; + break; + case 16: + // same as 16, but don't unset bit 0x8 on unk_14C + if (anim->unk_38 == 0) { + anim->unk_38 = 1; + } else if (camera->unk_14C & 8) { + anim->unk_38 = 0; + } + + *at = anim->atTarget; + *eyeNext = anim->eyeTarget; + camera->fov = anim->fovTarget; + camera->roll = anim->rollTarget; + break; + case 1: + // linear interpolation of eye/at using the spherical coordinates + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + OLib_Vec3fDiffToVecSphGeo(&anim->atEyeOffsetTarget, &anim->atTarget, &anim->eyeTarget); + invKeyFrameTimer = 1.0f / anim->keyFrameTimer; + scratchSph.r = F32_LERPIMP(eyeNextAtOffset.r, anim->atEyeOffsetTarget.r, invKeyFrameTimer); + scratchSph.pitch = eyeNextAtOffset.pitch + + (BINANG_SUB(anim->atEyeOffsetTarget.pitch, eyeNextAtOffset.pitch) * invKeyFrameTimer); + scratchSph.yaw = + eyeNextAtOffset.yaw + (BINANG_SUB(anim->atEyeOffsetTarget.yaw, eyeNextAtOffset.yaw) * invKeyFrameTimer); + Camera_Vec3fVecSphGeoAdd(&eyeTarget, at, &scratchSph); + goto setEyeNext; + case 2: + // linear interpolation of eye/at using the eyeTarget + invKeyFrameTimer = 1.0f / anim->keyFrameTimer; + eyeTarget.x = F32_LERPIMP(camera->eyeNext.x, anim->eyeTarget.x, invKeyFrameTimer); + eyeTarget.y = F32_LERPIMP(camera->eyeNext.y, anim->eyeTarget.y, invKeyFrameTimer); + eyeTarget.z = F32_LERPIMP(camera->eyeNext.z, anim->eyeTarget.z, invKeyFrameTimer); + + setEyeNext: + camera->eyeNext.x = + Camera_LERPFloorF(eyeTarget.x, camera->eyeNext.x, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->eyeNext.y = + Camera_LERPFloorF(eyeTarget.y, camera->eyeNext.y, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->eyeNext.z = + Camera_LERPFloorF(eyeTarget.z, camera->eyeNext.z, anim->curKeyFrame->lerpStepScale, 1.0f); + case 9: + case 10: + // linear interpolation of at/fov/roll + invKeyFrameTimer = 1.0f / anim->keyFrameTimer; + atTarget.x = F32_LERPIMP(camera->at.x, anim->atTarget.x, invKeyFrameTimer); + atTarget.y = F32_LERPIMP(camera->at.y, anim->atTarget.y, invKeyFrameTimer); + atTarget.z = F32_LERPIMP(camera->at.z, anim->atTarget.z, invKeyFrameTimer); + camera->at.x = Camera_LERPFloorF(atTarget.x, camera->at.x, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->at.y = Camera_LERPFloorF(atTarget.y, camera->at.y, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->at.z = Camera_LERPFloorF(atTarget.z, camera->at.z, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->fov = Camera_LERPFloorF(F32_LERPIMP(camera->fov, anim->fovTarget, invKeyFrameTimer), camera->fov, + anim->curKeyFrame->lerpStepScale, 0.01f); + camera->roll = Camera_LERPFloorS(BINANG_LERPIMPINV(camera->roll, anim->rollTarget, anim->keyFrameTimer), + camera->roll, anim->curKeyFrame->lerpStepScale, 0xA); + break; + case 4: + // linear interpolation of eye/at/fov/roll using the step scale, and spherical coordinates + OLib_Vec3fDiffToVecSphGeo(&eyeNextAtOffset, at, eyeNext); + OLib_Vec3fDiffToVecSphGeo(&anim->atEyeOffsetTarget, &anim->atTarget, &anim->eyeTarget); + scratchSph.r = + Camera_LERPCeilF(anim->atEyeOffsetTarget.r, eyeNextAtOffset.r, anim->curKeyFrame->lerpStepScale, 0.1f); + scratchSph.pitch = Camera_LERPCeilS(anim->atEyeOffsetTarget.pitch, eyeNextAtOffset.pitch, + anim->curKeyFrame->lerpStepScale, 1); + scratchSph.yaw = + Camera_LERPCeilS(anim->atEyeOffsetTarget.yaw, eyeNextAtOffset.yaw, anim->curKeyFrame->lerpStepScale, 1); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &scratchSph); + goto setAtFOVRoll; + case 3: + // linear interplation of eye/at/fov/roll using the step scale using eyeTarget + camera->eyeNext.x = + Camera_LERPCeilF(anim->eyeTarget.x, camera->eyeNext.x, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->eyeNext.y = + Camera_LERPCeilF(anim->eyeTarget.y, camera->eyeNext.y, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->eyeNext.z = + Camera_LERPCeilF(anim->eyeTarget.z, camera->eyeNext.z, anim->curKeyFrame->lerpStepScale, 1.0f); + case 11: + case 12: + setAtFOVRoll: + // linear interpolation of at/fov/roll using the step scale. + camera->at.x = Camera_LERPCeilF(anim->atTarget.x, camera->at.x, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->at.y = Camera_LERPCeilF(anim->atTarget.y, camera->at.y, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->at.z = Camera_LERPCeilF(anim->atTarget.z, camera->at.z, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->fov = Camera_LERPCeilF(anim->fovTarget, camera->fov, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->roll = Camera_LERPCeilS(anim->rollTarget, camera->roll, anim->curKeyFrame->lerpStepScale, 1); + break; + case 13: + // linear interpolation of at, with rotation around eyeTargetInit.y + camera->at.x = Camera_LERPCeilF(anim->atTarget.x, camera->at.x, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->at.y += camera->playerPosDelta.y * anim->curKeyFrame->lerpStepScale; + camera->at.z = Camera_LERPCeilF(anim->atTarget.z, camera->at.z, anim->curKeyFrame->lerpStepScale, 1.0f); + OLib_Vec3fDiffToVecSphGeo(&scratchSph, at, eyeNext); + scratchSph.yaw += DEGF_TO_BINANG(anim->curKeyFrame->eyeTargetInit.y); + + // 3A98 ~ 82.40 degrees + if (scratchSph.pitch >= 0x3A99) { + scratchSph.pitch = 0x3A98; + } + + if (scratchSph.pitch < -0x3A98) { + scratchSph.pitch = -0x3A98; + } + + spB4 = scratchSph.r; + if (1) {} + scratchSph.r = + !(spB4 < anim->curKeyFrame->eyeTargetInit.z) + ? Camera_LERPCeilF(anim->curKeyFrame->eyeTargetInit.z, spB4, anim->curKeyFrame->lerpStepScale, 1.0f) + : scratchSph.r; + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &scratchSph); + camera->fov = + Camera_LERPCeilF(F32_LERPIMPINV(camera->fov, anim->curKeyFrame->fovTargetInit, anim->keyFrameTimer), + camera->fov, anim->curKeyFrame->lerpStepScale, 1.0f); + camera->roll = Camera_LERPCeilS(anim->rollTarget, camera->roll, anim->curKeyFrame->lerpStepScale, 1); + break; + case 24: + // Set current keyframe to the roll target? + anim->curKeyFrameIdx = anim->rollTarget; + break; + case 19: { + // Change the parent camera (or default)'s mode to normal + s32 camIdx = camera->parentCamIdx <= SUBCAM_NONE ? MAIN_CAM : camera->parentCamIdx; + + Camera_ChangeModeFlags(camera->globalCtx->cameraPtrs[camIdx], CAM_MODE_NORMAL, 1); + } + case 18: { + // copy the current camera to the parent (or default)'s camera. + s32 camIdx = camera->parentCamIdx <= SUBCAM_NONE ? MAIN_CAM : camera->parentCamIdx; + Camera* cam = camera->globalCtx->cameraPtrs[camIdx]; + + *eye = *eyeNext; + Camera_Copy(cam, camera); + } + default: + if (camera->thisIdx != MAIN_CAM) { + camera->timer = 0; + } + } + + *eye = *eyeNext; + + if (anim->curKeyFrame->actionFlags & 0x80) { + Camera_BGCheck(camera, at, eye); + } + + if (anim->curKeyFrame->actionFlags & 0x40) { + // Set the player's position + camera->player->actor.world.pos.x = anim->playerPos.x; + camera->player->actor.world.pos.z = anim->playerPos.z; + if (camera->player->stateFlags1 & 0x8000000 && player->currentBoots != PLAYER_BOOTS_IRON) { + camera->player->actor.world.pos.y = anim->playerPos.y; + } + } else { + anim->playerPos.x = playerPosRot.pos.x; + anim->playerPos.y = playerPosRot.pos.y; + anim->playerPos.z = playerPosRot.pos.z; + } + + if (anim->unk_38 == 0 && camera->timer > 0) { + camera->timer--; + } + + if (camera->player != NULL) { + camera->posOffset.x = camera->at.x - camera->playerPosRot.pos.x; + camera->posOffset.y = camera->at.y - camera->playerPosRot.pos.y; + camera->posOffset.z = camera->at.z - camera->playerPosRot.pos.z; + } + + camera->dist = OLib_Vec3fDist(at, eye); + return true; +} + +void Camera_DebugPrintSplineArray(char* name, s16 length, CutsceneCameraPoint cameraPoints[]) { + s32 i; + + osSyncPrintf("static SplinedatZ %s[] = {\n", name); + for (i = 0; i < length; i++) { + osSyncPrintf(" /* key frame %2d */ {\n", i); + osSyncPrintf(" /* code */ %d,\n", cameraPoints[i].continueFlag); + osSyncPrintf(" /* z */ %d,\n", cameraPoints[i].cameraRoll); + osSyncPrintf(" /* T */ %d,\n", cameraPoints[i].nextPointFrame); + osSyncPrintf(" /* zoom */ %f,\n", cameraPoints[i].viewAngle); + osSyncPrintf(" /* pos */ { %d, %d, %d }\n", cameraPoints[i].pos.x, cameraPoints[i].pos.y, + cameraPoints[i].pos.z); + osSyncPrintf(" },\n"); + } + osSyncPrintf("};\n\n"); +} + +/** + * Copies `src` to `dst`, used in Camera_Demo1 + * Name from AC map: Camera2_SetPos_Demo + */ +void Camera_Vec3fCopy(Vec3f* src, Vec3f* dst) { + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} + +/** + * Calculates new position from `at` to `pos`, outputs to `dst + * Name from AC map: Camera2_CalcPos_Demo + */ +void Camera_RotateAroundPoint(PosRot* at, Vec3f* pos, Vec3f* dst) { + VecSph posSph; + Vec3f posCopy; + + Camera_Vec3fCopy(pos, &posCopy); + OLib_Vec3fToVecSphGeo(&posSph, &posCopy); + posSph.yaw += at->rot.y; + Camera_Vec3fVecSphGeoAdd(dst, &at->pos, &posSph); +} + +/** + * Camera follows points specified at pointers to CutsceneCameraPoints, + * camera->data0 for camera at positions, and camera->data1 for camera eye positions + * until all keyFrames have been exhausted. + */ +s32 Camera_Demo1(Camera* camera) { + s32 pad; + Demo1* demo1 = (Demo1*)camera->paramData; + CameraModeValue* values; + Vec3f* at = &camera->at; + CutsceneCameraPoint* csAtPoints = (CutsceneCameraPoint*)camera->data0; + CutsceneCameraPoint* csEyePoints = (CutsceneCameraPoint*)camera->data1; + Vec3f* eye = &camera->eye; + PosRot curPlayerPosRot; + Vec3f csEyeUpdate; + Vec3f csAtUpdate; + f32 newRoll; + Vec3f* eyeNext = &camera->eyeNext; + f32* cameraFOV = &camera->fov; + s16* relativeToPlayer = &camera->data2; + Demo1Anim* anim = &demo1->anim; + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + demo1->interfaceFlags = NEXTSETTING; + } + + sCameraInterfaceFlags = demo1->interfaceFlags; + + switch (camera->animState) { + case 0: + // initalize camera state + anim->keyframe = 0; + anim->curFrame = 0.0f; + camera->animState++; + // "absolute" : "relative" + osSyncPrintf(VT_SGR("1") "%06u:" VT_RST " camera: spline demo: start %s \n", + camera->globalCtx->state.frames, *relativeToPlayer == 0 ? "絶対" : "相対"); + + if (PREG(93)) { + Camera_DebugPrintSplineArray("CENTER", 5, csAtPoints); + Camera_DebugPrintSplineArray(" EYE", 5, csEyePoints); + } + case 1: + // follow CutsceneCameraPoints. function returns 1 if at the end. + if (func_800BB2B4(&csEyeUpdate, &newRoll, cameraFOV, csEyePoints, &anim->keyframe, &anim->curFrame) || + func_800BB2B4(&csAtUpdate, &newRoll, cameraFOV, csAtPoints, &anim->keyframe, &anim->curFrame)) { + camera->animState++; + } + if (*relativeToPlayer) { + // if the camera is set to be relative to the player, move the interpolated points + // relative to the player's position + if (camera->player != NULL && camera->player->actor.update != NULL) { + Actor_GetWorld(&curPlayerPosRot, &camera->player->actor); + Camera_RotateAroundPoint(&curPlayerPosRot, &csEyeUpdate, eyeNext); + Camera_RotateAroundPoint(&curPlayerPosRot, &csAtUpdate, at); + } else { + osSyncPrintf(VT_COL(RED, WHITE) "camera: spline demo: owner dead\n" VT_RST); + } + } else { + // simply copy the interpolated values to the eye and at + Camera_Vec3fCopy(&csEyeUpdate, eyeNext); + Camera_Vec3fCopy(&csAtUpdate, at); + } + *eye = *eyeNext; + camera->roll = newRoll * 256.0f; + camera->dist = OLib_Vec3fDist(at, eye); + break; + } + return true; +} + +s32 Camera_Demo2(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Opening large chests. + * The camera position will be at a fixed point, and rotate around at different intervals. + * The direction, and initial position is dependent on when the camera was started. + */ +s32 Camera_Demo3(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + PosRot* camPlayerPosRot = &camera->playerPosRot; + VecSph eyeAtOffset; + VecSph eyeOffset; + VecSph atOffset; + Vec3f sp74; + Vec3f sp68; + Vec3f sp5C; + f32 temp_f0; + s32 pad; + u8 skipUpdateEye = false; + f32 yOffset = Player_GetHeight(camera->player); + s16 angle; + Demo3* demo3 = (Demo3*)camera->paramData; + Demo3Anim* anim = &demo3->anim; + s32 pad2; + + camera->unk_14C &= ~0x10; + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + + demo3->fov = NEXTSETTING; + demo3->unk_04 = NEXTSETTING; // unused. + demo3->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, at, eye); + + sCameraInterfaceFlags = demo3->interfaceFlags; + + switch (camera->animState) { + case 0: + camera->unk_14C &= ~(0x8 | 0x4); + func_80043B60(camera); + camera->fov = demo3->fov; + camera->roll = anim->animFrame = 0; + anim->initialAt = camPlayerPosRot->pos; + if (camera->playerGroundY != BGCHECK_Y_MIN) { + anim->initialAt.y = camera->playerGroundY; + } + angle = camPlayerPosRot->rot.y; + sp68.x = anim->initialAt.x + (Math_SinS(angle) * 40.0f); + sp68.y = anim->initialAt.y + 40.0f; + sp68.z = anim->initialAt.z + (Math_CosS(angle) * 40.0f); + if (camera->globalCtx->state.frames & 1) { + angle -= 0x3FFF; + anim->yawDir = 1; + } else { + angle += 0x3FFF; + anim->yawDir = -1; + } + sp74.x = sp68.x + (D_8011D658[1].r * Math_SinS(angle)); + sp74.y = anim->initialAt.y + 5.0f; + sp74.z = sp68.z + (D_8011D658[1].r * Math_CosS(angle)); + if (Camera_BGCheck(camera, &sp68, &sp74)) { + anim->yawDir = -anim->yawDir; + } + OLib_Vec3fToVecSphGeo(&atOffset, &D_8011D678[0]); + atOffset.yaw += camPlayerPosRot->rot.y; + Camera_Vec3fVecSphGeoAdd(at, &anim->initialAt, &atOffset); + eyeOffset.r = D_8011D658[0].r; + eyeOffset.pitch = D_8011D658[0].pitch; + eyeOffset.yaw = (D_8011D658[0].yaw * anim->yawDir) + camPlayerPosRot->rot.y; + anim->unk_0C = 1.0f; + break; + case 1: + temp_f0 = (anim->animFrame - 2) * (1.0f / 146.0f); + + sp5C.x = F32_LERPIMP(D_8011D678[0].x, D_8011D678[1].x, temp_f0); + sp5C.y = F32_LERPIMP(D_8011D678[0].y, D_8011D678[1].y, temp_f0); + sp5C.z = F32_LERPIMP(D_8011D678[0].z, D_8011D678[1].z, temp_f0); + + OLib_Vec3fToVecSphGeo(&atOffset, &sp5C); + atOffset.yaw = (atOffset.yaw * anim->yawDir) + camPlayerPosRot->rot.y; + Camera_Vec3fVecSphGeoAdd(at, &anim->initialAt, &atOffset); + + atOffset.r = F32_LERPIMP(D_8011D658[0].r, D_8011D658[1].r, temp_f0); + atOffset.pitch = BINANG_LERPIMP(D_8011D658[0].pitch, D_8011D658[1].pitch, temp_f0); + atOffset.yaw = BINANG_LERPIMP(D_8011D658[0].yaw, D_8011D658[1].yaw, temp_f0); + + eyeOffset.r = atOffset.r; + eyeOffset.pitch = atOffset.pitch; + eyeOffset.yaw = (atOffset.yaw * anim->yawDir) + camPlayerPosRot->rot.y; + + anim->unk_0C -= (1.0f / 365.0f); + break; + case 2: + temp_f0 = (anim->animFrame - 0x94) * 0.1f; + + sp5C.x = F32_LERPIMP(D_8011D678[1].x, D_8011D678[2].x, temp_f0); + sp5C.y = F32_LERPIMP((D_8011D678[1].y - yOffset), D_8011D678[2].y, temp_f0); + sp5C.y += yOffset; + sp5C.z = F32_LERPIMP(D_8011D678[1].z, D_8011D678[2].z, temp_f0); + + OLib_Vec3fToVecSphGeo(&atOffset, &sp5C); + atOffset.yaw = (atOffset.yaw * anim->yawDir) + camPlayerPosRot->rot.y; + Camera_Vec3fVecSphGeoAdd(at, &anim->initialAt, &atOffset); + + atOffset.r = F32_LERPIMP(D_8011D658[1].r, D_8011D658[2].r, temp_f0); + atOffset.pitch = BINANG_LERPIMP(D_8011D658[1].pitch, D_8011D658[2].pitch, temp_f0); + atOffset.yaw = BINANG_LERPIMP(D_8011D658[1].yaw, D_8011D658[2].yaw, temp_f0); + + eyeOffset.r = atOffset.r; + eyeOffset.pitch = atOffset.pitch; + eyeOffset.yaw = (atOffset.yaw * anim->yawDir) + camPlayerPosRot->rot.y; + anim->unk_0C -= 0.04f; + break; + case 3: + temp_f0 = (anim->animFrame - 0x9F) * (1.0f / 9.0f); + + sp5C.x = F32_LERPIMP(D_8011D678[2].x, D_8011D678[3].x, temp_f0); + sp5C.y = F32_LERPIMP(D_8011D678[2].y, D_8011D678[3].y, temp_f0); + sp5C.y += yOffset; + sp5C.z = F32_LERPIMP(D_8011D678[2].z, D_8011D678[3].z, temp_f0); + + OLib_Vec3fToVecSphGeo(&atOffset, &sp5C); + atOffset.yaw = (atOffset.yaw * anim->yawDir) + camPlayerPosRot->rot.y; + Camera_Vec3fVecSphGeoAdd(at, &anim->initialAt, &atOffset); + + atOffset.r = F32_LERPIMP(D_8011D658[2].r, D_8011D658[3].r, temp_f0); + atOffset.pitch = BINANG_LERPIMP(D_8011D658[2].pitch, D_8011D658[3].pitch, temp_f0); + atOffset.yaw = BINANG_LERPIMP(D_8011D658[2].yaw, D_8011D658[3].yaw, temp_f0); + + eyeOffset.r = atOffset.r; + eyeOffset.pitch = atOffset.pitch; + eyeOffset.yaw = (atOffset.yaw * anim->yawDir) + camPlayerPosRot->rot.y; + anim->unk_0C += (4.0f / 45.0f); + break; + case 30: + camera->unk_14C |= 0x400; + if (camera->unk_14C & 8) { + camera->animState = 4; + } + case 10: + case 20: + skipUpdateEye = true; + break; + case 4: + eyeOffset.r = 80.0f; + eyeOffset.pitch = 0; + eyeOffset.yaw = eyeAtOffset.yaw; + anim->unk_0C = 0.1f; + sCameraInterfaceFlags = 0x3400; + + if (!((anim->animFrame < 0 || camera->xzSpeed > 0.001f || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CLEFT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CDOWN) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CUP) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CRIGHT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_R) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_Z)) && + camera->unk_14C & 8)) { + goto skipeyeUpdate; + } + + default: + camera->unk_14C |= 0x14; + camera->unk_14C &= ~8; + if (camera->prevCamDataIdx < 0) { + Camera_ChangeSettingFlags(camera, camera->prevSetting, 2); + } else { + Camera_ChangeDataIdx(camera, camera->prevCamDataIdx); + camera->prevCamDataIdx = -1; + } + sCameraInterfaceFlags = 0; + skipeyeUpdate: + skipUpdateEye = true; + break; + } + + anim->animFrame++; + + if (anim->animFrame == 1) { + camera->animState = 0xA; + } else if (anim->animFrame == 2) { + camera->animState = 0x1; + } else if (anim->animFrame == 0x94) { + camera->animState = 2; + } else if (anim->animFrame == 0x9E) { + camera->animState = 0x14; + } else if (anim->animFrame == 0x9F) { + camera->animState = 3; + } else if (anim->animFrame == 0xA8) { + camera->animState = 0x1E; + } else if (anim->animFrame == 0xE4) { + camera->animState = 4; + } + + if (!skipUpdateEye) { + eyeOffset.r = Camera_LERPCeilF(eyeOffset.r, eyeAtOffset.r, anim->unk_0C, 2.0f); + eyeOffset.pitch = Camera_LERPCeilS(eyeOffset.pitch, eyeAtOffset.pitch, anim->unk_0C, 0xA); + eyeOffset.yaw = Camera_LERPCeilS(eyeOffset.yaw, eyeAtOffset.yaw, anim->unk_0C, 0xA); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeOffset); + *eye = *eyeNext; + } + + camera->dist = OLib_Vec3fDist(at, eye); + camera->atLERPStepScale = 0.1f; + camera->posOffset.x = camera->at.x - camPlayerPosRot->pos.x; + camera->posOffset.y = camera->at.y - camPlayerPosRot->pos.y; + camera->posOffset.z = camera->at.z - camPlayerPosRot->pos.z; + return true; +} + +s32 Camera_Demo4(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Sets up a cutscene for Camera_Uniq9 + */ +s32 Camera_Demo5(Camera* camera) { + f32 eyeTargetDist; + f32 sp90; + VecSph playerTargetGeo; + VecSph eyePlayerGeo; + VecSph sp78; + PosRot playerhead; + PosRot targethead; + Player* player; + s16 sp4A; + s32 pad; + s32 temp_v0; + s16 t; + s32 pad2; + + Actor_GetFocus(&playerhead, &camera->player->actor); + player = camera->player; + sCameraInterfaceFlags = 0x3200; + if ((camera->target == NULL) || (camera->target->update == NULL)) { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: attention: target is not valid, stop!\n" VT_RST); + } + camera->target = NULL; + return true; + } + Actor_GetFocus(&camera->targetPosRot, camera->target); + OLib_Vec3fDiffToVecSphGeo(&playerTargetGeo, &camera->targetPosRot.pos, &camera->playerPosRot.pos); + D_8011D3AC = camera->target->category; + Actor_GetScreenPos(camera->globalCtx, camera->target, &sp78.yaw, &sp78.pitch); + eyeTargetDist = OLib_Vec3fDist(&camera->targetPosRot.pos, &camera->eye); + OLib_Vec3fDiffToVecSphGeo(&eyePlayerGeo, &playerhead.pos, &camera->eyeNext); + sp4A = eyePlayerGeo.yaw - playerTargetGeo.yaw; + if (camera->target->category == ACTORCAT_PLAYER) { + // camera is targeting a(the) player actor + if (eyePlayerGeo.r > 30.0f) { + D_8011D6AC[1].timerInit = camera->timer - 1; + D_8011D6AC[1].atTargetInit.z = Rand_ZeroOne() * 10.0f; + D_8011D6AC[1].eyeTargetInit.x = Rand_ZeroOne() * 10.0f; + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D6AC; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D6AC); + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt--; + } else { + camera->timer += D_8011D6AC[2].timerInit; + } + } else { + D_8011D724[1].eyeTargetInit.x = Rand_ZeroOne() * 10.0f; + D_8011D724[1].timerInit = camera->timer - 1; + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D724; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D724); + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt--; + } else { + camera->timer += D_8011D724[2].timerInit; + } + } + } else if (playerTargetGeo.r < 30.0f) { + // distance between player and target is less than 30 units. + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D79C; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D79C); + if ((sp78.yaw < 0x15) || (sp78.yaw >= 0x12C) || (sp78.pitch < 0x29) || (sp78.pitch >= 0xC8)) { + D_8011D79C[0].actionFlags = 0x41; + D_8011D79C[0].atTargetInit.y = -30.0f; + D_8011D79C[0].atTargetInit.x = 0.0f; + D_8011D79C[0].atTargetInit.z = 0.0f; + D_8011D79C[0].eyeTargetInit.y = 0.0f; + D_8011D79C[0].eyeTargetInit.x = 10.0f; + D_8011D79C[0].eyeTargetInit.z = -50.0f; + } + + D_8011D79C[1].timerInit = camera->timer - 1; + + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt -= 2; + } else { + camera->timer += D_8011D79C[2].timerInit + D_8011D79C[3].timerInit; + } + } else if (eyeTargetDist < 300.0f && eyePlayerGeo.r < 30.0f) { + // distance from the camera's current positon and the target is less than 300 units + // and the distance fromthe camera's current position to the player is less than 30 units + D_8011D83C[0].timerInit = camera->timer; + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D83C; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D83C); + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt--; + } else { + camera->timer += D_8011D83C[1].timerInit; + } + } else if (eyeTargetDist < 700.0f && ABS(sp4A) < 0x36B0) { + // The distance between the camera's current position and the target is less than 700 units + // and the angle between the camera's position and the player, and the player to the target + // is less than ~76.9 degrees + if (sp78.yaw >= 0x15 && sp78.yaw < 0x12C && sp78.pitch >= 0x29 && sp78.pitch < 0xC8 && eyePlayerGeo.r > 30.0f) { + D_8011D88C[0].timerInit = camera->timer; + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D88C; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D88C); + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt--; + } else { + camera->timer += D_8011D88C[1].timerInit; + } + } else { + D_8011D8DC[0].atTargetInit.z = eyeTargetDist * 0.6f; + D_8011D8DC[0].eyeTargetInit.z = eyeTargetDist + 50.0f; + D_8011D8DC[0].eyeTargetInit.x = Rand_ZeroOne() * 10.0f; + if (BINANG_SUB(eyePlayerGeo.yaw, playerTargetGeo.yaw) > 0) { + D_8011D8DC[0].atTargetInit.x = -D_8011D8DC[0].atTargetInit.x; + D_8011D8DC[0].eyeTargetInit.x = -D_8011D8DC[0].eyeTargetInit.x; + D_8011D8DC[0].rollTargetInit = -D_8011D8DC[0].rollTargetInit; + } + D_8011D8DC[0].timerInit = camera->timer; + D_8011D8DC[1].timerInit = (s16)(eyeTargetDist * 0.005f) + 8; + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D8DC; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D8DC); + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt -= 2; + } else { + camera->timer += D_8011D8DC[1].timerInit + D_8011D8DC[2].timerInit; + } + } + } else if (camera->target->category == ACTORCAT_DOOR) { + // the target is a door. + D_8011D954[0].timerInit = camera->timer - 5; + sp4A = 0; + if (!func_800C0D34(camera->globalCtx, camera->target, &sp4A)) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: attention demo: this door is dummy door!\n" VT_RST); + if (ABS(playerTargetGeo.yaw - camera->target->shape.rot.y) >= 0x4000) { + sp4A = camera->target->shape.rot.y; + } else { + sp4A = BINANG_ROT180(camera->target->shape.rot.y); + } + } + + D_8011D954[0].atTargetInit.y = D_8011D954[0].eyeTargetInit.y = D_8011D954[1].atTargetInit.y = + camera->target->shape.rot.y == sp4A ? 180.0f : 0.0f; + sp90 = (BINANG_SUB(playerTargetGeo.yaw, sp4A) < 0 ? 20.0f : -20.0f) * Rand_ZeroOne(); + D_8011D954[0].eyeTargetInit.y = D_8011D954->eyeTargetInit.y + sp90; + temp_v0 = Rand_ZeroOne() * (sp90 * -0.2f); + D_8011D954[1].rollTargetInit = temp_v0; + D_8011D954[0].rollTargetInit = temp_v0; + Actor_GetFocus(&targethead, camera->target); + targethead.pos.x += 50.0f * Math_SinS(BINANG_ROT180(sp4A)); + targethead.pos.z += 50.0f * Math_CosS(BINANG_ROT180(sp4A)); + if (Camera_BGCheck(camera, &playerhead.pos, &targethead.pos)) { + D_8011D954[1].actionFlags = 0xC1; + D_8011D954[2].actionFlags = 0x8F; + } else { + D_8011D954[2].timerInit = (s16)(eyeTargetDist * 0.004f) + 6; + } + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D954; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D954); + if (camera->parentCamIdx != MAIN_CAM) { + ONEPOINT_CS_INFO(camera)->keyFrameCnt -= 2; + } else { + camera->timer += D_8011D954[2].timerInit + D_8011D954[3].timerInit; + } + } else { + if (playerTargetGeo.r < 200.0f) { + D_8011D9F4[0].eyeTargetInit.z = playerTargetGeo.r; + D_8011D9F4[0].atTargetInit.z = playerTargetGeo.r * 0.25f; + } + if (playerTargetGeo.r < 400.0f) { + D_8011D9F4[0].eyeTargetInit.x = Rand_ZeroOne() * 25.0f; + } + Player_GetHeight(camera->player); + D_8011D9F4[0].timerInit = camera->timer; + Actor_GetFocus(&targethead, camera->target); + if (Camera_BGCheck(camera, &playerhead.pos, &targethead.pos)) { + D_8011D9F4[1].timerInit = 4; + D_8011D9F4[1].actionFlags = 0x8F; + } else { + t = eyeTargetDist * 0.005f; + D_8011D9F4[1].timerInit = t + 8; + } + ONEPOINT_CS_INFO(camera)->keyFrames = D_8011D9F4; + ONEPOINT_CS_INFO(camera)->keyFrameCnt = ARRAY_COUNT(D_8011D9F4); + if (camera->parentCamIdx != MAIN_CAM) { + if (camera->globalCtx->state.frames & 1) { + D_8011D9F4[0].rollTargetInit = -D_8011D9F4[0].rollTargetInit; + D_8011D9F4[1].rollTargetInit = -D_8011D9F4[1].rollTargetInit; + } + ONEPOINT_CS_INFO(camera)->keyFrameCnt -= 2; + } else { + camera->timer += D_8011D9F4[1].timerInit + D_8011D9F4[2].timerInit; + D_8011D9F4[0].rollTargetInit = D_8011D9F4[1].rollTargetInit = 0; + } + } + + pad = sDemo5PrevSfxFrame - camera->globalCtx->state.frames; + if ((pad >= 0x33) || (pad < -0x32)) { + func_80078884(camera->data1); + } + + sDemo5PrevSfxFrame = camera->globalCtx->state.frames; + + if (camera->player->stateFlags1 & 0x8000000 && (player->currentBoots != PLAYER_BOOTS_IRON)) { + // swimming, and not iron boots + player->stateFlags1 |= 0x20000000; + // env frozen + player->actor.freezeTimer = camera->timer; + } else { + sp4A = playerhead.rot.y - playerTargetGeo.yaw; + if (camera->target->category == ACTORCAT_PLAYER) { + pad = camera->globalCtx->state.frames - sDemo5PrevAction12Frame; + if (player->stateFlags1 & 0x800) { + // holding object over head. + func_8002DF54(camera->globalCtx, camera->target, 8); + } else if (ABS(pad) > 3000) { + func_8002DF54(camera->globalCtx, camera->target, 12); + } else { + func_8002DF54(camera->globalCtx, camera->target, 69); + } + } else { + func_8002DF54(camera->globalCtx, camera->target, 1); + } + } + + sDemo5PrevAction12Frame = camera->globalCtx->state.frames; + Camera_ChangeSettingFlags(camera, CAM_SET_CS_C, (4 | 1)); + Camera_Unique9(camera); + return true; +} + +/** + * Used in Forest Temple when poes are defeated, follows the flames to the torches. + * Fixed position, rotates to follow the target + */ +s32 Camera_Demo6(Camera* camera) { + Camera* mainCam; + Demo6Anim* anim = (Demo6Anim*)&((Demo6*)camera->paramData)->anim; + Vec3f* eyeNext = &camera->eyeNext; + CameraModeValue* values; + VecSph eyeOffset; + Actor* camFocus; + PosRot focusPosRot; + s16 stateTimers[4]; + Vec3f* at = &camera->at; + + mainCam = Gameplay_GetCamera(camera->globalCtx, MAIN_CAM); + camFocus = camera->target; + stateTimers[1] = 0x37; + stateTimers[2] = 0x46; + stateTimers[3] = 0x5A; + + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + ((Demo6*)camera->paramData)->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + switch (camera->animState) { + case 0: + // initalizes the camera state. + anim->animTimer = 0; + camera->fov = 60.0f; + Actor_GetWorld(&focusPosRot, camFocus); + camera->at.x = focusPosRot.pos.x; + camera->at.y = focusPosRot.pos.y + 20.0f; + camera->at.z = focusPosRot.pos.z; + eyeOffset.r = 200.0f; + // 0x7D0 ~10.99 degrees + eyeOffset.yaw = Camera_XZAngle(&focusPosRot.pos, &mainCam->playerPosRot.pos) + 0x7D0; + // -0x3E8 ~5.49 degrees + eyeOffset.pitch = -0x3E8; + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeOffset); + camera->eye = *eyeNext; + camera->animState++; + case 1: + if (stateTimers[camera->animState] < anim->animTimer) { + func_8002DF54(camera->globalCtx, &camera->player->actor, 8); + Actor_GetWorld(&focusPosRot, camFocus); + anim->atTarget.x = focusPosRot.pos.x; + anim->atTarget.y = focusPosRot.pos.y - 20.0f; + anim->atTarget.z = focusPosRot.pos.z; + camera->animState++; + } else { + break; + } + case 2: + Camera_LERPCeilVec3f(&anim->atTarget, at, 0.1f, 0.1f, 8.0f); + if (stateTimers[camera->animState] < anim->animTimer) { + camera->animState++; + } else { + break; + } + case 3: + camera->fov = Camera_LERPCeilF(50.0f, camera->fov, 0.2f, 0.01f); + if (stateTimers[camera->animState] < anim->animTimer) { + camera->timer = 0; + return true; + } + break; + } + + anim->animTimer++; + Actor_GetWorld(&focusPosRot, camFocus); + + return true; +} + +s32 Camera_Demo7(Camera* camera) { + if (camera->animState == 0) { + camera->unk_14C &= ~4; + camera->unk_14C |= 0x1000; + camera->animState++; + } + //! @bug doesn't return +} + +s32 Camera_Demo8(Camera* camera) { + return Camera_Noop(camera); +} + +/** + * Camera follows points specified by demo9.atPoints and demo9.eyePoints, allows finer control + * over the final eye and at points than Camera_Demo1, by allowing the interpolated at and eye points + * to be relative to the main camera's player, the current camera's player, or the main camera's target + */ +s32 Camera_Demo9(Camera* camera) { + s32 pad; + s32 finishAction; + s16 onePointTimer; + Demo9OnePointCs* demo9OnePoint = (Demo9OnePointCs*)camera->paramData; + Vec3f csEyeUpdate; + Vec3f csAtUpdate; + Vec3f newEye; + Vec3f newAt; + f32 newRoll; + CameraModeValue* values; + Camera* mainCam; + Vec3f* eye = &camera->eye; + PosRot* mainCamPlayerPosRot; + PosRot focusPosRot; + s32 pad3; + Vec3f* eyeNext = &camera->eyeNext; + Demo9* demo9 = &demo9OnePoint->demo9; + Vec3f* at = &camera->at; + f32* camFOV = &camera->fov; + Demo9Anim* anim = &demo9->anim; + + mainCam = Gameplay_GetCamera(camera->globalCtx, MAIN_CAM); + mainCamPlayerPosRot = &mainCam->playerPosRot; + if (RELOAD_PARAMS) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + demo9->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = demo9->interfaceFlags; + + switch (camera->animState) { + case 0: + // initalize the camera state + anim->keyframe = 0; + anim->finishAction = 0; + anim->curFrame = 0.0f; + camera->animState++; + anim->doLERPAt = false; + finishAction = demo9OnePoint->onePointCs.actionParameters & 0xF000; + if (finishAction != 0) { + anim->finishAction = finishAction; + + // Clear finish parameters + demo9OnePoint->onePointCs.actionParameters &= 0xFFF; + } + anim->animTimer = demo9OnePoint->onePointCs.initTimer; + case 1: + // Run the camera state + if (anim->animTimer > 0) { + // if the animation timer is still running, run the demo logic + // if it is not, then the case will fallthrough to the finish logic. + + // Run the at and eye cs interpoloation functions, if either of them return 1 (that no more points + // exist) change the animation state to 2 (standby) + if (func_800BB2B4(&csEyeUpdate, &newRoll, camFOV, demo9OnePoint->onePointCs.eyePoints, &anim->keyframe, + &anim->curFrame) != 0 || + func_800BB2B4(&csAtUpdate, &newRoll, camFOV, demo9OnePoint->onePointCs.atPoints, &anim->keyframe, + &anim->curFrame) != 0) { + camera->animState = 2; + } + + if (demo9OnePoint->onePointCs.actionParameters == 1) { + // rotate around mainCam's player + Camera_RotateAroundPoint(mainCamPlayerPosRot, &csEyeUpdate, &newEye); + Camera_RotateAroundPoint(mainCamPlayerPosRot, &csAtUpdate, &newAt); + } else if (demo9OnePoint->onePointCs.actionParameters == 4) { + // rotate around the current camera's player + Actor_GetWorld(&focusPosRot, &camera->player->actor); + Camera_RotateAroundPoint(&focusPosRot, &csEyeUpdate, &newEye); + Camera_RotateAroundPoint(&focusPosRot, &csAtUpdate, &newAt); + } else if (demo9OnePoint->onePointCs.actionParameters == 8) { + // rotate around the current camera's target + if (camera->target != NULL && camera->target->update != NULL) { + Actor_GetWorld(&focusPosRot, camera->target); + Camera_RotateAroundPoint(&focusPosRot, &csEyeUpdate, &newEye); + Camera_RotateAroundPoint(&focusPosRot, &csAtUpdate, &newAt); + } else { + camera->target = NULL; + newEye = *eye; + newAt = *at; + } + } else { + // simple copy + Camera_Vec3fCopy(&csEyeUpdate, &newEye); + Camera_Vec3fCopy(&csAtUpdate, &newAt); + } + + *eyeNext = newEye; + *eye = *eyeNext; + if (anim->doLERPAt) { + Camera_LERPCeilVec3f(&newAt, at, 0.5f, 0.5f, 0.1f); + } else { + *at = newAt; + anim->doLERPAt = true; + } + camera->roll = newRoll * 256.0f; + anim->animTimer--; + break; + } + case 3: + // the cs is finished, decide the next action + camera->timer = 0; + if (anim->finishAction != 0) { + if (anim->finishAction != 0x1000) { + if (anim->finishAction == 0x2000) { + // finish action = 0x2000, run OnePointCs 0x3FC (Dramatic Return to Link) + onePointTimer = + demo9OnePoint->onePointCs.initTimer < 50 ? 5 : demo9OnePoint->onePointCs.initTimer / 5; + OnePointCutscene_Init(camera->globalCtx, 1020, onePointTimer, NULL, camera->parentCamIdx); + } + } else { + // finish action = 0x1000, copy the current camera's values to the + // default camera. + Camera_Copy(mainCam, camera); + } + } + break; + case 2: + // standby while the timer finishes, change the animState to finish when + // the timer runs out. + anim->animTimer--; + if (anim->animTimer < 0) { + camera->animState++; + } + break; + case 4: + // do nothing. + break; + } + + return true; +} + +s32 Camera_Demo0(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Special0(Camera* camera) { + PosRot* playerPosRot = &camera->playerPosRot; + Special0* spec0 = (Special0*)camera->paramData; + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + spec0->lerpAtScale = NEXTPCT; + spec0->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + sCameraInterfaceFlags = spec0->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + } + + if ((camera->target == NULL) || (camera->target->update == NULL)) { + if (camera->target == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: warning: circle: target is not valid, stop!\n" VT_RST); + } + camera->target = NULL; + return true; + } + + Actor_GetFocus(&camera->targetPosRot, camera->target); + Camera_LERPCeilVec3f(&camera->targetPosRot.pos, &camera->at, spec0->lerpAtScale, spec0->lerpAtScale, 0.1f); + + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + + camera->dist = OLib_Vec3fDist(&camera->at, &camera->eye); + camera->xzSpeed = 0.0f; + if (camera->timer > 0) { + camera->timer--; + } + return true; +} + +s32 Camera_Special1(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Special2(Camera* camera) { + return Camera_Unique2(camera); +} + +s32 Camera_Special3(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Special4(Camera* camera) { + PosRot curTargetPosRot; + s16 sp3A; + s16* timer = &camera->timer; + Special4* spec4 = (Special4*)camera->paramData; + + if (camera->animState == 0) { + sCameraInterfaceFlags = 0x3200; + camera->fov = 40.0f; + camera->animState++; + spec4->initalTimer = camera->timer; + } + + camera->fov = Camera_LERPCeilF(80.0f, camera->fov, 1.0f / *timer, 0.1f); + if ((spec4->initalTimer - *timer) < 0xF) { + (*timer)--; + return false; + } else { + camera->roll = -0x1F4; + Actor_GetWorld(&curTargetPosRot, camera->target); + + camera->at = curTargetPosRot.pos; + camera->at.y -= 150.0f; + + // 0x3E8 ~ 5.49 degrees + sp3A = BINANG_ROT180(curTargetPosRot.rot.y) + 0x3E8; + camera->eye.x = camera->eyeNext.x = (Math_SinS(sp3A) * 780.0f) + camera->at.x; + camera->eyeNext.y = camera->at.y; + camera->eye.z = camera->eyeNext.z = (Math_CosS(sp3A) * 780.0f) + camera->at.z; + camera->eye.y = curTargetPosRot.pos.y; + camera->eye.y = Camera_GetFloorY(camera, &camera->eye) + 20.0f; + (*timer)--; + return true; + } +} + +/** + * Flying with hookshot + */ +s32 Camera_Special5(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + PosRot spA8; + s16 pad; + s16 spA4; + CamColChk sp7C; + VecSph sp74; + VecSph sp6C; + VecSph sp64; + VecSph sp5C; + PosRot* playerPosRot = &camera->playerPosRot; + Special5* spec5 = (Special5*)camera->paramData; + Special5Anim* anim = &spec5->anim; + f32 temp_f0_2; + f32 yOffset; + + yOffset = Player_GetHeight(camera->player); + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + f32 yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / yOffset)); + spec5->yOffset = (NEXTPCT * yOffset) * yNormal; + spec5->eyeDist = NEXTSETTING; + spec5->minDistForRot = NEXTSETTING; + spec5->timerInit = NEXTSETTING; + spec5->pitch = DEGF_TO_BINANG(NEXTSETTING); + spec5->fovTarget = NEXTSETTING; + spec5->atMaxLERPScale = NEXTPCT; + spec5->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&sp64, at, eye); + OLib_Vec3fDiffToVecSphGeo(&sp5C, at, eyeNext); + Actor_GetWorld(&spA8, camera->target); + + sCameraInterfaceFlags = spec5->interfaceFlags; + + if (camera->animState == 0) { + camera->animState++; + anim->animTimer = spec5->timerInit; + } + + if (anim->animTimer > 0) { + anim->animTimer--; + } else if (anim->animTimer == 0) { + if (camera->target == NULL || camera->target->update == NULL) { + camera->target = NULL; + return true; + } + + anim->animTimer--; + if (spec5->minDistForRot < OLib_Vec3fDist(&spA8.pos, &playerPosRot->pos)) { + sp6C.yaw = playerPosRot->rot.y; + sp6C.pitch = -playerPosRot->rot.x; + sp6C.r = 20.0f; + Camera_Vec3fVecSphGeoAdd(&sp7C.pos, &spA8.pos, &sp6C); + Camera_BGCheckInfo(camera, at, &sp7C); + OLib_Vec3fToVecSphGeo(&sp6C, &sp7C.norm); + spA4 = BINANG_SUB(playerPosRot->rot.y, sp6C.yaw); + sp74.r = spec5->eyeDist; + temp_f0_2 = Rand_ZeroOne(); + sp74.yaw = + BINANG_ROT180(playerPosRot->rot.y) + (s16)(spA4 < 0 ? -(s16)(0x1553 + (s16)(temp_f0_2 * 2730.0f)) + : (s16)(0x1553 + (s16)(temp_f0_2 * 2730.0f))); + sp74.pitch = spec5->pitch; + Camera_Vec3fVecSphGeoAdd(eyeNext, &spA8.pos, &sp74); + *eye = *eyeNext; + Camera_BGCheck(camera, &spA8.pos, eye); + } + } + + Camera_CalcAtDefault(camera, &sp5C, spec5->yOffset, 0); + camera->fov = Camera_LERPCeilF(spec5->fovTarget, camera->fov, camera->atLERPStepScale * PCT(OREG(4)), 1.0f); + camera->roll = Camera_LERPCeilS(0, camera->roll, 0.5f, 0xA); + camera->atLERPStepScale = Camera_ClampLERPScale(camera, spec5->atMaxLERPScale); + return true; +} + +/** + * Camera's eye is fixed at points specified at D_8011DA6C / D_8011DA9C + * depending on the player's position + */ +s32 Camera_Special7(Camera* camera) { + Special7* spec7 = (Special7*)camera->paramData; + PosRot* playerPosRot = &camera->playerPosRot; + Vec3f atTarget; + f32 yOffset; + f32 temp_f0; + + yOffset = Player_GetHeight(camera->player); + if (camera->animState == 0) { + if (camera->globalCtx->sceneNum == SCENE_JYASINZOU) { + // Spirit Temple + spec7->idx = 3; + } else if (playerPosRot->pos.x < 1500.0f) { + spec7->idx = 2; + } else if (playerPosRot->pos.y < 3000.0f) { + spec7->idx = 0; + } else { + spec7->idx = 1; + } + camera->animState++; + camera->roll = 0; + } + + if (camera->at.y < D_8011DACC[spec7->idx]) { + atTarget = playerPosRot->pos; + atTarget.y -= 20.0f; + Camera_LERPCeilVec3f(&atTarget, &camera->at, 0.4f, 0.4f, 0.10f); + camera->eye = camera->eyeNext = D_8011DA6C[spec7->idx]; + temp_f0 = (playerPosRot->pos.y - D_8011DADC[spec7->idx]) / (D_8011DACC[spec7->idx] - D_8011DADC[spec7->idx]); + camera->roll = D_8011DAEC[spec7->idx] * temp_f0; + camera->fov = (20.0f * temp_f0) + 60.0f; + } else { + atTarget = playerPosRot->pos; + atTarget.y += yOffset; + Camera_LERPCeilVec3f(&atTarget, &camera->at, 0.4f, 0.4f, 0.1f); + camera->roll = 0; + camera->eye = camera->eyeNext = D_8011DA9C[spec7->idx]; + camera->fov = 70.0f; + } + + camera->dist = OLib_Vec3fDist(&camera->at, &camera->eye); + camera->atLERPStepScale = 0.0f; + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + return true; +} + +/** + * Courtyard. + * Camera's eye is fixed on the z plane, slides on the xy plane with link + * When the camera's scene data changes the animation to the next "screen" + * happens for 12 frames. The camera's eyeNext is the scene's camera data's position + */ +s32 Camera_Special6(Camera* camera) { + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + VecSph atOffset; + Vec3f sceneCamPos; + Vec3f eyePosCalc; + Vec3f eyeAnim; + Vec3f atAnim; + VecSph eyeAtOffset; + PosRot* playerPosRot = &camera->playerPosRot; + Vec3s* sceneCamData; + Vec3s sceneCamRot; + s16 fov; + f32 sp54; + f32 timerF; + f32 timerDivisor; + Special6* spec6 = (Special6*)camera->paramData; + Special6Anim* anim = &spec6->anim; + s32 pad; + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + spec6->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + OLib_Vec3fDiffToVecSphGeo(&eyeAtOffset, eye, at); + + sceneCamData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(&sceneCamPos, &BGCAM_POS(sceneCamData)); + sceneCamRot = BGCAM_ROT(sceneCamData); + fov = BGCAM_FOV(sceneCamData); + if (fov == -1) { + fov = 6000; + } + + if (fov < 361) { + fov *= 100; + } + + sCameraInterfaceFlags = spec6->interfaceFlags; + + if (eyeNext->x != sceneCamPos.x || eyeNext->y != sceneCamPos.y || eyeNext->z != sceneCamPos.z || + camera->animState == 0) { + // A change in the current scene's camera positon has been detected, + // Change "screens" + camera->player->actor.freezeTimer = 12; + sCameraInterfaceFlags = (sCameraInterfaceFlags & 0xF0FF) | 0x300; + anim->initalPlayerY = playerPosRot->pos.y; + anim->animTimer = 12; + *eyeNext = sceneCamPos; + if (camera->animState == 0) { + camera->animState++; + } + } + + if (anim->animTimer > 0) { + // In transition between "screens" + timerF = anim->animTimer; + eyePosCalc = *eyeNext; + eyePosCalc.x += (playerPosRot->pos.x - eyePosCalc.x) * 0.5f; + eyePosCalc.y += (playerPosRot->pos.y - anim->initalPlayerY) * 0.2f; + eyeAnim = eyePosCalc; + eyeAnim.y = Camera_LERPCeilF(eyePosCalc.y, eye->y, 0.5f, 0.01f); + + // set the at point to be 100 units from the eye looking at the + // direction specified in the scene's camera data. + atOffset.r = 100.0f; + atOffset.yaw = sceneCamRot.y; + atOffset.pitch = -sceneCamRot.x; + Camera_Vec3fVecSphGeoAdd(&atAnim, &eyeAnim, &atOffset); + timerDivisor = 1.0f / timerF; + eye->x += (eyeAnim.x - eye->x) * timerDivisor; + eye->y += (eyeAnim.y - eye->y) * timerDivisor; + eye->z += (eyeAnim.z - eye->z) * timerDivisor; + at->x += (atAnim.x - at->x) * timerDivisor; + at->y += (atAnim.y - at->y) * timerDivisor; + at->z += (atAnim.z - at->z) * timerDivisor; + camera->fov += (PCT(fov) - camera->fov) / anim->animTimer; + anim->animTimer--; + } else { + // Camera following link on the x axis. + sCameraInterfaceFlags &= 0xF0FF; + eyePosCalc = *eyeNext; + eyePosCalc.x += (playerPosRot->pos.x - eyePosCalc.x) * 0.5f; + eyePosCalc.y += (playerPosRot->pos.y - anim->initalPlayerY) * 0.2f; + *eye = eyePosCalc; + eye->y = Camera_LERPCeilF(eyePosCalc.y, eye->y, 0.5f, 0.01f); + + // set the at point to be 100 units from the eye looking at the + // direction specified in the scene's camera data. + atOffset.r = 100.0f; + atOffset.yaw = sceneCamRot.y; + atOffset.pitch = -sceneCamRot.x; + Camera_Vec3fVecSphGeoAdd(at, eye, &atOffset); + } + return true; +} + +s32 Camera_Special8(Camera* camera) { + return Camera_Noop(camera); +} + +s32 Camera_Special9(Camera* camera) { + s32 pad; + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + Vec3f spAC; + VecSph eyeAdjustment; + VecSph atEyeOffsetGeo; + f32 playerYOffset; + s32 pad3; + PosRot* playerPosRot = &camera->playerPosRot; + PosRot adjustedPlayerPosRot; + f32 yNormal; + Special9* spec9 = (Special9*)camera->paramData; + Special9Params* params = &spec9->params; + Special9Anim* anim = ¶ms->anim; + s32 pad4; + Vec3s* camPosData; + + playerYOffset = Player_GetHeight(camera->player); + camera->unk_14C &= ~0x10; + yNormal = (1.0f + PCT(R_CAM_YOFFSET_NORM)) - (PCT(R_CAM_YOFFSET_NORM) * (68.0f / playerYOffset)); + + if (RELOAD_PARAMS) { + CameraModeValue* values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + + params->yOffset = NEXTPCT * playerYOffset * yNormal; + params->unk_04 = NEXTSETTING; + params->interfaceFlags = NEXTSETTING; + } + + if (R_RELOAD_CAM_PARAMS) { + Camera_CopyPREGToModeValues(camera); + } + + if (spec9->doorParams.doorActor != NULL) { + Actor_GetWorldPosShapeRot(&adjustedPlayerPosRot, spec9->doorParams.doorActor); + } else { + adjustedPlayerPosRot = *playerPosRot; + adjustedPlayerPosRot.pos.y += playerYOffset + params->yOffset; + adjustedPlayerPosRot.rot.x = 0; + } + + OLib_Vec3fDiffToVecSphGeo(&atEyeOffsetGeo, at, eye); + + sCameraInterfaceFlags = params->interfaceFlags; + + switch (camera->animState) { + if (1) {} + + case 0: + camera->unk_14C &= ~(0x4 | 0x2); + camera->animState++; + anim->targetYaw = ABS(playerPosRot->rot.y - adjustedPlayerPosRot.rot.y) >= 0x4000 + ? BINANG_ROT180(adjustedPlayerPosRot.rot.y) + : adjustedPlayerPosRot.rot.y; + case 1: + spec9->doorParams.timer1--; + if (spec9->doorParams.timer1 <= 0) { + camera->animState++; + if (params->interfaceFlags & 1) { + camPosData = Camera_GetCamBGData(camera); + Camera_Vec3sToVec3f(eyeNext, &BGCAM_POS(camPosData)); + spAC = *eye = *eyeNext; + } else { + s16 yaw; + + // 0xE38 ~ 20 degrees + eyeAdjustment.pitch = 0xE38; + // 0xAAA ~ 15 degrees. + yaw = 0xAAA * ((camera->globalCtx->state.frames & 1) ? 1 : -1); + eyeAdjustment.yaw = anim->targetYaw + yaw; + eyeAdjustment.r = 200.0f * yNormal; + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeAdjustment); + spAC = *eye = *eyeNext; + if (Camera_CheckOOB(camera, &spAC, &playerPosRot->pos)) { + yaw = -yaw; + eyeAdjustment.yaw = anim->targetYaw + yaw; + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeAdjustment); + *eye = *eyeNext; + } + } + } else { + break; + } + case 2: + spAC = playerPosRot->pos; + spAC.y += playerYOffset + params->yOffset; + + Camera_LERPCeilVec3f(&spAC, at, 0.25f, 0.25f, 0.1f); + spec9->doorParams.timer2--; + if (spec9->doorParams.timer2 <= 0) { + camera->animState++; + anim->targetYaw = BINANG_ROT180(anim->targetYaw); + } else { + break; + } + case 3: + spAC = playerPosRot->pos; + spAC.y += (playerYOffset + params->yOffset); + Camera_LERPCeilVec3f(&spAC, at, 0.5f, 0.5f, 0.1f); + eyeAdjustment.pitch = Camera_LERPCeilS(0xAAA, atEyeOffsetGeo.pitch, 0.3f, 0xA); + eyeAdjustment.yaw = Camera_LERPCeilS(anim->targetYaw, atEyeOffsetGeo.yaw, 0.3f, 0xA); + eyeAdjustment.r = Camera_LERPCeilF(60.0f, atEyeOffsetGeo.r, 0.3f, 1.0f); + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeAdjustment); + *eye = *eyeNext; + spec9->doorParams.timer3--; + if (spec9->doorParams.timer3 <= 0) { + camera->animState++; + } else { + break; + } + case 4: + camera->animState++; + default: + camera->unk_14C |= (0x400 | 0x10); + sCameraInterfaceFlags = 0; + + if (camera->xzSpeed > 0.001f || CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CLEFT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CDOWN) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CUP) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_CRIGHT) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_R) || + CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_Z) || params->interfaceFlags & 0x8) { + + Camera_ChangeSettingFlags(camera, camera->prevSetting, 2); + camera->unk_14C |= (0x4 | 0x2); + } + break; + } + if (1) {} + spAC = playerPosRot->pos; + spAC.y += playerYOffset; + camera->dist = OLib_Vec3fDist(&spAC, eye); + camera->posOffset.x = camera->at.x - playerPosRot->pos.x; + camera->posOffset.y = camera->at.y - playerPosRot->pos.y; + camera->posOffset.z = camera->at.z - playerPosRot->pos.z; + return true; +} + +Camera* Camera_Create(View* view, CollisionContext* colCtx, GlobalContext* globalCtx) { + Camera* newCamera = ZeldaArena_MallocDebug(sizeof(*newCamera), "../z_camera.c", 9370); + + if (newCamera != NULL) { + osSyncPrintf(VT_FGCOL(BLUE) "camera: create --- allocate %d byte" VT_RST "\n", sizeof(*newCamera) * 4); + Camera_Init(newCamera, view, colCtx, globalCtx); + } else { + osSyncPrintf(VT_COL(RED, WHITE) "camera: create: not enough memory\n" VT_RST); + } + return newCamera; +} + +void Camera_Destroy(Camera* camera) { + if (camera != NULL) { + osSyncPrintf(VT_FGCOL(BLUE) "camera: destroy ---" VT_RST "\n"); + ZeldaArena_FreeDebug(camera, "../z_camera.c", 9391); + } else { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: destroy: already cleared\n" VT_RST); + } +} + +void Camera_Init(Camera* camera, View* view, CollisionContext* colCtx, GlobalContext* globalCtx) { + Camera* camP; + s32 i; + s16 curUID; + s16 j; + + memset(camera, 0, sizeof(*camera)); + if (sInitRegs) { + for (i = 0; i < sOREGInitCnt; i++) { + OREG(i) = sOREGInit[i]; + } + + for (i = 0; i < sCamDataRegsInitCount; i++) { + R_CAM_DATA(i) = sCamDataRegsInit[i]; + } + + DbCamera_Reset(camera, &D_8015BD80); + sInitRegs = false; + PREG(88) = -1; + } + camera->globalCtx = D_8015BD7C = globalCtx; + DbCamera_Init(&D_8015BD80, camera); + curUID = sNextUID; + sNextUID++; + while (curUID != 0) { + if (curUID == 0) { + sNextUID++; + } + + for (j = 0; j < NUM_CAMS; j++) { + camP = camera->globalCtx->cameraPtrs[j]; + if (camP != NULL && curUID == camP->uid) { + break; + } + } + + if (j == 4) { + break; + } + + curUID = sNextUID++; + } + + // ~ 90 degrees + camera->inputDir.y = 0x3FFF; + camera->uid = curUID; + camera->camDir = camera->inputDir; + camera->rUpdateRateInv = 10.0f; + camera->yawUpdateRateInv = 10.0f; + camera->up.x = 0.0f; + camera->up.y = 1.0f; + camera->up.z = 0.0f; + camera->fov = 60.0f; + camera->pitchUpdateRateInv = R_CAM_DEFA_PHI_UPDRATE; + camera->xzOffsetUpdateRate = PCT(OREG(2)); + camera->yOffsetUpdateRate = PCT(OREG(3)); + camera->fovUpdateRate = PCT(OREG(4)); + sCameraShrinkWindowVal = 0x20; + sCameraInterfaceAlpha = 0; + camera->unk_14C = 0; + camera->setting = camera->prevSetting = CAM_SET_FREE0; + camera->camDataIdx = camera->prevCamDataIdx = -1; + camera->mode = 0; + camera->bgCheckId = BGCHECK_SCENE; + camera->csId = 0x7FFF; + camera->timer = -1; + camera->unk_14C |= 0x4000; + + camera->up.y = 1.0f; + camera->up.z = camera->up.x = 0.0f; + camera->skyboxOffset.x = camera->skyboxOffset.y = camera->skyboxOffset.z = 0; + camera->atLERPStepScale = 1; + sCameraInterfaceFlags = 0xFF00; + sDbgModeIdx = -1; + D_8011D3F0 = 3; + osSyncPrintf(VT_FGCOL(BLUE) "camera: initialize --- " VT_RST " UID %d\n", camera->uid); +} + +void func_80057FC4(Camera* camera) { + if (camera != &camera->globalCtx->mainCamera) { + camera->prevSetting = camera->setting = CAM_SET_FREE0; + camera->unk_14C &= ~0x4; + } else if (camera->globalCtx->roomCtx.curRoom.mesh->polygon.type != 1) { + switch (camera->globalCtx->roomCtx.curRoom.unk_03) { + case 1: + Camera_ChangeDoorCam(camera, NULL, -99, 0, 0, 18, 10); + camera->prevSetting = camera->setting = CAM_SET_DUNGEON0; + break; + case 0: + osSyncPrintf("camera: room type: default set field\n"); + Camera_ChangeDoorCam(camera, NULL, -99, 0, 0, 18, 10); + camera->prevSetting = camera->setting = CAM_SET_NORMAL0; + break; + default: + osSyncPrintf("camera: room type: default set etc (%d)\n", camera->globalCtx->roomCtx.curRoom.unk_03); + Camera_ChangeDoorCam(camera, NULL, -99, 0, 0, 18, 10); + camera->prevSetting = camera->setting = CAM_SET_NORMAL0; + camera->unk_14C |= 4; + break; + } + } else { + osSyncPrintf("camera: room type: prerender\n"); + camera->prevSetting = camera->setting = CAM_SET_FREE0; + camera->unk_14C &= ~0x4; + } +} + +void Camera_Stub80058140(Camera* camera) { +} + +void Camera_InitPlayerSettings(Camera* camera, Player* player) { + PosRot playerPosShape; + VecSph eyeNextAtOffset; + s32 bgId; + Vec3f floorPos; + s32 upXZ; + f32 playerYOffset; + Vec3f* eye = &camera->eye; + Vec3f* at = &camera->at; + Vec3f* eyeNext = &camera->eyeNext; + + Actor_GetWorldPosShapeRot(&playerPosShape, &player->actor); + playerYOffset = Player_GetHeight(player); + camera->player = player; + camera->playerPosRot = playerPosShape; + camera->dist = eyeNextAtOffset.r = 180.0f; + camera->inputDir.y = playerPosShape.rot.y; + eyeNextAtOffset.yaw = BINANG_ROT180(camera->inputDir.y); + camera->inputDir.x = eyeNextAtOffset.pitch = 0x71C; + camera->inputDir.z = 0; + camera->camDir = camera->inputDir; + camera->xzSpeed = 0.0f; + camera->playerPosDelta.y = 0.0f; + camera->at = playerPosShape.pos; + camera->at.y += playerYOffset; + + camera->posOffset.x = 0; + camera->posOffset.y = playerYOffset; + camera->posOffset.z = 0; + + Camera_Vec3fVecSphGeoAdd(eyeNext, at, &eyeNextAtOffset); + *eye = *eyeNext; + camera->roll = 0; + + upXZ = 0; + camera->up.z = upXZ; + camera->up.y = 1.0f; + camera->up.x = upXZ; + + if (Camera_GetFloorYNorm(camera, &floorPos, at, &bgId) != BGCHECK_Y_MIN) { + camera->bgCheckId = bgId; + } + + camera->waterPrevCamIdx = -1; + camera->waterPrevCamSetting = -1; + camera->unk_14C |= 4; + + if (camera == &camera->globalCtx->mainCamera) { + sCameraInterfaceFlags = 0xB200; + } else { + sCameraInterfaceFlags = 0; + } + + func_80057FC4(camera); + camera->unk_14A = 0; + camera->paramFlags = 0; + camera->nextCamDataIdx = -1; + camera->atLERPStepScale = 1.0f; + Camera_CopyDataToRegs(camera, camera->mode); + Camera_QRegInit(); + osSyncPrintf(VT_FGCOL(BLUE) "camera: personalize ---" VT_RST "\n"); + + if (camera->thisIdx == MAIN_CAM) { + Camera_CheckWater(camera); + } +} + +s16 Camera_ChangeStatus(Camera* camera, s16 status) { + CameraModeValue* values; + CameraModeValue* valueP; + s32 i; + + if (PREG(82)) { + osSyncPrintf("camera: change camera status: cond %c%c\n", status == 7 ? 'o' : 'x', + camera->status != 7 ? 'o' : 'x'); + } + + if (PREG(82)) { + osSyncPrintf("camera: res: stat (%d/%d/%d)\n", camera->thisIdx, camera->setting, camera->mode); + } + + if (status == CAM_STAT_ACTIVE && camera->status != CAM_STAT_ACTIVE) { + values = sCameraSettings[camera->setting].cameraModes[camera->mode].values; + for (i = 0; i < sCameraSettings[camera->setting].cameraModes[camera->mode].valueCnt; i++) { + valueP = &values[i]; + R_CAM_DATA(valueP->dataType) = valueP->val; + if (PREG(82)) { + osSyncPrintf("camera: change camera status: PREG(%02d) = %d\n", valueP->dataType, valueP->val); + } + } + } + camera->status = status; + return camera->status; +} + +void Camera_PrintSettings(Camera* camera) { + char sp58[8]; + char sp50[8]; + char sp48[8]; + s32 i; + + if ((OREG(0) & 1) && (camera->globalCtx->activeCamera == camera->thisIdx) && !gDbgCamEnabled) { + for (i = 0; i < NUM_CAMS; i++) { + if (camera->globalCtx->cameraPtrs[i] == NULL) { + sp58[i] = '-'; + sp48[i] = ' '; + } else { + switch (camera->globalCtx->cameraPtrs[i]->status) { + case 0: + sp58[i] = 'c'; + break; + case 1: + sp58[i] = 'w'; + break; + case 3: + sp58[i] = 's'; + break; + case 7: + sp58[i] = 'a'; + break; + case 0x100: + sp58[i] = 'd'; + break; + default: + sp58[i] = '*'; + break; + } + } + sp48[i] = ' '; + } + sp58[i] = '\0'; + sp48[i] = '\0'; + + sp48[camera->globalCtx->activeCamera] = 'a'; + func_8006376C(3, 0x16, 5, sp58); + func_8006376C(3, 0x16, 1, sp48); + func_8006376C(3, 0x17, 5, "S:"); + func_8006376C(5, 0x17, 4, sCameraSettingNames[camera->setting]); + func_8006376C(3, 0x18, 5, "M:"); + func_8006376C(5, 0x18, 4, sCameraModeNames[camera->mode]); + func_8006376C(3, 0x19, 5, "F:"); + func_8006376C(5, 0x19, 4, + sCameraFunctionNames[sCameraSettings[camera->setting].cameraModes[camera->mode].funcIdx]); + + i = 0; + if (camera->camDataIdx < 0) { + sp50[i++] = '-'; + } + + //! @bug: this code was clearly meaning to print `abs(camera->camDataIdx)` as a + //! one-or-two-digit number, instead of `i`. + // "sp50[i++] = ..." matches here, but is undefined behavior due to conflicting + // reads/writes between sequence points, triggering warnings. Work around by + // putting i++ afterwards while on the same line. + // clang-format off + if (camera->camDataIdx / 10 != 0) { + sp50[i] = i / 10 + '0'; i++; + } + sp50[i] = i % 10 + '0'; i++; + // clang-format on + + sp50[i++] = ' '; + sp50[i++] = ' '; + sp50[i++] = ' '; + sp50[i++] = ' '; + sp50[i] = '\0'; + func_8006376C(3, 26, 5, "I:"); + func_8006376C(5, 26, 4, sp50); + } +} + +s32 Camera_CheckWater(Camera* camera) { + f32 waterY; + s16 newQuakeId; + s32 waterLightsIndex; + s32* waterPrevCamSetting = &camera->waterPrevCamSetting; + s16 waterCamIdx; + s16* quakeId = (s16*)&camera->waterQuakeId; + Player* player = camera->player; + s16 prevBgId; + + if (!(camera->unk_14C & 2) || sCameraSettings[camera->setting].unk_00 & 0x40000000) { + return 0; + } + + if (camera->unk_14C & 0x200) { + if (player->stateFlags2 & 0x800) { + Camera_ChangeSettingFlags(camera, CAM_SET_PIVOT_WATER_SURFACE, 6); + camera->unk_14C |= (s16)0x8000; + } else if (camera->unk_14C & (s16)0x8000) { + Camera_ChangeSettingFlags(camera, *waterPrevCamSetting, 6); + camera->unk_14C &= ~((s16)0x8000); + } + } + if (!(camera->unk_14C & (s16)0x8000)) { + if (waterCamIdx = Camera_GetWaterBoxDataIdx(camera, &waterY), waterCamIdx == -2) { + // No camera data idx + if (!(camera->unk_14C & 0x200)) { + camera->unk_14C |= 0x200; + camera->waterYPos = waterY; + camera->waterPrevCamIdx = camera->camDataIdx; + *quakeId = -1; + } + if (camera->playerGroundY != camera->playerPosRot.pos.y) { + prevBgId = camera->bgCheckId; + camera->bgCheckId = BGCHECK_SCENE; + Camera_ChangeSettingFlags(camera, CAM_SET_NORMAL3, 2); + *waterPrevCamSetting = camera->setting; + camera->bgCheckId = prevBgId; + camera->camDataIdx = -2; + } + } else if (waterCamIdx != -1) { + // player is in a water box + if (!(camera->unk_14C & 0x200)) { + camera->unk_14C |= 0x200; + camera->waterYPos = waterY; + camera->waterPrevCamIdx = camera->camDataIdx; + *quakeId = -1; + } + if (camera->playerGroundY != camera->playerPosRot.pos.y) { + prevBgId = camera->bgCheckId; + camera->bgCheckId = BGCHECK_SCENE; + Camera_ChangeDataIdx(camera, waterCamIdx); + *waterPrevCamSetting = camera->setting; + camera->bgCheckId = prevBgId; + } + } else if (camera->unk_14C & 0x200) { + // player is out of a water box. + osSyncPrintf("camera: water: off\n"); + camera->unk_14C &= ~0x200; + prevBgId = camera->bgCheckId; + camera->bgCheckId = BGCHECK_SCENE; + if (camera->waterPrevCamIdx < 0) { + func_80057FC4(camera); + camera->camDataIdx = -1; + } else { + Camera_ChangeDataIdx(camera, camera->waterPrevCamIdx); + } + camera->bgCheckId = prevBgId; + } + } + + if (waterY = Camera_GetWaterSurface(camera, &camera->eye, &waterLightsIndex), waterY != BGCHECK_Y_MIN) { + camera->waterYPos = waterY; + if (!(camera->unk_14C & 0x100)) { + camera->unk_14C |= 0x100; + osSyncPrintf("kankyo changed water, sound on\n"); + Environment_EnableUnderwaterLights(camera->globalCtx, waterLightsIndex); + camera->unk_150 = 0x50; + } + + Audio_SetExtraFilter(0x20); + + if (PREG(81)) { + Quake_RemoveFromIdx(*quakeId); + *quakeId = -1; + PREG(81) = 0; + } + + if ((*quakeId == -1) || (Quake_GetCountdown(*quakeId) == 0xA)) { + if (*quakeId = newQuakeId = Quake_Add(camera, 5U), newQuakeId != 0) { + Quake_SetSpeed(*quakeId, 550); + Quake_SetQuakeValues(*quakeId, 1, 1, 180, 0); + Quake_SetCountdown(*quakeId, 1000); + } + } + + if (camera->unk_150 > 0) { + camera->unk_150--; + camera->unk_152 |= 8; + } else if (camera->globalCtx->sceneNum == 0x49) { + camera->unk_152 |= 0x10; + } else { + camera->unk_152 |= 2; + } + } else { + if (camera->unk_14C & 0x100) { + camera->unk_14C &= ~0x100; + osSyncPrintf("kankyo changed water off, sound off\n"); + Environment_DisableUnderwaterLights(camera->globalCtx); + if (*quakeId != 0) { + Quake_RemoveFromIdx(*quakeId); + } + camera->unk_150 = 0; + camera->unk_152 = 0; + } + Audio_SetExtraFilter(0); + } + //! @bug: doesn't always return a value, but sometimes does. +} + +/** + * Sets the room to be hot camera quake flag + */ +s32 Camera_SetRoomHotFlag(Camera* camera) { + camera->unk_152 &= ~1; + if (camera->globalCtx->roomCtx.curRoom.unk_02 == 3) { + camera->unk_152 |= 1; + } + + return 1; +} + +s32 Camera_DbgChangeMode(Camera* camera) { + s32 changeDir = 0; + + if (!gDbgCamEnabled && camera->globalCtx->activeCamera == MAIN_CAM) { + if (CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_CUP)) { + osSyncPrintf("attention sound URGENCY\n"); + func_80078884(NA_SE_SY_ATTENTION_URGENCY); + } + if (CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_CDOWN)) { + osSyncPrintf("attention sound NORMAL\n"); + func_80078884(NA_SE_SY_ATTENTION_ON); + } + + if (CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_CRIGHT)) { + changeDir = 1; + } + if (CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_CLEFT)) { + changeDir = -1; + } + if (changeDir != 0) { + sDbgModeIdx = (sDbgModeIdx + changeDir) % 6; + if (Camera_ChangeSetting(camera, D_8011DAFC[sDbgModeIdx]) > 0) { + osSyncPrintf("camera: force change SET to %s!\n", sCameraSettingNames[D_8011DAFC[sDbgModeIdx]]); + } + } + } + return true; +} + +void func_80058E8C(Camera* camera) { + static s16 D_8011DB08 = 0x3F0; + static s16 D_8011DB0C = 0x156; + s32 pad3; + f32 sp60; + s32 pad; + s32 pad1; + s32 pad4; + f32 phi_f2; + s32 pad2; + f32 phi_f0; + f32 phi_f20; + f32 sp40; + f32 sp3C; + f32 sp38; + f32 sp34; + + if (camera->unk_152 != 0) { + if (camera->unk_152 & 4) { + phi_f0 = 0.0f; + phi_f2 = 170.0f; + sp3C = 0.01f; + sp40 = -0.01f; + sp38 = 0.0f; + sp34 = 0.6f; + phi_f20 = camera->unk_150 / 60.0f; + sp60 = 1.0f; + } else if (camera->unk_152 & 8) { + phi_f0 = 248.0f; + phi_f2 = -90.0f; + sp38 = 0.2f; + sp34 = 0.2f; + sp40 = -0.3f; + sp3C = 0.3f; + phi_f20 = camera->unk_150 / 80.0f; + sp60 = 1.0f; + } else if (camera->unk_152 & 2) { + phi_f0 = 359.2f; + phi_f2 = -18.5f; + sp40 = 0.09f; + sp38 = 0.01f; + sp3C = 0.09f; + sp34 = 0.08f; + phi_f20 = + (((camera->waterYPos - camera->eye.y) > 150.0f ? 1.0f : (camera->waterYPos - camera->eye.y) / 150.0f) * + 0.45f) + + (camera->speedRatio * 0.45f); + sp60 = phi_f20; + } else if (camera->unk_152 & 1) { + // hot room flag + phi_f2 = 150.0f; + phi_f0 = 0.0f; + sp3C = 0.01f; + sp38 = 0.01f; + sp40 = -0.01f; + sp34 = 0.6f; + sp60 = 1.0f; + phi_f20 = 1.0f; + + } else { + return; + } + D_8011DB08 += DEGF_TO_BINANG(phi_f0); + D_8011DB0C += DEGF_TO_BINANG(phi_f2); + Math_CosS(D_8011DB08); + Math_SinS(D_8011DB08); + Math_SinS(D_8011DB0C); + func_800AA76C(&camera->globalCtx->view, 0.0f, 0.0f, 0.0f); + func_800AA78C(&camera->globalCtx->view, Math_SinS(D_8011DB0C) * (sp40 * phi_f20) + 1.0f, + Math_CosS(D_8011DB0C) * (sp3C * phi_f20) + 1.0f, Math_CosS(D_8011DB08) * (sp38 * phi_f20) + 1.0f); + func_800AA7AC(&camera->globalCtx->view, sp34 * sp60); + camera->unk_14C |= 0x40; + } else if (camera->unk_14C & 0x40) { + func_800AA814(&camera->globalCtx->view); + camera->unk_14C &= ~0x40; + } +} + +Vec3s Camera_Update(Camera* camera) { + static s32 sOOBTimer = 0; + Vec3f viewAt; + Vec3f viewEye; + Vec3f viewUp; + f32 viewFov; + Vec3f spAC; + s32 bgId; + f32 playerGroundY; + f32 playerXZSpeed; + VecSph eyeAtAngle; + s16 camDataIdx; + PosRot curPlayerPosRot; + QuakeCamCalc quake; + Player* player; + + player = camera->globalCtx->cameraPtrs[MAIN_CAM]->player; + + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: in %x\n", camera); + } + + if (camera->status == CAM_STAT_CUT) { + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: cut out %x\n", camera); + } + return camera->inputDir; + } + + sUpdateCameraDirection = false; + + if (camera->player != NULL) { + Actor_GetWorldPosShapeRot(&curPlayerPosRot, &camera->player->actor); + camera->xzSpeed = playerXZSpeed = OLib_Vec3fDistXZ(&curPlayerPosRot.pos, &camera->playerPosRot.pos); + + camera->speedRatio = OLib_ClampMaxDist(playerXZSpeed / (func_8002DCE4(camera->player) * PCT(OREG(8))), 1.0f); + camera->playerPosDelta.x = curPlayerPosRot.pos.x - camera->playerPosRot.pos.x; + camera->playerPosDelta.y = curPlayerPosRot.pos.y - camera->playerPosRot.pos.y; + camera->playerPosDelta.z = curPlayerPosRot.pos.z - camera->playerPosRot.pos.z; + spAC = curPlayerPosRot.pos; + spAC.y += Player_GetHeight(camera->player); + + playerGroundY = BgCheck_EntityRaycastFloor5(camera->globalCtx, &camera->globalCtx->colCtx, &playerFloorPoly, + &bgId, &camera->player->actor, &spAC); + if (playerGroundY != BGCHECK_Y_MIN) { + // player is above ground. + sOOBTimer = 0; + camera->floorNorm.x = COLPOLY_GET_NORMAL(playerFloorPoly->normal.x); + camera->floorNorm.y = COLPOLY_GET_NORMAL(playerFloorPoly->normal.y); + camera->floorNorm.z = COLPOLY_GET_NORMAL(playerFloorPoly->normal.z); + camera->bgCheckId = bgId; + camera->playerGroundY = playerGroundY; + } else { + // player is not above ground. + sOOBTimer++; + camera->floorNorm.x = 0.0; + camera->floorNorm.y = 1.0f; + camera->floorNorm.z = 0.0; + } + + camera->playerPosRot = curPlayerPosRot; + + if (sOOBTimer < 200) { + if (camera->status == CAM_STAT_ACTIVE) { + Camera_CheckWater(camera); + Camera_SetRoomHotFlag(camera); + } + + if (!(camera->unk_14C & 4)) { + camera->nextCamDataIdx = -1; + } + + if ((camera->unk_14C & 1) && (camera->unk_14C & 4) && (!(camera->unk_14C & 0x400)) && + (!(camera->unk_14C & 0x200) || (player->currentBoots == PLAYER_BOOTS_IRON)) && + (!(camera->unk_14C & (s16)0x8000)) && (playerGroundY != BGCHECK_Y_MIN)) { + camDataIdx = Camera_GetDataIdxForPoly(camera, &bgId, playerFloorPoly); + if (camDataIdx != -1) { + camera->nextBGCheckId = bgId; + if (bgId == BGCHECK_SCENE) { + camera->nextCamDataIdx = camDataIdx; + } + } + } + + if (camera->nextCamDataIdx != -1 && (fabsf(curPlayerPosRot.pos.y - playerGroundY) < 2.0f) && + (!(camera->unk_14C & 0x200) || (player->currentBoots == PLAYER_BOOTS_IRON))) { + camera->bgCheckId = camera->nextBGCheckId; + Camera_ChangeDataIdx(camera, camera->nextCamDataIdx); + camera->nextCamDataIdx = -1; + } + } + } + Camera_PrintSettings(camera); + Camera_DbgChangeMode(camera); + + if (camera->status == CAM_STAT_WAIT) { + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: wait out %x\n", camera); + } + return camera->inputDir; + } + + camera->unk_14A = 0; + camera->unk_14C &= ~(0x400 | 0x20); + camera->unk_14C |= 0x10; + + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: engine (%d %d %d) %04x \n", camera->setting, camera->mode, + sCameraSettings[camera->setting].cameraModes[camera->mode].funcIdx, camera->unk_14C); + } + + if (sOOBTimer < 200) { + sCameraFunctions[sCameraSettings[camera->setting].cameraModes[camera->mode].funcIdx](camera); + } else if (camera->player != NULL) { + OLib_Vec3fDiffToVecSphGeo(&eyeAtAngle, &camera->at, &camera->eye); + Camera_CalcAtDefault(camera, &eyeAtAngle, 0.0f, 0); + } + + if (camera->status == CAM_STAT_ACTIVE) { + if ((gSaveContext.gameMode != 0) && (gSaveContext.gameMode != 3)) { + sCameraInterfaceFlags = 0; + Camera_UpdateInterface(sCameraInterfaceFlags); + } else if ((D_8011D3F0 != 0) && (camera->thisIdx == MAIN_CAM)) { + D_8011D3F0--; + sCameraInterfaceFlags = 0x3200; + Camera_UpdateInterface(sCameraInterfaceFlags); + } else if (camera->globalCtx->transitionMode != 0) { + sCameraInterfaceFlags = 0xF200; + Camera_UpdateInterface(sCameraInterfaceFlags); + } else if (camera->globalCtx->csCtx.state != CS_STATE_IDLE) { + sCameraInterfaceFlags = 0x3200; + Camera_UpdateInterface(sCameraInterfaceFlags); + } else { + Camera_UpdateInterface(sCameraInterfaceFlags); + } + } + + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: shrink_and_bitem %x(%d)\n", sCameraInterfaceFlags, camera->globalCtx->transitionMode); + } + + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: engine (%s(%d) %s(%d) %s(%d)) ok!\n", &sCameraSettingNames[camera->setting], + camera->setting, &sCameraModeNames[camera->mode], camera->mode, + &sCameraFunctionNames[sCameraSettings[camera->setting].cameraModes[camera->mode].funcIdx], + sCameraSettings[camera->setting].cameraModes[camera->mode].funcIdx); + } + + // enable/disable debug cam + if (CVar_GetS32("gDebugCamera", 0) && CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_START)) { + gDbgCamEnabled ^= 1; + if (gDbgCamEnabled) { + DbgCamera_Enable(&D_8015BD80, camera); + } else if (camera->globalCtx->csCtx.state != CS_STATE_IDLE) { + func_80064534(camera->globalCtx, &camera->globalCtx->csCtx); + } + } + + // Debug cam update + if (gDbgCamEnabled) { + camera->globalCtx->view.fovy = D_8015BD80.fov; + DbCamera_Update(&D_8015BD80, camera); + func_800AA358(&camera->globalCtx->view, &D_8015BD80.eye, &D_8015BD80.at, &D_8015BD80.unk_1C); + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: debug out\n"); + } + return D_8015BD80.sub.unk_104A; + } + + OREG(0) &= ~8; + + if (camera->status == 3) { + return camera->inputDir; + } + + // setting bgCheckId to the ret of Quake_Calc, and checking that + // is required, it doesn't make too much sense though. + if ((bgId = Quake_Calc(camera, &quake), bgId != 0) && (camera->setting != CAM_SET_TURN_AROUND)) { + viewAt.x = camera->at.x + quake.atOffset.x; + viewAt.y = camera->at.y + quake.atOffset.y; + viewAt.z = camera->at.z + quake.atOffset.z; + viewEye.x = camera->eye.x + quake.eyeOffset.x; + viewEye.y = camera->eye.y + quake.eyeOffset.y; + viewEye.z = camera->eye.z + quake.eyeOffset.z; + OLib_Vec3fDiffToVecSphGeo(&eyeAtAngle, &viewEye, &viewAt); + Camera_CalcUpFromPitchYawRoll(&viewUp, eyeAtAngle.pitch + quake.rotZ, eyeAtAngle.yaw + quake.unk_1A, + camera->roll); + viewFov = camera->fov + BINANG_TO_DEGF(quake.zoom); + } else { + viewAt = camera->at; + viewEye = camera->eye; + OLib_Vec3fDiffToVecSphGeo(&eyeAtAngle, &viewEye, &viewAt); + Camera_CalcUpFromPitchYawRoll(&viewUp, eyeAtAngle.pitch, eyeAtAngle.yaw, camera->roll); + viewFov = camera->fov; + } + + if (camera->paramFlags & 4) { + camera->paramFlags &= ~4; + viewUp = camera->up; + } else { + camera->up = viewUp; + } + + camera->skyboxOffset = quake.eyeOffset; + + func_80058E8C(camera); + if ((camera->globalCtx->sceneNum == SCENE_SPOT00) && (camera->fov < 59.0f)) { + View_SetScale(&camera->globalCtx->view, 0.79f); + } else { + View_SetScale(&camera->globalCtx->view, 1.0f); + } + camera->globalCtx->view.fovy = viewFov; + func_800AA358(&camera->globalCtx->view, &viewEye, &viewAt, &viewUp); + camera->camDir.x = eyeAtAngle.pitch; + camera->camDir.y = eyeAtAngle.yaw; + camera->camDir.z = 0; + + if (sUpdateCameraDirection == 0) { + camera->inputDir.x = eyeAtAngle.pitch; + camera->inputDir.y = eyeAtAngle.yaw; + camera->inputDir.z = 0; + } + + if (PREG(81)) { + osSyncPrintf("dir (%d) %d(%f) %d(%f) 0(0) \n", sUpdateCameraDirection, camera->inputDir.x, + BINANG_TO_DEGF(camera->inputDir.x), camera->inputDir.y, BINANG_TO_DEGF(camera->inputDir.y)); + osSyncPrintf("real (%d) %d(%f) %d(%f) 0(0) \n", sUpdateCameraDirection, camera->camDir.x, + BINANG_TO_DEGF(camera->camDir.x), camera->camDir.y, BINANG_TO_DEGF(camera->camDir.y)); + } + + if (camera->timer != -1 && CHECK_BTN_ALL(D_8015BD7C->state.input[0].press.button, BTN_DRIGHT)) { + camera->timer = 0; + } + + if (R_DBG_CAM_UPDATE) { + osSyncPrintf("camera: out (%f %f %f) (%f %f %f)\n", camera->at.x, camera->at.y, camera->at.z, camera->eye.x, + camera->eye.y, camera->eye.z); + osSyncPrintf("camera: dir (%f %d(%f) %d(%f)) (%f)\n", eyeAtAngle.r, eyeAtAngle.pitch, + BINANG_TO_DEGF(eyeAtAngle.pitch), eyeAtAngle.yaw, BINANG_TO_DEGF(eyeAtAngle.yaw), camera->fov); + if (camera->player != NULL) { + osSyncPrintf("camera: foot(%f %f %f) dist (%f)\n", curPlayerPosRot.pos.x, curPlayerPosRot.pos.y, + curPlayerPosRot.pos.z, camera->dist); + } + } + + return camera->inputDir; +} + +/** + * When the camera's timer is 0, change the camera to its parent + */ +void Camera_Finish(Camera* camera) { + Camera* mainCam = camera->globalCtx->cameraPtrs[MAIN_CAM]; + Player* player = GET_PLAYER(camera->globalCtx); + + if (camera->timer == 0) { + Gameplay_ChangeCameraStatus(camera->globalCtx, camera->parentCamIdx, CAM_STAT_ACTIVE); + + if ((camera->parentCamIdx == MAIN_CAM) && (camera->csId != 0)) { + player->actor.freezeTimer = 0; + player->stateFlags1 &= ~0x20000000; + + if (player->csMode != 0) { + func_8002DF54(camera->globalCtx, &player->actor, 7); + osSyncPrintf("camera: player demo end!!\n"); + } + + mainCam->unk_14C |= 8; + } + + if (CHILD_CAM(camera)->parentCamIdx == camera->thisIdx) { + CHILD_CAM(camera)->parentCamIdx = camera->parentCamIdx; + } + + if (PARENT_CAM(camera)->childCamIdx == camera->thisIdx) { + PARENT_CAM(camera)->childCamIdx = camera->childCamIdx; + } + + if (PARENT_CAM(camera)->thisIdx == MAIN_CAM) { + PARENT_CAM(camera)->animState = 0; + } + + camera->childCamIdx = camera->parentCamIdx = SUBCAM_FREE; + camera->timer = -1; + camera->globalCtx->envCtx.fillScreen = false; + + Gameplay_ClearCamera(camera->globalCtx, camera->thisIdx); + } +} + +s32 func_8005A02C(Camera* camera) { + camera->unk_14C |= 0xC; + camera->unk_14C &= ~(0x1000 | 0x8); + return true; +} + +s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags) { + static s32 modeChangeFlags = 0; + + if (QREG(89)) { + osSyncPrintf("+=+(%d)+=+ recive request -> %s\n", camera->globalCtx->state.frames, sCameraModeNames[mode]); + } + + if (camera->unk_14C & 0x20 && flags == 0) { + camera->unk_14A |= 0x20; + return -1; + } + + if (!((sCameraSettings[camera->setting].unk_00 & 0x3FFFFFFF) & (1 << mode))) { + if (mode == CAM_MODE_FIRSTPERSON) { + osSyncPrintf("camera: error sound\n"); + func_80078884(NA_SE_SY_ERROR); + } + + if (camera->mode != CAM_MODE_NORMAL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "camera: change camera mode: force NORMAL: %s %s refused\n" VT_RST, + sCameraSettingNames[camera->setting], sCameraModeNames[mode]); + camera->mode = CAM_MODE_NORMAL; + Camera_CopyDataToRegs(camera, camera->mode); + func_8005A02C(camera); + return 0xC0000000 | mode; + } else { + camera->unk_14A |= 0x20; + camera->unk_14A |= 2; + return 0; + } + } else { + if (mode == camera->mode && flags == 0) { + camera->unk_14A |= 0x20; + camera->unk_14A |= 2; + return -1; + } + camera->unk_14A |= 0x20; + camera->unk_14A |= 2; + Camera_CopyDataToRegs(camera, mode); + modeChangeFlags = 0; + switch (mode) { + case CAM_MODE_FIRSTPERSON: + modeChangeFlags = 0x20; + break; + case CAM_MODE_BATTLE: + modeChangeFlags = 4; + break; + case CAM_MODE_FOLLOWTARGET: + if (camera->target != NULL && camera->target->id != ACTOR_EN_BOOM) { + modeChangeFlags = 8; + } + break; + case CAM_MODE_TARGET: + case CAM_MODE_TALK: + case CAM_MODE_BOWARROWZ: + case CAM_MODE_HANGZ: + case CAM_MODE_PUSHPULL: + modeChangeFlags = 2; + break; + } + + switch (camera->mode) { + case CAM_MODE_FIRSTPERSON: + if (modeChangeFlags & 0x20) { + camera->animState = 0xA; + } + break; + case CAM_MODE_TARGET: + if (modeChangeFlags & 0x10) { + camera->animState = 0xA; + } + modeChangeFlags |= 1; + break; + case CAM_MODE_CHARGE: + modeChangeFlags |= 1; + break; + case CAM_MODE_FOLLOWTARGET: + if (modeChangeFlags & 8) { + camera->animState = 0xA; + } + modeChangeFlags |= 1; + break; + case CAM_MODE_BATTLE: + if (modeChangeFlags & 4) { + camera->animState = 0xA; + } + modeChangeFlags |= 1; + break; + case CAM_MODE_BOWARROWZ: + case CAM_MODE_HANGZ: + case CAM_MODE_PUSHPULL: + modeChangeFlags |= 1; + break; + case CAM_MODE_NORMAL: + if (modeChangeFlags & 0x10) { + camera->animState = 0xA; + } + break; + } + modeChangeFlags &= ~0x10; + if (camera->status == CAM_STAT_ACTIVE) { + switch (modeChangeFlags) { + case 1: + func_80078884(0); + break; + case 2: + if (camera->globalCtx->roomCtx.curRoom.unk_03 == 1) { + func_80078884(NA_SE_SY_ATTENTION_URGENCY); + } else { + func_80078884(NA_SE_SY_ATTENTION_ON); + } + break; + case 4: + func_80078884(NA_SE_SY_ATTENTION_URGENCY); + break; + case 8: + func_80078884(NA_SE_SY_ATTENTION_ON); + break; + } + } + func_8005A02C(camera); + camera->mode = mode; + return 0x80000000 | mode; + } +} + +s32 Camera_ChangeMode(Camera* camera, s16 mode) { + return Camera_ChangeModeFlags(camera, mode, 0); +} + +s32 Camera_CheckValidMode(Camera* camera, s16 mode) { + if (QREG(89) != 0) { + osSyncPrintf("+=+=+=+ recive asking -> %s (%s)\n", sCameraModeNames[mode], + sCameraSettingNames[camera->setting]); + } + if (!(sCameraSettings[camera->setting].validModes & (1 << mode))) { + return 0; + } else if (mode == camera->mode) { + return -1; + } else { + return mode | 0x80000000; + } +} + +s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags) { + if (camera->unk_14A & 1) { + if (((u32)(sCameraSettings[camera->setting].unk_00 & 0xF000000) >> 0x18) >= + ((u32)(sCameraSettings[setting].unk_00 & 0xF000000) >> 0x18)) { + camera->unk_14A |= 0x10; + return -2; + } + } + if (((setting == CAM_SET_MEADOW_BIRDS_EYE) || (setting == CAM_SET_MEADOW_UNUSED)) && LINK_IS_ADULT && + (camera->globalCtx->sceneNum == SCENE_SPOT05)) { + camera->unk_14A |= 0x10; + return -5; + } + + if (setting == CAM_SET_NONE || setting >= CAM_SET_MAX) { + osSyncPrintf(VT_COL(RED, WHITE) "camera: error: illegal camera set (%d) !!!!\n" VT_RST, setting); + return -99; + } + + if ((setting == camera->setting) && (!(flags & 1))) { + camera->unk_14A |= 0x10; + if (!(flags & 2)) { + camera->unk_14A |= 1; + } + return -1; + } + + camera->unk_14A |= 0x10; + if (!(flags & 2)) { + camera->unk_14A |= 1; + } + + camera->unk_14C |= 0xC; + camera->unk_14C &= ~0x1008; + + if (!(sCameraSettings[camera->setting].unk_00 & 0x40000000)) { + camera->prevSetting = camera->setting; + } + + if (flags & 8) { + if (1) {} + camera->camDataIdx = camera->prevCamDataIdx; + camera->prevCamDataIdx = -1; + } else if (!(flags & 4)) { + if (!(sCameraSettings[camera->setting].unk_00 & 0x40000000)) { + camera->prevCamDataIdx = camera->camDataIdx; + } + camera->camDataIdx = -1; + } + + camera->setting = setting; + + if (Camera_ChangeModeFlags(camera, camera->mode, 1) >= 0) { + Camera_CopyDataToRegs(camera, camera->mode); + } + + osSyncPrintf(VT_SGR("1") "%06u:" VT_RST " camera: change camera[%d] set %s\n", camera->globalCtx->state.frames, + camera->thisIdx, sCameraSettingNames[camera->setting]); + + return setting; +} + +s32 Camera_ChangeSetting(Camera* camera, s16 setting) { + return Camera_ChangeSettingFlags(camera, setting, 0); +} + +s32 Camera_ChangeDataIdx(Camera* camera, s32 camDataIdx) { + s16 newCameraSetting; + s16 settingChangeSuccessful; + + if (camDataIdx == -1 || camDataIdx == camera->camDataIdx) { + camera->unk_14A |= 0x40; + return -1; + } + + if (!(camera->unk_14A & 0x40)) { + newCameraSetting = Camera_GetCamDataSetting(camera, camDataIdx); + camera->unk_14A |= 0x40; + settingChangeSuccessful = Camera_ChangeSettingFlags(camera, newCameraSetting, 5) >= 0; + if (settingChangeSuccessful || sCameraSettings[camera->setting].unk_00 & 0x80000000) { + camera->camDataIdx = camDataIdx; + camera->unk_14A |= 4; + Camera_CopyDataToRegs(camera, camera->mode); + } else if (settingChangeSuccessful < -1) { + //! @bug: This is likely checking the wrong value. The actual return of Camera_ChangeSettingFlags or + // camDataIdx would make more sense. + osSyncPrintf(VT_COL(RED, WHITE) "camera: error: illegal camera ID (%d) !! (%d|%d|%d)\n" VT_RST, camDataIdx, + camera->thisIdx, 0x32, newCameraSetting); + } + return 0x80000000 | camDataIdx; + } +} + +Vec3s* Camera_GetInputDir(Vec3s* dst, Camera* camera) { + if (gDbgCamEnabled) { + *dst = D_8015BD80.sub.unk_104A; + return dst; + } else { + *dst = camera->inputDir; + return dst; + } +} + +s16 Camera_GetInputDirPitch(Camera* camera) { + Vec3s dir; + + Camera_GetInputDir(&dir, camera); + return dir.x; +} + +s16 Camera_GetInputDirYaw(Camera* camera) { + Vec3s dir; + + Camera_GetInputDir(&dir, camera); + return dir.y; +} + +Vec3s* Camera_GetCamDir(Vec3s* dst, Camera* camera) { + if (gDbgCamEnabled) { + *dst = D_8015BD80.sub.unk_104A; + return dst; + } else { + *dst = camera->camDir; + return dst; + } +} + +s16 Camera_GetCamDirPitch(Camera* camera) { + Vec3s camDir; + + Camera_GetCamDir(&camDir, camera); + return camDir.x; +} + +s16 Camera_GetCamDirYaw(Camera* camera) { + Vec3s camDir; + + Camera_GetCamDir(&camDir, camera); + return camDir.y; +} + +s32 Camera_AddQuake(Camera* camera, s32 arg1, s16 y, s32 countdown) { + s16 quakeIdx; + + quakeIdx = Quake_Add(camera, 3); + if (quakeIdx == 0) { + return 0; + } + Quake_SetSpeed(quakeIdx, 0x61A8); + Quake_SetQuakeValues(quakeIdx, y, 0, 0, 0); + Quake_SetCountdown(quakeIdx, countdown); + return 1; +} + +s32 Camera_SetParam(Camera* camera, s32 param, void* value) { + s32 pad[3]; + + if (value != NULL) { + switch (param) { + case 1: + camera->paramFlags &= ~(0x10 | 0x8 | 0x1); + camera->at = *(Vec3f*)value; + break; + case 16: + camera->paramFlags &= ~(0x10 | 0x8 | 0x1); + camera->targetPosRot.pos = *(Vec3f*)value; + break; + case 8: + if (camera->setting == CAM_SET_CS_C || camera->setting == CAM_SET_CS_ATTENTION) { + break; + } + camera->target = (Actor*)value; + camera->paramFlags &= ~(0x10 | 0x8 | 0x1); + break; + case 2: + camera->eye = camera->eyeNext = *(Vec3f*)value; + break; + case 4: + camera->up = *(Vec3f*)value; + break; + case 0x40: + camera->roll = DEGF_TO_BINANG(*(f32*)value); + break; + case 0x20: + camera->fov = *(f32*)value; + break; + default: + return false; + } + camera->paramFlags |= param; + } else { + return false; + } + return true; +} + +s32 Camera_UnsetParam(Camera* camera, s16 param) { + camera->paramFlags &= ~param; + return true; +} + +s32 func_8005AC48(Camera* camera, s16 arg1) { + camera->unk_14C = arg1; + return true; +} + +s32 Camera_ResetAnim(Camera* camera) { + camera->animState = 0; + return 1; +} + +s32 Camera_SetCSParams(Camera* camera, CutsceneCameraPoint* atPoints, CutsceneCameraPoint* eyePoints, Player* player, + s16 relativeToPlayer) { + PosRot playerPosRot; + + camera->data0 = atPoints; + camera->data1 = eyePoints; + camera->data2 = relativeToPlayer; + + if (camera->data2 != 0) { + camera->player = player; + Actor_GetWorldPosShapeRot(&playerPosRot, &player->actor); + camera->playerPosRot = playerPosRot; + + camera->nextCamDataIdx = -1; + camera->xzSpeed = 0.0f; + camera->speedRatio = 0.0f; + } + + return 1; +} + +s16 func_8005ACFC(Camera* camera, s16 arg1) { + camera->unk_14C |= arg1; + return camera->unk_14C; +} + +s16 func_8005AD1C(Camera* camera, s16 arg1) { + camera->unk_14C &= ~arg1; + return camera->unk_14C; +} + +s32 Camera_ChangeDoorCam(Camera* camera, Actor* doorActor, s16 camDataIdx, f32 arg3, s16 timer1, s16 timer2, + s16 timer3) { + DoorParams* doorParams = (DoorParams*)camera->paramData; + + if ((camera->setting == CAM_SET_CS_ATTENTION) || (camera->setting == CAM_SET_DOORC)) { + return 0; + } + + doorParams->doorActor = doorActor; + doorParams->timer1 = timer1; + doorParams->timer2 = timer2; + doorParams->timer3 = timer3; + doorParams->camDataIdx = camDataIdx; + + if (camDataIdx == -99) { + Camera_CopyDataToRegs(camera, camera->mode); + return -99; + } + + if (camDataIdx == -1) { + Camera_ChangeSetting(camera, CAM_SET_DOORC); + osSyncPrintf(".... change default door camera (set %d)\n", CAM_SET_DOORC); + } else { + s32 setting = Camera_GetCamDataSetting(camera, camDataIdx); + + camera->unk_14A |= 0x40; + + if (Camera_ChangeSetting(camera, setting) >= 0) { + camera->camDataIdx = camDataIdx; + camera->unk_14A |= 4; + } + + osSyncPrintf("....change door camera ID %d (set %d)\n", camera->camDataIdx, camera->setting); + } + + Camera_CopyDataToRegs(camera, camera->mode); + return -1; +} + +s32 Camera_Copy(Camera* dstCamera, Camera* srcCamera) { + s32 pad; + + dstCamera->posOffset.x = 0.0f; + dstCamera->posOffset.y = 0.0f; + dstCamera->posOffset.z = 0.0f; + dstCamera->atLERPStepScale = 0.1f; + dstCamera->at = srcCamera->at; + + dstCamera->eye = dstCamera->eyeNext = srcCamera->eye; + + dstCamera->dist = OLib_Vec3fDist(&dstCamera->at, &dstCamera->eye); + dstCamera->fov = srcCamera->fov; + dstCamera->roll = srcCamera->roll; + func_80043B60(dstCamera); + + if (dstCamera->player != NULL) { + Actor_GetWorld(&dstCamera->playerPosRot, &dstCamera->player->actor); + dstCamera->posOffset.x = dstCamera->at.x - dstCamera->playerPosRot.pos.x; + dstCamera->posOffset.y = dstCamera->at.y - dstCamera->playerPosRot.pos.y; + dstCamera->posOffset.z = dstCamera->at.z - dstCamera->playerPosRot.pos.z; + dstCamera->dist = OLib_Vec3fDist(&dstCamera->playerPosRot.pos, &dstCamera->eye); + dstCamera->xzOffsetUpdateRate = 1.0f; + dstCamera->yOffsetUpdateRate = 1.0f; + } + return true; +} + +s32 Camera_GetDbgCamEnabled() { + return gDbgCamEnabled; +} + +Vec3f* Camera_GetSkyboxOffset(Vec3f* dst, Camera* camera) { + *dst = camera->skyboxOffset; + return dst; +} + +void Camera_SetCameraData(Camera* camera, s16 setDataFlags, void* data0, void* data1, s16 data2, s16 data3, + UNK_TYPE arg6) { + if (setDataFlags & 0x1) { + camera->data0 = data0; + } + + if (setDataFlags & 0x2) { + camera->data1 = data1; + } + + if (setDataFlags & 0x4) { + camera->data2 = data2; + } + + if (setDataFlags & 0x8) { + camera->data3 = data3; + } + + if (setDataFlags & 0x10) { + osSyncPrintf(VT_COL(RED, WHITE) "camera: setCameraData: last argument not alive!\n" VT_RST); + } +} + +s32 Camera_QRegInit() { + if (!R_RELOAD_CAM_PARAMS) { + QREG(2) = 1; + QREG(10) = -1; + QREG(11) = 100; + QREG(12) = 80; + QREG(20) = 90; + QREG(21) = 10; + QREG(22) = 10; + QREG(23) = 50; + QREG(24) = 6000; + QREG(25) = 240; + QREG(26) = 40; + QREG(27) = 85; + QREG(28) = 55; + QREG(29) = 87; + QREG(30) = 23; + QREG(31) = 20; + QREG(32) = 4; + QREG(33) = 5; + QREG(50) = 1; + QREG(51) = 20; + QREG(52) = 200; + QREG(53) = 1; + QREG(54) = 15; + QREG(55) = 60; + QREG(56) = 15; + QREG(57) = 30; + QREG(58) = 0; + } + + QREG(65) = 50; + return true; +} + +s32 func_8005B198() { + return D_8011D3AC; +} + +s16 func_8005B1A4(Camera* camera) { + camera->unk_14C |= 0x8; + + if ((camera->thisIdx == MAIN_CAM) && (camera->globalCtx->activeCamera != MAIN_CAM)) { + GET_ACTIVE_CAM(camera->globalCtx)->unk_14C |= 0x8; + return camera->globalCtx->activeCamera; + } + + return camera->thisIdx; +} diff --git a/soh/src/code/z_camera_data.c b/soh/src/code/z_camera_data.c new file mode 100644 index 000000000..1f457888e --- /dev/null +++ b/soh/src/code/z_camera_data.c @@ -0,0 +1,2403 @@ +#include "ultra64.h" +#include "global.h" + +typedef struct { + s16 val; + s16 dataType; +} CameraModeValue; + +typedef struct { + s16 funcIdx; + s16 valueCnt; + CameraModeValue* values; +} CameraMode; + +typedef struct { + union { + u32 unk_00; + struct { + u32 unk_bit0 : 1; + u32 unk_bit1 : 1; + u32 validModes : 30; + }; + }; + CameraMode* cameraModes; +} CameraSetting; + +/*==================================================================*/ +// Data +s16 sOREGInit[] = { + 0, 1, 5, 5, 5, 14500, 20, 16, 150, 25, 150, 6, 10, 10, 0, 0, 1, 100, + 250, 120, 80, 30, 120, 4, 1, 50, 20, 1800, 50, 50, 50, 20, 20, -10, -5460, -9100, + -6, 8, 15, 75, 60, 12, 110, 40, 50, 250, -10, 30, 30, 70, 20, 20, 20, +}; + +s16 sOREGInitCnt = 53; + +s16 sCamDataRegsInit[CAM_DATA_MAX] = { + -20, // CAM_DATA_Y_OFFSET + 200, // CAM_DATA_EYE_DIST + 300, // CAM_DATA_EYE_DIST_NEXT + 10, // CAM_DATA_PITCH_TARGET + 12, // CAM_DATA_YAW_UPDATE_RATE_TARGET + 10, // CAM_DATA_XZ_UPDATE_RATE_TARGET + 35, // CAM_DATA_MAX_YAW_UPDATE + 60, // CAM_DATA_FOV + 60, // CAM_DATA_AT_LERP_STEP_SCALE + 3, // CAM_DATA_FLAGS + 0, // CAM_DATA_YAW_TARGET + -40, // CAM_DATA_GROUND_Y_OFFSET + 20, // CAM_DATA_GROUND_AT_LERP_STEP_SCALE + 25, // CAM_DATA_SWING_YAW_INIT + 45, // CAM_DATA_SWING_YAW_FINAL + -5, // CAM_DATA_SWING_PITCH_INIT + 15, // CAM_DATA_SWING_PITCH_FINAL + 15, // CAM_DATA_SWING_PITCH_ADJ + 20, // CAM_DATA_MIN_MAX_DIST_FACTOR + 0, // CAM_DATA_AT_OFFSET_X + 0, // CAM_DATA_AT_OFFSET_Y + 0, // CAM_DATA_AT_OFFSET_Z + 6, // CAM_DATA_UNK_22 + 60, // CAM_DATA_UNK_23 + 30, // CAM_DATA_FOV_SCALE + 0, // CAM_DATA_YAW_SCALE + 5, // CAM_DATA_UNK_26 +}; + +s16 sCamDataRegsInitCount = ARRAY_COUNT(sCamDataRegsInit); + +char sCameraSettingNames[][12] = { + "NONE ", "NORMAL0 ", "NORMAL1 ", "DUNGEON0 ", "DUNGEON1 ", "NORMAL3 ", "HORSE0 ", + "BOSS_GOMA ", "BOSS_DODO ", "BOSS_BARI ", "BOSS_FGANON", "BOSS_BAL ", "BOSS_SHADES", "BOSS_MOFA ", + "BOSS_TWIN0 ", "BOSS_TWIN1 ", "BOSS_GANON1", "BOSS_GANON2", "TOWER0 ", "TOWER1 ", "FIXED0 ", + "FIXED1 ", "CIRCLE0 ", "CIRCLE2 ", "CIRCLE3 ", "PREREND0 ", "PREREND1 ", "PREREND3 ", + "DOOR0 ", "DOORC ", "RAIL3 ", "START0 ", "START1 ", "FREE0 ", "FREE2 ", + "CIRCLE4 ", "CIRCLE5 ", "DEMO0 ", "DEMO1 ", "MORI1 ", "ITEM0 ", "ITEM1 ", + "DEMO3 ", "DEMO4 ", "UFOBEAN ", "LIFTBEAN ", "SCENE0 ", "SCENE1 ", "HIDAN1 ", + "HIDAN2 ", "MORI2 ", "MORI3 ", "TAKO ", "SPOT05A ", "SPOT05B ", "HIDAN3 ", + "ITEM2 ", "CIRCLE6 ", "NORMAL2 ", "FISHING ", "DEMOC ", "UO_FIBER ", "DUNGEON2 ", + "TEPPEN ", "CIRCLE7 ", "NORMAL4 ", +}; + +char sCameraModeNames[][12] = { + "NORMAL ", "PARALLEL ", "KEEPON ", "TALK ", "BATTLE ", "CLIMB ", "SUBJECT ", + "BOWARROW ", "BOWARROWZ ", "FOOKSHOT ", "BOOMERANG ", "PACHINCO ", "CLIMBZ ", "JUMP ", + "HANG ", "HANGZ ", "FREEFALL ", "CHARGE ", "STILL ", "PUSHPULL ", "BOOKEEPON ", +}; + +/** + *===================================================================== + * General Data: NORMAL0 Setting + *===================================================================== + */ + +CameraModeValue sSetNormal0ModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 200, 300, 10, 12, 10, 35, 60, 60, 0x0003), +}; + +CameraModeValue sSetNormal0ModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 250, 0, 0, 5, 5, 45, 50, 0x200A, -40, 20), +}; + +CameraModeValue sSetNormal0ModeFollowTargetData[] = { + CAM_FUNCDATA_KEEP1(-20, 120, 140, 25, 45, -5, 15, 15, 45, 50, 0x2001, -50, 30), +}; + +CameraModeValue sSetNormal0ModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-30, 70, 200, 40, 10, 0, 5, 70, 45, 50, 10, 0x3500), +}; + +CameraModeValue sSetNormal0ModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 180, 10, 80, 0, 10, 25, 50, 80, 0x2002, -40, 25), +}; + +CameraModeValue sSetNormal0ModeClimbData[] = { + CAM_FUNCDATA_JUMP2(-20, 200, 300, 20, 5, 5, 60, 40, 0x0000), +}; + +CameraModeValue sSetNormal0ModeFirstPersonData[] = { + CAM_FUNCDATA_SUBJ3(0, 5, 50, 10, 0, 0, 0, 45, 0x0000), +}; + +CameraModeValue sSetNormal0ModeBowArrowData[] = { + CAM_FUNCDATA_SUBJ3(-7, 14, 50, 10, 0, -30, -5, 45, 0x2000), +}; + +CameraModeValue sSetNormal0ModeBowArrowZData[] = { + CAM_FUNCDATA_SUBJ3(20, 70, 70, 10, -120, 20, 0, 45, 0x2000), +}; + +CameraModeValue sSetNormal0ModeHookshotData[] = { + CAM_FUNCDATA_SPEC5_ALT(-20, 80, 250, 45, 60, 40, 6, 0x2000), +}; + +CameraModeValue sSetNormal0ModeBoomerangData[] = { + CAM_FUNCDATA_SUBJ3(5, 50, 50, 10, 0, 0, 0, 45, 0x2000), +}; + +CameraModeValue sSetNormal0ModeSlingshotData[] = { + CAM_FUNCDATA_SUBJ3(-7, 14, 50, 10, -9, -63, -30, 45, 0x2000), +}; + +CameraModeValue sSetNormal0ModeClimbZData[] = { + CAM_FUNCDATA_JUMP2(-20, 200, 300, 20, 999, 5, 60, 40, 0x2006), +}; + +CameraModeValue sSetNormal0ModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-20, 200, 300, 12, 35, 60, 40, 0x0000), +}; + +CameraModeValue sSetNormal0ModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 200, 300, 40, 60, 10, 0x0000), +}; + +CameraModeValue sSetNormal0ModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 300, 300, 70, 45, 10, 0x2000), +}; + +CameraModeValue sSetNormal0ModeFreeFallData[] = { + CAM_FUNCDATA_JUMP1(-20, 200, 300, 15, 80, 60, 20, 0x0000), +}; + +CameraModeValue sSetNormal0ModeChargeData[] = { + CAM_FUNCDATA_BATT4(-20, 300, 50, 2, 80, 20, 0xF000), +}; + +CameraModeValue sSetNormal0ModeStillData[] = { + CAM_FUNCDATA_NORM1(-20, 200, 300, 10, 100, 10, 100, 60, 5, 0xF003), +}; + +CameraModeValue sSetNormal0ModePushPullData[] = { + CAM_FUNCDATA_PARA1(0, 250, 25, 0, 5, 5, 70, 30, 0x206A, -20, 30), +}; + +CameraModeValue sSetNormal0ModeFollowBoomerangData[] = { + CAM_FUNCDATA_KEEP1(-5, 120, 140, 5, 85, 10, 5, 25, 45, 50, 0x2001, -15, 30), +}; + +/** + *===================================================================== + * Custom Data: NORMAL1 Setting + *===================================================================== + */ + +CameraModeValue sSetNormal1ModeNormalData[] = { + CAM_FUNCDATA_NORM1(0, 200, 400, 10, 12, 20, 40, 60, 60, 0x0003), +}; + +CameraModeValue sSetNormal1ModeTargetData[] = { + CAM_FUNCDATA_PARA1(0, 250, 0, 0, 5, 5, 45, 50, 0x2002, -40, 20), +}; + +CameraModeValue sSetNormal1ModeFollowTargetData[] = { + CAM_FUNCDATA_KEEP1(-20, 120, 140, 25, 45, -5, 15, 15, 45, 50, 0x2001, -50, 20), +}; + +CameraModeValue sSetNormal1ModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 250, 10, 80, 0, 10, 25, 50, 65, 0x2002, -40, 25), +}; + +CameraModeValue sSetNormal1ModeHookshotData[] = { + CAM_FUNCDATA_SPEC5(-20, 80, 250, 6, 45, 60, 40, 0x2000), +}; + +CameraModeValue sSetNormal1ModeJumpData[] = { + CAM_FUNCDATA_JUMP1(0, 250, 400, 15, 50, 60, 30, 0x0000), +}; + +CameraModeValue sSetNormal1ModeFreeFallData[] = { + CAM_FUNCDATA_JUMP1(0, 200, 400, 30, 80, 60, 20, 0x0000), +}; + +CameraModeValue sSetNormal1ModeClimbData[] = { + CAM_FUNCDATA_JUMP2(-20, 200, 400, 20, 5, 5, 60, 40, 0x0000), +}; + +CameraModeValue sSetNormal1ModeClimbZData[] = { + CAM_FUNCDATA_JUMP2(-20, 250, 400, 20, 999, 5, 60, 40, 0x2006), +}; + +CameraModeValue sSetNormal1ModeChargeData[] = { + CAM_FUNCDATA_BATT4(0, 300, 50, 2, 80, 20, 0xF000), +}; + +CameraModeValue sSetNormal1ModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 200, 400, 40, 60, 10, 0x0000), +}; + +CameraModeValue sSetNormal1ModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 400, 400, 70, 45, 10, 0x2000), +}; + +CameraModeValue sSetNormal1ModeStillData[] = { + CAM_FUNCDATA_NORM1(0, 200, 400, 10, 100, 20, 100, 60, 5, 0xF003), +}; + +/** + *===================================================================== + * Custom Data: DUNGEON0 Setting + *===================================================================== + */ + +CameraModeValue sSetDungeon0ModeNormalData[] = { + CAM_FUNCDATA_NORM1(-10, 150, 250, 5, 10, 5, 30, 60, 60, 0x0003), +}; + +CameraModeValue sSetDungeon0ModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 150, 0, 0, 5, 5, 45, 50, 0x200A, -40, 20), +}; + +CameraModeValue sSetDungeon0ModeFollowTargetData[] = { + CAM_FUNCDATA_KEEP1(-20, 120, 140, 25, 45, -5, 15, 15, 45, 50, 0x2001, -40, 20), +}; + +CameraModeValue sSetDungeon0ModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 180, 10, 80, 0, 10, 25, 45, 80, 0x2002, -40, 25), +}; + +CameraModeValue sSetDungeon0ModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-10, 150, 250, 10, 50, 60, 40, 0x0000), +}; + +CameraModeValue sSetDungeon0ModeFreeFallData[] = { + CAM_FUNCDATA_JUMP1(-10, 150, 250, 10, 80, 60, 20, 0x0000), +}; + +CameraModeValue sSetDungeon0ModeClimbData[] = { + CAM_FUNCDATA_JUMP2(-40, 150, 250, 20, 5, 5, 60, 40, 0x0000), +}; + +CameraModeValue sSetDungeon0ModeClimbZData[] = { + CAM_FUNCDATA_JUMP2(-40, 250, 250, 20, 999, 5, 60, 40, 0x2006), +}; + +CameraModeValue sSetDungeon0ModeChargeData[] = { + CAM_FUNCDATA_BATT4(-10, 300, 50, 2, 80, 20, 0xF000), +}; + +CameraModeValue sSetDungeon0ModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 150, 250, 40, 60, 10, 0x0000), +}; + +CameraModeValue sSetDungeon0ModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 250, 250, 70, 45, 10, 0x2000), +}; + +CameraModeValue sSetDungeon0ModeStillData[] = { + CAM_FUNCDATA_NORM1(-10, 150, 250, 5, 100, 5, 100, 60, 5, 0xF003), +}; + +/** + *===================================================================== + * Custom Data: DUNGEON1 Setting + *===================================================================== + */ + +CameraModeValue sSetDungeon1ModeNormalData[] = { + CAM_FUNCDATA_NORM1(-40, 150, 150, 0, 10, 5, 30, 60, 60, 0x0003), +}; + +CameraModeValue sSetDungeon1ModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-20, 70, 200, 40, 10, 0, 5, 70, 45, 50, 10, 0x3500), +}; + +CameraModeValue sSetDungeon1ModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-40, 150, 150, 10, 50, 60, 40, 0x0000), +}; + +CameraModeValue sSetDungeon1ModeFreeFallData[] = { + CAM_FUNCDATA_JUMP1(-40, 150, 180, 12, 80, 60, 20, 0x0000), +}; + +CameraModeValue sSetDungeon1ModeClimbData[] = { + CAM_FUNCDATA_JUMP2(-40, 150, 150, 20, 5, 5, 60, 40, 0x0000), +}; + +CameraModeValue sSetDungeon1ModeClimbZData[] = { + CAM_FUNCDATA_JUMP2(-40, 150, 150, 20, 999, 5, 60, 40, 0x2006), +}; + +CameraModeValue sSetDungeon1ModeChargeData[] = { + CAM_FUNCDATA_BATT4(-40, 200, 50, 2, 80, 20, 0xF000), +}; + +CameraModeValue sSetDungeon1ModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 150, 150, 40, 60, 10, 0x0000), +}; + +CameraModeValue sSetDungeon1ModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 150, 150, 70, 45, 10, 0x2000), +}; + +CameraModeValue sSetDungeon1ModeStillData[] = { + CAM_FUNCDATA_NORM1(-40, 150, 150, 0, 100, 5, 100, 60, 5, 0xF003), +}; + +CameraModeValue sSetDungeon1ModePushPullData[] = { + CAM_FUNCDATA_PARA1(-40, 180, 25, 0, 5, 5, 60, 50, 0x206A, -20, 30), +}; + +/** + *===================================================================== + * Custom Data: NORMAL3 Setting + *===================================================================== + */ + +CameraModeValue sSetNormal3ModeNormalData[] = { + CAM_FUNCDATA_JUMP3(-20, 280, 300, 20, 15, 5, 40, 60, 100, 0x0004), +}; + +CameraModeValue sSetNormal3ModeTargetData[] = { + CAM_FUNCDATA_PARA1(-50, 250, 70, 0, 15, 5, 60, 100, 0x200A, -50, 20), +}; + +CameraModeValue sSetNormal3ModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-30, 70, 200, 40, 10, 10, 20, 70, 45, 10, 10, 0x3500), +}; + +/* These values are for when the eye + * >= OREG(45) units below the surface of the water. + */ +CameraModeValue sSetNormal3ModeBoomerangData[] = { + CAM_FUNCDATA_JUMP3(-40, 150, 250, -5, 18, 5, 60, 60, 40, 0x0005), +}; + +/** + *===================================================================== + * Custom Data: HORSE Setting + *===================================================================== + */ + +CameraModeValue sSetHorseModeNormalData[] = { + CAM_FUNCDATA_NORM3(-50, 220, 250, 10, 16, 20, 60, 100, 0x0600), +}; + +CameraModeValue sSetHorseModeTargetData[] = { + CAM_FUNCDATA_NORM3(-40, 180, 220, -2, 12, 100, 45, 100, 0x2600), +}; + +CameraModeValue sSetHorseModeBowArrowData[] = { + CAM_FUNCDATA_SUBJ3(-7, 14, 100, 10, 0, -30, -5, 40, 0x2600), +}; + +CameraModeValue sSetHorseModeFollowTargetData[] = { + CAM_FUNCDATA_KEEP1(-60, 180, 220, 25, 45, -5, 15, 15, 45, 50, 0x2601, -60, 20), +}; + +CameraModeValue sSetHorseModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-60, 140, 200, 40, 10, 0, 5, 70, 45, 50, 10, 0x3500), +}; + +/** + *===================================================================== + * Custom Data: BOSS_GOHMA Setting + *===================================================================== + */ + +CameraModeValue sSetBossGohmaModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 150, 250, 0, 15, 5, 40, 60, 60, 0x0001), +}; + +CameraModeValue sSetBossGohmaModeBattleData[] = { + CAM_FUNCDATA_BATT1(-30, 150, 10, 40, -10, 0, 25, 60, 40, 0x2002, -50, 20), +}; + +/** + *===================================================================== + * Custom Data: BOSS_DODONGO Setting + *===================================================================== + */ + +CameraModeValue sSetBossDodongoModeNormalData[] = { + CAM_FUNCDATA_NORM1(0, 150, 300, 0, 12, 5, 70, 70, 40, 0x0003), +}; + +CameraModeValue sSetBossDodongoModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 160, 10, 60, -5, 0, 25, 70, 50, 0x2002, -40, 20), +}; + +/** + *===================================================================== + * Custom Data: BOSS_BARINADE Setting + *===================================================================== + */ + +CameraModeValue sSetBossBarinadeModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 150, 300, -5, 15, 5, 40, 70, 70, 0x0003), +}; + +CameraModeValue sSetBossBarinadeModeBattleData[] = { + CAM_FUNCDATA_BATT1(-30, 125, 10, 10, 0, 0, 50, 60, 50, 0x2002, -50, 20), +}; + +/** + *===================================================================== + * Custom Data: BOSS_PHANTOM_GANON Setting + *===================================================================== + */ + +CameraModeValue sSetBossPhantomGanonModeNormalData[] = { + CAM_FUNCDATA_NORM1(10, 150, 250, 0, 15, 15, 40, 60, 100, 0x0003), +}; + +CameraModeValue sSetBossPhantomGanonModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 200, 45, 40, 5, -5, 35, 60, 100, 0x2002, -40, 60), +}; + +/** + *===================================================================== + * Custom Data: BOSS_VOLVAGIA Setting + *===================================================================== + */ + +CameraModeValue sSetBossVolvagiaModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 10, 16, 10, 40, 60, 80, 0x0003), +}; + +CameraModeValue sSetBossVolvagiaModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 200, 20, 60, 0, 10, 15, 45, 50, 0x2002, -40, 20), +}; + +/** + *===================================================================== + * Custom Data: BOSS_BONGO Setting + *===================================================================== + */ + +CameraModeValue sSetBossBongoModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 10, 20, 10, 40, 60, 80, 0x0083), +}; + +CameraModeValue sSetBossBongoModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 200, 20, 60, 0, 10, 15, 45, 50, 0x2082, -40, 20), +}; + +CameraModeValue sSetBossBongoModeJumpData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 10, 20, 10, 80, 60, 80, 0x0083), +}; + +/** + *===================================================================== + * Custom Data: BOSS_MORPHA Setting + *===================================================================== + */ + +CameraModeValue sSetBossMorphaModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 100, 150, -10, 15, 10, 40, 80, 60, 0x0003), +}; + +CameraModeValue sSetBossMorphaModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 200, 10, 80, -10, 10, 25, 70, 40, 0x2002, -40, 20), +}; + +/** + *===================================================================== + * Custom Data: TWINROVA Setting + *===================================================================== + */ + +CameraModeValue sSetBossTwinrovaPlatformModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 150, 300, 0, 20, 10, 40, 60, 80, 0x0003), +}; + +CameraModeValue sSetBossTwinrovaModeBattleData[] = { + CAM_FUNCDATA_BATT1(0, 400, 0, 60, -10, 5, 25, 45, 40, 0x2002, -20, 20), +}; + +CameraModeValue sSetBossTwinrovaFloorModeNormalData[] = { + CAM_FUNCDATA_NORM1(-10, 150, 200, -10, 12, 10, 40, 60, 50, 0x0003), +}; + +/** + *===================================================================== + * Custom Data: BOSS_GANONDORF Setting + *===================================================================== + */ + +CameraModeValue sSetBossGanondorfModeNormalData[] = { + CAM_FUNCDATA_NORM1(40, 330, 330, -5, 15, 15, 40, 60, 100, 0x0000), +}; + +CameraModeValue sSetBossGanondorfModeChargeData[] = { + CAM_FUNCDATA_BATT4(-40, 250, 0, 2, 80, 20, 0xF000), +}; + +/** + *===================================================================== + * Custom Data: BOSS_GANON Setting + *===================================================================== + */ + +CameraModeValue sSetBossGanonModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 10, 20, 10, 40, 60, 80, 0x0003), +}; + +CameraModeValue sSetBossGanonModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 180, 20, 60, 0, 10, 25, 45, 50, 0x2002, -40, 20), +}; + +/** + *===================================================================== + * Custom Data: TOWER_CLIMB Setting + *===================================================================== + */ + +CameraModeValue sSetTowerClimbModeNormalData[] = { + CAM_FUNCDATA_NORM2(0, 120, 280, 60, 8, 40, 60, 50, 0x0000), +}; + +CameraModeValue sSetTowerClimbModeJumpData[] = { + CAM_FUNCDATA_NORM2(0, 120, 280, 60, 8, 40, 60, 50, 0x0080), +}; + +/** + *===================================================================== + * Custom Data: TOWER_UNUSED Setting + *===================================================================== + */ + +CameraModeValue sSetTowerUnusedModeNormalData[] = { + CAM_FUNCDATA_NORM2(0, 270, 300, 120, 8, 60, 60, 100, 0x0000), +}; + +CameraModeValue sSetTowerUnusedModeJumpData[] = { + CAM_FUNCDATA_NORM2(0, 270, 300, 120, 6, 60, 60, 100, 0x0000), +}; + +/** + *===================================================================== + * Custom Data: MARKET_BALCONY Setting + *===================================================================== + */ + +CameraModeValue sSetMarketBalconyModeNormalData[] = { + CAM_FUNCDATA_FIXD1(-40, 100, 60, 0x0000), +}; + +CameraModeValue sSetMarketBalconyModeFollowTargetData[] = { + CAM_FUNCDATA_FIXD1(-40, 100, 60, 0x2000), +}; + +CameraModeValue sSetMarketBalconyModeTalkData[] = { + CAM_FUNCDATA_FIXD1(-40, 100, 60, 0x3500), +}; + +/** + *===================================================================== + * Custom Data: CHU_BOWLING Setting + *===================================================================== + */ + +CameraModeValue sSetChuBowlingModeNormalData[] = { + CAM_FUNCDATA_FIXD1(-40, 25, 60, 0x0000), +}; + +/** + *===================================================================== + * Custom Data: PIVOT_CRAWLSPACE Setting + *===================================================================== + */ + +CameraModeValue sSetPivotCrawlspaceModeNormalData[] = { + CAM_FUNCDATA_FIXD2(-40, 50, 80, 60, 0x0001), +}; + +/** + *===================================================================== + * Custom Data: PIVOT_SHOP_BROWSING Setting + *===================================================================== + */ + +CameraModeValue sSetPivotShopBrowsingModeNormalData[] = { + CAM_FUNCDATA_DATA4(-40, 60, 0x3F00), +}; + +/** + *===================================================================== + * Custom Data: PIVOT_IN_FRONT and PIVOT_FROM_SIDE + *===================================================================== + */ + +CameraModeValue sSetPivotInFrontAndFromSideModeNormalData[] = { + CAM_FUNCDATA_FIXD4(-40, 50, 80, 60, 0x0004), +}; + +/** + *===================================================================== + * Custom Data: No data, all flags off + *===================================================================== + */ + +CameraModeValue sDataOnlyNullFlags[] = { + CAM_FUNCDATA_FLAGS(0x0000), +}; + +/** + *===================================================================== + * Custom Data: PREREND_FIXED Setting + *===================================================================== + */ + +CameraModeValue sSetPrerendFixedModeFollowTargetData[] = { + CAM_FUNCDATA_FLAGS(0x2000), +}; + +/** + *===================================================================== + * Custom Data: PREREND_PIVOT Setting + *===================================================================== + */ + +CameraModeValue sSetPrerendPivotModeNormalData[] = { + CAM_FUNCDATA_UNIQ7(60, 0x0000), +}; + +CameraModeValue sSetPrerendPivotModeFollowTargetData[] = { + CAM_FUNCDATA_UNIQ7(60, 0x2000), +}; + +CameraModeValue sSetPrerendPivotModeTalkData[] = { + CAM_FUNCDATA_KEEP0(30, 0, 4, 0x3500), +}; + +/** + *===================================================================== + * Custom Data: DOOR0 Setting + *===================================================================== + */ + +CameraModeValue sSetDoor0ModeNormalData[] = { + CAM_FUNCDATA_UNIQ3(-40, 60, 0x3200), +}; + +/** + *===================================================================== + * Custom Data: DOORC Setting + *===================================================================== + */ + +CameraModeValue sSetDoorCModeNormalData[] = { + CAM_FUNCDATA_SPEC9(-5, 60, 0x3202), +}; + +CameraModeValue sSetDoorCModeTargetData[] = { + CAM_FUNCDATA_SPEC9(-5, 60, 0x320A), +}; + +/** + *===================================================================== + * Custom Data: CRAWLSPACE Setting + *===================================================================== + */ + +// Camera_Subj4 only reads one setting which is used for flags +CameraModeValue sSetCrawlspaceModeNormalData[] = { + CAM_FUNCDATA_SUBJ4(0x0000, 2, 30, 10, 45, 0x3200), +}; + +/** + *===================================================================== + * Custom Data: START1 Setting + *===================================================================== + */ + +CameraModeValue sSetStart1ModeNormalData[] = { + CAM_FUNCDATA_FLAGS(0x0001), +}; + +/** + *===================================================================== + * Custom Data: FREE0 Setting + *===================================================================== + */ + +CameraModeValue sSetFree0ModeNormalData[] = { + CAM_FUNCDATA_FLAGS(0xFF00), +}; + +/** + *===================================================================== + * Custom Data: FREE1 Setting + *===================================================================== + */ + +CameraModeValue sSetFree1ModeNormalData[] = { + CAM_FUNCDATA_FLAGS(0xFF01), +}; + +/** + *===================================================================== + * Custom Data: PIVOT_CORNER Setting + *===================================================================== + */ + +CameraModeValue sSetPivotCornerModeNormalData[] = { + CAM_FUNCDATA_FIXD2(-40, 100, 80, 60, 0x0000), +}; + +/** + *===================================================================== + * Custom Data: PIVOT_WATER_SURFACE Setting + *===================================================================== + */ + +CameraModeValue sSetPivotWaterSurfaceModeNormalData[] = { + CAM_FUNCDATA_UNIQ2(-40, 60, 60, 0x0002), +}; + +CameraModeValue sSetPivotWaterSurfaceModeTargetData[] = { + CAM_FUNCDATA_UNIQ2(-30, 45, 100, 0x2001), +}; + +/** + *===================================================================== + * Custom Data: Various cutscene settings + *===================================================================== + */ + +CameraModeValue sDataOnlyInterfaceFlags[] = { + CAM_FUNCDATA_FLAGS(0x3200), +}; + +/** + *===================================================================== + * Custom Data: FOREST_BIRDS_EYE Setting + *===================================================================== + */ + +CameraModeValue sSetForestBirdsEyeModeNormalData[] = { + CAM_FUNCDATA_PARA1(-50, 450, 40, 180, 5, 5, 70, 30, 0x000C, -50, 20), +}; + +CameraModeValue sSetForestBirdsEyeModeTalkData[] = { + CAM_FUNCDATA_FLAGS(0x3501), +}; + +/** + *===================================================================== + * Custom Data: SLOW_CHEST_CS Setting + *===================================================================== + */ + +// Also set to but unused by function Demo4 +CameraModeValue sSetSlowChestCsModeNormalData[] = { + CAM_FUNCDATA_DEMO3(60, 30, 0x3200), +}; + +/** + *===================================================================== + * Custom Data: CS_3 Setting + *===================================================================== + */ + +CameraModeValue sSetCs3ModeNormalData[] = { + CAM_FUNCDATA_FLAGS(0x3212), +}; + +/** + *===================================================================== + * Custom Data: BEAN_GENERIC Setting + *===================================================================== + */ + +CameraModeValue sSetBeanGenericModeNormalData[] = { + CAM_FUNCDATA_NORM1(-50, 300, 300, 50, 20, 10, 50, 70, 40, 0x0002), +}; + +CameraModeValue sSetBeanGenericModeTargetData[] = { + CAM_FUNCDATA_PARA1(-50, 300, 10, 0, 5, 5, 45, 50, 0x200A, -40, 20), +}; + +CameraModeValue sSetBeanGenericModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-50, 300, 300, 12, 35, 60, 40, 0x0000), +}; + +CameraModeValue sSetBeanGenericModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 300, 300, 60, 70, 30, 0x0000), +}; + +CameraModeValue sSetBeanGenericModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 300, 300, 70, 50, 30, 0x2000), +}; + +CameraModeValue sSetBeanGenericModeStillData[] = { + CAM_FUNCDATA_NORM1(-20, 300, 350, 50, 100, 10, 100, 70, 30, 0xF002), +}; + +/** + *===================================================================== + * Custom Data: BEAN_LOST_WOODS Setting + *===================================================================== + */ + +CameraModeValue sSetBeanLostWoodsModeNormalData[] = { + CAM_FUNCDATA_NORM1(-50, 200, 200, 20, 16, 10, 50, 60, 50, 0x0002), +}; + +CameraModeValue sSetBeanLostWoodsModeTargetData[] = { + CAM_FUNCDATA_PARA1(-50, 200, 40, 0, 5, 5, 45, 50, 0x200A, -40, 20), +}; + +CameraModeValue sSetBeanLostWoodsModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-50, 150, 250, 12, 35, 60, 40, 0x0000), +}; + +CameraModeValue sSetBeanLostWoodsModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 200, 200, 40, 60, 30, 0x0000), +}; + +CameraModeValue sSetBeanLostWoodsModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 200, 200, 60, 50, 30, 0x2000), +}; + +CameraModeValue sSetBeanLostWoodsModeStillData[] = { + CAM_FUNCDATA_NORM1(-20, 200, 250, 20, 100, 10, 100, 60, 30, 0xF002), +}; + +/** + *===================================================================== + * Custom Data: SCENE_UNUSED Setting + *===================================================================== + */ + +CameraModeValue sSetSceneUnusedModeNormalData[] = { + CAM_FUNCDATA_SPEC9(-30, 60, 0x010A), +}; + +/** + *===================================================================== + * Custom Data: SCENE_TRANSITION Setting + *===================================================================== + */ + +CameraModeValue sSetSceneTransitionModeNormalData[] = { + CAM_FUNCDATA_UNIQ2(-20, 150, 60, 0x0210), +}; + +/** + *===================================================================== + * Custom Data: BIG_OCTO Setting + *===================================================================== + */ + +CameraModeValue sSetBigOctoModeNormalData[] = { + CAM_FUNCDATA_NORM1(0, 400, 500, 35, 14, 5, 20, 60, 40, 0x0012), +}; + +CameraModeValue sSetBigOctoModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 250, 5, 10, 30, 20, 25, 45, 60, 0x2002, -40, 25), +}; + +CameraModeValue sSetBigOctoModeStillData[] = { + CAM_FUNCDATA_NORM1(0, 300, 500, 60, 8, 5, 60, 60, 30, 0x0012), +}; + +/** + *===================================================================== + * Custom Data: MEADOW_BIRDS_EYE Setting + *===================================================================== + */ + +CameraModeValue sSetMeadowBirdsEyeModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 80, 20, 10, 70, 70, 80, 0x0012), +}; + +CameraModeValue sSetMeadowBirdsEyeModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 500, 80, 0, 5, 5, 70, 80, 0x201A, -40, 40), +}; + +CameraModeValue sSetMeadowBirdsEyeModeBattleData[] = { + CAM_FUNCDATA_PARA1(-20, 500, 80, 0, 5, 5, 60, 80, 0x201A, -40, 40), +}; + +CameraModeValue sSetMeadowBirdsEyeModeClimbData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 80, 20, 10, 80, 60, 20, 0x0012), +}; + +/** + *===================================================================== + * Custom Data: MEADOW_UNUSED Setting + *===================================================================== + */ + +CameraModeValue sSetMeadowUnusedModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 750, 750, 80, 20, 10, 70, 70, 80, 0x0012), +}; + +CameraModeValue sSetMeadowUnusedModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 750, 80, 0, 5, 5, 70, 80, 0x201A, -40, 40), +}; + +CameraModeValue sSetMeadowUnusedModeBattleData[] = { + CAM_FUNCDATA_PARA1(-20, 750, 80, 0, 5, 5, 70, 80, 0x200A, -40, 40), +}; + +CameraModeValue sSetMeadowUnusedModeClimbData[] = { + CAM_FUNCDATA_NORM1(-20, 750, 750, 80, 20, 10, 80, 70, 20, 0x0012), +}; + +/** + *===================================================================== + * Custom Data: FIRE_BIRDS_EYE Setting + *===================================================================== + */ + +CameraModeValue sSetFireBirdsEyeModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 80, 20, 10, 70, 70, 80, 0x0002), +}; + +CameraModeValue sSetFireBirdsEyeModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 500, 80, 0, 5, 5, 70, 80, 0x200A, -40, 40), +}; + +CameraModeValue sSetFireBirdsEyeModeBattleData[] = { + CAM_FUNCDATA_PARA1(-20, 500, 80, 0, 5, 5, 60, 80, 0x200A, -40, 40), +}; + +CameraModeValue sSetFireBirdsEyeModeClimbData[] = { + CAM_FUNCDATA_NORM1(-20, 500, 500, 80, 20, 10, 80, 60, 20, 0x0002), +}; + +/** + *===================================================================== + * Custom Data: TURN_AROUND Setting + *===================================================================== + */ + +CameraModeValue sSetTurnAroundModeNormalData[] = { + CAM_FUNCDATA_KEEP4(-30, 120, -10, 170, 0, 60, 0x2502, 25, 6), +}; + +/** + *===================================================================== + * Custom Data: PIVOT_VERTICAL Setting + *===================================================================== + */ + +CameraModeValue sSetPivotVerticalModeNormalData[] = { + CAM_FUNCDATA_SPEC0(20, 0x3200), +}; + +/** + *===================================================================== + * Custom Data: NORMAL2/4 Setting + *===================================================================== + */ + +CameraModeValue sSetNormal2and4ModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 200, 300, 10, 12, 10, 35, 60, 60, 0x0002), +}; + +/** + *===================================================================== + * Custom Data: FISHING Setting + *===================================================================== + */ + +CameraModeValue sSetFishingModeNormalData[] = { + CAM_FUNCDATA_NORM1(0, 200, 300, 20, 12, 10, 35, 55, 60, 0x0F02), +}; + +CameraModeValue sSetFishingModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 250, 0, 0, 5, 5, 45, 50, 0x2F0A, -40, 20), +}; + +CameraModeValue sSetFishingModeFollowTargetData[] = { + CAM_FUNCDATA_BATT1(-20, 250, 0, 80, 0, 0, 25, 55, 80, 0x2F02, -40, 25), +}; + +CameraModeValue sSetFishingModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-30, 70, 200, 40, 10, 0, 5, 70, 45, 50, 10, 0x3F20), +}; + +CameraModeValue sSetFishingModeFirstPersonData[] = { + CAM_FUNCDATA_SUBJ3(0, 5, 50, 10, 0, 0, 0, 45, 0x0F00), +}; + +CameraModeValue sSetFishingModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-20, 200, 300, 12, 35, 60, 40, 0x0F00), +}; + +CameraModeValue sSetFishingModeFreeFallData[] = { + CAM_FUNCDATA_JUMP1(-20, 200, 300, 15, 80, 60, 20, 0x0F00), +}; + +CameraModeValue sSetFishingModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-80, 200, 300, 40, 60, 10, 0x0F00), +}; + +CameraModeValue sSetFishingModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-120, 300, 300, 70, 45, 10, 0x2F00), +}; + +/** + *===================================================================== + * Custom Data: CS_C Setting + *===================================================================== + */ + +CameraModeValue sSetCsCModeNormalData[] = { + CAM_FUNCDATA_FLAGS(0x3F00), +}; + +/** + *===================================================================== + * Custom Data: JABU_TENTACLE Setting + *===================================================================== + */ + +CameraModeValue sSetJabuTentacleModeNormalData[] = { + CAM_FUNCDATA_NORM1_ALT(30, 200, 300, -20, 15, 5, 50, 70, 70, 0x0003), +}; + +CameraModeValue sSetJabuTentacleModeBattleData[] = { + CAM_FUNCDATA_BATT1(-30, 160, 10, 10, 0, 0, 70, 60, 40, 0x2002, -50, 20), +}; + +/** + *===================================================================== + * Custom Data: DUNGEON2 Setting + *===================================================================== + */ + +CameraModeValue sSetDungeon2ModeNormalData[] = { + CAM_FUNCDATA_NORM1(-20, 350, 350, 20, 15, 5, 30, 60, 60, 0x0003), +}; + +CameraModeValue sSetDungeon2ModeTargetData[] = { + CAM_FUNCDATA_PARA1(-20, 200, 0, 0, 5, 5, 45, 50, 0x200A, -40, 20), +}; + +CameraModeValue sSetDungeon2ModeBattleData[] = { + CAM_FUNCDATA_BATT1(-20, 180, 10, 80, -10, 10, 25, 45, 80, 0x2002, -40, 25), +}; + +CameraModeValue sSetDungeon2ModeJumpData[] = { + CAM_FUNCDATA_JUMP1(-20, 350, 350, 10, 50, 60, 40, 0x0000), +}; + +CameraModeValue sSetDungeon2ModeFreeFallData[] = { + CAM_FUNCDATA_JUMP1(-20, 350, 350, 15, 80, 60, 20, 0x0000), +}; + +CameraModeValue sSetDungeon2ModeClimbData[] = { + CAM_FUNCDATA_JUMP2(-40, 350, 350, 20, 5, 5, 60, 40, 0x0000), +}; + +CameraModeValue sSetDungeon2ModeClimbZData[] = { + CAM_FUNCDATA_JUMP2(-40, 350, 350, 20, 999, 5, 60, 40, 0x2006), +}; + +CameraModeValue sSetDungeon2ModeHangData[] = { + CAM_FUNCDATA_UNIQ1(-50, 350, 350, 40, 60, 10, 0x0000), +}; + +CameraModeValue sSetDungeon2ModeHangZData[] = { + CAM_FUNCDATA_UNIQ1(-100, 350, 350, 70, 45, 10, 0x2000), +}; + +CameraModeValue sSetDungeon2ModeStillData[] = { + CAM_FUNCDATA_NORM1(-10, 350, 350, 20, 100, 5, 100, 60, 5, 0xF003), +}; + +CameraModeValue sSetDungeon2ModePushPullData[] = { + CAM_FUNCDATA_PARA1(0, 280, 25, 0, 5, 5, 70, 30, 0x206A, -20, 30), +}; + +/** + *===================================================================== + * Custom Data: DIRECTED_YAW Setting + *===================================================================== + */ + +CameraModeValue sSetDirectedYawModeNormalData[] = { + CAM_FUNCDATA_NORM1(-10, 280, 320, -8, 20, 10, 80, 60, 80, 0x0002), +}; + +CameraModeValue sSetDirectedYawModeFollowTargetData[] = { + CAM_FUNCDATA_KEEP1(-20, 180, 200, 35, 45, 0, -5, 20, 50, 50, 0x2001, -50, 30), +}; + +CameraModeValue sSetDirectedYawModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-80, 200, 250, 30, 10, -8, -8, 30, 50, 50, 10, 0x3520), +}; + +/** + *===================================================================== + * Custom Data: NORMAL4 Setting + *===================================================================== + */ + +CameraModeValue sNormal4ModeTalkData[] = { + CAM_FUNCDATA_KEEP3(-30, 70, 200, 40, 10, 0, 5, 70, 45, 50, 10, 0x35A0), +}; + +/** + * ===================================================================== + * CAMERA SETTINGS: USAGE OF FUNCTIONS AND DATA FOR SPECIFIC MODES + * ===================================================================== + */ + +#define CAM_SETTING_MODE_ENTRY(func, data) \ + { func, ARRAY_COUNT(data), data } + +CameraMode sCamSetNormal0Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeFreeFallData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetNormal1Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal1ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal1ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal1ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal1ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal1ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal1ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal1ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal1ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal1ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal1ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal1ModeFreeFallData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal1ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal1ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetDungeon0Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDungeon0ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetDungeon0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetDungeon0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetDungeon0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal1ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon0ModeFreeFallData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetDungeon0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDungeon0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetDungeon1Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDungeon1ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetDungeon0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal1ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetDungeon1ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetDungeon0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon1ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal1ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon1ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon1ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon1ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon1ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon1ModeFreeFallData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetDungeon1ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDungeon1ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetDungeon1ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetNormal3Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP3, sSetNormal3ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal3ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal3ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP3, sSetNormal3ModeBoomerangData), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), +}; + +CameraMode sCamSetHorseModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM3, sSetHorseModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM3, sSetHorseModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetHorseModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetHorseModeTalkData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetHorseModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), +}; + +CameraMode sCamSetBossGohmaModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossGohmaModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossGohmaModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetDungeon0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossDodongoModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossDodongoModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossDodongoModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossBarinadeModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossBarinadeModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossBarinadeModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossPhantomGanonModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossPhantomGanonModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossPhantomGanonModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossVolvagiaModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossVolvagiaModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossVolvagiaModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossBongoModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossBongoModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossBongoModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossBongoModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossBongoModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossMorphaModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossMorphaModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossMorphaModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossTwinrovaPlatformModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossTwinrovaPlatformModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossTwinrovaModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossTwinrovaFloorModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossTwinrovaFloorModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossTwinrovaModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossGanondorfModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossGanondorfModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossPhantomGanonModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetBossGanondorfModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBossGanonModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBossGanonModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBossGanonModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetTowerClimbModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM2, sSetTowerClimbModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM2, sSetTowerClimbModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetTowerUnusedModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM2, sSetTowerUnusedModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM2, sSetTowerUnusedModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetMarketBalconyModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD1, sSetMarketBalconyModeNormalData), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD1, sSetMarketBalconyModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD1, sSetMarketBalconyModeTalkData), +}; + +CameraMode sCamSetChuBowlingModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD1, sSetChuBowlingModeNormalData), +}; + +CameraMode sCamSetPivotCrawlspaceModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD2, sSetPivotCrawlspaceModeNormalData), +}; + +CameraMode sCamSetPivotShopBrowsingModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DATA4, sSetPivotShopBrowsingModeNormalData), +}; + +CameraMode sCamSetPivotInFrontModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD4, sSetPivotInFrontAndFromSideModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal1ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetPreRendFixedModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD3, sDataOnlyNullFlags), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD3, sSetPrerendFixedModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD3, sSetPrerendFixedModeFollowTargetData), +}; + +CameraMode sCamSetPreRendPivotModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ7, sSetPrerendPivotModeNormalData), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ7, sSetPrerendPivotModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP0, sSetPrerendPivotModeTalkData), +}; + +CameraMode sCamSetPreRendSideScrollModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC6, sDataOnlyNullFlags), +}; + +CameraMode sCamSetDoor0Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ3, sSetDoor0ModeNormalData), +}; + +CameraMode sCamSetDoorCModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC9, sSetDoorCModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC9, sSetDoorCModeTargetData), +}; + +CameraMode sCamSetCrawlspaceModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ4, sSetCrawlspaceModeNormalData), +}; + +CameraMode sCamSetStart0Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ0, sDataOnlyNullFlags), +}; + +CameraMode sCamSetStart1Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ0, sSetStart1ModeNormalData), +}; + +CameraMode sCamSetFree0Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ6, sSetFree0ModeNormalData), +}; + +CameraMode sCamSetFree1Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ6, sSetFree1ModeNormalData), +}; + +CameraMode sCamSetPivotCornerModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD2, sSetPivotCornerModeNormalData), +}; + +CameraMode sCamSetPivotDivingModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ2, sSetPivotWaterSurfaceModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ2, sSetPivotWaterSurfaceModeTargetData), +}; + +CameraMode sCamSetCs0Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO1, sDataOnlyInterfaceFlags), +}; + +CameraMode sCamSetCsTwistedHallwayModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO2, sDataOnlyInterfaceFlags), +}; + +CameraMode sCamSetForestBirdsEyeModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetForestBirdsEyeModeNormalData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA3, sSetForestBirdsEyeModeTalkData), +}; + +CameraMode sCamSetSlowChestCsModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO3, sSetSlowChestCsModeNormalData), +}; + +CameraMode sCamSetItemUnusedModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO4, sSetSlowChestCsModeNormalData), +}; + +CameraMode sCamSetCs3Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO9, sSetCs3ModeNormalData), +}; + +CameraMode sCamSetCsAttentionModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO5, sDataOnlyInterfaceFlags), +}; + +CameraMode sCamSetBeanGenericModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBeanGenericModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetBeanGenericModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal1ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetBeanGenericModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetBeanGenericModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetBeanGenericModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBeanGenericModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetBeanLostWoodsModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBeanLostWoodsModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetBeanLostWoodsModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal1ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetBeanLostWoodsModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetBeanLostWoodsModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetBeanLostWoodsModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBeanLostWoodsModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetSceneUnusedModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC9, sSetSceneUnusedModeNormalData), +}; + +CameraMode sCamSetSceneTransitionModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ2, sSetSceneTransitionModeNormalData), +}; + +CameraMode sCamSetFirePlatformModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC7, sDataOnlyNullFlags), +}; + +CameraMode sCamSetFireStaircaseModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC4, sDataOnlyInterfaceFlags), +}; + +CameraMode sCamSetForestUnusedModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ5, sDataOnlyInterfaceFlags), +}; + +CameraMode sCamSetForestDefeatPoeModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_DEMO6, sDataOnlyInterfaceFlags), +}; + +CameraMode sCamSetBigOctoModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBigOctoModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetDungeon0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetDungeon0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetBigOctoModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal1ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon0ModeFreeFallData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetDungeon0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetBigOctoModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetMeadowBirdsEyeModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetMeadowBirdsEyeModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetMeadowBirdsEyeModeTargetData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetMeadowBirdsEyeModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetMeadowBirdsEyeModeClimbData), +}; + +CameraMode sCamSetMeadowUnusedModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetMeadowUnusedModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetMeadowUnusedModeTargetData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetMeadowUnusedModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetMeadowUnusedModeClimbData), +}; + +CameraMode sCamSetFireBirdsEyeModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetFireBirdsEyeModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetFireBirdsEyeModeTargetData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetFireBirdsEyeModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetFireBirdsEyeModeClimbData), +}; + +CameraMode sCamSetTurnAroundModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP4, sSetTurnAroundModeNormalData), +}; + +CameraMode sCamSetPivotVerticalModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC0, sSetPivotVerticalModeNormalData), +}; + +CameraMode sCamSetNorm2Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal2and4ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal1ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetFishingModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetFishingModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetFishingModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetFishingModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetFishingModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetFishingModeFollowTargetData), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetFishingModeFirstPersonData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetFishingModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetFishingModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetFishingModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetFishingModeFreeFallData), +}; + +CameraMode sCamSetCsCModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ9, sSetCsCModeNormalData), +}; + +CameraMode sCamSetJabuTentacleModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetJabuTentacleModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetJabuTentacleModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetDungeon2Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDungeon2ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetDungeon2ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetDungeon0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetDungeon2ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon2ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal1ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetDungeon2ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon2ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon2ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetDungeon2ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetDungeon2ModeFreeFallData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDungeon2ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetDungeon2ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetDirectedYawModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetDirectedYawModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetDirectedYawModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetDirectedYawModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal0ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraMode sCamSetPivotFromSideModes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD4, sSetPivotInFrontAndFromSideModeNormalData), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_FIXD4, sSetPivotInFrontAndFromSideModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sSetNormal0ModeTalkData), + { CAM_FUNC_NONE, 0, NULL }, + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + { CAM_FUNC_NONE, 0, NULL }, + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), +}; + +CameraMode sCamSetNormal4Modes[] = { + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal2and4ModeNormalData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModeTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowTargetData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP3, sNormal4ModeTalkData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT1, sSetNormal1ModeBattleData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeFirstPersonData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBowArrowZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SPEC5, sSetNormal0ModeHookshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeBoomerangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_SUBJ3, sSetNormal0ModeSlingshotData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP2, sSetNormal0ModeClimbZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_UNIQ1, sSetNormal0ModeHangZData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_JUMP1, sSetNormal0ModeJumpData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_BATT4, sSetNormal0ModeChargeData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_NORM1, sSetNormal0ModeStillData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_PARA1, sSetNormal0ModePushPullData), + CAM_SETTING_MODE_ENTRY(CAM_FUNC_KEEP1, sSetNormal0ModeFollowBoomerangData), +}; + +CameraSetting sCameraSettings[] = { + { { 0x00000000 }, NULL }, + { { 0x051FFFFF }, sCamSetNormal0Modes }, + { { 0x051FFFFF }, sCamSetNormal1Modes }, + { { 0x051FFFFF }, sCamSetDungeon0Modes }, + { { 0x051FFFFF }, sCamSetDungeon1Modes }, + { { 0x050FF7FF }, sCamSetNormal3Modes }, + { { 0x8500018F }, sCamSetHorseModes }, + { { 0x051FFFFF }, sCamSetBossGohmaModes }, + { { 0x051FFFFF }, sCamSetBossDodongoModes }, + { { 0x051FFFFF }, sCamSetBossBarinadeModes }, + { { 0x051FFFFF }, sCamSetBossPhantomGanonModes }, + { { 0x051FFFFF }, sCamSetBossVolvagiaModes }, + { { 0x051FFFFF }, sCamSetBossBongoModes }, + { { 0x051FFFFF }, sCamSetBossMorphaModes }, + { { 0x051FFFFF }, sCamSetBossTwinrovaPlatformModes }, + { { 0x051FFFFF }, sCamSetBossTwinrovaFloorModes }, + { { 0x051FFFFF }, sCamSetBossGanondorfModes }, + { { 0x051FFFFF }, sCamSetBossGanonModes }, + { { 0x851FFFFF }, sCamSetTowerClimbModes }, + { { 0x851FFFFF }, sCamSetTowerUnusedModes }, + { { 0x8500000D }, sCamSetMarketBalconyModes }, + { { 0x85000001 }, sCamSetChuBowlingModes }, + { { 0x85000001 }, sCamSetPivotCrawlspaceModes }, + { { 0x85000001 }, sCamSetPivotShopBrowsingModes }, + { { 0x851E1FFF }, sCamSetPivotInFrontModes }, + { { 0x8C00000D }, sCamSetPreRendFixedModes }, + { { 0x8C00000D }, sCamSetPreRendPivotModes }, + { { 0x8C000001 }, sCamSetPreRendSideScrollModes }, + { { 0xC5000001 }, sCamSetDoor0Modes }, + { { 0xC5000003 }, sCamSetDoorCModes }, + { { 0xC5000001 }, sCamSetCrawlspaceModes }, + { { 0xC5000001 }, sCamSetStart0Modes }, + { { 0xC5000001 }, sCamSetStart1Modes }, + { { 0x05000001 }, sCamSetFree0Modes }, + { { 0x05000001 }, sCamSetFree1Modes }, + { { 0x85000001 }, sCamSetPivotCornerModes }, + { { 0x05000003 }, sCamSetPivotDivingModes }, + { { 0xCE000001 }, sCamSetCs0Modes }, + { { 0x4E000001 }, sCamSetCsTwistedHallwayModes }, + { { 0x05000009 }, sCamSetForestBirdsEyeModes }, + { { 0x45000001 }, sCamSetSlowChestCsModes }, + { { 0x45000001 }, sCamSetItemUnusedModes }, + { { 0x45000001 }, sCamSetCs3Modes }, + { { 0x45000001 }, sCamSetCsAttentionModes }, + { { 0x451FFFFF }, sCamSetBeanGenericModes }, + { { 0x451FFFFF }, sCamSetBeanLostWoodsModes }, + { { 0xC5000001 }, sCamSetSceneUnusedModes }, + { { 0x45000001 }, sCamSetSceneTransitionModes }, + { { 0x05000001 }, sCamSetFirePlatformModes }, + { { 0x45000001 }, sCamSetFireStaircaseModes }, + { { 0x45000001 }, sCamSetForestUnusedModes }, + { { 0x45000001 }, sCamSetForestDefeatPoeModes }, + { { 0x451FFFFF }, sCamSetBigOctoModes }, + { { 0x05000033 }, sCamSetMeadowBirdsEyeModes }, + { { 0x05000033 }, sCamSetMeadowUnusedModes }, + { { 0x05000033 }, sCamSetFireBirdsEyeModes }, + { { 0x4A000001 }, sCamSetTurnAroundModes }, + { { 0x05000001 }, sCamSetPivotVerticalModes }, + { { 0x051FFFFF }, sCamSetNorm2Modes }, + { { 0x0501E05F }, sCamSetFishingModes }, + { { 0x45000001 }, sCamSetCsCModes }, + { { 0x051FFFFF }, sCamSetJabuTentacleModes }, + { { 0x051FFFFF }, sCamSetDungeon2Modes }, + { { 0x051FFFFF }, sCamSetDirectedYawModes }, + { { 0xC5000ECD }, sCamSetPivotFromSideModes }, + { { 0x051FFFFF }, sCamSetNormal4Modes }, +}; + +s32 Camera_Normal0(Camera* camera); +s32 Camera_Normal1(Camera* camera); +s32 Camera_Normal2(Camera* camera); +s32 Camera_Normal3(Camera* camera); +s32 Camera_Normal4(Camera* camera); +s32 Camera_Parallel0(Camera* camera); +s32 Camera_Parallel1(Camera* camera); +s32 Camera_Parallel2(Camera* camera); +s32 Camera_Parallel3(Camera* camera); +s32 Camera_Parallel4(Camera* camera); +s32 Camera_KeepOn0(Camera* camera); +s32 Camera_KeepOn1(Camera* camera); +s32 Camera_KeepOn2(Camera* camera); +s32 Camera_KeepOn3(Camera* camera); +s32 Camera_KeepOn4(Camera* camera); +s32 Camera_Subj0(Camera* camera); +s32 Camera_Subj1(Camera* camera); +s32 Camera_Subj2(Camera* camera); +s32 Camera_Subj3(Camera* camera); +s32 Camera_Subj4(Camera* camera); +s32 Camera_Jump0(Camera* camera); +s32 Camera_Jump1(Camera* camera); +s32 Camera_Jump2(Camera* camera); +s32 Camera_Jump3(Camera* camera); +s32 Camera_Jump4(Camera* camera); +s32 Camera_Battle0(Camera* camera); +s32 Camera_Battle1(Camera* camera); +s32 Camera_Battle2(Camera* camera); +s32 Camera_Battle3(Camera* camera); +s32 Camera_Battle4(Camera* camera); +s32 Camera_Fixed0(Camera* camera); +s32 Camera_Fixed1(Camera* camera); +s32 Camera_Fixed2(Camera* camera); +s32 Camera_Fixed3(Camera* camera); +s32 Camera_Fixed4(Camera* camera); +s32 Camera_Data0(Camera* camera); +s32 Camera_Data1(Camera* camera); +s32 Camera_Data2(Camera* camera); +s32 Camera_Data3(Camera* camera); +s32 Camera_Data4(Camera* camera); +s32 Camera_Unique0(Camera* camera); +s32 Camera_Unique1(Camera* camera); +s32 Camera_Unique2(Camera* camera); +s32 Camera_Unique3(Camera* camera); +s32 Camera_Unique4(Camera* camera); +s32 Camera_Unique5(Camera* camera); +s32 Camera_Unique6(Camera* camera); +s32 Camera_Unique7(Camera* camera); +s32 Camera_Unique8(Camera* camera); +s32 Camera_Unique9(Camera* camera); +s32 Camera_Demo0(Camera* camera); +s32 Camera_Demo1(Camera* camera); +s32 Camera_Demo2(Camera* camera); +s32 Camera_Demo3(Camera* camera); +s32 Camera_Demo4(Camera* camera); +s32 Camera_Demo5(Camera* camera); +s32 Camera_Demo6(Camera* camera); +s32 Camera_Demo7(Camera* camera); +s32 Camera_Demo8(Camera* camera); +s32 Camera_Demo9(Camera* camera); +s32 Camera_Special0(Camera* camera); +s32 Camera_Special1(Camera* camera); +s32 Camera_Special2(Camera* camera); +s32 Camera_Special3(Camera* camera); +s32 Camera_Special4(Camera* camera); +s32 Camera_Special5(Camera* camera); +s32 Camera_Special6(Camera* camera); +s32 Camera_Special7(Camera* camera); +s32 Camera_Special8(Camera* camera); +s32 Camera_Special9(Camera* camera); + +s32 (*sCameraFunctions[])(Camera*) = { + NULL, + Camera_Normal0, + Camera_Normal1, + Camera_Normal2, + Camera_Normal3, + Camera_Normal4, + Camera_Parallel0, + Camera_Parallel1, + Camera_Parallel2, + Camera_Parallel3, + Camera_Parallel4, + Camera_KeepOn0, + Camera_KeepOn1, + Camera_KeepOn2, + Camera_KeepOn3, + Camera_KeepOn4, + Camera_Subj0, + Camera_Subj1, + Camera_Subj2, + Camera_Subj3, + Camera_Subj4, + Camera_Jump0, + Camera_Jump1, + Camera_Jump2, + Camera_Jump3, + Camera_Jump4, + Camera_Battle0, + Camera_Battle1, + Camera_Battle2, + Camera_Battle3, + Camera_Battle4, + Camera_Fixed0, + Camera_Fixed1, + Camera_Fixed2, + Camera_Fixed3, + Camera_Fixed4, + Camera_Data0, + Camera_Data1, + Camera_Data2, + Camera_Data3, + Camera_Data4, + Camera_Unique0, + Camera_Unique1, + Camera_Unique2, + Camera_Unique3, + Camera_Unique4, + Camera_Unique5, + Camera_Unique6, + Camera_Unique7, + Camera_Unique8, + Camera_Unique9, + Camera_Demo0, + Camera_Demo1, + Camera_Demo2, + Camera_Demo3, + Camera_Demo4, + Camera_Demo5, + Camera_Demo6, + Camera_Demo7, + Camera_Demo8, + Camera_Demo9, + Camera_Special0, + Camera_Special1, + Camera_Special2, + Camera_Special3, + Camera_Special4, + Camera_Special5, + Camera_Special6, + Camera_Special7, + Camera_Special8, + Camera_Special9, +}; + +s32 sInitRegs = 1; + +s32 gDbgCamEnabled = 0; +s32 sDbgModeIdx = -1; +s16 sNextUID = 0; + +s32 sCameraInterfaceFlags = 1; + +s32 sCameraInterfaceAlpha = 0x02; +s32 sCameraShrinkWindowVal = 0x20; +s32 D_8011D3AC = -1; + +s16 D_8011D3B0[] = { + 0x0AAA, 0xF556, 0x1555, 0xEAAB, 0x2AAA, 0xD556, 0x3FFF, 0xC001, 0x5555, 0xAAAB, 0x6AAA, 0x9556, 0x7FFF, 0x0000, +}; + +s16 D_8011D3CC[] = { + 0x0000, 0x02C6, 0x058C, 0x0000, 0x0000, 0xFD3A, 0x0000, 0x0852, 0x0000, 0x0000, 0x0B18, 0x02C6, 0xFA74, 0x0000, +}; + +s32 sUpdateCameraDirection = 0; +s32 D_8011D3EC = 0; +s32 D_8011D3F0 = 0; + +s32 sDemo5PrevAction12Frame = -16; + +char sCameraFunctionNames[][8] = { + "NONE ", "NORM0()", "NORM1()", "NORM2()", "NORM3()", "NORM4()", "PARA0()", "PARA1()", "PARA2()", "PARA3()", + "PARA4()", "KEEP0()", "KEEP1()", "KEEP2()", "KEEP3()", "KEEP4()", "SUBJ0()", "SUBJ1()", "SUBJ2()", "SUBJ3()", + "SUBJ4()", "JUMP0()", "JUMP1()", "JUMP2()", "JUMP3()", "JUMP4()", "BATT0()", "BATT1()", "BATT2()", "BATT3()", + "BATT4()", "FIXD0()", "FIXD1()", "FIXD2()", "FIXD3()", "FIXD4()", "DATA0()", "DATA1()", "DATA2()", "DATA3()", + "DATA4()", "UNIQ0()", "UNIQ1()", "UNIQ2()", "UNIQ3()", "UNIQ4()", "UNIQ5()", "UNIQ6()", "UNIQ7()", "UNIQ8()", + "UNIQ9()", "DEMO0()", "DEMO1()", "DEMO2()", "DEMO3()", "DEMO4()", "DEMO5()", "DEMO6()", "DEMO7()", "DEMO8()", + "DEMO9()", "SPEC0()", "SPEC1()", "SPEC2()", "SPEC3()", "SPEC4()", "SPEC5()", "SPEC6()", "SPEC7()", "SPEC8()", + "SPEC9()", "", "", "", "", "", +}; + +VecSph D_8011D658[] = { + { 50.0f, 0xEE3A, 0xD558 }, + { 75.0f, 0x0000, 0x8008 }, + { 80.0f, 0xEE3A, 0x8008 }, + { 15.0f, 0xEE3A, 0x8008 }, +}; + +Vec3f D_8011D678[] = { + { 0.0f, 40.0f, 20.0f }, + { 0.0f, 40.0f, 0.0f }, + { 0.0f, 3.0f, -3.0f }, + { 0.0f, 3.0f, -3.0 }, +}; + +/******************************************************* + * OnePoint initalization values for Demo5 + ********************************************************/ +s32 sDemo5PrevSfxFrame = -200; + +// target is player, far from eye +OnePointCsFull D_8011D6AC[] = { + { // initflags & 0x00FF (at): 2, atTarget is view lookAt + atInit + // initFlags & 0xFF00 (eye): none + // action: 15, copy at, eye, roll, fov to camera + // result: eye remains in the same locaiton, at is View's lookAt + 0x8F, + 0xFF, + 0x0002, + 0x0001, + 0x0000, + 60.0f, + 1.0f, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f } }, + { // initFlags & 0x00FF (at): 3, atTarget is camera's current at + atInit + // initFlags & 0xFF00 (eye): 3, eyeTarget is the camera's current eye + eyeInit + // action: interplate eye and at. + // result: eye and at's y interpolate to become +20 from their current location. + 0x81, + 0xFF, + 0x0303, + 0x0013, + 0x0000, + 45.0f, + 1.0f, + { 0.0f, 20.0f, 0.0f }, + { 0.0f, 20.0f, 0.0f } }, + { // initFlags & 0x00FF (at): 0 none + // initFlags & 0xFF00 (eye): 0 none + // action: 18, copy this camera to default camera. + 0x12, + 0xFF, + 0x0000, + 0x0001, + 0x0000, + 60.0f, + 1.0f, + { -1.0f, -1.0f, -1.0f }, + { -1.0f, -1.0f, -1.0f } }, +}; + +// target is player close to current eye +OnePointCsFull D_8011D724[] = { + { 0x8F, 0xFF, 0x2424, 0x0001, 0x0000, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 10.0f, -20.0f } }, + { 0x81, 0xFF, 0x2121, 0x0013, 0x0000, 50.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 0.0f, 60.0f } }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +// target is close to player +OnePointCsFull D_8011D79C[] = { + { 0xCF, 0xFF, 0x0002, 0x0001, 0x0000, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0xC1, 0xFF, 0x0303, 0x0013, 0x0000, 45.0f, 1.0f, { 0.0f, -20.0f, 0.0f }, { 0.0f, -10.0f, 5.0f } }, + { + 0xC1, + 0xFF, + 0x0303, + 0x0009, + 0x0000, + 60.0f, + 1.0f, + { 0.0f, 10.0f, 0.0f }, + { 0.0f, 10.0f, 0.0f }, + }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +// target is within 300 units of eye, and player is within 30 units of eye +OnePointCsFull D_8011D83C[] = { + { 0x83, 0xFF, 0x2141, 0x0014, 0x0000, 45.0f, 0.2f, { 0.0f, 0.0f, 10.0f }, { 0.0f, 0.0f, 10.0f } }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +// target is within 700 units of eye, angle between player/eye and target/eye is less than +// 76.9 degrees. The x/y coordinates of the target on screen is between (21, 41) and (300, 200), +// and the player is farther than 30 units of the eye +OnePointCsFull D_8011D88C[] = { + { 0x81, 0xFF, 0x0303, 0x0014, 0x0000, 45.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +// same as above, but the target is NOT within the screen area. +OnePointCsFull D_8011D8DC[] = { + { 0x8F, 0xFF, 0x0404, 0x0014, 0x0001, 50.0f, 1.0f, { 0.0f, 5.0f, 10.0f }, { 0.0f, 10.0f, -80.0f } }, + { 0x82, 0xFF, 0x2121, 0x0005, 0x0000, 60.0f, 1.0f, { 0.0f, 5.0f, 0.0f }, { 5.0f, 5.0f, -200.0f } }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +// target is a door. +OnePointCsFull D_8011D954[] = { + { 0x0F, 0xFF, 0xC1C1, 0x0014, 0x0000, 60.0f, 1.0f, { 0.0f, 0.0f, 50.0f }, { 0.0f, 0.0f, 250.0f } }, + { 0x83, 0xFF, 0x05B1, 0x0005, 0x0000, 60.0f, 0.1f, { 0.0f, 10.0f, 50.0f }, { 0.0f, 10.0f, 100.0f } }, + { 0x82, 0xFF, 0x2121, 0x0005, 0x0002, 60.0f, 1.0f, { 0.0f, 10.0f, 0.0f }, { 0.0f, 20.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +// otherwise +OnePointCsFull D_8011D9F4[] = { + { 0x8F, 0xFF, 0x0504, 0x0014, 0x0002, 60.0f, 1.0f, { 0.0f, 5.0f, 50.0f }, { 0.0f, 20.0f, 300.0f } }, + { 0x82, 0xFF, 0x2121, 0x0005, 0x0002, 60.0f, 1.0f, { 0.0f, 10.0f, 0.0f }, { 0.0f, 20.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 0x0001, 0x0000, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +Vec3f D_8011DA6C[] = { + { 3050.0f, 700.0f, 0.0f }, { 1755.0f, 3415.0f, -380.0f }, { -3120.0f, 3160.0f, 245.0f }, { 0.0f, -10.0f, 240.0f } +}; + +Vec3f D_8011DA9C[] = { + { 3160.0f, 2150.0f, 0.0f }, + { 1515.0f, 4130.0f, -835.0f }, + { -3040.0f, 4135.0f, 230.0f }, + { -50.0f, 600.0f, -75.0f }, +}; + +f32 D_8011DACC[] = { 1570.0f, 3680.0f, 3700.0f, 395.0f }; + +f32 D_8011DADC[] = { 320.0f, 320.0f, 320.0f, 0.0f }; + +s16 D_8011DAEC[] = { -2000, -1000, 0, 0, 0, 0, 0, 0 }; + +s16 D_8011DAFC[] = { + CAM_SET_NORMAL0, CAM_SET_NORMAL1, CAM_SET_NORMAL2, CAM_SET_DUNGEON0, CAM_SET_DUNGEON1, CAM_SET_DUNGEON2, +}; + +GlobalContext* D_8015BD7C; +DbCamera D_8015BD80; +CollisionPoly* playerFloorPoly; diff --git a/soh/src/code/z_cheap_proc.c b/soh/src/code/z_cheap_proc.c new file mode 100644 index 000000000..d611c08d2 --- /dev/null +++ b/soh/src/code/z_cheap_proc.c @@ -0,0 +1,23 @@ +#include "global.h" + +void Gfx_DrawDListOpa(GlobalContext* globalCtx, Gfx* dlist) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_cheap_proc.c", 214); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_cheap_proc.c", 216), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dlist); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_cheap_proc.c", 219); +} + +void Gfx_DrawDListXlu(GlobalContext* globalCtx, Gfx* dlist) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_cheap_proc.c", 228); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_cheap_proc.c", 230), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, dlist); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_cheap_proc.c", 233); +} diff --git a/soh/src/code/z_collision_btltbls.c b/soh/src/code/z_collision_btltbls.c new file mode 100644 index 000000000..2d8a32ce3 --- /dev/null +++ b/soh/src/code/z_collision_btltbls.c @@ -0,0 +1,826 @@ +#include "global.h" + +static DamageTable sDamageTablePresets[] = { + { { + // 0 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0xE), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(2, 0xF), + /* Fire arrow */ DMG_ENTRY(2, 0x2), + /* Ice arrow */ DMG_ENTRY(1, 0x0), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(2, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 1 Used by En_Karebaba + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0xE), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(2, 0xF), + /* Fire arrow */ DMG_ENTRY(2, 0x2), + /* Ice arrow */ DMG_ENTRY(1, 0x0), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(2, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 2 Used by En_St, En_Ssh + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x0), + /* Light arrow */ DMG_ENTRY(4, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(3, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(4, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(4, 0x0), + /* Master jump */ DMG_ENTRY(8, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 3 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(1, 0x0), + /* Hookshot */ DMG_ENTRY(1, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(1, 0x0), + /* Giant's Knife */ DMG_ENTRY(1, 0x0), + /* Fire arrow */ DMG_ENTRY(1, 0x0), + /* Ice arrow */ DMG_ENTRY(1, 0x0), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(1, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(1, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(1, 0x0), + /* Fire magic */ DMG_ENTRY(1, 0x0), + /* Ice magic */ DMG_ENTRY(1, 0x0), + /* Light magic */ DMG_ENTRY(1, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(2, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(2, 0x0), + /* Master jump */ DMG_ENTRY(2, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 4 Used by En_Dodojr + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x0), + /* Light arrow */ DMG_ENTRY(4, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(4, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(4, 0x0), + /* Master jump */ DMG_ENTRY(8, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 5 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(1, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(1, 0x0), + /* Giant's Knife */ DMG_ENTRY(1, 0x0), + /* Fire arrow */ DMG_ENTRY(1, 0x0), + /* Ice arrow */ DMG_ENTRY(1, 0x3), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(1, 0x3), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(1, 0x0), + /* Master spin */ DMG_ENTRY(1, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 6 + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(3, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(6, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(4, 0x0), + /* Hammer swing */ DMG_ENTRY(4, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(2, 0x0), + /* Master sword */ DMG_ENTRY(6, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(6, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 7 + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(3, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(6, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(4, 0x0), + /* Hammer swing */ DMG_ENTRY(4, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(2, 0x0), + /* Master sword */ DMG_ENTRY(6, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(6, 0x0), + /* Ice arrow */ DMG_ENTRY(6, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 8 + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(3, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(4, 0x0), + /* Hammer swing */ DMG_ENTRY(4, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(2, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 9 Used by En_Bubble + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(4, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(4, 0x0), + /* Master jump */ DMG_ENTRY(8, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 10 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(2, 0xE), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(2, 0xF), + /* Fire arrow */ DMG_ENTRY(2, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0x3), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(2, 0x2), + /* Ice magic */ DMG_ENTRY(2, 0x3), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 11 Used by En_Horse + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(9, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 12 + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(4, 0x0), + /* Hammer swing */ DMG_ENTRY(4, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(2, 0x0), + /* Master sword */ DMG_ENTRY(4, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 13 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(2, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x2), + /* Ice arrow */ DMG_ENTRY(1, 0x0), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(2, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(1, 0x0), + /* Master spin */ DMG_ENTRY(1, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 14 Used by En_Sw + /* Deku nut */ DMG_ENTRY(1, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x0), + /* Light arrow */ DMG_ENTRY(4, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(4, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(4, 0x0), + /* Master jump */ DMG_ENTRY(8, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 15 Used by En_Fd + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 16 Used by En_Fw + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x0), + /* Light arrow */ DMG_ENTRY(4, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(4, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(4, 0x0), + /* Master jump */ DMG_ENTRY(8, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 17 + /* Deku nut */ DMG_ENTRY(1, 0x0), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(1, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(1, 0x0), + /* Hookshot */ DMG_ENTRY(1, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(1, 0x0), + /* Giant's Knife */ DMG_ENTRY(1, 0x0), + /* Fire arrow */ DMG_ENTRY(1, 0x0), + /* Ice arrow */ DMG_ENTRY(1, 0x0), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(1, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(1, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(1, 0x0), + /* Fire magic */ DMG_ENTRY(1, 0x0), + /* Ice magic */ DMG_ENTRY(1, 0x0), + /* Light magic */ DMG_ENTRY(1, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 18 + /* Deku nut */ DMG_ENTRY(1, 0x0), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(1, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(1, 0x0), + /* Hookshot */ DMG_ENTRY(1, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(1, 0x0), + /* Giant's Knife */ DMG_ENTRY(1, 0x0), + /* Fire arrow */ DMG_ENTRY(1, 0x0), + /* Ice arrow */ DMG_ENTRY(1, 0x0), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(1, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(1, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(1, 0x0), + /* Fire magic */ DMG_ENTRY(1, 0x0), + /* Ice magic */ DMG_ENTRY(1, 0x0), + /* Light magic */ DMG_ENTRY(1, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 19 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(2, 0xF), + /* Fire arrow */ DMG_ENTRY(2, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0x3), + /* Light arrow */ DMG_ENTRY(1, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(2, 0x2), + /* Ice magic */ DMG_ENTRY(2, 0x3), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 20 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0xE), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0xD), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(2, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x3), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(2, 0x3), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(2, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 21 + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(1, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(2, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x1), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(2, 0x1), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, + { { + // 22 Used by En_Du, En_Go, En_Ma1, En_Ma2, En_Ma3 + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), + } }, +}; + +// Gets the pointer to one of the 23 preset damage tables. Returns NULL if index is out of range. +DamageTable* DamageTable_Get(s32 index) { + if (!(0 <= index && index < ARRAY_COUNT(sDamageTablePresets))) { + osSyncPrintf("CollisionBtlTbl_get():インデックスオーバー\n"); // "Index over" + return NULL; + } + return &sDamageTablePresets[index]; +} + +// Sets all entries in the damage table to 0x00 +void DamageTable_Clear(DamageTable* table) { + s32 i; + for (i = 0; i < 32; i++) { + table->table[i] = 0; + } +} diff --git a/soh/src/code/z_collision_check.c b/soh/src/code/z_collision_check.c new file mode 100644 index 000000000..3170eb87a --- /dev/null +++ b/soh/src/code/z_collision_check.c @@ -0,0 +1,3632 @@ +#include "global.h" +#include "vt.h" +#include "overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h" + +typedef s32 (*ColChkResetFunc)(GlobalContext*, Collider*); +typedef void (*ColChkBloodFunc)(GlobalContext*, Collider*, Vec3f*); +typedef void (*ColChkApplyFunc)(GlobalContext*, CollisionCheckContext*, Collider*); +typedef void (*ColChkVsFunc)(GlobalContext*, CollisionCheckContext*, Collider*, Collider*); +typedef s32 (*ColChkLineFunc)(GlobalContext*, CollisionCheckContext*, Collider*, Vec3f*, Vec3f*); + +typedef struct { + /* 0 */ u8 blood; + /* 1 */ u8 effect; +} HitInfo; // size = 0x2 + +typedef enum { + /* 0 */ BLOOD_NONE, + /* 1 */ BLOOD_BLUE, + /* 2 */ BLOOD_GREEN, + /* 3 */ BLOOD_WATER, + /* 4 */ BLOOD_RED, + /* 5 */ BLOOD_RED2 +} ColChkBloodType; + +typedef enum { + /* 0 */ HIT_WHITE, + /* 1 */ HIT_DUST, + /* 2 */ HIT_RED, + /* 3 */ HIT_SOLID, + /* 4 */ HIT_WOOD, + /* 5 */ HIT_NONE +} ColChkHitType; + +typedef enum { + /* 0 */ MASSTYPE_IMMOVABLE, + /* 1 */ MASSTYPE_HEAVY, + /* 2 */ MASSTYPE_NORMAL +} ColChkMassType; + +/** + * Draws a red triangle with vertices vA, vB, and vC. + */ +void Collider_DrawRedPoly(GraphicsContext* gfxCtx, Vec3f* vA, Vec3f* vB, Vec3f* vC) { + Collider_DrawPoly(gfxCtx, vA, vB, vC, 255, 0, 0); +} + +/** + * Draws the triangle with vertices vA, vB, and vC and with the specified color. + */ +void Collider_DrawPoly(GraphicsContext* gfxCtx, Vec3f* vA, Vec3f* vB, Vec3f* vC, u8 r, u8 g, u8 b) { + Vtx* vtxTbl; + Vtx* vtx; + f32 nx; + f32 ny; + f32 nz; + f32 originDist; + + OPEN_DISPS(gfxCtx, "../z_collision_check.c", 713); + + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0xFF, r, g, b, 50); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2); + gSPTexture(POLY_OPA_DISP++, 0, 0, 0, G_TX_RENDERTILE, G_OFF); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, 0, 0, 0, COMBINED, 0, 0, 0, + COMBINED); + gSPClearGeometryMode(POLY_OPA_DISP++, G_CULL_BOTH); + gSPSetGeometryMode(POLY_OPA_DISP++, G_LIGHTING); + gDPPipeSync(POLY_OPA_DISP++); + + vtxTbl = Graph_Alloc(gfxCtx, 3 * sizeof(Vtx)); + ASSERT(vtxTbl != NULL, "vtx_tbl != NULL", "../z_collision_check.c", 726); + + vtxTbl[0].n.ob[0] = vA->x; + vtxTbl[0].n.ob[1] = vA->y; + vtxTbl[0].n.ob[2] = vA->z; + vtxTbl[1].n.ob[0] = vB->x; + vtxTbl[1].n.ob[1] = vB->y; + vtxTbl[1].n.ob[2] = vB->z; + vtxTbl[2].n.ob[0] = vC->x; + vtxTbl[2].n.ob[1] = vC->y; + vtxTbl[2].n.ob[2] = vC->z; + + Math3D_DefPlane(vA, vB, vC, &nx, &ny, &nz, &originDist); + + for (vtx = vtxTbl; vtx < vtxTbl + 3; vtx++) { + vtx->n.flag = 0; + vtx->n.tc[0] = 0; + vtx->n.tc[1] = 0; + vtx->n.n[0] = (u8)(s32)nx & 0xFF; + vtx->n.n[1] = (u8)(s32)ny & 0xFF; + vtx->n.n[2] = (u8)(s32)nz & 0xFF; + vtx->n.a = 255; + } + + gSPVertex(POLY_OPA_DISP++, vtxTbl, 3, 0); + gSP1Triangle(POLY_OPA_DISP++, 0, 1, 2, 0); + + CLOSE_DISPS(gfxCtx, "../z_collision_check.c", 757); +} + +s32 Collider_InitBase(GlobalContext* globalCtx, Collider* collider) { + static Collider init = { + NULL, NULL, NULL, NULL, AT_NONE, AC_NONE, OC1_NONE, OC2_NONE, COLTYPE_HIT3, COLSHAPE_INVALID, + }; + + *collider = init; + return 1; +} + +s32 Collider_DestroyBase(GlobalContext* globalCtx, Collider* collider) { + return 1; +} + +/** + * Uses default OC2_TYPE_1 and COLTYPE_HIT0 + */ +s32 Collider_SetBaseToActor(GlobalContext* globalCtx, Collider* collider, ColliderInitToActor* src) { + collider->actor = src->actor; + collider->atFlags = src->atFlags; + collider->acFlags = src->acFlags; + collider->ocFlags1 = src->ocFlags1; + collider->ocFlags2 = OC2_TYPE_1; + collider->shape = src->shape; + return 1; +} + +/** + * Uses default OC2_TYPE_1 + */ +s32 Collider_SetBaseType1(GlobalContext* globalCtx, Collider* collider, Actor* actor, ColliderInitType1* src) { + collider->actor = actor; + collider->colType = src->colType; + collider->atFlags = src->atFlags; + collider->acFlags = src->acFlags; + collider->ocFlags1 = src->ocFlags1; + collider->ocFlags2 = OC2_TYPE_1; + collider->shape = src->shape; + return 1; +} + +s32 Collider_SetBase(GlobalContext* globalCtx, Collider* collider, Actor* actor, ColliderInit* src) { + collider->actor = actor; + collider->colType = src->colType; + collider->atFlags = src->atFlags; + collider->acFlags = src->acFlags; + collider->ocFlags1 = src->ocFlags1; + collider->ocFlags2 = src->ocFlags2; + collider->shape = src->shape; + return 1; +} + +void Collider_ResetATBase(GlobalContext* globalCtx, Collider* collider) { + collider->at = NULL; + collider->atFlags &= ~(AT_HIT | AT_BOUNCED); +} + +void Collider_ResetACBase(GlobalContext* globalCtx, Collider* collider) { + collider->ac = NULL; + collider->acFlags &= ~(AC_HIT | AC_BOUNCED); +} + +void Collider_ResetOCBase(GlobalContext* globalCtx, Collider* collider) { + collider->oc = NULL; + collider->ocFlags1 &= ~OC1_HIT; + collider->ocFlags2 &= ~OC2_HIT_PLAYER; +} + +s32 Collider_InitTouch(GlobalContext* globalCtx, ColliderTouch* touch) { + static ColliderTouch init = { 0x00000000, 0, 0 }; + + *touch = init; + return 1; +} + +s32 Collider_DestroyTouch(GlobalContext* globalCtx, ColliderTouch* touch) { + return 1; +} + +s32 Collider_SetTouch(GlobalContext* globalCtx, ColliderTouch* dest, ColliderTouch* src) { + dest->dmgFlags = src->dmgFlags; + dest->effect = src->effect; + dest->damage = src->damage; + return 1; +} + +void Collider_ResetATInfo_Unk(GlobalContext* globalCtx, ColliderInfo* info) { +} + +s32 Collider_InitBump(GlobalContext* globalCtx, ColliderBump* bump) { + static ColliderBump init = { 0xFFCFFFFF, 0, 0, { 0, 0, 0 } }; + + *bump = init; + return 1; +} + +s32 Collider_DestroyBump(GlobalContext* globalCtx, ColliderBump* bump) { + return 1; +} + +s32 Collider_SetBump(GlobalContext* globalCtx, ColliderBump* bump, ColliderBumpInit* init) { + bump->dmgFlags = init->dmgFlags; + bump->effect = init->effect; + bump->defense = init->defense; + return 1; +} + +s32 Collider_InitInfo(GlobalContext* globalCtx, ColliderInfo* info) { + static ColliderInfo init = { + { 0, 0, 0 }, { 0xFFCFFFFF, 0, 0, { 0, 0, 0 } }, + ELEMTYPE_UNK0, TOUCH_NONE, + BUMP_NONE, OCELEM_NONE, + NULL, NULL, + NULL, NULL, + }; + + *info = init; + Collider_InitTouch(globalCtx, &info->toucher); + Collider_InitBump(globalCtx, &info->bumper); + return 1; +} + +s32 Collider_DestroyInfo(GlobalContext* globalCtx, ColliderInfo* info) { + Collider_DestroyTouch(globalCtx, &info->toucher); + Collider_DestroyBump(globalCtx, &info->bumper); + return 1; +} + +s32 Collider_SetInfo(GlobalContext* globalCtx, ColliderInfo* info, ColliderInfoInit* infoInit) { + info->elemType = infoInit->elemType; + Collider_SetTouch(globalCtx, &info->toucher, &infoInit->toucher); + Collider_SetBump(globalCtx, &info->bumper, &infoInit->bumper); + info->toucherFlags = infoInit->toucherFlags; + info->bumperFlags = infoInit->bumperFlags; + info->ocElemFlags = infoInit->ocElemFlags; + return 1; +} + +void Collider_ResetATInfo(GlobalContext* globalCtx, ColliderInfo* info) { + info->atHit = NULL; + info->atHitInfo = NULL; + info->toucherFlags &= ~TOUCH_HIT; + info->toucherFlags &= ~TOUCH_DREW_HITMARK; + Collider_ResetATInfo_Unk(globalCtx, info); +} + +void Collider_ResetACInfo(GlobalContext* globalCtx, ColliderInfo* info) { + info->bumper.hitPos.x = info->bumper.hitPos.y = info->bumper.hitPos.z = 0; + info->bumperFlags &= ~BUMP_HIT; + info->bumperFlags &= ~BUMP_DRAW_HITMARK; + info->acHit = NULL; + info->acHitInfo = NULL; +} + +void Collider_ResetOCInfo(GlobalContext* globalCtx, ColliderInfo* info) { + info->ocElemFlags &= ~OCELEM_HIT; +} + +s32 Collider_InitJntSphElementDim(GlobalContext* globalCtx, ColliderJntSphElementDim* dim) { + static ColliderJntSphElementDim init = { + { { 0, 0, 0 }, 0 }, + { { 0, 0, 0 }, 0 }, + 0.0f, + 0, + }; + *dim = init; + return 1; +} + +s32 Collider_DestroyJntSphElementDim(GlobalContext* globalCtx, ColliderJntSphElementDim* element) { + return 1; +} + +s32 Collider_SetJntSphElementDim(GlobalContext* globalCtx, ColliderJntSphElementDim* dest, + ColliderJntSphElementDimInit* src) { + dest->limb = src->limb; + dest->modelSphere = src->modelSphere; + dest->scale = src->scale * 0.01f; + return 1; +} + +s32 Collider_InitJntSphElement(GlobalContext* globalCtx, ColliderJntSphElement* element) { + Collider_InitInfo(globalCtx, &element->info); + Collider_InitJntSphElementDim(globalCtx, &element->dim); + return 1; +} + +s32 Collider_DestroyJntSphElement(GlobalContext* globalCtx, ColliderJntSphElement* element) { + Collider_DestroyInfo(globalCtx, &element->info); + Collider_DestroyJntSphElementDim(globalCtx, &element->dim); + return 1; +} + +s32 Collider_SetJntSphElement(GlobalContext* globalCtx, ColliderJntSphElement* dest, ColliderJntSphElementInit* src) { + Collider_SetInfo(globalCtx, &dest->info, &src->info); + Collider_SetJntSphElementDim(globalCtx, &dest->dim, &src->dim); + return 1; +} + +s32 Collider_ResetJntSphElementAT(GlobalContext* globalCtx, ColliderJntSphElement* collider) { + Collider_ResetATInfo(globalCtx, &collider->info); + return 1; +} + +s32 Collider_ResetJntSphElementAC(GlobalContext* globalCtx, ColliderJntSphElement* collider) { + Collider_ResetACInfo(globalCtx, &collider->info); + return 1; +} + +s32 Collider_ResetJntSphElementOC(GlobalContext* globalCtx, ColliderJntSphElement* collider) { + Collider_ResetOCInfo(globalCtx, &collider->info); + return 1; +} + +/** + * Initializes a ColliderJntSph to default values + */ +s32 Collider_InitJntSph(GlobalContext* globalCtx, ColliderJntSph* collider) { + Collider_InitBase(globalCtx, &collider->base); + collider->count = 0; + collider->elements = NULL; + return 1; +} + +/** + * Destroys a dynamically allocated ColliderJntSph + */ +s32 Collider_FreeJntSph(GlobalContext* globalCtx, ColliderJntSph* collider) { + ColliderJntSphElement* element; + + Collider_DestroyBase(globalCtx, &collider->base); + for (element = collider->elements; element < collider->elements + collider->count; element++) { + Collider_DestroyJntSphElement(globalCtx, element); + } + + collider->count = 0; + if (collider->elements != NULL) { + ZeldaArena_FreeDebug(collider->elements, "../z_collision_check.c", 1393); + } + collider->elements = NULL; + return 1; +} + +/** + * Destroys a preallocated ColliderJntSph + */ +s32 Collider_DestroyJntSph(GlobalContext* globalCtx, ColliderJntSph* collider) { + ColliderJntSphElement* element; + + Collider_DestroyBase(globalCtx, &collider->base); + for (element = collider->elements; element < collider->elements + collider->count; element++) { + Collider_DestroyJntSphElement(globalCtx, element); + } + collider->count = 0; + collider->elements = NULL; + return 1; +} + +/** + * Sets up the ColliderJntSph using the values in src, sets it to the actor specified in src, and dynamically allocates + * the element array. Uses default OC2_TYPE_1 and COLTYPE_HIT0. Unused. + */ +s32 Collider_SetJntSphToActor(GlobalContext* globalCtx, ColliderJntSph* dest, ColliderJntSphInitToActor* src) { + ColliderJntSphElement* destElem; + ColliderJntSphElementInit* srcElem; + + Collider_SetBaseToActor(globalCtx, &dest->base, &src->base); + dest->count = src->count; + dest->elements = ZeldaArena_MallocDebug(src->count * sizeof(ColliderJntSphElement), "../z_collision_check.c", 1443); + + if (dest->elements == NULL) { + dest->count = 0; + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("ClObjJntSph_set():zelda_malloc()出来ません。\n"); // "Can not." + osSyncPrintf(VT_RST); + return 0; + } + + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitJntSphElement(globalCtx, destElem); + Collider_SetJntSphElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Sets up the ColliderJntSph using the values in src and dynamically allocates the element array. Uses default + * OC2_TYPE_1. Only used by En_Nwc, an unused and unfinished actor. + */ +s32 Collider_SetJntSphAllocType1(GlobalContext* globalCtx, ColliderJntSph* dest, Actor* actor, + ColliderJntSphInitType1* src) { + ColliderJntSphElement* destElem; + ColliderJntSphElementInit* srcElem; + + Collider_SetBaseType1(globalCtx, &dest->base, actor, &src->base); + dest->count = src->count; + dest->elements = ZeldaArena_MallocDebug(src->count * sizeof(ColliderJntSphElement), "../z_collision_check.c", 1490); + + if (dest->elements == NULL) { + dest->count = 0; + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("ClObjJntSph_set3():zelda_malloc_出来ません。\n"); // "Can not." + osSyncPrintf(VT_RST); + return 0; + } + + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitJntSphElement(globalCtx, destElem); + Collider_SetJntSphElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Sets up the ColliderJntSph using the values in src and dynamically allocates the element array. + * Unused. + */ +s32 Collider_SetJntSphAlloc(GlobalContext* globalCtx, ColliderJntSph* dest, Actor* actor, ColliderJntSphInit* src) { + ColliderJntSphElement* destElem; + ColliderJntSphElementInit* srcElem; + + Collider_SetBase(globalCtx, &dest->base, actor, &src->base); + dest->count = src->count; + dest->elements = ZeldaArena_MallocDebug(src->count * sizeof(ColliderJntSphElement), "../z_collision_check.c", 1551); + + if (dest->elements == NULL) { + dest->count = 0; + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("ClObjJntSph_set5():zelda_malloc出来ません\n"); // "Can not." + osSyncPrintf(VT_RST); + return 0; + } + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitJntSphElement(globalCtx, destElem); + Collider_SetJntSphElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Sets up the ColliderJntSph using the values in src, placing the element array in elements. + */ +s32 Collider_SetJntSph(GlobalContext* globalCtx, ColliderJntSph* dest, Actor* actor, ColliderJntSphInit* src, + ColliderJntSphElement* elements) { + ColliderJntSphElement* destElem; + ColliderJntSphElementInit* srcElem; + + Collider_SetBase(globalCtx, &dest->base, actor, &src->base); + dest->count = src->count; + dest->elements = elements; + ASSERT(dest->elements != NULL, "pclobj_jntsph->elem_tbl != NULL", "../z_collision_check.c", 1603); + + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitJntSphElement(globalCtx, destElem); + Collider_SetJntSphElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Resets the collider's AT collision flags. + */ +s32 Collider_ResetJntSphAT(GlobalContext* globalCtx, Collider* collider) { + ColliderJntSphElement* element; + ColliderJntSph* jntSph = (ColliderJntSph*)collider; + + Collider_ResetATBase(globalCtx, &jntSph->base); + + for (element = jntSph->elements; element < jntSph->elements + jntSph->count; element++) { + Collider_ResetJntSphElementAT(globalCtx, element); + } + return 1; +} + +/** + * Resets the collider's AC collision flags. + */ +s32 Collider_ResetJntSphAC(GlobalContext* globalCtx, Collider* collider) { + ColliderJntSphElement* element; + ColliderJntSph* jntSph = (ColliderJntSph*)collider; + + Collider_ResetACBase(globalCtx, &jntSph->base); + + for (element = jntSph->elements; element < jntSph->elements + jntSph->count; element++) { + Collider_ResetJntSphElementAC(globalCtx, element); + } + return 1; +} + +/** + * Resets the collider's OC collision flags. + */ +s32 Collider_ResetJntSphOC(GlobalContext* globalCtx, Collider* collider) { + ColliderJntSphElement* element; + ColliderJntSph* jntSph = (ColliderJntSph*)collider; + + Collider_ResetOCBase(globalCtx, &jntSph->base); + + for (element = jntSph->elements; element < jntSph->elements + jntSph->count; element++) { + Collider_ResetJntSphElementOC(globalCtx, element); + } + return 1; +} + +s32 Collider_InitCylinderDim(GlobalContext* globalCtx, Cylinder16* dim) { + Cylinder16 init = { 0, 0, 0, { 0, 0, 0 } }; + + *dim = init; + return 1; +} + +s32 Collider_DestroyCylinderDim(GlobalContext* globalCtx, Cylinder16* dim) { + return 1; +} + +s32 Collider_SetCylinderDim(GlobalContext* globalCtx, Cylinder16* dest, Cylinder16* src) { + *dest = *src; + return 1; +} + +/** + * Initializes a ColliderCylinder to default values + */ +s32 Collider_InitCylinder(GlobalContext* globalCtx, ColliderCylinder* collider) { + Collider_InitBase(globalCtx, &collider->base); + Collider_InitInfo(globalCtx, &collider->info); + Collider_InitCylinderDim(globalCtx, &collider->dim); + return 1; +} + +/** + * Destroys a ColliderCylinder + */ +s32 Collider_DestroyCylinder(GlobalContext* globalCtx, ColliderCylinder* collider) { + Collider_DestroyBase(globalCtx, &collider->base); + Collider_DestroyInfo(globalCtx, &collider->info); + Collider_DestroyCylinderDim(globalCtx, &collider->dim); + return 1; +} + +/** + * Sets up the ColliderCylinder using the values in src and sets it to the actor specified in src. Uses default + * OC2_TYPE_1 and COLTYPE_0. Used only by DekuJr, who sets it to himself anyways. + */ +s32 Collider_SetCylinderToActor(GlobalContext* globalCtx, ColliderCylinder* collider, + ColliderCylinderInitToActor* src) { + Collider_SetBaseToActor(globalCtx, &collider->base, &src->base); + Collider_SetInfo(globalCtx, &collider->info, &src->info); + Collider_SetCylinderDim(globalCtx, &collider->dim, &src->dim); + return 1; +} + +/** + * Sets up the ColliderCylinder using the values in src. Uses default OC2_TYPE_1 + */ +s32 Collider_SetCylinderType1(GlobalContext* globalCtx, ColliderCylinder* collider, Actor* actor, + ColliderCylinderInitType1* src) { + Collider_SetBaseType1(globalCtx, &collider->base, actor, &src->base); + Collider_SetInfo(globalCtx, &collider->info, &src->info); + Collider_SetCylinderDim(globalCtx, &collider->dim, &src->dim); + return 1; +} + +/** + * Sets up the ColliderCylinder using the values in src. + */ +s32 Collider_SetCylinder(GlobalContext* globalCtx, ColliderCylinder* collider, Actor* actor, + ColliderCylinderInit* src) { + Collider_SetBase(globalCtx, &collider->base, actor, &src->base); + Collider_SetInfo(globalCtx, &collider->info, &src->info); + Collider_SetCylinderDim(globalCtx, &collider->dim, &src->dim); + return 1; +} + +/** + * Resets the collider's AT collision flags. + */ +s32 Collider_ResetCylinderAT(GlobalContext* globalCtx, Collider* collider) { + ColliderCylinder* cylinder = (ColliderCylinder*)collider; + + Collider_ResetATBase(globalCtx, &cylinder->base); + Collider_ResetATInfo(globalCtx, &cylinder->info); + return 1; +} + +/** + * Resets the collider's AC collision flags. + */ +s32 Collider_ResetCylinderAC(GlobalContext* globalCtx, Collider* collider) { + ColliderCylinder* cylinder = (ColliderCylinder*)collider; + + Collider_ResetACBase(globalCtx, &cylinder->base); + Collider_ResetACInfo(globalCtx, &cylinder->info); + return 1; +} + +/** + * Resets the collider's OC collision flags. + */ +s32 Collider_ResetCylinderOC(GlobalContext* globalCtx, Collider* collider) { + ColliderCylinder* cylinder = (ColliderCylinder*)collider; + + Collider_ResetOCBase(globalCtx, &cylinder->base); + Collider_ResetOCInfo(globalCtx, &cylinder->info); + return 1; +} + +s32 Collider_InitTrisElementDim(GlobalContext* globalCtx, TriNorm* dim) { + static TriNorm init = { + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { { 0.0f, 0.0f, 0.0f }, 0.0f }, + }; + + *dim = init; + return 1; +} + +s32 Collider_DestroyTrisElementDim(GlobalContext* globalCtx, TriNorm* dim) { + return 1; +} + +s32 Collider_SetTrisElementDim(GlobalContext* globalCtx, TriNorm* dest, ColliderTrisElementDimInit* src) { + Vec3f* destVtx; + Vec3f* srcVtx; + f32 nx; + f32 ny; + f32 nz; + f32 originDist; + + for (destVtx = dest->vtx, srcVtx = src->vtx; destVtx < dest->vtx + 3; destVtx++, srcVtx++) { + *destVtx = *srcVtx; + } + + Math3D_DefPlane(&src->vtx[0], &src->vtx[1], &src->vtx[2], &nx, &ny, &nz, &originDist); + + dest->plane.normal.x = nx; + dest->plane.normal.y = ny; + dest->plane.normal.z = nz; + dest->plane.originDist = originDist; + return 1; +} + +s32 Collider_InitTrisElement(GlobalContext* globalCtx, ColliderTrisElement* collider) { + Collider_InitInfo(globalCtx, &collider->info); + Collider_InitTrisElementDim(globalCtx, &collider->dim); + return 1; +} + +s32 Collider_DestroyTrisElement(GlobalContext* globalCtx, ColliderTrisElement* collider) { + Collider_DestroyInfo(globalCtx, &collider->info); + Collider_DestroyTrisElementDim(globalCtx, &collider->dim); + return 1; +} + +s32 Collider_SetTrisElement(GlobalContext* globalCtx, ColliderTrisElement* dest, ColliderTrisElementInit* src) { + Collider_SetInfo(globalCtx, &dest->info, &src->info); + Collider_SetTrisElementDim(globalCtx, &dest->dim, &src->dim); + return 1; +} + +s32 Collider_ResetTrisElementAT(GlobalContext* globalCtx, ColliderTrisElement* element) { + Collider_ResetATInfo(globalCtx, &element->info); + return 1; +} + +s32 Collider_ResetTrisElementAC(GlobalContext* globalCtx, ColliderTrisElement* element) { + Collider_ResetACInfo(globalCtx, &element->info); + return 1; +} + +s32 Collider_ResetTrisElementOC(GlobalContext* globalCtx, ColliderTrisElement* element) { + Collider_ResetOCInfo(globalCtx, &element->info); + return 1; +} + +/** + * Initializes a ColliderTris to default values + */ +s32 Collider_InitTris(GlobalContext* globalCtx, ColliderTris* tris) { + Collider_InitBase(globalCtx, &tris->base); + tris->count = 0; + tris->elements = 0; + return 1; +} + +/** + * Destroys a dynamically allocated ColliderTris + * Unused + */ +s32 Collider_FreeTris(GlobalContext* globalCtx, ColliderTris* tris) { + ColliderTrisElement* element; + + Collider_DestroyBase(globalCtx, &tris->base); + for (element = tris->elements; element < tris->elements + tris->count; element++) { + Collider_DestroyTrisElement(globalCtx, element); + } + + tris->count = 0; + if (tris->elements != NULL) { + ZeldaArena_FreeDebug(tris->elements, "../z_collision_check.c", 2099); + } + tris->elements = NULL; + return 1; +} + +/** + * Destroys a preallocated ColliderTris + */ +s32 Collider_DestroyTris(GlobalContext* globalCtx, ColliderTris* tris) { + ColliderTrisElement* element; + + Collider_DestroyBase(globalCtx, &tris->base); + for (element = tris->elements; element < tris->elements + tris->count; element++) { + Collider_DestroyTrisElement(globalCtx, element); + } + + tris->count = 0; + tris->elements = NULL; + return 1; +} + +/** + * Sets up the ColliderTris using the values in src and dynamically allocates the element array. Uses default OC2_TYPE_1 + * Unused. + */ +s32 Collider_SetTrisAllocType1(GlobalContext* globalCtx, ColliderTris* dest, Actor* actor, ColliderTrisInitType1* src) { + ColliderTrisElement* destElem; + ColliderTrisElementInit* srcElem; + + Collider_SetBaseType1(globalCtx, &dest->base, actor, &src->base); + dest->count = src->count; + dest->elements = ZeldaArena_MallocDebug(dest->count * sizeof(ColliderTrisElement), "../z_collision_check.c", 2156); + if (dest->elements == NULL) { + dest->count = 0; + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("ClObjTris_set3():zelda_malloc()出来ません\n"); // "Can not." + osSyncPrintf(VT_RST); + return 0; + } + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitTrisElement(globalCtx, destElem); + Collider_SetTrisElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Sets up the ColliderTris using the values in src and dynamically allocates the element array. + * Unused + */ +s32 Collider_SetTrisAlloc(GlobalContext* globalCtx, ColliderTris* dest, Actor* actor, ColliderTrisInit* src) { + ColliderTrisElement* destElem; + ColliderTrisElementInit* srcElem; + + Collider_SetBase(globalCtx, &dest->base, actor, &src->base); + dest->count = src->count; + dest->elements = ZeldaArena_MallocDebug(dest->count * sizeof(ColliderTrisElement), "../z_collision_check.c", 2207); + + if (dest->elements == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("ClObjTris_set5():zelda_malloc出来ません\n"); // "Can not." + osSyncPrintf(VT_RST); + dest->count = 0; + return 0; + } + + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitTrisElement(globalCtx, destElem); + Collider_SetTrisElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Sets up the ColliderTris using the values in src, placing the element array in elements. + */ +s32 Collider_SetTris(GlobalContext* globalCtx, ColliderTris* dest, Actor* actor, ColliderTrisInit* src, + ColliderTrisElement* elements) { + ColliderTrisElement* destElem; + ColliderTrisElementInit* srcElem; + + Collider_SetBase(globalCtx, &dest->base, actor, &src->base); + dest->count = src->count; + dest->elements = elements; + ASSERT(dest->elements != NULL, "pclobj_tris->elem_tbl != NULL", "../z_collision_check.c", 2258); + + for (destElem = dest->elements, srcElem = src->elements; destElem < dest->elements + dest->count; + destElem++, srcElem++) { + Collider_InitTrisElement(globalCtx, destElem); + Collider_SetTrisElement(globalCtx, destElem, srcElem); + } + return 1; +} + +/** + * Resets the collider's AT collision flags. + */ +s32 Collider_ResetTrisAT(GlobalContext* globalCtx, Collider* collider) { + ColliderTrisElement* element; + ColliderTris* tris = (ColliderTris*)collider; + + Collider_ResetATBase(globalCtx, &tris->base); + for (element = tris->elements; element < tris->elements + tris->count; element++) { + Collider_ResetTrisElementAT(globalCtx, element); + } + return 1; +} + +/** + * Resets the collider's AC collision flags. + */ +s32 Collider_ResetTrisAC(GlobalContext* globalCtx, Collider* collider) { + ColliderTrisElement* element; + ColliderTris* tris = (ColliderTris*)collider; + + Collider_ResetACBase(globalCtx, &tris->base); + for (element = tris->elements; element < tris->elements + tris->count; element++) { + Collider_ResetTrisElementAC(globalCtx, element); + } + return 1; +} + +/** + * Resets the collider's OC collision flags. + */ +s32 Collider_ResetTrisOC(GlobalContext* globalCtx, Collider* collider) { + ColliderTrisElement* element; + ColliderTris* tris = (ColliderTris*)collider; + + Collider_ResetOCBase(globalCtx, &tris->base); + for (element = tris->elements; element < tris->elements + tris->count; element++) { + Collider_ResetTrisElementOC(globalCtx, element); + } + return 1; +} + +s32 Collider_InitQuadDim(GlobalContext* globalCtx, ColliderQuadDim* dim) { + static ColliderQuadDim init = { + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0, 0, 0 }, + { 0, 0, 0 }, + 1.0E38f, + }; + + *dim = init; + return 1; +} + +s32 Collider_DestroyQuadDim(GlobalContext* globalCtx, ColliderQuadDim* dim) { + return 1; +} + +s32 Collider_ResetQuadACDist(GlobalContext* globalCtx, ColliderQuadDim* dim) { + dim->acDist = 1.0E38f; + return 1; +} + +void Collider_SetQuadMidpoints(ColliderQuadDim* dim) { + dim->dcMid.x = (dim->quad[3].x + dim->quad[2].x) * 0.5f; + dim->dcMid.y = (dim->quad[3].y + dim->quad[2].y) * 0.5f; + dim->dcMid.z = (dim->quad[3].z + dim->quad[2].z) * 0.5f; + dim->baMid.x = (dim->quad[1].x + dim->quad[0].x) * 0.5f; + dim->baMid.y = (dim->quad[1].y + dim->quad[0].y) * 0.5f; + dim->baMid.z = (dim->quad[1].z + dim->quad[0].z) * 0.5f; +} + +s32 Collider_SetQuadDim(GlobalContext* globalCtx, ColliderQuadDim* dest, ColliderQuadDimInit* src) { + dest->quad[0] = src->quad[0]; + dest->quad[1] = src->quad[1]; + dest->quad[2] = src->quad[2]; + dest->quad[3] = src->quad[3]; + Collider_SetQuadMidpoints(dest); + return 1; +} + +/** + * Initializes a ColliderQuad to default values. + */ +s32 Collider_InitQuad(GlobalContext* globalCtx, ColliderQuad* collider) { + Collider_InitBase(globalCtx, &collider->base); + Collider_InitInfo(globalCtx, &collider->info); + Collider_InitQuadDim(globalCtx, &collider->dim); + return 1; +} + +/** + * Destroys a ColliderQuad. + */ +s32 Collider_DestroyQuad(GlobalContext* globalCtx, ColliderQuad* collider) { + Collider_DestroyBase(globalCtx, &collider->base); + Collider_DestroyInfo(globalCtx, &collider->info); + Collider_DestroyQuadDim(globalCtx, &collider->dim); + return 1; +} + +/** + * Sets up the ColliderQuad using the values in src. Uses the default OC2_TYPE_1 + */ +s32 Collider_SetQuadType1(GlobalContext* globalCtx, ColliderQuad* collider, Actor* actor, ColliderQuadInitType1* src) { + Collider_SetBaseType1(globalCtx, &collider->base, actor, &src->base); + Collider_SetInfo(globalCtx, &collider->info, &src->info); + Collider_SetQuadDim(globalCtx, &collider->dim, &src->dim); + return 1; +} + +/** + * Sets up the ColliderQuad using the values in src. + */ +s32 Collider_SetQuad(GlobalContext* globalCtx, ColliderQuad* collider, Actor* actor, ColliderQuadInit* src) { + Collider_SetBase(globalCtx, &collider->base, actor, &src->base); + Collider_SetInfo(globalCtx, &collider->info, &src->info); + Collider_SetQuadDim(globalCtx, &collider->dim, &src->dim); + return 1; +} + +/** + * Resets the collider's AT collision flags. + */ +s32 Collider_ResetQuadAT(GlobalContext* globalCtx, Collider* collider) { + ColliderQuad* quad = (ColliderQuad*)collider; + + Collider_ResetATBase(globalCtx, &quad->base); + Collider_ResetATInfo(globalCtx, &quad->info); + Collider_ResetQuadACDist(globalCtx, &quad->dim); + return 1; +} + +/** + * Resets the collider's AC collision flags. + */ +s32 Collider_ResetQuadAC(GlobalContext* globalCtx, Collider* collider) { + ColliderQuad* quad = (ColliderQuad*)collider; + + Collider_ResetACBase(globalCtx, &quad->base); + Collider_ResetACInfo(globalCtx, &quad->info); + return 1; +} + +/** + * Resets the collider's OC collision flags. + */ +s32 Collider_ResetQuadOC(GlobalContext* globalCtx, Collider* collider) { + ColliderQuad* quad = (ColliderQuad*)collider; + + Collider_ResetOCBase(globalCtx, &quad->base); + Collider_ResetOCInfo(globalCtx, &quad->info); + return 1; +} + +/** + * For quad colliders with AT_NEAREST, resets the previous AC collider it hit if the current element is closer, + * otherwise returns false. Used on player AT colliders to prevent multiple collisions from registering. + */ +s32 Collider_QuadSetNearestAC(GlobalContext* globalCtx, ColliderQuad* quad, Vec3f* hitPos) { + f32 acDist; + Vec3f dcMid; + + if (!(quad->info.toucherFlags & TOUCH_NEAREST)) { + return true; + } + Math_Vec3s_ToVec3f(&dcMid, &quad->dim.dcMid); + acDist = Math3D_Vec3fDistSq(&dcMid, hitPos); + if (acDist < quad->dim.acDist) { + quad->dim.acDist = acDist; + if (quad->info.atHit != NULL) { + Collider_ResetACBase(globalCtx, quad->info.atHit); + } + if (quad->info.atHitInfo != NULL) { + Collider_ResetACInfo(globalCtx, quad->info.atHitInfo); + } + return true; + } + return false; +} + +/** + * Initializes an OcLine to default values + * OcLines are entirely unused. + */ +s32 Collider_InitLine(GlobalContext* globalCtx, OcLine* line) { + Vec3f init = { 0.0f, 0.0f, 0.0f }; + + Math_Vec3f_Copy(&line->line.a, &init); + Math_Vec3f_Copy(&line->line.b, &init); + return 1; +} + +/** + * Destroys an OcLine + * OcLines are entirely unused. + */ +s32 Collider_DestroyLine(GlobalContext* globalCtx, OcLine* line) { + return 1; +} + +/** + * Sets up an OcLine with endpoints a and b. + * OcLines are entirely unused. + */ +s32 Collider_SetLinePoints(GlobalContext* GlobalContext, OcLine* ocLine, Vec3f* a, Vec3f* b) { + Math_Vec3f_Copy(&ocLine->line.a, a); + Math_Vec3f_Copy(&ocLine->line.b, b); + return 1; +} + +/** + * Sets up an OcLine using the values in src. + * OcLines are entirely unused. + */ +s32 Collider_SetLine(GlobalContext* globalCtx, OcLine* dest, OcLine* src) { + dest->ocFlags = src->ocFlags; + Collider_SetLinePoints(globalCtx, dest, &src->line.a, &src->line.b); + return 1; +} + +/** + * Resets the OcLine's collision flags. + * OcLines are entirely unused. + */ +s32 Collider_ResetLineOC(GlobalContext* globalCtx, OcLine* line) { + line->ocFlags &= ~OCLINE_HIT; + return 1; +} + +/** + * Initializes CollisionCheckContext. Clears all collider arrays, disables SAC, and sets flags for drawing colliders. + */ +void CollisionCheck_InitContext(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + colChkCtx->sacFlags = 0; + CollisionCheck_ClearContext(globalCtx, colChkCtx); + AREG(21) = true; + AREG(22) = true; + AREG(23) = true; +} + +void CollisionCheck_DestroyContext(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { +} + +/** + * Clears all collider lists in CollisionCheckContext when not in SAC mode. + */ +void CollisionCheck_ClearContext(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + Collider** col; + OcLine** line; + + if (!(colChkCtx->sacFlags & 1)) { + colChkCtx->colATCount = 0; + colChkCtx->colACCount = 0; + colChkCtx->colOCCount = 0; + colChkCtx->colLineCount = 0; + for (col = colChkCtx->colAT; col < colChkCtx->colAT + COLLISION_CHECK_AT_MAX; col++) { + *col = NULL; + } + + for (col = colChkCtx->colAC; col < colChkCtx->colAC + COLLISION_CHECK_AC_MAX; col++) { + *col = NULL; + } + + for (col = colChkCtx->colOC; col < colChkCtx->colOC + COLLISION_CHECK_OC_MAX; col++) { + *col = NULL; + } + + for (line = colChkCtx->colLine; line < colChkCtx->colLine + COLLISION_CHECK_OC_LINE_MAX; line++) { + *line = NULL; + } + } +} + +/** + * Enables SAC, an alternate collision check mode that allows direct management of collider lists. Unused. + */ +void CollisionCheck_EnableSAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + colChkCtx->sacFlags |= 1; +} + +/** + * Disables SAC, an alternate collision check mode that allows direct management of collider lists. Unused. + */ +void CollisionCheck_DisableSAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + colChkCtx->sacFlags &= ~1; +} + +/** + * Draws a collider of any shape. + * Math3D_DrawSphere and Math3D_DrawCylinder are noops, so JntSph and Cylinder are not drawn. + */ +void Collider_Draw(GlobalContext* globalCtx, Collider* collider) { + ColliderJntSph* jntSph; + ColliderCylinder* cylinder; + ColliderTris* tris; + ColliderQuad* quad; + s32 i; + + if (collider == NULL) { + return; + } + switch (collider->shape) { + case COLSHAPE_JNTSPH: + jntSph = (ColliderJntSph*)collider; + for (i = 0; i < jntSph->count; i++) { + Math3D_DrawSphere(globalCtx, &jntSph->elements[i].dim.worldSphere); + } + break; + case COLSHAPE_CYLINDER: + cylinder = (ColliderCylinder*)collider; + Math3D_DrawCylinder(globalCtx, &cylinder->dim); + break; + case COLSHAPE_TRIS: + tris = (ColliderTris*)collider; + for (i = 0; i < tris->count; i++) { + Collider_DrawRedPoly(globalCtx->state.gfxCtx, &tris->elements[i].dim.vtx[0], + &tris->elements[i].dim.vtx[1], &tris->elements[i].dim.vtx[2]); + } + break; + case COLSHAPE_QUAD: + quad = (ColliderQuad*)collider; + Collider_DrawRedPoly(globalCtx->state.gfxCtx, &quad->dim.quad[2], &quad->dim.quad[3], &quad->dim.quad[1]); + Collider_DrawRedPoly(globalCtx->state.gfxCtx, &quad->dim.quad[1], &quad->dim.quad[0], &quad->dim.quad[2]); + break; + } +} + +/** + * Draws collision if AREG(15) and other AREGs are set. AREG(21) draws AT colliders, AREG(22) draws AC colliders, + * AREG(23) draws OC colliders, AREG(24) draws dynapolys, and AREG(25) draws bg polys + */ +void CollisionCheck_DrawCollision(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + Collider* collider; + s32 i; + + if (AREG(15)) { + if (AREG(21)) { + for (i = 0; i < colChkCtx->colATCount; i++) { + Collider_Draw(globalCtx, colChkCtx->colAT[i]); + } + } + if (AREG(22)) { + for (i = 0; i < colChkCtx->colACCount; i++) { + Collider_Draw(globalCtx, colChkCtx->colAC[i]); + } + } + if (AREG(23)) { + for (i = 0; i < colChkCtx->colOCCount; i++) { + collider = colChkCtx->colOC[i]; + if (collider->ocFlags1 & OC1_ON) { + Collider_Draw(globalCtx, collider); + } + } + } + if (AREG(24)) { + BgCheck_DrawDynaCollision(globalCtx, &globalCtx->colCtx); + } + if (AREG(25)) { + BgCheck_DrawStaticCollision(globalCtx, &globalCtx->colCtx); + } + } +} + +static ColChkResetFunc sATResetFuncs[] = { + Collider_ResetJntSphAT, + Collider_ResetCylinderAT, + Collider_ResetTrisAT, + Collider_ResetQuadAT, +}; + +/** + * Sets collider as an AT (attack) for the current frame, which will be checked against ACs (attack colliders) + * The last argument takes a Collider, so pass collider.base rather than the raw collider. + */ +s32 CollisionCheck_SetAT(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + s32 index; + + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + ASSERT(collider->shape <= COLSHAPE_QUAD, "pcl_obj->data_type <= CL_DATA_LBL_SWRD", "../z_collision_check.c", 2997); + sATResetFuncs[collider->shape](globalCtx, collider); + if (collider->actor != NULL && collider->actor->update == NULL) { + return -1; + } + if (colChkCtx->colATCount >= COLLISION_CHECK_AT_MAX) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setAT():インデックスがオーバーして追加不能\n"); + return -1; + } + if (colChkCtx->sacFlags & 1) { + return -1; + } + index = colChkCtx->colATCount; + colChkCtx->colAT[colChkCtx->colATCount++] = collider; + return index; +} + +/** + * Unused. Sets collider as an AT (attack) for the current frame, which will be checked against ACs (attack colliders). + * If CollisionCheck_SAC is enabled, the collider will be inserted into the list at the specified index, otherwise it + * will be inserted into the next slot + */ +s32 CollisionCheck_SetAT_SAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, + s32 index) { + ASSERT(collider->shape <= COLSHAPE_QUAD, "pcl_obj->data_type <= CL_DATA_LBL_SWRD", "../z_collision_check.c", 3037); + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + sATResetFuncs[collider->shape](globalCtx, collider); + if (collider->actor != NULL && collider->actor->update == NULL) { + return -1; + } + if (colChkCtx->sacFlags & 1) { + if (!(index < colChkCtx->colATCount)) { + // "You are trying to register a location that is larger than the total number of data." + osSyncPrintf("CollisionCheck_setAT_SAC():全データ数より大きいところに登録しようとしている。\n"); + return -1; + } + colChkCtx->colAT[index] = collider; + } else { + if (!(colChkCtx->colATCount < COLLISION_CHECK_AT_MAX)) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setAT():インデックスがオーバーして追加不能\n"); + return -1; + } + index = colChkCtx->colATCount; + colChkCtx->colAT[colChkCtx->colATCount++] = collider; + } + return index; +} + +static ColChkResetFunc sACResetFuncs[] = { + Collider_ResetJntSphAC, + Collider_ResetCylinderAC, + Collider_ResetTrisAC, + Collider_ResetQuadAC, +}; + +/** + * Sets collider as an AC (attack collider) for the current frame, allowing it to detect ATs (attacks) + * The last argument takes a Collider, so pass collider.base rather than the raw collider. + */ +s32 CollisionCheck_SetAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + s32 index; + + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + ASSERT(collider->shape <= COLSHAPE_QUAD, "pcl_obj->data_type <= CL_DATA_LBL_SWRD", "../z_collision_check.c", 3114); + sACResetFuncs[collider->shape](globalCtx, collider); + if (collider->actor != NULL && collider->actor->update == NULL) { + return -1; + } + if (colChkCtx->colACCount >= COLLISION_CHECK_AC_MAX) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setAC():インデックスがオーバして追加不能\n"); + return -1; + } + if (colChkCtx->sacFlags & 1) { + return -1; + } + index = colChkCtx->colACCount; + colChkCtx->colAC[colChkCtx->colACCount++] = collider; + return index; +} + +/** + * Unused. Sets collider as an AC (attack collider) for the current frame, allowing it to detect ATs (attacks). + * If CollisionCheck_SAC is enabled, the collider will be inserted into the list at the specified index, otherwise it + * will be inserted into the next slot + */ +s32 CollisionCheck_SetAC_SAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, + s32 index) { + ASSERT(collider->shape <= COLSHAPE_QUAD, "pcl_obj->data_type <= CL_DATA_LBL_SWRD", "../z_collision_check.c", 3153); + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + sACResetFuncs[collider->shape](globalCtx, collider); + if (collider->actor != NULL && collider->actor->update == NULL) { + return -1; + } + if (colChkCtx->sacFlags & 1) { + if (!(index < colChkCtx->colACCount)) { + // "You are trying to register a location that is larger than the total number of data." + osSyncPrintf("CollisionCheck_setAC_SAC():全データ数より大きいところに登録しようとしている。\n"); + return -1; + } + colChkCtx->colAC[index] = collider; + } else { + if (!(colChkCtx->colACCount < COLLISION_CHECK_AC_MAX)) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setAC():インデックスがオーバして追加不能\n"); + return -1; + } + index = colChkCtx->colACCount; + colChkCtx->colAC[colChkCtx->colACCount++] = collider; + } + return index; +} + +static ColChkResetFunc sOCResetFuncs[] = { + Collider_ResetJntSphOC, + Collider_ResetCylinderOC, + Collider_ResetTrisOC, + Collider_ResetQuadOC, +}; + +/** + * Sets collider as an OC (object collider) for the current frame, allowing it to detect other OCs + * The last argument takes a Collider, so pass collider.base rather than the raw collider. + */ +s32 CollisionCheck_SetOC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + s32 index; + + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + + ASSERT(collider->shape <= COLSHAPE_QUAD, "pcl_obj->data_type <= CL_DATA_LBL_SWRD", "../z_collision_check.c", 3229); + + sOCResetFuncs[collider->shape](globalCtx, collider); + if (collider->actor != NULL && collider->actor->update == NULL) { + return -1; + } + if (colChkCtx->colOCCount >= COLLISION_CHECK_OC_MAX) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setOC():インデックスがオーバして追加不能\n"); + return -1; + } + if (colChkCtx->sacFlags & 1) { + return -1; + } + index = colChkCtx->colOCCount; + colChkCtx->colOC[colChkCtx->colOCCount++] = collider; + return index; +} + +/** + * Unused. Sets collider as an OC (object collider) for the current frame, allowing it to detect other OCs + * If CollisionCheck_SAC is enabled, the collider will be inserted into the list at the specified index, otherwise it + * will be inserted into the next slot + */ +s32 CollisionCheck_SetOC_SAC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, + s32 index) { + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + ASSERT(collider->shape <= COLSHAPE_QUAD, "pcl_obj->data_type <= CL_DATA_LBL_SWRD", "../z_collision_check.c", 3274); + sOCResetFuncs[collider->shape](globalCtx, collider); + if (collider->actor != NULL && collider->actor->update == NULL) { + return -1; + } + if (colChkCtx->sacFlags & 1) { + if (!(index < colChkCtx->colOCCount)) { + // "You are trying to register a location that is larger than the total number of data." + osSyncPrintf("CollisionCheck_setOC_SAC():全データ数より大きいところに登録しようとしている。\n"); + return -1; + } + //! @bug Should be colOC + colChkCtx->colAT[index] = collider; + } else { + if (!(colChkCtx->colOCCount < COLLISION_CHECK_OC_MAX)) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setOC():インデックスがオーバして追加不能\n"); + return -1; + } + index = colChkCtx->colOCCount; + colChkCtx->colOC[colChkCtx->colOCCount++] = collider; + } + return index; +} + +/** + * Sets a line as an OC collider for this frame. + * OC lines are entirely unused, and do not even have collision check functions. + */ +s32 CollisionCheck_SetOCLine(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, OcLine* collider) { + s32 index; + + if (FrameAdvance_IsEnabled(globalCtx) == true) { + return -1; + } + Collider_ResetLineOC(globalCtx, collider); + if (!(colChkCtx->colLineCount < COLLISION_CHECK_OC_LINE_MAX)) { + // "Index exceeded and cannot add more" + osSyncPrintf("CollisionCheck_setOCLine():インデックスがオーバして追加不能\n"); + return -1; + } + index = colChkCtx->colLineCount; + colChkCtx->colLine[colChkCtx->colLineCount++] = collider; + return index; +} + +/** + * Skips AT elements that are off. + */ +s32 CollisionCheck_SkipTouch(ColliderInfo* info) { + if (!(info->toucherFlags & TOUCH_ON)) { + return 1; + } + return 0; +} + +/** + * Skips AC elements that are off. + */ +s32 CollisionCheck_SkipBump(ColliderInfo* info) { + if (!(info->bumperFlags & BUMP_ON)) { + return 1; + } + return 0; +} + +/** + * If the AT element has no dmgFlags in common with the AC element, no collision happens. + */ +s32 CollisionCheck_NoSharedFlags(ColliderInfo* atInfo, ColliderInfo* acInfo) { + if (!(atInfo->toucher.dmgFlags & acInfo->bumper.dmgFlags)) { + return 1; + } + return 0; +} + +/** + * Spawns no blood drops. + * Used by collider types HIT1, HIT3, HIT5, METAL, NONE, WOOD, HARD, and TREE + */ +void CollisionCheck_NoBlood(GlobalContext* globalCtx, Collider* collider, Vec3f* v) { +} + +/** + * Spawns blue blood drops. + * Used by collider types HIT0 and HIT8. + */ +void CollisionCheck_BlueBlood(GlobalContext* globalCtx, Collider* collider, Vec3f* v) { + static EffectSparkInit D_8015D8A0; + s32 effectIndex; + + D_8015D8A0.position.x = v->x; + D_8015D8A0.position.y = v->y; + D_8015D8A0.position.z = v->z; + D_8015D8A0.uDiv = 5; + D_8015D8A0.vDiv = 5; + D_8015D8A0.colorStart[0].r = 10; + D_8015D8A0.colorStart[0].g = 10; + D_8015D8A0.colorStart[0].b = 200; + D_8015D8A0.colorStart[0].a = 255; + D_8015D8A0.colorStart[1].r = 0; + D_8015D8A0.colorStart[1].g = 0; + D_8015D8A0.colorStart[1].b = 128; + D_8015D8A0.colorStart[1].a = 255; + D_8015D8A0.colorStart[2].r = 0; + D_8015D8A0.colorStart[2].g = 0; + D_8015D8A0.colorStart[2].b = 128; + D_8015D8A0.colorStart[2].a = 255; + D_8015D8A0.colorStart[3].r = 0; + D_8015D8A0.colorStart[3].g = 0; + D_8015D8A0.colorStart[3].b = 128; + D_8015D8A0.colorStart[3].a = 255; + D_8015D8A0.colorEnd[0].r = 0; + D_8015D8A0.colorEnd[0].g = 0; + D_8015D8A0.colorEnd[0].b = 32; + D_8015D8A0.colorEnd[0].a = 0; + D_8015D8A0.colorEnd[1].r = 0; + D_8015D8A0.colorEnd[1].g = 0; + D_8015D8A0.colorEnd[1].b = 32; + D_8015D8A0.colorEnd[1].a = 0; + D_8015D8A0.colorEnd[2].r = 0; + D_8015D8A0.colorEnd[2].g = 0; + D_8015D8A0.colorEnd[2].b = 64; + D_8015D8A0.colorEnd[2].a = 0; + D_8015D8A0.colorEnd[3].r = 0; + D_8015D8A0.colorEnd[3].g = 0; + D_8015D8A0.colorEnd[3].b = 64; + D_8015D8A0.colorEnd[3].a = 0; + D_8015D8A0.timer = 0; + D_8015D8A0.duration = 16; + D_8015D8A0.speed = 8.0f; + D_8015D8A0.gravity = -1.0f; + + Effect_Add(globalCtx, &effectIndex, EFFECT_SPARK, 0, 1, &D_8015D8A0); +} + +/** + * Spawns green blood drops. + * Used by collider types HIT2 and HIT6. No actor has type HIT2. + */ +void CollisionCheck_GreenBlood(GlobalContext* globalCtx, Collider* collider, Vec3f* v) { + static EffectSparkInit D_8015DD68; + s32 effectIndex; + + D_8015DD68.position.x = v->x; + D_8015DD68.position.y = v->y; + D_8015DD68.position.z = v->z; + D_8015DD68.uDiv = 5; + D_8015DD68.vDiv = 5; + D_8015DD68.colorStart[0].r = 10; + D_8015DD68.colorStart[0].g = 200; + D_8015DD68.colorStart[0].b = 10; + D_8015DD68.colorStart[0].a = 255; + D_8015DD68.colorStart[1].r = 0; + D_8015DD68.colorStart[1].g = 128; + D_8015DD68.colorStart[1].b = 0; + D_8015DD68.colorStart[1].a = 255; + D_8015DD68.colorStart[2].r = 0; + D_8015DD68.colorStart[2].g = 128; + D_8015DD68.colorStart[2].b = 0; + D_8015DD68.colorStart[2].a = 255; + D_8015DD68.colorStart[3].r = 0; + D_8015DD68.colorStart[3].g = 128; + D_8015DD68.colorStart[3].b = 0; + D_8015DD68.colorStart[3].a = 255; + D_8015DD68.colorEnd[0].r = 0; + D_8015DD68.colorEnd[0].g = 32; + D_8015DD68.colorEnd[0].b = 0; + D_8015DD68.colorEnd[0].a = 0; + D_8015DD68.colorEnd[1].r = 0; + D_8015DD68.colorEnd[1].g = 32; + D_8015DD68.colorEnd[1].b = 0; + D_8015DD68.colorEnd[1].a = 0; + D_8015DD68.colorEnd[2].r = 0; + D_8015DD68.colorEnd[2].g = 64; + D_8015DD68.colorEnd[2].b = 0; + D_8015DD68.colorEnd[2].a = 0; + D_8015DD68.colorEnd[3].r = 0; + D_8015DD68.colorEnd[3].g = 64; + D_8015DD68.colorEnd[3].b = 0; + D_8015DD68.colorEnd[3].a = 0; + D_8015DD68.timer = 0; + D_8015DD68.duration = 16; + D_8015DD68.speed = 8.0f; + D_8015DD68.gravity = -1.0f; + + Effect_Add(globalCtx, &effectIndex, EFFECT_SPARK, 0, 1, &D_8015DD68); +} + +/** + * Spawns a burst of water. + * Used by collider type HIT4, which no actor has. + */ +void CollisionCheck_WaterBurst(GlobalContext* globalCtx, Collider* collider, Vec3f* pos) { + EffectSsSibuki_SpawnBurst(globalCtx, pos); + CollisionCheck_SpawnWaterDroplets(globalCtx, pos); +} + +/** + * Spawns red blood drops. + * Used by collider type HIT7, which no actor has. + */ +void CollisionCheck_RedBlood(GlobalContext* globalCtx, Collider* collider, Vec3f* v) { + CollisionCheck_SpawnRedBlood(globalCtx, v); +} + +/** + * Spawns red blood drops. + * Unused. + */ +void CollisionCheck_RedBloodUnused(GlobalContext* globalCtx, Collider* collider, Vec3f* v) { + CollisionCheck_SpawnRedBlood(globalCtx, v); +} + +/** + * Plays sound effects and displays hitmarks for solid-type AC colliders (METAL, WOOD, HARD, and TREE) + */ +void CollisionCheck_HitSolid(GlobalContext* globalCtx, ColliderInfo* info, Collider* collider, Vec3f* hitPos) { + s32 flags = info->toucherFlags & TOUCH_SFX_NONE; + + if (flags == TOUCH_SFX_NORMAL && collider->colType != COLTYPE_METAL) { + EffectSsHitMark_SpawnFixedScale(globalCtx, EFFECT_HITMARK_WHITE, hitPos); + if (collider->actor == NULL) { + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_BOUND, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_BOUND, &collider->actor->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } else if (flags == TOUCH_SFX_NORMAL) { // collider->colType == COLTYPE_METAL + EffectSsHitMark_SpawnFixedScale(globalCtx, EFFECT_HITMARK_METAL, hitPos); + if (collider->actor == NULL) { + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, hitPos); + } else { + CollisionCheck_SpawnShieldParticlesMetalSound(globalCtx, hitPos, &collider->actor->projectedPos); + } + } else if (flags == TOUCH_SFX_HARD) { + EffectSsHitMark_SpawnFixedScale(globalCtx, EFFECT_HITMARK_WHITE, hitPos); + if (collider->actor == NULL) { + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_BOUND, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_BOUND, &collider->actor->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } else if (flags == TOUCH_SFX_WOOD) { + EffectSsHitMark_SpawnFixedScale(globalCtx, EFFECT_HITMARK_DUST, hitPos); + if (collider->actor == NULL) { + Audio_PlaySoundGeneral(NA_SE_IT_REFLECTION_WOOD, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_IT_REFLECTION_WOOD, &collider->actor->projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } +} + +/** + * Plays a hit sound effect for AT colliders attached to Player based on the AC element's elemType. + */ +s32 CollisionCheck_SwordHitAudio(Collider* at, ColliderInfo* acInfo) { + if (at->actor != NULL && at->actor->category == ACTORCAT_PLAYER) { + if (acInfo->elemType == ELEMTYPE_UNK0) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_STRIKE, &at->actor->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (acInfo->elemType == ELEMTYPE_UNK1) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_STRIKE_HARD, &at->actor->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (acInfo->elemType == ELEMTYPE_UNK2) { + Audio_PlaySoundGeneral(NA_SE_PL_WALK_GROUND - SFX_FLAG, &at->actor->projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else if (acInfo->elemType == ELEMTYPE_UNK3) { + Audio_PlaySoundGeneral(NA_SE_PL_WALK_GROUND - SFX_FLAG, &at->actor->projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } + return 1; +} + +static ColChkBloodFunc sBloodFuncs[] = { + CollisionCheck_NoBlood, CollisionCheck_BlueBlood, CollisionCheck_GreenBlood, + CollisionCheck_WaterBurst, CollisionCheck_RedBlood, CollisionCheck_RedBloodUnused, +}; +static HitInfo sHitInfo[] = { + { BLOOD_BLUE, HIT_WHITE }, { BLOOD_NONE, HIT_DUST }, { BLOOD_GREEN, HIT_DUST }, { BLOOD_NONE, HIT_WHITE }, + { BLOOD_WATER, HIT_NONE }, { BLOOD_NONE, HIT_RED }, { BLOOD_GREEN, HIT_WHITE }, { BLOOD_RED, HIT_WHITE }, + { BLOOD_BLUE, HIT_RED }, { BLOOD_NONE, HIT_SOLID }, { BLOOD_NONE, HIT_NONE }, { BLOOD_NONE, HIT_SOLID }, + { BLOOD_NONE, HIT_SOLID }, { BLOOD_NONE, HIT_WOOD }, +}; + +/** + * Handles hitmarks, blood, and sound effects for each AC collision, determined by the AC collider's colType + */ +void CollisionCheck_HitEffects(GlobalContext* globalCtx, Collider* at, ColliderInfo* atInfo, Collider* ac, + ColliderInfo* acInfo, Vec3f* hitPos) { + if (acInfo->bumperFlags & BUMP_NO_HITMARK) { + return; + } + if (!(atInfo->toucherFlags & TOUCH_AT_HITMARK) && atInfo->toucherFlags & TOUCH_DREW_HITMARK) { + return; + } + if (ac->actor != NULL) { + sBloodFuncs[sHitInfo[ac->colType].blood](globalCtx, ac, hitPos); + } + if (ac->actor != NULL) { + if (sHitInfo[ac->colType].effect == HIT_SOLID) { + CollisionCheck_HitSolid(globalCtx, atInfo, ac, hitPos); + } else if (sHitInfo[ac->colType].effect == HIT_WOOD) { + if (at->actor == NULL) { + CollisionCheck_SpawnShieldParticles(globalCtx, hitPos); + Audio_PlaySoundGeneral(NA_SE_IT_REFLECTION_WOOD, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + CollisionCheck_SpawnShieldParticlesWood(globalCtx, hitPos, &at->actor->projectedPos); + } + } else if (sHitInfo[ac->colType].effect != HIT_NONE) { + EffectSsHitMark_SpawnFixedScale(globalCtx, sHitInfo[ac->colType].effect, hitPos); + if (!(acInfo->bumperFlags & BUMP_NO_SWORD_SFX)) { + CollisionCheck_SwordHitAudio(at, acInfo); + } + } + } else { + EffectSsHitMark_SpawnFixedScale(globalCtx, EFFECT_HITMARK_WHITE, hitPos); + if (ac->actor == NULL) { + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_BOUND, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_BOUND, &ac->actor->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } +} + +/** + * Sets the flags to indicate an attack bounced off an AC_HARD collider. + */ +void CollisionCheck_SetBounce(Collider* at, Collider* ac) { + at->atFlags |= AT_BOUNCED; + ac->acFlags |= AC_BOUNCED; +} + +/** + * Performs the AC collision between the AT element and AC element that collided. + */ +s32 CollisionCheck_SetATvsAC(GlobalContext* globalCtx, Collider* at, ColliderInfo* atInfo, Vec3f* atPos, Collider* ac, + ColliderInfo* acInfo, Vec3f* acPos, Vec3f* hitPos) { + if (ac->acFlags & AC_HARD && at->actor != NULL && ac->actor != NULL) { + CollisionCheck_SetBounce(at, ac); + } + if (!(acInfo->bumperFlags & BUMP_NO_AT_INFO)) { + at->atFlags |= AT_HIT; + at->at = ac->actor; + atInfo->atHit = ac; + atInfo->atHitInfo = acInfo; + atInfo->toucherFlags |= TOUCH_HIT; + if (at->actor != NULL) { + at->actor->colChkInfo.atHitEffect = acInfo->bumper.effect; + } + } + ac->acFlags |= AC_HIT; + ac->ac = at->actor; + acInfo->acHit = at; + acInfo->acHitInfo = atInfo; + acInfo->bumperFlags |= BUMP_HIT; + if (ac->actor != NULL) { + ac->actor->colChkInfo.acHitEffect = atInfo->toucher.effect; + } + acInfo->bumper.hitPos.x = hitPos->x; + acInfo->bumper.hitPos.y = hitPos->y; + acInfo->bumper.hitPos.z = hitPos->z; + if (!(atInfo->toucherFlags & TOUCH_AT_HITMARK) && ac->colType != COLTYPE_METAL && ac->colType != COLTYPE_WOOD && + ac->colType != COLTYPE_HARD) { + acInfo->bumperFlags |= BUMP_DRAW_HITMARK; + } else { + CollisionCheck_HitEffects(globalCtx, at, atInfo, ac, acInfo, hitPos); + atInfo->toucherFlags |= TOUCH_DREW_HITMARK; + } + return 1; +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_JntSphVsJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderJntSph* at = (ColliderJntSph*)colAT; + ColliderJntSphElement* atItem; + ColliderJntSph* ac = (ColliderJntSph*)colAC; + ColliderJntSphElement* acElem; + f32 overlapSize; + f32 centerDist; + + if (at->count > 0 && at->elements != NULL && ac->count > 0 && ac->elements != NULL) { + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &acElem->info) == 1) { + continue; + } + if (Math3D_SphVsSphOverlapCenter(&atItem->dim.worldSphere, &acElem->dim.worldSphere, &overlapSize, + ¢erDist) == 1) { + f32 acToHit; + Vec3f hitPos; + Vec3f atPos; + Vec3f acPos; + + atPos.x = atItem->dim.worldSphere.center.x; + atPos.y = atItem->dim.worldSphere.center.y; + atPos.z = atItem->dim.worldSphere.center.z; + acPos.x = acElem->dim.worldSphere.center.x; + acPos.y = acElem->dim.worldSphere.center.y; + acPos.z = acElem->dim.worldSphere.center.z; + if (!IS_ZERO(centerDist)) { + acToHit = acElem->dim.worldSphere.radius / centerDist; + hitPos.x = (((atPos.x - acPos.x) * acToHit) + acPos.x); + hitPos.y = (((atPos.y - acPos.y) * acToHit) + acPos.y); + hitPos.z = (((atPos.z - acPos.z) * acToHit) + acPos.z); + } else { + Math_Vec3f_Copy(&hitPos, &atPos); + } + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &acElem->info, + &acPos, &hitPos); + if (!(ac->base.ocFlags2 & OC2_FIRST_ONLY)) { + return; + } + } + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_JntSphVsCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderJntSph* at = (ColliderJntSph*)colAT; + ColliderJntSphElement* atItem; + ColliderCylinder* ac = (ColliderCylinder*)colAC; + f32 overlapSize; + f32 centerDist; + + if (at->count > 0 && at->elements != NULL && ac->dim.radius > 0 && ac->dim.height > 0) { + if (CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &ac->info) == 1) { + continue; + } + if (Math3D_SphVsCylOverlapCenterDist(&atItem->dim.worldSphere, &ac->dim, &overlapSize, ¢erDist)) { + Vec3f hitPos; + Vec3f atPos; + Vec3f acPos; + f32 acToHit; + + atPos.x = atItem->dim.worldSphere.center.x; + atPos.y = atItem->dim.worldSphere.center.y; + atPos.z = atItem->dim.worldSphere.center.z; + acPos.x = ac->dim.pos.x; + acPos.y = ac->dim.pos.y; + acPos.z = ac->dim.pos.z; + if (!IS_ZERO(centerDist)) { + acToHit = ac->dim.radius / centerDist; + if (acToHit <= 1.0f) { + hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; + hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; + hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; + } else { + Math_Vec3f_Copy(&hitPos, &atPos); + } + } else { + Math_Vec3f_Copy(&hitPos, &atPos); + } + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &ac->info, &acPos, + &hitPos); + return; + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_CylVsJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderCylinder* at = (ColliderCylinder*)colAT; + ColliderJntSph* ac = (ColliderJntSph*)colAC; + f32 overlapSize; + f32 centerDist; + ColliderJntSphElement* acElem; + + if (ac->count > 0 && ac->elements != NULL && at->dim.radius > 0 && at->dim.height > 0) { + if (CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&at->info, &acElem->info) == 1) { + continue; + } + if (Math3D_SphVsCylOverlapCenterDist(&acElem->dim.worldSphere, &at->dim, &overlapSize, ¢erDist)) { + Vec3f hitPos; + Vec3f atPos; + Vec3f acPos; + f32 acToHit; + + atPos.x = at->dim.pos.x; + atPos.y = at->dim.pos.y; + atPos.z = at->dim.pos.z; + acPos.x = acElem->dim.worldSphere.center.x; + acPos.y = acElem->dim.worldSphere.center.y; + acPos.z = acElem->dim.worldSphere.center.z; + if (!IS_ZERO(centerDist)) { + acToHit = acElem->dim.worldSphere.radius / centerDist; + if (acToHit <= 1.0f) { + hitPos.x = ((atPos.x - acPos.x) * acToHit) + acPos.x; + hitPos.y = ((atPos.y - acPos.y) * acToHit) + acPos.y; + hitPos.z = ((atPos.z - acPos.z) * acToHit) + acPos.z; + } else { + Math_Vec3f_Copy(&hitPos, &atPos); + } + } else { + Math_Vec3f_Copy(&hitPos, &atPos); + } + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos, &ac->base, &acElem->info, &acPos, + &hitPos); + if (!(ac->base.ocFlags2 & OC2_FIRST_ONLY)) { + break; + } + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_JntSphVsTris(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderJntSph* at = (ColliderJntSph*)colAT; + ColliderJntSphElement* atSph; + ColliderTris* ac = (ColliderTris*)colAC; + ColliderTrisElement* acTri; + Vec3f hitPos; + + if (at->count > 0 && at->elements != NULL && ac->count > 0 && ac->elements != NULL) { + for (atSph = at->elements; atSph < at->elements + at->count; atSph++) { + if (CollisionCheck_SkipTouch(&atSph->info) == 1) { + continue; + } + for (acTri = ac->elements; acTri < ac->elements + ac->count; acTri++) { + if (CollisionCheck_SkipBump(&acTri->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atSph->info, &acTri->info) == 1) { + continue; + } + if (Math3D_TriVsSphIntersect(&atSph->dim.worldSphere, &acTri->dim, &hitPos) == 1) { + Vec3f atPos; + Vec3f acPos; + + atPos.x = atSph->dim.worldSphere.center.x; + atPos.y = atSph->dim.worldSphere.center.y; + atPos.z = atSph->dim.worldSphere.center.z; + acPos.x = (acTri->dim.vtx[0].x + acTri->dim.vtx[1].x + acTri->dim.vtx[2].x) * (1.0f / 3); + acPos.y = (acTri->dim.vtx[0].y + acTri->dim.vtx[1].y + acTri->dim.vtx[2].y) * (1.0f / 3); + acPos.z = (acTri->dim.vtx[0].z + acTri->dim.vtx[1].z + acTri->dim.vtx[2].z) * (1.0f / 3); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atSph->info, &atPos, &ac->base, &acTri->info, + &acPos, &hitPos); + return; + } + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_TrisVsJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderTris* at = (ColliderTris*)colAT; + ColliderTrisElement* atItem; + ColliderJntSph* ac = (ColliderJntSph*)colAC; + ColliderJntSphElement* acElem; + Vec3f hitPos; + + if (ac->count > 0 && ac->elements != NULL && at->count > 0 && at->elements != NULL) { + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &acElem->info) == 1) { + continue; + } + if (Math3D_TriVsSphIntersect(&acElem->dim.worldSphere, &atItem->dim, &hitPos) == 1) { + Vec3f atPos; + Vec3f acPos; + + Math_Vec3s_ToVec3f(&acPos, &acElem->dim.worldSphere.center); + atPos.x = (atItem->dim.vtx[0].x + atItem->dim.vtx[1].x + atItem->dim.vtx[2].x) * (1.0f / 3); + atPos.y = (atItem->dim.vtx[0].y + atItem->dim.vtx[1].y + atItem->dim.vtx[2].y) * (1.0f / 3); + atPos.z = (atItem->dim.vtx[0].z + atItem->dim.vtx[1].z + atItem->dim.vtx[2].z) * (1.0f / 3); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &acElem->info, + &acPos, &hitPos); + if (!(ac->base.ocFlags2 & OC2_FIRST_ONLY)) { + return; + } + } + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_JntSphVsQuad(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static TriNorm D_8015E230; + static TriNorm D_8015E268; + ColliderJntSph* at = (ColliderJntSph*)colAT; + ColliderQuad* ac = (ColliderQuad*)colAC; + Vec3f hitPos; + ColliderJntSphElement* atItem; + + if (at->count > 0 && at->elements != NULL) { + if (CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + Math3D_TriNorm(&D_8015E230, &ac->dim.quad[2], &ac->dim.quad[3], &ac->dim.quad[1]); + Math3D_TriNorm(&D_8015E268, &ac->dim.quad[1], &ac->dim.quad[0], &ac->dim.quad[2]); + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &ac->info) == 1) { + continue; + } + if (Math3D_TriVsSphIntersect(&atItem->dim.worldSphere, &D_8015E230, &hitPos) == 1 || + Math3D_TriVsSphIntersect(&atItem->dim.worldSphere, &D_8015E268, &hitPos) == 1) { + Vec3f atPos; + Vec3f acPos; + + Math_Vec3s_ToVec3f(&atPos, &atItem->dim.worldSphere.center); + + acPos.x = (ac->dim.quad[0].x + (ac->dim.quad[1].x + (ac->dim.quad[3].x + ac->dim.quad[2].x))) / 4.0f; + acPos.y = (ac->dim.quad[0].y + (ac->dim.quad[1].y + (ac->dim.quad[3].y + ac->dim.quad[2].y))) / 4.0f; + acPos.z = (ac->dim.quad[0].z + (ac->dim.quad[1].z + (ac->dim.quad[3].z + ac->dim.quad[2].z))) / 4.0f; + + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &ac->info, &acPos, + &hitPos); + return; + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_QuadVsJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static TriNorm D_8015E2A0; + static TriNorm D_8015E2D8; + ColliderJntSph* ac = (ColliderJntSph*)colAC; + Vec3f hitPos; + ColliderQuad* at = (ColliderQuad*)colAT; + ColliderJntSphElement* acElem; + + if (ac->count > 0 && ac->elements != NULL) { + if (CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + Math3D_TriNorm(&D_8015E2A0, &at->dim.quad[2], &at->dim.quad[3], &at->dim.quad[1]); + Math3D_TriNorm(&D_8015E2D8, &at->dim.quad[2], &at->dim.quad[1], &at->dim.quad[0]); + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&at->info, &acElem->info) == 1) { + continue; + } + if (Math3D_TriVsSphIntersect(&acElem->dim.worldSphere, &D_8015E2A0, &hitPos) == 1 || + Math3D_TriVsSphIntersect(&acElem->dim.worldSphere, &D_8015E2D8, &hitPos) == 1) { + if (Collider_QuadSetNearestAC(globalCtx, at, &hitPos)) { + Vec3f atPos; + Vec3f acPos; + + acPos.x = acElem->dim.worldSphere.center.x; + acPos.y = acElem->dim.worldSphere.center.y; + acPos.z = acElem->dim.worldSphere.center.z; + + atPos.x = + (at->dim.quad[0].x + (at->dim.quad[1].x + (at->dim.quad[3].x + at->dim.quad[2].x))) / 4.0f; + atPos.y = + (at->dim.quad[0].y + (at->dim.quad[1].y + (at->dim.quad[3].y + at->dim.quad[2].y))) / 4.0f; + atPos.z = + (at->dim.quad[0].z + (at->dim.quad[1].z + (at->dim.quad[3].z + at->dim.quad[2].z))) / 4.0f; + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos, &ac->base, &acElem->info, &acPos, + &hitPos); + if (!(ac->base.ocFlags2 & OC2_FIRST_ONLY)) { + return; + } + } + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_CylVsCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderCylinder* at = (ColliderCylinder*)colAT; + ColliderCylinder* ac = (ColliderCylinder*)colAC; + f32 deadSpace; + f32 centerDistXZ; + Vec3f hitPos; + + if (at->dim.radius > 0 && at->dim.height > 0 && ac->dim.radius > 0 && ac->dim.height > 0) { + if (CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + if (CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + if (CollisionCheck_NoSharedFlags(&at->info, &ac->info) == 1) { + return; + } + if (Math3D_CylOutsideCylDist(&at->dim, &ac->dim, &deadSpace, ¢erDistXZ) == 1) { + Vec3f atPos; + Vec3f acPos; + f32 acToHit; + + Math_Vec3s_ToVec3f(&atPos, &at->dim.pos); + Math_Vec3s_ToVec3f(&acPos, &ac->dim.pos); + if (!IS_ZERO(centerDistXZ)) { + acToHit = ac->dim.radius / centerDistXZ; + hitPos.y = (f32)ac->dim.pos.y + ac->dim.yShift + ac->dim.height * 0.5f; + hitPos.x = ((f32)at->dim.pos.x - ac->dim.pos.x) * acToHit + ac->dim.pos.x; + hitPos.z = ((f32)at->dim.pos.z - ac->dim.pos.z) * acToHit + ac->dim.pos.z; + } else { + Math_Vec3s_ToVec3f(&hitPos, &ac->dim.pos); + } + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos, &ac->base, &ac->info, &acPos, &hitPos); + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_CylVsTris(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + ColliderCylinder* at = (ColliderCylinder*)colAT; + ColliderTris* ac = (ColliderTris*)colAC; + ColliderTrisElement* acElem; + Vec3f hitPos; + + if (at->dim.radius > 0 && at->dim.height > 0 && ac->count > 0 && ac->elements != NULL) { + if (CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&at->info, &acElem->info) == 1) { + continue; + } + if (Math3D_CylTriVsIntersect(&at->dim, &acElem->dim, &hitPos) == 1) { + Vec3f atpos; + Vec3f acPos; + + Math_Vec3s_ToVec3f(&atpos, &at->dim.pos); + + acPos.x = (acElem->dim.vtx[0].x + acElem->dim.vtx[1].x + acElem->dim.vtx[2].x) * (1.0f / 3); + acPos.y = (acElem->dim.vtx[0].y + acElem->dim.vtx[1].y + acElem->dim.vtx[2].y) * (1.0f / 3); + acPos.z = (acElem->dim.vtx[0].z + acElem->dim.vtx[1].z + acElem->dim.vtx[2].z) * (1.0f / 3); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atpos, &ac->base, &acElem->info, &acPos, + &hitPos); + return; + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_TrisVsCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static Vec3f D_8015E310; + ColliderTris* at = (ColliderTris*)colAT; + ColliderTrisElement* atItem; + ColliderCylinder* ac = (ColliderCylinder*)colAC; + Vec3f atPos; + Vec3f acPos; + + if (ac->dim.radius > 0 && ac->dim.height > 0 && at->count > 0 && at->elements != NULL) { + if (CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &ac->info) == 1) { + continue; + } + + if (Math3D_CylTriVsIntersect(&ac->dim, &atItem->dim, &D_8015E310) == 1) { + atPos.x = (atItem->dim.vtx[0].x + atItem->dim.vtx[1].x + atItem->dim.vtx[2].x) * (1.0f / 3); + atPos.y = (atItem->dim.vtx[0].y + atItem->dim.vtx[1].y + atItem->dim.vtx[2].y) * (1.0f / 3); + atPos.z = (atItem->dim.vtx[0].z + atItem->dim.vtx[1].z + atItem->dim.vtx[2].z) * (1.0f / 3); + Math_Vec3s_ToVec3f(&acPos, &ac->dim.pos); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &ac->info, &acPos, + &D_8015E310); + return; + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_CylVsQuad(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static TriNorm D_8015E320; + static TriNorm D_8015E358; + static Vec3f D_8015E390; + ColliderCylinder* at = (ColliderCylinder*)colAT; + ColliderQuad* ac = (ColliderQuad*)colAC; + + if (at->dim.height > 0 && at->dim.radius > 0) { + if (CollisionCheck_SkipTouch(&at->info) == 1 || CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + if (CollisionCheck_NoSharedFlags(&at->info, &ac->info) == 1) { + return; + } + Math3D_TriNorm(&D_8015E320, &ac->dim.quad[2], &ac->dim.quad[3], &ac->dim.quad[1]); + Math3D_TriNorm(&D_8015E358, &ac->dim.quad[1], &ac->dim.quad[0], &ac->dim.quad[2]); + if (Math3D_CylTriVsIntersect(&at->dim, &D_8015E320, &D_8015E390) == 1) { + Vec3f atPos1; + Vec3f acPos1; + + Math_Vec3s_ToVec3f(&atPos1, &at->dim.pos); + acPos1.x = (ac->dim.quad[0].x + (ac->dim.quad[1].x + (ac->dim.quad[3].x + ac->dim.quad[2].x))) / 4.0f; + acPos1.y = (ac->dim.quad[0].y + (ac->dim.quad[1].y + (ac->dim.quad[3].y + ac->dim.quad[2].y))) / 4.0f; + acPos1.z = (ac->dim.quad[0].z + (ac->dim.quad[1].z + (ac->dim.quad[3].z + ac->dim.quad[2].z))) / 4.0f; + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos1, &ac->base, &ac->info, &acPos1, + &D_8015E390); + } else if (Math3D_CylTriVsIntersect(&at->dim, &D_8015E358, &D_8015E390) == 1) { + Vec3f atPos2; + Vec3f acPos2; + + Math_Vec3s_ToVec3f(&atPos2, &at->dim.pos); + acPos2.x = (ac->dim.quad[0].x + (ac->dim.quad[1].x + (ac->dim.quad[3].x + ac->dim.quad[2].x))) / 4.0f; + acPos2.y = (ac->dim.quad[0].y + (ac->dim.quad[1].y + (ac->dim.quad[3].y + ac->dim.quad[2].y))) / 4.0f; + acPos2.z = (ac->dim.quad[0].z + (ac->dim.quad[1].z + (ac->dim.quad[3].z + ac->dim.quad[2].z))) / 4.0f; + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos2, &ac->base, &ac->info, &acPos2, + &D_8015E390); + } + } +} + +static s8 sBssDummy0; +static s8 sBssDummy1; + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_QuadVsCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static TriNorm D_8015E3A0; + static TriNorm D_8015E3D8; + static Vec3f D_8015E410; + ColliderQuad* at = (ColliderQuad*)colAT; + ColliderCylinder* ac = (ColliderCylinder*)colAC; + + if (ac->dim.height > 0 && ac->dim.radius > 0) { + if (CollisionCheck_SkipBump(&ac->info) == 1 || CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + if (CollisionCheck_NoSharedFlags(&at->info, &ac->info) == 1) { + return; + } + Math3D_TriNorm(&D_8015E3A0, &at->dim.quad[2], &at->dim.quad[3], &at->dim.quad[1]); + Math3D_TriNorm(&D_8015E3D8, &at->dim.quad[2], &at->dim.quad[1], &at->dim.quad[0]); + if (Math3D_CylTriVsIntersect(&ac->dim, &D_8015E3A0, &D_8015E410) == 1) { + if (Collider_QuadSetNearestAC(globalCtx, at, &D_8015E410)) { + Vec3f atPos1; + Vec3f acPos1; + + atPos1.x = (at->dim.quad[0].x + (at->dim.quad[1].x + (at->dim.quad[3].x + at->dim.quad[2].x))) / 4.0f; + atPos1.y = (at->dim.quad[0].y + (at->dim.quad[1].y + (at->dim.quad[3].y + at->dim.quad[2].y))) / 4.0f; + atPos1.z = (at->dim.quad[0].z + (at->dim.quad[1].z + (at->dim.quad[3].z + at->dim.quad[2].z))) / 4.0f; + Math_Vec3s_ToVec3f(&acPos1, &ac->dim.pos); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos1, &ac->base, &ac->info, &acPos1, + &D_8015E410); + return; + } + } + if (Math3D_CylTriVsIntersect(&ac->dim, &D_8015E3D8, &D_8015E410) == 1) { + if (Collider_QuadSetNearestAC(globalCtx, at, &D_8015E410)) { + Vec3f atPos2; + Vec3f acPos2; + + atPos2.x = (at->dim.quad[0].x + (at->dim.quad[1].x + (at->dim.quad[3].x + at->dim.quad[2].x))) / 4.0f; + atPos2.y = (at->dim.quad[0].y + (at->dim.quad[1].y + (at->dim.quad[3].y + at->dim.quad[2].y))) / 4.0f; + atPos2.z = (at->dim.quad[0].z + (at->dim.quad[1].z + (at->dim.quad[3].z + at->dim.quad[2].z))) / 4.0f; + Math_Vec3s_ToVec3f(&acPos2, &ac->dim.pos); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos2, &ac->base, &ac->info, &acPos2, + &D_8015E410); + } + } + } +} + +static s8 sBssDummy3; +static s8 sBssDummy4; +static s8 sBssDummy5; +static s8 sBssDummy6; + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_TrisVsTris(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static Vec3f D_8015E420; + ColliderTris* at = (ColliderTris*)colAT; + ColliderTrisElement* atItem; + ColliderTris* ac = (ColliderTris*)colAC; + ColliderTrisElement* acElem; + + if (ac->count > 0 && ac->elements != NULL && at->count > 0 && at->elements != NULL) { + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &acElem->info) == 1) { + continue; + } + if (Math3D_TriVsTriIntersect(&atItem->dim, &acElem->dim, &D_8015E420) == 1) { + Vec3f atPos; + Vec3f acPos; + + atPos.x = (atItem->dim.vtx[0].x + atItem->dim.vtx[1].x + atItem->dim.vtx[2].x) * (1.0f / 3); + atPos.y = (atItem->dim.vtx[0].y + atItem->dim.vtx[1].y + atItem->dim.vtx[2].y) * (1.0f / 3); + atPos.z = (atItem->dim.vtx[0].z + atItem->dim.vtx[1].z + atItem->dim.vtx[2].z) * (1.0f / 3); + acPos.x = (acElem->dim.vtx[0].x + acElem->dim.vtx[1].x + acElem->dim.vtx[2].x) * (1.0f / 3); + acPos.y = (acElem->dim.vtx[0].y + acElem->dim.vtx[1].y + acElem->dim.vtx[2].y) * (1.0f / 3); + acPos.z = (acElem->dim.vtx[0].z + acElem->dim.vtx[1].z + acElem->dim.vtx[2].z) * (1.0f / 3); + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &acElem->info, + &acPos, &D_8015E420); + return; + } + } + } + } +} + +static s8 sBssDummy7; +static s8 sBssDummy8; +static s8 sBssDummy9; +static s8 sBssDummy10; + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_TrisVsQuad(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static Vec3f D_8015E430; + static TriNorm D_8015E440; + static TriNorm D_8015E478; + ColliderTris* at = (ColliderTris*)colAT; + ColliderTrisElement* atItem; + ColliderQuad* ac = (ColliderQuad*)colAC; + + if (at->count > 0 && at->elements != NULL) { + if (CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + Math3D_TriNorm(&D_8015E440, &ac->dim.quad[2], &ac->dim.quad[3], &ac->dim.quad[1]); + Math3D_TriNorm(&D_8015E478, &ac->dim.quad[1], &ac->dim.quad[0], &ac->dim.quad[2]); + for (atItem = at->elements; atItem < at->elements + at->count; atItem++) { + if (CollisionCheck_SkipTouch(&atItem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&atItem->info, &ac->info) == 1) { + continue; + } + if (Math3D_TriVsTriIntersect(&D_8015E440, &atItem->dim, &D_8015E430) == 1 || + Math3D_TriVsTriIntersect(&D_8015E478, &atItem->dim, &D_8015E430) == 1) { + Vec3f atPos; + Vec3f acPos; + + atPos.x = (atItem->dim.vtx[0].x + atItem->dim.vtx[1].x + atItem->dim.vtx[2].x) * (1.0f / 3); + atPos.y = (atItem->dim.vtx[0].y + atItem->dim.vtx[1].y + atItem->dim.vtx[2].y) * (1.0f / 3); + atPos.z = (atItem->dim.vtx[0].z + atItem->dim.vtx[1].z + atItem->dim.vtx[2].z) * (1.0f / 3); + acPos.x = (ac->dim.quad[0].x + (ac->dim.quad[1].x + (ac->dim.quad[3].x + ac->dim.quad[2].x))) / 4.0f; + acPos.y = (ac->dim.quad[0].y + (ac->dim.quad[1].y + (ac->dim.quad[3].y + ac->dim.quad[2].y))) / 4.0f; + acPos.z = (ac->dim.quad[0].z + (ac->dim.quad[1].z + (ac->dim.quad[3].z + ac->dim.quad[2].z))) / 4.0f; + CollisionCheck_SetATvsAC(globalCtx, &at->base, &atItem->info, &atPos, &ac->base, &ac->info, &acPos, + &D_8015E430); + return; + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_QuadVsTris(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static Vec3f D_8015E4B0; + static TriNorm D_8015E4C0; + static TriNorm D_8015E4F8; + ColliderQuad* at = (ColliderQuad*)colAT; + ColliderTris* ac = (ColliderTris*)colAC; + ColliderTrisElement* acElem; + + if (ac->count > 0 && ac->elements != NULL) { + if (CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + Math3D_TriNorm(&D_8015E4C0, &at->dim.quad[2], &at->dim.quad[3], &at->dim.quad[1]); + Math3D_TriNorm(&D_8015E4F8, &at->dim.quad[1], &at->dim.quad[0], &at->dim.quad[2]); + for (acElem = ac->elements; acElem < ac->elements + ac->count; acElem++) { + if (CollisionCheck_SkipBump(&acElem->info) == 1) { + continue; + } + if (CollisionCheck_NoSharedFlags(&at->info, &acElem->info) == 1) { + continue; + } + if (Math3D_TriVsTriIntersect(&D_8015E4C0, &acElem->dim, &D_8015E4B0) == 1 || + Math3D_TriVsTriIntersect(&D_8015E4F8, &acElem->dim, &D_8015E4B0) == 1) { + if (Collider_QuadSetNearestAC(globalCtx, at, &D_8015E4B0)) { + Vec3f atPos; + Vec3f acPos; + + acPos.x = (acElem->dim.vtx[0].x + acElem->dim.vtx[1].x + acElem->dim.vtx[2].x) * (1.0f / 3); + acPos.y = (acElem->dim.vtx[0].y + acElem->dim.vtx[1].y + acElem->dim.vtx[2].y) * (1.0f / 3); + acPos.z = (acElem->dim.vtx[0].z + acElem->dim.vtx[1].z + acElem->dim.vtx[2].z) * (1.0f / 3); + atPos.x = + (at->dim.quad[0].x + (at->dim.quad[1].x + (at->dim.quad[3].x + at->dim.quad[2].x))) / 4.0f; + atPos.y = + (at->dim.quad[0].y + (at->dim.quad[1].y + (at->dim.quad[3].y + at->dim.quad[2].y))) / 4.0f; + atPos.z = + (at->dim.quad[0].z + (at->dim.quad[1].z + (at->dim.quad[3].z + at->dim.quad[2].z))) / 4.0f; + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos, &ac->base, &acElem->info, &acPos, + &D_8015E4B0); + return; + } + } + } + } +} + +/** + * AC overlap check. Calculates the center of each collider element and the point of contact. + */ +void CollisionCheck_AC_QuadVsQuad(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT, + Collider* colAC) { + static TriNorm D_8015E530[2]; + static Vec3f D_8015E598; + static TriNorm D_8015E5A8[2]; + ColliderQuad* at = (ColliderQuad*)colAT; + ColliderQuad* ac = (ColliderQuad*)colAC; + s32 i; + s32 j; + + if (CollisionCheck_SkipTouch(&at->info) == 1) { + return; + } + if (CollisionCheck_SkipBump(&ac->info) == 1) { + return; + } + if (CollisionCheck_NoSharedFlags(&at->info, &ac->info) == 1) { + return; + } + + Math3D_TriNorm(&D_8015E5A8[0], &at->dim.quad[2], &at->dim.quad[3], &at->dim.quad[1]); + Math3D_TriNorm(&D_8015E5A8[1], &at->dim.quad[2], &at->dim.quad[1], &at->dim.quad[0]); + Math3D_TriNorm(&D_8015E530[0], &ac->dim.quad[2], &ac->dim.quad[3], &ac->dim.quad[1]); + Math3D_TriNorm(&D_8015E530[1], &ac->dim.quad[2], &ac->dim.quad[1], &ac->dim.quad[0]); + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + if (Math3D_TriVsTriIntersect(&D_8015E5A8[j], &D_8015E530[i], &D_8015E598) == 1) { + if (Collider_QuadSetNearestAC(globalCtx, at, &D_8015E598)) { + Vec3f atPos; + Vec3f acPos; + + atPos.x = + (at->dim.quad[0].x + (at->dim.quad[1].x + (at->dim.quad[3].x + at->dim.quad[2].x))) / 4.0f; + atPos.y = + (at->dim.quad[0].y + (at->dim.quad[1].y + (at->dim.quad[3].y + at->dim.quad[2].y))) / 4.0f; + atPos.z = + (at->dim.quad[0].z + (at->dim.quad[1].z + (at->dim.quad[3].z + at->dim.quad[2].z))) / 4.0f; + acPos.x = + (ac->dim.quad[0].x + (ac->dim.quad[1].x + (ac->dim.quad[3].x + ac->dim.quad[2].x))) / 4.0f; + acPos.y = + (ac->dim.quad[0].y + (ac->dim.quad[1].y + (ac->dim.quad[3].y + ac->dim.quad[2].y))) / 4.0f; + acPos.z = + (ac->dim.quad[0].z + (ac->dim.quad[1].z + (ac->dim.quad[3].z + ac->dim.quad[2].z))) / 4.0f; + CollisionCheck_SetATvsAC(globalCtx, &at->base, &at->info, &atPos, &ac->base, &ac->info, &acPos, + &D_8015E598); + return; + } + } + } + } +} + +/** + * Sets a ColliderJntSph's hit effects + */ +void CollisionCheck_SetJntSphHitFX(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderJntSph* jntSph = (ColliderJntSph*)collider; + ColliderJntSphElement* element; + Vec3f hitPos; + + for (element = jntSph->elements; element < jntSph->elements + jntSph->count; element++) { + if ((element->info.bumperFlags & BUMP_DRAW_HITMARK) && (element->info.acHitInfo != NULL) && + !(element->info.acHitInfo->toucherFlags & TOUCH_DREW_HITMARK)) { + Math_Vec3s_ToVec3f(&hitPos, &element->info.bumper.hitPos); + CollisionCheck_HitEffects(globalCtx, element->info.acHit, element->info.acHitInfo, &jntSph->base, + &element->info, &hitPos); + element->info.acHitInfo->toucherFlags |= TOUCH_DREW_HITMARK; + return; + } + } +} + +/** + * Sets a ColliderCylinder's hit effects + */ +void CollisionCheck_SetCylHitFX(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderCylinder* cylinder = (ColliderCylinder*)collider; + Vec3f hitPos; + + if ((cylinder->info.bumperFlags & BUMP_DRAW_HITMARK) && (cylinder->info.acHitInfo != NULL) && + !(cylinder->info.acHitInfo->toucherFlags & TOUCH_DREW_HITMARK)) { + Math_Vec3s_ToVec3f(&hitPos, &cylinder->info.bumper.hitPos); + CollisionCheck_HitEffects(globalCtx, cylinder->info.acHit, cylinder->info.acHitInfo, &cylinder->base, + &cylinder->info, &hitPos); + cylinder->info.acHitInfo->toucherFlags |= TOUCH_DREW_HITMARK; + } +} + +/** + * Sets a ColliderTris's hit effects + */ +void CollisionCheck_SetTrisHitFX(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderTris* tris = (ColliderTris*)collider; + ColliderTrisElement* element; + Vec3f hitPos; + + for (element = tris->elements; element < tris->elements + tris->count; element++) { + if ((element->info.bumperFlags & BUMP_DRAW_HITMARK) && (element->info.acHitInfo != NULL) && + !(element->info.acHitInfo->toucherFlags & TOUCH_DREW_HITMARK)) { + Math_Vec3s_ToVec3f(&hitPos, &element->info.bumper.hitPos); + CollisionCheck_HitEffects(globalCtx, element->info.acHit, element->info.acHitInfo, &tris->base, + &element->info, &hitPos); + element->info.acHitInfo->toucherFlags |= TOUCH_DREW_HITMARK; + return; + } + } +} + +/** + * Sets a ColliderQuad's hit effects + */ +void CollisionCheck_SetQuadHitFX(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderQuad* quad = (ColliderQuad*)collider; + Vec3f hitPos; + + if ((quad->info.bumperFlags & BUMP_DRAW_HITMARK) && (quad->info.acHitInfo != NULL) && + !(quad->info.acHitInfo->toucherFlags & TOUCH_DREW_HITMARK)) { + Math_Vec3s_ToVec3f(&hitPos, &quad->info.bumper.hitPos); + CollisionCheck_HitEffects(globalCtx, quad->info.acHit, quad->info.acHitInfo, &quad->base, &quad->info, &hitPos); + quad->info.acHitInfo->toucherFlags |= TOUCH_DREW_HITMARK; + } +} + +static ColChkApplyFunc sColChkApplyFuncs[] = { + CollisionCheck_SetJntSphHitFX, + CollisionCheck_SetCylHitFX, + CollisionCheck_SetTrisHitFX, + CollisionCheck_SetQuadHitFX, +}; + +/** + * Handles hit effects for each AC collider that had an AC collision. Spawns hitmarks and plays sound effects. + */ +void CollisionCheck_SetHitEffects(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + Collider** col; + + for (col = colChkCtx->colAC; col < colChkCtx->colAC + colChkCtx->colACCount; col++) { + Collider* colAC = *col; + + if (colAC != NULL && colAC->acFlags & AC_ON) { + if (colAC->actor != NULL && colAC->actor->update == NULL) { + continue; + } + sColChkApplyFuncs[colAC->shape](globalCtx, colChkCtx, colAC); + } + } +} + +static ColChkVsFunc sACVsFuncs[4][4] = { + { CollisionCheck_AC_JntSphVsJntSph, CollisionCheck_AC_JntSphVsCyl, CollisionCheck_AC_JntSphVsTris, + CollisionCheck_AC_JntSphVsQuad }, + { CollisionCheck_AC_CylVsJntSph, CollisionCheck_AC_CylVsCyl, CollisionCheck_AC_CylVsTris, + CollisionCheck_AC_CylVsQuad }, + { CollisionCheck_AC_TrisVsJntSph, CollisionCheck_AC_TrisVsCyl, CollisionCheck_AC_TrisVsTris, + CollisionCheck_AC_TrisVsQuad }, + { CollisionCheck_AC_QuadVsJntSph, CollisionCheck_AC_QuadVsCyl, CollisionCheck_AC_QuadVsTris, + CollisionCheck_AC_QuadVsQuad }, +}; + +/** + * Iterates through all AC colliders, performing AC collisions with the AT collider. + */ +void CollisionCheck_AC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* colAT) { + Collider** col; + + for (col = colChkCtx->colAC; col < colChkCtx->colAC + colChkCtx->colACCount; col++) { + Collider* colAC = *col; + + if (colAC != NULL && colAC->acFlags & AC_ON) { + if (colAC->actor != NULL && colAC->actor->update == NULL) { + continue; + } + if ((colAC->acFlags & colAT->atFlags & AC_TYPE_ALL) && (colAT != colAC)) { + if (!(colAT->atFlags & AT_SELF) && colAT->actor != NULL && colAC->actor == colAT->actor) { + continue; + } + sACVsFuncs[colAT->shape][colAC->shape](globalCtx, colChkCtx, colAT, colAC); + } + } + } +} + +/** + * Iterates through all AT colliders, testing them for AC collisions with each AC collider, setting the info regarding + * the collision for each AC and AT collider that collided. Then spawns hitmarks and plays sound effects for each + * successful collision. To collide, an AT collider must share a type (AC_TYPE_PLAYER, AC_TYPE_ENEMY, or AC_TYPE_OTHER) + * with the AC collider and the toucher and bumper elements that overlapped must share a dmgFlag. + */ +void CollisionCheck_AT(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + Collider** col; + + if (colChkCtx->colATCount == 0 || colChkCtx->colACCount == 0) { + return; + } + for (col = colChkCtx->colAT; col < colChkCtx->colAT + colChkCtx->colATCount; col++) { + Collider* colAT = *col; + + if (colAT != NULL && colAT->atFlags & AT_ON) { + if (colAT->actor != NULL && colAT->actor->update == NULL) { + continue; + } + CollisionCheck_AC(globalCtx, colChkCtx, colAT); + } + } + CollisionCheck_SetHitEffects(globalCtx, colChkCtx); +} + +/** + * Get mass type. Immobile colliders cannot be pushed, while heavy colliders can only be pushed by heavy and immobile + * colliders. + */ +s32 CollisionCheck_GetMassType(u8 mass) { + if (mass == MASS_IMMOVABLE) { + return MASSTYPE_IMMOVABLE; + } + if (mass == MASS_HEAVY) { + return MASSTYPE_HEAVY; + } + return MASSTYPE_NORMAL; +} + +/** + * Sets OC collision flags for OC collider overlaps. If both colliders are attached to actors and can push, + * also performs an elastic collision where both colliders are moved apart in proportion to their masses. + */ +void CollisionCheck_SetOCvsOC(Collider* left, ColliderInfo* leftInfo, Vec3f* leftPos, Collider* right, + ColliderInfo* rightInfo, Vec3f* rightPos, f32 overlap) { + f32 pad; + f32 leftDispRatio; + f32 rightDispRatio; + f32 xzDist; + f32 leftMass; + f32 rightMass; + f32 totalMass; + f32 inverseTotalMass; + f32 xDelta; + f32 zDelta; + Actor* leftActor = left->actor; + Actor* rightActor = right->actor; + s32 leftMassType; + s32 rightMassType; + + left->ocFlags1 |= OC1_HIT; + left->oc = rightActor; + leftInfo->ocElemFlags |= OCELEM_HIT; + if (right->ocFlags2 & OC2_TYPE_PLAYER) { + left->ocFlags2 |= OC2_HIT_PLAYER; + } + right->oc = leftActor; + right->ocFlags1 |= OC1_HIT; + rightInfo->ocElemFlags |= OCELEM_HIT; + if (left->ocFlags2 & OC2_TYPE_PLAYER) { + right->ocFlags2 |= OC2_HIT_PLAYER; + } + if (leftActor == NULL || rightActor == NULL || left->ocFlags1 & OC1_NO_PUSH || right->ocFlags1 & OC1_NO_PUSH) { + return; + } + rightMassType = CollisionCheck_GetMassType(leftActor->colChkInfo.mass); + leftMassType = CollisionCheck_GetMassType(rightActor->colChkInfo.mass); + leftMass = leftActor->colChkInfo.mass; + rightMass = rightActor->colChkInfo.mass; + totalMass = leftMass + rightMass; + if (IS_ZERO(totalMass)) { + leftMass = rightMass = 1.0f; + totalMass = 2.0f; + } + xDelta = rightPos->x - leftPos->x; + zDelta = rightPos->z - leftPos->z; + xzDist = sqrtf(SQ(xDelta) + SQ(zDelta)); + + if (rightMassType == MASSTYPE_IMMOVABLE) { + if (leftMassType == MASSTYPE_IMMOVABLE) { + return; + } else { // leftMassType == MASS_HEAVY | MASS_NORMAL + leftDispRatio = 0; + rightDispRatio = 1; + } + } else if (rightMassType == MASSTYPE_HEAVY) { + if (leftMassType == MASSTYPE_IMMOVABLE) { + leftDispRatio = 1; + rightDispRatio = 0; + } else if (leftMassType == MASSTYPE_HEAVY) { + leftDispRatio = 0.5f; + rightDispRatio = 0.5f; + } else { // leftMassType == MASS_NORMAL + leftDispRatio = 0; + rightDispRatio = 1; + } + } else { // rightMassType == MASS_NORMAL + if (leftMassType == MASSTYPE_NORMAL) { + inverseTotalMass = 1 / totalMass; + leftDispRatio = rightMass * inverseTotalMass; + rightDispRatio = leftMass * inverseTotalMass; + } else { // leftMassType == MASS_HEAVY | MASS_IMMOVABLE + leftDispRatio = 1; + rightDispRatio = 0; + } + } + + if (!IS_ZERO(xzDist)) { + xDelta *= overlap / xzDist; + zDelta *= overlap / xzDist; + leftActor->colChkInfo.displacement.x += -xDelta * leftDispRatio; + leftActor->colChkInfo.displacement.z += -zDelta * leftDispRatio; + rightActor->colChkInfo.displacement.x += xDelta * rightDispRatio; + rightActor->colChkInfo.displacement.z += zDelta * rightDispRatio; + } else if (!(overlap == 0.0f)) { + leftActor->colChkInfo.displacement.x += -overlap * leftDispRatio; + rightActor->colChkInfo.displacement.x += overlap * rightDispRatio; + } else { + leftActor->colChkInfo.displacement.x -= leftDispRatio; + rightActor->colChkInfo.displacement.x += rightDispRatio; + } +} + +/** + * OC overlap check for two JntSphs + */ +void CollisionCheck_OC_JntSphVsJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* l, + Collider* r) { + ColliderJntSph* left = (ColliderJntSph*)l; + ColliderJntSphElement* leftElem; + ColliderJntSph* right = (ColliderJntSph*)r; + ColliderJntSphElement* rightElem; + f32 overlap; + + if (left->count > 0 && left->elements != NULL && right->count > 0 && right->elements != NULL) { + for (leftElem = left->elements; leftElem < left->elements + left->count; leftElem++) { + if (!(leftElem->info.ocElemFlags & OCELEM_ON)) { + continue; + } + for (rightElem = right->elements; rightElem < right->elements + right->count; rightElem++) { + if (!(rightElem->info.ocElemFlags & OCELEM_ON)) { + continue; + } + if (Math3D_SphVsSphOverlap(&leftElem->dim.worldSphere, &rightElem->dim.worldSphere, &overlap) == 1) { + Vec3f leftPos; + Vec3f rightPos; + + Math_Vec3s_ToVec3f(&leftPos, &leftElem->dim.worldSphere.center); + Math_Vec3s_ToVec3f(&rightPos, &rightElem->dim.worldSphere.center); + CollisionCheck_SetOCvsOC(&left->base, &leftElem->info, &leftPos, &right->base, &rightElem->info, + &rightPos, overlap); + } + } + } + } +} + +/** + * OC overlap check for a JntSph and Cylinder + */ +void CollisionCheck_OC_JntSphVsCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* l, + Collider* r) { + ColliderJntSph* left = (ColliderJntSph*)l; + ColliderJntSphElement* leftElem; + ColliderCylinder* right = (ColliderCylinder*)r; + f32 overlap; + + if (left->count > 0 && left->elements != NULL) { + if ((right->base.ocFlags1 & OC1_ON) && (right->info.ocElemFlags & OCELEM_ON)) { + for (leftElem = left->elements; leftElem < left->elements + left->count; leftElem++) { + if (!(leftElem->info.ocElemFlags & OCELEM_ON)) { + continue; + } + if (Math3D_SphVsCylOverlapDist(&leftElem->dim.worldSphere, &right->dim, &overlap) == 1) { + Vec3f leftPos; + Vec3f rightPos; + + Math_Vec3s_ToVec3f(&leftPos, &leftElem->dim.worldSphere.center); + Math_Vec3s_ToVec3f(&rightPos, &right->dim.pos); + CollisionCheck_SetOCvsOC(&left->base, &leftElem->info, &leftPos, &right->base, &right->info, + &rightPos, overlap); + } + } + } + } +} + +/** + * OC overlap check for a Cylinder and JntSph + */ +void CollisionCheck_OC_CylVsJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* l, + Collider* r) { + CollisionCheck_OC_JntSphVsCyl(globalCtx, colChkCtx, r, l); +} + +/** + * OC overlap check for two Cylinders + */ +void CollisionCheck_OC_CylVsCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* l, Collider* r) { + ColliderCylinder* left = (ColliderCylinder*)l; + ColliderCylinder* right = (ColliderCylinder*)r; + f32 deadSpace; + + if ((left->base.ocFlags1 & OC1_ON) && (right->base.ocFlags1 & OC1_ON)) { + if ((left->info.ocElemFlags & OCELEM_ON) && (right->info.ocElemFlags & OCELEM_ON)) { + if (Math3D_CylOutsideCyl(&left->dim, &right->dim, &deadSpace) == 1) { + Vec3f leftPos; + Vec3f rightPos; + + Math_Vec3s_ToVec3f(&leftPos, &left->dim.pos); + Math_Vec3s_ToVec3f(&rightPos, &right->dim.pos); + CollisionCheck_SetOCvsOC(&left->base, &left->info, &leftPos, &right->base, &right->info, &rightPos, + deadSpace); + } + } + } +} + +/** + * Skip any OC colliders that are off + */ +s32 CollisionCheck_SkipOC(Collider* collider) { + if (!(collider->ocFlags1 & OC1_ON)) { + return 1; + } + return 0; +} + +/** + * Checks for OC compatibility. There are three conditions: + * First, each collider must have an OC flag corresponding to the other's OC type. + * Second, OC2_UNK1 and OC2_UNK2 can't collide with each other (has something to do with horses?) + * Third, the colliders can't collide if they belong to the same actor + */ +s32 CollisionCheck_Incompatible(Collider* left, Collider* right) { + if (!(left->ocFlags1 & right->ocFlags2 & OC1_TYPE_ALL) || !(left->ocFlags2 & right->ocFlags1 & OC1_TYPE_ALL) || + ((left->ocFlags2 & OC2_UNK1) && (right->ocFlags2 & OC2_UNK2)) || + ((right->ocFlags2 & OC2_UNK1) && (left->ocFlags2 & OC2_UNK2))) { + return 1; + } + if (left->actor == right->actor) { + return 1; + } + return 0; +} + +static ColChkVsFunc sOCVsFuncs[4][4] = { + { CollisionCheck_OC_JntSphVsJntSph, CollisionCheck_OC_JntSphVsCyl, NULL, NULL }, + { CollisionCheck_OC_CylVsJntSph, CollisionCheck_OC_CylVsCyl, NULL, NULL }, + { NULL, NULL, NULL, NULL }, + { NULL, NULL, NULL, NULL }, +}; + +/** + * Iterates through all OC colliders and collides them with all subsequent OC colliders on the list. During an OC + * collision, colliders with overlapping elements move away from each other so that their elements no longer overlap. + * The relative amount each collider is pushed is determined by the collider's mass. Only JntSph and Cylinder colliders + * can collide, and each collider must have the OC flag corresponding to the other's OC type. Additionally, OC2_UNK1 + * cannot collide with OC2_UNK2, nor can two colliders that share an actor. + */ +void CollisionCheck_OC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + Collider** left; + Collider** right; + ColChkVsFunc vsFunc; + + for (left = colChkCtx->colOC; left < colChkCtx->colOC + colChkCtx->colOCCount; left++) { + if (*left == NULL || CollisionCheck_SkipOC(*left) == 1) { + continue; + } + for (right = left + 1; right < colChkCtx->colOC + colChkCtx->colOCCount; right++) { + if (*right == NULL || CollisionCheck_SkipOC(*right) == 1 || + CollisionCheck_Incompatible(*left, *right) == 1) { + continue; + } + vsFunc = sOCVsFuncs[(*left)->shape][(*right)->shape]; + if (vsFunc == NULL) { + // "Not compatible" + osSyncPrintf("CollisionCheck_OC():未対応 %d, %d\n", (*left)->shape, (*right)->shape); + continue; + } + vsFunc(globalCtx, colChkCtx, *left, *right); + } + } +} + +/** + * Initializes CollisionCheckInfo to default values + */ +void CollisionCheck_InitInfo(CollisionCheckInfo* info) { + static CollisionCheckInfo init = { + NULL, { 0.0f, 0.0f, 0.0f }, 10, 10, 0, 50, 8, 0, 0, 0, 0, + }; + + *info = init; +} + +/** + * Resets ColisionCheckInfo fields other than DamageTable, mass, and dim. + */ +void CollisionCheck_ResetDamage(CollisionCheckInfo* info) { + info->damage = 0; + info->damageEffect = 0; + info->atHitEffect = 0; + info->acHitEffect = 0; + info->displacement.x = info->displacement.y = info->displacement.z = 0.0f; +} + +/** + * Sets up CollisionCheckInfo using the values in init. Does not set a damage table or the unused unk_14. + * Unused, as all actors that don't set a damage table set their CollisionCheckInfo manually + */ +void CollisionCheck_SetInfoNoDamageTable(CollisionCheckInfo* info, CollisionCheckInfoInit* init) { + info->health = init->health; + info->cylRadius = init->cylRadius; + info->cylHeight = init->cylHeight; + info->mass = init->mass; +} + +/** + * Sets up CollisionCheckInfo using the values in init. Does not set the unused unk_14 + */ +void CollisionCheck_SetInfo(CollisionCheckInfo* info, DamageTable* damageTable, CollisionCheckInfoInit* init) { + info->health = init->health; + info->damageTable = damageTable; + info->cylRadius = init->cylRadius; + info->cylHeight = init->cylHeight; + info->mass = init->mass; +} + +/** + * Sets up CollisionCheckInfo using the values in init. Sets the unused unk_14 + */ +void CollisionCheck_SetInfo2(CollisionCheckInfo* info, DamageTable* damageTable, CollisionCheckInfoInit2* init) { + info->health = init->health; + info->damageTable = damageTable; + info->cylRadius = init->cylRadius; + info->cylHeight = init->cylHeight; + info->cylYShift = init->cylYShift; + info->mass = init->mass; +} + +/** + * Sets up CollisionCheckInfo using the values in Init and a preset damage table. Sets the unused unk_14. + * Unused, as all actors that use a preset damage table set their CollisionCheckInfo manually. + */ +void CollisionCheck_SetInfoGetDamageTable(CollisionCheckInfo* info, s32 index, CollisionCheckInfoInit2* init) { + CollisionCheck_SetInfo2(info, DamageTable_Get(index), init); +} + +/** + * Apply AC damage effect + */ +void CollisionCheck_ApplyDamage(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, + ColliderInfo* info) { + DamageTable* tbl; + f32 damage; + + if (collider->actor == NULL || !(collider->acFlags & AC_HIT)) { + return; + } + if (!(info->bumperFlags & BUMP_HIT) || info->bumperFlags & BUMP_NO_DAMAGE) { + return; + } + + ASSERT(info->acHitInfo != NULL, "pclobj_elem->ac_hit_elem != NULL", "../z_collision_check.c", 6493); + tbl = collider->actor->colChkInfo.damageTable; + if (tbl == NULL) { + damage = (f32)info->acHitInfo->toucher.damage - info->bumper.defense; + if (damage < 0) { + damage = 0; + } + } else { + s32 i; + u32 flags = info->acHitInfo->toucher.dmgFlags; + + for (i = 0; i < 0x20; i++, flags >>= 1) { + if (flags == 1) { + break; + } + } + + damage = tbl->table[i] & 0xF; + collider->actor->colChkInfo.damageEffect = tbl->table[i] >> 4 & 0xF; + } + if (!(collider->acFlags & AC_HARD)) { + collider->actor->colChkInfo.damage += damage; + } +} + +/** + * Apply ColliderJntSph AC damage effect + */ +void CollisionCheck_ApplyDamageJntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderJntSph* jntSph = (ColliderJntSph*)collider; + s32 i; + + if (jntSph->count > 0 && jntSph->elements != NULL) { + for (i = 0; i < jntSph->count; i++) { + CollisionCheck_ApplyDamage(globalCtx, colChkCtx, &jntSph->base, &jntSph->elements[i].info); + } + } +} + +/** + * Apply ColliderCylinder AC damage effect + */ +void CollisionCheck_ApplyDamageCyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderCylinder* cylinder = (ColliderCylinder*)collider; + CollisionCheck_ApplyDamage(globalCtx, colChkCtx, &cylinder->base, &cylinder->info); +} + +/** + * Apply ColliderTris AC damage effect + */ +void CollisionCheck_ApplyDamageTris(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderTris* tris = (ColliderTris*)collider; + s32 i; + + for (i = 0; i < tris->count; i++) { + CollisionCheck_ApplyDamage(globalCtx, colChkCtx, collider, &tris->elements[i].info); + } +} + +/** + * Apply ColliderQuad AC damage effect + */ +void CollisionCheck_ApplyDamageQuad(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider) { + ColliderQuad* quad = (ColliderQuad*)collider; + CollisionCheck_ApplyDamage(globalCtx, colChkCtx, &quad->base, &quad->info); +} + +static ColChkApplyFunc sApplyDamageFuncs[4] = { + CollisionCheck_ApplyDamageJntSph, + CollisionCheck_ApplyDamageCyl, + CollisionCheck_ApplyDamageTris, + CollisionCheck_ApplyDamageQuad, +}; + +/** + * For all AC colliders, sets any damage effects from collisions with AT colliders to their corresponding actor's + * CollisionCheckInfo. + */ +void CollisionCheck_Damage(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx) { + s32 i; + + for (i = 0; i < colChkCtx->colACCount; i++) { + Collider* collider = colChkCtx->colAC[i]; + + if (collider == NULL) { + continue; + } + if (collider->acFlags & AC_NO_DAMAGE) { + continue; + } + sApplyDamageFuncs[collider->shape](globalCtx, colChkCtx, collider); + } +} + +/** + * Checks if the line ab intersects any of the ColliderJntSph's elements + */ +s32 CollisionCheck_LineOC_JntSph(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, + Vec3f* a, Vec3f* b) { + static Linef D_8015E610; + ColliderJntSph* jntSph = (ColliderJntSph*)collider; + s32 i; + + for (i = 0; i < jntSph->count; i++) { + ColliderJntSphElement* element = &jntSph->elements[i]; + + if (!(element->info.ocElemFlags & OCELEM_ON)) { + continue; + } + D_8015E610.a = *a; + D_8015E610.b = *b; + if (Math3D_LineVsSph(&element->dim.worldSphere, &D_8015E610) == 1) { + return true; + } + } + return false; +} + +/** + * Checks if the line segment ab intersects the ColliderCylinder + */ +s32 CollisionCheck_LineOC_Cyl(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Collider* collider, Vec3f* a, + Vec3f* b) { + static Vec3f D_8015E628; + static Vec3f D_8015E638; + ColliderCylinder* cylinder = (ColliderCylinder*)collider; + + if (!(cylinder->info.ocElemFlags & OCELEM_ON)) { + return false; + } + if (Math3D_CylVsLineSeg(&cylinder->dim, a, b, &D_8015E628, &D_8015E638) != 0) { + return true; + } + return false; +} + +static ColChkLineFunc sOCLineCheckFuncs[4] = { + CollisionCheck_LineOC_JntSph, + CollisionCheck_LineOC_Cyl, + NULL, + NULL, +}; + +/** + * Checks if the line segment ab intersects any OC colliders, excluding those attached to actors + * on the exclusion list. Returns true if there are any intersections and false otherwise. + */ +s32 CollisionCheck_LineOC(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b, + Actor** exclusions, s32 numExclusions) { + ColChkLineFunc lineCheck; + Collider** col; + s32 i; + s32 exclude; + s32 result = 0; + + for (col = colChkCtx->colOC; col < colChkCtx->colOC + colChkCtx->colOCCount; col++) { + if (CollisionCheck_SkipOC(*col) == 1) { + continue; + } + exclude = 0; + for (i = 0; i < numExclusions; i++) { + if ((*col)->actor == exclusions[i]) { + exclude = 1; + break; + } + } + if (exclude == 1) { + continue; + } + lineCheck = sOCLineCheckFuncs[(*col)->shape]; + if (lineCheck == NULL) { + // "type %d not supported" + osSyncPrintf("CollisionCheck_generalLineOcCheck():未対応 %dタイプ\n", (*col)->shape); + } else { + result = lineCheck(globalCtx, colChkCtx, (*col), a, b); + if (result) { + break; + } + } + } + return result; +} + +/** + * Checks if the line segment ab intersects any OC colliders. Returns true if there are any intersections and false + * otherwise. Unused. + */ +s32 CollisionCheck_LineOCCheckAll(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b) { + return CollisionCheck_LineOC(globalCtx, colChkCtx, a, b, NULL, 0); +} + +/** + * Checks if the line segment ab intersects any OC colliders, excluding those attached to actors on the exclusion list. + * Returns true if there are any intersections and false otherwise. + */ +s32 CollisionCheck_LineOCCheck(GlobalContext* globalCtx, CollisionCheckContext* colChkCtx, Vec3f* a, Vec3f* b, + Actor** exclusions, s32 numExclusions) { + return CollisionCheck_LineOC(globalCtx, colChkCtx, a, b, exclusions, numExclusions); +} + +/** + * Moves the ColliderCylinder's position to the actor's position + */ +void Collider_UpdateCylinder(Actor* actor, ColliderCylinder* collider) { + collider->dim.pos.x = actor->world.pos.x; + collider->dim.pos.y = actor->world.pos.y; + collider->dim.pos.z = actor->world.pos.z; +} + +/** + * Sets the ColliderCylinder's position + */ +void Collider_SetCylinderPosition(ColliderCylinder* collider, Vec3s* pos) { + collider->dim.pos.x = pos->x; + collider->dim.pos.y = pos->y; + collider->dim.pos.z = pos->z; +} + +/** + * Sets the ColliderQuad's vertices + */ +void Collider_SetQuadVertices(ColliderQuad* collider, Vec3f* a, Vec3f* b, Vec3f* c, Vec3f* d) { + Math_Vec3f_Copy(&collider->dim.quad[2], c); + Math_Vec3f_Copy(&collider->dim.quad[3], d); + Math_Vec3f_Copy(&collider->dim.quad[0], a); + Math_Vec3f_Copy(&collider->dim.quad[1], b); + Collider_SetQuadMidpoints(&collider->dim); +} + +/** + * Sets the specified ColliderTrisElement's vertices + */ +void Collider_SetTrisVertices(ColliderTris* collider, s32 index, Vec3f* a, Vec3f* b, Vec3f* c) { + ColliderTrisElement* element = &collider->elements[index]; + f32 nx; + f32 ny; + f32 nz; + f32 originDist; + + Math_Vec3f_Copy(&element->dim.vtx[0], a); + Math_Vec3f_Copy(&element->dim.vtx[1], b); + Math_Vec3f_Copy(&element->dim.vtx[2], c); + Math3D_DefPlane(a, b, c, &nx, &ny, &nz, &originDist); + element->dim.plane.normal.x = nx; + element->dim.plane.normal.y = ny; + element->dim.plane.normal.z = nz; + element->dim.plane.originDist = originDist; +} + +/** + * Sets the specified ColliderTrisElement's dim using the values in src + */ +void Collider_SetTrisDim(GlobalContext* globalCtx, ColliderTris* collider, s32 index, ColliderTrisElementDimInit* src) { + ColliderTrisElement* element = &collider->elements[index]; + + Collider_SetTrisElementDim(globalCtx, &element->dim, src); +} + +// Due to an unknown reason, bss ordering changed between the 2 static Vec3f variables in the function below. +// In order to reproduce this behavior, we need a specific number of bss variables in the file before that point. +// For this, we introduce a certain amount of dummy variables throughout the file, which we fit inside padding added +// by the compiler between structs like TriNorm and/or Vec3f, so they don't take space in bss. +static s8 sBssDummy11; +static s8 sBssDummy12; +static s8 sBssDummy13; +static s8 sBssDummy14; + +/** + * Updates the world spheres for all of the collider's JntSph elements attached to the specified limb + */ +void Collider_UpdateSpheres(s32 limb, ColliderJntSph* collider) { + static Vec3f D_8015E648; + static Vec3f D_8015CF00; // bss ordering changes here + s32 i; + + for (i = 0; i < collider->count; i++) { + if (limb == collider->elements[i].dim.limb) { + D_8015E648.x = collider->elements[i].dim.modelSphere.center.x; + D_8015E648.y = collider->elements[i].dim.modelSphere.center.y; + D_8015E648.z = collider->elements[i].dim.modelSphere.center.z; + Matrix_MultVec3f(&D_8015E648, &D_8015CF00); + collider->elements[i].dim.worldSphere.center.x = D_8015CF00.x; + collider->elements[i].dim.worldSphere.center.y = D_8015CF00.y; + collider->elements[i].dim.worldSphere.center.z = D_8015CF00.z; + collider->elements[i].dim.worldSphere.radius = + collider->elements[i].dim.modelSphere.radius * collider->elements[i].dim.scale; + } + } +} + +/** + * Spawns red blood droplets. + * No actor has a collision type that spawns red blood. + */ +void CollisionCheck_SpawnRedBlood(GlobalContext* globalCtx, Vec3f* v) { + static EffectSparkInit D_8015CF10; + s32 effectIndex; + + D_8015CF10.position.x = v->x; + D_8015CF10.position.y = v->y; + D_8015CF10.position.z = v->z; + D_8015CF10.uDiv = 5; + D_8015CF10.vDiv = 5; + D_8015CF10.colorStart[0].r = 128; + D_8015CF10.colorStart[0].g = 0; + D_8015CF10.colorStart[0].b = 64; + D_8015CF10.colorStart[0].a = 255; + D_8015CF10.colorStart[1].r = 128; + D_8015CF10.colorStart[1].g = 0; + D_8015CF10.colorStart[1].b = 64; + D_8015CF10.colorStart[1].a = 255; + D_8015CF10.colorStart[2].r = 255; + D_8015CF10.colorStart[2].g = 128; + D_8015CF10.colorStart[2].b = 0; + D_8015CF10.colorStart[2].a = 255; + D_8015CF10.colorStart[3].r = 255; + D_8015CF10.colorStart[3].g = 128; + D_8015CF10.colorStart[3].b = 0; + D_8015CF10.colorStart[3].a = 255; + D_8015CF10.colorEnd[0].r = 64; + D_8015CF10.colorEnd[0].g = 0; + D_8015CF10.colorEnd[0].b = 32; + D_8015CF10.colorEnd[0].a = 0; + D_8015CF10.colorEnd[1].r = 64; + D_8015CF10.colorEnd[1].g = 0; + D_8015CF10.colorEnd[1].b = 32; + D_8015CF10.colorEnd[1].a = 0; + D_8015CF10.colorEnd[2].r = 128; + D_8015CF10.colorEnd[2].g = 0; + D_8015CF10.colorEnd[2].b = 64; + D_8015CF10.colorEnd[2].a = 0; + D_8015CF10.colorEnd[3].r = 128; + D_8015CF10.colorEnd[3].g = 0; + D_8015CF10.colorEnd[3].b = 64; + D_8015CF10.colorEnd[3].a = 0; + D_8015CF10.timer = 0; + D_8015CF10.duration = 16; + D_8015CF10.speed = 8.0f; + D_8015CF10.gravity = -1.0f; + + Effect_Add(globalCtx, &effectIndex, EFFECT_SPARK, 0, 1, &D_8015CF10); +} + +/** + * Spawns water droplets. + * No actor has a collision type that spawns water droplets. + */ +void CollisionCheck_SpawnWaterDroplets(GlobalContext* globalCtx, Vec3f* v) { + static EffectSparkInit D_8015D3D8; + s32 effectIndex; + + D_8015D3D8.position.x = v->x; + D_8015D3D8.position.y = v->y; + D_8015D3D8.position.z = v->z; + D_8015D3D8.uDiv = 5; + D_8015D3D8.vDiv = 5; + D_8015D3D8.colorStart[0].r = 255; + D_8015D3D8.colorStart[0].g = 255; + D_8015D3D8.colorStart[0].b = 255; + D_8015D3D8.colorStart[0].a = 255; + D_8015D3D8.colorStart[1].r = 100; + D_8015D3D8.colorStart[1].g = 100; + D_8015D3D8.colorStart[1].b = 100; + D_8015D3D8.colorStart[1].a = 100; + D_8015D3D8.colorStart[2].r = 100; + D_8015D3D8.colorStart[2].g = 100; + D_8015D3D8.colorStart[2].b = 100; + D_8015D3D8.colorStart[2].a = 100; + D_8015D3D8.colorStart[3].r = 100; + D_8015D3D8.colorStart[3].g = 100; + D_8015D3D8.colorStart[3].b = 100; + D_8015D3D8.colorStart[3].a = 100; + D_8015D3D8.colorEnd[0].r = 50; + D_8015D3D8.colorEnd[0].g = 50; + D_8015D3D8.colorEnd[0].b = 50; + D_8015D3D8.colorEnd[0].a = 50; + D_8015D3D8.colorEnd[1].r = 50; + D_8015D3D8.colorEnd[1].g = 50; + D_8015D3D8.colorEnd[1].b = 50; + D_8015D3D8.colorEnd[1].a = 50; + D_8015D3D8.colorEnd[2].r = 50; + D_8015D3D8.colorEnd[2].g = 50; + D_8015D3D8.colorEnd[2].b = 50; + D_8015D3D8.colorEnd[2].a = 50; + D_8015D3D8.colorEnd[3].r = 0; + D_8015D3D8.colorEnd[3].g = 0; + D_8015D3D8.colorEnd[3].b = 0; + D_8015D3D8.colorEnd[3].a = 0; + D_8015D3D8.timer = 0; + D_8015D3D8.duration = 16; + D_8015D3D8.speed = 8.0f; + D_8015D3D8.gravity = -1.0f; + + Effect_Add(globalCtx, &effectIndex, EFFECT_SPARK, 0, 1, &D_8015D3D8); +} + +/** + * Spawns streaks of light from hits against solid objects + */ +void CollisionCheck_SpawnShieldParticles(GlobalContext* globalCtx, Vec3f* v) { + static EffectShieldParticleInit initMetal = { + 16, + { 0, 0, 0 }, + { 0, 200, 255, 255 }, + { 255, 255, 255, 255 }, + { 255, 255, 128, 255 }, + { 255, 255, 0, 255 }, + { 255, 64, 0, 200 }, + { 255, 0, 0, 255 }, + 2.1f, + 35.0f, + 30.0f, + 8, + { 0, 0, 0, { 0, 128, 255 }, false, 300 }, + true, + }; + s32 effectIndex; + + initMetal.position.x = v->x; + initMetal.position.y = v->y; + initMetal.position.z = v->z; + initMetal.lightPoint.x = initMetal.position.x; + initMetal.lightPoint.y = initMetal.position.y; + initMetal.lightPoint.z = initMetal.position.z; + + Effect_Add(globalCtx, &effectIndex, EFFECT_SHIELD_PARTICLE, 0, 1, &initMetal); +} + +/** + * Spawns streaks of light and makes a metallic sound + */ +void CollisionCheck_SpawnShieldParticlesMetal(GlobalContext* globalCtx, Vec3f* v) { + CollisionCheck_SpawnShieldParticles(globalCtx, v); + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_REFLECT_SW, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +/** + * Spawns streaks of light and makes a metallic sound at the specified position + */ +void CollisionCheck_SpawnShieldParticlesMetalSound(GlobalContext* globalCtx, Vec3f* v, Vec3f* pos) { + CollisionCheck_SpawnShieldParticles(globalCtx, v); + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_REFLECT_SW, pos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +/** + * Spawns streaks of light and makes a metallic sound + */ +void CollisionCheck_SpawnShieldParticlesMetal2(GlobalContext* globalCtx, Vec3f* v) { + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, v); +} + +/** + * Spawns streaks of light and makes a wooden sound + */ +void CollisionCheck_SpawnShieldParticlesWood(GlobalContext* globalCtx, Vec3f* v, Vec3f* actorPos) { + static EffectShieldParticleInit initWood = { + 16, + { 0, 0, 0 }, + { 0, 200, 255, 255 }, + { 255, 255, 255, 255 }, + { 255, 255, 128, 255 }, + { 255, 255, 0, 255 }, + { 255, 64, 0, 200 }, + { 255, 0, 0, 255 }, + 2.1f, + 35.0f, + 30.0f, + 8, + { 0, 0, 0, { 0, 128, 255 }, false, 300 }, + false, + }; + s32 effectIndex; + + initWood.position.x = v->x; + initWood.position.y = v->y; + initWood.position.z = v->z; + initWood.lightPoint.x = initWood.position.x; + initWood.lightPoint.y = initWood.position.y; + initWood.lightPoint.z = initWood.position.z; + + Effect_Add(globalCtx, &effectIndex, EFFECT_SHIELD_PARTICLE, 0, 1, &initWood); + Audio_PlaySoundGeneral(NA_SE_IT_REFLECTION_WOOD, actorPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +/** + * Determines if the line segment connecting itemPos and itemProjPos intersects the side of a cylinder with the given + * radius, height, and offset at actorPos. Returns 3 if either endpoint is inside the cylinder, otherwise returns the + * number of points of intersection with the side of the cylinder. The locations of those points are put in out1 and + * out2, with out1 being closer to itemPos. Line segments that pass through both bases of the cylinder are not detected. + */ +s32 CollisionCheck_CylSideVsLineSeg(f32 radius, f32 height, f32 offset, Vec3f* actorPos, Vec3f* itemPos, + Vec3f* itemProjPos, Vec3f* out1, Vec3f* out2) { + Vec3f actorToItem; + Vec3f actorToItemProj; + Vec3f itemStep; + f32 frac1; + f32 frac2; + u32 intersect2; + u32 intersect1; + u32 test1; + u32 test2; + f32 radSqDiff; + f32 actorDotItemXZ; + f32 zero = 0.0f; + f32 closeDist; + s32 pad1; + s32 pad2; + + actorToItem.x = itemPos->x - actorPos->x; + actorToItem.y = itemPos->y - actorPos->y - offset; + actorToItem.z = itemPos->z - actorPos->z; + + actorToItemProj.x = itemProjPos->x - actorPos->x; + actorToItemProj.y = itemProjPos->y - actorPos->y - offset; + actorToItemProj.z = itemProjPos->z - actorPos->z; + + itemStep.x = actorToItemProj.x - actorToItem.x; + itemStep.y = actorToItemProj.y - actorToItem.y; + itemStep.z = actorToItemProj.z - actorToItem.z; + + if ((actorToItem.y > 0.0f) && (actorToItem.y < height) && (sqrtf(SQXZ(actorToItem)) < radius)) { + return 3; + } + + if ((actorToItemProj.y > 0.0f) && (actorToItemProj.y < height) && (sqrtf(SQXZ(actorToItemProj)) < radius)) { + return 3; + } + radSqDiff = SQXZ(actorToItem) - SQ(radius); + if (!IS_ZERO(SQXZ(itemStep))) { + actorDotItemXZ = DOTXZ(2.0f * itemStep, actorToItem); + if (SQ(actorDotItemXZ) < (4.0f * SQXZ(itemStep) * radSqDiff)) { + return 0; + } + if (SQ(actorDotItemXZ) - (4.0f * SQXZ(itemStep) * radSqDiff) > zero) { + intersect1 = intersect2 = 1; + } else { + intersect1 = 1; + intersect2 = 0; + } + closeDist = sqrtf(SQ(actorDotItemXZ) - (4.0f * SQXZ(itemStep) * radSqDiff)); + if (intersect1 == 1) { + frac1 = (closeDist - actorDotItemXZ) / (2.0f * SQXZ(itemStep)); + } + if (intersect2 == 1) { + frac2 = (-actorDotItemXZ - closeDist) / (2.0f * SQXZ(itemStep)); + } + } else if (!IS_ZERO(DOTXZ(2.0f * itemStep, actorToItem))) { + intersect1 = 1; + intersect2 = 0; + frac1 = -radSqDiff / DOTXZ(2.0f * itemStep, actorToItem); + } else { + if (radSqDiff <= 0.0f) { + test1 = (0.0f < actorToItem.y) && (actorToItem.y < height); + test2 = (0.0f < actorToItemProj.y) && (actorToItemProj.y < height); + + if (test1 && test2) { + *out1 = actorToItem; + *out2 = actorToItemProj; + return 2; + } + if (test1) { + *out1 = actorToItem; + return 1; + } + if (test2) { + *out1 = actorToItemProj; + return 1; + } + } + return 0; + } + + if (intersect2 == 0) { + if (frac1 < 0.0f || 1.0f < frac1) { + return 0; + } + } else { + test1 = (frac1 < 0.0f || 1.0f < frac1); + test2 = (frac2 < 0.0f || 1.0f < frac2); + + if (test1 && test2) { + return 0; + } + if (test1) { + intersect1 = 0; + } + if (test2) { + intersect2 = 0; + } + } + + if ((intersect1 == 1) && + ((frac1 * itemStep.y + actorToItem.y < 0.0f) || (height < frac1 * itemStep.y + actorToItem.y))) { + intersect1 = 0; + } + if ((intersect2 == 1) && + ((frac2 * itemStep.y + actorToItem.y < 0.0f) || (height < frac2 * itemStep.y + actorToItem.y))) { + intersect2 = 0; + } + if (intersect1 == 0 && intersect2 == 0) { + return 0; + } else if ((intersect1 == 1) && (intersect2 == 1)) { + out1->x = frac1 * itemStep.x + actorToItem.x + actorPos->x; + out1->y = frac1 * itemStep.y + actorToItem.y + actorPos->y; + out1->z = frac1 * itemStep.z + actorToItem.z + actorPos->z; + out2->x = frac2 * itemStep.x + actorToItem.x + actorPos->x; + out2->y = frac2 * itemStep.y + actorToItem.y + actorPos->y; + out2->z = frac2 * itemStep.z + actorToItem.z + actorPos->z; + return 2; + } else if (intersect1 == 1) { + out1->x = frac1 * itemStep.x + actorToItem.x + actorPos->x; + out1->y = frac1 * itemStep.y + actorToItem.y + actorPos->y; + out1->z = frac1 * itemStep.z + actorToItem.z + actorPos->z; + return 1; + } else if (intersect2 == 1) { + out1->x = frac2 * itemStep.x + actorToItem.x + actorPos->x; + out1->y = frac2 * itemStep.y + actorToItem.y + actorPos->y; + out1->z = frac2 * itemStep.z + actorToItem.z + actorPos->z; + return 1; + } + return 1; +} + +/** + * Gets damage from a sword strike using generic values, and returns 0 if the attack is + * not sword-type. Used by bosses to require that a sword attack deal the killing blow. + */ +u8 CollisionCheck_GetSwordDamage(s32 dmgFlags) { + u8 damage = 0; + + if (dmgFlags & 0x00400100) { + damage = 1; + } else if (dmgFlags & 0x03000242) { + damage = 2; + } else if (dmgFlags & 0x48800400) { + damage = 4; + } else if (dmgFlags & 0x04000000) { + damage = 8; + } + + KREG(7) = damage; + return damage; +} diff --git a/soh/src/code/z_common_data.c b/soh/src/code/z_common_data.c new file mode 100644 index 000000000..e6682b18e --- /dev/null +++ b/soh/src/code/z_common_data.c @@ -0,0 +1,23 @@ +#include "global.h" +#include + +SaveContext gSaveContext; +u32 D_8015FA88; +u32 D_8015FA8C; + +void SaveContext_Init(void) { + memset(&gSaveContext, 0, sizeof(gSaveContext)); + D_8015FA88 = 0; + D_8015FA8C = 0; + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + gSaveContext.forcedSeqId = NA_BGM_GENERAL_SFX; + gSaveContext.nextCutsceneIndex = 0xFFEF; + gSaveContext.cutsceneTrigger = 0; + gSaveContext.chamberCutsceneNum = 0; + gSaveContext.nextDayTime = 0xFFFF; + gSaveContext.skyboxTime = 0; + gSaveContext.dogIsLost = true; + gSaveContext.nextTransition = 0xFF; + gSaveContext.unk_13EE = 50; +} diff --git a/soh/src/code/z_construct.c b/soh/src/code/z_construct.c new file mode 100644 index 000000000..2fd6d43ad --- /dev/null +++ b/soh/src/code/z_construct.c @@ -0,0 +1,608 @@ +#include "global.h" +#include + +void func_80110990(GlobalContext* globalCtx) { + Map_Destroy(globalCtx); +} + +void func_801109B0(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + u32 parameterSize; + u16 doActionOffset; + u8 temp; + + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + gSaveContext.unk_13E8 = gSaveContext.unk_13EA = 0; + + View_Init(&interfaceCtx->view, globalCtx->state.gfxCtx); + + interfaceCtx->unk_1FA = interfaceCtx->unk_261 = interfaceCtx->unk_1FC = 0; + interfaceCtx->unk_1EC = interfaceCtx->unk_1EE = interfaceCtx->unk_1F0 = 0; + interfaceCtx->unk_22E = 0; + interfaceCtx->unk_230 = 16; + interfaceCtx->unk_1F4 = 0.0f; + interfaceCtx->unk_228 = XREG(95); + interfaceCtx->minimapAlpha = 0; + interfaceCtx->unk_260 = 0; + interfaceCtx->unk_244 = interfaceCtx->aAlpha = interfaceCtx->bAlpha = interfaceCtx->cLeftAlpha = + interfaceCtx->cDownAlpha = interfaceCtx->cRightAlpha = interfaceCtx->healthAlpha = interfaceCtx->startAlpha = + interfaceCtx->magicAlpha = 0; + + parameterSize = (uintptr_t)_parameter_staticSegmentRomEnd - (uintptr_t)_parameter_staticSegmentRomStart; + + // "Permanent PARAMETER Segment = %x" + osSyncPrintf("常駐PARAMETERセグメント=%x\n", parameterSize); + + interfaceCtx->parameterSegment = GameState_Alloc(&globalCtx->state, parameterSize, "../z_construct.c", 159); + + osSyncPrintf("parameter->parameterSegment=%x\n", interfaceCtx->parameterSegment); + + ASSERT(interfaceCtx->parameterSegment != NULL, "parameter->parameterSegment != NULL", "../z_construct.c", 161); + DmaMgr_SendRequest1(interfaceCtx->parameterSegment, (uintptr_t)_parameter_staticSegmentRomStart, parameterSize, + "../z_construct.c", 162); + + interfaceCtx->doActionSegment = GameState_Alloc(&globalCtx->state, 0x480, "../z_construct.c", 166); + + osSyncPrintf("DOアクション テクスチャ初期=%x\n", 0x480); // "DO Action Texture Initialization" + osSyncPrintf("parameter->do_actionSegment=%x\n", interfaceCtx->doActionSegment); + + ASSERT(interfaceCtx->doActionSegment != NULL, "parameter->do_actionSegment != NULL", "../z_construct.c", 169); + + if (gSaveContext.language == LANGUAGE_ENG) { + doActionOffset = 0; + } else if (gSaveContext.language == LANGUAGE_GER) { + doActionOffset = 0x2B80; + } else { + doActionOffset = 0x5700; + } + + memcpy(interfaceCtx->doActionSegment, ResourceMgr_LoadTexByName(gAttackDoActionENGTex), 0x300); + //DmaMgr_SendRequest1(interfaceCtx->doActionSegment, (uintptr_t)_do_action_staticSegmentRomStart + doActionOffset, 0x300, + //"../z_construct.c", 174); + + if (gSaveContext.language == LANGUAGE_ENG) { + doActionOffset = 0x480; + } else if (gSaveContext.language == LANGUAGE_GER) { + doActionOffset = 0x3000; + } else { + doActionOffset = 0x5B80; + } + + memcpy(interfaceCtx->doActionSegment + 0x300, ResourceMgr_LoadTexByName(gReturnDoActionENGTex), 0x180); + //DmaMgr_SendRequest1(interfaceCtx->doActionSegment + 0x300, (uintptr_t)_do_action_staticSegmentRomStart + doActionOffset, + //0x180, "../z_construct.c", 178); + + interfaceCtx->iconItemSegment = GameState_Alloc(&globalCtx->state, 0x4000, "../z_construct.c", 190); + + // "Icon Item Texture Initialization = %x" + osSyncPrintf("アイコンアイテム テクスチャ初期=%x\n", 0x4000); + osSyncPrintf("parameter->icon_itemSegment=%x\n", interfaceCtx->iconItemSegment); + + ASSERT(interfaceCtx->iconItemSegment != NULL, "parameter->icon_itemSegment != NULL", "../z_construct.c", 193); + + osSyncPrintf("Register_Item[%x, %x, %x, %x]\n", gSaveContext.equips.buttonItems[0], + gSaveContext.equips.buttonItems[1], gSaveContext.equips.buttonItems[2], + gSaveContext.equips.buttonItems[3]); + + if (gSaveContext.equips.buttonItems[0] < 0xF0) { + DmaMgr_SendRequest1(interfaceCtx->iconItemSegment, + _icon_item_staticSegmentRomStart + gSaveContext.equips.buttonItems[0] * 0x1000, 0x1000, + "../z_construct.c", 198); + } else if (gSaveContext.equips.buttonItems[0] != 0xFF) { + DmaMgr_SendRequest1(interfaceCtx->iconItemSegment, + _icon_item_staticSegmentRomStart + gSaveContext.equips.buttonItems[0] * 0x1000, 0x1000, + "../z_construct.c", 203); + } + + if (gSaveContext.equips.buttonItems[1] < 0xF0) { + DmaMgr_SendRequest1(interfaceCtx->iconItemSegment + 0x1000, + _icon_item_staticSegmentRomStart + gSaveContext.equips.buttonItems[1] * 0x1000, 0x1000, + "../z_construct.c", 209); + } + + if (gSaveContext.equips.buttonItems[2] < 0xF0) { + DmaMgr_SendRequest1(interfaceCtx->iconItemSegment + 0x2000, + _icon_item_staticSegmentRomStart + gSaveContext.equips.buttonItems[2] * 0x1000, 0x1000, + "../z_construct.c", 214); + } + + if (gSaveContext.equips.buttonItems[3] < 0xF0) { + DmaMgr_SendRequest1(interfaceCtx->iconItemSegment + 0x3000, + _icon_item_staticSegmentRomStart + gSaveContext.equips.buttonItems[3] * 0x1000, 0x1000, + "../z_construct.c", 219); + } + + osSyncPrintf("EVENT=%d\n", ((void)0, gSaveContext.timer1State)); + + if ((gSaveContext.timer1State == 4) || (gSaveContext.timer1State == 8) || (gSaveContext.timer2State == 4) || + (gSaveContext.timer2State == 10)) { + osSyncPrintf("restart_flag=%d\n", ((void)0, gSaveContext.respawnFlag)); + + if ((gSaveContext.respawnFlag == -1) || (gSaveContext.respawnFlag == 1)) { + if (gSaveContext.timer1State == 4) { + gSaveContext.timer1State = 1; + gSaveContext.timerX[0] = 140; + gSaveContext.timerY[0] = 80; + } + } + + if ((gSaveContext.timer1State == 4) || (gSaveContext.timer1State == 8)) { + temp = 0; + } else { + temp = 1; + } + + gSaveContext.timerX[temp] = 26; + + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[temp] = 54; + } else { + gSaveContext.timerY[temp] = 46; + } + } + + if ((gSaveContext.timer1State >= 11) && (gSaveContext.timer1State < 16)) { + gSaveContext.timer1State = 0; + // "Timer Stop!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf("タイマー停止!!!!!!!!!!!!!!!!!!!!! = %d\n", gSaveContext.timer1State); + } + + osSyncPrintf("PARAMETER領域=%x\n", parameterSize + 0x5300); // "Parameter Area = %x" + + HealthMeter_Init(globalCtx); + Map_Init(globalCtx); + + interfaceCtx->unk_23C = interfaceCtx->unk_242 = 0; + + R_ITEM_BTN_X(0) = B_BUTTON_X; + R_B_BTN_COLOR(0) = 255; + R_B_BTN_COLOR(1) = 30; + R_B_BTN_COLOR(2) = 30; + R_ITEM_ICON_X(0) = B_BUTTON_X; + R_ITEM_AMMO_X(0) = B_BUTTON_X + 2; + R_A_BTN_X = A_BUTTON_X; + R_A_ICON_X = A_BUTTON_X; + R_A_BTN_COLOR(0) = 0; + R_A_BTN_COLOR(1) = 200; + R_A_BTN_COLOR(2) = 50; +} + +void Message_Init(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + s32 pad; + + Message_SetTables(); + + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + + msgCtx->msgMode = MSGMODE_NONE; + msgCtx->msgLength = 0; + msgCtx->textId = msgCtx->textboxEndType = msgCtx->choiceIndex = msgCtx->ocarinaAction = msgCtx->textUnskippable = 0; + msgCtx->textColorAlpha = 255; + + View_Init(&msgCtx->view, globalCtx->state.gfxCtx); + + msgCtx->textboxSegment = GameState_Alloc(&globalCtx->state, 0x2200, "../z_construct.c", 349); + + osSyncPrintf("message->fukidashiSegment=%x\n", msgCtx->textboxSegment); + + osSyncPrintf("吹き出しgame_alloc=%x\n", 0x2200); // "Textbox game_alloc=%x" + ASSERT(msgCtx->textboxSegment != NULL, "message->fukidashiSegment != NULL", "../z_construct.c", 352); + + Font_LoadOrderedFont(&globalCtx->msgCtx.font); + + YREG(31) = 0; +} + +void func_80111070(void) { + YREG(8) = 10; + YREG(14) = 0; + YREG(15) = 0; + R_TEXTBOX_TEXWIDTH = 0; + R_TEXTBOX_TEXHEIGHT = 0; + R_TEXTBOX_WIDTH = 50; + R_TEXTBOX_HEIGHT = 0; + YREG(24) = -60; + YREG(25) = 13; + YREG(26) = 15; + YREG(27) = 41; + YREG(28) = 15; + YREG(32) = 265; + YREG(33) = 55; + YREG(34) = 0; + YREG(35) = 20; + YREG(36) = 0; + YREG(37) = 0; + YREG(38) = 0; + YREG(40) = 2; + YREG(41) = 1; + YREG(42) = 2; + YREG(43) = 1; + YREG(44) = 0; + YREG(45) = 236; + YREG(46) = 36; + YREG(47) = 0; + YREG(48) = -45; + YREG(49) = -48; + YREG(50) = 16; + YREG(51) = 22; + YREG(52) = -55; + YREG(53) = -53; + YREG(54) = 43; + YREG(55) = 47; + YREG(56) = -33; + YREG(57) = -42; + YREG(58) = -33; + YREG(59) = -37; + YREG(60) = 14; + YREG(61) = -2; + YREG(62) = -2; + YREG(63) = -18; + YREG(64) = -18; + YREG(67) = 0; + YREG(68) = 0; + YREG(69) = 0; + YREG(70) = 0; + R_TEXTBOX_ICON_XPOS = -6; + R_TEXTBOX_ICON_YPOS = 10; + YREG(73) = -8; + YREG(74) = 8; + R_TEXTBOX_ICON_SIZE = 24; + YREG(76) = 32; + YREG(77) = 0; + R_MESSAGE_DEBUGGER_SELECT = 0; + R_MESSAGE_DEBUGGER_TEXTID = 48; + YREG(80) = 450; + YREG(81) = 0; + YREG(82) = -15; + YREG(83) = 500; + YREG(84) = 600; + YREG(85) = 0; + YREG(86) = -21; + YREG(87) = 510; + R_C_UP_ICON_X = C_UP_BUTTON_X - 7; + R_C_UP_ICON_Y = C_UP_BUTTON_Y + 4; + YREG(92) = 8; + YREG(93) = 6; + YREG(94) = 3; + YREG(95) = 1; + R_MAGIC_FILL_COLOR(0) = 0; + R_MAGIC_FILL_COLOR(1) = 200; + R_MAGIC_FILL_COLOR(2) = 0; + ZREG(9) = 140; + ZREG(10) = 200; + ZREG(11) = 0; + ZREG(12) = 200; + ZREG(13) = 0; + ZREG(14) = 110; + ZREG(15) = 56; + ZREG(16) = 1; + ZREG(17) = -50; + ZREG(18) = -200; + ZREG(19) = 0; + ZREG(20) = 0; + ZREG(21) = 50; + ZREG(22) = -50; + ZREG(23) = 20; + ZREG(24) = 20; + ZREG(25) = 4; + ZREG(26) = 20; + ZREG(27) = 10; + ZREG(28) = 20; + ZREG(29) = 4; + ZREG(30) = 20; + ZREG(31) = 10; + ZREG(32) = 0; + ZREG(33) = 0; + ZREG(34) = 0; + ZREG(36) = 0; + ZREG(37) = 0; + ZREG(38) = 0; + R_C_BTN_COLOR(0) = 255; + R_C_BTN_COLOR(1) = 160; + R_C_BTN_COLOR(2) = 0; + ZREG(46) = 1; + ZREG(47) = 1; + R_START_LABEL_DD(0) = 100; + R_START_LABEL_DD(1) = 89; + R_START_LABEL_DD(2) = 92; + R_START_LABEL_Y(0) = 20; + R_START_LABEL_Y(1) = 20; + R_START_LABEL_Y(2) = 20; + R_START_LABEL_X(0) = 120; + R_START_LABEL_X(1) = 119; + R_START_LABEL_X(2) = 119; + ZREG(61) = 1; + R_C_UP_BTN_X = C_UP_BUTTON_X; + R_C_UP_BTN_Y = C_UP_BUTTON_Y; + ZREG(64) = 20; + ZREG(65) = 21; + ZREG(66) = 122; + R_ITEM_BTN_X(1) = C_LEFT_BUTTON_X; + R_ITEM_BTN_X(2) = C_DOWN_BUTTON_X; + R_ITEM_BTN_X(3) = C_RIGHT_BUTTON_X; + R_ITEM_BTN_Y(0) = B_BUTTON_Y; + R_ITEM_BTN_Y(1) = C_LEFT_BUTTON_Y; + R_ITEM_BTN_Y(2) = C_DOWN_BUTTON_Y; + R_ITEM_BTN_Y(3) = C_RIGHT_BUTTON_Y; + R_ITEM_BTN_DD(0) = 565; + R_ITEM_BTN_DD(1) = 606; + R_ITEM_BTN_DD(2) = 606; + R_ITEM_BTN_DD(3) = 606; + R_ITEM_ICON_X(1) = C_LEFT_BUTTON_X; + R_ITEM_ICON_X(2) = C_DOWN_BUTTON_X; + R_ITEM_ICON_X(3) = C_RIGHT_BUTTON_X; + R_ITEM_ICON_Y(0) = B_BUTTON_Y; + R_ITEM_ICON_Y(1) = C_LEFT_BUTTON_Y; + R_ITEM_ICON_Y(2) = C_DOWN_BUTTON_Y; + R_ITEM_ICON_Y(3) = C_RIGHT_BUTTON_Y; + R_ITEM_ICON_DD(0) = 550; + R_ITEM_ICON_DD(1) = 680; + R_ITEM_ICON_DD(2) = 680; + R_ITEM_ICON_DD(3) = 680; + ZREG(94) = 1; + ZREG(95) = 0; + XREG(0) = 26; + XREG(1) = 22; + XREG(2) = -11; + XREG(3) = -4; + XREG(4) = 3; + XREG(5) = 0; + XREG(6) = 2; + XREG(7) = 30; + XREG(8) = 10; + XREG(9) = 0; + XREG(10) = -9550; + XREG(11) = 9950; + XREG(12) = 68; + XREG(13) = 36; + XREG(14) = 4; + XREG(15) = 1; + R_A_BTN_Y = A_BUTTON_Y; + XREG(18) = -380; + R_A_ICON_Y = A_BUTTON_Y; + XREG(21) = 48; + XREG(25) = 0; + XREG(26) = 0; + XREG(27) = 0; + XREG(28) = 16; + XREG(29) = 50; + XREG(30) = 15; + XREG(31) = 8; + XREG(32) = 4; + XREG(33) = 2; + XREG(34) = 100; + XREG(35) = 7; + XREG(36) = 20; + XREG(37) = 10; + XREG(38) = 2; + XREG(39) = 140; + XREG(40) = 20; + XREG(41) = 300; + XREG(42) = 100; + XREG(43) = 70; + XREG(44) = 50; + XREG(45) = 36; + XREG(46) = 16; + XREG(47) = 8; + R_MAGIC_BAR_SMALL_Y = 34; + R_MAGIC_BAR_X = 18; + R_MAGIC_BAR_LARGE_Y = 42; + R_MAGIC_FILL_X = 26; + XREG(52) = 0; + XREG(53) = 1; + R_TEXT_INIT_XPOS = 65; + R_TEXT_INIT_YPOS = 60; + R_TEXT_LINE_SPACING = 16; + R_TEXT_CHAR_SCALE = 80; + XREG(58) = 80; + XREG(59) = 12; + R_TEXT_DROP_SHADOW_OFFSET = 1; + R_TEXTBOX_BG_YPOS = 3; + XREG(62) = 0; + XREG(63) = 100; + R_TEXTBOX_END_XPOS = 158; + R_TEXTBOX_END_YPOS = 102; + R_TEXT_CHOICE_XPOS = 48; + R_TEXT_CHOICE_YPOS(0) = 54; + R_TEXT_CHOICE_YPOS(1) = 70; + R_TEXT_CHOICE_YPOS(2) = 86; + XREG(70) = -300; + XREG(71) = 0; + R_TEXTBOX_X_TARGET = 54; + R_TEXTBOX_Y_TARGET = 48; + R_TEXTBOX_WIDTH_TARGET = 128; + R_TEXTBOX_HEIGHT_TARGET = 64; + R_TEXTBOX_TEXWIDTH_TARGET = 2048; + R_TEXTBOX_TEXHEIGHT_TARGET = 512; + XREG(78) = 96; + XREG(79) = 98; + XREG(80) = 0; + XREG(81) = 50; + XREG(82) = 25; + XREG(83) = 100; + XREG(84) = 100; + XREG(85) = 0; + XREG(86) = 0; + XREG(87) = 0; + XREG(88) = -50; + XREG(89) = -100; + XREG(90) = -500; + XREG(91) = 0; + XREG(92) = 100; + XREG(93) = 100; + XREG(94) = 160; + XREG(95) = 200; + WREG(2) = -6080; + WREG(3) = 9355; + WREG(4) = 8; + WREG(5) = 3; + WREG(6) = 8; + WREG(7) = 0; + WREG(8) = 100; + WREG(9) = 109; + WREG(10) = 151; + WREG(11) = 148; + WREG(12) = 23; + WREG(13) = 22; + WREG(14) = -380; + WREG(15) = -350; + WREG(16) = -175; + WREG(17) = 155; + WREG(18) = 10; + WREG(19) = 10; + WREG(20) = -50; + WREG(21) = -54; + WREG(22) = -32; + WREG(23) = -38; + WREG(24) = -36; + WREG(25) = 40; + WREG(26) = -40; + WREG(27) = 0; + WREG(28) = 0; + R_OW_MINIMAP_X = 238; + R_OW_MINIMAP_Y = 164; + R_MINIMAP_DISABLED = false; + WREG(32) = 122; + WREG(33) = 60; + WREG(35) = 0; + WREG(36) = 0; + WREG(37) = 100; + WREG(38) = 99; + WREG(39) = 109; + R_B_LABEL_X(0) = B_BUTTON_X - 9; + R_B_LABEL_X(1) = B_BUTTON_X - 11; + R_B_LABEL_X(2) = B_BUTTON_X - 12; + R_B_LABEL_Y(0) = B_BUTTON_Y + 6; + R_B_LABEL_Y(1) = B_BUTTON_Y + 5; + R_B_LABEL_Y(2) = B_BUTTON_Y + 5; + WREG(46) = -380; + WREG(47) = -360; + WREG(48) = -350; + WREG(49) = -48; + WREG(50) = 16; + WREG(51) = -62; + WREG(52) = 22; + WREG(53) = -84; + WREG(54) = 20; + WREG(55) = -53; + WREG(56) = 40; + WREG(57) = -64; + WREG(58) = 47; + WREG(59) = -84; + WREG(60) = 44; + WREG(61) = -42; + WREG(62) = 32; + WREG(63) = -45; + WREG(64) = -37; + WREG(65) = 30; + WREG(66) = -50; + R_DGN_MINIMAP_X = 204; + R_DGN_MINIMAP_Y = 140; + WREG(87) = 80; + WREG(88) = 70; + WREG(89) = 40; + WREG(90) = 320; + WREG(91) = 40; + WREG(92) = 3; + WREG(93) = 6; + WREG(94) = 3; + WREG(95) = 6; + + if (gSaveContext.gameMode == 0) { + R_TEXTBOX_X = 52; + R_TEXTBOX_Y = 36; + VREG(2) = 214; + VREG(3) = 76; + VREG(4) = 304; + VREG(5) = 430; + VREG(6) = 1; + R_TEXTBOX_CLEF_XPOS = 78; + R_TEXTBOX_CLEF_YPOS = 166; + VREG(9) = 40; + R_COMPASS_SCALE_X = 32; + R_COMPASS_SCALE_Y = 32; + R_COMPASS_OFFSET_X = 110; + R_COMPASS_OFFSET_Y = -740; + R_MINIMAP_COLOR(0) = 0; + R_MINIMAP_COLOR(1) = 255; + R_MINIMAP_COLOR(2) = 255; + } + + VREG(21) = 0; + VREG(22) = 0; + VREG(23) = 0; + VREG(24) = 0; + VREG(25) = 0; + VREG(26) = 0; + VREG(27) = 0; + R_OCARINA_NOTES_XPOS = 98; + R_OCARINA_NOTES_XPOS_OFFSET = 18; + VREG(30) = 0; + VREG(31) = 0; + VREG(32) = 0; + + R_TEXT_ADJUST_COLOR_1_R = 70; + R_TEXT_ADJUST_COLOR_1_G = 255; + R_TEXT_ADJUST_COLOR_1_B = 80; + + R_TEXT_ADJUST_COLOR_2_R = 70; + R_TEXT_ADJUST_COLOR_2_G = 255; + R_TEXT_ADJUST_COLOR_2_B = 80; + + VREG(40) = 9; + VREG(42) = 250; + VREG(43) = 440; + VREG(44) = 10; + R_OCARINA_NOTES_YPOS(0) = 190; + R_OCARINA_NOTES_YPOS(1) = 184; + R_OCARINA_NOTES_YPOS(2) = 176; + R_OCARINA_NOTES_YPOS(3) = 172; + R_OCARINA_NOTES_YPOS(4) = 170; + VREG(50) = 30; + R_OCARINA_NOTES_YPOS_OFFSET = 0; + VREG(52) = -16; + VREG(53) = 230; + VREG(54) = 230; + VREG(55) = 120; + VREG(56) = -720; + VREG(57) = 255; + VREG(58) = 255; + VREG(59) = 255; + VREG(60) = 20; + VREG(61) = 100; + VREG(62) = 0; + VREG(63) = 10; + R_ITEM_AMMO_X(1) = C_LEFT_BUTTON_X + 1; + R_ITEM_AMMO_X(2) = C_DOWN_BUTTON_X + 1; + R_ITEM_AMMO_X(3) = C_RIGHT_BUTTON_X + 1; + R_ITEM_AMMO_Y(0) = B_BUTTON_Y + 18; + R_ITEM_AMMO_Y(1) = C_LEFT_BUTTON_Y + 17; + R_ITEM_AMMO_Y(2) = C_DOWN_BUTTON_Y + 17; + R_ITEM_AMMO_Y(3) = C_RIGHT_BUTTON_Y + 17; + VREG(72) = 0; + VREG(73) = 0; + VREG(74) = 0; + VREG(75) = 0; + R_ITEM_ICON_WIDTH(0) = 30; + R_ITEM_ICON_WIDTH(1) = 24; + R_ITEM_ICON_WIDTH(2) = 24; + R_ITEM_ICON_WIDTH(3) = 24; + R_ITEM_BTN_WIDTH(0) = 29; + R_ITEM_BTN_WIDTH(1) = 27; + R_ITEM_BTN_WIDTH(2) = 27; + R_ITEM_BTN_WIDTH(3) = 27; + VREG(84) = 0; + VREG(85) = 50; + VREG(86) = 0; + VREG(87) = 64; + VREG(88) = 66; + VREG(89) = 0; + VREG(90) = 126; + VREG(91) = 124; + VREG(92) = -63; +} + +void func_80112098(GlobalContext* globalCtx) { + func_80111070(); +} diff --git a/soh/src/code/z_debug.c b/soh/src/code/z_debug.c new file mode 100644 index 000000000..c88dd0288 --- /dev/null +++ b/soh/src/code/z_debug.c @@ -0,0 +1,252 @@ +#include "global.h" + +typedef struct { + u8 x; + u8 y; + u8 colorId; + char text[0x15]; +} PrintTextBuffer; + +typedef struct { + u16 push; + u16 held; +} InputCombo; + +GameInfo* gGameInfo; +s32 D_8015FA94; // no known symbols +PrintTextBuffer D_8015FA98[0x16]; + +s16 D_8011E0B0 = 0; // PrintTextBuffer index +Color_RGBA8 printTextColors[] = { + { 255, 255, 32, 192 }, { 255, 150, 128, 192 }, { 128, 96, 0, 64 }, { 192, 128, 16, 128 }, + { 255, 192, 32, 128 }, { 230, 230, 220, 64 }, { 128, 150, 255, 128 }, { 128, 255, 32, 128 }, +}; + +InputCombo inputCombos[REG_GROUPS] = { + { BTN_L, BTN_CUP }, { BTN_L, BTN_CLEFT }, { BTN_L, BTN_CDOWN }, { BTN_L, BTN_A }, { BTN_R, BTN_CDOWN }, + { BTN_L, BTN_CRIGHT }, { BTN_L, BTN_R }, { BTN_L, BTN_DLEFT }, { BTN_L, BTN_DRIGHT }, { BTN_L, BTN_DUP }, + { BTN_L, BTN_B }, { BTN_L, BTN_Z }, { BTN_L, BTN_DDOWN }, { BTN_R, BTN_A }, { BTN_R, BTN_B }, + { BTN_R, BTN_Z }, { BTN_R, BTN_L }, { BTN_R, BTN_CUP }, { BTN_R, BTN_CRIGHT }, { BTN_R, BTN_DLEFT }, + { BTN_R, BTN_CLEFT }, { BTN_R, BTN_START }, { BTN_L, BTN_START }, { BTN_R, BTN_DRIGHT }, { BTN_R, BTN_DUP }, + { BTN_START, BTN_R }, { BTN_START, BTN_A }, { BTN_START, BTN_B }, { BTN_START, BTN_CRIGHT }, +}; + +char regChar[] = " SOPQMYDUIZCNKXcsiWAVHGmnBdkb"; + +// initialize GameInfo +void func_800636C0(void) { + s32 i; + + gGameInfo = (GameInfo*)SystemArena_MallocDebug(sizeof(GameInfo), "../z_debug.c", 260); + gGameInfo->regPage = 0; + gGameInfo->regGroup = 0; + gGameInfo->regCur = 0; + gGameInfo->dpadLast = 0; + gGameInfo->repeat = 0; + for (i = 0; i < ARRAY_COUNT(gGameInfo->data); i++) { + gGameInfo->data[i] = 0; + } +} + +// Called when free movement is active. +// 8011D394 to enable camera debugger +void func_8006375C(s32 arg0, s32 arg1, const char* text) { +} + +// Copy Camera Debugger Text +void func_8006376C(u8 x, u8 y, u8 colorId, const char* text) { + PrintTextBuffer* buf; + char* bufText; + s16 i; + + buf = &D_8015FA98[D_8011E0B0]; + if (D_8011E0B0 < 0x16) { + buf->x = x; + buf->y = y; + buf->colorId = colorId; + + i = 0; + bufText = buf->text; + while ((*bufText++ = *text++)) { + if (i++ > 0x14) { + break; + } + } + + *bufText = '\0'; + D_8011E0B0++; + } +} + +// Draw Text +void func_80063828(GfxPrint* printer) { + s32 i; + Color_RGBA8* color; + PrintTextBuffer* buffer; + char* text; + + i = 0; + if (D_8011E0B0 > 0) { + do { + buffer = &D_8015FA98[i]; + text = buffer->text; + + color = &printTextColors[buffer->colorId]; + GfxPrint_SetColor(printer, color->r, color->g, color->b, color->a); + GfxPrint_SetPos(printer, buffer->x, buffer->y); + GfxPrint_Printf(printer, "%s", text); + i += 1; + } while (i < D_8011E0B0); + } +} + +// Edit REG +void func_8006390C(Input* input) { + + s32 dpad; + s32 regGroup; + s32 increment; + InputCombo* input_combo; + s32 i; + + if (!CVar_GetS32("gDebugEnabled", 0)) + return; + + regGroup = (gGameInfo->regGroup * REG_PAGES + gGameInfo->regPage) * REG_PER_PAGE - REG_PER_PAGE; + dpad = input->cur.button & (BTN_DUP | BTN_DLEFT | BTN_DRIGHT | BTN_DDOWN); + if (CHECK_BTN_ALL(input->cur.button, BTN_L) || CHECK_BTN_ALL(input->cur.button, BTN_R) || + CHECK_BTN_ALL(input->cur.button, BTN_START)) { + input_combo = inputCombos; + for (i = 0; i < REG_GROUPS; i++) { + if (~(~input_combo->push | input->cur.button) || ~(~input_combo->held | input->press.button)) { + input_combo++; + } else { + break; + } + } + + if (i < REG_GROUPS) { + if (i == gGameInfo->regGroup) { + gGameInfo->regPage = (gGameInfo->regPage + 1) % (REG_PAGES + 1); + return; + } + gGameInfo->regGroup = i; + gGameInfo->regPage = 0; + } + } else { + switch (gGameInfo->regPage - 1) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + + if (dpad == gGameInfo->dpadLast) { + gGameInfo->repeat--; + if (gGameInfo->repeat < 0) { + gGameInfo->repeat = 1; + } else { + dpad ^= gGameInfo->dpadLast; + } + } else { + gGameInfo->repeat = 0x10; + gGameInfo->dpadLast = dpad; + } + + increment = CHECK_BTN_ANY(dpad, BTN_DRIGHT) ? (CHECK_BTN_ALL(input->cur.button, BTN_A | BTN_B) ? 1000 + : CHECK_BTN_ALL(input->cur.button, BTN_A) ? 100 + : CHECK_BTN_ALL(input->cur.button, BTN_B) ? 10 + : 1) + : CHECK_BTN_ANY(dpad, BTN_DLEFT) ? (CHECK_BTN_ALL(input->cur.button, BTN_A | BTN_B) ? -1000 + : CHECK_BTN_ALL(input->cur.button, BTN_A) ? -100 + : CHECK_BTN_ALL(input->cur.button, BTN_B) ? -10 + : -1) + : 0; + + gGameInfo->data[gGameInfo->regCur + regGroup] += increment; + if (CHECK_BTN_ANY(dpad, BTN_DUP)) { + gGameInfo->regCur--; + if (gGameInfo->regCur < 0) { + gGameInfo->regCur = REG_PER_PAGE - 1; + } + } else if (CHECK_BTN_ANY(dpad, BTN_DDOWN)) { + gGameInfo->regCur++; + if (gGameInfo->regCur >= REG_PER_PAGE) { + gGameInfo->regCur = 0; + } + } + if (iREG(0)) { + iREG(0) = 0; + func_800AA000(0, iREG(1), iREG(2), iREG(3)); + } + } + } +} + +// Draw Memory Viewer +void func_80063C04(GfxPrint* printer) { + s32 i; + s32 page = (gGameInfo->regPage * REG_PER_PAGE) - REG_PER_PAGE; + s32 regGroup = (gGameInfo->regGroup * REG_PAGES + gGameInfo->regPage) * REG_PER_PAGE - REG_PER_PAGE; + s32 pad; + char name[3]; + + if (!CVar_GetS32("gDebugEnabled", 0)) + return; + + // set up register name string + name[0] = 'R'; + name[1] = regChar[gGameInfo->regGroup]; // r_group type char + name[2] = '\0'; + GfxPrint_SetColor(printer, 0, 128, 128, 128); + + for (i = 0; i != REG_PER_PAGE; i++) { + if (i == gGameInfo->regCur) { + GfxPrint_SetColor(printer, 0, 255, 255, 255); + } + GfxPrint_SetPos(printer, 3, i + 5); + GfxPrint_Printf(printer, "%s%02d%6d", &name, page + i, gGameInfo->data[i + regGroup]); + if (i == gGameInfo->regCur) { + GfxPrint_SetColor(printer, 0, 128, 128, 128); + } + } +} + +void func_80063D7C(GraphicsContext* gfxCtx) { + Gfx* sp7C; + Gfx* sp78; + GfxPrint printer; + Gfx* tempRet; + + if (!CVar_GetS32("gDebugEnabled", 0)) + return; + + OPEN_DISPS(gfxCtx, "../z_debug.c", 628); + + GfxPrint_Init(&printer); + sp78 = POLY_OPA_DISP; + tempRet = Graph_GfxPlusOne(POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, tempRet); + GfxPrint_Open(&printer, tempRet); + + if ((OREG(0) == 1) || (OREG(0) == 8)) { + func_80063828(&printer); + } + + if (gGameInfo->regPage != 0) { + func_80063C04(&printer); + } + + D_8011E0B0 = 0; + sp7C = GfxPrint_Close(&printer); + gSPEndDisplayList(sp7C++); + Graph_BranchDlist(sp78, sp7C); + POLY_OPA_DISP = sp7C; + + if (1) {} + + CLOSE_DISPS(gfxCtx, "../z_debug.c", 664); + + GfxPrint_Destroy(&printer); +} diff --git a/soh/src/code/z_debug_display.c b/soh/src/code/z_debug_display.c new file mode 100644 index 000000000..efa25dc89 --- /dev/null +++ b/soh/src/code/z_debug_display.c @@ -0,0 +1,108 @@ +#include "global.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +typedef struct { + /* 0x00 */ s16 drawType; // indicates which draw function to use when displaying the object + /* 0x04 */ void* drawArg; // segment address (display list or texture) passed to the draw function when called +} DebugDispObjectInfo; // size = 0x8 + +typedef void (*DebugDispObject_DrawFunc)(DebugDispObject*, void*, GlobalContext*); + +void DebugDisplay_DrawSpriteI8(DebugDispObject* dispObj, void* texture, GlobalContext* globalCtx); +void DebugDisplay_DrawPolygon(DebugDispObject* dispObj, void* dlist, GlobalContext* globalCtx); + +static DebugDispObject_DrawFunc sDebugObjectDrawFuncTable[] = { + DebugDisplay_DrawSpriteI8, + DebugDisplay_DrawPolygon, +}; + +static DebugDispObjectInfo sDebugObjectInfoTable[] = { + { 0, gDebugCircleTex }, { 0, gDebugCrossTex }, { 0, gDebugBallTex }, + { 0, gDebugCursorTex }, { 1, gDebugArrowDL }, { 1, gDebugCameraDL }, +}; + +static Lights1 sDebugObjectLights = gdSPDefLights1(0x80, 0x80, 0x80, 0xFF, 0xFF, 0xFF, 0x49, 0x49, 0x49); + +static DebugDispObject* sDebugObjectListHead; + +void DebugDisplay_Init(void) { + sDebugObjectListHead = NULL; +} + +DebugDispObject* DebugDisplay_AddObject(f32 posX, f32 posY, f32 posZ, s16 rotX, s16 rotY, s16 rotZ, f32 scaleX, + f32 scaleY, f32 scaleZ, u8 red, u8 green, u8 blue, u8 alpha, s16 type, + GraphicsContext* gfxCtx) { + DebugDispObject* prevHead = sDebugObjectListHead; + + sDebugObjectListHead = Graph_Alloc(gfxCtx, sizeof(DebugDispObject)); + + sDebugObjectListHead->pos.x = posX; + sDebugObjectListHead->pos.y = posY; + sDebugObjectListHead->pos.z = posZ; + sDebugObjectListHead->rot.x = rotX; + sDebugObjectListHead->rot.y = rotY; + sDebugObjectListHead->rot.z = rotZ; + sDebugObjectListHead->scale.x = scaleX; + sDebugObjectListHead->scale.y = scaleY; + sDebugObjectListHead->scale.z = scaleZ; + sDebugObjectListHead->color.r = red; + sDebugObjectListHead->color.g = green; + sDebugObjectListHead->color.b = blue; + sDebugObjectListHead->color.a = alpha; + sDebugObjectListHead->type = type; + sDebugObjectListHead->next = prevHead; + + return sDebugObjectListHead; +} + +void DebugDisplay_DrawObjects(GlobalContext* globalCtx) { + DebugDispObject* dispObj = sDebugObjectListHead; + DebugDispObjectInfo* objInfo; + + while (dispObj != NULL) { + objInfo = &sDebugObjectInfoTable[dispObj->type]; + sDebugObjectDrawFuncTable[objInfo->drawType](dispObj, objInfo->drawArg, globalCtx); + dispObj = dispObj->next; + } +} + +void DebugDisplay_DrawSpriteI8(DebugDispObject* dispObj, void* texture, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_debug_display.c", 169); + + func_80094678(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, dispObj->color.r, dispObj->color.g, dispObj->color.b, dispObj->color.a); + + Matrix_Translate(dispObj->pos.x, dispObj->pos.y, dispObj->pos.z, MTXMODE_NEW); + Matrix_Scale(dispObj->scale.x, dispObj->scale.y, dispObj->scale.z, MTXMODE_APPLY); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZYX(dispObj->rot.x, dispObj->rot.y, dispObj->rot.z, MTXMODE_APPLY); + + gDPLoadTextureBlock(POLY_XLU_DISP++, texture, G_IM_FMT_I, G_IM_SIZ_8b, 16, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_debug_display.c", 189), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, gDebugSpriteDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_debug_display.c", 192); +} + +void DebugDisplay_DrawPolygon(DebugDispObject* dispObj, void* dlist, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_debug_display.c", 211); + + func_8009435C(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, dispObj->color.r, dispObj->color.g, dispObj->color.b, dispObj->color.a); + + gSPSetLights1(POLY_XLU_DISP++, sDebugObjectLights); + + Matrix_SetTranslateRotateYXZ(dispObj->pos.x, dispObj->pos.y, dispObj->pos.z, &dispObj->rot); + Matrix_Scale(dispObj->scale.x, dispObj->scale.y, dispObj->scale.z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_debug_display.c", 228), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, dlist); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_debug_display.c", 231); +} diff --git a/soh/src/code/z_demo.c b/soh/src/code/z_demo.c new file mode 100644 index 000000000..d0145fb43 --- /dev/null +++ b/soh/src/code/z_demo.c @@ -0,0 +1,2114 @@ +#include "global.h" +#include "z64camera.h" + +#include + +#include "scenes/indoors/tokinoma/tokinoma_scene.h" +#include "scenes/overworld/spot00/spot00_scene.h" +#include "scenes/overworld/spot01/spot01_scene.h" +#include "scenes/overworld/spot02/spot02_scene.h" +#include "scenes/overworld/spot04/spot04_scene.h" +#include "scenes/overworld/spot05/spot05_scene.h" +#include "scenes/overworld/spot06/spot06_scene.h" +#include "scenes/overworld/spot07/spot07_scene.h" +#include "scenes/overworld/spot08/spot08_scene.h" +#include "scenes/overworld/spot09/spot09_scene.h" +#include "scenes/overworld/spot11/spot11_scene.h" +#include "scenes/overworld/spot12/spot12_scene.h" +#include "scenes/overworld/spot15/spot15_scene.h" +#include "scenes/overworld/spot16/spot16_scene.h" +#include "scenes/overworld/spot17/spot17_scene.h" +#include "scenes/overworld/spot18/spot18_scene.h" +#include "scenes/overworld/spot20/spot20_scene.h" + +#include "scenes/dungeons/bdan/bdan_scene.h" +#include "scenes/dungeons/ddan/ddan_scene.h" +#include "scenes/dungeons/ydan/ydan_scene.h" +#include "scenes/dungeons/ganontika/ganontika_scene.h" +#include "scenes/dungeons/ganon_tou/ganon_tou_scene.h" +#include "scenes/dungeons/jyasinboss/jyasinboss_scene.h" +#include "scenes/dungeons/ice_doukutu/ice_doukutu_scene.h" + +#include "scenes/misc/hakaana_ouke/hakaana_ouke_scene.h" + +u16 D_8011E1C0 = 0; +u16 D_8011E1C4 = 0; + +typedef void (*CutsceneStateHandler)(GlobalContext*, CutsceneContext*); + +void func_80064720(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80064760(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_800647C0(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80068C3C(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80068D84(GlobalContext* globalCtx, CutsceneContext* csCtx); +void func_80068DC0(GlobalContext* globalCtx, CutsceneContext* csCtx); + +CutsceneStateHandler sCsStateHandlers1[] = { + func_80064720, func_80064760, func_80064720, func_80068D84, func_80064720, +}; + +CutsceneStateHandler sCsStateHandlers2[] = { + func_80064720, func_800647C0, func_80068C3C, func_80068DC0, func_80068C3C, +}; + +u8 sTitleCsState = 0; + +EntranceCutscene sEntranceCutsceneTable[] = { + { 0x0185, 2, 0xA0, gHyruleFieldIntroCs }, + { 0x013D, 2, 0xA1, gDMTIntroCs }, + { 0x00DB, 2, 0xA3, gKakarikoVillageIntroCs }, + { 0x0108, 2, 0xA4, gZorasDomainIntroCs }, + { 0x0138, 1, 0xA5, gHyruleCastleIntroCs }, + { 0x014D, 2, 0xA6, gGoronCityIntroCs }, + { 0x0053, 2, 0xA7, gTempleOfTimeIntroCs }, + { 0x0000, 2, 0xA8, gDekuTreeIntroCs }, + { 0x028A, 0, 0x18, gHyruleFieldSouthEponaJumpCs }, + { 0x0292, 0, 0x18, gHyruleFieldEastEponaJumpCs }, + { 0x028E, 0, 0x18, gHyruleFieldWestEponaJumpCs }, + { 0x0476, 0, 0x18, gHyruleFieldGateEponaJumpCs }, + { 0x050F, 1, 0xA9, gHyruleFieldGetOoTCs }, + { 0x0102, 2, 0xB1, gLakeHyliaIntroCs }, + { 0x0117, 2, 0xB2, gGerudoValleyIntroCs }, + { 0x0129, 2, 0xB3, gGerudoFortressIntroCs }, + { 0x0157, 2, 0xB4, gLonLonRanchIntroCs }, + { 0x0028, 2, 0xB5, gJabuJabuIntroCs }, + { 0x00E4, 2, 0xB6, gGraveyardIntroCs }, + { 0x0225, 2, 0xB7, gZorasFountainIntroCs }, + { 0x0123, 2, 0xB8, gDesertColossusIntroCs }, + { 0x0147, 2, 0xB9, gDeathMountainCraterIntroCs }, + { 0x0138, 0, 0xBA, gGanonsCastleIntroCs }, + { 0x0574, 2, 0x5A, gSunSongGraveSunSongTeachPart2Cs }, + { 0x0538, 2, 0xBB, gForestBarrierCs }, + { 0x053C, 2, 0xBC, gWaterBarrierCs }, + { 0x0540, 2, 0xBD, gShadowBarrierCs }, + { 0x0544, 2, 0xBE, gFireBarrierCs }, + { 0x0548, 2, 0xBF, gLightBarrierCs }, + { 0x054C, 2, 0xAD, gSpiritBarrierCs }, + { 0x008D, 0, 0xC0, gSpiritBossNabooruKnuckleIntroCs }, + { 0x03B4, 0, 0xC7, gGerudoFortressFirstCaptureCs }, + { 0x0246, 2, 0xB9, gDeathMountainCraterIntroCs }, + { 0x05E8, 2, 0xC6, gKokiriForestDekuSproutCs }, +}; + +// Unused, seems to be an early list of dungeon entrance cutscene locations +void* D_8011E304[] = { + gDekuTreeIntroCs, gJabuJabuIntroCs, gDcOpeningCs, gMinuetCs, gIceCavernSerenadeCs, gTowerBarrierCs, +}; + +u16 D_8015FCC0; +u16 D_8015FCC2; +u16 D_8015FCC4; +s16 D_8015FCC6; +u8 D_8015FCC8; +s16 sQuakeIndex; +u16 D_8015FCCC; // only written to, never read +char D_8015FCD0[20]; // unreferenced +u8 D_8015FCE4; // only written to, never read + +void func_80068ECC(GlobalContext* globalCtx, CutsceneContext* csCtx); + +void Cutscene_DrawDebugInfo(GlobalContext* globalCtx, Gfx** dlist, CutsceneContext* csCtx) { + GfxPrint printer; + s32 pad[2]; + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, *dlist); + + GfxPrint_SetPos(&printer, 22, 25); + GfxPrint_SetColor(&printer, 255, 255, 55, 32); + GfxPrint_Printf(&printer, "%s", "FLAME "); + GfxPrint_SetColor(&printer, 255, 255, 255, 32); + GfxPrint_Printf(&printer, "%06d", csCtx->frames); + GfxPrint_SetColor(&printer, 50, 255, 255, 60); + GfxPrint_SetPos(&printer, 4, 26); + GfxPrint_Printf(&printer, "%s", "SKIP=(START) or (Cursole Right)"); + + *dlist = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); +} + +void func_8006450C(GlobalContext* globalCtx, CutsceneContext* csCtx) { + csCtx->state = CS_STATE_IDLE; + csCtx->unk_0C = 0.0f; +} + +void func_80064520(GlobalContext* globalCtx, CutsceneContext* csCtx) { + csCtx->state = CS_STATE_SKIPPABLE_INIT; + csCtx->linkAction = NULL; +} + +void func_80064534(GlobalContext* globalCtx, CutsceneContext* csCtx) { + if (csCtx->state != CS_STATE_UNSKIPPABLE_EXEC) { + csCtx->state = CS_STATE_UNSKIPPABLE_INIT; + } +} + +void func_80064558(GlobalContext* globalCtx, CutsceneContext* csCtx) { + if (gSaveContext.cutsceneIndex < 0xFFF0) { + sCsStateHandlers1[csCtx->state](globalCtx, csCtx); + } +} + +void func_800645A0(GlobalContext* globalCtx, CutsceneContext* csCtx) { + Input* input = &globalCtx->state.input[0]; + + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT) && (csCtx->state == CS_STATE_IDLE) && + (gSaveContext.sceneSetupIndex >= 4)) { + D_8015FCC8 = 0; + gSaveContext.cutsceneIndex = 0xFFFD; + gSaveContext.cutsceneTrigger = 1; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DUP) && (csCtx->state == CS_STATE_IDLE) && + (gSaveContext.sceneSetupIndex >= 4) && !gDbgCamEnabled) { + D_8015FCC8 = 1; + gSaveContext.cutsceneIndex = 0xFFFD; + gSaveContext.cutsceneTrigger = 1; + } + + if ((gSaveContext.cutsceneTrigger != 0) && (globalCtx->sceneLoadFlag == 0x14)) { + gSaveContext.cutsceneTrigger = 0; + } + + if ((gSaveContext.cutsceneTrigger != 0) && (csCtx->state == CS_STATE_IDLE)) { + osSyncPrintf("\nデモ開始要求 発令!"); // "Cutscene start request announcement!" + gSaveContext.cutsceneIndex = 0xFFFD; + gSaveContext.cutsceneTrigger = 1; + } + + if (gSaveContext.cutsceneIndex >= 0xFFF0) { + func_80068ECC(globalCtx, csCtx); + sCsStateHandlers2[csCtx->state](globalCtx, csCtx); + } +} + +void func_80064720(GlobalContext* globalCtx, CutsceneContext* csCtx) { +} + +u32 func_8006472C(GlobalContext* globalCtx, CutsceneContext* csCtx, f32 target) { + return Math_StepToF(&csCtx->unk_0C, target, 0.1f); +} + +void func_80064760(GlobalContext* globalCtx, CutsceneContext* csCtx) { + Interface_ChangeAlpha(1); + ShrinkWindow_SetVal(0x20); + + if (func_8006472C(globalCtx, csCtx, 1.0f)) { + Audio_SetCutsceneFlag(1); + csCtx->state++; + } +} + +void func_800647C0(GlobalContext* globalCtx, CutsceneContext* csCtx) { + func_80068C3C(globalCtx, csCtx); + Interface_ChangeAlpha(1); + ShrinkWindow_SetVal(0x20); + + if (func_8006472C(globalCtx, csCtx, 1.0f)) { + Audio_SetCutsceneFlag(1); + csCtx->state++; + } +} + +// Command 3: Misc. Actions +void func_80064824(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdBase* cmd) { + Player* player = GET_PLAYER(globalCtx); + f32 temp; + u8 sp3F = 0; + + if ((csCtx->frames < cmd->startFrame) || ((csCtx->frames >= cmd->endFrame) && (cmd->endFrame != cmd->startFrame))) { + return; + } + + temp = Environment_LerpWeight(cmd->endFrame - 1, cmd->startFrame, csCtx->frames); + + if (csCtx->frames == cmd->startFrame) { + sp3F = 1; + } + + switch (cmd->base) { + case 1: + if (sp3F != 0) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_4, 0x3F); + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 1); + globalCtx->envCtx.unk_EE[0] = 20; + } + break; + case 2: + if (sp3F != 0) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_0, 0); + Environment_AddLightningBolts(globalCtx, 3); + gLightningStrike.state = LIGHTNING_STRIKE_START; + } + break; + case 3: + if (sp3F != 0) { + Flags_SetEnv(globalCtx, 0); + if (gSaveContext.entranceIndex == 0x0053) { + Flags_SetEnv(globalCtx, 2); + } + } + break; + case 6: + if (globalCtx->envCtx.adjFogFar < 12800) { + globalCtx->envCtx.adjFogFar += 35; + } + break; + case 7: + if (sp3F != 0) { + globalCtx->envCtx.unk_19 = 1; + globalCtx->envCtx.unk_17 = 1; + globalCtx->envCtx.unk_18 = 0; + globalCtx->envCtx.unk_1A = 0x3C; + globalCtx->envCtx.unk_21 = 1; + globalCtx->envCtx.unk_1F = 0; + globalCtx->envCtx.unk_20 = 1; + globalCtx->envCtx.unk_22 = globalCtx->envCtx.unk_24 = 0x3C; + } + break; + case 8: + if (globalCtx->roomCtx.unk_74[0] < 0x80) { + globalCtx->roomCtx.unk_74[0] += 4; + } + break; + case 9: + globalCtx->envCtx.unk_EE[3] = 16; + break; + case 10: + Flags_SetEnv(globalCtx, 1); + break; + case 11: + if (globalCtx->roomCtx.unk_74[0] < 0x672) { + globalCtx->roomCtx.unk_74[0] += 0x14; + } + if (csCtx->frames == 0x30F) { + func_80078884(NA_SE_EV_DEKU_DEATH); + } else if (csCtx->frames == 0x2CD) { + globalCtx->roomCtx.unk_74[0] = 0; + } + break; + case 12: + if (sp3F != 0) { + if (csCtx->state != CS_STATE_UNSKIPPABLE_EXEC) { + csCtx->state = CS_STATE_UNSKIPPABLE_INIT; + } + } + break; + case 13: + if (globalCtx->roomCtx.unk_74[1] == 0) { + func_80078884(NA_SE_EV_TRIFORCE_FLASH); + } + if (globalCtx->roomCtx.unk_74[1] < 0xFF) { + globalCtx->roomCtx.unk_74[1] += 5; + } + break; + case 14: + if (sp3F != 0) { + func_800BC490(globalCtx, 1); + } + break; + case 15: + if (sp3F != 0) { + TitleCard_InitPlaceName(globalCtx, &globalCtx->actorCtx.titleCtx, player->giObjectSegment, 160, 120, + 144, 24, 20); + } + break; + case 16: + if (sp3F != 0) { + sQuakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 6); + Quake_SetSpeed(sQuakeIndex, 0x7FFF); + Quake_SetQuakeValues(sQuakeIndex, 4, 0, 1000, 0); + Quake_SetCountdown(sQuakeIndex, 800); + } + break; + case 17: + if (sp3F != 0) { + Quake_RemoveFromIdx(sQuakeIndex); + } + break; + case 18: + globalCtx->envCtx.unk_EE[0] = 0; + globalCtx->envCtx.gloomySkyMode = 2; + if (gSaveContext.dayTime < 0x4AAB) { + gSaveContext.dayTime += 30; + } + if (globalCtx->envCtx.unk_EE[1] == 0) { + gWeatherMode = 0; + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 0); + } + break; + case 19: + gSaveContext.eventChkInf[6] |= 0x0020; + break; + case 20: + gSaveContext.eventChkInf[6] |= 0x0080; + break; + case 21: + gSaveContext.eventChkInf[6] |= 0x0200; + break; + case 22: + D_801614B0.r = 255; + D_801614B0.g = 255; + D_801614B0.b = 255; + D_801614B0.a = 255; + break; + case 23: + D_801614B0.r = 255; + D_801614B0.g = 180; + D_801614B0.b = 100; + D_801614B0.a = 255.0f * temp; + break; + case 24: + globalCtx->roomCtx.curRoom.segment = NULL; + break; + case 25: + gSaveContext.dayTime += 30; + if ((gSaveContext.dayTime) > 0xCAAA) { + gSaveContext.dayTime = 0xCAAA; + } + break; + case 26: + if ((gSaveContext.dayTime < 0x3000) || (gSaveContext.dayTime >= 0x4555)) { + if ((gSaveContext.dayTime >= 0x4555) && (gSaveContext.dayTime < 0xAAAB)) { + globalCtx->envCtx.unk_BF = 1; + } else if ((gSaveContext.dayTime >= 0xAAAB) && (gSaveContext.dayTime < 0xC556)) { + globalCtx->envCtx.unk_BF = 2; + } else { + globalCtx->envCtx.unk_BF = 3; + } + } + break; + case 27: + if (globalCtx->state.frames & 8) { + if (globalCtx->envCtx.adjAmbientColor[0] < 40) { + globalCtx->envCtx.adjAmbientColor[0] += 2; + globalCtx->envCtx.adjLight1Color[1] -= 3; + globalCtx->envCtx.adjLight1Color[2] -= 3; + } + } else { + if (globalCtx->envCtx.adjAmbientColor[0] > 2) { + globalCtx->envCtx.adjAmbientColor[0] -= 2; + globalCtx->envCtx.adjLight1Color[1] += 3; + globalCtx->envCtx.adjLight1Color[2] += 3; + } + } + break; + case 28: + globalCtx->unk_11DE9 = 1; + break; + case 29: + globalCtx->unk_11DE9 = 0; + break; + case 30: + Flags_SetEnv(globalCtx, 3); + break; + case 31: + Flags_SetEnv(globalCtx, 4); + break; + case 32: + if (sp3F != 0) { + globalCtx->envCtx.sandstormState = 1; + } + func_800788CC(NA_SE_EV_SAND_STORM - SFX_FLAG); + break; + case 33: + gSaveContext.sunsSongState = SUNSSONG_START; + break; + case 34: + if (IS_DAY) { + gSaveContext.dayTime -= gTimeIncrement; + } else { + gSaveContext.dayTime -= gTimeIncrement * 2; + } + break; + case 35: + func_800EE824(); + csCtx->frames = cmd->startFrame - 1; + break; + } +} + +// Command 4: Set Environment Lighting +void Cutscene_Command_SetLighting(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdEnvLighting* cmd) { + if (csCtx->frames == cmd->startFrame) { + globalCtx->envCtx.unk_BF = cmd->setting - 1; + globalCtx->envCtx.unk_D8 = 1.0f; + } +} + +// Command 0x56: Play Background Music +void Cutscene_Command_PlayBGM(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdMusicChange* cmd) { + if (csCtx->frames == cmd->startFrame) { + func_800F595C(cmd->sequence - 1); + } +} + +// Command 0x57: Stop Background Music +void Cutscene_Command_StopBGM(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdMusicChange* cmd) { + if (csCtx->frames == cmd->startFrame) { + func_800F59E8(cmd->sequence - 1); + } +} + +// Command 0x7C: Fade Background Music over duration +void Cutscene_Command_FadeBGM(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdMusicFade* cmd) { + u8 var1; + + if ((csCtx->frames == cmd->startFrame) && (csCtx->frames < cmd->endFrame)) { + var1 = cmd->endFrame - cmd->startFrame; + + if (cmd->type == 3) { + Audio_QueueSeqCmd(var1 << 0x10 | (0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF)); + } else { + Audio_QueueSeqCmd(var1 << 0x10 | (0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xFF)); + } + } +} + +// Command 9: ? +void Cutscene_Command_09(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdUnknown9* cmd) { + if (csCtx->frames == cmd->startFrame) { + func_800AA000(0.0f, cmd->unk_06, cmd->unk_07, cmd->unk_08); + } +} + +// Command 0x8C: Set Time of Day & Environment Time +void func_80065134(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdDayTime* cmd) { + s16 temp1; + s16 temp2; + + if (csCtx->frames == cmd->startFrame) { + temp1 = (cmd->hour * 60.0f) / (360.0f / 0x4000); + temp2 = (cmd->minute + 1) / (360.0f / 0x4000); + + gSaveContext.dayTime = temp1 + temp2; + gSaveContext.skyboxTime = temp1 + temp2; + } +} + +// Command 0x3E8: Code Execution (& Terminates Cutscene?) +void Cutscene_Command_Terminator(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdBase* cmd) { + Player* player = GET_PLAYER(globalCtx); + s32 temp = 0; + + if ((gSaveContext.gameMode != 0) && (gSaveContext.gameMode != 3) && (globalCtx->sceneNum != SCENE_SPOT00) && + (csCtx->frames > 20) && + (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START)) && + (gSaveContext.fileNum != 0xFEDC) && (globalCtx->sceneLoadFlag == 0)) { + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + temp = 1; + } + + if ((csCtx->frames == cmd->startFrame) || (temp != 0) || + ((csCtx->frames > 20) && CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) && + (gSaveContext.fileNum != 0xFEDC)) && CVar_GetS32("gDebugEnabled", 0)) { + csCtx->state = CS_STATE_UNSKIPPABLE_EXEC; + Audio_SetCutsceneFlag(0); + gSaveContext.unk_1410 = 1; + + osSyncPrintf("\n分岐先指定!!=[%d]番", cmd->base); // "Future fork designation=No. [%d]" + + if ((gSaveContext.gameMode != 0) && (csCtx->frames != cmd->startFrame)) { + gSaveContext.unk_13E7 = 1; + } + + gSaveContext.cutsceneIndex = 0; + + switch (cmd->base) { + case 1: + globalCtx->nextEntranceIndex = 0x00A0; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 2: + globalCtx->nextEntranceIndex = 0x00A0; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 10; + break; + case 3: + globalCtx->nextEntranceIndex = 0x0117; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 10; + break; + case 4: + globalCtx->nextEntranceIndex = 0x013D; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 10; + break; + case 5: + globalCtx->nextEntranceIndex = 0x00EE; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 10; + break; + case 6: + globalCtx->nextEntranceIndex = 0x00A0; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 10; + break; + case 7: + globalCtx->nextEntranceIndex = 0x00EE; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 11; + break; + case 8: + gSaveContext.fw.set = 0; + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 0; + if (!(gSaveContext.eventChkInf[4] & 0x20)) { + gSaveContext.eventChkInf[4] |= 0x20; + globalCtx->nextEntranceIndex = 0x00A0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->fadeTransition = 11; + } else { + if (gSaveContext.sceneSetupIndex < 4) { + if (!LINK_IS_ADULT) { + globalCtx->linkAgeOnLoad = 0; + } else { + globalCtx->linkAgeOnLoad = 1; + } + } + globalCtx->nextEntranceIndex = 0x02CA; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + } + break; + case 9: + globalCtx->nextEntranceIndex = 0x0117; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 12; + break; + case 10: + globalCtx->nextEntranceIndex = 0x00BB; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 11: + globalCtx->nextEntranceIndex = 0x00EE; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + break; + case 12: + globalCtx->nextEntranceIndex = 0x047A; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 13: + globalCtx->nextEntranceIndex = 0x010E; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + break; + case 14: + globalCtx->nextEntranceIndex = 0x0457; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 15: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF4; + globalCtx->fadeTransition = 3; + break; + case 16: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF5; + globalCtx->fadeTransition = 3; + break; + case 17: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF6; + globalCtx->fadeTransition = 3; + break; + case 18: + gSaveContext.eventChkInf[4] |= 0x8000; + globalCtx->nextEntranceIndex = 0x0324; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + break; + case 19: + globalCtx->nextEntranceIndex = 0x013D; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 4; + gSaveContext.cutsceneIndex = 0x8000; + break; + case 21: + globalCtx->nextEntranceIndex = 0x0102; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 3; + break; + case 22: + Item_Give(globalCtx, ITEM_SONG_REQUIEM); + globalCtx->nextEntranceIndex = 0x0123; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 3; + break; + case 23: + globalCtx->nextEntranceIndex = 0x00A0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF8; + globalCtx->fadeTransition = 3; + break; + case 24: + globalCtx->nextEntranceIndex = 0x0028; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 25: + globalCtx->linkAgeOnLoad = 0; + globalCtx->nextEntranceIndex = 0x006B; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 3; + break; + case 26: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF4; + globalCtx->fadeTransition = 3; + break; + case 27: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF5; + globalCtx->fadeTransition = 3; + break; + case 28: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF6; + globalCtx->fadeTransition = 3; + break; + case 29: + globalCtx->nextEntranceIndex = 0x006B; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.chamberCutsceneNum = 0; + globalCtx->fadeTransition = 3; + break; + case 30: + globalCtx->nextEntranceIndex = 0x006B; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + Item_Give(globalCtx, ITEM_MEDALLION_FIRE); + gSaveContext.chamberCutsceneNum = 1; + break; + case 31: + globalCtx->nextEntranceIndex = 0x006B; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.chamberCutsceneNum = 2; + break; + case 32: + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x00CD; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->fadeTransition = 11; + break; + case 33: + globalCtx->nextEntranceIndex = 0x00CD; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + break; + case 34: + globalCtx->nextEntranceIndex = 0x00A0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->fadeTransition = 3; + break; + case 35: + globalCtx->nextEntranceIndex = 0x00CD; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 4; + break; + case 38: + globalCtx->nextEntranceIndex = 0x00A0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF4; + globalCtx->fadeTransition = 4; + break; + case 39: + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF9; + globalCtx->fadeTransition = 4; + break; + case 40: + globalCtx->linkAgeOnLoad = 0; + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFFA; + globalCtx->fadeTransition = 4; + break; + case 41: + globalCtx->nextEntranceIndex = 0x04E6; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 42: + globalCtx->nextEntranceIndex = 0x00DB; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->fadeTransition = 4; + break; + case 43: + globalCtx->nextEntranceIndex = 0x0503; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 4; + break; + case 44: + globalCtx->nextEntranceIndex = 0x0320; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 17; + break; + case 46: + gSaveContext.eventChkInf[4] |= 0x8000; + globalCtx->nextEntranceIndex = 0x0324; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 4; + break; + case 47: + Item_Give(globalCtx, ITEM_SONG_NOCTURNE); + gSaveContext.eventChkInf[5] |= 0x10; + globalCtx->nextEntranceIndex = 0x00DB; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 4; + break; + case 48: + globalCtx->nextEntranceIndex = 0x01ED; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 15; + gSaveContext.nextTransition = 15; + break; + case 49: + globalCtx->nextEntranceIndex = 0x058C; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 4; + break; + case 50: + globalCtx->nextEntranceIndex = 0x0513; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 17; + break; + case 51: + globalCtx->nextEntranceIndex = 0x00CD; + gSaveContext.cutsceneIndex = 0xFFF8; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 41; + break; + case 52: + globalCtx->nextEntranceIndex = 0x0053; + gSaveContext.cutsceneIndex = 0xFFF7; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 11; + break; + case 53: + globalCtx->nextEntranceIndex = 0x050F; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + break; + case 54: + gSaveContext.gameMode = 3; + Audio_SetSoundBanksMute(0x6F); + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x0117; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 55: + globalCtx->nextEntranceIndex = 0x0129; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 56: + globalCtx->nextEntranceIndex = 0x00DB; + gSaveContext.cutsceneIndex = 0xFFF4; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 57: + globalCtx->nextEntranceIndex = 0x013D; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 58: + globalCtx->nextEntranceIndex = 0x014D; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 59: + globalCtx->nextEntranceIndex = 0x0102; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 60: + globalCtx->nextEntranceIndex = 0x010E; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 61: + globalCtx->nextEntranceIndex = 0x0108; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 62: + globalCtx->linkAgeOnLoad = 0; + globalCtx->nextEntranceIndex = 0x00EE; + gSaveContext.cutsceneIndex = 0xFFF6; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 63: + globalCtx->nextEntranceIndex = 0x00EE; + gSaveContext.cutsceneIndex = 0xFFF7; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 64: + globalCtx->nextEntranceIndex = 0x00CD; + gSaveContext.cutsceneIndex = 0xFFF5; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 65: + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x0157; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 66: + globalCtx->nextEntranceIndex = 0x0554; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 67: + globalCtx->nextEntranceIndex = 0x027E; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 68: + globalCtx->nextEntranceIndex = 0x00A0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF5; + globalCtx->fadeTransition = 2; + break; + case 69: + globalCtx->nextEntranceIndex = 0x05E8; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 70: + globalCtx->nextEntranceIndex = 0x013D; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF4; + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + break; + case 71: + gSaveContext.equips.equipment |= 0x0100; + Player_SetEquipmentData(globalCtx, player); + gSaveContext.equips.equipment |= 0x1000; + Player_SetEquipmentData(globalCtx, player); + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 2; + break; + case 72: + globalCtx->nextEntranceIndex = 0x0400; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + break; + case 73: + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->fadeTransition = 2; + break; + case 74: + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + break; + case 75: + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF4; + globalCtx->fadeTransition = 2; + break; + case 76: + globalCtx->linkAgeOnLoad = 0; + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF5; + globalCtx->fadeTransition = 2; + break; + case 77: + globalCtx->linkAgeOnLoad = 1; + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF6; + globalCtx->fadeTransition = 2; + break; + case 78: + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF7; + globalCtx->fadeTransition = 2; + break; + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + globalCtx->nextEntranceIndex = 0x0157; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 94: + globalCtx->nextEntranceIndex = 0x02AE; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + break; + case 95: + if ((gSaveContext.eventChkInf[4] & 0x100) && (gSaveContext.eventChkInf[4] & 0x200) && + (gSaveContext.eventChkInf[4] & 0x400)) { + globalCtx->nextEntranceIndex = 0x0053; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->fadeTransition = 2; + } else { + switch (gSaveContext.sceneSetupIndex) { + case 8: + globalCtx->nextEntranceIndex = 0x00FC; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 9: + globalCtx->nextEntranceIndex = 0x0147; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 10: + globalCtx->nextEntranceIndex = 0x0102; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 3; + break; + } + } + break; + case 96: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { + globalCtx->nextEntranceIndex = 0x006B; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 5; + } else { + gSaveContext.eventChkInf[12] |= 0x100; + globalCtx->nextEntranceIndex = 0x0610; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + } + break; + case 97: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + globalCtx->nextEntranceIndex = 0x006B; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 5; + } else { + globalCtx->nextEntranceIndex = 0x0580; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + } + break; + case 98: + globalCtx->nextEntranceIndex = 0x0564; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + break; + case 99: + globalCtx->nextEntranceIndex = 0x0608; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + break; + case 100: + globalCtx->nextEntranceIndex = 0x00EE; + gSaveContext.cutsceneIndex = 0xFFF8; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + break; + case 101: + globalCtx->nextEntranceIndex = 0x01F5; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 15; + break; + case 102: + globalCtx->nextEntranceIndex = 0x0590; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 103: + globalCtx->nextEntranceIndex = 0x00CD; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF3; + globalCtx->fadeTransition = 2; + break; + case 104: + switch (sTitleCsState) { + case 0: + globalCtx->nextEntranceIndex = 0x008D; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF2; + globalCtx->fadeTransition = 2; + sTitleCsState++; + break; + case 1: + globalCtx->nextEntranceIndex = 0x0147; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 2; + sTitleCsState++; + break; + case 2: + globalCtx->nextEntranceIndex = 0x00A0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF6; + globalCtx->fadeTransition = 2; + sTitleCsState = 0; + break; + } + break; + case 105: + globalCtx->nextEntranceIndex = 0x00E4; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.cutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 2; + break; + case 106: + globalCtx->nextEntranceIndex = 0x0574; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 107: + globalCtx->nextEntranceIndex = 0x0538; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 108: + globalCtx->nextEntranceIndex = 0x053C; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 109: + globalCtx->nextEntranceIndex = 0x0540; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 110: + globalCtx->nextEntranceIndex = 0x0544; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 111: + globalCtx->nextEntranceIndex = 0x0548; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 112: + globalCtx->nextEntranceIndex = 0x054C; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 113: + if (Flags_GetEventChkInf(0xBB) && Flags_GetEventChkInf(0xBC) && Flags_GetEventChkInf(0xBD) && + Flags_GetEventChkInf(0xBE) && Flags_GetEventChkInf(0xBF) && Flags_GetEventChkInf(0xAD)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTowerBarrierCs); + globalCtx->csCtx.frames = 0; + gSaveContext.cutsceneTrigger = 1; + gSaveContext.cutsceneIndex = 0xFFFF; + csCtx->state = CS_STATE_UNSKIPPABLE_INIT; + } else { + gSaveContext.cutsceneIndex = 0xFFFF; + csCtx->state = CS_STATE_UNSKIPPABLE_INIT; + } + break; + case 114: + globalCtx->nextEntranceIndex = 0x0185; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + break; + case 115: + globalCtx->nextEntranceIndex = 0x0594; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + break; + case 116: + if (gSaveContext.eventChkInf[12] & 0x100) { + globalCtx->nextEntranceIndex = 0x0580; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + } else { + globalCtx->nextEntranceIndex = 0x0610; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + } + gSaveContext.nextTransition = 3; + break; + case 117: + gSaveContext.gameMode = 3; + Audio_SetSoundBanksMute(0x6F); + globalCtx->linkAgeOnLoad = 0; + globalCtx->nextEntranceIndex = 0x00CD; + gSaveContext.cutsceneIndex = 0xFFF7; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + break; + case 118: + gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = 0x0517; + Gameplay_TriggerVoidOut(globalCtx); + gSaveContext.respawnFlag = -2; + gSaveContext.nextTransition = 2; + break; + case 119: + gSaveContext.dayTime = 0x8000; + gSaveContext.skyboxTime = 0x8000; + globalCtx->nextEntranceIndex = 0x05F0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + break; + } + } +} + +// Command 0x2D: Transition Effects +void Cutscene_Command_TransitionFX(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdBase* cmd) { + f32 temp; + + if ((csCtx->frames >= cmd->startFrame) && (csCtx->frames <= cmd->endFrame)) { + globalCtx->envCtx.fillScreen = true; + temp = Environment_LerpWeight(cmd->endFrame, cmd->startFrame, csCtx->frames); + + switch (cmd->base) { + case 1: + case 5: + globalCtx->envCtx.screenFillColor[0] = 160; + globalCtx->envCtx.screenFillColor[1] = 160; + globalCtx->envCtx.screenFillColor[2] = 160; + if (cmd->base == 1) { + globalCtx->envCtx.screenFillColor[3] = 255.0f * temp; + if ((temp == 0.0f) && (gSaveContext.entranceIndex == 0x006B)) { + Audio_PlaySoundGeneral(NA_SE_SY_WHITE_OUT_S, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if ((temp == 0.0f) && + ((gSaveContext.entranceIndex == 0x0053) || (gSaveContext.entranceIndex == 0x0138) || + (gSaveContext.entranceIndex == 0x0371))) { + Audio_PlaySoundGeneral(NA_SE_EV_WHITE_OUT, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if ((temp == 0.0f) && (globalCtx->sceneNum == SCENE_GANONTIKA)) { + func_800788CC(NA_SE_EV_WHITE_OUT); + } + } else { + globalCtx->envCtx.screenFillColor[3] = (1.0f - temp) * 255.0f; + } + break; + case 2: + case 6: + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 255; + if (cmd->base == 2) { + globalCtx->envCtx.screenFillColor[3] = 255.0f * temp; + } else { + globalCtx->envCtx.screenFillColor[3] = (1.0f - temp) * 255.0f; + } + break; + case 3: + case 7: + globalCtx->envCtx.screenFillColor[0] = 255; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 0; + if (cmd->base == 3) { + globalCtx->envCtx.screenFillColor[3] = (1.0f - temp) * 255.0f; + } else { + globalCtx->envCtx.screenFillColor[3] = 255.0f * temp; + } + break; + case 4: + case 8: + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 255; + globalCtx->envCtx.screenFillColor[2] = 0; + if (cmd->base == 4) { + globalCtx->envCtx.screenFillColor[3] = (1.0f - temp) * 255.0f; + } else { + globalCtx->envCtx.screenFillColor[3] = 255.0f * temp; + } + break; + case 9: + gSaveContext.unk_1410 = 1; + break; + case 10: + case 11: + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 0; + if (cmd->base == 10) { + globalCtx->envCtx.screenFillColor[3] = (1.0f - temp) * 255.0f; + } else { + globalCtx->envCtx.screenFillColor[3] = 255.0f * temp; + } + break; + case 12: + gSaveContext.unk_1410 = 255.0f - (155.0f * temp); + break; + case 13: + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 0; + globalCtx->envCtx.screenFillColor[3] = 255.0f - ((1.0f - temp) * 155.0f); + break; + } + } +} + +// Command 0x1 & 0x5: Camera Positions +size_t Cutscene_Command_CameraPositions(GlobalContext* globalCtx, CutsceneContext* csCtx, u8* cmd, u8 relativeToLink) { + s32 shouldContinue = true; + CsCmdBase* cmdBase = (CsCmdBase*)cmd; + size_t size; + + cmd += sizeof(CutsceneData)*2; + size = sizeof(CutsceneData)*2; + + if ((cmdBase->startFrame < csCtx->frames) && (csCtx->frames < cmdBase->endFrame) && + ((csCtx->unk_18 < cmdBase->startFrame) || (csCtx->unk_18 >= 0xF000))) { + csCtx->unk_1B = 1; + csCtx->cameraPosition = (CutsceneCameraPoint*)cmd; + if (csCtx->unk_1A != 0) { + csCtx->unk_18 = cmdBase->startFrame; + if (D_8015FCC8 != 0) { + Gameplay_CameraChangeSetting(globalCtx, csCtx->unk_14, CAM_SET_CS_0); + Gameplay_ChangeCameraStatus(globalCtx, D_8015FCC6, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, csCtx->unk_14, CAM_STAT_ACTIVE); + Camera_ResetAnim(Gameplay_GetCamera(globalCtx, csCtx->unk_14)); + Camera_SetCSParams(Gameplay_GetCamera(globalCtx, csCtx->unk_14), csCtx->cameraFocus, + csCtx->cameraPosition, GET_PLAYER(globalCtx), relativeToLink); + } + } + } + + while (shouldContinue) { + if (((CutsceneCameraPoint*)cmd)->continueFlag == CS_CMD_STOP) { + shouldContinue = false; + } + cmd += sizeof(CutsceneData) * 4; + size += sizeof(CutsceneData) * 4; + } + + return size; +} + +// Command 0x2 & 0x6: Camera Focus Points +size_t Cutscene_Command_CameraFocus(GlobalContext* globalCtx, CutsceneContext* csCtx, u8* cmd, u8 relativeToLink) { + s32 shouldContinue = true; + CsCmdBase* cmdBase = (CsCmdBase*)cmd; + size_t size; + + cmd += sizeof(CutsceneData) * 2; + size = sizeof(CutsceneData) * 2; + + if ((cmdBase->startFrame < csCtx->frames) && (csCtx->frames < cmdBase->endFrame) && + ((D_8015FCC0 < cmdBase->startFrame) || (D_8015FCC0 >= 0xF000))) { + csCtx->unk_1A = 1; + csCtx->cameraFocus = (CutsceneCameraPoint*)cmd; + if (csCtx->unk_1B != 0) { + D_8015FCC0 = cmdBase->startFrame; + if (D_8015FCC8 != 0) { + Gameplay_CameraChangeSetting(globalCtx, csCtx->unk_14, CAM_SET_CS_0); + Gameplay_ChangeCameraStatus(globalCtx, D_8015FCC6, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, csCtx->unk_14, CAM_STAT_ACTIVE); + Camera_ResetAnim(Gameplay_GetCamera(globalCtx, csCtx->unk_14)); + Camera_SetCSParams(Gameplay_GetCamera(globalCtx, csCtx->unk_14), csCtx->cameraFocus, + csCtx->cameraPosition, GET_PLAYER(globalCtx), relativeToLink); + } + } + } + + while (shouldContinue) { + if (((CutsceneCameraPoint*)cmd)->continueFlag == CS_CMD_STOP) { + shouldContinue = false; + } + cmd += sizeof(CutsceneData) * 4; + size += sizeof(CutsceneData) * 4; + } + + return size; +} + +// Command 0x7: ? (Related to camera positons) +size_t Cutscene_Command_07(GlobalContext* globalCtx, CutsceneContext* csCtx, u8* cmd, u8 unused) { + CsCmdBase* cmdBase = (CsCmdBase*)cmd; + size_t size; + Vec3f sp3C; + Vec3f sp30; + Camera* sp2C; + f32 sp28; + + cmd += sizeof(CutsceneData) * 2; + size = sizeof(CutsceneData) * 2; + + if ((cmdBase->startFrame < csCtx->frames) && (csCtx->frames < cmdBase->endFrame) && + ((D_8015FCC2 < cmdBase->startFrame) || (D_8015FCC2 >= 0xF000))) { + csCtx->unk_1B = 1; + csCtx->cameraPosition = (CutsceneCameraPoint*)cmd; + if (csCtx->unk_1A != 0) { + D_8015FCC2 = cmdBase->startFrame; + if (D_8015FCC8 != 0) { + sp2C = Gameplay_GetCamera(globalCtx, csCtx->unk_14); + sp2C->player = NULL; + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, csCtx->unk_14, CAM_STAT_ACTIVE); + Gameplay_CameraChangeSetting(globalCtx, csCtx->unk_14, CAM_SET_FREE0); + sp28 = csCtx->cameraFocus->cameraRoll * 1.40625f; + Camera_SetParam(sp2C, 64, &sp28); + sp3C.x = csCtx->cameraFocus->pos.x; + sp3C.y = csCtx->cameraFocus->pos.y; + sp3C.z = csCtx->cameraFocus->pos.z; + sp30.x = csCtx->cameraPosition->pos.x; + sp30.y = csCtx->cameraPosition->pos.y; + sp30.z = csCtx->cameraPosition->pos.z; + Gameplay_CameraSetAtEye(globalCtx, csCtx->unk_14, &sp3C, &sp30); + Gameplay_CameraSetFov(globalCtx, csCtx->unk_14, csCtx->cameraPosition->viewAngle); + } + } + } + + size += sizeof(CutsceneData) * 4; + + return size; +} + +// Command 0x8: ? (Related to camera focus points) +size_t Cutscene_Command_08(GlobalContext* globalCtx, CutsceneContext* csCtx, u8* cmd, u8 unused) { + CsCmdBase* cmdBase = (CsCmdBase*)cmd; + size_t size; + Vec3f sp3C; + Vec3f sp30; + Camera* sp2C; + f32 sp28; + + cmd += sizeof(CutsceneData) * 2; + size = sizeof(CutsceneData) * 2; + + if ((cmdBase->startFrame < csCtx->frames) && (csCtx->frames < cmdBase->endFrame) && + ((D_8015FCC4 < cmdBase->startFrame) || (D_8015FCC4 >= 0xF000))) { + csCtx->unk_1A = 1; + csCtx->cameraFocus = (CutsceneCameraPoint*)cmd; + if (csCtx->unk_1B != 0) { + D_8015FCC4 = cmdBase->startFrame; + if (D_8015FCC8 != 0) { + sp2C = Gameplay_GetCamera(globalCtx, csCtx->unk_14); + sp2C->player = NULL; + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, csCtx->unk_14, CAM_STAT_ACTIVE); + Gameplay_CameraChangeSetting(globalCtx, csCtx->unk_14, CAM_SET_FREE0); + sp3C.x = csCtx->cameraFocus->pos.x; + sp3C.y = csCtx->cameraFocus->pos.y; + sp3C.z = csCtx->cameraFocus->pos.z; + sp30.x = csCtx->cameraPosition->pos.x; + sp30.y = csCtx->cameraPosition->pos.y; + sp30.z = csCtx->cameraPosition->pos.z; + Gameplay_CameraSetAtEye(globalCtx, csCtx->unk_14, &sp3C, &sp30); + Gameplay_CameraSetFov(globalCtx, csCtx->unk_14, csCtx->cameraPosition->viewAngle); + } + } + } + + size += sizeof(CutsceneData) * 4; + + return size; +} + +// Command 0x13: Textbox +void Cutscene_Command_Textbox(GlobalContext* globalCtx, CutsceneContext* csCtx, CsCmdTextbox* cmd) { + u8 dialogState; + s16 originalCsFrames; + + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + if (cmd->type != 2) { + if (D_8011E1C0 != cmd->base) { + D_8011E1C0 = cmd->base; + if ((cmd->type == 3) && CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + Message_StartTextbox(globalCtx, cmd->textId1, NULL); + } else if ((cmd->type == 4) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + Message_StartTextbox(globalCtx, cmd->textId1, NULL); + } else { + Message_StartTextbox(globalCtx, cmd->base, NULL); + } + return; + } + } else { + if (D_8011E1C4 != cmd->base) { + D_8011E1C4 = cmd->base; + func_8010BD58(globalCtx, cmd->base); + return; + } + } + + if (csCtx->frames >= cmd->endFrame) { + originalCsFrames = csCtx->frames; + dialogState = Message_GetState(&globalCtx->msgCtx); + + if ((dialogState != TEXT_STATE_CLOSING) && (dialogState != TEXT_STATE_NONE) && + (dialogState != TEXT_STATE_SONG_DEMO_DONE) && (dialogState != TEXT_STATE_8)) { + csCtx->frames--; + + if ((dialogState == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + if (cmd->textId1 != 0xFFFF) { + Message_ContinueTextbox(globalCtx, cmd->textId1); + } else { + csCtx->frames++; + } + } else { + if (cmd->textId2 != 0xFFFF) { + Message_ContinueTextbox(globalCtx, cmd->textId2); + } else { + csCtx->frames++; + } + } + } + + if (dialogState == TEXT_STATE_9) { + if (cmd->textId1 != 0xFFFF) { + Message_ContinueTextbox(globalCtx, cmd->textId1); + } else { + csCtx->frames++; + } + } + + if (dialogState == TEXT_STATE_EVENT) { + if (Message_ShouldAdvance(globalCtx)) { + func_8010BD58(globalCtx, cmd->base); + } + } + } + + if (csCtx->frames == originalCsFrames) { + Interface_ChangeAlpha(1); + D_8011E1C0 = 0; + D_8011E1C4 = 0; + } + } + } +} + +void Cutscene_ProcessCommands(GlobalContext* globalCtx, CutsceneContext* csCtx, u8* cutscenePtr) { + s16 i; + s32 totalEntries; + s32 cmdType; + s32 cmdEntries; + CsCmdBase* cmd; + s32 cutsceneEndFrame; + s16 j; + + if (ResourceMgr_OTRSigCheck(cutscenePtr)) + cutscenePtr = ResourceMgr_LoadCSByName(cutscenePtr); + + memcpy(&totalEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + memcpy(&cutsceneEndFrame, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + + if ((cutsceneEndFrame < csCtx->frames) && (csCtx->state != CS_STATE_UNSKIPPABLE_EXEC)) { + csCtx->state = CS_STATE_UNSKIPPABLE_INIT; + return; + } + + if (CVar_GetS32("gDebugEnabled", 0) && CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_DRIGHT)) { + csCtx->state = CS_STATE_UNSKIPPABLE_INIT; + return; + } + + for (i = 0; i < totalEntries; i++) { + memcpy(&cmdType, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + + if (cmdType == -1) { + return; + } + + //printf("CmdType: %04X\n", cmdType); + + switch (cmdType) { + case CS_CMD_MISC: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + func_80064824(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_LIGHTING: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + Cutscene_Command_SetLighting(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_PLAYBGM: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + Cutscene_Command_PlayBGM(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_STOPBGM: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + Cutscene_Command_StopBGM(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_FADEBGM: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + Cutscene_Command_FadeBGM(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_09: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + Cutscene_Command_09(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 3; + } + break; + case CS_CMD_SETTIME: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + func_80065134(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 3; + } + break; + case CS_CMD_SET_PLAYER_ACTION: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->linkAction = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_1: + case 17: + case 18: + case 23: + case 34: + case 39: + case 46: + case 76: + case 85: + case 93: + case 105: + case 107: + case 110: + case 119: + case 123: + case 138: + case 139: + case 144: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[0] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_2: + case 16: + case 24: + case 35: + case 40: + case 48: + case 64: + case 68: + case 70: + case 78: + case 80: + case 94: + case 116: + case 118: + case 120: + case 125: + case 131: + case 141: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[1] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_3: + case 36: + case 41: + case 50: + case 67: + case 69: + case 72: + case 74: + case 81: + case 106: + case 117: + case 121: + case 126: + case 132: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[2] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_4: + case 37: + case 42: + case 51: + case 53: + case 63: + case 65: + case 66: + case 75: + case 82: + case 108: + case 127: + case 133: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[3] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_5: + case 38: + case 43: + case 47: + case 54: + case 79: + case 83: + case 128: + case 135: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[4] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_6: + case 55: + case 77: + case 84: + case 90: + case 129: + case 136: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[5] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_7: + case 52: + case 57: + case 58: + case 88: + case 115: + case 130: + case 137: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[6] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_8: + case 60: + case 89: + case 111: + case 114: + case 134: + case 142: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[7] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_9: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[8] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_SET_ACTOR_ACTION_10: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if ((cmd->startFrame < csCtx->frames) && (csCtx->frames <= cmd->endFrame)) { + csCtx->npcActions[9] = (void*)cutscenePtr; + } + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + case CS_CMD_CAM_EYE: + cutscenePtr += Cutscene_Command_CameraPositions(globalCtx, csCtx, (void*)cutscenePtr, 0); + break; + case CS_CMD_CAM_EYE_REL_TO_PLAYER: + cutscenePtr += Cutscene_Command_CameraPositions(globalCtx, csCtx, (void*)cutscenePtr, 1); + break; + case CS_CMD_CAM_AT: + cutscenePtr += Cutscene_Command_CameraFocus(globalCtx, csCtx, (void*)cutscenePtr, 0); + break; + case CS_CMD_CAM_AT_REL_TO_PLAYER: + cutscenePtr += Cutscene_Command_CameraFocus(globalCtx, csCtx, (void*)cutscenePtr, 1); + break; + case CS_CMD_07: + cutscenePtr += Cutscene_Command_07(globalCtx, csCtx, (void*)cutscenePtr, 0); + break; + case CS_CMD_08: + cutscenePtr += Cutscene_Command_08(globalCtx, csCtx, (void*)cutscenePtr, 0); + break; + case CS_CMD_TERMINATOR: + cutscenePtr += 4; + Cutscene_Command_Terminator(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += 8; + break; + case CS_CMD_TEXTBOX: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += 4; + for (j = 0; j < cmdEntries; j++) { + cmd = (CsCmdBase*)cutscenePtr; + if (cmd->base != 0xFFFF) { + Cutscene_Command_Textbox(globalCtx, csCtx, (void*)cutscenePtr); + } + cutscenePtr += sizeof(CutsceneData) * 3; + } + break; + case CS_CMD_SCENE_TRANS_FX: + cutscenePtr += sizeof(CutsceneData); + Cutscene_Command_TransitionFX(globalCtx, csCtx, (void*)cutscenePtr); + cutscenePtr += sizeof(CutsceneData) * 2; + break; + default: + memcpy(&cmdEntries, cutscenePtr, sizeof(CutsceneData)); + cutscenePtr += sizeof(CutsceneData); + for (j = 0; j < cmdEntries; j++) { + cutscenePtr += sizeof(CutsceneData) * 12; + } + break; + } + } +} + +void func_80068C3C(GlobalContext* globalCtx, CutsceneContext* csCtx) { + Gfx* displayList; + Gfx* prevDisplayList; + + if (0) {} // Necessary to match + + if (gSaveContext.cutsceneIndex >= 0xFFF0) { + if (0) {} // Also necessary to match + + if (BREG(0) != 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo.c", 4101); + + prevDisplayList = POLY_OPA_DISP; + displayList = Graph_GfxPlusOne(POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, displayList); + Cutscene_DrawDebugInfo(globalCtx, &displayList, csCtx); + gSPEndDisplayList(displayList++); + Graph_BranchDlist(prevDisplayList, displayList); + POLY_OPA_DISP = displayList; + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo.c", 4108); + } + + csCtx->frames++; + if (dREG(95) != 0) { + Cutscene_ProcessCommands(globalCtx, csCtx, D_8012D1F0); + } else { + Cutscene_ProcessCommands(globalCtx, csCtx, globalCtx->csCtx.segment); + } + } +} + +void func_80068D84(GlobalContext* globalCtx, CutsceneContext* csCtx) { + if (func_8006472C(globalCtx, csCtx, 0.0f)) { + Audio_SetCutsceneFlag(0); + csCtx->state = CS_STATE_IDLE; + } +} + +void func_80068DC0(GlobalContext* globalCtx, CutsceneContext* csCtx) { + s16 i; + + if (func_8006472C(globalCtx, csCtx, 0.0f)) { + csCtx->linkAction = NULL; + + for (i = 0; i < 10; i++) { + csCtx->npcActions[i] = NULL; + } + + osSyncPrintf("\n\n\n\n\nやっぱりここかいな"); // "Right here, huh" + gSaveContext.cutsceneIndex = 0; + gSaveContext.gameMode = 0; + + if (D_8015FCC8 != 0) { + switch (gSaveContext.entranceIndex) { + case 0x028A: + case 0x028E: + case 0x0292: + case 0x0476: + Gameplay_CopyCamera(globalCtx, D_8015FCC6, csCtx->unk_14); + } + + Gameplay_ChangeCameraStatus(globalCtx, D_8015FCC6, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, csCtx->unk_14); + func_8005B1A4(globalCtx->cameraPtrs[D_8015FCC6]); + } + + Audio_SetCutsceneFlag(0); + csCtx->state = CS_STATE_IDLE; + } +} + +void func_80068ECC(GlobalContext* globalCtx, CutsceneContext* csCtx) { + u8 i; + + if ((gSaveContext.cutsceneTrigger != 0) && (csCtx->state == CS_STATE_IDLE) && !Player_InCsMode(globalCtx)) { + gSaveContext.cutsceneIndex = 0xFFFD; + } + + if ((gSaveContext.cutsceneIndex >= 0xFFF0) && (csCtx->state == CS_STATE_IDLE)) { + Flags_UnsetEnv(globalCtx, 0); + + D_8011E1C0 = 0; + D_8011E1C4 = 0; + csCtx->unk_12 = 0; + csCtx->linkAction = NULL; + + for (i = 0; i < 10; i++) { + csCtx->npcActions[i] = NULL; + } + + csCtx->state++; + + if (csCtx->state == CS_STATE_SKIPPABLE_INIT) { + Audio_SetCutsceneFlag(1); + + csCtx->frames = 0xFFFF; + csCtx->unk_18 = 0xFFFF; + D_8015FCC0 = 0xFFFF; + D_8015FCC2 = 0xFFFF; + D_8015FCC4 = 0xFFFF; + csCtx->unk_1A = 0; + csCtx->unk_1B = 0; + D_8015FCC6 = globalCtx->activeCamera; + + if (D_8015FCC8 != 0) { + csCtx->unk_14 = Gameplay_CreateSubCamera(globalCtx); + } + + if (gSaveContext.cutsceneTrigger == 0) { + Interface_ChangeAlpha(1); + ShrinkWindow_SetVal(0x20); + ShrinkWindow_SetCurrentVal(0x20); + csCtx->state++; + } + + func_80068C3C(globalCtx, csCtx); + } + + gSaveContext.cutsceneTrigger = 0; + } +} + +void func_80069048(GlobalContext* globalCtx) { + s16 i; + + D_8015FCCC = 0; + for (i = 0; i < 20; i++) { + ; // Empty Loop + } + D_8015FCE4 = 0; +} + +void func_8006907C(GlobalContext* globalCtx) { + if (D_8015FCCC != 0) { + D_8015FCCC = 0; + } +} + +void Cutscene_HandleEntranceTriggers(GlobalContext* globalCtx) { + EntranceCutscene* entranceCutscene; + u8 requiredAge; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sEntranceCutsceneTable); i++) { + entranceCutscene = &sEntranceCutsceneTable[i]; + + requiredAge = entranceCutscene->ageRestriction; + if (requiredAge == 2) { + requiredAge = gSaveContext.linkAge; + } + + if ((gSaveContext.entranceIndex == entranceCutscene->entrance) && + (!Flags_GetEventChkInf(entranceCutscene->flag) || (entranceCutscene->flag == 0x18)) && + (gSaveContext.cutsceneIndex < 0xFFF0) && ((u8)gSaveContext.linkAge == requiredAge) && + (gSaveContext.respawnFlag <= 0)) { + Flags_SetEventChkInf(entranceCutscene->flag); + Cutscene_SetSegment(globalCtx, entranceCutscene->segAddr); + gSaveContext.cutsceneTrigger = 2; + gSaveContext.showTitleCard = false; + break; + } + } +} + +void Cutscene_HandleConditionalTriggers(GlobalContext* globalCtx) { + osSyncPrintf("\ngame_info.mode=[%d] restart_flag", ((void)0, gSaveContext.respawnFlag)); + + if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) { + if ((gSaveContext.entranceIndex == 0x01E1) && !Flags_GetEventChkInf(0xAC)) { + Flags_SetEventChkInf(0xAC); + gSaveContext.entranceIndex = 0x0123; + gSaveContext.cutsceneIndex = 0xFFF0; + } else if ((gSaveContext.entranceIndex == 0x00DB) && LINK_IS_ADULT && (gSaveContext.eventChkInf[4] & 0x0100) && + (gSaveContext.eventChkInf[4] & 0x0200) && (gSaveContext.eventChkInf[4] & 0x0400) && + !Flags_GetEventChkInf(0xAA)) { + Flags_SetEventChkInf(0xAA); + gSaveContext.cutsceneIndex = 0xFFF0; + } else if ((gSaveContext.entranceIndex == 0x05E0) && !Flags_GetEventChkInf(0xC1)) { + Flags_SetEventChkInf(0xC1); + Item_Give(globalCtx, ITEM_OCARINA_FAIRY); + gSaveContext.entranceIndex = 0x011E; + gSaveContext.cutsceneIndex = 0xFFF0; + } else if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) && + LINK_IS_ADULT && !Flags_GetEventChkInf(0xC4) && + (gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TOKINOMA)) { + Flags_SetEventChkInf(0xC4); + gSaveContext.entranceIndex = 0x0053; + gSaveContext.cutsceneIndex = 0xFFF8; + } else if (!Flags_GetEventChkInf(0xC7) && + (gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_GANON_DEMO)) { + Flags_SetEventChkInf(0xC7); + gSaveContext.entranceIndex = 0x0517; + gSaveContext.cutsceneIndex = 0xFFF0; + } + } +} + +void Cutscene_SetSegment(GlobalContext* globalCtx, void* segment) { + if (SEGMENT_NUMBER(segment) != 0) + { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(segment); + } else { + globalCtx->csCtx.segment = segment; + } +} diff --git a/soh/src/code/z_draw.c b/soh/src/code/z_draw.c new file mode 100644 index 000000000..20ff39b0b --- /dev/null +++ b/soh/src/code/z_draw.c @@ -0,0 +1,881 @@ +#include "global.h" +#include "objects/object_gi_key/object_gi_key.h" +#include "objects/object_gi_jewel/object_gi_jewel.h" +#include "objects/object_gi_melody/object_gi_melody.h" +#include "objects/object_gi_heart/object_gi_heart.h" +#include "objects/object_gi_compass/object_gi_compass.h" +#include "objects/object_gi_bosskey/object_gi_bosskey.h" +#include "objects/object_gi_medal/object_gi_medal.h" +#include "objects/object_gi_nuts/object_gi_nuts.h" +#include "objects/object_gi_hearts/object_gi_hearts.h" +#include "objects/object_gi_arrowcase/object_gi_arrowcase.h" +#include "objects/object_gi_bombpouch/object_gi_bombpouch.h" +#include "objects/object_gi_bottle/object_gi_bottle.h" +#include "objects/object_gi_stick/object_gi_stick.h" +#include "objects/object_gi_map/object_gi_map.h" +#include "objects/object_gi_shield_1/object_gi_shield_1.h" +#include "objects/object_gi_magicpot/object_gi_magicpot.h" +#include "objects/object_gi_bomb_1/object_gi_bomb_1.h" +#include "objects/object_gi_purse/object_gi_purse.h" +#include "objects/object_gi_gerudo/object_gi_gerudo.h" +#include "objects/object_gi_arrow/object_gi_arrow.h" +#include "objects/object_gi_bomb_2/object_gi_bomb_2.h" +#include "objects/object_gi_egg/object_gi_egg.h" +#include "objects/object_gi_scale/object_gi_scale.h" +#include "objects/object_gi_shield_2/object_gi_shield_2.h" +#include "objects/object_gi_hookshot/object_gi_hookshot.h" +#include "objects/object_gi_ocarina/object_gi_ocarina.h" +#include "objects/object_gi_milk/object_gi_milk.h" +#include "objects/object_gi_pachinko/object_gi_pachinko.h" +#include "objects/object_gi_boomerang/object_gi_boomerang.h" +#include "objects/object_gi_bow/object_gi_bow.h" +#include "objects/object_gi_glasses/object_gi_glasses.h" +#include "objects/object_gi_liquid/object_gi_liquid.h" +#include "objects/object_gi_shield_3/object_gi_shield_3.h" +#include "objects/object_gi_letter/object_gi_letter.h" +#include "objects/object_gi_clothes/object_gi_clothes.h" +#include "objects/object_gi_bean/object_gi_bean.h" +#include "objects/object_gi_fish/object_gi_fish.h" +#include "objects/object_gi_saw/object_gi_saw.h" +#include "objects/object_gi_hammer/object_gi_hammer.h" +#include "objects/object_gi_grass/object_gi_grass.h" +#include "objects/object_gi_longsword/object_gi_longsword.h" +#include "objects/object_gi_niwatori/object_gi_niwatori.h" +#include "objects/object_gi_bottle_letter/object_gi_bottle_letter.h" +#include "objects/object_gi_ocarina_0/object_gi_ocarina_0.h" +#include "objects/object_gi_boots_2/object_gi_boots_2.h" +#include "objects/object_gi_seed/object_gi_seed.h" +#include "objects/object_gi_gloves/object_gi_gloves.h" +#include "objects/object_gi_coin/object_gi_coin.h" +#include "objects/object_gi_ki_tan_mask/object_gi_ki_tan_mask.h" +#include "objects/object_gi_redead_mask/object_gi_redead_mask.h" +#include "objects/object_gi_skj_mask/object_gi_skj_mask.h" +#include "objects/object_gi_rabit_mask/object_gi_rabit_mask.h" +#include "objects/object_gi_truth_mask/object_gi_truth_mask.h" +#include "objects/object_gi_eye_lotion/object_gi_eye_lotion.h" +#include "objects/object_gi_powder/object_gi_powder.h" +#include "objects/object_gi_mushroom/object_gi_mushroom.h" +#include "objects/object_gi_ticketstone/object_gi_ticketstone.h" +#include "objects/object_gi_brokensword/object_gi_brokensword.h" +#include "objects/object_gi_prescription/object_gi_prescription.h" +#include "objects/object_gi_bracelet/object_gi_bracelet.h" +#include "objects/object_gi_soldout/object_gi_soldout.h" +#include "objects/object_gi_frog/object_gi_frog.h" +#include "objects/object_gi_golonmask/object_gi_golonmask.h" +#include "objects/object_gi_zoramask/object_gi_zoramask.h" +#include "objects/object_gi_gerudomask/object_gi_gerudomask.h" +#include "objects/object_gi_hoverboots/object_gi_hoverboots.h" +#include "objects/object_gi_m_arrow/object_gi_m_arrow.h" +#include "objects/object_gi_sutaru/object_gi_sutaru.h" +#include "objects/object_gi_goddess/object_gi_goddess.h" +#include "objects/object_gi_fire/object_gi_fire.h" +#include "objects/object_gi_insect/object_gi_insect.h" +#include "objects/object_gi_butterfly/object_gi_butterfly.h" +#include "objects/object_gi_ghost/object_gi_ghost.h" +#include "objects/object_gi_soul/object_gi_soul.h" +#include "objects/object_gi_dekupouch/object_gi_dekupouch.h" +#include "objects/object_gi_rupy/object_gi_rupy.h" +#include "objects/object_gi_sword_1/object_gi_sword_1.h" +#include "objects/object_st/object_st.h" + +// "Get Item" Model Draw Functions +void GetItem_DrawMaskOrBombchu(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawSoldOut(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawBlueFire(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawPoes(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawFairy(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawMirrorShield(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawSkullToken(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawEggOrMedallion(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawCompass(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawPotion(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawGoronSword(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawDekuNuts(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawRecoveryHeart(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawFish(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawOpa0(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawOpa0Xlu1(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawXlu01(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawOpa10Xlu2(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawMagicArrow(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawMagicSpell(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawOpa1023(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawOpa10Xlu32(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawSmallRupee(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawScale(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawBulletBag(GlobalContext* globalCtx, s16 drawId); +void GetItem_DrawWallet(GlobalContext* globalCtx, s16 drawId); + +typedef struct { + /* 0x00 */ void (*drawFunc)(GlobalContext*, s16); + /* 0x04 */ Gfx* dlists[8]; +} DrawItemTableEntry; // size = 0x24 + +DrawItemTableEntry sDrawItemTable[] = { + // bottle, OBJECT_GI_BOTTLE + { GetItem_DrawOpa0Xlu1, { gGiBottleStopperDL, gGiBottleDL } }, + // small key, OBJECT_GI_KEY + { GetItem_DrawOpa0, { gGiSmallKeyDL } }, + // minuet of forest, OBJECT_GI_MELODY + { GetItem_DrawXlu01, { gGiMinuetColorDL, gGiSongNoteDL } }, + // bolero of fire, OBJECT_GI_MELODY + { GetItem_DrawXlu01, { gGiBoleroColorDL, gGiSongNoteDL } }, + // serenade of water, OBJECT_GI_MELODY + { GetItem_DrawXlu01, { gGiSerenadeColorDL, gGiSongNoteDL } }, + // requiem of spirit, OBJECT_GI_MELODY + { GetItem_DrawXlu01, { gGiRequiemColorDL, gGiSongNoteDL } }, + // nocturne of shadow, OBJECT_GI_MELODY + { GetItem_DrawXlu01, { gGiNocturneColorDL, gGiSongNoteDL } }, + // prelude of light, OBJECT_GI_MELODY + { GetItem_DrawXlu01, { gGiPreludeColorDL, gGiSongNoteDL } }, + // recovery heart, OBJECT_GI_HEART + { GetItem_DrawRecoveryHeart, { gGiRecoveryHeartDL } }, + // boss key, OBJECT_GI_BOSSKEY + { GetItem_DrawOpa0Xlu1, { gGiBossKeyDL, gGiBossKeyGemDL } }, + // compass, OBJECT_GI_COMPASS + { GetItem_DrawCompass, { gGiCompassDL, gGiCompassGlassDL } }, + // forest medallion, OBJECT_GI_MEDAL + { GetItem_DrawEggOrMedallion, { gGiForestMedallionFaceDL, gGiMedallionDL } }, + // fire medallion, OBJECT_GI_MEDAL + { GetItem_DrawEggOrMedallion, { gGiFireMedallionFaceDL, gGiMedallionDL } }, + // water medallion, OBJECT_GI_MEDAL + { GetItem_DrawEggOrMedallion, { gGiWaterMedallionFaceDL, gGiMedallionDL } }, + // spirit medallion, OBJECT_GI_MEDAL + { GetItem_DrawEggOrMedallion, { gGiSpiritMedallionFaceDL, gGiMedallionDL } }, + // shadow medallion, OBJECT_GI_MEDAL + { GetItem_DrawEggOrMedallion, { gGiShadowMedallionFaceDL, gGiMedallionDL } }, + // light medallion, OBJECT_GI_MEDAL + { GetItem_DrawEggOrMedallion, { gGiLightMedallionFaceDL, gGiMedallionDL } }, + // deku nuts, OBJECT_GI_NUTS + { GetItem_DrawDekuNuts, { gGiNutDL } }, + // heart container, OBJECT_GI_HEARTS + { GetItem_DrawXlu01, { gGiHeartBorderDL, gGiHeartContainerDL } }, + // heart piece, OBJECT_GI_HEARTS + { GetItem_DrawXlu01, { gGiHeartBorderDL, gGiHeartPieceDL } }, + // quiver 30, OBJECT_GI_ARROWCASE + { GetItem_DrawOpa1023, { gGiQuiverInnerDL, gGiQuiver30InnerColorDL, gGiQuiver30OuterColorDL, gGiQuiverOuterDL } }, + // quiver 40, OBJECT_GI_ARROWCASE + { GetItem_DrawOpa1023, { gGiQuiverInnerDL, gGiQuiver40InnerColorDL, gGiQuiver40OuterColorDL, gGiQuiverOuterDL } }, + // quiver 50, OBJECT_GI_ARROWCASE + { GetItem_DrawOpa1023, { gGiQuiverInnerDL, gGiQuiver50InnerColorDL, gGiQuiver50OuterColorDL, gGiQuiverOuterDL } }, + // bomb bag 20, OBJECT_GI_BOMBPOUCH + { GetItem_DrawOpa1023, { gGiBombBagDL, gGiBombBag20BagColorDL, gGiBombBag20RingColorDL, gGiBombBagRingDL } }, + // bomb bag 30, OBJECT_GI_BOMBPOUCH + { GetItem_DrawOpa1023, { gGiBombBagDL, gGiBombBag30BagColorDL, gGiBombBag30RingColorDL, gGiBombBagRingDL } }, + // bomb bag 40, OBJECT_GI_BOMBPOUCH + { GetItem_DrawOpa1023, { gGiBombBagDL, gGiBombBag40BagColorDL, gGiBombBag40RingColorDL, gGiBombBagRingDL } }, + // stick, OBJECT_GI_STICK + { GetItem_DrawOpa0, { gGiStickDL } }, + // dungeon map, OBJECT_GI_MAP + { GetItem_DrawOpa0, { gGiDungeonMapDL } }, + // deku shield, OBJECT_GI_SHIELD_1 + { GetItem_DrawOpa0, { gGiDekuShieldDL } }, + // small magic jar, OBJECT_GI_MAGICPOT + { GetItem_DrawOpa0, { gGiMagicJarSmallDL } }, + // large magic jar, OBJECT_GI_MAGICPOT + { GetItem_DrawOpa0, { gGiMagicJarLargeDL } }, + // bombs, OBJECT_GI_BOMB_1 + { GetItem_DrawOpa0, { gGiBombDL } }, + // stone of agony, OBJECT_GI_MAP + { GetItem_DrawOpa0, { gGiStoneOfAgonyDL } }, + // adult's wallet, OBJECT_GI_PURSE + { GetItem_DrawWallet, + { gGiWalletDL, gGiAdultWalletColorDL, gGiAdultWalletRupeeOuterColorDL, gGiWalletRupeeOuterDL, + gGiAdultWalletStringColorDL, gGiWalletStringDL, gGiAdultWalletRupeeInnerColorDL, gGiWalletRupeeInnerDL } }, + // giant's wallet, OBJECT_GI_PURSE + { GetItem_DrawWallet, + { gGiWalletDL, gGiGiantsWalletColorDL, gGiGiantsWalletRupeeOuterColorDL, gGiWalletRupeeOuterDL, + gGiGiantsWalletStringColorDL, gGiWalletStringDL, gGiGiantsWalletRupeeInnerColorDL, gGiWalletRupeeInnerDL } }, + // gerudo card, OBJECT_GI_GERUDO + { GetItem_DrawOpa0, { gGiGerudoCardDL } }, + // arrows (small), OBJECT_GI_ARROW + { GetItem_DrawOpa0, { gGiArrowSmallDL } }, + // arrows (medium), OBJECT_GI_ARROW + { GetItem_DrawOpa0, { gGiArrowMediumDL } }, + // arrows (large), OBJECT_GI_ARROW + { GetItem_DrawOpa0, { gGiArrowLargeDL } }, + // bombchus, OBJECT_GI_BOMB_2 + { GetItem_DrawMaskOrBombchu, { gGiBombchuDL } }, + // egg, OBJECT_GI_EGG + { GetItem_DrawEggOrMedallion, { gGiEggMaterialDL, gGiEggDL } }, + // silver scale, OBJECT_GI_SCALE + { GetItem_DrawScale, { gGiScaleWaterDL, gGiSilverScaleWaterColorDL, gGiSilverScaleColorDL, gGiScaleDL } }, + // gold scale, OBJECT_GI_SCALE + { GetItem_DrawScale, { gGiScaleWaterDL, gGiGoldenScaleWaterColorDL, gGiGoldenScaleColorDL, gGiScaleDL } }, + // hylian shield, OBJECT_GI_SHIELD_2 + { GetItem_DrawOpa0, { gGiHylianShieldDL } }, + // hookshot, OBJECT_GI_HOOKSHOT + { GetItem_DrawOpa0, { gGiHookshotDL } }, + // longshot, OBJECT_GI_HOOKSHOT + { GetItem_DrawOpa0, { gGiLongshotDL } }, + // ocarina of time, OBJECT_GI_OCARINA + { GetItem_DrawOpa0Xlu1, { gGiOcarinaTimeDL, gGiOcarinaTimeHolesDL } }, + // milk, OBJECT_GI_MILK + { GetItem_DrawOpa0Xlu1, { gGiMilkBottleContentsDL, gGiMilkBottleDL } }, + // keaton mask, OBJECT_GI_KI_TAN_MASK + { GetItem_DrawOpa0Xlu1, { gGiKeatonMaskDL, gGiKeatonMaskEyesDL } }, + // spooky mask, OBJECT_GI_REDEAD_MASK + { GetItem_DrawOpa0, { gGiSpookyMaskDL } }, + // slingshot, OBJECT_GI_PACHINKO + { GetItem_DrawOpa0, { gGiSlingshotDL } }, + // boomerang, OBJECT_GI_BOOMERANG + { GetItem_DrawOpa0, { gGiBoomerangDL } }, + // bow, OBJECT_GI_BOW + { GetItem_DrawOpa0, { gGiBowDL } }, + // lens, OBJECT_GI_GLASSES + { GetItem_DrawOpa0Xlu1, { gGiLensDL, gGiLensGlassDL } }, + // green potion, OBJECT_GI_LIQUID + { GetItem_DrawPotion, + { gGiPotionPotDL, gGiGreenPotColorDL, gGiGreenLiquidColorDL, gGiPotionLiquidDL, gGiGreenPatternColorDL, + gGiPotionPatternDL } }, + // red potion, OBJECT_GI_LIQUID + { GetItem_DrawPotion, + { gGiPotionPotDL, gGiRedPotColorDL, gGiRedLiquidColorDL, gGiPotionLiquidDL, gGiRedPatternColorDL, + gGiPotionPatternDL } }, + // blue potion, OBJECT_GI_LIQUID + { GetItem_DrawPotion, + { gGiPotionPotDL, gGiBluePotColorDL, gGiBlueLiquidColorDL, gGiPotionLiquidDL, gGiBluePatternColorDL, + gGiPotionPatternDL } }, + // mirror shield, OBJECT_GI_SHIELD_3 + { GetItem_DrawMirrorShield, { gGiMirrorShieldDL, gGiMirrorShieldSymbolDL } }, + // zelda's letter, OBJECT_GI_LETTER + { GetItem_DrawOpa0Xlu1, { gGiLetterDL, gGiLetterWritingDL } }, + // goron tunic, OBJECT_GI_CLOTHES + { GetItem_DrawOpa1023, { gGiTunicCollarDL, gGiGoronCollarColorDL, gGiGoronTunicColorDL, gGiTunicDL } }, + // zora tunic, OBJECT_GI_CLOTHES + { GetItem_DrawOpa1023, { gGiTunicCollarDL, gGiZoraCollarColorDL, gGiZoraTunicColorDL, gGiTunicDL } }, + // beans, OBJECT_GI_BEAN + { GetItem_DrawOpa0, { gGiBeanDL } }, + // fish, OBJECT_GI_FISH + { GetItem_DrawFish, { gGiFishDL } }, + // saw, OBJECT_GI_SAW + { GetItem_DrawOpa0, { gGiSawDL } }, + // hammer, OBJECT_GI_HAMMER + { GetItem_DrawOpa0, { gGiHammerDL } }, + // grass, OBJECT_GI_GRASS + { GetItem_DrawOpa0, { gGiGrassDL } }, + // biggorons sword, OBJECT_GI_LONGSWORD + { GetItem_DrawGoronSword, { gGiBiggoronSwordDL } }, + // chicken, OBJECT_GI_NIWATORI + { GetItem_DrawOpa10Xlu2, { gGiChickenDL, gGiChickenColorDL, gGiChickenEyesDL } }, + // ruto's letter, OBJECT_GI_BOTTLE_LETTER + { GetItem_DrawOpa0Xlu1, { gGiLetterBottleContentsDL, gGiLetterBottleDL } }, + // fairy ocarina, OBJECT_GI_OCARINA_0 + { GetItem_DrawOpa0Xlu1, { gGiOcarinaFairyDL, gGiOcarinaFairyHolesDL } }, + // iron boots, OBJECT_GI_BOOTS_2 + { GetItem_DrawOpa0Xlu1, { gGiIronBootsDL, gGiIronBootsRivetsDL } }, + // seeds, OBJECT_GI_SEED + { GetItem_DrawOpa0, { gGiSeedDL } }, + // silver gauntlets, OBJECT_GI_GLOVES + { GetItem_DrawOpa10Xlu32, + { gGiGauntletsDL, gGiSilverGauntletsColorDL, gGiGauntletsPlateDL, gGiSilverGauntletsPlateColorDL } }, + // golden gauntlets, OBJECT_GI_GLOVES + { GetItem_DrawOpa10Xlu32, + { gGiGauntletsDL, gGiGoldenGauntletsColorDL, gGiGauntletsPlateDL, gGiGoldenGauntletsPlateColorDL } }, + // yellow n coin, OBJECT_GI_COIN + { GetItem_DrawOpa10Xlu2, { gGiCoinDL, gGiYellowCoinColorDL, gGiNDL } }, + // red n coin, OBJECT_GI_COIN + { GetItem_DrawOpa10Xlu2, { gGiCoinDL, gGiRedCoinColorDL, gGiNDL } }, + // green n coin, OBJECT_GI_COIN + { GetItem_DrawOpa10Xlu2, { gGiCoinDL, gGiGreenCoinColorDL, gGiNDL } }, + // blue n coin, OBJECT_GI_COIN + { GetItem_DrawOpa10Xlu2, { gGiCoinDL, gGiBlueCoinColorDL, gGiNDL } }, + // skull mask, OBJECT_GI_SKJ_MASK + { GetItem_DrawOpa0, { gGiSkullMaskDL } }, + // bunny hood OBJECT_GI_RABIT_MASK + { GetItem_DrawOpa0Xlu1, { gGiBunnyHoodDL, gGiBunnyHoodEyesDL } }, + // mask of truth, OBJECT_GI_TRUTH_MASK + { GetItem_DrawOpa0Xlu1, { gGiMaskOfTruthDL, gGiMaskOfTruthAccentsDL } }, + // eyedrops, OBJECT_GI_EYE_LOTION + { GetItem_DrawOpa0Xlu1, { gGiEyeDropsCapDL, gGiEyeDropsBottleDL } }, + // odd potion, OBJECT_GI_POWDER + { GetItem_DrawOpa0, { gGiOddPotionDL } }, + // odd mushroom, OBJECT_GI_MUSHROOM + { GetItem_DrawOpa0, { gGiOddMushroomDL } }, + // claim check, OBJECT_GI_TICKETSTONE + { GetItem_DrawOpa0Xlu1, { gGiClaimCheckDL, gGiClaimCheckWritingDL } }, + // broken goron's sword, OBJECT_GI_BROKENSWORD + { GetItem_DrawGoronSword, { gGiBrokenGoronSwordDL } }, + // prescription, OBJECT_GI_PRESCRIPTION + { GetItem_DrawOpa0Xlu1, { gGiPrescriptionDL, gGiPrescriptionWritingDL } }, + // goron bracelet, OBJECT_GI_BRACELET + { GetItem_DrawOpa0, { gGiGoronBraceletDL } }, + // sold out, OBJECT_GI_SOLDOUT + { GetItem_DrawSoldOut, { gGiSoldOutDL } }, + // frog, OBJECT_GI_FROG + { GetItem_DrawOpa0Xlu1, { gGiFrogDL, gGiFrogEyesDL } }, + // goron mask, OBJECT_GI_GOLONMASK + { GetItem_DrawMaskOrBombchu, { gGiGoronMaskDL } }, + // zora mask, OBJECT_GI_ZORAMASK + { GetItem_DrawMaskOrBombchu, { gGiZoraMaskDL } }, + // gerudo mask, OBJECT_GI_GERUDOMASK + { GetItem_DrawMaskOrBombchu, { gGiGerudoMaskDL } }, + // cojiro, OBJECT_GI_NIWATORI + { GetItem_DrawOpa10Xlu2, { gGiChickenDL, gGiCojiroColorDL, gGiChickenEyesDL } }, + // hover boots, OBJECT_GI_HOVERBOOTS + { GetItem_DrawOpa0, { gGiHoverBootsDL } }, + // fire arrows, OBJECT_GI_M_ARROW + { GetItem_DrawMagicArrow, { gGiMagicArrowDL, gGiFireArrowColorDL, gGiArrowMagicDL } }, + // ice arrows, OBJECT_GI_M_ARROW + { GetItem_DrawMagicArrow, { gGiMagicArrowDL, gGiIceArrowColorDL, gGiArrowMagicDL } }, + // light arrows, OBJECT_GI_M_ARROW + { GetItem_DrawMagicArrow, { gGiMagicArrowDL, gGiLightArrowColorDL, gGiArrowMagicDL } }, + // skulltula token, OBJECT_GI_SUTARU + { GetItem_DrawSkullToken, { gGiSkulltulaTokenDL, gGiSkulltulaTokenFlameDL } }, + // din's fire, OBJECT_GI_GODDESS + { GetItem_DrawMagicSpell, { gGiMagicSpellDiamondDL, gGiDinsFireColorDL, gGiMagicSpellOrbDL } }, + // farore's wind, OBJECT_GI_GODDESS + { GetItem_DrawMagicSpell, { gGiMagicSpellDiamondDL, gGiFaroresWindColorDL, gGiMagicSpellOrbDL } }, + // nayru's Love, OBJECT_GI_GODDESS + { GetItem_DrawMagicSpell, { gGiMagicSpellDiamondDL, gGiNayrusLoveColorDL, gGiMagicSpellOrbDL } }, + // blue fire, OBJECT_GI_FIRE + { GetItem_DrawBlueFire, { gGiBlueFireChamberstickDL, gGiBlueFireFlameDL } }, + // bugs, OBJECT_GI_INSECT + { GetItem_DrawOpa0Xlu1, { gGiBugsContainerDL, gGiBugsGlassDL } }, + // butterfly, OBJECT_GI_BUTTERFLY + { GetItem_DrawOpa0Xlu1, { gGiButterflyContainerDL, gGiButterflyGlassDL } }, + // poe, OBJECT_GI_GHOST + { GetItem_DrawPoes, + { gGiGhostContainerLidDL, gGiGhostContainerGlassDL, gGiGhostContainerContentsDL, gGiPoeColorDL } }, + // fairy, OBJECT_GI_SOUL + { GetItem_DrawFairy, { gGiFairyContainerBaseCapDL, gGiFairyContainerGlassDL, gGiFairyContainerContentsDL } }, + // bullet bag 40, OBJECT_GI_DEKUPOUCH + { GetItem_DrawBulletBag, + { gGiBulletBagDL, gGiBulletBagColorDL, gGiBulletBagStringDL, gGiBulletBagStringColorDL, gGiBulletBagWritingDL } }, + // green rupee, OBJECT_GI_RUPY + { GetItem_DrawSmallRupee, + { gGiRupeeInnerDL, gGiGreenRupeeInnerColorDL, gGiRupeeOuterDL, gGiGreenRupeeOuterColorDL } }, + // blue rupee, OBJECT_GI_RUPY + { GetItem_DrawSmallRupee, + { gGiRupeeInnerDL, gGiBlueRupeeInnerColorDL, gGiRupeeOuterDL, gGiBlueRupeeOuterColorDL } }, + // red rupee, OBJECT_GI_RUPY + { GetItem_DrawSmallRupee, { gGiRupeeInnerDL, gGiRedRupeeInnerColorDL, gGiRupeeOuterDL, gGiRedRupeeOuterColorDL } }, + // big poe, OBJECT_GI_GHOST + { GetItem_DrawPoes, + { gGiGhostContainerLidDL, gGiGhostContainerGlassDL, gGiGhostContainerContentsDL, gGiBigPoeColorDL } }, + // purple rupee, OBJECT_GI_RUPY + { GetItem_DrawOpa10Xlu32, + { gGiRupeeInnerDL, gGiPurpleRupeeInnerColorDL, gGiRupeeOuterDL, gGiPurpleRupeeOuterColorDL } }, + // gold rupee, OBJECT_GI_RUPY + { GetItem_DrawOpa10Xlu32, + { gGiRupeeInnerDL, gGiGoldRupeeInnerColorDL, gGiRupeeOuterDL, gGiGoldRupeeOuterColorDL } }, + // bullet bag 50, OBJECT_GI_DEKUPOUCH + { GetItem_DrawBulletBag, + { gGiBulletBagDL, gGiBulletBag50ColorDL, gGiBulletBagStringDL, gGiBulletBag50StringColorDL, + gGiBulletBagWritingDL } }, + // kokiri sword, OBJECT_GI_SWORD_1 + { GetItem_DrawOpa0, { gGiKokiriSwordDL } }, + // gold skulltula token, OBJECT_ST + { GetItem_DrawSkullToken, { object_st_DL_004DB0, object_st_DL_004EB8 } }, +}; + +/** + * Draw "Get Item" Model + * Calls the corresponding draw function for the given draw ID + */ +void GetItem_Draw(GlobalContext* globalCtx, s16 drawId) { + sDrawItemTable[drawId].drawFunc(globalCtx, drawId); +} + +// All remaining functions in this file are draw functions referenced in the table and called by the function above + +void GetItem_DrawMaskOrBombchu(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 556); + + func_80093BA8(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 560), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 565); +} + +void GetItem_DrawSoldOut(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 572); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 5); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 576), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 581); +} + +void GetItem_DrawBlueFire(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 588); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 592), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 0), + 0 * (globalCtx->state.frames * 0), 16, 32, 1, 1 * (globalCtx->state.frames * 1), + 1 * -(globalCtx->state.frames * 8), 16, 32)); + Matrix_Push(); + Matrix_Translate(-8.0f, -2.0f, 0.0f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 615), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 621); +} + +void GetItem_DrawPoes(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 628); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 632), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 641), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 0), + 0 * (globalCtx->state.frames * 0), 16, 32, 1, 1 * (globalCtx->state.frames * 1), + 1 * -(globalCtx->state.frames * 6), 16, 32)); + Matrix_Push(); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 656), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[3]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 663); +} + +void GetItem_DrawFairy(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 670); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 674), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 683), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 0), + 0 * (globalCtx->state.frames * 0), 32, 32, 1, 1 * (globalCtx->state.frames * 1), + 1 * -(globalCtx->state.frames * 6), 32, 32)); + Matrix_Push(); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 698), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 704); +} + +void GetItem_DrawMirrorShield(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 712); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 0) % 256, + 1 * (globalCtx->state.frames * 2) % 256, 64, 64, 1, + 0 * (globalCtx->state.frames * 0) % 128, 1 * (globalCtx->state.frames * 1) % 128, 32, + 32)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 723), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 730), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 735); +} + +void GetItem_DrawSkullToken(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 742); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 746), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 0), + 1 * -(globalCtx->state.frames * 5), 32, 32, 1, 0 * (globalCtx->state.frames * 0), + 0 * (globalCtx->state.frames * 0), 32, 64)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 760), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 765); +} + +void GetItem_DrawEggOrMedallion(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 772); + + func_80093BA8(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 776), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 783); +} + +void GetItem_DrawCompass(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 811); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 815), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 5); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 822), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 827); +} + +void GetItem_DrawPotion(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 834); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -1 * (globalCtx->state.frames * 1), + 1 * (globalCtx->state.frames * 1), 32, 32, 1, -1 * (globalCtx->state.frames * 1), + 1 * (globalCtx->state.frames * 1), 32, 32)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 845), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[2]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[3]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 855), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[4]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[5]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 861); +} + +void GetItem_DrawGoronSword(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 868); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 1 * (globalCtx->state.frames * 1), + 0 * (globalCtx->state.frames * 1), 32, 32, 1, 0 * (globalCtx->state.frames * 1), + 0 * (globalCtx->state.frames * 1), 32, 32)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 878), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 883); +} + +void GetItem_DrawDekuNuts(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 890); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 1 * (globalCtx->state.frames * 6), + 1 * (globalCtx->state.frames * 6), 32, 32, 1, 1 * (globalCtx->state.frames * 6), + 1 * (globalCtx->state.frames * 6), 32, 32)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 901), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 906); +} + +void GetItem_DrawRecoveryHeart(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 913); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 1), + 1 * -(globalCtx->state.frames * 3), 32, 32, 1, 0 * (globalCtx->state.frames * 1), + 1 * -(globalCtx->state.frames * 2), 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 924), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 929); +} + +void GetItem_DrawFish(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 936); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 * (globalCtx->state.frames * 0), + 1 * (globalCtx->state.frames * 1), 32, 32, 1, 0 * (globalCtx->state.frames * 0), + 1 * (globalCtx->state.frames * 1), 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 947), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 952); +} + +void GetItem_DrawOpa0(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 959); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 963), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 968); +} + +void GetItem_DrawOpa0Xlu1(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 975); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 979), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 986), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 991); +} + +void GetItem_DrawXlu01(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 998); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1002), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[0]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1008); +} + +void GetItem_DrawOpa10Xlu2(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1015); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1019), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1027), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1032); +} + +void GetItem_DrawMagicArrow(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1039); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1043), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1050), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1056); +} + +void GetItem_DrawMagicSpell(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1063); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 1 * (globalCtx->state.frames * 2), + 1 * -(globalCtx->state.frames * 6), 32, 32, 1, 1 * (globalCtx->state.frames * 1), + -1 * (globalCtx->state.frames * 2), 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1074), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[0]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1081); +} + +void GetItem_DrawOpa1023(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1088); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1092), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[2]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[3]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1100); +} + +void GetItem_DrawOpa10Xlu32(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1108); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1112), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1120), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[3]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1126); +} + +void GetItem_DrawSmallRupee(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1133); + + Matrix_Scale(0.7f, 0.7f, 0.7f, MTXMODE_APPLY); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1140), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1148), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[3]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1154); +} + +void GetItem_DrawScale(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1162); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 1 * (globalCtx->state.frames * 2), + -1 * (globalCtx->state.frames * 2), 64, 64, 1, 1 * (globalCtx->state.frames * 4), + 1 * -(globalCtx->state.frames * 4), 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1173), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[3]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[0]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1181); +} + +void GetItem_DrawBulletBag(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1188); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1192), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1200), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[2]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[3]); + gSPDisplayList(POLY_XLU_DISP++, sDrawItemTable[drawId].dlists[4]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1207); +} + +void GetItem_DrawWallet(GlobalContext* globalCtx, s16 drawId) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1214); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_draw.c", 1218), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[1]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[0]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[2]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[3]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[4]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[5]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[6]); + gSPDisplayList(POLY_OPA_DISP++, sDrawItemTable[drawId].dlists[7]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_draw.c", 1230); +} diff --git a/soh/src/code/z_eff_blure.c b/soh/src/code/z_eff_blure.c new file mode 100644 index 000000000..e08f8f337 --- /dev/null +++ b/soh/src/code/z_eff_blure.c @@ -0,0 +1,1062 @@ +#include "global.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +void EffectBlure_AddVertex(EffectBlure* this, Vec3f* p1, Vec3f* p2) { + EffectBlureElement* elem; + s32 numElements; + Vec3f sp16C; + Vec3f sp160; + Vec3f sp154; + f32 scale; + MtxF sp110; + MtxF spD0; + MtxF sp90; + MtxF sp50; + Vec3f sp44; + Vec3f sp38; + + if (this != NULL) { + numElements = this->numElements; + if (numElements >= 16) { + // "Blure vertex addition processing: Table over %d" + osSyncPrintf("ブラ─頂点追加処理:テーブルオーバー %d\n", numElements); + return; + } + + elem = &this->elements[numElements]; + elem->state = 1; + + if (!(this->flags & 2)) { + elem->p1.x = p1->x; + elem->p1.y = p1->y; + elem->p1.z = p1->z; + elem->p2.x = p2->x; + elem->p2.y = p2->y; + elem->p2.z = p2->z; + } else { + sp16C.x = ((f32)(elem - 1)->p2.x + (f32)(elem - 1)->p1.x) * 0.5f; + sp16C.y = ((f32)(elem - 1)->p2.y + (f32)(elem - 1)->p1.y) * 0.5f; + sp16C.z = ((f32)(elem - 1)->p2.z + (f32)(elem - 1)->p1.z) * 0.5f; + sp160.x = (p1->x + p2->x) * 0.5f; + sp160.y = (p1->y + p2->y) * 0.5f; + sp160.z = (p1->z + p2->z) * 0.5f; + + Math_Vec3f_Diff(&sp160, &sp16C, &sp154); + scale = Math3D_Vec3fMagnitude(&sp154); + if (!(fabsf(scale) < 0.008f)) { + scale = 1.0f / scale; + Math_Vec3f_Scale(&sp154, scale); + + SkinMatrix_SetTranslate(&sp110, sp160.x, sp160.y, sp160.z); + SkinMatrix_SetRotateAxis(&spD0, this->addAngle, sp154.x, sp154.y, sp154.z); + SkinMatrix_MtxFMtxFMult(&sp110, &spD0, &sp90); + SkinMatrix_SetTranslate(&sp110, -sp160.x, -sp160.y, -sp160.z); + SkinMatrix_MtxFMtxFMult(&sp90, &sp110, &sp50); + SkinMatrix_Vec3fMtxFMultXYZ(&sp50, p1, &sp38); + SkinMatrix_Vec3fMtxFMultXYZ(&sp50, p2, &sp44); + + elem->p1.x = sp38.x; + elem->p1.y = sp38.y; + elem->p1.z = sp38.z; + elem->p2.x = sp44.x; + elem->p2.y = sp44.y; + elem->p2.z = sp44.z; + } + } + + elem->timer = 1; + this->numElements++; + } +} + +void EffectBlure_AddSpace(EffectBlure* this) { + EffectBlureElement* elem; + s32 numElements; + + if (this != NULL) { + numElements = this->numElements; + if (numElements >= 16) { + // "Blure space addition processing: Table over %d" + osSyncPrintf("ブラ─空白追加処理:テーブルオーバー %d\n", numElements); + return; + } + + elem = &this->elements[numElements]; + elem->state = 0; + elem->timer = 1; + + this->numElements++; + } +} + +void EffectBlure_InitElements(EffectBlure* this) { + EffectBlureElement* elem; + s32 i; + + this->numElements = 0; + + for (i = 0; i < 16; i++) { + elem = &this->elements[i]; + + elem->state = 2; + elem->p1.x = 0; + elem->p1.y = 0; + elem->p1.z = 0; + elem->p2.x = 0; + elem->p2.y = 0; + elem->p2.z = 0; + elem->timer = 0; + elem->flags = 0; + } +} + +void EffectBlure_Init1(void* thisx, void* initParamsx) { + EffectBlure* this = (EffectBlure*)thisx; + EffectBlureInit1* initParams = (EffectBlureInit1*)initParamsx; + + if ((this != NULL) && (initParams != NULL)) { + EffectBlure_InitElements(this); + this->p1StartColor.r = initParams->p1StartColor[0]; + this->p2StartColor.r = initParams->p2StartColor[0]; + this->p1EndColor.r = initParams->p1EndColor[0]; + this->p2EndColor.r = initParams->p2EndColor[0]; + this->p1StartColor.g = initParams->p1StartColor[1]; + this->p2StartColor.g = initParams->p2StartColor[1]; + this->p1EndColor.g = initParams->p1EndColor[1]; + this->p2EndColor.g = initParams->p2EndColor[1]; + this->p1StartColor.b = initParams->p1StartColor[2]; + this->p2StartColor.b = initParams->p2StartColor[2]; + this->p1EndColor.b = initParams->p1EndColor[2]; + this->p2EndColor.b = initParams->p2EndColor[2]; + this->p1StartColor.a = initParams->p1StartColor[3]; + this->p2StartColor.a = initParams->p2StartColor[3]; + this->p1EndColor.a = initParams->p1EndColor[3]; + this->p2EndColor.a = initParams->p2EndColor[3]; + this->elemDuration = initParams->elemDuration; + this->unkFlag = initParams->unkFlag; + this->calcMode = initParams->calcMode; + this->flags = 0; + this->addAngleChange = 0; + this->addAngle = 0; + this->drawMode = 0; + this->altPrimColor.r = 0; + this->altPrimColor.g = 0; + this->altPrimColor.b = 0; + this->altPrimColor.a = 0; + this->altEnvColor.r = 0; + this->altEnvColor.g = 0; + this->altEnvColor.b = 0; + this->altEnvColor.a = 0; + this->mode4Param = 1.0f; + } +} + +void EffectBlure_Init2(void* thisx, void* initParamsx) { + EffectBlure* this = (EffectBlure*)thisx; + EffectBlureInit2* initParams = (EffectBlureInit2*)initParamsx; + + if ((this != NULL) && (initParams != NULL)) { + EffectBlure_InitElements(this); + this->p1StartColor.r = initParams->p1StartColor[0]; + this->p2StartColor.r = initParams->p2StartColor[0]; + this->p1EndColor.r = initParams->p1EndColor[0]; + this->p2EndColor.r = initParams->p2EndColor[0]; + this->p1StartColor.g = initParams->p1StartColor[1]; + this->p2StartColor.g = initParams->p2StartColor[1]; + this->p1EndColor.g = initParams->p1EndColor[1]; + this->p2EndColor.g = initParams->p2EndColor[1]; + this->p1StartColor.b = initParams->p1StartColor[2]; + this->p2StartColor.b = initParams->p2StartColor[2]; + this->p1EndColor.b = initParams->p1EndColor[2]; + this->p2EndColor.b = initParams->p2EndColor[2]; + this->p1StartColor.a = initParams->p1StartColor[3]; + this->p2StartColor.a = initParams->p2StartColor[3]; + this->p1EndColor.a = initParams->p1EndColor[3]; + this->p2EndColor.a = initParams->p2EndColor[3]; + this->elemDuration = initParams->elemDuration; + this->unkFlag = initParams->unkFlag; + this->calcMode = initParams->calcMode; + this->flags = initParams->flags; + this->drawMode = initParams->drawMode; + this->addAngleChange = initParams->addAngleChange; + this->addAngle = 0; + this->mode4Param = initParams->mode4Param; + this->altPrimColor = initParams->altPrimColor; + this->altEnvColor = initParams->altEnvColor; + } +} + +void EffectBlure_Destroy(void* thisx) { +} + +s32 EffectBlure_Update(void* thisx) { + EffectBlure* this = (EffectBlure*)thisx; + s32 i; + + if (this == NULL) { + return 0; + } + + if (this->numElements == 0) { + return 0; + } + + while (true) { + if (this->elements[0].state == 0) { + for (i = 0; i < 15; i++) { + this->elements[i] = this->elements[i + 1]; + } + + this->elements[i].state = 2; + this->elements[i].p1.x = 0; + this->elements[i].p1.y = 0; + this->elements[i].p1.z = 0; + this->elements[i].p2.x = 0; + this->elements[i].p2.y = 0; + this->elements[i].p2.z = 0; + this->elements[i].flags = 0; + this->elements[i].timer = 0; + + this->numElements--; + if (this->numElements <= 0) { + this->numElements = 0; + return 0; + } + } else { + break; + } + } + + if (this->elements[0].state == 2) { + return 0; + } + + for (i = 0; i < this->numElements; i++) { + this->elements[i].timer++; + } + + if (this->elemDuration < this->elements[0].timer) { + for (i = 0; i < 15; i++) { + this->elements[i] = this->elements[i + 1]; + } + + this->elements[i].state = 2; + this->elements[i].p1.x = 0; + this->elements[i].p1.y = 0; + this->elements[i].p1.z = 0; + this->elements[i].p2.x = 0; + this->elements[i].p2.y = 0; + this->elements[i].p2.z = 0; + this->elements[i].flags = 0; + this->elements[i].timer = 0; + + this->numElements--; + if (this->numElements <= 0) { + this->numElements = 0; + return 0; + } + return 0; + } + + this->addAngle += this->addAngleChange; + return 0; +} + +void EffectBlure_UpdateFlags(EffectBlureElement* elem) { + Vec3f sp64; + Vec3f sp58; + Vec3f sp4C; + Vec3f sp40; + + if (((elem - 1)->state == 0) || ((elem + 1)->state == 0)) { + elem->flags &= ~3; + elem->flags |= 2; + } else { + EffectBlureElement* prev = elem - 1; + EffectBlureElement* next = elem + 1; + f32 sp34; + f32 sp30; + f32 sp2C; + + if (1) {} // Necessary to match + + Math_Vec3s_DiffToVec3f(&sp64, &elem->p1, &prev->p1); + Math_Vec3s_DiffToVec3f(&sp58, &elem->p2, &prev->p2); + Math_Vec3s_DiffToVec3f(&sp4C, &next->p1, &elem->p1); + Math_Vec3s_DiffToVec3f(&sp40, &next->p2, &elem->p2); + + if (Math3D_CosOut(&sp64, &sp4C, &sp34) || Math3D_CosOut(&sp58, &sp40, &sp30) || + Math3D_CosOut(&sp4C, &sp40, &sp2C)) { + elem->flags &= ~3; + elem->flags |= 0; + } else if ((sp34 <= -0.5f) || (sp30 <= -0.5f) || (sp2C <= 0.7071f)) { // cos(45 degrees) + elem->flags &= ~3; + elem->flags |= 0; + } else { + elem->flags &= ~3; + elem->flags |= 1; + } + } +} + +void EffectBlure_GetComputedValues(EffectBlure* this, s32 index, f32 ratio, Vec3s* vec1, Vec3s* vec2, + Color_RGBA8* color1, Color_RGBA8* color2) { + Vec3s sp30; + f32 mode4Param; + EffectBlureElement* elem = &this->elements[index]; + + switch (this->calcMode) { + case 1: + vec1->x = func_80027E34(elem->p1.x, elem->p2.x, ratio); + vec1->y = func_80027E34(elem->p1.y, elem->p2.y, ratio); + vec1->z = func_80027E34(elem->p1.z, elem->p2.z, ratio); + vec2->x = elem->p2.x; + vec2->y = elem->p2.y; + vec2->z = elem->p2.z; + break; + + case 2: + vec1->x = elem->p1.x; + vec1->y = elem->p1.y; + vec1->z = elem->p1.z; + vec2->x = func_80027E34(elem->p2.x, elem->p1.x, ratio); + vec2->y = func_80027E34(elem->p2.y, elem->p1.y, ratio); + vec2->z = func_80027E34(elem->p2.z, elem->p1.z, ratio); + break; + + case 3: + ratio *= 0.5f; + vec1->x = func_80027E34(elem->p1.x, elem->p2.x, ratio); + vec1->y = func_80027E34(elem->p1.y, elem->p2.y, ratio); + vec1->z = func_80027E34(elem->p1.z, elem->p2.z, ratio); + vec2->x = func_80027E34(elem->p2.x, elem->p1.x, ratio); + vec2->y = func_80027E34(elem->p2.y, elem->p1.y, ratio); + vec2->z = func_80027E34(elem->p2.z, elem->p1.z, ratio); + ratio *= 2.0f; + break; + + case 4: + sp30.x = elem->p1.x - elem->p2.x; + sp30.y = elem->p1.y - elem->p2.y; + sp30.z = elem->p1.z - elem->p2.z; + mode4Param = this->mode4Param - 1.0f; + + vec1->x = (sp30.x * 0.5f * mode4Param * ratio) + elem->p1.x; + vec1->y = (sp30.y * 0.5f * mode4Param * ratio) + elem->p1.y; + if (1) {} // Necessary to match + vec1->z = (sp30.z * 0.5f * mode4Param * ratio) + elem->p1.z; + + vec2->x = -(sp30.x * 0.5f * mode4Param * ratio) + elem->p2.x; + vec2->y = -(sp30.y * 0.5f * mode4Param * ratio) + elem->p2.y; + vec2->z = -(sp30.z * 0.5f * mode4Param * ratio) + elem->p2.z; + break; + + case 0: + default: + vec1->x = elem->p1.x; + vec1->y = elem->p1.y; + vec1->z = elem->p1.z; + vec2->x = elem->p2.x; + vec2->y = elem->p2.y; + vec2->z = elem->p2.z; + break; + } + + //sp30 = sp30; // Optimized out but seems necessary to match stack usage + + if (this->flags & 0x10) { + color1->r = color1->g = color1->b = color1->a = 255; + color2->r = color2->g = color2->b = color2->a = 255; + } else { + color1->r = func_80027E84(this->p1StartColor.r, this->p1EndColor.r, ratio); + color1->g = func_80027E84(this->p1StartColor.g, this->p1EndColor.g, ratio); + color1->b = func_80027E84(this->p1StartColor.b, this->p1EndColor.b, ratio); + color1->a = func_80027E84(this->p1StartColor.a, this->p1EndColor.a, ratio); + color2->r = func_80027E84(this->p2StartColor.r, this->p2EndColor.r, ratio); + color2->g = func_80027E84(this->p2StartColor.g, this->p2EndColor.g, ratio); + color2->b = func_80027E84(this->p2StartColor.b, this->p2EndColor.b, ratio); + color2->a = func_80027E84(this->p2StartColor.a, this->p2EndColor.a, ratio); + } +} + +void EffectBlure_SetupSmooth(EffectBlure* this, GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 809); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x26); + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 813); +} + +// original name: "SQ_NoInterpolate_disp" +void EffectBlure_DrawElemNoInterpolation(EffectBlure* this, EffectBlureElement* elem, s32 index, + GraphicsContext* gfxCtx) { + static Vtx_t baseVtx = VTX_T(0, 0, 0, 0, 0, 255, 255, 255, 255); + Vtx* vtx; + Vec3s sp8C; + Vec3s sp84; + f32 ratio; + Color_RGBA8 sp7C; + Color_RGBA8 sp78; + Vec3f sp6C; + Vec3f sp60; + Vec3f sp54; + + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 838); + + Math_Vec3s_ToVec3f(&sp6C, &this->elements[0].p2); + + vtx = Graph_Alloc(gfxCtx, sizeof(Vtx[4])); + if (vtx == NULL) { + // "Vertices cannot be secured." + osSyncPrintf("z_eff_blure.c::SQ_NoInterpolate_disp() 頂点確保できず。\n"); + } else { + vtx[0].v = baseVtx; + vtx[1].v = baseVtx; + vtx[2].v = baseVtx; + vtx[3].v = baseVtx; + + ratio = (f32)elem->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, index, ratio, &sp8C, &sp84, &sp7C, &sp78); + + sp60.x = sp84.x; + sp60.y = sp84.y; + sp60.z = sp84.z; + Math_Vec3f_Diff(&sp60, &sp6C, &sp54); + Math_Vec3f_Scale(&sp54, 10.0f); + vtx[0].v.ob[0] = sp54.x; + vtx[0].v.ob[1] = sp54.y; + vtx[0].v.ob[2] = sp54.z; + vtx[0].v.cn[0] = sp78.r; + vtx[0].v.cn[1] = sp78.g; + vtx[0].v.cn[2] = sp78.b; + vtx[0].v.cn[3] = sp78.a; + + if (1) {} // Necessary to match + + sp60.x = sp8C.x; + sp60.y = sp8C.y; + sp60.z = sp8C.z; + Math_Vec3f_Diff(&sp60, &sp6C, &sp54); + Math_Vec3f_Scale(&sp54, 10.0f); + vtx[1].v.ob[0] = sp54.x; + vtx[1].v.ob[1] = sp54.y; + vtx[1].v.ob[2] = sp54.z; + vtx[1].v.cn[0] = sp7C.r; + vtx[1].v.cn[1] = sp7C.g; + vtx[1].v.cn[2] = sp7C.b; + vtx[1].v.cn[3] = sp7C.a; + + ratio = (f32)(elem + 1)->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, index + 1, ratio, &sp8C, &sp84, &sp7C, &sp78); + + sp60.x = sp8C.x; + sp60.y = sp8C.y; + sp60.z = sp8C.z; + Math_Vec3f_Diff(&sp60, &sp6C, &sp54); + Math_Vec3f_Scale(&sp54, 10.0f); + vtx[2].v.ob[0] = sp54.x; + vtx[2].v.ob[1] = sp54.y; + vtx[2].v.ob[2] = sp54.z; + vtx[2].v.cn[0] = sp7C.r; + vtx[2].v.cn[1] = sp7C.g; + vtx[2].v.cn[2] = sp7C.b; + vtx[2].v.cn[3] = sp7C.a; + + if (1) {} // Necessary to match + + sp60.x = sp84.x; + sp60.y = sp84.y; + sp60.z = sp84.z; + Math_Vec3f_Diff(&sp60, &sp6C, &sp54); + Math_Vec3f_Scale(&sp54, 10.0f); + vtx[3].v.ob[0] = sp54.x; + vtx[3].v.ob[1] = sp54.y; + vtx[3].v.ob[2] = sp54.z; + vtx[3].v.cn[0] = sp78.r; + vtx[3].v.cn[1] = sp78.g; + vtx[3].v.cn[2] = sp78.b; + vtx[3].v.cn[3] = sp78.a; + + gSPVertex(POLY_XLU_DISP++, vtx, 4, 0); + gSP2Triangles(POLY_XLU_DISP++, 0, 1, 2, 0, 0, 2, 3, 0); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 932); +} + +void EffectBlure_DrawElemHermiteInterpolation(EffectBlure* this, EffectBlureElement* elem, s32 index, + GraphicsContext* gfxCtx) { + static Vtx_t baseVtx = VTX_T(0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF); + Vtx* vtx; + Vec3s sp1EC; + Vec3s sp1E4; + f32 ratio; + Color_RGBA8 sp1DC; + Color_RGBA8 sp1D8; + Vec3f sp1CC; + Vec3f sp1C0; + Vec3f sp1B4; + Vec3f sp1A8; + Color_RGBA8 sp1A4; + Color_RGBA8 sp1A0; + Color_RGBA8 sp19C; + Color_RGBA8 sp198; + Vec3f sp18C; + Vec3f sp180; + Vec3f sp174; + Vec3f sp168; + s32 i; + Vec3f sp158; + Vec3f sp14C; + Color_RGBA8 sp148; + Color_RGBA8 sp144; + Vec3f sp138; + + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 971); + + Math_Vec3s_ToVec3f(&sp138, &this->elements[0].p2); + + ratio = (f32)elem->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, index, ratio, &sp1EC, &sp1E4, &sp1A4, &sp1A0); + Math_Vec3s_ToVec3f(&sp1CC, &sp1EC); + Math_Vec3s_ToVec3f(&sp1C0, &sp1E4); + + ratio = (f32)(elem + 1)->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, index + 1, ratio, &sp1EC, &sp1E4, &sp19C, &sp198); + Math_Vec3s_ToVec3f(&sp18C, &sp1EC); + Math_Vec3s_ToVec3f(&sp180, &sp1E4); + + if ((elem->flags & 3) == 2) { + Math_Vec3f_Diff(&sp18C, &sp1CC, &sp1B4); + Math_Vec3f_Diff(&sp180, &sp1C0, &sp1A8); + } else { + Vec3f sp118; + Vec3f sp10C; + + ASSERT(index - 1 >= 0, "index - 1 >= 0", "../z_eff_blure.c", 1005); + + ratio = (f32)(elem - 1)->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, index - 1, ratio, &sp1EC, &sp1E4, &sp1DC, &sp1D8); + Math_Vec3s_ToVec3f(&sp118, &sp1EC); + Math_Vec3s_ToVec3f(&sp10C, &sp1E4); + Math_Vec3f_Diff(&sp18C, &sp118, &sp1B4); + Math_Vec3f_Diff(&sp180, &sp10C, &sp1A8); + } + + Math_Vec3f_Scale(&sp1B4, 0.5f); + Math_Vec3f_Scale(&sp1A8, 0.5f); + + if (((elem + 1)->flags & 3) == 2) { + Math_Vec3f_Diff(&sp18C, &sp1CC, &sp174); + Math_Vec3f_Diff(&sp180, &sp1C0, &sp168); + } else { + Vec3f sp100; + Vec3f spF4; + + ASSERT(index + 2 < this->numElements, "index + 2 < this2->now_edge_num", "../z_eff_blure.c", 1032); + + ratio = (f32)(elem + 2)->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, index + 2, ratio, &sp1EC, &sp1E4, &sp1DC, &sp1D8); + Math_Vec3s_ToVec3f(&sp100, &sp1EC); + Math_Vec3s_ToVec3f(&spF4, &sp1E4); + Math_Vec3f_Diff(&sp100, &sp1CC, &sp174); + Math_Vec3f_Diff(&spF4, &sp1C0, &sp168); + } + + Math_Vec3f_Scale(&sp174, 0.5f); + Math_Vec3f_Scale(&sp168, 0.5f); + + vtx = Graph_Alloc(gfxCtx, sizeof(Vtx[16])); + if (vtx == NULL) { + // "Vertices cannot be secured." + osSyncPrintf("z_eff_blure.c::SQ_HermiteInterpolate_disp() 頂点確保できず。\n"); + } else { + Math_Vec3f_Diff(&sp1CC, &sp138, &sp158); + Math_Vec3f_Scale(&sp158, 10.0f); + Math_Vec3f_Diff(&sp1C0, &sp138, &sp14C); + Math_Vec3f_Scale(&sp14C, 10.0f); + + Color_RGBA8_Copy(&sp148, &sp1A4); + Color_RGBA8_Copy(&sp144, &sp1A0); + + vtx[0].v = baseVtx; + vtx[1].v = baseVtx; + + vtx[0].v.ob[0] = Math_FNearbyIntF(sp158.x); + vtx[0].v.ob[1] = Math_FNearbyIntF(sp158.y); + vtx[0].v.ob[2] = Math_FNearbyIntF(sp158.z); + vtx[0].v.cn[0] = sp148.r; + vtx[0].v.cn[1] = sp148.g; + vtx[0].v.cn[2] = sp148.b; + vtx[0].v.cn[3] = sp148.a; + vtx[1].v.ob[0] = Math_FNearbyIntF(sp14C.x); + vtx[1].v.ob[1] = Math_FNearbyIntF(sp14C.y); + vtx[1].v.ob[2] = Math_FNearbyIntF(sp14C.z); + vtx[1].v.cn[0] = sp144.r; + vtx[1].v.cn[1] = sp144.g; + vtx[1].v.cn[2] = sp144.b; + vtx[1].v.cn[3] = sp144.a; + + for (i = 1; i < 8; i++) { + s32 j1 = 2 * i; + s32 j2 = 2 * i + 1; + Vec3f spE0; + f32 temp_f28 = i / 7.0f; // t + f32 temp_f0 = temp_f28 * temp_f28; // t^2 + f32 temp_f2 = temp_f0 * temp_f28; // t^3 + f32 temp_f20 = temp_f2 - temp_f0; // t^3 - t^2 + f32 temp_f22 = temp_f2 - 2.0f * temp_f0 + temp_f28; // t^3 - 2t^2 + t + f32 temp_f24 = 2.0f * temp_f2 - temp_f0 * 3.0f + 1.0f; // 2t^3 - 3t^2 + 1 + f32 temp_f26 = temp_f0 * 3.0f - 2.0f * temp_f2; // 3t^2 - 2t^3 + s32 pad1; + s32 pad2; + + // p = (2t^3 - 3t^2 + 1)p0 + (3t^2 - 2t^3)p1 + (t^3 - 2t^2 + t)m0 + (t^3 - t^2)m1 + spE0.x = (temp_f24 * sp1CC.x) + (temp_f26 * sp18C.x) + (temp_f22 * sp1B4.x) + (temp_f20 * sp174.x); + spE0.y = (temp_f24 * sp1CC.y) + (temp_f26 * sp18C.y) + (temp_f22 * sp1B4.y) + (temp_f20 * sp174.y); + spE0.z = (temp_f24 * sp1CC.z) + (temp_f26 * sp18C.z) + (temp_f22 * sp1B4.z) + (temp_f20 * sp174.z); + Math_Vec3f_Diff(&spE0, &sp138, &sp158); + Math_Vec3f_Scale(&sp158, 10.0f); + + spE0.x = (temp_f24 * sp1C0.x) + (temp_f26 * sp180.x) + (temp_f22 * sp1A8.x) + (temp_f20 * sp168.x); + spE0.y = (temp_f24 * sp1C0.y) + (temp_f26 * sp180.y) + (temp_f22 * sp1A8.y) + (temp_f20 * sp168.y); + spE0.z = (temp_f24 * sp1C0.z) + (temp_f26 * sp180.z) + (temp_f22 * sp1A8.z) + (temp_f20 * sp168.z); + Math_Vec3f_Diff(&spE0, &sp138, &sp14C); + Math_Vec3f_Scale(&sp14C, 10.0f); + + vtx[j1].v = baseVtx; + vtx[j2].v = baseVtx; + + vtx[j1].v.ob[0] = Math_FNearbyIntF(sp158.x); + vtx[j1].v.ob[1] = Math_FNearbyIntF(sp158.y); + vtx[j1].v.ob[2] = Math_FNearbyIntF(sp158.z); + vtx[j1].v.cn[0] = func_80027E84(sp1A4.r, sp19C.r, temp_f28); + vtx[j1].v.cn[1] = func_80027E84(sp1A4.g, sp19C.g, temp_f28); + vtx[j1].v.cn[2] = func_80027E84(sp1A4.b, sp19C.b, temp_f28); + vtx[j1].v.cn[3] = func_80027E84(sp1A4.a, sp19C.a, temp_f28); + + vtx[j2].v.ob[0] = Math_FNearbyIntF(sp14C.x); + vtx[j2].v.ob[1] = Math_FNearbyIntF(sp14C.y); + vtx[j2].v.ob[2] = Math_FNearbyIntF(sp14C.z); + vtx[j2].v.cn[0] = func_80027E84(sp1A0.r, sp198.r, temp_f28); + vtx[j2].v.cn[1] = func_80027E84(sp1A0.g, sp198.g, temp_f28); + vtx[j2].v.cn[2] = func_80027E84(sp1A0.b, sp198.b, temp_f28); + vtx[j2].v.cn[3] = func_80027E84(sp1A0.a, sp198.a, temp_f28); + } + + gSPVertex(POLY_XLU_DISP++, vtx, 16, 0); + gSP2Triangles(POLY_XLU_DISP++, 0, 1, 3, 0, 0, 3, 2, 0); + gSP2Triangles(POLY_XLU_DISP++, 2, 3, 5, 0, 2, 5, 4, 0); + gSP2Triangles(POLY_XLU_DISP++, 4, 5, 7, 0, 4, 7, 6, 0); + gSP2Triangles(POLY_XLU_DISP++, 6, 7, 9, 0, 6, 9, 8, 0); + gSP2Triangles(POLY_XLU_DISP++, 8, 9, 11, 0, 8, 11, 10, 0); + gSP2Triangles(POLY_XLU_DISP++, 10, 11, 13, 0, 10, 13, 12, 0); + gSP2Triangles(POLY_XLU_DISP++, 12, 13, 15, 0, 12, 15, 14, 0); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1184); +} + +void EffectBlure_DrawSmooth(EffectBlure* this2, GraphicsContext* gfxCtx) { + EffectBlure* this = this2; + EffectBlureElement* elem; + s32 i; + MtxF spDC; + MtxF sp9C; + MtxF sp5C; + Mtx* mtx; + + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1201); + + if (this->numElements < 2) { + return; + } + + this->elements[0].flags &= ~3; + this->elements[0].flags |= 2; + + for (elem = &this->elements[1]; elem < this->elements + this->numElements - 1; elem++) { + EffectBlure_UpdateFlags(elem); + } + + this->elements[this->numElements - 1].flags &= ~3; + this->elements[this->numElements - 1].flags |= 2; + + EffectBlure_SetupSmooth(this, gfxCtx); + SkinMatrix_SetTranslate(&spDC, this->elements[0].p2.x, this->elements[0].p2.y, this->elements[0].p2.z); + SkinMatrix_SetScale(&sp9C, 0.1f, 0.1f, 0.1f); + SkinMatrix_MtxFMtxFMult(&spDC, &sp9C, &sp5C); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &sp5C); + if (mtx == NULL) { + return; + } + + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + for (i = 0, elem = &this->elements[0]; elem < this->elements + this->numElements - 1; i++, elem++) { + if ((elem->state == 0) || ((elem + 1)->state == 0)) { + continue; + } + if ((((elem->flags & 3) == 0) && (((elem + 1)->flags & 3) == 0)) || + (((elem->flags & 3) == 2) && (((elem + 1)->flags & 3) == 0)) || + (((elem->flags & 3) == 0) && (((elem + 1)->flags & 3) == 2)) || + (((elem->flags & 3) == 2) && (((elem + 1)->flags & 3) == 2))) { + EffectBlure_DrawElemNoInterpolation(this, elem, i, gfxCtx); + } else { + EffectBlure_DrawElemHermiteInterpolation(this, elem, i, gfxCtx); + } + } + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1263); +} + +void EffectBlure_SetupSimple(GraphicsContext* gfxCtx, EffectBlure* this, Vtx* vtx) { + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1280); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x26); + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1285); +} + +void EffectBlure_SetupSimpleAlt(GraphicsContext* gfxCtx, EffectBlure* this, Vtx* vtx) { + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1294); + + gDPPipeSync(POLY_XLU_DISP++); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x26); + + gDPSetCycleType(POLY_XLU_DISP++, G_CYC_2CYCLE); + gDPSetTextureLUT(POLY_XLU_DISP++, G_TT_NONE); + gSPTexture(POLY_XLU_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + + gDPLoadTextureBlock(POLY_XLU_DISP++, gUnknownEffBlureTex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 32, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_WRAP, 6, 5, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetCombineLERP(POLY_XLU_DISP++, TEXEL0, PRIMITIVE, PRIM_LOD_FRAC, TEXEL0, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, COMBINED, ENVIRONMENT, 0, 0, 0, COMBINED); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_ZB_CLD_SURF2); + gSPClearGeometryMode(POLY_XLU_DISP++, G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR); + gSPSetGeometryMode(POLY_XLU_DISP++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH); + gDPPipeSync(POLY_XLU_DISP++); + + gDPSetEnvColor(POLY_XLU_DISP++, this->altEnvColor.r, this->altEnvColor.g, this->altEnvColor.b, this->altEnvColor.a); + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1329); +} + +void (*sSetupHandlers[])(GraphicsContext* gfxCtx, EffectBlure* this, Vtx* vtx) = { + EffectBlure_SetupSimple, + EffectBlure_SetupSimpleAlt, +}; + +s32 D_80115788 = 0; // unused + +// original name: "EffectBlureInfo2_disp_makeDisplayList" +void EffectBlure_DrawSimpleVertices(GraphicsContext* gfxCtx, EffectBlure* this, Vtx* vtx) { + Mtx* mtx; + + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1356); + + sSetupHandlers[this->drawMode](gfxCtx, this, vtx); + gDPPipeSync(POLY_XLU_DISP++); + + { + Vec3f sp1B0; + Vec3f sp1A4; + Vec3f sp198; + f32 alphaRatio; + MtxF sp154; + MtxF sp114; + MtxF spD4; + MtxF sp94; + f32 scale; + s32 i; + s32 j; + + j = 0; + + for (i = 0; i < this->numElements - 1; i++) { + if (this->drawMode == 1) { + alphaRatio = (f32)this->elements[i].timer / (f32)this->elemDuration; + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, this->altPrimColor.r, this->altPrimColor.g, + this->altPrimColor.b, this->altPrimColor.a * (1.0f - alphaRatio)); + gDPPipeSync(POLY_XLU_DISP++); + } + + if (1) {} // Necessary to match + + gSPVertex(POLY_XLU_DISP++, &vtx[j], 4, 0); + gSP2Triangles(POLY_XLU_DISP++, 0, 1, 3, 0, 0, 3, 2, 0); + + if (this->flags & 4) { + sp1B0.x = ((f32)vtx[4 * i + 0].v.ob[0] + (f32)vtx[4 * i + 1].v.ob[0]) * 0.5f; + sp1B0.y = ((f32)vtx[4 * i + 0].v.ob[1] + (f32)vtx[4 * i + 1].v.ob[1]) * 0.5f; + sp1B0.z = ((f32)vtx[4 * i + 0].v.ob[2] + (f32)vtx[4 * i + 1].v.ob[2]) * 0.5f; + sp1A4.x = ((f32)vtx[4 * i + 2].v.ob[0] + (f32)vtx[4 * i + 3].v.ob[0]) * 0.5f; + sp1A4.y = ((f32)vtx[4 * i + 2].v.ob[1] + (f32)vtx[4 * i + 3].v.ob[1]) * 0.5f; + sp1A4.z = ((f32)vtx[4 * i + 2].v.ob[2] + (f32)vtx[4 * i + 3].v.ob[2]) * 0.5f; + + Math_Vec3f_Diff(&sp1A4, &sp1B0, &sp198); + scale = sqrtf(SQ(sp198.x) + SQ(sp198.y) + SQ(sp198.z)); + + if (fabsf(scale) > 0.0005f) { + scale = 1.0f / scale; + Math_Vec3f_Scale(&sp198, scale); + + SkinMatrix_SetTranslate(&sp154, sp1B0.x, sp1B0.y, sp1B0.z); + SkinMatrix_SetRotateAxis(&sp114, 0x3FFF, sp198.x, sp198.y, sp198.z); + SkinMatrix_MtxFMtxFMult(&sp154, &sp114, &spD4); + SkinMatrix_SetTranslate(&sp154, -sp1B0.x, -sp1B0.y, -sp1B0.z); + SkinMatrix_MtxFMtxFMult(&spD4, &sp154, &sp94); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &sp94); + if (mtx == NULL) { + // "Forced termination because a matrix cannot be taken" + osSyncPrintf("EffectBlureInfo2_disp_makeDisplayList()マトリックス取れないので,強制終了\n"); + break; + } + + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPVertex(POLY_XLU_DISP++, &vtx[j], 4, 0); + gSP2Triangles(POLY_XLU_DISP++, 0, 1, 3, 0, 0, 3, 2, 0); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + } + } + + j += 4; + } + } + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1452); +} + +Vtx_t D_8011578C[] = { + VTX_T(0, 0, 0, 0, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX_T(0, 0, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF), + VTX_T(0, 0, 0, 2048, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX_T(0, 0, 0, 2048, 0, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +Vtx_t D_801157CC[] = { + VTX_T(0, 0, 0, 2048, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX_T(0, 0, 0, 2048, 0, 0xFF, 0xFF, 0xFF, 0xFF), + VTX_T(0, 0, 0, 2048, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX_T(0, 0, 0, 2048, 0, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +void EffectBlure_DrawSimple(EffectBlure* this2, GraphicsContext* gfxCtx) { + EffectBlure* this = this2; + Vtx* vtx; + Vtx* vtxIter; + EffectBlureElement* elem; + s32 vtxCount; + s32 i; + s32 j; + Vec3s sp74; + Vec3s sp6C; + f32 ratio; + Color_RGBA8 sp64; + Color_RGBA8 sp60; + + if (this->numElements >= 2) { + vtxCount = this->numElements * 4; + + vtx = Graph_Alloc(gfxCtx, vtxCount * sizeof(Vtx)); + if (vtx == NULL) { + // "Vertices cannot be secured. Forced termination" + osSyncPrintf("ブラ─表示:頂点確保できず。強制終了\n"); + return; + } + + vtxIter = vtx; + for (i = 0; i < 4; i++) { + vtxIter->v = D_8011578C[i]; + vtxIter++; + } + + if (this->numElements >= 2) { + for (elem = this->elements; elem < this->elements + this->numElements - 2; elem++) { + for (i = 0; i < 4; i++) { + vtxIter->v = D_801157CC[i]; + vtxIter++; + } + } + } + + for (i = 0; i < this->numElements; i++) { + elem = &this->elements[i]; + + ratio = (f32)elem->timer / (f32)this->elemDuration; + EffectBlure_GetComputedValues(this, i, ratio, &sp74, &sp6C, &sp64, &sp60); + + j = i * 4 - 2; + if (j >= 0) { + vtx[j].v.ob[0] = sp74.x; + vtx[j].v.ob[1] = sp74.y; + vtx[j].v.ob[2] = sp74.z; + vtx[j].v.cn[0] = sp64.r; + vtx[j].v.cn[1] = sp64.g; + vtx[j].v.cn[2] = sp64.b; + vtx[j].v.cn[3] = sp64.a; + } + + j++; + if (j >= 0) { + vtx[j].v.ob[0] = sp6C.x; + vtx[j].v.ob[1] = sp6C.y; + vtx[j].v.ob[2] = sp6C.z; + vtx[j].v.cn[0] = sp60.r; + vtx[j].v.cn[1] = sp60.g; + vtx[j].v.cn[2] = sp60.b; + vtx[j].v.cn[3] = sp60.a; + } + + j++; + if (vtxCount >= j) { + vtx[j].v.ob[0] = sp74.x; + vtx[j].v.ob[1] = sp74.y; + vtx[j].v.ob[2] = sp74.z; + vtx[j].v.cn[0] = sp64.r; + vtx[j].v.cn[1] = sp64.g; + vtx[j].v.cn[2] = sp64.b; + vtx[j].v.cn[3] = sp64.a; + } + + j++; + if (vtxCount >= j) { + vtx[j].v.ob[0] = sp6C.x; + vtx[j].v.ob[1] = sp6C.y; + vtx[j].v.ob[2] = sp6C.z; + vtx[j].v.cn[0] = sp60.r; + vtx[j].v.cn[1] = sp60.g; + vtx[j].v.cn[2] = sp60.b; + vtx[j].v.cn[3] = sp60.a; + } + } + + EffectBlure_DrawSimpleVertices(gfxCtx, this, vtx); + } +} + +void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) { + EffectBlure* this = (EffectBlure*)thisx; + Vtx* vtx; + EffectBlureElement* elem; + s32 i; + s32 j; + s32 phi_t2; + + OPEN_DISPS(gfxCtx, "../z_eff_blure.c", 1596); + + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->numElements != 0) { + if (this->flags == 0) { + func_800942F0(gfxCtx); + gDPPipeSync(POLY_XLU_DISP++); + + vtx = Graph_Alloc(gfxCtx, sizeof(Vtx[32])); + if (vtx == NULL) { + // "Blure display: Vertex table could not be secured" + osSyncPrintf("ブラ─表示:頂点テーブル確保できず\n"); + } else { + j = 0; + for (i = 0; i < this->numElements; i++) { + elem = &this->elements[i]; + + if (elem->state == 1) { + f32 ratio = (f32)elem->timer / (f32)this->elemDuration; + + switch (this->calcMode) { + case 1: + vtx[j].v.ob[0] = func_80027E34(elem->p1.x, elem->p2.x, ratio); + vtx[j].v.ob[1] = func_80027E34(elem->p1.y, elem->p2.y, ratio); + vtx[j].v.ob[2] = func_80027E34(elem->p1.z, elem->p2.z, ratio); + vtx[j + 1].v.ob[0] = elem->p2.x; + vtx[j + 1].v.ob[1] = elem->p2.y; + vtx[j + 1].v.ob[2] = elem->p2.z; + break; + case 2: + vtx[j].v.ob[0] = elem->p1.x; + vtx[j].v.ob[1] = elem->p1.y; + vtx[j].v.ob[2] = elem->p1.z; + vtx[j + 1].v.ob[0] = func_80027E34(elem->p2.x, elem->p1.x, ratio); + vtx[j + 1].v.ob[1] = func_80027E34(elem->p2.y, elem->p1.y, ratio); + vtx[j + 1].v.ob[2] = func_80027E34(elem->p2.z, elem->p1.z, ratio); + break; + case 3: + ratio = ratio * 0.5f; + vtx[j].v.ob[0] = func_80027E34(elem->p1.x, elem->p2.x, ratio); + vtx[j].v.ob[1] = func_80027E34(elem->p1.y, elem->p2.y, ratio); + vtx[j].v.ob[2] = func_80027E34(elem->p1.z, elem->p2.z, ratio); + vtx[j + 1].v.ob[0] = func_80027E34(elem->p2.x, elem->p1.x, ratio); + vtx[j + 1].v.ob[1] = func_80027E34(elem->p2.y, elem->p1.y, ratio); + vtx[j + 1].v.ob[2] = func_80027E34(elem->p2.z, elem->p1.z, ratio); + ratio = ratio + ratio; + break; + case 0: + default: + vtx[j].v.ob[0] = elem->p1.x; + vtx[j].v.ob[1] = elem->p1.y; + vtx[j].v.ob[2] = elem->p1.z; + vtx[j + 1].v.ob[0] = elem->p2.x; + vtx[j + 1].v.ob[1] = elem->p2.y; + vtx[j + 1].v.ob[2] = elem->p2.z; + break; + } + + vtx[j].v.flag = 0; + vtx[j].v.tc[0] = 0; + vtx[j].v.tc[1] = 0; + vtx[j].v.cn[0] = func_80027E84(this->p1StartColor.r, this->p1EndColor.r, ratio); + vtx[j].v.cn[1] = func_80027E84(this->p1StartColor.g, this->p1EndColor.g, ratio); + vtx[j].v.cn[2] = func_80027E84(this->p1StartColor.b, this->p1EndColor.b, ratio); + vtx[j].v.cn[3] = func_80027E84(this->p1StartColor.a, this->p1EndColor.a, ratio); + j++; + + vtx[j].v.flag = 0; + vtx[j].v.tc[0] = 0; + vtx[j].v.tc[1] = 0; + vtx[j].v.cn[0] = func_80027E84(this->p2StartColor.r, this->p2EndColor.r, ratio); + vtx[j].v.cn[1] = func_80027E84(this->p2StartColor.g, this->p2EndColor.g, ratio); + vtx[j].v.cn[2] = func_80027E84(this->p2StartColor.b, this->p2EndColor.b, ratio); + vtx[j].v.cn[3] = func_80027E84(this->p2StartColor.a, this->p2EndColor.a, ratio); + j++; + } + } + + j = 0; + + gSPVertex(POLY_XLU_DISP++, vtx, 32, 0); + + phi_t2 = 0; + for (i = 0; i < this->numElements; i++) { + elem = &this->elements[i]; + + if (elem->state == 0) { + phi_t2 = 0; + } else { + if (phi_t2 == 0) { + phi_t2 = 1; + } else { + gSP1Quadrangle(POLY_XLU_DISP++, j - 2, j - 1, j + 1, j, 0); + + if (1) {} // Necessary to match + + if (this->unkFlag == 1) { + phi_t2 = 0; + } + } + j += 2; + } + } + } + } else if (this->drawMode < 2) { + EffectBlure_DrawSimple(this, gfxCtx); + } else { + EffectBlure_DrawSmooth(this, gfxCtx); + } + } + + CLOSE_DISPS(gfxCtx, "../z_eff_blure.c", 1823); +} diff --git a/soh/src/code/z_eff_shield_particle.c b/soh/src/code/z_eff_shield_particle.c new file mode 100644 index 000000000..fad499286 --- /dev/null +++ b/soh/src/code/z_eff_shield_particle.c @@ -0,0 +1,216 @@ +#include "global.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +static Vtx sVertices[5] = { + VTX(-32, -32, 0, 0, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(32, 32, 0, 1024, 0, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-32, 32, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(32, -32, 0, 1024, 1024, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +// original name: "EffectShieldParticle_ct" +void EffectShieldParticle_Init(void* thisx, void* initParamsx) { + EffectShieldParticle* this = (EffectShieldParticle*)thisx; + EffectShieldParticleInit* initParams = (EffectShieldParticleInit*)initParamsx; + EffectShieldParticleElement* elem; + + if ((this != NULL) && (initParams != NULL)) { + this->numElements = initParams->numElements; + if (this->numElements > ARRAY_COUNT(this->elements)) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("EffectShieldParticle_ct():パーティクル数がオーバしてます。\n"); + osSyncPrintf(VT_RST); + return; + } + + this->position = initParams->position; + this->primColorStart = initParams->primColorStart; + this->envColorStart = initParams->envColorStart; + this->primColorMid = initParams->primColorMid; + this->envColorMid = initParams->envColorMid; + this->primColorEnd = initParams->primColorEnd; + this->envColorEnd = initParams->envColorEnd; + this->deceleration = initParams->deceleration; + this->maxInitialSpeed = initParams->maxInitialSpeed; + this->lengthCutoff = initParams->lengthCutoff; + this->duration = initParams->duration; + this->timer = 0; + + for (elem = &this->elements[0]; elem < &this->elements[this->numElements]; elem++) { + elem->initialSpeed = (Rand_ZeroOne() * (this->maxInitialSpeed * 0.5f)) + (this->maxInitialSpeed * 0.5f); + elem->endX = 0.0f; + elem->startXChange = 0.0f; + elem->startX = 0.0f; + elem->endXChange = elem->initialSpeed; + elem->yaw = Rand_ZeroOne() * 65534.0f; + elem->pitch = Rand_ZeroOne() * 65534.0f; + } + + this->lightDecay = initParams->lightDecay; + if (this->lightDecay == true) { + this->lightInfo.type = LIGHT_POINT_NOGLOW; + this->lightInfo.params.point = initParams->lightPoint; + this->lightNode = + LightContext_InsertLight(Effect_GetGlobalCtx(), &Effect_GetGlobalCtx()->lightCtx, &this->lightInfo); + } else { + this->lightNode = NULL; + } + } +} + +void EffectShieldParticle_Destroy(void* thisx) { + EffectShieldParticle* this = (EffectShieldParticle*)thisx; + + if ((this != NULL) && (this->lightDecay == true)) { + if (this->lightNode == Effect_GetGlobalCtx()->lightCtx.listHead) { + Effect_GetGlobalCtx()->lightCtx.listHead = this->lightNode->next; + } + LightContext_RemoveLight(Effect_GetGlobalCtx(), &Effect_GetGlobalCtx()->lightCtx, this->lightNode); + } +} + +s32 EffectShieldParticle_Update(void* thisx) { + EffectShieldParticle* this = (EffectShieldParticle*)thisx; + EffectShieldParticleElement* elem; + + if (this == NULL) { + return 0; + } + + for (elem = &this->elements[0]; elem < &this->elements[this->numElements]; elem++) { + elem->endXChange -= this->deceleration; + if (elem->endXChange < 0.0f) { + elem->endXChange = 0.0f; + } + + if (elem->startXChange > 0.0f) { + elem->startXChange -= this->deceleration; + if (elem->startXChange < 0.0f) { + elem->startXChange = 0.0f; + } + } + + elem->endX += elem->endXChange; + elem->startX += elem->startXChange; + + if ((elem->startXChange == 0.0f) && (this->lengthCutoff < (elem->endX - elem->startX))) { + elem->startXChange = elem->initialSpeed; + } + } + + if (this->lightDecay == true) { + this->lightInfo.params.point.radius /= 2; + } + + this->timer++; + + if (this->duration < this->timer) { + return 1; + } + + return 0; +} + +void EffectShieldParticle_GetColors(EffectShieldParticle* this, Color_RGBA8* primColor, Color_RGBA8* envColor) { + s32 halfDuration = this->duration * 0.5f; + f32 ratio; + + if (halfDuration == 0) { + primColor->r = this->primColorStart.r; + primColor->g = this->primColorStart.g; + primColor->b = this->primColorStart.b; + primColor->a = this->primColorStart.a; + envColor->r = this->envColorStart.r; + envColor->g = this->envColorStart.g; + envColor->b = this->envColorStart.b; + envColor->a = this->envColorStart.a; + } else if (this->timer < halfDuration) { + ratio = this->timer / (f32)halfDuration; + primColor->r = this->primColorStart.r + (this->primColorMid.r - this->primColorStart.r) * ratio; + primColor->g = this->primColorStart.g + (this->primColorMid.g - this->primColorStart.g) * ratio; + primColor->b = this->primColorStart.b + (this->primColorMid.b - this->primColorStart.b) * ratio; + primColor->a = this->primColorStart.a + (this->primColorMid.a - this->primColorStart.a) * ratio; + envColor->r = this->envColorStart.r + (this->envColorMid.r - this->envColorStart.r) * ratio; + envColor->g = this->envColorStart.g + (this->envColorMid.g - this->envColorStart.g) * ratio; + envColor->b = this->envColorStart.b + (this->envColorMid.b - this->envColorStart.b) * ratio; + envColor->a = this->envColorStart.a + (this->envColorMid.a - this->envColorStart.a) * ratio; + } else { + ratio = (this->timer - halfDuration) / (f32)halfDuration; + primColor->r = this->primColorMid.r + (this->primColorEnd.r - this->primColorMid.r) * ratio; + primColor->g = this->primColorMid.g + (this->primColorEnd.g - this->primColorMid.g) * ratio; + primColor->b = this->primColorMid.b + (this->primColorEnd.b - this->primColorMid.b) * ratio; + primColor->a = this->primColorMid.a + (this->primColorEnd.a - this->primColorMid.a) * ratio; + envColor->r = this->envColorMid.r + (this->envColorEnd.r - this->envColorMid.r) * ratio; + envColor->g = this->envColorMid.g + (this->envColorEnd.g - this->envColorMid.g) * ratio; + envColor->b = this->envColorMid.b + (this->envColorEnd.b - this->envColorMid.b) * ratio; + envColor->a = this->envColorMid.a + (this->envColorEnd.a - this->envColorMid.a) * ratio; + } +} + +void EffectShieldParticle_Draw(void* thisx, GraphicsContext* gfxCtx) { + EffectShieldParticle* this = (EffectShieldParticle*)thisx; + EffectShieldParticleElement* elem; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + + OPEN_DISPS(gfxCtx, "../z_eff_shield_particle.c", 272); + + if (this != NULL) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x26); + + gDPSetCycleType(POLY_XLU_DISP++, G_CYC_2CYCLE); + gDPPipeSync(POLY_XLU_DISP++); + gSPTexture(POLY_XLU_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + + gDPLoadTextureBlock(POLY_XLU_DISP++, gUnknownCircle6Tex, G_IM_FMT_I, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetCombineLERP(POLY_XLU_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0, 0, 0, + 0, COMBINED, 0, 0, 0, COMBINED); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_ZB_CLD_SURF2); + gSPClearGeometryMode(POLY_XLU_DISP++, G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR); + gSPSetGeometryMode(POLY_XLU_DISP++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH); + + EffectShieldParticle_GetColors(this, &primColor, &envColor); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, primColor.r, primColor.g, primColor.b, primColor.a); + gDPSetEnvColor(POLY_XLU_DISP++, envColor.r, envColor.g, envColor.b, envColor.a); + gDPPipeSync(POLY_XLU_DISP++); + + for (elem = &this->elements[0]; elem < &this->elements[this->numElements]; elem++) { + Mtx* mtx; + MtxF sp104; + MtxF spC4; + MtxF sp84; + f32 temp1 = (s16)((elem->endX + elem->startX) * 0.5f); + f32 temp2 = elem->endX - elem->startX; + f32 temp3 = (s16)((temp2 * (1.0f / 64.0f)) / 0.02f); + + if (temp3 < 1.0f) { + temp3 = 1.0f; + } + + SkinMatrix_SetTranslate(&spC4, this->position.x, this->position.y, this->position.z); + SkinMatrix_SetRotateZYX(&sp104, 0, elem->yaw, 0); + SkinMatrix_MtxFMtxFMult(&spC4, &sp104, &sp84); + SkinMatrix_SetRotateZYX(&sp104, 0, 0, elem->pitch); + SkinMatrix_MtxFMtxFMult(&sp84, &sp104, &spC4); + SkinMatrix_SetTranslate(&sp104, temp1, 0.0f, 0.0f); + SkinMatrix_MtxFMtxFMult(&spC4, &sp104, &sp84); + SkinMatrix_SetScale(&sp104, temp3 * 0.02f, 0.02f, 0.02f); + SkinMatrix_MtxFMtxFMult(&sp84, &sp104, &spC4); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &spC4); + if (mtx == NULL) { + break; + } + + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPVertex(POLY_XLU_DISP++, sVertices, 4, 0); + gSP2Triangles(POLY_XLU_DISP++, 0, 1, 2, 0, 0, 3, 1, 0); + } + } + + CLOSE_DISPS(gfxCtx, "../z_eff_shield_particle.c", 359); +} diff --git a/soh/src/code/z_eff_spark.c b/soh/src/code/z_eff_spark.c new file mode 100644 index 000000000..cfe8f628f --- /dev/null +++ b/soh/src/code/z_eff_spark.c @@ -0,0 +1,277 @@ +#include "global.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +// original name: "spark" +void EffectSpark_Init(void* thisx, void* initParamsx) { + EffectSpark* this = (EffectSpark*)thisx; + EffectSparkInit* initParams = (EffectSparkInit*)initParamsx; + EffectSparkElement* elem; + f32 velocityNorm; + s32 i; + + if ((this != NULL) && (initParams != NULL)) { + if ((initParams->uDiv == 0) || (initParams->vDiv == 0)) { + osSyncPrintf("spark():u_div,v_div 0では困る。\n"); // "u_div,v_div 0 is not good." + return; + } + + this->position = initParams->position; + this->speed = initParams->speed; + this->gravity = initParams->gravity; + this->uDiv = initParams->uDiv; + this->vDiv = initParams->vDiv; + this->colorStart[0].r = initParams->colorStart[0].r; + this->colorStart[0].g = initParams->colorStart[0].g; + this->colorStart[0].b = initParams->colorStart[0].b; + this->colorStart[0].a = initParams->colorStart[0].a; + this->colorStart[1].r = initParams->colorStart[1].r; + this->colorStart[1].g = initParams->colorStart[1].g; + this->colorStart[1].b = initParams->colorStart[1].b; + this->colorStart[1].a = initParams->colorStart[1].a; + this->colorStart[2].r = initParams->colorStart[2].r; + this->colorStart[2].g = initParams->colorStart[2].g; + this->colorStart[2].b = initParams->colorStart[2].b; + this->colorStart[2].a = initParams->colorStart[2].a; + this->colorStart[3].r = initParams->colorStart[3].r; + this->colorStart[3].g = initParams->colorStart[3].g; + this->colorStart[3].b = initParams->colorStart[3].b; + this->colorStart[3].a = initParams->colorStart[3].a; + this->colorEnd[0].r = initParams->colorEnd[0].r; + this->colorEnd[0].g = initParams->colorEnd[0].g; + this->colorEnd[0].b = initParams->colorEnd[0].b; + this->colorEnd[0].a = initParams->colorEnd[0].a; + this->colorEnd[1].r = initParams->colorEnd[1].r; + this->colorEnd[1].g = initParams->colorEnd[1].g; + this->colorEnd[1].b = initParams->colorEnd[1].b; + this->colorEnd[1].a = initParams->colorEnd[1].a; + this->colorEnd[2].r = initParams->colorEnd[2].r; + this->colorEnd[2].g = initParams->colorEnd[2].g; + this->colorEnd[2].b = initParams->colorEnd[2].b; + this->colorEnd[2].a = initParams->colorEnd[2].a; + this->colorEnd[3].r = initParams->colorEnd[3].r; + this->colorEnd[3].g = initParams->colorEnd[3].g; + this->colorEnd[3].b = initParams->colorEnd[3].b; + this->colorEnd[3].a = initParams->colorEnd[3].a; + this->duration = initParams->duration; + + this->numElements = (this->uDiv * this->vDiv) + 2; + if (this->numElements > ARRAY_COUNT(this->elements)) { + osSyncPrintf("table_sizeオーバー\n"); // "over table_size" + return; + } + + for (i = 0; i < this->numElements; i++) { + elem = &this->elements[i]; + + elem->position.x = this->position.x; + elem->position.y = this->position.y; + elem->position.z = this->position.z; + elem->velocity.x = Rand_ZeroOne() - 0.5f; + elem->velocity.y = Rand_ZeroOne() - 0.5f; + elem->velocity.z = Rand_ZeroOne() - 0.5f; + + velocityNorm = sqrtf(SQ(elem->velocity.x) + SQ(elem->velocity.y) + SQ(elem->velocity.z)); + + if (!(fabsf(velocityNorm) < 0.008f)) { + elem->velocity.x *= this->speed * (1.0f / velocityNorm); + elem->velocity.y *= this->speed * (1.0f / velocityNorm); + elem->velocity.z *= this->speed * (1.0f / velocityNorm); + } else { + elem->velocity.x = elem->velocity.z = 0.0f; + elem->velocity.y = this->speed; + } + + elem->unkVelocity.x = 30000.0f - Rand_ZeroOne() * 15000.0f; + elem->unkVelocity.y = 30000.0f - Rand_ZeroOne() * 15000.0f; + elem->unkVelocity.z = 30000.0f - Rand_ZeroOne() * 15000.0f; + elem->unkPosition.x = Rand_ZeroOne() * 65534.0f; + elem->unkPosition.y = Rand_ZeroOne() * 65534.0f; + elem->unkPosition.z = Rand_ZeroOne() * 65534.0f; + } + + this->timer = 0; + } +} + +void EffectSpark_Destroy(void* thisx) { +} + +// original name: "EffectSparkInfo_proc" +s32 EffectSpark_Update(void* thisx) { + EffectSpark* this = (EffectSpark*)thisx; + EffectSparkElement* elem; + s32 i; + + if (this == NULL) { + osSyncPrintf("EffectSparkInfo_proc():Spark Pointer is NULL\n"); + } + + for (i = 0; i < this->numElements; i++) { + elem = &this->elements[i]; + + elem->position.x += elem->velocity.x; + elem->position.y += elem->velocity.y; + elem->position.z += elem->velocity.z; + elem->velocity.y += this->gravity; + elem->unkPosition.x += elem->unkVelocity.x; + elem->unkPosition.y += elem->unkVelocity.y; + elem->unkPosition.z += elem->unkVelocity.z; + } + + this->timer++; + + if (this->duration < this->timer) { + return 1; + } else { + return 0; + } +} + +// original name: "EffectSparkInfo_disp" +void EffectSpark_Draw(void* thisx, GraphicsContext* gfxCtx) { + Vtx* vertices; + EffectSpark* this = (EffectSpark*)thisx; + GlobalContext* globalCtx = Effect_GetGlobalCtx(); + s32 i; + s32 j; + u8 sp1D3; + u8 sp1D2; + u8 sp1D1; + u8 sp1D0; + u8 sp1CF; + u8 sp1CE; + u8 sp1CD; + u8 sp1CC; + u8 sp1CB; + u8 sp1CA; + u8 sp1C9; + u8 sp1C8; + u8 sp1C7; + u8 sp1C6; + u8 sp1C5; + u8 sp1C4; + f32 ratio; + + OPEN_DISPS(gfxCtx, "../z_eff_spark.c", 293); + + if (this != NULL) { + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x26); + gDPSetCycleType(POLY_XLU_DISP++, G_CYC_2CYCLE); + gDPPipeSync(POLY_XLU_DISP++); + + gSPTexture(POLY_XLU_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + gDPLoadTextureBlock(POLY_XLU_DISP++, gUnknownCircle6Tex, G_IM_FMT_I, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetCombineMode(POLY_XLU_DISP++, G_CC_SHADEDECALA, G_CC_PASS2); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_ZB_CLD_SURF2); + gSPClearGeometryMode(POLY_XLU_DISP++, G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR); + gSPSetGeometryMode(POLY_XLU_DISP++, G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH); + gDPPipeSync(POLY_XLU_DISP++); + + vertices = Graph_Alloc(gfxCtx, this->numElements * sizeof(Vtx[4])); + if (vertices == NULL) { + // "Memory Allocation Failure graph_malloc" + osSyncPrintf("EffectSparkInfo_disp():メモリー確保失敗 graph_malloc\n"); + goto end; + } + + j = 0; + + ratio = (f32)this->timer / (f32)this->duration; + sp1D3 = this->colorStart[0].r + ((f32)this->colorEnd[0].r - (f32)this->colorStart[0].r) * ratio; + sp1D2 = this->colorStart[0].g + ((f32)this->colorEnd[0].g - (f32)this->colorStart[0].g) * ratio; + sp1D1 = this->colorStart[0].b + ((f32)this->colorEnd[0].b - (f32)this->colorStart[0].b) * ratio; + sp1D0 = this->colorStart[0].a + ((f32)this->colorEnd[0].a - (f32)this->colorStart[0].a) * ratio; + sp1CF = this->colorStart[1].r + ((f32)this->colorEnd[1].r - (f32)this->colorStart[1].r) * ratio; + sp1CE = this->colorStart[1].g + ((f32)this->colorEnd[1].g - (f32)this->colorStart[1].g) * ratio; + sp1CD = this->colorStart[1].b + ((f32)this->colorEnd[1].b - (f32)this->colorStart[1].b) * ratio; + sp1CC = this->colorStart[1].a + ((f32)this->colorEnd[1].a - (f32)this->colorStart[1].a) * ratio; + sp1CB = this->colorStart[2].r + ((f32)this->colorEnd[2].r - (f32)this->colorStart[2].r) * ratio; + sp1CA = this->colorStart[2].g + ((f32)this->colorEnd[2].g - (f32)this->colorStart[2].g) * ratio; + sp1C9 = this->colorStart[2].b + ((f32)this->colorEnd[2].b - (f32)this->colorStart[2].b) * ratio; + sp1C8 = this->colorStart[2].a + ((f32)this->colorEnd[2].a - (f32)this->colorStart[2].a) * ratio; + sp1C7 = this->colorStart[3].r + ((f32)this->colorEnd[3].r - (f32)this->colorStart[3].r) * ratio; + sp1C6 = this->colorStart[3].g + ((f32)this->colorEnd[3].g - (f32)this->colorStart[3].g) * ratio; + sp1C5 = this->colorStart[3].b + ((f32)this->colorEnd[3].b - (f32)this->colorStart[3].b) * ratio; + sp1C4 = this->colorStart[3].a + ((f32)this->colorEnd[3].a - (f32)this->colorStart[3].a) * ratio; + + for (i = 0; i < this->numElements; i++) { + MtxF sp12C; + MtxF spEC; + MtxF spAC; + MtxF sp6C; + EffectSparkElement* elem = &this->elements[i]; + Mtx* mtx; + f32 temp; + + SkinMatrix_SetTranslate(&spEC, elem->position.x, elem->position.y, elem->position.z); + temp = ((Rand_ZeroOne() * 2.5f) + 1.5f) / 64.0f; + SkinMatrix_SetScale(&spAC, temp, temp, 1.0f); + SkinMatrix_MtxFMtxFMult(&spEC, &globalCtx->billboardMtxF, &sp6C); + SkinMatrix_MtxFMtxFMult(&sp6C, &spAC, &sp12C); + + vertices[j].v.ob[0] = -32; + vertices[j].v.ob[1] = -32; + vertices[j].v.ob[2] = 0; + vertices[j].v.cn[0] = sp1D3; + vertices[j].v.cn[1] = sp1D2; + vertices[j].v.cn[2] = sp1D1; + vertices[j].v.cn[3] = sp1D0; + vertices[j].v.tc[0] = 0; + vertices[j].v.tc[1] = 1024; + vertices[j].v.flag = 0; + + vertices[j + 1].v.ob[0] = 32; + vertices[j + 1].v.ob[1] = 32; + vertices[j + 1].v.ob[2] = 0; + vertices[j + 1].v.cn[0] = sp1CF; + vertices[j + 1].v.cn[1] = sp1CE; + vertices[j + 1].v.cn[2] = sp1CD; + vertices[j + 1].v.cn[3] = sp1CC; + vertices[j + 1].v.tc[0] = 1024; + vertices[j + 1].v.tc[1] = 0; + vertices[j + 1].v.flag = 0; + + vertices[j + 2].v.ob[0] = -32; + vertices[j + 2].v.ob[1] = 32; + vertices[j + 2].v.ob[2] = 0; + vertices[j + 2].v.cn[0] = sp1CB; + vertices[j + 2].v.cn[1] = sp1CA; + vertices[j + 2].v.cn[2] = sp1C9; + vertices[j + 2].v.cn[3] = sp1C8; + vertices[j + 2].v.tc[0] = 0; + vertices[j + 2].v.tc[1] = 0; + vertices[j + 2].v.flag = 0; + + vertices[j + 3].v.ob[0] = 32; + vertices[j + 3].v.ob[1] = -32; + vertices[j + 3].v.ob[2] = 0; + vertices[j + 3].v.cn[0] = sp1C7; + vertices[j + 3].v.cn[1] = sp1C6; + vertices[j + 3].v.cn[2] = sp1C5; + vertices[j + 3].v.cn[3] = sp1C4; + vertices[j + 3].v.tc[0] = 1024; + vertices[j + 3].v.tc[1] = 1024; + vertices[j + 3].v.flag = 0; + + j += 4; + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &sp12C); + if (mtx == NULL) { + goto end; + } + + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPVertex(POLY_XLU_DISP++, &vertices[4 * i], 4, 0); + gSP2Triangles(POLY_XLU_DISP++, 2, 0, 3, 0, 2, 3, 1, 0); + } + + gDPPipeSync(POLY_XLU_DISP++); + } + +end: + CLOSE_DISPS(gfxCtx, "../z_eff_spark.c", 498); +} diff --git a/soh/src/code/z_eff_ss_dead.c b/soh/src/code/z_eff_ss_dead.c new file mode 100644 index 000000000..b3978547b --- /dev/null +++ b/soh/src/code/z_eff_ss_dead.c @@ -0,0 +1,121 @@ +#include "global.h" + +void func_80026230(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3) { + f32 cos; + Gfx* displayListHead; + f32 absCos; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 113); + + displayListHead = POLY_OPA_DISP; + cos = Math_CosS((0x8000 / arg3) * arg2); + absCos = ABS(cos); + + gDPPipeSync(displayListHead++); + + if (color == NULL) { + gDPSetFogColor(displayListHead++, 255, 0, 0, 0); + } else { + gDPSetFogColor(displayListHead++, color->r, color->g, color->b, color->a); + } + + gSPFogPosition(displayListHead++, 0, (s16)(absCos * 3000.0f) + 1500); + + POLY_OPA_DISP = displayListHead; + + if (1) {} // Necessary to match + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 129); +} + +void func_80026400(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3) { + Gfx* displayListHead; + f32 cos; + + if (arg3 != 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 141); + + cos = Math_CosS((0x4000 / arg3) * arg2); + displayListHead = POLY_OPA_DISP; + + gDPPipeSync(displayListHead++); + gDPSetFogColor(displayListHead++, color->r, color->g, color->b, color->a); + gSPFogPosition(displayListHead++, 0, (s16)(2800.0f * ABS(cos)) + 1700); + + POLY_OPA_DISP = displayListHead; + + if (1) {} // Necessary to match + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 153); + } +} + +void func_80026608(GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 159); + + gDPPipeSync(POLY_OPA_DISP++); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 164); +} + +void func_80026690(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3) { + f32 cos; + Gfx* displayListHead; + f32 absCos; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 178); + + displayListHead = POLY_XLU_DISP; + cos = Math_CosS((0x8000 / arg3) * arg2); + absCos = ABS(cos); + + gDPPipeSync(displayListHead++); + + if (color == NULL) { + gDPSetFogColor(displayListHead++, 255, 0, 0, 0); + } else { + gDPSetFogColor(displayListHead++, color->r, color->g, color->b, color->a); + } + + gSPFogPosition(displayListHead++, 0, (s16)(absCos * 3000.0f) + 1500); + + POLY_XLU_DISP = displayListHead; + + if (1) {} // Necessary to match + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 194); +} + +void func_80026860(GlobalContext* globalCtx, Color_RGBA8* color, s16 arg2, s16 arg3) { + f32 cos; + Gfx* displayListHead; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 201); + + displayListHead = POLY_XLU_DISP; + cos = Math_CosS((0x4000 / arg3) * arg2); + + gDPPipeSync(displayListHead++); + gDPSetFogColor(displayListHead++, color->r, color->g, color->b, color->a); + gSPFogPosition(displayListHead++, 0, (s16)(2800.0f * ABS(cos)) + 1700); + + POLY_XLU_DISP = displayListHead; + + if (1) {} // Necessary to match + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 212); +} + +void func_80026A6C(GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 217); + + gDPPipeSync(POLY_XLU_DISP++); + POLY_XLU_DISP = Gameplay_SetFog(globalCtx, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead.c", 222); +} diff --git a/soh/src/code/z_effect.c b/soh/src/code/z_effect.c new file mode 100644 index 000000000..d024d0892 --- /dev/null +++ b/soh/src/code/z_effect.c @@ -0,0 +1,259 @@ +#include "global.h" + +EffectContext sEffectContext; + +EffectInfo sEffectInfoTable[] = { + { + sizeof(EffectSpark), + EffectSpark_Init, + EffectSpark_Destroy, + EffectSpark_Update, + EffectSpark_Draw, + }, + { + sizeof(EffectBlure), + EffectBlure_Init1, + EffectBlure_Destroy, + EffectBlure_Update, + EffectBlure_Draw, + }, + { + sizeof(EffectBlure), + EffectBlure_Init2, + EffectBlure_Destroy, + EffectBlure_Update, + EffectBlure_Draw, + }, + { + sizeof(EffectShieldParticle), + EffectShieldParticle_Init, + EffectShieldParticle_Destroy, + EffectShieldParticle_Update, + EffectShieldParticle_Draw, + }, +}; + +GlobalContext* Effect_GetGlobalCtx(void) { + return sEffectContext.globalCtx; +} + +void* Effect_GetByIndex(s32 index) { + if (index == TOTAL_EFFECT_COUNT) { + return NULL; + } + + if (index < SPARK_COUNT) { + if (sEffectContext.sparks[index].status.active == true) { + return &sEffectContext.sparks[index].effect; + } else { + return NULL; + } + } + + index -= SPARK_COUNT; + if (index < BLURE_COUNT) { + if (sEffectContext.blures[index].status.active == true) { + return &sEffectContext.blures[index].effect; + } else { + return NULL; + } + } + + index -= BLURE_COUNT; + if (index < SHIELD_PARTICLE_COUNT) { + if (sEffectContext.shieldParticles[index].status.active == true) { + return &sEffectContext.shieldParticles[index].effect; + } else { + return NULL; + } + } + + return NULL; +} + +void Effect_InitStatus(EffectStatus* status) { + status->active = false; + status->unk_01 = 0; + status->unk_02 = 0; +} + +void Effect_InitContext(GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < SPARK_COUNT; i++) { + Effect_InitStatus(&sEffectContext.sparks[i].status); + } + + for (i = 0; i < BLURE_COUNT; i++) { + Effect_InitStatus(&sEffectContext.blures[i].status); + } + + for (i = 0; i < SHIELD_PARTICLE_COUNT; i++) { + //! @bug This is supposed to initialize shieldParticles, not blures again + Effect_InitStatus(&sEffectContext.blures[i].status); + } + + sEffectContext.globalCtx = globalCtx; +} + +void Effect_Add(GlobalContext* globalCtx, s32* pIndex, s32 type, u8 arg3, u8 arg4, void* initParams) { + s32 i; + u32 slotFound; + void* effect = NULL; + EffectStatus* status = NULL; + + *pIndex = TOTAL_EFFECT_COUNT; + + if (FrameAdvance_IsEnabled(globalCtx) != true) { + slotFound = false; + switch (type) { + case EFFECT_SPARK: + for (i = 0; i < SPARK_COUNT; i++) { + if (sEffectContext.sparks[i].status.active == false) { + slotFound = true; + *pIndex = i; + effect = &sEffectContext.sparks[i].effect; + status = &sEffectContext.sparks[i].status; + break; + } + } + break; + case EFFECT_BLURE1: + case EFFECT_BLURE2: + for (i = 0; i < BLURE_COUNT; i++) { + if (sEffectContext.blures[i].status.active == false) { + slotFound = true; + *pIndex = i + SPARK_COUNT; + effect = &sEffectContext.blures[i].effect; + status = &sEffectContext.blures[i].status; + break; + } + } + break; + case EFFECT_SHIELD_PARTICLE: + for (i = 0; i < SHIELD_PARTICLE_COUNT; i++) { + if (sEffectContext.shieldParticles[i].status.active == false) { + slotFound = true; + *pIndex = i + SPARK_COUNT + BLURE_COUNT; + effect = &sEffectContext.shieldParticles[i].effect; + status = &sEffectContext.shieldParticles[i].status; + break; + } + } + break; + } + + if (!slotFound) { + // "EffectAdd(): I cannot secure it. Be careful. Type %d" + osSyncPrintf("EffectAdd():確保できません。注意してください。Type%d\n", type); + osSyncPrintf("エフェクト追加せずに終了します。\n"); // "Exit without adding the effect." + } else { + sEffectInfoTable[type].init(effect, initParams); + status->unk_02 = arg3; + status->unk_01 = arg4; + status->active = true; + } + } +} + +void Effect_DrawAll(GraphicsContext* gfxCtx) { + s32 i; + + for (i = 0; i < SPARK_COUNT; i++) { + if (sEffectContext.sparks[i].status.active) { + sEffectInfoTable[EFFECT_SPARK].draw(&sEffectContext.sparks[i].effect, gfxCtx); + } + } + + for (i = 0; i < BLURE_COUNT; i++) { + if (sEffectContext.blures[i].status.active) { + sEffectInfoTable[EFFECT_BLURE1].draw(&sEffectContext.blures[i].effect, gfxCtx); + } + if (1) {} // Necessary to match + if (1) {} + } + + for (i = 0; i < SHIELD_PARTICLE_COUNT; i++) { + if (sEffectContext.shieldParticles[i].status.active) { + if (gfxCtx) {} // Necessary to match + sEffectInfoTable[EFFECT_SHIELD_PARTICLE].draw(&sEffectContext.shieldParticles[i].effect, gfxCtx); + } + } +} + +void Effect_UpdateAll(GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < SPARK_COUNT; i++) { + if (sEffectContext.sparks[i].status.active) { + if (sEffectInfoTable[EFFECT_SPARK].update(&sEffectContext.sparks[i].effect) == 1) { + Effect_Delete(globalCtx, i); + } + } + } + + for (i = 0; i < BLURE_COUNT; i++) { + if (sEffectContext.blures[i].status.active) { + if (sEffectInfoTable[EFFECT_BLURE1].update(&sEffectContext.blures[i].effect) == 1) { + Effect_Delete(globalCtx, i + SPARK_COUNT); + } + } + } + + for (i = 0; i < SHIELD_PARTICLE_COUNT; i++) { + if (sEffectContext.shieldParticles[i].status.active) { + if (sEffectInfoTable[EFFECT_SHIELD_PARTICLE].update(&sEffectContext.shieldParticles[i].effect) == 1) { + Effect_Delete(globalCtx, i + SPARK_COUNT + BLURE_COUNT); + } + } + } +} + +void Effect_Delete(GlobalContext* globalCtx, s32 index) { + if (index == TOTAL_EFFECT_COUNT) { + return; + } + + if (index < SPARK_COUNT) { + sEffectContext.sparks[index].status.active = false; + sEffectInfoTable[EFFECT_SPARK].destroy(&sEffectContext.sparks[index].effect); + return; + } + + index -= SPARK_COUNT; + if (index < BLURE_COUNT) { + sEffectContext.blures[index].status.active = false; + sEffectInfoTable[EFFECT_BLURE1].destroy(&sEffectContext.blures[index].effect); + return; + } + + index -= BLURE_COUNT; + if (index < SHIELD_PARTICLE_COUNT) { + sEffectContext.shieldParticles[index].status.active = false; + sEffectInfoTable[EFFECT_SHIELD_PARTICLE].destroy(&sEffectContext.shieldParticles[index].effect); + return; + } +} + +void Effect_DeleteAll(GlobalContext* globalCtx) { + s32 i; + + osSyncPrintf("エフェクト総て解放\n"); // "All effect release" + + for (i = 0; i < SPARK_COUNT; i++) { + sEffectContext.sparks[i].status.active = false; + sEffectInfoTable[EFFECT_SPARK].destroy(&sEffectContext.sparks[i].effect); + } + + for (i = 0; i < BLURE_COUNT; i++) { + sEffectContext.blures[i].status.active = false; + sEffectInfoTable[EFFECT_BLURE1].destroy(&sEffectContext.blures[i].effect); + } + + for (i = 0; i < SHIELD_PARTICLE_COUNT; i++) { + sEffectContext.shieldParticles[i].status.active = false; + sEffectInfoTable[EFFECT_SHIELD_PARTICLE].destroy(&sEffectContext.shieldParticles[i].effect); + } + + osSyncPrintf("エフェクト総て解放 終了\n"); // "All effects release End" +} diff --git a/soh/src/code/z_effect_soft_sprite.c b/soh/src/code/z_effect_soft_sprite.c new file mode 100644 index 000000000..aa0b922fe --- /dev/null +++ b/soh/src/code/z_effect_soft_sprite.c @@ -0,0 +1,338 @@ +#include "global.h" +#include "vt.h" + +EffectSsInfo sEffectSsInfo = { 0 }; // "EffectSS2Info" + +void EffectSs_InitInfo(GlobalContext* globalCtx, s32 tableSize) { + u32 i; + EffectSs* effectSs; + EffectSsOverlay* overlay; + + for (i = 0; i < ARRAY_COUNT(gEffectSsOverlayTable); i++) { + overlay = &gEffectSsOverlayTable[i]; + osSyncPrintf("effect index %3d:size=%6dbyte romsize=%6dbyte\n", i, + (uintptr_t)overlay->vramEnd - (uintptr_t)overlay->vramStart, overlay->vromEnd - overlay->vromStart); + } + + sEffectSsInfo.table = + GameState_Alloc(&globalCtx->state, tableSize * sizeof(EffectSs), "../z_effect_soft_sprite.c", 289); + ASSERT(sEffectSsInfo.table != NULL, "EffectSS2Info.data_table != NULL", "../z_effect_soft_sprite.c", 290); + + sEffectSsInfo.searchStartIndex = 0; + sEffectSsInfo.tableSize = tableSize; + + for (effectSs = &sEffectSsInfo.table[0]; effectSs < &sEffectSsInfo.table[sEffectSsInfo.tableSize]; effectSs++) { + EffectSs_Reset(effectSs); + } + + overlay = &gEffectSsOverlayTable[0]; + for (i = 0; i < ARRAY_COUNT(gEffectSsOverlayTable); i++) { + overlay->loadedRamAddr = NULL; + overlay++; + } +} + +void EffectSs_ClearAll(GlobalContext* globalCtx) { + u32 i; + EffectSs* effectSs; + EffectSsOverlay* overlay; + void* addr; + + sEffectSsInfo.table = NULL; + sEffectSsInfo.searchStartIndex = 0; + sEffectSsInfo.tableSize = 0; + + // This code doesn't actually work, since table was just set to NULL and tableSize to 0 + for (effectSs = &sEffectSsInfo.table[0]; effectSs < &sEffectSsInfo.table[sEffectSsInfo.tableSize]; effectSs++) { + EffectSs_Delete(effectSs); + } + + overlay = &gEffectSsOverlayTable[0]; + for (i = 0; i < ARRAY_COUNT(gEffectSsOverlayTable); i++) { + addr = overlay->loadedRamAddr; + + if (addr != NULL) { + ZeldaArena_FreeDebug(addr, "../z_effect_soft_sprite.c", 337); + } + + overlay->loadedRamAddr = NULL; + overlay++; + } +} + +void EffectSs_Delete(EffectSs* effectSs) { + if (effectSs->flags & 2) { + Audio_StopSfxByPos(&effectSs->pos); + } + + if (effectSs->flags & 4) { + Audio_StopSfxByPos(&effectSs->vec); + } + + EffectSs_Reset(effectSs); +} + +void EffectSs_Reset(EffectSs* effectSs) { + u32 i; + + effectSs->type = EFFECT_SS_TYPE_MAX; + effectSs->accel.x = effectSs->accel.y = effectSs->accel.z = 0; + effectSs->velocity.x = effectSs->velocity.y = effectSs->velocity.z = 0; + effectSs->vec.x = effectSs->vec.y = effectSs->vec.z = 0; + effectSs->pos.x = effectSs->pos.y = effectSs->pos.z = 0; + effectSs->life = -1; + effectSs->flags = 0; + effectSs->priority = 128; + effectSs->draw = NULL; + effectSs->update = NULL; + effectSs->gfx = NULL; + effectSs->actor = NULL; + + for (i = 0; i < ARRAY_COUNT(effectSs->regs); i++) { + effectSs->regs[i] = 0; + } +} + +s32 EffectSs_FindSlot(s32 priority, s32* pIndex) { + s32 foundFree; + s32 i; + + if (sEffectSsInfo.searchStartIndex >= sEffectSsInfo.tableSize) { + sEffectSsInfo.searchStartIndex = 0; + } + + // Search for a free slot + i = sEffectSsInfo.searchStartIndex; + foundFree = false; + while (true) { + if (sEffectSsInfo.table[i].life == -1) { + foundFree = true; + break; + } + + i++; + + if (i >= sEffectSsInfo.tableSize) { + i = 0; // Loop around the whole table + } + + // After a full loop, break out + if (i == sEffectSsInfo.searchStartIndex) { + break; + } + } + + if (foundFree == true) { + *pIndex = i; + return 0; + } + + // If all slots are in use, search for a slot with a lower priority + // Note that a lower priority is representend by a higher value + i = sEffectSsInfo.searchStartIndex; + while (true) { + // Equal priority should only be considered "lower" if flag 0 is set + if ((priority <= sEffectSsInfo.table[i].priority) && + !((priority == sEffectSsInfo.table[i].priority) && (sEffectSsInfo.table[i].flags & 1))) { + break; + } + + i++; + + if (i >= sEffectSsInfo.tableSize) { + i = 0; // Loop around the whole table + } + + // After a full loop, return 1 to indicate that we failed to find a suitable slot + if (i == sEffectSsInfo.searchStartIndex) { + return 1; + } + } + + *pIndex = i; + return 0; +} + +void EffectSs_Insert(GlobalContext* globalCtx, EffectSs* effectSs) { + s32 index; + + if (FrameAdvance_IsEnabled(globalCtx) != true) { + if (EffectSs_FindSlot(effectSs->priority, &index) == 0) { + sEffectSsInfo.searchStartIndex = index + 1; + sEffectSsInfo.table[index] = *effectSs; + } + } +} + +// original name: "EffectSoftSprite2_makeEffect" +void EffectSs_Spawn(GlobalContext* globalCtx, s32 type, s32 priority, void* initParams) { + s32 index; + u32 overlaySize; + EffectSsOverlay* overlayEntry; + EffectSsInit* initInfo; + + overlayEntry = &gEffectSsOverlayTable[type]; + + ASSERT(type < EFFECT_SS_TYPE_MAX, "type < EFFECT_SS2_TYPE_LAST_LABEL", "../z_effect_soft_sprite.c", 556); + + if (EffectSs_FindSlot(priority, &index) != 0) { + // Abort because we couldn't find a suitable slot to add this effect in + return; + } + + sEffectSsInfo.searchStartIndex = index + 1; + overlaySize = (uintptr_t)overlayEntry->vramEnd - (uintptr_t)overlayEntry->vramStart; + + if (overlayEntry->vramStart == NULL) { + // "Not an overlay" + osSyncPrintf("EffectSoftSprite2_makeEffect():オーバーレイではありません。\n"); + initInfo = overlayEntry->initInfo; + } else { + if (overlayEntry->loadedRamAddr == NULL) { + overlayEntry->loadedRamAddr = ZeldaArena_MallocRDebug(overlaySize, "../z_effect_soft_sprite.c", 585); + + if (overlayEntry->loadedRamAddr == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + // "The memory of %d byte cannot be secured. Therefore, the program cannot be loaded. + // What a dangerous situation! Naturally, effects will not produced either." + osSyncPrintf("EffectSoftSprite2_makeEffect():zelda_malloc_r()により,%" + "dbyteのメモリ確保ができま\nせん。そのため、プログラムのロードも\n出来ません。ただいま危険" + "な状態です!\nもちろん,エフェクトも出ません。\n", + overlaySize); + osSyncPrintf(VT_RST); + return; + } + + Overlay_Load(overlayEntry->vromStart, overlayEntry->vromEnd, overlayEntry->vramStart, overlayEntry->vramEnd, + overlayEntry->loadedRamAddr); + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("EFFECT SS OVL:SegRom %08x %08x, Seg %08x %08x, RamStart %08x, type: %d\n", + overlayEntry->vromStart, overlayEntry->vromEnd, overlayEntry->vramStart, overlayEntry->vramEnd, + overlayEntry->loadedRamAddr, type); + osSyncPrintf(VT_RST); + } + + initInfo = (void*)(uintptr_t)((overlayEntry->initInfo != NULL) + ? (void*)((uintptr_t)overlayEntry->initInfo - + ((intptr_t)overlayEntry->vramStart - (intptr_t)overlayEntry->loadedRamAddr)) + : NULL); + } + + if (initInfo->init == NULL) { + // "Effects have already been loaded, but the constructor is NULL so the addition will not occur. + // Please fix this. (Waste of memory) %08x %d" + osSyncPrintf("EffectSoftSprite2_makeEffect():すでにエフェクトはロード済みで\nすが," + "コンストラクターがNULLなので追加をやめます。\n直してください。(メモリーの無駄) %08x %d\n", + initInfo, type); + return; + } + + // Delete the previous effect in the slot, in case the slot wasn't free + EffectSs_Delete(&sEffectSsInfo.table[index]); + + sEffectSsInfo.table[index].type = type; + sEffectSsInfo.table[index].priority = priority; + + if (initInfo->init(globalCtx, index, &sEffectSsInfo.table[index], initParams) == 0) { + osSyncPrintf(VT_FGCOL(GREEN)); + // "Construction failed for some reason. The constructor returned an error. + // Ceasing effect addition." + osSyncPrintf("EffectSoftSprite2_makeEffect():" + "何らかの理由でコンストラクト失敗。コンストラクターがエラーを返しました。エフェクトの追加を中" + "止します。\n"); + osSyncPrintf(VT_RST); + EffectSs_Reset(&sEffectSsInfo.table[index]); + } +} + +void EffectSs_Update(GlobalContext* globalCtx, s32 index) { + EffectSs* effectSs = &sEffectSsInfo.table[index]; + + if (effectSs->update != NULL) { + effectSs->velocity.x += effectSs->accel.x; + effectSs->velocity.y += effectSs->accel.y; + effectSs->velocity.z += effectSs->accel.z; + + effectSs->pos.x += effectSs->velocity.x; + effectSs->pos.y += effectSs->velocity.y; + effectSs->pos.z += effectSs->velocity.z; + + effectSs->update(globalCtx, index, effectSs); + } +} + +void EffectSs_UpdateAll(GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < sEffectSsInfo.tableSize; i++) { + if (sEffectSsInfo.table[i].life > -1) { + sEffectSsInfo.table[i].life--; + + if (sEffectSsInfo.table[i].life < 0) { + EffectSs_Delete(&sEffectSsInfo.table[i]); + } + } + + if (sEffectSsInfo.table[i].life > -1) { + EffectSs_Update(globalCtx, i); + } + } +} + +void EffectSs_Draw(GlobalContext* globalCtx, s32 index) { + EffectSs* effectSs = &sEffectSsInfo.table[index]; + + if (effectSs->draw != NULL) { + effectSs->draw(globalCtx, index, effectSs); + } +} + +// original name: "EffectSoftSprite2_disp" +void EffectSs_DrawAll(GlobalContext* globalCtx) { + Lights* lights = LightContext_NewLights(&globalCtx->lightCtx, globalCtx->state.gfxCtx); + s32 i; + + Lights_BindAll(lights, globalCtx->lightCtx.listHead, NULL); + Lights_Draw(lights, globalCtx->state.gfxCtx); + + for (i = 0; i < sEffectSsInfo.tableSize; i++) { + if (sEffectSsInfo.table[i].life > -1) { + if ((sEffectSsInfo.table[i].pos.x > 32000.0f) || (sEffectSsInfo.table[i].pos.x < -32000.0f) || + (sEffectSsInfo.table[i].pos.y > 32000.0f) || (sEffectSsInfo.table[i].pos.y < -32000.0f) || + (sEffectSsInfo.table[i].pos.z > 32000.0f) || (sEffectSsInfo.table[i].pos.z < -32000.0f)) { + osSyncPrintf(VT_FGCOL(RED)); + // "Since the position is outside the area, delete it. + // Effect label No. %d: Please respond by the program. + // Here is ==> pos (%f, %f, %f) and the label is in z_effect_soft_sprite_dlftbls.decl." + osSyncPrintf("EffectSoftSprite2_disp():位置が領域外のため " + "削除します。エフェクトラベルNo.%d:プログラムの方で対応をお願いします。ここです ==> " + "pos(%f, %f, %f)で、ラベルはz_effect_soft_sprite_dlftbls.declにあります。\n", + sEffectSsInfo.table[i].type, sEffectSsInfo.table[i].pos.x, sEffectSsInfo.table[i].pos.y, + sEffectSsInfo.table[i].pos.z); + osSyncPrintf(VT_FGCOL(GREEN)); + // "If you are using pos for something else, consult me." + osSyncPrintf("もし、posを別のことに使っている場合相談に応じます。\n"); + osSyncPrintf(VT_RST); + + EffectSs_Delete(&sEffectSsInfo.table[i]); + } else { + EffectSs_Draw(globalCtx, i); + } + } + } +} + +s16 func_80027DD4(s16 arg0, s16 arg1, s32 arg2) { + s16 ret = (arg2 == 0) ? arg1 : (arg0 + (s32)((arg1 - arg0) / (f32)arg2)); + + return ret; +} + +s16 func_80027E34(s16 arg0, s16 arg1, f32 arg2) { + return (arg1 - arg0) * arg2 + arg0; +} + +u8 func_80027E84(u8 arg0, u8 arg1, f32 arg2) { + return arg2 * ((f32)arg1 - (f32)arg0) + arg0; +} diff --git a/soh/src/code/z_effect_soft_sprite_dlftbls.c b/soh/src/code/z_effect_soft_sprite_dlftbls.c new file mode 100644 index 000000000..3a090041e --- /dev/null +++ b/soh/src/code/z_effect_soft_sprite_dlftbls.c @@ -0,0 +1,51 @@ +#include "global.h" + +// Linker symbol declarations (used in the table below) +#define DEFINE_EFFECT_SS(name, _1) DECLARE_OVERLAY_SEGMENT(name) +#define DEFINE_EFFECT_SS_UNSET(_0) + +#include "tables/effect_ss_table.h" + +#undef DEFINE_EFFECT_SS +#undef DEFINE_EFFECT_SS_UNSET + +// Init Vars declarations (also used in the table below) +#define DEFINE_EFFECT_SS(name, _1) extern EffectSsInit name##_InitVars; +#define DEFINE_EFFECT_SS_UNSET(_0) + +#include "tables/effect_ss_table.h" + +#undef DEFINE_EFFECT_SS +#undef DEFINE_EFFECT_SS_UNSET + +// Effect SS Overlay Table definition +//#define DEFINE_EFFECT_SS(name, _1) \ +// { \ +// (uintptr_t)_ovl_##name##SegmentRomStart, \ +// (uintptr_t)_ovl_##name##SegmentRomEnd, \ +// _ovl_##name##SegmentStart, \ +// _ovl_##name##SegmentEnd, \ +// NULL, \ +// &name##_InitVars, \ +// 1, \ +// }, + +#define DEFINE_EFFECT_SS(name, _1) \ + { \ + (uintptr_t)0, \ + (uintptr_t)0, \ + 0, \ + 0, \ + NULL, \ + &name##_InitVars, \ + 1, \ + }, + +#define DEFINE_EFFECT_SS_UNSET(_0) { 0 }, + +EffectSsOverlay gEffectSsOverlayTable[] = { +#include "tables/effect_ss_table.h" +}; + +#undef DEFINE_EFFECT_SS +#undef DEFINE_EFFECT_SS_UNSET diff --git a/soh/src/code/z_effect_soft_sprite_old_init.c b/soh/src/code/z_effect_soft_sprite_old_init.c new file mode 100644 index 000000000..8d052a8bf --- /dev/null +++ b/soh/src/code/z_effect_soft_sprite_old_init.c @@ -0,0 +1,1186 @@ +#include "global.h" +#include "overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.h" +#include "overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.h" +#include "overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.h" +#include "overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h" +#include "overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.h" +#include "overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.h" +#include "overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.h" +#include "overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.h" +#include "overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.h" +#include "overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.h" +#include "overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.h" +#include "overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.h" +#include "overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.h" +#include "overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.h" +#include "overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.h" +#include "overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.h" +#include "overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.h" +#include "overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.h" +#include "overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h" +#include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" +#include "overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.h" +#include "overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.h" +#include "overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.h" +#include "overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.h" +#include "overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.h" +#include "overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.h" +#include "overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" +#include "overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.h" + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +// effects that use this draw function are responsible for making sure their regs line up with the usage here + +void EffectSs_DrawGEffect(GlobalContext* globalCtx, EffectSs* this, void* texture) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 scale; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + s32 pad1; + Mtx* mtx; + void* object = globalCtx->objectCtx.status[this->rgObjBankIdx].segment; + + OPEN_DISPS(gfxCtx, "../z_effect_soft_sprite_old_init.c", 196); + + scale = this->rgScale * 0.0025f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, scale); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x06, object); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(texture)); + func_80094C50(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rgPrimColorR, this->rgPrimColorG, this->rgPrimColorB, + this->rgPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rgEnvColorR, this->rgEnvColorG, this->rgEnvColorB, this->rgEnvColorA); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_effect_soft_sprite_old_init.c", 243); +} + +// EffectSsDust Spawn Functions + +void EffectSsDust_Spawn(GlobalContext* globalCtx, u16 drawFlags, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life, + u8 updateMode) { + EffectSsDustInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.primColor = *primColor; + initParams.envColor = *envColor; + initParams.drawFlags = drawFlags; + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.life = life; + initParams.updateMode = updateMode; + + EffectSs_Spawn(globalCtx, EFFECT_SS_DUST, 128, &initParams); +} + +void func_8002829C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep) { + EffectSsDust_Spawn(globalCtx, 0, pos, velocity, accel, primColor, envColor, scale, scaleStep, 10, 0); +} + +void func_80028304(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep) { + EffectSsDust_Spawn(globalCtx, 1, pos, velocity, accel, primColor, envColor, scale, scaleStep, 10, 0); +} + +void func_8002836C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life) { + EffectSsDust_Spawn(globalCtx, 0, pos, velocity, accel, primColor, envColor, scale, scaleStep, life, 0); +} + +void func_800283D4(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life) { + EffectSsDust_Spawn(globalCtx, 1, pos, velocity, accel, primColor, envColor, scale, scaleStep, life, 0); +} + +void func_8002843C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 life) { + EffectSsDust_Spawn(globalCtx, 2, pos, velocity, accel, primColor, envColor, scale, scaleStep, life, 0); +} + +// unused +void func_800284A4(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep) { + EffectSsDust_Spawn(globalCtx, 0, pos, velocity, accel, primColor, envColor, scale, scaleStep, 10, 1); +} + +// unused +void func_80028510(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep) { + EffectSsDust_Spawn(globalCtx, 1, pos, velocity, accel, primColor, envColor, scale, scaleStep, 10, 1); +} + +static Color_RGBA8 sDustBrownPrim = { 170, 130, 90, 255 }; +static Color_RGBA8 sDustBrownEnv = { 100, 60, 20, 255 }; + +void func_8002857C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + EffectSsDust_Spawn(globalCtx, 4, pos, velocity, accel, &sDustBrownPrim, &sDustBrownEnv, 100, 5, 10, 0); +} + +// unused +void func_800285EC(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + EffectSsDust_Spawn(globalCtx, 5, pos, velocity, accel, &sDustBrownPrim, &sDustBrownEnv, 100, 5, 10, 0); +} + +void func_8002865C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep) { + EffectSsDust_Spawn(globalCtx, 4, pos, velocity, accel, &sDustBrownPrim, &sDustBrownEnv, scale, scaleStep, 10, 0); +} + +void func_800286CC(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep) { + EffectSsDust_Spawn(globalCtx, 5, pos, velocity, accel, &sDustBrownPrim, &sDustBrownEnv, scale, scaleStep, 10, 0); +} + +void func_8002873C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 life) { + EffectSsDust_Spawn(globalCtx, 4, pos, velocity, accel, &sDustBrownPrim, &sDustBrownEnv, scale, scaleStep, life, 0); +} + +void func_800287AC(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 life) { + EffectSsDust_Spawn(globalCtx, 5, pos, velocity, accel, &sDustBrownPrim, &sDustBrownEnv, scale, scaleStep, life, 0); +} + +// unused +void func_8002881C(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor) { + func_8002829C(globalCtx, pos, velocity, accel, primColor, envColor, 100, 5); +} + +// unused +void func_80028858(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor) { + func_80028304(globalCtx, pos, velocity, accel, primColor, envColor, 100, 5); +} + +void func_80028894(Vec3f* srcPos, f32 randScale, Vec3f* newPos, Vec3f* velocity, Vec3f* accel) { + s16 randAngle; + f32 rand = Rand_ZeroOne() * randScale; + + randAngle = (Rand_ZeroOne() * 65536.0f); + + *newPos = *srcPos; + + newPos->x += Math_SinS(randAngle) * rand; + newPos->z += Math_CosS(randAngle) * rand; + + velocity->y = 1.0f; + velocity->x = Math_SinS(randAngle); + velocity->z = Math_CosS(randAngle); + + accel->x = 0.0f; + accel->y = 0.0f; + accel->z = 0.0f; +} + +void func_80028990(GlobalContext* globalCtx, f32 randScale, Vec3f* srcPos) { + s32 i; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + for (i = 0; i < 20; i++) { + func_80028894(srcPos, randScale, &pos, &velocity, &accel); + func_8002873C(globalCtx, &pos, &velocity, &accel, 100, 30, 7); + } +} + +void func_80028A54(GlobalContext* globalCtx, f32 randScale, Vec3f* srcPos) { + s32 i; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + for (i = 0; i < 20; i++) { + func_80028894(srcPos, randScale, &pos, &velocity, &accel); + func_800287AC(globalCtx, &pos, &velocity, &accel, 100, 30, 7); + } +} + +// EffectSsKiraKira Spawn Functions + +void EffectSsKiraKira_SpawnSmallYellow(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + Color_RGBA8 primColor = { 255, 255, 200, 255 }; + Color_RGBA8 envColor = { 255, 200, 0, 0 }; + + EffectSsKiraKira_SpawnDispersed(globalCtx, pos, velocity, accel, &primColor, &envColor, 1000, 16); +} + +void EffectSsKiraKira_SpawnSmall(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor) { + EffectSsKiraKira_SpawnDispersed(globalCtx, pos, velocity, accel, primColor, envColor, 1000, 16); +} + +void EffectSsKiraKira_SpawnDispersed(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s32 life) { + EffectSsKiraKiraInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + initParams.velocity.y = ((Rand_ZeroOne() * initParams.velocity.y) + initParams.velocity.y) * 0.5f; + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.accel.y = ((Rand_ZeroOne() * initParams.accel.y) + initParams.accel.y) * 0.5f; + initParams.life = life; + initParams.updateMode = 0; + initParams.rotSpeed = 0x1518; + initParams.yaw = Rand_ZeroOne() * 16384.0f; + initParams.scale = scale; + initParams.primColor = *primColor; + initParams.envColor = *envColor; + initParams.alphaStep = (-(255.0f / initParams.life)) + (-(255.0f / initParams.life)); + + EffectSs_Spawn(globalCtx, EFFECT_SS_KIRAKIRA, 128, &initParams); +} + +void EffectSsKiraKira_SpawnFocused(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s32 life) { + EffectSsKiraKiraInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.life = life; + initParams.updateMode = 1; + initParams.rotSpeed = 0x1518; + initParams.yaw = Rand_ZeroOne() * 16384.0f; + initParams.scale = scale; + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.alphaStep = (-(255.0f / initParams.life)) + (-(255.0f / initParams.life)); + + EffectSs_Spawn(globalCtx, EFFECT_SS_KIRAKIRA, 128, &initParams); +} + +// EffectSsBomb Spawn Functions + +// unused +void EffectSsBomb_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + EffectSsBombInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + + EffectSs_Spawn(globalCtx, EFFECT_SS_BOMB, 128, &initParams); +} + +// EffectSsBomb2 Spawn Functions + +// unused +void EffectSsBomb2_SpawnFade(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + EffectSsBomb2InitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = 100; + initParams.scaleStep = 0; + initParams.drawMode = 0; + + EffectSs_Spawn(globalCtx, EFFECT_SS_BOMB2, 10, &initParams); +} + +void EffectSsBomb2_SpawnLayered(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + s16 scaleStep) { + EffectSsBomb2InitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.drawMode = 1; + + EffectSs_Spawn(globalCtx, EFFECT_SS_BOMB2, 10, &initParams); +} + +// EffectSsBlast Spawn Functions + +void EffectSsBlast_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, Color_RGBA8* primColor, + Color_RGBA8* envColor, s16 scale, s16 scaleStep, s16 sclaeStepDecay, s16 life) { + EffectSsBlastParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.sclaeStepDecay = sclaeStepDecay; + initParams.life = life; + + EffectSs_Spawn(globalCtx, EFFECT_SS_BLAST, 128, &initParams); +} + +void EffectSsBlast_SpawnWhiteCustomScale(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + s16 scaleStep, s16 life) { + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 200, 200, 200, 0 }; + + EffectSsBlast_Spawn(globalCtx, pos, velocity, accel, &primColor, &envColor, scale, scaleStep, 35, life); +} + +void EffectSsBlast_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 life) { + EffectSsBlast_Spawn(globalCtx, pos, velocity, accel, primColor, envColor, 100, 375, 35, life); +} + +void EffectSsBlast_SpawnWhiteShockwave(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 200, 200, 200, 0 }; + + EffectSsBlast_SpawnShockwave(globalCtx, pos, velocity, accel, &primColor, &envColor, 10); +} + +// EffectSsGSpk Spawn Functions + +void EffectSsGSpk_SpawnAccel(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 scaleStep) { + EffectSsGSpkInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.actor = actor; + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.updateMode = 0; + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_SPK, 128, &initParams); +} + +// unused +void EffectSsGSpk_SpawnNoAccel(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 scaleStep) { + EffectSsGSpkInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.actor = actor; + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.updateMode = 1; + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_SPK, 128, &initParams); +} + +void EffectSsGSpk_SpawnFuse(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + Color_RGBA8 primColor = { 255, 255, 150, 255 }; + Color_RGBA8 envColor = { 255, 0, 0, 0 }; + + EffectSsGSpk_SpawnSmall(globalCtx, actor, pos, velocity, accel, &primColor, &envColor); +} + +// unused +void EffectSsGSpk_SpawnRandColor(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + s16 scale, s16 scaleStep) { + Color_RGBA8 primColor = { 255, 255, 150, 255 }; + Color_RGBA8 envColor = { 255, 0, 0, 0 }; + s32 randOffset = (Rand_ZeroOne() * 20.0f) - 10.0f; + + primColor.r += randOffset; + primColor.g += randOffset; + primColor.b += randOffset; + primColor.a += randOffset; + envColor.r += randOffset; + envColor.g += randOffset; + envColor.b += randOffset; + envColor.a += randOffset; + + EffectSsGSpk_SpawnAccel(globalCtx, actor, pos, velocity, accel, &primColor, &envColor, scale, scaleStep); +} + +void EffectSsGSpk_SpawnSmall(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor) { + EffectSsGSpk_SpawnAccel(globalCtx, actor, pos, velocity, accel, primColor, envColor, 100, 5); +} + +// EffectSsDFire Spawn Functions + +void EffectSsDFire_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 alpha, s16 fadeDelay, s32 life) { + EffectSsDFireInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.alpha = alpha; + initParams.fadeDelay = fadeDelay; + initParams.life = life; + + EffectSs_Spawn(globalCtx, EFFECT_SS_D_FIRE, 128, &initParams); +} + +void EffectSsDFire_SpawnFixedScale(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 alpha, + s16 fadeDelay) { + EffectSsDFire_Spawn(globalCtx, pos, velocity, accel, 100, 35, alpha, fadeDelay, 8); +} + +// EffectSsBubble Spawn Functions + +void EffectSsBubble_Spawn(GlobalContext* globalCtx, Vec3f* pos, f32 yPosOffset, f32 yPosRandScale, f32 xzPosRandScale, + f32 scale) { + EffectSsBubbleInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.yPosOffset = yPosOffset; + initParams.yPosRandScale = yPosRandScale; + initParams.xzPosRandScale = xzPosRandScale; + initParams.scale = scale; + + EffectSs_Spawn(globalCtx, EFFECT_SS_BUBBLE, 128, &initParams); +} + +// EffectSsGRipple Spawn Functions + +void EffectSsGRipple_Spawn(GlobalContext* globalCtx, Vec3f* pos, s16 radius, s16 radiusMax, s16 life) { + EffectSsGRippleInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.radius = radius; + initParams.radiusMax = radiusMax; + initParams.life = life; + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_RIPPLE, 128, &initParams); +} + +// EffectSsGSplash Spawn Functions + +void EffectSsGSplash_Spawn(GlobalContext* globalCtx, Vec3f* pos, Color_RGBA8* primColor, Color_RGBA8* envColor, + s16 type, s16 scale) { + EffectSsGSplashInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.type = type; + initParams.scale = scale; + + if (primColor != NULL) { + initParams.primColor = *primColor; + initParams.envColor = *envColor; + initParams.customColor = true; + } else { + initParams.customColor = false; + } + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_SPLASH, 128, &initParams); +} + +// EffectSsGMagma Spawn Functions + +void EffectSsGMagma_Spawn(GlobalContext* globalCtx, Vec3f* pos) { + EffectSsGMagmaInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_MAGMA, 128, &initParams); +} + +// EffectSsGFire Spawn Functions + +void EffectSsGFire_Spawn(GlobalContext* globalCtx, Vec3f* pos) { + EffectSsGFireInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_FIRE, 128, &initParams); +} + +// EffectSsLightning Spawn Functions + +void EffectSsLightning_Spawn(GlobalContext* globalCtx, Vec3f* pos, Color_RGBA8* primColor, Color_RGBA8* envColor, + s16 scale, s16 yaw, s16 life, s16 numBolts) { + EffectSsLightningInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.scale = scale; + initParams.yaw = yaw; + initParams.life = life; + initParams.numBolts = numBolts; + + EffectSs_Spawn(globalCtx, EFFECT_SS_LIGHTNING, 128, &initParams); +} + +// EffectSsDtBubble Spawn Functions + +void EffectSsDtBubble_SpawnColorProfile(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + s16 life, s16 colorProfile, s16 randXZ) { + EffectSsDtBubbleInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.customColor = false; + initParams.colorProfile = colorProfile; + initParams.scale = scale; + initParams.life = life; + initParams.randXZ = randXZ; + + EffectSs_Spawn(globalCtx, EFFECT_SS_DT_BUBBLE, 128, &initParams); +} + +void EffectSsDtBubble_SpawnCustomColor(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 scale, s16 life, s16 randXZ) { + EffectSsDtBubbleInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.scale = scale; + initParams.life = life; + initParams.randXZ = randXZ; + initParams.customColor = true; + + EffectSs_Spawn(globalCtx, EFFECT_SS_DT_BUBBLE, 128, &initParams); +} + +// EffectSsHahen Spawn Functions + +/** + * Spawn a single fragment + * + * Notes: + * - if a display list is not provided, D_0400C0D0 (wilted deku fragment) will be used as default + * - the unused arg does not do anything, any value can be passed here + * - due to how life is implemented it is capped at 200. Any value over 200 is accepted, but the fragment will + * only live for 200 frames + */ +void EffectSsHahen_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 unused, s16 scale, + s16 objId, s16 life, Gfx* dList) { + EffectSsHahenInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.dList = dList; + initParams.unused = unused; + initParams.scale = scale; + initParams.objId = objId; + initParams.life = life; + + EffectSs_Spawn(globalCtx, EFFECT_SS_HAHEN, 128, &initParams); +} + +/** + * Spawn a burst of fragments, with the amount of fragments specifed by count and burst speed set by + * + * Notes: + * - if a display list is not provided, D_0400C0D0 (wilted deku fragment) will be used as default + * - the unused arg does not do anything, any value can be passed here + * - due to how life is implemented it is capped at 200. Any value over 200 is accepted, but the fragment will + * only live for 200 frames + */ +void EffectSsHahen_SpawnBurst(GlobalContext* globalCtx, Vec3f* pos, f32 burstScale, s16 unused, s16 scale, + s16 randScaleRange, s16 count, s16 objId, s16 life, Gfx* dList) { + s32 i; + Vec3f velocity; + Vec3f accel; + + accel.y = -0.07f * burstScale; + accel.x = accel.z = 0.0f; + + for (i = 0; i < count; i++) { + velocity.x = (Rand_ZeroOne() - 0.5f) * burstScale; + velocity.z = (Rand_ZeroOne() - 0.5f) * burstScale; + velocity.y = ((Rand_ZeroOne() * 0.5f) + 0.5f) * burstScale; + + EffectSsHahen_Spawn(globalCtx, pos, &velocity, &accel, unused, Rand_S16Offset(scale, randScaleRange), objId, + life, dList); + } +} + +// EffectSsStick Spawn Functions + +/** + * As child, spawn a broken stick fragment + * As adult, spawn a broken sword fragment + */ +void EffectSsStick_Spawn(GlobalContext* globalCtx, Vec3f* pos, s16 yaw) { + EffectSsStickInitParams initParams; + + initParams.pos = *pos; + initParams.yaw = yaw; + + EffectSs_Spawn(globalCtx, EFFECT_SS_STICK, 128, &initParams); +} + +// EffectSsSibuki Spawn Functions + +void EffectSsSibuki_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 moveDelay, + s16 direction, s16 scale) { + EffectSsSibukiInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.moveDelay = moveDelay; + initParams.direction = direction; + initParams.scale = scale; + + EffectSs_Spawn(globalCtx, EFFECT_SS_SIBUKI, 128, &initParams); +} + +void EffectSsSibuki_SpawnBurst(GlobalContext* globalCtx, Vec3f* pos) { + s16 i; + Vec3f unusedZeroVec1 = { 0.0f, 0.0f, 0.0f }; + Vec3f unusedZeroVec2 = { 0.0f, 0.0f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s16 randDirection = Rand_ZeroOne() * 1.99f; + + for (i = 0; i < KREG(19) + 30; i++) { + EffectSsSibuki_Spawn(globalCtx, pos, &zeroVec, &zeroVec, i / (KREG(27) + 6), randDirection, KREG(18) + 40); + } +} + +// EffectSsSibuki2 Spawn Functions + +// unused +void EffectSsSibuki2_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale) { + EffectSsSibuki2InitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + EffectSs_Spawn(globalCtx, EFFECT_SS_SIBUKI2, 128, &initParams); +} + +// EffectSsGMagma2 Spawn Functions + +void EffectSsGMagma2_Spawn(GlobalContext* globalCtx, Vec3f* pos, Color_RGBA8* primColor, Color_RGBA8* envColor, + s16 updateRate, s16 drawMode, s16 scale) { + EffectSsGMagma2InitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.updateRate = updateRate; + initParams.drawMode = drawMode; + initParams.scale = scale; + + EffectSs_Spawn(globalCtx, EFFECT_SS_G_MAGMA2, 128, &initParams); +} + +// EffectSsStone1 Spawn Functions + +void EffectSsStone1_Spawn(GlobalContext* globalCtx, Vec3f* pos, s32 arg2) { + EffectSsStone1InitParams initParams; + + initParams.pos = *pos; + initParams.unk_C = arg2; + + EffectSs_Spawn(globalCtx, EFFECT_SS_STONE1, 128, &initParams); +} + +// EffectSsHitMark Spawn Functions + +void EffectSsHitMark_Spawn(GlobalContext* globalCtx, s32 type, s16 scale, Vec3f* pos) { + EffectSsHitMarkInitParams initParams; + + initParams.type = type; + initParams.scale = scale; + Math_Vec3f_Copy(&initParams.pos, pos); + + EffectSs_Spawn(globalCtx, EFFECT_SS_HITMARK, 128, &initParams); +} + +void EffectSsHitMark_SpawnFixedScale(GlobalContext* globalCtx, s32 type, Vec3f* pos) { + EffectSsHitMark_Spawn(globalCtx, type, 300, pos); +} + +void EffectSsHitMark_SpawnCustomScale(GlobalContext* globalCtx, s32 type, s16 scale, Vec3f* pos) { + EffectSsHitMark_Spawn(globalCtx, type, scale, pos); +} + +// EffectSsFhgFlash Spawn Functions + +/** + * Spawn a light ball effect + * + * param changes the color of the ball. Refer to FhgFlashLightBallParam for the options. + * Note: this type requires OBJECT_FHG to be loaded + */ +void EffectSsFhgFlash_SpawnLightBall(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, + u8 param) { + EffectSsFhgFlashInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.param = param; + initParams.type = FHGFLASH_LIGHTBALL; + + EffectSs_Spawn(globalCtx, EFFECT_SS_FHG_FLASH, 128, &initParams); +} + +/** + * Spawn a shock effect + * + * param determines where the ligntning should go + * 0: dont attach to any actor. spawns at the position specified by pos + * 1: spawn at one of Player's body parts, chosen at random + * 2: spawn at one of Phantom Ganon's body parts, chosen at random + */ +void EffectSsFhgFlash_SpawnShock(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 scale, u8 param) { + EffectSsFhgFlashInitParams initParams; + + initParams.actor = actor; + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.scale = scale; + initParams.param = param; + initParams.type = FHGFLASH_SHOCK; + + EffectSs_Spawn(globalCtx, EFFECT_SS_FHG_FLASH, 128, &initParams); +} + +// EffectSsKFire Spawn Functions + +void EffectSsKFire_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scaleMax, u8 type) { + EffectSsKFireInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scaleMax = scaleMax; + initParams.type = type; + + EffectSs_Spawn(globalCtx, EFFECT_SS_K_FIRE, 128, &initParams); +} + +// EffectSsSolderSrchBall Spawn Functions + +void EffectSsSolderSrchBall_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 unused, + s16* linkDetected) { + EffectSsSolderSrchBallInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.unused = unused; + initParams.linkDetected = linkDetected; + + EffectSs_Spawn(globalCtx, EFFECT_SS_SOLDER_SRCH_BALL, 128, &initParams); +} + +// EffectSsKakera Spawn Functions + +void EffectSsKakera_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* arg3, s16 gravity, s16 arg5, + s16 arg6, s16 arg7, s16 arg8, s16 scale, s16 arg10, s16 arg11, s32 life, s16 colorIdx, + s16 objId, Gfx* dList) { + EffectSsKakeraInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.unk_18, arg3); + initParams.gravity = gravity; + initParams.unk_26 = arg5; + initParams.unk_28 = arg6; + initParams.unk_2A = arg7; + initParams.unk_2C = arg8; + initParams.scale = scale; + initParams.unk_30 = arg10; + initParams.unk_32 = arg11; + initParams.life = life; + initParams.colorIdx = colorIdx; + initParams.objId = objId; + initParams.dList = dList; + + EffectSs_Spawn(globalCtx, EFFECT_SS_KAKERA, 101, &initParams); +} + +// EffectSsIcePiece Spawn Functions + +void EffectSsIcePiece_Spawn(GlobalContext* globalCtx, Vec3f* pos, f32 scale, Vec3f* velocity, Vec3f* accel, s32 life) { + EffectSsIcePieceInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.life = life; + EffectSs_Spawn(globalCtx, EFFECT_SS_ICE_PIECE, 128, &initParams); +} + +void EffectSsIcePiece_SpawnBurst(GlobalContext* globalCtx, Vec3f* refPos, f32 scale) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Vec3f vecScales[] = { + { 0.0f, 70.0f, 0.0f }, + { 0.0f, 45.0f, 20.0f }, + { 17.320474f, 45.0f, 9.999695f }, + { 17.320474f, 45.0f, -9.999695f }, + { 0.0f, 45.0f, -20.0f }, + { -17.320474f, 45.0f, -9.999695f }, + { -17.320474f, 45.0f, 9.999695f }, + { 0.0f, 20.0f, 20.0f }, + { 17.320474f, 20.0f, -9.999695f }, + { -17.320474f, 20.0f, -9.999695f }, + }; // 17.320474 is approximately 10 * sqrt(3) + s32 i; + Vec3f velocity; + Vec3f pos; + f32 velocityScale; + + accel.y = -0.2f; + + for (i = 0; i < ARRAY_COUNT(vecScales); i++) { + pos = *refPos; + velocityScale = Rand_ZeroFloat(1.0f) + 0.5f; + velocity.x = (vecScales[i].x * 0.18f) * velocityScale; + velocity.y = (vecScales[i].y * 0.18f) * velocityScale; + velocity.z = (vecScales[i].z * 0.18f) * velocityScale; + pos.x += vecScales[i].x; + pos.y += vecScales[i].y; + pos.z += vecScales[i].z; + + EffectSsIcePiece_Spawn(globalCtx, &pos, (Rand_ZeroFloat(1.0f) + 0.5f) * ((scale * 1.3f) * 100.0f), &velocity, + &accel, 25); + } +} + +// EffectSsEnIce Spawn Functions + +void EffectSsEnIce_SpawnFlyingVec3f(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 primR, s16 primG, s16 primB, + s16 primA, s16 envR, s16 envG, s16 envB, f32 scale) { + + EffectSsEnIceInitParams initParams; + + initParams.actor = actor; + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.type = 0; + initParams.primColor.r = primR; + initParams.primColor.g = primG; + initParams.primColor.b = primB; + initParams.primColor.a = primA; + initParams.envColor.r = envR; + initParams.envColor.g = envG; + initParams.envColor.b = envB; + initParams.scale = scale; + + if (actor != NULL) { + Audio_PlayActorSound2(actor, NA_SE_PL_FREEZE_S); + } + + EffectSs_Spawn(globalCtx, EFFECT_SS_EN_ICE, 80, &initParams); +} + +void EffectSsEnIce_SpawnFlyingVec3s(GlobalContext* globalCtx, Actor* actor, Vec3s* pos, s16 primR, s16 primG, s16 primB, + s16 primA, s16 envR, s16 envG, s16 envB, f32 scale) { + + EffectSsEnIceInitParams initParams; + + initParams.actor = actor; + initParams.pos.x = pos->x; + initParams.pos.y = pos->y; + initParams.pos.z = pos->z; + initParams.primColor.r = primR; + initParams.primColor.g = primG; + initParams.primColor.b = primB; + initParams.primColor.a = primA; + initParams.envColor.r = envR; + initParams.envColor.g = envG; + initParams.envColor.b = envB; + initParams.type = 0; + initParams.scale = scale; + + if (actor != NULL) { + Audio_PlayActorSound2(actor, NA_SE_PL_FREEZE_S); + } + + EffectSs_Spawn(globalCtx, EFFECT_SS_EN_ICE, 80, &initParams); +} + +void EffectSsEnIce_Spawn(GlobalContext* globalCtx, Vec3f* pos, f32 scale, Vec3f* velocity, Vec3f* accel, + Color_RGBA8* primColor, Color_RGBA8* envColor, s32 life) { + EffectSsEnIceInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.scale = scale; + initParams.life = life; + initParams.type = 1; + + EffectSs_Spawn(globalCtx, EFFECT_SS_EN_ICE, 128, &initParams); +} + +// EffectSsFireTail Spawn Functions + +void EffectSsFireTail_Spawn(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, f32 scale, Vec3f* arg4, s16 arg5, + Color_RGBA8* primColor, Color_RGBA8* envColor, s16 type, s16 bodyPart, s32 life) { + EffectSsFireTailInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.unk_14, arg4); + Color_RGBA8_Copy(&initParams.primColor, primColor); + Color_RGBA8_Copy(&initParams.envColor, envColor); + initParams.unk_20 = arg5; + initParams.actor = actor; + initParams.scale = scale; + initParams.type = type; + initParams.bodyPart = bodyPart; + initParams.life = life; + + EffectSs_Spawn(globalCtx, EFFECT_SS_FIRE_TAIL, 128, &initParams); +} + +void EffectSsFireTail_SpawnFlame(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, f32 arg3, s16 bodyPart, + f32 colorIntensity) { + static Color_RGBA8 primColor = { 255, 255, 0, 255 }; + static Color_RGBA8 envColor = { 255, 0, 0, 255 }; + + primColor.g = (s32)(255.0f * colorIntensity); + primColor.b = 0; + + envColor.g = 0; + envColor.b = 0; + primColor.r = envColor.r = (s32)(255.0f * colorIntensity); + + EffectSsFireTail_Spawn(globalCtx, actor, pos, arg3, &actor->velocity, 15, &primColor, &envColor, + (colorIntensity == 1.0f) ? 0 : 1, bodyPart, 1); +} + +void EffectSsFireTail_SpawnFlameOnPlayer(GlobalContext* globalCtx, f32 scale, s16 bodyPart, f32 colorIntensity) { + Player* player = GET_PLAYER(globalCtx); + + EffectSsFireTail_SpawnFlame(globalCtx, &player->actor, &player->bodyPartsPos[bodyPart], scale, bodyPart, + colorIntensity); +} + +// EffectSsEnFire Spawn Functions + +// note: if bodyPart is greater than -1 the actor MUST have a table of Vec3f positions at offset 0x14C in the instance +void EffectSsEnFire_SpawnVec3f(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 scale, s16 arg4, s16 flags, + s16 bodyPart) { + EffectSsEnFireInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.actor = actor; + initParams.scale = scale; + initParams.unk_12 = arg4; + initParams.flags = flags; + initParams.bodyPart = bodyPart; + + if (actor != NULL) { + Audio_PlayActorSound2(actor, NA_SE_EV_FLAME_IGNITION); + } + + EffectSs_Spawn(globalCtx, EFFECT_SS_EN_FIRE, 128, &initParams); +} + +// note: if bodyPart is greater than -1 the actor MUST have a table of Vec3s positions at offset 0x14C in the instance +void EffectSsEnFire_SpawnVec3s(GlobalContext* globalCtx, Actor* actor, Vec3s* pos, s16 scale, s16 arg4, s16 flags, + s16 bodyPart) { + EffectSsEnFireInitParams initParams; + + initParams.pos.x = pos->x; + initParams.pos.y = pos->y; + initParams.pos.z = pos->z; + initParams.actor = actor; + initParams.scale = scale; + initParams.unk_12 = arg4; + initParams.flags = flags | 0x8000; + initParams.bodyPart = bodyPart; + + if (actor != NULL) { + Audio_PlayActorSound2(actor, NA_SE_EV_FLAME_IGNITION); + } + + EffectSs_Spawn(globalCtx, EFFECT_SS_EN_FIRE, 128, &initParams); +} + +// EffectSsExtra Spawn Functions + +void EffectSsExtra_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scoreIdx) { + EffectSsExtraInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.scoreIdx = scoreIdx; + + EffectSs_Spawn(globalCtx, EFFECT_SS_EXTRA, 100, &initParams); +} + +// EffectSsFCircle Spawn Functions + +void EffectSsFCircle_Spawn(GlobalContext* globalCtx, Actor* actor, Vec3f* pos, s16 radius, s16 height) { + EffectSsFcircleInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.actor = actor; + initParams.radius = radius; + initParams.height = height; + + EffectSs_Spawn(globalCtx, EFFECT_SS_FCIRCLE, 128, &initParams); +} + +// EffectSsDeadDb Spawn Functions + +void EffectSsDeadDb_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 primR, s16 primG, s16 primB, s16 primA, s16 envR, s16 envG, s16 envB, s16 unused, + s32 arg14, s16 playSound) { + EffectSsDeadDbInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.primColor.r = primR; + initParams.primColor.g = primG; + initParams.primColor.b = primB; + initParams.primColor.a = primA; + initParams.envColor.r = envR; + initParams.envColor.g = envG; + initParams.envColor.b = envB; + initParams.unused = unused; + initParams.unk_34 = arg14; + initParams.playSound = playSound; + + EffectSs_Spawn(globalCtx, EFFECT_SS_DEAD_DB, 120, &initParams); +} + +// EffectSsDeadDd Spawn Functions + +void EffectSsDeadDd_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 primR, s16 primG, s16 primB, s16 alpha, s16 envR, s16 envG, s16 envB, s16 alphaStep, + s32 life) { + EffectSsDeadDdInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.type = 0; + initParams.scaleStep = scaleStep; + initParams.primColor.r = primR; + initParams.primColor.g = primG; + initParams.primColor.b = primB; + initParams.alpha = alpha; + initParams.envColor.r = envR; + initParams.envColor.g = envG; + initParams.envColor.b = envB; + initParams.alphaStep = alphaStep; + initParams.life = life; + + EffectSs_Spawn(globalCtx, EFFECT_SS_DEAD_DD, 120, &initParams); +} + +// unused +void EffectSsDeadDd_SpawnRandYellow(GlobalContext* globalCtx, Vec3f* pos, s16 scale, s16 scaleStep, f32 randPosScale, + s32 randIter, s32 life) { + EffectSsDeadDdInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.randPosScale = randPosScale; + initParams.randIter = randIter; + initParams.life = life; + initParams.type = 1; + + EffectSs_Spawn(globalCtx, EFFECT_SS_DEAD_DD, 120, &initParams); +} + +// EffectSsDeadDs Spawn Functions + +void EffectSsDeadDs_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale, s16 scaleStep, + s16 alpha, s32 life) { + EffectSsDeadDsInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + initParams.scaleStep = scaleStep; + initParams.alpha = alpha; + initParams.life = life; + EffectSs_Spawn(globalCtx, EFFECT_SS_DEAD_DS, 100, &initParams); +} + +void EffectSsDeadDs_SpawnStationary(GlobalContext* globalCtx, Vec3f* pos, s16 scale, s16 scaleStep, s16 alpha, + s32 life) { + EffectSsDeadDs_Spawn(globalCtx, pos, &sZeroVec, &sZeroVec, scale, scaleStep, alpha, life); +} + +// EffectSsDeadSound Spawn Functions + +void EffectSsDeadSound_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u16 sfxId, + s16 lowerPriority, s16 repeatMode, s32 life) { + EffectSsDeadSoundInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.sfxId = sfxId; + initParams.lowerPriority = lowerPriority; + initParams.repeatMode = repeatMode; + initParams.life = life; + + if (!lowerPriority) { + EffectSs_Spawn(globalCtx, EFFECT_SS_DEAD_SOUND, 100, &initParams); + } else { + EffectSs_Spawn(globalCtx, EFFECT_SS_DEAD_SOUND, 127, &initParams); + } +} + +void EffectSsDeadSound_SpawnStationary(GlobalContext* globalCtx, Vec3f* pos, u16 sfxId, s16 lowerPriority, + s16 repeatMode, s32 life) { + EffectSsDeadSound_Spawn(globalCtx, pos, &sZeroVec, &sZeroVec, sfxId, lowerPriority, repeatMode, life); +} + +// EffectSsIceSmoke Spawn Functions + +/** + * Spawn an Ice Smoke effect + * + * Note: this effect requires OBJECT_FZ to be loaded + */ +void EffectSsIceSmoke_Spawn(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, s16 scale) { + EffectSsIceSmokeInitParams initParams; + + Math_Vec3f_Copy(&initParams.pos, pos); + Math_Vec3f_Copy(&initParams.velocity, velocity); + Math_Vec3f_Copy(&initParams.accel, accel); + initParams.scale = scale; + + EffectSs_Spawn(globalCtx, EFFECT_SS_ICE_SMOKE, 128, &initParams); +} diff --git a/soh/src/code/z_elf_message.c b/soh/src/code/z_elf_message.c new file mode 100644 index 000000000..370fe8d41 --- /dev/null +++ b/soh/src/code/z_elf_message.c @@ -0,0 +1,174 @@ +#include "global.h" +#include "z64elf_message.h" + +ElfMessage sChildSariaMsgs[] = { + ELF_MSG_STRENGTH_UPG(SKIP, 3, false, 0), + ELF_MSG_FLAG(CHECK, 0x61, false, 0x37), /* eventChkInf[3] & 0x80 */ + ELF_MSG_END(0x64), + ELF_MSG_FLAG(CHECK, 0x62, false, 0x25), /* eventChkInf[2] & 0x20 */ + ELF_MSG_FLAG(CHECK, 0x63, false, 0x37), /* eventChkInf[3] & 0x80 */ + ELF_MSG_FLAG(CHECK, 0x65, false, 0x43), /* eventChkInf[4] & 0x8 */ + ELF_MSG_MEDALLION(CHECK, 0x66, false, ITEM_MEDALLION_FOREST), + ELF_MSG_MEDALLION(CHECK, 0x66, false, ITEM_MEDALLION_FIRE), + ELF_MSG_MEDALLION(CHECK, 0x66, false, ITEM_MEDALLION_WATER), + ELF_MSG_SONG(CHECK, 0x67, false, ITEM_SONG_STORMS), + ELF_MSG_MEDALLION(CHECK, 0x68, false, ITEM_MEDALLION_SPIRIT), + ELF_MSG_MEDALLION(CHECK, 0x68, false, ITEM_MEDALLION_SHADOW), + ELF_MSG_END(0x69), +}; + +ElfMessage sAdultSariaMsgs[] = { + ELF_MSG_MEDALLION(CHECK, 0x6A, false, ITEM_MEDALLION_FOREST), + ELF_MSG_MEDALLION(CHECK, 0x6B, false, ITEM_MEDALLION_FIRE), + ELF_MSG_MEDALLION(CHECK, 0x6B, false, ITEM_MEDALLION_WATER), + ELF_MSG_MEDALLION(CHECK, 0x6C, false, ITEM_MEDALLION_SPIRIT), + ELF_MSG_MEDALLION(CHECK, 0x6C, false, ITEM_MEDALLION_SHADOW), + ELF_MSG_END(0x6D), +}; + +u32 ElfMessage_CheckCondition(ElfMessage* msg) { + s32 type = msg->byte0 & 0x1E; + u16 flag; + + switch (type) { + case (ELF_MSG_CONDITION_FLAG << 1): + flag = 1 << (msg->byte1 & 0x0F); + return ((msg->byte0 & 1) == 1) == ((flag & gSaveContext.eventChkInf[(msg->byte1 & 0xF0) >> 4]) != 0); + case (ELF_MSG_CONDITION_DUNGEON_ITEM << 1): + return ((msg->byte0 & 1) == 1) == + (CHECK_DUNGEON_ITEM(msg->byte1 - ITEM_KEY_BOSS, gSaveContext.mapIndex) != 0); + case (ELF_MSG_CONDITION_ITEM << 1): + return ((msg->byte0 & 1) == 1) == (msg->byte3 == INV_CONTENT(msg->byte1)); + case (ELF_MSG_CONDITION_OTHER << 1): + switch (msg->byte1 & 0xF0) { + case (ELF_MSG_CONDITION_STRENGTH_UPG << 4): + return ((msg->byte0 & 1) == 1) == ((msg->byte1 & 0x0F) == CUR_UPG_VALUE(UPG_STRENGTH)); + case (ELF_MSG_CONDITION_BOOTS << 4): + return ((msg->byte0 & 1) == 1) == + (((gBitFlags[msg->byte3 - ITEM_BOOTS_KOKIRI] << gEquipShifts[EQUIP_BOOTS]) & + gSaveContext.inventory.equipment) != 0); + case (ELF_MSG_CONDITION_SONG << 4): + return ((msg->byte0 & 1) == 1) == + (CHECK_QUEST_ITEM(msg->byte3 - ITEM_SONG_MINUET + QUEST_SONG_MINUET) != 0); + case (ELF_MSG_CONDITION_MEDALLION << 4): + return ((msg->byte0 & 1) == 1) == + (CHECK_QUEST_ITEM(msg->byte3 - ITEM_MEDALLION_FOREST + QUEST_MEDALLION_FOREST) != 0); + case (ELF_MSG_CONDITION_MAGIC << 4): + return ((msg->byte0 & 1) == 1) == (((void)0, gSaveContext.magicAcquired) != 0); + } + } + + LOG_STRING("企画外 条件", "../z_elf_message.c", 156); // "Unplanned conditions" + ASSERT(0, "0", "../z_elf_message.c", 157); + + return false; +} + +u32 func_8006BE88(ElfMessage** msgp) { + u32 temp = true; + + while (((*msgp)->byte0 & 0xE0) == (ELF_MSG_TYPE_UNK_1 << 5)) { + if (!ElfMessage_CheckCondition(*msgp)) { + temp = false; + } + *msgp += 1; + } + + if (temp) { + return ElfMessage_CheckCondition(*msgp); + } else { + return false; + } +} + +u32 func_8006BF1C(ElfMessage** msgp) { + ElfMessage* msg = *msgp; + u32 sp44[10]; + s32 temp1 = 0; + s32 temp2 = 0; + s32 temp3; + + do { + sp44[temp2] = ElfMessage_CheckCondition(msg); + temp1 += sp44[temp2]; + temp2++; + msg++; + } while ((msg->byte0 & 0xE0) == (ELF_MSG_TYPE_UNK_2 << 5)); + + if (temp1 == 0) { + return false; + } + + temp3 = Rand_ZeroFloat(temp1); + for (temp1 = 0; temp1 < temp2; temp1++) { + if (sp44[temp1]) { + if (temp3 > 0) { + temp3--; + } else { + return true; + } + } + *msgp += 1; + } + + return false; +} + +u16 ElfMessage_GetTextFromMsgs(ElfMessage* msg) { + while (true) { + switch (msg->byte0 & 0xE0) { + case (ELF_MSG_TYPE_CHECK << 5): + if (ElfMessage_CheckCondition(msg)) { + return msg->byte2 | 0x100; + } + break; + case (ELF_MSG_TYPE_UNK_1 << 5): + if (func_8006BE88(&msg)) { + return msg->byte2 | 0x100; + } + break; + case (ELF_MSG_TYPE_UNK_2 << 5): + if (func_8006BF1C(&msg)) { + return msg->byte2 | 0x100; + } + break; + case (ELF_MSG_TYPE_SKIP << 5): + if (ElfMessage_CheckCondition(msg)) { + msg += msg->byte2; + msg--; + } + break; + case (ELF_MSG_TYPE_END << 5): + return msg->byte2 | 0x100; + default: + LOG_STRING("企画外 条件", "../z_elf_message.c", 281); // "Unplanned conditions" + ASSERT(0, "0", "../z_elf_message.c", 282); + } + msg++; + } +} + +u16 ElfMessage_GetSariaText(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + ElfMessage* msgs; + + if (!LINK_IS_ADULT) { + if (Actor_FindNearby(globalCtx, &player->actor, ACTOR_EN_SA, 4, 800.0f) == NULL) { + msgs = sChildSariaMsgs; + } else { + return 0x0160; // Special text about Saria preferring to talk to you face-to-face + } + } else { + msgs = sAdultSariaMsgs; + } + + return ElfMessage_GetTextFromMsgs(msgs); +} + +u16 ElfMessage_GetCUpText(GlobalContext* globalCtx) { + if (globalCtx->cUpElfMsgs == NULL) { + return 0; + } else { + return ElfMessage_GetTextFromMsgs(globalCtx->cUpElfMsgs); + } +} diff --git a/soh/src/code/z_en_a_keep.c b/soh/src/code/z_en_a_keep.c new file mode 100644 index 000000000..80ccb38f7 --- /dev/null +++ b/soh/src/code/z_en_a_keep.c @@ -0,0 +1,368 @@ +#include "global.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include + +#define FLAGS ACTOR_FLAG_4 + +void EnAObj_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAObj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAObj_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAObj_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnAObj_WaitFinishedTalking(EnAObj* this, GlobalContext* globalCtx); +void EnAObj_WaitTalk(EnAObj* this, GlobalContext* globalCtx); +void EnAObj_BlockRot(EnAObj* this, GlobalContext* globalCtx); +void EnAObj_BoulderFragment(EnAObj* this, GlobalContext* globalCtx); +void EnAObj_Block(EnAObj* this, GlobalContext* globalCtx); + +void EnAObj_SetupWaitTalk(EnAObj* this, s16 type); +void EnAObj_SetupBlockRot(EnAObj* this, s16 type); +void EnAObj_SetupBoulderFragment(EnAObj* this, s16 type); +void EnAObj_SetupBlock(EnAObj* this, s16 type); + +const ActorInit En_A_Obj_InitVars = { + ACTOR_EN_A_OBJ, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnAObj), + (ActorFunc)EnAObj_Init, + (ActorFunc)EnAObj_Destroy, + (ActorFunc)EnAObj_Update, + (ActorFunc)EnAObj_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ALL, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 25, 60, 0, { 0, 0, 0 } }, +}; + +//extern CollisionHeader D_06000730; // gHookshotTargetCol ? + +static CollisionHeader* sColHeaders[] = { + &gLargerCubeCol, // A_OBJ_GRASS_CLUMP, A_OBJ_TREE_STUMP + &gLargerCubeCol, // A_OBJ_BLOCK_LARGE, A_OBJ_BLOCK_HUGE + &gSmallerFlatBlockCol, // unused + &gLargerFlatBlockCol, // A_OBJ_BLOCK_SMALL_ROT, A_OBJ_BLOCK_LARGE_ROT + &gSmallerCubeCol, // unused + //&D_06000730, // A_OBJ_UNKNOWN_6 // OTRTODO +}; + +static Gfx* sDLists[] = { + gFlatBlockDL, + gFlatBlockDL, + gFlatBlockDL, + gFlatRotBlockDL, + gFlatRotBlockDL, + gSmallCubeDL, + gHookshotPostDL, /* gHookshotPostDL ? */ // 0x06000210, // OTRTODO! + gGrassBladesDL, + gTreeStumpDL, + gSignRectangularDL, + gSignDirectionalDL, + gBoulderFragmentsDL, +}; + +void EnAObj_SetupAction(EnAObj* this, EnAObjActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnAObj_Init(Actor* thisx, GlobalContext* globalCtx) { + CollisionHeader* colHeader = NULL; + s32 pad; + EnAObj* this = (EnAObj*)thisx; + f32 shadowScale = 6.0f; + + this->textId = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + + switch (thisx->params) { + case A_OBJ_BLOCK_SMALL: + Actor_SetScale(thisx, 0.025f); + break; + case A_OBJ_BLOCK_LARGE: + Actor_SetScale(thisx, 0.05f); + break; + case A_OBJ_BLOCK_HUGE: + case A_OBJ_CUBE_SMALL: + case A_OBJ_UNKNOWN_6: + Actor_SetScale(thisx, 0.1f); + break; + case A_OBJ_BLOCK_SMALL_ROT: + Actor_SetScale(thisx, 0.005f); + break; + case A_OBJ_BLOCK_LARGE_ROT: + default: + Actor_SetScale(thisx, 0.01f); + break; + } + + if (thisx->params >= A_OBJ_SIGNPOST_OBLONG) { + shadowScale = 12.0f; + } + + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, shadowScale); + + thisx->focus.pos = thisx->world.pos; + this->dyna.bgId = BGACTOR_NEG_ONE; + this->dyna.unk_160 = 0; + this->dyna.unk_15C = DPM_UNK; + thisx->uncullZoneDownward = 1200.0f; + thisx->uncullZoneScale = 200.0f; + + switch (thisx->params) { + case A_OBJ_BLOCK_LARGE: + case A_OBJ_BLOCK_HUGE: + this->dyna.bgId = 1; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_BG); + EnAObj_SetupBlock(this, thisx->params); + break; + case A_OBJ_BLOCK_SMALL_ROT: + case A_OBJ_BLOCK_LARGE_ROT: + this->dyna.bgId = 3; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_BG); + EnAObj_SetupBlockRot(this, thisx->params); + break; + case A_OBJ_UNKNOWN_6: + // clang-format off + thisx->flags |= ACTOR_FLAG_0; this->dyna.bgId = 5; this->focusYoffset = 10.0f; + // clang-format on + thisx->gravity = -2.0f; + EnAObj_SetupWaitTalk(this, thisx->params); + break; + case A_OBJ_GRASS_CLUMP: + case A_OBJ_TREE_STUMP: + this->dyna.bgId = 0; + EnAObj_SetupWaitTalk(this, thisx->params); + break; + case A_OBJ_SIGNPOST_OBLONG: + case A_OBJ_SIGNPOST_ARROW: + thisx->textId = (this->textId & 0xFF) | 0x300; + // clang-format off + thisx->flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; thisx->targetArrowOffset = 500.0f; + // clang-format on + this->focusYoffset = 45.0f; + EnAObj_SetupWaitTalk(this, thisx->params); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + thisx->colChkInfo.mass = MASS_IMMOVABLE; + thisx->targetMode = 0; + break; + case A_OBJ_BOULDER_FRAGMENT: + thisx->gravity = -1.5f; + EnAObj_SetupBoulderFragment(this, thisx->params); + break; + default: + thisx->gravity = -2.0f; + EnAObj_SetupWaitTalk(this, thisx->params); + break; + } + + if (thisx->params <= A_OBJ_BLOCK_LARGE_ROT) { // A_OBJ_BLOCK_* + thisx->colChkInfo.mass = MASS_IMMOVABLE; + } + + if (this->dyna.bgId != BGACTOR_NEG_ONE) { + CollisionHeader_GetVirtual(sColHeaders[this->dyna.bgId], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + } +} + +void EnAObj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnAObj* this = (EnAObj*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + + switch (this->dyna.actor.params) { + case A_OBJ_SIGNPOST_OBLONG: + case A_OBJ_SIGNPOST_ARROW: + Collider_DestroyCylinder(globalCtx, &this->collider); + break; + } +} + +void EnAObj_WaitFinishedTalking(EnAObj* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->dyna.actor, globalCtx)) { + EnAObj_SetupWaitTalk(this, this->dyna.actor.params); + } +} + +void EnAObj_SetupWaitTalk(EnAObj* this, s16 type) { + EnAObj_SetupAction(this, EnAObj_WaitTalk); +} + +void EnAObj_WaitTalk(EnAObj* this, GlobalContext* globalCtx) { + s16 relYawTowardsPlayer; + + if (this->dyna.actor.textId != 0) { + relYawTowardsPlayer = this->dyna.actor.yawTowardsPlayer - this->dyna.actor.shape.rot.y; + if (ABS(relYawTowardsPlayer) < 0x2800 || + (this->dyna.actor.params == A_OBJ_SIGNPOST_ARROW && ABS(relYawTowardsPlayer) > 0x5800)) { + if (Actor_ProcessTalkRequest(&this->dyna.actor, globalCtx)) { + EnAObj_SetupAction(this, EnAObj_WaitFinishedTalking); + } else { + func_8002F2F4(&this->dyna.actor, globalCtx); + } + } + } +} + +void EnAObj_SetupBlockRot(EnAObj* this, s16 type) { + this->rotateState = 0; + this->rotateWaitTimer = 10; + this->dyna.actor.world.rot.y = 0; + this->dyna.actor.shape.rot = this->dyna.actor.world.rot; + EnAObj_SetupAction(this, EnAObj_BlockRot); +} + +void EnAObj_BlockRot(EnAObj* this, GlobalContext* globalCtx) { + if (this->rotateState == 0) { + if (this->dyna.unk_160 != 0) { + this->rotateState++; + this->rotateForTimer = 20; + + if ((s16)(this->dyna.actor.yawTowardsPlayer + 0x4000) < 0) { + this->rotSpeedX = -0x3E8; + } else { + this->rotSpeedX = 0x3E8; + } + + if (this->dyna.actor.yawTowardsPlayer < 0) { + this->rotSpeedY = -this->rotSpeedX; + } else { + this->rotSpeedY = this->rotSpeedX; + } + } + } else { + if (this->rotateWaitTimer != 0) { + this->rotateWaitTimer--; + } else { + this->dyna.actor.shape.rot.y += this->rotSpeedY; + this->dyna.actor.shape.rot.x += this->rotSpeedX; + this->rotateForTimer--; + this->dyna.actor.gravity = -1.0f; + + if (this->rotateForTimer == 0) { + this->dyna.actor.world.pos = this->dyna.actor.home.pos; + this->rotateState = 0; + this->rotateWaitTimer = 10; + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.gravity = 0.0f; + this->dyna.actor.shape.rot = this->dyna.actor.world.rot; + } + } + } +} + +void EnAObj_SetupBoulderFragment(EnAObj* this, s16 type) { + EnAObj_SetupAction(this, EnAObj_BoulderFragment); +} + +void EnAObj_BoulderFragment(EnAObj* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->dyna.actor.speedXZ, 1.0f, 1.0f, 0.5f, 0.0f); + this->dyna.actor.shape.rot.x += this->dyna.actor.world.rot.x >> 1; + this->dyna.actor.shape.rot.z += this->dyna.actor.world.rot.z >> 1; + + if (this->dyna.actor.speedXZ != 0.0f && this->dyna.actor.bgCheckFlags & 0x8) { + this->dyna.actor.world.rot.y = + this->dyna.actor.wallYaw - this->dyna.actor.world.rot.y + this->dyna.actor.wallYaw - 0x8000; + if (1) {} + this->dyna.actor.bgCheckFlags &= ~0x8; + } + + if (this->dyna.actor.bgCheckFlags & 0x2) { + if (this->dyna.actor.velocity.y < -8.0f) { + this->dyna.actor.velocity.y *= -0.6f; + this->dyna.actor.speedXZ *= 0.6f; + this->dyna.actor.bgCheckFlags &= ~0x3; + } else { + Actor_Kill(&this->dyna.actor); + } + } +} + +void EnAObj_SetupBlock(EnAObj* this, s16 type) { + this->dyna.actor.uncullZoneDownward = 1200.0f; + this->dyna.actor.uncullZoneScale = 720.0f; + EnAObj_SetupAction(this, EnAObj_Block); +} + +void EnAObj_Block(EnAObj* this, GlobalContext* globalCtx) { + this->dyna.actor.speedXZ += this->dyna.unk_150; + this->dyna.actor.world.rot.y = this->dyna.unk_158; + this->dyna.actor.speedXZ = CLAMP(this->dyna.actor.speedXZ, -2.5f, 2.5f); + + Math_SmoothStepToF(&this->dyna.actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + + if (this->dyna.actor.speedXZ != 0.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); + } + + this->dyna.unk_154 = 0.0f; + this->dyna.unk_150 = 0.0f; +} + +void EnAObj_Update(Actor* thisx, GlobalContext* globalCtx) { + EnAObj* this = (EnAObj*)thisx; + + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->dyna.actor); + + if (this->dyna.actor.gravity != 0.0f) { + if (this->dyna.actor.params != A_OBJ_BOULDER_FRAGMENT) { + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 5.0f, 40.0f, 0.0f, 0x1D); + } else { + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 5.0f, 20.0f, 0.0f, 0x1D); + } + } + + this->dyna.actor.focus.pos = this->dyna.actor.world.pos; + this->dyna.actor.focus.pos.y += this->focusYoffset; + + switch (this->dyna.actor.params) { + case A_OBJ_SIGNPOST_OBLONG: + case A_OBJ_SIGNPOST_ARROW: + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + break; + } +} + +void EnAObj_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 type = thisx->params; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_a_keep.c", 701); + + func_80093D18(globalCtx->state.gfxCtx); + + if (type >= A_OBJ_MAX) { + type = A_OBJ_BOULDER_FRAGMENT; + } + + if (thisx->params == A_OBJ_BOULDER_FRAGMENT) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 1, 60, 60, 60, 50); + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_a_keep.c", 712), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDLists[type]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_a_keep.c", 715); +} diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c new file mode 100644 index 000000000..2693c4b0f --- /dev/null +++ b/soh/src/code/z_en_item00.c @@ -0,0 +1,1329 @@ +#include "global.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" + +#define FLAGS 0 + +void EnItem00_Init(Actor* thisx, GlobalContext* globalCtx); +void EnItem00_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnItem00_Update(Actor* thisx, GlobalContext* globalCtx); +void EnItem00_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8001DFC8(EnItem00* this, GlobalContext* globalCtx); +void func_8001E1C8(EnItem00* this, GlobalContext* globalCtx); +void func_8001E304(EnItem00* this, GlobalContext* globalCtx); +void func_8001E5C8(EnItem00* this, GlobalContext* globalCtx); + +void EnItem00_DrawRupee(EnItem00* this, GlobalContext* globalCtx); +void EnItem00_DrawCollectible(EnItem00* this, GlobalContext* globalCtx); +void EnItem00_DrawHeartContainer(EnItem00* this, GlobalContext* globalCtx); +void EnItem00_DrawHeartPiece(EnItem00* this, GlobalContext* globalCtx); + +const ActorInit En_Item00_InitVars = { + ACTOR_EN_ITEM00, + ACTORCAT_MISC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnItem00), + (ActorFunc)EnItem00_Init, + (ActorFunc)EnItem00_Destroy, + (ActorFunc)EnItem00_Update, + (ActorFunc)EnItem00_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000010, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 10, 30, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_STOP), +}; + +static Color_RGBA8 sEffectPrimColor = { 255, 255, 127, 0 }; +static Color_RGBA8 sEffectEnvColor = { 255, 255, 255, 0 }; +static Vec3f sEffectVelocity = { 0.0f, 0.1f, 0.0f }; +static Vec3f sEffectAccel = { 0.0f, 0.01f, 0.0f }; + +static void* sRupeeTex[] = { + gRupeeGreenTex, gRupeeBlueTex, gRupeeRedTex, gRupeePinkTex, gRupeeOrangeTex, +}; + +static void* sItemDropTex[] = { + gDropRecoveryHeartTex, gDropBombTex, gDropArrows1Tex, gDropArrows2Tex, + gDropArrows3Tex, gDropBombTex, gDropDekuNutTex, gDropDekuStickTex, + gDropMagicLargeTex, gDropMagicSmallTex, gDropDekuSeedsTex, gDropKeySmallTex, +}; + +static u8 sItemDropIds[] = { + ITEM00_RUPEE_GREEN, + ITEM00_RUPEE_BLUE, + 0xFF, + 0xFF, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_GREEN, + ITEM00_MAGIC_SMALL, + ITEM00_HEART, + ITEM00_HEART, + 0xFF, + ITEM00_MAGIC_SMALL, + ITEM00_FLEXIBLE, + ITEM00_SEEDS, + ITEM00_SEEDS, + 0xFF, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_GREEN, + ITEM00_MAGIC_SMALL, + ITEM00_RUPEE_GREEN, + ITEM00_RUPEE_BLUE, + ITEM00_HEART, + 0xFF, + ITEM00_HEART, + 0xFF, + ITEM00_FLEXIBLE, + 0xFF, + ITEM00_BOMBS_A, + 0xFF, + ITEM00_SEEDS, + 0xFF, + 0xFF, + ITEM00_MAGIC_SMALL, + ITEM00_RUPEE_GREEN, + ITEM00_RUPEE_GREEN, + ITEM00_MAGIC_SMALL, + 0xFF, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_HEART, + 0xFF, + ITEM00_SEEDS, + ITEM00_SEEDS, + 0xFF, + ITEM00_BOMBS_A, + 0xFF, + ITEM00_FLEXIBLE, + ITEM00_MAGIC_SMALL, + ITEM00_RUPEE_GREEN, + ITEM00_RUPEE_GREEN, + ITEM00_NUTS, + 0xFF, + ITEM00_SEEDS, + ITEM00_SEEDS, + ITEM00_NUTS, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_SEEDS, + 0xFF, + ITEM00_FLEXIBLE, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ITEM00_RUPEE_GREEN, + ITEM00_RUPEE_GREEN, + ITEM00_SEEDS, + ITEM00_BOMBS_A, + ITEM00_MAGIC_SMALL, + ITEM00_BOMBS_A, + 0xFF, + 0xFF, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_HEART, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_MAGIC_SMALL, + ITEM00_RUPEE_GREEN, + ITEM00_MAGIC_SMALL, + ITEM00_RUPEE_GREEN, + 0xFF, + ITEM00_RUPEE_BLUE, + 0xFF, + 0xFF, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_HEART, + ITEM00_FLEXIBLE, + ITEM00_SEEDS, + ITEM00_SEEDS, + 0xFF, + ITEM00_MAGIC_SMALL, + ITEM00_RUPEE_GREEN, + ITEM00_RUPEE_BLUE, + 0xFF, + ITEM00_RUPEE_GREEN, + 0xFF, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_BOMBS_A, + ITEM00_ARROWS_SMALL, + 0xFF, + ITEM00_ARROWS_MEDIUM, + ITEM00_MAGIC_SMALL, + ITEM00_FLEXIBLE, + 0xFF, + ITEM00_MAGIC_LARGE, + ITEM00_RUPEE_GREEN, + 0xFF, + ITEM00_RUPEE_BLUE, + 0xFF, + ITEM00_RUPEE_GREEN, + ITEM00_HEART, + ITEM00_FLEXIBLE, + ITEM00_BOMBS_A, + ITEM00_ARROWS_SMALL, + 0xFF, + 0xFF, + 0xFF, + ITEM00_MAGIC_SMALL, + 0xFF, + 0xFF, + ITEM00_MAGIC_LARGE, + ITEM00_ARROWS_LARGE, + ITEM00_ARROWS_MEDIUM, + ITEM00_ARROWS_MEDIUM, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_SMALL, + ITEM00_FLEXIBLE, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_MEDIUM, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_SMALL, + ITEM00_ARROWS_MEDIUM, + ITEM00_ARROWS_LARGE, + ITEM00_ARROWS_LARGE, + ITEM00_MAGIC_LARGE, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_LARGE, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_LARGE, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_LARGE, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_SMALL, + ITEM00_MAGIC_LARGE, + ITEM00_BOMBS_A, + 0xFF, + ITEM00_BOMBS_A, + 0xFF, + ITEM00_BOMBS_A, + ITEM00_FLEXIBLE, + ITEM00_BOMBS_A, + ITEM00_BOMBS_A, + ITEM00_BOMBS_A, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + ITEM00_BOMBS_A, + 0xFF, + ITEM00_BOMBS_A, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_HEART, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_BLUE, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_RED, + ITEM00_RUPEE_RED, + ITEM00_SEEDS, + 0xFF, + ITEM00_NUTS, + 0xFF, + ITEM00_STICK, + 0xFF, + 0xFF, + ITEM00_SEEDS, + 0xFF, + 0xFF, + 0xFF, + ITEM00_NUTS, + 0xFF, + ITEM00_NUTS, + ITEM00_HEART, + ITEM00_SEEDS, + ITEM00_HEART, + 0xFF, + ITEM00_SEEDS, + 0xFF, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_HEART, + ITEM00_HEART, + 0xFF, + 0xFF, + ITEM00_HEART, + 0xFF, + ITEM00_HEART, + ITEM00_SEEDS, + ITEM00_FLEXIBLE, +}; + +static u8 sDropQuantities[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 0, 0, 0, 0, +}; + +void EnItem00_SetupAction(EnItem00* this, EnItem00ActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnItem00_Init(Actor* thisx, GlobalContext* globalCtx) { + EnItem00* this = (EnItem00*)thisx; + s32 pad; + f32 yOffset = 980.0f; + f32 shadowScale = 6.0f; + s32 getItemId = GI_NONE; + s16 spawnParam8000 = this->actor.params & 0x8000; + s32 pad1; + + this->collectibleFlag = (this->actor.params & 0x3F00) >> 8; + + this->actor.params &= 0xFF; + + if (Flags_GetCollectible(globalCtx, this->collectibleFlag)) { + Actor_Kill(&this->actor); + return; + } + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + this->unk_158 = 1; + + switch (this->actor.params) { + case ITEM00_RUPEE_GREEN: + case ITEM00_RUPEE_BLUE: + case ITEM00_RUPEE_RED: + Actor_SetScale(&this->actor, 0.015f); + this->scale = 0.015f; + yOffset = 750.0f; + break; + case ITEM00_SMALL_KEY: + this->unk_158 = 0; + Actor_SetScale(&this->actor, 0.03f); + this->scale = 0.03f; + yOffset = 350.0f; + break; + case ITEM00_HEART_PIECE: + this->unk_158 = 0; + yOffset = 650.0f; + Actor_SetScale(&this->actor, 0.02f); + this->scale = 0.02f; + break; + case ITEM00_HEART: + this->actor.home.rot.z = Rand_CenteredFloat(65535.0f); + yOffset = 430.0f; + Actor_SetScale(&this->actor, 0.02f); + this->scale = 0.02f; + break; + case ITEM00_HEART_CONTAINER: + yOffset = 430.0f; + this->unk_158 = 0; + Actor_SetScale(&this->actor, 0.02f); + this->scale = 0.02f; + break; + case ITEM00_ARROWS_SINGLE: + yOffset = 400.0f; + Actor_SetScale(&this->actor, 0.02f); + this->scale = 0.02f; + break; + case ITEM00_ARROWS_SMALL: + case ITEM00_ARROWS_MEDIUM: + case ITEM00_ARROWS_LARGE: + Actor_SetScale(&this->actor, 0.035f); + this->scale = 0.035f; + yOffset = 250.0f; + break; + case ITEM00_BOMBS_A: + case ITEM00_BOMBS_B: + case ITEM00_NUTS: + case ITEM00_STICK: + case ITEM00_MAGIC_SMALL: + case ITEM00_SEEDS: + case ITEM00_BOMBS_SPECIAL: + Actor_SetScale(&this->actor, 0.03f); + this->scale = 0.03f; + yOffset = 320.0f; + break; + case ITEM00_MAGIC_LARGE: + Actor_SetScale(&this->actor, 0.045 - 1e-10); + this->scale = 0.045 - 1e-10; + yOffset = 320.0f; + break; + case ITEM00_RUPEE_ORANGE: + Actor_SetScale(&this->actor, 0.045 - 1e-10); + this->scale = 0.045 - 1e-10; + yOffset = 750.0f; + break; + case ITEM00_RUPEE_PURPLE: + Actor_SetScale(&this->actor, 0.03f); + this->scale = 0.03f; + yOffset = 750.0f; + break; + case ITEM00_FLEXIBLE: + yOffset = 500.0f; + Actor_SetScale(&this->actor, 0.01f); + this->scale = 0.01f; + break; + case ITEM00_SHIELD_DEKU: + this->actor.objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GI_SHIELD_1); + Actor_SetObjectDependency(globalCtx, &this->actor); + Actor_SetScale(&this->actor, 0.5f); + this->scale = 0.5f; + yOffset = 0.0f; + shadowScale = 0.6f; + this->actor.world.rot.x = 0x4000; + break; + case ITEM00_SHIELD_HYLIAN: + this->actor.objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GI_SHIELD_2); + Actor_SetObjectDependency(globalCtx, &this->actor); + Actor_SetScale(&this->actor, 0.5f); + this->scale = 0.5f; + yOffset = 0.0f; + shadowScale = 0.6f; + this->actor.world.rot.x = 0x4000; + break; + case ITEM00_TUNIC_ZORA: + case ITEM00_TUNIC_GORON: + this->actor.objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GI_CLOTHES); + Actor_SetObjectDependency(globalCtx, &this->actor); + Actor_SetScale(&this->actor, 0.5f); + this->scale = 0.5f; + yOffset = 0.0f; + shadowScale = 0.6f; + this->actor.world.rot.x = 0x4000; + break; + } + + this->unk_156 = 0; + ActorShape_Init(&this->actor.shape, yOffset, ActorShadow_DrawCircle, shadowScale); + this->actor.shape.shadowAlpha = 180; + this->actor.focus.pos = this->actor.world.pos; + this->getItemId = GI_NONE; + + if (!spawnParam8000) { + EnItem00_SetupAction(this, func_8001DFC8); + this->unk_15A = -1; + return; + } + + this->unk_15A = 15; + this->unk_154 = 35; + + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + + switch (this->actor.params) { + case ITEM00_RUPEE_GREEN: + Item_Give(globalCtx, ITEM_RUPEE_GREEN); + break; + case ITEM00_RUPEE_BLUE: + Item_Give(globalCtx, ITEM_RUPEE_BLUE); + break; + case ITEM00_RUPEE_RED: + Item_Give(globalCtx, ITEM_RUPEE_RED); + break; + case ITEM00_RUPEE_PURPLE: + Item_Give(globalCtx, ITEM_RUPEE_PURPLE); + break; + case ITEM00_RUPEE_ORANGE: + Item_Give(globalCtx, ITEM_RUPEE_GOLD); + break; + case ITEM00_HEART: + Item_Give(globalCtx, ITEM_HEART); + break; + case ITEM00_FLEXIBLE: + Health_ChangeBy(globalCtx, 0x70); + break; + case ITEM00_BOMBS_A: + case ITEM00_BOMBS_B: + Item_Give(globalCtx, ITEM_BOMBS_5); + break; + case ITEM00_ARROWS_SINGLE: + Item_Give(globalCtx, ITEM_BOW); + break; + case ITEM00_ARROWS_SMALL: + Item_Give(globalCtx, ITEM_ARROWS_SMALL); + break; + case ITEM00_ARROWS_MEDIUM: + Item_Give(globalCtx, ITEM_ARROWS_MEDIUM); + break; + case ITEM00_ARROWS_LARGE: + Item_Give(globalCtx, ITEM_ARROWS_LARGE); + break; + case ITEM00_MAGIC_LARGE: + getItemId = GI_MAGIC_SMALL; + break; + case ITEM00_MAGIC_SMALL: + getItemId = GI_MAGIC_LARGE; + break; + case ITEM00_SMALL_KEY: + Item_Give(globalCtx, ITEM_KEY_SMALL); + break; + case ITEM00_SEEDS: + getItemId = GI_SEEDS_5; + break; + case ITEM00_NUTS: + getItemId = GI_NUTS_5; + break; + case ITEM00_STICK: + getItemId = GI_STICKS_1; + break; + case ITEM00_HEART_PIECE: + case ITEM00_HEART_CONTAINER: + case ITEM00_SHIELD_DEKU: + case ITEM00_SHIELD_HYLIAN: + case ITEM00_TUNIC_ZORA: + case ITEM00_TUNIC_GORON: + case ITEM00_BOMBS_SPECIAL: + break; + } + + if ((getItemId != GI_NONE) && !Actor_HasParent(&this->actor, globalCtx)) { + func_8002F554(&this->actor, globalCtx, getItemId); + } + + EnItem00_SetupAction(this, func_8001E5C8); + this->actionFunc(this, globalCtx); +} + +void EnItem00_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnItem00* this = (EnItem00*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_8001DFC8(EnItem00* this, GlobalContext* globalCtx) { + if ((this->actor.params <= ITEM00_RUPEE_RED) || ((this->actor.params == ITEM00_HEART) && (this->unk_15A < 0)) || + (this->actor.params == ITEM00_HEART_PIECE)) { + this->actor.shape.rot.y += 960; + } else { + if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL)) { + if (this->unk_15A == -1) { + if (Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x - 0x4000, 2, 3000, 1500) == + 0) { + this->unk_15A = -2; + } + } else { + if (Math_SmoothStepToS(&this->actor.shape.rot.x, -this->actor.world.rot.x - 0x4000, 2, 3000, 1500) == + 0) { + this->unk_15A = -1; + } + } + Math_SmoothStepToS(&this->actor.world.rot.x, 0, 2, 2500, 500); + } + } + + if (this->actor.params == ITEM00_HEART_PIECE) { + this->actor.shape.yOffset = Math_SinS(this->actor.shape.rot.y) * 150.0f + 850.0f; + } + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + + if (this->unk_154 == 0) { + if ((this->actor.params != ITEM00_SMALL_KEY) && (this->actor.params != ITEM00_HEART_PIECE) && + (this->actor.params != ITEM00_HEART_CONTAINER)) { + this->unk_154 = -1; + } + } + + if (this->unk_15A == 0) { + if ((this->actor.params != ITEM00_SMALL_KEY) && (this->actor.params != ITEM00_HEART_PIECE) && + (this->actor.params != ITEM00_HEART_CONTAINER)) { + Actor_Kill(&this->actor); + } + } + + if ((this->actor.gravity != 0.0f) && !(this->actor.bgCheckFlags & 0x0001)) { + EnItem00_SetupAction(this, func_8001E1C8); + } +} + +void func_8001E1C8(EnItem00* this, GlobalContext* globalCtx) { + f32 originalVelocity; + Vec3f effectPos; + + if (this->actor.params <= ITEM00_RUPEE_RED) { + this->actor.shape.rot.y += 960; + } + + if (globalCtx->gameplayFrames & 1) { + effectPos.x = this->actor.world.pos.x + Rand_CenteredFloat(10.0f); + effectPos.y = this->actor.world.pos.y + Rand_CenteredFloat(10.0f); + effectPos.z = this->actor.world.pos.z + Rand_CenteredFloat(10.0f); + EffectSsKiraKira_SpawnSmall(globalCtx, &effectPos, &sEffectVelocity, &sEffectAccel, &sEffectPrimColor, + &sEffectEnvColor); + } + + if (this->actor.bgCheckFlags & 0x0003) { + originalVelocity = this->actor.velocity.y; + if (originalVelocity > -2.0f) { + EnItem00_SetupAction(this, func_8001DFC8); + this->actor.velocity.y = 0.0f; + } else { + this->actor.velocity.y = originalVelocity * -0.8f; + this->actor.bgCheckFlags &= ~1; + } + } +} + +void func_8001E304(EnItem00* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f pos; + s32 rotOffset; + + this->unk_15A++; + + if (this->actor.params == ITEM00_HEART) { + if (this->actor.velocity.y < 0.0f) { + this->actor.speedXZ = 0.0f; + this->actor.gravity = -0.4f; + if (this->actor.velocity.y < -1.5f) { + this->actor.velocity.y = -1.5f; + } + this->actor.home.rot.z += (s16)((this->actor.velocity.y + 3.0f) * 1000.0f); + this->actor.world.pos.x += + Math_CosS(this->actor.yawTowardsPlayer) * (-3.0f * Math_CosS(this->actor.home.rot.z)); + this->actor.world.pos.z += + Math_SinS(this->actor.yawTowardsPlayer) * (-3.0f * Math_CosS(this->actor.home.rot.z)); + } + } + + if (this->actor.params <= ITEM00_RUPEE_RED) { + this->actor.shape.rot.y += 960; + } else if ((this->actor.params >= ITEM00_SHIELD_DEKU) && (this->actor.params != ITEM00_BOMBS_SPECIAL)) { + this->actor.world.rot.x -= 700; + this->actor.shape.rot.y += 400; + this->actor.shape.rot.x = this->actor.world.rot.x - 0x4000; + } + + if (this->actor.velocity.y <= 2.0f) { + rotOffset = (u16)this->actor.shape.rot.z + 10000; + if (rotOffset < 65535) { + this->actor.shape.rot.z += 10000; + } else { + this->actor.shape.rot.z = -1; + } + } + + if (!(globalCtx->gameplayFrames & 1)) { + pos.x = this->actor.world.pos.x + (Rand_ZeroOne() - 0.5f) * 10.0f; + pos.y = this->actor.world.pos.y + (Rand_ZeroOne() - 0.5f) * 10.0f; + pos.z = this->actor.world.pos.z + (Rand_ZeroOne() - 0.5f) * 10.0f; + EffectSsKiraKira_SpawnSmall(globalCtx, &pos, &sEffectVelocity, &sEffectAccel, &sEffectPrimColor, + &sEffectEnvColor); + } + + if (this->actor.bgCheckFlags & 0x0003) { + EnItem00_SetupAction(this, func_8001DFC8); + this->actor.shape.rot.z = 0; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } +} + +void func_8001E5C8(EnItem00* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->getItemId != GI_NONE) { + if (!Actor_HasParent(&this->actor, globalCtx)) { + func_8002F434(&this->actor, globalCtx, this->getItemId, 50.0f, 80.0f); + this->unk_15A++; + } else { + this->getItemId = GI_NONE; + } + } + + if (this->unk_15A == 0) { + Actor_Kill(&this->actor); + return; + } + + this->actor.world.pos = player->actor.world.pos; + + if (this->actor.params <= ITEM00_RUPEE_RED) { + this->actor.shape.rot.y += 960; + } else if (this->actor.params == ITEM00_HEART) { + this->actor.shape.rot.y = 0; + } + + this->actor.world.pos.y += 40.0f + Math_SinS(this->unk_15A * 15000) * (this->unk_15A * 0.3f); + + if (LINK_IS_ADULT) { + this->actor.world.pos.y += 20.0f; + } +} + +// The BSS in the function acted weird in the past. It is matching now but might cause issues in the future +void EnItem00_Update(Actor* thisx, GlobalContext* globalCtx) { + static u32 D_80157D90; + static s16 D_80157D94[1]; + s16* params; + Actor* dynaActor; + s32 getItemId = GI_NONE; + s16 sp3A = 0; + s16 i; + u32* temp; + EnItem00* this = (EnItem00*)thisx; + s32 pad; + + if (this->unk_15A > 0) { + this->unk_15A--; + } + + if ((this->unk_15A > 0) && (this->unk_15A < 41) && (this->unk_154 <= 0)) { + this->unk_156 = this->unk_15A; + } + + this->actionFunc(this, globalCtx); + Math_SmoothStepToF(&this->actor.scale.x, this->scale, 0.1f, this->scale * 0.1f, 0.0f); + temp = &D_80157D90; + + this->actor.scale.z = this->actor.scale.x; + this->actor.scale.y = this->actor.scale.x; + + if (this->actor.gravity) { + if (this->actor.bgCheckFlags & 0x0003) { + if (*temp != globalCtx->gameplayFrames) { + D_80157D90 = globalCtx->gameplayFrames; + D_80157D94[0] = 0; + for (i = 0; i < 50; i++) { + if (globalCtx->colCtx.dyna.bgActorFlags[i] & 1) { + dynaActor = globalCtx->colCtx.dyna.bgActors[i].actor; + if ((dynaActor != NULL) && (dynaActor->update != NULL)) { + if ((dynaActor->world.pos.x != dynaActor->prevPos.x) || + (dynaActor->world.pos.y != dynaActor->prevPos.y) || + (dynaActor->world.pos.z != dynaActor->prevPos.z)) { + D_80157D94[0]++; + break; + } + } + } + } + } + + } else { + sp3A = 1; + Actor_MoveForward(&this->actor); + } + + if (sp3A || D_80157D94[0]) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 15.0f, 15.0f, 0x1D); + + if (this->actor.floorHeight <= -10000.0f) { + Actor_Kill(&this->actor); + return; + } + } + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if ((this->actor.params == ITEM00_SHIELD_DEKU) || (this->actor.params == ITEM00_SHIELD_HYLIAN) || + (this->actor.params == ITEM00_TUNIC_ZORA) || (this->actor.params == ITEM00_TUNIC_GORON)) { + this->actor.shape.yOffset = Math_CosS(this->actor.shape.rot.x) * 37.0f; + this->actor.shape.yOffset = ABS(this->actor.shape.yOffset); + } + + if (this->unk_154 > 0) { + return; + } + + if (!((this->actor.xzDistToPlayer <= 30.0f) && (this->actor.yDistToPlayer >= -50.0f) && + (this->actor.yDistToPlayer <= 50.0f))) { + if (!Actor_HasParent(&this->actor, globalCtx)) { + return; + } + } + + if (globalCtx->gameOverCtx.state != GAMEOVER_INACTIVE) { + return; + } + + switch (this->actor.params) { + case ITEM00_RUPEE_GREEN: + Item_Give(globalCtx, ITEM_RUPEE_GREEN); + break; + case ITEM00_RUPEE_BLUE: + Item_Give(globalCtx, ITEM_RUPEE_BLUE); + break; + case ITEM00_RUPEE_RED: + Item_Give(globalCtx, ITEM_RUPEE_RED); + break; + case ITEM00_RUPEE_PURPLE: + Item_Give(globalCtx, ITEM_RUPEE_PURPLE); + break; + case ITEM00_RUPEE_ORANGE: + Item_Give(globalCtx, ITEM_RUPEE_GOLD); + break; + case ITEM00_STICK: + getItemId = GI_STICKS_1; + break; + case ITEM00_NUTS: + getItemId = GI_NUTS_5; + break; + case ITEM00_HEART: + Item_Give(globalCtx, ITEM_HEART); + break; + case ITEM00_FLEXIBLE: + Health_ChangeBy(globalCtx, 0x70); + break; + case ITEM00_BOMBS_A: + case ITEM00_BOMBS_B: + Item_Give(globalCtx, ITEM_BOMBS_5); + break; + case ITEM00_ARROWS_SINGLE: + Item_Give(globalCtx, ITEM_BOW); + break; + case ITEM00_ARROWS_SMALL: + Item_Give(globalCtx, ITEM_ARROWS_SMALL); + break; + case ITEM00_ARROWS_MEDIUM: + Item_Give(globalCtx, ITEM_ARROWS_MEDIUM); + break; + case ITEM00_ARROWS_LARGE: + Item_Give(globalCtx, ITEM_ARROWS_LARGE); + break; + case ITEM00_SEEDS: + getItemId = GI_SEEDS_5; + break; + case ITEM00_SMALL_KEY: + getItemId = GI_KEY_SMALL; + break; + case ITEM00_HEART_PIECE: + getItemId = GI_HEART_PIECE; + break; + case ITEM00_HEART_CONTAINER: + getItemId = GI_HEART_CONTAINER; + break; + case ITEM00_MAGIC_LARGE: + getItemId = GI_MAGIC_LARGE; + break; + case ITEM00_MAGIC_SMALL: + getItemId = GI_MAGIC_SMALL; + break; + case ITEM00_SHIELD_DEKU: + getItemId = GI_SHIELD_DEKU; + break; + case ITEM00_SHIELD_HYLIAN: + getItemId = GI_SHIELD_HYLIAN; + break; + case ITEM00_TUNIC_ZORA: + getItemId = GI_TUNIC_ZORA; + break; + case ITEM00_TUNIC_GORON: + getItemId = GI_TUNIC_GORON; + break; + case ITEM00_BOMBS_SPECIAL: + break; + } + + params = &this->actor.params; + + if ((getItemId != GI_NONE) && !Actor_HasParent(&this->actor, globalCtx)) { + func_8002F554(&this->actor, globalCtx, getItemId); + } + + switch (*params) { + case ITEM00_HEART_PIECE: + case ITEM00_HEART_CONTAINER: + case ITEM00_SMALL_KEY: + case ITEM00_SHIELD_DEKU: + case ITEM00_SHIELD_HYLIAN: + case ITEM00_TUNIC_ZORA: + case ITEM00_TUNIC_GORON: + if (Actor_HasParent(&this->actor, globalCtx)) { + Flags_SetCollectible(globalCtx, this->collectibleFlag); + Actor_Kill(&this->actor); + } + return; + } + + if ((*params <= ITEM00_RUPEE_RED) || (*params == ITEM00_RUPEE_ORANGE)) { + Audio_PlaySoundGeneral(NA_SE_SY_GET_RUPY, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (getItemId != GI_NONE) { + if (Actor_HasParent(&this->actor, globalCtx)) { + Flags_SetCollectible(globalCtx, this->collectibleFlag); + Actor_Kill(&this->actor); + } + return; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_GET_ITEM, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + Flags_SetCollectible(globalCtx, this->collectibleFlag); + + this->unk_15A = 15; + this->unk_154 = 35; + this->actor.shape.rot.z = 0; + this->actor.speedXZ = 0; + this->actor.velocity.y = 0; + this->actor.gravity = 0; + + Actor_SetScale(&this->actor, this->scale); + + this->getItemId = GI_NONE; + EnItem00_SetupAction(this, func_8001E5C8); +} + +void EnItem00_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnItem00* this = (EnItem00*)thisx; + f32 mtxScale; + + if (!(this->unk_156 & this->unk_158)) { + switch (this->actor.params) { + case ITEM00_RUPEE_GREEN: + case ITEM00_RUPEE_BLUE: + case ITEM00_RUPEE_RED: + case ITEM00_RUPEE_ORANGE: + case ITEM00_RUPEE_PURPLE: + EnItem00_DrawRupee(this, globalCtx); + break; + case ITEM00_HEART_PIECE: + EnItem00_DrawHeartPiece(this, globalCtx); + break; + case ITEM00_HEART_CONTAINER: + EnItem00_DrawHeartContainer(this, globalCtx); + break; + case ITEM00_HEART: + if (this->unk_15A < 0) { + if (this->unk_15A == -1) { + s8 bankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GI_HEART); + + if (Object_IsLoaded(&globalCtx->objectCtx, bankIndex)) { + this->actor.objBankIndex = bankIndex; + Actor_SetObjectDependency(globalCtx, &this->actor); + this->unk_15A = -2; + } + } else { + mtxScale = 16.0f; + Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); + GetItem_Draw(globalCtx, GID_HEART); + } + break; + } + case ITEM00_BOMBS_A: + case ITEM00_BOMBS_B: + case ITEM00_BOMBS_SPECIAL: + case ITEM00_ARROWS_SINGLE: + case ITEM00_ARROWS_SMALL: + case ITEM00_ARROWS_MEDIUM: + case ITEM00_ARROWS_LARGE: + case ITEM00_NUTS: + case ITEM00_STICK: + case ITEM00_MAGIC_LARGE: + case ITEM00_MAGIC_SMALL: + case ITEM00_SEEDS: + case ITEM00_SMALL_KEY: + EnItem00_DrawCollectible(this, globalCtx); + break; + case ITEM00_SHIELD_DEKU: + GetItem_Draw(globalCtx, GID_SHIELD_DEKU); + break; + case ITEM00_SHIELD_HYLIAN: + GetItem_Draw(globalCtx, GID_SHIELD_HYLIAN); + break; + case ITEM00_TUNIC_ZORA: + GetItem_Draw(globalCtx, GID_TUNIC_ZORA); + break; + case ITEM00_TUNIC_GORON: + GetItem_Draw(globalCtx, GID_TUNIC_GORON); + break; + case ITEM00_FLEXIBLE: + break; + } + } +} + +/** + * Draw Function used for Rupee types of En_Item00. + */ +void EnItem00_DrawRupee(EnItem00* this, GlobalContext* globalCtx) { + s32 pad; + s32 texIndex; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1546); + + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + + if (this->actor.params <= ITEM00_RUPEE_RED) { + texIndex = this->actor.params; + } else { + texIndex = this->actor.params - 0x10; + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_item00.c", 1562), + G_MTX_MODELVIEW | G_MTX_LOAD); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sRupeeTex[texIndex])); + + gSPDisplayList(POLY_OPA_DISP++, gRupeeDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1568); +} + +/** + * Draw Function used for most collectible types of En_Item00 (ammo, bombs, sticks, nuts, magic...). + */ +void EnItem00_DrawCollectible(EnItem00* this, GlobalContext* globalCtx) { + s32 texIndex = this->actor.params - 3; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1594); + + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + + if (this->actor.params == ITEM00_BOMBS_SPECIAL) { + texIndex = 1; + } else if (this->actor.params >= ITEM00_ARROWS_SMALL) { + texIndex -= 3; + } + + POLY_OPA_DISP = func_800946E4(POLY_OPA_DISP); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sItemDropTex[texIndex])); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_item00.c", 1607), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gItemDropDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1611); +} + +/** + * Draw Function used for the Heart Container type of En_Item00. + */ +void EnItem00_DrawHeartContainer(EnItem00* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1623); + + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_item00.c", 1634), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gHeartPieceExteriorDL); + + func_80093D84(globalCtx->state.gfxCtx); + func_8002ED80(&this->actor, globalCtx, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_item00.c", 1644), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, gHeartContainerInteriorDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1647); +} + +/** + * Draw Function used for the Piece of Heart type of En_Item00. + */ +void EnItem00_DrawHeartPiece(EnItem00* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1658); + + func_80093D84(globalCtx->state.gfxCtx); + func_8002ED80(&this->actor, globalCtx, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_item00.c", 1670), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, gHeartPieceInteriorDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_item00.c", 1673); +} + +/** + * Converts a given drop type ID based on link's current age, health and owned items. + * Returns a new drop type ID or -1 to cancel the drop. + */ +s16 func_8001F404(s16 dropId) { + if (LINK_IS_ADULT) { + if (dropId == ITEM00_SEEDS) { + dropId = ITEM00_ARROWS_SMALL; + } else if (dropId == ITEM00_STICK) { + dropId = ITEM00_RUPEE_GREEN; + } + } else { + if (dropId == ITEM00_ARROWS_SMALL || dropId == ITEM00_ARROWS_MEDIUM || dropId == ITEM00_ARROWS_LARGE) { + dropId = ITEM00_SEEDS; + } + } + + // This is convoluted but it seems like it must be a single condition to match + // clang-format off + if (((dropId == ITEM00_BOMBS_A || dropId == ITEM00_BOMBS_SPECIAL || dropId == ITEM00_BOMBS_B) && INV_CONTENT(ITEM_BOMB) == ITEM_NONE) || + ((dropId == ITEM00_ARROWS_SMALL || dropId == ITEM00_ARROWS_MEDIUM || dropId == ITEM00_ARROWS_LARGE) && INV_CONTENT(ITEM_BOW) == ITEM_NONE) || + ((dropId == ITEM00_MAGIC_LARGE || dropId == ITEM00_MAGIC_SMALL) && gSaveContext.magicLevel == 0) || + ((dropId == ITEM00_SEEDS) && INV_CONTENT(ITEM_SLINGSHOT) == ITEM_NONE)) { + return -1; + } + // clang-format on + + if (dropId == ITEM00_HEART && gSaveContext.healthCapacity == gSaveContext.health) { + return ITEM00_RUPEE_GREEN; + } + + return dropId; +} + +// External functions used by other actors to drop collectibles, which usually results in spawning an En_Item00 actor. + +EnItem00* Item_DropCollectible(GlobalContext* globalCtx, Vec3f* spawnPos, s16 params) { + s32 pad[2]; + EnItem00* spawnedActor = NULL; + s16 param4000 = params & 0x4000; + s16 param8000 = params & 0x8000; + s16 param3F00 = params & 0x3F00; + + params &= 0x3FFF; + + if (((params & 0x00FF) == ITEM00_FLEXIBLE) && !param4000) { + // TODO: Prevent the cast to EnItem00 here since this is a different actor (En_Elf) + spawnedActor = (EnItem00*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, spawnPos->x, + spawnPos->y + 40.0f, spawnPos->z, 0, 0, 0, FAIRY_HEAL_TIMED); + EffectSsDeadSound_SpawnStationary(globalCtx, spawnPos, NA_SE_EV_BUTTERFRY_TO_FAIRY, true, + DEADSOUND_REPEAT_MODE_OFF, 40); + } else { + if (!param8000) { + params = func_8001F404(params & 0x00FF); + } + + if (params != -1) { + spawnedActor = (EnItem00*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ITEM00, spawnPos->x, + spawnPos->y, spawnPos->z, 0, 0, 0, params | param8000 | param3F00); + if ((spawnedActor != NULL) && !param8000) { + spawnedActor->actor.velocity.y = !param4000 ? 8.0f : -2.0f; + spawnedActor->actor.speedXZ = 2.0f; + spawnedActor->actor.gravity = -0.9f; + spawnedActor->actor.world.rot.y = Rand_CenteredFloat(65536.0f); + Actor_SetScale(&spawnedActor->actor, 0.0f); + EnItem00_SetupAction(spawnedActor, func_8001E304); + spawnedActor->unk_15A = 220; + if ((spawnedActor->actor.params != ITEM00_SMALL_KEY) && + (spawnedActor->actor.params != ITEM00_HEART_PIECE) && + (spawnedActor->actor.params != ITEM00_HEART_CONTAINER)) { + spawnedActor->actor.room = -1; + } + spawnedActor->actor.flags |= ACTOR_FLAG_4; + } + } + } + return spawnedActor; +} + +EnItem00* Item_DropCollectible2(GlobalContext* globalCtx, Vec3f* spawnPos, s16 params) { + EnItem00* spawnedActor = NULL; + s32 pad; + s16 param4000 = params & 0x4000; + s16 param8000 = params & 0x8000; + s16 param3F00 = params & 0x3F00; + + params &= 0x3FFF; + + if (((params & 0x00FF) == ITEM00_FLEXIBLE) && !param4000) { + // TODO: Prevent the cast to EnItem00 here since this is a different actor (En_Elf) + spawnedActor = (EnItem00*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, spawnPos->x, + spawnPos->y + 40.0f, spawnPos->z, 0, 0, 0, FAIRY_HEAL_TIMED); + EffectSsDeadSound_SpawnStationary(globalCtx, spawnPos, NA_SE_EV_BUTTERFRY_TO_FAIRY, true, + DEADSOUND_REPEAT_MODE_OFF, 40); + } else { + params = func_8001F404(params & 0x00FF); + if (params != -1) { + spawnedActor = (EnItem00*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ITEM00, spawnPos->x, + spawnPos->y, spawnPos->z, 0, 0, 0, params | param8000 | param3F00); + if ((spawnedActor != NULL) && !param8000) { + spawnedActor->actor.velocity.y = 0.0f; + spawnedActor->actor.speedXZ = 0.0f; + spawnedActor->actor.gravity = param4000 ? 0.0f : -0.9f; + spawnedActor->actor.world.rot.y = Rand_CenteredFloat(65536.0f); + spawnedActor->actor.flags |= ACTOR_FLAG_4; + } + } + } + + return spawnedActor; +} + +void Item_DropCollectibleRandom(GlobalContext* globalCtx, Actor* fromActor, Vec3f* spawnPos, s16 params) { + s32 pad; + EnItem00* spawnedActor; + s16 dropQuantity; + s16 param8000; + s16 dropTableIndex = Rand_ZeroOne() * 16.0f; + u8 dropId; + + param8000 = params & 0x8000; + params &= 0x7FFF; + + if (fromActor != NULL) { + if (fromActor->dropFlag) { + if (fromActor->dropFlag & 0x01) { + params = 1 * 0x10; + dropTableIndex = 11; + } else if (fromActor->dropFlag & 0x02) { + params = 1 * 0x10; + dropTableIndex = 6; + } else if (fromActor->dropFlag & 0x04) { + params = 6 * 0x10; + dropTableIndex = 9; + } else if (fromActor->dropFlag & 0x08) { + params = 3 * 0x10; + dropTableIndex = 11; + } else if (fromActor->dropFlag & 0x10) { + params = 6 * 0x10; + dropTableIndex = 12; + } else if (fromActor->dropFlag & 0x20) { + params = 0 * 0x10; + dropTableIndex = 0; + } else if (fromActor->dropFlag & 0x40) { + params = 0 * 0x10; + dropTableIndex = 1; + } + } + if (fromActor->dropFlag & 0x20) { + dropId = ITEM00_RUPEE_PURPLE; + } else { + dropId = sItemDropIds[params + dropTableIndex]; + } + } else { + dropId = sItemDropIds[params + dropTableIndex]; + } + + if (dropId == ITEM00_FLEXIBLE) { + if (gSaveContext.health <= 0x10) { // 1 heart or less + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, spawnPos->x, spawnPos->y + 40.0f, spawnPos->z, 0, + 0, 0, FAIRY_HEAL_TIMED); + EffectSsDeadSound_SpawnStationary(globalCtx, spawnPos, NA_SE_EV_BUTTERFRY_TO_FAIRY, true, + DEADSOUND_REPEAT_MODE_OFF, 40); + return; + } else if (gSaveContext.health <= 0x30) { // 3 hearts or less + params = 0xB * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_HEART; + } else if (gSaveContext.health <= 0x50) { // 5 hearts or less + params = 0xA * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_HEART; + } else if ((gSaveContext.magicLevel != 0) && (gSaveContext.magic == 0)) { // Empty magic meter + params = 0xA * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_MAGIC_LARGE; + } else if ((gSaveContext.magicLevel != 0) && (gSaveContext.magic <= (gSaveContext.magicLevel >> 1))) { + params = 0xA * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_MAGIC_SMALL; + } else if (!LINK_IS_ADULT && (AMMO(ITEM_SLINGSHOT) < 6)) { + params = 0xA * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_SEEDS; + } else if (LINK_IS_ADULT && (AMMO(ITEM_BOW) < 6)) { + params = 0xA * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_ARROWS_MEDIUM; + } else if (AMMO(ITEM_BOMB) < 6) { + params = 0xD * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_BOMBS_A; + } else if (gSaveContext.rupees < 11) { + params = 0xA * 0x10; + dropTableIndex = 0x0; + dropId = ITEM00_RUPEE_RED; + } else { + return; + } + } + + if (dropId != 0xFF) { + dropQuantity = sDropQuantities[params + dropTableIndex]; + while (dropQuantity > 0) { + if (!param8000) { + dropId = func_8001F404(dropId); + if (dropId != 0xFF) { + spawnedActor = (EnItem00*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ITEM00, spawnPos->x, + spawnPos->y, spawnPos->z, 0, 0, 0, dropId); + if ((spawnedActor != NULL) && (dropId != 0xFF)) { + spawnedActor->actor.velocity.y = 8.0f; + spawnedActor->actor.speedXZ = 2.0f; + spawnedActor->actor.gravity = -0.9f; + spawnedActor->actor.world.rot.y = Rand_ZeroOne() * 40000.0f; + Actor_SetScale(&spawnedActor->actor, 0.0f); + EnItem00_SetupAction(spawnedActor, func_8001E304); + spawnedActor->actor.flags |= ACTOR_FLAG_4; + if ((spawnedActor->actor.params != ITEM00_SMALL_KEY) && + (spawnedActor->actor.params != ITEM00_HEART_PIECE) && + (spawnedActor->actor.params != ITEM00_HEART_CONTAINER)) { + spawnedActor->actor.room = -1; + } + spawnedActor->unk_15A = 220; + } + } + } else { + Item_DropCollectible(globalCtx, spawnPos, params | 0x8000); + } + dropQuantity--; + } + } +} diff --git a/soh/src/code/z_face_reaction.c b/soh/src/code/z_face_reaction.c new file mode 100644 index 000000000..d463da4f7 --- /dev/null +++ b/soh/src/code/z_face_reaction.c @@ -0,0 +1,70 @@ +#include "global.h" + +u16 sReactionTextIds[][PLAYER_MASK_MAX] = { + { 0x0000, 0x7124, 0x7127, 0x7126, 0x7125, 0x7127, 0x7124, 0x7125, 0x7127 }, + { 0x0000, 0x7128, 0x7129, 0x7128, 0x7128, 0x7128, 0x7128, 0x712A, 0x712B }, + { 0x0000, 0x7128, 0x712B, 0x7128, 0x7128, 0x7129, 0x7128, 0x712B, 0x7128 }, + { 0x0000, 0x7128, 0x7129, 0x7128, 0x7128, 0x7128, 0x7128, 0x712A, 0x712B }, + { 0x0000, 0x7128, 0x7129, 0x712B, 0x7128, 0x7128, 0x7128, 0x7129, 0x7128 }, + { 0x0000, 0x712D, 0x712D, 0x712D, 0x712D, 0x712D, 0x712D, 0x712D, 0x712F }, + { 0x0000, 0x712C, 0x712C, 0x712C, 0x712E, 0x712C, 0x712C, 0x712F, 0x712F }, + { 0x0000, 0x712C, 0x712C, 0x712C, 0x712F, 0x712C, 0x712C, 0x712F, 0x712F }, + { 0x0000, 0x7130, 0x7132, 0x7133, 0x7130, 0x7130, 0x7131, 0x7132, 0x7131 }, + { 0x0000, 0x7134, 0x7137, 0x7135, 0x7134, 0x7136, 0x7135, 0x7134, 0x7135 }, + { 0x0000, 0x7138, 0x713A, 0x7138, 0x7139, 0x713A, 0x7138, 0x7139, 0x713B }, + { 0x0000, 0x7144, 0x7146, 0x7144, 0x7146, 0x7147, 0x7145, 0x7145, 0x7147 }, + { 0x0000, 0x7148, 0x7149, 0x7149, 0x714A, 0x714A, 0x714B, 0x7149, 0x714B }, + { 0x0000, 0x714C, 0x714D, 0x714C, 0x714C, 0x714E, 0x714C, 0x714E, 0x714F }, + { 0x0000, 0x7150, 0x7153, 0x7152, 0x7150, 0x7151, 0x7153, 0x7153, 0x7151 }, + { 0x0000, 0x7155, 0x7156, 0x7157, 0x7154, 0x7156, 0x7156, 0x7156, 0x7156 }, + { 0x0000, 0x715A, 0x7159, 0x715B, 0x715A, 0x715A, 0x7158, 0x7158, 0x715B }, + { 0x0000, 0x715E, 0x715D, 0x715D, 0x715F, 0x715E, 0x715C, 0x715C, 0x715D }, + { 0x0000, 0x7163, 0x7162, 0x7160, 0x7163, 0x7160, 0x7161, 0x7161, 0x7160 }, + { 0x0000, 0x7164, 0x7166, 0x7164, 0x7167, 0x7164, 0x7164, 0x7164, 0x7167 }, + { 0x0000, 0x716B, 0x7169, 0x7168, 0x716B, 0x716A, 0x716B, 0x716B, 0x716A }, + { 0x0000, 0x716C, 0x716D, 0x716F, 0x716C, 0x716E, 0x716E, 0x716E, 0x716F }, + { 0x0000, 0x7171, 0x7173, 0x7170, 0x7172, 0x0000, 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x7176, 0x7177, 0x7174, 0x7174, 0x7175, 0x7174, 0x7174, 0x7177 }, + { 0x0000, 0x7178, 0x7179, 0x7179, 0x717B, 0x717A, 0x717B, 0x717A, 0x717B }, + { 0x0000, 0x717D, 0x717C, 0x717C, 0x717D, 0x717F, 0x717C, 0x717E, 0x717D }, + { 0x0000, 0x7183, 0x7181, 0x7180, 0x7183, 0x7182, 0x7183, 0x7181, 0x7183 }, + { 0x0000, 0x7184, 0x7186, 0x7185, 0x7186, 0x7184, 0x7187, 0x7186, 0x7184 }, + { 0x0000, 0x71A4, 0x71A6, 0x71A5, 0x0000, 0x71A6, 0x71A6, 0x71A6, 0x71A7 }, + { 0x0000, 0x7188, 0x7188, 0x7189, 0x7188, 0x7189, 0x718B, 0x718A, 0x7189 }, + { 0x0000, 0x718C, 0x718C, 0x718D, 0x718C, 0x718E, 0x718F, 0x718D, 0x718C }, + { 0x0000, 0x7190, 0x7190, 0x7191, 0x7192, 0x7191, 0x7193, 0x7190, 0x7191 }, + { 0x0000, 0x7196, 0x7194, 0x7195, 0x7196, 0x7197, 0x7194, 0x7196, 0x7195 }, + { 0x0000, 0x7199, 0x719A, 0x7198, 0x7198, 0x719A, 0x719A, 0x719B, 0x7198 }, + { 0x0000, 0x719D, 0x719C, 0x719E, 0x719D, 0x719D, 0x719C, 0x719F, 0x719E }, + { 0x0000, 0x71A1, 0x71A0, 0x71A1, 0x71A2, 0x71A1, 0x71A2, 0x71A3, 0x71A2 }, + { 0x0000, 0x711C, 0x711E, 0x711C, 0x711F, 0x711E, 0x711C, 0x711D, 0x711F }, + { 0x0000, 0x7104, 0x7105, 0x7107, 0x7107, 0x7105, 0x7106, 0x7107, 0x7107 }, + { 0x0000, 0x7107, 0x7105, 0x7107, 0x7107, 0x7106, 0x7107, 0x7107, 0x7105 }, + { 0x0000, 0x7113, 0x7117, 0x7113, 0x7110, 0x7112, 0x7112, 0x7116, 0x7112 }, + { 0x0000, 0x7113, 0x7113, 0x7113, 0x7113, 0x7113, 0x7113, 0x7111, 0x7113 }, + { 0x0000, 0x7113, 0x7117, 0x7113, 0x7110, 0x7112, 0x7112, 0x7116, 0x7112 }, + { 0x0000, 0x7117, 0x7117, 0x7117, 0x7117, 0x7117, 0x7117, 0x7117, 0x7113 }, + { 0x0000, 0x7101, 0x7100, 0x7102, 0x7103, 0x7101, 0x7100, 0x7102, 0x7103 }, + { 0x0000, 0x7100, 0x7102, 0x7100, 0x7100, 0x7100, 0x7100, 0x7100, 0x7102 }, + { 0x0000, 0x710A, 0x7109, 0x7109, 0x710A, 0x710B, 0x7108, 0x7109, 0x710B }, + { 0x0000, 0x7117, 0x7112, 0x7113, 0x7110, 0x710C, 0x7117, 0x710E, 0x7112 }, + { 0x0000, 0x710D, 0x710F, 0x710C, 0x7112, 0x710D, 0x710C, 0x710C, 0x710F }, + { 0x0000, 0x710A, 0x7109, 0x711A, 0x710A, 0x7109, 0x7108, 0x710B, 0x7109 }, + { 0x0000, 0x710C, 0x710F, 0x7113, 0x7110, 0x710D, 0x7112, 0x7116, 0x710D }, + { 0x0000, 0x7115, 0x7114, 0x7114, 0x7115, 0x7114, 0x7114, 0x7116, 0x7117 }, + { 0x0000, 0x7113, 0x710F, 0x7113, 0x7110, 0x710C, 0x711A, 0x710D, 0x7112 }, + { 0x0000, 0x7101, 0x7102, 0x7103, 0x7101, 0x7100, 0x7100, 0x7102, 0x7100 }, + { 0x0000, 0x7112, 0x710E, 0x7112, 0x710E, 0x710D, 0x7112, 0x710E, 0x710F }, + { 0x0000, 0x7142, 0x7141, 0x7142, 0x7143, 0x7140, 0x7140, 0x7141, 0x7143 }, + { 0x0000, 0x713C, 0x713D, 0x713D, 0x713E, 0x713E, 0x713F, 0x713D, 0x713F }, + { 0x0000, 0x7101, 0x7102, 0x7103, 0x7101, 0x7100, 0x7100, 0x7102, 0x7100 }, + { 0x0000, 0x7113, 0x7117, 0x7113, 0x7110, 0x7112, 0x7112, 0x7116, 0x7112 }, + { 0x0000, 0x7104, 0x7105, 0x7107, 0x7105, 0x7105, 0x7105, 0x7107, 0x7107 }, + { 0x0000, 0x7104, 0x7105, 0x7107, 0x7105, 0x710C, 0x7105, 0x7107, 0x7107 }, +}; + +u16 Text_GetFaceReaction(GlobalContext* globalCtx, u32 reactionSet) { + u8 currentMask = Player_GetMask(globalCtx); + + return sReactionTextIds[reactionSet][currentMask]; +} diff --git a/soh/src/code/z_fbdemo.c b/soh/src/code/z_fbdemo.c new file mode 100644 index 000000000..16989fe5e --- /dev/null +++ b/soh/src/code/z_fbdemo.c @@ -0,0 +1,231 @@ +#include "global.h" +#include + +Gfx D_8012AFB0[] = { + gsDPPipeSync(), + gsDPSetCycleType(G_CYC_FILL), + gsDPSetColorImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, 320, 0x0F000000), + gsDPSetFillColor((GPACK_RGBA5551(65, 65, 65, 1) << 16) | GPACK_RGBA5551(65, 65, 65, 1)), + gsDPFillRectangle(0, 0, 319, 239), + gsDPPipeSync(), + gsDPSetFillColor((GPACK_RGBA5551(65, 65, 255, 1) << 16) | GPACK_RGBA5551(65, 65, 255, 1)), + gsDPFillRectangle(20, 20, 300, 220), + gsDPPipeSync(), + gsSPEndDisplayList(), +}; + +Gfx D_8012B000[] = { + gsDPPipeSync(), + gsSPTexture(0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON), + gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | + G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH), + gsDPSetCombineMode(G_CC_DECALRGB, G_CC_DECALRGB), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_1PRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_OPA_SURF | G_RM_AA_OPA_SURF2), + gsSPEndDisplayList(), +}; + +void TransitionUnk_InitGraphics(TransitionUnk* this) { + s32 row2; + s32 pad2; + s32 pad3; + Vtx_t* vtx2; + s32 frame; + s32 rowTex; + s32 row; + Gfx* gfx; + Vtx* vtx; + s32 col; + s32 colTex; + + guMtxIdent(&this->modelView); + guMtxIdent(&this->unk_98); + guOrtho(&this->projection, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT, 0.0f, -1000.0f, 1000.0f, 1.0f); + + for (frame = 0; frame < 2; frame++) { + this->frame = frame; + vtx = (this->frame == 0) ? this->vtxFrame1 : this->vtxFrame2; + for (colTex = 0, col = 0; col < this->col + 1; colTex += 0x20, col++) { + for (rowTex = 0, row = 0; row < this->row + 1; row++) { + vtx2 = &vtx->v; + vtx++; + + vtx2->tc[0] = rowTex << 6; + vtx2->ob[0] = row * 0x20; + vtx2->ob[1] = col * 0x20; + vtx2->ob[2] = -5; + vtx2->flag = 0; + vtx2->tc[1] = colTex << 6; + vtx2->cn[0] = 0; + vtx2->cn[1] = 0; + vtx2->cn[2] = 120; + vtx2->cn[3] = 255; + rowTex += 0x20; + } + } + } + + gfx = this->gfx; + for (colTex = 0, col = 0; col < this->col; colTex += 0x20, col++) { + + gSPVertex(gfx++, SEGMENT_ADDR(0xA, (u32)col * (this->row + 1) * sizeof(Vtx)), 2 * (this->row + 1), 0); + + for (rowTex = 0, row = 0, row2 = 0; row < this->row;) { + gDPPipeSync(gfx++); + + gDPLoadTextureTile(gfx++, SEGMENT_ADDR(0xB, 0), G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, SCREEN_HEIGHT, + rowTex, colTex, rowTex + 0x20, colTex + 0x20, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(gfx++, row, row + 1, row2 + this->row + 2, this->row + row2 + 1, 0); + + rowTex += 0x20; + row2++; + row++; + } + } + + gDPPipeSync(gfx++); + gSPEndDisplayList(gfx++); + + LOG_NUM("this->col * (1 + this->row * (1 + 7 + 1)) + 1 + 1", this->col * (1 + this->row * 9) + 2, "../z_fbdemo.c", + 144); + LOG_NUM("gp - this->gfxtbl", gfx - this->gfx, "../z_fbdemo.c", 145); +} + +void TransitionUnk_InitData(TransitionUnk* this) { + s32 col; + s32 row; + + for (col = 0; col < this->col + 1; col++) { + for (row = 0; row < this->row + 1; row++) { + (this->unk_0C + row + col * (this->row + 1))->unk_0 = row * 32; + (this->unk_0C + row + col * (this->row + 1))->unk_4 = col * 32; + } + } +} + +void TransitionUnk_Destroy(TransitionUnk* this) { + osSyncPrintf("fbdemo_cleanup(%08x)\n", this); + osSyncPrintf("msleep(100);\n"); + Sleep_Msec(100); + + if (this->unk_0C != NULL) { + SystemArena_FreeDebug(this->unk_0C, "../z_fbdemo.c", 180); + this->unk_0C = NULL; + } + if (this->vtxFrame1 != NULL) { + SystemArena_FreeDebug(this->vtxFrame1, "../z_fbdemo.c", 181); + this->vtxFrame1 = NULL; + } + if (this->vtxFrame2 != NULL) { + SystemArena_FreeDebug(this->vtxFrame2, "../z_fbdemo.c", 182); + this->vtxFrame2 = NULL; + } + if (this->gfx != NULL) { + SystemArena_FreeDebug(this->gfx, "../z_fbdemo.c", 183); + this->gfx = NULL; + } +} + +TransitionUnk* TransitionUnk_Init(TransitionUnk* this, s32 row, s32 col) { + osSyncPrintf("fbdemo_init(%08x, %d, %d)\n", this, row, col); + memset(this, 0, sizeof(*this)); + this->frame = 0; + this->row = row; + this->col = col; + this->unk_0C = SystemArena_MallocDebug((row + 1) * sizeof(TransitionUnkData) * (col + 1), "../z_fbdemo.c", 195); + this->vtxFrame1 = SystemArena_MallocDebug((row + 1) * sizeof(Vtx) * (col + 1), "../z_fbdemo.c", 196); + this->vtxFrame2 = SystemArena_MallocDebug((row + 1) * sizeof(Vtx) * (col + 1), "../z_fbdemo.c", 197); + this->gfx = SystemArena_MallocDebug((this->col * (1 + this->row * 9) + 2) * sizeof(Gfx), "../z_fbdemo.c", 198); + + if (this->unk_0C == NULL || this->vtxFrame1 == NULL || this->vtxFrame2 == NULL || this->gfx == NULL) { + osSyncPrintf("fbdemo_init allocation error\n"); + if (this->unk_0C != NULL) { + SystemArena_FreeDebug(this->unk_0C, "../z_fbdemo.c", 202); + this->unk_0C = NULL; + } + if (this->vtxFrame1 != NULL) { + SystemArena_FreeDebug(this->vtxFrame1, "../z_fbdemo.c", 203); + this->vtxFrame1 = NULL; + } + if (this->vtxFrame2 != NULL) { + SystemArena_FreeDebug(this->vtxFrame2, "../z_fbdemo.c", 204); + this->vtxFrame2 = NULL; + } + if (this->gfx != NULL) { + SystemArena_FreeDebug(this->gfx, "../z_fbdemo.c", 205); + this->gfx = NULL; + } + return NULL; + } + TransitionUnk_InitGraphics(this); + TransitionUnk_InitData(this); + this->frame = 0; + + return this; +} + +void TransitionUnk_SetData(TransitionUnk* this) { + s32 col; + Vtx* vtx; + s32 row; + + for (col = 0; col < this->col + 1; col++) { + for (row = 0; row < this->row + 1; row++) { + vtx = (this->frame == 0) ? this->vtxFrame1 : this->vtxFrame2; + (vtx + row + col * (this->row + 1))->v.ob[0] = (this->unk_0C + row + col * (this->row + 1))->unk_0; + vtx = (this->frame == 0) ? this->vtxFrame1 : this->vtxFrame2; + (vtx + row + col * (this->row + 1))->v.ob[1] = (this->unk_0C + row + col * (this->row + 1))->unk_4; + } + } +} + +void TransitionUnk_Draw(TransitionUnk* this, Gfx** gfxP) { + Gfx* gfx = *gfxP; + + gSPDisplayList(gfx++, D_8012B000); + TransitionUnk_SetData(this); + gSPMatrix(gfx++, &this->projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(gfx++, &this->modelView, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(gfx++, 0xA, this->frame == 0 ? this->vtxFrame1 : this->vtxFrame2); + gSPSegment(gfx++, 0xB, this->zBuffer); + gSPDisplayList(gfx++, D_8012B000); + gSPDisplayList(gfx++, this->gfx); + gDPPipeSync(gfx++); + this->frame ^= 1; + *gfxP = gfx; +} + +void TransitionUnk_Update(TransitionUnk* this) { + f32 temp_f00; + f32 temp_f12; + s32 col; + f32 phi_f14; + s32 row; + + for (col = 0; col < this->col + 1; col++) { + for (row = 0; row < this->row + 1; row++) { + temp_f00 = + (this->unk_0C + row + col * (this->row + 1))->unk_0 - (this->unk_0C + 5 + 4 * (this->row + 1))->unk_0; + temp_f12 = + (this->unk_0C + row + col * (this->row + 1))->unk_4 - (this->unk_0C + 5 + 4 * (this->row + 1))->unk_4; + phi_f14 = (SQ(temp_f00) + SQ(temp_f12)) / 100.0f; + if (phi_f14 != 0.0f) { + if (phi_f14 < 1.0f) { + phi_f14 = 1.0f; + } + (this->unk_0C + row + col * (this->row + 1))->unk_0 -= temp_f00 / phi_f14; + (this->unk_0C + row + col * (this->row + 1))->unk_4 -= temp_f12 / phi_f14; + } + } + } +} + +void func_800B23E8(TransitionUnk* this) { +} + +s32 func_800B23F0(TransitionUnk* this) { + return 0; +} diff --git a/soh/src/code/z_fbdemo_circle.c b/soh/src/code/z_fbdemo_circle.c new file mode 100644 index 000000000..630f198d1 --- /dev/null +++ b/soh/src/code/z_fbdemo_circle.c @@ -0,0 +1,223 @@ +#include "global.h" +#include + +// unused +Gfx sCircleNullDList[] = { + gsSPEndDisplayList(), +}; + +//#include "code/fbdemo_circle/z_fbdemo_circle.c" +#include "code/fbdemo_circle/z_fbdemo_circle.h" + +Gfx __sCircleDList[] = { + gsDPPipeSync(), // 0 + gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | // 1 + G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH), + gsSPSetGeometryMode(G_SHADE | G_SHADING_SMOOTH), // 2 + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | // 3 + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), // 4 + gsDPSetCombineMode(G_CC_BLENDPEDECALA, G_CC_BLENDPEDECALA), // 5 + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), // 6 + gsDPLoadTextureBlock(0xF8000000, G_IM_FMT_I, G_IM_SIZ_8b, 16, 64, 0, G_TX_NOMIRROR | G_TX_WRAP, // 7 + G_TX_NOMIRROR | G_TX_CLAMP, 4, 6, G_TX_NOLOD, G_TX_NOLOD), + gsSPDisplayList(0xF9000000), // 8 + gsSPVertex(sCircleWipeVtx, 32, 0), // 9 + gsSP2Triangles(0, 1, 2, 0, 1, 3, 4, 0), // 10 + gsSP2Triangles(3, 5, 6, 0, 5, 7, 8, 0), // 11 + gsSP2Triangles(7, 9, 10, 0, 9, 11, 12, 0), // 12 + gsSP2Triangles(11, 13, 14, 0, 13, 15, 16, 0), // 13 + gsSP2Triangles(15, 17, 18, 0, 17, 19, 20, 0), // 14 + gsSP2Triangles(19, 21, 22, 0, 21, 23, 24, 0), // 15 + gsSP2Triangles(23, 25, 26, 0, 25, 27, 28, 0), // 16 + gsSP1Triangle(27, 29, 30, 0), // 17 + gsSPVertex(&sCircleWipeVtx[31], 3, 0), // 18 + gsSP1Triangle(0, 1, 2, 0), // 19 + gsSPEndDisplayList(), // 20 +}; + +void TransitionCircle_Start(void* thisx) { + TransitionCircle* this = (TransitionCircle*)thisx; + + this->isDone = 0; + + switch (this->effect) { + case 1: + this->texture = sCircleWipeWaveTex; + break; + case 2: + this->texture = sCircleWipeRippleTex; + break; + case 3: + this->texture = sCircleWipeStarburstTex; + break; + default: + this->texture = sCircleWipeDefaultTex; + break; + } + + this->texture = ResourceMgr_LoadTexByName(this->texture); + + if (this->speed == 0) { + this->step = 0x14; + } else { + this->step = 0xA; + } + + if (this->typeColor == 0) { + this->color.rgba = RGBA8(0, 0, 0, 255); + } else if (this->typeColor == 1) { + this->color.rgba = RGBA8(160, 160, 160, 255); + } else if (this->typeColor == 2) { + // yes, really. + this->color.r = 100; + this->color.g = 100; + this->color.b = 100; + this->color.a = 255; + } else { + this->step = 0x28; + this->color.rgba = this->effect == 1 ? RGBA8(0, 0, 0, 255) : RGBA8(160, 160, 160, 255); + } + if (this->unk_14 != 0) { + this->texY = 0; + if (this->typeColor == 3) { + this->texY = 0xFA; + } + } else { + this->texY = 0x1F4; + if (this->effect == 2) { + Audio_PlaySoundGeneral(NA_SE_OC_SECRET_WARP_OUT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + guPerspective(&this->projection, &this->normal, 60.0f, (4.0f / 3.0f), 10.0f, 12800.0f, 1.0f); + guLookAt(&this->lookAt, 0.0f, 0.0f, 400.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); +} + +void* TransitionCircle_Init(void* thisx) { + TransitionCircle* this = (TransitionCircle*)thisx; + + memset(this, 0, sizeof(*this)); + return this; +} + +void TransitionCircle_Destroy(void* thisx) { +} + +void TransitionCircle_Update(void* thisx, s32 updateRate) { + TransitionCircle* this = (TransitionCircle*)thisx; + s32 temp_t2; + s32 temp_t3; + + if (this->unk_14 != 0) { + if (this->texY == 0) { + if (this->effect == 2) { + Audio_PlaySoundGeneral(NA_SE_OC_SECRET_WARP_IN, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + this->texY += this->step * 3 / updateRate; + if (this->texY >= 0x1F4) { + this->texY = 0x1F4; + this->isDone = 1; + } + } else { + this->texY -= this->step * 3 / updateRate; + if (this->typeColor != 3) { + if (this->texY <= 0) { + this->texY = 0; + this->isDone = 1; + } + } else { + if (this->texY < 0xFB) { + this->texY = 0xFA; + this->isDone = 1; + } + } + } +} + +void TransitionCircle_Draw(void* thisx, Gfx** gfxP) { + Gfx* gfx = *gfxP; + Mtx* modelView; + TransitionCircle* this = (TransitionCircle*)thisx; + Gfx* texScroll; + // These variables are a best guess based on the other transition types. + f32 tPos = 0.0f; + f32 rot = 0.0f; + f32 scale = 14.8f; + + modelView = this->modelView[this->frame]; + + this->color.rgba = 0xFFFFFFFF; + + this->frame ^= 1; + gDPPipeSync(gfx++); + texScroll = Gfx_BranchTexScroll(&gfx, this->texX, this->texY, 0x10, 0x40); + gSPSegment(gfx++, 9, texScroll); + gSPSegment(gfx++, 8, this->texture); + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->color.rgba); + gDPSetColor(gfx++, G_SETENVCOLOR, this->color.rgba); + gSPMatrix(gfx++, &this->projection, G_MTX_PROJECTION | G_MTX_LOAD); + gSPPerspNormalize(gfx++, this->normal); + gSPMatrix(gfx++, &this->lookAt, G_MTX_PROJECTION | G_MTX_NOPUSH | G_MTX_MUL); + + float aspectRatio = OTRGetAspectRatio(); + + if (scale != 1.0f) { + guScale(&modelView[0], scale * aspectRatio, scale * aspectRatio, 1.0f); + gSPMatrix(gfx++, &modelView[0], G_MTX_LOAD); + } + + if (rot != 0.0f) { + guRotate(&modelView[1], rot, 0.0f, 0.0f, 1.0f); + gSPMatrix(gfx++, &modelView[1], G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + } + + if ((tPos != 0.0f) || (tPos != 0.0f)) { + guTranslate(&modelView[2], tPos, tPos, 0.0f); + gSPMatrix(gfx++, &modelView[2], G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + } + + // OTRTODO: This is an ugly hack but it will do for now... + Vtx* vtx = ResourceMgr_LoadVtxByName(sCircleWipeVtx); + Gfx var1 = gsSPVertex(vtx, 32, 0); + Gfx var2 = gsSPVertex(&vtx[31], 3, 0); + __sCircleDList[0xe] = var1; + __sCircleDList[0x17] = var2; + + gSPDisplayList(gfx++, __sCircleDList); + gDPPipeSync(gfx++); + *gfxP = gfx; +} + +s32 TransitionCircle_IsDone(void* thisx) { + TransitionCircle* this = (TransitionCircle*)thisx; + + return this->isDone; +} + +void TransitionCircle_SetType(void* thisx, s32 type) { + TransitionCircle* this = (TransitionCircle*)thisx; + + if (type & 0x80) { + this->unk_14 = (type >> 5) & 0x1; + this->typeColor = (type >> 3) & 0x3; + this->speed = type & 0x1; + this->effect = (type >> 1) & 0x3; + } else if (type == 1) { + this->unk_14 = 1; + } else { + this->unk_14 = 0; + } +} + +void TransitionCircle_SetColor(void* thisx, u32 color) { + TransitionCircle* this = (TransitionCircle*)thisx; + + this->color.rgba = color; +} + +void TransitionCircle_SetEnvColor(void* thisx, u32 envColor) { + TransitionCircle* this = (TransitionCircle*)thisx; + + this->envColor.rgba = envColor; +} diff --git a/soh/src/code/z_fbdemo_fade.c b/soh/src/code/z_fbdemo_fade.c new file mode 100644 index 000000000..5cec2c4d9 --- /dev/null +++ b/soh/src/code/z_fbdemo_fade.c @@ -0,0 +1,127 @@ +#include "global.h" +#include "vt.h" + +#include + +static Gfx sRCPSetupFade[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | + G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_1PRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsDPSetCombineLERP(0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE, 0, 0, 0, PRIMITIVE), + gsSPEndDisplayList(), +}; + +void TransitionFade_Start(void* thisx) { + TransitionFade* this = (TransitionFade*)thisx; + + switch (this->fadeType) { + case 0: + break; + case 1: + this->fadeTimer = 0; + this->fadeColor.a = this->fadeDirection != 0 ? 0xFF : 0; + break; + case 2: + this->fadeColor.a = 0; + break; + } + this->isDone = 0; +} + +void* TransitionFade_Init(void* thisx) { + TransitionFade* this = (TransitionFade*)thisx; + + memset(this, 0, sizeof(*this)); + return this; +} + +void TransitionFade_Destroy(void* thisx) { +} + +void TransitionFade_Update(void* thisx, s32 updateRate) { + s32 alpha; + s16 newAlpha; + TransitionFade* this = (TransitionFade*)thisx; + + switch (this->fadeType) { + case 0: + break; + case 1: + this->fadeTimer += updateRate; + if (this->fadeTimer >= gSaveContext.fadeDuration) { + this->fadeTimer = gSaveContext.fadeDuration; + this->isDone = 1; + } + if (!gSaveContext.fadeDuration) { + // "Divide by 0! Zero is included in ZCommonGet fade_speed" + osSyncPrintf(VT_COL(RED, WHITE) "0除算! ZCommonGet fade_speed に0がはいってる" VT_RST); + } + + alpha = (255.0f * this->fadeTimer) / ((void)0, gSaveContext.fadeDuration); + this->fadeColor.a = (this->fadeDirection != 0) ? 255 - alpha : alpha; + break; + case 2: + newAlpha = this->fadeColor.a; + if (iREG(50) != 0) { + if (iREG(50) < 0) { + if (Math_StepToS(&newAlpha, 255, 255)) { + iREG(50) = 150; + } + } else { + Math_StepToS(&iREG(50), 20, 60); + if (Math_StepToS(&newAlpha, 0, iREG(50))) { + iREG(50) = 0; + this->isDone = 1; + } + } + } + this->fadeColor.a = newAlpha; + break; + } +} + +void TransitionFade_Draw(void* thisx, Gfx** gfxP) { + TransitionFade* this = (TransitionFade*)thisx; + Gfx* gfx; + Color_RGBA8_u32* color = &this->fadeColor; + + if (color->a > 0) { + gfx = *gfxP; + gSPDisplayList(gfx++, sRCPSetupFade); + gDPSetPrimColor(gfx++, 0, 0, color->r, color->g, color->b, color->a); + gDPFillRectangle(gfx++, 0, 0, gScreenWidth - 1, gScreenHeight - 1); + gDPPipeSync(gfx++); + *gfxP = gfx; + } +} + +s32 TransitionFade_IsDone(void* thisx) { + TransitionFade* this = (TransitionFade*)thisx; + + return this->isDone; +} + +void TransitionFade_SetColor(void* thisx, u32 color) { + TransitionFade* this = (TransitionFade*)thisx; + + this->fadeColor.rgba = color; +} + +void TransitionFade_SetType(void* thisx, s32 type) { + TransitionFade* this = (TransitionFade*)thisx; + + if (type == 1) { + this->fadeType = 1; + this->fadeDirection = 1; + } else if (type == 2) { + this->fadeType = 1; + this->fadeDirection = 0; + } else if (type == 3) { + this->fadeType = 2; + } else { + this->fadeType = 0; + } +} diff --git a/soh/src/code/z_fbdemo_triforce.c b/soh/src/code/z_fbdemo_triforce.c new file mode 100644 index 000000000..417ae3d8a --- /dev/null +++ b/soh/src/code/z_fbdemo_triforce.c @@ -0,0 +1,132 @@ +#include "global.h" +#include + +#include "code/fbdemo_triforce/z_fbdemo_triforce.h" + +void TransitionTriforce_Start(void* thisx) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + + switch (this->state) { + case 1: + case 2: + this->transPos = 1.0f; + return; + } + this->transPos = 0.03f; +} + +void* TransitionTriforce_Init(void* thisx) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + + memset(this,0, sizeof(*this)); + guOrtho(&this->projection, -160.0f, 160.0f, -120.0f, 120.0f, -1000.0f, 1000.0f, 1.0f); + this->transPos = 1.0f; + this->state = 2; + this->step = 0.015f; + this->fadeDirection = 1; + return this; +} + +void TransitionTriforce_Destroy(void* thisx) { +} + +void TransitionTriforce_Update(void* thisx, s32 updateRate) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + f32 temp_f0; + s32 i; + + for (i = updateRate; i > 0; i--) { + if (this->state == 1) { + this->transPos = CLAMP_MIN(this->transPos * (1.0f - this->step), 0.03f); + } else if (this->state == 2) { + this->transPos = CLAMP_MIN(this->transPos - this->step, 0.03f); + } else if (this->state == 3) { + this->transPos = CLAMP_MAX(this->transPos / (1.0f - this->step), 1.0f); + } else if (this->state == 4) { + this->transPos = CLAMP_MAX(this->transPos + this->step, 1.0f); + } + } +} + +void TransitionTriforce_SetColor(void* thisx, u32 color) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + + this->color.rgba = color; +} + +void TransitionTriforce_SetType(void* thisx, s32 type) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + + this->fadeDirection = type; +} + +// unused +void TransitionTriforce_SetState(void* thisx, s32 state) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + + this->state = state; +} + +void TransitionTriforce_Draw(void* thisx, Gfx** gfxP) { + Gfx* gfx = *gfxP; + Mtx* modelView; + f32 scale; + TransitionTriforce* this = (TransitionTriforce*)thisx; + s32 pad; + f32 rotation = this->transPos * 360.0f; + + modelView = this->modelView[this->frame]; + scale = this->transPos * 0.625f; + this->frame ^= 1; + osSyncPrintf("rate=%f tx=%f ty=%f rotate=%f\n", this->transPos, 0.0f, 0.0f, rotation); + guScale(&modelView[0], scale, scale, 1.0f); + guRotate(&modelView[1], rotation, 0.0f, 0.0f, 1.0f); + guTranslate(&modelView[2], 0.0f, 0.0f, 0.0f); + gDPPipeSync(gfx++); + gSPDisplayList(gfx++, sTriforceWipeDL); + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->color.rgba); + gDPSetCombineMode(gfx++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + gSPMatrix(gfx++, &this->projection, G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(gfx++, &modelView[0], G_MTX_LOAD); + gSPMatrix(gfx++, &modelView[1], G_MTX_NOPUSH | G_MTX_MODELVIEW | G_MTX_MUL); + gSPMatrix(gfx++, &modelView[2], G_MTX_NOPUSH | G_MTX_MODELVIEW | G_MTX_MUL); + gSPVertex(gfx++, sTriforceWipeVtx, 10, 0); + if (!TransitionTriforce_IsDone(this)) { + switch (this->fadeDirection) { + case 1: + gSP2Triangles(gfx++, 0, 4, 5, 0, 4, 1, 3, 0); + gSP1Triangle(gfx++, 5, 3, 2, 0); + break; + case 2: + gSP2Triangles(gfx++, 3, 4, 5, 0, 0, 2, 6, 0); + gSP2Triangles(gfx++, 0, 6, 7, 0, 1, 0, 7, 0); + gSP2Triangles(gfx++, 1, 7, 8, 0, 1, 8, 9, 0); + gSP2Triangles(gfx++, 1, 9, 2, 0, 2, 9, 6, 0); + break; + } + } else { + switch (this->fadeDirection) { + case 1: + break; + case 2: + gSP1Quadrangle(gfx++, 6, 7, 8, 9, 0); + break; + } + } + gDPPipeSync(gfx++); + *gfxP = gfx; +} + +s32 TransitionTriforce_IsDone(void* thisx) { + TransitionTriforce* this = (TransitionTriforce*)thisx; + + s32 ret = 0; + + if (this->state == 1 || this->state == 2) { + return this->transPos <= 0.03f; + + } else if (this->state == 3 || this->state == 4) { + return this->transPos >= 1.0f; + } + return ret; +} diff --git a/soh/src/code/z_fbdemo_wipe1.c b/soh/src/code/z_fbdemo_wipe1.c new file mode 100644 index 000000000..3c3587420 --- /dev/null +++ b/soh/src/code/z_fbdemo_wipe1.c @@ -0,0 +1,146 @@ +#include "global.h" +#include + +#include "code/fbdemo_wipe1/z_fbdemo_wipe1.h" + +Gfx sWipeDList[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | + G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH), + gsSPSetGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_1PRIMITIVE, + G_AC_NONE | G_ZS_PRIM | G_RM_PASS | G_RM_AA_ZB_TEX_EDGE2), + gsDPSetCombineLERP(TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, COMBINED, 0, + PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0), + gsDPSetPrimDepth(0, 0), + gsDPLoadTextureBlock_4b(sWipe1Tex, G_IM_FMT_I, 64, 64, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_MIRROR | G_TX_WRAP, 6, 6, + 11, G_TX_NOLOD), + gsDPLoadMultiBlock_4b(sWipe1Tex, 0x0100, 1, G_IM_FMT_I, 64, 64, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_MIRROR | G_TX_WRAP, 6, 6, 11, 1), + gsDPSetTextureLUT(G_TT_NONE), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsSPDisplayList(0x08000000), + gsSPVertex(sWipe1Vtx, 25, 0), + gsSP2Triangles(0, 1, 2, 0, 1, 3, 4, 0), + gsSP2Triangles(5, 6, 7, 0, 6, 8, 9, 0), + gsSP2Triangles(8, 10, 11, 0, 10, 12, 13, 0), + gsSP2Triangles(12, 14, 15, 0, 14, 16, 17, 0), + gsSP2Triangles(16, 18, 19, 0, 18, 20, 21, 0), + gsSP2Triangles(20, 22, 23, 0, 22, 0, 24, 0), + gsSPEndDisplayList(), +}; + +// unused. +Gfx sWipeSyncDList[] = { + gsDPPipeSync(), + gsSPEndDisplayList(), +}; + +void TransitionWipe_Start(void* thisx) { + TransitionWipe* this = (TransitionWipe*)thisx; + + this->isDone = 0; + + if (this->direction) { + this->texY = 0x14D; + } else { + this->texY = 0x264; + } + + guPerspective(&this->projection, &this->normal, 60.0f, (4.0 / 3.0f), 10.0f, 12800.0f, 1.0f); + guLookAt(&this->lookAt, 0.0f, 0.0f, 400.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); +} + +void* TransitionWipe_Init(void* thisx) { + TransitionWipe* this = (TransitionWipe*)thisx; + + memset(this, 0, sizeof(*this)); + return this; +} + +void TransitionWipe_Destroy(void* thisx) { +} + +void TransitionWipe_Update(void* thisx, s32 updateRate) { + TransitionWipe* this = (TransitionWipe*)thisx; + u8 unk1419; + + if (this->direction != 0) { + unk1419 = gSaveContext.unk_1419; + this->texY += (unk1419 * 3) / updateRate; + if (this->texY >= 0x264) { + this->texY = 0x264; + this->isDone = 1; + } + } else { + unk1419 = gSaveContext.unk_1419; + this->texY -= (unk1419 * 3) / updateRate; + if (this->texY < 0x14E) { + this->texY = 0x14D; + this->isDone = 1; + } + } +} + +void TransitionWipe_Draw(void* thisx, Gfx** gfxP) { + Gfx* gfx = *gfxP; + Mtx* modelView; + TransitionWipe* this = (TransitionWipe*)thisx; + s32 pad[4]; + Gfx* tex; + + modelView = this->modelView[this->frame]; + + this->frame ^= 1; + guScale(&modelView[0], 0.56f, 0.56f, 1.0f); + guRotate(&modelView[1], 0.0f, 0.0f, 0.0f, 1.0f); + guTranslate(&modelView[2], 0.0f, 0.0f, 0.0f); + gDPPipeSync(gfx++); + tex = Gfx_BranchTexScroll(&gfx, this->texX, this->texY, 0, 0); + gSPSegment(gfx++, 8, tex); + gDPSetPrimColor(gfx++, 0, 0x80, this->color.r, this->color.g, this->color.b, 255); + gSPMatrix(gfx++, &this->projection, G_MTX_LOAD | G_MTX_PROJECTION); + gSPPerspNormalize(gfx++, this->normal); + gSPMatrix(gfx++, &this->lookAt, G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(gfx++, &modelView[0], G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(gfx++, &modelView[1], G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPMatrix(gfx++, &modelView[2], G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, sWipeDList); + gDPPipeSync(gfx++); + *gfxP = gfx; +} + +s32 TransitionWipe_IsDone(void* thisx) { + TransitionWipe* this = (TransitionWipe*)thisx; + + return this->isDone; +} + +void TransitionWipe_SetType(void* thisx, s32 type) { + TransitionWipe* this = (TransitionWipe*)thisx; + + if (type == 1) { + this->direction = 1; + } else { + this->direction = 0; + } + + if (this->direction != 0) { + this->texY = 0x14D; + } else { + this->texY = 0x264; + } +} + +void TransitionWipe_SetColor(void* thisx, u32 color) { + TransitionWipe* this = (TransitionWipe*)thisx; + + this->color.rgba = color; +} + +void TransitionWipe_SetEnvColor(void* thisx, u32 color) { + TransitionWipe* this = (TransitionWipe*)thisx; + + this->envColor.rgba = color; +} diff --git a/soh/src/code/z_fcurve_data_skelanime.c b/soh/src/code/z_fcurve_data_skelanime.c new file mode 100644 index 000000000..78169e664 --- /dev/null +++ b/soh/src/code/z_fcurve_data_skelanime.c @@ -0,0 +1,186 @@ +#include "global.h" + +void SkelCurve_Clear(SkelAnimeCurve* skelCurve) { + skelCurve->limbCount = 0; + skelCurve->limbList = NULL; + skelCurve->transUpdIdx = NULL; + skelCurve->animCurFrame = 0.0f; + skelCurve->animSpeed = 0.0f; + skelCurve->animFinalFrame = 0.0f; + skelCurve->transforms = NULL; + skelCurve->unk_0C = 0.0f; +} + +s32 SkelCurve_Init(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, SkelCurveLimbList* limbListSeg, + TransformUpdateIndex* transUpdIdx) { + + if (ResourceMgr_OTRSigCheck(limbListSeg)) + limbListSeg = ResourceMgr_LoadSkeletonByName(limbListSeg); + + SkelCurveLimb** limbs; + SkelCurveLimbList* limbList = SEGMENTED_TO_VIRTUAL(limbListSeg); + + skelCurve->limbCount = limbList->limbCount; + skelCurve->limbList = SEGMENTED_TO_VIRTUAL(limbList->limbs); + + skelCurve->transforms = ZeldaArena_MallocDebug(sizeof(*skelCurve->transforms) * skelCurve->limbCount, + "../z_fcurve_data_skelanime.c", 125); + ASSERT(skelCurve->transforms != NULL, "this->now_joint != NULL", "../z_fcurve_data_skelanime.c", 127); + skelCurve->animCurFrame = 0.0f; + return 1; +} + +void SkelCurve_Destroy(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve) { + if (skelCurve->transforms != NULL) { + ZeldaArena_FreeDebug(skelCurve->transforms, "../z_fcurve_data_skelanime.c", 146); + } +} + +void SkelCurve_SetAnim(SkelAnimeCurve* skelCurve, TransformUpdateIndex* transUpdIdx, f32 arg2, f32 animFinalFrame, + f32 animCurFrame, f32 animSpeed) { + skelCurve->unk_0C = arg2 - skelCurve->animSpeed; + skelCurve->animFinalFrame = animFinalFrame; + skelCurve->animCurFrame = animCurFrame; + skelCurve->animSpeed = animSpeed; + skelCurve->transUpdIdx = transUpdIdx; +} + +s32 SkelCurve_Update(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve) { + s16* transforms; + u8* transformRefIdx; + TransformUpdateIndex* transformIndex; + u16* transformCopyValues; + s32 i; + s32 ret = 0; + s32 k; + TransformData* transData; + f32 transformValue; + s32 j; + + transformIndex = SEGMENTED_TO_VIRTUAL(skelCurve->transUpdIdx); + + if (ResourceMgr_OTRSigCheck(transformIndex)) + transformIndex = ResourceMgr_LoadAnimByName(transformIndex); + + transformRefIdx = SEGMENTED_TO_VIRTUAL(transformIndex->refIndex); + transData = SEGMENTED_TO_VIRTUAL(transformIndex->transformData); + transformCopyValues = SEGMENTED_TO_VIRTUAL(transformIndex->copyValues); + transforms = (s16*)skelCurve->transforms; + + skelCurve->animCurFrame += skelCurve->animSpeed * R_UPDATE_RATE * 0.5f; + + if ((skelCurve->animSpeed >= 0.0f && skelCurve->animCurFrame > skelCurve->animFinalFrame) || + (skelCurve->animSpeed < 0.0f && skelCurve->animCurFrame < skelCurve->animFinalFrame)) { + skelCurve->animCurFrame = skelCurve->animFinalFrame; + ret = 1; + } + + for (i = 0; i < skelCurve->limbCount; i++) { + for (j = 0; j < 3; j++) { + for (k = 0; k < 3; k++, transformRefIdx++, transforms++) { + if (*transformRefIdx == 0) { + transformValue = *transformCopyValues; + *transforms = transformValue; + transformCopyValues++; + } else { + transformValue = func_8006C5A8(skelCurve->animCurFrame, transData, *transformRefIdx); + transData += *transformRefIdx; + if (j == 0) { + *transforms = transformValue * 1024.0f; + } else if (j == 1) { + *transforms = transformValue * (32768.0f / 180.0f); + } else { + *transforms = transformValue * 100.0f; + } + } + } + } + } + + return ret; +} + +void SkelCurve_DrawLimb(GlobalContext* globalCtx, s32 limbIndex, SkelAnimeCurve* skelCurve, + OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* data) { + SkelCurveLimb* limb = SEGMENTED_TO_VIRTUAL(skelCurve->limbList[limbIndex]); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 279); + + Matrix_Push(); + + if (overrideLimbDraw == NULL || + (overrideLimbDraw != NULL && overrideLimbDraw(globalCtx, skelCurve, limbIndex, data))) { + Vec3f scale; + Vec3s rot; + Vec3f pos; + Gfx* dList; + Vec3s* transform = (Vec3s*)&skelCurve->transforms[limbIndex]; + + scale.x = transform->x / 1024.0f; + scale.y = transform->y / 1024.0f; + scale.z = transform->z / 1024.0f; + transform++; + rot.x = transform->x; + rot.y = transform->y; + rot.z = transform->z; + transform++; + pos.x = transform->x; + pos.y = transform->y; + pos.z = transform->z; + + Matrix_TranslateRotateZYX(&pos, &rot); + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + + if (lod == 0) { + s32 pad1; + + dList = limb->dList[0]; + if (dList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 321), + G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, dList); + } + } else if (lod == 1) { + s32 pad2; + + dList = limb->dList[0]; + if (dList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 332), + G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, dList); + } + dList = limb->dList[1]; + if (dList != NULL) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 338), + G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, dList); + } + } else { + // "FcSkeletonInfo_draw_child (): Not supported" + osSyncPrintf("FcSkeletonInfo_draw_child():未対応\n"); + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, skelCurve, limbIndex, data); + } + + if (limb->firstChildIdx != LIMB_DONE) { + SkelCurve_DrawLimb(globalCtx, limb->firstChildIdx, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); + } + + Matrix_Pop(); + + if (limb->nextLimbIdx != LIMB_DONE) { + SkelCurve_DrawLimb(globalCtx, limb->nextLimbIdx, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fcurve_data_skelanime.c", 371); +} + +void SkelCurve_Draw(Actor* actor, GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, + OverrideCurveLimbDraw overrideLimbDraw, PostCurveLimbDraw postLimbDraw, s32 lod, void* data) { + if (skelCurve->transforms != NULL) { + SkelCurve_DrawLimb(globalCtx, 0, skelCurve, overrideLimbDraw, postLimbDraw, lod, data); + } +} diff --git a/soh/src/code/z_frame_advance.c b/soh/src/code/z_frame_advance.c new file mode 100644 index 000000000..930db59b3 --- /dev/null +++ b/soh/src/code/z_frame_advance.c @@ -0,0 +1,29 @@ +#include "global.h" + +void FrameAdvance_Init(FrameAdvanceContext* frameAdvCtx) { + frameAdvCtx->timer = 0; + frameAdvCtx->enabled = false; +} + +/** + * Frame advance allows you to advance through the game one frame at a time on command. + * To enable, hold R and press Dpad Down on the specified controller. + * To advance a frame, hold Z and press R. + * Holding Z and R will advance a frame every half second. + * + * This function returns true when frame advance is not active (game will run normally) + */ +s32 FrameAdvance_Update(FrameAdvanceContext* frameAdvCtx, Input* input) { + if (CHECK_BTN_ALL(input->cur.button, BTN_R) && CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + frameAdvCtx->enabled = !frameAdvCtx->enabled; + } + + if (!frameAdvCtx->enabled || (CHECK_BTN_ALL(input->cur.button, BTN_Z) && + (CHECK_BTN_ALL(input->press.button, BTN_R) || + (CHECK_BTN_ALL(input->cur.button, BTN_R) && (++frameAdvCtx->timer >= 9))))) { + frameAdvCtx->timer = 0; + return true; + } + + return false; +} diff --git a/soh/src/code/z_game_dlftbls.c b/soh/src/code/z_game_dlftbls.c new file mode 100644 index 000000000..0f5b845d8 --- /dev/null +++ b/soh/src/code/z_game_dlftbls.c @@ -0,0 +1,21 @@ +#include "global.h" + +//#define GAMESTATE_OVERLAY(name, init, destroy, size) \ +// { \ +// NULL, (uintptr_t)_ovl_##name##SegmentRomStart, (uintptr_t)_ovl_##name##SegmentRomEnd, _ovl_##name##SegmentStart, \ +// _ovl_##name##SegmentEnd, 0, init, destroy, 0, 0, 0, size \ +// } +#define GAMESTATE_OVERLAY_INTERNAL(init, destroy, size) \ + { NULL, 0, 0, NULL, NULL, 0, init, destroy, 0, 0, 0, size } + +#define GAMESTATE_OVERLAY(name, init, destroy, size) \ + { NULL, 0, 0, NULL, NULL, 0, init, destroy, 0, 0, 0, size } + +GameStateOverlay gGameStateOverlayTable[] = { + GAMESTATE_OVERLAY_INTERNAL(TitleSetup_Init, TitleSetup_Destroy, sizeof(GameState)), + GAMESTATE_OVERLAY(select, Select_Init, Select_Destroy, sizeof(SelectContext)), + GAMESTATE_OVERLAY(title, Title_Init, Title_Destroy, sizeof(TitleContext)), + GAMESTATE_OVERLAY_INTERNAL(Gameplay_Init, Gameplay_Destroy, sizeof(GlobalContext)), + GAMESTATE_OVERLAY(opening, Opening_Init, Opening_Destroy, sizeof(OpeningContext)), + GAMESTATE_OVERLAY(file_choose, FileChoose_Init, FileChoose_Destroy, sizeof(FileChooseContext)), +}; diff --git a/soh/src/code/z_game_over.c b/soh/src/code/z_game_over.c new file mode 100644 index 000000000..3605c5bd6 --- /dev/null +++ b/soh/src/code/z_game_over.c @@ -0,0 +1,148 @@ +#include "global.h" + +void GameOver_Init(GlobalContext* globalCtx) { + globalCtx->gameOverCtx.state = GAMEOVER_INACTIVE; +} + +void GameOver_FadeInLights(GlobalContext* globalCtx) { + GameOverContext* gameOverCtx = &globalCtx->gameOverCtx; + + if ((gameOverCtx->state >= GAMEOVER_DEATH_WAIT_GROUND && gameOverCtx->state < GAMEOVER_REVIVE_START) || + (gameOverCtx->state >= GAMEOVER_REVIVE_RUMBLE && gameOverCtx->state < GAMEOVER_REVIVE_FADE_OUT)) { + Environment_FadeInGameOverLights(globalCtx); + } +} + +// This variable cannot be moved into this file as all of z_message_PAL rodata is in the way +extern s16 gGameOverTimer; + +void GameOver_Update(GlobalContext* globalCtx) { + GameOverContext* gameOverCtx = &globalCtx->gameOverCtx; + s16 i; + s16 j; + s32 v90; + s32 v91; + s32 v92; + + switch (gameOverCtx->state) { + case GAMEOVER_DEATH_START: + Message_CloseTextbox(globalCtx); + + gSaveContext.timer1State = 0; + gSaveContext.timer2State = 0; + gSaveContext.eventInf[1] &= ~1; + + // search inventory for spoiling items and revert if necessary + for (i = 0; i < ARRAY_COUNT(gSpoilingItems); i++) { + if (INV_CONTENT(ITEM_POCKET_EGG) == gSpoilingItems[i]) { + INV_CONTENT(gSpoilingItemReverts[i]) = gSpoilingItemReverts[i]; + + // search c buttons for the found spoiling item and revert if necessary + for (j = 1; j < ARRAY_COUNT(gSaveContext.equips.buttonItems); j++) { + if (gSaveContext.equips.buttonItems[j] == gSpoilingItems[i]) { + gSaveContext.equips.buttonItems[j] = gSpoilingItemReverts[i]; + Interface_LoadItemIcon1(globalCtx, j); + } + } + } + } + + // restore "temporary B" to the B Button if not a sword item + if (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI && + gSaveContext.equips.buttonItems[0] != ITEM_SWORD_MASTER && + gSaveContext.equips.buttonItems[0] != ITEM_SWORD_BGS && + gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KNIFE) { + + if (gSaveContext.buttonStatus[0] != BTN_ENABLED) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + } else { + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + } + } + + gSaveContext.nayrusLoveTimer = 2000; + gSaveContext.naviTimer = 0; + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + gSaveContext.eventInf[0] = 0; + gSaveContext.eventInf[1] = 0; + gSaveContext.eventInf[2] = 0; + gSaveContext.eventInf[3] = 0; + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = gSaveContext.buttonStatus[4] = BTN_ENABLED; + gSaveContext.unk_13E7 = gSaveContext.unk_13E8 = gSaveContext.unk_13EA = gSaveContext.unk_13EC = 0; + + Environment_InitGameOverLights(globalCtx); + gGameOverTimer = 20; + if (1) {} + v90 = VREG(90); + v91 = VREG(91); + v92 = VREG(92); + + func_800AA000(0.0f, ((v90 > 0x64) ? 0xFF : (v90 * 0xFF) / 0x64), (CLAMP_MAX(v91 * 3, 0xFF)), + ((v92 > 0x64) ? 0xFF : (v92 * 0xFF) / 0x64)); + + gameOverCtx->state = GAMEOVER_DEATH_WAIT_GROUND; + break; + + case GAMEOVER_DEATH_WAIT_GROUND: + break; + + case GAMEOVER_DEATH_DELAY_MENU: + gGameOverTimer--; + + if (gGameOverTimer == 0) { + globalCtx->pauseCtx.state = 8; + gameOverCtx->state++; + func_800AA15C(); + } + break; + + case GAMEOVER_REVIVE_START: + gameOverCtx->state++; + gGameOverTimer = 0; + Environment_InitGameOverLights(globalCtx); + ShrinkWindow_SetVal(0x20); + return; + + case GAMEOVER_REVIVE_RUMBLE: + gGameOverTimer = 50; + gameOverCtx->state++; + if (1) {} + + v90 = VREG(90); + v91 = VREG(91); + v92 = VREG(92); + + func_800AA000(0.0f, ((v90 > 0x64) ? 0xFF : (v90 * 0xFF) / 0x64), (CLAMP_MAX(v91 * 3, 0xFF)), + ((v92 > 0x64) ? 0xFF : (v92 * 0xFF) / 0x64)); + break; + + case GAMEOVER_REVIVE_WAIT_GROUND: + gGameOverTimer--; + + if (gGameOverTimer == 0) { + gGameOverTimer = 64; + gameOverCtx->state++; + } + break; + + case GAMEOVER_REVIVE_WAIT_FAIRY: + gGameOverTimer--; + + if (gGameOverTimer == 0) { + gGameOverTimer = 50; + gameOverCtx->state++; + } + break; + + case GAMEOVER_REVIVE_FADE_OUT: + Environment_FadeOutGameOverLights(globalCtx); + gGameOverTimer--; + + if (gGameOverTimer == 0) { + gameOverCtx->state = GAMEOVER_INACTIVE; + } + break; + } +} diff --git a/soh/src/code/z_horse.c b/soh/src/code/z_horse.c new file mode 100644 index 000000000..3a2870116 --- /dev/null +++ b/soh/src/code/z_horse.c @@ -0,0 +1,274 @@ +#include "global.h" +#include "vt.h" + +s32 func_8006CFC0(s32 scene) { + s32 validScenes[] = { SCENE_SPOT00, SCENE_SPOT06, SCENE_SPOT09, SCENE_SPOT12, SCENE_SPOT20 }; + s32 i; + + for (i = 0; i < ARRAY_COUNT(validScenes); i++) { + if (scene == validScenes[i]) { + return 1; + } + } + + return 0; +} + +void func_8006D074(GlobalContext* globalCtx) { + gSaveContext.horseData.scene = SCENE_SPOT00; + gSaveContext.horseData.pos.x = -1840; + gSaveContext.horseData.pos.y = 72; + gSaveContext.horseData.pos.z = 5497; + gSaveContext.horseData.angle = -27353; +} + +void func_8006D0AC(GlobalContext* globalCtx) { + if (gSaveContext.horseData.scene == SCENE_SPOT06) { + gSaveContext.horseData.scene = SCENE_SPOT06; + gSaveContext.horseData.pos.x = -2065; + gSaveContext.horseData.pos.y = -863; + gSaveContext.horseData.pos.z = 1839; + gSaveContext.horseData.angle = 0; + } +} + +typedef struct { + /* 0x00 */ s16 scene; + /* 0x02 */ Vec3s pos; + /* 0x08 */ s16 angle; + /* 0x0A */ s16 type; +} HorseSpawn; + +void func_8006D0EC(GlobalContext* globalCtx, Player* player) { + s32 i; + HorseSpawn horseSpawns[] = { + { SCENE_SPOT00, -460, 100, 6640, 0, 2 }, { SCENE_SPOT06, -1929, -1025, 768, 0, 2 }, + { SCENE_SPOT09, 2566, -259, 767, 0, 2 }, { SCENE_SPOT12, -328, 10, 953, 0, 2 }, + { SCENE_SPOT20, 928, 0, -2280, 0, 2 }, + }; + + if ((AREG(6) != 0) && (Flags_GetEventChkInf(0x18) || (DREG(1) != 0))) { + player->rideActor = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, player->actor.world.pos.x, + player->actor.world.pos.y, player->actor.world.pos.z, player->actor.shape.rot.x, + player->actor.shape.rot.y, player->actor.shape.rot.z, 9); + + ASSERT(player->rideActor != NULL, "player->ride.actor != NULL", "../z_horse.c", 343); + + Actor_MountHorse(globalCtx, player, player->rideActor); + func_8002DE74(globalCtx, player); + gSaveContext.horseData.scene = globalCtx->sceneNum; + + if (globalCtx->sceneNum == SCENE_SPOT12) { + player->rideActor->room = -1; + } + } else if ((globalCtx->sceneNum == SCENE_SPOT12) && (gSaveContext.minigameState == 3)) { + Actor* horseActor; + gSaveContext.minigameState = 0; + horseActor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, 3586.0f, 1413.0f, -402.0f, 0, 0x4000, 0, 1); + horseActor->room = -1; + } else if ((gSaveContext.entranceIndex == 1230) && (gSaveContext.eventChkInf[1] & 0x100)) { + Actor* horseActor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, -25.0f, 0.0f, -1600.0f, 0, -0x4000, 0, 1); + ASSERT(horseActor != NULL, "horse_actor != NULL", "../z_horse.c", 389); + } else if ((globalCtx->sceneNum == gSaveContext.horseData.scene) && + (Flags_GetEventChkInf(0x18) != 0 || DREG(1) != 0)) { + // "Set by existence of horse %d %d %d" + osSyncPrintf("馬存在によるセット %d %d %d\n", gSaveContext.horseData.scene, Flags_GetEventChkInf(0x18), + DREG(1)); + + if (func_8006CFC0(gSaveContext.horseData.scene)) { + Actor* horseActor = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, + gSaveContext.horseData.pos.x, gSaveContext.horseData.pos.y, + gSaveContext.horseData.pos.z, 0, gSaveContext.horseData.angle, 0, 1); + ASSERT(horseActor != NULL, "horse_actor != NULL", "../z_horse.c", 414); + if (globalCtx->sceneNum == SCENE_SPOT12) { + horseActor->room = -1; + } + } else { + osSyncPrintf(VT_COL(RED, WHITE)); + // "Horse_SetNormal():%d set spot is no good." + osSyncPrintf("Horse_SetNormal():%d セットスポットまずいです。\n", gSaveContext.horseData.scene); + osSyncPrintf(VT_RST); + func_8006D074(globalCtx); + } + } else if ((globalCtx->sceneNum == SCENE_SPOT20) && !Flags_GetEventChkInf(0x18) && (DREG(1) == 0)) { + Actor* horseActor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, 0.0f, 0.0f, -500.0f, 0, 0, 0, 1); + ASSERT(horseActor != NULL, "horse_actor != NULL", "../z_horse.c", 443); + } else if (Flags_GetEventChkInf(0x18) || (DREG(1) != 0)) { + for (i = 0; i < ARRAY_COUNT(horseSpawns); i++) { + HorseSpawn* horseSpawn = &horseSpawns[i]; + if (horseSpawn->scene == globalCtx->sceneNum) { + Actor* horseActor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, horseSpawn->pos.x, horseSpawn->pos.y, + horseSpawn->pos.z, 0, horseSpawn->angle, 0, horseSpawn->type); + ASSERT(horseActor != NULL, "horse_actor != NULL", "../z_horse.c", 466); + if (globalCtx->sceneNum == SCENE_SPOT12) { + horseActor->room = -1; + } + + break; + } + } + } else if (!Flags_GetEventChkInf(0x18)) { + if ((DREG(1) == 0) && (globalCtx->sceneNum == SCENE_SOUKO) && !IS_DAY) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, 0.0f, 0.0f, -60.0f, 0, 0x7360, 0, 1); + } + } +} + +typedef struct { + /* 0x00 */ s16 scene; + /* 0x04 */ s32 cutsceneIndex; + /* 0x08 */ Vec3s pos; + /* 0x0E */ s16 angle; + /* 0x10 */ s16 type; +} struct_8011F9B8; + +void func_8006D684(GlobalContext* globalCtx, Player* player) { + s32 pad; + s32 i; + Vec3s spawnPos; + + if ((gSaveContext.entranceIndex == 0x028A || gSaveContext.entranceIndex == 0x028E || + gSaveContext.entranceIndex == 0x0292 || gSaveContext.entranceIndex == 0x0476) && + (gSaveContext.respawnFlag == 0)) { + Vec3s spawnPositions[] = { + { 0xF46F, 0x0139, 0x1E14 }, + { 0xF894, 0x0139, 0x1B67 }, + { 0xF035, 0x0139, 0x1B15 }, + { 0xF6F7, 0x0139, 0x1766 }, + }; + + if (gSaveContext.entranceIndex == 0x028A) { + spawnPos = spawnPositions[0]; + } else if (gSaveContext.entranceIndex == 0x028E) { + spawnPos = spawnPositions[1]; + } else if (gSaveContext.entranceIndex == 0x0292) { + spawnPos = spawnPositions[2]; + } else { + spawnPos = spawnPositions[3]; + } + + player->rideActor = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, spawnPos.x, spawnPos.y, + spawnPos.z, 0, player->actor.world.rot.y, 0, 7); + ASSERT(player->rideActor != NULL, "player->ride.actor != NULL", "../z_horse.c", 561); + + Actor_MountHorse(globalCtx, player, player->rideActor); + func_8002DE74(globalCtx, player); + gSaveContext.horseData.scene = globalCtx->sceneNum; + } else if ((globalCtx->sceneNum == SCENE_SPOT20) && ((gSaveContext.eventInf[0] & 0xF) == 6) && + (Flags_GetEventChkInf(0x18) == 0) && (DREG(1) == 0)) { + player->rideActor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, 894.0f, 0.0f, -2084.0f, 0, -0x7FFF, 0, 5); + ASSERT(player->rideActor != NULL, "player->ride.actor != NULL", "../z_horse.c", 582); + + Actor_MountHorse(globalCtx, player, player->rideActor); + func_8002DE74(globalCtx, player); + gSaveContext.horseData.scene = globalCtx->sceneNum; + + if (globalCtx->sceneNum == SCENE_SPOT12) { + player->rideActor->room = -1; + } + } else { + static struct_8011F9B8 D_8011F9B8[] = { + { 93, 0xFFF0, 0x0E10, 0x0585, 0x0168, 0x8001, 8 }, { 99, 0xFFF0, 0xFF06, 0x0001, 0xF9D4, 0x4000, 6 }, + { 99, 0xFFF1, 0x0000, 0x0000, 0x0000, 0x0000, 5 }, { 99, 0xFFF5, 0x0000, 0x0000, 0x0000, 0x0000, 7 }, + { 81, 0xFFF3, 0xF46F, 0x0139, 0x1E14, 0x0000, 7 }, { 81, 0xFFF4, 0xF894, 0x0139, 0x1B67, 0x0000, 7 }, + { 81, 0xFFF5, 0xF035, 0x0139, 0x1B15, 0x0000, 7 }, { 81, 0xFFF6, 0xF035, 0x0139, 0x1B15, 0x0000, 7 }, + }; + + for (i = 0; i < ARRAY_COUNT(D_8011F9B8); i++) { + if ((globalCtx->sceneNum == D_8011F9B8[i].scene) && + (((void)0, gSaveContext.cutsceneIndex) == D_8011F9B8[i].cutsceneIndex)) { + if (D_8011F9B8[i].type == 7) { + if ((globalCtx->sceneNum == 99) && (((void)0, gSaveContext.cutsceneIndex) == 0xFFF1)) { + D_8011F9B8[i].pos.x = player->actor.world.pos.x; + D_8011F9B8[i].pos.y = player->actor.world.pos.y; + D_8011F9B8[i].pos.z = player->actor.world.pos.z; + } + + player->rideActor = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, + D_8011F9B8[i].pos.x, D_8011F9B8[i].pos.y, D_8011F9B8[i].pos.z, 0, + player->actor.world.rot.y, 0, D_8011F9B8[i].type); + ASSERT(player->rideActor != NULL, "player->ride.actor != NULL", "../z_horse.c", 628); + + Actor_MountHorse(globalCtx, player, player->rideActor); + func_8002DE74(globalCtx, player); + } else if ((D_8011F9B8[i].type == 5) || (D_8011F9B8[i].type == 6) || (D_8011F9B8[i].type == 8)) { + Vec3f sp54; + s32 temp = 0; + + if (((gSaveContext.eventInf[0] & 0x10) >> 4) && D_8011F9B8[i].type == 6) { + temp = 0x8000; + } + + player->rideActor = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, + D_8011F9B8[i].pos.x, D_8011F9B8[i].pos.y, D_8011F9B8[i].pos.z, 0, + D_8011F9B8[i].angle, 0, D_8011F9B8[i].type | temp); + ASSERT(player->rideActor != NULL, "player->ride.actor != NULL", "../z_horse.c", 667); + + player->actor.world.pos.x = D_8011F9B8[i].pos.x; + player->actor.world.pos.y = D_8011F9B8[i].pos.y; + player->actor.world.pos.z = D_8011F9B8[i].pos.z; + player->actor.shape.rot.x = player->actor.shape.rot.z = 0; + player->actor.shape.rot.y = D_8011F9B8[i].angle; + + Actor_MountHorse(globalCtx, player, player->rideActor); + func_8002DE74(globalCtx, player); + + sp54.x = player->actor.world.pos.x - 200.0f; + sp54.y = player->actor.world.pos.y + 100.0f; + sp54.z = player->actor.world.pos.z; + + Gameplay_CameraSetAtEye(globalCtx, globalCtx->activeCamera, &player->actor.world.pos, &sp54); + } else { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, D_8011F9B8[i].pos.x, + D_8011F9B8[i].pos.y, D_8011F9B8[i].pos.z, 0, D_8011F9B8[i].angle, 0, + D_8011F9B8[i].type); + } + break; + } + } + } +} + +void func_8006DC68(GlobalContext* globalCtx, Player* player) { + if (LINK_IS_ADULT) { + if (!func_8006CFC0(gSaveContext.horseData.scene)) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "Horse_Set_Check():%d set spot is no good." + osSyncPrintf("Horse_Set_Check():%d セットスポットまずいです。\n", gSaveContext.horseData.scene); + osSyncPrintf(VT_RST); + func_8006D074(globalCtx); + } + + if (func_8006CFC0(globalCtx->sceneNum)) { + if ((gSaveContext.sceneSetupIndex > 3) || + ((gSaveContext.entranceIndex == 0x028A || gSaveContext.entranceIndex == 0x028E || + gSaveContext.entranceIndex == 0x0292 || gSaveContext.entranceIndex == 0x0476) && + (gSaveContext.respawnFlag == 0)) || + ((globalCtx->sceneNum == SCENE_SPOT20) && ((gSaveContext.eventInf[0] & 0xF) == 6) && + !Flags_GetEventChkInf(0x18) && (DREG(1) == 0))) { + func_8006D684(globalCtx, player); + } else { + func_8006D0EC(globalCtx, player); + } + } + } +} + +void func_8006DD9C(Actor* actor, Vec3f* arg1, s16 arg2) { + s16 x = Math_Vec3f_Yaw(&actor->world.pos, arg1) - actor->world.rot.y; + + if (x > arg2) { + actor->world.rot.y += arg2; + } else if (x < -arg2) { + actor->world.rot.y -= arg2; + } else { + actor->world.rot.y += x; + } + + actor->shape.rot.y = actor->world.rot.y; +} diff --git a/soh/src/code/z_jpeg.c b/soh/src/code/z_jpeg.c new file mode 100644 index 000000000..c289a173e --- /dev/null +++ b/soh/src/code/z_jpeg.c @@ -0,0 +1,366 @@ +#include "global.h" +#include "vt.h" + +#define MARKER_ESCAPE 0x00 +#define MARKER_SOI 0xD8 +#define MARKER_SOF 0xC0 +#define MARKER_DHT 0xC4 +#define MARKER_DQT 0xDB +#define MARKER_DRI 0xDD +#define MARKER_SOS 0xDA +#define MARKER_APP0 0xE0 +#define MARKER_APP1 0xE1 +#define MARKER_APP2 0xE2 +#define MARKER_COM 0xFE +#define MARKER_EOI 0xD9 + +/** + * Configures and schedules a JPEG decoder task and waits for it to finish. + */ +void Jpeg_ScheduleDecoderTask(JpegContext* ctx) +{ +#if 0 + static OSTask_t sJpegTask = { + M_NJPEGTASK, // type + 0, // flags + NULL, // ucode_boot + 0, // ucode_boot_size + gJpegUCode, // ucode + 0x1000, // ucode_size + gJpegUCodeData, // ucode_data + 0x800, // ucode_data_size + NULL, // dram_stack + 0, // dram_stack_size + NULL, // output_buff + NULL, // output_buff_size + NULL, // data_ptr + sizeof(JpegTaskData), // data_size + NULL, // yield_data_ptr + 0x200, // yield_data_size + }; + + JpegWork* workBuf = ctx->workBuf; + s32 pad[2]; + + workBuf->taskData.address = PHYSICAL_TO_VIRTUAL(&workBuf->data); + workBuf->taskData.mode = ctx->mode; + workBuf->taskData.mbCount = 4; + workBuf->taskData.qTableYPtr = PHYSICAL_TO_VIRTUAL(&workBuf->qTableY); + workBuf->taskData.qTableUPtr = PHYSICAL_TO_VIRTUAL(&workBuf->qTableU); + workBuf->taskData.qTableVPtr = PHYSICAL_TO_VIRTUAL(&workBuf->qTableV); + + sJpegTask.flags = 0; + sJpegTask.ucode_boot = SysUcode_GetUCodeBoot(); + sJpegTask.ucode_boot_size = SysUcode_GetUCodeBootSize(); + sJpegTask.yield_data_ptr = (u64*)&workBuf->yieldData; + sJpegTask.data_ptr = (u64*)&workBuf->taskData; + + ctx->scTask.next = NULL; + ctx->scTask.flags = OS_SC_NEEDS_RSP; + ctx->scTask.msgQ = &ctx->mq; + ctx->scTask.msg = NULL; + ctx->scTask.framebuffer = NULL; + ctx->scTask.list.t = sJpegTask; + + osSendMesg(&gSchedContext.cmdQ, (OSMesg)&ctx->scTask, OS_MESG_BLOCK); + Sched_SendEntryMsg(&gSchedContext); // osScKickEntryMsg + osRecvMesg(&ctx->mq, NULL, OS_MESG_BLOCK); +#endif +} + +/** + * Copies a 16x16 block of decoded image data to the Z-buffer. + */ +void Jpeg_CopyToZbuffer(u16* src, u16* zbuffer, s32 x, s32 y) { + u16* dst = zbuffer + (((y * SCREEN_WIDTH) + x) * 16); + s32 i; + + for (i = 0; i < 16; i++) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + dst[8] = src[8]; + dst[9] = src[9]; + dst[10] = src[10]; + dst[11] = src[11]; + dst[12] = src[12]; + dst[13] = src[13]; + dst[14] = src[14]; + dst[15] = src[15]; + + src += 16; + dst += SCREEN_WIDTH; + } +} + +/** + * Reads an u16 from a possibly unaligned address in memory. + * + * Replaces unaligned 16-bit reads with a pair of aligned reads, allowing for reading the possibly + * unaligned values in JPEG header files. + */ +u16 Jpeg_GetUnalignedU16(u8* ptr) { + if (((u32)ptr & 1) == 0) { + // Read the value normally if it's aligned to a 16-bit address. + return *(u16*)ptr; + } else { + // Read unaligned values using two separate aligned memory accesses when it's not. + return *(u16*)(ptr - 1) << 8 | (*(u16*)(ptr + 1) >> 8); + } +} + +/** + * Parses the markers in the JPEG file, storing information such as the pointer to the image data + * in `ctx` for later processing. + */ +void Jpeg_ParseMarkers(u8* ptr, JpegContext* ctx) { + u32 exit = false; + + ctx->dqtCount = 0; + ctx->dhtCount = 0; + + while (true) { + if (exit) { + break; + } + + // 0xFF indicates the start of a JPEG marker, so look for the next. + if (*ptr++ == 0xFF) { + switch (*ptr++) { + case MARKER_ESCAPE: { + // Compressed value 0xFF is stored as 0xFF00 to escape it, so ignore it. + break; + } + case MARKER_SOI: { + // Start of Image + osSyncPrintf("MARKER_SOI\n"); + break; + } + case MARKER_APP0: { + // Application marker for JFIF + osSyncPrintf("MARKER_APP0 %d\n", Jpeg_GetUnalignedU16(ptr)); + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_APP1: { + // Application marker for EXIF + osSyncPrintf("MARKER_APP1 %d\n", Jpeg_GetUnalignedU16(ptr)); + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_APP2: { + osSyncPrintf("MARKER_APP2 %d\n", Jpeg_GetUnalignedU16(ptr)); + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_DQT: { + // Define Quantization Table, stored for later processing + osSyncPrintf("MARKER_DQT %d %d %02x\n", ctx->dqtCount, Jpeg_GetUnalignedU16(ptr), ptr[2]); + ctx->dqtPtr[ctx->dqtCount++] = ptr + 2; + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_DHT: { + // Define Huffman Table, stored for later processing + osSyncPrintf("MARKER_DHT %d %d %02x\n", ctx->dhtCount, Jpeg_GetUnalignedU16(ptr), ptr[2]); + ctx->dhtPtr[ctx->dhtCount++] = ptr + 2; + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_DRI: { + // Define Restart Interval + osSyncPrintf("MARKER_DRI %d\n", Jpeg_GetUnalignedU16(ptr)); + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_SOF: { + // Start of Frame, stores important metadata of the image. + // Only used for extracting the sampling factors (ctx->mode). + osSyncPrintf("MARKER_SOF %d " + "精度%02x " // "accuracy" + "垂直%d " // "vertical" + "水平%d " // "horizontal" + "compo%02x " + "(1:Y)%d (H0=2,V0=1(422) or 2(420))%02x (量子化テーブル)%02x " + "(2:Cb)%d (H1=1,V1=1)%02x (量子化テーブル)%02x " + "(3:Cr)%d (H2=1,V2=1)%02x (量子化テーブル)%02x\n", + Jpeg_GetUnalignedU16(ptr), + ptr[2], // precision + Jpeg_GetUnalignedU16(ptr + 3), // height + Jpeg_GetUnalignedU16(ptr + 5), // width + ptr[7], // component count (assumed to be 3) + ptr[8], ptr[9], ptr[10], // Y component + ptr[11], ptr[12], ptr[13], // Cb component + ptr[14], ptr[15], ptr[16] // Cr component + ); + + if (ptr[9] == 0x21) { + // component Y : V0 == 1 + ctx->mode = 0; + } else if (ptr[9] == 0x22) { + // component Y : V0 == 2 + ctx->mode = 2; + } + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + case MARKER_SOS: { + // Start of Scan marker, indicates the start of the image data. + osSyncPrintf("MARKER_SOS %d\n", Jpeg_GetUnalignedU16(ptr)); + ptr += Jpeg_GetUnalignedU16(ptr); + ctx->imageData = ptr; + break; + } + case MARKER_EOI: { + // End of Image + osSyncPrintf("MARKER_EOI\n"); + exit = true; + break; + } + default: { + osSyncPrintf("マーカー不明 %02x\n", ptr[-1]); // "Unknown marker" + ptr += Jpeg_GetUnalignedU16(ptr); + break; + } + } + } + } +} + +s32 Jpeg_Decode(void* data, void* zbuffer, void* work, u32 workSize) { + s32 y; + s32 x; + u32 j; + u32 i; + JpegContext ctx; + JpegHuffmanTable hTables[4]; + JpegDecoder decoder; + JpegDecoderState state; + JpegWork* workBuff; + OSTime diff; + OSTime time; + OSTime curTime; + + workBuff = work; + + time = osGetTime(); + // (?) I guess MB_SIZE=0x180, PROC_OF_MBS=5 which means data is not a part of JpegWork + ASSERT(workSize >= sizeof(JpegWork), "worksize >= sizeof(JPEGWork) + MB_SIZE * (PROC_OF_MBS - 1)", "../z_jpeg.c", + 527); + + osCreateMesgQueue(&ctx.mq, &ctx.msg, 1); + MsgEvent_SendNullTask(); + + curTime = osGetTime(); + diff = curTime - time; + time = curTime; + // "Wait for synchronization of fifo buffer" + osSyncPrintf("*** fifoバッファの同期待ち time = %6.3f ms ***\n", OS_CYCLES_TO_USEC(diff) / 1000.0f); + + ctx.workBuf = workBuff; + Jpeg_ParseMarkers(data, &ctx); + + curTime = osGetTime(); + diff = curTime - time; + time = curTime; + // "Check markers for each segment" + osSyncPrintf("*** 各セグメントのマーカーのチェック time = %6.3f ms ***\n", OS_CYCLES_TO_USEC(diff) / 1000.0f); + + switch (ctx.dqtCount) { + case 1: + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[0], &workBuff->qTableY, 3); + break; + case 2: + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[0], &workBuff->qTableY, 1); + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[1], &workBuff->qTableU, 1); + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[1], &workBuff->qTableV, 1); + break; + case 3: + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[0], &workBuff->qTableY, 1); + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[1], &workBuff->qTableU, 1); + JpegUtils_ProcessQuantizationTable(ctx.dqtPtr[2], &workBuff->qTableV, 1); + break; + default: + return -1; + } + + curTime = osGetTime(); + diff = curTime - time; + time = curTime; + // "Create quantization table" + osSyncPrintf("*** 量子化テーブル作成 time = %6.3f ms ***\n", OS_CYCLES_TO_USEC(diff) / 1000.0f); + + switch (ctx.dhtCount) { + case 1: + if (JpegUtils_ProcessHuffmanTable(ctx.dhtPtr[0], &hTables[0], workBuff->codesLengths, workBuff->codes, 4)) { + osSyncPrintf("Error : Cant' make huffman table.\n"); + } + break; + case 4: + if (JpegUtils_ProcessHuffmanTable(ctx.dhtPtr[0], &hTables[0], workBuff->codesLengths, workBuff->codes, 1)) { + osSyncPrintf("Error : Cant' make huffman table.\n"); + } + if (JpegUtils_ProcessHuffmanTable(ctx.dhtPtr[1], &hTables[1], workBuff->codesLengths, workBuff->codes, 1)) { + osSyncPrintf("Error : Cant' make huffman table.\n"); + } + if (JpegUtils_ProcessHuffmanTable(ctx.dhtPtr[2], &hTables[2], workBuff->codesLengths, workBuff->codes, 1)) { + osSyncPrintf("Error : Cant' make huffman table.\n"); + } + if (JpegUtils_ProcessHuffmanTable(ctx.dhtPtr[3], &hTables[3], workBuff->codesLengths, workBuff->codes, 1)) { + osSyncPrintf("Error : Cant' make huffman table.\n"); + } + break; + default: + return -1; + } + + curTime = osGetTime(); + diff = curTime - time; + time = curTime; + // "Huffman table creation" + osSyncPrintf("*** ハフマンテーブル作成 time = %6.3f ms ***\n", OS_CYCLES_TO_USEC(diff) / 1000.0f); + + decoder.imageData = ctx.imageData; + decoder.mode = ctx.mode; + decoder.unk_05 = 2; + decoder.hTablePtrs[0] = &hTables[0]; + decoder.hTablePtrs[1] = &hTables[1]; + decoder.hTablePtrs[2] = &hTables[2]; + decoder.hTablePtrs[3] = &hTables[3]; + decoder.unk_18 = 0; + + x = y = 0; + for (i = 0; i < 300; i += 4) { + if (JpegDecoder_Decode(&decoder, (u16*)workBuff->data, 4, i != 0, &state)) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Error : Can't decode jpeg\n"); + osSyncPrintf(VT_RST); + } else { + Jpeg_ScheduleDecoderTask(&ctx); + osInvalDCache(&workBuff->data, sizeof(workBuff->data[0])); + + for (j = 0; j < ARRAY_COUNT(workBuff->data); j++) { + Jpeg_CopyToZbuffer(workBuff->data[j], zbuffer, x, y); + x++; + + if (x >= 20) { + x = 0; + y++; + } + } + } + } + + curTime = osGetTime(); + diff = curTime - time; + time = curTime; + // "Unfold & draw" + osSyncPrintf("*** 展開 & 描画 time = %6.3f ms ***\n", OS_CYCLES_TO_USEC(diff) / 1000.0f); + + return 0; +} diff --git a/soh/src/code/z_kaleido_manager.c b/soh/src/code/z_kaleido_manager.c new file mode 100644 index 000000000..783fd6d6e --- /dev/null +++ b/soh/src/code/z_kaleido_manager.c @@ -0,0 +1,115 @@ +#include "global.h" +#include "vt.h" + +#include + +//#define KALEIDO_OVERLAY(name) \ +// { \ +// NULL, (uintptr_t)_ovl_##name##SegmentRomStart, (uintptr_t)_ovl_##name##SegmentRomEnd, _ovl_##name##SegmentStart, \ +// _ovl_##name##SegmentEnd, 0, #name, \ +// } + +#define KALEIDO_OVERLAY(name) \ +{ 0 } + +KaleidoMgrOverlay gKaleidoMgrOverlayTable[] = { + KALEIDO_OVERLAY(kaleido_scope), + KALEIDO_OVERLAY(player_actor), +}; + +void* sKaleidoAreaPtr = NULL; +KaleidoMgrOverlay* gKaleidoMgrCurOvl = NULL; +u8 gBossMarkState = 0; + +void KaleidoManager_LoadOvl(KaleidoMgrOverlay* ovl) { + //LogUtils_CheckNullPointer("KaleidoArea_allocp", sKaleidoAreaPtr, "../z_kaleido_manager.c", 99); + + ovl->loadedRamAddr = sKaleidoAreaPtr; + Overlay_Load(ovl->vromStart, ovl->vromEnd, ovl->vramStart, ovl->vramEnd, ovl->loadedRamAddr); + + //osSyncPrintf(VT_FGCOL(GREEN)); + //osSyncPrintf("OVL(k):Seg:%08x-%08x Ram:%08x-%08x Off:%08x %s\n", ovl->vramStart, ovl->vramEnd, ovl->loadedRamAddr, + //(uintptr_t)ovl->loadedRamAddr + (uintptr_t)ovl->vramEnd - (uintptr_t)ovl->vramStart, + //(uintptr_t)ovl->vramStart - (uintptr_t)ovl->loadedRamAddr, ovl->name); + //osSyncPrintf(VT_RST); + + ovl->offset = (uintptr_t)ovl->loadedRamAddr - (uintptr_t)ovl->vramStart; + gKaleidoMgrCurOvl = ovl; +} + +void KaleidoManager_ClearOvl(KaleidoMgrOverlay* ovl) { + if (ovl->loadedRamAddr != NULL) { + ovl->offset = 0; + memset(ovl->loadedRamAddr, 0, (uintptr_t)ovl->vramEnd - (uintptr_t)ovl->vramStart); + ovl->loadedRamAddr = NULL; + gKaleidoMgrCurOvl = NULL; + } +} + +void KaleidoManager_Init(GlobalContext* globalCtx) { + ptrdiff_t largestSize = 0; + ptrdiff_t size; + u32 i; + + for (i = 0; i < ARRAY_COUNT(gKaleidoMgrOverlayTable); i++) { + size = (uintptr_t)gKaleidoMgrOverlayTable[i].vramEnd - (uintptr_t)gKaleidoMgrOverlayTable[i].vramStart; + if (size > largestSize) { + largestSize = size; + } + } + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("KaleidoArea の最大サイズは %d バイトを確保します\n", largestSize); + osSyncPrintf(VT_RST); + + sKaleidoAreaPtr = GameState_Alloc(&globalCtx->state, largestSize, "../z_kaleido_manager.c", 150); + LogUtils_CheckNullPointer("KaleidoArea_allocp", sKaleidoAreaPtr, "../z_kaleido_manager.c", 151); + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("KaleidoArea %08x - %08x\n", sKaleidoAreaPtr, (uintptr_t)sKaleidoAreaPtr + largestSize); + osSyncPrintf(VT_RST); + + gKaleidoMgrCurOvl = 0; +} + +void KaleidoManager_Destroy() { + if (gKaleidoMgrCurOvl != NULL) { + KaleidoManager_ClearOvl(gKaleidoMgrCurOvl); + gKaleidoMgrCurOvl = NULL; + } + + sKaleidoAreaPtr = NULL; +} + +// NOTE: this function looks messed up and probably doesn't work how it was intended to +void* KaleidoManager_GetRamAddr(void* vram) { + return vram; + +#if 0 + KaleidoMgrOverlay* iter = gKaleidoMgrCurOvl; + KaleidoMgrOverlay* ovl = iter; + u32 i; + + if (ovl == NULL) { + iter = &gKaleidoMgrOverlayTable[0]; + for (i = 0; i < ARRAY_COUNT(gKaleidoMgrOverlayTable); i++) { + if (((uintptr_t)vram >= (uintptr_t)iter->vramStart) && ((uintptr_t)iter->vramEnd >= (uintptr_t)vram)) { + KaleidoManager_LoadOvl(iter); + ovl = iter; + goto KaleidoManager_GetRamAddr_end; + } + //! @bug Probably missing iter++ here + } + + osSyncPrintf("異常\n"); // "Abnormal" + return NULL; + } + +KaleidoManager_GetRamAddr_end: + if ((ovl == NULL) || ((uintptr_t)vram < (uintptr_t)ovl->vramStart) || ((uintptr_t)vram >= (uintptr_t)ovl->vramEnd)) { + return NULL; + } + + return (void*)((uintptr_t)vram + ovl->offset); +#endif +} diff --git a/soh/src/code/z_kaleido_scope_call.c b/soh/src/code/z_kaleido_scope_call.c new file mode 100644 index 000000000..c023066be --- /dev/null +++ b/soh/src/code/z_kaleido_scope_call.c @@ -0,0 +1,132 @@ +#include "global.h" +#include "vt.h" + +void (*sKaleidoScopeUpdateFunc)(GlobalContext* globalCtx); +void (*sKaleidoScopeDrawFunc)(GlobalContext* globalCtx); +f32 gBossMarkScale; +u32 D_8016139C; +PauseMapMarksData* gLoadedPauseMarkDataTable; + +extern void KaleidoScope_Update(GlobalContext* globalCtx); +extern void KaleidoScope_Draw(GlobalContext* globalCtx); + +void KaleidoScopeCall_LoadPlayer() { + KaleidoMgrOverlay* playerActorOvl = &gKaleidoMgrOverlayTable[KALEIDO_OVL_PLAYER_ACTOR]; + + if (gKaleidoMgrCurOvl != playerActorOvl) { + if (gKaleidoMgrCurOvl != NULL) { + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("カレイド領域 強制排除\n"); // "Kaleido area forced exclusion" + osSyncPrintf(VT_RST); + + KaleidoManager_ClearOvl(gKaleidoMgrCurOvl); + } + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("プレイヤーアクター搬入\n"); // "Player actor import" + osSyncPrintf(VT_RST); + + KaleidoManager_LoadOvl(playerActorOvl); + } +} + +void KaleidoScopeCall_Init(GlobalContext* globalCtx) { + // "Kaleidoscope replacement construction" + osSyncPrintf("カレイド・スコープ入れ替え コンストラクト \n"); + + sKaleidoScopeUpdateFunc = KaleidoManager_GetRamAddr(KaleidoScope_Update); + sKaleidoScopeDrawFunc = KaleidoManager_GetRamAddr(KaleidoScope_Draw); + + LOG_ADDRESS("kaleido_scope_move", KaleidoScope_Update, "../z_kaleido_scope_call.c", 98); + LOG_ADDRESS("kaleido_scope_move_func", sKaleidoScopeUpdateFunc, "../z_kaleido_scope_call.c", 99); + LOG_ADDRESS("kaleido_scope_draw", KaleidoScope_Draw, "../z_kaleido_scope_call.c", 100); + LOG_ADDRESS("kaleido_scope_draw_func", sKaleidoScopeDrawFunc, "../z_kaleido_scope_call.c", 101); + + KaleidoSetup_Init(globalCtx); +} + +void KaleidoScopeCall_Destroy(GlobalContext* globalCtx) { + // "Kaleidoscope replacement destruction" + osSyncPrintf("カレイド・スコープ入れ替え デストラクト \n"); + + KaleidoSetup_Destroy(globalCtx); +} + +void KaleidoScopeCall_Update(GlobalContext* globalCtx) { + KaleidoMgrOverlay* kaleidoScopeOvl = &gKaleidoMgrOverlayTable[KALEIDO_OVL_KALEIDO_SCOPE]; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + + if ((pauseCtx->state != 0) || (pauseCtx->debugState != 0)) { + if (pauseCtx->state == 1) { + if (ShrinkWindow_GetCurrentVal() == 0) { + HREG(80) = 7; + HREG(82) = 3; + R_PAUSE_MENU_MODE = 3; + pauseCtx->unk_1E4 = 0; + pauseCtx->unk_1EC = 0; + pauseCtx->state = (pauseCtx->state & 0xFFFF) + 1; + } + } else if (pauseCtx->state == 8) { + HREG(80) = 7; + HREG(82) = 3; + R_PAUSE_MENU_MODE = 1; + pauseCtx->unk_1E4 = 0; + pauseCtx->unk_1EC = 0; + pauseCtx->state = (pauseCtx->state & 0xFFFF) + 1; + } else if ((pauseCtx->state == 2) || (pauseCtx->state == 9)) { + osSyncPrintf("PR_KAREIDOSCOPE_MODE=%d\n", R_PAUSE_MENU_MODE); + + if (R_PAUSE_MENU_MODE >= 3) { + pauseCtx->state++; + } + } else if (pauseCtx->state != 0) { + if (gKaleidoMgrCurOvl != kaleidoScopeOvl) + { + if (gKaleidoMgrCurOvl != NULL) { + osSyncPrintf(VT_FGCOL(GREEN)); + // "Kaleido area Player Forced Elimination" + osSyncPrintf("カレイド領域 プレイヤー 強制排除\n"); + osSyncPrintf(VT_RST); + + KaleidoManager_ClearOvl(gKaleidoMgrCurOvl); + } + + osSyncPrintf(VT_FGCOL(GREEN)); + // "Kaleido area Kaleidoscope loading" + osSyncPrintf("カレイド領域 カレイドスコープ搬入\n"); + osSyncPrintf(VT_RST); + + KaleidoManager_LoadOvl(kaleidoScopeOvl); + } + + if (gKaleidoMgrCurOvl == kaleidoScopeOvl) + { + sKaleidoScopeUpdateFunc(globalCtx); + + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0)) { + osSyncPrintf(VT_FGCOL(GREEN)); + // "Kaleido area Kaleidoscope Emission" + osSyncPrintf("カレイド領域 カレイドスコープ排出\n"); + osSyncPrintf(VT_RST); + + KaleidoManager_ClearOvl(kaleidoScopeOvl); + KaleidoScopeCall_LoadPlayer(); + } + } + } + } +} + +void KaleidoScopeCall_Draw(GlobalContext* globalCtx) { + KaleidoMgrOverlay* kaleidoScopeOvl = &gKaleidoMgrOverlayTable[KALEIDO_OVL_KALEIDO_SCOPE]; + + if (R_PAUSE_MENU_MODE >= 3) { + if (((globalCtx->pauseCtx.state >= 4) && (globalCtx->pauseCtx.state <= 7)) || + ((globalCtx->pauseCtx.state >= 11) && (globalCtx->pauseCtx.state <= 18))) { + if (gKaleidoMgrCurOvl == kaleidoScopeOvl) + { + sKaleidoScopeDrawFunc(globalCtx); + } + } + } +} diff --git a/soh/src/code/z_kaleido_setup.c b/soh/src/code/z_kaleido_setup.c new file mode 100644 index 000000000..f578f98b4 --- /dev/null +++ b/soh/src/code/z_kaleido_setup.c @@ -0,0 +1,127 @@ +#include "global.h" + +s16 sKaleidoSetupKscpPos0[] = { PAUSE_QUEST, PAUSE_EQUIP, PAUSE_ITEM, PAUSE_MAP }; +f32 sKaleidoSetupEyeX0[] = { 0.0f, 64.0f, 0.0f, -64.0f }; +f32 sKaleidoSetupEyeZ0[] = { -64.0f, 0.0f, 64.0f, 0.0f }; + +s16 sKaleidoSetupKscpPos1[] = { PAUSE_MAP, PAUSE_QUEST, PAUSE_EQUIP, PAUSE_ITEM }; +f32 sKaleidoSetupEyeX1[] = { -64.0f, 0.0f, 64.0f, 0.0f }; +f32 sKaleidoSetupEyeZ1[] = { 0.0f, -64.0f, 0.0f, 64.0f }; + +void KaleidoSetup_Update(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Input* input = &globalCtx->state.input[0]; + + if (pauseCtx->state == 0 && pauseCtx->debugState == 0 && globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE && + globalCtx->sceneLoadFlag == 0 && globalCtx->transitionMode == 0 && gSaveContext.cutsceneIndex < 0xFFF0 && + gSaveContext.nextCutsceneIndex < 0xFFF0 && !Gameplay_InCsMode(globalCtx) && + globalCtx->shootingGalleryStatus <= 1 && gSaveContext.unk_13F0 != 8 && gSaveContext.unk_13F0 != 9 && + (globalCtx->sceneNum != SCENE_BOWLING || !Flags_GetSwitch(globalCtx, 0x38))) { + + if (CHECK_BTN_ALL(input->cur.button, BTN_L) && CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + if (BREG(0)) { + pauseCtx->debugState = 3; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_START)) { + gSaveContext.unk_13EE = gSaveContext.unk_13EA; + + if (CHECK_BTN_ALL(input->cur.button, BTN_L)) + CVar_SetS32("gPauseTriforce", 1); + else + CVar_SetS32("gPauseTriforce", 0); + + + WREG(16) = -175; + WREG(17) = 155; + + pauseCtx->unk_1EA = 0; + pauseCtx->unk_1E4 = 1; + + if (ZREG(48) == 0) { + pauseCtx->eye.x = sKaleidoSetupEyeX0[pauseCtx->pageIndex]; + pauseCtx->eye.z = sKaleidoSetupEyeZ0[pauseCtx->pageIndex]; + pauseCtx->pageIndex = sKaleidoSetupKscpPos0[pauseCtx->pageIndex]; + } else { + pauseCtx->eye.x = sKaleidoSetupEyeX1[pauseCtx->pageIndex]; + pauseCtx->eye.z = sKaleidoSetupEyeZ1[pauseCtx->pageIndex]; + pauseCtx->pageIndex = sKaleidoSetupKscpPos1[pauseCtx->pageIndex]; + } + + pauseCtx->mode = (u16)(pauseCtx->pageIndex * 2) + 1; + pauseCtx->state = 1; + + osSyncPrintf("Mode=%d eye.x=%f, eye.z=%f kscp_pos=%d\n", pauseCtx->mode, pauseCtx->eye.x, + pauseCtx->eye.z, pauseCtx->pageIndex); + } + + if (pauseCtx->state == 1) { + WREG(2) = -6240; + R_UPDATE_RATE = 2; + + if (ShrinkWindow_GetVal()) { + ShrinkWindow_SetVal(0); + } + + func_800F64E0(1); + } + } +} + +void KaleidoSetup_Init(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + u64 temp = 0; // Necessary to match + + pauseCtx->state = 0; + pauseCtx->debugState = 0; + pauseCtx->alpha = 0; + pauseCtx->unk_1EA = 0; + pauseCtx->unk_1E4 = 0; + pauseCtx->mode = 0; + pauseCtx->pageIndex = PAUSE_ITEM; + + pauseCtx->unk_1F4 = 160.0f; + pauseCtx->unk_1F8 = 160.0f; + pauseCtx->unk_1FC = 160.0f; + pauseCtx->unk_200 = 160.0f; + pauseCtx->eye.z = 64.0f; + pauseCtx->unk_1F0 = 936.0f; + pauseCtx->eye.x = pauseCtx->eye.y = 0.0f; + pauseCtx->unk_204 = -314.0f; + + pauseCtx->cursorPoint[PAUSE_ITEM] = 0; + pauseCtx->cursorPoint[PAUSE_MAP] = VREG(30) + 3; + pauseCtx->cursorPoint[PAUSE_QUEST] = 0; + pauseCtx->cursorPoint[PAUSE_EQUIP] = 1; + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 10; + + pauseCtx->cursorX[PAUSE_ITEM] = 0; + pauseCtx->cursorY[PAUSE_ITEM] = 0; + pauseCtx->cursorX[PAUSE_MAP] = 0; + pauseCtx->cursorY[PAUSE_MAP] = 0; + pauseCtx->cursorX[PAUSE_QUEST] = temp; + pauseCtx->cursorY[PAUSE_QUEST] = temp; + pauseCtx->cursorX[PAUSE_EQUIP] = 1; + pauseCtx->cursorY[PAUSE_EQUIP] = 0; + + pauseCtx->cursorItem[PAUSE_ITEM] = PAUSE_ITEM_NONE; + pauseCtx->cursorItem[PAUSE_MAP] = VREG(30) + 3; + pauseCtx->cursorItem[PAUSE_QUEST] = PAUSE_ITEM_NONE; + pauseCtx->cursorItem[PAUSE_EQUIP] = ITEM_SWORD_KOKIRI; + + pauseCtx->cursorSlot[PAUSE_ITEM] = 0; + pauseCtx->cursorSlot[PAUSE_MAP] = VREG(30) + 3; + pauseCtx->cursorSlot[PAUSE_QUEST] = 0; + pauseCtx->cursorSlot[PAUSE_EQUIP] = pauseCtx->cursorPoint[PAUSE_EQUIP]; + + pauseCtx->infoPanelOffsetY = -40; + pauseCtx->nameDisplayTimer = 0; + pauseCtx->nameColorSet = 0; + pauseCtx->cursorColorSet = 4; + pauseCtx->ocarinaSongIdx = -1; + pauseCtx->cursorSpecialPos = 0; + + View_Init(&pauseCtx->view, globalCtx->state.gfxCtx); +} + +void KaleidoSetup_Destroy(GlobalContext* globalCtx) { +} diff --git a/soh/src/code/z_kanfont.c b/soh/src/code/z_kanfont.c new file mode 100644 index 000000000..c7f3234bb --- /dev/null +++ b/soh/src/code/z_kanfont.c @@ -0,0 +1,226 @@ +#include "global.h" + +#include + +#include "message_data_static.h" +#include "textures/nes_font_static/nes_font_static.h" +#include "textures/message_static/message_static.h" + +static const char* fntTbl[] = +{ + gMsgChar20SpaceTex, + gMsgChar21ExclamationMarkTex, + gMsgChar22QuotationMarkTex, + gMsgChar23NumberSignTex, + gMsgChar24DollarSignTex, + gMsgChar25PercentSignTex, + gMsgChar26AmpersandTex, + gMsgChar27ApostropheTex, + gMsgChar28LeftParenthesesTex, + gMsgChar29RightParenthesesTex, + gMsgChar2AAsteriskTex, + gMsgChar2BPlusSignTex, + gMsgChar2CCommaTex, + gMsgChar2DHyphenMinusTex, + gMsgChar2EFullStopTex, + gMsgChar2FSolidusTex, + gMsgChar30Digit0Tex, + gMsgChar31Digit1Tex, + gMsgChar32Digit2Tex, + gMsgChar33Digit3Tex, + gMsgChar34Digit4Tex, + gMsgChar35Digit5Tex, + gMsgChar36Digit6Tex, + gMsgChar37Digit7Tex, + gMsgChar38Digit8Tex, + gMsgChar39Digit9Tex, + gMsgChar3AColonTex, + gMsgChar3BSemicolonTex, + gMsgChar3CLessThanSignTex, + gMsgChar3DEqualsSignTex, + gMsgChar3EGreaterThanSignTex, + gMsgChar3FQuestionMarkTex, + gMsgChar40CommercialAtTex, + gMsgChar41LatinCapitalLetterATex, + gMsgChar42LatinCapitalLetterBTex, + gMsgChar43LatinCapitalLetterCTex, + gMsgChar44LatinCapitalLetterDTex, + gMsgChar45LatinCapitalLetterETex, + gMsgChar46LatinCapitalLetterFTex, + gMsgChar47LatinCapitalLetterGTex, + gMsgChar48LatinCapitalLetterHTex, + gMsgChar49LatinCapitalLetterITex, + gMsgChar4ALatinCapitalLetterJTex, + gMsgChar4BLatinCapitalLetterKTex, + gMsgChar4CLatinCapitalLetterLTex, + gMsgChar4DLatinCapitalLetterMTex, + gMsgChar4ELatinCapitalLetterNTex, + gMsgChar4FLatinCapitalLetterOTex, + gMsgChar50LatinCapitalLetterPTex, + gMsgChar51LatinCapitalLetterQTex, + gMsgChar52LatinCapitalLetterRTex, + gMsgChar53LatinCapitalLetterSTex, + gMsgChar54LatinCapitalLetterTTex, + gMsgChar55LatinCapitalLetterUTex, + gMsgChar56LatinCapitalLetterVTex, + gMsgChar57LatinCapitalLetterWTex, + gMsgChar58LatinCapitalLetterXTex, + gMsgChar59LatinCapitalLetterYTex, + gMsgChar5ALatinCapitalLetterZTex, + gMsgChar5BLeftSquareBracketTex, + gMsgChar5CYenSignTex, + gMsgChar5DRightSquareBracketTex, + gMsgChar5ECircumflexAccentTex, + gMsgChar5FLowLineTex, + gMsgChar60GraveAccentTex, + gMsgChar61LatinSmallLetterATex, + gMsgChar62LatinSmallLetterBTex, + gMsgChar63LatinSmallLetterCTex, + gMsgChar64LatinSmallLetterDTex, + gMsgChar65LatinSmallLetterETex, + gMsgChar66LatinSmallLetterFTex, + gMsgChar67LatinSmallLetterGTex, + gMsgChar68LatinSmallLetterHTex, + gMsgChar69LatinSmallLetterITex, + gMsgChar6ALatinSmallLetterJTex, + gMsgChar6BLatinSmallLetterKTex, + gMsgChar6CLatinSmallLetterLTex, + gMsgChar6DLatinSmallLetterMTex, + gMsgChar6ELatinSmallLetterNTex, + gMsgChar6FLatinSmallLetterOTex, + gMsgChar70LatinSmallLetterPTex, + gMsgChar71LatinSmallLetterQTex, + gMsgChar72LatinSmallLetterRTex, + gMsgChar73LatinSmallLetterSTex, + gMsgChar74LatinSmallLetterTTex, + gMsgChar75LatinSmallLetterUTex, + gMsgChar76LatinSmallLetterVTex, + gMsgChar77LatinSmallLetterWTex, + gMsgChar78LatinSmallLetterXTex, + gMsgChar79LatinSmallLetterYTex, + gMsgChar7ALatinSmallLetterZTex, + gMsgChar7BLeftCurlyBracketTex, + gMsgChar7CVerticalLineTex, + gMsgChar7DRightCurlyBracketTex, + gMsgChar7ETildeTex, + gMsgChar7FBlankTex, + gMsgChar80LatinCapitalLetterAWithGraveTex, + gMsgChar81LatinCapitalLetterIWithCircumflexTex, + gMsgChar82LatinCapitalLetterAWithCircumflexTex, + gMsgChar83LatinCapitalLetterAWithDiaeresisTex, + gMsgChar84LatinCapitalLetterCWithCedillaTex, + gMsgChar85LatinCapitalLetterEWithGraveTex, + gMsgChar86LatinCapitalLetterEWithAcuteTex, + gMsgChar87LatinCapitalLetterEWithCircumflexTex, + gMsgChar88LatinCapitalLetterEWithDiaeresisTex, + gMsgChar89LatinCapitalLetterIWithDiaeresisTex, + gMsgChar8ALatinCapitalLetterOWithCircumflexTex, + gMsgChar8BLatinCapitalLetterOWithDiaeresisTex, + gMsgChar8CLatinCapitalLetterUWithGraveTex, + gMsgChar8DLatinCapitalLetterUWithCircumflexTex, + gMsgChar8ELatinCapitalLetterUWithDiaeresisTex, + gMsgChar8FLatinSmallLetterSharpSTex, + gMsgChar90LatinSmallLetterAWithGraveTex, + gMsgChar91LatinSmallLetterAWithAcuteTex, + gMsgChar92LatinSmallLetterAWithCircumflexTex, + gMsgChar93LatinSmallLetterAWithDiaeresisTex, + gMsgChar94LatinSmallLetterCWithCedillaTex, + gMsgChar95LatinSmallLetterEWithGraveTex, + gMsgChar96LatinSmallLetterEWithAcuteTex, + gMsgChar97LatinSmallLetterEWithCircumflexTex, + gMsgChar98LatinSmallLetterEWithDiaeresisTex, + gMsgChar99LatinSmallLetterIWithDiaeresisTex, + gMsgChar9ALatinSmallLetterOWithCircumflexTex, + gMsgChar9BLatinSmallLetterOWithDiaeresisTex, + gMsgChar9CLatinSmallLetterUWithGraveTex, + gMsgChar9DLatinSmallLetterUWithCircumflexTex, + gMsgChar9ELatinSmallLetterUWithDiaeresisTex, + gMsgChar9FButtonATex, + gMsgCharA0ButtonBTex, + gMsgCharA1ButtonCTex, + gMsgCharA2ButtonLTex, + gMsgCharA3ButtonRTex, + gMsgCharA4ButtonZTex, + gMsgCharA5ButtonCUpTex, + gMsgCharA6ButtonCDownTex, + gMsgCharA7ButtonCLeftTex, + gMsgCharA8ButtonCRightTex, + gMsgCharA9ZTargetSignTex, + gMsgCharAAControlStickTex, + gMsgCharABControlPadTex, +}; + +const char* msgStaticTbl[] = +{ + gDefaultMessageBackgroundTex, + gSignMessageBackgroundTex, + gNoteStaffMessageBackgroundTex, + gFadingMessageBackgroundTex, + gMessageContinueTriangleTex, + gMessageEndSquareTex, + gMessageArrowTex +}; + +void func_8006EE50(Font* font, u16 arg1, u16 arg2) { +} + +/** + * Loads a texture from nes_font_static for the requested `character` into the character texture buffer + * at `codePointIndex`. The value of `character` is the ASCII codepoint subtract ' '/0x20. + */ +void Font_LoadChar(Font* font, u8 character, u16 codePointIndex) { + //DmaMgr_SendRequest1(&font->charTexBuf[codePointIndex], + //&_nes_font_staticSegmentRomStart[character * FONT_CHAR_TEX_SIZE], FONT_CHAR_TEX_SIZE, + //"../z_kanfont.c", 93); + + if (character < 0x8B) + memcpy(&font->charTexBuf[codePointIndex], ResourceMgr_LoadTexByName(fntTbl[character]), FONT_CHAR_TEX_SIZE); +} + +/** + * Loads a message box icon from message_static, such as the ending triangle/square or choice arrow into the + * icon buffer. + * The different icons are given in the MessageBoxIcon enum. + */ +void Font_LoadMessageBoxIcon(Font* font, u16 icon) { + memcpy(font->iconBuf, msgStaticTbl[4 + icon], FONT_CHAR_TEX_SIZE); +} + +/** + * Loads a full set of character textures based on their ordering in the message with text id 0xFFFC into + * the font buffer. + */ +void Font_LoadOrderedFont(Font* font) { + size_t len; + size_t jj; + s32 fontStatic; + u32 fontBuf; + s32 codePointIndex; + s32 fontBufIndex; + s32 offset; + + len = strlen(_message_0xFFFC_nes); + memcpy(font->msgBuf, _message_0xFFFC_nes, len); + + osSyncPrintf("msg_data=%x, msg_data0=%x jj=%x\n", font->msgOffset, font->msgLength, jj = len); + + len = jj; + for (fontBufIndex = 0, codePointIndex = 0; font->msgBuf[codePointIndex] != MESSAGE_END; codePointIndex++) { + if (codePointIndex > len) { + osSyncPrintf("ERROR!! エラー!!! error───!!!!\n"); + return; + } + + if (font->msgBuf[codePointIndex] != MESSAGE_NEWLINE) { + fontBuf = font->fontBuf + fontBufIndex * 8; + fontStatic = _nes_font_staticSegmentRomStart; + + osSyncPrintf("nes_mes_buf[%d]=%d\n", codePointIndex, font->msgBuf[codePointIndex]); + + offset = (font->msgBuf[codePointIndex] - '\x20') * FONT_CHAR_TEX_SIZE; + memcpy(fontBuf, ResourceMgr_LoadTexByName(fntTbl[offset / FONT_CHAR_TEX_SIZE]), FONT_CHAR_TEX_SIZE); + //DmaMgr_SendRequest1(fontBuf, fontStatic + offset, FONT_CHAR_TEX_SIZE, "../z_kanfont.c", 134); + fontBufIndex += FONT_CHAR_TEX_SIZE / 8; + } + } +} diff --git a/soh/src/code/z_kankyo.c b/soh/src/code/z_kankyo.c new file mode 100644 index 000000000..90194835e --- /dev/null +++ b/soh/src/code/z_kankyo.c @@ -0,0 +1,2509 @@ +#include "global.h" +#include "ultra64.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +typedef enum { + /* 0 */ LENS_FLARE_CIRCLE0, + /* 1 */ LENS_FLARE_CIRCLE1, + /* 2 */ LENS_FLARE_RING +} LensFlareType; + +typedef enum { + /* 0x00 */ LIGHTNING_BOLT_START, + /* 0x01 */ LIGHTNING_BOLT_WAIT, + /* 0x02 */ LIGHTNING_BOLT_DRAW, + /* 0xFF */ LIGHTNING_BOLT_INACTIVE = 0xFF +} LightningBoltState; + +typedef struct { + /* 0x00 */ u16 startTime; + /* 0x02 */ u16 endTime; + /* 0x04 */ u8 unk_04; + /* 0x05 */ u8 unk_05; +} struct_8011FB48; // size = 0x6 + +typedef struct { + /* 0x00 */ u8 state; + /* 0x04 */ Vec3f offset; + /* 0x10 */ Vec3f pos; + /* 0x1C */ s8 pitch; + /* 0x1D */ s8 roll; + /* 0x1E */ u8 textureIndex; + /* 0x1F */ u8 delayTimer; +} LightningBolt; // size = 0x20 + +typedef struct { + /* 0x00 */ s32 unk0; + /* 0x04 */ s32 unk1; +} Struct_8011FAF0; // size = 0x8 + +Struct_8011FAF0 D_8011FAF0[] = { + { 6, 0x00000000 }, { 5, 0x00020000 }, { 4, 0x00030000 }, { 3, 0x00038000 }, + { 2, 0x0003C000 }, { 1, 0x0003E000 }, { 0, 0x0003F000 }, { 0, 0x0003F800 }, +}; + +u8 gWeatherMode = 0; // "E_wether_flg" + +u8 D_8011FB34 = 0; + +u8 D_8011FB38 = 0; + +u8 gSkyboxBlendingEnabled = false; + +u16 gTimeIncrement = 0; + +u16 D_8011FB44 = 0xFFFC; + +struct_8011FB48 D_8011FB48[][7] = { + { + { 0x0000, 0x2AAC, 3, 3 }, + { 0x2AAC, 0x4000, 3, 0 }, + { 0x4000, 0x5556, 0, 1 }, + { 0x5556, 0xAAAB, 1, 1 }, + { 0xAAAB, 0xB556, 1, 2 }, + { 0xB556, 0xCAAC, 2, 3 }, + { 0xCAAC, 0xFFFF, 3, 3 }, + }, + { + { 0x0000, 0x2AAC, 7, 7 }, + { 0x2AAC, 0x4000, 7, 4 }, + { 0x4000, 0x5556, 4, 5 }, + { 0x5556, 0xAAAB, 5, 5 }, + { 0xAAAB, 0xB556, 5, 6 }, + { 0xB556, 0xCAAC, 6, 7 }, + { 0xCAAC, 0xFFFF, 7, 7 }, + }, + { + { 0x0000, 0x2AAC, 11, 11 }, + { 0x2AAC, 0x4000, 11, 8 }, + { 0x4000, 0x5556, 8, 9 }, + { 0x5556, 0xAAAB, 9, 9 }, + { 0xAAAB, 0xB556, 9, 10 }, + { 0xB556, 0xCAAC, 10, 11 }, + { 0xCAAC, 0xFFFF, 11, 11 }, + }, + { + { 0x0000, 0x2AAC, 15, 15 }, + { 0x2AAC, 0x4000, 15, 12 }, + { 0x4000, 0x5556, 12, 13 }, + { 0x5556, 0xAAAB, 13, 13 }, + { 0xAAAB, 0xB556, 13, 14 }, + { 0xB556, 0xCAAC, 14, 15 }, + { 0xCAAC, 0xFFFF, 15, 15 }, + }, + { + { 0x0000, 0x2AAC, 23, 23 }, + { 0x2AAC, 0x4000, 23, 20 }, + { 0x4000, 0x5556, 20, 21 }, + { 0x5556, 0xAAAB, 21, 21 }, + { 0xAAAB, 0xB556, 21, 22 }, + { 0xB556, 0xCAAC, 22, 23 }, + { 0xCAAC, 0xFFFF, 23, 23 }, + }, +}; + +struct_8011FC1C D_8011FC1C[][9] = { + { + { 0x0000, 0x2AAC, 0, 3, 3 }, + { 0x2AAC, 0x3556, 1, 3, 0 }, + { 0x3556, 0x4000, 0, 0, 0 }, + { 0x4000, 0x5556, 1, 0, 1 }, + { 0x5556, 0xAAAB, 0, 1, 1 }, + // { 0x5556, 0xAAAB, 0, 2, 2 }, + { 0xAAAB, 0xB556, 1, 1, 2 }, + { 0xB556, 0xC001, 0, 2, 2 }, + { 0xC001, 0xCAAC, 1, 2, 3 }, + { 0xCAAC, 0xFFFF, 0, 3, 3 }, + }, + { + { 0x0000, 0x2AAC, 0, 7, 7 }, + { 0x2AAC, 0x3556, 1, 7, 4 }, + { 0x3556, 0x4000, 0, 4, 4 }, + { 0x4000, 0x5556, 1, 4, 5 }, + { 0x5556, 0xAAAB, 0, 5, 5 }, + { 0xAAAB, 0xB556, 1, 5, 6 }, + { 0xB556, 0xC001, 0, 6, 6 }, + { 0xC001, 0xCAAC, 1, 6, 7 }, + { 0xCAAC, 0xFFFF, 0, 7, 7 }, + }, + { + { 0x0000, 0x1556, 0, 3, 3 }, + { 0x1556, 0x2AAC, 1, 3, 0 }, + { 0x2AAC, 0x5556, 0, 0, 0 }, + { 0x5556, 0x6AAB, 1, 0, 1 }, + { 0x6AAB, 0x9556, 0, 1, 1 }, + { 0x9556, 0xAAAB, 1, 1, 2 }, + { 0xAAAB, 0xD556, 0, 2, 2 }, + { 0xD556, 0xEAAB, 1, 2, 3 }, + { 0xEAAB, 0xFFFF, 0, 3, 3 }, + }, + { + { 0x0000, 0x3556, 0, 11, 11 }, + { 0x3556, 0x4000, 1, 11, 8 }, + { 0x4000, 0x4AAB, 0, 8, 8 }, + { 0x4AAB, 0x5556, 1, 8, 9 }, + { 0x5556, 0xAAAB, 0, 9, 9 }, + { 0xAAAB, 0xB556, 1, 9, 10 }, + { 0xB556, 0xC001, 0, 10, 10 }, + { 0xC001, 0xCAAC, 1, 10, 11 }, + { 0xCAAC, 0xFFFF, 0, 11, 11 }, + }, +}; + +SkyboxFile gSkyboxFiles[] = { + { + ROM_FILE(vr_fine0_static), + ROM_FILE(vr_fine0_pal_static), + }, + { + ROM_FILE(vr_fine1_static), + ROM_FILE(vr_fine1_pal_static), + }, + { + ROM_FILE(vr_fine2_static), + ROM_FILE(vr_fine2_pal_static), + }, + { + ROM_FILE(vr_fine3_static), + ROM_FILE(vr_fine3_pal_static), + }, + { + ROM_FILE(vr_cloud0_static), + ROM_FILE(vr_cloud0_pal_static), + }, + { + ROM_FILE(vr_cloud1_static), + ROM_FILE(vr_cloud1_pal_static), + }, + { + ROM_FILE(vr_cloud2_static), + ROM_FILE(vr_cloud2_pal_static), + }, + { + ROM_FILE(vr_cloud3_static), + ROM_FILE(vr_cloud3_pal_static), + }, + { + ROM_FILE(vr_holy0_static), + ROM_FILE(vr_holy0_pal_static), + }, +}; + +u8 D_8011FDCC = 0; +u8 D_8011FDD0 = 0; +f32 D_8011FDD4 = 0.0f; + +u8 gCustomLensFlareOn; +Vec3f gCustomLensFlarePos; +s16 gLensFlareUnused; +s16 gLensFlareScale; +f32 gLensFlareColorIntensity; +s16 gLensFlareScreenFillAlpha; +LightningBolt sLightningBolts[3]; +LightningStrike gLightningStrike; +s16 sLightningFlashAlpha; +s16 D_8015FD7E; +s16 D_8015FD80; +LightNode* sNGameOverLightNode; +LightInfo sNGameOverLightInfo; +LightNode* sSGameOverLightNode; +LightInfo sSGameOverLightInfo; +u8 sGameOverLightsIntensity; +u16 D_8015FDB0; + +s32 func_8006F0A0(s32 a0) { + s32 ret = ((a0 >> 4 & 0x7FF) << D_8011FAF0[a0 >> 15 & 7].unk0) + D_8011FAF0[a0 >> 15 & 7].unk1; + + return ret; +} + +u16 Environment_GetPixelDepth(s32 x, s32 y) { + return OTRGetPixelDepth(x, y); +} + +void Environment_GraphCallback(GraphicsContext* gfxCtx, void* param) { + GlobalContext* globalCtx = (GlobalContext*)param; + + D_8011FB44 = Environment_GetPixelDepth(D_8015FD7E, D_8015FD80); + Lights_GlowCheck(globalCtx); +} + +void Environment_Init(GlobalContext* globalCtx2, EnvironmentContext* envCtx, s32 unused) { + u8 i; + GlobalContext* globalCtx = globalCtx2; + + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + + if (((void)0, gSaveContext.dayTime) > 0xC000 || ((void)0, gSaveContext.dayTime) < 0x4555) { + ((void)0, gSaveContext.nightFlag = 1); + } else { + ((void)0, gSaveContext.nightFlag = 0); + } + + globalCtx->state.gfxCtx->callback = Environment_GraphCallback; + globalCtx->state.gfxCtx->callbackParam = globalCtx; + + Lights_DirectionalSetInfo(&envCtx->dirLight1, 80, 80, 80, 80, 80, 80); + LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &envCtx->dirLight1); + + Lights_DirectionalSetInfo(&envCtx->dirLight2, 80, 80, 80, 80, 80, 80); + LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &envCtx->dirLight2); + + envCtx->skybox1Index = 99; + envCtx->skybox2Index = 99; + envCtx->unk_19 = 0; + envCtx->unk_1A = 0; + envCtx->unk_21 = 0; + envCtx->unk_22 = 0; + envCtx->skyboxDmaState = SKYBOX_DMA_INACTIVE; + envCtx->unk_1F = 0; + envCtx->unk_20 = 0; + envCtx->unk_84 = 0.0f; + envCtx->unk_88 = 0.0f; + envCtx->unk_BD = 0; + envCtx->unk_BE = 0; + envCtx->unk_D8 = 1.0f; + envCtx->unk_DC = 0; + envCtx->gloomySkyMode = 0; + envCtx->unk_DE = 0; + envCtx->lightningMode = LIGHTNING_MODE_OFF; + envCtx->unk_E0 = 0; + envCtx->fillScreen = false; + envCtx->screenFillColor[0] = 0; + envCtx->screenFillColor[1] = 0; + envCtx->screenFillColor[2] = 0; + envCtx->screenFillColor[3] = 0; + envCtx->customSkyboxFilter = false; + envCtx->skyboxFilterColor[0] = 0; + envCtx->skyboxFilterColor[1] = 0; + envCtx->skyboxFilterColor[2] = 0; + envCtx->skyboxFilterColor[3] = 0; + envCtx->sandstormState = 0; + envCtx->sandstormPrimA = 0; + envCtx->sandstormEnvA = 0; + + gLightningStrike.state = LIGHTNING_STRIKE_WAIT; + gLightningStrike.flashRed = 0; + gLightningStrike.flashGreen = 0; + gLightningStrike.flashBlue = 0; + + sLightningFlashAlpha = 0; + + gSaveContext.unk_1410 = 0; + + envCtx->adjAmbientColor[0] = envCtx->adjAmbientColor[1] = envCtx->adjAmbientColor[2] = envCtx->adjLight1Color[0] = + envCtx->adjLight1Color[1] = envCtx->adjLight1Color[2] = envCtx->adjFogColor[0] = envCtx->adjFogColor[1] = + envCtx->adjFogColor[2] = envCtx->adjFogNear = envCtx->adjFogFar = 0; + + envCtx->sunPos.x = -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + envCtx->sunPos.y = +(Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + envCtx->sunPos.z = +(Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f) * 25.0f; + + envCtx->windDirection.x = 80; + envCtx->windDirection.y = 80; + envCtx->windDirection.z = 80; + + envCtx->blendIndoorLights = false; + envCtx->unk_BF = 0xFF; + envCtx->unk_D6 = 0xFFFF; + R_ENV_TIME_INCREMENT = gTimeIncrement = envCtx->timeIncrement = 0; + R_ENV_DISABLE_DBG = true; + + if (CREG(3) != 0) { + gSaveContext.chamberCutsceneNum = CREG(3) - 1; + } + + globalCtx->envCtx.unk_EE[0] = 0; + globalCtx->envCtx.unk_EE[1] = 0; + globalCtx->envCtx.unk_EE[2] = 0; + globalCtx->envCtx.unk_EE[3] = 0; + + globalCtx->envCtx.unk_F2[0] = 0; + + if (gSaveContext.unk_13C3 != 0) { + if (((void)0, gSaveContext.sceneSetupIndex) < 4) { + switch (gWeatherMode) { + case 1: + envCtx->unk_17 = 1; + envCtx->unk_18 = 1; + envCtx->unk_1F = 3; + envCtx->unk_20 = 3; + globalCtx->envCtx.unk_EE[3] = 0; + globalCtx->envCtx.unk_EE[2] = 0; + break; + case 2: + case 3: + case 4: + envCtx->unk_17 = 1; + envCtx->unk_18 = 1; + envCtx->unk_1F = 2; + envCtx->unk_20 = 2; + globalCtx->envCtx.unk_EE[3] = 0; + globalCtx->envCtx.unk_EE[2] = 0; + break; + case 5: + envCtx->unk_17 = 1; + envCtx->unk_18 = 1; + envCtx->unk_1F = 4; + envCtx->unk_20 = 4; + globalCtx->envCtx.unk_EE[3] = 0; + globalCtx->envCtx.unk_EE[2] = 0; + break; + default: + break; + } + + if (globalCtx->skyboxId == SKYBOX_NORMAL_SKY) { + if (gWeatherMode == 3) { + globalCtx->envCtx.unk_EE[2] = globalCtx->envCtx.unk_EE[3] = 0x40; + } else if (gWeatherMode == 4) { + globalCtx->envCtx.unk_EE[0] = 0x14; + globalCtx->envCtx.unk_EE[1] = 0x14; + } else if (gWeatherMode == 5) { + globalCtx->envCtx.unk_EE[0] = 0x1E; + globalCtx->envCtx.unk_EE[1] = 0x1E; + } + } + } + } else { + gWeatherMode = 0; + } + + D_8011FB38 = 0; + D_8011FB34 = 0; + gSkyboxBlendingEnabled = false; + gSaveContext.unk_13C3 = 0; + R_ENV_LIGHT1_DIR(0) = 80; + R_ENV_LIGHT1_DIR(1) = 80; + R_ENV_LIGHT1_DIR(2) = 80; + R_ENV_LIGHT2_DIR(0) = -80; + R_ENV_LIGHT2_DIR(1) = -80; + R_ENV_LIGHT2_DIR(2) = -80; + cREG(9) = 10; + cREG(10) = 0; + cREG(11) = 0; + cREG(12) = 0; + cREG(13) = 0; + cREG(14) = 0; + D_8015FCC8 = 1; + + for (i = 0; i < ARRAY_COUNT(sLightningBolts); i++) { + sLightningBolts[i].state = LIGHTNING_BOLT_INACTIVE; + } + + globalCtx->roomCtx.unk_74[0] = 0; + globalCtx->roomCtx.unk_74[1] = 0; + + for (i = 0; i < ARRAY_COUNT(globalCtx->csCtx.npcActions); i++) { + globalCtx->csCtx.npcActions[i] = 0; + } + + if (Object_GetIndex(&globalCtx->objectCtx, OBJECT_GAMEPLAY_FIELD_KEEP) < 0 && !globalCtx->envCtx.sunMoonDisabled) { + globalCtx->envCtx.sunMoonDisabled = true; + // "Sun setting other than field keep! So forced release!" + osSyncPrintf(VT_COL(YELLOW, BLACK) "\n\nフィールド常駐以外、太陽設定!よって強制解除!\n" VT_RST); + } + + gCustomLensFlareOn = false; + func_800AA15C(); +} + +u8 Environment_SmoothStepToU8(u8* pvalue, u8 target, u8 scale, u8 step, u8 minStep) { + s16 stepSize = 0; + s16 diff = target - *pvalue; + + if (target != *pvalue) { + stepSize = diff / scale; + if ((stepSize >= (s16)minStep) || ((s16)-minStep >= stepSize)) { + if ((s16)step < stepSize) { + stepSize = step; + } + if ((s16)-step > stepSize) { + stepSize = -step; + } + *pvalue += (u8)stepSize; + } else { + if (stepSize < (s16)minStep) { + stepSize = minStep; + *pvalue += (u8)stepSize; + if (target < *pvalue) { + *pvalue = target; + } + } + if ((s16)-minStep < stepSize) { + stepSize = -minStep; + *pvalue += (u8)stepSize; + if (*pvalue < target) { + *pvalue = target; + } + } + } + } + return diff; +} + +u8 Environment_SmoothStepToS8(s8* pvalue, s8 target, u8 scale, u8 step, u8 minStep) { + s16 stepSize = 0; + s16 diff = target - *pvalue; + + if (target != *pvalue) { + stepSize = diff / scale; + if ((stepSize >= (s16)minStep) || ((s16)-minStep >= stepSize)) { + if ((s16)step < stepSize) { + stepSize = step; + } + if ((s16)-step > stepSize) { + stepSize = -step; + } + *pvalue += (s8)stepSize; + } else { + if (stepSize < (s16)minStep) { + stepSize = minStep; + *pvalue += (s8)stepSize; + if (target < *pvalue) { + *pvalue = target; + } + } + if ((s16)-minStep < stepSize) { + stepSize = -minStep; + *pvalue += (s8)stepSize; + if (*pvalue < target) { + *pvalue = target; + } + } + } + } + return diff; +} + +f32 Environment_LerpWeight(u16 max, u16 min, u16 val) { + f32 diff = max - min; + f32 ret; + + if (diff != 0.0f) { + ret = 1.0f - (max - val) / diff; + + if (!(ret >= 1.0f)) { + return ret; + } + } + + return 1.0f; +} + +f32 Environment_LerpWeightAccelDecel(u16 endFrame, u16 startFrame, u16 curFrame, u16 accelDuration, u16 decelDuration) { + f32 endFrameF; + f32 startFrameF; + f32 curFrameF; + f32 accelDurationF; + f32 decelDurationF; + f32 totalFrames; + f32 temp; + f32 framesElapsed; + f32 ret; + + if (curFrame <= startFrame) { + return 0.0f; + } + + if (curFrame >= endFrame) { + return 1.0f; + } + + endFrameF = (s32)endFrame; + startFrameF = (s32)startFrame; + curFrameF = (s32)curFrame; + totalFrames = endFrameF - startFrameF; + framesElapsed = curFrameF - startFrameF; + accelDurationF = (s32)accelDuration; + decelDurationF = (s32)decelDuration; + + if ((startFrameF >= endFrameF) || (accelDurationF + decelDurationF > totalFrames)) { + // "The frame relation between end_frame and start_frame is wrong!!!" + osSyncPrintf(VT_COL(RED, WHITE) "\nend_frameとstart_frameのフレーム関係がおかしい!!!" VT_RST); + osSyncPrintf(VT_COL(RED, WHITE) "\nby get_parcent_forAccelBrake!!!!!!!!!" VT_RST); + + return 0.0f; + } + + temp = 1.0f / ((totalFrames * 2.0f) - accelDurationF - decelDurationF); + + if (accelDurationF != 0.0f) { + if (framesElapsed <= accelDurationF) { + return temp * framesElapsed * framesElapsed / accelDurationF; + } + ret = temp * accelDurationF; + } else { + ret = 0.0f; + } + + if (framesElapsed <= totalFrames - decelDurationF) { + ret += 2.0f * temp * (framesElapsed - accelDurationF); + return ret; + } + + ret += 2.0f * temp * (totalFrames - accelDurationF - decelDurationF); + + if (decelDurationF != 0.0f) { + ret += temp * decelDurationF; + if (framesElapsed < totalFrames) { + ret -= temp * (totalFrames - framesElapsed) * (totalFrames - framesElapsed) / decelDurationF; + } + } + + return ret; +} + +void func_8006FB94(EnvironmentContext* envCtx, u8 unused) { + if (envCtx->gloomySkyMode != 0) { + switch (envCtx->unk_DE) { + case 0: + if ((envCtx->gloomySkyMode == 1) && !gSkyboxBlendingEnabled) { + envCtx->unk_19 = 1; + envCtx->unk_17 = 0; + envCtx->unk_18 = 1; + envCtx->unk_1A = 100; + envCtx->unk_21 = 1; + envCtx->unk_1F = 0; + envCtx->unk_20 = 2; + D_8011FB34 = 2; + envCtx->unk_22 = envCtx->unk_24 = 100; + envCtx->unk_DE++; + } + break; + case 1: + if (!gSkyboxBlendingEnabled && (envCtx->gloomySkyMode == 2)) { + gWeatherMode = 0; + envCtx->unk_19 = 1; + envCtx->unk_17 = 1; + envCtx->unk_18 = 0; + envCtx->unk_1A = 100; + envCtx->unk_21 = 1; + envCtx->unk_1F = 2; + envCtx->unk_20 = 0; + D_8011FB34 = 0; + envCtx->unk_22 = envCtx->unk_24 = 100; + envCtx->unk_EE[0] = 0; + envCtx->gloomySkyMode = 0; + envCtx->unk_DE = 0; + } + break; + } + } +} + +extern SkyboxTableEntry sSkyboxTable[]; + +void Environment_UpdateSkybox(GlobalContext* globalCtx, u8 skyboxId, EnvironmentContext* envCtx, SkyboxContext* skyboxCtx) { + size_t size; + u8 i; + u8 newSkybox1Index = 0xFF; + u8 newSkybox2Index = 0xFF; + u8 skyboxBlend = 0; + + if (skyboxId == SKYBOX_CUTSCENE_MAP) { + envCtx->unk_17 = 3; + + for (i = 0; i < ARRAY_COUNT(D_8011FC1C[envCtx->unk_17]); i++) { + if (gSaveContext.skyboxTime >= D_8011FC1C[envCtx->unk_17][i].startTime && + (gSaveContext.skyboxTime < D_8011FC1C[envCtx->unk_17][i].endTime || + D_8011FC1C[envCtx->unk_17][i].endTime == 0xFFFF)) { + if (D_8011FC1C[envCtx->unk_17][i].blend) { + envCtx->skyboxBlend = Environment_LerpWeight(D_8011FC1C[envCtx->unk_17][i].endTime, + D_8011FC1C[envCtx->unk_17][i].startTime, + ((void)0, gSaveContext.skyboxTime)) * + 255; + } else { + envCtx->skyboxBlend = 0; + } + break; + } + } + } else if (skyboxId == SKYBOX_NORMAL_SKY && !envCtx->skyboxDisabled) { + for (i = 0; i < ARRAY_COUNT(D_8011FC1C[envCtx->unk_17]); i++) { + if (gSaveContext.skyboxTime >= D_8011FC1C[envCtx->unk_17][i].startTime && + (gSaveContext.skyboxTime < D_8011FC1C[envCtx->unk_17][i].endTime || + D_8011FC1C[envCtx->unk_17][i].endTime == 0xFFFF)) { + newSkybox1Index = D_8011FC1C[envCtx->unk_17][i].skybox1Index; + newSkybox2Index = D_8011FC1C[envCtx->unk_17][i].skybox2Index; + gSkyboxBlendingEnabled = D_8011FC1C[envCtx->unk_17][i].blend; + + if (gSkyboxBlendingEnabled) { + skyboxBlend = Environment_LerpWeight(D_8011FC1C[envCtx->unk_17][i].endTime, + D_8011FC1C[envCtx->unk_17][i].startTime, + ((void)0, gSaveContext.skyboxTime)) * + 255; + } else { + skyboxBlend = Environment_LerpWeight(D_8011FC1C[envCtx->unk_17][i].endTime, + D_8011FC1C[envCtx->unk_17][i].startTime, + ((void)0, gSaveContext.skyboxTime)) * + 255; + + skyboxBlend = (skyboxBlend < 0x80) ? 0xFF : 0; + + if ((envCtx->unk_19 != 0) && (envCtx->unk_19 < 3)) { + envCtx->unk_19++; + skyboxBlend = 0; + } + } + break; + } + } + + func_8006FB94(envCtx, skyboxBlend); + + if (envCtx->unk_19 >= 3) { + newSkybox1Index = D_8011FC1C[envCtx->unk_17][i].skybox1Index; + newSkybox2Index = D_8011FC1C[envCtx->unk_18][i].skybox2Index; + + skyboxBlend = ((f32)envCtx->unk_24 - envCtx->unk_1A--) / (f32)envCtx->unk_24 * 255; + + if (envCtx->unk_1A <= 0) { + envCtx->unk_19 = 0; + envCtx->unk_17 = envCtx->unk_18; + } + } + + if (newSkybox1Index == 0xFF) { + // "Environment VR data acquisition failed! Report to Sasaki!" + osSyncPrintf(VT_COL(RED, WHITE) "\n環境VRデータ取得失敗! ささきまでご報告を!" VT_RST); + } + + if ((envCtx->skybox1Index != newSkybox1Index) && (envCtx->skyboxDmaState == SKYBOX_DMA_INACTIVE)) { + envCtx->skyboxDmaState = SKYBOX_DMA_FILE1_START; + + SkyboxTableEntry entryA = sSkyboxTable[newSkybox1Index]; + + for (int i = 0; i < 5; i++) + LoadSkyboxTex(globalCtx, skyboxCtx, 0, i, entryA.textures[i], 128, i == 4 ? 128 : 64, 128, 64); + + envCtx->skybox1Index = newSkybox1Index; + + //size = gSkyboxFiles[newSkybox1Index].file.vromEnd - gSkyboxFiles[newSkybox1Index].file.vromStart; + + //osCreateMesgQueue(&envCtx->loadQueue, &envCtx->loadMsg, 1); + //DmaMgr_SendRequest2(&envCtx->dmaRequest, (uintptr_t)skyboxCtx->staticSegments[0], + //gSkyboxFiles[newSkybox1Index].file.vromStart, size, 0, &envCtx->loadQueue, NULL, + //"../z_kankyo.c", 1264); + } + + if ((envCtx->skybox2Index != newSkybox2Index) && (envCtx->skyboxDmaState == SKYBOX_DMA_INACTIVE)) { + envCtx->skyboxDmaState = SKYBOX_DMA_FILE2_START; + + SkyboxTableEntry entryA = sSkyboxTable[newSkybox2Index]; + + for (int i = 0; i < 5; i++) + LoadSkyboxTex(globalCtx, skyboxCtx, 1, i, entryA.textures[i], 128, i == 4 ? 128 : 64, 128, 64); + + envCtx->skybox2Index = newSkybox2Index; + + //size = gSkyboxFiles[newSkybox2Index].file.vromEnd - gSkyboxFiles[newSkybox2Index].file.vromStart; + //osCreateMesgQueue(&envCtx->loadQueue, &envCtx->loadMsg, 1); + //DmaMgr_SendRequest2(&envCtx->dmaRequest, (uintptr_t)skyboxCtx->staticSegments[1], + //gSkyboxFiles[newSkybox2Index].file.vromStart, size, 0, &envCtx->loadQueue, NULL, + //"../z_kankyo.c", 1281); + } + + if (envCtx->skyboxDmaState == SKYBOX_DMA_FILE1_DONE) { + envCtx->skyboxDmaState = SKYBOX_DMA_PAL1_START; + + if ((newSkybox1Index & 1) ^ ((newSkybox1Index & 4) >> 2)) { + + SkyboxTableEntry entryA = sSkyboxTable[newSkybox1Index]; + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, entryA.palettes[0], 16, 8); + + //size = gSkyboxFiles[newSkybox1Index].palette.vromEnd - gSkyboxFiles[newSkybox1Index].palette.vromStart; + //osCreateMesgQueue(&envCtx->loadQueue, &envCtx->loadMsg, 1); + //DmaMgr_SendRequest2(&envCtx->dmaRequest, (uintptr_t)skyboxCtx->palettes, + //gSkyboxFiles[newSkybox1Index].palette.vromStart, size, 0, &envCtx->loadQueue, NULL, + //"../z_kankyo.c", 1307); + } else { + SkyboxTableEntry entryA = sSkyboxTable[newSkybox1Index]; + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, entryA.palettes[0], 16, 8); + + //size = gSkyboxFiles[newSkybox1Index].palette.vromEnd - gSkyboxFiles[newSkybox1Index].palette.vromStart; + //osCreateMesgQueue(&envCtx->loadQueue, &envCtx->loadMsg, 1); + //DmaMgr_SendRequest2(&envCtx->dmaRequest, (uintptr_t)skyboxCtx->palettes + size, + //gSkyboxFiles[newSkybox1Index].palette.vromStart, size, 0, &envCtx->loadQueue, NULL, + //"../z_kankyo.c", 1320); + } + } + + if (envCtx->skyboxDmaState == SKYBOX_DMA_FILE2_DONE) { + envCtx->skyboxDmaState = SKYBOX_DMA_PAL2_START; + + if ((newSkybox2Index & 1) ^ ((newSkybox2Index & 4) >> 2)) + { + SkyboxTableEntry entryA = sSkyboxTable[newSkybox2Index]; + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, entryA.palettes[0], 16, 8); + + /*size = gSkyboxFiles[newSkybox2Index].palette.vromEnd - gSkyboxFiles[newSkybox2Index].palette.vromStart; + osCreateMesgQueue(&envCtx->loadQueue, &envCtx->loadMsg, 1); + DmaMgr_SendRequest2(&envCtx->dmaRequest, (uintptr_t)skyboxCtx->palettes, + gSkyboxFiles[newSkybox2Index].palette.vromStart, size, 0, &envCtx->loadQueue, NULL, + "../z_kankyo.c", 1342);*/ + } else + { + SkyboxTableEntry entryA = sSkyboxTable[newSkybox2Index]; + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, entryA.palettes[0], 16, 8); + + /*size = gSkyboxFiles[newSkybox2Index].palette.vromEnd - gSkyboxFiles[newSkybox2Index].palette.vromStart; + osCreateMesgQueue(&envCtx->loadQueue, &envCtx->loadMsg, 1); + DmaMgr_SendRequest2(&envCtx->dmaRequest, (uintptr_t)skyboxCtx->palettes + size, + gSkyboxFiles[newSkybox2Index].palette.vromStart, size, 0, &envCtx->loadQueue, NULL, + "../z_kankyo.c", 1355);*/ + } + } + + if ((envCtx->skyboxDmaState == SKYBOX_DMA_FILE1_START) || (envCtx->skyboxDmaState == SKYBOX_DMA_FILE2_START)) { + //if (osRecvMesg(&envCtx->loadQueue, 0, OS_MESG_NOBLOCK) == 0) + { + envCtx->skyboxDmaState++; + } + } else if (envCtx->skyboxDmaState >= SKYBOX_DMA_FILE1_DONE) { + //if (osRecvMesg(&envCtx->loadQueue, 0, OS_MESG_NOBLOCK) == 0) + { + envCtx->skyboxDmaState = SKYBOX_DMA_INACTIVE; + } + } + + envCtx->skyboxBlend = skyboxBlend; + } +} + +void Environment_EnableUnderwaterLights(GlobalContext* globalCtx, s32 waterLightsIndex) { + if (waterLightsIndex == 0x1F) { + waterLightsIndex = 0; + // "Underwater color is not set in the water poly data!" + osSyncPrintf(VT_COL(YELLOW, BLACK) "\n水ポリゴンデータに水中カラーが設定されておりません!" VT_RST); + } + + if (!globalCtx->envCtx.indoors) { + D_8011FB34 = globalCtx->envCtx.unk_20; + + if (globalCtx->envCtx.unk_1F != waterLightsIndex) { + globalCtx->envCtx.unk_1F = waterLightsIndex; + globalCtx->envCtx.unk_20 = waterLightsIndex; + } + } else { + globalCtx->envCtx.blendIndoorLights = false; // instantly switch to water lights + globalCtx->envCtx.unk_BF = waterLightsIndex; + } +} + +void Environment_DisableUnderwaterLights(GlobalContext* globalCtx) { + if (!globalCtx->envCtx.indoors) { + globalCtx->envCtx.unk_1F = D_8011FB34; + globalCtx->envCtx.unk_20 = D_8011FB34; + } else { + globalCtx->envCtx.blendIndoorLights = false; // instantly switch to previous lights + globalCtx->envCtx.unk_BF = 0xFF; + globalCtx->envCtx.unk_D8 = 1.0f; + } +} + +void Environment_PrintDebugInfo(GlobalContext* globalCtx, Gfx** gfx) { + GfxPrint printer; + s32 pad[2]; + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, *gfx); + + GfxPrint_SetPos(&printer, 22, 7); + GfxPrint_SetColor(&printer, 155, 155, 255, 64); + GfxPrint_Printf(&printer, "T%03d ", ((void)0, gSaveContext.totalDays)); + GfxPrint_Printf(&printer, "E%03d", ((void)0, gSaveContext.bgsDayCount)); + + GfxPrint_SetColor(&printer, 255, 255, 55, 64); + GfxPrint_SetPos(&printer, 22, 8); + GfxPrint_Printf(&printer, "%s", "ZELDATIME "); + + GfxPrint_SetColor(&printer, 255, 255, 255, 64); + GfxPrint_Printf(&printer, "%02d", (u8)(24 * 60 / (f32)0x10000 * ((void)0, gSaveContext.dayTime) / 60.0f)); + + if ((gSaveContext.dayTime & 0x1F) >= 0x10 || gTimeIncrement >= 6) { + GfxPrint_Printf(&printer, "%s", ":"); + } else { + GfxPrint_Printf(&printer, "%s", " "); + } + + GfxPrint_Printf(&printer, "%02d", (s16)(24 * 60 / (f32)0x10000 * ((void)0, gSaveContext.dayTime)) % 60); + + GfxPrint_SetColor(&printer, 255, 255, 55, 64); + GfxPrint_SetPos(&printer, 22, 9); + GfxPrint_Printf(&printer, "%s", "VRBOXTIME "); + + GfxPrint_SetColor(&printer, 255, 255, 255, 64); + GfxPrint_Printf(&printer, "%02d", (u8)(24 * 60 / (f32)0x10000 * ((void)0, gSaveContext.skyboxTime) / 60.0f)); + + if ((((void)0, gSaveContext.skyboxTime) & 0x1F) >= 0x10 || gTimeIncrement >= 6) { + GfxPrint_Printf(&printer, "%s", ":"); + } else { + GfxPrint_Printf(&printer, "%s", " "); + } + + GfxPrint_Printf(&printer, "%02d", (s16)(24 * 60 / (f32)0x10000 * ((void)0, gSaveContext.skyboxTime)) % 60); + + GfxPrint_SetColor(&printer, 55, 255, 255, 64); + GfxPrint_SetPos(&printer, 22, 6); + + if (!IS_DAY) { + GfxPrint_Printf(&printer, "%s", "YORU"); // "night" + } else { + GfxPrint_Printf(&printer, "%s", "HIRU"); // "day" + } + + *gfx = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); +} + +#define TIME_ENTRY_1F (D_8011FB48[envCtx->unk_1F][i]) +#define TIME_ENTRY_20 (D_8011FB48[envCtx->unk_20][i]) + +void func_80075B44(GlobalContext* globalCtx); +void func_800766C4(GlobalContext* globalCtx); + +void Environment_Update(GlobalContext* globalCtx, EnvironmentContext* envCtx, LightContext* lightCtx, + PauseContext* pauseCtx, MessageContext* msgCtx, GameOverContext* gameOverCtx, + GraphicsContext* gfxCtx) { + f32 sp8C; + f32 sp88 = 0.0f; + u16 i; + u16 j; + u16 time; + EnvLightSettings* lightSettingsList = globalCtx->envCtx.lightSettingsList; + s32 adjustment; + + if ((((void)0, gSaveContext.gameMode) != 0) && (((void)0, gSaveContext.gameMode) != 3)) { + func_800AA16C(globalCtx); + } + + if (pauseCtx->state == 0) { + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0)) { + if (globalCtx->skyboxId == SKYBOX_NORMAL_SKY) { + globalCtx->skyboxCtx.rot.y -= 0.001f; + } else if (globalCtx->skyboxId == SKYBOX_CUTSCENE_MAP) { + globalCtx->skyboxCtx.rot.y -= 0.005f; + } + } + + func_800766C4(globalCtx); // increments or decrements unk_EE[1] depending on some condition + func_80075B44(globalCtx); // updates bgm/sfx and other things as the day progresses + + if (((void)0, gSaveContext.nextDayTime) >= 0xFF00 && ((void)0, gSaveContext.nextDayTime) != 0xFFFF) { + gSaveContext.nextDayTime -= 0x10; + osSyncPrintf("\nnext_zelda_time=[%x]", ((void)0, gSaveContext.nextDayTime)); + + if (((void)0, gSaveContext.nextDayTime) == 0xFF0E) { + func_80078884(NA_SE_EV_CHICKEN_CRY_M); + gSaveContext.nextDayTime = 0xFFFF; + } else if (((void)0, gSaveContext.nextDayTime) == 0xFF0D) { + func_800788CC(NA_SE_EV_DOG_CRY_EVENING); + gSaveContext.nextDayTime = 0xFFFF; + } + } + + if ((pauseCtx->state == 0) && (gameOverCtx->state == GAMEOVER_INACTIVE)) { + if (((msgCtx->msgLength == 0) && (msgCtx->msgMode == 0)) || (((void)0, gSaveContext.gameMode) == 3)) { + if ((envCtx->unk_1A == 0) && !FrameAdvance_IsEnabled(globalCtx) && + (globalCtx->transitionMode == 0 || ((void)0, gSaveContext.gameMode) != 0)) { + + if (IS_DAY || gTimeIncrement >= 0x190) { + gSaveContext.dayTime += gTimeIncrement; + } else { + gSaveContext.dayTime += gTimeIncrement * 2; // time moves twice as fast at night + } + } + } + } + + //! @bug `gTimeIncrement` is unsigned, it can't be negative + if (((((void)0, gSaveContext.sceneSetupIndex) >= 5 || gTimeIncrement != 0) && + ((void)0, gSaveContext.dayTime) > gSaveContext.skyboxTime) || + (((void)0, gSaveContext.dayTime) < 0xAAB || gTimeIncrement < 0)) { + gSaveContext.skyboxTime = ((void)0, gSaveContext.dayTime); + } + + time = gSaveContext.dayTime; + + if (time > 0xC000 || time < 0x4555) { + gSaveContext.nightFlag = 1; + } else { + gSaveContext.nightFlag = 0; + } + + if (SREG(0) != 0 || CREG(2) != 0) { + Gfx* displayList; + Gfx* prevDisplayList; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 1682); + + prevDisplayList = POLY_OPA_DISP; + displayList = Graph_GfxPlusOne(POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, displayList); + Environment_PrintDebugInfo(globalCtx, &displayList); + gSPEndDisplayList(displayList++); + Graph_BranchDlist(prevDisplayList, displayList); + POLY_OPA_DISP = displayList; + if (1) {} + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 1690); + } + + if ((envCtx->unk_BF != 0xFF) && (envCtx->unk_DC != 2) && (envCtx->unk_BD != envCtx->unk_BF) && + (envCtx->unk_D8 >= 1.0f) && (envCtx->unk_BF < 0x20)) { + envCtx->unk_BE = envCtx->unk_BD; + envCtx->unk_BD = envCtx->unk_BF; + envCtx->unk_D8 = 0.0f; + } + + if (envCtx->unk_BF != 0xFE) { + if (!envCtx->indoors && (envCtx->unk_BF == 0xFF)) { + for (i = 0; i < ARRAY_COUNT(D_8011FB48[envCtx->unk_1F]); i++) { + if ((gSaveContext.skyboxTime >= TIME_ENTRY_1F.startTime) && + ((gSaveContext.skyboxTime < TIME_ENTRY_1F.endTime) || TIME_ENTRY_1F.endTime == 0xFFFF)) { + u8 blend8[2]; + s16 blend16[2]; + + sp8C = Environment_LerpWeight(TIME_ENTRY_1F.endTime, TIME_ENTRY_1F.startTime, + ((void)0, gSaveContext.skyboxTime)); + + D_8011FDCC = TIME_ENTRY_1F.unk_04 & 3; + D_8011FDD0 = TIME_ENTRY_1F.unk_05 & 3; + D_8011FDD4 = sp8C; + + if (envCtx->unk_21) { + sp88 = ((f32)envCtx->unk_24 - envCtx->unk_22) / envCtx->unk_24; + envCtx->unk_22--; + + if (envCtx->unk_22 <= 0) { + envCtx->unk_21 = 0; + envCtx->unk_1F = envCtx->unk_20; + } + } + + for (j = 0; j < 3; j++) { + // blend ambient color + blend8[0] = LERP(lightSettingsList[TIME_ENTRY_1F.unk_04].ambientColor[j], + lightSettingsList[TIME_ENTRY_1F.unk_05].ambientColor[j], sp8C); + blend8[1] = LERP(lightSettingsList[TIME_ENTRY_20.unk_04].ambientColor[j], + lightSettingsList[TIME_ENTRY_20.unk_05].ambientColor[j], sp8C); + *(envCtx->lightSettings.ambientColor + j) = LERP(blend8[0], blend8[1], sp88); + } + + // set light1 direction for the sun + envCtx->lightSettings.light1Dir[0] = + -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f); + envCtx->lightSettings.light1Dir[1] = + Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f; + envCtx->lightSettings.light1Dir[2] = + (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f); + + // set light2 direction for the moon + envCtx->lightSettings.light2Dir[0] = -envCtx->lightSettings.light1Dir[0]; + envCtx->lightSettings.light2Dir[1] = -envCtx->lightSettings.light1Dir[1]; + envCtx->lightSettings.light2Dir[2] = -envCtx->lightSettings.light1Dir[2]; + + for (j = 0; j < 3; j++) { + // blend light1Color + blend8[0] = LERP(lightSettingsList[TIME_ENTRY_1F.unk_04].light1Color[j], + lightSettingsList[TIME_ENTRY_1F.unk_05].light1Color[j], sp8C); + blend8[1] = LERP(lightSettingsList[TIME_ENTRY_20.unk_04].light1Color[j], + lightSettingsList[TIME_ENTRY_20.unk_05].light1Color[j], sp8C); + *(envCtx->lightSettings.light1Color + j) = LERP(blend8[0], blend8[1], sp88); + + // blend light2Color + blend8[0] = LERP(lightSettingsList[TIME_ENTRY_1F.unk_04].light2Color[j], + lightSettingsList[TIME_ENTRY_1F.unk_05].light2Color[j], sp8C); + blend8[1] = LERP(lightSettingsList[TIME_ENTRY_20.unk_04].light2Color[j], + lightSettingsList[TIME_ENTRY_20.unk_05].light2Color[j], sp8C); + *(envCtx->lightSettings.light2Color + j) = LERP(blend8[0], blend8[1], sp88); + } + + // blend fogColor + for (j = 0; j < 3; j++) { + blend8[0] = LERP(lightSettingsList[TIME_ENTRY_1F.unk_04].fogColor[j], + lightSettingsList[TIME_ENTRY_1F.unk_05].fogColor[j], sp8C); + blend8[1] = LERP(lightSettingsList[TIME_ENTRY_20.unk_04].fogColor[j], + lightSettingsList[TIME_ENTRY_20.unk_05].fogColor[j], sp8C); + *(envCtx->lightSettings.fogColor + j) = LERP(blend8[0], blend8[1], sp88); + } + + blend16[0] = LERP16((lightSettingsList[TIME_ENTRY_1F.unk_04].fogNear & 0x3FF), + (lightSettingsList[TIME_ENTRY_1F.unk_05].fogNear & 0x3FF), sp8C); + blend16[1] = LERP16(lightSettingsList[TIME_ENTRY_20.unk_04].fogNear & 0x3FF, + lightSettingsList[TIME_ENTRY_20.unk_05].fogNear & 0x3FF, sp8C); + + envCtx->lightSettings.fogNear = LERP16(blend16[0], blend16[1], sp88); + + blend16[0] = LERP16(lightSettingsList[TIME_ENTRY_1F.unk_04].fogFar, + lightSettingsList[TIME_ENTRY_1F.unk_05].fogFar, sp8C); + blend16[1] = LERP16(lightSettingsList[TIME_ENTRY_20.unk_04].fogFar, + lightSettingsList[TIME_ENTRY_20.unk_05].fogFar, sp8C); + + envCtx->lightSettings.fogFar = LERP16(blend16[0], blend16[1], sp88); + + if (TIME_ENTRY_20.unk_05 >= envCtx->numLightSettings) { + // "The color palette setting seems to be wrong!" + osSyncPrintf(VT_COL(RED, WHITE) "\nカラーパレットの設定がおかしいようです!" VT_RST); + + // "Palette setting = [] Last palette number = []" + osSyncPrintf(VT_COL(RED, WHITE) "\n設定パレット=[%d] 最後パレット番号=[%d]\n" VT_RST, + TIME_ENTRY_20.unk_05, envCtx->numLightSettings - 1); + } + break; + } + } + } else { + if (!envCtx->blendIndoorLights) { + for (i = 0; i < 3; i++) { + envCtx->lightSettings.ambientColor[i] = lightSettingsList[envCtx->unk_BD].ambientColor[i]; + envCtx->lightSettings.light1Dir[i] = lightSettingsList[envCtx->unk_BD].light1Dir[i]; + envCtx->lightSettings.light1Color[i] = lightSettingsList[envCtx->unk_BD].light1Color[i]; + envCtx->lightSettings.light2Dir[i] = lightSettingsList[envCtx->unk_BD].light2Dir[i]; + envCtx->lightSettings.light2Color[i] = lightSettingsList[envCtx->unk_BD].light2Color[i]; + envCtx->lightSettings.fogColor[i] = lightSettingsList[envCtx->unk_BD].fogColor[i]; + } + + envCtx->lightSettings.fogNear = lightSettingsList[envCtx->unk_BD].fogNear & 0x3FF; + envCtx->lightSettings.fogFar = lightSettingsList[envCtx->unk_BD].fogFar; + envCtx->unk_D8 = 1.0f; + } else { + u8 blendRate = (lightSettingsList[envCtx->unk_BD].fogNear >> 0xA) * 4; + + if (blendRate == 0) { + blendRate++; + } + + if (envCtx->unk_D6 != 0xFFFF) { + blendRate = envCtx->unk_D6; + } + + if (envCtx->unk_DC == 0) { + envCtx->unk_D8 += blendRate / 255.0f; + } + + if (envCtx->unk_D8 > 1.0f) { + envCtx->unk_D8 = 1.0f; + } + + for (i = 0; i < 3; i++) { + envCtx->lightSettings.ambientColor[i] = + LERP(lightSettingsList[envCtx->unk_BE].ambientColor[i], + lightSettingsList[envCtx->unk_BD].ambientColor[i], envCtx->unk_D8); + envCtx->lightSettings.light1Dir[i] = + LERP16(lightSettingsList[envCtx->unk_BE].light1Dir[i], + lightSettingsList[envCtx->unk_BD].light1Dir[i], envCtx->unk_D8); + envCtx->lightSettings.light1Color[i] = + LERP(lightSettingsList[envCtx->unk_BE].light1Color[i], + lightSettingsList[envCtx->unk_BD].light1Color[i], envCtx->unk_D8); + envCtx->lightSettings.light2Dir[i] = + LERP16(lightSettingsList[envCtx->unk_BE].light2Dir[i], + lightSettingsList[envCtx->unk_BD].light2Dir[i], envCtx->unk_D8); + envCtx->lightSettings.light2Color[i] = + LERP(lightSettingsList[envCtx->unk_BE].light2Color[i], + lightSettingsList[envCtx->unk_BD].light2Color[i], envCtx->unk_D8); + envCtx->lightSettings.fogColor[i] = + LERP(lightSettingsList[envCtx->unk_BE].fogColor[i], + lightSettingsList[envCtx->unk_BD].fogColor[i], envCtx->unk_D8); + } + envCtx->lightSettings.fogNear = + LERP16(lightSettingsList[envCtx->unk_BE].fogNear & 0x3FF, + lightSettingsList[envCtx->unk_BD].fogNear & 0x3FF, envCtx->unk_D8); + envCtx->lightSettings.fogFar = LERP16(lightSettingsList[envCtx->unk_BE].fogFar, + lightSettingsList[envCtx->unk_BD].fogFar, envCtx->unk_D8); + } + + if (envCtx->unk_BD >= envCtx->numLightSettings) { + // "The color palette seems to be wrong!" + osSyncPrintf("\n" VT_FGCOL(RED) "カラーパレットがおかしいようです!"); + + // "Palette setting = [] Last palette number = []" + osSyncPrintf("\n" VT_FGCOL(YELLOW) "設定パレット=[%d] パレット数=[%d]\n" VT_RST, envCtx->unk_BD, + envCtx->numLightSettings); + } + } + } + + envCtx->blendIndoorLights = true; + + // Apply lighting adjustments + for (i = 0; i < 3; i++) { + if ((s16)(envCtx->lightSettings.ambientColor[i] + envCtx->adjAmbientColor[i]) > 255) { + lightCtx->ambientColor[i] = 255; + } else if ((s16)(envCtx->lightSettings.ambientColor[i] + envCtx->adjAmbientColor[i]) < 0) { + lightCtx->ambientColor[i] = 0; + } else { + lightCtx->ambientColor[i] = (s16)(envCtx->lightSettings.ambientColor[i] + envCtx->adjAmbientColor[i]); + } + + if ((s16)(envCtx->lightSettings.light1Color[i] + envCtx->adjLight1Color[i]) > 255) { + envCtx->dirLight1.params.dir.color[i] = 255; + } else if ((s16)(envCtx->lightSettings.light1Color[i] + envCtx->adjLight1Color[i]) < 0) { + envCtx->dirLight1.params.dir.color[i] = 0; + } else { + envCtx->dirLight1.params.dir.color[i] = + (s16)(envCtx->lightSettings.light1Color[i] + envCtx->adjLight1Color[i]); + } + + if ((s16)(envCtx->lightSettings.light2Color[i] + envCtx->adjLight1Color[i]) > 255) { + envCtx->dirLight2.params.dir.color[i] = 255; + } else if ((s16)(envCtx->lightSettings.light2Color[i] + envCtx->adjLight1Color[i]) < 0) { + envCtx->dirLight2.params.dir.color[i] = 0; + } else { + envCtx->dirLight2.params.dir.color[i] = + (s16)(envCtx->lightSettings.light2Color[i] + envCtx->adjLight1Color[i]); + } + + if ((s16)(envCtx->lightSettings.fogColor[i] + envCtx->adjFogColor[i]) > 255) { + lightCtx->fogColor[i] = 255; + } else if ((s16)(envCtx->lightSettings.fogColor[i] + envCtx->adjFogColor[i]) < 0) { + lightCtx->fogColor[i] = 0; + } else { + lightCtx->fogColor[i] = (s16)(envCtx->lightSettings.fogColor[i] + envCtx->adjFogColor[i]); + } + } + + // Set both directional light directions + envCtx->dirLight1.params.dir.x = envCtx->lightSettings.light1Dir[0]; + envCtx->dirLight1.params.dir.y = envCtx->lightSettings.light1Dir[1]; + envCtx->dirLight1.params.dir.z = envCtx->lightSettings.light1Dir[2]; + + envCtx->dirLight2.params.dir.x = envCtx->lightSettings.light2Dir[0]; + envCtx->dirLight2.params.dir.y = envCtx->lightSettings.light2Dir[1]; + envCtx->dirLight2.params.dir.z = envCtx->lightSettings.light2Dir[2]; + + // Adjust fog near and far if necessary + adjustment = envCtx->lightSettings.fogNear + envCtx->adjFogNear; + + if (adjustment <= 996) { + lightCtx->fogNear = adjustment; + } else { + lightCtx->fogNear = 996; + } + + adjustment = envCtx->lightSettings.fogFar + envCtx->adjFogFar; + + if (adjustment <= 12800) { + lightCtx->fogFar = adjustment; + } else { + lightCtx->fogFar = 12800; + } + + // When environment debug is enabled, various environment related variables can be configured via the reg editor + if (R_ENV_DISABLE_DBG) { + R_ENV_AMBIENT_COLOR(0) = lightCtx->ambientColor[0]; + R_ENV_AMBIENT_COLOR(1) = lightCtx->ambientColor[1]; + R_ENV_AMBIENT_COLOR(2) = lightCtx->ambientColor[2]; + + R_ENV_LIGHT1_COLOR(0) = envCtx->dirLight1.params.dir.color[0]; + R_ENV_LIGHT1_COLOR(1) = envCtx->dirLight1.params.dir.color[1]; + R_ENV_LIGHT1_COLOR(2) = envCtx->dirLight1.params.dir.color[2]; + + R_ENV_LIGHT2_COLOR(0) = envCtx->dirLight2.params.dir.color[0]; + R_ENV_LIGHT2_COLOR(1) = envCtx->dirLight2.params.dir.color[1]; + R_ENV_LIGHT2_COLOR(2) = envCtx->dirLight2.params.dir.color[2]; + + R_ENV_FOG_COLOR(0) = lightCtx->fogColor[0]; + R_ENV_FOG_COLOR(1) = lightCtx->fogColor[1]; + R_ENV_FOG_COLOR(2) = lightCtx->fogColor[2]; + + R_ENV_FOG_FAR = lightCtx->fogFar; + R_ENV_FOG_NEAR = lightCtx->fogNear; + + R_ENV_LIGHT1_DIR(0) = envCtx->dirLight1.params.dir.x; + R_ENV_LIGHT1_DIR(1) = envCtx->dirLight1.params.dir.y; + R_ENV_LIGHT1_DIR(2) = envCtx->dirLight1.params.dir.z; + + R_ENV_LIGHT2_DIR(0) = envCtx->dirLight2.params.dir.x; + R_ENV_LIGHT2_DIR(1) = envCtx->dirLight2.params.dir.y; + R_ENV_LIGHT2_DIR(2) = envCtx->dirLight2.params.dir.z; + + R_ENV_WIND_DIR(0) = envCtx->windDirection.x; + R_ENV_WIND_DIR(1) = envCtx->windDirection.y; + R_ENV_WIND_DIR(2) = envCtx->windDirection.z; + R_ENV_WIND_SPEED = envCtx->windSpeed; + } else { + lightCtx->ambientColor[0] = R_ENV_AMBIENT_COLOR(0); + lightCtx->ambientColor[1] = R_ENV_AMBIENT_COLOR(1); + lightCtx->ambientColor[2] = R_ENV_AMBIENT_COLOR(2); + + envCtx->dirLight1.params.dir.color[0] = R_ENV_LIGHT1_COLOR(0); + envCtx->dirLight1.params.dir.color[1] = R_ENV_LIGHT1_COLOR(1); + envCtx->dirLight1.params.dir.color[2] = R_ENV_LIGHT1_COLOR(2); + + envCtx->dirLight2.params.dir.color[0] = R_ENV_LIGHT2_COLOR(0); + envCtx->dirLight2.params.dir.color[1] = R_ENV_LIGHT2_COLOR(1); + envCtx->dirLight2.params.dir.color[2] = R_ENV_LIGHT2_COLOR(2); + lightCtx->fogColor[0] = R_ENV_FOG_COLOR(0); + lightCtx->fogColor[1] = R_ENV_FOG_COLOR(1); + lightCtx->fogColor[2] = R_ENV_FOG_COLOR(2); + lightCtx->fogNear = R_ENV_FOG_NEAR; + lightCtx->fogFar = R_ENV_FOG_FAR; + + if (cREG(14)) { + R_ENV_LIGHT1_DIR(0) = Math_CosS(cREG(10)) * Math_CosS(cREG(11)) * 120.0f; + envCtx->dirLight1.params.dir.x = R_ENV_LIGHT1_DIR(0); + R_ENV_LIGHT1_DIR(1) = Math_SinS(cREG(10)) * Math_CosS(cREG(11)) * 120.0f; + envCtx->dirLight1.params.dir.y = R_ENV_LIGHT1_DIR(1); + R_ENV_LIGHT1_DIR(2) = Math_SinS(cREG(11)) * 120.0f; + envCtx->dirLight1.params.dir.z = R_ENV_LIGHT1_DIR(2); + + R_ENV_LIGHT2_DIR(0) = Math_CosS(cREG(12)) * Math_CosS(cREG(13)) * 120.0f; + envCtx->dirLight2.params.dir.x = R_ENV_LIGHT2_DIR(0); + R_ENV_LIGHT2_DIR(1) = Math_SinS(cREG(12)) * Math_CosS(cREG(13)) * 120.0f; + envCtx->dirLight2.params.dir.y = R_ENV_LIGHT2_DIR(1); + R_ENV_LIGHT2_DIR(2) = Math_SinS(cREG(13)) * 120.0f; + envCtx->dirLight2.params.dir.z = R_ENV_LIGHT2_DIR(2); + } else { + envCtx->dirLight1.params.dir.x = R_ENV_LIGHT1_DIR(0); + envCtx->dirLight1.params.dir.y = R_ENV_LIGHT1_DIR(1); + envCtx->dirLight1.params.dir.z = R_ENV_LIGHT1_DIR(2); + + envCtx->dirLight2.params.dir.x = R_ENV_LIGHT2_DIR(0); + envCtx->dirLight2.params.dir.y = R_ENV_LIGHT2_DIR(1); + envCtx->dirLight2.params.dir.z = R_ENV_LIGHT2_DIR(2); + } + + envCtx->windDirection.x = R_ENV_WIND_DIR(0); + envCtx->windDirection.y = R_ENV_WIND_DIR(1); + envCtx->windDirection.z = R_ENV_WIND_DIR(2); + envCtx->windSpeed = R_ENV_WIND_SPEED; + } + + if ((envCtx->dirLight1.params.dir.x == 0) && (envCtx->dirLight1.params.dir.y == 0) && + (envCtx->dirLight1.params.dir.z == 0)) { + envCtx->dirLight1.params.dir.x = 1; + } + + if ((envCtx->dirLight2.params.dir.x == 0) && (envCtx->dirLight2.params.dir.y == 0) && + (envCtx->dirLight2.params.dir.z == 0)) { + envCtx->dirLight2.params.dir.x = 1; + } + } +} + +void Environment_DrawSunAndMoon(GlobalContext* globalCtx) { + f32 alpha; + f32 color; + f32 y; + f32 scale; + f32 temp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 2266); + + if (globalCtx->csCtx.state != 0) { + Math_SmoothStepToF(&globalCtx->envCtx.sunPos.x, + -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f, 1.0f, 0.8f, 0.8f); + Math_SmoothStepToF(&globalCtx->envCtx.sunPos.y, + (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f, 1.0f, 0.8f, 0.8f); + //! @bug This should be z. + Math_SmoothStepToF(&globalCtx->envCtx.sunPos.y, + (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f) * 25.0f, 1.0f, 0.8f, 0.8f); + } else { + globalCtx->envCtx.sunPos.x = -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + globalCtx->envCtx.sunPos.y = +(Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + globalCtx->envCtx.sunPos.z = +(Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f) * 25.0f; + } + + if (gSaveContext.entranceIndex != 0xCD || ((void)0, gSaveContext.sceneSetupIndex) != 5) { + Matrix_Translate(globalCtx->view.eye.x + globalCtx->envCtx.sunPos.x, + globalCtx->view.eye.y + globalCtx->envCtx.sunPos.y, + globalCtx->view.eye.z + globalCtx->envCtx.sunPos.z, MTXMODE_NEW); + + y = globalCtx->envCtx.sunPos.y / 25.0f; + temp = y / 80.0f; + + alpha = temp * 255.0f; + if (alpha < 0.0f) { + alpha = 0.0f; + } + if (alpha > 255.0f) { + alpha = 255.0f; + } + + alpha = 255.0f - alpha; + + color = temp; + if (color < 0.0f) { + color = 0.0f; + } + + if (color > 1.0f) { + color = 1.0f; + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, (u8)(color * 75.0f) + 180, (u8)(color * 155.0f) + 100, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, (u8)(color * 255.0f), (u8)(color * 255.0f), alpha); + + scale = (color * 2.0f) + 10.0f; + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_kankyo.c", 2364), G_MTX_LOAD); + func_80093AD0(globalCtx->state.gfxCtx); + + static Vtx vertices[] = { + VTX(-31, -31, 0, 0, 0, 255, 255, 255, 255), + VTX(32, -31, 0, 2016, 0, 255, 255, 255, 255), + VTX(-31, 32, 0, 0, 2016, 255, 255, 255, 255), + VTX(32, 32, 0, 2016, 2016, 255, 255, 255, 255), + }; + + // Replacement for gSunDL + gSPMatrix(POLY_OPA_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gDPPipeSync(POLY_OPA_DISP++); + gDPLoadTextureBlock_4b(POLY_OPA_DISP++, gSunTex, G_IM_FMT_I, 64, 64, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, + 6, 6, G_TX_NOLOD, G_TX_NOLOD); + gDPLoadMultiBlock_4b(POLY_OPA_DISP++, gSunEveningTex, 0x0100, 1, G_IM_FMT_I, 64, 64, 0, + G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); + gSPVertex(POLY_OPA_DISP++, vertices, 4, 0); + gSP2Triangles(POLY_OPA_DISP++, 0, 1, 2, 0, 2, 1, 3, 0); + + Matrix_Translate(globalCtx->view.eye.x - globalCtx->envCtx.sunPos.x, + globalCtx->view.eye.y - globalCtx->envCtx.sunPos.y, + globalCtx->view.eye.z - globalCtx->envCtx.sunPos.z, MTXMODE_NEW); + + color = -y / 120.0f; + color = CLAMP_MIN(color, 0.0f); + + scale = -15.0f * color + 25.0f; + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + + temp = -y / 80.0f; + temp = CLAMP_MAX(temp, 1.0f); + + alpha = temp * 255.0f; + + if (alpha > 0.0f) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_kankyo.c", 2406), G_MTX_LOAD); + func_8009398C(globalCtx->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 240, 255, 180, alpha); + gDPSetEnvColor(POLY_OPA_DISP++, 80, 70, 20, alpha); + gSPDisplayList(POLY_OPA_DISP++, gMoonDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 2429); +} + +void Environment_DrawSunLensFlare(GlobalContext* globalCtx, EnvironmentContext* envCtx, View* view, + GraphicsContext* gfxCtx, Vec3f pos, s32 unused) { + if ((globalCtx->envCtx.unk_EE[1] == 0) && (globalCtx->envCtx.unk_17 == 0)) { + Environment_DrawLensFlare(globalCtx, &globalCtx->envCtx, &globalCtx->view, globalCtx->state.gfxCtx, pos, 2000, + 370, Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f, 400, 1); + } +} + +f32 sLensFlareScales[] = { 23.0f, 12.0f, 7.0f, 5.0f, 3.0f, 10.0f, 6.0f, 2.0f, 3.0f, 1.0f }; + +void Environment_DrawLensFlare(GlobalContext* globalCtx, EnvironmentContext* envCtx, View* view, + GraphicsContext* gfxCtx, Vec3f pos, s32 unused, s16 scale, f32 colorIntensity, + s16 screenFillAlpha, u8 arg9) { + s16 i; + f32 tempX; + f32 tempY; + f32 tempZ; + f32 lookDirX; + f32 lookDirY; + f32 lookDirZ; + f32 tempX2; + f32 tempY2; + f32 tempZ2; + f32 posDirX; + f32 posDirY; + f32 posDirZ; + f32 length; + f32 dist; + f32 halfPosX; + f32 halfPosY; + f32 halfPosZ; + f32 cosAngle; + f32 pad160; + f32 unk88Target; + u32 isOffScreen = false; + f32 alpha; + f32 adjScale; + Vec3f screenPos; + f32 fogInfluence; + f32 temp; + f32 alphaScale; + Color_RGB8 lensFlareColors[] = { + { 155, 205, 255 }, // blue + { 255, 255, 205 }, // yellow + { 255, 255, 205 }, // yellow + { 255, 255, 205 }, // yellow + { 155, 255, 205 }, // green + { 205, 255, 255 }, // light blue + { 155, 155, 255 }, // dark blue + { 205, 175, 255 }, // purple + { 175, 255, 205 }, // light green + { 255, 155, 235 }, // pink + }; + u32 lensFlareAlphas[] = { + 50, 10, 25, 40, 70, 30, 50, 70, 50, 40, + }; + u32 lensFlareTypes[] = { + LENS_FLARE_RING, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, + LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, LENS_FLARE_CIRCLE1, + }; + + OPEN_DISPS(gfxCtx, "../z_kankyo.c", 2516); + + dist = Math3D_Vec3f_DistXYZ(&pos, &view->eye) / 12.0f; + + // compute a unit vector in the look direction + tempX = view->lookAt.x - view->eye.x; + tempY = view->lookAt.y - view->eye.y; + tempZ = view->lookAt.z - view->eye.z; + + length = sqrtf(SQ(tempX) + SQ(tempY) + SQ(tempZ)); + + lookDirX = tempX / length; + lookDirY = tempY / length; + lookDirZ = tempZ / length; + + // compute a position along the look vector half as far as pos + halfPosX = view->eye.x + lookDirX * (dist * 6.0f); + halfPosY = view->eye.y + lookDirY * (dist * 6.0f); + halfPosZ = view->eye.z + lookDirZ * (dist * 6.0f); + + // compute a unit vector in the direction from halfPos to pos + tempX2 = pos.x - halfPosX; + tempY2 = pos.y - halfPosY; + tempZ2 = pos.z - halfPosZ; + + length = sqrtf(SQ(tempX2) + SQ(tempY2) + SQ(tempZ2)); + + posDirX = tempX2 / length; + posDirY = tempY2 / length; + posDirZ = tempZ2 / length; + + // compute the cosine of the angle between lookDir and posDir + cosAngle = (lookDirX * posDirX + lookDirY * posDirY + lookDirZ * posDirZ) / + sqrtf((SQ(lookDirX) + SQ(lookDirY) + SQ(lookDirZ)) * (SQ(posDirX) + SQ(posDirY) + SQ(posDirZ))); + + unk88Target = cosAngle * 3.5f; + unk88Target = CLAMP_MAX(unk88Target, 1.0f); + + if (arg9 == 0) { + unk88Target = cosAngle; + } + + if (cosAngle < 0.0f) { + + } else { + if (arg9) { + u32 shrink = ShrinkWindow_GetCurrentVal(); + func_800C016C(globalCtx, &pos, &screenPos); + D_8015FD7E = (s16)screenPos.x; + D_8015FD80 = (s16)screenPos.y - 5.0f; + if (D_8011FB44 != 0xFFFC || screenPos.x < 0.0f || screenPos.y < shrink || screenPos.x > SCREEN_WIDTH || + screenPos.y > (SCREEN_HEIGHT - shrink)) { + isOffScreen = true; + } + } + + for (i = 0; i < ARRAY_COUNT(lensFlareTypes); i++) { + Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW); + + if (arg9) { + temp = Environment_LerpWeight(60, 15, globalCtx->view.fovy); + } + + Matrix_Translate(-posDirX * i * dist, -posDirY * i * dist, -posDirZ * i * dist, MTXMODE_APPLY); + + adjScale = sLensFlareScales[i] * cosAngle; + + if (arg9) { + adjScale *= 0.001 * (scale + 630.0f * temp); + } else { + adjScale *= 0.0001f * scale * (2.0f * dist); + } + + Matrix_Scale(adjScale, adjScale, adjScale, MTXMODE_APPLY); + + alpha = colorIntensity / 10.0f; + alpha = CLAMP_MAX(alpha, 1.0f); + alpha = alpha * lensFlareAlphas[i]; + alpha = CLAMP_MIN(alpha, 0.0f); + + fogInfluence = (996 - globalCtx->lightCtx.fogNear) / 50.0f; + + fogInfluence = CLAMP_MAX(fogInfluence, 1.0f); + + alpha *= 1.0f - fogInfluence; + + if (1) {} + + if (!(isOffScreen ^ 0)) { + Math_SmoothStepToF(&envCtx->unk_88, unk88Target, 0.5f, 0.05f, 0.001f); + } else { + Math_SmoothStepToF(&envCtx->unk_88, 0.0f, 0.5f, 0.05f, 0.001f); + } + + POLY_XLU_DISP = func_800947AC(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, lensFlareColors[i].r, lensFlareColors[i].g, lensFlareColors[i].b, + alpha * envCtx->unk_88); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_kankyo.c", 2662), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetCombineLERP(POLY_XLU_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, + 0, PRIMITIVE, 0); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + + switch (lensFlareTypes[i]) { + case LENS_FLARE_CIRCLE0: + case LENS_FLARE_CIRCLE1: + gSPDisplayList(POLY_XLU_DISP++, gLensFlareCircleDL); + break; + case LENS_FLARE_RING: + gSPDisplayList(POLY_XLU_DISP++, gLensFlareRingDL); + break; + } + } + + alphaScale = cosAngle - (1.5f - cosAngle); + + if (screenFillAlpha != 0) { + if (alphaScale > 0.0f) { + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + + alpha = colorIntensity / 10.0f; + alpha = CLAMP_MAX(alpha, 1.0f); + alpha = alpha * screenFillAlpha; + alpha = CLAMP_MIN(alpha, 0.0f); + + fogInfluence = (996 - globalCtx->lightCtx.fogNear) / 50.0f; + + fogInfluence = CLAMP_MAX(fogInfluence, 1.0f); + + alpha *= 1.0f - fogInfluence; + + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + + if (!(isOffScreen ^ 0)) { + Math_SmoothStepToF(&envCtx->unk_84, alpha * alphaScale, 0.5f, 50.0f, 0.1f); + } else { + Math_SmoothStepToF(&envCtx->unk_84, 0.0f, 0.5f, 50.0f, 0.1f); + } + + temp = colorIntensity / 120.0f; + temp = CLAMP_MIN(temp, 0.0f); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, (u8)(temp * 75.0f) + 180, (u8)(temp * 155.0f) + 100, + (u8)envCtx->unk_84); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + } else { + envCtx->unk_84 = 0.0f; + } + } + } + + CLOSE_DISPS(gfxCtx, "../z_kankyo.c", 2750); +} + +f32 func_800746DC(void) { + return Rand_ZeroOne() - 0.5f; +} + +void Environment_DrawRain(GlobalContext* globalCtx, View* view, GraphicsContext* gfxCtx) { + s16 i; + s32 pad; + Vec3f vec; + f32 temp1; + f32 temp2; + f32 temp3; + f32 length; + f32 rotX; + f32 rotY; + f32 x50; + f32 y50; + f32 z50; + f32 x280; + f32 z280; + Vec3f unused = { 0.0f, 0.0f, 0.0f }; + Vec3f windDirection = { 0.0f, 0.0f, 0.0f }; + Player* player = GET_PLAYER(globalCtx); + + if (!(globalCtx->cameraPtrs[0]->unk_14C & 0x100) && (globalCtx->envCtx.unk_EE[2] == 0)) { + OPEN_DISPS(gfxCtx, "../z_kankyo.c", 2799); + + vec.x = view->lookAt.x - view->eye.x; + vec.y = view->lookAt.y - view->eye.y; + vec.z = view->lookAt.z - view->eye.z; + + length = sqrtf(SQXYZ(vec)); + + temp1 = vec.x / length; + temp2 = vec.y / length; + temp3 = vec.z / length; + + x50 = view->eye.x + temp1 * 50.0f; + y50 = view->eye.y + temp2 * 50.0f; + z50 = view->eye.z + temp3 * 50.0f; + + x280 = view->eye.x + temp1 * 280.0f; + z280 = view->eye.z + temp3 * 280.0f; + + if (globalCtx->envCtx.unk_EE[1]) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 255, 255, 30); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 20); + } + + // draw rain drops + for (i = 0; i < globalCtx->envCtx.unk_EE[1]; i++) { + temp2 = Rand_ZeroOne(); + temp1 = Rand_ZeroOne(); + temp3 = Rand_ZeroOne(); + + Matrix_Translate((temp2 - 0.7f) * 100.0f + x50, (temp1 - 0.7f) * 100.0f + y50, + (temp3 - 0.7f) * 100.0f + z50, MTXMODE_NEW); + + windDirection.x = globalCtx->envCtx.windDirection.x; + windDirection.y = globalCtx->envCtx.windDirection.y; + windDirection.z = globalCtx->envCtx.windDirection.z; + + vec.x = windDirection.x; + vec.y = windDirection.y + 500.0f + Rand_ZeroOne() * 200.0f; + vec.z = windDirection.z; + length = sqrtf(SQXZ(vec)); + + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + rotX = Math_Atan2F(length, -vec.y); + rotY = Math_Atan2F(vec.z, vec.x); + Matrix_RotateY(-rotY, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2 - rotX, MTXMODE_APPLY); + Matrix_Scale(0.4f, 1.2f, 0.4f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_kankyo.c", 2887), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gRaindropDL); + } + + // draw droplet rings on the ground + if (player->actor.world.pos.y < view->eye.y) { + u8 firstDone = false; + + for (i = 0; i < globalCtx->envCtx.unk_EE[1]; i++) { + if (!firstDone) { + func_80093D84(gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 120); + firstDone++; + } + + Matrix_Translate(func_800746DC() * 280.0f + x280, player->actor.world.pos.y + 2.0f, + func_800746DC() * 280.0f + z280, MTXMODE_NEW); + + if ((LINK_IS_ADULT && ((player->actor.world.pos.y + 2.0f - view->eye.y) > -48.0f)) || + (!LINK_IS_ADULT && ((player->actor.world.pos.y + 2.0f - view->eye.y) > -30.0f))) { + Matrix_Scale(0.02f, 0.02f, 0.02f, MTXMODE_APPLY); + } else { + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_kankyo.c", 2940), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffShockwaveDL); + } + } + + CLOSE_DISPS(gfxCtx, "../z_kankyo.c", 2946); + } +} + +void func_80074CE8(GlobalContext* globalCtx, u32 arg1) { + if ((globalCtx->envCtx.unk_BD != arg1) && (globalCtx->envCtx.unk_D8 >= 1.0f) && + (globalCtx->envCtx.unk_BF == 0xFF)) { + if (arg1 > 30) { + arg1 = 0; + } + + globalCtx->envCtx.unk_D8 = 0.0f; + globalCtx->envCtx.unk_BE = globalCtx->envCtx.unk_BD; + globalCtx->envCtx.unk_BD = arg1; + } +} + +/** + * Draw color filters over the skybox. There are two filters. + * The first uses the global fog color, and an alpha calculated with `fogNear`. + * This filter draws unconditionally for skybox 29 at full alpha. + * (note: skybox 29 is unused in the original game) + * For the rest of the skyboxes it will draw if fogNear is less than 980. + * + * The second filter uses a custom color specified in `skyboxFilterColor` + * and can be enabled with `customSkyboxFilter`. + * + * An example usage of a filter is to dim the skybox in cloudy conditions. + */ +void Environment_DrawSkyboxFilters(GlobalContext* globalCtx) { + if (((globalCtx->skyboxId != SKYBOX_NONE) && (globalCtx->lightCtx.fogNear < 980)) || + (globalCtx->skyboxId == SKYBOX_UNSET_1D)) { + f32 alpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3032); + + func_800938B4(globalCtx->state.gfxCtx); + + alpha = (1000 - globalCtx->lightCtx.fogNear) * 0.02f; + + if (globalCtx->skyboxId == SKYBOX_UNSET_1D) { + alpha = 1.0f; + } + + if (alpha > 1.0f) { + alpha = 1.0f; + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, globalCtx->lightCtx.fogColor[0], globalCtx->lightCtx.fogColor[1], + globalCtx->lightCtx.fogColor[2], 255.0f * alpha); + gDPFillRectangle(POLY_OPA_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3043); + } + + if (globalCtx->envCtx.customSkyboxFilter) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3048); + + func_800938B4(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, globalCtx->envCtx.skyboxFilterColor[0], + globalCtx->envCtx.skyboxFilterColor[1], globalCtx->envCtx.skyboxFilterColor[2], + globalCtx->envCtx.skyboxFilterColor[3]); + gDPFillRectangle(POLY_OPA_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3056); + } +} + +void Environment_DrawLightningFlash(GlobalContext* globalCtx, u8 red, u8 green, u8 blue, u8 alpha) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3069); + + func_800938B4(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, red, green, blue, alpha); + gDPFillRectangle(POLY_OPA_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3079); +} + +void Environment_UpdateLightningStrike(GlobalContext* globalCtx) { + if (globalCtx->envCtx.lightningMode != LIGHTNING_MODE_OFF) { + switch (gLightningStrike.state) { + case LIGHTNING_STRIKE_WAIT: + // every frame theres a 10% chance of the timer advancing 50 units + if (Rand_ZeroOne() < 0.1f) { + gLightningStrike.delayTimer += 50.0f; + } + + gLightningStrike.delayTimer += Rand_ZeroOne(); + + if (gLightningStrike.delayTimer > 500.0f) { + gLightningStrike.flashRed = 200; + gLightningStrike.flashGreen = 200; + gLightningStrike.flashBlue = 255; + gLightningStrike.flashAlphaTarget = 200; + + gLightningStrike.delayTimer = 0.0f; + Environment_AddLightningBolts(globalCtx, + (u8)(Rand_ZeroOne() * (ARRAY_COUNT(sLightningBolts) - 0.1f)) + 1); + sLightningFlashAlpha = 0; + gLightningStrike.state++; + } + break; + case LIGHTNING_STRIKE_START: + gLightningStrike.flashRed = 200; + gLightningStrike.flashGreen = 200; + gLightningStrike.flashBlue = 255; + + globalCtx->envCtx.adjAmbientColor[0] += 80; + globalCtx->envCtx.adjAmbientColor[1] += 80; + globalCtx->envCtx.adjAmbientColor[2] += 100; + + sLightningFlashAlpha += 100; + + if (sLightningFlashAlpha >= gLightningStrike.flashAlphaTarget) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_0, 0); + gLightningStrike.state++; + gLightningStrike.flashAlphaTarget = 0; + } + break; + case LIGHTNING_STRIKE_END: + if (globalCtx->envCtx.adjAmbientColor[0] > 0) { + globalCtx->envCtx.adjAmbientColor[0] -= 10; + globalCtx->envCtx.adjAmbientColor[1] -= 10; + } + + if (globalCtx->envCtx.adjAmbientColor[2] > 0) { + globalCtx->envCtx.adjAmbientColor[2] -= 10; + } + + sLightningFlashAlpha -= 10; + + if (sLightningFlashAlpha <= gLightningStrike.flashAlphaTarget) { + globalCtx->envCtx.adjAmbientColor[0] = 0; + globalCtx->envCtx.adjAmbientColor[1] = 0; + globalCtx->envCtx.adjAmbientColor[2] = 0; + + gLightningStrike.state = LIGHTNING_STRIKE_WAIT; + + if (globalCtx->envCtx.lightningMode == LIGHTNING_MODE_LAST) { + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_OFF; + } + } + break; + } + } + + if (gLightningStrike.state != LIGHTNING_STRIKE_WAIT) { + Environment_DrawLightningFlash(globalCtx, gLightningStrike.flashRed, gLightningStrike.flashGreen, + gLightningStrike.flashBlue, sLightningFlashAlpha); + } +} + +/** + * Request the number of lightning bolts specified by `num` + * Note: only 3 lightning bolts can be active at the same time. + */ +void Environment_AddLightningBolts(GlobalContext* globalCtx, u8 num) { + s16 boltsAdded = 0; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sLightningBolts); i++) { + if (sLightningBolts[i].state == LIGHTNING_BOLT_INACTIVE) { + sLightningBolts[i].state = LIGHTNING_BOLT_START; + boltsAdded++; + + if (boltsAdded >= num) { + break; + } + } + } +} + +/** + * Draw any active lightning bolt entries contained in `sLightningBolts` + */ +void Environment_DrawLightning(GlobalContext* globalCtx, s32 unused) { + static void* lightningTextures[] = { + gEffLightning1Tex, gEffLightning2Tex, gEffLightning3Tex, + gEffLightning4Tex, gEffLightning5Tex, gEffLightning6Tex, + gEffLightning7Tex, gEffLightning8Tex, NULL, + }; + s16 i; + f32 dx; + f32 dz; + f32 x; + f32 z; + s32 pad[2]; + Vec3f unused1 = { 0.0f, 0.0f, 0.0f }; + Vec3f unused2 = { 0.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3253); + + for (i = 0; i < ARRAY_COUNT(sLightningBolts); i++) { + switch (sLightningBolts[i].state) { + case LIGHTNING_BOLT_START: + dx = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + dz = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + + x = dx / sqrtf(SQ(dx) + SQ(dz)); + z = dz / sqrtf(SQ(dx) + SQ(dz)); + + sLightningBolts[i].pos.x = globalCtx->view.eye.x + x * 9500.0f; + sLightningBolts[i].pos.y = Rand_ZeroOne() * 1000.0f + 4000.0f; + sLightningBolts[i].pos.z = globalCtx->view.eye.z + z * 9500.0f; + + sLightningBolts[i].offset.x = (Rand_ZeroOne() - 0.5f) * 5000.0f; + sLightningBolts[i].offset.y = 0.0f; + sLightningBolts[i].offset.z = (Rand_ZeroOne() - 0.5f) * 5000.0f; + + sLightningBolts[i].textureIndex = 0; + sLightningBolts[i].pitch = (Rand_ZeroOne() - 0.5f) * 40.0f; + sLightningBolts[i].roll = (Rand_ZeroOne() - 0.5f) * 40.0f; + sLightningBolts[i].delayTimer = 3 * (i + 1); + sLightningBolts[i].state++; + break; + case LIGHTNING_BOLT_WAIT: + sLightningBolts[i].delayTimer--; + + if (sLightningBolts[i].delayTimer <= 0) { + sLightningBolts[i].state++; + } + break; + case LIGHTNING_BOLT_DRAW: + if (sLightningBolts[i].textureIndex < 7) { + sLightningBolts[i].textureIndex++; + } else { + sLightningBolts[i].state = LIGHTNING_BOLT_INACTIVE; + } + break; + } + + if (sLightningBolts[i].state == LIGHTNING_BOLT_DRAW) { + Matrix_Translate(sLightningBolts[i].pos.x + sLightningBolts[i].offset.x, + sLightningBolts[i].pos.y + sLightningBolts[i].offset.y, + sLightningBolts[i].pos.z + sLightningBolts[i].offset.z, MTXMODE_NEW); + Matrix_RotateX(sLightningBolts[i].pitch * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_RotateZ(sLightningBolts[i].roll * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_Scale(22.0f, 100.0f, 22.0f, MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 128); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, 128); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_kankyo.c", 3333), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(lightningTextures[sLightningBolts[i].textureIndex])); + func_80094C50(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffLightningDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 3353); +} + +void Environment_PlaySceneSequence(GlobalContext* globalCtx) { + globalCtx->envCtx.unk_E0 = 0xFF; + + // both lost woods exits on the bridge from kokiri to hyrule field + if (((void)0, gSaveContext.entranceIndex) == 0x4DE || ((void)0, gSaveContext.entranceIndex) == 0x5E0) { + Audio_PlayNatureAmbienceSequence(NATURE_ID_KOKIRI_REGION); + } else if (((void)0, gSaveContext.forcedSeqId) != NA_BGM_GENERAL_SFX) { + if (!Environment_IsForcedSequenceDisabled()) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | (s32)((void)0, gSaveContext.forcedSeqId)); + } + gSaveContext.forcedSeqId = NA_BGM_GENERAL_SFX; + } else if (globalCtx->sequenceCtx.seqId == NA_BGM_NO_MUSIC) { + if (globalCtx->sequenceCtx.natureAmbienceId == NATURE_ID_NONE) { + return; + } + if (((void)0, gSaveContext.natureAmbienceId) != globalCtx->sequenceCtx.natureAmbienceId) { + Audio_PlayNatureAmbienceSequence(globalCtx->sequenceCtx.natureAmbienceId); + } + } else if (globalCtx->sequenceCtx.natureAmbienceId == NATURE_ID_NONE) { + // "BGM Configuration" + osSyncPrintf("\n\n\nBGM設定game_play->sound_info.BGM=[%d] old_bgm=[%d]\n\n", globalCtx->sequenceCtx.seqId, + ((void)0, gSaveContext.seqId)); + if (((void)0, gSaveContext.seqId) != globalCtx->sequenceCtx.seqId) { + func_800F5550(globalCtx->sequenceCtx.seqId); + } + } else if (((void)0, gSaveContext.dayTime) > 0x4AAA && ((void)0, gSaveContext.dayTime) < 0xB71D) { + if (((void)0, gSaveContext.seqId) != globalCtx->sequenceCtx.seqId) { + func_800F5550(globalCtx->sequenceCtx.seqId); + } + + globalCtx->envCtx.unk_E0 = 1; + } else { + if (((void)0, gSaveContext.natureAmbienceId) != globalCtx->sequenceCtx.natureAmbienceId) { + Audio_PlayNatureAmbienceSequence(globalCtx->sequenceCtx.natureAmbienceId); + } + + if (((void)0, gSaveContext.dayTime) > 0xB71C && ((void)0, gSaveContext.dayTime) < 0xCAAC) { + globalCtx->envCtx.unk_E0 = 3; + } else if (((void)0, gSaveContext.dayTime) > 0xCAAC || ((void)0, gSaveContext.dayTime) < 0x4555) { + globalCtx->envCtx.unk_E0 = 5; + } else { + globalCtx->envCtx.unk_E0 = 7; + } + } + + osSyncPrintf("\n-----------------\n", ((void)0, gSaveContext.forcedSeqId)); + osSyncPrintf("\n 強制BGM=[%d]", ((void)0, gSaveContext.forcedSeqId)); // "Forced BGM" + osSyncPrintf("\n BGM=[%d]", globalCtx->sequenceCtx.seqId); + osSyncPrintf("\n エンブ=[%d]", globalCtx->sequenceCtx.natureAmbienceId); + osSyncPrintf("\n status=[%d]", globalCtx->envCtx.unk_E0); + + Audio_SetEnvReverb(globalCtx->roomCtx.curRoom.echo); +} + +// updates bgm/sfx and other things as the day progresses +void func_80075B44(GlobalContext* globalCtx) { + switch (globalCtx->envCtx.unk_E0) { + case 0: + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_CRITTER_4 << 4 | NATURE_CHANNEL_CRITTER_5, + CHANNEL_IO_PORT_1, 0); + if (globalCtx->envCtx.unk_EE[0] == 0 && globalCtx->envCtx.unk_F2[0] == 0) { + osSyncPrintf("\n\n\nNa_StartMorinigBgm\n\n"); + func_800F5510(globalCtx->sequenceCtx.seqId); + } + globalCtx->envCtx.unk_E0++; + break; + case 1: + if (gSaveContext.dayTime > 0xB71C) { + if (globalCtx->envCtx.unk_EE[0] == 0 && globalCtx->envCtx.unk_F2[0] == 0) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xF000FF); + } + globalCtx->envCtx.unk_E0++; + } + break; + case 2: + if (gSaveContext.dayTime > 0xC000) { + func_800788CC(NA_SE_EV_DOG_CRY_EVENING); + globalCtx->envCtx.unk_E0++; + } + break; + case 3: + if (globalCtx->envCtx.unk_EE[0] == 0 && globalCtx->envCtx.unk_F2[0] == 0) { + Audio_PlayNatureAmbienceSequence(globalCtx->sequenceCtx.natureAmbienceId); + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_CRITTER_0, CHANNEL_IO_PORT_1, 1); + } + globalCtx->envCtx.unk_E0++; + break; + case 4: + if (gSaveContext.dayTime > 0xCAAB) { + globalCtx->envCtx.unk_E0++; + } + break; + case 5: + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_CRITTER_0, CHANNEL_IO_PORT_1, 0); + if (globalCtx->envCtx.unk_EE[0] == 0 && globalCtx->envCtx.unk_F2[0] == 0) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_CRITTER_1 << 4 | NATURE_CHANNEL_CRITTER_3, + CHANNEL_IO_PORT_1, 1); + } + globalCtx->envCtx.unk_E0++; + break; + case 6: + if ((gSaveContext.dayTime < 0xCAAC) && (gSaveContext.dayTime > 0x4555)) { + gSaveContext.totalDays++; + gSaveContext.bgsDayCount++; + gSaveContext.dogIsLost = true; + func_80078884(NA_SE_EV_CHICKEN_CRY_M); + if ((Inventory_ReplaceItem(globalCtx, ITEM_WEIRD_EGG, ITEM_CHICKEN) || + Inventory_ReplaceItem(globalCtx, ITEM_POCKET_EGG, ITEM_POCKET_CUCCO)) && + globalCtx->csCtx.state == 0 && !Player_InCsMode(globalCtx)) { + Message_StartTextbox(globalCtx, 0x3066, NULL); + } + globalCtx->envCtx.unk_E0++; + } + break; + case 7: + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_CRITTER_1 << 4 | NATURE_CHANNEL_CRITTER_3, + CHANNEL_IO_PORT_1, 0); + if (globalCtx->envCtx.unk_EE[0] == 0 && globalCtx->envCtx.unk_F2[0] == 0) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_CRITTER_4 << 4 | NATURE_CHANNEL_CRITTER_5, + CHANNEL_IO_PORT_1, 1); + } + globalCtx->envCtx.unk_E0++; + break; + case 8: + if (gSaveContext.dayTime > 0x4AAB) { + globalCtx->envCtx.unk_E0 = 0; + } + break; + } +} + +void Environment_DrawCustomLensFlare(GlobalContext* globalCtx) { + Vec3f pos; + + if (gCustomLensFlareOn) { + pos.x = gCustomLensFlarePos.x; + pos.y = gCustomLensFlarePos.y; + pos.z = gCustomLensFlarePos.z; + + Environment_DrawLensFlare(globalCtx, &globalCtx->envCtx, &globalCtx->view, globalCtx->state.gfxCtx, pos, + gLensFlareUnused, gLensFlareScale, gLensFlareColorIntensity, + gLensFlareScreenFillAlpha, 0); + } +} + +void Environment_InitGameOverLights(GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + sGameOverLightsIntensity = 0; + + Lights_PointNoGlowSetInfo(&sNGameOverLightInfo, (s16)player->actor.world.pos.x - 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z - 10.0f, 0, 0, 0, + 255); + sNGameOverLightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &sNGameOverLightInfo); + + Lights_PointNoGlowSetInfo(&sSGameOverLightInfo, (s16)player->actor.world.pos.x + 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z + 10.0f, 0, 0, 0, + 255); + sSGameOverLightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &sSGameOverLightInfo); +} + +void Environment_FadeInGameOverLights(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 i; + + Lights_PointNoGlowSetInfo(&sNGameOverLightInfo, (s16)player->actor.world.pos.x - 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z - 10.0f, + sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255); + Lights_PointNoGlowSetInfo(&sSGameOverLightInfo, (s16)player->actor.world.pos.x + 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z + 10.0f, + sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255); + + if (sGameOverLightsIntensity < 254) { + sGameOverLightsIntensity += 2; + } + + if (func_800C0CB8(globalCtx)) { + for (i = 0; i < 3; i++) { + if (globalCtx->envCtx.adjAmbientColor[i] > -255) { + globalCtx->envCtx.adjAmbientColor[i] -= 12; + globalCtx->envCtx.adjLight1Color[i] -= 12; + } + globalCtx->envCtx.adjFogColor[i] = -255; + } + + if (globalCtx->envCtx.lightSettings.fogFar + globalCtx->envCtx.adjFogFar > 900) { + globalCtx->envCtx.adjFogFar -= 100; + } + + if (globalCtx->envCtx.lightSettings.fogNear + globalCtx->envCtx.adjFogNear > 950) { + globalCtx->envCtx.adjFogNear -= 10; + } + } else { + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 0; + globalCtx->envCtx.screenFillColor[3] = sGameOverLightsIntensity; + } +} + +void Environment_FadeOutGameOverLights(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 i; + + if (sGameOverLightsIntensity >= 3) { + sGameOverLightsIntensity -= 3; + } else { + sGameOverLightsIntensity = 0; + } + + if (sGameOverLightsIntensity == 1) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, sNGameOverLightNode); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, sSGameOverLightNode); + } else if (sGameOverLightsIntensity >= 2) { + Lights_PointNoGlowSetInfo(&sNGameOverLightInfo, (s16)player->actor.world.pos.x - 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z - 10.0f, + sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255); + Lights_PointNoGlowSetInfo(&sSGameOverLightInfo, (s16)player->actor.world.pos.x + 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z + 10.0f, + sGameOverLightsIntensity, sGameOverLightsIntensity, sGameOverLightsIntensity, 255); + } + + if (func_800C0CB8(globalCtx)) { + for (i = 0; i < 3; i++) { + Math_SmoothStepToS(&globalCtx->envCtx.adjAmbientColor[i], 0, 5, 12, 1); + Math_SmoothStepToS(&globalCtx->envCtx.adjLight1Color[i], 0, 5, 12, 1); + globalCtx->envCtx.adjFogColor[i] = 0; + } + globalCtx->envCtx.adjFogFar = 0; + globalCtx->envCtx.adjFogNear = 0; + } else { + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 0; + globalCtx->envCtx.screenFillColor[3] = sGameOverLightsIntensity; + if (sGameOverLightsIntensity == 0) { + globalCtx->envCtx.fillScreen = false; + } + } +} + +void func_800766C4(GlobalContext* globalCtx) { + u8 max = MAX(globalCtx->envCtx.unk_EE[0], globalCtx->envCtx.unk_F2[0]); + + if (globalCtx->envCtx.unk_EE[1] != max && ((globalCtx->state.frames % 8) == 0)) { + if (globalCtx->envCtx.unk_EE[1] < max) { + globalCtx->envCtx.unk_EE[1] += 2; + } else { + globalCtx->envCtx.unk_EE[1] -= 2; + } + } +} + +void Environment_FillScreen(GraphicsContext* gfxCtx, u8 red, u8 green, u8 blue, u8 alpha, u8 drawFlags) { + if (alpha != 0) { + OPEN_DISPS(gfxCtx, "../z_kankyo.c", 3835); + + if (drawFlags & FILL_SCREEN_OPA) { + POLY_OPA_DISP = func_800937C0(POLY_OPA_DISP); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, red, green, blue, alpha); + gDPSetAlphaDither(POLY_OPA_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_OPA_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_OPA_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + } + + if (drawFlags & FILL_SCREEN_XLU) { + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, red, green, blue, alpha); + + if ((u32)alpha == 255) { + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_OPA_SURF, G_RM_OPA_SURF2); + } + + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + } + + CLOSE_DISPS(gfxCtx, "../z_kankyo.c", 3863); + } +} + +Color_RGB8 sSandstormPrimColors[] = { + { 210, 156, 85 }, + { 255, 200, 100 }, + { 225, 160, 50 }, + { 105, 90, 40 }, +}; + +Color_RGB8 sSandstormEnvColors[] = { + { 155, 106, 35 }, + { 200, 150, 50 }, + { 170, 110, 0 }, + { 50, 40, 0 }, +}; + +void Environment_DrawSandstorm(GlobalContext* globalCtx, u8 sandstormState) { + s32 primA1; + s32 envA1; + s32 primA = globalCtx->envCtx.sandstormPrimA; + s32 envA = globalCtx->envCtx.sandstormEnvA; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + s32 pad; + f32 sp98; + u16 sp96; + u16 sp94; + u16 sp92; + + switch (sandstormState) { + case 3: + if ((globalCtx->sceneNum == SCENE_SPOT13) && (globalCtx->roomCtx.curRoom.num == 0)) { + envA1 = 0; + primA1 = (globalCtx->envCtx.sandstormEnvA > 128) ? 255 : globalCtx->envCtx.sandstormEnvA >> 1; + } else { + primA1 = globalCtx->state.frames % 128; + if (primA1 > 64) { + primA1 = 128 - primA1; + } + primA1 += 73; + envA1 = 128; + } + break; + case 1: + primA1 = 255; + envA1 = (globalCtx->envCtx.sandstormPrimA >= 255) ? 255 : 128; + break; + case 2: + envA1 = 128; + if (globalCtx->envCtx.sandstormEnvA > 128) { + primA1 = 0xFF; + } else { + primA1 = globalCtx->state.frames % 128; + if (primA1 > 64) { + primA1 = 128 - primA1; + } + primA1 += 73; + } + if ((primA1 >= primA) && (primA1 != 255)) { + globalCtx->envCtx.sandstormState = 3; + } + break; + case 4: + envA1 = 0; + primA1 = (globalCtx->envCtx.sandstormEnvA > 128) ? 255 : globalCtx->envCtx.sandstormEnvA >> 1; + + if (primA == 0) { + globalCtx->envCtx.sandstormState = 0; + } + break; + } + + if (ABS(primA - primA1) < 9) { + primA = primA1; + } else if (primA1 < primA) { + primA = primA - 9; + } else { + primA = primA + 9; + } + if (ABS(envA - envA1) < 9) { + envA = envA1; + } else if (envA1 < envA) { + envA = envA - 9; + } else { + envA = envA + 9; + } + globalCtx->envCtx.sandstormPrimA = primA; + globalCtx->envCtx.sandstormEnvA = envA; + + sp98 = (512.0f - (primA + envA)) * (3.0f / 128.0f); + if (sp98 > 6.0f) { + sp98 = 6.0f; + } + if (globalCtx->envCtx.indoors || (globalCtx->envCtx.unk_BF != 0xFF)) { + primColor.r = sSandstormPrimColors[1].r; + primColor.g = sSandstormPrimColors[1].g; + primColor.b = sSandstormPrimColors[1].b; + envColor.r = sSandstormEnvColors[1].r; + envColor.g = sSandstormEnvColors[1].g; + envColor.b = sSandstormEnvColors[1].b; + } else if (D_8011FDCC == D_8011FDD0) { + primColor.r = sSandstormPrimColors[D_8011FDCC].r; + primColor.g = sSandstormPrimColors[D_8011FDCC].g; + primColor.b = sSandstormPrimColors[D_8011FDCC].b; + envColor.r = sSandstormEnvColors[D_8011FDCC].r; + envColor.g = sSandstormEnvColors[D_8011FDCC].g; + envColor.b = sSandstormEnvColors[D_8011FDCC].b; + } else { + primColor.r = (s32)F32_LERP(sSandstormPrimColors[D_8011FDCC].r, sSandstormPrimColors[D_8011FDD0].r, D_8011FDD4); + primColor.g = (s32)F32_LERP(sSandstormPrimColors[D_8011FDCC].g, sSandstormPrimColors[D_8011FDD0].g, D_8011FDD4); + primColor.b = (s32)F32_LERP(sSandstormPrimColors[D_8011FDCC].b, sSandstormPrimColors[D_8011FDD0].b, D_8011FDD4); + envColor.r = (s32)F32_LERP(sSandstormEnvColors[D_8011FDCC].r, sSandstormEnvColors[D_8011FDD0].r, D_8011FDD4); + envColor.g = (s32)F32_LERP(sSandstormEnvColors[D_8011FDCC].g, sSandstormEnvColors[D_8011FDD0].g, D_8011FDD4); + envColor.b = (s32)F32_LERP(sSandstormEnvColors[D_8011FDCC].b, sSandstormEnvColors[D_8011FDD0].b, D_8011FDD4); + } + + envColor.r = ((envColor.r * sp98) + ((6.0f - sp98) * primColor.r)) * (1.0f / 6.0f); + envColor.g = ((envColor.g * sp98) + ((6.0f - sp98) * primColor.g)) * (1.0f / 6.0f); + envColor.b = ((envColor.b * sp98) + ((6.0f - sp98) * primColor.b)) * (1.0f / 6.0f); + + sp96 = (s32)(D_8015FDB0 * (11.0f / 6.0f)); + sp94 = (s32)(D_8015FDB0 * (9.0f / 6.0f)); + sp92 = (s32)(D_8015FDB0 * (6.0f / 6.0f)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 4044); + + POLY_XLU_DISP = func_80093F34(POLY_XLU_DISP); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, primColor.r, primColor.g, primColor.b, globalCtx->envCtx.sandstormPrimA); + gDPSetEnvColor(POLY_XLU_DISP++, envColor.r, envColor.g, envColor.b, globalCtx->envCtx.sandstormEnvA); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (u32)sp96 % 0x1000, 0, 0x200, 0x20, 1, (u32)sp94 % 0x1000, + 0xFFF - ((u32)sp92 % 0x1000), 0x100, 0x40)); + gDPSetTextureLUT(POLY_XLU_DISP++, G_TT_NONE); + gSPDisplayList(POLY_XLU_DISP++, gFieldSandstormDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kankyo.c", 4068); + + D_8015FDB0 += (s32)sp98; +} + +void Environment_AdjustLights(GlobalContext* globalCtx, f32 arg1, f32 arg2, f32 arg3, f32 arg4) { + f32 temp; + s32 i; + + if (globalCtx->roomCtx.curRoom.unk_03 != 5 && func_800C0CB8(globalCtx)) { + arg1 = CLAMP_MIN(arg1, 0.0f); + arg1 = CLAMP_MAX(arg1, 1.0f); + + temp = arg1 - arg3; + if (arg1 < arg3) { + temp = 0.0f; + } + + globalCtx->envCtx.adjFogNear = (arg2 - globalCtx->envCtx.lightSettings.fogNear) * temp; + + if (arg1 == 0.0f) { + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjFogColor[i] = 0; + } + } else { + temp = arg1 * 5.0f; + temp = CLAMP_MAX(temp, 1.0f); + + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjFogColor[i] = -(s16)(globalCtx->envCtx.lightSettings.fogColor[i] * temp); + } + } + + if (arg4 <= 0.0f) { + return; + } + + arg1 *= arg4; + + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjAmbientColor[i] = -(s16)(globalCtx->envCtx.lightSettings.ambientColor[i] * arg1); + globalCtx->envCtx.adjLight1Color[i] = -(s16)(globalCtx->envCtx.lightSettings.light1Color[i] * arg1); + } + } +} + +s32 Environment_GetBgsDayCount(void) { + return gSaveContext.bgsDayCount; +} + +void Environment_ClearBgsDayCount(void) { + gSaveContext.bgsDayCount = 0; +} + +s32 Environment_GetTotalDays(void) { + return gSaveContext.totalDays; +} + +void Environment_ForcePlaySequence(u16 seqId) { + gSaveContext.forcedSeqId = seqId; +} + +s32 Environment_IsForcedSequenceDisabled(void) { + s32 isDisabled = false; + + if (gSaveContext.forcedSeqId == NA_BGM_DISABLED) { + isDisabled = true; + } + + return isDisabled; +} + +void Environment_PlayStormNatureAmbience(GlobalContext* globalCtx) { + if (globalCtx->sequenceCtx.natureAmbienceId == NATURE_ID_NONE) { + Audio_PlayNatureAmbienceSequence(NATURE_ID_MARKET_NIGHT); + } else { + Audio_PlayNatureAmbienceSequence(globalCtx->sequenceCtx.natureAmbienceId); + } + + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 1); + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 1); +} + +void Environment_StopStormNatureAmbience(GlobalContext* globalCtx) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 0); + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 0); + + if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) == NA_BGM_NATURE_AMBIENCE) { + gSaveContext.seqId = NA_BGM_NATURE_SFX_RAIN; + Environment_PlaySceneSequence(globalCtx); + } +} + +void Environment_WarpSongLeave(GlobalContext* globalCtx) { + gWeatherMode = 0; + gSaveContext.cutsceneIndex = 0; + gSaveContext.respawnFlag = -3; + globalCtx->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_RETURN].entranceIndex; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + + switch (globalCtx->nextEntranceIndex) { + case 0x147: + Flags_SetEventChkInf(0xB9); + break; + case 0x0102: + Flags_SetEventChkInf(0xB1); + break; + case 0x0123: + Flags_SetEventChkInf(0xB8); + break; + case 0x00E4: + Flags_SetEventChkInf(0xB6); + break; + case 0x0053: + Flags_SetEventChkInf(0xA7); + break; + case 0x00FC: + break; + } +} diff --git a/soh/src/code/z_lib.c b/soh/src/code/z_lib.c new file mode 100644 index 000000000..1da31eb45 --- /dev/null +++ b/soh/src/code/z_lib.c @@ -0,0 +1,572 @@ +#include "global.h" + +f32 Math_CosS(s16 angle) { + return coss(angle) * SHT_MINV; +} + +f32 Math_SinS(s16 angle) { + return sins(angle) * SHT_MINV; +} + +/** + * Changes pValue by step (scaled by the update rate) towards target, setting it equal when the target is reached. + * Returns true when target is reached, false otherwise. + */ +s32 Math_ScaledStepToS(s16* pValue, s16 target, s16 step) { + if (step != 0) { + f32 updateScale = R_UPDATE_RATE * 0.5f; + + if ((s16)(*pValue - target) > 0) { + step = -step; + } + + *pValue += (s16)(step * updateScale); + + if (((s16)(*pValue - target) * step) >= 0) { + *pValue = target; + return true; + } + } else if (target == *pValue) { + return true; + } + + return false; +} + +/** + * Changes pValue by step towards target, setting it equal when the target is reached. + * Returns true when target is reached, false otherwise. + */ +s32 Math_StepToS(s16* pValue, s16 target, s16 step) { + if (step != 0) { + if (target < *pValue) { + step = -step; + } + + *pValue += step; + + if (((*pValue - target) * step) >= 0) { + *pValue = target; + return true; + } + } else if (target == *pValue) { + return true; + } + + return false; +} + +/** + * Changes pValue by step towards target, setting it equal when the target is reached. + * Returns true when target is reached, false otherwise. + */ +s32 Math_StepToF(f32* pValue, f32 target, f32 step) { + if (step != 0.0f) { + if (target < *pValue) { + step = -step; + } + + *pValue += step; + + if (((*pValue - target) * step) >= 0) { + *pValue = target; + return true; + } + } else if (target == *pValue) { + return true; + } + + return false; +} + +/** + * Changes pValue by step. If pvalue reaches limit angle or its opposite, sets it equal to limit angle. + * Returns true when limit angle or its opposite is reached, false otherwise. + */ +s32 Math_StepUntilAngleS(s16* pValue, s16 limit, s16 step) { + s16 orig = *pValue; + + *pValue += step; + + if (((s16)(*pValue - limit) * (s16)(orig - limit)) <= 0) { + *pValue = limit; + return true; + } + + return false; +} + +/** + * Changes pValue by step. If pvalue reaches limit, sets it equal to limit. + * Returns true when limit is reached, false otherwise. + */ +s32 Math_StepUntilS(s16* pValue, s16 limit, s16 step) { + s16 orig = *pValue; + + *pValue += step; + + if (((*pValue - limit) * (orig - limit)) <= 0) { + *pValue = limit; + return true; + } + + return false; +} + +/** + * Changes pValue by step towards target angle, setting it equal when the target is reached. + * Returns true when target is reached, false otherwise. + */ +s32 Math_StepToAngleS(s16* pValue, s16 target, s16 step) { + s32 diff = target - *pValue; + + if (diff < 0) { + step = -step; + } + + if (diff >= 0x8000) { + step = -step; + diff = -0xFFFF - -diff; + } else if (diff <= -0x8000) { + diff += 0xFFFF; + step = -step; + } + + if (step != 0) { + *pValue += step; + + if ((diff * step) <= 0) { + *pValue = target; + return true; + } + } else if (target == *pValue) { + return true; + } + + return false; +} + +/** + * Changes pValue by step. If pvalue reaches limit, sets it equal to limit. + * Returns true when limit is reached, false otherwise. + */ +s32 Math_StepUntilF(f32* pValue, f32 limit, f32 step) { + f32 orig = *pValue; + + *pValue += step; + + if (((*pValue - limit) * (orig - limit)) <= 0) { + *pValue = limit; + return true; + } + + return false; +} + +/** + * Changes pValue toward target by incrStep if pValue is smaller and by decrStep if it is greater, setting it equal when + * target is reached. Returns true when target is reached, false otherwise. + */ +s32 Math_AsymStepToF(f32* pValue, f32 target, f32 incrStep, f32 decrStep) { + f32 step = (target >= *pValue) ? incrStep : decrStep; + + if (step != 0.0f) { + if (target < *pValue) { + step = -step; + } + + *pValue += step; + + if (((*pValue - target) * step) >= 0) { + *pValue = target; + return 1; + } + } else if (target == *pValue) { + return 1; + } + + return 0; +} + +void func_80077D10(f32* arg0, s16* arg1, Input* input) { + f32 relX = input->rel.stick_x; + f32 relY = input->rel.stick_y; + + *arg0 = sqrtf(SQ(relX) + SQ(relY)); + *arg0 = (60.0f < *arg0) ? 60.0f : *arg0; + + *arg1 = Math_Atan2S(relY, -relX); +} + +s16 Rand_S16Offset(s16 base, s16 range) { + return (s16)(Rand_ZeroOne() * range) + base; +} + +s16 Rand_S16OffsetStride(s16 base, s16 stride, s16 range) { + return (s16)(Rand_ZeroOne() * range) * stride + base; +} + +void Math_Vec3f_Copy(Vec3f* dest, Vec3f* src) { + dest->x = src->x; + dest->y = src->y; + dest->z = src->z; +} + +void Math_Vec3s_ToVec3f(Vec3f* dest, Vec3s* src) { + dest->x = src->x; + dest->y = src->y; + dest->z = src->z; +} + +void Math_Vec3f_Sum(Vec3f* a, Vec3f* b, Vec3f* dest) { + dest->x = a->x + b->x; + dest->y = a->y + b->y; + dest->z = a->z + b->z; +} + +void Math_Vec3f_Diff(Vec3f* a, Vec3f* b, Vec3f* dest) { + dest->x = a->x - b->x; + dest->y = a->y - b->y; + dest->z = a->z - b->z; +} + +void Math_Vec3s_DiffToVec3f(Vec3f* dest, Vec3s* a, Vec3s* b) { + dest->x = a->x - b->x; + dest->y = a->y - b->y; + dest->z = a->z - b->z; +} + +void Math_Vec3f_Scale(Vec3f* vec, f32 scaleF) { + vec->x *= scaleF; + vec->y *= scaleF; + vec->z *= scaleF; +} + +f32 Math_Vec3f_DistXYZ(Vec3f* a, Vec3f* b) { + f32 dx = b->x - a->x; + f32 dy = b->y - a->y; + f32 dz = b->z - a->z; + + return sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); +} + +f32 Math_Vec3f_DistXYZAndStoreDiff(Vec3f* a, Vec3f* b, Vec3f* dest) { + dest->x = b->x - a->x; + dest->y = b->y - a->y; + dest->z = b->z - a->z; + + return sqrtf(SQ(dest->x) + SQ(dest->y) + SQ(dest->z)); +} + +f32 Math_Vec3f_DistXZ(Vec3f* a, Vec3f* b) { + f32 dx = b->x - a->x; + f32 dz = b->z - a->z; + + return sqrtf(SQ(dx) + SQ(dz)); +} + +f32 Math_Vec3f_DiffY(Vec3f* a, Vec3f* b) { + return b->y - a->y; +} + +s16 Math_Vec3f_Yaw(Vec3f* a, Vec3f* b) { + f32 dx = b->x - a->x; + f32 dz = b->z - a->z; + + return Math_Atan2S(dz, dx); +} + +s16 Math_Vec3f_Pitch(Vec3f* a, Vec3f* b) { + return Math_Atan2S(Math_Vec3f_DistXZ(a, b), a->y - b->y); +} + +void IChain_Apply_u8(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_s8(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_u16(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_s16(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_u32(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_s32(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_f32(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_f32div1000(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_Vec3f(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_Vec3fdiv1000(u8* ptr, InitChainEntry* ichain); +void IChain_Apply_Vec3s(u8* ptr, InitChainEntry* ichain); + +void (*sInitChainHandlers[])(u8* ptr, InitChainEntry* ichain) = { + IChain_Apply_u8, IChain_Apply_s8, IChain_Apply_u16, IChain_Apply_s16, + IChain_Apply_u32, IChain_Apply_s32, IChain_Apply_f32, IChain_Apply_f32div1000, + IChain_Apply_Vec3f, IChain_Apply_Vec3fdiv1000, IChain_Apply_Vec3s, +}; + +void Actor_ProcessInitChain(Actor* actor, InitChainEntry* ichain) { + do { + sInitChainHandlers[ichain->type]((u8*)actor, ichain); + } while ((ichain++)->cont); +} + +void IChain_Apply_u8(u8* ptr, InitChainEntry* ichain) { + *(u8*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_s8(u8* ptr, InitChainEntry* ichain) { + *(s8*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_u16(u8* ptr, InitChainEntry* ichain) { + *(u16*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_s16(u8* ptr, InitChainEntry* ichain) { + *(s16*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_u32(u8* ptr, InitChainEntry* ichain) { + *(u32*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_s32(u8* ptr, InitChainEntry* ichain) { + *(s32*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_f32(u8* ptr, InitChainEntry* ichain) { + *(f32*)(ptr + ichain->offset) = ichain->value; +} + +void IChain_Apply_f32div1000(u8* ptr, InitChainEntry* ichain) { + *(f32*)(ptr + ichain->offset) = ichain->value / 1000.0f; +} + +void IChain_Apply_Vec3f(u8* ptr, InitChainEntry* ichain) { + Vec3f* vec = (Vec3f*)(ptr + ichain->offset); + f32 val = ichain->value; + + vec->z = val; + vec->y = val; + vec->x = val; +} + +void IChain_Apply_Vec3fdiv1000(u8* ptr, InitChainEntry* ichain) { + Vec3f* vec = (Vec3f*)(ptr + ichain->offset); + f32 val; + + osSyncPrintf("pp=%x data=%f\n", vec, ichain->value / 1000.0f); + val = ichain->value / 1000.0f; + + vec->z = val; + vec->y = val; + vec->x = val; +} + +void IChain_Apply_Vec3s(u8* ptr, InitChainEntry* ichain) { + Vec3s* vec = (Vec3s*)(ptr + ichain->offset); + s16 val = ichain->value; + + vec->z = val; + vec->y = val; + vec->x = val; +} + +/** + * Changes pValue by step towards target. If this step is more than fraction of the remaining distance, step by that + * instead, with a minimum step of minStep. Returns remaining distance to target. + */ +f32 Math_SmoothStepToF(f32* pValue, f32 target, f32 fraction, f32 step, f32 minStep) { + if (*pValue != target) { + f32 stepSize = (target - *pValue) * fraction; + + if ((stepSize >= minStep) || (stepSize <= -minStep)) { + if (stepSize > step) { + stepSize = step; + } + + if (stepSize < -step) { + stepSize = -step; + } + + *pValue += stepSize; + } else { + if (stepSize < minStep) { + *pValue += minStep; + stepSize = minStep; + + if (target < *pValue) { + *pValue = target; + } + } + if (stepSize > -minStep) { + *pValue += -minStep; + + if (*pValue < target) { + *pValue = target; + } + } + } + } + + return fabsf(target - *pValue); +} + +/** + * Changes pValue by step towards target. If step is more than fraction of the remaining distance, step by that instead. + */ +void Math_ApproachF(f32* pValue, f32 target, f32 fraction, f32 step) { + if (*pValue != target) { + f32 stepSize = (target - *pValue) * fraction; + + if (stepSize > step) { + stepSize = step; + } else if (stepSize < -step) { + stepSize = -step; + } + + *pValue += stepSize; + } +} + +/** + * Changes pValue by step towards zero. If step is more than fraction of the remaining distance, step by that instead. + */ +void Math_ApproachZeroF(f32* pValue, f32 fraction, f32 step) { + f32 stepSize = *pValue * fraction; + + if (stepSize > step) { + stepSize = step; + } else if (stepSize < -step) { + stepSize = -step; + } + + *pValue -= stepSize; +} + +/** + * Changes pValue by step towards target angle in degrees. If this step is more than fraction of the remaining distance, + * step by that instead, with a minimum step of minStep. Returns the value of the step taken. + */ +f32 Math_SmoothStepToDegF(f32* pValue, f32 target, f32 fraction, f32 step, f32 minStep) { + f32 stepSize = 0.0f; + f32 diff = target - *pValue; + + if (*pValue != target) { + if (diff > 180.0f) { + diff = -(360.0f - diff); + } else if (diff < -180.0f) { + diff = 360.0f + diff; + } + + stepSize = diff * fraction; + + if ((stepSize >= minStep) || (stepSize <= -minStep)) { + if (stepSize > step) { + stepSize = step; + } + + if (stepSize < -step) { + stepSize = -step; + } + + *pValue += stepSize; + } else { + if (stepSize < minStep) { + stepSize = minStep; + *pValue += stepSize; + if (*pValue > target) { + *pValue = target; + } + } + if (stepSize > -minStep) { + stepSize = -minStep; + *pValue += stepSize; + if (*pValue < target) { + *pValue = target; + } + } + } + } + + if (*pValue >= 360.0f) { + *pValue -= 360.0f; + } + + if (*pValue < 0.0f) { + *pValue += 360.0f; + } + + return stepSize; +} + +/** + * Changes pValue by step towards target. If this step is more than 1/scale of the remaining distance, step by that + * instead, with a minimum step of minStep. Returns remaining distance to target. + */ +s16 Math_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step, s16 minStep) { + s16 stepSize = 0; + s16 diff = target - *pValue; + + if (*pValue != target) { + stepSize = diff / scale; + + if ((stepSize > minStep) || (stepSize < -minStep)) { + if (stepSize > step) { + stepSize = step; + } + + if (stepSize < -step) { + stepSize = -step; + } + + *pValue += stepSize; + } else { + if (diff >= 0) { + *pValue += minStep; + + if ((s16)(target - *pValue) <= 0) { + *pValue = target; + } + } else { + *pValue -= minStep; + + if ((s16)(target - *pValue) >= 0) { + *pValue = target; + } + } + } + } + + return diff; +} + +/** + * Changes pValue by step towards target. If step is more than 1/scale of the remaining distance, step by that instead. + */ +void Math_ApproachS(s16* pValue, s16 target, s16 scale, s16 maxStep) { + s16 diff = target - *pValue; + + diff /= scale; + + if (diff > maxStep) { + *pValue += maxStep; + } else if (diff < -maxStep) { + *pValue -= maxStep; + } else { + *pValue += diff; + } +} + +void Color_RGBA8_Copy(Color_RGBA8* dst, Color_RGBA8* src) { + dst->r = src->r; + dst->g = src->g; + dst->b = src->b; + dst->a = src->a; +} + +void func_80078884(u16 sfxId) { + Audio_PlaySoundGeneral(sfxId, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void func_800788CC(u16 sfxId) { + Audio_PlaySoundGeneral(sfxId, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void func_80078914(Vec3f* arg0, u16 sfxId) { + Audio_PlaySoundGeneral(sfxId, arg0, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c new file mode 100644 index 000000000..344022505 --- /dev/null +++ b/soh/src/code/z_lifemeter.c @@ -0,0 +1,533 @@ +#include "global.h" +#include "textures/parameter_static/parameter_static.h" + +static s16 sHeartsPrimColors[3][3] = { + { HEARTS_PRIM_R, HEARTS_PRIM_G, HEARTS_PRIM_B }, + { HEARTS_BURN_PRIM_R, HEARTS_BURN_PRIM_G, HEARTS_BURN_PRIM_B }, // unused + { HEARTS_DROWN_PRIM_R, HEARTS_DROWN_PRIM_G, HEARTS_DROWN_PRIM_B }, // unused +}; + +static s16 sHeartsEnvColors[3][3] = { + { HEARTS_ENV_R, HEARTS_ENV_G, HEARTS_ENV_B }, + { HEARTS_BURN_ENV_R, HEARTS_BURN_ENV_G }, // unused + { HEARTS_DROWN_ENV_R, HEARTS_DROWN_ENV_G, HEARTS_DROWN_ENV_B }, // unused +}; + +static s16 sHeartsPrimFactors[3][3] = { + { + HEARTS_PRIM_R - HEARTS_PRIM_R, + HEARTS_PRIM_G - HEARTS_PRIM_G, + HEARTS_PRIM_B - HEARTS_PRIM_B, + }, + // unused + { + HEARTS_BURN_PRIM_R - HEARTS_PRIM_R, + HEARTS_BURN_PRIM_G - HEARTS_PRIM_G, + HEARTS_BURN_PRIM_B - HEARTS_PRIM_B, + }, + // unused + { + HEARTS_DROWN_PRIM_R - HEARTS_PRIM_R, + HEARTS_DROWN_PRIM_G - HEARTS_PRIM_G, + HEARTS_DROWN_PRIM_B - HEARTS_PRIM_B, + }, +}; + +static s16 sHeartsEnvFactors[3][3] = { + { + HEARTS_ENV_R - HEARTS_ENV_R, + HEARTS_ENV_G - HEARTS_ENV_G, + HEARTS_ENV_B - HEARTS_ENV_B, + }, + // unused + { + HEARTS_BURN_ENV_R - HEARTS_ENV_R, + HEARTS_BURN_ENV_G - HEARTS_ENV_G, + HEARTS_BURN_ENV_B - HEARTS_ENV_B, + }, + // unused + { + HEARTS_DROWN_ENV_R - HEARTS_ENV_R, + HEARTS_DROWN_ENV_G - HEARTS_ENV_G, + HEARTS_DROWN_ENV_B - HEARTS_ENV_B, + }, +}; + +static s16 sHeartsDDPrimColors[3][3] = { + { HEARTS_DD_PRIM_R, HEARTS_DD_PRIM_G, HEARTS_DD_PRIM_B }, + { HEARTS_BURN_PRIM_R, HEARTS_BURN_PRIM_G, HEARTS_BURN_PRIM_B }, // unused + { HEARTS_DROWN_PRIM_R, HEARTS_DROWN_PRIM_G, HEARTS_DROWN_PRIM_B }, // unused +}; + +static s16 sHeartsDDEnvColors[3][3] = { + { HEARTS_DD_ENV_R, HEARTS_DD_ENV_G, HEARTS_DD_ENV_B }, + { HEARTS_BURN_ENV_R, HEARTS_BURN_ENV_G, HEARTS_BURN_ENV_B }, // unused + { HEARTS_DROWN_ENV_R, HEARTS_DROWN_ENV_G, HEARTS_DROWN_ENV_B }, // unused +}; + +static s16 sHeartsDDPrimFactors[3][3] = { + { + HEARTS_DD_PRIM_R - HEARTS_DD_PRIM_R, + HEARTS_DD_PRIM_G - HEARTS_DD_PRIM_G, + HEARTS_DD_PRIM_B - HEARTS_DD_PRIM_B, + }, + // unused + { + HEARTS_BURN_PRIM_R - HEARTS_DD_PRIM_R, + HEARTS_BURN_PRIM_G - HEARTS_DD_PRIM_G, + HEARTS_BURN_PRIM_B - HEARTS_DD_PRIM_B, + }, + // unused + { + HEARTS_DROWN_PRIM_R - HEARTS_DD_PRIM_R, + HEARTS_DROWN_PRIM_G - HEARTS_DD_PRIM_G, + HEARTS_DROWN_PRIM_B - HEARTS_DD_PRIM_B, + }, +}; + +static s16 sHeartsDDEnvFactors[3][3] = { + { + HEARTS_DD_ENV_R - HEARTS_DD_ENV_R, + HEARTS_DD_ENV_G - HEARTS_DD_ENV_G, + HEARTS_DD_ENV_B - HEARTS_DD_ENV_B, + }, + // unused + { + HEARTS_BURN_ENV_R - HEARTS_DD_ENV_R, + HEARTS_BURN_ENV_G - HEARTS_DD_ENV_G, + HEARTS_BURN_ENV_B - HEARTS_DD_ENV_B, + }, + // unused + { + HEARTS_DROWN_ENV_R - HEARTS_DD_ENV_R, + HEARTS_DROWN_ENV_G - HEARTS_DD_ENV_G, + HEARTS_DROWN_ENV_B - HEARTS_DD_ENV_B, + }, +}; + +// Current colors for the double defense hearts +s16 sBeatingHeartsDDPrim[3]; +s16 sBeatingHeartsDDEnv[3]; +s16 sHeartsDDPrim[2][3]; +s16 sHeartsDDEnv[2][3]; + +void HealthMeter_Init(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + interfaceCtx->unk_228 = 0x140; + interfaceCtx->unk_226 = gSaveContext.health; + interfaceCtx->unk_22A = interfaceCtx->unk_1FE = 0; + interfaceCtx->unk_22C = interfaceCtx->unk_200 = 0; + + interfaceCtx->heartsPrimR[0] = HEARTS_PRIM_R; + interfaceCtx->heartsPrimG[0] = HEARTS_PRIM_G; + interfaceCtx->heartsPrimB[0] = HEARTS_PRIM_B; + + interfaceCtx->heartsEnvR[0] = HEARTS_ENV_R; + interfaceCtx->heartsEnvG[0] = HEARTS_ENV_G; + interfaceCtx->heartsEnvB[0] = HEARTS_ENV_B; + + interfaceCtx->heartsPrimR[1] = HEARTS_PRIM_R; + interfaceCtx->heartsPrimG[1] = HEARTS_PRIM_G; + interfaceCtx->heartsPrimB[1] = HEARTS_PRIM_B; + + interfaceCtx->heartsEnvR[1] = HEARTS_ENV_R; + interfaceCtx->heartsEnvG[1] = HEARTS_ENV_G; + interfaceCtx->heartsEnvB[1] = HEARTS_ENV_B; + + sHeartsDDPrim[0][0] = sHeartsDDPrim[1][0] = HEARTS_DD_PRIM_R; + sHeartsDDPrim[0][1] = sHeartsDDPrim[1][1] = HEARTS_DD_PRIM_G; + sHeartsDDPrim[0][2] = sHeartsDDPrim[1][2] = HEARTS_DD_PRIM_B; + + sHeartsDDEnv[0][0] = sHeartsDDEnv[1][0] = HEARTS_DD_ENV_R; + sHeartsDDEnv[0][1] = sHeartsDDEnv[1][1] = HEARTS_DD_ENV_G; + sHeartsDDEnv[0][2] = sHeartsDDEnv[1][2] = HEARTS_DD_ENV_B; +} + +void HealthMeter_Update(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + f32 factor = interfaceCtx->unk_1FE * 0.1f; + f32 ddFactor; + s32 type = 0; + s32 ddType; + s16 rFactor; + s16 gFactor; + s16 bFactor; + + if (interfaceCtx) {} + + if (interfaceCtx->unk_200 != 0) { + interfaceCtx->unk_1FE--; + if (interfaceCtx->unk_1FE <= 0) { + interfaceCtx->unk_1FE = 0; + interfaceCtx->unk_200 = 0; + } + } else { + interfaceCtx->unk_1FE++; + if (interfaceCtx->unk_1FE >= 10) { + interfaceCtx->unk_1FE = 10; + interfaceCtx->unk_200 = 1; + } + } + + ddFactor = factor; + + interfaceCtx->heartsPrimR[0] = HEARTS_PRIM_R; + interfaceCtx->heartsPrimG[0] = HEARTS_PRIM_G; + interfaceCtx->heartsPrimB[0] = HEARTS_PRIM_B; + + interfaceCtx->heartsEnvR[0] = HEARTS_ENV_R; + interfaceCtx->heartsEnvG[0] = HEARTS_ENV_G; + interfaceCtx->heartsEnvB[0] = HEARTS_ENV_B; + + interfaceCtx->heartsPrimR[1] = sHeartsPrimColors[type][0]; + interfaceCtx->heartsPrimG[1] = sHeartsPrimColors[type][1]; + interfaceCtx->heartsPrimB[1] = sHeartsPrimColors[type][2]; + + interfaceCtx->heartsEnvR[1] = sHeartsEnvColors[type][0]; + interfaceCtx->heartsEnvG[1] = sHeartsEnvColors[type][1]; + interfaceCtx->heartsEnvB[1] = sHeartsEnvColors[type][2]; + + rFactor = sHeartsPrimFactors[0][0] * factor; + gFactor = sHeartsPrimFactors[0][1] * factor; + bFactor = sHeartsPrimFactors[0][2] * factor; + + interfaceCtx->beatingHeartPrim[0] = (u8)(rFactor + HEARTS_PRIM_R) & 0xFF; + interfaceCtx->beatingHeartPrim[1] = (u8)(gFactor + HEARTS_PRIM_G) & 0xFF; + interfaceCtx->beatingHeartPrim[2] = (u8)(bFactor + HEARTS_PRIM_B) & 0xFF; + + rFactor = sHeartsEnvFactors[0][0] * factor; + gFactor = sHeartsEnvFactors[0][1] * factor; + bFactor = sHeartsEnvFactors[0][2] * factor; + + if (1) {} + ddType = type; + + interfaceCtx->beatingHeartEnv[0] = (u8)(rFactor + HEARTS_ENV_R) & 0xFF; + interfaceCtx->beatingHeartEnv[1] = (u8)(gFactor + HEARTS_ENV_G) & 0xFF; + interfaceCtx->beatingHeartEnv[2] = (u8)(bFactor + HEARTS_ENV_B) & 0xFF; + + sHeartsDDPrim[0][0] = HEARTS_DD_PRIM_R; + sHeartsDDPrim[0][1] = HEARTS_DD_PRIM_G; + sHeartsDDPrim[0][2] = HEARTS_DD_PRIM_B; + + sHeartsDDEnv[0][0] = HEARTS_DD_ENV_R; + sHeartsDDEnv[0][1] = HEARTS_DD_ENV_G; + sHeartsDDEnv[0][2] = HEARTS_DD_ENV_B; + + sHeartsDDPrim[1][0] = sHeartsDDPrimColors[ddType][0]; + sHeartsDDPrim[1][1] = sHeartsDDPrimColors[ddType][1]; + sHeartsDDPrim[1][2] = sHeartsDDPrimColors[ddType][2]; + + sHeartsDDEnv[1][0] = sHeartsDDEnvColors[ddType][0]; + sHeartsDDEnv[1][1] = sHeartsDDEnvColors[ddType][1]; + sHeartsDDEnv[1][2] = sHeartsDDEnvColors[ddType][2]; + + rFactor = sHeartsDDPrimFactors[ddType][0] * ddFactor; + gFactor = sHeartsDDPrimFactors[ddType][1] * ddFactor; + bFactor = sHeartsDDPrimFactors[ddType][2] * ddFactor; + + sBeatingHeartsDDPrim[0] = (u8)(rFactor + HEARTS_DD_PRIM_R) & 0xFF; + sBeatingHeartsDDPrim[1] = (u8)(gFactor + HEARTS_DD_PRIM_G) & 0xFF; + sBeatingHeartsDDPrim[2] = (u8)(bFactor + HEARTS_DD_PRIM_B) & 0xFF; + + rFactor = sHeartsDDEnvFactors[ddType][0] * ddFactor; + gFactor = sHeartsDDEnvFactors[ddType][1] * ddFactor; + bFactor = sHeartsDDEnvFactors[ddType][2] * ddFactor; + + sBeatingHeartsDDEnv[0] = (u8)(rFactor + HEARTS_DD_ENV_R) & 0xFF; + sBeatingHeartsDDEnv[1] = (u8)(gFactor + HEARTS_DD_ENV_G) & 0xFF; + sBeatingHeartsDDEnv[2] = (u8)(bFactor + HEARTS_DD_ENV_B) & 0xFF; +} + +s32 func_80078E18(GlobalContext* globalCtx) { + gSaveContext.health = globalCtx->interfaceCtx.unk_226; + return 1; +} + +s32 func_80078E34(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + interfaceCtx->unk_228 = 0x140; + interfaceCtx->unk_226 += 0x10; + + if (interfaceCtx->unk_226 >= gSaveContext.health) { + interfaceCtx->unk_226 = gSaveContext.health; + return 1; + } + + return 0; +} + +s32 func_80078E84(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + if (interfaceCtx->unk_228 != 0) { + interfaceCtx->unk_228--; + } else { + interfaceCtx->unk_228 = 0x140; + interfaceCtx->unk_226 -= 0x10; + if (interfaceCtx->unk_226 <= 0) { + interfaceCtx->unk_226 = 0; + globalCtx->damagePlayer(globalCtx, -(gSaveContext.health + 1)); + return 1; + } + } + return 0; +} + +static void* sHeartTextures[] = { + gHeartFullTex, gHeartQuarterTex, gHeartQuarterTex, gHeartQuarterTex, + gHeartQuarterTex, gHeartQuarterTex, gHeartHalfTex, gHeartHalfTex, + gHeartHalfTex, gHeartHalfTex, gHeartHalfTex, gHeartThreeQuarterTex, + gHeartThreeQuarterTex, gHeartThreeQuarterTex, gHeartThreeQuarterTex, gHeartThreeQuarterTex, +}; + +static void* sHeartDDTextures[] = { + gDefenseHeartFullTex, gDefenseHeartQuarterTex, gDefenseHeartQuarterTex, + gDefenseHeartQuarterTex, gDefenseHeartQuarterTex, gDefenseHeartQuarterTex, + gDefenseHeartHalfTex, gDefenseHeartHalfTex, gDefenseHeartHalfTex, + gDefenseHeartHalfTex, gDefenseHeartHalfTex, gDefenseHeartThreeQuarterTex, + gDefenseHeartThreeQuarterTex, gDefenseHeartThreeQuarterTex, gDefenseHeartThreeQuarterTex, + gDefenseHeartThreeQuarterTex, +}; + +void HealthMeter_Draw(GlobalContext* globalCtx) { + s32 pad[5]; + void* heartBgImg; + u32 curColorSet; + f32 offsetX; + f32 offsetY; + s32 i; + f32 temp1; + f32 temp2; + f32 temp3; + f32 temp4; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + Vtx* sp154 = interfaceCtx->beatingHeartVtx; + s32 curHeartFraction = gSaveContext.health % 0x10; + s16 totalHeartCount = gSaveContext.healthCapacity / 0x10; + s16 fullHeartCount = gSaveContext.health / 0x10; + s32 pad2; + f32 sp144 = interfaceCtx->unk_22A * 0.1f; + s32 curCombineModeSet = 0; + u8* curBgImgLoaded = NULL; + s32 ddHeartCountMinusOne = gSaveContext.inventory.defenseHearts - 1; + + OPEN_DISPS(gfxCtx, "../z_lifemeter.c", 353); + + if (!(gSaveContext.health % 0x10)) { + fullHeartCount--; + } + + curColorSet = -1; + offsetY = 0.0f; + offsetX = OTRGetDimensionFromLeftEdge(0.0f); + + for (i = 0; i < totalHeartCount; i++) { + if ((ddHeartCountMinusOne < 0) || (i > ddHeartCountMinusOne)) { + if (i < fullHeartCount) { + if (curColorSet != 0) { + curColorSet = 0; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->heartsPrimR[0], interfaceCtx->heartsPrimG[0], + interfaceCtx->heartsPrimB[0], interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->heartsEnvR[0], interfaceCtx->heartsEnvG[0], + interfaceCtx->heartsEnvB[0], 255); + } + } else if (i == fullHeartCount) { + if (curColorSet != 1) { + curColorSet = 1; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->beatingHeartPrim[0], + interfaceCtx->beatingHeartPrim[1], interfaceCtx->beatingHeartPrim[2], + interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->beatingHeartEnv[0], interfaceCtx->beatingHeartEnv[1], + interfaceCtx->beatingHeartEnv[2], 255); + } + } else if (i > fullHeartCount) { + if (curColorSet != 2) { + curColorSet = 2; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->heartsPrimR[0], interfaceCtx->heartsPrimG[0], + interfaceCtx->heartsPrimB[0], interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->heartsEnvR[0], interfaceCtx->heartsEnvG[0], + interfaceCtx->heartsEnvB[0], 255); + } + } else { + if (curColorSet != 3) { + curColorSet = 3; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->heartsPrimR[1], interfaceCtx->heartsPrimG[1], + interfaceCtx->heartsPrimB[1], interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->heartsEnvR[1], interfaceCtx->heartsEnvG[1], + interfaceCtx->heartsEnvB[1], 255); + } + } + + if (i < fullHeartCount) { + heartBgImg = gHeartFullTex; + } else if (i == fullHeartCount) { + heartBgImg = sHeartTextures[curHeartFraction]; + } else { + heartBgImg = gHeartEmptyTex; + } + } else { + if (i < fullHeartCount) { + if (curColorSet != 4) { + curColorSet = 4; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, sHeartsDDPrim[0][0], sHeartsDDPrim[0][1], sHeartsDDPrim[0][2], + interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, sHeartsDDEnv[0][0], sHeartsDDEnv[0][1], sHeartsDDEnv[0][2], 255); + } + } else if (i == fullHeartCount) { + if (curColorSet != 5) { + curColorSet = 5; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, sBeatingHeartsDDPrim[0], sBeatingHeartsDDPrim[1], + sBeatingHeartsDDPrim[2], interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, sBeatingHeartsDDEnv[0], sBeatingHeartsDDEnv[1], + sBeatingHeartsDDEnv[2], 255); + } + } else if (i > fullHeartCount) { + if (curColorSet != 6) { + curColorSet = 6; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, sHeartsDDPrim[0][0], sHeartsDDPrim[0][1], sHeartsDDPrim[0][2], + interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, sHeartsDDEnv[0][0], sHeartsDDEnv[0][1], sHeartsDDEnv[0][2], 255); + } + } else { + if (curColorSet != 7) { + curColorSet = 7; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, sHeartsDDPrim[1][0], sHeartsDDPrim[1][1], sHeartsDDPrim[1][2], + interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, sHeartsDDEnv[1][0], sHeartsDDEnv[1][1], sHeartsDDEnv[1][2], 255); + } + } + + if (i < fullHeartCount) { + heartBgImg = gDefenseHeartFullTex; + } else if (i == fullHeartCount) { + heartBgImg = sHeartDDTextures[curHeartFraction]; + } else { + heartBgImg = gDefenseHeartEmptyTex; + } + } + + if (curBgImgLoaded != heartBgImg) { + curBgImgLoaded = heartBgImg; + gDPLoadTextureBlock(OVERLAY_DISP++, heartBgImg, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } + + if (i != fullHeartCount) { + if ((ddHeartCountMinusOne < 0) || (i > ddHeartCountMinusOne)) { + if (curCombineModeSet != 1) { + curCombineModeSet = 1; + func_80094520(gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + } + } else { + if (curCombineModeSet != 3) { + curCombineModeSet = 3; + func_80094520(gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0, ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0); + } + } + + temp3 = 26.0f + offsetY; + temp2 = 30.0f + offsetX; + temp4 = 1.0f; + temp4 /= 0.68f; + temp4 *= 1 << 10; + temp1 = 8.0f; + temp1 *= 0.68f; + gSPWideTextureRectangle(OVERLAY_DISP++, (s32)((temp2 - temp1) * 4), (s32)((temp3 - temp1) * 4), + (s32)((temp2 + temp1) * 4), (s32)((temp3 + temp1) * 4), G_TX_RENDERTILE, 0, 0, + (s32)temp4, (s32)temp4); + } else { + if ((ddHeartCountMinusOne < 0) || (i > ddHeartCountMinusOne)) { + if (curCombineModeSet != 2) { + curCombineModeSet = 2; + func_80094A14(gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + } + } else { + if (curCombineModeSet != 4) { + curCombineModeSet = 4; + func_80094A14(gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0, ENVIRONMENT, PRIMITIVE, TEXEL0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0); + } + } + + { + Mtx* matrix = Graph_Alloc(gfxCtx, sizeof(Mtx)); + Matrix_SetTranslateScaleMtx2(matrix, 1.0f - (0.32f * sp144), 1.0f - (0.32f * sp144), + 1.0f - (0.32f * sp144), -130.0f + offsetX, + 94.5f - offsetY, 0.0f); + gSPMatrix(OVERLAY_DISP++, matrix, G_MTX_MODELVIEW | G_MTX_LOAD); + gSPVertex(OVERLAY_DISP++, sp154, 4, 0); + gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0); + } + } + + offsetX += 10.0f; + if (i == 9) { + offsetY += 10.0f; + offsetX = OTRGetDimensionFromLeftEdge(0.0f); + } + } + + CLOSE_DISPS(gfxCtx, "../z_lifemeter.c", 606); +} + +void HealthMeter_HandleCriticalAlarm(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + if (interfaceCtx->unk_22C != 0) { + interfaceCtx->unk_22A--; + if (interfaceCtx->unk_22A <= 0) { + interfaceCtx->unk_22A = 0; + interfaceCtx->unk_22C = 0; + if (!Player_InCsMode(globalCtx) && (globalCtx->pauseCtx.state == 0) && + (globalCtx->pauseCtx.debugState == 0) && HealthMeter_IsCritical() && !Gameplay_InCsMode(globalCtx)) { + func_80078884(NA_SE_SY_HITPOINT_ALARM); + } + } + } else { + interfaceCtx->unk_22A++; + if (interfaceCtx->unk_22A >= 10) { + interfaceCtx->unk_22A = 10; + interfaceCtx->unk_22C = 1; + } + } +} + +u32 HealthMeter_IsCritical(void) { + s32 var; + + if (gSaveContext.healthCapacity <= 0x50) { + var = 0x10; + } else if (gSaveContext.healthCapacity <= 0xA0) { + var = 0x18; + } else if (gSaveContext.healthCapacity <= 0xF0) { + var = 0x20; + } else { + var = 0x2C; + } + + if ((var >= gSaveContext.health) && (gSaveContext.health > 0)) { + return true; + } else { + return false; + } +} diff --git a/soh/src/code/z_lights.c b/soh/src/code/z_lights.c new file mode 100644 index 000000000..2c7140c73 --- /dev/null +++ b/soh/src/code/z_lights.c @@ -0,0 +1,411 @@ +#include "global.h" + +#include + +#include "objects/gameplay_keep/gameplay_keep.h" + +#define LIGHTS_BUFFER_SIZE 32 +//#define LIGHTS_BUFFER_SIZE 1024 // Kill me + +typedef struct { + /* 0x000 */ s32 numOccupied; + /* 0x004 */ s32 searchIndex; + /* 0x008 */ LightNode buf[LIGHTS_BUFFER_SIZE]; +} LightsBuffer; // size = 0x188 + +LightsBuffer sLightsBuffer; + +void Lights_PointSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius, s32 type) { + info->type = type; + info->params.point.x = x; + info->params.point.y = y; + info->params.point.z = z; + Lights_PointSetColorAndRadius(info, r, g, b, radius); +} + +void Lights_PointNoGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius) { + Lights_PointSetInfo(info, x, y, z, r, g, b, radius, LIGHT_POINT_NOGLOW); +} + +void Lights_PointGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius) { + Lights_PointSetInfo(info, x, y, z, r, g, b, radius, LIGHT_POINT_GLOW); +} + +void Lights_PointSetColorAndRadius(LightInfo* info, u8 r, u8 g, u8 b, s16 radius) { + info->params.point.color[0] = r; + info->params.point.color[1] = g; + info->params.point.color[2] = b; + info->params.point.radius = radius; +} + +void Lights_DirectionalSetInfo(LightInfo* info, s8 x, s8 y, s8 z, u8 r, u8 g, u8 b) { + info->type = LIGHT_DIRECTIONAL; + info->params.dir.x = x; + info->params.dir.y = y; + info->params.dir.z = z; + info->params.dir.color[0] = r; + info->params.dir.color[1] = g; + info->params.dir.color[2] = b; +} + +// unused +void Lights_Reset(Lights* lights, u8 ambentR, u8 ambentG, u8 ambentB) { + lights->l.a.l.col[0] = lights->l.a.l.colc[0] = ambentR; + lights->l.a.l.col[1] = lights->l.a.l.colc[1] = ambentG; + lights->l.a.l.col[2] = lights->l.a.l.colc[2] = ambentB; + lights->numLights = 0; +} + +/* + * Draws every light in the provided Lights group + */ +void Lights_Draw(Lights* lights, GraphicsContext* gfxCtx) { + Light* light; + s32 i; + +#if 1 + + OPEN_DISPS(gfxCtx, "../z_lights.c", 339); + + gSPNumLights(POLY_OPA_DISP++, lights->numLights); + gSPNumLights(POLY_XLU_DISP++, lights->numLights); + + i = 0; + light = &lights->l.l[0]; + + while (i < lights->numLights) { + i++; + gSPLight(POLY_OPA_DISP++, light, i); + gSPLight(POLY_XLU_DISP++, light, i); + light++; + } + + if (0) {} + + i++; // abmient light is total number of lights + 1 + gSPLight(POLY_OPA_DISP++, &lights->l.a, i); + gSPLight(POLY_XLU_DISP++, &lights->l.a, i); + + CLOSE_DISPS(gfxCtx, "../z_lights.c", 352); +#endif +} + +Light* Lights_FindSlot(Lights* lights) { + if (lights->numLights >= 7) { + return NULL; + } else { + return &lights->l.l[lights->numLights++]; + } +} + +void Lights_BindPoint(Lights* lights, LightParams* params, Vec3f* vec) { + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 posDiff; + f32 scale; + Light* light; + + if (vec != NULL) { + xDiff = params->point.x - vec->x; + yDiff = params->point.y - vec->y; + zDiff = params->point.z - vec->z; + scale = params->point.radius; + posDiff = SQ(xDiff) + SQ(yDiff) + SQ(zDiff); + + if (posDiff < SQ(scale)) { + light = Lights_FindSlot(lights); + + if (light != NULL) { + posDiff = sqrtf(posDiff); + if (1) {} + scale = posDiff / scale; + scale = 1 - SQ(scale); + + light->l.col[0] = light->l.colc[0] = params->point.color[0] * scale; + light->l.col[1] = light->l.colc[1] = params->point.color[1] * scale; + light->l.col[2] = light->l.colc[2] = params->point.color[2] * scale; + + scale = (posDiff < 1.0f) ? 120.0f : 120.0f / posDiff; + + light->l.dir[0] = xDiff * scale; + light->l.dir[1] = yDiff * scale; + light->l.dir[2] = zDiff * scale; + } + } + } +} + +void Lights_BindDirectional(Lights* lights, LightParams* params, Vec3f* vec) { + Light* light = Lights_FindSlot(lights); + + if (light != NULL) { + light->l.col[0] = light->l.colc[0] = params->dir.color[0]; + light->l.col[1] = light->l.colc[1] = params->dir.color[1]; + light->l.col[2] = light->l.colc[2] = params->dir.color[2]; + light->l.dir[0] = params->dir.x; + light->l.dir[1] = params->dir.y; + light->l.dir[2] = params->dir.z; + } +} + +/** + * For every light in a provided list, try to find a free slot in the provided Lights group and bind + * a light to it. Then apply color and positional/directional info for each light + * based on the parameters supplied by the node. + * + * Note: Lights in a given list can only be binded to however many free slots are + * available in the Lights group. This is at most 7 slots for a new group, but could be less. + */ +void Lights_BindAll(Lights* lights, LightNode* listHead, Vec3f* vec) { + LightsBindFunc bindFuncs[] = { Lights_BindPoint, Lights_BindDirectional, Lights_BindPoint }; + LightInfo* info; + + while (listHead != NULL) { + info = listHead->info; + bindFuncs[info->type](lights, &info->params, vec); + listHead = listHead->next; + } +} + +LightNode* Lights_FindBufSlot() { + LightNode* node; + + if (sLightsBuffer.numOccupied >= LIGHTS_BUFFER_SIZE) { + return NULL; + } + + node = &sLightsBuffer.buf[sLightsBuffer.searchIndex]; + + while (node->info != NULL) { + sLightsBuffer.searchIndex++; + + if (sLightsBuffer.searchIndex < LIGHTS_BUFFER_SIZE) { + node++; + } else { + sLightsBuffer.searchIndex = 0; + node = &sLightsBuffer.buf[0]; + } + } + + sLightsBuffer.numOccupied++; + + return node; +} + +// return type must not be void to match +s32 Lights_FreeNode(LightNode* light) { + if (light != NULL) { + sLightsBuffer.numOccupied--; + light->info = NULL; + sLightsBuffer.searchIndex = (light - sLightsBuffer.buf) / sizeof(LightNode); + } +} + +void LightContext_Init(GlobalContext* globalCtx, LightContext* lightCtx) { + LightContext_InitList(globalCtx, lightCtx); + LightContext_SetAmbientColor(lightCtx, 80, 80, 80); + LightContext_SetFog(lightCtx, 0, 0, 0, 996, 12800); + memset(&sLightsBuffer, 0, sizeof(sLightsBuffer)); +} + +void LightContext_SetAmbientColor(LightContext* lightCtx, u8 r, u8 g, u8 b) { + lightCtx->ambientColor[0] = r; + lightCtx->ambientColor[1] = g; + lightCtx->ambientColor[2] = b; +} + +void LightContext_SetFog(LightContext* lightCtx, u8 r, u8 g, u8 b, s16 fogNear, s16 fogFar) { + lightCtx->fogColor[0] = r; + lightCtx->fogColor[1] = g; + lightCtx->fogColor[2] = b; + lightCtx->fogNear = fogNear; + lightCtx->fogFar = fogFar; +} + +/** + * Allocate a new Lights group and initilize the ambient color with that provided by LightContext + */ +Lights* LightContext_NewLights(LightContext* lightCtx, GraphicsContext* gfxCtx) { + return Lights_New(gfxCtx, lightCtx->ambientColor[0], lightCtx->ambientColor[1], lightCtx->ambientColor[2]); +} + +void LightContext_InitList(GlobalContext* globalCtx, LightContext* lightCtx) { + lightCtx->listHead = NULL; +} + +void LightContext_DestroyList(GlobalContext* globalCtx, LightContext* lightCtx) { + while (lightCtx->listHead != NULL) { + LightContext_RemoveLight(globalCtx, lightCtx, lightCtx->listHead); + lightCtx->listHead = lightCtx->listHead->next; + } +} + +/** + * Insert a new light into the list pointed to by LightContext + * + * Note: Due to the limited number of slots in a Lights group, inserting too many lights in the + * list may result in older entries not being bound to a Light when calling Lights_BindAll + */ +LightNode* LightContext_InsertLight(GlobalContext* globalCtx, LightContext* lightCtx, LightInfo* info) { + LightNode* node; + + node = Lights_FindBufSlot(); + + if (node != NULL) { + node->info = info; + node->prev = NULL; + node->next = lightCtx->listHead; + + if (lightCtx->listHead != NULL) { + lightCtx->listHead->prev = node; + } + + lightCtx->listHead = node; + } + + return node; +} + +void LightContext_RemoveLight(GlobalContext* globalCtx, LightContext* lightCtx, LightNode* node) { + if (node != NULL) { + if (node->prev != NULL) { + node->prev->next = node->next; + } else { + lightCtx->listHead = node->next; + } + + if (node->next != NULL) { + node->next->prev = node->prev; + } + + Lights_FreeNode(node); + } +} + +// unused +Lights* Lights_NewAndDraw(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB, u8 numLights, u8 r, u8 g, + u8 b, s8 x, s8 y, s8 z) { + Lights* lights; + s32 i; + + lights = Graph_Alloc(gfxCtx, sizeof(Lights)); + + lights->l.a.l.col[0] = lights->l.a.l.colc[0] = ambientR; + lights->l.a.l.col[1] = lights->l.a.l.colc[1] = ambientG; + lights->l.a.l.col[2] = lights->l.a.l.colc[2] = ambientB; + lights->numLights = numLights; + + for (i = 0; i < numLights; i++) { + lights->l.l[i].l.col[0] = lights->l.l[i].l.colc[0] = r; + lights->l.l[i].l.col[1] = lights->l.l[i].l.colc[1] = g; + lights->l.l[i].l.col[2] = lights->l.l[i].l.colc[2] = b; + lights->l.l[i].l.dir[0] = x; + lights->l.l[i].l.dir[1] = y; + lights->l.l[i].l.dir[2] = z; + } + + Lights_Draw(lights, gfxCtx); + + return lights; +} + +Lights* Lights_New(GraphicsContext* gfxCtx, u8 ambientR, u8 ambientG, u8 ambientB) { + Lights* lights; + + lights = Graph_Alloc(gfxCtx, sizeof(Lights)); + + lights->l.a.l.col[0] = lights->l.a.l.colc[0] = ambientR; + lights->l.a.l.col[1] = lights->l.a.l.colc[1] = ambientG; + lights->l.a.l.col[2] = lights->l.a.l.colc[2] = ambientB; + lights->numLights = 0; + + return lights; +} + +void Lights_GlowCheck(GlobalContext* globalCtx) { + LightNode* node; + LightPoint* params; + Vec3f pos; + Vec3f multDest; + f32 wDest; + f32 wX; + f32 wY; + s32 wZ; + s32 zBuf; + + node = globalCtx->lightCtx.listHead; + + while (node != NULL) { + params = &node->info->params.point; + + if (node->info->type == LIGHT_POINT_GLOW) { + f32 x, y; + u32 shrink; + uint32_t height; + + pos.x = params->x; + pos.y = params->y; + pos.z = params->z; + func_8002BE04(globalCtx, &pos, &multDest, &wDest); + params->drawGlow = false; + wX = multDest.x * wDest; + wY = multDest.y * wDest; + + x = wX * 160 + 160; + y = wY * 120 + 120; + shrink = ShrinkWindow_GetCurrentVal(); + + if ((multDest.z > 1.0f) && y >= shrink && y <= SCREEN_HEIGHT - shrink) { + wZ = (s32)((multDest.z * wDest) * 16352.0f) + 16352; + zBuf = OTRGetPixelDepth(x, y) * 4; + if (1) {} + if (1) {} + + if (wZ < (zBuf >> 3)) { + params->drawGlow = true; + } + } + } + node = node->next; + } +} + +void Lights_DrawGlow(GlobalContext* globalCtx) { + s32 pad; + LightNode* node; + + node = globalCtx->lightCtx.listHead; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_lights.c", 887); + + POLY_XLU_DISP = func_800947AC(POLY_XLU_DISP++); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_MAGICSQ); + gSPDisplayList(POLY_XLU_DISP++, gGlowCircleTextureLoadDL); + + while (node != NULL) { + LightInfo* info; + LightPoint* params; + f32 scale; + s32 pad[4]; + + info = node->info; + params = &info->params.point; + + if ((info->type == LIGHT_POINT_GLOW) && (params->drawGlow)) { + scale = SQ(params->radius) * 0.0000026f; + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, params->color[0], params->color[1], params->color[2], 50); + Matrix_Translate(params->x, params->y, params->z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_lights.c", 918), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gGlowCircleDL); + } + + node = node->next; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_lights.c", 927); +} diff --git a/soh/src/code/z_malloc.c b/soh/src/code/z_malloc.c new file mode 100644 index 000000000..acd4429c0 --- /dev/null +++ b/soh/src/code/z_malloc.c @@ -0,0 +1,110 @@ +#include "global.h" +#include + +#define LOG_SEVERITY_NOLOG 0 +#define LOG_SEVERITY_ERROR 2 +#define LOG_SEVERITY_VERBOSE 3 + +s32 gZeldaArenaLogSeverity = LOG_SEVERITY_ERROR; +Arena sZeldaArena; + +void ZeldaArena_CheckPointer(void* ptr, size_t size, const char* name, const char* action) { + if (ptr == NULL) { + if (gZeldaArenaLogSeverity >= LOG_SEVERITY_ERROR) { + // "%s: %u bytes %s failed\n" + osSyncPrintf("%s: %u バイトの%sに失敗しました\n", name, size, action); + __osDisplayArena(&sZeldaArena); + } + } else if (gZeldaArenaLogSeverity >= LOG_SEVERITY_VERBOSE) { + // "%s: %u bytes %s succeeded\n" + osSyncPrintf("%s: %u バイトの%sに成功しました\n", name, size, action); + } +} + +void* ZeldaArena_Malloc(size_t size) { + void* ptr = __osMalloc(&sZeldaArena, size); + + ZeldaArena_CheckPointer(ptr, size, "zelda_malloc", "確保"); // "Secure" + return ptr; +} + +void* ZeldaArena_MallocDebug(size_t size, const char* file, s32 line) { + void* ptr = __osMallocDebug(&sZeldaArena, size, file, line); + + ZeldaArena_CheckPointer(ptr, size, "zelda_malloc_DEBUG", "確保"); // "Secure" + return ptr; +} + +void* ZeldaArena_MallocR(size_t size) { + void* ptr = __osMallocR(&sZeldaArena, size); + + ZeldaArena_CheckPointer(ptr, size, "zelda_malloc_r", "確保"); // "Secure" + return ptr; +} + +void* ZeldaArena_MallocRDebug(size_t size, const char* file, s32 line) { + void* ptr = __osMallocRDebug(&sZeldaArena, size, file, line); + + ZeldaArena_CheckPointer(ptr, size, "zelda_malloc_r_DEBUG", "確保"); // "Secure" + return ptr; +} + +void* ZeldaArena_Realloc(void* ptr, size_t newSize) { + ptr = __osRealloc(&sZeldaArena, ptr, newSize); + ZeldaArena_CheckPointer(ptr, newSize, "zelda_realloc", "再確保"); // "Re-securing" + return ptr; +} + +void* ZeldaArena_ReallocDebug(void* ptr, size_t newSize, const char* file, s32 line) { + ptr = __osReallocDebug(&sZeldaArena, ptr, newSize, file, line); + ZeldaArena_CheckPointer(ptr, newSize, "zelda_realloc_DEBUG", "再確保"); // "Re-securing" + return ptr; +} + +void ZeldaArena_Free(void* ptr) { + __osFree(&sZeldaArena, ptr); +} + +void ZeldaArena_FreeDebug(void* ptr, const char* file, s32 line) { + __osFreeDebug(&sZeldaArena, ptr, file, line); +} + +void* ZeldaArena_Calloc(size_t num, size_t size) { + void* ret; + size_t n = num * size; + + ret = __osMalloc(&sZeldaArena, n); + if (ret != NULL) { + memset(ret, 0,n); + } + + ZeldaArena_CheckPointer(ret, n, "zelda_calloc", "確保"); + return ret; +} + +void ZeldaArena_Display() { + osSyncPrintf("ゼルダヒープ表示\n"); // "Zelda heap display" + __osDisplayArena(&sZeldaArena); +} + +void ZeldaArena_GetSizes(u32* outMaxFree, u32* outFree, u32* outAlloc) { + ArenaImpl_GetSizes(&sZeldaArena, outMaxFree, outFree, outAlloc); +} + +void ZeldaArena_Check() { + __osCheckArena(&sZeldaArena); +} + +void ZeldaArena_Init(void* start, size_t size) { + gZeldaArenaLogSeverity = LOG_SEVERITY_NOLOG; + __osMallocInit(&sZeldaArena, start, size); +} + +void ZeldaArena_Cleanup() { + gZeldaArenaLogSeverity = LOG_SEVERITY_NOLOG; + __osMallocCleanup(&sZeldaArena); +} + +u8 ZeldaArena_IsInitalized() { + return __osMallocIsInitalized(&sZeldaArena); +} diff --git a/soh/src/code/z_map_data.c b/soh/src/code/z_map_data.c new file mode 100644 index 000000000..f518a124c --- /dev/null +++ b/soh/src/code/z_map_data.c @@ -0,0 +1,332 @@ +#include "global.h" + +static s16 sFloorTexIndexOffset[10][8] = { + { 0, 0, 0, 0, 2, 4, 6, 8 }, { 0, 0, 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 2 }, { 0, 0, 0, 0, 0, 2, 4, 6 }, + { 0, 0, 0, 0, 2, 4, 6, 8 }, { 0, 0, 0, 0, 0, 2, 4, 6 }, + { 0, 0, 0, 0, 0, 2, 4, 6 }, { 0, 0, 0, 0, 0, 2, 4, 6 }, + { 0, 0, 0, 0, 0, 0, 2, 4 }, { 0 }, +}; + +static s16 sBossFloor[8] = { + 7, 7, 6, 7, 7, 4, 5, 7, +}; + +static s16 sRoomPalette[10][32] = { + { 10, 1, 2, 10, 4, 5, 6, 7, 8, 10, 11 }, + { 1, 3, 5, 6, 10, 3, 9, 2, 4, 2, 4, 7, 7, 8, 13, 11 }, + { 3, 1, 2, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 3 }, + { 9, 1, 2, 3, 6, 5, 6, 7, 8, 8, 5, 4, 3, 13, 11, 10, 11, 2, 13, 12, 10, 12, 13 }, + { 1, 6, 7, 2, 3, 4, 6, 10, 6, 10, 7, 9, 11, 1, 12, 3, 11, 4, 5, 8, 9, 10, 11, 12, 13, 2 }, + { 1, 2, 10, 3, 3, 4, 5, 13, 5, 6, 7, 8, 7, 9, 8, 9, 11, 10, 12, 11, 12, 13, 9, 7, 8, 12 }, + { 10, 1, 2, 3, 4, 5, 10, 7, 8, 9, 10, 6, 12, 13, 11, 8, 13, 1, 2, 3, 4, 11, 6, 12, 8, 5, 10, 9, 6 }, + { 13, 1, 5, 4, 4, 5, 3, 7, 8, 9, 10, 11, 12, 13, 2, 5, 1, 1, 6, 7, 8, 3, 9, 7, 8, 12 }, + { 13, 1, 2, 3, 4, 5, 6 }, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, +}; + +static s16 sMaxPaletteCount[10] = { + 6, 11, 12, 14, 11, 13, 13, 13, 7, 12, +}; + +static s16 sPaletteRoom[10][8][14] = { + { + { 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255 }, + { 0, 10, 255, 255, 255, 255 }, + { 0, 1, 2, 255, 255, 255 }, + { 0, 255, 255, 255, 255, 255 }, + { 3, 4, 5, 6, 7, 8 }, + { 9, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 2, 3, 5, 6, 9, 10, 12, 255, 255, 255 }, + { 0, 1, 2, 3, 4, 7, 8, 11, 13, 14, 15 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, + { 1, 3, 6, 13, 14, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 6, 7, 8, 10, 11, 12, 13, 14, 19, 20, 255, 255, 255 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 15, 16, 18, 21 }, + { 9, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 17, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 7, 8, 12, 13, 255, 255, 255, 255, 255, 255, 255 }, + { 5, 6, 7, 10, 11, 12, 13, 14, 24, 255, 255 }, + { 4, 5, 6, 9, 10, 11, 16, 23, 24, 25, 255 }, + { 4, 6, 10, 21, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 2, 3, 15, 17, 18, 19, 20, 21, 22 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 4, 5, 6, 7, 10, 11, 13, 17, 19, 20, 255 }, + { 0, 1, 4, 5, 6, 10, 17, 20, 21, 255, 255, 255, 255 }, + { 0, 1, 3, 5, 8, 9, 12, 14, 15, 16, 17, 18, 21 }, + { 0, 1, 2, 3, 5, 8, 9, 12, 14, 15, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 22, 23, 24, 25, 26, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 5, 7, 8, 9, 10, 11, 16, 17, 18, 19, 20, 21, 23 }, + { 4, 5, 6, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 2, 3, 4, 12, 13, 14, 15, 27, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 2, 4, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 5, 6, 7, 8, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 8, 9, 12, 14, 16, 21, 255, 255, 255, 255, 255, 255, 255 }, + { 3, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 2, 3, 4, 5, 6 }, + { 0, 1, 255, 255, 255, 255, 255 }, + { 1, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + }, +}; + +static s16 sRoomCompassOffsetX[10][44] = { + { 1090, 1390, 1560, 1220, 1200, 1390, 1770, 1610, 2000, 1290, 1420, + 1110, 1040, 470, 790, 1570, 720, 1000, 1580, 70, 0 }, + { 940, 320, 1500, 240, 580, 1510, 720, 1030, 800, 660, 180, 520, 310, 550, 790, 1650, 1000, 1570, 80, 70 }, + { 1130, 1070, 1090, 1160, 1500, 690, 1540, 920, 1160, 700, 1650, 950, 1380, 1460, 830, 1170, 1620 }, + { 1130, 1170, 965, 890, 1170, 1460, 1170, 800, 1320, 880, 1130, 1590, 1390, 830, 610, 580, 710, + 980, 1640, 1510, 590, 1610, 1130, 1130, 820, 1320, 1620, 0, 0, 0, 0, 1300, 1270 }, + { 1160, 620, 1330, 1280, 440, 600, 810, 830, 720, 1170, 1490, 1640, 1870, 1800, 1610, 1130, 860, 1310, 1140, + 850, 760, 380, 800, 800, 1930, 1410, 640, 845, 810, 810, 850, 1390, 1540, 1650, 1880, 1530, 420, 1950 }, + { 1120, 1290, 1120, 1380, 930, 1520, 1980, 2010, 1590, 1510, 1500, 1300, 1240, 1800, 1290, + 1450, 1560, 880, 820, 820, 1060, 1670, 1120, 1130, 1130, 1290, 1290, 1280, 1390, 940, + 1520, 1520, 1980, 1620, 1510, 1490, 1240, 1290, 1450, 880, 880, 1060, 1670, 1520 }, + { 800, 1500, 1370, 1730, 1590, 1020, 1060, 1470, 1600, 1830, 1630, 2000, 650, 660, 1020, 880, + 940, 720, 570, 620, 570, 550, 970, 920, 1040, 1150, 1200, 1550, 1520, 1020, 820, 1010 }, + { 1320, 1320, 1090, 1510, 1480, 940, 920, 910, 800, 820, 1150, 1000, 1800, 1660, + 1090, 1630, 710, 1670, 830, 770, 800, 850, 830, 820, 1800, 1090, 850 }, + { 1080, 1420, 1620, 1040, 940, 1190, 1310, 1090, 1380, 1080 }, + { 1070, 1180, 1270, 990, 1280, 1450, 1680, 1530, 760, 860, 1500, 800 }, +}; + +static s16 sRoomCompassOffsetY[10][44] = { + { -660, -570, -410, -690, -500, -380, -470, -630, -990, -870, + -720, -630, -660, -1280, -1910, -670, -1220, -870, -1070, -1080 }, + { -780, -800, -1090, -1230, -1140, -820, -1210, -1430, -1580, -920, + -830, -730, -950, -1580, -1910, -950, -860, -1070, -1070, -1080 }, + { -770, -1320, -1750, -1800, -1360, -1330, -1700, -2280, -2700, -2360, -2360, -2650, -2650, -1600, -1445, -1370, + -1910 }, + { -660, -900, -1040, -1210, -1520, -1190, -1670, -1320, -1310, -1260, -1500, + -1080, -1650, -1620, -1090, -1390, -1220, -1090, -1290, -1410, -1390, -1600, + -660, -1630, -1290, -1290, -1080, 0, 0, 0, 0, -1100, -1280 }, + { -580, -620, -700, -1150, -710, -670, -850, -1000, -720, -840, -870, -840, -790, + -1000, -980, -900, -660, -910, -1130, -1230, -1300, -810, -380, -1270, -820, -1120, + -880, -1070, -850, -850, -1060, -640, -900, -850, -890, -740, -880, -840 }, + { -610, -930, -770, -680, -1060, -930, -980, -1860, -1380, -1050, -830, -1230, -1380, -1330, -1750, + -1610, -1600, -830, -800, -820, -690, -1540, -620, -790, -780, -930, -930, -870, -690, -1050, + -930, -930, -980, -1370, -1050, -800, -1380, -1750, -1610, -840, -830, -690, -1540, -930 }, + { -570, -900, -1070, -1090, -1280, -1160, -920, -980, -780, -530, -350, -1000, -1440, -1080, -1100, -1110, + -980, -860, -790, -540, -350, -1000, -1250, -820, -1070, -1180, -950, -1380, -1160, -1100, -1150, -830 }, + { -940, -960, -860, -870, -920, -870, -980, -820, -860, -520, -500, -780, -1050, -1130, + -770, -1010, -680, -930, -880, -900, -980, -990, -770, -520, -1050, -770, -990 }, + { -570, -930, -1040, -1100, -800, -1100, -1100, -570, -930, -890 }, + { 100, -280, -690, -840, -1010, -950, -730, -470, -1130, -1440, -420, -700 }, +}; + +static u8 sDgnMinimapCount[12] = { + 13, 19, 17, 27, 38, 44, 32, 27, 10, 12, 0, 0, +}; + +static u16 sDgnMinimapTexIndexOffset[10] = { + 0, 13, 32, 49, 76, 114, 158, 190, 217, 227, +}; + +static u16 sOwMinimapTexSize[24] = { + 2920, 2560, 1560, 2784, 2976, 2040, 3240, 2336, 2080, 2600, 1792, 1888, + 3400, 1792, 1888, 2040, 3120, 2304, 2176, 1888, 1560, 3240, 2600, 3400, +}; + +static u16 sOwMinimapTexOffset[24] = { + 0x0000, 0x0B68, 0x1568, 0x1B80, 0x2660, 0x3200, 0x39F8, 0x46A0, 0x4FC0, 0x57E0, 0x6208, 0x6908, + 0x7068, 0x7DB0, 0x84B0, 0x8C10, 0x9408, 0xA038, 0xA938, 0xB1B8, 0xB918, 0xBF30, 0xCBD8, 0xD600, +}; + +static s16 sOwMinimapPosX[24] = { + 216, 216, 218, 202, 202, 250, 216, 234, 234, 216, 234, 234, + 216, 234, 234, 250, 216, 234, 234, 234, 218, 80, 80, 216, +}; + +static s16 sOwMinimapPosY[24] = { + 150, 158, 184, 164, 160, 138, 140, 150, 156, 158, 168, 162, + 138, 168, 162, 138, 146, 150, 154, 162, 218, 81, 65, 216, +}; + +static s16 sOwCompassInfo[24][4] = { + { 25, 25, 1080, -360 }, { 7, 6, 1000, -650 }, { 6, 6, 890, -800 }, { 7, 7, 720, -730 }, + { 8, 8, 660, -730 }, { 5, 7, 1220, -660 }, { 13, 13, 1080, -260 }, { 5, 5, 1120, -880 }, + { 8, 8, 1150, -630 }, { 11, 11, 1060, -680 }, { 12, 12, 1100, -720 }, { 11, 11, 930, -710 }, + { 9, 9, 850, -830 }, { 15, 15, 1100, -780 }, { 10, 10, 1030, -540 }, { 10, 9, 1240, -700 }, + { 6, 6, 1030, -620 }, { 5, 5, 1100, -660 }, { 11, 11, 1120, -750 }, { 10, 10, 1070, -580 }, + { 6, 6, 890, -800 }, { 13, 13, 1080, -260 }, { 11, 11, 1060, -680 }, { 9, 9, 850, -830 }, +}; + +static s16 sDgnMinimapTexIndexBase[10] = { + 0, 10, 14, 18, 26, 36, 44, 52, 60, 66, +}; + +static s16 sDgnCompassInfo[10][4] = { + { 3, 3, 1070, -690 }, { 4, 4, 1070, -690 }, { 3, 3, 1070, -690 }, { 4, 4, 1070, -690 }, { 4, 4, 1070, -690 }, + { 4, 4, 900, -640 }, { 3, 3, 900, -640 }, { 10, 10, 900, -640 }, { 5, 5, 900, -640 }, { 3, 3, 900, -640 }, +}; + +static s16 sOwMinimapWidth[24] = { + 80, 80, 80, 96, 96, 48, 80, 64, 64, 80, 64, 64, 80, 64, 64, 48, 80, 64, 64, 64, 80, 80, 80, 80, +}; + +static s16 sOwMinimapHeight[24] = { + 73, 64, 39, 58, 62, 85, 81, 73, 65, 65, 56, 59, 85, 56, 59, 85, 78, 72, 68, 59, 39, 81, 65, 85, +}; + +static s16 sOwEntranceIconPosX[24] = { + 1, 269, 1, 1, 273, 279, 259, 1, 260, 1, 1, 235, 1, 1, 1, 267, 261, 1, 1, 260, 294, 259, 1, 243, +}; + +static s16 sOwEntranceIconPosY[24] = { + 0, -833, 0, 0, -850, -889, -829, 0, -844, 0, 0, -836, 0, 0, 0, -852, -873, 0, 0, -848, -825, -829, 0, -833, +}; + +static u16 sOwEntranceFlag[20] = { + 0xFFFF, 0x0008, 0x0007, 0xFFFF, 0x0000, 0x0003, 0x0005, 0xFFFF, 0x0002, 0xFFFF, + 0xFFFF, 0x0006, 0x000B, 0xFFFF, 0xFFFF, 0x0001, 0x0004, 0xFFFF, 0xFFFF, 0x000D, +}; + +static f32 sFloorCoordY[10][8] = { + { 9999.0f, 9999.0f, 9999.0f, 760.0f, 360.0f, -40.0f, -1000.0f, -2000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, 280.0f, -440.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, -640.0f, -3000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 760.0f, -40.0f, -440.0f, -3000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 4360.0f, 3640.0f, 2760.0f, 2020.0f, -140.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 690.0f, 330.0f, -160.0f, -3000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 1480.0f, 760.0f, 280.0f, -3000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, -343.3f, -863.3f, -1143.3f, -3000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, -100.0f, -520.0f, -3000.0f }, + { 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, 9999.0f, -3000.0f }, +}; + +static u16 sSwitchEntryCount[10] = { 5, 6, 4, 10, 25, 50, 8, 10, 6, 1 }; + +static u8 sSwitchFromRoom[10][51] = { + { 11, 0, 0, 12, 11 }, + { 0, 2, 3, 16, 17, 18 }, + { 1, 6, 15, 16 }, + { 0, 6, 7, 8, 11, 22, 23, 24, 25, 26 }, + { 21, 4, 6, 10, 5, 11, 24, 28, 31, 10, 7, 12, 13, 30, 34, 35, 27, 33, 37, 29, 32, 26, 28, 31, 36 }, + { 0, 1, 4, 5, 6, 10, 17, 20, 0, 1, 22, 25, 30, 39, 21, 17, 0, 1, 23, 26, 3, 9, 12, 14, 15, + 31, 8, 24, 27, 28, 33, 36, 37, 38, 43, 34, 23, 26, 31, 40, 42, 17, 22, 25, 29, 30, 32, 35, 39, 41 }, + { 4, 15, 5, 23, 31, 29, 28, 30 }, + { 8, 9, 12, 14, 21, 23, 24, 25, 26, 22 }, + { 0, 1, 8, 9, 7, 8 }, + { 255 }, +}; + +static u8 sSwitchFromFloor[10][51] = { + { 3, 4, 3, 4, 5 }, + { 6, 6, 6, 7, 7, 7 }, + { 7, 7, 6, 6 }, + { 4, 4, 4, 4, 4, 5, 5, 5, 5, 5 }, + { 6, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 7 }, + { 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4 }, + { 6, 6, 5, 4, 5, 6, 7, 7 }, + { 6, 7, 7, 7, 7, 6, 6, 6, 6, 5 }, + { 6, 6, 7, 6, 5, 5 }, + { 255 }, +}; + +static u8 sSwitchToRoom[10][51] = { + { 12, 11, 12, 11 }, + { 16, 17, 18, 0, 2, 3 }, + { 15, 16, 1, 6 }, + { 22, 23, 24, 25, 26, 0, 6, 7, 8, 11 }, + { 36, 26, 28, 31, 27, 33, 37, 29, 32, 32, 30, 34, 35, 7, 12, 13, 5, 11, 24, 28, 31, 4, 6, 10, 21 }, + { 22, 25, 29, 30, 32, 35, 39, 41, 23, 26, 23, 26, 31, 40, 42, 40, 24, 27, 24, 27, 28, 34, 36, 37, 38, + 43, 33, 23, 26, 3, 8, 12, 14, 15, 31, 9, 22, 25, 30, 39, 21, 39, 0, 1, 4, 5, 6, 10, 17, 20 }, + { 28, 30, 29, 31, 23, 5, 4, 15 }, + { 22, 23, 24, 25, 26, 9, 12, 14, 21, 8 }, + { 7, 8, 9, 8, 0, 1 }, + { 255 }, +}; + +static u8 sFloorID[10][8] = { + /* clang-format off */ + { 0, 0, 0, F_3F, F_2F, F_1F, F_B1, F_B2 }, + { 0, 0, 0, 0, 0, 0, F_2F, F_1F }, + { 0, 0, 0, 0, 0, 0, F_1F, F_B1 }, + { 0, 0, 0, 0, F_2F, F_1F, F_B1, F_B2 }, + { 0, 0, 0, F_5F, F_4F, F_3F, F_2F, F_1F }, + { 0, 0, 0, 0, F_3F, F_2F, F_1F, F_B1 }, + { 0, 0, 0, 0, F_4F, F_3F, F_2F, F_1F }, + { 0, 0, 0, 0, F_B1, F_B2, F_B3, F_B4 }, + { 0, 0, 0, 0, 0, F_B1, F_B2, F_B3 }, + { 0, 0, 0, 0, 0, 0, 0, F_1F }, +}; /* clang-format on */ + +/* Y coord of big skull icon on map screen, relative to center of screen. + -99 if no dungeon map, otherwise (51 - 14 * FloorNumber) */ +static s16 sSkullFloorIconY[10] = { -47, -47, -33, -47, -47, -5, -19, -47, -99, -99 }; + +MapData gMapDataTable = { + sFloorTexIndexOffset, sBossFloor, sRoomPalette, + sMaxPaletteCount, sPaletteRoom, sRoomCompassOffsetX, + sRoomCompassOffsetY, sDgnMinimapCount, sDgnMinimapTexIndexOffset, + sOwMinimapTexSize, sOwMinimapTexOffset, sOwMinimapPosX, + sOwMinimapPosY, sOwCompassInfo, sDgnMinimapTexIndexBase, + sDgnCompassInfo, sOwMinimapWidth, sOwMinimapHeight, + sOwEntranceIconPosX, sOwEntranceIconPosY, sOwEntranceFlag, + sFloorCoordY, sSwitchEntryCount, sSwitchFromRoom, + sSwitchFromFloor, sSwitchToRoom, sFloorID, + sSkullFloorIconY, +}; diff --git a/soh/src/code/z_map_exp.c b/soh/src/code/z_map_exp.c new file mode 100644 index 000000000..734209770 --- /dev/null +++ b/soh/src/code/z_map_exp.c @@ -0,0 +1,868 @@ +#include "global.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/map_i_static/map_i_static.h" +#include "textures/map_grand_static/map_grand_static.h" + +MapData* gMapData; + +s16 sPlayerInitialPosX = 0; +s16 sPlayerInitialPosZ = 0; +s16 sPlayerInitialDirection = 0; +s16 sEntranceIconMapIndex = 0; + +void Map_SavePlayerInitialInfo(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + sPlayerInitialPosX = player->actor.world.pos.x; + sPlayerInitialPosZ = player->actor.world.pos.z; + sPlayerInitialDirection = (s16)((0x7FFF - player->actor.shape.rot.y) / 0x400); +} + +void Map_SetPaletteData(GlobalContext* globalCtx, s16 room) { + s32 mapIndex = gSaveContext.mapIndex; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 paletteIndex = gMapData->roomPalette[mapIndex][room]; + + if (interfaceCtx->mapRoomNum == room) { + interfaceCtx->mapPaletteIndex = paletteIndex; + } + + osSyncPrintf(VT_FGCOL(YELLOW)); + // "PALETE Set" + osSyncPrintf("PALETEセット 【 i=%x : room=%x 】Room_Inf[%d][4]=%x ( map_palete_no = %d )\n", paletteIndex, + room, mapIndex, gSaveContext.sceneFlags[mapIndex].rooms, interfaceCtx->mapPaletteIndex); + osSyncPrintf(VT_RST); + + interfaceCtx->mapPalette[paletteIndex * 2] = 2; + interfaceCtx->mapPalette[paletteIndex * 2 + 1] = 0xBF; +} + +void Map_SetFloorPalettesData(GlobalContext* globalCtx, s16 floor) { + s32 mapIndex = gSaveContext.mapIndex; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 room; + s16 i; + + for (i = 0; i < 16; i++) { + interfaceCtx->mapPalette[i] = 0; + interfaceCtx->mapPalette[i + 16] = 0; + } + + if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, mapIndex)) { + interfaceCtx->mapPalette[30] = 0; + interfaceCtx->mapPalette[31] = 1; + } + + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + for (i = 0; i < gMapData->maxPaletteCount[mapIndex]; i++) { + room = gMapData->paletteRoom[mapIndex][floor][i]; + if ((room != 0xFF) && (gSaveContext.sceneFlags[mapIndex].rooms & gBitFlags[room])) { + Map_SetPaletteData(globalCtx, room); + } + } + break; + } +} + +const char* minimapTableOW[] = +{ + gHyruleFieldMinimapTex, + gKakarikoVillageMinimapTex, + gGraveyardMinimapTex, + gZorasRiverMinimapTex, + gKokiriForestMinimapTex, + gSacredMeadowMinimapTex, + gLakeHyliaMinimapTex, + gZorasDomainMinimapTex, + gZorasFountainMinimapTex, + gGerudoValleyMinimapTex, + gHauntedWastelandMinimapTex, + gDesertColossusMinimapTex, + gGerudosFortessMinimapTex, + gLostWoodsMinimapTex, + gHyruleCastleAreaMinimapTex, + gDeathMountainTrailMinimapTex, + gDeathMountainCraterMinimapTex, + gGoronCityMinimapTex, + gLonLonRanchMinimapTex, + gOutsideGanonsCastleMinimapTex, + gExploredShadowGraveyardMinimapTex, + gDrainedLakeHyliaMinimapTex, + gGerudoValleyWithBrokenBridgeMinimapTex, + gGerudosFortressMinimapTex, +}; + +const char* minimapTableDangeon[] = +{ + gDekuTreeRoom0Floor1MinimapTex, + gDekuTreeRoom1MinimapTex, + gDekuTreeRoom2MinimapTex, + gDekuTreeRoom3MinimapTex, + gDekuTreeRoom4MinimapTex, + gDekuTreeRoom5MinimapTex, + gDekuTreeRoom6MinimapTex, + gDekuTreeRoom7MinimapTex, + gDekuTreeRoom8MinimapTex, + gDekuTreeRoom9MinimapTex, + gDekuTreeRoom10MinimapTex, + gDekuTreeRoom0Floor2MinimapTex, + gDekuTreeRoom0Floor3MinimapTex, + gDodongosCavernRoom0MinimapTex, + gDodongosCavernRoom1MinimapTex, + gDodongosCavernRoom2MinimapTex, + gDodongosCavernRoom3MinimapTex, + gDodongosCavernRoom4MinimapTex, + gDodongosCavernRoom5MinimapTex, + gDodongosCavernRoom6MinimapTex, + gDodongosCavernRoom7MinimapTex, + gDodongosCavernRoom8MinimapTex, + gDodongosCavernRoom9MinimapTex, + gDodongosCavernRoom10MinimapTex, + gDodongosCavernRoom11MinimapTex, + gDodongosCavernRoom12MinimapTex, + gDodongosCavernRoom13MinimapTex, + gDodongosCavernRoom14MinimapTex, + gDodongosCavernRoom15MinimapTex, + gDodongosCavernRoom0Floor3MinimapTex, + gDodongosCavernRoom2Floor3MinimapTex, + gDodongosCavernRoom3Floor3MinimapTex, + gJabuRoom0MinimapTex, + gJabuRoom1MinimapTex, + gJabuRoom2MinimapTex, + gJabuRoom3MinimapTex, + gJabuRoom4MinimapTex, + gJabuRoom5MinimapTex, + gJabuRoom6MinimapTex, + gJabuRoom7MinimapTex, + gJabuRoom8MinimapTex, + gJabuRoom9MinimapTex, + gJabuRoom10MinimapTex, + gJabuRoom11MinimapTex, + gJabuRoom12MinimapTex, + gJabuRoom13MinimapTex, + gJabuRoom14MinimapTex, + gJabuRoom1Basement1MinimapTex, + gJabuRoom6Basement1MinimapTex, + gForestTempleRoom0Floor1MinimapTex, + gForestTempleRoom1MinimapTex, + gForestTempleRoom2MinimapTex, + gForestTempleRoom3MinimapTex, + gForestTempleRoom4MinimapTex, + gForestTempleRoom5MinimapTex, + gForestTempleRoom6Floor1MinimapTex, + gForestTempleRoom7Floor1MinimapTex, + gForestTempleRoom8Floor1MinimapTex, + gForestTempleRoom9MinimapTex, + gForestTempleRoom10MinimapTex, + gForestTempleRoom11Floor1MinimapTex, + gForestTempleRoom12MinimapTex, + gForestTempleRoom13MinimapTex, + gForestTempleRoom14MinimapTex, + gForestTempleRoom15MinimapTex, + gForestTempleRoom16MinimapTex, + gForestTempleRoom17MinimapTex, + gForestTempleRoom18MinimapTex, + gForestTempleRoom19MinimapTex, + gForestTempleRoom20MinimapTex, + gForestTempleRoom21MinimapTex, + gForestTempleRoom0Floor3MinimapTex, + gForestTempleRoom6Floor3MinimapTex, + gForestTempleRoom7Floor3MinimapTex, + gForestTempleRoom8Floor3MinimapTex, + gForestTempleRoom11Floor3MinimapTex, + gFireTempleRoom0MinimapTex, + gFireTempleRoom1MinimapTex, + gFireTempleRoom2MinimapTex, + gFireTempleRoom3MinimapTex, + gFireTempleRoom4Floor1MinimapTex, + gFireTempleRoom5Floor1MinimapTex, + gFireTempleRoom6Floor1MinimapTex, + gFireTempleRoom7Floor1MinimapTex, + gFireTempleRoom8MinimapTex, + gFireTempleRoom9MinimapTex, + gFireTempleRoom10Floor1MinimapTex, + gFireTempleRoom11Floor1MinimapTex, + gFireTempleRoom12Floor1MinimapTex, + gFireTempleRoom13Floor1MinimapTex, + gFireTempleRoom14MinimapTex, + gFireTempleRoom15MinimapTex, + gFireTempleRoom16MinimapTex, + gFireTempleRoom17MinimapTex, + gFireTempleRoom18MinimapTex, + gFireTempleRoom19MinimapTex, + gFireTempleRoom20MinimapTex, + gFireTempleRoom21Floor1MinimapTex, + gFireTempleRoom22MinimapTex, + gFireTempleRoom23MinimapTex, + gFireTempleRoom24Floor1MinimapTex, + gFireTempleRoom25MinimapTex, + gFireTempleRoom4Floor3MinimapTex, + gFireTempleRoom5Floor3MinimapTex, + gFireTempleRoom6Floor2MinimapTex, + gFireTempleRoom6Floor3MinimapTex, + gFireTempleRoom7Floor3MinimapTex, + gFireTempleRoom10Floor2MinimapTex, + gFireTempleRoom10Floor3MinimapTex, + gFireTempleRoom11Floor3MinimapTex, + gFireTempleRoom12Floor3MinimapTex, + gFireTempleRoom13Floor3MinimapTex, + gFireTempleRoom21Floor3MinimapTex, + gFireTempleRoom24Floor3MinimapTex, + gWaterTempleRoom0Floor3MinimapTex, + gWaterTempleRoom1Floor3MinimapTex, + gWaterTempleRoom2MinimapTex, + gWaterTempleRoom3Floor3MinimapTex, + gWaterTempleRoom4Floor3MinimapTex, + gWaterTempleRoom5Floor3MinimapTex, + gWaterTempleRoom6MinimapTex, + gWaterTempleRoom7MinimapTex, + gWaterTempleRoom8Floor3MinimapTex, + gWaterTempleRoom9Floor3MinimapTex, + gWaterTempleRoom10Floor3MinimapTex, + gWaterTempleRoom11MinimapTex, + gWaterTempleRoom12Floor3MinimapTex, + gWaterTempleRoom13MinimapTex, + gWaterTempleRoom14Floor3MinimapTex, + gWaterTempleRoom15Floor3MinimapTex, + gWaterTempleRoom16MinimapTex, + gWaterTempleRoom17Floor3MinimapTex, + gWaterTempleRoom18MinimapTex, + gWaterTempleRoom19MinimapTex, + gWaterTempleRoom20Floor3MinimapTex, + gWaterTempleRoom21Floor3MinimapTex, + gWaterTempleRoom0Floor2MinimapTex, + gWaterTempleRoom0Floor1MinimapTex, + gWaterTempleRoom0Basement1MinimapTex, + gWaterTempleRoom1Floor2MinimapTex, + gWaterTempleRoom1Floor1MinimapTex, + gWaterTempleRoom1Basement1MinimapTex, + gWaterTempleRoom3Basement1MinimapTex, + gWaterTempleRoom4Floor2MinimapTex, + gWaterTempleRoom5Floor2MinimapTex, + gWaterTempleRoom5Floor1MinimapTex, + gWaterTempleRoom6Floor2MinimapTex, + gWaterTempleRoom8Basement1MinimapTex, + gWaterTempleRoom9Basement1MinimapTex, + gWaterTempleRoom10Floor2MinimapTex, + gWaterTempleRoom12Basement1MinimapTex, + gWaterTempleRoom14Basement1MinimapTex, + gWaterTempleRoom15Basement1MinimapTex, + gWaterTempleRoom17Floor2MinimapTex, + gWaterTempleRoom17Floor1MinimapTex, + gWaterTempleRoom20Floor2MinimapTex, + gWaterTempleRoom21Floor1MinimapTex, + gWaterTempleRoom5Basement1MinimapTex, + gSpiritTempleRoom0MinimapTex, + gSpiritTempleRoom1MinimapTex, + gSpiritTempleRoom2MinimapTex, + gSpiritTempleRoom3MinimapTex, + gSpiritTempleRoom4Floor1MinimapTex, + gSpiritTempleRoom5Floor2MinimapTex, + gSpiritTempleRoom6MinimapTex, + gSpiritTempleRoom7MinimapTex, + gSpiritTempleRoom8MinimapTex, + gSpiritTempleRoom9MinimapTex, + gSpiritTempleRoom10MinimapTex, + gBlankSpiritTempleRoom11MinimapTex, + gSpiritTempleRoom12MinimapTex, + gSpiritTempleRoom13MinimapTex, + gSpiritTempleRoom14MinimapTex, + gSpiritTempleRoom15Floor1MinimapTex, + gSpiritTempleRoom16MinimapTex, + gSpiritTempleRoom17MinimapTex, + gSpiritTempleRoom18MinimapTex, + gSpiritTempleRoom19MinimapTex, + gSpiritTempleRoom20MinimapTex, + gSpiritTempleRoom21MinimapTex, + gSpiritTempleRoom22MinimapTex, + gSpiritTempleRoom23Floor3MinimapTex, + gSpiritTempleRoom24MinimapTex, + gSpiritTempleRoom25MinimapTex, + gSpiritTempleRoom26MinimapTex, + gSpiritTempleRoom27MinimapTex, + gSpiritTempleRoom4Floor2MinimapTex, + gSpiritTempleRoom5Floor3MinimapTex, + gSpiritTempleRoom15Floor2MinimapTex, + gSpiritTempleRoom23Floor4MinimapTex, + gShadowTempleRoom0MinimapTex, + gShadowTempleRoom1MinimapTex, + gShadowTempleRoom2MinimapTex, + gShadowTempleRoom3MinimapTex, + gShadowTempleRoom4MinimapTex, + gShadowTempleRoom5MinimapTex, + gShadowTempleRoom6MinimapTex, + gShadowTempleRoom7MinimapTex, + gShadowTempleRoom8Basement2MinimapTex, + gShadowTempleRoom9Basement3MinimapTex, + gShadowTempleRoom10MinimapTex, + gShadowTempleRoom11MinimapTex, + gShadowTempleRoom12Basement3MinimapTex, + gShadowTempleRoom13MinimapTex, + gShadowTempleRoom14Basement3MinimapTex, + gShadowTempleRoom15MinimapTex, + gShadowTempleRoom16MinimapTex, + gShadowTempleRoom17MinimapTex, + gShadowTempleRoom18MinimapTex, + gShadowTempleRoom19MinimapTex, + gShadowTempleRoom20MinimapTex, + gShadowTempleRoom21Basement3MinimapTex, + gShadowTempleRoom8Basement3MinimapTex, + gShadowTempleRoom9Basement4MinimapTex, + gShadowTempleRoom12Basement4MinimapTex, + gShadowTempleRoom14Basement4MinimapTex, + gShadowTempleRoom21Basement4MinimapTex, + gBottomOfTheWellRoom0Basement1MinimapTex, + gBottomOfTheWellRoom1Basement1MinimapTex, + gBottomOfTheWellRoom2MinimapTex, + gBottomOfTheWellRoom3MinimapTex, + gBottomOfTheWellRoom4MinimapTex, + gBottomOfTheWellRoom5MinimapTex, + gBottomOfTheWellRoom6MinimapTex, + gBottomOfTheWellRoom0Basement2MinimapTex, + gBottomOfTheWellRoom1Basement2MinimapTex, + gBottomOfTheWellRoom1Basement3MinimapTex, + gIceCavernRoom0MinimapTex, + gIceCavernRoom1MinimapTex, + gIceCavernRoom2MinimapTex, + gIceCavernRoom3MinimapTex, + gIceCavernRoom4MinimapTex, + gIceCavernRoom5MinimapTex, + gIceCavernRoom6MinimapTex, + gIceCavernRoom7MinimapTex, + gIceCavernRoom8MinimapTex, + gIceCavernRoom9MinimapTex, + gIceCavernRoom10MinimapTex, + gIceCavernRoom11MinimapTex, +}; + +void Map_InitData(GlobalContext* globalCtx, s16 room) { + s32 mapIndex = gSaveContext.mapIndex; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 extendedMapIndex; + + switch (globalCtx->sceneNum) { + case SCENE_SPOT00: + case SCENE_SPOT01: + case SCENE_SPOT02: + case SCENE_SPOT03: + case SCENE_SPOT04: + case SCENE_SPOT05: + case SCENE_SPOT06: + case SCENE_SPOT07: + case SCENE_SPOT08: + case SCENE_SPOT09: + case SCENE_SPOT10: + case SCENE_SPOT11: + case SCENE_SPOT12: + case SCENE_SPOT13: + case SCENE_SPOT15: + case SCENE_SPOT16: + case SCENE_SPOT17: + case SCENE_SPOT18: + case SCENE_SPOT20: + case SCENE_GANON_TOU: + extendedMapIndex = mapIndex; + if (globalCtx->sceneNum == SCENE_SPOT02) { + if (CHECK_QUEST_ITEM(QUEST_SONG_NOCTURNE)) { + extendedMapIndex = 0x14; + } + } else if (globalCtx->sceneNum == SCENE_SPOT06) { + if ((LINK_AGE_IN_YEARS == YEARS_ADULT) && !CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER)) { + extendedMapIndex = 0x15; + } + } else if (globalCtx->sceneNum == SCENE_SPOT09) { + if ((LINK_AGE_IN_YEARS == YEARS_ADULT) && !((gSaveContext.eventChkInf[9] & 0xF) == 0xF)) { + extendedMapIndex = 0x16; + } + } else if (globalCtx->sceneNum == SCENE_SPOT12) { + if ((gSaveContext.eventChkInf[9] & 0xF) == 0xF) { + extendedMapIndex = 0x17; + } + } + osSyncPrintf(VT_FGCOL(BLUE)); + osSyncPrintf("KKK=%d\n", extendedMapIndex); + osSyncPrintf(VT_RST); + sEntranceIconMapIndex = extendedMapIndex; + //DmaMgr_SendRequest1(interfaceCtx->mapSegment, + //(uintptr_t)_map_grand_staticSegmentRomStart + gMapData->owMinimapTexOffset[extendedMapIndex], + //gMapData->owMinimapTexSize[mapIndex], "../z_map_exp.c", 309); + + if (sEntranceIconMapIndex < 24) + memcpy(globalCtx->interfaceCtx.mapSegment, ResourceMgr_LoadTexByName(minimapTableOW[sEntranceIconMapIndex]), gMapData->owMinimapTexSize[mapIndex]); + + interfaceCtx->unk_258 = mapIndex; + break; + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Deku Tree Dungeon MAP Texture DMA" + osSyncPrintf("デクの樹ダンジョンMAP テクスチャDMA(%x) scene_id_offset=%d VREG(30)=%d\n", room, + mapIndex, VREG(30)); + osSyncPrintf(VT_RST); + //DmaMgr_SendRequest1(globalCtx->interfaceCtx.mapSegment, + //(uintptr_t)_map_i_staticSegmentRomStart + + //((gMapData->dgnMinimapTexIndexOffset[mapIndex] + room) * 0xFF0), + //0xFF0, "../z_map_exp.c", 346); + + memcpy(globalCtx->interfaceCtx.mapSegment, ResourceMgr_LoadTexByName(minimapTableDangeon[gMapData->dgnMinimapTexIndexOffset[mapIndex] + room]), 0xFF0); + + R_COMPASS_OFFSET_X = gMapData->roomCompassOffsetX[mapIndex][room]; + R_COMPASS_OFFSET_Y = gMapData->roomCompassOffsetY[mapIndex][room]; + Map_SetFloorPalettesData(globalCtx, VREG(30)); + osSyncPrintf("MAP 各階ONチェック\n"); // "MAP Individual Floor ON Check" + break; + } +} + +void Map_InitRoomData(GlobalContext* globalCtx, s16 room) { + s32 mapIndex = gSaveContext.mapIndex; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + osSyncPrintf("*******\n*******\nroom_no=%d (%d)(%d)\n*******\n*******\n", room, + mapIndex, globalCtx->sceneNum); + + if (room >= 0) { + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + gSaveContext.sceneFlags[mapIndex].rooms |= gBitFlags[room]; + osSyncPrintf("ROOM_INF=%d\n", gSaveContext.sceneFlags[mapIndex].rooms); + interfaceCtx->mapRoomNum = room; + interfaceCtx->unk_25A = mapIndex; + Map_SetPaletteData(globalCtx, room); + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("部屋部屋=%d\n", room); // "Room Room = %d" + osSyncPrintf(VT_RST); + Map_InitData(globalCtx, room); + break; + } + } else { + interfaceCtx->mapRoomNum = 0; + } + + if (gSaveContext.sunsSongState != SUNSSONG_SPEED_TIME) { + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + } +} + +void Map_Destroy(GlobalContext* globalCtx) { + MapMark_ClearPointers(globalCtx); + gMapData = NULL; +} + +void Map_Init(GlobalContext* globalCtx) { + s32 mapIndex = gSaveContext.mapIndex; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + gMapData = &gMapDataTable; + + interfaceCtx->unk_258 = -1; + interfaceCtx->unk_25A = -1; + + interfaceCtx->mapSegment = GameState_Alloc(&globalCtx->state, 0x1000, "../z_map_exp.c", 457); + // "MAP texture initialization scene_data_ID=%d mapSegment=%x" + osSyncPrintf("\n\n\nMAP テクスチャ初期化 scene_data_ID=%d\nmapSegment=%x\n\n", globalCtx->sceneNum, + interfaceCtx->mapSegment, globalCtx); + ASSERT(interfaceCtx->mapSegment != NULL, "parameter->mapSegment != NULL", "../z_map_exp.c", 459); + + switch (globalCtx->sceneNum) { + case SCENE_SPOT00: + case SCENE_SPOT01: + case SCENE_SPOT02: + case SCENE_SPOT03: + case SCENE_SPOT04: + case SCENE_SPOT05: + case SCENE_SPOT06: + case SCENE_SPOT07: + case SCENE_SPOT08: + case SCENE_SPOT09: + case SCENE_SPOT10: + case SCENE_SPOT11: + case SCENE_SPOT12: + case SCENE_SPOT13: + case SCENE_SPOT15: + case SCENE_SPOT16: + case SCENE_SPOT17: + case SCENE_SPOT18: + case SCENE_SPOT20: + case SCENE_GANON_TOU: + mapIndex = globalCtx->sceneNum - SCENE_SPOT00; + R_MAP_INDEX = gSaveContext.mapIndex = mapIndex; + R_COMPASS_SCALE_X = gMapData->owCompassInfo[mapIndex][0]; + R_COMPASS_SCALE_Y = gMapData->owCompassInfo[mapIndex][1]; + R_COMPASS_OFFSET_X = gMapData->owCompassInfo[mapIndex][2]; + R_COMPASS_OFFSET_Y = gMapData->owCompassInfo[mapIndex][3]; + Map_InitData(globalCtx, mapIndex); + R_OW_MINIMAP_X = gMapData->owMinimapPosX[mapIndex]; + R_OW_MINIMAP_Y = gMapData->owMinimapPosY[mapIndex]; + break; + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_GANON: + case SCENE_MEN: + case SCENE_GERUDOWAY: + case SCENE_GANONTIKA: + case SCENE_GANON_SONOGO: + case SCENE_GANONTIKA_SONOGO: + case SCENE_TAKARAYA: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + mapIndex = + (globalCtx->sceneNum >= SCENE_YDAN_BOSS) ? globalCtx->sceneNum - SCENE_YDAN_BOSS : globalCtx->sceneNum; + R_MAP_INDEX = gSaveContext.mapIndex = mapIndex; + if ((globalCtx->sceneNum <= SCENE_ICE_DOUKUTO) || (globalCtx->sceneNum >= SCENE_YDAN_BOSS)) { + R_COMPASS_SCALE_X = gMapData->dgnCompassInfo[mapIndex][0]; + R_COMPASS_SCALE_Y = gMapData->dgnCompassInfo[mapIndex][1]; + R_COMPASS_OFFSET_X = gMapData->dgnCompassInfo[mapIndex][2]; + R_COMPASS_OFFSET_Y = gMapData->dgnCompassInfo[mapIndex][3]; + R_MAP_TEX_INDEX = R_MAP_TEX_INDEX_BASE = gMapData->dgnMinimapTexIndexBase[mapIndex]; + Map_InitRoomData(globalCtx, globalCtx->roomCtx.curRoom.num); + MapMark_Init(globalCtx); + } + break; + } +} + +void Minimap_DrawCompassIcons(GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s16 tempX, tempZ; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_map_exp.c", 565); + + if (globalCtx->interfaceCtx.minimapAlpha >= 0xAA) { + func_80094A14(globalCtx->state.gfxCtx); + + gSPMatrix(OVERLAY_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + tempX = player->actor.world.pos.x; + tempZ = player->actor.world.pos.z; + tempX /= R_COMPASS_SCALE_X; + tempZ /= R_COMPASS_SCALE_Y; + Matrix_Translate(OTRGetDimensionFromRightEdge((R_COMPASS_OFFSET_X + tempX) / 10.0f), (R_COMPASS_OFFSET_Y - tempZ) / 10.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.4f, 0.4f, 0.4f, MTXMODE_APPLY); + Matrix_RotateX(-1.6f, MTXMODE_APPLY); + tempX = (0x7FFF - player->actor.shape.rot.y) / 0x400; + Matrix_RotateY(tempX / 10.0f, MTXMODE_APPLY); + gSPMatrix(OVERLAY_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_map_exp.c", 585), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 200, 255, 0, 255); + gSPDisplayList(OVERLAY_DISP++, gCompassArrowDL); + + tempX = sPlayerInitialPosX; + tempZ = sPlayerInitialPosZ; + tempX /= R_COMPASS_SCALE_X; + tempZ /= R_COMPASS_SCALE_Y; + Matrix_Translate(OTRGetDimensionFromRightEdge((R_COMPASS_OFFSET_X + tempX) / 10.0f), (R_COMPASS_OFFSET_Y - tempZ) / 10.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(VREG(9) / 100.0f, VREG(9) / 100.0f, VREG(9) / 100.0f, MTXMODE_APPLY); + Matrix_RotateX(VREG(52) / 10.0f, MTXMODE_APPLY); + Matrix_RotateY(sPlayerInitialDirection / 10.0f, MTXMODE_APPLY); + gSPMatrix(OVERLAY_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_map_exp.c", 603), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0xFF, 200, 0, 0, 255); + gSPDisplayList(OVERLAY_DISP++, gCompassArrowDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_map_exp.c", 607); +} + +void Minimap_Draw(GlobalContext* globalCtx) { + s32 pad[2]; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s32 mapIndex = gSaveContext.mapIndex; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_map_exp.c", 626); + + if (globalCtx->pauseCtx.state < 4) { + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + if (!R_MINIMAP_DISABLED) { + func_80094520(globalCtx->state.gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, + TEXEL0, 0, PRIMITIVE, 0); + + if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, mapIndex)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 255, 255, interfaceCtx->minimapAlpha); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->mapSegment, G_IM_FMT_I, 96, 85, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + const s16 dgnMiniMapX = OTRGetRectDimensionFromRightEdge(R_DGN_MINIMAP_X); + + gSPWideTextureRectangle(OVERLAY_DISP++, dgnMiniMapX << 2, R_DGN_MINIMAP_Y << 2, + (dgnMiniMapX + 96) << 2, (R_DGN_MINIMAP_Y + 85) << 2, G_TX_RENDERTILE, + 0, 0, 1 << 10, 1 << 10); + } + + if (CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, mapIndex)) { + Minimap_DrawCompassIcons(globalCtx); // Draw icons for the player spawn and current position + func_80094520(globalCtx->state.gfxCtx); + MapMark_Draw(globalCtx); + } + } + + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_L) && !Gameplay_InCsMode(globalCtx)) { + osSyncPrintf("Game_play_demo_mode_check=%d\n", Gameplay_InCsMode(globalCtx)); + // clang-format off + if (!R_MINIMAP_DISABLED) { Audio_PlaySoundGeneral(NA_SE_SY_CAMERA_ZOOM_UP, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); } + else { Audio_PlaySoundGeneral(NA_SE_SY_CAMERA_ZOOM_DOWN, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); } + // clang-format on + R_MINIMAP_DISABLED ^= 1; + } + + break; + case SCENE_SPOT00: + case SCENE_SPOT01: + case SCENE_SPOT02: + case SCENE_SPOT03: + case SCENE_SPOT04: + case SCENE_SPOT05: + case SCENE_SPOT06: + case SCENE_SPOT07: + case SCENE_SPOT08: + case SCENE_SPOT09: + case SCENE_SPOT10: + case SCENE_SPOT11: + case SCENE_SPOT12: + case SCENE_SPOT13: + case SCENE_SPOT15: + case SCENE_SPOT16: + case SCENE_SPOT17: + case SCENE_SPOT18: + case SCENE_SPOT20: + case SCENE_GANON_TOU: + if (!R_MINIMAP_DISABLED) { + func_80094520(globalCtx->state.gfxCtx); + + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_MINIMAP_COLOR(0), R_MINIMAP_COLOR(1), R_MINIMAP_COLOR(2), + interfaceCtx->minimapAlpha); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->mapSegment, G_IM_FMT_IA, + gMapData->owMinimapWidth[mapIndex], gMapData->owMinimapHeight[mapIndex], 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + const s16 oWMiniMapX = OTRGetRectDimensionFromRightEdge(R_OW_MINIMAP_X); + + gSPWideTextureRectangle(OVERLAY_DISP++, oWMiniMapX << 2, R_OW_MINIMAP_Y << 2, + (oWMiniMapX + gMapData->owMinimapWidth[mapIndex]) << 2, + (R_OW_MINIMAP_Y + gMapData->owMinimapHeight[mapIndex]) << 2, G_TX_RENDERTILE, 0, + 0, 1 << 10, 1 << 10); + + if (((globalCtx->sceneNum != SCENE_SPOT01) && (globalCtx->sceneNum != SCENE_SPOT04) && + (globalCtx->sceneNum != SCENE_SPOT08)) || + (LINK_AGE_IN_YEARS != YEARS_ADULT)) { + if ((gMapData->owEntranceFlag[sEntranceIconMapIndex] == 0xFFFF) || + ((gMapData->owEntranceFlag[sEntranceIconMapIndex] != 0xFFFF) && + (gSaveContext.infTable[26] & gBitFlags[gMapData->owEntranceFlag[mapIndex]]))) { + + gDPLoadTextureBlock(OVERLAY_DISP++, gMapDungeonEntranceIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, + 8, 8, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, + OTRGetRectDimensionFromLeftEdge(gMapData->owEntranceIconPosX[sEntranceIconMapIndex]) << 2, + gMapData->owEntranceIconPosY[sEntranceIconMapIndex] << 2, + OTRGetRectDimensionFromLeftEdge(gMapData->owEntranceIconPosX[sEntranceIconMapIndex] + 8) << 2, + (gMapData->owEntranceIconPosY[sEntranceIconMapIndex] + 8) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + } + + if ((globalCtx->sceneNum == SCENE_SPOT08) && (gSaveContext.infTable[26] & gBitFlags[9])) { + gDPLoadTextureBlock(OVERLAY_DISP++, gMapDungeonEntranceIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, + 8, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + const s16 entranceX = OTRGetRectDimensionFromRightEdge(270); + + gSPWideTextureRectangle(OVERLAY_DISP++, entranceX << 2, 154 << 2, (entranceX + 32) << 2, (154 + 8) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + Minimap_DrawCompassIcons(globalCtx); // Draw icons for the player spawn and current position + } + + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_L) && !Gameplay_InCsMode(globalCtx)) { + // clang-format off + if (!R_MINIMAP_DISABLED) { Audio_PlaySoundGeneral(NA_SE_SY_CAMERA_ZOOM_UP, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); } + else { Audio_PlaySoundGeneral(NA_SE_SY_CAMERA_ZOOM_DOWN, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); } + // clang-format on + R_MINIMAP_DISABLED ^= 1; + } + + break; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_map_exp.c", 782); +} + +s16 Map_GetFloorTextIndexOffset(s32 mapIndex, s32 floor) { + return gMapData->floorTexIndexOffset[mapIndex][floor]; +} + +void Map_Update(GlobalContext* globalCtx) { + static s16 sLastRoomNum = 99; + Player* player = GET_PLAYER(globalCtx); + s32 mapIndex = gSaveContext.mapIndex; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 floor; + s16 i; + + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0)) { + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + interfaceCtx->mapPalette[30] = 0; + if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, mapIndex)) { + interfaceCtx->mapPalette[31] = 1; + } else { + interfaceCtx->mapPalette[31] = 0; + } + + for (floor = 0; floor < 8; floor++) { + if (player->actor.world.pos.y > gMapData->floorCoordY[mapIndex][floor]) { + break; + } + } + + gSaveContext.sceneFlags[mapIndex].floors |= gBitFlags[floor]; + VREG(30) = floor; + if (R_MAP_TEX_INDEX != (R_MAP_TEX_INDEX_BASE + Map_GetFloorTextIndexOffset(mapIndex, floor))) { + R_MAP_TEX_INDEX = R_MAP_TEX_INDEX_BASE + Map_GetFloorTextIndexOffset(mapIndex, floor); + } + if (1) {} // Appears to be necessary to match + + if (interfaceCtx->mapRoomNum != sLastRoomNum) { + // "Current floor = %d Current room = %x Number of rooms = %d" + osSyncPrintf("現在階=%d 現在部屋=%x 部屋数=%d\n", floor, interfaceCtx->mapRoomNum, + gMapData->switchEntryCount[mapIndex]); + sLastRoomNum = interfaceCtx->mapRoomNum; + } + + for (i = 0; i < gMapData->switchEntryCount[mapIndex]; i++) { + if ((interfaceCtx->mapRoomNum == gMapData->switchFromRoom[mapIndex][i]) && + (floor == gMapData->switchFromFloor[mapIndex][i])) { + interfaceCtx->mapRoomNum = gMapData->switchToRoom[mapIndex][i]; + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Layer switching = %x" + osSyncPrintf("階層切替=%x\n", interfaceCtx->mapRoomNum); + osSyncPrintf(VT_RST); + Map_InitData(globalCtx, interfaceCtx->mapRoomNum); + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + Map_SavePlayerInitialInfo(globalCtx); + } + } + + VREG(10) = interfaceCtx->mapRoomNum; + break; + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + VREG(30) = gMapData->bossFloor[globalCtx->sceneNum - SCENE_YDAN_BOSS]; + R_MAP_TEX_INDEX = R_MAP_TEX_INDEX_BASE + + gMapData->floorTexIndexOffset[globalCtx->sceneNum - SCENE_YDAN_BOSS][VREG(30)]; + break; + } + } +} diff --git a/soh/src/code/z_map_mark.c b/soh/src/code/z_map_mark.c new file mode 100644 index 000000000..f5a8a3c39 --- /dev/null +++ b/soh/src/code/z_map_mark.c @@ -0,0 +1,155 @@ +#include "global.h" +#include "vt.h" +#include "textures/parameter_static/parameter_static.h" + +typedef struct { + /* 0x00 */ void* texture; + /* 0x04 */ u32 imageFormat; + /* 0x08 */ u32 imageSize; + /* 0x0C */ u32 textureWidth; + /* 0x10 */ u32 textureHeight; + /* 0x14 */ u32 rectWidth; + /* 0x18 */ u32 rectHeight; + /* 0x1C */ u32 dsdx; + /* 0x20 */ u32 dtdy; +} MapMarkInfo; // size = 0x24 + +typedef struct { + /* 0x00 */ void* loadedRamAddr; // original name: "allocp" + /* 0x04 */ uintptr_t vromStart; + /* 0x08 */ uintptr_t vromEnd; + /* 0x0C */ void* vramStart; + /* 0x10 */ void* vramEnd; + /* 0x14 */ void* vramTable; +} MapMarkDataOverlay; // size = 0x18 + +static u32 sBaseImageSizes[] = { 0, 1, 2, 3 }; +static u32 sLoadBlockImageSizes[] = { 2, 2, 2, 3 }; +static u32 sIncrImageSizes[] = { 3, 1, 0, 0 }; +static u32 sShiftImageSizes[] = { 2, 1, 0, 0 }; +static u32 sBytesImageSizes[] = { 0, 1, 2, 4 }; +static u32 sLineBytesImageSizes[] = { 0, 1, 2, 2 }; + +#define G_IM_SIZ_MARK sBaseImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_LOAD_BLOCK sLoadBlockImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_INCR sIncrImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_SHIFT sShiftImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_BYTES sBytesImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_LINE_BYTES sLineBytesImageSizes[markInfo->imageSize] + +static MapMarkInfo sMapMarkInfoTable[] = { + { gMapChestIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 8, 32, 32, 1 << 10, 1 << 10 }, // Chest Icon + { gMapBossIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 32, 32, 1 << 10, 1 << 10 }, // Boss Skull Icon +}; + +static MapMarkDataOverlay sMapMarkDataOvl = { + NULL, + //(uintptr_t)_ovl_map_mark_dataSegmentRomStart, + //(uintptr_t)_ovl_map_mark_dataSegmentRomEnd, + //_ovl_map_mark_dataSegmentStart, + //_ovl_map_mark_dataSegmentEnd, + 0, 0, 0, 0, + gMapMarkDataTable, +}; + +static MapMarkData** sLoadedMarkDataTable; + +void MapMark_Init(GlobalContext* globalCtx) { + MapMarkDataOverlay* overlay = &sMapMarkDataOvl; + u32 overlaySize = (uintptr_t)overlay->vramEnd - (uintptr_t)overlay->vramStart; + + overlay->loadedRamAddr = GameState_Alloc(&globalCtx->state, overlaySize, "../z_map_mark.c", 235); + LogUtils_CheckNullPointer("dlftbl->allocp", overlay->loadedRamAddr, "../z_map_mark.c", 236); + + Overlay_Load(overlay->vromStart, overlay->vromEnd, overlay->vramStart, overlay->vramEnd, overlay->loadedRamAddr); + + sLoadedMarkDataTable = gMapMarkDataTable; + //sLoadedMarkDataTable = (void*)(uintptr_t)( + //(overlay->vramTable != NULL) + //? (void*)((uintptr_t)overlay->vramTable - ((intptr_t)overlay->vramStart - (intptr_t)overlay->loadedRamAddr)) + //: NULL); +} + +void MapMark_ClearPointers(GlobalContext* globalCtx) { + sMapMarkDataOvl.loadedRamAddr = NULL; + sLoadedMarkDataTable = NULL; +} + +void MapMark_DrawForDungeon(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx; + MapMarkIconData* mapMarkIconData; + MapMarkPoint* markPoint; + MapMarkInfo* markInfo; + u16 dungeon = gSaveContext.mapIndex; + s32 i; + s32 rectLeft; + s32 rectTop; + + interfaceCtx = &globalCtx->interfaceCtx; + + if ((gMapData != NULL) && (globalCtx->interfaceCtx.mapRoomNum >= gMapData->dgnMinimapCount[dungeon])) { + // "Room number exceeded, yikes %d/%d MapMarkDraw processing interrupted" + osSyncPrintf(VT_COL(RED, WHITE) "部屋番号がオーバーしてるで,ヤバイで %d/%d \nMapMarkDraw の処理を中断します\n", + VT_RST, globalCtx->interfaceCtx.mapRoomNum, gMapData->dgnMinimapCount[dungeon]); + return; + } + + mapMarkIconData = &sLoadedMarkDataTable[dungeon][interfaceCtx->mapRoomNum][0]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_map_mark.c", 303); + + while (true) { + if (mapMarkIconData->markType == MAP_MARK_NONE) { + break; + } + + gDPPipeSync(OVERLAY_DISP++); + gDPSetTextureLUT(OVERLAY_DISP++, G_TT_NONE); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->minimapAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, interfaceCtx->minimapAlpha); + + markPoint = &mapMarkIconData->points[0]; + for (i = 0; i < mapMarkIconData->count; i++) { + if ((mapMarkIconData->markType != MAP_MARK_CHEST) || !Flags_GetTreasure(globalCtx, markPoint->chestFlag)) { + markInfo = &sMapMarkInfoTable[mapMarkIconData->markType]; + + gDPPipeSync(OVERLAY_DISP++); + gDPLoadTextureBlock(OVERLAY_DISP++, markInfo->texture, markInfo->imageFormat, G_IM_SIZ_MARK, + markInfo->textureWidth, markInfo->textureHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + rectLeft = (GREG(94) + OTRGetRectDimensionFromRightEdge(markPoint->x) + 204) << 2; + rectTop = (GREG(95) + markPoint->y + 140) << 2; + gSPTextureRectangle(OVERLAY_DISP++, rectLeft, rectTop, markInfo->rectWidth + rectLeft, + rectTop + markInfo->rectHeight, G_TX_RENDERTILE, 0, 0, markInfo->dsdx, + markInfo->dtdy); + } + markPoint++; + } + mapMarkIconData++; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_map_mark.c", 339); +} + +void MapMark_Draw(GlobalContext* globalCtx) { + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + MapMark_DrawForDungeon(globalCtx); + break; + } +} diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c new file mode 100644 index 000000000..4979f047c --- /dev/null +++ b/soh/src/code/z_message_PAL.c @@ -0,0 +1,3335 @@ +#include "global.h" +#include "message_data_static.h" +#include "vt.h" + +#include + +#include "textures/parameter_static/parameter_static.h" +#include "textures/message_static/message_static.h" +#include "textures/message_texture_static/message_texture_static.h" + +s16 sTextFade = false; // original name: key_off_flag ? + +u8 D_8014B2F4 = 0; + +s16 sOcarinaNoteBufPos = 0; + +s16 sOcarinaNoteBufLen = 0; + +u8 sTextboxSkipped = false; + +u16 sNextTextId = 0; + +s16 sTextIsCredits = false; + +UNK_TYPE D_8014B30C = 0; + +s16 sLastPlayedSong = 0xFF; // last played song? + +s16 sHasSunsSong = false; + +s16 sMessageHasSetSfx = false; + +u16 sOcarinaSongBitFlags = 0; // ocarina bit flags + +MessageTableEntry* sNesMessageEntryTablePtr; +MessageTableEntry* sStaffMessageEntryTablePtr; + +char* _message_0xFFFC_nes; + +//MessageTableEntry sNesMessageEntryTable[] = { +//#define DEFINE_MESSAGE(textId, type, yPos, nesMessage, gerMessage, fraMessage) \ +// { textId, (_SHIFTL(type, 4, 8) | _SHIFTL(yPos, 0, 8)), _message_##textId##_nes }, +//#define DEFINE_MESSAGE_FFFC +//#include "text/message_data.h" +//#undef DEFINE_MESSAGE_FFFC +//#undef DEFINE_MESSAGE +// { 0xFFFF, 0, NULL }, +//}; +// +//const char* sGerMessageEntryTable[] = { +//#define DEFINE_MESSAGE(textId, type, yPos, nesMessage, gerMessage, fraMessage) _message_##textId##_ger, +//#include "text/message_data.h" +//#undef DEFINE_MESSAGE +// NULL, +//}; +// +//const char* sFraMessageEntryTable[] = { +//#define DEFINE_MESSAGE(textId, type, yPos, nesMessage, gerMessage, fraMessage) _message_##textId##_fra, +//#include "text/message_data.h" +//#undef DEFINE_MESSAGE +// NULL, +//}; +// +//MessageTableEntry sStaffMessageEntryTable[] = { +//#define DEFINE_MESSAGE(textId, type, yPos, staffMessage) \ +// { textId, (_SHIFTL(type, 4, 8) | _SHIFTL(yPos, 0, 8)), _message_##textId##_staff }, +//#include "text/message_data_staff.h" +//#undef DEFINE_MESSAGE +// { 0xFFFF, 0, NULL }, +//}; + +//MessageTableEntry* sNesMessageEntryTablePtr = sNesMessageEntryTable; +//const char** sGerMessageEntryTablePtr = sGerMessageEntryTable; +//const char** sFraMessageEntryTablePtr = sFraMessageEntryTable; +//MessageTableEntry* sStaffMessageEntryTablePtr = sStaffMessageEntryTable; + +s16 sTextboxBackgroundForePrimColors[][3] = { + { 255, 255, 255 }, { 50, 20, 0 }, { 255, 60, 0 }, { 255, 255, 255 }, + { 255, 255, 255 }, { 255, 255, 255 }, { 255, 255, 255 }, { 255, 255, 255 }, +}; + +s16 sTextboxBackgroundBackPrimColors[][3] = { + { 0, 0, 0 }, + { 220, 150, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, +}; + +s16 sTextboxBackgroundYOffsets[] = { + 1, + 2, +}; + +// original name: onpu_buff +u8 sOcarinaNoteBuf[12] = { 0 }; + +s16 sOcarinaNotesAlphaValues[9] = { 0 }; + +// Maps the ocarina song order to the quest item order +s16 gOcarinaSongItemMap[] = { + OCARINA_SONG_MINUET, OCARINA_SONG_BOLERO, OCARINA_SONG_SERENADE, OCARINA_SONG_REQUIEM, + OCARINA_SONG_NOCTURNE, OCARINA_SONG_PRELUDE, OCARINA_SONG_LULLABY, OCARINA_SONG_EPONAS, + OCARINA_SONG_SARIAS, OCARINA_SONG_SUNS, OCARINA_SONG_TIME, OCARINA_SONG_STORMS, +}; + +s32 sCharTexSize; +s32 sCharTexScale; +s16 sOcarinaNoteAPrimR; +s16 sOcarinaNoteAPrimB; +s16 sOcarinaNoteAPrimG; +s16 sOcarinaNoteAEnvR; +s16 sOcarinaNoteAEnvB; +s16 sOcarinaNoteAEnvG; +s16 sOcarinaNoteCPrimR; +s16 sOcarinaNoteCPrimB; +s16 sOcarinaNoteCPrimG; +s16 sOcarinaNoteCEnvR; +s16 sOcarinaNoteCEnvB; +s16 sOcarinaNoteCEnvG; + +void Message_ResetOcarinaNoteState(void) { + R_OCARINA_NOTES_YPOS(0) = 189; + R_OCARINA_NOTES_YPOS(1) = 184; + R_OCARINA_NOTES_YPOS(2) = 179; + R_OCARINA_NOTES_YPOS(3) = 174; + R_OCARINA_NOTES_YPOS(4) = 169; + sOcarinaNoteBuf[0] = 0xFF; + sOcarinaNotesAlphaValues[0] = sOcarinaNotesAlphaValues[1] = sOcarinaNotesAlphaValues[2] = + sOcarinaNotesAlphaValues[3] = sOcarinaNotesAlphaValues[4] = sOcarinaNotesAlphaValues[5] = + sOcarinaNotesAlphaValues[6] = sOcarinaNotesAlphaValues[7] = sOcarinaNotesAlphaValues[8] = 0; + sOcarinaNoteAPrimR = 80; + sOcarinaNoteAPrimG = 255; + sOcarinaNoteAPrimB = 150; + sOcarinaNoteAEnvR = 10; + sOcarinaNoteAEnvG = 10; + sOcarinaNoteAEnvB = 10; + sOcarinaNoteCPrimR = 255; + sOcarinaNoteCPrimG = 255; + sOcarinaNoteCPrimB = 50; + sOcarinaNoteCEnvR = 10; + sOcarinaNoteCEnvG = 10; + sOcarinaNoteCEnvB = 10; +} + +void Message_UpdateOcarinaGame(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + + globalCtx->msgCtx.msgMode++; + + if (globalCtx->msgCtx.msgMode == MSGMODE_MEMORY_GAME_PLAYER_PLAYING) { + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + func_800ECC04((1 << OCARINA_SONG_MEMORY_GAME) + 0x8000); + msgCtx->textDrawPos = msgCtx->decodedTextLen; + } else if (msgCtx->msgMode == MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_PLAYING) { + Audio_OcaSetInstrument(6); + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + Audio_OcaSetSongPlayback(OCARINA_SONG_MEMORY_GAME + 1, 1); + msgCtx->stateTimer = 2; + } + Message_ResetOcarinaNoteState(); +} + +u8 Message_ShouldAdvance(GlobalContext* globalCtx) { + Input* input = &globalCtx->state.input[0]; + + bool isB_Held = CVar_GetS32("gFastText", 0) != 0 ? CHECK_BTN_ALL(input->cur.button, BTN_B) + : CHECK_BTN_ALL(input->press.button, BTN_B); + + if (CHECK_BTN_ALL(input->press.button, BTN_A) || isB_Held || CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Audio_PlaySoundGeneral(NA_SE_SY_MESSAGE_PASS, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + return CHECK_BTN_ALL(input->press.button, BTN_A) || isB_Held || CHECK_BTN_ALL(input->press.button, BTN_CUP); +} + +u8 Message_ShouldAdvanceSilent(GlobalContext* globalCtx) { + Input* input = &globalCtx->state.input[0]; + + bool isB_Held = CVar_GetS32("gFastText", 0) != 0 ? CHECK_BTN_ALL(input->cur.button, BTN_B) + : CHECK_BTN_ALL(input->press.button, BTN_B); + + return CHECK_BTN_ALL(input->press.button, BTN_A) || isB_Held || CHECK_BTN_ALL(input->press.button, BTN_CUP); +} + +/** + * Closes any currently displayed textbox immediately, without waiting for + * input from the player. + */ +void Message_CloseTextbox(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + + if (msgCtx->msgLength != 0) { + msgCtx->stateTimer = 2; + msgCtx->msgMode = MSGMODE_TEXT_CLOSING; + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_DEFAULT; + Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void Message_HandleChoiceSelection(GlobalContext* globalCtx, u8 numChoices) { + static s16 sAnalogStickHeld = false; + MessageContext* msgCtx = &globalCtx->msgCtx; + Input* input = &globalCtx->state.input[0]; + + if (input->rel.stick_y >= 30 && !sAnalogStickHeld) { + sAnalogStickHeld = true; + msgCtx->choiceIndex--; + if (msgCtx->choiceIndex > 128) { + msgCtx->choiceIndex = 0; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else if (input->rel.stick_y <= -30 && !sAnalogStickHeld) { + sAnalogStickHeld = true; + msgCtx->choiceIndex++; + if (msgCtx->choiceIndex > numChoices) { + msgCtx->choiceIndex = numChoices; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else if (ABS(input->rel.stick_y) < 30) { + sAnalogStickHeld = false; + } + msgCtx->textPosX = R_TEXT_CHOICE_XPOS; + msgCtx->textPosY = + (numChoices == 1) ? R_TEXT_CHOICE_YPOS(msgCtx->choiceIndex + 1) : R_TEXT_CHOICE_YPOS(msgCtx->choiceIndex); +} + +void Message_DrawTextChar(GlobalContext* globalCtx, void* textureImage, Gfx** p) { + MessageContext* msgCtx = &globalCtx->msgCtx; + Gfx* gfx = *p; + s16 x = msgCtx->textPosX; + s16 y = msgCtx->textPosY; + + //gSPInvalidateTexCache(gfx++, 0); + //gSPInvalidateTexCache(gfx++, msgCtx->textboxSegment); + gSPInvalidateTexCache(gfx++, textureImage); + + gDPPipeSync(gfx++); + + sCharTexSize = (R_TEXT_CHAR_SCALE / 100.0f) * 16.0f; + sCharTexScale = 1024.0f / (R_TEXT_CHAR_SCALE / 100.0f); + + gDPLoadTextureBlock_4b(gfx++, textureImage, G_IM_FMT_I, FONT_CHAR_TEX_WIDTH, FONT_CHAR_TEX_HEIGHT, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + // Draw drop shadow + if (msgCtx->textBoxType != TEXTBOX_TYPE_NONE_NO_SHADOW) { + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, msgCtx->textColorAlpha); + gSPTextureRectangle(gfx++, (x + R_TEXT_DROP_SHADOW_OFFSET) << 2, (y + R_TEXT_DROP_SHADOW_OFFSET) << 2, + (x + R_TEXT_DROP_SHADOW_OFFSET + sCharTexSize) << 2, + (y + R_TEXT_DROP_SHADOW_OFFSET + sCharTexSize) << 2, G_TX_RENDERTILE, 0, 0, sCharTexScale, + sCharTexScale); + } + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, msgCtx->textColorR, msgCtx->textColorG, msgCtx->textColorB, msgCtx->textColorAlpha); + gSPTextureRectangle(gfx++, x << 2, y << 2, (x + sCharTexSize) << 2, (y + sCharTexSize) << 2, G_TX_RENDERTILE, 0, 0, + sCharTexScale, sCharTexScale); + *p = gfx; +} + +// resizes textboxes when opening them +void Message_GrowTextbox(MessageContext* msgCtx) { + static f32 sWidthCoefficients[] = { + 1.2f, 1.5f, 1.8f, 2.0f, 2.1f, 2.2f, 2.1f, 2.0f, + }; + static f32 sHeightCoefficients[] = { + 0.6f, 0.75f, 0.9f, 1.0f, 1.05f, 1.1f, 1.05f, 1.0f, + }; + f32 width = + R_TEXTBOX_WIDTH_TARGET * (sWidthCoefficients[msgCtx->stateTimer] + sWidthCoefficients[msgCtx->stateTimer]); + f32 height = R_TEXTBOX_HEIGHT_TARGET * sHeightCoefficients[msgCtx->stateTimer]; + f32 texWidth = + R_TEXTBOX_TEXWIDTH_TARGET / (sWidthCoefficients[msgCtx->stateTimer] + sWidthCoefficients[msgCtx->stateTimer]); + f32 texHeight = R_TEXTBOX_TEXHEIGHT_TARGET / sHeightCoefficients[msgCtx->stateTimer]; + + // Adjust y pos + R_TEXTBOX_Y = R_TEXTBOX_Y_TARGET + + (R_TEXTBOX_Y_TARGET - (s16)(R_TEXTBOX_Y_TARGET * sHeightCoefficients[msgCtx->stateTimer] + 0.5f)) / 2; + + msgCtx->textboxColorAlphaCurrent += msgCtx->textboxColorAlphaTarget / 8; + msgCtx->stateTimer++; + if (msgCtx->stateTimer == 8) { + // Reached the end + R_TEXTBOX_X = R_TEXTBOX_X_TARGET; + R_TEXTBOX_Y = R_TEXTBOX_Y_TARGET; + msgCtx->msgMode = MSGMODE_TEXT_STARTING; + msgCtx->textboxColorAlphaCurrent = msgCtx->textboxColorAlphaTarget; + } + // Adjust width and height + R_TEXTBOX_WIDTH = (s16)(width + 0.5f) / 2; + R_TEXTBOX_HEIGHT = (s16)(height + 0.5f); + // Adjust texture coordinates + R_TEXTBOX_TEXWIDTH = texWidth + 0.5f; + R_TEXTBOX_TEXHEIGHT = texHeight + 0.5f; + // Adjust x pos + R_TEXTBOX_X = (R_TEXTBOX_X_TARGET + R_TEXTBOX_WIDTH_TARGET) - (R_TEXTBOX_WIDTH / 2); +} + +void Message_FindMessage(GlobalContext* globalCtx, u16 textId) { + const char* foundSeg; + const char* nextSeg; + MessageTableEntry* messageTableEntry = sNesMessageEntryTablePtr; + const char** languageSegmentTable; + Font* font; + const char* seg; + + + if (gSaveContext.language == LANGUAGE_ENG) { + seg = messageTableEntry->segment; + + while (messageTableEntry->textId != 0xFFFF) { + font = &globalCtx->msgCtx.font; + + if (messageTableEntry->textId == textId) { + foundSeg = messageTableEntry->segment; + font->charTexBuf[0] = messageTableEntry->typePos; + //messageTableEntry++; + nextSeg = messageTableEntry->segment; + font->msgOffset = messageTableEntry->segment; + font->msgLength = messageTableEntry->msgSize; + // "Message found!!!" + osSyncPrintf(" メッセージが,見つかった!!! = %x " + "(data=%x) (data0=%x) (data1=%x) (data2=%x) (data3=%x)\n", + textId, font->msgOffset, font->msgLength, foundSeg, seg, nextSeg); + return; + } + messageTableEntry++; + } + } else { + //languageSegmentTable = (gSaveContext.language == LANGUAGE_GER) ? sGerMessageEntryTablePtr : sFraMessageEntryTablePtr; // OTRTODO + seg = messageTableEntry->segment; + + while (messageTableEntry->textId != 0xFFFF) { + font = &globalCtx->msgCtx.font; + + if (messageTableEntry->textId == textId) { + foundSeg = *languageSegmentTable; + font->charTexBuf[0] = messageTableEntry->typePos; + languageSegmentTable++; + nextSeg = *languageSegmentTable; + font->msgOffset = foundSeg - seg; + font->msgLength = nextSeg - foundSeg; + // "Message found!!!" + osSyncPrintf(" メッセージが,見つかった!!! = %x " + "(data=%x) (data0=%x) (data1=%x) (data2=%x) (data3=%x)\n", + textId, font->msgOffset, font->msgLength, foundSeg, seg, nextSeg); + return; + } + messageTableEntry++; + languageSegmentTable++; + } + } + // "Message not found!!!" + osSyncPrintf(" メッセージが,見つからなかった!!! = %x\n", textId); + font = &globalCtx->msgCtx.font; + messageTableEntry = sNesMessageEntryTablePtr; + + if (gSaveContext.language == LANGUAGE_ENG) { + foundSeg = messageTableEntry->segment; + font->charTexBuf[0] = messageTableEntry->typePos; + messageTableEntry++; + nextSeg = messageTableEntry->segment; + } else { + //languageSegmentTable = (gSaveContext.language == LANGUAGE_GER) ? sGerMessageEntryTablePtr : sFraMessageEntryTablePtr; // OTRTODO + foundSeg = *languageSegmentTable; + font->charTexBuf[0] = messageTableEntry->typePos; + languageSegmentTable++; + nextSeg = *languageSegmentTable; + } + font->msgOffset = foundSeg - seg; + font->msgLength = nextSeg - foundSeg; +} + +void Message_FindCreditsMessage(GlobalContext* globalCtx, u16 textId) { + const char* foundSeg; + const char* nextSeg; + const char* seg; + MessageTableEntry* messageTableEntry = sStaffMessageEntryTablePtr; + Font* font; + + seg = messageTableEntry->segment; + while (messageTableEntry->textId != 0xFFFF) { + font = &globalCtx->msgCtx.font; + + if (messageTableEntry->textId == textId) { + foundSeg = messageTableEntry->segment; + font->charTexBuf[0] = messageTableEntry->typePos; + messageTableEntry++; + nextSeg = messageTableEntry->segment; + font->msgOffset = messageTableEntry->segment; + font->msgLength = messageTableEntry->msgSize; + // "Message found!!!" + osSyncPrintf(" メッセージが,見つかった!!! = %x (data=%x) (data0=%x) (data1=%x) (data2=%x) (data3=%x)\n", + textId, font->msgOffset, font->msgLength, foundSeg, seg, nextSeg); + return; + } + messageTableEntry++; + } +} + +void Message_SetTextColor(MessageContext* msgCtx, u16 colorParameter) { + switch (colorParameter) { + case MSGCOL_RED: + if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textColorR = 255; + msgCtx->textColorG = 120; + msgCtx->textColorB = 0; + } else { + msgCtx->textColorR = 255; + msgCtx->textColorG = 60; + msgCtx->textColorB = 60; + } + break; + case MSGCOL_ADJUSTABLE: + if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textColorR = R_TEXT_ADJUST_COLOR_1_R; + msgCtx->textColorG = R_TEXT_ADJUST_COLOR_1_G; + msgCtx->textColorB = R_TEXT_ADJUST_COLOR_1_B; + } else { + msgCtx->textColorR = R_TEXT_ADJUST_COLOR_2_R; + msgCtx->textColorG = R_TEXT_ADJUST_COLOR_2_G; + msgCtx->textColorB = R_TEXT_ADJUST_COLOR_2_B; + } + break; + case MSGCOL_BLUE: + if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textColorR = 80; + msgCtx->textColorG = 110; + msgCtx->textColorB = 255; + } else { + msgCtx->textColorR = 80; + msgCtx->textColorG = 90; + msgCtx->textColorB = 255; + } + break; + case MSGCOL_LIGHTBLUE: + if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textColorR = 90; + msgCtx->textColorG = 180; + msgCtx->textColorB = 255; + } else if (msgCtx->textBoxType == TEXTBOX_TYPE_NONE_NO_SHADOW) { + msgCtx->textColorR = 80; + msgCtx->textColorG = 150; + msgCtx->textColorB = 180; + } else { + msgCtx->textColorR = 100; + msgCtx->textColorG = 180; + msgCtx->textColorB = 255; + } + break; + case MSGCOL_PURPLE: + if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textColorR = 210; + msgCtx->textColorG = 100; + msgCtx->textColorB = 255; + } else { + msgCtx->textColorR = 255; + msgCtx->textColorG = 150; + msgCtx->textColorB = 180; + } + break; + case MSGCOL_YELLOW: + if (msgCtx->textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textColorR = 255; + msgCtx->textColorG = 255; + msgCtx->textColorB = 30; + } else { + msgCtx->textColorR = 225; + msgCtx->textColorG = 255; + msgCtx->textColorB = 50; + } + break; + case MSGCOL_BLACK: + msgCtx->textColorR = msgCtx->textColorG = msgCtx->textColorB = 0; + break; + case MSGCOL_DEFAULT: + default: + if (msgCtx->textBoxType == TEXTBOX_TYPE_NONE_NO_SHADOW) { + msgCtx->textColorR = msgCtx->textColorG = msgCtx->textColorB = 0; + } else { + msgCtx->textColorR = msgCtx->textColorG = msgCtx->textColorB = 255; + } + break; + } +} + +void Message_DrawTextboxIcon(GlobalContext* globalCtx, Gfx** p, s16 x, s16 y) { + static s16 sIconPrimColors[][3] = { + { 0, 200, 80 }, + { 50, 255, 130 }, + }; + static s16 sIconEnvColors[][3] = { + { 0, 0, 0 }, + { 0, 255, 130 }, + }; + static s16 sIconPrimR = 0; + static s16 sIconPrimG = 200; + static s16 sIconPrimB = 80; + static s16 sIconFlashTimer = 12; + static s16 sIconFlashColorIdx = 0; + static s16 sIconEnvR = 0; + static s16 sIconEnvG = 0; + static s16 sIconEnvB = 0; + MessageContext* msgCtx = &globalCtx->msgCtx; + Font* font = &msgCtx->font; + Gfx* gfx = *p; + s16 primR; + s16 primG; + s16 primB; + s16 envR; + s16 envG; + s16 envB; + u8* iconTexture = font->iconBuf; + + if (sTextIsCredits) { + return; + } + + primR = (ABS(sIconPrimR - sIconPrimColors[sIconFlashColorIdx][0])) / sIconFlashTimer; + primG = (ABS(sIconPrimG - sIconPrimColors[sIconFlashColorIdx][1])) / sIconFlashTimer; + primB = (ABS(sIconPrimB - sIconPrimColors[sIconFlashColorIdx][2])) / sIconFlashTimer; + + if (sIconPrimR >= sIconPrimColors[sIconFlashColorIdx][0]) { + sIconPrimR -= primR; + } else { + sIconPrimR += primR; + } + + if (sIconPrimG >= sIconPrimColors[sIconFlashColorIdx][1]) { + sIconPrimG -= primG; + } else { + sIconPrimG += primG; + } + + if (sIconPrimB >= sIconPrimColors[sIconFlashColorIdx][2]) { + sIconPrimB -= primB; + } else { + sIconPrimB += primB; + } + + envR = (ABS(sIconEnvR - sIconEnvColors[sIconFlashColorIdx][0])) / sIconFlashTimer; + envG = (ABS(sIconEnvG - sIconEnvColors[sIconFlashColorIdx][1])) / sIconFlashTimer; + envB = (ABS(sIconEnvB - sIconEnvColors[sIconFlashColorIdx][2])) / sIconFlashTimer; + + if (sIconEnvR >= sIconEnvColors[sIconFlashColorIdx][0]) { + sIconEnvR -= envR; + } else { + sIconEnvR += envR; + } + + if (sIconEnvG >= sIconEnvColors[sIconFlashColorIdx][1]) { + sIconEnvG -= envG; + } else { + sIconEnvG += envG; + } + + if (sIconEnvB >= sIconEnvColors[sIconFlashColorIdx][2]) { + sIconEnvB -= envB; + } else { + sIconEnvB += envB; + } + + sIconFlashTimer--; + + if (sIconFlashTimer == 0) { + sIconPrimR = sIconPrimColors[sIconFlashColorIdx][0]; + sIconPrimG = sIconPrimColors[sIconFlashColorIdx][1]; + sIconPrimB = sIconPrimColors[sIconFlashColorIdx][2]; + sIconEnvR = sIconEnvColors[sIconFlashColorIdx][0]; + sIconEnvG = sIconEnvColors[sIconFlashColorIdx][1]; + sIconEnvB = sIconEnvColors[sIconFlashColorIdx][2]; + sIconFlashTimer = 12; + sIconFlashColorIdx ^= 1; + } + + gDPPipeSync(gfx++); + + gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + gDPSetPrimColor(gfx++, 0, 0, sIconPrimR, sIconPrimG, sIconPrimB, 255); + gDPSetEnvColor(gfx++, sIconEnvR, sIconEnvG, sIconEnvB, 255); + + gDPLoadTextureBlock_4b(gfx++, iconTexture, G_IM_FMT_I, FONT_CHAR_TEX_WIDTH, FONT_CHAR_TEX_HEIGHT, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + sCharTexSize = 16.0f * ((f32)R_TEXT_CHAR_SCALE / 100.0f); + sCharTexScale = 1024.0f / ((f32)R_TEXT_CHAR_SCALE / 100.0f); + + gSPTextureRectangle(gfx++, x << 2, y << 2, (x + sCharTexSize) << 2, (y + sCharTexSize) << 2, G_TX_RENDERTILE, 0, 0, + sCharTexScale, sCharTexScale); + + msgCtx->stateTimer++; + + *p = gfx; +} + +#define MESSAGE_SPACE_WIDTH 6 +f32 sFontWidths[144] = { + 8.0f, // ' ' + 8.0f, // '!' + 6.0f, // '"' + 9.0f, // '#' + 9.0f, // '$' + 14.0f, // '%' + 12.0f, // '&' + 3.0f, // ''' + 7.0f, // '(' + 7.0f, // ')' + 7.0f, // '*' + 9.0f, // '+' + 4.0f, // ',' + 6.0f, // '-' + 4.0f, // '.' + 9.0f, // '/' + 10.0f, // '0' + 5.0f, // '1' + 9.0f, // '2' + 9.0f, // '3' + 10.0f, // '4' + 9.0f, // '5' + 9.0f, // '6' + 9.0f, // '7' + 9.0f, // '8' + 9.0f, // '9' + 6.0f, // ':' + 6.0f, // ';' + 9.0f, // '<' + 11.0f, // '=' + 9.0f, // '>' + 11.0f, // '?' + 13.0f, // '@' + 12.0f, // 'A' + 9.0f, // 'B' + 11.0f, // 'C' + 11.0f, // 'D' + 8.0f, // 'E' + 8.0f, // 'F' + 12.0f, // 'G' + 10.0f, // 'H' + 4.0f, // 'I' + 8.0f, // 'J' + 10.0f, // 'K' + 8.0f, // 'L' + 13.0f, // 'M' + 11.0f, // 'N' + 13.0f, // 'O' + 9.0f, // 'P' + 13.0f, // 'Q' + 10.0f, // 'R' + 10.0f, // 'S' + 9.0f, // 'T' + 10.0f, // 'U' + 11.0f, // 'V' + 15.0f, // 'W' + 11.0f, // 'X' + 10.0f, // 'Y' + 10.0f, // 'Z' + 7.0f, // '[' + 10.0f, // '\' + 7.0f, // ']' + 10.0f, // '^' + 9.0f, // '_' + 5.0f, // '`' + 8.0f, // 'a' + 9.0f, // 'b' + 8.0f, // 'c' + 9.0f, // 'd' + 9.0f, // 'e' + 6.0f, // 'f' + 9.0f, // 'g' + 8.0f, // 'h' + 4.0f, // 'i' + 6.0f, // 'j' + 8.0f, // 'k' + 4.0f, // 'l' + 12.0f, // 'm' + 9.0f, // 'n' + 9.0f, // 'o' + 9.0f, // 'p' + 9.0f, // 'q' + 7.0f, // 'r' + 8.0f, // 's' + 7.0f, // 't' + 8.0f, // 'u' + 9.0f, // 'v' + 12.0f, // 'w' + 8.0f, // 'x' + 9.0f, // 'y' + 8.0f, // 'z' + 7.0f, // '{' + 5.0f, // '|' + 7.0f, // '}' + 10.0f, // '~' + 10.0f, // '‾' + 12.0f, // 'À' + 6.0f, // 'î' + 12.0f, // 'Â' + 12.0f, // 'Ä' + 11.0f, // 'Ç' + 8.0f, // 'È' + 8.0f, // 'É' + 8.0f, // 'Ê' + 6.0f, // 'Ë' + 6.0f, // 'Ï' + 13.0f, // 'Ô' + 13.0f, // 'Ö' + 10.0f, // 'Ù' + 10.0f, // 'Û' + 10.0f, // 'Ü' + 9.0f, // 'ß' + 8.0f, // 'à' + 8.0f, // 'á' + 8.0f, // 'â' + 8.0f, // 'ä' + 8.0f, // 'ç' + 9.0f, // 'è' + 9.0f, // 'é' + 9.0f, // 'ê' + 9.0f, // 'ë' + 6.0f, // 'ï' + 9.0f, // 'ô' + 9.0f, // 'ö' + 9.0f, // 'ù' + 9.0f, // 'û' + 9.0f, // 'ü' + 14.0f, // '[A]' + 14.0f, // '[B]' + 14.0f, // '[C]' + 14.0f, // '[L]' + 14.0f, // '[R]' + 14.0f, // '[Z]' + 14.0f, // '[C-Up]' + 14.0f, // '[C-Down]' + 14.0f, // '[C-Left]' + 14.0f, // '[C-Right]' + 14.0f, // '▼' + 14.0f, // '[Control-Pad]' + 14.0f, // '[D-Pad]' + 14.0f, // ? + 14.0f, // ? + 14.0f, // ? + 14.0f, // ? +}; + +u16 Message_DrawItemIcon(GlobalContext* globalCtx, u16 itemId, Gfx** p, u16 i) { + s32 pad; + Gfx* gfx = *p; + MessageContext* msgCtx = &globalCtx->msgCtx; + + // clang-format off + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); } + // clang-format on + + gDPPipeSync(gfx++); + gDPSetCombineMode(gfx++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(gfx++, 0, 0, 255, 255, 255, msgCtx->textColorAlpha); + + if (itemId >= ITEM_MEDALLION_FOREST) { + gDPLoadTextureBlock(gfx++, (uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE, G_IM_FMT_RGBA, G_IM_SIZ_32b, + 24, 24, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } else { + gDPLoadTextureBlock(gfx++, (uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE, G_IM_FMT_RGBA, G_IM_SIZ_32b, + 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } + gSPTextureRectangle(gfx++, (msgCtx->textPosX + R_TEXTBOX_ICON_XPOS) << 2, R_TEXTBOX_ICON_YPOS << 2, + (msgCtx->textPosX + R_TEXTBOX_ICON_XPOS + R_TEXTBOX_ICON_SIZE) << 2, + (R_TEXTBOX_ICON_YPOS + R_TEXTBOX_ICON_SIZE) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0); + + msgCtx->textPosX += 32; + + i++; + *p = gfx; + + return i; +} + +void Message_HandleOcarina(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + + if (globalCtx->msgCtx.msgMode == MSGMODE_TEXT_DISPLAYING) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + + if (msgCtx->ocarinaAction == OCARINA_ACTION_SCARECROW_LONG_RECORDING) { + msgCtx->msgMode = MSGMODE_SCARECROW_LONG_RECORDING_START; + // "Recording Start / Recording Start / Recording Start / Recording Start -> " + osSyncPrintf("録音開始 録音開始 録音開始 録音開始 -> "); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_SCARECROW_LONG_PLAYBACK) { + // "Recording Playback / Recording Playback / Recording Playback / Recording Playback -> " + osSyncPrintf("録音再生 録音再生 録音再生 録音再生 -> "); + Audio_OcaSetInstrument(1); + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + sOcarinaNoteBufPos = sOcarinaNoteBufLen = 0; + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos; + Message_ResetOcarinaNoteState(); + msgCtx->stateTimer = 3; + msgCtx->msgMode = MSGMODE_SCARECROW_LONG_PLAYBACK; + Audio_OcaSetSongPlayback(OCARINA_SONG_SCARECROW_LONG + 1, 1); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_SCARECROW_RECORDING) { + msgCtx->msgMode = MSGMODE_SCARECROW_RECORDING_START; + // "8 Note Recording Start / 8 Note Recording Start / 8 Note Recording Start -> " + osSyncPrintf("8音録音開始 8音録音開始 8音録音開始 -> "); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_SCARECROW_PLAYBACK) { + // "8 Note Playback / 8 Note Playback / 8 Note Playback -> " + osSyncPrintf("8音再生 8音再生 8音再生 -> "); + Audio_OcaSetInstrument(1); + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + sOcarinaNoteBufPos = sOcarinaNoteBufLen = 0; + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos; + Message_ResetOcarinaNoteState(); + msgCtx->stateTimer = 3; + msgCtx->msgMode = MSGMODE_SCARECROW_PLAYBACK; + Audio_OcaSetSongPlayback(OCARINA_SONG_SCARECROW + 1, 1); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_MEMORY_GAME) { + msgCtx->msgMode = MSGMODE_MEMORY_GAME_START; + // "Musical Round Start / Musical Round Start / Musical Round Start / Musical Round Start -> " + osSyncPrintf("輪唱開始 輪唱開始 輪唱開始 輪唱開始 -> "); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_FROGS) { + msgCtx->msgMode = MSGMODE_FROGS_START; + // "Frog Chorus / Frog Chorus -> " + osSyncPrintf("カエルの合唱 カエルの合唱 -> "); + } else { + // "Ocarina(%d)" + osSyncPrintf("オカリナ(%d) ", msgCtx->ocarinaAction); + if (msgCtx->ocarinaAction == OCARINA_ACTION_UNK_0 || msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY || + msgCtx->ocarinaAction >= OCARINA_ACTION_CHECK_SARIA) { + msgCtx->msgMode = MSGMODE_OCARINA_STARTING; + osSyncPrintf("000000000000 -> "); + } else if (msgCtx->ocarinaAction >= OCARINA_ACTION_TEACH_MINUET && + msgCtx->ocarinaAction <= OCARINA_ACTION_TEACH_STORMS) { + msgCtx->msgMode = MSGMODE_SONG_DEMONSTRATION_STARTING; + osSyncPrintf("111111111111 -> "); + } else { + msgCtx->msgMode = MSGMODE_SONG_PLAYBACK_STARTING; + osSyncPrintf("222222222222 -> "); + } + } + osSyncPrintf("msg_mode=%d\n", msgCtx->msgMode); + } +} + +/** + * Draws the text contents of a textbox, up to the current point that has + * been scrolled to so far. + */ +void Message_DrawText(GlobalContext* globalCtx, Gfx** gfxP) { + MessageContext* msgCtx = &globalCtx->msgCtx; + u16 lookAheadCharacter; + u8 character; + u16 j; + u16 i; + u16 sfxHi; + u16 charTexIdx; + Font* font = &globalCtx->msgCtx.font; + Gfx* gfx = *gfxP; + + globalCtx->msgCtx.textPosX = R_TEXT_INIT_XPOS; + + if (sTextIsCredits == false) { + msgCtx->textPosY = R_TEXT_INIT_YPOS; + } else { + msgCtx->textPosY = YREG(1); + } + + if (msgCtx->textBoxType == TEXTBOX_TYPE_NONE_NO_SHADOW) { + msgCtx->textColorR = msgCtx->textColorG = msgCtx->textColorB = 0; + } else { + msgCtx->textColorR = msgCtx->textColorG = msgCtx->textColorB = 255; + } + + msgCtx->unk_E3D0 = 0; + charTexIdx = 0; + + for (i = 0; i < msgCtx->textDrawPos; i++) { + character = msgCtx->msgBufDecoded[i]; + + switch (character) { + case MESSAGE_NEWLINE: + msgCtx->textPosX = R_TEXT_INIT_XPOS; + if (msgCtx->choiceNum == 1 || msgCtx->choiceNum == 3) { + msgCtx->textPosX += 32; + } + if (msgCtx->choiceNum == 2 && msgCtx->textPosY != R_TEXT_INIT_YPOS) { + msgCtx->textPosX += 32; + } + msgCtx->textPosY += R_TEXT_LINE_SPACING; + break; + case MESSAGE_COLOR: + Message_SetTextColor(msgCtx, msgCtx->msgBufDecoded[++i] & 0xF); + break; + case ' ': + msgCtx->textPosX += MESSAGE_SPACE_WIDTH; + break; + case MESSAGE_BOX_BREAK: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + if (!sTextboxSkipped) { + Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + msgCtx->msgMode = MSGMODE_TEXT_AWAIT_NEXT; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_TRIANGLE); + } else { + msgCtx->msgMode = MSGMODE_TEXT_NEXT_MSG; + msgCtx->textUnskippable = false; + msgCtx->msgBufPos++; + } + } + *gfxP = gfx; + return; + case MESSAGE_SHIFT: + msgCtx->textPosX += msgCtx->msgBufDecoded[++i]; + break; + case MESSAGE_TEXTID: + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_HAS_NEXT; + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + msgCtx->msgMode = MSGMODE_TEXT_DONE; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_TRIANGLE); + } + *gfxP = gfx; + return; + case MESSAGE_QUICKTEXT_ENABLE: + if (i + 1 == msgCtx->textDrawPos && (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || + (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && + msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START))) { + j = i; + while (true) { + lookAheadCharacter = msgCtx->msgBufDecoded[j]; + if (lookAheadCharacter == MESSAGE_SHIFT) { + j += 2; + } else if ((lookAheadCharacter != MESSAGE_QUICKTEXT_DISABLE) && + (lookAheadCharacter != MESSAGE_PERSISTENT) && + (lookAheadCharacter != MESSAGE_EVENT) && + (lookAheadCharacter != MESSAGE_BOX_BREAK_DELAYED) && + (lookAheadCharacter != MESSAGE_AWAIT_BUTTON_PRESS) && + (lookAheadCharacter != MESSAGE_BOX_BREAK) && (lookAheadCharacter != MESSAGE_END)) { + j++; + } else { + break; + } + } + i = j - 1; + msgCtx->textDrawPos = i + 1; + + if (character) {} + } + case MESSAGE_QUICKTEXT_DISABLE: + break; + case MESSAGE_AWAIT_BUTTON_PRESS: + if (i + 1 == msgCtx->textDrawPos) { + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->msgMode = MSGMODE_TEXT_AWAIT_INPUT; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_TRIANGLE); + } + *gfxP = gfx; + return; + } + break; + case MESSAGE_BOX_BREAK_DELAYED: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->stateTimer = msgCtx->msgBufDecoded[++i]; + msgCtx->msgMode = MSGMODE_TEXT_DELAYED_BREAK; + } + *gfxP = gfx; + return; + case MESSAGE_FADE2: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->msgMode = MSGMODE_TEXT_DONE; + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_FADING; + // "Timer" + osSyncPrintf("タイマー (%x) (%x)", msgCtx->msgBufDecoded[i + 1], msgCtx->msgBufDecoded[i + 2]); + msgCtx->stateTimer = msgCtx->msgBufDecoded[++i] << 8; + msgCtx->stateTimer |= msgCtx->msgBufDecoded[++i]; + // "Total wct" + osSyncPrintf("合計wct=%x(%d)\n", msgCtx->stateTimer, msgCtx->stateTimer); + } + *gfxP = gfx; + return; + case MESSAGE_SFX: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING && !sMessageHasSetSfx) { + sMessageHasSetSfx = true; + // "Sound (SE)" + osSyncPrintf("サウンド(SE)\n"); + sfxHi = msgCtx->msgBufDecoded[i + 1] << 8; + Audio_PlaySoundGeneral(sfxHi | msgCtx->msgBufDecoded[i + 2], &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + i += 2; + break; + case MESSAGE_ITEM_ICON: + i = Message_DrawItemIcon(globalCtx, msgCtx->msgBufDecoded[i + 1], &gfx, i); + break; + case MESSAGE_BACKGROUND: + // clang-format off + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); } + // clang-format on + gDPPipeSync(gfx++); + gDPSetCombineMode(gfx++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(gfx++, 0, 0, sTextboxBackgroundBackPrimColors[msgCtx->textboxBackgroundBackColorIdx][0], + sTextboxBackgroundBackPrimColors[msgCtx->textboxBackgroundBackColorIdx][1], + sTextboxBackgroundBackPrimColors[msgCtx->textboxBackgroundBackColorIdx][2], + msgCtx->textColorAlpha); + + gDPLoadTextureBlock_4b(gfx++, (uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE, G_IM_FMT_I, 96, 48, + 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle( + gfx++, (msgCtx->textPosX + 1) << 2, + (R_TEXTBOX_BG_YPOS + sTextboxBackgroundYOffsets[msgCtx->textboxBackgroundYOffsetIdx]) << 2, + (msgCtx->textPosX + 96 + 1) << 2, + (R_TEXTBOX_BG_YPOS + sTextboxBackgroundYOffsets[msgCtx->textboxBackgroundYOffsetIdx] + 48) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + gDPLoadTextureBlock_4b(gfx++, (uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE + 0x900, G_IM_FMT_I, + 96, 48, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle( + gfx++, (msgCtx->textPosX + 96 + 1) << 2, + (R_TEXTBOX_BG_YPOS + sTextboxBackgroundYOffsets[msgCtx->textboxBackgroundYOffsetIdx]) << 2, + (msgCtx->textPosX + 96 + 1 + 96 + 1) << 2, + (R_TEXTBOX_BG_YPOS + sTextboxBackgroundYOffsets[msgCtx->textboxBackgroundYOffsetIdx] + 48) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, sTextboxBackgroundForePrimColors[msgCtx->textboxBackgroundForeColorIdx][0], + sTextboxBackgroundForePrimColors[msgCtx->textboxBackgroundForeColorIdx][1], + sTextboxBackgroundForePrimColors[msgCtx->textboxBackgroundForeColorIdx][2], + msgCtx->textColorAlpha); + + gDPLoadTextureBlock_4b(gfx++, ((uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE), G_IM_FMT_I, 96, + 48, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle(gfx++, msgCtx->textPosX << 2, R_TEXTBOX_BG_YPOS << 2, (msgCtx->textPosX + 96) << 2, + (R_TEXTBOX_BG_YPOS + 48) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + gDPLoadTextureBlock_4b(gfx++, ((uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE + 0x900), + G_IM_FMT_I, 96, 48, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle(gfx++, (msgCtx->textPosX + 96) << 2, R_TEXTBOX_BG_YPOS << 2, + (msgCtx->textPosX + 192) << 2, (R_TEXTBOX_BG_YPOS + 48) << 2, G_TX_RENDERTILE, 0, 0, + 1 << 10, 1 << 10); + + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + msgCtx->textPosX += 32; + break; + case MESSAGE_TEXT_SPEED: + msgCtx->textDelay = msgCtx->msgBufDecoded[++i]; + break; + case MESSAGE_UNSKIPPABLE: + msgCtx->textUnskippable = CVar_GetS32("gFastText", 0) != 1; + break; + case MESSAGE_TWO_CHOICE: + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_2_CHOICE; + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->choiceTextId = msgCtx->textId; + msgCtx->stateTimer = 4; + msgCtx->choiceIndex = 0; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_ARROW); + } + break; + case MESSAGE_THREE_CHOICE: + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_3_CHOICE; + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->choiceTextId = msgCtx->textId; + msgCtx->stateTimer = 4; + msgCtx->choiceIndex = 0; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_ARROW); + } + break; + case MESSAGE_END: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->msgMode = MSGMODE_TEXT_DONE; + if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_DEFAULT) { + Audio_PlaySoundGeneral(NA_SE_SY_MESSAGE_END, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_SQUARE); + if (globalCtx->csCtx.state == 0) { + Interface_SetDoAction(globalCtx, DO_ACTION_RETURN); + } + } + } + *gfxP = gfx; + return; + case MESSAGE_OCARINA: + if (i + 1 == msgCtx->textDrawPos) { + Message_HandleOcarina(globalCtx); + *gfxP = gfx; + return; + } + break; + case MESSAGE_FADE: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->msgMode = MSGMODE_TEXT_DONE; + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_FADING; + msgCtx->stateTimer = msgCtx->msgBufDecoded[++i]; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_SQUARE); + if (globalCtx->csCtx.state == 0) { + Interface_SetDoAction(globalCtx, DO_ACTION_RETURN); + } + } + *gfxP = gfx; + return; + case MESSAGE_PERSISTENT: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + msgCtx->msgMode = MSGMODE_TEXT_DONE; + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_PERSISTENT; + } + *gfxP = gfx; + return; + case MESSAGE_EVENT: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { + msgCtx->msgMode = MSGMODE_TEXT_DONE; + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_EVENT; + Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_TRIANGLE); + Audio_PlaySoundGeneral(NA_SE_SY_MESSAGE_END, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + *gfxP = gfx; + return; + default: + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING && i + 1 == msgCtx->textDrawPos && + msgCtx->textDelayTimer == msgCtx->textDelay) { + Audio_PlaySoundGeneral(0, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + Message_DrawTextChar(globalCtx, &font->charTexBuf[charTexIdx], &gfx); + charTexIdx += FONT_CHAR_TEX_SIZE; + + msgCtx->textPosX += (s32)(sFontWidths[character - ' '] * (R_TEXT_CHAR_SCALE / 100.0f)); + break; + } + } + if (msgCtx->textDelayTimer == 0) { + msgCtx->textDrawPos = i + 1; + msgCtx->textDelayTimer = msgCtx->textDelay; + } else { + msgCtx->textDelayTimer--; + } + *gfxP = gfx; +} + +void Message_LoadItemIcon(GlobalContext* globalCtx, u16 itemId, s16 y) { + static s16 sIconItem32XOffsets[] = { 74, 74, 74 }; + static s16 sIconItem24XOffsets[] = { 72, 72, 72 }; + MessageContext* msgCtx = &globalCtx->msgCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + if (itemId == ITEM_DUNGEON_MAP) { + interfaceCtx->mapPalette[30] = 0xFF; + interfaceCtx->mapPalette[31] = 0xFF; + } + if (itemId < ITEM_MEDALLION_FOREST) { + R_TEXTBOX_ICON_XPOS = R_TEXT_INIT_XPOS - sIconItem32XOffsets[gSaveContext.language]; + R_TEXTBOX_ICON_YPOS = y + 6; + R_TEXTBOX_ICON_SIZE = 32; + memcpy((uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE, gItemIcons[itemId], 0x1000); + // "Item 32-0" + osSyncPrintf("アイテム32-0\n"); + } else { + R_TEXTBOX_ICON_XPOS = R_TEXT_INIT_XPOS - sIconItem24XOffsets[gSaveContext.language]; + R_TEXTBOX_ICON_YPOS = y + 10; + R_TEXTBOX_ICON_SIZE = 24; + memcpy((uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE, gItemIcons[itemId], 0x900); + // "Item 24" + osSyncPrintf("アイテム24=%d (%d) {%d}\n", itemId, itemId - ITEM_KOKIRI_EMERALD, 84); + } + msgCtx->msgBufPos++; + msgCtx->choiceNum = 1; +} + +void Message_Decode(GlobalContext* globalCtx) { + u8 temp_s2; + u8 phi_s1; + u16 phi_s0_3; + s32 loadChar; + s32 charTexIdx = 0; + s16 playerNameLen; + s16 decodedBufPos = 0; + s16 numLines = 0; + s16 i; + s16 digits[4]; + f32 timeInSeconds; + MessageContext* msgCtx = &globalCtx->msgCtx; + Font* font = &globalCtx->msgCtx.font; + + globalCtx->msgCtx.textDelayTimer = 0; + globalCtx->msgCtx.textUnskippable = globalCtx->msgCtx.textDelay = globalCtx->msgCtx.textDelayTimer = 0; + sTextFade = false; + + while (true) { + phi_s1 = temp_s2 = msgCtx->msgBufDecoded[decodedBufPos] = font->msgBuf[msgCtx->msgBufPos]; + + if (temp_s2 == MESSAGE_BOX_BREAK || temp_s2 == MESSAGE_TEXTID || temp_s2 == MESSAGE_BOX_BREAK_DELAYED || + temp_s2 == MESSAGE_EVENT || temp_s2 == MESSAGE_END) { + // Textbox decoding ends with any of the above text control characters + msgCtx->msgMode = MSGMODE_TEXT_DISPLAYING; + msgCtx->textDrawPos = 1; + R_TEXT_INIT_YPOS = R_TEXTBOX_Y + 8; + osSyncPrintf("JJ=%d\n", numLines); + if (msgCtx->textBoxType != TEXTBOX_TYPE_NONE_BOTTOM) { + if (numLines == 0) { + R_TEXT_INIT_YPOS = (u16)(R_TEXTBOX_Y + 26); + } else if (numLines == 1) { + R_TEXT_INIT_YPOS = (u16)(R_TEXTBOX_Y + 20); + } else if (numLines == 2) { + R_TEXT_INIT_YPOS = (u16)(R_TEXTBOX_Y + 16); + } + } + if (phi_s1 == MESSAGE_TEXTID) { + osSyncPrintf("NZ_NEXTMSG=%x, %x, %x\n", font->msgBuf[msgCtx->msgBufPos], + font->msgBuf[msgCtx->msgBufPos + 1], font->msgBuf[msgCtx->msgBufPos + 2]); + temp_s2 = msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[msgCtx->msgBufPos + 1]; + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[msgCtx->msgBufPos + 2]; + phi_s0_3 = temp_s2 << 8; + sNextTextId = msgCtx->msgBufDecoded[decodedBufPos] | phi_s0_3; + } + if (phi_s1 == MESSAGE_BOX_BREAK_DELAYED) { + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[msgCtx->msgBufPos + 1]; + msgCtx->msgBufPos += 2; + } + msgCtx->decodedTextLen = decodedBufPos; + if (sTextboxSkipped) { + msgCtx->textDrawPos = msgCtx->decodedTextLen; + } + break; + } else if (temp_s2 == MESSAGE_NAME) { + // Substitute the player name control character for the file's player name. + for (playerNameLen = ARRAY_COUNT(gSaveContext.playerName); playerNameLen > 0; playerNameLen--) { + if (gSaveContext.playerName[playerNameLen - 1] != 0x3E) { + break; + } + } + // "Name" + osSyncPrintf("\n名前 = "); + for (i = 0; i < playerNameLen; i++) { + phi_s1 = gSaveContext.playerName[i]; + if (phi_s1 == 0x3E) { + phi_s1 = ' '; + } else if (phi_s1 == 0x40) { + phi_s1 = '.'; + } else if (phi_s1 == 0x3F) { + phi_s1 = '-'; + } else if (phi_s1 < 0xA) { + phi_s1 += 0; + phi_s1 += '0'; + } else if (phi_s1 < 0x24) { + phi_s1 += 0; + phi_s1 += '7'; + } else if (phi_s1 < 0x3E) { + phi_s1 += 0; + phi_s1 += '='; + } + if (phi_s1 != ' ') { + Font_LoadChar(font, phi_s1 - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + } + osSyncPrintf("%x ", phi_s1); + msgCtx->msgBufDecoded[decodedBufPos] = phi_s1; + decodedBufPos++; + } + decodedBufPos--; + } else if (temp_s2 == MESSAGE_MARATHON_TIME || temp_s2 == MESSAGE_RACE_TIME) { + // Convert the values of the appropriate timer to digits and add the + // digits to the decoded buffer in place of the control character. + // "EVENT timer" + osSyncPrintf("\nEVENTタイマー = "); + digits[0] = digits[1] = digits[2] = 0; + if (temp_s2 == MESSAGE_RACE_TIME) { + digits[3] = gSaveContext.timer1Value; + } else { + digits[3] = gSaveContext.timer2Value; + } + + while (digits[3] >= 60) { + digits[1]++; + if (digits[1] >= 10) { + digits[0]++; + digits[1] -= 10; + } + digits[3] -= 60; + } + while (digits[3] >= 10) { + digits[2]++; + digits[3] -= 10; + } + + for (i = 0; i < 4; i++) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + decodedBufPos++; + if (i == 1) { + Font_LoadChar(font, '"' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = '"'; + decodedBufPos++; + } else if (i == 3) { + Font_LoadChar(font, '"' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = '"'; + } + } + } else if (temp_s2 == MESSAGE_POINTS) { + // Convert the values of the current minigame score to digits and + // add the digits to the decoded buffer in place of the control character. + // "Yabusame score" + osSyncPrintf("\n流鏑馬スコア = %d\n", gSaveContext.minigameScore); + digits[0] = digits[1] = digits[2] = 0; + digits[3] = gSaveContext.minigameScore; + + while (digits[3] >= 1000) { + digits[0]++; + digits[3] -= 1000; + } + while (digits[3] >= 100) { + digits[1]++; + digits[3] -= 100; + } + while (digits[3] >= 10) { + digits[2]++; + digits[3] -= 10; + } + + loadChar = false; + for (i = 0; i < 4; i++) { + if (i == 3 || digits[i] != 0) { + loadChar = true; + } + if (loadChar) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + charTexIdx += FONT_CHAR_TEX_SIZE; + decodedBufPos++; + } + } + decodedBufPos--; + } else if (temp_s2 == MESSAGE_TOKENS) { + // Convert the current number of collected gold skulltula tokens to digits and + // add the digits to the decoded buffer in place of the control character. + // "Total number of gold stars" + osSyncPrintf("\n金スタ合計数 = %d", gSaveContext.inventory.gsTokens); + digits[0] = digits[1] = 0; + digits[2] = gSaveContext.inventory.gsTokens; + + while (digits[2] >= 100) { + digits[0]++; + digits[2] -= 100; + } + while (digits[2] >= 10) { + digits[1]++; + digits[2] -= 10; + } + + loadChar = false; + for (i = 0; i < 3; i++) { + if (i == 2 || digits[i] != 0) { + loadChar = true; + } + if (loadChar) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + charTexIdx += FONT_CHAR_TEX_SIZE; + osSyncPrintf("%x(%x) ", digits[i] + '0' - ' ', digits[i]); + decodedBufPos++; + } + } + decodedBufPos--; + } else if (temp_s2 == MESSAGE_FISH_INFO) { + // "Fishing hole fish size" + osSyncPrintf("\n釣り堀魚サイズ = "); + digits[0] = 0; + digits[1] = gSaveContext.minigameScore; + + while (digits[1] >= 10) { + digits[0]++; + digits[1] -= 10; + } + + for (i = 0; i < 2; i++) { + if (i == 1 || digits[i] != 0) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + charTexIdx += FONT_CHAR_TEX_SIZE; + osSyncPrintf("%x(%x) ", digits[i] + '0' - ' ', digits[i]); + decodedBufPos++; + } + } + decodedBufPos--; + } else if (temp_s2 == MESSAGE_HIGHSCORE) { + phi_s0_3 = HIGH_SCORE((u8)font->msgBuf[++msgCtx->msgBufPos]); + // "Highscore" + osSyncPrintf("ランキング=%d\n", font->msgBuf[msgCtx->msgBufPos]); + if ((font->msgBuf[msgCtx->msgBufPos] & 0xFF) == 2) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + phi_s0_3 &= 0x7F; + } else { + osSyncPrintf("HI_SCORE( kanfont->mbuff.nes_mes_buf[message->rdp] & 0xff000000 ) = %x\n", + HIGH_SCORE(font->msgBufWide[msgCtx->msgBufPos] & 0xFF000000)); + phi_s0_3 = ((HIGH_SCORE((u8)font->msgBuf[msgCtx->msgBufPos]) & 0xFF000000) >> 0x18) & 0x7F; + } + phi_s0_3 = SQ((f32)phi_s0_3) * 0.0036f + 0.5f; + osSyncPrintf("score=%d\n", phi_s0_3); + } + switch (font->msgBuf[msgCtx->msgBufPos] & 0xFF) { + case HS_HBA: + case HS_POE_POINTS: + case HS_FISHING: + digits[0] = digits[1] = digits[2] = 0; + digits[3] = phi_s0_3; + + while (digits[3] >= 1000) { + digits[0]++; + digits[3] -= 1000; + } + while (digits[3] >= 100) { + digits[1]++; + digits[3] -= 100; + } + while (digits[3] >= 10) { + digits[2]++; + digits[3] -= 10; + } + if (temp_s2) {} + + loadChar = false; + for (i = 0; i < 4; i++) { + if (i == 3 || digits[i] != 0) { + loadChar = true; + } + if (loadChar) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + charTexIdx += FONT_CHAR_TEX_SIZE; + decodedBufPos++; + } + } + decodedBufPos--; + break; + case HS_UNK_05: + break; + case HS_HORSE_RACE: + case HS_MARATHON: + case HS_DAMPE_RACE: + digits[0] = digits[1] = digits[2] = 0; + digits[3] = phi_s0_3; + + while (digits[3] >= 60) { + digits[1]++; + if (digits[1] >= 10) { + digits[0]++; + digits[1] -= 10; + } + digits[3] -= 60; + } + while (digits[3] >= 10) { + digits[2]++; + digits[3] -= 10; + } + + for (i = 0; i < 4; i++) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + decodedBufPos++; + if (i == 1) { + Font_LoadChar(font, '"' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = '"'; + decodedBufPos++; + } else if (i == 3) { + Font_LoadChar(font, '"' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = '"'; + } + } + break; + } + } else if (temp_s2 == MESSAGE_TIME) { + // "Zelda time" + osSyncPrintf("\nゼルダ時間 = "); + digits[0] = 0; + timeInSeconds = gSaveContext.dayTime * (24.0f * 60.0f / 0x10000); + + digits[1] = timeInSeconds / 60.0f; + while (digits[1] >= 10) { + digits[0]++; + digits[1] -= 10; + } + digits[2] = 0; + digits[3] = (s16)timeInSeconds % 60; + while (digits[3] >= 10) { + digits[2]++; + digits[3] -= 10; + } + + for (i = 0; i < 4; i++) { + Font_LoadChar(font, digits[i] + '0' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = digits[i] + '0'; + decodedBufPos++; + if (i == 1) { + Font_LoadChar(font, ':' - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + msgCtx->msgBufDecoded[decodedBufPos] = ':'; + decodedBufPos++; + } + } + decodedBufPos--; + } else if (temp_s2 == MESSAGE_ITEM_ICON) { + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[msgCtx->msgBufPos + 1]; + osSyncPrintf("ITEM_NO=(%d) (%d)\n", msgCtx->msgBufDecoded[decodedBufPos], + font->msgBuf[msgCtx->msgBufPos + 1]); + Message_LoadItemIcon(globalCtx, font->msgBuf[msgCtx->msgBufPos + 1], R_TEXTBOX_Y + 10); + } else if (temp_s2 == MESSAGE_BACKGROUND) { + msgCtx->textboxBackgroundIdx = font->msgBuf[msgCtx->msgBufPos + 1] * 2; + msgCtx->textboxBackgroundForeColorIdx = (font->msgBuf[msgCtx->msgBufPos + 2] & 0xF0) >> 4; + msgCtx->textboxBackgroundBackColorIdx = font->msgBuf[msgCtx->msgBufPos + 2] & 0xF; + msgCtx->textboxBackgroundYOffsetIdx = (font->msgBuf[msgCtx->msgBufPos + 3] & 0xF0) >> 4; + msgCtx->textboxBackgroundUnkArg = font->msgBuf[msgCtx->msgBufPos + 3] & 0xF; + + memcpy((uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE, + ResourceMgr_LoadTexByName(gRedMessageXLeftTex), 0x900); + memcpy((uintptr_t)msgCtx->textboxSegment + MESSAGE_STATIC_TEX_SIZE + 0x900, + ResourceMgr_LoadTexByName(gRedMessageXRightTex), 0x900); + + msgCtx->msgBufPos += 3; + R_TEXTBOX_BG_YPOS = R_TEXTBOX_Y + 8; + numLines = 2; + R_TEXT_INIT_XPOS = 50; + } else if (temp_s2 == MESSAGE_COLOR) { + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos]; + } else if (temp_s2 == MESSAGE_NEWLINE) { + numLines++; + } else if (temp_s2 != MESSAGE_QUICKTEXT_ENABLE && temp_s2 != MESSAGE_QUICKTEXT_DISABLE && + temp_s2 != MESSAGE_AWAIT_BUTTON_PRESS && temp_s2 != MESSAGE_OCARINA && + temp_s2 != MESSAGE_PERSISTENT && temp_s2 != MESSAGE_UNSKIPPABLE) { + if (temp_s2 == MESSAGE_FADE) { + sTextFade = true; + osSyncPrintf("NZ_TIMER_END (key_off_flag=%d)\n", sTextFade); + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos]; + } else if (temp_s2 == MESSAGE_FADE2) { + sTextFade = true; + osSyncPrintf("NZ_BGM (key_off_flag=%d)\n", sTextFade); + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos]; + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos]; + } else if (temp_s2 == MESSAGE_SHIFT || temp_s2 == MESSAGE_TEXT_SPEED) { + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos] & 0xFF; + } else if (temp_s2 == MESSAGE_SFX) { + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos]; + msgCtx->msgBufDecoded[++decodedBufPos] = font->msgBuf[++msgCtx->msgBufPos]; + } else if (temp_s2 == MESSAGE_TWO_CHOICE) { + msgCtx->choiceNum = 2; + } else if (temp_s2 == MESSAGE_THREE_CHOICE) { + msgCtx->choiceNum = 3; + } else if (temp_s2 != ' ') { + Font_LoadChar(font, temp_s2 - ' ', charTexIdx); + charTexIdx += FONT_CHAR_TEX_SIZE; + } + } + decodedBufPos++; + msgCtx->msgBufPos++; + } +} + +extern const char* msgStaticTbl[]; + +void Message_OpenText(GlobalContext* globalCtx, u16 textId) { + static s16 messageStaticIndices[] = { 0, 1, 3, 2 }; + MessageContext* msgCtx = &globalCtx->msgCtx; + Font* font = &msgCtx->font; + s16 textBoxType; + + if (msgCtx->msgMode == MSGMODE_NONE) { + gSaveContext.unk_13EE = gSaveContext.unk_13EA; + } + if (YREG(15) == 0x10) { + Interface_ChangeAlpha(5); + } + + sMessageHasSetSfx = D_8014B2F4 = sTextboxSkipped = sTextIsCredits = 0; + + if (textId >= 0x0500 && textId < 0x0600) { // text ids 0500 to 0600 are reserved for credits + sTextIsCredits = true; + R_TEXT_CHAR_SCALE = 85; + R_TEXT_LINE_SPACING = 6; + R_TEXT_INIT_XPOS = 20; + YREG(1) = 48; + } else { + R_TEXT_CHAR_SCALE = 75; + R_TEXT_LINE_SPACING = 12; + R_TEXT_INIT_XPOS = 65; + } + if (textId == 0xC2 || textId == 0xFA) { + // Increments text id based on piece of heart count, assumes the piece of heart text is all + // in order and that you don't have more than the intended amount of heart pieces. + textId += (gSaveContext.inventory.questItems & 0xF0000000 & 0xF0000000) >> 0x1C; + } else if (msgCtx->textId == 0xC && CHECK_OWNED_EQUIP(EQUIP_SWORD, 2)) { + textId = 0xB; // Traded Giant's Knife for Biggoron Sword + } else if (msgCtx->textId == 0xB4 && (gSaveContext.eventChkInf[9] & 0x40)) { + textId = 0xB5; // Destroyed Gold Skulltula + } + // Ocarina Staff + Dialog + if (textId == 0x4077 || // Pierre? + textId == 0x407A || // Pierre? + textId == 0x2061 || // Learning Epona's Song + textId == 0x5035 || // Guru-Guru in Windmill + textId == 0x40AC) { // Ocarina Frog Minigame + Interface_ChangeAlpha(1); + } + msgCtx->textId = textId; + + if (textId == 0x2030) { // Talking to Ingo as adult in Lon Lon Ranch for the first time before freeing Epona + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("???????????????? z_message.c ??????????????????\n"); + osSyncPrintf(VT_RST); + gSaveContext.eventInf[0] = gSaveContext.eventInf[1] = gSaveContext.eventInf[2] = gSaveContext.eventInf[3] = 0; + } + + if (sTextIsCredits) { + Message_FindCreditsMessage(globalCtx, textId); + msgCtx->msgLength = font->msgLength; + char* src = (uintptr_t)font->msgOffset; + memcpy(font->msgBuf, src, font->msgLength); + + // OTRTODO + //DmaMgr_SendRequest1(font->msgBuf, (uintptr_t)(_staff_message_data_staticSegmentRomStart + 4 + font->msgOffset), + //font->msgLength, "../z_message_PAL.c", 1954); + } else { + if (gSaveContext.language == LANGUAGE_ENG) + { + Message_FindMessage(globalCtx, textId); + msgCtx->msgLength = font->msgLength; + char* src = (uintptr_t)font->msgOffset; + memcpy(font->msgBuf, src, font->msgLength); + } else if (gSaveContext.language == LANGUAGE_GER) { + // OTRTODO + //Message_FindMessage(globalCtx, textId); + //msgCtx->msgLength = font->msgLength; + //DmaMgr_SendRequest1(font->msgBuf, (uintptr_t)(_ger_message_data_staticSegmentRomStart + font->msgOffset), + //font->msgLength, "../z_message_PAL.c", 1978); + } else + { + // OTRTODO + //Message_FindMessage(globalCtx, textId); + //msgCtx->msgLength = font->msgLength; + //DmaMgr_SendRequest1(font->msgBuf, (uintptr_t)(_fra_message_data_staticSegmentRomStart + font->msgOffset), + //font->msgLength, "../z_message_PAL.c", 1990); + } + } + + msgCtx->textBoxProperties = font->charTexBuf[0]; + msgCtx->textBoxType = msgCtx->textBoxProperties >> 4; + msgCtx->textBoxPos = msgCtx->textBoxProperties & 0xF; + textBoxType = msgCtx->textBoxType; + // "Text Box Type" + osSyncPrintf("吹き出し種類=%d\n", msgCtx->textBoxType); + if (textBoxType < TEXTBOX_TYPE_NONE_BOTTOM) { + memcpy(msgCtx->textboxSegment, ResourceMgr_LoadTexByName(msgStaticTbl[messageStaticIndices[textBoxType]]), MESSAGE_STATIC_TEX_SIZE); + if (textBoxType == TEXTBOX_TYPE_BLACK) { + msgCtx->textboxColorRed = 0; + msgCtx->textboxColorGreen = 0; + msgCtx->textboxColorBlue = 0; + } else if (textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textboxColorRed = 70; + msgCtx->textboxColorGreen = 50; + msgCtx->textboxColorBlue = 30; + } else if (textBoxType == TEXTBOX_TYPE_BLUE) { + msgCtx->textboxColorRed = 0; + msgCtx->textboxColorGreen = 10; + msgCtx->textboxColorBlue = 50; + } else { + msgCtx->textboxColorRed = 255; + msgCtx->textboxColorGreen = 0; + msgCtx->textboxColorBlue = 0; + } + if (textBoxType == TEXTBOX_TYPE_WOODEN) { + msgCtx->textboxColorAlphaTarget = 230; + } else if (textBoxType == TEXTBOX_TYPE_OCARINA) { + msgCtx->textboxColorAlphaTarget = 180; + } else { + msgCtx->textboxColorAlphaTarget = 170; + } + msgCtx->textboxColorAlphaCurrent = 0; + } + msgCtx->choiceNum = msgCtx->textUnskippable = msgCtx->textboxEndType = 0; + msgCtx->msgBufPos = msgCtx->unk_E3D0 = msgCtx->textDrawPos = 0; +} + +void Message_StartTextbox(GlobalContext* globalCtx, u16 textId, Actor* actor) { + MessageContext* msgCtx = &globalCtx->msgCtx; + + osSyncPrintf(VT_FGCOL(BLUE)); + // "Message" + osSyncPrintf("めっせーじ=%x(%d)\n", textId, actor); + osSyncPrintf(VT_RST); + + msgCtx->ocarinaAction = 0xFFFF; + Message_OpenText(globalCtx, textId); + msgCtx->talkActor = actor; + msgCtx->msgMode = MSGMODE_TEXT_START; + msgCtx->stateTimer = 0; + msgCtx->textDelayTimer = 0; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; +} + +void Message_ContinueTextbox(GlobalContext* globalCtx, u16 textId) { + MessageContext* msgCtx = &globalCtx->msgCtx; + + osSyncPrintf(VT_FGCOL(GREEN)); + // "Message" + osSyncPrintf("めっせーじ=%x message->msg_data\n", textId, msgCtx->msgLength); + osSyncPrintf(VT_RST); + + msgCtx->msgLength = 0; + Message_OpenText(globalCtx, textId); + msgCtx->msgMode = MSGMODE_NONE; + msgCtx->textboxColorAlphaCurrent = msgCtx->textboxColorAlphaTarget; + msgCtx->msgMode = MSGMODE_TEXT_CONTINUING; + msgCtx->stateTimer = 3; + msgCtx->textboxEndType = msgCtx->msgBufPos = msgCtx->unk_E3D0 = msgCtx->textDrawPos = msgCtx->textDelayTimer = 0; + msgCtx->textColorAlpha = 255; + + if (YREG(31) == 0 && globalCtx->interfaceCtx.unk_1FA == 0) { + Interface_SetDoAction(globalCtx, DO_ACTION_NEXT); + } + msgCtx->textboxColorAlphaCurrent = msgCtx->textboxColorAlphaTarget; +} + +void Message_StartOcarina(GlobalContext* globalCtx, u16 ocarinaActionId) { + static u16 sOcarinaSongFlagsMap[] = { + (1 << OCARINA_SONG_MINUET), (1 << OCARINA_SONG_BOLERO), (1 << OCARINA_SONG_SERENADE), + (1 << OCARINA_SONG_REQUIEM), (1 << OCARINA_SONG_NOCTURNE), (1 << OCARINA_SONG_PRELUDE), + (1 << OCARINA_SONG_LULLABY), (1 << OCARINA_SONG_EPONAS), (1 << OCARINA_SONG_SARIAS), + (1 << OCARINA_SONG_SUNS), (1 << OCARINA_SONG_TIME), (1 << OCARINA_SONG_STORMS), + (1 << OCARINA_SONG_SCARECROW), + }; + MessageContext* msgCtx = &globalCtx->msgCtx; + s32 textId; + s16 j; + s16 i; + s16 noStop; + s32 k; + + osSyncPrintf(VT_FGCOL(GREEN)); + + for (i = sOcarinaSongBitFlags = 0; i < (QUEST_KOKIRI_EMERALD - QUEST_SONG_MINUET); i++) { + if (CHECK_QUEST_ITEM(QUEST_SONG_MINUET + i)) { + osSyncPrintf("ocarina_check_bit[%d]=%x\n", i, sOcarinaSongFlagsMap[i]); + sOcarinaSongBitFlags |= sOcarinaSongFlagsMap[i]; + } + } + if (gSaveContext.scarecrowSpawnSongSet) { + sOcarinaSongBitFlags |= (1 << OCARINA_SONG_SCARECROW); + } + osSyncPrintf("ocarina_bit = %x\n", sOcarinaSongBitFlags); + osSyncPrintf(VT_RST); + + sHasSunsSong = CHECK_QUEST_ITEM(QUEST_SONG_SUN); + msgCtx->ocarinaStaff = Audio_OcaGetRecordingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + sOcarinaNoteBufLen = 0; + Message_ResetOcarinaNoteState(); + sLastPlayedSong = msgCtx->unk_E3F2 = msgCtx->lastOcaNoteIdx = 0xFF; + + // "Ocarina Number" + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ オカリナ番号=%d(%d) ☆☆☆☆☆\n" VT_RST, ocarinaActionId, 2); + noStop = false; + if (ocarinaActionId >= 0x893) { + Message_OpenText(globalCtx, ocarinaActionId); // You played the [song name] + textId = ocarinaActionId + 0x86E; + } else if (ocarinaActionId == OCARINA_ACTION_MEMORY_GAME) { + msgCtx->ocarinaAction = ocarinaActionId; + Message_OpenText(globalCtx, 0x86D); // Play using [A] and [C]. + textId = ocarinaActionId + 0x86E; + } else if (ocarinaActionId == OCARINA_ACTION_FREE_PLAY || ocarinaActionId >= OCARINA_ACTION_CHECK_SARIA) { + osSyncPrintf("ocarina_set 000000000000000000 = %d\n", ocarinaActionId); + msgCtx->ocarinaAction = ocarinaActionId; + if (ocarinaActionId >= OCARINA_ACTION_CHECK_SARIA && ocarinaActionId <= OCARINA_ACTION_CHECK_STORMS) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + if (ocarinaActionId == OCARINA_ACTION_SCARECROW_PLAYBACK) { + Message_OpenText(globalCtx, 0x86F); // Ocarina + textId = ocarinaActionId + 0x86E; + } else { + Message_OpenText(globalCtx, 0x86E); // Play using [A] and [C]; [B] to Stop. + textId = ocarinaActionId + 0x86E; + } + } else { + msgCtx->ocarinaAction = ocarinaActionId; + noStop = true; + if (ocarinaActionId >= OCARINA_ACTION_PLAYBACK_MINUET) { + osSyncPrintf("222222222\n"); + Message_OpenText(globalCtx, 0x86D); // Play using [A] and [C]. + textId = ocarinaActionId + 0x86E; + } else { + osSyncPrintf("333333333\n"); + textId = ocarinaActionId + 0x86E; + Message_OpenText(globalCtx, textId); // Play using [A] and [C]; [B] to Stop. + } + } + msgCtx->talkActor = NULL; + // "Ocarina Mode" + osSyncPrintf("オカリナモード = %d (%x)\n", msgCtx->ocarinaAction, textId); + msgCtx->textDelayTimer = 0; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + R_TEXTBOX_X = 34; + R_TEXTBOX_Y = 142; + R_TEXTBOX_TEXWIDTH = 512; + R_TEXTBOX_TEXHEIGHT = 512; + R_TEXTBOX_WIDTH = 256; + R_TEXTBOX_HEIGHT = 64; + msgCtx->stateTimer = 0; + if (YREG(12) != 0) { + msgCtx->msgMode = MSGMODE_TEXT_NEXT_MSG; + } else { + msgCtx->stateTimer = 2; + msgCtx->msgMode = MSGMODE_TEXT_CONTINUING; + } + msgCtx->textboxColorAlphaCurrent = msgCtx->textboxColorAlphaTarget; + if (noStop == false) { + Interface_LoadActionLabelB(globalCtx, DO_ACTION_STOP); + noStop = gSaveContext.unk_13EA; + Interface_ChangeAlpha(0xA); + gSaveContext.unk_13EA = noStop; + } + // "Music Performance Start" + osSyncPrintf("演奏開始\n"); + if (ocarinaActionId == OCARINA_ACTION_FREE_PLAY || ocarinaActionId == OCARINA_ACTION_CHECK_NOWARP) { + msgCtx->msgMode = MSGMODE_OCARINA_STARTING; + msgCtx->textBoxType = 0x63; + } else if (ocarinaActionId == OCARINA_ACTION_FROGS) { + msgCtx->msgMode = MSGMODE_FROGS_START; + msgCtx->textBoxType = TEXTBOX_TYPE_BLUE; + } else if (ocarinaActionId == OCARINA_ACTION_MEMORY_GAME) { + Interface_ChangeAlpha(1); + Message_Decode(globalCtx); + msgCtx->msgMode = MSGMODE_MEMORY_GAME_START; + } else if (ocarinaActionId == OCARINA_ACTION_SCARECROW_LONG_PLAYBACK) { + // "?????Recording Playback / Recording Playback / Recording Playback / Recording Playback -> " + osSyncPrintf("?????録音再生 録音再生 録音再生 録音再生 -> "); + Audio_OcaSetInstrument(1); + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + sOcarinaNoteBufPos = sOcarinaNoteBufLen = 0; + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos; + Message_ResetOcarinaNoteState(); + msgCtx->stateTimer = 3; + msgCtx->msgMode = MSGMODE_SCARECROW_LONG_PLAYBACK; + Audio_OcaSetSongPlayback(OCARINA_SONG_SCARECROW_LONG + 1, 1); + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(1); + } + for (k = 0, j = 0; j < 48; j++, k += 0x80) { + func_8006EE50(&globalCtx->msgCtx.font, 0x8140, k); + } +} + +void func_8010BD58(GlobalContext* globalCtx, u16 ocarinaActionId) { + globalCtx->msgCtx.unk_E40E = 0; + Message_StartOcarina(globalCtx, ocarinaActionId); +} + +void func_8010BD88(GlobalContext* globalCtx, u16 ocarinaActionId) { + globalCtx->msgCtx.unk_E40E = 1; + Message_StartOcarina(globalCtx, ocarinaActionId); +} + +u8 Message_GetState(MessageContext* msgCtx) { + u8 state; + + if (msgCtx->msgLength == 0) { + state = TEXT_STATE_NONE; + } else if (msgCtx->msgMode == MSGMODE_TEXT_DONE) { + if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_HAS_NEXT) { + state = TEXT_STATE_DONE_HAS_NEXT; + } else if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_2_CHOICE || + msgCtx->textboxEndType == TEXTBOX_ENDTYPE_3_CHOICE) { + state = TEXT_STATE_CHOICE; + } else if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_EVENT || + msgCtx->textboxEndType == TEXTBOX_ENDTYPE_PERSISTENT) { + state = TEXT_STATE_EVENT; + } else if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_FADING) { + state = TEXT_STATE_DONE_FADING; + } else { + state = TEXT_STATE_DONE; + } + } else if (msgCtx->msgMode == MSGMODE_TEXT_AWAIT_NEXT) { + state = TEXT_STATE_AWAITING_NEXT; + } else if (msgCtx->msgMode == MSGMODE_SONG_DEMONSTRATION_DONE) { + state = TEXT_STATE_SONG_DEMO_DONE; + } else if (msgCtx->ocarinaMode == OCARINA_MODE_03) { + state = TEXT_STATE_8; + } else if (msgCtx->msgMode == MSGMODE_OCARINA_AWAIT_INPUT) { + state = TEXT_STATE_9; + } else if (msgCtx->msgMode == MSGMODE_TEXT_CLOSING && msgCtx->stateTimer == 1) { + state = TEXT_STATE_CLOSING; + } else { + state = TEXT_STATE_DONE_FADING; + } + return state; +} + +void Message_DrawTextBox(GlobalContext* globalCtx, Gfx** p) { + MessageContext* msgCtx = &globalCtx->msgCtx; + Gfx* gfx = *p; + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, msgCtx->textboxColorRed, msgCtx->textboxColorGreen, msgCtx->textboxColorBlue, + msgCtx->textboxColorAlphaCurrent); + + if (!(msgCtx->textBoxType) || msgCtx->textBoxType == TEXTBOX_TYPE_BLUE) { + gDPLoadTextureBlock_4b(gfx++, msgCtx->textboxSegment, G_IM_FMT_I, 128, 64, 0, G_TX_MIRROR, G_TX_NOMIRROR, 7, 0, + G_TX_NOLOD, G_TX_NOLOD); + } else { + if (msgCtx->textBoxType == TEXTBOX_TYPE_OCARINA) { + gDPSetEnvColor(gfx++, 0, 0, 0, 255); + } else { + gDPSetEnvColor(gfx++, 50, 20, 0, 255); + } + + gDPLoadTextureBlock_4b(gfx++, msgCtx->textboxSegment, G_IM_FMT_IA, 128, 64, 0, G_TX_MIRROR, G_TX_MIRROR, 7, 0, + G_TX_NOLOD, G_TX_NOLOD); + } + + gSPTextureRectangle(gfx++, R_TEXTBOX_X << 2, R_TEXTBOX_Y << 2, (R_TEXTBOX_X + R_TEXTBOX_WIDTH) << 2, + (R_TEXTBOX_Y + R_TEXTBOX_HEIGHT) << 2, G_TX_RENDERTILE, 0, 0, R_TEXTBOX_TEXWIDTH << 1, + R_TEXTBOX_TEXHEIGHT << 1); + + // Draw treble clef + if (msgCtx->textBoxType == TEXTBOX_TYPE_OCARINA) { + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, + 0); + gDPSetPrimColor(gfx++, 0, 0, 255, 100, 0, 255); + gDPLoadTextureBlock_4b(gfx++, gOcarinaTrebleClefTex, G_IM_FMT_I, 16, 32, 0, G_TX_MIRROR, G_TX_MIRROR, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle(gfx++, R_TEXTBOX_CLEF_XPOS << 2, R_TEXTBOX_CLEF_YPOS << 2, (R_TEXTBOX_CLEF_XPOS + 16) << 2, + (R_TEXTBOX_CLEF_YPOS + 32) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + *p = gfx; +} + +void Message_SetView(View* view) { + SET_FULLSCREEN_VIEWPORT(view); + func_800AB2C4(view); +} + +/** + * Draws the textbox in full and updates ocarina state + */ +void Message_DrawMain(GlobalContext* globalCtx, Gfx** p) { + static s16 sOcarinaEffectActorIds[] = { + ACTOR_OCEFF_WIPE3, ACTOR_OCEFF_WIPE2, ACTOR_OCEFF_WIPE, ACTOR_OCEFF_SPOT, + ACTOR_OCEFF_WIPE, ACTOR_OCEFF_STORM, ACTOR_OCEFF_WIPE4, + }; + static s16 sOcarinaEffectActorParams[] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000 }; + static void* sOcarinaNoteTextures[] = { + gOcarinaATex, gOcarinaCDownTex, gOcarinaCRightTex, gOcarinaCLeftTex, gOcarinaCUpTex, + }; + static s16 sOcarinaNoteAPrimColors[][3] = { + { 80, 255, 150 }, + { 100, 255, 200 }, + }; + static s16 sOcarinaNoteAEnvColors[][3] = { + { 10, 10, 10 }, + { 50, 255, 50 }, + }; + static s16 sOcarinaNoteCPrimColors[][3] = { + { 255, 255, 50 }, + { 255, 255, 180 }, + }; + static s16 sOcarinaNoteCEnvColors[][3] = { + { 10, 10, 10 }, + { 110, 110, 50 }, + }; + static s16 sOcarinaNoteFlashTimer = 12; + static s16 sOcarinaNoteFlashColorIdx = 1; + static s16 sOcarinaSongFanfares[] = { + NA_BGM_OCA_MINUET, NA_BGM_OCA_BOLERO, NA_BGM_OCA_SERENADE, NA_BGM_OCA_REQUIEM, + NA_BGM_OCA_NOCTURNE, NA_BGM_OCA_LIGHT, NA_BGM_OCA_SARIA, NA_BGM_OCA_EPONA, + NA_BGM_OCA_ZELDA, NA_BGM_OCA_SUNS, NA_BGM_OCA_TIME, NA_BGM_OCA_STORM, + }; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + MessageContext* msgCtx = &globalCtx->msgCtx; + u16 noteBufPos; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + Gfx* gfx = *p; + s16 r; + s16 g; + s16 b; + u16 i; + u16 notePosX; + u16 pad1; + u16 j; + + gSPSegment(gfx++, 0x02, globalCtx->interfaceCtx.parameterSegment); + gSPSegment(gfx++, 0x07, msgCtx->textboxSegment); + + if (msgCtx->msgLength != 0) { + if (msgCtx->ocarinaAction != OCARINA_ACTION_FROGS && msgCtx->msgMode != MSGMODE_SONG_PLAYED_ACT && + msgCtx->msgMode >= MSGMODE_TEXT_BOX_GROWING && msgCtx->msgMode < MSGMODE_TEXT_CLOSING && + msgCtx->textBoxType < TEXTBOX_TYPE_NONE_BOTTOM) { + Message_SetView(&msgCtx->view); + func_8009457C(&gfx); + Message_DrawTextBox(globalCtx, &gfx); + } + + func_8009457C(&gfx); + + gDPSetAlphaCompare(gfx++, G_AC_NONE); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0); + + bool isB_Held = CVar_GetS32("gFastText", 0) != 0 ? CHECK_BTN_ALL(globalCtx->state.input[0].cur.button, BTN_B) + : CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B); + + switch (msgCtx->msgMode) { + case MSGMODE_TEXT_START: + case MSGMODE_TEXT_BOX_GROWING: + case MSGMODE_TEXT_STARTING: + case MSGMODE_TEXT_NEXT_MSG: + break; + case MSGMODE_TEXT_CONTINUING: + if (msgCtx->stateTimer == 1) { + for (j = 0, i = 0; i < 48; i++, j += 0x80) { + func_8006EE50(&globalCtx->msgCtx.font, 0x8140, j); + } + Message_DrawText(globalCtx, &gfx); + } + break; + case MSGMODE_TEXT_DISPLAYING: + case MSGMODE_TEXT_DELAYED_BREAK: + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_TEXT_AWAIT_INPUT: + case MSGMODE_TEXT_AWAIT_NEXT: + Message_DrawText(globalCtx, &gfx); + Message_DrawTextboxIcon(globalCtx, &gfx, R_TEXTBOX_END_XPOS, R_TEXTBOX_END_YPOS); + break; + case MSGMODE_OCARINA_STARTING: + case MSGMODE_SONG_DEMONSTRATION_STARTING: + case MSGMODE_SONG_PLAYBACK_STARTING: + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + Message_ResetOcarinaNoteState(); + sOcarinaNoteFlashTimer = 3; + sOcarinaNoteFlashColorIdx = 1; + if (msgCtx->msgMode == MSGMODE_OCARINA_STARTING) { + if (msgCtx->ocarinaAction == OCARINA_ACTION_UNK_0 || + msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY || + msgCtx->ocarinaAction == OCARINA_ACTION_SCARECROW_RECORDING || + msgCtx->ocarinaAction == OCARINA_ACTION_CHECK_NOWARP || + msgCtx->ocarinaAction >= OCARINA_ACTION_CHECK_SARIA) { + if (msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY || + msgCtx->ocarinaAction == OCARINA_ACTION_CHECK_NOWARP) { + func_800ECC04(sOcarinaSongBitFlags + 0xC000); + } else { + // "On Stage Performance" + osSyncPrintf("台上演奏\n"); + func_800ECC04(sOcarinaSongBitFlags); + } + } else { + osSyncPrintf("Na_StartOcarinaSinglePlayCheck2( message->ocarina_no );\n"); + func_800ECC04((1 << msgCtx->ocarinaAction) + 0x8000); + } + msgCtx->msgMode = MSGMODE_OCARINA_PLAYING; + } else if (msgCtx->msgMode == MSGMODE_SONG_DEMONSTRATION_STARTING) { + msgCtx->stateTimer = 20; + msgCtx->msgMode = MSGMODE_SONG_DEMONSTRATION_SELECT_INSTRUMENT; + } else { + func_800ECC04((1 << (msgCtx->ocarinaAction + 0x11)) + 0x8000); + // "Performance Check" + osSyncPrintf("演奏チェック=%d\n", msgCtx->ocarinaAction - OCARINA_ACTION_PLAYBACK_MINUET); + msgCtx->msgMode = MSGMODE_SONG_PLAYBACK; + } + if (msgCtx->ocarinaAction != OCARINA_ACTION_FREE_PLAY && + msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP) { + Message_DrawText(globalCtx, &gfx); + } + break; + case MSGMODE_OCARINA_PLAYING: + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + if (msgCtx->ocarinaStaff->pos) { + osSyncPrintf("locate=%d onpu_pt=%d\n", msgCtx->ocarinaStaff->pos, sOcarinaNoteBufPos); + if (msgCtx->ocarinaStaff->pos == 1 && sOcarinaNoteBufPos == 8) { + sOcarinaNoteBufPos = 0; + } + if (sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + msgCtx->lastOcaNoteIdx = sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = + msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + } + msgCtx->lastPlayedSong = msgCtx->ocarinaStaff->state; + if (msgCtx->ocarinaStaff->state < OCARINA_SONG_MEMORY_GAME) { + if (msgCtx->ocarinaStaff->state == OCARINA_SONG_SCARECROW || + CHECK_QUEST_ITEM(QUEST_SONG_MINUET + gOcarinaSongItemMap[msgCtx->ocarinaStaff->state])) { + sLastPlayedSong = msgCtx->unk_E3F2 = msgCtx->lastPlayedSong = msgCtx->ocarinaStaff->state; + msgCtx->msgMode = MSGMODE_OCARINA_CORRECT_PLAYBACK; + msgCtx->stateTimer = 20; + if (msgCtx->ocarinaAction == OCARINA_ACTION_CHECK_NOWARP) { + if (msgCtx->ocarinaStaff->state < OCARINA_SONG_SARIAS || + msgCtx->ocarinaStaff->state == OCARINA_SONG_SCARECROW) { + Audio_OcaSetInstrument(0); + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->msgMode = MSGMODE_OCARINA_STARTING; + } else { + // "Ocarina_Flog Correct Example Performance" + osSyncPrintf("Ocarina_Flog 正解模範演奏=%x\n", msgCtx->lastPlayedSong); + Message_ContinueTextbox(globalCtx, 0x86F); // Ocarina + msgCtx->msgMode = MSGMODE_SONG_PLAYED; + msgCtx->textBoxType = TEXTBOX_TYPE_OCARINA; + msgCtx->stateTimer = 10; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + Interface_ChangeAlpha(1); + } + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_CHECK_SCARECROW) { + if (msgCtx->ocarinaStaff->state < OCARINA_SONG_SCARECROW) { + Audio_OcaSetInstrument(0); + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->stateTimer = 10; + msgCtx->msgMode = MSGMODE_OCARINA_FAIL; + } else { + // "Ocarina_Flog Correct Example Performance" + osSyncPrintf("Ocarina_Flog 正解模範演奏=%x\n", msgCtx->lastPlayedSong); + Message_ContinueTextbox(globalCtx, 0x86F); // Ocarina + msgCtx->msgMode = MSGMODE_SONG_PLAYED; + msgCtx->textBoxType = TEXTBOX_TYPE_OCARINA; + msgCtx->stateTimer = 10; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + Interface_ChangeAlpha(1); + } + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY) { + // "Ocarina_Free Correct Example Performance" + osSyncPrintf("Ocarina_Free 正解模範演奏=%x\n", msgCtx->lastPlayedSong); + Message_ContinueTextbox(globalCtx, 0x86F); // Ocarina + msgCtx->msgMode = MSGMODE_SONG_PLAYED; + msgCtx->textBoxType = TEXTBOX_TYPE_OCARINA; + msgCtx->stateTimer = 10; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + Interface_ChangeAlpha(1); + } else { + Audio_OcaSetInstrument(0); + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->msgMode = MSGMODE_OCARINA_STARTING; + } + } else if (msgCtx->ocarinaStaff->state == 0xFF) { + Audio_OcaSetInstrument(0); + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->stateTimer = 10; + msgCtx->msgMode = MSGMODE_OCARINA_FAIL; + } else if (isB_Held) { + Audio_OcaSetInstrument(0); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + Message_CloseTextbox(globalCtx); + } + if (msgCtx->ocarinaAction != OCARINA_ACTION_FREE_PLAY && + msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP) { + Message_DrawText(globalCtx, &gfx); + } + break; + case MSGMODE_OCARINA_CORRECT_PLAYBACK: + case MSGMODE_SONG_PLAYBACK_SUCCESS: + case MSGMODE_SCARECROW_RECORDING_DONE: + r = ABS(sOcarinaNoteAPrimR - sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][0]) / + sOcarinaNoteFlashTimer; + g = ABS(sOcarinaNoteAPrimG - sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][1]) / + sOcarinaNoteFlashTimer; + b = ABS(sOcarinaNoteAPrimB - sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][2]) / + sOcarinaNoteFlashTimer; + + if (sOcarinaNoteAPrimR >= sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][0]) { + sOcarinaNoteAPrimR -= r; + } else { + sOcarinaNoteAPrimR += r; + } + if (sOcarinaNoteAPrimG >= sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][1]) { + sOcarinaNoteAPrimG -= g; + } else { + sOcarinaNoteAPrimG += g; + } + if (sOcarinaNoteAPrimB >= sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][2]) { + sOcarinaNoteAPrimB -= b; + } else { + sOcarinaNoteAPrimB += b; + } + + r = ABS(sOcarinaNoteAEnvR - sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][0]) / + sOcarinaNoteFlashTimer; + g = ABS(sOcarinaNoteAEnvG - sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][1]) / + sOcarinaNoteFlashTimer; + b = ABS(sOcarinaNoteAEnvB - sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][2]) / + sOcarinaNoteFlashTimer; + + if (sOcarinaNoteCEnvR >= sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][0]) { + sOcarinaNoteAEnvR -= r; + } else { + sOcarinaNoteAEnvR += r; + } + if (sOcarinaNoteCEnvG >= sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][1]) { + sOcarinaNoteAEnvG -= g; + } else { + sOcarinaNoteAEnvG += g; + } + if (sOcarinaNoteCEnvB >= sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][2]) { + sOcarinaNoteAEnvB -= b; + } else { + sOcarinaNoteAEnvB += b; + } + + r = ABS(sOcarinaNoteCPrimR - sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][0]) / + sOcarinaNoteFlashTimer; + g = ABS(sOcarinaNoteCPrimG - sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][1]) / + sOcarinaNoteFlashTimer; + b = ABS(sOcarinaNoteCPrimB - sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][2]) / + sOcarinaNoteFlashTimer; + + if (sOcarinaNoteCPrimR >= sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][0]) { + sOcarinaNoteCPrimR -= r; + } else { + sOcarinaNoteCPrimR += r; + } + if (sOcarinaNoteCPrimG >= sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][1]) { + sOcarinaNoteCPrimG -= g; + } else { + sOcarinaNoteCPrimG += g; + } + if (sOcarinaNoteCPrimB >= sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][2]) { + sOcarinaNoteCPrimB -= b; + } else { + sOcarinaNoteCPrimB += b; + } + + r = ABS(sOcarinaNoteCEnvR - sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][0]) / + sOcarinaNoteFlashTimer; + g = ABS(sOcarinaNoteCEnvG - sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][1]) / + sOcarinaNoteFlashTimer; + b = ABS(sOcarinaNoteCEnvB - sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][2]) / + sOcarinaNoteFlashTimer; + + if (sOcarinaNoteCEnvR >= sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][0]) { + sOcarinaNoteCEnvR -= r; + } else { + sOcarinaNoteCEnvR += r; + } + if (sOcarinaNoteCEnvG >= sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][1]) { + sOcarinaNoteCEnvG -= g; + } else { + sOcarinaNoteCEnvG += g; + } + if (sOcarinaNoteCEnvB >= sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][2]) { + sOcarinaNoteCEnvB -= b; + } else { + sOcarinaNoteCEnvB += b; + } + + sOcarinaNoteFlashTimer--; + if (sOcarinaNoteFlashTimer == 0) { + sOcarinaNoteAPrimR = sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][0]; + sOcarinaNoteAPrimG = sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][1]; + sOcarinaNoteAPrimB = sOcarinaNoteAPrimColors[sOcarinaNoteFlashColorIdx][2]; + sOcarinaNoteAEnvR = sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][0]; + sOcarinaNoteAEnvG = sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][1]; + sOcarinaNoteAEnvB = sOcarinaNoteAEnvColors[sOcarinaNoteFlashColorIdx][2]; + sOcarinaNoteCPrimR = sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][0]; + sOcarinaNoteCPrimG = sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][1]; + sOcarinaNoteCPrimB = sOcarinaNoteCPrimColors[sOcarinaNoteFlashColorIdx][2]; + sOcarinaNoteCEnvR = sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][0]; + sOcarinaNoteCEnvG = sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][1]; + sOcarinaNoteCEnvB = sOcarinaNoteCEnvColors[sOcarinaNoteFlashColorIdx][2]; + sOcarinaNoteFlashTimer = 3; + sOcarinaNoteFlashColorIdx ^= 1; + } + + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + Audio_OcaSetInstrument(0); + if (msgCtx->msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) { + // "Correct Example Performance" + osSyncPrintf("正解模範演奏=%x\n", msgCtx->lastPlayedSong); + Message_ContinueTextbox(globalCtx, 0x86F); // Ocarina + msgCtx->msgMode = MSGMODE_SONG_PLAYED; + msgCtx->textBoxType = TEXTBOX_TYPE_OCARINA; + msgCtx->stateTimer = 1; + } else if (msgCtx->msgMode == MSGMODE_SONG_PLAYBACK_SUCCESS) { + if (msgCtx->lastPlayedSong >= OCARINA_SONG_SARIAS) { + Message_ContinueTextbox(globalCtx, 0x86F); // Ocarina + msgCtx->msgMode = MSGMODE_SONG_PLAYED; + msgCtx->textBoxType = TEXTBOX_TYPE_OCARINA; + msgCtx->stateTimer = 1; + } else { + Message_CloseTextbox(globalCtx); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } + } else { + Message_CloseTextbox(globalCtx); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_03; + } + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_OCARINA_FAIL: + case MSGMODE_SONG_PLAYBACK_FAIL: + Message_DrawText(globalCtx, &gfx); + case MSGMODE_OCARINA_FAIL_NO_TEXT: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + R_OCARINA_NOTES_YPOS_OFFSET = 1; + if (msgCtx->msgMode == MSGMODE_SONG_PLAYBACK_FAIL) { + // "kokokokokoko" + osSyncPrintf("ここここここ\n"); + Message_ContinueTextbox(globalCtx, 0x88B); // red X background + Message_Decode(globalCtx); + msgCtx->msgMode = MSGMODE_SONG_PLAYBACK_NOTES_DROP; + } else { + msgCtx->msgMode = MSGMODE_OCARINA_NOTES_DROP; + } + // "Cancel" + osSyncPrintf("キャンセル\n"); + } + break; + case MSGMODE_OCARINA_NOTES_DROP: + case MSGMODE_SONG_PLAYBACK_NOTES_DROP: + for (i = 0; i < 5; i++) { + R_OCARINA_NOTES_YPOS(i) += R_OCARINA_NOTES_YPOS_OFFSET; + } + R_OCARINA_NOTES_YPOS_OFFSET += R_OCARINA_NOTES_YPOS_OFFSET; + if (R_OCARINA_NOTES_YPOS_OFFSET >= 550) { + sOcarinaNoteBuf[0] = OCARINA_NOTE_INVALID; + sOcarinaNotesAlphaValues[0] = sOcarinaNotesAlphaValues[1] = sOcarinaNotesAlphaValues[2] = + sOcarinaNotesAlphaValues[3] = sOcarinaNotesAlphaValues[4] = sOcarinaNotesAlphaValues[5] = + sOcarinaNotesAlphaValues[6] = sOcarinaNotesAlphaValues[7] = sOcarinaNotesAlphaValues[8] = 0; + if (msgCtx->msgMode == MSGMODE_SONG_PLAYBACK_NOTES_DROP) { + msgCtx->msgMode = MSGMODE_OCARINA_AWAIT_INPUT; + } else { + msgCtx->msgMode = MSGMODE_OCARINA_STARTING; + } + } + break; + case MSGMODE_SONG_PLAYED: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + Audio_OcaSetInstrument(0); + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("Na_StopOcarinaMode();\n"); + osSyncPrintf("Na_StopOcarinaMode();\n"); + osSyncPrintf("Na_StopOcarinaMode();\n"); + osSyncPrintf(VT_RST); + Message_Decode(globalCtx); + msgCtx->msgMode = MSGMODE_SETUP_DISPLAY_SONG_PLAYED; + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + Message_ResetOcarinaNoteState(); + if (msgCtx->lastPlayedSong >= OCARINA_SONG_SARIAS && + msgCtx->lastPlayedSong < OCARINA_SONG_MEMORY_GAME) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, + sOcarinaEffectActorIds[msgCtx->lastPlayedSong - OCARINA_SONG_SARIAS], + player->actor.world.pos.x, player->actor.world.pos.y, player->actor.world.pos.z, 0, + 0, 0, sOcarinaEffectActorParams[msgCtx->lastPlayedSong - OCARINA_SONG_SARIAS]); + } + } + break; + case MSGMODE_SETUP_DISPLAY_SONG_PLAYED: + Message_DrawText(globalCtx, &gfx); + Audio_OcaSetInstrument(1); + Audio_OcaSetInstrument(1); + Audio_OcaSetSongPlayback(msgCtx->lastPlayedSong + 1, 1); + if (msgCtx->lastPlayedSong != OCARINA_SONG_SCARECROW) { + Audio_PlayFanfare(sOcarinaSongFanfares[msgCtx->lastPlayedSong]); + Audio_SetSoundBanksMute(0x20); + } + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + if (msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY) { + msgCtx->ocarinaAction = OCARINA_ACTION_FREE_PLAY_DONE; + } + if (msgCtx->ocarinaAction == OCARINA_ACTION_CHECK_NOWARP) { + msgCtx->ocarinaAction = OCARINA_ACTION_CHECK_NOWARP_DONE; + } + sOcarinaNoteBufPos = 0; + msgCtx->msgMode = MSGMODE_DISPLAY_SONG_PLAYED; + break; + case MSGMODE_SONG_DEMONSTRATION_SELECT_INSTRUMENT: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + // "ocarina_no=%d Song Chosen=%d" + osSyncPrintf("ocarina_no=%d 選曲=%d\n", msgCtx->ocarinaAction, 0x16); + if (msgCtx->ocarinaAction < OCARINA_ACTION_TEACH_SARIA) { + Audio_OcaSetInstrument(4); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_TEACH_EPONA) { + Audio_OcaSetInstrument(2); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_TEACH_LULLABY) { + Audio_OcaSetInstrument(3); + } else if (msgCtx->ocarinaAction == OCARINA_ACTION_TEACH_STORMS) { + Audio_OcaSetInstrument(5); + } else { + Audio_OcaSetInstrument(1); + } + // "Example Performance" + osSyncPrintf("模範演奏=%x\n", msgCtx->ocarinaAction - OCARINA_ACTION_TEACH_MINUET); + Audio_OcaSetSongPlayback(msgCtx->ocarinaAction - OCARINA_ACTION_TEACH_MINUET + 1, 2); + sOcarinaNoteBufPos = 0; + msgCtx->msgMode = MSGMODE_SONG_DEMONSTRATION; + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_DISPLAY_SONG_PLAYED_TEXT_BEGIN: + Message_ContinueTextbox(globalCtx, msgCtx->lastPlayedSong + 0x893); // You played [song name] + Message_Decode(globalCtx); + msgCtx->msgMode = MSGMODE_DISPLAY_SONG_PLAYED_TEXT; + msgCtx->stateTimer = 20; + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_DISPLAY_SONG_PLAYED_TEXT: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + msgCtx->msgMode = MSGMODE_SONG_PLAYED_ACT_BEGIN; + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SONG_PLAYED_ACT_BEGIN: + Audio_OcaSetInstrument(0); + Message_ResetOcarinaNoteState(); + msgCtx->msgMode = MSGMODE_SONG_PLAYED_ACT; + msgCtx->stateTimer = 2; + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SONG_PLAYED_ACT: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + if (msgCtx->lastPlayedSong < OCARINA_SONG_SARIAS && + (msgCtx->ocarinaAction < OCARINA_ACTION_PLAYBACK_MINUET || + msgCtx->ocarinaAction >= OCARINA_ACTION_PLAYBACK_SARIA)) { + if (msgCtx->disableWarpSongs || interfaceCtx->restrictions.warpSongs == 3) { + Message_StartTextbox(globalCtx, 0x88C, NULL); // "You can't warp here!" + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else if ((gSaveContext.eventInf[0] & 0xF) != 1) { + Message_StartTextbox(globalCtx, msgCtx->lastPlayedSong + 0x88D, + NULL); // "Warp to [place name]?" + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + } else { + Message_CloseTextbox(globalCtx); + } + } else { + Message_CloseTextbox(globalCtx); + if (msgCtx->lastPlayedSong == OCARINA_SONG_EPONAS) { + DREG(53) = 1; + } + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("☆☆☆ocarina=%d message->ocarina_no=%d ", msgCtx->lastPlayedSong, + msgCtx->ocarinaAction); + if (msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY_DONE) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + if (msgCtx->lastPlayedSong == OCARINA_SONG_SCARECROW) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_0B; + } + } else if (msgCtx->ocarinaAction >= OCARINA_ACTION_CHECK_MINUET) { + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("Ocarina_PC_Wind=%d(%d) ☆☆☆ ", OCARINA_ACTION_CHECK_MINUET, + msgCtx->ocarinaAction - OCARINA_ACTION_CHECK_MINUET); + if (msgCtx->lastPlayedSong + OCARINA_ACTION_CHECK_MINUET == msgCtx->ocarinaAction) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_03; + } else { + globalCtx->msgCtx.ocarinaMode = msgCtx->lastPlayedSong - 1; + } + } else { + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("Ocarina_C_Wind=%d(%d) ☆☆☆ ", OCARINA_ACTION_PLAYBACK_MINUET, + msgCtx->ocarinaAction - OCARINA_ACTION_PLAYBACK_MINUET); + if (msgCtx->lastPlayedSong + OCARINA_ACTION_PLAYBACK_MINUET == msgCtx->ocarinaAction) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_03; + } else { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } + } + osSyncPrintf(VT_RST); + osSyncPrintf("→ OCARINA_MODE=%d\n", globalCtx->msgCtx.ocarinaMode); + } + } + break; + case MSGMODE_DISPLAY_SONG_PLAYED: + case MSGMODE_SONG_DEMONSTRATION: + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + if (msgCtx->ocarinaStaff->state == 0) { + if (msgCtx->msgMode == MSGMODE_DISPLAY_SONG_PLAYED) { + msgCtx->msgMode = MSGMODE_DISPLAY_SONG_PLAYED_TEXT_BEGIN; + } else { + msgCtx->msgMode = MSGMODE_SONG_DEMONSTRATION_DONE; + } + osSyncPrintf("onpu_buff[%d]=%x\n", msgCtx->ocarinaStaff->pos, + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos]); + } else { + if (sOcarinaNoteBufPos != 0 && msgCtx->ocarinaStaff->pos == 1) { + sOcarinaNoteBufPos = 0; + } + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + msgCtx->lastOcaNoteIdx = sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = + msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + } + case MSGMODE_SONG_DEMONSTRATION_DONE: + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SONG_PLAYBACK: + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + if (msgCtx->ocarinaStaff->state < OCARINA_SONG_MEMORY_GAME) { + osSyncPrintf("M_OCARINA20 : ocarina_no=%x status=%x\n", msgCtx->ocarinaAction, + msgCtx->ocarinaStaff->state); + msgCtx->lastPlayedSong = msgCtx->ocarinaStaff->state; + msgCtx->msgMode = MSGMODE_SONG_PLAYBACK_SUCCESS; + Item_Give(globalCtx, ITEM_SONG_MINUET + gOcarinaSongItemMap[msgCtx->ocarinaStaff->state]); + osSyncPrintf(VT_FGCOL(YELLOW)); + // "z_message.c Song Acquired" + osSyncPrintf("z_message.c 取得メロディ=%d\n", ITEM_SONG_MINUET + msgCtx->ocarinaStaff->state); + osSyncPrintf(VT_RST); + msgCtx->stateTimer = 20; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (msgCtx->ocarinaStaff->state == 0xFF) { + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->stateTimer = 10; + msgCtx->msgMode = MSGMODE_SONG_PLAYBACK_FAIL; + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_OCARINA_AWAIT_INPUT: + Message_DrawText(globalCtx, &gfx); + if (Message_ShouldAdvance(globalCtx)) { + func_8010BD58(globalCtx, msgCtx->ocarinaAction); + } + break; + case MSGMODE_SCARECROW_LONG_RECORDING_START: + // "Scarecrow Recording Initialization" + osSyncPrintf("案山子録音 初期化\n"); + Audio_OcaSetRecordingState(1); + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetRecordingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + sOcarinaNoteBufLen = 0; + Message_ResetOcarinaNoteState(); + msgCtx->msgMode = MSGMODE_SCARECROW_LONG_RECORDING_ONGOING; + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SCARECROW_LONG_RECORDING_ONGOING: + msgCtx->ocarinaStaff = Audio_OcaGetRecordingStaff(); + osSyncPrintf("\nonpu_pt=%d, locate=%d", sOcarinaNoteBufPos, msgCtx->ocarinaStaff->pos); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + if (sOcarinaNoteBufLen >= 8) { + for (noteBufPos = sOcarinaNoteBufLen - 8, i = 0; i < 8; i++, noteBufPos++) { + sOcarinaNoteBuf[noteBufPos] = sOcarinaNoteBuf[noteBufPos + 1]; + } + sOcarinaNoteBufLen--; + } + // "Button Entered" + osSyncPrintf(" 入力ボタン【%d】=%d", sOcarinaNoteBufLen, msgCtx->ocarinaStaff->noteIdx); + msgCtx->lastOcaNoteIdx = sOcarinaNoteBuf[sOcarinaNoteBufLen] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBufLen++; + sOcarinaNoteBuf[sOcarinaNoteBufLen] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + if (msgCtx->ocarinaStaff->pos == 8) { + sOcarinaNoteBufPos = 0; + } + } + if (msgCtx->ocarinaStaff->state == 0 || isB_Held) { + if (sOcarinaNoteBufLen != 0) { + // "Recording complete!!!!!!!!!" + osSyncPrintf("録音終了!!!!!!!!! message->info->status=%d \n", + msgCtx->ocarinaStaff->state); + gSaveContext.scarecrowCustomSongSet = true; + } + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + osSyncPrintf("aaaaaaaaaaaaaa\n"); + Audio_OcaSetRecordingState(0); + msgCtx->stateTimer = 10; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + Message_CloseTextbox(globalCtx); + // "Recording complete!!!!!!!!!Recording Complete" + osSyncPrintf("録音終了!!!!!!!!!録音終了\n"); + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("\n====================================================================\n"); + memcpy(gSaveContext.scarecrowCustomSong, gScarecrowCustomSongPtr, + sizeof(gSaveContext.scarecrowCustomSong)); + for (i = 0; i < ARRAY_COUNT(gSaveContext.scarecrowCustomSong); i++) { + osSyncPrintf("%d, ", gSaveContext.scarecrowCustomSong[i]); + } + osSyncPrintf(VT_RST); + osSyncPrintf("\n====================================================================\n"); + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SCARECROW_LONG_PLAYBACK: + case MSGMODE_SCARECROW_PLAYBACK: + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + if (sOcarinaNoteBufLen >= 8) { + for (noteBufPos = sOcarinaNoteBufLen - 8, i = 0; i < 8; i++, noteBufPos++) { + sOcarinaNoteBuf[noteBufPos] = sOcarinaNoteBuf[noteBufPos + 1]; + } + sOcarinaNoteBufLen--; + } + sOcarinaNoteBuf[sOcarinaNoteBufLen] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBufLen++; + sOcarinaNoteBuf[sOcarinaNoteBufLen] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + if (msgCtx->ocarinaStaff->pos == 8) { + sOcarinaNoteBufLen = sOcarinaNoteBufPos = 0; + } + } + osSyncPrintf("status=%d (%d)\n", msgCtx->ocarinaStaff->state, 0); + if (msgCtx->stateTimer == 0) { + if (msgCtx->ocarinaStaff->state == 0) { + osSyncPrintf("bbbbbbbbbbb\n"); + Audio_OcaSetInstrument(0); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_0F; + Message_CloseTextbox(globalCtx); + } + } else { + msgCtx->stateTimer--; + } + break; + case MSGMODE_SCARECROW_RECORDING_START: + Audio_OcaSetRecordingState(2); + Audio_OcaSetInstrument(1); + msgCtx->msgMode = MSGMODE_SCARECROW_RECORDING_ONGOING; + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SCARECROW_RECORDING_ONGOING: + msgCtx->ocarinaStaff = Audio_OcaGetRecordingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + msgCtx->lastOcaNoteIdx = sOcarinaNoteBuf[sOcarinaNoteBufPos] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBufPos++; + sOcarinaNoteBuf[sOcarinaNoteBufPos] = OCARINA_NOTE_INVALID; + } + if (msgCtx->ocarinaStaff->state == 0) { + // "8 Note Recording OK!" + osSyncPrintf("8音録音OK!\n"); + msgCtx->stateTimer = 20; + gSaveContext.scarecrowSpawnSongSet = true; + msgCtx->msgMode = MSGMODE_SCARECROW_RECORDING_DONE; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("\n====================================================================\n"); + memcpy(gSaveContext.scarecrowSpawnSong, gScarecrowSpawnSongPtr, + sizeof(gSaveContext.scarecrowSpawnSong)); + for (i = 0; i < ARRAY_COUNT(gSaveContext.scarecrowSpawnSong); i++) { + osSyncPrintf("%d, ", gSaveContext.scarecrowSpawnSong[i]); + } + osSyncPrintf(VT_RST); + osSyncPrintf("\n====================================================================\n"); + } else if (msgCtx->ocarinaStaff->state == 0xFF || isB_Held) { + // "Played an existing song!!!" + osSyncPrintf("すでに存在する曲吹いた!!! \n"); + Audio_OcaSetRecordingState(0); + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Message_CloseTextbox(globalCtx); + msgCtx->msgMode = MSGMODE_SCARECROW_RECORDING_FAILED; + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_SCARECROW_RECORDING_FAILED: + osSyncPrintf("cccccccccccc\n"); + Audio_OcaSetInstrument(0); + Message_StartTextbox(globalCtx, 0x40AD, NULL); // Bonooru doesn't remember your song + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + break; + case MSGMODE_MEMORY_GAME_START: + Audio_OcaSetInstrument(1); + Audio_OcaSetInstrument(6); + Audio_OcaMemoryGameStart(gSaveContext.ocarinaGameRoundNum); + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + Message_ResetOcarinaNoteState(); + Audio_OcaSetSongPlayback(OCARINA_SONG_MEMORY_GAME + 1, 1); + msgCtx->msgMode = MSGMODE_MEMORY_GAME_LEFT_SKULLKID_PLAYING; + msgCtx->stateTimer = 2; + break; + case MSGMODE_MEMORY_GAME_LEFT_SKULLKID_PLAYING: + case MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_PLAYING: + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME_LV - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + if (msgCtx->stateTimer == 0) { + if (msgCtx->ocarinaStaff->state == 0) { + if (msgCtx->msgMode == MSGMODE_MEMORY_GAME_LEFT_SKULLKID_PLAYING) { + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME_2, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + msgCtx->msgMode++; + } + } else { + msgCtx->stateTimer--; + } + break; + case MSGMODE_MEMORY_GAME_LEFT_SKULLKID_WAIT: + case MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_WAIT: + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + break; + case MSGMODE_MEMORY_GAME_PLAYER_PLAYING: + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME_LV - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + if (msgCtx->ocarinaStaff->state == 0xFF) { + // "Musical round failed!!!!!!!!!" + osSyncPrintf("輪唱失敗!!!!!!!!!\n"); + Audio_OcaSetInstrument(0); + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->stateTimer = 10; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_03; + } else if (msgCtx->ocarinaStaff->state == 0xD) { + // "Musical round succeeded!!!!!!!!!" + osSyncPrintf("輪唱成功!!!!!!!!!\n"); + Audio_PlaySoundGeneral(NA_SE_SY_GET_ITEM, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + msgCtx->msgMode = MSGMODE_MEMORY_GAME_ROUND_SUCCESS; + msgCtx->stateTimer = 30; + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_MEMORY_GAME_ROUND_SUCCESS: + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos - 1] = msgCtx->ocarinaStaff->noteIdx; + sOcarinaNoteBuf[msgCtx->ocarinaStaff->pos] = OCARINA_NOTE_INVALID; + sOcarinaNoteBufPos++; + } + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + if (Audio_OcaMemoryGameGenNote() != 1) { + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + Message_ResetOcarinaNoteState(); + msgCtx->msgMode = MSGMODE_MEMORY_GAME_START_NEXT_ROUND; + } else { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_0F; + } + } + Message_DrawText(globalCtx, &gfx); + break; + case MSGMODE_MEMORY_GAME_START_NEXT_ROUND: + if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) { + msgCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + Message_ResetOcarinaNoteState(); + Audio_OcaSetSongPlayback(OCARINA_SONG_MEMORY_GAME + 1, 1); + } + break; + case MSGMODE_FROGS_START: + Audio_OcaSetInstrument(1); + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + Message_ResetOcarinaNoteState(); + func_800ECC04(sOcarinaSongBitFlags + 0xC000); + msgCtx->msgMode = MSGMODE_FROGS_PLAYING; + break; + case MSGMODE_FROGS_PLAYING: + msgCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + if (msgCtx->ocarinaStaff->pos && sOcarinaNoteBufPos == msgCtx->ocarinaStaff->pos - 1) { + msgCtx->lastOcaNoteIdx = msgCtx->ocarinaStaff->noteIdx; + msgCtx->ocarinaStaff->pos = sOcarinaNoteBufPos = 0; + Message_ResetOcarinaNoteState(); + msgCtx->msgMode = MSGMODE_FROGS_WAITING; + } + case MSGMODE_FROGS_WAITING: + break; + case MSGMODE_TEXT_DONE: + Message_DrawText(globalCtx, &gfx); + + switch (msgCtx->textboxEndType) { + case TEXTBOX_ENDTYPE_2_CHOICE: + Message_HandleChoiceSelection(globalCtx, 1); + Message_DrawTextboxIcon(globalCtx, &gfx, msgCtx->textPosX, msgCtx->textPosY); + break; + case TEXTBOX_ENDTYPE_3_CHOICE: + Message_HandleChoiceSelection(globalCtx, 2); + Message_DrawTextboxIcon(globalCtx, &gfx, msgCtx->textPosX, msgCtx->textPosY); + break; + case TEXTBOX_ENDTYPE_PERSISTENT: + if (msgCtx->textId >= 0x6D && msgCtx->textId < 0x73) { + msgCtx->stateTimer++; + if (msgCtx->stateTimer >= 31) { + msgCtx->stateTimer = 2; + msgCtx->msgMode = MSGMODE_TEXT_CLOSING; + } + } + break; + case TEXTBOX_ENDTYPE_EVENT: + default: + Message_DrawTextboxIcon(globalCtx, &gfx, R_TEXTBOX_END_XPOS, R_TEXTBOX_END_YPOS); + case TEXTBOX_ENDTYPE_FADING: + break; + } + break; + case MSGMODE_TEXT_CLOSING: + case MSGMODE_PAUSED: + break; + case MSGMODE_UNK_20: + default: + msgCtx->msgMode = MSGMODE_TEXT_DISPLAYING; + break; + } + + if (msgCtx->msgMode >= MSGMODE_OCARINA_PLAYING && msgCtx->msgMode < MSGMODE_TEXT_AWAIT_NEXT && + msgCtx->ocarinaAction != OCARINA_ACTION_FREE_PLAY && msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP) { + func_8009457C(&gfx); + + gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + if (msgCtx->msgMode == MSGMODE_SONG_PLAYBACK) { + g = msgCtx->ocarinaAction - OCARINA_ACTION_PLAYBACK_MINUET; + r = gOcarinaSongNotes[g].len; + for (notePosX = R_OCARINA_NOTES_XPOS, i = 0; i < r; i++, notePosX += R_OCARINA_NOTES_XPOS_OFFSET) { + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 150, 150, 150, 150); + gDPSetEnvColor(gfx++, 10, 10, 10, 0); + + gDPLoadTextureBlock(gfx++, sOcarinaNoteTextures[gOcarinaSongNotes[g].notesIdx[i]], G_IM_FMT_IA, + G_IM_SIZ_8b, 16, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle( + gfx++, notePosX << 2, R_OCARINA_NOTES_YPOS(gOcarinaSongNotes[g].notesIdx[i]) << 2, + (notePosX + 16) << 2, (R_OCARINA_NOTES_YPOS(gOcarinaSongNotes[g].notesIdx[i]) + 16) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + } + + if (msgCtx->msgMode != MSGMODE_SCARECROW_LONG_RECORDING_START && + msgCtx->msgMode != MSGMODE_MEMORY_GAME_START) { + for (notePosX = R_OCARINA_NOTES_XPOS, i = 0; i < 8; i++, notePosX += R_OCARINA_NOTES_XPOS_OFFSET) { + if (sOcarinaNoteBuf[i] == OCARINA_NOTE_INVALID) { + break; + } + + if (1) {} + if (sOcarinaNotesAlphaValues[i] != 255) { + sOcarinaNotesAlphaValues[i] += VREG(50); + if (sOcarinaNotesAlphaValues[i] >= 255) { + sOcarinaNotesAlphaValues[i] = 255; + } + } + + gDPPipeSync(gfx++); + if (sOcarinaNoteBuf[i] == OCARINA_NOTE_A) { + gDPSetPrimColor(gfx++, 0, 0, sOcarinaNoteAPrimR, sOcarinaNoteAPrimG, sOcarinaNoteAPrimB, + sOcarinaNotesAlphaValues[i]); + gDPSetEnvColor(gfx++, sOcarinaNoteAEnvR, sOcarinaNoteAEnvG, sOcarinaNoteAEnvB, 0); + } else { + gDPSetPrimColor(gfx++, 0, 0, sOcarinaNoteCPrimR, sOcarinaNoteCPrimG, sOcarinaNoteCPrimB, + sOcarinaNotesAlphaValues[i]); + gDPSetEnvColor(gfx++, sOcarinaNoteCEnvR, sOcarinaNoteCEnvG, sOcarinaNoteCEnvB, 0); + } + + gDPLoadTextureBlock(gfx++, sOcarinaNoteTextures[sOcarinaNoteBuf[i]], G_IM_FMT_IA, G_IM_SIZ_8b, 16, + 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, notePosX << 2, R_OCARINA_NOTES_YPOS(sOcarinaNoteBuf[i]) << 2, + (notePosX + 16) << 2, (R_OCARINA_NOTES_YPOS(sOcarinaNoteBuf[i]) + 16) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + } + } + } + *p = gfx; +} + +/** + * If the s16 variable pointed to by `var` changes in value, a black bar and white box + * are briefly drawn onto the screen. It can only watch one variable per build due to + * the last value being saved in a static variable. + */ +void Message_DrawDebugVariableChanged(s16* var, GraphicsContext* gfxCtx) { + static s16 sVarLastValue = 0; + static s16 sFillTimer = 0; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_message_PAL.c", 3485); + + if (sVarLastValue != *var) { + sVarLastValue = *var; + sFillTimer = 30; + } + if (sFillTimer != 0) { + sFillTimer--; + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_FILL); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_NOOP, G_RM_NOOP2); + gDPSetFillColor(POLY_OPA_DISP++, GPACK_RGBA5551(0, 0, 0, 1) << 0x10 | GPACK_RGBA5551(0, 0, 0, 1)); + gDPFillRectangle(POLY_OPA_DISP++, 0, 110, SCREEN_WIDTH - 1, 150); // 40x319 black bar + gDPPipeSync(POLY_OPA_DISP++); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_FILL); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_NOOP, G_RM_NOOP2); + gDPSetFillColor(POLY_OPA_DISP++, GPACK_RGBA5551(255, 255, 255, 1) << 0x10 | GPACK_RGBA5551(255, 255, 255, 1)); + gDPFillRectangle(POLY_OPA_DISP++, 40, 120, 60, 140); // 20x20 white box + gDPPipeSync(POLY_OPA_DISP++); + } + CLOSE_DISPS(gfxCtx, "../z_message_PAL.c", 3513); +} + +void Message_DrawDebugText(GlobalContext* globalCtx, Gfx** p) { + s32 pad; + GfxPrint printer; + s32 pad1; + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, *p); + GfxPrint_SetPos(&printer, 6, 26); + GfxPrint_SetColor(&printer, 255, 60, 0, 255); + GfxPrint_Printf(&printer, "%s", "MESSAGE"); + GfxPrint_SetPos(&printer, 14, 26); + GfxPrint_Printf(&printer, "%s", "="); + GfxPrint_SetPos(&printer, 16, 26); + GfxPrint_Printf(&printer, "%x", globalCtx->msgCtx.textId); + *p = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); +} + +void Message_Draw(GlobalContext* globalCtx) { + Gfx* plusOne; + Gfx* polyOpaP; + s16 watchVar; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_message_PAL.c", 3554); + + watchVar = gSaveContext.scarecrowCustomSongSet; + Message_DrawDebugVariableChanged(&watchVar, globalCtx->state.gfxCtx); + if (BREG(0) != 0 && globalCtx->msgCtx.textId != 0) { + plusOne = Graph_GfxPlusOne(polyOpaP = POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, plusOne); + Message_DrawDebugText(globalCtx, &plusOne); + gSPEndDisplayList(plusOne++); + Graph_BranchDlist(polyOpaP, plusOne); + POLY_OPA_DISP = plusOne; + } + if (1) {} + plusOne = Graph_GfxPlusOne(polyOpaP = POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, plusOne); + Message_DrawMain(globalCtx, &plusOne); + gSPEndDisplayList(plusOne++); + Graph_BranchDlist(polyOpaP, plusOne); + POLY_OPA_DISP = plusOne; + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_message_PAL.c", 3582); +} + +void Message_Update(GlobalContext* globalCtx) { + static s16 sTextboxXPositions[] = { + 34, 34, 34, 34, 34, 34, + }; + static s16 sTextboxMidYPositions[] = { + 142, 142, 142, 142, 174, 142, + }; + static s16 sTextboxUpperYPositions[] = { + 38, 38, 38, 38, 174, 38, + }; + static s16 sTextboxLowerYPositions[] = { + 90, 90, 90, 90, 174, 90, + }; + static s16 sTextboxEndIconYOffset[] = { + 59, 59, 59, 59, 34, 59, + }; + static s16 D_80153D3C[] = { + // additional unreferenced data + 0x0400, 0x0400, 0x0200, 0x0000, 0x1038, 0x0008, 0x200A, 0x088B, 0x0007, 0x0009, 0x000A, 0x107E, 0x2008, 0x2007, + 0x0015, 0x0016, 0x0017, 0x0003, 0x0000, 0x270B, 0x00C8, 0x012C, 0x012D, 0xFFDA, 0x0014, 0x0016, 0x0014, 0x0016, + }; + static u8 D_80153D74 = 0; + static u16 D_80153D78 = 0; + MessageContext* msgCtx = &globalCtx->msgCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + Player* player = GET_PLAYER(globalCtx); + Input* input = &globalCtx->state.input[0]; + s16 var; + s16 focusScreenPosX; + s16 averageY; + s16 playerFocusScreenPosY; + s16 actorFocusScreenPosY; + + if (BREG(0) != 0) { + if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN) && CHECK_BTN_ALL(input->cur.button, BTN_L)) { + osSyncPrintf("msgno=%d\n", D_80153D78); + Message_StartTextbox(globalCtx, R_MESSAGE_DEBUGGER_TEXTID, NULL); + D_80153D78 = (D_80153D78 + 1) % 10; + } + if (R_MESSAGE_DEBUGGER_SELECT != 0) { + while (R_MESSAGE_DEBUGGER_TEXTID != 0x8000) { + MessageTableEntry* entry = &sNesMessageEntryTablePtr[0]; + + while (entry->textId != 0xFFFD) { + if (entry->textId == R_MESSAGE_DEBUGGER_TEXTID) { + // "The message was found! !! !!" + osSyncPrintf(" メッセージが,見つかった!!! = %x\n", R_MESSAGE_DEBUGGER_TEXTID); + Message_StartTextbox(globalCtx, R_MESSAGE_DEBUGGER_TEXTID, NULL); + R_MESSAGE_DEBUGGER_TEXTID++; + R_MESSAGE_DEBUGGER_SELECT = 0; + return; + } + entry++; + } + R_MESSAGE_DEBUGGER_TEXTID++; + } + } + } + + if (msgCtx->msgLength == 0) { + return; + } + + bool isB_Held = CVar_GetS32("gFastText", 0) != 0 ? CHECK_BTN_ALL(input->cur.button, BTN_B) && !sTextboxSkipped + : CHECK_BTN_ALL(input->press.button, BTN_B); + + switch (msgCtx->msgMode) { + case MSGMODE_TEXT_START: + D_8014B2F4++; + + var = false; + if (YREG(15) == 0x40) { + if (D_8014B2F4 >= 4) { + var = true; + } + } else if (YREG(15) != 0 || globalCtx->sceneNum == SCENE_HAIRAL_NIWA) { + var = true; + } else if (D_8014B2F4 >= 4 || msgCtx->talkActor == NULL) { + var = true; + } + + if (var) { + if (msgCtx->talkActor != NULL) { + Actor_GetScreenPos(globalCtx, &GET_PLAYER(globalCtx)->actor, &focusScreenPosX, + &playerFocusScreenPosY); + Actor_GetScreenPos(globalCtx, msgCtx->talkActor, &focusScreenPosX, &actorFocusScreenPosY); + + if (playerFocusScreenPosY >= actorFocusScreenPosY) { + averageY = ((playerFocusScreenPosY - actorFocusScreenPosY) / 2) + actorFocusScreenPosY; + } else { + averageY = ((actorFocusScreenPosY - playerFocusScreenPosY) / 2) + playerFocusScreenPosY; + } + osSyncPrintf("dxpos=%d dypos=%d dypos1 dypos2=%d\n", focusScreenPosX, averageY, + playerFocusScreenPosY, actorFocusScreenPosY); + } else { + R_TEXTBOX_X = R_TEXTBOX_X_TARGET; + R_TEXTBOX_Y = R_TEXTBOX_Y_TARGET; + } + + var = msgCtx->textBoxType; + + if (!msgCtx->textBoxPos) { // variable position + if (YREG(15) != 0 || globalCtx->sceneNum == SCENE_HAIRAL_NIWA) { + if (averageY < XREG(92)) { + R_TEXTBOX_Y_TARGET = sTextboxMidYPositions[var]; + } else { + R_TEXTBOX_Y_TARGET = sTextboxUpperYPositions[var]; + } + } else if (globalCtx->sceneNum == SCENE_MARKET_DAY || globalCtx->sceneNum == SCENE_MARKET_NIGHT || + globalCtx->sceneNum == SCENE_MARKET_RUINS) { + if (averageY < XREG(93)) { + R_TEXTBOX_Y_TARGET = sTextboxMidYPositions[var]; + } else { + R_TEXTBOX_Y_TARGET = sTextboxUpperYPositions[var]; + } + } else { + if (averageY < XREG(94)) { + R_TEXTBOX_Y_TARGET = sTextboxMidYPositions[var]; + } else { + R_TEXTBOX_Y_TARGET = sTextboxUpperYPositions[var]; + } + } + } else { + if (msgCtx->textBoxPos == TEXTBOX_POS_TOP) { + R_TEXTBOX_Y_TARGET = sTextboxUpperYPositions[var]; + } else if (msgCtx->textBoxPos == TEXTBOX_POS_BOTTOM) { + R_TEXTBOX_Y_TARGET = sTextboxLowerYPositions[var]; + } else { + R_TEXTBOX_Y_TARGET = sTextboxMidYPositions[var]; + } + } + + R_TEXTBOX_X_TARGET = sTextboxXPositions[var]; + R_TEXTBOX_END_YPOS = sTextboxEndIconYOffset[var] + R_TEXTBOX_Y_TARGET; + R_TEXT_CHOICE_YPOS(0) = R_TEXTBOX_Y_TARGET + 20; + R_TEXT_CHOICE_YPOS(1) = R_TEXTBOX_Y_TARGET + 32; + R_TEXT_CHOICE_YPOS(2) = R_TEXTBOX_Y_TARGET + 44; + osSyncPrintf("message->msg_disp_type=%x\n", msgCtx->textBoxProperties & 0xF0); + if (msgCtx->textBoxType == TEXTBOX_TYPE_NONE_BOTTOM || + msgCtx->textBoxType == TEXTBOX_TYPE_NONE_NO_SHADOW) { + msgCtx->msgMode = MSGMODE_TEXT_STARTING; + R_TEXTBOX_X = R_TEXTBOX_X_TARGET; + R_TEXTBOX_Y = R_TEXTBOX_Y_TARGET; + R_TEXTBOX_WIDTH = 256; + R_TEXTBOX_HEIGHT = 64; + R_TEXTBOX_TEXWIDTH = 512; + R_TEXTBOX_TEXHEIGHT = 512; + } else { + Message_GrowTextbox(msgCtx); + Audio_PlaySoundIfNotInCutscene(0); + msgCtx->stateTimer = 0; + msgCtx->msgMode = MSGMODE_TEXT_BOX_GROWING; + } + } + break; + case MSGMODE_TEXT_BOX_GROWING: + Message_GrowTextbox(msgCtx); + break; + case MSGMODE_TEXT_STARTING: + msgCtx->msgMode = MSGMODE_TEXT_NEXT_MSG; + if (YREG(31) == 0) { + Interface_SetDoAction(globalCtx, DO_ACTION_NEXT); + } + break; + case MSGMODE_TEXT_NEXT_MSG: + Message_Decode(globalCtx); + if (sTextFade) { + Interface_ChangeAlpha(1); + } + if (D_80153D74 != 0) { + msgCtx->textDrawPos = msgCtx->decodedTextLen; + D_80153D74 = 0; + } + break; + case MSGMODE_TEXT_CONTINUING: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + Message_Decode(globalCtx); + } + break; + case MSGMODE_TEXT_DISPLAYING: + if (msgCtx->textBoxType != TEXTBOX_TYPE_NONE_BOTTOM && YREG(31) == 0 && isB_Held && + !msgCtx->textUnskippable) { + sTextboxSkipped = true; + msgCtx->textDrawPos = msgCtx->decodedTextLen; + } + break; + case MSGMODE_TEXT_AWAIT_INPUT: + if (YREG(31) == 0 && Message_ShouldAdvance(globalCtx)) { + msgCtx->msgMode = MSGMODE_TEXT_DISPLAYING; + msgCtx->textDrawPos++; + } + break; + case MSGMODE_TEXT_DELAYED_BREAK: + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + msgCtx->msgMode = MSGMODE_TEXT_NEXT_MSG; + } + break; + case MSGMODE_TEXT_AWAIT_NEXT: + if (Message_ShouldAdvance(globalCtx)) { + msgCtx->msgMode = MSGMODE_TEXT_NEXT_MSG; + msgCtx->textUnskippable = false; + msgCtx->msgBufPos++; + } + break; + case MSGMODE_TEXT_DONE: + if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_FADING) { + msgCtx->stateTimer--; + if (msgCtx->stateTimer == 0) { + Message_CloseTextbox(globalCtx); + } + } else if (msgCtx->textboxEndType != TEXTBOX_ENDTYPE_PERSISTENT && + msgCtx->textboxEndType != TEXTBOX_ENDTYPE_EVENT && YREG(31) == 0) { + if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_2_CHOICE && + globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + if (Message_ShouldAdvance(globalCtx)) { + osSyncPrintf("OCARINA_MODE=%d -> ", globalCtx->msgCtx.ocarinaMode); + globalCtx->msgCtx.ocarinaMode = (msgCtx->choiceIndex == 0) ? OCARINA_MODE_02 : OCARINA_MODE_04; + osSyncPrintf("InRaceSeq=%d(%d) OCARINA_MODE=%d --> ", gSaveContext.eventInf[0] & 0xF, 1, + globalCtx->msgCtx.ocarinaMode); + Message_CloseTextbox(globalCtx); + osSyncPrintf("OCARINA_MODE=%d\n", globalCtx->msgCtx.ocarinaMode); + } + } else if (Message_ShouldAdvanceSilent(globalCtx)) { + osSyncPrintf("select=%d\n", msgCtx->textboxEndType); + if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_HAS_NEXT) { + Audio_PlaySoundGeneral(NA_SE_SY_MESSAGE_PASS, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Message_ContinueTextbox(globalCtx, sNextTextId); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Message_CloseTextbox(globalCtx); + } + } + } + break; + case MSGMODE_TEXT_CLOSING: + msgCtx->stateTimer--; + if (msgCtx->stateTimer != 0) { + break; + } + if ((msgCtx->textId >= 0xC2 && msgCtx->textId < 0xC7) || + (msgCtx->textId >= 0xFA && msgCtx->textId < 0xFE)) { + gSaveContext.healthAccumulator = 0x140; // Refill 20 hearts + } + if (msgCtx->textId == 0x301F || msgCtx->textId == 0xA || msgCtx->textId == 0xC || msgCtx->textId == 0xCF || + msgCtx->textId == 0x21C || msgCtx->textId == 9 || msgCtx->textId == 0x4078 || + msgCtx->textId == 0x2015 || msgCtx->textId == 0x3040) { + gSaveContext.unk_13EE = 0x32; + } + if (globalCtx->csCtx.state == 0) { + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("day_time=%x active_camera=%d ", gSaveContext.cutsceneIndex, globalCtx->activeCamera); + + if (msgCtx->textId != 0x2061 && msgCtx->textId != 0x2025 && msgCtx->textId != 0x208C && + ((msgCtx->textId < 0x88D || msgCtx->textId >= 0x893) || msgCtx->choiceIndex != 0) && + (msgCtx->textId != 0x3055 && gSaveContext.cutsceneIndex < 0xFFF0)) { + osSyncPrintf("=== day_time=%x ", ((void)0, gSaveContext.cutsceneIndex)); + if (globalCtx->activeCamera == MAIN_CAM) { + if (gSaveContext.unk_13EE == 0 || gSaveContext.unk_13EE == 1 || gSaveContext.unk_13EE == 2) { + gSaveContext.unk_13EE = 0x32; + } + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(gSaveContext.unk_13EE); + } + } + } + osSyncPrintf(VT_RST); + msgCtx->msgLength = 0; + msgCtx->msgMode = MSGMODE_NONE; + interfaceCtx->unk_1FA = interfaceCtx->unk_1FC = 0; + msgCtx->textId = msgCtx->stateTimer = 0; + + if (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_PERSISTENT) { + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_DEFAULT; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_02; + } else { + msgCtx->textboxEndType = TEXTBOX_ENDTYPE_DEFAULT; + } + if ((s32)(gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000) { + gSaveContext.inventory.questItems ^= 0x40000000; + gSaveContext.healthCapacity += 0x10; + gSaveContext.health += 0x10; + } + if (msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) { + if (sLastPlayedSong == OCARINA_SONG_SARIAS) { + //! @bug The last played song is not unset often, and if something interrupts the message system + // before it reaches this point after playing Saria's song, the song will be "stored". + // Later, if the ocarina has not been played and another textbox is closed, this handling + // for Saria's song will be carried out. + player->naviTextId = -0xE0; + player->naviActor->flags |= 0x10000; + } + if (msgCtx->ocarinaAction == OCARINA_ACTION_FREE_PLAY_DONE && + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01 || + globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_0B)) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + if (msgCtx->unk_E3F2 == OCARINA_SONG_SUNS) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_01; + } + } + } + sLastPlayedSong = 0xFF; + osSyncPrintf("OCARINA_MODE=%d chk_ocarina_no=%d\n", globalCtx->msgCtx.ocarinaMode, msgCtx->unk_E3F2); + break; + case MSGMODE_PAUSED: + break; + default: + msgCtx->lastOcaNoteIdx = OCARINA_NOTE_INVALID; + break; + } +} + +void Message_SetTables(void) { + OTRMessage_Init(); + + // OTRTODO + //sNesMessageEntryTablePtr = sNesMessageEntryTable; + //sGerMessageEntryTablePtr = sGerMessageEntryTable; + //sFraMessageEntryTablePtr = sFraMessageEntryTable; + //sStaffMessageEntryTablePtr = sStaffMessageEntryTable; +} + +// Appears to be file padding +UNK_TYPE D_80153D7C = 0x00000000; + +// This should be part of z_game_over.c, but cannot be moved there as the entire +// late_rodata section of this file is in the way +s16 gGameOverTimer = 0; diff --git a/soh/src/code/z_moji.c b/soh/src/code/z_moji.c new file mode 100644 index 000000000..dddaf4d3d --- /dev/null +++ b/soh/src/code/z_moji.c @@ -0,0 +1,144 @@ +/** + * Unused. A very simple utility for drawing text on screen. + */ + +#include "global.h" + +// OTRTODO +u64 gMojiFontTex[] = +{ + 0, +}; + +u64 gMojiFontTLUTs[4][4] = +{ + 0 +}; + +// how big to draw the characters on screen +#define DISP_CHAR_WIDTH 8 +#define DISP_CHAR_HEIGHT 8 + +// gMojiFontTex is a TEX_CHAR_COLS x TEX_CHAR_ROWS grid of characters, +// each character being TEX_CHAR_WIDTH x TEX_CHAR_HEIGHT in size. +// Each spot on the grid contains 4 characters, which are revealed by using different TLUTs. + +#define TEX_CHAR_WIDTH 8 +#define TEX_CHAR_HEIGHT 8 + +#define TEX_CHAR_COLS 2 +#define TEX_CHAR_ROWS 16 + +// A character `c = 0bRRRRRCTT` maps to row 0bRRRRR, column 0bC and TLUT 0bTT. +#define GET_CHAR_TLUT_INDEX(c) (c & 3) +// `/ 4` matches the `& 4` (`(c & 4) / 4` is the column the character is in) +#define GET_TEX_CHAR_S(c) ((u16)(c & 4) * ((1 << 5) * TEX_CHAR_WIDTH / 4)) +#define GET_TEX_CHAR_T(c) ((u16)(c >> 3) * ((1 << 5) * TEX_CHAR_HEIGHT)) + +u32 sFontColorRed = 255; +u32 sFontColorGreen = 255; +u32 sFontColorBlue = 255; +u32 sFontColorAlpha = 255; + +s32 sScreenPosX = 0; +s32 sScreenPosY = 0; + +s32 sCurTLUTIndex; + +void Moji_SetColor(u32 red, u32 green, u32 blue, u32 alpha) { + sFontColorRed = red; + sFontColorGreen = green; + sFontColorBlue = blue; + sFontColorAlpha = alpha; +} + +void Moji_SetPosition(s32 gridX, s32 gridY) { + if (gridX >= SCREEN_WIDTH / DISP_CHAR_WIDTH) { + sScreenPosX = SCREEN_WIDTH - DISP_CHAR_WIDTH; + } else if (gridX < 0) { + sScreenPosX = 0; + } else { + sScreenPosX = gridX * DISP_CHAR_WIDTH; + } + + if (gridY >= SCREEN_HEIGHT / DISP_CHAR_HEIGHT) { + sScreenPosY = SCREEN_HEIGHT - DISP_CHAR_HEIGHT; + } else if (gridY < 0) { + sScreenPosY = 0; + } else { + sScreenPosY = gridY * DISP_CHAR_HEIGHT; + } +} + +void Moji_DrawChar(GraphicsContext* gfxCtx, char c) { + s32 pad[2]; + + OPEN_DISPS(gfxCtx, "../z_moji.c", 86); + + if ((u32)gMojiFontTLUTs & 0xF) { + osSyncPrintf("moji_tlut --> %X\n", gMojiFontTLUTs); + } + + if (sCurTLUTIndex != GET_CHAR_TLUT_INDEX(c)) { + gDPLoadTLUT(POLY_OPA_DISP++, 16, 256, gMojiFontTLUTs[GET_CHAR_TLUT_INDEX(c)]); + sCurTLUTIndex = GET_CHAR_TLUT_INDEX(c); + } + gSPTextureRectangle(POLY_OPA_DISP++, sScreenPosX << 2, sScreenPosY << 2, (sScreenPosX + DISP_CHAR_WIDTH) << 2, + (sScreenPosY + DISP_CHAR_HEIGHT) << 2, G_TX_RENDERTILE, GET_TEX_CHAR_S(c), GET_TEX_CHAR_T(c), + (1 << 10) * TEX_CHAR_WIDTH / DISP_CHAR_WIDTH, (1 << 10) * TEX_CHAR_HEIGHT / DISP_CHAR_HEIGHT); + + CLOSE_DISPS(gfxCtx, "../z_moji.c", 123); +} + +/** + * Does not work as is in most cases. + * Can work if the render mode, combiner and possibly other settings are set correctly. + * For example this works with the render mode used in `GfxPrint_Setup`, + * and `G_CC_MODULATEI_PRIM` for both combiner cycles. + */ +void Moji_DrawString(GraphicsContext* gfxCtx, const char* str) { + s32 i; + + OPEN_DISPS(gfxCtx, "../z_moji.c", 137); + + if ((u32)gMojiFontTex & 0xF) { + osSyncPrintf("font_ff --> %X\n", gMojiFontTex); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, sFontColorRed, sFontColorGreen, sFontColorBlue, sFontColorAlpha); + + gDPLoadTextureBlock_4b(POLY_OPA_DISP++, (s32)gMojiFontTex, G_IM_FMT_CI, TEX_CHAR_COLS * TEX_CHAR_WIDTH, + TEX_CHAR_ROWS * TEX_CHAR_HEIGHT, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + sCurTLUTIndex = -1; + + for (i = 0; str[i] != '\0'; i++) { + switch (str[i]) { + case '\t': + sScreenPosX = (((sScreenPosX / DISP_CHAR_WIDTH) / 8) + 1) * DISP_CHAR_WIDTH * 8; + if (sScreenPosX >= SCREEN_WIDTH) { + sScreenPosX = 0; + sScreenPosY += DISP_CHAR_HEIGHT; + if (sScreenPosY >= SCREEN_HEIGHT) { + sScreenPosY = 0; + } + } + break; + case '\n': + case '\r': + sScreenPosX = 0; + sScreenPosY += DISP_CHAR_HEIGHT; + if (sScreenPosY >= SCREEN_HEIGHT) { + sScreenPosY = 0; + } + break; + default: + Moji_DrawChar(gfxCtx, str[i]); + sScreenPosX += DISP_CHAR_WIDTH; + } + } + + CLOSE_DISPS(gfxCtx, "../z_moji.c", 181); +} diff --git a/soh/src/code/z_msgevent.c b/soh/src/code/z_msgevent.c new file mode 100644 index 000000000..267180e1f --- /dev/null +++ b/soh/src/code/z_msgevent.c @@ -0,0 +1,20 @@ +#include "global.h" + +void MsgEvent_SendNullTask(void) { + s32 pad[4]; + OSScTask task; + OSMesgQueue queue; + OSMesg msg; + u32 pad2[1]; + + task.next = NULL; + task.flags = OS_SC_RCP_MASK; + task.msgQ = &queue; + task.msg = NULL; + task.framebuffer = NULL; + task.list.t.type = M_NULTASK; + osCreateMesgQueue(task.msgQ, &msg, 1); + osSendMesg(&gSchedContext.cmdQ, &task, OS_MESG_BLOCK); + Sched_SendEntryMsg(&gSchedContext); + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); +} diff --git a/soh/src/code/z_olib.c b/soh/src/code/z_olib.c new file mode 100644 index 000000000..72cd6694b --- /dev/null +++ b/soh/src/code/z_olib.c @@ -0,0 +1,224 @@ +#include "global.h" + +/** + * Calculates the distances between `a` and `b` + */ +f32 OLib_Vec3fDist(Vec3f* a, Vec3f* b) { + f32 dx = a->x - b->x; + f32 dy = a->y - b->y; + f32 dz = a->z - b->z; + + return sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); +} + +/** + * Calculates the distances between `a` and `b`, and outputs the vector + * created by the difference into `dest` + */ + +f32 OLib_Vec3fDistOutDiff(Vec3f* a, Vec3f* b, Vec3f* dest) { + dest->x = a->x - b->x; + dest->y = a->y - b->y; + dest->z = a->z - b->z; + + return sqrtf(SQ(dest->x) + SQ(dest->y) + SQ(dest->z)); +} + +/** + * Calculates the distances on the xz plane between `a` and `b` + */ +f32 OLib_Vec3fDistXZ(Vec3f* a, Vec3f* b) { + return sqrtf(SQ(a->x - b->x) + SQ(a->z - b->z)); +} + +/** + * Clamps `val` to a maximum of -`min` as `val` approaches zero, and a minimum of + * `min` as `val` approaches zero + */ +f32 OLib_ClampMinDist(f32 val, f32 min) { + return (min <= fabsf(val)) ? val : ((val >= 0) ? min : -min); +} + +/** + * Clamps `val` to a minimum of -`max` as `val` approaches -`max`, and a maximum of `max` + * as `val` approaches `max` + */ +f32 OLib_ClampMaxDist(f32 val, f32 max) { + return (fabsf(val) <= max) ? val : ((val >= 0) ? max : -max); +} + +/** + * Takes the difference of points b and a, and creates a normal vector + */ +Vec3f* OLib_Vec3fDistNormalize(Vec3f* dest, Vec3f* a, Vec3f* b) { + Vec3f v1; + Vec3f v2; + f32 dist; + + v1.x = b->x - a->x; + v1.y = b->y - a->y; + v1.z = b->z - a->z; + + dist = OLib_ClampMinDist(sqrtf(SQ(v1.x) + SQ(v1.y) + SQ(v1.z)), 0.01f); + + v2.x = v1.x / dist; + v2.y = v1.y / dist; + v2.z = v1.z / dist; + + *dest = v2; + + return dest; +} + +/** + * Takes the spherical coordinate `sph`, and converts it into a x,y,z position + */ +Vec3f* OLib_VecSphToVec3f(Vec3f* dest, VecSph* sph) { + Vec3f v; + f32 sinPitch; + f32 cosPitch = Math_CosS(sph->pitch); + f32 sinYaw; + f32 cosYaw = Math_CosS(sph->yaw); + + sinPitch = Math_SinS(sph->pitch); + sinYaw = Math_SinS(sph->yaw); + + v.x = sph->r * sinPitch * sinYaw; + v.y = sph->r * cosPitch; + v.z = sph->r * sinPitch * cosYaw; + + *dest = v; + + return dest; +} + +/** + * Takes the geographic point `sph` and converts it into a x,y,z position + */ +Vec3f* OLib_VecSphGeoToVec3f(Vec3f* dest, VecSph* sph) { + VecSph geo; + + geo.r = sph->r; + geo.pitch = 0x3FFF - sph->pitch; + geo.yaw = sph->yaw; + + return OLib_VecSphToVec3f(dest, &geo); +} + +/** + * Takes the point `vec`, and converts it into a spherical coordinate + */ +VecSph* OLib_Vec3fToVecSph(VecSph* dest, Vec3f* vec) { + VecSph sph; + + f32 distSquared = SQ(vec->x) + SQ(vec->z); + f32 dist = sqrtf(distSquared); + + if ((dist == 0.0f) && (vec->y == 0.0f)) { + sph.pitch = 0; + } else { + sph.pitch = DEGF_TO_BINANG(RADF_TO_DEGF(Math_FAtan2F(dist, vec->y))); + } + + sph.r = sqrtf(SQ(vec->y) + distSquared); + if ((vec->x == 0.0f) && (vec->z == 0.0f)) { + sph.yaw = 0; + } else { + sph.yaw = DEGF_TO_BINANG(RADF_TO_DEGF(Math_FAtan2F(vec->x, vec->z))); + } + + *dest = sph; + + return dest; +} + +/** + * Takes the point `vec`, and converts it to a geographic coordinate + */ +VecSph* OLib_Vec3fToVecSphGeo(VecSph* dest, Vec3f* vec) { + VecSph sph; + + OLib_Vec3fToVecSph(&sph, vec); + sph.pitch = 0x3FFF - sph.pitch; + + *dest = sph; + + return dest; +} + +/** + * Takes the differences of positions `a` and `b`, and converts them to spherical coordinates + */ +VecSph* OLib_Vec3fDiffToVecSph(VecSph* dest, Vec3f* a, Vec3f* b) { + Vec3f sph; + + sph.x = b->x - a->x; + sph.y = b->y - a->y; + sph.z = b->z - a->z; + + return OLib_Vec3fToVecSph(dest, &sph); +} + +/** + * Takes the difference of positions `a` and `b`, and converts them to geographic coordinates + */ +VecSph* OLib_Vec3fDiffToVecSphGeo(VecSph* dest, Vec3f* a, Vec3f* b) { + Vec3f sph; + + sph.x = b->x - a->x; + sph.y = b->y - a->y; + sph.z = b->z - a->z; + + return OLib_Vec3fToVecSphGeo(dest, &sph); +} + +/** + * Gets the pitch/yaw of the vector formed from `b`-`a`, result is in radians + */ +Vec3f* OLib_Vec3fDiffRad(Vec3f* dest, Vec3f* a, Vec3f* b) { + Vec3f anglesRad; + + anglesRad.x = Math_FAtan2F(b->z - a->z, b->y - a->y); + anglesRad.y = Math_FAtan2F(b->x - a->x, b->z - a->z); + anglesRad.z = 0; + + *dest = anglesRad; + + return dest; +} + +/** + * Gets the pitch/yaw of the vector formed from `b`-`a`, result is in degrees + */ +Vec3f* OLib_Vec3fDiffDegF(Vec3f* dest, Vec3f* a, Vec3f* b) { + Vec3f anglesRad; + Vec3f anglesDegrees; + + OLib_Vec3fDiffRad(&anglesRad, a, b); + + anglesDegrees.x = RADF_TO_DEGF(anglesRad.x); + anglesDegrees.y = RADF_TO_DEGF(anglesRad.y); + anglesDegrees.z = 0.0f; + + *dest = anglesDegrees; + + return dest; +} + +/** + * Gets the pitch/yaw of the vector formed from `b`-`a`, result is in binary degrees + */ +Vec3s* OLib_Vec3fDiffBinAng(Vec3s* dest, Vec3f* a, Vec3f* b) { + Vec3f anglesRad; + Vec3s anglesBinAng; + + OLib_Vec3fDiffRad(&anglesRad, a, b); + + anglesBinAng.x = DEGF_TO_BINANG(RADF_TO_DEGF(anglesRad.x)); + anglesBinAng.y = DEGF_TO_BINANG(RADF_TO_DEGF(anglesRad.y)); + anglesBinAng.z = 0.0f; + + *dest = anglesBinAng; + + return dest; +} diff --git a/soh/src/code/z_onepointdemo.c b/soh/src/code/z_onepointdemo.c new file mode 100644 index 000000000..96ef33c18 --- /dev/null +++ b/soh/src/code/z_onepointdemo.c @@ -0,0 +1,1377 @@ +#include "global.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Sw/z_en_sw.h" + +static s16 sDisableAttention = false; +static s16 sUnused = -1; +static s32 sPrevFrameCs1100 = -4096; + +#include "z_onepointdemo_data.c" + +void OnePointCutscene_AddVecSphToVec3f(Vec3f* dst, Vec3f* src, VecSph* vecSph) { + Vec3f out; + Vec3f vec; + + OLib_VecSphGeoToVec3f(&vec, vecSph); + + out.x = src->x + vec.x; + out.y = src->y + vec.y; + out.z = src->z + vec.z; + if (dst) {} + *dst = out; +} + +s16 OnePointCutscene_Vec3fYaw(Vec3f* vec1, Vec3f* vec2) { + return DEGF_TO_BINANG(RADF_TO_DEGF(Math_FAtan2F(vec2->x - vec1->x, vec2->z - vec1->z))); +} + +void OnePointCutscene_Vec3sToVec3f(Vec3f* src, Vec3s* dst) { + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} + +s32 OnePointCutscene_BgCheckLineTest(CollisionContext* colCtx, Vec3f* vec1, Vec3f* vec2) { + Vec3f posResult; + s32 bgId; + CollisionPoly* outPoly = NULL; + + return BgCheck_CameraLineTest1(colCtx, vec1, vec2, &posResult, &outPoly, true, true, true, false, &bgId); +} + +f32 OnePointCutscene_RaycastFloor(CollisionContext* colCtx, Vec3f* pos) { + CollisionPoly* outPoly; + s32 bgId; + + return BgCheck_EntityRaycastFloor3(colCtx, &outPoly, &bgId, pos); +} + +void OnePointCutscene_SetCsCamPoints(Camera* camera, s16 actionParameters, s16 initTimer, CutsceneCameraPoint* atPoints, + CutsceneCameraPoint* eyePoints) { + OnePointCsCamera* onePointCamData = (OnePointCsCamera*)&camera->paramData; + + onePointCamData->atPoints = atPoints; + onePointCamData->eyePoints = eyePoints; + onePointCamData->actionParameters = actionParameters; + onePointCamData->initTimer = initTimer; +} + +s32 OnePointCutscene_SetInfo(GlobalContext* globalCtx, s16 camIdx, s16 csId, Actor* actor, s16 timer) { + Camera* csCam = globalCtx->cameraPtrs[camIdx]; + Camera* childCam = globalCtx->cameraPtrs[csCam->childCamIdx]; + Camera* mainCam = globalCtx->cameraPtrs[MAIN_CAM]; + Player* player = mainCam->player; + VecSph spD0; + s32 i; + Vec3f spC0; + Vec3f spB4; + PosRot spA0; + PosRot sp8C; + f32 tempRand; + Unique9OnePointCs* csInfo = ONEPOINT_CS_INFO(csCam); + + switch (csId) { + case 1020: + if (timer < 20) { + timer = 20; + } + D_801208EC[0].atTargetInit = globalCtx->view.lookAt; + D_801208EC[0].eyeTargetInit = globalCtx->view.eye; + D_801208EC[0].fovTargetInit = globalCtx->view.fovy; + D_801208EC[1].atTargetInit = mainCam->at; + D_801208EC[1].eyeTargetInit = mainCam->eye; + D_801208EC[1].fovTargetInit = mainCam->fov; + D_801208EC[1].timerInit = timer - 1; + csCam->timer = timer + 1; + D_801208EC[1].lerpStepScale = 1.0f / (0.5f * timer); + + csInfo->keyFrames = D_801208EC; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 1030: + D_80120964[0].atTargetInit = globalCtx->view.lookAt; + D_80120964[0].eyeTargetInit = globalCtx->view.eye; + D_80120964[0].fovTargetInit = globalCtx->view.fovy; + OLib_Vec3fDiffToVecSphGeo(&spD0, &mainCam->at, &mainCam->eye); + D_80120964[1].eyeTargetInit.y = BINANG_TO_DEGF(spD0.yaw); + D_80120964[1].timerInit = timer - 1; + + csInfo->keyFrames = D_80120964; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 5000: + D_801209B4[0].atTargetInit = D_801209B4[1].atTargetInit = globalCtx->view.lookAt; + D_801209B4[0].eyeTargetInit = globalCtx->view.eye; + D_801209B4[0].fovTargetInit = D_801209B4[2].fovTargetInit = globalCtx->view.fovy; + OLib_Vec3fDiffToVecSphGeo(&spD0, &actor->focus.pos, &mainCam->at); + spD0.r = mainCam->dist; + OnePointCutscene_AddVecSphToVec3f(&D_801209B4[1].eyeTargetInit, &D_801209B4[1].atTargetInit, &spD0); + D_801209B4[1].atTargetInit.y += 20.0f; + + csInfo->keyFrames = D_801209B4; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 5010: + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_ATTENTION); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &mainCam->at, &mainCam->eye); + csCam->roll = 0; + break; + case 9500: + csInfo->keyFrames = D_80120A54; + csInfo->keyFrameCnt = 3; + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 2260: + D_80120ACC[0].atTargetInit.x = D_80120ACC[2].atTargetInit.x = + ((mainCam->globalCtx->state.frames & 1) ? -10.0f : 10.0f) + (Rand_ZeroOne() * 8.0f); + + D_80120ACC[0].eyeTargetInit.x = D_80120ACC[2].eyeTargetInit.x = + ((mainCam->globalCtx->state.frames & 1) ? 20.0f : -20.0f) + (Rand_ZeroOne() * 5.0f); + + csInfo->keyFrames = D_80120ACC; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 2270: + csInfo->keyFrames = D_80120B94; + csInfo->keyFrameCnt = 11; + + for (i = 0; i < csInfo->keyFrameCnt - 3; i++) { + if (D_80120B94[i].actionFlags != 0x8F) { + D_80120B94[i].atTargetInit.x = Rand_ZeroOne() * 5.0f; + D_80120B94[i].atTargetInit.z = (Rand_ZeroOne() * 30.0f) + 10.0f; + D_80120B94[i].eyeTargetInit.x = (Rand_ZeroOne() * 100.0f) + 20.0f; + D_80120B94[i].eyeTargetInit.z = (Rand_ZeroOne() * 80.0f) + 50.0f; + } + } + + D_80120B94[camIdx - 1].eyeTargetInit.y = + ((mainCam->globalCtx->state.frames & 1) ? 3.0f : -3.0f) + Rand_ZeroOne(); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 5); + Quake_SetSpeed(i, 400); + Quake_SetQuakeValues(i, 4, 5, 40, 0x3C); + Quake_SetCountdown(i, 1600); + break; + case 2280: + csInfo->keyFrames = D_80120D4C; + csInfo->keyFrameCnt = 7; + + for (i = 0; i < csInfo->keyFrameCnt - 3; i++) { + if (D_80120D4C[i].actionFlags != 0x8F) { + D_80120D4C[i].atTargetInit.x = Rand_ZeroOne() * 20.0f; + D_80120D4C[i].atTargetInit.z = (Rand_ZeroOne() * 40.0f) + 10.0f; + D_80120D4C[i].eyeTargetInit.x = (Rand_ZeroOne() * 40.0f) + 60.0f; + D_80120D4C[i].eyeTargetInit.z = (Rand_ZeroOne() * 40.0f) + 80.0f; + } + } + D_80120D4C[camIdx - 1].eyeTargetInit.y = + ((mainCam->globalCtx->state.frames & 1) ? 3.0f : -3.0f) + Rand_ZeroOne(); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 5); + Quake_SetSpeed(i, 400); + Quake_SetQuakeValues(i, 2, 3, 200, 0x32); + Quake_SetCountdown(i, 9999); + break; + case 2220: + csInfo->keyFrames = D_80120E64; + csInfo->keyFrameCnt = 8; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 5); + Quake_SetSpeed(i, 400); + Quake_SetQuakeValues(i, 2, 2, 50, 0); + Quake_SetCountdown(i, 280); + break; + case 2230: + if (player->actor.world.pos.z < 1000.0f) { + D_80120FA4[0].eyeTargetInit.x = -D_80120FA4[0].eyeTargetInit.x; + D_80120FA4[2].eyeTargetInit.x = -D_80120FA4[2].eyeTargetInit.x; + } + + csInfo->keyFrames = D_80120FA4; + csInfo->keyFrameCnt = 6; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 2340: + csInfo->keyFrames = D_80121094; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 5); + Quake_SetSpeed(i, 400); + Quake_SetQuakeValues(i, 2, 2, 50, 0); + Quake_SetCountdown(i, 60); + break; + case 2350: + csInfo->keyFrames = D_8012110C; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 2200: { + s16 sp82; + s16 sp80; + s16 sp7E; + s16 sp7C; + + Actor_GetScreenPos(globalCtx, &player->actor, &sp82, &sp7E); + Actor_GetScreenPos(globalCtx, actor, &sp80, &sp7C); + if ((sp82 > 0) && (sp82 < 320) && (sp7E > 0) && (sp7E < 240) && (sp80 > 0) && (sp80 < 320) && (sp7C > 0) && + (sp7C < 240) && + !OnePointCutscene_BgCheckLineTest(&globalCtx->colCtx, &actor->focus.pos, &player->actor.focus.pos)) { + D_80121184[0].atTargetInit.x = (globalCtx->view.lookAt.x + actor->focus.pos.x) * 0.5f; + D_80121184[0].atTargetInit.y = (globalCtx->view.lookAt.y + actor->focus.pos.y) * 0.5f; + D_80121184[0].atTargetInit.z = (globalCtx->view.lookAt.z + actor->focus.pos.z) * 0.5f; + D_80121184[0].eyeTargetInit = globalCtx->view.eye; + D_80121184[0].eyeTargetInit.y = player->actor.focus.pos.y + 20.0f; + D_80121184[0].fovTargetInit = mainCam->fov * 0.75f; + + csInfo->keyFrames = D_80121184; + csInfo->keyFrameCnt = 2; + } else { + D_801211D4[0].atTargetInit.x = actor->focus.pos.x; + D_801211D4[0].atTargetInit.y = actor->focus.pos.y - 5.0f; + D_801211D4[0].atTargetInit.z = actor->focus.pos.z; + spC0 = ((EnSw*)actor)->unk_364; + osSyncPrintf("%s(%d): xyz_t: %s (%f %f %f)\n", "../z_onepointdemo.c", 1671, "&cp", spC0.x, spC0.y, + spC0.z); + D_801211D4[0].eyeTargetInit.x = (actor->focus.pos.x + (120.0f * spC0.x)) - (Rand_ZeroOne() * 20.0f); + D_801211D4[0].eyeTargetInit.y = actor->focus.pos.y + (120.0f * spC0.y) + 20.0f; + D_801211D4[0].eyeTargetInit.z = (actor->focus.pos.z + (120.0f * spC0.z)) - (Rand_ZeroOne() * 20.0f); + + csInfo->keyFrames = D_801211D4; + csInfo->keyFrameCnt = 2; + } + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_UNK3); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + } break; + case 2290: { + Actor* rideActor = player->rideActor; + + func_8002DF54(globalCtx, NULL, 8); + if (rideActor != NULL) { + rideActor->freezeTimer = 180; + } + + csInfo->keyFrames = D_80121224; + csInfo->keyFrameCnt = 6; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + } break; + case 5120: + func_8002DF54(globalCtx, NULL, 8); + + csInfo->keyFrames = D_80121314; + csInfo->keyFrameCnt = 1; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4510: + D_8012133C[0].eyeTargetInit = actor->world.pos; + D_8012133C[0].eyeTargetInit.y = player->actor.world.pos.y + 40.0f; + func_8002DF54(globalCtx, NULL, 8); + + csInfo->keyFrames = D_8012133C; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4500: + Actor_GetFocus(&spA0, actor); + spC0 = spA0.pos; + spC0.y = OnePointCutscene_RaycastFloor(&globalCtx->colCtx, &spC0) + 40.0f; + spD0.r = 150.0f; + spD0.yaw = spA0.rot.y; + spD0.pitch = 0x3E8; + + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + func_8002DF54(globalCtx, NULL, 8); + csCam->roll = 0; + csCam->fov = 50.0f; + if (csCam->childCamIdx != SUBCAM_FREE) { + OnePointCutscene_EndCutscene(globalCtx, csCam->childCamIdx); + } + break; + case 2210: + OLib_Vec3fDiffToVecSphGeo(&spD0, &player->actor.world.pos, &actor->world.pos); + D_801213B4[0].eyeTargetInit.y = D_801213B4[1].eyeTargetInit.y = D_801213B4[2].eyeTargetInit.y = + D_801213B4[2].atTargetInit.y = BINANG_TO_DEGF(spD0.yaw); + if (Rand_ZeroOne() < 0.0f) { + D_801213B4[3].eyeTargetInit.x = -D_801213B4[3].eyeTargetInit.x; + } + func_8002DF54(globalCtx, NULL, 8); + + csInfo->keyFrames = D_801213B4; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 1010: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &childCam->at, &childCam->eye); + Gameplay_CameraSetFov(globalCtx, camIdx, childCam->fov); + Gameplay_SetCameraRoll(globalCtx, camIdx, childCam->roll); + break; + case 9601: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_CS_3); + Gameplay_CameraChangeSetting(globalCtx, MAIN_CAM, mainCam->prevSetting); + OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120398); + break; + case 9602: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_CS_3); + Gameplay_CameraChangeSetting(globalCtx, MAIN_CAM, mainCam->prevSetting); + OnePointCutscene_SetCsCamPoints(csCam, D_80120430 | 0x1000, D_8012042C, D_80120308, D_80120434); + break; + case 4175: + csInfo->keyFrames = D_8012147C; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4180: + spC0.x = -1881.0f; + spC0.y = 766.0f; + spC0.z = -330.0f; + spB4.x = -1979.0f; + spB4.y = 703.0f; + spB4.z = -269.0f; + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 6; + csCam->fov = 75.0f; + func_8002DF54(globalCtx, NULL, 8); + break; + case 3040: + func_8002DF54(globalCtx, NULL, 8); + D_8012151C[0].timerInit = timer - 1; + + csInfo->keyFrames = D_8012151C; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3020: + D_8012156C[1].timerInit = timer - 1; + if (mainCam->globalCtx->state.frames & 1) { + D_8012156C[0].atTargetInit.x = -D_8012156C[0].atTargetInit.x; + D_8012156C[0].eyeTargetInit.x = -D_8012156C[0].eyeTargetInit.x; + D_8012156C[1].atTargetInit.x = -D_8012156C[1].atTargetInit.x; + D_8012156C[1].eyeTargetInit.x = -D_8012156C[1].eyeTargetInit.x; + } + tempRand = Rand_ZeroOne() * 15.0f; + D_8012156C[0].eyeTargetInit.x += tempRand; + D_8012156C[1].eyeTargetInit.x += tempRand; + + csInfo->keyFrames = D_8012156C; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + func_8002DF54(globalCtx, NULL, 8); + break; + case 3010: + D_801215BC[0].timerInit = timer; + + csInfo->keyFrames = D_801215BC; + csInfo->keyFrameCnt = 1; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3070: + csInfo->keyFrames = D_801215E4; + csInfo->keyFrameCnt = 10; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 3); + Quake_SetSpeed(i, 22000); + Quake_SetQuakeValues(i, 2, 0, 200, 0); + Quake_SetCountdown(i, 10); + break; + case 3080: + csInfo->keyFrames = D_80121774; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3090: + func_8002DF54(globalCtx, NULL, 8); + + csInfo->keyFrames = D_80121814; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3100: + VEC_SET(spB4, 0.0f, -280.0f, -1400.0f); + + Actor_GetFocus(&spA0, actor); + spC0 = spA0.pos; + func_800C0808(globalCtx, camIdx, player, CAM_SET_PIVOT_VERTICAL); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0; + csCam->fov = 70.0f; + func_8002DF54(globalCtx, NULL, 8); + break; + case 3380: + case 3065: + csInfo->keyFrames = D_801218B4; + csInfo->keyFrameCnt = 2; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 24000); + Quake_SetQuakeValues(i, 2, 0, 0, 0); + Quake_SetCountdown(i, 160); + break; + case 3060: + csInfo->keyFrames = D_80121904; + csInfo->keyFrameCnt = 2; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3050: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_CS_3); + func_8002DF54(globalCtx, &player->actor, 5); + OnePointCutscene_SetCsCamPoints(csCam, D_80120304 | 0x2000, D_80120300, D_8012013C, D_8012021C); + func_80078884(NA_SE_SY_CORRECT_CHIME); + OnePointCutscene_Vec3sToVec3f(&mainCam->at, &D_8012013C[D_801202FC - 2].pos); + OnePointCutscene_Vec3sToVec3f(&mainCam->eye, &D_8012021C[D_801202FC - 2].pos); + D_8012013C[D_801202FC - 3].pos.x += + (D_8012013C[D_801202FC - 2].pos.x - D_8012013C[D_801202FC - 3].pos.x) / 2; + D_8012013C[D_801202FC - 3].pos.y += + (D_8012013C[D_801202FC - 2].pos.y - D_8012013C[D_801202FC - 3].pos.y) / 2; + D_8012013C[D_801202FC - 3].pos.z += + (D_8012013C[D_801202FC - 2].pos.z - D_8012013C[D_801202FC - 3].pos.z) / 2; + D_8012021C[D_801202FC - 3].pos.x += + (D_8012021C[D_801202FC - 2].pos.x - D_8012021C[D_801202FC - 3].pos.x) / 2; + D_8012021C[D_801202FC - 3].pos.y += + (D_8012021C[D_801202FC - 2].pos.y - D_8012021C[D_801202FC - 3].pos.y) / 2; + D_8012021C[D_801202FC - 3].pos.z += + (D_8012021C[D_801202FC - 2].pos.z - D_8012021C[D_801202FC - 3].pos.z) / 2; + + i = Quake_Add(mainCam, 3); + Quake_SetSpeed(i, 30000); + Quake_SetQuakeValues(i, 2, 1, 1, 0); + Quake_SetCountdown(i, 200); + break; + case 3120: + csInfo->keyFrames = D_80121954[-(timer + 101)]; + csCam->timer = 100; + csCam->unk_14C |= 2; + csInfo->keyFrameCnt = 2; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3130: + csInfo->keyFrames = D_80121A44; + csInfo->keyFrameCnt = 12; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + csCam->unk_14C |= 2; + break; + case 3140: + D_80121C24[0].atTargetInit = globalCtx->view.lookAt; + D_80121C24[0].eyeTargetInit = globalCtx->view.eye; + D_80121C24[0].fovTargetInit = globalCtx->view.fovy; + + csInfo->keyFrames = D_80121C24; + csInfo->keyFrameCnt = 7; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3150: + spC0.x = 1890.0f; + spC0.y = 886.0f; + spC0.z = -1432.0f; + spB4.x = 1729.0f; + spB4.y = 995.0f; + spB4.z = -1405.0f; + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0x50; + csCam->fov = 55.0f; + func_8002DF38(globalCtx, &player->actor, 8); + break; + case 3170: + Actor_GetWorld(&spA0, actor); + spC0 = spA0.pos; + spD0.pitch = -0x5DC; + spC0.y += 50.0f; + spD0.r = 250.0f; + Actor_GetWorld(&spA0, &player->actor); + spD0.yaw = OnePointCutscene_Vec3fYaw(&spC0, &spA0.pos) - 0x7D0; + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + Gameplay_CopyCamera(globalCtx, MAIN_CAM, camIdx); + csCam->roll = -1; + csCam->fov = 55.0f; + func_8002DF38(globalCtx, actor, 1); + break; + case 3160: + Actor_GetWorld(&spA0, actor); + spC0 = spA0.pos; + spD0.pitch = 0; + spD0.yaw = spA0.rot.y; + spD0.r = 150.0f; + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0; + csCam->fov = 55.0f; + func_8002DF38(globalCtx, &player->actor, 8); + break; + case 3180: + Actor_GetWorldPosShapeRot(&spA0, actor); + spC0 = spA0.pos; + spC0.y += 120.0f; + spD0.r = 300.0f; + spD0.yaw = spA0.rot.y; + spD0.pitch = -0xAF0; + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0; + csCam->fov = 60.0f; + func_8002DF38(globalCtx, actor, 1); + break; + case 3190: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FOREST_DEFEAT_POE); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + func_8002DF38(globalCtx, actor, 0xC); + break; + case 3230: + spC0.x = 120.0f; + spC0.y = 265.0f; + spC0.z = -1570.0f; + spB4.x = 80.0f; + spB4.y = 445.0f; + spB4.z = -1425.0f; + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0x1E; + csCam->fov = 75.0f; + func_8002DF38(globalCtx, &player->actor, 8); + Actor_GetWorldPosShapeRot(&spA0, actor); + Actor_GetFocus(&sp8C, &player->actor); + spC0.x = sp8C.pos.x; + spC0.y = sp8C.pos.y + 70.0f; + spC0.z = sp8C.pos.z; + OLib_Vec3fDiffToVecSphGeo(&spD0, &spA0.pos, &sp8C.pos); + spD0.pitch = 0x5DC; + spD0.r = 120.0f; + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + Gameplay_CameraSetAtEye(globalCtx, MAIN_CAM, &spC0, &spB4); + + i = Quake_Add(csCam, 3); + Quake_SetSpeed(i, 22000); + Quake_SetQuakeValues(i, 1, 0, 0, 0); + Quake_SetCountdown(i, 90); + break; + case 6010: + Actor_GetWorld(&spA0, actor); + spC0 = spA0.pos; + spD0.pitch = 0; + spC0.y += 70.0f; + spD0.yaw = spA0.rot.y + 0x7FFF; + spD0.r = 300.0f; + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0; + csCam->fov = 45.0f; + func_8002DF38(globalCtx, &player->actor, 8); + break; + case 3220: + Actor_GetFocus(&spA0, actor); + spC0 = spA0.pos; + func_800C0808(globalCtx, camIdx, player, CAM_SET_PIVOT_VERTICAL); + Actor_GetWorld(&spA0, &player->actor); + OLib_Vec3fDiffToVecSphGeo(&spD0, &spC0, &spA0.pos); + spD0.yaw += 0x3E8; + spD0.r = 400.0f; + OnePointCutscene_AddVecSphToVec3f(&spB4, &spC0, &spD0); + spB4.y = spA0.pos.y + 60.0f; + Gameplay_CameraSetAtEye(globalCtx, camIdx, &spC0, &spB4); + csCam->roll = 0; + csCam->fov = 75.0f; + player->actor.shape.rot.y = player->actor.world.rot.y = player->currentYaw = spD0.yaw + 0x7FFF; + func_8002DF54(globalCtx, NULL, 8); + break; + case 3240: + D_80121D3C[2].timerInit = timer - 5; + + csInfo->keyFrames = D_80121D3C; + csInfo->keyFrameCnt = 3; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 6001: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_CS_3); + func_8002DF54(globalCtx, NULL, 8); + Actor_GetWorld(&spA0, actor); + if (spA0.pos.z > -750.0f) { + OnePointCutscene_SetCsCamPoints(csCam, D_801208E8, D_801208E4, D_801206A0, D_80120820); + } else { + OnePointCutscene_SetCsCamPoints(csCam, D_801208E8, D_801208E4, D_801206A0, D_80120760); + } + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 32000); + Quake_SetQuakeValues(i, 0, 0, 20, 0); + Quake_SetCountdown(i, D_801208E4 - 10); + break; + case 3400: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_CS_3); + func_8002DF38(globalCtx, &player->actor, 8); + OnePointCutscene_SetCsCamPoints(csCam, D_8012069C | 0x2000, D_80120698, D_801204D4, D_801205B4); + OnePointCutscene_Vec3sToVec3f(&mainCam->eye, &D_801205B4[D_80120694 - 2].pos); + OnePointCutscene_Vec3sToVec3f(&mainCam->at, &D_801204D4[D_80120694 - 2].pos); + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 0x4E20); + Quake_SetQuakeValues(i, 1, 0, 50, 0); + Quake_SetCountdown(i, D_80120698 - 20); + break; + case 3390: + player->actor.shape.rot.y = player->actor.world.rot.y = player->currentYaw = -0x3FD9; + + csInfo->keyFrames = D_80121DB4; + csInfo->keyFrameCnt = 9; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3310: + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FIRE_STAIRCASE); + func_8002DF54(globalCtx, NULL, 8); + Gameplay_CopyCamera(globalCtx, camIdx, MAIN_CAM); + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 32000); + Quake_SetQuakeValues(i, 2, 0, 0, 0); + Quake_SetCountdown(i, timer); + break; + case 3290: + D_80121F1C[0].atTargetInit = globalCtx->view.lookAt; + D_80121F1C[0].eyeTargetInit = globalCtx->view.eye; + D_80121F1C[0].fovTargetInit = globalCtx->view.fovy; + Actor_GetFocus(&spA0, actor); + player->actor.shape.rot.y = player->actor.world.rot.y = player->currentYaw = spA0.rot.y; + + csInfo->keyFrames = D_80121F1C; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 3); + Quake_SetSpeed(i, 12000); + Quake_SetQuakeValues(i, 0, 0, 1000, 0); + Quake_SetCountdown(i, 5); + break; + case 3340: + D_80121FBC[0].atTargetInit = globalCtx->view.lookAt; + D_80121FBC[0].eyeTargetInit = globalCtx->view.eye; + D_80121FBC[0].fovTargetInit = globalCtx->view.fovy; + + csInfo->keyFrames = D_80121FBC; + csInfo->keyFrameCnt = 4; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 3); + Quake_SetSpeed(i, 12000); + Quake_SetQuakeValues(i, 0, 0, 1000, 0); + Quake_SetCountdown(i, 5); + break; + case 3360: + csInfo->keyFrames = D_8012205C; + csInfo->keyFrameCnt = 3; + + func_8002DF38(globalCtx, &player->actor, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3350: + D_801220D4[0].atTargetInit = globalCtx->view.lookAt; + D_801220D4[0].eyeTargetInit = globalCtx->view.eye; + D_801220D4[0].fovTargetInit = globalCtx->view.fovy; + if (actor->world.pos.x > 0.0f) { + D_801220D4[1].rollTargetInit = -D_801220D4[1].rollTargetInit; + D_801220D4[2].rollTargetInit = -D_801220D4[2].rollTargetInit; + D_801220D4[1].atTargetInit.x = -D_801220D4[1].atTargetInit.x; + D_801220D4[1].atTargetInit.y = 50.0f; + D_801220D4[1].eyeTargetInit.y = 80.0f; + D_801220D4[1].eyeTargetInit.x = -D_801220D4[1].eyeTargetInit.x; + } + func_8002DF54(globalCtx, NULL, 8); + + csInfo->keyFrames = D_801220D4; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3330: + csInfo->keyFrames = D_8012219C; + csInfo->keyFrameCnt = 7; + + func_8002DF38(globalCtx, &player->actor, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3410: + csInfo->keyFrames = D_801222B4; + csInfo->keyFrameCnt = 5; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 32000); + Quake_SetQuakeValues(i, 4, 0, 0, 0); + Quake_SetCountdown(i, 20); + break; + case 3450: + csInfo->keyFrames = D_8012237C; + csInfo->keyFrameCnt = 2; + + func_8002DF38(globalCtx, &player->actor, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 32000); + Quake_SetQuakeValues(i, 2, 0, 0, 0); + Quake_SetCountdown(i, 10); + break; + case 3440: + csInfo->keyFrames = D_801223CC; + csInfo->keyFrameCnt = 6; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + player->stateFlags1 |= 0x20000000; + player->actor.freezeTimer = 90; + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 32000); + Quake_SetQuakeValues(i, 2, 0, 0, 0); + Quake_SetCountdown(i, 10); + break; + case 3430: + csInfo->keyFrames = D_801224BC; + csInfo->keyFrameCnt = 7; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 1); + Quake_SetSpeed(i, 32000); + Quake_SetQuakeValues(i, 1, 0, 10, 0); + Quake_SetCountdown(i, 20); + break; + case 4100: + csInfo->keyFrames = D_801225D4; + csInfo->keyFrameCnt = 5; + + player->actor.shape.rot.y = player->actor.world.rot.y = player->currentYaw = 0x3FFC; + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + func_8002DF54(globalCtx, NULL, 8); + break; + case 4110: + csInfo->keyFrames = D_8012269C; + csInfo->keyFrameCnt = 3; + + func_8002DF38(globalCtx, &player->actor, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4120: + func_8002DF54(globalCtx, NULL, 8); + D_80122714[1].timerInit = 80; + csInfo->keyFrames = D_80122714; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4140: + csInfo->keyFrames = D_801227B4; + csInfo->keyFrameCnt = 6; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + break; + case 4150: + csInfo->keyFrames = D_801228A4; + csInfo->keyFrameCnt = 5; + + func_8002DF54(globalCtx, NULL, 8); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4160: + csInfo->keyFrames = D_8012296C; + csInfo->keyFrameCnt = 4; + + func_8002DF54(globalCtx, NULL, 8); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4170: + csInfo->keyFrames = D_80122A0C; + csInfo->keyFrameCnt = 2; + + func_8002DF54(globalCtx, NULL, 8); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4190: + csInfo->keyFrames = D_80122A5C; + csInfo->keyFrameCnt = 8; + + func_8002DF38(globalCtx, &player->actor, 8); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4200: + csInfo->keyFrames = D_80122B9C; + csInfo->keyFrameCnt = 3; + + func_8002DF38(globalCtx, &player->actor, 8); + Camera_ChangeMode(mainCam, CAM_MODE_NORMAL); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4210: + player->actor.freezeTimer = timer; + + csInfo->keyFrames = D_80122C14; + csInfo->keyFrameCnt = 1; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + + i = Quake_Add(csCam, 3); + Quake_SetSpeed(i, 12000); + Quake_SetQuakeValues(i, 0, 1, 100, 0); + Quake_SetCountdown(i, timer - 80); + break; + case 4220: + csInfo->keyFrames = (player->actor.world.pos.z < -15.0f) ? D_80122C3C : D_80122C64; + csInfo->keyFrameCnt = 1; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + func_8002DF38(globalCtx, &player->actor, 1); + + i = Quake_Add(csCam, 3); + Quake_SetSpeed(i, 12000); + Quake_SetQuakeValues(i, 0, 1, 10, 0); + Quake_SetCountdown(i, timer - 10); + break; + case 4221: + csInfo->keyFrames = D_80122C8C; + csInfo->keyFrameCnt = 1; + + func_8002DF54(globalCtx, NULL, 8); + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3260: + func_8002DF54(globalCtx, NULL, 8); + D_80122CB4[1].timerInit = timer - 5; + + csInfo->keyFrames = D_80122CB4; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 3261: + func_8002DF54(globalCtx, NULL, 8); + D_80122D04[1].timerInit = timer - 10; + + csInfo->keyFrames = D_80122D04; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 8010: + csInfo->keyFrames = D_80122D54; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 8002: + csInfo->keyFrames = D_80122DCC; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 8700: + Actor_GetFocus(&spA0, actor); + Actor_GetFocus(&sp8C, &player->actor); + D_80122E44[timer & 1][0].atTargetInit.y = ((spA0.pos.y - sp8C.pos.y) / 10.0f) + 90.0f; + D_80122E44[timer & 1][5].atTargetInit = mainCam->at; + + csInfo->keyFrames = D_80122E44[timer & 1]; + csInfo->keyFrameCnt = 7; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 1100: { + s32 tempDiff = globalCtx->state.frames - sPrevFrameCs1100; + + if ((tempDiff > 3600) || (tempDiff < -3600)) { + csInfo->keyFrames = D_80123074; + csInfo->keyFrameCnt = 5; + } else { + if (globalCtx->state.frames & 1) { + D_8012313C[0].rollTargetInit = -D_8012313C[0].rollTargetInit; + D_8012313C[0].atTargetInit.y = -D_8012313C[0].atTargetInit.y; + D_8012313C[0].eyeTargetInit.y = -D_8012313C[0].eyeTargetInit.y; + D_8012313C[1].atTargetInit.y = -D_8012313C[1].atTargetInit.y; + } + csInfo->keyFrames = D_8012313C; + csInfo->keyFrameCnt = 3; + } + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + sPrevFrameCs1100 = globalCtx->state.frames; + + } break; + case 9806: + csCam->timer = -99; + if (func_800C0CB8(globalCtx)) { + func_800C0808(globalCtx, camIdx, player, CAM_SET_TURN_AROUND); + csCam->data2 = 0xC; + } else { + Gameplay_CopyCamera(globalCtx, camIdx, MAIN_CAM); + Gameplay_CameraChangeSetting(globalCtx, camIdx, CAM_SET_FREE2); + } + break; + case 9908: + if (func_800C0CB8(globalCtx)) { + D_801231B4[0].eyeTargetInit.z = D_801231B4[1].eyeTargetInit.z = !LINK_IS_ADULT ? 100.0f : 120.0f; + + if (player->stateFlags1 & 0x08000000) { + D_801231B4[2].atTargetInit.z = 0.0f; + } + Actor_GetWorldPosShapeRot(&spA0, &player->actor); + OLib_Vec3fDiffToVecSphGeo(&spD0, &spA0.pos, &mainCam->at); + spD0.yaw -= spA0.rot.y; + OLib_VecSphGeoToVec3f(&D_801231B4[3].atTargetInit, &spD0); + OLib_Vec3fDiffToVecSphGeo(&spD0, &spA0.pos, &mainCam->eye); + spD0.yaw -= spA0.rot.y; + OLib_VecSphGeoToVec3f(&D_801231B4[3].eyeTargetInit, &spD0); + D_801231B4[3].fovTargetInit = mainCam->fov; + D_801231B4[3].timerInit = timer - 50; + + csInfo->keyFrames = D_801231B4; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + } else { + D_80123254[1].timerInit = timer - 1; + D_80123254[0].fovTargetInit = mainCam->fov; + D_80123254[0].atTargetInit = D_80123254[1].atTargetInit = mainCam->at; + D_80123254[0].eyeTargetInit = D_80123254[1].eyeTargetInit = mainCam->eye; + + csInfo->keyFrames = D_80123254; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + } + break; + case 1000: + D_801232A4[0].atTargetInit = globalCtx->view.lookAt; + D_801232A4[0].eyeTargetInit = globalCtx->view.eye; + D_801232A4[0].fovTargetInit = globalCtx->view.fovy; + + csInfo->keyFrames = D_801232A4; + csInfo->keyFrameCnt = 1; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 8603: + csInfo->keyFrames = D_801232CC; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 8604: + csInfo->keyFrames = D_80123394; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4000: + csInfo->keyFrames = D_8012345C; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4010: + csInfo->keyFrames = D_801234FC; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4011: + csInfo->keyFrames = D_801235C4; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4020: + csInfo->keyFrames = D_8012368C; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4021: + csInfo->keyFrames = D_8012372C; + csInfo->keyFrameCnt = 4; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 4022: + csCam->timer = D_801237CC[0].timerInit + D_801237CC[3].timerInit + D_801237CC[1].timerInit + + D_801237CC[2].timerInit + D_801237CC[4].timerInit; + + csInfo->keyFrames = D_801237CC; + csInfo->keyFrameCnt = 5; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 9703: + D_80123894[0].atTargetInit = globalCtx->view.lookAt; + D_80123894[0].eyeTargetInit = globalCtx->view.eye; + D_80123894[0].fovTargetInit = globalCtx->view.fovy; + if (LINK_IS_ADULT) { + D_80123894[1].atTargetInit.y = 60.0f; + D_80123894[1].eyeTargetInit.y = 52.0f; + } + + csInfo->keyFrames = D_80123894; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 9704: + D_8012390C[0].atTargetInit = globalCtx->view.lookAt; + D_8012390C[0].eyeTargetInit = globalCtx->view.eye; + D_8012390C[0].fovTargetInit = globalCtx->view.fovy; + + csInfo->keyFrames = D_8012390C; + csInfo->keyFrameCnt = 2; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 9705: + D_8012395C[0].atTargetInit = globalCtx->view.lookAt; + D_8012395C[0].eyeTargetInit = globalCtx->view.eye; + D_8012395C[0].fovTargetInit = globalCtx->view.fovy; + + csInfo->keyFrames = D_8012395C; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, player, CAM_SET_CS_C); + break; + case 5110: + D_801239D4[1].timerInit = 10; + + csInfo->keyFrames = D_801239D4; + csInfo->keyFrameCnt = 3; + + func_800C0808(globalCtx, camIdx, (Player*)actor, CAM_SET_CS_C); + break; + default: + osSyncPrintf(VT_COL(RED, WHITE) "onepointdemo camera: demo number not found !! (%d)\n" VT_RST, csId); + break; + } + return 0; +} + +s16 OnePointCutscene_SetAsChild(GlobalContext* globalCtx, s16 newCamIdx, s16 parentCamIdx) { + s16 prevCamIdx = globalCtx->cameraPtrs[parentCamIdx]->childCamIdx; + + globalCtx->cameraPtrs[newCamIdx]->parentCamIdx = parentCamIdx; + globalCtx->cameraPtrs[parentCamIdx]->childCamIdx = newCamIdx; + + return prevCamIdx; +} + +/** + * Removes a cutscene camera from the list. Returns the parent cam if the removed camera is active, otherwise returns + * SUBCAM_NONE + */ +s32 OnePointCutscene_RemoveCamera(GlobalContext* globalCtx, s16 camIdx) { + Camera* camera = globalCtx->cameraPtrs[camIdx]; + s32 nextCamIdx; + + if (camera->thisIdx == CHILD_CAM(camera)->parentCamIdx) { + CHILD_CAM(camera)->parentCamIdx = camera->parentCamIdx; + } + if (camera->thisIdx == PARENT_CAM(camera)->childCamIdx) { + PARENT_CAM(camera)->childCamIdx = camera->childCamIdx; + } + nextCamIdx = (globalCtx->activeCamera == camIdx) ? camera->parentCamIdx : SUBCAM_NONE; + camera->parentCamIdx = MAIN_CAM; + camera->childCamIdx = camera->parentCamIdx; + camera->timer = -1; + Gameplay_ClearCamera(camera->globalCtx, camera->thisIdx); + return nextCamIdx; +} + +#define vChildCamIdx temp2 +#define vCsStatus temp1 +#define vCurCamIdx temp2 +#define vNextCamIdx temp1 + +/** + * Creates a cutscene subcamera with the specified ID, duration, and targeted actor. The camera is placed into the + * cutscene queue in front of the specified camera, then all lower priority demos in front of it are removed from the + * queue. + */ +s16 OnePointCutscene_Init(GlobalContext* globalCtx, s16 csId, s16 timer, Actor* actor, s16 parentCamIdx) { + s16 temp1; + s16 temp2; + s16 csCamIdx; + Camera* csCam; + + if (parentCamIdx == SUBCAM_ACTIVE) { + parentCamIdx = globalCtx->activeCamera; + } + csCamIdx = Gameplay_CreateSubCamera(globalCtx); + if (csCamIdx == SUBCAM_NONE) { + osSyncPrintf(VT_COL(RED, WHITE) "onepoint demo: error: too many cameras ... give up! type=%d\n" VT_RST, csId); + return SUBCAM_NONE; + } + + // Inserts the cutscene camera into the cutscene queue in front of parentCam + + vChildCamIdx = globalCtx->cameraPtrs[parentCamIdx]->childCamIdx; + vCsStatus = CAM_STAT_ACTIVE; + if (vChildCamIdx >= SUBCAM_FIRST) { + OnePointCutscene_SetAsChild(globalCtx, vChildCamIdx, csCamIdx); + vCsStatus = CAM_STAT_WAIT; + } else { + Interface_ChangeAlpha(2); + } + OnePointCutscene_SetAsChild(globalCtx, csCamIdx, parentCamIdx); + + csCam = globalCtx->cameraPtrs[csCamIdx]; + + csCam->timer = timer; + csCam->target = actor; + + csCam->at = globalCtx->view.lookAt; + csCam->eye = globalCtx->view.eye; + csCam->fov = globalCtx->view.fovy; + + csCam->csId = csId; + + if (parentCamIdx == MAIN_CAM) { + Gameplay_ChangeCameraStatus(globalCtx, parentCamIdx, CAM_STAT_UNK3); + } else { + Gameplay_ChangeCameraStatus(globalCtx, parentCamIdx, CAM_STAT_WAIT); + } + OnePointCutscene_SetInfo(globalCtx, csCamIdx, csId, actor, timer); + Gameplay_ChangeCameraStatus(globalCtx, csCamIdx, vCsStatus); + + // Removes all lower priority cutscenes in front of this cutscene from the queue. + vCurCamIdx = csCamIdx; + vNextCamIdx = globalCtx->cameraPtrs[csCamIdx]->childCamIdx; + + while (vNextCamIdx >= SUBCAM_FIRST) { + s16 nextCsId = globalCtx->cameraPtrs[vNextCamIdx]->csId; + s16 thisCsId = globalCtx->cameraPtrs[csCamIdx]->csId; + + if ((nextCsId / 100) < (thisCsId / 100)) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "onepointdemo camera[%d]: killed 'coz low priority (%d < %d)\n" VT_RST, + vNextCamIdx, nextCsId, thisCsId); + if (globalCtx->cameraPtrs[vNextCamIdx]->csId != 5010) { + if ((vNextCamIdx = OnePointCutscene_RemoveCamera(globalCtx, vNextCamIdx)) != SUBCAM_NONE) { + Gameplay_ChangeCameraStatus(globalCtx, vNextCamIdx, CAM_STAT_ACTIVE); + } + } else { + vCurCamIdx = vNextCamIdx; + OnePointCutscene_EndCutscene(globalCtx, vNextCamIdx); + } + } else { + vCurCamIdx = vNextCamIdx; + } + vNextCamIdx = globalCtx->cameraPtrs[vCurCamIdx]->childCamIdx; + } + return csCamIdx; +} + +/** + * Ends the cutscene in camIdx by setting its timer to 0. For attention cutscenes, it is set to 5 instead. + */ +s16 OnePointCutscene_EndCutscene(GlobalContext* globalCtx, s16 camIdx) { + if (camIdx == SUBCAM_ACTIVE) { + camIdx = globalCtx->activeCamera; + } + if (globalCtx->cameraPtrs[camIdx] != NULL) { + osSyncPrintf("onepointdemo camera[%d]: delete timer=%d next=%d\n", camIdx, globalCtx->cameraPtrs[camIdx]->timer, + globalCtx->cameraPtrs[camIdx]->parentCamIdx); + if (globalCtx->cameraPtrs[camIdx]->csId == 5010) { + globalCtx->cameraPtrs[camIdx]->timer = 5; + } else { + globalCtx->cameraPtrs[camIdx]->timer = 0; + } + } + return camIdx; +} + +#define vTargetCat temp1 +#define vParentCamIdx temp1 +#define vLastHigherCat temp2 +#define vCsCamIdx temp2 + +/** + * Adds an attention cutscene to the cutscene queue. + */ +s32 OnePointCutscene_Attention(GlobalContext* globalCtx, Actor* actor) { + Camera* parentCam; + s32 temp1; + s32 temp2; + s32 timer; + + if (sDisableAttention) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "actor attention demo camera: canceled by other camera\n" VT_RST); + return SUBCAM_NONE; + } + sUnused = -1; + + parentCam = globalCtx->cameraPtrs[MAIN_CAM]; + if (parentCam->mode == CAM_MODE_FOLLOWBOOMERANG) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "actor attention demo camera: change mode BOOKEEPON -> NORMAL\n" VT_RST); + Camera_ChangeMode(parentCam, CAM_MODE_NORMAL); + } + + // Finds the camera of the first actor attention demo with a lower category actor, or the first non-attention demo + // after at least one attention demo. + + vLastHigherCat = -1; + while (parentCam->childCamIdx != SUBCAM_FREE) { + parentCam = globalCtx->cameraPtrs[parentCam->childCamIdx]; + if (parentCam == NULL) { + break; + } else if (parentCam->setting != CAM_SET_CS_ATTENTION) { + if (vLastHigherCat == -1) { + continue; + } else { + break; + } + } else { + vTargetCat = parentCam->target->category; + if (actor->category > vTargetCat) { + break; + } + vLastHigherCat = vTargetCat; + } + } + // Actorcat is only undefined if the actor is in a higher category than all other attention cutscenes. In this case, + // it goes in the first position of the list. Otherwise, it goes in the index found in the loop. + vParentCamIdx = (vLastHigherCat == -1) ? MAIN_CAM : parentCam->thisIdx; + + switch (actor->category) { + case ACTORCAT_SWITCH: + case ACTORCAT_BG: + case ACTORCAT_PLAYER: + case ACTORCAT_PROP: + case ACTORCAT_DOOR: + timer = 30; + break; + case ACTORCAT_NPC: + case ACTORCAT_ITEMACTION: + case ACTORCAT_CHEST: + timer = 100; + break; + case ACTORCAT_EXPLOSIVE: + case ACTORCAT_ENEMY: + case ACTORCAT_MISC: + case ACTORCAT_BOSS: + default: + osSyncPrintf(VT_COL(YELLOW, BLACK) "actor attention demo camera: %d: unkown part of actor %d\n" VT_RST, + globalCtx->state.frames, actor->category); + timer = 30; + break; + } + osSyncPrintf(VT_FGCOL(CYAN) "%06u:" VT_RST " actor attention demo camera: request %d ", globalCtx->state.frames, + actor->category); + + // If the previous attention cutscene has an actor in the same category, skip this actor. + if (actor->category == vLastHigherCat) { + osSyncPrintf("→ " VT_FGCOL(PURPLE) "×" VT_RST " (%d)\n", actor->id); + return SUBCAM_NONE; + } + osSyncPrintf("→ " VT_FGCOL(BLUE) "○" VT_RST " (%d)\n", actor->id); + vCsCamIdx = OnePointCutscene_Init(globalCtx, 5010, timer, actor, vParentCamIdx); + if (vCsCamIdx == SUBCAM_NONE) { + osSyncPrintf(VT_COL(RED, WHITE) "actor attention demo: give up! \n" VT_RST, actor->id); + return SUBCAM_NONE; + } else { + s32* data = (s32*)&globalCtx->cameraPtrs[vCsCamIdx]->data1; + + *data = NA_SE_SY_CORRECT_CHIME; + return vCsCamIdx; + } +} + +/** + * Adds an attention cutscene to the cutscene queue with the specified sound effect + */ +s32 OnePointCutscene_AttentionSetSfx(GlobalContext* globalCtx, Actor* actor, s32 sfxId) { + s32 csCamIdx = OnePointCutscene_Attention(globalCtx, actor); + + if (csCamIdx != SUBCAM_NONE) { + s32* data = (s32*)&globalCtx->cameraPtrs[csCamIdx]->data1; + + *data = sfxId; + } + return csCamIdx; +} + +// unused +void OnePointCutscene_EnableAttention() { + sDisableAttention = false; +} + +// unused +void OnePointCutscene_DisableAttention() { + sDisableAttention = true; +} + +s32 OnePointCutscene_CheckForCategory(GlobalContext* globalCtx, s32 category) { + Camera* parentCam = globalCtx->cameraPtrs[MAIN_CAM]; + + while (parentCam->childCamIdx != SUBCAM_FREE) { + parentCam = globalCtx->cameraPtrs[parentCam->childCamIdx]; + if ((parentCam == NULL) || (parentCam->setting != CAM_SET_CS_ATTENTION)) { + break; + } else if (category == parentCam->target->category) { + return true; + } + } + return false; +} + +// unused, also empty. +void OnePointCutscene_Noop(GlobalContext* globalCtx, s32 arg1) { +} diff --git a/soh/src/code/z_onepointdemo_data.c b/soh/src/code/z_onepointdemo_data.c new file mode 100644 index 000000000..1d1838b05 --- /dev/null +++ b/soh/src/code/z_onepointdemo_data.c @@ -0,0 +1,670 @@ +#include "global.h" + +static CutsceneCameraPoint D_8012013C[14] = { + { CS_CMD_CONTINUE, 25, 40, 70.79991f, { -1814, 533, -1297 } }, + { CS_CMD_CONTINUE, 20, 40, 70.99991f, { -1805, 434, -1293 } }, + { CS_CMD_CONTINUE, 10, 30, 60.0f, { -1794, 323, -1280 } }, + { CS_CMD_CONTINUE, 5, 25, 60.0f, { -1817, 218, -1270 } }, + { CS_CMD_CONTINUE, 3, 20, 60.0f, { -1836, 168, -1243 } }, + { CS_CMD_CONTINUE, 0, 20, 60.0f, { -1905, 115, -1193 } }, + { CS_CMD_CONTINUE, 0, 30, 55.0f, { -1969, 58, -1212 } }, + { CS_CMD_CONTINUE, 0, 30, 55.0f, { -1969, 31, -1164 } }, + { CS_CMD_CONTINUE, 0, 30, 60.0f, { -1969, 54, -1209 } }, + { CS_CMD_CONTINUE, 0, 30, 60.0f, { -1973, 35, -1206 } }, + { CS_CMD_CONTINUE, 0, 50, 60.0f, { -1974, 12, -1179 } }, + { CS_CMD_CONTINUE, 0, 50, 60.0f, { -1974, 12, -1179 } }, + { CS_CMD_STOP, 0, 50, 60.0f, { -1974, 12, -1179 } }, + { CS_CMD_STOP, 0, 30, 60.0f, { -1974, 12, -1179 } }, +}; +static CutsceneCameraPoint D_8012021C[14] = { + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1751, 604, -1233 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1752, 516, -1233 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1751, 417, -1233 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1767, 306, -1219 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1776, 257, -1205 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1881, 147, -1149 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1969, 72, -1077 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1969, 7, -1048 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1969, 1, -1030 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { -1987, 17, -1076 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -2007, 10, -1004 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { -2007, 10, -1004 } }, + { CS_CMD_STOP, 0, 0, 60.0f, { -2007, 10, -1004 } }, { CS_CMD_STOP, 0, 0, 60.0f, { -2007, 10, -1004 } }, +}; +static s16 D_801202FC = 13; +static s16 D_80120300 = 210; +static s16 D_80120304 = 0; + +static CutsceneCameraPoint D_80120308[9] = { + { CS_CMD_CONTINUE, 0, 10, 40.0f, { 0, 4, 0 } }, { CS_CMD_CONTINUE, 0, 10, 40.000004f, { 0, 4, 0 } }, + { CS_CMD_CONTINUE, 0, 10, 50.0f, { 0, 9, 0 } }, { CS_CMD_CONTINUE, 0, 12, 55.0f, { 0, 12, 0 } }, + { CS_CMD_CONTINUE, 0, 15, 61.0f, { 0, 18, 0 } }, { CS_CMD_CONTINUE, 0, 20, 65.0f, { 0, 29, 0 } }, + { CS_CMD_CONTINUE, 0, 40, 60.0f, { 0, 34, 0 } }, { CS_CMD_STOP, 0, 40, 60.0f, { 0, 34, 0 } }, + { CS_CMD_STOP, 0, 10, 60.0f, { 0, 34, 0 } }, +}; +static CutsceneCameraPoint D_80120398[9] = { + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 9, 45 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 8, 50 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 17, 58 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 21, 78 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 46, 109 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 58, 118 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 63, 119 } }, { CS_CMD_STOP, 0, 0, 60.0f, { 0, 62, 119 } }, + { CS_CMD_STOP, 0, 0, 60.0f, { 0, 62, 119 } }, +}; +static s16 D_80120428 = 9; +static s16 D_8012042C = 90; +static s16 D_80120430 = 1; +static CutsceneCameraPoint D_80120434[10] = { + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 9, -45 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 9, -45 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 8, -50 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 17, -58 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 21, -78 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 46, -109 } }, + { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 58, -118 } }, { CS_CMD_CONTINUE, 0, 0, 60.0f, { 0, 63, -119 } }, + { CS_CMD_STOP, 0, 0, 60.0f, { 0, 62, -119 } }, { CS_CMD_STOP, 0, 0, 60.0f, { 0, 62, -119 } }, +}; + +static CutsceneCameraPoint D_801204D4[14] = { + { CS_CMD_CONTINUE, -15, 40, 80.600006f, { -60, 332, 183 } }, + { CS_CMD_CONTINUE, -22, 30, 80.600006f, { -60, 332, 183 } }, + { CS_CMD_CONTINUE, -20, 38, 80.600006f, { -118, 344, 41 } }, + { CS_CMD_CONTINUE, -18, 32, 80.600006f, { -80, 251, -8 } }, + { CS_CMD_CONTINUE, -12, 28, 80.600006f, { -64, 259, -28 } }, + { CS_CMD_CONTINUE, -8, 22, 80.600006f, { -79, 200, -342 } }, + { CS_CMD_CONTINUE, -5, 10, 65.80005f, { -110, 140, -549 } }, + { CS_CMD_CONTINUE, -2, 8, 65.2f, { -74, 109, -507 } }, + { CS_CMD_CONTINUE, 0, 10, 65.80002f, { -32, 78, -680 } }, + { CS_CMD_CONTINUE, 0, 20, 85.199936f, { 25, 127, -950 } }, + { CS_CMD_CONTINUE, 0, 30, 85.199936f, { 25, 127, -950 } }, + { CS_CMD_CONTINUE, 0, 40, 85.199936f, { 25, 127, -950 } }, + { CS_CMD_STOP, 6, 30, 85.199936f, { 25, 127, -950 } }, + { CS_CMD_STOP, 0, 30, 85.199936f, { 25, 127, -950 } }, +}; +static CutsceneCameraPoint D_801205B4[14] = { + { CS_CMD_CONTINUE, 0, 0, 60.0f, { -225, 785, -242 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -245, 784, -242 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -288, 485, -379 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -250, 244, -442 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -163, 21, -415 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -98, 86, -520 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -86, 31, -816 } }, + { CS_CMD_CONTINUE, -21, 0, 80.600006f, { -74, 18, -931 } }, + { CS_CMD_CONTINUE, 1, 0, 80.600006f, { -91, 80, -1220 } }, + { CS_CMD_CONTINUE, 0, 0, 85.199936f, { 14, 153, -1340 } }, + { CS_CMD_CONTINUE, 0, 0, 85.199936f, { 28, 125, -1340 } }, + { CS_CMD_CONTINUE, 0, 0, 85.199936f, { 48, 124, -1340 } }, + { CS_CMD_STOP, 0, 0, 85.199936f, { 48, 124, -1502 } }, + { CS_CMD_STOP, 0, 0, 85.199936f, { 48, 124, -1262 } }, +}; +static s16 D_80120694 = 14; +static s16 D_80120698 = 190; +static s16 D_8012069C = 8; + +static CutsceneCameraPoint D_801206A0[12] = { + { CS_CMD_CONTINUE, 6, 20, 80.0f, { -96, 40, 170 } }, { CS_CMD_CONTINUE, 6, 20, 80.0f, { -96, 40, 170 } }, + { CS_CMD_CONTINUE, 6, 20, 70.0f, { -70, 35, 150 } }, { CS_CMD_CONTINUE, 5, 10, 60.0f, { -57, 34, 133 } }, + { CS_CMD_CONTINUE, 4, 25, 65.0f, { -22, 32, 110 } }, { CS_CMD_CONTINUE, 3, 12, 60.0f, { -9, 33, 98 } }, + { CS_CMD_CONTINUE, 3, 5, 65.0f, { -3, 29, 87 } }, { CS_CMD_CONTINUE, 2, 10, 65.0f, { -1, 15, 84 } }, + { CS_CMD_CONTINUE, 1, 200, 65.0f, { 0, 17, 82 } }, { CS_CMD_CONTINUE, 1, 500, 65.0f, { 0, 18, 82 } }, + { CS_CMD_STOP, 8, 50, 65.0f, { 0, 18, 82 } }, { CS_CMD_STOP, 11, 60, 65.0f, { 0, 18, 82 } }, +}; +static CutsceneCameraPoint D_80120760[12] = { + { CS_CMD_CONTINUE, 6, 0, 80.0f, { -50, 10, 180 } }, { CS_CMD_CONTINUE, 6, 0, 80.0f, { -50, 20, 180 } }, + { CS_CMD_CONTINUE, 6, 0, 70.0f, { -40, 30, 177 } }, { CS_CMD_CONTINUE, 5, 0, 65.0f, { 0, 35, 172 } }, + { CS_CMD_CONTINUE, 4, 0, 65.0f, { 34, 35, 162 } }, { CS_CMD_CONTINUE, 3, 0, 65.0f, { 61, 32, 147 } }, + { CS_CMD_CONTINUE, 3, 0, 65.0f, { 72, 30, 128 } }, { CS_CMD_CONTINUE, 2, 0, 65.0f, { 74, 20, 125 } }, + { CS_CMD_CONTINUE, 1, 0, 65.0f, { 75, 18, 123 } }, { CS_CMD_CONTINUE, 1, 0, 65.0f, { 75, 10, 123 } }, + { CS_CMD_STOP, 0, 0, 65.0f, { 75, 10, 122 } }, { CS_CMD_STOP, 0, 0, 65.0f, { 75, 10, 122 } }, +}; +static CutsceneCameraPoint D_80120820[12] = { + { CS_CMD_CONTINUE, 6, 0, 80.0f, { 85, 5, 170 } }, { CS_CMD_CONTINUE, 6, 0, 80.0f, { 85, 10, 170 } }, + { CS_CMD_CONTINUE, 6, 0, 70.0f, { 80, 20, 167 } }, { CS_CMD_CONTINUE, 5, 0, 65.0f, { 74, 25, 165 } }, + { CS_CMD_CONTINUE, 4, 0, 65.0f, { 63, 30, 162 } }, { CS_CMD_CONTINUE, 3, 0, 65.0f, { 66, 34, 147 } }, + { CS_CMD_CONTINUE, 3, 0, 65.0f, { 72, 34, 128 } }, { CS_CMD_CONTINUE, 2, 0, 65.0f, { 74, 20, 125 } }, + { CS_CMD_CONTINUE, 1, 0, 65.0f, { 75, 18, 123 } }, { CS_CMD_CONTINUE, 1, 0, 65.0f, { 75, 10, 123 } }, + { CS_CMD_STOP, 0, 0, 65.0f, { 75, 10, 122 } }, { CS_CMD_STOP, 0, 0, 65.0f, { 75, 10, 122 } }, +}; +static s16 D_801208E0 = 12; +static s16 D_801208E4 = 90; +static s16 D_801208E8 = 8; + +static OnePointCsFull D_801208EC[3] = { + { 0x0F, 0x08, 0x0101, 1, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x81, 0xFF, 0x0101, 1, 0, 60.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 0.0f, 150.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120964[2] = { + { 0x8F, 0xFF, 0x0101, 1, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x81, 0xFF, 0xA121, 1, 0, 75.0f, 0.6f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 0.0f, 150.0f } }, +}; + +static OnePointCsFull D_801209B4[4] = { + { 0x8F, 0x08, 0x0101, 1, 0, 60.0f, 0.9f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x84, 0x01, 0x0100, 29, 0, 45.0f, 0.1f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 0.0f, 150.0f } }, + { 0x83, 0xFF, 0x0000, 10, 0, 60.0f, 0.2f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 0.0f, 150.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120A54[3] = { + { 0x8F, 0xFF, 0x2525, 1, 0, 75.0f, 0.1f, { 0.0f, 20.0f, -10.0f }, { 0.0f, 10.0f, -40.0f } }, + { 0x8F, 0xFF, 0x0000, 9, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x8B, 0xFF, 0x0022, 5000, 0, 75.0f, 0.005f, { 0.0f, 0.0f, -10.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120ACC[5] = { + { 0x8F, 0xFF, 0x0442, 10, 0, 40.0f, 1.0f, { -10.0f, 45.0f, 20.0f }, { 20.0f, 30.0f, 160.0f } }, + { 0x95, 0xFF, 0x0000, 1, 0, 40.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x8F, 0x01, 0x0442, 10, 0, 40.0f, 1.0f, { -10.0f, 45.0f, 20.0f }, { 20.0f, 30.0f, 160.0f } }, + { 0x95, 0xFF, 0x0000, 1, 0, 40.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120B94[11] = { + { 0x8F, 0x01, 0x2142, 1, 0, 40.0f, 1.0f, { 20.0f, 40.0f, 20.0f }, { -20.0f, 0.0f, -30.0f } }, + { 0x84, 0xFF, 0x0404, 19, 5, 70.0f, 0.01f, { 0.0f, 30.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x84, 0xFF, 0x0404, 20, 0, 60.0f, 0.01f, { 0.0f, 20.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x84, 0xFF, 0x0404, 40, -10, 50.0f, 0.02f, { 0.0f, 30.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x8F, 0xFF, 0x4141, 1, 0, 40.0f, 1.0f, { 0.0f, -10.0f, 20.0f }, { 0.0f, 20.0f, 50.0f } }, + { 0x84, 0xFF, 0x0404, 19, 0, 70.0f, 0.01f, { 0.0f, 30.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x84, 0xFF, 0x0404, 40, 10, 50.0f, 0.01f, { 0.0f, 20.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x84, 0xFF, 0x0404, 70, 0, 60.0f, 0.01f, { 0.0f, 30.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x8F, 0xFF, 0x4141, 1, 0, 50.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 80.0f, 20.0f, 60.0f } }, + { 0x8D, 0xFF, 0x4141, 150, 0, 50.0f, 1.0f, { 0.0f, 5.0f, 0.0f }, { 0.0f, 4.0f, 120.0f } }, + { 0x98, 0xFF, 0x0000, 1, 0, 50.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120D4C[7] = { + { 0x8F, 0x01, 0x2142, 1, 0, 40.0f, 1.0f, { 20.0f, 40.0f, 20.0f }, { -20.0f, 0.0f, -30.0f } }, + { 0x84, 0xFF, 0x0404, 19, 5, 70.0f, 0.01f, { 0.0f, 30.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x84, 0xFF, 0x0404, 20, 0, 60.0f, 0.01f, { 0.0f, 20.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x84, 0xFF, 0x0404, 40, -10, 50.0f, 0.02f, { 0.0f, 30.0f, 20.0f }, { 120.0f, 60.0f, 120.0f } }, + { 0x8F, 0xFF, 0x4141, 1, 0, 50.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 80.0f, 20.0f, 60.0f } }, + { 0x8D, 0xFF, 0x4141, 150, 0, 50.0f, 1.0f, { 0.0f, 5.0f, 0.0f }, { 0.0f, 4.0f, 120.0f } }, + { 0x98, 0xFF, 0x0000, 1, 0, 50.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120E64[8] = { + { 0x41, 0x01, 0x2142, 20, 0, 50.0f, 1.0f, { -25.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x4F, 0xFF, 0x0000, 80, 0, 50.0f, 1.0f, { -25.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x4B, 0xFF, 0x4242, 8, 0, 60.0f, 0.1f, { -25.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x4B, 0xFF, 0x4242, 15, 4, 55.0f, 0.05f, { -50.0f, 20.0f, 20.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x4B, 0xFF, 0x4242, 15, -4, 50.0f, 0.05f, { 0.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x4B, 0xFF, 0x4242, 15, 0, 50.0f, 0.1f, { -25.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x4F, 0xFF, 0x0000, 40, 0, 50.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80120FA4[6] = { + { 0x8F, 0x01, 0x2143, 30, 0, 70.0f, 0.4f, { 0.0f, 40.0f, 50.0f }, { 30.0f, 10.0f, -50.0f } }, + { 0x95, 0xFF, 0x0000, 1, 0, 50.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x8F, 0xFF, 0x2222, 10, 0, 42.0f, 1.0f, { 0.0f, 40.0f, 0.0f }, { 0.0f, 85.0f, 45.0f } }, + { 0x90, 0xFF, 0x0000, 1, 0, 50.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x81, 0xFF, 0x2121, 10, 0, 60.0f, 1.0f, { 0.0f, 10.0f, 0.0f }, { 30.0f, 10.0f, -80.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121094[3] = { + { 0x0F, 0x08, 0x2101, 20, 0, 50.0f, 1.0f, { 3840.0f, 10.0f, 950.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x01, 0x01, 0x2101, 50, 0, 55.0f, 1.0f, { 4000.0f, 50.0f, 1000.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012110C[3] = { + { 0x4F, 0x05, 0x2142, 1, 0, 50.0f, 1.0f, { -25.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 5.0f } }, + { 0x41, 0xFF, 0x2121, 10, 0, 60.0f, 1.0f, { 0.0f, 10.0f, 0.0f }, { 0.0f, 10.0f, -80.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121184[2] = { + { 0x83, 0x01, 0x0101, 40, 0, -1.0f, 0.1f, { 0.0f, 10.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801211D4[2] = { + { 0x8F, 0x08, 0x0101, 50, 0, 60.0f, 1.0f, { 0.0f, 10.0f, 0.0f }, { -10.0f, 85.0f, 0.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121224[6] = { + { 0x8F, 0x01, 0x4141, 2, 0, 60.0f, 1.0f, { 0.0f, 5.0f, 10.0f }, { 0.0f, 0.0f, 45.0f } }, + { 0x81, 0xFF, 0x4141, 18, 0, 45.0f, 1.0f, { 0.0f, 5.0f, 10.0f }, { 0.0f, -10.0f, 50.0f } }, + { 0x84, 0x34, 0x4104, 80, 0, 70.0f, 0.05f, { 0.0f, 0.0f, 60.0f }, { 0.0f, 250.0f, -50.0f } }, + { 0x8F, 0x01, 0x0000, 20, 0, 70.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x8F, 0xFF, 0x0421, 60, 0, 50.0f, 1.0f, { 0.0f, -30.0f, 20.0f }, { 10.0f, 5.0f, -50.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121314[1] = { + { 0x8F, 0x08, 0x4141, 1000, 0, 75.0f, 0.6f, { 0.0f, 0.0f, 10.0f }, { 0.0f, 0.0f, 100.0f } }, +}; + +static OnePointCsFull D_8012133C[3] = { + { 0x8F, 0x01, 0x0141, 40, 0, 75.0f, 1.0f, { 0.0f, 60.0f, 0.0f }, { 0.0f, 0.0f, 100.0f } }, + { 0x83, 0xFF, 0x2121, 20, 0, 60.0f, 0.2f, { 0.0f, -10.0f, -10.0f }, { 0.0f, 10.0f, -100.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801213B4[5] = { + { 0x8F, 0x08, 0xC2C2, 40, 0, 70.0f, 1.0f, { 80.0f, 0.0f, 20.0f }, { 20.0f, 0.0f, 80.0f } }, + { 0x8B, 0x01, 0xC2C2, 120, 0, 70.0f, 0.1f, { 80.0f, 0.0f, 20.0f }, { 20.0f, 0.0f, 80.0f } }, + { 0x8F, 0x53, 0xC2C2, 30, 0, 50.0f, 1.0f, { 60.0f, 0.0f, 20.0f }, { 60.0f, 0.0f, 60.0f } }, + { 0x84, 0x45, 0x4222, 30, 0, 60.0f, 0.1f, { 0.0f, 50.0f, 0.0f }, { 5.0f, 30.0f, 220.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 75.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012147C[4] = { + { 0x8F, 0x01, 0x0101, 40, 0, 45.0f, 1.0f, { 820.0f, 1600.0f, -400.0f }, { 777.0f, 1577.0f, -577.0f } }, + { 0x8F, 0x01, 0x0142, 1, 0, 50.0f, 1.0f, { -50.0f, 80.0f, 0.0f }, { 900.0f, 1575.0f, 850.0f } }, + { 0x83, 0x08, 0x0142, 89, -4, 80.0f, 0.07f, { -50.0f, 70.0f, 0.0f }, { 975.0f, 1575.0f, 770.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012151C[2] = { + { 0x0F, 0x01, 0x0101, 29, 0, 60.0f, 1.0f, { -700.0f, 875.0f, -100.0f }, { -550.0f, 920.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012156C[2] = { + { 0x8F, 0x4D, 0x4242, 1, 0, 65.0f, 1.0f, { 60.0f, 30.0f, 0.0f }, { 50.0f, 20.0f, 150.0f } }, + { 0x81, 0xFF, 0x4242, -1, 0, 65.0f, 1.0f, { -50.0f, 60.0f, 0.0f }, { -60.0f, 40.0f, 150.0f } }, +}; + +static OnePointCsFull D_801215BC[1] = { + { 0x0F, 0xFF, 0x0101, 5, 0, 65.0f, 1.0f, { -1185.0f, 655.0f, 1185.0f }, { -1255.0f, 735.0f, 1255.0f } }, +}; + +static OnePointCsFull D_801215E4[10] = { + { 0x0F, 0x08, 0x4141, 20, 0, 30.0f, 1.0f, { 0.0f, 120.0f, 0.0f }, { -10.0f, 140.0f, -90.0f } }, + { 0x0F, 0x01, 0x0101, 1, 4, 75.0f, 1.0f, { -1360.0f, -940.0f, -3343.0f }, { -1060.0f, -980.0f, -3325.0f } }, + { 0x0B, 0xFF, 0x4141, 129, 0, 75.0f, 0.5f, { 0.0f, 50.0f, 0.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x03, 0xFF, 0x0303, 30, 0, 70.0f, 0.05f, { 0.0f, 80.0f, 0.0f }, { -10.0f, 120.0f, 10.0f } }, + { 0x0F, 0x09, 0x0101, 40, -5, 70.0f, 1.0f, { -973.0f, -924.0f, -3263.0f }, { -1190.0f, -1010.0f, -3365.0f } }, + { 0x0F, 0x01, 0x0101, 1, 0, 75.0f, 1.0f, { -1355.0f, -700.0f, -3340.0f }, { -1040.0f, -940.0f, -3345.0f } }, + { 0x02, 0xFF, 0x0101, 60, 0, 45.0f, 0.8f, { -1370.0f, -875.0f, -3345.0f }, { -1230.0f, -885.0f, -3345.0f } }, + { 0x01, 0xFF, 0x0101, 10, 0, 70.0f, 1.0f, { -1370.0f, -875.0f, -3345.0f }, { -1210.0f, -900.0f, -3420.0f } }, + { 0x0F, 0xFF, 0x0000, 20, 0, 70.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121774[4] = { + { 0x0F, 0x08, 0x0101, 1, -2, 75.0f, 1.0f, { -1340.0f, -860.0f, -3345.0f }, { -1415.0f, -940.0f, -3520.0f } }, + { 0x01, 0x01, 0x0142, 39, 2, 70.0f, 1.0f, { 0.0f, -20.0f, 10.0f }, { -1140.0f, -1010.0f, -3560.0f } }, + { 0x01, 0x05, 0x0121, 20, 0, 60.0f, 1.0f, { 0.0f, -20.0f, 20.0f }, { -1220.0f, -1005.0f, -3660.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121814[4] = { + { 0x0F, 0x4C, 0x0101, 5, 0, 40.0f, 1.0f, { -1400.0f, -540.0f, -3327.0f }, { -1254.0f, -20.0f, -3357.0f } }, + { 0x01, 0xFF, 0x0101, 70, 0, 75.0f, 0.75f, { -1327.0f, 100.0f, -3342.0f }, { -1320.0f, 350.0f, -3540.0f } }, + { 0x01, 0xFF, 0x2121, 10, 0, 60.0f, 0.75f, { 0.0f, 10.0f, 0.0f }, { 0.0f, 20.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801218B4[2] = { + { 0x0F, 0xFF, 0x0101, 60, 0, 65.0f, 1.0f, { 0.0f, 350.0f, -1520.0f }, { 0.0f, 715.0f, -885.0f } }, + { 0x03, 0xFF, 0x0101, 100, 0, 70.0f, 0.02f, { 0.0f, 75.0f, -1335.0f }, { 0.0f, 20.0f, -1190.0f } }, +}; + +static OnePointCsFull D_80121904[2] = { + { 0x0F, 0xFF, 0x0101, 50, 10, 65.0f, 1.0f, { 165.0f, 85.0f, -920.0f }, { 65.0f, -30.0f, -720.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121954[3][2] = { + { + { 0x0F, 0xFF, 0x0101, 20, 5, 60.0f, 1.0f, { -700.0f, 940.0f, 300.0f }, { -765.0f, 1000.0f, 335.0f } }, + { 0x03, 0xFF, 0x0101, 80, -10, 70.0f, 0.1f, { -540.0f, 875.0f, 245.0f }, { -585.0f, 900.0f, 335.0f } }, + }, + { + { 0x0F, 0xFF, 0x0101, 40, -30, 70.0f, 1.0f, { -80.0f, 115.0f, -180.0f }, { -5.0f, 240.0f, -190.0f } }, + { 0x0B, 0xFF, 0x0101, 60, 20, 70.0f, 0.1f, { -100.0f, 350.0f, -175.0f }, { -5.0f, 240.0f, -190.0f } }, + }, + { + { 0x03, 0xFF, 0x0101, 80, 5, 70.0f, 0.2f, { 960.0f, 900.0f, 260.0f }, { 970.0f, 950.0f, 250.0f } }, + { 0x0F, 0xFF, 0x0101, 20, 5, 70.0f, 1.0f, { 960.0f, 900.0f, 260.0f }, { 970.0f, 950.0f, 250.0f } }, + }, +}; + +static OnePointCsFull D_80121A44[12] = { + { 0x4F, 0x05, 0x2121, 10, 0, 60.0f, 1.0f, { 0.0f, -5.0f, 0.0f }, { 0.0f, 0.0f, -80.0f } }, + { 0x42, 0x01, 0x4242, 30, 0, 50.0f, 1.0f, { 0.0f, 45.0f, 0.0f }, { 0.0f, 45.0f, 50.0f } }, + { 0x55, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x4F, 0x05, 0x2222, 40, 5, 50.0f, 1.0f, { 0.0f, 50.0f, 0.0f }, { 0.0f, 50.0f, 50.0f } }, + { 0x4F, 0x01, 0x4242, 40, 5, 60.0f, 1.0f, { 30.0f, 30.0f, 15.0f }, { 70.0f, 30.0f, -40.0f } }, + { 0x55, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x4F, 0xFF, 0x4242, 30, -5, 50.0f, 1.0f, { 20.0f, 30.0f, -5.0f }, { 0.0f, 70.0f, 70.0f } }, + { 0x50, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x4F, 0x01, 0x2242, 40, 0, 45.0f, 1.0f, { 0.0f, 30.0f, 30.0f }, { 25.0f, 60.0f, -60.0f } }, + { 0x4B, 0x01, 0x22C2, 140, 0, 60.0f, 0.04f, { 0.0f, 0.0f, 30.0f }, { 25.0f, 60.0f, -60.0f } }, + { 0x49, 0xFF, 0x2222, 20, 0, 60.0f, 0.8f, { 0.0f, 50.0f, 0.0f }, { 0.0f, 60.0f, -60.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121C24[7] = { + { 0x0F, 0x05, 0x0101, 1, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x03, 0xFF, 0x0101, 89, 0, 50.0f, 0.4f, { 125.0f, 320.0f, -1500.0f }, { 125.0f, 500.0f, -1150.0f } }, + { 0x0F, 0x08, 0x0101, 40, 4, 55.0f, 1.0f, { 0.0f, 375.0f, -1440.0f }, { 5.0f, 365.0f, -1315.0f } }, + { 0x0F, 0xFF, 0x0101, 40, -4, 55.0f, 1.0f, { 250.0f, 375.0f, -1440.0f }, { 235.0f, 365.0f, -1315.0f } }, + { 0x0F, 0xFF, 0x0101, 100, 0, 95.0f, 1.0f, { 125.0f, 345.0f, -1500.0f }, { 125.0f, 255.0f, -1350.0f } }, + { 0x02, 0xFF, 0x0101, 100, 0, 60.0f, 1.0f, { 125.0f, 325.0f, -1500.0f }, { 125.0f, 480.0f, -1000.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121D3C[3] = { + { 0x0F, 0xFF, 0x0101, 1, 0, 60.0f, 1.0f, { 1023.0f, 738.0f, -2628.0f }, { 993.0f, 770.0f, -2740.0f } }, + { 0x02, 0xFF, 0x0101, 4, 0, 50.0f, 1.0f, { 1255.0f, 350.0f, -1870.0f }, { 1240.0f, 575.0f, -2100.0f } }, + { 0x0F, 0xFF, 0x0000, -1, 0, 75.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121DB4[9] = { + { 0x0F, 0xFF, 0x0101, 40, 0, 70.0f, 1.0f, { 4290.0f, -1332.0f, -1900.0f }, { 4155.0f, -1360.0f, -1840.0f } }, + { 0x02, 0xFF, 0x0101, 60, 0, 70.0f, 1.0f, { 4215.0f, -975.0f, -2095.0f }, { 4070.0f, -1000.0f, -2025.0f } }, + { 0x0F, 0xFF, 0x0101, 5, 0, 70.0f, 1.0f, { 4215.0f, -975.0f, -2095.0f }, { 4070.0f, -1000.0f, -2025.0f } }, + { 0x0F, 0xFF, 0x0101, 80, 8, 75.0f, 1.0f, { 4010.0f, -1152.0f, -1728.0f }, { 3997.0f, -1194.0f, -1629.0f } }, + { 0x0F, 0x39, 0x2121, 1, 8, 75.0f, 1.0f, { 20.0f, 20.0f, 0.0f }, { 50.0f, 30.0f, 200.0f } }, + { 0x04, 0xFF, 0x2121, 99, 2, 70.0f, 0.02f, { -20.0f, 0.0f, 20.0f }, { 300.0f, 50.0f, -500.0f } }, + { 0x09, 0x38, 0x2121, 149, -20, 70.0f, 0.1f, { 100.0f, 50.0f, -100.0f }, { 5000.0f, 1055.0f, -2250.0f } }, + { 0x0F, 0xFF, 0x2121, 1, 0, 60.0f, 1.0f, { 0.0f, -20.0f, 0.0f }, { 0.0f, 20.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121F1C[4] = { + { 0x0F, 0x08, 0x0101, 10, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x01, 0xFF, 0x2121, 10, 0, 50.0f, 0.5f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 150.0f } }, + { 0x01, 0x02, 0x2121, 23, 0, 50.0f, 0.5f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 150.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80121FBC[4] = { + { 0x0F, 0xFF, 0x0101, 5, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x01, 0xFF, 0x0101, 10, 0, 30.0f, 1.0f, { -2130.0f, 2885.0f, -1055.0f }, { -2085.0f, 2875.0f, -1145.0f } }, + { 0x0F, 0xFF, 0x0000, 30, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x12, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012205C[3] = { + { 0x0F, 0xFF, 0x42C2, 1, 0, 50.0f, 1.0f, { 0.0f, 220.0f, 0.0f }, { 0.0f, 220.0f, 240.0f } }, + { 0x0F, 0xFF, 0x0080, 29, 0, 50.0f, 1.0f, { 0.0f, 220.0f, 0.0f }, { 0.0f, 220.0f, 240.0f } }, + { 0x01, 0x01, 0x21A1, 10, 0, 60.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, -200.0f } }, +}; + +static OnePointCsFull D_801220D4[5] = { + { 0x0F, 0x01, 0x0101, 5, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x01, 0xFF, 0x4141, 10, 5, 55.0f, 0.75f, { 400.0f, -50.0f, 800.0f }, { 600.0f, -60.0f, 800.0f } }, + { 0x01, 0xFF, 0x4141, 15, 10, 40.0f, 0.75f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 10.0f, 200.0f } }, + { 0x0F, 0xFF, 0x0000, 25, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012219C[7] = { + { 0x0F, 0xFF, 0x2121, 5, 0, 60.0f, 1.0f, { 0.0f, -5.0f, 0.0f }, { 0.0f, 0.0f, -80.0f } }, + { 0x02, 0xFF, 0x4242, 15, 0, 40.0f, 0.4f, { 0.0f, 60.0f, -20.0f }, { 0.0f, 60.0f, 100.0f } }, + { 0x0F, 0xFF, 0x0000, 20, 0, 40.0f, 1.0f, { 0.0f, 60.0f, -20.0f }, { 0.0f, 60.0f, 100.0f } }, + { 0x01, 0xFF, 0x4242, 20, 0, 60.0f, 1.0f, { 20.0f, 60.0f, 20.0f }, { 40.0f, 60.0f, -80.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0000, 90, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801222B4[5] = { + { 0x0F, 0xFF, 0x0101, 20, 10, 45.0f, 1.0f, { -1200.0f, 730.0f, -860.0f }, { -1100.0f, 500.0f, -1025.0f } }, + { 0x0B, 0xFF, 0x0101, 20, 10, 45.0f, 0.1f, { -880.0f, 480.0f, -860.0f }, { -1100.0f, 500.0f, -1025.0f } }, + { 0x0B, 0x81, 0x0101, 20, 10, 45.0f, 0.1f, { -880.0f, 500.0f, -860.0f }, { -1100.0f, 500.0f, -1025.0f } }, + { 0x0B, 0x8A, 0x0101, 5, 10, 45.0f, 0.1f, { -880.0f, 500.0f, -860.0f }, { -1100.0f, 500.0f, -1025.0f } }, + { 0x12, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012237C[2] = { + { 0x0F, 0xFF, 0x0101, 20, -2, 65.0f, 1.0f, { -625.0f, 185.0f, -685.0f }, { -692.0f, 226.0f, -515.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801223CC[6] = { + { 0x0F, 0xFF, 0x0101, 20, 0, 55.0f, 1.0f, { 60.0f, 1130.0f, -1430.0f }, { 60.0f, 1130.0f, -1190.0f } }, + { 0x0F, 0xFF, 0x0101, 18, -13, 68.0f, 1.0f, { 60.0f, 1130.0f, -1445.0f }, { 180.0f, 1170.0f, -1240.0f } }, + { 0x0F, 0xFF, 0x0101, 16, 18, 75.0f, 1.0f, { 42.0f, 1040.0f, -1400.0f }, { -20.0f, 940.0f, -1280.0f } }, + { 0x0F, 0xFF, 0x0101, 4, 0, 60.0f, 1.0f, { 60.0f, 1100.0f, -1465.0f }, { 60.0f, 1100.0f, -1180.0f } }, + { 0x02, 0xFF, 0x0101, 32, 0, 70.0f, 1.0f, { 60.0f, 1100.0f, -1030.0f }, { 60.0f, 1150.0f, -740.0f } }, + { 0x12, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801224BC[7] = { + { 0x0F, 0xFF, 0x0101, 5, 0, 70.0f, 1.0f, { 60.0f, 1800.0f, -920.0f }, { 60.0f, 1860.0f, -800.0f } }, + { 0x03, 0xFF, 0x0101, 20, 0, 70.0f, 0.1f, { 60.0f, 1720.0f, -920.0f }, { 60.0f, 1780.0f, -800.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0142, 1, 0, 75.0f, 1.0f, { 0.0f, 70.0f, 0.0f }, { 60.0f, 990.0f, -690.0f } }, + { 0x03, 0xFF, 0x0142, 119, 0, 75.0f, 0.05f, { 0.0f, 70.0f, 0.0f }, { 60.0f, 990.0f, -690.0f } }, + { 0x03, 0xFF, 0x4242, 20, 0, 60.0f, 0.1f, { 0.0f, 70.0f, 0.0f }, { 0.0f, 100.0f, 200.0f } }, + { 0x12, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801225D4[5] = { + { 0x0F, 0x08, 0x0101, 1, 0, 50.0f, 1.0f, { 4100.0f, 1200.0f, -1400.0f }, { 3900.0f, 1100.0f, -1400.0f } }, + { 0x01, 0x3B, 0x0101, 60, 4, 50.0f, 0.94f, { 4100.0f, 965.0f, -1385.0f }, { 3790.0f, 825.0f, -1325.0f } }, + { 0x03, 0xFF, 0x0101, 90, -5, 130.0f, 0.02f, { 4100.0f, 975.0f, -1375.0f }, { 3735.0f, 715.0f, -1325.0f } }, + { 0x0F, 0x08, 0x2323, 2, 0, 60.0f, 1.0f, { 0.0f, 60.0f, 0.0f }, { -10.0f, 15.0f, -200.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012269C[3] = { + { 0x0F, 0xFF, 0x0101, 20, 2, 45.0f, 1.0f, { 975.0f, 225.0f, -1195.0f }, { 918.0f, 228.0f, -1228.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122714[4] = { + { 0x0F, 0xFF, 0x0101, 20, 0, 45.0f, 1.0f, { -915.0f, -2185.0f, 6335.0f }, { -915.0f, -2290.0f, 6165.0f } }, + { 0x02, 0xFF, 0x0101, -1, 0, 80.0f, 0.8f, { -920.0f, -2270.0f, 6140.0f }, { -920.0f, -2280.0f, 6070.0f } }, + { 0x02, 0xFF, 0x0101, 20, 0, 80.0f, 0.9f, { -920.0f, -2300.0f, 6140.0f }, { -920.0f, -2300.0f, 6070.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801227B4[6] = { + { 0x0F, 0xFF, 0x0101, 30, 0, 60.0f, 1.0f, { 1400.0f, 100.0f, -170.0f }, { 1250.0f, 100.0f, -170.0f } }, + { 0x03, 0xFF, 0x4242, 130, 0, 60.0f, 0.2f, { 0.0f, -5.0f, 0.0f }, { -150.0f, -5.0f, 0.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x02, 0xFF, 0x0303, 69, 0, 85.0f, 1.0f, { -40.0f, 0.0f, 0.0f }, { -40.0f, 0.0f, 0.0f } }, + { 0x02, 0xFF, 0x0303, 20, 0, 60.0f, 1.0f, { 10.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801228A4[5] = { + { 0x0F, 0x01, 0x0101, 20, 5, 30.0f, 1.0f, { 800.0f, -40.0f, 2170.0f }, { 512.0f, 142.0f, 2020.0f } }, + { 0x02, 0xFF, 0x0101, 20, -2, 70.0f, 0.8f, { 800.0f, -40.0f, 2170.0f }, { 512.0f, 142.0f, 2020.0f } }, + { 0x0F, 0x08, 0x0101, 90, 2, 62.0f, 1.0f, { 1140.0f, 125.0f, 1920.0f }, { 1255.0f, 150.0f, 1785.0f } }, + { 0x81, 0xFF, 0x2121, 10, 0, 60.0f, 1.0f, { 0.0f, 10.0f, 0.0f }, { 30.0f, 10.0f, -80.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012296C[4] = { + { 0x0F, 0xFF, 0x0101, 20, -10, 70.0f, 1.0f, { -930.0f, 765.0f, -3075.0f }, { -700.0f, 700.0f, -3075.0f } }, + { 0x03, 0xFF, 0x0101, 80, -10, 70.0f, 0.05f, { -930.0f, 205.0f, -3075.0f }, { -700.0f, 140.0f, -3075.0f } }, + { 0x0F, 0xFF, 0x0000, 120, 0, 70.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122A0C[2] = { + { 0x0F, 0xFF, 0x0101, 60, 4, 50.0f, 1.0f, { 0.0f, 400.0f, -1000.0f }, { -200.0f, 500.0f, -850.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 50.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122A5C[8] = { + { 0x0F, 0xFF, 0x0101, 1, -15, 70.0f, 1.0f, { 230.0f, 3675.0f, -4230.0f }, { -45.0f, 3650.0f, -4415.0f } }, + { 0x15, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0101, 1, 0, 60.0f, 1.0f, { -120.0f, 2187.0f, -3286.0f }, { -110.0f, 2162.0f, -3262.0f } }, + { 0x15, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0101, 55, 0, 60.0f, 1.0f, { -38.0f, 1467.0f, -1102.0f }, { 64.0f, 1423.0f, -1188.0f } }, + { 0x0F, 0xFF, 0x0101, 1, -15, 70.0f, 1.0f, { 230.0f, 3675.0f, -4230.0f }, { -20.0f, 3650.0f, -4400.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122B9C[3] = { + { 0x0F, 0xFF, 0x0101, 60, 0, 65.0f, 1.0f, { 1095.0f, 2890.0f, -2980.0f }, { 1166.0f, 2695.0f, -2710.0f } }, + { 0x0F, 0xFF, 0x0101, 60, 15, 65.0f, 1.0f, { 566.0f, 4654.0f, -4550.0f }, { 606.0f, 5160.0f, -4740.0f } }, + { 0x11, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122C14[1] = { + { 0x0F, 0xFF, 0x0101, 999, 0, 85.0f, 1.0f, { -15.0f, 185.0f, 160.0f }, { -15.0f, 210.0f, 250.0f } }, +}; + +static OnePointCsFull D_80122C3C[1] = { + { 0x0F, 0xFF, 0x0101, 999, -2, 70.0f, 1.0f, { -62.0f, 60.0f, -315.0f }, { -115.0f, 30.0f, -445.0f } }, +}; + +static OnePointCsFull D_80122C64[1] = { + { 0x0F, 0xFF, 0x0101, 999, 3, 70.0f, 1.0f, { -40.0f, 80.0f, 375.0f }, { -85.0f, 45.0f, 485.0f } }, +}; + +static OnePointCsFull D_80122C8C[1] = { + { 0x0F, 0xFF, 0x0101, 999, 5, 60.0f, 1.0f, { -70.0f, 140.0f, 25.0f }, { 10.0f, 180.0f, 195.0f } }, +}; + +static OnePointCsFull D_80122CB4[2] = { + { 0x0F, 0xFF, 0x4242, 5, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 1000.0f }, { 0.0f, 0.0f, 1100.0f } }, + { 0x02, 0xFF, 0x4242, -1, 0, 60.0f, 1.0f, { 0.0f, 0.0f, -100.0f }, { 0.0f, 0.0f, 0.0f } }, +}; + +static OnePointCsFull D_80122D04[2] = { + { 0x0F, 0xFF, 0x4242, 10, 0, 60.0f, 1.0f, { 0.0f, 0.0f, -100.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x02, 0xFF, 0x4242, -1, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 1000.0f }, { 0.0f, 0.0f, 1100.0f } }, +}; + +static OnePointCsFull D_80122D54[3] = { + { 0x0F, 0xFF, 0x0101, 1, -4, 50.0f, 1.0f, { 230.0f, 65.0f, 300.0f }, { 50.0f, 50.0f, 225.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122DCC[3] = { + { 0x0F, 0xFF, 0x0101, 1, 0, 50.0f, 1.0f, { 0.0f, 5.0f, -145.0f }, { 0.0f, 55.0f, 55.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x11, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80122E44[2][7] = { + { + { 0x83, 0xFF, 0x2222, 10, 5, 90.0f, 0.2f, { 50.0f, 100.0f, 140.0f }, { -30.0f, 10.0f, -20.0f } }, + { 0x8F, 0xFF, 0x0000, 20, 0, 90.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x03, 0xFF, 0x4343, 30, -5, 50.0f, 0.2f, { -10.0f, 80.0f, 10.0f }, { 20.0f, 20.0f, 120.0f } }, + { 0x10, 0xFF, 0x0000, 1, -5, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0B, 0x01, 0x4343, 160, 10, 80.0f, 0.005f, { -50.0f, 60.0f, 0.0f }, { -100.0f, 20.0f, 50.0f } }, + { 0x02, 0xFF, 0x0501, 50, 0, 60.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 80.0f } }, + { 0x13, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + }, + { + { 0x83, 0xFF, 0x2222, 10, -5, 90.0f, 0.2f, { -50.0f, 100.0f, 140.0f }, { 30.0f, 10.0f, -20.0f } }, + { 0x8F, 0xFF, 0x0000, 20, 0, 90.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x03, 0xFF, 0x4343, 30, 5, 50.0f, 0.2f, { 10.0f, 80.0f, 10.0f }, { -20.0f, 20.0f, 120.0f } }, + { 0x10, 0xFF, 0x0000, 1, 5, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0B, 0x01, 0x4343, 160, -10, 80.0f, 0.005f, { 50.0f, 60.0f, 0.0f }, { 100.0f, 20.0f, 50.0f } }, + { 0x02, 0xFF, 0x0501, 50, 0, 60.0f, 1.0f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 80.0f } }, + { 0x13, 0xFF, 0x0000, 1, -1, -1.0f, -1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + }, +}; + +static OnePointCsFull D_80123074[5] = { + { 0x8F, 0xFF, 0xA2A2, 2, 8, 70.0f, 1.0f, { -27.0f, -96.0f, 25.0f }, { 37.0f, -5.0f, 100.0f } }, + { 0x81, 0xFF, 0xA2A2, 38, 4, 60.0f, 1.0f, { 64.0f, -109.0f, 55.0f }, { 37.0f, 150.0f, 155.0f } }, + { 0x8F, 0xFF, 0xA2A2, 2, 8, 70.0f, 1.0f, { 45.0f, 123.0f, 45.0f }, { 70.0f, 5.0f, 125.0f } }, + { 0x81, 0xFF, 0xA2A2, 58, 4, 60.0f, 0.9f, { 82.0f, 95.0f, 55.0f }, { 25.0f, -175.0f, 180.0f } }, + { 0x92, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012313C[3] = { + { 0x8F, 0xFF, 0xA2A2, 20, 8, 70.0f, 1.0f, { 65.0f, -150.0f, 50.0f }, { 30.0f, 10.0f, 90.0f } }, + { 0x81, 0xFF, 0xA2A2, 100, 0, 60.0f, 1.0f, { 70.0f, -160.0f, 50.0f }, { 25.0f, 180.0f, 180.0f } }, + { 0x92, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801231B4[4] = { + { 0x8F, 0xC5, 0x4343, 1, 0, 50.0f, 1.0f, { 0.0f, 20.0f, 0.0f }, { 0.0f, 5.0f, -1.0f } }, + { 0x81, 0xC5, 0x4343, 48, 0, 50.0f, 0.75f, { 0.0f, 80.0f, 0.0f }, { 0.0f, 15.0f, -1.0f } }, + { 0x8F, 0xC5, 0x4343, 1, 5, 45.0f, 1.0f, { 0.0f, 0.0f, 30.0f }, { 30.0f, 120.0f, 60.0f } }, + { 0x81, 0xC5, 0x4343, -1, 0, -1.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80123254[2] = { + { 0x0F, 0xFF, 0x0101, 1, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x03, 0xC5, 0x0101, 49, 0, 50.0f, 0.05f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, +}; + +static OnePointCsFull D_801232A4[1] = { + { 0x0F, 0x45, 0x0101, 9999, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, +}; + +static OnePointCsFull D_801232CC[5] = { + { 0x01, 0xFF, 0x0101, 45, -3, 65.0f, 1.0f, { -52.0f, 84.0f, -846.0f }, { -159.0f, 33.0f, -729.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0000, 10, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x01, 0xFF, 0x2121, 15, 0, 60.0f, 1.0f, { 10.0f, -5.0f, 0.0f }, { 0.0f, 0.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80123394[5] = { + { 0x01, 0xFF, 0x0101, 45, 3, 65.0f, 1.0f, { -16.0f, 87.0f, -829.0f }, { 98.0f, 24.0f, -714.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0000, 10, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x01, 0xFF, 0x2121, 15, 0, 60.0f, 1.0f, { 10.0f, -5.0f, 0.0f }, { 0.0f, 0.0f, -150.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012345C[4] = { + { 0x01, 0x01, 0x4242, 40, 0, 40.0f, 1.0f, { 0.0f, 50.0f, -40.0f }, { 0.0f, 60.0f, -160.0f } }, + { 0x04, 0x4D, 0x4242, 40, 0, 60.0f, 0.3f, { 0.0f, 90.0f, -40.0f }, { 0.0f, 60.0f, -160.0f } }, + { 0x04, 0x01, 0x2121, 10, 0, 60.0f, 0.2f, { 0.0f, -10.0f, 10.0f }, { 0.0f, 10.0f, -80.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801234FC[5] = { + { 0x01, 0x05, 0x0441, 10, 0, 70.0f, 1.0f, { 0.0f, -10.0f, 20.0f }, { 0.0f, 0.0f, 120.0f } }, + { 0x03, 0xFF, 0x4141, 30, 0, 50.0f, 0.1f, { 0.0f, -10.0f, 20.0f }, { 0.0f, 10.0f, 80.0f } }, + { 0x10, 0x01, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x82, 0xFF, 0x2121, 10, 0, 60.0f, 0.9f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, -80.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_801235C4[5] = { + { 0x0F, 0x01, 0x4141, 1, 0, 50.0f, 1.0f, { 0.0f, -10.0f, 20.0f }, { 0.0f, 10.0f, 60.0f } }, + { 0x83, 0xFF, 0x0441, 39, 0, 70.0f, 0.1f, { 0.0f, -10.0f, 20.0f }, { 0.0f, 0.0f, 100.0f } }, + { 0x10, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x82, 0xFF, 0x2121, 15, 0, 60.0f, 0.9f, { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, -80.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_8012368C[4] = { + { 0x0F, 0xFF, 0x0101, 10, 0, 60.0f, 1.0f, { -1110.0f, -180.0f, -840.0f }, { -985.0f, -220.0f, -840.0f } }, + { 0x02, 0xFF, 0x0101, 70, -45, 75.0f, 1.0f, { -1060.0f, -160.0f, -840.0f }, { -1005.0f, -230.0f, -840.0f } }, + { 0x0F, 0xFF, 0x0000, 10, -45, 75.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x0F, 0xFF, 0x0101, 180, 9, 80.0f, 1.0f, { -1205.0f, -175.0f, -840.0f }, { -1305.0f, -230.0f, -828.0f } }, +}; + +static OnePointCsFull D_8012372C[4] = { + { 0x0F, 0xFF, 0x0142, 10, 0, 70.0f, 1.0f, { 0.0f, 80.0f, 0.0f }, { -1650.0f, 200.0f, -2920.0f } }, + { 0x02, 0xFF, 0x0142, 110, -2, 50.0f, 0.5f, { 0.0f, 150.0f, 0.0f }, { -1320.0f, 170.0f, -2900.0f } }, + { 0x0B, 0xFF, 0x4242, 100, 2, 70.0f, 0.1f, { 0.0f, 150.0f, 50.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x03, 0xFF, 0x4242, 60, 2, 45.0f, 0.01f, { 0.0f, 150.0f, 50.0f }, { 0.0f, 200.0f, -80.0f } }, +}; + +static OnePointCsFull D_801237CC[5] = { + { 0x8F, 0xFF, 0x4242, 20, 0, 50.0f, 1.0f, { 0.0f, 50.0f, -10.0f }, { 0.0f, 0.0f, 100.0f } }, + { 0x0A, 0xFF, 0x0101, 80, 0, 75.0f, 1.0f, { 2900.0f, 1300.0f, 530.0f }, { 2800.0f, 1190.0f, 540.0f } }, + { 0x0F, 0xFF, 0x0000, 10, 0, 75.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, + { 0x02, 0xFF, 0x0101, 55, 0, 75.0f, 1.0f, { 2900.0f, 1300.0f, 530.0f }, { 1500.0f, 1415.0f, 650.0f } }, + { 0x0F, 0xFF, 0x0000, 100, -45, 75.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; + +static OnePointCsFull D_80123894[3] = { + { 0x0F, 0xFF, 0x0101, 60, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x0F, 0xFF, 0x4242, 30, 0, 50.0f, 1.0f, { 0.0f, 28.0f, 0.0f }, { 0.0f, 20.0f, 40.0f } }, + { 0x0D, 0xFF, 0x0000, 120, 0, 180.0f, 0.4f, { 0.0f, -5.0f, 0.0f }, { 0.0f, 2.0f, 40.0f } }, +}; + +static OnePointCsFull D_8012390C[2] = { + { 0x0F, 0xFF, 0x0101, 30, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x0F, 0xFF, 0x4242, 180, 0, 60.0f, 1.0f, { 0.0f, 78.0f, 0.0f }, { 0.0f, 78.0f, 200.0f } }, +}; + +static OnePointCsFull D_8012395C[3] = { + { 0x0F, 0xFF, 0x0101, 60, 0, 60.0f, 1.0f, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x0F, 0xFF, 0x4242, 30, 0, 50.0f, 1.0f, { 0.0f, 28.0f, 0.0f }, { 0.0f, 20.0f, -45.0f } }, + { 0x0D, 0xFF, 0x0000, 120, 0, 180.0f, 0.4f, { 0.0f, -5.0f, 0.0f }, { 0.0f, 2.0f, 45.0f } }, +}; + +static OnePointCsFull D_801239D4[3] = { + { 0x0F, 0xFF, 0x4242, 5, 0, 60.0f, 1.0f, { 0.0f, 20.0f, 0.0f }, { 0.0f, 40.0f, -120.0f } }, + { 0x09, 0xFF, 0x4242, 0, 0, 60.0f, 1.0f, { 0.0f, 20.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }, + { 0x12, 0xFF, 0x0000, 1, 0, 60.0f, 1.0f, { -1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f } }, +}; diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c new file mode 100644 index 000000000..3219527a9 --- /dev/null +++ b/soh/src/code/z_parameter.c @@ -0,0 +1,4297 @@ +#include "global.h" +#include "vt.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/do_action_static/do_action_static.h" +#include "textures/icon_item_static/icon_item_static.h" + +#ifdef _MSC_VER +#include +#include +#include +#endif + +// TODO extract this information from the texture definitions themselves +#define DO_ACTION_TEX_WIDTH 48 +#define DO_ACTION_TEX_HEIGHT 16 +#define DO_ACTION_TEX_SIZE ((DO_ACTION_TEX_WIDTH * DO_ACTION_TEX_HEIGHT) / 2) // (sizeof(gCheckDoActionENGTex)) + +typedef struct { + /* 0x00 */ u8 scene; + /* 0x01 */ u8 flags1; + /* 0x02 */ u8 flags2; + /* 0x03 */ u8 flags3; +} RestrictionFlags; + +static RestrictionFlags sRestrictionFlags[] = { + { SCENE_SPOT00, 0x00, 0x00, 0x10 }, + { SCENE_SPOT01, 0x00, 0x00, 0x10 }, + { SCENE_SPOT02, 0x00, 0x00, 0x10 }, + { SCENE_SPOT03, 0x00, 0x00, 0x10 }, + { SCENE_SPOT04, 0x00, 0x00, 0x10 }, + { SCENE_SPOT05, 0x00, 0x00, 0x10 }, + { SCENE_SPOT06, 0x00, 0x00, 0x10 }, + { SCENE_SPOT07, 0x00, 0x00, 0x10 }, + { SCENE_SPOT08, 0x00, 0x00, 0x10 }, + { SCENE_SPOT09, 0x00, 0x00, 0x10 }, + { SCENE_SPOT10, 0x00, 0x00, 0x10 }, + { SCENE_SPOT11, 0x00, 0x00, 0x10 }, + { SCENE_SPOT12, 0x00, 0x00, 0x10 }, + { SCENE_SPOT13, 0x00, 0x00, 0x10 }, + { SCENE_SPOT15, 0x00, 0x00, 0x10 }, + { SCENE_GANON_TOU, 0x00, 0x00, 0x10 }, + { SCENE_SPOT16, 0x00, 0x00, 0x10 }, + { SCENE_SPOT17, 0x00, 0x00, 0x10 }, + { SCENE_SPOT18, 0x00, 0x00, 0x10 }, + { SCENE_SPOT20, 0x00, 0x00, 0x10 }, + { SCENE_TOKINOMA, 0x00, 0x10, 0x15 }, + { SCENE_KENJYANOMA, 0xA2, 0xAA, 0xAA }, + { SCENE_SYATEKIJYOU, 0x11, 0x55, 0x55 }, + { SCENE_HAIRAL_NIWA, 0x11, 0x55, 0x55 }, + { SCENE_HAIRAL_NIWA_N, 0x11, 0x55, 0x55 }, + { SCENE_HAKAANA, 0x00, 0x00, 0xD0 }, + { SCENE_HAKAANA2, 0x00, 0x00, 0xD0 }, + { SCENE_HAKAANA_OUKE, 0x00, 0x00, 0xD0 }, + { SCENE_DAIYOUSEI_IZUMI, 0x00, 0x00, 0x10 }, + { SCENE_YOUSEI_IZUMI_TATE, 0x00, 0x00, 0xD0 }, + { SCENE_YOUSEI_IZUMI_YOKO, 0x00, 0x00, 0x10 }, + { SCENE_GANON_FINAL, 0x00, 0x05, 0x50 }, + { SCENE_NAKANIWA, 0x00, 0x05, 0x54 }, + { SCENE_TURIBORI, 0x11, 0x55, 0x55 }, + { SCENE_BOWLING, 0x11, 0x55, 0x55 }, + { SCENE_SOUKO, 0x00, 0x10, 0x15 }, + { SCENE_MIHARIGOYA, 0x00, 0x10, 0x14 }, + { SCENE_MAHOUYA, 0x10, 0x15, 0x55 }, + { SCENE_TAKARAYA, 0x10, 0x15, 0x55 }, + { SCENE_KINSUTA, 0x00, 0x10, 0x15 }, + { SCENE_ENTRA, 0x00, 0x10, 0x15 }, + { SCENE_ENTRA_N, 0x00, 0x10, 0x15 }, + { SCENE_ENRUI, 0x00, 0x10, 0xD5 }, + { SCENE_MARKET_DAY, 0x00, 0x10, 0x15 }, + { SCENE_MARKET_NIGHT, 0x00, 0x10, 0x15 }, + { SCENE_MARKET_RUINS, 0x00, 0x10, 0xD5 }, + { SCENE_MARKET_ALLEY, 0x00, 0x10, 0x15 }, + { SCENE_MARKET_ALLEY_N, 0x00, 0x10, 0x15 }, + { SCENE_SHRINE, 0x00, 0x10, 0x15 }, + { SCENE_SHRINE_N, 0x00, 0x10, 0x15 }, + { SCENE_SHRINE_R, 0x00, 0x10, 0xD5 }, + { SCENE_LINK_HOME, 0x10, 0x10, 0x15 }, + { SCENE_KAKARIKO, 0x10, 0x10, 0x15 }, + { SCENE_KAKARIKO3, 0x10, 0x10, 0x15 }, + { SCENE_KOKIRI_HOME, 0x10, 0x10, 0x15 }, + { SCENE_KOKIRI_HOME3, 0x10, 0x10, 0x15 }, + { SCENE_KOKIRI_HOME4, 0x10, 0x10, 0x15 }, + { SCENE_KOKIRI_HOME5, 0x10, 0x10, 0x15 }, + { SCENE_MALON_STABLE, 0x10, 0x10, 0x15 }, + { SCENE_HUT, 0x10, 0x10, 0x15 }, + { SCENE_IMPA, 0x10, 0x10, 0x15 }, + { SCENE_LABO, 0x10, 0x10, 0x15 }, + { SCENE_HYLIA_LABO, 0x00, 0x10, 0x15 }, + { SCENE_TENT, 0x10, 0x10, 0x15 }, + { SCENE_SHOP1, 0x10, 0x10, 0x15 }, + { SCENE_KOKIRI_SHOP, 0x10, 0x10, 0x15 }, + { SCENE_GOLON, 0x10, 0x10, 0x15 }, + { SCENE_ZOORA, 0x10, 0x10, 0x15 }, + { SCENE_DRAG, 0x10, 0x10, 0x15 }, + { SCENE_ALLEY_SHOP, 0x10, 0x10, 0x15 }, + { SCENE_NIGHT_SHOP, 0x10, 0x10, 0x15 }, + { SCENE_FACE_SHOP, 0x10, 0x10, 0x15 }, + { SCENE_MEN, 0x00, 0x03, 0x10 }, + { SCENE_YDAN, 0x00, 0x00, 0x00 }, + { SCENE_YDAN_BOSS, 0x00, 0x45, 0x50 }, + { SCENE_DDAN, 0x00, 0x00, 0x00 }, + { SCENE_DDAN_BOSS, 0x00, 0x45, 0x50 }, + { SCENE_BDAN, 0x00, 0x00, 0x00 }, + { SCENE_BDAN_BOSS, 0x00, 0x45, 0x50 }, + { SCENE_BMORI1, 0x00, 0x00, 0x00 }, + { SCENE_MORIBOSSROOM, 0x00, 0x45, 0x50 }, + { SCENE_HAKADANCH, 0x00, 0x00, 0x00 }, + { SCENE_HAKADAN, 0x00, 0x00, 0x00 }, + { SCENE_HAKADAN_BS, 0x00, 0x45, 0x50 }, + { SCENE_HIDAN, 0x00, 0x00, 0x00 }, + { SCENE_FIRE_BS, 0x00, 0x45, 0x50 }, + { SCENE_MIZUSIN, 0x00, 0x00, 0x00 }, + { SCENE_MIZUSIN_BS, 0x00, 0x45, 0x50 }, + { SCENE_JYASINZOU, 0x00, 0x00, 0x00 }, + { SCENE_JYASINBOSS, 0x00, 0x45, 0x50 }, + { SCENE_GANON, 0x00, 0x00, 0x00 }, + { SCENE_GANON_BOSS, 0x00, 0x45, 0x50 }, + { SCENE_ICE_DOUKUTO, 0x00, 0x00, 0xC0 }, + { SCENE_HAKASITARELAY, 0x00, 0x03, 0x14 }, + { SCENE_GANONTIKA, 0x00, 0x03, 0x10 }, + { SCENE_GANON_DEMO, 0x00, 0x45, 0x50 }, + { SCENE_GANON_SONOGO, 0x00, 0x05, 0x50 }, + { SCENE_GANONTIKA_SONOGO, 0x00, 0x05, 0x50 }, + { SCENE_GERUDOWAY, 0x00, 0x00, 0x10 }, + { SCENE_KAKUSIANA, 0x00, 0x00, 0xD0 }, + { 0xFF, 0x00, 0x00, 0x00 }, +}; + +static s16 sHBAScoreTier = 0; +static u16 sHBAScoreDigits[] = { 0, 0, 0, 0 }; + +static u16 sCUpInvisible = 0; +static u16 sCUpTimer = 0; + +s16 gSpoilingItems[] = { ITEM_ODD_MUSHROOM, ITEM_FROG, ITEM_EYEDROPS }; +s16 gSpoilingItemReverts[] = { ITEM_COJIRO, ITEM_PRESCRIPTION, ITEM_PRESCRIPTION }; + +static s16 sMagicBorderR = 255; +static s16 sMagicBorderG = 255; +static s16 sMagicBorderB = 255; + +static s16 sExtraItemBases[] = { + ITEM_STICK, ITEM_STICK, ITEM_NUT, ITEM_NUT, ITEM_BOMB, ITEM_BOMB, ITEM_BOMB, ITEM_BOMB, ITEM_BOW, + ITEM_BOW, ITEM_BOW, ITEM_SEEDS, ITEM_BOMBCHU, ITEM_BOMBCHU, ITEM_STICK, ITEM_STICK, ITEM_NUT, ITEM_NUT, +}; + +static s16 D_80125A58 = 0; +static s16 D_80125A5C = 0; + +static Gfx sSetupDL_80125A60[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | + G_TEXTURE_GEN_LINEAR | G_SHADING_SMOOTH | G_LOD), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_1PRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE), + gsSPEndDisplayList(), +}; + +static const char* actionsTbl[] = +{ + gAttackDoActionENGTex, + gCheckDoActionENGTex, + gEnterDoActionENGTex, + gReturnDoActionENGTex, + gOpenDoActionENGTex, + gJumpDoActionENGTex, + gDecideDoActionENGTex, + gDiveDoActionENGTex, + gFasterDoActionENGTex, + gThrowDoActionENGTex, + gUnusedNaviDoActionENGTex, + gClimbDoActionENGTex, + gDropDoActionENGTex, + gDownDoActionENGTex, + gSaveDoActionENGTex, + gSpeakDoActionENGTex, + gNextDoActionENGTex, + gGrabDoActionENGTex, + gStopDoActionENGTex, + gPutAwayDoActionENGTex, + gReelDoActionENGTex, + gNum1DoActionENGTex, + gNum2DoActionENGTex, + gNum3DoActionENGTex, + gNum4DoActionENGTex, + gNum5DoActionENGTex, + gNum6DoActionENGTex, + gNum7DoActionENGTex, + gNum8DoActionENGTex, +}; + +// original name: "alpha_change" +void Interface_ChangeAlpha(u16 alphaType) { + if (alphaType != gSaveContext.unk_13EA) { + osSyncPrintf("ALPHAーTYPE=%d LAST_TIME_TYPE=%d\n", alphaType, gSaveContext.unk_13EE); + gSaveContext.unk_13EA = gSaveContext.unk_13E8 = alphaType; + gSaveContext.unk_13EC = 1; + } +} + +void func_80082644(GlobalContext* globalCtx, s16 alpha) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + if (gSaveContext.buttonStatus[0] == BTN_DISABLED) { + if (interfaceCtx->bAlpha != 70) { + interfaceCtx->bAlpha = 70; + } + } else { + if (interfaceCtx->bAlpha != 255) { + interfaceCtx->bAlpha = alpha; + } + } + + if (gSaveContext.buttonStatus[1] == BTN_DISABLED) { + if (interfaceCtx->cLeftAlpha != 70) { + interfaceCtx->cLeftAlpha = 70; + } + } else { + if (interfaceCtx->cLeftAlpha != 255) { + interfaceCtx->cLeftAlpha = alpha; + } + } + + if (gSaveContext.buttonStatus[2] == BTN_DISABLED) { + if (interfaceCtx->cDownAlpha != 70) { + interfaceCtx->cDownAlpha = 70; + } + } else { + if (interfaceCtx->cDownAlpha != 255) { + interfaceCtx->cDownAlpha = alpha; + } + } + + if (gSaveContext.buttonStatus[3] == BTN_DISABLED) { + if (interfaceCtx->cRightAlpha != 70) { + interfaceCtx->cRightAlpha = 70; + } + } else { + if (interfaceCtx->cRightAlpha != 255) { + interfaceCtx->cRightAlpha = alpha; + } + } + + if (gSaveContext.buttonStatus[4] == BTN_DISABLED) { + if (interfaceCtx->aAlpha != 70) { + interfaceCtx->aAlpha = 70; + } + } else { + if (interfaceCtx->aAlpha != 255) { + interfaceCtx->aAlpha = alpha; + } + } +} + +void func_8008277C(GlobalContext* globalCtx, s16 maxAlpha, s16 alpha) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + if (gSaveContext.unk_13E7 != 0) { + func_80082644(globalCtx, alpha); + return; + } + + if ((interfaceCtx->bAlpha != 0) && (interfaceCtx->bAlpha > maxAlpha)) { + interfaceCtx->bAlpha = maxAlpha; + } + + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } +} + +void func_80082850(GlobalContext* globalCtx, s16 maxAlpha) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 alpha = 255 - maxAlpha; + + switch (gSaveContext.unk_13E8) { + case 1: + case 2: + case 8: + osSyncPrintf("a_alpha=%d, c_alpha=%d → ", interfaceCtx->aAlpha, interfaceCtx->cLeftAlpha); + + if (gSaveContext.unk_13E8 == 8) { + if (interfaceCtx->bAlpha != 255) { + interfaceCtx->bAlpha = alpha; + } + } else { + if ((interfaceCtx->bAlpha != 0) && (interfaceCtx->bAlpha > maxAlpha)) { + interfaceCtx->bAlpha = maxAlpha; + } + } + + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } + + if ((interfaceCtx->healthAlpha != 0) && (interfaceCtx->healthAlpha > maxAlpha)) { + interfaceCtx->healthAlpha = maxAlpha; + } + + if ((interfaceCtx->magicAlpha != 0) && (interfaceCtx->magicAlpha > maxAlpha)) { + interfaceCtx->magicAlpha = maxAlpha; + } + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + osSyncPrintf("a_alpha=%d, c_alpha=%d\n", interfaceCtx->aAlpha, interfaceCtx->cLeftAlpha); + + break; + case 3: + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + func_8008277C(globalCtx, maxAlpha, alpha); + + if ((interfaceCtx->magicAlpha != 0) && (interfaceCtx->magicAlpha > maxAlpha)) { + interfaceCtx->magicAlpha = maxAlpha; + } + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + break; + case 4: + if ((interfaceCtx->bAlpha != 0) && (interfaceCtx->bAlpha > maxAlpha)) { + interfaceCtx->bAlpha = maxAlpha; + } + + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } + + if ((interfaceCtx->healthAlpha != 0) && (interfaceCtx->healthAlpha > maxAlpha)) { + interfaceCtx->healthAlpha = maxAlpha; + } + + if ((interfaceCtx->magicAlpha != 0) && (interfaceCtx->magicAlpha > maxAlpha)) { + interfaceCtx->magicAlpha = maxAlpha; + } + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if (interfaceCtx->aAlpha != 255) { + interfaceCtx->aAlpha = alpha; + } + + break; + case 5: + func_8008277C(globalCtx, maxAlpha, alpha); + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if (interfaceCtx->aAlpha != 255) { + interfaceCtx->aAlpha = alpha; + } + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + if (interfaceCtx->magicAlpha != 255) { + interfaceCtx->magicAlpha = alpha; + } + + break; + case 6: + func_8008277C(globalCtx, maxAlpha, alpha); + + if (interfaceCtx->aAlpha != 255) { + interfaceCtx->aAlpha = alpha; + } + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + if (interfaceCtx->magicAlpha != 255) { + interfaceCtx->magicAlpha = alpha; + } + + switch (globalCtx->sceneNum) { + case SCENE_SPOT00: + case SCENE_SPOT01: + case SCENE_SPOT02: + case SCENE_SPOT03: + case SCENE_SPOT04: + case SCENE_SPOT05: + case SCENE_SPOT06: + case SCENE_SPOT07: + case SCENE_SPOT08: + case SCENE_SPOT09: + case SCENE_SPOT10: + case SCENE_SPOT11: + case SCENE_SPOT12: + case SCENE_SPOT13: + case SCENE_SPOT15: + case SCENE_SPOT16: + case SCENE_SPOT17: + case SCENE_SPOT18: + case SCENE_SPOT20: + case SCENE_GANON_TOU: + if (interfaceCtx->minimapAlpha < 170) { + interfaceCtx->minimapAlpha = alpha; + } else { + interfaceCtx->minimapAlpha = 170; + } + break; + default: + if (interfaceCtx->minimapAlpha != 255) { + interfaceCtx->minimapAlpha = alpha; + } + break; + } + break; + case 7: + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + func_80082644(globalCtx, alpha); + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + if (interfaceCtx->magicAlpha != 255) { + interfaceCtx->magicAlpha = alpha; + } + + break; + case 9: + if ((interfaceCtx->bAlpha != 0) && (interfaceCtx->bAlpha > maxAlpha)) { + interfaceCtx->bAlpha = maxAlpha; + } + + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + if (interfaceCtx->magicAlpha != 255) { + interfaceCtx->magicAlpha = alpha; + } + + break; + case 10: + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } + + if ((interfaceCtx->healthAlpha != 0) && (interfaceCtx->healthAlpha > maxAlpha)) { + interfaceCtx->healthAlpha = maxAlpha; + } + + if ((interfaceCtx->magicAlpha != 0) && (interfaceCtx->magicAlpha > maxAlpha)) { + interfaceCtx->magicAlpha = maxAlpha; + } + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if (interfaceCtx->bAlpha != 255) { + interfaceCtx->bAlpha = alpha; + } + + break; + case 11: + if ((interfaceCtx->bAlpha != 0) && (interfaceCtx->bAlpha > maxAlpha)) { + interfaceCtx->bAlpha = maxAlpha; + } + + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if ((interfaceCtx->magicAlpha != 0) && (interfaceCtx->magicAlpha > maxAlpha)) { + interfaceCtx->magicAlpha = maxAlpha; + } + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + break; + case 12: + if (interfaceCtx->aAlpha != 255) { + interfaceCtx->aAlpha = alpha; + } + + if (interfaceCtx->bAlpha != 255) { + interfaceCtx->bAlpha = alpha; + } + + if (interfaceCtx->minimapAlpha != 255) { + interfaceCtx->minimapAlpha = alpha; + } + + if ((interfaceCtx->cLeftAlpha != 0) && (interfaceCtx->cLeftAlpha > maxAlpha)) { + interfaceCtx->cLeftAlpha = maxAlpha; + } + + if ((interfaceCtx->cDownAlpha != 0) && (interfaceCtx->cDownAlpha > maxAlpha)) { + interfaceCtx->cDownAlpha = maxAlpha; + } + + if ((interfaceCtx->cRightAlpha != 0) && (interfaceCtx->cRightAlpha > maxAlpha)) { + interfaceCtx->cRightAlpha = maxAlpha; + } + + if ((interfaceCtx->magicAlpha != 0) && (interfaceCtx->magicAlpha > maxAlpha)) { + interfaceCtx->magicAlpha = maxAlpha; + } + + if ((interfaceCtx->healthAlpha != 0) && (interfaceCtx->healthAlpha > maxAlpha)) { + interfaceCtx->healthAlpha = maxAlpha; + } + + break; + case 13: + func_8008277C(globalCtx, maxAlpha, alpha); + + if ((interfaceCtx->minimapAlpha != 0) && (interfaceCtx->minimapAlpha > maxAlpha)) { + interfaceCtx->minimapAlpha = maxAlpha; + } + + if ((interfaceCtx->aAlpha != 0) && (interfaceCtx->aAlpha > maxAlpha)) { + interfaceCtx->aAlpha = maxAlpha; + } + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha; + } + + if (interfaceCtx->magicAlpha != 255) { + interfaceCtx->magicAlpha = alpha; + } + + break; + } + + if ((globalCtx->roomCtx.curRoom.unk_03 == 1) && (interfaceCtx->minimapAlpha >= 0xFF)) { + interfaceCtx->minimapAlpha = 255; + } +} + +void func_80083108(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + Player* player = GET_PLAYER(globalCtx); + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 i; + s16 sp28 = 0; + + if ((gSaveContext.cutsceneIndex < 0xFFF0) || + ((globalCtx->sceneNum == SCENE_SPOT20) && (gSaveContext.cutsceneIndex == 0xFFF0))) { + gSaveContext.unk_13E7 = 0; + + if ((player->stateFlags1 & 0x00800000) || (globalCtx->shootingGalleryStatus > 1) || + ((globalCtx->sceneNum == SCENE_BOWLING) && Flags_GetSwitch(globalCtx, 0x38))) { + if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) { + gSaveContext.unk_13E7 = 1; + + if (gSaveContext.buttonStatus[0] == BTN_DISABLED) { + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + } + + if ((gSaveContext.equips.buttonItems[0] != ITEM_SLINGSHOT) && + (gSaveContext.equips.buttonItems[0] != ITEM_BOW) && + (gSaveContext.equips.buttonItems[0] != ITEM_BOMBCHU) && + (gSaveContext.equips.buttonItems[0] != ITEM_NONE)) { + gSaveContext.buttonStatus[0] = gSaveContext.equips.buttonItems[0]; + + if ((globalCtx->sceneNum == SCENE_BOWLING) && Flags_GetSwitch(globalCtx, 0x38)) { + gSaveContext.equips.buttonItems[0] = ITEM_BOMBCHU; + Interface_LoadItemIcon1(globalCtx, 0); + } else { + gSaveContext.equips.buttonItems[0] = ITEM_BOW; + if (globalCtx->shootingGalleryStatus > 1) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + gSaveContext.equips.buttonItems[0] = ITEM_SLINGSHOT; + } + + Interface_LoadItemIcon1(globalCtx, 0); + } else { + if (gSaveContext.inventory.items[SLOT_BOW] == ITEM_NONE) { + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + } else { + Interface_LoadItemIcon1(globalCtx, 0); + } + } + } + + gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = gSaveContext.buttonStatus[3] = + BTN_DISABLED; + Interface_ChangeAlpha(6); + } + + if (globalCtx->transitionMode != 0) { + Interface_ChangeAlpha(1); + } else if (gSaveContext.minigameState == 1) { + Interface_ChangeAlpha(8); + } else if (globalCtx->shootingGalleryStatus > 1) { + Interface_ChangeAlpha(8); + } else if ((globalCtx->sceneNum == SCENE_BOWLING) && Flags_GetSwitch(globalCtx, 0x38)) { + Interface_ChangeAlpha(8); + } else if (player->stateFlags1 & 0x00800000) { + Interface_ChangeAlpha(12); + } + } else { + if (player->stateFlags1 & 0x00800000) { + Interface_ChangeAlpha(12); + } + } + } else if (globalCtx->sceneNum == SCENE_KENJYANOMA) { + Interface_ChangeAlpha(1); + } else if (globalCtx->sceneNum == SCENE_TURIBORI) { + gSaveContext.unk_13E7 = 2; + if (globalCtx->interfaceCtx.unk_260 != 0) { + if (gSaveContext.equips.buttonItems[0] != ITEM_FISHING_POLE) { + gSaveContext.buttonStatus[0] = gSaveContext.equips.buttonItems[0]; + gSaveContext.equips.buttonItems[0] = ITEM_FISHING_POLE; + gSaveContext.unk_13EA = 0; + Interface_LoadItemIcon1(globalCtx, 0); + Interface_ChangeAlpha(12); + } + + if (gSaveContext.unk_13EA != 12) { + Interface_ChangeAlpha(12); + } + } else if (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + gSaveContext.unk_13EA = 0; + + if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) { + Interface_LoadItemIcon1(globalCtx, 0); + } + + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_DISABLED; + Interface_ChangeAlpha(50); + } else { + if (gSaveContext.buttonStatus[0] == BTN_ENABLED) { + gSaveContext.unk_13EA = 0; + } + + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_DISABLED; + Interface_ChangeAlpha(50); + } + } else if (msgCtx->msgMode == MSGMODE_NONE) { + if ((func_8008F2F8(globalCtx) >= 2) && (func_8008F2F8(globalCtx) < 5)) { + if (gSaveContext.buttonStatus[0] != BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[0] = BTN_DISABLED; + + for (i = 1; i < 4; i++) { + if (func_8008F2F8(globalCtx) == 2) { + if ((gSaveContext.equips.buttonItems[i] != ITEM_HOOKSHOT) && + (gSaveContext.equips.buttonItems[i] != ITEM_LONGSHOT)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } else { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } else { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } + } + + if (sp28) { + gSaveContext.unk_13EA = 0; + } + + Interface_ChangeAlpha(50); + } else if ((player->stateFlags1 & 0x00200000) || (player->stateFlags2 & 0x00040000)) { + if (gSaveContext.buttonStatus[0] != BTN_DISABLED) { + gSaveContext.buttonStatus[0] = BTN_DISABLED; + gSaveContext.buttonStatus[1] = BTN_DISABLED; + gSaveContext.buttonStatus[2] = BTN_DISABLED; + gSaveContext.buttonStatus[3] = BTN_DISABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + } + } else if ((gSaveContext.eventInf[0] & 0xF) == 1) { + if (player->stateFlags1 & 0x00800000) { + if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) && + (gSaveContext.equips.buttonItems[0] != ITEM_BOW)) { + if (gSaveContext.inventory.items[SLOT_BOW] == ITEM_NONE) { + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + } else { + gSaveContext.equips.buttonItems[0] = ITEM_BOW; + sp28 = 1; + } + } + } else { + sp28 = 1; + + if ((gSaveContext.equips.buttonItems[0] == ITEM_NONE) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW)) { + + if ((gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_MASTER) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_BGS) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KNIFE)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + } else { + gSaveContext.buttonStatus[0] = gSaveContext.equips.buttonItems[0]; + } + } + if (1) {} // Necessary to match + } + + if (sp28) { + Interface_LoadItemIcon1(globalCtx, 0); + sp28 = 0; + } + + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] != ITEM_OCARINA_FAIRY) && + (gSaveContext.equips.buttonItems[i] != ITEM_OCARINA_TIME)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } else { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + + if (sp28) { + gSaveContext.unk_13EA = 0; + } + + Interface_ChangeAlpha(50); + } else { + if (interfaceCtx->restrictions.bButton == 0) { + if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || + (gSaveContext.equips.buttonItems[0] == ITEM_NONE)) { + if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + sp28 = 1; + + if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) { + Interface_LoadItemIcon1(globalCtx, 0); + } + } + } else if ((gSaveContext.buttonStatus[0] & 0xFF) == BTN_DISABLED) { + sp28 = 1; + + if (((gSaveContext.buttonStatus[0] & 0xFF) == BTN_DISABLED) || + ((gSaveContext.buttonStatus[0] & 0xFF) == BTN_ENABLED)) { + gSaveContext.buttonStatus[0] = BTN_ENABLED; + } else { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0] & 0xFF; + } + } + } else if (interfaceCtx->restrictions.bButton == 1) { + if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || + (gSaveContext.equips.buttonItems[0] == ITEM_NONE)) { + if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + sp28 = 1; + + if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) { + Interface_LoadItemIcon1(globalCtx, 0); + } + } + } else { + if (gSaveContext.buttonStatus[0] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[0] = BTN_DISABLED; + } + } + + if (interfaceCtx->restrictions.bottles != 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) && + (gSaveContext.equips.buttonItems[i] <= ITEM_POE)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } + } + } else if (interfaceCtx->restrictions.bottles == 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) && + (gSaveContext.equips.buttonItems[i] <= ITEM_POE)) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + + if (interfaceCtx->restrictions.tradeItems != 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } + } + } else if (interfaceCtx->restrictions.tradeItems == 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK)) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + + if (interfaceCtx->restrictions.hookshot != 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] == ITEM_HOOKSHOT) || + (gSaveContext.equips.buttonItems[i] == ITEM_LONGSHOT)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } + } + } else if (interfaceCtx->restrictions.hookshot == 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] == ITEM_HOOKSHOT) || + (gSaveContext.equips.buttonItems[i] == ITEM_LONGSHOT)) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + + if (interfaceCtx->restrictions.ocarina != 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] == ITEM_OCARINA_FAIRY) || + (gSaveContext.equips.buttonItems[i] == ITEM_OCARINA_TIME)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } + } + } else if (interfaceCtx->restrictions.ocarina == 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] == ITEM_OCARINA_FAIRY) || + (gSaveContext.equips.buttonItems[i] == ITEM_OCARINA_TIME)) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + + if (interfaceCtx->restrictions.farores != 0) { + for (i = 1; i < 4; i++) { + if (gSaveContext.equips.buttonItems[i] == ITEM_FARORES_WIND) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + osSyncPrintf("***(i=%d)*** ", i); + } + } + } else if (interfaceCtx->restrictions.farores == 0) { + for (i = 1; i < 4; i++) { + if (gSaveContext.equips.buttonItems[i] == ITEM_FARORES_WIND) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + + if (interfaceCtx->restrictions.dinsNayrus != 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] == ITEM_DINS_FIRE) || + (gSaveContext.equips.buttonItems[i] == ITEM_NAYRUS_LOVE)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } + } + } else if (interfaceCtx->restrictions.dinsNayrus == 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] == ITEM_DINS_FIRE) || + (gSaveContext.equips.buttonItems[i] == ITEM_NAYRUS_LOVE)) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + + if (interfaceCtx->restrictions.all != 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] != ITEM_OCARINA_FAIRY) && + (gSaveContext.equips.buttonItems[i] != ITEM_OCARINA_TIME) && + !((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) && + (gSaveContext.equips.buttonItems[i] <= ITEM_POE)) && + !((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) { + if ((globalCtx->sceneNum != SCENE_TAKARAYA) || + (gSaveContext.equips.buttonItems[i] != ITEM_LENS)) { + if (gSaveContext.buttonStatus[i] == BTN_ENABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_DISABLED; + } else { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + } else if (interfaceCtx->restrictions.all == 0) { + for (i = 1; i < 4; i++) { + if ((gSaveContext.equips.buttonItems[i] != ITEM_DINS_FIRE) && + (gSaveContext.equips.buttonItems[i] != ITEM_HOOKSHOT) && + (gSaveContext.equips.buttonItems[i] != ITEM_LONGSHOT) && + (gSaveContext.equips.buttonItems[i] != ITEM_FARORES_WIND) && + (gSaveContext.equips.buttonItems[i] != ITEM_NAYRUS_LOVE) && + (gSaveContext.equips.buttonItems[i] != ITEM_OCARINA_FAIRY) && + (gSaveContext.equips.buttonItems[i] != ITEM_OCARINA_TIME) && + !((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) && + (gSaveContext.equips.buttonItems[i] <= ITEM_POE)) && + !((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) { + if (gSaveContext.buttonStatus[i] == BTN_DISABLED) { + sp28 = 1; + } + + gSaveContext.buttonStatus[i] = BTN_ENABLED; + } + } + } + } + } + } + + if (sp28) { + gSaveContext.unk_13EA = 0; + if ((globalCtx->sceneLoadFlag == 0) && (globalCtx->transitionMode == 0)) { + Interface_ChangeAlpha(50); + osSyncPrintf("???????? alpha_change( 50 ); ?????\n"); + } else { + osSyncPrintf("game_play->fade_direction || game_play->fbdemo_wipe_modem"); + } + } +} + +void Interface_SetSceneRestrictions(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 i; + u8 currentScene; + + // clang-format off + interfaceCtx->restrictions.hGauge = interfaceCtx->restrictions.bButton = + interfaceCtx->restrictions.aButton = interfaceCtx->restrictions.bottles = + interfaceCtx->restrictions.tradeItems = interfaceCtx->restrictions.hookshot = + interfaceCtx->restrictions.ocarina = interfaceCtx->restrictions.warpSongs = + interfaceCtx->restrictions.sunsSong = interfaceCtx->restrictions.farores = + interfaceCtx->restrictions.dinsNayrus = interfaceCtx->restrictions.all = 0; + // clang-format on + + i = 0; + + // "Data settings related to button display scene_data_ID=%d\n" + osSyncPrintf("ボタン表示関係データ設定 scene_data_ID=%d\n", globalCtx->sceneNum); + + do { + currentScene = (u8)globalCtx->sceneNum; + if (sRestrictionFlags[i].scene == currentScene) { + interfaceCtx->restrictions.hGauge = (sRestrictionFlags[i].flags1 & 0xC0) >> 6; + interfaceCtx->restrictions.bButton = (sRestrictionFlags[i].flags1 & 0x30) >> 4; + interfaceCtx->restrictions.aButton = (sRestrictionFlags[i].flags1 & 0x0C) >> 2; + interfaceCtx->restrictions.bottles = (sRestrictionFlags[i].flags1 & 0x03) >> 0; + interfaceCtx->restrictions.tradeItems = (sRestrictionFlags[i].flags2 & 0xC0) >> 6; + interfaceCtx->restrictions.hookshot = (sRestrictionFlags[i].flags2 & 0x30) >> 4; + interfaceCtx->restrictions.ocarina = (sRestrictionFlags[i].flags2 & 0x0C) >> 2; + interfaceCtx->restrictions.warpSongs = (sRestrictionFlags[i].flags2 & 0x03) >> 0; + interfaceCtx->restrictions.sunsSong = (sRestrictionFlags[i].flags3 & 0xC0) >> 6; + interfaceCtx->restrictions.farores = (sRestrictionFlags[i].flags3 & 0x30) >> 4; + interfaceCtx->restrictions.dinsNayrus = (sRestrictionFlags[i].flags3 & 0x0C) >> 2; + interfaceCtx->restrictions.all = (sRestrictionFlags[i].flags3 & 0x03) >> 0; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("parameter->button_status = %x,%x,%x\n", sRestrictionFlags[i].flags1, + sRestrictionFlags[i].flags2, sRestrictionFlags[i].flags3); + osSyncPrintf("h_gage=%d, b_button=%d, a_button=%d, c_bottle=%d\n", interfaceCtx->restrictions.hGauge, + interfaceCtx->restrictions.bButton, interfaceCtx->restrictions.aButton, + interfaceCtx->restrictions.bottles); + osSyncPrintf("c_warasibe=%d, c_hook=%d, c_ocarina=%d, c_warp=%d\n", interfaceCtx->restrictions.tradeItems, + interfaceCtx->restrictions.hookshot, interfaceCtx->restrictions.ocarina, + interfaceCtx->restrictions.warpSongs); + osSyncPrintf("c_sunmoon=%d, m_wind=%d, m_magic=%d, another=%d\n", interfaceCtx->restrictions.sunsSong, + interfaceCtx->restrictions.farores, interfaceCtx->restrictions.dinsNayrus, + interfaceCtx->restrictions.all); + osSyncPrintf(VT_RST); + return; + } + i++; + } while (sRestrictionFlags[i].scene != 0xFF); +} + +Gfx* Gfx_TextureIA8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock(displayListHead++, texture, G_IM_FMT_IA, G_IM_SIZ_8b, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + gDPLoadTextureBlock(displayListHead++, texture, G_IM_FMT_I, G_IM_SIZ_8b, textureWidth, textureHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPWideTextureRectangle(displayListHead++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, + (rectTop + rectHeight) << 2, G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + return displayListHead; +} + +void Inventory_SwapAgeEquipment(void) { + s16 i; + u16 temp; + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + for (i = 0; i < 4; i++) { + if (i != 0) { + gSaveContext.childEquips.buttonItems[i] = gSaveContext.equips.buttonItems[i]; + } else { + gSaveContext.childEquips.buttonItems[i] = ITEM_SWORD_KOKIRI; + } + + if (i != 0) { + gSaveContext.childEquips.cButtonSlots[i - 1] = gSaveContext.equips.cButtonSlots[i - 1]; + } + } + + gSaveContext.childEquips.equipment = gSaveContext.equips.equipment; + + if (gSaveContext.adultEquips.buttonItems[0] == ITEM_NONE) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; + + if (gSaveContext.inventory.items[SLOT_NUT] != ITEM_NONE) { + gSaveContext.equips.buttonItems[1] = ITEM_NUT; + gSaveContext.equips.cButtonSlots[0] = SLOT_NUT; + } else { + gSaveContext.equips.buttonItems[1] = gSaveContext.equips.cButtonSlots[0] = ITEM_NONE; + } + + gSaveContext.equips.buttonItems[2] = ITEM_BOMB; + gSaveContext.equips.buttonItems[3] = gSaveContext.inventory.items[SLOT_OCARINA]; + gSaveContext.equips.cButtonSlots[1] = SLOT_BOMB; + gSaveContext.equips.cButtonSlots[2] = SLOT_OCARINA; + gSaveContext.equips.equipment = 0x1122; + } else { + for (i = 0; i < 4; i++) { + gSaveContext.equips.buttonItems[i] = gSaveContext.adultEquips.buttonItems[i]; + + if (i != 0) { + gSaveContext.equips.cButtonSlots[i - 1] = gSaveContext.adultEquips.cButtonSlots[i - 1]; + } + + if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) && + (gSaveContext.equips.buttonItems[i] <= ITEM_POE)) || + ((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) { + osSyncPrintf("Register_Item_Pt(%d)=%d\n", i, gSaveContext.equips.cButtonSlots[i - 1]); + gSaveContext.equips.buttonItems[i] = + gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]]; + } + } + + gSaveContext.equips.equipment = gSaveContext.adultEquips.equipment; + } + } else { + for (i = 0; i < 4; i++) { + gSaveContext.adultEquips.buttonItems[i] = gSaveContext.equips.buttonItems[i]; + + if (i != 0) { + gSaveContext.adultEquips.cButtonSlots[i - 1] = gSaveContext.equips.cButtonSlots[i - 1]; + } + } + + gSaveContext.adultEquips.equipment = gSaveContext.equips.equipment; + + if (gSaveContext.childEquips.buttonItems[0] != ITEM_NONE) { + for (i = 0; i < 4; i++) { + gSaveContext.equips.buttonItems[i] = gSaveContext.childEquips.buttonItems[i]; + + if (i != 0) { + gSaveContext.equips.cButtonSlots[i - 1] = gSaveContext.childEquips.cButtonSlots[i - 1]; + } + + if (((gSaveContext.equips.buttonItems[i] >= ITEM_BOTTLE) && + (gSaveContext.equips.buttonItems[i] <= ITEM_POE)) || + ((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) { + osSyncPrintf("Register_Item_Pt(%d)=%d\n", i, gSaveContext.equips.cButtonSlots[i - 1]); + gSaveContext.equips.buttonItems[i] = + gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]]; + } + } + + gSaveContext.equips.equipment = gSaveContext.childEquips.equipment; + gSaveContext.equips.equipment &= 0xFFF0; + gSaveContext.equips.equipment |= 0x0001; + } + } + + temp = gEquipMasks[EQUIP_SHIELD] & gSaveContext.equips.equipment; + if (temp != 0) { + temp >>= gEquipShifts[EQUIP_SHIELD]; + if (!(gBitFlags[temp + 3] & gSaveContext.inventory.equipment)) { + gSaveContext.equips.equipment &= gEquipNegMasks[EQUIP_SHIELD]; + } + } +} + +void Interface_InitHorsebackArchery(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + gSaveContext.minigameState = 1; + interfaceCtx->unk_23C = interfaceCtx->unk_240 = interfaceCtx->unk_242 = 0; + gSaveContext.minigameScore = sHBAScoreTier = 0; + interfaceCtx->hbaAmmo = 20; +} + +void func_800849EC(GlobalContext* globalCtx) { + gSaveContext.inventory.equipment |= gBitFlags[2] << gEquipShifts[EQUIP_SWORD]; + gSaveContext.inventory.equipment ^= 8 << gEquipShifts[EQUIP_SWORD]; + + if (gBitFlags[3] & gSaveContext.inventory.equipment) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KNIFE; + } else { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + } + + Interface_LoadItemIcon1(globalCtx, 0); +} + +void Interface_LoadItemIcon1(GlobalContext* globalCtx, u16 button) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_160, interfaceCtx->iconItemSegment + button * 0x1000, + (uintptr_t)_icon_item_staticSegmentRomStart + (gSaveContext.equips.buttonItems[button] * 0x1000), + 0x1000, 0, &interfaceCtx->loadQueue, NULL, "../z_parameter.c", 1171); + osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); +} + +void Interface_LoadItemIcon2(GlobalContext* globalCtx, u16 button) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_180, interfaceCtx->iconItemSegment + button * 0x1000, + (uintptr_t)_icon_item_staticSegmentRomStart + (gSaveContext.equips.buttonItems[button] * 0x1000), + 0x1000, 0, &interfaceCtx->loadQueue, NULL, "../z_parameter.c", 1193); + osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); +} + +void func_80084BF4(GlobalContext* globalCtx, u16 flag) { + if (flag) { + if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || + (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE) || + (gSaveContext.buttonStatus[0] == BTN_DISABLED)) { + if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || + (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + Interface_LoadItemIcon1(globalCtx, 0); + } + } else if (gSaveContext.equips.buttonItems[0] == ITEM_NONE) { + if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + Interface_LoadItemIcon1(globalCtx, 0); + } + } + + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + Interface_ChangeAlpha(7); + } else { + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + func_80083108(globalCtx); + } +} + +u8 Item_Give(GlobalContext* globalCtx, u8 item) { + static s16 sAmmoRefillCounts[] = { 5, 10, 20, 30, 5, 10, 30, 0, 5, 20, 1, 5, 20, 50, 200, 10 }; + s16 i; + s16 slot; + s16 temp; + + slot = SLOT(item); + if (item >= ITEM_STICKS_5) { + slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]); + } + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("item_get_setting=%d pt=%d z=%x\n", item, slot, gSaveContext.inventory.items[slot]); + osSyncPrintf(VT_RST); + + if ((item >= ITEM_MEDALLION_FOREST) && (item <= ITEM_MEDALLION_LIGHT)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_MEDALLION_FOREST + QUEST_MEDALLION_FOREST]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("封印 = %x\n", gSaveContext.inventory.questItems); // "Seals = %x" + osSyncPrintf(VT_RST); + + if (item == ITEM_MEDALLION_WATER) { + func_8006D0AC(globalCtx); + } + + return ITEM_NONE; + } else if ((item >= ITEM_SONG_MINUET) && (item <= ITEM_SONG_STORMS)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("楽譜 = %x\n", gSaveContext.inventory.questItems); // "Musical scores = %x" + // "Musical scores = %x (%x) (%x)" + osSyncPrintf("楽譜 = %x (%x) (%x)\n", gSaveContext.inventory.questItems, + gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET], gBitFlags[item - ITEM_SONG_MINUET]); + osSyncPrintf(VT_RST); + + return ITEM_NONE; + } else if ((item >= ITEM_KOKIRI_EMERALD) && (item <= ITEM_ZORA_SAPPHIRE)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_KOKIRI_EMERALD + QUEST_KOKIRI_EMERALD]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("精霊石 = %x\n", gSaveContext.inventory.questItems); // "Spiritual Stones = %x" + osSyncPrintf(VT_RST); + + return ITEM_NONE; + } else if ((item == ITEM_STONE_OF_AGONY) || (item == ITEM_GERUDO_CARD)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_STONE_OF_AGONY + QUEST_STONE_OF_AGONY]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("アイテム = %x\n", gSaveContext.inventory.questItems); // "Items = %x" + osSyncPrintf(VT_RST); + + return ITEM_NONE; + } else if (item == ITEM_SKULL_TOKEN) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SKULL_TOKEN + QUEST_SKULL_TOKEN]; + gSaveContext.inventory.gsTokens++; + + osSyncPrintf(VT_FGCOL(YELLOW)); + // "N Coins = %x(%d)" + osSyncPrintf("Nコイン = %x(%d)\n", gSaveContext.inventory.questItems, gSaveContext.inventory.gsTokens); + osSyncPrintf(VT_RST); + + return ITEM_NONE; + } else if ((item >= ITEM_SWORD_KOKIRI) && (item <= ITEM_SWORD_BGS)) { + gSaveContext.inventory.equipment |= gBitFlags[item - ITEM_SWORD_KOKIRI] << gEquipShifts[EQUIP_SWORD]; + + if (item == ITEM_SWORD_BGS) { + gSaveContext.swordHealth = 8; + + if (ALL_EQUIP_VALUE(EQUIP_SWORD) == 0xF) { + gSaveContext.inventory.equipment ^= 8 << gEquipShifts[EQUIP_SWORD]; + if (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KNIFE) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + Interface_LoadItemIcon1(globalCtx, 0); + } + } + } else if (item == ITEM_SWORD_MASTER) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; + gSaveContext.equips.equipment &= 0xFFF0; + gSaveContext.equips.equipment |= 0x0002; + Interface_LoadItemIcon1(globalCtx, 0); + } + + return ITEM_NONE; + } else if ((item >= ITEM_SHIELD_DEKU) && (item <= ITEM_SHIELD_MIRROR)) { + gSaveContext.inventory.equipment |= (gBitFlags[item - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_SHIELD]); + return ITEM_NONE; + } else if ((item >= ITEM_TUNIC_KOKIRI) && (item <= ITEM_TUNIC_ZORA)) { + gSaveContext.inventory.equipment |= (gBitFlags[item - ITEM_TUNIC_KOKIRI] << gEquipShifts[EQUIP_TUNIC]); + return ITEM_NONE; + } else if ((item >= ITEM_BOOTS_KOKIRI) && (item <= ITEM_BOOTS_HOVER)) { + gSaveContext.inventory.equipment |= (gBitFlags[item - ITEM_BOOTS_KOKIRI] << gEquipShifts[EQUIP_BOOTS]); + return ITEM_NONE; + } else if ((item == ITEM_KEY_BOSS) || (item == ITEM_COMPASS) || (item == ITEM_DUNGEON_MAP)) { + gSaveContext.inventory.dungeonItems[gSaveContext.mapIndex] |= gBitFlags[item - ITEM_KEY_BOSS]; + return ITEM_NONE; + } else if (item == ITEM_KEY_SMALL) { + if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] < 0) { + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = 1; + return ITEM_NONE; + } else { + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]++; + return ITEM_NONE; + } + } else if ((item == ITEM_QUIVER_30) || (item == ITEM_BOW)) { + if (CUR_UPG_VALUE(UPG_QUIVER) == 0) { + Inventory_ChangeUpgrade(UPG_QUIVER, 1); + INV_CONTENT(ITEM_BOW) = ITEM_BOW; + AMMO(ITEM_BOW) = CAPACITY(UPG_QUIVER, 1); + return ITEM_NONE; + } else { + AMMO(ITEM_BOW)++; + if (AMMO(ITEM_BOW) > CUR_CAPACITY(UPG_QUIVER)) { + AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); + } + } + } else if (item == ITEM_QUIVER_40) { + Inventory_ChangeUpgrade(UPG_QUIVER, 2); + AMMO(ITEM_BOW) = CAPACITY(UPG_QUIVER, 2); + return ITEM_NONE; + } else if (item == ITEM_QUIVER_50) { + Inventory_ChangeUpgrade(UPG_QUIVER, 3); + AMMO(ITEM_BOW) = CAPACITY(UPG_QUIVER, 3); + return ITEM_NONE; + } else if (item == ITEM_BULLET_BAG_40) { + Inventory_ChangeUpgrade(UPG_BULLET_BAG, 2); + AMMO(ITEM_SLINGSHOT) = CAPACITY(UPG_BULLET_BAG, 2); + return ITEM_NONE; + } else if (item == ITEM_BULLET_BAG_50) { + Inventory_ChangeUpgrade(UPG_BULLET_BAG, 3); + AMMO(ITEM_SLINGSHOT) = CAPACITY(UPG_BULLET_BAG, 3); + return ITEM_NONE; + } else if (item == ITEM_BOMB_BAG_20) { + if (CUR_UPG_VALUE(UPG_BOMB_BAG) == 0) { + Inventory_ChangeUpgrade(UPG_BOMB_BAG, 1); + INV_CONTENT(ITEM_BOMB) = ITEM_BOMB; + AMMO(ITEM_BOMB) = CAPACITY(UPG_BOMB_BAG, 1); + return ITEM_NONE; + } else { + AMMO(ITEM_BOMB)++; + if (AMMO(ITEM_BOMB) > CUR_CAPACITY(UPG_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } + } + } else if (item == ITEM_BOMB_BAG_30) { + Inventory_ChangeUpgrade(UPG_BOMB_BAG, 2); + AMMO(ITEM_BOMB) = CAPACITY(UPG_BOMB_BAG, 2); + return ITEM_NONE; + } else if (item == ITEM_BOMB_BAG_40) { + Inventory_ChangeUpgrade(UPG_BOMB_BAG, 3); + AMMO(ITEM_BOMB) = CAPACITY(UPG_BOMB_BAG, 3); + return ITEM_NONE; + } else if (item == ITEM_BRACELET) { + Inventory_ChangeUpgrade(UPG_STRENGTH, 1); + return ITEM_NONE; + } else if (item == ITEM_GAUNTLETS_SILVER) { + Inventory_ChangeUpgrade(UPG_STRENGTH, 2); + return ITEM_NONE; + } else if (item == ITEM_GAUNTLETS_GOLD) { + Inventory_ChangeUpgrade(UPG_STRENGTH, 3); + return ITEM_NONE; + } else if (item == ITEM_SCALE_SILVER) { + Inventory_ChangeUpgrade(UPG_SCALE, 1); + return ITEM_NONE; + } else if (item == ITEM_SCALE_GOLDEN) { + Inventory_ChangeUpgrade(UPG_SCALE, 2); + return ITEM_NONE; + } else if (item == ITEM_WALLET_ADULT) { + Inventory_ChangeUpgrade(UPG_WALLET, 1); + return ITEM_NONE; + } else if (item == ITEM_WALLET_GIANT) { + Inventory_ChangeUpgrade(UPG_WALLET, 2); + return ITEM_NONE; + } else if (item == ITEM_STICK_UPGRADE_20) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(ITEM_STICK) = ITEM_STICK; + } + Inventory_ChangeUpgrade(UPG_STICKS, 2); + AMMO(ITEM_STICK) = CAPACITY(UPG_STICKS, 2); + return ITEM_NONE; + } else if (item == ITEM_STICK_UPGRADE_30) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(ITEM_STICK) = ITEM_STICK; + } + Inventory_ChangeUpgrade(UPG_STICKS, 3); + AMMO(ITEM_STICK) = CAPACITY(UPG_STICKS, 3); + return ITEM_NONE; + } else if (item == ITEM_NUT_UPGRADE_30) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(ITEM_NUT) = ITEM_NUT; + } + Inventory_ChangeUpgrade(UPG_NUTS, 2); + AMMO(ITEM_NUT) = CAPACITY(UPG_NUTS, 2); + return ITEM_NONE; + } else if (item == ITEM_NUT_UPGRADE_40) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(ITEM_NUT) = ITEM_NUT; + } + Inventory_ChangeUpgrade(UPG_NUTS, 3); + AMMO(ITEM_NUT) = CAPACITY(UPG_NUTS, 3); + return ITEM_NONE; + } else if (item == ITEM_LONGSHOT) { + INV_CONTENT(item) = item; + for (i = 1; i < 4; i++) { + if (gSaveContext.equips.buttonItems[i] == ITEM_HOOKSHOT) { + gSaveContext.equips.buttonItems[i] = ITEM_LONGSHOT; + Interface_LoadItemIcon1(globalCtx, i); + } + } + return ITEM_NONE; + } else if (item == ITEM_STICK) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + Inventory_ChangeUpgrade(UPG_STICKS, 1); + AMMO(ITEM_STICK) = 1; + } else { + AMMO(ITEM_STICK)++; + if (AMMO(ITEM_STICK) > CUR_CAPACITY(UPG_STICKS)) { + AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); + } + } + } else if ((item == ITEM_STICKS_5) || (item == ITEM_STICKS_10)) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + Inventory_ChangeUpgrade(UPG_STICKS, 1); + AMMO(ITEM_STICK) = sAmmoRefillCounts[item - ITEM_STICKS_5]; + } else { + AMMO(ITEM_STICK) += sAmmoRefillCounts[item - ITEM_STICKS_5]; + if (AMMO(ITEM_STICK) > CUR_CAPACITY(UPG_STICKS)) { + AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); + } + } + item = ITEM_STICK; + } else if (item == ITEM_NUT) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + Inventory_ChangeUpgrade(UPG_NUTS, 1); + AMMO(ITEM_NUT) = ITEM_NUT; + } else { + AMMO(ITEM_NUT)++; + if (AMMO(ITEM_NUT) > CUR_CAPACITY(UPG_NUTS)) { + AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); + } + } + } else if ((item == ITEM_NUTS_5) || (item == ITEM_NUTS_10)) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + Inventory_ChangeUpgrade(UPG_NUTS, 1); + AMMO(ITEM_NUT) += sAmmoRefillCounts[item - ITEM_NUTS_5]; + // "Deku Nuts %d(%d)=%d BS_count=%d" + osSyncPrintf("デクの実 %d(%d)=%d BS_count=%d\n", item, ITEM_NUTS_5, item - ITEM_NUTS_5, + sAmmoRefillCounts[item - ITEM_NUTS_5]); + } else { + AMMO(ITEM_NUT) += sAmmoRefillCounts[item - ITEM_NUTS_5]; + if (AMMO(ITEM_NUT) > CUR_CAPACITY(UPG_NUTS)) { + AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); + } + } + item = ITEM_NUT; + } else if (item == ITEM_BOMB) { + // "Bomb Bomb Bomb Bomb Bomb Bomb Bomb" + osSyncPrintf(" 爆弾 爆弾 爆弾 爆弾 爆弾 爆弾 爆弾 \n"); + if ((AMMO(ITEM_BOMB) += 1) > CUR_CAPACITY(UPG_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } + return ITEM_NONE; + } else if ((item >= ITEM_BOMBS_5) && (item <= ITEM_BOMBS_30)) { + if ((AMMO(ITEM_BOMB) += sAmmoRefillCounts[item - ITEM_BOMBS_5]) > CUR_CAPACITY(UPG_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } + return ITEM_NONE; + } else if (item == ITEM_BOMBCHU) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + AMMO(ITEM_BOMBCHU) = 10; + return ITEM_NONE; + } else { + AMMO(ITEM_BOMBCHU) += 10; + if (AMMO(ITEM_BOMBCHU) > 50) { + AMMO(ITEM_BOMBCHU) = 50; + } + return ITEM_NONE; + } + } else if ((item == ITEM_BOMBCHUS_5) || (item == ITEM_BOMBCHUS_20)) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(ITEM_BOMBCHU) = ITEM_BOMBCHU; + AMMO(ITEM_BOMBCHU) += sAmmoRefillCounts[item - ITEM_BOMBCHUS_5 + 8]; + return ITEM_NONE; + } else { + AMMO(ITEM_BOMBCHU) += sAmmoRefillCounts[item - ITEM_BOMBCHUS_5 + 8]; + if (AMMO(ITEM_BOMBCHU) > 50) { + AMMO(ITEM_BOMBCHU) = 50; + } + return ITEM_NONE; + } + } else if ((item >= ITEM_ARROWS_SMALL) && (item <= ITEM_ARROWS_LARGE)) { + AMMO(ITEM_BOW) += sAmmoRefillCounts[item - ITEM_ARROWS_SMALL + 4]; + + if ((AMMO(ITEM_BOW) >= CUR_CAPACITY(UPG_QUIVER)) || (AMMO(ITEM_BOW) < 0)) { + AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); + } + + osSyncPrintf("%d本 Item_MaxGet=%d\n", AMMO(ITEM_BOW), CUR_CAPACITY(UPG_QUIVER)); + + return ITEM_BOW; + } else if (item == ITEM_SLINGSHOT) { + Inventory_ChangeUpgrade(UPG_BULLET_BAG, 1); + INV_CONTENT(ITEM_SLINGSHOT) = ITEM_SLINGSHOT; + AMMO(ITEM_SLINGSHOT) = 30; + return ITEM_NONE; + } else if (item == ITEM_SEEDS) { + AMMO(ITEM_SLINGSHOT) += 5; + + if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { + AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); + } + + if (!(gSaveContext.itemGetInf[1] & 8)) { + gSaveContext.itemGetInf[1] |= 8; + return ITEM_NONE; + } + + return ITEM_SEEDS; + } else if (item == ITEM_SEEDS_30) { + AMMO(ITEM_SLINGSHOT) += 30; + + if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { + AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); + } + + if (!(gSaveContext.itemGetInf[1] & 8)) { + gSaveContext.itemGetInf[1] |= 8; + return ITEM_NONE; + } + + return ITEM_SEEDS; + } else if (item == ITEM_OCARINA_FAIRY) { + INV_CONTENT(ITEM_OCARINA_FAIRY) = ITEM_OCARINA_FAIRY; + return ITEM_NONE; + } else if (item == ITEM_OCARINA_TIME) { + INV_CONTENT(ITEM_OCARINA_TIME) = ITEM_OCARINA_TIME; + for (i = 1; i < 4; i++) { + if (gSaveContext.equips.buttonItems[i] == ITEM_OCARINA_FAIRY) { + gSaveContext.equips.buttonItems[i] = ITEM_OCARINA_TIME; + Interface_LoadItemIcon1(globalCtx, i); + } + } + return ITEM_NONE; + } else if (item == ITEM_BEAN) { + if (gSaveContext.inventory.items[slot] == ITEM_NONE) { + INV_CONTENT(item) = item; + AMMO(ITEM_BEAN) = 1; + BEANS_BOUGHT = 1; + } else { + AMMO(ITEM_BEAN)++; + BEANS_BOUGHT++; + } + return ITEM_NONE; + } else if ((item == ITEM_HEART_PIECE_2) || (item == ITEM_HEART_PIECE)) { + gSaveContext.inventory.questItems += 1 << (QUEST_HEART_PIECE + 4); + return ITEM_NONE; + } else if (item == ITEM_HEART_CONTAINER) { + gSaveContext.healthCapacity += 0x10; + gSaveContext.health += 0x10; + return ITEM_NONE; + } else if (item == ITEM_HEART) { + osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" + Health_ChangeBy(globalCtx, 0x10); + return item; + } else if (item == ITEM_MAGIC_SMALL) { + if (gSaveContext.unk_13F0 != 10) { + Magic_Fill(globalCtx); + } + + func_80087708(globalCtx, 12, 5); + + if (!(gSaveContext.infTable[25] & 0x100)) { + gSaveContext.infTable[25] |= 0x100; + return ITEM_NONE; + } + + return item; + } else if (item == ITEM_MAGIC_LARGE) { + if (gSaveContext.unk_13F0 != 10) { + Magic_Fill(globalCtx); + } + + func_80087708(globalCtx, 24, 5); + + if (!(gSaveContext.infTable[25] & 0x100)) { + gSaveContext.infTable[25] |= 0x100; + return ITEM_NONE; + } + + return item; + } else if ((item >= ITEM_RUPEE_GREEN) && (item <= ITEM_INVALID_8)) { + Rupees_ChangeBy(sAmmoRefillCounts[item - ITEM_RUPEE_GREEN + 10]); + return ITEM_NONE; + } else if (item == ITEM_BOTTLE) { + temp = SLOT(item); + + for (i = 0; i < 4; i++) { + if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) { + gSaveContext.inventory.items[temp + i] = item; + return ITEM_NONE; + } + } + } else if (((item >= ITEM_POTION_RED) && (item <= ITEM_POE)) || (item == ITEM_MILK)) { + temp = SLOT(item); + + if ((item != ITEM_MILK_BOTTLE) && (item != ITEM_LETTER_RUTO)) { + if (item == ITEM_MILK) { + item = ITEM_MILK_BOTTLE; + temp = SLOT(item); + } + + for (i = 0; i < 4; i++) { + if (gSaveContext.inventory.items[temp + i] == ITEM_BOTTLE) { + // "Item_Pt(1)=%d Item_Pt(2)=%d Item_Pt(3)=%d Empty Bottle=%d Content=%d" + osSyncPrintf("Item_Pt(1)=%d Item_Pt(2)=%d Item_Pt(3)=%d 空瓶=%d 中味=%d\n", + gSaveContext.equips.cButtonSlots[0], gSaveContext.equips.cButtonSlots[1], + gSaveContext.equips.cButtonSlots[2], temp + i, item); + + if ((temp + i) == gSaveContext.equips.cButtonSlots[0]) { + gSaveContext.equips.buttonItems[1] = item; + Interface_LoadItemIcon2(globalCtx, 1); + gSaveContext.buttonStatus[1] = BTN_ENABLED; + } else if ((temp + i) == gSaveContext.equips.cButtonSlots[1]) { + gSaveContext.equips.buttonItems[2] = item; + Interface_LoadItemIcon2(globalCtx, 2); + gSaveContext.buttonStatus[2] = BTN_ENABLED; + } else if ((temp + i) == gSaveContext.equips.cButtonSlots[2]) { + gSaveContext.equips.buttonItems[3] = item; + Interface_LoadItemIcon1(globalCtx, 3); + gSaveContext.buttonStatus[3] = BTN_ENABLED; + } + + gSaveContext.inventory.items[temp + i] = item; + return ITEM_NONE; + } + } + } else { + for (i = 0; i < 4; i++) { + if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) { + gSaveContext.inventory.items[temp + i] = item; + return ITEM_NONE; + } + } + } + } else if ((item >= ITEM_WEIRD_EGG) && (item <= ITEM_CLAIM_CHECK)) { + if (item == ITEM_SAW) { + gSaveContext.itemGetInf[1] |= 0x8000; + } + + temp = INV_CONTENT(item); + INV_CONTENT(item) = item; + + if (temp != ITEM_NONE) { + for (i = 1; i < 4; i++) { + if (temp == gSaveContext.equips.buttonItems[i]) { + if (item != ITEM_SOLD_OUT) { + gSaveContext.equips.buttonItems[i] = item; + Interface_LoadItemIcon1(globalCtx, i); + } else { + gSaveContext.equips.buttonItems[i] = ITEM_NONE; + } + return ITEM_NONE; + } + } + } + + return ITEM_NONE; + } + + temp = gSaveContext.inventory.items[slot]; + osSyncPrintf("Item_Register(%d)=%d %d\n", slot, item, temp); + INV_CONTENT(item) = item; + + return temp; +} + +u8 Item_CheckObtainability(u8 item) { + s16 i; + s16 slot = SLOT(item); + s32 temp; + + if (item >= ITEM_STICKS_5) { + slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]); + } + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("item_get_non_setting=%d pt=%d z=%x\n", item, slot, gSaveContext.inventory.items[slot]); + osSyncPrintf(VT_RST); + + if ((item >= ITEM_MEDALLION_FOREST) && (item <= ITEM_MEDALLION_LIGHT)) { + return ITEM_NONE; + } else if ((item >= ITEM_KOKIRI_EMERALD) && (item <= ITEM_SKULL_TOKEN)) { + return ITEM_NONE; + } else if ((item >= ITEM_SWORD_KOKIRI) && (item <= ITEM_SWORD_BGS)) { + if (item == ITEM_SWORD_BGS) { + return ITEM_NONE; + } else if ((gBitFlags[item - ITEM_SWORD_KOKIRI] << gEquipShifts[EQUIP_SWORD]) & + gSaveContext.inventory.equipment) { + return item; + } else { + return ITEM_NONE; + } + } else if ((item >= ITEM_SHIELD_DEKU) && (item <= ITEM_SHIELD_MIRROR)) { + if ((gBitFlags[item - ITEM_SHIELD_DEKU] << gEquipShifts[EQUIP_SHIELD]) & gSaveContext.inventory.equipment) { + return item; + } else { + return ITEM_NONE; + } + } else if ((item >= ITEM_TUNIC_KOKIRI) && (item <= ITEM_TUNIC_ZORA)) { + if ((gBitFlags[item - ITEM_TUNIC_KOKIRI] << gEquipShifts[EQUIP_TUNIC]) & gSaveContext.inventory.equipment) { + return item; + } else { + return ITEM_NONE; + } + } else if ((item >= ITEM_BOOTS_KOKIRI) && (item <= ITEM_BOOTS_HOVER)) { + if ((gBitFlags[item - ITEM_BOOTS_KOKIRI] << gEquipShifts[EQUIP_BOOTS]) & gSaveContext.inventory.equipment) { + return item; + } else { + return ITEM_NONE; + } + } else if ((item == ITEM_KEY_BOSS) || (item == ITEM_COMPASS) || (item == ITEM_DUNGEON_MAP)) { + return ITEM_NONE; + } else if (item == ITEM_KEY_SMALL) { + return ITEM_NONE; + } else if ((item >= ITEM_SLINGSHOT) && (item <= ITEM_BOMBCHU)) { + return ITEM_NONE; + } else if ((item == ITEM_BOMBCHUS_5) || (item == ITEM_BOMBCHUS_20)) { + return ITEM_NONE; + } else if ((item == ITEM_QUIVER_30) || (item == ITEM_BOW)) { + if (CUR_UPG_VALUE(UPG_QUIVER) == 0) { + return ITEM_NONE; + } else { + return 0; + } + } else if ((item == ITEM_QUIVER_40) || (item == ITEM_QUIVER_50)) { + return ITEM_NONE; + } else if ((item == ITEM_BULLET_BAG_40) || (item == ITEM_BULLET_BAG_50)) { + return ITEM_NONE; + } else if ((item == ITEM_BOMB_BAG_20) || (item == ITEM_BOMB)) { + if (CUR_UPG_VALUE(UPG_BOMB_BAG) == 0) { + return ITEM_NONE; + } else { + return 0; + } + } else if ((item >= ITEM_STICK_UPGRADE_20) && (item <= ITEM_NUT_UPGRADE_40)) { + return ITEM_NONE; + } else if ((item >= ITEM_BOMB_BAG_30) && (item <= ITEM_WALLET_GIANT)) { + return ITEM_NONE; + } else if (item == ITEM_LONGSHOT) { + return ITEM_NONE; + } else if ((item == ITEM_SEEDS) || (item == ITEM_SEEDS_30)) { + if (!(gSaveContext.itemGetInf[1] & 0x8)) { + return ITEM_NONE; + } else { + return ITEM_SEEDS; + } + } else if (item == ITEM_BEAN) { + return ITEM_NONE; + } else if ((item == ITEM_HEART_PIECE_2) || (item == ITEM_HEART_PIECE)) { + return ITEM_NONE; + } else if (item == ITEM_HEART_CONTAINER) { + return ITEM_NONE; + } else if (item == ITEM_HEART) { + return ITEM_HEART; + } else if ((item == ITEM_MAGIC_SMALL) || (item == ITEM_MAGIC_LARGE)) { + // "Magic Pot Get_Inf_Table( 25, 0x0100)=%d" + osSyncPrintf("魔法の壷 Get_Inf_Table( 25, 0x0100)=%d\n", gSaveContext.infTable[25] & 0x100); + if (!(gSaveContext.infTable[25] & 0x100)) { + return ITEM_NONE; + } else { + return item; + } + } else if ((item >= ITEM_RUPEE_GREEN) && (item <= ITEM_INVALID_8)) { + return ITEM_NONE; + } else if (item == ITEM_BOTTLE) { + return ITEM_NONE; + } else if (((item >= ITEM_POTION_RED) && (item <= ITEM_POE)) || (item == ITEM_MILK)) { + temp = SLOT(item); + + if ((item != ITEM_MILK_BOTTLE) && (item != ITEM_LETTER_RUTO)) { + if (item == ITEM_MILK) { + item = ITEM_MILK_BOTTLE; + temp = SLOT(item); + } + + for (i = 0; i < 4; i++) { + if (gSaveContext.inventory.items[temp + i] == ITEM_BOTTLE) { + return ITEM_NONE; + } + } + } else { + for (i = 0; i < 4; i++) { + if (gSaveContext.inventory.items[temp + i] == ITEM_NONE) { + return ITEM_NONE; + } + } + } + } else if ((item >= ITEM_WEIRD_EGG) && (item <= ITEM_CLAIM_CHECK)) { + return ITEM_NONE; + } + + return gSaveContext.inventory.items[slot]; +} + +void Inventory_DeleteItem(u16 item, u16 invSlot) { + s16 i; + + if (item == ITEM_BEAN) { + BEANS_BOUGHT = 0; + } + + gSaveContext.inventory.items[invSlot] = ITEM_NONE; + + osSyncPrintf("\nItem_Register(%d)\n", invSlot, gSaveContext.inventory.items[invSlot]); + + for (i = 1; i < 4; i++) { + if (gSaveContext.equips.buttonItems[i] == item) { + gSaveContext.equips.buttonItems[i] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE; + } + } +} + +s32 Inventory_ReplaceItem(GlobalContext* globalCtx, u16 oldItem, u16 newItem) { + s16 i; + + for (i = 0; i < ARRAY_COUNT(gSaveContext.inventory.items); i++) { + if (gSaveContext.inventory.items[i] == oldItem) { + gSaveContext.inventory.items[i] = newItem; + osSyncPrintf("アイテム消去(%d)\n", i); // "Item Purge (%d)" + for (i = 1; i < 4; i++) { + if (gSaveContext.equips.buttonItems[i] == oldItem) { + gSaveContext.equips.buttonItems[i] = newItem; + Interface_LoadItemIcon1(globalCtx, i); + break; + } + } + return 1; + } + } + + return 0; +} + +s32 Inventory_HasEmptyBottle(void) { + u8* items = gSaveContext.inventory.items; + + if (items[SLOT_BOTTLE_1] == ITEM_BOTTLE) { + return 1; + } else if (items[SLOT_BOTTLE_2] == ITEM_BOTTLE) { + return 1; + } else if (items[SLOT_BOTTLE_3] == ITEM_BOTTLE) { + return 1; + } else if (items[SLOT_BOTTLE_4] == ITEM_BOTTLE) { + return 1; + } else { + return 0; + } +} + +s32 Inventory_HasSpecificBottle(u8 bottleItem) { + u8* items = gSaveContext.inventory.items; + + if (items[SLOT_BOTTLE_1] == bottleItem) { + return 1; + } else if (items[SLOT_BOTTLE_2] == bottleItem) { + return 1; + } else if (items[SLOT_BOTTLE_3] == bottleItem) { + return 1; + } else if (items[SLOT_BOTTLE_4] == bottleItem) { + return 1; + } else { + return 0; + } +} + +void Inventory_UpdateBottleItem(GlobalContext* globalCtx, u8 item, u8 button) { + osSyncPrintf("item_no=%x, c_no=%x, Pt=%x Item_Register=%x\n", item, button, + gSaveContext.equips.cButtonSlots[button - 1], + gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[button - 1]]); + + // Special case to only empty half of a Lon Lon Milk Bottle + if ((gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[button - 1]] == ITEM_MILK_BOTTLE) && + (item == ITEM_BOTTLE)) { + item = ITEM_MILK_HALF; + } + + gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[button - 1]] = item; + gSaveContext.equips.buttonItems[button] = item; + + Interface_LoadItemIcon1(globalCtx, button); + + globalCtx->pauseCtx.cursorItem[PAUSE_ITEM] = item; + gSaveContext.buttonStatus[button] = BTN_ENABLED; +} + +s32 Inventory_ConsumeFairy(GlobalContext* globalCtx) { + s32 bottleSlot = SLOT(ITEM_FAIRY); + s16 i; + s16 j; + + for (i = 0; i < 4; i++) { + if (gSaveContext.inventory.items[bottleSlot + i] == ITEM_FAIRY) { + for (j = 1; j < 4; j++) { + if (gSaveContext.equips.buttonItems[j] == ITEM_FAIRY) { + gSaveContext.equips.buttonItems[j] = ITEM_BOTTLE; + Interface_LoadItemIcon1(globalCtx, j); + i = 0; + bottleSlot = gSaveContext.equips.cButtonSlots[j - 1]; + break; + } + } + osSyncPrintf("妖精使用=%d\n", bottleSlot); // "Fairy Usage=%d" + gSaveContext.inventory.items[bottleSlot + i] = ITEM_BOTTLE; + return 1; + } + } + + return 0; +} + +void func_80086D5C(s32* buf, u16 size) { + u16 i; + + //buf = ResourceMgr_LoadTexByName(buf); + + for (i = 0; i < size; i++) { + buf[i] = 0; + } +} + +void Interface_LoadActionLabel(InterfaceContext* interfaceCtx, u16 action, s16 loadOffset) { + static void* sDoActionTextures[] = { gAttackDoActionENGTex, gCheckDoActionENGTex }; + if (action >= DO_ACTION_MAX) { + action = DO_ACTION_NONE; + } + + char* doAction = actionsTbl[action]; + + char newName[512]; + if (gSaveContext.language != LANGUAGE_ENG) { + size_t length = strlen(doAction); + strcpy(newName, doAction); + if (gSaveContext.language == LANGUAGE_FRA) { + newName[length - 6] = 'F'; + newName[length - 5] = 'R'; + newName[length - 4] = 'A'; + } else if (gSaveContext.language == LANGUAGE_GER) { + newName[length - 6] = 'G'; + newName[length - 5] = 'E'; + newName[length - 4] = 'R'; + } + doAction = newName; + } + + /* + if (gSaveContext.language != LANGUAGE_ENG) { + action += DO_ACTION_MAX; + } + + if (gSaveContext.language == LANGUAGE_FRA) { + action += DO_ACTION_MAX; + }*/ + + + if (action != DO_ACTION_NONE) { + //osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + memcpy(interfaceCtx->doActionSegment + (loadOffset * DO_ACTION_TEX_SIZE), ResourceMgr_LoadTexByName(doAction), + DO_ACTION_TEX_SIZE); + //DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_160, + //interfaceCtx->doActionSegment + (loadOffset * DO_ACTION_TEX_SIZE), + //(uintptr_t)_do_action_staticSegmentRomStart + (action * DO_ACTION_TEX_SIZE), DO_ACTION_TEX_SIZE, + //0, &interfaceCtx->loadQueue, NULL, "../z_parameter.c", 2145); + //osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); + } else { + gSegments[7] = VIRTUAL_TO_PHYSICAL(interfaceCtx->doActionSegment); + //func_80086D5C(SEGMENTED_TO_VIRTUAL(sDoActionTextures[loadOffset]), DO_ACTION_TEX_SIZE / 4); + func_80086D5C(interfaceCtx->doActionSegment + (loadOffset * DO_ACTION_TEX_SIZE), DO_ACTION_TEX_SIZE / 4); + } +} + +void Interface_SetDoAction(GlobalContext* globalCtx, u16 action) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + + if (interfaceCtx->unk_1F0 != action) { + interfaceCtx->unk_1F0 = action; + interfaceCtx->unk_1EC = 1; + interfaceCtx->unk_1F4 = 0.0f; + Interface_LoadActionLabel(interfaceCtx, action, 1); + if (pauseCtx->state != 0) { + interfaceCtx->unk_1EC = 3; + } + } +} + +void Interface_SetNaviCall(GlobalContext* globalCtx, u16 naviCallState) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + if (((naviCallState == 0x1D) || (naviCallState == 0x1E)) && !interfaceCtx->naviCalling && + (globalCtx->csCtx.state == CS_STATE_IDLE)) { + // clang-format off + if (naviCallState == 0x1E) { Audio_PlaySoundGeneral(NA_SE_VO_NAVY_CALL, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); } + // clang-format on + + if (naviCallState == 0x1D) { + func_800F4524(&D_801333D4, NA_SE_VO_NA_HELLO_2, 32); + } + + interfaceCtx->naviCalling = 1; + sCUpInvisible = 0; + sCUpTimer = 10; + } else if ((naviCallState == 0x1F) && interfaceCtx->naviCalling) { + interfaceCtx->naviCalling = 0; + } +} + +void Interface_LoadActionLabelB(GlobalContext* globalCtx, u16 action) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + char* doAction = actionsTbl[action]; + char newName[512]; + + if (gSaveContext.language != LANGUAGE_ENG) { + size_t length = strlen(doAction); + strcpy(newName, doAction); + if (gSaveContext.language == LANGUAGE_FRA) { + newName[length - 6] = 'F'; + newName[length - 5] = 'R'; + newName[length - 4] = 'A'; + } else if (gSaveContext.language == LANGUAGE_GER) { + newName[length - 6] = 'G'; + newName[length - 5] = 'E'; + newName[length - 4] = 'R'; + } + doAction = newName; + } + + /*if (gSaveContext.language != LANGUAGE_ENG) { + action += DO_ACTION_MAX; + } + + if (gSaveContext.language == LANGUAGE_FRA) { + action += DO_ACTION_MAX; + }*/ + + interfaceCtx->unk_1FC = action; + + + + // OTRTODO + osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + memcpy(interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE, ResourceMgr_LoadTexByName(doAction), DO_ACTION_TEX_SIZE); + //DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_160, interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE, + //(uintptr_t)_do_action_staticSegmentRomStart + (action * DO_ACTION_TEX_SIZE), DO_ACTION_TEX_SIZE, 0, + //&interfaceCtx->loadQueue, NULL, "../z_parameter.c", 2228); + osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); + + interfaceCtx->unk_1FA = 1; +} + +s32 Health_ChangeBy(GlobalContext* globalCtx, s16 healthChange) { + u16 heartCount; + u16 healthLevel; + + // "***** Fluctuation=%d (now=%d, max=%d) ***" + osSyncPrintf("***** 増減=%d (now=%d, max=%d) ***", healthChange, gSaveContext.health, + gSaveContext.healthCapacity); + + // clang-format off + if (healthChange > 0) { Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + } else if ((gSaveContext.doubleDefense != 0) && (healthChange < 0)) { + healthChange >>= 1; + osSyncPrintf("ハート減少半分!!=%d\n", healthChange); // "Heart decrease halved!!=%d" + } + // clang-format on + + gSaveContext.health += healthChange; + + if (gSaveContext.health > gSaveContext.healthCapacity) { + gSaveContext.health = gSaveContext.healthCapacity; + } + + heartCount = gSaveContext.health % 0x10; + + healthLevel = heartCount; + if (heartCount != 0) { + if (heartCount > 10) { + healthLevel = 3; + } else if (heartCount > 5) { + healthLevel = 2; + } else { + healthLevel = 1; + } + } + + // "Life=%d *** %d ******" + osSyncPrintf(" ライフ=%d *** %d ******\n", gSaveContext.health, healthLevel); + + if (gSaveContext.health <= 0) { + gSaveContext.health = 0; + return 0; + } else { + return 1; + } +} + +void Health_GiveHearts(s16 hearts) { + gSaveContext.healthCapacity += hearts * 0x10; +} + +void Rupees_ChangeBy(s16 rupeeChange) { + gSaveContext.rupeeAccumulator += rupeeChange; +} + +void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { + // "Item = (%d) Amount = (%d + %d)" + osSyncPrintf("アイテム = (%d) 数 = (%d + %d) ", item, AMMO(item), ammoChange); + + if (item == ITEM_STICK) { + AMMO(ITEM_STICK) += ammoChange; + + if (AMMO(ITEM_STICK) >= CUR_CAPACITY(UPG_STICKS)) { + AMMO(ITEM_STICK) = CUR_CAPACITY(UPG_STICKS); + } else if (AMMO(ITEM_STICK) < 0) { + AMMO(ITEM_STICK) = 0; + } + } else if (item == ITEM_NUT) { + AMMO(ITEM_NUT) += ammoChange; + + if (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS)) { + AMMO(ITEM_NUT) = CUR_CAPACITY(UPG_NUTS); + } else if (AMMO(ITEM_NUT) < 0) { + AMMO(ITEM_NUT) = 0; + } + } else if (item == ITEM_BOMBCHU) { + AMMO(ITEM_BOMBCHU) += ammoChange; + + if (AMMO(ITEM_BOMBCHU) >= 50) { + AMMO(ITEM_BOMBCHU) = 50; + } else if (AMMO(ITEM_BOMBCHU) < 0) { + AMMO(ITEM_BOMBCHU) = 0; + } + } else if (item == ITEM_BOW) { + AMMO(ITEM_BOW) += ammoChange; + + if (AMMO(ITEM_BOW) >= CUR_CAPACITY(UPG_QUIVER)) { + AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); + } else if (AMMO(ITEM_BOW) < 0) { + AMMO(ITEM_BOW) = 0; + } + } else if ((item == ITEM_SLINGSHOT) || (item == ITEM_SEEDS)) { + AMMO(ITEM_SLINGSHOT) += ammoChange; + + if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { + AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); + } else if (AMMO(ITEM_SLINGSHOT) < 0) { + AMMO(ITEM_SLINGSHOT) = 0; + } + } else if (item == ITEM_BOMB) { + AMMO(ITEM_BOMB) += ammoChange; + + if (AMMO(ITEM_BOMB) >= CUR_CAPACITY(UPG_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } else if (AMMO(ITEM_BOMB) < 0) { + AMMO(ITEM_BOMB) = 0; + } + } else if (item == ITEM_BEAN) { + AMMO(ITEM_BEAN) += ammoChange; + } + + osSyncPrintf("合計 = (%d)\n", AMMO(item)); // "Total = (%d)" +} + +void Magic_Fill(GlobalContext* globalCtx) { + if (gSaveContext.magicAcquired) { + gSaveContext.unk_13F2 = gSaveContext.unk_13F0; + gSaveContext.unk_13F6 = (gSaveContext.doubleMagic * 0x30) + 0x30; + gSaveContext.unk_13F0 = 9; + } +} + +void func_800876C8(GlobalContext* globalCtx) { + if ((gSaveContext.unk_13F0 != 8) && (gSaveContext.unk_13F0 != 9)) { + if (gSaveContext.unk_13F0 == 10) { + gSaveContext.unk_13F2 = gSaveContext.unk_13F0; + } + gSaveContext.unk_13F0 = 5; + } +} + +s32 func_80087708(GlobalContext* globalCtx, s16 arg1, s16 arg2) { + if (!gSaveContext.magicAcquired) { + return 0; + } + + if ((arg2 != 5) && (gSaveContext.magic - arg1) < 0) { + if (gSaveContext.unk_13F4 != 0) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + return 0; + } + + switch (arg2) { + case 0: + case 2: + if ((gSaveContext.unk_13F0 == 0) || (gSaveContext.unk_13F0 == 7)) { + if (gSaveContext.unk_13F0 == 7) { + globalCtx->actorCtx.unk_03 = 0; + } + gSaveContext.unk_13F8 = gSaveContext.magic - arg1; + gSaveContext.unk_13F0 = 1; + return 1; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + return 0; + } + case 1: + if ((gSaveContext.unk_13F0 == 0) || (gSaveContext.unk_13F0 == 7)) { + if (gSaveContext.unk_13F0 == 7) { + globalCtx->actorCtx.unk_03 = 0; + } + gSaveContext.unk_13F8 = gSaveContext.magic - arg1; + gSaveContext.unk_13F0 = 6; + return 1; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + return 0; + } + case 3: + if (gSaveContext.unk_13F0 == 0) { + if (gSaveContext.magic != 0) { + globalCtx->interfaceCtx.unk_230 = 80; + gSaveContext.unk_13F0 = 7; + return 1; + } else { + return 0; + } + } else { + if (gSaveContext.unk_13F0 == 7) { + return 1; + } else { + return 0; + } + } + case 4: + if ((gSaveContext.unk_13F0 == 0) || (gSaveContext.unk_13F0 == 7)) { + if (gSaveContext.unk_13F0 == 7) { + globalCtx->actorCtx.unk_03 = 0; + } + gSaveContext.unk_13F8 = gSaveContext.magic - arg1; + gSaveContext.unk_13F0 = 4; + return 1; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + return 0; + } + case 5: + if (gSaveContext.unk_13F4 >= gSaveContext.magic) { + gSaveContext.unk_13F8 = gSaveContext.magic + arg1; + + if (gSaveContext.unk_13F8 >= gSaveContext.unk_13F4) { + gSaveContext.unk_13F8 = gSaveContext.unk_13F4; + } + + gSaveContext.unk_13F0 = 10; + return 1; + } + break; + } + + return 0; +} + +void Interface_UpdateMagicBar(GlobalContext* globalCtx) { + static s16 sMagicBorderColors[][3] = { + { 255, 255, 255 }, + { 150, 150, 150 }, + { 255, 255, 150 }, + { 255, 255, 50 }, + }; + static s16 sMagicBorderIndexes[] = { 0, 1, 1, 0 }; + static s16 sMagicBorderRatio = 2; + static s16 sMagicBorderStep = 1; + MessageContext* msgCtx = &globalCtx->msgCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 borderChangeR; + s16 borderChangeG; + s16 borderChangeB; + s16 temp; + + switch (gSaveContext.unk_13F0) { + case 8: + temp = gSaveContext.magicLevel * 0x30; + if (gSaveContext.unk_13F4 != temp) { + if (gSaveContext.unk_13F4 < temp) { + gSaveContext.unk_13F4 += 8; + if (gSaveContext.unk_13F4 > temp) { + gSaveContext.unk_13F4 = temp; + } + } else { + gSaveContext.unk_13F4 -= 8; + if (gSaveContext.unk_13F4 <= temp) { + gSaveContext.unk_13F4 = temp; + } + } + } else { + gSaveContext.unk_13F0 = 9; + } + break; + + case 9: + gSaveContext.magic += 4; + + if (gSaveContext.gameMode == 0 && gSaveContext.sceneSetupIndex < 4) { + Audio_PlaySoundGeneral(NA_SE_SY_GAUGE_UP - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + // "Storage MAGIC_NOW=%d (%d)" + osSyncPrintf("蓄電 MAGIC_NOW=%d (%d)\n", gSaveContext.magic, gSaveContext.unk_13F6); + if (gSaveContext.magic >= gSaveContext.unk_13F6) { + gSaveContext.magic = gSaveContext.unk_13F6; + gSaveContext.unk_13F0 = gSaveContext.unk_13F2; + gSaveContext.unk_13F2 = 0; + } + break; + + case 1: + sMagicBorderRatio = 2; + gSaveContext.unk_13F0 = 2; + break; + + case 2: + gSaveContext.magic -= 2; + if (gSaveContext.magic <= 0) { + gSaveContext.magic = 0; + gSaveContext.unk_13F0 = 3; + sMagicBorderR = sMagicBorderG = sMagicBorderB = 255; + } else if (gSaveContext.magic == gSaveContext.unk_13F8) { + gSaveContext.unk_13F0 = 3; + sMagicBorderR = sMagicBorderG = sMagicBorderB = 255; + } + case 3: + case 4: + case 6: + temp = sMagicBorderIndexes[sMagicBorderStep]; + borderChangeR = ABS(sMagicBorderR - sMagicBorderColors[temp][0]) / sMagicBorderRatio; + borderChangeG = ABS(sMagicBorderG - sMagicBorderColors[temp][1]) / sMagicBorderRatio; + borderChangeB = ABS(sMagicBorderB - sMagicBorderColors[temp][2]) / sMagicBorderRatio; + + if (sMagicBorderR >= sMagicBorderColors[temp][0]) { + sMagicBorderR -= borderChangeR; + } else { + sMagicBorderR += borderChangeR; + } + + if (sMagicBorderG >= sMagicBorderColors[temp][1]) { + sMagicBorderG -= borderChangeG; + } else { + sMagicBorderG += borderChangeG; + } + + if (sMagicBorderB >= sMagicBorderColors[temp][2]) { + sMagicBorderB -= borderChangeB; + } else { + sMagicBorderB += borderChangeB; + } + + sMagicBorderRatio--; + if (sMagicBorderRatio == 0) { + sMagicBorderR = sMagicBorderColors[temp][0]; + sMagicBorderG = sMagicBorderColors[temp][1]; + sMagicBorderB = sMagicBorderColors[temp][2]; + sMagicBorderRatio = YREG(40 + sMagicBorderStep); + sMagicBorderStep++; + if (sMagicBorderStep >= 4) { + sMagicBorderStep = 0; + } + } + break; + + case 5: + sMagicBorderR = sMagicBorderG = sMagicBorderB = 255; + gSaveContext.unk_13F0 = 0; + break; + + case 7: + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0) && + (msgCtx->msgMode == MSGMODE_NONE) && (globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE) && + (globalCtx->sceneLoadFlag == 0) && (globalCtx->transitionMode == 0) && !Gameplay_InCsMode(globalCtx)) { + if ((gSaveContext.magic == 0) || ((func_8008F2F8(globalCtx) >= 2) && (func_8008F2F8(globalCtx) < 5)) || + ((gSaveContext.equips.buttonItems[1] != ITEM_LENS) && + (gSaveContext.equips.buttonItems[2] != ITEM_LENS) && + (gSaveContext.equips.buttonItems[3] != ITEM_LENS)) || + (globalCtx->actorCtx.unk_03 == 0)) { + globalCtx->actorCtx.unk_03 = 0; + Audio_PlaySoundGeneral(NA_SE_SY_GLASSMODE_OFF, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + gSaveContext.unk_13F0 = 0; + sMagicBorderR = sMagicBorderG = sMagicBorderB = 255; + break; + } + + interfaceCtx->unk_230--; + if (interfaceCtx->unk_230 == 0) { + gSaveContext.magic--; + interfaceCtx->unk_230 = 80; + } + } + + temp = sMagicBorderIndexes[sMagicBorderStep]; + borderChangeR = ABS(sMagicBorderR - sMagicBorderColors[temp][0]) / sMagicBorderRatio; + borderChangeG = ABS(sMagicBorderG - sMagicBorderColors[temp][1]) / sMagicBorderRatio; + borderChangeB = ABS(sMagicBorderB - sMagicBorderColors[temp][2]) / sMagicBorderRatio; + + if (sMagicBorderR >= sMagicBorderColors[temp][0]) { + sMagicBorderR -= borderChangeR; + } else { + sMagicBorderR += borderChangeR; + } + + if (sMagicBorderG >= sMagicBorderColors[temp][1]) { + sMagicBorderG -= borderChangeG; + } else { + sMagicBorderG += borderChangeG; + } + + if (sMagicBorderB >= sMagicBorderColors[temp][2]) { + sMagicBorderB -= borderChangeB; + } else { + sMagicBorderB += borderChangeB; + } + + sMagicBorderRatio--; + if (sMagicBorderRatio == 0) { + sMagicBorderR = sMagicBorderColors[temp][0]; + sMagicBorderG = sMagicBorderColors[temp][1]; + sMagicBorderB = sMagicBorderColors[temp][2]; + sMagicBorderRatio = YREG(40 + sMagicBorderStep); + sMagicBorderStep++; + if (sMagicBorderStep >= 4) { + sMagicBorderStep = 0; + } + } + break; + + case 10: + gSaveContext.magic += 4; + Audio_PlaySoundGeneral(NA_SE_SY_GAUGE_UP - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (gSaveContext.magic >= gSaveContext.unk_13F8) { + gSaveContext.magic = gSaveContext.unk_13F8; + gSaveContext.unk_13F0 = gSaveContext.unk_13F2; + gSaveContext.unk_13F2 = 0; + } + break; + + default: + gSaveContext.unk_13F0 = 0; + break; + } +} + +void Interface_DrawMagicBar(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 magicBarY; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 2650); + + if (gSaveContext.magicLevel != 0) { + if (gSaveContext.healthCapacity > 0xA0) { + magicBarY = R_MAGIC_BAR_LARGE_Y; + } else { + magicBarY = R_MAGIC_BAR_SMALL_Y; + } + + func_80094520(globalCtx->state.gfxCtx); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, sMagicBorderR, sMagicBorderG, sMagicBorderB, interfaceCtx->magicAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 100, 50, 50, 255); + + OVERLAY_DISP = + Gfx_TextureIA8(OVERLAY_DISP, gMagicBarEndTex, 8, 16, OTRGetRectDimensionFromLeftEdge(R_MAGIC_BAR_X), + magicBarY, 8, 16, 1 << 10, 1 << 10); + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gMagicBarMidTex, 24, 16, OTRGetRectDimensionFromLeftEdge(R_MAGIC_BAR_X) + 8, magicBarY, + gSaveContext.unk_13F4, 16, 1 << 10, 1 << 10); + + gDPLoadTextureBlock(OVERLAY_DISP++, gMagicBarEndTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 16, 0, + G_TX_MIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 3, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + const s16 rMagicBarX = OTRGetRectDimensionFromLeftEdge(R_MAGIC_BAR_X); + + gSPWideTextureRectangle(OVERLAY_DISP++, ((rMagicBarX + gSaveContext.unk_13F4) + 8) << 2, magicBarY << 2, + ((rMagicBarX + gSaveContext.unk_13F4) + 16) << 2, (magicBarY + 16) << 2, G_TX_RENDERTILE, + 256, 0, 1 << 10, 1 << 10); + + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, PRIMITIVE, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, PRIMITIVE); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + + const s16 rMagicFillX = OTRGetRectDimensionFromLeftEdge(R_MAGIC_FILL_X); + + if (gSaveContext.unk_13F0 == 4) { + // Yellow part of the bar indicating the amount of magic to be subtracted + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 250, 250, 0, interfaceCtx->magicAlpha); + + gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicBarFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, + (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, + 0, 1 << 10, 1 << 10); + + // Fill the rest of the bar with the normal magic color + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_MAGIC_FILL_COLOR(0), R_MAGIC_FILL_COLOR(1), R_MAGIC_FILL_COLOR(2), + interfaceCtx->magicAlpha); + + gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, + (rMagicFillX + gSaveContext.unk_13F8) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, + 0, 0, 1 << 10, 1 << 10); + } else { + // Fill the whole bar with the normal magic color + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_MAGIC_FILL_COLOR(0), R_MAGIC_FILL_COLOR(1), R_MAGIC_FILL_COLOR(2), + interfaceCtx->magicAlpha); + + gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicBarFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, + (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, + 0, 1 << 10, 1 << 10); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 2731); +} + +void func_80088AA0(s16 arg0) { + gSaveContext.timerX[1] = 140; + gSaveContext.timerY[1] = 80; + D_80125A5C = 0; + gSaveContext.timer2Value = arg0; + + if (arg0 != 0) { + gSaveContext.timer2State = 1; + } else { + gSaveContext.timer2State = 7; + } +} + +void func_80088AF0(GlobalContext* globalCtx) { + if (gSaveContext.timer2State != 0) { + if (gSaveContext.eventInf[1] & 1) { + gSaveContext.timer2Value = 239; + } else { + gSaveContext.timer2Value = 1; + } + } +} + +void func_80088B34(s16 arg0) { + gSaveContext.timerX[0] = 140; + gSaveContext.timerY[0] = 80; + D_80125A5C = 0; + gSaveContext.timer1Value = arg0; + + if (arg0 != 0) { + gSaveContext.timer1State = 5; + } else { + gSaveContext.timer1State = 11; + } +} + +void Interface_DrawActionLabel(GraphicsContext* gfxCtx, void* texture) { + OPEN_DISPS(gfxCtx, "../z_parameter.c", 2820); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, texture, G_IM_FMT_IA, DO_ACTION_TEX_WIDTH, DO_ACTION_TEX_HEIGHT, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0); + + CLOSE_DISPS(gfxCtx, "../z_parameter.c", 2829); +} + +void Interface_DrawItemButtons(GlobalContext* globalCtx) { + static void* cUpLabelTextures[] = { gNaviCUpENGTex, gNaviCUpENGTex, gNaviCUpENGTex }; + static s16 startButtonLeftPos[] = { 132, 130, 130 }; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + Player* player = GET_PLAYER(globalCtx); + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 temp; // Used as both an alpha value and a button index + s16 dxdy; + s16 width; + s16 height; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 2900); + + // B Button Color & Texture + // Also loads the Item Button Texture reused by other buttons afterwards + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_B_BTN_COLOR(0), R_B_BTN_COLOR(1), R_B_BTN_COLOR(2), interfaceCtx->bAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + + OVERLAY_DISP = + Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(0)), R_ITEM_BTN_Y(0), + R_ITEM_BTN_WIDTH(0), R_ITEM_BTN_WIDTH(0), R_ITEM_BTN_DD(0) << 1, R_ITEM_BTN_DD(0) << 1); + + // C-Left Button Color & Texture + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), + interfaceCtx->cLeftAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(1)) << 2, R_ITEM_BTN_Y(1) << 2, + (OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(1)) + R_ITEM_BTN_WIDTH(1)) << 2, + (R_ITEM_BTN_Y(1) + R_ITEM_BTN_WIDTH(1)) << 2, + G_TX_RENDERTILE, 0, 0, R_ITEM_BTN_DD(1) << 1, R_ITEM_BTN_DD(1) << 1); + + // C-Down Button Color & Texture + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), + interfaceCtx->cDownAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(2)) << 2, R_ITEM_BTN_Y(2) << 2, + (OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(2)) + R_ITEM_BTN_WIDTH(2)) << 2, + (R_ITEM_BTN_Y(2) + R_ITEM_BTN_WIDTH(2)) << 2, + G_TX_RENDERTILE, 0, 0, R_ITEM_BTN_DD(2) << 1, R_ITEM_BTN_DD(2) << 1); + + // C-Right Button Color & Texture + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), + interfaceCtx->cRightAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(3)) << 2, R_ITEM_BTN_Y(3) << 2, + (OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(3)) + R_ITEM_BTN_WIDTH(3)) << 2, + (R_ITEM_BTN_Y(3) + R_ITEM_BTN_WIDTH(3)) << 2, + G_TX_RENDERTILE, 0, 0, R_ITEM_BTN_DD(3) << 1, R_ITEM_BTN_DD(3) << 1); + + if ((pauseCtx->state < 8) || (pauseCtx->state >= 18)) { + if ((globalCtx->pauseCtx.state != 0) || (globalCtx->pauseCtx.debugState != 0)) { + // Start Button Texture, Color & Label + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 120, 120, interfaceCtx->startAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, OTRGetRectDimensionFromRightEdge(startButtonLeftPos[gSaveContext.language]) << 2, 68, + (OTRGetRectDimensionFromRightEdge(startButtonLeftPos[gSaveContext.language]) + 22) << 2, 156, + G_TX_RENDERTILE, 0, 0, 1462, 1462); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->startAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE * 2, G_IM_FMT_IA, + DO_ACTION_TEX_WIDTH, DO_ACTION_TEX_HEIGHT, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + dxdy = (1 << 10) / (R_START_LABEL_DD(gSaveContext.language) / 100.0f); + width = DO_ACTION_TEX_WIDTH / (R_START_LABEL_DD(gSaveContext.language) / 100.0f); + height = DO_ACTION_TEX_HEIGHT / (R_START_LABEL_DD(gSaveContext.language) / 100.0f); + const s16 rStartLabelX = OTRGetRectDimensionFromRightEdge(R_START_LABEL_X(gSaveContext.language)); + gSPWideTextureRectangle( + OVERLAY_DISP++, rStartLabelX << 2, + R_START_LABEL_Y(gSaveContext.language) << 2, (rStartLabelX + width) << 2, + (R_START_LABEL_Y(gSaveContext.language) + height) << 2, G_TX_RENDERTILE, 0, 0, dxdy, dxdy); + } + } + + if (interfaceCtx->naviCalling && (globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0) && + (globalCtx->csCtx.state == CS_STATE_IDLE)) { + if (!sCUpInvisible) { + // C-Up Button Texture, Color & Label (Navi Text) + gDPPipeSync(OVERLAY_DISP++); + + if ((gSaveContext.unk_13EA == 1) || (gSaveContext.unk_13EA == 2) || (gSaveContext.unk_13EA == 5)) { + temp = 0; + } else if ((player->stateFlags1 & 0x00200000) || (func_8008F2F8(globalCtx) == 4) || + (player->stateFlags2 & 0x00040000)) { + temp = 70; + } else { + temp = interfaceCtx->healthAlpha; + } + + const s16 rCUpBtnX = OTRGetRectDimensionFromRightEdge(R_C_UP_BTN_X); + const s16 rCUPIconX = OTRGetRectDimensionFromRightEdge(R_C_UP_ICON_X); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), temp); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gSPWideTextureRectangle(OVERLAY_DISP++, rCUpBtnX << 2, R_C_UP_BTN_Y << 2, (rCUpBtnX + 16) << 2, + (R_C_UP_BTN_Y + 16) << 2, G_TX_RENDERTILE, 0, 0, 2 << 10, 2 << 10); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, temp); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, cUpLabelTextures[gSaveContext.language], G_IM_FMT_IA, 32, 8, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, rCUPIconX << 2, R_C_UP_ICON_Y << 2, (rCUPIconX + 32) << 2, + (R_C_UP_ICON_Y + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + sCUpTimer--; + if (sCUpTimer == 0) { + sCUpInvisible ^= 1; + sCUpTimer = 10; + } + } + + gDPPipeSync(OVERLAY_DISP++); + + // Empty C Button Arrows + for (temp = 1; temp < 4; temp++) { + if (gSaveContext.equips.buttonItems[temp] > 0xF0) { + if (temp == 1) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), + interfaceCtx->cLeftAlpha); + } else if (temp == 2) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), + interfaceCtx->cDownAlpha); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_C_BTN_COLOR(0), R_C_BTN_COLOR(1), R_C_BTN_COLOR(2), + interfaceCtx->cRightAlpha); + } + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, ((u8*)gButtonBackgroundTex), 32, 32, + OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(temp)), R_ITEM_BTN_Y(temp), R_ITEM_BTN_WIDTH(temp), + R_ITEM_BTN_WIDTH(temp), R_ITEM_BTN_DD(temp) << 1, R_ITEM_BTN_DD(temp) << 1); + + const char* cButtonIcons[] = { gButtonBackgroundTex, gEquippedItemOutlineTex, gEmptyCLeftArrowTex, + gEmptyCDownArrowTex, gEmptyCRightArrowTex + }; + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, cButtonIcons[(temp + 1)], 32, 32, + OTRGetRectDimensionFromRightEdge(R_ITEM_BTN_X(temp)), R_ITEM_BTN_Y(temp), R_ITEM_BTN_WIDTH(temp), + R_ITEM_BTN_WIDTH(temp), R_ITEM_BTN_DD(temp) << 1, R_ITEM_BTN_DD(temp) << 1); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3071); +} + +void Interface_DrawItemIconTexture(GlobalContext* globalCtx, void* texture, s16 button) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3079); + + gDPLoadTextureBlock(OVERLAY_DISP++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, OTRGetRectDimensionFromRightEdge(R_ITEM_ICON_X(button)) << 2, R_ITEM_ICON_Y(button) << 2, + (OTRGetRectDimensionFromRightEdge(R_ITEM_ICON_X(button)) + R_ITEM_ICON_WIDTH(button)) << 2, + (R_ITEM_ICON_Y(button) + R_ITEM_ICON_WIDTH(button)) << 2, G_TX_RENDERTILE, 0, 0, + R_ITEM_ICON_DD(button) << 1, R_ITEM_ICON_DD(button) << 1); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3094); +} + +const char* _gAmmoDigit0Tex[] = +{ + gAmmoDigit0Tex, gAmmoDigit1Tex, gAmmoDigit2Tex, gAmmoDigit3Tex, gAmmoDigit4Tex, + gAmmoDigit5Tex, gAmmoDigit6Tex, gAmmoDigit7Tex, gAmmoDigit8Tex, gAmmoDigit9Tex, + gUnusedAmmoDigitHalfTex +}; + +void Interface_DrawAmmoCount(GlobalContext* globalCtx, s16 button, s16 alpha) { + s16 i; + s16 ammo; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3105); + + i = gSaveContext.equips.buttonItems[button]; + + if ((i == ITEM_STICK) || (i == ITEM_NUT) || (i == ITEM_BOMB) || (i == ITEM_BOW) || + ((i >= ITEM_BOW_ARROW_FIRE) && (i <= ITEM_BOW_ARROW_LIGHT)) || (i == ITEM_SLINGSHOT) || (i == ITEM_BOMBCHU) || + (i == ITEM_BEAN)) { + + if ((i >= ITEM_BOW_ARROW_FIRE) && (i <= ITEM_BOW_ARROW_LIGHT)) { + i = ITEM_BOW; + } + + ammo = AMMO(i); + + gDPPipeSync(OVERLAY_DISP++); + + if ((button == 0) && (gSaveContext.minigameState == 1)) { + ammo = globalCtx->interfaceCtx.hbaAmmo; + } else if ((button == 0) && (globalCtx->shootingGalleryStatus > 1)) { + ammo = globalCtx->shootingGalleryStatus - 1; + } else if ((button == 0) && (globalCtx->sceneNum == SCENE_BOWLING) && Flags_GetSwitch(globalCtx, 0x38)) { + ammo = globalCtx->bombchuBowlingStatus; + if (ammo < 0) { + ammo = 0; + } + } else if (((i == ITEM_BOW) && (AMMO(i) == CUR_CAPACITY(UPG_QUIVER))) || + ((i == ITEM_BOMB) && (AMMO(i) == CUR_CAPACITY(UPG_BOMB_BAG))) || + ((i == ITEM_SLINGSHOT) && (AMMO(i) == CUR_CAPACITY(UPG_BULLET_BAG))) || + ((i == ITEM_STICK) && (AMMO(i) == CUR_CAPACITY(UPG_STICKS))) || + ((i == ITEM_NUT) && (AMMO(i) == CUR_CAPACITY(UPG_NUTS))) || ((i == ITEM_BOMBCHU) && (ammo == 50)) || + ((i == ITEM_BEAN) && (ammo == 15))) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, alpha); + } + + if (ammo == 0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, alpha); + } + + for (i = 0; ammo >= 10; i++) { + ammo -= 10; + } + + if (i != 0) { + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[i], 8, 8, + OTRGetRectDimensionFromRightEdge(R_ITEM_AMMO_X(button)), R_ITEM_AMMO_Y(button), 8, 8, 1 << 10, 1 << 10); + } + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[ammo], 8, 8, + OTRGetRectDimensionFromRightEdge(R_ITEM_AMMO_X(button)) + 6, R_ITEM_AMMO_Y(button), 8, 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3158); +} + +void Interface_DrawActionButton(GlobalContext* globalCtx, f32 x, f32 y) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3172); + + Matrix_Translate(-137.0f + x, 97.0f - y, XREG(18) / 10.0f, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(interfaceCtx->unk_1F4 / 10000.0f, MTXMODE_APPLY); + + gSPMatrix(OVERLAY_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_parameter.c", 3177), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPVertex(OVERLAY_DISP++, &interfaceCtx->actionVtx[0], 4, 0); + + gDPLoadTextureBlock(OVERLAY_DISP++, gButtonBackgroundTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3187); +} + +void Interface_InitVertices(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + s16 i; + + interfaceCtx->actionVtx = Graph_Alloc(globalCtx->state.gfxCtx, 8 * sizeof(Vtx)); + + // clang-format off + interfaceCtx->actionVtx[0].v.ob[0] = + interfaceCtx->actionVtx[2].v.ob[0] = -15; + interfaceCtx->actionVtx[1].v.ob[0] = + interfaceCtx->actionVtx[3].v.ob[0] = interfaceCtx->actionVtx[0].v.ob[0] + 29; + + interfaceCtx->actionVtx[0].v.ob[1] = + interfaceCtx->actionVtx[1].v.ob[1] = 15; + interfaceCtx->actionVtx[2].v.ob[1] = + interfaceCtx->actionVtx[3].v.ob[1] = interfaceCtx->actionVtx[0].v.ob[1] - 29; + + interfaceCtx->actionVtx[4].v.ob[0] = + interfaceCtx->actionVtx[6].v.ob[0] = -((XREG(21) + 1) / 2); + interfaceCtx->actionVtx[5].v.ob[0] = + interfaceCtx->actionVtx[7].v.ob[0] = interfaceCtx->actionVtx[4].v.ob[0] + (XREG(21) + 1); + + interfaceCtx->actionVtx[4].v.ob[1] = + interfaceCtx->actionVtx[5].v.ob[1] = XREG(28) / 2; + interfaceCtx->actionVtx[6].v.ob[1] = + interfaceCtx->actionVtx[7].v.ob[1] = interfaceCtx->actionVtx[4].v.ob[1] - XREG(28); + + for (i = 0; i < 8; i += 4) { + interfaceCtx->actionVtx[i].v.ob[2] = interfaceCtx->actionVtx[i+1].v.ob[2] = + interfaceCtx->actionVtx[i+2].v.ob[2] = interfaceCtx->actionVtx[i+3].v.ob[2] = 0; + + interfaceCtx->actionVtx[i].v.flag = interfaceCtx->actionVtx[i+1].v.flag = + interfaceCtx->actionVtx[i+2].v.flag = interfaceCtx->actionVtx[i+3].v.flag = 0; + + interfaceCtx->actionVtx[i].v.tc[0] = interfaceCtx->actionVtx[i].v.tc[1] = + interfaceCtx->actionVtx[i+1].v.tc[1] = interfaceCtx->actionVtx[i+2].v.tc[0] = -16; + interfaceCtx->actionVtx[i+1].v.tc[0] = interfaceCtx->actionVtx[i+2].v.tc[1] = + interfaceCtx->actionVtx[i+3].v.tc[0] = interfaceCtx->actionVtx[i+3].v.tc[1] = 1024 - 16; + + interfaceCtx->actionVtx[i].v.cn[0] = interfaceCtx->actionVtx[i+1].v.cn[0] = + interfaceCtx->actionVtx[i+2].v.cn[0] = interfaceCtx->actionVtx[i+3].v.cn[0] = + interfaceCtx->actionVtx[i].v.cn[1] = interfaceCtx->actionVtx[i+1].v.cn[1] = + interfaceCtx->actionVtx[i+2].v.cn[1] = interfaceCtx->actionVtx[i+3].v.cn[1] = + interfaceCtx->actionVtx[i].v.cn[2] = interfaceCtx->actionVtx[i+1].v.cn[2] = + interfaceCtx->actionVtx[i+2].v.cn[2] = interfaceCtx->actionVtx[i+3].v.cn[2] = 255; + + interfaceCtx->actionVtx[i].v.cn[3] = interfaceCtx->actionVtx[i+1].v.cn[3] = + interfaceCtx->actionVtx[i+2].v.cn[3] = interfaceCtx->actionVtx[i+3].v.cn[3] = 255; + } + + interfaceCtx->actionVtx[5].v.tc[0] = interfaceCtx->actionVtx[7].v.tc[0] = 1536; + interfaceCtx->actionVtx[6].v.tc[1] = interfaceCtx->actionVtx[7].v.tc[1] = 512; + + interfaceCtx->beatingHeartVtx = Graph_Alloc(globalCtx->state.gfxCtx, 4 * sizeof(Vtx)); + + interfaceCtx->beatingHeartVtx[0].v.ob[0] = interfaceCtx->beatingHeartVtx[2].v.ob[0] = -8; + interfaceCtx->beatingHeartVtx[1].v.ob[0] = interfaceCtx->beatingHeartVtx[3].v.ob[0] = 8; + interfaceCtx->beatingHeartVtx[0].v.ob[1] = interfaceCtx->beatingHeartVtx[1].v.ob[1] = 8; + interfaceCtx->beatingHeartVtx[2].v.ob[1] = interfaceCtx->beatingHeartVtx[3].v.ob[1] = -8; + + interfaceCtx->beatingHeartVtx[0].v.ob[2] = interfaceCtx->beatingHeartVtx[1].v.ob[2] = + interfaceCtx->beatingHeartVtx[2].v.ob[2] = interfaceCtx->beatingHeartVtx[3].v.ob[2] = 0; + + interfaceCtx->beatingHeartVtx[0].v.flag = interfaceCtx->beatingHeartVtx[1].v.flag = + interfaceCtx->beatingHeartVtx[2].v.flag = interfaceCtx->beatingHeartVtx[3].v.flag = 0; + + interfaceCtx->beatingHeartVtx[0].v.tc[0] = interfaceCtx->beatingHeartVtx[0].v.tc[1] = + interfaceCtx->beatingHeartVtx[1].v.tc[1] = interfaceCtx->beatingHeartVtx[2].v.tc[0] = 0; + interfaceCtx->beatingHeartVtx[1].v.tc[0] = interfaceCtx->beatingHeartVtx[2].v.tc[1] = + interfaceCtx->beatingHeartVtx[3].v.tc[0] = interfaceCtx->beatingHeartVtx[3].v.tc[1] = 512; + + interfaceCtx->beatingHeartVtx[0].v.cn[0] = interfaceCtx->beatingHeartVtx[1].v.cn[0] = + interfaceCtx->beatingHeartVtx[2].v.cn[0] = interfaceCtx->beatingHeartVtx[3].v.cn[0] = + interfaceCtx->beatingHeartVtx[0].v.cn[1] = interfaceCtx->beatingHeartVtx[1].v.cn[1] = + interfaceCtx->beatingHeartVtx[2].v.cn[1] = interfaceCtx->beatingHeartVtx[3].v.cn[1] = + interfaceCtx->beatingHeartVtx[0].v.cn[2] = interfaceCtx->beatingHeartVtx[1].v.cn[2] = + interfaceCtx->beatingHeartVtx[2].v.cn[2] = interfaceCtx->beatingHeartVtx[3].v.cn[2] = + interfaceCtx->beatingHeartVtx[0].v.cn[3] = interfaceCtx->beatingHeartVtx[1].v.cn[3] = + interfaceCtx->beatingHeartVtx[2].v.cn[3] = interfaceCtx->beatingHeartVtx[3].v.cn[3] = 255; + // clang-format on +} + +/* +void func_8008A8B8(GlobalContext* globalCtx, s32 topY, s32 bottomY, s32 leftX, s32 rightX) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + Vec3f eye; + Vec3f lookAt; + Vec3f up; + + eye.x = eye.y = eye.z = 0.0f; + lookAt.x = lookAt.y = 0.0f; + lookAt.z = -1.0f; + up.x = up.z = 0.0f; + up.y = 1.0f; + + func_800AA358(&interfaceCtx->view, &eye, &lookAt, &up); + + interfaceCtx->viewport.topY = topY; + interfaceCtx->viewport.bottomY = bottomY; + interfaceCtx->viewport.leftX = leftX; + interfaceCtx->viewport.rightX = rightX; + View_SetViewport(&interfaceCtx->view, &interfaceCtx->viewport); + + func_800AA460(&interfaceCtx->view, 60.0f, 10.0f, 60.0f); + func_800AB560(&interfaceCtx->view); +} +*/ + +void func_8008A994(InterfaceContext* interfaceCtx) { + SET_FULLSCREEN_VIEWPORT(&interfaceCtx->view); + func_800AB2C4(&interfaceCtx->view); +} + +const char* digitTextures[] = +{ + gCounterDigit0Tex, gCounterDigit1Tex, gCounterDigit2Tex, gCounterDigit3Tex, + gCounterDigit4Tex, gCounterDigit5Tex, gCounterDigit6Tex, gCounterDigit7Tex, gCounterDigit8Tex, + gCounterDigit9Tex, gCounterColonTex, gCounterDigit1Tex, gCounterDigit2Tex, gCounterDigit3Tex, + gCounterDigit4Tex, gCounterDigit5Tex, gCounterDigit6Tex, gCounterDigit7Tex, gCounterDigit8Tex +}; + +void Interface_Draw(GlobalContext* globalCtx) { + static s16 magicArrowEffectsR[] = { 255, 100, 255 }; + static s16 magicArrowEffectsG[] = { 0, 100, 255 }; + static s16 magicArrowEffectsB[] = { 0, 255, 100 }; + static s16 timerDigitLeftPos[] = { 16, 25, 34, 42, 51 }; + static s16 digitWidth[] = { 9, 9, 8, 9, 9 }; + // unused, most likely colors + static s16 D_80125B1C[][3] = { + { 0, 150, 0 }, { 100, 255, 0 }, { 255, 255, 255 }, { 0, 0, 0 }, { 255, 255, 255 }, + }; + static s16 rupeeDigitsFirst[] = { 1, 0, 0 }; + static s16 rupeeDigitsCount[] = { 2, 3, 3 }; + static s16 spoilingItemEntrances[] = { 0x01AD, 0x0153, 0x0153 }; + static f32 D_80125B54[] = { -40.0f, -35.0f }; // unused + static s16 D_80125B5C[] = { 91, 91 }; // unused + static s16 D_8015FFE0; + static s16 D_8015FFE2; + static s16 D_8015FFE4; + static s16 D_8015FFE6; + static s16 timerDigits[5]; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + MessageContext* msgCtx = &globalCtx->msgCtx; + Player* player = GET_PLAYER(globalCtx); + s16 svar1; + s16 svar2; + s16 svar3; + s16 svar4; + s16 svar5; + s16 svar6; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 3405); + + // Invalidate Do Action textures as they may have changed + gSPInvalidateTexCache(OVERLAY_DISP++, interfaceCtx->doActionSegment); + gSPInvalidateTexCache(OVERLAY_DISP++, interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE); + + gSPSegment(OVERLAY_DISP++, 0x02, interfaceCtx->parameterSegment); + gSPSegment(OVERLAY_DISP++, 0x07, interfaceCtx->doActionSegment); + gSPSegment(OVERLAY_DISP++, 0x08, interfaceCtx->iconItemSegment); + gSPSegment(OVERLAY_DISP++, 0x0B, interfaceCtx->mapSegment); + + if (pauseCtx->debugState == 0) { + Interface_InitVertices(globalCtx); + func_8008A994(interfaceCtx); + HealthMeter_Draw(globalCtx); + + func_80094520(globalCtx->state.gfxCtx); + + // Rupee Icon + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 200, 255, 100, interfaceCtx->magicAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 80, 0, 255); + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gRupeeCounterIconTex, 16, 16, OTRGetRectDimensionFromLeftEdge(26), + 206, 16, 16, 1 << 10, 1 << 10); + + switch (globalCtx->sceneNum) { + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_GANON: + case SCENE_MEN: + case SCENE_GERUDOWAY: + case SCENE_GANONTIKA: + case SCENE_GANON_SONOGO: + case SCENE_GANONTIKA_SONOGO: + case SCENE_TAKARAYA: + if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] >= 0) { + // Small Key Icon + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 200, 230, 255, interfaceCtx->magicAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 20, 255); + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gSmallKeyCounterIconTex, 16, 16, OTRGetRectDimensionFromLeftEdge(26), 190, 16, 16, + 1 << 10, 1 << 10); + + // Small Key Counter + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, + TEXEL0, 0, PRIMITIVE, 0); + + interfaceCtx->counterDigits[2] = 0; + interfaceCtx->counterDigits[3] = gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]; + + while (interfaceCtx->counterDigits[3] >= 10) { + interfaceCtx->counterDigits[2]++; + interfaceCtx->counterDigits[3] -= 10; + } + + svar3 = OTRGetRectDimensionFromLeftEdge(42); + + if (interfaceCtx->counterDigits[2] != 0) { + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, ((u8*)((u8*)digitTextures[interfaceCtx->counterDigits[2]])), 8, + 16, svar3, 190, 8, 16, 1 << 10, 1 << 10); + svar3 += 8; + } + + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, + ((u8*)digitTextures[interfaceCtx->counterDigits[3]]), 8, 16, + svar3, 190, 8, 16, 1 << 10, 1 << 10); + } + break; + default: + break; + } + + // Rupee Counter + gDPPipeSync(OVERLAY_DISP++); + + if (gSaveContext.rupees == CUR_CAPACITY(UPG_WALLET)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, interfaceCtx->magicAlpha); + } else if (gSaveContext.rupees != 0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->magicAlpha); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, interfaceCtx->magicAlpha); + } + + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + interfaceCtx->counterDigits[0] = interfaceCtx->counterDigits[1] = 0; + interfaceCtx->counterDigits[2] = gSaveContext.rupees; + + if ((interfaceCtx->counterDigits[2] > 9999) || (interfaceCtx->counterDigits[2] < 0)) { + interfaceCtx->counterDigits[2] &= 0xDDD; + } + + while (interfaceCtx->counterDigits[2] >= 100) { + interfaceCtx->counterDigits[0]++; + interfaceCtx->counterDigits[2] -= 100; + } + + while (interfaceCtx->counterDigits[2] >= 10) { + interfaceCtx->counterDigits[1]++; + interfaceCtx->counterDigits[2] -= 10; + } + + svar2 = rupeeDigitsFirst[CUR_UPG_VALUE(UPG_WALLET)]; + svar5 = rupeeDigitsCount[CUR_UPG_VALUE(UPG_WALLET)]; + + for (svar1 = 0, svar3 = 42; svar1 < svar5; svar1++, svar2++, svar3 += 8) { + OVERLAY_DISP = + Gfx_TextureI8(OVERLAY_DISP, ((u8*)digitTextures[interfaceCtx->counterDigits[svar2]]), 8, 16, + OTRGetRectDimensionFromLeftEdge(svar3), 206, 8, 16, 1 << 10, 1 << 10); + } + + Interface_DrawMagicBar(globalCtx); + Minimap_Draw(globalCtx); + + if ((R_PAUSE_MENU_MODE != 2) && (R_PAUSE_MENU_MODE != 3)) { + func_8002C124(&globalCtx->actorCtx.targetCtx, globalCtx); // Draw Z-Target + } + + func_80094520(globalCtx->state.gfxCtx); + + Interface_DrawItemButtons(globalCtx); + + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->bAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + + if (!(interfaceCtx->unk_1FA)) { + // B Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) + { + Interface_DrawItemIconTexture(globalCtx, gItemIcons[gSaveContext.equips.buttonItems[0]], 0); + + if ((player->stateFlags1 & 0x00800000) || (globalCtx->shootingGalleryStatus > 1) || + ((globalCtx->sceneNum == SCENE_BOWLING) && Flags_GetSwitch(globalCtx, 0x38))) { + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + Interface_DrawAmmoCount(globalCtx, 0, interfaceCtx->bAlpha); + } + } + } else { + // B Button Do Action Label + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->bAlpha); + + gDPLoadTextureBlock_4b(OVERLAY_DISP++, interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE, G_IM_FMT_IA, + DO_ACTION_TEX_WIDTH, DO_ACTION_TEX_HEIGHT, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + R_B_LABEL_DD = (1 << 10) / (WREG(37 + gSaveContext.language) / 100.0f); + const s16 rBLabelX = OTRGetRectDimensionFromRightEdge(R_B_LABEL_X(gSaveContext.language)); + gSPWideTextureRectangle(OVERLAY_DISP++, rBLabelX << 2, R_B_LABEL_Y(gSaveContext.language) << 2, + (rBLabelX + DO_ACTION_TEX_WIDTH) << 2, + (R_B_LABEL_Y(gSaveContext.language) + DO_ACTION_TEX_HEIGHT) << 2, G_TX_RENDERTILE, 0, 0, + R_B_LABEL_DD, R_B_LABEL_DD); + } + + gDPPipeSync(OVERLAY_DISP++); + + // C-Left Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[1] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(globalCtx, gItemIcons[gSaveContext.equips.buttonItems[1]], 1); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + Interface_DrawAmmoCount(globalCtx, 1, interfaceCtx->cLeftAlpha); + } + + gDPPipeSync(OVERLAY_DISP++); + + // C-Down Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[2] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cDownAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(globalCtx, gItemIcons[gSaveContext.equips.buttonItems[2]], 2); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + Interface_DrawAmmoCount(globalCtx, 2, interfaceCtx->cDownAlpha); + } + + gDPPipeSync(OVERLAY_DISP++); + + // C-Right Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[3] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cRightAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(globalCtx, gItemIcons[gSaveContext.equips.buttonItems[3]], 3); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + Interface_DrawAmmoCount(globalCtx, 3, interfaceCtx->cRightAlpha); + } + + // A Button + func_80094A14(globalCtx->state.gfxCtx); + const f32 rABtnX = OTRGetDimensionFromRightEdge(R_A_BTN_X); + //func_8008A8B8(globalCtx, R_A_BTN_Y, R_A_BTN_Y + 45, rABtnX, rABtnX + 45); + gSPClearGeometryMode(OVERLAY_DISP++, G_CULL_BOTH); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, R_A_BTN_COLOR(0), R_A_BTN_COLOR(1), R_A_BTN_COLOR(2), + interfaceCtx->aAlpha); + Interface_DrawActionButton(globalCtx, rABtnX, R_A_BTN_Y); + gDPPipeSync(OVERLAY_DISP++); + const f32 rAIconX = OTRGetDimensionFromRightEdge(R_A_ICON_X); + //func_8008A8B8(globalCtx, R_A_ICON_Y, R_A_ICON_Y + 45, rAIconX, rAIconX + 45); + gSPSetGeometryMode(OVERLAY_DISP++, G_CULL_BACK); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->aAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); + Matrix_Translate(-138.0f + rAIconX, 98.0f - R_A_ICON_Y, WREG(46 + gSaveContext.language) / 10.0f, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(interfaceCtx->unk_1F4 / 10000.0f, MTXMODE_APPLY); + gSPMatrix(OVERLAY_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_parameter.c", 3701), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPVertex(OVERLAY_DISP++, &interfaceCtx->actionVtx[4], 4, 0); + + if ((interfaceCtx->unk_1EC < 2) || (interfaceCtx->unk_1EC == 3)) { + Interface_DrawActionLabel(globalCtx->state.gfxCtx, interfaceCtx->doActionSegment); + } else { + Interface_DrawActionLabel(globalCtx->state.gfxCtx, interfaceCtx->doActionSegment + DO_ACTION_TEX_SIZE); + } + + gDPPipeSync(OVERLAY_DISP++); + + func_8008A994(interfaceCtx); + svar3 = 16; + + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 3)) { + // Inventory Equip Effects + gSPSegment(OVERLAY_DISP++, 0x08, pauseCtx->iconItemSegment); + func_80094A14(globalCtx->state.gfxCtx); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + gSPMatrix(OVERLAY_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + + pauseCtx->cursorVtx[svar3].v.ob[0] = pauseCtx->cursorVtx[18].v.ob[0] = svar2 = pauseCtx->equipAnimX / 10; + pauseCtx->cursorVtx[17].v.ob[0] = pauseCtx->cursorVtx[19].v.ob[0] = svar2 = + pauseCtx->cursorVtx[svar3].v.ob[0] + WREG(90) / 10; + pauseCtx->cursorVtx[svar3].v.ob[1] = pauseCtx->cursorVtx[17].v.ob[1] = svar2 = pauseCtx->equipAnimY / 10; + pauseCtx->cursorVtx[18].v.ob[1] = pauseCtx->cursorVtx[19].v.ob[1] = svar2 = + pauseCtx->cursorVtx[svar3].v.ob[1] - WREG(90) / 10; + + if (pauseCtx->equipTargetItem < 0xBF) { + // Normal Equip (icon goes from the inventory slot to the C button when equipping it) + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, pauseCtx->equipAnimAlpha); + gSPVertex(OVERLAY_DISP++, &pauseCtx->cursorVtx[16], 4, 0); + + gDPLoadTextureBlock(OVERLAY_DISP++, gItemIcons[pauseCtx->equipTargetItem], G_IM_FMT_RGBA, G_IM_SIZ_32b, + 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + } else { + // Magic Arrow Equip Effect + svar1 = pauseCtx->equipTargetItem - 0xBF; + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicArrowEffectsR[svar1], magicArrowEffectsG[svar1], + magicArrowEffectsB[svar1], pauseCtx->equipAnimAlpha); + + if ((pauseCtx->equipAnimAlpha > 0) && (pauseCtx->equipAnimAlpha < 255)) { + svar1 = (pauseCtx->equipAnimAlpha / 8) / 2; + pauseCtx->cursorVtx[16].v.ob[0] = pauseCtx->cursorVtx[18].v.ob[0] = svar2 = + pauseCtx->cursorVtx[16].v.ob[0] - svar1; + pauseCtx->cursorVtx[17].v.ob[0] = pauseCtx->cursorVtx[19].v.ob[0] = svar2 = + pauseCtx->cursorVtx[16].v.ob[0] + svar1 * 2 + 32; + pauseCtx->cursorVtx[16].v.ob[1] = pauseCtx->cursorVtx[17].v.ob[1] = svar2 = + pauseCtx->cursorVtx[16].v.ob[1] + svar1; + pauseCtx->cursorVtx[18].v.ob[1] = pauseCtx->cursorVtx[19].v.ob[1] = svar2 = + pauseCtx->cursorVtx[16].v.ob[1] - svar1 * 2 - 32; + } + + gSPVertex(OVERLAY_DISP++, &pauseCtx->cursorVtx[16], 4, 0); + gDPLoadTextureBlock(OVERLAY_DISP++, gMagicArrowEquipEffectTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } + + gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0); + } + + func_80094520(globalCtx->state.gfxCtx); + + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0)) { + if (gSaveContext.minigameState != 1) { + // Carrots rendering if the action corresponds to riding a horse + if (interfaceCtx->unk_1EE == 8) { + // Load Carrot Icon + gDPLoadTextureBlock(OVERLAY_DISP++, gCarrotIconTex, G_IM_FMT_RGBA, G_IM_SIZ_32b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + // Draw 6 carrots + for (svar1 = 1, svar5 = ZREG(14); svar1 < 7; svar1++, svar5 += 16) { + // Carrot Color (based on availability) + if ((interfaceCtx->numHorseBoosts == 0) || (interfaceCtx->numHorseBoosts < svar1)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 150, 255, interfaceCtx->aAlpha); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->aAlpha); + } + + gSPTextureRectangle(OVERLAY_DISP++, svar5 << 2, ZREG(15) << 2, (svar5 + 16) << 2, + (ZREG(15) + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + } + } else { + // Score for the Horseback Archery + svar5 = OTRGetRectDimensionFromRightEdge(WREG(32)); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->bAlpha); + + // Target Icon + gDPLoadTextureBlock(OVERLAY_DISP++, gArcheryScoreIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 24, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(OVERLAY_DISP++, (svar5 + 28) << 2, ZREG(15) << 2, (svar5 + 52) << 2, + (ZREG(15) + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + // Score Counter + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, + TEXEL0, 0, PRIMITIVE, 0); + + svar5 = OTRGetRectDimensionFromRightEdge(WREG(32)) + 6 * 9; + + for (svar1 = svar2 = 0; svar1 < 4; svar1++) { + if (sHBAScoreDigits[svar1] != 0 || (svar2 != 0) || (svar1 >= 3)) { + OVERLAY_DISP = Gfx_TextureI8( + OVERLAY_DISP, digitTextures[sHBAScoreDigits[svar1]], 8, 16, svar5, + (ZREG(15) - 2), digitWidth[0], VREG(42), VREG(43) << 1, VREG(43) << 1); + svar5 += 9; + svar2++; + } + } + + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + } + } + + if ((gSaveContext.timer2State == 5) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT)) { + // Trade quest timer reached 0 + D_8015FFE6 = 40; + gSaveContext.cutsceneIndex = 0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.timer2State = 0; + + if ((gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_MASTER) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_BGS) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KNIFE)) { + if (gSaveContext.buttonStatus[0] != BTN_ENABLED) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + } else { + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + } + } + + // Revert any spoiling trade quest items + for (svar1 = 0; svar1 < ARRAY_COUNT(gSpoilingItems); svar1++) { + if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[svar1]) { + gSaveContext.eventInf[0] &= 0x7F80; + osSyncPrintf("EVENT_INF=%x\n", gSaveContext.eventInf[0]); + globalCtx->nextEntranceIndex = spoilingItemEntrances[svar1]; + INV_CONTENT(gSpoilingItemReverts[svar1]) = gSpoilingItemReverts[svar1]; + + for (svar2 = 1; svar2 < 4; svar2++) { + if (gSaveContext.equips.buttonItems[svar2] == gSpoilingItems[svar1]) { + gSaveContext.equips.buttonItems[svar2] = gSpoilingItemReverts[svar1]; + Interface_LoadItemIcon1(globalCtx, svar2); + } + } + } + } + } + + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0) && + (globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE) && (msgCtx->msgMode == MSGMODE_NONE) && + !(player->stateFlags2 & 0x01000000) && (globalCtx->sceneLoadFlag == 0) && + (globalCtx->transitionMode == 0) && !Gameplay_InCsMode(globalCtx) && (gSaveContext.minigameState != 1) && + (globalCtx->shootingGalleryStatus <= 1) && + !((globalCtx->sceneNum == SCENE_BOWLING) && Flags_GetSwitch(globalCtx, 0x38))) { + svar6 = 0; + switch (gSaveContext.timer1State) { + case 1: + D_8015FFE2 = 20; + D_8015FFE0 = 20; + gSaveContext.timer1Value = gSaveContext.health >> 1; + gSaveContext.timer1State = 2; + break; + case 2: + D_8015FFE2--; + if (D_8015FFE2 == 0) { + D_8015FFE2 = 20; + gSaveContext.timer1State = 3; + } + break; + case 5: + case 11: + D_8015FFE2 = 20; + D_8015FFE0 = 20; + if (gSaveContext.timer1State == 5) { + gSaveContext.timer1State = 6; + } else { + gSaveContext.timer1State = 12; + } + break; + case 6: + case 12: + D_8015FFE2--; + if (D_8015FFE2 == 0) { + D_8015FFE2 = 20; + if (gSaveContext.timer1State == 6) { + gSaveContext.timer1State = 7; + } else { + gSaveContext.timer1State = 13; + } + } + break; + case 3: + case 7: + svar1 = (gSaveContext.timerX[0] - 26) / D_8015FFE2; + gSaveContext.timerX[0] -= svar1; + + if (gSaveContext.healthCapacity > 0xA0) { + svar1 = (gSaveContext.timerY[0] - 54) / D_8015FFE2; + } else { + svar1 = (gSaveContext.timerY[0] - 46) / D_8015FFE2; + } + gSaveContext.timerY[0] -= svar1; + + D_8015FFE2--; + if (D_8015FFE2 == 0) { + D_8015FFE2 = 20; + gSaveContext.timerX[0] = 26; + + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[0] = 54; + } else { + gSaveContext.timerY[0] = 46; + } + + if (gSaveContext.timer1State == 3) { + gSaveContext.timer1State = 4; + } else { + gSaveContext.timer1State = 8; + } + } + case 4: + case 8: + if ((gSaveContext.timer1State == 4) || (gSaveContext.timer1State == 8)) { + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[0] = 54; + } else { + gSaveContext.timerY[0] = 46; + } + } + + if ((gSaveContext.timer1State >= 3) && (msgCtx->msgLength == 0)) { + D_8015FFE0--; + if (D_8015FFE0 == 0) { + if (gSaveContext.timer1Value != 0) { + gSaveContext.timer1Value--; + } + + D_8015FFE0 = 20; + + if (gSaveContext.timer1Value == 0) { + gSaveContext.timer1State = 10; + if (D_80125A5C != 0) { + gSaveContext.health = 0; + globalCtx->damagePlayer(globalCtx, -(gSaveContext.health + 2)); + } + D_80125A5C = 0; + } else if (gSaveContext.timer1Value > 60) { + if (timerDigits[4] == 1) { + Audio_PlaySoundGeneral(NA_SE_SY_MESSAGE_WOMAN, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } else if (gSaveContext.timer1Value >= 11) { + if (timerDigits[4] & 1) { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_N, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } else { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_E, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } + } + break; + case 13: + svar1 = (gSaveContext.timerX[0] - 26) / D_8015FFE2; + gSaveContext.timerX[0] -= svar1; + + if (gSaveContext.healthCapacity > 0xA0) { + svar1 = (gSaveContext.timerY[0] - 54) / D_8015FFE2; + } else { + svar1 = (gSaveContext.timerY[0] - 46) / D_8015FFE2; + } + gSaveContext.timerY[0] -= svar1; + + D_8015FFE2--; + if (D_8015FFE2 == 0) { + D_8015FFE2 = 20; + gSaveContext.timerX[0] = 26; + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[0] = 54; + } else { + gSaveContext.timerY[0] = 46; + } + + gSaveContext.timer1State = 14; + } + case 14: + if (gSaveContext.timer1State == 14) { + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[0] = 54; + } else { + gSaveContext.timerY[0] = 46; + } + } + + if (gSaveContext.timer1State >= 3) { + D_8015FFE0--; + if (D_8015FFE0 == 0) { + gSaveContext.timer1Value++; + D_8015FFE0 = 20; + + if (gSaveContext.timer1Value == 3599) { + D_8015FFE2 = 40; + gSaveContext.timer1State = 15; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_N, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } + } + break; + case 10: + if (gSaveContext.timer2State != 0) { + D_8015FFE6 = 20; + D_8015FFE4 = 20; + gSaveContext.timerX[1] = 140; + gSaveContext.timerY[1] = 80; + + if (gSaveContext.timer2State < 7) { + gSaveContext.timer2State = 2; + } else { + gSaveContext.timer2State = 8; + } + + gSaveContext.timer1State = 0; + } else { + gSaveContext.timer1State = 0; + } + case 15: + break; + default: + svar6 = 1; + switch (gSaveContext.timer2State) { + case 1: + case 7: + D_8015FFE6 = 20; + D_8015FFE4 = 20; + gSaveContext.timerX[1] = 140; + gSaveContext.timerY[1] = 80; + if (gSaveContext.timer2State == 1) { + gSaveContext.timer2State = 2; + } else { + gSaveContext.timer2State = 8; + } + break; + case 2: + case 8: + D_8015FFE6--; + if (D_8015FFE6 == 0) { + D_8015FFE6 = 20; + if (gSaveContext.timer2State == 2) { + gSaveContext.timer2State = 3; + } else { + gSaveContext.timer2State = 9; + } + } + break; + case 3: + case 9: + osSyncPrintf("event_xp[1]=%d, event_yp[1]=%d TOTAL_EVENT_TM=%d\n", + svar5 = gSaveContext.timerX[1], svar2 = gSaveContext.timerY[1], + gSaveContext.timer2Value); + svar1 = (gSaveContext.timerX[1] - 26) / D_8015FFE6; + gSaveContext.timerX[1] -= svar1; + if (gSaveContext.healthCapacity > 0xA0) { + svar1 = (gSaveContext.timerY[1] - 54) / D_8015FFE6; + } else { + svar1 = (gSaveContext.timerY[1] - 46) / D_8015FFE6; + } + gSaveContext.timerY[1] -= svar1; + + D_8015FFE6--; + if (D_8015FFE6 == 0) { + D_8015FFE6 = 20; + gSaveContext.timerX[1] = 26; + + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[1] = 54; + } else { + gSaveContext.timerY[1] = 46; + } + + if (gSaveContext.timer2State == 3) { + gSaveContext.timer2State = 4; + } else { + gSaveContext.timer2State = 10; + } + } + case 4: + case 10: + if ((gSaveContext.timer2State == 4) || (gSaveContext.timer2State == 10)) { + if (gSaveContext.healthCapacity > 0xA0) { + gSaveContext.timerY[1] = 54; + } else { + gSaveContext.timerY[1] = 46; + } + } + + if (gSaveContext.timer2State >= 3) { + D_8015FFE4--; + if (D_8015FFE4 == 0) { + D_8015FFE4 = 20; + if (gSaveContext.timer2State == 4) { + gSaveContext.timer2Value--; + osSyncPrintf("TOTAL_EVENT_TM=%d\n", gSaveContext.timer2Value); + + if (gSaveContext.timer2Value <= 0) { + if (!Flags_GetSwitch(globalCtx, 0x37) || + ((globalCtx->sceneNum != SCENE_GANON_DEMO) && + (globalCtx->sceneNum != SCENE_GANON_FINAL) && + (globalCtx->sceneNum != SCENE_GANON_SONOGO) && + (globalCtx->sceneNum != SCENE_GANONTIKA_SONOGO))) { + D_8015FFE6 = 40; + gSaveContext.timer2State = 5; + gSaveContext.cutsceneIndex = 0; + Message_StartTextbox(globalCtx, 0x71B0, NULL); + func_8002DF54(globalCtx, NULL, 8); + } else { + D_8015FFE6 = 40; + gSaveContext.timer2State = 6; + } + } else if (gSaveContext.timer2Value > 60) { + if (timerDigits[4] == 1) { + Audio_PlaySoundGeneral(NA_SE_SY_MESSAGE_WOMAN, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + } + } else if (gSaveContext.timer2Value > 10) { + if ((timerDigits[4] & 1)) { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_N, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_E, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + gSaveContext.timer2Value++; + if (gSaveContext.eventInf[1] & 1) { + if (gSaveContext.timer2Value == 240) { + Message_StartTextbox(globalCtx, 0x6083, NULL); + gSaveContext.eventInf[1] &= ~1; + gSaveContext.timer2State = 0; + } + } + } + + if ((gSaveContext.timer2Value % 60) == 0) { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_N, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } + } + break; + case 6: + D_8015FFE6--; + if (D_8015FFE6 == 0) { + gSaveContext.timer2State = 0; + } + break; + } + break; + } + + if (((gSaveContext.timer1State != 0) && (gSaveContext.timer1State != 10)) || + (gSaveContext.timer2State != 0)) { + timerDigits[0] = timerDigits[1] = svar2 = timerDigits[3] = 0; + timerDigits[2] = 10; // digit 10 is used as ':' (colon) + + if (gSaveContext.timer1State != 0) { + timerDigits[4] = gSaveContext.timer1Value; + } else { + timerDigits[4] = gSaveContext.timer2Value; + } + + while (timerDigits[4] >= 60) { + timerDigits[1]++; + if (timerDigits[1] >= 10) { + timerDigits[0]++; + timerDigits[1] -= 10; + } + timerDigits[4] -= 60; + } + + while (timerDigits[4] >= 10) { + timerDigits[3]++; + timerDigits[4] -= 10; + } + + // Clock Icon + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); + svar5 = OTRGetRectDimensionFromLeftEdge(gSaveContext.timerX[svar6]); + svar2 = gSaveContext.timerY[svar6]; + OVERLAY_DISP = + Gfx_TextureIA8(OVERLAY_DISP, gClockIconTex, 16, 16, svar5, svar2 + 2, 16, 16, 1 << 10, 1 << 10); + + // Timer Counter + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, + TEXEL0, 0, PRIMITIVE, 0); + + if (gSaveContext.timer1State != 0) { + if ((gSaveContext.timer1Value < 10) && (gSaveContext.timer1State < 11)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 50, 0, 255); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, 255); + } + } else { + if ((gSaveContext.timer2Value < 10) && (gSaveContext.timer2State < 6)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 50, 0, 255); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 0, 255); + } + } + + for (svar1 = 0; svar1 < 5; svar1++) { + // clang-format off + svar5 = OTRGetRectDimensionFromLeftEdge(gSaveContext.timerX[svar6]); + OVERLAY_DISP = Gfx_TextureI8(OVERLAY_DISP, digitTextures[timerDigits[svar1]], 8, 16, + svar5 + timerDigitLeftPos[svar1], + svar2 = gSaveContext.timerY[svar6], digitWidth[svar1], VREG(42), VREG(43) << 1, + VREG(43) << 1); + // clang-format on + } + } + } + } + + if (pauseCtx->debugState == 3) { + FlagSet_Update(globalCtx); + } + + if (interfaceCtx->unk_244 != 0) { + gDPPipeSync(OVERLAY_DISP++); + gSPDisplayList(OVERLAY_DISP++, sSetupDL_80125A60); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, interfaceCtx->unk_244); + gDPFillRectangle(OVERLAY_DISP++, 0, 0, gScreenWidth - 1, gScreenHeight - 1); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_parameter.c", 4269); +} + +void Interface_Update(GlobalContext* globalCtx) { + static u8 D_80125B60 = 0; + static s16 sPrevTimeIncrement = 0; + MessageContext* msgCtx = &globalCtx->msgCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + Player* player = GET_PLAYER(globalCtx); + s16 alpha; + s16 alpha1; + u16 action; + Input* debugInput = &globalCtx->state.input[2]; + + if (CHECK_BTN_ALL(debugInput->press.button, BTN_DLEFT)) { + gSaveContext.language = LANGUAGE_ENG; + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DUP)) { + gSaveContext.language = LANGUAGE_GER; + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DRIGHT)) { + gSaveContext.language = LANGUAGE_FRA; + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } + + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0)) { + if ((gSaveContext.minigameState == 1) || (gSaveContext.sceneSetupIndex < 4) || + ((globalCtx->sceneNum == SCENE_SPOT20) && (gSaveContext.sceneSetupIndex == 4))) { + if ((msgCtx->msgMode == MSGMODE_NONE) || + ((msgCtx->msgMode != MSGMODE_NONE) && (globalCtx->sceneNum == SCENE_BOWLING))) { + if (globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE) { + func_80083108(globalCtx); + } + } + } + } + + switch (gSaveContext.unk_13E8) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + alpha = 255 - (gSaveContext.unk_13EC << 5); + if (alpha < 0) { + alpha = 0; + } + + func_80082850(globalCtx, alpha); + gSaveContext.unk_13EC++; + + if (alpha == 0) { + gSaveContext.unk_13E8 = 0; + } + break; + case 50: + alpha = 255 - (gSaveContext.unk_13EC << 5); + if (alpha < 0) { + alpha = 0; + } + + alpha1 = 0xFF - alpha; + if (alpha1 >= 0xFF) { + alpha1 = 0xFF; + } + + osSyncPrintf("case 50 : alpha=%d alpha1=%d\n", alpha, alpha1); + func_80082644(globalCtx, alpha1); + + if (interfaceCtx->healthAlpha != 255) { + interfaceCtx->healthAlpha = alpha1; + } + + if (interfaceCtx->magicAlpha != 255) { + interfaceCtx->magicAlpha = alpha1; + } + + switch (globalCtx->sceneNum) { + case SCENE_SPOT00: + case SCENE_SPOT01: + case SCENE_SPOT02: + case SCENE_SPOT03: + case SCENE_SPOT04: + case SCENE_SPOT05: + case SCENE_SPOT06: + case SCENE_SPOT07: + case SCENE_SPOT08: + case SCENE_SPOT09: + case SCENE_SPOT10: + case SCENE_SPOT11: + case SCENE_SPOT12: + case SCENE_SPOT13: + case SCENE_SPOT15: + case SCENE_SPOT16: + case SCENE_SPOT17: + case SCENE_SPOT18: + case SCENE_SPOT20: + case SCENE_GANON_TOU: + if (interfaceCtx->minimapAlpha < 170) { + interfaceCtx->minimapAlpha = alpha1; + } else { + interfaceCtx->minimapAlpha = 170; + } + break; + default: + if (interfaceCtx->minimapAlpha != 255) { + interfaceCtx->minimapAlpha = alpha1; + } + break; + } + + gSaveContext.unk_13EC++; + if (alpha1 == 0xFF) { + gSaveContext.unk_13E8 = 0; + } + + break; + case 52: + gSaveContext.unk_13E8 = 1; + func_80082850(globalCtx, 0); + gSaveContext.unk_13E8 = 0; + default: + break; + } + + Map_Update(globalCtx); + + if (gSaveContext.healthAccumulator != 0) { + gSaveContext.healthAccumulator -= 4; + gSaveContext.health += 4; + + if ((gSaveContext.health & 0xF) < 4) { + Audio_PlaySoundGeneral(NA_SE_SY_HP_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + osSyncPrintf("now_life=%d max_life=%d\n", gSaveContext.health, gSaveContext.healthCapacity); + + if (gSaveContext.health >= gSaveContext.healthCapacity) { + gSaveContext.health = gSaveContext.healthCapacity; + osSyncPrintf("S_Private.now_life=%d S_Private.max_life=%d\n", gSaveContext.health, + gSaveContext.healthCapacity); + gSaveContext.healthAccumulator = 0; + } + } + + HealthMeter_HandleCriticalAlarm(globalCtx); + D_80125A58 = func_8008F2F8(globalCtx); + + if (D_80125A58 == 1) { + if (CUR_EQUIP_VALUE(EQUIP_TUNIC) == 2) { + D_80125A58 = 0; + } + } else if ((func_8008F2F8(globalCtx) >= 2) && (func_8008F2F8(globalCtx) < 5)) { + if (CUR_EQUIP_VALUE(EQUIP_TUNIC) == 3) { + D_80125A58 = 0; + } + } + + HealthMeter_Update(globalCtx); + + if ((gSaveContext.timer1State >= 3) && (globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0) && + (msgCtx->msgMode == MSGMODE_NONE) && !(player->stateFlags2 & 0x01000000) && (globalCtx->sceneLoadFlag == 0) && + (globalCtx->transitionMode == 0) && !Gameplay_InCsMode(globalCtx)) {} + + if (gSaveContext.rupeeAccumulator != 0) { + if (gSaveContext.rupeeAccumulator > 0) { + if (gSaveContext.rupees < CUR_CAPACITY(UPG_WALLET)) { + gSaveContext.rupeeAccumulator--; + gSaveContext.rupees++; + Audio_PlaySoundGeneral(NA_SE_SY_RUPY_COUNT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + // "Rupee Amount MAX = %d" + osSyncPrintf("ルピー数MAX = %d\n", CUR_CAPACITY(UPG_WALLET)); + gSaveContext.rupees = CUR_CAPACITY(UPG_WALLET); + gSaveContext.rupeeAccumulator = 0; + } + } else if (gSaveContext.rupees != 0) { + if (gSaveContext.rupeeAccumulator <= -50) { + gSaveContext.rupeeAccumulator += 10; + gSaveContext.rupees -= 10; + + if (gSaveContext.rupees < 0) { + gSaveContext.rupees = 0; + } + + Audio_PlaySoundGeneral(NA_SE_SY_RUPY_COUNT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + gSaveContext.rupeeAccumulator++; + gSaveContext.rupees--; + Audio_PlaySoundGeneral(NA_SE_SY_RUPY_COUNT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + gSaveContext.rupeeAccumulator = 0; + } + } + + switch (interfaceCtx->unk_1EC) { + case 1: + interfaceCtx->unk_1F4 += 31400.0f / WREG(5); + if (interfaceCtx->unk_1F4 >= 15700.0f) { + interfaceCtx->unk_1F4 = -15700.0f; + interfaceCtx->unk_1EC = 2; + } + break; + case 2: + interfaceCtx->unk_1F4 += 31400.0f / WREG(5); + if (interfaceCtx->unk_1F4 >= 0.0f) { + interfaceCtx->unk_1F4 = 0.0f; + interfaceCtx->unk_1EC = 0; + interfaceCtx->unk_1EE = interfaceCtx->unk_1F0; + action = interfaceCtx->unk_1EE; + if ((action == DO_ACTION_MAX) || (action == DO_ACTION_MAX + 1)) { + action = DO_ACTION_NONE; + } + Interface_LoadActionLabel(interfaceCtx, action, 0); + } + break; + case 3: + interfaceCtx->unk_1F4 += 31400.0f / WREG(5); + if (interfaceCtx->unk_1F4 >= 15700.0f) { + interfaceCtx->unk_1F4 = -15700.0f; + interfaceCtx->unk_1EC = 2; + } + break; + case 4: + interfaceCtx->unk_1F4 += 31400.0f / WREG(5); + if (interfaceCtx->unk_1F4 >= 0.0f) { + interfaceCtx->unk_1F4 = 0.0f; + interfaceCtx->unk_1EC = 0; + interfaceCtx->unk_1EE = interfaceCtx->unk_1F0; + action = interfaceCtx->unk_1EE; + if ((action == DO_ACTION_MAX) || (action == DO_ACTION_MAX + 1)) { + action = DO_ACTION_NONE; + } + Interface_LoadActionLabel(interfaceCtx, action, 0); + } + break; + } + + WREG(7) = interfaceCtx->unk_1F4; + + if ((globalCtx->pauseCtx.state == 0) && (globalCtx->pauseCtx.debugState == 0) && + (msgCtx->msgMode == MSGMODE_NONE) && (globalCtx->sceneLoadFlag == 0) && + (globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE) && (globalCtx->transitionMode == 0) && + ((globalCtx->csCtx.state == CS_STATE_IDLE) || !Player_InCsMode(globalCtx))) { + if ((gSaveContext.magicAcquired != 0) && (gSaveContext.magicLevel == 0)) { + gSaveContext.magicLevel = gSaveContext.doubleMagic + 1; + gSaveContext.unk_13F0 = 8; + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("魔法スター─────ト!!!!!!!!!\n"); // "Magic Start!!!!!!!!!" + osSyncPrintf("MAGIC_MAX=%d\n", gSaveContext.magicLevel); + osSyncPrintf("MAGIC_NOW=%d\n", gSaveContext.magic); + osSyncPrintf("Z_MAGIC_NOW_NOW=%d\n", gSaveContext.unk_13F6); + osSyncPrintf("Z_MAGIC_NOW_MAX=%d\n", gSaveContext.unk_13F4); + osSyncPrintf(VT_RST); + } + + Interface_UpdateMagicBar(globalCtx); + } + + if (gSaveContext.timer1State == 0) { + if (((D_80125A58 == 1) || (D_80125A58 == 2) || (D_80125A58 == 4)) && ((gSaveContext.health >> 1) != 0)) { + gSaveContext.timer1State = 1; + gSaveContext.timerX[0] = 140; + gSaveContext.timerY[0] = 80; + D_80125A5C = 1; + } + } else { + if (((D_80125A58 == 0) || (D_80125A58 == 3)) && (gSaveContext.timer1State < 5)) { + gSaveContext.timer1State = 0; + } + } + + if (gSaveContext.minigameState == 1) { + gSaveContext.minigameScore += interfaceCtx->unk_23C; + interfaceCtx->unk_23C = 0; + + if (sHBAScoreTier == 0) { + if (gSaveContext.minigameScore >= 1000) { + sHBAScoreTier++; + } + } else if (sHBAScoreTier == 1) { + if (gSaveContext.minigameScore >= 1500) { + sHBAScoreTier++; + } + } + + sHBAScoreDigits[0] = sHBAScoreDigits[1] = 0; + sHBAScoreDigits[2] = 0; + sHBAScoreDigits[3] = gSaveContext.minigameScore; + + while (sHBAScoreDigits[3] >= 1000) { + sHBAScoreDigits[0]++; + sHBAScoreDigits[3] -= 1000; + } + + while (sHBAScoreDigits[3] >= 100) { + sHBAScoreDigits[1]++; + sHBAScoreDigits[3] -= 100; + } + + while (sHBAScoreDigits[3] >= 10) { + sHBAScoreDigits[2]++; + sHBAScoreDigits[3] -= 10; + } + } + + if (gSaveContext.sunsSongState != SUNSSONG_INACTIVE) { + // exit out of ocarina mode after suns song finishes playing + if ((msgCtx->ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE) && + (gSaveContext.sunsSongState == SUNSSONG_START)) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } + + // handle suns song in areas where time moves + if (globalCtx->envCtx.timeIncrement != 0) { + if (gSaveContext.sunsSongState != SUNSSONG_SPEED_TIME) { + D_80125B60 = 0; + if ((gSaveContext.dayTime >= 0x4555) && (gSaveContext.dayTime <= 0xC001)) { + D_80125B60 = 1; + } + + gSaveContext.sunsSongState = SUNSSONG_SPEED_TIME; + sPrevTimeIncrement = gTimeIncrement; + gTimeIncrement = 400; + } else if (D_80125B60 == 0) { + if ((gSaveContext.dayTime >= 0x4555) && (gSaveContext.dayTime <= 0xC001)) { + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + gTimeIncrement = sPrevTimeIncrement; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } + } else if (gSaveContext.dayTime > 0xC001) { + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + gTimeIncrement = sPrevTimeIncrement; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } + } else if ((globalCtx->roomCtx.curRoom.unk_03 != 1) && (interfaceCtx->restrictions.sunsSong != 3)) { + if ((gSaveContext.dayTime >= 0x4555) && (gSaveContext.dayTime < 0xC001)) { + gSaveContext.nextDayTime = 0; + globalCtx->fadeTransition = 4; + gSaveContext.nextTransition = 2; + globalCtx->unk_11DE9 = 1; + } else { + gSaveContext.nextDayTime = 0x8001; + globalCtx->fadeTransition = 5; + gSaveContext.nextTransition = 3; + globalCtx->unk_11DE9 = 1; + } + + if (globalCtx->sceneNum == SCENE_SPOT13) { + globalCtx->fadeTransition = 14; + gSaveContext.nextTransition = 14; + } + + gSaveContext.respawnFlag = -2; + globalCtx->nextEntranceIndex = gSaveContext.entranceIndex; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + func_800F6964(30); + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + } else { + gSaveContext.sunsSongState = SUNSSONG_SPECIAL; + } + } +} diff --git a/soh/src/code/z_path.c b/soh/src/code/z_path.c new file mode 100644 index 000000000..8ff42c07a --- /dev/null +++ b/soh/src/code/z_path.c @@ -0,0 +1,45 @@ +#include "global.h" + +Path* Path_GetByIndex(GlobalContext* globalCtx, s16 index, s16 max) { + Path* path; + + if (index != max) { + path = &globalCtx->setupPathList[index]; + } else { + path = NULL; + } + + return path; +} + +f32 Path_OrientAndGetDistSq(Actor* actor, Path* path, s16 waypoint, s16* yaw) { + f32 dx; + f32 dz; + Vec3s* pointPos; + + if (path == NULL) { + return -1.0; + } + + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos = &pointPos[waypoint]; + + dx = pointPos->x - actor->world.pos.x; + dz = pointPos->z - actor->world.pos.z; + + *yaw = Math_FAtan2F(dx, dz) * (32768 / M_PI); + + return SQ(dx) + SQ(dz); +} + +void Path_CopyLastPoint(Path* path, Vec3f* dest) { + Vec3s* pointPos; + + if (path != NULL) { + pointPos = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[path->count - 1]; + + dest->x = pointPos->x; + dest->y = pointPos->y; + dest->z = pointPos->z; + } +} diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c new file mode 100644 index 000000000..f0880b3da --- /dev/null +++ b/soh/src/code/z_play.c @@ -0,0 +1,1862 @@ +#include "global.h" +#include "vt.h" + +#include + +#include "soh/Enhancements/gameconsole.h" + +void* D_8012D1F0 = NULL; +//UNK_TYPE D_8012D1F4 = 0; // unused +Input* D_8012D1F8 = NULL; + +TransitionUnk sTrnsnUnk; +s32 gTrnsnUnkState; +VisMono D_80161498; +Color_RGBA8_u32 D_801614B0; +FaultClient D_801614B8; +s16 D_801614C8; +#if 0 +u64 D_801614D0[0xA00]; +#endif + +GlobalContext* gGlobalCtx; + +void func_800BC450(GlobalContext* globalCtx) { + Camera_ChangeDataIdx(GET_ACTIVE_CAM(globalCtx), globalCtx->unk_1242B - 1); +} + +void func_800BC490(GlobalContext* globalCtx, s16 point) { + ASSERT(point == 1 || point == 2, "point == 1 || point == 2", "../z_play.c", 2160); + + globalCtx->unk_1242B = point; + + if ((YREG(15) != 0x10) && (gSaveContext.cutsceneIndex < 0xFFF0)) { + Audio_PlaySoundGeneral((point == 1) ? NA_SE_SY_CAMERA_ZOOM_DOWN : NA_SE_SY_CAMERA_ZOOM_UP, &D_801333D4, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + } + + func_800BC450(globalCtx); +} + +s32 func_800BC56C(GlobalContext* globalCtx, s16 arg1) { + return (arg1 == globalCtx->unk_1242B); +} + +// original name: "Game_play_shop_pr_vr_switch_set" +void func_800BC590(GlobalContext* globalCtx) { + osSyncPrintf("Game_play_shop_pr_vr_switch_set()\n"); + + if (YREG(15) == 0x10) { + globalCtx->unk_1242B = 2; + } +} + +void func_800BC5E0(GlobalContext* globalCtx, s32 transitionType) { + TransitionContext* transitionCtx = &globalCtx->transitionCtx; + + memset(transitionCtx,0, sizeof(TransitionContext)); + + transitionCtx->transitionType = transitionType; + + if ((transitionCtx->transitionType >> 5) == 1) { + transitionCtx->init = TransitionCircle_Init; + transitionCtx->destroy = TransitionCircle_Destroy; + transitionCtx->start = TransitionCircle_Start; + transitionCtx->isDone = TransitionCircle_IsDone; + transitionCtx->draw = TransitionCircle_Draw; + transitionCtx->update = TransitionCircle_Update; + transitionCtx->setType = TransitionCircle_SetType; + transitionCtx->setColor = TransitionCircle_SetColor; + transitionCtx->setEnvColor = TransitionCircle_SetEnvColor; + } else { + switch (transitionCtx->transitionType) { + case 1: + transitionCtx->init = TransitionTriforce_Init; + transitionCtx->destroy = TransitionTriforce_Destroy; + transitionCtx->start = TransitionTriforce_Start; + transitionCtx->isDone = TransitionTriforce_IsDone; + transitionCtx->draw = TransitionTriforce_Draw; + transitionCtx->update = TransitionTriforce_Update; + transitionCtx->setType = TransitionTriforce_SetType; + transitionCtx->setColor = TransitionTriforce_SetColor; + transitionCtx->setEnvColor = NULL; + break; + case 0: + case 8: + transitionCtx->init = TransitionWipe_Init; + transitionCtx->destroy = TransitionWipe_Destroy; + transitionCtx->start = TransitionWipe_Start; + transitionCtx->isDone = TransitionWipe_IsDone; + transitionCtx->draw = TransitionWipe_Draw; + transitionCtx->update = TransitionWipe_Update; + transitionCtx->setType = TransitionWipe_SetType; + transitionCtx->setColor = TransitionWipe_SetColor; + transitionCtx->setEnvColor = NULL; + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 13: + case 17: + case 18: + case 19: + transitionCtx->init = TransitionFade_Init; + transitionCtx->destroy = TransitionFade_Destroy; + transitionCtx->start = TransitionFade_Start; + transitionCtx->isDone = TransitionFade_IsDone; + transitionCtx->draw = TransitionFade_Draw; + transitionCtx->update = TransitionFade_Update; + transitionCtx->setType = TransitionFade_SetType; + transitionCtx->setColor = TransitionFade_SetColor; + transitionCtx->setEnvColor = NULL; + break; + case 9: + case 10: + globalCtx->transitionMode = 4; + break; + case 11: + globalCtx->transitionMode = 10; + break; + case 12: + globalCtx->transitionMode = 7; + break; + case 14: + globalCtx->transitionMode = 12; + break; + case 15: + globalCtx->transitionMode = 14; + break; + case 16: + globalCtx->transitionMode = 16; + break; + default: + Fault_AddHungupAndCrash("../z_play.c", 2290); + break; + } + } +} + +void func_800BC88C(GlobalContext* globalCtx) { + globalCtx->transitionCtx.transitionType = -1; +} + +Gfx* Gameplay_SetFog(GlobalContext* globalCtx, Gfx* gfx) { + return Gfx_SetFog2(gfx, globalCtx->lightCtx.fogColor[0], globalCtx->lightCtx.fogColor[1], + globalCtx->lightCtx.fogColor[2], 0, globalCtx->lightCtx.fogNear, 1000); +} + +void Gameplay_Destroy(GameState* thisx) { + GlobalContext* globalCtx = (GlobalContext*)thisx; + Player* player = GET_PLAYER(globalCtx); + + globalCtx->state.gfxCtx->callback = NULL; + globalCtx->state.gfxCtx->callbackParam = 0; + SREG(91) = 0; + R_PAUSE_MENU_MODE = 0; + + PreRender_Destroy(&globalCtx->pauseBgPreRender); + Effect_DeleteAll(globalCtx); + EffectSs_ClearAll(globalCtx); + CollisionCheck_DestroyContext(globalCtx, &globalCtx->colChkCtx); + + if (gTrnsnUnkState == 3) { + TransitionUnk_Destroy(&sTrnsnUnk); + gTrnsnUnkState = 0; + } + + if (globalCtx->transitionMode == 3) { + globalCtx->transitionCtx.destroy(&globalCtx->transitionCtx.data); + func_800BC88C(globalCtx); + globalCtx->transitionMode = 0; + } + + ShrinkWindow_Destroy(); + TransitionFade_Destroy(&globalCtx->transitionFade); + VisMono_Destroy(&D_80161498); + + if (gSaveContext.linkAge != globalCtx->linkAgeOnLoad) { + Inventory_SwapAgeEquipment(); + Player_SetEquipmentData(globalCtx, player); + } + + func_80031C3C(&globalCtx->actorCtx, globalCtx); + func_80110990(globalCtx); + KaleidoScopeCall_Destroy(globalCtx); + KaleidoManager_Destroy(); + ZeldaArena_Cleanup(); + Fault_RemoveClient(&D_801614B8); + gGlobalCtx = NULL; +} + +void Gameplay_Init(GameState* thisx) { + GlobalContext* globalCtx = (GlobalContext*)thisx; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + gGlobalCtx = globalCtx; + //globalCtx->state.gfxCtx = NULL; + u32 zAlloc; + u32 zAllocAligned; + size_t zAllocSize; + Player* player; + s32 playerStartCamId; + s32 i; + u8 tempSetupIndex; + s32 pad[2]; + + if (gSaveContext.entranceIndex == -1) { + gSaveContext.entranceIndex = 0; + globalCtx->state.running = false; + SET_NEXT_GAMESTATE(&globalCtx->state, Opening_Init, OpeningContext); + return; + } + + SystemArena_Display(); + // OTRTODO allocate double the normal amount of memory + // This is to avoid some parts of the game, like loading actors, causing OoM + // This is potionally unavoidable due to struct size differences, but is x2 the right amount? + GameState_Realloc(&globalCtx->state, 0x1D4790 * 2); + KaleidoManager_Init(globalCtx); + View_Init(&globalCtx->view, gfxCtx); + Audio_SetExtraFilter(0); + Quake_Init(); + + for (i = 0; i < 4; i++) { + globalCtx->cameraPtrs[i] = NULL; + } + + Camera_Init(&globalCtx->mainCamera, &globalCtx->view, &globalCtx->colCtx, globalCtx); + Camera_ChangeStatus(&globalCtx->mainCamera, CAM_STAT_ACTIVE); + + for (i = 0; i < 3; i++) { + Camera_Init(&globalCtx->subCameras[i], &globalCtx->view, &globalCtx->colCtx, globalCtx); + Camera_ChangeStatus(&globalCtx->subCameras[i], CAM_STAT_UNK100); + } + + globalCtx->cameraPtrs[MAIN_CAM] = &globalCtx->mainCamera; + globalCtx->cameraPtrs[MAIN_CAM]->uid = 0; + globalCtx->activeCamera = MAIN_CAM; + func_8005AC48(&globalCtx->mainCamera, 0xFF); + Sram_Init(globalCtx, &globalCtx->sramCtx); + func_80112098(globalCtx); + Message_Init(globalCtx); + GameOver_Init(globalCtx); + SoundSource_InitAll(globalCtx); + Effect_InitContext(globalCtx); + EffectSs_InitInfo(globalCtx, 0x55); + CollisionCheck_InitContext(globalCtx, &globalCtx->colChkCtx); + AnimationContext_Reset(&globalCtx->animationCtx); + func_8006450C(globalCtx, &globalCtx->csCtx); + + if (gSaveContext.nextCutsceneIndex != 0xFFEF) { + gSaveContext.cutsceneIndex = gSaveContext.nextCutsceneIndex; + gSaveContext.nextCutsceneIndex = 0xFFEF; + } + + if (gSaveContext.cutsceneIndex == 0xFFFD) { + gSaveContext.cutsceneIndex = 0; + } + + if (gSaveContext.nextDayTime != 0xFFFF) { + gSaveContext.dayTime = gSaveContext.nextDayTime; + gSaveContext.skyboxTime = gSaveContext.nextDayTime; + } + + if (gSaveContext.dayTime > 0xC000 || gSaveContext.dayTime < 0x4555) { + gSaveContext.nightFlag = 1; + } else { + gSaveContext.nightFlag = 0; + } + + Cutscene_HandleConditionalTriggers(globalCtx); + + if (gSaveContext.gameMode != 0 || gSaveContext.cutsceneIndex >= 0xFFF0) { + gSaveContext.nayrusLoveTimer = 0; + func_800876C8(globalCtx); + gSaveContext.sceneSetupIndex = (gSaveContext.cutsceneIndex & 0xF) + 4; + } else if (!LINK_IS_ADULT && IS_DAY) { + gSaveContext.sceneSetupIndex = 0; + } else if (!LINK_IS_ADULT && !IS_DAY) { + gSaveContext.sceneSetupIndex = 1; + } else if (LINK_IS_ADULT && IS_DAY) { + gSaveContext.sceneSetupIndex = 2; + } else { + gSaveContext.sceneSetupIndex = 3; + } + + tempSetupIndex = gSaveContext.sceneSetupIndex; + if ((gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_SPOT00) && !LINK_IS_ADULT && + gSaveContext.sceneSetupIndex < 4) { + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY) && + CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + gSaveContext.sceneSetupIndex = 1; + } else { + gSaveContext.sceneSetupIndex = 0; + } + } else if ((gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_SPOT04) && LINK_IS_ADULT && + gSaveContext.sceneSetupIndex < 4) { + gSaveContext.sceneSetupIndex = (gSaveContext.eventChkInf[4] & 0x100) ? 3 : 2; + } + + Gameplay_SpawnScene( + globalCtx, + gEntranceTable[((void)0, gSaveContext.entranceIndex) + ((void)0, gSaveContext.sceneSetupIndex)].scene, + gEntranceTable[((void)0, gSaveContext.sceneSetupIndex) + ((void)0, gSaveContext.entranceIndex)].spawn); + osSyncPrintf("\nSCENE_NO=%d COUNTER=%d\n", ((void)0, gSaveContext.entranceIndex), gSaveContext.sceneSetupIndex); + + Cutscene_HandleEntranceTriggers(globalCtx); + KaleidoScopeCall_Init(globalCtx); + func_801109B0(globalCtx); + + if (gSaveContext.nextDayTime != 0xFFFF) { + if (gSaveContext.nextDayTime == 0x8001) { + gSaveContext.totalDays++; + gSaveContext.bgsDayCount++; + gSaveContext.dogIsLost = true; + if (Inventory_ReplaceItem(globalCtx, ITEM_WEIRD_EGG, ITEM_CHICKEN) || + Inventory_ReplaceItem(globalCtx, ITEM_POCKET_EGG, ITEM_POCKET_CUCCO)) { + Message_StartTextbox(globalCtx, 0x3066, NULL); + } + gSaveContext.nextDayTime = 0xFFFE; + } else { + gSaveContext.nextDayTime = 0xFFFD; + } + } + + SREG(91) = -1; + R_PAUSE_MENU_MODE = 0; + PreRender_Init(&globalCtx->pauseBgPreRender); + PreRender_SetValuesSave(&globalCtx->pauseBgPreRender, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0); + PreRender_SetValues(&globalCtx->pauseBgPreRender, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); + gTrnsnUnkState = 0; + globalCtx->transitionMode = 0; + FrameAdvance_Init(&globalCtx->frameAdvCtx); + Rand_Seed((u32)osGetTime()); + Matrix_Init(&globalCtx->state); + globalCtx->state.main = Gameplay_Main; + globalCtx->state.destroy = Gameplay_Destroy; + globalCtx->sceneLoadFlag = -0x14; + globalCtx->unk_11E16 = 0xFF; + globalCtx->unk_11E18 = 0; + globalCtx->unk_11DE9 = 0; + + if (gSaveContext.gameMode != 1) { + if (gSaveContext.nextTransition == 0xFF) { + globalCtx->fadeTransition = + (gEntranceTable[((void)0, gSaveContext.entranceIndex) + tempSetupIndex].field >> 7) & 0x7F; // Fade In + } else { + globalCtx->fadeTransition = gSaveContext.nextTransition; + gSaveContext.nextTransition = 0xFF; + } + } else { + globalCtx->fadeTransition = 6; + } + + ShrinkWindow_Init(); + TransitionFade_Init(&globalCtx->transitionFade); + TransitionFade_SetType(&globalCtx->transitionFade, 3); + TransitionFade_SetColor(&globalCtx->transitionFade, RGBA8(160, 160, 160, 255)); + TransitionFade_Start(&globalCtx->transitionFade); + VisMono_Init(&D_80161498); + D_801614B0.a = 0; + Flags_UnsetAllEnv(globalCtx); + + osSyncPrintf("ZELDA ALLOC SIZE=%x\n", THA_GetSize(&globalCtx->state.tha)); + zAllocSize = THA_GetSize(&globalCtx->state.tha); + zAlloc = GameState_Alloc(&globalCtx->state, zAllocSize, "../z_play.c", 2918); + zAllocAligned = (zAlloc + 8) & ~0xF; + ZeldaArena_Init(zAllocAligned, zAllocSize - zAllocAligned + zAlloc); + // "Zelda Heap" + osSyncPrintf("ゼルダヒープ %08x-%08x\n", zAllocAligned, + (s32)(zAllocAligned + zAllocSize) - (s32)(zAllocAligned - zAlloc)); + + Fault_AddClient(&D_801614B8, ZeldaArena_Display, NULL, NULL); + func_800304DC(globalCtx, &globalCtx->actorCtx, globalCtx->linkActorEntry); + + while (!func_800973FC(globalCtx, &globalCtx->roomCtx)) { + ; // Empty Loop + } + + player = GET_PLAYER(globalCtx); + Camera_InitPlayerSettings(&globalCtx->mainCamera, player); + Camera_ChangeMode(&globalCtx->mainCamera, CAM_MODE_NORMAL); + + playerStartCamId = player->actor.params & 0xFF; + if (playerStartCamId != 0xFF) { + osSyncPrintf("player has start camera ID (" VT_FGCOL(BLUE) "%d" VT_RST ")\n", playerStartCamId); + Camera_ChangeDataIdx(&globalCtx->mainCamera, playerStartCamId); + } + + if (YREG(15) == 32) { + globalCtx->unk_1242B = 2; + } else if (YREG(15) == 16) { + globalCtx->unk_1242B = 1; + } else { + globalCtx->unk_1242B = 0; + } + + Interface_SetSceneRestrictions(globalCtx); + Environment_PlaySceneSequence(globalCtx); + gSaveContext.seqId = globalCtx->sequenceCtx.seqId; + gSaveContext.natureAmbienceId = globalCtx->sequenceCtx.natureAmbienceId; + func_8002DF18(globalCtx, GET_PLAYER(globalCtx)); + AnimationContext_Update(globalCtx, &globalCtx->animationCtx); + gSaveContext.respawnFlag = 0; + #if 0 + if (dREG(95) != 0) { + D_8012D1F0 = D_801614D0; + osSyncPrintf("\nkawauso_data=[%x]", D_8012D1F0); + DmaMgr_DmaRomToRam(0x03FEB000, D_8012D1F0, sizeof(D_801614D0)); + } + #endif +} + +void Gameplay_Update(GlobalContext* globalCtx) { + s32 pad1; + s32 sp80; + Input* input; + u32 i; + s32 pad2; + + input = globalCtx->state.input; + + if ((SREG(1) < 0) || (DREG(0) != 0)) { + SREG(1) = 0; + ZeldaArena_Display(); + } + + if ((HREG(80) == 18) && (HREG(81) < 0)) { + HREG(81) = 0; + osSyncPrintf("object_exchange_rom_address %u\n", gObjectTableSize); + osSyncPrintf("RomStart RomEnd Size\n"); + for (i = 0; i < gObjectTableSize; i++) { + ptrdiff_t size = gObjectTable[i].vromEnd - gObjectTable[i].vromStart; + + osSyncPrintf("%08x-%08x %08x(%8.3fKB)\n", gObjectTable[i].vromStart, gObjectTable[i].vromEnd, size, + size / 1024.0f); + } + osSyncPrintf("\n"); + } + + if ((HREG(81) == 18) && (HREG(82) < 0)) { + HREG(82) = 0; + ActorOverlayTable_LogPrint(); + } + + gSegments[4] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.mainKeepIndex].segment); + gSegments[5] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); + gSegments[2] = VIRTUAL_TO_PHYSICAL(globalCtx->sceneSegment); + + if (FrameAdvance_Update(&globalCtx->frameAdvCtx, &input[1])) { + if ((globalCtx->transitionMode == 0) && (globalCtx->sceneLoadFlag != 0)) { + globalCtx->transitionMode = 1; + } + + if (gTrnsnUnkState != 0) { + switch (gTrnsnUnkState) { + case 2: + if (TransitionUnk_Init(&sTrnsnUnk, 10, 7) == NULL) { + osSyncPrintf("fbdemo_init呼出し失敗!\n"); // "fbdemo_init call failed!" + gTrnsnUnkState = 0; + } else { + sTrnsnUnk.zBuffer = (u16*)gZBuffer; + gTrnsnUnkState = 3; + R_UPDATE_RATE = 1; + } + break; + case 3: + func_800B23E8(&sTrnsnUnk); + break; + } + } + + if (globalCtx->transitionMode) { + switch (globalCtx->transitionMode) { + case 1: + if (globalCtx->sceneLoadFlag != -0x14) { + s16 sp6E = 0; + Interface_ChangeAlpha(1); + + if (gSaveContext.cutsceneIndex >= 0xFFF0) { + sp6E = (gSaveContext.cutsceneIndex & 0xF) + 4; + } + + if (!(gEntranceTable[globalCtx->nextEntranceIndex + sp6E].field & 0x8000)) { // Continue BGM Off + // "Sound initalized. 111" + osSyncPrintf("\n\n\nサウンドイニシャル来ました。111"); + if ((globalCtx->fadeTransition < 56) && !Environment_IsForcedSequenceDisabled()) { + // "Sound initalized. 222" + osSyncPrintf("\n\n\nサウンドイニシャル来ました。222"); + func_800F6964(0x14); + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + } + } + } + + if (CREG(11) == 0) { + func_800BC5E0(globalCtx, globalCtx->fadeTransition); + } else { + func_800BC5E0(globalCtx, CREG(12)); + } + + if (globalCtx->transitionMode >= 4) { + break; + } + + case 2: + globalCtx->transitionCtx.init(&globalCtx->transitionCtx.data); + + if ((globalCtx->transitionCtx.transitionType >> 5) == 1) { + globalCtx->transitionCtx.setType(&globalCtx->transitionCtx.data, + globalCtx->transitionCtx.transitionType | 0x80); + } + + gSaveContext.unk_1419 = 14; + if ((globalCtx->transitionCtx.transitionType == 8) || + (globalCtx->transitionCtx.transitionType == 9)) { + gSaveContext.unk_1419 = 28; + } + + gSaveContext.fadeDuration = 60; + if ((globalCtx->transitionCtx.transitionType == 4) || + (globalCtx->transitionCtx.transitionType == 5)) { + gSaveContext.fadeDuration = 20; + } else if ((globalCtx->transitionCtx.transitionType == 6) || + (globalCtx->transitionCtx.transitionType == 7)) { + gSaveContext.fadeDuration = 150; + } else if (globalCtx->transitionCtx.transitionType == 17) { + gSaveContext.fadeDuration = 2; + } + + if ((globalCtx->transitionCtx.transitionType == 3) || + (globalCtx->transitionCtx.transitionType == 5) || + (globalCtx->transitionCtx.transitionType == 7) || + (globalCtx->transitionCtx.transitionType == 13) || + (globalCtx->transitionCtx.transitionType == 17)) { + globalCtx->transitionCtx.setColor(&globalCtx->transitionCtx.data, RGBA8(160, 160, 160, 255)); + if (globalCtx->transitionCtx.setEnvColor != NULL) { + globalCtx->transitionCtx.setEnvColor(&globalCtx->transitionCtx.data, + RGBA8(160, 160, 160, 255)); + } + } else if (globalCtx->transitionCtx.transitionType == 18) { + globalCtx->transitionCtx.setColor(&globalCtx->transitionCtx.data, RGBA8(140, 140, 100, 255)); + if (globalCtx->transitionCtx.setEnvColor != NULL) { + globalCtx->transitionCtx.setEnvColor(&globalCtx->transitionCtx.data, + RGBA8(140, 140, 100, 255)); + } + } else if (globalCtx->transitionCtx.transitionType == 19) { + globalCtx->transitionCtx.setColor(&globalCtx->transitionCtx.data, RGBA8(70, 100, 110, 255)); + if (globalCtx->transitionCtx.setEnvColor != NULL) { + globalCtx->transitionCtx.setEnvColor(&globalCtx->transitionCtx.data, + RGBA8(70, 100, 110, 255)); + } + } else { + globalCtx->transitionCtx.setColor(&globalCtx->transitionCtx.data, RGBA8(0, 0, 0, 0)); + if (globalCtx->transitionCtx.setEnvColor != NULL) { + globalCtx->transitionCtx.setEnvColor(&globalCtx->transitionCtx.data, RGBA8(0, 0, 0, 0)); + } + } + + if (globalCtx->sceneLoadFlag == -0x14) { + globalCtx->transitionCtx.setType(&globalCtx->transitionCtx.data, 1); + } else { + globalCtx->transitionCtx.setType(&globalCtx->transitionCtx.data, 2); + } + + globalCtx->transitionCtx.start(&globalCtx->transitionCtx); + + if (globalCtx->transitionCtx.transitionType == 13) { + globalCtx->transitionMode = 11; + } else { + globalCtx->transitionMode = 3; + } + break; + + case 3: + if (globalCtx->transitionCtx.isDone(&globalCtx->transitionCtx) != 0) { + if (globalCtx->transitionCtx.transitionType >= 56) { + if (globalCtx->sceneLoadFlag == -0x14) { + globalCtx->transitionCtx.destroy(&globalCtx->transitionCtx); + func_800BC88C(globalCtx); + globalCtx->transitionMode = 0; + } + } else if (globalCtx->sceneLoadFlag != -0x14) { + globalCtx->state.running = 0; + if (gSaveContext.gameMode != 2) { + SET_NEXT_GAMESTATE(&globalCtx->state, Gameplay_Init, GlobalContext); + gSaveContext.entranceIndex = globalCtx->nextEntranceIndex; + if (gSaveContext.minigameState == 1) { + gSaveContext.minigameState = 3; + } + } else { + SET_NEXT_GAMESTATE(&globalCtx->state, FileChoose_Init, FileChooseContext); + } + } else { + globalCtx->transitionCtx.destroy(&globalCtx->transitionCtx); + func_800BC88C(globalCtx); + globalCtx->transitionMode = 0; + if (gTrnsnUnkState == 3) { + TransitionUnk_Destroy(&sTrnsnUnk); + gTrnsnUnkState = 0; + R_UPDATE_RATE = 3; + } + } + globalCtx->sceneLoadFlag = 0; + } else { + globalCtx->transitionCtx.update(&globalCtx->transitionCtx.data, R_UPDATE_RATE); + } + break; + } + + switch (globalCtx->transitionMode) { + case 4: + D_801614C8 = 0; + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = 160; + globalCtx->envCtx.screenFillColor[1] = 160; + globalCtx->envCtx.screenFillColor[2] = 160; + if (globalCtx->sceneLoadFlag != -0x14) { + globalCtx->envCtx.screenFillColor[3] = 0; + globalCtx->transitionMode = 5; + } else { + globalCtx->envCtx.screenFillColor[3] = 255; + globalCtx->transitionMode = 6; + } + break; + + case 5: + globalCtx->envCtx.screenFillColor[3] = (D_801614C8 / 20.0f) * 255.0f; + if (D_801614C8 >= 20 && 1) { + globalCtx->state.running = 0; + SET_NEXT_GAMESTATE(&globalCtx->state, Gameplay_Init, GlobalContext); + gSaveContext.entranceIndex = globalCtx->nextEntranceIndex; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } else { + D_801614C8++; + } + break; + + case 6: + globalCtx->envCtx.screenFillColor[3] = (1 - D_801614C8 / 20.0f) * 255.0f; + if (D_801614C8 >= 20 && 1) { + gTrnsnUnkState = 0; + R_UPDATE_RATE = 3; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + globalCtx->envCtx.fillScreen = false; + } else { + D_801614C8++; + } + break; + + case 7: + D_801614C8 = 0; + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = 170; + globalCtx->envCtx.screenFillColor[1] = 160; + globalCtx->envCtx.screenFillColor[2] = 150; + if (globalCtx->sceneLoadFlag != -0x14) { + globalCtx->envCtx.screenFillColor[3] = 0; + globalCtx->transitionMode = 5; + } else { + globalCtx->envCtx.screenFillColor[3] = 255; + globalCtx->transitionMode = 6; + } + break; + + case 10: + if (globalCtx->sceneLoadFlag != -0x14) { + globalCtx->state.running = 0; + SET_NEXT_GAMESTATE(&globalCtx->state, Gameplay_Init, GlobalContext); + gSaveContext.entranceIndex = globalCtx->nextEntranceIndex; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } else { + gTrnsnUnkState = 0; + R_UPDATE_RATE = 3; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } + break; + + case 11: + if (gSaveContext.unk_1410 != 0) { + globalCtx->transitionMode = 3; + } + break; + + case 12: + if (globalCtx->sceneLoadFlag != -0x14) { + globalCtx->envCtx.sandstormState = 1; + globalCtx->transitionMode = 13; + } else { + globalCtx->envCtx.sandstormState = 2; + globalCtx->envCtx.sandstormPrimA = 255; + globalCtx->envCtx.sandstormEnvA = 255; + globalCtx->transitionMode = 13; + } + break; + + case 13: + Audio_PlaySoundGeneral(NA_SE_EV_SAND_STORM - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (globalCtx->sceneLoadFlag == -0x14) { + if (globalCtx->envCtx.sandstormPrimA < 110) { + gTrnsnUnkState = 0; + R_UPDATE_RATE = 3; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } + } else { + if (globalCtx->envCtx.sandstormEnvA == 255) { + globalCtx->state.running = 0; + SET_NEXT_GAMESTATE(&globalCtx->state, Gameplay_Init, GlobalContext); + gSaveContext.entranceIndex = globalCtx->nextEntranceIndex; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } + } + break; + + case 14: + if (globalCtx->sceneLoadFlag == -0x14) { + globalCtx->envCtx.sandstormState = 4; + globalCtx->envCtx.sandstormPrimA = 255; + globalCtx->envCtx.sandstormEnvA = 255; + // "It's here!!!!!!!!!" + LOG_STRING("来た!!!!!!!!!!!!!!!!!!!!!", "../z_play.c", 3471); + globalCtx->transitionMode = 15; + } else { + globalCtx->transitionMode = 12; + } + break; + + case 15: + Audio_PlaySoundGeneral(NA_SE_EV_SAND_STORM - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (globalCtx->sceneLoadFlag == -0x14) { + if (globalCtx->envCtx.sandstormPrimA <= 0) { + gTrnsnUnkState = 0; + R_UPDATE_RATE = 3; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } + } + break; + + case 16: + D_801614C8 = 0; + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = 0; + globalCtx->envCtx.screenFillColor[1] = 0; + globalCtx->envCtx.screenFillColor[2] = 0; + globalCtx->envCtx.screenFillColor[3] = 255; + globalCtx->transitionMode = 17; + break; + + case 17: + if (gSaveContext.unk_1410 != 0) { + globalCtx->envCtx.screenFillColor[3] = gSaveContext.unk_1410; + if (gSaveContext.unk_1410 < 0x65) { + gTrnsnUnkState = 0; + R_UPDATE_RATE = 3; + globalCtx->sceneLoadFlag = 0; + globalCtx->transitionMode = 0; + } + } + break; + } + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3533); + } + + if (1 && (gTrnsnUnkState != 3)) { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3542); + } + + if ((gSaveContext.gameMode == 0) && (globalCtx->msgCtx.msgMode == MSGMODE_NONE) && + (globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE)) { + KaleidoSetup_Update(globalCtx); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3551); + } + + sp80 = (globalCtx->pauseCtx.state != 0) || (globalCtx->pauseCtx.debugState != 0); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3555); + } + + AnimationContext_Reset(&globalCtx->animationCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3561); + } + + Object_UpdateBank(&globalCtx->objectCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3577); + } + + if ((sp80 == 0) && (IREG(72) == 0)) { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3580); + } + + globalCtx->gameplayFrames++; + + func_800AA178(1); + + if (globalCtx->actorCtx.freezeFlashTimer && (globalCtx->actorCtx.freezeFlashTimer-- < 5)) { + osSyncPrintf("FINISH=%d\n", globalCtx->actorCtx.freezeFlashTimer); + if ((globalCtx->actorCtx.freezeFlashTimer > 0) && + ((globalCtx->actorCtx.freezeFlashTimer % 2) != 0)) { + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] = + globalCtx->envCtx.screenFillColor[2] = 150; + globalCtx->envCtx.screenFillColor[3] = 80; + } else { + globalCtx->envCtx.fillScreen = false; + } + } else { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3606); + } + + func_800973FC(globalCtx, &globalCtx->roomCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3612); + } + + CollisionCheck_AT(globalCtx, &globalCtx->colChkCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3618); + } + + CollisionCheck_OC(globalCtx, &globalCtx->colChkCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3624); + } + + CollisionCheck_Damage(globalCtx, &globalCtx->colChkCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3631); + } + + CollisionCheck_ClearContext(globalCtx, &globalCtx->colChkCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3637); + } + + if (globalCtx->unk_11DE9 == 0) { + Actor_UpdateAll(globalCtx, &globalCtx->actorCtx); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3643); + } + + func_80064558(globalCtx, &globalCtx->csCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3648); + } + + func_800645A0(globalCtx, &globalCtx->csCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3651); + } + + Effect_UpdateAll(globalCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3657); + } + + EffectSs_UpdateAll(globalCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3662); + } + } + } else { + func_800AA178(0); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3672); + } + + func_80095AA0(globalCtx, &globalCtx->roomCtx.curRoom, &input[1], 0); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3675); + } + + func_80095AA0(globalCtx, &globalCtx->roomCtx.prevRoom, &input[1], 1); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3677); + } + + if (globalCtx->unk_1242B != 0) { + if (CHECK_BTN_ALL(input[0].press.button, BTN_CUP)) { + if ((globalCtx->pauseCtx.state != 0) || (globalCtx->pauseCtx.debugState != 0)) { + // "Changing viewpoint is prohibited due to the kaleidoscope" + osSyncPrintf(VT_FGCOL(CYAN) "カレイドスコープ中につき視点変更を禁止しております\n" VT_RST); + } else if (Player_InCsMode(globalCtx)) { + // "Changing viewpoint is prohibited during the cutscene" + osSyncPrintf(VT_FGCOL(CYAN) "デモ中につき視点変更を禁止しております\n" VT_RST); + } else if (YREG(15) == 0x10) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + func_800BC490(globalCtx, globalCtx->unk_1242B ^ 3); + } + } + func_800BC450(globalCtx); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3708); + } + + SkyboxDraw_Update(&globalCtx->skyboxCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3716); + } + + if ((globalCtx->pauseCtx.state != 0) || (globalCtx->pauseCtx.debugState != 0)) { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3721); + } + + KaleidoScopeCall_Update(globalCtx); + } else if (globalCtx->gameOverCtx.state != GAMEOVER_INACTIVE) { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3727); + } + + GameOver_Update(globalCtx); + } else { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3733); + } + + Message_Update(globalCtx); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3737); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3742); + } + + Interface_Update(globalCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3765); + } + + AnimationContext_Update(globalCtx, &globalCtx->animationCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3771); + } + + SoundSource_UpdateAll(globalCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3777); + } + + ShrinkWindow_Update(R_UPDATE_RATE); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3783); + } + + TransitionFade_Update(&globalCtx->transitionFade, R_UPDATE_RATE); + } else { + goto skip; + } + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3799); + } + +skip: + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3801); + } + + if ((sp80 == 0) || (gDbgCamEnabled)) { + s32 pad3[5]; + s32 i; + + globalCtx->nextCamera = globalCtx->activeCamera; + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3806); + } + + for (i = 0; i < NUM_CAMS; i++) { + if ((i != globalCtx->nextCamera) && (globalCtx->cameraPtrs[i] != NULL)) { + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3809); + } + + Camera_Update(globalCtx->cameraPtrs[i]); + } + } + + Camera_Update(globalCtx->cameraPtrs[globalCtx->nextCamera]); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3814); + } + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 3816); + } + + Environment_Update(globalCtx, &globalCtx->envCtx, &globalCtx->lightCtx, &globalCtx->pauseCtx, &globalCtx->msgCtx, + &globalCtx->gameOverCtx, globalCtx->state.gfxCtx); +} + +void Gameplay_DrawOverlayElements(GlobalContext* globalCtx) { + if ((globalCtx->pauseCtx.state != 0) || (globalCtx->pauseCtx.debugState != 0)) { + KaleidoScopeCall_Draw(globalCtx); + } + + if (gSaveContext.gameMode == 0) { + Interface_Draw(globalCtx); + } + + Message_Draw(globalCtx); + + if (globalCtx->gameOverCtx.state != GAMEOVER_INACTIVE) { + GameOver_FadeInLights(globalCtx); + } +} + +void Gameplay_Draw(GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + Lights* sp228; + Vec3f sp21C; + + OPEN_DISPS(gfxCtx, "../z_play.c", 3907); + + gSegments[4] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.mainKeepIndex].segment); + gSegments[5] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); + gSegments[2] = VIRTUAL_TO_PHYSICAL(globalCtx->sceneSegment); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + gSPSegment(POLY_XLU_DISP++, 0x00, NULL); + gSPSegment(OVERLAY_DISP++, 0x00, NULL); + + gSPSegment(POLY_OPA_DISP++, 0x04, globalCtx->objectCtx.status[globalCtx->objectCtx.mainKeepIndex].segment); + gSPSegment(POLY_XLU_DISP++, 0x04, globalCtx->objectCtx.status[globalCtx->objectCtx.mainKeepIndex].segment); + gSPSegment(OVERLAY_DISP++, 0x04, globalCtx->objectCtx.status[globalCtx->objectCtx.mainKeepIndex].segment); + + gSPSegment(POLY_OPA_DISP++, 0x05, globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); + gSPSegment(POLY_XLU_DISP++, 0x05, globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); + gSPSegment(OVERLAY_DISP++, 0x05, globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); + + gSPSegment(POLY_OPA_DISP++, 0x02, globalCtx->sceneSegment); + gSPSegment(POLY_XLU_DISP++, 0x02, globalCtx->sceneSegment); + gSPSegment(OVERLAY_DISP++, 0x02, globalCtx->sceneSegment); + + func_80095248(gfxCtx, 0, 0, 0); + + if ((HREG(80) != 10) || (HREG(82) != 0)) { + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + POLY_XLU_DISP = Gameplay_SetFog(globalCtx, POLY_XLU_DISP); + POLY_KAL_DISP = Gameplay_SetFog(globalCtx, POLY_KAL_DISP); + + func_800AA460(&globalCtx->view, globalCtx->view.fovy, globalCtx->view.zNear, globalCtx->lightCtx.fogFar); + func_800AAA50(&globalCtx->view, 15); + + // The billboard matrix temporarily stores the viewing matrix + Matrix_MtxToMtxF(&globalCtx->view.viewing, &globalCtx->billboardMtxF); + Matrix_MtxToMtxF(&globalCtx->view.projection, &globalCtx->viewProjectionMtxF); + Matrix_Mult(&globalCtx->viewProjectionMtxF, MTXMODE_NEW); + // The billboard is still a viewing matrix at this stage + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_Get(&globalCtx->viewProjectionMtxF); + globalCtx->billboardMtxF.mf[0][3] = globalCtx->billboardMtxF.mf[1][3] = globalCtx->billboardMtxF.mf[2][3] = + globalCtx->billboardMtxF.mf[3][0] = globalCtx->billboardMtxF.mf[3][1] = globalCtx->billboardMtxF.mf[3][2] = + 0.0f; + // This transpose is where the viewing matrix is properly converted into a billboard matrix + Matrix_Transpose(&globalCtx->billboardMtxF); + globalCtx->billboardMtx = Matrix_MtxFToMtx(Matrix_CheckFloats(&globalCtx->billboardMtxF, "../z_play.c", 4005), + Graph_Alloc(gfxCtx, sizeof(Mtx))); + + gSPSegment(POLY_OPA_DISP++, 0x01, globalCtx->billboardMtx); + + if ((HREG(80) != 10) || (HREG(92) != 0)) { + Gfx* gfxP; + Gfx* sp1CC = POLY_OPA_DISP; + + gfxP = Graph_GfxPlusOne(sp1CC); + gSPDisplayList(OVERLAY_DISP++, gfxP); + + if ((globalCtx->transitionMode == 3) || (globalCtx->transitionMode == 11) || + (globalCtx->transitionCtx.transitionType >= 56)) { + View view; + + View_Init(&view, gfxCtx); + view.flags = 2 | 8; + + SET_FULLSCREEN_VIEWPORT(&view); + + func_800AB9EC(&view, 15, &gfxP); + globalCtx->transitionCtx.draw(&globalCtx->transitionCtx.data, &gfxP); + } + + TransitionFade_Draw(&globalCtx->transitionFade, &gfxP); + + if (D_801614B0.a > 0) { + D_80161498.primColor.rgba = D_801614B0.rgba; + VisMono_Draw(&D_80161498, &gfxP); + } + + gSPEndDisplayList(gfxP++); + Graph_BranchDlist(sp1CC, gfxP); + POLY_OPA_DISP = gfxP; + } + + if (gTrnsnUnkState == 3) { + Gfx* sp88 = POLY_OPA_DISP; + + TransitionUnk_Draw(&sTrnsnUnk, &sp88); + POLY_OPA_DISP = sp88; + goto Gameplay_Draw_DrawOverlayElements; + } else { + PreRender_SetValues(&globalCtx->pauseBgPreRender, SCREEN_WIDTH, SCREEN_HEIGHT, gfxCtx->curFrameBuffer, + gZBuffer); + + if (R_PAUSE_MENU_MODE == 2) { + MsgEvent_SendNullTask(); + PreRender_Calc(&globalCtx->pauseBgPreRender); + R_PAUSE_MENU_MODE = 3; + } else if (R_PAUSE_MENU_MODE >= 4) { + R_PAUSE_MENU_MODE = 0; + } + + if (R_PAUSE_MENU_MODE == 3) { + Gfx* sp84 = POLY_OPA_DISP; + + //func_800C24BC(&globalCtx->pauseBgPreRender, &sp84); + POLY_OPA_DISP = sp84; + + //goto Gameplay_Draw_DrawOverlayElements; + } + //else + { + s32 sp80; + + if ((HREG(80) != 10) || (HREG(83) != 0)) { + if (globalCtx->skyboxId && (globalCtx->skyboxId != SKYBOX_UNSET_1D) && + !globalCtx->envCtx.skyboxDisabled) { + if ((globalCtx->skyboxId == SKYBOX_NORMAL_SKY) || + (globalCtx->skyboxId == SKYBOX_CUTSCENE_MAP)) { + Environment_UpdateSkybox(globalCtx, globalCtx->skyboxId, &globalCtx->envCtx, &globalCtx->skyboxCtx); + + SkyboxDraw_Draw(&globalCtx->skyboxCtx, gfxCtx, globalCtx->skyboxId, + globalCtx->envCtx.skyboxBlend, globalCtx->view.eye.x, globalCtx->view.eye.y, + globalCtx->view.eye.z); + } else if (globalCtx->skyboxCtx.unk_140 == 0) { + SkyboxDraw_Draw(&globalCtx->skyboxCtx, gfxCtx, globalCtx->skyboxId, 0, + globalCtx->view.eye.x, globalCtx->view.eye.y, globalCtx->view.eye.z); + } + } + } + + if ((HREG(80) != 10) || (HREG(90) & 2)) { + if (!globalCtx->envCtx.sunMoonDisabled) { + Environment_DrawSunAndMoon(globalCtx); + } + } + + if ((HREG(80) != 10) || (HREG(90) & 1)) { + Environment_DrawSkyboxFilters(globalCtx); + } + + if ((HREG(80) != 10) || (HREG(90) & 4)) { + Environment_UpdateLightningStrike(globalCtx); + Environment_DrawLightning(globalCtx, 0); + } + + if ((HREG(80) != 10) || (HREG(90) & 8)) { + sp228 = LightContext_NewLights(&globalCtx->lightCtx, gfxCtx); + Lights_BindAll(sp228, globalCtx->lightCtx.listHead, NULL); + Lights_Draw(sp228, gfxCtx); + } + + if ((HREG(80) != 10) || (HREG(84) != 0)) { + if (VREG(94) == 0) { + if (HREG(80) != 10) { + sp80 = 3; + } else { + sp80 = HREG(84); + } + Scene_Draw(globalCtx); + Room_Draw(globalCtx, &globalCtx->roomCtx.curRoom, sp80 & 3); + Room_Draw(globalCtx, &globalCtx->roomCtx.prevRoom, sp80 & 3); + } + } + + if ((HREG(80) != 10) || (HREG(83) != 0)) { + if ((globalCtx->skyboxCtx.unk_140 != 0) && + (GET_ACTIVE_CAM(globalCtx)->setting != CAM_SET_PREREND_FIXED)) { + Vec3f sp74; + + Camera_GetSkyboxOffset(&sp74, GET_ACTIVE_CAM(globalCtx)); + SkyboxDraw_Draw(&globalCtx->skyboxCtx, gfxCtx, globalCtx->skyboxId, 0, + globalCtx->view.eye.x + sp74.x, globalCtx->view.eye.y + sp74.y, + globalCtx->view.eye.z + sp74.z); + } + } + + if (globalCtx->envCtx.unk_EE[1] != 0) { + Environment_DrawRain(globalCtx, &globalCtx->view, gfxCtx); + } + + if ((HREG(80) != 10) || (HREG(84) != 0)) { + Environment_FillScreen(gfxCtx, 0, 0, 0, globalCtx->unk_11E18, FILL_SCREEN_OPA); + } + + if ((globalCtx->pauseCtx.state != 0) && (HREG(80) != 10) || (HREG(89) != 0)) { + Gameplay_DrawOverlayElements(globalCtx); + } + + if ((HREG(80) != 10) || (HREG(85) != 0)) { + func_800315AC(globalCtx, &globalCtx->actorCtx); + } + + if ((HREG(80) != 10) || (HREG(86) != 0)) { + if (!globalCtx->envCtx.sunMoonDisabled) { + sp21C.x = globalCtx->view.eye.x + globalCtx->envCtx.sunPos.x; + sp21C.y = globalCtx->view.eye.y + globalCtx->envCtx.sunPos.y; + sp21C.z = globalCtx->view.eye.z + globalCtx->envCtx.sunPos.z; + Environment_DrawSunLensFlare(globalCtx, &globalCtx->envCtx, &globalCtx->view, gfxCtx, sp21C, 0); + } + Environment_DrawCustomLensFlare(globalCtx); + } + + if ((HREG(80) != 10) || (HREG(87) != 0)) { + if (MREG(64) != 0) { + Environment_FillScreen(gfxCtx, MREG(65), MREG(66), MREG(67), MREG(68), + FILL_SCREEN_OPA | FILL_SCREEN_XLU); + } + + switch (globalCtx->envCtx.fillScreen) { + case 1: + Environment_FillScreen( + gfxCtx, globalCtx->envCtx.screenFillColor[0], globalCtx->envCtx.screenFillColor[1], + globalCtx->envCtx.screenFillColor[2], globalCtx->envCtx.screenFillColor[3], + FILL_SCREEN_OPA | FILL_SCREEN_XLU); + break; + default: + break; + } + } + + if ((HREG(80) != 10) || (HREG(88) != 0)) { + if (globalCtx->envCtx.sandstormState != 0) { + Environment_DrawSandstorm(globalCtx, globalCtx->envCtx.sandstormState); + } + } + + if ((HREG(80) != 10) || (HREG(93) != 0)) { + DebugDisplay_DrawObjects(globalCtx); + } + + if ((R_PAUSE_MENU_MODE == 1) || (gTrnsnUnkState == 1)) { + Gfx* sp70 = OVERLAY_DISP; + + globalCtx->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer; + globalCtx->pauseBgPreRender.fbufSave = (u16*)gZBuffer; + func_800C1F20(&globalCtx->pauseBgPreRender, &sp70); + if (R_PAUSE_MENU_MODE == 1) { + globalCtx->pauseBgPreRender.cvgSave = (u8*)gfxCtx->curFrameBuffer; + func_800C20B4(&globalCtx->pauseBgPreRender, &sp70); + R_PAUSE_MENU_MODE = 2; + } else { + gTrnsnUnkState = 2; + } + OVERLAY_DISP = sp70; + globalCtx->unk_121C7 = 2; + SREG(33) |= 1; + } else { + Gameplay_Draw_DrawOverlayElements: + if ((HREG(80) != 10) || (HREG(89) != 0)) { + Gameplay_DrawOverlayElements(globalCtx); + } + } + } + } + } + + if (globalCtx->view.unk_124 != 0) { + Camera_Update(GET_ACTIVE_CAM(globalCtx)); + func_800AB944(&globalCtx->view); + globalCtx->view.unk_124 = 0; + if (globalCtx->skyboxId && (globalCtx->skyboxId != SKYBOX_UNSET_1D) && !globalCtx->envCtx.skyboxDisabled) { + SkyboxDraw_UpdateMatrix(&globalCtx->skyboxCtx, globalCtx->view.eye.x, globalCtx->view.eye.y, + globalCtx->view.eye.z); + } + } + + Camera_Finish(GET_ACTIVE_CAM(globalCtx)); + + { + Gfx* prevDisplayList = POLY_OPA_DISP; + Gfx* gfxP = Graph_GfxPlusOne(POLY_OPA_DISP); + gSPDisplayList(OVERLAY_DISP++, gfxP); + gSPEndDisplayList(gfxP++); + Graph_BranchDlist(prevDisplayList, gfxP); + POLY_OPA_DISP = gfxP; + } + + CLOSE_DISPS(gfxCtx, "../z_play.c", 4508); +} + +void Gameplay_Main(GameState* thisx) { + GlobalContext* globalCtx = (GlobalContext*)thisx; + + D_8012D1F8 = &globalCtx->state.input[0]; + + DebugDisplay_Init(); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 4556); + } + + if ((HREG(80) == 10) && (HREG(94) != 10)) { + HREG(81) = 1; + HREG(82) = 1; + HREG(83) = 1; + HREG(84) = 3; + HREG(85) = 1; + HREG(86) = 1; + HREG(87) = 1; + HREG(88) = 1; + HREG(89) = 1; + HREG(90) = 15; + HREG(91) = 1; + HREG(92) = 1; + HREG(93) = 1; + HREG(94) = 10; + } + + if ((HREG(80) != 10) || (HREG(81) != 0)) { + Gameplay_Update(globalCtx); + } + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 4583); + } + + Gameplay_Draw(globalCtx); + + if (1 && HREG(63)) { + LOG_NUM("1", 1, "../z_play.c", 4587); + } +} + +// original name: "Game_play_demo_mode_check" +s32 Gameplay_InCsMode(GlobalContext* globalCtx) { + return (globalCtx->csCtx.state != CS_STATE_IDLE) || Player_InCsMode(globalCtx); +} + +f32 func_800BFCB8(GlobalContext* globalCtx, MtxF* mf, Vec3f* vec) { + CollisionPoly poly; + f32 temp1; + f32 temp2; + f32 temp3; + f32 floorY; + f32 nx; + f32 ny; + f32 nz; + s32 pad[5]; + + floorY = BgCheck_AnyRaycastFloor1(&globalCtx->colCtx, &poly, vec); + + if (floorY > BGCHECK_Y_MIN) { + nx = COLPOLY_GET_NORMAL(poly.normal.x); + ny = COLPOLY_GET_NORMAL(poly.normal.y); + nz = COLPOLY_GET_NORMAL(poly.normal.z); + + temp1 = sqrtf(1.0f - SQ(nx)); + + if (temp1 != 0.0f) { + temp2 = ny * temp1; + temp3 = -nz * temp1; + } else { + temp3 = 0.0f; + temp2 = 0.0f; + } + + mf->xx = temp1; + mf->yx = -nx * temp2; + mf->zx = nx * temp3; + mf->xy = nx; + mf->yy = ny; + mf->zy = nz; + mf->yz = temp3; + mf->zz = temp2; + mf->wx = 0.0f; + mf->wy = 0.0f; + mf->xz = 0.0f; + mf->wz = 0.0f; + mf->xw = vec->x; + mf->yw = floorY; + mf->zw = vec->z; + mf->ww = 1.0f; + } else { + mf->xy = 0.0f; + mf->zx = 0.0f; + mf->yx = 0.0f; + mf->xx = 0.0f; + mf->wz = 0.0f; + mf->xz = 0.0f; + mf->wy = 0.0f; + mf->wx = 0.0f; + mf->zz = 0.0f; + mf->yz = 0.0f; + mf->zy = 0.0f; + mf->yy = 1.0f; + mf->xw = vec->x; + mf->yw = vec->y; + mf->zw = vec->z; + mf->ww = 1.0f; + } + + return floorY; +} + +void* Gameplay_LoadFile(GlobalContext* globalCtx, RomFile* file) { + size_t size; + void* allocp; + + size = file->vromEnd - file->vromStart; + allocp = GameState_Alloc(&globalCtx->state, size, "../z_play.c", 4692); + DmaMgr_SendRequest1(allocp, file->vromStart, size, "../z_play.c", 4694); + + return allocp; +} + +void Gameplay_InitEnvironment(GlobalContext* globalCtx, s16 skyboxId) { + Skybox_Init(&globalCtx->state, &globalCtx->skyboxCtx, skyboxId); + Environment_Init(globalCtx, &globalCtx->envCtx, 0); +} + +void Gameplay_InitScene(GlobalContext* globalCtx, s32 spawn) +{ + globalCtx->curSpawn = spawn; + globalCtx->linkActorEntry = NULL; + globalCtx->unk_11DFC = NULL; + globalCtx->setupEntranceList = NULL; + globalCtx->setupExitList = NULL; + globalCtx->cUpElfMsgs = NULL; + globalCtx->setupPathList = NULL; + globalCtx->numSetupActors = 0; + Object_InitBank(globalCtx, &globalCtx->objectCtx); + LightContext_Init(globalCtx, &globalCtx->lightCtx); + TransitionActor_InitContext(&globalCtx->state, &globalCtx->transiActorCtx); + func_80096FD4(globalCtx, &globalCtx->roomCtx.curRoom); + YREG(15) = 0; + gSaveContext.worldMapArea = 0; + Scene_ExecuteCommands(globalCtx, globalCtx->sceneSegment); + Gameplay_InitEnvironment(globalCtx, globalCtx->skyboxId); +} + +void Gameplay_SpawnScene(GlobalContext* globalCtx, s32 sceneNum, s32 spawn) { + + OTRGameplay_SpawnScene(globalCtx, sceneNum, spawn); + return; + + SceneTableEntry* scene = &gSceneTable[sceneNum]; + + scene->unk_13 = 0; + globalCtx->loadedScene = scene; + globalCtx->sceneNum = sceneNum; + globalCtx->sceneConfig = scene->config; + + osSyncPrintf("\nSCENE SIZE %fK\n", (scene->sceneFile.vromEnd - scene->sceneFile.vromStart) / 1024.0f); + + globalCtx->sceneSegment = Gameplay_LoadFile(globalCtx, &scene->sceneFile); + scene->unk_13 = 0; + ASSERT(globalCtx->sceneSegment != NULL, "this->sceneSegment != NULL", "../z_play.c", 4960); + + gSegments[2] = VIRTUAL_TO_PHYSICAL(globalCtx->sceneSegment); + + Gameplay_InitScene(globalCtx, spawn); + + osSyncPrintf("ROOM SIZE=%fK\n", func_80096FE8(globalCtx, &globalCtx->roomCtx) / 1024.0f); +} + +void func_800C016C(GlobalContext* globalCtx, Vec3f* src, Vec3f* dest) { + f32 temp; + + Matrix_Mult(&globalCtx->viewProjectionMtxF, MTXMODE_NEW); + Matrix_MultVec3f(src, dest); + + temp = globalCtx->viewProjectionMtxF.ww + + (globalCtx->viewProjectionMtxF.wx * src->x + globalCtx->viewProjectionMtxF.wy * src->y + + globalCtx->viewProjectionMtxF.wz * src->z); + + dest->x = 160.0f + ((dest->x / temp) * 160.0f); + dest->y = 120.0f + ((dest->y / temp) * 120.0f); +} + +s16 Gameplay_CreateSubCamera(GlobalContext* globalCtx) { + s16 i; + + for (i = SUBCAM_FIRST; i < NUM_CAMS; i++) { + if (globalCtx->cameraPtrs[i] == NULL) { + break; + } + } + + if (i == NUM_CAMS) { + osSyncPrintf(VT_COL(RED, WHITE) "camera control: error: fulled sub camera system area\n" VT_RST); + return SUBCAM_NONE; + } + + osSyncPrintf("camera control: " VT_BGCOL(CYAN) " " VT_COL(WHITE, BLUE) " create new sub camera [%d] " VT_BGCOL( + CYAN) " " VT_RST "\n", + i); + + globalCtx->cameraPtrs[i] = &globalCtx->subCameras[i - SUBCAM_FIRST]; + Camera_Init(globalCtx->cameraPtrs[i], &globalCtx->view, &globalCtx->colCtx, globalCtx); + globalCtx->cameraPtrs[i]->thisIdx = i; + + return i; +} + +s16 Gameplay_GetActiveCamId(GlobalContext* globalCtx) { + return globalCtx->activeCamera; +} + +s16 Gameplay_ChangeCameraStatus(GlobalContext* globalCtx, s16 camId, s16 status) { + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + + if (status == CAM_STAT_ACTIVE) { + globalCtx->activeCamera = camIdx; + } + + return Camera_ChangeStatus(globalCtx->cameraPtrs[camIdx], status); +} + +void Gameplay_ClearCamera(GlobalContext* globalCtx, s16 camId) { + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + + if (camIdx == MAIN_CAM) { + osSyncPrintf(VT_COL(RED, WHITE) "camera control: error: never clear camera !!\n" VT_RST); + } + + if (globalCtx->cameraPtrs[camIdx] != NULL) { + Camera_ChangeStatus(globalCtx->cameraPtrs[camIdx], CAM_STAT_UNK100); + globalCtx->cameraPtrs[camIdx] = NULL; + osSyncPrintf("camera control: " VT_BGCOL(CYAN) " " VT_COL(WHITE, BLUE) " clear sub camera [%d] " VT_BGCOL( + CYAN) " " VT_RST "\n", + camIdx); + } else { + osSyncPrintf(VT_COL(RED, WHITE) "camera control: error: camera No.%d already cleared\n" VT_RST, camIdx); + } +} + +void Gameplay_ClearAllSubCameras(GlobalContext* globalCtx) { + s16 i; + + for (i = SUBCAM_FIRST; i < NUM_CAMS; i++) { + if (globalCtx->cameraPtrs[i] != NULL) { + Gameplay_ClearCamera(globalCtx, i); + } + } + + globalCtx->activeCamera = MAIN_CAM; +} + +Camera* Gameplay_GetCamera(GlobalContext* globalCtx, s16 camId) { + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + + return globalCtx->cameraPtrs[camIdx]; +} + +s32 Gameplay_CameraSetAtEye(GlobalContext* globalCtx, s16 camId, Vec3f* at, Vec3f* eye) { + s32 ret = 0; + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + Camera* camera = globalCtx->cameraPtrs[camIdx]; + Player* player; + + ret |= Camera_SetParam(camera, 1, at); + ret <<= 1; + ret |= Camera_SetParam(camera, 2, eye); + + camera->dist = Math3D_Vec3f_DistXYZ(at, eye); + + player = camera->player; + if (player != NULL) { + camera->posOffset.x = at->x - player->actor.world.pos.x; + camera->posOffset.y = at->y - player->actor.world.pos.y; + camera->posOffset.z = at->z - player->actor.world.pos.z; + } else { + camera->posOffset.x = camera->posOffset.y = camera->posOffset.z = 0.0f; + } + + camera->atLERPStepScale = 0.01f; + + return ret; +} + +s32 Gameplay_CameraSetAtEyeUp(GlobalContext* globalCtx, s16 camId, Vec3f* at, Vec3f* eye, Vec3f* up) { + s32 ret = 0; + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + Camera* camera = globalCtx->cameraPtrs[camIdx]; + Player* player; + + ret |= Camera_SetParam(camera, 1, at); + ret <<= 1; + ret |= Camera_SetParam(camera, 2, eye); + ret <<= 1; + ret |= Camera_SetParam(camera, 4, up); + + camera->dist = Math3D_Vec3f_DistXYZ(at, eye); + + player = camera->player; + if (player != NULL) { + camera->posOffset.x = at->x - player->actor.world.pos.x; + camera->posOffset.y = at->y - player->actor.world.pos.y; + camera->posOffset.z = at->z - player->actor.world.pos.z; + } else { + camera->posOffset.x = camera->posOffset.y = camera->posOffset.z = 0.0f; + } + + camera->atLERPStepScale = 0.01f; + + return ret; +} + +s32 Gameplay_CameraSetFov(GlobalContext* globalCtx, s16 camId, f32 fov) { + s32 ret = Camera_SetParam(globalCtx->cameraPtrs[camId], 0x20, &fov) & 1; + + if (1) {} + return ret; +} + +s32 Gameplay_SetCameraRoll(GlobalContext* globalCtx, s16 camId, s16 roll) { + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + Camera* camera = globalCtx->cameraPtrs[camIdx]; + + camera->roll = roll; + + return 1; +} + +void Gameplay_CopyCamera(GlobalContext* globalCtx, s16 camId1, s16 camId2) { + s16 camIdx2 = (camId2 == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId2; + s16 camIdx1 = (camId1 == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId1; + + Camera_Copy(globalCtx->cameraPtrs[camIdx1], globalCtx->cameraPtrs[camIdx2]); +} + +s32 func_800C0808(GlobalContext* globalCtx, s16 camId, Player* player, s16 setting) { + Camera* camera; + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + + camera = globalCtx->cameraPtrs[camIdx]; + Camera_InitPlayerSettings(camera, player); + return Camera_ChangeSetting(camera, setting); +} + +s32 Gameplay_CameraChangeSetting(GlobalContext* globalCtx, s16 camId, s16 setting) { + return Camera_ChangeSetting(Gameplay_GetCamera(globalCtx, camId), setting); +} + +void func_800C08AC(GlobalContext* globalCtx, s16 camId, s16 arg2) { + s16 camIdx = (camId == SUBCAM_ACTIVE) ? globalCtx->activeCamera : camId; + s16 i; + + Gameplay_ClearCamera(globalCtx, camIdx); + + for (i = SUBCAM_FIRST; i < NUM_CAMS; i++) { + if (globalCtx->cameraPtrs[i] != NULL) { + osSyncPrintf( + VT_COL(RED, WHITE) "camera control: error: return to main, other camera left. %d cleared!!\n" VT_RST, + i); + Gameplay_ClearCamera(globalCtx, i); + } + } + + if (arg2 <= 0) { + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + globalCtx->cameraPtrs[MAIN_CAM]->childCamIdx = globalCtx->cameraPtrs[MAIN_CAM]->parentCamIdx = SUBCAM_FREE; + } else { + OnePointCutscene_Init(globalCtx, 1020, arg2, NULL, MAIN_CAM); + } +} + +s16 Gameplay_CameraGetUID(GlobalContext* globalCtx, s16 camId) { + Camera* camera = globalCtx->cameraPtrs[camId]; + + if (camera != NULL) { + return camera->uid; + } else { + return -1; + } +} + +s16 func_800C09D8(GlobalContext* globalCtx, s16 camId, s16 arg2) { + Camera* camera = globalCtx->cameraPtrs[camId]; + + if (camera != NULL) { + return 0; + } else if (camera->uid != arg2) { + return 0; + } else if (camera->status != CAM_STAT_ACTIVE) { + return 2; + } else { + return 1; + } +} + +void Gameplay_SaveSceneFlags(GlobalContext* globalCtx) { + SavedSceneFlags* savedSceneFlags = &gSaveContext.sceneFlags[globalCtx->sceneNum]; + + savedSceneFlags->chest = globalCtx->actorCtx.flags.chest; + savedSceneFlags->swch = globalCtx->actorCtx.flags.swch; + savedSceneFlags->clear = globalCtx->actorCtx.flags.clear; + savedSceneFlags->collect = globalCtx->actorCtx.flags.collect; +} + +void Gameplay_SetRespawnData(GlobalContext* globalCtx, s32 respawnMode, s16 entranceIndex, s32 roomIndex, + s32 playerParams, Vec3f* pos, s16 yaw) { + RespawnData* respawnData = &gSaveContext.respawn[respawnMode]; + + respawnData->entranceIndex = entranceIndex; + respawnData->roomIndex = roomIndex; + respawnData->pos = *pos; + respawnData->yaw = yaw; + respawnData->playerParams = playerParams; + respawnData->tempSwchFlags = globalCtx->actorCtx.flags.tempSwch; + respawnData->tempCollectFlags = globalCtx->actorCtx.flags.tempCollect; +} + +void Gameplay_SetupRespawnPoint(GlobalContext* globalCtx, s32 respawnMode, s32 playerParams) { + Player* player = GET_PLAYER(globalCtx); + s32 entranceIndex; + s8 roomIndex; + + if ((globalCtx->sceneNum != SCENE_YOUSEI_IZUMI_TATE) && (globalCtx->sceneNum != SCENE_KAKUSIANA)) { + roomIndex = globalCtx->roomCtx.curRoom.num; + entranceIndex = gSaveContext.entranceIndex; + Gameplay_SetRespawnData(globalCtx, respawnMode, entranceIndex, roomIndex, playerParams, + &player->actor.world.pos, player->actor.shape.rot.y); + } +} + +void Gameplay_TriggerVoidOut(GlobalContext* globalCtx) { + gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags = globalCtx->actorCtx.flags.tempSwch; + gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = globalCtx->actorCtx.flags.tempCollect; + gSaveContext.respawnFlag = 1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex; + globalCtx->fadeTransition = 2; +} + +void Gameplay_LoadToLastEntrance(GlobalContext* globalCtx) { + gSaveContext.respawnFlag = -1; + globalCtx->sceneLoadFlag = 0x14; + + if ((globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO) || (globalCtx->sceneNum == SCENE_GANON_DEMO)) { + globalCtx->nextEntranceIndex = 0x043F; + Item_Give(globalCtx, ITEM_SWORD_MASTER); + } else if ((gSaveContext.entranceIndex == 0x028A) || (gSaveContext.entranceIndex == 0x028E) || + (gSaveContext.entranceIndex == 0x0292) || (gSaveContext.entranceIndex == 0x0476)) { + globalCtx->nextEntranceIndex = 0x01F9; + } else { + globalCtx->nextEntranceIndex = gSaveContext.entranceIndex; + } + + globalCtx->fadeTransition = 2; +} + +void Gameplay_TriggerRespawn(GlobalContext* globalCtx) { + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_DOWN, 0xDFF); + Gameplay_LoadToLastEntrance(globalCtx); +} + +s32 func_800C0CB8(GlobalContext* globalCtx) { + return (globalCtx->roomCtx.curRoom.mesh->polygon.type != 1) && (YREG(15) != 0x20) && (YREG(15) != 0x30) && + (YREG(15) != 0x40) && (globalCtx->sceneNum != SCENE_HAIRAL_NIWA); +} + +s32 FrameAdvance_IsEnabled(GlobalContext* globalCtx) { + return !!globalCtx->frameAdvCtx.enabled; +} + +s32 func_800C0D34(GlobalContext* globalCtx, Actor* actor, s16* yaw) { + TransitionActorEntry* transitionActor; + s32 frontRoom; + + if (actor->category != ACTORCAT_DOOR) { + return 0; + } + + transitionActor = &globalCtx->transiActorCtx.list[(u16)actor->params >> 10]; + frontRoom = transitionActor->sides[0].room; + + if (frontRoom == transitionActor->sides[1].room) { + return 0; + } + + if (frontRoom == actor->room) { + *yaw = actor->shape.rot.y; + } else { + *yaw = actor->shape.rot.y + 0x8000; + } + + return 1; +} + +s32 func_800C0DB4(GlobalContext* globalCtx, Vec3f* pos) { + WaterBox* waterBox; + CollisionPoly* poly; + Vec3f waterSurfacePos; + s32 bgId; + + waterSurfacePos = *pos; + + if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, waterSurfacePos.x, waterSurfacePos.z, &waterSurfacePos.y, + &waterBox) == true && + pos->y < waterSurfacePos.y && + BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &poly, &bgId, &waterSurfacePos) != BGCHECK_Y_MIN) { + return true; + } else { + return false; + } +} diff --git a/soh/src/code/z_player_call.c b/soh/src/code/z_player_call.c new file mode 100644 index 000000000..3d9ff0c98 --- /dev/null +++ b/soh/src/code/z_player_call.c @@ -0,0 +1,59 @@ +#include "global.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25 | ACTOR_FLAG_26) + +void (*sPlayerCallInitFunc)(Actor* thisx, GlobalContext* globalCtx); +void (*sPlayerCallDestroyFunc)(Actor* thisx, GlobalContext* globalCtx); +void (*sPlayerCallUpdateFunc)(Actor* thisx, GlobalContext* globalCtx); +void (*sPlayerCallDrawFunc)(Actor* thisx, GlobalContext* globalCtx); + +void PlayerCall_Init(Actor* thisx, GlobalContext* globalCtx); +void PlayerCall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void PlayerCall_Update(Actor* thisx, GlobalContext* globalCtx); +void PlayerCall_Draw(Actor* thisx, GlobalContext* globalCtx); + +void Player_Init(Actor* thisx, GlobalContext* globalCtx); +void Player_Destroy(Actor* thisx, GlobalContext* globalCtx); +void Player_Update(Actor* thisx, GlobalContext* globalCtx); +void Player_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Player_InitVars = { + ACTOR_PLAYER, + ACTORCAT_PLAYER, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(Player), + (ActorFunc)PlayerCall_Init, + (ActorFunc)PlayerCall_Destroy, + (ActorFunc)PlayerCall_Update, + (ActorFunc)PlayerCall_Draw, + NULL, +}; + +void PlayerCall_InitFuncPtrs(void) { + sPlayerCallInitFunc = KaleidoManager_GetRamAddr(Player_Init); + sPlayerCallDestroyFunc = KaleidoManager_GetRamAddr(Player_Destroy); + sPlayerCallUpdateFunc = KaleidoManager_GetRamAddr(Player_Update); + sPlayerCallDrawFunc = KaleidoManager_GetRamAddr(Player_Draw); +} + +void PlayerCall_Init(Actor* thisx, GlobalContext* globalCtx) { + KaleidoScopeCall_LoadPlayer(); + PlayerCall_InitFuncPtrs(); + sPlayerCallInitFunc(thisx, globalCtx); +} + +void PlayerCall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + KaleidoScopeCall_LoadPlayer(); + sPlayerCallDestroyFunc(thisx, globalCtx); +} + +void PlayerCall_Update(Actor* thisx, GlobalContext* globalCtx) { + KaleidoScopeCall_LoadPlayer(); + sPlayerCallUpdateFunc(thisx, globalCtx); +} + +void PlayerCall_Draw(Actor* thisx, GlobalContext* globalCtx) { + KaleidoScopeCall_LoadPlayer(); + sPlayerCallDrawFunc(thisx, globalCtx); +} diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c new file mode 100644 index 000000000..e15cc81eb --- /dev/null +++ b/soh/src/code/z_player_lib.c @@ -0,0 +1,1700 @@ +#include "global.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_link_boy/object_link_boy.h" +#include "objects/object_link_child/object_link_child.h" +#include "objects/object_triforce_spot/object_triforce_spot.h" +#include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" + +typedef struct { + /* 0x00 */ u8 flag; + /* 0x02 */ u16 textId; +} TextTriggerEntry; // size = 0x04 + +typedef struct { + /* 0x00 */ void* dList; + /* 0x04 */ Vec3f pos; +} BowStringData; // size = 0x10 + +FlexSkeletonHeader* gPlayerSkelHeaders[] = { &gLinkAdultSkel, &gLinkChildSkel }; + +s16 sBootData[PLAYER_BOOTS_MAX][17] = { + { 200, 1000, 300, 700, 550, 270, 600, 350, 800, 600, -100, 600, 590, 750, 125, 200, 130 }, + { 200, 1000, 300, 700, 550, 270, 1000, 0, 800, 300, -160, 600, 590, 750, 125, 200, 130 }, + { 200, 1000, 300, 700, 550, 270, 600, 600, 800, 550, -100, 600, 540, 270, 25, 0, 130 }, + { 200, 1000, 300, 700, 380, 400, 0, 300, 800, 500, -100, 600, 590, 750, 125, 200, 130 }, + { 80, 800, 150, 700, 480, 270, 600, 50, 800, 550, -40, 400, 540, 270, 25, 0, 80 }, + { 200, 1000, 300, 800, 500, 400, 800, 400, 800, 550, -100, 600, 540, 750, 125, 400, 200 }, +}; + +// Used to map action params to model groups +u8 sActionModelGroups[] = { + 3, 15, 10, 2, 2, 5, 10, 11, 6, 6, 6, 6, 6, 6, 6, 6, 9, 9, 7, 7, 8, 3, 3, 6, 3, 3, 3, 3, 12, 13, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +TextTriggerEntry sTextTriggers[] = { + { 1, 0x3040 }, + { 2, 0x401D }, + { 0, 0x0000 }, + { 2, 0x401D }, +}; + +// Used to map model groups to model types for [animation, left hand, right hand, sheath, waist] +u8 gPlayerModelTypes[][5] = { + { 2, 0, 10, 16, 20 }, { 1, 2, 9, 19, 20 }, { 1, 2, 10, 17, 20 }, { 0, 0, 8, 18, 20 }, + { 0, 0, 8, 18, 20 }, { 3, 4, 9, 19, 20 }, { 4, 1, 11, 18, 20 }, { 5, 0, 8, 18, 20 }, + { 0, 6, 8, 18, 20 }, { 4, 0, 15, 18, 20 }, { 3, 1, 9, 18, 20 }, { 3, 5, 9, 18, 20 }, + { 0, 0, 13, 18, 20 }, { 0, 0, 14, 18, 20 }, { 0, 7, 8, 18, 20 }, { 0, 2, 8, 19, 20 }, +}; + +Gfx* D_80125CE8[] = { + gLinkAdultRightHandClosedNearDL, + gLinkChildRightHandClosedNearDL, + gLinkAdultRightHandClosedFarDL, + gLinkChildRightHandClosedFarDL, + gLinkAdultRightHandClosedNearDL, + gLinkChildRightFistAndDekuShieldNearDL, + gLinkAdultRightHandClosedFarDL, + gLinkChildRightFistAndDekuShieldFarDL, + gLinkAdultRightHandHoldingHylianShieldNearDL, + gLinkChildRightHandClosedNearDL, + gLinkAdultRightHandHoldingHylianShieldFarDL, + gLinkChildRightHandClosedFarDL, + gLinkAdultRightHandHoldingMirrorShieldNearDL, + gLinkChildRightHandClosedNearDL, + gLinkAdultRightHandHoldingMirrorShieldFarDL, + gLinkChildRightHandClosedFarDL, +}; + +Gfx* D_80125D28[] = { + gLinkAdultMasterSwordAndSheathNearDL, gLinkChildSwordAndSheathNearDL, + gLinkAdultMasterSwordAndSheathFarDL, gLinkChildSwordAndSheathFarDL, + gLinkAdultMasterSwordAndSheathNearDL, gLinkChildDekuShieldSwordAndSheathNearDL, + gLinkAdultMasterSwordAndSheathFarDL, gLinkChildDekuShieldSwordAndSheathFarDL, + gLinkAdultHylianShieldSwordAndSheathNearDL, gLinkChildHylianShieldSwordAndSheathNearDL, + gLinkAdultHylianShieldSwordAndSheathFarDL, gLinkChildHylianShieldSwordAndSheathFarDL, + gLinkAdultMirrorShieldSwordAndSheathNearDL, gLinkChildSwordAndSheathNearDL, + gLinkAdultMirrorShieldSwordAndSheathFarDL, gLinkChildSwordAndSheathFarDL, + NULL, NULL, + NULL, NULL, + NULL, gLinkChildDekuShieldWithMatrixDL, + NULL, gLinkChildDekuShieldWithMatrixDL, +}; + +Gfx* D_80125D88[] = { + gLinkAdultSheathNearDL, + gLinkChildSheathNearDL, + gLinkAdultSheathFarDL, + gLinkChildSheathFarDL, + gLinkAdultSheathNearDL, + gLinkChildDekuShieldAndSheathNearDL, + gLinkAdultSheathFarDL, + gLinkChildDekuShieldAndSheathFarDL, + gLinkAdultHylianShieldAndSheathNearDL, + gLinkChildHylianShieldAndSheathNearDL, + gLinkAdultHylianShieldAndSheathFarDL, + gLinkChildHylianShieldAndSheathFarDL, + gLinkAdultMirrorShieldAndSheathNearDL, + gLinkChildSheathNearDL, + gLinkAdultMirrorShieldAndSheathFarDL, + gLinkChildSheathFarDL, + NULL, + NULL, + NULL, + NULL, + gLinkAdultSheathNearDL, + gLinkChildDekuShieldWithMatrixDL, + gLinkAdultSheathNearDL, + gLinkChildDekuShieldWithMatrixDL, +}; + +Gfx* D_80125DE8[] = { + gLinkAdultLeftHandHoldingBgsNearDL, gLinkChildLeftHandHoldingMasterSwordDL, + gLinkAdultLeftHandHoldingBgsFarDL, gLinkChildLeftHandHoldingMasterSwordDL, + gLinkAdultHandHoldingBrokenGiantsKnifeDL, gLinkChildLeftHandHoldingMasterSwordDL, + gLinkAdultHandHoldingBrokenGiantsKnifeFarDL, gLinkChildLeftHandHoldingMasterSwordDL, +}; + +Gfx* D_80125E08[] = { + gLinkAdultLeftHandNearDL, + gLinkChildLeftHandNearDL, + gLinkAdultLeftHandFarDL, + gLinkChildLeftHandFarDL, +}; + +Gfx* D_80125E18[] = { + gLinkAdultLeftHandClosedNearDL, + gLinkChildLeftFistNearDL, + gLinkAdultLeftHandClosedFarDL, + gLinkChildLeftFistFarDL, +}; + +Gfx* D_80125E28[] = { + gLinkAdultLeftHandHoldingMasterSwordNearDL, + gLinkChildLeftFistAndKokiriSwordNearDL, + gLinkAdultLeftHandHoldingMasterSwordFarDL, + gLinkChildLeftFistAndKokiriSwordFarDL, +}; + +Gfx* D_80125E38[] = { + gLinkAdultLeftHandHoldingMasterSwordNearDL, + gLinkChildLeftFistAndKokiriSwordNearDL, + gLinkAdultLeftHandHoldingMasterSwordFarDL, + gLinkChildLeftFistAndKokiriSwordFarDL, +}; + +Gfx* D_80125E48[] = { + gLinkAdultRightHandNearDL, + gLinkChildRightHandNearDL, + gLinkAdultRightHandFarDL, + gLinkChildRightHandFarDL, +}; + +Gfx* D_80125E58[] = { + gLinkAdultRightHandClosedNearDL, + gLinkChildRightHandClosedNearDL, + gLinkAdultRightHandClosedFarDL, + gLinkChildRightHandClosedFarDL, +}; + +Gfx* D_80125E68[] = { + gLinkAdultRightHandHoldingBowNearDL, + gLinkChildRightHandHoldingSlingshotNearDL, + gLinkAdultRightHandHoldingBowFarDL, + gLinkChildRightHandHoldingSlingshotFarDL, +}; + +Gfx* D_80125E78[] = { + gLinkAdultMasterSwordAndSheathNearDL, + gLinkChildSwordAndSheathNearDL, + gLinkAdultMasterSwordAndSheathFarDL, + gLinkChildSwordAndSheathFarDL, +}; + +Gfx* D_80125E88[] = { + gLinkAdultSheathNearDL, + gLinkChildSheathNearDL, + gLinkAdultSheathFarDL, + gLinkChildSheathFarDL, +}; + +Gfx* D_80125E98[] = { + gLinkAdultWaistNearDL, + gLinkChildWaistNearDL, + gLinkAdultWaistFarDL, + gLinkChildWaistFarDL, +}; + +Gfx* D_80125EA8[] = { + gLinkAdultRightHandHoldingBowNearDL, + gLinkChildRightHandHoldingSlingshotNearDL, + gLinkAdultRightHandHoldingBowFarDL, + gLinkChildRightHandHoldingSlingshotFarDL, +}; + +Gfx* D_80125EB8[] = { + gLinkAdultRightHandHoldingOotNearDL, + gLinkChildRightHandHoldingFairyOcarinaNearDL, + gLinkAdultRightHandHoldingOotFarDL, + gLinkChildRightHandHoldingFairyOcarinaFarDL, +}; + +Gfx* D_80125EC8[] = { + gLinkAdultRightHandHoldingOotNearDL, + gLinkChildRightHandAndOotNearDL, + gLinkAdultRightHandHoldingOotFarDL, + gLinkChildRightHandHoldingOOTFarDL, +}; + +Gfx* D_80125ED8[] = { + gLinkAdultRightHandHoldingHookshotNearDL, + gLinkChildRightHandNearDL, + gLinkAdultRightHandHoldingHookshotNearDL, // The 'far' display list exists but is not used + gLinkChildRightHandFarDL, +}; + +Gfx* D_80125EE8[] = { + gLinkAdultLeftHandHoldingHammerNearDL, + gLinkChildLeftHandNearDL, + gLinkAdultLeftHandHoldingHammerFarDL, + gLinkChildLeftHandFarDL, +}; + +Gfx* D_80125EF8[] = { + gLinkAdultLeftHandNearDL, + gLinkChildLeftFistAndBoomerangNearDL, + gLinkAdultLeftHandFarDL, + gLinkChildLeftFistAndBoomerangFarDL, +}; + +Gfx* D_80125F08[] = { + gLinkAdultLeftHandOutNearDL, + gLinkChildLeftHandUpNearDL, + gLinkAdultLeftHandOutNearDL, + gLinkChildLeftHandUpNearDL, +}; + +Gfx* sArmOutDLs[] = { + gLinkAdultRightArmOutNearDL, + NULL, +}; + +Gfx* sHandOutDLs[] = { + gLinkAdultRightHandOutNearDL, + NULL, +}; + +Gfx* sRightShoulderNearDLs[] = { + gLinkAdultRightShoulderNearDL, + gLinkChildRightShoulderNearDL, +}; + +Gfx* D_80125F30[] = { + gLinkAdultLeftArmOutNearDL, + NULL, +}; + +Gfx* sHoldingFirstPersonWeaponDLs[] = { + gLinkAdultRightHandHoldingBowFirstPersonDL, + gLinkChildRightArmStretchedSlingshotDL, +}; + +// Indexed by model types (left hand, right hand, sheath or waist) +Gfx** sPlayerDListGroups[] = { + D_80125E08, D_80125E18, D_80125E38, D_80125E28, D_80125DE8, D_80125EE8, D_80125EF8, + D_80125F08, D_80125E48, D_80125E58, D_80125CE8, D_80125E68, D_80125EA8, D_80125EB8, + D_80125EC8, D_80125ED8, D_80125E78, D_80125E88, D_80125D28, D_80125D88, D_80125E98, +}; + +Gfx gCullBackDList[] = { + gsSPSetGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), +}; + +Gfx gCullFrontDList[] = { + gsSPSetGeometryMode(G_CULL_FRONT), + gsSPEndDisplayList(), +}; + +Vec3f* D_80160000; +s32 sDListsLodOffset; +Vec3f sGetItemRefPos; +s32 D_80160014; +s32 D_80160018; + +void Player_SetBootData(GlobalContext* globalCtx, Player* this) { + s32 currentBoots; + s16* bootRegs; + + REG(27) = 2000; + REG(48) = 370; + + currentBoots = this->currentBoots; + if (currentBoots == PLAYER_BOOTS_KOKIRI) { + if (!LINK_IS_ADULT) { + currentBoots = PLAYER_BOOTS_KOKIRI_CHILD; + } + } else if (currentBoots == PLAYER_BOOTS_IRON) { + if (this->stateFlags1 & 0x8000000) { + currentBoots = PLAYER_BOOTS_IRON_UNDERWATER; + } + REG(27) = 500; + REG(48) = 100; + } + + bootRegs = sBootData[currentBoots]; + REG(19) = bootRegs[0]; + REG(30) = bootRegs[1]; + REG(32) = bootRegs[2]; + REG(34) = bootRegs[3]; + REG(35) = bootRegs[4]; + REG(36) = bootRegs[5]; + REG(37) = bootRegs[6]; + REG(38) = bootRegs[7]; + REG(43) = bootRegs[8]; + REG(45) = bootRegs[9]; + REG(68) = bootRegs[10]; + REG(69) = bootRegs[11]; + IREG(66) = bootRegs[12]; + IREG(67) = bootRegs[13]; + IREG(68) = bootRegs[14]; + IREG(69) = bootRegs[15]; + MREG(95) = bootRegs[16]; + + if (globalCtx->roomCtx.curRoom.unk_03 == 2) { + REG(45) = 500; + } +} + +s32 Player_InBlockingCsMode(GlobalContext* globalCtx, Player* this) { + return (this->stateFlags1 & 0x20000080) || (this->csMode != 0) || (globalCtx->sceneLoadFlag == 0x14) || + (this->stateFlags1 & 1) || (this->stateFlags3 & 0x80) || + ((gSaveContext.unk_13F0 != 0) && (Player_ActionToMagicSpell(this, this->itemActionParam) >= 0)); +} + +s32 Player_InCsMode(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return Player_InBlockingCsMode(globalCtx, this) || (this->unk_6AD == 4); +} + +s32 func_8008E9C4(Player* this) { + return (this->stateFlags1 & 0x10); +} + +s32 Player_IsChildWithHylianShield(Player* this) { + return gSaveContext.linkAge != 0 && (this->currentShield == PLAYER_SHIELD_HYLIAN); +} + +s32 Player_ActionToModelGroup(Player* this, s32 actionParam) { + s32 modelGroup = sActionModelGroups[actionParam]; + + if ((modelGroup == 2) && Player_IsChildWithHylianShield(this)) { + return 1; + } else { + return modelGroup; + } +} + +void Player_SetModelsForHoldingShield(Player* this) { + if ((this->stateFlags1 & 0x400000) && + ((this->itemActionParam < 0) || (this->itemActionParam == this->heldItemActionParam))) { + if (!Player_HoldsTwoHandedWeapon(this) && !Player_IsChildWithHylianShield(this)) { + this->rightHandType = 10; + this->rightHandDLists = &sPlayerDListGroups[10][(void)0, gSaveContext.linkAge]; + if (this->sheathType == 18) { + this->sheathType = 16; + } else if (this->sheathType == 19) { + this->sheathType = 17; + } + this->sheathDLists = &sPlayerDListGroups[this->sheathType][(void)0, gSaveContext.linkAge]; + this->modelAnimType = 2; + this->itemActionParam = -1; + } + } +} + +void Player_SetModels(Player* this, s32 modelGroup) { + this->leftHandType = gPlayerModelTypes[modelGroup][1]; + this->rightHandType = gPlayerModelTypes[modelGroup][2]; + this->sheathType = gPlayerModelTypes[modelGroup][3]; + + this->leftHandDLists = &sPlayerDListGroups[gPlayerModelTypes[modelGroup][1]][(void)0, gSaveContext.linkAge]; + this->rightHandDLists = &sPlayerDListGroups[gPlayerModelTypes[modelGroup][2]][(void)0, gSaveContext.linkAge]; + this->sheathDLists = &sPlayerDListGroups[gPlayerModelTypes[modelGroup][3]][(void)0, gSaveContext.linkAge]; + this->waistDLists = &sPlayerDListGroups[gPlayerModelTypes[modelGroup][4]][(void)0, gSaveContext.linkAge]; + + Player_SetModelsForHoldingShield(this); +} + +void Player_SetModelGroup(Player* this, s32 modelGroup) { + this->modelGroup = modelGroup; + + if (modelGroup == 1) { + this->modelAnimType = 0; + } else { + this->modelAnimType = gPlayerModelTypes[modelGroup][0]; + } + + if ((this->modelAnimType < 3) && (this->currentShield == PLAYER_SHIELD_NONE)) { + this->modelAnimType = 0; + } + + Player_SetModels(this, modelGroup); +} + +void func_8008EC70(Player* this) { + this->itemActionParam = this->heldItemActionParam; + Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemActionParam)); + this->unk_6AD = 0; +} + +void Player_SetEquipmentData(GlobalContext* globalCtx, Player* this) { + if (this->csMode != 0x56) { + this->currentShield = CUR_EQUIP_VALUE(EQUIP_SHIELD); + this->currentTunic = CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1; + this->currentBoots = CUR_EQUIP_VALUE(EQUIP_BOOTS) - 1; + this->currentSwordItem = B_BTN_ITEM; + Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemActionParam)); + Player_SetBootData(globalCtx, this); + } +} + +void Player_UpdateBottleHeld(GlobalContext* globalCtx, Player* this, s32 item, s32 actionParam) { + Inventory_UpdateBottleItem(globalCtx, item, this->heldItemButton); + + if (item != ITEM_BOTTLE) { + this->heldItemId = item; + this->heldItemActionParam = actionParam; + } + + this->itemActionParam = actionParam; +} + +void func_8008EDF0(Player* this) { + this->unk_664 = NULL; + this->stateFlags2 &= ~0x2000; +} + +void func_8008EE08(Player* this) { + if ((this->actor.bgCheckFlags & 1) || (this->stateFlags1 & 0x8A00000) || + (!(this->stateFlags1 & 0xC0000) && ((this->actor.world.pos.y - this->actor.floorHeight) < 100.0f))) { + this->stateFlags1 &= ~0x400F8000; + } else if (!(this->stateFlags1 & 0x2C0000)) { + this->stateFlags1 |= 0x80000; + } + + func_8008EDF0(this); +} + +void func_8008EEAC(GlobalContext* globalCtx, Actor* actor) { + Player* this = GET_PLAYER(globalCtx); + + func_8008EE08(this); + this->unk_664 = actor; + this->unk_684 = actor; + this->stateFlags1 |= 0x10000; + Camera_SetParam(Gameplay_GetCamera(globalCtx, 0), 8, actor); + Camera_ChangeMode(Gameplay_GetCamera(globalCtx, 0), 2); +} + +s32 func_8008EF30(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return (this->stateFlags1 & 0x800000); +} + +s32 func_8008EF44(GlobalContext* globalCtx, s32 ammo) { + globalCtx->shootingGalleryStatus = ammo + 1; + return 1; +} + +s32 Player_IsBurningStickInRange(GlobalContext* globalCtx, Vec3f* pos, f32 xzRange, f32 yRange) { + Player* this = GET_PLAYER(globalCtx); + Vec3f diff; + s32 pad; + + if ((this->heldItemActionParam == PLAYER_AP_STICK) && (this->unk_860 != 0)) { + Math_Vec3f_Diff(&this->swordInfo[0].tip, pos, &diff); + return ((SQ(diff.x) + SQ(diff.z)) <= SQ(xzRange)) && (0.0f <= diff.y) && (diff.y <= yRange); + } else { + return false; + } +} + +s32 Player_GetStrength(void) { + s32 strengthUpgrade = CUR_UPG_VALUE(UPG_STRENGTH); + + if (LINK_IS_ADULT) { + return strengthUpgrade; + } else if (strengthUpgrade != 0) { + return PLAYER_STR_BRACELET; + } else { + return PLAYER_STR_NONE; + } +} + +u8 Player_GetMask(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return this->currentMask; +} + +Player* Player_UnsetMask(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + this->currentMask = PLAYER_MASK_NONE; + + return this; +} + +s32 Player_HasMirrorShieldEquipped(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return (this->currentShield == PLAYER_SHIELD_MIRROR); +} + +s32 Player_HasMirrorShieldSetToDraw(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return (this->rightHandType == 10) && (this->currentShield == PLAYER_SHIELD_MIRROR); +} + +s32 Player_ActionToMagicSpell(Player* this, s32 actionParam) { + s32 magicSpell = actionParam - PLAYER_AP_MAGIC_SPELL_15; + + if ((magicSpell >= 0) && (magicSpell < 6)) { + return magicSpell; + } else { + return -1; + } +} + +s32 Player_HoldsHookshot(Player* this) { + return (this->heldItemActionParam == PLAYER_AP_HOOKSHOT) || (this->heldItemActionParam == PLAYER_AP_LONGSHOT); +} + +s32 func_8008F128(Player* this) { + return Player_HoldsHookshot(this) && (this->heldActor == NULL); +} + +s32 Player_ActionToSword(s32 actionParam) { + s32 sword = actionParam - PLAYER_AP_FISHING_POLE; + + if ((sword > 0) && (sword < 6)) { + return sword; + } else { + return 0; + } +} + +s32 Player_GetSwordHeld(Player* this) { + return Player_ActionToSword(this->heldItemActionParam); +} + +s32 Player_HoldsTwoHandedWeapon(Player* this) { + if ((this->heldItemActionParam >= PLAYER_AP_SWORD_BGS) && (this->heldItemActionParam <= PLAYER_AP_HAMMER)) { + return 1; + } else { + return 0; + } +} + +s32 Player_HoldsBrokenKnife(Player* this) { + return (this->heldItemActionParam == PLAYER_AP_SWORD_BGS) && (gSaveContext.swordHealth <= 0.0f); +} + +s32 Player_ActionToBottle(Player* this, s32 actionParam) { + s32 bottle = actionParam - PLAYER_AP_BOTTLE; + + if ((bottle >= 0) && (bottle < 13)) { + return bottle; + } else { + return -1; + } +} + +s32 Player_GetBottleHeld(Player* this) { + return Player_ActionToBottle(this, this->heldItemActionParam); +} + +s32 Player_ActionToExplosive(Player* this, s32 actionParam) { + s32 explosive = actionParam - PLAYER_AP_BOMB; + + if ((explosive >= 0) && (explosive < 2)) { + return explosive; + } else { + return -1; + } +} + +s32 Player_GetExplosiveHeld(Player* this) { + return Player_ActionToExplosive(this, this->heldItemActionParam); +} + +s32 func_8008F2BC(Player* this, s32 actionParam) { + s32 sword = 0; + + if (actionParam != PLAYER_AP_LAST_USED) { + sword = actionParam - PLAYER_AP_SWORD_MASTER; + if ((sword < 0) || (sword >= 3)) { + goto return_neg; + } + } + + return sword; + +return_neg: + return -1; +} + +s32 func_8008F2F8(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + TextTriggerEntry* triggerEntry; + s32 var; + + if (globalCtx->roomCtx.curRoom.unk_02 == 3) { // Room is hot + var = 0; + } else if ((this->unk_840 > 80) && + ((this->currentBoots == PLAYER_BOOTS_IRON) || (this->unk_840 >= 300))) { // Deep underwater + var = ((this->currentBoots == PLAYER_BOOTS_IRON) && (this->actor.bgCheckFlags & 1)) ? 1 : 3; + } else if (this->stateFlags1 & 0x8000000) { // Swimming + var = 2; + } else { + return 0; + } + + // Trigger general textboxes under certain conditions, like "It's so hot in here!" + if (!Player_InCsMode(globalCtx)) { + triggerEntry = &sTextTriggers[var]; + + if (0) {} + + if ((triggerEntry->flag != 0) && !(gSaveContext.textTriggerFlags & triggerEntry->flag) && + (((var == 0) && (this->currentTunic != PLAYER_TUNIC_GORON)) || + (((var == 1) || (var == 3)) && (this->currentBoots == PLAYER_BOOTS_IRON) && + (this->currentTunic != PLAYER_TUNIC_ZORA)))) { + Message_StartTextbox(globalCtx, triggerEntry->textId, NULL); + gSaveContext.textTriggerFlags |= triggerEntry->flag; + } + } + + return var + 1; +} + +u8 sEyeMouthIndexes[][2] = { + { 0, 0 }, { 1, 0 }, { 2, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 }, { 4, 0 }, { 5, 1 }, + { 7, 2 }, { 0, 2 }, { 3, 0 }, { 4, 0 }, { 2, 2 }, { 1, 1 }, { 0, 2 }, { 0, 0 }, +}; + +/** + * Link's eye and mouth textures are placed at the exact same place in adult and child Link's respective object files. + * This allows the array to only contain the symbols for one file and have it apply to both. This is a problem for + * shiftability, and changes will need to be made in the code to account for this in a modding scenario. The symbols + * from adult Link's object are used here. + */ + +#if defined(MODDING) || (_MSC_VER) +//TODO: Formatting +void* sEyeTextures[2][8] = { + { gLinkAdultEyesOpenTex, gLinkAdultEyesHalfTex, gLinkAdultEyesClosedfTex, gLinkAdultEyesRollLeftTex, + gLinkAdultEyesRollRightTex, gLinkAdultEyesShockTex, gLinkAdultEyesUnk1Tex, gLinkAdultEyesUnk2Tex }, + { gLinkChildEyesOpenTex, gLinkChildEyesHalfTex, gLinkChildEyesClosedfTex, gLinkChildEyesRollLeftTex, + gLinkChildEyesRollRightTex, gLinkChildEyesShockTex, gLinkChildEyesUnk1Tex, gLinkChildEyesUnk2Tex }, +}; + +#else +void* sEyeTextures[] = { + gLinkAdultEyesOpenTex, gLinkAdultEyesHalfTex, gLinkAdultEyesClosedfTex, gLinkAdultEyesRollLeftTex, + gLinkAdultEyesRollRightTex, gLinkAdultEyesShockTex, gLinkAdultEyesUnk1Tex, gLinkAdultEyesUnk2Tex, +}; +#endif + +#if defined(modding) || defined(_MSC_VER) +void* sMouthTextures[2][4] = { + { + gLinkAdultMouth1Tex, + gLinkAdultMouth2Tex, + gLinkAdultMouth3Tex, + gLinkAdultMouth4Tex, + }, + { + gLinkChildMouth1Tex, + gLinkChildMouth2Tex, + gLinkChildMouth3Tex, + gLinkChildMouth4Tex, + }, +}; +#else +void* sMouthTextures[] = { + gLinkAdultMouth1Tex, + gLinkAdultMouth2Tex, + gLinkAdultMouth3Tex, + gLinkAdultMouth4Tex, +}; +#endif + +Color_RGB8 sTunicColors[] = { + { 30, 105, 27 }, + { 100, 20, 0 }, + { 0, 60, 100 }, +}; + +Color_RGB8 sGauntletColors[] = { + { 255, 255, 255 }, + { 254, 207, 15 }, +}; + +Gfx* sBootDListGroups[][2] = { + { gLinkAdultLeftIronBootDL, gLinkAdultRightIronBootDL }, + { gLinkAdultLeftHoverBootDL, gLinkAdultRightHoverBootDL }, +}; + +void func_8008F470(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, s32 lod, s32 tunic, + s32 boots, s32 face, OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, + void* data) { + Color_RGB8* color; + s32 eyeIndex = (jointTable[22].x & 0xF) - 1; + s32 mouthIndex = (jointTable[22].x >> 4) - 1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 1721); + + if (eyeIndex < 0) { + eyeIndex = sEyeMouthIndexes[face][0]; + } + + if (eyeIndex > 7) + eyeIndex = 7; + +#if defined(MODDING) || (_MSC_VER) + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[gSaveContext.linkAge][eyeIndex])); +#else + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[eyeIndex])); +#endif + if (mouthIndex < 0) { + mouthIndex = sEyeMouthIndexes[face][1]; + } + + if (mouthIndex > 3) + mouthIndex = 3; + +#if defined(MODDING) || (_MSC_VER) + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[gSaveContext.linkAge][mouthIndex])); +#else + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[eyeIndex])); +#endif + + color = &sTunicColors[tunic]; + gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); + + sDListsLodOffset = lod * 2; + + SkelAnime_DrawFlexLod(globalCtx, skeleton, jointTable, dListCount, overrideLimbDraw, postLimbDraw, data, lod); + + if ((overrideLimbDraw != func_800902F0) && (overrideLimbDraw != func_80090440) && (gSaveContext.gameMode != 3)) { + if (LINK_IS_ADULT) { + s32 strengthUpgrade = CUR_UPG_VALUE(UPG_STRENGTH); + + if (strengthUpgrade >= 2) { // silver or gold gauntlets + gDPPipeSync(POLY_OPA_DISP++); + + color = &sGauntletColors[strengthUpgrade - 2]; + gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); + + gSPDisplayList(POLY_OPA_DISP++, gLinkAdultLeftGauntletPlate1DL); + gSPDisplayList(POLY_OPA_DISP++, gLinkAdultRightGauntletPlate1DL); + gSPDisplayList(POLY_OPA_DISP++, + (D_80160014 == 0) ? gLinkAdultLeftGauntletPlate2DL : gLinkAdultLeftGauntletPlate3DL); + gSPDisplayList(POLY_OPA_DISP++, + (D_80160018 == 8) ? gLinkAdultRightGauntletPlate2DL : gLinkAdultRightGauntletPlate3DL); + } + + if (boots != 0) { + Gfx** bootDLists = sBootDListGroups[boots - 1]; + + gSPDisplayList(POLY_OPA_DISP++, bootDLists[0]); + gSPDisplayList(POLY_OPA_DISP++, bootDLists[1]); + } + } else { + if (Player_GetStrength() > PLAYER_STR_NONE) { + gSPDisplayList(POLY_OPA_DISP++, gLinkChildGoronBraceletDL); + } + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 1803); +} + +Vec3f D_8012602C = { 0.0f, 0.0f, 0.0f }; + +Vec3f D_80126038[] = { + { 1304.0f, 0.0f, 0.0f }, + { 695.0f, 0.0f, 0.0f }, +}; + +f32 D_80126050[] = { 1265.0f, 826.0f }; +f32 D_80126058[] = { SQ(13.04f), SQ(6.95f) }; +f32 D_80126060[] = { 10.019104f, -19.925102f }; +f32 D_80126068[] = { 5.0f, 3.0f }; + +Vec3f D_80126070 = { 0.0f, -300.0f, 0.0f }; + +void func_8008F87C(GlobalContext* globalCtx, Player* this, SkelAnime* skelAnime, Vec3f* pos, Vec3s* rot, + s32 thighLimbIndex, s32 shinLimbIndex, s32 footLimbIndex) { + Vec3f spA4; + Vec3f sp98; + Vec3f footprintPos; + CollisionPoly* sp88; + s32 sp84; + f32 sp80; + f32 sp7C; + f32 sp78; + f32 sp74; + f32 sp70; + f32 sp6C; + f32 sp68; + f32 sp64; + f32 sp60; + f32 sp5C; + f32 sp58; + f32 sp54; + f32 sp50; + s16 temp1; + s16 temp2; + s32 temp3; + + if ((this->actor.scale.y >= 0.0f) && !(this->stateFlags1 & 0x80) && + (Player_ActionToMagicSpell(this, this->itemActionParam) < 0)) { + s32 pad; + + sp7C = D_80126058[(void)0, gSaveContext.linkAge]; + sp78 = D_80126060[(void)0, gSaveContext.linkAge]; + sp74 = D_80126068[(void)0, gSaveContext.linkAge] - this->unk_6C4; + + Matrix_Push(); + Matrix_TranslateRotateZYX(pos, rot); + Matrix_MultVec3f(&D_8012602C, &spA4); + Matrix_TranslateRotateZYX(&D_80126038[(void)0, gSaveContext.linkAge], &skelAnime->jointTable[shinLimbIndex]); + Matrix_Translate(D_80126050[(void)0, gSaveContext.linkAge], 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_MultVec3f(&D_8012602C, &sp98); + Matrix_MultVec3f(&D_80126070, &footprintPos); + Matrix_Pop(); + + footprintPos.y += 15.0f; + + sp80 = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &sp88, &sp84, &this->actor, &footprintPos) + sp74; + + if (sp98.y < sp80) { + sp70 = sp98.x - spA4.x; + sp6C = sp98.y - spA4.y; + sp68 = sp98.z - spA4.z; + + sp64 = sqrtf(SQ(sp70) + SQ(sp6C) + SQ(sp68)); + sp60 = (SQ(sp64) + sp78) / (2.0f * sp64); + + sp58 = sp7C - SQ(sp60); + sp58 = (sp7C < SQ(sp60)) ? 0.0f : sqrtf(sp58); + + sp54 = Math_FAtan2F(sp58, sp60); + + sp6C = sp80 - spA4.y; + + sp64 = sqrtf(SQ(sp70) + SQ(sp6C) + SQ(sp68)); + sp60 = (SQ(sp64) + sp78) / (2.0f * sp64); + sp5C = sp64 - sp60; + + sp58 = sp7C - SQ(sp60); + sp58 = (sp7C < SQ(sp60)) ? 0.0f : sqrtf(sp58); + + sp50 = Math_FAtan2F(sp58, sp60); + + temp1 = (M_PI - (Math_FAtan2F(sp5C, sp58) + ((M_PI / 2) - sp50))) * (0x8000 / M_PI); + temp1 = temp1 - skelAnime->jointTable[shinLimbIndex].z; + + if ((s16)(ABS(skelAnime->jointTable[shinLimbIndex].x) + ABS(skelAnime->jointTable[shinLimbIndex].y)) < 0) { + temp1 += 0x8000; + } + + temp2 = (sp50 - sp54) * (0x8000 / M_PI); + rot->z -= temp2; + + skelAnime->jointTable[thighLimbIndex].z = skelAnime->jointTable[thighLimbIndex].z - temp2; + skelAnime->jointTable[shinLimbIndex].z = skelAnime->jointTable[shinLimbIndex].z + temp1; + skelAnime->jointTable[footLimbIndex].z = skelAnime->jointTable[footLimbIndex].z + temp2 - temp1; + + temp3 = func_80041D4C(&globalCtx->colCtx, sp88, sp84); + + if ((temp3 >= 2) && (temp3 < 4) && !SurfaceType_IsWallDamage(&globalCtx->colCtx, sp88, sp84)) { + footprintPos.y = sp80; + EffectSsGFire_Spawn(globalCtx, &footprintPos); + } + } + } +} + +s32 func_8008FCC8(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + Player* this = (Player*)thisx; + + if (limbIndex == PLAYER_LIMB_ROOT) { + D_80160014 = this->leftHandType; + D_80160018 = this->rightHandType; + D_80160000 = &this->swordInfo[2].base; + + if (!LINK_IS_ADULT) { + if (!(this->skelAnime.moveFlags & 4) || (this->skelAnime.moveFlags & 1)) { + pos->x *= 0.64f; + pos->z *= 0.64f; + } + + if (!(this->skelAnime.moveFlags & 4) || (this->skelAnime.moveFlags & 2)) { + pos->y *= 0.64f; + } + } + + pos->y -= this->unk_6C4; + + if (this->unk_6C2 != 0) { + Matrix_Translate(pos->x, ((Math_CosS(this->unk_6C2) - 1.0f) * 200.0f) + pos->y, pos->z, MTXMODE_APPLY); + Matrix_RotateX(this->unk_6C2 * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZYX(rot->x, rot->y, rot->z, MTXMODE_APPLY); + pos->x = pos->y = pos->z = 0.0f; + rot->x = rot->y = rot->z = 0; + } + } else { + if (*dList != NULL) { + D_80160000++; + } + + if (limbIndex == PLAYER_LIMB_HEAD) { + rot->x += this->unk_6BA; + rot->y -= this->unk_6B8; + rot->z += this->unk_6B6; + } else if (limbIndex == PLAYER_LIMB_UPPER) { + if (this->unk_6B0 != 0) { + Matrix_RotateZ(0x44C * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateY(this->unk_6B0 * (M_PI / 0x8000), MTXMODE_APPLY); + } + if (this->unk_6BE != 0) { + Matrix_RotateY(this->unk_6BE * (M_PI / 0x8000), MTXMODE_APPLY); + } + if (this->unk_6BC != 0) { + Matrix_RotateX(this->unk_6BC * (M_PI / 0x8000), MTXMODE_APPLY); + } + if (this->unk_6C0 != 0) { + Matrix_RotateZ(this->unk_6C0 * (M_PI / 0x8000), MTXMODE_APPLY); + } + } else if (limbIndex == PLAYER_LIMB_L_THIGH) { + func_8008F87C(globalCtx, this, &this->skelAnime, pos, rot, PLAYER_LIMB_L_THIGH, PLAYER_LIMB_L_SHIN, + PLAYER_LIMB_L_FOOT); + } else if (limbIndex == PLAYER_LIMB_R_THIGH) { + func_8008F87C(globalCtx, this, &this->skelAnime, pos, rot, PLAYER_LIMB_R_THIGH, PLAYER_LIMB_R_SHIN, + PLAYER_LIMB_R_FOOT); + return false; + } else { + return false; + } + } + + return false; +} + +s32 func_80090014(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + Player* this = (Player*)thisx; + + if (!func_8008FCC8(globalCtx, limbIndex, dList, pos, rot, thisx)) { + if (limbIndex == PLAYER_LIMB_L_HAND) { + Gfx** dLists = this->leftHandDLists; + + if ((D_80160014 == 4) && (gSaveContext.swordHealth <= 0.0f)) { + dLists += 4; + } else if ((D_80160014 == 6) && (this->stateFlags1 & 0x2000000)) { + dLists = &D_80125E08[gSaveContext.linkAge]; + D_80160014 = 0; + } else if ((this->leftHandType == 0) && (this->actor.speedXZ > 2.0f) && !(this->stateFlags1 & 0x8000000)) { + dLists = &D_80125E18[gSaveContext.linkAge]; + D_80160014 = 1; + } + + *dList = ResourceMgr_LoadGfxByName(dLists[sDListsLodOffset]); + } else if (limbIndex == PLAYER_LIMB_R_HAND) { + Gfx** dLists = this->rightHandDLists; + + if (D_80160018 == 10) { + dLists += this->currentShield * 4; + } else if ((this->rightHandType == 8) && (this->actor.speedXZ > 2.0f) && !(this->stateFlags1 & 0x8000000)) { + dLists = &D_80125E58[gSaveContext.linkAge]; + D_80160018 = 9; + } + + *dList = ResourceMgr_LoadGfxByName(dLists[sDListsLodOffset]); + } else if (limbIndex == PLAYER_LIMB_SHEATH) { + Gfx** dLists = this->sheathDLists; + + if ((this->sheathType == 18) || (this->sheathType == 19)) { + dLists += this->currentShield * 4; + if (!LINK_IS_ADULT && (this->currentShield < PLAYER_SHIELD_HYLIAN) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI)) { + dLists += 16; + } + } else if (!LINK_IS_ADULT && ((this->sheathType == 16) || (this->sheathType == 17)) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI)) { + dLists = &D_80125D28[16]; + } + + if (dLists[sDListsLodOffset] != NULL) { + *dList = ResourceMgr_LoadGfxByName(dLists[sDListsLodOffset]); + } else { + *dList = NULL; + } + + + } else if (limbIndex == PLAYER_LIMB_WAIST) { + *dList = ResourceMgr_LoadGfxByName(this->waistDLists[sDListsLodOffset]); + } + } + + return false; +} + +s32 func_800902F0(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + Player* this = (Player*)thisx; + + if (!func_8008FCC8(globalCtx, limbIndex, dList, pos, rot, thisx)) { + if (this->unk_6AD != 2) { + *dList = NULL; + } else if (limbIndex == PLAYER_LIMB_L_FOREARM) { + *dList = sArmOutDLs[(void)0, gSaveContext.linkAge]; + } else if (limbIndex == PLAYER_LIMB_L_HAND) { + *dList = sHandOutDLs[(void)0, gSaveContext.linkAge]; + } else if (limbIndex == PLAYER_LIMB_R_SHOULDER) { + *dList = sRightShoulderNearDLs[(void)0, gSaveContext.linkAge]; + } else if (limbIndex == PLAYER_LIMB_R_FOREARM) { + *dList = D_80125F30[(void)0, gSaveContext.linkAge]; + } else if (limbIndex == PLAYER_LIMB_R_HAND) { + *dList = Player_HoldsHookshot(this) ? gLinkAdultRightHandHoldingHookshotFarDL + : sHoldingFirstPersonWeaponDLs[(void)0, gSaveContext.linkAge]; + } else { + *dList = NULL; + } + } + + return false; +} + +s32 func_80090440(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + if (!func_8008FCC8(globalCtx, limbIndex, dList, pos, rot, thisx)) { + *dList = NULL; + } + + return false; +} + +u8 func_80090480(GlobalContext* globalCtx, ColliderQuad* collider, WeaponInfo* weaponInfo, Vec3f* newTip, + Vec3f* newBase) { + if (weaponInfo->active == 0) { + if (collider != NULL) { + Collider_ResetQuadAT(globalCtx, &collider->base); + } + Math_Vec3f_Copy(&weaponInfo->tip, newTip); + Math_Vec3f_Copy(&weaponInfo->base, newBase); + weaponInfo->active = 1; + return 1; + } else if ((weaponInfo->tip.x == newTip->x) && (weaponInfo->tip.y == newTip->y) && + (weaponInfo->tip.z == newTip->z) && (weaponInfo->base.x == newBase->x) && + (weaponInfo->base.y == newBase->y) && (weaponInfo->base.z == newBase->z)) { + if (collider != NULL) { + Collider_ResetQuadAT(globalCtx, &collider->base); + } + return 0; + } else { + if (collider != NULL) { + Collider_SetQuadVertices(collider, newBase, newTip, &weaponInfo->base, &weaponInfo->tip); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &collider->base); + } + Math_Vec3f_Copy(&weaponInfo->base, newBase); + Math_Vec3f_Copy(&weaponInfo->tip, newTip); + weaponInfo->active = 1; + return 1; + } +} + +void func_80090604(GlobalContext* globalCtx, Player* this, ColliderQuad* collider, Vec3f* quadSrc) { + static u8 shieldColTypes[PLAYER_SHIELD_MAX] = { + COLTYPE_METAL, + COLTYPE_WOOD, + COLTYPE_METAL, + COLTYPE_METAL, + }; + + if (this->stateFlags1 & 0x400000) { + Vec3f quadDest[4]; + + this->shieldQuad.base.colType = shieldColTypes[this->currentShield]; + + Matrix_MultVec3f(&quadSrc[0], &quadDest[0]); + Matrix_MultVec3f(&quadSrc[1], &quadDest[1]); + Matrix_MultVec3f(&quadSrc[2], &quadDest[2]); + Matrix_MultVec3f(&quadSrc[3], &quadDest[3]); + Collider_SetQuadVertices(collider, &quadDest[0], &quadDest[1], &quadDest[2], &quadDest[3]); + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &collider->base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &collider->base); + } +} + +Vec3f D_80126080 = { 5000.0f, 400.0f, 0.0f }; +Vec3f D_8012608C = { 5000.0f, -400.0f, 1000.0f }; +Vec3f D_80126098 = { 5000.0f, 1400.0f, -1000.0f }; + +Vec3f D_801260A4[3] = { + { 0.0f, 400.0f, 0.0f }, + { 0.0f, 1400.0f, -1000.0f }, + { 0.0f, -400.0f, 1000.0f }, +}; + +void func_800906D4(GlobalContext* globalCtx, Player* this, Vec3f* newTipPos) { + Vec3f newBasePos[3]; + + Matrix_MultVec3f(&D_801260A4[0], &newBasePos[0]); + Matrix_MultVec3f(&D_801260A4[1], &newBasePos[1]); + Matrix_MultVec3f(&D_801260A4[2], &newBasePos[2]); + + if (func_80090480(globalCtx, NULL, &this->swordInfo[0], &newTipPos[0], &newBasePos[0]) && + !(this->stateFlags1 & 0x400000)) { + EffectBlure_AddVertex(Effect_GetByIndex(this->swordEffectIndex), &this->swordInfo[0].tip, + &this->swordInfo[0].base); + } + + if ((this->swordState > 0) && ((this->swordAnimation < 0x18) || (this->stateFlags2 & 0x20000))) { + func_80090480(globalCtx, &this->swordQuads[0], &this->swordInfo[1], &newTipPos[1], &newBasePos[1]); + func_80090480(globalCtx, &this->swordQuads[1], &this->swordInfo[2], &newTipPos[2], &newBasePos[2]); + } +} + +void Player_DrawGetItemImpl(GlobalContext* globalCtx, Player* this, Vec3f* refPos, s32 drawIdPlusOne) { + f32 height = (this->exchangeItemId != EXCH_ITEM_NONE) ? 6.0f : 14.0f; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2401); + + gSegments[6] = VIRTUAL_TO_PHYSICAL(this->giObjectSegment); + + gSPSegment(POLY_OPA_DISP++, 0x06, this->giObjectSegment); + gSPSegment(POLY_XLU_DISP++, 0x06, this->giObjectSegment); + + Matrix_Translate(refPos->x + (3.3f * Math_SinS(this->actor.shape.rot.y)), refPos->y + height, + refPos->z + ((3.3f + (IREG(90) / 10.0f)) * Math_CosS(this->actor.shape.rot.y)), MTXMODE_NEW); + Matrix_RotateZYX(0, globalCtx->gameplayFrames * 1000, 0, MTXMODE_APPLY); + Matrix_Scale(0.2f, 0.2f, 0.2f, MTXMODE_APPLY); + + GetItem_Draw(globalCtx, drawIdPlusOne - 1); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2421); +} + +void Player_DrawGetItem(GlobalContext* globalCtx, Player* this) { + //if (!this->giObjectLoading || !osRecvMesg(&this->giObjectLoadQueue, NULL, OS_MESG_NOBLOCK)) // OTRTODO: Do something about osRecvMesg here... + { + this->giObjectLoading = false; + Player_DrawGetItemImpl(globalCtx, this, &sGetItemRefPos, ABS(this->unk_862)); + } +} + +void func_80090A28(Player* this, Vec3f* vecs) { + D_8012608C.x = D_80126080.x; + + if (this->unk_845 >= 3) { + this->unk_845 += 1; + D_8012608C.x *= 1.0f + ((9 - this->unk_845) * 0.1f); + } + + D_8012608C.x += 1200.0f; + D_80126098.x = D_8012608C.x; + + Matrix_MultVec3f(&D_80126080, &vecs[0]); + Matrix_MultVec3f(&D_8012608C, &vecs[1]); + Matrix_MultVec3f(&D_80126098, &vecs[2]); +} + +void Player_DrawHookshotReticle(GlobalContext* globalCtx, Player* this, f32 arg2) { + static Vec3f D_801260C8 = { -500.0f, -100.0f, 0.0f }; + CollisionPoly* sp9C; + s32 bgId; + Vec3f sp8C; + Vec3f sp80; + Vec3f sp74; + Vec3f sp68; + f32 sp64; + f32 sp60; + + D_801260C8.z = 0.0f; + Matrix_MultVec3f(&D_801260C8, &sp8C); + D_801260C8.z = arg2; + Matrix_MultVec3f(&D_801260C8, &sp80); + + if (1) {} + + if (BgCheck_AnyLineTest3(&globalCtx->colCtx, &sp8C, &sp80, &sp74, &sp9C, 1, 1, 1, 1, &bgId)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2572); + + OVERLAY_DISP = Gfx_CallSetupDL(OVERLAY_DISP, 0x07); + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &sp74, &sp68, &sp64); + + sp60 = (sp64 < 200.0f) ? 0.08f : (sp64 / 200.0f) * 0.08f; + + Matrix_Translate(sp74.x, sp74.y, sp74.z, MTXMODE_NEW); + Matrix_Scale(sp60, sp60, sp60, MTXMODE_APPLY); + + gSPMatrix(OVERLAY_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_player_lib.c", 2587), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(OVERLAY_DISP++, 0x06, globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + gSPDisplayList(OVERLAY_DISP++, gLinkAdultHookshotReticleDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2592); + } +} + +Vec3f D_801260D4 = { 1100.0f, -700.0f, 0.0f }; + +f32 sSwordLengths[] = { + 0.0f, 4000.0f, 3000.0f, 5500.0f, 0.0f, 2500.0f, +}; + +Gfx* sBottleDLists[] = { gLinkAdultBottleDL, gLinkChildBottleDL }; + +Color_RGB8 sBottleColors[] = { + { 255, 255, 255 }, { 80, 80, 255 }, { 255, 100, 255 }, { 0, 0, 255 }, { 255, 0, 255 }, + { 255, 0, 255 }, { 200, 200, 100 }, { 255, 0, 0 }, { 0, 0, 255 }, { 0, 255, 0 }, + { 255, 255, 255 }, { 255, 255, 255 }, { 80, 80, 255 }, +}; + +Vec3f D_80126128 = { 398.0f, 1419.0f, 244.0f }; + +BowStringData sBowStringData[] = { + { gLinkAdultBowStringDL, { 0.0f, -360.4f, 0.0f } }, // bow + { gLinkChildSlinghotStringDL, { 606.0f, 236.0f, 0.0f } }, // slingshot +}; + +Vec3f D_80126154[] = { + { -4500.0f, -3000.0f, -600.0f }, + { 1500.0f, -3000.0f, -600.0f }, + { -4500.0f, 3000.0f, -600.0f }, + { 1500.0f, 3000.0f, -600.0f }, +}; + +Vec3f D_80126184 = { 100.0f, 1500.0f, 0.0f }; +Vec3f D_80126190 = { 100.0f, 1640.0f, 0.0f }; + +Vec3f D_8012619C[] = { + { -3000.0f, -3000.0f, -900.0f }, + { 3000.0f, -3000.0f, -900.0f }, + { -3000.0f, 3000.0f, -900.0f }, + { 3000.0f, 3000.0f, -900.0f }, +}; + +Vec3f D_801261CC = { 630.0f, 100.0f, -30.0f }; +Vec3s D_801261D8 = { 0, 0, 0x7FFF }; + +Vec3f D_801261E0[] = { + { 200.0f, 300.0f, 0.0f }, + { 200.0f, 200.0f, 0.0f }, +}; + +void func_80090D20(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + Player* this = (Player*)thisx; + + if (*dList != NULL) { + Matrix_MultVec3f(&D_8012602C, D_80160000); + } + + if (limbIndex == PLAYER_LIMB_L_HAND) { + MtxF sp14C; + Actor* hookedActor; + + Math_Vec3f_Copy(&this->leftHandPos, D_80160000); + + if (this->itemActionParam == PLAYER_AP_STICK) { + Vec3f sp124[3]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2633); + + if (this->actor.scale.y >= 0.0f) { + D_80126080.x = this->unk_85C * 5000.0f; + func_80090A28(this, sp124); + if (this->swordState != 0) { + func_800906D4(globalCtx, this, sp124); + } else { + Math_Vec3f_Copy(&this->swordInfo[0].tip, &sp124[0]); + } + } + + Matrix_Translate(-428.26f, 267.2f, -33.82f, MTXMODE_APPLY); + Matrix_RotateZYX(-0x8000, 0, 0x4000, MTXMODE_APPLY); + Matrix_Scale(1.0f, this->unk_85C, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_player_lib.c", 2653), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gLinkChildLinkDekuStickDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2656); + } else if ((this->actor.scale.y >= 0.0f) && (this->swordState != 0)) { + Vec3f spE4[3]; + + if (Player_HoldsBrokenKnife(this)) { + D_80126080.x = 1500.0f; + } else { + D_80126080.x = sSwordLengths[Player_GetSwordHeld(this)]; + } + + func_80090A28(this, spE4); + func_800906D4(globalCtx, this, spE4); + } else if ((*dList != NULL) && (this->leftHandType == 7)) { + Color_RGB8* bottleColor = &sBottleColors[Player_ActionToBottle(this, this->itemActionParam)]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2710); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_player_lib.c", 2712), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_XLU_DISP++, bottleColor->r, bottleColor->g, bottleColor->b, 0); + gSPDisplayList(POLY_XLU_DISP++, sBottleDLists[((void)0, gSaveContext.linkAge)]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2717); + } + + if (this->actor.scale.y >= 0.0f) { + if (!Player_HoldsHookshot(this) && ((hookedActor = this->heldActor) != NULL)) { + if (this->stateFlags1 & 0x200) { + Matrix_MultVec3f(&D_80126128, &hookedActor->world.pos); + Matrix_RotateZYX(0x69E8, -0x5708, 0x458E, MTXMODE_APPLY); + Matrix_Get(&sp14C); + Matrix_MtxFToYXZRotS(&sp14C, &hookedActor->world.rot, 0); + hookedActor->shape.rot = hookedActor->world.rot; + } else if (this->stateFlags1 & 0x800) { + Vec3s spB8; + + Matrix_Get(&sp14C); + Matrix_MtxFToYXZRotS(&sp14C, &spB8, 0); + + if (hookedActor->flags & ACTOR_FLAG_17) { + hookedActor->world.rot.x = hookedActor->shape.rot.x = spB8.x - this->unk_3BC.x; + } else { + hookedActor->world.rot.y = hookedActor->shape.rot.y = this->actor.shape.rot.y + this->unk_3BC.y; + } + } + } else { + Matrix_Get(&this->mf_9E0); + Matrix_MtxFToYXZRotS(&this->mf_9E0, &this->unk_3BC, 0); + } + } + } else if (limbIndex == PLAYER_LIMB_R_HAND) { + Actor* heldActor = this->heldActor; + + if (this->rightHandType == 0xFF) { + Matrix_Get(&this->shieldMf); + } else if ((this->rightHandType == 11) || (this->rightHandType == 12)) { + BowStringData* stringData = &sBowStringData[gSaveContext.linkAge]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2783); + + Matrix_Push(); + Matrix_Translate(stringData->pos.x, stringData->pos.y, stringData->pos.z, MTXMODE_APPLY); + + if ((this->stateFlags1 & 0x200) && (this->unk_860 >= 0) && (this->unk_834 <= 10)) { + Vec3f sp90; + f32 distXYZ; + + Matrix_MultVec3f(&D_8012602C, &sp90); + distXYZ = Math_Vec3f_DistXYZ(D_80160000, &sp90); + + this->unk_858 = distXYZ - 3.0f; + if (distXYZ < 3.0f) { + this->unk_858 = 0.0f; + } else { + this->unk_858 *= 1.6f; + if (this->unk_858 > 1.0f) { + this->unk_858 = 1.0f; + } + } + + this->unk_85C = -0.5f; + } + + Matrix_Scale(1.0f, this->unk_858, 1.0f, MTXMODE_APPLY); + + if (!LINK_IS_ADULT) { + Matrix_RotateZ(this->unk_858 * -0.2f, MTXMODE_APPLY); + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_player_lib.c", 2804), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, stringData->dList); + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 2809); + } else if ((this->actor.scale.y >= 0.0f) && (this->rightHandType == 10)) { + Matrix_Get(&this->shieldMf); + func_80090604(globalCtx, this, &this->shieldQuad, D_80126154); + } + + if (this->actor.scale.y >= 0.0f) { + if ((this->heldItemActionParam == PLAYER_AP_HOOKSHOT) || + (this->heldItemActionParam == PLAYER_AP_LONGSHOT)) { + Matrix_MultVec3f(&D_80126184, &this->unk_3C8); + + if (heldActor != NULL) { + MtxF sp44; + s32 pad; + + Matrix_MultVec3f(&D_80126190, &heldActor->world.pos); + Matrix_RotateZYX(0, -0x4000, -0x4000, MTXMODE_APPLY); + Matrix_Get(&sp44); + Matrix_MtxFToYXZRotS(&sp44, &heldActor->world.rot, 0); + heldActor->shape.rot = heldActor->world.rot; + + if (func_8002DD78(this) != 0) { + Matrix_Translate(500.0f, 300.0f, 0.0f, MTXMODE_APPLY); + Player_DrawHookshotReticle( + globalCtx, this, (this->heldItemActionParam == PLAYER_AP_HOOKSHOT) ? 38600.0f : 77600.0f); + } + } + } + + if ((this->unk_862 != 0) || ((func_8002DD6C(this) == 0) && (heldActor != NULL))) { + if (!(this->stateFlags1 & 0x400) && (this->unk_862 != 0) && (this->exchangeItemId != EXCH_ITEM_NONE)) { + Math_Vec3f_Copy(&sGetItemRefPos, &this->leftHandPos); + } else { + sGetItemRefPos.x = (this->bodyPartsPos[15].x + this->leftHandPos.x) * 0.5f; + sGetItemRefPos.y = (this->bodyPartsPos[15].y + this->leftHandPos.y) * 0.5f; + sGetItemRefPos.z = (this->bodyPartsPos[15].z + this->leftHandPos.z) * 0.5f; + } + + if (this->unk_862 == 0) { + Math_Vec3f_Copy(&heldActor->world.pos, &sGetItemRefPos); + } + } + } + } else if (this->actor.scale.y >= 0.0f) { + if (limbIndex == PLAYER_LIMB_SHEATH) { + if ((this->rightHandType != 10) && (this->rightHandType != 0xFF)) { + if (Player_IsChildWithHylianShield(this)) { + func_80090604(globalCtx, this, &this->shieldQuad, D_8012619C); + } + + Matrix_TranslateRotateZYX(&D_801261CC, &D_801261D8); + Matrix_Get(&this->shieldMf); + } + } else if (limbIndex == PLAYER_LIMB_HEAD) { + Matrix_MultVec3f(&D_801260D4, &this->actor.focus.pos); + } else { + Vec3f* vec = &D_801261E0[((void)0, gSaveContext.linkAge)]; + + Actor_SetFeetPos(&this->actor, limbIndex, PLAYER_LIMB_L_FOOT, vec, PLAYER_LIMB_R_FOOT, vec); + } + } +} + +u32 func_80091738(GlobalContext* globalCtx, u8* segment, SkelAnime* skelAnime) { + s16 linkObjectId = gLinkObjectIds[(void)0, gSaveContext.linkAge]; + size_t size; + void* ptr; + + size = gObjectTable[OBJECT_GAMEPLAY_KEEP].vromEnd - gObjectTable[OBJECT_GAMEPLAY_KEEP].vromStart; + ptr = segment + 0x3800; + DmaMgr_SendRequest1(ptr, gObjectTable[OBJECT_GAMEPLAY_KEEP].vromStart, size, "../z_player_lib.c", 2982); + + size = gObjectTable[linkObjectId].vromEnd - gObjectTable[linkObjectId].vromStart; + ptr = segment + 0x8800; + DmaMgr_SendRequest1(ptr, gObjectTable[linkObjectId].vromStart, size, "../z_player_lib.c", 2988); + + ptr = (void*)ALIGN16((intptr_t)ptr + size); + + gSegments[4] = VIRTUAL_TO_PHYSICAL(segment + 0x3800); + gSegments[6] = VIRTUAL_TO_PHYSICAL(segment + 0x8800); + + SkelAnime_InitLink(globalCtx, skelAnime, gPlayerSkelHeaders[(void)0, gSaveContext.linkAge], &gPlayerAnim_003238, 9, + ptr, ptr, PLAYER_LIMB_MAX); + + return size + 0x8800 + 0x90; +} + +u8 D_801261F8[] = { 2, 2, 5 }; + +s32 func_80091880(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* arg) { + u8* ptr = arg; + u8 modelGroup = D_801261F8[ptr[0] > 0 ? ptr[0] - 1 : 0]; + s32 type; + s32 dListOffset = 0; + Gfx** dLists; + + if ((modelGroup == 2) && !LINK_IS_ADULT && (ptr[1] == 2)) { + modelGroup = 1; + } + + if (limbIndex == PLAYER_LIMB_L_HAND) { + type = gPlayerModelTypes[modelGroup][1]; + D_80160014 = type; + + if (ptr[0] == 0) + type = 0; + + if ((type == 4) && (gSaveContext.swordHealth <= 0.0f)) { + dListOffset = 4; + } + } else if (limbIndex == PLAYER_LIMB_R_HAND) { + type = gPlayerModelTypes[modelGroup][2]; + D_80160018 = type; + if (type == 10) { + dListOffset = ptr[1] * sizeof(uintptr_t); + } + } else if (limbIndex == PLAYER_LIMB_SHEATH) { + type = gPlayerModelTypes[modelGroup][3]; + if ((type == 18) || (type == 19)) { + dListOffset = ptr[1] * sizeof(uintptr_t); + } else if (type == 16 && CVar_GetS32("gPauseLiveLink", 0)) { + //if (ptr[0] == 1) + //dListOffset = 4; + } + } else if (limbIndex == PLAYER_LIMB_WAIST) { + type = gPlayerModelTypes[modelGroup][4]; + } else { + return 0; + } + + dLists = &sPlayerDListGroups[type][(void)0, gSaveContext.linkAge]; + *dList = dLists[dListOffset]; + + return 0; +} + +#include + +void Pause_DrawTriforceSpot(GlobalContext* globalCtx, s32 showLightColumn) { + static DemoEffect triforce; + static s16 rotation = 0; + + triforce.triforceSpot.crystalLightOpacity = 244; + triforce.triforceSpot.triforceSpotOpacity = 249; + triforce.triforceSpot.lightColumnOpacity = showLightColumn ? 244 : 0; + triforce.triforceSpot.rotation = rotation; + + DemoEffect_DrawTriforceSpot(&triforce, globalCtx); + + rotation += 0x03E8; +} + +void func_80091A24(GlobalContext* globalCtx, void* seg04, void* seg06, SkelAnime* skelAnime, Vec3f* pos, Vec3s* rot, + f32 scale, s32 sword, s32 tunic, s32 shield, s32 boots, s32 width, s32 height, Vec3f* eye, Vec3f* at, + f32 fovy, void* img1, void* img2) { + static Vp viewport = { 128, 224, 511, 0, 128, 224, 511, 0 }; + static Lights1 lights1 = gdSPDefLights1(80, 80, 80, 255, 255, 255, 84, 84, 172); + static Vec3f lightDir = { 89.8f, 0.0f, 89.8f }; + u8 sp12C[2]; + Gfx* opaRef; + Gfx* xluRef; + Gfx* kalRef; + u16 perspNorm; + Mtx* perspMtx = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx)); + Mtx* lookAtMtx = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 3129); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + + gDPPipeSync(POLY_OPA_DISP++); + + gSPLoadGeometryMode(POLY_OPA_DISP++, 0); + gSPTexture(POLY_OPA_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_SHADE, G_CC_SHADE); + gDPSetOtherMode(POLY_OPA_DISP++, + G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + gSPLoadGeometryMode(POLY_OPA_DISP++, G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH); + gDPSetScissor(POLY_OPA_DISP++, G_SC_NON_INTERLACE, 0, 0, width, height); + gSPClipRatio(POLY_OPA_DISP++, FRUSTRATIO_1); + + viewport.vp.vscale[0] = viewport.vp.vtrans[0] = width * 2; + viewport.vp.vscale[1] = viewport.vp.vtrans[1] = height * 2; + gSPViewport(POLY_OPA_DISP++, &viewport); + + guPerspective(perspMtx, &perspNorm, fovy, (f32)width / (f32)height, 10.0f, 4000.0f, 1.0f); + + gSPPerspNormalize(POLY_OPA_DISP++, perspNorm); + gSPMatrix(POLY_OPA_DISP++, perspMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + + guLookAt(lookAtMtx, eye->x, eye->y, eye->z, at->x, at->y, at->z, 0.0f, 1.0f, 0.0f); + + gSPMatrix(POLY_OPA_DISP++, lookAtMtx, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + + sp12C[0] = sword; + sp12C[1] = shield; + + Matrix_SetTranslateRotateYXZ(pos->x - (LINK_AGE_IN_YEARS == YEARS_ADULT ? 25 : 0), + pos->y - (CVar_GetS32("gPauseTriforce", 0) ? 16 : 0), pos->z, rot); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + + gSPSegment(POLY_OPA_DISP++, 0x04, seg04); + gSPSegment(POLY_OPA_DISP++, 0x06, seg06); + + gSPSetLights1(POLY_OPA_DISP++, lights1); + + func_80093C80(globalCtx); + + POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP++, 0, 0, 0, 0, 997, 1000); + + func_8002EABC(pos, &globalCtx->view.eye, &lightDir, globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x0C, gCullBackDList); + + func_8008F470(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, 0, tunic, boots, 0, + func_80091880, NULL, &sp12C); + + if (CVar_GetS32("gPauseTriforce", 0)) { + + Matrix_SetTranslateRotateYXZ(pos->x - (LINK_AGE_IN_YEARS == YEARS_ADULT ? 25 : 0), + pos->y + 280 + (LINK_AGE_IN_YEARS == YEARS_ADULT ? 48 : 0), pos->z, rot); + Matrix_Scale(scale * 1, scale * 1, scale * 1, MTXMODE_APPLY); + + Gfx* ohNo = POLY_XLU_DISP; + POLY_XLU_DISP = POLY_OPA_DISP; + + Pause_DrawTriforceSpot(globalCtx, 1); + + POLY_OPA_DISP = POLY_XLU_DISP; + POLY_XLU_DISP = ohNo; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player_lib.c", 3288); +} + +void func_8009214C(GlobalContext* globalCtx, u8* segment, SkelAnime* skelAnime, Vec3f* pos, Vec3s* rot, f32 scale, + s32 sword, s32 tunic, s32 shield, s32 boots) { + Vec3f eye = { 0.0f, 0.0f, -400.0f }; + Vec3f at = { 0.0f, 0.0f, 0.0f }; + Vec3s* destTable; + Vec3s* srcTable; + s32 i; + + gSegments[4] = VIRTUAL_TO_PHYSICAL(segment + 0x3800); + gSegments[6] = VIRTUAL_TO_PHYSICAL(segment + 0x8800); + + if (CVar_GetS32("gPauseLiveLink", 0) || CVar_GetS32("gPauseTriforce", 0)) { + uintptr_t anim = gPlayerAnim_003238; // idle + + if (CUR_EQUIP_VALUE(EQUIP_SWORD) >= 3) + anim = gPlayerAnim_002BE0; // Two Handed Anim + else if (CUR_EQUIP_VALUE(EQUIP_SHIELD) == 0) + anim = gPlayerAnim_003240; + else if (CUR_EQUIP_VALUE(EQUIP_SHIELD) == 2 && LINK_AGE_IN_YEARS == YEARS_CHILD) + anim = gPlayerAnim_003240; + + //anim = gPlayerAnim_003428; // Use for biggoron sword? + + if (CVar_GetS32("gPauseTriforce", 0)) { + anim = gPlayerAnim_002D00; + sword = 0; + shield = 0; + } + + if (skelAnime->animation != anim) { + LinkAnimation_Change(globalCtx, skelAnime, anim, 1.0f, 0.0f, Animation_GetLastFrame(anim), ANIMMODE_LOOP, + -6.0f); + } + + LinkAnimation_Update(globalCtx, skelAnime); + + if (!LINK_IS_ADULT) { + // Link is placed too far up by default when animating + at.y += 60; + } + } else { + + if (!LINK_IS_ADULT) { + if (shield == PLAYER_SHIELD_DEKU) { + srcTable = D_040020D0; + } else { + srcTable = D_04002040; + } + } else { + if (sword == 3) { + srcTable = D_04002160; + } else if (shield != PLAYER_SHIELD_NONE) { + srcTable = D_04002280; + } else { + srcTable = D_040021F0; + } + } + + srcTable = ResourceMgr_LoadArrayByNameAsVec3s(srcTable); + destTable = skelAnime->jointTable; + for (i = 0; i < skelAnime->limbCount; i++) { + *destTable++ = *srcTable++; + } + } + + + func_80091A24(globalCtx, segment + 0x3800, segment + 0x8800, skelAnime, pos, rot, scale, sword, tunic, shield, + boots, 64, 112, &eye, &at, 60.0f, globalCtx->state.gfxCtx->curFrameBuffer, + globalCtx->state.gfxCtx->curFrameBuffer + 0x1C00); +} diff --git a/soh/src/code/z_prenmi.c b/soh/src/code/z_prenmi.c new file mode 100644 index 000000000..9f0d3ad58 --- /dev/null +++ b/soh/src/code/z_prenmi.c @@ -0,0 +1,65 @@ +#include "global.h" +#include "vt.h" + +void func_80092320(PreNMIContext* this) { + this->state.running = false; + this->state.init = NULL; + this->state.size = 0; +} + +void PreNMI_Update(PreNMIContext* this) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "prenmi_move\n" VT_RST); + + // Strings existing only in rodata + if (0) { + osSyncPrintf("../z_prenmi.c"); + osSyncPrintf("(int)volume = %d\n"); + } + + if (this->timer == 0) { + ViConfig_UpdateVi(1); + func_80092320(this); + return; + } + + this->timer--; +} + +void PreNMI_Draw(PreNMIContext* this) { + GraphicsContext* gfxCtx = this->state.gfxCtx; + + osSyncPrintf(VT_COL(YELLOW, BLACK) "prenmi_draw\n" VT_RST); + + OPEN_DISPS(gfxCtx, "../z_prenmi.c", 96); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + func_80095248(gfxCtx, 0, 0, 0); + func_800940B0(gfxCtx); + gDPSetFillColor(POLY_OPA_DISP++, (GPACK_RGBA5551(255, 255, 255, 1) << 16) | GPACK_RGBA5551(255, 255, 255, 1)); + gDPFillRectangle(POLY_OPA_DISP++, 0, this->timer + 100, SCREEN_WIDTH - 1, this->timer + 100); + + CLOSE_DISPS(gfxCtx, "../z_prenmi.c", 112); +} + +void PreNMI_Main(GameState* thisx) { + PreNMIContext* this = (PreNMIContext*)thisx; + + PreNMI_Update(this); + PreNMI_Draw(this); + + this->state.unk_A0 = 1; +} + +void PreNMI_Destroy(GameState* thisx) { +} + +void PreNMI_Init(GameState* thisx) { + PreNMIContext* this = (PreNMIContext*)thisx; + + this->state.main = PreNMI_Main; + this->state.destroy = PreNMI_Destroy; + this->timer = 30; + this->unk_A8 = 10; + + R_UPDATE_RATE = 1; +} diff --git a/soh/src/code/z_prenmi_buff.c b/soh/src/code/z_prenmi_buff.c new file mode 100644 index 000000000..c00d0513c --- /dev/null +++ b/soh/src/code/z_prenmi_buff.c @@ -0,0 +1,27 @@ +#include "global.h" + +#define COLD_RESET 0 +#define NMI 1 + +void PreNmiBuff_Init(PreNmiBuff* this) { + this->resetting = false; + + if (osResetType == COLD_RESET) { + this->resetCount = 0; + this->duration = 0; + } else { + this->resetCount++; + this->duration += this->resetTime; + } + + this->resetTime = 0; +} + +void PreNmiBuff_SetReset(PreNmiBuff* this) { + this->resetting = true; + this->resetTime = osGetTime(); +} + +u32 PreNmiBuff_IsResetting(PreNmiBuff* this) { + return this->resetting; +} diff --git a/soh/src/code/z_quake.c b/soh/src/code/z_quake.c new file mode 100644 index 000000000..997d068c0 --- /dev/null +++ b/soh/src/code/z_quake.c @@ -0,0 +1,407 @@ +#include "global.h" +#include "vt.h" + +#include + +QuakeRequest sQuakeRequest[4]; +s16 D_80126250 = 1; +s16 sQuakeRequestCount = 0; + +s16 (*sQuakeCallbacks[])(QuakeRequest*, ShakeInfo*) = { + NULL, Quake_Callback1, Quake_Callback2, Quake_Callback3, Quake_Callback4, Quake_Callback5, Quake_Callback6, +}; + +Vec3f* Quake_AddVec(Vec3f* dst, Vec3f* arg1, VecSph* arg2) { + Vec3f vec1; + Vec3f vec2; + + OLib_VecSphGeoToVec3f(&vec2, arg2); + vec1.x = arg1->x + vec2.x; + vec1.y = arg1->y + vec2.y; + vec1.z = arg1->z + vec2.z; + *dst = vec1; + return dst; +} + +void Quake_UpdateShakeInfo(QuakeRequest* req, ShakeInfo* shake, f32 y, f32 x) { + Vec3f* unk50 = &req->cam->at; + Vec3f* unk5C = &req->cam->eye; + + Vec3f vec; + VecSph struc2; + VecSph struc1; + Vec3f vec2; + + if (req->unk_1C) { + vec.x = 0; + vec.y = 0; + vec.z = 0; + OLib_Vec3fDiffToVecSphGeo(&struc1, unk5C, unk50); + struc2.r = req->y * y; + struc2.pitch = struc1.pitch + req->unk_14.unk_00 + 0x4000; + struc2.yaw = struc1.yaw + req->unk_14.unk_02; + Quake_AddVec(&vec, &vec, &struc2); + struc2.r = req->x * x; + struc2.pitch = struc1.pitch + req->unk_14.unk_00; + struc2.yaw = struc1.yaw + req->unk_14.unk_02 + 0x4000; + Quake_AddVec(&vec, &vec, &struc2); + } else { + vec.x = 0; + vec.y = req->y * y; + vec.z = 0; + struc2.r = req->x * x; + struc2.pitch = req->unk_14.unk_00; + struc2.yaw = req->unk_14.unk_02; + Quake_AddVec(&vec, &vec, &struc2); + } + + vec2 = vec; + shake->vec2 = vec2; + shake->vec1 = vec2; + shake->unk_1A = (f32)0x8000 * y; + shake->rotZ = req->rotZ * y; + shake->zoom = req->zoom * y; +} + +s16 Quake_Callback1(QuakeRequest* req, ShakeInfo* shake) { + s32 pad; + + if (req->countdown > 0) { + f32 a = Math_SinS(req->speed * req->countdown); + + Quake_UpdateShakeInfo(req, shake, a, Rand_ZeroOne() * a); + req->countdown--; + } + return req->countdown; +} + +s16 Quake_Callback5(QuakeRequest* req, ShakeInfo* shake) { + if (req->countdown > 0) { + f32 a = Math_SinS(req->speed * req->countdown); + + Quake_UpdateShakeInfo(req, shake, a, a); + req->countdown--; + } + return req->countdown; +} + +s16 Quake_Callback6(QuakeRequest* req, ShakeInfo* shake) { + s32 pad; + f32 a; + + req->countdown--; + a = Math_SinS(req->speed * ((req->countdown & 0xF) + 500)); + Quake_UpdateShakeInfo(req, shake, a, Rand_ZeroOne() * a); + return 1; +} + +s16 Quake_Callback3(QuakeRequest* req, ShakeInfo* shake) { + if (req->countdown > 0) { + f32 a = Math_SinS(req->speed * req->countdown) * ((f32)req->countdown / (f32)req->countdownMax); + + Quake_UpdateShakeInfo(req, shake, a, a); + req->countdown--; + } + return req->countdown; +} + +s16 Quake_Callback2(QuakeRequest* req, ShakeInfo* shake) { + if (req->countdown > 0) { + f32 a = Rand_ZeroOne(); + + Quake_UpdateShakeInfo(req, shake, a, Rand_ZeroOne() * a); + req->countdown--; + } + return req->countdown; +} + +s16 Quake_Callback4(QuakeRequest* req, ShakeInfo* shake) { + if (req->countdown > 0) { + f32 a = Rand_ZeroOne() * ((f32)req->countdown / (f32)req->countdownMax); + + Quake_UpdateShakeInfo(req, shake, a, Rand_ZeroOne() * a); + req->countdown--; + } + return req->countdown; +} + +s16 Quake_GetFreeIndex(void) { + s32 i; + s32 ret; + s32 min = 0x10000; + + for (i = 0; i < ARRAY_COUNT(sQuakeRequest); i++) { + if (sQuakeRequest[i].callbackIdx == 0) { + ret = i; + min = 0x20000; + break; + } + + if (sQuakeRequest[i].countdown < min) { + min = sQuakeRequest[i].countdown; + ret = i; + } + } + + if (min != 0x20000) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "quake: too many request %d is changed new one !!\n" VT_RST, ret); + } + + return ret; +} + +QuakeRequest* Quake_AddImpl(Camera* cam, u32 callbackIdx) { + s16 idx = Quake_GetFreeIndex(); + QuakeRequest* req = &sQuakeRequest[idx]; + + memset(req, 0, sizeof(QuakeRequest)); + req->cam = cam; + req->camPtrIdx = cam->thisIdx; + req->callbackIdx = callbackIdx; + req->unk_1C = 1; + req->randIdx = ((s16)(Rand_ZeroOne() * (f32)0x10000) & ~3) + idx; + sQuakeRequestCount++; + + return req; +} + +void Quake_Remove(QuakeRequest* req) { + req->callbackIdx = 0; + req->countdown = -1; + sQuakeRequestCount--; +} + +QuakeRequest* Quake_GetRequest(s16 idx) { + QuakeRequest* req = &sQuakeRequest[idx & 3]; + + if (req->callbackIdx == 0) { + return NULL; + } + + if (idx != req->randIdx) { + return NULL; + } + + return req; +} + +QuakeRequest* Quake_SetValue(s16 idx, s16 valueType, s16 value) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req == NULL) { + return NULL; + } else { + switch (valueType) { + case 1: + req->speed = value; + break; + case 2: + req->y = value; + break; + case 4: + req->x = value; + break; + case 8: + req->zoom = value; + break; + case 0x10: + req->rotZ = value; + break; + case 0x20: + req->unk_14.unk_00 = value; + break; + case 0x40: + req->unk_14.unk_02 = value; + break; + case 0x80: + req->unk_14.unk_04 = value; + break; + case 0x100: + req->countdown = value; + req->countdownMax = req->countdown; + break; + case 0x200: + req->unk_1C = value; + break; + } + } +} + +u32 Quake_SetSpeed(s16 idx, s16 value) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req != NULL) { + req->speed = value; + return true; + } + return false; +} + +u32 Quake_SetCountdown(s16 idx, s16 value) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req != NULL) { + req->countdown = value; + req->countdownMax = req->countdown; + return true; + } + return false; +} + +s16 Quake_GetCountdown(s16 idx) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req != NULL) { + return req->countdown; + } + return 0; +} + +u32 Quake_SetQuakeValues(s16 idx, s16 y, s16 x, s16 zoom, s16 rotZ) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req != NULL) { + req->y = y; + req->x = x; + req->zoom = zoom; + req->rotZ = rotZ; + return true; + } + return false; +} + +u32 Quake_SetUnkValues(s16 idx, s16 arg1, SubQuakeRequest14 arg2) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req != NULL) { + req->unk_1C = arg1; + + req->unk_14 = arg2; + return true; + } + return false; +} + +void Quake_Init(void) { + s16 i; + + for (i = 0; i < ARRAY_COUNT(sQuakeRequest); i++) { + sQuakeRequest[i].callbackIdx = 0; + sQuakeRequest[i].countdown = 0; + } + D_80126250 = 1; + sQuakeRequestCount = 0; +} + +s16 Quake_Add(Camera* cam, u32 callbackIdx) { + return Quake_AddImpl(cam, callbackIdx)->randIdx; +} + +u32 Quake_RemoveFromIdx(s16 idx) { + QuakeRequest* req = Quake_GetRequest(idx); + + if (req != NULL) { + Quake_Remove(req); + return true; + } + return false; +} + +s16 Quake_Calc(Camera* camera, QuakeCamCalc* camData) { + f32 max; + f32 max2; + QuakeRequest* req; + ShakeInfo shake; + f32 absSpeedDiv; + s16* temp; + u32 pad2; + s32 idx; + s32 ret; + u32 eq; + Vec3f vec; + GlobalContext* globalCtx; + + globalCtx = camera->globalCtx; + vec.x = 0.0f; + vec.y = 0.0f; + vec.z = 0.0f; + camData->rotZ = 0; + camData->unk_1A = 0; + camData->zoom = 0; + camData->atOffset.x = 0.0f; + camData->atOffset.y = 0.0f; + camData->atOffset.z = 0.0f; + camData->eyeOffset.x = 0.0f; + camData->eyeOffset.y = 0.0f; + camData->eyeOffset.z = 0.0f; + camData->unk_20 = 0.0f; + + if (sQuakeRequestCount == 0) { + return 0; + } + + ret = 0; + for (idx = 0; idx < ARRAY_COUNT(sQuakeRequest); idx++) { + req = &sQuakeRequest[idx]; + if (req->callbackIdx != 0) { + if (globalCtx->cameraPtrs[req->camPtrIdx] == NULL) { + osSyncPrintf(VT_COL(YELLOW, BLACK) "quake: stopped! 'coz camera [%d] killed!!\n" VT_RST, + req->camPtrIdx); + Quake_Remove(req); + } else { + temp = &camera->thisIdx; + eq = req->cam->thisIdx != *temp; + absSpeedDiv = ABS(req->speed) / (f32)0x8000; + if (sQuakeCallbacks[req->callbackIdx](req, &shake) == 0) { + Quake_Remove(req); + } else if (eq == 0) { + if (fabsf(camData->atOffset.x) < fabsf(shake.vec1.x)) { + camData->atOffset.x = shake.vec1.x; + } + if (fabsf(camData->atOffset.y) < fabsf(shake.vec1.y)) { + camData->atOffset.y = shake.vec1.y; + } + if (fabsf(camData->atOffset.z) < fabsf(shake.vec1.z)) { + camData->atOffset.z = shake.vec1.z; + } + if (fabsf(camData->eyeOffset.x) < fabsf(shake.vec2.x)) { + camData->eyeOffset.x = shake.vec2.x; + } + if (fabsf(camData->eyeOffset.y) < fabsf(shake.vec2.y)) { + camData->eyeOffset.y = shake.vec2.y; + } + if (fabsf(camData->eyeOffset.z) < fabsf(shake.vec2.z)) { + camData->eyeOffset.z = shake.vec2.z; + } + if (camData->rotZ < shake.rotZ) { + camData->rotZ = shake.rotZ; + camData->unk_1A = shake.unk_1A; + } + if (camData->zoom < shake.zoom) { + camData->zoom = shake.zoom; + } + + max = OLib_Vec3fDist(&shake.vec1, &vec) * absSpeedDiv; + max2 = OLib_Vec3fDist(&shake.vec2, &vec) * absSpeedDiv; + if (max < max2) { + max = max2; + } + max2 = (camData->rotZ * 0.005f) * absSpeedDiv; + if (max < max2) { + max = max2; + } + max2 = (camData->zoom * 0.005f) * absSpeedDiv; + if (max < max2) { + max = max2; + } + if (camData->unk_20 < max) { + camData->unk_20 = max; + } + + ret++; + } + } + } + } + return ret; +} diff --git a/soh/src/code/z_rcp.c b/soh/src/code/z_rcp.c new file mode 100644 index 000000000..63ce5bb8a --- /dev/null +++ b/soh/src/code/z_rcp.c @@ -0,0 +1,1560 @@ +#include "global.h" + +Gfx sSetupDL[][6] = { + { + /* 0x00 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0, 0, 0, 0, COMBINED, 0, + 0, 0, COMBINED), + gsDPSetOtherMode(G_AD_NOISE | G_CD_NOISE | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_ZB_CLD_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x01 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_PASS2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x02 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_OPA_SURF | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x03 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_PASS2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x04 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineLERP(PRIMITIVE, 0, SHADE, 0, 0, 0, 0, PRIMITIVE, PRIMITIVE, 0, SHADE, 0, 0, 0, 0, PRIMITIVE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_SURF | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x05 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEI_PRIM, G_CC_MODULATEI_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_SURF | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x06 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_PASS2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_TEX_EDGE2), + gsSPLoadGeometryMode(G_SHADE | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x07 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_XLU_SURF | G_RM_AA_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x08 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_OPA_SURF | G_RM_AA_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x09 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA, G_CC_MODULATEIA2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x0A */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_OPA_SURF | G_RM_AA_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x0B */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIA_PRIM2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x0C */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEI_PRIM, G_CC_MODULATEI_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_OPA_SURF | G_RM_AA_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x0D */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x0E */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_TEX_EDGE | G_RM_AA_ZB_TEX_EDGE2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x0F */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_PASS2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_TEX_EDGE2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x10 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x11 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_TEX_EDGE | G_RM_AA_ZB_TEX_EDGE2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x12 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_PASS2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_TEX_EDGE2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x13 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | AA_EN | Z_CMP | IM_RD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | + FORCE_BL | GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x14 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_ZB_CLD_SURF | G_RM_ZB_CLD_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x15 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA, G_CC_MODULATEIA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_SURF | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x16 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA, G_CC_MODULATEIA2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x17 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_OPA_SURF | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x18 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIA2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x19 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIA_PRIM2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x1A */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEI_PRIM, G_CC_MODULATEI_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_OPA_SURF | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x1B */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEI_PRIM, G_CC_MODULATEI_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_SURF | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x1C */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA_PRIM, G_CC_MODULATEIDECALA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_RGBA16 | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_AA_TEX_EDGE | G_RM_AA_TEX_EDGE2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x1D */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, SHADE, 0, NOISE, 0, COMBINED, 0, 0, 0, 0, COMBINED), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_PASS | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x1E */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_LINE | G_RM_AA_ZB_XLU_LINE2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x1F */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_HILITERGBA, G_CC_HILITERGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_OPA_SURF | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_TEXTURE_GEN | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x20 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_HILITERGBA, G_CC_HILITERGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_OPA_SURF | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | + G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x21 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, SHADE, 1, 0, TEXEL0, SHADE, ENVIRONMENT, COMBINED, TEXEL0, COMBINED, + ENVIRONMENT, COMBINED, TEXEL0, COMBINED), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_PASS | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | + G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x22 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGB, G_CC_DECALRGB), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_OPA_SURF | G_RM_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x23 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_DECAL | G_RM_AA_ZB_XLU_DECAL2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x24 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x25 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineLERP(PRIMITIVE, 0, SHADE, 0, 0, 0, 0, PRIMITIVE, 0, 0, 0, COMBINED, 0, 0, 0, COMBINED), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x26 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_ZB_XLU_SURF | G_RM_AA_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x27 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x28 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL1, TEXEL0, PRIMITIVE_ALPHA, TEXEL0, TEXEL1, TEXEL0, PRIMITIVE, TEXEL0, 0, 0, 0, + COMBINED, 0, 0, 0, COMBINED), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_OPA_SURF | G_RM_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_FRONT | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x29 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA, G_CC_MODULATEIA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_ZB_XLU_SURF | G_RM_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x2A */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x2B */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA_PRIM, G_CC_MODULATEIDECALA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_RGBA16 | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x2C */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_PASS2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_ZB_OVL_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x2D */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(0, 0, 0, PRIMITIVE, 0, 0, 0, TEXEL0, 0, 0, 0, PRIMITIVE, 0, 0, 0, TEXEL0), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x2E */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(0, 0, 0, PRIMITIVE, 0, 0, 0, TEXEL0, 0, 0, 0, PRIMITIVE, 0, 0, 0, TEXEL0), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x2F */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEI_PRIM, G_CC_MODULATEI_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_ZB_XLU_SURF | G_RM_ZB_XLU_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x30 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_XLU_SURF | G_RM_AA_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x31 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineLERP(PRIMITIVE, 0, SHADE, 0, 0, 0, 0, PRIMITIVE, PRIMITIVE, 0, SHADE, 0, 0, 0, 0, PRIMITIVE), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_AA_XLU_SURF | G_RM_AA_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x32 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_OPA_SURF | G_RM_OPA_SURF2), + gsSPLoadGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), + }, + { + /* 0x33 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), + }, + { + /* 0x34 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), + }, + { + /* 0x35 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL1, TEXEL0, ENV_ALPHA, TEXEL0, TEXEL1, TEXEL0, ENVIRONMENT, TEXEL0, PRIMITIVE, + ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_OPA_SURF | G_RM_OPA_SURF2), + gsSPLoadGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), + }, + { + /* 0x36 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL1, TEXEL0, ENV_ALPHA, TEXEL0, TEXEL1, TEXEL0, ENVIRONMENT, TEXEL0, PRIMITIVE, + ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_PASS | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), + }, + { + /* 0x37 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL1, TEXEL0, ENV_ALPHA, TEXEL0, TEXEL1, TEXEL0, ENVIRONMENT, TEXEL0, PRIMITIVE, + ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_PASS | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_CULL_BACK), + gsSPEndDisplayList(), + }, + { + /* 0x38 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_XLU_SURF | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x39 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsSPLoadGeometryMode(G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x3A */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineLERP(PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, SHADE, + 0), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x3B */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, SHADE, 1, 0, TEXEL0, SHADE, ENVIRONMENT, COMBINED, TEXEL0, COMBINED, + ENVIRONMENT, COMBINED, TEXEL0, COMBINED), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_PASS | G_RM_AA_OPA_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_CULL_BACK | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | + G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x3C */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, ENVIRONMENT, + TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_ZB_CLD_SURF | G_RM_ZB_CLD_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x3D */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineLERP(PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, ENVIRONMENT, + TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0), + gsDPSetOtherMode(G_AD_NOISE | G_CD_NOISE | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_ZB_CLD_SURF | G_RM_ZB_CLD_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x3E */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIDECALA, G_CC_MODULATEIA_PRIM2), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_FOG_SHADE_A | G_RM_AA_ZB_OPA_SURF2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_FOG | G_LIGHTING | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x3F */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | Z_UPD | IM_RD | CVG_DST_SAVE | ZMODE_OPA | FORCE_BL | + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA) | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x40 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_PASS2), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_PASS | G_RM_XLU_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x41 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsSPLoadGeometryMode(G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x42 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | + ALPHA_CVG_SEL | G_RM_PASS | GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x43 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | + ALPHA_CVG_SEL | G_RM_PASS | GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x44 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | + ALPHA_CVG_SEL | GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x45 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | Z_CMP | Z_UPD | IM_RD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | + ALPHA_CVG_SEL | GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM) | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_A_MEM)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, + { + /* 0x46 */ + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPSetCombineMode(G_CC_DECALRGBA, G_CC_DECALRGBA), + gsDPSetOtherMode(G_AD_NOTPATTERN | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | Z_CMP | Z_UPD | CVG_DST_FULL | ZMODE_OPA | CVG_X_ALPHA | + ALPHA_CVG_SEL | G_RM_PASS | GBL_c2(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1)), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_FOG | G_SHADING_SMOOTH), + gsSPEndDisplayList(), + }, +}; + +Gfx sFillSetupDL[] = { + gsDPPipeSync(), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsDPSetScissor(G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), + gsDPSetBlendColor(0, 0, 0, 8), + gsSPClipRatio(FRUSTRATIO_2), + gsSPEndDisplayList(), +}; + +// unused? +Gfx D_80127030[] = { + gsDPPipeSync(), + gsDPSetFillColor((GPACK_RGBA5551(255, 255, 240, 0) << 16) | GPACK_RGBA5551(255, 255, 240, 0)), + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsDPSetDepthSource(G_ZS_PIXEL), + gsDPPipeSync(), + gsSPEndDisplayList(), +}; + +// unused? +Gfx D_80127060[] = { + gsDPPipeSync(), + gsDPSetCycleType(G_CYC_FILL), + gsDPSetRenderMode(G_RM_NOOP, G_RM_NOOP2), + gsDPSetFillColor((GPACK_RGBA5551(0, 0, 0, 1) << 16) | GPACK_RGBA5551(0, 0, 0, 1)), + gsDPFillRectangle(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1), + gsDPPipeSync(), + gsSPEndDisplayList(), +}; + +Gfx gEmptyDL[] = { + gsSPEndDisplayList(), +}; + +Gfx* Gfx_SetFog(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) { + if (far == near) { + far++; + } + + ASSERT(near != far, "n != f", "../z_rcp.c", 1155); + + gDPSetFogColor(gfx++, r, g, b, a); + + if (near >= 1000) { + gSPFogFactor(gfx++, 0, 0); + } else if (near >= 997) { + gSPFogFactor(gfx++, 0x7FFF, 0x8100); + } else if (near < 0) { + gSPFogFactor(gfx++, 0, 255); + } else { + gSPFogPosition(gfx++, near, far); + } + + return gfx; +} + +Gfx* Gfx_SetFogWithSync(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) { + if (far == near) { + far++; + } + ASSERT(near != far, "n != f", "../z_rcp.c", 1187); + + gDPPipeSync(gfx++); + gDPSetFogColor(gfx++, r, g, b, a); + + if (near >= 1000) { + gSPFogFactor(gfx++, 0, 0); + } else if (near >= 997) { + gSPFogFactor(gfx++, 0x7FFF, 0x8100); + } else if (near < 0) { + gSPFogFactor(gfx++, 0, 255); + } else { + gSPFogPosition(gfx++, near, far); + } + + return gfx; +} + +Gfx* Gfx_SetFog2(Gfx* gfx, s32 r, s32 g, s32 b, s32 a, s32 near, s32 far) { + return Gfx_SetFog(gfx, r, g, b, a, near, far); +} + +Gfx* Gfx_CallSetupDLImpl(Gfx* gfx, u32 i) { + s32 dListIndex = 6 * i; + + gSPDisplayList(gfx++, &((Gfx*)sSetupDL)[dListIndex]); + // Equivalent to gSPDisplayList(gfx++, sSetupDL[i]) + return gfx; +} + +Gfx* Gfx_CallSetupDL(Gfx* gfx, u32 i) { + return Gfx_CallSetupDLImpl(gfx, i); +} + +void Gfx_CallSetupDLAtPtr(Gfx** gfxp, u32 i) { + *gfxp = Gfx_CallSetupDL(*gfxp, i); +} + +Gfx* func_800937C0(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x39]); + return gfx; +} + +Gfx* func_800937E4(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x39]); + return gfx; +} + +Gfx* func_80093808(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x34]); + gDPSetColorDither(gfx++, G_CD_DISABLE); + return gfx; +} + +void func_80093848(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1293); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x3A]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1297); +} + +void func_800938B4(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1309); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x39]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1313); +} + +void func_80093920(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1325); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x32]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1329); +} + +void func_8009398C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1341); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x33]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1345); +} + +void func_800939F8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1357); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x34]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1361); +} + +void func_80093A64(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1373); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x35]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1377); +} + +void func_80093AD0(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1389); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x36]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1393); +} + +void func_80093B3C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1405); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x37]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1409); +} + +void func_80093BA8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1421); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x1A]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1425); +} + +void func_80093C14(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1439); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x19]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1443); +} + +void func_80093C80(GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + func_80093D18(gfxCtx); + + if (globalCtx->roomCtx.curRoom.unk_03 == 3) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1460); + + gDPSetColorDither(POLY_OPA_DISP++, G_CD_DISABLE); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1462); + } +} + +void func_80093D18(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1475); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x19]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1479); +} + +void func_80093D84(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1491); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x19]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1495); +} + +void func_80093DF0(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1507); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x1F]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1511); +} + +void func_80093E5C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1523); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x20]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1527); +} + +void func_80093EC8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1539); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x21]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1543); +} + +Gfx* func_80093F34(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x40]); + return gfx; +} + +Gfx* func_80093F58(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x22]); + return gfx; +} + +void func_80093F7C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1569); + + POLY_OPA_DISP = func_80093F58(POLY_OPA_DISP); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1573); +} + +void func_80093FD8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1585); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x23]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1589); +} + +void func_80094044(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1601); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x2C]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1605); +} + +void func_800940B0(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1617); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x24]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1621); +} + +Gfx* func_8009411C(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x1C]); + return gfx; +} + +void func_80094140(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1640); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x1C]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1644); +} + +void func_800941AC(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1651); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x2B]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1655); +} + +void func_80094218(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1670); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x2D]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1674); +} + +void func_80094284(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1681); + + gSPDisplayList(OVERLAY_DISP++, sSetupDL[0x2E]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1685); +} + +void func_800942F0(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1700); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x26]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1704); +} + +void func_8009435C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1722); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x04]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1726); +} + +void func_800943C8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1758); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x25]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1762); +} + +void func_80094434(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1775); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x02]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1779); +} + +Gfx* func_800944A0(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x27]); + return gfx; +} + +void func_800944C4(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1799); + + POLY_OPA_DISP = func_800944A0(POLY_OPA_DISP); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1801); +} + +void func_800944C4_KAL(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1799); + + POLY_KAL_DISP = func_800944A0(POLY_KAL_DISP); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1801); +} + +void func_80094520(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1809); + + OVERLAY_DISP = func_800944A0(OVERLAY_DISP); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1811); +} + +void func_8009457C(Gfx** gfxp) { + Gfx* gfx = *gfxp; + gSPDisplayList(gfx++, sSetupDL[0x27]); + *gfxp = gfx; +} + +void func_800945A0(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1837); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x28]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1841); +} + +void func_8009460C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1853); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x29]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1857); +} + +void func_80094678(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1869); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x2F]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1873); +} + +Gfx* func_800946E4(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x42]); + return gfx; +} + +Gfx* func_80094708(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x43]); + return gfx; +} + +Gfx* func_8009472C(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x44]); + gDPSetColorDither(gfx++, G_CD_DISABLE); + return gfx; +} + +Gfx* func_8009476C(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x45]); + gDPSetColorDither(gfx++, G_CD_DISABLE); + return gfx; +} + +Gfx* func_800947AC(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x41]); + gDPSetColorDither(gfx++, G_CD_DISABLE); + + // clang-format off + switch (HREG(21)) { + case 1: gDPSetAlphaDither(gfx++, G_AD_DISABLE); break; + case 2: gDPSetAlphaDither(gfx++, G_AD_PATTERN); break; + case 3: gDPSetAlphaDither(gfx++, G_AD_NOTPATTERN); break; + case 4: gDPSetAlphaDither(gfx++, G_AD_NOISE); break; + } + + switch (HREG(22)) { + case 1: gDPSetColorDither(gfx++, G_CD_DISABLE); break; + case 2: gDPSetColorDither(gfx++, G_CD_MAGICSQ); break; + case 3: gDPSetColorDither(gfx++, G_CD_BAYER); break; + case 4: gDPSetColorDither(gfx++, G_CD_NOISE); break; + } + // clang-format on + + return gfx; +} + +Gfx* func_80094944(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x46]); + return gfx; +} + +Gfx* func_80094968(Gfx* gfx) { + gSPDisplayList(gfx++, sSetupDL[0x14]); + gDPSetColorDither(gfx++, G_CD_DISABLE); + return gfx; +} + +void func_800949A8(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1953); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x2A]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1957); +} + +void func_800949A8_KAL(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1953); + + gSPDisplayList(POLY_KAL_DISP++, sSetupDL[0x2A]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1957); +} + +void func_80094A14(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1964); + + gSPDisplayList(OVERLAY_DISP++, sSetupDL[0x2A]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1968); +} + +void func_80094A80(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 1992); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x30]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 1996); +} + +void func_80094AEC(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2008); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x31]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2012); +} + +void func_80094B58(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2024); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x1B]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2028); +} + +void func_80094BC4(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2040); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x3C]); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2043); +} + +void func_80094C50(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2056); + + gSPDisplayList(POLY_XLU_DISP++, sSetupDL[0x3D]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2058); +} + +void func_80094CBC(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2086); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x38]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2090); +} + +void func_80094D28(Gfx** gfxp) { + Gfx* gfx = *gfxp; + + gSPDisplayList(gfx++, sSetupDL[0x38]); + + *gfxp = gfx; +} + +void func_80094D4C(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2112); + + gSPDisplayList(POLY_OPA_DISP++, sSetupDL[0x3B]); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2116); +} + +Gfx* Gfx_BranchTexScroll(Gfx** gfxp, u32 x, u32 y, s32 width, s32 height) { + Gfx* displayList = Graph_DlistAlloc(gfxp, 3 * sizeof(Gfx)); + + gDPTileSync(displayList); + gDPSetTileSize(displayList + 1, 0, x, y, (x + ((width - 1) << 2)), (y + ((height - 1) << 2))); + gSPEndDisplayList(displayList + 2); + + return displayList; +} + +Gfx* func_80094E54(Gfx** gfxp, u32 x, u32 y) { + return Gfx_BranchTexScroll(gfxp, x, y, 0, 0); +} + +Gfx* func_80094E78(GraphicsContext* gfxCtx, u32 x, u32 y) { + return Gfx_TexScroll(gfxCtx, x, y, 0, 0); +} + +Gfx* Gfx_TexScroll(GraphicsContext* gfxCtx, u32 x, u32 y, s32 width, s32 height) { + Gfx* displayList = Graph_Alloc(gfxCtx, 3 * sizeof(Gfx)); + + x %= 2048; + y %= 2048; + + gDPTileSync(displayList); + gDPSetTileSize(displayList + 1, 0, x, y, (x + ((width - 1) << 2)), (y + ((height - 1) << 2))); + gSPEndDisplayList(displayList + 2); + + return displayList; +} + +Gfx* Gfx_TwoTexScroll(GraphicsContext* gfxCtx, s32 tile1, u32 x1, u32 y1, s32 width1, s32 height1, s32 tile2, u32 x2, + u32 y2, s32 width2, s32 height2) { + Gfx* displayList = Graph_Alloc(gfxCtx, 5 * sizeof(Gfx)); + + x1 %= 2048; + y1 %= 2048; + x2 %= 2048; + y2 %= 2048; + + gDPTileSync(displayList); + gDPSetTileSize(displayList + 1, tile1, x1, y1, (x1 + ((width1 - 1) << 2)), (y1 + ((height1 - 1) << 2))); + gDPTileSync(displayList + 2); + gDPSetTileSize(displayList + 3, tile2, x2, y2, (x2 + ((width2 - 1) << 2)), (y2 + ((height2 - 1) << 2))); + gSPEndDisplayList(displayList + 4); + + return displayList; +} + +Gfx* Gfx_TwoTexScrollEnvColor(GraphicsContext* gfxCtx, s32 tile1, u32 x1, u32 y1, s32 width1, s32 height1, s32 tile2, + u32 x2, u32 y2, s32 width2, s32 height2, s32 r, s32 g, s32 b, s32 a) { + Gfx* displayList = Graph_Alloc(gfxCtx, 6 * sizeof(Gfx)); + + x1 %= 2048; + y1 %= 2048; + x2 %= 2048; + y2 %= 2048; + + gDPTileSync(displayList); + gDPSetTileSize(displayList + 1, tile1, x1, y1, (x1 + ((width1 - 1) << 2)), (y1 + ((height1 - 1) << 2))); + gDPTileSync(displayList + 2); + gDPSetTileSize(displayList + 3, tile2, x2, y2, (x2 + ((width2 - 1) << 2)), (y2 + ((height2 - 1) << 2))); + gDPSetEnvColor(displayList + 4, r, g, b, a); + gSPEndDisplayList(displayList + 5); + + return displayList; +} + +Gfx* Gfx_EnvColor(GraphicsContext* gfxCtx, s32 r, s32 g, s32 b, s32 a) { + Gfx* displayList = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + + gDPSetEnvColor(displayList, r, g, b, a); + gSPEndDisplayList(displayList + 1); + + return displayList; +} + +void func_80095248(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2386); + + gSPDisplayList(POLY_OPA_DISP++, sFillSetupDL); + gSPDisplayList(POLY_XLU_DISP++, sFillSetupDL); + gSPDisplayList(POLY_KAL_DISP++, sFillSetupDL); + gSPDisplayList(OVERLAY_DISP++, sFillSetupDL); + + gDPSetScissor(POLY_OPA_DISP++, G_SC_NON_INTERLACE, 0, 0, gScreenWidth, gScreenHeight); + gDPSetScissor(POLY_XLU_DISP++, G_SC_NON_INTERLACE, 0, 0, gScreenWidth, gScreenHeight); + gDPSetScissor(POLY_KAL_DISP++, G_SC_NON_INTERLACE, 0, 0, gScreenWidth, gScreenHeight); + gDPSetScissor(OVERLAY_DISP++, G_SC_NON_INTERLACE, 0, 0, gScreenWidth, gScreenHeight); + + gDPSetColorImage(POLY_OPA_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + gDPSetColorImage(POLY_OPA_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + gDPSetColorImage(POLY_XLU_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + gDPSetColorImage(POLY_KAL_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + gDPSetColorImage(OVERLAY_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + + gDPSetDepthImage(POLY_OPA_DISP++, gZBuffer); + gDPSetDepthImage(POLY_XLU_DISP++, gZBuffer); + gDPSetDepthImage(POLY_KAL_DISP++, gZBuffer); + gDPSetDepthImage(OVERLAY_DISP++, gZBuffer); + + if ((R_PAUSE_MENU_MODE < 2) && (gTrnsnUnkState < 2)) { + s32 ret = ShrinkWindow_GetCurrentVal(); + + if (HREG(80) == 16) { + if (HREG(95) != 16) { + HREG(81) = 3; + HREG(82) = 3; + HREG(83) = 0; + HREG(84) = 0; + HREG(85) = 0; + HREG(86) = 0; + HREG(87) = 0; + HREG(88) = 0; + HREG(89) = 0; + HREG(90) = 0; + HREG(91) = 0; + HREG(92) = 0; + HREG(93) = 0; + HREG(94) = 0; + HREG(95) = 16; + } + + if (HREG(81) & 1) { + HREG(83) = ret; + } + + if (HREG(81) & 2) { + HREG(84) = r; + HREG(85) = g; + HREG(86) = b; + } + + if (HREG(82) & 1) { + ret = HREG(83); + } + + if (HREG(82) & 2) { + r = HREG(84); + g = HREG(85); + b = HREG(86); + } + } + + gDPSetColorImage(POLY_OPA_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gZBuffer); + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_FILL); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_NOOP, G_RM_NOOP2); + gDPSetFillColor(POLY_OPA_DISP++, (GPACK_RGBA5551(255, 255, 240, 0) << 16) | GPACK_RGBA5551(255, 255, 240, 0)); + gDPFillRectangle(POLY_OPA_DISP++, 0, ret, gScreenWidth - 1, gScreenHeight - ret - 1); + gDPPipeSync(POLY_OPA_DISP++); + + gDPSetColorImage(POLY_OPA_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_FILL); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_NOOP, G_RM_NOOP2); + gDPSetFillColor(POLY_OPA_DISP++, (GPACK_RGBA5551(r, g, b, 1) << 16) | GPACK_RGBA5551(r, g, b, 1)); + gDPFillRectangle(POLY_OPA_DISP++, 0, ret, gScreenWidth - 1, gScreenHeight - ret - 1); + gDPPipeSync(POLY_OPA_DISP++); + + if (ret > 0) { + gDPPipeSync(OVERLAY_DISP++); + gDPSetCycleType(OVERLAY_DISP++, G_CYC_FILL); + gDPSetRenderMode(OVERLAY_DISP++, G_RM_NOOP, G_RM_NOOP2); + gDPSetFillColor(OVERLAY_DISP++, (GPACK_RGBA5551(r, g, b, 1) << 16) | GPACK_RGBA5551(r, g, b, 1)); + gDPFillRectangle(OVERLAY_DISP++, 0, 0, gScreenWidth - 1, ret - 1); + gDPFillRectangle(OVERLAY_DISP++, 0, gScreenHeight - ret, gScreenWidth - 1, gScreenHeight - 1); + gDPPipeSync(OVERLAY_DISP++); + } + } + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2497); +} + +void func_80095974(GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx, "../z_rcp.c", 2503); + + gSPDisplayList(POLY_OPA_DISP++, sFillSetupDL); + gDPSetScissor(POLY_OPA_DISP++, G_SC_NON_INTERLACE, 0, 0, gScreenWidth, gScreenHeight); + gDPSetDepthImage(POLY_OPA_DISP++, gZBuffer); + gDPSetColorImage(POLY_OPA_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, gScreenWidth, gfxCtx->curFrameBuffer); + + CLOSE_DISPS(gfxCtx, "../z_rcp.c", 2513); +} diff --git a/soh/src/code/z_room.c b/soh/src/code/z_room.c new file mode 100644 index 000000000..93aab731f --- /dev/null +++ b/soh/src/code/z_room.c @@ -0,0 +1,638 @@ +#include "global.h" +#include "vt.h" + +void func_80095AB4(GlobalContext* globalCtx, Room* room, u32 flags); +void func_80095D04(GlobalContext* globalCtx, Room* room, u32 flags); +void func_80096F6C(GlobalContext* globalCtx, Room* room, u32 flags); + +Vec3f D_801270A0 = { 0.0f, 0.0f, 0.0f }; + +// unused +Gfx D_801270B0[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_ZBUFFER | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | G_LOD), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_OFF), + gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_PERSP | G_CYC_FILL | G_PM_NPRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_LIGHTING | G_SHADING_SMOOTH), + gsDPSetScissor(G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), + gsSPClipRatio(FRUSTRATIO_1), + gsSPEndDisplayList(), +}; + +void (*sRoomDrawHandlers[])(GlobalContext* globalCtx, Room* room, u32 flags) = { + func_80095AB4, + func_80096F6C, + func_80095D04, +}; + +void func_80095AA0(GlobalContext* globalCtx, Room* room, Input* arg2, UNK_TYPE arg3) { +} + +// Room Draw Polygon Type 0 +void func_80095AB4(GlobalContext* globalCtx, Room* room, u32 flags) { + s32 i; + PolygonType0* polygon0; + PolygonDlist* polygonDlist; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 193); + + if (flags & 1) { + func_800342EC(&D_801270A0, globalCtx); + gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); + func_80093C80(globalCtx); + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + } + + if (flags & 2) { + func_8003435C(&D_801270A0, globalCtx); + gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + } + + polygon0 = &room->mesh->polygon0; + polygonDlist = SEGMENTED_TO_VIRTUAL(polygon0->start); + for (i = 0; i < polygon0->num; i++) { + if ((flags & 1) && (polygonDlist->opa != NULL)) { + gSPDisplayList(POLY_OPA_DISP++, polygonDlist->opa); + } + + if ((flags & 2) && (polygonDlist->xlu != NULL)) { + gSPDisplayList(POLY_XLU_DISP++, polygonDlist->xlu); + } + + polygonDlist++; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 239); +} + +#define SHAPE_SORT_MAX 64 + +typedef struct struct_80095D04 { + /* 0x00 */ PolygonDlist2* unk_00; + /* 0x04 */ f32 unk_04; + /* 0x08 */ struct struct_80095D04* unk_08; + /* 0x0C */ struct struct_80095D04* unk_0C; +} struct_80095D04; // size = 0x10 + +// Room Draw Polygon Type 2 +void func_80095D04(GlobalContext* globalCtx, Room* room, u32 flags) { + PolygonType2* polygon2; + PolygonDlist2* polygonDlist; + struct_80095D04 spB8[SHAPE_SORT_MAX]; + struct_80095D04* spB4 = NULL; + struct_80095D04* spB0 = NULL; + struct_80095D04* phi_v0; + s32 pad; + struct_80095D04* spA4; + s32 phi_v1; + s32 sp9C; + Vec3f sp90; + Vec3f sp84; + f32 sp80; + s32 pad2; + PolygonDlist2* sp78; + PolygonDlist2* temp; + f32 temp_f2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 287); + if (flags & 1) { + func_800342EC(&D_801270A0, globalCtx); + //gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); + func_80093C80(globalCtx); + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + } + if (1) {} + if (flags & 2) { + func_8003435C(&D_801270A0, globalCtx); + //gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + } + + polygon2 = &room->mesh->polygon2; + polygonDlist = SEGMENTED_TO_VIRTUAL(polygon2->start); + spA4 = spB8; + + ASSERT(polygon2->num <= SHAPE_SORT_MAX, "polygon2->num <= SHAPE_SORT_MAX", "../z_room.c", 317); + sp78 = polygonDlist; + + for (sp9C = 0; sp9C < polygon2->num; sp9C++, polygonDlist++) { + sp90.x = polygonDlist->pos.x; + sp90.y = polygonDlist->pos.y; + sp90.z = polygonDlist->pos.z; + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &sp90, &sp84, &sp80); + if (-(f32)polygonDlist->unk_06 < sp84.z) + { + temp_f2 = sp84.z - polygonDlist->unk_06; + if (temp_f2 < globalCtx->lightCtx.fogFar) { + phi_v0 = spB4; + spA4->unk_00 = polygonDlist; + spA4->unk_04 = temp_f2; + if (phi_v0 == NULL) { + spB4 = spB0 = spA4; + spA4->unk_08 = spA4->unk_0C = NULL; + } else { + do { + if (spA4->unk_04 < phi_v0->unk_04) { + break; + } + phi_v0 = phi_v0->unk_0C; + } while (phi_v0 != NULL); + + if (phi_v0 == NULL) { + spA4->unk_08 = spB0; + spA4->unk_0C = NULL; + spB0->unk_0C = spA4; + spB0 = spA4; + } else { + spA4->unk_08 = phi_v0->unk_08; + if (spA4->unk_08 == NULL) { + spB4 = spA4; + } else { + spA4->unk_08->unk_0C = spA4; + } + phi_v0->unk_08 = spA4; + spA4->unk_0C = phi_v0; + } + } + spA4++; + } + } + } + + iREG(87) = polygon2->num & 0xFFFF & 0xFFFF & 0xFFFF; // if this is real then I might not be + + for (sp9C = 1; spB4 != NULL; spB4 = spB4->unk_0C, sp9C++) { + Gfx* temp2; + + polygonDlist = spB4->unk_00; + if (iREG(86) != 0) + { + temp = sp78; + for (phi_v1 = 0; phi_v1 < polygon2->num; phi_v1++, temp++) { + if (polygonDlist == temp) { + break; // This loop does nothing? + } + } + + if (((iREG(86) == 1) && (iREG(89) >= sp9C)) || ((iREG(86) == 2) && (iREG(89) == sp9C))) + { + if (flags & 1) { + temp2 = polygonDlist->opa; + if (temp2 != NULL) { + gSPDisplayList(POLY_OPA_DISP++, temp2); + } + } + + if (flags & 2) { + temp2 = polygonDlist->xlu; + if (temp2 != NULL) { + gSPDisplayList(POLY_XLU_DISP++, temp2); + } + } + } + } else { + if (flags & 1) { + temp2 = polygonDlist->opa; + if (temp2 != NULL) { + gSPDisplayList(POLY_OPA_DISP++, temp2); + } + } + + if (flags & 2) { + temp2 = polygonDlist->xlu; + if (temp2 != NULL) { + gSPDisplayList(POLY_XLU_DISP++, temp2); + } + } + } + } + + iREG(88) = sp9C - 1; + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 430); +} + +//#define JPEG_MARKER 0xFFD8FFE0 +#define JPEG_MARKER 0xE0FFD8FF + +s32 func_80096238(void* data) { + OSTime time; + + if (*(u32*)data == JPEG_MARKER) + { + char* decodedJpeg = ResourceMgr_LoadJPEG(data, 320 * 240 * 2); + //char* decodedJpeg = ResourceMgr_LoadJPEG(data, 480 * 240 * 2); + + osSyncPrintf("JPEGデータを展開します\n"); // "Expanding jpeg data" + osSyncPrintf("JPEGデータアドレス %08x\n", data); // "Jpeg data address %08x" + // "Work buffer address (Z buffer) %08x" + osSyncPrintf("ワークバッファアドレス(Zバッファ)%08x\n", gZBuffer); + + time = osGetTime(); + + //if (!Jpeg_Decode(data, gZBuffer, gGfxSPTaskOutputBuffer, sizeof(gGfxSPTaskOutputBuffer))) + if (1) + { + memcpy(data, decodedJpeg, 320 * 240 * 2); + //memcpy(data, decodedJpeg, 480 * 240 * 2); + time = osGetTime() - time; + + // "Success... I think. time = %6.3f ms" + osSyncPrintf("成功…だと思う。 time = %6.3f ms \n", OS_CYCLES_TO_USEC(time) / 1000.0f); + // "Writing back to original address from work buffer." + osSyncPrintf("ワークバッファから元のアドレスに書き戻します。\n"); + // "If the original buffer size isn't at least 150kb, it will be out of control." + osSyncPrintf("元のバッファのサイズが150キロバイト無いと暴走するでしょう。\n"); + + //bcopy(gZBuffer, data, sizeof(gZBuffer)); + } else { + osSyncPrintf("失敗!なんで〜\n"); // "Failure! Why is it 〜" + } + } + + return 0; +} + +void func_8009638C(Gfx** displayList, void* source, void* tlut, u16 width, u16 height, u8 fmt, u8 siz, u16 mode0, + u16 tlutCount, f32 frameX, f32 frameY) { + Gfx* displayListHead; + uObjBg* bg; + s32 temp; + + displayListHead = *displayList; + func_80096238(SEGMENTED_TO_VIRTUAL(source)); + + bg = (uObjBg*)(displayListHead + 1); + gSPBranchList(displayListHead, (u8*)bg + sizeof(uObjBg)); + bg->b.imageX = 0; + bg->b.imageW = width * 4; + bg->b.frameX = frameX * 4; + bg->b.imageY = 0; + bg->b.imageH = height * 4; + bg->b.frameY = frameY * 4; + bg->b.imagePtr = source; + bg->b.imageLoad = G_BGLT_LOADTILE; + bg->b.imageFmt = fmt; + bg->b.imageSiz = siz; + bg->b.imagePal = 0; + bg->b.imageFlip = 0; + + displayListHead = (void*)(bg + 1); + if (fmt == G_IM_FMT_CI) { + gDPLoadTLUT(displayListHead++, tlutCount, 256, tlut); + } else { + gDPPipeSync(displayListHead++); + } + + if ((fmt == G_IM_FMT_RGBA) && (SREG(26) == 0)) { + bg->b.frameW = width * 4; + bg->b.frameH = height * 4; + guS2DInitBg(bg); + + gDPSetOtherMode(displayListHead++, mode0 | G_TL_TILE | G_TD_CLAMP | G_TP_NONE | G_CYC_COPY | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | G_RM_NOOP | G_RM_NOOP2); + + gDPSetFillColor(displayListHead++, GPACK_RGBA5551(0, 0, 0, 1) << 16 | GPACK_RGBA5551(0, 0, 0, 1)); + gDPFillWideRectangle(displayListHead++, OTRGetRectDimensionFromLeftEdge(0), 0, + OTRGetRectDimensionFromRightEdge(SCREEN_WIDTH), SCREEN_HEIGHT); + + gDPLoadMultiTile(displayListHead++, bg->b.imagePtr, 0, + G_TX_RENDERTILE, G_IM_FMT_RGBA, G_IM_SIZ_16b, 320, 0, 0, 0, 0 + 31, + 0 + 31, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); + + gSPBgRectCopy(displayListHead++, bg); + + } else { + bg->s.frameW = width * 4; + bg->s.frameH = height * 4; + bg->s.scaleW = 1024; + bg->s.scaleH = 1024; + bg->s.imageYorig = bg->b.imageY; + gDPSetOtherMode(displayListHead++, + mode0 | G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_NONE | + G_TL_TILE | G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_NPRIMITIVE, + G_AC_THRESHOLD | G_ZS_PIXEL | AA_EN | CVG_DST_CLAMP | ZMODE_OPA | CVG_X_ALPHA | ALPHA_CVG_SEL | + GBL_c1(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_BL, G_BL_1MA) | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_BL, G_BL_1MA)); + gDPSetCombineLERP(displayListHead++, 0, 0, 0, TEXEL0, 0, 0, 0, 1, 0, 0, 0, TEXEL0, 0, 0, 0, 1); + gSPObjRenderMode(displayListHead++, G_OBJRM_ANTIALIAS | G_OBJRM_BILERP); + gSPBgRect1Cyc(displayListHead++, bg); + } + + gDPPipeSync(displayListHead++); + *displayList = displayListHead; +} + +// Room Draw Polygon Type 1 - Single Format +void func_80096680(GlobalContext* globalCtx, Room* room, u32 flags) { + Camera* camera; + Gfx* spA8; + PolygonType1* polygon1; + PolygonDlist* polygonDlist; + u32 sp9C; + u32 sp98; + u32 sp94; + u32 sp90; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 628); + + camera = GET_ACTIVE_CAM(globalCtx); + sp9C = (camera->setting == CAM_SET_PREREND_FIXED); + polygon1 = &room->mesh->polygon1; + polygonDlist = SEGMENTED_TO_VIRTUAL(polygon1->dlist); + sp98 = (flags & 1) && sp9C && polygon1->single.source && !(SREG(25) & 1); + sp94 = (flags & 1) && polygonDlist->opa && !(SREG(25) & 2); + sp90 = (flags & 2) && polygonDlist->xlu && !(SREG(25) & 4); + + if (sp94 || sp98) { + gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); + + if (sp94) { + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, polygonDlist->opa); + } + + if (sp98) { + // gSPLoadUcodeL(POLY_OPA_DISP++, rspS2DEX)? + //gSPLoadUcodeEx(POLY_OPA_DISP++, OS_K0_TO_PHYSICAL(D_80113070), OS_K0_TO_PHYSICAL(D_801579A0), 0x800); + + { + Vec3f sp60; + spA8 = POLY_OPA_DISP; + Camera_GetSkyboxOffset(&sp60, camera); + func_8009638C(&spA8, polygon1->single.source, polygon1->single.tlut, polygon1->single.width, + polygon1->single.height, polygon1->single.fmt, polygon1->single.siz, + polygon1->single.mode0, polygon1->single.tlutCount, + (sp60.x + sp60.z) * 1.2f + sp60.y * 0.6f, sp60.y * 2.4f + (sp60.x + sp60.z) * 0.3f); + POLY_OPA_DISP = spA8; + } + + // gSPLoadUcode(POLY_OPA_DISP++, SysUcode_GetUCode(), SysUcode_GetUCodeData())? + gSPLoadUcodeEx(POLY_OPA_DISP++, SysUcode_GetUCode(), SysUcode_GetUCodeData(), 0x800); + } + } + + if (sp90) { + gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, polygonDlist->xlu); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 691); +} + +BgImage* func_80096A74(PolygonType1* polygon1, GlobalContext* globalCtx) { + Camera* camera; + s32 camId; + s16 camId2; + Player* player; + BgImage* bgImage; + s32 i; + + camera = GET_ACTIVE_CAM(globalCtx); + camId = camera->camDataIdx; + // jfifid + camId2 = func_80041C10(&globalCtx->colCtx, camId, BGCHECK_SCENE)[2].y; + if (camId2 >= 0) { + camId = camId2; + } + + player = GET_PLAYER(globalCtx); + player->actor.params = (player->actor.params & 0xFF00) | camId; + + bgImage = SEGMENTED_TO_VIRTUAL(polygon1->multi.list); + for (i = 0; i < polygon1->multi.count; i++) { + if (bgImage->id == camId) { + return bgImage; + } + bgImage++; + } + + // "z_room.c: Data consistent with camera id does not exist camid=%d" + osSyncPrintf(VT_COL(RED, WHITE) "z_room.c:カメラIDに一致するデータが存在しません camid=%d\n" VT_RST, camId); + LogUtils_HungupThread("../z_room.c", 726); + + return NULL; +} + +// Room Draw Polygon Type 1 - Multi Format +void func_80096B6C(GlobalContext* globalCtx, Room* room, u32 flags) { + Camera* camera; + Gfx* spA8; + PolygonType1* polygon1; + BgImage* bgImage; + PolygonDlist* polygonDlist; + u32 sp98; + u32 sp94; + u32 sp90; + u32 sp8C; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 752); + + camera = GET_ACTIVE_CAM(globalCtx); + sp98 = (camera->setting == CAM_SET_PREREND_FIXED); + polygon1 = &room->mesh->polygon1; + polygonDlist = SEGMENTED_TO_VIRTUAL(polygon1->dlist); + bgImage = func_80096A74(polygon1, globalCtx); + sp94 = (flags & 1) && sp98 && bgImage->source && !(SREG(25) & 1); + sp90 = (flags & 1) && polygonDlist->opa && !(SREG(25) & 2); + sp8C = (flags & 2) && polygonDlist->xlu && !(SREG(25) & 4); + + if (sp90 || sp94) { + gSPSegment(POLY_OPA_DISP++, 0x03, room->segment); + + if (sp90) { + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, polygonDlist->opa); + } + + if (sp94) { + // gSPLoadUcodeL(POLY_OPA_DISP++, rspS2DEX)? + //gSPLoadUcodeEx(POLY_OPA_DISP++, OS_K0_TO_PHYSICAL(D_80113070), OS_K0_TO_PHYSICAL(D_801579A0), 0x800); + + { + Vec3f sp5C; + spA8 = POLY_OPA_DISP; + Camera_GetSkyboxOffset(&sp5C, camera); + func_8009638C(&spA8, bgImage->source, bgImage->tlut, bgImage->width, bgImage->height, bgImage->fmt, + bgImage->siz, bgImage->mode0, bgImage->tlutCount, + (sp5C.x + sp5C.z) * 1.2f + sp5C.y * 0.6f, sp5C.y * 2.4f + (sp5C.x + sp5C.z) * 0.3f); + POLY_OPA_DISP = spA8; + } + + // gSPLoadUcode(POLY_OPA_DISP++, SysUcode_GetUCode(), SysUcode_GetUCodeData())? + gSPLoadUcodeEx(POLY_OPA_DISP++, SysUcode_GetUCode(), SysUcode_GetUCodeData(), 0x800); + } + } + + if (sp8C) { + gSPSegment(POLY_XLU_DISP++, 0x03, room->segment); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, polygonDlist->xlu); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_room.c", 819); +} + +// Room Draw Polygon Type 1 +void func_80096F6C(GlobalContext* globalCtx, Room* room, u32 flags) { + PolygonType1* polygon1 = &room->mesh->polygon1; + + if (polygon1->format == 1) { + func_80096680(globalCtx, room, flags); + } else if (polygon1->format == 2) { + func_80096B6C(globalCtx, room, flags); + } else { + LogUtils_HungupThread("../z_room.c", 841); + } +} + +void func_80096FD4(GlobalContext* globalCtx, Room* room) { + room->num = -1; + room->segment = NULL; +} + +u32 func_80096FE8(GlobalContext* globalCtx, RoomContext* roomCtx) { + u32 maxRoomSize = 0; + RomFile* roomList = globalCtx->roomList; + u32 roomSize; + s32 i; + s32 j; + s32 frontRoom; + s32 backRoom; + u32 frontRoomSize; + u32 backRoomSize; + u32 cumulRoomSize; + + for (i = 0; i < globalCtx->numRooms; i++) { + roomSize = roomList[i].vromEnd - roomList[i].vromStart; + osSyncPrintf("ROOM%d size=%d\n", i, roomSize); + if (maxRoomSize < roomSize) { + maxRoomSize = roomSize; + } + } + + if (globalCtx->transiActorCtx.numActors != 0) { + RomFile* roomList = globalCtx->roomList; + TransitionActorEntry* transitionActor = &globalCtx->transiActorCtx.list[0]; + + LOG_NUM("game_play->room_rom_address.num", globalCtx->numRooms, "../z_room.c", 912); + + for (j = 0; j < globalCtx->transiActorCtx.numActors; j++) { + frontRoom = transitionActor->sides[0].room; + backRoom = transitionActor->sides[1].room; + frontRoomSize = (frontRoom < 0) ? 0 : roomList[frontRoom].vromEnd - roomList[frontRoom].vromStart; + backRoomSize = (backRoom < 0) ? 0 : roomList[backRoom].vromEnd - roomList[backRoom].vromStart; + cumulRoomSize = (frontRoom != backRoom) ? frontRoomSize + backRoomSize : frontRoomSize; + + osSyncPrintf("DOOR%d=<%d> ROOM1=<%d, %d> ROOM2=<%d, %d>\n", j, cumulRoomSize, frontRoom, frontRoomSize, + backRoom, backRoomSize); + if (maxRoomSize < cumulRoomSize) { + maxRoomSize = cumulRoomSize; + } + transitionActor++; + } + } + + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Room buffer size=%08x(%5.1fK)" + osSyncPrintf("部屋バッファサイズ=%08x(%5.1fK)\n", maxRoomSize, maxRoomSize / 1024.0f); + roomCtx->bufPtrs[0] = GameState_Alloc(&globalCtx->state, maxRoomSize, "../z_room.c", 946); + // "Room buffer initial pointer=%08x" + osSyncPrintf("部屋バッファ開始ポインタ=%08x\n", roomCtx->bufPtrs[0]); + roomCtx->bufPtrs[1] = (void*)((s32)roomCtx->bufPtrs[0] + maxRoomSize); + // "Room buffer end pointer=%08x" + osSyncPrintf("部屋バッファ終了ポインタ=%08x\n", roomCtx->bufPtrs[1]); + osSyncPrintf(VT_RST); + roomCtx->unk_30 = 0; + roomCtx->status = 0; + + frontRoom = gSaveContext.respawnFlag > 0 ? ((void)0, gSaveContext.respawn[gSaveContext.respawnFlag - 1].roomIndex) + : globalCtx->setupEntranceList[globalCtx->curSpawn].room; + func_8009728C(globalCtx, roomCtx, frontRoom); + + return maxRoomSize; +} + +s32 func_8009728C(GlobalContext* globalCtx, RoomContext* roomCtx, s32 roomNum) { + size_t size; + + return OTRfunc_8009728C(globalCtx, roomCtx, roomNum); + + if (roomCtx->status == 0) { + roomCtx->prevRoom = roomCtx->curRoom; + roomCtx->curRoom.num = roomNum; + roomCtx->curRoom.segment = NULL; + roomCtx->status = 1; + + ASSERT(roomNum < globalCtx->numRooms, "read_room_ID < game_play->room_rom_address.num", "../z_room.c", 1009); + + size = globalCtx->roomList[roomNum].vromEnd - globalCtx->roomList[roomNum].vromStart; + roomCtx->unk_34 = (void*)ALIGN16((intptr_t)roomCtx->bufPtrs[roomCtx->unk_30] - ((size + 8) * roomCtx->unk_30 + 7)); + + osCreateMesgQueue(&roomCtx->loadQueue, &roomCtx->loadMsg, 1); + DmaMgr_SendRequest2(&roomCtx->dmaRequest, roomCtx->unk_34, globalCtx->roomList[roomNum].vromStart, size, 0, + &roomCtx->loadQueue, NULL, "../z_room.c", 1036); + roomCtx->unk_30 ^= 1; + + return 1; + } + + return 0; +} + +s32 func_800973FC(GlobalContext* globalCtx, RoomContext* roomCtx) { + return OTRfunc_800973FC(globalCtx, roomCtx); + + + if (roomCtx->status == 1) { + if (!osRecvMesg(&roomCtx->loadQueue, NULL, OS_MESG_NOBLOCK)) { + roomCtx->status = 0; + roomCtx->curRoom.segment = roomCtx->unk_34; + gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->unk_34); + + Scene_ExecuteCommands(globalCtx, roomCtx->curRoom.segment); + Player_SetBootData(globalCtx, GET_PLAYER(globalCtx)); + Actor_SpawnTransitionActors(globalCtx, &globalCtx->actorCtx); + + return 1; + } + + return 0; + } + + return 1; +} + +void Room_Draw(GlobalContext* globalCtx, Room* room, u32 flags) { + if (room->segment != NULL) + { + gSegments[3] = VIRTUAL_TO_PHYSICAL(room->segment); + ASSERT(room->mesh->polygon.type < ARRAY_COUNTU(sRoomDrawHandlers), + "this->ground_shape->polygon.type < number(Room_Draw_Proc)", "../z_room.c", 1125); + sRoomDrawHandlers[room->mesh->polygon.type](globalCtx, room, flags); + } +} + +void func_80097534(GlobalContext* globalCtx, RoomContext* roomCtx) { + roomCtx->prevRoom.num = -1; + roomCtx->prevRoom.segment = NULL; + func_80031B14(globalCtx, &globalCtx->actorCtx); + Actor_SpawnTransitionActors(globalCtx, &globalCtx->actorCtx); + Map_InitRoomData(globalCtx, roomCtx->curRoom.num); + if (!((globalCtx->sceneNum >= SCENE_SPOT00) && (globalCtx->sceneNum <= SCENE_SPOT20))) { + Map_SavePlayerInitialInfo(globalCtx); + } + Audio_SetEnvReverb(globalCtx->roomCtx.curRoom.echo); +} diff --git a/soh/src/code/z_sample.c b/soh/src/code/z_sample.c new file mode 100644 index 000000000..21e959678 --- /dev/null +++ b/soh/src/code/z_sample.c @@ -0,0 +1,100 @@ +#include "global.h" + +void Sample_HandleStateChange(SampleContext* this) { + if (CHECK_BTN_ALL(this->state.input[0].press.button, BTN_START)) { + SET_NEXT_GAMESTATE(&this->state, Gameplay_Init, GlobalContext); + this->state.running = false; + } +} + +void Sample_Draw(SampleContext* this) { + GraphicsContext* gfxCtx = this->state.gfxCtx; + View* view = &this->view; + + OPEN_DISPS(gfxCtx, "../z_sample.c", 62); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + gSPSegment(POLY_OPA_DISP++, 0x01, this->staticSegment); + + func_80095248(gfxCtx, 0, 0, 0); + + view->flags = 1 | 2 | 4; + func_800AAA50(view, 15); + + { + Mtx* mtx = Graph_Alloc(gfxCtx, sizeof(Mtx)); + + guPosition(mtx, SREG(37), SREG(38), SREG(39), 1.0f, SREG(40), SREG(41), SREG(42)); + gSPMatrix(POLY_OPA_DISP++, mtx, G_MTX_LOAD); + } + + POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP, 255, 255, 255, 0, 0, 0); + func_80093D18(gfxCtx); + + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_1CYCLE); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 0, 0); + + CLOSE_DISPS(gfxCtx, "../z_sample.c", 111); +} + +void Sample_Main(GameState* thisx) { + SampleContext* this = (SampleContext*)thisx; + + Sample_Draw(this); + Sample_HandleStateChange(this); +} + +void Sample_Destroy(GameState* thisx) { +} + +void Sample_SetupView(SampleContext* this) { + View* view = &this->view; + GraphicsContext* gfxCtx = this->state.gfxCtx; + + View_Init(view, gfxCtx); + SET_FULLSCREEN_VIEWPORT(view); + func_800AA460(view, 60.0f, 10.0f, 12800.0f); + + { + Vec3f eye; + Vec3f lookAt; + Vec3f up; + + eye.x = 0.0f; + eye.y = 0.0f; + eye.z = 3000.0f; + lookAt.x = 0.0f; + lookAt.y = 0.0f; + lookAt.z = 0.0f; + up.x = 0.0f; + up.z = 0.0f; + up.y = 1.0f; + + func_800AA358(view, &eye, &lookAt, &up); + } +} + +void Sample_LoadTitleStatic(SampleContext* this) { + size_t size = _title_staticSegmentRomEnd - _title_staticSegmentRomStart; + + this->staticSegment = GameState_Alloc(&this->state, size, "../z_sample.c", 163); + DmaMgr_SendRequest1(this->staticSegment, _title_staticSegmentRomStart, size, "../z_sample.c", 164); +} + +void Sample_Init(GameState* thisx) { + SampleContext* this = (SampleContext*)thisx; + + this->state.main = Sample_Main; + this->state.destroy = Sample_Destroy; + R_UPDATE_RATE = 1; + Sample_SetupView(this); + Sample_LoadTitleStatic(this); + SREG(37) = 0; + SREG(38) = 0; + SREG(39) = 0; + SREG(40) = 0; + SREG(41) = 0; + SREG(42) = 0; +} diff --git a/soh/src/code/z_scene.c b/soh/src/code/z_scene.c new file mode 100644 index 000000000..fe311aa63 --- /dev/null +++ b/soh/src/code/z_scene.c @@ -0,0 +1,536 @@ +#include "global.h" +#include "vt.h" + +RomFile sNaviMsgFiles[]; + +s32 Object_Spawn(ObjectContext* objectCtx, s16 objectId) { + size_t size; + + objectCtx->status[objectCtx->num].id = objectId; + size = gObjectTable[objectId].vromEnd - gObjectTable[objectId].vromStart; + + osSyncPrintf("OBJECT[%d] SIZE %fK SEG=%x\n", objectId, size / 1024.0f, objectCtx->status[objectCtx->num].segment); + + osSyncPrintf("num=%d adrs=%x end=%x\n", objectCtx->num, (uintptr_t)objectCtx->status[objectCtx->num].segment + size, + objectCtx->spaceEnd); + + ASSERT(((objectCtx->num < OBJECT_EXCHANGE_BANK_MAX) && + (((uintptr_t)objectCtx->status[objectCtx->num].segment + size) < (uintptr_t)objectCtx->spaceEnd)), + "this->num < OBJECT_EXCHANGE_BANK_MAX && (this->status[this->num].Segment + size) < this->endSegment", + "../z_scene.c", 142); + + DmaMgr_SendRequest1(objectCtx->status[objectCtx->num].segment, gObjectTable[objectId].vromStart, size, + "../z_scene.c", 145); + + if (objectCtx->num < OBJECT_EXCHANGE_BANK_MAX - 1) { + objectCtx->status[objectCtx->num + 1].segment = + (void*)ALIGN16((uintptr_t)objectCtx->status[objectCtx->num].segment + size); + } + + objectCtx->num++; + objectCtx->unk_09 = objectCtx->num; + + return objectCtx->num - 1; +} + +void Object_InitBank(GlobalContext* globalCtx, ObjectContext* objectCtx) { + GlobalContext* globalCtx2 = globalCtx; // Needs to be a new variable to match (possibly a sub struct?) + size_t spaceSize; + s32 i; + + if (globalCtx2->sceneNum == SCENE_SPOT00) { + spaceSize = 1024000; + } else if (globalCtx2->sceneNum == SCENE_GANON_DEMO) { + if (gSaveContext.sceneSetupIndex != 4) { + spaceSize = 1177600; + } else { + spaceSize = 1024000; + } + } else if (globalCtx2->sceneNum == SCENE_JYASINBOSS) { + spaceSize = 1075200; + } else if (globalCtx2->sceneNum == SCENE_KENJYANOMA) { + spaceSize = 1075200; + } else if (globalCtx2->sceneNum == SCENE_GANON_BOSS) { + spaceSize = 1075200; + } else { + spaceSize = 1024000; + } + + objectCtx->num = objectCtx->unk_09 = 0; + objectCtx->mainKeepIndex = objectCtx->subKeepIndex = 0; + + for (i = 0; i < OBJECT_EXCHANGE_BANK_MAX; i++) { + objectCtx->status[i].id = OBJECT_INVALID; + } + + osSyncPrintf(VT_FGCOL(GREEN)); + // "Object exchange bank data %8.3fKB" + osSyncPrintf("オブジェクト入れ替えバンク情報 %8.3fKB\n", spaceSize / 1024.0f); + osSyncPrintf(VT_RST); + + objectCtx->spaceStart = objectCtx->status[0].segment = + GameState_Alloc(&globalCtx->state, spaceSize, "../z_scene.c", 219); + objectCtx->spaceEnd = (void*)((uintptr_t)objectCtx->spaceStart + spaceSize); + + objectCtx->mainKeepIndex = Object_Spawn(objectCtx, OBJECT_GAMEPLAY_KEEP); + gSegments[4] = VIRTUAL_TO_PHYSICAL(objectCtx->status[objectCtx->mainKeepIndex].segment); +} + +void Object_UpdateBank(ObjectContext* objectCtx) { + s32 i; + ObjectStatus* status = &objectCtx->status[0]; + RomFile* objectFile; + size_t size; + + /* + for (i = 0; i < objectCtx->num; i++) { + if (status->id < 0) { + if (status->dmaRequest.vromAddr == 0) { + osCreateMesgQueue(&status->loadQueue, &status->loadMsg, 1); + objectFile = &gObjectTable[-status->id]; + size = objectFile->vromEnd - objectFile->vromStart; + osSyncPrintf("OBJECT EXCHANGE BANK-%2d SIZE %8.3fK SEG=%08x\n", i, size / 1024.0f, status->segment); + DmaMgr_SendRequest2(&status->dmaRequest, status->segment, objectFile->vromStart, size, 0, + &status->loadQueue, NULL, "../z_scene.c", 266); + } else if (!osRecvMesg(&status->loadQueue, NULL, OS_MESG_NOBLOCK)) { + status->id = -status->id; + } + } + status++; + } + */ +} + +s32 Object_GetIndex(ObjectContext* objectCtx, s16 objectId) { + s32 i; + + //return 0; + + for (i = 0; i < objectCtx->num; i++) { + if (ABS(objectCtx->status[i].id) == objectId) { + return i; + } + } + + return -1; +} + +s32 Object_IsLoaded(ObjectContext* objectCtx, s32 bankIndex) { + if (objectCtx->status[bankIndex].id > 0) { + return true; + } else { + return false; + } +} + +void func_800981B8(ObjectContext* objectCtx) { + s32 i; + s32 id; + size_t size; + + for (i = 0; i < objectCtx->num; i++) { + id = objectCtx->status[i].id; + size = gObjectTable[id].vromEnd - gObjectTable[id].vromStart; + osSyncPrintf("OBJECT[%d] SIZE %fK SEG=%x\n", objectCtx->status[i].id, size / 1024.0f, + objectCtx->status[i].segment); + osSyncPrintf("num=%d adrs=%x end=%x\n", objectCtx->num, (uintptr_t)objectCtx->status[i].segment + size, + objectCtx->spaceEnd); + DmaMgr_SendRequest1(objectCtx->status[i].segment, gObjectTable[id].vromStart, size, "../z_scene.c", 342); + } +} + +void* func_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId) { + ObjectStatus* status = &objectCtx->status[bankIndex]; + RomFile* objectFile = &gObjectTable[objectId]; + size_t size; + void* nextPtr; + + status->id = -objectId; + status->dmaRequest.vromAddr = 0; + + size = objectFile->vromEnd - objectFile->vromStart; + osSyncPrintf("OBJECT EXCHANGE NO=%2d BANK=%3d SIZE=%8.3fK\n", bankIndex, objectId, size / 1024.0f); + + nextPtr = (void*)ALIGN16((uintptr_t)status->segment + size); + if (1) {} // Necessary to match + + ASSERT(nextPtr < objectCtx->spaceEnd, "nextptr < this->endSegment", "../z_scene.c", 381); + + // "Object exchange free size=%08x" + osSyncPrintf("オブジェクト入れ替え空きサイズ=%08x\n", (uintptr_t)objectCtx->spaceEnd - (uintptr_t)nextPtr); + + return nextPtr; +} + +s32 Scene_ExecuteCommands(GlobalContext* globalCtx, SceneCmd* sceneCmd) { + u32 cmdCode; + + while (true) { + cmdCode = sceneCmd->base.code; + osSyncPrintf("*** Scene_Word = { code=%d, data1=%02x, data2=%04x } ***\n", cmdCode, sceneCmd->base.data1, + sceneCmd->base.data2); + + if (cmdCode == 0x14) { + break; + } + + if (cmdCode <= 0x19) { + gSceneCmdHandlers[cmdCode](globalCtx, sceneCmd); + } else { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("code の値が異常です\n"); // "code variable is abnormal" + osSyncPrintf(VT_RST); + } + sceneCmd++; + } + return 0; +} + +void func_80098508(GlobalContext* globalCtx, SceneCmd* cmd) { + ActorEntry* linkEntry = globalCtx->linkActorEntry = (ActorEntry*)SEGMENTED_TO_VIRTUAL(cmd->spawnList.segment) + + globalCtx->setupEntranceList[globalCtx->curSpawn].spawn; + s16 linkObjectId; + + globalCtx->linkAgeOnLoad = ((void)0, gSaveContext.linkAge); + + linkObjectId = gLinkObjectIds[((void)0, gSaveContext.linkAge)]; + + gActorOverlayTable[linkEntry->id].initInfo->objectId = linkObjectId; + Object_Spawn(&globalCtx->objectCtx, linkObjectId); +} + +// Scene Command 0x01: Actor List +void func_800985DC(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->numSetupActors = cmd->actorList.num; + globalCtx->setupActorList = SEGMENTED_TO_VIRTUAL(cmd->actorList.segment); +} + +// Scene Command 0x02: Unused 02 +void func_80098630(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->unk_11DFC = SEGMENTED_TO_VIRTUAL(cmd->unused02.segment); +} + +// Scene Command 0x03: Collision Header +void func_80098674(GlobalContext* globalCtx, SceneCmd* cmd) { + CollisionHeader* colHeader = SEGMENTED_TO_VIRTUAL(cmd->colHeader.segment); + + colHeader->vtxList = SEGMENTED_TO_VIRTUAL(colHeader->vtxList); + colHeader->polyList = SEGMENTED_TO_VIRTUAL(colHeader->polyList); + colHeader->surfaceTypeList = SEGMENTED_TO_VIRTUAL(colHeader->surfaceTypeList); + colHeader->cameraDataList = SEGMENTED_TO_VIRTUAL(colHeader->cameraDataList); + colHeader->waterBoxes = SEGMENTED_TO_VIRTUAL(colHeader->waterBoxes); + + BgCheck_Allocate(&globalCtx->colCtx, globalCtx, colHeader); +} + +// Scene Command 0x04: Room List +void func_800987A4(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->numRooms = cmd->roomList.num; + globalCtx->roomList = SEGMENTED_TO_VIRTUAL(cmd->roomList.segment); +} + +// Scene Command 0x06: Entrance List +void func_800987F8(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->setupEntranceList = SEGMENTED_TO_VIRTUAL(cmd->entranceList.segment); +} + +// Scene Command 0x07: Special Files +void func_8009883C(GlobalContext* globalCtx, SceneCmd* cmd) { + if (cmd->specialFiles.keepObjectId != OBJECT_INVALID) { + globalCtx->objectCtx.subKeepIndex = Object_Spawn(&globalCtx->objectCtx, cmd->specialFiles.keepObjectId); + gSegments[5] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[globalCtx->objectCtx.subKeepIndex].segment); + } + + if (cmd->specialFiles.cUpElfMsgNum != 0) { + globalCtx->cUpElfMsgs = Gameplay_LoadFile(globalCtx, &sNaviMsgFiles[cmd->specialFiles.cUpElfMsgNum - 1]); + } +} + +// Scene Command 0x08: Room Behavior +void func_80098904(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->roomCtx.curRoom.unk_03 = cmd->roomBehavior.gpFlag1; + globalCtx->roomCtx.curRoom.unk_02 = cmd->roomBehavior.gpFlag2 & 0xFF; + globalCtx->roomCtx.curRoom.showInvisActors = (cmd->roomBehavior.gpFlag2 >> 8) & 1; + globalCtx->msgCtx.disableWarpSongs = (cmd->roomBehavior.gpFlag2 >> 0xA) & 1; +} + +// Scene Command 0x0A: Mesh Header +void func_80098958(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->roomCtx.curRoom.mesh = SEGMENTED_TO_VIRTUAL(cmd->mesh.segment); +} + +// Scene Command 0x0B: Object List +void func_8009899C(GlobalContext* globalCtx, SceneCmd* cmd) { + s32 i; + s32 j; + s32 k; + ObjectStatus* status; + ObjectStatus* status2; + ObjectStatus* firstStatus; + s16* objectEntry = SEGMENTED_TO_VIRTUAL(cmd->objectList.segment); + void* nextPtr; + + k = 0; + i = globalCtx->objectCtx.unk_09; + firstStatus = &globalCtx->objectCtx.status[0]; + status = &globalCtx->objectCtx.status[i]; + + while (i < globalCtx->objectCtx.num) { + if (status->id != *objectEntry) { + status2 = &globalCtx->objectCtx.status[i]; + for (j = i; j < globalCtx->objectCtx.num; j++) { + status2->id = OBJECT_INVALID; + status2++; + } + globalCtx->objectCtx.num = i; + func_80031A28(globalCtx, &globalCtx->actorCtx); + + continue; + } + + i++; + k++; + objectEntry++; + status++; + } + + ASSERT(cmd->objectList.num <= OBJECT_EXCHANGE_BANK_MAX, "scene_info->object_bank.num <= OBJECT_EXCHANGE_BANK_MAX", + "../z_scene.c", 705); + + if (1) {} + + while (k < cmd->objectList.num) { + nextPtr = func_800982FC(&globalCtx->objectCtx, i, *objectEntry); + if (i < OBJECT_EXCHANGE_BANK_MAX - 1) { + firstStatus[i + 1].segment = nextPtr; + } + i++; + k++; + objectEntry++; + } + + globalCtx->objectCtx.num = i; +} + +// Scene Command 0x0C: Light List +void func_80098B74(GlobalContext* globalCtx, SceneCmd* cmd) { + s32 i; + LightInfo* lightInfo = SEGMENTED_TO_VIRTUAL(cmd->lightList.segment); + + for (i = 0; i < cmd->lightList.num; i++) { + LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, lightInfo); + lightInfo++; + } +} + +// Scene Command 0x0D: Path List +void func_80098C24(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->setupPathList = SEGMENTED_TO_VIRTUAL(cmd->pathList.segment); +} + +// Scene Command 0x0E: Transition Actor List +void func_80098C68(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->transiActorCtx.numActors = cmd->transiActorList.num; + globalCtx->transiActorCtx.list = SEGMENTED_TO_VIRTUAL(cmd->transiActorList.segment); +} + +void TransitionActor_InitContext(GameState* state, TransitionActorContext* transiActorCtx) { + transiActorCtx->numActors = 0; +} + +// Scene Command 0x0F: Light Setting List +void func_80098CC8(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->envCtx.numLightSettings = cmd->lightSettingList.num; + globalCtx->envCtx.lightSettingsList = SEGMENTED_TO_VIRTUAL(cmd->lightSettingList.segment); +} + +// Scene Command 0x11: Skybox Settings +void func_80098D1C(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->skyboxId = cmd->skyboxSettings.skyboxId; + globalCtx->envCtx.unk_17 = globalCtx->envCtx.unk_18 = cmd->skyboxSettings.unk_05; + globalCtx->envCtx.indoors = cmd->skyboxSettings.unk_06; +} + +// Scene Command 0x12: Skybox Disables +void func_80098D5C(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->envCtx.skyboxDisabled = cmd->skyboxDisables.unk_04; + globalCtx->envCtx.sunMoonDisabled = cmd->skyboxDisables.unk_05; +} + +// Scene Command 0x10: Time Settings +void func_80098D80(GlobalContext* globalCtx, SceneCmd* cmd) { + if ((cmd->timeSettings.hour != 0xFF) && (cmd->timeSettings.min != 0xFF)) { + gSaveContext.skyboxTime = gSaveContext.dayTime = + ((cmd->timeSettings.hour + (cmd->timeSettings.min / 60.0f)) * 60.0f) / ((f32)(24 * 60) / 0x10000); + } + + if (cmd->timeSettings.unk_06 != 0xFF) { + globalCtx->envCtx.timeIncrement = cmd->timeSettings.unk_06; + } else { + globalCtx->envCtx.timeIncrement = 0; + } + + if (gSaveContext.sunsSongState == SUNSSONG_INACTIVE) { + gTimeIncrement = globalCtx->envCtx.timeIncrement; + } + + globalCtx->envCtx.sunPos.x = -(Math_SinS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + globalCtx->envCtx.sunPos.y = (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 120.0f) * 25.0f; + globalCtx->envCtx.sunPos.z = (Math_CosS(((void)0, gSaveContext.dayTime) - 0x8000) * 20.0f) * 25.0f; + + if (((globalCtx->envCtx.timeIncrement == 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) || + (gSaveContext.entranceIndex == 0x0604)) { + gSaveContext.skyboxTime = ((void)0, gSaveContext.dayTime); + if ((gSaveContext.skyboxTime >= 0x2AAC) && (gSaveContext.skyboxTime < 0x4555)) { + gSaveContext.skyboxTime = 0x3556; + } else if ((gSaveContext.skyboxTime >= 0x4555) && (gSaveContext.skyboxTime < 0x5556)) { + gSaveContext.skyboxTime = 0x5556; + } else if ((gSaveContext.skyboxTime >= 0xAAAB) && (gSaveContext.skyboxTime < 0xB556)) { + gSaveContext.skyboxTime = 0xB556; + } else if ((gSaveContext.skyboxTime >= 0xC001) && (gSaveContext.skyboxTime < 0xCAAC)) { + gSaveContext.skyboxTime = 0xCAAC; + } + } +} + +// Scene Command 0x05: Wind Settings +void func_80099090(GlobalContext* globalCtx, SceneCmd* cmd) { + s8 x = cmd->windSettings.x; + s8 y = cmd->windSettings.y; + s8 z = cmd->windSettings.z; + + globalCtx->envCtx.windDirection.x = x; + globalCtx->envCtx.windDirection.y = y; + globalCtx->envCtx.windDirection.z = z; + + globalCtx->envCtx.windSpeed = cmd->windSettings.unk_07; +} + +// Scene Command 0x13: Exit List +void func_800990F0(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->setupExitList = SEGMENTED_TO_VIRTUAL(cmd->exitList.segment); +} + +// Scene Command 0x09: Undefined +void func_80099134(GlobalContext* globalCtx, SceneCmd* cmd) { +} + +// Scene Command 0x15: Sound Settings +void func_80099140(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->sequenceCtx.seqId = cmd->soundSettings.seqId; + globalCtx->sequenceCtx.natureAmbienceId = cmd->soundSettings.natureAmbienceId; + + if (gSaveContext.seqId == (u8)NA_BGM_DISABLED) { + Audio_QueueSeqCmd(cmd->soundSettings.specId | 0xF0000000); + } +} + +// Scene Command 0x16: Echo Setting +void func_8009918C(GlobalContext* globalCtx, SceneCmd* cmd) { + globalCtx->roomCtx.curRoom.echo = cmd->echoSettings.echo; +} + +// Scene Command 0x18: Alternate Headers +void func_800991A0(GlobalContext* globalCtx, SceneCmd* cmd) { + s32 pad; + SceneCmd* altHeader; + + osSyncPrintf("\n[ZU]sceneset age =[%X]", ((void)0, gSaveContext.linkAge)); + osSyncPrintf("\n[ZU]sceneset time =[%X]", ((void)0, gSaveContext.cutsceneIndex)); + osSyncPrintf("\n[ZU]sceneset counter=[%X]", ((void)0, gSaveContext.sceneSetupIndex)); + + if (gSaveContext.sceneSetupIndex != 0) { + altHeader = ((SceneCmd**)SEGMENTED_TO_VIRTUAL(cmd->altHeaders.segment))[gSaveContext.sceneSetupIndex - 1]; + + if (1) {} + + if (altHeader != NULL) { + Scene_ExecuteCommands(globalCtx, SEGMENTED_TO_VIRTUAL(altHeader)); + (cmd + 1)->base.code = 0x14; + } else { + // "Coughh! There is no specified dataaaaa!" + osSyncPrintf("\nげぼはっ! 指定されたデータがないでええっす!"); + + if (gSaveContext.sceneSetupIndex == 3) { + altHeader = + ((SceneCmd**)SEGMENTED_TO_VIRTUAL(cmd->altHeaders.segment))[gSaveContext.sceneSetupIndex - 2]; + + // "Using adult day data there!" + osSyncPrintf("\nそこで、大人の昼データを使用するでええっす!!"); + + if (altHeader != NULL) { + Scene_ExecuteCommands(globalCtx, SEGMENTED_TO_VIRTUAL(altHeader)); + (cmd + 1)->base.code = 0x14; + } + } + } + } +} + +// Scene Command 0x17: Cutscene Data +void func_8009934C(GlobalContext* globalCtx, SceneCmd* cmd) { + osSyncPrintf("\ngame_play->demo_play.data=[%x]", globalCtx->csCtx.segment); + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(cmd->cutsceneData.segment); +} + +// Scene Command 0x19: Misc. Settings (Camera & World Map Area) +void func_800993C0(GlobalContext* globalCtx, SceneCmd* cmd) { + YREG(15) = cmd->miscSettings.cameraMovement; + gSaveContext.worldMapArea = cmd->miscSettings.area; + + if ((globalCtx->sceneNum == SCENE_SHOP1) || (globalCtx->sceneNum == SCENE_SYATEKIJYOU)) { + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + gSaveContext.worldMapArea = 1; + } + } + + if (((globalCtx->sceneNum >= SCENE_SPOT00) && (globalCtx->sceneNum <= SCENE_GANON_TOU)) || + ((globalCtx->sceneNum >= SCENE_ENTRA) && (globalCtx->sceneNum <= SCENE_SHRINE_R))) { + if (gSaveContext.cutsceneIndex < 0xFFF0) { + gSaveContext.worldMapAreaData |= gBitFlags[gSaveContext.worldMapArea]; + osSyncPrintf("000 area_arrival=%x (%d)\n", gSaveContext.worldMapAreaData, + gSaveContext.worldMapArea); + } + } +} + +void (*gSceneCmdHandlers[])(GlobalContext*, SceneCmd*) = { + func_80098508, func_800985DC, func_80098630, func_80098674, func_800987A4, func_80099090, func_800987F8, + func_8009883C, func_80098904, func_80099134, func_80098958, func_8009899C, func_80098B74, func_80098C24, + func_80098C68, func_80098CC8, func_80098D80, func_80098D1C, func_80098D5C, func_800990F0, NULL, + func_80099140, func_8009918C, func_8009934C, func_800991A0, func_800993C0, +}; + +RomFile sNaviMsgFiles[] = { + ROM_FILE(text/elf_message_field/elf_message_field), + ROM_FILE(text/elf_message_ydan/elf_message_ydan), + ROM_FILE_UNSET, +}; + +s16 gLinkObjectIds[] = { OBJECT_LINK_BOY, OBJECT_LINK_CHILD }; + +u32 gObjectTableSize = ARRAY_COUNT(gObjectTable); + +// Object linker symbol declarations (used in the table below) +#define DEFINE_OBJECT(name, _1) DECLARE_ROM_SEGMENT(name) +#define DEFINE_OBJECT_NULL(_0, _1) +#define DEFINE_OBJECT_UNSET(_0) + +//#include "tables/object_table.h" + +#undef DEFINE_OBJECT +#undef DEFINE_OBJECT_NULL +#undef DEFINE_OBJECT_UNSET + +// Object Table definition +#define DEFINE_OBJECT(name, _1) ROM_FILE(name), +#define DEFINE_OBJECT_NULL(name, _1) ROM_FILE(name), +#define DEFINE_OBJECT_UNSET(_0) { 0, 0, "" }, + +RomFile gObjectTable[] = { +#include "tables/object_table.h" +}; + +#undef DEFINE_OBJECT +#undef DEFINE_OBJECT_NULL +#undef DEFINE_OBJECT_UNSET diff --git a/soh/src/code/z_scene_table.c b/soh/src/code/z_scene_table.c new file mode 100644 index 000000000..742d57b9e --- /dev/null +++ b/soh/src/code/z_scene_table.c @@ -0,0 +1,2566 @@ +#include "global.h" + +#include "scenes/overworld/spot00/spot00_scene.h" +#include "scenes/overworld/spot00/spot00_room_0.h" +#include "scenes/overworld/spot01/spot01_scene.h" +#include "scenes/overworld/spot07/spot07_scene.h" +#include "scenes/overworld/spot12/spot12_scene.h" +#include "scenes/overworld/spot16/spot16_scene.h" +#include "scenes/overworld/spot16/spot16_room_0.h" +#include "scenes/overworld/spot18/spot18_scene.h" +#include "scenes/overworld/spot20/spot20_scene.h" +#include "scenes/overworld/souko/souko_scene.h" + +#include "scenes/dungeons/men/men_scene.h" +#include "scenes/dungeons/ddan/ddan_scene.h" +#include "scenes/dungeons/ydan/ydan_scene.h" +#include "scenes/dungeons/Bmori1/Bmori1_scene.h" +#include "scenes/dungeons/MIZUsin/MIZUsin_scene.h" +#include "scenes/dungeons/gerudoway/gerudoway_scene.h" +#include "scenes/dungeons/jyasinzou/jyasinzou_scene.h" +#include "scenes/indoors/miharigoya/miharigoya_scene.h" +#include "scenes/dungeons/ice_doukutu/ice_doukutu_scene.h" + +#include "overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.h" + +#define ENTRANCE(scene, spawn, continueBgm, displayTitleCard, fadeIn, fadeOut) \ + { \ + scene, spawn, \ + ((continueBgm & 1) << 15) | ((displayTitleCard & 1) << 14) | ((fadeIn & 0x7F) << 7) | (fadeOut & 0x7F) \ + } + +EntranceInfo gEntranceTable[] = { + ENTRANCE(0x00, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x00, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x00, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x00, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x01, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x01, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x01, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x01, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x0B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x0B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x0B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x0B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x14, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x14, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x14, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x14, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x05, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x05, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x05, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x05, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6E, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6E, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6E, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6E, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6C, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6C, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6C, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6C, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x68, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x68, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x68, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x68, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x69, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x69, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x69, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x69, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x02, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x02, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x02, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x02, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x02, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x41, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x41, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x41, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x41, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x41, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x41, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x1B, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x1C, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x1D, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x1D, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x07, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x07, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x42, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x42, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x42, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x42, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x3E, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x38, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x38, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x38, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x38, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x6A, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6A, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x6A, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x6A, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x40, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x40, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x40, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x40, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x4C, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x4C, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x4C, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x4C, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x0B, 0x0B), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x10, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x10, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x10, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x10, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x1E, 0x03, 1, 1, 0x03, 0x03), + ENTRANCE(0x1F, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x1E, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x1F, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x44, 0x00, 0, 1, 0x07, 0x07), + ENTRANCE(0x44, 0x00, 0, 1, 0x07, 0x07), ENTRANCE(0x44, 0x00, 0, 1, 0x07, 0x07), + ENTRANCE(0x44, 0x00, 0, 1, 0x07, 0x07), ENTRANCE(0x44, 0x00, 0, 1, 0x0D, 0x02), + ENTRANCE(0x44, 0x00, 0, 1, 0x0D, 0x02), ENTRANCE(0x44, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x4E, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x4E, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x4E, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x4E, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x6B, 0x00, 0, 1, 0x22, 0x22), ENTRANCE(0x6B, 0x00, 0, 1, 0x22, 0x22), + ENTRANCE(0x6B, 0x00, 0, 1, 0x22, 0x22), ENTRANCE(0x6B, 0x00, 0, 1, 0x22, 0x22), + ENTRANCE(0x45, 0x00, 0, 1, 0x22, 0x04), ENTRANCE(0x46, 0x00, 0, 1, 0x22, 0x04), + ENTRANCE(0x45, 0x00, 0, 1, 0x22, 0x04), ENTRANCE(0x46, 0x00, 0, 1, 0x22, 0x04), + ENTRANCE(0x4D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x4D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x4D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x4D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x06, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x06, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x06, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x06, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x06, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x06, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x09, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x09, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x09, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x09, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x09, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x17, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x17, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x17, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x17, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x17, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x17, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x17, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x65, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x65, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x65, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x65, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x08, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x08, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x08, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x08, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x27, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x27, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x27, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x27, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x47, 0x00, 0, 1, 0x0A, 0x0A), ENTRANCE(0x47, 0x00, 0, 1, 0x02, 0x0A), + ENTRANCE(0x47, 0x00, 1, 1, 0x0A, 0x0A), ENTRANCE(0x47, 0x00, 0, 1, 0x0B, 0x0B), + ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x47, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x47, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x47, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x1E, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x1F, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x1E, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x1F, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x20, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x21, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x22, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x22, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x20, 0x00, 0, 1, 0x08, 0x08), + ENTRANCE(0x67, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x2C, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2C, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x2C, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2C, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x34, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x34, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x34, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x34, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x34, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x34, 0x00, 0, 1, 0x10, 0x10), ENTRANCE(0x2D, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x2D, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x2D, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x2D, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x01, 0x01, 1, 0, 0x02, 0x02), + ENTRANCE(0x01, 0x01, 1, 0, 0x02, 0x02), ENTRANCE(0x01, 0x01, 1, 0, 0x02, 0x02), + ENTRANCE(0x01, 0x01, 1, 0, 0x02, 0x02), ENTRANCE(0x26, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x26, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x26, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x26, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x51, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x00, 0, 1, 0x21, 0x21), ENTRANCE(0x51, 0x00, 0, 1, 0x21, 0x21), + ENTRANCE(0x51, 0x00, 0, 1, 0x23, 0x23), ENTRANCE(0x51, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x51, 0x00, 0, 1, 0x23, 0x23), + ENTRANCE(0x51, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x00, 0, 1, 0x29, 0x29), + ENTRANCE(0x51, 0x00, 1, 1, 0x03, 0x03), ENTRANCE(0x52, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x00, 0, 1, 0x26, 0x26), + ENTRANCE(0x52, 0x00, 0, 1, 0x21, 0x21), ENTRANCE(0x52, 0x00, 1, 1, 0x21, 0x21), + ENTRANCE(0x52, 0x00, 0, 1, 0x21, 0x21), ENTRANCE(0x52, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x53, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x54, 0x00, 1, 1, 0x03, 0x03), ENTRANCE(0x54, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x54, 0x00, 1, 1, 0x03, 0x03), ENTRANCE(0x54, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x55, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x55, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x00, 1, 1, 0x0A, 0x0A), ENTRANCE(0x55, 0x00, 0, 1, 0x0A, 0x0A), + ENTRANCE(0x55, 0x00, 1, 1, 0x0A, 0x0A), ENTRANCE(0x55, 0x00, 0, 1, 0x0D, 0x0A), + ENTRANCE(0x55, 0x00, 0, 1, 0x0A, 0x0A), ENTRANCE(0x55, 0x00, 0, 1, 0x0A, 0x0A), + ENTRANCE(0x55, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x55, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x55, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x56, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x56, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x56, 0x00, 0, 1, 0x00, 0x00), + ENTRANCE(0x57, 0x00, 1, 1, 0x03, 0x03), ENTRANCE(0x57, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x57, 0x00, 1, 1, 0x03, 0x03), ENTRANCE(0x57, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x57, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x57, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x58, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x58, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x58, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x59, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x59, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x00, 0, 1, 0x0D, 0x00), ENTRANCE(0x59, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x59, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x00, 1, 1, 0x0C, 0x0A), + ENTRANCE(0x5A, 0x00, 1, 1, 0x0A, 0x0A), ENTRANCE(0x5A, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x00, 1, 1, 0x0F, 0x0F), + ENTRANCE(0x5C, 0x00, 1, 1, 0x0F, 0x0F), ENTRANCE(0x5C, 0x00, 1, 1, 0x0F, 0x0F), + ENTRANCE(0x5C, 0x00, 1, 1, 0x0F, 0x0F), ENTRANCE(0x5C, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x5C, 0x00, 1, 1, 0x0F, 0x0F), ENTRANCE(0x5D, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x00, 1, 1, 0x21, 0x21), + ENTRANCE(0x5D, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x5E, 0x00, 1, 1, 0x0E, 0x0E), ENTRANCE(0x5E, 0x00, 1, 1, 0x0E, 0x0E), + ENTRANCE(0x5E, 0x00, 1, 1, 0x0E, 0x0E), ENTRANCE(0x5E, 0x00, 1, 1, 0x0E, 0x0E), + ENTRANCE(0x0E, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x5F, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x5F, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x64, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x64, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x64, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x60, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x60, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x60, 0x00, 1, 1, 0x0A, 0x0A), + ENTRANCE(0x60, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x60, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x60, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x60, 0x00, 1, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x00, 1, 1, 0x03, 0x03), ENTRANCE(0x61, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x61, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x62, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x62, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x62, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x62, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x62, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x62, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x58, 0x03, 0, 1, 0x00, 0x00), + ENTRANCE(0x58, 0x03, 0, 1, 0x00, 0x00), ENTRANCE(0x58, 0x03, 0, 1, 0x00, 0x00), + ENTRANCE(0x58, 0x03, 0, 1, 0x00, 0x00), ENTRANCE(0x63, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x63, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x63, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x63, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x63, 0x00, 0, 1, 0x20, 0x20), + ENTRANCE(0x63, 0x00, 0, 1, 0x2A, 0x2A), ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x04, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x04, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x04, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x04, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x03, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x03, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x03, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x03, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x42, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x42, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x42, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x42, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x23, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x24, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x25, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x25, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x04, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x04, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x04, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x04, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x51, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x02, 1, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x51, 0x02, 1, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x51, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x04, 1, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x51, 0x04, 1, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x51, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x54, 0x01, 1, 1, 0x03, 0x03), + ENTRANCE(0x54, 0x01, 1, 1, 0x03, 0x03), ENTRANCE(0x54, 0x01, 1, 1, 0x03, 0x03), + ENTRANCE(0x54, 0x01, 1, 1, 0x03, 0x03), ENTRANCE(0x54, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x54, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x54, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x54, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x58, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x58, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x60, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x60, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x60, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x60, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x60, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x62, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x62, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x62, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x62, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x38, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x38, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x38, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x38, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x1A, 0x05, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x05, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x05, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x05, 1, 1, 0x02, 0x02), ENTRANCE(0x20, 0x08, 0, 1, 0x04, 0x02), + ENTRANCE(0x21, 0x08, 0, 1, 0x04, 0x02), ENTRANCE(0x22, 0x08, 0, 1, 0x04, 0x02), + ENTRANCE(0x22, 0x08, 0, 1, 0x04, 0x02), ENTRANCE(0x20, 0x09, 0, 1, 0x04, 0x02), + ENTRANCE(0x21, 0x09, 0, 1, 0x04, 0x02), ENTRANCE(0x22, 0x09, 0, 1, 0x04, 0x02), + ENTRANCE(0x22, 0x09, 0, 1, 0x04, 0x02), ENTRANCE(0x20, 0x0A, 0, 1, 0x04, 0x02), + ENTRANCE(0x21, 0x0A, 0, 1, 0x04, 0x02), ENTRANCE(0x22, 0x0A, 0, 1, 0x04, 0x02), + ENTRANCE(0x22, 0x0A, 0, 1, 0x04, 0x02), ENTRANCE(0x54, 0x03, 1, 1, 0x03, 0x03), + ENTRANCE(0x54, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x54, 0x03, 1, 1, 0x03, 0x03), + ENTRANCE(0x54, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x54, 0x04, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x54, 0x04, 0, 1, 0x2C, 0x2C), ENTRANCE(0x54, 0x04, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x54, 0x04, 0, 1, 0x2C, 0x2C), ENTRANCE(0x5C, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5C, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5C, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x02, 0, 0, 0x03, 0x03), + ENTRANCE(0x5C, 0x02, 0, 0, 0x02, 0x02), ENTRANCE(0x5C, 0x02, 0, 0, 0x03, 0x03), + ENTRANCE(0x5C, 0x02, 0, 0, 0x02, 0x02), ENTRANCE(0x5C, 0x03, 0, 0, 0x03, 0x03), + ENTRANCE(0x5C, 0x03, 0, 0, 0x02, 0x02), ENTRANCE(0x5C, 0x03, 0, 0, 0x03, 0x03), + ENTRANCE(0x5C, 0x03, 0, 0, 0x02, 0x02), ENTRANCE(0x5C, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x5C, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x5C, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x5C, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x5C, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x5C, 0x06, 1, 0, 0x0F, 0x0F), + ENTRANCE(0x5C, 0x06, 1, 0, 0x0F, 0x0F), ENTRANCE(0x5C, 0x06, 1, 0, 0x0F, 0x0F), + ENTRANCE(0x5C, 0x06, 1, 0, 0x0F, 0x0F), ENTRANCE(0x51, 0x06, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x06, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x07, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x51, 0x07, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x03, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x03, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x03, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x03, 0, 1, 0x04, 0x02), ENTRANCE(0x53, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x53, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x55, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x55, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x03, 0, 1, 0x04, 0x04), + ENTRANCE(0x55, 0x03, 0, 1, 0x04, 0x04), ENTRANCE(0x55, 0x03, 0, 1, 0x04, 0x04), + ENTRANCE(0x55, 0x03, 0, 1, 0x04, 0x04), ENTRANCE(0x56, 0x01, 0, 1, 0x12, 0x12), + ENTRANCE(0x56, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x56, 0x01, 0, 1, 0x12, 0x12), + ENTRANCE(0x56, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x57, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x57, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x02, 0, 1, 0x13, 0x13), + ENTRANCE(0x57, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x02, 0, 1, 0x13, 0x13), + ENTRANCE(0x57, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x59, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x59, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x59, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x59, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x59, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x59, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x02, 1, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x02, 1, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x03, 1, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x5A, 0x03, 1, 1, 0x03, 0x03), + ENTRANCE(0x5A, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x02, 0, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x5D, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x5F, 0x01, 0, 1, 0x03, 0x03), + ENTRANCE(0x5F, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x64, 0x01, 0, 1, 0x05, 0x05), + ENTRANCE(0x64, 0x01, 0, 1, 0x05, 0x05), ENTRANCE(0x5F, 0x01, 0, 1, 0x00, 0x00), + ENTRANCE(0x60, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x60, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x60, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x60, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x03, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x03, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x03, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x03, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x00, 0x01, 1, 0, 0x02, 0x02), ENTRANCE(0x00, 0x01, 1, 0, 0x02, 0x02), + ENTRANCE(0x00, 0x01, 1, 0, 0x02, 0x02), ENTRANCE(0x00, 0x01, 1, 0, 0x02, 0x02), + ENTRANCE(0x0E, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x20, 0x01, 0, 1, 0x03, 0x03), ENTRANCE(0x21, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x22, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x22, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x20, 0x02, 0, 1, 0x03, 0x03), ENTRANCE(0x21, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x22, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x22, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x20, 0x03, 1, 1, 0x03, 0x03), ENTRANCE(0x21, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x22, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x22, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x04, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x04, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x04, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x04, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x05, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x05, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x05, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x05, 0, 0, 0x04, 0x04), + ENTRANCE(0x1B, 0x02, 0, 0, 0x02, 0x02), ENTRANCE(0x1C, 0x02, 0, 0, 0x02, 0x02), + ENTRANCE(0x1D, 0x02, 0, 0, 0x02, 0x02), ENTRANCE(0x1D, 0x02, 0, 0, 0x02, 0x02), + ENTRANCE(0x34, 0x01, 0, 1, 0x04, 0x04), ENTRANCE(0x34, 0x01, 0, 1, 0x04, 0x04), + ENTRANCE(0x34, 0x01, 0, 1, 0x04, 0x04), ENTRANCE(0x34, 0x01, 0, 1, 0x04, 0x04), + ENTRANCE(0x1B, 0x01, 0, 1, 0x03, 0x03), ENTRANCE(0x1C, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x1D, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x1D, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x08, 1, 1, 0x03, 0x03), ENTRANCE(0x51, 0x08, 1, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x08, 1, 1, 0x03, 0x03), ENTRANCE(0x51, 0x08, 1, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x09, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x09, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x09, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x09, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x0A, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x0A, 0, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x0A, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x0A, 0, 1, 0x03, 0x03), + ENTRANCE(0x55, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x55, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x0B, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0B, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0B, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0B, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0C, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0C, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0C, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0C, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0D, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0D, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0D, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0D, 0, 1, 0x04, 0x02), + ENTRANCE(0x45, 0x01, 0, 1, 0x03, 0x03), ENTRANCE(0x46, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x45, 0x01, 0, 1, 0x03, 0x03), ENTRANCE(0x46, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x1E, 0x01, 1, 1, 0x03, 0x03), ENTRANCE(0x1F, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x1E, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x1F, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x20, 0x04, 1, 1, 0x03, 0x03), ENTRANCE(0x21, 0x04, 1, 1, 0x02, 0x02), + ENTRANCE(0x22, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x22, 0x04, 1, 1, 0x02, 0x02), + ENTRANCE(0x20, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x21, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x22, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x22, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x52, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x04, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x04, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x01, 0, 0, 0x03, 0x03), ENTRANCE(0x63, 0x01, 0, 0, 0x02, 0x02), + ENTRANCE(0x63, 0x01, 0, 0, 0x03, 0x03), ENTRANCE(0x63, 0x01, 0, 0, 0x02, 0x02), + ENTRANCE(0x07, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x07, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x07, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x07, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x07, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x05, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x05, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x06, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x06, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x07, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x07, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x08, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x08, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x02, 0, 1, 0x0A, 0x0A), ENTRANCE(0x43, 0x02, 0, 1, 0x0A, 0x0A), + ENTRANCE(0x43, 0x02, 0, 1, 0x0A, 0x0A), ENTRANCE(0x43, 0x02, 0, 1, 0x0A, 0x0A), + ENTRANCE(0x44, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x44, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x44, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x44, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x09, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x09, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x09, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x09, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0A, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0A, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0A, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0A, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0B, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0B, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0B, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0B, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0C, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0C, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0C, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0C, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x02, 0, 1, 0x03, 0x03), ENTRANCE(0x63, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x02, 0, 1, 0x03, 0x03), ENTRANCE(0x63, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x63, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x63, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x47, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x42, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x06, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x06, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x06, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x06, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x36, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x36, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x36, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x36, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x2A, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2A, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x2A, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2A, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x13, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x13, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x13, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x13, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x15, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x15, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x15, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x15, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x57, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x57, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x57, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x57, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x3A, 0x00, 0, 1, 0x02, 0x20), + ENTRANCE(0x3A, 0x00, 0, 1, 0x02, 0x20), ENTRANCE(0x3A, 0x00, 0, 1, 0x02, 0x20), + ENTRANCE(0x3A, 0x00, 0, 1, 0x02, 0x20), ENTRANCE(0x51, 0x0E, 1, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x0E, 1, 1, 0x03, 0x03), ENTRANCE(0x51, 0x0E, 1, 1, 0x03, 0x03), + ENTRANCE(0x51, 0x0E, 1, 1, 0x03, 0x03), ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3F, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x3F, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x3F, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x3F, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x43, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x04, 0, 1, 0x2C, 0x2C), ENTRANCE(0x58, 0x04, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x58, 0x04, 0, 1, 0x2C, 0x2C), ENTRANCE(0x58, 0x04, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x1A, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x03, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x03, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x04, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x04, 1, 1, 0x02, 0x02), + ENTRANCE(0x55, 0x07, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x07, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x07, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x07, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x08, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x08, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x08, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x08, 0, 0, 0x04, 0x04), + ENTRANCE(0x5F, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x5F, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x64, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x64, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x5F, 0x02, 0, 1, 0x00, 0x00), ENTRANCE(0x52, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x07, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x07, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x07, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x07, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x08, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x08, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x08, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x08, 0, 1, 0x04, 0x02), ENTRANCE(0x53, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x53, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x53, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x53, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x53, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x04, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x04, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x04, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x04, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x05, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x05, 0, 1, 0x03, 0x03), ENTRANCE(0x5E, 0x01, 1, 1, 0x0E, 0x0E), + ENTRANCE(0x5E, 0x01, 1, 1, 0x0E, 0x0E), ENTRANCE(0x5E, 0x01, 1, 1, 0x0E, 0x0E), + ENTRANCE(0x5E, 0x01, 1, 1, 0x0E, 0x0E), ENTRANCE(0x5E, 0x02, 0, 1, 0x0E, 0x0E), + ENTRANCE(0x5E, 0x02, 0, 1, 0x0E, 0x0E), ENTRANCE(0x5E, 0x02, 0, 1, 0x0E, 0x0E), + ENTRANCE(0x5E, 0x02, 0, 1, 0x0E, 0x0E), ENTRANCE(0x3C, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x3C, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x3C, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x3C, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x3D, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x63, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x63, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x63, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x2E, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x2E, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2E, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x2E, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2F, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x2F, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x2F, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x2F, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x30, 0x00, 0, 0, 0x04, 0x20), ENTRANCE(0x30, 0x00, 0, 0, 0x04, 0x20), + ENTRANCE(0x30, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x30, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x31, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x31, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x31, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x31, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x1E, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x1F, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x1E, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x1F, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x32, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x32, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x32, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x32, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x05, 0, 1, 0x03, 0x03), ENTRANCE(0x59, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x59, 0x05, 0, 1, 0x03, 0x03), ENTRANCE(0x59, 0x05, 0, 1, 0x03, 0x03), + ENTRANCE(0x35, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x35, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x35, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x35, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x37, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x37, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x37, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x37, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x39, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x39, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x39, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x39, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x5D, 0x0D, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0D, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0D, 0, 1, 0x03, 0x03), ENTRANCE(0x5D, 0x0D, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x0E, 0, 1, 0x03, 0x05), ENTRANCE(0x5D, 0x0E, 0, 1, 0x02, 0x04), + ENTRANCE(0x5D, 0x0E, 0, 1, 0x03, 0x05), ENTRANCE(0x5D, 0x0E, 0, 1, 0x02, 0x04), + ENTRANCE(0x5D, 0x0F, 1, 1, 0x0F, 0x0F), ENTRANCE(0x5D, 0x0F, 1, 1, 0x0F, 0x0F), + ENTRANCE(0x5D, 0x0F, 1, 1, 0x0F, 0x0F), ENTRANCE(0x5D, 0x0F, 1, 1, 0x0F, 0x0F), + ENTRANCE(0x5D, 0x10, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x10, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x10, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x10, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x11, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x11, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x11, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x11, 0, 1, 0x02, 0x02), + ENTRANCE(0x20, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x21, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x22, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x22, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x20, 0x07, 0, 1, 0x04, 0x02), ENTRANCE(0x21, 0x07, 0, 1, 0x04, 0x02), + ENTRANCE(0x22, 0x07, 0, 1, 0x04, 0x02), ENTRANCE(0x22, 0x07, 0, 1, 0x04, 0x02), + ENTRANCE(0x1E, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x1F, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x1E, 0x02, 0, 1, 0x03, 0x03), ENTRANCE(0x1F, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x58, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x58, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x58, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x57, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x57, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x57, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x57, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x57, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x57, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x57, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x57, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x5A, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x5A, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x5A, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x5A, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x59, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x59, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x59, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x59, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x59, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x59, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x04, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x04, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x04, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x05, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x05, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x05, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x05, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x06, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x06, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x06, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x06, 1, 1, 0x02, 0x02), + ENTRANCE(0x30, 0x01, 0, 0, 0x04, 0x20), ENTRANCE(0x30, 0x01, 0, 0, 0x04, 0x20), + ENTRANCE(0x30, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x30, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x30, 0x02, 0, 0, 0x04, 0x20), ENTRANCE(0x30, 0x02, 0, 0, 0x04, 0x20), + ENTRANCE(0x30, 0x02, 0, 1, 0x04, 0x20), ENTRANCE(0x30, 0x02, 0, 1, 0x04, 0x20), + ENTRANCE(0x06, 0x02, 0, 0, 0x02, 0x02), ENTRANCE(0x06, 0x02, 0, 0, 0x02, 0x02), + ENTRANCE(0x06, 0x02, 0, 0, 0x02, 0x02), ENTRANCE(0x06, 0x02, 0, 0, 0x02, 0x02), + ENTRANCE(0x06, 0x03, 0, 0, 0x02, 0x02), ENTRANCE(0x06, 0x03, 0, 0, 0x02, 0x02), + ENTRANCE(0x06, 0x03, 0, 0, 0x02, 0x02), ENTRANCE(0x06, 0x03, 0, 0, 0x02, 0x02), + ENTRANCE(0x06, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x06, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x06, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x06, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x62, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x62, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x62, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x62, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x4A, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x4A, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x4A, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x4A, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x4A, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x4A, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x4A, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x02, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x02, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x02, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x02, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x12, 0x00, 1, 0, 0x02, 0x02), + ENTRANCE(0x12, 0x00, 1, 0, 0x02, 0x02), ENTRANCE(0x12, 0x00, 1, 0, 0x02, 0x02), + ENTRANCE(0x12, 0x00, 1, 0, 0x02, 0x02), ENTRANCE(0x11, 0x00, 1, 0, 0x02, 0x02), + ENTRANCE(0x11, 0x00, 1, 0, 0x02, 0x02), ENTRANCE(0x11, 0x00, 1, 0, 0x02, 0x02), + ENTRANCE(0x11, 0x00, 1, 0, 0x02, 0x02), ENTRANCE(0x18, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x18, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x18, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x18, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x16, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x16, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x16, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x16, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x0A, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x0A, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x0A, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x0A, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x19, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x19, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x19, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x19, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x05, 0x01, 1, 1, 0x03, 0x02), + ENTRANCE(0x05, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x05, 0x01, 1, 1, 0x03, 0x02), + ENTRANCE(0x05, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x0A, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x0A, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x0A, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x0A, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x0A, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x0A, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x0A, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x0A, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x63, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x63, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x63, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x63, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x28, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x28, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x28, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x28, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x29, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x29, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x29, 0x00, 0, 1, 0x04, 0x04), + ENTRANCE(0x29, 0x00, 0, 1, 0x04, 0x04), ENTRANCE(0x2B, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2B, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x2B, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x2B, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x1A, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x55, 0x09, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x09, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x09, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x09, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x0A, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x0A, 0, 0, 0x04, 0x04), ENTRANCE(0x55, 0x0A, 0, 0, 0x04, 0x04), + ENTRANCE(0x55, 0x0A, 0, 0, 0x04, 0x04), ENTRANCE(0x52, 0x09, 0, 1, 0x04, 0x04), + ENTRANCE(0x52, 0x09, 0, 1, 0x04, 0x04), ENTRANCE(0x52, 0x09, 0, 1, 0x04, 0x04), + ENTRANCE(0x52, 0x09, 0, 1, 0x04, 0x04), ENTRANCE(0x48, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x48, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x48, 0x00, 0, 1, 0x02, 0x26), + ENTRANCE(0x48, 0x00, 0, 1, 0x02, 0x26), ENTRANCE(0x48, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x48, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x48, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x48, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x55, 0x0B, 0, 1, 0x04, 0x02), + ENTRANCE(0x55, 0x0B, 0, 1, 0x04, 0x02), ENTRANCE(0x55, 0x0B, 0, 1, 0x04, 0x02), + ENTRANCE(0x55, 0x0B, 0, 1, 0x04, 0x02), ENTRANCE(0x60, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x60, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x60, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x60, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x49, 0x00, 0, 1, 0x02, 0x20), + ENTRANCE(0x49, 0x00, 0, 1, 0x02, 0x20), ENTRANCE(0x49, 0x00, 0, 1, 0x02, 0x20), + ENTRANCE(0x49, 0x00, 0, 1, 0x02, 0x20), ENTRANCE(0x52, 0x0A, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x0A, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x0A, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x0A, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x00, 0, 1, 0x04, 0x02), + ENTRANCE(0x23, 0x01, 0, 1, 0x04, 0x02), ENTRANCE(0x24, 0x01, 0, 1, 0x04, 0x02), + ENTRANCE(0x25, 0x01, 0, 1, 0x04, 0x02), ENTRANCE(0x25, 0x01, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0F, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0F, 0, 1, 0x04, 0x02), + ENTRANCE(0x51, 0x0F, 0, 1, 0x05, 0x05), ENTRANCE(0x51, 0x0F, 0, 1, 0x04, 0x02), + ENTRANCE(0x60, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x60, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x60, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x60, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x5F, 0x04, 0, 1, 0x03, 0x03), ENTRANCE(0x5F, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x64, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x64, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x08, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x08, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x09, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x09, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x09, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x09, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x0A, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x0A, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x0A, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x0A, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x0B, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x0B, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x0B, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x0B, 0, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x07, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x07, 1, 1, 0x02, 0x02), + ENTRANCE(0x0E, 0x07, 1, 1, 0x02, 0x02), ENTRANCE(0x0E, 0x07, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x01, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x01, 1, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x06, 0, 0, 0x02, 0x26), ENTRANCE(0x63, 0x06, 0, 0, 0x02, 0x26), + ENTRANCE(0x63, 0x06, 0, 0, 0x02, 0x26), ENTRANCE(0x63, 0x06, 0, 0, 0x02, 0x26), + ENTRANCE(0x63, 0x07, 0, 0, 0x2E, 0x2E), ENTRANCE(0x63, 0x07, 0, 0, 0x2E, 0x2E), + ENTRANCE(0x63, 0x07, 0, 0, 0x2E, 0x2E), ENTRANCE(0x63, 0x07, 0, 0, 0x2E, 0x2E), + ENTRANCE(0x5B, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x5B, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x06, 0, 1, 0x2C, 0x2C), ENTRANCE(0x5B, 0x06, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x5B, 0x06, 0, 1, 0x2C, 0x2C), ENTRANCE(0x5B, 0x06, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x5B, 0x07, 0, 1, 0x2C, 0x2C), ENTRANCE(0x5B, 0x07, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x5B, 0x07, 0, 1, 0x2C, 0x2C), ENTRANCE(0x5B, 0x07, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x5B, 0x08, 0, 0, 0x02, 0x02), ENTRANCE(0x5B, 0x08, 0, 0, 0x02, 0x02), + ENTRANCE(0x5B, 0x08, 0, 0, 0x02, 0x02), ENTRANCE(0x5B, 0x08, 0, 0, 0x02, 0x02), + ENTRANCE(0x62, 0x03, 0, 1, 0x2C, 0x2C), ENTRANCE(0x62, 0x03, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x62, 0x03, 0, 1, 0x2C, 0x2C), ENTRANCE(0x62, 0x03, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x57, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x57, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x07, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x07, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x07, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x52, 0x0B, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x0B, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x0B, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x0B, 0, 1, 0x04, 0x02), + ENTRANCE(0x3B, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x3B, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x3B, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x5F, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x5F, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x64, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x64, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x5F, 0x03, 0, 1, 0x00, 0x00), ENTRANCE(0x52, 0x0C, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x0C, 0, 1, 0x04, 0x02), ENTRANCE(0x52, 0x0C, 0, 1, 0x04, 0x02), + ENTRANCE(0x52, 0x0C, 0, 1, 0x04, 0x02), ENTRANCE(0x48, 0x02, 1, 1, 0x04, 0x20), + ENTRANCE(0x48, 0x02, 1, 1, 0x04, 0x20), ENTRANCE(0x48, 0x02, 1, 1, 0x04, 0x20), + ENTRANCE(0x48, 0x02, 1, 1, 0x04, 0x20), ENTRANCE(0x4B, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x4B, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x4B, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x4B, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x53, 0x06, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x06, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x06, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x06, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x10, 0, 1, 0x0B, 0x0B), + ENTRANCE(0x51, 0x10, 0, 1, 0x0B, 0x0B), ENTRANCE(0x51, 0x10, 0, 1, 0x0B, 0x0B), + ENTRANCE(0x51, 0x10, 0, 1, 0x0B, 0x0B), ENTRANCE(0x52, 0x0D, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x0D, 0, 1, 0x02, 0x02), ENTRANCE(0x52, 0x0D, 0, 1, 0x03, 0x03), + ENTRANCE(0x52, 0x0D, 0, 1, 0x02, 0x02), ENTRANCE(0x4F, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x4F, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x4F, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x4F, 0x00, 0, 1, 0x03, 0x03), ENTRANCE(0x4F, 0x00, 0, 1, 0x03, 0x03), + ENTRANCE(0x1A, 0x06, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x06, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x06, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x06, 1, 1, 0x02, 0x02), + ENTRANCE(0x66, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x66, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x66, 0x00, 0, 1, 0x02, 0x02), ENTRANCE(0x66, 0x00, 0, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x07, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x07, 1, 1, 0x02, 0x02), + ENTRANCE(0x1A, 0x07, 1, 1, 0x02, 0x02), ENTRANCE(0x1A, 0x07, 1, 1, 0x02, 0x02), + ENTRANCE(0x32, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x32, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x32, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x32, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x2C, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x2C, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x2C, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x2C, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x33, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x33, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x33, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x33, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x0D, 0x01, 0, 0, 0x04, 0x02), ENTRANCE(0x0D, 0x01, 0, 0, 0x04, 0x02), + ENTRANCE(0x0D, 0x01, 0, 0, 0x04, 0x02), ENTRANCE(0x0D, 0x01, 0, 0, 0x04, 0x02), + ENTRANCE(0x0D, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x02, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x02, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x03, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x03, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x03, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x03, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x04, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x04, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x05, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x05, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x06, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x06, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x07, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x07, 0, 1, 0x04, 0x02), + ENTRANCE(0x0D, 0x07, 0, 1, 0x04, 0x02), ENTRANCE(0x0D, 0x07, 0, 1, 0x04, 0x02), + ENTRANCE(0x50, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x50, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x50, 0x00, 0, 1, 0x04, 0x20), ENTRANCE(0x50, 0x00, 0, 1, 0x04, 0x20), + ENTRANCE(0x52, 0x0E, 0, 1, 0x03, 0x03), ENTRANCE(0x52, 0x0E, 0, 1, 0x02, 0x02), + ENTRANCE(0x52, 0x0E, 0, 1, 0x03, 0x03), ENTRANCE(0x52, 0x0E, 0, 1, 0x02, 0x02), + ENTRANCE(0x63, 0x08, 0, 0, 0x20, 0x20), ENTRANCE(0x63, 0x08, 0, 0, 0x20, 0x20), + ENTRANCE(0x63, 0x08, 0, 0, 0x20, 0x20), ENTRANCE(0x63, 0x08, 0, 0, 0x20, 0x20), + ENTRANCE(0x63, 0x09, 0, 0, 0x02, 0x02), ENTRANCE(0x63, 0x09, 0, 0, 0x02, 0x02), + ENTRANCE(0x63, 0x09, 0, 0, 0x02, 0x02), ENTRANCE(0x63, 0x09, 0, 0, 0x02, 0x02), + ENTRANCE(0x57, 0x07, 0, 1, 0x2C, 0x2C), ENTRANCE(0x57, 0x07, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x57, 0x07, 0, 1, 0x2C, 0x2C), ENTRANCE(0x57, 0x07, 0, 1, 0x2C, 0x2C), + ENTRANCE(0x61, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x61, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x61, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x07, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x07, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x07, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x07, 0, 1, 0x03, 0x03), + ENTRANCE(0x0F, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x0F, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x0F, 0x00, 1, 1, 0x02, 0x02), ENTRANCE(0x0F, 0x00, 1, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x0C, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x0C, 0, 1, 0x02, 0x02), + ENTRANCE(0x0C, 0x0C, 0, 1, 0x02, 0x02), ENTRANCE(0x0C, 0x0C, 0, 1, 0x02, 0x02), + ENTRANCE(0x41, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x41, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x41, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x41, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x3D, 0x01, 0, 1, 0x04, 0x04), ENTRANCE(0x3D, 0x01, 0, 1, 0x04, 0x04), + ENTRANCE(0x3D, 0x01, 0, 1, 0x04, 0x04), ENTRANCE(0x3D, 0x01, 0, 1, 0x04, 0x04), + ENTRANCE(0x5C, 0x07, 0, 1, 0x03, 0x03), ENTRANCE(0x5C, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x5C, 0x07, 0, 1, 0x03, 0x03), ENTRANCE(0x5C, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x53, 0x08, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x08, 0, 1, 0x03, 0x03), + ENTRANCE(0x53, 0x08, 0, 1, 0x03, 0x03), ENTRANCE(0x53, 0x08, 0, 1, 0x03, 0x03), + ENTRANCE(0x03, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x03, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x03, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x03, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x3D, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x3D, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x3D, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x3D, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x05, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x05, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x11, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x11, 0, 1, 0x02, 0x02), + ENTRANCE(0x51, 0x11, 0, 1, 0x03, 0x03), ENTRANCE(0x51, 0x11, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x02, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x03, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x03, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x04, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x04, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x05, 0, 1, 0x02, 0x03), ENTRANCE(0x3E, 0x05, 0, 1, 0x02, 0x03), + ENTRANCE(0x3E, 0x05, 0, 1, 0x02, 0x03), ENTRANCE(0x3E, 0x05, 0, 1, 0x02, 0x03), + ENTRANCE(0x3E, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x06, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x06, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x08, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x08, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x09, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x09, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x09, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x09, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0A, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0A, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0A, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0A, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0B, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0B, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0B, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0B, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0C, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0C, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0C, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0C, 0, 1, 0x02, 0x02), + ENTRANCE(0x37, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x37, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x37, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x37, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x08, 0x01, 0, 1, 0x04, 0x04), ENTRANCE(0x08, 0x01, 0, 1, 0x04, 0x04), + ENTRANCE(0x08, 0x01, 0, 1, 0x04, 0x04), ENTRANCE(0x08, 0x01, 0, 1, 0x04, 0x04), + ENTRANCE(0x4C, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x4C, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x4C, 0x01, 0, 1, 0x04, 0x20), ENTRANCE(0x4C, 0x01, 0, 1, 0x04, 0x20), + ENTRANCE(0x63, 0x0A, 0, 1, 0x04, 0x02), ENTRANCE(0x63, 0x0A, 0, 1, 0x04, 0x02), + ENTRANCE(0x63, 0x0A, 0, 1, 0x04, 0x02), ENTRANCE(0x63, 0x0A, 0, 1, 0x04, 0x02), + ENTRANCE(0x09, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x09, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x09, 0x01, 0, 1, 0x02, 0x02), ENTRANCE(0x09, 0x01, 0, 1, 0x02, 0x02), + ENTRANCE(0x52, 0x0F, 0, 1, 0x03, 0x03), ENTRANCE(0x52, 0x0F, 0, 1, 0x02, 0x02), + ENTRANCE(0x52, 0x0F, 0, 1, 0x03, 0x03), ENTRANCE(0x52, 0x0F, 0, 1, 0x02, 0x02), + ENTRANCE(0x5B, 0x09, 0, 0, 0x02, 0x02), ENTRANCE(0x5B, 0x09, 0, 0, 0x02, 0x02), + ENTRANCE(0x5B, 0x09, 0, 0, 0x02, 0x02), ENTRANCE(0x5B, 0x09, 0, 0, 0x02, 0x02), + ENTRANCE(0x4C, 0x02, 0, 1, 0x04, 0x20), ENTRANCE(0x4C, 0x02, 0, 1, 0x04, 0x20), + ENTRANCE(0x4C, 0x02, 0, 1, 0x04, 0x20), ENTRANCE(0x4C, 0x02, 0, 1, 0x04, 0x20), + ENTRANCE(0x55, 0x0C, 0, 1, 0x04, 0x02), ENTRANCE(0x55, 0x0C, 0, 1, 0x04, 0x02), + ENTRANCE(0x55, 0x0C, 0, 1, 0x04, 0x02), ENTRANCE(0x55, 0x0C, 0, 1, 0x04, 0x02), + ENTRANCE(0x17, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x17, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x17, 0x02, 1, 1, 0x02, 0x02), ENTRANCE(0x17, 0x02, 1, 1, 0x02, 0x02), + ENTRANCE(0x4A, 0x01, 0, 0, 0x03, 0x03), ENTRANCE(0x4A, 0x01, 0, 0, 0x02, 0x02), + ENTRANCE(0x4A, 0x01, 0, 0, 0x03, 0x03), ENTRANCE(0x4A, 0x01, 0, 0, 0x02, 0x02), + ENTRANCE(0x43, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x43, 0x07, 0, 1, 0x02, 0x02), ENTRANCE(0x43, 0x07, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x12, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x12, 0, 1, 0x02, 0x02), + ENTRANCE(0x5D, 0x12, 0, 1, 0x02, 0x02), ENTRANCE(0x5D, 0x12, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0D, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0D, 0, 1, 0x02, 0x02), + ENTRANCE(0x3E, 0x0D, 0, 1, 0x02, 0x02), ENTRANCE(0x3E, 0x0D, 0, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x02, 0, 1, 0x12, 0x12), ENTRANCE(0x56, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x02, 0, 1, 0x12, 0x12), ENTRANCE(0x56, 0x02, 0, 1, 0x02, 0x02), + ENTRANCE(0x57, 0x08, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x57, 0x08, 0, 1, 0x02, 0x02), ENTRANCE(0x57, 0x08, 0, 1, 0x02, 0x02), + ENTRANCE(0x56, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x56, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x56, 0x03, 0, 1, 0x03, 0x03), ENTRANCE(0x56, 0x03, 0, 1, 0x03, 0x03), + ENTRANCE(0x57, 0x09, 0, 1, 0x03, 0x03), ENTRANCE(0x57, 0x09, 0, 1, 0x03, 0x03), + ENTRANCE(0x57, 0x09, 0, 1, 0x03, 0x03), ENTRANCE(0x57, 0x09, 0, 1, 0x03, 0x03), + ENTRANCE(0x5C, 0x08, 0, 1, 0x03, 0x03), ENTRANCE(0x5C, 0x08, 0, 1, 0x03, 0x03), + ENTRANCE(0x5C, 0x08, 0, 1, 0x03, 0x03), ENTRANCE(0x5C, 0x08, 0, 1, 0x03, 0x03), +}; + +//#define TITLED_SCENE(name, title, unk_10, config, unk_12) \ +// { \ +// (u32) _##name##SegmentRomStart, (u32)_##name##SegmentRomEnd, (u32)_##title##SegmentRomStart, \ +// (u32)_##title##SegmentRomEnd, unk_10, config, unk_12, 0 \ +// } + +#define TITLED_SCENE(name, title, unk_10, config, unk_12) \ + { \ + {0, 0, #name}, {0, 0, #title}, unk_10, config, unk_12, 0 \ + } + +//#define UNTITLED_SCENE(name, unk_10, config, unk_12) \ + //{ (u32) _##name##SegmentRomStart, (u32)_##name##SegmentRomEnd, 0, 0, unk_10, config, unk_12, 0 } + +#define UNTITLED_SCENE(name, unk_10, config, unk_12) \ + { { 0, 0, #name }, (u32)0, 0, 0, unk_10, config, unk_12, 0 } + +SceneTableEntry gSceneTable[] = { + TITLED_SCENE(ydan_scene, g_pn_06, 1, 19, 2), + TITLED_SCENE(ddan_scene, g_pn_08, 1, 20, 3), + TITLED_SCENE(bdan_scene, g_pn_07, 1, 21, 4), + TITLED_SCENE(Bmori1_scene, g_pn_01, 2, 22, 5), + TITLED_SCENE(HIDAN_scene, g_pn_03, 2, 18, 6), + TITLED_SCENE(MIZUsin_scene, g_pn_04, 1, 23, 7), + TITLED_SCENE(jyasinzou_scene, g_pn_05, 1, 25, 8), + TITLED_SCENE(HAKAdan_scene, g_pn_02, 2, 24, 9), + TITLED_SCENE(HAKAdanCH_scene, g_pn_54, 2, 24, 10), + TITLED_SCENE(ice_doukutu_scene, g_pn_10, 0, 37, 0), + UNTITLED_SCENE(ganon_scene, 2, 0, 0), + TITLED_SCENE(men_scene, g_pn_11, 0, 27, 0), + TITLED_SCENE(gerudoway_scene, g_pn_49, 0, 40, 0), + TITLED_SCENE(ganontika_scene, g_pn_09, 0, 26, 0), + UNTITLED_SCENE(ganon_sonogo_scene, 0, 51, 0), + UNTITLED_SCENE(ganontikasonogo_scene, 0, 52, 0), + TITLED_SCENE(takaraya_scene, g_pn_51, 0, 0, 0), + UNTITLED_SCENE(ydan_boss_scene, 0, 28, 0), + UNTITLED_SCENE(ddan_boss_scene, 0, 0, 0), + UNTITLED_SCENE(bdan_boss_scene, 0, 21, 0), + UNTITLED_SCENE(moribossroom_scene, 1, 0, 0), + UNTITLED_SCENE(FIRE_bs_scene, 0, 18, 0), + UNTITLED_SCENE(MIZUsin_bs_scene, 0, 29, 0), + UNTITLED_SCENE(jyasinboss_scene, 0, 0, 0), + UNTITLED_SCENE(HAKAdan_bs_scene, 0, 24, 0), + UNTITLED_SCENE(ganon_boss_scene, 0, 0, 0), + UNTITLED_SCENE(ganon_final_scene, 0, 38, 0), + UNTITLED_SCENE(entra_scene, 0, 0, 0), + UNTITLED_SCENE(entra_n_scene, 0, 0, 0), + UNTITLED_SCENE(enrui_scene, 0, 0, 0), + TITLED_SCENE(market_alley_scene, g_pn_18, 0, 0, 0), + TITLED_SCENE(market_alley_n_scene, g_pn_18, 0, 0, 0), + TITLED_SCENE(market_day_scene, g_pn_17, 0, 0, 0), + TITLED_SCENE(market_night_scene, g_pn_17, 0, 0, 0), + TITLED_SCENE(market_ruins_scene, g_pn_17, 0, 0, 0), + UNTITLED_SCENE(shrine_scene, 0, 0, 0), + UNTITLED_SCENE(shrine_n_scene, 0, 0, 0), + UNTITLED_SCENE(shrine_r_scene, 0, 0, 0), + UNTITLED_SCENE(kokiri_home_scene, 0, 0, 0), + UNTITLED_SCENE(kokiri_home3_scene, 0, 0, 0), + UNTITLED_SCENE(kokiri_home4_scene, 0, 0, 0), + UNTITLED_SCENE(kokiri_home5_scene, 0, 0, 0), + UNTITLED_SCENE(kakariko_scene, 0, 0, 0), + UNTITLED_SCENE(kakariko3_scene, 0, 0, 0), + TITLED_SCENE(shop1_scene, g_pn_23, 0, 0, 0), + TITLED_SCENE(kokiri_shop_scene, g_pn_19, 0, 0, 0), + TITLED_SCENE(golon_scene, g_pn_20, 0, 0, 0), + TITLED_SCENE(zoora_scene, g_pn_21, 0, 0, 0), + TITLED_SCENE(drag_scene, g_pn_24, 0, 0, 0), + TITLED_SCENE(alley_shop_scene, g_pn_24, 0, 0, 0), + TITLED_SCENE(night_shop_scene, g_pn_56, 0, 0, 0), + TITLED_SCENE(face_shop_scene, g_pn_50, 0, 0, 0), + UNTITLED_SCENE(link_home_scene, 0, 0, 0), + UNTITLED_SCENE(impa_scene, 0, 0, 0), + TITLED_SCENE(malon_stable_scene, g_pn_48, 0, 0, 0), + UNTITLED_SCENE(labo_scene, 0, 0, 0), + TITLED_SCENE(hylia_labo_scene, g_pn_26, 0, 43, 0), + UNTITLED_SCENE(tent_scene, 0, 0, 0), + TITLED_SCENE(hut_scene, g_pn_25, 0, 0, 0), + TITLED_SCENE(daiyousei_izumi_scene, g_pn_13, 0, 33, 0), + TITLED_SCENE(yousei_izumi_tate_scene, g_pn_45, 0, 39, 0), + TITLED_SCENE(yousei_izumi_yoko_scene, g_pn_13, 0, 33, 0), + UNTITLED_SCENE(kakusiana_scene, 0, 31, 0), + UNTITLED_SCENE(hakaana_scene, 0, 48, 0), + UNTITLED_SCENE(hakaana2_scene, 0, 39, 0), + TITLED_SCENE(hakaana_ouke_scene, g_pn_44, 0, 42, 0), + TITLED_SCENE(syatekijyou_scene, g_pn_15, 0, 34, 0), + TITLED_SCENE(tokinoma_scene, g_pn_16, 0, 30, 0), + TITLED_SCENE(kenjyanoma_scene, g_pn_14, 0, 32, 0), + TITLED_SCENE(hairal_niwa_scene, g_pn_12, 0, 35, 0), + TITLED_SCENE(hairal_niwa_n_scene, g_pn_12, 0, 35, 0), + UNTITLED_SCENE(hiral_demo_scene, 0, 0, 0), + TITLED_SCENE(hakasitarelay_scene, g_pn_57, 0, 48, 0), + TITLED_SCENE(turibori_scene, g_pn_46, 0, 50, 0), + TITLED_SCENE(nakaniwa_scene, g_pn_12, 0, 47, 0), + TITLED_SCENE(bowling_scene, g_pn_47, 0, 41, 0), + UNTITLED_SCENE(souko_scene, 0, 44, 0), + UNTITLED_SCENE(miharigoya_scene, 0, 45, 0), + TITLED_SCENE(mahouya_scene, g_pn_24, 0, 46, 0), + UNTITLED_SCENE(ganon_demo_scene, 0, 36, 0), + TITLED_SCENE(kinsuta_scene, g_pn_22, 0, 0, 0), + TITLED_SCENE(spot00_scene, g_pn_27, 0, 1, 0), + TITLED_SCENE(spot01_scene, g_pn_28, 0, 2, 0), + TITLED_SCENE(spot02_scene, g_pn_29, 0, 0, 0), + TITLED_SCENE(spot03_scene, g_pn_30, 0, 3, 0), + TITLED_SCENE(spot04_scene, g_pn_31, 0, 4, 0), + TITLED_SCENE(spot05_scene, g_pn_52, 0, 47, 0), + TITLED_SCENE(spot06_scene, g_pn_32, 0, 5, 0), + TITLED_SCENE(spot07_scene, g_pn_33, 0, 6, 0), + TITLED_SCENE(spot08_scene, g_pn_34, 0, 7, 0), + TITLED_SCENE(spot09_scene, g_pn_35, 0, 8, 0), + TITLED_SCENE(spot10_scene, g_pn_36, 0, 9, 0), + TITLED_SCENE(spot11_scene, g_pn_55, 0, 10, 0), + TITLED_SCENE(spot12_scene, g_pn_53, 0, 11, 0), + TITLED_SCENE(spot13_scene, g_pn_37, 0, 12, 0), + TITLED_SCENE(spot15_scene, g_pn_38, 0, 13, 0), + TITLED_SCENE(spot16_scene, g_pn_39, 0, 14, 0), + TITLED_SCENE(spot17_scene, g_pn_40, 0, 15, 0), + TITLED_SCENE(spot18_scene, g_pn_41, 0, 16, 0), + TITLED_SCENE(spot20_scene, g_pn_42, 0, 17, 0), + TITLED_SCENE(ganon_tou_scene, g_pn_43, 0, 36, 0), + UNTITLED_SCENE(test01_scene, 0, 47, 0), + UNTITLED_SCENE(besitu_scene, 0, 49, 0), + UNTITLED_SCENE(depth_test_scene, 0, 0, 0), + UNTITLED_SCENE(syotes_scene, 0, 0, 0), + UNTITLED_SCENE(syotes2_scene, 0, 0, 0), + UNTITLED_SCENE(sutaru_scene, 0, 0, 0), + TITLED_SCENE(hairal_niwa2_scene, g_pn_12, 0, 35, 0), + UNTITLED_SCENE(sasatest_scene, 0, 0, 0), + UNTITLED_SCENE(testroom_scene, 0, 0, 0), +}; + +Gfx sDefaultDisplayList[] = { + gsSPSegment(0x08, gEmptyDL), + gsSPSegment(0x09, gEmptyDL), + gsSPSegment(0x0A, gEmptyDL), + gsSPSegment(0x0B, gEmptyDL), + gsSPSegment(0x0C, gEmptyDL), + gsSPSegment(0x0D, gEmptyDL), + gsDPPipeSync(), + gsDPSetPrimColor(0, 0, 128, 128, 128, 128), + gsDPSetEnvColor(128, 128, 128, 128), + gsSPEndDisplayList(), +}; + +// Computes next entrance index based on age and day time to set the fade out transition +void func_800994A0(GlobalContext* globalCtx) { + s16 computedEntranceIndex; + + if (!IS_DAY) { + if (!LINK_IS_ADULT) { + computedEntranceIndex = globalCtx->nextEntranceIndex + 1; + } else { + computedEntranceIndex = globalCtx->nextEntranceIndex + 3; + } + } else { + if (!LINK_IS_ADULT) { + computedEntranceIndex = globalCtx->nextEntranceIndex; + } else { + computedEntranceIndex = globalCtx->nextEntranceIndex + 2; + } + } + + globalCtx->fadeTransition = gEntranceTable[computedEntranceIndex].field & 0x7F; // Fade out +} + +// Scene Draw Config 0 +void func_80099550(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4725); + + gSPDisplayList(POLY_OPA_DISP++, sDefaultDisplayList); + gSPDisplayList(POLY_XLU_DISP++, sDefaultDisplayList); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4735); +} + +void* D_8012A2F8[] = { + gYdanTex_00BA18, + gYdanTex_00CA18, +}; + +// Scene Draw Config 19 +void func_800995DC(GlobalContext* globalCtx) { + u32 gameplayFrames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4763); + + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - (gameplayFrames % 128), (gameplayFrames * 1) % 128, + 32, 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + { s32 pad; } + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_8012A2F8[gSaveContext.nightFlag])); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4783); +} + +// Scene Draw Config 28 +void func_80099760(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4845); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 2) % 256, 0, 64, 32, 1, 0, + (gameplayFrames * 2) % 128, 64, 32)); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4859); +} + +void* gDCEntranceTextures[] = { + gDCDayEntranceTex, + gDCNightEntranceTex, +}; +void* sDCLavaFloorTextures[] = { + gDCLavaFloor1Tex, gDCLavaFloor2Tex, gDCLavaFloor3Tex, gDCLavaFloor4Tex, + gDCLavaFloor5Tex, gDCLavaFloor6Tex, gDCLavaFloor7Tex, gDCLavaFloor8Tex, +}; + +// Scene Draw Config 20 - Dodongo's Cavern +void func_80099878(GlobalContext* globalCtx) { + u32 gameplayFrames; + s32 pad; + Gfx* displayListHead = Graph_Alloc(globalCtx->state.gfxCtx, 2 * sizeof(Gfx[3])); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4905); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDCEntranceTextures[gSaveContext.nightFlag])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sDCLavaFloorTextures[(s32)(gameplayFrames & 14) >> 1])); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 256, 0, 64, 32, 1, 0, + (gameplayFrames * 1) % 128, 64, 32)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 128, 32, 32, 1, 0, + (gameplayFrames * 2) % 128, 32, 32)); + + { s32 pad2[2]; } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + gSPSegment(POLY_OPA_DISP++, 0x0B, displayListHead); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 255, 255, 255, globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_LEFT]); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_OPA_DISP++, 0x0C, displayListHead); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 255, 255, 255, globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_RIGHT]); + gSPEndDisplayList(displayListHead); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 4956); +} + +// Scene Draw Config 30 +void func_80099BD8(GlobalContext* globalCtx) { + f32 temp; + Gfx* displayListHead = Graph_Alloc(globalCtx->state.gfxCtx, 18 * sizeof(Gfx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5069); + + temp = globalCtx->roomCtx.unk_74[0] / 255.0f; + + gSPSegment(POLY_XLU_DISP++, 0x08, displayListHead); + gSPSegment(POLY_OPA_DISP++, 0x08, displayListHead); + gDPSetPrimColor(displayListHead++, 0, 0, 255 - (u8)(185.0f * temp), 255 - (u8)(145.0f * temp), + 255 - (u8)(105.0f * temp), 255); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_XLU_DISP++, 0x09, displayListHead); + gSPSegment(POLY_OPA_DISP++, 0x09, displayListHead); + gDPSetPrimColor(displayListHead++, 0, 0, 76 + (u8)(6.0f * temp), 76 + (u8)(34.0f * temp), 76 + (u8)(74.0f * temp), + 255); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_OPA_DISP++, 0x0A, displayListHead); + gSPSegment(POLY_XLU_DISP++, 0x0A, displayListHead); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 0, 0, 0, globalCtx->roomCtx.unk_74[0]); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_OPA_DISP++, 0x0B, displayListHead); + gSPSegment(POLY_XLU_DISP++, 0x0B, displayListHead); + gDPSetPrimColor(displayListHead++, 0, 0, 89 + (u8)(166.0f * temp), 89 + (u8)(166.0f * temp), + 89 + (u8)(166.0f * temp), 255); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 0, 0, 0, globalCtx->roomCtx.unk_74[0]); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_OPA_DISP++, 0x0C, displayListHead); + gSPSegment(POLY_XLU_DISP++, 0x0C, displayListHead); + gDPSetPrimColor(displayListHead++, 0, 0, 255 + (u8)(179.0f * temp), 255 + (u8)(179.0f * temp), + 255 + (u8)(179.0f * temp), 255); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 0, 0, 0, globalCtx->roomCtx.unk_74[0]); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_OPA_DISP++, 0x0D, displayListHead); + gSPSegment(POLY_XLU_DISP++, 0x0D, displayListHead); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 0, 0, 0, globalCtx->roomCtx.unk_74[1]); + gSPEndDisplayList(displayListHead); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5145); + + if (gSaveContext.sceneSetupIndex == 5) { + gCustomLensFlareOn = true; + gCustomLensFlarePos.x = -20.0f; + gCustomLensFlarePos.y = 1220.0f; + gCustomLensFlarePos.z = -684.0f; + gLensFlareScale = 10; + gLensFlareColorIntensity = 8.0f; + gLensFlareScreenFillAlpha = 200; + } +} + +// Scene Draw Config 31 +void func_8009A45C(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5171); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 64, 256, 16)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - (gameplayFrames % 128), (gameplayFrames * 1) % 128, + 32, 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment( + POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, 0, 127 - (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_OPA_DISP++, 0x0B, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0C, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 50) % 2048, 8, 512, 1, 0, + (gameplayFrames * 60) % 2048, 8, 512)); + gSPSegment(POLY_OPA_DISP++, 0x0D, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5212); +} + +// Scene Draw Config 32 +void func_8009A798(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5226); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 2) % 256, 64, 64)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - (gameplayFrames * 1) % 128, + (gameplayFrames * 1) % 256, 32, 64, 1, 0, 0, 32, 128)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5264); +} + +// Scene Draw Config 33 +void func_8009A9DC(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5278); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 256, 32, + 64, 1, gameplayFrames % 128, (gameplayFrames * 3) % 256, 32, 64)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5301); +} + +// Scene Draw Config 48 +void func_8009AB98(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5317); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames % 64, 256, 16)); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5330); +} + +// Scene Draw Config 39 +void func_8009ACA8(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5346); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames % 64, 256, 16)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5367); +} + +// Scene Draw Config 24 +void func_8009AE30(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5384); + + gameplayFrames = globalCtx->gameplayFrames; + + if (globalCtx->sceneNum == SCENE_HAKADAN_BS) { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 2) % 128, 0, 32, 32, 1, + (gameplayFrames * 2) % 128, 0, 32, 32)); + } else { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 2) % 128, 0, 32, 32, 1, + (gameplayFrames * 2) % 128, 0, 32, 32)); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5416); +} + +void* sThievesHideoutEntranceTextures[] = { + gThievesHideoutDayEntranceTex, + gThievesHideoutNightEntranceTex, +}; + +// Scene Draw Config 40 +void func_8009AFE0(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5490); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x09, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 3) % 128, 32, 32)); + + { s32 pad[2]; } + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sThievesHideoutEntranceTextures[gSaveContext.nightFlag])); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5507); +} + +void* D_8012A330[] = { + gWaterTempleDayEntranceTex, + gWaterTempleNightEntranceTex, +}; + +// Scene Draw Config 23 (Water Temple) +void func_8009B0FC(GlobalContext* globalCtx) { + u32 gameplayFrames; + s32 spB0; + s32 spAC; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5535); + + if (1) {} // Necessary to match + + spB0 = (globalCtx->roomCtx.unk_74[1] >> 8) & 0xFF; + spAC = globalCtx->roomCtx.unk_74[1] & 0xFF; + gameplayFrames = globalCtx->gameplayFrames; + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_8012A330[gSaveContext.nightFlag])); + + if (spB0 == 1) { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, spAC)); + } else if (spB0 < 1) { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 255)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 160)); + } + + if (spB0 == 2) { + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, spAC)); + } else if (spB0 < 2) { + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 255)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 160)); + } + + if (spB0 != 0) { + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 160)); + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 3, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 180)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 128, 0, 32, 32, 1, 0, 0, + 32, 32, 0, 0, 0, 160 + (s32)((spAC / 200.0f) * 95.0f))); + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 3, 0, 32, 32, 1, 0, 0, 32, 32, + 0, 0, 0, 185 + (s32)((spAC / 200.0f) * 70.0f))); + } + + gSPSegment(POLY_XLU_DISP++, 0x0C, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, gameplayFrames * 1, 32, 32, 1, + 0, 127 - (gameplayFrames * 1), 32, 32, 0, 0, 0, 128)); + gSPSegment(POLY_XLU_DISP++, 0x0D, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames * 4, 0, 32, 32, 1, + gameplayFrames * 4, 0, 32, 32, 0, 0, 0, 128)); + + { s32 pad[2]; } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5644); +} + +// Scene Draw Config 29 +void func_8009B86C(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5791); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, 0, 32, 32, 1, 0, 0, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, globalCtx->roomCtx.unk_74[0]); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 145); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5808); +} + +// Scene Draw Config 34 +void func_8009B9BC(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5822); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames % 64, 4, 16)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5836); +} + +// Scene Draw Config 35 +void func_8009BAA4(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5850); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, 32)); + + if (globalCtx->sceneNum == SCENE_HAIRAL_NIWA) { + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 10) % 256, 32, 64)); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5876); +} + +// Scene Draw Config 36 +void func_8009BC44(GlobalContext* globalCtx) { + u32 gameplayFrames; + s8 sp83; + + if (1) {} // Necessary to match + + sp83 = coss((globalCtx->gameplayFrames * 1500) & 0xFFFF) >> 8; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5894); + + gameplayFrames = globalCtx->gameplayFrames; + + if (globalCtx->sceneNum == SCENE_GANON_TOU) { + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 256, 64, 64)); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 255 - (gameplayFrames * 1) % 256, 64, 64, 1, 0, + (gameplayFrames * 1) % 256, 64, 64)); + } + + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 255 - (gameplayFrames * 1) % 128, + (gameplayFrames * 1) % 128, 32, 32, 1, (gameplayFrames * 1) % 128, + (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + sp83 = (sp83 >> 1) + 192; + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, sp83, sp83, sp83, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5930); +} + +// Screen Shake for Ganon's Tower Collapse +void func_8009BEEC(GlobalContext* globalCtx) { + s32 var; + + if (globalCtx->gameplayFrames % 128 == 13) { + var = Quake_Add(GET_ACTIVE_CAM(globalCtx), 2); + Quake_SetSpeed(var, 10000); + Quake_SetQuakeValues(var, 4, 0, 0, 0); + Quake_SetCountdown(var, 127); + } + + if ((globalCtx->gameplayFrames % 64 == 0) && (Rand_ZeroOne() > 0.6f)) { + var = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(var, 32000.0f + (Rand_ZeroOne() * 3000.0f)); + Quake_SetQuakeValues(var, 10.0f - (Rand_ZeroOne() * 9.0f), 0, 0, 0); + Quake_SetCountdown(var, 48.0f - (Rand_ZeroOne() * 15.0f)); + } +} + +// Scene Draw Config 38 +void func_8009C0AC(GlobalContext* globalCtx) { + u32 gameplayFrames; + s8 sp7B; + + if (1) {} // Necessary to match + + sp7B = coss((globalCtx->gameplayFrames * 1500) & 0xFFFF) >> 8; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 5968); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 512, 64, 128, 1, 0, + 511 - (gameplayFrames * 1) % 512, 64, 128)); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 256, 32, 64, 1, 0, + 255 - (gameplayFrames * 1) % 256, 32, 64)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 20) % 2048, 16, 512, 1, 0, + (gameplayFrames * 30) % 2048, 16, 512)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + sp7B = (sp7B >> 1) + 192; + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, sp7B, sp7B, sp7B, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6004); + + if (Flags_GetSwitch(globalCtx, 0x37)) { + if ((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO)) { + func_8009BEEC(globalCtx); + } + } +} + +void* sIceCavernEntranceTextures[] = { + gIceCavernDayEntranceTex, + gIceCavernNightEntranceTex, +}; + +// Scene Draw Config 37 +void func_8009C3EC(GlobalContext* globalCtx) { + u32 gameplayFrames; + + if (0) {} // Necessary to match + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6042); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sIceCavernEntranceTextures[gSaveContext.nightFlag])); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + { s32 pad[2]; } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6076); +} + +// Scene Draw Config 42 +void func_8009C608(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6151); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 64, 256, 16)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 60) % 2048, 8, 512, 1, 0, + (gameplayFrames * 50) % 2048, 8, 512)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - (gameplayFrames * 1) % 128, 0, 32, 32, 1, + (gameplayFrames * 1) % 128, 0, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0B, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 1023 - (gameplayFrames * 6) % 1024, 16, 256, 1, 0, + 1023 - (gameplayFrames * 3) % 1024, 16, 256)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6187); +} + +// Scene Draw Config 43 +void func_8009C8B8(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6201); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, 0, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, 255 - (gameplayFrames * 10) % 256, 32, 64)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6232); +} + +// Scene Draw Config 47 +void func_8009CAC0(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6249); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6264); +} + +void* sGTGEntranceTextures[] = { + gGTGDayEntranceTex, + gGTGNightEntranceTex, +}; + +// Scene Draw Config 27 +void func_8009CC00(GlobalContext* globalCtx) { + u32 gameplayFrames; + + if (0) {} // Necessary to match + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6290); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sGTGEntranceTextures[gSaveContext.nightFlag])); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + { s32 pad[2]; } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6320); +} + +Gfx* Gfx_TwoTexScrollPrimColor(GraphicsContext* gfxCtx, s32 tile1, u32 x1, u32 y1, s32 width1, s32 height1, s32 tile2, + u32 x2, u32 y2, s32 width2, s32 height2, s32 r, s32 g, s32 b, s32 a) { + Gfx* displayList = Graph_Alloc(gfxCtx, 10 * sizeof(Gfx)); + + x1 %= 2048; + y1 %= 2048; + x2 %= 2048; + y2 %= 2048; + + gDPTileSync(displayList); + gDPSetTileSize(displayList + 1, tile1, x1, y1, (x1 + ((width1 - 1) << 2)), (y1 + ((height1 - 1) << 2))); + gDPTileSync(displayList + 2); + gDPSetTileSize(displayList + 3, tile2, x2, y2, (x2 + ((width2 - 1) << 2)), (y2 + ((height2 - 1) << 2))); + gDPSetPrimColor(displayList + 4, 0, 0, r, g, b, a); + gSPEndDisplayList(displayList + 5); + + return displayList; +} + +// Scene Draw Config 50 +void func_8009CF84(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6433); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScrollPrimColor(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, + (gameplayFrames * 1) % 128, 32, 32, 1, gameplayFrames % 128, + (gameplayFrames * 1) % 128, 32, 32, 255, 255, 255, + globalCtx->roomCtx.unk_74[0] + 127)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6449); +} + +// Scene Draw Config 41 +void func_8009D0E8(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6463); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, 127 - (gameplayFrames * 4) % 128, 0, 32, 32)); + gSPSegment(POLY_OPA_DISP++, 0x09, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 5) % 64, 16, 16)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, 63 - (gameplayFrames * 2) % 64, 16, 16)); + gSPSegment( + POLY_XLU_DISP++, 0x0B, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 127 - (gameplayFrames * 3) % 128, 32, 32, 1, 0, 0, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6491); +} + +void* sLonLonHouseEntranceTextures[] = { + gLonLonHouseDayEntranceTex, + gLonLonHouseNightEntranceTex, +}; + +// Scene Draw Config 44 +void func_8009D31C(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6515); + + { s32 pad[2]; } + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sLonLonHouseEntranceTextures[gSaveContext.nightFlag])); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6528); +} + +void* sGuardHouseView2Textures[] = { + gGuardHouseOutSideView1DayTex, + gGuardHouseOutSideView1NightTex, +}; +void* sGuardHouseView1Textures[] = { + gGuardHouseOutSideView2DayTex, + gGuardHouseOutSideView2NightTex, +}; + +// Scene Draw Config 45 +void func_8009D438(GlobalContext* globalCtx) { + s32 var; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6560); + + if (LINK_IS_ADULT) { + var = 1; + } else { + var = gSaveContext.nightFlag; + } + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sGuardHouseView1Textures[var])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sGuardHouseView2Textures[var])); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6581); +} + +// Scene Draw Config 46 +void func_8009D5B4(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6595); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 3) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 1023 - (gameplayFrames * 3) % 1024, 16, 256, 1, 0, + 1023 - (gameplayFrames * 6) % 1024, 16, 256)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6615); +} + +void* sForestTempleEntranceTextures[] = { + gForestTempleDayEntranceTex, + gForestTempleNightEntranceTex, +}; + +// Scene Draw Config 22 +void func_8009D758(GlobalContext* globalCtx) { + u32 gameplayFrames; + + if (0) {} // Necessary to match + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6640); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sForestTempleEntranceTextures[gSaveContext.nightFlag])); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + { s32 pad[2]; } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6671); +} + +void* sSpiritTempleEntranceTextures[] = { + gSpiritTempleDayEntranceTex, + gSpiritTempleNightEntranceTex, +}; + +// Scene Draw Config 25 +void func_8009D974(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6752); + + { s32 pad[2]; } + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sSpiritTempleEntranceTextures[gSaveContext.nightFlag])); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6762); +} + +// Scene Draw Config 1 +void func_8009DA30(GlobalContext* globalCtx) { + u32 gameplayFrames; + Gfx* displayListHead; + + displayListHead = Graph_Alloc(globalCtx->state.gfxCtx, 3 * sizeof(Gfx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6814); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 10) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 10) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + gSPSegment(POLY_XLU_DISP++, 0x0A, displayListHead); + + if ((gSaveContext.dayTime >= 0x4AAC) && (gSaveContext.dayTime <= 0xC555)) { + gSPEndDisplayList(displayListHead); + } else { + if (gSaveContext.dayTime > 0xC555) { + if (globalCtx->roomCtx.unk_74[0] != 255) { + Math_StepToS(&globalCtx->roomCtx.unk_74[0], 255, 5); + } + } else if (gSaveContext.dayTime >= 0x4000) { + if (globalCtx->roomCtx.unk_74[0] != 0) { + Math_StepToS(&globalCtx->roomCtx.unk_74[0], 0, 10); + } + } + + gDPSetPrimColor(displayListHead++, 0, 0, 255, 255, 255, globalCtx->roomCtx.unk_74[0]); + gSPDisplayList(displayListHead++, spot00_room_0DL_012B20); + gSPEndDisplayList(displayListHead); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6866); +} + +void* sKakarikoWindowTextures[] = { + gKakarikoVillageDayWindowTex, + gKakarikoVillageNightWindowTex, +}; + +// Scene Draw Config 2 +void func_8009DD5C(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6890); + + { s32 pad[2]; } + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sKakarikoWindowTextures[gSaveContext.nightFlag])); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6903); +} + +// Scene Draw Config 3 +void func_8009DE78(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6917); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 6) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 6) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6948); +} + +// Scene Draw Config 4 +void func_8009E0B8(GlobalContext* globalCtx) { + u32 gameplayFrames; + u8 spA3; + u16 spA0; + Gfx* displayListHead; + + spA3 = 128; + spA0 = 500; + displayListHead = Graph_Alloc(globalCtx->state.gfxCtx, 6 * sizeof(Gfx)); + + if (1) {} + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 6965); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 10) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 10) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + if (gSaveContext.sceneSetupIndex == 4) { + spA3 = 255 - (u8)globalCtx->roomCtx.unk_74[0]; + } else if (gSaveContext.sceneSetupIndex == 6) { + spA0 = globalCtx->roomCtx.unk_74[0] + 500; + } else if (((gSaveContext.sceneSetupIndex < 4) || LINK_IS_ADULT) && (gSaveContext.eventChkInf[0] & 0x80)) { + spA0 = 2150; + } + + gSPSegment(POLY_OPA_DISP++, 0x0A, displayListHead); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 128, 128, 128, spA3); + gSPEndDisplayList(displayListHead++); + + gSPSegment(POLY_XLU_DISP++, 0x0B, displayListHead); + gSPSegment(POLY_OPA_DISP++, 0x0B, displayListHead); + gDPPipeSync(displayListHead++); + gDPSetEnvColor(displayListHead++, 128, 128, 128, spA0 * 0.1f); + gSPEndDisplayList(displayListHead); + + gSPSegment(POLY_OPA_DISP++, 0x0C, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (s16)(-globalCtx->roomCtx.unk_74[0] * 0.02f), 32, 16, 1, + 0, (s16)(-globalCtx->roomCtx.unk_74[0] * 0.02f), 32, 16)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7044); +} + +// Scene Draw Config 5 +void func_8009E54C(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7058); + + if ((gSaveContext.sceneSetupIndex > 3) || (LINK_IS_ADULT && !(gSaveContext.eventChkInf[6] & 0x200))) { + globalCtx->roomCtx.unk_74[0] = 87; + } + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, gameplayFrames, gameplayFrames, 32, 32, 1, 0, 0, 32, + 32, 0, 0, 0, globalCtx->roomCtx.unk_74[0] + 168)); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, -gameplayFrames, -gameplayFrames, 32, 32, 1, 0, 0, + 16, 64, 0, 0, 0, globalCtx->roomCtx.unk_74[0] + 168)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7097); +} + +void* sZorasDomainEntranceTextures[] = { + gZorasDomainDayEntranceTex, + gZorasDomainNightEntranceTex, +}; + +// Scene Draw Config 6 +void func_8009E730(GlobalContext* globalCtx) { + u32 gameplayFrames; + u32 var; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7123); + + gameplayFrames = globalCtx->gameplayFrames; + var = 127 - (gameplayFrames * 1) % 128; + if (LINK_IS_ADULT) { + var = 0; + } + gSPSegment(POLY_OPA_DISP++, 0x0C, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 64, 32, 1, 0, var, 64, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sZorasDomainEntranceTextures[gSaveContext.nightFlag])); + + { s32 pad[2]; } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7147); +} + +// Scene Draw Config 7 +void func_8009E8C0(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7161); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 1) % 128, 0, 32, 32, 1, 0, 0, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 255 - (gameplayFrames * 2) % 256, 64, 64, 1, 0, + 255 - (gameplayFrames * 2) % 256, 64, 64)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 128, 32, 32, 1, 0, + (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7192); +} + +// Scene Draw Config 8 +void func_8009EAD8(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7206); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 3) % 1024, 32, 256, 1, 0, + (gameplayFrames * 3) % 1024, 32, 256)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 256, 64, 64, 1, 0, + (gameplayFrames * 1) % 256, 64, 64)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 2) % 128, 32, 32, 1, 0, + (gameplayFrames * 2) % 128, 32, 32)); + gSPSegment( + POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, 0, 127 - (gameplayFrames * 3) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0C, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 128, 32, 32, 1, 0, + (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0D, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (gameplayFrames * 1) % 64, 16, 16, 1, 0, + (gameplayFrames * 1) % 64, 16, 16)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7260); +} + +// Scene Draw Config 9 +void func_8009EE44(GlobalContext* globalCtx) { + u32 gameplayFrames; + + if (0) {} // Necessary to match + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7274); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames % 128, 0, 32, 16, 1, gameplayFrames % 128, 0, + 32, 16)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, gameplayFrames % 128, 32, 32, 1, + gameplayFrames % 128, gameplayFrames % 128, 32, 32)); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + if ((globalCtx->roomCtx.unk_74[0] == 0) && (INV_CONTENT(ITEM_COJIRO) == ITEM_COJIRO)) { + if (globalCtx->roomCtx.unk_74[1] == 50) { + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_EV_CHICKEN_CRY_M); + globalCtx->roomCtx.unk_74[0] = 1; + } + globalCtx->roomCtx.unk_74[1]++; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7309); +} + +// Scene Draw Config 10 +void func_8009F074(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7323); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, 0, 127 - gameplayFrames % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7339); +} + +void* D_8012A380[] = { + gSpot12_009678Tex, + gSpot12_00DE78Tex, +}; + +// Scene Draw Config 11 +void func_8009F1B4(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7363); + + { s32 pad[2]; } + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_8012A380[gSaveContext.nightFlag])); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7371); +} + +// Scene Draw Config 12 +void func_8009F270(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7385); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, gameplayFrames % 128, 32, 32, 1, 0, gameplayFrames % 128, + 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, gameplayFrames % 128, 32, 32, 1, 0, gameplayFrames % 128, + 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7409); +} + +// Scene Draw Config 13 +void func_8009F40C(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7423); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 10) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 10) % 128, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 3) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7443); +} + +// Scene Draw Config 14 +void func_8009F5D4(GlobalContext* globalCtx) { + Gfx* displayListHead = Graph_Alloc(globalCtx->state.gfxCtx, 3 * sizeof(Gfx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7461); + + gSPSegment(POLY_XLU_DISP++, 0x08, displayListHead); + + if ((gSaveContext.dayTime >= 0x4AAC) && (gSaveContext.dayTime <= 0xC000)) { + gSPEndDisplayList(displayListHead); + } else { + if (gSaveContext.dayTime > 0xC000) { + if (globalCtx->roomCtx.unk_74[0] != 255) { + Math_StepToS(&globalCtx->roomCtx.unk_74[0], 255, 5); + } + } else if (gSaveContext.dayTime >= 0x4000) { + if (globalCtx->roomCtx.unk_74[0] != 0) { + Math_StepToS(&globalCtx->roomCtx.unk_74[0], 0, 10); + } + } + + gDPSetPrimColor(displayListHead++, 0, 0, 255, 255, 255, globalCtx->roomCtx.unk_74[0]); + gSPDisplayList(displayListHead++, spot16_room_0DL_00AA48); + gSPEndDisplayList(displayListHead); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7495); +} + +// Scene Draw Config 15 +void func_8009F7D4(GlobalContext* globalCtx) { + s8 sp6F = coss((globalCtx->gameplayFrames * 1500) & 0xFFFF) >> 8; + s8 sp6E = coss((globalCtx->gameplayFrames * 1500) & 0xFFFF) >> 8; + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7512); + + gameplayFrames = globalCtx->gameplayFrames; + sp6F = (sp6F >> 1) + 192; + sp6E = (sp6E >> 1) + 192; + + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, gameplayFrames % 128, 32, 32, 1, 0, gameplayFrames % 128, + 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, sp6F, sp6E, 255, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7530); +} + +void* sGoronCityEntranceTextures[] = { + gGoronCityDayEntranceTex, + gGoronCityNightEntranceTex, +}; + +// Scene Draw Config 16 +void func_8009F9D0(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7555); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 127 - gameplayFrames % 128, 32, 32, 1, + gameplayFrames % 128, 0, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sGoronCityEntranceTextures[gSaveContext.nightFlag])); + + { s32 pad[2]; } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7578); +} + +void* sLonLonRanchWindowTextures[] = { + gLonLonRanchDayWindowTex, + gLonLonRangeNightWindowsTex, +}; + +// Scene Draw Config 17 +void func_8009FB74(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7602); + + { s32 pad[2]; } + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sLonLonRanchWindowTextures[gSaveContext.nightFlag])); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7615); +} + +// Scene Draw Config 18 +void func_8009FC90(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7630); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 127 - gameplayFrames % 128, 32, 32, 1, + 127 - gameplayFrames % 128, 0, 32, 32)); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 3) % 128, + 127 - (gameplayFrames * 6) % 128, 32, 32, 1, (gameplayFrames * 6) % 128, + 127 - (gameplayFrames * 3) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 64); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 64); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7653); +} + +f32 D_8012A398 = 0.0f; + +void func_8009FE58(GlobalContext* globalCtx) { + static s16 D_8012A39C = 538; + static s16 D_8012A3A0 = 4272; + u32 gameplayFrames; + f32 temp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7712); + + gameplayFrames = globalCtx->gameplayFrames; + if (globalCtx->sceneNum == SCENE_BDAN) { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames % 128, (gameplayFrames * 2) % 128, 32, + 32, 1, 127 - gameplayFrames % 128, (gameplayFrames * 2) % 128, 32, 32)); + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 255 - (gameplayFrames * 4) % 256, 32, 64, 1, 0, + 255 - (gameplayFrames * 4) % 256, 32, 64)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, (127 - (gameplayFrames * 1)) % 128, + (gameplayFrames * 1) % 128, 32, 32)); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + if (FrameAdvance_IsEnabled(globalCtx) != true) { + + D_8012A39C += 1820; + D_8012A3A0 += 1820; + + temp = 0.020000001f; + + if (globalCtx->pauseCtx.state == 0) { + func_800AA76C(&globalCtx->view, ((360.00018f / 65535.0f) * (M_PI / 180.0f)) * temp * Math_CosS(D_8012A39C), + ((360.00018f / 65535.0f) * (M_PI / 180.0f)) * temp * Math_SinS(D_8012A39C), + ((360.00018f / 65535.0f) * (M_PI / 180.0f)) * temp * Math_SinS(D_8012A3A0)); + func_800AA78C(&globalCtx->view, 1.f + (0.79999995f * temp * Math_SinS(D_8012A3A0)), + 1.f + (0.39999998f * temp * Math_CosS(D_8012A3A0)), 1.f + (1 * temp * Math_CosS(D_8012A39C))); + func_800AA7AC(&globalCtx->view, 0.95f); + } + + switch (globalCtx->roomCtx.unk_74[0]) { + case 0: + break; + case 1: + if (globalCtx->roomCtx.unk_74[1] < 1200) { + globalCtx->roomCtx.unk_74[1] += 200; + } else { + globalCtx->roomCtx.unk_74[0]++; + } + break; + case 2: + if (globalCtx->roomCtx.unk_74[1] > 0) { + globalCtx->roomCtx.unk_74[1] -= 30; + } else { + globalCtx->roomCtx.unk_74[1] = 0; + globalCtx->roomCtx.unk_74[0] = 0; + } + break; + } + + if (globalCtx->pauseCtx.state == 0) { + D_8012A398 += 0.15f + (globalCtx->roomCtx.unk_74[1] * 0.001f); + } + } + + if (globalCtx->roomCtx.curRoom.num == 2) { + Matrix_Scale(1.0f, sinf(D_8012A398) * 0.8f, 1.0f, MTXMODE_NEW); + } else { + Matrix_Scale(1.005f, sinf(D_8012A398) * 0.8f, 1.005f, MTXMODE_NEW); + } + + gSPSegment(POLY_OPA_DISP++, 0x0D, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_scene_table.c", 7809)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7811); +} + +// Scene Draw Config 26 +void func_800A0334(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7825); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 512, 32, + 128, 1, gameplayFrames % 128, (gameplayFrames * 1) % 512, 32, 128)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, + 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7852); +} + +// Scene Draw Config 52 +void func_800A055C(GlobalContext* globalCtx) { + func_8009BEEC(globalCtx); +} + +// Scene Draw Config 51 +void func_800A057C(GlobalContext* globalCtx) { + func_8009BEEC(globalCtx); +} + +// Scene Draw Config 49 +void func_800A059C(GlobalContext* globalCtx) { + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7893); + + gameplayFrames = globalCtx->gameplayFrames; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, 127 - (gameplayFrames * 2) % 128, 0, 32, 64)); + gSPSegment(POLY_OPA_DISP++, 0x09, Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 2) % 512, 128, 128)); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, 128); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 7910); +} + +void (*sSceneDrawHandlers[])(GlobalContext*) = { + func_80099550, func_8009DA30, func_8009DD5C, func_8009DE78, func_8009E0B8, func_8009E54C, func_8009E730, + func_8009E8C0, func_8009EAD8, func_8009EE44, func_8009F074, func_8009F1B4, func_8009F270, func_8009F40C, + func_8009F5D4, func_8009F7D4, func_8009F9D0, func_8009FB74, func_8009FC90, func_800995DC, func_80099878, + func_8009FE58, func_8009D758, func_8009B0FC, func_8009AE30, func_8009D974, func_800A0334, func_8009CC00, + func_80099760, func_8009B86C, func_80099BD8, func_8009A45C, func_8009A798, func_8009A9DC, func_8009B9BC, + func_8009BAA4, func_8009BC44, func_8009C3EC, func_8009C0AC, func_8009ACA8, func_8009AFE0, func_8009D0E8, + func_8009C608, func_8009C8B8, func_8009D31C, func_8009D438, func_8009D5B4, func_8009CAC0, func_8009AB98, + func_800A059C, func_8009CF84, func_800A057C, func_800A055C, +}; + +void Scene_Draw(GlobalContext* globalCtx) { + if (HREG(80) == 17) { + if (HREG(95) != 17) { + HREG(95) = 17; + HREG(81) = 1; + HREG(82) = 1; + HREG(83) = 0; + HREG(84) = 0; + HREG(85) = 0; + HREG(86) = 0; + HREG(87) = 0; + HREG(88) = 0; + HREG(89) = 0; + HREG(91) = 0; + HREG(92) = 0; + HREG(93) = 0; + HREG(94) = 0; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 8104); + + if (HREG(81) == 1) { + gSPDisplayList(POLY_OPA_DISP++, sDefaultDisplayList); + gSPDisplayList(POLY_XLU_DISP++, sDefaultDisplayList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_scene_table.c", 8109); + + if (HREG(82) == 1) { + sSceneDrawHandlers[globalCtx->sceneConfig](globalCtx); + } + } else { + sSceneDrawHandlers[globalCtx->sceneConfig](globalCtx); + } +} diff --git a/soh/src/code/z_skelanime.c b/soh/src/code/z_skelanime.c new file mode 100644 index 000000000..2c03390e9 --- /dev/null +++ b/soh/src/code/z_skelanime.c @@ -0,0 +1,1920 @@ +#include "global.h" +#include "vt.h" + +#define ANIM_INTERP 1 + +s32 LinkAnimation_Loop(GlobalContext* globalCtx, SkelAnime* skelAnime); +s32 LinkAnimation_Once(GlobalContext* globalCtx, SkelAnime* skelAnime); +s32 SkelAnime_LoopFull(SkelAnime* skelAnime); +s32 SkelAnime_Once(SkelAnime* skelAnime); +s32 SkelAnime_LoopPartial(SkelAnime* skelAnime); + +void SkelAnime_CopyFrameTable(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src); + +static u32 sDisableAnimQueueFlags = 0; +static u32 sAnimQueueFlags; + +/** + * Draw a limb of type `LodLimb` + * Near or far display list is specified via `lod` + */ +void SkelAnime_DrawLimbLod(GlobalContext* globalCtx, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 lod) { + LodLimb* limb; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 773); + + Matrix_Push(); + limb = (LodLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + limbIndex++; + rot = jointTable[limbIndex]; + + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + dList = limb->dLists[lod]; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, limbIndex, &dList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_skelanime.c", 805), G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dList); + } + } + + if (1) {} + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, limbIndex, &dList, &rot, arg); + } + + if (limb->child != LIMB_DONE) { + SkelAnime_DrawLimbLod(globalCtx, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, lod); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + SkelAnime_DrawLimbLod(globalCtx, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, lod); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 821); +} + +/** + * Draw all limbs of type `LodLimb` in a given skeleton + * Near or far display list is specified via `lod` + */ +void SkelAnime_DrawLod(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 lod) { + LodLimb* rootLimb; + s32 pad; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + if (skeleton == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Si2_Lod_draw():skelがNULLです。\n"); // "skel is NULL." + osSyncPrintf(VT_RST); + return; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 849); + + Matrix_Push(); + + rootLimb = (LodLimb*)SEGMENTED_TO_VIRTUAL(skeleton[0]); + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + dList = rootLimb->dLists[lod]; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, 1, &dList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_skelanime.c", 881), G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dList); + } + } + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, 1, &dList, &rot, arg); + } + + if (rootLimb->child != LIMB_DONE) { + SkelAnime_DrawLimbLod(globalCtx, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + lod); + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 894); +} + +/** + * Draw a limb of type `LodLimb` contained within a flexible skeleton + * Near or far display list is specified via `lod` + */ +void SkelAnime_DrawFlexLimbLod(GlobalContext* globalCtx, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 lod, + Mtx** mtx) { + LodLimb* limb; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + + Matrix_Push(); + + limb = (LodLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + limbIndex++; + + rot = jointTable[limbIndex]; + + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + newDList = limbDList = limb->dLists[lod]; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, limbIndex, &newDList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + Matrix_ToMtx(*mtx, "../z_skelanime.c", 945); + { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 946); + gSPMatrix(POLY_OPA_DISP++, *mtx, G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, newDList); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 949); + } + (*mtx)++; + } else if (limbDList != NULL) { + Matrix_ToMtx(*mtx, "../z_skelanime.c", 954); + (*mtx)++; + } + } + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, limbIndex, &limbDList, &rot, arg); + } + if (limb->child != LIMB_DONE) { + SkelAnime_DrawFlexLimbLod(globalCtx, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + lod, mtx); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + SkelAnime_DrawFlexLimbLod(globalCtx, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + lod, mtx); + } +} + +/** + * Draws all limbs of type `LodLimb` in a given flexible skeleton + * Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs. + * An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up. + */ +void SkelAnime_DrawFlexLod(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, s32 lod) { + LodLimb* rootLimb; + s32 pad; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + Mtx* mtx = Graph_Alloc(globalCtx->state.gfxCtx, dListCount * sizeof(Mtx)); + + if (skeleton == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Si2_Lod_draw_SV():skelがNULLです。\n"); // "skel is NULL." + osSyncPrintf(VT_RST); + return; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1000); + + gSPSegment(POLY_OPA_DISP++, 0xD, mtx); + Matrix_Push(); + + rootLimb = (LodLimb*)SEGMENTED_TO_VIRTUAL(skeleton[0]); + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + + newDList = limbDList = rootLimb->dLists[lod]; + + if ((overrideLimbDraw == 0) || !overrideLimbDraw(globalCtx, 1, &newDList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + Matrix_ToMtx(mtx, "../z_skelanime.c", 1033); + gDPNoOpString(POLY_OPA_DISP++, "T5ST", 0); + gSPMatrix(POLY_OPA_DISP++, mtx, G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, newDList); + mtx++; + } else if (limbDList != NULL) { + Matrix_ToMtx(mtx, "../z_skelanime.c", 1040); + mtx++; + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, 1, &limbDList, &rot, arg); + } + if (rootLimb->child != LIMB_DONE) { + SkelAnime_DrawFlexLimbLod(globalCtx, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + lod, &mtx); + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1053); +} + +/** + * Draw a limb of type `StandardLimb` to the polyOpa buffer + */ +void SkelAnime_DrawLimbOpa(GlobalContext* globalCtx, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg) { + StandardLimb* limb; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1076); + Matrix_Push(); + + limb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + limbIndex++; + rot = jointTable[limbIndex]; + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + dList = limb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, limbIndex, &dList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_skelanime.c", 1103), G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dList); + } + } + + if (1) {} + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, limbIndex, &dList, &rot, arg); + } + + if (limb->child != LIMB_DONE) { + SkelAnime_DrawLimbOpa(globalCtx, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + SkelAnime_DrawLimbOpa(globalCtx, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1121); +} + +/** + * Draw all limbs of type `StandardLimb` in a given skeleton to the polyOpa buffer + */ +void SkelAnime_DrawOpa(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg) { + StandardLimb* rootLimb; + s32 pad; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + if (skeleton == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Si2_draw():skelがNULLです。\n"); // "skel is NULL." + osSyncPrintf(VT_RST); + return; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1148); + + Matrix_Push(); + rootLimb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[0]); + + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + dList = rootLimb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, 1, &dList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_skelanime.c", 1176), G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dList); + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, 1, &dList, &rot, arg); + } + + if (rootLimb->child != LIMB_DONE) { + SkelAnime_DrawLimbOpa(globalCtx, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg); + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1190); +} + +/** + * Draw a limb of type `StandardLimb` contained within a flexible skeleton to the polyOpa buffer + */ +void SkelAnime_DrawFlexLimbOpa(GlobalContext* globalCtx, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg, + Mtx** limbMatricies) { + StandardLimb* limb; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1214); + + Matrix_Push(); + + limb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + limbIndex++; + rot = jointTable[limbIndex]; + + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + newDList = limbDList = limb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, limbIndex, &newDList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + Matrix_ToMtx(*limbMatricies, "../z_skelanime.c", 1242); + gSPMatrix(POLY_OPA_DISP++, *limbMatricies, G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, newDList); + (*limbMatricies)++; + } else if (limbDList != NULL) { + Matrix_ToMtx(*limbMatricies, "../z_skelanime.c", 1249); + (*limbMatricies)++; + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, limbIndex, &limbDList, &rot, arg); + } + + if (limb->child != LIMB_DONE) { + SkelAnime_DrawFlexLimbOpa(globalCtx, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + limbMatricies); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + SkelAnime_DrawFlexLimbOpa(globalCtx, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + limbMatricies); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1265); +} + +/** + * Draw all limbs of type `StandardLimb` in a given flexible skeleton to the polyOpa buffer + * Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs. + * An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up. + */ +void SkelAnime_DrawFlexOpa(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* arg) { + StandardLimb* rootLimb; + s32 pad; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + Mtx* mtx = Graph_Alloc(globalCtx->state.gfxCtx, dListCount * sizeof(Mtx)); + + if (skeleton == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Si2_draw_SV():skelがNULLです。\n"); // "skel is NULL." + osSyncPrintf(VT_RST); + return; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1294); + + gSPSegment(POLY_OPA_DISP++, 0xD, mtx); + + Matrix_Push(); + + rootLimb = SEGMENTED_TO_VIRTUAL(skeleton[0]); + + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + + newDList = limbDList = rootLimb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, 1, &newDList, &pos, &rot, arg)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + Matrix_ToMtx(mtx, "../z_skelanime.c", 1327); + gSPMatrix(POLY_OPA_DISP++, mtx, G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, newDList); + mtx++; + } else if (limbDList != NULL) { + Matrix_ToMtx(mtx, "../z_skelanime.c", 1334); + mtx++; + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, 1, &limbDList, &rot, arg); + } + + if (rootLimb->child != LIMB_DONE) { + SkelAnime_DrawFlexLimbOpa(globalCtx, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + &mtx); + } + + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_skelanime.c", 1347); +} + +/** + * Copies frame data from the frame data table, indexed by the joint index table. + * Indices below limit are copied from that entry in the static frame data table. + * Indices above limit are offsets to a frame data array indexed by the frame. + */ +void SkelAnime_GetFrameData(AnimationHeader* animation, s32 frame, s32 limbCount, Vec3s* frameTable) { + if (ResourceMgr_OTRSigCheck(animation) != 0) + animation = ResourceMgr_LoadAnimByName(animation); + + AnimationHeader* animHeader = SEGMENTED_TO_VIRTUAL(animation); + JointIndex* jointIndices = SEGMENTED_TO_VIRTUAL(animHeader->jointIndices); + s16* frameData = SEGMENTED_TO_VIRTUAL(animHeader->frameData); + s16* staticData = &frameData[0]; + s16* dynamicData = &frameData[frame]; + u16 staticIndexMax = animHeader->staticIndexMax; + s32 i; + + for (i = 0; i < limbCount; i++, frameTable++, jointIndices++) { + if ((frameTable == NULL) || (jointIndices == NULL) || (dynamicData == NULL) || (staticData == NULL)) { + LOG_ADDRESS("out", frameTable, "../z_skelanime.c", 1392); + LOG_ADDRESS("ref_tbl", jointIndices, "../z_skelanime.c", 1393); + LOG_ADDRESS("frame_tbl", dynamicData, "../z_skelanime.c", 1394); + LOG_ADDRESS("tbl", staticData, "../z_skelanime.c", 1395); + } + + frameTable->x = + (jointIndices->x >= staticIndexMax) ? dynamicData[jointIndices->x] : staticData[jointIndices->x]; + frameTable->y = + (jointIndices->y >= staticIndexMax) ? dynamicData[jointIndices->y] : staticData[jointIndices->y]; + frameTable->z = + (jointIndices->z >= staticIndexMax) ? dynamicData[jointIndices->z] : staticData[jointIndices->z]; + } +} + +s16 Animation_GetLength(void* animation) { + if (ResourceMgr_OTRSigCheck(animation) != 0) + animation = ResourceMgr_LoadAnimByName(animation); + + AnimationHeaderCommon* common = SEGMENTED_TO_VIRTUAL(animation); + + return common->frameCount; +} + +s16 Animation_GetLastFrame(void* animation) { + if (ResourceMgr_OTRSigCheck(animation) != 0) + animation = ResourceMgr_LoadAnimByName(animation); + AnimationHeaderCommon* common = SEGMENTED_TO_VIRTUAL(animation); + // Loads an unsigned half for some reason. + return (u16)common->frameCount - 1; +} + +/** + * Draw a limb of type `StandardLimb` to the specified display buffer + */ +Gfx* SkelAnime_DrawLimb(GlobalContext* globalCtx, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, void* arg, Gfx* gfx) { + StandardLimb* limb; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + Matrix_Push(); + + limb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + limbIndex++; + + rot = jointTable[limbIndex]; + + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + dList = limb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, limbIndex, &dList, &pos, &rot, arg, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(gfx++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_skelanime.c", 1489), G_MTX_LOAD); + gSPDisplayList(gfx++, dList); + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, limbIndex, &dList, &rot, arg, &gfx); + } + + if (limb->child != LIMB_DONE) { + gfx = + SkelAnime_DrawLimb(globalCtx, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, gfx); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + gfx = SkelAnime_DrawLimb(globalCtx, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + gfx); + } + + return gfx; +} + +/** + * Draw all limbs of type `StandardLimb` in a given skeleton to the specified display buffer + */ +Gfx* SkelAnime_Draw(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, OverrideLimbDraw overrideLimbDraw, + PostLimbDraw postLimbDraw, void* arg, Gfx* gfx) { + StandardLimb* rootLimb; + s32 pad; + Gfx* dList; + Vec3f pos; + Vec3s rot; + + if (skeleton == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + // "skel is NULL. Returns NULL." + osSyncPrintf("Si2_draw2():skelがNULLです。NULLを返します。\n"); + osSyncPrintf(VT_RST); + return NULL; + } + + Matrix_Push(); + + rootLimb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[0]); + + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + + dList = rootLimb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, 1, &dList, &pos, &rot, arg, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (dList != NULL) { + gSPMatrix(gfx++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_skelanime.c", 1558), G_MTX_LOAD); + gSPDisplayList(gfx++, dList); + } + } + + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, 1, &dList, &rot, arg, &gfx); + } + + if (rootLimb->child != LIMB_DONE) { + gfx = SkelAnime_DrawLimb(globalCtx, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + gfx); + } + + Matrix_Pop(); + + return gfx; +} + +/** + * Draw a limb of type `StandardLimb` contained within a flexible skeleton to the specified display buffer + */ +Gfx* SkelAnime_DrawFlexLimb(GlobalContext* globalCtx, s32 limbIndex, void** skeleton, Vec3s* jointTable, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, void* arg, Mtx** mtx, + Gfx* gfx) { + StandardLimb* limb; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + + Matrix_Push(); + + limb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + limbIndex++; + rot = jointTable[limbIndex]; + + pos.x = limb->jointPos.x; + pos.y = limb->jointPos.y; + pos.z = limb->jointPos.z; + + newDList = limbDList = limb->dList; + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, limbIndex, &newDList, &pos, &rot, arg, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + Matrix_ToMtx(*mtx, "../z_skelanime.c", 1623); + gSPMatrix(gfx++, *mtx, G_MTX_LOAD); + gSPDisplayList(gfx++, newDList); + (*mtx)++; + } else if (limbDList != NULL) { + Matrix_ToMtx(*mtx, "../z_skelanime.c", 1630); + (*mtx)++; + } + } + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, limbIndex, &limbDList, &rot, arg, &gfx); + } + if (limb->child != LIMB_DONE) { + gfx = SkelAnime_DrawFlexLimb(globalCtx, limb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, arg, + mtx, gfx); + } + + Matrix_Pop(); + + if (limb->sibling != LIMB_DONE) { + gfx = SkelAnime_DrawFlexLimb(globalCtx, limb->sibling, skeleton, jointTable, overrideLimbDraw, postLimbDraw, + arg, mtx, gfx); + } + + return gfx; +} + +/** + * Draw all limbs of type `StandardLimb` in a given flexible skeleton to the specified display buffer + * Limbs in a flexible skeleton have meshes that can stretch to line up with other limbs. + * An array of matrices is dynamically allocated so each limb can access any transform to ensure its meshes line up. + */ +Gfx* SkelAnime_DrawFlex(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, void* arg, Gfx* gfx) { + StandardLimb* rootLimb; + s32 pad; + Gfx* newDList; + Gfx* limbDList; + Vec3f pos; + Vec3s rot; + Mtx* mtx = Graph_Alloc(globalCtx->state.gfxCtx, dListCount * sizeof(*mtx)); + + if (skeleton == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + // "skel is NULL. Returns NULL." + osSyncPrintf("Si2_draw2_SV():skelがNULLです。NULLを返します。\n"); + osSyncPrintf(VT_RST); + return NULL; + } + + gSPSegment(gfx++, 0xD, mtx); + Matrix_Push(); + rootLimb = (StandardLimb*)SEGMENTED_TO_VIRTUAL(skeleton[0]); + + pos.x = jointTable[0].x; + pos.y = jointTable[0].y; + pos.z = jointTable[0].z; + + rot = jointTable[1]; + + newDList = limbDList = rootLimb->dList; + + if ((overrideLimbDraw == NULL) || !overrideLimbDraw(globalCtx, 1, &newDList, &pos, &rot, arg, &gfx)) { + Matrix_TranslateRotateZYX(&pos, &rot); + if (newDList != NULL) { + Matrix_ToMtx(mtx, "../z_skelanime.c", 1710); + gSPMatrix(gfx++, mtx, G_MTX_LOAD); + gSPDisplayList(gfx++, newDList); + mtx++; + } else if (limbDList != NULL) { + Matrix_ToMtx(mtx, "../z_skelanime.c", 1717); + mtx++; + } + } + if (postLimbDraw != NULL) { + postLimbDraw(globalCtx, 1, &limbDList, &rot, arg, &gfx); + } + if (rootLimb->child != LIMB_DONE) { + gfx = SkelAnime_DrawFlexLimb(globalCtx, rootLimb->child, skeleton, jointTable, overrideLimbDraw, postLimbDraw, + arg, &mtx, gfx); + } + + Matrix_Pop(); + + return gfx; +} + +/** + * Unpacks frame data for the animation at the given frame into frameTable + * Used by the legacy animation format + */ +s32 SkelAnime_GetFrameDataLegacy(LegacyAnimationHeader* animation, s32 frame, Vec3s* frameTable) { + LegacyAnimationHeader* animHeader = SEGMENTED_TO_VIRTUAL(animation); + s32 limbCount = animHeader->limbCount; + JointKey* key = SEGMENTED_TO_VIRTUAL(animHeader->jointKey); + s16* frameData = SEGMENTED_TO_VIRTUAL(animHeader->frameData); + s16* staticData = &frameData[0]; + s16* dynamicData = &frameData[frame]; + s32 i; + + frameTable->x = frame < key->xMax ? dynamicData[key->x] : staticData[key->x]; + frameTable->y = frame < key->yMax ? dynamicData[key->y] : staticData[key->y]; + frameTable->z = frame < key->zMax ? dynamicData[key->z] : staticData[key->z]; + + frameTable++; + key++; + + for (i = 1; i <= limbCount; i++, key++, frameTable++) { + frameTable->x = frame < key->xMax ? dynamicData[key->x] : staticData[key->x]; + frameTable->y = frame < key->yMax ? dynamicData[key->y] : staticData[key->y]; + frameTable->z = frame < key->zMax ? dynamicData[key->z] : staticData[key->z]; + } + + return limbCount; +} + +/** + * Used by legacy animation format + */ +s16 Animation_GetLimbCountLegacy(LegacyAnimationHeader* animation) { + LegacyAnimationHeader* animHeader = SEGMENTED_TO_VIRTUAL(animation); + + return animHeader->limbCount; +} + +/** + * Used by legacy animation format + */ +s16 Animation_GetLengthLegacy(LegacyAnimationHeader* animation) { + LegacyAnimationHeader* animHeader = SEGMENTED_TO_VIRTUAL(animation); + + return animHeader->frameCount; +} + +/** + * Used by legacy animation format + */ +s16 Animation_GetLastFrameLegacy(LegacyAnimationHeader* animation) { + LegacyAnimationHeader* animHeader = SEGMENTED_TO_VIRTUAL(animation); + + return animHeader->frameCount - 1; +} + +/** + * Linearly interpolates the start and target frame tables with the given weight, putting the result in dst + */ +void SkelAnime_InterpFrameTable(s32 limbCount, Vec3s* dst, Vec3s* start, Vec3s* target, f32 weight) { + s32 i; + s16 diff; + s16 base; + + if (weight < 1.0f) { + for (i = 0; i < limbCount; i++, dst++, start++, target++) { + base = start->x; + diff = target->x - base; + dst->x = (s16)(diff * weight) + base; + base = start->y; + diff = target->y - base; + dst->y = (s16)(diff * weight) + base; + base = start->z; + diff = target->z - base; + dst->z = (s16)(diff * weight) + base; + } + } else { + for (i = 0; i < limbCount; i++, dst++, target++) { + dst->x = target->x; + dst->y = target->y; + dst->z = target->z; + } + } +} + +/** + * zeroes out the current request count + */ +void AnimationContext_Reset(AnimationContext* animationCtx) { + animationCtx->animationCount = 0; +} + +/** + * Shifts the queue flag to the next queue + */ +void AnimationContext_SetNextQueue(GlobalContext* globalCtx) { + sAnimQueueFlags <<= 1; +} + +/** + * Disables the current animation queue. Only load and move actor requests will be processed for that queue. + */ +void AnimationContext_DisableQueue(GlobalContext* globalCtx) { + sDisableAnimQueueFlags |= sAnimQueueFlags; +} + +AnimationEntry* AnimationContext_AddEntry(AnimationContext* animationCtx, AnimationType type) { + AnimationEntry* entry; + s16 index = animationCtx->animationCount; + + if (index >= ANIMATION_ENTRY_MAX) { + return NULL; + } + + animationCtx->animationCount = index + 1; + entry = &animationCtx->entries[index]; + entry->type = type; + return entry; +} + +/** + * Requests loading frame data from the Link animation into frameTable + */ +void AnimationContext_SetLoadFrame(GlobalContext* globalCtx, LinkAnimationHeader* animation, s32 frame, s32 limbCount, + Vec3s* frameTable) { + AnimationEntry* entry = AnimationContext_AddEntry(&globalCtx->animationCtx, ANIMENTRY_LOADFRAME); + + if (entry != NULL) + { + if (ResourceMgr_OTRSigCheck(animation) != 0) + animation = ResourceMgr_LoadAnimByName(animation); + + LinkAnimationHeader* linkAnimHeader = SEGMENTED_TO_VIRTUAL(animation); + u32 ram = frameTable; + + osCreateMesgQueue(&entry->data.load.msgQueue, &entry->data.load.msg, 1); + + char animPath[2048]; + + sprintf(animPath, "misc\\link_animetion\\gPlayerAnimData_%06X", (((uintptr_t)linkAnimHeader->segment - 0x07000000))); + + //printf("Streaming %s, seg = %08X\n", animPath, linkAnimHeader->segment); + + s16* animData = ResourceMgr_LoadPlayerAnimByName(animPath); + + memcpy(ram, (uintptr_t)animData + (((sizeof(Vec3s) * limbCount + 2) * frame)), sizeof(Vec3s) * limbCount + 2); + + /*u32* ramPtr = (u32*)ram; + + for (int i = 0; i < 1024; i++) + { + ramPtr[i] = i * 7; + }*/ + + + //DmaMgr_SendRequest2(&entry->data.load.req, ram, + //LINK_ANIMATION_OFFSET(linkAnimHeader->segment, ((sizeof(Vec3s) * limbCount + 2) * frame)), + //sizeof(Vec3s) * limbCount + 2, 0, &entry->data.load.msgQueue, NULL, "../z_skelanime.c", + //2004); + } +} + +/** + * Requests copying all vectors from src frame table into dst frame table + */ +void AnimationContext_SetCopyAll(GlobalContext* globalCtx, s32 vecCount, Vec3s* dst, Vec3s* src) { + AnimationEntry* entry = AnimationContext_AddEntry(&globalCtx->animationCtx, ANIMENTRY_COPYALL); + + if (entry != NULL) { + entry->data.copy.queueFlag = sAnimQueueFlags; + entry->data.copy.vecCount = vecCount; + entry->data.copy.dst = dst; + entry->data.copy.src = src; + } +} + +/** + * Requests interpolating between base and mod frame tables with the given weight, placing the result in base + */ +void AnimationContext_SetInterp(GlobalContext* globalCtx, s32 vecCount, Vec3s* base, Vec3s* mod, f32 weight) { + AnimationEntry* entry = AnimationContext_AddEntry(&globalCtx->animationCtx, ANIMENTRY_INTERP); + + if (entry != NULL) { + entry->data.interp.queueFlag = sAnimQueueFlags; + entry->data.interp.vecCount = vecCount; + entry->data.interp.base = base; + entry->data.interp.mod = mod; + entry->data.interp.weight = weight; + } +} + +/** + * Requests copying vectors from src frame table to dst frame table whose copy flag is true + */ +void AnimationContext_SetCopyTrue(GlobalContext* globalCtx, s32 vecCount, Vec3s* dst, Vec3s* src, u8* copyFlag) { + AnimationEntry* entry = AnimationContext_AddEntry(&globalCtx->animationCtx, ANIMENTRY_COPYTRUE); + + if (entry != NULL) { + entry->data.copy1.queueFlag = sAnimQueueFlags; + entry->data.copy1.vecCount = vecCount; + entry->data.copy1.dst = dst; + entry->data.copy1.src = src; + entry->data.copy1.copyFlag = copyFlag; + } +} + +/** + * Requests copying vectors from src frame table to dst frame table whose copy flag is false + */ +void AnimationContext_SetCopyFalse(GlobalContext* globalCtx, s32 vecCount, Vec3s* dst, Vec3s* src, u8* copyFlag) { + AnimationEntry* entry = AnimationContext_AddEntry(&globalCtx->animationCtx, ANIMENTRY_COPYFALSE); + + if (entry != NULL) { + entry->data.copy0.queueFlag = sAnimQueueFlags; + entry->data.copy0.vecCount = vecCount; + entry->data.copy0.dst = dst; + entry->data.copy0.src = src; + entry->data.copy0.copyFlag = copyFlag; + } +} + +/** + * Requests moving an actor according to the translation of its root limb + */ +void AnimationContext_SetMoveActor(GlobalContext* globalCtx, Actor* actor, SkelAnime* skelAnime, f32 arg3) { + AnimationEntry* entry = AnimationContext_AddEntry(&globalCtx->animationCtx, ANIMENTRY_MOVEACTOR); + + if (entry != NULL) { + entry->data.move.actor = actor; + entry->data.move.skelAnime = skelAnime; + entry->data.move.unk_08 = arg3; + } +} + +/** + * Receives the request for Link's animation frame data + */ +void AnimationContext_LoadFrame(GlobalContext* globalCtx, AnimationEntryData* data) { + AnimEntryLoadFrame* entry = &data->load; + + osRecvMesg(&entry->msgQueue, NULL, OS_MESG_BLOCK); +} + +/** + * If the entry's queue is enabled, copies all vectors from src frame table to dst frame table + */ +void AnimationContext_CopyAll(GlobalContext* globalCtx, AnimationEntryData* data) { + AnimEntryCopyAll* entry = &data->copy; + + if (!(entry->queueFlag & sDisableAnimQueueFlags)) { + Vec3s* dst = entry->dst; + Vec3s* src = entry->src; + s32 i; + + for (i = 0; i < entry->vecCount; i++) { + *dst++ = *src++; + } + } +} + +/** + * If the entry's queue is enabled, interpolates between the base and mod frame tables, placing the result in base + */ +void AnimationContext_Interp(GlobalContext* globalCtx, AnimationEntryData* data) { + AnimEntryInterp* entry = &data->interp; + + if (!(entry->queueFlag & sDisableAnimQueueFlags)) { + SkelAnime_InterpFrameTable(entry->vecCount, entry->base, entry->base, entry->mod, entry->weight); + } +} + +/** + * If the entry's queue is enabled, copies all vectors from src frame table to dst frame table whose copy flag is true + */ +void AnimationContext_CopyTrue(GlobalContext* globalCtx, AnimationEntryData* data) { + AnimEntryCopyTrue* entry = &data->copy1; + + if (!(entry->queueFlag & sDisableAnimQueueFlags)) { + Vec3s* dst = entry->dst; + Vec3s* src = entry->src; + u8* copyFlag = entry->copyFlag; + s32 i; + + for (i = 0; i < entry->vecCount; i++, dst++, src++) { + if (*copyFlag++) { + *dst = *src; + } + } + } +} + +/** + * If the entry's queue is enabled, copies all vectors from src frame table to dst frame table whose copy flag is false + */ +void AnimationContext_CopyFalse(GlobalContext* globalCtx, AnimationEntryData* data) { + AnimEntryCopyFalse* entry = &data->copy0; + + if (!(entry->queueFlag & sDisableAnimQueueFlags)) { + Vec3s* dst = entry->dst; + Vec3s* src = entry->src; + u8* copyFlag = entry->copyFlag; + s32 i; + + for (i = 0; i < entry->vecCount; i++, dst++, src++) { + if (!(*copyFlag++)) { + *dst = *src; + } + } + } +} + +/** + * Moves an actor according to the translation of its root limb + */ +void AnimationContext_MoveActor(GlobalContext* globalCtx, AnimationEntryData* data) { + AnimEntryMoveActor* entry = &data->move; + Actor* actor = entry->actor; + Vec3f diff; + + SkelAnime_UpdateTranslation(entry->skelAnime, &diff, actor->shape.rot.y); + actor->world.pos.x += diff.x * actor->scale.x; + actor->world.pos.y += diff.y * actor->scale.y * entry->unk_08; + actor->world.pos.z += diff.z * actor->scale.z; +} + +/** + * Performs all requests in the animation queue, then resets the queue flags. + */ +void AnimationContext_Update(GlobalContext* globalCtx, AnimationContext* animationCtx) { + static AnimationEntryCallback animFuncs[] = { + AnimationContext_LoadFrame, AnimationContext_CopyAll, AnimationContext_Interp, + AnimationContext_CopyTrue, AnimationContext_CopyFalse, AnimationContext_MoveActor, + }; + AnimationEntry* entry; + + for (entry = animationCtx->entries; animationCtx->animationCount != 0; entry++, animationCtx->animationCount--) { + animFuncs[entry->type](globalCtx, &entry->data); + } + + sAnimQueueFlags = 1; + sDisableAnimQueueFlags = 0; +} + +/** + * Initializes a skeleton to be used with Link animations to a looping animation, dynamically allocating the frame + * tables if not given. + */ +void SkelAnime_InitLink(GlobalContext* globalCtx, SkelAnime* skelAnime, FlexSkeletonHeader* skeletonHeaderSeg, + LinkAnimationHeader* animation, s32 flags, Vec3s* jointTable, Vec3s* morphTable, + s32 limbBufCount) { + if (ResourceMgr_OTRSigCheck(skeletonHeaderSeg) != 0) + skeletonHeaderSeg = ResourceMgr_LoadSkeletonByName(skeletonHeaderSeg); + + FlexSkeletonHeader* skeletonHeader = SEGMENTED_TO_VIRTUAL(skeletonHeaderSeg); + s32 headerJointCount = skeletonHeader->sh.limbCount; + s32 limbCount; + size_t allocSize; + + skelAnime->initFlags = flags; + limbCount = (flags & 2) ? headerJointCount : 1; + + if (flags & 1) { + limbCount += headerJointCount; + } + if (flags & 4) { + limbCount += headerJointCount; + } + + skelAnime->limbCount = limbCount; + skelAnime->dListCount = skeletonHeader->dListCount; + + allocSize = limbCount * sizeof(Vec3s); + skelAnime->skeleton = SEGMENTED_TO_VIRTUAL(skeletonHeader->sh.segment); + + if (flags & 8) { + allocSize += 2; + } + + if (jointTable == NULL) { + skelAnime->jointTable = ZeldaArena_MallocDebug(allocSize, "../z_skelanime.c", 2364); + skelAnime->morphTable = ZeldaArena_MallocDebug(allocSize, "../z_skelanime.c", 2365); + } else { + ASSERT(limbBufCount == limbCount, "joint_buff_num == joint_num", "../z_skelanime.c", 2369); + + skelAnime->jointTable = (Vec3s*)ALIGN16((uintptr_t)jointTable); + skelAnime->morphTable = (Vec3s*)ALIGN16((uintptr_t)morphTable); + } + + if ((skelAnime->jointTable == NULL) || (skelAnime->morphTable == NULL)) { + osSyncPrintf(VT_FGCOL(RED)); + // "Memory allocation error" + osSyncPrintf("Skeleton_Info_Rom_SV_ct メモリアロケーションエラー\n"); + osSyncPrintf(VT_RST); + } + + LinkAnimation_Change(globalCtx, skelAnime, animation, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, 0.0f); +} + +/** + * Sets the update function of a SkelAnime that uses Link animations based on its mode + */ +void LinkAnimation_SetUpdateFunction(SkelAnime* skelAnime) { + if (skelAnime->mode <= ANIMMODE_LOOP_INTERP) { + skelAnime->update = LinkAnimation_Loop; + } else { + skelAnime->update = LinkAnimation_Once; + } + skelAnime->morphWeight = 0.0f; +} + +/** + * Advances the current Link animation and updates all frame tables. If the animation plays once, returns true when it + * finishes. + */ +s32 LinkAnimation_Update(GlobalContext* globalCtx, SkelAnime* skelAnime) { + return skelAnime->update(globalCtx, skelAnime); +} + +/** + * Requests an interpolation between the pose in jointTable to the one in morphTable, advancing the morph but not the + * animation frame + */ +s32 LinkAnimation_Morph(GlobalContext* globalCtx, SkelAnime* skelAnime) { + f32 prevMorphWeight = skelAnime->morphWeight; + f32 updateRate = R_UPDATE_RATE * 0.5f; + + skelAnime->morphWeight -= skelAnime->morphRate * updateRate; + + if (skelAnime->morphWeight <= 0.0f) { + LinkAnimation_SetUpdateFunction(skelAnime); + } + + AnimationContext_SetInterp(globalCtx, skelAnime->limbCount, skelAnime->jointTable, skelAnime->morphTable, + 1.0f - (skelAnime->morphWeight / prevMorphWeight)); + return 0; +} + +/** + * Requests a load of the next frame of a Link animation, advances the morph, and requests an interpolation between + * jointTable and morphTable + */ +void LinkAnimation_AnimateFrame(GlobalContext* globalCtx, SkelAnime* skelAnime) { + AnimationContext_SetLoadFrame(globalCtx, skelAnime->animation, skelAnime->curFrame, skelAnime->limbCount, + skelAnime->jointTable); + if (skelAnime->morphWeight != 0) { + f32 updateRate = R_UPDATE_RATE * 0.5f; + + skelAnime->morphWeight -= skelAnime->morphRate * updateRate; + if (skelAnime->morphWeight <= 0.0f) { + skelAnime->morphWeight = 0.0f; + } else { + AnimationContext_SetInterp(globalCtx, skelAnime->limbCount, skelAnime->jointTable, skelAnime->morphTable, + skelAnime->morphWeight); + } + } +} + +/** + * Advances a Link animation that loops over its full length + */ +s32 LinkAnimation_Loop(GlobalContext* globalCtx, SkelAnime* skelAnime) { + f32 updateRate = R_UPDATE_RATE * 0.5f; + + skelAnime->curFrame += skelAnime->playSpeed * updateRate; + if (skelAnime->curFrame < 0.0f) { + skelAnime->curFrame += skelAnime->animLength; + } else if (skelAnime->animLength <= skelAnime->curFrame) { + skelAnime->curFrame -= skelAnime->animLength; + } + LinkAnimation_AnimateFrame(globalCtx, skelAnime); + return 0; +} + +/** + * Advances a Link animation that stops at endFrame and returns true when it is reached. + */ +s32 LinkAnimation_Once(GlobalContext* globalCtx, SkelAnime* skelAnime) { + f32 updateRate = R_UPDATE_RATE * 0.5f; + + if (skelAnime->curFrame == skelAnime->endFrame) { + LinkAnimation_AnimateFrame(globalCtx, skelAnime); + return 1; + } + skelAnime->curFrame += skelAnime->playSpeed * updateRate; + if ((skelAnime->curFrame - skelAnime->endFrame) * skelAnime->playSpeed > 0.0f) { + skelAnime->curFrame = skelAnime->endFrame; + } else if (skelAnime->curFrame < 0.0f) { + skelAnime->curFrame += skelAnime->animLength; + } else if (skelAnime->animLength <= skelAnime->curFrame) { + skelAnime->curFrame -= skelAnime->animLength; + } + LinkAnimation_AnimateFrame(globalCtx, skelAnime); + return 0; +} + +/** + * Sets a new morph and resets the morph weight for the current animation. + */ +void Animation_SetMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, f32 morphFrames) { + skelAnime->morphWeight = 1.0f; + skelAnime->morphRate = 1.0f / morphFrames; +} + +/** + * General way to set a new Link animation, allowing choice of playback speed, start frame, end frame, play mode, and + * number of transition frames. Positive morph frames morph from the current pose to the start pose of the new + * animation, then start the new animation. Negative morph frames start the new animation immediately, modified by the + * pose immediately before the animation change. + */ +void LinkAnimation_Change(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, f32 playSpeed, + f32 startFrame, f32 endFrame, u8 mode, f32 morphFrames) { + LinkAnimationHeader* ogAnim = animation; + + if (ResourceMgr_OTRSigCheck(animation) != 0) + animation = ResourceMgr_LoadAnimByName(animation); + + skelAnime->mode = mode; + if ((morphFrames != 0.0f) && ((animation != skelAnime->animation) || (startFrame != skelAnime->curFrame))) { + if (morphFrames < 0) { + LinkAnimation_SetUpdateFunction(skelAnime); + SkelAnime_CopyFrameTable(skelAnime, skelAnime->morphTable, skelAnime->jointTable); + morphFrames = -morphFrames; + } else { + skelAnime->update = LinkAnimation_Morph; + AnimationContext_SetLoadFrame(globalCtx, animation, (s32)startFrame, skelAnime->limbCount, + skelAnime->morphTable); + } + skelAnime->morphWeight = 1.0f; + skelAnime->morphRate = 1.0f / morphFrames; + } else { + LinkAnimation_SetUpdateFunction(skelAnime); + AnimationContext_SetLoadFrame(globalCtx, animation, (s32)startFrame, skelAnime->limbCount, + skelAnime->jointTable); + skelAnime->morphWeight = 0.0f; + } + + skelAnime->animation = ogAnim; + skelAnime->curFrame = 0.0f; + skelAnime->startFrame = startFrame; + skelAnime->curFrame = startFrame; + skelAnime->endFrame = endFrame; + skelAnime->animLength = Animation_GetLength(animation); + skelAnime->playSpeed = playSpeed; +} + +/** + * Immediately changes to a Link animation that plays once at the default speed. + */ +void LinkAnimation_PlayOnce(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation) { + LinkAnimation_Change(globalCtx, skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, + 0.0f); +} + +/** + * Immediately changes to a Link animation that plays once at the specified speed. + */ +void LinkAnimation_PlayOnceSetSpeed(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 playSpeed) { + LinkAnimation_Change(globalCtx, skelAnime, animation, playSpeed, 0.0f, Animation_GetLastFrame(animation), + ANIMMODE_ONCE, 0.0f); +} + +/** + * Immediately changes to a Link animation that loops at the default speed. + */ +void LinkAnimation_PlayLoop(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation) { + LinkAnimation_Change(globalCtx, skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + 0.0f); +} + +/** + * Immediately changes to a Link animation that loops at the specified speed. + */ +void LinkAnimation_PlayLoopSetSpeed(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 playSpeed) { + LinkAnimation_Change(globalCtx, skelAnime, animation, playSpeed, 0.0f, Animation_GetLastFrame(animation), + ANIMMODE_LOOP, 0.0f); +} + +/** + * Requests copying jointTable to morphTable + */ +void LinkAnimation_CopyJointToMorph(GlobalContext* globalCtx, SkelAnime* skelAnime) { + AnimationContext_SetCopyAll(globalCtx, skelAnime->limbCount, skelAnime->morphTable, skelAnime->jointTable); +} + +/** + * Requests copying morphTable to jointTable + * unused + */ +void LinkAnimation_CopyMorphToJoint(GlobalContext* globalCtx, SkelAnime* skelAnime) { + AnimationContext_SetCopyAll(globalCtx, skelAnime->limbCount, skelAnime->jointTable, skelAnime->morphTable); +} + +/** + * Requests loading frame data from the Link animation into morphTable + */ +void LinkAnimation_LoadToMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 frame) { + AnimationContext_SetLoadFrame(globalCtx, animation, (s32)frame, skelAnime->limbCount, skelAnime->morphTable); +} + +/** + * Requests loading frame data from the Link animation into jointTable + */ +void LinkAnimation_LoadToJoint(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation, + f32 frame) { + AnimationContext_SetLoadFrame(globalCtx, animation, (s32)frame, skelAnime->limbCount, skelAnime->jointTable); +} + +/** + * Requests interpolating between jointTable and morphTable, placing the result in jointTable + */ +void LinkAnimation_InterpJointMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, f32 weight) { + AnimationContext_SetInterp(globalCtx, skelAnime->limbCount, skelAnime->jointTable, skelAnime->morphTable, weight); +} + +/** + * Requests loading frame data from the Link animations and blending them, placing the result in jointTable + */ +void LinkAnimation_BlendToJoint(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation1, + f32 frame1, LinkAnimationHeader* animation2, f32 frame2, f32 blendWeight, + Vec3s* blendTable) { + Vec3s* alignedBlendTable; + + AnimationContext_SetLoadFrame(globalCtx, animation1, (s32)frame1, skelAnime->limbCount, skelAnime->jointTable); + + alignedBlendTable = (Vec3s*)ALIGN16((uintptr_t)blendTable); + + AnimationContext_SetLoadFrame(globalCtx, animation2, (s32)frame2, skelAnime->limbCount, alignedBlendTable); + AnimationContext_SetInterp(globalCtx, skelAnime->limbCount, skelAnime->jointTable, alignedBlendTable, blendWeight); +} + +/** + * Requests loading frame data from the Link animations and blending them, placing the result in morphTable + */ +void LinkAnimation_BlendToMorph(GlobalContext* globalCtx, SkelAnime* skelAnime, LinkAnimationHeader* animation1, + f32 frame1, LinkAnimationHeader* animation2, f32 frame2, f32 blendWeight, + Vec3s* blendTable) { + Vec3s* alignedBlendTable; + + AnimationContext_SetLoadFrame(globalCtx, animation1, (s32)frame1, skelAnime->limbCount, skelAnime->morphTable); + + alignedBlendTable = (Vec3s*)ALIGN16((uintptr_t)blendTable); + + AnimationContext_SetLoadFrame(globalCtx, animation2, (s32)frame2, skelAnime->limbCount, alignedBlendTable); + AnimationContext_SetInterp(globalCtx, skelAnime->limbCount, skelAnime->morphTable, alignedBlendTable, blendWeight); +} + +/** + * Changes a looping animation to one that stops at the end. Unused + */ +void LinkAnimation_EndLoop(SkelAnime* skelAnime) { + skelAnime->mode = ANIMMODE_ONCE; + LinkAnimation_SetUpdateFunction(skelAnime); +} + +/** + * Checks if the current frame is after frame and the previous frame was before it. + */ +s32 Animation_OnFrameImpl(SkelAnime* skelAnime, f32 frame, f32 updateRate) { + f32 updateSpeed = skelAnime->playSpeed * updateRate; + f32 prevFrame = skelAnime->curFrame - updateSpeed; + f32 curFrameDiff; + f32 prevFrameDiff; + + if (prevFrame < 0.0f) { + prevFrame += skelAnime->animLength; + } else if (prevFrame >= skelAnime->animLength) { + prevFrame -= skelAnime->animLength; + } + + if ((frame == 0.0f) && (updateSpeed > 0.0f)) { + frame = skelAnime->animLength; + } + + curFrameDiff = prevFrame + updateSpeed - frame; + prevFrameDiff = curFrameDiff - updateSpeed; + if ((curFrameDiff * updateSpeed >= 0.0f) && (prevFrameDiff * updateSpeed < 0.0f)) { + return true; + } + return false; +} + +/** + * Checks if the current Link animation has reached the specified frame + */ +s32 LinkAnimation_OnFrame(SkelAnime* skelAnime, f32 frame) { + f32 updateRate = R_UPDATE_RATE * 0.5f; + + return Animation_OnFrameImpl(skelAnime, frame, updateRate); +} + +/** + * Initializes a normal skeleton to a looping animation, dynamically allocating the frame tables if not provided. + */ +s32 SkelAnime_Init(GlobalContext* globalCtx, SkelAnime* skelAnime, SkeletonHeader* skeletonHeaderSeg, + AnimationHeader* animation, Vec3s* jointTable, Vec3s* morphTable, s32 limbCount) { + if (ResourceMgr_OTRSigCheck(skeletonHeaderSeg)) + skeletonHeaderSeg = ResourceMgr_LoadSkeletonByName(skeletonHeaderSeg); + + SkeletonHeader* skeletonHeader = SEGMENTED_TO_VIRTUAL(skeletonHeaderSeg); + + skelAnime->limbCount = skeletonHeader->limbCount + 1; + skelAnime->skeleton = SEGMENTED_TO_VIRTUAL(skeletonHeader->segment); + if (jointTable == NULL) { + skelAnime->jointTable = + ZeldaArena_MallocDebug(skelAnime->limbCount * sizeof(*skelAnime->jointTable), "../z_skelanime.c", 2968); + skelAnime->morphTable = + ZeldaArena_MallocDebug(skelAnime->limbCount * sizeof(*skelAnime->morphTable), "../z_skelanime.c", 2969); + } else { + ASSERT(limbCount == skelAnime->limbCount, "joint_buff_num == this->joint_num", "../z_skelanime.c", 2973); + skelAnime->jointTable = jointTable; + skelAnime->morphTable = morphTable; + } + if ((skelAnime->jointTable == NULL) || (skelAnime->morphTable == NULL)) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Skeleton_Info2_ct メモリアロケーションエラー\n"); // "Memory allocation error" + osSyncPrintf(VT_RST); + } + + if (animation != NULL) { + Animation_PlayLoop(skelAnime, animation); + } +} + +/** + * Initializes a flex skeleton to a looping animation, dynamically allocating the frame tables if not given. + */ +s32 SkelAnime_InitFlex(GlobalContext* globalCtx, SkelAnime* skelAnime, FlexSkeletonHeader* skeletonHeaderSeg, + AnimationHeader* animation, Vec3s* jointTable, Vec3s* morphTable, s32 limbCount) { + if (ResourceMgr_OTRSigCheck(skeletonHeaderSeg) != 0) + skeletonHeaderSeg = ResourceMgr_LoadSkeletonByName(skeletonHeaderSeg); + + FlexSkeletonHeader* skeletonHeader = SEGMENTED_TO_VIRTUAL(skeletonHeaderSeg); + + skelAnime->limbCount = skeletonHeader->sh.limbCount + 1; + skelAnime->dListCount = skeletonHeader->dListCount; + skelAnime->skeleton = SEGMENTED_TO_VIRTUAL(skeletonHeader->sh.segment); + + if (jointTable == NULL) { + skelAnime->jointTable = + ZeldaArena_MallocDebug(skelAnime->limbCount * sizeof(*skelAnime->jointTable), "../z_skelanime.c", 3047); + + skelAnime->morphTable = + ZeldaArena_MallocDebug(skelAnime->limbCount * sizeof(*skelAnime->morphTable), "../z_skelanime.c", 3048); + } else { + ASSERT(limbCount == skelAnime->limbCount, "joint_buff_num == this->joint_num", "../z_skelanime.c", 3052); + skelAnime->jointTable = jointTable; + skelAnime->morphTable = morphTable; + } + if ((skelAnime->jointTable == NULL) || (skelAnime->morphTable == NULL)) { + osSyncPrintf(VT_FGCOL(RED)); + // "Memory allocation error" + osSyncPrintf("Skeleton_Info_Rom_SV_ct メモリアロケーションエラー\n"); + osSyncPrintf(VT_RST); + } + + if (animation != NULL) { + Animation_PlayLoop(skelAnime, animation); + } +} + +/** + * Initializes a skeleton with SkinLimbs to a looping animation, dynamically allocating the frame tables. + */ +s32 SkelAnime_InitSkin(GlobalContext* globalCtx, SkelAnime* skelAnime, SkeletonHeader* skeletonHeaderSeg, + AnimationHeader* animation) { + if (ResourceMgr_OTRSigCheck(skeletonHeaderSeg) != 0) + animation = ResourceMgr_LoadAnimByName(skeletonHeaderSeg); + + SkeletonHeader* skeletonHeader = SEGMENTED_TO_VIRTUAL(skeletonHeaderSeg); + + skelAnime->limbCount = skeletonHeader->limbCount + 1; + skelAnime->skeleton = SEGMENTED_TO_VIRTUAL(skeletonHeader->segment); + skelAnime->jointTable = + ZeldaArena_MallocDebug(skelAnime->limbCount * sizeof(*skelAnime->jointTable), "../z_skelanime.c", 3120); + skelAnime->morphTable = + ZeldaArena_MallocDebug(skelAnime->limbCount * sizeof(*skelAnime->morphTable), "../z_skelanime.c", 3121); + if ((skelAnime->jointTable == NULL) || (skelAnime->morphTable == NULL)) { + osSyncPrintf(VT_FGCOL(RED)); + // "Memory allocation error" + osSyncPrintf("Skeleton_Info2_skin2_ct メモリアロケーションエラー\n"); + osSyncPrintf(VT_RST); + } + + if (animation != NULL) { + Animation_PlayLoop(skelAnime, animation); + } +} + +/** + * Sets the SkelAnime's update function based on its current mode. + */ +void SkelAnime_SetUpdate(SkelAnime* skelAnime) { + if (skelAnime->mode <= ANIMMODE_LOOP_INTERP) { + skelAnime->update = SkelAnime_LoopFull; + } else if (skelAnime->mode <= ANIMMODE_ONCE_INTERP) { + skelAnime->update = SkelAnime_Once; + } else { + skelAnime->update = SkelAnime_LoopPartial; + } +} + +/** + * Advances the current animation and updates all frame tables. If the animation plays once, returns true when it + * finishes. + */ +s32 SkelAnime_Update(SkelAnime* skelAnime) { + return skelAnime->update(skelAnime); +} + +/** + * Morphs from the pose in jointTable to the one in morphTable, advancing the morph but not the animation frame + */ +s32 SkelAnime_Morph(SkelAnime* skelAnime) { + f32 prevMorphWeight = skelAnime->morphWeight; + f32 updateRate = R_UPDATE_RATE * (1.0f / 3.0f); + + skelAnime->morphWeight -= skelAnime->morphRate * updateRate; + if (skelAnime->morphWeight <= 0.0f) { + SkelAnime_SetUpdate(skelAnime); + skelAnime->morphWeight = 0.0f; + } + SkelAnime_InterpFrameTable(skelAnime->limbCount, skelAnime->jointTable, skelAnime->jointTable, + skelAnime->morphTable, 1.0f - (skelAnime->morphWeight / prevMorphWeight)); + return 0; +} + +/** + * Performs a tapered morph from the pose in jointTable to the one in morphTable, advancing the morph but not the + * animation frame + */ +s32 SkelAnime_MorphTaper(SkelAnime* skelAnime) { + s16 prevPhase = skelAnime->morphWeight * 0x4000; + s16 curPhase; + f32 prevWeight; + f32 curWeight; + f32 updateRate = R_UPDATE_RATE * (1.0f / 3.0f); + + skelAnime->morphWeight -= skelAnime->morphRate * updateRate; + if (skelAnime->morphWeight <= 0.0f) { + SkelAnime_SetUpdate(skelAnime); + skelAnime->morphWeight = 0.0f; + } + curPhase = skelAnime->morphWeight * 0x4000; + if (skelAnime->taper <= ANIMTAPER_DECEL) { + prevWeight = 1.0f - Math_CosS(prevPhase); + curWeight = 1.0f - Math_CosS(curPhase); + } else { + prevWeight = Math_SinS(prevPhase); + curWeight = Math_SinS(curPhase); + } + if (curWeight != 0.0f) { + curWeight /= prevWeight; + } else { + curWeight = 0.0f; + } + SkelAnime_InterpFrameTable(skelAnime->limbCount, skelAnime->jointTable, skelAnime->jointTable, + skelAnime->morphTable, 1.0f - curWeight); + return 0; +} + +/** + * Gets frame data for the current frame as modified by morphTable and advances the morph + */ +void SkelAnime_AnimateFrame(SkelAnime* skelAnime) { + Vec3s nextjointTable[100]; + + SkelAnime_GetFrameData(skelAnime->animation, skelAnime->curFrame, skelAnime->limbCount, skelAnime->jointTable); + if (skelAnime->mode & ANIM_INTERP) { + s32 frame = skelAnime->curFrame; + f32 partialFrame = skelAnime->curFrame - frame; + + if (++frame >= (s32)skelAnime->animLength) { + frame = 0; + } + SkelAnime_GetFrameData(skelAnime->animation, frame, skelAnime->limbCount, nextjointTable); + SkelAnime_InterpFrameTable(skelAnime->limbCount, skelAnime->jointTable, skelAnime->jointTable, nextjointTable, + partialFrame); + } + if (skelAnime->morphWeight != 0) { + f32 updateRate = R_UPDATE_RATE * (1.0f / 3.0f); + + skelAnime->morphWeight -= skelAnime->morphRate * updateRate; + if (skelAnime->morphWeight <= 0.0f) { + skelAnime->morphWeight = 0.0f; + } else { + SkelAnime_InterpFrameTable(skelAnime->limbCount, skelAnime->jointTable, skelAnime->jointTable, + skelAnime->morphTable, skelAnime->morphWeight); + } + } +} + +/** + * Advances an animation that loops over its full length and updates the frame tables + */ +s32 SkelAnime_LoopFull(SkelAnime* skelAnime) { + f32 updateRate = R_UPDATE_RATE * (1.0f / 3.0f); + + skelAnime->curFrame += skelAnime->playSpeed * updateRate; + if (skelAnime->curFrame < 0.0f) { + skelAnime->curFrame += skelAnime->animLength; + } else if (skelAnime->animLength <= skelAnime->curFrame) { + skelAnime->curFrame -= skelAnime->animLength; + } + SkelAnime_AnimateFrame(skelAnime); + return 0; +} + +/** + * Advances an animation that loops over part of its length and updates the frame tables + */ +s32 SkelAnime_LoopPartial(SkelAnime* skelAnime) { + f32 updateRate = R_UPDATE_RATE * (1.0f / 3.0f); + + skelAnime->curFrame += skelAnime->playSpeed * updateRate; + if (skelAnime->curFrame < skelAnime->startFrame) { + skelAnime->curFrame = (skelAnime->curFrame - skelAnime->startFrame) + skelAnime->endFrame; + } else if (skelAnime->endFrame <= skelAnime->curFrame) { + skelAnime->curFrame = (skelAnime->curFrame - skelAnime->endFrame) + skelAnime->startFrame; + } + + SkelAnime_AnimateFrame(skelAnime); + return 0; +} + +/** + * Advances an animation that stops at endFrame and returns true when it is reached. + */ +s32 SkelAnime_Once(SkelAnime* skelAnime) { + f32 updateRate = R_UPDATE_RATE * (1.0f / 3.0f); + + if (skelAnime->curFrame == skelAnime->endFrame) { + SkelAnime_GetFrameData(skelAnime->animation, (s32)skelAnime->curFrame, skelAnime->limbCount, + skelAnime->jointTable); + SkelAnime_AnimateFrame(skelAnime); + return 1; + } + + skelAnime->curFrame += skelAnime->playSpeed * updateRate; + + if ((skelAnime->curFrame - skelAnime->endFrame) * skelAnime->playSpeed > 0.0f) { + skelAnime->curFrame = skelAnime->endFrame; + } else if (skelAnime->curFrame < 0.0f) { + skelAnime->curFrame += skelAnime->animLength; + } else if (skelAnime->animLength <= skelAnime->curFrame) { + skelAnime->curFrame -= skelAnime->animLength; + } + SkelAnime_AnimateFrame(skelAnime); + return 0; +} + +/** + * Fully general way to set a new animation, allowing choice of playback speed, start frame, end frame, play mode, + * number of transition frames, and tapering of the transition. Positive morph frames morph from the current pose to the + * start pose of the new animation, then start the new animation. Negative morph frames start the new animation + * immediately, modified by the pose immediately before the animation change. + */ +void Animation_ChangeImpl(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed, f32 startFrame, f32 endFrame, + u8 mode, f32 morphFrames, s8 taper) { + skelAnime->mode = mode; + if ((morphFrames != 0.0f) && ((animation != skelAnime->animation) || (startFrame != skelAnime->curFrame))) { + if (morphFrames < 0) { + SkelAnime_SetUpdate(skelAnime); + SkelAnime_CopyFrameTable(skelAnime, skelAnime->morphTable, skelAnime->jointTable); + morphFrames = -morphFrames; + } else { + if (taper != ANIMTAPER_NONE) { + skelAnime->update = SkelAnime_MorphTaper; + skelAnime->taper = taper; + } else { + skelAnime->update = SkelAnime_Morph; + } + SkelAnime_GetFrameData(animation, startFrame, skelAnime->limbCount, skelAnime->morphTable); + } + skelAnime->morphWeight = 1.0f; + skelAnime->morphRate = 1.0f / morphFrames; + } else { + SkelAnime_SetUpdate(skelAnime); + SkelAnime_GetFrameData(animation, startFrame, skelAnime->limbCount, skelAnime->jointTable); + skelAnime->morphWeight = 0.0f; + } + + skelAnime->animation = animation; + skelAnime->startFrame = startFrame; + skelAnime->endFrame = endFrame; + skelAnime->animLength = Animation_GetLength(animation); + if (skelAnime->mode >= ANIMMODE_LOOP_PARTIAL) { + skelAnime->curFrame = 0.0f; + } else { + skelAnime->curFrame = startFrame; + if (skelAnime->mode <= ANIMMODE_LOOP_INTERP) { + skelAnime->endFrame = skelAnime->animLength - 1.0f; + } + } + skelAnime->playSpeed = playSpeed; +} + +/** + * General way to set a new animation, allowing choice of playback speed, start frame, end frame, play mode, and number + * of transition frames. Positive morph frames morph from the current pose to the start pose of the new animation, then + * start the new animation. Negative morph frames start the new animation immediately, modified by the pose immediately + * before the animation change. + */ +void Animation_Change(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed, f32 startFrame, f32 endFrame, + u8 mode, f32 morphFrames) { + AnimationHeader* ogAnim = animation; + + if (ResourceMgr_OTRSigCheck(animation) != 0) + animation = ResourceMgr_LoadAnimByName(animation); + + Animation_ChangeImpl(skelAnime, animation, playSpeed, startFrame, endFrame, mode, morphFrames, ANIMTAPER_NONE); + skelAnime->animation = ogAnim; +} + +/** + * Immediately changes to an animation that plays once at the default speed. + */ +void Animation_PlayOnce(SkelAnime* skelAnime, AnimationHeader* animation) { + Animation_Change(skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, 0.0f); +} + +/** + * Smoothly transitions to an animation that plays once at the default speed. + * Positive morph frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose immediately before the + * animation change. + */ +void Animation_MorphToPlayOnce(SkelAnime* skelAnime, AnimationHeader* animation, f32 morphFrames) { + Animation_Change(skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, morphFrames); +} + +/** + * Immediately changes to an animation that plays once at the specified speed. + */ +void Animation_PlayOnceSetSpeed(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed) { + Animation_Change(skelAnime, animation, playSpeed, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, 0.0f); +} + +/** + * Immediately changes to an animation that loops at the default. + */ +void Animation_PlayLoop(SkelAnime* skelAnime, AnimationHeader* animation) { + Animation_Change(skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, 0.0f); +} + +/** + * Smoothly transitions to a looping animation, specifying the number of frames for the transition. + * Positive morph frames morph from the current pose to the start pose of the new animation, then start the new + * animation. Negative morph frames start the new animation immediately, modified by the pose immediately before the + * animation change. + */ +void Animation_MorphToLoop(SkelAnime* skelAnime, AnimationHeader* animation, f32 morphFrames) { + Animation_Change(skelAnime, animation, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, morphFrames); +} + +/** + * Immediately changes to an animation that loops at the specified speed. + */ +void Animation_PlayLoopSetSpeed(SkelAnime* skelAnime, AnimationHeader* animation, f32 playSpeed) { + Animation_Change(skelAnime, animation, playSpeed, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, 0.0f); +} + +/** + * Changes a looping animation to one that stops at the end. Unused + */ +void Animation_EndLoop(SkelAnime* skelAnime) { + skelAnime->mode = ANIMMODE_ONCE; + skelAnime->endFrame = skelAnime->animLength; + SkelAnime_SetUpdate(skelAnime); +} + +/** + * Reverses the current animation. + */ +void Animation_Reverse(SkelAnime* skelAnime) { + f32 startFrame = skelAnime->startFrame; + + skelAnime->startFrame = skelAnime->endFrame; + skelAnime->playSpeed = -skelAnime->playSpeed; + skelAnime->endFrame = startFrame; +} + +/** + * Copies the src frame table to the dst frame table if copyFlag for that limb is true. + * Used only by En_Test + */ +void SkelAnime_CopyFrameTableTrue(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src, u8* copyFlag) { + s32 i; + + for (i = 0; i < skelAnime->limbCount; i++, dst++, src++) { + if (*copyFlag++) { + *dst = *src; + } + } +} + +/** + * Copies the src frame table to the dst frame table if copyFlag for that limb is false. + * Unused. + */ +void SkelAnime_CopyFrameTableFalse(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src, u8* copyFlag) { + s32 i; + + for (i = 0; i < skelAnime->limbCount; i++, dst++, src++) { + if (!*copyFlag++) { + *dst = *src; + } + } +} + +/** + * Updates translation of the root limb and calculates `pos`, the difference between + * the old and new positions of the root limb as rotated by `angle`. Used to allow + * animations to change an actor's position. + */ +void SkelAnime_UpdateTranslation(SkelAnime* skelAnime, Vec3f* diff, s16 angle) { + f32 x; + f32 z; + f32 sin; + f32 cos; + + if (skelAnime->moveFlags & ANIM_FLAG_NOMOVE) { + diff->x = diff->z = 0.0f; + } else { + x = skelAnime->jointTable[0].x; + z = skelAnime->jointTable[0].z; + sin = Math_SinS(angle); + cos = Math_CosS(angle); + diff->x = x * cos + z * sin; + diff->z = z * cos - x * sin; + x = skelAnime->prevTransl.x; + z = skelAnime->prevTransl.z; + sin = Math_SinS(skelAnime->prevRot); + cos = Math_CosS(skelAnime->prevRot); + diff->x -= x * cos + z * sin; + diff->z -= z * cos - x * sin; + } + + skelAnime->prevRot = angle; + skelAnime->prevTransl.x = skelAnime->jointTable[0].x; + skelAnime->jointTable[0].x = skelAnime->baseTransl.x; + skelAnime->prevTransl.z = skelAnime->jointTable[0].z; + skelAnime->jointTable[0].z = skelAnime->baseTransl.z; + if (skelAnime->moveFlags & ANIM_FLAG_UPDATEY) { + if (skelAnime->moveFlags & ANIM_FLAG_NOMOVE) { + diff->y = 0.0f; + } else { + diff->y = skelAnime->jointTable[0].y - skelAnime->prevTransl.y; + } + skelAnime->prevTransl.y = skelAnime->jointTable[0].y; + skelAnime->jointTable[0].y = skelAnime->baseTransl.y; + } else { + diff->y = 0.0f; + skelAnime->prevTransl.y = skelAnime->jointTable[0].y; + } + skelAnime->moveFlags &= ~ANIM_FLAG_NOMOVE; +} + +/** + * Checks if the current animation is at the specified frame + */ +s32 Animation_OnFrame(SkelAnime* skelAnime, f32 frame) { + return Animation_OnFrameImpl(skelAnime, frame, 1.0f); +} + +/** + * Frees the frame tables for a skelAnime with dynamically allocated tables. + */ +void SkelAnime_Free(SkelAnime* skelAnime, GlobalContext* globalCtx) { + if (skelAnime->jointTable != NULL) { + ZeldaArena_FreeDebug(skelAnime->jointTable, "../z_skelanime.c", 3729); + } else { + osSyncPrintf("now_joint あきまへん!!\n"); // "now_joint is freed! !" + } + + if (skelAnime->morphTable != NULL) { + ZeldaArena_FreeDebug(skelAnime->morphTable, "../z_skelanime.c", 3731); + } else { + osSyncPrintf("morf_joint あきまへん!!\n"); // "morf_joint is freed !!" + } +} + +/** + * Copies the src frame table to the dst frame table. + */ +void SkelAnime_CopyFrameTable(SkelAnime* skelAnime, Vec3s* dst, Vec3s* src) { + s32 i; + + for (i = 0; i < skelAnime->limbCount; i++) { + *dst++ = *src++; + } +} diff --git a/soh/src/code/z_skin.c b/soh/src/code/z_skin.c new file mode 100644 index 000000000..9c886fa2e --- /dev/null +++ b/soh/src/code/z_skin.c @@ -0,0 +1,269 @@ +#include "global.h" + +MtxF gSkinLimbMatrices[60]; // holds matrices for each limb of the skeleton currently being drawn + +static s32 sUnused; + +void Skin_UpdateVertices(MtxF* mtx, SkinVertex* skinVertices, SkinLimbModif* modifEntry, Vtx* vtxBuf, Vec3f* pos) { + Vtx* vtx; + SkinVertex* vertexEntry; + f32 xwTemp; + f32 ywTemp; + f32 zwTemp; + Vec3f normal; + Vec3f sp64; + + for (vertexEntry = skinVertices; vertexEntry < &skinVertices[modifEntry->vtxCount]; vertexEntry++) { + vtx = &vtxBuf[vertexEntry->index]; + + vtx->n.ob[0] = pos->x; + vtx->n.ob[1] = pos->y; + vtx->n.ob[2] = pos->z; + + xwTemp = mtx->xw; + ywTemp = mtx->yw; + zwTemp = mtx->zw; + + mtx->xw = mtx->yw = mtx->zw = 0.0f; + + sp64.x = vertexEntry->normX; + sp64.y = vertexEntry->normY; + sp64.z = vertexEntry->normZ; + + SkinMatrix_Vec3fMtxFMultXYZ(mtx, &sp64, &normal); + + vtx->n.n[0] = normal.x; + vtx->n.n[1] = normal.y; + vtx->n.n[2] = normal.z; + + mtx->xw = xwTemp; + mtx->yw = ywTemp; + mtx->zw = zwTemp; + } +} + +void Skin_ApplyLimbModifications(GraphicsContext* gfxCtx, Skin* skin, s32 limbIndex, s32 arg3) { + s32 modifCount; + SkinLimb** skeleton; + SkinLimb* limb; + SkinAnimatedLimbData* data; + SkinLimbModif* modif; + SkinLimbVtx* vtxEntry; + s32 transformCount; + f32 scale; + SkinVertex* skinVertices; + SkinTransformation* limbTransformations; + Vtx* vtxBuf; + SkinLimbModif* modifications; + Vec3f vtxPoint; + Vec3f spD0; + SkinTransformation* transformationEntry; + + OPEN_DISPS(gfxCtx, "../z_skin.c", 254); + + skeleton = (SkinLimb**)SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + data = SEGMENTED_TO_VIRTUAL(((SkinLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]))->segment); + modifications = (SkinLimbModif*)SEGMENTED_TO_VIRTUAL(data->limbModifications); + + vtxEntry = &skin->vtxTable[limbIndex]; + vtxBuf = vtxEntry->buf[vtxEntry->index]; + modifCount = data->limbModifCount; + + for (modif = modifications; modif < modifications + modifCount; modif++) { + transformCount = modif->transformCount; + skinVertices = (SkinVertex*)SEGMENTED_TO_VIRTUAL(modif->skinVertices); + limbTransformations = (SkinTransformation*)SEGMENTED_TO_VIRTUAL(modif->limbTransformations); + + if (transformCount == 1) { + Vec3f spAC; + + spAC.x = limbTransformations[0].x; + spAC.y = limbTransformations[0].y; + spAC.z = limbTransformations[0].z; + + SkinMatrix_Vec3fMtxFMultXYZ(&gSkinLimbMatrices[limbTransformations[0].limbIndex], &spAC, &vtxPoint); + } else if (arg3 == 1) { + Vec3f spA0; + + transformationEntry = &limbTransformations[modif->unk_4]; + + spA0.x = transformationEntry->x; + spA0.y = transformationEntry->y; + spA0.z = transformationEntry->z; + + SkinMatrix_Vec3fMtxFMultXYZ(&gSkinLimbMatrices[transformationEntry->limbIndex], &spA0, &vtxPoint); + } else { + Vec3f phi_f20; + Vec3f sp88; + + phi_f20.x = phi_f20.y = phi_f20.z = 0.0f; + + for (transformationEntry = &limbTransformations[0]; + transformationEntry < &limbTransformations[transformCount]; transformationEntry++) { + scale = transformationEntry->scale * 0.01f; + + sp88.x = transformationEntry->x; + sp88.y = transformationEntry->y; + sp88.z = transformationEntry->z; + + SkinMatrix_Vec3fMtxFMultXYZ(&gSkinLimbMatrices[transformationEntry->limbIndex], &sp88, &spD0); + + spD0.x *= scale; + spD0.y *= scale; + spD0.z *= scale; + + phi_f20.x += spD0.x; + phi_f20.y += spD0.y; + phi_f20.z += spD0.z; + } + + vtxPoint.x = phi_f20.x; + vtxPoint.y = phi_f20.y; + vtxPoint.z = phi_f20.z; + } + + Skin_UpdateVertices(&gSkinLimbMatrices[limbTransformations[modif->unk_4].limbIndex], skinVertices, modif, + vtxBuf, &vtxPoint); + } + + gSPSegment(POLY_OPA_DISP++, 0x08, vtxEntry->buf[vtxEntry->index]); + + vtxEntry->index = (vtxEntry->index == 0) ? 1 : 0; + + CLOSE_DISPS(gfxCtx, "../z_skin.c", 344); +} + +/** + * Draw a limb of type SKIN_LIMB_TYPE_ANIMATED, of the skeleton `skin` at index `limbIndex` + * The vertices of this limb are modified dynamically + */ +void Skin_DrawAnimatedLimb(GraphicsContext* gfxCtx, Skin* skin, s32 limbIndex, s32 arg3, s32 drawFlags) { + SkinLimb** skeleton; + SkinAnimatedLimbData* data; + + OPEN_DISPS(gfxCtx, "../z_skin.c", 364); + + skeleton = SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + data = SEGMENTED_TO_VIRTUAL(((SkinLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]))->segment); + + if (!(drawFlags & SKIN_DRAW_FLAG_CUSTOM_TRANSFORMS)) { + Skin_ApplyLimbModifications(gfxCtx, skin, limbIndex, arg3); + } + + gSPDisplayList(POLY_OPA_DISP++, data->dlist); + + CLOSE_DISPS(gfxCtx, "../z_skin.c", 377); +} + +/** + * Draw a limb of type SKIN_LIMB_TYPE_NORMAL, of the skeleton `skin` at index `limbIndex` + */ +void Skin_DrawLimb(GraphicsContext* gfxCtx, Skin* skin, s32 limbIndex, Gfx* dlistOverride, s32 drawFlags) { + Gfx* gfx = dlistOverride; + SkinLimb** skeleton; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_skin.c", 395); + + skeleton = SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + + if (dlistOverride == NULL) { + gfx = ((SkinLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]))->segment; + } + + if (gfx != NULL) { + Mtx* mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &gSkinLimbMatrices[limbIndex]); + + if (mtx != NULL) { + gSPMatrix(POLY_OPA_DISP++, mtx, G_MTX_PUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gfx); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + gDPPipeSync(POLY_OPA_DISP++); + } + } + + CLOSE_DISPS(gfxCtx, "../z_skin.c", 433); +} + +void Skin_DrawImpl(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, + SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6, s32 drawFlags) { + s32 i; + s32 segmentType; + SkinLimb** skeleton; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_skin.c", 471); + + if (!(drawFlags & SKIN_DRAW_FLAG_CUSTOM_TRANSFORMS)) { + Skin_ApplyAnimTransformations(skin, gSkinLimbMatrices, actor, setTranslation); + } + + skeleton = SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + + if (!(drawFlags & SKIN_DRAW_FLAG_CUSTOM_MATRIX)) { + Mtx* mtx; + + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &skin->mtx); + + if (mtx == NULL) { + goto close_disps; + } + + gSPMatrix(POLY_OPA_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + } + + for (i = 0; i < skin->skeletonHeader->limbCount; i++) { + s32 shouldDraw = true; + + if (overrideLimbDraw != NULL) { + shouldDraw = overrideLimbDraw(actor, globalCtx, i, skin); + } + + segmentType = ((SkinLimb*)SEGMENTED_TO_VIRTUAL(skeleton[i]))->segmentType; + + if (segmentType == SKIN_LIMB_TYPE_ANIMATED && shouldDraw == true) { + Skin_DrawAnimatedLimb(gfxCtx, skin, i, arg6, drawFlags); + } else if (segmentType == SKIN_LIMB_TYPE_NORMAL && shouldDraw == true) { + Skin_DrawLimb(gfxCtx, skin, i, NULL, drawFlags); + } + } + + if (postDraw != NULL) { + postDraw(actor, globalCtx, skin); + } + +close_disps: + CLOSE_DISPS(gfxCtx, "../z_skin.c", 534); +} + +// allows specifying PostLimbDraw and setTranslation +void func_800A6330(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, s32 setTranslation) { + Skin_DrawImpl(actor, globalCtx, skin, postDraw, NULL, setTranslation, false, 0); +} + +// allows specifying OverrideLimbDraw, PostLimbDraw and setTranslation +void func_800A6360(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, + SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation) { + Skin_DrawImpl(actor, globalCtx, skin, postDraw, overrideLimbDraw, setTranslation, false, 0); +} + +// allows specifying OverrideLimbDraw, PostLimbDraw, setTranslation, and arg6 +void func_800A6394(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, + SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6) { + Skin_DrawImpl(actor, globalCtx, skin, postDraw, overrideLimbDraw, setTranslation, arg6, 0); +} + +// allows specifying all variables +void func_800A63CC(Actor* actor, GlobalContext* globalCtx, Skin* skin, SkinPostDraw postDraw, + SkinOverrideLimbDraw overrideLimbDraw, s32 setTranslation, s32 arg6, s32 drawFlags) { + Skin_DrawImpl(actor, globalCtx, skin, postDraw, overrideLimbDraw, setTranslation, arg6, drawFlags); +} + +void Skin_GetLimbPos(Skin* skin, s32 limbIndex, Vec3f* offset, Vec3f* dst) { + MtxF mtxf; + + SkinMatrix_MtxFMtxFMult(&skin->mtx, &gSkinLimbMatrices[limbIndex], &mtxf); + SkinMatrix_Vec3fMtxFMultXYZ(&mtxf, offset, dst); +} diff --git a/soh/src/code/z_skin_awb.c b/soh/src/code/z_skin_awb.c new file mode 100644 index 000000000..636de0f8c --- /dev/null +++ b/soh/src/code/z_skin_awb.c @@ -0,0 +1,214 @@ +#include "global.h" +#include "overlays/actors/ovl_En_fHG/z_en_fhg.h" + +/** + * Initialises the Vtx buffers used for limb at index `limbIndex` + */ +void Skin_InitAnimatedLimb(GlobalContext* globalCtx, Skin* skin, s32 limbIndex) { + s32 i; + SkinLimb** skeleton = SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + SkinAnimatedLimbData* animatedLimbData = + SEGMENTED_TO_VIRTUAL(((SkinLimb*)SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]))->segment); + SkinLimbModif* limbModifications = SEGMENTED_TO_VIRTUAL(animatedLimbData->limbModifications); + SkinLimbModif* modifEntry; + SkinVertex* skinVtxEntry; + + for (i = 0; i < ARRAY_COUNT(skin->vtxTable->buf); i++) { + Vtx* vtxBuf = skin->vtxTable[limbIndex].buf[i]; + + for (modifEntry = limbModifications; modifEntry < limbModifications + animatedLimbData->limbModifCount; + modifEntry++) { + SkinVertex* skinVertices = SEGMENTED_TO_VIRTUAL(modifEntry->skinVertices); + + for (skinVtxEntry = skinVertices; skinVtxEntry < &skinVertices[modifEntry->vtxCount]; skinVtxEntry++) { + Vtx* vtx = &vtxBuf[skinVtxEntry->index]; + + vtx->n.flag = 0; + vtx->n.tc[0] = skinVtxEntry->s; + vtx->n.tc[1] = skinVtxEntry->t; + vtx->n.a = skinVtxEntry->alpha; + } + } + } +} + +/** + * Initializes a skin skeleton to looping animation, dynamically allocating the frame tables, + * and dynamically allocating and initializing the Vtx and SkinLimbVtx buffers for its animated limbs + */ +void Skin_Init(GlobalContext* globalCtx, Skin* skin, SkeletonHeader* skeletonHeader, AnimationHeader* animationHeader) { + if (ResourceMgr_OTRSigCheck(skeletonHeader)) + skeletonHeader = ResourceMgr_LoadSkeletonByName(skeletonHeader); + + s32 limbCount; + s32 i; + SkinLimb** skeleton; + SkeletonHeader* virtSkelHeader = SEGMENTED_TO_VIRTUAL(skeletonHeader); + + skin->limbCount = virtSkelHeader->limbCount; + skin->skeletonHeader = virtSkelHeader; + + skeleton = SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + limbCount = skin->skeletonHeader->limbCount; + + skin->vtxTable = ZeldaArena_MallocDebug(limbCount * sizeof(SkinLimbVtx), "../z_skin_awb.c", 212); + + ASSERT(skin->vtxTable != NULL, "pskin_awb->avb_tbl != NULL", "../z_skin_awb.c", 214); + + for (i = 0; i < limbCount; i++) { + SkinLimbVtx* vtxEntry = &skin->vtxTable[i]; + SkinLimb* limb = SEGMENTED_TO_VIRTUAL(skeleton[i]); + + if ((limb->segmentType != SKIN_LIMB_TYPE_ANIMATED) || (limb->segment == NULL)) { + vtxEntry->index = 0; + + vtxEntry->buf[0] = NULL; + vtxEntry->buf[1] = NULL; + } else { + SkinAnimatedLimbData* animatedLimbData = SEGMENTED_TO_VIRTUAL(((void)0, limb->segment)); + + vtxEntry->index = 0; + + vtxEntry->buf[0] = + ZeldaArena_MallocDebug(animatedLimbData->totalVtxCount * sizeof(Vtx), "../z_skin_awb.c", 235); + ASSERT(vtxEntry->buf[0] != NULL, "psavb->buf[0] != NULL", "../z_skin_awb.c", 237); + + vtxEntry->buf[1] = + ZeldaArena_MallocDebug(animatedLimbData->totalVtxCount * sizeof(Vtx), "../z_skin_awb.c", 240); + ASSERT(vtxEntry->buf[1] != NULL, "psavb->buf[1] != NULL", "../z_skin_awb.c", 242); + + Skin_InitAnimatedLimb(globalCtx, skin, i); + } + } + + SkelAnime_InitSkin(globalCtx, &skin->skelAnime, skeletonHeader, animationHeader); +} + +/** + * Frees the dynamically allocated Vtx and SkinLimbVtx buffers and tables + */ +void Skin_Free(GlobalContext* globalCtx, Skin* skin) { + if (skin->vtxTable != NULL) { + s32 i; + + for (i = 0; i < skin->limbCount; i++) { + if (skin->vtxTable[i].buf[0] != NULL) { + ZeldaArena_FreeDebug(skin->vtxTable[i].buf[0], "../z_skin_awb.c", 276); + skin->vtxTable[i].buf[0] = NULL; + } + if (skin->vtxTable[i].buf[1] != NULL) { + ZeldaArena_FreeDebug(skin->vtxTable[i].buf[1], "../z_skin_awb.c", 280); + skin->vtxTable[i].buf[1] = NULL; + } + } + + if (skin->vtxTable != NULL) { + ZeldaArena_FreeDebug(skin->vtxTable, "../z_skin_awb.c", 286); + } + + SkelAnime_Free(&skin->skelAnime, globalCtx); + } +} + +s32 func_800A698C(Skin* skin, SkinLimb** skeleton, MtxF* limbMatrices, u8 parentIndex, u8 limbIndex) { + s32 pad; + SkinLimb* limb = SEGMENTED_TO_VIRTUAL(skeleton[limbIndex]); + MtxF* mtx; + s32 ret; + MtxF sp28; + + if (parentIndex == LIMB_DONE) { + SkinMatrix_GetClear(&mtx); + } else { + mtx = &limbMatrices[(s32)parentIndex]; + } + + SkinMatrix_MtxFMtxFMult(mtx, &limbMatrices[limbIndex], &sp28); + SkinMatrix_MtxFCopy(&sp28, &limbMatrices[limbIndex]); + + if (limb->child != LIMB_DONE) { + ret = func_800A698C(skin, skeleton, limbMatrices, limbIndex, limb->child); + if (ret) { + return ret; + } + } + + if (limb->sibling != LIMB_DONE) { + ret = func_800A698C(skin, skeleton, limbMatrices, parentIndex, limb->sibling); + if (ret) { + return ret; + } + } + + return false; +} + +/** + * Recursively applies matrix tranformations to each limb + */ +s32 Skin_ApplyAnimTransformations(Skin* skin, MtxF* limbMatrices, Actor* actor, s32 setTranslation) { + s32 i; + s32 pad; + f32 yRot; + f32 xRot; + f32 zRot; + s32 ret; + f32 yTransl; + f32 xTransl; + f32 zTransl; + SkinLimb** skeleton = SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment); + Vec3s* jointRot = &skin->skelAnime.jointTable[0]; + + jointRot++; + + xRot = jointRot->x; + yRot = jointRot->y; + zRot = jointRot->z; + + if (setTranslation) { + jointRot--; // access joint table entry 0 for translation data + + xTransl = jointRot->x; + yTransl = jointRot->y; + zTransl = jointRot->z; + + jointRot++; + + if (setTranslation == SKIN_TRANSFORM_IS_FHG) { + EnfHG* horse = (EnfHG*)actor; + + yRot += horse->turnRot; + } + + SkinMatrix_SetTranslateRotateZYX(&limbMatrices[0], xRot, yRot, zRot, xTransl, yTransl, zTransl); + } else { + SkinMatrix_SetTranslateRotateZYX(&limbMatrices[0], xRot, yRot, zRot, 0.0f, 0.0f, 0.0f); + } + + jointRot++; + + for (i = 1; i < skin->skeletonHeader->limbCount; i++) { + SkinLimb* limb = SEGMENTED_TO_VIRTUAL(skeleton[i]); + + xTransl = limb->jointPos.x; + yTransl = limb->jointPos.y; + zTransl = limb->jointPos.z; + xRot = jointRot->x; + yRot = jointRot->y; + zRot = jointRot->z; + jointRot++; + SkinMatrix_SetTranslateRotateZYX(&limbMatrices[i], xRot, yRot, zRot, xTransl, yTransl, zTransl); + } + + SkinMatrix_SetTranslateRotateYXZScale( + &skin->mtx, actor->scale.x, actor->scale.y, actor->scale.z, actor->shape.rot.x, actor->shape.rot.y, + actor->shape.rot.z, actor->world.pos.x, actor->world.pos.y + (actor->shape.yOffset * actor->scale.y), + actor->world.pos.z); + + ret = func_800A698C(skin, SEGMENTED_TO_VIRTUAL(skin->skeletonHeader->segment), limbMatrices, LIMB_DONE, 0); + if (!ret) { + return ret; + } + + return false; +} diff --git a/soh/src/code/z_skin_matrix.c b/soh/src/code/z_skin_matrix.c new file mode 100644 index 000000000..f7df8091f --- /dev/null +++ b/soh/src/code/z_skin_matrix.c @@ -0,0 +1,630 @@ +#include "global.h" +#include "vt.h" + +// clang-format off +MtxF sMtxFClear = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; +// clang-format on + +/** + * Multiplies the matrix mf by a 4 components column vector [ src , 1 ] and writes the resulting 4 components to xyzDest + * and wDest. + * + * \f[ \begin{bmatrix} \texttt{xyzDest} \\ \texttt{wDest} \\ \end{bmatrix} + * = [\texttt{mf}] \cdot + * \begin{bmatrix} \texttt{src} \\ 1 \\ \end{bmatrix} + * \f] + */ +void SkinMatrix_Vec3fMtxFMultXYZW(MtxF* mf, Vec3f* src, Vec3f* xyzDest, f32* wDest) { + xyzDest->x = mf->xw + ((src->x * mf->xx) + (src->y * mf->xy) + (src->z * mf->xz)); + xyzDest->y = mf->yw + ((src->x * mf->yx) + (src->y * mf->yy) + (src->z * mf->yz)); + xyzDest->z = mf->zw + ((src->x * mf->zx) + (src->y * mf->zy) + (src->z * mf->zz)); + *wDest = mf->ww + ((src->x * mf->wx) + (src->y * mf->wy) + (src->z * mf->wz)); +} + +/** + * Multiplies the matrix mf by a 4 components column vector [ src , 1 ] and writes the resulting xyz components to dest. + * + * \f[ \begin{bmatrix} \texttt{dest} \\ - \\ \end{bmatrix} + * = [\texttt{mf}] \cdot + * \begin{bmatrix} \texttt{src} \\ 1 \\ \end{bmatrix} + * \f] + */ +void SkinMatrix_Vec3fMtxFMultXYZ(MtxF* mf, Vec3f* src, Vec3f* dest) { + f32 mx = mf->xx; + f32 my = mf->xy; + f32 mz = mf->xz; + f32 mw = mf->xw; + + dest->x = mw + ((src->x * mx) + (src->y * my) + (src->z * mz)); + mx = mf->yx; + my = mf->yy; + mz = mf->yz; + mw = mf->yw; + dest->y = mw + ((src->x * mx) + (src->y * my) + (src->z * mz)); + mx = mf->zx; + my = mf->zy; + mz = mf->zz; + mw = mf->zw; + dest->z = mw + ((src->x * mx) + (src->y * my) + (src->z * mz)); +} + +/** + * Matrix multiplication, dest = mfA * mfB. + * mfB and dest should not be the same matrix. + */ +void SkinMatrix_MtxFMtxFMult(MtxF* mfA, MtxF* mfB, MtxF* dest) { + f32 cx; + f32 cy; + f32 cz; + f32 cw; + //---ROW1--- + f32 rx = mfA->xx; + f32 ry = mfA->xy; + f32 rz = mfA->xz; + f32 rw = mfA->xw; + //-------- + + cx = mfB->xx; + cy = mfB->yx; + cz = mfB->zx; + cw = mfB->wx; + dest->xx = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xy; + cy = mfB->yy; + cz = mfB->zy; + cw = mfB->wy; + dest->xy = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xz; + cy = mfB->yz; + cz = mfB->zz; + cw = mfB->wz; + dest->xz = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xw; + cy = mfB->yw; + cz = mfB->zw; + cw = mfB->ww; + dest->xw = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + //---ROW2--- + rx = mfA->yx; + ry = mfA->yy; + rz = mfA->yz; + rw = mfA->yw; + //-------- + cx = mfB->xx; + cy = mfB->yx; + cz = mfB->zx; + cw = mfB->wx; + dest->yx = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xy; + cy = mfB->yy; + cz = mfB->zy; + cw = mfB->wy; + dest->yy = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xz; + cy = mfB->yz; + cz = mfB->zz; + cw = mfB->wz; + dest->yz = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xw; + cy = mfB->yw; + cz = mfB->zw; + cw = mfB->ww; + dest->yw = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + //---ROW3--- + rx = mfA->zx; + ry = mfA->zy; + rz = mfA->zz; + rw = mfA->zw; + //-------- + cx = mfB->xx; + cy = mfB->yx; + cz = mfB->zx; + cw = mfB->wx; + dest->zx = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xy; + cy = mfB->yy; + cz = mfB->zy; + cw = mfB->wy; + dest->zy = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xz; + cy = mfB->yz; + cz = mfB->zz; + cw = mfB->wz; + dest->zz = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xw; + cy = mfB->yw; + cz = mfB->zw; + cw = mfB->ww; + dest->zw = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + //---ROW4--- + rx = mfA->wx; + ry = mfA->wy; + rz = mfA->wz; + rw = mfA->ww; + //-------- + cx = mfB->xx; + cy = mfB->yx; + cz = mfB->zx; + cw = mfB->wx; + dest->wx = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xy; + cy = mfB->yy; + cz = mfB->zy; + cw = mfB->wy; + dest->wy = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xz; + cy = mfB->yz; + cz = mfB->zz; + cw = mfB->wz; + dest->wz = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); + + cx = mfB->xw; + cy = mfB->yw; + cz = mfB->zw; + cw = mfB->ww; + dest->ww = (rx * cx) + (ry * cy) + (rz * cz) + (rw * cw); +} + +/** + * "Clear" in this file means the identity matrix. + */ +void SkinMatrix_GetClear(MtxF** mfp) { + *mfp = &sMtxFClear; +} + +void SkinMatrix_Clear(MtxF* mf) { + mf->xx = 1.0f; + mf->yy = 1.0f; + mf->zz = 1.0f; + mf->ww = 1.0f; + mf->yx = 0.0f; + mf->zx = 0.0f; + mf->wx = 0.0f; + mf->xy = 0.0f; + mf->zy = 0.0f; + mf->wy = 0.0f; + mf->xz = 0.0f; + mf->yz = 0.0f; + mf->wz = 0.0f; + mf->xw = 0.0f; + mf->yw = 0.0f; + mf->zw = 0.0f; +} + +void SkinMatrix_MtxFCopy(MtxF* src, MtxF* dest) { + dest->xx = src->xx; + dest->yx = src->yx; + dest->zx = src->zx; + dest->wx = src->wx; + dest->xy = src->xy; + dest->yy = src->yy; + dest->zy = src->zy; + dest->wy = src->wy; + dest->xz = src->xz; + dest->yz = src->yz; + dest->zz = src->zz; + dest->wz = src->wz; + dest->xw = src->xw; + dest->yw = src->yw; + dest->zw = src->zw; + dest->ww = src->ww; +} + +/** + * Inverts a matrix using the Gauss-Jordan method. + * returns 0 if successfully inverted + * returns 2 if matrix non-invertible (0 determinant) + */ +s32 SkinMatrix_Invert(MtxF* src, MtxF* dest) { + MtxF mfCopy; + s32 i; + s32 pad; + f32 temp2; + f32 temp1; + s32 thisCol; + s32 thisRow; + + SkinMatrix_MtxFCopy(src, &mfCopy); + SkinMatrix_Clear(dest); + for (thisCol = 0; thisCol < 4; thisCol++) { + thisRow = thisCol; + while ((thisRow < 4) && (fabsf(mfCopy.mf[thisCol][thisRow]) < 0.0005f)) { + thisRow++; + } + if (thisRow == 4) { + // Reaching row = 4 means the column is either all 0 or a duplicate column. + // Therefore src is a singular matrix (0 determinant). + + osSyncPrintf(VT_COL(YELLOW, BLACK)); + osSyncPrintf("Skin_Matrix_InverseMatrix():逆行列つくれません\n"); + osSyncPrintf(VT_RST); + return 2; + } + + if (thisRow != thisCol) { + // Diagonal element mf[thisCol][thisCol] is zero. + // Swap the rows thisCol and thisRow. + for (i = 0; i < 4; i++) { + temp1 = mfCopy.mf[i][thisRow]; + mfCopy.mf[i][thisRow] = mfCopy.mf[i][thisCol]; + mfCopy.mf[i][thisCol] = temp1; + + temp2 = dest->mf[i][thisRow]; + dest->mf[i][thisRow] = dest->mf[i][thisCol]; + dest->mf[i][thisCol] = temp2; + } + } + + // Scale this whole row such that the diagonal element is 1. + temp1 = mfCopy.mf[thisCol][thisCol]; + for (i = 0; i < 4; i++) { + mfCopy.mf[i][thisCol] /= temp1; + dest->mf[i][thisCol] /= temp1; + } + + for (thisRow = 0; thisRow < 4; thisRow++) { + if (thisRow != thisCol) { + temp1 = mfCopy.mf[thisCol][thisRow]; + for (i = 0; i < 4; i++) { + mfCopy.mf[i][thisRow] -= mfCopy.mf[i][thisCol] * temp1; + dest->mf[i][thisRow] -= dest->mf[i][thisCol] * temp1; + } + } + } + } + return 0; +} + +/** + * Produces a matrix which scales x,y,z components of vectors or x,y,z rows of matrices (when applied on LHS) + */ +void SkinMatrix_SetScale(MtxF* mf, f32 x, f32 y, f32 z) { + mf->yx = 0.0f; + mf->zx = 0.0f; + mf->wx = 0.0f; + mf->xy = 0.0f; + mf->zy = 0.0f; + mf->wy = 0.0f; + mf->xz = 0.0f; + mf->yz = 0.0f; + mf->wz = 0.0f; + mf->xw = 0.0f; + mf->yw = 0.0f; + mf->zw = 0.0f; + mf->ww = 1.0f; + mf->xx = x; + mf->yy = y; + mf->zz = z; +} + +/** + * Produces a rotation matrix using ZYX Tait-Bryan angles. + */ +void SkinMatrix_SetRotateZYX(MtxF* mf, s16 x, s16 y, s16 z) { + f32 cos; + f32 sinZ = Math_SinS(z); + f32 cosZ = Math_CosS(z); + f32 xy; + f32 sin; + f32 xz; + f32 yy; + f32 yz; + + mf->yy = cosZ; + mf->xy = -sinZ; + mf->wx = mf->wy = mf->wz = 0; + mf->xw = mf->yw = mf->zw = 0; + mf->ww = 1; + + if (y != 0) { + sin = Math_SinS(y); + cos = Math_CosS(y); + + mf->xx = cosZ * cos; + mf->xz = cosZ * sin; + + mf->yx = sinZ * cos; + mf->yz = sinZ * sin; + mf->zx = -sin; + mf->zz = cos; + + } else { + mf->xx = cosZ; + if (1) {} + if (1) {} + xz = sinZ; // required to match + mf->yx = sinZ; + mf->zx = mf->xz = mf->yz = 0; + mf->zz = 1; + } + + if (x != 0) { + sin = Math_SinS(x); + cos = Math_CosS(x); + + xy = mf->xy; + xz = mf->xz; + mf->xy = (xy * cos) + (xz * sin); + mf->xz = (xz * cos) - (xy * sin); + + if (1) {} + yz = mf->yz; + yy = mf->yy; + mf->yy = (yy * cos) + (yz * sin); + mf->yz = (yz * cos) - (yy * sin); + + if (cos) {} + mf->zy = mf->zz * sin; + mf->zz = mf->zz * cos; + } else { + mf->zy = 0; + } +} + +/** + * Produces a rotation matrix using YXZ Tait-Bryan angles. + */ +void SkinMatrix_SetRotateYXZ(MtxF* mf, s16 x, s16 y, s16 z) { + f32 cos; + f32 sinY = Math_SinS(y); + f32 cosY = Math_CosS(y); + f32 zx; + f32 sin; + f32 zy; + f32 xx; + f32 xy; + + mf->xx = cosY; + mf->zx = -sinY; + mf->wz = 0; + mf->wy = 0; + mf->wx = 0; + mf->zw = 0; + mf->yw = 0; + mf->xw = 0; + mf->ww = 1; + + if (x != 0) { + sin = Math_SinS(x); + cos = Math_CosS(x); + + mf->zz = cosY * cos; + mf->zy = cosY * sin; + + mf->xz = sinY * cos; + mf->xy = sinY * sin; + mf->yz = -sin; + mf->yy = cos; + + } else { + mf->zz = cosY; + if (1) {} + if (1) {} + xy = sinY; // required to match + mf->xz = sinY; + mf->xy = mf->zy = mf->yz = 0; + mf->yy = 1; + } + + if (z != 0) { + sin = Math_SinS(z); + cos = Math_CosS(z); + xx = mf->xx; + xy = mf->xy; + mf->xx = (xx * cos) + (xy * sin); + mf->xy = xy * cos - (xx * sin); + if (1) {} + zy = mf->zy; + zx = mf->zx; + mf->zx = (zx * cos) + (zy * sin); + mf->zy = (zy * cos) - (zx * sin); + if (cos) {} + mf->yx = mf->yy * sin; + mf->yy = mf->yy * cos; + } else { + mf->yx = 0; + } +} + +/** + * Produces a matrix which translates a vector by amounts in the x, y and z directions + */ +void SkinMatrix_SetTranslate(MtxF* mf, f32 x, f32 y, f32 z) { + mf->yx = 0.0f; + mf->zx = 0.0f; + mf->wx = 0.0f; + mf->xy = 0.0f; + mf->zy = 0.0f; + mf->wy = 0.0f; + mf->xz = 0.0f; + mf->yz = 0.0f; + mf->wz = 0.0f; + mf->xx = 1.0f; + mf->yy = 1.0f; + mf->zz = 1.0f; + mf->ww = 1.0f; + mf->xw = x; + mf->yw = y; + mf->zw = z; +} + +/** + * Produces a matrix which scales, then rotates (using ZYX Tait-Bryan angles), then translates. + */ +void SkinMatrix_SetTranslateRotateZYXScale(MtxF* dest, f32 scaleX, f32 scaleY, f32 scaleZ, s16 rotX, s16 rotY, s16 rotZ, + f32 translateX, f32 translateY, f32 translateZ) { + MtxF mft1; + MtxF mft2; + + SkinMatrix_SetTranslate(dest, translateX, translateY, translateZ); + SkinMatrix_SetRotateZYX(&mft1, rotX, rotY, rotZ); + SkinMatrix_MtxFMtxFMult(dest, &mft1, &mft2); + SkinMatrix_SetScale(&mft1, scaleX, scaleY, scaleZ); + SkinMatrix_MtxFMtxFMult(&mft2, &mft1, dest); +} + +/** + * Produces a matrix which scales, then rotates (using YXZ Tait-Bryan angles), then translates. + */ +void SkinMatrix_SetTranslateRotateYXZScale(MtxF* dest, f32 scaleX, f32 scaleY, f32 scaleZ, s16 rotX, s16 rotY, s16 rotZ, + f32 translateX, f32 translateY, f32 translateZ) { + MtxF mft1; + MtxF mft2; + + SkinMatrix_SetTranslate(dest, translateX, translateY, translateZ); + SkinMatrix_SetRotateYXZ(&mft1, rotX, rotY, rotZ); + SkinMatrix_MtxFMtxFMult(dest, &mft1, &mft2); + SkinMatrix_SetScale(&mft1, scaleX, scaleY, scaleZ); + SkinMatrix_MtxFMtxFMult(&mft2, &mft1, dest); +} + +/** + * Produces a matrix which rotates (using ZYX Tait-Bryan angles), then translates. + */ +void SkinMatrix_SetTranslateRotateZYX(MtxF* dest, s16 rotX, s16 rotY, s16 rotZ, f32 translateX, f32 translateY, + f32 translateZ) { + MtxF rotation; + MtxF translation; + + SkinMatrix_SetTranslate(&translation, translateX, translateY, translateZ); + SkinMatrix_SetRotateZYX(&rotation, rotX, rotY, rotZ); + SkinMatrix_MtxFMtxFMult(&translation, &rotation, dest); +} + +void SkinMatrix_Vec3fToVec3s(Vec3f* src, Vec3s* dest) { + dest->x = src->x; + dest->y = src->y; + dest->z = src->z; +} + +void SkinMatrix_Vec3sToVec3f(Vec3s* src, Vec3f* dest) { + dest->x = src->x; + dest->y = src->y; + dest->z = src->z; +} + +void SkinMatrix_MtxFToMtx(MtxF* src, Mtx* dest) { + guMtxF2L(src, dest); +} + +Mtx* SkinMatrix_MtxFToNewMtx(GraphicsContext* gfxCtx, MtxF* src) { + Mtx* mtx = Graph_Alloc(gfxCtx, sizeof(Mtx)); + + if (mtx == NULL) { + osSyncPrintf("Skin_Matrix_to_Mtx_new() 確保失敗:NULLを返して終了\n", mtx); + return NULL; + } + SkinMatrix_MtxFToMtx(src, mtx); + return mtx; +} + +/** + * Produces a matrix which rotates by binary angle `angle` around a unit vector (`axisX`,`axisY`,`axisZ`). + * NB: the rotation axis is assumed to be a unit vector. + */ +void SkinMatrix_SetRotateAxis(MtxF* mf, s16 angle, f32 axisX, f32 axisY, f32 axisZ) { + f32 sinA; + f32 cosA; + f32 xx; + f32 yy; + f32 zz; + f32 xy; + f32 yz; + f32 xz; + f32 pad; + + sinA = Math_SinS(angle); + cosA = Math_CosS(angle); + + xx = axisX * axisX; + yy = axisY * axisY; + zz = axisZ * axisZ; + xy = axisX * axisY; + yz = axisY * axisZ; + xz = axisX * axisZ; + + mf->xx = (1.0f - xx) * cosA + xx; + mf->yx = (1.0f - cosA) * xy + axisZ * sinA; + mf->zx = (1.0f - cosA) * xz - axisY * sinA; + mf->wx = 0.0f; + + mf->xy = (1.0f - cosA) * xy - axisZ * sinA; + mf->yy = (1.0f - yy) * cosA + yy; + mf->zy = (1.0f - cosA) * yz + axisX * sinA; + mf->wy = 0.0f; + + mf->xz = (1.0f - cosA) * xz + axisY * sinA; + mf->yz = (1.0f - cosA) * yz - axisX * sinA; + mf->zz = (1.0f - zz) * cosA + zz; + mf->wz = 0.0f; + + mf->xw = mf->yw = mf->zw = 0.0f; + mf->ww = 1.0f; +} + +void func_800A8030(MtxF* mf, f32* arg1) { + f32 n; + f32 xNorm; + f32 yNorm; + f32 zNorm; + f32 wxNorm; + f32 wyNorm; + f32 wzNorm; + f32 xxNorm; + f32 xyNorm; + f32 xzNorm; + f32 yyNorm; + f32 yzNorm; + f32 zzNorm; + + n = 2.0f / ((arg1[3] * arg1[3]) + ((arg1[2] * arg1[2]) + ((arg1[1] * arg1[1]) + (arg1[0] * arg1[0])))); + xNorm = arg1[0] * n; + yNorm = arg1[1] * n; + zNorm = arg1[2] * n; + + wxNorm = arg1[3] * xNorm; + wyNorm = arg1[3] * yNorm; + wzNorm = arg1[3] * zNorm; + xxNorm = arg1[0] * xNorm; + xyNorm = arg1[0] * yNorm; + xzNorm = arg1[0] * zNorm; + yyNorm = arg1[1] * yNorm; + yzNorm = arg1[1] * zNorm; + zzNorm = arg1[2] * zNorm; + + mf->xx = (1.0f - (yyNorm + zzNorm)); + mf->yx = (xyNorm + wzNorm); + mf->zx = (xzNorm - wyNorm); + mf->wx = 0.0f; + mf->xy = (xyNorm - wzNorm); + mf->yy = (1.0f - (xxNorm + zzNorm)); + mf->zy = (yzNorm + wxNorm); + mf->wy = 0.0f; + mf->xz = (yzNorm + wyNorm); + mf->yz = (yzNorm - wxNorm); + mf->zz = (1.0f - (xxNorm + yyNorm)); + mf->wz = 0.0f; + mf->xw = 0.0f; + mf->yw = 0.0f; + mf->ww = 1.0f; + mf->zw = 0.0f; +} diff --git a/soh/src/code/z_sound_source.c b/soh/src/code/z_sound_source.c new file mode 100644 index 000000000..ce54ee393 --- /dev/null +++ b/soh/src/code/z_sound_source.c @@ -0,0 +1,62 @@ +#include "global.h" + +void SoundSource_InitAll(GlobalContext* globalCtx) { + SoundSource* sources = &globalCtx->soundSources[0]; + s32 i; + + // clang-format off + for (i = 0; i < ARRAY_COUNT(globalCtx->soundSources); i++) { sources[i].countdown = 0; } + // clang-format on +} + +void SoundSource_UpdateAll(GlobalContext* globalCtx) { + SoundSource* source = &globalCtx->soundSources[0]; + s32 i; + + for (i = 0; i < ARRAY_COUNT(globalCtx->soundSources); i++) { + if (source->countdown != 0) { + if (DECR(source->countdown) == 0) { + Audio_StopSfxByPos(&source->projectedPos); + } else { + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &source->worldPos, &source->projectedPos); + } + } + + source++; + } +} + +void SoundSource_PlaySfxAtFixedWorldPos(GlobalContext* globalCtx, Vec3f* worldPos, s32 duration, u16 sfxId) { + s32 countdown; + SoundSource* source; + s32 smallestCountdown = 0xFFFF; + SoundSource* backupSource; + s32 i; + + source = &globalCtx->soundSources[0]; + for (i = 0; i < ARRAY_COUNT(globalCtx->soundSources); i++) { + if (source->countdown == 0) { + break; + } + + // Store the sound source with the smallest remaining countdown + countdown = source->countdown; + if (countdown < smallestCountdown) { + smallestCountdown = countdown; + backupSource = source; + } + source++; + } + + // If no sound source is available, replace the sound source with the smallest remaining countdown + if (i >= ARRAY_COUNT(globalCtx->soundSources)) { + source = backupSource; + Audio_StopSfxByPos(&source->projectedPos); + } + + source->worldPos = *worldPos; + source->countdown = duration; + + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &source->worldPos, &source->projectedPos); + Audio_PlaySoundGeneral(sfxId, &source->projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c new file mode 100644 index 000000000..a774636f7 --- /dev/null +++ b/soh/src/code/z_sram.c @@ -0,0 +1,881 @@ +#include "global.h" +#include "vt.h" + +#include + +// these are the main substructs of save context. +// we are going to hold off on splitting save context until later on, +// so these temporary structs will live here for now. + +typedef struct { + /* 0x00 */ char newf[6]; // string "ZELDAZ" + /* 0x06 */ s16 deaths; + /* 0x08 */ char playerName[8]; + /* 0x10 */ s16 n64ddFlag; + /* 0x12 */ s16 healthCapacity; // "max_life" + /* 0x14 */ s16 health; // "now_life" + /* 0x16 */ s8 magicLevel; + /* 0x17 */ s8 magic; + /* 0x18 */ s16 rupees; + /* 0x1A */ u16 swordHealth; + /* 0x1C */ u16 naviTimer; + /* 0x1E */ u8 magicAcquired; + /* 0x1F */ u8 unk_1F; + /* 0x20 */ u8 doubleMagic; + /* 0x21 */ u8 doubleDefense; + /* 0x22 */ u8 bgsFlag; + /* 0x23 */ u8 ocarinaGameRoundNum; + /* 0x24 */ ItemEquips childEquips; + /* 0x2E */ ItemEquips adultEquips; + /* 0x38 */ u32 unk_38; // this may be incorrect, currently used for alignement + /* 0x3C */ char unk_3C[0x0E]; + /* 0x4A */ s16 savedSceneNum; +} SavePlayerData; // size = 0x4C + +typedef struct { + /* 0x0000 */ SavePlayerData playerData; // "S_Private" substruct name + /* 0x004C */ ItemEquips equips; + /* 0x0058 */ Inventory inventory; + /* 0x00B8 */ SavedSceneFlags sceneFlags[124]; + /* 0x0E48 */ FaroresWindData fw; + /* 0x0E70 */ char unk_E70[0x10]; + /* 0x0E80 */ s32 gsFlags[6]; + /* 0x0E98 */ char unk_E98[0x10]; + /* 0x0EA8 */ s32 horseRaceRecord; + /* 0x0EAC */ char unk_EAC[0x0C]; + /* 0x0EB8 */ u16 eventChkInf[14]; // "event_chk_inf" + /* 0x0ED4 */ u16 itemGetInf[4]; // "item_get_inf" + /* 0x0EDC */ u16 infTable[30]; // "inf_table" + /* 0x0F18 */ char unk_F18[0x04]; + /* 0x0F1C */ u32 worldMapAreaData; // "area_arrival" + /* 0x0F20 */ char unk_F20[0x4]; + /* 0x0F24 */ u8 scarecrowCustomSongSet; + /* 0x0F25 */ u8 scarecrowCustomSong[0x360]; + /* 0x1285 */ char unk_1285[0x24]; + /* 0x12A9 */ u8 scarecrowSpawnSongSet; + /* 0x12AA */ u8 scarecrowSpawnSong[0x80]; + /* 0x132A */ char unk_132A[0x02]; + /* 0x132C */ HorseData horseData; + /* 0x1336 */ u16 checksum; // "check_sum" +} SaveInfo; // size = 0x1338 + +typedef struct { + /* 0x00 */ s32 entranceIndex; + /* 0x04 */ s32 linkAge; // 0: Adult; 1: Child + /* 0x08 */ s32 cutsceneIndex; + /* 0x0C */ u16 dayTime; // "zelda_time" + /* 0x10 */ s32 nightFlag; + /* 0x14 */ s32 totalDays; + /* 0x18 */ s32 unk_18; // increments with totalDays, gets reset by goron for bgs and one other use + /* 0x1C */ SaveInfo info; // "information" +} Save; // size = 0x1354 + +#define SAVE_PLAYER_DATA (*((SavePlayerData*)&gSaveContext.newf)) +#define SAVE_INFO (*((SaveInfo*)&gSaveContext.newf)) + +#define SLOT_SIZE (sizeof(SaveContext) + 0x28) +#define CHECKSUM_SIZE (sizeof(Save) / 2) + +#define DEATHS OFFSETOF(SaveContext, deaths) +#define NAME OFFSETOF(SaveContext, playerName) +#define N64DD OFFSETOF(SaveContext, n64ddFlag) +#define HEALTH_CAP OFFSETOF(SaveContext, healthCapacity) +#define QUEST OFFSETOF(SaveContext, inventory.questItems) +#define DEFENSE OFFSETOF(SaveContext, inventory.defenseHearts) +#define HEALTH OFFSETOF(SaveContext, health) + +#define SLOT_OFFSET(index) (SRAM_HEADER_SIZE + 0x10 + (index * SLOT_SIZE)) + +u16 gSramSlotOffsets[] = { + SLOT_OFFSET(0), + SLOT_OFFSET(1), + SLOT_OFFSET(2), + // the latter three saves are backup saves for the former saves + SLOT_OFFSET(3), + SLOT_OFFSET(4), + SLOT_OFFSET(5), +}; + +static u8 sZeldaMagic[] = { '\0', '\0', '\0', '\x98', '\x09', '\x10', '\x21', 'Z', 'E', 'L', 'D', 'A' }; + +static SavePlayerData sNewSavePlayerData = { + { '\0', '\0', '\0', '\0', '\0', '\0' }, // newf + 0, // deaths + { 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E }, // playerName + 0, // n64ddFlag + 0x30, // healthCapacity + 0x30, // defense + 0, // magicLevel + 0x30, // magic + 0, // rupees + 0, // swordHealth + 0, // naviTimer + 0, // magicAcquired + 0, // unk_1F + 0, // doubleMagic + 0, // doubleDefense + 0, // bgsFlag + 0, // ocarinaGameRoundNum + { + { ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }, // buttonItems + { SLOT_NONE, SLOT_NONE, SLOT_NONE }, // cButtonSlots + 0, // equipment + }, // childEquips + { + { ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }, // buttonItems + { SLOT_NONE, SLOT_NONE, SLOT_NONE }, // cButtonSlots + 0, // equipment + }, // adultEquips + 0, // unk_38 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // unk_3C + 0x34, // savedSceneNum +}; + +static ItemEquips sNewSaveEquips = { + { ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }, // buttonItems + { SLOT_NONE, SLOT_NONE, SLOT_NONE }, // cButtonSlots + 0x1100, // equipment +}; + +static Inventory sNewSaveInventory = { + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // items + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // ammo + 0x1100, // equipment + 0, // upgrades + 0, // questItems + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // dungeonItems + { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + }, // dungeonKeys + 0, // defenseHearts + 0, // gsTokens +}; + +static u16 sNewSaveChecksum = 0; + +/** + * Initialize new save. + * This save has an empty inventory with 3 hearts and single magic. + */ +void Sram_InitNewSave(void) { + SaveContext* temp = &gSaveContext; + + memset(&SAVE_INFO, 0, sizeof(SaveInfo)); + gSaveContext.totalDays = 0; + gSaveContext.bgsDayCount = 0; + + SAVE_PLAYER_DATA = sNewSavePlayerData; + gSaveContext.equips = sNewSaveEquips; + gSaveContext.inventory = sNewSaveInventory; + + temp->checksum = sNewSaveChecksum; + gSaveContext.horseData.scene = SCENE_SPOT00; + gSaveContext.horseData.pos.x = -1840; + gSaveContext.horseData.pos.y = 72; + gSaveContext.horseData.pos.z = 5497; + gSaveContext.horseData.angle = -0x6AD9; + gSaveContext.magicLevel = 0; + gSaveContext.infTable[29] = 1; + gSaveContext.sceneFlags[5].swch = 0x40000000; +} + +static SavePlayerData sDebugSavePlayerData = { + { 'Z', 'E', 'L', 'D', 'A', 'Z' }, // newf + 0, // deaths + { 0x15, 0x12, 0x17, 0x14, 0x3E, 0x3E, 0x3E, 0x3E }, // playerName ( "LINK" ) + 0, // n64ddFlag + 0xE0, // healthCapacity + 0xE0, // health + 0, // magicLevel + 0x30, // magic + 150, // rupees + 8, // swordHealth + 0, // naviTimer + 1, // magicAcquired + 0, // unk_1F + 0, // doubleMagic + 0, // doubleDefense + 0, // bgsFlag + 0, // ocarinaGameRoundNum + { + { ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }, // buttonItems + { SLOT_NONE, SLOT_NONE, SLOT_NONE }, // cButtonSlots + 0, // equipment + }, // childEquips + { + { ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }, // buttonItems + { SLOT_NONE, SLOT_NONE, SLOT_NONE }, // cButtonSlots + 0, // equipment + }, // adultEquips + 0, // unk_38 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // unk_3C + 0x51, // savedSceneNum +}; + +static ItemEquips sDebugSaveEquips = { + { ITEM_SWORD_MASTER, ITEM_BOW, ITEM_BOMB, ITEM_OCARINA_FAIRY }, // buttonItems + { SLOT_BOW, SLOT_BOMB, SLOT_OCARINA }, // cButtonSlots + 0x1122, // equipment +}; + +static Inventory sDebugSaveInventory = { + { + ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_BOW, ITEM_ARROW_FIRE, ITEM_DINS_FIRE, + ITEM_SLINGSHOT, ITEM_OCARINA_FAIRY, ITEM_BOMBCHU, ITEM_HOOKSHOT, ITEM_ARROW_ICE, ITEM_FARORES_WIND, + ITEM_BOOMERANG, ITEM_LENS, ITEM_BEAN, ITEM_HAMMER, ITEM_ARROW_LIGHT, ITEM_NAYRUS_LOVE, + ITEM_BOTTLE, ITEM_POTION_RED, ITEM_POTION_GREEN, ITEM_POTION_BLUE, ITEM_POCKET_EGG, ITEM_WEIRD_EGG, + }, // items + { 50, 50, 10, 30, 1, 1, 30, 1, 50, 1, 1, 1, 1, 1, 1, 1 }, // ammo + 0x7777, // equipment + 0x125249, // upgrades + 0x1E3FFFF, // questItems + { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // dungeonItems + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 }, // dungeonKeys + 0, // defenseHearts + 0, // gsTokens +}; + +static u16 sDebugSaveChecksum = 0; + +/** + * Initialize debug save. This is also used on the Title Screen + * This save has a mostly full inventory with 10 hearts and single magic. + * + * Some noteable flags that are set: + * Showed Mido sword/shield, met Deku Tree, Deku Tree mouth opened, + * used blue warp in Gohmas room, Zelda fled castle, light arrow cutscene watched, + * and set water level in Water Temple to lowest level. + */ +void Sram_InitDebugSave(void) { + SaveContext* temp = &gSaveContext; + + memset(&SAVE_INFO, 0, sizeof(SaveInfo)); + gSaveContext.totalDays = 0; + gSaveContext.bgsDayCount = 0; + + SAVE_PLAYER_DATA = sDebugSavePlayerData; + gSaveContext.equips = sDebugSaveEquips; + gSaveContext.inventory = sDebugSaveInventory; + + temp->checksum = sDebugSaveChecksum; + gSaveContext.horseData.scene = SCENE_SPOT00; + gSaveContext.horseData.pos.x = -1840; + gSaveContext.horseData.pos.y = 72; + gSaveContext.horseData.pos.z = 5497; + gSaveContext.horseData.angle = -0x6AD9; + gSaveContext.infTable[0] |= 0x5009; + gSaveContext.eventChkInf[0] |= 0x123F; + gSaveContext.eventChkInf[8] |= 1; + gSaveContext.eventChkInf[12] |= 0x10; + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KOKIRI; + Inventory_ChangeEquipment(EQUIP_SWORD, 1); + if (gSaveContext.fileNum == 0xFF) { + gSaveContext.equips.buttonItems[1] = ITEM_SLINGSHOT; + gSaveContext.equips.cButtonSlots[0] = SLOT_SLINGSHOT; + Inventory_ChangeEquipment(EQUIP_SHIELD, 1); + } + } + + gSaveContext.entranceIndex = 0xCD; + gSaveContext.magicLevel = 0; + gSaveContext.sceneFlags[5].swch = 0x40000000; +} + +/** + * Copy save currently on the buffer to Save Context and complete various tasks to open the save. + * This includes: + * - Set proper entrance depending on where the game was saved + * - If health is less than 3 hearts, give 3 hearts + * - If either scarecrow song is set, copy them from save context to the proper location + * - Handle a case where the player saved and quit after zelda cutscene but didnt get the song + * - Give and equip master sword if player is adult and doesnt have kokiri sword (bug?) + * - Revert any trade items that spoil + */ +void Sram_OpenSave(SramContext* sramCtx) { + static s16 dungeonEntrances[] = { + 0x0000, 0x0004, 0x0028, 0x0169, 0x0165, 0x0010, 0x0082, 0x0037, + 0x0098, 0x0088, 0x041B, 0x0008, 0x0486, 0x0467, 0x0179, 0x056C, + }; + u16 i; + u16 j; + u8* ptr; + + osSyncPrintf("個人File作成\n"); // "Create personal file" + i = gSramSlotOffsets[gSaveContext.fileNum]; + osSyncPrintf("ぽいんと=%x(%d)\n", i, gSaveContext.fileNum); // "Point=" + + memcpy(&gSaveContext, sramCtx->readBuff + i, sizeof(Save)); + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("SCENE_DATA_ID = %d SceneNo = %d\n", gSaveContext.savedSceneNum, + ((void)0, gSaveContext.entranceIndex)); + + switch (gSaveContext.savedSceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_GANON: + case SCENE_MEN: + case SCENE_GERUDOWAY: + case SCENE_GANONTIKA: + gSaveContext.entranceIndex = dungeonEntrances[gSaveContext.savedSceneNum]; + break; + case SCENE_YDAN_BOSS: + gSaveContext.entranceIndex = 0; + break; + case SCENE_DDAN_BOSS: + gSaveContext.entranceIndex = 4; + break; + case SCENE_BDAN_BOSS: + gSaveContext.entranceIndex = 0x28; + break; + case SCENE_MORIBOSSROOM: + gSaveContext.entranceIndex = 0x169; + break; + case SCENE_FIRE_BS: + gSaveContext.entranceIndex = 0x165; + break; + case SCENE_MIZUSIN_BS: + gSaveContext.entranceIndex = 0x10; + break; + case SCENE_JYASINBOSS: + gSaveContext.entranceIndex = 0x82; + break; + case SCENE_HAKADAN_BS: + gSaveContext.entranceIndex = 0x37; + break; + case SCENE_GANON_SONOGO: + case SCENE_GANONTIKA_SONOGO: + case SCENE_GANON_BOSS: + case SCENE_GANON_FINAL: + case SCENE_GANON_DEMO: + gSaveContext.entranceIndex = 0x41B; + break; + + default: + if (gSaveContext.savedSceneNum != SCENE_LINK_HOME) { + gSaveContext.entranceIndex = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? 0xBB : 0x5F4; + } else { + gSaveContext.entranceIndex = 0xBB; + } + break; + } + + osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex); + osSyncPrintf(VT_RST); + + if (gSaveContext.health < 0x30) { + gSaveContext.health = 0x30; + } + + if (gSaveContext.scarecrowCustomSongSet) { + osSyncPrintf(VT_FGCOL(BLUE)); + osSyncPrintf("\n====================================================================\n"); + + memcpy(gScarecrowCustomSongPtr, gSaveContext.scarecrowCustomSong, sizeof(gSaveContext.scarecrowCustomSong)); + + ptr = (u8*)gScarecrowCustomSongPtr; + for (i = 0; i < ARRAY_COUNT(gSaveContext.scarecrowCustomSong); i++, ptr++) { + osSyncPrintf("%d, ", *ptr); + } + + osSyncPrintf("\n====================================================================\n"); + osSyncPrintf(VT_RST); + } + + if (gSaveContext.scarecrowSpawnSongSet) { + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("\n====================================================================\n"); + + memcpy(gScarecrowSpawnSongPtr, gSaveContext.scarecrowSpawnSong, sizeof(gSaveContext.scarecrowSpawnSong)); + + ptr = gScarecrowSpawnSongPtr; + for (i = 0; i < ARRAY_COUNT(gSaveContext.scarecrowSpawnSong); i++, ptr++) { + osSyncPrintf("%d, ", *ptr); + } + + osSyncPrintf("\n====================================================================\n"); + osSyncPrintf(VT_RST); + } + + // if zelda cutscene has been watched but lullaby was not obtained, restore cutscene and take away letter + if ((gSaveContext.eventChkInf[4] & 1) && !CHECK_QUEST_ITEM(QUEST_SONG_LULLABY)) { + i = gSaveContext.eventChkInf[4] & ~1; + gSaveContext.eventChkInf[4] = i; + + INV_CONTENT(ITEM_LETTER_ZELDA) = ITEM_CHICKEN; + + for (j = 1; j < 4; j++) { + if (gSaveContext.equips.buttonItems[j] == ITEM_LETTER_ZELDA) { + gSaveContext.equips.buttonItems[j] = ITEM_CHICKEN; + } + } + } + + // check for owning kokiri sword.. to restore master sword? bug or debug feature? + if (LINK_AGE_IN_YEARS == YEARS_ADULT && !CHECK_OWNED_EQUIP(EQUIP_SWORD, 1)) { + gSaveContext.inventory.equipment |= gBitFlags[1] << gEquipShifts[EQUIP_SWORD]; + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; + gSaveContext.equips.equipment &= ~0xF; + gSaveContext.equips.equipment |= 2; + } + + for (i = 0; i < ARRAY_COUNT(gSpoilingItems); i++) { + if (INV_CONTENT(ITEM_TRADE_ADULT) == gSpoilingItems[i]) { + INV_CONTENT(gSpoilingItemReverts[i]) = gSpoilingItemReverts[i]; + + for (j = 1; j < 4; j++) { + if (gSaveContext.equips.buttonItems[j] == gSpoilingItems[i]) { + gSaveContext.equips.buttonItems[j] = gSpoilingItemReverts[i]; + } + } + } + } + + gSaveContext.magicLevel = 0; +} + +/** + * Write the contents of the Save Context to a main and backup slot in SRAM. + * Note: The whole Save Context is written even though only the `save` substruct is read back later + */ +void Sram_WriteSave(SramContext* sramCtx) { + u16 offset; + u16 checksum; + u16 j; + u16* ptr; + + gSaveContext.checksum = 0; + + ptr = (u16*)&gSaveContext; + checksum = 0; + j = 0; + + for (offset = 0; offset < CHECKSUM_SIZE; offset++) { + if (++j == 0x20) { + j = 0; + } + checksum += *ptr++; + } + + gSaveContext.checksum = checksum; + + ptr = (u16*)&gSaveContext; + checksum = 0; + + for (offset = 0; offset < CHECKSUM_SIZE; offset++) { + if (++j == 0x20) { + j = 0; + } + checksum += *ptr++; + } + + offset = gSramSlotOffsets[gSaveContext.fileNum]; + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000) + offset, &gSaveContext, SLOT_SIZE, OS_WRITE); + + ptr = (u16*)&gSaveContext; + checksum = 0; + + for (offset = 0; offset < CHECKSUM_SIZE; offset++) { + if (++j == 0x20) { + j = 0; + } + checksum += *ptr++; + } + + offset = gSramSlotOffsets[gSaveContext.fileNum + 3]; + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000) + offset, &gSaveContext, SLOT_SIZE, OS_WRITE); +} + +/** + * For all 3 slots, verify that the checksum is correct. If corrupted, attempt to load a backup save. + * If backup is also corrupted, default to a new save (or debug save for slot 0 on debug rom). + * + * After verifying all 3 saves, pass relevant data to File Select to be displayed. + */ +void Sram_VerifyAndLoadAllSaves(FileChooseContext* fileChooseCtx, SramContext* sramCtx) { + u16 i; + u16 newChecksum; + u16 slotNum; + u16 offset; + u16 j; + u16 oldChecksum; + u16* ptr; + u16 dayTime; + + osSyncPrintf("SRAM START─LOAD\n"); + memset(sramCtx->readBuff,0, SRAM_SIZE); + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + + dayTime = ((void)0, gSaveContext.dayTime); + + for (slotNum = 0; slotNum < 3; slotNum++) { + offset = gSramSlotOffsets[slotNum]; + osSyncPrintf("ぽいんと=%x(%d) SAVE_MAX=%d\n", offset, gSaveContext.fileNum, sizeof(Save)); + memcpy(&gSaveContext, sramCtx->readBuff + offset, sizeof(Save)); + + oldChecksum = gSaveContext.checksum; + gSaveContext.checksum = 0; + ptr = (u16*)&gSaveContext; + osSyncPrintf("\n============= S(%d) =============\n", slotNum); + + for (i = newChecksum = j = 0; i < CHECKSUM_SIZE; i++, offset += 2) { + newChecksum += *ptr++; + } + + // "SAVE checksum calculation" + osSyncPrintf("\nSAVEチェックサム計算 j=%x mmm=%x ", newChecksum, oldChecksum); + + if (newChecksum != oldChecksum) { + // checksum didnt match, try backup save + osSyncPrintf("ERROR!!! = %x(%d)\n", gSramSlotOffsets[slotNum], slotNum); + offset = gSramSlotOffsets[slotNum + 3]; + memcpy(&gSaveContext, sramCtx->readBuff + offset, sizeof(Save)); + + oldChecksum = gSaveContext.checksum; + gSaveContext.checksum = 0; + ptr = (u16*)&gSaveContext; + osSyncPrintf("================= BACK─UP ========================\n"); + + for (i = newChecksum = j = 0; i < CHECKSUM_SIZE; i++, offset += 2) { + newChecksum += *ptr++; + } + // "(B) SAVE checksum calculation" + osSyncPrintf("\n(B)SAVEチェックサム計算 j=%x mmm=%x ", newChecksum, oldChecksum); + + if (newChecksum != oldChecksum) { + // backup save didnt work, make new save + osSyncPrintf("ERROR!!! = %x(%d+3)\n", gSramSlotOffsets[slotNum + 3], slotNum); + memset(&gSaveContext.entranceIndex, 0, sizeof(s32)); + memset(&gSaveContext.linkAge, 0, sizeof(s32)); + memset(&gSaveContext.cutsceneIndex, 0, sizeof(s32)); + // note that gSaveContext.dayTime is not actually the sizeof(s32) + memset(&gSaveContext.dayTime, 0, sizeof(s32)); + memset(&gSaveContext.nightFlag, 0, sizeof(s32)); + memset(&gSaveContext.totalDays, 0, sizeof(s32)); + memset(&gSaveContext.bgsDayCount, 0, sizeof(s32)); + + if (!slotNum && CVar_GetS32("gDebugEnabled", 0)) { + Sram_InitDebugSave(); + gSaveContext.newf[0] = 'Z'; + gSaveContext.newf[1] = 'E'; + gSaveContext.newf[2] = 'L'; + gSaveContext.newf[3] = 'D'; + gSaveContext.newf[4] = 'A'; + gSaveContext.newf[5] = 'Z'; + osSyncPrintf("newf=%x,%x,%x,%x,%x,%x\n", gSaveContext.newf[0], gSaveContext.newf[1], + gSaveContext.newf[2], gSaveContext.newf[3], gSaveContext.newf[4], + gSaveContext.newf[5]); + } else { + Sram_InitNewSave(); + } + + ptr = (u16*)&gSaveContext; + osSyncPrintf("\n--------------------------------------------------------------\n"); + + for (i = newChecksum = j = 0; i < CHECKSUM_SIZE; i++) { + osSyncPrintf("%x ", *ptr); + if (++j == 0x20) { + osSyncPrintf("\n"); + j = 0; + } + newChecksum += *ptr++; + } + + gSaveContext.checksum = newChecksum; + osSyncPrintf("\nCheck_Sum=%x(%x)\n", gSaveContext.checksum, newChecksum); + + i = gSramSlotOffsets[slotNum + 3]; + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000) + i, &gSaveContext, SLOT_SIZE, OS_WRITE); + + osSyncPrintf("????#%x,%x,%x,%x,%x,%x\n", gSaveContext.newf[0], gSaveContext.newf[1], + gSaveContext.newf[2], gSaveContext.newf[3], gSaveContext.newf[4], gSaveContext.newf[5]); + osSyncPrintf("\nぽいんと=%x(%d+3) check_sum=%x(%x)\n", i, slotNum, gSaveContext.checksum, + newChecksum); + } + + i = gSramSlotOffsets[slotNum]; + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000) + i, &gSaveContext, SLOT_SIZE, OS_WRITE); + + osSyncPrintf("ぽいんと=%x(%d) check_sum=%x(%x)\n", i, slotNum, gSaveContext.checksum, newChecksum); + } else { + osSyncPrintf("\nSAVEデータ OK!!!!\n"); // "SAVE data OK! ! ! !" + } + } + + memset(sramCtx->readBuff,0, SRAM_SIZE); + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + gSaveContext.dayTime = dayTime; + + osSyncPrintf("SAVECT=%x, NAME=%x, LIFE=%x, ITEM=%x, 64DD=%x, HEART=%x\n", DEATHS, NAME, HEALTH_CAP, QUEST, N64DD, + DEFENSE); + + memcpy(&fileChooseCtx->deaths[0], sramCtx->readBuff + SLOT_OFFSET(0) + DEATHS, sizeof(fileChooseCtx->deaths[0])); + memcpy(&fileChooseCtx->deaths[1], sramCtx->readBuff + SLOT_OFFSET(1) + DEATHS, sizeof(fileChooseCtx->deaths[0])); + memcpy(&fileChooseCtx->deaths[2], sramCtx->readBuff + SLOT_OFFSET(2) + DEATHS, sizeof(fileChooseCtx->deaths[0])); + + memcpy(&fileChooseCtx->fileNames[0], sramCtx->readBuff + SLOT_OFFSET(0) + NAME, + sizeof(fileChooseCtx->fileNames[0])); + memcpy(&fileChooseCtx->fileNames[1], sramCtx->readBuff + SLOT_OFFSET(1) + NAME, + sizeof(fileChooseCtx->fileNames[0])); + memcpy(&fileChooseCtx->fileNames[2], sramCtx->readBuff + SLOT_OFFSET(2) + NAME, + sizeof(fileChooseCtx->fileNames[0])); + + memcpy(&fileChooseCtx->healthCapacities[0], sramCtx->readBuff + SLOT_OFFSET(0) + HEALTH_CAP, + sizeof(fileChooseCtx->healthCapacities[0])); + memcpy(&fileChooseCtx->healthCapacities[1], sramCtx->readBuff + SLOT_OFFSET(1) + HEALTH_CAP, + sizeof(fileChooseCtx->healthCapacities[0])); + memcpy(&fileChooseCtx->healthCapacities[2], sramCtx->readBuff + SLOT_OFFSET(2) + HEALTH_CAP, + sizeof(fileChooseCtx->healthCapacities[0])); + + memcpy(&fileChooseCtx->questItems[0], sramCtx->readBuff + SLOT_OFFSET(0) + QUEST, + sizeof(fileChooseCtx->questItems[0])); + memcpy(&fileChooseCtx->questItems[1], sramCtx->readBuff + SLOT_OFFSET(1) + QUEST, + sizeof(fileChooseCtx->questItems[0])); + memcpy(&fileChooseCtx->questItems[2], sramCtx->readBuff + SLOT_OFFSET(2) + QUEST, + sizeof(fileChooseCtx->questItems[0])); + + memcpy(&fileChooseCtx->n64ddFlags[0], sramCtx->readBuff + SLOT_OFFSET(0) + N64DD, + sizeof(fileChooseCtx->n64ddFlags[0])); + memcpy(&fileChooseCtx->n64ddFlags[1], sramCtx->readBuff + SLOT_OFFSET(1) + N64DD, + sizeof(fileChooseCtx->n64ddFlags[0])); + memcpy(&fileChooseCtx->n64ddFlags[2], sramCtx->readBuff + SLOT_OFFSET(2) + N64DD, + sizeof(fileChooseCtx->n64ddFlags[0])); + + memcpy(&fileChooseCtx->defense[0], sramCtx->readBuff + SLOT_OFFSET(0) + DEFENSE, + sizeof(fileChooseCtx->defense[0])); + memcpy(&fileChooseCtx->defense[1], sramCtx->readBuff + SLOT_OFFSET(1) + DEFENSE, + sizeof(fileChooseCtx->defense[0])); + memcpy(&fileChooseCtx->defense[2], sramCtx->readBuff + SLOT_OFFSET(2) + DEFENSE, + sizeof(fileChooseCtx->defense[0])); + + memcpy(&fileChooseCtx->health[0], sramCtx->readBuff + SLOT_OFFSET(0) + HEALTH, sizeof(fileChooseCtx->health[0])); + memcpy(&fileChooseCtx->health[1], sramCtx->readBuff + SLOT_OFFSET(1) + HEALTH, sizeof(fileChooseCtx->health[0])); + memcpy(&fileChooseCtx->health[2], sramCtx->readBuff + SLOT_OFFSET(2) + HEALTH, sizeof(fileChooseCtx->health[0])); + + osSyncPrintf("f_64dd=%d, %d, %d\n", fileChooseCtx->n64ddFlags[0], fileChooseCtx->n64ddFlags[1], + fileChooseCtx->n64ddFlags[2]); + osSyncPrintf("heart_status=%d, %d, %d\n", fileChooseCtx->defense[0], fileChooseCtx->defense[1], + fileChooseCtx->defense[2]); + osSyncPrintf("now_life=%d, %d, %d\n", fileChooseCtx->health[0], fileChooseCtx->health[1], fileChooseCtx->health[2]); +} + +void Sram_InitSave(FileChooseContext* fileChooseCtx, SramContext* sramCtx) { + u16 offset; + u16 j; + u16* ptr; + u16 checksum; + + if (fileChooseCtx->buttonIndex != 0 || !CVar_GetS32("gDebugEnabled", 0)) { + Sram_InitNewSave(); + } else { + Sram_InitDebugSave(); + } + + gSaveContext.entranceIndex = 0xBB; + gSaveContext.linkAge = 1; + gSaveContext.dayTime = 0x6AAB; + gSaveContext.cutsceneIndex = 0xFFF1; + + if ((fileChooseCtx->buttonIndex == 0 && CVar_GetS32("gDebugEnabled", 0)) || CVar_GetS32("gNaviSkipCutscene", 0)) { + gSaveContext.cutsceneIndex = 0; + } + + for (offset = 0; offset < 8; offset++) { + gSaveContext.playerName[offset] = fileChooseCtx->fileNames[fileChooseCtx->buttonIndex][offset]; + } + + gSaveContext.newf[0] = 'Z'; + gSaveContext.newf[1] = 'E'; + gSaveContext.newf[2] = 'L'; + gSaveContext.newf[3] = 'D'; + gSaveContext.newf[4] = 'A'; + gSaveContext.newf[5] = 'Z'; + + gSaveContext.n64ddFlag = fileChooseCtx->n64ddFlag; + osSyncPrintf("64DDフラグ=%d\n", fileChooseCtx->n64ddFlag); + osSyncPrintf("newf=%x,%x,%x,%x,%x,%x\n", gSaveContext.newf[0], gSaveContext.newf[1], gSaveContext.newf[2], + gSaveContext.newf[3], gSaveContext.newf[4], gSaveContext.newf[5]); + osSyncPrintf("\n$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); + + ptr = (u16*)&gSaveContext; + j = 0; + checksum = 0; + + for (offset = 0; offset < CHECKSUM_SIZE; offset++) { + osSyncPrintf("%x ", *ptr); + checksum += *ptr++; + if (++j == 0x20) { + osSyncPrintf("\n"); + j = 0; + } + } + + gSaveContext.checksum = checksum; + osSyncPrintf("\nチェックサム=%x\n", gSaveContext.checksum); // "Checksum = %x" + + offset = gSramSlotOffsets[gSaveContext.fileNum]; + osSyncPrintf("I=%x no=%d\n", offset, gSaveContext.fileNum); + memcpy(sramCtx->readBuff + offset, &gSaveContext, sizeof(Save)); + + offset = gSramSlotOffsets[gSaveContext.fileNum + 3]; + osSyncPrintf("I=%x no=%d\n", offset, gSaveContext.fileNum + 3); + memcpy(sramCtx->readBuff + offset, &gSaveContext, sizeof(Save)); + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_WRITE); + + osSyncPrintf("SAVE終了\n"); // "SAVE end" + osSyncPrintf("z_common_data.file_no = %d\n", gSaveContext.fileNum); + osSyncPrintf("SAVECT=%x, NAME=%x, LIFE=%x, ITEM=%x, SAVE_64DD=%x\n", DEATHS, NAME, HEALTH_CAP, QUEST, N64DD); + + j = gSramSlotOffsets[gSaveContext.fileNum]; + + memcpy(&fileChooseCtx->deaths[gSaveContext.fileNum], sramCtx->readBuff + j + DEATHS, + sizeof(fileChooseCtx->deaths[0])); + memcpy(&fileChooseCtx->fileNames[gSaveContext.fileNum], sramCtx->readBuff + j + NAME, + sizeof(fileChooseCtx->fileNames[0])); + memcpy(&fileChooseCtx->healthCapacities[gSaveContext.fileNum], sramCtx->readBuff + j + HEALTH_CAP, + sizeof(fileChooseCtx->healthCapacities[0])); + memcpy(&fileChooseCtx->questItems[gSaveContext.fileNum], sramCtx->readBuff + j + QUEST, + sizeof(fileChooseCtx->questItems[0])); + memcpy(&fileChooseCtx->n64ddFlags[gSaveContext.fileNum], sramCtx->readBuff + j + N64DD, + sizeof(fileChooseCtx->n64ddFlags[0])); + memcpy(&fileChooseCtx->defense[gSaveContext.fileNum], sramCtx->readBuff + j + DEFENSE, + sizeof(fileChooseCtx->defense[0])); + memcpy(&fileChooseCtx->health[gSaveContext.fileNum], sramCtx->readBuff + j + HEALTH, + sizeof(fileChooseCtx->health[0])); + + osSyncPrintf("f_64dd[%d]=%d\n", gSaveContext.fileNum, fileChooseCtx->n64ddFlags[gSaveContext.fileNum]); + osSyncPrintf("heart_status[%d]=%d\n", gSaveContext.fileNum, fileChooseCtx->defense[gSaveContext.fileNum]); + osSyncPrintf("now_life[%d]=%d\n", gSaveContext.fileNum, fileChooseCtx->health[gSaveContext.fileNum]); +} + +void Sram_EraseSave(FileChooseContext* fileChooseCtx, SramContext* sramCtx) { + s32 offset; + + Sram_InitNewSave(); + + offset = gSramSlotOffsets[fileChooseCtx->selectedFileIndex]; + memcpy(sramCtx->readBuff + offset, &gSaveContext, sizeof(Save)); + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000) + offset, &gSaveContext, SLOT_SIZE, OS_WRITE); + + memcpy(&fileChooseCtx->n64ddFlags[fileChooseCtx->selectedFileIndex], sramCtx->readBuff + offset + N64DD, + sizeof(fileChooseCtx->n64ddFlags[0])); + + offset = gSramSlotOffsets[fileChooseCtx->selectedFileIndex + 3]; + memcpy(sramCtx->readBuff + offset, &gSaveContext, sizeof(Save)); + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000) + offset, &gSaveContext, SLOT_SIZE, OS_WRITE); + + osSyncPrintf("CLEAR終了\n"); +} + +void Sram_CopySave(FileChooseContext* fileChooseCtx, SramContext* sramCtx) { + s32 offset; + + osSyncPrintf("READ=%d(%x) COPY=%d(%x)\n", fileChooseCtx->selectedFileIndex, + gSramSlotOffsets[fileChooseCtx->selectedFileIndex], fileChooseCtx->copyDestFileIndex, + gSramSlotOffsets[fileChooseCtx->copyDestFileIndex]); + + offset = gSramSlotOffsets[fileChooseCtx->selectedFileIndex]; + memcpy(&gSaveContext, sramCtx->readBuff + offset, sizeof(Save)); + + offset = gSramSlotOffsets[fileChooseCtx->copyDestFileIndex]; + memcpy(sramCtx->readBuff + offset, &gSaveContext, sizeof(Save)); + + offset = gSramSlotOffsets[fileChooseCtx->copyDestFileIndex + 3]; + memcpy(sramCtx->readBuff + offset, &gSaveContext, sizeof(Save)); + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_WRITE); + + offset = gSramSlotOffsets[fileChooseCtx->copyDestFileIndex]; + + memcpy(&fileChooseCtx->deaths[fileChooseCtx->copyDestFileIndex], sramCtx->readBuff + offset + DEATHS, + sizeof(fileChooseCtx->deaths[0])); + memcpy(&fileChooseCtx->fileNames[fileChooseCtx->copyDestFileIndex], sramCtx->readBuff + offset + NAME, + sizeof(fileChooseCtx->fileNames[0])); + memcpy(&fileChooseCtx->healthCapacities[fileChooseCtx->copyDestFileIndex], sramCtx->readBuff + offset + HEALTH_CAP, + sizeof(fileChooseCtx->healthCapacities[0])); + memcpy(&fileChooseCtx->questItems[fileChooseCtx->copyDestFileIndex], sramCtx->readBuff + offset + QUEST, + sizeof(fileChooseCtx->questItems[0])); + memcpy(&fileChooseCtx->n64ddFlags[fileChooseCtx->copyDestFileIndex], sramCtx->readBuff + offset + N64DD, + sizeof(fileChooseCtx->n64ddFlags[0])); + memcpy(&fileChooseCtx->defense[fileChooseCtx->copyDestFileIndex], sramCtx->readBuff + offset + DEFENSE, + sizeof(fileChooseCtx->defense[0])); + memcpy(&fileChooseCtx->health[fileChooseCtx->copyDestFileIndex], (sramCtx->readBuff + offset) + HEALTH, + sizeof(fileChooseCtx->health[0])); + + osSyncPrintf("f_64dd[%d]=%d\n", gSaveContext.fileNum, fileChooseCtx->n64ddFlags[gSaveContext.fileNum]); + osSyncPrintf("heart_status[%d]=%d\n", gSaveContext.fileNum, fileChooseCtx->defense[gSaveContext.fileNum]); + osSyncPrintf("COPY終了\n"); // "Copy end" +} + +/** + * Write the first 16 bytes of the read buffer to the SRAM header + */ +void Sram_WriteSramHeader(SramContext* sramCtx) { + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_HEADER_SIZE, OS_WRITE); +} + +void Sram_InitSram(GameState* gameState, SramContext* sramCtx) { + u16 i; + + osSyncPrintf("sram_initialize( Game *game, Sram *sram )\n"); + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + + for (i = 0; i < ARRAY_COUNTU(sZeldaMagic) - 3; i++) { + if (sZeldaMagic[i + SRAM_HEADER_MAGIC] != sramCtx->readBuff[i + SRAM_HEADER_MAGIC]) { + osSyncPrintf("SRAM破壊!!!!!!\n"); // "SRAM destruction! ! ! ! ! !" + gSaveContext.language = sramCtx->readBuff[SRAM_HEADER_LANGUAGE]; + memcpy(sramCtx->readBuff, sZeldaMagic, sizeof(sZeldaMagic)); + sramCtx->readBuff[SRAM_HEADER_LANGUAGE] = gSaveContext.language; + Sram_WriteSramHeader(sramCtx); + } + } + + gSaveContext.audioSetting = sramCtx->readBuff[SRAM_HEADER_SOUND] & 3; + gSaveContext.zTargetSetting = sramCtx->readBuff[SRAM_HEADER_ZTARGET] & 1; + gSaveContext.language = sramCtx->readBuff[SRAM_HEADER_LANGUAGE]; + + if (gSaveContext.language >= LANGUAGE_MAX) { + gSaveContext.language = LANGUAGE_ENG; + sramCtx->readBuff[SRAM_HEADER_LANGUAGE] = gSaveContext.language; + Sram_WriteSramHeader(sramCtx); + } + + if (CHECK_BTN_ANY(gameState->input[2].cur.button, BTN_DRIGHT)) { + memset(sramCtx->readBuff, 0,SRAM_SIZE); + for (i = 0; i < CHECKSUM_SIZE; i++) { + sramCtx->readBuff[i] = i; + } + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_WRITE); + osSyncPrintf("SRAM破壊!!!!!!\n"); // "SRAM destruction! ! ! ! ! !" + } + + // "GOOD! GOOD! Size = %d + %d = %d" + osSyncPrintf("GOOD!GOOD! サイズ=%d + %d = %d\n", sizeof(SaveInfo), 4, sizeof(SaveInfo) + 4); + osSyncPrintf(VT_FGCOL(BLUE)); + osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting); + osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting); + osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting); + osSyncPrintf(VT_RST); + func_800F6700(gSaveContext.audioSetting); +} + +void Sram_Alloc(GameState* gameState, SramContext* sramCtx) { + sramCtx->readBuff = GameState_Alloc(gameState, SRAM_SIZE, "../z_sram.c", 1294); + ASSERT(sramCtx->readBuff != NULL, "sram->read_buff != NULL", "../z_sram.c", 1295); +} + +void Sram_Init(GlobalContext* globalCtx, SramContext* sramCtx) { +} diff --git a/soh/src/code/z_ss_sram.c b/soh/src/code/z_ss_sram.c new file mode 100644 index 000000000..6b4ea50ca --- /dev/null +++ b/soh/src/code/z_ss_sram.c @@ -0,0 +1,97 @@ +#include "ultra64.h" +#include "global.h" + +#include +#include + +#if 0 +typedef struct { + /* 0x00 */ OSPiHandle piHandle; + /* 0x74 */ OSIoMesg ioMesg; + /* 0x8C */ OSMesgQueue mesgQ; +} SsSramContext; // size = 0xA4 + +SsSramContext sSsSramContext = { 0 }; + + + +void SsSram_Init(uintptr_t addr, u8 handleType, u8 handleDomain, u8 handleLatency, u8 handlePageSize, u8 handleRelDuration, + u8 handlePulse, u32 handleSpeed) { + u32 prevInt; + OSPiHandle* handle = &sSsSramContext.piHandle; + + if ((uintptr_t)OS_PHYSICAL_TO_K1(addr) != (*handle).baseAddress) { + sSsSramContext.piHandle.type = handleType; + (*handle).baseAddress = OS_PHYSICAL_TO_K1(addr); + sSsSramContext.piHandle.latency = handleLatency; + sSsSramContext.piHandle.pulse = handlePulse; + sSsSramContext.piHandle.pageSize = handlePageSize; + sSsSramContext.piHandle.relDuration = handleRelDuration; + sSsSramContext.piHandle.domain = handleDomain; + sSsSramContext.piHandle.speed = handleSpeed; + + bzero(&sSsSramContext.piHandle.transferInfo, sizeof(__OSTranxInfo)); + + prevInt = __osDisableInt(); + //sSsSramContext.piHandle.next = __osPiTable; + //__osPiTable = &sSsSramContext.piHandle; + __osRestoreInt(prevInt); + + sSsSramContext.ioMesg.hdr.pri = OS_MESG_PRI_NORMAL; + sSsSramContext.ioMesg.hdr.retQueue = &sSsSramContext.mesgQ; + sSsSramContext.ioMesg.devAddr = addr; + } +} + +void SsSram_Dma(void* dramAddr, size_t size, s32 direction) { + OSMesg mesg; + + osCreateMesgQueue(&sSsSramContext.mesgQ, &mesg, 1); + sSsSramContext.ioMesg.dramAddr = dramAddr; + sSsSramContext.ioMesg.size = size; + osWritebackDCache(dramAddr, size); + osEPiStartDma(&sSsSramContext.piHandle, &sSsSramContext.ioMesg, direction); + osRecvMesg(&sSsSramContext.mesgQ, &mesg, OS_MESG_BLOCK); + osInvalDCache(dramAddr, size); +} +#endif + +void SsSram_ReadWrite(uintptr_t addr, void* dramAddr, size_t size, s32 direction) { + osSyncPrintf("ssSRAMReadWrite:%08x %08x %08x %d\n", addr, (uintptr_t)dramAddr, size, direction); + //Check to see if the file exists + FILE* saveFile; + saveFile = fopen("oot_save.sav", "rb"); + + if (saveFile == NULL) { + + saveFile = fopen("oot_save.sav", "wb"); + fseek(saveFile, 0, SEEK_SET); + assert(saveFile != NULL); // OTRTODO LOG + uint8_t zero = 0; + + for (uint32_t i = 0; i < SRAM_SIZE; i++) { + fwrite(&zero, 1, 1, saveFile); + } + fclose(saveFile); + } else { + fclose(saveFile); + } + switch (direction) { + case OS_WRITE: { + saveFile = fopen("oot_save.sav", "r+b"); + rewind(saveFile); + fseek(saveFile, addr, SEEK_SET); + fwrite(dramAddr, size, 1, saveFile); + fclose(saveFile); + } break; + case OS_READ: { + saveFile = fopen("oot_save.sav", "rb+"); + rewind(saveFile); + fseek(saveFile, addr, SEEK_SET); + fread(dramAddr, size, 1, saveFile); + fclose(saveFile); + } break; + } + //SsSram_Init(addr, DEVICE_TYPE_SRAM, PI_DOMAIN2, 5, 0xD, 2, 0xC, 0); + //SsSram_Dma(dramAddr, size, direction); +} diff --git a/soh/src/code/z_view.c b/soh/src/code/z_view.c new file mode 100644 index 000000000..97f1ad26e --- /dev/null +++ b/soh/src/code/z_view.c @@ -0,0 +1,628 @@ +#include "global.h" +#include "vt.h" + +#include + +vu32 D_8012ABF0 = true; + +void View_ViewportToVp(Vp* dest, Viewport* src) { + s32 width = src->rightX - src->leftX; + s32 height = src->bottomY - src->topY; + + dest->vp.vscale[0] = width * 2; + dest->vp.vscale[1] = height * 2; + dest->vp.vscale[2] = 0x01FF; + dest->vp.vscale[3] = 0; + dest->vp.vtrans[0] = ((src->leftX * 2) + width) * 2; + dest->vp.vtrans[1] = ((src->topY * 2) + height) * 2; + dest->vp.vtrans[2] = 0x01FF; + dest->vp.vtrans[3] = 0; +} + +View* View_New(GraphicsContext* gfxCtx) { + View* view = SystemArena_MallocDebug(sizeof(View), "../z_view.c", 285); + + if (view != NULL) { + memset(view, 0, sizeof(View)); + View_Init(view, gfxCtx); + } + + return view; +} + +void View_Free(View* view) { + SystemArena_FreeDebug(view, "../z_view.c", 297); +} + +void View_Init(View* view, GraphicsContext* gfxCtx) { + view->gfxCtx = gfxCtx; + view->viewport.topY = 0; + view->viewport.bottomY = SCREEN_HEIGHT; + view->viewport.leftX = 0; + view->viewport.rightX = SCREEN_WIDTH; + view->magic = 0x56494557; // "VIEW" + view->eye.x = 0.0f; + view->eye.y = 0.0f; + view->scale = 1.0f; + view->fovy = 60.0f; + view->zNear = 10.0f; + view->zFar = 12800.0f; + view->lookAt.x = 0.0f; + view->up.x = 0.0f; + view->up.y = 1.0f; + view->up.z = 0.0f; + view->eye.z = -1.0f; + + if (D_8012ABF0) { + if (D_8012ABF0 == 0) {} + osSyncPrintf("\nview: initialize ---\n"); + D_8012ABF0 = false; + } + + view->unk_124 = 0; + view->flags = 1 | 2 | 4; + func_800AA7B8(view); +} + +void View_GetParams(View* view, Vec3f* eye, Vec3f* lookAt, Vec3f* up) { + /*if (eye->x == lookAt->x && eye->z == lookAt->z) { + eye->x += 0.1f; + }*/ + + *eye = view->eye; + *lookAt = view->lookAt; + *up = view->up; + //view->flags |= 1; +} + + +void func_800AA358(View* view, Vec3f* eye, Vec3f* lookAt, Vec3f* up) { + if (eye->x == lookAt->x && eye->z == lookAt->z) { + eye->x += 0.1f; + } + + view->eye = *eye; + view->lookAt = *lookAt; + view->up = *up; + view->flags |= 1; +} + +void func_800AA3F0(View* view, Vec3f* eye, Vec3f* lookAt, Vec3f* up) { + view->eye = *eye; + view->lookAt = *lookAt; + view->up = *up; +} + +void View_SetScale(View* view, f32 scale) { + view->flags |= 4; + view->scale = scale; +} + +void View_GetScale(View* view, f32* scale) { + *scale = view->scale; +} + +void func_800AA460(View* view, f32 fovy, f32 near, f32 far) { + view->fovy = fovy; + view->zNear = near; + //view->zNear = -30; + view->zFar = far; + view->flags |= 4; +} + +void func_800AA48C(View* view, f32* fovy, f32* near, f32* far) { + *fovy = view->fovy; + *near = view->zNear; + *far = view->zFar; +} + +void func_800AA4A8(View* view, f32 fovy, f32 near, f32 far) { + view->fovy = fovy; + view->zNear = near; + //view->zNear = -30; + view->zFar = far; + view->flags |= 8; + view->scale = 1.0f; +} + +void func_800AA4E0(View* view, f32* fovy, f32* near, f32* far) { + *fovy = view->fovy; + *near = view->zNear; + *far = view->zFar; +} + +void View_SetViewport(View* view, Viewport* viewport) { + view->viewport = *viewport; + view->flags |= 2; +} + +void View_GetViewport(View* view, Viewport* viewport) { + *viewport = view->viewport; +} + +void func_800AA550(View* view) { + s32 varY; + s32 varX; + s32 pad; + s32 ulx; + s32 uly; + s32 lrx; + s32 lry; + GraphicsContext* gfxCtx = view->gfxCtx; + + varY = ShrinkWindow_GetCurrentVal(); + + varX = -1; // The following is optimized to varX = 0 but affects codegen + + if (varX < 0) { + varX = 0; + } + if (varX > SCREEN_WIDTH / 2) { + varX = SCREEN_WIDTH / 2; + } + + if (varY < 0) { + varY = 0; + } + if (varY > SCREEN_HEIGHT / 2) { + varY = SCREEN_HEIGHT / 2; + } + + ulx = view->viewport.leftX + varX; + uly = view->viewport.topY + varY; + lrx = view->viewport.rightX - varX; + lry = view->viewport.bottomY - varY; + + ASSERT(ulx >= 0, "ulx >= 0", "../z_view.c", 454); + ASSERT(uly >= 0, "uly >= 0", "../z_view.c", 455); + ASSERT(lrx <= SCREEN_WIDTH, "lrx <= SCREEN_WD", "../z_view.c", 456); + ASSERT(lry <= SCREEN_HEIGHT, "lry <= SCREEN_HT", "../z_view.c", 457); + + OPEN_DISPS(gfxCtx, "../z_view.c", 459); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetScissor(POLY_OPA_DISP++, G_SC_NON_INTERLACE, ulx, uly, lrx, lry); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetScissor(POLY_XLU_DISP++, G_SC_NON_INTERLACE, ulx, uly, lrx, lry); + gDPPipeSync(POLY_KAL_DISP++); + gDPSetScissor(POLY_KAL_DISP++, G_SC_NON_INTERLACE, ulx, uly, lrx, lry); + + CLOSE_DISPS(gfxCtx, "../z_view.c", 472); +} + +void func_800AA76C(View* view, f32 x, f32 y, f32 z) { + view->unk_E8.x = x; + view->unk_E8.y = y; + view->unk_E8.z = z; +} + +void func_800AA78C(View* view, f32 x, f32 y, f32 z) { + view->unk_F4.x = x; + view->unk_F4.y = y; + view->unk_F4.z = z; +} + +s32 func_800AA7AC(View* view, f32 arg1) { + view->unk_100 = arg1; +} + +void func_800AA7B8(View* view) { + view->unk_E8.x = 0.0f; + view->unk_E8.y = 0.0f; + view->unk_E8.z = 0.0f; + view->unk_F4.x = 1.0f; + view->unk_F4.y = 1.0f; + view->unk_F4.z = 1.0f; + view->unk_104 = view->unk_E8; + view->unk_110 = view->unk_F4; + view->unk_100 = 0.0f; +} + +void func_800AA814(View* view) { + view->unk_E8.x = 0.0f; + view->unk_E8.y = 0.0f; + view->unk_E8.z = 0.0f; + view->unk_F4.x = 1.0f; + view->unk_F4.y = 1.0f; + view->unk_F4.z = 1.0f; + view->unk_100 = 1.0f; +} + +void func_800AA840(View* view, Vec3f vec1, Vec3f vec2, f32 arg3) { + view->unk_E8 = vec1; + view->unk_F4 = vec2; + view->unk_100 = arg3; +} + +s32 func_800AA890(View* view, Mtx* mtx) { + MtxF mf; + + if (view->unk_100 == 0.0f) { + return 0; + } else if (view->unk_100 == 1.0f) { + view->unk_104 = view->unk_E8; + view->unk_110 = view->unk_F4; + view->unk_100 = 0.0f; + } else { + view->unk_104.x += ((view->unk_E8.x - view->unk_104.x) * view->unk_100); + view->unk_104.y += ((view->unk_E8.y - view->unk_104.y) * view->unk_100); + view->unk_104.z += ((view->unk_E8.z - view->unk_104.z) * view->unk_100); + + view->unk_110.x += ((view->unk_F4.x - view->unk_110.x) * view->unk_100); + view->unk_110.y += ((view->unk_F4.y - view->unk_110.y) * view->unk_100); + view->unk_110.z += ((view->unk_F4.z - view->unk_110.z) * view->unk_100); + } + + Matrix_MtxToMtxF(mtx, &mf); + Matrix_Put(&mf); + Matrix_RotateX(view->unk_104.x, MTXMODE_APPLY); + Matrix_RotateY(view->unk_104.y, MTXMODE_APPLY); + Matrix_RotateZ(view->unk_104.z, MTXMODE_APPLY); + Matrix_Scale(view->unk_110.x, view->unk_110.y, view->unk_110.z, MTXMODE_APPLY); + Matrix_RotateZ(-view->unk_104.z, MTXMODE_APPLY); + Matrix_RotateY(-view->unk_104.y, MTXMODE_APPLY); + Matrix_RotateX(-view->unk_104.x, MTXMODE_APPLY); + Matrix_ToMtx(mtx, "../z_view.c", 566); + + return 1; +} + +void func_800AAA50(View* view, s32 arg1) { + arg1 = (view->flags & arg1) | (arg1 >> 4); + + if (arg1 & 8) { + func_800AB0A8(view); + } else { + func_800AAA9C(view); + } +} + +s32 func_800AAA9C(View* view) { + f32 aspect; + s32 width; + s32 height; + Vp* vp; + Mtx* projection; + Mtx* viewing; + GraphicsContext* gfxCtx = view->gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_view.c", 596); + + vp = Graph_Alloc(gfxCtx, sizeof(Vp)); + LogUtils_CheckNullPointer("vp", vp, "../z_view.c", 601); + View_ViewportToVp(vp, &view->viewport); + view->vp = *vp; + + func_800AA550(view); + + gSPViewport(POLY_OPA_DISP++, vp); + gSPViewport(POLY_XLU_DISP++, vp); + gSPViewport(POLY_KAL_DISP++, vp); + + projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("projection", projection, "../z_view.c", 616); + view->projectionPtr = projection; + + width = view->viewport.rightX - view->viewport.leftX; + height = view->viewport.bottomY - view->viewport.topY; + aspect = (f32)width / (f32)height; + + if (HREG(80) == 11) { + if (HREG(94) != 11) { + HREG(94) = 11; + HREG(83) = 60; + HREG(84) = 13333; + HREG(85) = 10; + HREG(86) = 12800; + HREG(87) = 100; + } + guPerspective(projection, &view->normal, HREG(83), HREG(84) / 10000.0f, HREG(85), HREG(86), HREG(87) / 100.0f); + } else { + guPerspective(projection, &view->normal, view->fovy, aspect, view->zNear, view->zFar, view->scale); + } + + if (QREG(88) & 1) { + s32 i; + MtxF mf; + + osSyncPrintf("fovy %f near %f far %f scale %f aspect %f normal %08x\n", view->fovy, view->zNear, view->zFar, + view->scale, aspect, view->normal); + + Matrix_MtxToMtxF(projection, &mf); + osSyncPrintf("projection\n"); + for (i = 0; i < 4; i++) { + osSyncPrintf(" %f %f %f %f\n", mf.mf[i][0], mf.mf[i][1], mf.mf[i][2], mf.mf[i][3]); + } + osSyncPrintf("\n"); + } + + view->projection = *projection; + + func_800AA890(view, projection); + + gSPPerspNormalize(POLY_OPA_DISP++, view->normal); + gSPMatrix(POLY_OPA_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPPerspNormalize(POLY_XLU_DISP++, view->normal); + gSPMatrix(POLY_XLU_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPPerspNormalize(POLY_KAL_DISP++, view->normal); + gSPMatrix(POLY_KAL_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + + viewing = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 667); + view->viewingPtr = viewing; + + if (view->eye.x == view->lookAt.x && view->eye.y == view->lookAt.y && view->eye.z == view->lookAt.z) { + view->eye.x += 1.0f; + view->eye.y += 1.0f; + view->eye.z += 1.0f; + } + + func_800ABE74(view->eye.x, view->eye.y, view->eye.z); + guLookAt(viewing, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x, + view->up.y, view->up.z); + + view->viewing = *viewing; + + if (QREG(88) & 2) { + s32 i; + MtxF mf; + + Matrix_MtxToMtxF(view->viewingPtr, &mf); + osSyncPrintf("viewing\n"); + for (i = 0; i < 4; i++) { + osSyncPrintf(" %f %f %f %f\n", mf.mf[i][0], mf.mf[i][1], mf.mf[i][2], mf.mf[i][3]); + } + osSyncPrintf("\n"); + } + + gSPMatrix(POLY_OPA_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(POLY_KAL_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + + CLOSE_DISPS(gfxCtx, "../z_view.c", 711); + + return 1; +} + +s32 func_800AB0A8(View* view) { + Vp* vp; + Mtx* projection; + GraphicsContext* gfxCtx = view->gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_view.c", 726); + + vp = Graph_Alloc(gfxCtx, sizeof(Vp)); + LogUtils_CheckNullPointer("vp", vp, "../z_view.c", 730); + View_ViewportToVp(vp, &view->viewport); + view->vp = *vp; + + func_800AA550(view); + + gSPViewport(POLY_OPA_DISP++, vp); + gSPViewport(POLY_XLU_DISP++, vp); + gSPViewport(POLY_KAL_DISP++, vp); + gSPViewport(OVERLAY_DISP++, vp); + + projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("projection", projection, "../z_view.c", 744); + view->projectionPtr = projection; + + guOrtho(projection, -(f32)gScreenWidth * 0.5f, (f32)gScreenWidth * 0.5f, -(f32)gScreenHeight * 0.5f, + (f32)gScreenHeight * 0.5f, view->zNear, view->zFar, view->scale); + + view->projection = *projection; + + gSPMatrix(POLY_OPA_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_KAL_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + + CLOSE_DISPS(gfxCtx, "../z_view.c", 762); + + return 1; +} + +s32 func_800AB2C4(View* view) { + Vp* vp; + Mtx* projection; + GraphicsContext* gfxCtx; + + gfxCtx = view->gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_view.c", 777); + + vp = Graph_Alloc(gfxCtx, sizeof(Vp)); + LogUtils_CheckNullPointer("vp", vp, "../z_view.c", 781); + View_ViewportToVp(vp, &view->viewport); + view->vp = *vp; + + gDPPipeSync(OVERLAY_DISP++); + gDPSetScissor(OVERLAY_DISP++, G_SC_NON_INTERLACE, view->viewport.leftX, view->viewport.topY, view->viewport.rightX, + view->viewport.bottomY); + gSPViewport(OVERLAY_DISP++, vp); + + projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("projection", projection, "../z_view.c", 791); + view->projectionPtr = projection; + + guOrtho(projection, -(f32)gScreenWidth * 0.5f, (f32)gScreenWidth * 0.5f, -(f32)gScreenHeight * 0.5f, + (f32)gScreenHeight * 0.5f, -30, view->zFar, view->scale); + + view->projection = *projection; + + gSPMatrix(OVERLAY_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + + CLOSE_DISPS(gfxCtx, "../z_view.c", 801); + + return 1; +} + +s32 func_800AB560(View* view) { + s32 pad[2]; + f32 aspect; + s32 width; + s32 height; + Vp* vp; + Mtx* projection; + Mtx* viewing; + GraphicsContext* gfxCtx = view->gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_view.c", 816); + + vp = Graph_Alloc(gfxCtx, sizeof(Vp)); + LogUtils_CheckNullPointer("vp", vp, "../z_view.c", 821); + View_ViewportToVp(vp, &view->viewport); + view->vp = *vp; + + gDPPipeSync(OVERLAY_DISP++); + gDPSetScissor(OVERLAY_DISP++, G_SC_NON_INTERLACE, view->viewport.leftX, view->viewport.topY, view->viewport.rightX, + view->viewport.bottomY); + gSPViewport(OVERLAY_DISP++, vp); + + projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("projection", projection, "../z_view.c", 833); + view->projectionPtr = projection; + + width = view->viewport.rightX - view->viewport.leftX; + height = view->viewport.bottomY - view->viewport.topY; + + aspect = (f32)width / (f32)height; + guPerspective(projection, &view->normal, view->fovy, aspect, view->zNear, view->zFar, view->scale); + + view->projection = *projection; + + gSPPerspNormalize(OVERLAY_DISP++, view->normal); + gSPMatrix(OVERLAY_DISP++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + + viewing = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 848); + view->viewingPtr = viewing; + + if (view->eye.x == view->lookAt.x && view->eye.y == view->lookAt.y && view->eye.z == view->lookAt.z) { + view->eye.x += 1.0f; + view->eye.y += 1.0f; + view->eye.z += 1.0f; + } + + func_800ABE74(view->eye.x, view->eye.y, view->eye.z); + guLookAt(viewing, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x, + view->up.y, view->up.z); + + view->viewing = *viewing; + + gSPMatrix(OVERLAY_DISP++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + + CLOSE_DISPS(gfxCtx, "../z_view.c", 871); + + return 1; +} + +s32 func_800AB944(View* view) { + OPEN_DISPS(view->gfxCtx, "../z_view.c", 878); + + func_800ABE74(view->eye.x, view->eye.y, view->eye.z); + guLookAt(view->viewingPtr, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, + view->up.x, view->up.y, view->up.z); + + CLOSE_DISPS(view->gfxCtx, "../z_view.c", 886); + + return 1; +} + +s32 func_800AB9EC(View* view, s32 arg1, Gfx** gfxp) { + Gfx* gfx = *gfxp; + GraphicsContext* gfxCtx = view->gfxCtx; + s32 width; + s32 height; + Vp* vp; + Mtx* projection; + Mtx* viewing; + + arg1 = (view->flags & arg1) | (arg1 >> 4); + + if (arg1 & 2) { + vp = Graph_Alloc(gfxCtx, sizeof(Vp)); + LogUtils_CheckNullPointer("vp", vp, "../z_view.c", 910); + View_ViewportToVp(vp, &view->viewport); + + view->vp = *vp; + + gDPPipeSync(gfx++); + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, view->viewport.leftX, view->viewport.topY, view->viewport.rightX, + view->viewport.bottomY); + gSPViewport(gfx++, vp); + } + + if (arg1 & 8) { + projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("projection", projection, "../z_view.c", 921); + view->projectionPtr = projection; + + guOrtho(projection, -(f32)gScreenWidth * 0.5f, (f32)gScreenWidth * 0.5f, -(f32)gScreenHeight * 0.5f, + (f32)gScreenHeight * 0.5f, view->zNear, view->zFar, view->scale); + + view->projection = *projection; + + gSPMatrix(gfx++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + } else if (arg1 & 6) { + projection = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("projection", projection, "../z_view.c", 932); + view->projectionPtr = projection; + + width = view->viewport.rightX - view->viewport.leftX; + height = view->viewport.bottomY - view->viewport.topY; + + guPerspective(projection, &view->normal, view->fovy, (f32)width / (f32)height, view->zNear, view->zFar, + view->scale); + + view->projection = *projection; + + gSPPerspNormalize(gfx++, view->normal); + gSPMatrix(gfx++, projection, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + } + + if (arg1 & 1) { + viewing = Graph_Alloc(gfxCtx, sizeof(Mtx)); + LogUtils_CheckNullPointer("viewing", viewing, "../z_view.c", 948); + view->viewingPtr = viewing; + + func_800ABE74(view->eye.x, view->eye.y, view->eye.z); + guLookAt(viewing, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, + view->up.x, view->up.y, view->up.z); + + view->viewing = *viewing; + + gSPMatrix(gfx++, viewing, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + } + + view->flags = 0; + *gfxp = gfx; + + return 1; +} + +s32 func_800ABE74(f32 eyeX, f32 eyeY, f32 eyeZ) { + s32 error = 0; + + if (SQ(eyeX) + SQ(eyeY) + SQ(eyeZ) > SQ(32767.0f)) { + error = 3; + } else { + f32 absEyeX = ABS(eyeX); + f32 absEyeY = ABS(eyeY); + f32 absEyeZ = ABS(eyeZ); + + if (((18900.0f < absEyeX) || (18900.0f < absEyeY)) || (18900.0f < absEyeZ)) { + error = 2; + } else if (((16000.0f < absEyeX) || (16000.0f < absEyeY)) || (16000.0f < absEyeZ)) { + error = 1; + } + } + + if (error != 0) { + osSyncPrintf(VT_FGCOL(RED)); + // "Is too large" + osSyncPrintf("eye が大きすぎます eye=[%8.3f %8.3f %8.3f] error=%d\n", eyeX, eyeY, eyeZ, error); + osSyncPrintf(VT_RST); + } + + return error; +} diff --git a/soh/src/code/z_vimode.c b/soh/src/code/z_vimode.c new file mode 100644 index 000000000..d0987ea24 --- /dev/null +++ b/soh/src/code/z_vimode.c @@ -0,0 +1,321 @@ +#include "global.h" + +void ViMode_LogPrint(OSViMode* osViMode) { + LOG_ADDRESS("osvimodep", osViMode, "../z_vimode.c", 87); + LOG_ADDRESS("osvimodep->comRegs.ctrl", osViMode->comRegs.ctrl, "../z_vimode.c", 88); + LOG_ADDRESS("osvimodep->comRegs.width", osViMode->comRegs.width, "../z_vimode.c", 89); + LOG_ADDRESS("osvimodep->comRegs.burst", osViMode->comRegs.burst, "../z_vimode.c", 90); + LOG_ADDRESS("osvimodep->comRegs.vSync", osViMode->comRegs.vSync, "../z_vimode.c", 91); + LOG_ADDRESS("osvimodep->comRegs.hSync", osViMode->comRegs.hSync, "../z_vimode.c", 92); + LOG_ADDRESS("osvimodep->comRegs.leap", osViMode->comRegs.leap, "../z_vimode.c", 93); + LOG_ADDRESS("osvimodep->comRegs.hStart", osViMode->comRegs.hStart, "../z_vimode.c", 94); + LOG_ADDRESS("osvimodep->comRegs.xScale", osViMode->comRegs.xScale, "../z_vimode.c", 95); + LOG_ADDRESS("osvimodep->fldRegs[0].vStart", osViMode->fldRegs[0].vStart, "../z_vimode.c", 96); + LOG_ADDRESS("osvimodep->fldRegs[0].vBurst", osViMode->fldRegs[0].vBurst, "../z_vimode.c", 97); + LOG_ADDRESS("osvimodep->fldRegs[0].origin", osViMode->fldRegs[0].origin, "../z_vimode.c", 98); + LOG_ADDRESS("osvimodep->fldRegs[0].yScale", osViMode->fldRegs[0].yScale, "../z_vimode.c", 99); + LOG_ADDRESS("osvimodep->fldRegs[0].vIntr", osViMode->fldRegs[0].vIntr, "../z_vimode.c", 100); + LOG_ADDRESS("osvimodep->fldRegs[1].vStart", osViMode->fldRegs[1].vStart, "../z_vimode.c", 101); + LOG_ADDRESS("osvimodep->fldRegs[1].vBurst", osViMode->fldRegs[1].vBurst, "../z_vimode.c", 102); + LOG_ADDRESS("osvimodep->fldRegs[1].origin", osViMode->fldRegs[1].origin, "../z_vimode.c", 103); + LOG_ADDRESS("osvimodep->fldRegs[1].yScale", osViMode->fldRegs[1].yScale, "../z_vimode.c", 104); + LOG_ADDRESS("osvimodep->fldRegs[1].vIntr", osViMode->fldRegs[1].vIntr, "../z_vimode.c", 105); +} + +// This function configures the custom VI mode (`viMode.customViMode`) based on the other flags in `viMode`. +void ViMode_Configure(ViMode* viMode, s32 mode, s32 type, s32 unk_70, s32 unk_74, s32 unk_78, s32 unk_7C, s32 width, + s32 height, s32 unk_left, s32 unk_right, s32 unk_top, s32 unk_bottom) { + s32 not_70; + s32 not_74; + s32 not_78; + s32 not_7C; + s32 cond_4C; + s32 cond_48; + s32 cond_44; + s32 cond_40; + s32 cond_3C; + s32 cond_38; + s32 cond_34; + s32 yScaleLo; + s32 yScaleHi0; + s32 yScaleHi1; + + not_70 = !unk_70; + not_74 = !unk_74; + not_78 = !unk_78; + not_7C = !unk_7C; + + cond_4C = not_70 && not_78; + cond_48 = not_70 && unk_78; + cond_44 = unk_70 && unk_78; + cond_40 = unk_70 && not_78; + cond_3C = unk_70 && not_74 && unk_78 && unk_7C; + cond_38 = unk_70 && unk_74 && unk_78 && not_7C; + cond_34 = not_70 && unk_74 && unk_78 && not_7C; + + unk_top &= ~1; + unk_bottom &= ~1; + + yScaleLo = (cond_4C ? 2 : 1) * (((height << 11)) / (SCREEN_HEIGHT * 2 + unk_bottom - unk_top) / (unk_70 ? 1 : 2)); + + yScaleHi0 = not_78 ? (cond_40 ? 0x1000000 : 0x2000000) : 0; + yScaleHi1 = not_78 ? (cond_40 ? 0x3000000 : 0x2000000) : 0; + + viMode->customViMode.type = mode; + viMode->customViMode.comRegs.ctrl = OS_VI_UNK2000 | OS_VI_UNK1000 | OS_VI_GAMMA | OS_VI_GAMMA_DITHER | + (!cond_44 ? OS_VI_UNK40 : 0) | (not_74 ? OS_VI_DIVOT : 0) | + (not_7C ? OS_VI_UNK2 | OS_VI_UNK1 : OS_VI_UNK2); + + if (cond_3C) { + viMode->customViMode.comRegs.ctrl |= 0x100; + } else if (cond_38 | cond_34) { + viMode->customViMode.comRegs.ctrl |= 0x300; + } else if (unk_74) { + viMode->customViMode.comRegs.ctrl |= 0x200; + } else { + viMode->customViMode.comRegs.ctrl |= 0; + } + + viMode->customViMode.comRegs.width = width * (cond_48 ? 2 : 1); + + if (type == 1) { + viMode->customViMode.comRegs.burst = 0x3E52239; + viMode->customViMode.comRegs.vSync = 0x20C; + viMode->customViMode.comRegs.hSync = 0xC15; + viMode->customViMode.comRegs.leap = 0xC150C15; + viMode->customViMode.comRegs.hStart = 0x6C02EC; + viMode->customViMode.fldRegs[0].vStart = 0x2501FF; + viMode->customViMode.fldRegs[0].vBurst = 0xE0204; + } else if (type == 0) { + viMode->customViMode.comRegs.burst = 0x404233A; + viMode->customViMode.comRegs.vSync = 0x270; + viMode->customViMode.comRegs.hSync = 0x150C69; + viMode->customViMode.comRegs.leap = 0xC6F0C6E; + viMode->customViMode.comRegs.hStart = 0x800300; + viMode->customViMode.fldRegs[0].vStart = 0x5F0239; + viMode->customViMode.fldRegs[0].vBurst = 0x9026B; + } else if (type == 2) { + viMode->customViMode.comRegs.burst = 0x4651E39; + viMode->customViMode.comRegs.vSync = 0x20C; + viMode->customViMode.comRegs.hSync = 0xC10; + viMode->customViMode.comRegs.leap = 0xC1C0C1C; + viMode->customViMode.comRegs.hStart = 0x6C02EC; + viMode->customViMode.fldRegs[0].vStart = 0x2501FF; + viMode->customViMode.fldRegs[0].vBurst = 0xE0204; + } + + viMode->customViMode.fldRegs[1].vStart = viMode->customViMode.fldRegs[0].vStart; + + viMode->customViMode.comRegs.hStart += (unk_left << 16) + (s16)unk_right; + viMode->customViMode.fldRegs[0].vStart += (unk_top << 16) + (s16)unk_bottom; + viMode->customViMode.fldRegs[1].vStart += (unk_top << 16) + (s16)unk_bottom; + + viMode->customViMode.fldRegs[1].vBurst = viMode->customViMode.fldRegs[0].vBurst; + + if (cond_44) { + viMode->customViMode.comRegs.vSync++; + if (type == 2) { + viMode->customViMode.comRegs.hSync += 0x40001; + } + if (type == 2) { + viMode->customViMode.comRegs.leap += 0xFFFCFFFE; + } + } else { + viMode->customViMode.fldRegs[0].vStart += 0xFFFDFFFE; + if (type == 2) { + viMode->customViMode.fldRegs[0].vBurst += 0xFFFCFFFE; + } + if (type == 0) { + viMode->customViMode.fldRegs[1].vBurst += 0x2FFFE; + } + } + + viMode->customViMode.comRegs.xScale = (width << 10) / (SCREEN_WIDTH * 2 + unk_right - unk_left); + viMode->customViMode.comRegs.vCurrent = 0; + + viMode->customViMode.fldRegs[0].origin = width * 2 * (unk_7C ? 1 : 2); + viMode->customViMode.fldRegs[1].origin = width * 2 * (unk_7C ? 1 : 2) * (unk_70 ? 1 : 2); + + viMode->customViMode.fldRegs[0].yScale = yScaleLo | yScaleHi0; + viMode->customViMode.fldRegs[1].yScale = yScaleLo | yScaleHi1; + + viMode->customViMode.fldRegs[0].vIntr = 2; + viMode->customViMode.fldRegs[1].vIntr = 2; +} + +void ViMode_Save(ViMode* viMode) { + SREG(48) = viMode->viModeBase; + SREG(49) = viMode->viWidth; + SREG(50) = viMode->viHeight; + SREG(51) = viMode->unk_64; + SREG(52) = viMode->unk_60; + SREG(53) = viMode->unk_5C; + SREG(54) = viMode->unk_58; + if (SREG(58) == 1) { + SREG(58) = 0; + switch (SREG(59)) { + case 1: + osSyncPrintf("osViModePalLan1\n"); + ViMode_LogPrint(&osViModePalLan1); + break; + case 2: + osSyncPrintf("osViModeFpalLan1\n"); + ViMode_LogPrint(&osViModeFpalLan1); + break; + default: + osSyncPrintf("Custom\n"); + ViMode_LogPrint(&viMode->customViMode); + break; + } + } +} + +void ViMode_Load(ViMode* viMode) { + if ((SREG(49) & ~3) == 1) { + SREG(49) += 4; + } + + viMode->viModeBase = SREG(48); + viMode->viWidth = SREG(49) & ~3; + viMode->viHeight = SREG(50); + viMode->unk_64 = SREG(51); + viMode->unk_60 = SREG(52); + viMode->unk_5C = SREG(53); + viMode->unk_58 = SREG(54); +} + +void ViMode_Init(ViMode* viMode) { + viMode->viModeBase = 0; + viMode->viWidth = SCREEN_WIDTH; + viMode->viHeight = SCREEN_HEIGHT; + viMode->unk_5C = 0; + viMode->unk_58 = 0; + viMode->unk_64 = 0; + viMode->unk_60 = 0; + viMode->viFeatures = OS_VI_DITHER_FILTER_ON | OS_VI_GAMMA_OFF; + viMode->viTvType = osTvType; + viMode->unk_7C = true; + viMode->unk_78 = true; + viMode->unk_74 = false; + viMode->unk_70 = true; + + ViMode_Save(viMode); +} + +void ViMode_Destroy(ViMode* viMode) { +} + +void ViMode_ConfigureFeatures(ViMode* viMode, s32 viFeatures) { + u32 ctrl = viMode->customViMode.comRegs.ctrl; + + if (viFeatures & OS_VI_GAMMA_ON) { + ctrl |= OS_VI_GAMMA; + } + if (viFeatures & OS_VI_GAMMA_OFF) { + ctrl &= ~OS_VI_GAMMA; + } + if (viFeatures & OS_VI_GAMMA_DITHER_ON) { + ctrl |= OS_VI_GAMMA_DITHER; + } + if (viFeatures & OS_VI_GAMMA_DITHER_OFF) { + ctrl &= ~OS_VI_GAMMA_DITHER; + } + if (viFeatures & OS_VI_DIVOT_ON) { + ctrl |= OS_VI_DIVOT; + } + if (viFeatures & OS_VI_DIVOT_OFF) { + ctrl &= ~OS_VI_DIVOT; + } + viMode->customViMode.comRegs.ctrl = ctrl; +} + +// This function uses controller input (C buttons + D pad) to reconfigure the custom VI mode +void ViMode_Update(ViMode* viMode, Input* input) { + ViMode_Load(viMode); + if ((viMode->viModeBase == 1) || (viMode->viModeBase == 2) || (viMode->viModeBase == 3)) { + gScreenWidth = viMode->viWidth; + gScreenHeight = viMode->viHeight; + if (CHECK_BTN_ALL(input->cur.button, BTN_START | BTN_CUP | BTN_CRIGHT)) { + ViMode_Init(viMode); + } + if (CHECK_BTN_ALL(input->cur.button, BTN_CUP)) { + if (CHECK_BTN_ALL(input->cur.button, BTN_DUP)) { + viMode->unk_64--; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DDOWN)) { + viMode->unk_64++; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DLEFT)) { + viMode->unk_5C--; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT)) { + viMode->unk_5C++; + } + } + if (CHECK_BTN_ALL(input->cur.button, BTN_CRIGHT)) { + if (CHECK_BTN_ALL(input->cur.button, BTN_DUP)) { + viMode->unk_60--; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DDOWN)) { + viMode->unk_60++; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DLEFT)) { + viMode->unk_58--; + } + if (CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT)) { + viMode->unk_58++; + } + } + if (CHECK_BTN_ALL(input->cur.button, BTN_CDOWN)) { + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + viMode->unk_70 = !viMode->unk_70; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + viMode->unk_74 = !viMode->unk_74; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + viMode->unk_78 = !viMode->unk_78; + } + if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + viMode->unk_7C = !viMode->unk_7C; + } + } + if (viMode->viModeBase >= 2) { + if (viMode->unk_5C < -16) { + viMode->unk_5C = -16; + } + if (viMode->unk_64 < -50) { + viMode->unk_64 = -50; + } + if (viMode->unk_58 > 16) { + viMode->unk_58 = 16; + } + if (viMode->unk_60 > 50) { + viMode->unk_60 = 50; + } + } else { + if (viMode->unk_5C < 0) { + viMode->unk_5C = 0; + } + if (viMode->unk_64 < 0) { + viMode->unk_64 = 0; + } + if (viMode->unk_58 > 0) { + viMode->unk_58 = 0; + } + if (viMode->unk_60 > 0) { + viMode->unk_60 = 0; + } + } + ViMode_Configure(viMode, OS_VI_UNK28, osTvType, viMode->unk_70, viMode->unk_74, viMode->unk_78, viMode->unk_7C, + viMode->viWidth, viMode->viHeight, viMode->unk_5C, viMode->unk_58, viMode->unk_64, + viMode->unk_60); + ViMode_ConfigureFeatures(viMode, viMode->viFeatures); + if (viMode->viModeBase == 3) { + ViMode_LogPrint(&osViModeNtscLan1); + ViMode_LogPrint(&viMode->customViMode); + viMode->viModeBase = 2; + } + } + ViMode_Save(viMode); +} diff --git a/soh/src/code/z_vismono.c b/soh/src/code/z_vismono.c new file mode 100644 index 000000000..82acc9a17 --- /dev/null +++ b/soh/src/code/z_vismono.c @@ -0,0 +1,136 @@ +#include "global.h" +#include + +// (Note: 80 = SCREEN_HEIGHT/3, see VisMono_DrawTexture) +// This may not have been kept up-to-date with the code, 1+1+1+80*(7+2+2+3)+1+1 makes more sense +#define DLSIZE (1 + 3 + 1 + 1 + 80 * (7 + 2 + 2 + 3) + 1) + +// framebuffer +extern u16 D_0F000000[]; + +void VisMono_Init(VisMono* this) { + memset(this, 0, sizeof(VisMono)); + this->unk_00 = 0; + this->setScissor = false; + this->primColor.r = 255; + this->primColor.g = 255; + this->primColor.b = 255; + this->primColor.a = 255; + this->envColor.r = 0; + this->envColor.g = 0; + this->envColor.b = 0; + this->envColor.a = 0; +} + +void VisMono_Destroy(VisMono* this) { + SystemArena_FreeDebug(this->monoDl, "../z_vismono.c", 137); +} + +void VisMono_UpdateTexture(VisMono* this, u16* tex) { + s32 i; + + for (i = 0; i < 256; i++) { + tex[i] = ((((i >> 3 & 0x1F) * 2 + (i << 2 & 0x1F) * 4) * 0xFF / 0xD9) << 8) | + (((i >> 6 & 0x1F) * 4 + (i >> 1 & 0x1F)) * 0xFF / 0xD9); + } +} + +Gfx* VisMono_DrawTexture(VisMono* this, Gfx* gfx) +{ +// OTRTODO +#if 1 + s32 y; + s32 height = 3; + //u16* tex = D_0F000000; + u16* tex = 0xFF000000; + + gDPPipeSync(gfx++); + gDPSetOtherMode(gfx++, + G_AD_DISABLE | G_CD_DISABLE | G_CK_NONE | G_TC_FILT | G_TF_POINT | G_TT_IA16 | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_2CYCLE | G_PM_1PRIMITIVE, + G_AC_NONE | G_ZS_PRIM | GBL_c1(G_BL_CLR_IN, G_BL_0, G_BL_CLR_IN, G_BL_1) | G_RM_CLD_SURF2); + gDPSetCombineLERP(gfx++, 1, 0, TEXEL1_ALPHA, TEXEL0, 0, 0, 0, 1, PRIMITIVE, ENVIRONMENT, COMBINED, ENVIRONMENT, 0, + 0, 0, PRIMITIVE); + + for (y = 0; y <= SCREEN_HEIGHT - height; y += height) { + gDPLoadTextureBlock(gfx++, tex, G_IM_FMT_CI, G_IM_SIZ_8b, SCREEN_WIDTH * 2, height, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, 80, 0x0, G_TX_RENDERTILE, 0, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, + G_TX_NOMIRROR | G_TX_CLAMP, 0, 0); + gDPSetTileSize(gfx++, G_TX_RENDERTILE, (2 << 2), 0, ((SCREEN_WIDTH * 2 + 1) << 2), (2 << 2)); + + gDPSetTile(gfx++, G_IM_FMT_CI, G_IM_SIZ_8b, 80, 0x0, 1, 1, G_TX_NOMIRROR | G_TX_CLAMP, 0, 0, + G_TX_NOMIRROR | G_TX_CLAMP, 0, 0); + gDPSetTileSize(gfx++, 1, (1 << 2), 0, ((SCREEN_WIDTH * 2) << 2), (2 << 2)); + + gSPTextureRectangle(gfx++, 0, y << 2, (SCREEN_WIDTH << 2), (y + height) << 2, G_TX_RENDERTILE, 2 << 5, 0, + 2 << 10, 1 << 10); + tex += SCREEN_WIDTH * height; + } + + gDPPipeSync(gfx++); + gSPEndDisplayList(gfx++); +#endif + return gfx; +} + +void VisMono_Draw(VisMono* this, Gfx** gfxp) { + Gfx* gfx = *gfxp; + u16* tlut; + Gfx* monoDL; + Gfx* glistpEnd; + + if (this->tlut) { + tlut = this->tlut; + } else { + tlut = Graph_DlistAlloc(&gfx, 256 * sizeof(u16)); + VisMono_UpdateTexture(this, tlut); + } + + if (this->monoDl) { + monoDL = this->monoDl; + } else { + monoDL = Graph_DlistAlloc(&gfx, DLSIZE * sizeof(Gfx)); + glistpEnd = VisMono_DrawTexture(this, monoDL); + + if (!(glistpEnd <= monoDL + DLSIZE)) { + LOG_ADDRESS("glistp_end", glistpEnd, "../z_vismono.c", 257); + LOG_ADDRESS("mono_dl", monoDL, "../z_vismono.c", 258); + LOG_ADDRESS("mono_dl + (1+3+1+1+80*(7+2+2+3)+1)", monoDL + DLSIZE, "../z_vismono.c", 259); + LOG_ADDRESS("(1+3+1+1+80*(7+2+2+3)+1)", DLSIZE, "../z_vismono.c", 260); + } + ASSERT(glistpEnd <= monoDL + DLSIZE, "glistp_end <= mono_dl + DLSIZE", "../z_vismono.c", 262); + } + + gDPPipeSync(gfx++); + if (this->setScissor == true) { + gDPSetScissor(gfx++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + } + + gDPSetColor(gfx++, G_SETPRIMCOLOR, this->primColor.rgba); + gDPSetColor(gfx++, G_SETENVCOLOR, this->envColor.rgba); + + gDPLoadTLUT_pal256(gfx++, tlut); + + gSPDisplayList(gfx++, monoDL); + gDPPipeSync(gfx++); + + *gfxp = gfx; +} + +void VisMono_DrawOld(VisMono* this) { + Gfx* glistpEnd; + + if (!this->tlut) { + this->tlut = SystemArena_MallocDebug(256 * sizeof(u16), "../z_vismono.c", 283); + VisMono_UpdateTexture(this, this->tlut); + } + + if (!this->monoDl) { + this->monoDl = SystemArena_MallocDebug(DLSIZE * sizeof(Gfx), "../z_vismono.c", 289); + glistpEnd = VisMono_DrawTexture(this, this->monoDl); + ASSERT(glistpEnd <= this->monoDl + DLSIZE, "glistp_end <= this->mono_dl + DLSIZE", "../z_vismono.c", 292); + } +} diff --git a/soh/src/code/z_vr_box.c b/soh/src/code/z_vr_box.c new file mode 100644 index 000000000..7925e5cb9 --- /dev/null +++ b/soh/src/code/z_vr_box.c @@ -0,0 +1,1053 @@ +#include "global.h" +#include "vt.h" + +#include + +#include "z64environment.h" +#include "assets/textures/backgrounds/vr_ALVR_static.h" +#include "assets/textures/backgrounds/vr_ALVR_pal_static.h" +#include "assets/textures/backgrounds/vr_DGVR_static.h" +#include "assets/textures/backgrounds/vr_DGVR_pal_static.h" +#include "assets/textures/backgrounds/vr_FCVR_static.h" +#include "assets/textures/backgrounds/vr_FCVR_pal_static.h" +#include "assets/textures/backgrounds/vr_GLVR_static.h" +#include "assets/textures/backgrounds/vr_GLVR_pal_static.h" +#include "assets/textures/backgrounds/vr_IPVR_static.h" +#include "assets/textures/backgrounds/vr_IPVR_pal_static.h" +#include "assets/textures/backgrounds/vr_K3VR_static.h" +#include "assets/textures/backgrounds/vr_K3VR_pal_static.h" +#include "assets/textures/backgrounds/vr_K4VR_static.h" +#include "assets/textures/backgrounds/vr_K4VR_pal_static.h" +#include "assets/textures/backgrounds/vr_K5VR_static.h" +#include "assets/textures/backgrounds/vr_K5VR_pal_static.h" +#include "assets/textures/backgrounds/vr_KHVR_static.h" +#include "assets/textures/backgrounds/vr_KHVR_pal_static.h" +#include "assets/textures/backgrounds/vr_KKRVR_static.h" +#include "assets/textures/backgrounds/vr_KKRVR_pal_static.h" +#include "assets/textures/backgrounds/vr_KR3VR_static.h" +#include "assets/textures/backgrounds/vr_KR3VR_pal_static.h" +#include "assets/textures/backgrounds/vr_KSVR_static.h" +#include "assets/textures/backgrounds/vr_KSVR_pal_static.h" +#include "assets/textures/backgrounds/vr_LBVR_static.h" +#include "assets/textures/backgrounds/vr_LBVR_pal_static.h" +#include "assets/textures/backgrounds/vr_LHVR_static.h" +#include "assets/textures/backgrounds/vr_LHVR_pal_static.h" +#include "assets/textures/backgrounds/vr_MDVR_static.h" +#include "assets/textures/backgrounds/vr_MDVR_pal_static.h" +#include "assets/textures/backgrounds/vr_MLVR_static.h" +#include "assets/textures/backgrounds/vr_MLVR_pal_static.h" +#include "assets/textures/backgrounds/vr_MNVR_static.h" +#include "assets/textures/backgrounds/vr_MNVR_pal_static.h" +#include "assets/textures/backgrounds/vr_NSVR_static.h" +#include "assets/textures/backgrounds/vr_NSVR_pal_static.h" +#include "assets/textures/backgrounds/vr_RUVR_static.h" +#include "assets/textures/backgrounds/vr_RUVR_pal_static.h" +#include "assets/textures/backgrounds/vr_SP1a_static.h" +#include "assets/textures/backgrounds/vr_SP1a_pal_static.h" +#include "assets/textures/backgrounds/vr_TTVR_static.h" +#include "assets/textures/backgrounds/vr_TTVR_pal_static.h" +#include "assets/textures/backgrounds/vr_ZRVR_static.h" +#include "assets/textures/backgrounds/vr_ZRVR_pal_static.h" + +#include "assets/textures/skyboxes/vr_cloud0_static.h" +#include "assets/textures/skyboxes/vr_cloud0_pal_static.h" +#include "assets/textures/skyboxes/vr_cloud1_static.h" +#include "assets/textures/skyboxes/vr_cloud1_pal_static.h" +#include "assets/textures/skyboxes/vr_cloud2_static.h" +#include "assets/textures/skyboxes/vr_cloud2_pal_static.h" +#include "assets/textures/skyboxes/vr_cloud3_static.h" +#include "assets/textures/skyboxes/vr_cloud3_pal_static.h" +#include "assets/textures/skyboxes/vr_fine0_static.h" +#include "assets/textures/skyboxes/vr_fine0_pal_static.h" +#include "assets/textures/skyboxes/vr_fine1_static.h" +#include "assets/textures/skyboxes/vr_fine1_pal_static.h" +#include "assets/textures/skyboxes/vr_fine2_static.h" +#include "assets/textures/skyboxes/vr_fine2_pal_static.h" +#include "assets/textures/skyboxes/vr_fine3_static.h" +#include "assets/textures/skyboxes/vr_fine3_pal_static.h" +#include "assets/textures/skyboxes/vr_holy0_static.h" +#include "assets/textures/skyboxes/vr_holy0_pal_static.h" +#include "assets/textures/skyboxes/vr_holy1_static.h" +#include "assets/textures/skyboxes/vr_holy1_pal_static.h" + + +u32 D_8012AC90[4] = { + 0x00000000, + 0x00010000, + 0x00020000, + 0x00030000, +}; + +u16 D_8012ACA0[2][0x20] = { + { 0x00, 0x02, 0x0A, 0x0C, 0x02, 0x04, 0x0C, 0x0E, 0x0A, 0x0C, 0x14, 0x16, 0x0C, 0x0E, 0x16, 0x18, + 0x01, 0x03, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x15, 0x17 }, + { 0x14, 0x16, 0x1E, 0x20, 0x16, 0x18, 0x20, 0x22, 0x1E, 0x20, 0x28, 0x2A, 0x20, 0x22, 0x2A, 0x2C, + 0x15, 0x17, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1F, 0x21, 0x23, 0x24, 0x25, 0x26, 0x27, 0x29, 0x2B }, +}; + +s16 D_8012AD20[5] = { + 0x0000, 0x0FC0, 0x1F80, 0x2F40, 0x3F00, +}; + +s16 D_8012AD2C[9] = { + 0x0000, 0x07C0, 0x0F80, 0x1740, 0x1F00, 0x26C0, 0x2E80, 0x3640, 0x3E00, +}; + +s16 D_8012AD40[0x40] = { + 0x00, 0x10, 0x13, 0x12, 0x10, 0x01, 0x14, 0x13, 0x01, 0x11, 0x15, 0x14, 0x11, 0x05, 0x16, 0x15, + 0x12, 0x13, 0x17, 0x02, 0x13, 0x14, 0x03, 0x17, 0x14, 0x15, 0x18, 0x03, 0x15, 0x16, 0x07, 0x18, + 0x02, 0x17, 0x1A, 0x19, 0x17, 0x03, 0x1B, 0x1A, 0x03, 0x18, 0x1C, 0x1B, 0x18, 0x07, 0x1D, 0x1C, + 0x19, 0x1A, 0x1E, 0x0A, 0x1A, 0x1B, 0x0B, 0x1E, 0x1B, 0x1C, 0x1F, 0x0B, 0x1C, 0x1D, 0x0F, 0x1F, +}; + +u32 D_8012ADC0[6] = { + 0x00000000, 0x00002000, 0x00004000, 0x00006000, 0x00008000, 0x0000C000, +}; + +u16 D_8012ADD8[0x20] = { + 0x00, 0x02, 0x0A, 0x0C, 0x02, 0x04, 0x0C, 0x0E, 0x0A, 0x0C, 0x14, 0x16, 0x0C, 0x0E, 0x16, 0x18, + 0x01, 0x03, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x0D, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x15, 0x17, +}; + +s16 D_8012AE18[5] = { + 0x0000, 0x07C0, 0x0F80, 0x1740, 0x1F00, +}; + +s16 D_8012AE24[5] = { + 0x0000, 0x07C0, 0x0F80, 0x1740, 0x1F00, +}; + +s16 D_8012AE30[5] = { + 0x0000, 0x07C0, 0x0F80, 0x07C0, 0x0000, +}; + +s16 D_8012AE3C[0x40] = { + 0x00, 0x10, 0x13, 0x12, 0x10, 0x01, 0x14, 0x13, 0x01, 0x11, 0x15, 0x14, 0x11, 0x05, 0x16, 0x15, + 0x12, 0x13, 0x17, 0x02, 0x13, 0x14, 0x03, 0x17, 0x14, 0x15, 0x18, 0x03, 0x15, 0x16, 0x07, 0x18, + 0x02, 0x17, 0x1A, 0x19, 0x17, 0x03, 0x1B, 0x1A, 0x03, 0x18, 0x1C, 0x1B, 0x18, 0x07, 0x1D, 0x1C, + 0x19, 0x1A, 0x1E, 0x0A, 0x1A, 0x1B, 0x0B, 0x1E, 0x1B, 0x1C, 0x1F, 0x0B, 0x1C, 0x1D, 0x0F, 0x1F, +}; + +typedef struct { + /* 0x000 */ s32 unk_0; + /* 0x004 */ s32 unk_4; + /* 0x008 */ s32 unk_8; + /* 0x00C */ s32 unk_C; + /* 0x010 */ s32 unk_10; +} Struct_8012AF0C; // size = 0x14 + +Struct_8012AF0C D_8012AEBC[4] = { + { -0x7E, 0x7C, -0x7E, 0x3F, -0x1F }, + { 0x7E, 0x7C, -0x7E, 0x3F, -0x1F }, + { 0x7E, 0x7C, 0x7E, -0x3F, -0x1F }, + { -0x7E, 0x7C, 0x7E, -0x3F, -0x1F }, +}; + +Struct_8012AF0C D_8012AF0C[6] = { + { -0x40, 0x40, -0x40, 0x20, -0x20 }, { 0x40, 0x40, 0x40, -0x20, -0x20 }, { -0x40, 0x40, 0x40, -0x20, -0x20 }, + { 0x40, 0x40, -0x40, 0x20, -0x20 }, { -0x40, 0x40, 0x40, 0x20, -0x20 }, { -0x40, -0x40, -0x40, 0x20, 0x20 }, +}; + +s32 func_800ADBB0(SkyboxContext* skyboxCtx, Vtx* roomVtx, s32 arg2, s32 arg3, s32 arg4, s32 arg5, s32 arg6, s32 arg7, + s32 arg8, s32 arg9) { + u32 pad42C; + s32 pad428; + s32 sp424; + s32 i; + s32 j; + s32 k; + u16 index; + s16 phi_t1; + s16 phi_a2_4; + s16 phi_a0_4; + s16 phi_t2_4; + s16 phi_ra; + s32 sp358[9 * 5]; + s32 sp2A4[9 * 5]; + s32 sp1F0[9 * 5]; + s32 sp13C[9 * 5]; + s32 sp88[9 * 5]; + s32 pad; + + switch (arg8) { + case 0: + case 2: + pad428 = arg4; + + for (i = 0, k = 0; k < 45; i++) { + pad42C = arg3; + for (j = 0; j < 5; j++, k++) { + sp1F0[k] = arg5; + sp358[k] = pad42C; + sp2A4[k] = pad428; + sp13C[k] = D_8012AD20[j]; + sp88[k] = D_8012AD2C[i]; + pad42C += arg6; + } + pad428 += arg7; + } + break; + + case 1: + case 3: + pad428 = arg4; + + for (i = 0, k = 0; k < 45; i++) { + pad42C = arg5; + for (j = 0; j < 5; j++, k++) { + sp358[k] = arg3; + sp2A4[k] = pad428; + sp1F0[k] = pad42C; + sp13C[k] = D_8012AD20[j]; + sp88[k] = D_8012AD2C[i]; + pad42C += arg6; + } + pad428 += arg7; + } + break; + + case 4: + case 5: + pad428 = arg5; + + for (i = 0, k = 0; k < 45; i++) { + pad42C = arg3; + for (j = 0; j < 5; j++, k++) { + sp2A4[k] = arg4; + sp358[k] = pad42C; + sp1F0[k] = pad428; + sp13C[k] = D_8012AD20[j]; + sp88[k] = D_8012AD2C[i]; + pad42C += arg6; + } + pad428 += arg7; + } + break; + } + + for (phi_a2_4 = 0, sp424 = 0; sp424 < 2; sp424++) { + skyboxCtx->unk_138 = skyboxCtx->dListBuf[arg9 + sp424]; + + for (i = 0; i < 0x20; i++) { + index = D_8012ACA0[sp424][i]; + + roomVtx[arg2 + i].v.ob[0] = sp358[index]; + roomVtx[arg2 + i].v.ob[1] = sp2A4[index]; + roomVtx[arg2 + i].v.ob[2] = sp1F0[index]; + roomVtx[arg2 + i].v.flag = 0; + roomVtx[arg2 + i].v.tc[0] = sp13C[index]; + roomVtx[arg2 + i].v.tc[1] = sp88[index]; + roomVtx[arg2 + i].v.cn[1] = 0; + roomVtx[arg2 + i].v.cn[2] = 0; + roomVtx[arg2 + i].v.cn[0] = 255; + } + gSPVertex(skyboxCtx->unk_138++, &roomVtx[arg2], 32, 0); + arg2 += i; + gSPCullDisplayList(skyboxCtx->unk_138++, 0, 15); + + + for (phi_t2_4 = 0, phi_ra = 0; phi_ra < 4; phi_ra++, phi_a2_4 += 0x1F) { + for (phi_a0_4 = 0, phi_t1 = 0; phi_t1 < 4; phi_t1++, phi_a0_4 += 0x3F, phi_t2_4 += 4) { + gDPLoadTextureTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[0] + D_8012AC90[arg8], + G_IM_FMT_CI, G_IM_SIZ_8b, 256, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x3F, + phi_a2_4 + 0x1F, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); + + gSP1Quadrangle(skyboxCtx->unk_138++, D_8012AD40[phi_t2_4 + 1], D_8012AD40[phi_t2_4 + 2], + D_8012AD40[phi_t2_4 + 3], D_8012AD40[phi_t2_4 + 0], 3); + } + } + gSPEndDisplayList(skyboxCtx->unk_138++); + } + return arg2; +} + +s32 func_800AE2C0(SkyboxContext* skyboxCtx, Vtx* roomVtx, s32 arg2, s32 arg3, s32 arg4, s32 arg5, s32 arg6, s32 arg7, + s32 arg8) { + s32 i; + s32 j; + s32 k; + s16 phi_a0_4; + s16 phi_t1; + s32 temp1; + u16 index; + s16 phi_a2_4; + s16 phi_ra; + s16 phi_t2_4; + s32 temp2; + s32 sp2B4[5 * 5]; + s32 sp250[5 * 5]; + s32 sp1EC[5 * 5]; + s32 sp188[5 * 5]; + s32 sp124[5 * 5]; + s32 pad; + + switch (arg8) { + case 0: + case 1: + temp1 = arg4; + + for (i = 0, k = 0; k < 25; i++) { + temp2 = arg3; + for (j = 0; j < 5; j++, k++) { + sp1EC[k] = arg5; + sp2B4[k] = temp2; + sp250[k] = temp1; + sp188[k] = D_8012AE18[j]; + sp124[k] = D_8012AE30[i]; + temp2 += arg6; + } + temp1 += arg7; + } + break; + + case 2: + case 3: + temp1 = arg4; + + for (i = 0, k = 0; k < 25; i++) { + temp2 = arg5; + for (j = 0; j < 5; j++, k++) { + sp2B4[k] = arg3; + sp250[k] = temp1; + sp1EC[k] = temp2; + sp188[k] = D_8012AE18[j]; + sp124[k] = D_8012AE30[i]; + temp2 += arg6; + } + temp1 += arg7; + } + break; + + case 4: + case 5: + temp1 = arg5; + + for (i = 0, k = 0; k < 25; i++) { + temp2 = arg3; + for (j = 0; j < 5; j++, k++) { + sp250[k] = arg4; + sp2B4[k] = temp2; + sp1EC[k] = temp1; + sp188[k] = D_8012AE18[j]; + sp124[k] = D_8012AE24[i]; + temp2 += arg6; + } + temp1 += arg7; + } + break; + } + skyboxCtx->unk_138 = &skyboxCtx->dListBuf[2 * arg8]; + + for (i = 0; i < 0x20; i++) { + index = D_8012ADD8[i]; + + roomVtx[arg2 + i].v.ob[0] = sp2B4[index]; + roomVtx[arg2 + i].v.ob[1] = sp250[index]; + roomVtx[arg2 + i].v.ob[2] = sp1EC[index]; + roomVtx[arg2 + i].v.flag = 0; + roomVtx[arg2 + i].v.tc[0] = sp188[index]; + roomVtx[arg2 + i].v.tc[1] = sp124[index]; + roomVtx[arg2 + i].v.cn[1] = 0; + roomVtx[arg2 + i].v.cn[2] = 0; + roomVtx[arg2 + i].v.cn[0] = 255; + } + gSPVertex(skyboxCtx->unk_138++, &roomVtx[arg2], 32, 0); + arg2 += i; + gSPCullDisplayList(skyboxCtx->unk_138++, 0, 15); + + if ((arg8 == 4) || (arg8 == 5)) { + phi_a2_4 = 0; + for (phi_t2_4 = 0, phi_ra = 0; phi_ra < 4; phi_ra++, phi_a2_4 += 0x1F) { + for (phi_a0_4 = 0, phi_t1 = 0; phi_t1 < 4; phi_t1++, phi_a0_4 += 0x1F, phi_t2_4 += 4) { + gDPLoadMultiTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[0] + D_8012ADC0[arg8], 0, + G_TX_RENDERTILE, G_IM_FMT_CI, G_IM_SIZ_8b, 128, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x1F, + phi_a2_4 + 0x1F, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); + gDPLoadMultiTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[1] + D_8012ADC0[arg8], 0x80, 1, + G_IM_FMT_CI, G_IM_SIZ_8b, 128, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x1F, phi_a2_4 + 0x1F, + 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOLOD); + + gDPNoOpString(skyboxCtx->unk_138++, "TEST2", 0); + + gSP1Quadrangle(skyboxCtx->unk_138++, D_8012AE3C[phi_t2_4 + 1], D_8012AE3C[phi_t2_4 + 2], + D_8012AE3C[phi_t2_4 + 3], D_8012AE3C[phi_t2_4 + 0], 3); + } + } + } else { + phi_a2_4 = 0; + for (phi_t2_4 = 0, phi_ra = 0; phi_ra < 2; phi_ra++, phi_a2_4 += 0x1F) { + for (phi_a0_4 = 0, phi_t1 = 0; phi_t1 < 4; phi_t1++, phi_a0_4 += 0x1F, phi_t2_4 += 4) { + gDPLoadMultiTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[0] + D_8012ADC0[arg8], 0, + G_TX_RENDERTILE, G_IM_FMT_CI, G_IM_SIZ_8b, 128, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x1F, + phi_a2_4 + 0x1F, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); + gDPLoadMultiTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[1] + D_8012ADC0[arg8], 0x80, 1, + G_IM_FMT_CI, G_IM_SIZ_8b, 128, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x1F, phi_a2_4 + 0x1F, + 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOLOD); + gSP1Quadrangle(skyboxCtx->unk_138++, D_8012AE3C[phi_t2_4 + 1], D_8012AE3C[phi_t2_4 + 2], + D_8012AE3C[phi_t2_4 + 3], D_8012AE3C[phi_t2_4 + 0], 3); + } + } + phi_a2_4 -= 0x1F; + for (phi_ra = 0; phi_ra < 2; phi_ra++, phi_a2_4 -= 0x1F) { + for (phi_a0_4 = 0, phi_t1 = 0; phi_t1 < 4; phi_t1++, phi_a0_4 += 0x1F, phi_t2_4 += 4) { + gDPLoadMultiTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[0] + D_8012ADC0[arg8], 0, + G_TX_RENDERTILE, G_IM_FMT_CI, G_IM_SIZ_8b, 128, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x1F, + phi_a2_4 + 0x1F, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD); + gDPLoadMultiTile(skyboxCtx->unk_138++, (uintptr_t)skyboxCtx->staticSegments[1] + D_8012ADC0[arg8], 0x80, 1, + G_IM_FMT_CI, G_IM_SIZ_8b, 128, 0, phi_a0_4, phi_a2_4, phi_a0_4 + 0x1F, phi_a2_4 + 0x1F, + 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOLOD); + gSP1Quadrangle(skyboxCtx->unk_138++, D_8012AE3C[phi_t2_4 + 1], D_8012AE3C[phi_t2_4 + 2], + D_8012AE3C[phi_t2_4 + 3], D_8012AE3C[phi_t2_4 + 0], 3); + } + } + } + gSPEndDisplayList(skyboxCtx->unk_138++); + return arg2; +} + +void func_800AEFC8(SkyboxContext* skyboxCtx, s16 skyboxId) { + s32 i; + s32 j; + s32 phi_s3 = 0; + + if (skyboxId == SKYBOX_BAZAAR || (skyboxId > SKYBOX_HOUSE_KAKARIKO && skyboxId <= SKYBOX_BOMBCHU_SHOP)) { + for (j = 0, i = 0; i < 2; i++, j += 2) { + phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4, + D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j); + } + } else if (skyboxCtx->unk_140 == 2) { + for (j = 0, i = 0; i < 3; i++, j += 2) { + phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4, + D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j); + } + } else { + for (j = 0, i = 0; i < 4; i++, j += 2) { + phi_s3 = func_800ADBB0(skyboxCtx, skyboxCtx->roomVtx, phi_s3, D_8012AEBC[i].unk_0, D_8012AEBC[i].unk_4, + D_8012AEBC[i].unk_8, D_8012AEBC[i].unk_C, D_8012AEBC[i].unk_10, i, j); + } + } +} + +void func_800AF178(SkyboxContext* skyboxCtx, s32 arg1) { + s32 phi_s2 = 0; + s32 i; + + for (i = 0; i < arg1; i++) { + phi_s2 = func_800AE2C0(skyboxCtx, skyboxCtx->roomVtx, phi_s2, D_8012AF0C[i].unk_0, D_8012AF0C[i].unk_4, + D_8012AF0C[i].unk_8, D_8012AF0C[i].unk_C, D_8012AF0C[i].unk_10, i); + } +} + +void LoadSkyboxTex(GlobalContext* globalCtx, SkyboxContext* skyboxCtx, int segmentIndex, int imageIndex, char* tex, int width, int height, int offsetW, int offsetH) +{ + if (globalCtx != NULL && globalCtx->state.gfxCtx != NULL && globalCtx->state.gfxCtx != 0xABABABAB) + gSPInvalidateTexCache(globalCtx->state.gfxCtx->polyOpa.p++, ((uintptr_t)skyboxCtx->staticSegments[segmentIndex] + (imageIndex * (offsetW * offsetH)))); + + memcpy((uintptr_t)skyboxCtx->staticSegments[segmentIndex] + (imageIndex * (offsetW * offsetH)), ResourceMgr_LoadTexByName(tex), width * height); +} + +void LoadSkyboxTexAtOffset(SkyboxContext* skyboxCtx, int segmentIndex, char* tex, int width, int height, int offset) +{ + memcpy((uintptr_t)skyboxCtx->staticSegments[segmentIndex] + offset, ResourceMgr_LoadTexByName(tex), width * height); +} + +void LoadSkyboxPalette(GlobalContext* globalCtx, SkyboxContext* skyboxCtx, int paletteIndex, char* palTex, int width, + int height) { + if (globalCtx != NULL && globalCtx->state.gfxCtx != NULL && globalCtx->state.gfxCtx != 0xABABABAB) + gSPInvalidateTexCache(globalCtx->state.gfxCtx->polyOpa.p++, (uintptr_t)skyboxCtx->palettes + (paletteIndex * (width * height * 2))); + + memcpy((uintptr_t)skyboxCtx->palettes + (paletteIndex * (width * height * 2)), ResourceMgr_LoadTexByName(palTex), width * height * 2); +} + +static const char* sSBVRFine0Tex[] = +{ + gSunriseSkybox1Tex, gSunriseSkybox2Tex, gSunriseSkybox3Tex, gSunriseSkybox4Tex, gSunriseSkybox5Tex +}; + +static const char* sSBVRFine0Pal[] = +{ + gSunriseSkyboxTlut +}; + +static const char* sSBVRFine1Tex[] = +{ + gDaySkybox1Tex, gDaySkybox2Tex, gDaySkybox3Tex, gDaySkybox4Tex, gDaySkybox5Tex +}; + +static const char* sSBVRFine1Pal[] = +{ + gDaySkyboxTlut +}; + +static const char* sSBVRFine2Tex[] = +{ + gSunsetSkybox1Tex, gSunsetSkybox2Tex, gSunsetSkybox3Tex, gSunsetSkybox4Tex, gSunsetSkybox5Tex +}; + +static const char* sSBVRFine2Pal[] = +{ + gSunsetSkyboxTlut +}; + +static const char* sSBVRFine3Tex[] = +{ + gNightSkybox1Tex, gNightSkybox2Tex, gNightSkybox3Tex, gNightSkybox4Tex, gNightSkybox5Tex +}; + +static const char* sSBVRFine3Pal[] = +{ + gNightSkyboxTlut +}; + +static const char* sSBVRCloud0Tex[] = +{ + gSunriseOvercastSkybox1Tex, gSunriseOvercastSkybox2Tex, gSunriseOvercastSkybox3Tex, gSunriseOvercastSkybox4Tex, gSunriseOvercastSkybox5Tex +}; + +static const char* sSBVRCloud0Pal[] = +{ + gSunriseOvercastSkyboxTlut +}; + +static const char* sSBVRCloud1Tex[] = +{ + gDayOvercastSkybox1Tex, gDayOvercastSkybox2Tex, gDayOvercastSkybox3Tex, gDayOvercastSkybox4Tex, gDayOvercastSkybox5Tex +}; + +static const char* sSBVRCloud1Pal[] = +{ + gDayOvercastSkyboxTlut +}; + +static const char* sSBVRCloud2Tex[] = +{ + gSunsetOvercastSkybox1Tex, gSunsetOvercastSkybox2Tex, gSunsetOvercastSkybox3Tex, gSunsetOvercastSkybox4Tex, gSunsetOvercastSkybox5Tex +}; + +static const char* sSBVRCloud2Pal[] = +{ + gSunsetOvercastSkyboxTlut +}; + +static const char* sSBVRCloud3Tex[] = +{ + gNightOvercastSkybox1Tex, gNightOvercastSkybox2Tex, gNightOvercastSkybox3Tex, gNightOvercastSkybox4Tex, gNightOvercastSkybox5Tex +}; + +static const char* sSBVRCloud3Pal[] = +{ + gNightOvercastSkyboxTlut +}; + +static const char* sSBVRHoly0Tex[] = +{ + gHoly0Skybox1Tex, gHoly0Skybox2Tex, gHoly0Skybox3Tex, gHoly0Skybox4Tex, gHoly0Skybox5Tex +}; + +static const char* sSBVRHoly0Pal[] = +{ + gHoly0SkyboxTlut +}; + + +SkyboxTableEntry sSkyboxTable[] = +{ + { + sSBVRFine0Tex, + sSBVRFine0Pal + + }, + { + sSBVRFine1Tex, + sSBVRFine1Pal + }, + { + sSBVRFine2Tex, + sSBVRFine2Pal + }, + { + sSBVRFine3Tex, + sSBVRFine3Pal + }, + { + sSBVRCloud0Tex, + sSBVRCloud0Pal, + }, + { + sSBVRCloud1Tex, + sSBVRCloud1Pal, + }, + { + sSBVRCloud2Tex, + sSBVRCloud2Pal, + }, + { + sSBVRCloud3Tex, + sSBVRCloud3Pal, + }, + { + sSBVRHoly0Tex, + sSBVRHoly0Pal, + }, +}; + +void Skybox_Setup(GlobalContext* globalCtx, SkyboxContext* skyboxCtx, s16 skyboxId) { + size_t size; + s16 i; + u8 sp41; // imageIdx + u8 sp40; // imageIdx2 + uintptr_t start; + s32 phi_v1; + + switch (skyboxId) + { + case SKYBOX_NORMAL_SKY: + phi_v1 = 0; + if (gSaveContext.unk_13C3 != 0 && gSaveContext.sceneSetupIndex < 4 && gWeatherMode > 0 && + gWeatherMode < 6) { + phi_v1 = 1; + } + + for (i = 0; i < 9; i++) { + if (gSaveContext.skyboxTime >= D_8011FC1C[phi_v1][i].startTime && + (gSaveContext.skyboxTime < D_8011FC1C[phi_v1][i].endTime || + D_8011FC1C[phi_v1][i].endTime == 0xFFFF)) { + globalCtx->envCtx.skybox1Index = sp41 = D_8011FC1C[phi_v1][i].skybox1Index; + globalCtx->envCtx.skybox2Index = sp40 = D_8011FC1C[phi_v1][i].skybox2Index; + if (D_8011FC1C[phi_v1][i].blend != 0) { + globalCtx->envCtx.skyboxBlend = + Environment_LerpWeight(D_8011FC1C[phi_v1][i].endTime, D_8011FC1C[phi_v1][i].startTime, + ((void)0, gSaveContext.skyboxTime)) * + 255.0f; + } + else { + globalCtx->envCtx.skyboxBlend = 0; + } + break; + } + } + + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, (128 * 64 * 4) + (128 * 128), "../z_vr_box.c", 1054); + + SkyboxTableEntry entryA = sSkyboxTable[sp41]; + + for (int i = 0; i < 5; i++) + LoadSkyboxTex(globalCtx, skyboxCtx, 0, i, entryA.textures[i], 128, i == 4 ? 128 : 64, 128, 64); + + SkyboxTableEntry entryB = sSkyboxTable[sp40]; + + skyboxCtx->staticSegments[1] = GameState_Alloc(&globalCtx->state, (128 * 64 * 4) + (128 * 128), "../z_vr_box.c", 1060); + + for (int i = 0; i < 5; i++) + LoadSkyboxTex(globalCtx, skyboxCtx, 1, i, entryB.textures[i], 128, i == 4 ? 128 : 64, 128, 64); + + if ((sp41 & 1) ^ ((sp41 & 4) >> 2)) + { + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 256 * 2, "../z_vr_box.c", 1072); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, entryA.palettes[0], 16, 8); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, entryB.palettes[0], 16, 8); + } + else + { + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 256 * 2, "../z_vr_box.c", 1085); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, entryB.palettes[0], 16, 8); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, entryA.palettes[0], 16, 8); + } + break; + case SKYBOX_BAZAAR: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gBazaarBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gBazaar2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gBazaarBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gBazaarBg2Tlut, 16, 16); + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_HOUSE_LINK: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gLinksHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gLinksHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gLinksHouse3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gLinksHouse4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gLinksHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gLinksHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gLinksHouseBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gLinksHouseBg4Tlut, 16, 16); + break; + case SKYBOX_OVERCAST_SUNSET: + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0xC000, "../z_vr_box.c", 1226); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gSunsetOvercastSkybox1Tex, 128, 64, 0x0); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gSunsetOvercastSkybox2Tex, 128, 64, 0x2000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gSunsetOvercastSkybox3Tex, 128, 64, 0x4000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gSunsetOvercastSkybox4Tex, 128, 64, 0x6000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gSunsetOvercastSkybox5Tex, 128, 128, 0x8000); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x100, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gSunsetOvercastSkyboxTlut, 16, 8); + break; + case SKYBOX_MARKET_ADULT: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gMarketRuinsBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gMarketRuins2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gMarketRuins3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gMarketRuins4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gMarketRuinsBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gMarketRuinsBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gMarketRuinsBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gMarketRuinsBg4Tlut, 16, 16); + break; + case SKYBOX_CUTSCENE_MAP: + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000, "../z_vr_box.c", 1226); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gHoly0Skybox1Tex, 128, 64, 0x0); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gHoly0Skybox2Tex, 128, 64, 0x2000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gHoly0Skybox3Tex, 128, 64, 0x4000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gHoly0Skybox4Tex, 128, 64, 0x6000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gHoly0Skybox5Tex, 128, 128, 0x8000); + LoadSkyboxTexAtOffset(skyboxCtx, 0, gHoly0Skybox6Tex, 128, 128, 0xC000); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x100 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gHoly0SkyboxTlut, 16, 8); + + skyboxCtx->staticSegments[1] = GameState_Alloc(&globalCtx->state, 0x10000, "../z_vr_box.c", 1226); + LoadSkyboxTexAtOffset(skyboxCtx, 1, gHoly1Skybox1Tex, 128, 64, 0x0); + LoadSkyboxTexAtOffset(skyboxCtx, 1, gHoly1Skybox2Tex, 128, 64, 0x2000); + LoadSkyboxTexAtOffset(skyboxCtx, 1, gHoly1Skybox3Tex, 128, 64, 0x4000); + LoadSkyboxTexAtOffset(skyboxCtx, 1, gHoly1Skybox4Tex, 128, 64, 0x6000); + LoadSkyboxTexAtOffset(skyboxCtx, 1, gHoly1Skybox5Tex, 128, 128, 0x8000); + LoadSkyboxTexAtOffset(skyboxCtx, 1, gHoly1Skybox6Tex, 128, 128, 0xC000); + + + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gHoly1SkyboxTlut, 16, 8); + break; + case SKYBOX_MARKET_CHILD_DAY: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gMarketDayBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gMarketDay2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gMarketDay3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gMarketDay4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gMarketDayBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gMarketDayBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gMarketDayBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gMarketDayBg4Tlut, 16, 16); + break; + case SKYBOX_MARKET_CHILD_NIGHT: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gMarketNightBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gMarketNight2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gMarketNight3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gMarketNight4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gMarketNightBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gMarketNightBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gMarketNightBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gMarketNightBg4Tlut, 16, 16); + break; + case SKYBOX_HAPPY_MASK_SHOP: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gMaskShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gMaskShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gMaskShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gMaskShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_HOUSE_KNOW_IT_ALL_BROTHERS: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gKnowItAllBrosHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gKnowItAllBrosHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gKnowItAllBrosHouse3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gKnowItAllBrosHouse4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gKnowItAllBrosHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gKnowItAllBrosHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gKnowItAllBrosHouseBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gKnowItAllBrosHouseBg4Tlut, 16, 16); + break; + case SKYBOX_HOUSE_OF_TWINS: + skyboxCtx->unk_140 = 2; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 3, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gHouseOfTwinsBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gHouseOfTwins2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gHouseOfTwins3BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 3, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gHouseOfTwinsBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gHouseOfTwinsBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gHouseOfTwinsBg3Tlut, 16, 16); + break; + case SKYBOX_STABLES: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gStableBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gStable2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gStable3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gStable4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gStableBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gStableBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gStableBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gStableBg4Tlut, 16, 16); + break; + case SKYBOX_HOUSE_KAKARIKO: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gCarpentersHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gCarpentersHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gCarpentersHouse3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gCarpentersHouse4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gCarpentersHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gCarpentersHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gCarpentersHouseBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gCarpentersHouseBg4Tlut, 16, 16); + break; + case SKYBOX_KOKIRI_SHOP: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gKokiriShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gKokiriShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gKokiriShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gKokiriShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_GORON_SHOP: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gGoronShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gGoronShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gGoronShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gGoronShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_ZORA_SHOP: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gZoraShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gZoraShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gZoraShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gZoraShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_POTION_SHOP_KAKARIKO: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gKakPotionShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gKakPotionShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gKakPotionShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gKakPotionShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_POTION_SHOP_MARKET: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gMarketPotionShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gMarketPotionShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gMarketPotionShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gMarketPotionShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_BOMBCHU_SHOP: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 2, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gBombchuShopBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gBombchuShop2BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 2, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gBombchuShopBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gBombchuShopBg2Tlut, 16, 16); + + skyboxCtx->rot.y = 0.8f; + break; + case SKYBOX_HOUSE_RICHARD: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gRichardsHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gRichardsHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gRichardsHouse3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gRichardsHouse4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gRichardsHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gRichardsHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gRichardsHouseBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gRichardsHouseBg4Tlut, 16, 16); + break; + case SKYBOX_HOUSE_IMPA: + skyboxCtx->unk_140 = 1; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 4, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gCowHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gCowHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gCowHouse3BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 3, gCowHouse4BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 4, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gCowHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gCowHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gCowHouseBg3Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 3, gCowHouseBg4Tlut, 16, 16); + break; + case SKYBOX_TENT: + skyboxCtx->unk_140 = 2; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 3, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gCarpentersTentBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gCarpentersTent2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gCarpentersTent3BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 3, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gCarpentersTentBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gCarpentersTentBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gCarpentersTentBg3Tlut, 16, 16); + break; + case SKYBOX_HOUSE_MIDO: + skyboxCtx->unk_140 = 2; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 3, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gMidosHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gMidosHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gMidosHouse3BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 3, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gMidosHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gMidosHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gMidosHouseBg3Tlut, 16, 16); + break; + case SKYBOX_HOUSE_SARIA: + skyboxCtx->unk_140 = 2; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 3, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gSariasHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gSariasHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gSariasHouse3BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 3, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gSariasHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gSariasHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gSariasHouseBg3Tlut, 16, 16); + break; + case SKYBOX_HOUSE_ALLEY: + skyboxCtx->unk_140 = 2; + + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 3, "../z_vr_box.c", 1226); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 0, gBackAlleyHouseBgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 1, gBackAlleyHouse2BgTex, 256, 256, 256, 256); + LoadSkyboxTex(globalCtx, skyboxCtx, 0, 2, gBackAlleyHouse3BgTex, 256, 256, 256, 256); + + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x200 * 3, "../z_vr_box.c", 1231); + LoadSkyboxPalette(globalCtx, skyboxCtx, 0, gBackAlleyHouseBgTlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 1, gBackAlleyHouseBg2Tlut, 16, 16); + LoadSkyboxPalette(globalCtx, skyboxCtx, 2, gBackAlleyHouseBg3Tlut, 16, 16); + break; + default: + skyboxCtx->staticSegments[0] = GameState_Alloc(&globalCtx->state, 0x10000 * 8, "../z_vr_box.c", 1226); + //skyboxCtx->staticSegments[1] = GameState_Alloc(&globalCtx->state, 0x10000 * 8, "../z_vr_box.c", 1226); + skyboxCtx->staticSegments[1] = malloc(0x10000 * 8); // OTRTODO + skyboxCtx->palettes = GameState_Alloc(&globalCtx->state, 0x1000, "../z_vr_box.c", 1226); + break; + } +} + +void Skybox_Init(GameState* state, SkyboxContext* skyboxCtx, s16 skyboxId) { + GlobalContext* globalCtx = (GlobalContext*)state; + + skyboxCtx->unk_140 = 0; + skyboxCtx->rot.x = skyboxCtx->rot.y = skyboxCtx->rot.z = 0.0f; + + Skybox_Setup(globalCtx, skyboxCtx, skyboxId); + osSyncPrintf("\n\n\n********************\n\n\n" + "TYPE=%d" + "\n\n\n********************\n\n\n", + skyboxId); + + if (skyboxId != SKYBOX_NONE) { + osSyncPrintf(VT_FGCOL(GREEN)); + + if (skyboxCtx->unk_140 != 0) { + skyboxCtx->dListBuf = GameState_Alloc(state, 8 * 150 * sizeof(Gfx), "../z_vr_box.c", 1636); + ASSERT(skyboxCtx->dListBuf != NULL, "vr_box->dpList != NULL", "../z_vr_box.c", 1637); + + skyboxCtx->roomVtx = GameState_Alloc(state, 256 * sizeof(Vtx), "../z_vr_box.c", 1639); + ASSERT(skyboxCtx->roomVtx != NULL, "vr_box->roomVtx != NULL", "../z_vr_box.c", 1640); + + func_800AEFC8(skyboxCtx, skyboxId); + } else { + skyboxCtx->dListBuf = GameState_Alloc(state, 12 * 150 * sizeof(Gfx), "../z_vr_box.c", 1643); + ASSERT(skyboxCtx->dListBuf != NULL, "vr_box->dpList != NULL", "../z_vr_box.c", 1644); + + if (skyboxId == SKYBOX_CUTSCENE_MAP) { + skyboxCtx->roomVtx = GameState_Alloc(state, 192 * sizeof(Vtx), "../z_vr_box.c", 1648); + ASSERT(skyboxCtx->roomVtx != NULL, "vr_box->roomVtx != NULL", "../z_vr_box.c", 1649); + + func_800AF178(skyboxCtx, 6); + } else { + skyboxCtx->roomVtx = GameState_Alloc(state, 160 * sizeof(Vtx), "../z_vr_box.c", 1653); + ASSERT(skyboxCtx->roomVtx != NULL, "vr_box->roomVtx != NULL", "../z_vr_box.c", 1654); + + func_800AF178(skyboxCtx, 5); + } + } + osSyncPrintf(VT_RST); + } +} diff --git a/soh/src/code/z_vr_box_draw.c b/soh/src/code/z_vr_box_draw.c new file mode 100644 index 000000000..a5c0610f7 --- /dev/null +++ b/soh/src/code/z_vr_box_draw.c @@ -0,0 +1,100 @@ +#include "global.h" + +Mtx* sSkyboxDrawMatrix; + +Mtx* SkyboxDraw_UpdateMatrix(SkyboxContext* skyboxCtx, f32 x, f32 y, f32 z) { + Matrix_Translate(x, y, z, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(skyboxCtx->rot.x, MTXMODE_APPLY); + Matrix_RotateY(skyboxCtx->rot.y, MTXMODE_APPLY); + Matrix_RotateZ(skyboxCtx->rot.z, MTXMODE_APPLY); + return Matrix_ToMtx(sSkyboxDrawMatrix, "../z_vr_box_draw.c", 42); +} + +void SkyboxDraw_Draw(SkyboxContext* skyboxCtx, GraphicsContext* gfxCtx, s16 skyboxId, s16 blend, f32 x, f32 y, f32 z) { + OPEN_DISPS(gfxCtx, "../z_vr_box_draw.c", 52); + + func_800945A0(gfxCtx); + + //gsSPShaderTest(POLY_OPA_DISP++); + gSPInvalidateTexCache(POLY_OPA_DISP++, 0); + + // OTRTODO: Not working... + + /*for (int i = 0; i < 8; i++) + { + if (skyboxCtx->staticSegments[0] != NULL) + gSPInvalidateTexCache(POLY_OPA_DISP++, (uintptr_t)skyboxCtx->staticSegments[0] + (0x10000 * i)); + + if (skyboxCtx->staticSegments[1] != NULL) + gSPInvalidateTexCache(POLY_OPA_DISP++, (uintptr_t)skyboxCtx->staticSegments[1] + (0x10000 * i)); + }*/ + + gSPSegment(POLY_OPA_DISP++, 0x7, skyboxCtx->staticSegments[0]); + gSPSegment(POLY_OPA_DISP++, 0x8, skyboxCtx->staticSegments[1]); + gSPSegment(POLY_OPA_DISP++, 0x9, skyboxCtx->palettes); + + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 0, 0, 0, blend); + gSPTexture(POLY_OPA_DISP++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON); + + sSkyboxDrawMatrix = Graph_Alloc(gfxCtx, sizeof(Mtx)); + + Matrix_Translate(x, y, z, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(skyboxCtx->rot.x, MTXMODE_APPLY); + Matrix_RotateY(skyboxCtx->rot.y, MTXMODE_APPLY); + Matrix_RotateZ(skyboxCtx->rot.z, MTXMODE_APPLY); + Matrix_ToMtx(sSkyboxDrawMatrix, "../z_vr_box_draw.c", 76); + gSPMatrix(POLY_OPA_DISP++, sSkyboxDrawMatrix, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetColorDither(POLY_OPA_DISP++, G_CD_MAGICSQ); + gDPSetTextureFilter(POLY_OPA_DISP++, G_TF_BILERP); + + gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[0]); + gDPSetTextureLUT(POLY_OPA_DISP++, G_TT_RGBA16); + gDPSetTextureConvert(POLY_OPA_DISP++, G_TC_FILT); + + if (skyboxCtx->unk_140) { + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[0]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[1]); + + gDPPipeSync(POLY_OPA_DISP++); + gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[1]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[2]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[3]); + + if (skyboxId != SKYBOX_BAZAAR) { + if (skyboxId <= SKYBOX_HOUSE_KAKARIKO || skyboxId > SKYBOX_BOMBCHU_SHOP) { + gDPPipeSync(POLY_OPA_DISP++); + gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[2]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[4]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[5]); + + gDPPipeSync(POLY_OPA_DISP++); + if (skyboxCtx->unk_140 != 2) { + gDPLoadTLUT_pal256(POLY_OPA_DISP++, skyboxCtx->palettes[3]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[6]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[7]); + } + } + } + } else { + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[0]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[2]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[4]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[6]); + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[8]); + if (skyboxId == SKYBOX_CUTSCENE_MAP) { + gSPDisplayList(POLY_OPA_DISP++, skyboxCtx->dListBuf[10]); + } + } + + gDPPipeSync(POLY_OPA_DISP++); + //gsSPShaderTest2(POLY_OPA_DISP++); + + + CLOSE_DISPS(gfxCtx, "../z_vr_box_draw.c", 125); +} + +void SkyboxDraw_Update(SkyboxContext* skyboxCtx) { +} diff --git a/soh/src/dmadata/dmadata.c b/soh/src/dmadata/dmadata.c new file mode 100644 index 000000000..c3ac7220e --- /dev/null +++ b/soh/src/dmadata/dmadata.c @@ -0,0 +1,24 @@ +#include "z64dma.h" + +// Linker symbol declarations (used in the table below) +#define DEFINE_DMA_ENTRY(name) \ + extern u8 _##name##SegmentRomStart[]; \ + extern u8 _##name##SegmentRomEnd[]; + +#include "tables/dmadata_table.h" + +#undef DEFINE_DMA_ENTRY + +// dmadata Table definition +#define DEFINE_DMA_ENTRY(name) \ + { (uintptr_t)_##name##SegmentRomStart, (uintptr_t)_##name##SegmentRomEnd, (uintptr_t)_##name##SegmentRomStart, 0 }, + +DmaEntry gDmaDataTable[] = { +#include "tables/dmadata_table.h" + { 0 }, +}; + +#undef DEFINE_DMA_ENTRY + +// Additional padding? +u8 sDmaDataPadding[0xF0] = { 0 }; diff --git a/soh/src/elf_message/elf_message_field.c b/soh/src/elf_message/elf_message_field.c new file mode 100644 index 000000000..fe1e8ee97 --- /dev/null +++ b/soh/src/elf_message/elf_message_field.c @@ -0,0 +1,33 @@ +#include "global.h" +#include "z64elf_message.h" + +ElfMessage gOverworldNaviMsgs[] = { + ELF_MSG_FLAG(CHECK, 0x40, false, 0x05), /* eventChkInf[0] & 0x20 */ + ELF_MSG_FLAG(CHECK, 0x41, false, 0x09), /* eventChkInf[0] & 0x200 */ + ELF_MSG_FLAG(CHECK, 0x42, false, 0x12), /* eventChkInf[1] & 0x4 */ + ELF_MSG_FLAG(CHECK, 0x43, false, 0x14), /* eventChkInf[1] & 0x10 */ + ELF_MSG_FLAG(CHECK, 0x44, false, 0x40), /* eventChkInf[4] & 0x1 */ + ELF_MSG_SONG(CHECK, 0x45, false, ITEM_SONG_SARIA), + ELF_MSG_STRENGTH_UPG(CHECK, 0x46, true, 0), + ELF_MSG_FLAG(CHECK, 0x47, false, 0x25), /* eventChkInf[2] & 0x20 */ + ELF_MSG_MAGIC(CHECK, 0x48, false), + ELF_MSG_FLAG(CHECK, 0x49, false, 0x33), /* eventChkInf[3] & 0x8 */ + ELF_MSG_FLAG(CHECK, 0x4A, false, 0x37), /* eventChkInf[3] & 0x80 */ + ELF_MSG_FLAG(CHECK, 0x4B, false, 0x80), /* eventChkInf[8] & 0x1 */ + ELF_MSG_FLAG(CHECK, 0x4C, false, 0x43), /* eventChkInf[4] & 0x8 */ + ELF_MSG_FLAG(CHECK, 0x4D, false, 0x45), /* eventChkInf[4] & 0x20 */ + ELF_MSG_ITEM(CHECK, 0x4E, true, ITEM_HOOKSHOT, ITEM_NONE), + ELF_MSG_MEDALLION(CHECK, 0x50, false, ITEM_MEDALLION_FOREST), + ELF_MSG_MEDALLION(CHECK, 0x51, false, ITEM_MEDALLION_FIRE), + ELF_MSG_BOOTS(CHECK, 0x52, false, ITEM_BOOTS_IRON), + ELF_MSG_MEDALLION(CHECK, 0x53, false, ITEM_MEDALLION_WATER), + ELF_MSG_FLAG(CHECK, 0x54, false, 0xAA), /* eventChkInf[10] & 0x400 */ + ELF_MSG_ITEM(CHECK, 0x55, true, ITEM_LENS, ITEM_NONE), + ELF_MSG_MEDALLION(CHECK, 0x57, false, ITEM_MEDALLION_SHADOW), + ELF_MSG_SONG(CHECK, 0x58, false, ITEM_SONG_REQUIEM), + ELF_MSG_STRENGTH_UPG(CHECK, 0x56, true, 1), + ELF_MSG_MEDALLION(CHECK, 0x5A, false, ITEM_MEDALLION_SPIRIT), + ELF_MSG_ITEM(CHECK, 0x5B, true, ITEM_ARROW_LIGHT, ITEM_NONE), + ELF_MSG_FLAG(CHECK, 0x5C, false, 0xC3), /* eventChkInf[12] & 0x8 */ + ELF_MSG_END(0x5F), +}; diff --git a/soh/src/elf_message/elf_message_ydan.c b/soh/src/elf_message/elf_message_ydan.c new file mode 100644 index 000000000..54050e372 --- /dev/null +++ b/soh/src/elf_message/elf_message_ydan.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "z64elf_message.h" + +ElfMessage gDungeonNaviMsgs[] = { + ELF_MSG_END(0x5F), +}; diff --git a/soh/src/libultra/gu/cosf.c b/soh/src/libultra/gu/cosf.c new file mode 100644 index 000000000..cae7159ea --- /dev/null +++ b/soh/src/libultra/gu/cosf.c @@ -0,0 +1,59 @@ +#include "ultra64.h" +#include "global.h" + +static const du P[] = { + { 0x3FF00000, 0x00000000 }, { 0xBFC55554, 0xBC83656D }, { 0x3F8110ED, 0x3804C2A0 }, + { 0xBF29F6FF, 0xEEA56814 }, { 0x3EC5DBDF, 0x0E314BFE }, +}; + +static const du rpi = { 0x3FD45F30, 0x6DC9C883 }; + +static const du pihi = { 0x400921FB, 0x50000000 }; + +static const du pilo = { 0x3E6110B4, 0x611A6263 }; + +static const fu zero = { 0x00000000 }; + +f32 cosf(f32 x) { + f32 absx; + f64 dx; + f64 xSq; + f64 polyApprox; + f64 dn; + s32 n; + f64 result; + s32 ix = *(s32*)&x; + s32 xpt = (ix >> 22); + + xpt &= 0x1FF; + + if (xpt < 0x136) { + absx = (x > 0) ? x : -x; + dx = absx; + + dn = dx * rpi.d + 0.5; + n = ROUND(dn); + dn = n; + + dn -= 0.5; + + dx -= dn * pihi.d; + dx -= dn * pilo.d; + + xSq = SQ(dx); + + polyApprox = ((P[4].d * xSq + P[3].d) * xSq + P[2].d) * xSq + P[1].d; + + result = dx + (dx * xSq) * polyApprox; + + if (!(n & 1)) { + return (f32)result; + } + return -(f32)result; + } + if (x != x) { + return __libm_qnan_f; + } + + return zero.f; +} diff --git a/soh/src/libultra/gu/coss.c b/soh/src/libultra/gu/coss.c new file mode 100644 index 000000000..2dca0234b --- /dev/null +++ b/soh/src/libultra/gu/coss.c @@ -0,0 +1,5 @@ +#include "global.h" + +s16 coss(u16 angle) { + return sins(angle + 0x4000); +} diff --git a/soh/src/libultra/gu/guLookAt.c b/soh/src/libultra/gu/guLookAt.c new file mode 100644 index 000000000..58946f4dc --- /dev/null +++ b/soh/src/libultra/gu/guLookAt.c @@ -0,0 +1,66 @@ +#include "global.h" + +void guLookAtF(f32 mf[4][4], f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, f32 yUp, f32 zUp) { + f32 length; + f32 xLook; + f32 yLook; + f32 zLook; + f32 xRight; + f32 yRight; + f32 zRight; + + guMtxIdentF(mf); + + xLook = xAt - xEye; + yLook = yAt - yEye; + zLook = zAt - zEye; + length = -1.0 / sqrtf(SQ(xLook) + SQ(yLook) + SQ(zLook)); + xLook *= length; + //xLook = 2.0f; + yLook *= length; + zLook *= length; + + xRight = yUp * zLook - zUp * yLook; + yRight = zUp * xLook - xUp * zLook; + zRight = xUp * yLook - yUp * xLook; + length = 1.0 / sqrtf(SQ(xRight) + SQ(yRight) + SQ(zRight)); + xRight *= length; + yRight *= length; + zRight *= length; + + xUp = yLook * zRight - zLook * yRight; + yUp = zLook * xRight - xLook * zRight; + zUp = xLook * yRight - yLook * xRight; + length = 1.0 / sqrtf(SQ(xUp) + SQ(yUp) + SQ(zUp)); + xUp *= length; + yUp *= length; + zUp *= length; + + mf[0][0] = xRight; + mf[1][0] = yRight; + mf[2][0] = zRight; + mf[3][0] = -(xEye * xRight + yEye * yRight + zEye * zRight); + + mf[0][1] = xUp; + mf[1][1] = yUp; + mf[2][1] = zUp; + mf[3][1] = -(xEye * xUp + yEye * yUp + zEye * zUp); + + mf[0][2] = xLook; + mf[1][2] = yLook; + mf[2][2] = zLook; + mf[3][2] = -(xEye * xLook + yEye * yLook + zEye * zLook); + + mf[0][3] = 0; + mf[1][3] = 0; + mf[2][3] = 0; + mf[3][3] = 1; +} + +void guLookAt(Mtx* m, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, f32 yUp, f32 zUp) { + f32 mf[4][4]; + + guLookAtF(mf, xEye, yEye, zEye, xAt, yAt, zAt, xUp, yUp, zUp); + + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/guLookAtHilite.c b/soh/src/libultra/gu/guLookAtHilite.c new file mode 100644 index 000000000..ebb915724 --- /dev/null +++ b/soh/src/libultra/gu/guLookAtHilite.c @@ -0,0 +1,161 @@ +#include "global.h" + +#define FTOFRAC8(x) ((s32)MIN(((x) * (128.0f)), 127.0f) & 0xff) + +/** + * guLookAtHiliteF + * This function creates the viewing matrix (floating point) and sets the LookAt/Hilite structures + **/ +void guLookAtHiliteF(f32 mf[4][4], LookAt* l, Hilite* h, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, + f32 xUp, f32 yUp, f32 zUp, f32 xl1, f32 yl1, f32 zl1, /* light 1 direction */ + f32 xl2, f32 yl2, f32 zl2, /* light 2 direction */ + s32 hiliteWidth, s32 hiliteHeight) /* size of hilite texture */ +{ + f32 length; + f32 xLook; + f32 yLook; + f32 zLook; + f32 xRight; + f32 yRight; + f32 zRight; + f32 xHilite; + f32 yHilite; + f32 zHilite; + + guMtxIdentF(mf); + + xLook = xAt - xEye; + yLook = yAt - yEye; + zLook = zAt - zEye; + length = -1.0 / sqrtf(xLook * xLook + yLook * yLook + zLook * zLook); + xLook *= length; + yLook *= length; + zLook *= length; + + xRight = yUp * zLook - zUp * yLook; + yRight = zUp * xLook - xUp * zLook; + zRight = xUp * yLook - yUp * xLook; + length = 1.0 / sqrtf(xRight * xRight + yRight * yRight + zRight * zRight); + xRight *= length; + yRight *= length; + zRight *= length; + + xUp = yLook * zRight - zLook * yRight; + yUp = zLook * xRight - xLook * zRight; + zUp = xLook * yRight - yLook * xRight; + length = 1.0 / sqrtf(xUp * xUp + yUp * yUp + zUp * zUp); + xUp *= length; + yUp *= length; + zUp *= length; + + /* hilite vectors */ + + length = 1.0 / sqrtf(xl1 * xl1 + yl1 * yl1 + zl1 * zl1); + xl1 *= length; + yl1 *= length; + zl1 *= length; + + xHilite = xl1 + xLook; + yHilite = yl1 + yLook; + zHilite = zl1 + zLook; + + length = sqrtf(xHilite * xHilite + yHilite * yHilite + zHilite * zHilite); + + if (length > 0.1) { + length = 1.0 / length; + xHilite *= length; + yHilite *= length; + zHilite *= length; + + h->h.x1 = hiliteWidth * 4 + (xHilite * xRight + yHilite * yRight + zHilite * zRight) * hiliteWidth * 2; + + h->h.y1 = hiliteHeight * 4 + (xHilite * xUp + yHilite * yUp + zHilite * zUp) * hiliteHeight * 2; + } else { + h->h.x1 = hiliteWidth * 2; + h->h.y1 = hiliteHeight * 2; + } + + length = 1.0 / sqrtf(xl2 * xl2 + yl2 * yl2 + zl2 * zl2); + xl2 *= length; + yl2 *= length; + zl2 *= length; + + xHilite = xl2 + xLook; + yHilite = yl2 + yLook; + zHilite = zl2 + zLook; + length = sqrtf(xHilite * xHilite + yHilite * yHilite + zHilite * zHilite); + if (length > 0.1) { + length = 1.0 / length; + xHilite *= length; + yHilite *= length; + zHilite *= length; + + h->h.x2 = hiliteWidth * 4 + (xHilite * xRight + yHilite * yRight + zHilite * zRight) * hiliteWidth * 2; + + h->h.y2 = hiliteHeight * 4 + (xHilite * xUp + yHilite * yUp + zHilite * zUp) * hiliteHeight * 2; + } else { + h->h.x2 = hiliteWidth * 2; + h->h.y2 = hiliteHeight * 2; + } + + /* reflectance vectors = Up and Right */ + + l->l[0].l.dir[0] = FTOFRAC8(xRight); + l->l[0].l.dir[1] = FTOFRAC8(yRight); + l->l[0].l.dir[2] = FTOFRAC8(zRight); + l->l[1].l.dir[0] = FTOFRAC8(xUp); + l->l[1].l.dir[1] = FTOFRAC8(yUp); + l->l[1].l.dir[2] = FTOFRAC8(zUp); + l->l[0].l.col[0] = 0x00; + l->l[0].l.col[1] = 0x00; + l->l[0].l.col[2] = 0x00; + l->l[0].l.pad1 = 0x00; + l->l[0].l.colc[0] = 0x00; + l->l[0].l.colc[1] = 0x00; + l->l[0].l.colc[2] = 0x00; + l->l[0].l.pad2 = 0x00; + l->l[1].l.col[0] = 0x00; + l->l[1].l.col[1] = 0x80; + l->l[1].l.col[2] = 0x00; + l->l[1].l.pad1 = 0x00; + l->l[1].l.colc[0] = 0x00; + l->l[1].l.colc[1] = 0x80; + l->l[1].l.colc[2] = 0x00; + l->l[1].l.pad2 = 0x00; + + mf[0][0] = xRight; + mf[1][0] = yRight; + mf[2][0] = zRight; + mf[3][0] = -(xEye * xRight + yEye * yRight + zEye * zRight); + + mf[0][1] = xUp; + mf[1][1] = yUp; + mf[2][1] = zUp; + mf[3][1] = -(xEye * xUp + yEye * yUp + zEye * zUp); + + mf[0][2] = xLook; + mf[1][2] = yLook; + mf[2][2] = zLook; + mf[3][2] = -(xEye * xLook + yEye * yLook + zEye * zLook); + + mf[0][3] = 0; + mf[1][3] = 0; + mf[2][3] = 0; + mf[3][3] = 1; +} + +/** + * guLookAtHilite + * This function creates the viewing matrix (fixed point) and sets the LookAt/Hilite structures + * Same args as previous function + **/ +void guLookAtHilite(Mtx* m, LookAt* l, Hilite* h, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, + f32 yUp, f32 zUp, f32 xl1, f32 yl1, f32 zl1, f32 xl2, f32 yl2, f32 zl2, s32 hiliteWidth, + s32 hiliteHeight) { + f32 mf[4][4]; + + guLookAtHiliteF(mf, l, h, xEye, yEye, zEye, xAt, yAt, zAt, xUp, yUp, zUp, xl1, yl1, zl1, xl2, yl2, zl2, hiliteWidth, + hiliteHeight); + + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/guPerspectiveF.c b/soh/src/libultra/gu/guPerspectiveF.c new file mode 100644 index 000000000..731cec3e9 --- /dev/null +++ b/soh/src/libultra/gu/guPerspectiveF.c @@ -0,0 +1,42 @@ +#include "global.h" + +void guPerspectiveF(f32 mf[4][4], u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 far, f32 scale) { + f32 yscale; + s32 row; + s32 col; + + guMtxIdentF(mf); + + fovy *= GU_PI / 180.0; + yscale = cosf(fovy / 2) / sinf(fovy / 2); + mf[0][0] = yscale / aspect; + mf[1][1] = yscale; + mf[2][2] = (near + far) / (near - far); + mf[2][3] = -1; + mf[3][2] = 2 * near * far / (near - far); + mf[3][3] = 0.0f; + + for (row = 0; row < 4; row++) { + for (col = 0; col < 4; col++) { + mf[row][col] *= scale; + } + } + + if (perspNorm != NULL) { + if (near + far <= 2.0) { + *perspNorm = 65535; + } else { + *perspNorm = (f64)(1 << 17) / (near + far); + if (*perspNorm <= 0) { + *perspNorm = 1; + } + } + } +} +void guPerspective(Mtx* m, u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 far, f32 scale) { + f32 mf[4][4]; + + guPerspectiveF(mf, perspNorm, fovy, aspect, near, far, scale); + guMtxF2L((MtxF*)mf, m); + +} diff --git a/soh/src/libultra/gu/guPosition.c b/soh/src/libultra/gu/guPosition.c new file mode 100644 index 000000000..2233a72f3 --- /dev/null +++ b/soh/src/libultra/gu/guPosition.c @@ -0,0 +1,54 @@ +#include "global.h" + +/** + * guPositionF + * Creates a rotation/parallel translation modeling matrix (floating point) + */ +void guPositionF(f32 mf[4][4], f32 rot, f32 pitch, f32 yaw, f32 scale, f32 x, f32 y, f32 z) { + static f32 D_80134D00 = M_PI / 180.0; + f32 sinr, sinp, sinh; + f32 cosr, cosp, cosh; + + rot *= D_80134D00; + pitch *= D_80134D00; + yaw *= D_80134D00; + + sinr = sinf(rot); + cosr = cosf(rot); + sinp = sinf(pitch); + cosp = cosf(pitch); + sinh = sinf(yaw); + cosh = cosf(yaw); + + mf[0][0] = (cosp * cosh) * scale; + mf[0][1] = (cosp * sinh) * scale; + mf[0][2] = (-sinp) * scale; + mf[0][3] = 0.0f; + + mf[1][0] = ((sinr * sinp * cosh) - (cosr * sinh)) * scale; + mf[1][1] = ((sinr * sinp * sinh) + (cosr * cosh)) * scale; + mf[1][2] = (sinr * cosp) * scale; + mf[1][3] = 0.0f; + + mf[2][0] = ((cosr * sinp * cosh) + (sinr * sinh)) * scale; + mf[2][1] = ((cosr * sinp * sinh) - (sinr * cosh)) * scale; + mf[2][2] = (cosr * cosp) * scale; + mf[2][3] = 0.0f; + + mf[3][0] = x; + mf[3][1] = y; + mf[3][2] = z; + mf[3][3] = 1.0f; +} + +/** + * guPosition + * Creates a rotational/paralell translation moeling matrix (fixed point) + */ +void guPosition(Mtx* m, f32 rot, f32 pitch, f32 yaw, f32 scale, f32 x, f32 y, f32 z) { + f32 mf[4][4]; + + guPositionF(mf, rot, pitch, yaw, scale, x, y, z); + + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/guS2DInitBg.c b/soh/src/libultra/gu/guS2DInitBg.c new file mode 100644 index 000000000..d2e5a2b04 --- /dev/null +++ b/soh/src/libultra/gu/guS2DInitBg.c @@ -0,0 +1,25 @@ +#include "global.h" + +void guS2DInitBg(uObjBg* bg) { + u32 size; + s32 tmem = (bg->b.imageFmt == G_IM_FMT_CI) ? 0x100 : 0x200; + u16 shift = (6 - bg->b.imageSiz); + + if (bg->b.imageLoad == G_BGLT_LOADBLOCK) { + bg->b.tmemW = bg->b.imageW >> shift; + bg->b.tmemH = (tmem / bg->b.tmemW) * 4; + bg->b.tmemSizeW = bg->b.tmemW * 2; + bg->b.tmemSize = bg->b.tmemH * bg->b.tmemSizeW; + bg->b.tmemLoadSH = (bg->b.tmemSize >> 1) - 1; + bg->b.tmemLoadTH = (0x7FF / bg->b.tmemW) + 1; + } else { // G_BGLT_LOADTILE + bg->b.tmemW = (bg->b.frameW >> shift) + 3; + bg->b.tmemH = (tmem / bg->b.tmemW) * 4; + bg->b.tmemSizeW = (bg->b.imageW >> shift) * 2; + + size = bg->b.tmemH * bg->b.tmemSizeW; + bg->b.tmemSize = (size >> 16); + bg->b.tmemLoadSH = (size >> 0) & 0xFFFF; + bg->b.tmemLoadTH = bg->b.tmemH - 1; + } +} diff --git a/soh/src/libultra/gu/lookat.c b/soh/src/libultra/gu/lookat.c new file mode 100644 index 000000000..5313d3cc3 --- /dev/null +++ b/soh/src/libultra/gu/lookat.c @@ -0,0 +1,65 @@ +#include "global.h" + +void guLookAtF(f32 mf[4][4], f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, f32 yUp, f32 zUp) { + f32 length; + f32 xLook; + f32 yLook; + f32 zLook; + f32 xRight; + f32 yRight; + f32 zRight; + + guMtxIdentF(mf); + + xLook = xAt - xEye; + yLook = yAt - yEye; + zLook = zAt - zEye; + length = -1.0 / sqrtf(SQ(xLook) + SQ(yLook) + SQ(zLook)); + xLook *= length; + yLook *= length; + zLook *= length; + + xRight = yUp * zLook - zUp * yLook; + yRight = zUp * xLook - xUp * zLook; + zRight = xUp * yLook - yUp * xLook; + length = 1.0 / sqrtf(SQ(xRight) + SQ(yRight) + SQ(zRight)); + xRight *= length; + yRight *= length; + zRight *= length; + + xUp = yLook * zRight - zLook * yRight; + yUp = zLook * xRight - xLook * zRight; + zUp = xLook * yRight - yLook * xRight; + length = 1.0 / sqrtf(SQ(xUp) + SQ(yUp) + SQ(zUp)); + xUp *= length; + yUp *= length; + zUp *= length; + + mf[0][0] = xRight; + mf[1][0] = yRight; + mf[2][0] = zRight; + mf[3][0] = -(xEye * xRight + yEye * yRight + zEye * zRight); + + mf[0][1] = xUp; + mf[1][1] = yUp; + mf[2][1] = zUp; + mf[3][1] = -(xEye * xUp + yEye * yUp + zEye * zUp); + + mf[0][2] = xLook; + mf[1][2] = yLook; + mf[2][2] = zLook; + mf[3][2] = -(xEye * xLook + yEye * yLook + zEye * zLook); + + mf[0][3] = 0; + mf[1][3] = 0; + mf[2][3] = 0; + mf[3][3] = 1; +} + +void guLookAt(Mtx* m, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, f32 yUp, f32 zUp) { + f32 mf[4][4]; + + guLookAtF(mf, xEye, yEye, zEye, xAt, yAt, zAt, xUp, yUp, zUp); + + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/lookathil.c b/soh/src/libultra/gu/lookathil.c new file mode 100644 index 000000000..d1070b579 --- /dev/null +++ b/soh/src/libultra/gu/lookathil.c @@ -0,0 +1,161 @@ +#include "global.h" + +#define FTOFRAC8(x) ((s32)MIN(((x) * (128.0f)), 127.0f) & 0xFF) + +/** + * guLookAtHiliteF + * This function creates the viewing matrix (floating point) and sets the LookAt/Hilite structures + **/ +void guLookAtHiliteF(f32 mf[4][4], LookAt* l, Hilite* h, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, + f32 xUp, f32 yUp, f32 zUp, f32 xl1, f32 yl1, f32 zl1, /* light 1 direction */ + f32 xl2, f32 yl2, f32 zl2, /* light 2 direction */ + s32 hiliteWidth, s32 hiliteHeight) /* size of hilite texture */ +{ + f32 length; + f32 xLook; + f32 yLook; + f32 zLook; + f32 xRight; + f32 yRight; + f32 zRight; + f32 xHilite; + f32 yHilite; + f32 zHilite; + + guMtxIdentF(mf); + + xLook = xAt - xEye; + yLook = yAt - yEye; + zLook = zAt - zEye; + length = -1.0 / sqrtf(xLook * xLook + yLook * yLook + zLook * zLook); + xLook *= length; + yLook *= length; + zLook *= length; + + xRight = yUp * zLook - zUp * yLook; + yRight = zUp * xLook - xUp * zLook; + zRight = xUp * yLook - yUp * xLook; + length = 1.0 / sqrtf(xRight * xRight + yRight * yRight + zRight * zRight); + xRight *= length; + yRight *= length; + zRight *= length; + + xUp = yLook * zRight - zLook * yRight; + yUp = zLook * xRight - xLook * zRight; + zUp = xLook * yRight - yLook * xRight; + length = 1.0 / sqrtf(xUp * xUp + yUp * yUp + zUp * zUp); + xUp *= length; + yUp *= length; + zUp *= length; + + /* hilite vectors */ + + length = 1.0 / sqrtf(xl1 * xl1 + yl1 * yl1 + zl1 * zl1); + xl1 *= length; + yl1 *= length; + zl1 *= length; + + xHilite = xl1 + xLook; + yHilite = yl1 + yLook; + zHilite = zl1 + zLook; + + length = sqrtf(xHilite * xHilite + yHilite * yHilite + zHilite * zHilite); + + if (length > 0.1) { + length = 1.0 / length; + xHilite *= length; + yHilite *= length; + zHilite *= length; + + h->h.x1 = hiliteWidth * 4 + (xHilite * xRight + yHilite * yRight + zHilite * zRight) * hiliteWidth * 2; + + h->h.y1 = hiliteHeight * 4 + (xHilite * xUp + yHilite * yUp + zHilite * zUp) * hiliteHeight * 2; + } else { + h->h.x1 = hiliteWidth * 2; + h->h.y1 = hiliteHeight * 2; + } + + length = 1.0 / sqrtf(xl2 * xl2 + yl2 * yl2 + zl2 * zl2); + xl2 *= length; + yl2 *= length; + zl2 *= length; + + xHilite = xl2 + xLook; + yHilite = yl2 + yLook; + zHilite = zl2 + zLook; + length = sqrtf(xHilite * xHilite + yHilite * yHilite + zHilite * zHilite); + if (length > 0.1) { + length = 1.0 / length; + xHilite *= length; + yHilite *= length; + zHilite *= length; + + h->h.x2 = hiliteWidth * 4 + (xHilite * xRight + yHilite * yRight + zHilite * zRight) * hiliteWidth * 2; + + h->h.y2 = hiliteHeight * 4 + (xHilite * xUp + yHilite * yUp + zHilite * zUp) * hiliteHeight * 2; + } else { + h->h.x2 = hiliteWidth * 2; + h->h.y2 = hiliteHeight * 2; + } + + /* reflectance vectors = Up and Right */ + + l->l[0].l.dir[0] = FTOFRAC8(xRight); + l->l[0].l.dir[1] = FTOFRAC8(yRight); + l->l[0].l.dir[2] = FTOFRAC8(zRight); + l->l[1].l.dir[0] = FTOFRAC8(xUp); + l->l[1].l.dir[1] = FTOFRAC8(yUp); + l->l[1].l.dir[2] = FTOFRAC8(zUp); + l->l[0].l.col[0] = 0x00; + l->l[0].l.col[1] = 0x00; + l->l[0].l.col[2] = 0x00; + l->l[0].l.pad1 = 0x00; + l->l[0].l.colc[0] = 0x00; + l->l[0].l.colc[1] = 0x00; + l->l[0].l.colc[2] = 0x00; + l->l[0].l.pad2 = 0x00; + l->l[1].l.col[0] = 0x00; + l->l[1].l.col[1] = 0x80; + l->l[1].l.col[2] = 0x00; + l->l[1].l.pad1 = 0x00; + l->l[1].l.colc[0] = 0x00; + l->l[1].l.colc[1] = 0x80; + l->l[1].l.colc[2] = 0x00; + l->l[1].l.pad2 = 0x00; + + mf[0][0] = xRight; + mf[1][0] = yRight; + mf[2][0] = zRight; + mf[3][0] = -(xEye * xRight + yEye * yRight + zEye * zRight); + + mf[0][1] = xUp; + mf[1][1] = yUp; + mf[2][1] = zUp; + mf[3][1] = -(xEye * xUp + yEye * yUp + zEye * zUp); + + mf[0][2] = xLook; + mf[1][2] = yLook; + mf[2][2] = zLook; + mf[3][2] = -(xEye * xLook + yEye * yLook + zEye * zLook); + + mf[0][3] = 0; + mf[1][3] = 0; + mf[2][3] = 0; + mf[3][3] = 1; +} + +/** + * guLookAtHilite + * This function creates the viewing matrix (fixed point) and sets the LookAt/Hilite structures + * Same args as previous function + **/ +void guLookAtHilite(Mtx* m, LookAt* l, Hilite* h, f32 xEye, f32 yEye, f32 zEye, f32 xAt, f32 yAt, f32 zAt, f32 xUp, + f32 yUp, f32 zUp, f32 xl1, f32 yl1, f32 zl1, f32 xl2, f32 yl2, f32 zl2, s32 hiliteWidth, + s32 hiliteHeight) { + f32 mf[4][4]; + + guLookAtHiliteF(mf, l, h, xEye, yEye, zEye, xAt, yAt, zAt, xUp, yUp, zUp, xl1, yl1, zl1, xl2, yl2, zl2, hiliteWidth, + hiliteHeight); + + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/ortho.c b/soh/src/libultra/gu/ortho.c new file mode 100644 index 000000000..517ba1dcc --- /dev/null +++ b/soh/src/libultra/gu/ortho.c @@ -0,0 +1,29 @@ +#include "global.h" + +void guOrthoF(f32 mf[4][4], f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far, f32 scale) { + s32 i, j; + + guMtxIdentF(mf); + + mf[0][0] = 2 / (right - left); + mf[1][1] = 2 / (top - bottom); + mf[2][2] = -2 / (far - near); + mf[3][0] = -(right + left) / (right - left); + mf[3][1] = -(top + bottom) / (top - bottom); + mf[3][2] = -(far + near) / (far - near); + mf[3][3] = 1; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + mf[i][j] *= scale; + } + } +} + +void guOrtho(Mtx* mtx, f32 left, f32 right, f32 bottom, f32 top, f32 near, f32 far, f32 scale) { + f32 mf[4][4]; + + guOrthoF(mf, left, right, bottom, top, near, far, scale); + + guMtxF2L((MtxF*)mf, mtx); +} diff --git a/soh/src/libultra/gu/perspective.c b/soh/src/libultra/gu/perspective.c new file mode 100644 index 000000000..4418a6122 --- /dev/null +++ b/soh/src/libultra/gu/perspective.c @@ -0,0 +1,41 @@ +#include "global.h" + +void guPerspectiveF(f32 mf[4][4], u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 far, f32 scale) { + f32 yscale; + s32 row; + s32 col; + + guMtxIdentF(mf); + + fovy *= GU_PI / 180.0; + yscale = cosf(fovy / 2) / sinf(fovy / 2); + mf[0][0] = yscale / aspect; + mf[1][1] = yscale; + mf[2][2] = (near + far) / (near - far); + mf[2][3] = -1; + mf[3][2] = 2 * near * far / (near - far); + mf[3][3] = 0.0f; + + for (row = 0; row < 4; row++) { + for (col = 0; col < 4; col++) { + mf[row][col] *= scale; + } + } + + if (perspNorm != NULL) { + if (near + far <= 2.0) { + *perspNorm = 65535; + } else { + *perspNorm = (f64)(1 << 17) / (near + far); + if (*perspNorm <= 0) { + *perspNorm = 1; + } + } + } +} +void guPerspective(Mtx* m, u16* perspNorm, f32 fovy, f32 aspect, f32 near, f32 far, f32 scale) { + f32 mf[4][4]; + + guPerspectiveF(mf, perspNorm, fovy, aspect, near, far, scale); + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/position.c b/soh/src/libultra/gu/position.c new file mode 100644 index 000000000..65517139d --- /dev/null +++ b/soh/src/libultra/gu/position.c @@ -0,0 +1,54 @@ +#include "global.h" + +/** + * guPositionF + * Creates a rotation/parallel translation modeling matrix (floating point) + */ +void guPositionF(f32 mf[4][4], f32 rot, f32 pitch, f32 yaw, f32 scale, f32 x, f32 y, f32 z) { + static f32 D_80134D00 = M_PI / 180.0; + f32 sinr, sinp, sinh; + f32 cosr, cosp, cosh; + + rot *= D_80134D00; + pitch *= D_80134D00; + yaw *= D_80134D00; + + sinr = sinf(rot); + cosr = cosf(rot); + sinp = sinf(pitch); + cosp = cosf(pitch); + sinh = sinf(yaw); + cosh = cosf(yaw); + + mf[0][0] = (cosp * cosh) * scale; + mf[0][1] = (cosp * sinh) * scale; + mf[0][2] = (-sinp) * scale; + mf[0][3] = 0.0f; + + mf[1][0] = ((sinr * sinp * cosh) - (cosr * sinh)) * scale; + mf[1][1] = ((sinr * sinp * sinh) + (cosr * cosh)) * scale; + mf[1][2] = (sinr * cosp) * scale; + mf[1][3] = 0.0f; + + mf[2][0] = ((cosr * sinp * cosh) + (sinr * sinh)) * scale; + mf[2][1] = ((cosr * sinp * sinh) - (sinr * cosh)) * scale; + mf[2][2] = (cosr * cosp) * scale; + mf[2][3] = 0.0f; + + mf[3][0] = x; + mf[3][1] = y; + mf[3][2] = z; + mf[3][3] = 1.0f; +} + +/** + * guPosition + * Creates a rotational/parallel translation modeling matrix (fixed point) + */ +void guPosition(Mtx* m, f32 rot, f32 pitch, f32 yaw, f32 scale, f32 x, f32 y, f32 z) { + f32 mf[4][4]; + + guPositionF(mf, rot, pitch, yaw, scale, x, y, z); + + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/rotate.c b/soh/src/libultra/gu/rotate.c new file mode 100644 index 000000000..64d4bd6ac --- /dev/null +++ b/soh/src/libultra/gu/rotate.c @@ -0,0 +1,51 @@ +#include "global.h" + +void guRotateF(f32 m[4][4], f32 a, f32 x, f32 y, f32 z) { + static f32 D_80134D10 = M_PI / 180.0f; + f32 sine; + f32 cosine; + f32 ab; + f32 bc; + f32 ca; + f32 t; + f32 xs; + f32 ys; + f32 zs; + + guNormalize(&x, &y, &z); + + a = a * D_80134D10; + + sine = sinf(a); + cosine = cosf(a); + + ab = x * y * (1 - cosine); + bc = y * z * (1 - cosine); + ca = z * x * (1 - cosine); + + guMtxIdentF(m); + + xs = x * sine; + ys = y * sine; + zs = z * sine; + + t = x * x; + m[0][0] = (1 - t) * cosine + t; + m[2][1] = bc - xs; + m[1][2] = bc + xs; + t = y * y; + m[1][1] = (1 - t) * cosine + t; + m[2][0] = ca + ys; + m[0][2] = ca - ys; + t = z * z; + m[2][2] = (1 - t) * cosine + t; + m[1][0] = ab - zs; + m[0][1] = ab + zs; +} + +void guRotate(Mtx* m, f32 a, f32 x, f32 y, f32 z) { + f32 mf[4][4]; + + guRotateF(mf, a, x, y, z); + guMtxF2L((MtxF*)mf, m); +} diff --git a/soh/src/libultra/gu/sinf.c b/soh/src/libultra/gu/sinf.c new file mode 100644 index 000000000..bb7f58e72 --- /dev/null +++ b/soh/src/libultra/gu/sinf.c @@ -0,0 +1,64 @@ +#include "global.h" +#include "ultra64.h" + +static const du P[] = { + { 0x3FF00000, 0x00000000 }, { 0xBFC55554, 0xBC83656D }, { 0x3F8110ED, 0x3804C2A0 }, + { 0xBF29F6FF, 0xEEA56814 }, { 0x3EC5DBDF, 0x0E314BFE }, +}; + +static const du rpi = { 0x3FD45F30, 0x6DC9C883 }; + +static const du pihi = { 0x400921FB, 0x50000000 }; + +static const du pilo = { 0x3E6110B4, 0x611A6263 }; + +static const fu zero = { 0x00000000 }; + +f32 sinf(f32 x) { + f64 dx; + f64 xSq; + f64 polyApprox; + f64 dn; + s32 n; + f64 result; + s32 ix = *(s32*)&x; + s32 xpt = (ix >> 22); + + xpt &= 0x1FF; + + if (xpt < 0xFF) { + dx = x; + + if (xpt >= 0xE6) { + xSq = SQ(dx); + polyApprox = ((P[4].d * xSq + P[3].d) * xSq + P[2].d) * xSq + P[1].d; + result = dx + (dx * xSq) * polyApprox; + return (f32)result; + } + return x; + } + + if (xpt < 0x136) { + dx = x; + dn = dx * rpi.d; + n = ROUND(dn); + dn = n; + + dx -= dn * pihi.d; + dx -= dn * pilo.d; + xSq = SQ(dx); + + polyApprox = ((P[4].d * xSq + P[3].d) * xSq + P[2].d) * xSq + P[1].d; + result = dx + (dx * xSq) * polyApprox; + + if (!(n & 1)) { + return (f32)result; + } + return -(f32)result; + } + + if (x != x) { + return __libm_qnan_f; + } + return zero.f; +} diff --git a/soh/src/libultra/gu/sins.c b/soh/src/libultra/gu/sins.c new file mode 100644 index 000000000..4b538f67d --- /dev/null +++ b/soh/src/libultra/gu/sins.c @@ -0,0 +1,20 @@ +#include "ultra64.h" +#include "sintable.c" + +s16 sins(u16 x) { + s16 value; + + x >>= 4; + + if (x & 0x400) { + value = sintable[0x3FF - (x & 0x3FF)]; + } else { + value = sintable[x & 0x3FF]; + } + + if (x & 0x800) { + return -value; + } else { + return value; + } +} diff --git a/soh/src/libultra/gu/sintable.c b/soh/src/libultra/gu/sintable.c new file mode 100644 index 000000000..d430d65ae --- /dev/null +++ b/soh/src/libultra/gu/sintable.c @@ -0,0 +1,78 @@ +#include "ultra64/types.h" + +static s16 sintable[0x400] = { + 0x0000, 0x0032, 0x0064, 0x0096, 0x00C9, 0x00FB, 0x012D, 0x0160, 0x0192, 0x01C4, 0x01F7, 0x0229, 0x025B, 0x028E, + 0x02C0, 0x02F2, 0x0324, 0x0357, 0x0389, 0x03BB, 0x03EE, 0x0420, 0x0452, 0x0484, 0x04B7, 0x04E9, 0x051B, 0x054E, + 0x0580, 0x05B2, 0x05E4, 0x0617, 0x0649, 0x067B, 0x06AD, 0x06E0, 0x0712, 0x0744, 0x0776, 0x07A9, 0x07DB, 0x080D, + 0x083F, 0x0871, 0x08A4, 0x08D6, 0x0908, 0x093A, 0x096C, 0x099F, 0x09D1, 0x0A03, 0x0A35, 0x0A67, 0x0A99, 0x0ACB, + 0x0AFE, 0x0B30, 0x0B62, 0x0B94, 0x0BC6, 0x0BF8, 0x0C2A, 0x0C5C, 0x0C8E, 0x0CC0, 0x0CF2, 0x0D25, 0x0D57, 0x0D89, + 0x0DBB, 0x0DED, 0x0E1F, 0x0E51, 0x0E83, 0x0EB5, 0x0EE7, 0x0F19, 0x0F4B, 0x0F7C, 0x0FAE, 0x0FE0, 0x1012, 0x1044, + 0x1076, 0x10A8, 0x10DA, 0x110C, 0x113E, 0x116F, 0x11A1, 0x11D3, 0x1205, 0x1237, 0x1269, 0x129A, 0x12CC, 0x12FE, + 0x1330, 0x1361, 0x1393, 0x13C5, 0x13F6, 0x1428, 0x145A, 0x148C, 0x14BD, 0x14EF, 0x1520, 0x1552, 0x1584, 0x15B5, + 0x15E7, 0x1618, 0x164A, 0x167B, 0x16AD, 0x16DF, 0x1710, 0x1741, 0x1773, 0x17A4, 0x17D6, 0x1807, 0x1839, 0x186A, + 0x189B, 0x18CD, 0x18FE, 0x1930, 0x1961, 0x1992, 0x19C3, 0x19F5, 0x1A26, 0x1A57, 0x1A88, 0x1ABA, 0x1AEB, 0x1B1C, + 0x1B4D, 0x1B7E, 0x1BAF, 0x1BE1, 0x1C12, 0x1C43, 0x1C74, 0x1CA5, 0x1CD6, 0x1D07, 0x1D38, 0x1D69, 0x1D9A, 0x1DCB, + 0x1DFC, 0x1E2D, 0x1E5D, 0x1E8E, 0x1EBF, 0x1EF0, 0x1F21, 0x1F52, 0x1F82, 0x1FB3, 0x1FE4, 0x2015, 0x2045, 0x2076, + 0x20A7, 0x20D7, 0x2108, 0x2139, 0x2169, 0x219A, 0x21CA, 0x21FB, 0x222B, 0x225C, 0x228C, 0x22BD, 0x22ED, 0x231D, + 0x234E, 0x237E, 0x23AE, 0x23DF, 0x240F, 0x243F, 0x2470, 0x24A0, 0x24D0, 0x2500, 0x2530, 0x2560, 0x2591, 0x25C1, + 0x25F1, 0x2621, 0x2651, 0x2681, 0x26B1, 0x26E1, 0x2711, 0x2740, 0x2770, 0x27A0, 0x27D0, 0x2800, 0x2830, 0x285F, + 0x288F, 0x28BF, 0x28EE, 0x291E, 0x294E, 0x297D, 0x29AD, 0x29DD, 0x2A0C, 0x2A3C, 0x2A6B, 0x2A9B, 0x2ACA, 0x2AF9, + 0x2B29, 0x2B58, 0x2B87, 0x2BB7, 0x2BE6, 0x2C15, 0x2C44, 0x2C74, 0x2CA3, 0x2CD2, 0x2D01, 0x2D30, 0x2D5F, 0x2D8E, + 0x2DBD, 0x2DEC, 0x2E1B, 0x2E4A, 0x2E79, 0x2EA8, 0x2ED7, 0x2F06, 0x2F34, 0x2F63, 0x2F92, 0x2FC0, 0x2FEF, 0x301E, + 0x304C, 0x307B, 0x30A9, 0x30D8, 0x3107, 0x3135, 0x3163, 0x3192, 0x31C0, 0x31EF, 0x321D, 0x324B, 0x3279, 0x32A8, + 0x32D6, 0x3304, 0x3332, 0x3360, 0x338E, 0x33BC, 0x33EA, 0x3418, 0x3446, 0x3474, 0x34A2, 0x34D0, 0x34FE, 0x352B, + 0x3559, 0x3587, 0x35B5, 0x35E2, 0x3610, 0x363D, 0x366B, 0x3698, 0x36C6, 0x36F3, 0x3721, 0x374E, 0x377C, 0x37A9, + 0x37D6, 0x3803, 0x3831, 0x385E, 0x388B, 0x38B8, 0x38E5, 0x3912, 0x393F, 0x396C, 0x3999, 0x39C6, 0x39F3, 0x3A20, + 0x3A4D, 0x3A79, 0x3AA6, 0x3AD3, 0x3B00, 0x3B2C, 0x3B59, 0x3B85, 0x3BB2, 0x3BDE, 0x3C0B, 0x3C37, 0x3C64, 0x3C90, + 0x3CBC, 0x3CE9, 0x3D15, 0x3D41, 0x3D6D, 0x3D99, 0x3DC5, 0x3DF1, 0x3E1D, 0x3E49, 0x3E75, 0x3EA1, 0x3ECD, 0x3EF9, + 0x3F25, 0x3F50, 0x3F7C, 0x3FA8, 0x3FD3, 0x3FFF, 0x402B, 0x4056, 0x4082, 0x40AD, 0x40D8, 0x4104, 0x412F, 0x415A, + 0x4186, 0x41B1, 0x41DC, 0x4207, 0x4232, 0x425D, 0x4288, 0x42B3, 0x42DE, 0x4309, 0x4334, 0x435F, 0x4389, 0x43B4, + 0x43DF, 0x4409, 0x4434, 0x445F, 0x4489, 0x44B4, 0x44DE, 0x4508, 0x4533, 0x455D, 0x4587, 0x45B1, 0x45DC, 0x4606, + 0x4630, 0x465A, 0x4684, 0x46AE, 0x46D8, 0x4702, 0x472C, 0x4755, 0x477F, 0x47A9, 0x47D2, 0x47FC, 0x4826, 0x484F, + 0x4879, 0x48A2, 0x48CC, 0x48F5, 0x491E, 0x4948, 0x4971, 0x499A, 0x49C3, 0x49EC, 0x4A15, 0x4A3E, 0x4A67, 0x4A90, + 0x4AB9, 0x4AE2, 0x4B0B, 0x4B33, 0x4B5C, 0x4B85, 0x4BAD, 0x4BD6, 0x4BFE, 0x4C27, 0x4C4F, 0x4C78, 0x4CA0, 0x4CC8, + 0x4CF0, 0x4D19, 0x4D41, 0x4D69, 0x4D91, 0x4DB9, 0x4DE1, 0x4E09, 0x4E31, 0x4E58, 0x4E80, 0x4EA8, 0x4ED0, 0x4EF7, + 0x4F1F, 0x4F46, 0x4F6E, 0x4F95, 0x4FBD, 0x4FE4, 0x500B, 0x5032, 0x505A, 0x5081, 0x50A8, 0x50CF, 0x50F6, 0x511D, + 0x5144, 0x516B, 0x5191, 0x51B8, 0x51DF, 0x5205, 0x522C, 0x5253, 0x5279, 0x52A0, 0x52C6, 0x52EC, 0x5313, 0x5339, + 0x535F, 0x5385, 0x53AB, 0x53D1, 0x53F7, 0x541D, 0x5443, 0x5469, 0x548F, 0x54B5, 0x54DA, 0x5500, 0x5525, 0x554B, + 0x5571, 0x5596, 0x55BB, 0x55E1, 0x5606, 0x562B, 0x5650, 0x5675, 0x569B, 0x56C0, 0x56E5, 0x5709, 0x572E, 0x5753, + 0x5778, 0x579D, 0x57C1, 0x57E6, 0x580A, 0x582F, 0x5853, 0x5878, 0x589C, 0x58C0, 0x58E5, 0x5909, 0x592D, 0x5951, + 0x5975, 0x5999, 0x59BD, 0x59E1, 0x5A04, 0x5A28, 0x5A4C, 0x5A6F, 0x5A93, 0x5AB7, 0x5ADA, 0x5AFD, 0x5B21, 0x5B44, + 0x5B67, 0x5B8B, 0x5BAE, 0x5BD1, 0x5BF4, 0x5C17, 0x5C3A, 0x5C5D, 0x5C7F, 0x5CA2, 0x5CC5, 0x5CE7, 0x5D0A, 0x5D2D, + 0x5D4F, 0x5D71, 0x5D94, 0x5DB6, 0x5DD8, 0x5DFA, 0x5E1D, 0x5E3F, 0x5E61, 0x5E83, 0x5EA5, 0x5EC6, 0x5EE8, 0x5F0A, + 0x5F2C, 0x5F4D, 0x5F6F, 0x5F90, 0x5FB2, 0x5FD3, 0x5FF4, 0x6016, 0x6037, 0x6058, 0x6079, 0x609A, 0x60BB, 0x60DC, + 0x60FD, 0x611E, 0x613E, 0x615F, 0x6180, 0x61A0, 0x61C1, 0x61E1, 0x6202, 0x6222, 0x6242, 0x6263, 0x6283, 0x62A3, + 0x62C3, 0x62E3, 0x6303, 0x6323, 0x6342, 0x6362, 0x6382, 0x63A1, 0x63C1, 0x63E0, 0x6400, 0x641F, 0x643F, 0x645E, + 0x647D, 0x649C, 0x64BB, 0x64DA, 0x64F9, 0x6518, 0x6537, 0x6556, 0x6574, 0x6593, 0x65B2, 0x65D0, 0x65EF, 0x660D, + 0x662B, 0x664A, 0x6668, 0x6686, 0x66A4, 0x66C2, 0x66E0, 0x66FE, 0x671C, 0x673A, 0x6757, 0x6775, 0x6792, 0x67B0, + 0x67CD, 0x67EB, 0x6808, 0x6825, 0x6843, 0x6860, 0x687D, 0x689A, 0x68B7, 0x68D4, 0x68F1, 0x690D, 0x692A, 0x6947, + 0x6963, 0x6980, 0x699C, 0x69B9, 0x69D5, 0x69F1, 0x6A0E, 0x6A2A, 0x6A46, 0x6A62, 0x6A7E, 0x6A9A, 0x6AB5, 0x6AD1, + 0x6AED, 0x6B08, 0x6B24, 0x6B40, 0x6B5B, 0x6B76, 0x6B92, 0x6BAD, 0x6BC8, 0x6BE3, 0x6BFE, 0x6C19, 0x6C34, 0x6C4F, + 0x6C6A, 0x6C84, 0x6C9F, 0x6CBA, 0x6CD4, 0x6CEF, 0x6D09, 0x6D23, 0x6D3E, 0x6D58, 0x6D72, 0x6D8C, 0x6DA6, 0x6DC0, + 0x6DDA, 0x6DF3, 0x6E0D, 0x6E27, 0x6E40, 0x6E5A, 0x6E73, 0x6E8D, 0x6EA6, 0x6EBF, 0x6ED9, 0x6EF2, 0x6F0B, 0x6F24, + 0x6F3D, 0x6F55, 0x6F6E, 0x6F87, 0x6FA0, 0x6FB8, 0x6FD1, 0x6FE9, 0x7002, 0x701A, 0x7032, 0x704A, 0x7062, 0x707A, + 0x7092, 0x70AA, 0x70C2, 0x70DA, 0x70F2, 0x7109, 0x7121, 0x7138, 0x7150, 0x7167, 0x717E, 0x7196, 0x71AD, 0x71C4, + 0x71DB, 0x71F2, 0x7209, 0x7220, 0x7236, 0x724D, 0x7264, 0x727A, 0x7291, 0x72A7, 0x72BD, 0x72D4, 0x72EA, 0x7300, + 0x7316, 0x732C, 0x7342, 0x7358, 0x736E, 0x7383, 0x7399, 0x73AE, 0x73C4, 0x73D9, 0x73EF, 0x7404, 0x7419, 0x742E, + 0x7443, 0x7458, 0x746D, 0x7482, 0x7497, 0x74AC, 0x74C0, 0x74D5, 0x74EA, 0x74FE, 0x7512, 0x7527, 0x753B, 0x754F, + 0x7563, 0x7577, 0x758B, 0x759F, 0x75B3, 0x75C7, 0x75DA, 0x75EE, 0x7601, 0x7615, 0x7628, 0x763B, 0x764F, 0x7662, + 0x7675, 0x7688, 0x769B, 0x76AE, 0x76C1, 0x76D3, 0x76E6, 0x76F9, 0x770B, 0x771E, 0x7730, 0x7742, 0x7754, 0x7767, + 0x7779, 0x778B, 0x779D, 0x77AF, 0x77C0, 0x77D2, 0x77E4, 0x77F5, 0x7807, 0x7818, 0x782A, 0x783B, 0x784C, 0x785D, + 0x786E, 0x787F, 0x7890, 0x78A1, 0x78B2, 0x78C3, 0x78D3, 0x78E4, 0x78F4, 0x7905, 0x7915, 0x7925, 0x7936, 0x7946, + 0x7956, 0x7966, 0x7976, 0x7985, 0x7995, 0x79A5, 0x79B5, 0x79C4, 0x79D4, 0x79E3, 0x79F2, 0x7A02, 0x7A11, 0x7A20, + 0x7A2F, 0x7A3E, 0x7A4D, 0x7A5B, 0x7A6A, 0x7A79, 0x7A87, 0x7A96, 0x7AA4, 0x7AB3, 0x7AC1, 0x7ACF, 0x7ADD, 0x7AEB, + 0x7AF9, 0x7B07, 0x7B15, 0x7B23, 0x7B31, 0x7B3E, 0x7B4C, 0x7B59, 0x7B67, 0x7B74, 0x7B81, 0x7B8E, 0x7B9B, 0x7BA8, + 0x7BB5, 0x7BC2, 0x7BCF, 0x7BDC, 0x7BE8, 0x7BF5, 0x7C02, 0x7C0E, 0x7C1A, 0x7C27, 0x7C33, 0x7C3F, 0x7C4B, 0x7C57, + 0x7C63, 0x7C6F, 0x7C7A, 0x7C86, 0x7C92, 0x7C9D, 0x7CA9, 0x7CB4, 0x7CBF, 0x7CCB, 0x7CD6, 0x7CE1, 0x7CEC, 0x7CF7, + 0x7D02, 0x7D0C, 0x7D17, 0x7D22, 0x7D2C, 0x7D37, 0x7D41, 0x7D4B, 0x7D56, 0x7D60, 0x7D6A, 0x7D74, 0x7D7E, 0x7D88, + 0x7D91, 0x7D9B, 0x7DA5, 0x7DAE, 0x7DB8, 0x7DC1, 0x7DCB, 0x7DD4, 0x7DDD, 0x7DE6, 0x7DEF, 0x7DF8, 0x7E01, 0x7E0A, + 0x7E13, 0x7E1B, 0x7E24, 0x7E2C, 0x7E35, 0x7E3D, 0x7E45, 0x7E4D, 0x7E56, 0x7E5E, 0x7E66, 0x7E6D, 0x7E75, 0x7E7D, + 0x7E85, 0x7E8C, 0x7E94, 0x7E9B, 0x7EA3, 0x7EAA, 0x7EB1, 0x7EB8, 0x7EBF, 0x7EC6, 0x7ECD, 0x7ED4, 0x7EDB, 0x7EE1, + 0x7EE8, 0x7EEE, 0x7EF5, 0x7EFB, 0x7F01, 0x7F08, 0x7F0E, 0x7F14, 0x7F1A, 0x7F20, 0x7F25, 0x7F2B, 0x7F31, 0x7F36, + 0x7F3C, 0x7F41, 0x7F47, 0x7F4C, 0x7F51, 0x7F56, 0x7F5B, 0x7F60, 0x7F65, 0x7F6A, 0x7F6F, 0x7F74, 0x7F78, 0x7F7D, + 0x7F81, 0x7F85, 0x7F8A, 0x7F8E, 0x7F92, 0x7F96, 0x7F9A, 0x7F9E, 0x7FA2, 0x7FA6, 0x7FA9, 0x7FAD, 0x7FB0, 0x7FB4, + 0x7FB7, 0x7FBA, 0x7FBE, 0x7FC1, 0x7FC4, 0x7FC7, 0x7FCA, 0x7FCC, 0x7FCF, 0x7FD2, 0x7FD4, 0x7FD7, 0x7FD9, 0x7FDC, + 0x7FDE, 0x7FE0, 0x7FE2, 0x7FE4, 0x7FE6, 0x7FE8, 0x7FEA, 0x7FEC, 0x7FED, 0x7FEF, 0x7FF1, 0x7FF2, 0x7FF3, 0x7FF5, + 0x7FF6, 0x7FF7, 0x7FF8, 0x7FF9, 0x7FFA, 0x7FFB, 0x7FFB, 0x7FFC, 0x7FFD, 0x7FFD, 0x7FFE, 0x7FFE, 0x7FFE, 0x7FFE, + 0x7FFE, 0x7FFF, +}; diff --git a/soh/src/libultra/gu/sqrtf.c b/soh/src/libultra/gu/sqrtf.c new file mode 100644 index 000000000..5e5fa233c --- /dev/null +++ b/soh/src/libultra/gu/sqrtf.c @@ -0,0 +1,9 @@ +#include "global.h" + +#ifndef __GNUC__ +#define __builtin_sqrtf sqrtf +#endif + +f32 sqrtf(f32 f) { + return __builtin_sqrtf(f); +} diff --git a/soh/src/libultra/gu/us2dex.c b/soh/src/libultra/gu/us2dex.c new file mode 100644 index 000000000..a6a6e6374 --- /dev/null +++ b/soh/src/libultra/gu/us2dex.c @@ -0,0 +1,25 @@ +#include "global.h" + +void guS2DInitBg(uObjBg* bg) { + size_t size; + s32 tmem = (bg->b.imageFmt == G_IM_FMT_CI) ? 0x100 : 0x200; + u16 shift = (6 - bg->b.imageSiz); + + if (bg->b.imageLoad == G_BGLT_LOADBLOCK) { + bg->b.tmemW = bg->b.imageW >> shift; + bg->b.tmemH = (tmem / bg->b.tmemW) * 4; + bg->b.tmemSizeW = bg->b.tmemW * 2; + bg->b.tmemSize = bg->b.tmemH * bg->b.tmemSizeW; + bg->b.tmemLoadSH = (bg->b.tmemSize >> 1) - 1; + bg->b.tmemLoadTH = (0x7FF / bg->b.tmemW) + 1; + } else { // G_BGLT_LOADTILE + bg->b.tmemW = (bg->b.frameW >> shift) + 3; + bg->b.tmemH = (tmem / bg->b.tmemW) * 4; + bg->b.tmemSizeW = (bg->b.imageW >> shift) * 2; + + size = bg->b.tmemH * bg->b.tmemSizeW; + bg->b.tmemSize = (size >> 16); + bg->b.tmemLoadSH = (size >> 0) & 0xFFFF; + bg->b.tmemLoadTH = bg->b.tmemH - 1; + } +} diff --git a/soh/src/libultra/io/aigetlen.c b/soh/src/libultra/io/aigetlen.c new file mode 100644 index 000000000..58d8db487 --- /dev/null +++ b/soh/src/libultra/io/aigetlen.c @@ -0,0 +1,5 @@ +#include "global.h" + +u32 osAiGetLength(void) { + return HW_REG(AI_LEN_REG, u32); +} diff --git a/soh/src/libultra/io/aisetfreq.c b/soh/src/libultra/io/aisetfreq.c new file mode 100644 index 000000000..8702941b9 --- /dev/null +++ b/soh/src/libultra/io/aisetfreq.c @@ -0,0 +1,20 @@ +#include "global.h" + +s32 osAiSetFrequency(u32 frequency) { + u8 bitrate; + f32 dacRateF = ((f32)osViClock / frequency) + 0.5f; + u32 dacRate = dacRateF; + + if (dacRate < 132) { + return -1; + } + + bitrate = (dacRate / 66); + if (bitrate > 16) { + bitrate = 16; + } + + HW_REG(AI_DACRATE_REG, u32) = dacRate - 1; + HW_REG(AI_BITRATE_REG, u32) = bitrate - 1; + return osViClock / (s32)dacRate; +} diff --git a/soh/src/libultra/io/aisetnextbuf.c b/soh/src/libultra/io/aisetnextbuf.c new file mode 100644 index 000000000..bc63b936c --- /dev/null +++ b/soh/src/libultra/io/aisetnextbuf.c @@ -0,0 +1,31 @@ +#include "global.h" + +//! Note that this is not the same as the original libultra +//! osAiSetNextBuffer, see comments in the function + +s32 osAiSetNextBuffer(void* buf, size_t size) { + static u8 D_80130500 = false; + uintptr_t bufAdjusted = (uintptr_t)buf; + s32 status; + + if (D_80130500) { + bufAdjusted = (uintptr_t)buf - 0x2000; + } + if ((((uintptr_t)buf + size) & 0x1FFF) == 0) { + D_80130500 = true; + } else { + D_80130500 = false; + } + + // Originally a call to __osAiDeviceBusy + status = HW_REG(AI_STATUS_REG, s32); + if (status & AI_STATUS_AI_FULL) { + return -1; + } + + // OS_K0_TO_PHYSICAL replaces osVirtualToPhysical, this replacement + // assumes that only KSEG0 addresses are given + HW_REG(AI_DRAM_ADDR_REG, u32) = OS_K0_TO_PHYSICAL(bufAdjusted); + HW_REG(AI_LEN_REG, u32) = size; + return 0; +} diff --git a/soh/src/libultra/io/cartrominit.c b/soh/src/libultra/io/cartrominit.c new file mode 100644 index 000000000..d7e9aef4f --- /dev/null +++ b/soh/src/libultra/io/cartrominit.c @@ -0,0 +1,62 @@ +#include "global.h" + +OSPiHandle __CartRomHandle; + +OSPiHandle* osCartRomInit(void) { + register u32 a; + register s32 status; + register u32 prevInt; + register u32 lastLatency; + register u32 lastPageSize; + register u32 lastRelDuration; + register u32 lastPulse; + + static u32 D_8000AF10 = 1; + + __osPiGetAccess(); + + if (!D_8000AF10) { + __osPiRelAccess(); + return &__CartRomHandle; + } + + D_8000AF10 = 0; + __CartRomHandle.type = DEVICE_TYPE_CART; + __CartRomHandle.baseAddress = 0xB0000000; + __CartRomHandle.domain = PI_DOMAIN1; + __CartRomHandle.speed = 0; + bzero(&__CartRomHandle.transferInfo, sizeof(__OSTranxInfo)); + + while (status = HW_REG(PI_STATUS_REG, u32), status & (PI_STATUS_BUSY | PI_STATUS_IOBUSY)) { + ; + } + + lastLatency = HW_REG(PI_BSD_DOM1_LAT_REG, u32); + lastPageSize = HW_REG(PI_BSD_DOM1_PGS_REG, u32); + lastRelDuration = HW_REG(PI_BSD_DOM1_RLS_REG, u32); + lastPulse = HW_REG(PI_BSD_DOM1_PWD_REG, u32); + + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = 0xFF; + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = 0; + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = 3; + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = 0xFF; + + a = HW_REG(__CartRomHandle.baseAddress, u32); + __CartRomHandle.latency = a & 0xFF; + __CartRomHandle.pageSize = (a >> 0x10) & 0xF; + __CartRomHandle.relDuration = (a >> 0x14) & 0xF; + __CartRomHandle.pulse = (a >> 8) & 0xFF; + + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = lastLatency; + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = lastPageSize; + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = lastRelDuration; + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = lastPulse; + + prevInt = __osDisableInt(); + __CartRomHandle.next = __osPiTable; + __osPiTable = &__CartRomHandle; + __osRestoreInt(prevInt); + __osPiRelAccess(); + + return &__CartRomHandle; +} diff --git a/soh/src/libultra/io/contpfs.c b/soh/src/libultra/io/contpfs.c new file mode 100644 index 000000000..26ac77b6f --- /dev/null +++ b/soh/src/libultra/io/contpfs.c @@ -0,0 +1,311 @@ +#include "ultra64.h" +#include "global.h" + +s32 __osPfsInodeCacheChannel = -1; +u8 __osPfsInodeCacheBank = 250; + +u16 __osSumcalc(u8* ptr, s32 length) { + s32 i; + u32 sum = 0; + u8* temp = ptr; + + for (i = 0; i < length; i++) { + sum += *temp++; + } + return sum & 0xFFFF; +} + +s32 __osIdCheckSum(u16* ptr, u16* checkSum, u16* idSum) { + u16 data = 0; + u32 i; + + *checkSum = *idSum = 0; + for (i = 0; i < ((sizeof(__OSPackId) - sizeof(uintptr_t)) / sizeof(u8)); i += 2) { + data = *((u16*)((uintptr_t)ptr + i)); + *checkSum += data; + *idSum += ~data; + } + return 0; +} + +s32 __osRepairPackId(OSPfs* pfs, __OSPackId* badid, __OSPackId* newid) { + s32 ret = 0; + u8 temp[BLOCKSIZE]; + u8 comp[BLOCKSIZE]; + u8 mask = 0; + s32 i, j = 0; + u16 index[4]; + + newid->repaired = 0xFFFFFFFF; + newid->random = osGetCount(); + newid->serialMid = badid->serialMid; + newid->serialLow = badid->serialLow; + + if ((pfs->activebank != 0) && ((ret = __osPfsSelectBank(pfs, 0)) != 0)) { + return ret; + } + + do { + if ((ret = __osPfsSelectBank(pfs, j)) != 0) { + return ret; + } + + if ((ret = __osContRamRead(pfs->queue, pfs->channel, 0, temp)) != 0) { + return ret; + } + temp[0] = j | 0x80; + for (i = 1; i < BLOCKSIZE; i++) { + temp[i] = ~temp[i]; + } + + if ((ret = __osContRamWrite(pfs->queue, pfs->channel, 0, temp, 0)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, 0, comp)) != 0) { + return (ret); + } + for (i = 0; i < BLOCKSIZE; i++) { + if (comp[i] != temp[i]) { + break; + } + } + if (i != BLOCKSIZE) { + break; + } + + if (j > 0) { + if ((ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, 0, temp)) != 0) { + return ret; + } + if (temp[0] != 0x80) { + break; + } + } + + j++; + } while (j < PFS_MAX_BANKS); + + if ((pfs->activebank != 0) && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + + mask = (j > 0) ? 1 : 0; + newid->deviceid = (badid->deviceid & 0xFFFE) | mask; + newid->banks = j; + newid->version = badid->version; + __osIdCheckSum((u16*)newid, &newid->checksum, &newid->invertedChecksum); + + index[0] = PFS_ID_0AREA; + index[1] = PFS_ID_1AREA; + index[2] = PFS_ID_2AREA; + index[3] = PFS_ID_3AREA; + for (i = 0; i < 4; i++) { + if ((ret = __osContRamWrite(pfs->queue, pfs->channel, index[i], (u8*)newid, PFS_FORCE)) != 0) { + return ret; + } + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_ID_0AREA, temp)) != 0) { + return ret; + } + for (i = 0; i < BLOCKSIZE; i++) { + if (temp[i] != *(u8*)((s32)newid + i)) { + return PFS_ERR_DEVICE; + } + } + return 0; +} + +s32 __osCheckPackId(OSPfs* pfs, __OSPackId* check) { + u16 index[4]; + s32 ret = 0; + u16 sum; + u16 idSum; + s32 i; + s32 j; + + if ((pfs->activebank != 0) && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + + index[0] = PFS_ID_0AREA; + index[1] = PFS_ID_1AREA; + index[2] = PFS_ID_2AREA; + index[3] = PFS_ID_3AREA; + for (i = 1; i < 4; i++) { + if ((ret = __osContRamRead(pfs->queue, pfs->channel, index[i], (u8*)check)) != 0) { + return ret; + } + __osIdCheckSum((u16*)check, &sum, &idSum); + if ((check->checksum == sum) && (check->invertedChecksum == idSum)) { + break; + } + } + if (i == 4) { + return PFS_ERR_ID_FATAL; + } + + for (j = 0; j < 4; j++) { + if (j != i) { + if ((ret = __osContRamWrite(pfs->queue, pfs->channel, index[j], (u8*)check, PFS_FORCE)) != 0) { + return ret; + } + } + } + return 0; +} + +s32 __osGetId(OSPfs* pfs) { + u16 sum; + u16 isum; + u8 temp[BLOCKSIZE]; + __OSPackId* id; + __OSPackId newid; + s32 ret; + + if (pfs->activebank != 0) { + if ((ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + } + + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_ID_0AREA, temp)) != 0) { + return ret; + } + + __osIdCheckSum((u16*)temp, &sum, &isum); + id = (__OSPackId*)temp; + if ((id->checksum != sum) || (id->invertedChecksum != isum)) { + if ((ret = __osCheckPackId(pfs, id)) == PFS_ERR_ID_FATAL) { + ret = __osRepairPackId(pfs, id, &newid); + if (ret) { + return ret; + } + id = &newid; + } else if (ret != 0) { + return ret; + } + } + + if ((id->deviceid & 0x01) == 0) { + ret = __osRepairPackId(pfs, id, &newid); + if (ret) { + return ret; + } + id = &newid; + if ((id->deviceid & 0x01) == 0) { + return PFS_ERR_DEVICE; + } + } + + bcopy(id, pfs->id, BLOCKSIZE); + + if (0) {} + + pfs->version = id->version; + + pfs->banks = id->banks; + pfs->inodeStartPage = 1 + DEF_DIR_PAGES + (2 * pfs->banks); + pfs->dir_size = DEF_DIR_PAGES * PFS_ONE_PAGE; + pfs->inode_table = 1 * PFS_ONE_PAGE; + pfs->minode_table = (1 + pfs->banks) * PFS_ONE_PAGE; + pfs->dir_table = pfs->minode_table + (pfs->banks * PFS_ONE_PAGE); + + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_LABEL_AREA, pfs->label)) != 0) { + return ret; + } + + return 0; +} + +s32 __osCheckId(OSPfs* pfs) { + u8 temp[BLOCKSIZE]; + s32 ret; + + if (pfs->activebank != 0) { + ret = __osPfsSelectBank(pfs, 0); + if (ret == PFS_ERR_NEW_PACK) { + ret = __osPfsSelectBank(pfs, 0); + } + if (ret != 0) { + return ret; + } + } + + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_ID_0AREA, temp)) != 0) { + if (ret != PFS_ERR_NEW_PACK) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_ID_0AREA, temp)) != 0) { + return ret; + } + } + + if (bcmp(pfs->id, temp, BLOCKSIZE) != 0) { + return PFS_ERR_NEW_PACK; + } + + return 0; +} + +s32 __osPfsRWInode(OSPfs* pfs, __OSInode* inode, u8 flag, u8 bank) { + u8 sum; + s32 j; + s32 ret; + s32 offset; + u8* addr; + + if (flag == PFS_READ && bank == __osPfsInodeCacheBank && (pfs->channel == __osPfsInodeCacheChannel)) { + bcopy(&__osPfsInodeCache, inode, sizeof(__OSInode)); + return 0; + } + + if ((pfs->activebank != 0) && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + + offset = ((bank > 0) ? 1 : pfs->inodeStartPage); + + if (flag == PFS_WRITE) { + inode->inodePage[0].inode_t.page = + __osSumcalc((u8*)(inode->inodePage + offset), (PFS_INODE_SIZE_PER_PAGE - offset) * 2); + } + + for (j = 0; j < PFS_ONE_PAGE; j++) { + addr = (u8*)(((u8*)inode) + (j * BLOCKSIZE)); + if (flag == PFS_WRITE) { + ret = __osContRamWrite(pfs->queue, pfs->channel, pfs->inode_table + (bank * PFS_ONE_PAGE) + j, addr, 0); + ret = __osContRamWrite(pfs->queue, pfs->channel, pfs->minode_table + (bank * PFS_ONE_PAGE) + j, addr, 0); + } else { + ret = __osContRamRead(pfs->queue, pfs->channel, pfs->inode_table + (bank * PFS_ONE_PAGE) + j, addr); + } + if (ret) { + return ret; + } + } + + if (flag == PFS_READ) { + sum = __osSumcalc((u8*)(inode->inodePage + offset), (PFS_INODE_SIZE_PER_PAGE - offset) * 2); + if (sum != inode->inodePage[0].inode_t.page) { + for (j = 0; j < PFS_ONE_PAGE; j++) { + addr = (u8*)(((u8*)inode) + (j * BLOCKSIZE)); + ret = __osContRamRead(pfs->queue, pfs->channel, pfs->minode_table + (bank * PFS_ONE_PAGE) + j, addr); + } + sum = __osSumcalc((u8*)(inode->inodePage + offset), (PFS_INODE_SIZE_PER_PAGE - offset) * 2); + if (sum != inode->inodePage[0].inode_t.page) { + return PFS_ERR_INCONSISTENT; + } + for (j = 0; j < PFS_ONE_PAGE; j++) { + addr = (u8*)(((u8*)inode) + (j * BLOCKSIZE)); + ret = __osContRamWrite(pfs->queue, pfs->channel, pfs->inode_table + (bank * PFS_ONE_PAGE) + j, addr, 0); + } + } + } + __osPfsInodeCacheBank = bank; + bcopy(inode, &__osPfsInodeCache, sizeof(__OSInode)); + __osPfsInodeCacheChannel = pfs->channel; + + return 0; +} diff --git a/soh/src/libultra/io/contquery.c b/soh/src/libultra/io/contquery.c new file mode 100644 index 000000000..21cb884d4 --- /dev/null +++ b/soh/src/libultra/io/contquery.c @@ -0,0 +1,30 @@ +#include "global.h" + +/** + * osContStartQuery: + * Starts to read the values for SI device status and type which are connected to the controller port and joyport + * connector. + */ +s32 osContStartQuery(OSMesgQueue* mq) { + s32 ret = 0; + + __osSiGetAccess(); + if (__osContLastPoll != CONT_CMD_REQUEST_STATUS) { + __osPackRequestData(CONT_CMD_REQUEST_STATUS); + ret = __osSiRawStartDma(OS_WRITE, &__osPifInternalBuff); + osRecvMesg(mq, NULL, OS_MESG_BLOCK); + } + ret = __osSiRawStartDma(OS_READ, &__osPifInternalBuff); + __osContLastPoll = CONT_CMD_REQUEST_STATUS; + __osSiRelAccess(); + return ret; +} + +/** + * osContGetQuery: + * Returns the values from osContStartQuery to status. Both functions must be paired for use. + */ +void osContGetQuery(OSContStatus* data) { + u8 pattern; + __osContGetInitData(&pattern, data); +} diff --git a/soh/src/libultra/io/contramread.c b/soh/src/libultra/io/contramread.c new file mode 100644 index 000000000..97d5db651 --- /dev/null +++ b/soh/src/libultra/io/contramread.c @@ -0,0 +1,63 @@ +#include "global.h" + +#define BLOCKSIZE 32 + +s32 __osPfsLastChannel = -1; + +s32 __osContRamRead(OSMesgQueue* ctrlrqueue, s32 channel, u16 addr, u8* data) { + s32 ret; + s32 i; + u8* bufptr; + s32 retryCount = 2; + + __osSiGetAccess(); + do { + bufptr = (u8*)&gPifMempakBuf; + + if ((__osContLastPoll != 2) || (__osPfsLastChannel != channel)) { + __osContLastPoll = 2; + __osPfsLastChannel = channel; + // clang-format off + for (i = 0; i < channel; i++) { *bufptr++ = 0; } + // clang-format on + gPifMempakBuf.status = 1; + ((__OSContRamHeader*)bufptr)->unk_00 = 0xFF; + ((__OSContRamHeader*)bufptr)->txsize = 3; + ((__OSContRamHeader*)bufptr)->rxsize = 0x21; + ((__OSContRamHeader*)bufptr)->poll = CONT_CMD_READ_MEMPACK; // read mempak; send byte 0 + ((__OSContRamHeader*)bufptr)->datacrc = 0xFF; // read mempak; send byte 0 + // Received bytes are 6-26 inclusive + bufptr[sizeof(__OSContRamHeader)] = CONT_CMD_END; // End of commands + } else { + bufptr += channel; + } + + ((__OSContRamHeader*)bufptr)->hi = addr >> 3; // send byte 1 + ((__OSContRamHeader*)bufptr)->lo = (s8)(__osContAddressCrc(addr) | (addr << 5)); // send byte 2 + __osSiRawStartDma(OS_WRITE, &gPifMempakBuf); + osRecvMesg(ctrlrqueue, NULL, OS_MESG_BLOCK); + __osSiRawStartDma(OS_READ, &gPifMempakBuf); + osRecvMesg(ctrlrqueue, NULL, OS_MESG_BLOCK); + + ret = (((__OSContRamHeader*)bufptr)->rxsize & 0xC0) >> 4; + if (!ret) { + if (((__OSContRamHeader*)bufptr)->datacrc != __osContDataCrc(bufptr + 6)) { + ret = __osPfsGetStatus(ctrlrqueue, channel); + if (ret) { + break; + } + ret = 4; // Retry + } else { + bcopy(bufptr + 6, data, BLOCKSIZE); + } + } else { + ret = 1; // Error + } + if (ret != 4) { + break; + } + } while (0 <= retryCount--); + __osSiRelAccess(); + + return ret; +} diff --git a/soh/src/libultra/io/contramwrite.c b/soh/src/libultra/io/contramwrite.c new file mode 100644 index 000000000..e27c03227 --- /dev/null +++ b/soh/src/libultra/io/contramwrite.c @@ -0,0 +1,68 @@ +#include "ultra64.h" +#include "global.h" + +s32 __osContRamWrite(OSMesgQueue* mq, s32 channel, u16 address, u8* buffer, s32 force) { + s32 ret = 0; + s32 i; + u8* ptr; + s32 retry = 2; + u8 crc; + + if ((force != PFS_FORCE) && (address < PFS_LABEL_AREA) && (address != 0)) { + return 0; + } + + __osSiGetAccess(); + + do { + ptr = (u8*)(&gPifMempakBuf); + + if (__osContLastPoll != CONT_CMD_WRITE_MEMPACK || __osPfsLastChannel != channel) { + __osContLastPoll = CONT_CMD_WRITE_MEMPACK; + __osPfsLastChannel = channel; + + // clang-format off + for (i = 0; i < channel; i++) { *ptr++ = 0; } + // clang-format on + + gPifMempakBuf.status = 1; + + ((__OSContRamHeader*)ptr)->unk_00 = 0xFF; + ((__OSContRamHeader*)ptr)->txsize = 35; + ((__OSContRamHeader*)ptr)->rxsize = 1; + ((__OSContRamHeader*)ptr)->poll = CONT_CMD_WRITE_MEMPACK; + ((__OSContRamHeader*)ptr)->datacrc = 0xFF; + + ptr[sizeof(__OSContRamHeader)] = CONT_CMD_END; + } else { + ptr += channel; + } + ((__OSContRamHeader*)ptr)->hi = address >> 3; + ((__OSContRamHeader*)ptr)->lo = ((address << 5) | __osContAddressCrc(address)); + + bcopy(buffer, ((__OSContRamHeader*)ptr)->data, BLOCKSIZE); + + ret = __osSiRawStartDma(OS_WRITE, &gPifMempakBuf); + crc = __osContDataCrc(buffer); + osRecvMesg(mq, (OSMesg*)NULL, OS_MESG_BLOCK); + + ret = __osSiRawStartDma(OS_READ, &gPifMempakBuf); + osRecvMesg(mq, (OSMesg*)NULL, OS_MESG_BLOCK); + + ret = ((((__OSContRamHeader*)ptr)->rxsize & 0xC0) >> 4); + if (!ret) { + if (crc != ((__OSContRamHeader*)ptr)->datacrc) { + if ((ret = __osPfsGetStatus(mq, channel))) { + break; + } else { + ret = PFS_ERR_CONTRFAIL; + } + } + } else { + ret = PFS_ERR_NOPACK; + } + } while ((ret == PFS_ERR_CONTRFAIL) && (retry-- >= 0)); + __osSiRelAccess(); + + return ret; +} diff --git a/soh/src/libultra/io/contreaddata.c b/soh/src/libultra/io/contreaddata.c new file mode 100644 index 000000000..a85a3337b --- /dev/null +++ b/soh/src/libultra/io/contreaddata.c @@ -0,0 +1,55 @@ +#include "global.h" + +s32 osContStartReadData(OSMesgQueue* mq) { + s32 ret; + + __osSiGetAccess(); + if (__osContLastPoll != 1) { + __osPackReadData(); + __osSiRawStartDma(OS_WRITE, &__osPifInternalBuff); + osRecvMesg(mq, NULL, OS_MESG_BLOCK); + } + ret = __osSiRawStartDma(OS_READ, &__osPifInternalBuff); + __osContLastPoll = CONT_CMD_READ_BUTTON; + __osSiRelAccess(); + return ret; +} + +void osContGetReadData(OSContPad* contData) { + u8* bufptr = (u8*)(&__osPifInternalBuff); + __OSContReadHeader read; + s32 i; + + for (i = 0; i < __osMaxControllers; i++, bufptr += sizeof(read), contData++) { + read = *((__OSContReadHeader*)bufptr); + contData->errno = (read.rxsize & 0xC0) >> 4; + if (contData->errno == 0) { + contData->button = read.button; + contData->stick_x = read.joyX; + contData->stick_y = read.joyY; + } + }; +} + +void __osPackReadData(void) { + u8* bufptr = (u8*)(&__osPifInternalBuff); + __OSContReadHeader read; + s32 i; + + for (i = 0; i < 0xF; i++) { + __osPifInternalBuff.ram[i] = 0; + } + __osPifInternalBuff.status = 1; + read.align = 0xFF; + read.txsize = 1; + read.rxsize = 4; + read.poll = CONT_CMD_READ_BUTTON; + read.button = 0xFFFF; + read.joyX = 0xFF; + read.joyY = 0xFF; + for (i = 0; i < __osMaxControllers; i++) { + *((__OSContReadHeader*)bufptr) = read; + bufptr += sizeof(read); + } + *((u8*)bufptr) = CONT_CMD_END; +} diff --git a/soh/src/libultra/io/controller.c b/soh/src/libultra/io/controller.c new file mode 100644 index 000000000..5b44e0112 --- /dev/null +++ b/soh/src/libultra/io/controller.c @@ -0,0 +1,94 @@ +#include "global.h" + +OSPifRam __osPifInternalBuff; +u8 __osContLastPoll; +u8 __osMaxControllers; // always 4 + +OSTimer __osEepromTimer; +OSMesgQueue __osEepromTimerMsgQ; +OSMesg __osEepromTimerMsg; + +u32 gOSContInitialized = 0; + +#define HALF_SECOND OS_USEC_TO_CYCLES(500000) + +s32 osContInit(OSMesgQueue* mq, u8* ctlBitfield, OSContStatus* status) { + OSMesg mesg; + s32 ret = 0; + OSTime currentTime; + OSTimer timer; + OSMesgQueue timerqueue; + + if (gOSContInitialized) { + return 0; + } + + gOSContInitialized = 1; + currentTime = osGetTime(); + if (HALF_SECOND > currentTime) { + osCreateMesgQueue(&timerqueue, &mesg, 1); + osSetTimer(&timer, HALF_SECOND - currentTime, 0, &timerqueue, &mesg); + osRecvMesg(&timerqueue, &mesg, OS_MESG_BLOCK); + } + __osMaxControllers = MAXCONTROLLERS; + __osPackRequestData(CONT_CMD_REQUEST_STATUS); + ret = __osSiRawStartDma(OS_WRITE, &__osPifInternalBuff); + osRecvMesg(mq, &mesg, OS_MESG_BLOCK); + ret = __osSiRawStartDma(OS_READ, &__osPifInternalBuff); + osRecvMesg(mq, &mesg, OS_MESG_BLOCK); + __osContGetInitData(ctlBitfield, status); + __osContLastPoll = CONT_CMD_REQUEST_STATUS; + __osSiCreateAccessQueue(); + osCreateMesgQueue(&__osEepromTimerMsgQ, &__osEepromTimerMsg, 1); + + return ret; +} + +void __osContGetInitData(u8* ctlBitfield, OSContStatus* status) { + u8* bufptr; + __OSContRequestHeader req; + s32 i; + u8 bitfieldTemp = 0; + + bufptr = (u8*)(&__osPifInternalBuff); + + for (i = 0; i < __osMaxControllers; i++, bufptr += sizeof(req), status++) { + req = *((__OSContRequestHeader*)bufptr); + status->errno = (req.rxsize & 0xC0) >> 4; + if (status->errno) { + continue; + } + status->type = req.typel << 8 | req.typeh; + status->status = req.status; + bitfieldTemp |= 1 << i; + } + *ctlBitfield = bitfieldTemp; +} + +void __osPackRequestData(u8 poll) { + u8* bufptr; + __OSContRequestHeader req; + s32 i; + + for (i = 0; i < 0xF; i++) { + __osPifInternalBuff.ram[i] = 0; + } + __osPifInternalBuff.status = 1; + + bufptr = (u8*)(&__osPifInternalBuff); + + req.align = 0xFF; + req.txsize = 1; + req.rxsize = 3; + req.poll = poll; + req.typeh = 0xFF; + req.typel = 0xFF; + req.status = 0xFF; + req.align1 = 0xFF; + + for (i = 0; i < __osMaxControllers; i++) { + *((__OSContRequestHeader*)bufptr) = req; + bufptr += sizeof(req); + } + *((u8*)bufptr) = 254; +} diff --git a/soh/src/libultra/io/contsetch.c b/soh/src/libultra/io/contsetch.c new file mode 100644 index 000000000..0e7502b0c --- /dev/null +++ b/soh/src/libultra/io/contsetch.c @@ -0,0 +1,21 @@ +#include "ultra64.h" +#include "global.h" + +/* + * s32 osContSetCh(u8 ch) + * This function specifies the number of devices for the functions to access when those functions access to multiple + * direct SI devices. + */ +s32 osContSetCh(u8 ch) { + __osSiGetAccess(); + + if (ch > MAXCONTROLLERS) { + __osMaxControllers = MAXCONTROLLERS; + } else { + __osMaxControllers = ch; + } + + __osContLastPoll = -2; + __osSiRelAccess(); + return 0; +} diff --git a/soh/src/libultra/io/crc.c b/soh/src/libultra/io/crc.c new file mode 100644 index 000000000..c1af0b12c --- /dev/null +++ b/soh/src/libultra/io/crc.c @@ -0,0 +1,62 @@ +#include "global.h" + +// Valid addr up to 0x7FF +// It's the address of a block of 0x20 bytes in the mempak +// So that means the whole mempak has a 16-bit address space +u8 __osContAddressCrc(u16 addr) { + u32 addr32 = addr; + u32 ret = 0; + u32 bit; + s32 i; + + for (bit = 0x400; bit; bit >>= 1) { + ret <<= 1; + if (addr32 & bit) { + if (ret & 0x20) { + ret ^= 0x14; + } else { + ++ret; + } + } else if (ret & 0x20) { + ret ^= 0x15; + } + } + for (i = 0; i < 5; ++i) { + ret <<= 1; + if (ret & 0x20) { + ret ^= 0x15; + } + } + + return ret & 0x1F; +} + +u8 __osContDataCrc(u8* data) { + s32 ret = 0; + u32 bit; + u32 byte; + + for (byte = 0x20; byte; --byte, ++data) { + for (bit = 0x80; bit; bit >>= 1) { + ret <<= 1; + if ((*data & bit) != 0) { + if ((ret & 0x100) != 0) { + ret ^= 0x84; + } else { + ++ret; + } + } else if (ret & 0x100) { + ret ^= 0x85; + } + } + } + do { + ret <<= 1; + if (ret & 0x100) { + ret ^= 0x85; + } + ++byte; + } while (byte < 8U); + + return ret; +} diff --git a/soh/src/libultra/io/devmgr.c b/soh/src/libultra/io/devmgr.c new file mode 100644 index 000000000..af7f4d575 --- /dev/null +++ b/soh/src/libultra/io/devmgr.c @@ -0,0 +1,98 @@ +#include "global.h" +#include "ultra64/internal.h" + +void __osDevMgrMain(void* arg) { + OSIoMesg* ioMesg; + OSMesg sp70; + OSMesg sp6C; + OSMgrArgs* arg0 = (OSMgrArgs*)arg; + __OSTranxInfo* transfer; + __OSBlockInfo* block; + s32 phi_s2; + s32 phi_s0; + u32 sp54; + + ioMesg = NULL; + + while (true) { + osRecvMesg(arg0->cmdQueue, (OSMesg)&ioMesg, OS_MESG_BLOCK); + if ((ioMesg->piHandle != NULL) && (ioMesg->piHandle->type == DEVICE_TYPE_64DD) && + ((ioMesg->piHandle->transferInfo.cmdType == 0) || (ioMesg->piHandle->transferInfo.cmdType == 1))) { + transfer = &ioMesg->piHandle->transferInfo; + block = &transfer->block[transfer->blockNum]; + transfer->sectorNum = -1; + if (transfer->transferMode != 3) { + block->dramAddr = (void*)((uintptr_t)block->dramAddr - block->sectorSize); + } + + phi_s2 = ((transfer->transferMode == 2) && (ioMesg->piHandle->transferInfo.cmdType == 0)) ? 1 : 0; + + osRecvMesg(arg0->acccessQueue, &sp6C, OS_MESG_BLOCK); + __osResetGlobalIntMask(0x00100401); + __osEPiRawWriteIo(ioMesg->piHandle, 0x05000510, transfer->bmCtlShadow | 0x80000000); + + while (true) { + osRecvMesg(arg0->eventQueue, &sp70, OS_MESG_BLOCK); + transfer = &ioMesg->piHandle->transferInfo; + block = &transfer->block[transfer->blockNum]; + if (block->errStatus == 0x1D) { + __osEPiRawWriteIo(ioMesg->piHandle, 0x05000510, transfer->bmCtlShadow | 0x10000000); + __osEPiRawWriteIo(ioMesg->piHandle, 0x05000510, transfer->bmCtlShadow); + __osEPiRawReadIo(ioMesg->piHandle, 0x05000508, &sp54); + if (sp54 & 0x02000000) { + __osEPiRawWriteIo(ioMesg->piHandle, 0x05000510, transfer->bmCtlShadow | 0x1000000); + } + block->errStatus = 4; + HW_REG(PI_STATUS_REG, u32) = PI_STATUS_CLEAR_INTR; + __osSetGlobalIntMask(0x00100C01); + } + osSendMesg(ioMesg->hdr.retQueue, ioMesg, OS_MESG_NOBLOCK); + + if ((phi_s2 != 1) || (ioMesg->piHandle->transferInfo.block[0].errStatus != 0)) { + break; + } + + phi_s2 = 0; + } + + osSendMesg(arg0->acccessQueue, 0, OS_MESG_NOBLOCK); + if (ioMesg->piHandle->transferInfo.blockNum == 1) { + osYieldThread(); + } + } else { + switch (ioMesg->hdr.type) { + case OS_MESG_TYPE_DMAREAD: + osRecvMesg(arg0->acccessQueue, &sp6C, OS_MESG_BLOCK); + phi_s0 = arg0->piDmaCallback(OS_READ, ioMesg->devAddr, ioMesg->dramAddr, ioMesg->size); + break; + case OS_MESG_TYPE_DMAWRITE: + osRecvMesg(arg0->acccessQueue, &sp6C, OS_MESG_BLOCK); + phi_s0 = arg0->piDmaCallback(OS_WRITE, ioMesg->devAddr, ioMesg->dramAddr, ioMesg->size); + break; + case OS_MESG_TYPE_EDMAREAD: + osRecvMesg(arg0->acccessQueue, &sp6C, OS_MESG_BLOCK); + phi_s0 = arg0->epiDmaCallback(ioMesg->piHandle, OS_READ, ioMesg->devAddr, ioMesg->dramAddr, + ioMesg->size); + break; + case OS_MESG_TYPE_EDMAWRITE: + osRecvMesg(arg0->acccessQueue, &sp6C, OS_MESG_BLOCK); + phi_s0 = arg0->epiDmaCallback(ioMesg->piHandle, OS_WRITE, ioMesg->devAddr, ioMesg->dramAddr, + ioMesg->size); + break; + case OS_MESG_TYPE_LOOPBACK: + osSendMesg(ioMesg->hdr.retQueue, ioMesg, OS_MESG_NOBLOCK); + phi_s0 = -1; + break; + default: + phi_s0 = -1; + break; + } + + if (phi_s0 == 0) { + osRecvMesg(arg0->eventQueue, &sp70, OS_MESG_BLOCK); + osSendMesg(ioMesg->hdr.retQueue, ioMesg, OS_MESG_NOBLOCK); + osSendMesg(arg0->acccessQueue, NULL, OS_MESG_NOBLOCK); + } + } + } +} diff --git a/soh/src/libultra/io/dpgetstat.c b/soh/src/libultra/io/dpgetstat.c new file mode 100644 index 000000000..b5267b060 --- /dev/null +++ b/soh/src/libultra/io/dpgetstat.c @@ -0,0 +1,5 @@ +#include "global.h" + +u32 osDpGetStatus(void) { + return DPC_STATUS_REG; +} diff --git a/soh/src/libultra/io/dpsetstat.c b/soh/src/libultra/io/dpsetstat.c new file mode 100644 index 000000000..ecd3f77e0 --- /dev/null +++ b/soh/src/libultra/io/dpsetstat.c @@ -0,0 +1,5 @@ +#include "global.h" + +void osDpSetStatus(u32 status) { + DPC_STATUS_REG = status; +} diff --git a/soh/src/libultra/io/driverominit.c b/soh/src/libultra/io/driverominit.c new file mode 100644 index 000000000..672b60ea6 --- /dev/null +++ b/soh/src/libultra/io/driverominit.c @@ -0,0 +1,58 @@ +#include "global.h" + +OSPiHandle __DriveRomHandle; + +OSPiHandle* osDriveRomInit(void) { + register s32 status; + register u32 a; + register u32 prevInt; + static u32 D_8000AC70 = 1; + + __osPiGetAccess(); + + if (!D_8000AC70) { + __osPiRelAccess(); + return &__DriveRomHandle; + } + + D_8000AC70 = 0; + __DriveRomHandle.type = DEVICE_TYPE_BULK; + __DriveRomHandle.baseAddress = 0xA6000000; + __DriveRomHandle.domain = PI_DOMAIN1; + __DriveRomHandle.speed = 0; + bzero(&__DriveRomHandle.transferInfo, sizeof(__OSTranxInfo)); + + while (status = HW_REG(PI_STATUS_REG, u32), status & (PI_STATUS_BUSY | PI_STATUS_IOBUSY)) { + ; + } + + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = 0xFF; + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = 0; + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = 3; + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = 0xFF; + + a = HW_REG(__DriveRomHandle.baseAddress, u32); + __DriveRomHandle.latency = a & 0xFF; + __DriveRomHandle.pulse = (a >> 8) & 0xFF; + __DriveRomHandle.pageSize = (a >> 0x10) & 0xF; + __DriveRomHandle.relDuration = (a >> 0x14) & 0xF; + + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = (u8)a; + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = __DriveRomHandle.pageSize; + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = __DriveRomHandle.relDuration; + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = __DriveRomHandle.pulse; + + __osCurrentHandle[__DriveRomHandle.domain]->type = __DriveRomHandle.type; + __osCurrentHandle[__DriveRomHandle.domain]->latency = __DriveRomHandle.latency; + __osCurrentHandle[__DriveRomHandle.domain]->pageSize = __DriveRomHandle.pageSize; + __osCurrentHandle[__DriveRomHandle.domain]->relDuration = __DriveRomHandle.relDuration; + __osCurrentHandle[__DriveRomHandle.domain]->pulse = __DriveRomHandle.pulse; + + prevInt = __osDisableInt(); + __DriveRomHandle.next = __osPiTable; + __osPiTable = &__DriveRomHandle; + __osRestoreInt(prevInt); + __osPiRelAccess(); + + return &__DriveRomHandle; +} diff --git a/soh/src/libultra/io/epidma.c b/soh/src/libultra/io/epidma.c new file mode 100644 index 000000000..41b39caa9 --- /dev/null +++ b/soh/src/libultra/io/epidma.c @@ -0,0 +1,25 @@ +#include "global.h" +#include "ultra64/internal.h" + +s32 osEPiStartDma(OSPiHandle* handle, OSIoMesg* mb, s32 direction) { + s32 ret; + + if (!__osPiDevMgr.initialized) { + return -1; + } + + mb->piHandle = handle; + if (direction == OS_READ) { + mb->hdr.type = 0xF; + } else { + mb->hdr.type = 0x10; + } + + if (mb->hdr.pri == 1) { + ret = osJamMesg(osPiGetCmdQueue(), (OSMesg)mb, OS_MESG_NOBLOCK); + } else { + ret = osSendMesg(osPiGetCmdQueue(), (OSMesg)mb, OS_MESG_NOBLOCK); + } + + return ret; +} diff --git a/soh/src/libultra/io/epirawdma.c b/soh/src/libultra/io/epirawdma.c new file mode 100644 index 000000000..c8f4de5fd --- /dev/null +++ b/soh/src/libultra/io/epirawdma.c @@ -0,0 +1,71 @@ +#include "global.h" + +s32 __osEPiRawStartDma(OSPiHandle* handle, s32 direction, u32 cartAddr, void* dramAddr, size_t size) { + s32 status; + OSPiHandle* curHandle; + + while (status = HW_REG(PI_STATUS_REG, u32), status & (PI_STATUS_BUSY | PI_STATUS_IOBUSY)) { + ; + } + + if (__osCurrentHandle[handle->domain]->type != handle->type) { + curHandle = __osCurrentHandle[handle->domain]; + + if (handle->domain == 0) { + if (curHandle->latency != handle->latency) { + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = handle->latency; + } + + if (curHandle->pageSize != handle->pageSize) { + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = handle->pageSize; + } + + if (curHandle->relDuration != handle->relDuration) { + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = handle->relDuration; + } + + if (curHandle->pulse != handle->pulse) { + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = handle->pulse; + } + } else { + if (curHandle->latency != handle->latency) { + HW_REG(PI_BSD_DOM2_LAT_REG, u32) = handle->latency; + } + + if (curHandle->pageSize != handle->pageSize) { + HW_REG(PI_BSD_DOM2_PGS_REG, u32) = handle->pageSize; + } + + if (curHandle->relDuration != handle->relDuration) { + HW_REG(PI_BSD_DOM2_RLS_REG, u32) = handle->relDuration; + } + + if (curHandle->pulse != handle->pulse) { + HW_REG(PI_BSD_DOM2_PWD_REG, u32) = handle->pulse; + } + } + + curHandle->type = handle->type; + curHandle->latency = handle->latency; + curHandle->pageSize = handle->pageSize; + curHandle->relDuration = handle->relDuration; + curHandle->pulse = handle->pulse; + } + + HW_REG(PI_DRAM_ADDR_REG, void*) = (void*)osVirtualToPhysical(dramAddr); + HW_REG(PI_CART_ADDR_REG, void*) = (void*)((handle->baseAddress | cartAddr) & 0x1FFFFFFF); + + switch (direction) { + case OS_READ: + HW_REG(PI_WR_LEN_REG, u32) = size - 1; + break; + case OS_WRITE: + HW_REG(PI_RD_LEN_REG, u32) = size - 1; + break; + default: + return -1; + break; + } + + return 0; +} diff --git a/soh/src/libultra/io/epirawread.c b/soh/src/libultra/io/epirawread.c new file mode 100644 index 000000000..513a246ee --- /dev/null +++ b/soh/src/libultra/io/epirawread.c @@ -0,0 +1,57 @@ +#include "global.h" + +s32 __osEPiRawReadIo(OSPiHandle* handle, u32 devAddr, u32* data) { + s32 status; + OSPiHandle* curHandle; + + while (status = HW_REG(PI_STATUS_REG, u32), status & (PI_STATUS_BUSY | PI_STATUS_IOBUSY)) { + ; + } + + if (__osCurrentHandle[handle->domain]->type != handle->type) { + curHandle = __osCurrentHandle[handle->domain]; + + if (handle->domain == 0) { + if (curHandle->latency != handle->latency) { + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = handle->latency; + } + + if (curHandle->pageSize != handle->pageSize) { + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = handle->pageSize; + } + + if (curHandle->relDuration != handle->relDuration) { + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = handle->relDuration; + } + + if (curHandle->pulse != handle->pulse) { + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = handle->pulse; + } + } else { + if (curHandle->latency != handle->latency) { + HW_REG(PI_BSD_DOM2_LAT_REG, u32) = handle->latency; + } + + if (curHandle->pageSize != handle->pageSize) { + HW_REG(PI_BSD_DOM2_PGS_REG, u32) = handle->pageSize; + } + + if (curHandle->relDuration != handle->relDuration) { + HW_REG(PI_BSD_DOM2_RLS_REG, u32) = handle->relDuration; + } + + if (curHandle->pulse != handle->pulse) { + HW_REG(PI_BSD_DOM2_PWD_REG, u32) = handle->pulse; + } + } + + curHandle->type = handle->type; + curHandle->latency = handle->latency; + curHandle->pageSize = handle->pageSize; + curHandle->relDuration = handle->relDuration; + curHandle->pulse = handle->pulse; + } + + *data = HW_REG(handle->baseAddress | devAddr | 0, u32); + return 0; +} diff --git a/soh/src/libultra/io/epirawwrite.c b/soh/src/libultra/io/epirawwrite.c new file mode 100644 index 000000000..dc169ecf7 --- /dev/null +++ b/soh/src/libultra/io/epirawwrite.c @@ -0,0 +1,57 @@ +#include "global.h" + +s32 __osEPiRawWriteIo(OSPiHandle* handle, u32 devAddr, u32 data) { + s32 status; + OSPiHandle* curHandle; + + while (status = HW_REG(PI_STATUS_REG, u32), status & (PI_STATUS_BUSY | PI_STATUS_IOBUSY)) { + ; + } + + if (__osCurrentHandle[handle->domain]->type != handle->type) { + curHandle = __osCurrentHandle[handle->domain]; + + if (handle->domain == 0) { + if (curHandle->latency != handle->latency) { + HW_REG(PI_BSD_DOM1_LAT_REG, u32) = handle->latency; + } + + if (curHandle->pageSize != handle->pageSize) { + HW_REG(PI_BSD_DOM1_PGS_REG, u32) = handle->pageSize; + } + + if (curHandle->relDuration != handle->relDuration) { + HW_REG(PI_BSD_DOM1_RLS_REG, u32) = handle->relDuration; + } + + if (curHandle->pulse != handle->pulse) { + HW_REG(PI_BSD_DOM1_PWD_REG, u32) = handle->pulse; + } + } else { + if (curHandle->latency != handle->latency) { + HW_REG(PI_BSD_DOM2_LAT_REG, u32) = handle->latency; + } + + if (curHandle->pageSize != handle->pageSize) { + HW_REG(PI_BSD_DOM2_PGS_REG, u32) = handle->pageSize; + } + + if (curHandle->relDuration != handle->relDuration) { + HW_REG(PI_BSD_DOM2_RLS_REG, u32) = handle->relDuration; + } + + if (curHandle->pulse != handle->pulse) { + HW_REG(PI_BSD_DOM2_PWD_REG, u32) = handle->pulse; + } + } + + curHandle->type = handle->type; + curHandle->latency = handle->latency; + curHandle->pageSize = handle->pageSize; + curHandle->relDuration = handle->relDuration; + curHandle->pulse = handle->pulse; + } + + HW_REG(handle->baseAddress | devAddr, u32) = data; + return 0; +} diff --git a/soh/src/libultra/io/epiread.c b/soh/src/libultra/io/epiread.c new file mode 100644 index 000000000..d05604a58 --- /dev/null +++ b/soh/src/libultra/io/epiread.c @@ -0,0 +1,11 @@ +#include "global.h" + +s32 osEPiReadIo(OSPiHandle* handle, u32 devAddr, u32* data) { + register s32 ret; + + __osPiGetAccess(); + ret = __osEPiRawReadIo(handle, devAddr, data); + __osPiRelAccess(); + + return ret; +} diff --git a/soh/src/libultra/io/epiwrite.c b/soh/src/libultra/io/epiwrite.c new file mode 100644 index 000000000..0547e6999 --- /dev/null +++ b/soh/src/libultra/io/epiwrite.c @@ -0,0 +1,11 @@ +#include "global.h" + +s32 osEPiWriteIo(OSPiHandle* handle, u32 devAddr, u32 data) { + register s32 ret; + + __osPiGetAccess(); + ret = __osEPiRawWriteIo(handle, devAddr, data); + __osPiRelAccess(); + + return ret; +} diff --git a/soh/src/libultra/io/motor.c b/soh/src/libultra/io/motor.c new file mode 100644 index 000000000..5b5abb9e1 --- /dev/null +++ b/soh/src/libultra/io/motor.c @@ -0,0 +1,120 @@ +#include "global.h" + +#define BANK_ADDR 0x400 +#define MOTOR_ID 0x80 + +OSPifRam osPifBuffers[MAXCONTROLLERS]; + +s32 __osMotorAccess(OSPfs* pfs, u32 vibrate) { + s32 i; + s32 ret; + u8* buf = (u8*)&osPifBuffers[pfs->channel]; + + if (!(pfs->status & 8)) { + return 5; + } + + __osSiGetAccess(); + osPifBuffers[pfs->channel].status = 1; + buf += pfs->channel; + for (i = 0; i < BLOCKSIZE; i++) { + ((__OSContRamHeader*)buf)->data[i] = vibrate; + } + + __osContLastPoll = CONT_CMD_END; + __osSiRawStartDma(OS_WRITE, &osPifBuffers[pfs->channel]); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + __osSiRawStartDma(OS_READ, &osPifBuffers[pfs->channel]); + osRecvMesg(pfs->queue, NULL, OS_MESG_BLOCK); + + ret = ((__OSContRamHeader*)buf)->rxsize & 0xC0; + if (!ret) { + if (!vibrate) { + if (((__OSContRamHeader*)buf)->datacrc != 0) { + ret = PFS_ERR_CONTRFAIL; + } + } else { + if (((__OSContRamHeader*)buf)->datacrc != 0xEB) { + ret = PFS_ERR_CONTRFAIL; + } + } + } + + __osSiRelAccess(); + + return ret; +} + +void _MakeMotorData(s32 channel, OSPifRam* buf) { + u8* bufptr = (u8*)buf; + __OSContRamHeader mempakwr; + s32 i; + + mempakwr.unk_00 = 0xFF; + mempakwr.txsize = 0x23; + mempakwr.rxsize = 1; + mempakwr.poll = 3; // write mempak + mempakwr.hi = 0x600 >> 3; + mempakwr.lo = (u8)(__osContAddressCrc(0x600) | (0x600 << 5)); + + if (channel != 0) { + for (i = 0; i < channel; ++i) { + *bufptr++ = 0; + } + } + + *(__OSContRamHeader*)bufptr = mempakwr; + bufptr += sizeof(mempakwr); + *bufptr = 0xFE; +} + +s32 osMotorInit(OSMesgQueue* ctrlrqueue, OSPfs* pfs, s32 channel) { + s32 ret; + u8 sp24[BLOCKSIZE]; + + pfs->queue = ctrlrqueue; + pfs->channel = channel; + pfs->activebank = 0xFF; + pfs->status = 0; + + ret = __osPfsSelectBank(pfs, 0xFE); + if (ret == 2) { + ret = __osPfsSelectBank(pfs, MOTOR_ID); + } + if (ret != 0) { + return ret; + } + ret = __osContRamRead(ctrlrqueue, channel, BANK_ADDR, sp24); + if (ret == 2) { + ret = 4; // "Controller pack communication error" + } + if (ret != 0) { + return ret; + } + if (sp24[BLOCKSIZE - 1] == 0xFE) { + return 0xB; + } + ret = __osPfsSelectBank(pfs, MOTOR_ID); + if (ret == 2) { + ret = 4; // "Controller pack communication error" + } + if (ret != 0) { + return ret; + } + ret = __osContRamRead(ctrlrqueue, channel, BANK_ADDR, sp24); + if (ret == 2) { + ret = 4; // "Controller pack communication error" + } + if (ret != 0) { + return ret; + } + if (sp24[BLOCKSIZE - 1] != MOTOR_ID) { + return 0xB; + } + if ((pfs->status & PFS_MOTOR_INITIALIZED) == 0) { + _MakeMotorData(channel, &osPifBuffers[channel]); + } + pfs->status = PFS_MOTOR_INITIALIZED; + + return 0; // "Recognized rumble pak" +} diff --git a/soh/src/libultra/io/pfsallocatefile.c b/soh/src/libultra/io/pfsallocatefile.c new file mode 100644 index 000000000..c20531224 --- /dev/null +++ b/soh/src/libultra/io/pfsallocatefile.c @@ -0,0 +1,139 @@ +#include "ultra64.h" +#include "global.h" +#include "ultra64/pfs.h" + +s32 osPfsAllocateFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName, s32 fileSize, s32* fileNo) { + s32 startPage; + s32 decleared; + s32 prevPage; + s32 oldPrevPage = 0; + s32 ret = 0; + s32 fileSizeInPages; + __OSInode inode; + __OSInode backupInode; + __OSDir dir; + u8 bank; + u8 prevBank = 0; + s32 firsttime = 0; + s32 bytes; + __OSInodeUnit fpage; + + if ((companyCode == 0) || (gameCode == 0)) { + return PFS_ERR_INVALID; + } + + fileSizeInPages = (fileSize + PFS_PAGE_SIZE - 1) / PFS_PAGE_SIZE; + + if (((ret = osPfsFindFile(pfs, companyCode, gameCode, gameName, extName, fileNo)) != 0) && + (ret != PFS_ERR_INVALID)) { + return ret; + } + if (*fileNo != -1) { + return PFS_ERR_EXIST; + } + + ret = osPfsFreeBlocks(pfs, &bytes); + if (fileSize > bytes) { + return PFS_DATA_FULL; + } + + if (fileSizeInPages == 0) { + return (PFS_ERR_INVALID); + } + + if (((ret = osPfsFindFile(pfs, 0, 0, 0, 0, fileNo)) != 0) && (ret != PFS_ERR_INVALID)) { + return ret; + } + if (*fileNo == -1) { + return PFS_DIR_FULL; + } + + for (bank = PFS_ID_BANK_256K; bank < pfs->banks; bank++) { + if ((ret = __osPfsRWInode(pfs, &inode, PFS_READ, bank)) != 0) { + return ret; + } + ret = __osPfsDeclearPage(pfs, &inode, fileSizeInPages, &startPage, bank, &decleared, &prevPage); + if (ret) { + return ret; + } + if (startPage != -1) { /* There is free space */ + if (firsttime == 0) { + fpage.inode_t.page = (u8)startPage; + fpage.inode_t.bank = bank; + } else { /* Writing previous bank inode */ + backupInode.inodePage[oldPrevPage].inode_t.bank = bank; + backupInode.inodePage[oldPrevPage].inode_t.page = (u8)startPage; + if ((ret = __osPfsRWInode(pfs, &backupInode, PFS_WRITE, prevBank)) != 0) { + return ret; + } + } + if (fileSizeInPages > decleared) { + bcopy(&inode, &backupInode, sizeof(__OSInode)); + oldPrevPage = prevPage; + prevBank = bank; + fileSizeInPages -= decleared; + firsttime++; + } else { + fileSizeInPages = 0; + if ((ret = __osPfsRWInode(pfs, &inode, PFS_WRITE, bank)) != 0) { + return ret; + } + break; + } + } + } + if ((fileSizeInPages > 0) || (startPage == -1)) { + return PFS_ERR_INCONSISTENT; + } + + dir.start_page = fpage; + dir.company_code = companyCode; + dir.game_code = gameCode; + dir.data_sum = 0; + + bcopy(gameName, dir.game_name, PFS_FILE_NAME_LEN); + bcopy(extName, dir.ext_name, PFS_FILE_EXT_LEN); + + return __osContRamWrite(pfs->queue, pfs->channel, pfs->dir_table + *fileNo, (u8*)&dir, 0); +} + +s32 __osPfsDeclearPage(OSPfs* pfs, __OSInode* inode, s32 fileSizeInPages, s32* startPage, u8 bank, s32* decleared, + s32* finalPage) { + s32 j; + s32 spage, prevPage; + s32 ret = 0; + s32 offset = ((bank > PFS_ID_BANK_256K) ? 1 : pfs->inodeStartPage); + + for (j = offset; j < PFS_INODE_SIZE_PER_PAGE; j++) { + if (inode->inodePage[j].ipage == PFS_PAGE_NOT_USED) { + break; + } + } + if (j == PFS_INODE_SIZE_PER_PAGE) { + *startPage = -1; + return ret; + } + + spage = j; + *decleared = 1; + prevPage = j; + j++; + while ((fileSizeInPages > *decleared) && (j < PFS_INODE_SIZE_PER_PAGE)) { + if (inode->inodePage[j].ipage == PFS_PAGE_NOT_USED) { + inode->inodePage[prevPage].inode_t.bank = (u8)bank; + inode->inodePage[prevPage].inode_t.page = (u8)j; + prevPage = j; + (*decleared)++; + } + j++; + } + + *startPage = spage; + if ((j == (PFS_INODE_SIZE_PER_PAGE)) && (fileSizeInPages > *decleared)) { + *finalPage = prevPage; + } else { + inode->inodePage[prevPage].ipage = PFS_EOF; + *finalPage = 0; + } + return ret; +} diff --git a/soh/src/libultra/io/pfschecker.c b/soh/src/libultra/io/pfschecker.c new file mode 100644 index 000000000..b0a94f1ad --- /dev/null +++ b/soh/src/libultra/io/pfschecker.c @@ -0,0 +1,193 @@ +#include "ultra64.h" +#include "global.h" +#include "ultra64/pfs.h" + +#define CHECK_IPAGE(p) \ + (((p).ipage >= pfs->inodeStartPage) && ((p).inode_t.bank < pfs->banks) && ((p).inode_t.page >= 0x01) && \ + ((p).inode_t.page < 0x80)) + +s32 osPfsChecker(OSPfs* pfs) { + s32 j; + s32 ret; + __OSInodeUnit next; + __OSInode checkedInode; + __OSInode tempInode; + __OSDir tempDir; + __OSInodeUnit nextNodeInFile[16]; + __OSInodeCache cache; + s32 fixed = 0; + u8 bank, prevBank = 254; + s32 cc, cl; + s32 offset; + + ret = __osCheckId(pfs); + if (ret == PFS_ERR_NEW_PACK) { + ret = __osGetId(pfs); + } + if (ret) { + return ret; + } + if ((ret = func_80105788(pfs, &cache)) != 0) { + return ret; + } + + for (j = 0; j < pfs->dir_size; j++) { + if ((ret = __osContRamRead(pfs->queue, pfs->channel, pfs->dir_table + j, (u8*)&tempDir)) != 0) { + return ret; + } + if ((tempDir.company_code != 0) || (tempDir.game_code != 0)) { + if ((tempDir.company_code == 0) || (tempDir.game_code == 0)) { + cc = -1; + } else { + next = tempDir.start_page; + cl = cc = 0; + bank = 255; + + while (CHECK_IPAGE(next)) { + if (bank != next.inode_t.bank) { + bank = next.inode_t.bank; + if (prevBank != bank) { + ret = __osPfsRWInode(pfs, &tempInode, PFS_READ, bank); + prevBank = bank; + } + if ((ret != 0) && (ret != PFS_ERR_INCONSISTENT)) { + return ret; + } + } + if ((cc = func_80105A60(pfs, next, &cache) - cl) != 0) { + break; + } + cl = 1; + next = tempInode.inodePage[next.inode_t.page]; + } + } + if ((cc != 0) || (next.ipage != PFS_EOF)) { + bzero(&tempDir, sizeof(__OSDir)); + if (pfs->activebank != 0) { + if ((ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + } + if ((ret = __osContRamWrite(pfs->queue, pfs->channel, pfs->dir_table + j, (u8*)&tempDir, 0)) != 0) { + return ret; + } + + fixed++; + } + } + } + + for (j = 0; j < pfs->dir_size; j++) { + if ((ret = __osContRamRead(pfs->queue, pfs->channel, pfs->dir_table + j, (u8*)&tempDir)) != 0) { + return ret; + } + + if ((tempDir.company_code != 0) && (tempDir.game_code != 0) && + (tempDir.start_page.ipage >= (u16)pfs->inodeStartPage)) { // cast required + nextNodeInFile[j].ipage = tempDir.start_page.ipage; + } else { + nextNodeInFile[j].ipage = 0; + } + } + + for (bank = 0; bank < pfs->banks; bank++) { + ret = __osPfsRWInode(pfs, &tempInode, PFS_READ, bank); + if ((ret != 0) && (ret != PFS_ERR_INCONSISTENT)) { + return (ret); + } + offset = ((bank > PFS_ID_BANK_256K) ? 1 : pfs->inodeStartPage); + for (j = 0; j < offset; j++) { + checkedInode.inodePage[j].ipage = tempInode.inodePage[j].ipage; + } + for (; j < PFS_INODE_SIZE_PER_PAGE; j++) { + checkedInode.inodePage[j].ipage = PFS_PAGE_NOT_USED; + } + + for (j = 0; j < pfs->dir_size; j++) { + while (nextNodeInFile[j].inode_t.bank == bank && + nextNodeInFile[j].ipage >= (u16)pfs->inodeStartPage) { // cast required + u8 val; + val = nextNodeInFile[j].inode_t.page; + nextNodeInFile[j] = checkedInode.inodePage[val] = tempInode.inodePage[val]; + } + } + if ((ret = __osPfsRWInode(pfs, &checkedInode, PFS_WRITE, bank)) != 0) { + return ret; + } + } + + if (fixed != 0) { + pfs->status |= PFS_CORRUPTED; + } else { + pfs->status &= ~PFS_CORRUPTED; + } + return 0; +} + +// Original name: corrupted_init (probably needs better name) +s32 func_80105788(OSPfs* pfs, __OSInodeCache* cache) { + s32 i; + s32 n; + s32 offset; + u8 bank; + __OSInodeUnit tpage; + __OSInode tempInode; + s32 ret; + + for (i = 0; i < PFS_INODE_DIST_MAP; i++) { + cache->map[i] = 0; + } + cache->bank = 255; + + for (bank = PFS_ID_BANK_256K; bank < pfs->banks; bank++) { + offset = ((bank > PFS_ID_BANK_256K) ? 1 : pfs->inodeStartPage); + ret = __osPfsRWInode(pfs, &tempInode, PFS_READ, bank); + if ((ret != 0) && (ret != PFS_ERR_INCONSISTENT)) { + return ret; + } + + for (i = offset; i < PFS_INODE_SIZE_PER_PAGE; i++) { + tpage = tempInode.inodePage[i]; + if ((tpage.ipage >= pfs->inodeStartPage) && (tpage.inode_t.bank != bank)) { + n = ((tpage.inode_t.page & 0x7F) / PFS_SECTOR_SIZE) + + PFS_SECTOR_PER_BANK * (tpage.inode_t.bank % PFS_BANK_LAPPED_BY); + cache->map[n] |= (1 << (bank % PFS_BANK_LAPPED_BY)); + } + } + } + return 0; +} + +// original name: corrupted (probably needs a better name) +s32 func_80105A60(OSPfs* pfs, __OSInodeUnit fpage, __OSInodeCache* cache) { + s32 j; + s32 n; + s32 hit = 0; + u8 bank; + s32 offset; + s32 ret = 0; + + n = (fpage.inode_t.page / PFS_SECTOR_SIZE) + PFS_SECTOR_PER_BANK * (fpage.inode_t.bank % PFS_BANK_LAPPED_BY); + + for (bank = PFS_ID_BANK_256K; bank < pfs->banks; bank++) { + offset = ((bank > PFS_ID_BANK_256K) ? 1 : pfs->inodeStartPage); + if ((bank == fpage.inode_t.bank) || (cache->map[n] & (1 << (bank % PFS_BANK_LAPPED_BY))) != 0) { + if (bank != cache->bank) { + ret = __osPfsRWInode(pfs, &(cache->inode), PFS_READ, bank); + if (ret && (ret != PFS_ERR_INCONSISTENT)) { + return ret; + } + cache->bank = bank; + } + for (j = offset; ((hit < 2) && (j < PFS_INODE_SIZE_PER_PAGE)); j++) { + if (cache->inode.inodePage[j].ipage == fpage.ipage) { + hit++; + } + } + if (hit >= 2) { + return 2; + } + } + } + return hit; +} diff --git a/soh/src/libultra/io/pfsdeletefile.c b/soh/src/libultra/io/pfsdeletefile.c new file mode 100644 index 000000000..573af6a72 --- /dev/null +++ b/soh/src/libultra/io/pfsdeletefile.c @@ -0,0 +1,71 @@ +#include "ultra64/pfs.h" +#include "global.h" + +s32 osPfsDeleteFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName) { + s32 file_no; + s32 ret; + __OSInode inode; + __OSDir dir; + __OSInodeUnit last_page; + u8 startpage; + u8 bank; + + if ((companyCode == 0) || (gameCode == 0)) { + return PFS_ERR_INVALID; + } + if ((ret = osPfsFindFile(pfs, companyCode, gameCode, gameName, extName, &file_no)) != 0) { + return ret; + } + if ((pfs->activebank != 0) && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + + if ((ret = __osContRamRead(pfs->queue, pfs->channel, pfs->dir_table + file_no, (u8*)&dir)) != 0) { + return ret; + } + + startpage = dir.start_page.inode_t.page; + for (bank = dir.start_page.inode_t.bank; bank < pfs->banks;) { + if ((ret = __osPfsRWInode(pfs, &inode, PFS_READ, bank)) != 0) { + return ret; + } + if ((ret = __osPfsReleasePages(pfs, &inode, startpage, bank, &last_page)) != 0) { + return ret; + } + if ((ret = __osPfsRWInode(pfs, &inode, PFS_WRITE, bank)) != 0) { + return ret; + } + if (last_page.ipage == PFS_EOF) { + break; + } + bank = last_page.inode_t.bank; + startpage = last_page.inode_t.page; + } + + if (bank >= pfs->banks) { + return PFS_ERR_INCONSISTENT; + } + bzero(&dir, sizeof(__OSDir)); + + ret = __osContRamWrite(pfs->queue, pfs->channel, pfs->dir_table + file_no, (u8*)&dir, 0); + + return ret; +} + +s32 __osPfsReleasePages(OSPfs* pfs, __OSInode* inode, u8 initialPage, u8 bank, __OSInodeUnit* finalPage) { + __OSInodeUnit next; + __OSInodeUnit prev; + s32 ret = 0; + + next.ipage = (u16)((bank << 8) + initialPage); + + do { + prev = next; + next = inode->inodePage[next.inode_t.page]; + inode->inodePage[prev.inode_t.page].ipage = PFS_PAGE_NOT_USED; + } while (next.ipage >= pfs->inodeStartPage && next.inode_t.bank == bank); + + *finalPage = next; + + return ret; +} diff --git a/soh/src/libultra/io/pfsfilestate.c b/soh/src/libultra/io/pfsfilestate.c new file mode 100644 index 000000000..367a7f27d --- /dev/null +++ b/soh/src/libultra/io/pfsfilestate.c @@ -0,0 +1,58 @@ +#include "ultra64.h" +#include "global.h" + +s32 osPfsFileState(OSPfs* pfs, s32 fileNo, OSPfsState* state) { + s32 ret; + s32 pages; + __OSInode inode; + __OSDir dir; + __OSInodeUnit page; + u8 bank; + + if (fileNo >= pfs->dir_size || fileNo < 0) { + return PFS_ERR_INVALID; + } + if (!(pfs->status & PFS_INITIALIZED)) { + return PFS_ERR_INVALID; + } + if ((ret = __osCheckId(pfs)) != 0) { + return ret; + } + if (pfs->activebank != 0 && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, pfs->dir_table + fileNo, (u8*)&dir)) != 0) { + return ret; + } + if (dir.company_code == 0 || dir.game_code == 0) { + return PFS_ERR_INVALID; + } + + page = dir.start_page; + pages = 0; + bank = 0xFF; + while (true) { + if (page.ipage < pfs->inodeStartPage) { + break; + } + if (page.inode_t.bank != bank) { + bank = page.inode_t.bank; + if ((ret = __osPfsRWInode(pfs, &inode, PFS_READ, bank)) != 0) { + return ret; + } + } + pages++; + page = inode.inodePage[page.inode_t.page]; + } + if (page.ipage != PFS_EOF) { + return PFS_ERR_INCONSISTENT; + } + + state->file_size = pages * PFS_ONE_PAGE * BLOCKSIZE; + state->company_code = dir.company_code; + state->game_code = dir.game_code; + bcopy(&dir.game_name, state->game_name, PFS_FILE_NAME_LEN); + bcopy(&dir.ext_name, state->ext_name, PFS_FILE_EXT_LEN); + + return __osPfsGetStatus(pfs->queue, pfs->channel); +} diff --git a/soh/src/libultra/io/pfsfindfile.c b/soh/src/libultra/io/pfsfindfile.c new file mode 100644 index 000000000..d7798e4fb --- /dev/null +++ b/soh/src/libultra/io/pfsfindfile.c @@ -0,0 +1,53 @@ +#include "ultra64.h" +#include "global.h" + +s32 osPfsFindFile(OSPfs* pfs, u16 companyCode, u32 gameCode, u8* gameName, u8* extName, s32* fileNo) { + s32 j; + s32 i; + __OSDir dir; + s32 ret = 0; + s32 err; + + if (!(pfs->status & PFS_INITIALIZED)) { + return PFS_ERR_INVALID; + } + + if ((ret = __osCheckId(pfs)) != 0) { + return ret; + } + + for (j = 0; j < pfs->dir_size; j++) { + if ((ret = __osContRamRead(pfs->queue, pfs->channel, pfs->dir_table + j, (u8*)&dir)) != 0) { + return ret; + } + if ((ret = __osPfsGetStatus(pfs->queue, pfs->channel)) != 0) { + return ret; + } + + if ((dir.company_code == companyCode) && (dir.game_code == gameCode)) { + err = 0; + if (gameName != 0) { + for (i = 0; i < PFS_FILE_NAME_LEN; i++) { + if (dir.game_name[i] != gameName[i]) { + err = 1; + break; + } + } + } + if ((extName != 0) && (err == 0)) { + for (i = 0; i < PFS_FILE_EXT_LEN; i++) { + if (dir.ext_name[i] != extName[i]) { + err = 1; + break; + } + } + } + if (err == 0) { + *fileNo = j; + return ret; + } + } + } + *fileNo = -1; + return PFS_ERR_INVALID; +} diff --git a/soh/src/libultra/io/pfsfreeblocks.c b/soh/src/libultra/io/pfsfreeblocks.c new file mode 100644 index 000000000..7210c6581 --- /dev/null +++ b/soh/src/libultra/io/pfsfreeblocks.c @@ -0,0 +1,35 @@ +#include "ultra64.h" +#include "global.h" +#include "ultra64/pfs.h" + +s32 osPfsFreeBlocks(OSPfs* pfs, s32* leftoverBytes) { + s32 j; + s32 pages = 0; + __OSInode inode; + s32 ret = 0; + u8 bank; + s32 offset; + + if (!(pfs->status & PFS_INITIALIZED)) { + return (PFS_ERR_INVALID); + } + if ((ret = __osCheckId(pfs)) != 0) { + return ret; + } + + for (bank = PFS_ID_BANK_256K; bank < pfs->banks; bank++) { + if ((ret = __osPfsRWInode(pfs, &inode, PFS_READ, bank)) != 0) { + return ret; + } + + offset = ((bank > PFS_ID_BANK_256K) ? 1 : pfs->inodeStartPage); + for (j = offset; j < PFS_INODE_SIZE_PER_PAGE; j++) { + if (inode.inodePage[j].ipage == PFS_PAGE_NOT_USED) { + pages++; + } + } + } + + *leftoverBytes = pages * PFS_ONE_PAGE * BLOCKSIZE; + return 0; +} diff --git a/soh/src/libultra/io/pfsgetstatus.c b/soh/src/libultra/io/pfsgetstatus.c new file mode 100644 index 000000000..3a1e962a2 --- /dev/null +++ b/soh/src/libultra/io/pfsgetstatus.c @@ -0,0 +1,74 @@ +#include "ultra64.h" +#include "global.h" + +OSPifRam gPifMempakBuf; + +s32 __osPfsGetStatus(OSMesgQueue* queue, s32 channel) { + s32 ret = 0; + OSMesg msg; + OSContStatus data; + + __osPfsInodeCacheBank = 250; + + __osPfsRequestOneChannel(channel, CONT_CMD_REQUEST_STATUS); + ret = __osSiRawStartDma(OS_WRITE, &gPifMempakBuf); + osRecvMesg(queue, &msg, OS_MESG_BLOCK); + + ret = __osSiRawStartDma(OS_READ, &gPifMempakBuf); + osRecvMesg(queue, &msg, OS_MESG_BLOCK); + + __osPfsGetOneChannelData(channel, &data); + if (((data.status & CONT_CARD_ON) != 0) && ((data.status & CONT_CARD_PULL) != 0)) { + return PFS_ERR_NEW_PACK; + } else if (data.errno || ((data.status & CONT_CARD_ON) == 0)) { + return PFS_ERR_NOPACK; + } else if ((data.status & CONT_ADDR_CRC_ER) != 0) { + return PFS_ERR_CONTRFAIL; + } + return ret; +} + +void __osPfsRequestOneChannel(s32 channel, u8 poll) { + u8* bufptr; + __OSContRequestHeaderAligned req; + s32 idx; + + __osContLastPoll = CONT_CMD_END; + gPifMempakBuf.status = CONT_CMD_READ_BUTTON; + + bufptr = (u8*)&gPifMempakBuf; + + req.txsize = 1; + req.rxsize = 3; + req.poll = poll; + req.typeh = 0xFF; + req.typel = 0xFF; + req.status = 0xFF; + + for (idx = 0; idx < channel; idx++) { + *bufptr++ = 0; + } + + *((__OSContRequestHeaderAligned*)bufptr) = req; + bufptr += sizeof(req); + *((u8*)bufptr) = CONT_CMD_END; +} + +void __osPfsGetOneChannelData(s32 channel, OSContStatus* contData) { + u8* bufptr = (u8*)&gPifMempakBuf; + __OSContRequestHeaderAligned req; + s32 idx; + + for (idx = 0; idx < channel; idx++) { + bufptr++; + } + + req = *((__OSContRequestHeaderAligned*)bufptr); + contData->errno = (req.rxsize & 0xC0) >> 4; + if (contData->errno) { + return; + } + + contData->type = (req.typel << 8) | req.typeh; + contData->status = req.status; +} diff --git a/soh/src/libultra/io/pfsinitpak.c b/soh/src/libultra/io/pfsinitpak.c new file mode 100644 index 000000000..59fb8eda9 --- /dev/null +++ b/soh/src/libultra/io/pfsinitpak.c @@ -0,0 +1,107 @@ +#include "ultra64.h" +#include "global.h" + +s32 osPfsInitPak(OSMesgQueue* queue, OSPfs* pfs, s32 channel) { + s32 ret; + u16 sum; + u16 isum; + u8 temp[BLOCKSIZE]; + __OSPackId* id; + __OSPackId newid; + + __osSiGetAccess(); + + ret = __osPfsGetStatus(queue, channel); + + __osSiRelAccess(); + + if (ret != 0) { + return ret; + } + + pfs->queue = queue; + pfs->channel = channel; + pfs->status = 0; + + if ((ret = __osPfsCheckRamArea(pfs)) != 0) { + return ret; + } + if ((ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_ID_0AREA, temp)) != 0) { + return (ret); + } + + __osIdCheckSum((u16*)temp, &sum, &isum); + id = (__OSPackId*)temp; + if ((id->checksum != sum) || (id->invertedChecksum != isum)) { + if ((ret = __osCheckPackId(pfs, id)) != 0) { + pfs->status |= PFS_ID_BROKEN; + return ret; + } + } + + if ((id->deviceid & 0x01) == 0) { + ret = __osRepairPackId(pfs, id, &newid); + if (ret) { + if (ret == PFS_ERR_ID_FATAL) { + pfs->status |= PFS_ID_BROKEN; + } + return ret; + } + id = &newid; + if ((id->deviceid & 0x01) == 0) { + return PFS_ERR_DEVICE; + } + } + + bcopy(id, pfs->id, BLOCKSIZE); + + if (1) {} + + pfs->version = id->version; + pfs->banks = id->banks; + pfs->inodeStartPage = 1 + DEF_DIR_PAGES + (2 * pfs->banks); + pfs->dir_size = DEF_DIR_PAGES * PFS_ONE_PAGE; + pfs->inode_table = 1 * PFS_ONE_PAGE; + pfs->minode_table = (1 + pfs->banks) * PFS_ONE_PAGE; + pfs->dir_table = pfs->minode_table + (pfs->banks * PFS_ONE_PAGE); + + if ((ret = __osContRamRead(pfs->queue, pfs->channel, PFS_LABEL_AREA, pfs->label)) != 0) { + return ret; + } + + ret = osPfsChecker(pfs); + pfs->status |= PFS_INITIALIZED; + + return ret; +} + +s32 __osPfsCheckRamArea(OSPfs* pfs) { + s32 i = 0; + s32 ret = 0; + u8 temp1[BLOCKSIZE]; + u8 temp2[BLOCKSIZE]; + u8 saveReg[BLOCKSIZE]; + + if ((ret = __osPfsSelectBank(pfs, PFS_ID_BANK_256K)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, 0, saveReg)) != 0) { + return ret; + } + for (i = 0; i < BLOCKSIZE; i++) { + temp1[i] = i; + } + if ((ret = __osContRamWrite(pfs->queue, pfs->channel, 0, temp1, 0)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, 0, temp2)) != 0) { + return ret; + } + if (bcmp(temp1, temp2, BLOCKSIZE) != 0) { + return PFS_ERR_DEVICE; + } + return __osContRamWrite(pfs->queue, pfs->channel, 0, saveReg, 0); +} diff --git a/soh/src/libultra/io/pfsisplug.c b/soh/src/libultra/io/pfsisplug.c new file mode 100644 index 000000000..a1b73cf8e --- /dev/null +++ b/soh/src/libultra/io/pfsisplug.c @@ -0,0 +1,93 @@ +#include "ultra64.h" +#include "global.h" + +s32 osPfsIsPlug(OSMesgQueue* mq, u8* pattern) { + s32 ret = 0; + OSMesg msg; + u8 bitpattern; + OSContStatus contData[MAXCONTROLLERS]; + s32 channel; + u8 bits = 0; + s32 crcErrorCount = 3; + + __osSiGetAccess(); + + do { + __osPfsRequestData(CONT_CMD_REQUEST_STATUS); + + ret = __osSiRawStartDma(OS_WRITE, &gPifMempakBuf); + osRecvMesg(mq, &msg, OS_MESG_BLOCK); + + ret = __osSiRawStartDma(OS_READ, &gPifMempakBuf); + osRecvMesg(mq, &msg, OS_MESG_BLOCK); + + __osPfsGetInitData(&bitpattern, &contData[0]); + + for (channel = 0; channel < __osMaxControllers; channel++) { + if ((contData[channel].status & CONT_ADDR_CRC_ER) == 0) { + crcErrorCount--; + break; + } + } + if (channel == __osMaxControllers) { + crcErrorCount = 0; + } + } while (crcErrorCount > 0); + + for (channel = 0; channel < __osMaxControllers; channel++) { + if ((contData[channel].errno == 0) && ((contData[channel].status & CONT_CARD_ON) != 0)) { + bits |= (1 << channel); + } + } + __osSiRelAccess(); + *pattern = bits; + return ret; +} + +void __osPfsRequestData(u8 poll) { + u8* bufPtr = (u8*)&gPifMempakBuf; + __OSContRequestHeader req; + s32 i; + + __osContLastPoll = poll; + + gPifMempakBuf.status = 1; + + req.align = 0xFF; + req.txsize = 1; + req.rxsize = 3; + req.poll = poll; + req.typeh = 0xFF; + req.typel = 0xFF; + req.status = 0xFF; + req.align1 = 0xFF; + + for (i = 0; i < __osMaxControllers; i++) { + *((__OSContRequestHeader*)bufPtr) = req; + bufPtr += sizeof(req); + } + *((u8*)bufPtr) = CONT_CMD_END; +} + +void __osPfsGetInitData(u8* pattern, OSContStatus* contData) { + u8* bufptr; + __OSContRequestHeader req; + s32 i; + u8 bits = 0; + + bufptr = (u8*)&gPifMempakBuf; + + for (i = 0; i < __osMaxControllers; i++, bufptr += sizeof(req), contData++) { + req = *((__OSContRequestHeader*)bufptr); + contData->errno = ((req.rxsize & 0xC0) >> 4); + + if (contData->errno) { + continue; + } + + contData->type = ((req.typel << 8) | req.typeh); + contData->status = req.status; + bits |= (1 << i); + } + *pattern = bits; +} diff --git a/soh/src/libultra/io/pfsreadwritefile.c b/soh/src/libultra/io/pfsreadwritefile.c new file mode 100644 index 000000000..8c93ddc7b --- /dev/null +++ b/soh/src/libultra/io/pfsreadwritefile.c @@ -0,0 +1,126 @@ +#include "ultra64.h" +#include "global.h" + +#define CHECK_IPAGE(p, pfs) \ + (((p).ipage >= (pfs).inodeStartPage) && ((p).inode_t.bank < (pfs).banks) && ((p).inode_t.page >= 0x01) && \ + ((p).inode_t.page < 0x80)) + +__OSInode __osPfsInodeCache; + +s32 __osPfsGetNextPage(OSPfs* pfs, u8* bank, __OSInode* inode, __OSInodeUnit* page) { + s32 ret; + + if (page->inode_t.bank != *bank) { + *bank = page->inode_t.bank; + if ((ret = __osPfsRWInode(pfs, inode, PFS_READ, *bank)) != 0) { + return ret; + } + } + *page = inode->inodePage[page->inode_t.page]; + + if (!CHECK_IPAGE(*page, *pfs)) { + if (page->ipage == PFS_EOF) { + return PFS_ERR_INVALID; + } + return PFS_ERR_INCONSISTENT; + } + return 0; +} + +s32 osPfsReadWriteFile(OSPfs* pfs, s32 fileNo, u8 flag, s32 offset, ssize_t size, u8* data) { + s32 ret; + __OSDir dir; + __OSInode inode; + __OSInodeUnit curPage; + s32 curBlock; + s32 blockSize; + u8* buffer; + u8 bank; + u16 blockno; + + if ((fileNo >= pfs->dir_size) || (fileNo < 0)) { + return PFS_ERR_INVALID; + } + if ((size <= 0) || ((size % BLOCKSIZE) != 0)) { + return PFS_ERR_INVALID; + } + if ((offset < 0) || ((offset % BLOCKSIZE) != 0)) { + return PFS_ERR_INVALID; + } + if (!(pfs->status & PFS_INITIALIZED)) { + return PFS_ERR_INVALID; + } + if (__osCheckId(pfs) == PFS_ERR_NEW_PACK) { + return PFS_ERR_NEW_PACK; + } + if (pfs->activebank != 0 && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + if ((ret = __osContRamRead(pfs->queue, pfs->channel, pfs->dir_table + fileNo, (u8*)&dir)) != 0) { + return ret; + } + if ((dir.company_code == 0) || (dir.game_code == 0)) { + return PFS_ERR_INVALID; + } + if (!CHECK_IPAGE(dir.start_page, *pfs)) { + if (dir.start_page.ipage == PFS_EOF) { + return PFS_ERR_INVALID; + } + return PFS_ERR_INCONSISTENT; + } + if ((flag == PFS_READ) && ((dir.status & PFS_WRITTEN) == 0)) { + return PFS_ERR_BAD_DATA; + } + + bank = 255; + curBlock = offset / BLOCKSIZE; + curPage = dir.start_page; + + while (curBlock >= 8) { + if ((ret = __osPfsGetNextPage(pfs, &bank, &inode, &curPage)) != 0) { + return ret; + } + curBlock -= 8; + } + + blockSize = size / BLOCKSIZE; + buffer = data; + + while (blockSize > 0) { + if (curBlock == 8) { + if ((ret = __osPfsGetNextPage(pfs, &bank, &inode, &curPage)) != 0) { + return ret; + } + curBlock = 0; + } + if (pfs->activebank != curPage.inode_t.bank && (ret = __osPfsSelectBank(pfs, curPage.inode_t.bank)) != 0) { + return ret; + } + + blockno = curPage.inode_t.page * PFS_ONE_PAGE + curBlock; + if (flag == PFS_READ) { + ret = __osContRamRead(pfs->queue, pfs->channel, blockno, buffer); + } else { + ret = __osContRamWrite(pfs->queue, pfs->channel, blockno, buffer, 0); + } + if (ret != 0) { + return ret; + } + + buffer += BLOCKSIZE; + curBlock++; + blockSize--; + } + + if (flag == PFS_WRITE && !(dir.status & PFS_WRITTEN)) { + dir.status |= PFS_WRITTEN; + if (pfs->activebank != 0 && (ret = __osPfsSelectBank(pfs, 0)) != 0) { + return ret; + } + if ((ret = __osContRamWrite(pfs->queue, pfs->channel, pfs->dir_table + fileNo, (u8*)&dir, 0)) != 0) { + return ret; + } + } + + return __osPfsGetStatus(pfs->queue, pfs->channel); +} diff --git a/soh/src/libultra/io/pfsselectbank.c b/soh/src/libultra/io/pfsselectbank.c new file mode 100644 index 000000000..d257e0491 --- /dev/null +++ b/soh/src/libultra/io/pfsselectbank.c @@ -0,0 +1,18 @@ +#include "ultra64/pfs.h" +#include "global.h" + +s32 __osPfsSelectBank(OSPfs* pfs, u8 bank) { + u8 temp[BLOCKSIZE]; + s32 i; + s32 ret = 0; + + for (i = 0; i < BLOCKSIZE; i++) { + temp[i] = bank; + } + + ret = __osContRamWrite(pfs->queue, pfs->channel, 0x8000 / BLOCKSIZE, temp, 0); + if (ret == 0) { + pfs->activebank = bank; + } + return ret; +} diff --git a/soh/src/libultra/io/piacs.c b/soh/src/libultra/io/piacs.c new file mode 100644 index 000000000..681e5f0ae --- /dev/null +++ b/soh/src/libultra/io/piacs.c @@ -0,0 +1,26 @@ +#include "global.h" + +u32 __osPiAccessQueueEnabled = 0; + +OSMesg piAccessBuf; +OSMesgQueue __osPiAccessQueue; + +void __osPiCreateAccessQueue(void) { + __osPiAccessQueueEnabled = 1; + osCreateMesgQueue(&__osPiAccessQueue, &piAccessBuf, 1); + osSendMesg(&__osPiAccessQueue, NULL, OS_MESG_NOBLOCK); +} + +void __osPiGetAccess(void) { + OSMesg mesg; + + if (!__osPiAccessQueueEnabled) { + __osPiCreateAccessQueue(); + } + + osRecvMesg(&__osPiAccessQueue, &mesg, OS_MESG_BLOCK); +} + +void __osPiRelAccess(void) { + osSendMesg(&__osPiAccessQueue, NULL, OS_MESG_NOBLOCK); +} diff --git a/soh/src/libultra/io/pigetcmdq.c b/soh/src/libultra/io/pigetcmdq.c new file mode 100644 index 000000000..f0e32ae20 --- /dev/null +++ b/soh/src/libultra/io/pigetcmdq.c @@ -0,0 +1,10 @@ +#include "global.h" +#include "ultra64/internal.h" + +OSMesgQueue* osPiGetCmdQueue(void) { + if (!__osPiDevMgr.initialized) { + return NULL; + } + + return __osPiDevMgr.cmdQueue; +} diff --git a/soh/src/libultra/io/pimgr.c b/soh/src/libultra/io/pimgr.c new file mode 100644 index 000000000..32c36e695 --- /dev/null +++ b/soh/src/libultra/io/pimgr.c @@ -0,0 +1,58 @@ +#include "global.h" +#include "ultra64/internal.h" + +OSMgrArgs __osPiDevMgr = { 0 }; + +OSPiHandle __Dom1SpeedParam; +OSPiHandle __Dom2SpeedParam; +OSThread piThread; +u8 piStackThread[0x1000]; +OSMesgQueue piEventQueue; +OSMesg piEventBuf[2]; +OSThread __osThreadSave; + +OSPiHandle* __osPiTable = NULL; +OSPiHandle* __osCurrentHandle[] = { + &__Dom1SpeedParam, + &__Dom2SpeedParam, +}; + +void osCreatePiManager(OSPri pri, OSMesgQueue* cmdQ, OSMesg* cmdBuf, s32 cmdMsgCnt) { + u32 prevInt; + OSPri newPri; + OSPri currentPri; + + if (!__osPiDevMgr.initialized) { + osCreateMesgQueue(cmdQ, cmdBuf, cmdMsgCnt); + osCreateMesgQueue(&piEventQueue, piEventBuf, 1); + if (!__osPiAccessQueueEnabled) { + __osPiCreateAccessQueue(); + } + + osSetEventMesg(OS_EVENT_PI, &piEventQueue, (OSMesg)0x22222222); + newPri = -1; + currentPri = osGetThreadPri(NULL); + if (currentPri < pri) { + newPri = currentPri; + osSetThreadPri(NULL, pri); + } + prevInt = __osDisableInt(); + + __osPiDevMgr.initialized = true; + __osPiDevMgr.cmdQueue = cmdQ; + __osPiDevMgr.mgrThread = &piThread; + __osPiDevMgr.eventQueue = &piEventQueue; + __osPiDevMgr.acccessQueue = &__osPiAccessQueue; + __osPiDevMgr.piDmaCallback = __osPiRawStartDma; + __osPiDevMgr.epiDmaCallback = __osEPiRawStartDma; + + osCreateThread(&piThread, 0, __osDevMgrMain, (void*)&__osPiDevMgr, piStackThread + sizeof(piStackThread), pri); + osStartThread(&piThread); + + __osRestoreInt(prevInt); + + if (newPri != -1) { + osSetThreadPri(NULL, newPri); + } + } +} diff --git a/soh/src/libultra/io/pirawdma.c b/soh/src/libultra/io/pirawdma.c new file mode 100644 index 000000000..b642b508f --- /dev/null +++ b/soh/src/libultra/io/pirawdma.c @@ -0,0 +1,26 @@ +#include "global.h" + +s32 __osPiRawStartDma(s32 dir, u32 cartAddr, void* dramAddr, size_t size) { + register s32 status = HW_REG(PI_STATUS_REG, u32); + + while (status & (PI_STATUS_BUSY | PI_STATUS_IOBUSY)) { + status = HW_REG(PI_STATUS_REG, u32); + } + + HW_REG(PI_DRAM_ADDR_REG, void*) = (void*)osVirtualToPhysical(dramAddr); + + HW_REG(PI_CART_ADDR_REG, void*) = (void*)((osRomBase | cartAddr) & 0x1FFFFFFF); + + switch (dir) { + case OS_READ: + HW_REG(PI_WR_LEN_REG, u32) = size - 1; + break; + case OS_WRITE: + HW_REG(PI_RD_LEN_REG, u32) = size - 1; + break; + default: + return -1; + break; + } + return 0; +} diff --git a/soh/src/libultra/io/si.c b/soh/src/libultra/io/si.c new file mode 100644 index 000000000..789815312 --- /dev/null +++ b/soh/src/libultra/io/si.c @@ -0,0 +1,11 @@ +#include "global.h" + +s32 __osSiDeviceBusy(void) { + register u32 status = HW_REG(SI_STATUS_REG, u32); + + if (status & (SI_STATUS_DMA_BUSY | SI_STATUS_IO_READ_BUSY)) { + return true; + } else { + return false; + } +} diff --git a/soh/src/libultra/io/siacs.c b/soh/src/libultra/io/siacs.c new file mode 100644 index 000000000..90dbe7e5d --- /dev/null +++ b/soh/src/libultra/io/siacs.c @@ -0,0 +1,24 @@ +#include "global.h" + +OSMesg osSiMesgBuff[SIAccessQueueSize]; +OSMesgQueue gOSSiMessageQueue; +u32 gOSSiAccessQueueCreated = 0; + +void __osSiCreateAccessQueue(void) { + gOSSiAccessQueueCreated = 1; + osCreateMesgQueue(&gOSSiMessageQueue, &osSiMesgBuff[0], SIAccessQueueSize - 1); + osSendMesg(&gOSSiMessageQueue, NULL, OS_MESG_NOBLOCK); +} + +void __osSiGetAccess(void) { + OSMesg mesg; + + if (!gOSSiAccessQueueCreated) { + __osSiCreateAccessQueue(); + } + osRecvMesg(&gOSSiMessageQueue, &mesg, OS_MESG_BLOCK); +} + +void __osSiRelAccess(void) { + osSendMesg(&gOSSiMessageQueue, NULL, OS_MESG_NOBLOCK); +} diff --git a/soh/src/libultra/io/sirawdma.c b/soh/src/libultra/io/sirawdma.c new file mode 100644 index 000000000..00b80b4b0 --- /dev/null +++ b/soh/src/libultra/io/sirawdma.c @@ -0,0 +1,20 @@ +#include "global.h" + +s32 __osSiRawStartDma(s32 dir, void* addr) { + if (HW_REG(SI_STATUS_REG, u32) & (SI_STATUS_DMA_BUSY | SI_STATUS_IO_READ_BUSY)) { + return -1; + } + if (dir == OS_WRITE) { + osWritebackDCache(addr, 0x40); + } + HW_REG(SI_DRAM_ADDR_REG, void*) = (void*)osVirtualToPhysical(addr); + if (dir == OS_READ) { + HW_REG(SI_PIF_ADDR_RD64B_REG, void*) = (void*)PIF_RAM_START; + } else { + HW_REG(SI_PIF_ADDR_WR64B_REG, void*) = (void*)PIF_RAM_START; + } + if (dir == OS_READ) { + osInvalDCache(addr, 0x40); + } + return 0; +} diff --git a/soh/src/libultra/io/sirawread.c b/soh/src/libultra/io/sirawread.c new file mode 100644 index 000000000..898d26fd7 --- /dev/null +++ b/soh/src/libultra/io/sirawread.c @@ -0,0 +1,9 @@ +#include "global.h" + +s32 __osSiRawReadIo(void* devAddr, u32* dst) { + if (__osSiDeviceBusy()) { + return -1; + } + *dst = HW_REG((uintptr_t)devAddr, u32); + return 0; +} diff --git a/soh/src/libultra/io/sirawwrite.c b/soh/src/libultra/io/sirawwrite.c new file mode 100644 index 000000000..a1935c2e2 --- /dev/null +++ b/soh/src/libultra/io/sirawwrite.c @@ -0,0 +1,9 @@ +#include "global.h" + +s32 __osSiRawWriteIo(void* devAddr, u32 val) { + if (__osSiDeviceBusy()) { + return -1; + } + HW_REG((u32)devAddr, u32) = val; + return 0; +} diff --git a/soh/src/libultra/io/sp.c b/soh/src/libultra/io/sp.c new file mode 100644 index 000000000..67ccd8939 --- /dev/null +++ b/soh/src/libultra/io/sp.c @@ -0,0 +1,10 @@ +#include "global.h" + +u32 __osSpDeviceBusy(void) { + register u32 status = HW_REG(SP_STATUS_REG, u32); + + if (status & (SP_STATUS_DMA_BUSY | SP_STATUS_DMA_FULL | SP_STATUS_IO_FULL)) { + return 1; + } + return 0; +} diff --git a/soh/src/libultra/io/spgetstat.c b/soh/src/libultra/io/spgetstat.c new file mode 100644 index 000000000..99a4ad0a1 --- /dev/null +++ b/soh/src/libultra/io/spgetstat.c @@ -0,0 +1,5 @@ +#include "global.h" + +u32 __osSpGetStatus(void) { + return HW_REG(SP_STATUS_REG, u32); +} diff --git a/soh/src/libultra/io/sprawdma.c b/soh/src/libultra/io/sprawdma.c new file mode 100644 index 000000000..f5df77561 --- /dev/null +++ b/soh/src/libultra/io/sprawdma.c @@ -0,0 +1,15 @@ +#include "global.h" + +s32 __osSpRawStartDma(s32 direction, void* devAddr, void* dramAddr, size_t size) { + if (__osSpDeviceBusy()) { + return -1; + } + HW_REG(SP_MEM_ADDR_REG, u32) = (u32)devAddr; + HW_REG(SP_DRAM_ADDR_REG, u32) = osVirtualToPhysical(dramAddr); + if (direction == OS_READ) { + HW_REG(SP_WR_LEN_REG, u32) = size - 1; + } else { + HW_REG(SP_RD_LEN_REG, u32) = size - 1; + } + return 0; +} diff --git a/soh/src/libultra/io/spsetpc.c b/soh/src/libultra/io/spsetpc.c new file mode 100644 index 000000000..2c3cc32ba --- /dev/null +++ b/soh/src/libultra/io/spsetpc.c @@ -0,0 +1,13 @@ +#include "global.h" + +s32 __osSpSetPc(void* pc) { + register u32 spStatus = HW_REG(SP_STATUS_REG, u32); + + if (!(spStatus & SP_STATUS_HALT)) { + return -1; + } else { + HW_REG(SP_PC_REG, void*) = pc; + } + + return 0; +} diff --git a/soh/src/libultra/io/spsetstat.c b/soh/src/libultra/io/spsetstat.c new file mode 100644 index 000000000..b2f97a3ff --- /dev/null +++ b/soh/src/libultra/io/spsetstat.c @@ -0,0 +1,5 @@ +#include "global.h" + +void __osSpSetStatus(u32 status) { + HW_REG(SP_STATUS_REG, u32) = status; +} diff --git a/soh/src/libultra/io/sptask.c b/soh/src/libultra/io/sptask.c new file mode 100644 index 000000000..84a9624fb --- /dev/null +++ b/soh/src/libultra/io/sptask.c @@ -0,0 +1,60 @@ +#include "global.h" + +#define _osVirtualToPhysical(ptr) \ + if (ptr != NULL) { \ + ptr = (void*)osVirtualToPhysical(ptr); \ + } + +static OSTask sTmpTask; + +OSTask* _VirtualToPhysicalTask(OSTask* intp) { + OSTask* tp = &sTmpTask; + + bcopy(intp, tp, sizeof(OSTask)); + + _osVirtualToPhysical(tp->t.ucode); + _osVirtualToPhysical(tp->t.ucode_data); + _osVirtualToPhysical(tp->t.dram_stack); + _osVirtualToPhysical(tp->t.output_buff); + _osVirtualToPhysical(tp->t.output_buff_size); + _osVirtualToPhysical(tp->t.data_ptr); + _osVirtualToPhysical(tp->t.yield_data_ptr); + + return tp; +} + +void osSpTaskLoad(OSTask* intp) { + OSTask* tp = _VirtualToPhysicalTask(intp); + + if (tp->t.flags & OS_TASK_YIELDED) { + tp->t.ucode_data = tp->t.yield_data_ptr; + tp->t.ucode_data_size = tp->t.yield_data_size; + intp->t.flags &= ~OS_TASK_YIELDED; + + if (tp->t.flags & OS_TASK_LOADABLE) { + tp->t.ucode = HW_REG((u32)intp->t.yield_data_ptr + OS_YIELD_DATA_SIZE - 4, u32); + } + } + osWritebackDCache(tp, sizeof(OSTask)); + __osSpSetStatus(SP_CLR_SIG0 | SP_CLR_SIG1 | SP_CLR_SIG2 | SP_SET_INTR_BREAK); + + while (__osSpSetPc((void*)SP_IMEM_START) == -1) { + ; + } + while (__osSpRawStartDma(1, (void*)(SP_IMEM_START - sizeof(*tp)), tp, sizeof(OSTask)) == -1) { + ; + } + while (__osSpDeviceBusy()) { + ; + } + while (__osSpRawStartDma(1, (void*)SP_IMEM_START, tp->t.ucode_boot, tp->t.ucode_boot_size) == -1) { + ; + } +} + +void osSpTaskStartGo(OSTask* tp) { + while (__osSpDeviceBusy()) { + ; + } + __osSpSetStatus(SP_SET_INTR_BREAK | SP_CLR_SSTEP | SP_CLR_BROKE | SP_CLR_HALT); +} diff --git a/soh/src/libultra/io/sptaskyield.c b/soh/src/libultra/io/sptaskyield.c new file mode 100644 index 000000000..a78b7f9ad --- /dev/null +++ b/soh/src/libultra/io/sptaskyield.c @@ -0,0 +1,5 @@ +#include "global.h" + +void osSpTaskYield(void) { + __osSpSetStatus(SP_STATUS_SIG3); +} diff --git a/soh/src/libultra/io/sptaskyielded.c b/soh/src/libultra/io/sptaskyielded.c new file mode 100644 index 000000000..4fec2a6d5 --- /dev/null +++ b/soh/src/libultra/io/sptaskyielded.c @@ -0,0 +1,19 @@ +#include "global.h" + +u32 osSpTaskYielded(OSTask* task) { + u32 ret; + u32 status = __osSpGetStatus(); + + if (status & SP_STATUS_YIELDED) { + ret = OS_TASK_YIELDED; + } else { + ret = 0; + } + + if (status & SP_STATUS_YIELD) { + task->t.flags |= ret; + task->t.flags &= ~OS_TASK_DP_WAIT; + } + + return ret; +} diff --git a/soh/src/libultra/io/vi.c b/soh/src/libultra/io/vi.c new file mode 100644 index 000000000..12905742e --- /dev/null +++ b/soh/src/libultra/io/vi.c @@ -0,0 +1,34 @@ +#include "global.h" + +OSViContext vi[2] = { 0 }; +OSViContext* __osViCurr = &vi[0]; +OSViContext* __osViNext = &vi[1]; + +void __osViInit(void) { + bzero(vi, sizeof(vi)); + __osViCurr = &vi[0]; + __osViNext = &vi[1]; + + __osViNext->retraceCount = 1; + __osViCurr->retraceCount = 1; + __osViNext->buffer = (void*)0x80000000; + __osViCurr->buffer = (void*)0x80000000; + + if (osTvType == OS_TV_PAL) { + __osViNext->modep = &osViModePalLan1; + } else if (osTvType == OS_TV_MPAL) { + __osViNext->modep = &osViModeMpalLan1; + } else { + __osViNext->modep = &osViModeNtscLan1; + } + + __osViNext->state = 0x20; + __osViNext->features = __osViNext->modep->comRegs.ctrl; + + while (HW_REG(VI_CURRENT_REG, u32) > 10) { + ; + } + + HW_REG(VI_CONTROL_REG, u32) = 0; + __osViSwapContext(); +} diff --git a/soh/src/libultra/io/viblack.c b/soh/src/libultra/io/viblack.c new file mode 100644 index 000000000..657168866 --- /dev/null +++ b/soh/src/libultra/io/viblack.c @@ -0,0 +1,13 @@ +#include "global.h" + +// TODO: name magic constants +void osViBlack(u8 active) { + register u32 prevInt = __osDisableInt(); + + if (active) { + __osViNext->state |= 0x20; + } else { + __osViNext->state &= ~0x20; + } + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/viextend.c b/soh/src/libultra/io/viextend.c new file mode 100644 index 000000000..4808b0e29 --- /dev/null +++ b/soh/src/libultra/io/viextend.c @@ -0,0 +1,5 @@ +#include "global.h" + +void osViExtendVStart(u32 arg0) { + __additional_scanline = arg0; +} diff --git a/soh/src/libultra/io/vigetcurrcontext.c b/soh/src/libultra/io/vigetcurrcontext.c new file mode 100644 index 000000000..1e3c6ffa3 --- /dev/null +++ b/soh/src/libultra/io/vigetcurrcontext.c @@ -0,0 +1,5 @@ +#include "global.h" + +OSViContext* __osViGetCurrentContext(void) { + return __osViCurr; +} diff --git a/soh/src/libultra/io/vigetcurrframebuf.c b/soh/src/libultra/io/vigetcurrframebuf.c new file mode 100644 index 000000000..f0e4c7443 --- /dev/null +++ b/soh/src/libultra/io/vigetcurrframebuf.c @@ -0,0 +1,10 @@ +#include "global.h" + +u32* osViGetCurrentFramebuffer(void) { + register u32 prevInt = __osDisableInt(); + u32* var1 = __osViCurr->buffer; + + __osRestoreInt(prevInt); + + return var1; +} diff --git a/soh/src/libultra/io/vigetnextframebuf.c b/soh/src/libultra/io/vigetnextframebuf.c new file mode 100644 index 000000000..a15a2db21 --- /dev/null +++ b/soh/src/libultra/io/vigetnextframebuf.c @@ -0,0 +1,9 @@ +#include "global.h" + +void* osViGetNextFramebuffer(void) { + u32 prevInt = __osDisableInt(); + void* buff = __osViNext->buffer; + + __osRestoreInt(prevInt); + return buff; +} diff --git a/soh/src/libultra/io/vimgr.c b/soh/src/libultra/io/vimgr.c new file mode 100644 index 000000000..51a578b8c --- /dev/null +++ b/soh/src/libultra/io/vimgr.c @@ -0,0 +1,106 @@ +#include "global.h" +#include "ultra64/internal.h" + +OSThread viThread; +u8 viThreadStack[0x1000]; +OSMesgQueue viEventQueue; +OSMesg viEventBuf[6]; +OSIoMesg viRetraceMsg; +OSIoMesg viCounterMsg; +OSMgrArgs __osViDevMgr = { 0 }; +u32 __additional_scanline = 0; + +void viMgrMain(void*); + +void osCreateViManager(OSPri pri) { + u32 prevInt; + OSPri newPri; + OSPri currentPri; + + if (!__osViDevMgr.initialized) { + __osTimerServicesInit(); + __additional_scanline = 0; + osCreateMesgQueue(&viEventQueue, viEventBuf, 5); + viRetraceMsg.hdr.type = OS_MESG_TYPE_VRETRACE; + viRetraceMsg.hdr.pri = OS_MESG_PRI_NORMAL; + viRetraceMsg.hdr.retQueue = NULL; + viCounterMsg.hdr.type = OS_MESG_TYPE_COUNTER; + viCounterMsg.hdr.pri = OS_MESG_PRI_NORMAL; + viCounterMsg.hdr.retQueue = NULL; + osSetEventMesg(OS_EVENT_VI, &viEventQueue, &viRetraceMsg); + osSetEventMesg(OS_EVENT_COUNTER, &viEventQueue, &viCounterMsg); + newPri = -1; + currentPri = osGetThreadPri(NULL); + if (currentPri < pri) { + newPri = currentPri; + osSetThreadPri(NULL, pri); + } + + prevInt = __osDisableInt(); + __osViDevMgr.initialized = true; + __osViDevMgr.mgrThread = &viThread; + __osViDevMgr.cmdQueue = &viEventQueue; + __osViDevMgr.eventQueue = &viEventQueue; + __osViDevMgr.acccessQueue = NULL; + __osViDevMgr.piDmaCallback = NULL; + __osViDevMgr.epiDmaCallback = NULL; + + osCreateThread(&viThread, 0, &viMgrMain, &__osViDevMgr, viThreadStack + sizeof(viThreadStack), pri); + __osViInit(); + osStartThread(&viThread); + __osRestoreInt(prevInt); + if (newPri != -1) { + osSetThreadPri(NULL, newPri); + } + } +} + +void viMgrMain(void* vargs) { + static u16 viRetrace; + OSMgrArgs* args; + u32 addTime; + OSIoMesg* mesg = NULL; + u32 temp = 0; // always 0 + + viRetrace = __osViGetCurrentContext()->retraceCount; + if (viRetrace == 0) { + viRetrace = 1; + } + + args = (OSMgrArgs*)vargs; + + while (true) { + osRecvMesg(args->eventQueue, (OSMesg)&mesg, OS_MESG_BLOCK); + switch (mesg->hdr.type) { + case OS_MESG_TYPE_VRETRACE: + __osViSwapContext(); + viRetrace--; + if (!viRetrace) { + OSViContext* ctx = __osViGetCurrentContext(); + if (ctx->mq) { + osSendMesg(ctx->mq, ctx->msg, OS_MESG_NOBLOCK); + } + viRetrace = ctx->retraceCount; + } + + __osViIntrCount++; + + // block optimized out since temp is always 0, + // but it changes register allocation and ordering for __osCurrentTime + if (temp != 0) { + addTime = osGetCount(); + __osCurrentTime = addTime; + temp = 0; + } + + addTime = __osBaseCounter; + __osBaseCounter = osGetCount(); + addTime = __osBaseCounter - addTime; + __osCurrentTime = __osCurrentTime + addTime; + break; + case OS_MESG_TYPE_COUNTER: + __osTimerInterrupt(); + break; + } + } +} diff --git a/soh/src/libultra/io/vimodefpallan1.c b/soh/src/libultra/io/vimodefpallan1.c new file mode 100644 index 000000000..f3e7421ba --- /dev/null +++ b/soh/src/libultra/io/vimodefpallan1.c @@ -0,0 +1,32 @@ +#include "global.h" + +OSViMode osViModeFpalLan1 = { + 0x2C, // type + { + // comRegs + 0x311E, // ctrl + SCREEN_WIDTH, // width + 0x4541E3A, // burst + 0x271, // vSync + 0x170C69, // hSync + 0xC6F0C6D, // leap + 0x800300, // hStart + 0x200, // xScale + 0 // vCurrent + }, + { // fldRegs + { + 0x280, // origin + 0x400, // yScale + 0x2F0269, // vStart + 0x9026B, // vBurst + 2, // vIntr + }, + { + 0x280, // origin + 0x400, // yScale + 0x2F0269, // vStart + 0x9026B, // vBurst + 2 // vIntr + } }, +}; diff --git a/soh/src/libultra/io/vimodempallan1.c b/soh/src/libultra/io/vimodempallan1.c new file mode 100644 index 000000000..b872b897e --- /dev/null +++ b/soh/src/libultra/io/vimodempallan1.c @@ -0,0 +1,34 @@ +#include "global.h" + +OSViMode osViModeMpalLan1 = { + 0x1E, // type + { + // comRegs + 0x311E, // ctrl + SCREEN_WIDTH, // width + 0x4651E39, // burst + 0x20D, // vSync + 0x40C11, // hSync + 0xC190C1A, // leap + 0x6C02EC, // hStart + 0x200, // xScale + 0, // vCurrent + }, + { // fldRegs + { + // [0] + 0x280, // origin + 0x400, // yScale + 0x2501FF, // vStart + 0xE0204, // vBurst + 2, // vIntr + }, + { + // [1] + 0x280, // origin + 0x400, // yScale + 0x2501FF, // vStart + 0xE0204, // vBurst + 2, // vIntr + } }, +}; diff --git a/soh/src/libultra/io/vimodentsclan1.c b/soh/src/libultra/io/vimodentsclan1.c new file mode 100644 index 000000000..80330f21e --- /dev/null +++ b/soh/src/libultra/io/vimodentsclan1.c @@ -0,0 +1,34 @@ +#include "global.h" + +OSViMode osViModeNtscLan1 = { + 2, // type + { + // comRegs + 0x311E, // ctrl + SCREEN_WIDTH, // width + 0x3E52239, // burst + 0x20D, // vSync + 0xC15, // hSync + 0xC150C15, // leap + 0x6C02EC, // hStart + 0x200, // xScale + 0, // vCurrent + }, + { // fldRegs + { + // [0] + 0x280, // origin + 0x400, // yScale + 0x2501FF, // vStart + 0xE0204, // vBurst + 2, // vIntr + }, + { + // [1] + 0x280, // origin + 0x400, // yScale + 0x2501FF, // vStart + 0xE0204, // vBurst + 2, // vIntr + } }, +}; diff --git a/soh/src/libultra/io/vimodepallan1.c b/soh/src/libultra/io/vimodepallan1.c new file mode 100644 index 000000000..2f69c8197 --- /dev/null +++ b/soh/src/libultra/io/vimodepallan1.c @@ -0,0 +1,32 @@ +#include "global.h" + +OSViMode osViModePalLan1 = { + 0x10, // type + { + // comRegs + 0x311E, // ctrl + SCREEN_WIDTH, // width + 0x4541E3A, // burst + 0x271, // vSync + 0x170C69, // hSync + 0xC6F0C6D, // leap + 0x800300, // hStart + 0x200, // xScale + 0 // vCurrent + }, + { // fldRegs + { + 0x280, // origin + 0x400, // yScale + 0x5F0239, // vStart + 0x9026B, // vBurst + 2, // vIntr + }, + { + 0x280, // origin + 0x400, // yScale + 0x5F0239, // vStart + 0x9026B, // vBurst + 2 // vIntr + } }, +}; diff --git a/soh/src/libultra/io/visetevent.c b/soh/src/libultra/io/visetevent.c new file mode 100644 index 000000000..0239c1f7c --- /dev/null +++ b/soh/src/libultra/io/visetevent.c @@ -0,0 +1,13 @@ +#include "global.h" + +extern OSViContext* __osViNext; + +void osViSetEvent(OSMesgQueue* mq, OSMesg msg, u32 retraceCount) { + register u32 prevInt = __osDisableInt(); + + __osViNext->mq = mq; + __osViNext->msg = msg; + __osViNext->retraceCount = retraceCount; + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/visetmode.c b/soh/src/libultra/io/visetmode.c new file mode 100644 index 000000000..621b7ca4a --- /dev/null +++ b/soh/src/libultra/io/visetmode.c @@ -0,0 +1,11 @@ +#include "global.h" + +void osViSetMode(OSViMode* mode) { + register u32 prevInt = __osDisableInt(); + + __osViNext->modep = mode; + __osViNext->state = 1; + __osViNext->features = __osViNext->modep->comRegs.ctrl; + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/visetspecial.c b/soh/src/libultra/io/visetspecial.c new file mode 100644 index 000000000..e5bd21029 --- /dev/null +++ b/soh/src/libultra/io/visetspecial.c @@ -0,0 +1,38 @@ +#include "global.h" + +void osViSetSpecialFeatures(u32 func) { + register u32 prevInt = __osDisableInt(); + + if (func & OS_VI_GAMMA_ON) { + __osViNext->features |= OS_VI_GAMMA; + } + if (func & OS_VI_GAMMA_OFF) { + __osViNext->features &= ~OS_VI_GAMMA; + } + if (func & OS_VI_GAMMA_DITHER_ON) { + __osViNext->features |= OS_VI_GAMMA_DITHER; + } + if (func & OS_VI_GAMMA_DITHER_OFF) { + + __osViNext->features &= ~OS_VI_GAMMA_DITHER; + } + if (func & OS_VI_DIVOT_ON) { + + __osViNext->features |= OS_VI_DIVOT; + } + if (func & OS_VI_DIVOT_OFF) { + + __osViNext->features &= ~OS_VI_DIVOT; + } + if (func & OS_VI_DITHER_FILTER_ON) { + __osViNext->features |= OS_VI_DITHER_FILTER; + __osViNext->features &= ~(OS_VI_UNK200 | OS_VI_UNK100); + } + if (func & OS_VI_DITHER_FILTER_OFF) { + __osViNext->features &= ~OS_VI_DITHER_FILTER; + __osViNext->features |= __osViNext->modep->comRegs.ctrl & (OS_VI_UNK200 | OS_VI_UNK100); + } + __osViNext->state |= 8; + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/visetxscale.c b/soh/src/libultra/io/visetxscale.c new file mode 100644 index 000000000..aeeb5e784 --- /dev/null +++ b/soh/src/libultra/io/visetxscale.c @@ -0,0 +1,14 @@ +#include "global.h" + +void osViSetXScale(f32 value) { + register u32 nomValue; + register u32 prevInt = __osDisableInt(); + + __osViNext->x.factor = value; + __osViNext->state |= 0x2; + + nomValue = __osViNext->modep->comRegs.xScale & 0xFFF; + __osViNext->x.scale = (u32)(__osViNext->x.factor * nomValue) & 0xFFF; + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/visetyscale.c b/soh/src/libultra/io/visetyscale.c new file mode 100644 index 000000000..0d7a5f968 --- /dev/null +++ b/soh/src/libultra/io/visetyscale.c @@ -0,0 +1,10 @@ +#include "global.h" + +void osViSetYScale(f32 scale) { + register u32 prevInt = __osDisableInt(); + + __osViNext->y.factor = scale; + __osViNext->state |= 4; + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/viswapbuf.c b/soh/src/libultra/io/viswapbuf.c new file mode 100644 index 000000000..b7ab9f917 --- /dev/null +++ b/soh/src/libultra/io/viswapbuf.c @@ -0,0 +1,10 @@ +#include "global.h" + +void osViSwapBuffer(void* vaddr) { + u32 prevInt = __osDisableInt(); + + __osViNext->buffer = vaddr; + __osViNext->state |= 0x10; // TODO: figure out what this flag means + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/io/viswapcontext.c b/soh/src/libultra/io/viswapcontext.c new file mode 100644 index 000000000..d82d5cce6 --- /dev/null +++ b/soh/src/libultra/io/viswapcontext.c @@ -0,0 +1,62 @@ +#include "global.h" + +void __osViSwapContext(void) { + register OSViMode* viMode; + register OSViContext* viNext; + u32 origin; + u32 hStart; + u32 vstart; + u32 sp34; + u32 field; + register u32 s2; + + field = 0; + viNext = __osViNext; + viMode = viNext->modep; + field = HW_REG(VI_V_CURRENT_LINE_REG, u32) & 1; + s2 = osVirtualToPhysical(viNext->buffer); + origin = (viMode->fldRegs[field].origin) + s2; + if (viNext->state & 2) { + viNext->x.scale |= viMode->comRegs.xScale & ~0xFFF; + } else { + viNext->x.scale = viMode->comRegs.xScale; + } + if (viNext->state & 4) { + sp34 = (u32)(viMode->fldRegs[field].yScale & 0xFFF); + viNext->y.scale = viNext->y.factor * sp34; + viNext->y.scale |= viMode->fldRegs[field].yScale & ~0xFFF; + } else { + viNext->y.scale = viMode->fldRegs[field].yScale; + } + + vstart = (viMode->fldRegs[field].vStart - (__additional_scanline << 0x10)) + __additional_scanline; + hStart = viMode->comRegs.hStart; + + if (viNext->state & 0x20) { + hStart = 0; + } + if (viNext->state & 0x40) { + viNext->y.scale = 0; + origin = osVirtualToPhysical(viNext->buffer); + } + if (viNext->state & 0x80) { + viNext->y.scale = (viNext->y.offset << 0x10) & 0x3FF0000; + origin = osVirtualToPhysical(viNext->buffer); + } + HW_REG(VI_ORIGIN_REG, u32) = origin; + HW_REG(VI_WIDTH_REG, u32) = viMode->comRegs.width; + HW_REG(VI_BURST_REG, u32) = viMode->comRegs.burst; + HW_REG(VI_V_SYNC_REG, u32) = viMode->comRegs.vSync; + HW_REG(VI_H_SYNC_REG, u32) = viMode->comRegs.hSync; + HW_REG(VI_LEAP_REG, u32) = viMode->comRegs.leap; + HW_REG(VI_H_START_REG, u32) = hStart; + HW_REG(VI_V_START_REG, u32) = vstart; + HW_REG(VI_V_BURST_REG, u32) = viMode->fldRegs[field].vBurst; + HW_REG(VI_INTR_REG, u32) = viMode->fldRegs[field].vIntr; + HW_REG(VI_X_SCALE_REG, u32) = viNext->x.scale; + HW_REG(VI_Y_SCALE_REG, u32) = viNext->y.scale; + HW_REG(VI_CONTROL_REG, u32) = viNext->features; + __osViNext = __osViCurr; + __osViCurr = viNext; + *__osViNext = *__osViCurr; +} diff --git a/soh/src/libultra/libc/absf.c b/soh/src/libultra/libc/absf.c new file mode 100644 index 000000000..ebdbb72c0 --- /dev/null +++ b/soh/src/libultra/libc/absf.c @@ -0,0 +1,5 @@ +#include "global.h" + +f32 absf(f32 a) { + return fabsf(a); +} diff --git a/soh/src/libultra/libc/ldiv.c b/soh/src/libultra/libc/ldiv.c new file mode 100644 index 000000000..8c53d3d4c --- /dev/null +++ b/soh/src/libultra/libc/ldiv.c @@ -0,0 +1,27 @@ +#include "global.h" + +ldiv_t ldiv(s32 num, s32 denom) { + ldiv_t ret; + + ret.quot = num / denom; + ret.rem = num - denom * ret.quot; + if (ret.quot < 0 && ret.rem > 0) { + ret.quot++; + ret.rem -= denom; + } + + return ret; +} + +lldiv_t lldiv(s64 num, s64 denom) { + lldiv_t ret; + + ret.quot = num / denom; + ret.rem = num - denom * ret.quot; + if (ret.quot < 0 && ret.rem > 0) { + ret.quot++; + ret.rem -= denom; + } + + return ret; +} diff --git a/soh/src/libultra/libc/ll.c b/soh/src/libultra/libc/ll.c new file mode 100644 index 000000000..9d87204d1 --- /dev/null +++ b/soh/src/libultra/libc/ll.c @@ -0,0 +1,47 @@ +#include "global.h" + +s64 __ull_rshift(u64 l, s64 r) { + return l >> r; +} + +u64 __ull_rem(u64 l, u64 r) { + return l % r; +} + +u64 __ull_div(u64 l, u64 r) { + return l / r; +} + +s64 __ll_lshift(s64 l, s64 r) { + return l << r; +} + +s64 __ll_rem(s64 l, u64 r) { + return l % r; +} + +s64 __ll_div(s64 l, s64 r) { + return l / r; +} + +s64 __ll_mul(s64 l, s64 r) { + return l * r; +} + +void __ull_divremi(u64* quotient, u64* remainder, u64 dividend, u16 divisor) { + *quotient = dividend / divisor; + *remainder = dividend % divisor; +} + +s64 __ll_mod(s64 l, s64 r) { + s64 remainder = l % r; + + if (((remainder < 0) && (r > 0)) || ((remainder > 0) && (r < 0))) { + remainder += r; + } + return remainder; +} + +s64 __ll_rshift(s64 l, s64 r) { + return l >> r; +} diff --git a/soh/src/libultra/libc/llcvt.c b/soh/src/libultra/libc/llcvt.c new file mode 100644 index 000000000..c88d6f04f --- /dev/null +++ b/soh/src/libultra/libc/llcvt.c @@ -0,0 +1,33 @@ +#include "global.h" + +s64 __d_to_ll(f64 d) { + return d; +} + +s64 __f_to_ll(f32 f) { + return f; +} + +u64 __d_to_ull(f64 d) { + return d; +} + +u64 __f_to_ull(f32 f) { + return f; +} + +f64 __ll_to_d(s64 l) { + return l; +} + +f32 __ll_to_f(s64 l) { + return l; +} + +f64 __ull_to_d(u64 l) { + return l; +} + +f32 __ull_to_f(u64 l) { + return l; +} diff --git a/soh/src/libultra/libc/sprintf.c b/soh/src/libultra/libc/sprintf.c new file mode 100644 index 000000000..99c616cbc --- /dev/null +++ b/soh/src/libultra/libc/sprintf.c @@ -0,0 +1,28 @@ +//#include "global.h" +// +//void* proutSprintf(void* dst, const char* fmt, u32 size) { +// return (void*)((u32)memcpy(dst, fmt, size) + size); +//} +// +//s32 vsprintf(char* dst, const char* fmt, va_list args) { +// s32 ret = _Printf(proutSprintf, dst, fmt, args); +// if (ret > -1) { +// dst[ret] = 0; +// } +// return ret; +//} +// +//s32 sprintf(char* dst, const char* fmt, ...) { +// s32 ret; +// va_list args; +// va_start(args, fmt); +// +// ret = _Printf(proutSprintf, dst, fmt, args); +// if (ret > -1) { +// dst[ret] = 0; +// } +// +// va_end(args); +// +// return ret; +//} diff --git a/soh/src/libultra/libc/sqrt.c b/soh/src/libultra/libc/sqrt.c new file mode 100644 index 000000000..0e8cc95d5 --- /dev/null +++ b/soh/src/libultra/libc/sqrt.c @@ -0,0 +1,9 @@ +#include "global.h" + +#ifndef __GNUC__ +#define __builtin_sqrt sqrt +#endif + +f64 sqrt(f64 f) { + return __builtin_sqrt(f); +} diff --git a/soh/src/libultra/os/afterprenmi.c b/soh/src/libultra/os/afterprenmi.c new file mode 100644 index 000000000..b59814763 --- /dev/null +++ b/soh/src/libultra/os/afterprenmi.c @@ -0,0 +1,6 @@ +#include "ultra64.h" +#include "global.h" + +s32 osAfterPreNMI(void) { + return __osSpSetPc(0); +} diff --git a/soh/src/libultra/os/createmesgqueue.c b/soh/src/libultra/os/createmesgqueue.c new file mode 100644 index 000000000..87a4031f3 --- /dev/null +++ b/soh/src/libultra/os/createmesgqueue.c @@ -0,0 +1,10 @@ +#include "global.h" + +void osCreateMesgQueue(OSMesgQueue* mq, OSMesg* msg, s32 count) { + mq->mtqueue = (OSThread*)__osThreadTail; + mq->fullqueue = (OSThread*)__osThreadTail; + mq->validCount = 0; + mq->first = 0; + mq->msgCount = count; + mq->msg = msg; +} diff --git a/soh/src/libultra/os/createthread.c b/soh/src/libultra/os/createthread.c new file mode 100644 index 000000000..d0952d246 --- /dev/null +++ b/soh/src/libultra/os/createthread.c @@ -0,0 +1,34 @@ +#include "global.h" + +OSThread* __osThreadTail[2] = { NULL, (OSThread*)-1 }; +OSThread* __osRunQueue = (OSThread*)__osThreadTail; +OSThread* __osActiveQueue = (OSThread*)__osThreadTail; +OSThread* __osRunningThread = NULL; +OSThread* __osFaultedThread = NULL; + +void osCreateThread(OSThread* thread, OSId id, void (*entry)(void*), void* arg, void* sp, OSPri pri) { + register u32 prevInt; + OSIntMask mask; + + thread->id = id; + thread->priority = pri; + thread->next = NULL; + thread->queue = NULL; + thread->context.pc = (u32)entry; + thread->context.a0 = arg; + thread->context.sp = (u64)(s32)sp - 16; + thread->context.ra = __osCleanupThread; + + mask = OS_IM_ALL; + thread->context.sr = (mask & OS_IM_CPU) | 2; + thread->context.rcp = (mask & RCP_IMASK) >> 16; + thread->context.fpcsr = FPCSR_FS | FPCSR_EV; + thread->fp = 0; + thread->state = OS_STATE_STOPPED; + thread->flags = 0; + + prevInt = __osDisableInt(); + thread->tlnext = __osActiveQueue; + __osActiveQueue = thread; + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/dequeuethread.c b/soh/src/libultra/os/dequeuethread.c new file mode 100644 index 000000000..14e9d346d --- /dev/null +++ b/soh/src/libultra/os/dequeuethread.c @@ -0,0 +1,15 @@ +#include "global.h" + +void __osDequeueThread(OSThread** queue, OSThread* thread) { + register OSThread** a2 = queue; + register OSThread* a3 = *a2; + + while (a3 != NULL) { + if (a3 == thread) { + *a2 = thread->next; + return; + } + a2 = &a3->next; + a3 = *a2; + } +} diff --git a/soh/src/libultra/os/destroythread.c b/soh/src/libultra/os/destroythread.c new file mode 100644 index 000000000..6f1ffff13 --- /dev/null +++ b/soh/src/libultra/os/destroythread.c @@ -0,0 +1,34 @@ +#include "global.h" + +void osDestroyThread(OSThread* thread) { + register u32 prevInt = __osDisableInt(); + register OSThread* s1; + register OSThread* s2; + + if (thread == NULL) { + thread = __osRunningThread; + + } else if (thread->state != OS_STATE_STOPPED) { + __osDequeueThread(thread->queue, thread); + } + + if (__osActiveQueue == thread) { + __osActiveQueue = __osActiveQueue->tlnext; + } else { + s1 = __osActiveQueue; + while (s1->priority != -1) { + s2 = s1->tlnext; + if (s2 == thread) { + s1->tlnext = thread->tlnext; + break; + } + s1 = s2; + } + } + + if (thread == __osRunningThread) { + __osDispatchThread(); + } + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/getactivequeue.c b/soh/src/libultra/os/getactivequeue.c new file mode 100644 index 000000000..de55d7f04 --- /dev/null +++ b/soh/src/libultra/os/getactivequeue.c @@ -0,0 +1,5 @@ +#include "global.h" + +OSThread* __osGetActiveQueue(void) { + return __osActiveQueue; +} diff --git a/soh/src/libultra/os/getcurrfaultedthread.c b/soh/src/libultra/os/getcurrfaultedthread.c new file mode 100644 index 000000000..f36799b26 --- /dev/null +++ b/soh/src/libultra/os/getcurrfaultedthread.c @@ -0,0 +1,5 @@ +#include "global.h" + +OSThread* __osGetCurrFaultedThread(void) { + return __osFaultedThread; +} diff --git a/soh/src/libultra/os/gethwintrroutine.c b/soh/src/libultra/os/gethwintrroutine.c new file mode 100644 index 000000000..d7d889780 --- /dev/null +++ b/soh/src/libultra/os/gethwintrroutine.c @@ -0,0 +1,7 @@ +#include "global.h" +#include "ultra64/internal.h" + +void __osGetHWIntrRoutine(OSHWIntr intr, s32 (**callbackOut)(void), void** spOut) { + *callbackOut = __osHwIntTable[intr].callback; + *spOut = __osHwIntTable[intr].sp; +} diff --git a/soh/src/libultra/os/getmemsize.c b/soh/src/libultra/os/getmemsize.c new file mode 100644 index 000000000..452202739 --- /dev/null +++ b/soh/src/libultra/os/getmemsize.c @@ -0,0 +1,31 @@ +#include "global.h" + +#define STEP 0x100000 + +u32 osGetMemSize(void) { + u32* ptr; + size_t size = 0x400000; + u32 data0; + u32 data1; + + while (size < 0x800000) { + ptr = (u32*)(0xA0000000 + size); + + data0 = *ptr; + data1 = ptr[STEP / 4 - 1]; + + *ptr ^= ~0; + ptr[STEP / 4 - 1] ^= ~0; + + if ((*ptr != (data0 ^ ~0)) || (ptr[STEP / 4 - 1] != (data1 ^ ~0))) { + return size; + } + + *ptr = data0; + ptr[STEP / 4 - 1] = data1; + + size += STEP; + } + + return size; +} diff --git a/soh/src/libultra/os/getthreadid.c b/soh/src/libultra/os/getthreadid.c new file mode 100644 index 000000000..792229c9b --- /dev/null +++ b/soh/src/libultra/os/getthreadid.c @@ -0,0 +1,9 @@ +#include "global.h" + +OSId osGetThreadId(OSThread* thread) { + if (thread == NULL) { + thread = __osRunningThread; + } + + return thread->id; +} diff --git a/soh/src/libultra/os/getthreadpri.c b/soh/src/libultra/os/getthreadpri.c new file mode 100644 index 000000000..6c36a7c3b --- /dev/null +++ b/soh/src/libultra/os/getthreadpri.c @@ -0,0 +1,9 @@ +#include "global.h" + +OSPri osGetThreadPri(OSThread* thread) { + if (thread == NULL) { + thread = __osRunningThread; + } + + return thread->priority; +} diff --git a/soh/src/libultra/os/gettime.c b/soh/src/libultra/os/gettime.c new file mode 100644 index 000000000..2ca90b0df --- /dev/null +++ b/soh/src/libultra/os/gettime.c @@ -0,0 +1,15 @@ +#include "global.h" + +OSTime osGetTime(void) { + u32 count; + u32 base; + OSTime t; + register u32 prevInt = __osDisableInt(); + + count = osGetCount(); + base = count - __osBaseCounter; + t = __osCurrentTime; + __osRestoreInt(prevInt); + + return base + t; +} diff --git a/soh/src/libultra/os/initialize.c b/soh/src/libultra/os/initialize.c new file mode 100644 index 000000000..fb6202612 --- /dev/null +++ b/soh/src/libultra/os/initialize.c @@ -0,0 +1,85 @@ +#include "global.h" + +typedef struct { + u32 ins_00; // lui k0, 0x8000 + u32 ins_04; // addiu k0, k0, 0x39E0 + u32 ins_08; // jr k0 ; __osException + u32 ins_0C; // nop +} struct_exceptionPreamble; + +u64 osClockRate = OS_CLOCK_RATE; +s32 osViClock = VI_NTSC_CLOCK; +u32 __osShutdown = 0; +OSHWIntr __OSGlobalIntMask = OS_IM_ALL; + +u32 D_800145C0; + +void __createSpeedParam(void) { + __Dom1SpeedParam.type = DEVICE_TYPE_INIT; + __Dom1SpeedParam.latency = HW_REG(PI_BSD_DOM1_LAT_REG, u32); + __Dom1SpeedParam.pulse = HW_REG(PI_BSD_DOM1_PWD_REG, u32); + __Dom1SpeedParam.pageSize = HW_REG(PI_BSD_DOM1_PGS_REG, u32); + __Dom1SpeedParam.relDuration = HW_REG(PI_BSD_DOM1_RLS_REG, u32); + + __Dom2SpeedParam.type = DEVICE_TYPE_INIT; + __Dom2SpeedParam.latency = HW_REG(PI_BSD_DOM2_LAT_REG, u32); + __Dom2SpeedParam.pulse = HW_REG(PI_BSD_DOM2_PWD_REG, u32); + __Dom2SpeedParam.pageSize = HW_REG(PI_BSD_DOM2_PGS_REG, u32); + __Dom2SpeedParam.relDuration = HW_REG(PI_BSD_DOM2_RLS_REG, u32); +} + +void __osInitialize_common(void) { + u32 sp2C; + + D_800145C0 = 1; + __osSetSR(__osGetSR() | SR_CU1); + __osSetFpcCsr(FPCSR_FS | FPCSR_EV); + __osSetWatchLo(0x4900000); + + while (__osSiRawReadIo((void*)(PIF_RAM_START + 0x3C), &sp2C)) { + ; + } + + while (__osSiRawWriteIo((void*)(PIF_RAM_START + 0x3C), sp2C | 8)) { + ; + } + + *(struct_exceptionPreamble*)UT_VEC = *(struct_exceptionPreamble*)__osExceptionPreamble; // TLB miss + *(struct_exceptionPreamble*)XUT_VEC = *(struct_exceptionPreamble*)__osExceptionPreamble; // XTLB miss + *(struct_exceptionPreamble*)ECC_VEC = *(struct_exceptionPreamble*)__osExceptionPreamble; // cache errors + *(struct_exceptionPreamble*)E_VEC = *(struct_exceptionPreamble*)__osExceptionPreamble; // general exceptions + + osWritebackDCache(K0BASE, E_VEC - K0BASE + sizeof(struct_exceptionPreamble)); + osInvalICache(K0BASE, E_VEC - K0BASE + sizeof(struct_exceptionPreamble)); + __createSpeedParam(); + osUnmapTLBAll(); + osMapTLBRdb(); + + osClockRate = (u64)((osClockRate * 3ll) / 4ull); + + if (!osResetType) { + bzero(osAppNmiBuffer, sizeof(osAppNmiBuffer)); + } + + if (osTvType == OS_TV_PAL) { + osViClock = VI_PAL_CLOCK; + } else if (osTvType == OS_TV_MPAL) { + osViClock = VI_MPAL_CLOCK; + } else { + osViClock = VI_NTSC_CLOCK; + } + + // Wait until there are no RCP interrupts + if (__osGetCause() & CAUSE_IP5) { + while (true) { + ; + } + } + + HW_REG(AI_CONTROL_REG, u32) = 1; + HW_REG(AI_DACRATE_REG, u32) = 0x3FFF; + HW_REG(AI_BITRATE_REG, u32) = 0xF; +} + +void __osInitialize_autodetect(void) { +} diff --git a/soh/src/libultra/os/jammesg.c b/soh/src/libultra/os/jammesg.c new file mode 100644 index 000000000..5668c476e --- /dev/null +++ b/soh/src/libultra/os/jammesg.c @@ -0,0 +1,24 @@ +#include "global.h" + +s32 osJamMesg(OSMesgQueue* mq, OSMesg msg, s32 flag) { + register u32 prevInt = __osDisableInt(); + + while (mq->validCount >= mq->msgCount) { + if (flag == OS_MESG_BLOCK) { + __osRunningThread->state = OS_STATE_WAITING; + __osEnqueueAndYield(&mq->fullqueue); + } else { + __osRestoreInt(prevInt); + return -1; + } + } + + mq->first = (mq->first + mq->msgCount - 1) % mq->msgCount; + mq->msg[mq->first] = msg; + mq->validCount++; + if (mq->mtqueue->next != NULL) { + osStartThread(__osPopThread(&mq->mtqueue)); + } + __osRestoreInt(prevInt); + return 0; +} diff --git a/soh/src/libultra/os/recvmesg.c b/soh/src/libultra/os/recvmesg.c new file mode 100644 index 000000000..0f4c83cff --- /dev/null +++ b/soh/src/libultra/os/recvmesg.c @@ -0,0 +1,29 @@ +#include "global.h" + +s32 osRecvMesg(OSMesgQueue* mq, OSMesg* msg, s32 flag) { + register u32 prevInt = __osDisableInt(); + + while (mq->validCount == 0) { + if (flag == OS_MESG_NOBLOCK) { + __osRestoreInt(prevInt); + return -1; + } + __osRunningThread->state = 8; + __osEnqueueAndYield((OSThread**)mq); + } + + if (msg != NULL) { + *msg = mq->msg[mq->first]; + } + + mq->first = (mq->first + 1) % mq->msgCount; + mq->validCount--; + + if (mq->fullqueue->next != NULL) { + osStartThread(__osPopThread(&mq->fullqueue)); + } + + __osRestoreInt(prevInt); + + return 0; +} diff --git a/soh/src/libultra/os/resetglobalintmask.c b/soh/src/libultra/os/resetglobalintmask.c new file mode 100644 index 000000000..3744ed848 --- /dev/null +++ b/soh/src/libultra/os/resetglobalintmask.c @@ -0,0 +1,8 @@ +#include "global.h" + +void __osResetGlobalIntMask(OSHWIntr mask) { + register u32 prevInt = __osDisableInt(); + + __OSGlobalIntMask &= ~(mask & ~0x401); + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/sendmesg.c b/soh/src/libultra/os/sendmesg.c new file mode 100644 index 000000000..eafd9da8b --- /dev/null +++ b/soh/src/libultra/os/sendmesg.c @@ -0,0 +1,28 @@ +#include "global.h" + +s32 osSendMesg(OSMesgQueue* mq, OSMesg mesg, s32 flag) { + register u32 prevInt = __osDisableInt(); + register u32 index; + + while (mq->validCount >= mq->msgCount) { + if (flag == OS_MESG_BLOCK) { + __osRunningThread->state = 8; + __osEnqueueAndYield(&mq->fullqueue); + } else { + __osRestoreInt(prevInt); + return -1; + } + } + + index = (mq->first + mq->validCount) % mq->msgCount; + mq->msg[index] = mesg; + mq->validCount++; + + if (mq->mtqueue->next != NULL) { + osStartThread(__osPopThread(&mq->mtqueue)); + } + + __osRestoreInt(prevInt); + + return 0; +} diff --git a/soh/src/libultra/os/seteventmesg.c b/soh/src/libultra/os/seteventmesg.c new file mode 100644 index 000000000..0580c1e24 --- /dev/null +++ b/soh/src/libultra/os/seteventmesg.c @@ -0,0 +1,22 @@ +#include "global.h" +#include "ultra64/internal.h" + +__OSEventState __osEventStateTab[OS_NUM_EVENTS + 1]; + +u32 __osPreNMI = false; + +void osSetEventMesg(OSEvent e, OSMesgQueue* mq, OSMesg msg) { + register u32 prevInt = __osDisableInt(); + __OSEventState* msgs = &__osEventStateTab[e]; + + msgs->queue = mq; + msgs->msg = msg; + + if (e == OS_EVENT_PRENMI) { + if (__osShutdown && !__osPreNMI) { + osSendMesg(mq, msg, OS_MESG_NOBLOCK); + } + __osPreNMI = true; + } + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/setglobalintmask.c b/soh/src/libultra/os/setglobalintmask.c new file mode 100644 index 000000000..8c4aebb92 --- /dev/null +++ b/soh/src/libultra/os/setglobalintmask.c @@ -0,0 +1,8 @@ +#include "global.h" + +void __osSetGlobalIntMask(OSHWIntr mask) { + register u32 prevInt = __osDisableInt(); + + __OSGlobalIntMask |= mask; + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/sethwintrroutine.c b/soh/src/libultra/os/sethwintrroutine.c new file mode 100644 index 000000000..e6bb74f0f --- /dev/null +++ b/soh/src/libultra/os/sethwintrroutine.c @@ -0,0 +1,11 @@ +#include "global.h" +#include "ultra64/internal.h" + +void __osSetHWIntrRoutine(OSHWIntr intr, s32 (*callback)(void), void* sp) { + register u32 prevInt = __osDisableInt(); + + __osHwIntTable[intr].callback = callback; + __osHwIntTable[intr].sp = sp; + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/setthreadpri.c b/soh/src/libultra/os/setthreadpri.c new file mode 100644 index 000000000..572d3495a --- /dev/null +++ b/soh/src/libultra/os/setthreadpri.c @@ -0,0 +1,23 @@ +#include "global.h" + +void osSetThreadPri(OSThread* thread, OSPri pri) { + register u32 prevInt = __osDisableInt(); + + if (thread == NULL) { + thread = __osRunningThread; + } + + if (thread->priority != pri) { + thread->priority = pri; + if (thread != __osRunningThread && thread->state != 1) { + __osDequeueThread(thread->queue, thread); + __osEnqueueThread(thread->queue, thread); + } + if (__osRunningThread->priority < __osRunQueue->priority) { + __osRunningThread->state = 2; + __osEnqueueAndYield(&__osRunQueue); + } + } + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/settimer.c b/soh/src/libultra/os/settimer.c new file mode 100644 index 000000000..b0c4a05e9 --- /dev/null +++ b/soh/src/libultra/os/settimer.c @@ -0,0 +1,45 @@ +#include "global.h" + +s32 osSetTimer(OSTimer* timer, OSTime countdown, OSTime interval, OSMesgQueue* mq, OSMesg msg) { + OSTime time; + OSTimer* next; + u32 count; + u32 value; + u32 prevInt; + + timer->next = NULL; + timer->prev = NULL; + timer->interval = interval; + + if (countdown != 0) { + timer->value = countdown; + } else { + timer->value = interval; + } + timer->mq = mq; + timer->msg = msg; + + prevInt = __osDisableInt(); + if (__osTimerList->next != __osTimerList) { + if (1) {} + + next = __osTimerList->next; + count = osGetCount(); + value = count - __osTimerCounter; + + if (value < next->value) { + next->value -= value; + } else { + next->value = 1; + } + } + + time = __osInsertTimer(timer); + __osSetTimerIntr(__osTimerList->next->value); + + __osRestoreInt(prevInt); + + if (time) {} // suppresses set but unused warning + + return 0; +} diff --git a/soh/src/libultra/os/startthread.c b/soh/src/libultra/os/startthread.c new file mode 100644 index 000000000..80d1f2564 --- /dev/null +++ b/soh/src/libultra/os/startthread.c @@ -0,0 +1,33 @@ +#include "global.h" + +void osStartThread(OSThread* thread) { + register u32 prevInt = __osDisableInt(); + + switch (thread->state) { + case 8: + thread->state = 2; + __osEnqueueThread(&__osRunQueue, thread); + break; + case 1: + if (thread->queue == NULL || thread->queue == &__osRunQueue) { + thread->state = 2; + __osEnqueueThread(&__osRunQueue, thread); + } else { + thread->state = 8; + __osEnqueueThread(thread->queue, thread); + __osEnqueueThread(&__osRunQueue, __osPopThread(thread->queue)); + } + break; + } + + if (__osRunningThread == NULL) { + __osDispatchThread(); + } else { + if (__osRunningThread->priority < __osRunQueue->priority) { + __osRunningThread->state = 2; + __osEnqueueAndYield(&__osRunQueue); + } + } + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/stopthread.c b/soh/src/libultra/os/stopthread.c new file mode 100644 index 000000000..eebd8ce9b --- /dev/null +++ b/soh/src/libultra/os/stopthread.c @@ -0,0 +1,26 @@ +#include "global.h" + +void osStopThread(OSThread* thread) { + register u32 prevInt = __osDisableInt(); + register u32 state; + + if (thread == NULL) { + state = 4; + } else { + state = thread->state; + } + + switch (state) { + case 4: + __osRunningThread->state = 1; + __osEnqueueAndYield(NULL); + break; + case 2: + case 8: + thread->state = 1; + __osDequeueThread(thread->queue, thread); + break; + } + + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/os/stoptimer.c b/soh/src/libultra/os/stoptimer.c new file mode 100644 index 000000000..d6d20536a --- /dev/null +++ b/soh/src/libultra/os/stoptimer.c @@ -0,0 +1,28 @@ +#include "global.h" + +s32 osStopTimer(OSTimer* timer) { + register u32 prevInt; + OSTimer* next; + + if (!timer->next) { + return -1; + } + + prevInt = __osDisableInt(); + + next = timer->next; + if (next != __osTimerList) { + next->value += timer->value; + } + + timer->prev->next = timer->next; + timer->next->prev = timer->prev; + timer->next = NULL; + timer->prev = NULL; + if (__osTimerList->next == __osTimerList) { + __osSetCompare(0); + } + + __osRestoreInt(prevInt); + return 0; +} diff --git a/soh/src/libultra/os/timerintr.c b/soh/src/libultra/os/timerintr.c new file mode 100644 index 000000000..7e2e36138 --- /dev/null +++ b/soh/src/libultra/os/timerintr.c @@ -0,0 +1,101 @@ +#include "global.h" + +OSTimer __osBaseTimer; +OSTime __osCurrentTime; +u32 __osBaseCounter; +u32 __osViIntrCount; +u32 __osTimerCounter; +OSTimer* __osTimerList = &__osBaseTimer; + +void __osTimerServicesInit(void) { + __osCurrentTime = 0; + __osBaseCounter = 0; + __osViIntrCount = 0; + __osTimerList->prev = __osTimerList; + __osTimerList->next = __osTimerList->prev; + __osTimerList->value = 0; + __osTimerList->interval = __osTimerList->value; + __osTimerList->mq = NULL; + __osTimerList->msg = NULL; +} + +void __osTimerInterrupt(void) { + OSTimer* timer; + u32 sp20; + u32 sp1c; + + if (__osTimerList->next == __osTimerList) { + return; + } + + while (true) { + timer = __osTimerList->next; + if (timer == __osTimerList) { + __osSetCompare(0); + __osTimerCounter = 0; + break; + } + + sp20 = osGetCount(); + sp1c = sp20 - __osTimerCounter; + __osTimerCounter = sp20; + if (sp1c < timer->value) { + timer->value -= sp1c; + __osSetTimerIntr(timer->value); + break; + } + + timer->prev->next = timer->next; + timer->next->prev = timer->prev; + timer->next = NULL; + timer->prev = NULL; + if (timer->mq != NULL) { + osSendMesg(timer->mq, timer->msg, OS_MESG_NOBLOCK); + } + if (timer->interval != 0) { + timer->value = timer->interval; + __osInsertTimer(timer); + } + } +} + +void __osSetTimerIntr(OSTime time) { + OSTime newTime; + u32 prevInt; + + if (time < 468) { + time = 468; + } + + prevInt = __osDisableInt(); + + __osTimerCounter = osGetCount(); + newTime = time + __osTimerCounter; + __osSetCompare((u32)newTime); + __osRestoreInt(prevInt); +} + +OSTime __osInsertTimer(OSTimer* timer) { + OSTimer* nextTimer; + u64 timerValue; + u32 prevInt = __osDisableInt(); + + for (nextTimer = __osTimerList->next, timerValue = timer->value; + nextTimer != __osTimerList && timerValue > nextTimer->value; + timerValue -= nextTimer->value, nextTimer = nextTimer->next) { + ; + } + + timer->value = timerValue; + if (nextTimer != __osTimerList) { + nextTimer->value -= timerValue; + } + + timer->next = nextTimer; + timer->prev = nextTimer->prev; + nextTimer->prev->next = timer; + nextTimer->prev = timer; + __osRestoreInt(prevInt); + + return timerValue; +} diff --git a/soh/src/libultra/os/virtualtophysical.c b/soh/src/libultra/os/virtualtophysical.c new file mode 100644 index 000000000..30ada113c --- /dev/null +++ b/soh/src/libultra/os/virtualtophysical.c @@ -0,0 +1,13 @@ +#include "global.h" + +u32 osVirtualToPhysical(void* vaddr) { + if ((u32)vaddr >= 0x80000000 && (u32)vaddr < 0xA0000000) { + return (u32)vaddr & 0x1FFFFFFF; + } + + if ((u32)vaddr >= 0xA0000000 && (u32)vaddr < 0xC0000000) { + return (u32)vaddr & 0x1FFFFFFF; + } + + return __osProbeTLB(vaddr); +} diff --git a/soh/src/libultra/os/yieldthread.c b/soh/src/libultra/os/yieldthread.c new file mode 100644 index 000000000..af9e15a2c --- /dev/null +++ b/soh/src/libultra/os/yieldthread.c @@ -0,0 +1,9 @@ +#include "global.h" + +void osYieldThread(void) { + register u32 prevInt = __osDisableInt(); + + __osRunningThread->state = OS_STATE_RUNNABLE; + __osEnqueueAndYield(&__osRunQueue); + __osRestoreInt(prevInt); +} diff --git a/soh/src/libultra/rmon/sprintf.c b/soh/src/libultra/rmon/sprintf.c new file mode 100644 index 000000000..a3f130fd1 --- /dev/null +++ b/soh/src/libultra/rmon/sprintf.c @@ -0,0 +1,28 @@ +#include "global.h" + +void* proutSprintf(void* dst, const char* fmt, size_t size) { + return (void*)((u32)memcpy(dst, fmt, size) + size); +} + +s32 vsprintf(char* dst, const char* fmt, va_list args) { + s32 ret = _Printf(proutSprintf, dst, fmt, args); + if (ret > -1) { + dst[ret] = '\0'; + } + return ret; +} + +s32 sprintf(char* dst, const char* fmt, ...) { + s32 ret; + va_list args; + va_start(args, fmt); + + ret = _Printf(proutSprintf, dst, fmt, args); + if (ret > -1) { + dst[ret] = '\0'; + } + + va_end(args); + + return ret; +} diff --git a/soh/src/libultra/rmon/xldtob.c b/soh/src/libultra/rmon/xldtob.c new file mode 100644 index 000000000..cc518a44b --- /dev/null +++ b/soh/src/libultra/rmon/xldtob.c @@ -0,0 +1,276 @@ +#include "global.h" + +#define BUFF_LEN 0x20 + +s16 _Ldunscale(s16*, _Pft*); +void _Genld(_Pft*, u8, u8*, s16, s16); + +const f64 D_800122E0[] = { 10e0L, 10e1L, 10e3L, 10e7L, 10e15L, 10e31L, 10e63L, 10e127L, 10e255L }; + +/* float properties */ +#define _D0 0 +#define _DBIAS 0x3FF +#define _DLONG 1 +#define _DOFF 4 +#define _FBIAS 0x7E +#define _FOFF 7 +#define _FRND 1 +#define _LBIAS 0x3FFE +#define _LOFF 15 +/* integer properties */ +#define _C2 1 +#define _CSIGN 1 +#define _ILONG 0 +#define _MBMAX 8 +#define NAN 2 +#define INF 1 +#define FINITE -1 +#define _DFRAC ((1 << _DOFF) - 1) +#define _DMASK (0x7FFF & ~_DFRAC) +#define _DMAX ((1 << (15 - _DOFF)) - 1) +#define _DNAN (0x8000 | _DMAX << _DOFF | 1 << (_DOFF - 1)) +#define _DSIGN 0x8000 +#if _D0 == 3 +#define _D1 2 /* little-endian order */ +#define _D2 1 +#define _D3 0 +#else +#define _D1 1 /* big-endian order */ +#define _D2 2 +#define _D3 3 +#endif + +void _Ldtob(_Pft* args, u8 type) { + u8 buff[BUFF_LEN]; + u8* ptr = buff; + u32 sp70; + f64 val = args->v.ld; + /* maybe struct? */ + s16 err; + s16 nsig; + s16 exp; + + s32 i; + s32 n; + f64 factor; + s32 gen; + s32 j; + s32 lo; + ldiv_t qr; + u8 drop; + s32 n2; + + if (args->prec < 0) { + args->prec = 6; + } else if (args->prec == 0 && (type == 'g' || type == 'G')) { + args->prec = 1; + } + err = _Ldunscale(&exp, (_Pft*)args); + if (err > 0) { + memcpy(args->s, err == 2 ? "NaN" : "Inf", args->n1 = 3); + return; + } + if (err == 0) { + nsig = 0; + exp = 0; + } else { + if (val < 0) { + val = -val; + } + exp = exp * 30103 / 0x000186A0 - 4; + if (exp < 0) { + n = (3 - exp) & ~3; + exp = -n; + for (i = 0; n > 0; n >>= 1, i++) { + if ((n & 1) != 0) { + val *= D_800122E0[i]; + } + } + } else if (exp > 0) { + factor = 1; + exp &= ~3; + + for (n = exp, i = 0; n > 0; n >>= 1, i++) { + if ((n & 1) != 0) { + factor *= D_800122E0[i]; + } + } + val /= factor; + } + gen = ((type == 'f') ? exp + 10 : 6) + args->prec; + if (gen > 0x13) { + gen = 0x13; + } + *ptr++ = '0'; + while (gen > 0 && 0 < val) { + lo = val; + if ((gen -= 8) > 0) { + val = (val - lo) * 1.0e8; + } + ptr = ptr + 8; + for (j = 8; lo > 0 && --j >= 0;) { + qr = ldiv(lo, 10); + *--ptr = qr.rem + '0'; + lo = qr.quot; + } + while (--j >= 0) { + ptr--; + *ptr = '0'; + } + ptr += 8; + } + + gen = ptr - &buff[1]; + for (ptr = &buff[1], exp += 7; *ptr == '0'; ptr++) { + --gen, --exp; + } + + nsig = ((type == 'f') ? exp + 1 : ((type == 'e' || type == 'E') ? 1 : 0)) + args->prec; + if (gen < nsig) { + nsig = gen; + } + if (nsig > 0) { + if (nsig < gen && ptr[nsig] > '4') { + drop = '9'; + } else { + drop = '0'; + } + + for (n2 = nsig; ptr[--n2] == drop;) { + nsig--; + } + if (drop == '9') { + ptr[n2]++; + } + if (n2 < 0) { + --ptr, ++nsig, ++exp; + } + } + } + _Genld((_Pft*)args, type, ptr, nsig, exp); +} + +s16 _Ldunscale(s16* pex, _Pft* px) { + u16* ps = (u16*)px; + s16 xchar = (ps[_D0] & _DMASK) >> _DOFF; + + if (xchar == _DMAX) { /* NaN or INF */ + *pex = 0; + return (s16)(ps[_D0] & _DFRAC || ps[_D1] || ps[_D2] || ps[_D3] ? NAN : INF); + } else if (0 < xchar) { + ps[_D0] = (ps[_D0] & ~_DMASK) | (_DBIAS << _DOFF); + *pex = xchar - (_DBIAS - 1); + return FINITE; + } + if (0 > xchar) { + return NAN; + } else { + *pex = 0; + return 0; + } +} + +void _Genld(_Pft* px, u8 code, u8* p, s16 nsig, s16 xexp) { + u8 point = '.'; + + if (nsig <= 0) { + nsig = 1, + + p = (u8*)"0"; + } + + if (code == 'f' || ((code == 'g' || code == 'G') && (-4 <= xexp) && (xexp < px->prec))) { /* 'f' format */ + ++xexp; /* change to leading digit count */ + if (code != 'f') { /* fixup for 'g' */ + if (!(px->flags & FLAGS_HASH) && nsig < px->prec) { + px->prec = nsig; + } + if ((px->prec -= xexp) < 0) { + px->prec = 0; + } + } + if (xexp <= 0) { /* digits only to right of point */ + px->s[px->n1++] = '0'; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1++] = point; + } + if (px->prec < -xexp) { + xexp = -px->prec; + } + px->nz1 = -xexp; + px->prec += xexp; + if (px->prec < nsig) { + nsig = px->prec; + } + memcpy(&px->s[px->n1], p, px->n2 = nsig); + px->nz2 = px->prec - nsig; + } else if (nsig < xexp) { /* zeros before point */ + memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = xexp - nsig; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1] = point, ++px->n2; + } + px->nz2 = px->prec; + } else { /* enough digits before point */ + memcpy(&px->s[px->n1], p, xexp); + px->n1 += xexp; + nsig -= xexp; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1++] = point; + } + if (px->prec < nsig) { + nsig = px->prec; + } + memcpy(&px->s[px->n1], p + xexp, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + } else { /* 'e' format */ + if (code == 'g' || code == 'G') { /* fixup for 'g' */ + if (nsig < px->prec) { + px->prec = nsig; + } + if (--px->prec < 0) { + px->prec = 0; + } + code = code == 'g' ? 'e' : 'E'; + } + px->s[px->n1++] = *p++; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1++] = point; + } + if (0 < px->prec) { /* put fraction digits */ + if (px->prec < --nsig) { + nsig = px->prec; + } + memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + p = (u8*)&px->s[px->n1]; /* put exponent */ + *p++ = code; + if (0 <= xexp) { + *p++ = '+'; + } else { /* negative exponent */ + *p++ = '-'; + xexp = -xexp; + } + if (100 <= xexp) { /* put oversize exponent */ + if (1000 <= xexp) { + *p++ = xexp / 1000 + '0', xexp %= 1000; + } + *p++ = xexp / 100 + '0', xexp %= 100; + } + *p++ = xexp / 10 + '0', xexp %= 10; + *p++ = xexp + '0'; + px->n2 = p - (u8*)&px->s[px->n1]; + } + if ((px->flags & (FLAGS_ZERO | FLAGS_MINUS)) == FLAGS_ZERO) { /* pad with leading zeros */ + s32 n = px->n0 + px->n1 + px->nz1 + px->n2 + px->nz2; + + if (n < px->width) { + px->nz0 = px->width - n; + } + } +} diff --git a/soh/src/libultra/rmon/xlitob.c b/soh/src/libultra/rmon/xlitob.c new file mode 100644 index 000000000..2f8152a10 --- /dev/null +++ b/soh/src/libultra/rmon/xlitob.c @@ -0,0 +1,56 @@ +#include "global.h" + +#define BUFF_LEN 0x18 + +u8 D_8000AF70[] = "0123456789abcdef"; +u8 D_8000AF84[] = "0123456789ABCDEF"; + +void _Litob(_Pft* args, u8 type) { + u8 buff[BUFF_LEN]; + const u8* numMap; + s32 base; + s32 idx; + u64 num; + lldiv_t quotrem; + + if (type == 'X') { + numMap = D_8000AF84; + } else { + numMap = D_8000AF70; + } + + base = (type == 'o') ? 8 : ((type != 'x' && type != 'X') ? 10 : 16); + idx = BUFF_LEN; + num = args->v.ll; + + if ((type == 'd' || type == 'i') && args->v.ll < 0) { + num = -num; + } + + if (num != 0 || args->prec != 0) { + buff[--idx] = numMap[num % base]; + } + + args->v.ll = num / base; + + while (args->v.ll > 0 && idx > 0) { + quotrem = lldiv(args->v.ll, base); + args->v.ll = quotrem.quot; + buff[--idx] = numMap[quotrem.rem]; + } + + args->n1 = BUFF_LEN - idx; + + memcpy(args->s, buff + idx, args->n1); + + if (args->n1 < args->prec) { + args->nz0 = args->prec - args->n1; + } + + if (args->prec < 0 && (args->flags & (FLAGS_ZERO | FLAGS_MINUS)) == FLAGS_ZERO) { + idx = args->width - args->n0 - args->nz0 - args->n1; + if (idx > 0) { + args->nz0 += idx; + } + } +} diff --git a/soh/src/libultra/rmon/xprintf.c b/soh/src/libultra/rmon/xprintf.c new file mode 100644 index 000000000..c9ac6c524 --- /dev/null +++ b/soh/src/libultra/rmon/xprintf.c @@ -0,0 +1,217 @@ +#include "global.h" + +#define ATOI(i, a) \ + for (i = 0; *a >= '0' && *a <= '9'; a++) \ + if (i < 999) \ + i = *a + i * 10 - '0'; + +#define _PROUT(fmt, _size) \ + if (_size > 0) { \ + arg = (void*)pfn(arg, fmt, _size); \ + if (arg != 0) \ + x.nchar += _size; \ + else \ + return x.nchar; \ + } +#define _PAD(m, src, extracond) \ + if (extracond && m > 0) { \ + s32 i; \ + s32 j; \ + for (j = m; j > 0; j -= i) { \ + if ((u32)j > 32) \ + i = 32; \ + else \ + i = j; \ + _PROUT(src, i); \ + } \ + } + +char spaces[] = " "; +char zeroes[] = "00000000000000000000000000000000"; + +void _Putfld(_Pft*, va_list*, u8, u8*); + +s32 _Printf(PrintCallback pfn, void* arg, const char* fmt, va_list ap) { + _Pft x; + x.nchar = 0; + + while (true) { + static const char fchar[] = " +-#0"; + static const u32 fbit[] = { FLAGS_SPACE, FLAGS_PLUS, FLAGS_MINUS, FLAGS_HASH, FLAGS_ZERO, 0 }; + + const u8* s = (u8*)fmt; + u8 c; + const char* t; + u8 ac[0x20]; + + while ((c = *s) != 0 && c != '%') { + s++; + } + _PROUT(fmt, s - (u8*)fmt); + if (c == 0) { + return x.nchar; + } + fmt = (char*)++s; + x.flags = 0; + for (; (t = strchr(fchar, *s)) != NULL; s++) { + x.flags |= fbit[t - fchar]; + } + if (*s == '*') { + x.width = va_arg(ap, s32); + if (x.width < 0) { + x.width = -x.width; + x.flags |= FLAGS_MINUS; + } + s++; + } else { + ATOI(x.width, s); + } + if (*s != '.') { + x.prec = -1; + } else { + s++; + if (*s == '*') { + x.prec = va_arg(ap, s32); + s++; + } else { + ATOI(x.prec, s); + } + } + if (strchr("hlL", *s) != NULL) { + x.qual = *s++; + } else { + x.qual = 0; + } + + if (x.qual == 'l' && *s == 'l') { + x.qual = 'L'; + s++; + } + _Putfld(&x, &ap, *s, ac); + x.width -= x.n0 + x.nz0 + x.n1 + x.nz1 + x.n2 + x.nz2; + _PAD(x.width, spaces, !(x.flags & FLAGS_MINUS)); + _PROUT((char*)ac, x.n0); + _PAD(x.nz0, zeroes, 1); + _PROUT(x.s, x.n1); + _PAD(x.nz1, zeroes, 1); + _PROUT((char*)(&x.s[x.n1]), x.n2) + _PAD(x.nz2, zeroes, 1); + _PAD(x.width, spaces, x.flags & FLAGS_MINUS); + fmt = (char*)s + 1; + } +} + +void _Putfld(_Pft* px, va_list* pap, u8 code, u8* ac) { + px->n0 = px->nz0 = px->n1 = px->nz1 = px->n2 = px->nz2 = 0; + + switch (code) { + case 'c': + ac[px->n0++] = va_arg(*pap, u32); + break; + + case 'd': + case 'i': + if (px->qual == 'l') { + px->v.ll = va_arg(*pap, s32); + } else if (px->qual == 'L') { + px->v.ll = va_arg(*pap, s64); + } else { + px->v.ll = va_arg(*pap, s32); + } + + if (px->qual == 'h') { + px->v.ll = (s16)px->v.ll; + } + + if (px->v.ll < 0) { + ac[px->n0++] = '-'; + } else if (px->flags & FLAGS_PLUS) { + ac[px->n0++] = '+'; + } else if (px->flags & FLAGS_SPACE) { + ac[px->n0++] = ' '; + } + + px->s = (char*)&ac[px->n0]; + + _Litob(px, code); + break; + case 'x': + case 'X': + case 'u': + case 'o': + if (px->qual == 'l') { + px->v.ll = va_arg(*pap, s32); + } else if (px->qual == 'L') { + px->v.ll = va_arg(*pap, s64); + } else { + px->v.ll = va_arg(*pap, s32); + } + + if (px->qual == 'h') { + px->v.ll = (u16)px->v.ll; + } else if (px->qual == 0) { + px->v.ll = (u32)px->v.ll; + } + + if (px->flags & FLAGS_HASH) { + ac[px->n0++] = '0'; + if (code == 'x' || code == 'X') { + + ac[px->n0++] = code; + } + } + px->s = (char*)&ac[px->n0]; + _Litob(px, code); + break; + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + px->v.ld = px->qual == 'L' ? va_arg(*pap, f64) : va_arg(*pap, f64); + + if (*(u16*)&px->v.ll & 0x8000) { + ac[px->n0++] = '-'; + } else { + if (px->flags & FLAGS_PLUS) { + ac[px->n0++] = '+'; + } else if (px->flags & FLAGS_SPACE) { + ac[px->n0++] = ' '; + } + } + + px->s = (char*)&ac[px->n0]; + _Ldtob(px, code); + break; + case 'n': + if (px->qual == 'h') { + *(va_arg(*pap, u16*)) = px->nchar; + } else if (px->qual == 'l') { + *va_arg(*pap, u32*) = px->nchar; + } else if (px->qual == 'L') { + *va_arg(*pap, u64*) = px->nchar; + } else { + *va_arg(*pap, u32*) = px->nchar; + } + break; + + case 'p': + px->v.ll = va_arg(*pap, void*); + px->s = (char*)&ac[px->n0]; + _Litob(px, 'x'); + break; + case 's': + px->s = va_arg(*pap, char*); + px->n1 = strlen(px->s); + if (px->prec >= 0 && px->n1 > px->prec) { + px->n1 = px->prec; + } + break; + case '%': + ac[px->n0++] = '%'; + break; + default: + ac[px->n0++] = code; + break; + } +} diff --git a/soh/src/overlays/actors/ovl_Arms_Hook/z_arms_hook.c b/soh/src/overlays/actors/ovl_Arms_Hook/z_arms_hook.c new file mode 100644 index 000000000..bda9b5195 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arms_Hook/z_arms_hook.c @@ -0,0 +1,340 @@ +#include "z_arms_hook.h" +#include "objects/object_link_boy/object_link_boy.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void ArmsHook_Init(Actor* thisx, GlobalContext* globalCtx); +void ArmsHook_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ArmsHook_Update(Actor* thisx, GlobalContext* globalCtx); +void ArmsHook_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ArmsHook_Wait(ArmsHook* this, GlobalContext* globalCtx); +void ArmsHook_Shoot(ArmsHook* this, GlobalContext* globalCtx); + +const ActorInit Arms_Hook_InitVars = { + ACTOR_ARMS_HOOK, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_LINK_BOY, + sizeof(ArmsHook), + (ActorFunc)ArmsHook_Init, + (ActorFunc)ArmsHook_Destroy, + (ActorFunc)ArmsHook_Update, + (ActorFunc)ArmsHook_Draw, + NULL, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_PLAYER, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK2, + { 0x00000080, 0x00, 0x01 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_NEAREST | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static Vec3f sUnusedVec1 = { 0.0f, 0.5f, 0.0f }; +static Vec3f sUnusedVec2 = { 0.0f, 0.5f, 0.0f }; + +static Color_RGB8 sUnusedColors[] = { + { 255, 255, 100 }, + { 255, 255, 50 }, +}; + +static Vec3f D_80865B70 = { 0.0f, 0.0f, 0.0f }; +static Vec3f D_80865B7C = { 0.0f, 0.0f, 900.0f }; +static Vec3f D_80865B88 = { 0.0f, 500.0f, -3000.0f }; +static Vec3f D_80865B94 = { 0.0f, -500.0f, -3000.0f }; +static Vec3f D_80865BA0 = { 0.0f, 500.0f, 1200.0f }; +static Vec3f D_80865BAC = { 0.0f, -500.0f, 1200.0f }; + +void ArmsHook_SetupAction(ArmsHook* this, ArmsHookActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ArmsHook_Init(Actor* thisx, GlobalContext* globalCtx) { + ArmsHook* this = (ArmsHook*)thisx; + + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->actor, &sQuadInit); + ArmsHook_SetupAction(this, ArmsHook_Wait); + this->unk_1E8 = this->actor.world.pos; +} + +void ArmsHook_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ArmsHook* this = (ArmsHook*)thisx; + + if (this->grabbed != NULL) { + this->grabbed->flags &= ~ACTOR_FLAG_13; + } + Collider_DestroyQuad(globalCtx, &this->collider); +} + +void ArmsHook_Wait(ArmsHook* this, GlobalContext* globalCtx) { + if (this->actor.parent == NULL) { + Player* player = GET_PLAYER(globalCtx); + // get correct timer length for hookshot or longshot + s32 length = (player->heldItemActionParam == PLAYER_AP_HOOKSHOT) ? 13 : 26; + + ArmsHook_SetupAction(this, ArmsHook_Shoot); + func_8002D9A4(&this->actor, 20.0f); + this->actor.parent = &GET_PLAYER(globalCtx)->actor; + this->timer = length; + } +} + +void func_80865044(ArmsHook* this) { + this->actor.child = this->actor.parent; + this->actor.parent->parent = &this->actor; +} + +s32 ArmsHook_AttachToPlayer(ArmsHook* this, Player* player) { + player->actor.child = &this->actor; + player->heldActor = &this->actor; + if (this->actor.child != NULL) { + player->actor.parent = NULL; + this->actor.child = NULL; + return true; + } + return false; +} + +void ArmsHook_DetachHookFromActor(ArmsHook* this) { + if (this->grabbed != NULL) { + this->grabbed->flags &= ~ACTOR_FLAG_13; + this->grabbed = NULL; + } +} + +s32 ArmsHook_CheckForCancel(ArmsHook* this) { + Player* player = (Player*)this->actor.parent; + + if (Player_HoldsHookshot(player)) { + if ((player->itemActionParam != player->heldItemActionParam) || (player->actor.flags & ACTOR_FLAG_8) || + ((player->stateFlags1 & 0x4000080))) { + this->timer = 0; + ArmsHook_DetachHookFromActor(this); + Math_Vec3f_Copy(&this->actor.world.pos, &player->unk_3C8); + return 1; + } + } + return 0; +} + +void ArmsHook_AttachHookToActor(ArmsHook* this, Actor* actor) { + actor->flags |= ACTOR_FLAG_13; + this->grabbed = actor; + Math_Vec3f_Diff(&actor->world.pos, &this->actor.world.pos, &this->grabbedDistDiff); +} + +void ArmsHook_Shoot(ArmsHook* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* touchedActor; + Actor* grabbed; + Vec3f bodyDistDiffVec; + Vec3f newPos; + f32 bodyDistDiff; + f32 phi_f16; + DynaPolyActor* dynaPolyActor; + f32 sp94; + f32 sp90; + s32 pad; + CollisionPoly* poly; + s32 bgId; + Vec3f sp78; + Vec3f prevFrameDiff; + Vec3f sp60; + f32 sp5C; + f32 sp58; + f32 velocity; + s32 pad1; + + if ((this->actor.parent == NULL) || (!Player_HoldsHookshot(player))) { + ArmsHook_DetachHookFromActor(this); + Actor_Kill(&this->actor); + return; + } + + func_8002F8F0(&player->actor, NA_SE_IT_HOOKSHOT_CHAIN - SFX_FLAG); + ArmsHook_CheckForCancel(this); + + if ((this->timer != 0) && (this->collider.base.atFlags & AT_HIT) && + (this->collider.info.atHitInfo->elemType != ELEMTYPE_UNK4)) { + touchedActor = this->collider.base.at; + if ((touchedActor->update != NULL) && (touchedActor->flags & (ACTOR_FLAG_9 | ACTOR_FLAG_10))) { + if (this->collider.info.atHitInfo->bumperFlags & BUMP_HOOKABLE) { + ArmsHook_AttachHookToActor(this, touchedActor); + if (CHECK_FLAG_ALL(touchedActor->flags, ACTOR_FLAG_10)) { + func_80865044(this); + } + } + } + this->timer = 0; + Audio_PlaySoundGeneral(NA_SE_IT_ARROW_STICK_CRE, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (DECR(this->timer) == 0) { + grabbed = this->grabbed; + if (grabbed != NULL) { + if ((grabbed->update == NULL) || !CHECK_FLAG_ALL(grabbed->flags, ACTOR_FLAG_13)) { + grabbed = NULL; + this->grabbed = NULL; + } else if (this->actor.child != NULL) { + sp94 = Actor_WorldDistXYZToActor(&this->actor, grabbed); + sp90 = sqrtf(SQ(this->grabbedDistDiff.x) + SQ(this->grabbedDistDiff.y) + SQ(this->grabbedDistDiff.z)); + Math_Vec3f_Diff(&grabbed->world.pos, &this->grabbedDistDiff, &this->actor.world.pos); + if (50.0f < (sp94 - sp90)) { + ArmsHook_DetachHookFromActor(this); + grabbed = NULL; + } + } + } + + bodyDistDiff = Math_Vec3f_DistXYZAndStoreDiff(&player->unk_3C8, &this->actor.world.pos, &bodyDistDiffVec); + if (bodyDistDiff < 30.0f) { + velocity = 0.0f; + phi_f16 = 0.0f; + } else { + if (this->actor.child != NULL) { + velocity = 30.0f; + } else if (grabbed != NULL) { + velocity = 50.0f; + } else { + velocity = 200.0f; + } + phi_f16 = bodyDistDiff - velocity; + if (bodyDistDiff <= velocity) { + phi_f16 = 0.0f; + } + velocity = phi_f16 / bodyDistDiff; + } + + newPos.x = bodyDistDiffVec.x * velocity; + newPos.y = bodyDistDiffVec.y * velocity; + newPos.z = bodyDistDiffVec.z * velocity; + + if (this->actor.child == NULL) { + if ((grabbed != NULL) && (grabbed->id == ACTOR_BG_SPOT06_OBJECTS)) { + Math_Vec3f_Diff(&grabbed->world.pos, &this->grabbedDistDiff, &this->actor.world.pos); + phi_f16 = 1.0f; + } else { + Math_Vec3f_Sum(&player->unk_3C8, &newPos, &this->actor.world.pos); + if (grabbed != NULL) { + Math_Vec3f_Sum(&this->actor.world.pos, &this->grabbedDistDiff, &grabbed->world.pos); + } + } + } else { + Math_Vec3f_Diff(&bodyDistDiffVec, &newPos, &player->actor.velocity); + player->actor.world.rot.x = + Math_Atan2S(sqrtf(SQ(bodyDistDiffVec.x) + SQ(bodyDistDiffVec.z)), -bodyDistDiffVec.y); + } + + if (phi_f16 < 50.0f) { + ArmsHook_DetachHookFromActor(this); + if (phi_f16 == 0.0f) { + ArmsHook_SetupAction(this, ArmsHook_Wait); + if (ArmsHook_AttachToPlayer(this, player)) { + Math_Vec3f_Diff(&this->actor.world.pos, &player->actor.world.pos, &player->actor.velocity); + player->actor.velocity.y -= 20.0f; + } + } + } + } else { + Actor_MoveForward(&this->actor); + Math_Vec3f_Diff(&this->actor.world.pos, &this->actor.prevPos, &prevFrameDiff); + Math_Vec3f_Sum(&this->unk_1E8, &prevFrameDiff, &this->unk_1E8); + this->actor.shape.rot.x = Math_Atan2S(this->actor.speedXZ, -this->actor.velocity.y); + sp60.x = this->unk_1F4.x - (this->unk_1E8.x - this->unk_1F4.x); + sp60.y = this->unk_1F4.y - (this->unk_1E8.y - this->unk_1F4.y); + sp60.z = this->unk_1F4.z - (this->unk_1E8.z - this->unk_1F4.z); + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp60, &this->unk_1E8, &sp78, &poly, true, true, true, true, + &bgId) && + !func_8002F9EC(globalCtx, &this->actor, poly, bgId, &sp78)) { + sp5C = COLPOLY_GET_NORMAL(poly->normal.x); + sp58 = COLPOLY_GET_NORMAL(poly->normal.z); + Math_Vec3f_Copy(&this->actor.world.pos, &sp78); + this->actor.world.pos.x += 10.0f * sp5C; + this->actor.world.pos.z += 10.0f * sp58; + this->timer = 0; + if (SurfaceType_IsHookshotSurface(&globalCtx->colCtx, poly, bgId)) { + if (bgId != BGCHECK_SCENE) { + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, bgId); + if (dynaPolyActor != NULL) { + ArmsHook_AttachHookToActor(this, &dynaPolyActor->actor); + } + } + func_80865044(this); + Audio_PlaySoundGeneral(NA_SE_IT_HOOKSHOT_STICK_OBJ, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else { + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, &this->actor.world.pos); + Audio_PlaySoundGeneral(NA_SE_IT_HOOKSHOT_REFLECT, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } else if (CHECK_BTN_ANY(globalCtx->state.input[0].press.button, + (BTN_A | BTN_B | BTN_R | BTN_CUP | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN))) { + this->timer = 0; + } + } +} + +void ArmsHook_Update(Actor* thisx, GlobalContext* globalCtx) { + ArmsHook* this = (ArmsHook*)thisx; + + this->actionFunc(this, globalCtx); + this->unk_1F4 = this->unk_1E8; +} + +void ArmsHook_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ArmsHook* this = (ArmsHook*)thisx; + Player* player = GET_PLAYER(globalCtx); + Vec3f sp78; + Vec3f sp6C; + Vec3f sp60; + f32 sp5C; + f32 sp58; + + if ((player->actor.draw != NULL) && (player->rightHandType == 15)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_arms_hook.c", 850); + + if ((ArmsHook_Shoot != this->actionFunc) || (this->timer <= 0)) { + Matrix_MultVec3f(&D_80865B70, &this->unk_1E8); + Matrix_MultVec3f(&D_80865B88, &sp6C); + Matrix_MultVec3f(&D_80865B94, &sp60); + this->hookInfo.active = 0; + } else { + Matrix_MultVec3f(&D_80865B7C, &this->unk_1E8); + Matrix_MultVec3f(&D_80865BA0, &sp6C); + Matrix_MultVec3f(&D_80865BAC, &sp60); + } + + func_80090480(globalCtx, &this->collider, &this->hookInfo, &sp6C, &sp60); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_arms_hook.c", 895), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gLinkAdultHookshotTipDL); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Math_Vec3f_Diff(&player->unk_3C8, &this->actor.world.pos, &sp78); + sp58 = SQ(sp78.x) + SQ(sp78.z); + sp5C = sqrtf(sp58); + Matrix_RotateY(Math_FAtan2F(sp78.x, sp78.z), MTXMODE_APPLY); + Matrix_RotateX(Math_FAtan2F(-sp78.y, sp5C), MTXMODE_APPLY); + Matrix_Scale(0.015f, 0.015f, sqrtf(SQ(sp78.y) + sp58) * 0.01f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_arms_hook.c", 910), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gLinkAdultHookshotChainDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_arms_hook.c", 913); + } +} diff --git a/soh/src/overlays/actors/ovl_Arms_Hook/z_arms_hook.h b/soh/src/overlays/actors/ovl_Arms_Hook/z_arms_hook.h new file mode 100644 index 000000000..822d87fec --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arms_Hook/z_arms_hook.h @@ -0,0 +1,23 @@ +#ifndef Z_ARMS_HOOK_H +#define Z_ARMS_HOOK_H + +#include "ultra64.h" +#include "global.h" + +struct ArmsHook; + +typedef void (*ArmsHookActionFunc)(struct ArmsHook*, GlobalContext*); + +typedef struct ArmsHook { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderQuad collider; + /* 0x01CC */ WeaponInfo hookInfo; + /* 0x01E8 */ Vec3f unk_1E8; + /* 0x01F4 */ Vec3f unk_1F4; + /* 0x0200 */ Actor* grabbed; + /* 0x0204 */ Vec3f grabbedDistDiff; + /* 0x0210 */ s16 timer; + /* 0x0214 */ ArmsHookActionFunc actionFunc; +} ArmsHook; // size = 0x0218 + +#endif diff --git a/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c b/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c new file mode 100644 index 000000000..8e616b1ec --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c @@ -0,0 +1,247 @@ +/* + * File: z_arrow_fire.c + * Overlay: ovl_Arrow_Fire + * Description: Fire Arrow. Spawned as a child of a normal arrow. + */ + +#include "z_arrow_fire.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void ArrowFire_Init(Actor* thisx, GlobalContext* globalCtx); +void ArrowFire_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ArrowFire_Update(Actor* thisx, GlobalContext* globalCtx); +void ArrowFire_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ArrowFire_Charge(ArrowFire* this, GlobalContext* globalCtx); +void ArrowFire_Fly(ArrowFire* this, GlobalContext* globalCtx); +void ArrowFire_Hit(ArrowFire* this, GlobalContext* globalCtx); + +#include "overlays/ovl_Arrow_Fire/ovl_Arrow_Fire.h" + +const ActorInit Arrow_Fire_InitVars = { + ACTOR_ARROW_FIRE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ArrowFire), + (ActorFunc)ArrowFire_Init, + (ActorFunc)ArrowFire_Destroy, + (ActorFunc)ArrowFire_Update, + (ActorFunc)ArrowFire_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_STOP), +}; + +void ArrowFire_SetupAction(ArrowFire* this, ArrowFireActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ArrowFire_Init(Actor* thisx, GlobalContext* globalCtx) { + ArrowFire* this = (ArrowFire*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->radius = 0; + this->unk_158 = 1.0f; + ArrowFire_SetupAction(this, ArrowFire_Charge); + Actor_SetScale(&this->actor, 0.01f); + this->alpha = 160; + this->timer = 0; + this->unk_15C = 0.0f; +} + +void ArrowFire_Destroy(Actor* thisx, GlobalContext* globalCtx) { + func_800876C8(globalCtx); + LOG_STRING("消滅", "../z_arrow_fire.c", 421); // "Disappearance" +} + +void ArrowFire_Charge(ArrowFire* this, GlobalContext* globalCtx) { + EnArrow* arrow; + + arrow = (EnArrow*)this->actor.parent; + if ((arrow == NULL) || (arrow->actor.update == NULL)) { + Actor_Kill(&this->actor); + return; + } + + if (this->radius < 10) { + this->radius += 1; + } + // copy position and rotation from arrow + this->actor.world.pos = arrow->actor.world.pos; + this->actor.shape.rot = arrow->actor.shape.rot; + + func_8002F974(&this->actor, NA_SE_PL_ARROW_CHARGE_FIRE - SFX_FLAG); + + // if arrow has no parent, player has fired the arrow + if (arrow->actor.parent == NULL) { + this->unkPos = this->actor.world.pos; + this->radius = 10; + ArrowFire_SetupAction(this, ArrowFire_Fly); + this->alpha = 255; + } +} + +void func_80865ECC(Vec3f* unkPos, Vec3f* firePos, f32 scale) { + unkPos->x += ((firePos->x - unkPos->x) * scale); + unkPos->y += ((firePos->y - unkPos->y) * scale); + unkPos->z += ((firePos->z - unkPos->z) * scale); +} + +void ArrowFire_Hit(ArrowFire* this, GlobalContext* globalCtx) { + f32 scale; + f32 offset; + u16 timer; + + if (this->actor.projectedW < 50.0f) { + scale = 10.0f; + } else { + if (950.0f < this->actor.projectedW) { + scale = 310.0f; + } else { + scale = this->actor.projectedW; + scale = ((scale - 50.0f) * (1.0f / 3.0f)) + 10.0f; + } + } + + timer = this->timer; + if (timer != 0) { + this->timer -= 1; + + if (this->timer >= 8) { + offset = ((this->timer - 8) * (1.0f / 24.0f)); + offset = SQ(offset); + this->radius = (((1.0f - offset) * scale) + 10.0f); + this->unk_158 += ((2.0f - this->unk_158) * 0.1f); + if (this->timer < 16) { + if (1) {} + this->alpha = ((this->timer * 0x23) - 0x118); + } + } + } + + if (this->timer >= 9) { + if (this->unk_15C < 1.0f) { + this->unk_15C += 0.25f; + } + } else { + if (this->unk_15C > 0.0f) { + this->unk_15C -= 0.125f; + } + } + + if (this->timer < 8) { + this->alpha = 0; + } + + if (this->timer == 0) { + this->timer = 255; + Actor_Kill(&this->actor); + } +} + +void ArrowFire_Fly(ArrowFire* this, GlobalContext* globalCtx) { + EnArrow* arrow; + f32 distanceScaled; + s32 pad; + + arrow = (EnArrow*)this->actor.parent; + if ((arrow == NULL) || (arrow->actor.update == NULL)) { + Actor_Kill(&this->actor); + return; + } + // copy position and rotation from arrow + this->actor.world.pos = arrow->actor.world.pos; + this->actor.shape.rot = arrow->actor.shape.rot; + distanceScaled = Math_Vec3f_DistXYZ(&this->unkPos, &this->actor.world.pos) * (1.0f / 24.0f); + this->unk_158 = distanceScaled; + if (distanceScaled < 1.0f) { + this->unk_158 = 1.0f; + } + func_80865ECC(&this->unkPos, &this->actor.world.pos, 0.05f); + + if (arrow->hitFlags & 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_EXPLOSION_FRAME); + ArrowFire_SetupAction(this, ArrowFire_Hit); + this->timer = 32; + this->alpha = 255; + } else if (arrow->timer < 34) { + if (this->alpha < 35) { + Actor_Kill(&this->actor); + } else { + this->alpha -= 0x19; + } + } +} + +void ArrowFire_Update(Actor* thisx, GlobalContext* globalCtx) { + ArrowFire* this = (ArrowFire*)thisx; + + if (globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK || + globalCtx->msgCtx.msgMode == MSGMODE_SONG_PLAYED) { + Actor_Kill(&this->actor); + } else { + this->actionFunc(this, globalCtx); + } +} + +void ArrowFire_Draw(Actor* thisx, GlobalContext* globalCtx2) { + ArrowFire* this = (ArrowFire*)thisx; + GlobalContext* globalCtx = globalCtx2; + u32 stateFrames; + EnArrow* arrow; + Actor* tranform; + + stateFrames = globalCtx->state.frames; + arrow = (EnArrow*)this->actor.parent; + if (1) {} + + if ((arrow != NULL) && (arrow->actor.update != NULL) && (this->timer < 255)) { + if (1) {} + tranform = (arrow->hitFlags & 2) ? &this->actor : &arrow->actor; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_arrow_fire.c", 618); + + Matrix_Translate(tranform->world.pos.x, tranform->world.pos.y, tranform->world.pos.z, MTXMODE_NEW); + Matrix_RotateY(tranform->shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(tranform->shape.rot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(tranform->shape.rot.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + + // Draw red effect over the screen when arrow hits + if (this->unk_15C > 0) { + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (s32)(40.0f * this->unk_15C) & 0xFF, 0, 0, + (s32)(150.0f * this->unk_15C) & 0xFF); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + } + + // Draw fire on the arrow + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 200, 0, this->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 128); + Matrix_RotateZYX(0x4000, 0x0, 0x0, MTXMODE_APPLY); + if (this->timer != 0) { + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 1500.0f, 0.0f, MTXMODE_APPLY); + } + Matrix_Scale(this->radius * 0.2f, this->unk_158 * 4.0f, this->radius * 0.2f, MTXMODE_APPLY); + Matrix_Translate(0.0f, -700.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_arrow_fire.c", 666), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 255 - (stateFrames * 2) % 256, 0, 64, 32, 1, + 255 - stateFrames % 256, 511 - (stateFrames * 10) % 512, 64, 64)); + gSPDisplayList(POLY_XLU_DISP++, sModelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_arrow_fire.c", 682); + } +} diff --git a/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.h b/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.h new file mode 100644 index 000000000..f5b2d3f63 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.h @@ -0,0 +1,22 @@ +#ifndef Z_ARROW_FIRE_H +#define Z_ARROW_FIRE_H + +#include "ultra64.h" +#include "global.h" + +struct ArrowFire; + +typedef void (*ArrowFireActionFunc)(struct ArrowFire*, GlobalContext*); + +typedef struct ArrowFire { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3f unkPos; + /* 0x0158 */ f32 unk_158; + /* 0x015C */ f32 unk_15C; + /* 0x0160 */ ArrowFireActionFunc actionFunc; + /* 0x0164 */ s16 radius; + /* 0x0166 */ u16 timer; + /* 0x0168 */ u8 alpha; +} ArrowFire; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c b/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c new file mode 100644 index 000000000..0033caa7a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c @@ -0,0 +1,246 @@ +/* + * File: z_arrow_ice.c + * Overlay: ovl_Arrow_Ice + * Description: Ice Arrow. Spawned as a child of a normal arrow. + */ + +#include "z_arrow_ice.h" + +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void ArrowIce_Init(Actor* thisx, GlobalContext* globalCtx); +void ArrowIce_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ArrowIce_Update(Actor* thisx, GlobalContext* globalCtx); +void ArrowIce_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ArrowIce_Charge(ArrowIce* this, GlobalContext* globalCtx); +void ArrowIce_Fly(ArrowIce* this, GlobalContext* globalCtx); +void ArrowIce_Hit(ArrowIce* this, GlobalContext* globalCtx); + +#include "overlays/ovl_Arrow_Ice/ovl_Arrow_Ice.h" + +const ActorInit Arrow_Ice_InitVars = { + ACTOR_ARROW_ICE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ArrowIce), + (ActorFunc)ArrowIce_Init, + (ActorFunc)ArrowIce_Destroy, + (ActorFunc)ArrowIce_Update, + (ActorFunc)ArrowIce_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_STOP), +}; + +void ArrowIce_SetupAction(ArrowIce* this, ArrowIceActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ArrowIce_Init(Actor* thisx, GlobalContext* globalCtx) { + ArrowIce* this = (ArrowIce*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->radius = 0; + this->unk_160 = 1.0f; + ArrowIce_SetupAction(this, ArrowIce_Charge); + Actor_SetScale(&this->actor, 0.01f); + this->alpha = 100; + this->timer = 0; + this->unk_164 = 0.0f; +} + +void ArrowIce_Destroy(Actor* thisx, GlobalContext* globalCtx) { + func_800876C8(globalCtx); + LOG_STRING("消滅", "../z_arrow_ice.c", 415); // "Disappearance" +} + +void ArrowIce_Charge(ArrowIce* this, GlobalContext* globalCtx) { + EnArrow* arrow; + + arrow = (EnArrow*)this->actor.parent; + if ((arrow == NULL) || (arrow->actor.update == NULL)) { + Actor_Kill(&this->actor); + return; + } + + if (this->radius < 10) { + this->radius += 1; + } + // copy position and rotation from arrow + this->actor.world.pos = arrow->actor.world.pos; + this->actor.shape.rot = arrow->actor.shape.rot; + + func_8002F974(&this->actor, NA_SE_PL_ARROW_CHARGE_ICE - SFX_FLAG); + + // if arrow has no parent, player has fired the arrow + if (arrow->actor.parent == NULL) { + this->unkPos = this->actor.world.pos; + this->radius = 10; + ArrowIce_SetupAction(this, ArrowIce_Fly); + this->alpha = 255; + } +} + +void func_80867E8C(Vec3f* unkPos, Vec3f* icePos, f32 scale) { + unkPos->x += ((icePos->x - unkPos->x) * scale); + unkPos->y += ((icePos->y - unkPos->y) * scale); + unkPos->z += ((icePos->z - unkPos->z) * scale); +} + +void ArrowIce_Hit(ArrowIce* this, GlobalContext* globalCtx) { + f32 scale; + f32 offset; + u16 timer; + + if (this->actor.projectedW < 50.0f) { + scale = 10.0f; + } else { + if (950.0f < this->actor.projectedW) { + scale = 310.0f; + } else { + scale = this->actor.projectedW; + scale = ((scale - 50.0f) * (1.0f / 3.0f)) + 10.0f; + } + } + + timer = this->timer; + if (timer != 0) { + this->timer -= 1; + + if (this->timer >= 8) { + offset = ((this->timer - 8) * (1.0f / 24.0f)); + offset = SQ(offset); + this->radius = (((1.0f - offset) * scale) + 10.0f); + this->unk_160 += ((2.0f - this->unk_160) * 0.1f); + if (this->timer < 16) { + if (1) {} + this->alpha = ((this->timer * 0x23) - 0x118); + } + } + } + + if (this->timer >= 9) { + if (this->unk_164 < 1.0f) { + this->unk_164 += 0.25f; + } + } else { + if (this->unk_164 > 0.0f) { + this->unk_164 -= 0.125f; + } + } + + if (this->timer < 8) { + this->alpha = 0; + } + + if (this->timer == 0) { + this->timer = 255; + Actor_Kill(&this->actor); + } +} + +void ArrowIce_Fly(ArrowIce* this, GlobalContext* globalCtx) { + EnArrow* arrow; + f32 distanceScaled; + s32 pad; + + arrow = (EnArrow*)this->actor.parent; + if ((arrow == NULL) || (arrow->actor.update == NULL)) { + Actor_Kill(&this->actor); + return; + } + // copy position and rotation from arrow + this->actor.world.pos = arrow->actor.world.pos; + this->actor.shape.rot = arrow->actor.shape.rot; + distanceScaled = Math_Vec3f_DistXYZ(&this->unkPos, &this->actor.world.pos) * (1.0f / 24.0f); + this->unk_160 = distanceScaled; + if (distanceScaled < 1.0f) { + this->unk_160 = 1.0f; + } + func_80867E8C(&this->unkPos, &this->actor.world.pos, 0.05f); + + if (arrow->hitFlags & 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_EXPLOSION_ICE); + ArrowIce_SetupAction(this, ArrowIce_Hit); + this->timer = 32; + this->alpha = 255; + } else if (arrow->timer < 34) { + if (this->alpha < 35) { + Actor_Kill(&this->actor); + } else { + this->alpha -= 0x19; + } + } +} + +void ArrowIce_Update(Actor* thisx, GlobalContext* globalCtx) { + ArrowIce* this = (ArrowIce*)thisx; + + if (globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK || + globalCtx->msgCtx.msgMode == MSGMODE_SONG_PLAYED) { + Actor_Kill(&this->actor); + } else { + this->actionFunc(this, globalCtx); + } +} + +void ArrowIce_Draw(Actor* thisx, GlobalContext* globalCtx) { + ArrowIce* this = (ArrowIce*)thisx; + s32 pad; + Actor* tranform; + u32 stateFrames = globalCtx->state.frames; + EnArrow* arrow = (EnArrow*)this->actor.parent; + + if (1) {} + + if ((arrow != NULL) && (arrow->actor.update != NULL) && (this->timer < 255)) { + if (1) {} + tranform = (arrow->hitFlags & 2) ? &this->actor : &arrow->actor; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_arrow_ice.c", 610); + + Matrix_Translate(tranform->world.pos.x, tranform->world.pos.y, tranform->world.pos.z, MTXMODE_NEW); + Matrix_RotateY(tranform->shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(tranform->shape.rot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(tranform->shape.rot.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + + // Draw blue effect over the screen when arrow hits + if (this->unk_164 > 0) { + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, (s32)(10.0f * this->unk_164) & 0xFF, + (s32)(50.0f * this->unk_164) & 0xFF, (s32)(150.0f * this->unk_164) & 0xFF); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + } + + // Draw ice on the arrow + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 170, 255, 255, this->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 255, 128); + Matrix_RotateZYX(0x4000, 0x0, 0x0, MTXMODE_APPLY); + if (this->timer != 0) { + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 1500.0f, 0.0f, MTXMODE_APPLY); + } + Matrix_Scale(this->radius * 0.2f, this->unk_160 * 3.0f, this->radius * 0.2f, MTXMODE_APPLY); + Matrix_Translate(0.0f, -700.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_arrow_ice.c", 660), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 511 - (stateFrames * 5) % 512, 0, 128, 32, 1, + 511 - (stateFrames * 10) % 512, 511 - (stateFrames * 10) % 512, 4, 16)); + gSPDisplayList(POLY_XLU_DISP++, sModelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_arrow_ice.c", 676); + } +} diff --git a/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.h b/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.h new file mode 100644 index 000000000..b326f7c43 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.h @@ -0,0 +1,22 @@ +#ifndef Z_ARROW_ICE_H +#define Z_ARROW_ICE_H + +#include "ultra64.h" +#include "global.h" + +struct ArrowIce; + +typedef void (*ArrowIceActionFunc)(struct ArrowIce*, GlobalContext*); + +typedef struct ArrowIce { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 radius; + /* 0x014E */ u16 timer; + /* 0x0150 */ u8 alpha; + /* 0x0154 */ Vec3f unkPos; + /* 0x0160 */ f32 unk_160; + /* 0x0164 */ f32 unk_164; + /* 0x0168 */ ArrowIceActionFunc actionFunc; +} ArrowIce; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c b/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c new file mode 100644 index 000000000..a98f8b958 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c @@ -0,0 +1,244 @@ +/* + * File: z_arrow_light.c + * Overlay: ovl_Arrow_Light + * Description: Light Arrow. Spawned as a child of a normal arrow. + */ + +#include "z_arrow_light.h" + +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void ArrowLight_Init(Actor* thisx, GlobalContext* globalCtx); +void ArrowLight_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ArrowLight_Update(Actor* thisx, GlobalContext* globalCtx); +void ArrowLight_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ArrowLight_Charge(ArrowLight* this, GlobalContext* globalCtx); +void ArrowLight_Fly(ArrowLight* this, GlobalContext* globalCtx); +void ArrowLight_Hit(ArrowLight* this, GlobalContext* globalCtx); + +#include "overlays/ovl_Arrow_Light/ovl_Arrow_Light.h" + +const ActorInit Arrow_Light_InitVars = { + ACTOR_ARROW_LIGHT, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ArrowLight), + (ActorFunc)ArrowLight_Init, + (ActorFunc)ArrowLight_Destroy, + (ActorFunc)ArrowLight_Update, + (ActorFunc)ArrowLight_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_STOP), +}; + +void ArrowLight_SetupAction(ArrowLight* this, ArrowLightActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ArrowLight_Init(Actor* thisx, GlobalContext* globalCtx) { + ArrowLight* this = (ArrowLight*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->radius = 0; + this->unk_160 = 1.0f; + ArrowLight_SetupAction(this, ArrowLight_Charge); + Actor_SetScale(&this->actor, 0.01f); + this->alpha = 130; + this->timer = 0; + this->unk_164 = 0.0f; +} + +void ArrowLight_Destroy(Actor* thisx, GlobalContext* globalCtx) { + func_800876C8(globalCtx); + LOG_STRING("消滅", "../z_arrow_light.c", 403); // "Disappearance" +} + +void ArrowLight_Charge(ArrowLight* this, GlobalContext* globalCtx) { + EnArrow* arrow = (EnArrow*)this->actor.parent; + + if ((arrow == NULL) || (arrow->actor.update == NULL)) { + Actor_Kill(&this->actor); + return; + } + + if (this->radius < 10) { + this->radius += 1; + } + // copy position and rotation from arrow + this->actor.world.pos = arrow->actor.world.pos; + this->actor.shape.rot = arrow->actor.shape.rot; + + func_8002F974(&this->actor, NA_SE_PL_ARROW_CHARGE_LIGHT - SFX_FLAG); + + // if arrow has no parent, player has fired the arrow + if (arrow->actor.parent == NULL) { + this->unkPos = this->actor.world.pos; + this->radius = 10; + ArrowLight_SetupAction(this, ArrowLight_Fly); + this->alpha = 255; + } +} + +void func_80869E6C(Vec3f* unkPos, Vec3f* lightPos, f32 scale) { + unkPos->x += ((lightPos->x - unkPos->x) * scale); + unkPos->y += ((lightPos->y - unkPos->y) * scale); + unkPos->z += ((lightPos->z - unkPos->z) * scale); +} + +void ArrowLight_Hit(ArrowLight* this, GlobalContext* globalCtx) { + f32 scale; + f32 offset; + u16 timer; + + if (this->actor.projectedW < 50.0f) { + scale = 10.0f; + } else { + if (950.0f < this->actor.projectedW) { + scale = 310.0f; + } else { + scale = this->actor.projectedW; + scale = ((scale - 50.0f) * (1.0f / 3.0f)) + 10.0f; + } + } + + timer = this->timer; + if (timer != 0) { + this->timer -= 1; + + if (this->timer >= 8) { + offset = ((this->timer - 8) * (1.0f / 24.0f)); + offset = SQ(offset); + this->radius = (((1.0f - offset) * scale) + 10.0f); + this->unk_160 += ((2.0f - this->unk_160) * 0.1f); + if (this->timer < 16) { + if (1) {} + this->alpha = ((this->timer * 0x23) - 0x118); + } + } + } + + if (this->timer >= 9) { + if (this->unk_164 < 1.0f) { + this->unk_164 += 0.25f; + } + } else { + if (this->unk_164 > 0.0f) { + this->unk_164 -= 0.125f; + } + } + + if (this->timer < 8) { + this->alpha = 0; + } + + if (this->timer == 0) { + this->timer = 255; + Actor_Kill(&this->actor); + } +} + +void ArrowLight_Fly(ArrowLight* this, GlobalContext* globalCtx) { + EnArrow* arrow = (EnArrow*)this->actor.parent; + f32 distanceScaled; + s32 pad; + + if ((arrow == NULL) || (arrow->actor.update == NULL)) { + Actor_Kill(&this->actor); + return; + } + // copy position and rotation from parent arrow + this->actor.world.pos = arrow->actor.world.pos; + this->actor.shape.rot = arrow->actor.shape.rot; + distanceScaled = Math_Vec3f_DistXYZ(&this->unkPos, &this->actor.world.pos) * (1.0f / 24.0f); + this->unk_160 = distanceScaled; + if (distanceScaled < 1.0f) { + this->unk_160 = 1.0f; + } + func_80869E6C(&this->unkPos, &this->actor.world.pos, 0.05f); + + if (arrow->hitFlags & 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_EXPLOSION_LIGHT); + ArrowLight_SetupAction(this, ArrowLight_Hit); + this->timer = 32; + this->alpha = 255; + } else if (arrow->timer < 34) { + if (this->alpha < 35) { + Actor_Kill(&this->actor); + } else { + this->alpha -= 0x19; + } + } +} + +void ArrowLight_Update(Actor* thisx, GlobalContext* globalCtx) { + ArrowLight* this = (ArrowLight*)thisx; + + if (globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK || + globalCtx->msgCtx.msgMode == MSGMODE_SONG_PLAYED) { + Actor_Kill(&this->actor); + } else { + this->actionFunc(this, globalCtx); + } +} + +void ArrowLight_Draw(Actor* thisx, GlobalContext* globalCtx) { + ArrowLight* this = (ArrowLight*)thisx; + s32 pad; + u32 stateFrames = globalCtx->state.frames; + EnArrow* arrow = (EnArrow*)this->actor.parent; + Actor* tranform; + + if (1) {} + + if ((arrow != NULL) && (arrow->actor.update != NULL) && (this->timer < 255)) { + if (1) {} + tranform = (arrow->hitFlags & 2) ? &this->actor : &arrow->actor; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_arrow_light.c", 598); + + Matrix_Translate(tranform->world.pos.x, tranform->world.pos.y, tranform->world.pos.z, MTXMODE_NEW); + Matrix_RotateY(tranform->shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(tranform->shape.rot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(tranform->shape.rot.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + + // Draw yellow effect over the screen when arrow hits + if (this->unk_164 > 0) { + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (s32)(30.0f * this->unk_164) & 0xFF, + (s32)(40.0f * this->unk_164) & 0xFF, 0, (s32)(150.0f * this->unk_164) & 0xFF); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + } + + // Draw light on the arrow + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 170, this->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 128); + Matrix_RotateZYX(0x4000, 0x0, 0x0, MTXMODE_APPLY); + if (this->timer != 0) { + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 1500.0f, 0.0f, MTXMODE_APPLY); + } + Matrix_Scale(this->radius * 0.2f, this->unk_160 * 4.0f, this->radius * 0.2f, MTXMODE_APPLY); + Matrix_Translate(0.0f, -700.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_arrow_light.c", 648), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 511 - (stateFrames * 5) % 512, 0, 4, 32, 1, + 511 - (stateFrames * 10) % 512, 511 - (stateFrames * 30) % 512, 8, 16)); + gSPDisplayList(POLY_XLU_DISP++, sModelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_arrow_light.c", 664); + } +} diff --git a/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.h b/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.h new file mode 100644 index 000000000..79db3621a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.h @@ -0,0 +1,22 @@ +#ifndef Z_ARROW_LIGHT_H +#define Z_ARROW_LIGHT_H + +#include "ultra64.h" +#include "global.h" + +struct ArrowLight; + +typedef void (*ArrowLightActionFunc)(struct ArrowLight*, GlobalContext*); + +typedef struct ArrowLight { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 radius; + /* 0x014E */ u16 timer; + /* 0x0150 */ u8 alpha; + /* 0x0154 */ Vec3f unkPos; + /* 0x0160 */ f32 unk_160; + /* 0x0164 */ f32 unk_164; + /* 0x0168 */ ArrowLightActionFunc actionFunc; +} ArrowLight; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.c b/soh/src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.c new file mode 100644 index 000000000..e02315768 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.c @@ -0,0 +1,463 @@ +/* + * File: z_bg_bdan_objects.c + * Overlay: ovl_Bg_Bdan_Objects + * Description: Lord Jabu-Jabu Objects + */ + +#include "z_bg_bdan_objects.h" +#include "objects/object_bdan_objects/object_bdan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgBdanObjects_Init(Actor* thisx, GlobalContext* globalCtx); +void BgBdanObjects_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgBdanObjects_Update(Actor* thisx, GlobalContext* globalCtx); +void BgBdanObjects_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8086C054(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C1A0(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C29C(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C55C(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C5BC(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C618(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C6EC(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C76C(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C7D0(BgBdanObjects* this, GlobalContext* globalCtx); +void BgBdanObjects_DoNothing(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C874(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C9A8(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086C9F0(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086CABC(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086CB10(BgBdanObjects* this, GlobalContext* globalCtx); +void func_8086CB8C(BgBdanObjects* this, GlobalContext* globalCtx); + +const ActorInit Bg_Bdan_Objects_InitVars = { + ACTOR_BG_BDAN_OBJECTS, + ACTORCAT_BG, + FLAGS, + OBJECT_BDAN_OBJECTS, + sizeof(BgBdanObjects), + (ActorFunc)BgBdanObjects_Init, + (ActorFunc)BgBdanObjects_Destroy, + (ActorFunc)BgBdanObjects_Update, + (ActorFunc)BgBdanObjects_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_NONE, + OCELEM_NONE, + }, + { 0x00BB, 0x0050, 0x0000, { 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static Gfx* sDLists[] = { + gJabuObjectsLargeRotatingSpikePlatformDL, + gJabuElevatorPlatformDL, + gJabuWaterDL, + gJabuFallingPlatformDL, +}; + +s32 BgBdanObjects_GetContactRu1(BgBdanObjects* this, s32 arg1) { + switch (arg1) { + case 0: + return this->cameraSetting == CAM_SET_NORMAL0; + case 4: + return gSaveContext.infTable[20] & 0x40; + case 3: + return this->cameraSetting == CAM_SET_DUNGEON1; + default: + osSyncPrintf("Bg_Bdan_Objects_Get_Contact_Ru1\nそんな受信モードは無い%d!!!!!!!!\n"); + return -1; + } +} + +void BgBdanObjects_SetContactRu1(BgBdanObjects* this, s32 arg1) { + switch (arg1) { + case 1: + this->cameraSetting = CAM_SET_NORMAL1; + break; + case 2: + this->cameraSetting = CAM_SET_DUNGEON0; + break; + case 4: + gSaveContext.infTable[20] |= 0x40; + break; + default: + osSyncPrintf("Bg_Bdan_Objects_Set_Contact_Ru1\nそんな送信モードは無い%d!!!!!!!!\n"); + } +} + +void BgBdanObjects_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgBdanObjects* this = (BgBdanObjects*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + this->switchFlag = (thisx->params >> 8) & 0x3F; + thisx->params &= 0xFF; + if (thisx->params == 2) { + thisx->flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + globalCtx->colCtx.colHeader->waterBoxes[7].ySurface = thisx->world.pos.y; + this->actionFunc = func_8086C9A8; + return; + } + if (thisx->params == 0) { + CollisionHeader_GetVirtual(&gJabuBigOctoPlatformCol, &colHeader); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + thisx->world.pos.y += -79.0f; + if (Flags_GetClear(globalCtx, thisx->room)) { + Flags_SetSwitch(globalCtx, this->switchFlag); + this->actionFunc = func_8086C6EC; + } else { + if (BgBdanObjects_GetContactRu1(this, 4)) { + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_BIGOKUTA, + thisx->home.pos.x, thisx->home.pos.y, thisx->home.pos.z, 0, + thisx->shape.rot.y + 0x8000, 0, 3) != NULL) { + thisx->child->world.pos.z = thisx->child->home.pos.z + 263.0f; + } + thisx->world.rot.y = 0; + this->actionFunc = func_8086C618; + thisx->world.pos.y = thisx->home.pos.y + -70.0f; + } else { + Flags_SetSwitch(globalCtx, this->switchFlag); + this->timer = 0; + this->actionFunc = func_8086C054; + } + } + } else { + if (thisx->params == 1) { + CollisionHeader_GetVirtual(&gJabuElevatorCol, &colHeader); + this->timer = 512; + this->switchFlag = 0; + this->actionFunc = func_8086C874; + } else { + CollisionHeader_GetVirtual(&gJabuLoweringPlatformCol, &colHeader); + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actionFunc = BgBdanObjects_DoNothing; + thisx->world.pos.y = thisx->home.pos.y - 400.0f; + } else { + this->actionFunc = func_8086CB10; + } + } + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); +} + +void BgBdanObjects_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgBdanObjects* this = (BgBdanObjects*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if (thisx->params == 0) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void func_8086C054(BgBdanObjects* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (BgBdanObjects_GetContactRu1(this, 0)) { + if (this->dyna.actor.xzDistToPlayer < 250.0f) { + BgBdanObjects_SetContactRu1(this, 1); + this->timer = 20; + OnePointCutscene_Init(globalCtx, 3070, -99, &this->dyna.actor, MAIN_CAM); + player->actor.world.pos.x = -1130.0f; + player->actor.world.pos.y = -1025.0f; + player->actor.world.pos.z = -3300.0f; + func_800AA000(0.0f, 0xFF, 0x14, 0x96); + } + } else if (this->timer != 0) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + this->actionFunc = func_8086C1A0; + } + } + + if (!Gameplay_InCsMode(globalCtx) && !BgBdanObjects_GetContactRu1(this, 0)) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + -79.0f; + } else { + this->dyna.actor.world.pos.y = (this->dyna.actor.home.pos.y + -79.0f) - 5.0f; + } +} + +void func_8086C1A0(BgBdanObjects* this, GlobalContext* globalCtx) { + if (Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 500.0f, 0.5f, 7.5f, 1.0f) < + 0.1f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSTAND_STOP_A); + this->actionFunc = func_8086C29C; + this->timer = 30; + BgBdanObjects_SetContactRu1(this, 2); + func_800AA000(0.0f, 0xFF, 0x14, 0x96); + } else { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + func_800AA000(0.0f, 0x78, 0x14, 0xA); + this->timer = 11; + } + func_8002F974(&this->dyna.actor, NA_SE_EV_BUYOSTAND_RISING - SFX_FLAG); + } +} + +void func_8086C29C(BgBdanObjects* this, GlobalContext* globalCtx) { + s32 temp; + + if (this->timer != 0) { + this->timer--; + if (this->timer == 0) { + temp = Quake_Add(GET_ACTIVE_CAM(globalCtx), 1); + Quake_SetSpeed(temp, 0x3A98); + Quake_SetQuakeValues(temp, 0, 1, 0xFA, 1); + Quake_SetCountdown(temp, 0xA); + } + } + + if (BgBdanObjects_GetContactRu1(this, 3)) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_BIGOKUTA, + this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y + 140.0f, + this->dyna.actor.world.pos.z, 0, this->dyna.actor.shape.rot.y + 0x8000, 0, 0); + BgBdanObjects_SetContactRu1(this, 4); + this->timer = 10; + this->actionFunc = func_8086C55C; + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + } +} + +void func_8086C3D8(BgBdanObjects* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->dyna.actor.velocity.y += 0.5f; + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + -70.0f, + this->dyna.actor.velocity.y)) { + this->dyna.actor.world.rot.y = 0; + this->timer = 60; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSTAND_STOP_U); + this->dyna.actor.child->world.pos.y = this->dyna.actor.world.pos.y + 140.0f; + this->actionFunc = func_8086C5BC; + OnePointCutscene_Init(globalCtx, 3080, -99, this->dyna.actor.child, MAIN_CAM); + player->actor.world.pos.x = -1130.0f; + player->actor.world.pos.y = -1025.0f; + player->actor.world.pos.z = -3500.0f; + player->actor.shape.rot.y = 0x7530; + player->actor.world.rot.y = player->actor.shape.rot.y; + func_800AA000(0.0f, 0xFF, 0x1E, 0x96); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_BUYOSTAND_FALL - SFX_FLAG); + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + func_800AA000(0.0f, 0x78, 0x14, 0xA); + this->timer = 11; + } + if (this->dyna.actor.child != NULL) { + this->dyna.actor.child->world.pos.y = this->dyna.actor.world.pos.y + 140.0f; + } + } +} + +void func_8086C55C(BgBdanObjects* this, GlobalContext* globalCtx) { + this->timer--; + + if (this->timer == 0) { + Flags_UnsetSwitch(globalCtx, this->switchFlag); + } else if (this->timer == -40) { + this->timer = 0; + this->actionFunc = func_8086C3D8; + } +} + +void func_8086C5BC(BgBdanObjects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if ((this->timer == 0) && (this->dyna.actor.child != NULL)) { + if (this->dyna.actor.child->params == 2) { + this->actionFunc = func_8086C618; + } else if (this->dyna.actor.child->params == 0) { + this->dyna.actor.child->params = 1; + } + } +} + +void func_8086C618(BgBdanObjects* this, GlobalContext* globalCtx) { + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (Flags_GetClear(globalCtx, this->dyna.actor.room)) { + Flags_SetSwitch(globalCtx, this->switchFlag); + this->dyna.actor.home.rot.y = (s16)(this->dyna.actor.shape.rot.y + 0x2000) & 0xC000; + this->actionFunc = func_8086C6EC; + } else { + this->dyna.actor.shape.rot.y += this->dyna.actor.world.rot.y; + func_800F436C(&this->dyna.actor.projectedPos, 0x2063, ABS(this->dyna.actor.world.rot.y) / 512.0f); + } +} + +void func_8086C6EC(BgBdanObjects* this, GlobalContext* globalCtx) { + s32 cond = Math_ScaledStepToS(&this->dyna.actor.shape.rot.y, this->dyna.actor.home.rot.y, 0x200); + + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + -125.0f, 3.0f)) { + if (cond) { + this->actionFunc = func_8086C76C; + } + } +} + +void func_8086C76C(BgBdanObjects* this, GlobalContext* globalCtx) { + if (func_8004356C(&this->dyna)) { + if (this->dyna.actor.xzDistToPlayer < 120.0f) { + this->actionFunc = func_8086C7D0; + OnePointCutscene_Init(globalCtx, 3090, -99, &this->dyna.actor, MAIN_CAM); + } + } +} + +void func_8086C7D0(BgBdanObjects* this, GlobalContext* globalCtx) { + if (Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 965.0f, 0.5f, 15.0f, 0.2f) < + 0.01f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSTAND_STOP_A); + this->actionFunc = BgBdanObjects_DoNothing; + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_BUYOSTAND_RISING - SFX_FLAG); + } +} + +void BgBdanObjects_DoNothing(BgBdanObjects* this, GlobalContext* globalCtx) { +} + +void func_8086C874(BgBdanObjects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->switchFlag == 0) { + if (func_8004356C(&this->dyna)) { + this->cameraSetting = globalCtx->cameraPtrs[MAIN_CAM]->setting; + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_NORMAL2); + func_8005AD1C(globalCtx->cameraPtrs[MAIN_CAM], 4); + this->switchFlag = 10; + } + } else { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_NORMAL2); + if (!func_8004356C(&this->dyna)) { + if (this->switchFlag != 0) { + this->switchFlag--; + } + } + if (this->switchFlag == 0) { + if (1) {} + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], this->cameraSetting); + func_8005ACFC(globalCtx->cameraPtrs[MAIN_CAM], 4); + } + } + this->dyna.actor.world.pos.y = + this->dyna.actor.home.pos.y - (sinf(this->timer * (M_PI / 256.0f)) * 471.24f); // pi * 150 + if (this->timer == 0) { + this->timer = 512; + } +} + +void func_8086C9A8(BgBdanObjects* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->timer = 100; + this->actionFunc = func_8086C9F0; + } +} + +void func_8086C9F0(BgBdanObjects* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 0.5f)) { + Flags_UnsetSwitch(globalCtx, this->switchFlag); + this->actionFunc = func_8086C9A8; + } + func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } else { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 75.0f, 0.5f)) { + this->actionFunc = func_8086CABC; + } + func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } + globalCtx->colCtx.colHeader->waterBoxes[7].ySurface = this->dyna.actor.world.pos.y; +} + +void func_8086CABC(BgBdanObjects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + func_8002F994(&this->dyna.actor, this->timer); + if (this->timer == 0) { + this->actionFunc = func_8086C9F0; + } +} + +void func_8086CB10(BgBdanObjects* this, GlobalContext* globalCtx) { + if (func_8004356C(&this->dyna)) { + Flags_SetSwitch(globalCtx, this->switchFlag); + this->timer = 50; + this->actionFunc = func_8086CB8C; + this->dyna.actor.home.pos.y -= 200.0f; + OnePointCutscene_Init(globalCtx, 3100, 51, &this->dyna.actor, MAIN_CAM); + } +} + +void func_8086CB8C(BgBdanObjects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - (cosf(this->timer * (M_PI / 50.0f)) * 200.0f); + + if (this->timer == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSTAND_STOP_U); + this->actionFunc = BgBdanObjects_DoNothing; + Gameplay_CopyCamera(globalCtx, MAIN_CAM, SUBCAM_ACTIVE); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_BUYOSTAND_FALL - SFX_FLAG); + } +} + +void BgBdanObjects_Update(Actor* thisx, GlobalContext* globalCtx) { + BgBdanObjects* this = (BgBdanObjects*)thisx; + + Actor_SetFocus(thisx, 50.0f); + this->actionFunc(this, globalCtx); +} + +void BgBdanObjects_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgBdanObjects* this = (BgBdanObjects*)thisx; + + if (thisx->params == 0) { + if (this->actionFunc == func_8086C054) { + if (((thisx->home.pos.y + -79.0f) - 5.0f) < thisx->world.pos.y) { + Matrix_Translate(0.0f, -50.0f, 0.0f, MTXMODE_APPLY); + } + } + } + + if (thisx->params == 2) { + Gfx_DrawDListXlu(globalCtx, gJabuWaterDL); + } else { + Gfx_DrawDListOpa(globalCtx, sDLists[thisx->params]); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.h b/soh/src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.h new file mode 100644 index 000000000..e21fe6c96 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_BDAN_OBJECTS_H +#define Z_BG_BDAN_OBJECTS_H + +#include "ultra64.h" +#include "global.h" + +struct BgBdanObjects; + +typedef void (*BgBdanObjectsActionFunc)(struct BgBdanObjects*, GlobalContext*); + +typedef struct BgBdanObjects { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgBdanObjectsActionFunc actionFunc; + /* 0x0168 */ u8 switchFlag; + /* 0x016A */ s16 timer; + /* 0x016C */ ColliderCylinder collider; + /* 0x01B8 */ s32 cameraSetting; +} BgBdanObjects; // size = 0x01BC + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.c b/soh/src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.c new file mode 100644 index 000000000..2574d29eb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.c @@ -0,0 +1,535 @@ +/* + * File: z_bg_bdan_switch.c + * Overlay: ovl_Bg_Bdan_Switch + * Description: Switches Inside Lord Jabu-Jabu + */ + +#include "z_bg_bdan_switch.h" +#include "objects/object_bdan_objects/object_bdan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgBdanSwitch_Init(Actor* thisx, GlobalContext* globalCtx); +void BgBdanSwitch_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgBdanSwitch_Update(Actor* thisx, GlobalContext* globalCtx); +void BgBdanSwitch_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8086D5C4(BgBdanSwitch* this); +void func_8086D5E0(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D67C(BgBdanSwitch* this); +void func_8086D694(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D730(BgBdanSwitch* this); +void func_8086D754(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D7FC(BgBdanSwitch* this); +void func_8086D80C(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D86C(BgBdanSwitch* this); +void func_8086D888(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D8BC(BgBdanSwitch* this); +void func_8086D8CC(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D95C(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086D9F8(BgBdanSwitch* this); +void func_8086DA1C(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086DAB4(BgBdanSwitch* this); +void func_8086DAC4(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086DB24(BgBdanSwitch* this); +void func_8086DB40(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086DB4C(BgBdanSwitch* this); +void func_8086DB68(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086DC30(BgBdanSwitch* this); +void func_8086DC48(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086DCCC(BgBdanSwitch* this); +void func_8086DCE8(BgBdanSwitch* this, GlobalContext* globalCtx); +void func_8086DDA8(BgBdanSwitch* this); +void func_8086DDC0(BgBdanSwitch* this, GlobalContext* globalCtx); + +const ActorInit Bg_Bdan_Switch_InitVars = { + ACTOR_BG_BDAN_SWITCH, + ACTORCAT_SWITCH, + FLAGS, + OBJECT_BDAN_OBJECTS, + sizeof(BgBdanSwitch), + (ActorFunc)BgBdanSwitch_Init, + (ActorFunc)BgBdanSwitch_Destroy, + (ActorFunc)BgBdanSwitch_Update, + (ActorFunc)BgBdanSwitch_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xEFC1FFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 120, 0 }, 370 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1200, ICHAIN_STOP), +}; + +static Vec3f D_8086E0E0 = { 0.0f, 140.0f, 0.0f }; + +void BgBdanSwitch_InitDynaPoly(BgBdanSwitch* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flag) { + s16 pad1; + CollisionHeader* colHeader = NULL; + s16 pad2; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_bdan_switch.c", 325, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgBdanSwitch_InitCollision(BgBdanSwitch* this, GlobalContext* globalCtx) { + Actor* actor = &this->dyna.actor; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, actor, &sJntSphInit, this->colliderItems); +} + +void func_8086D0EC(BgBdanSwitch* this) { + if (this->unk_1CC > 0) { + this->unk_1CC += 0x5DC; + } else { + this->unk_1CC += 0xFA0; + } + + switch (this->dyna.actor.params & 0xFF) { + case BLUE: + case YELLOW_HEAVY: + case YELLOW: + this->unk_1D4 = ((Math_CosS(this->unk_1CC) * 0.5f) + (53.000004f / 6.0f)) * 0.012f; + this->unk_1D0 = ((Math_CosS(this->unk_1CC) * 0.5f) + 20.5f) * (this->unk_1C8 * 0.0050000004f); + this->dyna.actor.scale.y = this->unk_1C8 * 0.1f; + break; + case YELLOW_TALL_1: + case YELLOW_TALL_2: + this->unk_1D4 = ((Math_CosS(this->unk_1CC) * 0.5f) + (43.0f / 6.0f)) * 0.0075000003f; + this->unk_1D0 = ((Math_CosS(this->unk_1CC) * 0.5f) + 20.5f) * (this->unk_1C8 * 0.0050000004f); + this->dyna.actor.scale.y = this->unk_1C8 * 0.1f; + } + this->dyna.actor.shape.yOffset = 1.2f / this->unk_1D0; +} + +void BgBdanSwitch_Init(Actor* thisx, GlobalContext* globalCtx) { + BgBdanSwitch* this = (BgBdanSwitch*)thisx; + s32 pad; + s16 type; + s32 flag; + + type = this->dyna.actor.params & 0xFF; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + if (type == YELLOW_TALL_1 || type == YELLOW_TALL_2) { + this->dyna.actor.scale.z = 0.05f; + this->dyna.actor.scale.x = 0.05f; + } else { + this->dyna.actor.scale.z = 0.1f; + this->dyna.actor.scale.x = 0.1f; + } + this->dyna.actor.scale.y = 0.0f; + Actor_SetFocus(&this->dyna.actor, 10.0f); + + switch (type) { + case BLUE: + case YELLOW_HEAVY: + case YELLOW: + BgBdanSwitch_InitDynaPoly(this, globalCtx, &gJabuFloorSwitchCol, DPM_PLAYER); + break; + case YELLOW_TALL_1: + case YELLOW_TALL_2: + BgBdanSwitch_InitCollision(this, globalCtx); + this->dyna.actor.flags |= ACTOR_FLAG_0; + this->dyna.actor.targetMode = 4; + break; + } + + flag = Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + + switch (type) { + case BLUE: + case YELLOW: + if (flag) { + func_8086D730(this); + } else { + func_8086D5C4(this); + } + break; + case YELLOW_HEAVY: + if (flag) { + func_8086DB24(this); + } else { + func_8086D86C(this); + } + break; + case YELLOW_TALL_1: + case YELLOW_TALL_2: + if (flag) { + func_8086DCCC(this); + } else { + func_8086DB4C(this); + } + break; + default: + osSyncPrintf("不正な ARG_DATA(arg_data 0x%04x)(%s %d)\n", this->dyna.actor.params, "../z_bg_bdan_switch.c", + 454); + Actor_Kill(&this->dyna.actor); + return; + } + osSyncPrintf("(巨大魚ダンジョン 専用スイッチ)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgBdanSwitch_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgBdanSwitch* this = (BgBdanSwitch*)thisx; + + switch (this->dyna.actor.params & 0xFF) { + case BLUE: + case YELLOW_HEAVY: + case YELLOW: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + break; + case YELLOW_TALL_1: + case YELLOW_TALL_2: + Collider_DestroyJntSph(globalCtx, &this->collider); + break; + } +} + +void func_8086D4B4(BgBdanSwitch* this, GlobalContext* globalCtx) { + s32 pad; + s32 type; + + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + type = this->dyna.actor.params & 0xFF; + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + if (type == BLUE || type == YELLOW_TALL_2) { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); + } else { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->dyna.actor, NA_SE_SY_CORRECT_CHIME); + } + } +} + +void func_8086D548(BgBdanSwitch* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + Flags_UnsetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + if ((this->dyna.actor.params & 0xFF) == YELLOW_TALL_2) { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); + } + } +} + +void func_8086D5C4(BgBdanSwitch* this) { + this->actionFunc = func_8086D5E0; + this->unk_1C8 = 1.0f; +} + +void func_8086D5E0(BgBdanSwitch* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xFF) { + case BLUE: + if (func_800435B4(&this->dyna)) { + func_8086D67C(this); + func_8086D4B4(this, globalCtx); + } + break; + case YELLOW: + if (func_8004356C(&this->dyna)) { + func_8086D67C(this); + func_8086D4B4(this, globalCtx); + } + break; + } +} + +void func_8086D67C(BgBdanSwitch* this) { + this->actionFunc = func_8086D694; + this->unk_1DA = 0x64; +} + +void func_8086D694(BgBdanSwitch* this, GlobalContext* globalCtx) { + if ((func_8005B198() == this->dyna.actor.category) || (this->unk_1DA <= 0)) { + this->unk_1C8 -= 0.2f; + if (this->unk_1C8 <= 0.1f) { + func_8086D730(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 0x78, 0x14, 0xA); + } + } +} + +void func_8086D730(BgBdanSwitch* this) { + this->unk_1C8 = 0.1f; + this->actionFunc = func_8086D754; + this->unk_1D8 = 6; +} + +void func_8086D754(BgBdanSwitch* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xFF) { + case BLUE: + if (!func_800435B4(&this->dyna)) { + if (this->unk_1D8 <= 0) { + func_8086D7FC(this); + func_8086D548(this, globalCtx); + } + } else { + this->unk_1D8 = 6; + } + break; + case YELLOW: + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + func_8086D7FC(this); + } + break; + } +} + +void func_8086D7FC(BgBdanSwitch* this) { + this->actionFunc = func_8086D80C; +} + +void func_8086D80C(BgBdanSwitch* this, GlobalContext* globalCtx) { + this->unk_1C8 += 0.2f; + if (this->unk_1C8 >= 1.0f) { + func_8086D5C4(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + } +} + +void func_8086D86C(BgBdanSwitch* this) { + this->actionFunc = func_8086D888; + this->unk_1C8 = 1.0f; +} + +void func_8086D888(BgBdanSwitch* this, GlobalContext* globalCtx) { + if (func_8004356C(&this->dyna)) { + func_8086D8BC(this); + } +} + +void func_8086D8BC(BgBdanSwitch* this) { + this->actionFunc = func_8086D8CC; +} + +void func_8086D8CC(BgBdanSwitch* this, GlobalContext* globalCtx) { + this->unk_1C8 -= 0.2f; + if (this->unk_1C8 <= 0.6f) { + func_8086D9F8(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 0x78, 0x14, 0xA); + } +} + +void func_8086D944(BgBdanSwitch* this) { + this->actionFunc = func_8086D95C; + this->unk_1DA = 0x64; +} + +void func_8086D95C(BgBdanSwitch* this, GlobalContext* globalCtx) { + if ((func_8005B198() == this->dyna.actor.category) || (this->unk_1DA <= 0)) { + this->unk_1C8 -= 0.2f; + if (this->unk_1C8 <= 0.1f) { + func_8086DB24(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 0x78, 0x14, 0xA); + } + } +} + +void func_8086D9F8(BgBdanSwitch* this) { + this->unk_1C8 = 0.6f; + this->actionFunc = func_8086DA1C; + this->unk_1D8 = 6; +} + +void func_8086DA1C(BgBdanSwitch* this, GlobalContext* globalCtx) { + Actor* heldActor = GET_PLAYER(globalCtx)->heldActor; + + if (func_8004356C(&this->dyna)) { + if (heldActor != NULL && heldActor->id == ACTOR_EN_RU1) { + if (this->unk_1D8 <= 0) { + func_8086D944(this); + func_8086D4B4(this, globalCtx); + } + } else { + this->unk_1D8 = 6; + } + } else { + if (this->unk_1D8 <= 0) { + func_8086DAB4(this); + } + } +} + +void func_8086DAB4(BgBdanSwitch* this) { + this->actionFunc = func_8086DAC4; +} + +void func_8086DAC4(BgBdanSwitch* this, GlobalContext* globalCtx) { + this->unk_1C8 += 0.2f; + if (this->unk_1C8 >= 1.0f) { + func_8086D86C(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + } +} + +void func_8086DB24(BgBdanSwitch* this) { + this->unk_1C8 = 0.1f; + this->actionFunc = func_8086DB40; +} + +void func_8086DB40(BgBdanSwitch* this, GlobalContext* globalCtx) { +} + +void func_8086DB4C(BgBdanSwitch* this) { + this->actionFunc = func_8086DB68; + this->unk_1C8 = 2.0f; +} + +void func_8086DB68(BgBdanSwitch* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xFF) { + default: + return; + case YELLOW_TALL_1: + if (((this->collider.base.acFlags & AC_HIT) != 0) && this->unk_1D8 <= 0) { + this->unk_1D8 = 0xA; + func_8086DC30(this); + func_8086D4B4(this, globalCtx); + } + break; + case YELLOW_TALL_2: + if (((this->collider.base.acFlags & AC_HIT) != 0) && ((this->unk_1DC & 2) == 0) && this->unk_1D8 <= 0) { + this->unk_1D8 = 0xA; + func_8086DC30(this); + func_8086D4B4(this, globalCtx); + } + break; + } +} + +void func_8086DC30(BgBdanSwitch* this) { + this->actionFunc = func_8086DC48; + this->unk_1DA = 0x64; +} + +void func_8086DC48(BgBdanSwitch* this, GlobalContext* globalCtx) { + if ((func_8005B198() == this->dyna.actor.category) || (this->unk_1DA <= 0)) { + this->unk_1C8 -= 0.3f; + if (this->unk_1C8 <= 1.0f) { + func_8086DCCC(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + } + } +} + +void func_8086DCCC(BgBdanSwitch* this) { + this->actionFunc = func_8086DCE8; + this->unk_1C8 = 1.0f; +} + +void func_8086DCE8(BgBdanSwitch* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xFF) { + case YELLOW_TALL_1: + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + func_8086DDA8(this); + } + break; + case YELLOW_TALL_2: + if (((this->collider.base.acFlags & AC_HIT) != 0) && ((this->unk_1DC & 2) == 0) && (this->unk_1D8 <= 0)) { + this->unk_1D8 = 0xA; + func_8086DDA8(this); + func_8086D548(this, globalCtx); + } + break; + } +} + +void func_8086DDA8(BgBdanSwitch* this) { + this->actionFunc = func_8086DDC0; + this->unk_1DA = 0x64; +} + +void func_8086DDC0(BgBdanSwitch* this, GlobalContext* globalCtx) { + if ((((this->dyna.actor.params & 0xFF) != YELLOW_TALL_2) || (func_8005B198() == this->dyna.actor.category)) || + (this->unk_1DA <= 0)) { + this->unk_1C8 += 0.3f; + if (this->unk_1C8 >= 2.0f) { + func_8086DB4C(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + } + } +} + +void BgBdanSwitch_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgBdanSwitch* this = (BgBdanSwitch*)thisx; + s32 type; + + if (this->unk_1DA > 0) { + this->unk_1DA--; + } + this->actionFunc(this, globalCtx); + func_8086D0EC(this); + type = this->dyna.actor.params & 0xFF; + if (type != 3 && type != 4) { + this->unk_1D8--; + } else { + if (!Player_InCsMode(globalCtx) && this->unk_1D8 > 0) { + this->unk_1D8--; + } + this->unk_1DC = this->collider.base.acFlags; + this->collider.base.acFlags &= ~AC_HIT; + this->collider.elements[0].dim.modelSphere.radius = this->unk_1D4 * 370.0f; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_8086DF58(BgBdanSwitch* this, GlobalContext* globalCtx, Gfx* dlist) { + Matrix_SetTranslateRotateYXZ(this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y + (this->dyna.actor.shape.yOffset * this->unk_1D0), + this->dyna.actor.world.pos.z, &this->dyna.actor.shape.rot); + Matrix_Scale(this->unk_1D4, this->unk_1D0, this->unk_1D4, MTXMODE_APPLY); + Gfx_DrawDListOpa(globalCtx, dlist); +} + +void BgBdanSwitch_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgBdanSwitch* this = (BgBdanSwitch*)thisx; + + switch (this->dyna.actor.params & 0xFF) { + case YELLOW_HEAVY: + case YELLOW: + func_8086DF58(this, globalCtx, gJabuYellowFloorSwitchDL); + break; + case YELLOW_TALL_1: + case YELLOW_TALL_2: + func_8086DF58(this, globalCtx, gJabuYellowFloorSwitchDL); + Collider_UpdateSpheres(0, &this->collider); + Matrix_MultVec3f(&D_8086E0E0, &this->dyna.actor.focus.pos); + break; + case BLUE: + func_8086DF58(this, globalCtx, gJabuBlueFloorSwitchDL); + break; + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.h b/soh/src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.h new file mode 100644 index 000000000..629643c7e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bdan_Switch/z_bg_bdan_switch.h @@ -0,0 +1,36 @@ +#ifndef Z_BG_BDAN_SWITCH_H +#define Z_BG_BDAN_SWITCH_H + +#include "ultra64.h" +#include "global.h" + +// BgBdanSwitch.actor.params & 0xFF +typedef enum { + /* 0x00 */ BLUE, + /* 0x01 */ YELLOW_HEAVY, + /* 0x02 */ YELLOW, + /* 0x03 */ YELLOW_TALL_1, + /* 0x04 */ YELLOW_TALL_2 +} BgBdanSwitchType; + +struct BgBdanSwitch; + +typedef void (*BgBdanSwitchActionFunc)(struct BgBdanSwitch*, GlobalContext*); + +typedef struct BgBdanSwitch { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgBdanSwitchActionFunc actionFunc; + /* 0x0168 */ ColliderJntSph collider; + /* 0x0188 */ ColliderJntSphElement colliderItems[1]; + /* 0x01C8 */ f32 unk_1C8; + /* 0x01CC */ s16 unk_1CC; + /* 0x01CE */ char unk_1CE[0x2]; + /* 0x01D0 */ f32 unk_1D0; + /* 0x01D4 */ f32 unk_1D4; + /* 0x01D8 */ s16 unk_1D8; + /* 0x01DA */ s16 unk_1DA; + /* 0x01DC */ u8 unk_1DC; + /* 0x01DD */ char unk_1DD[0x3]; +} BgBdanSwitch; // size = 0x01E0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Bom_Guard/z_bg_bom_guard.c b/soh/src/overlays/actors/ovl_Bg_Bom_Guard/z_bg_bom_guard.c new file mode 100644 index 000000000..ed289bbb2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bom_Guard/z_bg_bom_guard.c @@ -0,0 +1,90 @@ +/* + * File: z_bg_bom_guard.c + * Overlay: Bg_Bom_Guard + * Description: Bombchu Bowling Alley Walls + */ + +#include "z_bg_bom_guard.h" +#include "overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h" +#include "objects/object_bowl/object_bowl.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgBomGuard_Init(Actor* thisx, GlobalContext* globalCtx); +void BgBomGuard_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgBomGuard_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_8086E638(BgBomGuard* this, GlobalContext* globalCtx); + +const ActorInit Bg_Bom_Guard_InitVars = { + ACTOR_BG_BOM_GUARD, + ACTORCAT_PROP, + FLAGS, + OBJECT_BOWL, + sizeof(BgBomGuard), + (ActorFunc)BgBomGuard_Init, + (ActorFunc)BgBomGuard_Destroy, + (ActorFunc)BgBomGuard_Update, + NULL, + NULL, +}; + +void BgBomGuard_SetupAction(BgBomGuard* this, BgBomGuardActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgBomGuard_Init(Actor* thisx, GlobalContext* globalCtx) { + BgBomGuard* this = (BgBomGuard*)thisx; + s32 pad[2]; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gBowlingDefaultCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 透明ガード出現 ☆☆☆☆☆ \n" VT_RST); + + thisx->scale.x = 1.0f; + thisx->scale.y = 1.0f; + thisx->scale.z = 1.0f; + this->unk_16C = thisx->world.pos; + BgBomGuard_SetupAction(this, func_8086E638); +} + +void BgBomGuard_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgBomGuard* this = (BgBomGuard*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8086E638(BgBomGuard* this, GlobalContext* globalCtx) { + Actor* it = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + Actor* thisx = &this->dyna.actor; + + this->unk_168 = 0; + + while (it != 0) { + if (it->id == ACTOR_EN_BOM_BOWL_MAN) { + if ((((EnBomBowlMan*)it)->minigamePlayStatus != 0) && (fabsf(globalCtx->view.eye.x) > -20.0f) && + (fabsf(globalCtx->view.eye.y) > 110.0f)) { + this->unk_168 = 1; + } + break; + } + it = it->next; + } + + if (this->unk_168 == 0) { + thisx->world.pos.y = sREG(64) + -200.0f; + } else { + thisx->world.pos.y = 0.0f; + } +} + +void BgBomGuard_Update(Actor* thisx, GlobalContext* globalCtx) { + BgBomGuard* this = (BgBomGuard*)thisx; + + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Bom_Guard/z_bg_bom_guard.h b/soh/src/overlays/actors/ovl_Bg_Bom_Guard/z_bg_bom_guard.h new file mode 100644 index 000000000..41a395a38 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bom_Guard/z_bg_bom_guard.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_BOM_GUARD_H +#define Z_BG_BOM_GUARD_H + +#include "ultra64.h" +#include "global.h" + +struct BgBomGuard; + +typedef void (*BgBomGuardActionFunc)(struct BgBomGuard*, GlobalContext*); + +typedef struct BgBomGuard { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgBomGuardActionFunc actionFunc; + /* 0x0168 */ u8 unk_168; + /* 0x016C */ Vec3f unk_16C; +} BgBomGuard; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Bombwall/z_bg_bombwall.c b/soh/src/overlays/actors/ovl_Bg_Bombwall/z_bg_bombwall.c new file mode 100644 index 000000000..49532c000 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bombwall/z_bg_bombwall.c @@ -0,0 +1,260 @@ +/* + * File: z_bg_bombwall.c + * Overlay: ovl_Bg_Bombwall + * Description: Bombable Wall + */ + +#include "z_bg_bombwall.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS ACTOR_FLAG_22 + +void BgBombwall_Init(Actor* thisx, GlobalContext* globalCtx); +void BgBombwall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgBombwall_Update(Actor* thisx, GlobalContext* globalCtx); +void BgBombwall_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8086ED50(BgBombwall* this, GlobalContext* globalCtx); +void func_8086ED70(BgBombwall* this, GlobalContext* globalCtx); +void func_8086EDFC(BgBombwall* this, GlobalContext* globalCtx); +void func_8086EE40(BgBombwall* this, GlobalContext* globalCtx); +void func_8086EE94(BgBombwall* this, GlobalContext* globalCtx); + +static ColliderTrisElementInit sTrisElementsInit[3] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000048, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -70.0f, 176.0f, 0.0f }, { -70.0f, -4.0f, 0.0f }, { 0.0f, -4.0f, 30.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000048, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 70.0f, 176.0f, 0.0f }, { -70.0f, 176.0f, 0.0f }, { 0.0f, -4.0f, 30.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000048, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 70.0f, -4.0f, 0.0f }, { 70.0f, 176.0f, 0.0f }, { 0.0f, -4.0f, 30.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 3, + sTrisElementsInit, +}; + +const ActorInit Bg_Bombwall_InitVars = { + ACTOR_BG_BOMBWALL, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(BgBombwall), + (ActorFunc)BgBombwall_Init, + (ActorFunc)BgBombwall_Destroy, + (ActorFunc)BgBombwall_Update, + (ActorFunc)BgBombwall_Draw, + NULL, +}; + +void BgBombwall_InitDynapoly(BgBombwall* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gBgBombwallCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG login failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_bombwall.c", 243, + this->dyna.actor.params); + } +} + +void BgBombwall_RotateVec(Vec3f* arg0, Vec3f* arg1, f32 arg2, f32 arg3) { + arg0->x = (arg1->z * arg2) + (arg1->x * arg3); + arg0->y = arg1->y; + arg0->z = (arg1->z * arg3) - (arg1->x * arg2); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgBombwall_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 i; + s32 j; + Vec3f vecs[3]; + Vec3f sp80; + s32 pad; + BgBombwall* this = (BgBombwall*)thisx; + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + Actor_SetScale(&this->dyna.actor, 0.1f); + + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + func_8086EE94(this, globalCtx); + } else { + BgBombwall_InitDynapoly(this, globalCtx); + this->unk_2A2 |= 2; + Collider_InitTris(globalCtx, &this->collider); + Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInit, this->colliderItems); + + for (i = 0; i <= 2; i++) { + for (j = 0; j <= 2; j++) { + sp80.x = sTrisInit.elements[i].dim.vtx[j].x; + sp80.y = sTrisInit.elements[i].dim.vtx[j].y; + sp80.z = sTrisInit.elements[i].dim.vtx[j].z + 2.0f; + + BgBombwall_RotateVec(&vecs[j], &sp80, sin, cos); + + vecs[j].x += this->dyna.actor.world.pos.x; + vecs[j].y += this->dyna.actor.world.pos.y; + vecs[j].z += this->dyna.actor.world.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &vecs[0], &vecs[1], &vecs[2]); + } + + this->unk_2A2 |= 1; + func_8086ED50(this, globalCtx); + } + + osSyncPrintf("(field keep 汎用爆弾壁)(arg_data 0x%04x)(angY %d)\n", this->dyna.actor.params, + this->dyna.actor.shape.rot.y); +} + +void BgBombwall_DestroyCollision(BgBombwall* this, GlobalContext* globalCtx) { + if (this->unk_2A2 & 2) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->unk_2A2 &= ~2; + } + + if (this->unk_2A2 & 1) { + Collider_DestroyTris(globalCtx, &this->collider); + this->unk_2A2 &= ~1; + } +} + +void BgBombwall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgBombwall* this = (BgBombwall*)thisx; + + BgBombwall_DestroyCollision(this, globalCtx); +} + +static Vec3s D_8086F010[] = { + { 40, 85, 21 }, { -43, 107, 14 }, { -1, 142, 14 }, { -27, 44, 27 }, { 28, 24, 20 }, { -39, 54, 21 }, { 49, 50, 20 }, +}; + +void func_8086EB5C(BgBombwall* this, GlobalContext* globalCtx) { + s16 rand; + s16 rand2; + Vec3f sp88; + s32 i; + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + Vec3f* pos = &this->dyna.actor.world.pos; + f32 temp; + f32 new_var; + + for (i = 0; i < 7; i++) { + new_var = D_8086F010[i].x; + temp = new_var * cos; + sp88.x = ((sin * D_8086F010[i].z) + ((f32)temp)) + pos->x; + sp88.y = pos->y + D_8086F010[i].y; + sp88.z = ((D_8086F010[i].z * cos) - (sin * D_8086F010[i].x)) + pos->z; + rand = ((s16)(Rand_ZeroOne() * 120.0f)) + 0x14; + rand2 = ((s16)(Rand_ZeroOne() * 240.0f)) + 0x14; + func_80033480(globalCtx, &sp88, 50.0f, 2, rand, rand2, 1); + } + + sp88.x = pos->x; + new_var = pos->y + 90.0f; + sp88.y = pos->y + 90.0f; + sp88.z = pos->z + 15.0f; + func_80033480(globalCtx, &sp88, 40.0f, 4, 0xA, 0x32, 1); +} + +void func_8086ED50(BgBombwall* this, GlobalContext* globalCtx) { + this->dList = gBgBombwallNormalDL; + this->actionFunc = func_8086ED70; +} + +void func_8086ED70(BgBombwall* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + func_8086EDFC(this, globalCtx); + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + } else if (this->dyna.actor.xzDistToPlayer < 600.0f) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_8086EDFC(BgBombwall* this, GlobalContext* globalCtx) { + this->dList = gBgBombwallNormalDL; + this->unk_2A0 = 1; + func_8086EB5C(this, globalCtx); + this->actionFunc = func_8086EE40; +} + +void func_8086EE40(BgBombwall* this, GlobalContext* globalCtx) { + if (this->unk_2A0 > 0) { + this->unk_2A0--; + } else { + func_8086EE94(this, globalCtx); + + if (((this->dyna.actor.params >> 0xF) & 1) != 0) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + } +} + +void func_8086EE94(BgBombwall* this, GlobalContext* globalCtx) { + this->dList = gBgBombwallBrokenDL; + BgBombwall_DestroyCollision(this, globalCtx); + this->actionFunc = NULL; +} + +void BgBombwall_Update(Actor* thisx, GlobalContext* globalCtx) { + BgBombwall* this = (BgBombwall*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgBombwall_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgBombwall* this = (BgBombwall*)thisx; + + Gfx_DrawDListOpa(globalCtx, this->dList); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Bombwall/z_bg_bombwall.h b/soh/src/overlays/actors/ovl_Bg_Bombwall/z_bg_bombwall.h new file mode 100644 index 000000000..68e17e028 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bombwall/z_bg_bombwall.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_BOMBWALL_H +#define Z_BG_BOMBWALL_H + +#include "ultra64.h" +#include "global.h" + +struct BgBombwall; + +typedef void (*BgBombwallActionFunc)(struct BgBombwall*, GlobalContext*); + +typedef struct BgBombwall { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderTris collider; + /* 0x0184 */ ColliderTrisElement colliderItems[3]; + /* 0x0298 */ BgBombwallActionFunc actionFunc; + /* 0x029C */ Gfx* dList; + /* 0x02A0 */ s16 unk_2A0; + /* 0x02A2 */ u8 unk_2A2; + /* 0x02A3 */ u8 unk_2A3; +} BgBombwall; // size = 0x02A4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.c b/soh/src/overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.c new file mode 100644 index 000000000..9c81bb686 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.c @@ -0,0 +1,221 @@ +/* + * File: z_bg_bowl_wall.c + * Overlay: Bg_Bowl_Wall + * Description: Bombchu Bowling Alley Wall + */ + +#include "z_bg_bowl_wall.h" +#include "overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "objects/object_bowl/object_bowl.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgBowlWall_Init(Actor* thisx, GlobalContext* globalCtx); +void BgBowlWall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgBowlWall_Update(Actor* thisx, GlobalContext* globalCtx); +void BgBowlWall_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgBowlWall_SpawnBullseyes(BgBowlWall* this, GlobalContext* globalCtx); +void BgBowlWall_WaitForHit(BgBowlWall* this, GlobalContext* globalCtx); +void BgBowlWall_FallDoEffects(BgBowlWall* this, GlobalContext* globalCtx); +void BgBowlWall_FinishFall(BgBowlWall* this, GlobalContext* globalCtx); +void BgBowlWall_Reset(BgBowlWall* this, GlobalContext* globalCtx); + +const ActorInit Bg_Bowl_Wall_InitVars = { + ACTOR_BG_BOWL_WALL, + ACTORCAT_PROP, + FLAGS, + OBJECT_BOWL, + sizeof(BgBowlWall), + (ActorFunc)BgBowlWall_Init, + (ActorFunc)BgBowlWall_Destroy, + (ActorFunc)BgBowlWall_Update, + (ActorFunc)BgBowlWall_Draw, + NULL, +}; + +static Vec3f sBullseyeOffset[] = { + { 0.0f, 210.0f, -20.0f }, + { 0.0f, 170.0f, -20.0f }, + { -170.0f, 0.0f, -20.0f }, + { 170.0f, 0.0f, -20.0f }, +}; + +static s16 sTargetRot[] = { 0x0000, 0x0000, 0x3FFF, -0x3FFF }; + +void BgBowlWall_Init(Actor* thisx, GlobalContext* globalCtx) { + BgBowlWall* this = (BgBowlWall*)thisx; + s32 pad1; + s32 pad2; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + + if (this->dyna.actor.params == 0) { + CollisionHeader_GetVirtual(&gBowlingFirstAndFinalRoundCol, &colHeader); + } else { + CollisionHeader_GetVirtual(&gBowlingSecondRoundCol, &colHeader); + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->initPos = this->dyna.actor.world.pos; + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ ボーリングおじゃま壁発生 ☆☆☆☆☆ %d\n" VT_RST, this->dyna.actor.params); + this->actionFunc = BgBowlWall_SpawnBullseyes; + this->dyna.actor.scale.x = this->dyna.actor.scale.y = this->dyna.actor.scale.z = 1.0f; +} + +void BgBowlWall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgBowlWall* this = (BgBowlWall*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgBowlWall_SpawnBullseyes(BgBowlWall* this, GlobalContext* globalCtx) { + s32 pad; + EnWallTubo* bullseye; + Actor* lookForGirl; + s16 type; + + type = this->dyna.actor.params; + if (type != 0) { + type += (s16)Rand_ZeroFloat(2.99f); + this->dyna.actor.shape.rot.z = this->dyna.actor.world.rot.z = sTargetRot[type]; + osSyncPrintf("\n\n"); + } + this->bullseyeCenter.x = sBullseyeOffset[type].x + this->dyna.actor.world.pos.x; + this->bullseyeCenter.y = sBullseyeOffset[type].y + this->dyna.actor.world.pos.y; + this->bullseyeCenter.z = sBullseyeOffset[type].z + this->dyna.actor.world.pos.z; + if (1) {} + bullseye = (EnWallTubo*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_WALL_TUBO, + this->bullseyeCenter.x, this->bullseyeCenter.y, this->bullseyeCenter.z, + 0, 0, 0, this->dyna.actor.params); + if (bullseye != NULL) { + bullseye->explosionCenter = this->bullseyeCenter; + if (type != 0) { + bullseye->explosionCenter = this->bullseyeCenter = this->dyna.actor.world.pos; + } + if (this->chuGirl == NULL) { + lookForGirl = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + while (lookForGirl != NULL) { + if (lookForGirl->id != ACTOR_EN_BOM_BOWL_MAN) { + lookForGirl = lookForGirl->next; + } else { + this->chuGirl = (EnBomBowlMan*)lookForGirl; + break; + } + } + } + this->actionFunc = BgBowlWall_WaitForHit; + } +} + +void BgBowlWall_WaitForHit(BgBowlWall* this, GlobalContext* globalCtx) { + if (this->isHit) { + this->actionFunc = BgBowlWall_FallDoEffects; + } +} + +void BgBowlWall_FallDoEffects(BgBowlWall* this, GlobalContext* globalCtx) { + s16 pad; + Vec3f effectAccel = { 0.0f, 0.1f, 0.0f }; + Vec3f effectVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f effectPos; + s16 quakeIndex; + s32 wallFallen; + s32 i; + + wallFallen = false; + + if (this->dyna.actor.params == 0) { // wall collapses backwards + Math_SmoothStepToS(&this->dyna.actor.shape.rot.x, -0x3E80, 3, 500, 0); + this->dyna.actor.world.rot.x = this->dyna.actor.shape.rot.x; + if (this->dyna.actor.shape.rot.x < -0x3C1E) { + wallFallen = true; + } + } else { // wall slides downwards + Math_ApproachF(&this->dyna.actor.world.pos.y, this->initPos.y - 450.0f, 0.3f, 10.0f); + if (this->dyna.actor.world.pos.y < (this->initPos.y - 400.0f)) { + wallFallen = true; + } + } + + if (wallFallen) { + for (i = 0; i < 15; i++) { + effectPos.x = Rand_CenteredFloat(300.0f) + this->bullseyeCenter.x; + effectPos.y = -100.0f; + effectPos.z = Rand_CenteredFloat(400.0f) + this->bullseyeCenter.z; + EffectSsBomb2_SpawnLayered(globalCtx, &effectPos, &effectVelocity, &effectAccel, 100, 30); + effectPos.y = -50.0f; + EffectSsHahen_SpawnBurst(globalCtx, &effectPos, 10.0f, 0, 50, 15, 3, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_IT_BOMB_EXPLOSION); + } + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 1); + Quake_SetSpeed(quakeIndex, 0x7FFF); + Quake_SetQuakeValues(quakeIndex, 300, 0, 0, 0); + Quake_SetCountdown(quakeIndex, 30); + this->timer = 20; + this->actionFunc = BgBowlWall_FinishFall; + } +} + +void BgBowlWall_FinishFall(BgBowlWall* this, GlobalContext* globalCtx) { + if (this->timer >= 2) { + if (this->dyna.actor.params == 0) { + Math_SmoothStepToS(&this->dyna.actor.shape.rot.x, -0x3E80, 1, 200, 0); + } else { + Math_ApproachF(&this->dyna.actor.world.pos.y, this->initPos.y - 450.0f, 0.3f, 10.0f); + } + } else if (this->timer == 1) { + this->dyna.actor.world.rot.x = this->dyna.actor.shape.rot.x = 0; + this->dyna.actor.world.pos.y = this->initPos.y - 450.0f; + this->chuGirl->wallStatus[this->dyna.actor.params] = 2; + this->actionFunc = BgBowlWall_Reset; + } +} + +void BgBowlWall_Reset(BgBowlWall* this, GlobalContext* globalCtx) { + if (this->chuGirl->wallStatus[this->dyna.actor.params] != 2) { + Math_ApproachF(&this->dyna.actor.world.pos.y, this->initPos.y, 0.3f, 50.0f); + if (fabsf(this->dyna.actor.world.pos.y - this->initPos.y) <= 10.0f) { + this->dyna.actor.world.pos.y = this->initPos.y; + this->isHit = false; + this->actionFunc = BgBowlWall_SpawnBullseyes; + } + } +} + +void BgBowlWall_Update(Actor* thisx, GlobalContext* globalCtx) { + BgBowlWall* this = (BgBowlWall*)thisx; + + if (this->timer != 0) { + this->timer--; + } + + this->actionFunc(this, globalCtx); +} + +void BgBowlWall_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgBowlWall* this = (BgBowlWall*)thisx; + u32 frames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_bowl_wall.c", 441); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x8, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, -2 * (frames = globalCtx->state.frames), 16, 16)); + gDPPipeSync(POLY_OPA_DISP++); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_bowl_wall.c", 453), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->dyna.actor.params == 0) { + gSPDisplayList(POLY_OPA_DISP++, gBowlingRound1WallDL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gBowlingRound2WallDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_bowl_wall.c", 464); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.h b/soh/src/overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.h new file mode 100644 index 000000000..0a5b7a512 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_BOWL_WALL_H +#define Z_BG_BOWL_WALL_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h" + +struct BgBowlWall; + +typedef void (*BgBowlWallActionFunc)(struct BgBowlWall*, GlobalContext*); + +typedef struct BgBowlWall { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgBowlWallActionFunc actionFunc; + /* 0x0168 */ Vec3f initPos; + /* 0x0174 */ Vec3f bullseyeCenter; + /* 0x0180 */ s16 isHit; + /* 0x0182 */ s16 timer; + /* 0x0184 */ EnBomBowlMan* chuGirl; +} BgBowlWall; // size = 0x0188 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c new file mode 100644 index 000000000..5340c49b0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c @@ -0,0 +1,320 @@ +/* + * File: z_bg_breakwall.c + * Overlay: Bg_Breakwall + * Description: Bombable Wall + */ + +#include "z_bg_breakwall.h" +#include "scenes/dungeons/ddan/ddan_scene.h" +#include "objects/object_bwall/object_bwall.h" +#include "objects/object_kingdodongo/object_kingdodongo.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef struct { + /* 0x00 */ CollisionHeader* colHeader; + /* 0x04 */ Gfx* dList; + /* 0x08 */ s8 colType; +} BombableWallInfo; + +void BgBreakwall_Init(Actor* thisx, GlobalContext* globalCtx); +void BgBreakwall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgBreakwall_Update(Actor* thisx, GlobalContext* globalCtx); +void BgBreakwall_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgBreakwall_WaitForObject(BgBreakwall* this, GlobalContext* globalCtx); +void BgBreakwall_Wait(BgBreakwall* this, GlobalContext* globalCtx); +void BgBreakwall_LavaCoverMove(BgBreakwall* this, GlobalContext* globalCtx); + +const ActorInit Bg_Breakwall_InitVars = { + ACTOR_BG_BREAKWALL, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgBreakwall), + (ActorFunc)BgBreakwall_Init, + (ActorFunc)BgBreakwall_Destroy, + (ActorFunc)BgBreakwall_Update, + NULL, + NULL, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER | AC_TYPE_OTHER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x00000048, 0x00, 0x00 }, + { 0x00000048, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static BombableWallInfo sBombableWallInfo[] = { + { &object_bwall_Col_000118, object_bwall_DL_000040, 0 }, + { &object_bwall_Col_000118, object_bwall_DL_000040, 0 }, + { &object_kingdodongo_Col_0264A8, object_kingdodongo_DL_025BD0, 1 }, + { &object_kingdodongo_Col_025B64, NULL, -1 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), +}; + +void BgBreakwall_SetupAction(BgBreakwall* this, BgBreakwallActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgBreakwall_Init(Actor* thisx, GlobalContext* globalCtx) { + BgBreakwall* this = (BgBreakwall*)thisx; + s32 pad; + s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + this->bombableWallDList = sBombableWallInfo[wallType].dList; + this->colType = sBombableWallInfo[wallType].colType; + + if (this->colType == 1) { + this->dyna.actor.world.rot.x = 0x4000; + } + + if (this->bombableWallDList != NULL) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + Actor_Kill(&this->dyna.actor); + return; + } + + ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->dyna.actor, &sQuadInit); + } else { + this->dyna.actor.world.pos.y -= 40.0f; + } + + this->bankIndex = (wallType >= BWALL_KD_FLOOR) ? Object_GetIndex(&globalCtx->objectCtx, OBJECT_KINGDODONGO) + : Object_GetIndex(&globalCtx->objectCtx, OBJECT_BWALL); + + if (this->bankIndex < 0) { + Actor_Kill(&this->dyna.actor); + } else { + BgBreakwall_SetupAction(this, BgBreakwall_WaitForObject); + } +} + +void BgBreakwall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgBreakwall* this = (BgBreakwall*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +/** + * Spawns fragments using ACTOR_EN_A_OBJ whenever the wall or floor is exploded. + * Returns the last spawned actor + */ +Actor* BgBreakwall_SpawnFragments(GlobalContext* globalCtx, BgBreakwall* this, Vec3f* pos, f32 velocity, f32 scaleY, + f32 scaleX, s32 count, f32 accel) { + Actor* actor; + Vec3f actorPos; + s32 k; + s32 j; + s32 i; + s16 angle1; + s16 angle2 = 0; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; // unused + Vec3s actorRotList[] = { { 0, 0, 0 }, { 0, 0, 0x4000 }, { 0, 0, -0x4000 }, { 0, 0, 0 } }; + Vec3f actorScaleList[] = { + { 0.004f, 0.004f, 0.004f }, + { 0.004f, 0.004f, 0.004f }, + { 0.004f, 0.004f, 0.004f }, + { 0.004f, 0.004f, 0.004f }, + }; + Vec3f actorPosList[][4] = { + { { 40.0f, 15.0f, 0.0f }, { 30.0f, 57.0f, 0.0f }, { 50.0f, 57.0f, 0.0f }, { 40.0f, 70.0f, 0.0f } }, + { { 55.0f, -15.0f, 0.0f }, { 30.0f, -32.0f, 0.0f }, { 50.0f, -32.0f, 0.0f }, { 20.0f, -10.0f, 0.0f } }, + { { -40.0f, 14.0f, 0.0f }, { -50.0f, 57.0f, 0.0f }, { -30.0f, 57.0f, 0.0f }, { -40.0f, 70.0f, 0.0f } }, + { { -55.0f, -15.0f, 0.0f }, { -55.0f, -32.0f, 0.0f }, { -30.0f, -32.0f, 0.0f }, { -20.0f, -10.0f, 0.0f } }, + }; + s32 pad; + + for (k = 3; k >= 0; k--) { + if ((k == 0) || (k == 3)) { + actorScaleList[k].x *= scaleX; + actorScaleList[k].y *= scaleY; + actorScaleList[k].z *= scaleY; + } else { + actorScaleList[k].x *= scaleY; + actorScaleList[k].y *= scaleX; + actorScaleList[k].z *= scaleX; + } + } + + for (i = 0; i < count; angle2 += 0x4000, i++) { + angle1 = ABS(this->dyna.actor.world.rot.y) + angle2; + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateZYX(this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, this->dyna.actor.world.rot.z, + MTXMODE_APPLY); + Matrix_Translate(pos->x, pos->y, pos->z, MTXMODE_APPLY); + + for (j = 3; j >= 0; j--) { + for (k = 3; k >= 0; k--) { + Matrix_MultVec3f(&actorPosList[j][k], &actorPos); + actor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_A_OBJ, Rand_CenteredFloat(20.0f) + actorPos.x, + Rand_CenteredFloat(20.0f) + actorPos.y, Rand_CenteredFloat(20.0f) + actorPos.z, + actorRotList[k].x, actorRotList[k].y + angle1, actorRotList[k].z, 0x000B); + + if ((j & 1) == 0) { + func_80033480(globalCtx, &actorPos, velocity * 200.0f, 1, 650, 150, 1); + } + + if (actor != NULL) { + actor->speedXZ = Rand_ZeroOne() + (accel * 0.6f); + actor->velocity.y = Rand_ZeroOne() + (accel * 0.6f); + actor->world.rot.y += (s16)((Rand_ZeroOne() - 0.5f) * 3000.0f); + actor->world.rot.x = (s16)(Rand_ZeroOne() * 3500.0f) + 2000; + actor->world.rot.z = (s16)(Rand_ZeroOne() * 3500.0f) + 2000; + actor->parent = &this->dyna.actor; + actor->scale.x = actorScaleList[k].x + Rand_CenteredFloat(0.001f); + actor->scale.y = actorScaleList[k].y + Rand_CenteredFloat(0.001f); + actor->scale.z = actorScaleList[k].z + Rand_CenteredFloat(0.001f); + } + } + } + } + + return actor; +} + +/** + * Sets up the collision model as well is the object dependency and action function to use. + */ +void BgBreakwall_WaitForObject(BgBreakwall* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->bankIndex)) { + CollisionHeader* colHeader = NULL; + s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; + + this->dyna.actor.objBankIndex = this->bankIndex; + Actor_SetObjectDependency(globalCtx, &this->dyna.actor); + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + this->dyna.actor.draw = BgBreakwall_Draw; + CollisionHeader_GetVirtual(sBombableWallInfo[wallType].colHeader, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (wallType == BWALL_KD_LAVA_COVER) { + BgBreakwall_SetupAction(this, BgBreakwall_LavaCoverMove); + } else { + BgBreakwall_SetupAction(this, BgBreakwall_Wait); + } + } +} + +/** + * Checks for an explosion using quad collision. If the wall or floor is exploded then it will spawn fragments and + * despawn itself. + */ +void BgBreakwall_Wait(BgBreakwall* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & 2) { + Vec3f effectPos; + s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + effectPos.y = effectPos.z = effectPos.x = 0.0f; + + if (this->dyna.actor.world.rot.x == 0) { + effectPos.y = 55.0f; + } else { + effectPos.z = 25.0f; + effectPos.y = -10.0f; + } + + BgBreakwall_SpawnFragments(globalCtx, this, &effectPos, 0.0f, 6.4f, 5.0f, 1, 2.0f); + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + + if (wallType == BWALL_KD_FLOOR) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_EXPLOSION); + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WALL_BROKEN); + } + + if ((wallType == BWALL_DC_ENTRANCE) && (!(Flags_GetEventChkInf(0xB0)))) { + Flags_SetEventChkInf(0xB0); + Cutscene_SetSegment(globalCtx, gDcOpeningCs); + gSaveContext.cutsceneTrigger = 1; + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_8002DF54(globalCtx, NULL, 0x31); + } + + if (this->dyna.actor.params < 0) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + Actor_Kill(&this->dyna.actor); + } +} + +/** + * Moves the actor's y position to cover the lava floor in King Dodongo's lair after he is defeated so the player is no + * longer hurt by the lava. + */ +void BgBreakwall_LavaCoverMove(BgBreakwall* this, GlobalContext* globalCtx) { + Math_StepToF(&this->dyna.actor.world.pos.y, KREG(80) + this->dyna.actor.home.pos.y, 1.0f); +} + +void BgBreakwall_Update(Actor* thisx, GlobalContext* globalCtx) { + BgBreakwall* this = (BgBreakwall*)thisx; + + this->actionFunc(this, globalCtx); +} + +/** + * These are the quads used for the wall and floor collision. These are used for the detecting when a bomb explosion has + * collided with a wall, and can be adjusted for different wall or floor sizes. + */ +static Vec3f sColQuadList[][4] = { + { { 800.0f, 1600.0f, 100.0f }, { -800.0f, 1600.0f, 100.0f }, { 800.0f, 0.0f, 100.0f }, { -800.0f, 0.0f, 100.0f } }, + { { 10.0f, 0.0f, 10.0f }, { -10.0f, 0.0f, 10.0f }, { 10.0f, 0.0f, -10.0f }, { -10.0f, 0.0f, -10.0f } }, +}; + +void BgBreakwall_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgBreakwall* this = (BgBreakwall*)thisx; + + if (this->bombableWallDList != NULL) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_breakwall.c", 767); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_breakwall.c", 771), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, this->bombableWallDList); + + if (this->colType >= 0) { + Vec3f colQuad[4]; + Vec3f* src = &sColQuadList[this->colType][0]; + Vec3f* dst = &colQuad[0]; + s32 i; + + for (i = 0; i < 4; i++) { + Matrix_MultVec3f(src++, dst++); + } + + Collider_SetQuadVertices(&this->collider, &colQuad[0], &colQuad[1], &colQuad[2], &colQuad[3]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_breakwall.c", 822); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.h b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.h new file mode 100644 index 000000000..43fd4ee93 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.h @@ -0,0 +1,27 @@ +#ifndef Z_BG_BREAKWALL_H +#define Z_BG_BREAKWALL_H + +#include "ultra64.h" +#include "global.h" + +struct BgBreakwall; + +typedef void (*BgBreakwallActionFunc)(struct BgBreakwall*, GlobalContext*); + +typedef struct BgBreakwall { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ Gfx* bombableWallDList; + /* 0x0168 */ s8 colType; + /* 0x0169 */ s8 bankIndex; + /* 0x016C */ ColliderQuad collider; + /* 0x01EC */ BgBreakwallActionFunc actionFunc; +} BgBreakwall; // size = 0x01F0 + +typedef enum { + /* 0 */ BWALL_DC_ENTRANCE, // When exploded it will play the Dodongo's Cavern intro cutscene + /* 1 */ BWALL_WALL, // Used a lot in Dodongo's Cavern and other places + /* 2 */ BWALL_KD_FLOOR, // Used in the King Dodongo boss room + /* 3 */ BWALL_KD_LAVA_COVER // Spawned after the KD fight in order to cover the lava floor to disable damage +} BombableWallType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ddan_Jd/z_bg_ddan_jd.c b/soh/src/overlays/actors/ovl_Bg_Ddan_Jd/z_bg_ddan_jd.c new file mode 100644 index 000000000..f256c6e6e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ddan_Jd/z_bg_ddan_jd.c @@ -0,0 +1,180 @@ +/* + * File: z_bg_ddan_jd.c + * Overlay: ovl_Bg_Ddan_Jd + * Description: Rising stone platform (Dodongo's Cavern) + */ + +#include "z_bg_ddan_jd.h" +#include "objects/object_ddan_objects/object_ddan_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgDdanJd_Init(Actor* thisx, GlobalContext* globalCtx); +void BgDdanJd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgDdanJd_Update(Actor* thisx, GlobalContext* globalCtx); +void BgDdanJd_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgDdanJd_Idle(BgDdanJd* this, GlobalContext* globalCtx); +void BgDdanJd_Move(BgDdanJd* this, GlobalContext* globalCtx); + +const ActorInit Bg_Ddan_Jd_InitVars = { + ACTOR_BG_DDAN_JD, + ACTORCAT_BG, + FLAGS, + OBJECT_DDAN_OBJECTS, + sizeof(BgDdanJd), + (ActorFunc)BgDdanJd_Init, + (ActorFunc)BgDdanJd_Destroy, + (ActorFunc)BgDdanJd_Update, + (ActorFunc)BgDdanJd_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +typedef enum { + /* 0 */ STATE_GO_BOTTOM, + /* 1 */ STATE_GO_MIDDLE_FROM_BOTTOM, + /* 2 */ STATE_GO_MIDDLE_FROM_TOP, + /* 3 */ STATE_GO_TOP +} BgDdanJdState; + +#define MOVE_HEIGHT_MIDDLE 140.0f +#define MOVE_HEIGHT_TOP 700.0f + +#define IDLE_FRAMES 100 + +// Since ySpeed is used to determine if the platform should rise to the top of the dungeon, these must be assigned +// different values in order for the shortcut to work correctly +#define DEFAULT_Y_SPEED 1 +#define SHORTCUT_Y_SPEED 5 + +void BgDdanJd_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgDdanJd* this = (BgDdanJd*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gDodongoRisingPlatformCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->idleTimer = IDLE_FRAMES; + this->state = STATE_GO_BOTTOM; + + // Missing check for actor.params < 0x40. This will cause inconsistent behavior if params >= 0x40 and the bound + // switch state is turned on while in the same room, as the shortcut behavior won't become enabled until the actor + // is reloaded. + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params)) { + this->ySpeed = SHORTCUT_Y_SPEED; + } else { + this->ySpeed = DEFAULT_Y_SPEED; + } + this->actionFunc = BgDdanJd_Idle; +} + +void BgDdanJd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgDdanJd* this = (BgDdanJd*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgDdanJd_Idle(BgDdanJd* this, GlobalContext* globalCtx) { + if (this->idleTimer != 0) { + this->idleTimer--; + } + + // if this is the platform that rises all the way to the top, and the switch state has just changed to on + if (this->ySpeed == DEFAULT_Y_SPEED && this->dyna.actor.params < 0x40 && + Flags_GetSwitch(globalCtx, this->dyna.actor.params)) { + this->ySpeed = SHORTCUT_Y_SPEED; + this->state = STATE_GO_MIDDLE_FROM_BOTTOM; + this->idleTimer = 0; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + MOVE_HEIGHT_MIDDLE; + OnePointCutscene_Init(globalCtx, 3060, -99, &this->dyna.actor, MAIN_CAM); + } + if (this->idleTimer == 0) { + this->idleTimer = IDLE_FRAMES; + if (this->state == STATE_GO_BOTTOM) { + this->state = STATE_GO_MIDDLE_FROM_BOTTOM; + this->targetY = this->dyna.actor.home.pos.y + MOVE_HEIGHT_MIDDLE; + } else if (this->state == STATE_GO_MIDDLE_FROM_BOTTOM) { + // If the platform has been activated as a shortcut + if (this->ySpeed != DEFAULT_Y_SPEED) { + this->state = STATE_GO_TOP; + this->targetY = this->dyna.actor.home.pos.y + MOVE_HEIGHT_TOP; + } else { + this->state = STATE_GO_BOTTOM; + this->targetY = this->dyna.actor.home.pos.y; + } + } else if (this->state == STATE_GO_MIDDLE_FROM_TOP) { + // If the platform has been activated as a shortcut + if (this->ySpeed != DEFAULT_Y_SPEED) { + this->state = STATE_GO_TOP; + this->targetY = this->dyna.actor.home.pos.y + MOVE_HEIGHT_TOP; + } else { + this->state = STATE_GO_BOTTOM; + this->targetY = this->dyna.actor.home.pos.y; + } + } else if (this->state == STATE_GO_TOP) { + this->state = STATE_GO_MIDDLE_FROM_TOP; + this->targetY = this->dyna.actor.home.pos.y + MOVE_HEIGHT_MIDDLE; + } + this->actionFunc = BgDdanJd_Move; + } +} + +// Handles dust particles and sfx when moving +void BgDdanJd_MoveEffects(BgDdanJd* this, GlobalContext* globalCtx) { + Vec3f dustPos; + + // Generate random dust particles at the platform's base. + dustPos.y = this->dyna.actor.home.pos.y; + if (globalCtx->gameplayFrames & 1) { + dustPos.x = this->dyna.actor.world.pos.x + 65.0f; + dustPos.z = Rand_CenteredFloat(110.0f) + this->dyna.actor.world.pos.z; + func_80033480(globalCtx, &dustPos, 5.0f, 1, 20, 60, 1); + dustPos.x = this->dyna.actor.world.pos.x - 65.0f; + dustPos.z = Rand_CenteredFloat(110.0f) + this->dyna.actor.world.pos.z; + func_80033480(globalCtx, &dustPos, 5.0f, 1, 20, 60, 1); + } else { + dustPos.x = Rand_CenteredFloat(110.0f) + this->dyna.actor.world.pos.x; + dustPos.z = this->dyna.actor.world.pos.z + 65.0f; + func_80033480(globalCtx, &dustPos, 5.0f, 1, 20, 60, 1); + dustPos.x = Rand_CenteredFloat(110.0f) + this->dyna.actor.world.pos.x; + dustPos.z = this->dyna.actor.world.pos.z - 65.0f; + func_80033480(globalCtx, &dustPos, 5.0f, 1, 20, 60, 1); + } + if (this->ySpeed == SHORTCUT_Y_SPEED) { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + } +} + +// Implements the platform's movement state +void BgDdanJd_Move(BgDdanJd* this, GlobalContext* globalCtx) { + // if this is the platform that rises all the way to the top, and the switch state has just changed to on + if (this->ySpeed == DEFAULT_Y_SPEED && this->dyna.actor.params < 0x40 && + Flags_GetSwitch(globalCtx, this->dyna.actor.params)) { + this->ySpeed = SHORTCUT_Y_SPEED; + this->state = STATE_GO_MIDDLE_FROM_BOTTOM; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + MOVE_HEIGHT_MIDDLE; + this->idleTimer = 0; + this->actionFunc = BgDdanJd_Idle; + OnePointCutscene_Init(globalCtx, 3060, -99, &this->dyna.actor, MAIN_CAM); + } else if (Math_StepToF(&this->dyna.actor.world.pos.y, this->targetY, this->ySpeed)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_PILLAR_MOVE_STOP); + this->actionFunc = BgDdanJd_Idle; + } + BgDdanJd_MoveEffects(this, globalCtx); +} + +void BgDdanJd_Update(Actor* thisx, GlobalContext* globalCtx) { + BgDdanJd* this = (BgDdanJd*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgDdanJd_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gDodongoRisingPlatformDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ddan_Jd/z_bg_ddan_jd.h b/soh/src/overlays/actors/ovl_Bg_Ddan_Jd/z_bg_ddan_jd.h new file mode 100644 index 000000000..33ab8be8a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ddan_Jd/z_bg_ddan_jd.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_DDAN_JD_H +#define Z_BG_DDAN_JD_H + +#include "ultra64.h" +#include "global.h" + +struct BgDdanJd; + +typedef void (*BgDdanJdActionFunc)(struct BgDdanJd*, GlobalContext*); + +typedef struct BgDdanJd { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgDdanJdActionFunc actionFunc; + /* 0x0168 */ u8 state; + /* 0x0169 */ u8 ySpeed; // also differentiates between normal and shortcut platform behavior + /* 0x016A */ s16 idleTimer; + /* 0x016C */ f32 targetY; +} BgDdanJd; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.c b/soh/src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.c new file mode 100644 index 000000000..260fa21ea --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.c @@ -0,0 +1,202 @@ +/* + * File: z_bg_ddan_kd.c + * Overlay: ovl_Bg_Ddan_Kd + * Description: Falling stairs in Dodongo's Cavern + */ + +#include "z_bg_ddan_kd.h" +#include "objects/object_ddan_objects/object_ddan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgDdanKd_Init(Actor* thisx, GlobalContext* globalCtx); +void BgDdanKd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgDdanKd_Update(Actor* thisx, GlobalContext* globalCtx); +void BgDdanKd_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgDdanKd_Reset(void); + +void BgDdanKd_CheckForExplosions(BgDdanKd* this, GlobalContext* globalCtx); +void BgDdanKd_LowerStairs(BgDdanKd* this, GlobalContext* globalCtx); +void BgDdanKd_DoNothing(BgDdanKd* this, GlobalContext* globalCtx); + +const ActorInit Bg_Ddan_Kd_InitVars = { + ACTOR_BG_DDAN_KD, + ACTORCAT_BG, + FLAGS, + OBJECT_DDAN_OBJECTS, + sizeof(BgDdanKd), + (ActorFunc)BgDdanKd_Init, + (ActorFunc)BgDdanKd_Destroy, + (ActorFunc)BgDdanKd_Update, + (ActorFunc)BgDdanKd_Draw, + (ActorResetFunc)BgDdanKd_Reset, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ALL, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 245, 180, -400, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 32767, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 32767, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 32767, ICHAIN_STOP), +}; + +void BgDdanKd_SetupAction(BgDdanKd* this, BgDdanKdActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgDdanKd_Init(Actor* thisx, GlobalContext* globalCtx) { + BgDdanKd* this = (BgDdanKd*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + this->prevExplosive = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + CollisionHeader_GetVirtual(&gDodongoFallingStairsCol, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (!Flags_GetSwitch(globalCtx, this->dyna.actor.params)) { + BgDdanKd_SetupAction(this, BgDdanKd_CheckForExplosions); + } else { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - 200.0f - 20.0f; + BgDdanKd_SetupAction(this, BgDdanKd_DoNothing); + } +} + +void BgDdanKd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgDdanKd* this = (BgDdanKd*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void BgDdanKd_CheckForExplosions(BgDdanKd* this, GlobalContext* globalCtx) { + Actor* explosive; + + explosive = Actor_GetCollidedExplosive(globalCtx, &this->collider.base); + if (explosive != NULL) { + osSyncPrintf("dam %d\n", this->dyna.actor.colChkInfo.damage); + explosive->params = 2; + } + + if ((explosive != NULL) && (this->prevExplosive != NULL) && (explosive != this->prevExplosive) && + (Math_Vec3f_DistXZ(&this->prevExplosivePos, &explosive->world.pos) > 80.0f)) { + BgDdanKd_SetupAction(this, BgDdanKd_LowerStairs); + OnePointCutscene_Init(globalCtx, 3050, 999, &this->dyna.actor, MAIN_CAM); + } else { + if (this->timer != 0) { + this->timer--; + } else { + this->prevExplosive = explosive; + if (explosive != NULL) { + this->timer = 13; + this->prevExplosivePos = explosive->world.pos; + } + } + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +static Vec3f velocity = { 0.0f, 5.0f, 0.0f }; +static Vec3f accel = { 0.0f, -0.45f, 0.0f }; + +void BgDdanKd_LowerStairs(BgDdanKd* this, GlobalContext* globalCtx) { + Vec3f pos1; + Vec3f pos2; + f32 effectStrength; + + Math_SmoothStepToF(&this->dyna.actor.speedXZ, 4.0f, 0.5f, 0.025f, 0.0f); + func_800AA000(500.0f, 0x78, 0x14, 0xA); + + if (Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 200.0f - 20.0f, 0.075f, + this->dyna.actor.speedXZ, 0.0075f) == 0.0f) { + Flags_SetSwitch(globalCtx, this->dyna.actor.params); + BgDdanKd_SetupAction(this, BgDdanKd_DoNothing); + } else { + effectStrength = + (this->dyna.actor.prevPos.y - this->dyna.actor.world.pos.y) + (this->dyna.actor.speedXZ * 0.25f); + + if (globalCtx->state.frames & 1) { + pos1 = pos2 = this->dyna.actor.world.pos; + + if (globalCtx->state.frames & 2) { + pos1.z += 210.0f + Rand_ZeroOne() * 230.0f; + pos2.z += 210.0f + Rand_ZeroOne() * 230.0f; + } else { + pos1.z += 330.0f + Rand_ZeroOne() * 240.0f; + pos2.z += 330.0f + Rand_ZeroOne() * 240.0f; + } + pos1.x += 80.0f + Rand_ZeroOne() * 10.0f; + pos2.x -= 80.0f + Rand_ZeroOne() * 10.0f; + pos1.y = this->dyna.actor.floorHeight + 20.0f + Rand_ZeroOne(); + pos2.y = this->dyna.actor.floorHeight + 20.0f + Rand_ZeroOne(); + + func_80033480(globalCtx, &pos1, 20.0f, 1, effectStrength * 135.0f, 60, 1); + func_80033480(globalCtx, &pos2, 20.0f, 1, effectStrength * 135.0f, 60, 1); + + velocity.x = Rand_CenteredFloat(3.0f); + velocity.z = Rand_CenteredFloat(3.0f); + + func_8003555C(globalCtx, &pos1, &velocity, &accel); + func_8003555C(globalCtx, &pos2, &velocity, &accel); + + pos1 = this->dyna.actor.world.pos; + pos1.z += 560.0f + Rand_ZeroOne() * 5.0f; + pos1.x += (Rand_ZeroOne() - 0.5f) * 160.0f; + pos1.y = Rand_ZeroOne() * 3.0f + (this->dyna.actor.floorHeight + 20.0f); + + func_80033480(globalCtx, &pos1, 20.0f, 1, effectStrength * 135.0f, 60, 1); + func_8003555C(globalCtx, &pos1, &velocity, &accel); + } + Camera_AddQuake(&globalCtx->mainCamera, 0, effectStrength * 0.6f, 3); + Audio_PlaySoundGeneral(NA_SE_EV_PILLAR_SINK - SFX_FLAG, &this->dyna.actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } +} + +void BgDdanKd_DoNothing(BgDdanKd* this, GlobalContext* globalCtx) { +} + +void BgDdanKd_Update(Actor* thisx, GlobalContext* globalCtx) { + BgDdanKd* this = (BgDdanKd*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgDdanKd_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gDodongoFallingStairsDL); +} + +void BgDdanKd_Reset(void) { + velocity.x = 0.0f; + velocity.y = 5.0f; + velocity.z = 0.0f; + + accel.x = 0.0f; + accel.y = -0.45f; + accel.z = 0.0f; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h b/soh/src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h new file mode 100644 index 000000000..12048002b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ddan_Kd/z_bg_ddan_kd.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_DDAN_KD_H +#define Z_BG_DDAN_KD_H + +#include "ultra64.h" +#include "global.h" + +struct BgDdanKd; + +typedef void (*BgDdanKdActionFunc)(struct BgDdanKd*, GlobalContext*); + +typedef struct BgDdanKd { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ Actor* prevExplosive; + /* 0x0168 */ s16 timer; + /* 0x016C */ Vec3f prevExplosivePos; + /* 0x0178 */ ColliderCylinder collider; + /* 0x01C4 */ BgDdanKdActionFunc actionFunc; +} BgDdanKd; // size = 0x01C8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.c b/soh/src/overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.c new file mode 100644 index 000000000..a089aa013 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.c @@ -0,0 +1,328 @@ +/* + * File: z_bg_dodoago.c + * Overlay: ovl_Bg_Dodoago + * Description: Dodongo Head Statue in Dodongo's Cavern + */ + +#include "z_bg_dodoago.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/object_ddan_objects/object_ddan_objects.h" + +#define FLAGS 0 + +void BgDodoago_Init(Actor* thisx, GlobalContext* globalCtx); +void BgDodoago_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgDodoago_Update(Actor* thisx, GlobalContext* globalCtx); +void BgDodoago_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgDodoago_Reset(void); + +void BgDodoago_WaitExplosives(BgDodoago* this, GlobalContext* globalCtx); +void BgDodoago_OpenJaw(BgDodoago* this, GlobalContext* globalCtx); +void BgDodoago_DoNothing(BgDodoago* this, GlobalContext* globalCtx); +void BgDodoago_LightOneEye(BgDodoago* this, GlobalContext* globalCtx); + +const ActorInit Bg_Dodoago_InitVars = { + ACTOR_BG_DODOAGO, + ACTORCAT_BG, + FLAGS, + OBJECT_DDAN_OBJECTS, + sizeof(BgDodoago), + (ActorFunc)BgDodoago_Init, + (ActorFunc)BgDodoago_Destroy, + (ActorFunc)BgDodoago_Update, + (ActorFunc)BgDodoago_Draw, + (ActorResetFunc)BgDodoago_Reset, +}; + +static ColliderCylinderInit sColCylinderInitMain = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ALL, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 80, 30, 80, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sColCylinderInitLeftRight = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_NO_PUSH | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 50, 60, 280, { 0, 0, 0 } }, +}; + +static s16 sFirstExplosiveFlag = false; + +static u8 sDisableBombCatcher; + +static u8 sUnused[90]; // unknown length + +static s32 sTimer; + +void BgDodoago_SetupAction(BgDodoago* this, BgDodoagoActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgDodoago_SpawnSparkles(Vec3f* meanPos, GlobalContext* globalCtx) { + Vec3f pos; + Color_RGBA8 primColor = { 100, 100, 100, 0 }; + Color_RGBA8 envColor = { 40, 40, 40, 0 }; + static Vec3f velocity = { 0.0f, -1.5f, 0.0f }; + static Vec3f acceleration = { 0.0f, -0.2f, 0.0f }; + s32 i; + + for (i = 4; i > 0; i--) { + pos.x = Rand_CenteredFloat(20.0f) + meanPos->x; + pos.y = Rand_CenteredFloat(10.0f) + meanPos->y; + pos.z = Rand_CenteredFloat(20.0f) + meanPos->z; + EffectSsKiraKira_SpawnSmall(globalCtx, &pos, &velocity, &acceleration, &primColor, &envColor); + } +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 5000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), +}; + +void BgDodoago_Init(Actor* thisx, GlobalContext* globalCtx) { + BgDodoago* this = (BgDodoago*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gDodongoLowerJawCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); + + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + BgDodoago_SetupAction(this, BgDodoago_DoNothing); + this->dyna.actor.shape.rot.x = 0x1333; + globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_LEFT] = globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_RIGHT] = 255; + return; + } + + Collider_InitCylinder(globalCtx, &this->colliderMain); + Collider_InitCylinder(globalCtx, &this->colliderLeft); + Collider_InitCylinder(globalCtx, &this->colliderRight); + Collider_SetCylinder(globalCtx, &this->colliderMain, &this->dyna.actor, &sColCylinderInitMain); + Collider_SetCylinder(globalCtx, &this->colliderLeft, &this->dyna.actor, &sColCylinderInitLeftRight); + Collider_SetCylinder(globalCtx, &this->colliderRight, &this->dyna.actor, &sColCylinderInitLeftRight); + + BgDodoago_SetupAction(this, BgDodoago_WaitExplosives); + sDisableBombCatcher = false; +} + +void BgDodoago_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgDodoago* this = (BgDodoago*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->colliderMain); + Collider_DestroyCylinder(globalCtx, &this->colliderLeft); + Collider_DestroyCylinder(globalCtx, &this->colliderRight); +} + +void BgDodoago_WaitExplosives(BgDodoago* this, GlobalContext* globalCtx) { + Actor* explosive = Actor_GetCollidedExplosive(globalCtx, &this->colliderMain.base); + + if (explosive != NULL) { + this->state = + (Math_Vec3f_Yaw(&this->dyna.actor.world.pos, &explosive->world.pos) >= this->dyna.actor.shape.rot.y) + ? BGDODOAGO_EYE_RIGHT + : BGDODOAGO_EYE_LEFT; + + if (((globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_LEFT] == 255) && (this->state == BGDODOAGO_EYE_RIGHT)) || + ((globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_RIGHT] == 255) && (this->state == BGDODOAGO_EYE_LEFT))) { + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + this->state = 0; + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + BgDodoago_SetupAction(this, BgDodoago_OpenJaw); + OnePointCutscene_Init(globalCtx, 3380, 160, &this->dyna.actor, MAIN_CAM); + } else if (globalCtx->roomCtx.unk_74[this->state] == 0) { + OnePointCutscene_Init(globalCtx, 3065, 40, &this->dyna.actor, MAIN_CAM); + BgDodoago_SetupAction(this, BgDodoago_LightOneEye); + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + OnePointCutscene_Init(globalCtx, 3065, 20, &this->dyna.actor, MAIN_CAM); + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sTimer += 30; + return; + } + + // the flag is never set back to false, so this only runs once + if (!sFirstExplosiveFlag) { + // this disables the bomb catcher (see BgDodoago_Update) for a few seconds + this->dyna.actor.parent = explosive; + sFirstExplosiveFlag = true; + sTimer = 50; + } + } else if (Flags_GetEventChkInf(0xB0)) { + Collider_UpdateCylinder(&this->dyna.actor, &this->colliderMain); + Collider_UpdateCylinder(&this->dyna.actor, &this->colliderLeft); + Collider_UpdateCylinder(&this->dyna.actor, &this->colliderRight); + + this->colliderMain.dim.pos.z += 200; + + this->colliderLeft.dim.pos.z += 215; + this->colliderLeft.dim.pos.x += 90; + + this->colliderRight.dim.pos.z += 215; + this->colliderRight.dim.pos.x -= 90; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderMain.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderLeft.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderRight.base); + } +} + +void BgDodoago_OpenJaw(BgDodoago* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f dustOffsets[] = { + { 0.0f, -200.0f, 430.0f }, { 20.0f, -200.0f, 420.0f }, { -20.0f, -200.0f, 420.0f }, + { 40.0, -200.0f, 380.0f }, { -40.0, -200.0f, 380.0f }, { 50.0, -200.0f, 350.0f }, + { -50.0f, -200.0f, 350.0f }, { 60.0f, -200.0f, 320.0f }, { -60.0f, -200.0f, 320.0f }, + { 70.0f, -200.0f, 290.0f }, { -70.0, -200.0f, 290.0f }, + }; + s32 i; + + // make both eyes red (one already is) + if (globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_LEFT] < 255) { + globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_LEFT] += 5; + } + if (globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_RIGHT] < 255) { + globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_RIGHT] += 5; + } + + if (globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_LEFT] != 255 || globalCtx->roomCtx.unk_74[BGDODOAGO_EYE_RIGHT] != 255) { + sTimer--; + return; + } + + if (sTimer == 108) { + for (i = ARRAY_COUNT(dustOffsets) - 1; i >= 0; i--) { + pos.x = dustOffsets[i].x + this->dyna.actor.world.pos.x; + pos.y = dustOffsets[i].y + this->dyna.actor.world.pos.y; + pos.z = dustOffsets[i].z + this->dyna.actor.world.pos.z; + func_80033480(globalCtx, &pos, 2.0f, 3, 200, 75, 1); + } + } + + pos.x = this->dyna.actor.world.pos.x + 200.0f; + pos.y = this->dyna.actor.world.pos.y - 20.0f; + pos.z = this->dyna.actor.world.pos.z + 100.0f; + BgDodoago_SpawnSparkles(&pos, globalCtx); + + pos.x = this->dyna.actor.world.pos.x - 200.0f; + pos.y = this->dyna.actor.world.pos.y - 20.0f; + pos.z = this->dyna.actor.world.pos.z + 100.0f; + BgDodoago_SpawnSparkles(&pos, globalCtx); + + Math_StepToS(&this->state, 100, 3); + func_800AA000(500.0f, 0x78, 0x14, 0xA); + + if (Math_SmoothStepToS(&this->dyna.actor.shape.rot.x, 0x1333, 110 - this->state, 0x3E8, 0x32) == 0) { + BgDodoago_SetupAction(this, BgDodoago_DoNothing); + Audio_PlaySoundGeneral(NA_SE_EV_STONE_BOUND, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_EV_STONE_STATUE_OPEN - SFX_FLAG, &this->dyna.actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } +} + +void BgDodoago_DoNothing(BgDodoago* this, GlobalContext* globalCtx) { +} + +void BgDodoago_LightOneEye(BgDodoago* this, GlobalContext* globalCtx) { + globalCtx->roomCtx.unk_74[this->state] += 5; + + if (globalCtx->roomCtx.unk_74[this->state] == 255) { + BgDodoago_SetupAction(this, BgDodoago_WaitExplosives); + } +} + +void BgDodoago_Update(Actor* thisx, GlobalContext* globalCtx) { + BgDodoago* this = (BgDodoago*)thisx; + Actor* actor; + EnBom* bomb; + + if (this->dyna.actor.parent == NULL) { + // this is a "bomb catcher", it kills the XZ speed and sets the timer for bombs that are dropped through the + // holes in the bridge above the skull + if ((this->colliderLeft.base.ocFlags1 & OC1_HIT) || (this->colliderRight.base.ocFlags1 & OC1_HIT)) { + + if (this->colliderLeft.base.ocFlags1 & OC1_HIT) { + actor = this->colliderLeft.base.oc; + } else { + actor = this->colliderRight.base.oc; + } + this->colliderLeft.base.ocFlags1 &= ~OC1_HIT; + this->colliderRight.base.ocFlags1 &= ~OC1_HIT; + + if (actor->category == ACTORCAT_EXPLOSIVE && actor->id == ACTOR_EN_BOM && actor->params == 0) { + bomb = (EnBom*)actor; + // disable the bomb catcher for a few seconds + this->dyna.actor.parent = &bomb->actor; + bomb->timer = 50; + bomb->actor.speedXZ = 0.0f; + sTimer = 0; + } + } + } else { + sTimer++; + Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + if (!sDisableBombCatcher && sTimer > 140) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + // this prevents clearing the actor's parent pointer, effectively disabling the bomb catcher + sDisableBombCatcher++; + } else { + this->dyna.actor.parent = NULL; + } + } + } + this->actionFunc(this, globalCtx); +} + +void BgDodoago_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_dodoago.c", 672); + + if (Flags_GetEventChkInf(0xB0)) { + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_dodoago.c", 677), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDodongoLowerJawDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_dodoago.c", 681); +} + +void BgDodoago_Reset(void) { + sFirstExplosiveFlag = false; + sDisableBombCatcher = 0; + sTimer = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.h b/soh/src/overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.h new file mode 100644 index 000000000..9729162f8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Dodoago/z_bg_dodoago.h @@ -0,0 +1,25 @@ +#ifndef Z_BG_DODOAGO_H +#define Z_BG_DODOAGO_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0 */ BGDODOAGO_EYE_LEFT, + /* 1 */ BGDODOAGO_EYE_RIGHT +} BgDodoagoEye; + +struct BgDodoago; + +typedef void (*BgDodoagoActionFunc)(struct BgDodoago*, GlobalContext*); + +typedef struct BgDodoago { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s16 state; // BgDodoagoEye or a timer-like value + /* 0x0168 */ ColliderCylinder colliderMain; // Used to detect explosions for lighting the eyes + /* 0x01B4 */ ColliderCylinder colliderLeft; // OC-colliding bombs have their xz speed cleared and timer set + /* 0x0200 */ ColliderCylinder colliderRight; // same as colliderLeft + /* 0x024C */ BgDodoagoActionFunc actionFunc; +} BgDodoago; // size = 0x0250 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c new file mode 100644 index 000000000..5b13aab9b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.c @@ -0,0 +1,1041 @@ +/* + * File: z_bg_dy_yoseizo.c + * Overlay: ovl_Bg_Dy_Yoseizo + * Description: Great Fairy + */ + +#include "z_bg_dy_yoseizo.h" +#include "objects/object_dy_obj/object_dy_obj.h" +#include "vt.h" +#include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" +#include "scenes/indoors/yousei_izumi_yoko/yousei_izumi_yoko_scene.h" +#include "scenes/indoors/daiyousei_izumi/daiyousei_izumi_scene.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) + +typedef enum { + /* 0 */ FAIRY_UPGRADE_MAGIC, + /* 1 */ FAIRY_UPGRADE_DOUBLE_MAGIC, + /* 2 */ FAIRY_UPGRADE_HALF_DAMAGE +} BgDyYoseizoRewardType; + +typedef enum { + /* 0 */ FAIRY_SPELL_FARORES_WIND, + /* 1 */ FAIRY_SPELL_DINS_FIRE, + /* 2 */ FAIRY_SPELL_NAYRUS_LOVE +} BgDyYoseizoSpellType; + +void BgDyYoseizo_Init(Actor* thisx, GlobalContext* globalCtx); +void BgDyYoseizo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgDyYoseizo_Update(Actor* thisx, GlobalContext* globalCtx); +void BgDyYoseizo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgDyYoseizo_CheckMagicAcquired(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_ChooseType(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SetupSpinGrow_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SpinGrow_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_CompleteSpinGrow_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SetupGreetPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_GreetPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SetupHealPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SayFarewell_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SetupSpinShrink(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SpinShrink(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_Vanish(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SetupSpinGrow_Reward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_SpinGrowSetupGive_Reward(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, GlobalContext* globalCtx); + +void BgDyYoseizo_ParticleInit(BgDyYoseizo* this, Vec3f* initPos, Vec3f* initVelocity, Vec3f* accel, + Color_RGB8* primColor, Color_RGB8* envColor, f32 scale, s16 life, s16 type); +void BgDyYoseizo_ParticleUpdate(BgDyYoseizo* this, GlobalContext* globalCtx); +void BgDyYoseizo_ParticleDraw(BgDyYoseizo* this, GlobalContext* globalCtx); + +static s32 sUnusedGetItemIds[] = { GI_FARORES_WIND, GI_NAYRUS_LOVE, GI_DINS_FIRE }; + +const ActorInit Bg_Dy_Yoseizo_InitVars = { + ACTOR_BG_DY_YOSEIZO, + ACTORCAT_PROP, + FLAGS, + OBJECT_DY_OBJ, + sizeof(BgDyYoseizo), + (ActorFunc)BgDyYoseizo_Init, + (ActorFunc)BgDyYoseizo_Destroy, + (ActorFunc)BgDyYoseizo_Update, + NULL, + NULL, +}; + +void BgDyYoseizo_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgDyYoseizo* this = (BgDyYoseizo*)thisx; + + this->fountainType = globalCtx->curSpawn; + + if (this->fountainType < 0) { + this->fountainType = 0; + } + + this->vanishHeight = this->actor.world.pos.y; + this->grownHeight = this->vanishHeight + 40.0f; + this->actor.focus.pos = this->actor.world.pos; + + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + // "Great Fairy Fountain" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 大妖精の泉 ☆☆☆☆☆ %d\n" VT_RST, globalCtx->curSpawn); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGreatFairySkel, &gGreatFairySittingTransitionAnim, + this->jointTable, this->morphTable, 28); + } else { + // "Stone/Jewel Fairy Fountain" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 石妖精の泉 ☆☆☆☆☆ %d\n" VT_RST, globalCtx->curSpawn); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGreatFairySkel, &gGreatFairyLayingDownTransitionAnim, + this->jointTable, this->morphTable, 28); + } + this->actionFunc = BgDyYoseizo_CheckMagicAcquired; +} + +void BgDyYoseizo_Destroy(Actor* this, GlobalContext* globalCtx) { +} + +static Color_RGB8 sParticlePrimColors[] = { + { 255, 255, 255 }, { 255, 255, 100 }, { 100, 255, 100 }, { 255, 100, 100 }, { 255, 255, 170 }, + { 255, 255, 100 }, { 100, 255, 100 }, { 255, 100, 100 }, { 255, 255, 170 }, +}; + +static Color_RGB8 sParticleEnvColors[] = { + { 155, 255, 255 }, { 255, 255, 100 }, { 100, 255, 100 }, { 255, 100, 100 }, { 255, 100, 255 }, + { 255, 255, 100 }, { 100, 255, 100 }, { 255, 100, 100 }, { 100, 255, 255 }, +}; + +void BgDyYoseizo_SpawnParticles(BgDyYoseizo* this, GlobalContext* globalCtx, s16 type) { + Vec3f particleInitVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f particleAccel; + Vec3f particleInitPos; + Color_RGB8 particlePrimColor; + Color_RGB8 particleEnvColor; + f32 spawnPosVariation; + s32 particleType; + f32 particleScale; + s32 i; + s16 particleLife; + + if (!(this->scale < 0.01f)) { + spawnPosVariation = this->scale * 3500.0f; + particleAccel.x = Rand_ZeroOne() - 0.5f; + particleAccel.y = Rand_ZeroOne() - 0.5f; + particleAccel.z = Rand_ZeroOne() - 0.5f; + for (i = 0; i < 2; i++) { + if (type == 0) { + particleType = 0; + particleScale = 0.4f; + particleLife = 90; + particleInitPos.x = this->actor.world.pos.x; + particleInitPos.y = this->actor.world.pos.y + spawnPosVariation + + ((Rand_ZeroOne() - 0.5f) * (spawnPosVariation * 0.5f)); + particleInitPos.z = this->actor.world.pos.z + 30.0f; + } else { + particleLife = 50; + particleType = type; + particleScale = 0.2f; + particleInitPos.x = this->actor.world.pos.x + Rand_CenteredFloat(10.0f); + + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + particleInitPos.y = this->actor.world.pos.y + spawnPosVariation + 50.0f + + ((Rand_ZeroOne() - 0.5f) * (spawnPosVariation * 0.1f)); + particleInitPos.z = this->actor.world.pos.z + 30.0f; + } else { + particleInitPos.y = this->actor.world.pos.y + spawnPosVariation - 30.0f + + ((Rand_ZeroOne() - 0.5f) * (spawnPosVariation * 0.1f)); + particleInitPos.z = this->actor.world.pos.z + 60.0f; + } + + if (LINK_IS_ADULT) { + particleInitPos.y += 20.0f; + } + } + + particlePrimColor.r = sParticlePrimColors[particleType].r; + particlePrimColor.g = sParticlePrimColors[particleType].g; + particlePrimColor.b = sParticlePrimColors[particleType].b; + particleEnvColor.r = sParticleEnvColors[particleType].r; + particleEnvColor.g = sParticleEnvColors[particleType].g; + particleEnvColor.b = sParticleEnvColors[particleType].b; + BgDyYoseizo_ParticleInit(this, &particleInitPos, &particleInitVelocity, &particleAccel, &particlePrimColor, + &particleEnvColor, particleScale, particleLife, particleType); + } + } +} + +void BgDyYoseizo_Bob(BgDyYoseizo* this, GlobalContext* globalCtx) { + this->targetHeight = this->grownHeight + this->bobOffset; + Math_ApproachF(&this->actor.world.pos.y, this->targetHeight, 0.1f, 10.0f); + Math_ApproachF(&this->bobOffset, 10.0f, 0.1f, 0.5f); + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + this->actor.velocity.y = Math_SinS(this->bobTimer); + } else { + this->actor.velocity.y = Math_SinS(this->bobTimer) * 0.4f; + } +} + +void BgDyYoseizo_CheckMagicAcquired(BgDyYoseizo* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, 0x38)) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + if (!gSaveContext.magicAcquired && (this->fountainType != FAIRY_UPGRADE_MAGIC)) { + Actor_Kill(&this->actor); + return; + } + } else { + if (!gSaveContext.magicAcquired) { + Actor_Kill(&this->actor); + return; + } + } + func_8002DF54(globalCtx, &this->actor, 1); + this->actionFunc = BgDyYoseizo_ChooseType; + } +} + +void BgDyYoseizo_ChooseType(BgDyYoseizo* this, GlobalContext* globalCtx) { + s32 givingReward; + + func_8002DF54(globalCtx, &this->actor, 1); + // "Mode" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ もうど ☆☆☆☆☆ %d\n" VT_RST, globalCtx->msgCtx.ocarinaMode); + givingReward = false; + + if (globalCtx->sceneNum != SCENE_DAIYOUSEI_IZUMI) { + switch (this->fountainType) { + case FAIRY_SPELL_FARORES_WIND: + if (!(gSaveContext.itemGetInf[1] & 0x100)) { + givingReward = true; + } + break; + case FAIRY_SPELL_DINS_FIRE: + if (!(gSaveContext.itemGetInf[1] & 0x200)) { + givingReward = true; + } + break; + case FAIRY_SPELL_NAYRUS_LOVE: + if (!(gSaveContext.itemGetInf[1] & 0x400)) { + givingReward = true; + } + break; + } + } else { + switch (this->fountainType) { + case FAIRY_UPGRADE_MAGIC: + if (!gSaveContext.magicAcquired || BREG(2)) { + // "Spin Attack speed UP" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 回転切り速度UP ☆☆☆☆☆ \n" VT_RST); + this->givingSpell = true; + givingReward = true; + } + break; + case FAIRY_UPGRADE_DOUBLE_MAGIC: + if (!gSaveContext.doubleMagic) { + // "Magic Meter doubled" + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ 魔法ゲージメーター倍増 ☆☆☆☆☆ \n" VT_RST); + this->givingSpell = true; + givingReward = true; + } + break; + case FAIRY_UPGRADE_HALF_DAMAGE: + if (!gSaveContext.doubleDefense) { + // "Damage halved" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ ダメージ半減 ☆☆☆☆☆ \n" VT_RST); + this->givingSpell = true; + givingReward = true; + } + break; + } + } + + if (givingReward) { + if (gSaveContext.sceneSetupIndex < 4) { + if (globalCtx->sceneNum != SCENE_DAIYOUSEI_IZUMI) { + switch (this->fountainType) { + case FAIRY_SPELL_FARORES_WIND: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGreatFairyFaroresWindCs); + gSaveContext.cutsceneTrigger = 1; + break; + case FAIRY_SPELL_DINS_FIRE: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGreatFairyDinsFireCs); + gSaveContext.cutsceneTrigger = 1; + break; + case FAIRY_SPELL_NAYRUS_LOVE: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGreatFairyNayrusLoveCs); + gSaveContext.cutsceneTrigger = 1; + break; + } + } else { + switch (this->fountainType) { + case FAIRY_UPGRADE_MAGIC: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGreatFairyMagicCs); + gSaveContext.cutsceneTrigger = 1; + break; + case FAIRY_UPGRADE_DOUBLE_MAGIC: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGreatFairyDoubleMagicCs); + gSaveContext.cutsceneTrigger = 1; + break; + case FAIRY_UPGRADE_HALF_DAMAGE: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGreatFairyDoubleDefenceCs); + gSaveContext.cutsceneTrigger = 1; + break; + } + } + } + this->actionFunc = BgDyYoseizo_SetupSpinGrow_Reward; + return; + } + + globalCtx->envCtx.unk_BF = 2; + + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + OnePointCutscene_Init(globalCtx, 8603, -99, NULL, MAIN_CAM); + } else { + OnePointCutscene_Init(globalCtx, 8604, -99, NULL, MAIN_CAM); + }; + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GREAT_FAIRY_APPEAR); + this->actor.draw = BgDyYoseizo_Draw; + this->actionFunc = BgDyYoseizo_SetupSpinGrow_NoReward; +} + +// Sets animations for spingrow +void BgDyYoseizo_SetupSpinGrow_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairySittingTransitionAnim); + Animation_Change(&this->skelAnime, &gGreatFairySittingTransitionAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyLayingDownTransitionAnim); + Animation_Change(&this->skelAnime, &gGreatFairyLayingDownTransitionAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_VO_FR_LAUGH_0); + func_8002DF54(globalCtx, &this->actor, 1); + this->actionFunc = BgDyYoseizo_SpinGrow_NoReward; +} + +void BgDyYoseizo_SpinGrow_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + func_8002DF54(globalCtx, &this->actor, 1); + Math_ApproachF(&this->actor.world.pos.y, this->grownHeight, this->heightFraction, 100.0f); + Math_ApproachF(&this->scale, 0.035f, this->scaleFraction, 0.005f); + Math_ApproachF(&this->heightFraction, 0.8f, 0.1f, 0.02f); + Math_ApproachF(&this->scaleFraction, 0.2f, 0.03f, 0.05f); + // Finished growing + if (this->scale >= 0.034f) { + if ((this->actor.shape.rot.y > -8000) && (this->actor.shape.rot.y < 1000)) { + SkelAnime_Update(&this->skelAnime); + // Turn to front + Math_SmoothStepToS(&this->actor.shape.rot.y, 0, 5, 1000, 0); + if (fabsf(this->actor.shape.rot.y) < 50.0f) { + this->actionFunc = BgDyYoseizo_CompleteSpinGrow_NoReward; + } + } else { + this->actor.shape.rot.y += 3000; + } + } else { + this->actor.shape.rot.y += 3000; + } + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); +} + +void BgDyYoseizo_CompleteSpinGrow_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + f32 curFrame = this->skelAnime.curFrame; + + func_8002DF54(globalCtx, &this->actor, 1); + + if ((this->frameCount * 1273.0f) <= this->bobTimer) { + this->bobTimer = 0.0f; + } + + SkelAnime_Update(&this->skelAnime); + + if ((this->frameCount <= curFrame) && !this->animationChanged) { + this->actionFunc = BgDyYoseizo_SetupGreetPlayer_NoReward; + } +} + +void BgDyYoseizo_SetupGreetPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + func_8002DF54(globalCtx, &this->actor, 1); + + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairySittingAnim); + Animation_Change(&this->skelAnime, &gGreatFairySittingAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, + -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyLayingSidewaysAnim); + Animation_Change(&this->skelAnime, &gGreatFairyLayingSidewaysAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, + -10.0f); + } + + this->actor.textId = 0xDB; + this->dialogState = TEXT_STATE_EVENT; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); + this->actionFunc = BgDyYoseizo_GreetPlayer_NoReward; +} + +void BgDyYoseizo_GreetPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + func_8002DF54(globalCtx, &this->actor, 1); + this->bobTimer = this->skelAnime.curFrame * 1273.0f; + + if ((this->frameCount * 1273.0f) <= this->bobTimer) { + this->bobTimer = 0.0f; + } + + SkelAnime_Update(&this->skelAnime); + + if ((this->dialogState == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + Interface_ChangeAlpha(5); + this->actionFunc = BgDyYoseizo_SetupHealPlayer_NoReward; + } + + BgDyYoseizo_Bob(this, globalCtx); + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); +} + +void BgDyYoseizo_SetupHealPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairyGivingUpgradeAnim); + Animation_Change(&this->skelAnime, &gGreatFairyGivingUpgradeAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_ONCE, + -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyAnim_005810); + Animation_Change(&this->skelAnime, &gGreatFairyAnim_005810, 1.0f, 0.0f, this->frameCount, ANIMMODE_ONCE, + -10.0f); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_VO_FR_SMILE_0); + this->mouthState = 1; + this->actionFunc = BgDyYoseizo_HealPlayer_NoReward; +} + +void BgDyYoseizo_HealPlayer_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 curFrame = this->skelAnime.curFrame; + Vec3f beamPos; + s16 beamParams; + + if (this->animationChanged) { + this->bobTimer = this->skelAnime.curFrame * 1300.0f; + if ((this->frameCount * 1300.0f) <= this->bobTimer) { + this->bobTimer = 0.0f; + } + } + + SkelAnime_Update(&this->skelAnime); + if ((this->frameCount <= curFrame) && !(this->animationChanged)) { + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairyAfterUpgradeAnim); + Animation_Change(&this->skelAnime, &gGreatFairyAfterUpgradeAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_LOOP, -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyAfterSpellAnim); + Animation_Change(&this->skelAnime, &gGreatFairyAfterSpellAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, + -10.0f); + } + this->healingTimer = 150; + this->animationChanged = true; + if (!this->givingSpell) { + beamPos.x = player->actor.world.pos.x; + beamPos.y = player->actor.world.pos.y + 200.0f; + beamPos.z = player->actor.world.pos.z; + + beamParams = ((globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) ? 0 : 1); + + this->beam = + (EnDyExtra*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_DY_EXTRA, + beamPos.x, beamPos.y, beamPos.z, 0, 0, 0, beamParams); + } + } + if (this->refillTimer > 1) { + this->refillTimer--; + } + + if (this->healingTimer >= 110) { + this->healingTimer--; + } + + if (this->healingTimer == 110) { + gSaveContext.healthAccumulator = 0x140; + Magic_Fill(globalCtx); + this->refillTimer = 200; + } + + if (((gSaveContext.healthCapacity == gSaveContext.health) && (gSaveContext.magic == gSaveContext.unk_13F4)) || + (this->refillTimer == 1)) { + this->healingTimer--; + if (this->healingTimer == 90) { + if (!this->givingSpell) { + this->beam->trigger = 1; + } + this->givingSpell = false; + } + } + + if (this->healingTimer == 1) { + this->actor.textId = 0xDA; + this->dialogState = TEXT_STATE_EVENT; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->actionFunc = BgDyYoseizo_SayFarewell_NoReward; + return; + } + BgDyYoseizo_Bob(this, globalCtx); +} + +void BgDyYoseizo_SayFarewell_NoReward(BgDyYoseizo* this, GlobalContext* globalCtx) { + this->bobTimer = this->skelAnime.curFrame * 1400.0f; + + if (this->bobTimer >= (this->frameCount * 1400.0f)) { + this->bobTimer = 0.0f; + } + + SkelAnime_Update(&this->skelAnime); + + if ((this->dialogState == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->mouthState = 0; + this->actionFunc = BgDyYoseizo_SetupSpinShrink; + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + } + + BgDyYoseizo_Bob(this, globalCtx); + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); +} + +void BgDyYoseizo_SetupSpinShrink(BgDyYoseizo* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairyJewelFountainSpinShrinkAnim); + Animation_Change(&this->skelAnime, &gGreatFairyJewelFountainSpinShrinkAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairySpellFountainSpinShrinkAnim); + Animation_Change(&this->skelAnime, &gGreatFairySpellFountainSpinShrinkAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } + + this->vanishTimer = 5; + this->scaleFraction = 0.0f; + this->heightFraction = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_FR_LAUGH_0); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GREAT_FAIRY_VANISH); + this->actionFunc = BgDyYoseizo_SpinShrink; +} + +void BgDyYoseizo_SpinShrink(BgDyYoseizo* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->vanishTimer == 0) { + if (this->scale < 0.003f) { + this->vanishTimer = 30; + this->actionFunc = BgDyYoseizo_Vanish; + } else { + Math_ApproachF(&this->actor.world.pos.y, this->vanishHeight, this->heightFraction, 100.0f); + Math_ApproachZeroF(&this->scale, this->scaleFraction, 0.005f); + Math_ApproachF(&this->heightFraction, 0.8f, 0.1f, 0.02f); + Math_ApproachF(&this->scaleFraction, 0.2f, 0.03f, 0.05f); + this->actor.shape.rot.y += 3000; + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); + } + } +} + +void BgDyYoseizo_Vanish(BgDyYoseizo* this, GlobalContext* globalCtx) { + Actor* findOcarinaSpot; + + if (this->vanishTimer == 0) { + func_8002DF54(globalCtx, &this->actor, 7); + globalCtx->envCtx.unk_BF = 0; + findOcarinaSpot = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + + while (findOcarinaSpot != NULL) { + if (findOcarinaSpot->id != ACTOR_EN_OKARINA_TAG) { + findOcarinaSpot = findOcarinaSpot->next; + continue; + } + Actor_Kill(findOcarinaSpot); + break; + } + + Flags_UnsetSwitch(globalCtx, 0x38); + Actor_Kill(&this->actor); + } +} + +void BgDyYoseizo_SetupSpinGrow_Reward(BgDyYoseizo* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if ((globalCtx->csCtx.npcActions[0] != NULL) && (globalCtx->csCtx.npcActions[0]->action == 2)) { + this->actor.draw = BgDyYoseizo_Draw; + func_8002DF54(globalCtx, &this->actor, 1); + this->finishedSpinGrow = false; + + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairySittingTransitionAnim); + Animation_Change(&this->skelAnime, &gGreatFairySittingTransitionAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyLayingDownTransitionAnim); + Animation_Change(&this->skelAnime, &gGreatFairyLayingDownTransitionAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GREAT_FAIRY_APPEAR); + this->actionFunc = BgDyYoseizo_SpinGrowSetupGive_Reward; + } + } +} + +void BgDyYoseizo_SpinGrowSetupGive_Reward(BgDyYoseizo* this, GlobalContext* globalCtx) { + f32 curFrame = this->skelAnime.curFrame; + + if (!this->finishedSpinGrow) { + Math_ApproachF(&this->actor.world.pos.y, this->grownHeight, this->heightFraction, 100.0f); + Math_ApproachF(&this->scale, 0.035f, this->scaleFraction, 0.005f); + Math_ApproachF(&this->heightFraction, 0.8f, 0.1f, 0.02f); + Math_ApproachF(&this->scaleFraction, 0.2f, 0.03f, 0.05f); + // Finished growing + if (this->scale >= 0.034f) { + if ((this->actor.shape.rot.y > -8000) && (this->actor.shape.rot.y < 1000)) { + SkelAnime_Update(&this->skelAnime); + // Spin until facing front + Math_ApproachS(&this->actor.shape.rot.y, 0, 5, 1000); + if (fabsf(this->actor.shape.rot.y) < 50.0f) { + this->finishedSpinGrow = true; + } + } else { + this->actor.shape.rot.y += 3000; + } + } else { + this->actor.shape.rot.y += 3000; + } + } else { + SkelAnime_Update(&this->skelAnime); + + if ((this->frameCount <= curFrame) && !this->animationChanged) { + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairySittingAnim); + Animation_Change(&this->skelAnime, &gGreatFairySittingAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, + -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyLayingSidewaysAnim); + Animation_Change(&this->skelAnime, &gGreatFairyLayingSidewaysAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_LOOP, -10.0f); + } + this->animationChanged = true; + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && + ((globalCtx->csCtx.npcActions[0] != NULL) && (globalCtx->csCtx.npcActions[0]->action == 3))) { + this->finishedSpinGrow = this->animationChanged = false; + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairyGivingUpgradeAnim); + Animation_Change(&this->skelAnime, &gGreatFairyGivingUpgradeAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_ONCE, -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyAnim_005810); + Animation_Change(&this->skelAnime, &gGreatFairyAnim_005810, 1.0f, 0.0f, this->frameCount, ANIMMODE_ONCE, + -10.0f); + } + this->mouthState = 1; + this->actionFunc = BgDyYoseizo_Give_Reward; + } + } + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); +} + +static s16 sDemoEffectLightColors[] = { DEMO_EFFECT_LIGHT_GREEN, DEMO_EFFECT_LIGHT_RED, DEMO_EFFECT_LIGHT_BLUE }; + +static s16 sExItemTypes[] = { EXITEM_MAGIC_WIND, EXITEM_MAGIC_FIRE, EXITEM_MAGIC_DARK }; + +static s16 sItemGetFlags[] = { 0x100, 0x200, 0x400 }; + +static u8 sItemIds[] = { ITEM_FARORES_WIND, ITEM_DINS_FIRE, ITEM_NAYRUS_LOVE }; + +void BgDyYoseizo_Give_Reward(BgDyYoseizo* this, GlobalContext* globalCtx) { + f32 curFrame = this->skelAnime.curFrame; + Player* player = GET_PLAYER(globalCtx); + s16 actionIndex; + s16 demoEffectParams; + Vec3f itemPos; + + if (this->animationChanged) { + this->bobTimer = this->skelAnime.curFrame * 1400.0f; + if ((this->frameCount * 1400.0f) <= this->bobTimer) { + this->bobTimer = 0.0f; + } + } + SkelAnime_Update(&this->skelAnime); + + if ((this->frameCount <= curFrame) && !this->animationChanged) { + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + this->frameCount = Animation_GetLastFrame(&gGreatFairyAfterUpgradeAnim); + Animation_Change(&this->skelAnime, &gGreatFairyAfterUpgradeAnim, 1.0f, 0.0f, this->frameCount, + ANIMMODE_LOOP, -10.0f); + } else { + this->frameCount = Animation_GetLastFrame(&gGreatFairyAfterSpellAnim); + Animation_Change(&this->skelAnime, &gGreatFairyAfterSpellAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, + -10.0f); + } + this->animationChanged = true; + } + + if (globalCtx->csCtx.npcActions[0]->action == 13) { + this->actionFunc = BgDyYoseizo_SetupSpinShrink; + return; + } + + if ((globalCtx->csCtx.npcActions[0]->action >= 4) && (globalCtx->csCtx.npcActions[0]->action < 7)) { + actionIndex = globalCtx->csCtx.npcActions[0]->action - 4; + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + actionIndex++; + BgDyYoseizo_SpawnParticles(this, globalCtx, actionIndex); + + } else if (!this->lightBallSpawned) { + demoEffectParams = ((s16)(sDemoEffectLightColors[actionIndex] << 0xC) | DEMO_EFFECT_LIGHT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, (s32)demoEffectParams); + this->lightBallSpawned = true; + } + } else { + BgDyYoseizo_SpawnParticles(this, globalCtx, 0); + } + + if ((globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) && (globalCtx->csCtx.npcActions[0]->action >= 10) && + (globalCtx->csCtx.npcActions[0]->action < 13)) { + actionIndex = globalCtx->csCtx.npcActions[0]->action - 10; + + switch (actionIndex) { + case FAIRY_UPGRADE_MAGIC: + gSaveContext.magicAcquired = true; + gSaveContext.unk_13F6 = 0x30; + Interface_ChangeAlpha(9); + break; + case FAIRY_UPGRADE_DOUBLE_MAGIC: + if (!gSaveContext.magicAcquired) { + gSaveContext.magicAcquired = true; + } + gSaveContext.doubleMagic = true; + gSaveContext.unk_13F6 = 0x60; + gSaveContext.magicLevel = 0; + Interface_ChangeAlpha(9); + break; + case FAIRY_UPGRADE_HALF_DAMAGE: + gSaveContext.doubleDefense = true; + Interface_ChangeAlpha(9); + break; + } + + if (!this->healing) { + gSaveContext.healthAccumulator = 0x140; + this->healing = true; + if (actionIndex == 2) { + Magic_Fill(globalCtx); + } + } + } + + if ((globalCtx->sceneNum != SCENE_DAIYOUSEI_IZUMI) && (globalCtx->csCtx.npcActions[0]->action >= 14) && + (globalCtx->csCtx.npcActions[0]->action < 17)) { + actionIndex = globalCtx->csCtx.npcActions[0]->action - 14; + + if (!this->itemSpawned) { + itemPos.x = player->actor.world.pos.x; + itemPos.y = (LINK_IS_ADULT ? player->actor.world.pos.y + 73.0f : player->actor.world.pos.y + 53.0f); + itemPos.z = player->actor.world.pos.z; + + this->item = + (EnExItem*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_ITEM, + itemPos.x, itemPos.y, itemPos.z, 0, 0, 0, sExItemTypes[actionIndex]); + + if (this->item != NULL) { + if (gSaveContext.magicAcquired == 0) { + gSaveContext.magicAcquired = 1; + } else { + Magic_Fill(globalCtx); + } + + this->itemSpawned = true; + gSaveContext.healthAccumulator = 0x140; + Interface_ChangeAlpha(9); + gSaveContext.itemGetInf[1] |= sItemGetFlags[actionIndex]; + Item_Give(globalCtx, sItemIds[actionIndex]); + } + } else { + this->item->actor.world.pos.x = player->actor.world.pos.x; + this->item->actor.world.pos.y = + (LINK_IS_ADULT ? player->actor.world.pos.y + 73.0f : player->actor.world.pos.y + 53.0f); + this->item->actor.world.pos.z = player->actor.world.pos.z; + this->item->scale = 0.3f; + } + } + + if ((globalCtx->sceneNum != SCENE_DAIYOUSEI_IZUMI) && (globalCtx->csCtx.npcActions[0]->action == 17) && + (this->item != NULL)) { + Actor_Kill(&this->item->actor); + this->item = NULL; + } + + if ((globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) && (globalCtx->csCtx.npcActions[0]->action == 18)) { + this->giveDefenseHearts = true; + } + + if (this->giveDefenseHearts) { + if (gSaveContext.inventory.defenseHearts < 20) { + gSaveContext.inventory.defenseHearts++; + } + } + + if ((globalCtx->csCtx.npcActions[0]->action >= 19) && (globalCtx->csCtx.npcActions[0]->action < 22) && + !this->warpEffectSpawned) { + actionIndex = globalCtx->csCtx.npcActions[0]->action - 11; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, player->actor.world.pos.x, + player->actor.world.pos.y, player->actor.world.pos.z, 0, 0, 0, actionIndex); + this->warpEffectSpawned = true; + } + BgDyYoseizo_Bob(this, globalCtx); +} + +void BgDyYoseizo_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgDyYoseizo* this = (BgDyYoseizo*)thisx; + s32 phi_v1; + + this->absoluteTimer++; + + if (this->vanishTimer != 0) { + this->vanishTimer--; + } + if (this->blinkTimer != 0) { + this->blinkTimer--; + } + if (this->unusedTimer != 0) { + this->unusedTimer--; + } + + this->actionFunc(this, globalCtx); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + phi_v1 = 0; + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI) { + if ((globalCtx->csCtx.frames == 32) || (globalCtx->csCtx.frames == 291) || + (globalCtx->csCtx.frames == 426) || (globalCtx->csCtx.frames == 851)) { + phi_v1 = 1; + } + if (globalCtx->csCtx.frames == 101) { + phi_v1 = 2; + } + } else { + if ((globalCtx->csCtx.frames == 35) || (globalCtx->csCtx.frames == 181) || + (globalCtx->csCtx.frames == 462) || (globalCtx->csCtx.frames == 795)) { + phi_v1 = 1; + } + if (globalCtx->csCtx.frames == 90) { + phi_v1 = 2; + } + } + + if (phi_v1 == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_FR_SMILE_0); + } + if (phi_v1 == 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_FR_LAUGH_0); + } + } + + if ((this->blinkTimer == 0) && (this->actionFunc != BgDyYoseizo_HealPlayer_NoReward)) { + this->eyeState++; + this->eyeState2++; + if (this->eyeState >= 3) { + this->eyeState = this->eyeState2 = 0; + this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + + Actor_MoveForward(&this->actor); + this->heightOffset = this->scale * 7500.0f; + Actor_SetFocus(&this->actor, this->heightOffset); + this->actor.focus.pos.y = this->heightOffset; + func_80038290(globalCtx, &this->actor, &this->headRot, &this->torsoRot, this->actor.focus.pos); + BgDyYoseizo_ParticleUpdate(this, globalCtx); + Actor_SetScale(&this->actor, this->scale); +} + +s32 BgDyYoseizo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BgDyYoseizo* this = (BgDyYoseizo*)thisx; + + if (limbIndex == 8) { // Torso + rot->x += this->torsoRot.y; + } + if (limbIndex == 15) { // Head + rot->x += this->headRot.y; + rot->z += this->headRot.z; + } + return 0; +} + +static void* sEyeTextures[] = { + gGreatFairyEyeOpenTex, // Open + gGreatFairyEyeHalfTex, // Half + gGreatFairyEyeClosedTex, // Closed +}; + +static void* sMouthTextures[] = { + gGreatFairyMouthClosedTex, // Closed + gGreatFairyMouthOpenTex, // Open +}; + +void BgDyYoseizo_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgDyYoseizo* this = (BgDyYoseizo*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_dy_yoseizo.c", 1609); + if (this->actionFunc != BgDyYoseizo_Vanish) { + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeState])); + + // This was probably intended to allow this actor to wink, but segment 09 is not used in the dList for the head, + // so it can only blink + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeState2])); + + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(sMouthTextures[this->mouthState])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BgDyYoseizo_OverrideLimbDraw, NULL, this); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_dy_yoseizo.c", 1629); + BgDyYoseizo_ParticleDraw(this, globalCtx); +} + +void BgDyYoseizo_ParticleInit(BgDyYoseizo* this, Vec3f* initPos, Vec3f* initVelocity, Vec3f* accel, + Color_RGB8* primColor, Color_RGB8* envColor, f32 scale, s16 life, s16 type) { + BgDyYoseizoParticle* particle; + s16 i; + + particle = this->particles; + + for (i = 0; i < 200; i++, particle++) { + if (particle->alive == 0) { + particle->alive = 1; + particle->pos = *initPos; + particle->velocity = *initVelocity; + particle->accel = *accel; + particle->primColor = *primColor; + particle->alpha = 0; + particle->envColor = *envColor; + particle->scale = scale; + particle->timer = life; + particle->type = type; + particle->pitch = 0.0f; + particle->yaw = Rand_CenteredFloat(30000.0f); + particle->roll = 0.0f; + return; + } + } +} + +void BgDyYoseizo_ParticleUpdate(BgDyYoseizo* this, GlobalContext* globalCtx) { + BgDyYoseizoParticle* particle = this->particles; + Player* player = GET_PLAYER(globalCtx); + Vec3f sp94; + Vec3f sp88; + f32 goalPitch; + f32 goalYaw; + s16 i = 0; + + for (i = 0; i < 200; i++, particle++) { + if (particle->alive != 0) { + particle->roll += 3000.0f; + + if (particle->type == 0) { + particle->pos.x += particle->velocity.x; + particle->pos.y += particle->velocity.y; + particle->pos.z += particle->velocity.z; + particle->velocity.x += particle->accel.x; + particle->velocity.y += particle->accel.y; + particle->velocity.z += particle->accel.z; + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HEALING - SFX_FLAG); + + sp94 = player->actor.world.pos; + sp94.y = player->actor.world.pos.y - 150.0f; + sp94.z = player->actor.world.pos.z - 50.0f; + + goalPitch = Math_Vec3f_Pitch(&particle->pos, &sp94); + goalYaw = Math_Vec3f_Yaw(&particle->pos, &sp94); + + Math_ApproachF(&particle->pitch, goalPitch, 0.9f, 5000.0f); + Math_ApproachF(&particle->yaw, goalYaw, 0.9f, 5000.0f); + Matrix_Push(); + Matrix_RotateY(BINANG_TO_RAD(particle->yaw), MTXMODE_NEW); + Matrix_RotateX(BINANG_TO_RAD(particle->pitch), MTXMODE_APPLY); + + sp94.x = sp94.y = sp94.z = 3.0f; + + Matrix_MultVec3f(&sp94, &sp88); + Matrix_Pop(); + particle->pos.x += sp88.x; + particle->pos.y += sp88.y; + particle->pos.z += sp88.z; + } + } + + // fade up, fade down, vanish and reset + if (particle->timer != 0) { + particle->timer--; + particle->alpha += 30; + + if (particle->alpha > 255) { + particle->alpha = 255; + } + } else { + particle->alpha -= 30; + + if (particle->alpha <= 0) { + particle->alpha = particle->alive = 0; + } + } + } +} + +void BgDyYoseizo_ParticleDraw(BgDyYoseizo* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u8 phi_s3 = 0; + BgDyYoseizoParticle* particle = this->particles; + s16 i; + + OPEN_DISPS(gfxCtx, "../z_bg_dy_yoseizo.c", 1767); + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < 200; i++, particle++) { + if (particle->alive == 1) { + if (phi_s3 == 0) { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gGreatFairyParticleAppearDL)); + gDPPipeSync(POLY_XLU_DISP++); + + phi_s3++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, particle->primColor.r, particle->primColor.g, particle->primColor.b, + particle->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, particle->envColor.r, particle->envColor.g, particle->envColor.b, 0); + + Matrix_Translate(particle->pos.x, particle->pos.y, particle->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(particle->scale, particle->scale, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ(particle->roll, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_bg_dy_yoseizo.c", 1810), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gGreatFairyParticleAliveDL)); + } + } + + CLOSE_DISPS(gfxCtx, "../z_bg_dy_yoseizo.c", 1819); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h new file mode 100644 index 000000000..59abc6065 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Dy_Yoseizo/z_bg_dy_yoseizo.h @@ -0,0 +1,73 @@ +#ifndef Z_BG_DY_YOSEIZO_H +#define Z_BG_DY_YOSEIZO_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" + +struct BgDyYoseizo; + +typedef void (*BgDyYoseizoActionFunc)(struct BgDyYoseizo*, GlobalContext*); + +typedef struct { + /* 0x00 */ u8 alive; // drawn if 1, respawn if 0 + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ Color_RGB8 primColor; + /* 0x2B */ Color_RGB8 envColor; + /* 0x2E */ s16 alpha; + /* 0x30 */ f32 scale; + /* 0x34 */ s16 timer; // lifetime + /* 0x36 */ s16 type; // 0 is general radiance, else is directed towards Player + /* 0x36 */ f32 pitch; + /* 0x36 */ f32 yaw; + /* 0x40 */ f32 roll; +} BgDyYoseizoParticle; // size = 0x44 + +typedef struct BgDyYoseizo { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgDyYoseizoActionFunc actionFunc; + /* 0x0150 */ SkelAnime skelAnime; + /* 0x0194 */ Vec3s jointTable[28]; + /* 0x023C */ Vec3s morphTable[28]; + /* 0x02E4 */ u8 lightBallSpawned; + /* 0x02E5 */ u8 giveDefenseHearts; + /* 0x02E6 */ u8 healing; + /* 0x02E8 */ s16 vanishTimer; + /* 0x02EA */ s16 givingSpell; + /* 0x02EC */ s16 fountainType; + /* 0x02EE */ s16 dialogState; + /* 0x02F0 */ s16 absoluteTimer; + /* 0x02F2 */ s16 eyeState; + /* 0x02F4 */ s16 eyeState2; // Used, but does not actually change the actor's eyes + /* 0x02F6 */ s16 mouthState; + /* 0x02F8 */ s16 blinkTimer; + /* 0x02FA */ s16 unusedTimer; + /* 0x02FC */ s16 animationChanged; + /* 0x02FE */ s16 finishedSpinGrow; + /* 0x02FE */ s16 itemSpawned; + /* 0x0302 */ s16 healingTimer; + /* 0x0304 */ s16 warpEffectSpawned; + /* 0x0306 */ s16 refillTimer; + /* 0x0308 */ f32 scale; + /* 0x030C */ f32 grownHeight; + /* 0x0310 */ f32 vanishHeight; + /* 0x0314 */ f32 heightFraction; + /* 0x0318 */ f32 scaleFraction; + /* 0x031C */ f32 targetHeight; + /* 0x0320 */ f32 bobOffset; + /* 0x0324 */ f32 bobTimer; + /* 0x0328 */ f32 heightOffset; + /* 0x032C */ f32 frameCount; + /* 0x0330 */ char unk_330[4]; + /* 0x0334 */ Vec3s headRot; + /* 0x033A */ Vec3s torsoRot; + /* 0x0340 */ EnDyExtra* beam; + /* 0x0344 */ EnExItem* item; + /* 0x0348 */ char unk_348[0x4C]; + /* 0x0394 */ BgDyYoseizoParticle particles[200]; +} BgDyYoseizo; // size = 0x38B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c new file mode 100644 index 000000000..d1e940619 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.c @@ -0,0 +1,366 @@ +/* + * File: z_bg_ganon_otyka.c + * Overlay: ovl_Bg_Ganon_Otyka + * Description: Falling Platform (Ganondorf Fight) + */ + +#include "z_bg_ganon_otyuka.h" +#include "overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0x00 */ FLASH_NONE, + /* 0x01 */ FLASH_GROW, + /* 0x02 */ FLASH_SHRINK +} FlashState; + +void BgGanonOtyuka_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGanonOtyuka_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGanonOtyuka_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgGanonOtyuka_WaitToFall(BgGanonOtyuka* this, GlobalContext* globalCtx); +void BgGanonOtyuka_Fall(BgGanonOtyuka* this, GlobalContext* globalCtx); +void BgGanonOtyuka_DoNothing(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Ganon_Otyuka_InitVars = { + ACTOR_BG_GANON_OTYUKA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GANON, + sizeof(BgGanonOtyuka), + (ActorFunc)BgGanonOtyuka_Init, + (ActorFunc)BgGanonOtyuka_Destroy, + (ActorFunc)BgGanonOtyuka_Update, + (ActorFunc)BgGanonOtyuka_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +static u8 sSides[] = { OTYUKA_SIDE_EAST, OTYUKA_SIDE_WEST, OTYUKA_SIDE_SOUTH, OTYUKA_SIDE_NORTH }; + +static Vec3f D_80876A68[] = { + { 120.0f, 0.0f, 0.0f }, + { -120.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 120.0f }, + { 0.0f, 0.0f, -120.0f }, +}; + +static Color_RGBA8 sDustPrimColor = { 60, 60, 0, 0 }; + +static Color_RGBA8 sDustEnvColor = { 50, 20, 0, 0 }; + +static Vec3f sSideCenters[] = { + { 60.0f, 0.0f, 0.0f }, + { -60.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 60.0f }, + { 0.0f, 0.0f, -60.0f }, +}; + +static f32 sSideAngles[] = { M_PI / 2, -M_PI / 2, 0.0f, M_PI }; + +#include "overlays/ovl_Bg_Ganon_Otyuka/ovl_Bg_Ganon_Otyuka.h" + +void BgGanonOtyuka_Init(Actor* thisx, GlobalContext* globalCtx2) { + BgGanonOtyuka* this = (BgGanonOtyuka*)thisx; + GlobalContext* globalCtx = globalCtx2; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&sCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + if (thisx->params != 0x23) { + thisx->draw = NULL; + this->actionFunc = BgGanonOtyuka_WaitToFall; + } else { + thisx->update = BgGanonOtyuka_DoNothing; + } +} + +void BgGanonOtyuka_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + BgGanonOtyuka* this = (BgGanonOtyuka*)thisx; + GlobalContext* globalCtx = globalCtx2; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("WHY !!!!!!!!!!!!!!!!\n"); + osSyncPrintf(VT_RST); +} + +void BgGanonOtyuka_WaitToFall(BgGanonOtyuka* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + Actor* prop; + BgGanonOtyuka* platform; + f32 dx; + f32 dy; + f32 dz; + Vec3f center; + s16 i; + + if (this->isFalling || ((globalCtx->actorCtx.unk_02 != 0) && (this->dyna.actor.xyzDistToPlayerSq < 4900.0f))) { + osSyncPrintf("OTC O 1\n"); + + for (i = 0; i < ARRAY_COUNT(D_80876A68); i++) { + prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + while (prop != NULL) { + if ((prop == thisx) || (prop->id != ACTOR_BG_GANON_OTYUKA)) { + prop = prop->next; + continue; + } + + platform = (BgGanonOtyuka*)prop; + + dx = platform->dyna.actor.world.pos.x - this->dyna.actor.world.pos.x + D_80876A68[i].x; + dy = platform->dyna.actor.world.pos.y - this->dyna.actor.world.pos.y; + dz = platform->dyna.actor.world.pos.z - this->dyna.actor.world.pos.z + D_80876A68[i].z; + + if ((fabsf(dx) < 10.0f) && (fabsf(dy) < 10.0f) && (fabsf(dz) < 10.0f)) { + platform->visibleSides |= sSides[i]; + break; + } else { + prop = prop->next; + } + } + } + + osSyncPrintf("OTC O 2\n"); + + for (i = 0; i < ARRAY_COUNT(D_80876A68); i++) { + center.x = this->dyna.actor.world.pos.x + D_80876A68[i].x; + center.y = this->dyna.actor.world.pos.y; + center.z = this->dyna.actor.world.pos.z + D_80876A68[i].z; + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, ¢er, 50.0f)) { + this->unwalledSides |= sSides[i]; + } + } + + osSyncPrintf("OTC O 3\n"); + + this->actionFunc = BgGanonOtyuka_Fall; + this->isFalling = true; + this->dropTimer = 20; + this->flashState = FLASH_GROW; + this->flashTimer = 0; + this->flashPrimColorR = 255.0f; + this->flashPrimColorG = 255.0f; + this->flashPrimColorB = 255.0f; + this->flashEnvColorR = 255.0f; + this->flashEnvColorG = 255.0f; + this->flashEnvColorB = 0.0f; + } +} + +void BgGanonOtyuka_Fall(BgGanonOtyuka* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 i; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + osSyncPrintf("MODE DOWN\n"); + if (this->flashState == FLASH_GROW) { + Math_ApproachF(&this->flashPrimColorB, 170.0f, 1.0f, 8.5f); + Math_ApproachF(&this->flashEnvColorR, 120.0f, 1.0f, 13.5f); + Math_ApproachF(&this->flashYScale, 2.5f, 1.0f, 0.25f); + if (this->flashYScale == 2.5f) { + this->flashState = FLASH_SHRINK; + } + } else if (this->flashState == FLASH_SHRINK) { + Math_ApproachF(&this->flashPrimColorG, 0.0f, 1.0f, 25.5f); + Math_ApproachF(&this->flashEnvColorR, 0.0f, 1.0f, 12.0f); + Math_ApproachF(&this->flashEnvColorG, 0.0f, 1.0f, 25.5f); + Math_ApproachZeroF(&this->flashYScale, 1.0f, 0.25f); + if (this->flashYScale == 0.0f) { + this->flashState = FLASH_NONE; + } + } + if (this->dropTimer == 0) { + this->flashYScale = 0.0f; + Math_ApproachF(&this->dyna.actor.world.pos.y, -1000.0f, 1.0f, this->dyna.actor.speedXZ); + Math_ApproachF(&this->dyna.actor.speedXZ, 100.0f, 1.0f, 2.0f); + if (!(this->unwalledSides & OTYUKA_SIDE_EAST)) { + this->dyna.actor.shape.rot.z -= (s16)(this->dyna.actor.speedXZ * 30.0f); + } + if (!(this->unwalledSides & OTYUKA_SIDE_WEST)) { + this->dyna.actor.shape.rot.z += (s16)(this->dyna.actor.speedXZ * 30.0f); + } + if (!(this->unwalledSides & OTYUKA_SIDE_SOUTH)) { + this->dyna.actor.shape.rot.x += (s16)(this->dyna.actor.speedXZ * 30.0f); + } + if (!(this->unwalledSides & OTYUKA_SIDE_NORTH)) { + this->dyna.actor.shape.rot.x -= (s16)(this->dyna.actor.speedXZ * 30.0f); + } + if (this->dyna.actor.world.pos.y < -750.0f) { + if (player->actor.world.pos.y < -400.0f) { + accel.x = accel.z = 0.0f; + accel.y = 0.1f; + velocity.x = velocity.y = velocity.z = 0.0f; + + for (i = 0; i < 30; i++) { + pos.x = Rand_CenteredFloat(150.0f) + this->dyna.actor.world.pos.x; + pos.y = Rand_ZeroFloat(60.0f) + -750.0f; + pos.z = Rand_CenteredFloat(150.0f) + this->dyna.actor.world.pos.z; + func_8002836C(globalCtx, &pos, &velocity, &accel, &sDustPrimColor, &sDustEnvColor, + (s16)Rand_ZeroFloat(100.0f) + 250, 5, (s16)Rand_ZeroFloat(5.0f) + 15); + } + + func_80033DB8(globalCtx, 10, 15); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 40, NA_SE_EV_BOX_BREAK); + } + Actor_Kill(&this->dyna.actor); + } + } else { + if (this->dropTimer == 1) { + Audio_PlaySoundGeneral(NA_SE_EV_STONEDOOR_STOP, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_EV_BLOCKSINK - SFX_FLAG, &this->dyna.actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + Math_ApproachF(&this->dyna.actor.world.pos.y, -1000.0f, 1.0f, this->dyna.actor.speedXZ); + Math_ApproachF(&this->dyna.actor.speedXZ, 100.0f, 1.0f, 0.1f); + } + osSyncPrintf("MODE DOWN END\n"); +} + +void BgGanonOtyuka_DoNothing(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgGanonOtyuka_Update(Actor* thisx, GlobalContext* globalCtx) { + BgGanonOtyuka* this = (BgGanonOtyuka*)thisx; + + this->actionFunc(this, globalCtx); + this->flashTimer++; + if (this->dropTimer != 0) { + this->dropTimer--; + } +} + +void BgGanonOtyuka_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgGanonOtyuka* this = (BgGanonOtyuka*)thisx; + s16 i; + Gfx* phi_s2; + Gfx* phi_s1; + Camera* camera = Gameplay_GetCamera(globalCtx, 0); + Actor* actor; + BgGanonOtyuka* platform; + BossGanon* ganondorf; + f32 spBC = -30.0f; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_ganon_otyuka.c", 702); + + actor = globalCtx->actorCtx.actorLists[ACTORCAT_BOSS].head; + while (actor != NULL) { + if (actor->id == ACTOR_BOSS_GANON) { + ganondorf = (BossGanon*)actor; + + if (ganondorf->actor.params == 0) { + if (ganondorf->unk_198 != 0) { + spBC = -2000.0f; + } + + break; + } + } + + actor = actor->next; + } + + func_80093D18(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, sPlatformMaterialDL); + + actor = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + while (actor != NULL) { + if (actor->id == ACTOR_BG_GANON_OTYUKA) { + platform = (BgGanonOtyuka*)actor; + + if (platform->dyna.actor.projectedPos.z > spBC) { + if (camera->eye.y > platform->dyna.actor.world.pos.y) { + phi_s2 = sPlatformTopDL; + } else { + phi_s2 = sPlatformBottomDL; + } + Matrix_Translate(platform->dyna.actor.world.pos.x, platform->dyna.actor.world.pos.y, + platform->dyna.actor.world.pos.z, MTXMODE_NEW); + phi_s1 = NULL; + if (platform->isFalling) { + Matrix_RotateX((platform->dyna.actor.shape.rot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((platform->dyna.actor.shape.rot.z / (f32)0x8000) * M_PI, MTXMODE_APPLY); + if (camera->eye.y > platform->dyna.actor.world.pos.y) { + phi_s1 = sPlatformBottomDL; + } else { + phi_s1 = sPlatformTopDL; + } + } + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ganon_otyuka.c", 766), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, phi_s2); + + if (phi_s1 != NULL) { + gSPDisplayList(POLY_OPA_DISP++, phi_s1); + } + + for (i = 0; i < ARRAY_COUNT(sSides); i++) { + if (platform->visibleSides & sSides[i]) { + Matrix_Push(); + Matrix_Translate(sSideCenters[i].x, 0.0f, sSideCenters[i].z, MTXMODE_APPLY); + Matrix_RotateY(sSideAngles[i], MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, + Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ganon_otyuka.c", 785), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sPlatformSideDL); + Matrix_Pop(); + } + } + } + } + + actor = actor->next; + } + + func_80093D84(globalCtx->state.gfxCtx); + actor = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + while (actor != NULL) { + if (actor->id == ACTOR_BG_GANON_OTYUKA) { + platform = (BgGanonOtyuka*)actor; + + if ((platform->dyna.actor.projectedPos.z > -30.0f) && (platform->flashState != FLASH_NONE)) { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, platform->flashTimer * 4, 0, 32, 64, 1, + platform->flashTimer * 4, 0, 32, 64)); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, platform->flashPrimColorR, platform->flashPrimColorG, + platform->flashPrimColorB, 0); + gDPSetEnvColor(POLY_XLU_DISP++, platform->flashEnvColorR, platform->flashEnvColorG, + platform->flashEnvColorB, 128); + Matrix_Translate(platform->dyna.actor.world.pos.x, 0.0f, platform->dyna.actor.world.pos.z, MTXMODE_NEW); + + for (i = 0; i < ARRAY_COUNT(sSides); i++) { + if (platform->unwalledSides & sSides[i]) { + Matrix_Push(); + Matrix_Translate(sSideCenters[i].x, 0.0f, sSideCenters[i].z, MTXMODE_APPLY); + Matrix_RotateY(sSideAngles[i], MTXMODE_APPLY); + Matrix_Scale(0.3f, platform->flashYScale * 0.3f, 0.3f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, + Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ganon_otyuka.c", 847), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sFlashDL); + Matrix_Pop(); + } + } + } + } + + actor = actor->next; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_ganon_otyuka.c", 857); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.h b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.h new file mode 100644 index 000000000..79ba15e6d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.h @@ -0,0 +1,35 @@ +#ifndef Z_BG_GANON_OTYUKA_H +#define Z_BG_GANON_OTYUKA_H + +#include "ultra64.h" +#include "global.h" + +#define OTYUKA_SIDE_EAST (1 << 0) +#define OTYUKA_SIDE_WEST (1 << 1) +#define OTYUKA_SIDE_SOUTH (1 << 2) +#define OTYUKA_SIDE_NORTH (1 << 3) +#define OTYUKA_SIDE_ALL (OTYUKA_SIDE_EAST | OTYUKA_SIDE_WEST | OTYUKA_SIDE_SOUTH | OTYUKA_SIDE_NORTH) + +struct BgGanonOtyuka; + +typedef void (*BgGanonOtyukaActionFunc)(struct BgGanonOtyuka*, GlobalContext*); + +typedef struct BgGanonOtyuka { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgGanonOtyukaActionFunc actionFunc; + /* 0x0168 */ s16 dropTimer; + /* 0x016A */ u8 isFalling; + /* 0x016B */ u8 unwalledSides; + /* 0x016C */ u8 visibleSides; + /* 0x016D */ u8 flashTimer; + /* 0x016E */ u8 flashState; + /* 0x0170 */ f32 flashYScale; + /* 0x0174 */ f32 flashPrimColorR; + /* 0x0178 */ f32 flashPrimColorG; + /* 0x017C */ f32 flashPrimColorB; + /* 0x0180 */ f32 flashEnvColorR; + /* 0x0184 */ f32 flashEnvColorG; + /* 0x0188 */ f32 flashEnvColorB; +} BgGanonOtyuka; // size = 0x018C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c b/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c new file mode 100644 index 000000000..53a0bb82a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.c @@ -0,0 +1,136 @@ +/* + * File: z_bg_gate_shutter.c + * Overlay: Bg_Gate_Shutter + * Description: Death Mountain Trail Gate + */ + +#include "z_bg_gate_shutter.h" +#include "objects/object_spot01_matoyab/object_spot01_matoyab.h" +#include "vt.h" + +#define FLAGS 0 + +void BgGateShutter_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGateShutter_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGateShutter_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGateShutter_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8087828C(BgGateShutter* this, GlobalContext* globalCtx); +void func_80878300(BgGateShutter* this, GlobalContext* globalCtx); +void func_808783AC(BgGateShutter* this, GlobalContext* globalCtx); +void func_808783D4(BgGateShutter* this, GlobalContext* globalCtx); + +const ActorInit Bg_Gate_Shutter_InitVars = { + ACTOR_BG_GATE_SHUTTER, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_SPOT01_MATOYAB, + sizeof(BgGateShutter), + (ActorFunc)BgGateShutter_Init, + (ActorFunc)BgGateShutter_Destroy, + (ActorFunc)BgGateShutter_Update, + (ActorFunc)BgGateShutter_Draw, + NULL, +}; + +void BgGateShutter_Init(Actor* thisx, GlobalContext* globalCtx) { + BgGateShutter* this = (BgGateShutter*)thisx; + s32 pad[2]; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gKakarikoGuardGateCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + this->somePos.x = thisx->world.pos.x; + this->somePos.y = thisx->world.pos.y; + this->somePos.z = thisx->world.pos.z; + if (((gSaveContext.infTable[7] & 0x40) || (gSaveContext.eventChkInf[4] & 0x20)) && + (globalCtx->sceneNum == SCENE_SPOT01)) { + thisx->world.pos.x = -89.0f; + thisx->world.pos.z = -1375.0f; + } + thisx->scale.x = 1.0f; + thisx->scale.y = 1.0f; + thisx->scale.z = 1.0f; + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 柵でたなぁ ☆☆☆☆☆ \n" VT_RST); + this->actionFunc = func_8087828C; +} + +void BgGateShutter_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgGateShutter* this = (BgGateShutter*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8087828C(BgGateShutter* this, GlobalContext* globalCtx) { + if (this->openingState == 1 && !(gSaveContext.infTable[7] & 0x40)) { + this->unk_178 = 2; + this->actionFunc = func_80878300; + } else if (this->openingState == 2) { + this->unk_178 = 2; + this->actionFunc = func_80878300; + } else if (this->openingState < 0) { + this->unk_178 = 2; + this->actionFunc = func_808783D4; + } +} + +void func_80878300(BgGateShutter* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (this->unk_178 == 0) { + Audio_PlayActorSound2(thisx, NA_SE_EV_METALGATE_OPEN - SFX_FLAG); + thisx->world.pos.x -= 2.0f; + Math_ApproachF(&thisx->world.pos.z, -1375.0f, 0.8f, 0.3f); + if (thisx->world.pos.x < -89.0f) { + Audio_PlayActorSound2(thisx, NA_SE_EV_BRIDGE_OPEN_STOP); + this->unk_178 = 0x1E; + this->actionFunc = func_808783AC; + } + } +} + +void func_808783AC(BgGateShutter* this, GlobalContext* globalCtx) { + if (this->unk_178 == 0) { + this->openingState = 0; + this->actionFunc = func_8087828C; + } +} + +void func_808783D4(BgGateShutter* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (this->unk_178 == 0) { + Audio_PlayActorSound2(thisx, NA_SE_EV_METALGATE_OPEN - SFX_FLAG); + thisx->world.pos.x += 2.0f; + Math_ApproachF(&thisx->world.pos.z, -1350.0f, 0.8f, 0.3f); + if (thisx->world.pos.x > 90.0f) { + thisx->world.pos.x = 91.0f; + Audio_PlayActorSound2(thisx, NA_SE_EV_BRIDGE_OPEN_STOP); + this->unk_178 = 30; + this->actionFunc = func_808783AC; + } + } +} + +void BgGateShutter_Update(Actor* thisx, GlobalContext* globalCtx) { + BgGateShutter* this = (BgGateShutter*)thisx; + + if (this->unk_178 != 0) { + this->unk_178 -= 1; + } + this->actionFunc(this, globalCtx); +} + +void BgGateShutter_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_gate_shutter.c", 323); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_gate_shutter.c", 328), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gKakarikoGuardGateDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_gate_shutter.c", 333); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.h b/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.h new file mode 100644 index 000000000..4f93b6778 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_GATE_SHUTTER_H +#define Z_BG_GATE_SHUTTER_H + +#include "ultra64.h" +#include "global.h" + +struct BgGateShutter; + +typedef void (*BgGateShutterActionFunc)(struct BgGateShutter*, GlobalContext*); + +typedef struct BgGateShutter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgGateShutterActionFunc actionFunc; + /* 0x0168 */ s16 openingState; // 1 if gate is opening + /* 0x016C */ Vec3f somePos; + /* 0x0178 */ s16 unk_178; +} BgGateShutter; // size = 0x017C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gjyo_Bridge/z_bg_gjyo_bridge.c b/soh/src/overlays/actors/ovl_Bg_Gjyo_Bridge/z_bg_gjyo_bridge.c new file mode 100644 index 000000000..95dcbc50a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gjyo_Bridge/z_bg_gjyo_bridge.c @@ -0,0 +1,121 @@ +/* + * File: z_bg_gjyo_bridge.c + * Overlay: ovl_Bg_Gjyo_Bridge + * Description: Rainbow Bridge outside Ganon's Castle + */ + +#include "z_bg_gjyo_bridge.h" +#include "objects/object_gjyo_objects/object_gjyo_objects.h" +#include "scenes/dungeons/ganon_tou/ganon_tou_scene.h" + +#define FLAGS 0 + +void BgGjyoBridge_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGjyoBridge_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGjyoBridge_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGjyoBridge_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808787A4(BgGjyoBridge* this, GlobalContext* globalCtx); +void BgGjyoBridge_TriggerCutscene(BgGjyoBridge* this, GlobalContext* globalCtx); +void BgGjyoBridge_SpawnBridge(BgGjyoBridge* this, GlobalContext* globalCtx); + +const ActorInit Bg_Gjyo_Bridge_InitVars = { + ACTOR_BG_GJYO_BRIDGE, + ACTORCAT_PROP, + FLAGS, + OBJECT_GJYO_OBJECTS, + sizeof(BgGjyoBridge), + (ActorFunc)BgGjyoBridge_Init, + (ActorFunc)BgGjyoBridge_Destroy, + (ActorFunc)BgGjyoBridge_Update, + (ActorFunc)BgGjyoBridge_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgGjyoBridge_Init(Actor* thisx, GlobalContext* globalCtx) { + BgGjyoBridge* this = (BgGjyoBridge*)thisx; + s32 pad; + CollisionHeader* colHeader; + + colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gRainbowBridgeCol, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + if (gSaveContext.eventChkInf[4] & 0x2000) { + this->actionFunc = func_808787A4; + } else { + this->dyna.actor.draw = NULL; + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->actionFunc = BgGjyoBridge_TriggerCutscene; + } +} + +void BgGjyoBridge_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgGjyoBridge* this = (BgGjyoBridge*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808787A4(BgGjyoBridge* this, GlobalContext* globalCtx) { +} + +void BgGjyoBridge_TriggerCutscene(BgGjyoBridge* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) && + (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) && (player->actor.world.pos.x > -70.0f) && + (player->actor.world.pos.x < 300.0f) && (player->actor.world.pos.y > 1340.0f) && + (player->actor.world.pos.z > 1340.0f) && (player->actor.world.pos.z < 1662.0f) && + !Gameplay_InCsMode(globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gRainbowBridgeCs); + gSaveContext.cutsceneTrigger = 1; + this->actionFunc = BgGjyoBridge_SpawnBridge; + } +} + +void BgGjyoBridge_SpawnBridge(BgGjyoBridge* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[2] != NULL) && + (globalCtx->csCtx.npcActions[2]->action == 2)) { + this->dyna.actor.draw = BgGjyoBridge_Draw; + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + gSaveContext.eventChkInf[4] |= 0x2000; + } +} + +void BgGjyoBridge_Update(Actor* thisx, GlobalContext* globalCtx) { + BgGjyoBridge* this = (BgGjyoBridge*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgGjyoBridge_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgGjyoBridge* this = (BgGjyoBridge*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_gjyo_bridge.c", 260); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TexScroll(globalCtx->state.gfxCtx, globalCtx->gameplayFrames & 127, + globalCtx->gameplayFrames * -3 & 127, 32, 32)); + + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, -globalCtx->gameplayFrames & 127, 32, 32, 1, 0, + globalCtx->gameplayFrames & 127, 32, 32)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_gjyo_bridge.c", 281), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gRainbowBridgeDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_gjyo_bridge.c", 285); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gjyo_Bridge/z_bg_gjyo_bridge.h b/soh/src/overlays/actors/ovl_Bg_Gjyo_Bridge/z_bg_gjyo_bridge.h new file mode 100644 index 000000000..f0e74dbaf --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gjyo_Bridge/z_bg_gjyo_bridge.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_GJYO_BRIDGE_H +#define Z_BG_GJYO_BRIDGE_H + +#include "ultra64.h" +#include "global.h" + +struct BgGjyoBridge; + +typedef void (*BgGjyoBridgeActionFunc)(struct BgGjyoBridge*, GlobalContext*); + +typedef struct BgGjyoBridge { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgGjyoBridgeActionFunc actionFunc; +} BgGjyoBridge; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Darkmeiro/z_bg_gnd_darkmeiro.c b/soh/src/overlays/actors/ovl_Bg_Gnd_Darkmeiro/z_bg_gnd_darkmeiro.c new file mode 100644 index 000000000..47dce2bdd --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Darkmeiro/z_bg_gnd_darkmeiro.c @@ -0,0 +1,215 @@ +/* + * File: z_bg_gnd_darkmeiro.c + * Overlay: ovl_Bg_Gnd_Darkmeiro + * Description: Shadow trial actors (invisible path, clear block, and timer) + */ + +#include "z_bg_gnd_darkmeiro.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgGndDarkmeiro_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGndDarkmeiro_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGndDarkmeiro_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGndDarkmeiro_DrawInvisiblePath(Actor* thisx, GlobalContext* globalCtx); +void BgGndDarkmeiro_DrawSwitchBlock(Actor* thisx, GlobalContext* globalCtx); +void BgGndDarkmeiro_DrawStaticBlock(Actor* thisx, GlobalContext* globalCtx); + +void BgGndDarkmeiro_Noop(BgGndDarkmeiro* this, GlobalContext* globalCtx); +void BgGndDarkmeiro_UpdateBlockTimer(BgGndDarkmeiro* this, GlobalContext* globalCtx); +void BgGndDarkmeiro_UpdateStaticBlock(BgGndDarkmeiro* this, GlobalContext* globalCtx); +void BgGndDarkmeiro_UpdateSwitchBlock(BgGndDarkmeiro* this, GlobalContext* globalCtx); + +const ActorInit Bg_Gnd_Darkmeiro_InitVars = { + ACTOR_BG_GND_DARKMEIRO, + ACTORCAT_PROP, + FLAGS, + OBJECT_DEMO_KEKKAI, + sizeof(BgGndDarkmeiro), + (ActorFunc)BgGndDarkmeiro_Init, + (ActorFunc)BgGndDarkmeiro_Destroy, + (ActorFunc)BgGndDarkmeiro_Update, + NULL, + NULL, +}; + +void BgGndDarkmeiro_ToggleBlock(BgGndDarkmeiro* this, GlobalContext* globalCtx) { + if (this->actionFlags & 2) { + if (this->timer1 == 0) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->actionFlags &= ~2; + } + } else if (this->timer1 != 0) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->actionFlags |= 2; + } +} + +void BgGndDarkmeiro_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + CollisionHeader* colHeader = NULL; + BgGndDarkmeiro* this = (BgGndDarkmeiro*)thisx; + + this->updateFunc = BgGndDarkmeiro_Noop; + Actor_SetScale(&this->dyna.actor, 0.1f); + switch (this->dyna.actor.params & 0xFF) { + case DARKMEIRO_INVISIBLE_PATH: + this->dyna.actor.draw = BgGndDarkmeiro_DrawInvisiblePath; + this->dyna.actor.flags |= ACTOR_FLAG_7; + break; + case DARKMEIRO_CLEAR_BLOCK: + CollisionHeader_GetVirtual(&gClearBlockCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (((this->dyna.actor.params >> 8) & 0x3F) == 0x3F) { + this->updateFunc = BgGndDarkmeiro_UpdateStaticBlock; + this->dyna.actor.draw = BgGndDarkmeiro_DrawStaticBlock; + } else { + this->actionFlags = this->timer1 = this->timer2 = 0; + thisx->draw = BgGndDarkmeiro_DrawSwitchBlock; + this->updateFunc = BgGndDarkmeiro_UpdateSwitchBlock; + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else { + this->timer1 = 64; + this->actionFlags |= 2; + } + } + break; + case DARKMEIRO_BLOCK_TIMER: + this->actionFlags = this->timer1 = this->timer2 = 0; + this->updateFunc = BgGndDarkmeiro_UpdateBlockTimer; + thisx->draw = NULL; + if (Flags_GetSwitch(globalCtx, ((this->dyna.actor.params >> 8) & 0x3F) + 1)) { + this->timer1 = 64; + this->actionFlags |= 4; + } + if (Flags_GetSwitch(globalCtx, ((this->dyna.actor.params >> 8) & 0x3F) + 2)) { + this->timer2 = 64; + this->actionFlags |= 8; + } + if ((this->timer1 != 0) || (this->timer2 != 0)) { + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + } else { + Flags_UnsetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + } + break; + } +} + +void BgGndDarkmeiro_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgGndDarkmeiro* this = (BgGndDarkmeiro*)thisx; + + if ((this->dyna.actor.params & 0xFF) == 1) { + if (1) {} + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void BgGndDarkmeiro_Noop(BgGndDarkmeiro* this, GlobalContext* globalCtx) { +} + +void BgGndDarkmeiro_UpdateBlockTimer(BgGndDarkmeiro* this, GlobalContext* globalCtx) { + s16 timeLeft; + + if (Flags_GetSwitch(globalCtx, ((this->dyna.actor.params >> 8) & 0x3F) + 1)) { + if (this->actionFlags & 4) { + if (this->timer1 > 0) { + this->timer1--; + } else { + Flags_UnsetSwitch(globalCtx, ((this->dyna.actor.params >> 8) & 0x3F) + 1); + this->actionFlags &= ~4; + } + } else { + this->actionFlags |= 4; + this->timer1 = 304; + Audio_PlaySoundGeneral(NA_SE_EV_RED_EYE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + if (Flags_GetSwitch(globalCtx, ((this->dyna.actor.params >> 8) & 0x3F) + 2)) { + if (this->actionFlags & 8) { + if (this->timer2 > 0) { + this->timer2--; + } else { + Flags_UnsetSwitch(globalCtx, ((this->dyna.actor.params >> 8) & 0x3F) + 2); + this->actionFlags &= ~8; + } + } else { + this->actionFlags |= 8; + this->timer2 = 304; + Audio_PlaySoundGeneral(NA_SE_EV_RED_EYE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + timeLeft = CLAMP_MIN(this->timer1, this->timer2); + if (timeLeft > 0) { + func_8002F994(&this->dyna.actor, timeLeft); + } + if ((this->timer1 >= 64) || (this->timer2 >= 64)) { + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + } else { + Flags_UnsetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + } +} + +void BgGndDarkmeiro_UpdateStaticBlock(BgGndDarkmeiro* this, GlobalContext* globalCtx) { +} + +void BgGndDarkmeiro_UpdateSwitchBlock(BgGndDarkmeiro* this, GlobalContext* globalCtx) { + if (this->timer1 > 0) { + this->timer1--; + } + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + this->timer1 = 64; + } + + BgGndDarkmeiro_ToggleBlock(this, globalCtx); +} + +void BgGndDarkmeiro_Update(Actor* thisx, GlobalContext* globalCtx2) { + BgGndDarkmeiro* this = (BgGndDarkmeiro*)thisx; + GlobalContext* globalCtx = globalCtx2; + + this->updateFunc(this, globalCtx2); +} + +void BgGndDarkmeiro_DrawInvisiblePath(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListXlu(globalCtx, gShadowTrialPathDL); +} + +void BgGndDarkmeiro_DrawSwitchBlock(Actor* thisx, GlobalContext* globalCtx) { + BgGndDarkmeiro* this = (BgGndDarkmeiro*)thisx; + s16 vanishTimer; + + vanishTimer = this->timer1; + if (vanishTimer != 0) { + if (vanishTimer > 64) { + this->timer2 = (this->timer2 < 120) ? this->timer2 + 8 : 127; + } else if (vanishTimer > 16) { + this->timer2 = (Math_CosS((u16)this->timer1 * 0x1000) * 64.0f) + 127.0f; + if (this->timer2 > 127) { + this->timer2 = 127; + } + } else { + this->timer2 = vanishTimer * 8; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_darkmeiro.c", 378); + //! @bug Due to a bug in the display list, the transparency data is not used. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 198, 202, 208, this->timer2); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_darkmeiro.c", 380); + + Gfx_DrawDListXlu(globalCtx, gClearBlockDL); + } +} + +void BgGndDarkmeiro_DrawStaticBlock(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_darkmeiro.c", 391); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 198, 202, 208, 255); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_darkmeiro.c", 393); + + Gfx_DrawDListXlu(globalCtx, gClearBlockDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Darkmeiro/z_bg_gnd_darkmeiro.h b/soh/src/overlays/actors/ovl_Bg_Gnd_Darkmeiro/z_bg_gnd_darkmeiro.h new file mode 100644 index 000000000..6e1936e35 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Darkmeiro/z_bg_gnd_darkmeiro.h @@ -0,0 +1,32 @@ +#ifndef Z_BG_GND_DARKMEIRO_H +#define Z_BG_GND_DARKMEIRO_H + +#include "ultra64.h" +#include "global.h" + +struct BgGndDarkmeiro; + +typedef void (*BgGndDarkmeiroUpdateFunc)(struct BgGndDarkmeiro*, GlobalContext*); + +typedef struct BgGndDarkmeiro { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ u16 actionFlags; // 0x8 for timer 2, 0x4 for timer 1, 0x2 for blocks. + /* 0x0166 */ s16 timer1; // Vanish countdown for clear blocks + /* 0x0168 */ s16 timer2; // Transparency flicker for clear blocks. Does not work. + /* 0x016C */ BgGndDarkmeiroUpdateFunc updateFunc; +} BgGndDarkmeiro; // size = 0x0170 + +typedef enum { + /* 0 */ DARKMEIRO_INVISIBLE_PATH, // Textures for the invisible path in shadow trial. + + /* 1 */ DARKMEIRO_CLEAR_BLOCK, /* Clear blocks appear when their switch flag is set and + disappear 64 frames after their switch flag is cleared. + Clear blocks with flag 0x3F are always on. */ + + /* 2 */ DARKMEIRO_BLOCK_TIMER /* A block timer with switch flag N reacts to switch flags N+1 + and N+2 being set, setting its own switch flag and a timer + for 304 frames. There are separate timers for N+1 and N+2, + and the timer sets flag N if either timer is above 64 frames. */ +} DarkmeiroType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Firemeiro/z_bg_gnd_firemeiro.c b/soh/src/overlays/actors/ovl_Bg_Gnd_Firemeiro/z_bg_gnd_firemeiro.c new file mode 100644 index 000000000..1d26465db --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Firemeiro/z_bg_gnd_firemeiro.c @@ -0,0 +1,151 @@ +/* + * File: z_bg_gnd_firemeiro.c + * Overlay: ovl_Bg_Gnd_Firemeiro + * Description: Sinking lava platform (Ganon's Castle) + */ + +#include "z_bg_gnd_firemeiro.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgGndFiremeiro_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGndFiremeiro_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGndFiremeiro_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGndFiremeiro_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgGndFiremeiro_Sink(BgGndFiremeiro* this, GlobalContext* globalCtx); +void BgGndFiremeiro_Shake(BgGndFiremeiro* this, GlobalContext* globalCtx); +void BgGndFiremeiro_Rise(BgGndFiremeiro* this, GlobalContext* globalCtx); + +const ActorInit Bg_Gnd_Firemeiro_InitVars = { + ACTOR_BG_GND_FIREMEIRO, + ACTORCAT_PROP, + FLAGS, + OBJECT_DEMO_KEKKAI, + sizeof(BgGndFiremeiro), + (ActorFunc)BgGndFiremeiro_Init, + (ActorFunc)BgGndFiremeiro_Destroy, + (ActorFunc)BgGndFiremeiro_Update, + (ActorFunc)BgGndFiremeiro_Draw, + NULL, +}; + +void BgGndFiremeiro_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgGndFiremeiro* this = (BgGndFiremeiro*)thisx; + CollisionHeader* colHeader = NULL; + + ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); + Actor_SetScale(&this->dyna.actor, 0.1f); + this->initPos = this->dyna.actor.world.pos; + + if (this->dyna.actor.params == 0) { + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gFireTrialPlatformCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->actionFunc = BgGndFiremeiro_Rise; + } +} + +void BgGndFiremeiro_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgGndFiremeiro* this = (BgGndFiremeiro*)thisx; + + if (this->dyna.actor.params == 0) { + if (1) {} + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void BgGndFiremeiro_Sink(BgGndFiremeiro* this, GlobalContext* globalCtx) { + f32 sunkHeight = this->initPos.y - 150.0f; + + if (func_8004356C(&this->dyna)) { + this->timer = 10; + } + + if (sunkHeight < this->dyna.actor.world.pos.y) { + this->dyna.actor.world.pos.y -= 0.5f; + + if (this->dyna.actor.world.pos.y < sunkHeight) { + this->dyna.actor.world.pos.y = sunkHeight; + } + + func_8002F948(&this->dyna.actor, NA_SE_EV_ROLL_STAND_2 - SFX_FLAG); + } + + if (this->timer > 0) { + this->timer--; + } else { + this->actionFunc = BgGndFiremeiro_Rise; + } +} + +void BgGndFiremeiro_Shake(BgGndFiremeiro* this, GlobalContext* globalCtx) { + s32 pad; + f32 randSign; + + if (func_8004356C(&this->dyna)) { // Player standing on it + if (this->timer > 0) { + this->timer--; + + randSign = ((this->timer & 1) ? 2.0f : -2.0f); + + this->dyna.actor.world.pos = this->initPos; + this->dyna.actor.world.pos.x += randSign * Math_SinS(this->timer * 0x2FFF); + this->dyna.actor.world.pos.z += randSign * Math_CosS(this->timer * 0x2FFF); + this->dyna.actor.world.pos.y += Math_CosS(this->timer * 0x7FFF); + + if (!(this->timer % 4)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_SHAKE); + } + } else { + this->timer = 10; + this->dyna.actor.world.pos = this->initPos; + this->actionFunc = BgGndFiremeiro_Sink; + } + } else { + this->dyna.actor.world.pos = this->initPos; + this->actionFunc = BgGndFiremeiro_Rise; + } +} + +void BgGndFiremeiro_Rise(BgGndFiremeiro* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* thisx = &this->dyna.actor; + + if ((player->currentBoots != PLAYER_BOOTS_HOVER) && func_8004356C(&this->dyna)) { // Player standing on it + if (thisx->world.pos.y < this->initPos.y) { + this->actionFunc = BgGndFiremeiro_Sink; + this->timer = 20; + } else { + this->actionFunc = BgGndFiremeiro_Shake; + this->timer = 20; + } + } else { + if (thisx->world.pos.y < this->initPos.y) { + thisx->world.pos.y += 2.0f; + if (this->initPos.y < thisx->world.pos.y) { + thisx->world.pos.y = this->initPos.y; + } + } + } +} + +void BgGndFiremeiro_Update(Actor* thisx, GlobalContext* globalCtx) { + BgGndFiremeiro* this = (BgGndFiremeiro*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgGndFiremeiro_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_firemeiro.c", 280); + func_800943C8(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_gnd_firemeiro.c", 282), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFireTrialPlatformDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_firemeiro.c", 285); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Firemeiro/z_bg_gnd_firemeiro.h b/soh/src/overlays/actors/ovl_Bg_Gnd_Firemeiro/z_bg_gnd_firemeiro.h new file mode 100644 index 000000000..1a9a55ffe --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Firemeiro/z_bg_gnd_firemeiro.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_GND_FIREMEIRO_H +#define Z_BG_GND_FIREMEIRO_H + +#include "ultra64.h" +#include "global.h" + +struct BgGndFiremeiro; + +typedef void (*BgGndFiremeiroActionFunc)(struct BgGndFiremeiro*, GlobalContext*); + +typedef struct BgGndFiremeiro { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ Vec3f initPos; + /* 0x0170 */ u16 timer; + /* 0x0174 */ BgGndFiremeiroActionFunc actionFunc; +} BgGndFiremeiro; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Iceblock/z_bg_gnd_iceblock.c b/soh/src/overlays/actors/ovl_Bg_Gnd_Iceblock/z_bg_gnd_iceblock.c new file mode 100644 index 000000000..3fe07d388 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Iceblock/z_bg_gnd_iceblock.c @@ -0,0 +1,359 @@ +/* + * File: z_bg_gnd_iceblock.c + * Overlay: ovl_Bg_Gnd_Iceblock + * Description: Pushable ice block (Inside Ganon's Castle) + */ + +#include "z_bg_gnd_iceblock.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ GNDICE_IDLE, + /* 1 */ GNDICE_FALL, + /* 2 */ GNDICE_HOLE +} BgGndIceblockAction; + +void BgGndIceblock_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGndIceblock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGndIceblock_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGndIceblock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgGndIceblock_Idle(BgGndIceblock* this, GlobalContext* globalCtx); +void BgGndIceblock_Slide(BgGndIceblock* this, GlobalContext* globalCtx); + +const ActorInit Bg_Gnd_Iceblock_InitVars = { + ACTOR_BG_GND_ICEBLOCK, + ACTORCAT_PROP, + FLAGS, + OBJECT_DEMO_KEKKAI, + sizeof(BgGndIceblock), + (ActorFunc)BgGndIceblock_Init, + (ActorFunc)BgGndIceblock_Destroy, + (ActorFunc)BgGndIceblock_Update, + (ActorFunc)BgGndIceblock_Draw, + NULL, +}; + +static Color_RGBA8 sWhite = { 250, 250, 250, 255 }; +static Color_RGBA8 sGray = { 180, 180, 180, 255 }; +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static u8 sBlockPositions[2]; + +void BgGndIceblock_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgGndIceblock* this = (BgGndIceblock*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gWaterTrialIceBlockCol, &colHeader); + this->targetPos = this->dyna.actor.home.pos; + this->actionFunc = BgGndIceblock_Idle; + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.actor.world.pos.x == 2792.0f) { + this->dyna.actor.params = 0; + sBlockPositions[0] = 7; + } else if (this->dyna.actor.world.pos.x == 3032.0f) { + this->dyna.actor.params = 1; + sBlockPositions[1] = 14; + } else { + LOG_FLOAT("thisx->world.position.x", this->dyna.actor.world.pos.x, "../z_bg_gnd_iceblock.c", 138); + ASSERT(0, "0", "../z_bg_gnd_iceblock.c", 139); + } +} + +void BgGndIceblock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgGndIceblock* this = (BgGndIceblock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +/* + * Diagram of positions in the room: + * __ + * _________|10|_________ + * |*0* 6 *13****16*| + * |*1* 7 17 | + * | 2 14 18 | + * | 3 h8 15 19 | + * | 4 9 11 XX *20*| + * |*5* XX 12 *21*| + * ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + * XX are rocks + * ** are pits + * h is the hole. + * Block 0 starts at 7 and block 1 starts at 14 + */ + +void BgGndIceblock_SetPosition(BgGndIceblock* this, s32 blockPosition) { + Actor* thisx = &this->dyna.actor; + u8 xPosIdx[22] = { + 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 6, 6, 6, 6, 6, 6, + }; + u8 zPosIdx[22] = { + 5, 4, 3, 2, 1, 0, 5, 4, 2, 1, 6, 1, 0, 5, 3, 2, 5, 4, 3, 2, 1, 0, + }; + + sBlockPositions[thisx->params] = blockPosition; + this->targetPos.x = 2552.0f + (xPosIdx[blockPosition] * 120.0f); + this->targetPos.z = -540.0f - (zPosIdx[blockPosition] * 120.0f); +} + +s32 BgGndIceblock_CheckForBlock(s32 blockPosition) { + s32 i; + + for (i = 0; i < 2; i++) { + if (blockPosition == sBlockPositions[i]) { + return true; + } + } + return false; +} + +s32 BgGndIceblock_NextAction(BgGndIceblock* this) { + switch (sBlockPositions[this->dyna.actor.params]) { + case 0: + case 1: + case 5: + case 13: + case 16: + case 20: + case 21: + return GNDICE_FALL; + case 8: + return GNDICE_HOLE; + default: + return GNDICE_IDLE; + } +} + +void BgGndIceblock_SetNextPosition(BgGndIceblock* this) { + if (this->dyna.unk_158 == 0) { + switch (sBlockPositions[this->dyna.actor.params]) { + case 3: + case 4: + BgGndIceblock_SetPosition(this, 5); + break; + case 7: + if (BgGndIceblock_CheckForBlock(8)) { + BgGndIceblock_SetPosition(this, 9); + } else { + BgGndIceblock_SetPosition(this, 8); + } + break; + case 11: + BgGndIceblock_SetPosition(this, 12); + break; + case 14: + BgGndIceblock_SetPosition(this, 15); + break; + case 18: + case 19: + BgGndIceblock_SetPosition(this, 20); + break; + } + } else if (this->dyna.unk_158 == -0x8000) { + switch (sBlockPositions[this->dyna.actor.params]) { + case 2: + case 3: + BgGndIceblock_SetPosition(this, 1); + break; + case 7: + case 9: + BgGndIceblock_SetPosition(this, 6); + break; + case 11: + BgGndIceblock_SetPosition(this, 10); + break; + case 14: + case 15: + BgGndIceblock_SetPosition(this, 13); + break; + case 17: + BgGndIceblock_SetPosition(this, 16); + break; + case 18: + if (!BgGndIceblock_CheckForBlock(17)) { + BgGndIceblock_SetPosition(this, 16); + } + break; + } + } else if (this->dyna.unk_158 == 0x4000) { + switch (sBlockPositions[this->dyna.actor.params]) { + case 6: + BgGndIceblock_SetPosition(this, 13); + break; + case 7: + BgGndIceblock_SetPosition(this, 17); + break; + case 9: + BgGndIceblock_SetPosition(this, 11); + break; + case 12: + BgGndIceblock_SetPosition(this, 21); + break; + case 14: + BgGndIceblock_SetPosition(this, 18); + break; + case 15: + BgGndIceblock_SetPosition(this, 19); + break; + } + } else if (this->dyna.unk_158 == -0x4000) { + switch (sBlockPositions[this->dyna.actor.params]) { + case 6: + BgGndIceblock_SetPosition(this, 0); + break; + case 7: + BgGndIceblock_SetPosition(this, 1); + break; + case 9: + case 11: + BgGndIceblock_SetPosition(this, 4); + break; + case 14: + BgGndIceblock_SetPosition(this, 2); + break; + case 15: + if (BgGndIceblock_CheckForBlock(8)) { + BgGndIceblock_SetPosition(this, 3); + } else { + BgGndIceblock_SetPosition(this, 8); + } + break; + } + } +} + +void BgGndIceblock_Idle(BgGndIceblock* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + if (this->dyna.unk_150 > 0.0f) { + BgGndIceblock_SetNextPosition(this); + if (Actor_WorldDistXZToPoint(&this->dyna.actor, &this->targetPos) > 1.0f) { + func_8002DF54(globalCtx, &this->dyna.actor, 8); + this->actionFunc = BgGndIceblock_Slide; + } + } + this->dyna.unk_150 = 0.0f; + } +} + +void BgGndIceblock_Reset(BgGndIceblock* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* thisx = &this->dyna.actor; + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + if (Math_StepToF(&thisx->world.pos.y, thisx->home.pos.y, 1.0f)) { + this->targetPos = thisx->home.pos; + thisx->speedXZ = 0.0f; + this->actionFunc = BgGndIceblock_Idle; + switch (thisx->params) { + case 0: + sBlockPositions[0] = 7; + break; + case 1: + sBlockPositions[1] = 14; + break; + } + } +} + +void BgGndIceblock_Fall(BgGndIceblock* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + thisx->velocity.y += 1.0f; + if (Math_StepToF(&thisx->world.pos.y, thisx->home.pos.y - 300.0f, thisx->velocity.y)) { + thisx->velocity.y = 0.0f; + thisx->world.pos.x = thisx->home.pos.x; + thisx->world.pos.y = thisx->home.pos.y - 100.0f; + thisx->world.pos.z = thisx->home.pos.z; + if (Player_InCsMode(globalCtx)) { + func_8002DF54(globalCtx, thisx, 7); + } + this->actionFunc = BgGndIceblock_Reset; + } +} + +void BgGndIceblock_Hole(BgGndIceblock* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + thisx->velocity.y += 1.0f; + if (Math_StepToF(&thisx->world.pos.y, thisx->home.pos.y - 100.0f, thisx->velocity.y)) { + thisx->velocity.y = 0.0f; + if (Player_InCsMode(globalCtx)) { + func_8002DF54(globalCtx, thisx, 7); + } + this->actionFunc = BgGndIceblock_Idle; + } +} + +void BgGndIceblock_Slide(BgGndIceblock* this, GlobalContext* globalCtx) { + s32 atTarget; + Vec3f pos; + Vec3f velocity; + f32 spread; + Actor* thisx = &this->dyna.actor; + + Math_StepToF(&thisx->speedXZ, 10.0f, 0.5f); + atTarget = Math_StepToF(&thisx->world.pos.x, this->targetPos.x, thisx->speedXZ); + atTarget &= Math_StepToF(&thisx->world.pos.z, this->targetPos.z, thisx->speedXZ); + if (atTarget) { + thisx->speedXZ = 0.0f; + this->targetPos.x = thisx->world.pos.x; + this->targetPos.z = thisx->world.pos.z; + Audio_PlayActorSound2(thisx, NA_SE_EV_BLOCK_BOUND); + switch (BgGndIceblock_NextAction(this)) { + case GNDICE_IDLE: + this->actionFunc = BgGndIceblock_Idle; + func_8002DF54(globalCtx, thisx, 7); + break; + case GNDICE_FALL: + this->actionFunc = BgGndIceblock_Fall; + break; + case GNDICE_HOLE: + this->actionFunc = BgGndIceblock_Hole; + break; + } + } else if (thisx->speedXZ > 6.0f) { + spread = Rand_CenteredFloat(120.0f); + velocity.x = -(1.5f + Rand_ZeroOne()) * Math_SinS(this->dyna.unk_158); + velocity.y = Rand_ZeroOne() + 1.0f; + velocity.z = -(1.5f + Rand_ZeroOne()) * Math_CosS(this->dyna.unk_158); + pos.x = thisx->world.pos.x - (60.0f * Math_SinS(this->dyna.unk_158)) - (Math_CosS(this->dyna.unk_158) * spread); + pos.z = thisx->world.pos.z - (60.0f * Math_CosS(this->dyna.unk_158)) + (Math_SinS(this->dyna.unk_158) * spread); + pos.y = thisx->world.pos.y; + func_8002829C(globalCtx, &pos, &velocity, &sZeroVec, &sWhite, &sGray, 250, Rand_S16Offset(40, 15)); + spread = Rand_CenteredFloat(120.0f); + pos.x = thisx->world.pos.x - (60.0f * Math_SinS(this->dyna.unk_158)) + (Math_CosS(this->dyna.unk_158) * spread); + pos.z = thisx->world.pos.z - (60.0f * Math_CosS(this->dyna.unk_158)) - (Math_SinS(this->dyna.unk_158) * spread); + func_8002829C(globalCtx, &pos, &velocity, &sZeroVec, &sWhite, &sGray, 250, Rand_S16Offset(40, 15)); + func_8002F974(thisx, NA_SE_PL_SLIP_ICE_LEVEL - SFX_FLAG); + } +} + +void BgGndIceblock_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgGndIceblock* this = (BgGndIceblock*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgGndIceblock_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgGndIceblock* this = (BgGndIceblock*)thisx; + + Gfx_DrawDListOpa(globalCtx, gWaterTrialIceBlockDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Iceblock/z_bg_gnd_iceblock.h b/soh/src/overlays/actors/ovl_Bg_Gnd_Iceblock/z_bg_gnd_iceblock.h new file mode 100644 index 000000000..0010de14a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Iceblock/z_bg_gnd_iceblock.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_GND_ICEBLOCK_H +#define Z_BG_GND_ICEBLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct BgGndIceblock; + +typedef void (*BgGndIceblockActionFunc)(struct BgGndIceblock*, GlobalContext*); + +typedef struct BgGndIceblock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgGndIceblockActionFunc actionFunc; + /* 0x0168 */ Vec3f targetPos; +} BgGndIceblock; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Nisekabe/z_bg_gnd_nisekabe.c b/soh/src/overlays/actors/ovl_Bg_Gnd_Nisekabe/z_bg_gnd_nisekabe.c new file mode 100644 index 000000000..b97d8fd8a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Nisekabe/z_bg_gnd_nisekabe.c @@ -0,0 +1,64 @@ +/* + * File: z_bg_gnd_nisekabe.c + * Overlay: ovl_Bg_Gnd_Nisekabe + * Description: Ganon's Castle Fake Wall + */ + +#include "z_bg_gnd_nisekabe.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgGndNisekabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGndNisekabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGndNisekabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGndNisekabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Gnd_Nisekabe_InitVars = { + ACTOR_BG_GND_NISEKABE, + ACTORCAT_PROP, + FLAGS, + OBJECT_DEMO_KEKKAI, + sizeof(BgGndNisekabe), + (ActorFunc)BgGndNisekabe_Init, + (ActorFunc)BgGndNisekabe_Destroy, + (ActorFunc)BgGndNisekabe_Update, + (ActorFunc)BgGndNisekabe_Draw, + NULL, +}; + +void BgGndNisekabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgGndNisekabe* this = (BgGndNisekabe*)thisx; + + Actor_SetScale(&this->actor, 0.1); + this->actor.uncullZoneForward = 3000.0; +} + +void BgGndNisekabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgGndNisekabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgGndNisekabe* this = (BgGndNisekabe*)thisx; + + if (globalCtx->actorCtx.unk_03 != 0) { + this->actor.flags |= ACTOR_FLAG_7; + } else { + this->actor.flags &= ~ACTOR_FLAG_7; + } +} + +void BgGndNisekabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { + gLightTrialFakeWallDL, + gGanonsCastleUnusedFakeWallDL, + gGanonsCastleScrubsFakeWallDL, + }; + BgGndNisekabe* this = (BgGndNisekabe*)thisx; + u32 index = this->actor.params & 0xFF; + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + Gfx_DrawDListXlu(globalCtx, dLists[index]); + } else { + Gfx_DrawDListOpa(globalCtx, dLists[index]); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Nisekabe/z_bg_gnd_nisekabe.h b/soh/src/overlays/actors/ovl_Bg_Gnd_Nisekabe/z_bg_gnd_nisekabe.h new file mode 100644 index 000000000..32da8fa08 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Nisekabe/z_bg_gnd_nisekabe.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_GND_NISEKABE_H +#define Z_BG_GND_NISEKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgGndNisekabe; + +typedef struct BgGndNisekabe { + /* 0x0000 */ Actor actor; +} BgGndNisekabe; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Soulmeiro/z_bg_gnd_soulmeiro.c b/soh/src/overlays/actors/ovl_Bg_Gnd_Soulmeiro/z_bg_gnd_soulmeiro.c new file mode 100644 index 000000000..7c2d23e74 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Soulmeiro/z_bg_gnd_soulmeiro.c @@ -0,0 +1,218 @@ +/* + * File: z_bg_gnd_soulmeiro.c + * Overlay: ovl_Bg_Gnd_Soulmeiro + * Description: Web-Blocked Ceiling Hole (Inside Ganon's Castle) + */ + +#include "z_bg_gnd_soulmeiro.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" +#include "global.h" + +#define FLAGS 0 + +void BgGndSoulmeiro_Init(Actor* thisx, GlobalContext* globalCtx); +void BgGndSoulmeiro_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgGndSoulmeiro_Update(Actor* thisx, GlobalContext* globalCtx); +void BgGndSoulmeiro_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8087AF38(BgGndSoulmeiro* this, GlobalContext* globalCtx); +void func_8087B284(BgGndSoulmeiro* this, GlobalContext* globalCtx); +void func_8087B350(BgGndSoulmeiro* this, GlobalContext* globalCtx); + +const ActorInit Bg_Gnd_Soulmeiro_InitVars = { + ACTOR_BG_GND_SOULMEIRO, + ACTORCAT_PROP, + FLAGS, + OBJECT_DEMO_KEKKAI, + sizeof(BgGndSoulmeiro), + (ActorFunc)BgGndSoulmeiro_Init, + (ActorFunc)BgGndSoulmeiro_Destroy, + (ActorFunc)BgGndSoulmeiro_Update, + (ActorFunc)BgGndSoulmeiro_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0x00020800, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 50, 20, 20, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgGndSoulmeiro_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgGndSoulmeiro* this = (BgGndSoulmeiro*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actionFunc = NULL; + + switch (this->actor.params & 0xFF) { + case 0: + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actionFunc = func_8087B284; + if (Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_MIR_RAY, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 9); + this->actor.draw = NULL; + Actor_Kill(&this->actor); + return; + } else { + this->actor.draw = BgGndSoulmeiro_Draw; + } + break; + case 1: + case 2: + if (Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + this->actor.draw = BgGndSoulmeiro_Draw; + } else { + this->actor.draw = NULL; + } + this->actionFunc = func_8087B350; + break; + } +} + +void BgGndSoulmeiro_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgGndSoulmeiro* this = (BgGndSoulmeiro*)thisx; + + if ((this->actor.params & 0xFF) == 0) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void func_8087AF38(BgGndSoulmeiro* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f vecA; + Vec3f vecB; + Actor* thisx = &this->actor; + + if (this->unk_198 != 0) { + this->unk_198--; + } + + if (this->unk_198 == 20) { + Flags_SetSwitch(globalCtx, (thisx->params >> 8) & 0x3F); + thisx->draw = NULL; + } + + // This should be this->unk_198 == 0, this is required to match + if (!this->unk_198) { + Flags_SetSwitch(globalCtx, (thisx->params >> 8) & 0x3F); + Actor_Kill(&this->actor); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_MIR_RAY, thisx->world.pos.x, thisx->world.pos.y, + thisx->world.pos.z, 0, 0, 0, 9); + } else if ((this->unk_198 % 6) == 0) { + s32 i; + s16 temp_2 = Rand_ZeroOne() * (10922.0f); // This should be: 0x10000 / 6.0f + + vecA.y = 0.0f; + vecB.y = thisx->world.pos.y; + + for (i = 0; i < 6; i++) { + s16 temp_1 = Rand_CenteredFloat(0x2800) + temp_2; + f32 temp_3 = Math_SinS(temp_1); + f32 temp_4 = Math_CosS(temp_1); + f32 distXZ; + + vecB.x = thisx->world.pos.x + (120.0f * temp_3); + vecB.z = thisx->world.pos.z + (120.0f * temp_4); + distXZ = Math_Vec3f_DistXZ(&thisx->home.pos, &vecB) * (1.0f / 120.0f); + if (distXZ < 0.7f) { + temp_3 = Math_SinS(temp_1 + 0x8000); + temp_4 = Math_CosS(temp_1 + 0x8000); + vecB.x = thisx->world.pos.x + (120.0f * temp_3); + vecB.z = thisx->world.pos.z + (120.0f * temp_4); + distXZ = Math_Vec3f_DistXZ(&thisx->home.pos, &vecB) * (1.0f / 120.0f); + } + + vecA.x = 4.0f * temp_3 * distXZ; + vecA.y = 0.0f; + vecA.z = 4.0f * temp_4 * distXZ; + EffectSsDeadDb_Spawn(globalCtx, &thisx->home.pos, &vecA, &zeroVec, 60, 6, 255, 255, 150, 170, 255, 0, 0, 1, + 14, true); + temp_2 += 0x2AAA; + } + } +} + +void func_8087B284(BgGndSoulmeiro* this, GlobalContext* globalCtx) { + s32 pad; + + if (!Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + this->actor.draw = BgGndSoulmeiro_Draw; + if (this->collider.base.acFlags & AC_HIT) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->unk_198 = 40; + this->actionFunc = func_8087AF38; + } else { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +void func_8087B350(BgGndSoulmeiro* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + this->actor.draw = BgGndSoulmeiro_Draw; + } else { + this->actor.draw = NULL; + } +} + +void BgGndSoulmeiro_Update(Actor* thisx, GlobalContext* globalCtx) { + BgGndSoulmeiro* this = (BgGndSoulmeiro*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgGndSoulmeiro_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { + gSpiritTrialWebDL, + gSpiritTrialLightSourceDL, + gSpiritTrialLightFloorDL, + }; + s32 params = thisx->params & 0xFF; + + if (1) {} + + switch (params) { + case 0: + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_soulmeiro.c", 398); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_gnd_soulmeiro.c", 400), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, dLists[params]); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_gnd_soulmeiro.c", 403); + break; + case 1: + Gfx_DrawDListXlu(globalCtx, dLists[params]); + break; + case 2: + Gfx_DrawDListOpa(globalCtx, dLists[params]); + break; + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Gnd_Soulmeiro/z_bg_gnd_soulmeiro.h b/soh/src/overlays/actors/ovl_Bg_Gnd_Soulmeiro/z_bg_gnd_soulmeiro.h new file mode 100644 index 000000000..1d7024fff --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Gnd_Soulmeiro/z_bg_gnd_soulmeiro.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_GND_SOULMEIRO_H +#define Z_BG_GND_SOULMEIRO_H + +#include "ultra64.h" +#include "global.h" + +struct BgGndSoulmeiro; + +typedef void (*BgGndSoulmeiroActionFunc)(struct BgGndSoulmeiro*, GlobalContext*); + +typedef struct BgGndSoulmeiro { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ u16 unk_198; + /* 0x019C */ BgGndSoulmeiroActionFunc actionFunc; +} BgGndSoulmeiro; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka/z_bg_haka.c b/soh/src/overlays/actors/ovl_Bg_Haka/z_bg_haka.c new file mode 100644 index 000000000..2e5f776d2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka/z_bg_haka.c @@ -0,0 +1,165 @@ +/* + * File: z_bg_haka.c + * Overlay: ovl_Bg_Haka + * Description: Gravestone + */ + +#include "z_bg_haka.h" +#include "objects/object_haka/object_haka.h" + +#define FLAGS 0 + +void BgHaka_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHaka_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHaka_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHaka_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8087B758(BgHaka* this, Player* player); +void func_8087B7E8(BgHaka* this, GlobalContext* globalCtx); +void func_8087B938(BgHaka* this, GlobalContext* globalCtx); +void func_8087BAAC(BgHaka* this, GlobalContext* globalCtx); +void func_8087BAE4(BgHaka* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_InitVars = { + ACTOR_BG_HAKA, + ACTORCAT_BG, + FLAGS, + OBJECT_HAKA, + sizeof(BgHaka), + (ActorFunc)BgHaka_Init, + (ActorFunc)BgHaka_Destroy, + (ActorFunc)BgHaka_Update, + (ActorFunc)BgHaka_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(minVelocityY, 0, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHaka_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHaka* this = (BgHaka*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gGravestoneCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->actionFunc = func_8087B7E8; +} + +void BgHaka_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHaka* this = (BgHaka*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8087B758(BgHaka* this, Player* player) { + Vec3f sp1C; + + func_8002DBD0(&this->dyna.actor, &sp1C, &player->actor.world.pos); + if (fabsf(sp1C.x) < 34.6f && sp1C.z > -112.8f && sp1C.z < -36.0f) { + player->stateFlags2 |= 0x200; + } +} + +void func_8087B7E8(BgHaka* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + if (globalCtx->sceneNum == SCENE_SPOT02 && !LINK_IS_ADULT && IS_DAY) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + if (!Gameplay_InCsMode(globalCtx)) { + Message_StartTextbox(globalCtx, 0x5073, NULL); + this->dyna.actor.params = 100; + this->actionFunc = func_8087BAE4; + } + } else if (0.0f < this->dyna.unk_150 || + (globalCtx->sceneNum == SCENE_SPOT06 && !LINK_IS_ADULT && !Flags_GetSwitch(globalCtx, 0x23))) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } else { + this->dyna.actor.world.rot.y = this->dyna.actor.shape.rot.y + 0x8000; + this->actionFunc = func_8087B938; + } + } + func_8087B758(this, player); +} + +void func_8087B938(BgHaka* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 sp38; + + this->dyna.actor.speedXZ += 0.05f; + this->dyna.actor.speedXZ = CLAMP_MAX(this->dyna.actor.speedXZ, 1.5f); + sp38 = Math_StepToF(&this->dyna.actor.minVelocityY, 60.0f, this->dyna.actor.speedXZ); + this->dyna.actor.world.pos.x = + Math_SinS(this->dyna.actor.world.rot.y) * this->dyna.actor.minVelocityY + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = + Math_CosS(this->dyna.actor.world.rot.y) * this->dyna.actor.minVelocityY + this->dyna.actor.home.pos.z; + if (sp38 != 0) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + if (this->dyna.actor.params == 1) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } else if (!IS_DAY && globalCtx->sceneNum == SCENE_SPOT02) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_POH, this->dyna.actor.home.pos.x, + this->dyna.actor.home.pos.y, this->dyna.actor.home.pos.z, 0, this->dyna.actor.shape.rot.y, 0, + 1); + } + this->actionFunc = func_8087BAAC; + } + func_8002F974(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); +} + +void func_8087BAAC(BgHaka* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } +} + +void func_8087BAE4(BgHaka* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + if (this->dyna.actor.params != 0) { + this->dyna.actor.params -= 1; + } + if (this->dyna.unk_150 != 0.0f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } + if (this->dyna.actor.params == 0) { + this->actionFunc = func_8087B7E8; + } + func_8087B758(this, player); +} + +void BgHaka_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHaka* this = (BgHaka*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHaka_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka.c", 401); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka.c", 406), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGravestoneStoneDL); + Matrix_Translate(0.0f, 0.0f, thisx->minVelocityY * 10.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka.c", 416), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gGravestoneEarthDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka.c", 421); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka/z_bg_haka.h b/soh/src/overlays/actors/ovl_Bg_Haka/z_bg_haka.h new file mode 100644 index 000000000..20d71be2b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka/z_bg_haka.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_HAKA_H +#define Z_BG_HAKA_H + +#include "ultra64.h" +#include "global.h" + +struct BgHaka; + +typedef void (*BgHakaActionFunc)(struct BgHaka*, GlobalContext*); + +typedef struct BgHaka { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaActionFunc actionFunc; +} BgHaka; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Gate/z_bg_haka_gate.c b/soh/src/overlays/actors/ovl_Bg_Haka_Gate/z_bg_haka_gate.c new file mode 100644 index 000000000..7e3cd9155 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Gate/z_bg_haka_gate.c @@ -0,0 +1,377 @@ +/* + * File: z_bg_haka_gate.c + * Overlay: ovl_Bg_Haka_Gate + * Description: Truth Spinner Puzzle (Shadow Temple) + */ + +#include "z_bg_haka_gate.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS 0 + +// general purpose timer +#define vTimer actionVar1 + +// variables for turning the statue. Deg10 rotations are in tenths of a degree +#define vTurnDirection actionVar1 +#define vTurnRateDeg10 actionVar2 +#define vTurnAngleDeg10 actionVar3 +#define vRotYDeg10 actionVar4 +#define vInitTurnAngle actionVar5 + +// opening angle for floor +#define vOpenAngle actionVar2 + +// variables for the skull flames +#define vFlameScale actionVar3 +#define vIsSkullOfTruth actionVar4 +#define vScrollTimer actionVar5 + +#define SKULL_OF_TRUTH_FOUND 100 + +void BgHakaGate_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaGate_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaGate_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaGate_Draw(Actor* this, GlobalContext* globalCtx); + +void BgHakaGate_DoNothing(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_StatueInactive(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_StatueIdle(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_StatueTurn(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_FloorClosed(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_FloorOpen(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_GateWait(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_GateOpen(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_SkullOfTruth(BgHakaGate* this, GlobalContext* globalCtx); +void BgHakaGate_FalseSkull(BgHakaGate* this, GlobalContext* globalCtx); + +static s16 sSkullOfTruthRotY = 0x100; +static u8 sPuzzleState = 1; +static f32 sStatueDistToPlayer = 0; + +static s16 sStatueRotY; + +const ActorInit Bg_Haka_Gate_InitVars = { + ACTOR_BG_HAKA_GATE, + ACTORCAT_PROP, + FLAGS, + OBJECT_HAKA_OBJECTS, + sizeof(BgHakaGate), + (ActorFunc)BgHakaGate_Init, + (ActorFunc)BgHakaGate_Destroy, + (ActorFunc)BgHakaGate_Update, + (ActorFunc)BgHakaGate_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaGate_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHakaGate* this = (BgHakaGate*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + this->switchFlag = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (thisx->params == BGHAKAGATE_SKULL) { + if (sSkullOfTruthRotY != 0x100) { + this->actionFunc = BgHakaGate_FalseSkull; + } else if (ABS(thisx->shape.rot.y) < 0x4000) { + if ((Rand_ZeroOne() * 3.0f) < sPuzzleState) { + this->vIsSkullOfTruth = true; + sSkullOfTruthRotY = thisx->shape.rot.y + 0x8000; + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actionFunc = BgHakaGate_DoNothing; + } else { + this->actionFunc = BgHakaGate_SkullOfTruth; + } + } else { + sPuzzleState++; + this->actionFunc = BgHakaGate_FalseSkull; + } + } else { + this->actionFunc = BgHakaGate_FalseSkull; + } + this->vScrollTimer = Rand_ZeroOne() * 20.0f; + thisx->flags |= ACTOR_FLAG_4; + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->vFlameScale = 350; + } + } else { + if (thisx->params == BGHAKAGATE_STATUE) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_0131C4, &colHeader); + this->vTimer = 0; + sStatueDistToPlayer = 0.0f; + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actionFunc = BgHakaGate_StatueInactive; + } else { + this->actionFunc = BgHakaGate_StatueIdle; + } + } else if (thisx->params == BGHAKAGATE_FLOOR) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_010E10, &colHeader); + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actionFunc = BgHakaGate_DoNothing; + } else { + this->actionFunc = BgHakaGate_FloorClosed; + } + } else { // BGHAKAGATE_GATE + CollisionHeader_GetVirtual(&object_haka_objects_Col_00A938, &colHeader); + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actionFunc = BgHakaGate_DoNothing; + thisx->world.pos.y += 80.0f; + } else { + thisx->flags |= ACTOR_FLAG_4; + Actor_SetFocus(thisx, 30.0f); + this->actionFunc = BgHakaGate_GateWait; + } + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + } +} + +void BgHakaGate_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHakaGate* this = (BgHakaGate*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if (this->dyna.actor.params == BGHAKAGATE_STATUE) { + sSkullOfTruthRotY = 0x100; + sPuzzleState = 1; + } +} + +void BgHakaGate_DoNothing(BgHakaGate* this, GlobalContext* globalCtx) { +} + +void BgHakaGate_StatueInactive(BgHakaGate* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } +} + +void BgHakaGate_StatueIdle(BgHakaGate* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 linkDirection; + f32 forceDirection; + + if (this->dyna.unk_150 != 0.0f) { + if (this->vTimer == 0) { + this->vInitTurnAngle = this->dyna.actor.shape.rot.y - this->dyna.actor.yawTowardsPlayer; + sStatueDistToPlayer = this->dyna.actor.xzDistToPlayer; + forceDirection = (this->dyna.unk_150 >= 0.0f) ? 1.0f : -1.0f; + linkDirection = ((s16)(this->dyna.actor.yawTowardsPlayer - player->actor.shape.rot.y) > 0) ? -1 : 1; + this->vTurnDirection = linkDirection * forceDirection; + this->actionFunc = BgHakaGate_StatueTurn; + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + if (this->vTimer != 0) { + this->vTimer--; + } + } + } else { + if (sPuzzleState == SKULL_OF_TRUTH_FOUND) { + this->actionFunc = BgHakaGate_StatueInactive; + } else { + this->vTimer = 0; + } + } +} + +void BgHakaGate_StatueTurn(BgHakaGate* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 turnFinished; + s16 turnAngle; + + this->vTurnRateDeg10++; + this->vTurnRateDeg10 = CLAMP_MAX(this->vTurnRateDeg10, 5); + turnFinished = Math_StepToS(&this->vTurnAngleDeg10, 600, this->vTurnRateDeg10); + turnAngle = this->vTurnAngleDeg10 * this->vTurnDirection; + this->dyna.actor.shape.rot.y = (this->vRotYDeg10 + turnAngle) * 0.1f * (0x10000 / 360.0f); + if ((player->stateFlags2 & 0x10) && (sStatueDistToPlayer > 0.0f)) { + player->actor.world.pos.x = + this->dyna.actor.home.pos.x + + (Math_SinS(this->dyna.actor.shape.rot.y - this->vInitTurnAngle) * sStatueDistToPlayer); + player->actor.world.pos.z = + this->dyna.actor.home.pos.z + + (Math_CosS(this->dyna.actor.shape.rot.y - this->vInitTurnAngle) * sStatueDistToPlayer); + } else { + sStatueDistToPlayer = 0.0f; + } + sStatueRotY = this->dyna.actor.shape.rot.y; + if (turnFinished) { + player->stateFlags2 &= ~0x10; + this->vRotYDeg10 = (this->vRotYDeg10 + turnAngle) % 3600; + this->vTurnRateDeg10 = 0; + this->vTurnAngleDeg10 = 0; + this->vTimer = 5; + this->actionFunc = BgHakaGate_StatueIdle; + this->dyna.unk_150 = 0.0f; + } + func_8002F974(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); +} + +void BgHakaGate_FloorClosed(BgHakaGate* this, GlobalContext* globalCtx) { + if ((sStatueDistToPlayer > 1.0f) && (sStatueRotY != 0)) { + Player* player = GET_PLAYER(globalCtx); + f32 radialDist; + f32 angDist; + f32 cos = Math_CosS(sStatueRotY); + f32 sin = Math_SinS(sStatueRotY); + f32 dx = player->actor.world.pos.x - this->dyna.actor.world.pos.x; + f32 dz = player->actor.world.pos.z - this->dyna.actor.world.pos.z; + + radialDist = dx * cos - dz * sin; + angDist = dx * sin + dz * cos; + + if ((radialDist > 110.0f) || (fabsf(angDist) > 40.0f)) { + s16 yawDiff = sSkullOfTruthRotY - sStatueRotY; + + sStatueDistToPlayer = 0.0f; + if (ABS(yawDiff) < 0x80) { + Flags_SetSwitch(globalCtx, this->switchFlag); + sPuzzleState = SKULL_OF_TRUTH_FOUND; + this->actionFunc = BgHakaGate_DoNothing; + } else { + func_80078884(NA_SE_SY_ERROR); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_GROUND_GATE_OPEN); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->vTimer = 60; + this->actionFunc = BgHakaGate_FloorOpen; + } + } + } +} + +void BgHakaGate_FloorOpen(BgHakaGate* this, GlobalContext* globalCtx) { + if (this->vTimer != 0) { + this->vTimer--; + } + if (this->vTimer == 0) { + if (Math_ScaledStepToS(&this->vOpenAngle, 0, 0x800)) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->actionFunc = BgHakaGate_FloorClosed; + } + } else { + Math_ScaledStepToS(&this->vOpenAngle, 0x3000, 0x800); + } +} + +void BgHakaGate_GateWait(BgHakaGate* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->actionFunc = BgHakaGate_GateOpen; + } +} + +void BgHakaGate_GateOpen(BgHakaGate* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 80.0f, 1.0f)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_STOP); + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + this->actionFunc = BgHakaGate_DoNothing; + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG); + } +} + +void BgHakaGate_SkullOfTruth(BgHakaGate* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag) && Math_StepToS(&this->vFlameScale, 350, 20)) { + this->actionFunc = BgHakaGate_DoNothing; + } +} + +void BgHakaGate_FalseSkull(BgHakaGate* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + Math_StepToS(&this->vFlameScale, 350, 20); + } + if (globalCtx->actorCtx.unk_03) { + this->dyna.actor.flags |= ACTOR_FLAG_7; + } else { + this->dyna.actor.flags &= ~ACTOR_FLAG_7; + } +} + +void BgHakaGate_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHakaGate* this = (BgHakaGate*)thisx; + + this->actionFunc(this, globalCtx); + if (this->dyna.actor.params == BGHAKAGATE_SKULL) { + this->vScrollTimer++; + } +} + +void BgHakaGate_DrawFlame(BgHakaGate* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + f32 scale; + + if (this->vFlameScale > 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 716); + + if (1) {} + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + (this->vScrollTimer * -20) & 0x1FF, 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + + Matrix_Translate(thisx->world.pos.x, thisx->world.pos.y + 15.0f, thisx->world.pos.z, MTXMODE_NEW); + Matrix_RotateY(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) * (M_PI / 0x8000), MTXMODE_APPLY); + scale = this->vFlameScale * 0.00001f; + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 744), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 749); + } +} + +void BgHakaGate_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* displayLists[] = { + object_haka_objects_DL_012270, + object_haka_objects_DL_010A10, + object_haka_objects_DL_00A860, + object_haka_objects_DL_00F1B0, + }; + BgHakaGate* this = (BgHakaGate*)thisx; + MtxF currentMtxF; + + if (CHECK_FLAG_ALL(thisx->flags, ACTOR_FLAG_7)) { + Gfx_DrawDListXlu(globalCtx, object_haka_objects_DL_00F1B0); + } else { + func_80093D18(globalCtx->state.gfxCtx); + if (thisx->params == BGHAKAGATE_FLOOR) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 781); + Matrix_Get(¤tMtxF); + Matrix_Translate(0.0f, 0.0f, -2000.0f, MTXMODE_APPLY); + Matrix_RotateX(this->vOpenAngle * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 2000.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 788), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_haka_objects_DL_010A10); + Matrix_Put(¤tMtxF); + Matrix_Translate(0.0f, 0.0f, 2000.0f, MTXMODE_APPLY); + Matrix_RotateX(-this->vOpenAngle * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, -2000.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 796), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_haka_objects_DL_010C10); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_gate.c", 800); + } else { + Gfx_DrawDListOpa(globalCtx, displayLists[thisx->params]); + } + } + if (thisx->params == BGHAKAGATE_SKULL) { + BgHakaGate_DrawFlame(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Gate/z_bg_haka_gate.h b/soh/src/overlays/actors/ovl_Bg_Haka_Gate/z_bg_haka_gate.h new file mode 100644 index 000000000..005cdefc2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Gate/z_bg_haka_gate.h @@ -0,0 +1,29 @@ +#ifndef Z_BG_HAKA_GATE_H +#define Z_BG_HAKA_GATE_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaGate; + +typedef void (*BgHakaGateActionFunc)(struct BgHakaGate*, GlobalContext*); + +typedef struct BgHakaGate { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaGateActionFunc actionFunc; + /* 0x0168 */ u8 switchFlag; + /* 0x016A */ s16 actionVar1; + /* 0x016C */ s16 actionVar2; + /* 0x016E */ s16 actionVar3; + /* 0x0170 */ s16 actionVar4; + /* 0x0172 */ s16 actionVar5; +} BgHakaGate; // size = 0x0174 + +typedef enum { + BGHAKAGATE_STATUE, + BGHAKAGATE_FLOOR, + BGHAKAGATE_GATE, + BGHAKAGATE_SKULL +} BgHakaGateType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Huta/z_bg_haka_huta.c b/soh/src/overlays/actors/ovl_Bg_Haka_Huta/z_bg_haka_huta.c new file mode 100644 index 000000000..b198d537a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Huta/z_bg_haka_huta.c @@ -0,0 +1,216 @@ +/* + * File: z_bg_haka_huta.c + * Overlay: ovl_Bg_Haka_Huta + * Description: Coffin Lid + */ + +#include "z_bg_haka_huta.h" +#include "objects/object_hakach_objects/object_hakach_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgHakaHuta_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaHuta_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaHuta_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaHuta_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHakaHuta_SpawnDust(BgHakaHuta* this, GlobalContext* globalCtx); +void BgHakaHuta_PlaySound(BgHakaHuta* this, GlobalContext* globalCtx, u16 sfx); +void BgHakaHuta_SpawnEnemies(BgHakaHuta* this, GlobalContext* globalCtx); +void BgHakaHuta_Open(BgHakaHuta* this, GlobalContext* globalCtx); +void BgHakaHuta_SlideOpen(BgHakaHuta* this, GlobalContext* globalCtx); +void func_8087D720(BgHakaHuta* this, GlobalContext* globalCtx); +void BgHakaHuta_DoNothing(BgHakaHuta* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_Huta_InitVars = { + ACTOR_BG_HAKA_HUTA, + ACTORCAT_BG, + FLAGS, + OBJECT_HAKACH_OBJECTS, + sizeof(BgHakaHuta), + (ActorFunc)BgHakaHuta_Init, + (ActorFunc)BgHakaHuta_Destroy, + (ActorFunc)BgHakaHuta_Update, + (ActorFunc)BgHakaHuta_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaHuta_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHakaHuta* this = (BgHakaHuta*)thisx; + s16 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gBotwCoffinLidCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + this->unk_16A = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + if (Flags_GetSwitch(globalCtx, thisx->params)) { + this->counter = -1; + this->actionFunc = func_8087D720; + } else { + this->actionFunc = BgHakaHuta_SpawnEnemies; + } +} + +void BgHakaHuta_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaHuta* this = (BgHakaHuta*)thisx; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgHakaHuta_SpawnDust(BgHakaHuta* this, GlobalContext* globalCtx) { + static Vec3f sEffectAccel[] = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 30, 20, 50, 255 }; + static Color_RGBA8 envColor = { 0, 0, 0, 255 }; + f32 scale; + f32 phi_f20; + Vec3f effectPos; + Vec3f effectVel; + s32 i; + f32 new_Xpos; + f32 xPosOffset; + + phi_f20 = (this->dyna.actor.world.rot.y == 0) ? 1.0f : -1.0f; + effectVel.y = 0.0f; + effectVel.z = 0.0f; + effectVel.x = -0.5f * phi_f20; + effectPos.y = this->dyna.actor.world.pos.y; + effectPos.z = this->dyna.actor.world.pos.z; + new_Xpos = 50 - ((this->dyna.actor.world.pos.x - this->dyna.actor.home.pos.x) * phi_f20); + xPosOffset = new_Xpos * phi_f20; + + for (i = 0; i < 4; i++) { + if (i == 2) { + effectPos.z += 120.0f * phi_f20; + } + effectPos.x = this->dyna.actor.home.pos.x - (Rand_ZeroOne() * xPosOffset); + scale = ((Rand_ZeroOne() * 10.0f) + 50.0f); + func_8002829C(globalCtx, &effectPos, &effectVel, sEffectAccel, &primColor, &envColor, scale, 0xA); + } +} + +void BgHakaHuta_PlaySound(BgHakaHuta* this, GlobalContext* globalCtx, u16 sfx) { + Vec3f pos; + + pos.z = (this->dyna.actor.shape.rot.y == 0) ? this->dyna.actor.world.pos.z + 120.0f + : this->dyna.actor.world.pos.z - 120.0f; + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &pos, 30, sfx); +} + +void BgHakaHuta_SpawnEnemies(BgHakaHuta* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params) && !Player_InCsMode(globalCtx)) { + this->counter = 25; + this->actionFunc = BgHakaHuta_Open; + OnePointCutscene_Init(globalCtx, 6001, 999, &this->dyna.actor, MAIN_CAM); + if (this->unk_16A == 2) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_FIREFLY, + (this->dyna.actor.world.pos.x + (-25.0f) * Math_CosS(this->dyna.actor.shape.rot.y) + + 40.0f * Math_SinS(this->dyna.actor.shape.rot.y)), + this->dyna.actor.world.pos.y - 10.0f, + (this->dyna.actor.world.pos.z - (-25.0f) * Math_SinS(this->dyna.actor.shape.rot.y) + + Math_CosS(this->dyna.actor.shape.rot.y) * 40.0f), + 0, this->dyna.actor.shape.rot.y + 0x8000, 0, 2); + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_FIREFLY, + (this->dyna.actor.world.pos.x + (-25.0f) * (Math_CosS(this->dyna.actor.shape.rot.y)) + + Math_SinS(this->dyna.actor.shape.rot.y) * 80.0f), + this->dyna.actor.world.pos.y - 10.0f, + (this->dyna.actor.world.pos.z - (-25.0f) * (Math_SinS(this->dyna.actor.shape.rot.y)) + + Math_CosS(this->dyna.actor.shape.rot.y) * 80.0f), + 0, this->dyna.actor.shape.rot.y, 0, 2); + + } else if (this->unk_16A == 1) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_RD, + (this->dyna.actor.home.pos.x + (-25.0f) * (Math_CosS(this->dyna.actor.shape.rot.y)) + + Math_SinS(this->dyna.actor.shape.rot.y) * 100.0f), + this->dyna.actor.home.pos.y - 40.0f, + (this->dyna.actor.home.pos.z - (-25.0f) * (Math_SinS(this->dyna.actor.shape.rot.y)) + + Math_CosS(this->dyna.actor.shape.rot.y) * 100.0f), + 0, this->dyna.actor.shape.rot.y, 0, 0xFD); + } + } +} + +void BgHakaHuta_Open(BgHakaHuta* this, GlobalContext* globalCtx) { + f32 posOffset; + + if (this->counter != 0) { + this->counter--; + } + posOffset = (this->dyna.actor.world.rot.y == 0) ? 4.0f : -4.0f; + Math_StepToF(&this->dyna.actor.world.pos.x, this->dyna.actor.home.pos.x + posOffset, 2.0f); + if (this->counter == 0) { + this->counter = 37; + BgHakaHuta_PlaySound(this, globalCtx, NA_SE_EV_COFFIN_CAP_OPEN); + this->actionFunc = BgHakaHuta_SlideOpen; + } +} + +void BgHakaHuta_SlideOpen(BgHakaHuta* this, GlobalContext* globalCtx) { + f32 posOffset; + + if (this->counter != 0) { + this->counter--; + } + posOffset = (this->dyna.actor.world.rot.y == 0) ? 24.0f : -24.0f; + if (!Math_StepToF(&this->dyna.actor.world.pos.x, this->dyna.actor.home.pos.x + posOffset, 0.5f)) { + BgHakaHuta_SpawnDust(this, globalCtx); + } + if (this->counter == 0) { + BgHakaHuta_PlaySound(this, globalCtx, NA_SE_EV_COFFIN_CAP_BOUND); + this->actionFunc = func_8087D720; + } +} + +void func_8087D720(BgHakaHuta* this, GlobalContext* globalCtx) { + static Vec3f D_8087D958 = { 30.0f, 0.0f, 0.0f }; + static Vec3f D_8087D964 = { 0.03258f, 0.3258f, -0.9449f }; + MtxF mtx; + Vec3f vec; + s32 quakeIndex; + + this->counter++; + if (this->counter == 6) { + this->actionFunc = BgHakaHuta_DoNothing; + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, 0x7530); + Quake_SetQuakeValues(quakeIndex, 4, 0, 0, 0); + Quake_SetCountdown(quakeIndex, 2); + } else if (this->counter == 0) { + this->counter = 6; + this->actionFunc = BgHakaHuta_DoNothing; + } + + D_8087D958.x = this->counter + 24.0f; + if (D_8087D958.x > 30.0f) { + D_8087D958.x = 30.0f; + } + Matrix_RotateY(this->dyna.actor.world.rot.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_RotateAxis(this->counter * (191 * M_PI / 3750), &D_8087D964, MTXMODE_APPLY); + Matrix_MultVec3f(&D_8087D958, &vec); + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x + vec.x; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + vec.y; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z + vec.z; + Matrix_Get(&mtx); + Matrix_MtxFToYXZRotS(&mtx, &this->dyna.actor.shape.rot, 0); +} + +void BgHakaHuta_DoNothing(BgHakaHuta* this, GlobalContext* globalCtx) { +} + +void BgHakaHuta_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaHuta* this = (BgHakaHuta*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHakaHuta_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gBotwCoffinLidDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Huta/z_bg_haka_huta.h b/soh/src/overlays/actors/ovl_Bg_Haka_Huta/z_bg_haka_huta.h new file mode 100644 index 000000000..dff12e313 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Huta/z_bg_haka_huta.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_HAKA_HUTA_H +#define Z_BG_HAKA_HUTA_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaHuta; + +typedef void (*BgHakaHutaActionFunc)(struct BgHakaHuta*, GlobalContext*); + +typedef struct BgHakaHuta { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaHutaActionFunc actionFunc; + /* 0x0168 */ s16 counter; + /* 0x016A */ s16 unk_16A; +} BgHakaHuta; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Megane/z_bg_haka_megane.c b/soh/src/overlays/actors/ovl_Bg_Haka_Megane/z_bg_haka_megane.c new file mode 100644 index 000000000..596f1df7a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Megane/z_bg_haka_megane.c @@ -0,0 +1,142 @@ +/* + * File: z_bg_haka_megane.c + * Overlay: ovl_Bg_Haka_Megane + * Description: Shadow Temple Fake Walls + */ + +#include "z_bg_haka_megane.h" +#include "objects/object_hakach_objects/object_hakach_objects.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_7) + +void BgHakaMegane_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaMegane_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaMegane_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaMegane_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8087DB24(BgHakaMegane* this, GlobalContext* globalCtx); +void func_8087DBF0(BgHakaMegane* this, GlobalContext* globalCtx); +void BgHakaMegane_DoNothing(BgHakaMegane* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_Megane_InitVars = { + ACTOR_BG_HAKA_MEGANE, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgHakaMegane), + (ActorFunc)BgHakaMegane_Init, + (ActorFunc)BgHakaMegane_Destroy, + (ActorFunc)BgHakaMegane_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static CollisionHeader* sCollisionHeaders[] = { + &gBotw1Col, + &gBotw2Col, + NULL, + &object_haka_objects_Col_004330, + &object_haka_objects_Col_0044D0, + NULL, + &object_haka_objects_Col_004780, + &object_haka_objects_Col_004940, + NULL, + &object_haka_objects_Col_004B00, + NULL, + &object_haka_objects_Col_004CC0, + NULL, +}; + +static Gfx* sDLists[] = { + gBotwFakeWallsAndFloorsDL, gBotwThreeFakeFloorsDL, gBotwHoleTrap2DL, + object_haka_objects_DL_0040F0, object_haka_objects_DL_0043B0, object_haka_objects_DL_001120, + object_haka_objects_DL_0045A0, object_haka_objects_DL_0047F0, object_haka_objects_DL_0018F0, + object_haka_objects_DL_0049B0, object_haka_objects_DL_003CF0, object_haka_objects_DL_004B70, + object_haka_objects_DL_002ED0, +}; + +void BgHakaMegane_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMegane* this = (BgHakaMegane*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + + if (thisx->params < 3) { + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_HAKACH_OBJECTS); + } else { + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_HAKA_OBJECTS); + } + + if (this->objBankIndex < 0) { + Actor_Kill(thisx); + } else { + this->actionFunc = func_8087DB24; + } +} + +void BgHakaMegane_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMegane* this = (BgHakaMegane*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8087DB24(BgHakaMegane* this, GlobalContext* globalCtx) { + CollisionHeader* colHeader; + CollisionHeader* collision; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->dyna.actor.objBankIndex = this->objBankIndex; + this->dyna.actor.draw = BgHakaMegane_Draw; + Actor_SetObjectDependency(globalCtx, &this->dyna.actor); + if (globalCtx->roomCtx.curRoom.showInvisActors) { + this->actionFunc = func_8087DBF0; + collision = sCollisionHeaders[this->dyna.actor.params]; + if (collision != NULL) { + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } + } else { + this->actionFunc = BgHakaMegane_DoNothing; + } + } +} + +void func_8087DBF0(BgHakaMegane* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (globalCtx->actorCtx.unk_03 != 0) { + thisx->flags |= ACTOR_FLAG_7; + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else { + thisx->flags &= ~ACTOR_FLAG_7; + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void BgHakaMegane_DoNothing(BgHakaMegane* this, GlobalContext* globalCtx) { +} + +void BgHakaMegane_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMegane* this = (BgHakaMegane*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHakaMegane_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMegane* this = (BgHakaMegane*)thisx; + + if (CHECK_FLAG_ALL(thisx->flags, ACTOR_FLAG_7)) { + Gfx_DrawDListXlu(globalCtx, sDLists[thisx->params]); + } else { + Gfx_DrawDListOpa(globalCtx, sDLists[thisx->params]); + } + + if (thisx->params == 0) { + Gfx_DrawDListXlu(globalCtx, gBotwBloodSplatterDL); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Megane/z_bg_haka_megane.h b/soh/src/overlays/actors/ovl_Bg_Haka_Megane/z_bg_haka_megane.h new file mode 100644 index 000000000..dabde490e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Megane/z_bg_haka_megane.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_HAKA_MEGANE_H +#define Z_BG_HAKA_MEGANE_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaMegane; + +typedef void (*BgHakaMeganeActionFunc)(struct BgHakaMegane*, GlobalContext*); + +typedef struct BgHakaMegane { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaMeganeActionFunc actionFunc; + /* 0x0168 */ char unk_168[0x1]; + /* 0x0169 */ s8 objBankIndex; + /* 0x016A */ char unk_16A[0x2]; +} BgHakaMegane; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_MeganeBG/z_bg_haka_meganebg.c b/soh/src/overlays/actors/ovl_Bg_Haka_MeganeBG/z_bg_haka_meganebg.c new file mode 100644 index 000000000..f91be6f88 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_MeganeBG/z_bg_haka_meganebg.c @@ -0,0 +1,221 @@ +/* + * File: z_bg_haka_meganebg.c + * Overlay: ovl_Bg_Haka_MeganeBG + * Description: + */ + +#include "z_bg_haka_meganebg.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS 0 + +void BgHakaMeganeBG_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaMeganeBG_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaMeganeBG_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaMeganeBG_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8087DFF8(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E040(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E10C(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E1E0(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E258(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E288(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E2D8(BgHakaMeganeBG* this, GlobalContext* globalCtx); +void func_8087E34C(BgHakaMeganeBG* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_MeganeBG_InitVars = { + ACTOR_BG_HAKA_MEGANEBG, + ACTORCAT_BG, + FLAGS, + OBJECT_HAKA_OBJECTS, + sizeof(BgHakaMeganeBG), + (ActorFunc)BgHakaMeganeBG_Init, + (ActorFunc)BgHakaMeganeBG_Destroy, + (ActorFunc)BgHakaMeganeBG_Update, + (ActorFunc)BgHakaMeganeBG_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +// Unused +static u32 D_8087E3FC[] = { + 0x00000000, 0x00000000, 0x00000000, 0xC8C800FF, 0xFF0000FF, +}; + +static Gfx* D_8087E410[] = { + object_haka_objects_DL_008EB0, + object_haka_objects_DL_00A1A0, + object_haka_objects_DL_005000, + object_haka_objects_DL_000040, +}; + +void BgHakaMeganeBG_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHakaMeganeBG* this = (BgHakaMeganeBG*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->unk_168 = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + + if (thisx->params == 2) { + DynaPolyActor_Init(&this->dyna, DPM_UNK3); + thisx->flags |= ACTOR_FLAG_4; + CollisionHeader_GetVirtual(&object_haka_objects_Col_005334, &colHeader); + this->actionFunc = func_8087E258; + } else { + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + + if (thisx->params == 0) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_009168, &colHeader); + thisx->flags |= ACTOR_FLAG_7; + this->unk_16A = 20; + this->actionFunc = func_8087DFF8; + } else if (thisx->params == 3) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_000118, &colHeader); + thisx->home.pos.y += 100.0f; + + if (Flags_GetSwitch(globalCtx, this->unk_168)) { + this->actionFunc = func_8087E34C; + thisx->world.pos.y = thisx->home.pos.y; + } else { + thisx->flags |= ACTOR_FLAG_4; + this->actionFunc = func_8087E288; + } + } else { + CollisionHeader_GetVirtual(&object_haka_objects_Col_00A7F4, &colHeader); + this->unk_16A = 80; + this->actionFunc = func_8087E10C; + thisx->uncullZoneScale = 3000.0f; + thisx->uncullZoneDownward = 3000.0f; + } + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgHakaMeganeBG_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMeganeBG* this = (BgHakaMeganeBG*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8087DFF8(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + if (this->unk_16A != 0) { + this->unk_16A--; + } + + if (this->unk_16A == 0) { + this->unk_16A = 40; + this->dyna.actor.world.rot.y += 0x8000; + this->actionFunc = func_8087E040; + } +} + +void func_8087E040(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + f32 xSub; + + if (this->unk_16A != 0) { + this->unk_16A--; + } + + xSub = (sinf(((this->unk_16A * 0.025f) + 0.5f) * M_PI) + 1.0f) * 160.0f; + + if (this->dyna.actor.world.rot.y != this->dyna.actor.shape.rot.y) { + xSub = 320.0f - xSub; + } + + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x - xSub; + + if (this->unk_16A == 0) { + this->unk_16A = 20; + this->actionFunc = func_8087DFF8; + } +} + +void func_8087E10C(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + this->dyna.actor.velocity.y += 1.0f; + + if (this->dyna.actor.velocity.y > 20.0f) { + this->dyna.actor.velocity.y = 20.0f; + } else { + this->dyna.actor.velocity.y = this->dyna.actor.velocity.y; + } + + if (this->unk_16A != 0) { + this->unk_16A--; + } + + if (!Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 640.0f, + this->dyna.actor.velocity.y)) { + func_8002F974(&this->dyna.actor, NA_SE_EV_CHINETRAP_DOWN - SFX_FLAG); + } + + if (this->unk_16A == 0) { + this->unk_16A = 120; + this->actionFunc = func_8087E1E0; + this->dyna.actor.velocity.y = 0.0f; + } +} + +void func_8087E1E0(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 16.0f / 3.0f); + func_8002F974(&this->dyna.actor, NA_SE_EV_BRIDGE_CLOSE - SFX_FLAG); + + if (this->unk_16A != 0) { + this->unk_16A--; + } + + if (this->unk_16A == 0) { + this->unk_16A = 80; + this->actionFunc = func_8087E10C; + } +} + +void func_8087E258(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + this->dyna.actor.shape.rot.y += 0x180; + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); +} + +void func_8087E288(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->unk_168)) { + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->actionFunc = func_8087E2D8; + } +} + +void func_8087E2D8(BgHakaMeganeBG* this, GlobalContext* globalCtx) { + Math_StepToF(&this->dyna.actor.speedXZ, 30.0f, 2.0f); + + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, this->dyna.actor.speedXZ)) { + Actor_SetFocus(&this->dyna.actor, 50.0f); + this->actionFunc = func_8087E34C; + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_OPEN); + } +} + +void func_8087E34C(BgHakaMeganeBG* this, GlobalContext* globalCtx) { +} + +void BgHakaMeganeBG_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMeganeBG* this = (BgHakaMeganeBG*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHakaMeganeBG_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHakaMeganeBG* this = (BgHakaMeganeBG*)thisx; + s16 params = this->dyna.actor.params; + + if (params == 0) { + Gfx_DrawDListXlu(globalCtx, object_haka_objects_DL_008EB0); + } else { + Gfx_DrawDListOpa(globalCtx, D_8087E410[params]); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_MeganeBG/z_bg_haka_meganebg.h b/soh/src/overlays/actors/ovl_Bg_Haka_MeganeBG/z_bg_haka_meganebg.h new file mode 100644 index 000000000..18a0ba238 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_MeganeBG/z_bg_haka_meganebg.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_HAKA_MEGANEBG_H +#define Z_BG_HAKA_MEGANEBG_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaMeganeBG; + +typedef void (*BgHakaMeganeBGActionFunc)(struct BgHakaMeganeBG*, GlobalContext*); + +typedef struct BgHakaMeganeBG { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaMeganeBGActionFunc actionFunc; + /* 0x0168 */ u8 unk_168; + /* 0x016A */ s16 unk_16A; +} BgHakaMeganeBG; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Sgami/z_bg_haka_sgami.c b/soh/src/overlays/actors/ovl_Bg_Haka_Sgami/z_bg_haka_sgami.c new file mode 100644 index 000000000..35e96895a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Sgami/z_bg_haka_sgami.c @@ -0,0 +1,308 @@ +/* + * File: z_bg_haka_sgami.c + * Overlay: ovl_Bg_Haka_Sgami + * Description: Spinning Scythe Trap + */ + +#include "z_bg_haka_sgami.h" +#include "objects/object_haka_objects/object_haka_objects.h" +#include "objects/object_ice_objects/object_ice_objects.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4) + +typedef enum { + /* 0 */ SCYTHE_TRAP_SHADOW_TEMPLE, + /* 1 */ SCYTHE_TRAP_SHADOW_TEMPLE_INVISIBLE, + /* 2 */ SCYTHE_TRAP_ICE_CAVERN +} SpinningScytheTrapMode; + +#define SCYTHE_SPIN_TIME 32 + +void BgHakaSgami_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaSgami_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaSgami_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaSgami_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHakaSgami_SetupSpin(BgHakaSgami* this, GlobalContext* globalCtx); +void BgHakaSgami_Spin(BgHakaSgami* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_Sgami_InitVars = { + ACTOR_BG_HAKA_SGAMI, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgHakaSgami), + (ActorFunc)BgHakaSgami_Init, + (ActorFunc)BgHakaSgami_Destroy, + (ActorFunc)BgHakaSgami_Update, + NULL, + NULL, +}; + +static ColliderTrisElementInit sTrisElementsInit[4] = { + { + { + ELEMTYPE_UNK2, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 365.0f, 45.0f, 27.0f }, { 130.0f, 45.0f, 150.0f }, { 290.0f, 45.0f, 145.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 250.0f, 45.0f, 90.0f }, { 50.0f, 45.0f, 80.0f }, { 160.0f, 45.0f, 160.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { -305.0f, 33.0f, -7.0f }, { -220.0f, 33.0f, 40.0f }, { -130.0f, 33.0f, -5.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { -190.0f, 33.0f, 40.0f }, { -30.0f, 33.0f, 15.0f }, { -70.0f, 33.0f, -30.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 4, + sTrisElementsInit, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 80, 130, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 80, 130, MASS_IMMOVABLE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 4, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaSgami_Init(Actor* thisx, GlobalContext* globalCtx) { + static u8 sP1StartColor[] = { 250, 250, 250, 200 }; + static u8 sP2StartColor[] = { 200, 200, 200, 130 }; + static u8 sP1EndColor[] = { 200, 200, 200, 60 }; + static u8 sP2EndColor[] = { 150, 150, 150, 20 }; + BgHakaSgami* this = (BgHakaSgami*)thisx; + EffectBlureInit1 blureInit; + s32 i; + ColliderTris* colliderScythe = &this->colliderScythe; + + Actor_ProcessInitChain(thisx, sInitChain); + + this->unk_151 = thisx->params & 0xFF; + thisx->params = (thisx->params >> 8) & 0xFF; + + if (this->unk_151 != 0) { + thisx->flags |= ACTOR_FLAG_7; + } + + Collider_InitTris(globalCtx, colliderScythe); + Collider_SetTris(globalCtx, colliderScythe, thisx, &sTrisInit, this->colliderScytheItems); + Collider_InitCylinder(globalCtx, &this->colliderScytheCenter); + Collider_SetCylinder(globalCtx, &this->colliderScytheCenter, thisx, &sCylinderInit); + + this->colliderScytheCenter.dim.pos.x = thisx->world.pos.x; + this->colliderScytheCenter.dim.pos.y = thisx->world.pos.y; + this->colliderScytheCenter.dim.pos.z = thisx->world.pos.z; + + CollisionCheck_SetInfo(&thisx->colChkInfo, NULL, &sColChkInfoInit); + + for (i = 0; i < 4; i++) { + blureInit.p1StartColor[i] = sP1StartColor[i]; + blureInit.p2StartColor[i] = sP2StartColor[i]; + blureInit.p1EndColor[i] = sP1EndColor[i]; + blureInit.p2EndColor[i] = sP2EndColor[i]; + } + blureInit.elemDuration = 10; + blureInit.unkFlag = false; + blureInit.calcMode = 2; + Effect_Add(globalCtx, &this->blureEffectIndex[0], EFFECT_BLURE1, 0, 0, &blureInit); + Effect_Add(globalCtx, &this->blureEffectIndex[1], EFFECT_BLURE1, 0, 0, &blureInit); + + if (thisx->params == SCYTHE_TRAP_SHADOW_TEMPLE) { + this->requiredObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_HAKA_OBJECTS); + thisx->flags &= ~ACTOR_FLAG_0; + } else { + this->requiredObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_ICE_OBJECTS); + this->colliderScytheCenter.dim.radius = 30; + this->colliderScytheCenter.dim.height = 70; + Actor_SetFocus(thisx, 40.0f); + } + + if (this->requiredObjBankIndex < 0) { + Actor_Kill(thisx); + return; + } + + this->actionFunc = BgHakaSgami_SetupSpin; +} + +void BgHakaSgami_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaSgami* this = (BgHakaSgami*)thisx; + + Effect_Delete(globalCtx, this->blureEffectIndex[0]); + Effect_Delete(globalCtx, this->blureEffectIndex[1]); + Collider_DestroyTris(globalCtx, &this->colliderScythe); + Collider_DestroyCylinder(globalCtx, &this->colliderScytheCenter); +} + +void BgHakaSgami_SetupSpin(BgHakaSgami* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->requiredObjBankIndex)) { + this->actor.objBankIndex = this->requiredObjBankIndex; + this->actor.draw = BgHakaSgami_Draw; + this->timer = SCYTHE_SPIN_TIME; + this->actor.flags &= ~ACTOR_FLAG_4; + this->actionFunc = BgHakaSgami_Spin; + } +} + +void BgHakaSgami_Spin(BgHakaSgami* this, GlobalContext* globalCtx) { + static Vec3f blureEffectVertices2[] = { + { -20.0f, 50.0f, 130.0f }, + { -50.0f, 33.0f, 20.0f }, + }; + static Vec3f blureEffectVertices1[] = { + { 380.0f, 50.0f, 50.0f }, + { 310.0f, 33.0f, 0.0f }, + }; + s32 i; + s32 j; + Vec3f scytheVertices[3]; + f32 actorRotYSin; + f32 actorRotYCos; + s32 iterateCount; + ColliderTrisElementInit* elementInit; + + if (this->timer != 0) { + this->timer--; + } + + this->actor.shape.rot.y += ((s16)(512.0f * sinf(this->timer * (M_PI / 16.0f))) + 0x400) >> 1; + + if (this->timer == 0) { + this->timer = SCYTHE_SPIN_TIME; + } + + actorRotYSin = Math_SinS(this->actor.shape.rot.y); + actorRotYCos = Math_CosS(this->actor.shape.rot.y); + + iterateCount = (this->actor.params != 0) ? 4 : 2; + + for (i = iterateCount - 2; i < iterateCount; i++) { + elementInit = &sTrisInit.elements[i]; + + for (j = 0; j < 3; j++) { + scytheVertices[j].x = this->actor.world.pos.x + elementInit->dim.vtx[j].z * actorRotYSin + + elementInit->dim.vtx[j].x * actorRotYCos; + scytheVertices[j].y = this->actor.world.pos.y + elementInit->dim.vtx[j].y; + scytheVertices[j].z = this->actor.world.pos.z + elementInit->dim.vtx[j].z * actorRotYCos - + elementInit->dim.vtx[j].x * actorRotYSin; + } + + Collider_SetTrisVertices(&this->colliderScythe, i, &scytheVertices[0], &scytheVertices[1], &scytheVertices[2]); + + for (j = 0; j < 3; j++) { + scytheVertices[j].x = (2 * this->actor.world.pos.x) - scytheVertices[j].x; + scytheVertices[j].z = (2 * this->actor.world.pos.z) - scytheVertices[j].z; + } + + Collider_SetTrisVertices(&this->colliderScythe, (i + 2) % 4, &scytheVertices[0], &scytheVertices[1], + &scytheVertices[2]); + } + + if ((this->unk_151 == 0) || (globalCtx->actorCtx.unk_03 != 0)) { + scytheVertices[0].x = this->actor.world.pos.x + blureEffectVertices1[this->actor.params].z * actorRotYSin + + blureEffectVertices1[this->actor.params].x * actorRotYCos; + scytheVertices[0].y = this->actor.world.pos.y + blureEffectVertices1[this->actor.params].y; + scytheVertices[0].z = this->actor.world.pos.z + blureEffectVertices1[this->actor.params].z * actorRotYCos - + blureEffectVertices1[this->actor.params].x * actorRotYSin; + scytheVertices[1].x = this->actor.world.pos.x + blureEffectVertices2[this->actor.params].z * actorRotYSin + + blureEffectVertices2[this->actor.params].x * actorRotYCos; + scytheVertices[1].y = this->actor.world.pos.y + blureEffectVertices2[this->actor.params].y; + scytheVertices[1].z = this->actor.world.pos.z + blureEffectVertices2[this->actor.params].z * actorRotYCos - + blureEffectVertices2[this->actor.params].x * actorRotYSin; + EffectBlure_AddVertex(Effect_GetByIndex(this->blureEffectIndex[0]), &scytheVertices[0], &scytheVertices[1]); + + for (j = 0; j < 2; j++) { + scytheVertices[j].x = (2 * this->actor.world.pos.x) - scytheVertices[j].x; + scytheVertices[j].z = (2 * this->actor.world.pos.z) - scytheVertices[j].z; + } + + EffectBlure_AddVertex(Effect_GetByIndex(this->blureEffectIndex[1]), &scytheVertices[0], &scytheVertices[1]); + } + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderScythe.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderScytheCenter.base); + func_8002F974(&this->actor, NA_SE_EV_ROLLCUTTER_MOTOR - SFX_FLAG); +} + +void BgHakaSgami_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaSgami* this = (BgHakaSgami*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if (!(player->stateFlags1 & 0x300000C0) || (this->actionFunc == BgHakaSgami_SetupSpin)) { + this->actionFunc(this, globalCtx); + } +} + +void BgHakaSgami_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHakaSgami* this = (BgHakaSgami*)thisx; + + if (this->unk_151 != 0) { + Gfx_DrawDListXlu(globalCtx, object_haka_objects_DL_00BF20); + } else if (this->actor.params == SCYTHE_TRAP_SHADOW_TEMPLE) { + Gfx_DrawDListOpa(globalCtx, object_haka_objects_DL_00BF20); + } else { + Gfx_DrawDListOpa(globalCtx, object_ice_objects_DL_0021F0); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Sgami/z_bg_haka_sgami.h b/soh/src/overlays/actors/ovl_Bg_Haka_Sgami/z_bg_haka_sgami.h new file mode 100644 index 000000000..2d97b4794 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Sgami/z_bg_haka_sgami.h @@ -0,0 +1,23 @@ +#ifndef Z_BG_HAKA_SGAMI_H +#define Z_BG_HAKA_SGAMI_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaSgami; + +typedef void (*BgHakaSgamiActionFunc)(struct BgHakaSgami*, GlobalContext*); + +typedef struct BgHakaSgami { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgHakaSgamiActionFunc actionFunc; + /* 0x0150 */ s8 requiredObjBankIndex; + /* 0x0151 */ s8 unk_151; + /* 0x0152 */ s16 timer; + /* 0x0154 */ s32 blureEffectIndex[2]; + /* 0x015C */ ColliderCylinder colliderScytheCenter; + /* 0x01A8 */ ColliderTris colliderScythe; + /* 0x01C8 */ ColliderTrisElement colliderScytheItems[4]; +} BgHakaSgami; // size = 0x0338 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Ship/z_bg_haka_ship.c b/soh/src/overlays/actors/ovl_Bg_Haka_Ship/z_bg_haka_ship.c new file mode 100644 index 000000000..7cdd6093e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Ship/z_bg_haka_ship.c @@ -0,0 +1,235 @@ +/* + * File: z_bg_haka_ship.c + * Overlay: ovl_Bg_Haka_Ship + * Description: Shadow Temple Ship + */ + +#include "z_bg_haka_ship.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgHakaShip_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaShip_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaShip_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaShip_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgHakaShip_ChildUpdatePosition(BgHakaShip* this, GlobalContext* globalCtx); +void BgHakaShip_WaitForSong(BgHakaShip* this, GlobalContext* globalCtx); +void BgHakaShip_CutsceneStationary(BgHakaShip* this, GlobalContext* globalCtx); +void BgHakaShip_Move(BgHakaShip* this, GlobalContext* globalCtx); +void BgHakaShip_SetupCrash(BgHakaShip* this, GlobalContext* globalCtx); +void BgHakaShip_CrashShake(BgHakaShip* this, GlobalContext* globalCtx); +void BgHakaShip_CrashFall(BgHakaShip* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_Ship_InitVars = { + ACTOR_BG_HAKA_SHIP, + ACTORCAT_BG, + FLAGS, + OBJECT_HAKA_OBJECTS, + sizeof(BgHakaShip), + (ActorFunc)BgHakaShip_Init, + (ActorFunc)BgHakaShip_Destroy, + (ActorFunc)BgHakaShip_Update, + (ActorFunc)BgHakaShip_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaShip_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHakaShip* this = (BgHakaShip*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, 1); + this->switchFlag = (thisx->params >> 8) & 0xFF; + this->dyna.actor.params &= 0xFF; + + if (this->dyna.actor.params == 0) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_00E408, &colHeader); + this->counter = 8; + this->actionFunc = BgHakaShip_WaitForSong; + } else { + CollisionHeader_GetVirtual(&object_haka_objects_Col_00ED7C, &colHeader); + this->actionFunc = BgHakaShip_ChildUpdatePosition; + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->dyna.actor.world.rot.y = this->dyna.actor.shape.rot.y - 0x4000; + this->yOffset = 0; + if (this->dyna.actor.params == 0 && + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_HAKA_SHIP, + this->dyna.actor.world.pos.x + -10.0f, this->dyna.actor.world.pos.y + 82.0f, + this->dyna.actor.world.pos.z, 0, 0, 0, 1) == NULL) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgHakaShip_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaShip* this = (BgHakaShip*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Audio_StopSfxByPos(&this->bellSoundPos); +} + +void BgHakaShip_ChildUpdatePosition(BgHakaShip* this, GlobalContext* globalCtx) { + Actor* parent = this->dyna.actor.parent; + + if (parent != NULL && parent->update != NULL) { + this->dyna.actor.world.pos.x = parent->world.pos.x + -10.0f; + this->dyna.actor.world.pos.y = parent->world.pos.y + 82.0f; + this->dyna.actor.world.pos.z = parent->world.pos.z; + } else { + this->dyna.actor.parent = NULL; + } +} + +void BgHakaShip_WaitForSong(BgHakaShip* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + if (this->counter) { + this->counter--; + } + if (this->counter == 0) { + this->counter = 130; + this->actionFunc = BgHakaShip_CutsceneStationary; + osSyncPrintf("シーン 外輪船 ... アァクション!!\n"); + OnePointCutscene_Init(globalCtx, 3390, 999, &this->dyna.actor, MAIN_CAM); + } + } +} + +void BgHakaShip_CutsceneStationary(BgHakaShip* this, GlobalContext* globalCtx) { + if (this->counter) { + this->counter--; + } + this->yOffset = sinf(this->counter * (M_PI / 25)) * 6144.0f; + if (this->counter == 0) { + this->counter = 50; + this->actionFunc = BgHakaShip_Move; + } +} + +void BgHakaShip_Move(BgHakaShip* this, GlobalContext* globalCtx) { + f32 distanceFromHome; + Actor* child; + + if (this->counter) { + this->counter--; + } + if (this->counter == 0) { + this->counter = 50; + } + this->dyna.actor.world.pos.y = (sinf(this->counter * (M_PI / 25)) * 50.0f) + this->dyna.actor.home.pos.y; + + distanceFromHome = this->dyna.actor.home.pos.x - this->dyna.actor.world.pos.x; + if (distanceFromHome > 7650.0f) { + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x - 7650.0f; + this->dyna.actor.speedXZ = 0.0f; + } + if (distanceFromHome > 7600.0f && !Gameplay_InCsMode(globalCtx)) { + this->counter = 40; + this->dyna.actor.speedXZ = 0.0f; + Message_StartTextbox(globalCtx, 0x5071, NULL); + this->actionFunc = BgHakaShip_SetupCrash; + } else { + Math_StepToF(&this->dyna.actor.speedXZ, 4.0f, 0.2f); + } + child = this->dyna.actor.child; + if (child != NULL && child->update != NULL) { + child->shape.rot.z += ((655.0f / 13.0f) * this->dyna.actor.speedXZ); + } else { + this->dyna.actor.child = NULL; + } + this->yOffset = sinf(this->counter * (M_PI / 25)) * 6144.0f; +} + +void BgHakaShip_SetupCrash(BgHakaShip* this, GlobalContext* globalCtx) { + if (this->counter) { + this->counter--; + } + if (this->counter == 0) { + this->counter = 40; + this->actionFunc = BgHakaShip_CrashShake; + } + Math_ScaledStepToS(&this->yOffset, 0, 128); +} + +void BgHakaShip_CrashShake(BgHakaShip* this, GlobalContext* globalCtx) { + if (this->counter != 0) { + this->counter--; + } + this->dyna.actor.world.pos.y = this->counter % 4 * 3 - 6 + this->dyna.actor.home.pos.y; + if (!this->counter) { + this->dyna.actor.gravity = -1.0f; + this->actionFunc = BgHakaShip_CrashFall; + } + func_8002F974(&this->dyna.actor, NA_SE_EV_BLOCKSINK - SFX_FLAG); +} + +void BgHakaShip_CrashFall(BgHakaShip* this, GlobalContext* globalCtx) { + Actor* child; + + if (this->dyna.actor.home.pos.y - this->dyna.actor.world.pos.y > 2000.0f) { + Actor_Kill(&this->dyna.actor); + child = this->dyna.actor.child; + if (child != NULL && child->update != NULL) { + Actor_Kill(child); + } + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCKSINK - SFX_FLAG); + if ((this->dyna.actor.home.pos.y - this->dyna.actor.world.pos.y > 500.0f) && func_8004356C(&this->dyna)) { + Gameplay_TriggerVoidOut(globalCtx); + } + } +} + +void BgHakaShip_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaShip* this = (BgHakaShip*)thisx; + + this->actionFunc(this, globalCtx); + if (this->dyna.actor.params == 0) { + Actor_MoveForward(&this->dyna.actor); + } +} + +void BgHakaShip_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHakaShip* this = (BgHakaShip*)thisx; + f32 angleTemp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_ship.c", 528); + func_80093D18(globalCtx->state.gfxCtx); + if (this->dyna.actor.params == 0) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_ship.c", 534), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_haka_objects_DL_00D330); + angleTemp = this->yOffset * (M_PI / 0x8000); + Matrix_Translate(-3670.0f, 620.0f, 1150.0f, MTXMODE_APPLY); + Matrix_RotateZ(angleTemp, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_ship.c", 547), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_haka_objects_DL_005A70); + Matrix_Translate(0.0f, 0.0f, -2300.0f, MTXMODE_APPLY); + Matrix_RotateZ(-(2.0f * angleTemp), MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_ship.c", 556), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_haka_objects_DL_005A70); + } else { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_ship.c", 562), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_haka_objects_DL_00E910); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_ship.c", 568); + if (this->actionFunc == BgHakaShip_CutsceneStationary || this->actionFunc == BgHakaShip_Move) { + s32 pad; + Vec3f sp2C; + + sp2C.x = this->dyna.actor.world.pos.x + -367.0f; + sp2C.y = this->dyna.actor.world.pos.y + 62.0f; + sp2C.z = this->dyna.actor.world.pos.z; + + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &sp2C, &this->bellSoundPos); + func_80078914(&this->bellSoundPos, NA_SE_EV_SHIP_BELL - SFX_FLAG); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Ship/z_bg_haka_ship.h b/soh/src/overlays/actors/ovl_Bg_Haka_Ship/z_bg_haka_ship.h new file mode 100644 index 000000000..255c4c7d9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Ship/z_bg_haka_ship.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HAKA_SHIP_H +#define Z_BG_HAKA_SHIP_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaShip; + +typedef void (*BgHakaShipActionFunc)(struct BgHakaShip*, GlobalContext*); + +typedef struct BgHakaShip { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaShipActionFunc actionFunc; + /* 0x0168 */ u8 counter; + /* 0x0169 */ u8 switchFlag; + /* 0x016A */ s16 yOffset; + /* 0x016C */ Vec3f bellSoundPos; +} BgHakaShip; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Trap/z_bg_haka_trap.c b/soh/src/overlays/actors/ovl_Bg_Haka_Trap/z_bg_haka_trap.c new file mode 100644 index 000000000..83cd8dfd6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Trap/z_bg_haka_trap.c @@ -0,0 +1,556 @@ +/* + * File: z_bg_haka_trap.c + * Overlay: ovl_Bg_Haka_Trap + * Description: Shadow Temple Objects + */ + +#include "z_bg_haka_trap.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS 0 + +void BgHakaTrap_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTrap_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTrap_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTrap_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTrap_Reset(void); + +void func_8087FFC0(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808801B8(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808802D8(BgHakaTrap* this, GlobalContext* globalCtx); +void func_80880484(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808805C0(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808806BC(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808808F4(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808809B0(BgHakaTrap* this, GlobalContext* globalCtx); +void func_808809E4(BgHakaTrap* this, GlobalContext* globalCtx, s16 arg2); +void func_80880AE8(BgHakaTrap* this, GlobalContext* globalCtx); +void func_80880C0C(BgHakaTrap* this, GlobalContext* globalCtx); +void func_80880D68(BgHakaTrap* this); + +static UNK_TYPE D_80880F30 = 0; + +const ActorInit Bg_Haka_Trap_InitVars = { + ACTOR_BG_HAKA_TRAP, + ACTORCAT_BG, + FLAGS, + OBJECT_HAKA_OBJECTS, + sizeof(BgHakaTrap), + (ActorFunc)BgHakaTrap_Init, + (ActorFunc)BgHakaTrap_Destroy, + (ActorFunc)BgHakaTrap_Update, + (ActorFunc)BgHakaTrap_Draw, + (ActorResetFunc)BgHakaTrap_Reset, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 30, 90, 0, { 0, 0, 0 } }, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00020000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 1800.0f, 1200.0f, 0.0f }, { -1800.0f, 1200.0f, 0.0f }, { -1800.0f, 0.0f, 0.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00020000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 1800.0f, 1200.0f, 0.0f }, { -1800.0f, 0.0f, 0.0f }, { 1800.0f, 0.0f, 0.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 80, 100, MASS_IMMOVABLE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static UNK_TYPE D_80881014 = 0; +void BgHakaTrap_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTrap* this = (BgHakaTrap*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + thisx->params &= 0xFF; + + if (thisx->params != HAKA_TRAP_PROPELLER) { + Collider_InitCylinder(globalCtx, &this->colliderCylinder); + Collider_SetCylinder(globalCtx, &this->colliderCylinder, thisx, &sCylinderInit); + + if ((thisx->params == HAKA_TRAP_GUILLOTINE_SLOW) || (thisx->params == HAKA_TRAP_GUILLOTINE_FAST)) { + this->timer = 20; + this->colliderCylinder.dim.yShift = 10; + thisx->velocity.y = 0.1f; + + if (thisx->params == HAKA_TRAP_GUILLOTINE_FAST) { + thisx->params = HAKA_TRAP_GUILLOTINE_SLOW; + this->unk_16A = 1; + } + + this->actionFunc = func_80880484; + } else { + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + thisx->flags |= ACTOR_FLAG_4; + + if (thisx->params == HAKA_TRAP_SPIKED_BOX) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_009CD0, &colHeader); + this->timer = 30; + + if (D_80881014 != 0) { + this->actionFunc = func_808808F4; + D_80881014 = 0; + } else { + D_80881014 = 1; + this->actionFunc = func_808806BC; + thisx->velocity.y = 0.5f; + } + + thisx->floorHeight = thisx->home.pos.y - 225.0f; + this->unk_16A = (thisx->floorHeight + 50.0f) - 25.0f; + + this->colliderCylinder.dim.radius = 10; + this->colliderCylinder.dim.height = 40; + } else { + if (thisx->params == HAKA_TRAP_SPIKED_WALL) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_0081D0, &colHeader); + thisx->home.pos.x -= 200.0f; + } else { + thisx->home.pos.x += 200.0f; + CollisionHeader_GetVirtual(&object_haka_objects_Col_008D10, &colHeader); + } + + Collider_InitTris(globalCtx, &this->colliderSpikes); + Collider_SetTris(globalCtx, &this->colliderSpikes, thisx, &sTrisInit, this->colliderSpikesItem); + + this->colliderCylinder.dim.radius = 18; + this->colliderCylinder.dim.height = 115; + + this->colliderCylinder.info.toucherFlags = this->colliderCylinder.info.toucherFlags; + this->colliderCylinder.info.toucherFlags |= TOUCH_SFX_WOOD; + + this->actionFunc = func_808801B8; + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + } + } else { + this->timer = 40; + this->actionFunc = func_808809B0; + thisx->uncullZoneScale = 500.0f; + } + + CollisionCheck_SetInfo(&thisx->colChkInfo, 0, &sColChkInfoInit); +} + +void BgHakaTrap_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTrap* this = (BgHakaTrap*)thisx; + + if (this->dyna.actor.params != HAKA_TRAP_PROPELLER) { + if (this->dyna.actor.params != HAKA_TRAP_GUILLOTINE_SLOW) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if ((this->dyna.actor.params == HAKA_TRAP_SPIKED_WALL) || + (this->dyna.actor.params == HAKA_TRAP_SPIKED_WALL_2)) { + Collider_DestroyTris(globalCtx, &this->colliderSpikes); + } + } + + Collider_DestroyCylinder(globalCtx, &this->colliderCylinder); + } + + Audio_StopSfxByPos(&this->unk_16C); +} + +void func_8087FFC0(BgHakaTrap* this, GlobalContext* globalCtx) { + f32 cosine; + Vec3f sp28; + f32 sine; + f32 zNonNegative; + Player* player = GET_PLAYER(globalCtx); + + func_8002DBD0(&this->dyna.actor, &sp28, &player->actor.world.pos); + + sine = Math_SinS(this->dyna.actor.shape.rot.y); + cosine = Math_CosS(this->dyna.actor.shape.rot.y); + if (this->dyna.actor.params == HAKA_TRAP_GUILLOTINE_SLOW) { + sp28.x = CLAMP(sp28.x, -50.0f, 50.0f); + zNonNegative = (sp28.z >= 0.0f) ? 1.0f : -1.0f; + sp28.z = zNonNegative * -15.0f; + } else { + sp28.x = -CLAMP(sp28.x, -162.0f, 162.0f); + zNonNegative = (sp28.z >= 0.0f) ? 1.0f : -1.0f; + sp28.z = zNonNegative * 15.0f; + } + + this->colliderCylinder.dim.pos.x = this->dyna.actor.world.pos.x + sp28.x * cosine + sp28.z * sine; + this->colliderCylinder.dim.pos.z = this->dyna.actor.world.pos.z + sp28.x * sine + sp28.z * cosine; +} + +void func_808801B8(BgHakaTrap* this, GlobalContext* globalCtx) { + static UNK_TYPE D_80881018 = 0; + Player* player = GET_PLAYER(globalCtx); + + if ((D_80880F30 == 0) && (!Player_InCsMode(globalCtx))) { + if (!Math_StepToF(&this->dyna.actor.world.pos.x, this->dyna.actor.home.pos.x, 0.5f)) { + func_8002F974(&this->dyna.actor, NA_SE_EV_TRAP_OBJ_SLIDE - SFX_FLAG); + } else if (this->dyna.actor.params == HAKA_TRAP_SPIKED_WALL) { + D_80881018 |= 1; + } else if (this->dyna.actor.params == HAKA_TRAP_SPIKED_WALL_2) { + D_80881018 |= 2; + } + } + + func_8087FFC0(this, globalCtx); + + if (this->colliderSpikes.base.acFlags & AC_HIT) { + this->timer = 20; + D_80880F30 = 1; + this->actionFunc = func_808802D8; + } else if (D_80881018 == 3) { + D_80881018 = 4; + player->actor.bgCheckFlags |= 0x100; + } +} + +void func_808802D8(BgHakaTrap* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f vector; + f32 xScale; + s32 i; + + if (this->timer != 0) { + this->timer--; + } + + func_8002F974(&this->dyna.actor, NA_SE_EV_BURN_OUT - SFX_FLAG); + + for (i = 0; i < 2; i++) { + f32 rand = Rand_ZeroOne(); + + xScale = (this->dyna.actor.params == HAKA_TRAP_SPIKED_WALL) ? -30.0f : 30.0f; + + vector.x = xScale * rand + this->dyna.actor.world.pos.x; + vector.y = Rand_ZeroOne() * 10.0f + this->dyna.actor.world.pos.y + 30.0f; + vector.z = Rand_CenteredFloat(320.0f) + this->dyna.actor.world.pos.z; + + EffectSsDeadDb_Spawn(globalCtx, &vector, &zeroVec, &zeroVec, 130, 20, 255, 255, 150, 170, 255, 0, 0, 1, 9, + false); + } + + if (this->timer == 0) { + D_80880F30 = 0; + Actor_Kill(&this->dyna.actor); + } +} + +void func_80880484(BgHakaTrap* this, GlobalContext* globalCtx) { + s32 sp24; + s32 timer; + + if (this->unk_16A) { + this->dyna.actor.velocity.y *= 3.0f; + } else { + this->dyna.actor.velocity.y *= 2.0f; + } + + if (this->timer != 0) { + this->timer -= 1; + } + + sp24 = + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 185.0f, this->dyna.actor.velocity.y); + timer = this->timer; + + if ((timer == 10 && !this->unk_16A) || (timer == 13 && this->unk_16A)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_GUILLOTINE_BOUND); + } + + if (this->timer == 0) { + this->dyna.actor.velocity.y = 0.0f; + this->timer = (this->unk_16A) ? 10 : 40; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_GUILLOTINE_UP); + this->actionFunc = func_808805C0; + } + + func_8087FFC0(this, globalCtx); + + if (sp24 == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); + } +} + +void func_808805C0(BgHakaTrap* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->unk_16A) { + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 27.0f); + } else { + if (this->timer > 20) { + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 90.0f, 9.0f); + } else { + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 4.5f); + } + + if (this->timer == 20) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_GUILLOTINE_UP); + } + } + + if (this->timer == 0) { + this->timer = 20; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + this->dyna.actor.velocity.y = 0.1f; + this->actionFunc = func_80880484; + } + + func_8087FFC0(this, globalCtx); +} + +void func_808806BC(BgHakaTrap* this, GlobalContext* globalCtx) { + Vec3f vector; + f32 tempf20; + f32 temp; + s32 i; + s32 sp64; + + this->dyna.actor.velocity.y *= 1.6f; + + if (this->timer != 0) { + this->timer--; + } + + vector.x = this->dyna.actor.world.pos.x + 90.0f; + vector.y = (this->dyna.actor.world.pos.y + 1.0f) + 25.0f; + vector.z = this->dyna.actor.world.pos.z; + + tempf20 = this->dyna.actor.floorHeight; + + for (i = 0; i < 3; i++) { + temp = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->dyna.actor.floorPoly, &sp64, &this->dyna.actor, + &vector) - + 25.0f; + if (tempf20 < temp) { + tempf20 = temp; + } + + vector.x -= 90.0f; + } + + if (Math_StepToF(&this->dyna.actor.world.pos.y, tempf20, this->dyna.actor.velocity.y)) { + if (this->dyna.actor.velocity.y > 0.01f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_TRAP_BOUND); + } + this->dyna.actor.velocity.y = 0.0f; + } + + if (this->dyna.actor.velocity.y >= 0.01f) { + func_8002F974(&this->dyna.actor, NA_SE_EV_CHINETRAP_DOWN - SFX_FLAG); + } + + if (this->timer == 0) { + this->dyna.actor.velocity.y = 0.0f; + this->timer = 30; + this->unk_16A = (s16)this->dyna.actor.world.pos.y + 50.0f; + this->unk_16A = CLAMP_MAX(this->unk_16A, this->dyna.actor.home.pos.y); + + this->actionFunc = func_808808F4; + } +} + +void func_808808F4(BgHakaTrap* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer > 20) { + this->unk_169 = Math_StepToF(&this->dyna.actor.world.pos.y, this->unk_16A, 15.0f); + } else { + this->unk_169 = Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 20.0f); + } + + if (this->timer == 0) { + this->timer = 30; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + this->dyna.actor.velocity.y = 0.5f; + this->actionFunc = func_808806BC; + } +} + +void func_808809B0(BgHakaTrap* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer -= 1; + } + + if (this->timer == 0) { + this->actionFunc = func_80880AE8; + } +} + +void func_808809E4(BgHakaTrap* this, GlobalContext* globalCtx, s16 arg2) { + Player* player = GET_PLAYER(globalCtx); + Vec3f sp18; + + func_8002DBD0(&this->dyna.actor, &sp18, &player->actor.world.pos); + + if ((fabsf(sp18.x) < 70.0f) && (fabsf(sp18.y) < 100.0f) && (sp18.z < 500.0f) && + (GET_PLAYER(globalCtx)->currentBoots != PLAYER_BOOTS_IRON)) { + player->windSpeed = ((500.0f - sp18.z) * 0.06f + 5.0f) * arg2 * (1.0f / 0x3A00) * (2.0f / 3.0f); + player->windDirection = this->dyna.actor.shape.rot.y; + } +} + +void func_80880AE8(BgHakaTrap* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + if (Math_ScaledStepToS(&this->dyna.actor.world.rot.z, 0, this->dyna.actor.world.rot.z * 0.03f + 5.0f)) { + this->timer = 40; + this->actionFunc = func_808809B0; + } + } else { + if (Math_ScaledStepToS(&this->dyna.actor.world.rot.z, 0x3A00, this->dyna.actor.world.rot.z * 0.03f + 5.0f)) { + this->timer = 100; + this->actionFunc = func_80880C0C; + } + } + + this->dyna.actor.shape.rot.z += this->dyna.actor.world.rot.z; + if (this->dyna.actor.world.rot.z >= 0x1801) { + func_8002F974(&this->dyna.actor, NA_SE_EV_WIND_TRAP - SFX_FLAG); + } + + func_808809E4(this, globalCtx, this->dyna.actor.world.rot.z); +} + +void func_80880C0C(BgHakaTrap* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + func_8002F974(&this->dyna.actor, NA_SE_EV_WIND_TRAP - SFX_FLAG); + + if (this->timer == 0) { + this->timer = 1; + this->actionFunc = func_80880AE8; + } + + this->dyna.actor.shape.rot.z += this->dyna.actor.world.rot.z; + func_808809E4(this, globalCtx, this->dyna.actor.world.rot.z); +} + +void BgHakaTrap_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTrap* this = (BgHakaTrap*)thisx; + Vec3f* actorPos = &this->dyna.actor.world.pos; + + this->actionFunc(this, globalCtx); + + if ((this->dyna.actor.params != HAKA_TRAP_PROPELLER) && (thisx->params != HAKA_TRAP_SPIKED_BOX)) { + this->colliderCylinder.dim.pos.y = actorPos->y; + + if ((thisx->params == HAKA_TRAP_GUILLOTINE_SLOW) || (thisx->params == HAKA_TRAP_GUILLOTINE_FAST)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); + } else { + if (this->actionFunc == func_808801B8) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSpikes.base); + } + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); + } + } +} + +void func_80880D68(BgHakaTrap* this) { + Vec3f vec3; + Vec3f vec2; + Vec3f vec1; + + Matrix_MultVec3f(&sTrisElementsInit[0].dim.vtx[0], &vec1); + Matrix_MultVec3f(&sTrisElementsInit[0].dim.vtx[1], &vec2); + Matrix_MultVec3f(&sTrisElementsInit[0].dim.vtx[2], &vec3); + Collider_SetTrisVertices(&this->colliderSpikes, 0, &vec1, &vec2, &vec3); + + Matrix_MultVec3f(&sTrisElementsInit[1].dim.vtx[2], &vec2); + Collider_SetTrisVertices(&this->colliderSpikes, 1, &vec1, &vec3, &vec2); +} + +void BgHakaTrap_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* sDLists[5] = { + object_haka_objects_DL_007610, object_haka_objects_DL_009860, object_haka_objects_DL_007EF0, + object_haka_objects_DL_008A20, object_haka_objects_DL_0072C0, + }; + static Color_RGBA8 D_8088103C = { 0, 0, 0, 0 }; + BgHakaTrap* this = (BgHakaTrap*)thisx; + s32 pad; + Vec3f sp2C; + + if (this->actionFunc == func_808802D8) { + func_80026230(globalCtx, &D_8088103C, this->timer + 20, 0x28); + } + + Gfx_DrawDListOpa(globalCtx, sDLists[this->dyna.actor.params]); + + if (this->actionFunc == func_808801B8) { + func_80880D68(this); + } + + if (this->actionFunc == func_808802D8) { + func_80026608(globalCtx); + } + + if ((this->actionFunc == func_808808F4) && !this->unk_169) { + sp2C.x = this->dyna.actor.world.pos.x; + sp2C.z = this->dyna.actor.world.pos.z; + sp2C.y = this->dyna.actor.world.pos.y + 110.0f; + + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &sp2C, &this->unk_16C); + func_80078914(&this->unk_16C, NA_SE_EV_BRIDGE_CLOSE - SFX_FLAG); + } +} + +void BgHakaTrap_Reset(void) { + D_80880F30 = 0; + D_80881014 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Trap/z_bg_haka_trap.h b/soh/src/overlays/actors/ovl_Bg_Haka_Trap/z_bg_haka_trap.h new file mode 100644 index 000000000..f3024a086 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Trap/z_bg_haka_trap.h @@ -0,0 +1,32 @@ +#ifndef Z_BG_HAKA_TRAP_H +#define Z_BG_HAKA_TRAP_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ HAKA_TRAP_GUILLOTINE_SLOW, + /* 0x01 */ HAKA_TRAP_SPIKED_BOX, + /* 0x02 */ HAKA_TRAP_SPIKED_WALL, + /* 0x03 */ HAKA_TRAP_SPIKED_WALL_2, + /* 0x04 */ HAKA_TRAP_PROPELLER, + /* 0x05 */ HAKA_TRAP_GUILLOTINE_FAST +} HakaTrapType; + +struct BgHakaTrap; + +typedef void (*BgHakaTrapActionFunc)(struct BgHakaTrap*, GlobalContext*); + +typedef struct BgHakaTrap { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaTrapActionFunc actionFunc; + /* 0x0168 */ u8 timer; + /* 0x0169 */ u8 unk_169; + /* 0x016A */ s16 unk_16A; // used as boolean for HAKA_TRAP_GUILLOTINE_SLOW/FAST, s16 for HAKA_TRAP_SPIKED_BOX + /* 0x016C */ Vec3f unk_16C; + /* 0x0178 */ ColliderCylinder colliderCylinder; + /* 0x01C4 */ ColliderTris colliderSpikes; + /* 0x01E4 */ ColliderTrisElement colliderSpikesItem[2]; +} BgHakaTrap; // size = 0x029C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.c b/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.c new file mode 100644 index 000000000..fb119d12e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.c @@ -0,0 +1,250 @@ +/* + * File: z_bg_haka_tubo.c + * Overlay: ovl_Bg_Haka_Tubo + * Description: Shadow Temple Giant Skull Jar + */ + +#include "z_bg_haka_tubo.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgHakaTubo_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTubo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTubo_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaTubo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHakaTubo_Idle(BgHakaTubo* this, GlobalContext* globalCtx); +void BgHakaTubo_DropCollectible(BgHakaTubo* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_Tubo_InitVars = { + ACTOR_BG_HAKA_TUBO, + ACTORCAT_BG, + FLAGS, + OBJECT_HAKA_OBJECTS, + sizeof(BgHakaTubo), + (ActorFunc)BgHakaTubo_Init, + (ActorFunc)BgHakaTubo_Destroy, + (ActorFunc)BgHakaTubo_Update, + (ActorFunc)BgHakaTubo_Draw, + NULL, +}; + +static ColliderCylinderInit sPotColliderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 25, 60, 30, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sFlamesColliderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 60, 45, 235, { 0, 0, 0 } }, +}; + +static s32 sPotsDestroyed = 0; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaTubo_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTubo* this = (BgHakaTubo*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK3); + CollisionHeader_GetVirtual(&object_haka_objects_Col_0108B8, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Collider_InitCylinder(globalCtx, &this->potCollider); + Collider_SetCylinder(globalCtx, &this->potCollider, &this->dyna.actor, &sPotColliderInit); + Collider_InitCylinder(globalCtx, &this->flamesCollider); + Collider_SetCylinder(globalCtx, &this->flamesCollider, &this->dyna.actor, &sFlamesColliderInit); + this->fireScroll = Rand_ZeroOne() * 15.0f; + sPotsDestroyed = 0; + this->actionFunc = BgHakaTubo_Idle; +} + +void BgHakaTubo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTubo* this = (BgHakaTubo*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->potCollider); + Collider_DestroyCylinder(globalCtx, &this->flamesCollider); +} + +void BgHakaTubo_Idle(BgHakaTubo* this, GlobalContext* globalCtx) { + static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + + if (this->dyna.actor.room == 12) { // 3 spinning pots room in Shadow Temple + this->dyna.actor.shape.rot.y += 0x180; + this->dyna.actor.world.pos.x = Math_SinS(this->dyna.actor.shape.rot.y - 0x4000) * 145.0f + -5559.0f; + this->dyna.actor.world.pos.z = Math_CosS(this->dyna.actor.shape.rot.y - 0x4000) * 145.0f + -1587.0f; + } + // Colliding with flame circle + if (this->flamesCollider.base.atFlags & AT_HIT) { + this->flamesCollider.base.atFlags &= ~AT_HIT; + func_8002F71C(globalCtx, &this->dyna.actor, 5.0f, this->dyna.actor.yawTowardsPlayer, 5.0f); + } + // Colliding with hitbox inside the pot + if (this->potCollider.base.acFlags & AC_HIT) { + this->potCollider.base.acFlags &= ~AC_HIT; + // If the colliding actor is within a 50 unit radius and 50 unit height cylinder centered + // on the actor's position, break the pot + if (Actor_WorldDistXZToPoint(&this->dyna.actor, &this->potCollider.base.ac->world.pos) < 50.0f && + (this->potCollider.base.ac->world.pos.y - this->dyna.actor.world.pos.y) < 50.0f) { + pos.x = this->dyna.actor.world.pos.x; + pos.z = this->dyna.actor.world.pos.z; + pos.y = this->dyna.actor.world.pos.y + 80.0f; + EffectSsBomb2_SpawnLayered(globalCtx, &pos, &sZeroVector, &sZeroVector, 100, 45); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 50, NA_SE_EV_BOX_BREAK); + EffectSsHahen_SpawnBurst(globalCtx, &pos, 20.0f, 0, 350, 100, 50, OBJECT_HAKA_OBJECTS, 40, + gEffFragments2DL); + this->dropTimer = 5; + this->dyna.actor.draw = NULL; + Actor_SetScale(&this->dyna.actor, 0.0f); + this->actionFunc = BgHakaTubo_DropCollectible; + } + } else { + Collider_UpdateCylinder(&this->dyna.actor, &this->flamesCollider); + Collider_UpdateCylinder(&this->dyna.actor, &this->potCollider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->potCollider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->flamesCollider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->flamesCollider.base); + } +} + +void BgHakaTubo_DropCollectible(BgHakaTubo* this, GlobalContext* globalCtx) { + EnItem00* collectible; + f32 rnd; + Vec3f spawnPos; + s32 i; + s32 collectibleParams; + + this->dropTimer--; + if (this->dropTimer == 0) { // Creates a delay between destroying the pot and dropping the collectible + spawnPos.x = this->dyna.actor.world.pos.x; + spawnPos.y = this->dyna.actor.world.pos.y + 200.0f; + spawnPos.z = this->dyna.actor.world.pos.z; + if (this->dyna.actor.room == 12) { // 3 spinning pots room in Shadow Temple + rnd = Rand_ZeroOne(); + sPotsDestroyed++; + if (sPotsDestroyed == 3) { + // All 3 pots destroyed + collectibleParams = -1; + func_80078884(NA_SE_SY_CORRECT_CHIME); + // Drop rupees + for (i = 0; i < 9; i++) { + collectible = Item_DropCollectible(globalCtx, &spawnPos, i % 3); + if (collectible != NULL) { + collectible->actor.velocity.y = 15.0f; + collectible->actor.world.rot.y = this->dyna.actor.shape.rot.y + (i * 0x1C71); + } + } + } else if (rnd < 0.2f) { + // Unlucky, no reward and spawn keese + collectibleParams = -1; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_FIREFLY, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y + 80.0f, this->dyna.actor.world.pos.z, 0, + this->dyna.actor.shape.rot.y, 0, 2); + func_80078884(NA_SE_SY_ERROR); + } else { + // Random rewards + if (rnd < 0.4f) { + collectibleParams = ITEM00_BOMBS_A; + } else if (rnd < 0.6f) { + collectibleParams = ITEM00_MAGIC_LARGE; + } else if (rnd < 0.8f) { + collectibleParams = ITEM00_MAGIC_SMALL; + } else { + collectibleParams = ITEM00_ARROWS_SMALL; + } + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } else if (Flags_GetCollectible(globalCtx, this->dyna.actor.params) != 0) { + // If small key already collected, drop recovery heart instead + collectibleParams = ITEM00_HEART; + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } else { + // Drops a small key and sets a collect flag + collectibleParams = ((this->dyna.actor.params & 0x3F) << 8) | ITEM00_SMALL_KEY; + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + if (collectibleParams != -1) { + collectible = Item_DropCollectible(globalCtx, &spawnPos, collectibleParams); + if (collectible != NULL) { + collectible->actor.velocity.y = 15.0f; + collectible->actor.world.rot.y = this->dyna.actor.shape.rot.y; + } + } + Actor_Kill(&this->dyna.actor); + } +} + +void BgHakaTubo_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTubo* this = (BgHakaTubo*)thisx; + + this->actionFunc(this, globalCtx); + this->fireScroll++; +} + +void BgHakaTubo_DrawFlameCircle(BgHakaTubo* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_tubo.c", 476); + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y + 235.0f, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateY(this->dyna.actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.07f, 0.04f, 0.07f, MTXMODE_APPLY); + if (1) {} + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 0, 170, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 255, 255); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->fireScroll & 127, 0, 32, 64, 1, 0, + (this->fireScroll * -15) & 0xFF, 32, 64)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_tubo.c", 497), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFireCircleDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_tubo.c", 501); +} + +void BgHakaTubo_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHakaTubo* this = (BgHakaTubo*)thisx; + + Gfx_DrawDListOpa(globalCtx, object_haka_objects_DL_00FE40); + BgHakaTubo_DrawFlameCircle(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.h b/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.h new file mode 100644 index 000000000..7d13044e3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Tubo/z_bg_haka_tubo.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HAKA_TUBO_H +#define Z_BG_HAKA_TUBO_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaTubo; + +typedef void (*BgHakaTuboActionFunc)(struct BgHakaTubo*, GlobalContext*); + +typedef struct BgHakaTubo { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaTuboActionFunc actionFunc; + /* 0x0168 */ s16 dropTimer; + /* 0x016A */ s16 fireScroll; + /* 0x016C */ ColliderCylinder potCollider; + /* 0x01B8 */ ColliderCylinder flamesCollider; +} BgHakaTubo; // size = 0x0204 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Water/z_bg_haka_water.c b/soh/src/overlays/actors/ovl_Bg_Haka_Water/z_bg_haka_water.c new file mode 100644 index 000000000..b2abe37b4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Water/z_bg_haka_water.c @@ -0,0 +1,148 @@ +/* + * File: z_bg_haka_water.c + * Overlay: ovl_Bg_Haka_Water + * Description: Bottom of the Well water level changer + */ + +#include "z_bg_haka_water.h" +#include "objects/object_hakach_objects/object_hakach_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgHakaWater_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaWater_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaWater_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaWater_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHakaWater_LowerWater(BgHakaWater* this, GlobalContext* globalCtx); +void BgHakaWater_Wait(BgHakaWater* this, GlobalContext* globalCtx); +void BgHakaWater_ChangeWaterLevel(BgHakaWater* this, GlobalContext* globalCtx); + +const ActorInit Bg_Haka_Water_InitVars = { + ACTOR_BG_HAKA_WATER, + ACTORCAT_PROP, + FLAGS, + OBJECT_HAKACH_OBJECTS, + sizeof(BgHakaWater), + (ActorFunc)BgHakaWater_Init, + (ActorFunc)BgHakaWater_Destroy, + (ActorFunc)BgHakaWater_Update, + (ActorFunc)BgHakaWater_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaWater_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHakaWater* this = (BgHakaWater*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + this->isLowered = true; + this->actor.home.pos.y -= 200.0f; + this->actor.world.pos.y = this->actor.home.pos.y; + } else { + this->isLowered = false; + } + BgHakaWater_LowerWater(this, globalCtx); + this->actionFunc = BgHakaWater_Wait; +} + +void BgHakaWater_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgHakaWater_LowerWater(BgHakaWater* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 9; i++) { + globalCtx->colCtx.colHeader->waterBoxes[i].ySurface = (s16)this->actor.world.pos.y - 8; + } +} + +void BgHakaWater_Wait(BgHakaWater* this, GlobalContext* globalCtx) { + if ((!this->isLowered && Flags_GetSwitch(globalCtx, this->actor.params)) || + (this->isLowered && !Flags_GetSwitch(globalCtx, this->actor.params))) { + if (this->isLowered) { + this->isLowered = false; + this->actor.draw = BgHakaWater_Draw; + this->actor.home.pos.y += 200.0f; + } else { + this->isLowered = true; + this->actor.home.pos.y -= 200.0f; + } + this->actionFunc = BgHakaWater_ChangeWaterLevel; + } +} + +void BgHakaWater_ChangeWaterLevel(BgHakaWater* this, GlobalContext* globalCtx) { + if (!this->isLowered && Flags_GetSwitch(globalCtx, this->actor.params)) { + this->isLowered = true; + this->actor.home.pos.y -= 200.0f; + } else if (this->isLowered && !Flags_GetSwitch(globalCtx, this->actor.params)) { + this->isLowered = false; + this->actor.home.pos.y += 200.0f; + } + + if (this->actor.home.pos.y < this->actor.world.pos.y) { + func_8002F948(&this->actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } else { + func_8002F948(&this->actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } + + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f) != 0) { + this->actionFunc = BgHakaWater_Wait; + if (this->isLowered) { + this->actor.draw = NULL; + } + } + BgHakaWater_LowerWater(this, globalCtx); +} + +void BgHakaWater_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaWater* this = (BgHakaWater*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHakaWater_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHakaWater* this = (BgHakaWater*)thisx; + s32 pad; + f32 temp; + s32 pad2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_water.c", 287); + func_80093D84(globalCtx->state.gfxCtx); + + if (this->isLowered) { + temp = this->actor.world.pos.y - this->actor.home.pos.y; + } else { + temp = this->actor.world.pos.y - (this->actor.home.pos.y - 200.0f); + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)(0.765f * temp)); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames % 128, + globalCtx->gameplayFrames % 128, 32, 32, 1, 0, (0 - globalCtx->gameplayFrames) % 128, + 32, 32)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_water.c", 312), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBotwWaterRingDL); + + Matrix_Translate(0.0f, 92.0f, -1680.0f, MTXMODE_NEW); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + temp -= 170.0f; + if (temp < 0.0f) { + temp = 0.0f; + } + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)(5.1f * temp)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_haka_water.c", 328), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBotwWaterFallDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_haka_water.c", 332); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Water/z_bg_haka_water.h b/soh/src/overlays/actors/ovl_Bg_Haka_Water/z_bg_haka_water.h new file mode 100644 index 000000000..037421515 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Water/z_bg_haka_water.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_HAKA_WATER_H +#define Z_BG_HAKA_WATER_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaWater; + +typedef void (*BgHakaWaterActionFunc)(struct BgHakaWater*, GlobalContext*); + +typedef struct BgHakaWater { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgHakaWaterActionFunc actionFunc; + /* 0x0150 */ u8 isLowered; +} BgHakaWater; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Zou/z_bg_haka_zou.c b/soh/src/overlays/actors/ovl_Bg_Haka_Zou/z_bg_haka_zou.c new file mode 100644 index 000000000..d9cde1803 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Zou/z_bg_haka_zou.c @@ -0,0 +1,412 @@ +/* + * File: z_bg_haka_zou.c + * Overlay: ovl_Bg_Haka_Zou + * Description: Statue and Wall (Shadow Temple) + */ + +#include "z_bg_haka_zou.h" +#include "objects/object_hakach_objects/object_hakach_objects.h" +#include "objects/object_haka_objects/object_haka_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0x0 */ STA_GIANT_BIRD_STATUE, + /* 0x1 */ STA_BOMBABLE_SKULL_WALL, + /* 0x2 */ STA_BOMBABLE_RUBBLE, + /* 0x3 */ STA_UNKNOWN +} ShadowTempleAssetsType; + +void BgHakaZou_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHakaZou_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHakaZou_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHakaZou_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHakaZou_Wait(BgHakaZou* this, GlobalContext* globalCtx); +void func_80882BDC(BgHakaZou* this, GlobalContext* globalCtx); +void func_80883000(BgHakaZou* this, GlobalContext* globalCtx); +void func_80883104(BgHakaZou* this, GlobalContext* globalCtx); +void func_80883144(BgHakaZou* this, GlobalContext* globalCtx); +void func_80883254(BgHakaZou* this, GlobalContext* globalCtx); +void func_80883328(BgHakaZou* this, GlobalContext* globalCtx); +void func_808834D8(BgHakaZou* this, GlobalContext* globalCtx); +void BgHakaZou_DoNothing(BgHakaZou* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 5, 60, 0, { 0, 0, 0 } }, +}; + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +const ActorInit Bg_Haka_Zou_InitVars = { + ACTOR_BG_HAKA_ZOU, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgHakaZou), + (ActorFunc)BgHakaZou_Init, + (ActorFunc)BgHakaZou_Destroy, + (ActorFunc)BgHakaZou_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHakaZou_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHakaZou* this = (BgHakaZou*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + + this->switchFlag = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + + if (thisx->params == STA_UNKNOWN) { + Actor_SetScale(thisx, (Rand_ZeroOne() * 0.005f) + 0.025f); + + thisx->speedXZ = Rand_ZeroOne(); + thisx->world.rot.y = thisx->shape.rot.y * ((Rand_ZeroOne() < 0.5f) ? -1 : 1) + Rand_CenteredFloat(0x1000); + this->timer = 20; + thisx->world.rot.x = Rand_S16Offset(0x100, 0x300) * ((Rand_ZeroOne() < 0.5f) ? -1 : 1); + thisx->world.rot.z = Rand_S16Offset(0x400, 0x800) * ((Rand_ZeroOne() < 0.5f) ? -1 : 1); + } else { + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + Collider_UpdateCylinder(thisx, &this->collider); + + DynaPolyActor_Init(&this->dyna, 0); + + if (thisx->params == STA_GIANT_BIRD_STATUE) { + thisx->uncullZoneForward = 2000.0f; + thisx->uncullZoneScale = 3000.0f; + thisx->uncullZoneDownward = 3000.0f; + } + } + + this->requiredObjBankIndex = (thisx->params == STA_BOMBABLE_RUBBLE) + ? Object_GetIndex(&globalCtx->objectCtx, OBJECT_HAKACH_OBJECTS) + : Object_GetIndex(&globalCtx->objectCtx, OBJECT_HAKA_OBJECTS); + + if (this->requiredObjBankIndex < 0) { + Actor_Kill(thisx); + } else if ((thisx->params != STA_UNKNOWN) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + if (thisx->params != STA_GIANT_BIRD_STATUE) { + Actor_Kill(thisx); + } else { + thisx->shape.rot.x = -0x4000; + thisx->world.pos.z -= 80.0f; + thisx->world.pos.y -= 54.0f; + } + } + + this->actionFunc = BgHakaZou_Wait; +} + +void BgHakaZou_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHakaZou* this = (BgHakaZou*)thisx; + + if (this->dyna.actor.params != STA_UNKNOWN) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void func_808828F4(BgHakaZou* this, GlobalContext* globalCtx) { + Vec3f effectPos; + Vec3f effectVelocity; + f32 rand; + s32 i; + + effectVelocity.x = 0.0f; + effectVelocity.y = 1.0f; + effectVelocity.z = 2.0f; + + for (i = 0; i < 2; i++) { + if (i == 0) { + effectPos.x = this->dyna.actor.world.pos.x - (Rand_CenteredFloat(10.0f) + 112.0f); + } else { + effectPos.x = Rand_CenteredFloat(10.0f) + this->dyna.actor.world.pos.x; + } + + rand = Rand_ZeroOne(); + effectPos.y = this->dyna.actor.world.pos.y + (60.0f * rand); + effectPos.z = this->dyna.actor.world.pos.z + (112.0f * rand); + + func_800286CC(globalCtx, &effectPos, &effectVelocity, &sZeroVec, (Rand_ZeroOne() * 200.0f) + 1000.0f, 100); + } +} + +void BgHakaZou_Wait(BgHakaZou* this, GlobalContext* globalCtx) { + CollisionHeader* colHeader; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->requiredObjBankIndex)) { + this->dyna.actor.objBankIndex = this->requiredObjBankIndex; + this->dyna.actor.draw = BgHakaZou_Draw; + + if (this->dyna.actor.params == STA_UNKNOWN) { + this->actionFunc = func_80882BDC; + } else { + Actor_SetObjectDependency(globalCtx, &this->dyna.actor); + + colHeader = NULL; + + if (this->dyna.actor.params == STA_GIANT_BIRD_STATUE) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_006F70, &colHeader); + this->collider.dim.radius = 80; + this->collider.dim.height = 100; + this->collider.dim.yShift = -30; + this->collider.dim.pos.x -= 56; + this->collider.dim.pos.z += 56; + this->dyna.actor.uncullZoneScale = 1500.0f; + } else if (this->dyna.actor.params == STA_BOMBABLE_SKULL_WALL) { + CollisionHeader_GetVirtual(&object_haka_objects_Col_005E30, &colHeader); + this->collider.dim.yShift = -50; + } else { + CollisionHeader_GetVirtual(&gBotwBombSpotCol, &colHeader); + this->collider.dim.radius = 55; + this->collider.dim.height = 20; + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if ((this->dyna.actor.params == STA_GIANT_BIRD_STATUE) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actionFunc = BgHakaZou_DoNothing; + } else { + this->actionFunc = func_80883000; + } + } + } +} +void func_80882BDC(BgHakaZou* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + this->dyna.actor.shape.rot.x += this->dyna.actor.world.rot.x; + this->dyna.actor.shape.rot.z += this->dyna.actor.world.rot.z; + + if (this->dyna.actor.bgCheckFlags & 2) { + if (this->dyna.actor.velocity.y < -8.0f) { + this->dyna.actor.velocity.y *= -0.6f; + this->dyna.actor.velocity.y = CLAMP_MAX(this->dyna.actor.velocity.y, 10.0f); + this->dyna.actor.bgCheckFlags &= ~3; + this->dyna.actor.speedXZ = 2.0f; + } else { + Actor_Kill(&this->dyna.actor); + } + } + + if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + } +} + +void func_80882CC4(BgHakaZou* this, GlobalContext* globalCtx) { + s32 i; + s32 j; + Vec3f actorSpawnPos; + f32 sin; + f32 cos; + s32 pad; + + sin = Math_SinS(this->dyna.actor.shape.rot.y - 0x4000) * 40.0f; + cos = Math_CosS(this->dyna.actor.shape.rot.y - 0x4000) * 40.0f; + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + actorSpawnPos.x = this->dyna.actor.world.pos.x + (j - 1) * sin; + actorSpawnPos.z = this->dyna.actor.world.pos.z + (j - 1) * cos; + actorSpawnPos.y = this->dyna.actor.world.pos.y + (i - 1) * 55; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_HAKA_ZOU, actorSpawnPos.x, actorSpawnPos.y, + actorSpawnPos.z, 0, this->dyna.actor.shape.rot.y, 0, this->dyna.actor.params + 2); + func_800286CC(globalCtx, &actorSpawnPos, &sZeroVec, &sZeroVec, 1000, 50); + } + } +} + +void func_80882E54(BgHakaZou* this, GlobalContext* globalCtx) { + Vec3f fragmentPos; + s32 i; + s32 j; + s32 num = 25; + + fragmentPos.x = this->collider.dim.pos.x; + fragmentPos.y = this->collider.dim.pos.y; + fragmentPos.z = this->collider.dim.pos.z; + + EffectSsHahen_SpawnBurst(globalCtx, &fragmentPos, 10.0f, 0, 10, 10, 4, 141, 40, gBotwBombSpotDL); + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + fragmentPos.x = this->collider.dim.pos.x + (((j * 2) - 1) * num); + fragmentPos.z = this->collider.dim.pos.z + (((i * 2) - 1) * num); + EffectSsHahen_SpawnBurst(globalCtx, &fragmentPos, 10.0f, 0, 10, 10, 4, 141, 40, gBotwBombSpotDL); + func_800286CC(globalCtx, &fragmentPos, &sZeroVec, &sZeroVec, 1000, 50); + } + } +} + +void func_80883000(BgHakaZou* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + Flags_SetSwitch(globalCtx, this->switchFlag); + + if (this->dyna.actor.params == STA_GIANT_BIRD_STATUE) { + this->timer = 20; + this->actionFunc = func_80883144; + OnePointCutscene_Init(globalCtx, 3400, 999, &this->dyna.actor, MAIN_CAM); + } else if (this->dyna.actor.params == 2) { + func_80882E54(this, globalCtx); + this->dyna.actor.draw = NULL; + this->timer = 1; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_EXPLOSION); + this->actionFunc = func_80883104; + } else { + func_80882CC4(this, globalCtx); + this->timer = 1; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WALL_BROKEN); + this->actionFunc = func_80883104; + } + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_80883104(BgHakaZou* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + } +} + +void func_80883144(BgHakaZou* this, GlobalContext* globalCtx) { + Vec3f explosionPos; + + if (this->timer != 0) { + this->timer--; + } + + if (!(this->timer % 4)) { + explosionPos.x = Rand_CenteredFloat(200.0f) + (this->dyna.actor.world.pos.x - 56.0f); + explosionPos.y = (Rand_ZeroOne() * 80.0f) + this->dyna.actor.world.pos.y; + explosionPos.z = Rand_CenteredFloat(200.0f) + (this->dyna.actor.world.pos.z + 56.0f); + + EffectSsBomb2_SpawnLayered(globalCtx, &explosionPos, &sZeroVec, &sZeroVec, 150, 70); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_IT_BOMB_EXPLOSION); + } + + if (this->timer == 0) { + this->timer = 20; + this->actionFunc = func_80883254; + } +} + +void func_80883254(BgHakaZou* this, GlobalContext* globalCtx) { + f32 moveDist = (Rand_ZeroOne() * 0.5f) + 0.5f; + + Math_StepToF(&this->dyna.actor.world.pos.z, this->dyna.actor.home.pos.z - 80.0f, 2.0f * moveDist); + + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 40.0f, moveDist)) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + this->timer = 60; + this->dyna.actor.world.rot.x = 8; + this->actionFunc = func_80883328; + } + } else { + func_808828F4(this, globalCtx); + } +} + +void func_80883328(BgHakaZou* this, GlobalContext* globalCtx) { + Vec3f effectPos; + s32 i; + s32 j; + + this->dyna.actor.world.rot.x += this->dyna.actor.world.rot.x / 8.0f; + + if (Math_ScaledStepToS(&this->dyna.actor.shape.rot.x, -0x4000, this->dyna.actor.world.rot.x)) { + effectPos.x = this->dyna.actor.world.pos.x; + effectPos.y = this->dyna.actor.world.pos.y; + + for (j = 0; j < 2; j++) { + effectPos.z = this->dyna.actor.world.pos.z; + + for (i = 0; i < 4; i++) { + effectPos.z -= (i == 2) ? 550.0f : 50.0f; + func_800286CC(globalCtx, &effectPos, &sZeroVec, &sZeroVec, (Rand_ZeroOne() * 200.0f) + 1000.0f, 200); + } + + effectPos.x -= 112.0f; + } + + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + this->timer = 25; + this->actionFunc = func_808834D8; + } +} + +void func_808834D8(BgHakaZou* this, GlobalContext* globalCtx) { + f32 moveDist; + + if (this->timer != 0) { + this->timer--; + } + + moveDist = (this->timer % 2) ? 15.0f : -15.0f; + this->dyna.actor.world.pos.y += ((this->timer & 0xFE) * 0.04f * moveDist); + + if (this->timer == 0) { + this->actionFunc = BgHakaZou_DoNothing; + } +} + +void BgHakaZou_DoNothing(BgHakaZou* this, GlobalContext* globalCtx) { +} + +void BgHakaZou_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHakaZou* this = (BgHakaZou*)thisx; + + this->actionFunc(this, globalCtx); + + if (this->dyna.actor.params == 3) { + Actor_MoveForward(&this->dyna.actor); + } +} + +void BgHakaZou_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { + object_haka_objects_DL_0064E0, + object_haka_objects_DL_005CE0, + gBotwBombSpotDL, + object_haka_objects_DL_005CE0, + }; + + Gfx_DrawDListOpa(globalCtx, dLists[thisx->params]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Haka_Zou/z_bg_haka_zou.h b/soh/src/overlays/actors/ovl_Bg_Haka_Zou/z_bg_haka_zou.h new file mode 100644 index 000000000..eafdcc888 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Haka_Zou/z_bg_haka_zou.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HAKA_ZOU_H +#define Z_BG_HAKA_ZOU_H + +#include "ultra64.h" +#include "global.h" + +struct BgHakaZou; + +typedef void (*BgHakaZouActionFunc)(struct BgHakaZou*, GlobalContext*); + +typedef struct BgHakaZou { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHakaZouActionFunc actionFunc; + /* 0x0168 */ s8 requiredObjBankIndex; + /* 0x0169 */ u8 switchFlag; + /* 0x016A */ s16 timer; + /* 0x016C */ ColliderCylinder collider; +} BgHakaZou; // size = 0x01B8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.c b/soh/src/overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.c new file mode 100644 index 000000000..64b162ccb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.c @@ -0,0 +1,525 @@ +/* + * File: z_bg_heavy_block.c + * Overlay: ovl_Bg_Heavy_Block + * Description: Large block that can only be lifted with Golden Gauntlets + */ + +#include "z_bg_heavy_block.h" +#include "objects/object_heavy_object/object_heavy_object.h" +#include "vt.h" + +#define FLAGS 0 + +#define PIECE_FLAG_HIT_FLOOR (1 << 0) + +void BgHeavyBlock_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHeavyBlock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHeavyBlock_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHeavyBlock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHeavyBlock_DrawPiece(Actor* thisx, GlobalContext* globalCtx); + +void BgHeavyBlock_MovePiece(BgHeavyBlock* this, GlobalContext* globalCtx); +void BgHeavyBlock_Wait(BgHeavyBlock* this, GlobalContext* globalCtx); +void BgHeavyBlock_LiftedUp(BgHeavyBlock* this, GlobalContext* globalCtx); +void BgHeavyBlock_Fly(BgHeavyBlock* this, GlobalContext* globalCtx); +void BgHeavyBlock_Land(BgHeavyBlock* this, GlobalContext* globalCtx); +void BgHeavyBlock_DoNothing(BgHeavyBlock* this, GlobalContext* globalCtx); + +const ActorInit Bg_Heavy_Block_InitVars = { + ACTOR_BG_HEAVY_BLOCK, + ACTORCAT_BG, + FLAGS, + OBJECT_HEAVY_OBJECT, + sizeof(BgHeavyBlock), + (ActorFunc)BgHeavyBlock_Init, + (ActorFunc)BgHeavyBlock_Destroy, + (ActorFunc)BgHeavyBlock_Update, + (ActorFunc)BgHeavyBlock_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), +}; + +void BgHeavyBlock_SetPieceRandRot(BgHeavyBlock* this, f32 scale) { + this->dyna.actor.world.rot.x = Rand_CenteredFloat(1024.0f) * scale; + this->dyna.actor.world.rot.y = Rand_CenteredFloat(1024.0f) * scale; + this->dyna.actor.world.rot.z = Rand_CenteredFloat(1024.0f) * scale; +} + +void BgHeavyBlock_InitPiece(BgHeavyBlock* this, f32 scale) { + f32 rand; + f32 yawSinCos; + f32 randChoice; + + this->dyna.actor.gravity = -0.6f; + this->dyna.actor.minVelocityY = -12.0f; + randChoice = Rand_CenteredFloat(12.0f * scale); + rand = (randChoice < 0.0f) ? randChoice - 2.0f : randChoice + 2.0f; + this->dyna.actor.velocity.y = (Rand_ZeroFloat(8.0f) + 4.0f) * scale; + this->dyna.actor.velocity.z = Rand_ZeroFloat(-8.0f * scale); + yawSinCos = Math_CosS(this->dyna.actor.world.rot.y); + this->dyna.actor.velocity.x = + (Math_SinS(this->dyna.actor.world.rot.y) * this->dyna.actor.velocity.z + (yawSinCos * rand)); + yawSinCos = Math_SinS(this->dyna.actor.world.rot.y); + this->dyna.actor.velocity.z = + (Math_CosS(this->dyna.actor.world.rot.y) * this->dyna.actor.velocity.z) + (-yawSinCos * rand); + BgHeavyBlock_SetPieceRandRot(this, scale); + Actor_SetScale(&this->dyna.actor, Rand_CenteredFloat(0.2f) + 1.0f); +} + +void BgHeavyBlock_SetupDynapoly(BgHeavyBlock* this, GlobalContext* globalCtx) { + s32 pad[2]; + CollisionHeader* colHeader = NULL; + this->dyna.actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_17; + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gHeavyBlockCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgHeavyBlock_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHeavyBlock* this = (BgHeavyBlock*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + this->pieceFlags = 0; + + if (globalCtx->sceneNum == SCENE_GANON_TOU) { + thisx->params &= 0xFF00; + thisx->params |= 4; + } + + switch (thisx->params & 0xFF) { + case HEAVYBLOCK_BIG_PIECE: + thisx->draw = BgHeavyBlock_DrawPiece; + this->actionFunc = BgHeavyBlock_MovePiece; + BgHeavyBlock_InitPiece(this, 1.0f); + this->timer = 120; + thisx->flags |= ACTOR_FLAG_4; + this->unk_164.y = -50.0f; + break; + case HEAVYBLOCK_SMALL_PIECE: + thisx->draw = BgHeavyBlock_DrawPiece; + this->actionFunc = BgHeavyBlock_MovePiece; + BgHeavyBlock_InitPiece(this, 2.0f); + this->timer = 120; + thisx->flags |= ACTOR_FLAG_4; + this->unk_164.y = -20.0f; + break; + case HEAVYBLOCK_BREAKABLE: + BgHeavyBlock_SetupDynapoly(this, globalCtx); + + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0x3F)) { + Actor_Kill(thisx); + return; + } + + this->actionFunc = BgHeavyBlock_Wait; + break; + case HEAVYBLOCK_UNBREAKABLE_OUTSIDE_CASTLE: + BgHeavyBlock_SetupDynapoly(this, globalCtx); + + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0x3F)) { + this->actionFunc = BgHeavyBlock_DoNothing; + thisx->shape.rot.x = thisx->world.rot.x = 0x8AD0; + thisx->shape.rot.y = thisx->world.rot.y = 0xC000; + thisx->shape.rot.z = thisx->world.rot.z = 0x0; + thisx->world.pos.x = 1704.0f; + thisx->world.pos.y = 1504.0f; + thisx->world.pos.z = 516.0f; + } + + this->actionFunc = BgHeavyBlock_Wait; + break; + case HEAVYBLOCK_UNBREAKABLE: + BgHeavyBlock_SetupDynapoly(this, globalCtx); + this->actionFunc = BgHeavyBlock_Wait; + break; + default: + BgHeavyBlock_SetupDynapoly(this, globalCtx); + this->actionFunc = BgHeavyBlock_Wait; + break; + } + // "Largest Block Save Bit %x" + osSyncPrintf(VT_FGCOL(CYAN) " 最大 ブロック セーブビット %x\n" VT_RST, thisx->params); +} + +void BgHeavyBlock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHeavyBlock* this = (BgHeavyBlock*)thisx; + switch (this->dyna.actor.params & 0xFF) { + case HEAVYBLOCK_BIG_PIECE: + break; + case HEAVYBLOCK_SMALL_PIECE: + break; + default: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void BgHeavyBlock_MovePiece(BgHeavyBlock* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + thisx->velocity.y += thisx->gravity; + + if (thisx->velocity.y < thisx->minVelocityY) { + thisx->velocity.y = thisx->minVelocityY; + } + + thisx->velocity.x *= 0.98f; + thisx->velocity.z *= 0.98f; + func_8002D7EC(thisx); + thisx->shape.rot.x += thisx->world.rot.x; + thisx->shape.rot.y += thisx->world.rot.y; + thisx->shape.rot.z += thisx->world.rot.z; + + if (!(this->pieceFlags & PIECE_FLAG_HIT_FLOOR)) { + thisx->world.pos.y += this->unk_164.y; + thisx->prevPos.y += this->unk_164.y; + Actor_UpdateBgCheckInfo(globalCtx, thisx, 50.0f, 50.0f, 0.0f, 5); + thisx->world.pos.y -= this->unk_164.y; + thisx->prevPos.y -= this->unk_164.y; + if (thisx->bgCheckFlags & 1) { + this->pieceFlags |= PIECE_FLAG_HIT_FLOOR; + thisx->velocity.y = Rand_ZeroFloat(4.0f) + 2.0f; + thisx->velocity.x = Rand_CenteredFloat(8.0f); + thisx->velocity.z = Rand_CenteredFloat(8.0f); + BgHeavyBlock_SetPieceRandRot(this, 1.0f); + Audio_PlayActorSound2(thisx, NA_SE_EV_ROCK_BROKEN); + func_800AA000(thisx->xzDistToPlayer, 0x96, 0xA, 8); + } + } + + if (this->timer > 0) { + this->timer--; + } else { + Actor_Kill(thisx); + } +} + +void BgHeavyBlock_SpawnDust(GlobalContext* globalCtx, f32 posX, f32 posY, f32 posZ, f32 velX, f32 velY, f32 velZ, + u8 dustParams) { + Color_RGBA8 primColor; + Color_RGBA8 envColor; + Vec3f eye; + Vec3f at; + s16 sp6E; + s16 sp6C; + Vec3f accel; + Vec3f velocity; + Vec3f pos; + f32 sp44; + s16 scaleStep; + s16 scale; + + pos.x = posX; + pos.y = posY; + pos.z = posZ; + + if (dustParams & 1) { + // red dust, landed in fire + primColor.r = 150; + primColor.g = primColor.b = envColor.g = envColor.b = 0; + envColor.r = 80; + primColor.a = envColor.a = 0; + } else { + // brown dust + // clang-format off + primColor.r = 170; primColor.g = 130; primColor.b = 90; primColor.a = 255; + envColor.r = 100; envColor.g = 60; envColor.b = 20; envColor.a = 255; + // clang-format on + } + + accel.z = 0.0f; + accel.x = 0.0f; + accel.y = (dustParams & 8) ? 0.0f : 0.5f; + + eye = GET_ACTIVE_CAM(globalCtx)->eye; + at = GET_ACTIVE_CAM(globalCtx)->at; + + scale = 1000; + scaleStep = 160; + + switch (dustParams & 6) { + case 4: + case 6: + velocity.x = velX; + velocity.y = velY; + velocity.z = velZ; + scale = 300; + scaleStep = 50; + break; + case 2: + sp44 = Rand_ZeroFloat(5.0f) + 5.0f; + sp6E = Rand_CenteredFloat(65280.0f); + + velocity.x = (Math_SinS(sp6E) * sp44) + velX; + velocity.y = velY; + velocity.z = (Math_CosS(sp6E) * sp44) + velZ; + break; + case 0: + sp6E = Math_Vec3f_Yaw(&eye, &at); + sp6C = -Math_Vec3f_Pitch(&eye, &at); + + velocity.x = ((5.0f * Math_SinS(sp6E)) * Math_CosS(sp6C)) + velX; + velocity.y = (Math_SinS(sp6C) * 5.0f) + velY; + velocity.z = ((5.0f * Math_CosS(sp6E)) * Math_CosS(sp6C)) + velZ; + + pos.x -= (velocity.x * 20.0f); + pos.y -= (velocity.y * 20.0f); + pos.z -= (velocity.z * 20.0f); + break; + } + + func_8002843C(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, scale, scaleStep, + (s32)Rand_ZeroFloat(10.0f) + 20); +} + +void BgHeavyBlock_SpawnPieces(BgHeavyBlock* this, GlobalContext* globalCtx) { + s32 i; + Vec3f spA4[] = { + { 0.0f, 300.0f, -20.0f }, { 50.0f, 200.0f, -20.0f }, { -50.0f, 200.0f, -20.0f }, + { 0.0f, 100.0f, 30.0f }, { 0.0f, 100.0f, -70.0f }, { 0.0f, 0.0f, -20.0f }, + }; + s32 pad; + Vec3f pos; + f32 sinPitch; + f32 cosPitch; + f32 sinYaw; + f32 cosYaw; + + sinPitch = Math_SinS(this->dyna.actor.world.rot.x); + cosPitch = Math_CosS(this->dyna.actor.world.rot.x); + sinYaw = Math_SinS(this->dyna.actor.world.rot.y); + cosYaw = Math_CosS(this->dyna.actor.world.rot.y); + + for (i = 0; i < ARRAY_COUNT(spA4); i++) { + pos.z = (spA4[i].y * sinPitch) + (spA4[i].z * cosPitch); + + pos.x = this->dyna.actor.world.pos.x + (spA4[i].x * cosYaw) + (sinYaw * pos.z); + pos.y = this->dyna.actor.world.pos.y + (spA4[i].y * cosPitch) + (-spA4[i].z * sinPitch); + pos.z = this->dyna.actor.world.pos.z + (spA4[i].x * -sinYaw) + (cosYaw * pos.z); + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_HEAVY_BLOCK, pos.x, pos.y, pos.z, + this->dyna.actor.shape.rot.x, this->dyna.actor.shape.rot.y, 0, 2); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_HEAVY_BLOCK, pos.x, pos.y, pos.z, + this->dyna.actor.shape.rot.x, this->dyna.actor.shape.rot.y, 0, 3); + + BgHeavyBlock_SpawnDust(globalCtx, pos.x, pos.y, pos.z, 0.0f, 0.0f, 0.0f, 0); + } +} + +void BgHeavyBlock_Wait(BgHeavyBlock* this, GlobalContext* globalCtx) { + s32 quakeIndex; + + // if block has a parent link has lifted it, start one point cutscene and quake + if (Actor_HasParent(&this->dyna.actor, globalCtx)) { + this->timer = 0; + + switch (this->dyna.actor.params & 0xFF) { + case HEAVYBLOCK_BREAKABLE: + OnePointCutscene_Init(globalCtx, 4020, 270, &this->dyna.actor, MAIN_CAM); + break; + case HEAVYBLOCK_UNBREAKABLE: + OnePointCutscene_Init(globalCtx, 4021, 220, &this->dyna.actor, MAIN_CAM); + break; + case HEAVYBLOCK_UNBREAKABLE_OUTSIDE_CASTLE: + OnePointCutscene_Init(globalCtx, 4022, 210, &this->dyna.actor, MAIN_CAM); + break; + } + + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, 25000); + Quake_SetQuakeValues(quakeIndex, 1, 1, 5, 0); + Quake_SetCountdown(quakeIndex, 10); + this->actionFunc = BgHeavyBlock_LiftedUp; + } +} + +void BgHeavyBlock_LiftedUp(BgHeavyBlock* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + f32 cosYaw; + f32 zOffset; + f32 sinYaw; + f32 xOffset; + + if (this->timer == 11) { + func_800AA000(0.0f, 0xFF, 0x14, 0x14); + func_8002F7DC(&player->actor, NA_SE_PL_PULL_UP_BIGROCK); + LOG_STRING("NA_SE_PL_PULL_UP_BIGROCK", "../z_bg_heavy_block.c", 691); + } + + if (this->timer < 40) { + xOffset = Rand_CenteredFloat(110.0f); + sinYaw = Math_SinS(this->dyna.actor.shape.rot.y); + zOffset = Rand_CenteredFloat(110.0f); + cosYaw = Math_CosS(this->dyna.actor.shape.rot.y); + + BgHeavyBlock_SpawnDust(globalCtx, (sinYaw * -70.0f) + (this->dyna.actor.world.pos.x + xOffset), + this->dyna.actor.world.pos.y + 10.0f, + (cosYaw * -70.0f) + (this->dyna.actor.world.pos.z + zOffset), 0.0f, -1.0f, 0.0f, 0xC); + } + + this->timer++; + + func_8002DF54(globalCtx, &player->actor, 8); + + // if parent is NULL, link threw it + if (Actor_HasNoParent(&this->dyna.actor, globalCtx)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_HEAVY_THROW); + this->actionFunc = BgHeavyBlock_Fly; + } +} + +void BgHeavyBlock_Fly(BgHeavyBlock* this, GlobalContext* globalCtx) { + s32 bgId; + s32 quakeIndex; + Vec3f pos; + f32 raycastResult; + + Actor_MoveForward(&this->dyna.actor); + pos.x = this->dyna.actor.home.pos.x; + pos.y = this->dyna.actor.home.pos.y + 1000.0f; + pos.z = this->dyna.actor.home.pos.z; + raycastResult = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->dyna.actor.floorPoly, &bgId, &this->dyna.actor, &pos); + this->dyna.actor.floorHeight = raycastResult; + + if (this->dyna.actor.home.pos.y <= raycastResult) { + func_800AA000(0.0f, 0xFF, 0x3C, 4); + + switch (this->dyna.actor.params & 0xFF) { + case HEAVYBLOCK_BREAKABLE: + BgHeavyBlock_SpawnPieces(this, globalCtx); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + Actor_Kill(&this->dyna.actor); + + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, 28000); + Quake_SetQuakeValues(quakeIndex, 14, 2, 100, 0); + Quake_SetCountdown(quakeIndex, 30); + + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 2); + Quake_SetSpeed(quakeIndex, 12000); + Quake_SetQuakeValues(quakeIndex, 5, 0, 0, 0); + Quake_SetCountdown(quakeIndex, 999); + + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 30, + NA_SE_EV_ELECTRIC_EXPLOSION); + return; + case HEAVYBLOCK_UNBREAKABLE_OUTSIDE_CASTLE: + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, 28000); + Quake_SetQuakeValues(quakeIndex, 16, 2, 120, 0); + Quake_SetCountdown(quakeIndex, 40); + + this->actionFunc = BgHeavyBlock_Land; + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + break; + case HEAVYBLOCK_UNBREAKABLE: + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSTAND_STOP_U); + + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, 28000); + Quake_SetQuakeValues(quakeIndex, 14, 2, 100, 0); + Quake_SetCountdown(quakeIndex, 40); + + this->actionFunc = BgHeavyBlock_Land; + break; + default: + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, 28000); + Quake_SetQuakeValues(quakeIndex, 14, 2, 100, 0); + Quake_SetCountdown(quakeIndex, 40); + + this->actionFunc = BgHeavyBlock_Land; + } + } + this->dyna.actor.shape.rot.x = Math_Atan2S(this->dyna.actor.velocity.y, this->dyna.actor.speedXZ); +} + +void BgHeavyBlock_DoNothing(BgHeavyBlock* this, GlobalContext* globalCtx) { +} + +void BgHeavyBlock_Land(BgHeavyBlock* this, GlobalContext* globalCtx) { + s32 pad; + + if (Math_SmoothStepToS(&this->dyna.actor.shape.rot.x, 0x8AD0, 6, 2000, 100) != 0) { + Math_StepToF(&this->dyna.actor.speedXZ, 0.0f, 20.0f); + Math_StepToF(&this->dyna.actor.velocity.y, 0.0f, 3.0f); + this->dyna.actor.gravity = 0.0f; + this->dyna.actor.world.pos = this->dyna.actor.home.pos; + Actor_MoveForward(&this->dyna.actor); + this->dyna.actor.home.pos = this->dyna.actor.world.pos; + switch (this->dyna.actor.params & 0xFF) { + case HEAVYBLOCK_UNBREAKABLE_OUTSIDE_CASTLE: + BgHeavyBlock_SpawnDust(globalCtx, Rand_CenteredFloat(30.0f) + 1678.0f, Rand_ZeroFloat(100.0f) + 1286.0f, + Rand_CenteredFloat(30.0f) + 552.0f, 0.0f, 0.0f, 0.0f, 0); + BgHeavyBlock_SpawnDust(globalCtx, Rand_CenteredFloat(30.0f) + 1729.0f, Rand_ZeroFloat(80.0f) + 1269.0f, + Rand_CenteredFloat(30.0f) + 600.0f, 0.0f, 0.0f, 0.0f, 0); + break; + case HEAVYBLOCK_UNBREAKABLE: + BgHeavyBlock_SpawnDust(globalCtx, Rand_CenteredFloat(100.0f) + -735.0f, 29.0f, + Rand_CenteredFloat(100.0f) + -3418.0f, 0.0f, 0.0f, 0.0f, 3); + break; + } + } else { + this->dyna.actor.flags &= ~(ACTOR_FLAG_4 | ACTOR_FLAG_5); + this->actionFunc = BgHeavyBlock_DoNothing; + } +} + +void BgHeavyBlock_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHeavyBlock* this = (BgHeavyBlock*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHeavyBlock_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f D_80884EC8 = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_80884ED4 = { 0.0f, 400.0f, 0.0f }; + BgHeavyBlock* this = (BgHeavyBlock*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_heavy_block.c", 904); + + if (BgHeavyBlock_LiftedUp == this->actionFunc) { + Matrix_SetTranslateRotateYXZ(player->leftHandPos.x, player->leftHandPos.y, player->leftHandPos.z, + &thisx->shape.rot); + Matrix_Translate(-this->unk_164.x, -this->unk_164.y, -this->unk_164.z, MTXMODE_APPLY); + } else if ((thisx->gravity == 0.0f) && (BgHeavyBlock_Land == this->actionFunc)) { + Matrix_SetTranslateRotateYXZ(thisx->home.pos.x, thisx->home.pos.y, thisx->home.pos.z, &thisx->shape.rot); + Matrix_Translate(-D_80884ED4.x, -D_80884ED4.y, -D_80884ED4.z, MTXMODE_APPLY); + } + + Matrix_MultVec3f(&D_80884EC8, &thisx->world.pos); + Matrix_MultVec3f(&D_80884ED4, &thisx->home.pos); + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_heavy_block.c", 931), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gHeavyBlockEntirePillarDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_heavy_block.c", 935); +} + +void BgHeavyBlock_DrawPiece(Actor* thisx, GlobalContext* globalCtx) { + switch (thisx->params & 0xFF) { + case HEAVYBLOCK_BIG_PIECE: + Matrix_Translate(50.0f, -260.0f, -20.0f, MTXMODE_APPLY); + Gfx_DrawDListOpa(globalCtx, gHeavyBlockBigPieceDL); + break; + case HEAVYBLOCK_SMALL_PIECE: + Matrix_Translate(45.0f, -280.0f, -5.0f, MTXMODE_APPLY); + Gfx_DrawDListOpa(globalCtx, gHeavyBlockSmallPieceDL); + break; + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.h b/soh/src/overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.h new file mode 100644 index 000000000..e816bcf7a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.h @@ -0,0 +1,27 @@ +#ifndef Z_BG_HEAVY_BLOCK_H +#define Z_BG_HEAVY_BLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct BgHeavyBlock; + +typedef void (*BgHeavyBlockActionFunc)(struct BgHeavyBlock*, GlobalContext*); + +typedef struct BgHeavyBlock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ Vec3f unk_164; + /* 0x0170 */ s16 timer; + /* 0x0172 */ u16 pieceFlags; + /* 0x0174 */ BgHeavyBlockActionFunc actionFunc; +} BgHeavyBlock; // size = 0x0178 + +typedef enum { + /* 0x00 */ HEAVYBLOCK_UNBREAKABLE, + /* 0x01 */ HEAVYBLOCK_BREAKABLE, + /* 0x02 */ HEAVYBLOCK_BIG_PIECE, + /* 0x03 */ HEAVYBLOCK_SMALL_PIECE, + /* 0x04 */ HEAVYBLOCK_UNBREAKABLE_OUTSIDE_CASTLE +} HeavyBlockType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.c new file mode 100644 index 000000000..77c97e08c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.c @@ -0,0 +1,262 @@ +/* + * File: z_bg_hidan_curtain.c + * Overlay: ovl_Bg_Hidan_Curtain + * Description: Flame circle + */ + +#include "z_bg_hidan_curtain.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgHidanCurtain_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanCurtain_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanCurtain_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanCurtain_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHidanCurtain_WaitForSwitchOn(BgHidanCurtain* this, GlobalContext* globalCtx); +void BgHidanCurtain_WaitForCutscene(BgHidanCurtain* this, GlobalContext* globalCtx); +void BgHidanCurtain_WaitForClear(BgHidanCurtain* this, GlobalContext* globalCtx); +void BgHidanCurtain_TurnOn(BgHidanCurtain* this, GlobalContext* globalCtx); +void BgHidanCurtain_TurnOff(BgHidanCurtain* this, GlobalContext* globalCtx); +void BgHidanCurtain_WaitForTimer(BgHidanCurtain* this, GlobalContext* globalCtx); + +typedef struct { + /* 0x00 */ s16 radius; + /* 0x02 */ s16 height; + /* 0x04 */ f32 scale; + /* 0x08 */ f32 riseDist; + /* 0x0C */ f32 riseSpeed; +} BgHidanCurtainParams; // size = 0x10 + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 81, 144, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sCcInfoInit = { 1, 80, 100, MASS_IMMOVABLE }; + +static BgHidanCurtainParams sHCParams[] = { { 81, 144, 0.090f, 144.0f, 5.0f }, { 46, 88, 0.055f, 88.0f, 3.0f } }; + +const ActorInit Bg_Hidan_Curtain_InitVars = { + ACTOR_BG_HIDAN_CURTAIN, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgHidanCurtain), + (ActorFunc)BgHidanCurtain_Init, + (ActorFunc)BgHidanCurtain_Destroy, + (ActorFunc)BgHidanCurtain_Update, + (ActorFunc)BgHidanCurtain_Draw, + NULL, +}; + +void BgHidanCurtain_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHidanCurtain* this = (BgHidanCurtain*)thisx; + BgHidanCurtainParams* hcParams; + + osSyncPrintf("Curtain (arg_data 0x%04x)\n", this->actor.params); + Actor_SetFocus(&this->actor, 20.0f); + this->type = (thisx->params >> 0xC) & 0xF; + if (this->type > 6) { + // "Type is not set" + osSyncPrintf("Error : object のタイプが設定されていない(%s %d)(arg_data 0x%04x)\n", "../z_bg_hidan_curtain.c", + 352, this->actor.params); + Actor_Kill(&this->actor); + return; + } + + this->size = ((this->type == 2) || (this->type == 4)) ? 1 : 0; + hcParams = &sHCParams[this->size]; + this->treasureFlag = (thisx->params >> 6) & 0x3F; + thisx->params &= 0x3F; + + if ((this->actor.params < 0) || (this->actor.params > 0x3F)) { + // "Save bit is not set" + osSyncPrintf("Warning : object のセーブビットが設定されていない(%s %d)(arg_data 0x%04x)\n", + "../z_bg_hidan_curtain.c", 373, this->actor.params); + } + Actor_SetScale(&this->actor, hcParams->scale); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->collider.dim.pos.x = this->actor.world.pos.x; + this->collider.dim.pos.y = this->actor.world.pos.y; + this->collider.dim.pos.z = this->actor.world.pos.z; + this->collider.dim.radius = hcParams->radius; + this->collider.dim.height = hcParams->height; + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetInfo(&thisx->colChkInfo, NULL, &sCcInfoInit); + if (this->type == 0) { + this->actionFunc = BgHidanCurtain_WaitForClear; + } else { + this->actionFunc = BgHidanCurtain_WaitForSwitchOn; + if ((this->type == 4) || (this->type == 5)) { + this->actor.world.pos.y = this->actor.home.pos.y - hcParams->riseDist; + } + } + if (((this->type == 1) && Flags_GetTreasure(globalCtx, this->treasureFlag)) || + (((this->type == 0) || (this->type == 6)) && Flags_GetClear(globalCtx, this->actor.room))) { + Actor_Kill(&this->actor); + } + this->texScroll = Rand_ZeroOne() * 15.0f; +} + +void BgHidanCurtain_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHidanCurtain* this = (BgHidanCurtain*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void BgHidanCurtain_WaitForSwitchOn(BgHidanCurtain* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + if (this->type == 1) { + this->actionFunc = BgHidanCurtain_WaitForCutscene; + OnePointCutscene_Init(globalCtx, 3350, -99, &this->actor, MAIN_CAM); + this->timer = 50; + } else if (this->type == 3) { + this->actionFunc = BgHidanCurtain_WaitForCutscene; + OnePointCutscene_Init(globalCtx, 3360, 60, &this->actor, MAIN_CAM); + this->timer = 30; + } else { + this->actionFunc = BgHidanCurtain_TurnOff; + } + } +} + +void BgHidanCurtain_WaitForCutscene(BgHidanCurtain* this, GlobalContext* globalCtx) { + if (this->timer-- == 0) { + this->actionFunc = BgHidanCurtain_TurnOff; + } +} + +void BgHidanCurtain_WaitForClear(BgHidanCurtain* this, GlobalContext* globalCtx) { + if (Flags_GetClear(globalCtx, this->actor.room)) { + this->actionFunc = BgHidanCurtain_TurnOff; + } +} + +void BgHidanCurtain_WaitForSwitchOff(BgHidanCurtain* this, GlobalContext* globalCtx) { + if (!Flags_GetSwitch(globalCtx, this->actor.params)) { + this->actionFunc = BgHidanCurtain_TurnOn; + } +} + +void BgHidanCurtain_TurnOn(BgHidanCurtain* this, GlobalContext* globalCtx) { + f32 riseSpeed = sHCParams[this->size].riseSpeed; + + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, riseSpeed)) { + Flags_UnsetSwitch(globalCtx, this->actor.params); + this->actionFunc = BgHidanCurtain_WaitForSwitchOn; + } +} + +void BgHidanCurtain_TurnOff(BgHidanCurtain* this, GlobalContext* globalCtx) { + BgHidanCurtainParams* hcParams = &sHCParams[this->size]; + + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - hcParams->riseDist, hcParams->riseSpeed)) { + if ((this->type == 0) || (this->type == 6)) { + Actor_Kill(&this->actor); + } else if (this->type == 5) { + this->actionFunc = BgHidanCurtain_WaitForSwitchOff; + } else { + if (this->type == 2) { + this->timer = 400; + } else if (this->type == 4) { + this->timer = 200; + } else if (this->type == 3) { + this->timer = 160; + } else { // this->type == 1 + this->timer = 300; + } + this->actionFunc = BgHidanCurtain_WaitForTimer; + } + } +} + +void BgHidanCurtain_WaitForTimer(BgHidanCurtain* this, GlobalContext* globalCtx) { + DECR(this->timer); + if (this->timer == 0) { + this->actionFunc = BgHidanCurtain_TurnOn; + } + if ((this->type == 1) || (this->type == 3)) { + func_8002F994(&this->actor, this->timer); + } +} + +void BgHidanCurtain_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgHidanCurtain* this = (BgHidanCurtain*)thisx; + BgHidanCurtainParams* hcParams = &sHCParams[this->size]; + f32 riseProgress; + + if ((globalCtx->cameraPtrs[MAIN_CAM]->setting == CAM_SET_SLOW_CHEST_CS) || + (globalCtx->cameraPtrs[MAIN_CAM]->setting == CAM_SET_TURN_AROUND)) { + this->collider.base.atFlags &= ~AT_HIT; + } else { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + func_8002F71C(globalCtx, &this->actor, 5.0f, this->actor.yawTowardsPlayer, 1.0f); + } + if ((this->type == 4) || (this->type == 5)) { + this->actor.world.pos.y = (2.0f * this->actor.home.pos.y) - hcParams->riseDist - this->actor.world.pos.y; + } + + this->actionFunc(this, globalCtx); + + if ((this->type == 4) || (this->type == 5)) { + this->actor.world.pos.y = (2.0f * this->actor.home.pos.y) - hcParams->riseDist - this->actor.world.pos.y; + } + riseProgress = (hcParams->riseDist - (this->actor.home.pos.y - this->actor.world.pos.y)) / hcParams->riseDist; + this->alpha = 255.0f * riseProgress; + if (this->alpha > 50) { + this->collider.dim.height = hcParams->height * riseProgress; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (gSaveContext.sceneSetupIndex <= 3) { + func_8002F974(&this->actor, NA_SE_EV_FIRE_PILLAR_S - SFX_FLAG); + } + } else if ((this->type == 1) && Flags_GetTreasure(globalCtx, this->treasureFlag)) { + Actor_Kill(&this->actor); + } + this->texScroll++; + } +} + +void BgHidanCurtain_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanCurtain* this = (BgHidanCurtain*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_curtain.c", 685); + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 220, 0, this->alpha); + + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->texScroll & 0x7F, 0, 0x20, 0x40, 1, 0, + (this->texScroll * -0xF) & 0xFF, 0x20, 0x40)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_curtain.c", 698), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gEffFireCircleDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_curtain.c", 702); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.h new file mode 100644 index 000000000..de3b647b7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.h @@ -0,0 +1,23 @@ +#ifndef Z_BG_HIDAN_CURTAIN_H +#define Z_BG_HIDAN_CURTAIN_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanCurtain; + +typedef void (*BgHidanCurtainActionFunc)(struct BgHidanCurtain*, GlobalContext*); + +typedef struct BgHidanCurtain { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgHidanCurtainActionFunc actionFunc; + /* 0x0150 */ u8 type; + /* 0x0151 */ u8 treasureFlag; + /* 0x0152 */ u8 size; + /* 0x0153 */ u8 alpha; + /* 0x0154 */ s16 timer; + /* 0x0156 */ s16 texScroll; + /* 0x0158 */ ColliderCylinder collider; +} BgHidanCurtain; // size = 0x01A4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c new file mode 100644 index 000000000..a53826435 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.c @@ -0,0 +1,223 @@ +/* + * File: z_bg_hidan_dalm.c + * Overlay: ovl_Bg_Hidan_Dalm + * Description: Hammerable Totem Pieces (Fire Temple) + */ + +#include "z_bg_hidan_dalm.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanDalm_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanDalm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanDalm_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanDalm_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHidanDalm_Wait(BgHidanDalm* this, GlobalContext* globalCtx); +void BgHidanDalm_Shrink(BgHidanDalm* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Dalm_InitVars = { + ACTOR_BG_HIDAN_DALM, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanDalm), + (ActorFunc)BgHidanDalm_Init, + (ActorFunc)BgHidanDalm_Destroy, + (ActorFunc)BgHidanDalm_Update, + (ActorFunc)BgHidanDalm_Draw, + NULL, +}; + +static ColliderTrisElementInit sTrisElementInit[4] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { { { 305.0f, 0.0f, -300.0f }, { 305.0f, 600.0f, -300.0f }, { 305.0f, 600.0f, 300.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { { { 305.0f, 0.0f, -300.0f }, { 305.0f, 600.0f, 300.0f }, { 305.0f, 0.0f, 300.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { { { -305.0f, 0.0f, -300.0f }, { -305.0f, 600.0f, 300.0f }, { -305.0f, 600.0f, -300.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { { { -305.0f, 0.0f, -300.0f }, { -305.0f, 0.0f, 300.0f }, { -305.0f, 600.0f, 300.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 4, + sTrisElementInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -200, ICHAIN_STOP), +}; + +void BgHidanDalm_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanDalm* this = (BgHidanDalm*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gFireTempleHammerableTotemCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + Collider_InitTris(globalCtx, &this->collider); + Collider_SetTris(globalCtx, &this->collider, thisx, &sTrisInit, this->colliderItems); + + this->switchFlag = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + Actor_Kill(thisx); + } else { + this->actionFunc = BgHidanDalm_Wait; + } +} + +void BgHidanDalm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanDalm* this = (BgHidanDalm*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyTris(globalCtx, &this->collider); +} + +void BgHidanDalm_Wait(BgHidanDalm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((this->collider.base.acFlags & AC_HIT) && !Player_InCsMode(globalCtx) && + (player->swordAnimation == 22 || player->swordAnimation == 23)) { + this->collider.base.acFlags &= ~AC_HIT; + if ((this->collider.elements[0].info.bumperFlags & BUMP_HIT) || + (this->collider.elements[1].info.bumperFlags & BUMP_HIT)) { + this->dyna.actor.world.rot.y -= 0x4000; + } else { + this->dyna.actor.world.rot.y += 0x4000; + } + this->dyna.actor.world.pos.x += 32.5f * Math_SinS(this->dyna.actor.world.rot.y); + this->dyna.actor.world.pos.z += 32.5f * Math_CosS(this->dyna.actor.world.rot.y); + + func_8002DF54(globalCtx, &this->dyna.actor, 8); + this->dyna.actor.flags |= ACTOR_FLAG_4; + this->actionFunc = BgHidanDalm_Shrink; + this->dyna.actor.bgCheckFlags &= ~2; + this->dyna.actor.bgCheckFlags &= ~8; + this->dyna.actor.speedXZ = 10.0f; + Flags_SetSwitch(globalCtx, this->switchFlag); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_IT_HAMMER_HIT); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_DARUMA_VANISH); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgHidanDalm_Shrink(BgHidanDalm* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + s32 i; + Vec3f velocity; + Vec3f pos; + + if (Math_StepToF(&this->dyna.actor.scale.x, 0.0f, 0.004f)) { + func_8002DF54(globalCtx, &this->dyna.actor, 7); + Actor_Kill(&this->dyna.actor); + } + + this->dyna.actor.scale.y = this->dyna.actor.scale.z = this->dyna.actor.scale.x; + + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + this->dyna.actor.scale.x * 160.0f; + pos.z = this->dyna.actor.world.pos.z; + + for (i = 0; i < 4; i++) { + velocity.x = 5.0f * Math_SinS(this->dyna.actor.world.rot.y + 0x8000) + (Rand_ZeroOne() - 0.5f) * 5.0f; + velocity.z = 5.0f * Math_CosS(this->dyna.actor.world.rot.y + 0x8000) + (Rand_ZeroOne() - 0.5f) * 5.0f; + velocity.y = (Rand_ZeroOne() - 0.5f) * 1.5f; + EffectSsKiraKira_SpawnSmallYellow(globalCtx, &pos, &velocity, &accel); + } +} + +void BgHidanDalm_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanDalm* this = (BgHidanDalm*)thisx; + + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->dyna.actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 10.0f, 15.0f, 32.0f, 5); +} + +/** + * Update vertices of collider tris based on the current matrix + */ +void BgHidanDalm_UpdateCollider(BgHidanDalm* this) { + Vec3f pos2; + Vec3f pos1; + Vec3f pos0; + + Matrix_MultVec3f(&sTrisElementInit[0].dim.vtx[0], &pos0); + Matrix_MultVec3f(&sTrisElementInit[0].dim.vtx[1], &pos1); + Matrix_MultVec3f(&sTrisElementInit[0].dim.vtx[2], &pos2); + Collider_SetTrisVertices(&this->collider, 0, &pos0, &pos1, &pos2); + Matrix_MultVec3f(&sTrisElementInit[1].dim.vtx[2], &pos1); + Collider_SetTrisVertices(&this->collider, 1, &pos0, &pos2, &pos1); + + Matrix_MultVec3f(&sTrisElementInit[2].dim.vtx[0], &pos0); + Matrix_MultVec3f(&sTrisElementInit[2].dim.vtx[1], &pos1); + Matrix_MultVec3f(&sTrisElementInit[2].dim.vtx[2], &pos2); + Collider_SetTrisVertices(&this->collider, 2, &pos0, &pos1, &pos2); + Matrix_MultVec3f(&sTrisElementInit[3].dim.vtx[1], &pos2); + Collider_SetTrisVertices(&this->collider, 3, &pos0, &pos2, &pos1); +} + +void BgHidanDalm_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanDalm* this = (BgHidanDalm*)thisx; + + if (this->dyna.actor.params == 0) { + Gfx_DrawDListOpa(globalCtx, gFireTempleHammerableTotemBodyDL); + } else { + Gfx_DrawDListOpa(globalCtx, gFireTempleHammerableTotemHeadDL); + } + + if (this->actionFunc == BgHidanDalm_Wait) { + BgHidanDalm_UpdateCollider(this); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.h new file mode 100644 index 000000000..53d740114 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_HIDAN_DALM_H +#define Z_BG_HIDAN_DALM_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanDalm; + +typedef void (*BgHidanDalmActionFunc)(struct BgHidanDalm*, GlobalContext*); + +typedef struct BgHidanDalm { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanDalmActionFunc actionFunc; + /* 0x0168 */ u8 switchFlag; + /* 0x016C */ ColliderTris collider; + /* 0x018C */ ColliderTrisElement colliderItems[4]; +} BgHidanDalm; // size = 0x02FC + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Firewall/z_bg_hidan_firewall.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Firewall/z_bg_hidan_firewall.c new file mode 100644 index 000000000..ec7406c8c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Firewall/z_bg_hidan_firewall.c @@ -0,0 +1,217 @@ +/* + * File: z_bg_hidan_firewall.c + * Overlay: ovl_Bg_Hidan_Firewall + * Description: Proximity Triggered Flame Wall + */ + +#include "z_bg_hidan_firewall.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanFirewall_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFirewall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFirewall_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFirewall_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 BgHidanFirewall_CheckProximity(BgHidanFirewall* this, GlobalContext* globalCtx); +void BgHidanFirewall_Wait(BgHidanFirewall* this, GlobalContext* globalCtx); +void BgHidanFirewall_Countdown(BgHidanFirewall* this, GlobalContext* globalCtx); +void BgHidanFirewall_Erupt(BgHidanFirewall* this, GlobalContext* globalCtx); +void BgHidanFirewall_Collide(BgHidanFirewall* this, GlobalContext* globalCtx); +void BgHidanFirewall_ColliderFollowPlayer(BgHidanFirewall* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Firewall_InitVars = { + ACTOR_BG_HIDAN_FIREWALL, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanFirewall), + (ActorFunc)BgHidanFirewall_Init, + (ActorFunc)BgHidanFirewall_Destroy, + (ActorFunc)BgHidanFirewall_Update, + NULL, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 30, 83, 0, { 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 80, 100, MASS_IMMOVABLE }; + +void BgHidanFirewall_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanFirewall* this = (BgHidanFirewall*)thisx; + + this->actor.scale.x = 0.12f; + this->actor.scale.z = 0.12f; + this->actor.scale.y = 0.01f; + + this->unk_150 = 0; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + this->collider.dim.pos.y = this->actor.world.pos.y; + + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + + this->actionFunc = BgHidanFirewall_Wait; +} + +void BgHidanFirewall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanFirewall* this = (BgHidanFirewall*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 BgHidanFirewall_CheckProximity(BgHidanFirewall* this, GlobalContext* globalCtx) { + Player* player; + Vec3f distance; + + player = GET_PLAYER(globalCtx); + func_8002DBD0(&this->actor, &distance, &player->actor.world.pos); + + if (fabsf(distance.x) < 100.0f && fabsf(distance.z) < 120.0f) { + return 1; + } + return 0; +} + +void BgHidanFirewall_Wait(BgHidanFirewall* this, GlobalContext* globalCtx) { + if (BgHidanFirewall_CheckProximity(this, globalCtx) != 0) { + this->actor.draw = BgHidanFirewall_Draw; + this->actor.params = 5; + this->actionFunc = BgHidanFirewall_Countdown; + } +} + +void BgHidanFirewall_Countdown(BgHidanFirewall* this, GlobalContext* globalCtx) { + + if (this->actor.params != 0) { + this->actor.params--; + } + if (this->actor.params == 0) { + this->actionFunc = BgHidanFirewall_Erupt; + } +} + +void BgHidanFirewall_Erupt(BgHidanFirewall* this, GlobalContext* globalCtx) { + if (BgHidanFirewall_CheckProximity(this, globalCtx) != 0) { + Math_StepToF(&this->actor.scale.y, 0.1f, 0.01f / 0.4f); + } else { + if (Math_StepToF(&this->actor.scale.y, 0.01f, 0.01f) != 0) { + this->actor.draw = NULL; + this->actionFunc = BgHidanFirewall_Wait; + } else { + this->actor.params = 0; + } + } +} + +void BgHidanFirewall_Collide(BgHidanFirewall* this, GlobalContext* globalCtx) { + s16 phi_a3; + + if (Actor_IsFacingPlayer(&this->actor, 0x4000)) { + phi_a3 = this->actor.shape.rot.y; + } else { + phi_a3 = this->actor.shape.rot.y + 0x8000; + } + + func_8002F71C(globalCtx, &this->actor, 5.0f, phi_a3, 1.0f); +} + +void BgHidanFirewall_ColliderFollowPlayer(BgHidanFirewall* this, GlobalContext* globalCtx) { + Player* player; + Vec3f sp30; + f32 temp_ret; + f32 sp28; + f32 phi_f0; + + player = GET_PLAYER(globalCtx); + + func_8002DBD0(&this->actor, &sp30, &player->actor.world.pos); + if (sp30.x < -70.0f) { + sp30.x = -70.0f; + } else { + if (70.0f < sp30.x) { + phi_f0 = 70.0f; + } else { + phi_f0 = sp30.x; + } + sp30.x = phi_f0; + } + if (this->actor.params == 0) { + if (0.0f < sp30.z) { + sp30.z = -25.0f; + this->actor.params = -1; + } else { + sp30.z = 25.0f; + this->actor.params = 1; + } + } else { + sp30.z = this->actor.params * 25.0f; + } + sp28 = Math_SinS(this->actor.shape.rot.y); + temp_ret = Math_CosS(this->actor.shape.rot.y); + this->collider.dim.pos.x = this->actor.world.pos.x + sp30.x * temp_ret + sp30.z * sp28; + this->collider.dim.pos.z = this->actor.world.pos.z - sp30.x * sp28 + sp30.z * temp_ret; +} + +void BgHidanFirewall_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanFirewall* this = (BgHidanFirewall*)thisx; + s32 pad; + + this->unk_150 = (this->unk_150 + 1) % 8; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + BgHidanFirewall_Collide(this, globalCtx); + } + + this->actionFunc(this, globalCtx); + if (this->actionFunc == BgHidanFirewall_Erupt) { + BgHidanFirewall_ColliderFollowPlayer(this, globalCtx); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + func_8002F974(&this->actor, NA_SE_EV_FIRE_PLATE - SFX_FLAG); + } +} + +static void* sFireballTexs[] = { + gFireTempleFireball0Tex, gFireTempleFireball1Tex, gFireTempleFireball2Tex, gFireTempleFireball3Tex, + gFireTempleFireball4Tex, gFireTempleFireball5Tex, gFireTempleFireball6Tex, gFireTempleFireball7Tex, +}; + +void BgHidanFirewall_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanFirewall* this = (BgHidanFirewall*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_firewall.c", 448); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sFireballTexs[this->unk_150])); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x01, 255, 255, 0, 150); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_firewall.c", 458), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFireTempleFireballUpperHalfDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_firewall.c", 463); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Firewall/z_bg_hidan_firewall.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Firewall/z_bg_hidan_firewall.h new file mode 100644 index 000000000..8ea5aedfe --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Firewall/z_bg_hidan_firewall.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_HIDAN_FIREWALL_H +#define Z_BG_HIDAN_FIREWALL_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanFirewall; + +typedef void (*BgHidanFirewallActionFunc)(struct BgHidanFirewall*, GlobalContext*); + +typedef struct BgHidanFirewall { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgHidanFirewallActionFunc actionFunc; + /* 0x0150 */ s16 unk_150; + /* 0x0154 */ ColliderCylinder collider; +} BgHidanFirewall; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Fslift/z_bg_hidan_fslift.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Fslift/z_bg_hidan_fslift.c new file mode 100644 index 000000000..4bf62670e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Fslift/z_bg_hidan_fslift.c @@ -0,0 +1,144 @@ +/* + * File: z_bg_hidan_fslift.c + * Overlay: ovl_Bg_Hidan_Fslift + * Description: Hookshot Elevator + */ + +#include "z_bg_hidan_fslift.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgHidanFslift_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFslift_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFslift_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFslift_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80886FCC(BgHidanFslift* this, GlobalContext* globalCtx); +void func_8088706C(BgHidanFslift* this, GlobalContext* globalCtx); +void func_808870D8(BgHidanFslift* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Fslift_InitVars = { + ACTOR_BG_HIDAN_FSLIFT, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanFslift), + (ActorFunc)BgHidanFslift_Init, + (ActorFunc)BgHidanFslift_Destroy, + (ActorFunc)BgHidanFslift_Update, + (ActorFunc)BgHidanFslift_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 350, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_STOP), +}; + +void BgHidanFslift_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad1; + BgHidanFslift* this = (BgHidanFslift*)thisx; + CollisionHeader* colHeader = NULL; + s32 pad2; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gFireTempleHookshotElevatorCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_OBJ_HSBLOCK, + this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y + 40.0f, + this->dyna.actor.world.pos.z + -28.0f, 0, 0, 0, 2) == NULL) { + Actor_Kill(&this->dyna.actor); + return; + } + this->actionFunc = func_80886FCC; +} + +void func_80886F24(BgHidanFslift* this) { + if (this->dyna.actor.child != NULL && this->dyna.actor.child->update != NULL) { + this->dyna.actor.child->world.pos.x = this->dyna.actor.world.pos.x; + this->dyna.actor.child->world.pos.y = this->dyna.actor.world.pos.y + 40.0f; + this->dyna.actor.child->world.pos.z = this->dyna.actor.world.pos.z + -28.0f; + } else { + this->dyna.actor.child = NULL; + } +} + +void BgHidanFslift_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanFslift* this = (BgHidanFslift*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80886FB4(BgHidanFslift* this) { + this->timer = 40; + this->actionFunc = func_80886FCC; +} + +void func_80886FCC(BgHidanFslift* this, GlobalContext* globalCtx) { + s32 heightBool; + + if (this->timer) { + this->timer--; + } + + if (this->timer == 0) { + heightBool = false; + if ((this->dyna.actor.world.pos.y - this->dyna.actor.home.pos.y) < 0.5f) { + heightBool = true; + } + if (func_80043590(&this->dyna) && (heightBool)) { + this->actionFunc = func_808870D8; + } else if (!heightBool) { + this->actionFunc = func_8088706C; + } + } +} + +void func_8088706C(BgHidanFslift* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 4.0f)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + func_80886FB4(this); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE3 - SFX_FLAG); + } + func_80886F24(this); +} + +void func_808870D8(BgHidanFslift* this, GlobalContext* globalCtx) { + if (func_80043590(&this->dyna)) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 790.0f, 4.0f)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + func_80886FB4(this); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE3 - SFX_FLAG); + } + } else { + func_80886FB4(this); + } + func_80886F24(this); +} + +void BgHidanFslift_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanFslift* this = (BgHidanFslift*)thisx; + + this->actionFunc(this, globalCtx); + if (func_8004356C(&this->dyna)) { + if (this->unk_16A == 0) { + this->unk_16A = 3; + } + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_FIRE_PLATFORM); + } else if (!func_8004356C(&this->dyna)) { + if (this->unk_16A != 0) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + this->unk_16A = 0; + } +} + +void BgHidanFslift_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gFireTempleHookshotElevatorDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Fslift/z_bg_hidan_fslift.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Fslift/z_bg_hidan_fslift.h new file mode 100644 index 000000000..b18da3e95 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Fslift/z_bg_hidan_fslift.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_HIDAN_FSLIFT_H +#define Z_BG_HIDAN_FSLIFT_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanFslift; + +typedef void (*BgHidanFsliftActionFunc)(struct BgHidanFslift*, GlobalContext*); + +typedef struct BgHidanFslift { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanFsliftActionFunc actionFunc; + /* 0x0168 */ s16 timer; + /* 0x016A */ s16 unk_16A; +} BgHidanFslift; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Fwbig/z_bg_hidan_fwbig.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Fwbig/z_bg_hidan_fwbig.c new file mode 100644 index 000000000..251a8e534 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Fwbig/z_bg_hidan_fwbig.c @@ -0,0 +1,278 @@ +/* + * File: z_bg_hidan_fwbig.c + * Overlay: ovl_Bg_Hidan_Fwbig + * Description: Large fire walls at Fire Temple (flame wall before bombable door and the one that chases the player in + * the lava room) + */ + +#include "z_bg_hidan_fwbig.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0 */ FWBIG_MOVE, + /* 1 */ FWBIG_RESET, + /* 2 */ FWBIG_KILL +} HidanFwbigMoveState; + +void BgHidanFwbig_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFwbig_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFwbig_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanFwbig_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgHidanFwbig_UpdatePosition(BgHidanFwbig* this); + +void BgHidanFwbig_WaitForSwitch(BgHidanFwbig* this, GlobalContext* globalCtx); +void BgHidanFwbig_WaitForCs(BgHidanFwbig* this, GlobalContext* globalCtx); +void BgHidanFwbig_Lower(BgHidanFwbig* this, GlobalContext* globalCtx); +void BgHidanFwbig_WaitForTimer(BgHidanFwbig* this, GlobalContext* globalCtx); +void BgHidanFwbig_WaitForPlayer(BgHidanFwbig* this, GlobalContext* globalCtx); +void BgHidanFwbig_Move(BgHidanFwbig* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Fwbig_InitVars = { + ACTOR_BG_HIDAN_FWBIG, + ACTORCAT_PROP, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanFwbig), + (ActorFunc)BgHidanFwbig_Init, + (ActorFunc)BgHidanFwbig_Destroy, + (ActorFunc)BgHidanFwbig_Update, + (ActorFunc)BgHidanFwbig_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 30, 130, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_STOP), +}; + +void BgHidanFwbig_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgHidanFwbig* this = (BgHidanFwbig*)thisx; + Player* player = GET_PLAYER(globalCtx); + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->direction = (u16)(thisx->params >> 8); + thisx->params &= 0xFF; + if (this->direction != 0) { + this->actor.home.pos.x = 1560.0f; + this->actor.home.pos.z = 0.0f; + if (player->actor.world.pos.z > 300.0f) { + this->direction = -1; + this->actor.home.rot.y = this->actor.shape.rot.y = -0x4E38; + } else if (player->actor.world.pos.z < -300.0f) { + this->direction = 1; + this->actor.home.rot.y = this->actor.shape.rot.y = -0x31C8; + } else { + Actor_Kill(&this->actor); + return; + } + BgHidanFwbig_UpdatePosition(this); + Actor_SetScale(&this->actor, 0.15f); + this->collider.dim.height = 230; + this->actor.flags |= ACTOR_FLAG_4; + this->moveState = FWBIG_MOVE; + this->actionFunc = BgHidanFwbig_WaitForPlayer; + this->actor.world.pos.y = this->actor.home.pos.y - (2400.0f * this->actor.scale.y); + } else { + Actor_SetScale(&this->actor, 0.1f); + this->actionFunc = BgHidanFwbig_WaitForSwitch; + } +} + +void BgHidanFwbig_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHidanFwbig* this = (BgHidanFwbig*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void BgHidanFwbig_UpdatePosition(BgHidanFwbig* this) { + s16 startAngle = this->actor.shape.rot.y + this->direction * -0x4000; + + this->actor.world.pos.x = (Math_SinS(startAngle) * 885.4f) + this->actor.home.pos.x; + this->actor.world.pos.z = (Math_CosS(startAngle) * 885.4f) + this->actor.home.pos.z; +} + +void BgHidanFwbig_WaitForSwitch(BgHidanFwbig* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + this->actionFunc = BgHidanFwbig_WaitForCs; + OnePointCutscene_Init(globalCtx, 3340, -99, &this->actor, MAIN_CAM); + this->timer = 35; + } +} + +void BgHidanFwbig_WaitForCs(BgHidanFwbig* this, GlobalContext* globalCtx) { + if (this->timer-- == 0) { + this->actionFunc = BgHidanFwbig_Lower; + } +} + +void BgHidanFwbig_Rise(BgHidanFwbig* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 10.0f)) { + if (this->direction == 0) { + Flags_UnsetSwitch(globalCtx, this->actor.params); + this->actionFunc = BgHidanFwbig_WaitForSwitch; + } else { + this->actionFunc = BgHidanFwbig_Move; + } + } +} + +void BgHidanFwbig_Lower(BgHidanFwbig* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - (2400.0f * this->actor.scale.y), 10.0f)) { + if (this->direction == 0) { + this->actionFunc = BgHidanFwbig_WaitForTimer; + this->timer = 150; + } else if (this->moveState == FWBIG_KILL) { + Actor_Kill(&this->actor); + } else { + if (this->moveState == FWBIG_MOVE) { + this->actor.shape.rot.y -= (this->direction * 0x1800); + } else { + this->moveState = FWBIG_MOVE; + this->actor.shape.rot.y = this->actor.home.rot.y; + } + BgHidanFwbig_UpdatePosition(this); + this->actionFunc = BgHidanFwbig_Rise; + } + } +} + +void BgHidanFwbig_WaitForTimer(BgHidanFwbig* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + this->actionFunc = BgHidanFwbig_Rise; + } + func_8002F994(&this->actor, this->timer); +} + +void BgHidanFwbig_WaitForPlayer(BgHidanFwbig* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->actor.world.pos.x < 1150.0f) { + this->actionFunc = BgHidanFwbig_Rise; + OnePointCutscene_Init(globalCtx, 3290, -99, &this->actor, MAIN_CAM); + } +} + +void BgHidanFwbig_Move(BgHidanFwbig* this, GlobalContext* globalCtx) { + if (!Player_InCsMode(globalCtx)) { + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y + (this->direction * 0x6390), 0x20)) { + this->moveState = FWBIG_RESET; + this->actionFunc = BgHidanFwbig_Lower; + } else { + BgHidanFwbig_UpdatePosition(this); + } + } +} + +void BgHidanFwbig_MoveCollider(BgHidanFwbig* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f projPos; + f32 cs; + f32 sn; + + func_8002DBD0(&this->actor, &projPos, &player->actor.world.pos); + projPos.z = ((projPos.z >= 0.0f) ? 1.0f : -1.0f) * 25.0f * -1.0f; + if (this->direction == 0) { + projPos.x = CLAMP(projPos.x, -360.0f, 360.0f); + } else { + projPos.x = CLAMP(projPos.x, -500.0f, 500.0f); + } + + sn = Math_SinS(this->actor.shape.rot.y); + cs = Math_CosS(this->actor.shape.rot.y); + this->collider.dim.pos.x = this->actor.world.pos.x + (projPos.x * cs) + (projPos.z * sn); + this->collider.dim.pos.z = this->actor.world.pos.z - (projPos.x * sn) + (projPos.z * cs); + this->collider.dim.pos.y = this->actor.world.pos.y; + + this->actor.world.rot.y = (projPos.z < 0.0f) ? this->actor.shape.rot.y : this->actor.shape.rot.y + 0x8000; +} + +void BgHidanFwbig_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHidanFwbig* this = (BgHidanFwbig*)thisx; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + func_8002F71C(globalCtx, &this->actor, 5.0f, this->actor.world.rot.y, 1.0f); + if (this->direction != 0) { + this->actionFunc = BgHidanFwbig_Lower; + } + } + if ((this->direction != 0) && (globalCtx->roomCtx.prevRoom.num == this->actor.room)) { + this->moveState = FWBIG_KILL; + this->actionFunc = BgHidanFwbig_Lower; + } + + this->actionFunc(this, globalCtx); + + if ((this->actor.home.pos.y - 200.0f) < this->actor.world.pos.y) { + if (gSaveContext.sceneSetupIndex < 4) { + func_8002F974(&this->actor, NA_SE_EV_BURNING - SFX_FLAG); + } else if ((s16)this->actor.world.pos.x == -513) { + func_8002F974(&this->actor, NA_SE_EV_FLAME_OF_FIRE - SFX_FLAG); + } + BgHidanFwbig_MoveCollider(this, globalCtx); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgHidanFwbig_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + f32 height; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_fwbig.c", 630); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gEffUnknown4Tex)); + + gSPSegment(POLY_XLU_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gEffUnknown5Tex)); + + height = thisx->scale.y * 2400.0f; + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 220, 0, + ((height - (thisx->home.pos.y - thisx->world.pos.y)) * 255.0f) / height); + + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames % 0x80, 0, 0x20, 0x40, 1, 0, + (u8)(globalCtx->gameplayFrames * -15), 0x20, 0x40)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_fwbig.c", 660), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFireTempleBigFireWallDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_fwbig.c", 664); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Fwbig/z_bg_hidan_fwbig.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Fwbig/z_bg_hidan_fwbig.h new file mode 100644 index 000000000..c0fd6db5f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Fwbig/z_bg_hidan_fwbig.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HIDAN_FWBIG_H +#define Z_BG_HIDAN_FWBIG_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanFwbig; + +typedef void (*BgHidanFwbigActionFunc)(struct BgHidanFwbig*, GlobalContext*); + +typedef struct BgHidanFwbig { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgHidanFwbigActionFunc actionFunc; + /* 0x0150 */ s8 direction; + /* 0x0151 */ u8 moveState; + /* 0x0152 */ s16 timer; + /* 0x0154 */ ColliderCylinder collider; +} BgHidanFwbig; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Hamstep/z_bg_hidan_hamstep.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Hamstep/z_bg_hidan_hamstep.c new file mode 100644 index 000000000..816636d69 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Hamstep/z_bg_hidan_hamstep.c @@ -0,0 +1,414 @@ +/* + * File: z_bg_hidan_hamstep.c + * Overlay: ovl_Bg_Hidan_Hamstep + * Description: Stone Steps and Platforms (Fire Temple) + */ + +#include "z_bg_hidan_hamstep.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanHamstep_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanHamstep_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanHamstep_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanHamstep_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808887C4(BgHidanHamstep* this, GlobalContext* globalCtx); +void func_80888860(BgHidanHamstep* this, GlobalContext* globalCtx); +void func_808889B8(BgHidanHamstep* this, GlobalContext* globalCtx); +void func_80888A58(BgHidanHamstep* this, GlobalContext* globalCtx); +void BgHidanHamstep_DoNothing(BgHidanHamstep* this, GlobalContext* globalCtx); + +static f32 sYPosOffsets[] = { + -20.0f, -120.0f, -220.0f, -320.0f, -420.0f, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -20.0f, 3.0f, -20.0f }, { -20.0f, 3.0f, 20.0f }, { 20.0f, 3.0f, 20.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 20.0f, 3.0f, 20.0f }, { 20.0f, 3.0f, -20.0f }, { -20.0f, 3.0f, -20.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +const ActorInit Bg_Hidan_Hamstep_InitVars = { + ACTOR_BG_HIDAN_HAMSTEP, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanHamstep), + (ActorFunc)BgHidanHamstep_Init, + (ActorFunc)BgHidanHamstep_Destroy, + (ActorFunc)BgHidanHamstep_Update, + (ActorFunc)BgHidanHamstep_Draw, + NULL, +}; + +static BgHidanHamstepActionFunc sActionFuncs[] = { + func_808887C4, func_80888860, func_808889B8, func_80888A58, BgHidanHamstep_DoNothing, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static f32 sEffectPositions[][2] = { + { -100.0f, 40.0f }, { 100.0f, 40.0f }, { -100.0f, 0.0f }, { 100.0f, 0.0f }, + { -100.0f, -40.0f }, { 100.0f, 40.0f }, { -100.0f, -80.0f }, { -50.0f, -80.0f }, + { 0.0f, -80.0f }, { 50.0f, -80.0f }, { 100.0f, -80.0f }, +}; + +void BgHidanHamstep_SetupAction(BgHidanHamstep* this, s32 action) { + this->action = action; + this->actionFunc = sActionFuncs[action]; +} + +s32 BgHidanHamstep_SpawnChildren(BgHidanHamstep* this, GlobalContext* globalCtx2) { + BgHidanHamstep* step = this; + s32 i; + Vec3f pos; + f32 sin; + f32 cos; + s16 params; + GlobalContext* globalCtx = globalCtx2; + + pos = pos; // Required to match + pos.y = this->dyna.actor.home.pos.y - 100.0f; + sin = Math_SinS(this->dyna.actor.shape.rot.y + 0x8000); + cos = Math_CosS(this->dyna.actor.shape.rot.y + 0x8000); + + for (i = 0; i < 5; i++) { + pos.x = (((i * 160.0f) + 60.0f) * sin) + this->dyna.actor.home.pos.x; + pos.z = (((i * 160.0f) + 60.0f) * cos) + this->dyna.actor.home.pos.z; + + params = (i + 1) & 0xFF; + params |= (this->dyna.actor.params & 0xFF00); + + step = (BgHidanHamstep*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &step->dyna.actor, globalCtx, ACTOR_BG_HIDAN_HAMSTEP, pos.x, pos.y, pos.z, + this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, this->dyna.actor.world.rot.z, params); + + if (step == NULL) { + return 0; + } + } + return 1; +} + +void BgHidanHamstep_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanHamstep* this = (BgHidanHamstep*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + Vec3f sp48[3]; + s32 i; + s32 i2; + BgHidanHamstep* step; + + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + if ((this->dyna.actor.params & 0xFF) == 0) { + Collider_InitTris(globalCtx, &this->collider); + Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInit, this->colliderItems); + + for (i = 0; i < 2; i++) { + for (i2 = 0; i2 < 3; i2++) { + sp48[i2].x = sTrisInit.elements[i].dim.vtx[i2].x + this->dyna.actor.home.pos.x; + sp48[i2].y = sTrisInit.elements[i].dim.vtx[i2].y + this->dyna.actor.home.pos.y; + sp48[i2].z = sTrisInit.elements[i].dim.vtx[i2].z + this->dyna.actor.home.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &sp48[0], &sp48[1], &sp48[2]); + } + } + + if ((this->dyna.actor.params & 0xFF) == 0) { + CollisionHeader_GetVirtual(&gFireTempleStoneStep1Col, &colHeader); + } else { + CollisionHeader_GetVirtual(&gFireTempleStoneStep2Col, &colHeader); + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0xFF)) { + if ((this->dyna.actor.params & 0xFF) == 0) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + (-20.0f); + BgHidanHamstep_SetupAction(this, 4); + } else { + this->dyna.actor.world.pos.y = + sYPosOffsets[(this->dyna.actor.params & 0xFF) - 1] + this->dyna.actor.home.pos.y; + BgHidanHamstep_SetupAction(this, 4); + } + } else if ((this->dyna.actor.params & 0xFF) == 0) { + BgHidanHamstep_SetupAction(this, 0); + } else { + BgHidanHamstep_SetupAction(this, 2); + } + + this->dyna.actor.gravity = -1.2f; + this->dyna.actor.minVelocityY = -12.0f; + + if ((this->dyna.actor.params & 0xFF) == 0) { + // "Fire Temple Object [Hammer Step] appears" + osSyncPrintf("◯◯◯炎の神殿オブジェクト【ハンマーステップ】出現\n"); + if (BgHidanHamstep_SpawnChildren(this, globalCtx) == 0) { + step = this; + + // "[Hammer Step] I can't create a step!" + osSyncPrintf("【ハンマーステップ】 足場産れない!!\n"); + osSyncPrintf("%s %d\n", "../z_bg_hidan_hamstep.c", 425); + + while (step != NULL) { + Actor_Kill(&step->dyna.actor); + step = (BgHidanHamstep*)step->dyna.actor.child; + } + } + } +} + +void BgHidanHamstep_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanHamstep* this = (BgHidanHamstep*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + + if ((this->dyna.actor.params & 0xFF) == 0) { + Collider_DestroyTris(globalCtx, &this->collider); + } +} + +void func_808884C8(BgHidanHamstep* step, GlobalContext* globalCtx) { + Vec3f pos = step->dyna.actor.world.pos; + s32 i; + f32 sin; + f32 cos; + + pos.y -= 20.0f; + + func_80033480(globalCtx, &pos, 0.0f, 0, 600, 300, 0); + + sin = Math_SinS(step->dyna.actor.shape.rot.y + 0x8000); + cos = Math_CosS(step->dyna.actor.shape.rot.y + 0x8000); + + pos.y = step->dyna.actor.world.pos.y; + + for (i = 0; i < ARRAY_COUNT(sEffectPositions); i++) { + pos.x = (sEffectPositions[i][1] * sin) + (sEffectPositions[i][0] * cos) + step->dyna.actor.world.pos.x; + pos.z = ((sEffectPositions[i][1] * cos) - (sEffectPositions[i][0] * sin)) + step->dyna.actor.world.pos.z; + func_80033480(globalCtx, &pos, 0.0f, 0, 150, 150, 0); + } +} + +void func_80888638(BgHidanHamstep* this, GlobalContext* globalCtx) { + BgHidanHamstep* child = (BgHidanHamstep*)this->dyna.actor.child; + + while (child != NULL) { + if ((child->dyna.actor.params & 0xFF) != 0) { + func_808884C8(child, globalCtx); + } + child = (BgHidanHamstep*)child->dyna.actor.child; + } +} + +void func_80888694(BgHidanHamstep* this, BgHidanHamstep* parent) { + BgHidanHamstep* child; + + if ((this->dyna.actor.params & 0xFF) >= 2) { + if (parent->dyna.actor.world.pos.y < this->dyna.actor.world.pos.y) { + this->dyna.actor.world.pos.y = parent->dyna.actor.world.pos.y; + } else if ((this->dyna.actor.world.pos.y - parent->dyna.actor.world.pos.y) < -100.0f) { + this->dyna.actor.world.pos.y = parent->dyna.actor.world.pos.y - 100.0f; + } + } + + child = (BgHidanHamstep*)this->dyna.actor.child; + + while (child != NULL) { + if (this->dyna.actor.world.pos.y < child->dyna.actor.world.pos.y) { + child->dyna.actor.world.pos.y = this->dyna.actor.world.pos.y; + } + child = (BgHidanHamstep*)child->dyna.actor.child; + } +} + +void func_80888734(BgHidanHamstep* this) { + BgHidanHamstep* parent = (BgHidanHamstep*)this->dyna.actor.parent; + f32 frameDivisor = R_UPDATE_RATE * 0.5f; + + if (parent != NULL) { + this->dyna.actor.velocity.y = parent->dyna.actor.velocity.y; + + if ((this->dyna.actor.params & 0xFF) == 1) { + this->dyna.actor.world.pos.y = parent->dyna.actor.world.pos.y - 100.0f; + } else { + this->dyna.actor.world.pos.y += (this->dyna.actor.velocity.y * frameDivisor); + } + + func_80888694(this, parent); + } +} + +void func_808887C4(BgHidanHamstep* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + OnePointCutscene_Init(globalCtx, 3310, 100, &this->dyna.actor, MAIN_CAM); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_HAMMER_SWITCH); + this->collider.base.acFlags = AC_NONE; + BgHidanHamstep_SetupAction(this, 1); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0xFF); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_80888860(BgHidanHamstep* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + s32 quakeIndex; + + Actor_MoveForward(&this->dyna.actor); + + if (((this->dyna.actor.world.pos.y - this->dyna.actor.home.pos.y) < (-20.0f - this->dyna.actor.minVelocityY)) && + (this->dyna.actor.velocity.y <= 0.0f)) { + this->unk_244++; + + if (this->unk_244 >= 7) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + -20.0f; + BgHidanHamstep_SetupAction(this, 4); + } else { + this->dyna.actor.velocity.y *= -0.24f; + + if (1) {} + + if (this->unk_244 == 1) { + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, -15536); + Quake_SetQuakeValues(quakeIndex, 0, 0, 500, 0); + Quake_SetCountdown(quakeIndex, 20); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 255, 20, 150); + func_80888638(this, globalCtx); + osSyncPrintf("A(%d)\n", this->dyna.actor.params); + } + } + } +} + +void func_808889B8(BgHidanHamstep* this, GlobalContext* globalCtx) { + s32 pad; + BgHidanHamstep* parent = (BgHidanHamstep*)this->dyna.actor.parent; + + func_80888734(this); + + if ((parent->action == 4) || ((parent->action == 3) && (parent->unk_244 >= 5))) { + if ((this->dyna.actor.params & 0xFF) == 1) { + this->dyna.actor.world.pos.y = + sYPosOffsets[(this->dyna.actor.params & 0xFF) - 1] + this->dyna.actor.home.pos.y; + BgHidanHamstep_SetupAction(this, 4); + } else { + BgHidanHamstep_SetupAction(this, 3); + } + } +} + +void func_80888A58(BgHidanHamstep* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + s32 quakeIndex; + + Actor_MoveForward(&this->dyna.actor); + func_80888694(this, (BgHidanHamstep*)this->dyna.actor.parent); + + if (((this->dyna.actor.params & 0xFF) <= 0) || ((this->dyna.actor.params & 0xFF) >= 6)) { + // "[Hammer Step] arg_data strange (arg_data = %d)" + osSyncPrintf("【ハンマーステップ】 arg_data おかしい (arg_data = %d)", this->dyna.actor.params); + osSyncPrintf("%s %d\n", "../z_bg_hidan_hamstep.c", 696); + } + + if (((this->dyna.actor.world.pos.y - this->dyna.actor.home.pos.y) <= + sYPosOffsets[(this->dyna.actor.params & 0xFF) - 1]) && + (this->dyna.actor.velocity.y <= 0.0f)) { + this->unk_244++; + + if (this->unk_244 >= 7) { + this->dyna.actor.world.pos.y = + sYPosOffsets[(this->dyna.actor.params & 0xFF) - 1] + this->dyna.actor.home.pos.y; + BgHidanHamstep_SetupAction(this, 3); + } else { + this->dyna.actor.velocity.y *= -0.24f; + + if (1) {} + + if (this->unk_244 == 1) { + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIndex, -15536); + Quake_SetQuakeValues(quakeIndex, 20, 1, 0, 0); + Quake_SetCountdown(quakeIndex, 7); + + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + func_800AA000(10000.0f, 255, 20, 150); + func_808884C8(this, globalCtx); + + if ((this->dyna.actor.params & 0xFF) == 5) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + + osSyncPrintf("B(%d)\n", this->dyna.actor.params); + } + } + } +} + +void BgHidanHamstep_DoNothing(BgHidanHamstep* this, GlobalContext* globalCtx) { +} + +void BgHidanHamstep_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanHamstep* this = (BgHidanHamstep*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHidanHamstep_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_hamstep.c", 782); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_hamstep.c", 787), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if ((thisx->params & 0xFF) == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFireTempleStoneStep1DL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gFireTempleStoneStep2DL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_hamstep.c", 796); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Hamstep/z_bg_hidan_hamstep.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Hamstep/z_bg_hidan_hamstep.h new file mode 100644 index 000000000..223ac6278 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Hamstep/z_bg_hidan_hamstep.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HIDAN_HAMSTEP_H +#define Z_BG_HIDAN_HAMSTEP_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanHamstep; + +typedef void (*BgHidanHamstepActionFunc)(struct BgHidanHamstep*, GlobalContext*); + +typedef struct BgHidanHamstep { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderTris collider; + /* 0x0184 */ ColliderTrisElement colliderItems[2]; + /* 0x023C */ BgHidanHamstepActionFunc actionFunc; + /* 0x0240 */ s32 action; + /* 0x0244 */ s32 unk_244; +} BgHidanHamstep; // size = 0x0248 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Hrock/z_bg_hidan_hrock.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Hrock/z_bg_hidan_hrock.c new file mode 100644 index 000000000..6e70d64ce --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Hrock/z_bg_hidan_hrock.c @@ -0,0 +1,240 @@ +/* + * File: z_bg_hidan_hrock.c + * Overlay: ovl_Bg_Hidan_Hrock + * Description: Huge stone spike platform (Fire Temple) + */ + +#include "z_bg_hidan_hrock.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanHrock_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanHrock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanHrock_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanHrock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8088960C(BgHidanHrock* this, GlobalContext* globalCtx); +void func_808896B8(BgHidanHrock* this, GlobalContext* globalCtx); +void func_808894A4(BgHidanHrock* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Hrock_InitVars = { + ACTOR_BG_HIDAN_HROCK, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanHrock), + (ActorFunc)BgHidanHrock_Init, + (ActorFunc)BgHidanHrock_Destroy, + (ActorFunc)BgHidanHrock_Update, + (ActorFunc)BgHidanHrock_Draw, + NULL, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { { { -40.0f, 3.0f, -40.0f }, { -40.0f, 3.0f, 40.0f }, { 40.0f, 3.0f, 40.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { { { 40.0f, 3.0f, 40.0f }, { 40.0f, 3.0f, -40.0f }, { -40.0f, 3.0f, -40.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(gravity, -1, ICHAIN_STOP), +}; + +void BgHidanHrock_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanHrock* this = (BgHidanHrock*)thisx; + ColliderTrisElementInit* colliderElementInit; + Vec3f vertices[3]; + f32 cosRotY; + f32 sinRotY; + s32 i; + s32 j; + CollisionHeader* collisionHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + this->unk_16A = thisx->params & 0x3F; + thisx->params = (thisx->params >> 8) & 0xFF; + Collider_InitTris(globalCtx, &this->collider); + Collider_SetTris(globalCtx, &this->collider, thisx, &sTrisInit, this->colliderItems); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + + sinRotY = Math_SinS(thisx->shape.rot.y); + cosRotY = Math_CosS(thisx->shape.rot.y); + + if (thisx->params == 0) { + sinRotY *= 1.5f; + cosRotY *= 1.5f; + } + + for (i = 0; i < 2; i++) { + colliderElementInit = &sTrisInit.elements[i]; + + if (1) { + for (j = 0; j < 3; j++) { + Vec3f* vtx = &colliderElementInit->dim.vtx[j]; + + vertices[j].x = vtx->z * sinRotY + (thisx->home.pos.x + vtx->x * cosRotY); + vertices[j].y = vtx->y + thisx->home.pos.y; + vertices[j].z = vtx->z * cosRotY + (thisx->home.pos.z - vtx->x * sinRotY); + } + } + Collider_SetTrisVertices(&this->collider, i, &vertices[0], &vertices[1], &vertices[2]); + } + + if (Flags_GetSwitch(globalCtx, this->unk_16A)) { + this->actionFunc = func_808894A4; + if (thisx->params == 0) { + thisx->world.pos.y -= 2800.0f; + thisx->uncullZoneForward = 3000.0f; + } else if (thisx->params == 1) { + thisx->world.pos.y -= 800.0f; + } else if (thisx->params == 2) { + thisx->world.pos.y -= 240.0f; + } + } else { + if (thisx->params == 0) { + thisx->flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + thisx->uncullZoneForward = 3000.0f; + } + this->actionFunc = func_808896B8; + } + + if (thisx->params == 0) { + CollisionHeader_GetVirtual(&gFireTempleTallestPillarAboveRoomBeforeBossCol, &collisionHeader); + } else { + CollisionHeader_GetVirtual(&gFireTemplePillarInsertedInGroundCol, &collisionHeader); + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, collisionHeader); +} + +void BgHidanHrock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanHrock* this = (BgHidanHrock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyTris(globalCtx, &this->collider); +} + +void func_808894A4(BgHidanHrock* this, GlobalContext* globalCtx) { +} + +void func_808894B0(BgHidanHrock* this, GlobalContext* globalCtx) { + if (this->unk_168 != 0) { + this->unk_168--; + } + + this->dyna.actor.world.pos.x = + (Math_SinS(this->dyna.actor.world.rot.y + (this->unk_168 << 0xE)) * 5.0f) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = + (Math_CosS(this->dyna.actor.world.rot.y + (this->unk_168 << 0xE)) * 5.0f) + this->dyna.actor.home.pos.z; + + if (!(this->unk_168 % 4)) { + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 180, 10, 100); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_SHAKE); + } + + if (this->unk_168 == 0) { + if (this->dyna.actor.params == 0) { + this->dyna.actor.home.pos.y -= 2800.0f; + } else if (this->dyna.actor.params == 1) { + this->dyna.actor.home.pos.y -= 800.0f; + } else { + this->dyna.actor.home.pos.y -= 240.0f; + } + + this->actionFunc = func_8088960C; + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z; + } +} + +void func_8088960C(BgHidanHrock* this, GlobalContext* globalCtx) { + this->dyna.actor.velocity.y++; + + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, this->dyna.actor.velocity.y)) { + this->dyna.actor.flags &= ~(ACTOR_FLAG_4 | ACTOR_FLAG_5); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + + if (this->dyna.actor.params == 0) { + if (globalCtx->roomCtx.curRoom.num == 10) { + this->dyna.actor.room = 10; + } else { + Actor_Kill(&this->dyna.actor); + } + } + + this->actionFunc = func_808894A4; + } +} + +void func_808896B8(BgHidanHrock* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & 2) { + this->collider.base.acFlags &= ~2; + this->actionFunc = func_808894B0; + this->dyna.actor.flags |= ACTOR_FLAG_4; + + if (this->dyna.actor.params == 0) { + this->dyna.actor.room = -1; + } + + this->unk_168 = 20; + Flags_SetSwitch(globalCtx, this->unk_16A); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (func_8004356C(&this->dyna)) { + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 5.0f, 1.0f); + } else { + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 1.0f); + } +} + +void BgHidanHrock_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanHrock* this = (BgHidanHrock*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHidanHrock_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dlists[] = { + gFireTempleTallestPillarAboveRoomBeforeBossDL, + gFireTemplePillarInsertedInGroundDL, + gFireTemplePillarInsertedInGroundDL, + }; + + Gfx_DrawDListOpa(globalCtx, dlists[thisx->params]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Hrock/z_bg_hidan_hrock.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Hrock/z_bg_hidan_hrock.h new file mode 100644 index 000000000..3333f5bc4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Hrock/z_bg_hidan_hrock.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HIDAN_HROCK_H +#define Z_BG_HIDAN_HROCK_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanHrock; + +typedef void (*BgHidanHrockActionFunc)(struct BgHidanHrock*, GlobalContext*); + +typedef struct BgHidanHrock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanHrockActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ u8 unk_16A; + /* 0x016C */ ColliderTris collider; + /* 0x018C */ ColliderTrisElement colliderItems[2]; +} BgHidanHrock; // size = 0x0244 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.c new file mode 100644 index 000000000..bd125386c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.c @@ -0,0 +1,163 @@ +/* + * File: z_bg_hidan_kousi.c + * Overlay: ovl_Bg_Hidan_Kousi + * Description: + */ + +#include "z_bg_hidan_kousi.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgHidanKousi_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanKousi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanKousi_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanKousi_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80889ACC(BgHidanKousi* this); +void func_80889B5C(BgHidanKousi* this, GlobalContext* globalCtx); +void func_80889BC0(BgHidanKousi* this, GlobalContext* globalCtx); +void func_80889C18(BgHidanKousi* this, GlobalContext* globalCtx); +void func_80889C90(BgHidanKousi* this, GlobalContext* globalCtx); +void func_80889D28(BgHidanKousi* this, GlobalContext* globalCtx); + +static f32 D_80889E40[] = { 120.0f, 150.0f, 150.0f }; + +const ActorInit Bg_Hidan_Kousi_InitVars = { + ACTOR_BG_HIDAN_KOUSI, + ACTORCAT_PROP, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanKousi), + (ActorFunc)BgHidanKousi_Init, + (ActorFunc)BgHidanKousi_Destroy, + (ActorFunc)BgHidanKousi_Update, + (ActorFunc)BgHidanKousi_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static CollisionHeader* sMetalFencesCollisions[] = { + &gFireTempleMetalFenceWithSlantCol, + &gFireTempleMetalFenceCol, + &gFireTempleMetalFence2Col, +}; + +static s16 D_80889E7C[] = { + 0x4000, + 0xC000, + 0xC000, + 0x0000, +}; + +static Gfx* sMetalFencesDLs[] = { + gFireTempleMetalFenceWithSlantDL, + gFireTempleMetalFenceDL, + gFireTempleMetalFence2DL, +}; + +void BgHidanKousi_SetupAction(BgHidanKousi* this, BgHidanKousiActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgHidanKousi_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKousi* this = (BgHidanKousi*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + Actor_SetFocus(thisx, 50.0f); + osSyncPrintf("◯◯◯炎の神殿オブジェクト【格子(arg_data : %0x)】出現 (%d %d)\n", thisx->params, thisx->params & 0xFF, + ((s32)thisx->params >> 8) & 0xFF); + + Actor_ProcessInitChain(thisx, sInitChain); + if (((thisx->params & 0xFF) < 0) || ((thisx->params & 0xFF) >= 3)) { + osSyncPrintf("arg_data おかしい 【格子】\n"); + } + + CollisionHeader_GetVirtual(sMetalFencesCollisions[thisx->params & 0xFF], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + thisx->world.rot.y = D_80889E7C[this->dyna.actor.params & 0xFF] + thisx->shape.rot.y; + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0xFF)) { + func_80889ACC(this); + BgHidanKousi_SetupAction(this, func_80889D28); + } else { + BgHidanKousi_SetupAction(this, func_80889B5C); + } +} + +void BgHidanKousi_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKousi* this = (BgHidanKousi*)thisx; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80889ACC(BgHidanKousi* this) { + s32 pad[2]; + Vec3s* rot = &this->dyna.actor.world.rot; + f32 temp1 = D_80889E40[this->dyna.actor.params & 0xFF] * Math_SinS(rot->y); + f32 temp2 = D_80889E40[this->dyna.actor.params & 0xFF] * Math_CosS(rot->y); + + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x + temp1; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z + temp2; +} + +void func_80889B5C(BgHidanKousi* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0xFF)) { + BgHidanKousi_SetupAction(this, func_80889BC0); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->unk_168 = 0xC8; + } +} + +void func_80889BC0(BgHidanKousi* this, GlobalContext* globalCtx) { + this->unk_168 -= 1; + if (this->dyna.actor.category == func_8005B198() || (this->unk_168 <= 0)) { + BgHidanKousi_SetupAction(this, func_80889C18); + } +} + +void func_80889C18(BgHidanKousi* this, GlobalContext* globalCtx) { + this->dyna.actor.speedXZ += 0.2f; + if (this->dyna.actor.speedXZ > 2.0f) { + this->dyna.actor.speedXZ = 2.0f; + BgHidanKousi_SetupAction(this, func_80889C90); + } + Actor_MoveForward(&this->dyna.actor); + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG); +} + +void func_80889C90(BgHidanKousi* this, GlobalContext* globalCtx) { + func_8002D7EC(&this->dyna.actor); + if (D_80889E40[this->dyna.actor.params & 0xFF] < + Math_Vec3f_DistXYZ(&this->dyna.actor.home.pos, &this->dyna.actor.world.pos)) { + func_80889ACC(this); + BgHidanKousi_SetupAction(this, func_80889D28); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG); + } +} + +void func_80889D28(BgHidanKousi* this, GlobalContext* globalCtx) { +} + +void BgHidanKousi_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKousi* this = (BgHidanKousi*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgHidanKousi_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_kousi.c", 350); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_kousi.c", 354), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sMetalFencesDLs[thisx->params & 0xFF]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_kousi.c", 359); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h new file mode 100644 index 000000000..5507726ae --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Kousi/z_bg_hidan_kousi.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_HIDAN_KOUSI_H +#define Z_BG_HIDAN_KOUSI_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanKousi; + +typedef void (*BgHidanKousiActionFunc)(struct BgHidanKousi*, GlobalContext*); + +typedef struct BgHidanKousi { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanKousiActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; +} BgHidanKousi; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c new file mode 100644 index 000000000..8f2cb1240 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.c @@ -0,0 +1,338 @@ +/* + * File: z_bg_hidan_kowarerukabe.c + * Overlay: ovl_Bg_Hidan_Kowarerukabe + * Description: Fire Temple Bombable Walls and Floors + */ + +#include "z_bg_hidan_kowarerukabe.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +typedef enum { + /* 0 */ CRACKED_STONE_FLOOR, + /* 1 */ BOMBABLE_WALL, + /* 2 */ LARGE_BOMBABLE_WALL +} FireTempleBombableObjectsType; + +void BgHidanKowarerukabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanKowarerukabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanKowarerukabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanKowarerukabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Kowarerukabe_InitVars = { + ACTOR_BG_HIDAN_KOWARERUKABE, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanKowarerukabe), + (ActorFunc)BgHidanKowarerukabe_Init, + (ActorFunc)BgHidanKowarerukabe_Destroy, + (ActorFunc)BgHidanKowarerukabe_Update, + (ActorFunc)BgHidanKowarerukabe_Draw, + NULL, +}; + +static Gfx* sBreakableWallDLists[] = { + gFireTempleCrackedStoneFloorDL, + gFireTempleBombableWallDL, + gFireTempleLargeBombableWallDL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 100 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +void BgHidanKowarerukabe_InitDynaPoly(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + static CollisionHeader* collisionHeaders[] = { + &gFireTempleCrackedStoneFloorCol, + &gFireTempleBombableWallCol, + &gFireTempleLargeBombableWallCol, + }; + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + if (collisionHeaders[this->dyna.actor.params & 0xFF] != NULL) { + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(collisionHeaders[this->dyna.actor.params & 0xFF], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } else { + this->dyna.bgId = BGACTOR_NEG_ONE; + } +} + +void BgHidanKowarerukabe_InitColliderSphere(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + static s16 sphereRadii[] = { 80, 45, 80 }; + static s16 sphereYPositions[] = { 0, 500, 500 }; + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->colliderItems); + + this->collider.elements[0].dim.modelSphere.radius = sphereRadii[this->dyna.actor.params & 0xFF]; + this->collider.elements[0].dim.modelSphere.center.y = sphereYPositions[this->dyna.actor.params & 0xFF]; +} + +void BgHidanKowarerukabe_OffsetActorYPos(BgHidanKowarerukabe* this) { + static f32 actorYPosOffsets[] = { 0.7f, 0.0f, 0.0f }; + + this->dyna.actor.world.pos.y = actorYPosOffsets[this->dyna.actor.params & 0xFF] + this->dyna.actor.home.pos.y; +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgHidanKowarerukabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKowarerukabe* this = (BgHidanKowarerukabe*)thisx; + + BgHidanKowarerukabe_InitDynaPoly(this, globalCtx); + + if (((this->dyna.actor.params & 0xFF) < CRACKED_STONE_FLOOR) || + ((this->dyna.actor.params & 0xFF) > LARGE_BOMBABLE_WALL)) { + // "Error: Fire Temple Breakable Walls. arg_data I can't determine the (%s %d)(arg_data 0x%04x)" + osSyncPrintf("Error : 炎の神殿 壊れる壁 の arg_data が判別出来ない(%s %d)(arg_data 0x%04x)\n", + "../z_bg_hidan_kowarerukabe.c", 254, this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + return; + } + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + Actor_Kill(&this->dyna.actor); + return; + } + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + Actor_SetScale(&this->dyna.actor, 0.1f); + BgHidanKowarerukabe_InitColliderSphere(this, globalCtx); + BgHidanKowarerukabe_OffsetActorYPos(this); + // "(fire walls, floors, destroyed by bombs)(arg_data 0x%04x)" + osSyncPrintf("(hidan 爆弾で壊れる 壁 床)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgHidanKowarerukabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKowarerukabe* this = (BgHidanKowarerukabe*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgHidanKowarerukabe_SpawnDust(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f pos; + + pos = this->dyna.actor.world.pos; + pos.y += 10.0f; + + func_80033480(globalCtx, &pos, 0.0f, 0, 600, 300, 1); + + pos.x = ((Rand_ZeroOne() - 0.5f) * 80.0f) + this->dyna.actor.world.pos.x; + pos.y = (Rand_ZeroOne() * 100.0f) + this->dyna.actor.world.pos.y; + pos.z = ((Rand_ZeroOne() - 0.5f) * 80.0f) + this->dyna.actor.world.pos.z; + + func_80033480(globalCtx, &pos, 100.0f, 4, 200, 250, 1); +} + +void BgHidanKowarerukabe_FloorBreak(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + s32 i; + s32 j; + Vec3f velocity; + Vec3f pos; + s16 arg5; + Actor* thisx = &this->dyna.actor; + f32 sin = Math_SinS(thisx->shape.rot.y); + f32 cos = Math_CosS(thisx->shape.rot.y); + f32 tmp1; + f32 tmp2; + s16 arg9; + + pos.y = thisx->world.pos.y + 10.0f; + + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + tmp1 = 24 * (i - 2); + tmp2 = 24 * (j - 2); + + pos.x = (tmp2 * sin) + (tmp1 * cos) + thisx->world.pos.x; + pos.z = (tmp2 * cos) - (tmp1 * sin) + thisx->world.pos.z; + + tmp1 = 8.0f * Rand_ZeroOne() * (i - 2); + tmp2 = 8.0f * Rand_ZeroOne() * (j - 2); + + velocity.x = (tmp2 * sin) + (tmp1 * cos); + velocity.y = 30.0f * Rand_ZeroOne(); + velocity.z = (tmp2 * cos) - (tmp1 * sin); + + arg9 = ((Rand_ZeroOne() - 0.5f) * 11.0f * 1.4f) + 11.0f; + + arg5 = (((i == 0) || (i == 4)) && ((j == 0) || (j == 4))) ? 65 : 64; + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &thisx->world.pos, -550, arg5, 15, 15, 0, arg9, 2, 16, 100, + KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_DANGEON_KEEP, gBrownFragmentDL); + } + } +} + +void func_8088A67C(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + s32 i; + s32 j; + Vec3f velocity; + Vec3f pos; + s16 arg5; + Actor* thisx = &this->dyna.actor; + f32 sin = Math_SinS(thisx->shape.rot.y); + f32 cos = Math_CosS(thisx->shape.rot.y); + f32 tmp1; + f32 tmp2; + s16 arg9; + + for (i = 0; i < 5; i++) { + pos.y = (20 * i) + thisx->world.pos.y; + for (j = 0; j < 5; j++) { + tmp1 = 16 * (j - 2); + + pos.x = (tmp1 * cos) + thisx->world.pos.x; + pos.z = -(tmp1 * sin) + thisx->world.pos.z; + + tmp1 = 3.0f * Rand_ZeroOne() * (j - 2); + tmp2 = 6.0f * Rand_ZeroOne(); + + velocity.x = (tmp2 * sin) + (tmp1 * cos); + velocity.y = 18.0f * Rand_ZeroOne(); + velocity.z = (tmp2 * cos) - (tmp1 * sin); + + arg9 = ((Rand_ZeroOne() - 0.5f) * 11.0f * 1.4f) + 11.0f; + arg5 = (arg9 >= 15) ? 32 : 64; + + if (Rand_ZeroOne() < 5.0f) { + arg5 |= 1; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &thisx->world.pos, -540, arg5, 20, 20, 0, arg9, 2, 32, 100, + KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_DANGEON_KEEP, gBrownFragmentDL); + } + } +} + +void BgHidanKowarerukabe_LargeWallBreak(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + s32 i; + s32 j; + Vec3f velocity; + Vec3f pos; + s16 arg5; + Actor* thisx = &this->dyna.actor; + f32 sin = Math_SinS(thisx->shape.rot.y); + f32 cos = Math_CosS(thisx->shape.rot.y); + f32 tmp1; + f32 tmp2; + s16 arg9; + + for (i = 0; i < 5; i++) { + pos.y = (24 * i) + thisx->world.pos.y; + for (j = 0; j < 5; j++) { + tmp1 = 28 * (j - 2); + + pos.x = (tmp1 * cos) + thisx->world.pos.x; + pos.z = -(tmp1 * sin) + thisx->world.pos.z; + + tmp1 = 6.0f * Rand_ZeroOne() * (j - 2); + tmp2 = 6.0f * Rand_ZeroOne(); + + velocity.x = (tmp2 * sin) + (tmp1 * cos); + velocity.y = 34.0f * Rand_ZeroOne(); + velocity.z = (tmp2 * cos) - (tmp1 * sin); + + arg9 = ((Rand_ZeroOne() - 0.5f) * 14.0f * 1.6f) + 14.0f; + arg5 = (arg9 > 20) ? 32 : 64; + + if (Rand_ZeroOne() < 5.0f) { + arg5 |= 1; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &thisx->world.pos, -650, arg5, 20, 20, 0, arg9, 2, 32, 100, + KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_DANGEON_KEEP, gBrownFragmentDL); + } + } +} + +void BgHidanKowarerukabe_Break(BgHidanKowarerukabe* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xFF) { + case CRACKED_STONE_FLOOR: + BgHidanKowarerukabe_FloorBreak(this, globalCtx); + break; + case BOMBABLE_WALL: + func_8088A67C(this, globalCtx); + break; + case LARGE_BOMBABLE_WALL: + BgHidanKowarerukabe_LargeWallBreak(this, globalCtx); + break; + } + + BgHidanKowarerukabe_SpawnDust(this, globalCtx); +} + +void BgHidanKowarerukabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKowarerukabe* this = (BgHidanKowarerukabe*)thisx; + s32 pad; + + if (Actor_GetCollidedExplosive(globalCtx, &this->collider.base) != NULL) { + BgHidanKowarerukabe_Break(this, globalCtx); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + + if ((this->dyna.actor.params & 0xFF) == 0) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 40, NA_SE_EV_EXPLOSION); + } else { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 40, NA_SE_EV_WALL_BROKEN); + } + + func_80078884(NA_SE_SY_CORRECT_CHIME); + Actor_Kill(&this->dyna.actor); + return; + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void BgHidanKowarerukabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanKowarerukabe* this = (BgHidanKowarerukabe*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_kowarerukabe.c", 565); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_kowarerukabe.c", 568), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sBreakableWallDLists[this->dyna.actor.params & 0xFF]); + + Collider_UpdateSpheres(0, &this->collider); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_kowarerukabe.c", 573); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.h new file mode 100644 index 000000000..5b59c09eb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.h @@ -0,0 +1,15 @@ +#ifndef Z_BG_HIDAN_KOWARERUKABE_H +#define Z_BG_HIDAN_KOWARERUKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanKowarerukabe; + +typedef struct BgHidanKowarerukabe { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderJntSph collider; + /* 0x0184 */ ColliderJntSphElement colliderItems[1]; +} BgHidanKowarerukabe; // size = 0x01C4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Rock/z_bg_hidan_rock.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Rock/z_bg_hidan_rock.c new file mode 100644 index 000000000..4522a9486 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Rock/z_bg_hidan_rock.c @@ -0,0 +1,405 @@ +/* + * File: z_bg_hidan_rock.c + * Overlay: ovl_Bg_Hidan_Rock + * Description: Stone blocks (Fire Temple) + */ + +#include "z_bg_hidan_rock.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanRock_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRock_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRock_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRock_Reset(void); + +void func_8088B24C(BgHidanRock* this); + +void func_8088B268(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B5F4(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B634(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B69C(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B79C(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B90C(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B954(BgHidanRock* this, GlobalContext* globalCtx); +void func_8088B990(BgHidanRock* this, GlobalContext* globalCtx); + +void func_8088BC40(GlobalContext* globalCtx, BgHidanRock* this); + +static Vec3f D_8088BF60 = { 3310.0f, 120.0f, 0.0f }; + +const ActorInit Bg_Hidan_Rock_InitVars = { + ACTOR_BG_HIDAN_ROCK, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanRock), + (ActorFunc)BgHidanRock_Init, + (ActorFunc)BgHidanRock_Destroy, + (ActorFunc)BgHidanRock_Update, + (ActorFunc)BgHidanRock_Draw, + (ActorResetFunc)BgHidanRock_Reset, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 45, 77, -40, { 3310, 120, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -600, ICHAIN_STOP), +}; + +void BgHidanRock_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRock* this = (BgHidanRock*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + + this->type = thisx->params & 0xFF; + this->unk_169 = 0; + + thisx->params = ((thisx->params) >> 8) & 0xFF; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + + if (this->type == 0) { + if (Flags_GetSwitch(globalCtx, thisx->params)) { + Math_Vec3f_Copy(&thisx->home.pos, &D_8088BF60); + Math_Vec3f_Copy(&thisx->world.pos, &D_8088BF60); + this->timer = 60; + this->actionFunc = func_8088B5F4; + } else { + this->actionFunc = func_8088B268; + } + thisx->flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + CollisionHeader_GetVirtual(&gFireTempleStoneBlock1Col, &colHeader); + } else { + CollisionHeader_GetVirtual(&gFireTempleStoneBlock2Col, &colHeader); + this->collider.dim.pos.x = thisx->home.pos.x; + this->collider.dim.pos.y = thisx->home.pos.y; + this->collider.dim.pos.z = thisx->home.pos.z; + this->actionFunc = func_8088B634; + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + this->timer = 0; +} + +void BgHidanRock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRock* this = (BgHidanRock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->collider); + Audio_StopSfxByPos(&this->unk_170); +} + +void func_8088B24C(BgHidanRock* this) { + this->dyna.actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + this->actionFunc = func_8088B990; +} + +static f32 D_8088BFC0 = 0.0f; +void func_8088B268(BgHidanRock* this, GlobalContext* globalCtx) { + f32 sp2C; + s32 temp_v1; + s32 frame; + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + if (this->timer == 0) { + if (D_8088BFC0 == 0.0f) { + if (this->dyna.unk_150 > 0.0f) { + D_8088BFC0 += 0.01f; + } else { + D_8088BFC0 -= 0.01f; + } + } + + this->dyna.actor.speedXZ += 0.05f; + this->dyna.actor.speedXZ = CLAMP_MAX(this->dyna.actor.speedXZ, 2.0f); + + if (D_8088BFC0 > 0.0f) { + temp_v1 = Math_StepToF(&D_8088BFC0, 20.0f, this->dyna.actor.speedXZ); + } else { + temp_v1 = Math_StepToF(&D_8088BFC0, -20.0f, this->dyna.actor.speedXZ); + } + + this->dyna.actor.world.pos.x = (Math_SinS(this->dyna.unk_158) * D_8088BFC0) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->dyna.unk_158) * D_8088BFC0) + this->dyna.actor.home.pos.z; + + if (temp_v1) { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + this->dyna.actor.home.pos.x = this->dyna.actor.world.pos.x; + this->dyna.actor.home.pos.z = this->dyna.actor.world.pos.z; + D_8088BFC0 = 0.0f; + this->dyna.actor.speedXZ = 0.0f; + this->timer = 5; + } + + func_8002F974(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + if (this->timer != 0) { + this->timer--; + } + } + } else { + this->timer = 0; + } + + sp2C = this->dyna.actor.world.pos.z - D_8088BF60.z; + if (sp2C < 0.5f) { + Flags_SetSwitch(globalCtx, this->dyna.actor.params); + Math_Vec3f_Copy(&this->dyna.actor.home.pos, &D_8088BF60); + this->dyna.actor.world.pos.x = D_8088BF60.x; + this->dyna.actor.world.pos.z = D_8088BF60.z; + this->dyna.actor.speedXZ = 0.0f; + D_8088BFC0 = 0.0f; + player->stateFlags2 &= ~0x10; + this->actionFunc = func_8088B79C; + } + + frame = globalCtx->gameplayFrames & 0xFF; + if (globalCtx->gameplayFrames & 0x100) { + this->unk_16C = 0.0f; + } else if (frame < 128) { + this->unk_16C = sinf(frame * (4 * 0.001f * M_PI)) * 19.625f; + } else if (frame < 230) { + this->unk_16C = 19.625f; + } else { + this->unk_16C -= 1.0f; + this->unk_16C = CLAMP_MIN(this->unk_16C, 0.0f); + } + + if (sp2C < 100.0f) { + this->unk_16C = CLAMP_MAX(this->unk_16C, 6.125f); + } +} + +void func_8088B5F4(BgHidanRock* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + func_8088B24C(this); + } +} + +void func_8088B634(BgHidanRock* this, GlobalContext* globalCtx) { + if (func_8004356C(&this->dyna)) { + this->timer = 20; + this->dyna.actor.world.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4000; + this->actionFunc = func_8088B69C; + } +} + +void func_8088B69C(BgHidanRock* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer != 0) { + this->dyna.actor.world.pos.x = + this->dyna.actor.home.pos.x + 5.0f * Math_SinS(this->dyna.actor.world.rot.y + this->timer * 0x4000); + this->dyna.actor.world.pos.z = + this->dyna.actor.home.pos.z + 5.0f * Math_CosS(this->dyna.actor.world.rot.y + this->timer * 0x4000); + } else { + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z; + func_8088B24C(this); + } + + if (!(this->timer % 4)) { + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 0xB4, 0x0A, 0x64); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_SHAKE); + } +} + +void func_8088B79C(BgHidanRock* this, GlobalContext* globalCtx) { + this->timer--; + if (this->dyna.actor.bgCheckFlags & 2) { + if (this->type == 0) { + this->timer = 60; + this->actionFunc = func_8088B5F4; + } else { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - 15.0f; + this->actionFunc = func_8088B90C; + this->dyna.actor.flags &= ~(ACTOR_FLAG_4 | ACTOR_FLAG_5); + } + + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + Audio_PlayActorSound2( + &this->dyna.actor, + SurfaceType_GetSfx(&globalCtx->colCtx, this->dyna.actor.floorPoly, this->dyna.actor.floorBgId) + 0x800); + } + + this->unk_16C -= 0.5f; + this->unk_16C = CLAMP_MIN(this->unk_16C, 0.0f); + + if (this->type == 0) { + if (func_8004356C(&this->dyna)) { + if (this->unk_169 == 0) { + this->unk_169 = 3; + } + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_FIRE_PLATFORM); + } else if (!func_8004356C(&this->dyna)) { + if (this->unk_169 != 0) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + this->unk_169 = 0; + } + } +} + +void func_8088B90C(BgHidanRock* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 1.0f)) { + this->actionFunc = func_8088B634; + } +} + +void func_8088B954(BgHidanRock* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + this->actionFunc = func_8088B79C; + this->dyna.actor.velocity.y = 0.0f; + } +} + +void func_8088B990(BgHidanRock* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->timer++; + if (this->dyna.unk_150 != 0.0f) { + this->dyna.actor.speedXZ = 0.0f; + player->stateFlags2 &= ~0x10; + } + + if ((this->type == 0 && (Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 1820.0f, + 0.25f, 20.0f, 0.5f) < 0.1f)) || + ((this->type != 0) && (Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 480.0, + 0.25f, 20.0f, 0.5f) < 0.1f))) { + if (this->type == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + } + this->timer = 20; + this->actionFunc = func_8088B954; + } + + this->unk_16C = (this->dyna.actor.world.pos.y + 50.0f - this->dyna.actor.home.pos.y + 40.0f) / 80.0f; + if (this->type == 0) { + if (func_8004356C(&this->dyna)) { + if (this->unk_169 == 0) { + this->unk_169 = 3; + } + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_FIRE_PLATFORM); + } else if (!func_8004356C(&this->dyna)) { + if (this->unk_169 != 0) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + this->unk_169 = 0; + } + } +} + +void BgHidanRock_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRock* this = (BgHidanRock*)thisx; + + this->actionFunc(this, globalCtx); + if (this->actionFunc == func_8088B79C) { + Actor_MoveForward(&this->dyna.actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 0.0f, 0.0f, 0.0f, 4); + } + + if (this->unk_16C > 0.0f) { + this->collider.dim.height = sCylinderInit.dim.height * this->unk_16C; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +static void* sVerticalFlamesTexs[] = { + gFireTempleBigVerticalFlame0Tex, gFireTempleBigVerticalFlame1Tex, gFireTempleBigVerticalFlame2Tex, + gFireTempleBigVerticalFlame3Tex, gFireTempleBigVerticalFlame4Tex, gFireTempleBigVerticalFlame5Tex, + gFireTempleBigVerticalFlame6Tex, gFireTempleBigVerticalFlame7Tex, +}; + +void func_8088BC40(GlobalContext* globalCtx, BgHidanRock* this) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_rock.c", 808); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x01, 255, 255, 0, 150); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 255); + + if (this->type == 0) { + Matrix_Translate(D_8088BF60.x, D_8088BF60.y - 40.0f, D_8088BF60.z, MTXMODE_NEW); + } else { + Matrix_Translate(this->dyna.actor.home.pos.x, this->dyna.actor.home.pos.y - 40.0f, this->dyna.actor.home.pos.z, + MTXMODE_NEW); + } + + Matrix_RotateZYX(0, Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000, 0, MTXMODE_APPLY); + Matrix_Translate(-10.5f, 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(6.0f, this->unk_16C, 6.0f, MTXMODE_APPLY); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sVerticalFlamesTexs[globalCtx->gameplayFrames & 7])); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_rock.c", 853), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFireTempleBigVerticalFlameDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_rock.c", 857); +} + +void BgHidanRock_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRock* this = (BgHidanRock*)thisx; + s32 pad; + + if (this->type == 0) { + Gfx_DrawDListOpa(globalCtx, gFireTempleStoneBlock1DL); + } else { + Gfx_DrawDListOpa(globalCtx, gFireTempleStoneBlock2DL); + } + + if (this->unk_16C > 0.0f) { + if (this->type == 0) { + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &D_8088BF60, &this->unk_170); + } else { + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &this->dyna.actor.home.pos, &this->unk_170); + } + + func_80078914(&this->unk_170, NA_SE_EV_FIRE_PILLAR - SFX_FLAG); + func_8088BC40(globalCtx, this); + } +} + +void BgHidanRock_Reset(void) { + D_8088BFC0 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Rock/z_bg_hidan_rock.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Rock/z_bg_hidan_rock.h new file mode 100644 index 000000000..a65c4234e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Rock/z_bg_hidan_rock.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_HIDAN_ROCK_H +#define Z_BG_HIDAN_ROCK_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanRock; + +typedef void (*BgHidanRockActionFunc)(struct BgHidanRock*, GlobalContext*); + +typedef struct BgHidanRock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanRockActionFunc actionFunc; + /* 0x0168 */ u8 type; + /* 0x0169 */ u8 unk_169; + /* 0x016A */ s16 timer; + /* 0x016C */ f32 unk_16C; + /* 0x0170 */ Vec3f unk_170; + /* 0x017C */ ColliderCylinder collider; +} BgHidanRock; // size = 0x01C8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c new file mode 100644 index 000000000..73044e8b1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.c @@ -0,0 +1,262 @@ +/* + * File: z_bg_hidan_rsekizou.c + * Overlay: ovl_Bg_Hidan_Rsekizou + * Description: Spinning Stone flamethrower + */ + +#include "z_bg_hidan_rsekizou.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanRsekizou_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRsekizou_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRsekizou_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanRsekizou_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Rsekizou_InitVars = { + ACTOR_BG_HIDAN_RSEKIZOU, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanRsekizou), + (ActorFunc)BgHidanRsekizou_Init, + (ActorFunc)BgHidanRsekizou_Destroy, + (ActorFunc)BgHidanRsekizou_Update, + (ActorFunc)BgHidanRsekizou_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[6] = { + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { 0, 30, 40 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { -35, 32, 77 }, 32 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { -80, 35, 130 }, 42 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { 0, 30, -40 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { 35, 32, -77 }, 32 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { 80, 35, -130 }, 42 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 6, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1500, ICHAIN_STOP), +}; + +static void* sFireballsTexs[] = { + gFireTempleFireball0Tex, gFireTempleFireball1Tex, gFireTempleFireball2Tex, gFireTempleFireball3Tex, + gFireTempleFireball4Tex, gFireTempleFireball5Tex, gFireTempleFireball6Tex, gFireTempleFireball7Tex, +}; + +void BgHidanRsekizou_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRsekizou* this = (BgHidanRsekizou*)thisx; + s32 i; + s32 pad; + CollisionHeader* colHeader; + + colHeader = NULL; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gFireTempleSpinningFlamethrowerCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->colliderItems); + for (i = 0; i < ARRAY_COUNT(this->colliderItems); i++) { + this->collider.elements[i].dim.worldSphere.radius = this->collider.elements[i].dim.modelSphere.radius; + } + this->burnFrame = 0; + this->bendFrame = 0; +} + +void BgHidanRsekizou_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRsekizou* this = (BgHidanRsekizou*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgHidanRsekizou_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRsekizou* this = (BgHidanRsekizou*)thisx; + s32 i; + ColliderJntSphElement* sphere; + s32 pad; + f32 yawSine; + f32 yawCosine; + + this->burnFrame = (this->burnFrame + 1) % 8; + + if (this->bendFrame != 0) { + this->bendFrame--; + } + + if (this->bendFrame == 0) { + this->bendFrame = 3; + } + + this->dyna.actor.shape.rot.y += 0x180; // Approximately 2 Degrees per Frame + yawSine = Math_SinS(this->dyna.actor.shape.rot.y); + yawCosine = Math_CosS(this->dyna.actor.shape.rot.y); + + for (i = 0; i < ARRAY_COUNT(this->colliderItems); i++) { + sphere = &this->collider.elements[i]; + sphere->dim.worldSphere.center.x = this->dyna.actor.home.pos.x + yawCosine * sphere->dim.modelSphere.center.x + + yawSine * sphere->dim.modelSphere.center.z; + sphere->dim.worldSphere.center.y = (s16)this->dyna.actor.home.pos.y + sphere->dim.modelSphere.center.y; + sphere->dim.worldSphere.center.z = (this->dyna.actor.home.pos.z - yawSine * sphere->dim.modelSphere.center.x) + + yawCosine * sphere->dim.modelSphere.center.z; + } + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + func_8002F974(&this->dyna.actor, NA_SE_EV_FIRE_PILLAR - SFX_FLAG); +} + +Gfx* BgHidanRsekizou_DrawFireball(GlobalContext* globalCtx, BgHidanRsekizou* this, s16 frame, MtxF* mf, s32 a, + Gfx* displayList) { + f32 coss; + f32 sins; + s32 temp; + f32 fVar6; + f32 tmpf7; + + temp = (((this->burnFrame + frame) % 8) * 7) * (1.0f / 7.0f); + gSPSegment(displayList++, 0x09, SEGMENTED_TO_VIRTUAL(sFireballsTexs[temp])); + + frame++; + fVar6 = (frame != 4) ? frame + ((3 - this->bendFrame) * (1.0f / 3.0f)) : frame; + + gDPSetPrimColor(displayList++, 0, 1, 255, 255, 0, 150); + gDPSetEnvColor(displayList++, 255, 0, 0, 255); + + if (a == 0) { + sins = -Math_SinS(this->dyna.actor.shape.rot.y - (frame * 1500)); + coss = -Math_CosS(this->dyna.actor.shape.rot.y - (frame * 1500)); + } else { + sins = Math_SinS(this->dyna.actor.shape.rot.y - (frame * 1500)); + coss = Math_CosS(this->dyna.actor.shape.rot.y - (frame * 1500)); + } + + mf->xx = mf->yy = mf->zz = (0.7f * fVar6) + 0.5f; + tmpf7 = (((((0.7f * fVar6) + 0.5f) * 10.0f) * fVar6) + 20.0f); + + mf->xw = (tmpf7 * sins) + this->dyna.actor.world.pos.x; + mf->yw = (this->dyna.actor.world.pos.y + 30.0f) + ((7.0f / 10.0f) * fVar6); + mf->zw = (tmpf7 * coss) + this->dyna.actor.world.pos.z; + + gSPMatrix(displayList++, + Matrix_MtxFToMtx(Matrix_CheckFloats(mf, "../z_bg_hidan_rsekizou.c", 543), + Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx))), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(displayList++, gFireTempleFireballDL); + + return displayList; +} + +void BgHidanRsekizou_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanRsekizou* this = (BgHidanRsekizou*)thisx; + s32 i; + s32 pad; + MtxF mf; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_rsekizou.c", 564); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_rsekizou.c", 568), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFireTempleSpinningFlamethrowerDL); + Matrix_MtxFCopy(&mf, &gMtxFClear); + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + + if ((s16)((Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->dyna.actor.shape.rot.y) - 0x2E6C) >= 0) { + for (i = 3; i >= 0; i--) { + POLY_XLU_DISP = BgHidanRsekizou_DrawFireball(globalCtx, this, i, &mf, 0, POLY_XLU_DISP); + } + + for (i = 0; i < 4; i++) { + POLY_XLU_DISP = BgHidanRsekizou_DrawFireball(globalCtx, this, i, &mf, 1, POLY_XLU_DISP); + } + } else { + for (i = 3; i >= 0; i--) { + POLY_XLU_DISP = BgHidanRsekizou_DrawFireball(globalCtx, this, i, &mf, 1, POLY_XLU_DISP); + } + + for (i = 0; i < 4; i++) { + POLY_XLU_DISP = BgHidanRsekizou_DrawFireball(globalCtx, this, i, &mf, 0, POLY_XLU_DISP); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_rsekizou.c", 600); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.h new file mode 100644 index 000000000..cfbd5d3e5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Rsekizou/z_bg_hidan_rsekizou.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_HIDAN_RSEKIZOU_H +#define Z_BG_HIDAN_RSEKIZOU_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanRsekizou; + +typedef struct BgHidanRsekizou { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s16 bendFrame; + /* 0x0166 */ s16 burnFrame; + /* 0x0168 */ ColliderJntSph collider; + /* 0x0188 */ ColliderJntSphElement colliderItems[6]; +} BgHidanRsekizou; // size = 0x0308 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Sekizou/z_bg_hidan_sekizou.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Sekizou/z_bg_hidan_sekizou.c new file mode 100644 index 000000000..36659fdc1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Sekizou/z_bg_hidan_sekizou.c @@ -0,0 +1,434 @@ +/* + * File: z_bg_hidan_sekizou.c + * Overlay: ovl_Bg_Hidan_Sekizou + * Description: Stationary flame thrower statue + */ + +#include "z_bg_hidan_sekizou.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanSekizou_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSekizou_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSekizou_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSekizou_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8088D434(BgHidanSekizou* this, GlobalContext* globalCtx); +void func_8088D720(BgHidanSekizou* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Sekizou_InitVars = { + ACTOR_BG_HIDAN_SEKIZOU, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanSekizou), + (ActorFunc)BgHidanSekizou_Init, + (ActorFunc)BgHidanSekizou_Destroy, + (ActorFunc)BgHidanSekizou_Update, + (ActorFunc)BgHidanSekizou_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[6] = { + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 30, 40 }, 23 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 32, 87 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 35, 150 }, 40 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 30, 40 }, 23 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 32, 87 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 35, 150 }, 40 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 6, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 40, 240, MASS_IMMOVABLE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1500, ICHAIN_STOP), +}; + +static void* sFireballsTexs[] = { + gFireTempleFireball0Tex, gFireTempleFireball1Tex, gFireTempleFireball2Tex, gFireTempleFireball3Tex, + gFireTempleFireball4Tex, gFireTempleFireball5Tex, gFireTempleFireball6Tex, gFireTempleFireball7Tex, +}; + +void func_8088CEC0(BgHidanSekizou* this, s32 arg1, s16 arg2) { + s32 i; + s32 start = arg1 * 3; + s32 end = start + 3; + f32 sp30 = Math_SinS(arg2); + f32 sp2C = Math_CosS(arg2); + + for (i = start; i < end; i++) { + ColliderJntSphElement* element = &this->collider.elements[i]; + + element->dim.worldSphere.center.x = this->dyna.actor.home.pos.x + (sp2C * element->dim.modelSphere.center.x) + + (sp30 * element->dim.modelSphere.center.z); + element->dim.worldSphere.center.y = (s16)this->dyna.actor.home.pos.y + element->dim.modelSphere.center.y; + element->dim.worldSphere.center.z = this->dyna.actor.home.pos.z - (sp30 * element->dim.modelSphere.center.x) + + (sp2C * element->dim.modelSphere.center.z); + element->info.toucherFlags |= TOUCH_ON; + element->info.ocElemFlags |= OCELEM_ON; + } +} + +void BgHidanSekizou_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHidanSekizou* this = (BgHidanSekizou*)thisx; + s32 i; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->elements); + for (i = 0; i < ARRAY_COUNT(this->elements); i++) { + this->collider.elements[i].dim.worldSphere.radius = this->collider.elements[i].dim.modelSphere.radius; + } + if (this->dyna.actor.params == 0) { + this->unk_168[0] = 36; + for (i = 0; i < 2; i++) { + func_8088CEC0(this, i, this->dyna.actor.shape.rot.y + ((i == 0) ? 0x2000 : -0x2000)); + } + CollisionHeader_GetVirtual(&gFireTempleStationaryFlamethrowerShortCol, &colHeader); + this->updateFunc = func_8088D720; + } else { + this->unk_168[0] = this->unk_168[1] = this->unk_168[2] = this->unk_168[3] = 0; + CollisionHeader_GetVirtual(&gFireTempleStationaryFlamethrowerTallCol, &colHeader); + this->updateFunc = func_8088D434; + } + this->unk_170 = 0; + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + CollisionCheck_SetInfo(&this->dyna.actor.colChkInfo, NULL, &sColChkInfoInit); +} + +void BgHidanSekizou_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgHidanSekizou* this = (BgHidanSekizou*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void func_8088D434(BgHidanSekizou* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 i; + s32 isAligned[2]; + s32 isClose; + s32 phi_s4; + + isClose = this->dyna.actor.xzDistToPlayer < 300.0f; + isAligned[0] = fabsf(this->dyna.actor.world.pos.x - player->actor.world.pos.x) < 80.0f; + isAligned[1] = fabsf(this->dyna.actor.world.pos.z - player->actor.world.pos.z) < 80.0f; + phi_s4 = 0; + for (i = 0; i < 4; i++) { + s16 diff; + s16* temp = &this->unk_168[i]; + + DECR(*temp); + diff = this->dyna.actor.yawTowardsPlayer - i * 0x4000; + if (isAligned[i % 2] && isClose) { + if (ABS(diff) <= 0x4000) { + if (*temp < 4) { + *temp = 35 - *temp; + } + func_8088CEC0(this, (phi_s4 > 1) ? 1 : phi_s4, this->dyna.actor.shape.rot.y + i * 0x4000); + phi_s4++; + } + } + } + for (i = 3 * phi_s4; i < ARRAY_COUNT(this->elements); i++) { + this->collider.elements[i].info.toucherFlags &= ~TOUCH_ON; + this->collider.elements[i].info.ocElemFlags &= ~OCELEM_ON; + } +} + +void func_8088D720(BgHidanSekizou* this, GlobalContext* globalCtx) { + this->unk_168[0]--; + if (this->unk_168[0] <= -36) { + this->unk_168[0] = 36; + } +} + +void func_8088D750(BgHidanSekizou* this, GlobalContext* globalCtx) { + s16 phi_a3; + + if (this->dyna.actor.xzDistToPlayer > 200.0f) { + phi_a3 = this->dyna.actor.yawTowardsPlayer; + } else if (this->dyna.actor.params == 0) { + phi_a3 = this->dyna.actor.yawTowardsPlayer - this->dyna.actor.shape.rot.y; + if (phi_a3 > 0x2000) { + phi_a3 = this->dyna.actor.shape.rot.y + 0x6000; + } else if (phi_a3 < -0x2000) { + phi_a3 = this->dyna.actor.shape.rot.y - 0x6000; + } else if (phi_a3 > 0) { + phi_a3 = this->dyna.actor.shape.rot.y - 0x2000; + } else { + phi_a3 = this->dyna.actor.shape.rot.y + 0x2000; + } + } else { + phi_a3 = this->dyna.actor.yawTowardsPlayer; + if (phi_a3 > 0x6000) { + phi_a3 = 0x4000; + } else if (phi_a3 > 0x4000) { + phi_a3 = -0x8000; + } else if (phi_a3 > 0x2000) { + phi_a3 = 0; + } else if (phi_a3 > 0) { + phi_a3 = 0x4000; + } else if (phi_a3 < -0x6000) { + phi_a3 = -0x4000; + } else if (phi_a3 < -0x4000) { + phi_a3 = -0x8000; + } else if (phi_a3 < -0x2000) { + phi_a3 = 0; + } else { + phi_a3 = -0x4000; + } + } + func_8002F71C(globalCtx, &this->dyna.actor, 5.0f, phi_a3, 1.0f); +} + +void BgHidanSekizou_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgHidanSekizou* this = (BgHidanSekizou*)thisx; + + if (this->unk_170 != 0) { + this->unk_170--; + } + if (this->unk_170 == 0) { + this->unk_170 = 4; + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + func_8088D750(this, globalCtx); + } + + this->updateFunc(this, globalCtx); + + if (this->dyna.actor.params == 0) { + if (this->unk_168[0] > 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + func_8002F974(&this->dyna.actor, NA_SE_EV_FIRE_PILLAR - SFX_FLAG); + } + } else { + if ((this->unk_168[0] > 0) || (this->unk_168[1] > 0) || (this->unk_168[2] > 0) || (this->unk_168[3] > 0)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + func_8002F974(&this->dyna.actor, NA_SE_EV_FIRE_PILLAR - SFX_FLAG); + } + } +} + +Gfx* func_8088D9F4(GlobalContext* globalCtx, BgHidanSekizou* this, s16 arg2, MtxF* arg3, f32 arg4, f32 arg5, s16 arg6, + Gfx* arg7) { + f32 temp_f0; + f32 temp_f2; + f32 phi_f12; + + arg6 = (((arg6 + arg2) % 8) * 7) * (1 / 7.0f); + arg2++; + gSPSegment(arg7++, 9, SEGMENTED_TO_VIRTUAL(sFireballsTexs[arg6])); + if (arg2 != 4) { + phi_f12 = arg2 + ((4 - this->unk_170) / 4.0f); + } else { + phi_f12 = arg2; + } + gDPSetPrimColor(arg7++, 0, 1, 255, 255, 0, 150); + gDPSetEnvColor(arg7++, 255, 0, 0, 255); + + arg3->xx = arg3->yy = arg3->zz = (0.7f * phi_f12) + 0.5f; + + temp_f2 = (arg3->xx * 10.0f * phi_f12) + 20.0f; + arg3->xw = (temp_f2 * arg4) + this->dyna.actor.world.pos.x; + arg3->yw = this->dyna.actor.world.pos.y + 30.0f + (.7f * phi_f12); + arg3->zw = (temp_f2 * arg5) + this->dyna.actor.world.pos.z; + gSPMatrix(arg7++, + Matrix_MtxFToMtx(Matrix_CheckFloats(arg3, "../z_bg_hidan_sekizou.c", 711), + Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx))), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(arg7++, gFireTempleFireballDL); + + return arg7; +} + +Gfx* func_8088DC50(GlobalContext* globalCtx, BgHidanSekizou* this, s16 arg2, s16 arg3, Gfx* arg4) { + s32 pad; + s16 temp_v1; + s32 phi_s1; + s32 phi_s2; + f32 temp_f20; + f32 temp_f22; + MtxF sp68; + s32 i; + + if (arg3 < 4) { + phi_s1 = 4 - arg3; + phi_s2 = 4; + } else { + phi_s1 = 0; + phi_s2 = 36 - arg3; + phi_s2 = CLAMP_MAX(phi_s2, 4); + } + temp_f20 = Math_SinS(arg2); + temp_f22 = Math_CosS(arg2); + Matrix_MtxFCopy(&sp68, &gMtxFClear); + temp_v1 = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - arg2; + + if (ABS(temp_v1) < 0x4000) { + for (i = phi_s2 - 1; i >= phi_s1; i--) { + arg4 = func_8088D9F4(globalCtx, this, i, &sp68, temp_f20, temp_f22, arg3, arg4); + } + } else { + for (i = phi_s1; i < phi_s2; i++) { + arg4 = func_8088D9F4(globalCtx, this, i, &sp68, temp_f20, temp_f22, arg3, arg4); + } + } + return arg4; +} + +void func_8088DE08(s16 arg0, s16 arg1, s32 arg2[]) { + s16 diff = arg0 - arg1; + + if (ABS(diff) <= 0x2000) { + arg2[0] = 0; + arg2[1] = 1; + arg2[2] = 3; + arg2[3] = 2; + } else if (ABS(diff) >= 0x6000) { + arg2[0] = 2; + arg2[1] = 3; + arg2[2] = 1; + arg2[3] = 0; + } else if (diff > 0x2000) { + arg2[0] = 1; + arg2[1] = 0; + arg2[2] = 2; + arg2[3] = 3; + } else { + arg2[0] = 3; + arg2[1] = 2; + arg2[2] = 0; + arg2[3] = 1; + } +} + +void BgHidanSekizou_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgHidanSekizou* this = (BgHidanSekizou*)thisx; + s32 i; + s32 sp6C[4]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_sekizou.c", 827); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_sekizou.c", 831), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->dyna.actor.params == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFireTempleStationaryFlamethrowerShortDL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gFireTempleStationaryFlamethrowerTallDL); + } + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + if (this->dyna.actor.params == 0) { + if (this->unk_168[0] > 0) { + if ((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->dyna.actor.shape.rot.y) >= 0) { + POLY_XLU_DISP = func_8088DC50(globalCtx, this, this->dyna.actor.shape.rot.y + 0x2000, this->unk_168[0], + POLY_XLU_DISP); + POLY_XLU_DISP = func_8088DC50(globalCtx, this, this->dyna.actor.shape.rot.y - 0x2000, this->unk_168[0], + POLY_XLU_DISP); + } else { + POLY_XLU_DISP = func_8088DC50(globalCtx, this, this->dyna.actor.shape.rot.y - 0x2000, this->unk_168[0], + POLY_XLU_DISP); + POLY_XLU_DISP = func_8088DC50(globalCtx, this, this->dyna.actor.shape.rot.y + 0x2000, this->unk_168[0], + POLY_XLU_DISP); + } + } + } else { + func_8088DE08(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)), this->dyna.actor.shape.rot.y, sp6C); + for (i = 0; i < 4; i++) { + s32 index = sp6C[i]; + + if (this->unk_168[index] > 0) { + POLY_XLU_DISP = func_8088DC50(globalCtx, this, this->dyna.actor.shape.rot.y + index * 0x4000, + this->unk_168[index], POLY_XLU_DISP); + } + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_sekizou.c", 899); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Sekizou/z_bg_hidan_sekizou.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Sekizou/z_bg_hidan_sekizou.h new file mode 100644 index 000000000..ed513f660 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Sekizou/z_bg_hidan_sekizou.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_HIDAN_SEKIZOU_H +#define Z_BG_HIDAN_SEKIZOU_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanSekizou; + +typedef void (*BgHidanSekizouUpdateFunc)(struct BgHidanSekizou*, GlobalContext*); + +typedef struct BgHidanSekizou { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanSekizouUpdateFunc updateFunc; + /* 0x0168 */ s16 unk_168[4]; + /* 0x0170 */ s16 unk_170; + /* 0x0174 */ ColliderJntSph collider; + /* 0x0194 */ ColliderJntSphElement elements[6]; +} BgHidanSekizou; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Sima/z_bg_hidan_sima.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Sima/z_bg_hidan_sima.c new file mode 100644 index 000000000..16fca69e4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Sima/z_bg_hidan_sima.c @@ -0,0 +1,298 @@ +/* + * File: z_bg_hidan_sima.c + * Overlay: ovl_Bg_Hidan_Sima + * Description: Stone platform (Fire Temple) + */ + +#include "z_bg_hidan_sima.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS 0 + +void BgHidanSima_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSima_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSima_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSima_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8088E518(BgHidanSima* this, GlobalContext* globalCtx); +void func_8088E5D0(BgHidanSima* this, GlobalContext* globalCtx); +void func_8088E6D0(BgHidanSima* this, GlobalContext* globalCtx); +void func_8088E760(BgHidanSima* this, GlobalContext* globalCtx); +void func_8088E7A8(BgHidanSima* this, GlobalContext* globalCtx); +void func_8088E90C(BgHidanSima* this); + +const ActorInit Bg_Hidan_Sima_InitVars = { + ACTOR_BG_HIDAN_SIMA, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanSima), + (ActorFunc)BgHidanSima_Init, + (ActorFunc)BgHidanSima_Destroy, + (ActorFunc)BgHidanSima_Update, + (ActorFunc)BgHidanSima_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { 0, 40, 100 }, 22 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 1, { { 0, 40, 145 }, 30 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInit), + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static void* sFireballsTexs[] = { + gFireTempleFireball0Tex, gFireTempleFireball1Tex, gFireTempleFireball2Tex, gFireTempleFireball3Tex, + gFireTempleFireball4Tex, gFireTempleFireball5Tex, gFireTempleFireball6Tex, gFireTempleFireball7Tex, +}; + +void BgHidanSima_Init(Actor* thisx, GlobalContext* globalCtx) { + BgHidanSima* this = (BgHidanSima*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + s32 i; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + if (this->dyna.actor.params == 0) { + CollisionHeader_GetVirtual(&gFireTempleStonePlatform1Col, &colHeader); + } else { + CollisionHeader_GetVirtual(&gFireTempleStonePlatform2Col, &colHeader); + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->elements); + for (i = 0; i < ARRAY_COUNT(sJntSphElementsInit); i++) { + this->collider.elements[i].dim.worldSphere.radius = this->collider.elements[i].dim.modelSphere.radius; + } + if (this->dyna.actor.params == 0) { + this->actionFunc = func_8088E518; + } else { + this->actionFunc = func_8088E760; + } +} + +void BgHidanSima_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanSima* this = (BgHidanSima*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void func_8088E518(BgHidanSima* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 3.4f); + if (func_8004356C(&this->dyna) && !(player->stateFlags1 & 0x6000)) { + this->timer = 20; + this->dyna.actor.world.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4000; + if (this->dyna.actor.home.pos.y <= this->dyna.actor.world.pos.y) { + this->actionFunc = func_8088E5D0; + } else { + this->actionFunc = func_8088E6D0; + } + } +} + +void func_8088E5D0(BgHidanSima* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer != 0) { + this->dyna.actor.world.pos.x = + Math_SinS(this->dyna.actor.world.rot.y + (this->timer * 0x4000)) * 5.0f + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = + Math_CosS(this->dyna.actor.world.rot.y + (this->timer * 0x4000)) * 5.0f + this->dyna.actor.home.pos.z; + } else { + this->actionFunc = func_8088E6D0; + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z; + } + if (!(this->timer % 4)) { + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 180, 10, 100); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_SHAKE); + } +} + +void func_8088E6D0(BgHidanSima* this, GlobalContext* globalCtx) { + if (func_8004356C(&this->dyna)) { + this->timer = 20; + } else if (this->timer != 0) { + this->timer--; + } + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 100.0f, 1.7f); + if (this->timer == 0) { + this->actionFunc = func_8088E518; + } +} + +void func_8088E760(BgHidanSima* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + this->dyna.actor.world.rot.y += 0x8000; + this->timer = 60; + this->actionFunc = func_8088E7A8; + } +} + +void func_8088E7A8(BgHidanSima* this, GlobalContext* globalCtx) { + f32 temp; + + if (this->timer != 0) { + this->timer--; + } + if (this->dyna.actor.world.rot.y != this->dyna.actor.home.rot.y) { + temp = (sinf(((60 - this->timer) * 0.01667 - 0.5) * M_PI) + 1) * 200; + } else { + temp = (sinf((this->timer * 0.01667 - 0.5) * M_PI) + 1) * -200; + } + this->dyna.actor.world.pos.x = Math_SinS(this->dyna.actor.world.rot.y) * temp + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = Math_CosS(this->dyna.actor.world.rot.y) * temp + this->dyna.actor.home.pos.z; + if (this->timer == 0) { + this->timer = 20; + this->actionFunc = func_8088E760; + } + func_8002F974(&this->dyna.actor, NA_SE_EV_FIRE_PILLAR - SFX_FLAG); +} + +void func_8088E90C(BgHidanSima* this) { + ColliderJntSphElement* elem; + s32 i; + f32 cos = Math_CosS(this->dyna.actor.world.rot.y + 0x8000); + f32 sin = Math_SinS(this->dyna.actor.world.rot.y + 0x8000); + + for (i = 0; i < 2; i++) { + elem = &this->collider.elements[i]; + elem->dim.worldSphere.center.x = this->dyna.actor.world.pos.x + sin * elem->dim.modelSphere.center.z; + elem->dim.worldSphere.center.y = (s16)this->dyna.actor.world.pos.y + elem->dim.modelSphere.center.y; + elem->dim.worldSphere.center.z = this->dyna.actor.world.pos.z + cos * elem->dim.modelSphere.center.z; + } +} + +void BgHidanSima_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanSima* this = (BgHidanSima*)thisx; + s32 pad; + + this->actionFunc(this, globalCtx); + if (this->dyna.actor.params != 0) { + s32 temp = (this->dyna.actor.world.rot.y == this->dyna.actor.shape.rot.y) ? this->timer : (this->timer + 80); + + if (this->actionFunc == func_8088E7A8) { + temp += 20; + } + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - ((1.0f - cosf(temp * (M_PI / 20))) * 5.0f); + if (this->actionFunc == func_8088E7A8) { + func_8088E90C(this); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +Gfx* func_8088EB54(GlobalContext* globalCtx, BgHidanSima* this, Gfx* gfx) { + MtxF mtxF; + s32 phi_s5; + s32 s3; + s32 v0; + f32 cos; + f32 sin; + s32 pad[2]; + + Matrix_MtxFCopy(&mtxF, &gMtxFClear); + cos = Math_CosS(this->dyna.actor.world.rot.y + 0x8000); + sin = Math_SinS(this->dyna.actor.world.rot.y + 0x8000); + + phi_s5 = (60 - this->timer) >> 1; + phi_s5 = CLAMP_MAX(phi_s5, 3); + + v0 = 3 - (this->timer >> 1); + v0 = CLAMP_MIN(v0, 0); + + mtxF.xw = this->dyna.actor.world.pos.x + ((79 - ((this->timer % 6) * 4)) + v0 * 25) * sin; + mtxF.zw = this->dyna.actor.world.pos.z + ((79 - ((this->timer % 6) * 4)) + v0 * 25) * cos; + mtxF.yw = this->dyna.actor.world.pos.y + 40.0f; + mtxF.zz = v0 * 0.4f + 1.0f; + mtxF.yy = v0 * 0.4f + 1.0f; + mtxF.xx = v0 * 0.4f + 1.0f; + + for (s3 = v0; s3 < phi_s5; s3++) { + mtxF.xw += 25.0f * sin; + mtxF.zw += 25.0f * cos; + mtxF.xx += 0.4f; + mtxF.yy += 0.4f; + mtxF.zz += 0.4f; + + gSPSegment(gfx++, 0x09, SEGMENTED_TO_VIRTUAL(sFireballsTexs[(this->timer + s3) % 7])); + gSPMatrix(gfx++, + Matrix_MtxFToMtx(Matrix_CheckFloats(&mtxF, "../z_bg_hidan_sima.c", 611), + Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx))), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, gFireTempleFireballDL); + } + mtxF.xw = this->dyna.actor.world.pos.x + (phi_s5 * 25 + 80) * sin; + mtxF.zw = this->dyna.actor.world.pos.z + (phi_s5 * 25 + 80) * cos; + gSPSegment(gfx++, 0x09, SEGMENTED_TO_VIRTUAL(sFireballsTexs[(this->timer + s3) % 7])); + gSPMatrix(gfx++, + Matrix_MtxFToMtx(Matrix_CheckFloats(&mtxF, "../z_bg_hidan_sima.c", 624), + Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx))), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(gfx++, gFireTempleFireballDL); + return gfx; +} + +void BgHidanSima_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgHidanSima* this = (BgHidanSima*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_sima.c", 641); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_hidan_sima.c", 645), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->dyna.actor.params == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFireTempleStonePlatform1DL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gFireTempleStonePlatform2DL); + if (this->actionFunc == func_8088E7A8) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 1, 255, 255, 0, 150); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 255); + POLY_XLU_DISP = func_8088EB54(globalCtx, this, POLY_XLU_DISP); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_hidan_sima.c", 668); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Sima/z_bg_hidan_sima.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Sima/z_bg_hidan_sima.h new file mode 100644 index 000000000..f31999c3b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Sima/z_bg_hidan_sima.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_HIDAN_SIMA_H +#define Z_BG_HIDAN_SIMA_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanSima; + +typedef void (*BgHidanSimaActionFunc)(struct BgHidanSima*, GlobalContext*); + +typedef struct BgHidanSima { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanSimaActionFunc actionFunc; + /* 0x0168 */ s16 timer; + /* 0x016C */ ColliderJntSph collider; + /* 0x018C */ ColliderJntSphElement elements[2]; +} BgHidanSima; // size = 0x020C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Syoku/z_bg_hidan_syoku.c b/soh/src/overlays/actors/ovl_Bg_Hidan_Syoku/z_bg_hidan_syoku.c new file mode 100644 index 000000000..1d494690a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Syoku/z_bg_hidan_syoku.c @@ -0,0 +1,127 @@ +/* + * File: z_bg_hidan_syoku.c + * Overlay: ovl_Bg_Hidan_Syoku + * Description: Stone Elevator in the Fire Temple + */ + +#include "z_bg_hidan_syoku.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgHidanSyoku_Init(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSyoku_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSyoku_Update(Actor* thisx, GlobalContext* globalCtx); +void BgHidanSyoku_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8088F4B8(BgHidanSyoku* this, GlobalContext* globalCtx); +void func_8088F514(BgHidanSyoku* this, GlobalContext* globalCtx); +void func_8088F62C(BgHidanSyoku* this, GlobalContext* globalCtx); + +const ActorInit Bg_Hidan_Syoku_InitVars = { + ACTOR_BG_HIDAN_SYOKU, + ACTORCAT_BG, + FLAGS, + OBJECT_HIDAN_OBJECTS, + sizeof(BgHidanSyoku), + (ActorFunc)BgHidanSyoku_Init, + (ActorFunc)BgHidanSyoku_Destroy, + (ActorFunc)BgHidanSyoku_Update, + (ActorFunc)BgHidanSyoku_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgHidanSyoku_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgHidanSyoku* this = (BgHidanSyoku*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gFireTempleFlareDancerPlatformCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->actionFunc = func_8088F4B8; + this->dyna.actor.home.pos.y += 540.0f; +} + +void BgHidanSyoku_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgHidanSyoku* this = (BgHidanSyoku*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8088F47C(BgHidanSyoku* this) { + this->timer = 60; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + this->actionFunc = func_8088F62C; +} + +void func_8088F4B8(BgHidanSyoku* this, GlobalContext* globalCtx) { + if (Flags_GetClear(globalCtx, this->dyna.actor.room) && func_8004356C(&this->dyna)) { + this->timer = 140; + this->actionFunc = func_8088F514; + } +} + +void func_8088F514(BgHidanSyoku* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + this->dyna.actor.world.pos.y = (cosf(this->timer * (M_PI / 140)) * 540.0f) + this->dyna.actor.home.pos.y; + if (this->timer == 0) { + func_8088F47C(this); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE3 - SFX_FLAG); + } +} + +void func_8088F5A0(BgHidanSyoku* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - (cosf(this->timer * (M_PI / 140)) * 540.0f); + if (this->timer == 0) { + func_8088F47C(this); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE3 - SFX_FLAG); + } +} + +void func_8088F62C(BgHidanSyoku* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + this->timer = 140; + if (this->dyna.actor.world.pos.y < this->dyna.actor.home.pos.y) { + this->actionFunc = func_8088F514; + } else { + this->actionFunc = func_8088F5A0; + } + } +} + +void BgHidanSyoku_Update(Actor* thisx, GlobalContext* globalCtx) { + BgHidanSyoku* this = (BgHidanSyoku*)thisx; + + this->actionFunc(this, globalCtx); + if (func_8004356C(&this->dyna)) { + if (this->unk_168 == 0) { + this->unk_168 = 3; + } + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_FIRE_PLATFORM); + } else if (!func_8004356C(&this->dyna)) { + if (this->unk_168 != 0) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + this->unk_168 = 0; + } +} + +void BgHidanSyoku_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gFireTempleFlareDancerPlatformDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Hidan_Syoku/z_bg_hidan_syoku.h b/soh/src/overlays/actors/ovl_Bg_Hidan_Syoku/z_bg_hidan_syoku.h new file mode 100644 index 000000000..9147c8c76 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Hidan_Syoku/z_bg_hidan_syoku.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_HIDAN_SYOKU_H +#define Z_BG_HIDAN_SYOKU_H + +#include "ultra64.h" +#include "global.h" + +struct BgHidanSyoku; + +typedef void (*BgHidanSyokuActionFunc)(struct BgHidanSyoku*, GlobalContext*); + +typedef struct BgHidanSyoku { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgHidanSyokuActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ s16 timer; +} BgHidanSyoku; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Objects/z_bg_ice_objects.c b/soh/src/overlays/actors/ovl_Bg_Ice_Objects/z_bg_ice_objects.c new file mode 100644 index 000000000..b49813245 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Objects/z_bg_ice_objects.c @@ -0,0 +1,238 @@ +/* + * File: z_bg_ice_objects.c + * Overlay: ovl_Bg_Ice_Objects + * Description: Pushable ice block (Ice Cavern) + */ + +#include "z_bg_ice_objects.h" +#include "objects/object_ice_objects/object_ice_objects.h" + +#define FLAGS 0 + +void BgIceObjects_Init(Actor* thisx, GlobalContext* globalCtx); +void BgIceObjects_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgIceObjects_Update(Actor* thisx, GlobalContext* globalCtx); +void BgIceObjects_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgIceObjects_Idle(BgIceObjects* this, GlobalContext* globalCtx); +void BgIceObjects_Slide(BgIceObjects* this, GlobalContext* globalCtx); +void BgIceObjects_Reset(BgIceObjects* this, GlobalContext* globalCtx); +void BgIceObjects_Stuck(BgIceObjects* this, GlobalContext* globalCtx); + +static Color_RGBA8 sWhite = { 250, 250, 250, 255 }; +static Color_RGBA8 sGray = { 180, 180, 180, 255 }; +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +const ActorInit Bg_Ice_Objects_InitVars = { + ACTOR_BG_ICE_OBJECTS, + ACTORCAT_PROP, + FLAGS, + OBJECT_ICE_OBJECTS, + sizeof(BgIceObjects), + (ActorFunc)BgIceObjects_Init, + (ActorFunc)BgIceObjects_Destroy, + (ActorFunc)BgIceObjects_Update, + (ActorFunc)BgIceObjects_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgIceObjects_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgIceObjects* this = (BgIceObjects*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&object_ice_objects_Col_0003F0, &colHeader); + Math_Vec3f_Copy(&this->targetPos, &this->dyna.actor.home.pos); + this->actionFunc = BgIceObjects_Idle; + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->dyna.actor.params = 0; +} + +void BgIceObjects_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgIceObjects* this = (BgIceObjects*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +static s16 sXStarts[] = { + -1060, -1200, -1240, -1387, -1580, -1680, -1780, +}; +static s16 sZStarts[] = { + -580, -660, -780, -820, -860, -900, -1087, +}; +static s16 sZStops[7][2] = { + { -580, -1087 }, { -780, -1260 }, { -340, -820 }, { -260, -1260 }, { -340, -860 }, { -660, -1260 }, { -340, -740 }, +}; +static s16 sXStops[7][2] = { + { -860, -1580 }, { -1240, -1780 }, { -860, -1680 }, { -860, -1680 }, + { -1387, -1680 }, { -860, -1200 }, { -860, -1800 }, +}; + +/* + * Checks which of the eight possible x and z positions the block is at, + * defaulting to the maximum x wall or minimum z wall. Each x and z position + * has only one possible wall or pit on each side of it. + */ +void BgIceObjects_SetNextTarget(BgIceObjects* this, GlobalContext* globalCtx) { + s16 x16; + s16 z16 = 0; // needed to match + s32 i; + + if ((this->dyna.unk_158 == 0) || (this->dyna.unk_158 == -0x8000)) { + x16 = this->dyna.actor.world.pos.x; + for (i = 0; i < 7; i++) { + if (x16 == sXStarts[i]) { + z16 = (this->dyna.unk_158 == 0) ? sZStops[i][0] : sZStops[i][1]; + this->targetPos.z = z16; + return; + } + } + this->targetPos.z = (this->dyna.unk_158 == 0) ? -340 : -1260; + } else { + z16 = this->dyna.actor.world.pos.z; + for (i = 0; i < 7; i++) { + if (z16 == sZStarts[i]) { + x16 = (this->dyna.unk_158 == 0x4000) ? sXStops[i][0] : sXStops[i][1]; + this->targetPos.x = x16; + return; + } + } + this->targetPos.x = (this->dyna.unk_158 == 0x4000) ? -860 : -1780; + } +} + +/* + * Checks if the block has fallen into any of the pits. + */ +void BgIceObjects_CheckPits(BgIceObjects* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if ((thisx->velocity.y > 0.0f) || ((thisx->world.pos.x <= -1660.0f) && (thisx->world.pos.z <= -1060.0f)) || + ((thisx->world.pos.x <= -1580.0f) && (thisx->world.pos.z >= -420.0f)) || + ((thisx->world.pos.x >= -980.0f) && (thisx->world.pos.z <= -1180.0f)) || + ((thisx->world.pos.x >= -860.0f) && (thisx->world.pos.z >= -700.0f))) { + + thisx->velocity.y += 1.0f; + if (Math_StepToF(&thisx->world.pos.y, -300.0f, thisx->velocity.y)) { + thisx->velocity.y = 0.0f; + thisx->world.pos.x = thisx->home.pos.x; + thisx->world.pos.y = thisx->home.pos.y - 60.0f; + thisx->world.pos.z = thisx->home.pos.z; + if (thisx->params != 0) { + func_8002DF54(globalCtx, thisx, 7); + } + this->actionFunc = BgIceObjects_Reset; + } + } +} + +void BgIceObjects_Idle(BgIceObjects* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* thisx = &this->dyna.actor; + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + if ((this->dyna.unk_150 > 0.0f) && !Player_InCsMode(globalCtx)) { + BgIceObjects_SetNextTarget(this, globalCtx); + if (Actor_WorldDistXZToPoint(thisx, &this->targetPos) > 1.0f) { + thisx->flags |= ACTOR_FLAG_4; + func_8002DF54(globalCtx, thisx, 8); + thisx->params = 1; + this->actionFunc = BgIceObjects_Slide; + } + } + this->dyna.unk_150 = 0.0f; + } + if (thisx->velocity.y > 0.0f) { + BgIceObjects_CheckPits(this, globalCtx); + } +} + +void BgIceObjects_Slide(BgIceObjects* this, GlobalContext* globalCtx) { + s32 atTarget; + Vec3f pos; + Vec3f velocity; + f32 spread; + Actor* thisx = &this->dyna.actor; + + Math_StepToF(&thisx->speedXZ, 10.0f, 0.5f); + atTarget = Math_StepToF(&thisx->world.pos.x, this->targetPos.x, thisx->speedXZ); + atTarget &= Math_StepToF(&thisx->world.pos.z, this->targetPos.z, thisx->speedXZ); + if (atTarget) { + thisx->speedXZ = 0.0f; + this->targetPos.x = thisx->world.pos.x; + this->targetPos.z = thisx->world.pos.z; + if (thisx->velocity.y <= 0.0f) { + thisx->flags &= ~ACTOR_FLAG_4; + } + thisx->params = 0; + func_8002DF54(globalCtx, thisx, 7); + Audio_PlayActorSound2(thisx, NA_SE_EV_BLOCK_BOUND); + if ((fabsf(thisx->world.pos.x + 1387.0f) < 1.0f) && (fabsf(thisx->world.pos.z + 260.0f) < 1.0f)) { + this->actionFunc = BgIceObjects_Stuck; + } else { + this->actionFunc = BgIceObjects_Idle; + } + } else if ((thisx->speedXZ > 6.0f) && (thisx->world.pos.y >= 0.0f)) { + spread = Rand_CenteredFloat(120.0f); + velocity.x = -(1.5f + Rand_ZeroOne()) * Math_SinS(this->dyna.unk_158); + velocity.y = Rand_ZeroOne() + 1.0f; + velocity.z = -(1.5f + Rand_ZeroOne()) * Math_CosS(this->dyna.unk_158); + pos.x = thisx->world.pos.x - (60.0f * Math_SinS(this->dyna.unk_158)) - (Math_CosS(this->dyna.unk_158) * spread); + pos.z = thisx->world.pos.z - (60.0f * Math_CosS(this->dyna.unk_158)) + (Math_SinS(this->dyna.unk_158) * spread); + pos.y = thisx->world.pos.y; + func_8002829C(globalCtx, &pos, &velocity, &sZeroVec, &sWhite, &sGray, 250, Rand_S16Offset(40, 15)); + spread = Rand_CenteredFloat(120.0f); + pos.x = thisx->world.pos.x - (60.0f * Math_SinS(this->dyna.unk_158)) + (Math_CosS(this->dyna.unk_158) * spread); + pos.z = thisx->world.pos.z - (60.0f * Math_CosS(this->dyna.unk_158)) - (Math_SinS(this->dyna.unk_158) * spread); + func_8002829C(globalCtx, &pos, &velocity, &sZeroVec, &sWhite, &sGray, 250, Rand_S16Offset(40, 15)); + func_8002F974(thisx, NA_SE_PL_SLIP_ICE_LEVEL - SFX_FLAG); + } + BgIceObjects_CheckPits(this, globalCtx); +} + +void BgIceObjects_Reset(BgIceObjects* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* thisx = &this->dyna.actor; + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + if (Math_StepToF(&thisx->world.pos.y, thisx->home.pos.y, 1.0f)) { + thisx->flags &= ~ACTOR_FLAG_4; + Math_Vec3f_Copy(&this->targetPos, &thisx->home.pos); + this->actionFunc = BgIceObjects_Idle; + thisx->speedXZ = 0.0f; + } +} + +void BgIceObjects_Stuck(BgIceObjects* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } +} + +void BgIceObjects_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgIceObjects* this = (BgIceObjects*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgIceObjects_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgIceObjects* this = (BgIceObjects*)thisx; + + Gfx_DrawDListOpa(globalCtx, object_ice_objects_DL_000190); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Objects/z_bg_ice_objects.h b/soh/src/overlays/actors/ovl_Bg_Ice_Objects/z_bg_ice_objects.h new file mode 100644 index 000000000..ef9872823 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Objects/z_bg_ice_objects.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_ICE_OBJECTS_H +#define Z_BG_ICE_OBJECTS_H + +#include "ultra64.h" +#include "global.h" + +struct BgIceObjects; + +typedef void (*BgIceObjectsActionFunc) (struct BgIceObjects*, GlobalContext*); + +typedef struct BgIceObjects { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgIceObjectsActionFunc actionFunc; + /* 0x0168 */ Vec3f targetPos; +} BgIceObjects; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c new file mode 100644 index 000000000..dcaad6ef8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c @@ -0,0 +1,439 @@ +#include "z_bg_ice_shelter.h" +#include "objects/object_ice_objects/object_ice_objects.h" + +#define FLAGS 0 + +void BgIceShelter_Init(Actor* thisx, GlobalContext* globalCtx); +void BgIceShelter_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgIceShelter_Update(Actor* thisx, GlobalContext* globalCtx); +void BgIceShelter_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80891064(BgIceShelter* this); +void func_808911BC(BgIceShelter* this); + +void func_8089107C(BgIceShelter* this, GlobalContext* globalCtx); +void func_808911D4(BgIceShelter* this, GlobalContext* globalCtx); + +const ActorInit Bg_Ice_Shelter_InitVars = { + ACTOR_BG_ICE_SHELTER, + ACTORCAT_BG, + FLAGS, + OBJECT_ICE_OBJECTS, + sizeof(BgIceShelter), + (ActorFunc)BgIceShelter_Init, + (ActorFunc)BgIceShelter_Destroy, + (ActorFunc)BgIceShelter_Update, + (ActorFunc)BgIceShelter_Draw, + NULL, +}; + +static f32 sScales[] = { 0.1f, 0.06f, 0.1f, 0.1f, 0.25f }; + +static Color_RGBA8 sDustPrimColor = { 250, 250, 250, 255 }; +static Color_RGBA8 sDustEnvColor = { 180, 180, 180, 255 }; + +static ColliderCylinderInit D_8089170C = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_OTHER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, 0, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit D_80891738 = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC1FFF6, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, 0, 0, { 0, 0, 0 } }, +}; + +void func_80890740(BgIceShelter* this, GlobalContext* globalCtx) { + static s16 cylinderRadii[] = { 47, 33, 44, 41, 100 }; + static s16 cylinderHeights[] = { 80, 54, 90, 60, 200 }; + s32 pad; + s32 type = (this->dyna.actor.params >> 8) & 7; + + Collider_InitCylinder(globalCtx, &this->cylinder1); + Collider_SetCylinder(globalCtx, &this->cylinder1, &this->dyna.actor, &D_8089170C); + Collider_UpdateCylinder(&this->dyna.actor, &this->cylinder1); + + this->cylinder1.dim.radius = cylinderRadii[type]; + this->cylinder1.dim.height = cylinderHeights[type]; + + if (type == 0 || type == 1 || type == 4) { + Collider_InitCylinder(globalCtx, &this->cylinder2); + Collider_SetCylinder(globalCtx, &this->cylinder2, &this->dyna.actor, &D_80891738); + Collider_UpdateCylinder(&this->dyna.actor, &this->cylinder2); + this->cylinder2.dim.radius = cylinderRadii[type]; + this->cylinder2.dim.height = cylinderHeights[type]; + } + + if (type == 4) { + this->cylinder1.dim.pos.z += 30; + this->cylinder2.dim.pos.z += 30; + } +} + +void func_80890874(BgIceShelter* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG registration failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_ice_shelter.c", 362, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void func_808908FC(Vec3f* dest, Vec3f* src, s16 angle) { + f32 sin = Math_SinS(angle); + f32 cos = Math_CosS(angle); + + dest->x = (src->z * sin) + (src->x * cos); + dest->y = src->y; + dest->z = (src->z * cos) - (src->x * sin); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgIceShelter_Init(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f kzIceScale = { 0.18f, 0.27f, 0.24f }; + BgIceShelter* this = (BgIceShelter*)thisx; + s16 type = (this->dyna.actor.params >> 8) & 7; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + if (type == 4) { + this->dyna.actor.world.rot.x += 0xBB8; + this->dyna.actor.world.pos.y -= 45.0f; + this->dyna.actor.shape.rot.x = this->dyna.actor.world.rot.x; + this->dyna.actor.world.pos.z -= 38.0f; + } + + if (type == 4) { + Math_Vec3f_Copy(&this->dyna.actor.scale, &kzIceScale); + } else { + Actor_SetScale(&this->dyna.actor, sScales[type]); + } + + switch (type) { + case 2: + func_80890874(this, globalCtx, &object_ice_objects_Col_001C1C, 0); + break; + case 3: + func_80890874(this, globalCtx, &object_ice_objects_Col_002920, 0); + break; + } + + func_80890740(this, globalCtx); + + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; + + if (!((this->dyna.actor.params >> 6) & 1) && (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F))) { + Actor_Kill(&this->dyna.actor); + return; + } + + func_80891064(this); + + osSyncPrintf("(ice shelter)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgIceShelter_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgIceShelter* this = (BgIceShelter*)thisx; + + switch ((this->dyna.actor.params >> 8) & 7) { + case 2: + case 3: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + break; + + case 0: + case 1: + case 4: + Collider_DestroyCylinder(globalCtx, &this->cylinder2); + break; + } + + Collider_DestroyCylinder(globalCtx, &this->cylinder1); +} + +static s16 D_80891794[] = { 0x0000, 0x4000, 0x2000, 0x6000, 0x1000, 0x5000, 0x3000, 0x7000 }; +static s16 D_808917A4[] = { 0x0000, 0x003C, 0x0018, 0x0054, 0x0030, 0x000C, 0x0048, 0x0024 }; + +void func_80890B8C(BgIceShelter* this, GlobalContext* globalCtx, f32 chance, f32 scale) { + f32 cos; + f32 sin; + f32 xzOffset; + Vec3f* icePos; + s16 angle; + s16 frames; + s32 i; + s32 pad[2]; + Vec3f dustPos; + Vec3f dustVel; + Vec3f dustAccel; + + frames = (s16)globalCtx->state.frames & 7; + + for (i = 0; i < 2; i++) { + if (chance < Rand_ZeroOne()) { + continue; + } + + xzOffset = 42.0f * scale; + icePos = &this->dyna.actor.world.pos; + angle = D_80891794[frames] + (i * 0x8000); + sin = Math_SinS(angle); + cos = Math_CosS(angle); + + dustPos.x = (xzOffset * sin) + icePos->x; + dustPos.y = (16.0f * scale) + icePos->y; + dustPos.z = (xzOffset * cos) + icePos->z; + + dustVel.x = ((Rand_ZeroOne() * 3.0f) - 1.0f) * sin; + dustVel.y = 0.0f; + dustVel.z = ((Rand_ZeroOne() * 3.0f) - 1.0f) * cos; + + dustAccel.x = 0.07f * sin; + dustAccel.y = 0.8f; + dustAccel.z = 0.07f * cos; + + func_8002829C(globalCtx, &dustPos, &dustVel, &dustAccel, &sDustPrimColor, &sDustEnvColor, 450.0f * scale, + (s16)((Rand_ZeroOne() * 40.0f) + 40.0f) * scale); + } +} + +void func_80890E00(BgIceShelter* this, GlobalContext* globalCtx, f32 chance, f32 arg3) { + static f32 D_808917B4[] = { -1.0f, 1.0f }; + Vec3f* icePos; + s16 frames; + s32 pad[2]; + Vec3f dustPos; + Vec3f dustVel; + Vec3f dustAccel; + Vec3f posOffset; + s32 i; + + frames = (s16)globalCtx->state.frames & 7; + + for (i = 0; i < 2; i++) { + icePos = &this->dyna.actor.world.pos; + + if (chance < Rand_ZeroOne()) { + continue; + } + + posOffset.x = (D_808917A4[frames] + ((Rand_ZeroOne() * 12.0f) - 6.0f)) * D_808917B4[i]; + posOffset.y = 15.0f; + posOffset.z = ((84.0f - posOffset.x) * 0.2f) + (Rand_ZeroOne() * 20.0f); + + func_808908FC(&dustPos, &posOffset, this->dyna.actor.world.rot.y); + Math_Vec3f_Sum(&dustPos, icePos, &dustPos); + + dustVel.x = (Rand_ZeroOne() * 3.0f) - 1.5f; + dustVel.y = 0.0f; + dustVel.z = (Rand_ZeroOne() * 3.0f) - 1.5f; + + dustAccel.x = (Rand_ZeroOne() * 0.14f) - 0.07f; + dustAccel.y = 0.8f; + dustAccel.z = (Rand_ZeroOne() * 0.14f) - 0.07f; + + func_8002829C(globalCtx, &dustPos, &dustVel, &dustAccel, &sDustPrimColor, &sDustEnvColor, 450, + (Rand_ZeroOne() * 40.0f) + 40.0f); + } +} + +void func_80891064(BgIceShelter* this) { + this->actionFunc = func_8089107C; + this->alpha = 255; +} + +void func_8089107C(BgIceShelter* this, GlobalContext* globalCtx) { + s32 pad; + s16 type = (this->dyna.actor.params >> 8) & 7; + + if (type == 4) { + if (this->dyna.actor.parent != NULL) { + this->dyna.actor.parent->freezeTimer = 10000; + } + } + + if (this->cylinder1.base.acFlags & AC_HIT) { + this->cylinder1.base.acFlags &= ~AC_HIT; + + if ((this->cylinder1.base.ac != NULL) && (this->cylinder1.base.ac->id == ACTOR_EN_ICE_HONO)) { + if (type == 4) { + if (this->dyna.actor.parent != NULL) { + this->dyna.actor.parent->freezeTimer = 50; + } + } + + func_808911BC(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ICE_MELT); + } + } + + switch (type) { + case 0: + case 1: + case 4: + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cylinder1.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder2.base); + break; + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder1.base); +} + +void func_808911BC(BgIceShelter* this) { + this->actionFunc = func_808911D4; + this->alpha = 255; +} + +static f32 D_808917BC[] = { -0.0015f, -0.0009f, -0.0016f, -0.0016f, -0.00375f }; +static f32 D_808917D0[] = { 1.0f, 0.6f, 1.2f, 1.0f, 1.8f }; + +static void (*sEffSpawnFuncs[])(BgIceShelter* this, GlobalContext* globalCtx, f32 chance, f32 scale) = { + func_80890B8C, func_80890B8C, func_80890B8C, func_80890E00, func_80890B8C, +}; + +void func_808911D4(BgIceShelter* this, GlobalContext* globalCtx) { + + s32 pad; + s32 type = (this->dyna.actor.params >> 8) & 7; + f32 phi_f0; + + this->alpha -= 5; + this->alpha = CLAMP(this->alpha, 0, 255); + + this->dyna.actor.scale.y += D_808917BC[type]; + this->dyna.actor.scale.y = CLAMP_MIN(this->dyna.actor.scale.y, 0.0001f); + + if (this->alpha > 80) { + switch (type) { + case 0: + case 1: + case 4: + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cylinder1.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder2.base); + break; + } + } + + if (this->alpha > 180) { + phi_f0 = 1.0f; + } else if (this->alpha > 60) { + phi_f0 = 0.5f; + } else { + phi_f0 = 0.0f; + } + + sEffSpawnFuncs[type](this, globalCtx, phi_f0, D_808917D0[type]); + + if (this->alpha <= 0) { + if (!((this->dyna.actor.params >> 6) & 1)) { + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + } + + if (type == 4) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + + Actor_Kill(&this->dyna.actor); + } +} + +void BgIceShelter_Update(Actor* thisx, GlobalContext* globalCtx) { + BgIceShelter* this = (BgIceShelter*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgIceShelter_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgIceShelter* this = (BgIceShelter*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_ice_shelter.c", 748); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ice_shelter.c", 751), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + switch ((this->dyna.actor.params >> 8) & 7) { + case 0: + case 1: + case 2: + case 4: + func_8002ED80(&this->dyna.actor, globalCtx, 0); + break; + } + + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, this->alpha); + + switch ((this->dyna.actor.params >> 8) & 7) { + case 0: + case 1: + case 4: + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -globalCtx->gameplayFrames & 0x7F, + -globalCtx->gameplayFrames & 0x7F, 0x20, 0x20, 1, + -globalCtx->gameplayFrames & 0x7F, globalCtx->gameplayFrames & 0x7F, 0x20, + 0x20)); + gSPDisplayList(POLY_XLU_DISP++, object_ice_objects_DL_0006F0); + break; + + case 2: + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, globalCtx->gameplayFrames & 0xFF, 0x40, 0x40, 1, + 0, -globalCtx->gameplayFrames & 0xFF, 0x40, 0x40)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -globalCtx->gameplayFrames & 0xFF, + globalCtx->gameplayFrames & 0xFF, 0x40, 0x40, 1, + globalCtx->gameplayFrames & 0xFF, globalCtx->gameplayFrames & 0xFF, 0x40, + 0x40)); + gSPDisplayList(POLY_XLU_DISP++, object_ice_objects_DL_0012A0); + break; + + case 3: + gSPDisplayList(POLY_XLU_DISP++, object_ice_objects_DL_002640); + break; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_ice_shelter.c", 815); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h new file mode 100644 index 000000000..6f67f274e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_ICE_SHELTER_H +#define Z_BG_ICE_SHELTER_H + +#include "ultra64.h" +#include "global.h" + +struct BgIceShelter; + +typedef void (*BgIceShelterActionFunc)(struct BgIceShelter*, GlobalContext*); + +typedef struct BgIceShelter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgIceShelterActionFunc actionFunc; + /* 0x0168 */ ColliderCylinder cylinder1; + /* 0x01B4 */ ColliderCylinder cylinder2; + /* 0x0200 */ s16 alpha; +} BgIceShelter; // size = 0x0204 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shutter/z_bg_ice_shutter.c b/soh/src/overlays/actors/ovl_Bg_Ice_Shutter/z_bg_ice_shutter.c new file mode 100644 index 000000000..7e49925b3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shutter/z_bg_ice_shutter.c @@ -0,0 +1,135 @@ +/* + * File: z_bg_ice_shutter.c + * Overlay: ovl_Bg_Ice_Shutter + * Description: Vertical Ice Bars (Doors) in Ice Cavern + */ + +#include "z_bg_ice_shutter.h" +#include "objects/object_ice_objects/object_ice_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgIceShutter_Init(Actor* thisx, GlobalContext* globalCtx); +void BgIceShutter_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgIceShutter_Update(Actor* thisx, GlobalContext* globalCtx); +void BgIceShutter_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80891CF4(BgIceShutter* thisx, GlobalContext* globalCtx); +void func_80891D6C(BgIceShutter* thisx, GlobalContext* globalCtx); +void func_80891DD4(BgIceShutter* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Ice_Shutter_InitVars = { + ACTOR_BG_ICE_SHUTTER, + ACTORCAT_PROP, + FLAGS, + OBJECT_ICE_OBJECTS, + sizeof(BgIceShutter), + (ActorFunc)BgIceShutter_Init, + (ActorFunc)BgIceShutter_Destroy, + (ActorFunc)BgIceShutter_Update, + (ActorFunc)BgIceShutter_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void func_80891AC0(BgIceShutter* this) { + f32 sp24; + + sp24 = Math_SinS(this->dyna.actor.shape.rot.x) * this->dyna.actor.velocity.y; + this->dyna.actor.world.pos.y = + (Math_CosS(this->dyna.actor.shape.rot.x) * this->dyna.actor.velocity.y) + this->dyna.actor.home.pos.y; + this->dyna.actor.world.pos.x = (Math_SinS(this->dyna.actor.shape.rot.y) * sp24) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->dyna.actor.shape.rot.y) * sp24) + this->dyna.actor.home.pos.z; +} + +void BgIceShutter_Init(Actor* thisx, GlobalContext* globalCtx) { + BgIceShutter* this = (BgIceShutter*)thisx; + f32 sp24; + CollisionHeader* colHeader; + s32 sp28; + f32 temp_f6; + + colHeader = NULL; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + sp28 = this->dyna.actor.params & 0xFF; + this->dyna.actor.params = (this->dyna.actor.params >> 8) & 0xFF; + CollisionHeader_GetVirtual(&object_ice_objects_Col_002854, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (sp28 == 2) { + this->dyna.actor.shape.rot.x = -0x4000; + } + + if (sp28 != 1) { + if (Flags_GetClear(globalCtx, this->dyna.actor.room)) { + Actor_Kill(&this->dyna.actor); + } else { + this->actionFunc = func_80891CF4; + } + + } else { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params)) { + Actor_Kill(&this->dyna.actor); + } else { + this->actionFunc = func_80891D6C; + } + } + + if (sp28 == 2) { + temp_f6 = Math_SinS(this->dyna.actor.shape.rot.x) * 50.0f; + this->dyna.actor.focus.pos.x = + (Math_SinS(this->dyna.actor.shape.rot.y) * temp_f6) + this->dyna.actor.home.pos.x; + this->dyna.actor.focus.pos.y = this->dyna.actor.home.pos.y; + this->dyna.actor.focus.pos.z = + this->dyna.actor.home.pos.z + (Math_CosS(this->dyna.actor.shape.rot.y) * temp_f6); + } else { + Actor_SetFocus(&this->dyna.actor, 50.0f); + } +} + +void BgIceShutter_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgIceShutter* this = (BgIceShutter*)thisx; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80891CF4(BgIceShutter* this, GlobalContext* globalCtx) { + if (Flags_GetTempClear(globalCtx, this->dyna.actor.room)) { + Flags_SetClear(globalCtx, this->dyna.actor.room); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 30, NA_SE_EV_SLIDE_DOOR_OPEN); + this->actionFunc = func_80891DD4; + if (this->dyna.actor.shape.rot.x == 0) { + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + } + } +} + +void func_80891D6C(BgIceShutter* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params)) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 30, NA_SE_EV_SLIDE_DOOR_OPEN); + this->actionFunc = func_80891DD4; + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + } +} + +void func_80891DD4(BgIceShutter* this, GlobalContext* globalCtx) { + Math_StepToF(&this->dyna.actor.speedXZ, 30.0f, 2.0f); + if (Math_StepToF(&this->dyna.actor.velocity.y, 210.0f, this->dyna.actor.speedXZ)) { + Actor_Kill(&this->dyna.actor); + return; + } + + func_80891AC0(this); +} + +void BgIceShutter_Update(Actor* thisx, GlobalContext* globalCtx) { + BgIceShutter* this = (BgIceShutter*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgIceShutter_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, object_ice_objects_DL_002740); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shutter/z_bg_ice_shutter.h b/soh/src/overlays/actors/ovl_Bg_Ice_Shutter/z_bg_ice_shutter.h new file mode 100644 index 000000000..68ac83f10 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shutter/z_bg_ice_shutter.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_ICE_SHUTTER_H +#define Z_BG_ICE_SHUTTER_H + +#include "ultra64.h" +#include "global.h" + +struct BgIceShutter; + +typedef void (*BgIceShutterActionFunc)(struct BgIceShutter*, GlobalContext*); + +typedef struct BgIceShutter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgIceShutterActionFunc actionFunc; +} BgIceShutter; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c b/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c new file mode 100644 index 000000000..65a6ecc37 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c @@ -0,0 +1,202 @@ +/* + * File: z_bg_ice_turara.c + * Overlay: ovl_Bg_Ice_Turara + * Description: Icicles + */ + +#include "z_bg_ice_turara.h" +#include "objects/object_ice_objects/object_ice_objects.h" + +#define FLAGS 0 + +void BgIceTurara_Init(Actor* thisx, GlobalContext* globalCtx); +void BgIceTurara_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgIceTurara_Update(Actor* thisx, GlobalContext* globalCtx); +void BgIceTurara_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgIceTurara_Stalagmite(BgIceTurara* this, GlobalContext* globalCtx); +void BgIceTurara_Wait(BgIceTurara* this, GlobalContext* globalCtx); +void BgIceTurara_Shiver(BgIceTurara* this, GlobalContext* globalCtx); +void BgIceTurara_Fall(BgIceTurara* this, GlobalContext* globalCtx); +void BgIceTurara_Regrow(BgIceTurara* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x4FC007CA, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 13, 120, 0, { 0, 0, 0 } }, +}; + +const ActorInit Bg_Ice_Turara_InitVars = { + ACTOR_BG_ICE_TURARA, + ACTORCAT_PROP, + FLAGS, + OBJECT_ICE_OBJECTS, + sizeof(BgIceTurara), + (ActorFunc)BgIceTurara_Init, + (ActorFunc)BgIceTurara_Destroy, + (ActorFunc)BgIceTurara_Update, + (ActorFunc)BgIceTurara_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 600, ICHAIN_CONTINUE), + ICHAIN_F32(gravity, -3, ICHAIN_CONTINUE), + ICHAIN_F32(minVelocityY, -30, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgIceTurara_Init(Actor* thisx, GlobalContext* globalCtx) { + BgIceTurara* this = (BgIceTurara*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&object_ice_objects_Col_002594, &colHeader); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.actor.params == TURARA_STALAGMITE) { + this->actionFunc = BgIceTurara_Stalagmite; + } else { + this->dyna.actor.shape.rot.x = -0x8000; + this->dyna.actor.shape.yOffset = 1200.0f; + this->actionFunc = BgIceTurara_Wait; + } +} + +void BgIceTurara_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgIceTurara* this = (BgIceTurara*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void BgIceTurara_Break(BgIceTurara* this, GlobalContext* globalCtx, f32 arg2) { + static Vec3f accel = { 0.0f, -1.0f, 0.0f }; + static Color_RGBA8 primColor = { 170, 255, 255, 255 }; + static Color_RGBA8 envColor = { 0, 50, 100, 255 }; + Vec3f vel; + Vec3f pos; + s32 j; + s32 i; + + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 30, NA_SE_EV_ICE_BROKEN); + for (i = 0; i < 2; i++) { + for (j = 0; j < 10; j++) { + pos.x = this->dyna.actor.world.pos.x + Rand_CenteredFloat(8.0f); + pos.y = this->dyna.actor.world.pos.y + (Rand_ZeroOne() * arg2) + (i * arg2); + pos.z = this->dyna.actor.world.pos.z + Rand_CenteredFloat(8.0f); + + vel.x = Rand_CenteredFloat(7.0f); + vel.z = Rand_CenteredFloat(7.0f); + vel.y = (Rand_ZeroOne() * 4.0f) + 8.0f; + + EffectSsEnIce_Spawn(globalCtx, &pos, (Rand_ZeroOne() * 0.2f) + 0.1f, &vel, &accel, &primColor, &envColor, + 30); + } + } +} + +void BgIceTurara_Stalagmite(BgIceTurara* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + BgIceTurara_Break(this, globalCtx, 50.0f); + Actor_Kill(&this->dyna.actor); + return; + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void BgIceTurara_Wait(BgIceTurara* this, GlobalContext* globalCtx) { + if (this->dyna.actor.xzDistToPlayer < 60.0f) { + this->shiverTimer = 10; + this->actionFunc = BgIceTurara_Shiver; + } +} + +void BgIceTurara_Shiver(BgIceTurara* this, GlobalContext* globalCtx) { + s16 phi_v0_3; + s16 phi_v0_2; + f32 sp28; + + if (this->shiverTimer != 0) { + this->shiverTimer--; + } + if (!(this->shiverTimer % 4)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ICE_SWING); + } + if (this->shiverTimer == 0) { + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z; + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->actionFunc = BgIceTurara_Fall; + } else { + sp28 = Rand_ZeroOne(); + phi_v0_2 = (Rand_ZeroOne() < 0.5f ? -1 : 1); + this->dyna.actor.world.pos.x = (phi_v0_2 * ((0.5f * sp28) + 0.5f)) + this->dyna.actor.home.pos.x; + sp28 = Rand_ZeroOne(); + phi_v0_3 = (Rand_ZeroOne() < 0.5f ? -1 : 1); + this->dyna.actor.world.pos.z = (phi_v0_3 * ((0.5f * sp28) + 0.5f)) + this->dyna.actor.home.pos.z; + } +} + +void BgIceTurara_Fall(BgIceTurara* this, GlobalContext* globalCtx) { + if ((this->collider.base.atFlags & AT_HIT) || (this->dyna.actor.bgCheckFlags & 1)) { + this->collider.base.atFlags &= ~AT_HIT; + this->dyna.actor.bgCheckFlags &= ~1; + if (this->dyna.actor.world.pos.y < this->dyna.actor.floorHeight) { + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + } + BgIceTurara_Break(this, globalCtx, 40.0f); + if (this->dyna.actor.params == TURARA_STALACTITE_REGROW) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + 120.0f; + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->actionFunc = BgIceTurara_Regrow; + } else { + Actor_Kill(&this->dyna.actor); + return; + } + } else { + Actor_MoveForward(&this->dyna.actor); + this->dyna.actor.world.pos.y += 40.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 0.0f, 0.0f, 0.0f, 4); + this->dyna.actor.world.pos.y -= 40.0f; + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgIceTurara_Regrow(BgIceTurara* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 1.0f)) { + this->actionFunc = BgIceTurara_Wait; + this->dyna.actor.velocity.y = 0.0f; + } +} + +void BgIceTurara_Update(Actor* thisx, GlobalContext* globalCtx) { + BgIceTurara* this = (BgIceTurara*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgIceTurara_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, object_ice_objects_DL_0023D0); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.h b/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.h new file mode 100644 index 000000000..aff6417df --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.h @@ -0,0 +1,24 @@ +#ifndef Z_BG_ICE_TURARA_H +#define Z_BG_ICE_TURARA_H + +#include "ultra64.h" +#include "global.h" + +struct BgIceTurara; + +typedef void (*BgIceTuraraActionFunc)(struct BgIceTurara*, GlobalContext*); + +typedef enum { + /* 0 */ TURARA_STALAGMITE, + /* 1 */ TURARA_STALACTITE, + /* 2 */ TURARA_STALACTITE_REGROW +} BgIceTuraraType; + +typedef struct BgIceTurara { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgIceTuraraActionFunc actionFunc; + /* 0x0168 */ s16 shiverTimer; + /* 0x016C */ ColliderCylinder collider; +} BgIceTurara; // size = 0x01B8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ingate/z_bg_ingate.c b/soh/src/overlays/actors/ovl_Bg_Ingate/z_bg_ingate.c new file mode 100644 index 000000000..a3b8c3e33 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ingate/z_bg_ingate.c @@ -0,0 +1,117 @@ +/* + * File: z_bg_ingate.c + * Overlay: ovl_Bg_Ingate + * Description: Ingo's Gates (Lon Lon Ranch) + */ + +#include "z_bg_ingate.h" +#include "objects/object_ingate/object_ingate.h" + +#define FLAGS 0 + +void BgInGate_Init(Actor* thisx, GlobalContext* globalCtx); +void BgInGate_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgInGate_Update(Actor* thisx, GlobalContext* globalCtx); +void BgInGate_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80892890(BgInGate* this, GlobalContext* globalCtx); +void BgInGate_DoNothing(BgInGate* this, GlobalContext* globalCtx); + +const ActorInit Bg_Ingate_InitVars = { + ACTOR_BG_INGATE, + ACTORCAT_PROP, + FLAGS, + OBJECT_INGATE, + sizeof(BgInGate), + (ActorFunc)BgInGate_Init, + (ActorFunc)BgInGate_Destroy, + (ActorFunc)BgInGate_Update, + (ActorFunc)BgInGate_Draw, + NULL, +}; + +void BgInGate_SetupAction(BgInGate* this, BgInGateActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgInGate_Init(Actor* thisx, GlobalContext* globalCtx) { + BgInGate* this = (BgInGate*)thisx; + + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gIngoGateCol, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if ((globalCtx->sceneNum != SCENE_SPOT20 || !LINK_IS_ADULT) || + (((gSaveContext.eventChkInf[1] & 0x100)) && (gSaveContext.cutsceneIndex != 0xFFF0))) { + Actor_Kill(&this->dyna.actor); + return; + } + + Actor_SetScale(&this->dyna.actor, 0.1f); + if (((this->dyna.actor.params & 1) != 0) && ((gSaveContext.eventInf[0] & 0xF) == 6)) { + globalCtx->csCtx.frames = 0; + BgInGate_SetupAction(this, func_80892890); + } else { + BgInGate_SetupAction(this, BgInGate_DoNothing); + } +} + +void BgInGate_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgInGate* this = (BgInGate*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80892890(BgInGate* this, GlobalContext* globalCtx) { + s32 phi0; + s16 phi1; + s16 csFrames; + + if (globalCtx->csCtx.frames >= 50) { + phi0 = 0x4000; + if ((this->dyna.actor.params & 2) == 0) { + phi0 = -0x4000; + } + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y + phi0; + BgInGate_SetupAction(this, BgInGate_DoNothing); + } else if (globalCtx->csCtx.frames >= 10) { + csFrames = globalCtx->csCtx.frames - 10; + csFrames *= 400; + phi1 = csFrames; + if (csFrames > 0x4000) { + csFrames = 0x4000; + } + csFrames = (Math_SinS(csFrames) * 16384.0f); + phi1 = csFrames; + if ((this->dyna.actor.params & 2) == 0) { + phi1 = -phi1; + } + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y + phi1; + } +} + +void BgInGate_DoNothing(BgInGate* this, GlobalContext* globalCtx) { +} + +void BgInGate_Update(Actor* thisx, GlobalContext* globalCtx) { + BgInGate* this = (BgInGate*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgInGate_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_ingate.c", 240); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ingate.c", 245), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gIngoGateDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_ingate.c", 250); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ingate/z_bg_ingate.h b/soh/src/overlays/actors/ovl_Bg_Ingate/z_bg_ingate.h new file mode 100644 index 000000000..ba6e294d4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ingate/z_bg_ingate.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_INGATE_H +#define Z_BG_INGATE_H + +#include "ultra64.h" +#include "global.h" + +struct BgInGate; + +typedef void (*BgInGateActionFunc)(struct BgInGate*, GlobalContext*); + +typedef struct BgInGate { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgInGateActionFunc actionFunc; +} BgInGate; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_1flift/z_bg_jya_1flift.c b/soh/src/overlays/actors/ovl_Bg_Jya_1flift/z_bg_jya_1flift.c new file mode 100644 index 000000000..1d50e6c49 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_1flift/z_bg_jya_1flift.c @@ -0,0 +1,208 @@ +/* + * File: z_bg_jya_1flift.c + * Overlay: ovl_Bg_Jya_1flift + * Description: Shortcut Elevator used in the vanilla version of the Spirit Temple + */ + +#include "z_bg_jya_1flift.h" +#include "objects/object_jya_obj/object_jya_obj.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgJya1flift_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJya1flift_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJya1flift_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJya1flift_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJya1flift_SetupWaitForSwitch(BgJya1flift* this); +void BgJya1flift_WaitForSwitch(BgJya1flift* this, GlobalContext* globalCtx); +void BgJya1flift_DoNothing(BgJya1flift* this, GlobalContext* globalCtx); +void BgJya1flift_ChangeDirection(BgJya1flift* this); +void BgJya1flift_Move(BgJya1flift* this, GlobalContext* globalCtx); +void BgJya1flift_SetupDoNothing(BgJya1flift* this); +void BgJya1flift_ResetMoveDelay(BgJya1flift* this); +void BgJya1flift_DelayMove(BgJya1flift* this, GlobalContext* globalCtx); + +static u8 sIsSpawned = false; + +const ActorInit Bg_Jya_1flift_InitVars = { + ACTOR_BG_JYA_1FLIFT, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJya1flift), + (ActorFunc)BgJya1flift_Init, + (ActorFunc)BgJya1flift_Destroy, + (ActorFunc)BgJya1flift_Update, + (ActorFunc)BgJya1flift_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 70, 80, -82, { 0, 0, 0 } }, +}; + +static f32 sFinalPositions[] = { 443.0f, -50.0f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1200, ICHAIN_STOP), +}; + +void BgJya1flift_InitDynapoly(BgJya1flift* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG login failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_jya_1flift.c", 179, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgJya1flift_InitCollision(Actor* thisx, GlobalContext* globalCtx) { + BgJya1flift* this = (BgJya1flift*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; +} + +void BgJya1flift_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJya1flift* this = (BgJya1flift*)thisx; + // "1 F lift" + osSyncPrintf("(1Fリフト)(flag %d)(room %d)\n", sIsSpawned, globalCtx->roomCtx.curRoom.num); + this->hasInitialized = false; + if (sIsSpawned) { + Actor_Kill(thisx); + return; + } + BgJya1flift_InitDynapoly(this, globalCtx, &g1fliftCol, 0); + Actor_ProcessInitChain(thisx, sInitChain); + BgJya1flift_InitCollision(thisx, globalCtx); + if (Flags_GetSwitch(globalCtx, (thisx->params & 0x3F))) { + LINK_AGE_IN_YEARS == YEARS_ADULT ? BgJya1flift_ChangeDirection(this) : BgJya1flift_SetupDoNothing(this); + } else { + BgJya1flift_SetupWaitForSwitch(this); + } + thisx->room = -1; + sIsSpawned = true; + this->hasInitialized = true; +} + +void BgJya1flift_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJya1flift* this = (BgJya1flift*)thisx; + + if (this->hasInitialized) { + sIsSpawned = false; + Collider_DestroyCylinder(globalCtx, &this->collider); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void BgJya1flift_SetupWaitForSwitch(BgJya1flift* this) { + this->actionFunc = BgJya1flift_WaitForSwitch; + this->dyna.actor.world.pos.y = sFinalPositions[0]; +} + +void BgJya1flift_WaitForSwitch(BgJya1flift* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params & 0x3F))) { + BgJya1flift_ChangeDirection(this); + } +} + +void BgJya1flift_SetupDoNothing(BgJya1flift* this) { + this->actionFunc = BgJya1flift_DoNothing; + this->dyna.actor.world.pos.y = sFinalPositions[0]; +} + +void BgJya1flift_DoNothing(BgJya1flift* this, GlobalContext* globalCtx) { +} + +void BgJya1flift_ChangeDirection(BgJya1flift* this) { + this->actionFunc = BgJya1flift_Move; + this->isMovingDown ^= true; + this->dyna.actor.velocity.y = 0.0f; +} + +void BgJya1flift_Move(BgJya1flift* this, GlobalContext* globalCtx) { + f32 tempVelocity; + + Math_StepToF(&this->dyna.actor.velocity.y, 6.0f, 0.4f); + if (this->dyna.actor.velocity.y < 1.0f) { + tempVelocity = 1.0f; + } else { + tempVelocity = this->dyna.actor.velocity.y; + } + if (fabsf(Math_SmoothStepToF(&this->dyna.actor.world.pos.y, (sFinalPositions[this->isMovingDown]), 0.5f, + tempVelocity, 1.0f)) < 0.001f) { + this->dyna.actor.world.pos.y = sFinalPositions[this->isMovingDown]; + BgJya1flift_ResetMoveDelay(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE3 - SFX_FLAG); + } +} + +void BgJya1flift_ResetMoveDelay(BgJya1flift* this) { + this->actionFunc = BgJya1flift_DelayMove; + this->moveDelay = 0; +} + +void BgJya1flift_DelayMove(BgJya1flift* this, GlobalContext* globalCtx) { + this->moveDelay++; + if (this->moveDelay >= 21) { + BgJya1flift_ChangeDirection(this); + } +} + +void BgJya1flift_Update(Actor* thisx, GlobalContext* globalCtx2) { + BgJya1flift* this = (BgJya1flift*)thisx; + GlobalContext* globalCtx = globalCtx2; + s32 tempIsRiding; + + // Room 0 is the first room and 6 is the room that the lift starts on + if (globalCtx->roomCtx.curRoom.num == 6 || globalCtx->roomCtx.curRoom.num == 0) { + this->actionFunc(this, globalCtx); + tempIsRiding = func_8004356C(&this->dyna) ? true : false; + if ((this->actionFunc == BgJya1flift_Move) || (this->actionFunc == BgJya1flift_DelayMove)) { + if (tempIsRiding) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_FIRE_PLATFORM); + } else if (!tempIsRiding && this->isLinkRiding) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + } + this->isLinkRiding = tempIsRiding; + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } else { + Actor_Kill(thisx); + } +} + +void BgJya1flift_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, g1fliftDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_1flift/z_bg_jya_1flift.h b/soh/src/overlays/actors/ovl_Bg_Jya_1flift/z_bg_jya_1flift.h new file mode 100644 index 000000000..fbcc7afc0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_1flift/z_bg_jya_1flift.h @@ -0,0 +1,21 @@ +#ifndef Z_BG_JYA_1FLIFT_H +#define Z_BG_JYA_1FLIFT_H + +#include "ultra64.h" +#include "global.h" + +struct BgJya1flift; + +typedef void (*BgJya1fliftActionFunc)(struct BgJya1flift*, GlobalContext*); + +typedef struct BgJya1flift { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderCylinder collider; + /* 0x01B0 */ BgJya1fliftActionFunc actionFunc; + /* 0x01B4 */ s16 moveDelay; + /* 0x01B6 */ u8 isMovingDown; + /* 0x01B7 */ u8 hasInitialized; + /* 0x01B8 */ u8 isLinkRiding; +} BgJya1flift; // size = 0x01BC + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Amishutter/z_bg_jya_amishutter.c b/soh/src/overlays/actors/ovl_Bg_Jya_Amishutter/z_bg_jya_amishutter.c new file mode 100644 index 000000000..3c7c2705e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Amishutter/z_bg_jya_amishutter.c @@ -0,0 +1,129 @@ +/* + * File: z_bg_jya_amishutter.c + * Overlay: Bg_Jya_Amishutter + * Description: Circular metal grate. Lifts up when you get close to it. + */ + +#include "z_bg_jya_amishutter.h" +#include "objects/object_jya_obj/object_jya_obj.h" + +#define FLAGS 0 + +void BgJyaAmishutter_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaAmishutter_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaAmishutter_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaAmishutter_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJyaAmishutter_SetupWaitForPlayer(BgJyaAmishutter* this); +void BgJyaAmishutter_WaitForPlayer(BgJyaAmishutter* this); +void func_80893428(BgJyaAmishutter* this); +void func_80893438(BgJyaAmishutter* this); +void func_808934B0(BgJyaAmishutter* this); +void func_808934C0(BgJyaAmishutter* this); +void func_808934FC(BgJyaAmishutter* this); +void func_8089350C(BgJyaAmishutter* this); + +const ActorInit Bg_Jya_Amishutter_InitVars = { + ACTOR_BG_JYA_AMISHUTTER, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaAmishutter), + (ActorFunc)BgJyaAmishutter_Init, + (ActorFunc)BgJyaAmishutter_Destroy, + (ActorFunc)BgJyaAmishutter_Update, + (ActorFunc)BgJyaAmishutter_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgJyaAmishutter_InitDynaPoly(BgJyaAmishutter* this, GlobalContext* globalCtx, CollisionHeader* collision, + s32 flag) { + s32 pad1; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_jya_amishutter.c", 129, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgJyaAmishutter_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaAmishutter* this = (BgJyaAmishutter*)thisx; + + BgJyaAmishutter_InitDynaPoly(this, globalCtx, &gAmishutterCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + BgJyaAmishutter_SetupWaitForPlayer(this); +} + +void BgJyaAmishutter_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaAmishutter* this = (BgJyaAmishutter*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgJyaAmishutter_SetupWaitForPlayer(BgJyaAmishutter* this) { + this->actionFunc = BgJyaAmishutter_WaitForPlayer; +} + +void BgJyaAmishutter_WaitForPlayer(BgJyaAmishutter* this) { + if ((this->dyna.actor.xzDistToPlayer < 60.0f) && (fabsf(this->dyna.actor.yDistToPlayer) < 30.0f)) { + func_80893428(this); + } +} + +void func_80893428(BgJyaAmishutter* this) { + this->actionFunc = func_80893438; +} + +void func_80893438(BgJyaAmishutter* this) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 100.0f, 3.0f)) { + func_808934B0(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG); + } +} + +void func_808934B0(BgJyaAmishutter* this) { + this->actionFunc = func_808934C0; +} + +void func_808934C0(BgJyaAmishutter* this) { + if (this->dyna.actor.xzDistToPlayer > 300.0f) { + func_808934FC(this); + } +} + +void func_808934FC(BgJyaAmishutter* this) { + this->actionFunc = func_8089350C; +} + +void func_8089350C(BgJyaAmishutter* this) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 3.0f)) { + BgJyaAmishutter_SetupWaitForPlayer(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG); + } +} + +void BgJyaAmishutter_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaAmishutter* this = (BgJyaAmishutter*)thisx; + + this->actionFunc(this); +} + +void BgJyaAmishutter_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gAmishutterDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Amishutter/z_bg_jya_amishutter.h b/soh/src/overlays/actors/ovl_Bg_Jya_Amishutter/z_bg_jya_amishutter.h new file mode 100644 index 000000000..e15a42fe8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Amishutter/z_bg_jya_amishutter.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_JYA_AMISHUTTER_H +#define Z_BG_JYA_AMISHUTTER_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaAmishutter; + +typedef void (*BgJyaAmishutterActionFunc)(struct BgJyaAmishutter*); + +typedef struct BgJyaAmishutter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgJyaAmishutterActionFunc actionFunc; +} BgJyaAmishutter; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.c b/soh/src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.c new file mode 100644 index 000000000..934329337 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.c @@ -0,0 +1,254 @@ +/* + * File: z_bg_jya_bigmirror.c + * Overlay: ovl_Bg_Jya_Bigmirror + * Description: Ceiling mirror and puzzle in Spirit Temple top rooms + */ + +#include "z_bg_jya_bigmirror.h" +#include "objects/object_jya_obj/object_jya_obj.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgJyaBigmirror_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBigmirror_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBigmirror_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBigmirror_Draw(Actor* thisx, GlobalContext* globalCtx); + +static u8 sIsSpawned = false; + +const ActorInit Bg_Jya_Bigmirror_InitVars = { + ACTOR_BG_JYA_BIGMIRROR, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaBigmirror), + (ActorFunc)BgJyaBigmirror_Init, + (ActorFunc)BgJyaBigmirror_Destroy, + (ActorFunc)BgJyaBigmirror_Update, + (ActorFunc)BgJyaBigmirror_Draw, + NULL, +}; + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 params; + /* 0x0E */ s16 solvedRotY; + /* 0x10 */ s16 initRotY; +} BigMirrorDataEntry; // size = 0x14 + +static BigMirrorDataEntry sCobraSpawnData[] = { + { { -560.0f, 1743.0f, -310.0f }, 0xFF01, 0x4000, 0x8000 }, + { { 60.0f, 1743.0f, -310.0f }, 0xFF02, 0x8000, 0xA000 }, +}; + +void BgJyaBigmirror_SetRoomFlag(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + + this->puzzleFlags &= + ~(BIGMIR_PUZZLE_IN_STATUE_ROOM | BIGMIR_PUZZLE_IN_1ST_TOP_ROOM | BIGMIR_PUZZLE_IN_2ND_TOP_ROOM); + if (globalCtx->roomCtx.curRoom.num == 5) { + this->puzzleFlags |= BIGMIR_PUZZLE_IN_STATUE_ROOM; + } else if (globalCtx->roomCtx.curRoom.num == 0x19) { + this->puzzleFlags |= BIGMIR_PUZZLE_IN_1ST_TOP_ROOM; + } else if (globalCtx->roomCtx.curRoom.num == 0x1A) { + this->puzzleFlags |= BIGMIR_PUZZLE_IN_2ND_TOP_ROOM; + } +} + +void BgJyaBigmirror_HandleCobra(Actor* thisx, GlobalContext* globalCtx) { + static u8 cobraPuzzleFlags[] = { BIGMIR_PUZZLE_COBRA1_SOLVED, BIGMIR_PUZZLE_COBRA2_SOLVED }; + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + BigMirrorDataEntry* curSpawnData; + BigmirrorCobra* curCobraInfo; + s32 i; + + if (this->puzzleFlags & (BIGMIR_PUZZLE_IN_1ST_TOP_ROOM | BIGMIR_PUZZLE_IN_2ND_TOP_ROOM)) { + for (i = 0; i < 2; i++) { + curSpawnData = &sCobraSpawnData[i]; + curCobraInfo = &this->cobraInfo[i]; + if (curCobraInfo->cobra != NULL) { + curCobraInfo->rotY = curCobraInfo->cobra->dyna.actor.shape.rot.y; + + if (curCobraInfo->rotY == curSpawnData->solvedRotY) { + this->puzzleFlags |= cobraPuzzleFlags[i]; + } else { + this->puzzleFlags &= ~cobraPuzzleFlags[i]; + } + + if (curCobraInfo->cobra->dyna.actor.update == NULL) { + // "Cobra deleted" + osSyncPrintf("Error : コブラ削除された (%s %d)\n", "../z_bg_jya_bigmirror.c", 203); + } + } else { + curCobraInfo->cobra = (BgJyaCobra*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BG_JYA_COBRA, curSpawnData->pos.x, + curSpawnData->pos.y, curSpawnData->pos.z, 0, curCobraInfo->rotY, 0, curSpawnData->params); + this->actor.child = NULL; + + if (&curCobraInfo->cobra->dyna.actor == NULL) { + // "Cobra generation failed" + osSyncPrintf("Error : コブラ発生失敗 (%s %d)\n", "../z_bg_jya_bigmirror.c", 221); + } + } + } + } else { + + for (i = 0; i < 2; i++) { + curCobraInfo = &this->cobraInfo[i]; + if (curCobraInfo->cobra != NULL) { + if (curCobraInfo->cobra->dyna.actor.child != NULL) { + Actor_Kill(curCobraInfo->cobra->dyna.actor.child); + curCobraInfo->cobra->dyna.actor.child = NULL; + } + Actor_Kill(&curCobraInfo->cobra->dyna.actor); + curCobraInfo->cobra = NULL; + } + } + } +} + +void BgJyaBigmirror_SetBombiwaFlag(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + + if (Flags_GetSwitch(globalCtx, 0x29)) { + this->puzzleFlags |= BIGMIR_PUZZLE_BOMBIWA_DESTROYED; + } else { + this->puzzleFlags &= ~(BIGMIR_PUZZLE_BOMBIWA_DESTROYED); + } +} + +void BgJyaBigmirror_HandleMirRay(Actor* thisx, GlobalContext* globalCtx) { + static s16 sMirRayParamss[] = { 0x0005, 0x0007, 0x0008 }; + static Vec3f sMirRayPoss[] = { + { 60.0f, 1802.0f, -1102.0f }, + { -560.0f, 1800.0f, -310.0f }, + { 60.0f, 1800.0f, -310.0f }, + }; + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + s32 puzzleSolved; + s32 lightBeamToggles[3]; + s32 i; + s32 objBankIndex; + + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MIR_RAY); + + if ((objBankIndex < 0) || (objBankIndex != this->mirRayObjIndex)) { + this->lightBeams[2] = NULL; + this->lightBeams[1] = NULL; + this->lightBeams[0] = NULL; + } else { + puzzleSolved = !!(this->puzzleFlags & (BIGMIR_PUZZLE_IN_STATUE_ROOM | BIGMIR_PUZZLE_IN_1ST_TOP_ROOM)); + + if (puzzleSolved) { + puzzleSolved = !!(this->puzzleFlags & BIGMIR_PUZZLE_COBRA2_SOLVED); + + if (puzzleSolved) { + puzzleSolved = !!(this->puzzleFlags & BIGMIR_PUZZLE_COBRA1_SOLVED); + } + } + lightBeamToggles[0] = puzzleSolved; // Only spawn if puzzle solved + if (1) {} + lightBeamToggles[1] = lightBeamToggles[2] = + this->puzzleFlags & (BIGMIR_PUZZLE_IN_1ST_TOP_ROOM | BIGMIR_PUZZLE_IN_2ND_TOP_ROOM); + + for (i = 0; i < 3; i++) { + if (lightBeamToggles[i]) { + if ((this->lightBeams[i] == NULL) && Object_IsLoaded(&globalCtx->objectCtx, objBankIndex)) { + this->lightBeams[i] = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_MIR_RAY, sMirRayPoss[i].x, + sMirRayPoss[i].y, sMirRayPoss[i].z, 0, 0, 0, sMirRayParamss[i]); + + if (this->lightBeams[i] == NULL) { + // "Mir Ray generation failed" + osSyncPrintf("Error : Mir Ray 発生失敗 (%s %d)\n", "../z_bg_jya_bigmirror.c", 310); + } + } + } else { + if (this->lightBeams[i] != NULL) { + Actor_Kill(this->lightBeams[i]); + this->lightBeams[i] = NULL; + } + } + } + } + this->mirRayObjIndex = objBankIndex; +} + +void BgJyaBigmirror_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + + if (sIsSpawned) { + Actor_Kill(&this->actor); + return; + } + + Actor_SetScale(&this->actor, 0.1f); + this->cobraInfo[0].rotY = sCobraSpawnData[0].initRotY; + this->cobraInfo[1].rotY = sCobraSpawnData[1].initRotY; + this->actor.room = -1; + sIsSpawned = true; + this->spawned = true; + this->mirRayObjIndex = -1; + + // "jya Bigmirror" + osSyncPrintf("(jya 大鏡)(arg_data 0x%04x)\n", this->actor.params); +} + +void BgJyaBigmirror_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + + if (this->spawned) { + sIsSpawned = false; + } +} + +void BgJyaBigmirror_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBigmirror_SetRoomFlag(thisx, globalCtx); + BgJyaBigmirror_HandleCobra(thisx, globalCtx); + BgJyaBigmirror_SetBombiwaFlag(thisx, globalCtx); + BgJyaBigmirror_HandleMirRay(thisx, globalCtx); +} + +void BgJyaBigmirror_DrawLightBeam(Actor* thisx, GlobalContext* globalCtx) { + static Vec3s D_80893F4C = { 0, 0, 0 }; + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + Actor* lift; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_bigmirror.c", 435); + func_80093D84(globalCtx->state.gfxCtx); + lift = Actor_Find(&globalCtx->actorCtx, ACTOR_BG_JYA_LIFT, ACTORCAT_BG); + if (lift != NULL) { + this->liftHeight = lift->world.pos.y; + } + Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, this->actor.world.pos.y + 40.0f, this->actor.world.pos.z, + &this->actor.shape.rot); + Matrix_Scale(0.1f, (this->liftHeight * -(1.0f / 1280.0f)) + (1779.4f / 1280.0f), 0.1f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_bigmirror.c", 457), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBigMirror1DL); + + if (lift != NULL) { + if (1) {} + Matrix_SetTranslateRotateYXZ(lift->world.pos.x, lift->world.pos.y, lift->world.pos.z, &D_80893F4C); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_bigmirror.c", 467), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBigMirror2DL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_bigmirror.c", 476); +} + +void BgJyaBigmirror_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBigmirror* this = (BgJyaBigmirror*)thisx; + + if (this->puzzleFlags & BIGMIR_PUZZLE_IN_1ST_TOP_ROOM) { + Gfx_DrawDListOpa(globalCtx, gBigMirror3DL); + Gfx_DrawDListXlu(globalCtx, gBigMirror4DL); + } + + if ((this->puzzleFlags & + (BIGMIR_PUZZLE_IN_STATUE_ROOM | BIGMIR_PUZZLE_IN_1ST_TOP_ROOM | BIGMIR_PUZZLE_IN_2ND_TOP_ROOM)) && + (this->puzzleFlags & BIGMIR_PUZZLE_COBRA2_SOLVED) && (this->puzzleFlags & BIGMIR_PUZZLE_COBRA1_SOLVED)) { + BgJyaBigmirror_DrawLightBeam(&this->actor, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.h b/soh/src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.h new file mode 100644 index 000000000..298d95692 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.h @@ -0,0 +1,32 @@ +#ifndef Z_BG_JYA_BIGMIRROR_H +#define Z_BG_JYA_BIGMIRROR_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.h" + +#define BIGMIR_PUZZLE_COBRA1_SOLVED (1 << 0) +#define BIGMIR_PUZZLE_COBRA2_SOLVED (1 << 1) +#define BIGMIR_PUZZLE_BOMBIWA_DESTROYED (1 << 2) +#define BIGMIR_PUZZLE_IN_STATUE_ROOM (1 << 3) +#define BIGMIR_PUZZLE_IN_1ST_TOP_ROOM (1 << 4) +#define BIGMIR_PUZZLE_IN_2ND_TOP_ROOM (1 << 5) + +struct BgJyaBigmirror; + +typedef struct { + /* 0x00 */ BgJyaCobra* cobra; + /* 0x04 */ s16 rotY; +} BigmirrorCobra; // size = 0x08 + +typedef struct BgJyaBigmirror { + /* 0x0000 */ Actor actor; + /* 0x014C */ BigmirrorCobra cobraInfo[2]; + /* 0x015C */ u8 puzzleFlags; + /* 0x015D */ u8 spawned; + /* 0x0160 */ Actor* lightBeams[3]; + /* 0x016C */ s32 mirRayObjIndex; + /* 0x0170 */ f32 liftHeight; +} BgJyaBigmirror; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Block/z_bg_jya_block.c b/soh/src/overlays/actors/ovl_Bg_Jya_Block/z_bg_jya_block.c new file mode 100644 index 000000000..03d53f05f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Block/z_bg_jya_block.c @@ -0,0 +1,78 @@ +/* + * File: z_bg_jya_block.c + * Overlay: ovl_Bg_Jya_Block + * Description: Silver Block (Child Link) + */ + +#include "z_bg_jya_block.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" + +#define FLAGS 0 + +void BgJyaBlock_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBlock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBlock_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBlock_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Jya_Block_InitVars = { + ACTOR_BG_JYA_BLOCK, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(BgJyaBlock), + (ActorFunc)BgJyaBlock_Init, + (ActorFunc)BgJyaBlock_Destroy, + (ActorFunc)BgJyaBlock_Update, + (ActorFunc)BgJyaBlock_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 333, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP), +}; + +void BgJyaBlock_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgJyaBlock* this = (BgJyaBlock*)thisx; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&gPushBlockCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + if ((LINK_AGE_IN_YEARS != YEARS_CHILD) || !Flags_GetSwitch(globalCtx, thisx->params & 0x3F)) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgJyaBlock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBlock* this = (BgJyaBlock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgJyaBlock_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBlock* this = (BgJyaBlock*)thisx; + Player* player = GET_PLAYER(globalCtx); + + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; +} + +void BgJyaBlock_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_block.c", 145); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gPushBlockGrayTex)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_block.c", 153), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_OPA_DISP++, 232, 210, 176, 255); + gSPDisplayList(POLY_OPA_DISP++, gPushBlockDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_block.c", 158); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Block/z_bg_jya_block.h b/soh/src/overlays/actors/ovl_Bg_Jya_Block/z_bg_jya_block.h new file mode 100644 index 000000000..2d58de5c1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Block/z_bg_jya_block.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_JYA_BLOCK_H +#define Z_BG_JYA_BLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaBlock; + +typedef struct BgJyaBlock { + /* 0x0000 */ DynaPolyActor dyna; +} BgJyaBlock; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.c b/soh/src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.c new file mode 100644 index 000000000..4219cfe22 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.c @@ -0,0 +1,243 @@ +#include "z_bg_jya_bombchuiwa.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_jya_obj/object_jya_obj.h" +#define FLAGS ACTOR_FLAG_0 + +void BgJyaBombchuiwa_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBombchuiwa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBombchuiwa_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBombchuiwa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJyaBombchuiwa_WaitForExplosion(BgJyaBombchuiwa* this, GlobalContext* globalCtx); +void BgJyaBombchuiwa_SetupWaitForExplosion(BgJyaBombchuiwa* this, GlobalContext* globalCtx); +void func_808949B8(BgJyaBombchuiwa* this, GlobalContext* globalCtx); +void BgJyaBombchuiwa_CleanUpAfterExplosion(BgJyaBombchuiwa* this, GlobalContext* globalCtx); +void BgJyaBombchuiwa_SpawnLightRay(BgJyaBombchuiwa* this, GlobalContext* globalCtx); + +const ActorInit Bg_Jya_Bombchuiwa_InitVars = { + ACTOR_BG_JYA_BOMBCHUIWA, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaBombchuiwa), + (ActorFunc)BgJyaBombchuiwa_Init, + (ActorFunc)BgJyaBombchuiwa_Destroy, + (ActorFunc)BgJyaBombchuiwa_Update, + (ActorFunc)BgJyaBombchuiwa_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { -300, 0, 0 }, 40 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_2, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 3, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgJyaBombchuiwa_SetupCollider(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, &this->colliderItems); +} + +void BgJyaBombchuiwa_SetDrawFlags(BgJyaBombchuiwa* this, u8 drawFlags) { + this->drawFlags &= ~7; + this->drawFlags |= drawFlags; +} + +void BgJyaBombchuiwa_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombchuiwa* this = (BgJyaBombchuiwa*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + BgJyaBombchuiwa_SetupCollider(this, globalCtx); + if (Flags_GetSwitch(globalCtx, this->actor.params & 0x3F)) { + BgJyaBombchuiwa_SpawnLightRay(this, globalCtx); + } else { + BgJyaBombchuiwa_SetupWaitForExplosion(this, globalCtx); + } + Actor_SetFocus(&this->actor, 0.0f); +} + +void BgJyaBombchuiwa_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgJyaBombchuiwa* this = (BgJyaBombchuiwa*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgJyaBombchuiwa_Break(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + s16 scale; + s16 arg5; + s16 arg6; + s16 arg7; + s32 i; + + for (i = 0; i < 20; i++) { + pos.x = Rand_ZeroOne() * 10.0f + this->actor.world.pos.x - 10.0f; + pos.y = Rand_ZeroOne() * 40.0f + this->actor.world.pos.y - 20.0f; + pos.z = Rand_ZeroOne() * 50.0f + this->actor.world.pos.z - 25.0f; + velocity.x = Rand_ZeroOne() * 3.0f - 0.3f; + velocity.y = Rand_ZeroOne() * 18.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 15.0f; + scale = (s32)(Rand_ZeroOne() * 20.0f) + 1; + if (scale > 10) { + arg5 = 5; + } else { + arg5 = 1; + } + if (Rand_ZeroOne() < 0.4f) { + arg5 |= 0x40; + arg6 = 0xC; + arg7 = 8; + } else { + arg5 |= 0x20; + arg6 = 0xC; + arg7 = 8; + if (scale < 8) { + arg6 = 0x46; + arg7 = 0x28; + } + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -300, arg5, arg6, arg7, 0, scale, 1, 15, 80, + KAKERA_COLOR_NONE, OBJECT_JYA_OBJ, gBombiwaEffectDL); + } + func_80033480(globalCtx, &this->actor.world.pos, 100.0f, 8, 100, 160, 0); +} + +void BgJyaBombchuiwa_SetupWaitForExplosion(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + this->actionFunc = BgJyaBombchuiwa_WaitForExplosion; + BgJyaBombchuiwa_SetDrawFlags(this, 3); + this->timer = 0; +} + +void BgJyaBombchuiwa_WaitForExplosion(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + if ((this->collider.base.acFlags & AC_HIT) || (this->timer > 0)) { + if (this->timer == 0) { + OnePointCutscene_Init(globalCtx, 3410, -99, &this->actor, MAIN_CAM); + } + this->timer++; + if (this->timer > 10) { + BgJyaBombchuiwa_Break(this, globalCtx); + BgJyaBombchuiwa_CleanUpAfterExplosion(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_WALL_BROKEN); + } + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgJyaBombchuiwa_CleanUpAfterExplosion(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + this->actionFunc = func_808949B8; + BgJyaBombchuiwa_SetDrawFlags(this, 4); + this->lightRayIntensity = 0.3f; + this->timer = 0; + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void func_808949B8(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + this->timer++; + if (this->timer & 4) { + func_80033480(globalCtx, &this->actor.world.pos, 60.0f, 3, 100, 100, 0); + } + if (Math_StepToF(&this->lightRayIntensity, 1.0f, 0.028)) { + BgJyaBombchuiwa_SpawnLightRay(this, globalCtx); + } +} + +void BgJyaBombchuiwa_SpawnLightRay(BgJyaBombchuiwa* this, GlobalContext* globalCtx) { + this->actionFunc = NULL; + this->lightRayIntensity = 153.0f; + BgJyaBombchuiwa_SetDrawFlags(this, 4); + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_MIR_RAY, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0) == NULL) { + // "Occurrence failure" + osSyncPrintf("Error : Mir_Ray 発生失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_jya_bombchuiwa.c", 410, + this->actor.params); + } +} + +void BgJyaBombchuiwa_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombchuiwa* this = (BgJyaBombchuiwa*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgJyaBombchuiwa_DrawRock(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_bombchuiwa.c", 436); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_bombchuiwa.c", 439), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBombchuiwa2DL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_bombchuiwa.c", 443); +} + +void BgJyaBombchuiwa_DrawLight(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombchuiwa* this = (BgJyaBombchuiwa*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_bombchuiwa.c", 453); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_bombchuiwa.c", 457), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, CLAMP_MAX((u32)(this->lightRayIntensity * 153.0f), 153)); + gSPDisplayList(POLY_XLU_DISP++, gBombchuiwaLight1DL); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, CLAMP_MAX((u32)(this->lightRayIntensity * 255.0f), 255)); + gSPDisplayList(POLY_XLU_DISP++, gBombchuiwaLight2DL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_bombchuiwa.c", 472); +} + +void BgJyaBombchuiwa_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f D_80894F88 = { -920.0f, 480.0f, -889.0f }; + static Vec3s D_80894F94 = { 0, 0, 0 }; + BgJyaBombchuiwa* this = (BgJyaBombchuiwa*)thisx; + + if (this->drawFlags & 1) { + Gfx_DrawDListOpa(globalCtx, gBombchuiwaDL); + Collider_UpdateSpheres(0, &this->collider); + } + + if (this->drawFlags & 2) { + BgJyaBombchuiwa_DrawRock(globalCtx); + } + if (this->drawFlags & 4) { + Matrix_SetTranslateRotateYXZ(D_80894F88.x, D_80894F88.y, D_80894F88.z, &D_80894F94); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + if (this->drawFlags & 4) { + BgJyaBombchuiwa_DrawLight(thisx, globalCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.h b/soh/src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.h new file mode 100644 index 000000000..aa91448f3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Bombchuiwa/z_bg_jya_bombchuiwa.h @@ -0,0 +1,21 @@ +#ifndef Z_BG_JYA_BOMBCHUIWA_H +#define Z_BG_JYA_BOMBCHUIWA_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaBombchuiwa; + +typedef void (*BgJyaBombchuiwaActionFunc)(struct BgJyaBombchuiwa*, GlobalContext*); + +typedef struct BgJyaBombchuiwa { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgJyaBombchuiwaActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItems; + /* 0x01B0 */ f32 lightRayIntensity; + /* 0x01B4 */ s16 timer; + /* 0x01B6 */ u8 drawFlags; // Used to determine how the actor is drawn. +} BgJyaBombchuiwa; // size = 0x01B8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Bombiwa/z_bg_jya_bombiwa.c b/soh/src/overlays/actors/ovl_Bg_Jya_Bombiwa/z_bg_jya_bombiwa.c new file mode 100644 index 000000000..8ecb77738 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Bombiwa/z_bg_jya_bombiwa.c @@ -0,0 +1,181 @@ +/* + * File: z_bg_jya_bombiwa.c + * Overlay: ovl_Bg_Jya_Bombiwa + * Description: Spirit Temple top room bombable wall + */ + +#include "z_bg_jya_bombiwa.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_jya_obj/object_jya_obj.h" +#include "vt.h" + +#define FLAGS 0 + +void BgJyaBombiwa_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBombiwa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBombiwa_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaBombiwa_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Jya_Bombiwa_InitVars = { + ACTOR_BG_JYA_BOMBIWA, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaBombiwa), + (ActorFunc)BgJyaBombiwa_Init, + (ActorFunc)BgJyaBombiwa_Destroy, + (ActorFunc)BgJyaBombiwa_Update, + (ActorFunc)BgJyaBombiwa_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 50 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgJyaBombiwa_SetupDynaPoly(BgJyaBombiwa* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flag) { + s16 pad1; + CollisionHeader* colHeader = NULL; + s16 pad2; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + + // "Warning: move BG registration failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_jya_bombiwa.c", 174, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgJyaBombiwa_InitCollider(BgJyaBombiwa* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->colliderItems); +} + +void BgJyaBombiwa_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombiwa* this = (BgJyaBombiwa*)thisx; + + if ((this->dyna.actor.params & 0x3F) != 0x29) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + + // "Warning: Switch Number changed (%s %d)(SW %d)" + osSyncPrintf("Warning : Switch Number が変更された(%s %d)(SW %d)\n", "../z_bg_jya_bombiwa.c", 218, + this->dyna.actor.params & 0x3F); + osSyncPrintf(VT_RST); + } + BgJyaBombiwa_SetupDynaPoly(this, globalCtx, &gBombiwaCol, DPM_UNK); + BgJyaBombiwa_InitCollider(this, globalCtx); + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + Actor_Kill(&this->dyna.actor); + } else { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + // "Rock destroyed by jya bomb" + osSyncPrintf("(jya 爆弾で破壊岩)(arg_data 0x%04x)\n", this->dyna.actor.params); + } +} + +void BgJyaBombiwa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombiwa* this = (BgJyaBombiwa*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgJyaBombiwa_Break(BgJyaBombiwa* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + s16 arg5; + s8 arg6; + s8 arg7; + s32 i; + s16 scale; + + for (i = 0; i < 16; i++) { + pos.x = ((Rand_ZeroOne() * 80.0f) + this->dyna.actor.world.pos.x) - 40.0f; + pos.y = (Rand_ZeroOne() * 140.0f) + this->dyna.actor.world.pos.y; + pos.z = ((Rand_ZeroOne() * 80.0f) + this->dyna.actor.world.pos.z) - 40.0f; + velocity.x = (Rand_ZeroOne() - 0.5f) * 10.0f; + velocity.y = Rand_ZeroOne() * 12.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 10.0f; + scale = (s32)(i * 1.8f) + 3; + if (scale > 15) { + arg5 = 5; + } else { + arg5 = 1; + } + if (Rand_ZeroOne() < 0.4f) { + arg5 |= 0x40; + arg6 = 0xC; + arg7 = 8; + } else { + arg5 |= 0x20; + arg6 = 0xC; + arg7 = 8; + if (scale < 10) { + arg6 = 0x50; + arg7 = 80; + } + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -400, arg5, arg6, arg7, 0, scale, 1, 20, 80, + KAKERA_COLOR_NONE, OBJECT_JYA_OBJ, gBombiwaEffectDL); + } + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + 70.0f; + pos.z = this->dyna.actor.world.pos.z; + func_80033480(globalCtx, &pos, 100.0f, 0xA, 0x64, 0xA0, 1); +} + +void BgJyaBombiwa_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombiwa* this = (BgJyaBombiwa*)thisx; + + if (this->collider.base.acFlags & AC_HIT) { + BgJyaBombiwa_Break(this, globalCtx); + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 40, NA_SE_EV_WALL_BROKEN); + Actor_Kill(&this->dyna.actor); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgJyaBombiwa_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgJyaBombiwa* this = (BgJyaBombiwa*)thisx; + + Gfx_DrawDListOpa(globalCtx, gBombiwaDL); + Collider_UpdateSpheres(0, &this->collider); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Bombiwa/z_bg_jya_bombiwa.h b/soh/src/overlays/actors/ovl_Bg_Jya_Bombiwa/z_bg_jya_bombiwa.h new file mode 100644 index 000000000..879291f87 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Bombiwa/z_bg_jya_bombiwa.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_JYA_BOMBIWA_H +#define Z_BG_JYA_BOMBIWA_H + +#include "ultra64.h" +#include "global.h" + + +struct BgJyaBombiwa; + +typedef struct BgJyaBombiwa { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s32 unk_164; + /* 0x0168 */ ColliderJntSph collider; + /* 0x0188 */ ColliderJntSphElement colliderItems[1]; +} BgJyaBombiwa; // size = 0x01C8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.c b/soh/src/overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.c new file mode 100644 index 000000000..3f659ad57 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.c @@ -0,0 +1,626 @@ +#include "z_bg_jya_cobra.h" + +#include + +#include "overlays/actors/ovl_Bg_Jya_Bigmirror/z_bg_jya_bigmirror.h" +#include "overlays/actors/ovl_Mir_Ray/z_mir_ray.h" +#include "objects/object_jya_obj/object_jya_obj.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgJyaCobra_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaCobra_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaCobra_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaCobra_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80896918(BgJyaCobra* this, GlobalContext* globalCtx); +void func_80896950(BgJyaCobra* this, GlobalContext* globalCtx); +void func_808969F8(BgJyaCobra* this, GlobalContext* globalCtx); +void func_80896ABC(BgJyaCobra* this, GlobalContext* globalCtx); + +#include "overlays/ovl_Bg_Jya_Cobra/ovl_Bg_Jya_Cobra.h" + +const ActorInit Bg_Jya_Cobra_InitVars = { + ACTOR_BG_JYA_COBRA, + ACTORCAT_PROP, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaCobra), + (ActorFunc)BgJyaCobra_Init, + (ActorFunc)BgJyaCobra_Destroy, + (ActorFunc)BgJyaCobra_Update, + (ActorFunc)BgJyaCobra_Draw, + NULL, +}; + +static s16 D_80897308[] = { 0, 0, 0, 0 }; + +static u8 D_80897310[] = { true, false, true, false }; + +static s16 D_80897314[] = { -0x4000, 0000, 0x4000, 0000 }; + +static u8 D_8089731C[11][11] = { + { 0x00, 0x00, 0x20, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, 0x00, 0x00 }, + { 0x00, 0x20, 0x80, 0xA0, 0xA3, 0xA3, 0xA3, 0xA0, 0x80, 0x20, 0x00 }, + { 0x20, 0x80, 0xA0, 0xA5, 0xA6, 0xA6, 0xA6, 0xA5, 0xA0, 0x80, 0x20 }, + { 0x80, 0xA0, 0xA5, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA5, 0xA0, 0x80 }, + { 0x80, 0xA3, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA3, 0x80 }, + { 0x80, 0xA3, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA3, 0x80 }, + { 0x80, 0xA3, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA3, 0x80 }, + { 0x80, 0xA0, 0xA5, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA5, 0xA0, 0x80 }, + { 0x20, 0x80, 0xA0, 0xA5, 0xA6, 0xA6, 0xA6, 0xA5, 0xA0, 0x80, 0x20 }, + { 0x00, 0x20, 0x80, 0xA0, 0xA3, 0xA3, 0xA3, 0xA0, 0x80, 0x20, 0x00 }, + { 0x00, 0x00, 0x20, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20, 0x00, 0x00 }, +}; + +static u8 D_80897398[3][3] = { + { 0x20, 0x80, 0x20 }, + { 0x80, 0xA0, 0x80 }, + { 0x20, 0x80, 0x20 }, +}; + +static Vec3f D_808973A4[] = { + { -6.0f, 100.0f, 7.6f }, + { -12.6f, 69.200005f, -10.0f }, + { -9.0f, 43.0f, -1.0f }, + { -3.0f, 15.0f, 8.6f }, + { -8.6f, 15.0f, 13.5f }, + { -6.6f, 26.0f, 11.6f }, + { -12.5f, 43.0f, 8.0f }, + { -17.2f, 70.0f, 0.6f }, + { -8.0f, 100.0f, 7.6f }, + { 6.0f, 100.0f, 7.6f }, + { 12.6f, 69.200005f, -10.0f }, + { 9.0f, 43.0f, -1.0f }, + { 3.0f, 15.0f, 8.6f }, + { 8.6f, 15.0f, 13.5f }, + { 6.6f, 26.0f, 11.6f }, + { 12.5f, 43.0f, 8.0f }, + { 17.2f, 70.0f, 0.6f }, + { 8.0f, 100.0f, 7.6f }, + { 0.0f, 70.0f, -11.3f }, + { 0.0f, 44.600002f, -2.0f }, + { 0.0f, 15.0f, 10.6f }, + { 0.0f, 15.0f, 0.3f }, + { 0.0f, 26.0f, 11.6f }, + { 0.0f, 88.4f, -1.4f }, + { 0.0f, 95.700005f, 14.900001f }, + { 0.0f, 101.4f, 5.0f }, +}; + +static Vec3f D_808974DC[] = { + { 12.0f, 21.300001f, -2.5f }, { 30.0f, 21.300001f, -2.5f }, { -15.0f, 21.300001f, -2.5f }, + { -30.0f, 21.300001f, -2.5f }, { 12.0f, 21.300001f, -2.5f }, +}; + +static s32 D_80897518[] = { 0x80, 0xA0, 0xA0, 0x80 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static Vec3s D_80897538 = { 0, -0x4000, 0 }; + +static Vec3s D_80897540 = { 0, 0x4000, 0 }; + +static Vec3f D_80897548[] = { + { 0.1f, 0.1f, 0.1f }, + { 0.072f, 0.072f, 0.072f }, + { 0.1f, 0.1f, 0.132f }, +}; + +void func_808958F0(Vec3f* dest, Vec3f* src, f32 arg2, f32 arg3) { + dest->x = (src->z * arg2) + (src->x * arg3); + dest->y = src->y; + dest->z = (src->z * arg3) - (src->x * arg2); +} + +void BgJyaCobra_InitDynapoly(BgJyaCobra* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flags) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flags); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG Registration Failure" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_jya_cobra.c", 247, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgJyaCobra_SpawnRay(BgJyaCobra* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_MIR_RAY, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y + 57.0f, this->dyna.actor.world.pos.z, 0, 0, 0, 6); + if (this->dyna.actor.child == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + // "Error : Mir Ray occurrence failure" + osSyncPrintf("Error : Mir Ray 発生失敗 (%s %d)\n", "../z_bg_jya_cobra.c", 270); + osSyncPrintf(VT_RST); + } +} + +void func_80895A70(BgJyaCobra* this) { + s32 pad; + BgJyaBigmirror* mirror = (BgJyaBigmirror*)this->dyna.actor.parent; + MirRay* mirRay; + + switch (this->dyna.actor.params & 3) { + case 0: + mirRay = (MirRay*)this->dyna.actor.child; + if (mirRay == NULL) { + return; + } + if (this->dyna.actor.child->update == NULL) { + this->dyna.actor.child = NULL; + return; + } + break; + case 1: + mirRay = (MirRay*)mirror->lightBeams[1]; + if (mirRay == NULL) { + return; + } + break; + case 2: + mirRay = (MirRay*)mirror->lightBeams[2]; + if (mirRay == NULL) { + return; + } + break; + } + + if (this->unk_18C <= 0.0f) { + mirRay->unLit = 1; + } else { + Vec3f sp28; + + mirRay->unLit = 0; + Math_Vec3f_Copy(&mirRay->sourcePt, &this->unk_180); + Matrix_RotateY(this->dyna.actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_RotateX(D_80897308[this->dyna.actor.params & 3] * (M_PI / 0x8000), MTXMODE_APPLY); + sp28.x = 0.0f; + sp28.y = 0.0; + sp28.z = this->unk_190 * 2800.0f; + Matrix_MultVec3f(&sp28, &mirRay->poolPt); + Math_Vec3f_Sum(&mirRay->sourcePt, &mirRay->poolPt, &mirRay->poolPt); + } +} + +void func_80895BEC(BgJyaCobra* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + Vec3f sp2C; + + func_808958F0(&sp2C, &this->unk_174, Math_SinS(this->unk_170), Math_CosS(this->unk_170)); + player->actor.world.pos.x = this->dyna.actor.world.pos.x + sp2C.x; + player->actor.world.pos.y = this->dyna.actor.world.pos.y + sp2C.y; + player->actor.world.pos.z = this->dyna.actor.world.pos.z + sp2C.z; +} + +void func_80895C74(BgJyaCobra* this, GlobalContext* globalCtx) { + s16 phi_v0; + s16 params = this->dyna.actor.params; + BgJyaBigmirror* mirror = (BgJyaBigmirror*)this->dyna.actor.parent; + f32 phi_f0; + + if ((params & 3) == 2 && mirror != NULL && + (!(mirror->puzzleFlags & BIGMIR_PUZZLE_BOMBIWA_DESTROYED) || + !(mirror->puzzleFlags & BIGMIR_PUZZLE_COBRA1_SOLVED))) { + Math_StepToF(&this->unk_18C, 0.0f, 0.05f); + } else { + this->unk_18C = 1.0f; + if (D_80897310[params & 3]) { + phi_v0 = this->dyna.actor.shape.rot.y - D_80897314[params & 3]; + phi_v0 = ABS(phi_v0); + if (phi_v0 < 0x2000 && phi_v0 != -0x8000) { + this->unk_18C += (phi_v0 - 0x2000) * (3.0f / 0x4000); + if (this->unk_18C < 0.0f) { + this->unk_18C = 0.0f; + } + } + } + } + + this->unk_180.x = this->dyna.actor.world.pos.x; + this->unk_180.y = this->dyna.actor.world.pos.y + 57.0f; + this->unk_180.z = this->dyna.actor.world.pos.z; + + if ((params & 3) == 0) { + this->unk_190 = 0.1f; + } else if ((params & 3) == 1) { + phi_f0 = 0.1f; + phi_v0 = this->dyna.actor.shape.rot.y - 0x8000; + if (phi_v0 < 0x500 && phi_v0 > -0x500) { + phi_f0 = 0.34f; + } else { + phi_v0 = this->dyna.actor.shape.rot.y - 0x4000; + if (phi_v0 < 0x500 && phi_v0 > -0x500 && mirror != NULL && + (mirror->puzzleFlags & BIGMIR_PUZZLE_BOMBIWA_DESTROYED)) { + phi_f0 = 0.34f; + } + } + Math_StepToF(&this->unk_190, phi_f0, 0.04f); + } else if ((params & 3) == 2) { + phi_f0 = 0.1f; + phi_v0 = this->dyna.actor.shape.rot.y - 0x8000; + if (phi_v0 < 0x500 && phi_v0 > -0x500) { + phi_f0 = 0.34f; + } else { + phi_v0 = this->dyna.actor.shape.rot.y + 0xFFFF4000; + if (phi_v0 < 0x500 && phi_v0 > -0x500) { + phi_f0 = 0.34f; + } + } + Math_StepToF(&this->unk_190, phi_f0, 0.04f); + } +} + +/* + * Updates the shadow with light coming from the side of the mirror + */ +void BgJyaCobra_UpdateShadowFromSide(BgJyaCobra* this) { + Vec3f spD4; + Vec3f spC8; + Vec3f spBC; + u8* temp_s2; + s32 temp_x; + s32 temp_z; + s32 x; + s32 z; + s32 i; + s32 j; + s32 k; + s32 l; + s16 rotY; + + temp_s2 = ALIGN16((uintptr_t)(&this->shadowTexture)); + memset(temp_s2, 0, 0x1000); + + Matrix_RotateX((M_PI / 4), MTXMODE_NEW); + rotY = !(this->dyna.actor.params & 3) ? (this->dyna.actor.shape.rot.y + 0x4000) + : (this->dyna.actor.shape.rot.y - 0x4000); + Matrix_RotateY(rotY * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.9f, 0.9f, 0.9f, MTXMODE_APPLY); + + for (i = 0; i < 25; i++) { + Math_Vec3f_Diff(&D_808973A4[i + 1], &D_808973A4[i], &spD4); + spD4.x *= 1 / 2.0f; + spD4.y *= 1 / 2.0f; + spD4.z *= 1 / 2.0f; + for (j = 0; j < 2; j++) { + spC8.x = D_808973A4[i].x + (spD4.x * j); + spC8.y = D_808973A4[i].y + (spD4.y * j); + spC8.z = D_808973A4[i].z + (spD4.z * j); + Matrix_MultVec3f(&spC8, &spBC); + x = (spBC.x + 50.0f) * 0.64f + 0.5f; + z = (88.0f - spBC.z) * 0.64f + 0.5f; + for (k = 0; k < 11; k++) { + temp_z = z - 5 + k; + if (!(temp_z & ~0x3F)) { + temp_z *= 0x40; + for (l = 0; l < 11; l++) { + temp_x = x - 5 + l; + if (!(temp_x & ~0x3F)) { + temp_s2[temp_z + temp_x] |= D_8089731C[k][l]; + } + if (1) {} + } + } + } + } + } + + for (i = 0; i < 4; i++) { + Math_Vec3f_Diff(&D_808974DC[i + 1], &D_808974DC[i], &spD4); + spD4.x *= 1 / 5.0f; + spD4.y *= 1 / 5.0f; + spD4.z *= 1 / 5.0f; + for (j = 0; j < 5; j++) { + spC8.x = D_808974DC[i].x + (spD4.x * j); + spC8.y = D_808974DC[i].y + (spD4.y * j); + spC8.z = D_808974DC[i].z + (spD4.z * j); + Matrix_MultVec3f(&spC8, &spBC); + x = (s32)(((spBC.x + 50.0f) * 0.64f) + 0.5f); + z = (s32)(((88.0f - spBC.z) * 0.64f) + 0.5f); + for (k = 0; k < 3; k++) { + temp_z = z - 1 + k; + if (!(temp_z & ~0x3F)) { + temp_z *= 0x40; + for (l = 0; l < 3; l++) { + temp_x = x - 1 + l; + if (!(temp_x & ~0x3F)) { + temp_s2[temp_z + temp_x] |= D_80897398[k][l]; + } + } + } + } + } + } + + for (i = 0; i < 0x40; i++) { + temp_s2[0 * 0x40 + i] = 0; + temp_s2[0x3F * 0x40 + i] = 0; + } + + for (j = 1; j < 0x3F; j++) { + temp_s2[j * 0x40 + 0] = 0; + temp_s2[j * 0x40 + 0x3F] = 0; + } + if (D_80897398[0][0]) {} +} + +/* + * Updates the shadow with light coming from above the mirror + */ +void BgJyaCobra_UpdateShadowFromTop(BgJyaCobra* this) { + f32 sp58[0x40]; + s32 i; + s32 j; + s32 i_copy; + s32 counter; + u8* temp_s0; + u8* sp40; + + for (i = 0; i < 0x40; i++) { + sp58[i] = SQ(i - 31.5f); + } + + sp40 = temp_s0 = (u8*)ALIGN16((uintptr_t)(&this->shadowTexture)); + memset(temp_s0, 0, 0x1000); + + for (i = 0; i != 0x40; i++) { + f32 temp_f12 = sp58[i]; + + for (j = 0; j < 0x40; j++, sp40++) { + f32 temp_f2 = (sp58[j] * 0.5f) + temp_f12; + + if (temp_f2 < 300.0f) { + *sp40 |= CLAMP_MAX(640 - (s32)(temp_f2 * 2.0f), 166); + } + } + } + + for (i_copy = 0x780, counter = 0; counter < 4; counter++, i_copy += 0x40) { + i = i_copy; + for (j = 4; j < 0x3C; j++) { + if (temp_s0[i_copy + j] < D_80897518[counter]) { + temp_s0[i_copy + j] = D_80897518[counter]; + } + } + temp_s0[i + 0x3C] = 0x20; + temp_s0[i + 0x3] = 0x20; + } +} + +void BgJyaCobra_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaCobra* this = (BgJyaCobra*)thisx; + + BgJyaCobra_InitDynapoly(this, globalCtx, &gCobraCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + if (!(this->dyna.actor.params & 3) && Flags_GetSwitch(globalCtx, ((s32)this->dyna.actor.params >> 8) & 0x3F)) { + this->dyna.actor.world.rot.y = this->dyna.actor.home.rot.y = this->dyna.actor.shape.rot.y = 0; + } + + if (!(this->dyna.actor.params & 3)) { + BgJyaCobra_SpawnRay(this, globalCtx); + } + + func_80896918(this, globalCtx); + + if ((this->dyna.actor.params & 3) == 1 || (this->dyna.actor.params & 3) == 2) { + this->dyna.actor.room = -1; + } + + if ((this->dyna.actor.params & 3) == 1) { + BgJyaCobra_UpdateShadowFromTop(this); + } + + // "(jya cobra)" + osSyncPrintf("(jya コブラ)(arg_data 0x%04x)(act %x)(txt %x)(txt16 %x)\n", this->dyna.actor.params, this, + &this->shadowTexture, ALIGN16((s32)(&this->shadowTexture))); +} + +void BgJyaCobra_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaCobra* this = (BgJyaCobra*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80896918(BgJyaCobra* this, GlobalContext* globalCtx) { + this->actionFunc = func_80896950; + this->unk_168 = 0; + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y = + (this->unk_16C * 0x2000) + this->dyna.actor.home.rot.y; +} + +void func_80896950(BgJyaCobra* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 > 0.001f) { + this->unk_168++; + if (this->unk_168 >= 15) { + func_808969F8(this, globalCtx); + } + } else { + this->unk_168 = 0; + } + + if (fabsf(this->dyna.unk_150) > 0.001f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } +} + +void func_808969F8(BgJyaCobra* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 phi_a3; + s16 temp2; + + this->actionFunc = func_80896ABC; + + temp2 = this->dyna.actor.yawTowardsPlayer - this->dyna.actor.shape.rot.y; + phi_a3 = (s16)(this->dyna.actor.shape.rot.y - this->dyna.unk_158); + phi_a3 = ABS(phi_a3); + + if (temp2 > 0) { + this->unk_16A = (phi_a3 > 0x4000) ? 1 : -1; + } else { + this->unk_16A = (phi_a3 > 0x4000) ? -1 : 1; + } + + this->unk_174.x = player->actor.world.pos.x - this->dyna.actor.world.pos.x; + this->unk_174.y = player->actor.world.pos.y - this->dyna.actor.world.pos.y; + this->unk_174.z = player->actor.world.pos.z - this->dyna.actor.world.pos.z; + this->unk_170 = this->unk_16E = 0; + this->unk_172 = true; +} + +void func_80896ABC(BgJyaCobra* this, GlobalContext* globalCtx) { + s16 temp_v0; + Player* player = GET_PLAYER(globalCtx); + + temp_v0 = (s16)((this->unk_16C * 0x2000) + this->dyna.actor.home.rot.y) - this->dyna.actor.world.rot.y; + if (ABS(temp_v0) < 7424) { + Math_StepToS(&this->unk_16E, 106, 4); + } else { + Math_StepToS(&this->unk_16E, 21, 10); + } + + if (Math_ScaledStepToS(&this->unk_170, this->unk_16A * 0x2000, this->unk_16E)) { + this->unk_16C = (this->unk_16C + this->unk_16A) & 7; + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + func_80896918(this, globalCtx); + } else { + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y = + (this->unk_16C * 0x2000) + this->dyna.actor.home.rot.y + this->unk_170; + } + + if (player->stateFlags2 & 0x10) { + if (this->unk_172) { + func_80895BEC(this, globalCtx); + } + } else if (fabsf(this->dyna.unk_150) < 0.001f) { + this->unk_172 = false; + } + + this->dyna.unk_150 = 0.0f; + func_8002F974(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); +} + +void BgJyaCobra_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgJyaCobra* this = (BgJyaCobra*)thisx; + + this->actionFunc(this, globalCtx); + + func_80895C74(this, globalCtx); + func_80895A70(this); + + if ((this->dyna.actor.params & 3) == 0 || (this->dyna.actor.params & 3) == 2) { + BgJyaCobra_UpdateShadowFromSide(this); + } +} + +void func_80896CB4(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 864); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 867), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCobra2DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 872); +} + +void func_80896D78(BgJyaCobra* this, GlobalContext* globalCtx) { + s32 pad; + Vec3s sp44; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 924); + func_80093D84(globalCtx->state.gfxCtx); + + sp44.x = D_80897308[this->dyna.actor.params & 3] + this->dyna.actor.shape.rot.x; + sp44.y = this->dyna.actor.shape.rot.y; + sp44.z = this->dyna.actor.shape.rot.z; + Matrix_SetTranslateRotateYXZ(this->unk_180.x, this->unk_180.y, this->unk_180.z, &sp44); + + Matrix_Scale(0.1f, 0.1f, this->unk_190, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 939), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s32)(this->unk_18C * 140.0f)); + gSPDisplayList(POLY_XLU_DISP++, gCobra3DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 947); +} + +void BgJyaCobra_DrawShadow(BgJyaCobra* this, GlobalContext* globalCtx) { + s32 pad; + s16 params = this->dyna.actor.params & 3; + Vec3f sp64; + Vec3s* phi_a3; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 966); + + func_80094044(globalCtx->state.gfxCtx); + + if (params == 0) { + sp64.x = this->dyna.actor.world.pos.x - 50.0f; + sp64.y = this->dyna.actor.world.pos.y; + sp64.z = this->dyna.actor.world.pos.z; + phi_a3 = &D_80897538; + } else if (params == 2) { + sp64.x = this->dyna.actor.world.pos.x + 70.0f; + sp64.y = this->dyna.actor.world.pos.y; + sp64.z = this->dyna.actor.world.pos.z; + phi_a3 = &D_80897540; + } else { // params == 1 + phi_a3 = &this->dyna.actor.shape.rot; + Math_Vec3f_Copy(&sp64, &this->dyna.actor.world.pos); + } + + Matrix_SetTranslateRotateYXZ(sp64.x, sp64.y, sp64.z, phi_a3); + + Matrix_Scale(D_80897548[params].x, D_80897548[params].y, D_80897548[params].z, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 40.0f, MTXMODE_APPLY); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 120); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 994), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPLoadTextureBlock(POLY_XLU_DISP++, ALIGN16((s32)(&this->shadowTexture)), G_IM_FMT_I, G_IM_SIZ_8b, 0x40, 0x40, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPDisplayList(POLY_XLU_DISP++, sShadowDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_cobra.c", 1006); +} + +void BgJyaCobra_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgJyaCobra* this = (BgJyaCobra*)thisx; + + func_80896CB4(globalCtx); + Gfx_DrawDListOpa(globalCtx, gCobra1DL); + + if (this->unk_18C > 0.0f) { + func_80896D78(this, globalCtx); + } + + if ((this->dyna.actor.params & 3) == 2) { + BgJyaBigmirror* mirror = (BgJyaBigmirror*)this->dyna.actor.parent; + + if (mirror != NULL && (mirror->puzzleFlags & BIGMIR_PUZZLE_BOMBIWA_DESTROYED) && + (mirror->puzzleFlags & BIGMIR_PUZZLE_COBRA1_SOLVED)) { + BgJyaCobra_DrawShadow(this, globalCtx); + } + } else { + BgJyaCobra_DrawShadow(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.h b/soh/src/overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.h new file mode 100644 index 000000000..f9ad1e6c5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Cobra/z_bg_jya_cobra.h @@ -0,0 +1,27 @@ +#ifndef Z_BG_JYA_COBRA_H +#define Z_BG_JYA_COBRA_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaCobra; + +typedef void (*BgJyaCobraActionFunc)(struct BgJyaCobra*, GlobalContext*); + +typedef struct BgJyaCobra { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgJyaCobraActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ s16 unk_16A; + /* 0x016C */ s16 unk_16C; + /* 0x016E */ s16 unk_16E; + /* 0x0170 */ s16 unk_170; + /* 0x0172 */ u8 unk_172; + /* 0x0174 */ Vec3f unk_174; + /* 0x0180 */ Vec3f unk_180; + /* 0x018C */ f32 unk_18C; + /* 0x0190 */ f32 unk_190; + /* 0x0194 */ u8 shadowTexture[0x1010]; +} BgJyaCobra; // size = 0x11A4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Goroiwa/z_bg_jya_goroiwa.c b/soh/src/overlays/actors/ovl_Bg_Jya_Goroiwa/z_bg_jya_goroiwa.c new file mode 100644 index 000000000..6ff676f7a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Goroiwa/z_bg_jya_goroiwa.c @@ -0,0 +1,222 @@ +/* + * File: z_bg_jya_goroiwa.c + * Overlay: ovl_Bg_Jya_Goroiwa + * Description: Rolling Boulder + * moves very slowly in some cases + */ + +#include "z_bg_jya_goroiwa.h" +#include "objects/object_goroiwa/object_goroiwa.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgJyaGoroiwa_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaGoroiwa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaGoroiwa_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaGoroiwa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJyaGoroiwa_Wait(BgJyaGoroiwa* this, GlobalContext* globalCtx); +void BgJyaGoroiwa_Move(BgJyaGoroiwa* this, GlobalContext* globalCtx); + +void BgJyaGoroiwa_SetupWait(BgJyaGoroiwa* this); +void BgJyaGoroiwa_SetupMove(BgJyaGoroiwa* this); +void BgJyaGoroiwa_UpdateRotation(BgJyaGoroiwa* this); +void BgJyaGoroiwa_UpdateCollider(BgJyaGoroiwa* this); + +const ActorInit Bg_Jya_Goroiwa_InitVars = { + ACTOR_BG_JYA_GOROIWA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GOROIWA, + sizeof(BgJyaGoroiwa), + (ActorFunc)BgJyaGoroiwa_Init, + (ActorFunc)BgJyaGoroiwa_Destroy, + (ActorFunc)BgJyaGoroiwa_Update, + (ActorFunc)BgJyaGoroiwa_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 58 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 15, 0, MASS_HEAVY }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgJyaGoroiwa_UpdateCollider(BgJyaGoroiwa* this) { + Sphere16* worldSphere = &this->collider.elements[0].dim.worldSphere; + + worldSphere->center.x = this->actor.world.pos.x; + worldSphere->center.y = this->actor.world.pos.y + 59.5f; + worldSphere->center.z = this->actor.world.pos.z; +} + +void BgJyaGoroiwa_InitCollider(BgJyaGoroiwa* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, &this->colliderItem); + BgJyaGoroiwa_UpdateCollider(this); + this->collider.elements[0].dim.worldSphere.radius = 58; +} + +void BgJyaGoroiwa_UpdateRotation(BgJyaGoroiwa* this) { + f32 xDiff = this->actor.world.pos.x - this->actor.prevPos.x; + + this->actor.shape.rot.z -= 0x10000 / (119 * M_PI) * xDiff; +} + +void BgJyaGoroiwa_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaGoroiwa* this = (BgJyaGoroiwa*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + BgJyaGoroiwa_InitCollider(this, globalCtx); + this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = 0; + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + ActorShape_Init(&this->actor.shape, 595.0f, ActorShadow_DrawCircle, 9.0f); + this->actor.shape.shadowAlpha = 128; + BgJyaGoroiwa_SetupMove(this); +} + +void BgJyaGoroiwa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaGoroiwa* this = (BgJyaGoroiwa*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgJyaGoroiwa_SetupMove(BgJyaGoroiwa* this) { + this->actionFunc = BgJyaGoroiwa_Move; + this->collider.base.atFlags |= AT_ON; + this->hasHit = false; + this->speedFactor = 1.0f; +} + +void BgJyaGoroiwa_Move(BgJyaGoroiwa* this, GlobalContext* globalCtx) { + Actor* thisx = &this->actor; + s16 relYawTowardsPlayer; + f32 speedXZsqBase = (-100.0f - thisx->world.pos.y) * 2.5f; + f32 posYfac; + + if (speedXZsqBase < 0.01f) { + speedXZsqBase = 0.01f; + } + + thisx->speedXZ = sqrtf(speedXZsqBase) * this->speedFactor; + thisx->velocity.x = Math_SinS(thisx->world.rot.y) * thisx->speedXZ; + thisx->velocity.z = Math_CosS(thisx->world.rot.y) * thisx->speedXZ; + + thisx->world.pos.x = thisx->world.pos.x + thisx->velocity.x; + thisx->world.pos.z = thisx->world.pos.z + thisx->velocity.z; + + if ((thisx->world.pos.x > 1466.0f) && (thisx->world.pos.x < 1673.0f)) { + thisx->world.pos.y = -129.5f; + } else { + posYfac = 1569.0f - thisx->world.pos.x; + posYfac = fabsf(posYfac) - 103.0f; + thisx->world.pos.y = ((35.0f / 92.0f) * posYfac) - 129.5f; + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT & ~AT_ON; + + relYawTowardsPlayer = thisx->yawTowardsPlayer - thisx->world.rot.y; + if ((relYawTowardsPlayer > -0x4000) && (relYawTowardsPlayer < 0x4000)) { + thisx->world.rot.y += 0x8000; + } + + func_8002F6D4(globalCtx, thisx, 2.0f, thisx->yawTowardsPlayer, 0.0f, 0); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + + this->yOffsetSpeed = 10.0f; + this->speedFactor = 0.5f; + this->hasHit = true; + } + + if (this->hasHit) { + this->yOffsetSpeed -= 1.5f; + thisx->shape.yOffset += this->yOffsetSpeed * 10.0f; + if (thisx->shape.yOffset < 595.0f) { + thisx->shape.yOffset = 595.0f; + BgJyaGoroiwa_SetupWait(this); + } + } else { + Math_StepToF(&this->speedFactor, 1.0f, 0.04f); + } + + if (thisx->world.pos.x > 1745.0f) { + thisx->world.rot.y = -0x4000; + } else if (thisx->world.pos.x < 1393.0f) { + thisx->world.rot.y = 0x4000; + } + + Audio_PlayActorSound2(thisx, NA_SE_EV_BIGBALL_ROLL - SFX_FLAG); +} + +void BgJyaGoroiwa_SetupWait(BgJyaGoroiwa* this) { + this->actionFunc = BgJyaGoroiwa_Wait; + this->waitTimer = 0; +} + +void BgJyaGoroiwa_Wait(BgJyaGoroiwa* this, GlobalContext* globalCtx) { + this->waitTimer++; + if (this->waitTimer > 60) { + BgJyaGoroiwa_SetupMove(this); + this->speedFactor = 0.1f; + } +} + +void BgJyaGoroiwa_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgJyaGoroiwa* this = (BgJyaGoroiwa*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 bgId; + Vec3f pos; + + if (!(player->stateFlags1 & 0x300000C0)) { + this->actionFunc(this, globalCtx); + BgJyaGoroiwa_UpdateRotation(this); + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 59.5f; + pos.z = this->actor.world.pos.z; + this->actor.floorHeight = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId, &this->actor, &pos); + BgJyaGoroiwa_UpdateCollider(this); + if (this->collider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgJyaGoroiwa_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gRollingRockDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Goroiwa/z_bg_jya_goroiwa.h b/soh/src/overlays/actors/ovl_Bg_Jya_Goroiwa/z_bg_jya_goroiwa.h new file mode 100644 index 000000000..803f30d95 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Goroiwa/z_bg_jya_goroiwa.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_JYA_GOROIWA_H +#define Z_BG_JYA_GOROIWA_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaGoroiwa; + +typedef void (*BgJyaGoroiwaFunc)(struct BgJyaGoroiwa*, GlobalContext*); + +typedef struct BgJyaGoroiwa { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgJyaGoroiwaFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItem; + /* 0x01B0 */ f32 speedFactor; + /* 0x01B4 */ s16 hasHit; + /* 0x01B6 */ s16 waitTimer; + /* 0x01B8 */ f32 yOffsetSpeed; +} BgJyaGoroiwa; // size = 0x01BC + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Haheniron/z_bg_jya_haheniron.c b/soh/src/overlays/actors/ovl_Bg_Jya_Haheniron/z_bg_jya_haheniron.c new file mode 100644 index 000000000..ec75ec8ec --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Haheniron/z_bg_jya_haheniron.c @@ -0,0 +1,218 @@ +/* + * File: z_bg_jya_haheniron + * Overlay: ovl_Bg_Jya_Haheniron + * Description: Chunks of Iron Knucle Chair and Pillar + */ + +#include "z_bg_jya_haheniron.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_jya_iron/object_jya_iron.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgJyaHaheniron_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaHaheniron_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaHaheniron_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaHaheniron_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJyaHaheniron_SetupChairCrumble(BgJyaHaheniron* this); +void BgJyaHaheniron_ChairCrumble(BgJyaHaheniron* this, GlobalContext* globalCtx); +void BgJyaHaheniron_SetupPillarCrumble(BgJyaHaheniron* this); +void BgJyaHaheniron_PillarCrumble(BgJyaHaheniron* this, GlobalContext* globalCtx); +void BgJyaHaheniron_SetupRubbleCollide(BgJyaHaheniron* this); +void BgJyaHaheniron_RubbleCollide(BgJyaHaheniron* this, GlobalContext* globalCtx); + +const ActorInit Bg_Jya_Haheniron_InitVars = { + ACTOR_BG_JYA_HAHENIRON, + ACTORCAT_PROP, + FLAGS, + OBJECT_JYA_IRON, + sizeof(BgJyaHaheniron), + (ActorFunc)BgJyaHaheniron_Init, + (ActorFunc)BgJyaHaheniron_Destroy, + (ActorFunc)BgJyaHaheniron_Update, + (ActorFunc)BgJyaHaheniron_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 10 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static s16 sKakeraScales[] = { 5, 8, 11, 14, 17 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32_DIV1000(gravity, -2000, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(minVelocityY, -15000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static f32 D_80898794[] = { 0.13f, 0.1f, 0.1f }; + +static Vec3f D_808987A0[] = { 0.0f, 14.0f, 0.0f }; + +static Vec3f D_808987AC[] = { 0.0f, 8.0f, 0.0f }; + +void BgJyaHaheniron_ColliderInit(BgJyaHaheniron* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); +} + +void BgJyaHaheniron_SpawnFragments(GlobalContext* globalCtx, Vec3f* vec1, Vec3f* vec2) { + Vec3f vel; + Vec3f pos; + s16 arg5; + s32 angle; + s32 i; + f32 rand1; + + for (angle = 0, i = 0; i < ARRAY_COUNT(sKakeraScales); i++) { + rand1 = Rand_ZeroOne() * 10.0f; + vel.x = (Math_SinS(angle) * rand1) + vec2->x; + vel.y = (Rand_ZeroOne() * 10.0f) + vec2->y; + vel.z = (Math_CosS(angle) * rand1) + vec2->z; + + rand1 = Rand_ZeroOne(); + if (rand1 < 0.2f) { + arg5 = 96; + } else if (rand1 < 0.8f) { + arg5 = 64; + } else { + arg5 = 32; + } + + EffectSsKakera_Spawn(globalCtx, vec1, &vel, vec1, -350, arg5, 40, 4, 0, sKakeraScales[i], 0, 20, 40, + KAKERA_COLOR_NONE, OBJECT_JYA_IRON, gObjectJyaIronDL_000880); + angle += 0x3333; + } + pos.x = vec1->x + (vec2->x * 5.0f); + pos.y = vec1->y + (vec2->y * 5.0f); + pos.z = vec1->z + (vec2->z * 5.0f); + func_80033480(globalCtx, &pos, 100.0f, 4, 100, 160, 1); +} + +void BgJyaHaheniron_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgJyaHaheniron* this = (BgJyaHaheniron*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, D_80898794[this->actor.params]); + if (this->actor.params == 0) { + BgJyaHaheniron_ColliderInit(this, globalCtx); + this->actor.shape.rot.z = (Rand_ZeroOne() * 65535.0f); + BgJyaHaheniron_SetupChairCrumble(this); + } else if (this->actor.params == 1) { + BgJyaHaheniron_SetupPillarCrumble(this); + } else if (this->actor.params == 2) { + BgJyaHaheniron_SetupRubbleCollide(this); + } +} + +void BgJyaHaheniron_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgJyaHaheniron* this = (BgJyaHaheniron*)thisx; + + if (this->actor.params == 0) { + Collider_DestroyJntSph(globalCtx, &this->collider); + } +} + +void BgJyaHaheniron_SetupChairCrumble(BgJyaHaheniron* this) { + this->actionFunc = BgJyaHaheniron_ChairCrumble; +} + +void BgJyaHaheniron_ChairCrumble(BgJyaHaheniron* this, GlobalContext* globalCtx) { + Vec3f vec; + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 8.0f, 0.0f, 0x85); + if ((this->actor.bgCheckFlags & 9) || ((this->collider.base.atFlags & AT_HIT) && (this->collider.base.at != NULL) && + (this->collider.base.at->category == ACTORCAT_PLAYER))) { + vec.x = -Rand_ZeroOne() * this->actor.velocity.x; + vec.y = -Rand_ZeroOne() * this->actor.velocity.y; + vec.z = -Rand_ZeroOne() * this->actor.velocity.z; + BgJyaHaheniron_SpawnFragments(globalCtx, &this->actor.world.pos, &vec); + Actor_Kill(&this->actor); + } else if (this->timer > 60) { + Actor_Kill(&this->actor); + } else { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + this->actor.shape.rot.y += 0x4B0; + this->actor.shape.rot.x += 0xFA0; +} + +void BgJyaHaheniron_SetupPillarCrumble(BgJyaHaheniron* this) { + this->actionFunc = BgJyaHaheniron_PillarCrumble; +} + +void BgJyaHaheniron_PillarCrumble(BgJyaHaheniron* this, GlobalContext* globalCtx) { + if (this->timer >= 8) { + Actor_MoveForward(&this->actor); + } else if (this->timer >= 17) { + BgJyaHaheniron_SpawnFragments(globalCtx, &this->actor.world.pos, D_808987A0); + Actor_Kill(&this->actor); + } + this->actor.shape.rot.y += 0x258; + this->actor.shape.rot.x += 0x3E8; +} + +void BgJyaHaheniron_SetupRubbleCollide(BgJyaHaheniron* this) { + this->actionFunc = BgJyaHaheniron_RubbleCollide; +} + +void BgJyaHaheniron_RubbleCollide(BgJyaHaheniron* this, GlobalContext* globalCtx) { + if (this->timer >= 17) { + BgJyaHaheniron_SpawnFragments(globalCtx, &this->actor.world.pos, D_808987AC); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 80, NA_SE_EN_IRONNACK_BREAK_PILLAR2); + Actor_Kill(&this->actor); + } +} + +void BgJyaHaheniron_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgJyaHaheniron* this = (BgJyaHaheniron*)thisx; + + this->timer++; + this->actionFunc(this, globalCtx); +} + +void BgJyaHaheniron_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { + gObjectJyaIronDL_000880, + gObjectJyaIronDL_000AE0, + gObjectJyaIronDL_000600, + }; + s32 pad; + BgJyaHaheniron* this = (BgJyaHaheniron*)thisx; + + if (this->actor.params == 0) { + Collider_UpdateSpheres(0, &this->collider); + } + Gfx_DrawDListOpa(globalCtx, dLists[this->actor.params]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Haheniron/z_bg_jya_haheniron.h b/soh/src/overlays/actors/ovl_Bg_Jya_Haheniron/z_bg_jya_haheniron.h new file mode 100644 index 000000000..6cdcf6ee5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Haheniron/z_bg_jya_haheniron.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_JYA_HAHENIRON_H +#define Z_BG_JYA_HAHENIRON_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaHaheniron; + +typedef void (*BgJyaHahenironActionFunc)(struct BgJyaHaheniron*, GlobalContext*); + +typedef struct BgJyaHaheniron { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgJyaHahenironActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItems[1]; + /* 0x01B0 */ s16 timer; +} BgJyaHaheniron; // size = 0x01B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Ironobj/z_bg_jya_ironobj.c b/soh/src/overlays/actors/ovl_Bg_Jya_Ironobj/z_bg_jya_ironobj.c new file mode 100644 index 000000000..92c22a998 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Ironobj/z_bg_jya_ironobj.c @@ -0,0 +1,277 @@ +/* + * File: z_bg_jya_ironobj.c + * Overlay: ovl_Bg_Jya_Ironobj + * Description: Destructable Iron Knuckle objects + */ + +#include "z_bg_jya_ironobj.h" +#include "objects/object_jya_iron/object_jya_iron.h" +#include "overlays/actors/ovl_En_Ik/z_en_ik.h" + +#define FLAGS 0 + +typedef void (*BgJyaIronobjIkFunc)(BgJyaIronobj*, GlobalContext*, EnIk*); + +void BgJyaIronobj_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaIronobj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaIronobj_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaIronobj_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_808992D8(BgJyaIronobj* this); +void func_808992E8(BgJyaIronobj* this, GlobalContext* globalCtx); + +void BgJyaIronobj_SpawnPillarParticles(BgJyaIronobj* this, GlobalContext* globalCtx, EnIk* enIk); +void BgJyaIronobj_SpawnThoneParticles(BgJyaIronobj* this, GlobalContext* arg1, EnIk* enIk); + +static int sUnused = 0; + +const ActorInit Bg_Jya_Ironobj_InitVars = { + ACTOR_BG_JYA_IRONOBJ, + ACTORCAT_PROP, + FLAGS, + OBJECT_JYA_IRON, + sizeof(BgJyaIronobj), + (ActorFunc)BgJyaIronobj_Init, + (ActorFunc)BgJyaIronobj_Destroy, + (ActorFunc)BgJyaIronobj_Update, + (ActorFunc)BgJyaIronobj_Draw, + NULL, +}; + +static Gfx* sOpaDL[] = { gPillarDL, gThroneDL }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 30, 150, 0, { 0, 0, 0 } }, +}; + +static s16 D_808994D8[] = { 0x8700, 0x4000, 0xC000, 0x0000 }; + +// Pillar +static s16 D_808994E0[] = { 5, 8, 11, 14, 17, 20, 23, 26 }; +static s16 D_808994F0[] = { 18, 26, 34, 42, 50, 60, 70, 80 }; +static s16 D_80899500[] = { 48, 42, 36, 32, 28, 24, 20, 16 }; + +// Throne +static s16 D_80899510[] = { 5, 8, 11, 14, 17, 20, 23, 26 }; +static s16 D_80899520[] = { 18, 26, 34, 42, 50, 60, 70, 80 }; +static s16 D_80899530[] = { 48, 42, 36, 32, 28, 24, 20, 16 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static CollisionHeader* sCollisionHeaders[] = { &gPillarCol, &gThroneCol }; + +void BgJyaIronobj_InitCylinder(BgJyaIronobj* this, GlobalContext* globalCtx) { + ColliderCylinder* colCylinder = &this->colCylinder; + + Collider_InitCylinder(globalCtx, colCylinder); + Collider_SetCylinder(globalCtx, colCylinder, &this->dyna.actor, &sCylinderInit); + if ((this->dyna.actor.params & 1) == 1) { + this->colCylinder.dim.radius = 40; + this->colCylinder.dim.height = 100; + } + Collider_UpdateCylinder(&this->dyna.actor, colCylinder); +} + +/* + * Spawns particles for the destroyed pillar + */ +void BgJyaIronobj_SpawnPillarParticles(BgJyaIronobj* this, GlobalContext* globalCtx, EnIk* enIk) { + s32 i; + s32 j; + s16 unkArg5; + f32 temp_f22; + Vec3f pos; + Vec3f vel; + f32 coss; + s16 rotY; + f32 sins; + s32 pad[2]; + + if (enIk->unk_2FF <= 0 || enIk->unk_2FF >= 4) { + osSyncPrintf("Error 攻撃方法が分からない(%s %d)\n", "../z_bg_jya_ironobj.c", 233, enIk->unk_2FF); + return; + } + osSyncPrintf("¢ attack_type(%d)\n", enIk->unk_2FF); + rotY = Actor_WorldYawTowardActor(&this->dyna.actor, &enIk->actor) + D_808994D8[enIk->unk_2FF - 1]; + + for (i = 0; i < 8; i++) { + Actor* actor = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_JYA_HAHENIRON, this->dyna.actor.world.pos.x, + Rand_ZeroOne() * 80.0f + this->dyna.actor.world.pos.y + 20.0f, this->dyna.actor.world.pos.z, 0, + (s16)(Rand_ZeroOne() * 0x4000) + rotY - 0x2000, 0, 0); + if (actor != NULL) { + actor->speedXZ = Rand_ZeroOne() * 8.0f + 9.0f; + actor->velocity.y = Rand_ZeroOne() * 10.0f + 6.0f; + } + } + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_JYA_HAHENIRON, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y + 150.0f, this->dyna.actor.world.pos.z, 0, 0, 0, 1); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_JYA_HAHENIRON, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, 0, 0, 2); + sins = Math_SinS(rotY); + coss = Math_CosS(rotY); + for (j = 0; j < 32; j++) { + f32 rand = Rand_ZeroOne(); + + if (rand < 0.1f) { + unkArg5 = 0x60; + } else if (rand < 0.8f) { + unkArg5 = 0x40; + } else { + unkArg5 = 0x20; + } + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + ((4.375f * j) + 10.0f); + pos.z = this->dyna.actor.world.pos.z; + temp_f22 = fabsf(j - 15.5f) * (1.0f / 31) + 0.5f; + vel.x = 2.0f * ((Rand_ZeroOne() * 6.0f) - 3.0f) + (Rand_ZeroOne() * sins * 8.0f * temp_f22); + vel.y = (Rand_ZeroOne() * 8.0f) - 3.0f; + vel.z = 2.0f * ((Rand_ZeroOne() * 6.0f) - 3.0f) + (Rand_ZeroOne() * coss * 8.0f * temp_f22); + EffectSsKakera_Spawn(globalCtx, &pos, &vel, &pos, -350, unkArg5, D_80899500[j & 7], 4, 0, D_808994E0[j & 7], 0, + 5, D_808994F0[j & 7], -1, OBJECT_JYA_IRON, gObjectJyaIronDL_000880); + if (Rand_ZeroOne() < 0.26f) { + func_80033480(globalCtx, &pos, 200.0f, 1, D_808994E0[j & 7] * 4 + 60, D_808994E0[j & 7] * 4 + 80, 1); + } + } +} + +/* + * Spawns particles for the destroyed throne + */ +void BgJyaIronobj_SpawnThoneParticles(BgJyaIronobj* this, GlobalContext* arg1, EnIk* enIk) { + s32 i; + s32 j; + s16 unkArg5; + f32 temp_f22; + Vec3f pos; + Vec3f vel; + f32 coss; + s16 rotY; + f32 sins; + s32 pad[2]; + + if (enIk->unk_2FF <= 0 || enIk->unk_2FF >= 4) { + osSyncPrintf("Error 攻撃方法が分からない(%s %d)\n", "../z_bg_jya_ironobj.c", 362, enIk->unk_2FF); + return; + } + osSyncPrintf("¢ attack_type(%d)\n", enIk->unk_2FF); + rotY = Actor_WorldYawTowardActor(&this->dyna.actor, &enIk->actor) + D_808994D8[enIk->unk_2FF - 1]; + for (i = 0; i < 8; i++) { + Actor* actor = + Actor_Spawn(&arg1->actorCtx, arg1, ACTOR_BG_JYA_HAHENIRON, this->dyna.actor.world.pos.x, + (Rand_ZeroOne() * 80.0f) + this->dyna.actor.world.pos.y + 10.0f, this->dyna.actor.world.pos.z, + 0, ((s16)(s32)(Rand_ZeroOne() * 0x4000) + rotY) - 0x2000, 0, 0); + if (actor != NULL) { + actor->speedXZ = Rand_ZeroOne() * 8.0f + 9.0f; + actor->velocity.y = Rand_ZeroOne() * 10.0f + 6.0f; + } + } + sins = Math_SinS(rotY); + coss = Math_CosS(rotY); + + for (j = 0; j < 32; j++) { + f32 rand = Rand_ZeroOne(); + + if (rand < 0.1f) { + unkArg5 = 0x60; + } else if (rand < 0.8f) { + unkArg5 = 0x40; + } else { + unkArg5 = 0x20; + } + pos.x = this->dyna.actor.world.pos.x + (Rand_ZeroOne() * 40 - 20); + pos.y = this->dyna.actor.world.pos.y + (3.75f * j); + pos.z = this->dyna.actor.world.pos.z + (Rand_ZeroOne() * 40 - 20); + temp_f22 = fabsf(j - 15.5f) * (1.0f / 31) + 0.5f; + vel.x = 2.0f * (Rand_ZeroOne() * 6.0f - 3.0f) + (Rand_ZeroOne() * sins * 8.0f * temp_f22); + vel.y = Rand_ZeroOne() * 8.0f - 3.0f; + vel.z = 2.0f * (Rand_ZeroOne() * 6.0f - 3.0f) + (Rand_ZeroOne() * coss * 8.0f * temp_f22); + EffectSsKakera_Spawn(arg1, &pos, &vel, &pos, -350, unkArg5, D_80899530[j & 7], 4, 0, D_80899510[j & 7], 0, 5, + D_80899520[j & 7], -1, OBJECT_JYA_IRON, gObjectJyaIronDL_000880); + if (Rand_ZeroOne() < 0.26f) { + func_80033480(arg1, &pos, 200.0f, 1, D_80899510[j & 7] * 4 + 60, D_80899510[j & 7] * 4 + 80, 1); + } + } +} + +void BgJyaIronobj_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaIronobj* this = (BgJyaIronobj*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, 0); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + BgJyaIronobj_InitCylinder(this, globalCtx); + CollisionHeader_GetVirtual(sCollisionHeaders[thisx->params & 1], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + func_808992D8(this); +} + +void BgJyaIronobj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaIronobj* this = (BgJyaIronobj*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->colCylinder); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808992D8(BgJyaIronobj* this) { + this->actionFunc = func_808992E8; +} + +void func_808992E8(BgJyaIronobj* this, GlobalContext* globalCtx) { + static BgJyaIronobjIkFunc particleFunc[] = { BgJyaIronobj_SpawnPillarParticles, BgJyaIronobj_SpawnThoneParticles }; + Actor* actor; + Vec3f dropPos; + s32 i; + + if (this->colCylinder.base.acFlags & AC_HIT) { + actor = this->colCylinder.base.ac; + this->colCylinder.base.acFlags &= ~AC_HIT; + if (actor != NULL && actor->id == ACTOR_EN_IK) { + particleFunc[this->dyna.actor.params & 1](this, globalCtx, (EnIk*)actor); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 80, + NA_SE_EN_IRONNACK_BREAK_PILLAR); + dropPos.x = this->dyna.actor.world.pos.x; + dropPos.y = this->dyna.actor.world.pos.y + 20.0f; + dropPos.z = this->dyna.actor.world.pos.z; + for (i = 0; i < 3; i++) { + Item_DropCollectible(globalCtx, &dropPos, ITEM00_HEART); + dropPos.y += 18.0f; + } + Actor_Kill(&this->dyna.actor); + return; + } + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder.base); + } +} + +void BgJyaIronobj_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaIronobj* this = (BgJyaIronobj*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgJyaIronobj_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, sOpaDL[thisx->params & 1]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Ironobj/z_bg_jya_ironobj.h b/soh/src/overlays/actors/ovl_Bg_Jya_Ironobj/z_bg_jya_ironobj.h new file mode 100644 index 000000000..1abdd4be1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Ironobj/z_bg_jya_ironobj.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_JYA_IRONOBJ_H +#define Z_BG_JYA_IRONOBJ_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaIronobj; + +typedef void (*BgJyaIronobjActionFunc)(struct BgJyaIronobj*, GlobalContext*); + +typedef struct BgJyaIronobj { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgJyaIronobjActionFunc actionFunc; + /* 0x0168 */ ColliderCylinder colCylinder; +} BgJyaIronobj; // size = 0x01B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Kanaami/z_bg_jya_kanaami.c b/soh/src/overlays/actors/ovl_Bg_Jya_Kanaami/z_bg_jya_kanaami.c new file mode 100644 index 000000000..ed68c1efe --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Kanaami/z_bg_jya_kanaami.c @@ -0,0 +1,129 @@ +/* + * File: z_bg_jya_kanaami.c + * Overlay: ovl_Bg_Jya_Kanaami + * Description: Climbable grating/bridge (Spirit Temple) + */ + +#include "z_bg_jya_kanaami.h" +#include "objects/object_jya_obj/object_jya_obj.h" + +#define FLAGS 0 + +void BgJyaKanaami_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaKanaami_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaKanaami_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaKanaami_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80899880(BgJyaKanaami* this); +void func_80899894(BgJyaKanaami* this, GlobalContext* globalCtx); +void func_8089993C(BgJyaKanaami* this); +void func_80899950(BgJyaKanaami* this, GlobalContext* globalCtx); +void func_80899A08(BgJyaKanaami* this); + +const ActorInit Bg_Jya_Kanaami_InitVars = { + ACTOR_BG_JYA_KANAAMI, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaKanaami), + (ActorFunc)BgJyaKanaami_Init, + (ActorFunc)BgJyaKanaami_Destroy, + (ActorFunc)BgJyaKanaami_Update, + (ActorFunc)BgJyaKanaami_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 700, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgJyaKanaami_InitDynaPoly(BgJyaKanaami* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_jya_kanaami.c", 145, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgJyaKanaami_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaKanaami* this = (BgJyaKanaami*)thisx; + + BgJyaKanaami_InitDynaPoly(this, globalCtx, &gKanaamiCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + func_80899A08(this); + } else { + func_80899880(this); + } + osSyncPrintf("(jya 金網)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgJyaKanaami_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaKanaami* this = (BgJyaKanaami*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80899880(BgJyaKanaami* this) { + this->actionFunc = func_80899894; + this->unk_16A = 0; +} + +void func_80899894(BgJyaKanaami* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) || this->unk_16A > 0) { + if (this->dyna.actor.world.pos.x > -1000.0f && this->unk_16A == 0) { + OnePointCutscene_Init(globalCtx, 3450, -99, &this->dyna.actor, MAIN_CAM); + } + this->unk_16A += 1; + if (this->unk_16A >= 0xA) { + func_8089993C(this); + } + } +} + +void func_8089993C(BgJyaKanaami* this) { + this->actionFunc = func_80899950; + this->unk_168 = 0; +} + +void func_80899950(BgJyaKanaami* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 quakeId; + + this->unk_168 += 0x20; + if (Math_ScaledStepToS(&this->dyna.actor.world.rot.x, 0x4000, this->unk_168)) { + func_80899A08(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_TRAP_BOUND); + quakeId = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeId, 25000); + Quake_SetQuakeValues(quakeId, 2, 0, 0, 0); + Quake_SetCountdown(quakeId, 16); + } +} + +void func_80899A08(BgJyaKanaami* this) { + this->actionFunc = 0; + this->dyna.actor.world.rot.x = 0x4000; +} + +void BgJyaKanaami_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaKanaami* this = (BgJyaKanaami*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } + this->dyna.actor.shape.rot.x = this->dyna.actor.world.rot.x; +} + +void BgJyaKanaami_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gKanaamiDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Kanaami/z_bg_jya_kanaami.h b/soh/src/overlays/actors/ovl_Bg_Jya_Kanaami/z_bg_jya_kanaami.h new file mode 100644 index 000000000..b57f51838 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Kanaami/z_bg_jya_kanaami.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_JYA_KANAAMI_H +#define Z_BG_JYA_KANAAMI_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaKanaami; + +typedef void (*BgJyaKanaamiActionFunc)(struct BgJyaKanaami*, GlobalContext*); + +typedef struct BgJyaKanaami { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgJyaKanaamiActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ s16 unk_16A; +} BgJyaKanaami; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c b/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c new file mode 100644 index 000000000..f57c64b84 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.c @@ -0,0 +1,155 @@ +/* + * File: z_bg_jya_lift.c + * Overlay: ovl_Bg_Jya_Lift + * Description: Chain Platform (Spirit Temple) + */ + +#include "z_bg_jya_lift.h" +#include "objects/object_jya_obj/object_jya_obj.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgJyaLift_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaLift_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaLift_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaLift_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJyaLift_SetFinalPosY(BgJyaLift* this); +void BgJyaLift_SetInitPosY(BgJyaLift* this); +void BgJyaLift_DelayMove(BgJyaLift* this, GlobalContext* globalCtx); +void BgJyaLift_SetupMove(BgJyaLift* this); +void BgJyaLift_Move(BgJyaLift* this, GlobalContext* globalCtx); + +static s16 sIsSpawned = false; + +const ActorInit Bg_Jya_Lift_InitVars = { + ACTOR_BG_JYA_LIFT, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaLift), + (ActorFunc)BgJyaLift_Init, + (ActorFunc)BgJyaLift_Destroy, + (ActorFunc)BgJyaLift_Update, + (ActorFunc)BgJyaLift_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2500, ICHAIN_STOP), +}; + +void BgJyaLift_InitDynapoly(BgJyaLift* this, GlobalContext* globalCtx, CollisionHeader* collisionHeader, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collisionHeader, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgJyaLift_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaLift* this = (BgJyaLift*)thisx; + + this->isSpawned = false; + if (sIsSpawned) { + Actor_Kill(thisx); + return; + } + + // "Goddess lift CT" + osSyncPrintf("女神リフト CT\n"); + BgJyaLift_InitDynapoly(this, globalCtx, &gLiftCol, DPM_UNK); + Actor_ProcessInitChain(thisx, sInitChain); + if (Flags_GetSwitch(globalCtx, (thisx->params & 0x3F))) { + BgJyaLift_SetFinalPosY(this); + } else { + BgJyaLift_SetInitPosY(this); + } + thisx->room = -1; + sIsSpawned = true; + this->isSpawned = true; +} + +void BgJyaLift_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaLift* this = (BgJyaLift*)thisx; + + if (this->isSpawned) { + + // "Goddess Lift DT" + osSyncPrintf("女神リフト DT\n"); + sIsSpawned = false; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void BgJyaLift_SetInitPosY(BgJyaLift* this) { + this->actionFunc = BgJyaLift_DelayMove; + this->dyna.actor.world.pos.y = 1613.0f; + this->moveDelay = 0; +} + +void BgJyaLift_DelayMove(BgJyaLift* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) || (this->moveDelay > 0)) { + this->moveDelay++; + if (this->moveDelay >= 20) { + OnePointCutscene_Init(globalCtx, 3430, -99, &this->dyna.actor, MAIN_CAM); + BgJyaLift_SetupMove(this); + } + } +} + +void BgJyaLift_SetupMove(BgJyaLift* this) { + this->actionFunc = BgJyaLift_Move; +} + +void BgJyaLift_Move(BgJyaLift* this, GlobalContext* globalCtx) { + f32 distFromBottom; + f32 tempVelocity; + + Math_SmoothStepToF(&this->dyna.actor.velocity.y, 4.0f, 0.1f, 1.0f, 0.0f); + tempVelocity = (this->dyna.actor.velocity.y < 0.2f) ? 0.2f : this->dyna.actor.velocity.y; + distFromBottom = Math_SmoothStepToF(&this->dyna.actor.world.pos.y, 973.0f, 0.1f, tempVelocity, 0.2f); + if ((this->dyna.actor.world.pos.y < 1440.0f) && (1440.0f <= this->dyna.actor.prevPos.y)) { + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + } + if (fabsf(distFromBottom) < 0.001f) { + BgJyaLift_SetFinalPosY(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ELEVATOR_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN - SFX_FLAG); + } +} + +void BgJyaLift_SetFinalPosY(BgJyaLift* this) { + this->actionFunc = NULL; + this->dyna.actor.world.pos.y = 973.0f; +} + +void BgJyaLift_Update(Actor* thisx, GlobalContext* globalCtx2) { + BgJyaLift* this = (BgJyaLift*)thisx; + GlobalContext* globalCtx = globalCtx2; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } + if ((this->dyna.unk_160 & 4) && ((this->unk_16B & 4) == 0)) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DIRECTED_YAW); + } else if (((this->dyna.unk_160) & 4) == 0 && ((this->unk_16B & 4)) && + (globalCtx->cameraPtrs[MAIN_CAM]->setting == CAM_SET_DIRECTED_YAW)) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + this->unk_16B = this->dyna.unk_160; + + // Spirit Temple room 5 is the main room with the statue room 25 is directly above room 5 + if ((globalCtx->roomCtx.curRoom.num != 5) && (globalCtx->roomCtx.curRoom.num != 25)) { + Actor_Kill(thisx); + } +} + +void BgJyaLift_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gLiftDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.h b/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.h new file mode 100644 index 000000000..2b5bc918f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Lift/z_bg_jya_lift.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_JYA_LIFT_H +#define Z_BG_JYA_LIFT_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaLift; +typedef void (*BgJyaLiftActionFunc)(struct BgJyaLift*, GlobalContext*); + +typedef struct BgJyaLift { + /* 0x000 */ DynaPolyActor dyna; + /* 0x164 */ BgJyaLiftActionFunc actionFunc; + /* 0x168 */ s16 moveDelay; + /* 0x16A */ u8 isSpawned; + /* 0x16B */ u8 unk_16B; +} BgJyaLift; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.c b/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.c new file mode 100644 index 000000000..aba508a46 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.c @@ -0,0 +1,354 @@ +#include "z_bg_jya_megami.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_jya_obj/object_jya_obj.h" + +#define FLAGS 0 + +void BgJyaMegami_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaMegami_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaMegami_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaMegami_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgJyaMegami_SetupDetectLight(BgJyaMegami* this); +void BgJyaMegami_DetectLight(BgJyaMegami* this, GlobalContext* globalCtx); +void BgJyaMegami_SetupExplode(BgJyaMegami* this); +void BgJyaMegami_Explode(BgJyaMegami* this, GlobalContext* globalCtx); + +const ActorInit Bg_Jya_Megami_InitVars = { + ACTOR_BG_JYA_MEGAMI, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaMegami), + (ActorFunc)BgJyaMegami_Init, + (ActorFunc)BgJyaMegami_Destroy, + (ActorFunc)BgJyaMegami_Update, + (ActorFunc)BgJyaMegami_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00200000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, -600, -200 }, 60 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +typedef struct { + /* 0x00 */ Vec3f unk_00; + /* 0x0C */ f32 velX; + /* 0x10 */ s16 rotVelX; + /* 0x12 */ s16 rotVelY; + /* 0x14 */ s16 delay; +} BgJyaMegamiPieceInit; // size = 0x18 + +static BgJyaMegamiPieceInit sPiecesInit[] = { + { { -50.0f, -21.28f, -38.92f }, -1.6f, 0xFED4, 0xFE70, 40 }, + { { -4.32f, -13.44f, -13.76f }, 0.0f, 0x04B0, 0x0190, 42 }, + { { 30.92f, -20.480001f, -28.84f }, 1.6f, 0xFCE0, 0x0320, 44 }, + { { -24.08f, -40.960003f, -21.359999f }, -1.0f, 0xFD44, 0x01F4, 36 }, + { { -44.8f, -73.92f, -49.76f }, -3.2f, 0x03E8, 0xFF38, 30 }, + { { -9.0f, -76.479996f, -13.24f }, -1.0f, 0xFC18, 0x0258, 26 }, + { { -10.240001f, -52.56f, -13.400001f }, 1.6f, 0x0258, 0xFE70, 34 }, + { { 34.04f, -61.72f, -37.04f }, 1.5f, 0x0258, 0x0258, 28 }, + { { 59.48f, -38.399998f, -49.4f }, 1.8f, 0x01F4, 0x0000, 38 }, + { { -19.04f, -112.24f, -35.120003f }, -1.6f, 0x012C, 0x0320, 22 }, + { { 12.24f, -99.04f, -31.64f }, 1.8f, 0xFC18, 0xFE70, 24 }, + { { 49.16f, -81.24f, -55.52f }, 2.4f, 0x02BC, 0x012C, 32 }, + { { 14.759999f, -125.8f, -44.16f }, 0.2f, 0x0320, 0x0258, 20 }, +}; + +static s16 D_8089B14C[] = { + 0x0005, 0x0008, 0x000B, 0x000E, 0x0011, 0x0014, 0x0017, 0x001A, +}; + +static s16 D_8089B15C[] = { + 0x0012, 0x001A, 0x0022, 0x002A, 0x0032, 0x003C, 0x0046, 0x0050, +}; + +static s16 D_8089B16C[] = { + 0x0030, 0x002A, 0x0024, 0x0020, 0x001C, 0x0018, 0x0014, 0x0010, +}; + +static s16 D_8089B17C[] = { + 0x0001, + 0x0003, + 0x0007, +}; + +static Vec3f sVelocity = { 0.0f, 0.0f, 0.8f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1200, ICHAIN_STOP), +}; + +void BgJyaMegami_InitDynaPoly(BgJyaMegami* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flag) { + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgJyaMegami_InitCollider(BgJyaMegami* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, &this->colliderItem); +} + +void BgJyaMegami_SpawnEffect(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, s32 num, s32 arg4) { + Vec3f spB4; + s32 i; + + for (i = 0; i < num; i++) { + s32 idx = ((s16)(Rand_ZeroOne() * 8.0f)) & D_8089B17C[arg4]; + s16 arg5 = ((idx < 5) && (Rand_ZeroOne() < 0.7f)) ? 0x40 : 0x20; + EffectSsKakera_Spawn(globalCtx, pos, velocity, pos, -90, arg5, D_8089B16C[idx], 4, 0, D_8089B14C[idx], 0, 5, + D_8089B15C[idx], KAKERA_COLOR_NONE, OBJECT_JYA_OBJ, gMegami2DL); + if (Rand_ZeroOne() < 0.45f) { + Math_Vec3f_Copy(&spB4, pos); + spB4.z += 25.0f; + func_80033480(globalCtx, &spB4, 60.0f, 0, D_8089B14C[idx] * 4 + 50, D_8089B14C[idx] * 4 + 70, 1); + } + } +} + +void BgJyaMegami_SetupSpawnEffect(BgJyaMegami* this, GlobalContext* globalCtx, f32 arg2) { + s32 i; + Vec3f pos; + + for (i = 0; i < ARRAY_COUNT(this->pieces); i++) { + if (Rand_ZeroOne() < arg2) { + Math_Vec3f_Sum(&this->dyna.actor.world.pos, &sPiecesInit[i].unk_00, &pos); + pos.z += 15.0f; + BgJyaMegami_SpawnEffect(globalCtx, &pos, &sVelocity, 1, 0); + } + } +} + +void BgJyaMegami_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaMegami* this = (BgJyaMegami*)thisx; + + BgJyaMegami_InitDynaPoly(this, globalCtx, &GMegamiCol, DPM_UNK); + BgJyaMegami_InitCollider(this, globalCtx); + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + Actor_Kill(&this->dyna.actor); + } else { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + Actor_SetFocus(&this->dyna.actor, -50.0f); + BgJyaMegami_SetupDetectLight(this); + } +} + +void BgJyaMegami_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaMegami* this = (BgJyaMegami*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgJyaMegami_SetupDetectLight(BgJyaMegami* this) { + this->actionFunc = BgJyaMegami_DetectLight; + this->lightTimer = 0; + this->crumbleIndex = 0; +} + +void BgJyaMegami_DetectLight(BgJyaMegami* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->lightTimer++; + this->collider.base.acFlags &= ~AC_HIT; + if (globalCtx->gameplayFrames % 4 == 0) { + BgJyaMegami_SetupSpawnEffect(this, globalCtx, (this->crumbleIndex * 0.04f) + 0.05f); + } + func_8002F974(&this->dyna.actor, NA_SE_EV_FACE_CRUMBLE_SLOW - SFX_FLAG); + } else if (this->lightTimer > 0) { + this->lightTimer--; + } + if (this->lightTimer > 40) { + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + BgJyaMegami_SetupExplode(this); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 100, NA_SE_EV_FACE_EXPLOSION); + OnePointCutscene_Init(globalCtx, 3440, -99, &this->dyna.actor, MAIN_CAM); + } else { + if (this->lightTimer < 8) { + this->crumbleIndex = 0; + } else if (this->lightTimer < 16) { + this->crumbleIndex = 1; + } else if (this->lightTimer < 24) { + this->crumbleIndex = 2; + } else if (this->lightTimer < 32) { + this->crumbleIndex = 3; + } else { + this->crumbleIndex = 4; + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgJyaMegami_SetupExplode(BgJyaMegami* this) { + u32 i; + + this->actionFunc = BgJyaMegami_Explode; + for (i = 0; i < ARRAY_COUNT(this->pieces); i++) { + Math_Vec3f_Copy(&this->pieces[i].pos, &this->dyna.actor.world.pos); + this->pieces[i].vel.x = sPiecesInit[i].velX; + } + this->explosionTimer = 0; +} + +void BgJyaMegami_Explode(BgJyaMegami* this, GlobalContext* globalCtx) { + static Vec3f sVec = { 0.0f, 0.0f, 0.0f }; + BgJyaMegamiPiece* temp; + u32 i; + Vec3f sp8C; + BgJyaMegamiPieceInit* temp2; + s32 pad; + + this->explosionTimer++; + if (this->explosionTimer == 30) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 100, NA_SE_EV_FACE_BREAKDOWN); + } + + for (i = 0; i < ARRAY_COUNT(this->pieces); i++) { + temp = &this->pieces[i]; + temp2 = &sPiecesInit[i]; + if (this->explosionTimer > temp2->delay) { + temp->vel.y -= 0.6f; + if (temp->vel.y < -18.0f) { + temp->vel.y = -18.0f; + } + temp->vel.x *= 0.995f; + temp->pos.x += temp->vel.x; + temp->pos.y += temp->vel.y; + temp->rotVelX += temp2->rotVelX; + temp->rotVelY += temp2->rotVelY; + if (Rand_ZeroOne() < 0.067f) { + Math_Vec3f_Sum(&temp->pos, &temp2->unk_00, &sp8C); + sp8C.z += 10.0f; + BgJyaMegami_SpawnEffect(globalCtx, &sp8C, &temp->vel, 3, 2); + } + } else if (this->explosionTimer == temp2->delay) { + Math_Vec3f_Sum(&temp->pos, &temp2->unk_00, &sp8C); + sp8C.z += 10.0f; + BgJyaMegami_SpawnEffect(globalCtx, &sp8C, &temp->vel, 4, 2); + } + } + + if ((this->explosionTimer % 4 == 0) && (this->explosionTimer > 30) && (this->explosionTimer < 80) && + (this->explosionTimer > 40)) { + sp8C.x = ((Rand_ZeroOne() - 0.5f) * 90.0f) + this->dyna.actor.world.pos.x; + sp8C.y = (this->dyna.actor.world.pos.y - (Rand_ZeroOne() * 80.0f)) - 20.0f; + sp8C.z = this->dyna.actor.world.pos.z - (Rand_ZeroOne() - 0.5f) * 50.0f; + BgJyaMegami_SpawnEffect(globalCtx, &sp8C, &sVec, 1, 0); + } + if (this->explosionTimer < ARRAY_COUNT(this->pieces)) { + sp8C.x = this->dyna.actor.world.pos.x; + sp8C.y = this->dyna.actor.world.pos.y - 60.0f; + sp8C.z = this->dyna.actor.world.pos.z; + func_80033480(globalCtx, &sp8C, 100.0f, 1, 150, 100, 1); + } + if (this->explosionTimer == 60) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + if (this->explosionTimer >= 100) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgJyaMegami_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaMegami* this = (BgJyaMegami*)thisx; + + this->actionFunc(this, globalCtx); +} + +static void* sRightSideCrumbles[] = { + gMegamiRightCrumble1Tex, gMegamiRightCrumble2Tex, gMegamiRightCrumble3Tex, + gMegamiRightCrumble4Tex, gMegamiRightCrumble5Tex, +}; + +static void* sLeftSideCrumbles[] = { + gMegamiLeftCrumble1Tex, gMegamiLeftCrumble2Tex, gMegamiLeftCrumble3Tex, + gMegamiLeftCrumble4Tex, gMegamiLeftCrumble5Tex, +}; + +void BgJyaMegami_DrawFace(BgJyaMegami* this, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_megami.c", 706); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sRightSideCrumbles[this->crumbleIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sLeftSideCrumbles[this->crumbleIndex])); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_megami.c", 716), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gMegami1DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_megami.c", 720); +} + +static Gfx* sDLists[] = { + gMegamiPiece1DL, gMegamiPiece2DL, gMegamiPiece3DL, gMegamiPiece4DL, gMegamiPiece5DL, + gMegamiPiece6DL, gMegamiPiece7DL, gMegamiPiece8DL, gMegamiPiece9DL, gMegamiPiece10DL, + gMegamiPiece11DL, gMegamiPiece12DL, gMegamiPiece13DL, +}; + +void BgJyaMegami_DrawExplode(BgJyaMegami* this, GlobalContext* globalCtx) { + s32 pad; + BgJyaMegamiPiece* piece; + u32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_megami.c", 746); + + func_80093D18(globalCtx->state.gfxCtx); + + for (i = 0; i < ARRAY_COUNT(this->pieces); i++) { + piece = &this->pieces[i]; + Matrix_Translate(piece->pos.x + sPiecesInit[i].unk_00.x, piece->pos.y + sPiecesInit[i].unk_00.y, + piece->pos.z + sPiecesInit[i].unk_00.z, MTXMODE_NEW); + Matrix_RotateY(piece->rotVelY * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(piece->rotVelX * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + Matrix_Translate(sPiecesInit[i].unk_00.x * -10.0f, sPiecesInit[i].unk_00.y * -10.0f, + sPiecesInit[i].unk_00.z * -10.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_jya_megami.c", 778), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sDLists[i]); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_jya_megami.c", 783); +} + +void BgJyaMegami_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgJyaMegami* this = (BgJyaMegami*)thisx; + + Collider_UpdateSpheres(0, &this->collider); + if (this->actionFunc == BgJyaMegami_Explode) { + BgJyaMegami_DrawExplode(this, globalCtx); + } else { + BgJyaMegami_DrawFace(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h b/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h new file mode 100644 index 000000000..f4a859c31 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Megami/z_bg_jya_megami.h @@ -0,0 +1,29 @@ +#ifndef Z_BG_JYA_MEGAMI_H +#define Z_BG_JYA_MEGAMI_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaMegami; + +typedef void (*BgJyaMegamiActionFunc)(struct BgJyaMegami*, GlobalContext*); + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f vel; + /* 0x18 */ s16 rotVelX; + /* 0x1A */ s16 rotVelY; +} BgJyaMegamiPiece; // size = 0x1C + +typedef struct BgJyaMegami { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgJyaMegamiActionFunc actionFunc; + /* 0x0168 */ ColliderJntSph collider; + /* 0x0188 */ ColliderJntSphElement colliderItem; + /* 0x01C8 */ s16 lightTimer; + /* 0x01CA */ s16 explosionTimer; + /* 0x01CC */ s16 crumbleIndex; + /* 0x01D0 */ BgJyaMegamiPiece pieces[13]; +} BgJyaMegami; // size = 0x033C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Zurerukabe/z_bg_jya_zurerukabe.c b/soh/src/overlays/actors/ovl_Bg_Jya_Zurerukabe/z_bg_jya_zurerukabe.c new file mode 100644 index 000000000..050effb7d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Zurerukabe/z_bg_jya_zurerukabe.c @@ -0,0 +1,190 @@ +/* + * File: z_bg_jya_zurerukabe.c + * Overlay: ovl_Bg_Jya_Zurerukabe + * Description: Sliding, Climbable Brick Wall + */ + +#include "z_bg_jya_zurerukabe.h" +#include "objects/object_jya_obj/object_jya_obj.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgJyaZurerukabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgJyaZurerukabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgJyaZurerukabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgJyaZurerukabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8089B4C8(BgJyaZurerukabe* this, GlobalContext* globalCtx); +void func_8089B7B4(BgJyaZurerukabe* this); +void func_8089B7C4(BgJyaZurerukabe* this, GlobalContext* globalCtx); +void func_8089B80C(BgJyaZurerukabe* this); +void func_8089B870(BgJyaZurerukabe* this, GlobalContext* globalCtx); + +static f32 D_8089B9C0[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + +const ActorInit Bg_Jya_Zurerukabe_InitVars = { + ACTOR_BG_JYA_ZURERUKABE, + ACTORCAT_BG, + FLAGS, + OBJECT_JYA_OBJ, + sizeof(BgJyaZurerukabe), + (ActorFunc)BgJyaZurerukabe_Init, + (ActorFunc)BgJyaZurerukabe_Destroy, + (ActorFunc)BgJyaZurerukabe_Update, + (ActorFunc)BgJyaZurerukabe_Draw, + NULL, +}; + +static s16 D_8089B9F0[4] = { 943, 1043, 1243, 1343 }; + +static s16 D_8089B9F8[4] = { -1, 1, -1, 1 }; + +static s16 D_8089BA00[4] = { 48, 48, 36, 36 }; + +static f32 D_8089BA08[4] = { 8.0f, 8.0f, 10.0f, 10.0f }; + +static s16 D_8089BA18[6][2] = { + { 0x0388, 0x0395 }, { 0x03EA, 0x03FF }, { 0x0454, 0x0467 }, + { 0x04B4, 0x04C1 }, { 0x0518, 0x0528 }, { 0x0581, 0x0590 }, +}; + +static s16 D_8089BA30[6] = { + 0, 0, 1, 2, 2, 3, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgJyaZurerukabe_InitDynaPoly(BgJyaZurerukabe* this, GlobalContext* globalCtx, CollisionHeader* collision, + s32 flag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_jya_zurerukabe.c", 194, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void func_8089B4C8(BgJyaZurerukabe* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags1 == 0x200000) && (player->actor.wallPoly != NULL)) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(D_8089BA18); i++) { + f32 posY = player->actor.world.pos.y; + + if ((posY >= D_8089BA18[i][0]) && (posY <= D_8089BA18[i][1])) { + break; + } + } + + switch (i) { + case 0: + case 2: + case 3: + case 5: + if (fabsf(D_8089B9C0[D_8089BA30[i]]) > 1.0f) { + func_8002F6D4(globalCtx, &this->dyna.actor, 1.5f, this->dyna.actor.shape.rot.y, 0.0f, 0); + } + break; + case 1: + case 4: + if (fabsf(D_8089B9C0[D_8089BA30[i]] - D_8089B9C0[D_8089BA30[i + 1]]) > 1.0f) { + func_8002F6D4(globalCtx, &this->dyna.actor, 1.5f, this->dyna.actor.shape.rot.y, 0.0f, 0); + } + break; + } + } +} + +void BgJyaZurerukabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgJyaZurerukabe* this = (BgJyaZurerukabe*)thisx; + s32 i; + + BgJyaZurerukabe_InitDynaPoly(this, globalCtx, &gZurerukabeCol, DPM_UNK); + Actor_ProcessInitChain(thisx, sInitChain); + + for (i = 0; i < ARRAY_COUNT(D_8089B9F0); i++) { + if (fabsf(D_8089B9F0[i] - this->dyna.actor.home.pos.y) < 1.0f) { + this->unk_168 = i; + break; + } + } + + if (i == ARRAY_COUNT(D_8089B9F0)) { + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("home pos が変更されたみたい(%s %d)(arg_data 0x%04x)\n", "../z_bg_jya_zurerukabe.c", 299, + this->dyna.actor.params); + osSyncPrintf(VT_RST); + } + + this->unk_16E = D_8089B9F8[this->unk_168]; + func_8089B7B4(this); + osSyncPrintf("(jya ずれる壁)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgJyaZurerukabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgJyaZurerukabe* this = (BgJyaZurerukabe*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + D_8089B9C0[this->unk_168] = 0.0f; +} + +void func_8089B7B4(BgJyaZurerukabe* this) { + this->actionFunc = func_8089B7C4; +} + +void func_8089B7C4(BgJyaZurerukabe* this, GlobalContext* globalCtx) { + if (this->unk_16A <= 0) { + func_8089B80C(this); + } + D_8089B9C0[this->unk_168] = 0.0f; +} + +void func_8089B80C(BgJyaZurerukabe* this) { + this->actionFunc = func_8089B870; + this->unk_16A = D_8089BA00[this->unk_168]; + if (ABS(this->unk_16C) == 4) { + this->unk_16E = -this->unk_16E; + } + this->unk_16C += this->unk_16E; +} + +void func_8089B870(BgJyaZurerukabe* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.x, this->dyna.actor.home.pos.x + (this->unk_16C * 75), + D_8089BA08[this->unk_168])) { + func_8089B7B4(this); + } + + D_8089B9C0[this->unk_168] = D_8089BA08[this->unk_168] * this->unk_16E; + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); +} + +void BgJyaZurerukabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgJyaZurerukabe* this = (BgJyaZurerukabe*)thisx; + + if (this->unk_16A > 0) { + this->unk_16A--; + } + + this->actionFunc(this, globalCtx); + + if (this->unk_168 == 0) { + func_8089B4C8(this, globalCtx); + } +} + +void BgJyaZurerukabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gZurerukabeDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Jya_Zurerukabe/z_bg_jya_zurerukabe.h b/soh/src/overlays/actors/ovl_Bg_Jya_Zurerukabe/z_bg_jya_zurerukabe.h new file mode 100644 index 000000000..d2cb8ff97 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Jya_Zurerukabe/z_bg_jya_zurerukabe.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_JYA_ZURERUKABE_H +#define Z_BG_JYA_ZURERUKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgJyaZurerukabe; + +typedef void (*BgJyaZurerukabeActionFunc)(struct BgJyaZurerukabe*, GlobalContext*); + +typedef struct BgJyaZurerukabe { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgJyaZurerukabeActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ s16 unk_16A; + /* 0x016C */ s16 unk_16C; + /* 0x016E */ s16 unk_16E; +} BgJyaZurerukabe; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Menkuri_Eye/z_bg_menkuri_eye.c b/soh/src/overlays/actors/ovl_Bg_Menkuri_Eye/z_bg_menkuri_eye.c new file mode 100644 index 000000000..70360c423 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Menkuri_Eye/z_bg_menkuri_eye.c @@ -0,0 +1,147 @@ +/* + * File: z_bg_menkuri_eye.c + * Overlay: ovl_Bg_Menkuri_Eye + * Description: Eye platform eye switches + */ + +#include "z_bg_menkuri_eye.h" +#include "objects/object_menkuri_objects/object_menkuri_objects.h" + +#define FLAGS ACTOR_FLAG_5 + +void BgMenkuriEye_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriEye_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriEye_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriEye_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriEye_Reset(void); + +const ActorInit Bg_Menkuri_Eye_InitVars = { + ACTOR_BG_MENKURI_EYE, + ACTORCAT_BG, + FLAGS, + OBJECT_MENKURI_OBJECTS, + sizeof(BgMenkuriEye), + (ActorFunc)BgMenkuriEye_Init, + (ActorFunc)BgMenkuriEye_Destroy, + (ActorFunc)BgMenkuriEye_Update, + (ActorFunc)BgMenkuriEye_Draw, + (ActorResetFunc)BgMenkuriEye_Reset, +}; + +static s32 D_8089C1A0; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK4, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F820, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 1, { { 0, 0, 0 }, 14 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgMenkuriEye_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriEye* this = (BgMenkuriEye*)thisx; + ColliderJntSphElement* colliderList; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + this->collider.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x; + this->collider.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y; + this->collider.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z; + colliderList = this->collider.elements; + colliderList->dim.worldSphere.radius = colliderList->dim.modelSphere.radius; + if (!Flags_GetSwitch(globalCtx, this->actor.params)) { + D_8089C1A0 = 0; + } + this->framesUntilDisable = -1; +} + +void BgMenkuriEye_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriEye* this = (BgMenkuriEye*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgMenkuriEye_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriEye* this = (BgMenkuriEye*)thisx; + + if (!Flags_GetSwitch(globalCtx, this->actor.params)) { + if (this->framesUntilDisable != -1) { + if (this->framesUntilDisable != 0) { + this->framesUntilDisable -= 1; + } + if (this->framesUntilDisable == 0) { + this->framesUntilDisable = -1; + D_8089C1A0 -= 1; + } + } + } + if ((this->collider.base.acFlags & AC_HIT) && + (ABS((s16)(this->collider.base.ac->world.rot.y - this->actor.shape.rot.y)) > 0x5000)) { + this->collider.base.acFlags &= ~AC_HIT; + if (this->framesUntilDisable == -1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_AMOS_DAMAGE); + D_8089C1A0 += 1; + D_8089C1A0 = CLAMP_MAX(D_8089C1A0, 4); + } + this->framesUntilDisable = 416; + if (D_8089C1A0 == 4) { + Flags_SetSwitch(globalCtx, this->actor.params); + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + } + if (this->framesUntilDisable == -1) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Actor_SetFocus(&this->actor, 0.0f); +} + +void BgMenkuriEye_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriEye* this = (BgMenkuriEye*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_menkuri_eye.c", 292); + func_80093D84(globalCtx->state.gfxCtx); + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + gDPSetEnvColor(POLY_XLU_DISP++, 200, 0, 0, 255); + } else if (this->framesUntilDisable == -1) { + gDPSetEnvColor(POLY_XLU_DISP++, 200, 0, 0, 0); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 200, 0, 0, 255); + } + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_menkuri_eye.c", 331), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gGTGEyeStatueEyeDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_menkuri_eye.c", 335); +} + +void BgMenkuriEye_Reset(void) { + D_8089C1A0 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Menkuri_Eye/z_bg_menkuri_eye.h b/soh/src/overlays/actors/ovl_Bg_Menkuri_Eye/z_bg_menkuri_eye.h new file mode 100644 index 000000000..cf27ca182 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Menkuri_Eye/z_bg_menkuri_eye.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_MENKURI_EYE_H +#define Z_BG_MENKURI_EYE_H + +#include "ultra64.h" +#include "global.h" + +struct BgMenkuriEye; + +typedef struct BgMenkuriEye { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 framesUntilDisable; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItems[1]; +} BgMenkuriEye; // size = 0x01B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Menkuri_Kaiten/z_bg_menkuri_kaiten.c b/soh/src/overlays/actors/ovl_Bg_Menkuri_Kaiten/z_bg_menkuri_kaiten.c new file mode 100644 index 000000000..5df145a76 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Menkuri_Kaiten/z_bg_menkuri_kaiten.c @@ -0,0 +1,62 @@ +/* + * File: z_bg_menkuri_kaiten.c + * Overlay: Bg_Menkuri_Kaiten + * Description: Large rotating stone ring used in Gerudo Training Grounds and Forest Temple. + */ + +#include "z_bg_menkuri_kaiten.h" +#include "objects/object_menkuri_objects/object_menkuri_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgMenkuriKaiten_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriKaiten_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriKaiten_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriKaiten_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Menkuri_Kaiten_InitVars = { + ACTOR_BG_MENKURI_KAITEN, + ACTORCAT_BG, + FLAGS, + OBJECT_MENKURI_OBJECTS, + sizeof(BgMenkuriKaiten), + (ActorFunc)BgMenkuriKaiten_Init, + (ActorFunc)BgMenkuriKaiten_Destroy, + (ActorFunc)BgMenkuriKaiten_Update, + (ActorFunc)BgMenkuriKaiten_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgMenkuriKaiten_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriKaiten* this = (BgMenkuriKaiten*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK3); + CollisionHeader_GetVirtual(&gGTGRotatingRingPlatformCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgMenkuriKaiten_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriKaiten* this = (BgMenkuriKaiten*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgMenkuriKaiten_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriKaiten* this = (BgMenkuriKaiten*)thisx; + + if (!Flags_GetSwitch(globalCtx, this->dyna.actor.params) && func_80043590(&this->dyna)) { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + this->dyna.actor.shape.rot.y += 0x80; + } +} + +void BgMenkuriKaiten_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gGTGRotatingRingPlatformDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Menkuri_Kaiten/z_bg_menkuri_kaiten.h b/soh/src/overlays/actors/ovl_Bg_Menkuri_Kaiten/z_bg_menkuri_kaiten.h new file mode 100644 index 000000000..c65b08d6e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Menkuri_Kaiten/z_bg_menkuri_kaiten.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_MENKURI_KAITEN_H +#define Z_BG_MENKURI_KAITEN_H + +#include "ultra64.h" +#include "global.h" + +struct BgMenkuriKaiten; + +typedef struct BgMenkuriKaiten { + /* 0x0000 */ DynaPolyActor dyna; +} BgMenkuriKaiten; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Menkuri_Nisekabe/z_bg_menkuri_nisekabe.c b/soh/src/overlays/actors/ovl_Bg_Menkuri_Nisekabe/z_bg_menkuri_nisekabe.c new file mode 100644 index 000000000..d3c91fc36 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Menkuri_Nisekabe/z_bg_menkuri_nisekabe.c @@ -0,0 +1,60 @@ +/* + * File: z_bg_menkuri_nisekabe.c + * Overlay: ovl_Bg_Menkuri_Nisekabe + * Description: False Stone Walls (Gerudo Training Grounds) + */ + +#include "z_bg_menkuri_nisekabe.h" +#include "objects/object_menkuri_objects/object_menkuri_objects.h" + +#define FLAGS 0 + +void BgMenkuriNisekabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriNisekabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriNisekabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMenkuriNisekabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Menkuri_Nisekabe_InitVars = { + ACTOR_BG_MENKURI_NISEKABE, + ACTORCAT_PROP, + FLAGS, + OBJECT_MENKURI_OBJECTS, + sizeof(BgMenkuriNisekabe), + (ActorFunc)BgMenkuriNisekabe_Init, + (ActorFunc)BgMenkuriNisekabe_Destroy, + (ActorFunc)BgMenkuriNisekabe_Update, + (ActorFunc)BgMenkuriNisekabe_Draw, + NULL, +}; + +static Gfx* sDLists[] = { gGTGFakeWallDL, gGTGFakeCeilingDL }; + +void BgMenkuriNisekabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriNisekabe* this = (BgMenkuriNisekabe*)thisx; + + Actor_SetScale(&this->actor, 0.1f); +} + +void BgMenkuriNisekabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgMenkuriNisekabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriNisekabe* this = (BgMenkuriNisekabe*)thisx; + + if (globalCtx->actorCtx.unk_03 != 0) { + this->actor.flags |= ACTOR_FLAG_7; + } else { + this->actor.flags &= ~ACTOR_FLAG_7; + } +} + +void BgMenkuriNisekabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgMenkuriNisekabe* this = (BgMenkuriNisekabe*)thisx; + u32 index = this->actor.params & 0xFF; + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + Gfx_DrawDListXlu(globalCtx, sDLists[index]); + } else { + Gfx_DrawDListOpa(globalCtx, sDLists[index]); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Menkuri_Nisekabe/z_bg_menkuri_nisekabe.h b/soh/src/overlays/actors/ovl_Bg_Menkuri_Nisekabe/z_bg_menkuri_nisekabe.h new file mode 100644 index 000000000..f7cb56a5b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Menkuri_Nisekabe/z_bg_menkuri_nisekabe.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_MENKURI_NISEKABE_H +#define Z_BG_MENKURI_NISEKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgMenkuriNisekabe; + +typedef struct BgMenkuriNisekabe { + /* 0x0000 */ Actor actor; +} BgMenkuriNisekabe; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Bwall/z_bg_mizu_bwall.c b/soh/src/overlays/actors/ovl_Bg_Mizu_Bwall/z_bg_mizu_bwall.c new file mode 100644 index 000000000..0a4de80ce --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Bwall/z_bg_mizu_bwall.c @@ -0,0 +1,531 @@ +/* + * File: z_bg_mizu_bwall.c + * Overlay: ovl_Bg_Mizu_Bwall + * Description: Water Temple bombable walls + */ + +#include "z_bg_mizu_bwall.h" +#include "overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgMizuBwall_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMizuBwall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMizuBwall_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMizuBwall_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMizuBwall_Idle(BgMizuBwall* this, GlobalContext* globalCtx); +void BgMizuBwall_Break(BgMizuBwall* this, GlobalContext* globalCtx); +void BgMizuBwall_DoNothing(BgMizuBwall* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mizu_Bwall_InitVars = { + ACTOR_BG_MIZU_BWALL, + ACTORCAT_BG, + FLAGS, + OBJECT_MIZU_OBJECTS, + sizeof(BgMizuBwall), + (ActorFunc)BgMizuBwall_Init, + (ActorFunc)BgMizuBwall_Destroy, + (ActorFunc)BgMizuBwall_Update, + (ActorFunc)BgMizuBwall_Draw, + NULL, +}; + +static ColliderTrisElementInit sTrisElementInitFloor[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -40.0f, 0.0f, -40.0f }, { -40.0f, 0.0f, 40.0f }, { 40.0f, 0.0f, 40.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -40.0f, 0.0f, -40.0f }, { 40.0f, 0.0f, 40.0f }, { 40.0f, 0.0f, -40.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInitFloor = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sTrisElementInitFloor, +}; + +static ColliderTrisElementInit sTrisElementInitRutoWall[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 116.0f, 0.0f }, { 0.0f, 0.0f, 70.0f }, { 0.0f, 0.0f, -70.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInitRutoWall = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 1, + sTrisElementInitRutoWall, +}; + +static ColliderTrisElementInit sTrisElementInitWall[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 120.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 60.0f, 0.0f, 0.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 120.0f, 0.0f }, { 60.0f, 0.0f, 0.0f }, { 60.0f, 120.0f, 0.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInitUnusedWall = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sTrisElementInitWall, +}; + +static ColliderTrisInit sTrisInitStingerWall = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sTrisElementInitWall, +}; + +static Gfx* sDLists[] = { + gObjectMizuObjectsBwallDL_001A30, gObjectMizuObjectsBwallDL_002390, gObjectMizuObjectsBwallDL_001CD0, + gObjectMizuObjectsBwallDL_002090, gObjectMizuObjectsBwallDL_001770, +}; +static CollisionHeader* sColHeaders[] = { + &gObjectMizuObjectsBwallCol_001C58, &gObjectMizuObjectsBwallCol_0025A4, &gObjectMizuObjectsBwallCol_001DE8, + &gObjectMizuObjectsBwallCol_001DE8, &gObjectMizuObjectsBwallCol_001DE8, +}; + +static InitChainEntry D_8089D854[] = { + ICHAIN_F32(uncullZoneScale, 1500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgMizuBwall_RotateVec3f(Vec3f* out, Vec3f* in, f32 sin, f32 cos) { + out->x = (in->z * sin) + (in->x * cos); + out->y = in->y; + out->z = (in->z * cos) - (in->x * sin); +} + +void BgMizuBwall_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuBwall* this = (BgMizuBwall*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, D_8089D854); + this->yRot = this->dyna.actor.world.pos.y; + this->dList = sDLists[(u16)this->dyna.actor.params & 0xF]; + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(sColHeaders[(u16)this->dyna.actor.params & 0xF], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + switch ((u16)this->dyna.actor.params & 0xF) { + case MIZUBWALL_FLOOR: + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 8) & 0x3F)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dList = NULL; + this->actionFunc = BgMizuBwall_DoNothing; + } else { + Collider_InitTris(globalCtx, &this->collider); + if (!Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInitFloor, this->elements)) { + osSyncPrintf("Error : コリジョンデータセット失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_mizu_bwall.c", + 484, this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + } else { + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + s32 i; + s32 j; + Vec3f offset; + Vec3f vtx[3]; + + for (i = 0; i < ARRAY_COUNT(sTrisElementInitFloor); i++) { + for (j = 0; j < 3; j++) { + offset.x = sTrisInitFloor.elements[i].dim.vtx[j].x; + offset.y = sTrisInitFloor.elements[i].dim.vtx[j].y; + offset.z = sTrisInitFloor.elements[i].dim.vtx[j].z + 2.0f; + BgMizuBwall_RotateVec3f(&vtx[j], &offset, sin, cos); + vtx[j].x += this->dyna.actor.world.pos.x; + vtx[j].y += this->dyna.actor.world.pos.y; + vtx[j].z += this->dyna.actor.world.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &vtx[0], &vtx[1], &vtx[2]); + } + this->actionFunc = BgMizuBwall_Idle; + } + } + break; + case MIZUBWALL_RUTO_ROOM: + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 8) & 0x3F)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dList = NULL; + this->actionFunc = BgMizuBwall_DoNothing; + } else { + Collider_InitTris(globalCtx, &this->collider); + if (!Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInitRutoWall, + this->elements)) { + osSyncPrintf("Error : コリジョンデータセット失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_mizu_bwall.c", + 558, this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + } else { + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + s32 i; + s32 j; + Vec3f offset; + Vec3f vtx[3]; + + for (i = 0; i < ARRAY_COUNT(sTrisElementInitRutoWall); i++) { + for (j = 0; j < 3; j++) { + offset.x = sTrisInitRutoWall.elements[i].dim.vtx[j].x; + offset.y = sTrisInitRutoWall.elements[i].dim.vtx[j].y; + offset.z = sTrisInitRutoWall.elements[i].dim.vtx[j].z + 2.0f; + BgMizuBwall_RotateVec3f(&vtx[j], &offset, sin, cos); + vtx[j].x += this->dyna.actor.world.pos.x; + vtx[j].y += this->dyna.actor.world.pos.y; + vtx[j].z += this->dyna.actor.world.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &vtx[0], &vtx[1], &vtx[2]); + } + this->actionFunc = BgMizuBwall_Idle; + } + } + break; + case MIZUBWALL_UNUSED: + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 8) & 0x3F)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dList = NULL; + this->actionFunc = BgMizuBwall_DoNothing; + } else { + Collider_InitTris(globalCtx, &this->collider); + if (!Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInitUnusedWall, + this->elements)) { + osSyncPrintf("Error : コリジョンデータセット失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_mizu_bwall.c", + 638, this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + } else { + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + s32 i; + s32 j; + Vec3f offset; + Vec3f vtx[3]; + + for (i = 0; i < ARRAY_COUNT(sTrisElementInitFloor); i++) { + for (j = 0; j < 3; j++) { + //! @bug This uses the wrong set of collision triangles, causing the collider to be + //! flat to the ground instead of vertical. It should use sTrisInitUnusedWall. + offset.x = sTrisInitFloor.elements[i].dim.vtx[j].x; + offset.y = sTrisInitFloor.elements[i].dim.vtx[j].y; + offset.z = sTrisInitFloor.elements[i].dim.vtx[j].z; + BgMizuBwall_RotateVec3f(&vtx[j], &offset, sin, cos); + vtx[j].x += this->dyna.actor.world.pos.x; + vtx[j].y += this->dyna.actor.world.pos.y; + vtx[j].z += this->dyna.actor.world.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &vtx[0], &vtx[1], &vtx[2]); + } + this->actionFunc = BgMizuBwall_Idle; + } + } + break; + case MIZUBWALL_STINGER_ROOM_1: + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 8) & 0x3F)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dList = NULL; + this->actionFunc = BgMizuBwall_DoNothing; + } else { + Collider_InitTris(globalCtx, &this->collider); + if (!Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInitStingerWall, + this->elements)) { + osSyncPrintf("Error : コリジョンデータセット失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_mizu_bwall.c", + 724, this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + } else { + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + s32 i; + s32 j; + Vec3f offset; + Vec3f vtx[3]; + + for (i = 0; i < ARRAY_COUNT(sTrisElementInitFloor); i++) { + for (j = 0; j < 3; j++) { + //! @bug This uses the wrong set of collision triangles, causing the collider to be + //! flat to the ground instead of vertical. It should use sTrisInitStingerWall. + offset.x = sTrisInitFloor.elements[i].dim.vtx[j].x; + offset.y = sTrisInitFloor.elements[i].dim.vtx[j].y; + offset.z = sTrisInitFloor.elements[i].dim.vtx[j].z + 2.0f; + BgMizuBwall_RotateVec3f(&vtx[j], &offset, sin, cos); + vtx[j].x += this->dyna.actor.world.pos.x; + vtx[j].y += this->dyna.actor.world.pos.y; + vtx[j].z += this->dyna.actor.world.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &vtx[0], &vtx[1], &vtx[2]); + } + this->actionFunc = BgMizuBwall_Idle; + } + } + break; + case MIZUBWALL_STINGER_ROOM_2: + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 8) & 0x3F)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dList = NULL; + this->actionFunc = BgMizuBwall_DoNothing; + } else { + Collider_InitTris(globalCtx, &this->collider); + if (!Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInitStingerWall, + this->elements)) { + osSyncPrintf("Error : コリジョンデータセット失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_mizu_bwall.c", + 798, this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + } else { + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + s32 i; + s32 j; + Vec3f offset; + Vec3f vtx[3]; + + for (i = 0; i < ARRAY_COUNT(sTrisElementInitFloor); i++) { + for (j = 0; j < 3; j++) { + //! @bug This uses the wrong set of collision triangles, causing the collider to be + //! flat to the ground instead of vertical. It should use sTrisInitStingerWall. + offset.x = sTrisInitFloor.elements[i].dim.vtx[j].x; + offset.y = sTrisInitFloor.elements[i].dim.vtx[j].y; + offset.z = sTrisInitFloor.elements[i].dim.vtx[j].z + 2.0f; + BgMizuBwall_RotateVec3f(&vtx[j], &offset, sin, cos); + vtx[j].x += this->dyna.actor.world.pos.x; + vtx[j].y += this->dyna.actor.world.pos.y; + vtx[j].z += this->dyna.actor.world.pos.z; + } + Collider_SetTrisVertices(&this->collider, i, &vtx[0], &vtx[1], &vtx[2]); + } + this->actionFunc = BgMizuBwall_Idle; + } + } + break; + } +} + +void BgMizuBwall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuBwall* this = (BgMizuBwall*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyTris(globalCtx, &this->collider); +} + +void BgMizuBwall_SetAlpha(BgMizuBwall* this, GlobalContext* globalCtx) { + f32 waterLevel = globalCtx->colCtx.colHeader->waterBoxes[2].ySurface; + + if (globalCtx->colCtx.colHeader->waterBoxes) {} + + if (waterLevel < WATER_TEMPLE_WATER_F1_Y) { + this->scrollAlpha1 = 255; + } else if (waterLevel < WATER_TEMPLE_WATER_F2_Y) { + this->scrollAlpha1 = 255 - (s32)((waterLevel - WATER_TEMPLE_WATER_F1_Y) / + (WATER_TEMPLE_WATER_F2_Y - WATER_TEMPLE_WATER_F1_Y) * (255 - 160)); + } else { + this->scrollAlpha1 = 160; + } + + if (waterLevel < WATER_TEMPLE_WATER_F2_Y) { + this->scrollAlpha2 = 255; + } else if (waterLevel < WATER_TEMPLE_WATER_F3_Y) { + this->scrollAlpha2 = 255 - (s32)((waterLevel - WATER_TEMPLE_WATER_F2_Y) / + (WATER_TEMPLE_WATER_F3_Y - WATER_TEMPLE_WATER_F2_Y) * (255 - 160)); + } else { + this->scrollAlpha2 = 160; + } + + if (waterLevel < WATER_TEMPLE_WATER_B1_Y) { + this->scrollAlpha3 = 255; + } else if (waterLevel < WATER_TEMPLE_WATER_F1_Y) { + this->scrollAlpha3 = 255 - (s32)((waterLevel - WATER_TEMPLE_WATER_B1_Y) / + (WATER_TEMPLE_WATER_F1_Y - WATER_TEMPLE_WATER_B1_Y) * (255 - 160)); + } else { + this->scrollAlpha3 = 160; + } + + this->scrollAlpha4 = this->scrollAlpha3; +} + +void BgMizuBwall_SpawnDebris(BgMizuBwall* this, GlobalContext* globalCtx) { + s32 i; + s32 pad; + s16 rand1; + s16 rand2; + Vec3f* thisPos = &this->dyna.actor.world.pos; + Vec3f debrisPos; + f32 tempx; + f32 tempz; + f32 sin = Math_SinS(this->dyna.actor.shape.rot.y); + f32 cos = Math_CosS(this->dyna.actor.shape.rot.y); + Vec3f debrisOffsets[15]; + + for (i = 0; i < ARRAY_COUNT(debrisOffsets); i++) { + switch ((u16)this->dyna.actor.params & 0xF) { + case MIZUBWALL_FLOOR: + debrisOffsets[i].x = (Rand_ZeroOne() * 80.0f) - 40.0f; + debrisOffsets[i].y = Rand_ZeroOne() * 0; + debrisOffsets[i].z = (Rand_ZeroOne() * 80.0f) - 40.0f; + break; + case MIZUBWALL_RUTO_ROOM: + debrisOffsets[i].x = Rand_ZeroOne() * 0; + debrisOffsets[i].y = Rand_ZeroOne() * 100.0f; + debrisOffsets[i].z = (Rand_ZeroOne() * 80.0f) - 40.0f; + break; + case MIZUBWALL_UNUSED: + case MIZUBWALL_STINGER_ROOM_1: + default: + debrisOffsets[i].x = (Rand_ZeroOne() * 120) - 60.0f; + debrisOffsets[i].y = Rand_ZeroOne() * 120; + debrisOffsets[i].z = Rand_ZeroOne() * 0; + break; + } + } + + for (i = 0; i < ARRAY_COUNT(debrisOffsets); i++) { + tempx = debrisOffsets[i].x; + tempz = debrisOffsets[i].z; + + debrisPos.x = thisPos->x + tempz * sin + tempx * cos; + debrisPos.y = thisPos->y + debrisOffsets[i].y; + debrisPos.z = thisPos->z + tempz * cos - tempx * sin; + + rand1 = (s16)(Rand_ZeroOne() * 120.0f) + 20; + rand2 = (s16)(Rand_ZeroOne() * 240.0f) + 20; + func_80033480(globalCtx, &debrisPos, 50.0f, 2, rand1, rand2, 0); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_A_OBJ, debrisPos.x, debrisPos.y, debrisPos.z, 0, 0, 0, + 0xB); + } +} + +void BgMizuBwall_Idle(BgMizuBwall* this, GlobalContext* globalCtx) { + BgMizuBwall_SetAlpha(this, globalCtx); + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Flags_SetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 8) & 0x3F); + this->breakTimer = 1; + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dList = NULL; + BgMizuBwall_SpawnDebris(this, globalCtx); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WALL_BROKEN); + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->actionFunc = BgMizuBwall_Break; + } else if (this->dyna.actor.xzDistToPlayer < 600.0f) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgMizuBwall_Break(BgMizuBwall* this, GlobalContext* globalCtx) { + if (this->breakTimer > 0) { + this->breakTimer--; + } else { + this->actionFunc = BgMizuBwall_DoNothing; + } +} + +void BgMizuBwall_DoNothing(BgMizuBwall* this, GlobalContext* globalCtx) { +} + +void BgMizuBwall_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuBwall* this = (BgMizuBwall*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMizuBwall_Draw(Actor* thisx, GlobalContext* globalCtx2) { + BgMizuBwall* this = (BgMizuBwall*)thisx; + GlobalContext* globalCtx = globalCtx2; + u32 frames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_bwall.c", 1095); + if (1) {} + frames = globalCtx->gameplayFrames; + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, 1 * frames, 0, 0x20, 0x20, 1, 0, 0, 0x20, 0x20, 0, + 0, 0, this->scrollAlpha1)); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, 1 * frames, 0, 0x20, 0x20, 1, 0, 0, 0x20, 0x20, 0, + 0, 0, this->scrollAlpha2)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, 1 * frames, 0, 0x20, 0x20, 1, 0, 0, 0x20, 0x20, 0, + 0, 0, this->scrollAlpha3)); + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, 3 * frames, 0, 0x20, 0x20, 1, 0, 0, 0x20, 0x20, 0, + 0, 0, this->scrollAlpha4)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mizu_bwall.c", 1129), 2); + + if (this->dList != NULL) { + gSPDisplayList(POLY_OPA_DISP++, this->dList); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_bwall.c", 1136); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Bwall/z_bg_mizu_bwall.h b/soh/src/overlays/actors/ovl_Bg_Mizu_Bwall/z_bg_mizu_bwall.h new file mode 100644 index 000000000..b4172f8d5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Bwall/z_bg_mizu_bwall.h @@ -0,0 +1,34 @@ +#ifndef Z_BG_MIZU_BWALL_H +#define Z_BG_MIZU_BWALL_H + +#include "ultra64.h" +#include "global.h" + +struct BgMizuBwall; + +typedef void (*BgMizuBwallActionFunc)(struct BgMizuBwall*, GlobalContext*); + +typedef struct BgMizuBwall { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderTris collider; + /* 0x0184 */ ColliderTrisElement elements[3]; // only ever uses 2 + /* 0x0298 */ BgMizuBwallActionFunc actionFunc; + /* 0x029C */ f32 yRot; + /* 0x02A0 */ s32 scrollAlpha1; + /* 0x02A4 */ s32 scrollAlpha2; + /* 0x02A8 */ s32 scrollAlpha3; + /* 0x02AC */ s32 scrollAlpha4; + /* 0x02B0 */ s32 breakTimer; + /* 0x02B4 */ char unk_2B4[4]; + /* 0x02B8 */ Gfx* dList; +} BgMizuBwall; // size = 0x02BC + +typedef enum { + MIZUBWALL_FLOOR, + MIZUBWALL_RUTO_ROOM, + MIZUBWALL_UNUSED, + MIZUBWALL_STINGER_ROOM_1, + MIZUBWALL_STINGER_ROOM_2 +} BgMizuBwallType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Movebg/z_bg_mizu_movebg.c b/soh/src/overlays/actors/ovl_Bg_Mizu_Movebg/z_bg_mizu_movebg.c new file mode 100644 index 000000000..4b5c1c35b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Movebg/z_bg_mizu_movebg.c @@ -0,0 +1,397 @@ +/* + * File: z_bg_mizu_movebg.c + * Overlay: ovl_Bg_Mizu_Movebg + * Description: Kakariko Village Well Water + */ + +#include "z_bg_mizu_movebg.h" +#include "overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +#define MOVEBG_TYPE(params) (((u16)(params) >> 0xC) & 0xF) +#define MOVEBG_FLAGS(params) ((u16)(params)&0x3F) +#define MOVEBG_PATH_ID(params) (((u16)(params) >> 0x8) & 0xF) +#define MOVEBG_POINT_ID(params) ((u16)(params)&0xF) +#define MOVEBG_SPEED(params) (((u16)(params) >> 0x4) & 0xF) + +void BgMizuMovebg_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMizuMovebg_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMizuMovebg_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMizuMovebg_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8089E318(BgMizuMovebg* this, GlobalContext* globalCtx); +void func_8089E650(BgMizuMovebg* this, GlobalContext* globalCtx); +s32 func_8089E108(Path* pathList, Vec3f* pos, s32 pathId, s32 pointId); + +const ActorInit Bg_Mizu_Movebg_InitVars = { + ACTOR_BG_MIZU_MOVEBG, + ACTORCAT_BG, + FLAGS, + OBJECT_MIZU_OBJECTS, + sizeof(BgMizuMovebg), + (ActorFunc)BgMizuMovebg_Init, + (ActorFunc)BgMizuMovebg_Destroy, + (ActorFunc)BgMizuMovebg_Update, + (ActorFunc)BgMizuMovebg_Draw, + NULL, +}; + +static f32 D_8089EB40[] = { -115.200005f, -115.200005f, -115.200005f, 0.0f }; + +static Gfx* D_8089EB50[] = { + gObjectMizuObjectsMovebgDL_000190, gObjectMizuObjectsMovebgDL_000680, gObjectMizuObjectsMovebgDL_000C20, + gObjectMizuObjectsMovebgDL_002E10, gObjectMizuObjectsMovebgDL_002E10, gObjectMizuObjectsMovebgDL_002E10, + gObjectMizuObjectsMovebgDL_002E10, gObjectMizuObjectsMovebgDL_0011F0, +}; + +static CollisionHeader* D_8089EB70[] = { + &gObjectMizuObjectsMovebgCol_0003F0, &gObjectMizuObjectsMovebgCol_000998, &gObjectMizuObjectsMovebgCol_000ED0, + &gObjectMizuObjectsMovebgCol_003590, &gObjectMizuObjectsMovebgCol_003590, &gObjectMizuObjectsMovebgCol_003590, + &gObjectMizuObjectsMovebgCol_003590, &gObjectMizuObjectsMovebgCol_0015F8, +}; + +static InitChainEntry D_8089EB90[] = { + ICHAIN_F32(uncullZoneScale, 1500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static Vec3f D_8089EBA0 = { 0.0f, 80.0f, 23.0f }; +static Vec3f D_8089EBAC = { 0.0f, 80.0f, 23.0f }; + +static u8 D_8089EE40; + +s32 func_8089DC30(GlobalContext* globalCtx) { + s32 result; + + if (Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG)) { + result = 1; + } else if (Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG)) { + result = 2; + } else if (Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG)) { + result = 3; + } else { + result = 1; + } + return result; +} + +void BgMizuMovebg_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 type; + s32 waypointId; + WaterBox* waterBoxes = globalCtx->colCtx.colHeader->waterBoxes; + f32 temp; + CollisionHeader* colHeader = NULL; + Vec3f sp48; + + Actor_ProcessInitChain(thisx, D_8089EB90); + ((BgMizuMovebg*)thisx)->homeY = thisx->world.pos.y; + ((BgMizuMovebg*)thisx)->dlist = D_8089EB50[MOVEBG_TYPE(thisx->params)]; + DynaPolyActor_Init(&((BgMizuMovebg*)thisx)->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(D_8089EB70[MOVEBG_TYPE(thisx->params)], &colHeader); + ((BgMizuMovebg*)thisx)->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + type = MOVEBG_TYPE(thisx->params); + switch (type) { + case 0: + temp = waterBoxes[2].ySurface + 15.0f; + if (temp < ((BgMizuMovebg*)thisx)->homeY - 700.0f) { + thisx->world.pos.y = ((BgMizuMovebg*)thisx)->homeY - 700.0f; + } else { + thisx->world.pos.y = temp; + } + ((BgMizuMovebg*)thisx)->actionFunc = func_8089E318; + break; + case 1: + temp = waterBoxes[2].ySurface + 15.0f; + if (temp < ((BgMizuMovebg*)thisx)->homeY - 710.0f) { + thisx->world.pos.y = ((BgMizuMovebg*)thisx)->homeY - 710.0f; + } else { + thisx->world.pos.y = temp; + } + ((BgMizuMovebg*)thisx)->actionFunc = func_8089E318; + break; + case 2: + temp = waterBoxes[2].ySurface + 15.0f; + if (temp < ((BgMizuMovebg*)thisx)->homeY - 700.0f) { + thisx->world.pos.y = ((BgMizuMovebg*)thisx)->homeY - 700.0f; + } else { + thisx->world.pos.y = temp; + } + ((BgMizuMovebg*)thisx)->actionFunc = func_8089E318; + break; + case 3: + thisx->world.pos.y = ((BgMizuMovebg*)thisx)->homeY + D_8089EB40[func_8089DC30(globalCtx)]; + ((BgMizuMovebg*)thisx)->actionFunc = func_8089E318; + break; + case 4: + case 5: + case 6: + if (Flags_GetSwitch(globalCtx, MOVEBG_FLAGS(thisx->params))) { + thisx->world.pos.y = ((BgMizuMovebg*)thisx)->homeY + 115.19999999999999; + } else { + thisx->world.pos.y = ((BgMizuMovebg*)thisx)->homeY; + } + ((BgMizuMovebg*)thisx)->actionFunc = func_8089E318; + break; + case 7: + ((BgMizuMovebg*)thisx)->scrollAlpha1 = 160; + ((BgMizuMovebg*)thisx)->scrollAlpha2 = 160; + ((BgMizuMovebg*)thisx)->scrollAlpha3 = 160; + ((BgMizuMovebg*)thisx)->scrollAlpha4 = 160; + waypointId = MOVEBG_POINT_ID(thisx->params); + ((BgMizuMovebg*)thisx)->waypointId = waypointId; + func_8089E108(globalCtx->setupPathList, &thisx->world.pos, MOVEBG_PATH_ID(thisx->params), waypointId); + ((BgMizuMovebg*)thisx)->actionFunc = func_8089E650; + break; + } + + type = MOVEBG_TYPE(thisx->params); + switch (type) { + case 3: + case 4: + case 5: + case 6: + Matrix_RotateY(thisx->world.rot.y * (M_PI / 32768), MTXMODE_NEW); + Matrix_MultVec3f(&D_8089EBA0, &sp48); + + if (Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_OBJ_HSBLOCK, + thisx->world.pos.x + sp48.x, thisx->world.pos.y + sp48.y, + thisx->world.pos.z + sp48.z, thisx->world.rot.x, thisx->world.rot.y, + thisx->world.rot.z, 2) == NULL) { + Actor_Kill(thisx); + } + break; + } +} + +void BgMizuMovebg_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMizuMovebg* this = (BgMizuMovebg*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + switch (MOVEBG_TYPE(thisx->params)) { + case 3: + case 4: + case 5: + case 6: + if (this->sfxFlags & 2) { + D_8089EE40 &= ~2; + } + break; + case 7: + if (this->sfxFlags & 1) { + D_8089EE40 &= ~1; + } + break; + } +} + +s32 func_8089E108(Path* pathList, Vec3f* pos, s32 pathId, s32 pointId) { + Path* path = pathList; + Vec3s* point; + + path += pathId; + point = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[pointId]; + + pos->x = point->x; + pos->y = point->y; + pos->z = point->z; + + return 0; +} + +void func_8089E198(BgMizuMovebg* this, GlobalContext* globalCtx) { + f32 waterLevel = globalCtx->colCtx.colHeader->waterBoxes[2].ySurface; + + if (waterLevel < WATER_TEMPLE_WATER_F1_Y) { + this->scrollAlpha1 = 255; + } else if (waterLevel < WATER_TEMPLE_WATER_F2_Y) { + this->scrollAlpha1 = 255 - (s32)((waterLevel - WATER_TEMPLE_WATER_F1_Y) / + (WATER_TEMPLE_WATER_F2_Y - WATER_TEMPLE_WATER_F1_Y) * (255 - 160)); + } else { + this->scrollAlpha1 = 160; + } + + if (waterLevel < WATER_TEMPLE_WATER_F2_Y) { + this->scrollAlpha2 = 255; + } else if (waterLevel < WATER_TEMPLE_WATER_F3_Y) { + this->scrollAlpha2 = 255 - (s32)((waterLevel - WATER_TEMPLE_WATER_F2_Y) / + (WATER_TEMPLE_WATER_F3_Y - WATER_TEMPLE_WATER_F2_Y) * (255 - 160)); + } else { + this->scrollAlpha2 = 160; + } + + if (waterLevel < WATER_TEMPLE_WATER_B1_Y) { + this->scrollAlpha3 = 255; + } else if (waterLevel < WATER_TEMPLE_WATER_F1_Y) { + this->scrollAlpha3 = 255 - (s32)((waterLevel - WATER_TEMPLE_WATER_B1_Y) / + (WATER_TEMPLE_WATER_F1_Y - WATER_TEMPLE_WATER_B1_Y) * (255 - 160)); + } else { + this->scrollAlpha3 = 160; + } + + this->scrollAlpha4 = this->scrollAlpha3; +} + +void func_8089E318(BgMizuMovebg* this, GlobalContext* globalCtx) { + WaterBox* waterBoxes = globalCtx->colCtx.colHeader->waterBoxes; + f32 phi_f0; + s32 type; + Vec3f sp28; + + func_8089E198(this, globalCtx); + + type = MOVEBG_TYPE(this->dyna.actor.params); + switch (type) { + case 0: + case 2: + phi_f0 = waterBoxes[2].ySurface + 15.0f; + if (phi_f0 < this->homeY - 700.0f) { + this->dyna.actor.world.pos.y = this->homeY - 700.0f; + } else { + this->dyna.actor.world.pos.y = phi_f0; + } + break; + case 1: + phi_f0 = waterBoxes[2].ySurface + 15.0f; + if (phi_f0 < this->homeY - 710.0f) { + this->dyna.actor.world.pos.y = this->homeY - 710.0f; + } else { + this->dyna.actor.world.pos.y = phi_f0; + } + break; + case 3: + phi_f0 = this->homeY + D_8089EB40[func_8089DC30(globalCtx)]; + if (!Math_StepToF(&this->dyna.actor.world.pos.y, phi_f0, 1.0f)) { + if (!(D_8089EE40 & 2) && MOVEBG_SPEED(this->dyna.actor.params) != 0) { + D_8089EE40 |= 2; + this->sfxFlags |= 2; + } + if (this->sfxFlags & 2) { + if (this->dyna.actor.room == 0) { + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + } else { + func_8002F948(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + } + } + } + break; + case 4: + case 5: + case 6: + if (Flags_GetSwitch(globalCtx, MOVEBG_FLAGS(this->dyna.actor.params))) { + phi_f0 = this->homeY + 115.200005f; + } else { + phi_f0 = this->homeY; + } + if (!Math_StepToF(&this->dyna.actor.world.pos.y, phi_f0, 1.0f)) { + if (!(D_8089EE40 & 2) && MOVEBG_SPEED(this->dyna.actor.params) != 0) { + D_8089EE40 |= 2; + this->sfxFlags |= 2; + } + if (this->sfxFlags & 2) { + func_8002F948(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + } + } + break; + } + + type = MOVEBG_TYPE(this->dyna.actor.params); + switch (type) { + case 3: + case 4: + case 5: + case 6: + if (globalCtx->roomCtx.curRoom.num == this->dyna.actor.room) { + Matrix_RotateY(this->dyna.actor.world.rot.y * (M_PI / 32768), MTXMODE_NEW); + Matrix_MultVec3f(&D_8089EBAC, &sp28); + this->dyna.actor.child->world.pos.x = this->dyna.actor.world.pos.x + sp28.x; + this->dyna.actor.child->world.pos.y = this->dyna.actor.world.pos.y + sp28.y; + this->dyna.actor.child->world.pos.z = this->dyna.actor.world.pos.z + sp28.z; + this->dyna.actor.child->flags &= ~ACTOR_FLAG_0; + } + break; + } +} + +void func_8089E650(BgMizuMovebg* this, GlobalContext* globalCtx) { + Vec3f waypoint; + f32 dist; + f32 dx; + f32 dy; + f32 dz; + + this->dyna.actor.speedXZ = MOVEBG_SPEED(this->dyna.actor.params) * 0.1f; + func_8089E108(globalCtx->setupPathList, &waypoint, MOVEBG_PATH_ID(this->dyna.actor.params), this->waypointId); + dist = Actor_WorldDistXYZToPoint(&this->dyna.actor, &waypoint); + if (dist < this->dyna.actor.speedXZ) { + this->dyna.actor.speedXZ = dist; + } + func_80035844(&this->dyna.actor.world.pos, &waypoint, &this->dyna.actor.world.rot, 1); + func_8002D97C(&this->dyna.actor); + dx = waypoint.x - this->dyna.actor.world.pos.x; + dy = waypoint.y - this->dyna.actor.world.pos.y; + dz = waypoint.z - this->dyna.actor.world.pos.z; + if (fabsf(dx) < 2.0f && fabsf(dy) < 2.0f && fabsf(dz) < 2.0f) { + this->waypointId++; + if (this->waypointId >= globalCtx->setupPathList[MOVEBG_PATH_ID(this->dyna.actor.params)].count) { + this->waypointId = 0; + func_8089E108(globalCtx->setupPathList, &this->dyna.actor.world.pos, + MOVEBG_PATH_ID(this->dyna.actor.params), 0); + } + } + if (!(D_8089EE40 & 1) && MOVEBG_SPEED(this->dyna.actor.params) != 0) { + D_8089EE40 |= 1; + this->sfxFlags |= 1; + } + if (this->sfxFlags & 1) { + func_8002F948(&this->dyna.actor, NA_SE_EV_ROLL_STAND_2 - SFX_FLAG); + } +} + +void BgMizuMovebg_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMizuMovebg* this = (BgMizuMovebg*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMizuMovebg_Draw(Actor* thisx, GlobalContext* globalCtx2) { + BgMizuMovebg* this = (BgMizuMovebg*)thisx; + GlobalContext* globalCtx = globalCtx2; + u32 frames; + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_movebg.c", 754); + + frames = globalCtx->gameplayFrames; + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, frames * 1, 0, 32, 32, 1, 0, 0, 32, 32, 0, 0, 0, + this->scrollAlpha1)); + + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, frames * 1, 0, 32, 32, 1, 0, 0, 32, 32, 0, 0, 0, + this->scrollAlpha2)); + + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, frames * 1, 0, 32, 32, 1, 0, 0, 32, 32, 0, 0, 0, + this->scrollAlpha3)); + + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_TwoTexScrollEnvColor(globalCtx->state.gfxCtx, 0, frames * 3, 0, 32, 32, 1, 0, 0, 32, 32, 0, 0, 0, + this->scrollAlpha4)); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mizu_movebg.c", 788), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->dlist != NULL) { + gSPDisplayList(POLY_OPA_DISP++, this->dlist); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_movebg.c", 795); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Movebg/z_bg_mizu_movebg.h b/soh/src/overlays/actors/ovl_Bg_Mizu_Movebg/z_bg_mizu_movebg.h new file mode 100644 index 000000000..bf17eeac2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Movebg/z_bg_mizu_movebg.h @@ -0,0 +1,24 @@ +#ifndef Z_BG_MIZU_MOVEBG_H +#define Z_BG_MIZU_MOVEBG_H + +#include "ultra64.h" +#include "global.h" + +struct BgMizuMovebg; + +typedef void (*BgMizuMovebgActionFunc)(struct BgMizuMovebg*, GlobalContext*); + +typedef struct BgMizuMovebg { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMizuMovebgActionFunc actionFunc; + /* 0x0168 */ f32 homeY; + /* 0x016C */ s32 scrollAlpha1; + /* 0x0170 */ s32 scrollAlpha2; + /* 0x0174 */ s32 scrollAlpha3; + /* 0x0178 */ s32 scrollAlpha4; + /* 0x017C */ u8 sfxFlags; + /* 0x0180 */ Gfx* dlist; + /* 0x0184 */ s32 waypointId; +} BgMizuMovebg; // size = 0x0188 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Shutter/z_bg_mizu_shutter.c b/soh/src/overlays/actors/ovl_Bg_Mizu_Shutter/z_bg_mizu_shutter.c new file mode 100644 index 000000000..0da39e5f3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Shutter/z_bg_mizu_shutter.c @@ -0,0 +1,170 @@ +#include "z_bg_mizu_shutter.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +#define SIZE_PARAM (((u16)this->dyna.actor.params >> 0xC) & 0xF) +#define TIMER_PARAM (((u16)this->dyna.actor.params >> 6) & 0x3F) + +void BgMizuShutter_Init(BgMizuShutter* this, GlobalContext* globalCtx); +void BgMizuShutter_Destroy(BgMizuShutter* this, GlobalContext* globalCtx); +void BgMizuShutter_Update(BgMizuShutter* this, GlobalContext* globalCtx); +void BgMizuShutter_Draw(BgMizuShutter* this, GlobalContext* globalCtx); + +void BgMizuShutter_WaitForTimer(BgMizuShutter* this, GlobalContext* globalCtx); +void BgMizuShutter_WaitForSwitch(BgMizuShutter* this, GlobalContext* globalCtx); +void BgMizuShutter_Move(BgMizuShutter* this, GlobalContext* globalCtx); +void BgMizuShutter_WaitForCutscene(BgMizuShutter* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mizu_Shutter_InitVars = { + ACTOR_BG_MIZU_SHUTTER, + ACTORCAT_PROP, + FLAGS, + OBJECT_MIZU_OBJECTS, + sizeof(BgMizuShutter), + (ActorFunc)BgMizuShutter_Init, + (ActorFunc)BgMizuShutter_Destroy, + (ActorFunc)BgMizuShutter_Update, + (ActorFunc)BgMizuShutter_Draw, + NULL, +}; + +static Gfx* sDisplayLists[] = { gObjectMizuObjectsShutterDL_007130, gObjectMizuObjectsShutterDL_0072D0 }; + +static CollisionHeader* sCollisionHeaders[] = { + &gObjectMizuObjectsShutterCol_007250, + &gObjectMizuObjectsShutterCol_0073F0, +}; + +static Vec3f sDisplacements[] = { + { 0.0f, 100.0f, 0.0f }, + { 0.0f, 140.0f, 0.0f }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgMizuShutter_Init(BgMizuShutter* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuShutter* this = (BgMizuShutter*)thisx; + s32 pad2; + CollisionHeader* sp30 = NULL; + s32 pad3; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->displayList = sDisplayLists[SIZE_PARAM]; + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(sCollisionHeaders[SIZE_PARAM], &sp30); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, sp30); + if ((SIZE_PARAM == BGMIZUSHUTTER_SMALL) || (SIZE_PARAM == BGMIZUSHUTTER_LARGE)) { + this->closedPos = this->dyna.actor.world.pos; + this->timer = 0; + this->timerMax = TIMER_PARAM * 20; + Matrix_RotateY(this->dyna.actor.world.rot.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_RotateX(this->dyna.actor.world.rot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(this->dyna.actor.world.rot.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_MultVec3f(&sDisplacements[SIZE_PARAM], &this->openPos); + this->openPos.x += this->dyna.actor.world.pos.x; + this->openPos.y += this->dyna.actor.world.pos.y; + this->openPos.z += this->dyna.actor.world.pos.z; + if (this->timerMax != 0x3F * 20) { + Flags_UnsetSwitch(globalCtx, (u16)this->dyna.actor.params & 0x3F); + this->dyna.actor.world.pos = this->closedPos; + } + if (Flags_GetSwitch(globalCtx, (u16)this->dyna.actor.params & 0x3F)) { + this->dyna.actor.world.pos = this->openPos; + this->actionFunc = BgMizuShutter_WaitForTimer; + } else { + this->actionFunc = BgMizuShutter_WaitForSwitch; + } + } +} + +void BgMizuShutter_Destroy(BgMizuShutter* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuShutter* this = (BgMizuShutter*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgMizuShutter_WaitForSwitch(BgMizuShutter* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (u16)this->dyna.actor.params & 0x3F)) { + if (ABS(this->dyna.actor.world.rot.x) > 0x2C60) { + OnePointCutscene_Init(globalCtx, 4510, -99, &this->dyna.actor, MAIN_CAM); + } else { + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + } + this->actionFunc = BgMizuShutter_WaitForCutscene; + this->timer = 30; + } +} + +void BgMizuShutter_WaitForCutscene(BgMizuShutter* this, GlobalContext* globalCtx) { + if (this->timer-- == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_OPEN); + this->actionFunc = BgMizuShutter_Move; + } +} + +void BgMizuShutter_Move(BgMizuShutter* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (u16)this->dyna.actor.params & 0x3F)) { + Math_SmoothStepToF(&this->dyna.actor.world.pos.x, this->openPos.x, 1.0f, 4.0f, 0.1f); + Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->openPos.y, 1.0f, 4.0f, 0.1f); + Math_SmoothStepToF(&this->dyna.actor.world.pos.z, this->openPos.z, 1.0f, 4.0f, 0.1f); + if ((this->dyna.actor.world.pos.x == this->openPos.x) && (this->dyna.actor.world.pos.y == this->openPos.y) && + (this->dyna.actor.world.pos.z == this->openPos.z)) { + this->timer = this->timerMax; + this->actionFunc = BgMizuShutter_WaitForTimer; + } + } else { + Math_SmoothStepToF(&this->maxSpeed, 20.0f, 1.0f, 3.0f, 0.1f); + Math_SmoothStepToF(&this->dyna.actor.world.pos.x, this->closedPos.x, 1.0f, this->maxSpeed, 0.1f); + Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->closedPos.y, 1.0f, this->maxSpeed, 0.1f); + Math_SmoothStepToF(&this->dyna.actor.world.pos.z, this->closedPos.z, 1.0f, this->maxSpeed, 0.1f); + if ((this->dyna.actor.world.pos.x == this->closedPos.x) && + (this->dyna.actor.world.pos.y == this->closedPos.y) && + (this->dyna.actor.world.pos.z == this->closedPos.z)) { + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 0x78, 0x14, 0xA); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + this->actionFunc = BgMizuShutter_WaitForSwitch; + } + } +} + +void BgMizuShutter_WaitForTimer(BgMizuShutter* this, GlobalContext* globalCtx) { + if (this->timerMax != 0x3F * 20) { + this->timer--; + func_8002F994(&this->dyna.actor, this->timer); + if (this->timer == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_CLOSE); + Flags_UnsetSwitch(globalCtx, (u16)this->dyna.actor.params & 0x3F); + this->actionFunc = BgMizuShutter_Move; + } + } +} + +void BgMizuShutter_Update(BgMizuShutter* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuShutter* this = (BgMizuShutter*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMizuShutter_Draw(BgMizuShutter* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuShutter* this = (BgMizuShutter*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_shutter.c", 410); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mizu_shutter.c", 415), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->displayList != NULL) { + gSPDisplayList(POLY_OPA_DISP++, this->displayList); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_shutter.c", 422); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Shutter/z_bg_mizu_shutter.h b/soh/src/overlays/actors/ovl_Bg_Mizu_Shutter/z_bg_mizu_shutter.h new file mode 100644 index 000000000..1889da300 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Shutter/z_bg_mizu_shutter.h @@ -0,0 +1,29 @@ +#ifndef Z_BG_MIZU_SHUTTER_H +#define Z_BG_MIZU_SHUTTER_H + +#include "ultra64.h" +#include "global.h" + +#define BGMIZUSHUTTER_PARAM(size, timer, switchFlag) (size << 0xC) | (timer << 0x6) | switchFlag + +struct BgMizuShutter; + +typedef void (*BgMizuShutterActionFunc)(struct BgMizuShutter*, GlobalContext*); + +typedef struct BgMizuShutter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMizuShutterActionFunc actionFunc; + /* 0x0168 */ s32 timer; + /* 0x016C */ s32 timerMax; + /* 0x0170 */ Gfx* displayList; + /* 0x0174 */ f32 maxSpeed; + /* 0x0178 */ Vec3f closedPos; + /* 0x0184 */ Vec3f openPos; +} BgMizuShutter; // size = 0x0190 + +typedef enum BgMizuShutterSize { + BGMIZUSHUTTER_SMALL, + BGMIZUSHUTTER_LARGE +} BgMizuShutterSize; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Uzu/z_bg_mizu_uzu.c b/soh/src/overlays/actors/ovl_Bg_Mizu_Uzu/z_bg_mizu_uzu.c new file mode 100644 index 000000000..c6e1fdfd8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Uzu/z_bg_mizu_uzu.c @@ -0,0 +1,76 @@ +/* + * File: z_bg_mizu_uzu.c + * Overlay: ovl_Bg_Mizu_Uzu + * Description: Water Noise + */ + +#include "z_bg_mizu_uzu.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" + +#define FLAGS 0 + +void BgMizuUzu_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMizuUzu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMizuUzu_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMizuUzu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8089F788(BgMizuUzu* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mizu_Uzu_InitVars = { + ACTOR_BG_MIZU_UZU, + ACTORCAT_PROP, + FLAGS, + OBJECT_MIZU_OBJECTS, + sizeof(BgMizuUzu), + (ActorFunc)BgMizuUzu_Init, + (ActorFunc)BgMizuUzu_Destroy, + (ActorFunc)BgMizuUzu_Update, + (ActorFunc)BgMizuUzu_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgMizuUzu_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMizuUzu* this = (BgMizuUzu*)thisx; + CollisionHeader* colHeader = NULL; + s32 pad2; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gObjectMizuObjectsUzuCol_0074EC, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->actionFunc = func_8089F788; +} + +void BgMizuUzu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMizuUzu* this = (BgMizuUzu*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_8089F788(BgMizuUzu* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (GET_PLAYER(globalCtx)->currentBoots == PLAYER_BOOTS_IRON) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + Audio_PlayActorSound2(thisx, NA_SE_EV_WATER_CONVECTION - SFX_FLAG); + thisx->shape.rot.y += 0x1C0; +} + +void BgMizuUzu_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMizuUzu* this = (BgMizuUzu*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMizuUzu_Draw(Actor* thisx, GlobalContext* globalCtx) { +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Uzu/z_bg_mizu_uzu.h b/soh/src/overlays/actors/ovl_Bg_Mizu_Uzu/z_bg_mizu_uzu.h new file mode 100644 index 000000000..528a9386b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Uzu/z_bg_mizu_uzu.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_MIZU_UZU_H +#define Z_BG_MIZU_UZU_H + +#include "ultra64.h" +#include "global.h" + +struct BgMizuUzu; + +typedef void (*BgMizuUzuActionFunc)(struct BgMizuUzu*, GlobalContext*); + +typedef struct BgMizuUzu { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMizuUzuActionFunc actionFunc; +} BgMizuUzu; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.c b/soh/src/overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.c new file mode 100644 index 000000000..2ad9b5b41 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.c @@ -0,0 +1,350 @@ +/* + * File: z_bg_mizu_water.c + * Overlay: ovl_Bg_Mizu_Water + * Description: Water plane in Water Temple. Changes height based on switches 0x1C, 0x1D, 0x1E. + */ + +#include "z_bg_mizu_water.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgMizuWater_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMizuWater_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMizuWater_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMizuWater_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMizuWater_WaitForAction(BgMizuWater* this, GlobalContext* globalCtx); +void BgMizuWater_ChangeWaterLevel(BgMizuWater* this, GlobalContext* globalCtx); + +typedef struct { + s32 switchFlag; + s32 yDiff; +} WaterLevel; + +static WaterLevel sWaterLevels[] = { + { 0x00, 0 }, + { WATER_TEMPLE_WATER_F3_FLAG, 0 }, + { WATER_TEMPLE_WATER_F2_FLAG, WATER_TEMPLE_WATER_F2_Y - WATER_TEMPLE_WATER_F3_Y }, + { WATER_TEMPLE_WATER_F1_FLAG, WATER_TEMPLE_WATER_F1_Y - WATER_TEMPLE_WATER_F3_Y }, +}; + +const ActorInit Bg_Mizu_Water_InitVars = { + ACTOR_BG_MIZU_WATER, + ACTORCAT_BG, + FLAGS, + OBJECT_MIZU_OBJECTS, + sizeof(BgMizuWater), + (ActorFunc)BgMizuWater_Init, + (ActorFunc)BgMizuWater_Destroy, + (ActorFunc)BgMizuWater_Update, + (ActorFunc)BgMizuWater_Draw, + NULL, +}; + +static f32 sUnused1 = 0; +static f32 sUnused2 = 110.0f; + +static u32 sWaterBoxIndexes[] = { 2, 3, 5, 7, 12, 20, 21, 22 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_STOP), +}; + +u32 BgMizuWater_GetWaterLevelActionIndex(s16 switchFlag, GlobalContext* globalCtx) { + u32 ret; + + if (bREG(0) != 0) { + switch (bREG(1)) { + case 0: + Flags_SetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG); + break; + case 1: + Flags_SetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG); + break; + case 2: + Flags_SetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG); + break; + } + bREG(0) = 0; + } + if (Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG) && (switchFlag != WATER_TEMPLE_WATER_F1_FLAG)) { + ret = 3; + } else if (Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG) && (switchFlag != WATER_TEMPLE_WATER_F2_FLAG)) { + ret = 2; + } else if (Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG) && (switchFlag != WATER_TEMPLE_WATER_F3_FLAG)) { + ret = 1; + } else { + ret = 0; + } + + return ret; +} + +void BgMizuWater_SetWaterBoxesHeight(WaterBox* waterBoxes, s16 height) { + u32 i; + + for (i = 0; i < 8; i++) { + waterBoxes[sWaterBoxIndexes[i]].ySurface = height; + } +} + +void BgMizuWater_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMizuWater* this = (BgMizuWater*)thisx; + f32 initialActorY; + WaterBox* waterBoxes; + s32 waterLevelActionIndex; + + waterBoxes = globalCtx->colCtx.colHeader->waterBoxes; + this->type = this->actor.params & 0xFF; + this->switchFlag = (this->actor.params >> 8) & 0xFF; + Actor_ProcessInitChain(&this->actor, sInitChain); + initialActorY = this->actor.world.pos.y; + this->baseY = initialActorY; + this->targetY = initialActorY; + + switch (this->type) { + case 0: + if (bREG(15) == 0) { + osSyncPrintf("<コンストラクト>%x %x %x\n", Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG), + Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG), + Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG)); + } + waterLevelActionIndex = BgMizuWater_GetWaterLevelActionIndex(-1, globalCtx); + this->actor.world.pos.y = sWaterLevels[waterLevelActionIndex].yDiff + this->baseY; + BgMizuWater_SetWaterBoxesHeight(waterBoxes, this->actor.world.pos.y); + this->actor.params = sWaterLevels[waterLevelActionIndex].switchFlag; + Flags_UnsetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG); + Flags_UnsetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG); + Flags_UnsetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG); + + switch (this->actor.params) { + case 0x1E: + Flags_SetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG); + break; + case 0x1D: + Flags_SetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG); + break; + case 0x1C: + default: + Flags_SetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG); + break; + } + this->targetY = this->actor.world.pos.y; + break; + case 1: + break; + case 2: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actor.world.pos.y = this->baseY + 85.0f; + } + waterBoxes[6].ySurface = this->actor.world.pos.y; + break; + case 3: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actor.world.pos.y = this->baseY + 110.0f; + if (1) {} + this->targetY = this->actor.world.pos.y; + } + waterBoxes[8].ySurface = this->actor.world.pos.y; + break; + case 4: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actor.world.pos.y = this->baseY + 160.0f; + if (1) {} + this->targetY = this->actor.world.pos.y; + } + waterBoxes[16].ySurface = this->actor.world.pos.y; + break; + } + + this->actionFunc = BgMizuWater_WaitForAction; +} + +void BgMizuWater_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgMizuWater_WaitForAction(BgMizuWater* this, GlobalContext* globalCtx) { + s32 pad; + s32 waterLevelActionIndex; + s16 prevSwitchFlag; + + switch (this->type) { + case 0: + prevSwitchFlag = this->actor.params; + waterLevelActionIndex = BgMizuWater_GetWaterLevelActionIndex(this->actor.params, globalCtx); + if (waterLevelActionIndex != 0) { + if (prevSwitchFlag != sWaterLevels[waterLevelActionIndex].switchFlag) { + OnePointCutscene_Init(globalCtx, 3120, -100 - waterLevelActionIndex, NULL, MAIN_CAM); + this->actor.params = sWaterLevels[waterLevelActionIndex].switchFlag; + this->targetY = sWaterLevels[waterLevelActionIndex].yDiff + this->baseY; + } + } + if ((prevSwitchFlag != this->actor.params) && (prevSwitchFlag != 0)) { + Flags_UnsetSwitch(globalCtx, prevSwitchFlag); + } + break; + case 1: + break; + case 2: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->targetY = this->baseY + 85.0f; + } else { + this->targetY = this->baseY; + } + break; + case 3: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->targetY = this->baseY + 110.0f; + } else { + this->targetY = this->baseY; + } + break; + case 4: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->targetY = this->baseY + 160.0f; + } else { + this->targetY = this->baseY; + } + } + + if (this->targetY != this->actor.world.pos.y) { + this->actionFunc = BgMizuWater_ChangeWaterLevel; + } +} + +void BgMizuWater_ChangeWaterLevel(BgMizuWater* this, GlobalContext* globalCtx) { + s32 pad; + s16 prevSwitchFlag; + s32 waterLevelActionIndex; + WaterBox* waterBoxes; + + waterBoxes = globalCtx->colCtx.colHeader->waterBoxes; + switch (this->type) { + case 0: + prevSwitchFlag = this->actor.params; + waterLevelActionIndex = BgMizuWater_GetWaterLevelActionIndex(this->actor.params, globalCtx); + if (waterLevelActionIndex != 0) { + if (prevSwitchFlag != sWaterLevels[waterLevelActionIndex].switchFlag) { + this->actor.params = sWaterLevels[waterLevelActionIndex].switchFlag; + this->targetY = sWaterLevels[waterLevelActionIndex].yDiff + this->baseY; + } + } + + if ((prevSwitchFlag != this->actor.params) && (prevSwitchFlag != 0)) { + Flags_UnsetSwitch(globalCtx, prevSwitchFlag); + } + + if (Math_StepToF(&this->actor.world.pos.y, this->targetY, 5.0f)) { + globalCtx->roomCtx.unk_74[0] = 0; + this->actionFunc = BgMizuWater_WaitForAction; + Message_CloseTextbox(globalCtx); + } + BgMizuWater_SetWaterBoxesHeight(globalCtx->colCtx.colHeader->waterBoxes, this->actor.world.pos.y); + break; + case 1: + break; + case 2: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->targetY = this->baseY + 85.0f; + } else { + this->targetY = this->baseY; + } + if (Math_StepToF(&this->actor.world.pos.y, this->targetY, 1.0f)) { + globalCtx->roomCtx.unk_74[0] = 0; + this->actionFunc = BgMizuWater_WaitForAction; + } + waterBoxes[6].ySurface = this->actor.world.pos.y; + break; + case 3: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->targetY = this->baseY + 110.0f; + } else { + this->targetY = this->baseY; + } + if (Math_StepToF(&this->actor.world.pos.y, this->targetY, 1.0f)) { + globalCtx->roomCtx.unk_74[0] = 0; + this->actionFunc = BgMizuWater_WaitForAction; + } + waterBoxes[8].ySurface = this->actor.world.pos.y; + break; + case 4: + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->targetY = this->baseY + 160.0f; + } else { + this->targetY = this->baseY; + } + if (Math_StepToF(&this->actor.world.pos.y, this->targetY, 1.0f)) { + globalCtx->roomCtx.unk_74[0] = 0; + this->actionFunc = BgMizuWater_WaitForAction; + } + waterBoxes[16].ySurface = this->actor.world.pos.y; + break; + } + + if (this->targetY < this->actor.world.pos.y) { + func_800AA000(0.0f, 0x78, 0x14, 0xA); + func_8002F948(&this->actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } else if (this->targetY > this->actor.world.pos.y) { + func_800AA000(0.0f, 0x78, 0x14, 0xA); + func_8002F948(&this->actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } +} + +void BgMizuWater_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMizuWater* this = (BgMizuWater*)thisx; + s32 posY; + s32 unk0; + s32 unk1; + s32 pad; + + if (bREG(15) == 0) { + osSyncPrintf("%x %x %x\n", Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F1_FLAG), + Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F2_FLAG), + Flags_GetSwitch(globalCtx, WATER_TEMPLE_WATER_F3_FLAG)); + } + if (this->type == 0) { + posY = this->actor.world.pos.y; + unk0 = 0; + unk1 = 0; + if (posY < WATER_TEMPLE_WATER_F1_Y) { + unk0 = 0; + unk1 = (posY - WATER_TEMPLE_WATER_B1_Y) / (WATER_TEMPLE_WATER_F1_Y - WATER_TEMPLE_WATER_B1_Y) * 200; + } else if (posY < WATER_TEMPLE_WATER_F2_Y) { + unk0 = 1; + unk1 = 255 - (s32)((posY - WATER_TEMPLE_WATER_F1_Y) / (WATER_TEMPLE_WATER_F2_Y - WATER_TEMPLE_WATER_F1_Y) * + (255 - 160)); + } else if (posY <= WATER_TEMPLE_WATER_F3_Y) { + unk0 = 2; + unk1 = 255 - (s32)((posY - WATER_TEMPLE_WATER_F2_Y) / (WATER_TEMPLE_WATER_F3_Y - WATER_TEMPLE_WATER_F2_Y) * + (255 - 160)); + } + globalCtx->roomCtx.unk_74[1] = ((u8)unk0 << 8) | (unk1 & 0xFF); + } + + this->actionFunc(this, globalCtx); +} + +void BgMizuWater_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgMizuWater* this = (BgMizuWater*)thisx; + s32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_water.c", 738); + gameplayFrames = globalCtx->gameplayFrames; + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x0C, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -gameplayFrames * 1, gameplayFrames * 1, 32, 32, 1, 0, + -gameplayFrames * 1, 32, 32)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mizu_water.c", 749), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 102); + + gSPDisplayList(POLY_XLU_DISP++, gObjectMizuObjectsWaterDL_004B20); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mizu_water.c", 756); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.h b/soh/src/overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.h new file mode 100644 index 000000000..cb9d96bca --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mizu_Water/z_bg_mizu_water.h @@ -0,0 +1,29 @@ +#ifndef Z_BG_MIZU_WATER_H +#define Z_BG_MIZU_WATER_H + +#include "ultra64.h" +#include "global.h" + +struct BgMizuWater; + +typedef void (*BgMizuWaterActionFunc)(struct BgMizuWater*, GlobalContext*); + +typedef struct BgMizuWater { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgMizuWaterActionFunc actionFunc; + /* 0x0150 */ s32 type; + /* 0x0154 */ f32 targetY; + /* 0x0158 */ f32 baseY; + /* 0x015C */ s32 switchFlag; // only used for types 2-4 +} BgMizuWater; // size = 0x0160 + +#define WATER_TEMPLE_WATER_F3_Y 765.0f +#define WATER_TEMPLE_WATER_F2_Y 445.0f +#define WATER_TEMPLE_WATER_F1_Y -15.0f +#define WATER_TEMPLE_WATER_B1_Y -835.0f + +#define WATER_TEMPLE_WATER_F3_FLAG 0x1E +#define WATER_TEMPLE_WATER_F2_FLAG 0x1D +#define WATER_TEMPLE_WATER_F1_FLAG 0x1C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mjin/z_bg_mjin.c b/soh/src/overlays/actors/ovl_Bg_Mjin/z_bg_mjin.c new file mode 100644 index 000000000..333d25d55 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mjin/z_bg_mjin.c @@ -0,0 +1,131 @@ +/* + * File: z_bg_mjin.c + * Overlay: ovl_Bg_Mjin + * Description: Warp Pad + */ + +#include "z_bg_mjin.h" +#include "objects/object_mjin/object_mjin.h" +#include "objects/object_mjin_wind/object_mjin_wind.h" +#include "objects/object_mjin_soul/object_mjin_soul.h" +#include "objects/object_mjin_dark/object_mjin_dark.h" +#include "objects/object_mjin_ice/object_mjin_ice.h" +#include "objects/object_mjin_flame/object_mjin_flame.h" +#include "objects/object_mjin_flash/object_mjin_flash.h" +#include "objects/object_mjin_oka/object_mjin_oka.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgMjin_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMjin_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMjin_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMjin_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808A0850(BgMjin* this, GlobalContext* globalCtx); +void BgMjin_DoNothing(BgMjin* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mjin_InitVars = { + ACTOR_BG_MJIN, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgMjin), + (ActorFunc)BgMjin_Init, + (ActorFunc)BgMjin_Destroy, + (ActorFunc)BgMjin_Update, + NULL, + NULL, +}; + +extern UNK_TYPE D_06000000; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), +}; + +static s16 sObjectIDs[] = { OBJECT_MJIN_FLASH, OBJECT_MJIN_DARK, OBJECT_MJIN_FLAME, + OBJECT_MJIN_ICE, OBJECT_MJIN_SOUL, OBJECT_MJIN_WIND }; + +void* gPedestalEmblems[] = { gLightMedallionPlatformTex, gShadowMedallionPlatformTex, gFireMedallionPlatformTex, + gWaterMedallionPlatformTex, gSpiritMedallionPlatformTex, gForestMedallionPlatformTex }; + +void BgMjin_SetupAction(BgMjin* this, BgMjinActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgMjin_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMjin* this = (BgMjin*)thisx; + s8 objBankIndex; + + Actor_ProcessInitChain(thisx, sInitChain); + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, (thisx->params != 0 ? OBJECT_MJIN : OBJECT_MJIN_OKA)); + this->objBankIndex = objBankIndex; + if (objBankIndex < 0) { + Actor_Kill(thisx); + } else { + BgMjin_SetupAction(this, func_808A0850); + } +} + +void BgMjin_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMjin* this = (BgMjin*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808A0850(BgMjin* this, GlobalContext* globalCtx) { + CollisionHeader* colHeader; + CollisionHeader* collision; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + colHeader = NULL; + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + this->dyna.actor.objBankIndex = this->objBankIndex; + Actor_SetObjectDependency(globalCtx, &this->dyna.actor); + DynaPolyActor_Init(&this->dyna, 0); + collision = this->dyna.actor.params != 0 ? &gWarpPadCol : &gOcarinaWarpPadCol; + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + BgMjin_SetupAction(this, BgMjin_DoNothing); + this->dyna.actor.draw = BgMjin_Draw; + } +} + +void BgMjin_DoNothing(BgMjin* this, GlobalContext* globalCtx) { +} + +void BgMjin_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMjin* this = (BgMjin*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMjin_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgMjin* this = (BgMjin*)thisx; + Gfx* dlist; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mjin.c", 250); + + if (thisx->params != 0) { + s32 objBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectIDs[thisx->params - 1]); + + if (objBankIndex >= 0) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[objBankIndex].segment); + } + + gSPSegment(POLY_OPA_DISP++, 0x08, gPedestalEmblems[thisx->params - 1]); + dlist = gWarpPadBaseDL; + } else { + dlist = gOcarinaWarpPadDL; + } + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mjin.c", 285), + G_MTX_NOPUSH | G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, dlist); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mjin.c", 288); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mjin/z_bg_mjin.h b/soh/src/overlays/actors/ovl_Bg_Mjin/z_bg_mjin.h new file mode 100644 index 000000000..dd5964ed4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mjin/z_bg_mjin.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_MJIN_H +#define Z_BG_MJIN_H + +#include "ultra64.h" +#include "global.h" + +struct BgMjin; + +typedef void (*BgMjinActionFunc)(struct BgMjin*, GlobalContext*); + +typedef struct BgMjin { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s8 objBankIndex; + /* 0x0168 */ BgMjinActionFunc actionFunc; +} BgMjin; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Bigst/z_bg_mori_bigst.c b/soh/src/overlays/actors/ovl_Bg_Mori_Bigst/z_bg_mori_bigst.c new file mode 100644 index 000000000..5f91e2e9e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Bigst/z_bg_mori_bigst.c @@ -0,0 +1,257 @@ +/* + * File: z_bg_mori_bigst.c + * Overlay: ovl_Bg_Mori_Bigst + * Description: Forest Temple falling platform and Stalfos fight + */ + +#include "z_bg_mori_bigst.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgMoriBigst_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriBigst_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriBigst_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriBigst_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriBigst_SetupWaitForMoriTex(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_WaitForMoriTex(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_SetupNoop(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_SetupStalfosFight(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_StalfosFight(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_SetupFall(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_Fall(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_SetupLanding(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_Landing(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_SetupStalfosPairFight(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_StalfosPairFight(BgMoriBigst* this, GlobalContext* globalCtx); +void BgMoriBigst_SetupDone(BgMoriBigst* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mori_Bigst_InitVars = { + ACTOR_BG_MORI_BIGST, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriBigst), + (ActorFunc)BgMoriBigst_Init, + (ActorFunc)BgMoriBigst_Destroy, + (ActorFunc)BgMoriBigst_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 3000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 3000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 3000, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(gravity, -500, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(minVelocityY, -12000, ICHAIN_CONTINUE), ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgMoriBigst_SetupAction(BgMoriBigst* this, BgMoriBigstActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgMoriBigst_InitDynapoly(BgMoriBigst* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG login failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_mori_bigst.c", 190, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgMoriBigst_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriBigst* this = (BgMoriBigst*)thisx; + + // "mori (bigST.keyceiling)" + osSyncPrintf("mori (bigST.鍵型天井)(arg : %04x)(sw %d)(noE %d)(roomC %d)(playerPosY %f)\n", this->dyna.actor.params, + Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F), + Flags_GetTempClear(globalCtx, this->dyna.actor.room), Flags_GetClear(globalCtx, this->dyna.actor.room), + GET_PLAYER(globalCtx)->actor.world.pos.y); + BgMoriBigst_InitDynapoly(this, globalCtx, &gMoriBigstCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + // "【Big Stalfos key ceiling】 bank danger!" + osSyncPrintf("【ビッグスタルフォス鍵型天井】 バンク危険!\n"); + osSyncPrintf("%s %d\n", "../z_bg_mori_bigst.c", 234); + Actor_Kill(&this->dyna.actor); + return; + } + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + } else { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + 270.0f; + } + Actor_SetFocus(&this->dyna.actor, 50.0f); + BgMoriBigst_SetupWaitForMoriTex(this, globalCtx); +} + +void BgMoriBigst_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriBigst* this = (BgMoriBigst*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgMoriBigst_SetupWaitForMoriTex(BgMoriBigst* this, GlobalContext* globalCtx) { + BgMoriBigst_SetupAction(this, BgMoriBigst_WaitForMoriTex); +} + +void BgMoriBigst_WaitForMoriTex(BgMoriBigst* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + thisx->draw = BgMoriBigst_Draw; + if (Flags_GetClear(globalCtx, thisx->room) && (GET_PLAYER(globalCtx)->actor.world.pos.y > 700.0f)) { + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0x3F)) { + BgMoriBigst_SetupDone(this, globalCtx); + } else { + BgMoriBigst_SetupStalfosFight(this, globalCtx); + } + } else { + BgMoriBigst_SetupNoop(this, globalCtx); + } + } +} + +void BgMoriBigst_SetupNoop(BgMoriBigst* this, GlobalContext* globalCtx) { + BgMoriBigst_SetupAction(this, NULL); +} + +void BgMoriBigst_SetupStalfosFight(BgMoriBigst* this, GlobalContext* globalCtx) { + Actor* stalfos; + + BgMoriBigst_SetupAction(this, BgMoriBigst_StalfosFight); + Flags_UnsetClear(globalCtx, this->dyna.actor.room); + stalfos = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_TEST, 209.0f, 827.0f, + -3320.0f, 0, 0, 0, 1); + if (stalfos != NULL) { + this->dyna.actor.child = NULL; + this->dyna.actor.home.rot.z++; + } else { + // "Second Stalfos failure" + osSyncPrintf("Warning : 第2スタルフォス発生失敗\n"); + } + Flags_SetClear(globalCtx, this->dyna.actor.room); +} + +void BgMoriBigst_StalfosFight(BgMoriBigst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((this->dyna.actor.home.rot.z == 0) && + ((this->dyna.actor.home.pos.y - 5.0f) <= GET_PLAYER(globalCtx)->actor.world.pos.y)) { + BgMoriBigst_SetupFall(this, globalCtx); + OnePointCutscene_Init(globalCtx, 3220, 72, &this->dyna.actor, MAIN_CAM); + } +} + +void BgMoriBigst_SetupFall(BgMoriBigst* this, GlobalContext* globalCtx) { + BgMoriBigst_SetupAction(this, BgMoriBigst_Fall); +} + +void BgMoriBigst_Fall(BgMoriBigst* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + if (this->dyna.actor.world.pos.y <= this->dyna.actor.home.pos.y) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + BgMoriBigst_SetupLanding(this, globalCtx); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + OnePointCutscene_Init(globalCtx, 1020, 8, &this->dyna.actor, MAIN_CAM); + func_8002DF38(globalCtx, NULL, 0x3C); + } +} + +void BgMoriBigst_SetupLanding(BgMoriBigst* this, GlobalContext* globalCtx) { + s32 pad; + s32 quake; + + BgMoriBigst_SetupAction(this, BgMoriBigst_Landing); + this->waitTimer = 18; + quake = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quake, 25000); + Quake_SetQuakeValues(quake, 5, 0, 0, 0); + Quake_SetCountdown(quake, 16); +} + +void BgMoriBigst_Landing(BgMoriBigst* this, GlobalContext* globalCtx) { + if (this->waitTimer <= 0) { + BgMoriBigst_SetupStalfosPairFight(this, globalCtx); + } +} + +void BgMoriBigst_SetupStalfosPairFight(BgMoriBigst* this, GlobalContext* globalCtx) { + Actor* stalfos1; + Actor* stalfos2; + + BgMoriBigst_SetupAction(this, BgMoriBigst_StalfosPairFight); + Flags_UnsetClear(globalCtx, this->dyna.actor.room); + stalfos1 = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_TEST, 70.0f, 827.0f, + -3383.0f, 0, 0, 0, 5); + if (stalfos1 != NULL) { + this->dyna.actor.child = NULL; + this->dyna.actor.home.rot.z++; + } else { + // "Warning: 3-1 Stalfos failure" + osSyncPrintf("Warning : 第3-1スタルフォス発生失敗\n"); + } + stalfos2 = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_TEST, 170.0f, 827.0f, + -3260.0f, 0, 0, 0, 5); + if (stalfos2 != NULL) { + this->dyna.actor.child = NULL; + this->dyna.actor.home.rot.z++; + } else { + // "Warning: 3-2 Stalfos failure" + osSyncPrintf("Warning : 第3-2スタルフォス発生失敗\n"); + } + Flags_SetClear(globalCtx, this->dyna.actor.room); +} + +void BgMoriBigst_StalfosPairFight(BgMoriBigst* this, GlobalContext* globalCtx) { + if ((this->dyna.actor.home.rot.z == 0) && !Player_InCsMode(globalCtx)) { + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + BgMoriBigst_SetupDone(this, globalCtx); + } +} + +void BgMoriBigst_SetupDone(BgMoriBigst* this, GlobalContext* globalCtx) { + BgMoriBigst_SetupAction(this, NULL); +} + +void BgMoriBigst_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriBigst* this = (BgMoriBigst*)thisx; + + Actor_SetFocus(&this->dyna.actor, 50.0f); + if (this->waitTimer > 0) { + this->waitTimer--; + } + if (func_80043590(&this->dyna)) { + func_80074CE8(globalCtx, 6); + } + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgMoriBigst_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriBigst* this = (BgMoriBigst*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_bigst.c", 541); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_bigst.c", 548), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gMoriBigstDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_bigst.c", 553); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Bigst/z_bg_mori_bigst.h b/soh/src/overlays/actors/ovl_Bg_Mori_Bigst/z_bg_mori_bigst.h new file mode 100644 index 000000000..7f18e8b0c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Bigst/z_bg_mori_bigst.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_MORI_BIGST_H +#define Z_BG_MORI_BIGST_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriBigst; + +typedef void (*BgMoriBigstActionFunc)(struct BgMoriBigst*, GlobalContext*); + +typedef struct BgMoriBigst { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMoriBigstActionFunc actionFunc; + /* 0x0168 */ s16 waitTimer; + /* 0x016A */ s8 moriTexObjIndex; +} BgMoriBigst; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Elevator/z_bg_mori_elevator.c b/soh/src/overlays/actors/ovl_Bg_Mori_Elevator/z_bg_mori_elevator.c new file mode 100644 index 000000000..edda63bbb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Elevator/z_bg_mori_elevator.c @@ -0,0 +1,264 @@ +#include "z_bg_mori_elevator.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgMoriElevator_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriElevator_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriElevator_Update(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriElevator_SetupWaitAfterInit(BgMoriElevator* this); +void BgMoriElevator_WaitAfterInit(BgMoriElevator* this, GlobalContext* globalCtx); +void BgMoriElevator_SetupSetPosition(BgMoriElevator* this); +void BgMoriElevator_SetPosition(BgMoriElevator* this, GlobalContext* globalCtx); +void BgMoriElevator_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgMoriElevator_StopMovement(BgMoriElevator* this); +void func_808A2008(BgMoriElevator* this, GlobalContext* globalCtx); +void BgMoriElevator_MoveIntoGround(BgMoriElevator* this, GlobalContext* globalCtx); +void BgMoriElevator_MoveAboveGround(BgMoriElevator* this, GlobalContext* globalCtx); + +static s16 sIsSpawned = false; + +const ActorInit Bg_Mori_Elevator_InitVars = { + ACTOR_BG_MORI_ELEVATOR, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriElevator), + (ActorFunc)BgMoriElevator_Init, + (ActorFunc)BgMoriElevator_Destroy, + (ActorFunc)BgMoriElevator_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 3000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +f32 func_808A1800(f32* pValue, f32 target, f32 scale, f32 maxStep, f32 minStep) { + f32 var = (target - *pValue) * scale; + + if (*pValue < target) { + if (maxStep < var) { + var = maxStep; + } else { + if (var < minStep) { + var = minStep; + } + } + *pValue = (*pValue + var); + + if (target < *pValue) { + *pValue = target; + } + } else { + if (target < *pValue) { + if (var < (-maxStep)) { + var = (-maxStep); + } else { + if ((-minStep) < var) { + var = (-minStep); + } + } + *pValue = (*pValue + var); + if (*pValue < target) { + *pValue = target; + } + } else { + var = 0.0f; + } + } + return var; +} + +void func_808A18FC(BgMoriElevator* this, f32 distTo) { + f32 temp; + + temp = fabsf(distTo) * 0.09f; + func_800F436C(&this->dyna.actor.projectedPos, NA_SE_EV_ELEVATOR_MOVE2 - SFX_FLAG, CLAMP(temp, 0.0f, 1.0f)); +} + +void BgMoriElevator_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMoriElevator* this = (BgMoriElevator*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + this->unk_172 = sIsSpawned; + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + Actor_Kill(thisx); + // "Forest Temple obj elevator Bank Danger!" + osSyncPrintf("Error : 森の神殿 obj elevator バンク危険!(%s %d)\n", "../z_bg_mori_elevator.c", 277); + } else { + switch (sIsSpawned) { + case false: + // "Forest Temple elevator CT" + osSyncPrintf("森の神殿 elevator CT\n"); + sIsSpawned = true; + this->dyna.actor.room = -1; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gMoriElevatorCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + BgMoriElevator_SetupWaitAfterInit(this); + break; + case true: + Actor_Kill(thisx); + break; + } + } +} + +void BgMoriElevator_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMoriElevator* this = (BgMoriElevator*)thisx; + + if (this->unk_172 == 0) { + // "Forest Temple elevator DT" + osSyncPrintf("森の神殿 elevator DT\n"); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + sIsSpawned = false; + } +} + +s32 BgMoriElevator_IsPlayerRiding(BgMoriElevator* this, GlobalContext* globalCtx) { + return ((this->dyna.unk_160 & 2) && !(this->unk_170 & 2) && + ((GET_PLAYER(globalCtx)->actor.world.pos.y - this->dyna.actor.world.pos.y) < 80.0f)); +} + +void BgMoriElevator_SetupWaitAfterInit(BgMoriElevator* this) { + this->actionFunc = BgMoriElevator_WaitAfterInit; +} + +void BgMoriElevator_WaitAfterInit(BgMoriElevator* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + if (globalCtx->roomCtx.curRoom.num == 2) { + this->dyna.actor.world.pos.y = 73.0f; + BgMoriElevator_SetupSetPosition(this); + } else { + // "Error: Forest Temple obj elevator Room setting is dangerous" + osSyncPrintf("Error : 森の神殿 obj elevator 部屋設定が危険(%s %d)\n", "../z_bg_mori_elevator.c", 371); + } + } else { + BgMoriElevator_SetupSetPosition(this); + } + this->dyna.actor.draw = BgMoriElevator_Draw; + } +} + +void func_808A1C30(BgMoriElevator* this) { + this->actionFunc = BgMoriElevator_MoveIntoGround; +} + +void BgMoriElevator_MoveIntoGround(BgMoriElevator* this, GlobalContext* globalCtx) { + f32 distToTarget; + + func_808A1800(&this->dyna.actor.velocity.y, 2.0f, 0.05f, 1.0f, 0.0f); + distToTarget = func_808A1800(&this->dyna.actor.world.pos.y, 73.0f, 0.08f, this->dyna.actor.velocity.y, 1.5f); + if (fabsf(distToTarget) < 0.001f) { + BgMoriElevator_SetupSetPosition(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ELEVATOR_STOP); + } else { + func_808A18FC(this, distToTarget); + } +} + +void func_808A1CF4(BgMoriElevator* this, GlobalContext* globalCtx) { + this->actionFunc = BgMoriElevator_MoveAboveGround; + OnePointCutscene_Init(globalCtx, 3230, 70, &this->dyna.actor, MAIN_CAM); + OnePointCutscene_Init(globalCtx, 1020, 15, &this->dyna.actor, MAIN_CAM); +} + +void BgMoriElevator_MoveAboveGround(BgMoriElevator* this, GlobalContext* globalCtx) { + f32 distToTarget; + + func_808A1800(&this->dyna.actor.velocity.y, 2.0f, 0.05f, 1.0f, 0.0f); + distToTarget = func_808A1800(&this->dyna.actor.world.pos.y, 233.0f, 0.08f, this->dyna.actor.velocity.y, 1.5f); + if (fabsf(distToTarget) < 0.001f) { + BgMoriElevator_SetupSetPosition(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ELEVATOR_STOP); + } else { + func_808A18FC(this, distToTarget); + } +} + +void BgMoriElevator_SetupSetPosition(BgMoriElevator* this) { + this->actionFunc = BgMoriElevator_SetPosition; +} + +void BgMoriElevator_SetPosition(BgMoriElevator* this, GlobalContext* globalCtx) { + s32 pad; + + if (BgMoriElevator_IsPlayerRiding(this, globalCtx)) { + if (globalCtx->roomCtx.curRoom.num == 2) { + this->targetY = -779.0f; + BgMoriElevator_StopMovement(this); + } else if (globalCtx->roomCtx.curRoom.num == 17) { + this->targetY = 233.0f; + BgMoriElevator_StopMovement(this); + } else { + // "Error:Forest Temple obj elevator Room setting is dangerous(%s %d)" + osSyncPrintf("Error : 森の神殿 obj elevator 部屋設定が危険(%s %d)\n", "../z_bg_mori_elevator.c", 479); + } + } else if ((globalCtx->roomCtx.curRoom.num == 2) && (this->dyna.actor.world.pos.y < -275.0f)) { + this->targetY = 233.0f; + BgMoriElevator_StopMovement(this); + } else if ((globalCtx->roomCtx.curRoom.num == 17) && (-275.0f < this->dyna.actor.world.pos.y)) { + this->targetY = -779.0f; + BgMoriElevator_StopMovement(this); + } else if ((globalCtx->roomCtx.curRoom.num == 2) && Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) && + (this->unk_16C == 0)) { + this->targetY = 73.0f; + func_808A1C30(this); + } else if ((globalCtx->roomCtx.curRoom.num == 2) && !Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) && + (this->unk_16C != 0)) { + this->targetY = 233.0f; + func_808A1CF4(this, globalCtx); + } +} + +void BgMoriElevator_StopMovement(BgMoriElevator* this) { + this->actionFunc = func_808A2008; + this->dyna.actor.velocity.y = 0.0f; +} + +void func_808A2008(BgMoriElevator* this, GlobalContext* globalCtx) { + f32 distTo; + + func_808A1800(&this->dyna.actor.velocity.y, 12.0f, 0.1f, 1.0f, 0.0f); + distTo = func_808A1800(&this->dyna.actor.world.pos.y, this->targetY, 0.1f, this->dyna.actor.velocity.y, 0.3f); + if (fabsf(distTo) < 0.001f) { + BgMoriElevator_SetupSetPosition(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ELEVATOR_STOP); + + } else { + func_808A18FC(this, distTo); + } +} + +void BgMoriElevator_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMoriElevator* this = (BgMoriElevator*)thisx; + + this->actionFunc(this, globalCtx); + this->unk_170 = this->dyna.unk_160; + this->unk_16C = Flags_GetSwitch(globalCtx, (thisx->params & 0x3F)); +} + +void BgMoriElevator_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriElevator* this = (BgMoriElevator*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_elevator.c", 575); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_elevator.c", 580), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gMoriElevatorDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_elevator.c", 584); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Elevator/z_bg_mori_elevator.h b/soh/src/overlays/actors/ovl_Bg_Mori_Elevator/z_bg_mori_elevator.h new file mode 100644 index 000000000..6c202bb22 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Elevator/z_bg_mori_elevator.h @@ -0,0 +1,21 @@ +#ifndef Z_BG_MORI_ELEVATOR_H +#define Z_BG_MORI_ELEVATOR_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriElevator; + +typedef void (*BgMoriElevatorActionFunc)(struct BgMoriElevator*, GlobalContext*); + +typedef struct BgMoriElevator { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMoriElevatorActionFunc actionFunc; + /* 0x0168 */ f32 targetY; + /* 0x016C */ s32 unk_16C; + /* 0x0170 */ u8 unk_170; + /* 0x0171 */ s8 moriTexObjIndex; + /* 0x0172 */ s16 unk_172; +} BgMoriElevator; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Hashigo/z_bg_mori_hashigo.c b/soh/src/overlays/actors/ovl_Bg_Mori_Hashigo/z_bg_mori_hashigo.c new file mode 100644 index 000000000..16ac3b821 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Hashigo/z_bg_mori_hashigo.c @@ -0,0 +1,302 @@ +/* + * File: z_bg_mori_hashigo.c + * Overlay: ovl_Bg_Mori_Hashigo + * Description: Falling ladder and clasp that holds it. Unused. + */ + +#include "z_bg_mori_hashigo.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS 0 + +void BgMoriHashigo_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHashigo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHashigo_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHashigo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriHashigo_SetupWaitForMoriTex(BgMoriHashigo* this); +void BgMoriHashigo_WaitForMoriTex(BgMoriHashigo* this, GlobalContext* globalCtx); +void BgMoriHashigo_SetupClasp(BgMoriHashigo* this); +void BgMoriHashigo_Clasp(BgMoriHashigo* this, GlobalContext* globalCtx); +void BgMoriHashigo_SetupLadderWait(BgMoriHashigo* this); +void BgMoriHashigo_LadderWait(BgMoriHashigo* this, GlobalContext* globalCtx); +void BgMoriHashigo_SetupLadderFall(BgMoriHashigo* this); +void BgMoriHashigo_LadderFall(BgMoriHashigo* this, GlobalContext* globalCtx); +void BgMoriHashigo_SetupLadderRest(BgMoriHashigo* this); + +const ActorInit Bg_Mori_Hashigo_InitVars = { + ACTOR_BG_MORI_HASHIGO, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriHashigo), + (ActorFunc)BgMoriHashigo_Init, + (ActorFunc)BgMoriHashigo_Destroy, + (ActorFunc)BgMoriHashigo_Update, + NULL, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK4, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F820, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 25 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChainClasp[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), ICHAIN_U8(targetMode, 3, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 40, ICHAIN_CONTINUE), ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +static InitChainEntry sInitChainLadder[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgMoriHashigo_InitDynapoly(BgMoriHashigo* this, GlobalContext* globalCtx, CollisionHeader* collision, + s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader; + s32 pad2; + + colHeader = NULL; + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG login failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_mori_hashigo.c", 164, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgMoriHashigo_InitCollider(BgMoriHashigo* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->colliderItems); + + this->collider.elements[0].dim.worldSphere.center.x = (s16)this->dyna.actor.world.pos.x; + this->collider.elements[0].dim.worldSphere.center.y = (s16)this->dyna.actor.world.pos.y + 21; + this->collider.elements[0].dim.worldSphere.center.z = (s16)this->dyna.actor.world.pos.z; + this->collider.elements[0].dim.worldSphere.radius = 19; +} + +s32 BgMoriHashigo_SpawnLadder(BgMoriHashigo* this, GlobalContext* globalCtx) { + f32 sn; + f32 cs; + Vec3f pos; + Actor* ladder; + + cs = Math_CosS(this->dyna.actor.shape.rot.y); + sn = Math_SinS(this->dyna.actor.shape.rot.y); + + pos.x = 6.0f * sn + this->dyna.actor.world.pos.x; + pos.y = -210.0f + this->dyna.actor.world.pos.y; + pos.z = 6.0f * cs + this->dyna.actor.world.pos.z; + + ladder = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_MORI_HASHIGO, pos.x, pos.y, + pos.z, this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, + this->dyna.actor.world.rot.z, 0); + if (ladder != NULL) { + return true; + } else { + // "Ladder failure" + osSyncPrintf("Error : 梯子の発生失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_mori_hashigo.c", 220, + this->dyna.actor.params); + return false; + } +} + +s32 BgMoriHashigo_InitClasp(BgMoriHashigo* this, GlobalContext* globalCtx) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChainClasp); + this->dyna.actor.flags |= ACTOR_FLAG_0; + Actor_SetFocus(&this->dyna.actor, 55.0f); + BgMoriHashigo_InitCollider(this, globalCtx); + if ((this->dyna.actor.params == HASHIGO_CLASP) && !BgMoriHashigo_SpawnLadder(this, globalCtx)) { + return false; + } else { + return true; + } +} + +s32 BgMoriHashigo_InitLadder(BgMoriHashigo* this, GlobalContext* globalCtx) { + BgMoriHashigo_InitDynapoly(this, globalCtx, &gMoriHashigoCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChainLadder); + return true; +} + +void BgMoriHashigo_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashigo* this = (BgMoriHashigo*)thisx; + + if (this->dyna.actor.params == HASHIGO_CLASP) { + if (!BgMoriHashigo_InitClasp(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + return; + } + } else if (this->dyna.actor.params == HASHIGO_LADDER) { + if (!BgMoriHashigo_InitLadder(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + return; + } + } + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + // "Bank danger!" + osSyncPrintf("Error : バンク危険!(arg_data 0x%04x)(%s %d)\n", this->dyna.actor.params, + "../z_bg_mori_hashigo.c", 312); + Actor_Kill(&this->dyna.actor); + } else { + BgMoriHashigo_SetupWaitForMoriTex(this); + // "(Forest Temple Ladder and its clasp)" + osSyncPrintf("(森の神殿 梯子とその留め金)(arg_data 0x%04x)\n", this->dyna.actor.params); + } +} + +void BgMoriHashigo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashigo* this = (BgMoriHashigo*)thisx; + + if (this->dyna.actor.params == HASHIGO_LADDER) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + if (this->dyna.actor.params == HASHIGO_CLASP) { + Collider_DestroyJntSph(globalCtx, &this->collider); + } +} + +void BgMoriHashigo_SetupWaitForMoriTex(BgMoriHashigo* this) { + this->actionFunc = BgMoriHashigo_WaitForMoriTex; +} + +void BgMoriHashigo_WaitForMoriTex(BgMoriHashigo* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + if (this->dyna.actor.params == HASHIGO_CLASP) { + BgMoriHashigo_SetupClasp(this); + } else if (this->dyna.actor.params == HASHIGO_LADDER) { + BgMoriHashigo_SetupLadderWait(this); + } + this->dyna.actor.draw = BgMoriHashigo_Draw; + } +} + +void BgMoriHashigo_SetupClasp(BgMoriHashigo* this) { + this->actionFunc = BgMoriHashigo_Clasp; +} + +void BgMoriHashigo_Clasp(BgMoriHashigo* this, GlobalContext* globalCtx) { + if (this->hitTimer <= 0) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + this->hitTimer = 10; + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +void BgMoriHashigo_SetupLadderWait(BgMoriHashigo* this) { + this->actionFunc = BgMoriHashigo_LadderWait; +} + +void BgMoriHashigo_LadderWait(BgMoriHashigo* this, GlobalContext* globalCtx) { + BgMoriHashigo* clasp = (BgMoriHashigo*)this->dyna.actor.parent; + + if (clasp->hitTimer > 0) { + BgMoriHashigo_SetupLadderFall(this); + } +} + +void BgMoriHashigo_SetupLadderFall(BgMoriHashigo* this) { + this->bounceCounter = 0; + this->actionFunc = BgMoriHashigo_LadderFall; + this->dyna.actor.gravity = -1.0f; + this->dyna.actor.minVelocityY = -10.0f; + this->dyna.actor.velocity.y = 2.0f; +} + +void BgMoriHashigo_LadderFall(BgMoriHashigo* this, GlobalContext* globalCtx) { + static f32 bounceSpeed[3] = { 4.0f, 2.7f, 1.7f }; + Actor* thisx = &this->dyna.actor; + + Actor_MoveForward(thisx); + if ((thisx->bgCheckFlags & 1) && (thisx->velocity.y < 0.0f)) { + if (this->bounceCounter >= ARRAY_COUNT(bounceSpeed)) { + BgMoriHashigo_SetupLadderRest(this); + } else { + Actor_UpdateBgCheckInfo(globalCtx, thisx, 0.0f, 0.0f, 0.0f, 0x1C); + thisx->velocity.y = bounceSpeed[this->bounceCounter]; + this->bounceCounter++; + } + } else { + Actor_UpdateBgCheckInfo(globalCtx, thisx, 0.0f, 0.0f, 0.0f, 0x1C); + } +} + +void BgMoriHashigo_SetupLadderRest(BgMoriHashigo* this) { + this->actionFunc = NULL; + this->dyna.actor.gravity = 0.0f; + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; +} + +void BgMoriHashigo_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashigo* this = (BgMoriHashigo*)thisx; + + if (this->hitTimer > 0) { + this->hitTimer--; + } + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgMoriHashigo_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashigo* this = (BgMoriHashigo*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_hashigo.c", 516); + func_80093D18(globalCtx->state.gfxCtx); + if (1) {} + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_hashigo.c", 521), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + switch (this->dyna.actor.params) { + case HASHIGO_CLASP: + gSPDisplayList(POLY_OPA_DISP++, gMoriHashigoClaspDL); + break; + case HASHIGO_LADDER: + gSPDisplayList(POLY_OPA_DISP++, gMoriHashigoLadderDL); + break; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_hashigo.c", 531); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Hashigo/z_bg_mori_hashigo.h b/soh/src/overlays/actors/ovl_Bg_Mori_Hashigo/z_bg_mori_hashigo.h new file mode 100644 index 000000000..55c07ca92 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Hashigo/z_bg_mori_hashigo.h @@ -0,0 +1,26 @@ +#ifndef Z_BG_MORI_HASHIGO_H +#define Z_BG_MORI_HASHIGO_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriHashigo; + +typedef void (*BgMoriHashigoActionFunc)(struct BgMoriHashigo*, GlobalContext*); + +typedef struct BgMoriHashigo { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderJntSph collider; + /* 0x0184 */ ColliderJntSphElement colliderItems[1]; + /* 0x01C4 */ BgMoriHashigoActionFunc actionFunc; + /* 0x01C8 */ s16 hitTimer; + /* 0x01CA */ s16 bounceCounter; + /* 0x01CC */ s8 moriTexObjIndex; +} BgMoriHashigo; // size = 0x01D0 + +typedef enum { + /* -1 */ HASHIGO_CLASP = -1, + /* 0 */ HASHIGO_LADDER +} HasigoType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Hashira4/z_bg_mori_hashira4.c b/soh/src/overlays/actors/ovl_Bg_Mori_Hashira4/z_bg_mori_hashira4.c new file mode 100644 index 000000000..0317870e9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Hashira4/z_bg_mori_hashira4.c @@ -0,0 +1,175 @@ +/* + * File: z_bg_mori_hashira4 + * Overlay: ovl_Bg_Mori_Hashira4 + * Description: Forest Temple gates and rotating pillars + */ + +#include "z_bg_mori_hashira4.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgMoriHashira4_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHashira4_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHashira4_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHashira4_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriHashira4_SetupWaitForMoriTex(BgMoriHashira4* this); +void BgMoriHashira4_WaitForMoriTex(BgMoriHashira4* this, GlobalContext* globalCtx); +void BgMoriHashira4_SetupPillarsRotate(BgMoriHashira4* this); +void BgMoriHashira4_PillarsRotate(BgMoriHashira4* this, GlobalContext* globalCtx); +void BgMoriHashira4_GateWait(BgMoriHashira4* this, GlobalContext* globalCtx); +void BgMoriHashira4_GateOpen(BgMoriHashira4* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mori_Hashira4_InitVars = { + ACTOR_BG_MORI_HASHIRA4, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriHashira4), + (ActorFunc)BgMoriHashira4_Init, + (ActorFunc)BgMoriHashira4_Destroy, + (ActorFunc)BgMoriHashira4_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 700, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +static Gfx* sDisplayLists[] = { gMoriHashiraPlatformsDL, gMoriHashiraGateDL }; + +static s16 sUnkTimer; // seems to be unused + +void BgMoriHashira4_SetupAction(BgMoriHashira4* this, BgMoriHashira4ActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgMoriHashira4_InitDynaPoly(BgMoriHashira4* this, GlobalContext* globalCtx, CollisionHeader* collision, + s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader; + s32 pad2; + + colHeader = NULL; + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG login failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_mori_hashira4.c", 155, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgMoriHashira4_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashira4* this = (BgMoriHashira4*)thisx; + + this->switchFlag = (this->dyna.actor.params >> 8) & 0x3F; + this->dyna.actor.params &= 0xFF; + + if (this->dyna.actor.params == 0) { + BgMoriHashira4_InitDynaPoly(this, globalCtx, &gMoriHashira1Col, DPM_UNK3); + } else { + BgMoriHashira4_InitDynaPoly(this, globalCtx, &gMoriHashira2Col, DPM_UNK); + } + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + Actor_Kill(&this->dyna.actor); + // "Bank danger!" + osSyncPrintf("Error : バンク危険!(arg_data 0x%04x)(%s %d)\n", this->dyna.actor.params, + "../z_bg_mori_hashira4.c", 196); + return; + } + if ((this->dyna.actor.params != 0) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + Actor_Kill(&this->dyna.actor); + return; + } + Actor_SetFocus(&this->dyna.actor, 50.0f); + BgMoriHashira4_SetupWaitForMoriTex(this); + // "(4 pillars of the Forest Temple) Bank danger" + osSyncPrintf("(森の神殿 4本柱)(arg_data 0x%04x)\n", this->dyna.actor.params); + sUnkTimer = 0; +} + +void BgMoriHashira4_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashira4* this = (BgMoriHashira4*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgMoriHashira4_SetupWaitForMoriTex(BgMoriHashira4* this) { + BgMoriHashira4_SetupAction(this, BgMoriHashira4_WaitForMoriTex); +} + +void BgMoriHashira4_WaitForMoriTex(BgMoriHashira4* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + this->gateTimer = 0; + if (this->dyna.actor.params == 0) { + BgMoriHashira4_SetupPillarsRotate(this); + } else { + BgMoriHashira4_SetupAction(this, BgMoriHashira4_GateWait); + } + this->dyna.actor.draw = BgMoriHashira4_Draw; + } +} + +void BgMoriHashira4_SetupPillarsRotate(BgMoriHashira4* this) { + BgMoriHashira4_SetupAction(this, BgMoriHashira4_PillarsRotate); +} + +void BgMoriHashira4_PillarsRotate(BgMoriHashira4* this, GlobalContext* globalCtx) { + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y += 0x96; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ROLL_STAND_2 - SFX_FLAG); +} + +void BgMoriHashira4_GateWait(BgMoriHashira4* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag) || (this->gateTimer != 0)) { + this->gateTimer++; + if (this->gateTimer > 30) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_OPEN); + BgMoriHashira4_SetupAction(this, BgMoriHashira4_GateOpen); + OnePointCutscene_Init(globalCtx, 6010, 20, &this->dyna.actor, MAIN_CAM); + sUnkTimer++; + } + } +} + +void BgMoriHashira4_GateOpen(BgMoriHashira4* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 120.0f, 10.0f)) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgMoriHashira4_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashira4* this = (BgMoriHashira4*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgMoriHashira4_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriHashira4* this = (BgMoriHashira4*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_hashira4.c", 339); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_hashira4.c", 344), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, sDisplayLists[this->dyna.actor.params]); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_hashira4.c", 348); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Hashira4/z_bg_mori_hashira4.h b/soh/src/overlays/actors/ovl_Bg_Mori_Hashira4/z_bg_mori_hashira4.h new file mode 100644 index 000000000..447acdab9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Hashira4/z_bg_mori_hashira4.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_MORI_HASHIRA4_H +#define Z_BG_MORI_HASHIRA4_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriHashira4; + +typedef void (*BgMoriHashira4ActionFunc)(struct BgMoriHashira4*, GlobalContext*); + +typedef struct BgMoriHashira4 { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMoriHashira4ActionFunc actionFunc; + /* 0x0168 */ s8 moriTexObjIndex; + /* 0x0169 */ s8 switchFlag; + /* 0x016A */ s16 gateTimer; +} BgMoriHashira4; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Hineri/z_bg_mori_hineri.c b/soh/src/overlays/actors/ovl_Bg_Mori_Hineri/z_bg_mori_hineri.c new file mode 100644 index 000000000..8dfbff765 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Hineri/z_bg_mori_hineri.c @@ -0,0 +1,287 @@ +/* + * File: z_bg_mori_hineri.c + * Overlay: ovl_Bg_Mori_Hineri + * Description: Twisting hallway in Forest Temple + */ + +#include "z_bg_mori_hineri.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_box/object_box.h" +#include "objects/object_mori_hineri1/object_mori_hineri1.h" +#include "objects/object_mori_hineri1a/object_mori_hineri1a.h" +#include "objects/object_mori_hineri2/object_mori_hineri2.h" +#include "objects/object_mori_hineri2a/object_mori_hineri2a.h" +#include "objects/object_mori_tex/object_mori_tex.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgMoriHineri_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHineri_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHineri_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHineri_DrawHallAndRoom(Actor* thisx, GlobalContext* globalCtx); +void BgMoriHineri_Reset(void); + +void func_808A39FC(BgMoriHineri* this, GlobalContext* globalCtx); +void func_808A3E54(BgMoriHineri* this, GlobalContext* globalCtx); +void func_808A3C8C(BgMoriHineri* this, GlobalContext* globalCtx); +void BgMoriHineri_SpawnBossKeyChest(BgMoriHineri* this, GlobalContext* globalCtx); +void BgMoriHineri_DoNothing(BgMoriHineri* this, GlobalContext* globalCtx); +void func_808A3D58(BgMoriHineri* this, GlobalContext* globalCtx); + +static s16 sNextCamIdx = SUBCAM_NONE; + +const ActorInit Bg_Mori_Hineri_InitVars = { + ACTOR_BG_MORI_HINERI, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgMoriHineri), + (ActorFunc)BgMoriHineri_Init, + (ActorFunc)BgMoriHineri_Destroy, + (ActorFunc)BgMoriHineri_Update, + NULL, + BgMoriHineri_Reset, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +static Gfx* sDLists[] = { + object_mori_hineri1_DL_0024E0, + object_mori_hineri1a_DL_001980, + object_mori_hineri2_DL_0020F0, + object_mori_hineri2a_DL_002B70, +}; + +void BgMoriHineri_Init(Actor* thisx, GlobalContext* globalCtx) { + BgMoriHineri* this = (BgMoriHineri*)thisx; + s8 moriHineriObjIdx; + u32 switchFlagParam; + s32 t6; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + + switchFlagParam = this->dyna.actor.params & 0x3F; + t6 = this->dyna.actor.params & 0x4000; + + if (t6 != 0) { + this->switchFlag = switchFlagParam; + } else { + this->switchFlag = ((this->dyna.actor.params >> 8) & 0x3F); + this->switchFlag = (Flags_GetSwitch(globalCtx, this->switchFlag)) ? 1 : 0; + } + this->dyna.actor.params = ((this->dyna.actor.params & 0x8000) >> 0xE); + if (Flags_GetSwitch(globalCtx, switchFlagParam)) { + if (this->dyna.actor.params == 0) { + this->dyna.actor.params = 1; + } else if (this->dyna.actor.params == 2) { + this->dyna.actor.params = 3; + } + } + this->boxObjIdx = -1; + if (this->dyna.actor.params == 0) { + this->moriHineriObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI1); + if (t6 == 0) { + this->boxObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_BOX); + } + } else { + if (this->dyna.actor.params == 1) { + moriHineriObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI1A); + } else { + moriHineriObjIdx = (this->dyna.actor.params == 2) + ? Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI2) + : Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI2A); + } + this->moriHineriObjIdx = moriHineriObjIdx; + } + this->moriTexObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (t6 != 0) { + this->dyna.actor.params += 4; + } + if ((this->moriHineriObjIdx < 0) || (this->moriTexObjIdx < 0)) { + Actor_Kill(&this->dyna.actor); + } else { + this->actionFunc = func_808A39FC; + } +} + +void BgMoriHineri_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgMoriHineri* this = (BgMoriHineri*)thisx; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808A39FC(BgMoriHineri* this, GlobalContext* globalCtx) { + CollisionHeader* colHeader; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriHineriObjIdx) && + Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIdx) && + ((this->boxObjIdx < 0) || Object_IsLoaded(&globalCtx->objectCtx, this->boxObjIdx))) { + this->dyna.actor.objBankIndex = this->moriHineriObjIdx; + if (this->dyna.actor.params >= 4) { + this->dyna.actor.params -= 4; + if (this->dyna.actor.params == 0) { + this->moriHineriObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI1A); + } else if (this->dyna.actor.params == 1) { + this->moriHineriObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI1); + } else { + this->moriHineriObjIdx = (this->dyna.actor.params == 2) + ? Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI2A) + : Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_HINERI2); + } + if (this->moriHineriObjIdx < 0) { + Actor_Kill(&this->dyna.actor); + } else { + this->actionFunc = func_808A3D58; + } + } else { + Actor_SetObjectDependency(globalCtx, &this->dyna.actor); + colHeader = NULL; + this->dyna.actor.draw = BgMoriHineri_DrawHallAndRoom; + if (this->dyna.actor.params == 0) { + this->actionFunc = func_808A3C8C; + CollisionHeader_GetVirtual(&object_mori_hineri1_Col_0054B8, &colHeader); + } else if (this->dyna.actor.params == 1) { + this->actionFunc = BgMoriHineri_SpawnBossKeyChest; + CollisionHeader_GetVirtual(&object_mori_hineri1a_Col_003490, &colHeader); + } else if (this->dyna.actor.params == 2) { + this->actionFunc = BgMoriHineri_DoNothing; + CollisionHeader_GetVirtual(&object_mori_hineri2_Col_0043D0, &colHeader); + } else { + this->actionFunc = func_808A3C8C; + CollisionHeader_GetVirtual(&object_mori_hineri2a_Col_006078, &colHeader); + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } + } +} + +void BgMoriHineri_DoNothing(BgMoriHineri* this, GlobalContext* globalCtx) { +} + +void BgMoriHineri_SpawnBossKeyChest(BgMoriHineri* this, GlobalContext* globalCtx) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOX, this->dyna.actor.world.pos.x + 147.0f, + this->dyna.actor.world.pos.y + -245.0f, this->dyna.actor.world.pos.z + -453.0f, 0, 0x4000, 0, 0x27EE); + this->actionFunc = BgMoriHineri_DoNothing; +} + +void func_808A3C8C(BgMoriHineri* this, GlobalContext* globalCtx) { + f32 f0; + Player* player = GET_PLAYER(globalCtx); + + f0 = 1100.0f - (player->actor.world.pos.z - this->dyna.actor.world.pos.z); + this->dyna.actor.shape.rot.z = CLAMP(f0, 0.0f, 1000.0f) * 16.384f; + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON1); + if (this->dyna.actor.params != 0) { + this->dyna.actor.shape.rot.z = -this->dyna.actor.shape.rot.z; + } +} + +void func_808A3D58(BgMoriHineri* this, GlobalContext* globalCtx) { + s16 mainCamChildIdx; + + if ((Flags_GetSwitch(globalCtx, this->switchFlag) && + (this->dyna.actor.params == 0 || this->dyna.actor.params == 2)) || + (!Flags_GetSwitch(globalCtx, this->switchFlag) && + (this->dyna.actor.params == 1 || this->dyna.actor.params == 3))) { + this->dyna.actor.draw = BgMoriHineri_DrawHallAndRoom; + this->actionFunc = func_808A3E54; + + mainCamChildIdx = globalCtx->cameraPtrs[MAIN_CAM]->childCamIdx; + if ((mainCamChildIdx != SUBCAM_FREE) && + (globalCtx->cameraPtrs[mainCamChildIdx]->setting == CAM_SET_CS_TWISTED_HALLWAY)) { + OnePointCutscene_EndCutscene(globalCtx, mainCamChildIdx); + } + OnePointCutscene_Init(globalCtx, 3260, 40, &this->dyna.actor, MAIN_CAM); + sNextCamIdx = OnePointCutscene_Init(globalCtx, 3261, 40, &this->dyna.actor, MAIN_CAM); + } +} + +void func_808A3E54(BgMoriHineri* this, GlobalContext* globalCtx) { + s8 objBankIndex; + + if (globalCtx->activeCamera == sNextCamIdx) { + if (sNextCamIdx != MAIN_CAM) { + objBankIndex = this->dyna.actor.objBankIndex; + this->dyna.actor.objBankIndex = this->moriHineriObjIdx; + this->moriHineriObjIdx = objBankIndex; + this->dyna.actor.params ^= 1; + sNextCamIdx = MAIN_CAM; + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } else { + this->dyna.actor.draw = NULL; + this->actionFunc = func_808A3D58; + sNextCamIdx = SUBCAM_NONE; + } + } + if ((sNextCamIdx >= SUBCAM_FIRST) && + ((GET_ACTIVE_CAM(globalCtx)->eye.z - this->dyna.actor.world.pos.z) < 1100.0f)) { + func_8002F948(&this->dyna.actor, NA_SE_EV_FLOOR_ROLLING - SFX_FLAG); + } +} + +void BgMoriHineri_Update(Actor* thisx, GlobalContext* globalCtx) { + BgMoriHineri* this = (BgMoriHineri*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMoriHineri_DrawHallAndRoom(Actor* thisx, GlobalContext* globalCtx) { + BgMoriHineri* this = (BgMoriHineri*)thisx; + s8 objIndex; + MtxF mtx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_hineri.c", 611); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIdx].segment); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_hineri.c", 618), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sDLists[this->dyna.actor.params]); + if (this->boxObjIdx > 0) { + Matrix_Get(&mtx); + } + if ((this->actionFunc == func_808A3C8C) && (this->dyna.actor.shape.rot.z != 0)) { + func_80093D18(globalCtx->state.gfxCtx); + if (this->dyna.actor.params == 0) { + Matrix_Translate(-1761.0f, 1278.0f, -1821.0f, MTXMODE_NEW); + } else { + Matrix_Translate(1999.0f, 1278.0f, -1821.0f, MTXMODE_NEW); + } + Matrix_RotateZYX(0, -0x8000, this->dyna.actor.shape.rot.z, MTXMODE_APPLY); + Matrix_Translate(0.0f, -50.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_hineri.c", 652), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDungeonDoorDL); + } + if ((this->boxObjIdx > 0) && ((this->boxObjIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_BOX)) > 0) && + Object_IsLoaded(&globalCtx->objectCtx, this->boxObjIdx)) { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->boxObjIdx].segment); + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + Matrix_Put(&mtx); + Matrix_Translate(147.0f, -245.0f, -453.0f, MTXMODE_APPLY); + Matrix_RotateY(M_PI / 2, MTXMODE_APPLY); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_hineri.c", 689), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTreasureChestBossKeyChestFrontDL); + Matrix_Put(&mtx); + Matrix_Translate(167.0f, -218.0f, -453.0f, MTXMODE_APPLY); + if (Flags_GetTreasure(globalCtx, 0xE)) { + Matrix_RotateZ(0x3500 * (M_PI / 0x8000), MTXMODE_APPLY); + } else { + Matrix_RotateZ(M_PI, MTXMODE_APPLY); + } + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_hineri.c", 703), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTreasureChestBossKeyChestSideAndTopDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_hineri.c", 709); +} + +void BgMoriHineri_Reset() { + sNextCamIdx = SUBCAM_NONE; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Hineri/z_bg_mori_hineri.h b/soh/src/overlays/actors/ovl_Bg_Mori_Hineri/z_bg_mori_hineri.h new file mode 100644 index 000000000..a8187138c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Hineri/z_bg_mori_hineri.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_MORI_HINERI_H +#define Z_BG_MORI_HINERI_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriHineri; + +typedef void (*BgMoriHineriActionFunc)(struct BgMoriHineri*, GlobalContext*); + +typedef struct BgMoriHineri { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMoriHineriActionFunc actionFunc; + /* 0x0168 */ s8 moriHineriObjIdx; + /* 0x0169 */ s8 moriTexObjIdx; + /* 0x016A */ s8 boxObjIdx; + /* 0x016B */ s8 switchFlag; +} BgMoriHineri; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Idomizu/z_bg_mori_idomizu.c b/soh/src/overlays/actors/ovl_Bg_Mori_Idomizu/z_bg_mori_idomizu.c new file mode 100644 index 000000000..3c605926f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Idomizu/z_bg_mori_idomizu.c @@ -0,0 +1,184 @@ +/* + * File: z_bg_mori_idomizu.c + * Overlay: ovl_Bg_Mori_Idomizu + * Description: Square of water in Forest Temple well + */ + +#include "z_bg_mori_idomizu.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgMoriIdomizu_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriIdomizu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriIdomizu_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriIdomizu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriIdomizu_SetupWaitForMoriTex(BgMoriIdomizu* this); +void BgMoriIdomizu_WaitForMoriTex(BgMoriIdomizu* this, GlobalContext* globalCtx); +void BgMoriIdomizu_SetupMain(BgMoriIdomizu* this); +void BgMoriIdomizu_Main(BgMoriIdomizu* this, GlobalContext* globalCtx); + +static s16 sIsSpawned = false; + +const ActorInit Bg_Mori_Idomizu_InitVars = { + ACTOR_BG_MORI_IDOMIZU, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriIdomizu), + (ActorFunc)BgMoriIdomizu_Init, + (ActorFunc)BgMoriIdomizu_Destroy, + (ActorFunc)BgMoriIdomizu_Update, + NULL, + NULL, +}; + +void BgMoriIdomizu_SetupAction(BgMoriIdomizu* this, BgMoriIdomizuActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgMoriIdomizu_SetWaterLevel(GlobalContext* globalCtx, s16 waterLevel) { + WaterBox* waterBox = globalCtx->colCtx.colHeader->waterBoxes; + + waterBox[2].ySurface = waterLevel; + waterBox[3].ySurface = waterLevel; + waterBox[4].ySurface = waterLevel; +} + +void BgMoriIdomizu_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriIdomizu* this = (BgMoriIdomizu*)thisx; + + if (sIsSpawned) { + Actor_Kill(&this->actor); + return; + } + this->actor.scale.x = 1.1f; + this->actor.scale.y = 1.0f; + this->actor.scale.z = 1.0f; + this->actor.world.pos.x = 119.0f; + this->actor.world.pos.z = -1820.0f; + this->prevSwitchFlagSet = Flags_GetSwitch(globalCtx, this->actor.params & 0x3F); + if (this->prevSwitchFlagSet != 0) { + this->actor.world.pos.y = -282.0f; + BgMoriIdomizu_SetWaterLevel(globalCtx, -282); + } else { + this->actor.world.pos.y = 184.0f; + BgMoriIdomizu_SetWaterLevel(globalCtx, 184); + } + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + Actor_Kill(&this->actor); + // "Bank danger!" + osSyncPrintf("Error : バンク危険!(arg_data 0x%04x)(%s %d)\n", this->actor.params, "../z_bg_mori_idomizu.c", + 202); + return; + } + BgMoriIdomizu_SetupWaitForMoriTex(this); + sIsSpawned = true; + this->isLoaded = true; + this->actor.room = -1; + // "Forest Temple well water" + osSyncPrintf("(森の神殿 井戸水)(arg_data 0x%04x)\n", this->actor.params); +} + +void BgMoriIdomizu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriIdomizu* this = (BgMoriIdomizu*)thisx; + + if (this->isLoaded) { + sIsSpawned = false; + } +} + +void BgMoriIdomizu_SetupWaitForMoriTex(BgMoriIdomizu* this) { + BgMoriIdomizu_SetupAction(this, BgMoriIdomizu_WaitForMoriTex); +} + +void BgMoriIdomizu_WaitForMoriTex(BgMoriIdomizu* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + BgMoriIdomizu_SetupMain(this); + this->actor.draw = BgMoriIdomizu_Draw; + } +} + +void BgMoriIdomizu_SetupMain(BgMoriIdomizu* this) { + BgMoriIdomizu_SetupAction(this, BgMoriIdomizu_Main); +} + +void BgMoriIdomizu_Main(BgMoriIdomizu* this, GlobalContext* globalCtx) { + s8 roomNum; + Actor* thisx = &this->actor; + s32 switchFlagSet; + + roomNum = globalCtx->roomCtx.curRoom.num; + switchFlagSet = Flags_GetSwitch(globalCtx, thisx->params & 0x3F); + if (switchFlagSet) { + this->targetWaterLevel = -282.0f; + } else { + this->targetWaterLevel = 184.0f; + } + if (switchFlagSet && !this->prevSwitchFlagSet) { + OnePointCutscene_Init(globalCtx, 3240, 70, thisx, MAIN_CAM); + this->drainTimer = 90; + } else if (!switchFlagSet && this->prevSwitchFlagSet) { + OnePointCutscene_Init(globalCtx, 3240, 70, thisx, MAIN_CAM); + this->drainTimer = 90; + thisx->world.pos.y = 0.0f; + } + this->drainTimer--; + if ((roomNum == 7) || (roomNum == 8) || (roomNum == 9)) { + if (this->drainTimer < 70) { + Math_StepToF(&thisx->world.pos.y, this->targetWaterLevel, 3.5f); + BgMoriIdomizu_SetWaterLevel(globalCtx, thisx->world.pos.y); + if (this->drainTimer > 0) { + if (switchFlagSet) { + func_800788CC(NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } else { + func_800788CC(NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } + } + } + } else { + thisx->world.pos.y = this->targetWaterLevel; + BgMoriIdomizu_SetWaterLevel(globalCtx, thisx->world.pos.y); + Actor_Kill(thisx); + return; + } + this->prevSwitchFlagSet = switchFlagSet; +} + +void BgMoriIdomizu_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriIdomizu* this = (BgMoriIdomizu*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgMoriIdomizu_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriIdomizu* this = (BgMoriIdomizu*)thisx; + u32 gameplayFrames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_idomizu.c", 356); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_idomizu.c", 360), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPSegment(POLY_XLU_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 128); + + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0x7F - (gameplayFrames & 0x7F), gameplayFrames % 0x80, 0x20, + 0x20, 1, gameplayFrames & 0x7F, gameplayFrames % 0x80, 0x20, 0x20)); + + gSPDisplayList(POLY_XLU_DISP++, gMoriIdomizuWaterDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_idomizu.c", 382); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Idomizu/z_bg_mori_idomizu.h b/soh/src/overlays/actors/ovl_Bg_Mori_Idomizu/z_bg_mori_idomizu.h new file mode 100644 index 000000000..be9032337 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Idomizu/z_bg_mori_idomizu.h @@ -0,0 +1,21 @@ +#ifndef Z_BG_MORI_IDOMIZU_H +#define Z_BG_MORI_IDOMIZU_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriIdomizu; + +typedef void (*BgMoriIdomizuActionFunc)(struct BgMoriIdomizu*, GlobalContext*); + +typedef struct BgMoriIdomizu { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgMoriIdomizuActionFunc actionFunc; + /* 0x0150 */ f32 targetWaterLevel; + /* 0x0154 */ s32 prevSwitchFlagSet; + /* 0x0158 */ s16 isLoaded; + /* 0x015A */ s16 drainTimer; + /* 0x015C */ s8 moriTexObjIndex; +} BgMoriIdomizu; // size = 0x0160 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c new file mode 100644 index 000000000..4ad89d7b3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.c @@ -0,0 +1,175 @@ +/* + * File: z_bg_mori_kaitenkabe.c + * Overlay: ovl_Bg_Mori_Kaitenkabe + * Description: Rotating wall in Forest Temple basement + */ + +#include "z_bg_mori_kaitenkabe.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS 0 + +void BgMoriKaitenkabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriKaitenkabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriKaitenkabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriKaitenkabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriKaitenkabe_WaitForMoriTex(BgMoriKaitenkabe* this, GlobalContext* globalCtx); +void BgMoriKaitenkabe_SetupWait(BgMoriKaitenkabe* this); +void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, GlobalContext* globalCtx); +void BgMoriKaitenkabe_SetupRotate(BgMoriKaitenkabe* this); +void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, GlobalContext* globalCtx); + +const ActorInit Bg_Mori_Kaitenkabe_InitVars = { + ACTOR_BG_MORI_KAITENKABE, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriKaitenkabe), + (ActorFunc)BgMoriKaitenkabe_Init, + (ActorFunc)BgMoriKaitenkabe_Destroy, + (ActorFunc)BgMoriKaitenkabe_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgMoriKaitenkabe_CrossProduct(Vec3f* dest, Vec3f* v1, Vec3f* v2) { + dest->x = (v1->y * v2->z) - (v1->z * v2->y); + dest->y = (v1->z * v2->x) - (v1->x * v2->z); + dest->z = (v1->x * v2->y) - (v1->y * v2->x); +} + +void BgMoriKaitenkabe_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriKaitenkabe* this = (BgMoriKaitenkabe*)thisx; + CollisionHeader* colHeader = NULL; + + // "Forest Temple object 【Rotating Wall (arg_data: 0x% 04x)】 appears" + osSyncPrintf("◯◯◯森の神殿オブジェクト【回転壁(arg_data : 0x%04x)】出現 \n", this->dyna.actor.params); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gMoriKaitenkabeCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + Actor_Kill(&this->dyna.actor); + // "【Rotating wall】 Bank danger!" + osSyncPrintf("【回転壁】 バンク危険!(%s %d)\n", "../z_bg_mori_kaitenkabe.c", 176); + } else { + this->actionFunc = BgMoriKaitenkabe_WaitForMoriTex; + } +} + +void BgMoriKaitenkabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriKaitenkabe* this = (BgMoriKaitenkabe*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgMoriKaitenkabe_WaitForMoriTex(BgMoriKaitenkabe* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + BgMoriKaitenkabe_SetupWait(this); + this->dyna.actor.draw = BgMoriKaitenkabe_Draw; + } +} + +void BgMoriKaitenkabe_SetupWait(BgMoriKaitenkabe* this) { + this->actionFunc = BgMoriKaitenkabe_Wait; + this->timer = 0; +} + +void BgMoriKaitenkabe_Wait(BgMoriKaitenkabe* this, GlobalContext* globalCtx) { + Vec3f push; + Vec3f leverArm; + Vec3f torque; + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 > 0.001f) { + this->timer++; + if ((this->timer > 28) && !Player_InCsMode(globalCtx)) { + BgMoriKaitenkabe_SetupRotate(this); + func_8002DF54(globalCtx, &this->dyna.actor, 8); + Math_Vec3f_Copy(&this->lockedPlayerPos, &player->actor.world.pos); + push.x = Math_SinS(this->dyna.unk_158); + push.y = 0.0f; + push.z = Math_CosS(this->dyna.unk_158); + leverArm.x = this->dyna.actor.world.pos.x - player->actor.world.pos.x; + leverArm.y = 0.0f; + leverArm.z = this->dyna.actor.world.pos.z - player->actor.world.pos.z; + BgMoriKaitenkabe_CrossProduct(&torque, &push, &leverArm); + this->rotDirection = (torque.y > 0.0f) ? 1.0f : -1.0f; + } + } else { + this->timer = 0; + } + if (fabsf(this->dyna.unk_150) > 0.001f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } +} + +void BgMoriKaitenkabe_SetupRotate(BgMoriKaitenkabe* this) { + this->actionFunc = BgMoriKaitenkabe_Rotate; + this->rotSpeed = 0.0f; + this->rotYdeg = 0.0f; +} + +void BgMoriKaitenkabe_Rotate(BgMoriKaitenkabe* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* thisx = &this->dyna.actor; + s16 rotY; + + Math_StepToF(&this->rotSpeed, 0.6f, 0.02f); + if (Math_StepToF(&this->rotYdeg, this->rotDirection * 45.0f, this->rotSpeed)) { + BgMoriKaitenkabe_SetupWait(this); + func_8002DF54(globalCtx, thisx, 7); + if (this->rotDirection > 0.0f) { + thisx->home.rot.y += 0x2000; + } else { + thisx->home.rot.y -= 0x2000; + } + thisx->world.rot.y = thisx->shape.rot.y = thisx->home.rot.y; + func_800788CC(NA_SE_EV_STONEDOOR_STOP); + } else { + rotY = this->rotYdeg * (0x10000 / 360.0f); + thisx->world.rot.y = thisx->shape.rot.y = thisx->home.rot.y + rotY; + func_800788CC(NA_SE_EV_WALL_SLIDE - SFX_FLAG); + } + if (fabsf(this->dyna.unk_150) > 0.001f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } + Math_Vec3f_Copy(&player->actor.world.pos, &this->lockedPlayerPos); +} + +void BgMoriKaitenkabe_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriKaitenkabe* this = (BgMoriKaitenkabe*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgMoriKaitenkabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriKaitenkabe* this = (BgMoriKaitenkabe*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_kaitenkabe.c", 347); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_kaitenkabe.c", 352), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gMoriKaitenkabeDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_kaitenkabe.c", 356); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.h b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.h new file mode 100644 index 000000000..aa52d21a4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Kaitenkabe/z_bg_mori_kaitenkabe.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_MORI_KAITENKABE_H +#define Z_BG_MORI_KAITENKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriKaitenkabe; + +typedef void (*BgMoriKaitenkabeActionFunc)(struct BgMoriKaitenkabe*, GlobalContext*); + +typedef struct BgMoriKaitenkabe { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMoriKaitenkabeActionFunc actionFunc; + /* 0x0168 */ s32 timer; + /* 0x016C */ f32 rotDirection; + /* 0x0170 */ f32 rotSpeed; + /* 0x0174 */ f32 rotYdeg; + /* 0x0178 */ Vec3f lockedPlayerPos; + /* 0x0184 */ s8 moriTexObjIndex; +} BgMoriKaitenkabe; // size = 0x0188 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Rakkatenjo/z_bg_mori_rakkatenjo.c b/soh/src/overlays/actors/ovl_Bg_Mori_Rakkatenjo/z_bg_mori_rakkatenjo.c new file mode 100644 index 000000000..af5569dce --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Rakkatenjo/z_bg_mori_rakkatenjo.c @@ -0,0 +1,236 @@ +/* + * File: z_bg_mori_rakkatenjo.c + * Overlay: ovl_Bg_Mori_Rakkatenjo + * Description: Falling ceiling in Forest Temple + */ + +#include "z_bg_mori_rakkatenjo.h" +#include "objects/object_mori_objects/object_mori_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgMoriRakkatenjo_Init(Actor* thisx, GlobalContext* globalCtx); +void BgMoriRakkatenjo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgMoriRakkatenjo_Update(Actor* thisx, GlobalContext* globalCtx); +void BgMoriRakkatenjo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgMoriRakkatenjo_SetupWaitForMoriTex(BgMoriRakkatenjo* this); +void BgMoriRakkatenjo_WaitForMoriTex(BgMoriRakkatenjo* this, GlobalContext* globalCtx); +void BgMoriRakkatenjo_SetupWait(BgMoriRakkatenjo* this); +void BgMoriRakkatenjo_Wait(BgMoriRakkatenjo* this, GlobalContext* globalCtx); +void BgMoriRakkatenjo_SetupFall(BgMoriRakkatenjo* this); +void BgMoriRakkatenjo_Fall(BgMoriRakkatenjo* this, GlobalContext* globalCtx); +void BgMoriRakkatenjo_SetupRest(BgMoriRakkatenjo* this); +void BgMoriRakkatenjo_Rest(BgMoriRakkatenjo* this, GlobalContext* globalCtx); +void BgMoriRakkatenjo_SetupRise(BgMoriRakkatenjo* this); +void BgMoriRakkatenjo_Rise(BgMoriRakkatenjo* this, GlobalContext* globalCtx); + +static s16 sCamSetting = 0; + +const ActorInit Bg_Mori_Rakkatenjo_InitVars = { + ACTOR_BG_MORI_RAKKATENJO, + ACTORCAT_BG, + FLAGS, + OBJECT_MORI_OBJECTS, + sizeof(BgMoriRakkatenjo), + (ActorFunc)BgMoriRakkatenjo_Init, + (ActorFunc)BgMoriRakkatenjo_Destroy, + (ActorFunc)BgMoriRakkatenjo_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(gravity, -1, ICHAIN_CONTINUE), + ICHAIN_F32(minVelocityY, -11, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgMoriRakkatenjo_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriRakkatenjo* this = (BgMoriRakkatenjo*)thisx; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + // "Forest Temple obj. Falling Ceiling" + osSyncPrintf("森の神殿 obj. 落下天井 (home posY %f)\n", this->dyna.actor.home.pos.y); + if ((fabsf(1991.0f - this->dyna.actor.home.pos.x) > 0.001f) || + (fabsf(683.0f - this->dyna.actor.home.pos.y) > 0.001f) || + (fabsf(-2520.0f - this->dyna.actor.home.pos.z) > 0.001f)) { + // "The set position has been changed. Let's fix the program." + osSyncPrintf("Warning : セット位置が変更されています。プログラムを修正しましょう。\n"); + } + if (this->dyna.actor.home.rot.y != 0x8000) { + // "The set Angle has changed. Let's fix the program." + osSyncPrintf("Warning : セット Angle が変更されています。プログラムを修正しましょう。\n"); + } + this->moriTexObjIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_MORI_TEX); + if (this->moriTexObjIndex < 0) { + // "Forest Temple obj Falling Ceiling Bank Danger!" + osSyncPrintf("Error : 森の神殿 obj 落下天井 バンク危険!(%s %d)\n", "../z_bg_mori_rakkatenjo.c", 205); + Actor_Kill(&this->dyna.actor); + return; + } + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + CollisionHeader_GetVirtual(&gMoriRakkatenjoCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + BgMoriRakkatenjo_SetupWaitForMoriTex(this); + sCamSetting = 0; +} + +void BgMoriRakkatenjo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriRakkatenjo* this = (BgMoriRakkatenjo*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +s32 BgMoriRakkatenjo_IsLinkUnder(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + Vec3f* pos = &GET_PLAYER(globalCtx)->actor.world.pos; + + return (-3300.0f < pos->z) && (pos->z < -1840.0f) && (1791.0f < pos->x) && (pos->x < 2191.0f); +} + +s32 BgMoriRakkatenjo_IsLinkClose(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + Vec3f* pos = &GET_PLAYER(globalCtx)->actor.world.pos; + + return (-3360.0f < pos->z) && (pos->z < -1840.0f) && (1791.0f < pos->x) && (pos->x < 2191.0f); +} + +void BgMoriRakkatenjo_SetupWaitForMoriTex(BgMoriRakkatenjo* this) { + this->actionFunc = BgMoriRakkatenjo_WaitForMoriTex; +} + +void BgMoriRakkatenjo_WaitForMoriTex(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->moriTexObjIndex)) { + BgMoriRakkatenjo_SetupWait(this); + this->dyna.actor.draw = BgMoriRakkatenjo_Draw; + } +} + +void BgMoriRakkatenjo_SetupWait(BgMoriRakkatenjo* this) { + this->timer = (this->fallCount > 0) ? 100 : 21; + this->dyna.actor.world.pos.y = 683.0f; + this->actionFunc = BgMoriRakkatenjo_Wait; +} + +void BgMoriRakkatenjo_Wait(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + if (this->fallCount == 0) { + if (BgMoriRakkatenjo_IsLinkClose(this, globalCtx) || (this->timer < 20)) { + if (this->timer <= 0) { + BgMoriRakkatenjo_SetupFall(this); + } + } else { + this->timer = 21; + } + } else { + if (BgMoriRakkatenjo_IsLinkUnder(this, globalCtx) || (this->timer < 20)) { + if (this->timer <= 0) { + BgMoriRakkatenjo_SetupFall(this); + } + } else { + this->timer = 100; + } + } + if (this->timer < 20) { + func_800788CC(NA_SE_EV_BLOCKSINK - SFX_FLAG); + } +} + +void BgMoriRakkatenjo_SetupFall(BgMoriRakkatenjo* this) { + this->actionFunc = BgMoriRakkatenjo_Fall; + this->bounceCount = 0; + this->dyna.actor.velocity.y = 0.0f; +} + +void BgMoriRakkatenjo_Fall(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + static f32 bounceVel[] = { 4.0f, 1.5f, 0.4f, 0.1f }; + s32 pad; + Actor* thisx = &this->dyna.actor; + s32 quake; + + Actor_MoveForward(thisx); + if ((thisx->velocity.y < 0.0f) && (thisx->world.pos.y <= 403.0f)) { + if (this->bounceCount >= ARRAY_COUNT(bounceVel)) { + BgMoriRakkatenjo_SetupRest(this); + } else { + if (this->bounceCount == 0) { + this->fallCount++; + func_800788CC(NA_SE_EV_STONE_BOUND); + func_800AA000(SQ(thisx->yDistToPlayer), 0xFF, 0x14, 0x96); + } + thisx->world.pos.y = + 403.0f - (thisx->world.pos.y - 403.0f) * bounceVel[this->bounceCount] / fabsf(thisx->velocity.y); + thisx->velocity.y = bounceVel[this->bounceCount]; + this->bounceCount++; + quake = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quake, 50000); + Quake_SetQuakeValues(quake, 5, 0, 0, 0); + Quake_SetCountdown(quake, 5); + } + } +} + +void BgMoriRakkatenjo_SetupRest(BgMoriRakkatenjo* this) { + this->actionFunc = BgMoriRakkatenjo_Rest; + this->dyna.actor.world.pos.y = 403.0f; + this->timer = 20; +} + +void BgMoriRakkatenjo_Rest(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + if (this->timer <= 0) { + BgMoriRakkatenjo_SetupRise(this); + } +} + +void BgMoriRakkatenjo_SetupRise(BgMoriRakkatenjo* this) { + this->actionFunc = BgMoriRakkatenjo_Rise; + this->dyna.actor.velocity.y = -0.1f; +} + +void BgMoriRakkatenjo_Rise(BgMoriRakkatenjo* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->dyna.actor.velocity.y, 5.0f, 0.06f, 0.1f, 0.0f); + this->dyna.actor.world.pos.y += this->dyna.actor.velocity.y; + if (this->dyna.actor.world.pos.y >= 683.0f) { + BgMoriRakkatenjo_SetupWait(this); + } +} + +void BgMoriRakkatenjo_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriRakkatenjo* this = (BgMoriRakkatenjo*)thisx; + + if (this->timer > 0) { + this->timer--; + } + this->actionFunc(this, globalCtx); + if (BgMoriRakkatenjo_IsLinkUnder(this, globalCtx)) { + if (sCamSetting == CAM_SET_NONE) { + osSyncPrintf("camera changed (mori rakka tenjyo) ... \n"); + sCamSetting = globalCtx->cameraPtrs[MAIN_CAM]->setting; + Camera_SetCameraData(globalCtx->cameraPtrs[MAIN_CAM], 1, &this->dyna.actor, NULL, 0, 0, 0); + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_FOREST_BIRDS_EYE); + } + } else if (sCamSetting != CAM_SET_NONE) { + osSyncPrintf("camera changed (previous) ... \n"); + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON1); + sCamSetting = 0; + } +} + +void BgMoriRakkatenjo_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgMoriRakkatenjo* this = (BgMoriRakkatenjo*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_rakkatenjo.c", 497); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, globalCtx->objectCtx.status[this->moriTexObjIndex].segment); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_mori_rakkatenjo.c", 502), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gMoriRakkatenjoDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_mori_rakkatenjo.c", 506); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Mori_Rakkatenjo/z_bg_mori_rakkatenjo.h b/soh/src/overlays/actors/ovl_Bg_Mori_Rakkatenjo/z_bg_mori_rakkatenjo.h new file mode 100644 index 000000000..305e48a0a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Mori_Rakkatenjo/z_bg_mori_rakkatenjo.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_MORI_RAKKATENJO_H +#define Z_BG_MORI_RAKKATENJO_H + +#include "ultra64.h" +#include "global.h" + +struct BgMoriRakkatenjo; + +typedef void (*BgMoriRakkatenjoActionFunction)(struct BgMoriRakkatenjo*, GlobalContext*); + +typedef struct BgMoriRakkatenjo { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgMoriRakkatenjoActionFunction actionFunc; + /* 0x0168 */ s32 timer; + /* 0x016C */ s32 bounceCount; + /* 0x0170 */ s32 fallCount; + /* 0x0174 */ s8 moriTexObjIndex; +} BgMoriRakkatenjo; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Po_Event/z_bg_po_event.c b/soh/src/overlays/actors/ovl_Bg_Po_Event/z_bg_po_event.c new file mode 100644 index 000000000..1a1cc6c60 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Po_Event/z_bg_po_event.c @@ -0,0 +1,642 @@ +/* + * File: z_bg_po_event.c + * Overlay: ovl_Bg_Po_Event + * Description: Poe sisters' paintings and puzzle blocks + */ + +#include "z_bg_po_event.h" +#include "objects/object_po_sisters/object_po_sisters.h" + +#define FLAGS 0 + +void BgPoEvent_Init(Actor* thisx, GlobalContext* globalCtx); +void BgPoEvent_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgPoEvent_Update(Actor* thisx, GlobalContext* globalCtx); +void BgPoEvent_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgPoEvent_Reset(void); + +void BgPoEvent_BlockWait(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_BlockShake(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_BlockFall(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_BlockIdle(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_BlockPush(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_BlockReset(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_BlockSolved(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_AmyWait(BgPoEvent* this, GlobalContext* globalCtx); // Amy is the green Poe +void BgPoEvent_AmyPuzzle(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_PaintingEmpty(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_PaintingAppear(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_PaintingPresent(BgPoEvent* this, GlobalContext* globalCtx); +void BgPoEvent_PaintingBurn(BgPoEvent* this, GlobalContext* globalCtx); + +const ActorInit Bg_Po_Event_InitVars = { + ACTOR_BG_PO_EVENT, + ACTORCAT_BG, + FLAGS, + OBJECT_PO_SISTERS, + sizeof(BgPoEvent), + (ActorFunc)BgPoEvent_Init, + (ActorFunc)BgPoEvent_Destroy, + (ActorFunc)BgPoEvent_Update, + (ActorFunc)BgPoEvent_Draw, + (ActorResetFunc)BgPoEvent_Reset, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK4, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F820, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 25.0f, 33.0f, 0.0f }, { -25.0f, 33.0f, 0.0f }, { -25.0f, -33.0f, 0.0f } } }, + }, + { + { + ELEMTYPE_UNK4, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F820, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 25.0f, 33.0f, 0.0f }, { -25.0f, -33.0f, 0.0f }, { 25.0f, -33.0f, 0.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +static u8 sBlocksAtRest = 0; + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +static u8 sPuzzleState; + +void BgPoEvent_InitPaintings(BgPoEvent* this, GlobalContext* globalCtx) { + static s16 paintingPosX[] = { -1302, -866, 1421, 985 }; + static s16 paintingPosY[] = { 1107, 1091 }; + static s16 paintingPosZ[] = { -3384, -3252 }; + ColliderTrisElementInit* item; + Vec3f* vtxVec; + s32 i1; + s32 i2; + Vec3f sp9C[3]; + f32 coss; + f32 sins; + f32 scaleY; + s32 phi_t2; + Actor* newPainting; + + sins = Math_SinS(this->dyna.actor.shape.rot.y); + coss = Math_CosS(this->dyna.actor.shape.rot.y); + if (this->type == 4) { + sins *= 2.4f; + scaleY = 1.818f; + coss *= 2.4f; + } else { + scaleY = 1.0f; + } + for (i1 = 0; i1 < sTrisInit.count; i1++) { + item = &sTrisInit.elements[i1]; + if (1) {} // This section looks like a macro of some sort. + for (i2 = 0; i2 < 3; i2++) { + vtxVec = &item->dim.vtx[i2]; + sp9C[i2].x = (vtxVec->x * coss) + (this->dyna.actor.home.pos.x + (sins * vtxVec->z)); + sp9C[i2].y = (vtxVec->y * scaleY) + this->dyna.actor.home.pos.y; + sp9C[i2].z = this->dyna.actor.home.pos.z + (coss * vtxVec->z) - (vtxVec->x * sins); + } + Collider_SetTrisVertices(&this->collider, i1, &sp9C[0], &sp9C[1], &sp9C[2]); + } + if ((this->type != 4) && (this->index != 2)) { + phi_t2 = (this->type == 2) ? this->index : this->index + 2; + newPainting = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_PO_EVENT, + paintingPosX[phi_t2], paintingPosY[this->index], paintingPosZ[this->index], 0, + this->dyna.actor.shape.rot.y + 0x8000, 0, + ((this->index + 1) << 0xC) + (this->type << 8) + this->dyna.actor.params); + if (newPainting == NULL) { + Actor_Kill(&this->dyna.actor); + return; + } + if (this->index == 0) { + if (this->dyna.actor.child->child == NULL) { + Actor_Kill(&this->dyna.actor); + return; + } + this->dyna.actor.parent = this->dyna.actor.child->child; + this->dyna.actor.child->child->child = &this->dyna.actor; + } + } + this->timer = 0; + if (this->type == 4) { + sPuzzleState = 0; + this->actionFunc = BgPoEvent_AmyWait; + } else { + sPuzzleState = (s32)(Rand_ZeroOne() * 3.0f) % 3; + this->actionFunc = BgPoEvent_PaintingEmpty; + } +} + +void BgPoEvent_InitBlocks(BgPoEvent* this, GlobalContext* globalCtx) { + static s16 blockPosX[] = { 2149, 1969, 1909 }; + static s16 blockPosZ[] = { -1410, -1350, -1530 }; + Actor* newBlock; + CollisionHeader* colHeader = NULL; + s32 bgId; + + this->dyna.actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + CollisionHeader_GetVirtual(&gPoSistersAmyBlockCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if ((this->type == 0) && (this->index != 3)) { + newBlock = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_PO_EVENT, + blockPosX[this->index], this->dyna.actor.world.pos.y, blockPosZ[this->index], 0, + this->dyna.actor.shape.rot.y, this->dyna.actor.shape.rot.z - 0x4000, + ((this->index + 1) << 0xC) + (this->type << 8) + this->dyna.actor.params); + if (newBlock == NULL) { + Actor_Kill(&this->dyna.actor); + return; + } + if (this->index == 0) { + if (this->dyna.actor.child->child == NULL) { + Actor_Kill(&this->dyna.actor); + return; + } + if (this->dyna.actor.child->child->child == NULL) { + Actor_Kill(&this->dyna.actor); + Actor_Kill(this->dyna.actor.child); + return; + } + this->dyna.actor.parent = this->dyna.actor.child->child->child; + this->dyna.actor.child->child->child->child = &this->dyna.actor; + } + } + this->dyna.actor.world.pos.y = 833.0f; + this->dyna.actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->dyna.actor.floorPoly, &bgId, + &this->dyna.actor, &this->dyna.actor.world.pos); + this->actionFunc = BgPoEvent_BlockWait; +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgPoEvent_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgPoEvent* this = (BgPoEvent*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + this->type = (thisx->params >> 8) & 0xF; + this->index = (thisx->params >> 0xC) & 0xF; + thisx->params &= 0x3F; + + if (this->type >= 2) { + Collider_InitTris(globalCtx, &this->collider); + Collider_SetTris(globalCtx, &this->collider, thisx, &sTrisInit, this->colliderItems); + if (Flags_GetSwitch(globalCtx, thisx->params)) { + Actor_Kill(thisx); + } else { + BgPoEvent_InitPaintings(this, globalCtx); + } + } else { + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (Flags_GetSwitch(globalCtx, thisx->params)) { + Actor_Kill(thisx); + } else { + BgPoEvent_InitBlocks(this, globalCtx); + } + } +} + +void BgPoEvent_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgPoEvent* this = (BgPoEvent*)thisx; + + if (this->type >= 2) { + Collider_DestroyTris(globalCtx, &this->collider); + } else { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if ((this->type == 1) && (gSaveContext.timer1Value > 0)) { + gSaveContext.timer1State = 0xA; + } + } +} + +void BgPoEvent_BlockWait(BgPoEvent* this, GlobalContext* globalCtx) { + this->dyna.actor.world.pos.y = 833.0f; + if (sPuzzleState == 0x3F) { + if (this->type == 1) { + OnePointCutscene_Init(globalCtx, 3150, 65, NULL, MAIN_CAM); + } + this->timer = 45; + this->actionFunc = BgPoEvent_BlockShake; + } else if (this->dyna.actor.xzDistToPlayer > 50.0f) { + if (this->type != 1) { + sPuzzleState |= (1 << this->index); + } else { + sPuzzleState |= 0x10; + } + } else if (this->type != 1) { + sPuzzleState &= ~(1 << this->index); + } else { + sPuzzleState &= ~0x10; + } +} + +void BgPoEvent_BlockShake(BgPoEvent* this, GlobalContext* globalCtx) { + DECR(this->timer); + if (this->timer < 15) { + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x + 2.0f * ((this->timer % 3) - 1); + if (!(this->timer % 4)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_SHAKE); + } + } + if (this->timer == 0) { + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x; + sPuzzleState = 0; + this->timer = 60; + this->actionFunc = BgPoEvent_BlockFall; + } +} + +void BgPoEvent_CheckBlock(BgPoEvent* this) { + s32 phi_v1; + s32 phi_a1; + s32 phi_t0; + s32 phi_a3; + + if ((this->index == 3) || (this->index == 1)) { + phi_v1 = this->dyna.actor.world.pos.z; + phi_a1 = this->dyna.actor.child->world.pos.z; + if (this->index == 3) { + phi_a3 = this->dyna.actor.world.pos.x; + phi_t0 = this->dyna.actor.child->world.pos.x; + } else { // this->index == 1 + phi_a3 = this->dyna.actor.child->world.pos.x; + phi_t0 = this->dyna.actor.world.pos.x; + } + } else { + phi_v1 = this->dyna.actor.world.pos.x; + phi_a1 = this->dyna.actor.child->world.pos.x; + if (this->index == 0) { + phi_a3 = this->dyna.actor.world.pos.z; + phi_t0 = this->dyna.actor.child->world.pos.z; + } else { // this->index == 2 + phi_a3 = this->dyna.actor.child->world.pos.z; + phi_t0 = this->dyna.actor.world.pos.z; + } + } + if ((phi_v1 == phi_a1) && ((phi_t0 - phi_a3) == 60)) { + sPuzzleState |= (1 << this->index); + } else { + sPuzzleState &= ~(1 << this->index); + } +} + +void BgPoEvent_BlockFall(BgPoEvent* this, GlobalContext* globalCtx) { + static s32 firstFall = 0; + + this->dyna.actor.velocity.y++; + if (Math_StepToF(&this->dyna.actor.world.pos.y, 433.0f, this->dyna.actor.velocity.y)) { + this->dyna.actor.flags &= ~ACTOR_FLAG_5; + this->dyna.actor.velocity.y = 0.0f; + sBlocksAtRest++; + if (this->type != 1) { + BgPoEvent_CheckBlock(this); + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + func_80033E88(&this->dyna.actor, globalCtx, 5, 5); + func_80088B34(this->timer); + if (firstFall == 0) { + firstFall = 1; + } else { + func_8002DF54(globalCtx, &GET_PLAYER(globalCtx)->actor, 7); + } + } + this->direction = 0; + this->actionFunc = BgPoEvent_BlockIdle; + } +} + +void BgPoEvent_BlockIdle(BgPoEvent* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* amy; + + if (sPuzzleState == 0xF) { + this->actionFunc = BgPoEvent_BlockSolved; + if ((this->type == 0) && (this->index == 0)) { + amy = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, this->dyna.actor.world.pos.x + 30.0f, + this->dyna.actor.world.pos.y - 30.0f, this->dyna.actor.world.pos.z + 30.0f, 0, + this->dyna.actor.shape.rot.y, 0, this->dyna.actor.params + 0x300); + if (amy != NULL) { + OnePointCutscene_Init(globalCtx, 3170, 30, amy, MAIN_CAM); + } + func_80078884(NA_SE_SY_CORRECT_CHIME); + gSaveContext.timer1State = 0xA; + } + } else { + if ((gSaveContext.timer1Value == 0) && (sBlocksAtRest == 5)) { + player->stateFlags2 &= ~0x10; + sPuzzleState = 0x10; + sBlocksAtRest = 0; + } + if ((sPuzzleState == 0x40) || ((sPuzzleState == 0x10) && !Player_InCsMode(globalCtx))) { + this->dyna.actor.world.rot.z = this->dyna.actor.shape.rot.z; + this->actionFunc = BgPoEvent_BlockReset; + if (sPuzzleState == 0x10) { + sPuzzleState = 0x40; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_RISING); + func_8002DF54(globalCtx, &player->actor, 8); + } + } else if (this->dyna.unk_150 != 0.0f) { + if (this->direction == 0) { + if (func_800435D8(globalCtx, &this->dyna, 0x1E, 0x32, -0x14) != 0) { + sBlocksAtRest--; + this->direction = (this->dyna.unk_150 >= 0.0f) ? 1.0f : -1.0f; + this->actionFunc = BgPoEvent_BlockPush; + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + DECR(this->direction); + } + } else { + this->direction = 0; + } + } +} + +static f32 blockPushDist = 0.0f; +void BgPoEvent_BlockPush(BgPoEvent* this, GlobalContext* globalCtx) { + f32 displacement; + s32 blockStop; + Player* player = GET_PLAYER(globalCtx); + + this->dyna.actor.speedXZ += 0.1f; + this->dyna.actor.speedXZ = CLAMP_MAX(this->dyna.actor.speedXZ, 2.0f); + blockStop = Math_StepToF(&blockPushDist, 20.0f, this->dyna.actor.speedXZ); + displacement = this->direction * blockPushDist; + this->dyna.actor.world.pos.x = (Math_SinS(this->dyna.unk_158) * displacement) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->dyna.unk_158) * displacement) + this->dyna.actor.home.pos.z; + if (blockStop) { + player->stateFlags2 &= ~0x10; + if ((this->dyna.unk_150 > 0.0f) && (func_800435D8(globalCtx, &this->dyna, 0x1E, 0x32, -0x14) == 0)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + } + this->dyna.unk_150 = 0.0f; + this->dyna.actor.home.pos.x = this->dyna.actor.world.pos.x; + this->dyna.actor.home.pos.z = this->dyna.actor.world.pos.z; + blockPushDist = 0.0f; + this->dyna.actor.speedXZ = 0.0f; + this->direction = 5; + sBlocksAtRest++; + this->actionFunc = BgPoEvent_BlockIdle; + if (this->type == 1) { + return; + } + BgPoEvent_CheckBlock(this); + BgPoEvent_CheckBlock((BgPoEvent*)this->dyna.actor.parent); + } + func_8002F974(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); +} + +void BgPoEvent_BlockReset(BgPoEvent* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + if (Math_StepToF(&this->dyna.actor.world.pos.y, 493.0f, 1.0f) && + Math_ScaledStepToS(&this->dyna.actor.shape.rot.z, this->dyna.actor.world.rot.z - 0x4000, 0x400)) { + + this->index = (this->index + 1) % 4; + this->actionFunc = BgPoEvent_BlockFall; + sPuzzleState = 0; + if (this->type == 1) { + this->timer += 10; + this->timer = CLAMP_MAX(this->timer, 120); + } + } +} + +void BgPoEvent_BlockSolved(BgPoEvent* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.unk_150 != 0.0f) { + player->stateFlags2 &= ~0x10; + } + if (Math_StepToF(&this->dyna.actor.world.pos.y, 369.0f, 2.0f)) { + sPuzzleState = 0x20; + Actor_Kill(&this->dyna.actor); + } +} + +void BgPoEvent_AmyWait(BgPoEvent* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + sPuzzleState |= 0x20; + this->timer = 5; + Actor_SetColorFilter(&this->dyna.actor, 0x4000, 0xFF, 0, 5); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_PO_LAUGH2); + this->actionFunc = BgPoEvent_AmyPuzzle; + } +} + +void BgPoEvent_AmyPuzzle(BgPoEvent* this, GlobalContext* globalCtx) { + Vec3f pos; + + if (sPuzzleState == 0xF) { + pos.x = this->dyna.actor.world.pos.x - 5.0f; + pos.y = Rand_CenteredFloat(120.0f) + this->dyna.actor.world.pos.y; + pos.z = Rand_CenteredFloat(120.0f) + this->dyna.actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &pos, &sZeroVec, &sZeroVec, 170, 0, 200, 255, 100, 170, 0, 255, 0, 1, 9, true); + } else if (sPuzzleState == 0x20) { + Actor_Kill(&this->dyna.actor); + } else { + DECR(this->timer); + } +} + +s32 BgPoEvent_NextPainting(BgPoEvent* this) { + if ((this->dyna.actor.parent != NULL) && (this->dyna.actor.child != NULL)) { + if (Rand_ZeroOne() < 0.5f) { + sPuzzleState = ((BgPoEvent*)this->dyna.actor.parent)->index; + } else { + sPuzzleState = ((BgPoEvent*)this->dyna.actor.child)->index; + } + } else if (this->dyna.actor.parent != NULL) { + sPuzzleState = ((BgPoEvent*)this->dyna.actor.parent)->index; + } else if (this->dyna.actor.child != NULL) { + sPuzzleState = ((BgPoEvent*)this->dyna.actor.child)->index; + } else { + return false; + } + return true; +} + +void BgPoEvent_PaintingEmpty(BgPoEvent* this, GlobalContext* globalCtx) { + if (sPuzzleState == this->index) { + this->timer = 255; + this->actionFunc = BgPoEvent_PaintingAppear; + } +} + +void BgPoEvent_PaintingAppear(BgPoEvent* this, GlobalContext* globalCtx) { + this->timer -= 20; + if (this->timer <= 0) { + this->timer = 1000; + this->actionFunc = BgPoEvent_PaintingPresent; + } +} + +void BgPoEvent_PaintingVanish(BgPoEvent* this, GlobalContext* globalCtx) { + this->timer += 20; + if (this->timer >= 255) { + BgPoEvent_NextPainting(this); + this->actionFunc = BgPoEvent_PaintingEmpty; + } +} + +void BgPoEvent_PaintingPresent(BgPoEvent* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + Player* player = GET_PLAYER(globalCtx); + + DECR(this->timer); + + if (((this->timer == 0) || ((thisx->xzDistToPlayer < 150.0f) && (thisx->yDistToPlayer < 50.0f)) || + (func_8002DD78(player) && (thisx->xzDistToPlayer < 320.0f) && + ((this->index != 2) ? (thisx->yDistToPlayer < 100.0f) : (thisx->yDistToPlayer < 0.0f)) && + Player_IsFacingActor(thisx, 0x2000, globalCtx))) && + ((thisx->parent != NULL) || (thisx->child != NULL))) { + /*The third condition in the || is checking if + 1) Link is holding a ranged weapon + 2) Link is too close in the xz plane + 3) Link is too close in the y direction. The painting + under the balcony allows him to be closer. + 4) Link is within 45 degrees of facing the painting. */ + this->timer = 0; + Audio_PlayActorSound2(thisx, NA_SE_EN_PO_LAUGH); + this->actionFunc = BgPoEvent_PaintingVanish; + } else if (this->collider.base.acFlags & AC_HIT) { + if (!BgPoEvent_NextPainting(this)) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, thisx->world.pos.x, + thisx->world.pos.y - 40.0f, thisx->world.pos.z, 0, thisx->shape.rot.y, 0, + thisx->params + ((this->type - 1) << 8)); + OnePointCutscene_Init(globalCtx, 3160, 80, thisx, MAIN_CAM); + func_80078884(NA_SE_SY_CORRECT_CHIME); + + } else { + Audio_PlayActorSound2(thisx, NA_SE_EN_PO_LAUGH2); + OnePointCutscene_Init(globalCtx, 3160, 35, thisx, MAIN_CAM); + } + if (thisx->parent != NULL) { + thisx->parent->child = NULL; + thisx->parent = NULL; + } + if (thisx->child != NULL) { + thisx->child->parent = NULL; + thisx->child = NULL; + } + this->timer = 20; + this->actionFunc = BgPoEvent_PaintingBurn; + } +} + +void BgPoEvent_PaintingBurn(BgPoEvent* this, GlobalContext* globalCtx) { + Vec3f sp54; + + this->timer--; + sp54.x = (Math_SinS(this->dyna.actor.shape.rot.y) * 5.0f) + this->dyna.actor.world.pos.x; + sp54.y = Rand_CenteredFloat(66.0f) + this->dyna.actor.world.pos.y; + sp54.z = Rand_CenteredFloat(50.0f) + this->dyna.actor.world.pos.z; + if (this->timer >= 0) { + if (this->type == 2) { + EffectSsDeadDb_Spawn(globalCtx, &sp54, &sZeroVec, &sZeroVec, 100, 0, 255, 255, 150, 170, 255, 0, 0, 1, 9, + true); + } else { + EffectSsDeadDb_Spawn(globalCtx, &sp54, &sZeroVec, &sZeroVec, 100, 0, 200, 255, 255, 170, 50, 100, 255, 1, 9, + true); + } + } + if (this->timer == 0) { + this->dyna.actor.draw = NULL; + } + if (this->timer < -60) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgPoEvent_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgPoEvent* this = (BgPoEvent*)thisx; + + this->actionFunc(this, globalCtx); + if ((this->actionFunc == BgPoEvent_AmyWait) || (this->actionFunc == BgPoEvent_PaintingPresent)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgPoEvent_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* displayLists[] = { + gPoSistersAmyBlockDL, gPoSistersAmyBethBlockDL, gPoSistersJoellePaintingDL, + gPoSistersBethPaintingDL, gPoSistersAmyPaintingDL, + }; + s32 pad; + BgPoEvent* this = (BgPoEvent*)thisx; + u8 alpha; + Vec3f sp58; + Vec3f sp4C; + f32 sp48; + s32 pad2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_po_event.c", 1481); + func_80093D18(globalCtx->state.gfxCtx); + if ((this->type == 3) || (this->type == 2)) { + if (this->actionFunc == BgPoEvent_PaintingEmpty) { + alpha = 255; + } else if (this->actionFunc == BgPoEvent_PaintingPresent) { + alpha = 0; + } else { + alpha = this->timer; + } + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, alpha); + } + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_po_event.c", 1501), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, displayLists[this->type]); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_po_event.c", 1508); + + if ((this->type == 0) || (this->type == 1)) { + sp48 = (833.0f - this->dyna.actor.world.pos.y) * 0.0025f; + if (!(sp48 > 1.0f)) { + sp58.x = this->dyna.actor.world.pos.x; + sp58.y = this->dyna.actor.world.pos.y - 30.0f; + sp58.z = this->dyna.actor.world.pos.z; + sp4C.y = 1.0f; + sp4C.x = sp4C.z = (sp48 * 0.3f) + 0.4f; + func_80033C30(&sp58, &sp4C, (u8)(155.0f + sp48 * 100.0f), globalCtx); + } + } +} + +void BgPoEvent_Reset(void) { + sBlocksAtRest = 0; + sPuzzleState = 0; + blockPushDist = 0.0f; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Po_Event/z_bg_po_event.h b/soh/src/overlays/actors/ovl_Bg_Po_Event/z_bg_po_event.h new file mode 100644 index 000000000..3023774f9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Po_Event/z_bg_po_event.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_PO_EVENT_H +#define Z_BG_PO_EVENT_H + +#include "ultra64.h" +#include "global.h" + +struct BgPoEvent; + +typedef void (*BgPoEventActionFunc)(struct BgPoEvent*, GlobalContext*); + +typedef struct BgPoEvent { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgPoEventActionFunc actionFunc; + /* 0x0168 */ u8 type; + /* 0x0169 */ u8 index; + /* 0x016A */ s8 direction; + /* 0x016C */ s16 timer; + /* 0x0170 */ ColliderTris collider; + /* 0x0190 */ ColliderTrisElement colliderItems[2]; +} BgPoEvent; // size = 0x0248 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Po_Syokudai/z_bg_po_syokudai.c b/soh/src/overlays/actors/ovl_Bg_Po_Syokudai/z_bg_po_syokudai.c new file mode 100644 index 000000000..0a47aa706 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Po_Syokudai/z_bg_po_syokudai.c @@ -0,0 +1,191 @@ +/* + * File: z_bg_po_syokudai.c + * Overlay: ovl_Bg_Po_Syokudai + * Description: Golden Torch Stand (Poe Sisters) + */ + +#include "z_bg_po_syokudai.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_syokudai/object_syokudai.h" + +#define FLAGS 0 + +typedef enum { + POE_FLAME_PURPLE, // Meg + POE_FLAME_RED, // Joelle + POE_FLAME_BLUE, // Beth + POE_FLAME_GREEN // Amy +} PoeFlameColor; + +#define POE_TORCH_FLAG 0x1C + +void BgPoSyokudai_Init(Actor* thisx, GlobalContext* globalCtx); +void BgPoSyokudai_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgPoSyokudai_Update(Actor* thisx, GlobalContext* globalCtx); +void BgPoSyokudai_Draw(Actor* thisx, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 12, 60, 0, { 0, 0, 0 } }, +}; + +static Color_RGBA8 sPrimColors[] = { + { 255, 170, 255, 255 }, + { 255, 200, 0, 255 }, + { 0, 170, 255, 255 }, + { 170, 255, 0, 255 }, +}; + +static Color_RGBA8 sEnvColors[] = { + { 100, 0, 255, 255 }, + { 255, 0, 0, 255 }, + { 0, 0, 255, 255 }, + { 0, 150, 0, 255 }, +}; + +const ActorInit Bg_Po_Syokudai_InitVars = { + ACTOR_BG_PO_SYOKUDAI, + ACTORCAT_PROP, + FLAGS, + OBJECT_SYOKUDAI, + sizeof(BgPoSyokudai), + (ActorFunc)BgPoSyokudai_Init, + (ActorFunc)BgPoSyokudai_Destroy, + (ActorFunc)BgPoSyokudai_Update, + (ActorFunc)BgPoSyokudai_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgPoSyokudai_Init(Actor* thisx, GlobalContext* globalCtx) { + BgPoSyokudai* this = (BgPoSyokudai*)thisx; + s32 pad; + + Actor_ProcessInitChain(thisx, sInitChain); + + this->flameColor = (thisx->params >> 8) & 0xFF; + thisx->params &= 0x3F; + + thisx->colChkInfo.mass = MASS_IMMOVABLE; + + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointGlowSetInfo(&this->lightInfo, thisx->world.pos.x, (s16)thisx->world.pos.y + 65, thisx->world.pos.z, 0, + 0, 0, 0); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + + this->collider.dim.pos.x = thisx->world.pos.x; + this->collider.dim.pos.y = thisx->world.pos.y; + this->collider.dim.pos.z = thisx->world.pos.z; + + if (this->flameColor == POE_FLAME_PURPLE && Flags_GetSwitch(globalCtx, POE_TORCH_FLAG + POE_FLAME_GREEN) && + Flags_GetSwitch(globalCtx, POE_TORCH_FLAG + POE_FLAME_BLUE) && + Flags_GetSwitch(globalCtx, POE_TORCH_FLAG + POE_FLAME_RED) && !Flags_GetSwitch(globalCtx, thisx->params)) { + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, 119.0f, 225.0f, -1566.0f, 0, 0, 0, + thisx->params); + globalCtx->envCtx.unk_BF = 0x4; + + } else if (!Flags_GetSwitch(globalCtx, POE_TORCH_FLAG + POE_FLAME_PURPLE) && !Flags_GetSwitch(globalCtx, 0x1B)) { + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, thisx->world.pos.x, + thisx->world.pos.y + 52.0f, thisx->world.pos.z, 0, 0, 0, + (this->flameColor << 8) + thisx->params + 0x1000); + + } else if (!Flags_GetSwitch(globalCtx, thisx->params)) { + if (globalCtx->envCtx.unk_BF == 0xFF) { + globalCtx->envCtx.unk_BF = 4; + } + } + + this->flameTextureScroll = (s16)(Rand_ZeroOne() * 20.0f); +} + +void BgPoSyokudai_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgPoSyokudai* this = (BgPoSyokudai*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + Collider_DestroyCylinder(globalCtx, &this->collider); + + if (globalCtx->envCtx.unk_BF != 0xFF) { + globalCtx->envCtx.unk_BF = 0xFF; + } +} + +void BgPoSyokudai_Update(Actor* thisx, GlobalContext* globalCtx) { + BgPoSyokudai* this = (BgPoSyokudai*)thisx; + s32 pad; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + func_8002F974(&this->actor, NA_SE_EV_TORCH - SFX_FLAG); + } + this->flameTextureScroll++; +} + +void BgPoSyokudai_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgPoSyokudai* this = (BgPoSyokudai*)thisx; + f32 lightBrightness; + u8 red; + u8 green; + u8 blue; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_po_syokudai.c", 315); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_po_syokudai.c", 319), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGoldenTorchDL); + + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + Color_RGBA8* primColor = &sPrimColors[this->flameColor]; + Color_RGBA8* envColor = &sEnvColors[this->flameColor]; + + lightBrightness = (0.3f * Rand_ZeroOne()) + 0.7f; + + red = (u8)(primColor->r * lightBrightness); + green = (u8)(primColor->g * lightBrightness); + blue = (u8)(primColor->b * lightBrightness); + + Lights_PointSetColorAndRadius(&this->lightInfo, red, green, blue, 200); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + (this->flameTextureScroll * -20) & 0x1FF, 32, 128)); + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, primColor->r, primColor->g, primColor->b, 255); + gDPSetEnvColor(POLY_XLU_DISP++, envColor->r, envColor->g, envColor->b, 255); + + Matrix_Translate(0.0f, 52.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y + 0x8000) * + (M_PI / 0x8000), + MTXMODE_APPLY); + Matrix_Scale(0.0027f, 0.0027f, 0.0027f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_po_syokudai.c", 368), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_po_syokudai.c", 373); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Po_Syokudai/z_bg_po_syokudai.h b/soh/src/overlays/actors/ovl_Bg_Po_Syokudai/z_bg_po_syokudai.h new file mode 100644 index 000000000..8bb84f0fe --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Po_Syokudai/z_bg_po_syokudai.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_PO_SYOKUDAI_H +#define Z_BG_PO_SYOKUDAI_H + +#include "ultra64.h" +#include "global.h" + +struct BgPoSyokudai; + +typedef struct BgPoSyokudai { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 flameColor; + /* 0x014E */ s16 flameTextureScroll; + /* 0x0150 */ LightNode* lightNode; + /* 0x0154 */ LightInfo lightInfo; + /* 0x0164 */ ColliderCylinder collider; +} BgPoSyokudai; // size = 0x01B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Pushbox/z_bg_pushbox.c b/soh/src/overlays/actors/ovl_Bg_Pushbox/z_bg_pushbox.c new file mode 100644 index 000000000..31e750d19 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Pushbox/z_bg_pushbox.c @@ -0,0 +1,89 @@ +/* + * File: z_bg_pushbox.c + * Overlay: ovl_Bg_Pushbox + * Description: Unused (and non functional) pushable block + */ + +#include "z_bg_pushbox.h" +#include "objects/object_pu_box/object_pu_box.h" + +#define FLAGS 0 + +void BgPushbox_Init(Actor* thisx, GlobalContext* globalCtx); +void BgPushbox_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgPushbox_Update(Actor* thisx, GlobalContext* globalCtx); +void BgPushbox_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgPushbox_UpdateImpl(BgPushbox* this, GlobalContext* globalCtx); + +const ActorInit Bg_Pushbox_InitVars = { + ACTOR_BG_PUSHBOX, + ACTORCAT_BG, + FLAGS, + //! @bug fixing this actor would involve using OBJECT_PU_BOX + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(BgPushbox), + (ActorFunc)BgPushbox_Init, + (ActorFunc)BgPushbox_Destroy, + (ActorFunc)BgPushbox_Update, + (ActorFunc)BgPushbox_Draw, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32_DIV1000(gravity, -2000, ICHAIN_STOP), +}; + +void BgPushbox_SetupAction(BgPushbox* this, BgPushboxActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgPushbox_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgPushbox* this = (BgPushbox*)thisx; + CollisionHeader* colHeader = NULL; + s32 pad2; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gBlockSmallCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); + BgPushbox_SetupAction(this, BgPushbox_UpdateImpl); +} + +void BgPushbox_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgPushbox* this = (BgPushbox*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgPushbox_UpdateImpl(BgPushbox* this, GlobalContext* globalCtx) { + this->dyna.actor.speedXZ += this->dyna.unk_150 * 0.2f; + this->dyna.actor.speedXZ = (this->dyna.actor.speedXZ < -1.0f) + ? -1.0f + : ((this->dyna.actor.speedXZ > 1.0f) ? 1.0f : this->dyna.actor.speedXZ); + Math_StepToF(&this->dyna.actor.speedXZ, 0.0f, 0.2f); + this->dyna.actor.world.rot.y = this->dyna.unk_158; + Actor_MoveForward(&this->dyna.actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 20.0f, 40.0f, 40.0f, 0x1D); +} + +void BgPushbox_Update(Actor* thisx, GlobalContext* globalCtx) { + BgPushbox* this = (BgPushbox*)thisx; + + this->actionFunc(this, globalCtx); + func_8002DF90(&this->dyna); +} + +void BgPushbox_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_pushbox.c", 263); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_pushbox.c", 269), + G_MTX_NOPUSH | G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gBlockSmallDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_pushbox.c", 272); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Pushbox/z_bg_pushbox.h b/soh/src/overlays/actors/ovl_Bg_Pushbox/z_bg_pushbox.h new file mode 100644 index 000000000..31b8aac83 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Pushbox/z_bg_pushbox.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_PUSHBOX_H +#define Z_BG_PUSHBOX_H + +#include "ultra64.h" +#include "global.h" + +struct BgPushbox; + +typedef void (*BgPushboxActionFunc)(struct BgPushbox*, GlobalContext*); + +typedef struct BgPushbox { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgPushboxActionFunc actionFunc; +} BgPushbox; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Relay_Objects/z_bg_relay_objects.c b/soh/src/overlays/actors/ovl_Bg_Relay_Objects/z_bg_relay_objects.c new file mode 100644 index 000000000..33e94d172 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Relay_Objects/z_bg_relay_objects.c @@ -0,0 +1,215 @@ +/* + * File: z_bg_relay_objects.c + * Overlay: ovl_Bg_Relay_Objects + * Description: Windmill Setpieces + */ + +#include "z_bg_relay_objects.h" +#include "objects/object_relay_objects/object_relay_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0 */ WINDMILL_ROTATING_GEAR, + /* 1 */ WINDMILL_DAMPE_STONE_DOOR +} WindmillSetpiecesMode; + +void BgRelayObjects_Init(Actor* thisx, GlobalContext* globalCtx); +void BgRelayObjects_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgRelayObjects_Update(Actor* thisx, GlobalContext* globalCtx); +void BgRelayObjects_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgRelayObjects_Reset(void); + +void func_808A90F4(BgRelayObjects* this, GlobalContext* globalCtx); +void func_808A91AC(BgRelayObjects* this, GlobalContext* globalCtx); +void func_808A9234(BgRelayObjects* this, GlobalContext* globalCtx); +void BgRelayObjects_DoNothing(BgRelayObjects* this, GlobalContext* globalCtx); +void func_808A932C(BgRelayObjects* this, GlobalContext* globalCtx); +void func_808A939C(BgRelayObjects* this, GlobalContext* globalCtx); + +const ActorInit Bg_Relay_Objects_InitVars = { + ACTOR_BG_RELAY_OBJECTS, + ACTORCAT_BG, + FLAGS, + OBJECT_RELAY_OBJECTS, + sizeof(BgRelayObjects), + (ActorFunc)BgRelayObjects_Init, + (ActorFunc)BgRelayObjects_Destroy, + (ActorFunc)BgRelayObjects_Update, + (ActorFunc)BgRelayObjects_Draw, + BgRelayObjects_Reset, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(gravity, 5, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static u32 D_808A9508 = 0; +void BgRelayObjects_Init(Actor* thisx, GlobalContext* globalCtx) { + BgRelayObjects* this = (BgRelayObjects*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + this->switchFlag = thisx->params & 0x3F; + thisx->params = (thisx->params >> 8) & 0xFF; + DynaPolyActor_Init(&this->dyna, 3); + if (thisx->params == WINDMILL_ROTATING_GEAR) { + CollisionHeader_GetVirtual(&gWindmillRotatingPlatformCol, &colHeader); + if (gSaveContext.eventChkInf[6] & 0x20) { + thisx->world.rot.y = 0x400; + } else { + thisx->world.rot.y = 0x80; + } + func_800F5718(); + thisx->room = -1; + thisx->flags |= ACTOR_FLAG_5; + if (D_808A9508 & 2) { + thisx->params = 0xFF; + Actor_Kill(thisx); + } else { + D_808A9508 |= 2; + this->actionFunc = func_808A939C; + } + } else { + CollisionHeader_GetVirtual(&gDampeRaceDoorCol, &colHeader); + if (thisx->room == 0) { + this->unk_169 = this->switchFlag - 0x33; + } else { + this->unk_169 = thisx->room + 1; + } + thisx->room = -1; + this->timer = 1; + if (this->unk_169 >= 6) { + if (D_808A9508 & 1) { + Actor_Kill(thisx); + } else { + D_808A9508 |= 1; + this->actionFunc = BgRelayObjects_DoNothing; + } + } else if (this->unk_169 != 5) { + Flags_UnsetSwitch(globalCtx, this->switchFlag); + if (D_808A9508 & (1 << this->unk_169)) { + Actor_Kill(thisx); + } else { + D_808A9508 |= (1 << this->unk_169); + this->actionFunc = func_808A90F4; + } + } else { + Flags_SetSwitch(globalCtx, this->switchFlag); + this->actionFunc = func_808A91AC; + thisx->world.pos.y += 120.0f; + D_808A9508 |= 1; + } + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); +} + +void BgRelayObjects_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgRelayObjects* this = (BgRelayObjects*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if ((this->dyna.actor.params == WINDMILL_ROTATING_GEAR) && (gSaveContext.cutsceneIndex < 0xFFF0)) { + gSaveContext.eventChkInf[6] &= ~0x20; + } +} + +void func_808A90F4(BgRelayObjects* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + if (this->timer != 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_SLIDE_DOOR_OPEN); + if (INV_CONTENT(ITEM_HOOKSHOT) != ITEM_NONE) { + this->timer = 120; + } else { + this->timer = 160; + } + } + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 120.0f, 12.0f)) { + this->actionFunc = func_808A91AC; + } + } +} + +void func_808A91AC(BgRelayObjects* this, GlobalContext* globalCtx) { + if (this->unk_169 != 5) { + if (this->timer != 0) { + this->timer--; + } + func_8002F994(&this->dyna.actor, this->timer); + } + if ((this->timer == 0) || (this->unk_169 == globalCtx->roomCtx.curRoom.num)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_SLIDE_DOOR_CLOSE); + this->actionFunc = func_808A9234; + } +} + +void func_808A9234(BgRelayObjects* this, GlobalContext* globalCtx) { + this->dyna.actor.velocity.y += this->dyna.actor.gravity; + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, this->dyna.actor.velocity.y)) { + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 180, 20, 100); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + if (this->unk_169 != globalCtx->roomCtx.curRoom.num) { + func_800788CC(NA_SE_EN_PO_LAUGH); + this->timer = 5; + this->actionFunc = func_808A932C; + return; + } + Flags_UnsetSwitch(globalCtx, this->switchFlag); + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + if (globalCtx->roomCtx.curRoom.num == 4) { + gSaveContext.timer1State = 0xF; + } + this->actionFunc = BgRelayObjects_DoNothing; + } +} + +void BgRelayObjects_DoNothing(BgRelayObjects* this, GlobalContext* globalCtx) { +} + +void func_808A932C(BgRelayObjects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + if (!Player_InCsMode(globalCtx)) { + func_80078884(NA_SE_OC_ABYSS); + Gameplay_TriggerRespawn(globalCtx); + this->actionFunc = BgRelayObjects_DoNothing; + } + } +} + +void func_808A939C(BgRelayObjects* this, GlobalContext* globalCtx) { + if (Flags_GetEnv(globalCtx, 5)) { + gSaveContext.eventChkInf[6] |= 0x20; + } + if (gSaveContext.eventChkInf[6] & 0x20) { + Math_ScaledStepToS(&this->dyna.actor.world.rot.y, 0x400, 8); + } else { + Math_ScaledStepToS(&this->dyna.actor.world.rot.y, 0x80, 8); + } + this->dyna.actor.shape.rot.y += this->dyna.actor.world.rot.y; + func_800F436C(&this->dyna.actor.projectedPos, NA_SE_EV_WOOD_GEAR - SFX_FLAG, + ((this->dyna.actor.world.rot.y - 0x80) * (1.0f / 0x380)) + 1.0f); +} + +void BgRelayObjects_Update(Actor* thisx, GlobalContext* globalCtx) { + BgRelayObjects* this = (BgRelayObjects*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgRelayObjects_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgRelayObjects* this = (BgRelayObjects*)thisx; + + if (this->dyna.actor.params == WINDMILL_ROTATING_GEAR) { + Gfx_DrawDListOpa(globalCtx, gWindmillRotatingPlatformDL); + } else { + Gfx_DrawDListOpa(globalCtx, gDampeRaceDoorDL); + } +} + +void BgRelayObjects_Reset(void) { + D_808A9508 = 0; +} diff --git a/soh/src/overlays/actors/ovl_Bg_Relay_Objects/z_bg_relay_objects.h b/soh/src/overlays/actors/ovl_Bg_Relay_Objects/z_bg_relay_objects.h new file mode 100644 index 000000000..fa39ad76c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Relay_Objects/z_bg_relay_objects.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_RELAY_OBJECTS_H +#define Z_BG_RELAY_OBJECTS_H + +#include "ultra64.h" +#include "global.h" + +struct BgRelayObjects; + +typedef void (*BgRelayObjectsActionFunc)(struct BgRelayObjects*, GlobalContext*); + +typedef struct BgRelayObjects { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgRelayObjectsActionFunc actionFunc; + /* 0x0168 */ u8 switchFlag; + /* 0x0169 */ s8 unk_169; // a room id + /* 0x016A */ s16 timer; +} BgRelayObjects; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot00_Break/z_bg_spot00_break.c b/soh/src/overlays/actors/ovl_Bg_Spot00_Break/z_bg_spot00_break.c new file mode 100644 index 000000000..f0ea847d4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot00_Break/z_bg_spot00_break.c @@ -0,0 +1,75 @@ +/* + * File: z_bg_spot00_break.c + * Overlay: ovl_Bg_Spot00_Break + * Description: Broken drawbridge in Hyrule Field. + */ + +#include "z_bg_spot00_break.h" +#include "objects/object_spot00_break/object_spot00_break.h" + +#define FLAGS 0 + +void BgSpot00Break_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot00Break_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot00Break_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot00Break_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot00_Break_InitVars = { + ACTOR_BG_SPOT00_BREAK, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT00_BREAK, + sizeof(BgSpot00Break), + (ActorFunc)BgSpot00Break_Init, + (ActorFunc)BgSpot00Break_Destroy, + (ActorFunc)BgSpot00Break_Update, + (ActorFunc)BgSpot00Break_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgSpot00Break_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot00Break* this = (BgSpot00Break*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + + if (this->dyna.actor.params == 1) { + CollisionHeader_GetVirtual(&gBarbedWireFenceCol, &colHeader); + } else { + CollisionHeader_GetVirtual(&gBrokenDrawbridgeCol, &colHeader); + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (!LINK_IS_ADULT) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgSpot00Break_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot00Break* this = (BgSpot00Break*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgSpot00Break_Update(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgSpot00Break_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot00Break* this = (BgSpot00Break*)thisx; + + if (this->dyna.actor.params == 1) { + Gfx_DrawDListOpa(globalCtx, gBarbedWireFenceDL); + } else { + Gfx_DrawDListOpa(globalCtx, gBrokenDrawbridgeDL); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot00_Break/z_bg_spot00_break.h b/soh/src/overlays/actors/ovl_Bg_Spot00_Break/z_bg_spot00_break.h new file mode 100644 index 000000000..de282476d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot00_Break/z_bg_spot00_break.h @@ -0,0 +1,14 @@ +#ifndef Z_BG_SPOT00_BREAK_H +#define Z_BG_SPOT00_BREAK_H + +#include "ultra64.h" +#include "global.h" + + +struct BgSpot00Break; + +typedef struct BgSpot00Break { + /* 0x0000 */ DynaPolyActor dyna; +} BgSpot00Break; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c b/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c new file mode 100644 index 000000000..da202bba2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.c @@ -0,0 +1,331 @@ +/* + * File: z_bg_spot00_hanebasi.c + * Overlay: ovl_Bg_Spot00_Hanebasi + * Description: Hyrule Field Drawbridge and Torches + */ + +#include "z_bg_spot00_hanebasi.h" +#include "objects/object_spot00_objects/object_spot00_objects.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* -1 */ DT_DRAWBRIDGE = -1, + /* 0 */ DT_CHAIN_1, + /* 1 */ DT_CHAIN_2 +} DrawbridgeType; + +void BgSpot00Hanebasi_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot00Hanebasi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot00Hanebasi_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot00Hanebasi_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgSpot00Hanebasi_DrawbridgeWait(BgSpot00Hanebasi* this, GlobalContext* globalCtx); +void BgSpot00Hanebasi_DrawbridgeRiseAndFall(BgSpot00Hanebasi* this, GlobalContext* globalCtx); +void BgSpot00Hanebasi_SetTorchLightInfo(BgSpot00Hanebasi* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot00_Hanebasi_InitVars = { + ACTOR_BG_SPOT00_HANEBASI, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT00_OBJECTS, + sizeof(BgSpot00Hanebasi), + (ActorFunc)BgSpot00Hanebasi_Init, + (ActorFunc)BgSpot00Hanebasi_Destroy, + (ActorFunc)BgSpot00Hanebasi_Update, + (ActorFunc)BgSpot00Hanebasi_Draw, + NULL, +}; + +static f32 sTorchFlameScale = 0.0f; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 550, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 5000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgSpot00Hanebasi_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot00Hanebasi* this = (BgSpot00Hanebasi*)thisx; + s32 pad; + Vec3f chainPos; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, 1); + + if (this->dyna.actor.params == DT_DRAWBRIDGE) { + CollisionHeader_GetVirtual(&gHyruleFieldCastleDrawbridgeCol, &colHeader); + } else { + CollisionHeader_GetVirtual(&gHyruleFieldCastleDrawbridgeChainsCol, &colHeader); + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.actor.params == DT_DRAWBRIDGE) { + if (LINK_IS_ADULT && (gSaveContext.sceneSetupIndex < 4)) { + Actor_Kill(&this->dyna.actor); + return; + } + + if ((gSaveContext.sceneSetupIndex != 6) && + ((gSaveContext.sceneSetupIndex == 4) || (gSaveContext.sceneSetupIndex == 5) || + (!LINK_IS_ADULT && !IS_DAY))) { + this->dyna.actor.shape.rot.x = -0x4000; + } else { + this->dyna.actor.shape.rot.x = 0; + } + + if (gSaveContext.sceneSetupIndex != 6) { + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY) && + CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) && !(gSaveContext.eventChkInf[8] & 1)) { + this->dyna.actor.shape.rot.x = -0x4000; + } + } + + chainPos.y = + (10.0f * Math_CosS(this->dyna.actor.shape.rot.x)) - (Math_SinS(this->dyna.actor.shape.rot.x) * 400.0f); + chainPos.z = + (10.0f * Math_SinS(this->dyna.actor.shape.rot.x)) - (Math_CosS(this->dyna.actor.shape.rot.x) * 400.0f); + chainPos.x = + (158.0f * Math_CosS(this->dyna.actor.shape.rot.y)) + (Math_SinS(this->dyna.actor.shape.rot.y) * chainPos.z); + chainPos.z = (-158.0f * Math_SinS(this->dyna.actor.shape.rot.y)) + + (Math_CosS(this->dyna.actor.shape.rot.y) * chainPos.z); + + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_SPOT00_HANEBASI, + this->dyna.actor.world.pos.x + chainPos.x, this->dyna.actor.world.pos.y + chainPos.y, + this->dyna.actor.world.pos.z + chainPos.z, + ((this->dyna.actor.shape.rot.x == 0) ? 0 : 0xF020), this->dyna.actor.shape.rot.y, 0, + DT_CHAIN_1) == NULL) { + Actor_Kill(&this->dyna.actor); + } + + this->actionFunc = BgSpot00Hanebasi_DrawbridgeWait; + this->destAngle = 40; + } else if (this->dyna.actor.params == DT_CHAIN_1) { + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_SPOT00_HANEBASI, + this->dyna.actor.world.pos.x - (Math_CosS(this->dyna.actor.shape.rot.y) * 316.0f), + this->dyna.actor.world.pos.y, + this->dyna.actor.world.pos.z + (Math_SinS(this->dyna.actor.shape.rot.y) * 316.0f), + this->dyna.actor.shape.rot.x, this->dyna.actor.shape.rot.y, 0, DT_CHAIN_2) == NULL) { + Actor_Kill(&this->dyna.actor); + Actor_Kill(this->dyna.actor.parent); + } + + this->actionFunc = BgSpot00Hanebasi_SetTorchLightInfo; + } else { + this->actionFunc = BgSpot00Hanebasi_SetTorchLightInfo; + } + + if (this->dyna.actor.params >= DT_CHAIN_1) { + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointGlowSetInfo(&this->lightInfo, ((this->dyna.actor.params == DT_CHAIN_1) ? 260.0f : -260.0f), 168, + 690, 255, 255, 0, 0); + } +} + +void BgSpot00Hanebasi_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot00Hanebasi* this = (BgSpot00Hanebasi*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + + if (this->dyna.actor.params >= DT_CHAIN_1) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + } +} + +void BgSpot00Hanebasi_DrawbridgeWait(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { + BgSpot00Hanebasi* child = (BgSpot00Hanebasi*)this->dyna.actor.child; + + if ((gSaveContext.sceneSetupIndex >= 4) || !CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) || + !CHECK_QUEST_ITEM(QUEST_GORON_RUBY) || !CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) || + (gSaveContext.eventChkInf[8] & 1)) { + if (this->dyna.actor.shape.rot.x != 0) { + if (Flags_GetEnv(globalCtx, 0) || ((gSaveContext.sceneSetupIndex < 4) && IS_DAY)) { + this->actionFunc = BgSpot00Hanebasi_DrawbridgeRiseAndFall; + this->destAngle = 0; + child->destAngle = 0; + return; + } + + if (this) {} // required to match + } + if ((this->dyna.actor.shape.rot.x == 0) && (gSaveContext.sceneSetupIndex < 4) && !LINK_IS_ADULT && !IS_DAY) { + this->actionFunc = BgSpot00Hanebasi_DrawbridgeRiseAndFall; + this->destAngle = -0x4000; + child->destAngle = -0xFE0; + } + } +} + +void BgSpot00Hanebasi_DoNothing(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { +} + +void BgSpot00Hanebasi_DrawbridgeRiseAndFall(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { + BgSpot00Hanebasi* child; + Actor* childsChild; + s16 angle = 80; + + if (Math_ScaledStepToS(&this->dyna.actor.shape.rot.x, this->destAngle, 80)) { + this->actionFunc = BgSpot00Hanebasi_DrawbridgeWait; + } + + if (this->dyna.actor.shape.rot.x >= -0x27D8) { + child = (BgSpot00Hanebasi*)this->dyna.actor.child; + angle *= 0.4f; + Math_ScaledStepToS(&child->dyna.actor.shape.rot.x, child->destAngle, angle); + childsChild = child->dyna.actor.child; + Math_ScaledStepToS(&childsChild->shape.rot.x, child->destAngle, angle); + } + + if (this->destAngle < 0) { + if (this->actionFunc == BgSpot00Hanebasi_DrawbridgeWait) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_CLOSE_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_BRIDGE_CLOSE - SFX_FLAG); + } + } else { + if (this->actionFunc == BgSpot00Hanebasi_DrawbridgeWait) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN - SFX_FLAG); + } + } +} + +void BgSpot00Hanebasi_SetTorchLightInfo(BgSpot00Hanebasi* this, GlobalContext* globalCtx) { + u8 lightColor = (u8)(Rand_ZeroOne() * 127.0f) + 128; // intensity of the red and green channels + + Lights_PointGlowSetInfo(&this->lightInfo, (this->dyna.actor.params == DT_CHAIN_1) ? 260.0f : -260.0f, + (5000.0f * sTorchFlameScale) + 128.0f, 690, lightColor, lightColor, 0, + sTorchFlameScale * 37500.0f); +} + +void BgSpot00Hanebasi_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot00Hanebasi* this = (BgSpot00Hanebasi*)thisx; + s32 pad; + + this->actionFunc(this, globalCtx); + + if (this->dyna.actor.params == DT_DRAWBRIDGE) { + if (globalCtx->sceneNum == SCENE_SPOT00) { + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY) && + CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE) && !(gSaveContext.eventChkInf[8] & 1) && LINK_IS_CHILD) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->actor.world.pos.x > -450.0f) && (player->actor.world.pos.x < 450.0f) && + (player->actor.world.pos.z > 1080.0f) && (player->actor.world.pos.z < 1700.0f) && + (!(Gameplay_InCsMode(globalCtx)))) { + gSaveContext.eventChkInf[8] |= 1; + Flags_SetEventChkInf(0x82); + this->actionFunc = BgSpot00Hanebasi_DoNothing; + func_8002DF54(globalCtx, &player->actor, 8); + globalCtx->nextEntranceIndex = 0x00CD; + gSaveContext.nextCutsceneIndex = 0xFFF1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 4; + } else if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 3000.0f, 0x7530)) { + globalCtx->envCtx.gloomySkyMode = 1; + } + } + } + + if (gSaveContext.sceneSetupIndex == 5) { + u16 dayTime; + s32 tmp; + + if (gTimeIncrement == 50) { + tmp = 0xD556; + + if (gSaveContext.dayTime >= 0xD557) { + tmp = 0x1D556; + } + + gTimeIncrement = (tmp - gSaveContext.dayTime) * (1.0f / 350.0f); + } + + dayTime = gSaveContext.dayTime; + + if ((dayTime >= 0x2AAC) && (dayTime < 0x3000) && (gSaveContext.sceneSetupIndex == 5)) { + gTimeIncrement = 0; + } + } + } +} + +void BgSpot00Hanebasi_DrawTorches(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + f32 angle; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot00_hanebasi.c", 633); + + func_80093D84(globalCtx->state.gfxCtx); + + if (gSaveContext.sceneSetupIndex >= 4) { + sTorchFlameScale = 0.008f; + } else { + sTorchFlameScale = ((thisx->shape.rot.x * -1) - 0x2000) * (1.0f / 1024000.0f); + } + + angle = (s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * (M_PI / 32768.0f); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + + for (i = 0; i < 2; i++) { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + ((globalCtx->gameplayFrames + i) * -20) & 0x1FF, 32, 128)); + + Matrix_Translate((i == 0) ? 260.0f : -260.0f, 128.0f, 690.0f, MTXMODE_NEW); + Matrix_RotateY(angle, MTXMODE_APPLY); + Matrix_Scale(sTorchFlameScale, sTorchFlameScale, sTorchFlameScale, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot00_hanebasi.c", 674), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot00_hanebasi.c", 681); +} + +void BgSpot00Hanebasi_Draw(Actor* thisx, GlobalContext* globalCtx) { + Vec3f basePos = { 158.0f, 10.0f, 400.0f }; + Vec3f newPos; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot00_hanebasi.c", 698); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot00_hanebasi.c", 702), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (thisx->params == DT_DRAWBRIDGE) { + gSPDisplayList(POLY_OPA_DISP++, gHyruleFieldCastleDrawbridgeDL); + + Matrix_MultVec3f(&basePos, &newPos); + thisx->child->world.pos.x = newPos.x; + thisx->child->world.pos.y = newPos.y; + thisx->child->world.pos.z = newPos.z; + basePos.x *= -1.0f; + + Matrix_MultVec3f(&basePos, &newPos); + thisx->child->child->world.pos.x = newPos.x; + thisx->child->child->world.pos.y = newPos.y; + thisx->child->child->world.pos.z = newPos.z; + + if (gSaveContext.sceneSetupIndex != 12) { + if ((gSaveContext.sceneSetupIndex >= 4) || (!LINK_IS_ADULT && (thisx->shape.rot.x < -0x2000))) { + BgSpot00Hanebasi_DrawTorches(thisx, globalCtx); + } else { + sTorchFlameScale = 0.0f; + } + } + } else { + gSPDisplayList(POLY_OPA_DISP++, gHyruleFieldCastleDrawbridgeChainsDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot00_hanebasi.c", 733); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.h b/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.h new file mode 100644 index 000000000..71d41f9ad --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot00_Hanebasi/z_bg_spot00_hanebasi.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_SPOT00_HANEBASI_H +#define Z_BG_SPOT00_HANEBASI_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot00Hanebasi; + +typedef void (*BgSpot00HanebasiActionFunc)(struct BgSpot00Hanebasi*, GlobalContext*); + +typedef struct BgSpot00Hanebasi { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot00HanebasiActionFunc actionFunc; + /* 0x0168 */ s16 destAngle; + /* 0x016C */ LightNode* lightNode; + /* 0x0170 */ LightInfo lightInfo; +} BgSpot00Hanebasi; // size = 0x0180 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Fusya/z_bg_spot01_fusya.c b/soh/src/overlays/actors/ovl_Bg_Spot01_Fusya/z_bg_spot01_fusya.c new file mode 100644 index 000000000..0f66dc386 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Fusya/z_bg_spot01_fusya.c @@ -0,0 +1,88 @@ +/* + * File: z_bg_spot01_fusya.c + * Overlay: Bg_Spot01_Fusya + * Description: Windmill Sails + */ + +#include "z_bg_spot01_fusya.h" +#include "objects/object_spot01_objects/object_spot01_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot01Fusya_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Fusya_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Fusya_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Fusya_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808AAA50(BgSpot01Fusya* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot01_Fusya_InitVars = { + ACTOR_BG_SPOT01_FUSYA, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT01_OBJECTS, + sizeof(BgSpot01Fusya), + (ActorFunc)BgSpot01Fusya_Init, + (ActorFunc)BgSpot01Fusya_Destroy, + (ActorFunc)BgSpot01Fusya_Update, + (ActorFunc)BgSpot01Fusya_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 12800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1300, ICHAIN_STOP), +}; + +void BgSpot01Fusya_SetupAction(BgSpot01Fusya* this, BgSpot01FusyaActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgSpot01Fusya_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Fusya* this = (BgSpot01Fusya*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->unk_154 = 100.0f; + this->unk_158 = 100.0f; + this->unk_15C = 0.5f; + if (gSaveContext.sceneSetupIndex < 4) { + gSaveContext.eventChkInf[6] &= 0xFFDF; + } + BgSpot01Fusya_SetupAction(this, func_808AAA50); +} + +void BgSpot01Fusya_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_808AAA50(BgSpot01Fusya* this, GlobalContext* globalCtx) { + f32 temp; + Actor* thisx = &this->actor; + + if (gSaveContext.eventChkInf[6] & 0x20) { + this->unk_158 = 1800.0f; + } + thisx->shape.rot.z += this->unk_154; + temp = ((this->unk_154 - 100.0f) / 1700.0f) + 1.0f; + func_800F436C(&thisx->projectedPos, 0x2085, temp); + Math_ApproachF(&this->unk_154, this->unk_158, this->unk_15C, 100.0f); +} + +void BgSpot01Fusya_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Fusya* this = (BgSpot01Fusya*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot01Fusya_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot01_fusya.c", 210); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot01_fusya.c", 214), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gKakarikoWindmillSailsDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot01_fusya.c", 219); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Fusya/z_bg_spot01_fusya.h b/soh/src/overlays/actors/ovl_Bg_Spot01_Fusya/z_bg_spot01_fusya.h new file mode 100644 index 000000000..ba0ece272 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Fusya/z_bg_spot01_fusya.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_SPOT01_FUSYA_H +#define Z_BG_SPOT01_FUSYA_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot01Fusya; + +typedef void (*BgSpot01FusyaActionFunc)(struct BgSpot01Fusya*, GlobalContext*); + +typedef struct BgSpot01Fusya { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgSpot01FusyaActionFunc actionFunc; + /* 0x0150 */ char unk_150[0x4]; + /* 0x0154 */ f32 unk_154; + /* 0x0158 */ f32 unk_158; + /* 0x015C */ f32 unk_15C; +} BgSpot01Fusya; // size = 0x0160 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Idohashira/z_bg_spot01_idohashira.c b/soh/src/overlays/actors/ovl_Bg_Spot01_Idohashira/z_bg_spot01_idohashira.c new file mode 100644 index 000000000..b1f9f5a8d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Idohashira/z_bg_spot01_idohashira.c @@ -0,0 +1,344 @@ +/* + * File: z_bg_spot01_idohashira.c + * Overlay: Bg_Spot01_Idohashira + * Description: Wooden beam above well in Kakariko Village + */ + +#include "z_bg_spot01_idohashira.h" +#include "objects/object_spot01_objects/object_spot01_objects.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot01Idohashira_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idohashira_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idohashira_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idohashira_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808AB504(BgSpot01Idohashira* this, GlobalContext* globalCtx); +void func_808AB510(BgSpot01Idohashira* this, GlobalContext* globalCtx); +void func_808AB530(BgSpot01Idohashira* this, GlobalContext* globalCtx); +void func_808AB570(BgSpot01Idohashira* this, GlobalContext* globalCtx); +void func_808AB700(BgSpot01Idohashira* this, GlobalContext* globalCtx); + +static BgSpot01IdohashiraActionFunc sActionFuncs[] = { + func_808AB504, + func_808AB510, + func_808AB530, + func_808AB570, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static BgSpot01IdohashiraDrawFunc sDrawFuncs[] = { + func_808AB700, +}; + +const ActorInit Bg_Spot01_Idohashira_InitVars = { + ACTOR_BG_SPOT01_IDOHASHIRA, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT01_OBJECTS, + sizeof(BgSpot01Idohashira), + (ActorFunc)BgSpot01Idohashira_Init, + (ActorFunc)BgSpot01Idohashira_Destroy, + (ActorFunc)BgSpot01Idohashira_Update, + (ActorFunc)BgSpot01Idohashira_Draw, + NULL, +}; + +void BgSpot01Idohashira_PlayBreakSfx1(BgSpot01Idohashira* this) { + func_80078914(&this->dyna.actor.projectedPos, NA_SE_EV_BOX_BREAK); +} + +void BgSpot01Idohashira_PlayBreakSfx2(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 60, NA_SE_EV_WOODBOX_BREAK); +} + +void func_808AAD3C(GlobalContext* globalCtx, Vec3f* vec, u32 arg2) { + EffectSparkInit effect; + s32 sp24; + + effect.position.x = vec->x; + effect.position.y = vec->y; + effect.position.z = vec->z; + effect.speed = 8.0f; + effect.gravity = -1.0f; + effect.uDiv = arg2; + effect.vDiv = arg2; + effect.colorStart[0].r = 0; + effect.colorStart[0].g = 0; + effect.colorStart[0].b = 0; + effect.colorStart[0].a = 255; + effect.colorStart[1].r = 0; + effect.colorStart[1].g = 0; + effect.colorStart[1].b = 0; + effect.colorStart[1].a = 255; + effect.colorStart[2].r = 0; + effect.colorStart[2].g = 0; + effect.colorStart[2].b = 0; + effect.colorStart[2].a = 255; + effect.colorStart[3].r = 0; + effect.colorStart[3].g = 0; + effect.colorStart[3].b = 0; + effect.colorStart[3].a = 255; + effect.colorEnd[0].r = 0; + effect.colorEnd[0].g = 0; + effect.colorEnd[0].b = 0; + effect.colorEnd[0].a = 0; + effect.colorEnd[1].r = 0; + effect.colorEnd[1].g = 0; + effect.colorEnd[1].b = 0; + effect.colorEnd[1].a = 0; + effect.colorEnd[2].r = 0; + effect.colorEnd[2].g = 0; + effect.colorEnd[2].b = 0; + effect.colorEnd[2].a = 0; + effect.colorEnd[3].r = 0; + effect.colorEnd[3].g = 0; + effect.colorEnd[3].b = 0; + effect.colorEnd[3].a = 0; + effect.timer = 0; + effect.duration = 32; + + Effect_Add(globalCtx, &sp24, EFFECT_SPARK, 0, 1, &effect); +} + +void func_808AAE6C(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f sp30 = this->dyna.actor.world.pos; + + sp30.y += kREG(15); + func_80033480(globalCtx, &sp30, kREG(11) + 350.0f, kREG(12) + 5, kREG(13) + 0x7D0, kREG(14) + 0x320, 0); + func_808AAD3C(globalCtx, &sp30, 5); + BgSpot01Idohashira_PlayBreakSfx2(this, globalCtx); +} + +void func_808AAF34(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + s32 pad[2]; + Vec3f dest; + Vec3f src; + + if (this->unk_170 != 0) { + src.x = kREG(20) + 1300.0f; + src.y = kREG(21) + 200.0f; + src.z = 0.0f; + Matrix_MultVec3f(&src, &dest); + func_80033480(globalCtx, &dest, kREG(16) + 80.0f, kREG(17) + 10, kREG(18) + 1000, kREG(19), 0); + func_808AAD3C(globalCtx, &dest, 3); + src.x = -(kREG(20) + 1300.0f); + src.y = kREG(21) + 200.0f; + src.z = 0.0f; + Matrix_MultVec3f(&src, &dest); + func_80033480(globalCtx, &dest, kREG(16) + 80.0f, kREG(17) + 10, kREG(18) + 1000, kREG(19), 0); + func_808AAD3C(globalCtx, &dest, 3); + this->unk_170 = 0; + BgSpot01Idohashira_PlayBreakSfx1(this); + } +} + +void BgSpot01Idohashira_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idohashira* this = (BgSpot01Idohashira*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +s32 BgSpot01Idohashira_NotInCsMode(GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + return true; + } + return false; +} + +CsCmdActorAction* BgSpot01Idohashira_GetNpcAction(GlobalContext* globalCtx, s32 actionIdx) { + s32 pad[2]; + CsCmdActorAction* npcAction = NULL; + + if (!BgSpot01Idohashira_NotInCsMode(globalCtx)) { + npcAction = globalCtx->csCtx.npcActions[actionIdx]; + } + return npcAction; +} + +void func_808AB18C(BgSpot01Idohashira* this) { + this->dyna.actor.shape.rot.x += kREG(6); + this->dyna.actor.shape.rot.y += (s16)(kREG(7) + 0x3E8); + this->dyna.actor.shape.rot.z += (s16)(kREG(8) + 0x7D0); +} + +f32 func_808AB1DC(f32 arg0, f32 arg1, u16 arg2, u16 arg3, u16 arg4) { + f32 temp_f12; + f32 regFloat; + f32 diff23; + f32 diff43; + + diff23 = arg2 - arg3; + if (diff23 != 0.0f) { + regFloat = kREG(9) + 30.0f; + diff43 = arg4 - arg3; + temp_f12 = regFloat * diff43; + return (((((arg1 - arg0) - temp_f12) / SQ(diff23)) * diff43) * diff43) + temp_f12; + } + osSyncPrintf(VT_FGCOL(RED) "Bg_Spot01_Idohashira_Get_FreeFallで割り算出来ない!!!!!!!!!!!!!!\n" VT_RST); + return 0.0f; +} + +s32 func_808AB29C(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + Vec3f* thisPos; + f32 endX; + f32 temp_f0; + s32 pad2; + Vec3f initPos; + f32 endZ; + f32 tempY; + f32 tempZ; + + npcAction = BgSpot01Idohashira_GetNpcAction(globalCtx, 2); + if (npcAction != NULL) { + temp_f0 = Environment_LerpWeight(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames); + initPos = this->dyna.actor.home.pos; + endX = npcAction->endPos.x; + tempY = ((kREG(10) + 1100.0f) / 10.0f) + npcAction->endPos.y; + endZ = npcAction->endPos.z; + thisPos = &this->dyna.actor.world.pos; + thisPos->x = ((endX - initPos.x) * temp_f0) + initPos.x; + thisPos->y = + func_808AB1DC(initPos.y, tempY, npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames) + + initPos.y; + thisPos->z = ((endZ - initPos.z) * temp_f0) + initPos.z; + + if (temp_f0 >= 1.0f) { + return true; + } else { + return false; + } + } + return false; +} + +void func_808AB3E8(BgSpot01Idohashira* this) { + this->action = 1; + this->drawConfig = 0; +} + +void func_808AB3F8(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + this->action = 2; + this->drawConfig = 0; + this->unk_170 = 1; +} + +void func_808AB414(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + func_808AAE6C(this, globalCtx); + this->action = 3; + this->drawConfig = 0; +} + +void func_808AB444(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = BgSpot01Idohashira_GetNpcAction(globalCtx, 2); + u32 action; + u32 currentNpcAction; + + if (npcAction != NULL) { + action = npcAction->action; + currentNpcAction = this->npcAction; + if (action != currentNpcAction) { + switch (action) { + case 1: + func_808AB3E8(this); + break; + case 2: + func_808AB3F8(this, globalCtx); + break; + case 3: + Actor_Kill(&this->dyna.actor); + break; + default: + osSyncPrintf("Bg_Spot01_Idohashira_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->npcAction = action; + } + } +} + +void func_808AB504(BgSpot01Idohashira* this, GlobalContext* globalCtx) { +} + +void func_808AB510(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + func_808AB444(this, globalCtx); +} + +void func_808AB530(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + func_808AB18C(this); + if (func_808AB29C(this, globalCtx)) { + func_808AB414(this, globalCtx); + } +} + +void func_808AB570(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + func_808AB444(this, globalCtx); +} + +void BgSpot01Idohashira_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idohashira* this = (BgSpot01Idohashira*)thisx; + + if (this->action < 0 || this->action >= 4 || sActionFuncs[this->action] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void BgSpot01Idohashira_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad[2]; + BgSpot01Idohashira* this = (BgSpot01Idohashira*)thisx; + CollisionHeader* colHeader; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + colHeader = NULL; + CollisionHeader_GetVirtual(&gKakarikoWellArchCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (gSaveContext.sceneSetupIndex < 4) { + if ((gSaveContext.eventChkInf[5] & 0x10) && LINK_IS_ADULT) { + Actor_Kill(&this->dyna.actor); + } else { + this->action = 0; + } + } else if (gSaveContext.sceneSetupIndex == 4) { + this->action = 1; + this->dyna.actor.shape.yOffset = -(kREG(10) + 1100.0f); + } else if (gSaveContext.sceneSetupIndex == 6) { + this->action = 0; + } else { + Actor_Kill(&this->dyna.actor); + } +} + +void func_808AB700(BgSpot01Idohashira* this, GlobalContext* globalCtx) { + GraphicsContext* localGfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(localGfxCtx, "../z_bg_spot01_idohashira.c", 689); + + func_80093D18(localGfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(localGfxCtx, "../z_bg_spot01_idohashira.c", 699), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_808AAF34(this, globalCtx); + gSPDisplayList(POLY_OPA_DISP++, gKakarikoWellArchDL); + + CLOSE_DISPS(localGfxCtx, "../z_bg_spot01_idohashira.c", 708); +} + +void BgSpot01Idohashira_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idohashira* this = (BgSpot01Idohashira*)thisx; + + if (this->drawConfig < 0 || this->drawConfig > 0 || sDrawFuncs[this->drawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Idohashira/z_bg_spot01_idohashira.h b/soh/src/overlays/actors/ovl_Bg_Spot01_Idohashira/z_bg_spot01_idohashira.h new file mode 100644 index 000000000..f3779352f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Idohashira/z_bg_spot01_idohashira.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_SPOT01_IDOHASHIRA_H +#define Z_BG_SPOT01_IDOHASHIRA_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot01Idohashira; + +typedef void (*BgSpot01IdohashiraActionFunc)(struct BgSpot01Idohashira*, GlobalContext*); +typedef void (*BgSpot01IdohashiraDrawFunc)(struct BgSpot01Idohashira*, GlobalContext*); + +typedef struct BgSpot01Idohashira { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s32 action; + /* 0x0168 */ s32 drawConfig; + /* 0x016C */ u32 npcAction; + /* 0x0170 */ s32 unk_170; +} BgSpot01Idohashira; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Idomizu/z_bg_spot01_idomizu.c b/soh/src/overlays/actors/ovl_Bg_Spot01_Idomizu/z_bg_spot01_idomizu.c new file mode 100644 index 000000000..5df428902 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Idomizu/z_bg_spot01_idomizu.c @@ -0,0 +1,88 @@ +/* + * File: z_bg_spot01_idomizu.c + * Overlay: ovl_Bg_Spot01_Idomizu + * Description: Kakariko Village Well Water + */ + +#include "z_bg_spot01_idomizu.h" +#include "objects/object_spot01_objects/object_spot01_objects.h" + +#define FLAGS ACTOR_FLAG_5 + +void BgSpot01Idomizu_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idomizu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idomizu_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idomizu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808ABB84(BgSpot01Idomizu* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot01_Idomizu_InitVars = { + ACTOR_BG_SPOT01_IDOMIZU, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT01_OBJECTS, + sizeof(BgSpot01Idomizu), + (ActorFunc)BgSpot01Idomizu_Init, + (ActorFunc)BgSpot01Idomizu_Destroy, + (ActorFunc)BgSpot01Idomizu_Update, + (ActorFunc)BgSpot01Idomizu_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot01Idomizu_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idomizu* this = (BgSpot01Idomizu*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + if (gSaveContext.eventChkInf[6] & 0x80 || LINK_AGE_IN_YEARS == YEARS_ADULT) { + this->waterHeight = -550.0f; + } else { + this->waterHeight = 52.0f; + } + this->actionFunc = func_808ABB84; + this->actor.world.pos.y = this->waterHeight; +} + +void BgSpot01Idomizu_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_808ABB84(BgSpot01Idomizu* this, GlobalContext* globalCtx) { + if (gSaveContext.eventChkInf[6] & 0x80) { + this->waterHeight = -550.0f; + } + globalCtx->colCtx.colHeader->waterBoxes[0].ySurface = this->actor.world.pos.y; + if (this->waterHeight < this->actor.world.pos.y) { + Audio_PlaySoundGeneral(NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + Math_ApproachF(&this->actor.world.pos.y, this->waterHeight, 1.0f, 2.0f); +} + +void BgSpot01Idomizu_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idomizu* this = (BgSpot01Idomizu*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot01Idomizu_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 frames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot01_idomizu.c", 228); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot01_idomizu.c", 232), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + frames = globalCtx->state.frames; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - frames % 128, frames & 0x7F, 32, 32, 1, frames % 128, + frames & 0x7F, 32, 32)); + + gSPDisplayList(POLY_XLU_DISP++, gKakarikoWellWaterDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot01_idomizu.c", 244); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Idomizu/z_bg_spot01_idomizu.h b/soh/src/overlays/actors/ovl_Bg_Spot01_Idomizu/z_bg_spot01_idomizu.h new file mode 100644 index 000000000..35087146e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Idomizu/z_bg_spot01_idomizu.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_SPOT01_IDOMIZU_H +#define Z_BG_SPOT01_IDOMIZU_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot01Idomizu; + +typedef void (*BgSpot01IdomizuActionFunc)(struct BgSpot01Idomizu*, GlobalContext*); + +typedef struct BgSpot01Idomizu { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgSpot01IdomizuActionFunc actionFunc; + /* 0x0150 */ f32 waterHeight; + /* 0x0154 */ char unk_154[0x4]; +} BgSpot01Idomizu; // size = 0x0158 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Idosoko/z_bg_spot01_idosoko.c b/soh/src/overlays/actors/ovl_Bg_Spot01_Idosoko/z_bg_spot01_idosoko.c new file mode 100644 index 000000000..f907d55e4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Idosoko/z_bg_spot01_idosoko.c @@ -0,0 +1,82 @@ +/* + * File: z_bg_spot01_idosoko.c + * Overlay: ovl_Bg_Spot01_Idosoko + * Description: Stone that blocks the entrance to Bottom of the Well + */ + +#include "z_bg_spot01_idosoko.h" +#include "objects/object_spot01_matoya/object_spot01_matoya.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot01Idosoko_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idosoko_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idosoko_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Idosoko_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808ABF54(BgSpot01Idosoko* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot01_Idosoko_InitVars = { + ACTOR_BG_SPOT01_IDOSOKO, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT01_MATOYA, + sizeof(BgSpot01Idosoko), + (ActorFunc)BgSpot01Idosoko_Init, + (ActorFunc)BgSpot01Idosoko_Destroy, + (ActorFunc)BgSpot01Idosoko_Update, + (ActorFunc)BgSpot01Idosoko_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot01Idosoko_SetupAction(BgSpot01Idosoko* this, BgSpot01IdosokoActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgSpot01Idosoko_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSpot01Idosoko* this = (BgSpot01Idosoko*)thisx; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + CollisionHeader_GetVirtual(&gKakarikoBOTWStoneCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (!LINK_IS_ADULT) { + Actor_Kill(&this->dyna.actor); + } else { + BgSpot01Idosoko_SetupAction(this, func_808ABF54); + } +} + +void BgSpot01Idosoko_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idosoko* this = (BgSpot01Idosoko*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808ABF54(BgSpot01Idosoko* this, GlobalContext* globalCtx) { +} + +void BgSpot01Idosoko_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Idosoko* this = (BgSpot01Idosoko*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot01Idosoko_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot01_idosoko.c", 162); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot01_idosoko.c", 166), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gKakarikoBOTWStoneDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot01_idosoko.c", 171); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Idosoko/z_bg_spot01_idosoko.h b/soh/src/overlays/actors/ovl_Bg_Spot01_Idosoko/z_bg_spot01_idosoko.h new file mode 100644 index 000000000..9356c2a97 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Idosoko/z_bg_spot01_idosoko.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_SPOT01_IDOSOKO_H +#define Z_BG_SPOT01_IDOSOKO_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot01Idosoko; + +typedef void (*BgSpot01IdosokoActionFunc)(struct BgSpot01Idosoko*, GlobalContext*); + +typedef struct BgSpot01Idosoko { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot01IdosokoActionFunc actionFunc; +} BgSpot01Idosoko; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Objects2/z_bg_spot01_objects2.c b/soh/src/overlays/actors/ovl_Bg_Spot01_Objects2/z_bg_spot01_objects2.c new file mode 100644 index 000000000..724be0d4d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Objects2/z_bg_spot01_objects2.c @@ -0,0 +1,140 @@ +/* + * File: z_bg_spot01_objects2.c + * Overlay: ovl_Bg_Spot01_Objects2 + * Description: Kakariko Village Set Pieces + */ + +#include "z_bg_spot01_objects2.h" +#include "objects/object_spot01_matoya/object_spot01_matoya.h" +#include "objects/object_spot01_matoyab/object_spot01_matoyab.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot01Objects2_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Objects2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot01Objects2_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_808AC2BC(BgSpot01Objects2* this, GlobalContext* globalCtx); +void func_808AC474(BgSpot01Objects2* this, GlobalContext* globalCtx); +void func_808AC4A4(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot01_Objects2_InitVars = { + ACTOR_BG_SPOT01_OBJECTS2, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(BgSpot01Objects2), + (ActorFunc)BgSpot01Objects2_Init, + (ActorFunc)BgSpot01Objects2_Destroy, + (ActorFunc)BgSpot01Objects2_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 12800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static Gfx* D_808AC510[] = { + gKakarikoPotionShopSignDL, gKakarikoShootingGallerySignDL, gKakarikoBazaarSignDL, + gKakarikoConstructionSiteDL, gKakarikoShootingGalleryDL, +}; + +void BgSpot01Objects2_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Objects2* this = (BgSpot01Objects2*)thisx; + + switch (this->dyna.actor.params & 7) { + case 0: + case 1: + case 2: + this->objectId = OBJECT_SPOT01_MATOYA; + break; + case 3: + this->objectId = OBJECT_SPOT01_MATOYAB; + break; + case 4: + this->objectId = OBJECT_SPOT01_MATOYA; + } + + if (this->objectId >= 0) { + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, this->objectId); + if (this->objBankIndex < 0) { + // "There was no bank setting." + osSyncPrintf("-----------------------------バンク設定ありませんでした."); + Actor_Kill(&this->dyna.actor); + return; + } + } else { + Actor_Kill(&this->dyna.actor); + } + this->actionFunc = func_808AC2BC; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); +} + +void BgSpot01Objects2_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +s32 func_808AC22C(Path* pathList, Vec3f* pos, s32 path, s32 waypoint) { + Vec3s* pointPos = &((Vec3s*)SEGMENTED_TO_VIRTUAL((pathList + path)->points))[waypoint]; + + pos->x = pointPos->x; + pos->y = pointPos->y; + pos->z = pointPos->z; + return 0; +} + +void func_808AC2BC(BgSpot01Objects2* this, GlobalContext* globalCtx) { + CollisionHeader* colHeader = NULL; + Actor* thisx = &this->dyna.actor; + s32 pad; + Vec3f position; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + // "---- Successful bank switching!!" + osSyncPrintf("-----バンク切り換え成功!!\n"); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndex].segment); + + this->dyna.actor.objBankIndex = this->objBankIndex; + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + + switch (this->dyna.actor.params & 7) { + case 4: // Shooting gallery + CollisionHeader_GetVirtual(&gKakarikoShootingGalleryCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + break; + case 3: // Shooting Gallery, spawns Carpenter Sabooro during the day + CollisionHeader_GetVirtual(&object_spot01_matoyab_col, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + if (IS_DAY) { + func_808AC22C(globalCtx->setupPathList, &position, ((s32)thisx->params >> 8) & 0xFF, 0); + Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_EN_DAIKU_KAKARIKO, position.x, + position.y, position.z, thisx->world.rot.x, thisx->world.rot.y, + thisx->world.rot.z, ((((s32)thisx->params >> 8) & 0xFF) << 8) + 1); + } + break; + case 0: // Potion Shop Poster + case 1: // Shooting gallery Poster + case 2: // Bazaar Poster + break; + } + + this->dyna.actor.draw = func_808AC4A4; + this->actionFunc = func_808AC474; + } +} + +void func_808AC474(BgSpot01Objects2* this, GlobalContext* globalCtx) { +} + +void BgSpot01Objects2_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot01Objects2* this = (BgSpot01Objects2*)thisx; + + this->actionFunc(this, globalCtx); +} + +void func_808AC4A4(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, D_808AC510[thisx->params & 7]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot01_Objects2/z_bg_spot01_objects2.h b/soh/src/overlays/actors/ovl_Bg_Spot01_Objects2/z_bg_spot01_objects2.h new file mode 100644 index 000000000..0ee7d814e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot01_Objects2/z_bg_spot01_objects2.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_SPOT01_OBJECTS2_H +#define Z_BG_SPOT01_OBJECTS2_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot01Objects2; + +typedef void (*BgSpot01Objects2ActionFunc)(struct BgSpot01Objects2*, GlobalContext*); + +typedef struct BgSpot01Objects2 { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot01Objects2ActionFunc actionFunc; + /* 0x0168 */ char unk_168[0x10]; + /* 0x0178 */ s32 objectId; + /* 0x017C */ s8 objBankIndex; +} BgSpot01Objects2; // size = 0x0180 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.c b/soh/src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.c new file mode 100644 index 000000000..5fbc2b317 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.c @@ -0,0 +1,333 @@ +/* + * File: z_bg_spot02_objects.c + * Overlay: ovl_Bg_Spot02_Objects + * Description: Graveyard Actors + */ + +#include "z_bg_spot02_objects.h" +#include "objects/object_spot02_objects/object_spot02_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSpot02Objects_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot02Objects_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot02Objects_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot02Objects_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_808ACCB8(Actor* thisx, GlobalContext* globalCtx); +void func_808AD450(Actor* thisx, GlobalContext* globalCtx); + +void func_808AC8FC(BgSpot02Objects* this, GlobalContext* globalCtx); +void func_808AC908(BgSpot02Objects* this, GlobalContext* globalCtx); +void func_808ACA08(BgSpot02Objects* this, GlobalContext* globalCtx); +void func_808ACAFC(BgSpot02Objects* this, GlobalContext* globalCtx); +void func_808ACB58(BgSpot02Objects* this, GlobalContext* globalCtx); +void func_808ACC34(BgSpot02Objects* this, GlobalContext* globalCtx); +void func_808AD3D4(BgSpot02Objects* this, GlobalContext* globalCtx); + +static void* D_808AD850[] = { + object_spot02_objects_Tex_0096B0, object_spot02_objects_Tex_00A2B0, object_spot02_objects_Tex_00AEB0, + object_spot02_objects_Tex_00BAB0, object_spot02_objects_Tex_00C6B0, object_spot02_objects_Tex_00D2B0, + object_spot02_objects_Tex_00DEB0, object_spot02_objects_Tex_00EAB0, object_spot02_objects_Tex_00F6B0, + object_spot02_objects_Tex_0102B0, object_spot02_objects_Tex_010EB0, object_spot02_objects_Tex_011AB0, +}; + +const ActorInit Bg_Spot02_Objects_InitVars = { + ACTOR_BG_SPOT02_OBJECTS, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT02_OBJECTS, + sizeof(BgSpot02Objects), + (ActorFunc)BgSpot02Objects_Init, + (ActorFunc)BgSpot02Objects_Destroy, + (ActorFunc)BgSpot02Objects_Update, + (ActorFunc)BgSpot02Objects_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot02Objects_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSpot02Objects* this = (BgSpot02Objects*)thisx; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, 0); + this->unk_16B = (u16)(thisx->params >> 8); + thisx->params = (u16)(thisx->params & 0xFF); + + switch (thisx->params) { + case 0: + case 1: + case 2: + Actor_ProcessInitChain(thisx, sInitChain); + + if (thisx->params == 0) { + if (Flags_GetSwitch(globalCtx, this->unk_16B)) { + this->actionFunc = func_808AC8FC; + thisx->world.pos.y += 255.0f; + } else { + this->actionFunc = func_808ACAFC; + } + + CollisionHeader_GetVirtual(&object_spot02_objects_Col_012BA4, &colHeader); + } else if (thisx->params == 1) { + this->actionFunc = func_808AC8FC; + CollisionHeader_GetVirtual(&object_spot02_objects_Col_0128D8, &colHeader); + thisx->flags |= ACTOR_FLAG_22; + } else { + if (globalCtx->sceneNum == SCENE_SPOT02) { + this->actionFunc = func_808AC908; + } else { + this->actionFunc = func_808AC8FC; + } + + CollisionHeader_GetVirtual(&object_spot02_objects_Col_0133EC, &colHeader); + } + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + if (((gSaveContext.eventChkInf[1] & 0x2000) && (globalCtx->sceneNum == SCENE_SPOT02) && + (thisx->params == 2)) || + (LINK_IS_ADULT && (thisx->params == 1))) { + Actor_Kill(thisx); + } + break; + + case 3: + this->unk_16A = 0; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_ITEMACTION); + this->actionFunc = func_808ACC34; + thisx->draw = func_808ACCB8; + + if (gSaveContext.eventChkInf[1] & 0x2000) { + Actor_Kill(thisx); + } + break; + + case 4: + this->timer = -12; + this->unk_170 = 0xFFFF; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_ITEMACTION); + this->actionFunc = func_808AD3D4; + thisx->draw = func_808AD450; + break; + } +} + +void BgSpot02Objects_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot02Objects* this = (BgSpot02Objects*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808AC8FC(BgSpot02Objects* this, GlobalContext* globalCtx) { +} + +void func_808AC908(BgSpot02Objects* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + + if (globalCtx->csCtx.state != 0) { + if (globalCtx->csCtx.npcActions[3] != NULL && globalCtx->csCtx.npcActions[3]->action == 2) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_GRAVE_EXPLOSION); + gSaveContext.eventChkInf[1] |= 0x2000; + this->timer = 25; + pos.x = (Math_SinS(this->dyna.actor.shape.rot.y) * 50.0f) + this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + 30.0f; + pos.z = (Math_CosS(this->dyna.actor.shape.rot.y) * 50.0f) + this->dyna.actor.world.pos.z; + EffectSsBomb2_SpawnLayered(globalCtx, &pos, &zeroVec, &zeroVec, 70, 30); + this->actionFunc = func_808ACA08; + } + } +} + +void func_808ACA08(BgSpot02Objects* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 20) { + this->dyna.actor.draw = NULL; + EffectSsHahen_SpawnBurst(globalCtx, &this->dyna.actor.world.pos, 30.0f, 0, 25, 5, 40, OBJECT_SPOT02_OBJECTS, 20, + object_spot02_objects_DL_012D30); + } else if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + } + + if (globalCtx->csCtx.frames == 402) { + if (!LINK_IS_ADULT) { + func_8002F7DC(&player->actor, NA_SE_VO_LI_DEMO_DAMAGE_KID); + } else { + func_8002F7DC(&player->actor, NA_SE_VO_LI_DEMO_DAMAGE); + } + } +} + +void func_808ACAFC(BgSpot02Objects* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->unk_16B)) { + Actor_SetFocus(&this->dyna.actor, 60.0f); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->actionFunc = func_808ACB58; + } +} + +void func_808ACB58(BgSpot02Objects* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 255.0f, 1.0f)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONEDOOR_STOP); + this->actionFunc = func_808AC8FC; + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_WALL_MOVE_SP - SFX_FLAG); + } +} + +void BgSpot02Objects_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot02Objects* this = (BgSpot02Objects*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot02Objects_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { + object_spot02_objects_DL_012A50, + object_spot02_objects_DL_0127C0, + object_spot02_objects_DL_0130B0, + }; + + Gfx_DrawDListOpa(globalCtx, dLists[thisx->params]); +} + +void func_808ACC34(BgSpot02Objects* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != 0 && globalCtx->csCtx.npcActions[0] != NULL && + globalCtx->csCtx.npcActions[0]->action == 2) { + this->unk_16A++; + + if (this->unk_16A >= 12) { + Actor_Kill(&this->dyna.actor); + } + } + + if (globalCtx->csCtx.frames == 245 || globalCtx->csCtx.frames == 351) { + func_800788CC(NA_SE_EV_LIGHTNING); + } +} + +void func_808ACCB8(Actor* thisx, GlobalContext* globalCtx) { + BgSpot02Objects* this = (BgSpot02Objects*)thisx; + f32 rate; + s32 pad; + u8 redPrim; + u8 greenPrim; + u8 bluePrim; + u8 redEnv; + u8 greenEnv; + u8 blueEnv; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot02_objects.c", 600); + + if (globalCtx->csCtx.state != 0 && globalCtx->csCtx.npcActions[0] != NULL && + globalCtx->csCtx.npcActions[0]->action == 2) { + if (this->unk_16A < 5) { + rate = (this->unk_16A / 5.0f); + redPrim = greenPrim = bluePrim = 255; + redEnv = 100.0f + 155.0f * rate; + greenEnv = 255; + blueEnv = 255.0f - 155.0f * rate; + } else { + rate = ((this->unk_16A - 5) / 7.0f); + redPrim = 255.0f - (255.0f * rate); + greenPrim = 255.0f - (55.0f * rate); + bluePrim = 255.0f - (255.0f * rate); + redEnv = 255.0f - (105.0f * rate); + greenEnv = 255.0f - (255.0f * rate); + blueEnv = 100.0f + (100.0f * rate); + } + + Matrix_Translate(globalCtx->csCtx.npcActions[0]->startPos.x, globalCtx->csCtx.npcActions[0]->startPos.y, + globalCtx->csCtx.npcActions[0]->startPos.z, MTXMODE_NEW); + Matrix_RotateX(globalCtx->csCtx.npcActions[0]->urot.x * (M_PI / (f32)0x8000), MTXMODE_APPLY); + Matrix_RotateY(globalCtx->csCtx.npcActions[0]->urot.y * (M_PI / (f32)0x8000), MTXMODE_APPLY); + Matrix_RotateZ(globalCtx->csCtx.npcActions[0]->urot.z * (M_PI / (f32)0x8000), MTXMODE_APPLY); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, redPrim, greenPrim, bluePrim, 255); + gDPSetEnvColor(POLY_XLU_DISP++, redEnv, greenEnv, blueEnv, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot02_objects.c", 679), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_808AD850[this->unk_16A])); + gDPPipeSync(POLY_XLU_DISP++); + gSPDisplayList(POLY_XLU_DISP++, object_spot02_objects_DL_0126F0); + gDPPipeSync(POLY_XLU_DISP++); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot02_objects.c", 692); +} + +void func_808AD3D4(BgSpot02Objects* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != 0 && globalCtx->csCtx.npcActions[2] != NULL && + globalCtx->csCtx.npcActions[2]->action == 2) { + if (this->timer == 2) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_IT_EXPLOSION_ICE); + } + + if (this->timer < 32) { + this->timer++; + } else { + Actor_Kill(&this->dyna.actor); + } + } +} + +void func_808AD450(Actor* thisx, GlobalContext* globalCtx) { + BgSpot02Objects* this = (BgSpot02Objects*)thisx; + s32 pad; + f32 lerp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot02_objects.c", 736); + + if (globalCtx->csCtx.state != 0 && globalCtx->csCtx.npcActions[2] != NULL) { + u16 temp_v1 = globalCtx->csCtx.npcActions[2]->urot.z * 0.00549325f; + + if (this->unk_170 != temp_v1) { + if (this->unk_170 == 0xFFFF) { + this->unk_170 = temp_v1; + this->unk_172 = temp_v1; + } else { + this->unk_172 = this->unk_170; + this->unk_170 = temp_v1; + } + } + + lerp = Environment_LerpWeight(globalCtx->csCtx.npcActions[2]->endFrame, + globalCtx->csCtx.npcActions[2]->startFrame, globalCtx->csCtx.frames); + + // should be able to remove & 0xFFFF with some other change + if ((globalCtx->csCtx.npcActions[2]->action & 0xFFFF) == 2) { + Matrix_Translate(globalCtx->csCtx.npcActions[2]->startPos.x, globalCtx->csCtx.npcActions[2]->startPos.y, + globalCtx->csCtx.npcActions[2]->startPos.z, MTXMODE_NEW); + Matrix_RotateX(globalCtx->csCtx.npcActions[2]->urot.x * (M_PI / (f32)0x8000), MTXMODE_APPLY); + Matrix_RotateY(globalCtx->csCtx.npcActions[2]->urot.y * (M_PI / (f32)0x8000), MTXMODE_APPLY); + Matrix_Scale(0.9f, 0.9f, (((this->unk_170 - this->unk_172) * lerp) + this->unk_172) * 0.1f, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, 128); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 120, 0, 128); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot02_objects.c", 795), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 2 * this->timer, -3 * this->timer, 32, 64, 1, + 4 * this->timer, -6 * this->timer, 32, 64)); + gDPPipeSync(POLY_XLU_DISP++); + gSPDisplayList(POLY_XLU_DISP++, object_spot02_objects_DL_0013F0); + gDPPipeSync(POLY_XLU_DISP++); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot02_objects.c", 818); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h b/soh/src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h new file mode 100644 index 000000000..89bbf7d5e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot02_Objects/z_bg_spot02_objects.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_SPOT02_OBJECTS_H +#define Z_BG_SPOT02_OBJECTS_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot02Objects; + +typedef void (*BgSpot02ObjectsActionFunc)(struct BgSpot02Objects*, GlobalContext*); + +typedef struct BgSpot02Objects { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot02ObjectsActionFunc actionFunc; + /* 0x0168 */ s16 timer; + /* 0x016A */ u8 unk_16A; + /* 0x016B */ u8 unk_16B; + /* 0x016C */ char unk_16C[4]; + /* 0x0170 */ u16 unk_170; + /* 0x0172 */ u16 unk_172; +} BgSpot02Objects; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.c b/soh/src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.c new file mode 100644 index 000000000..9eccc8f39 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.c @@ -0,0 +1,162 @@ +/* + * File: z_bg_spot03_taki.c + * Overlay: ovl_Bg_Spot03_Taki + * Description: Zora's River Waterfall + */ + +#include "z_bg_spot03_taki.h" +#include "objects/object_spot03_object/object_spot03_object.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSpot03Taki_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot03Taki_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot03Taki_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot03Taki_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808ADEF0(BgSpot03Taki* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot03_Taki_InitVars = { + ACTOR_BG_SPOT03_TAKI, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT03_OBJECT, + sizeof(BgSpot03Taki), + (ActorFunc)BgSpot03Taki_Init, + (ActorFunc)BgSpot03Taki_Destroy, + (ActorFunc)BgSpot03Taki_Update, + (ActorFunc)BgSpot03Taki_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot03Taki_ApplyOpeningAlpha(BgSpot03Taki* this, s32 bufferIndex) { + s32 i; + Vtx* vtx = (bufferIndex == 0) ? SEGMENTED_TO_VIRTUAL(object_spot03_object_Vtx_000800) + : SEGMENTED_TO_VIRTUAL(object_spot03_object_Vtx_000990); + + vtx = ResourceMgr_LoadVtxByName(vtx); + + for (i = 0; i < 5; i++) { + vtx[i + 10].v.cn[3] = this->openingAlpha; + } +} + +void BgSpot03Taki_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot03Taki* this = (BgSpot03Taki*)thisx; + s16 pad; + CollisionHeader* colHeader = NULL; + + this->switchFlag = (this->dyna.actor.params & 0x3F); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&object_spot03_object_Col_000C98, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->bufferIndex = 0; + this->openingAlpha = 255.0f; + BgSpot03Taki_ApplyOpeningAlpha(this, 0); + BgSpot03Taki_ApplyOpeningAlpha(this, 1); + this->actionFunc = func_808ADEF0; +} + +void BgSpot03Taki_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot03Taki* this = (BgSpot03Taki*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808ADEF0(BgSpot03Taki* this, GlobalContext* globalCtx) { + if (this->state == WATERFALL_CLOSED) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->state = WATERFALL_OPENING_ANIMATED; + this->timer = 40; + OnePointCutscene_Init(globalCtx, 4100, -99, NULL, MAIN_CAM); + } + } else if (this->state == WATERFALL_OPENING_IDLE) { + this->timer--; + if (this->timer < 0) { + this->state = WATERFALL_OPENING_ANIMATED; + } + } else if (this->state == WATERFALL_OPENING_ANIMATED) { + if (this->openingAlpha > 0) { + this->openingAlpha -= 5; + if (this->openingAlpha <= 0.0f) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->timer = 400; + this->state = WATERFALL_OPENED; + this->openingAlpha = 0; + } + } + } else if (this->state == WATERFALL_OPENED) { + this->timer--; + if (this->timer < 0) { + this->state = WATERFALL_CLOSING; + } + } else if (this->state == WATERFALL_CLOSING) { + if (this->openingAlpha < 255.0f) { + this->openingAlpha += 5.0f; + if (this->openingAlpha >= 255.0f) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->state = WATERFALL_CLOSED; + this->openingAlpha = 255.0f; + Flags_UnsetSwitch(globalCtx, this->switchFlag); + } + } + } + + BgSpot03Taki_ApplyOpeningAlpha(this, this->bufferIndex); +} + +void BgSpot03Taki_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot03Taki* this = (BgSpot03Taki*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot03Taki_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot03Taki* this = (BgSpot03Taki*)thisx; + s32 pad; + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot03_taki.c", 321); + + gameplayFrames = globalCtx->gameplayFrames; + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot03_taki.c", 325), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, gameplayFrames * 5, 64, 64, 1, 0, gameplayFrames * 5, 64, 64)); + + gSPDisplayList(POLY_XLU_DISP++, object_spot03_object_DL_000B20); + + if (this->bufferIndex == 0) { + gSPVertex(POLY_XLU_DISP++, object_spot03_object_Vtx_000800, 25, 0); + } else { + gSPVertex(POLY_XLU_DISP++, object_spot03_object_Vtx_000990, 25, 0); + } + + gSPDisplayList(POLY_XLU_DISP++, object_spot03_object_DL_000BC0); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames * 1, gameplayFrames * 3, 64, 64, 1, + -gameplayFrames, gameplayFrames * 3, 64, 64)); + + gSPDisplayList(POLY_XLU_DISP++, object_spot03_object_DL_001580); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot03_taki.c", 358); + + this->bufferIndex = this->bufferIndex == 0; + + if (this->state >= WATERFALL_OPENING_IDLE && this->state <= WATERFALL_OPENED) { + Audio_PlaySoundWaterfall(&this->dyna.actor.projectedPos, 0.5f); + } else { + Audio_PlaySoundWaterfall(&this->dyna.actor.projectedPos, 1.0f); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h b/soh/src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h new file mode 100644 index 000000000..405ad9696 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot03_Taki/z_bg_spot03_taki.h @@ -0,0 +1,29 @@ +#ifndef Z_BG_SPOT03_TAKI_H +#define Z_BG_SPOT03_TAKI_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + WATERFALL_CLOSED, + WATERFALL_OPENING_IDLE, + WATERFALL_OPENING_ANIMATED, + WATERFALL_OPENED, + WATERFALL_CLOSING +} BgSpot03TakiState; + +struct BgSpot03Taki; + +typedef void (*BgSpot03TakiActionFunc)(struct BgSpot03Taki*, GlobalContext*); + +typedef struct BgSpot03Taki { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot03TakiActionFunc actionFunc; + /* 0x0168 */ s16 timer; + /* 0x016A */ u8 state; + /* 0x016C */ u16 switchFlag; + /* 0x0170 */ f32 openingAlpha; + /* 0x0174 */ u8 bufferIndex; +} BgSpot03Taki; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot05_Soko/z_bg_spot05_soko.c b/soh/src/overlays/actors/ovl_Bg_Spot05_Soko/z_bg_spot05_soko.c new file mode 100644 index 000000000..ae7094368 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot05_Soko/z_bg_spot05_soko.c @@ -0,0 +1,106 @@ +/* + * File: z_bg_spot05_soko.c + * Overlay: ovl_Bg_Spot05_Soko + * Description: Sacred Forest Meadow Entities + */ + +#include "z_bg_spot05_soko.h" +#include "objects/object_spot05_objects/object_spot05_objects.h" + +#define FLAGS 0 + +void BgSpot05Soko_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot05Soko_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot05Soko_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot05Soko_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_808AE5A8(BgSpot05Soko* this, GlobalContext* globalCtx); +void func_808AE5B4(BgSpot05Soko* this, GlobalContext* globalCtx); +void func_808AE630(BgSpot05Soko* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot05_Soko_InitVars = { + ACTOR_BG_SPOT05_SOKO, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT05_OBJECTS, + sizeof(BgSpot05Soko), + (ActorFunc)BgSpot05Soko_Init, + (ActorFunc)BgSpot05Soko_Destroy, + (ActorFunc)BgSpot05Soko_Update, + (ActorFunc)BgSpot05Soko_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static Gfx* sDLists[] = { + object_spot05_objects_DL_000840, + object_spot05_objects_DL_001190, +}; + +void BgSpot05Soko_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad1; + BgSpot05Soko* this = (BgSpot05Soko*)thisx; + CollisionHeader* colHeader = NULL; + s32 pad2; + + Actor_ProcessInitChain(thisx, sInitChain); + this->switchFlag = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (thisx->params == 0) { + CollisionHeader_GetVirtual(&object_spot05_objects_Col_000918, &colHeader); + if (LINK_IS_ADULT) { + Actor_Kill(thisx); + } else { + this->actionFunc = func_808AE5A8; + } + } else { + CollisionHeader_GetVirtual(&object_spot05_objects_Col_0012C0, &colHeader); + if (Flags_GetSwitch(globalCtx, this->switchFlag) != 0) { + Actor_Kill(thisx); + } else { + this->actionFunc = func_808AE5B4; + thisx->flags |= ACTOR_FLAG_4; + } + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); +} + +void BgSpot05Soko_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot05Soko* this = (BgSpot05Soko*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808AE5A8(BgSpot05Soko* this, GlobalContext* globalCtx) { +} + +void func_808AE5B4(BgSpot05Soko* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 30, NA_SE_EV_METALDOOR_CLOSE); + Actor_SetFocus(&this->dyna.actor, 50.0f); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->actionFunc = func_808AE630; + this->dyna.actor.speedXZ = 0.5f; + } +} + +void func_808AE630(BgSpot05Soko* this, GlobalContext* globalCtx) { + this->dyna.actor.speedXZ *= 1.5f; + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 120.0f, this->dyna.actor.speedXZ) != + 0) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgSpot05Soko_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot05Soko* this = (BgSpot05Soko*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot05Soko_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, sDLists[thisx->params]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot05_Soko/z_bg_spot05_soko.h b/soh/src/overlays/actors/ovl_Bg_Spot05_Soko/z_bg_spot05_soko.h new file mode 100644 index 000000000..1dc53b792 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot05_Soko/z_bg_spot05_soko.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_SPOT05_SOKO_H +#define Z_BG_SPOT05_SOKO_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot05Soko; + +typedef void (*BgSpot05SokoActionFunc)(struct BgSpot05Soko*, GlobalContext*); + +typedef struct BgSpot05Soko { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot05SokoActionFunc actionFunc; + /* 0x0168 */ s32 switchFlag; +} BgSpot05Soko; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c b/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c new file mode 100644 index 000000000..fbc707ade --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.c @@ -0,0 +1,510 @@ +/* + * File: z_bg_spot06_objects.c + * Overlay: ovl_Bg_Spot06_Objects + * Description: Lake Hylia Objects + */ + +#include "z_bg_spot06_objects.h" +#include "objects/object_spot06_objects/object_spot06_objects.h" + +#define FLAGS ACTOR_FLAG_9 + +typedef enum { + /* 0x0 */ LHO_WATER_TEMPLE_ENTRACE_GATE, + /* 0x1 */ LHO_WATER_TEMPLE_ENTRANCE_LOCK, + /* 0x2 */ LHO_WATER_PLANE, + /* 0x3 */ LHO_ICE_BLOCK +} LakeHyliaObjectsType; + +typedef enum { + /* 0x0 */ LHWB_GERUDO_VALLEY_RIVER_UPPER, // entrance from Gerudo Valley + /* 0x1 */ LHWB_GERUDO_VALLEY_RIVER_LOWER, // river flowing from Gerudo Valley + /* 0x2 */ LHWB_MAIN_1, // main water box + /* 0x3 */ LHWB_MAIN_2 // extension of main water box +} LakeHyliaWaterBoxIndices; + +// Lake Hylia water plane levels +#define WATER_LEVEL_RAISED (-1313) +#define WATER_LEVEL_RIVER_RAISED (WATER_LEVEL_RAISED + 200) +#define WATER_LEVEL_LOWERED (WATER_LEVEL_RAISED - 680) +#define WATER_LEVEL_RIVER_LOWERED (WATER_LEVEL_RIVER_RAISED - 80) + +void BgSpot06Objects_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot06Objects_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot06Objects_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot06Objects_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgSpot06Objects_GateWaitForSwitch(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_GateWaitToOpen(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_GateOpen(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_DoNothing(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_LockWait(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_LockPullOutward(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_LockSwimToSurface(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_LockFloat(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_WaterPlaneCutsceneWait(BgSpot06Objects* this, GlobalContext* globalCtx); +void BgSpot06Objects_WaterPlaneCutsceneRise(BgSpot06Objects* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot06_Objects_InitVars = { + ACTOR_BG_SPOT06_OBJECTS, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT06_OBJECTS, + sizeof(BgSpot06Objects), + (ActorFunc)BgSpot06Objects_Init, + (ActorFunc)BgSpot06Objects_Destroy, + (ActorFunc)BgSpot06Objects_Update, + (ActorFunc)BgSpot06Objects_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphItemsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000080, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 1, { { 0, 0, -160 }, 18 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphItemsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +static InitChainEntry sInitChainWaterPlane[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgSpot06Objects_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot06Objects* this = (BgSpot06Objects*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + this->switchFlag = thisx->params & 0xFF; + thisx->params = (thisx->params >> 8) & 0xFF; + + osSyncPrintf("spot06 obj nthisx->arg_data=[%d]", thisx->params); + + switch (thisx->params) { + case LHO_WATER_TEMPLE_ENTRACE_GATE: + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gLakeHyliaWaterTempleGateCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + if (LINK_IS_ADULT && Flags_GetSwitch(globalCtx, this->switchFlag)) { + thisx->world.pos.y = thisx->home.pos.y + 120.0f; + this->actionFunc = BgSpot06Objects_DoNothing; + + } else { + this->actionFunc = BgSpot06Objects_GateWaitForSwitch; + } + + break; + case LHO_WATER_TEMPLE_ENTRANCE_LOCK: + Actor_ProcessInitChain(thisx, sInitChain); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, thisx, &sJntSphInit, this->colliderItem); + + if (LINK_IS_ADULT && Flags_GetSwitch(globalCtx, this->switchFlag)) { + if (!(gSaveContext.eventChkInf[6] & 0x200)) { + thisx->home.pos.y = thisx->world.pos.y = WATER_LEVEL_LOWERED; + } else { + thisx->home.pos.y = thisx->world.pos.y = WATER_LEVEL_RAISED; + } + + this->actionFunc = BgSpot06Objects_LockFloat; + thisx->world.pos.z -= 100.0f; + thisx->home.pos.z = thisx->world.pos.z + 16.0f; + this->collider.elements[0].dim.worldSphere.radius = + this->collider.elements[0].dim.modelSphere.radius * 2; + this->collider.elements[0].dim.worldSphere.center.z = thisx->world.pos.z + 16.0f; + } else { + this->actionFunc = BgSpot06Objects_LockWait; + this->collider.elements[0].dim.worldSphere.radius = this->collider.elements[0].dim.modelSphere.radius; + this->collider.elements[0].dim.worldSphere.center.z = thisx->world.pos.z; + } + + this->collider.elements[0].dim.worldSphere.center.x = thisx->world.pos.x; + this->collider.elements[0].dim.worldSphere.center.y = thisx->world.pos.y; + thisx->colChkInfo.mass = MASS_IMMOVABLE; + break; + case LHO_WATER_PLANE: + Actor_ProcessInitChain(thisx, sInitChainWaterPlane); + thisx->flags = ACTOR_FLAG_4 | ACTOR_FLAG_5; + + if (LINK_IS_ADULT && !(gSaveContext.eventChkInf[6] & 0x200)) { + if (gSaveContext.sceneSetupIndex < 4) { + this->lakeHyliaWaterLevel = -681.0f; + globalCtx->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = + WATER_LEVEL_RIVER_LOWERED; + globalCtx->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].zMin -= 50; + globalCtx->colCtx.colHeader->waterBoxes[LHWB_MAIN_1].ySurface = WATER_LEVEL_LOWERED; + globalCtx->colCtx.colHeader->waterBoxes[LHWB_MAIN_2].ySurface = WATER_LEVEL_LOWERED; + this->actionFunc = BgSpot06Objects_DoNothing; + } else { + thisx->world.pos.y = this->lakeHyliaWaterLevel = -681.0f; + thisx->world.pos.y += WATER_LEVEL_RAISED; + this->actionFunc = BgSpot06Objects_WaterPlaneCutsceneWait; + } + } else { + this->lakeHyliaWaterLevel = 0.0f; + this->actionFunc = BgSpot06Objects_DoNothing; + } + break; + case LHO_ICE_BLOCK: + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gLakeHyliaZoraShortcutIceblockCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + this->actionFunc = BgSpot06Objects_DoNothing; + + if (!LINK_IS_ADULT) { + Actor_Kill(thisx); + } + break; + } +} + +void BgSpot06Objects_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot06Objects* this = (BgSpot06Objects*)thisx; + + switch (this->dyna.actor.params) { + case LHO_WATER_TEMPLE_ENTRACE_GATE: + case LHO_ICE_BLOCK: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + break; + case LHO_WATER_TEMPLE_ENTRANCE_LOCK: + Collider_DestroyJntSph(globalCtx, &this->collider); + break; + case LHO_WATER_PLANE: + break; + } +} + +/** + * Water Temple entrance gate effect functions + */ +void BgSpot06Objects_GateSpawnBubbles(BgSpot06Objects* this, GlobalContext* globalCtx) { + Vec3f sp34; + f32 tmp; + + if ((globalCtx->gameplayFrames % 3) == 0) { + tmp = Rand_CenteredFloat(160.0f); + sp34.x = (Math_SinS(this->dyna.actor.shape.rot.y + 0x4000) * tmp) + this->dyna.actor.world.pos.x; + sp34.y = this->dyna.actor.world.pos.y; + sp34.z = (Math_CosS(this->dyna.actor.shape.rot.y + 0x4000) * tmp) + this->dyna.actor.world.pos.z; + EffectSsBubble_Spawn(globalCtx, &sp34, 50.0f, 70.0f, 10.0f, (Rand_ZeroOne() * 0.05f) + 0.175f); + } +} + +/** + * This is where the gate waits for the switch to be set by the fish shaped lock. + */ +void BgSpot06Objects_GateWaitForSwitch(BgSpot06Objects* this, GlobalContext* globalCtx) { + s32 i; + + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->timer = 100; + this->dyna.actor.world.pos.y += 3.0f; + this->actionFunc = BgSpot06Objects_GateWaitToOpen; + + for (i = 0; i < 15; i++) { + BgSpot06Objects_GateSpawnBubbles(this, globalCtx); + } + } +} + +/** + * This is where the gate waits a few frames before rising after the switch is set. + */ +void BgSpot06Objects_GateWaitToOpen(BgSpot06Objects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + this->actionFunc = BgSpot06Objects_GateOpen; + } +} + +/** + * This is where the gate finally rises upward. + */ +void BgSpot06Objects_GateOpen(BgSpot06Objects* this, GlobalContext* globalCtx) { + BgSpot06Objects_GateSpawnBubbles(this, globalCtx); + + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 120.0f, 0.6f)) { + this->actionFunc = BgSpot06Objects_DoNothing; + this->timer = 0; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG); + } +} + +void BgSpot06Objects_DoNothing(BgSpot06Objects* this, GlobalContext* globalCtx) { +} + +/** + * Fish shaped lock effect functions + */ +void BgSpot06Objects_LockSpawnWaterRipples(BgSpot06Objects* this, GlobalContext* globalCtx, s32 flag) { + if (flag || !(globalCtx->gameplayFrames % 7)) { + EffectSsGRipple_Spawn(globalCtx, &this->dyna.actor.home.pos, 300, 700, 0); + } +} + +void BgSpot06Objects_LockSpawnBubbles(BgSpot06Objects* this, GlobalContext* globalCtx, s32 flag) { + if (!(globalCtx->gameplayFrames % 7) || flag) { + EffectSsBubble_Spawn(globalCtx, &this->dyna.actor.world.pos, 0.0f, 40.0f, 30.0f, + (Rand_ZeroOne() * 0.05f) + 0.175f); + } +} + +/** + * This is where the fish shaped lock waits to be pulled out by the hookshot. Once it does it will spawn bubbles. + */ +void BgSpot06Objects_LockWait(BgSpot06Objects* this, GlobalContext* globalCtx) { + s32 pad; + s32 i; + s32 pad2; + Vec3f effectPos; + f32 sin; + f32 cos; + + if (this->collider.base.acFlags & 2) { + this->timer = 130; + this->dyna.actor.flags |= ACTOR_FLAG_4; + sin = Math_SinS(this->dyna.actor.world.rot.y); + cos = Math_CosS(this->dyna.actor.world.rot.y); + this->dyna.actor.world.pos.x += (3.0f * sin); + this->dyna.actor.world.pos.z += (3.0f * cos); + + for (i = 0; i < 20; i++) { + BgSpot06Objects_LockSpawnBubbles(this, globalCtx, 1); + } + + effectPos.x = this->dyna.actor.world.pos.x + (5.0f * sin); + effectPos.y = this->dyna.actor.world.pos.y; + effectPos.z = this->dyna.actor.world.pos.z + (5.0f * cos); + + for (i = 0; i < 3; i++) { + EffectSsBubble_Spawn(globalCtx, &effectPos, 0.0f, 20.0f, 20.0f, (Rand_ZeroOne() * 0.1f) + 0.7f); + } + + EffectSsGSplash_Spawn(globalCtx, &this->dyna.actor.world.pos, NULL, NULL, 1, 700); + this->collider.elements->dim.worldSphere.radius = 45; + this->actionFunc = BgSpot06Objects_LockPullOutward; + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Flags_SetSwitch(globalCtx, this->switchFlag); + OnePointCutscene_Init(globalCtx, 4120, 170, &this->dyna.actor, MAIN_CAM); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +/** + * Once the fish shaped lock is pulled out from the Hookshot it will move outward. + */ +void BgSpot06Objects_LockPullOutward(BgSpot06Objects* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + this->dyna.actor.world.pos.x += (0.3f * Math_SinS(this->dyna.actor.world.rot.y)); + this->dyna.actor.world.pos.z += (0.3f * Math_CosS(this->dyna.actor.world.rot.y)); + BgSpot06Objects_LockSpawnBubbles(this, globalCtx, 0); + + if (this->timer == 0) { + this->dyna.actor.velocity.y = 0.5f; + this->dyna.actor.flags &= ~ACTOR_FLAG_13; + + this->actionFunc = BgSpot06Objects_LockSwimToSurface; + } +} + +/** + * After being pulled all the way out the fish shaped lock will rise to the surface, creating bubbles in the water as it + * does so. + */ +void BgSpot06Objects_LockSwimToSurface(BgSpot06Objects* this, GlobalContext* globalCtx) { + f32 cos; + f32 pad; + + this->dyna.actor.world.pos.y += this->dyna.actor.velocity.y; + + if (this->dyna.actor.velocity.y <= 0.0f) { + cos = Math_CosS(this->dyna.actor.shape.rot.x) * 4.3f; + this->dyna.actor.world.pos.x += (cos * Math_SinS(this->dyna.actor.shape.rot.y)); + this->dyna.actor.world.pos.z += (cos * Math_CosS(this->dyna.actor.shape.rot.y)); + this->dyna.actor.world.pos.y = this->dyna.actor.world.pos.y - 1.3f; + BgSpot06Objects_LockSpawnWaterRipples(this, globalCtx, 0); + + if (Math_ScaledStepToS(&this->dyna.actor.shape.rot.x, 0, 0x260) != 0) { + this->dyna.actor.home.pos.x = + this->dyna.actor.world.pos.x - (Math_SinS(this->dyna.actor.shape.rot.y) * 16.0f); + this->dyna.actor.home.pos.z = + this->dyna.actor.world.pos.z - (Math_CosS(this->dyna.actor.shape.rot.y) * 16.0f); + this->dyna.actor.world.pos.y = -1993.0f; + this->timer = 32; + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + this->collider.elements[0].dim.worldSphere.radius = this->collider.elements[0].dim.modelSphere.radius * 2; + this->actionFunc = BgSpot06Objects_LockFloat; + } + } else { + if (this->dyna.actor.world.pos.y >= -1973.0f) { + this->dyna.actor.velocity.y = 0.0f; + BgSpot06Objects_LockSpawnWaterRipples(this, globalCtx, 1); + EffectSsGSplash_Spawn(globalCtx, &this->dyna.actor.home.pos, NULL, NULL, 1, 700); + } else if (this->dyna.actor.shape.rot.x == -0x4000) { + this->dyna.actor.velocity.y += 0.02f; + this->dyna.actor.world.pos.x = Rand_CenteredFloat(1.0f) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = Rand_CenteredFloat(1.0f) + this->dyna.actor.home.pos.z; + this->dyna.actor.velocity.y = + (this->dyna.actor.velocity.y > 10.0f) ? (10.0f) : (this->dyna.actor.velocity.y); + BgSpot06Objects_LockSpawnBubbles(this, globalCtx, 0); + } else { + BgSpot06Objects_LockSpawnBubbles(this, globalCtx, 0); + + if (Math_ScaledStepToS(&this->dyna.actor.shape.rot.x, -0x4000, 0x30)) { + this->dyna.actor.home.pos.x = this->dyna.actor.world.pos.x; + this->dyna.actor.home.pos.y = -1993.0f; + this->dyna.actor.home.pos.z = this->dyna.actor.world.pos.z; + } + } + } +} + +/** + * Once the fish shaped lock finishes rising to the surface it will float and create ripples in the water every few + * frames. + */ +void BgSpot06Objects_LockFloat(BgSpot06Objects* this, GlobalContext* globalCtx) { + BgSpot06Objects_LockSpawnWaterRipples(this, globalCtx, 0); + + if (this->timer != 0) { + this->timer--; + } + + this->dyna.actor.world.pos.y = (2.0f * sinf(this->timer * (M_PI / 16.0f))) + this->dyna.actor.home.pos.y; + + if (this->timer == 0) { + this->timer = 32; + } +} + +void BgSpot06Objects_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot06Objects* this = (BgSpot06Objects*)thisx; + + this->actionFunc(this, globalCtx); + + if (thisx->params == LHO_WATER_TEMPLE_ENTRANCE_LOCK) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +/** + * Draw the Lake Hylia water plane, and scroll its texture + */ +void BgSpot06Objects_DrawLakeHyliaWater(BgSpot06Objects* this, GlobalContext* globalCtx) { + s32 pad; + s32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot06_objects.c", 844); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot06_objects.c", 850), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gameplayFrames = globalCtx->state.frames; + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -gameplayFrames, gameplayFrames, 32, 32, 1, gameplayFrames, + gameplayFrames, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -gameplayFrames, gameplayFrames * 6, 32, 32, 1, + gameplayFrames, gameplayFrames * 6, 32, 32)); + + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + + if ((this->lakeHyliaWaterLevel < -680.0f) && (gSaveContext.sceneSetupIndex < 4)) { + gSPDisplayList(POLY_XLU_DISP++, gLakeHyliaLowWaterDL); + } else { + gSPDisplayList(POLY_XLU_DISP++, gLakeHyliaHighWaterDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot06_objects.c", 879); +} + +void BgSpot06Objects_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot06Objects* this = (BgSpot06Objects*)thisx; + + switch (this->dyna.actor.params) { + case LHO_WATER_TEMPLE_ENTRACE_GATE: + Gfx_DrawDListOpa(globalCtx, gLakeHyliaWaterTempleGateDL); + break; + case LHO_WATER_TEMPLE_ENTRANCE_LOCK: + Gfx_DrawDListOpa(globalCtx, gLakeHyliaWaterTempleKeyDL); + + if (this->actionFunc == BgSpot06Objects_LockSwimToSurface) { + Collider_UpdateSpheres(1, &this->collider); + } + break; + case LHO_WATER_PLANE: + BgSpot06Objects_DrawLakeHyliaWater(this, globalCtx); + break; + case LHO_ICE_BLOCK: + Gfx_DrawDListOpa(globalCtx, gLakeHyliaZoraShortcutIceblockDL); + break; + } +} + +/** + * This is where the Lake Hylia water plane waits for the cutscene to set the water risen flag after the Water Temple is + * cleared. + */ +void BgSpot06Objects_WaterPlaneCutsceneWait(BgSpot06Objects* this, GlobalContext* globalCtx) { + if (gSaveContext.eventChkInf[6] & 0x200) { + this->actionFunc = BgSpot06Objects_WaterPlaneCutsceneRise; + } +} + +/** + * This is where the Lake Hylia water plane rises in the cutscene after the Water Temple is cleared. + */ +void BgSpot06Objects_WaterPlaneCutsceneRise(BgSpot06Objects* this, GlobalContext* globalCtx) { + s32 pad; + + this->dyna.actor.world.pos.y = this->lakeHyliaWaterLevel + WATER_LEVEL_RAISED; + + if (this->lakeHyliaWaterLevel >= 0.0001f) { + this->dyna.actor.world.pos.y = WATER_LEVEL_RAISED; + this->actionFunc = BgSpot06Objects_DoNothing; + } else { + Math_SmoothStepToF(&this->lakeHyliaWaterLevel, 1.0f, 0.1f, 1.0f, 0.001f); + globalCtx->colCtx.colHeader->waterBoxes[LHWB_GERUDO_VALLEY_RIVER_LOWER].ySurface = WATER_LEVEL_RIVER_LOWERED; + globalCtx->colCtx.colHeader->waterBoxes[LHWB_MAIN_1].ySurface = this->dyna.actor.world.pos.y; + globalCtx->colCtx.colHeader->waterBoxes[LHWB_MAIN_2].ySurface = this->dyna.actor.world.pos.y; + } + + func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h b/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h new file mode 100644 index 000000000..2deb727b0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot06_Objects/z_bg_spot06_objects.h @@ -0,0 +1,21 @@ +#ifndef Z_BG_SPOT06_OBJECTS_H +#define Z_BG_SPOT06_OBJECTS_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot06Objects; + +typedef void (*BgSpot06ObjectsActionFunc)(struct BgSpot06Objects*, GlobalContext*); + +typedef struct BgSpot06Objects { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot06ObjectsActionFunc actionFunc; + /* 0x0168 */ s16 switchFlag; + /* 0x016A */ s16 timer; + /* 0x016C */ f32 lakeHyliaWaterLevel; + /* 0x0170 */ ColliderJntSph collider; + /* 0x0190 */ ColliderJntSphElement colliderItem[1]; +} BgSpot06Objects; // size = 0x01D0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot07_Taki/z_bg_spot07_taki.c b/soh/src/overlays/actors/ovl_Bg_Spot07_Taki/z_bg_spot07_taki.c new file mode 100644 index 000000000..4970e4775 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot07_Taki/z_bg_spot07_taki.c @@ -0,0 +1,119 @@ +/* + * File: z_bg_spot07_taki.c + * Overlay: ovl_Bg_Spot07_Taki + * Description: Zora's Domain Waterfall and Ice + */ + +#include "z_bg_spot07_taki.h" +#include "objects/object_spot07_object/object_spot07_object.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSpot07Taki_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot07Taki_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot07Taki_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot07Taki_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgSpot07Taki_DoNothing(BgSpot07Taki* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot07_Taki_InitVars = { + ACTOR_BG_SPOT07_TAKI, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT07_OBJECT, + sizeof(BgSpot07Taki), + (ActorFunc)BgSpot07Taki_Init, + (ActorFunc)BgSpot07Taki_Destroy, + (ActorFunc)BgSpot07Taki_Update, + (ActorFunc)BgSpot07Taki_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot07Taki_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot07Taki* this = (BgSpot07Taki*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + if (LINK_IS_ADULT) { + if (this->dyna.actor.params == 0) { + CollisionHeader_GetVirtual(&object_spot07_object_Col_002590, &colHeader); + } else { + CollisionHeader_GetVirtual(&object_spot07_object_Col_0038FC, &colHeader); + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } + this->actionFunc = BgSpot07Taki_DoNothing; +} + +void BgSpot07Taki_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot07Taki* this = (BgSpot07Taki*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgSpot07Taki_DoNothing(BgSpot07Taki* this, GlobalContext* globalCtx) { +} + +void BgSpot07Taki_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot07Taki* this = (BgSpot07Taki*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot07Taki_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot07Taki* this = (BgSpot07Taki*)thisx; + u32 frames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot07_taki.c", 169); + frames = globalCtx->gameplayFrames; + if (LINK_IS_ADULT) { + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot07_taki.c", 177), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->dyna.actor.params == 0) { + gSPDisplayList(POLY_OPA_DISP++, object_spot07_object_DL_001CF0); + } else { + gSPDisplayList(POLY_OPA_DISP++, object_spot07_object_DL_003210); + } + } + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot07_taki.c", 191), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, ((frames * -1) & 0x7F), ((frames * 1) & 0x7F), 32, 32, 1, + ((frames * 1) & 0x7F), ((frames * 1) & 0x7F), 32, 32)); + + if (!LINK_IS_ADULT) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + if (this->dyna.actor.params == 0) { + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, ((frames * -1) & 0x7F), ((frames * -3) & 0xFF), 64, + 64, 1, ((frames * 1) & 0x7F), ((frames * -3) & 0xFF), 64, 64)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frames * 0, ((frames * 3) & 0x1FF), 32, 128, 1, + frames * 0, ((frames * 3) & 0x1FF), 32, 128)); + gSPDisplayList(POLY_XLU_DISP++, object_spot07_object_DL_000460); + } else { + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frames * 0, ((frames * -1) & 0x7F), 32, 32, 1, + frames * 0, ((frames * -1) & 0x7F), 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frames * 0, ((frames * 3) & 0x1FF), 32, 128, 1, + frames * 0, ((frames * 3) & 0x1FF), 32, 128)); + gSPDisplayList(POLY_XLU_DISP++, object_spot07_object_DL_000BE0); + } + } else if (this->dyna.actor.params == 0) { + gSPDisplayList(POLY_XLU_DISP++, object_spot07_object_DL_001F68); + } else { + gSPDisplayList(POLY_XLU_DISP++, object_spot07_object_DL_0032D8); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot07_taki.c", 272); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot07_Taki/z_bg_spot07_taki.h b/soh/src/overlays/actors/ovl_Bg_Spot07_Taki/z_bg_spot07_taki.h new file mode 100644 index 000000000..c033c68fb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot07_Taki/z_bg_spot07_taki.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_SPOT07_TAKI_H +#define Z_BG_SPOT07_TAKI_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot07Taki; + +typedef void (*BgSpot07TakiActionFunc)(struct BgSpot07Taki*, GlobalContext*); + +typedef struct BgSpot07Taki { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot07TakiActionFunc actionFunc; +} BgSpot07Taki; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot08_Bakudankabe/z_bg_spot08_bakudankabe.c b/soh/src/overlays/actors/ovl_Bg_Spot08_Bakudankabe/z_bg_spot08_bakudankabe.c new file mode 100644 index 000000000..23dec68be --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot08_Bakudankabe/z_bg_spot08_bakudankabe.c @@ -0,0 +1,204 @@ +/* + * File: z_bg_spot08_bakudankabe + * Overlay: ovl_Bg_Spot08_Bakudankabe + * Description: Destructible Wall (Zora's Fountain) + */ + +#include "z_bg_spot08_bakudankabe.h" +#include "objects/object_spot08_obj/object_spot08_obj.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS ACTOR_FLAG_22 + +void BgSpot08Bakudankabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot08Bakudankabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot08Bakudankabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot08Bakudankabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808B02D0(BgSpot08Bakudankabe* this, GlobalContext* globalCtx); +void func_808B0324(BgSpot08Bakudankabe* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot08_Bakudankabe_InitVars = { + ACTOR_BG_SPOT08_BAKUDANKABE, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT08_OBJ, + sizeof(BgSpot08Bakudankabe), + (ActorFunc)BgSpot08Bakudankabe_Init, + (ActorFunc)BgSpot08Bakudankabe_Destroy, + (ActorFunc)BgSpot08Bakudankabe_Update, + (ActorFunc)BgSpot08Bakudankabe_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 50, 50 }, 70 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { -100, 50, 50 }, 70 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 100, 50, 50 }, 70 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 3, + sJntSphElementsInit, +}; + +static Vec3f D_808B08AC[] = { + { 0.0f, 116.65f, 50.0f }, + { 115.0f, 95.0f, 10.0f }, + { -115.0f, 95.0f, 10.0f }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 3200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void func_808B02D0(BgSpot08Bakudankabe* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->dyna.actor, &sJntSphInit, this->colliderItems); +} + +void func_808B0324(BgSpot08Bakudankabe* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 i; + Vec3f burstDepthY; + Vec3f burstDepthX; + f32 sinY; + f32 cosY; + + sinY = Math_SinS(this->dyna.actor.shape.rot.y); + cosY = Math_CosS(this->dyna.actor.shape.rot.y); + + burstDepthX.z = 0.0f; + burstDepthX.x = 0.0f; + + for (i = 0; i < 24; i++) { + s16 gravityInfluence; + s16 scale; + f32 temp1; + f32 temp2; + s32 rotationSpeed; + + temp1 = (Rand_ZeroOne() - 0.5f) * 440.0f; + temp2 = (Rand_ZeroOne() - 0.5f) * 20.0f; + burstDepthY.x = this->dyna.actor.world.pos.x + temp2 * sinY + (temp1 * cosY); + burstDepthY.y = (this->dyna.actor.world.pos.y + 20.0f) + (i * (65.0f / 12.0f)); + burstDepthY.z = this->dyna.actor.world.pos.z + temp2 * cosY - (temp1 * sinY); + + burstDepthX.y = (Rand_ZeroOne() - 0.2f) * 12.0f; + scale = Rand_ZeroOne() * 75.0f + 10.0f; + + if (scale < 25) { + gravityInfluence = -300; + } else if (scale < 50) { + gravityInfluence = -360; + } else { + gravityInfluence = -420; + } + + if (Rand_ZeroOne() < 0.4f) { + rotationSpeed = 65; + } else { + rotationSpeed = 33; + } + + EffectSsKakera_Spawn(globalCtx, &burstDepthY, &burstDepthX, &burstDepthY, gravityInfluence, rotationSpeed, 0x1E, + 4, 0, scale, 1, 3, 80, KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_FIELD_KEEP, gFieldKakeraDL); + } + + for (i = 0; i < ARRAY_COUNT(D_808B08AC); i++) { + burstDepthY.x = this->dyna.actor.world.pos.x + D_808B08AC[i].z * sinY + D_808B08AC[i].x * cosY; + burstDepthY.y = this->dyna.actor.world.pos.y + D_808B08AC[i].y; + burstDepthY.z = this->dyna.actor.world.pos.z + D_808B08AC[i].z * cosY - (D_808B08AC[i].x * sinY); + func_80033480(globalCtx, &burstDepthY, 120.0f, 4, 0x78, 0xA0, 1); + } +} + +void BgSpot08Bakudankabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Bakudankabe* this = (BgSpot08Bakudankabe*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params & 0x3F))) { + Actor_Kill(&this->dyna.actor); + return; + } + func_808B02D0(this, globalCtx); + CollisionHeader_GetVirtual(&gZorasFountainBombableWallCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); +} + +void BgSpot08Bakudankabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Bakudankabe* this = (BgSpot08Bakudankabe*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BgSpot08Bakudankabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Bakudankabe* this = (BgSpot08Bakudankabe*)thisx; + + if (this->collider.base.acFlags & AC_HIT) { + func_808B0324(this, globalCtx); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params & 0x3F)); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 40, NA_SE_EV_WALL_BROKEN); + func_80078884(NA_SE_SY_CORRECT_CHIME); + Actor_Kill(&this->dyna.actor); + } else if (this->dyna.actor.xzDistToPlayer < 800.0f) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void BgSpot08Bakudankabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Bakudankabe* this = (BgSpot08Bakudankabe*)thisx; + + Collider_UpdateSpheres(0, &this->collider); + Collider_UpdateSpheres(1, &this->collider); + Collider_UpdateSpheres(2, &this->collider); + Gfx_DrawDListOpa(globalCtx, gZorasFountainBombableWallDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot08_Bakudankabe/z_bg_spot08_bakudankabe.h b/soh/src/overlays/actors/ovl_Bg_Spot08_Bakudankabe/z_bg_spot08_bakudankabe.h new file mode 100644 index 000000000..3be139505 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot08_Bakudankabe/z_bg_spot08_bakudankabe.h @@ -0,0 +1,15 @@ +#ifndef Z_BG_SPOT08_BAKUDANKABE_H +#define Z_BG_SPOT08_BAKUDANKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot08Bakudankabe; + +typedef struct BgSpot08Bakudankabe { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderJntSph collider; + /* 0x0184 */ ColliderJntSphElement colliderItems[3]; +} BgSpot08Bakudankabe; // size = 0x0244 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot08_Iceblock/z_bg_spot08_iceblock.c b/soh/src/overlays/actors/ovl_Bg_Spot08_Iceblock/z_bg_spot08_iceblock.c new file mode 100644 index 000000000..eeb778f53 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot08_Iceblock/z_bg_spot08_iceblock.c @@ -0,0 +1,447 @@ +/* + * File: z_bg_spot08_iceblock.c + * Overlay: ovl_Bg_Spot08_Iceblock + * Description: Floating ice platforms + */ + +#include "z_bg_spot08_iceblock.h" +#include "objects/object_spot08_obj/object_spot08_obj.h" + +#define FLAGS 0 + +void BgSpot08Iceblock_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot08Iceblock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot08Iceblock_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot08Iceblock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgSpot08Iceblock_SetupFloatNonrotating(BgSpot08Iceblock* this); +void BgSpot08Iceblock_FloatNonrotating(BgSpot08Iceblock* this, GlobalContext* globalCtx); +void BgSpot08Iceblock_SetupFloatRotating(BgSpot08Iceblock* this); +void BgSpot08Iceblock_FloatRotating(BgSpot08Iceblock* this, GlobalContext* globalCtx); +void BgSpot08Iceblock_SetupFloatOrbitingTwins(BgSpot08Iceblock* this); +void BgSpot08Iceblock_FloatOrbitingTwins(BgSpot08Iceblock* this, GlobalContext* globalCtx); +void BgSpot08Iceblock_SetupNoAction(BgSpot08Iceblock* this); + +const ActorInit Bg_Spot08_Iceblock_InitVars = { + ACTOR_BG_SPOT08_ICEBLOCK, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT08_OBJ, + sizeof(BgSpot08Iceblock), + (ActorFunc)BgSpot08Iceblock_Init, + (ActorFunc)BgSpot08Iceblock_Destroy, + (ActorFunc)BgSpot08Iceblock_Update, + (ActorFunc)BgSpot08Iceblock_Draw, + NULL, +}; + +void BgSpot08Iceblock_SetupAction(BgSpot08Iceblock* this, BgSpot08IceblockActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgSpot08Iceblock_InitDynaPoly(BgSpot08Iceblock* this, GlobalContext* globalCtx, CollisionHeader* collision, + s32 flags) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flags); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning: move BG registration failed" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_spot08_iceblock.c", 0xD9, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +// Sets params to 0x10 (medium, nonrotating) if not in the cases listed. +void BgSpot08Iceblock_CheckParams(BgSpot08Iceblock* this) { + switch (this->dyna.actor.params & 0xFF) { + case 0xFF: + this->dyna.actor.params = 0x10; + break; + default: + // "Error: arg_data setting error" + osSyncPrintf("Error : arg_data 設定ミスです。(%s %d)(arg_data 0x%04x)\n", "../z_bg_spot08_iceblock.c", 0xF6, + this->dyna.actor.params); + this->dyna.actor.params = 0x10; + break; + case 1: + case 4: + case 0x10: + case 0x11: + case 0x12: + case 0x14: + case 0x20: + case 0x23: + case 0x24: + break; + } +} + +void BgSpot08Iceblock_Bobbing(BgSpot08Iceblock* this) { + this->bobOffset = (Math_SinS(this->bobPhaseSlow) * 4.0f) + (Math_SinS(this->bobPhaseFast) * 3.0f); +} + +void BgSpot08Iceblock_SinkUnderPlayer(BgSpot08Iceblock* this) { + f32 target; + f32 step; + + switch (this->dyna.actor.params & 0xF0) { + case 0: + step = 0.15f; + break; + case 0x10: + step = 0.2f; + break; + case 0x20: + step = 0.4f; + break; + } + + // Sink under Player's weight if standing on it + target = (func_80043548(&this->dyna) ? -4.0f : 0.0f); + + Math_StepToF(&this->sinkOffset, target, step); +} + +void BgSpot08Iceblock_SetWaterline(BgSpot08Iceblock* this) { + this->dyna.actor.world.pos.y = this->sinkOffset + this->bobOffset + this->dyna.actor.home.pos.y; +} + +void BgSpot08Iceblock_MultVectorScalar(Vec3f* dest, Vec3f* v, f32 scale) { + dest->x = v->x * scale; + dest->y = v->y * scale; + dest->z = v->z * scale; +} + +void BgSpot08Iceblock_CrossProduct(Vec3f* dest, Vec3f* v1, Vec3f* v2) { + dest->x = (v1->y * v2->z) - (v1->z * v2->y); + dest->y = (v1->z * v2->x) - (v1->x * v2->z); + dest->z = (v1->x * v2->y) - (v1->y * v2->x); +} + +s32 BgSpot08Iceblock_NormalizeVector(Vec3f* dest, Vec3f* v) { + f32 magnitude; + + magnitude = Math3D_Vec3fMagnitude(v); + if (magnitude < 0.001f) { + dest->x = dest->y = 0.0f; + dest->z = 1.0f; + return false; + } else { + dest->x = v->x * (1.0f / magnitude); + dest->y = v->y * (1.0f / magnitude); + dest->z = v->z * (1.0f / magnitude); + return true; + } +} + +static Vec3f sVerticalVector = { 0.0f, 1.0f, 0.0f }; +static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f }; +static f32 sInertias[] = { 1.0f / 70000000, 1.0f / 175000000, 1.0f / 700000000 }; +static f32 sDampingFactors[] = { 0.96f, 0.96f, 0.98f }; + +static f32 sRollSins[] = { + 0.22495104f, // sin(13 degrees) + 0.22495104f, // sin(13 degrees) + 0.03489947f, // sin(2 degrees) +}; + +static f32 sRollCoss[] = { + 0.97437006f, // cos(13 degrees) + 0.97437006f, // cos(13 degrees) + 0.99939084f, // cos(2 degrees) +}; + +/** + * Handles all the factors that influence rolling: inertia, random oscillations, and most significantly, player weight, + * and combines them to produce a matrix that rotates the actor to match the surface normal + */ +void BgSpot08Iceblock_Roll(BgSpot08Iceblock* this, GlobalContext* globalCtx) { + f32 deviationFromVertSq; + f32 stabilityCorrection; + Vec3f surfaceNormalHorizontal; + Vec3f playerCentroidDiff; + Vec3f playerMoment; + Vec3f surfaceNormalHorizontalScaled; + Vec3f randomNutation; + Vec3f tempVec; // reused with different meanings + Vec3f torqueDirection; + f32 playerCentroidDist; + s32 rollDataIndex; + MtxF mtx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + switch (this->dyna.actor.params & 0xFF) { + case 0x11: // Medium nonrotating + rollDataIndex = 0; + break; + case 1: + rollDataIndex = 1; // Large nonrotating + break; + default: + rollDataIndex = 2; + break; + } + + Math_Vec3f_Diff(&player->actor.world.pos, &this->dyna.actor.world.pos, &playerCentroidDiff); + playerCentroidDiff.y -= (150.0f * this->dyna.actor.scale.y); + playerCentroidDist = Math3D_Vec3fMagnitude(&playerCentroidDiff); + + randomNutation.x = (Rand_ZeroOne() - 0.5f) * (1.0f / 625); + randomNutation.y = 0.0f; + randomNutation.z = (Rand_ZeroOne() - 0.5f) * (1.0f / 625); + + surfaceNormalHorizontal.x = this->surfaceNormal.x; + surfaceNormalHorizontal.y = 0.0f; + surfaceNormalHorizontal.z = this->surfaceNormal.z; + + // If player is standing on it or holding the edge + if (func_8004356C(&this->dyna) && (playerCentroidDist > 3.0f)) { + Math_Vec3f_Diff(&playerCentroidDiff, &surfaceNormalHorizontal, &playerMoment); + BgSpot08Iceblock_MultVectorScalar(&playerMoment, &playerMoment, + (sInertias[rollDataIndex] * playerCentroidDist) / this->dyna.actor.scale.x); + } else { + playerMoment = sZeroVector; + } + + BgSpot08Iceblock_MultVectorScalar(&surfaceNormalHorizontalScaled, &surfaceNormalHorizontal, -0.01f); + + // Add all three deviations + Math_Vec3f_Sum(&this->normalDelta, &playerMoment, &this->normalDelta); + Math_Vec3f_Sum(&this->normalDelta, &surfaceNormalHorizontalScaled, &this->normalDelta); + Math_Vec3f_Sum(&this->normalDelta, &randomNutation, &this->normalDelta); + + this->normalDelta.y = 0.0f; + + Math_Vec3f_Sum(&this->surfaceNormal, &this->normalDelta, &tempVec); + + tempVec.x *= sDampingFactors[rollDataIndex]; + tempVec.z *= sDampingFactors[rollDataIndex]; + + // Set up roll axis and final new angle + if (BgSpot08Iceblock_NormalizeVector(&this->surfaceNormal, &tempVec)) { + deviationFromVertSq = Math3D_Dist1DSq(this->surfaceNormal.z, this->surfaceNormal.x); + + // Prevent overrolling + if (sRollSins[rollDataIndex] < deviationFromVertSq) { + stabilityCorrection = sRollSins[rollDataIndex] / deviationFromVertSq; + + this->surfaceNormal.x *= stabilityCorrection; + this->surfaceNormal.y = sRollCoss[rollDataIndex]; + this->surfaceNormal.z *= stabilityCorrection; + } + + BgSpot08Iceblock_CrossProduct(&tempVec, &sVerticalVector, &this->surfaceNormal); + + if (BgSpot08Iceblock_NormalizeVector(&torqueDirection, &tempVec)) { + this->rotationAxis = torqueDirection; + } + } else { + this->surfaceNormal = sVerticalVector; + } + + // Rotation by the angle between surfaceNormal and the vertical about rotationAxis + Matrix_RotateAxis(Math_FAcosF(Math3D_Cos(&sVerticalVector, &this->surfaceNormal)), &this->rotationAxis, + MTXMODE_NEW); + Matrix_RotateY(this->dyna.actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Get(&mtx); + Matrix_MtxFToYXZRotS(&mtx, &this->dyna.actor.shape.rot, 0); +} + +void BgSpot08Iceblock_SpawnTwinFloe(BgSpot08Iceblock* this, GlobalContext* globalCtx) { + s32 pad[2]; + f32 sin; + f32 cos; + + sin = Math_SinS(this->dyna.actor.home.rot.y) * 100.0f; + cos = Math_CosS(this->dyna.actor.home.rot.y) * 100.0f; + + if (!(this->dyna.actor.params & 0x100)) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_SPOT08_ICEBLOCK, + this->dyna.actor.home.pos.x, this->dyna.actor.home.pos.y, this->dyna.actor.home.pos.z, + this->dyna.actor.home.rot.x, this->dyna.actor.home.rot.y, this->dyna.actor.home.rot.z, + 0x123); + + this->dyna.actor.world.pos.x += sin; + this->dyna.actor.world.pos.z += cos; + } else { + this->dyna.actor.world.pos.x -= sin; + this->dyna.actor.world.pos.z -= cos; + } + BgSpot08Iceblock_SetupFloatOrbitingTwins(this); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 3000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2200, ICHAIN_STOP), +}; + +void BgSpot08Iceblock_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Iceblock* this = (BgSpot08Iceblock*)thisx; + CollisionHeader* colHeader; + + // "spot08 ice floe" + osSyncPrintf("(spot08 流氷)(arg_data 0x%04x)\n", this->dyna.actor.params); + BgSpot08Iceblock_CheckParams(this); + + switch (this->dyna.actor.params & 0x200) { + case 0: + colHeader = &gZorasFountainIcebergCol; + break; + case 0x200: + colHeader = &gZorasFountainIceRampCol; + break; + } + + switch (this->dyna.actor.params & 0xF) { + case 2: + case 3: + BgSpot08Iceblock_InitDynaPoly(this, globalCtx, colHeader, DPM_UNK3); + break; + default: + BgSpot08Iceblock_InitDynaPoly(this, globalCtx, colHeader, DPM_UNK); + break; + } + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + Actor_Kill(&this->dyna.actor); + return; + } + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + switch (this->dyna.actor.params & 0xF0) { + case 0: + Actor_SetScale(&this->dyna.actor, 0.2f); + break; + case 0x10: + Actor_SetScale(&this->dyna.actor, 0.1f); + break; + case 0x20: + Actor_SetScale(&this->dyna.actor, 0.05f); + break; + } + + this->bobPhaseSlow = (s32)(Rand_ZeroOne() * (0xFFFF + 0.5f)); + this->bobPhaseFast = (s32)(Rand_ZeroOne() * (0xFFFF + 0.5f)); + this->surfaceNormal.y = 1.0f; + this->rotationAxis.x = 1.0f; + + switch (this->dyna.actor.params & 0xF) { + case 0: + case 1: + BgSpot08Iceblock_SetupFloatNonrotating(this); + break; + case 2: + BgSpot08Iceblock_SetupFloatRotating(this); + break; + case 3: + BgSpot08Iceblock_SpawnTwinFloe(this, globalCtx); + break; + case 4: + BgSpot08Iceblock_SetupNoAction(this); + break; + } +} + +void BgSpot08Iceblock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Iceblock* this = (BgSpot08Iceblock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgSpot08Iceblock_SetupFloatNonrotating(BgSpot08Iceblock* this) { + BgSpot08Iceblock_SetupAction(this, BgSpot08Iceblock_FloatNonrotating); +} + +void BgSpot08Iceblock_FloatNonrotating(BgSpot08Iceblock* this, GlobalContext* globalCtx) { + BgSpot08Iceblock_Bobbing(this); + BgSpot08Iceblock_SinkUnderPlayer(this); + BgSpot08Iceblock_SetWaterline(this); + this->dyna.actor.shape.rot.y = this->dyna.actor.home.rot.y; + BgSpot08Iceblock_Roll(this, globalCtx); +} + +void BgSpot08Iceblock_SetupFloatRotating(BgSpot08Iceblock* this) { + BgSpot08Iceblock_SetupAction(this, BgSpot08Iceblock_FloatRotating); +} + +void BgSpot08Iceblock_FloatRotating(BgSpot08Iceblock* this, GlobalContext* globalCtx) { + BgSpot08Iceblock_Bobbing(this); + BgSpot08Iceblock_SinkUnderPlayer(this); + BgSpot08Iceblock_SetWaterline(this); + this->dyna.actor.world.rot.y = this->dyna.actor.world.rot.y + 0x190; + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; + BgSpot08Iceblock_Roll(this, globalCtx); +} + +void BgSpot08Iceblock_SetupFloatOrbitingTwins(BgSpot08Iceblock* this) { + BgSpot08Iceblock_SetupAction(this, BgSpot08Iceblock_FloatOrbitingTwins); +} + +void BgSpot08Iceblock_FloatOrbitingTwins(BgSpot08Iceblock* this, GlobalContext* globalCtx) { + f32 cos; + f32 sin; + + BgSpot08Iceblock_Bobbing(this); + BgSpot08Iceblock_SinkUnderPlayer(this); + BgSpot08Iceblock_SetWaterline(this); + + // parent handles rotations of both + if (!(this->dyna.actor.params & 0x100)) { + this->dyna.actor.world.rot.y += 0x190; + sin = Math_SinS(this->dyna.actor.world.rot.y) * 100.0f; + cos = Math_CosS(this->dyna.actor.world.rot.y) * 100.0f; + + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x + sin; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z + cos; + + if (this->dyna.actor.child != NULL) { + this->dyna.actor.child->world.pos.x = this->dyna.actor.home.pos.x - sin; + this->dyna.actor.child->world.pos.z = this->dyna.actor.home.pos.z - cos; + } + } + + this->dyna.actor.shape.rot.y = this->dyna.actor.home.rot.y; + BgSpot08Iceblock_Roll(this, globalCtx); +} + +void BgSpot08Iceblock_SetupNoAction(BgSpot08Iceblock* this) { + BgSpot08Iceblock_SetupAction(this, NULL); +} + +void BgSpot08Iceblock_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot08Iceblock* this = (BgSpot08Iceblock*)thisx; + + if (Rand_ZeroOne() < 0.05f) { + this->bobIncrSlow = Rand_S16Offset(300, 100); + this->bobIncrFast = Rand_S16Offset(800, 400); + } + + this->bobPhaseSlow += this->bobIncrSlow; + this->bobPhaseFast += this->bobIncrFast; + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgSpot08Iceblock_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx* dList; + BgSpot08Iceblock* this = (BgSpot08Iceblock*)thisx; + + switch (this->dyna.actor.params & 0x200) { + case 0: + dList = gZorasFountainIcebergDL; + break; + case 0x200: + dList = gZorasFountainIceRampDL; + break; + } + + Gfx_DrawDListOpa(globalCtx, dList); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot08_Iceblock/z_bg_spot08_iceblock.h b/soh/src/overlays/actors/ovl_Bg_Spot08_Iceblock/z_bg_spot08_iceblock.h new file mode 100644 index 000000000..2fc0b2a72 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot08_Iceblock/z_bg_spot08_iceblock.h @@ -0,0 +1,52 @@ +#ifndef Z_BG_SPOT08_ICEBLOCK_H +#define Z_BG_SPOT08_ICEBLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot08Iceblock; + +typedef void (*BgSpot08IceblockActionFunc)(struct BgSpot08Iceblock*, GlobalContext*); + +typedef struct BgSpot08Iceblock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot08IceblockActionFunc actionFunc; + /* 0x0168 */ Vec3f surfaceNormal; + /* 0x0170 */ Vec3f normalDelta; // y always 0 + /* 0x0180 */ Vec3f rotationAxis; + /* 0x018C */ s16 bobPhaseSlow; + /* 0x018E */ s16 bobPhaseFast; + /* 0x0190 */ s16 bobIncrSlow; + /* 0x0192 */ s16 bobIncrFast; + /* 0x0194 */ f32 sinkOffset; + /* 0x0198 */ f32 bobOffset; +} BgSpot08Iceblock; // size = 0x019C + +// Params +/** + * 0x200: Shape + * 0x200 is the ice ramp where Jabu is as child + */ + +/** + * 0x100: Twins + * 0x100 is the spawned twin platform + */ + +/** + * 0xF0: Size + * 0x00 is large + * 0x10 is medium + * 0x20 is small + */ + +/** + * 0xF: Action + * 0 Floating, nonrotating + * 1 same as 0, but does not roll as much (?) + * 2 Floating, rotating + * 3 Floating orbiting twins + * 4 Completely static, does nothing + */ + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot09_Obj/z_bg_spot09_obj.c b/soh/src/overlays/actors/ovl_Bg_Spot09_Obj/z_bg_spot09_obj.c new file mode 100644 index 000000000..0347d4f66 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot09_Obj/z_bg_spot09_obj.c @@ -0,0 +1,182 @@ +/* + * File: z_bg_spot09_obj.c + * Overlay: ovl_Bg_Spot09_Obj + * Description: + */ + +#include "z_bg_spot09_obj.h" +#include "objects/object_spot09_obj/object_spot09_obj.h" + +#define FLAGS 0 + +void BgSpot09Obj_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot09Obj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot09Obj_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot09Obj_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 func_808B1AE0(BgSpot09Obj* this, GlobalContext* globalCtx); +s32 func_808B1BA0(BgSpot09Obj* this, GlobalContext* globalCtx); +s32 func_808B1BEC(BgSpot09Obj* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot09_Obj_InitVars = { + ACTOR_BG_SPOT09_OBJ, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT09_OBJ, + sizeof(BgSpot09Obj), + (ActorFunc)BgSpot09Obj_Init, + (ActorFunc)BgSpot09Obj_Destroy, + (ActorFunc)BgSpot09Obj_Update, + (ActorFunc)BgSpot09Obj_Draw, + NULL, +}; + +static CollisionHeader* D_808B1F90[] = { + NULL, &gValleyObjects1Col, &gValleyObjects2Col, &gValleyObjects3Col, &gValleyObjects4Col, +}; + +static s32 (*D_808B1FA4[])(BgSpot09Obj* this, GlobalContext* globalCtx) = { + func_808B1BEC, + func_808B1AE0, + func_808B1BA0, +}; + +static InitChainEntry sInitChain1[] = { + ICHAIN_F32(uncullZoneForward, 7200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 3000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 7200, ICHAIN_STOP), +}; + +static InitChainEntry sInitChain2[] = { + ICHAIN_F32(uncullZoneForward, 7200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP), +}; + +static Gfx* sDLists[] = { + gValleyBridgeSidesDL, gValleyBrokenBridgeDL, gValleyBridgeChildDL, gCarpentersTentDL, gValleyRepairedBridgeDL, +}; + +s32 func_808B1AE0(BgSpot09Obj* this, GlobalContext* globalCtx) { + s32 carpentersRescued; + + if (gSaveContext.sceneSetupIndex >= 4) { + return this->dyna.actor.params == 0; + } + + carpentersRescued = (gSaveContext.eventChkInf[9] & 0xF) == 0xF; + + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + switch (this->dyna.actor.params) { + case 0: + return 0; + case 1: + return !carpentersRescued; + case 4: + return carpentersRescued; + case 3: + return 1; + } + } else { + return this->dyna.actor.params == 2; + } + + return 0; +} + +s32 func_808B1BA0(BgSpot09Obj* this, GlobalContext* globalCtx) { + if (this->dyna.actor.params == 3) { + Actor_SetScale(&this->dyna.actor, 0.1f); + } else { + Actor_SetScale(&this->dyna.actor, 1.0f); + } + return 1; +} + +s32 func_808B1BEC(BgSpot09Obj* this, GlobalContext* globalCtx) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2[2]; + + if (D_808B1F90[this->dyna.actor.params] != NULL) { + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(D_808B1F90[this->dyna.actor.params], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } + return true; +} + +s32 func_808B1C70(BgSpot09Obj* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(D_808B1FA4); i++) { + if (!D_808B1FA4[i](this, globalCtx)) { + return false; + } + } + return true; +} + +s32 func_808B1CEC(BgSpot09Obj* this, GlobalContext* globalCtx) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain1); + return true; +} + +s32 func_808B1D18(BgSpot09Obj* this, GlobalContext* globalCtx) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain2); + return true; +} + +s32 func_808B1D44(BgSpot09Obj* this, GlobalContext* globalCtx) { + if (this->dyna.actor.params == 3) { + return func_808B1D18(this, globalCtx); + } else { + return func_808B1CEC(this, globalCtx); + } +} + +void BgSpot09Obj_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot09Obj* this = (BgSpot09Obj*)thisx; + + osSyncPrintf("Spot09 Object [arg_data : 0x%04x](大工救出フラグ 0x%x)\n", this->dyna.actor.params, + gSaveContext.eventChkInf[9] & 0xF); + this->dyna.actor.params &= 0xFF; + if ((this->dyna.actor.params < 0) || (this->dyna.actor.params >= 5)) { + osSyncPrintf("Error : Spot 09 object の arg_data が判別出来ない(%s %d)(arg_data 0x%04x)\n", + "../z_bg_spot09_obj.c", 322, this->dyna.actor.params); + } + + if (!func_808B1C70(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } else if (!func_808B1D44(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgSpot09Obj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DynaCollisionContext* dynaColCtx = &globalCtx->colCtx.dyna; + BgSpot09Obj* this = (BgSpot09Obj*)thisx; + + if (this->dyna.actor.params != 0) { + DynaPoly_DeleteBgActor(globalCtx, dynaColCtx, this->dyna.bgId); + } +} + +void BgSpot09Obj_Update(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgSpot09Obj_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, sDLists[thisx->params]); + + if (thisx->params == 3) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot09_obj.c", 388); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot09_obj.c", 391), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCarpentersTentEntranceDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot09_obj.c", 396); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot09_Obj/z_bg_spot09_obj.h b/soh/src/overlays/actors/ovl_Bg_Spot09_Obj/z_bg_spot09_obj.h new file mode 100644 index 000000000..a09ce32ed --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot09_Obj/z_bg_spot09_obj.h @@ -0,0 +1,14 @@ +#ifndef Z_BG_SPOT09_OBJ_H +#define Z_BG_SPOT09_OBJ_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot09Obj; + +typedef struct BgSpot09Obj { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ char unk_164[0x04]; +} BgSpot09Obj; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot11_Bakudankabe/z_bg_spot11_bakudankabe.c b/soh/src/overlays/actors/ovl_Bg_Spot11_Bakudankabe/z_bg_spot11_bakudankabe.c new file mode 100644 index 000000000..f24b35019 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot11_Bakudankabe/z_bg_spot11_bakudankabe.c @@ -0,0 +1,153 @@ +/* + * File: z_bg_spot11_bakudankabe.c + * Overlay: ovl_Bg_Spot11_Bakudankabe + * Description: Destructible Wall (Desert Colossus) + */ + +#include "z_bg_spot11_bakudankabe.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_spot11_obj/object_spot11_obj.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSpot11Bakudankabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot11Bakudankabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot11Bakudankabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot11Bakudankabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot11_Bakudankabe_InitVars = { + ACTOR_BG_SPOT11_BAKUDANKABE, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT11_OBJ, + sizeof(BgSpot11Bakudankabe), + (ActorFunc)BgSpot11Bakudankabe_Init, + (ActorFunc)BgSpot11Bakudankabe_Destroy, + (ActorFunc)BgSpot11Bakudankabe_Update, + (ActorFunc)BgSpot11Bakudankabe_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 40, 80, 0, { 2259, 108, -1580 } }, +}; + +static Vec3f D_808B272C = { 2259.0f, 108.0f, -1550.0f }; +static Vec3f D_808B2738 = { 2259.0f, 108.0f, -1550.0f }; + +void func_808B2180(BgSpot11Bakudankabe* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + this->collider.dim.pos.x += (s16)this->dyna.actor.world.pos.x; + this->collider.dim.pos.y += (s16)this->dyna.actor.world.pos.y; + this->collider.dim.pos.z += (s16)this->dyna.actor.world.pos.z; +} + +void func_808B2218(BgSpot11Bakudankabe* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + Vec3f burstDepthY; + Vec3f burstDepthX; + s32 i; + + burstDepthX.z = 0; + burstDepthX.x = 0; + + for (i = 0; i < 20; i++) { + s16 scale; + s32 gravityInfluence; + s32 rotationSpeed; + + Math_Vec3f_Sum(&thisx->world.pos, &D_808B272C, &burstDepthY); + + burstDepthY.x += (Rand_ZeroOne() - 0.5f) * 120.0f; + burstDepthY.y += (30.0f + (i * 6.5f)); + burstDepthY.z += (Rand_ZeroOne() - 0.5f) * 20.0f; + + burstDepthX.y = (Rand_ZeroOne() - 0.2f) * 12.0f; + scale = (Rand_ZeroOne() * 55.0f) + 8.0f; + + if (scale < 20) { + gravityInfluence = -300; + } else if (scale < 35) { + gravityInfluence = -360; + } else { + gravityInfluence = -420; + } + if (Rand_ZeroOne() < 0.4f) { + rotationSpeed = 65; + } else { + rotationSpeed = 33; + } + EffectSsKakera_Spawn(globalCtx, &burstDepthY, &burstDepthX, &burstDepthY, gravityInfluence, rotationSpeed, 0x1E, + 4, 0, scale, 1, 3, 80, KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_FIELD_KEEP, gFieldKakeraDL); + } + Math_Vec3f_Sum(&thisx->world.pos, &D_808B272C, &burstDepthY); + func_80033480(globalCtx, &burstDepthY, 70, 4, 110, 160, 1); + burstDepthY.y += 40; + func_80033480(globalCtx, &burstDepthY, 70, 5, 110, 160, 1); + burstDepthY.y += 40; + func_80033480(globalCtx, &burstDepthY, 70, 4, 110, 160, 1); +} + +void BgSpot11Bakudankabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot11Bakudankabe* this = (BgSpot11Bakudankabe*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params & 0x3F))) { + Actor_Kill(&this->dyna.actor); + return; + } + func_808B2180(this, globalCtx); + CollisionHeader_GetVirtual(&gDesertColossusBombableWallCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_SetScale(&this->dyna.actor, 1.0f); + osSyncPrintf("(spot11 爆弾壁)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgSpot11Bakudankabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot11Bakudankabe* this = (BgSpot11Bakudankabe*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void BgSpot11Bakudankabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot11Bakudankabe* this = (BgSpot11Bakudankabe*)thisx; + + if (this->collider.base.acFlags & AC_HIT) { + func_808B2218(this, globalCtx); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params & 0x3F)); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &D_808B2738, 40, NA_SE_EV_WALL_BROKEN); + func_80078884(NA_SE_SY_CORRECT_CHIME); + Actor_Kill(&this->dyna.actor); + return; + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void BgSpot11Bakudankabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot11Bakudankabe* this = (BgSpot11Bakudankabe*)thisx; + + Gfx_DrawDListOpa(globalCtx, gDesertColossusBombableWallDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot11_Bakudankabe/z_bg_spot11_bakudankabe.h b/soh/src/overlays/actors/ovl_Bg_Spot11_Bakudankabe/z_bg_spot11_bakudankabe.h new file mode 100644 index 000000000..b18f5c084 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot11_Bakudankabe/z_bg_spot11_bakudankabe.h @@ -0,0 +1,14 @@ +#ifndef Z_BG_SPOT11_BAKUDANKABE_H +#define Z_BG_SPOT11_BAKUDANKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot11Bakudankabe; + +typedef struct BgSpot11Bakudankabe { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderCylinder collider; +} BgSpot11Bakudankabe; // size = 0x01B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot11_Oasis/z_bg_spot11_oasis.c b/soh/src/overlays/actors/ovl_Bg_Spot11_Oasis/z_bg_spot11_oasis.c new file mode 100644 index 000000000..90627853d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot11_Oasis/z_bg_spot11_oasis.c @@ -0,0 +1,163 @@ +/* + * File: z_bg_spot11_oasis.c + * Overlay: ovl_Bg_Spot11_Oasis + * Description: Refilling Oasis (Desert Colossus) + */ + +#include "z_bg_spot11_oasis.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/object_spot11_obj/object_spot11_obj.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot11Oasis_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot11Oasis_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot11Oasis_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_808B2970(BgSpot11Oasis* this); +void func_808B2980(BgSpot11Oasis* this, GlobalContext* globalCtx); +void func_808B29E0(BgSpot11Oasis* this); +void func_808B29F0(BgSpot11Oasis* this, GlobalContext* globalCtx); +void func_808B2AA8(BgSpot11Oasis* this); +void func_808B2AB8(BgSpot11Oasis* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot11_Oasis_InitVars = { + ACTOR_BG_SPOT11_OASIS, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT11_OBJ, + sizeof(BgSpot11Oasis), + (ActorFunc)BgSpot11Oasis_Init, + (ActorFunc)Actor_Noop, + (ActorFunc)BgSpot11Oasis_Update, + NULL, + NULL, +}; + +static s16 D_808B2E10[][2] = { + { 1260, 2040 }, { 1259, 1947 }, { 1135, 1860 }, { 1087, 1912 }, { 1173, 2044 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 3000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static Vec3f D_808B2E34[] = { + { 0.0f, -100.0f, 0.0f }, { 100.0f, -80.0f, -50.0f }, { -50.0f, -80.0f, -100.0f }, + { -75.0f, -90.0f, 90.0f }, { 30.0f, -100.0f, 40.0f }, +}; + +void func_808B27F0(GlobalContext* globalCtx, s16 waterSurface) { + WaterBox* waterBox = &globalCtx->colCtx.colHeader->waterBoxes[0]; + + waterBox->ySurface = waterSurface; +} + +s32 func_808B280C(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f sp58; + Vec3f sp4C; + Vec3f sp40; + s32 i; + + sp58.x = D_808B2E10[0][0]; + sp58.z = D_808B2E10[0][1]; + sp58.y = 0.0f; + + sp4C.y = 0.0f; + sp40.y = 0.0f; + + for (i = 1; i < ARRAY_COUNT(D_808B2E10) - 1; i++) { + sp4C.x = D_808B2E10[i][0]; + sp4C.z = D_808B2E10[i][1]; + sp40.x = D_808B2E10[i + 1][0]; + sp40.z = D_808B2E10[i + 1][1]; + if (Math3D_TriChkPointParaYSlopedY(&sp58, &sp4C, &sp40, player->actor.world.pos.z, player->actor.world.pos.x)) { + return 1; + } + } + return 0; +} + +void BgSpot11Oasis_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot11Oasis* this = (BgSpot11Oasis*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + func_808B2970(this); + this->actor.world.pos.y = -100.0f; + func_808B27F0(globalCtx, -100); +} + +void func_808B2970(BgSpot11Oasis* this) { + this->actionFunc = func_808B2980; +} + +void func_808B2980(BgSpot11Oasis* this, GlobalContext* globalCtx) { + if (Flags_GetEnv(globalCtx, 5) && func_808B280C(globalCtx)) { + OnePointCutscene_Init(globalCtx, 4150, -99, &this->actor, MAIN_CAM); + func_808B29E0(this); + } +} + +void func_808B29E0(BgSpot11Oasis* this) { + this->actionFunc = func_808B29F0; +} + +void func_808B29F0(BgSpot11Oasis* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->actor.world.pos.y, 0.0f, 0.7f)) { + func_808B2AA8(this); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y + 40.0f, this->actor.world.pos.z, 0, 0, 0, FAIRY_SPAWNER); + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + func_808B27F0(globalCtx, this->actor.world.pos.y); +} + +void func_808B2AA8(BgSpot11Oasis* this) { + this->actionFunc = func_808B2AB8; +} + +void func_808B2AB8(BgSpot11Oasis* this, GlobalContext* globalCtx) { +} + +void BgSpot11Oasis_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot11Oasis* this = (BgSpot11Oasis*)thisx; + s32 pad; + u32 gameplayFrames; + Vec3f sp30; + + this->actionFunc(this, globalCtx); + if (this->actionFunc == func_808B2980) { + this->actor.draw = NULL; + return; + } + this->actor.draw = BgSpot11Oasis_Draw; + if (this->unk_150 && (this->actor.projectedPos.z < 400.0f) && (this->actor.projectedPos.z > -40.0f)) { + gameplayFrames = globalCtx->gameplayFrames; + if (gameplayFrames & 4) { + Math_Vec3f_Sum(&this->actor.world.pos, &D_808B2E34[this->unk_151], &sp30); + EffectSsBubble_Spawn(globalCtx, &sp30, 0.0f, 15.0f, 50.0f, (Rand_ZeroOne() * 0.12f) + 0.02f); + if (Rand_ZeroOne() < 0.3f) { + this->unk_151 = Rand_ZeroOne() * 4.9f; + } + } + } else { + this->unk_150 = 1; + } +} + +void BgSpot11Oasis_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 gameplayFrames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot11_oasis.c", 327); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot11_oasis.c", 331), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 127 - (gameplayFrames % 128), (gameplayFrames * 1) % 128, + 32, 32, 1, gameplayFrames % 128, (gameplayFrames * 1) % 128, 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, gDesertColossusOasisDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot11_oasis.c", 346); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot11_Oasis/z_bg_spot11_oasis.h b/soh/src/overlays/actors/ovl_Bg_Spot11_Oasis/z_bg_spot11_oasis.h new file mode 100644 index 000000000..a1e18e87a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot11_Oasis/z_bg_spot11_oasis.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_SPOT11_OASIS_H +#define Z_BG_SPOT11_OASIS_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot11Oasis; + +typedef void (*BgSpot11OasisActionFunc)(struct BgSpot11Oasis*, GlobalContext*); + +typedef struct BgSpot11Oasis { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgSpot11OasisActionFunc actionFunc; + /* 0x0150 */ u8 unk_150; + /* 0x0151 */ u8 unk_151; +} BgSpot11Oasis; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot12_Gate/z_bg_spot12_gate.c b/soh/src/overlays/actors/ovl_Bg_Spot12_Gate/z_bg_spot12_gate.c new file mode 100644 index 000000000..f553f755c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot12_Gate/z_bg_spot12_gate.c @@ -0,0 +1,143 @@ +/* + * File: z_bg_spot12_gate.c + * Overlay: ovl_Bg_Spot12_Gate + * Description: Haunted Wasteland Gate + */ + +#include "z_bg_spot12_gate.h" +#include "objects/object_spot12_obj/object_spot12_obj.h" + +#define FLAGS 0 + +void BgSpot12Gate_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot12Gate_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot12Gate_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot12Gate_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808B30C0(BgSpot12Gate* this); +void func_808B30D8(BgSpot12Gate* this, GlobalContext* globalCtx); +void func_808B3134(BgSpot12Gate* this); +void func_808B314C(BgSpot12Gate* this, GlobalContext* globalCtx); +void func_808B317C(BgSpot12Gate* this); +void func_808B318C(BgSpot12Gate* this, GlobalContext* globalCtx); +void func_808B3274(BgSpot12Gate* this); +void func_808B3298(BgSpot12Gate* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot12_Gate_InitVars = { + ACTOR_BG_SPOT12_GATE, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT12_OBJ, + sizeof(BgSpot12Gate), + (ActorFunc)BgSpot12Gate_Init, + (ActorFunc)BgSpot12Gate_Destroy, + (ActorFunc)BgSpot12Gate_Update, + (ActorFunc)BgSpot12Gate_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1200, ICHAIN_STOP), +}; + +void BgSpot12Gate_InitDynaPoly(BgSpot12Gate* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flags) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flags); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_spot12_gate.c", 145, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgSpot12Gate_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot12Gate* this = (BgSpot12Gate*)thisx; + + BgSpot12Gate_InitDynaPoly(this, globalCtx, &gGerudoFortressWastelandGateCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + func_808B3274(this); + } else { + func_808B30C0(this); + } +} + +void BgSpot12Gate_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot12Gate* this = (BgSpot12Gate*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808B30C0(BgSpot12Gate* this) { + this->actionFunc = func_808B30D8; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; +} + +void func_808B30D8(BgSpot12Gate* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + func_808B3134(this); + OnePointCutscene_Init(globalCtx, 4160, -99, &this->dyna.actor, MAIN_CAM); + } +} + +void func_808B3134(BgSpot12Gate* this) { + this->actionFunc = func_808B314C; + this->unk_168 = 40; +} + +void func_808B314C(BgSpot12Gate* this, GlobalContext* globalCtx) { + if (this->unk_168 <= 0) { + func_808B317C(this); + } +} + +void func_808B317C(BgSpot12Gate* this) { + this->actionFunc = func_808B318C; +} + +void func_808B318C(BgSpot12Gate* this, GlobalContext* globalCtx) { + s32 pad; + s32 var; + + Math_StepToF(&this->dyna.actor.velocity.y, 1.6f, 0.03f); + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 200.0f, + this->dyna.actor.velocity.y)) { + func_808B3274(this); + var = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(var, -0x3CB0); + Quake_SetQuakeValues(var, 3, 0, 0, 0); + Quake_SetCountdown(var, 0xC); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALGATE_OPEN - SFX_FLAG); + } +} + +void func_808B3274(BgSpot12Gate* this) { + this->actionFunc = func_808B3298; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + 200.0f; +} + +void func_808B3298(BgSpot12Gate* this, GlobalContext* globalCtx) { +} + +void BgSpot12Gate_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot12Gate* this = (BgSpot12Gate*)thisx; + + if (this->unk_168 > 0) { + this->unk_168--; + } + this->actionFunc(this, globalCtx); +} + +void BgSpot12Gate_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gGerudoFortressWastelandGateDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot12_Gate/z_bg_spot12_gate.h b/soh/src/overlays/actors/ovl_Bg_Spot12_Gate/z_bg_spot12_gate.h new file mode 100644 index 000000000..361873d25 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot12_Gate/z_bg_spot12_gate.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_SPOT12_GATE_H +#define Z_BG_SPOT12_GATE_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot12Gate; + +typedef void (*BgSpot12GateActionFunc)(struct BgSpot12Gate*, GlobalContext*); + +typedef struct BgSpot12Gate { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot12GateActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; +} BgSpot12Gate; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot12_Saku/z_bg_spot12_saku.c b/soh/src/overlays/actors/ovl_Bg_Spot12_Saku/z_bg_spot12_saku.c new file mode 100644 index 000000000..d5f0105ee --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot12_Saku/z_bg_spot12_saku.c @@ -0,0 +1,136 @@ +/* + * File: z_bg_spot12_saku.c + * Overlay: ovl_Bg_Spot12_Saku + * Description: + */ + +#include "z_bg_spot12_saku.h" +#include "objects/object_spot12_obj/object_spot12_obj.h" + +#define FLAGS 0 + +void BgSpot12Saku_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot12Saku_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot12Saku_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot12Saku_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808B3550(BgSpot12Saku* this); +void func_808B357C(BgSpot12Saku* this, GlobalContext* globalCtx); +void func_808B35E4(BgSpot12Saku* this); +void func_808B3604(BgSpot12Saku* this, GlobalContext* globalCtx); +void func_808B3714(BgSpot12Saku* this); +void func_808B37AC(BgSpot12Saku* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot12_Saku_InitVars = { + ACTOR_BG_SPOT12_SAKU, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT12_OBJ, + sizeof(BgSpot12Saku), + (ActorFunc)BgSpot12Saku_Init, + (ActorFunc)BgSpot12Saku_Destroy, + (ActorFunc)BgSpot12Saku_Update, + (ActorFunc)BgSpot12Saku_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void func_808B3420(BgSpot12Saku* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flags) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flags); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_spot12_saku.c", 140, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void BgSpot12Saku_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot12Saku* this = (BgSpot12Saku*)thisx; + + func_808B3420(this, globalCtx, &gGerudoFortressGTGShutterCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + func_808B3714(this); + } else { + func_808B3550(this); + } +} + +void BgSpot12Saku_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot12Saku* this = (BgSpot12Saku*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808B3550(BgSpot12Saku* this) { + this->actionFunc = func_808B357C; + this->dyna.actor.scale.x = 0.1f; + this->dyna.actor.world.pos.x = this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = this->dyna.actor.home.pos.z; +} + +void func_808B357C(BgSpot12Saku* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + func_808B35E4(this); + this->timer = 20; + OnePointCutscene_Init(globalCtx, 4170, -99, &this->dyna.actor, MAIN_CAM); + } +} + +void func_808B35E4(BgSpot12Saku* this) { + if (this->timer == 0) { + this->actionFunc = func_808B3604; + } +} + +void func_808B3604(BgSpot12Saku* this, GlobalContext* globalCtx) { + f32 temp_ret = Math_SmoothStepToF(&this->dyna.actor.scale.x, 0.001f / 0.14f, 0.16f, 0.0022f, 0.001f); + f32 temp_f18 = ((0.1f - this->dyna.actor.scale.x) * 840.0f); + + this->dyna.actor.world.pos.x = + this->dyna.actor.home.pos.x - (Math_SinS(this->dyna.actor.shape.rot.y + 0x4000) * temp_f18); + this->dyna.actor.world.pos.z = + this->dyna.actor.home.pos.z - (Math_CosS(this->dyna.actor.shape.rot.y + 0x4000) * temp_f18); + if (fabsf(temp_ret) < 0.0001f) { + func_808B3714(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN_STOP); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_METALGATE_OPEN - SFX_FLAG); + } +} + +void func_808B3714(BgSpot12Saku* this) { + this->actionFunc = func_808B37AC; + this->dyna.actor.scale.x = 0.001f / 0.14f; + this->dyna.actor.world.pos.x = + this->dyna.actor.home.pos.x - (Math_SinS(this->dyna.actor.shape.rot.y + 0x4000) * 78.0f); + this->dyna.actor.world.pos.z = + this->dyna.actor.home.pos.z - (Math_CosS(this->dyna.actor.shape.rot.y + 0x4000) * 78.0f); +} + +void func_808B37AC(BgSpot12Saku* this, GlobalContext* globalCtx) { +} + +void BgSpot12Saku_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot12Saku* this = (BgSpot12Saku*)thisx; + + if (this->timer > 0) { + this->timer--; + } + this->actionFunc(this, globalCtx); +} + +void BgSpot12Saku_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gGerudoFortressGTGShutterDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot12_Saku/z_bg_spot12_saku.h b/soh/src/overlays/actors/ovl_Bg_Spot12_Saku/z_bg_spot12_saku.h new file mode 100644 index 000000000..b8c8ef739 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot12_Saku/z_bg_spot12_saku.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_SPOT12_SAKU_H +#define Z_BG_SPOT12_SAKU_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot12Saku; + +typedef void (*BgSpot12SakuActionFunc)(struct BgSpot12Saku*, GlobalContext*); + +typedef struct BgSpot12Saku { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot12SakuActionFunc actionFunc; + /* 0x0168 */ s16 timer; +} BgSpot12Saku; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot15_Rrbox/z_bg_spot15_rrbox.c b/soh/src/overlays/actors/ovl_Bg_Spot15_Rrbox/z_bg_spot15_rrbox.c new file mode 100644 index 000000000..240235917 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot15_Rrbox/z_bg_spot15_rrbox.c @@ -0,0 +1,368 @@ +/* + * File: z_bg_spot15_rrbox.c + * Overlay: ovl_Bg_Spot15_Rrbox + * Description: Milk Crate + */ + +#include "z_bg_spot15_rrbox.h" +#include "objects/object_spot15_obj/object_spot15_obj.h" + +#define FLAGS 0 + +void BgSpot15Rrbox_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot15Rrbox_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot15Rrbox_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot15Rrbox_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808B4084(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B40AC(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B4194(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B4380(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B43D0(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B44B8(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B4178(BgSpot15Rrbox* this, GlobalContext* globalCtx); +void func_808B44CC(BgSpot15Rrbox* this, GlobalContext* globalCtx); + +static s16 D_808B4590 = 0; + +const ActorInit Bg_Spot15_Rrbox_InitVars = { + ACTOR_BG_SPOT15_RRBOX, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT15_OBJ, + sizeof(BgSpot15Rrbox), + (ActorFunc)BgSpot15Rrbox_Init, + (ActorFunc)BgSpot15Rrbox_Destroy, + (ActorFunc)BgSpot15Rrbox_Update, + (ActorFunc)BgSpot15Rrbox_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static Vec3f D_808B45C4[] = { + { 770.0f, 1490.0f, -299.0f }, + { 770.0f, 1550.0f, -299.0f }, +}; + +static Vec3f D_808B45DC[] = { + { 29.99f, 0.01f, -29.99f }, { -29.99f, 0.01f, -29.99f }, { -29.99f, 0.01f, 29.99f }, + { 29.99f, 0.01f, 29.99f }, { 0.0f, 0.01f, 0.0f }, +}; + +void func_808B3960(BgSpot15Rrbox* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flags) { + s32 pad; + CollisionHeader* colHeader = NULL; + u32 pad2; + + DynaPolyActor_Init(&this->dyna, flags); + CollisionHeader_GetVirtual(collision, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_bg_spot15_rrbox.c", 171, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void func_808B39E8(Vec3f* arg0, Vec3f* arg1, f32 arg2, f32 arg3) { + arg0->x = (arg1->z * arg2) + (arg1->x * arg3); + arg0->y = arg1->y; + arg0->z = (arg1->z * arg3) - (arg1->x * arg2); +} + +void func_808B3A34(BgSpot15Rrbox* this) { + this->bgId = BG_ACTOR_MAX; +} + +s32 func_808B3A40(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + DynaPolyActor* dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->bgId); + + if ((dynaPolyActor != NULL) && + Math3D_Dist2DSq(dynaPolyActor->actor.world.pos.x, dynaPolyActor->actor.world.pos.z, + this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.z) < 0.01f) { + return true; + } + return false; +} + +s32 func_808B3AAC(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + s16 rotY; + Actor* actor = &this->dyna.actor; + + if (globalCtx->sceneNum == SCENE_SOUKO) { + return true; + } else if (func_808B3A40(this, globalCtx)) { + return false; + } + + if (actor->world.pos.x <= 930.0f && actor->world.pos.z >= -360.0f) { + if (this->dyna.unk_150 >= 0.0f) { + rotY = actor->world.rot.y; + } else { + rotY = actor->world.rot.y + 0x8000; + } + + if (rotY < 0x2000 && rotY > -0x6000) { + return gSaveContext.eventChkInf[1] & 0x10; + } + return true; + } + + return true; +} + +void BgSpot15Rrbox_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot15Rrbox* this = (BgSpot15Rrbox*)thisx; + + func_808B3960(this, globalCtx, &gLonLonMilkCrateCol, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + func_808B3A34(this); + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params & 0x3F))) { + func_808B44B8(this, globalCtx); + this->dyna.actor.world.pos = D_808B45C4[D_808B4590]; + D_808B4590++; + } else { + func_808B4084(this, globalCtx); + } + osSyncPrintf("(spot15 ロンロン木箱)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void BgSpot15Rrbox_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot15Rrbox* this = (BgSpot15Rrbox*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + D_808B4590 = 0; +} + +s32 func_808B3CA0(BgSpot15Rrbox* this, GlobalContext* globalCtx, s32 arg2) { + f32 chkDist = 0.0f; + Vec3f actorPosition; + Vec3f actorScale; + + func_808B3A34(this); + + actorScale.x = D_808B45DC[arg2].x * (this->dyna.actor.scale.x * 10.0f); + actorScale.y = D_808B45DC[arg2].y * (this->dyna.actor.scale.y * 10.0f); + actorScale.z = D_808B45DC[arg2].z * (this->dyna.actor.scale.z * 10.0f); + + func_808B39E8(&actorPosition, &actorScale, this->unk_16C, this->unk_170); + + actorPosition.x += this->dyna.actor.world.pos.x; + actorPosition.y += this->dyna.actor.prevPos.y; + actorPosition.z += this->dyna.actor.world.pos.z; + + this->dyna.actor.floorHeight = BgCheck_EntityRaycastFloor6(&globalCtx->colCtx, &this->dyna.actor.floorPoly, + &this->bgId, &this->dyna.actor, &actorPosition, chkDist); + + if ((this->dyna.actor.floorHeight - this->dyna.actor.world.pos.y) >= -0.001f) { + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + return true; + } + return false; +} + +f32 func_808B3DDC(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + s32 i; + Vec3f position; + Vec3f scale; + Actor* actor = &this->dyna.actor; + f32 yIntersect; + f32 returnValue = BGCHECK_Y_MIN; + s32 bgId; + + func_808B3A34(this); + for (i = 0; i < ARRAY_COUNT(D_808B45DC); i++) { + scale.x = D_808B45DC[i].x * (actor->scale.x * 10.0f); + scale.y = D_808B45DC[i].y * (actor->scale.y * 10.0f); + scale.z = D_808B45DC[i].z * (actor->scale.z * 10.0f); + + func_808B39E8(&position, &scale, this->unk_16C, this->unk_170); + + position.x += actor->world.pos.x; + position.y += actor->prevPos.y; + position.z += actor->world.pos.z; + + yIntersect = BgCheck_EntityRaycastFloor6(&globalCtx->colCtx, &actor->floorPoly, &bgId, actor, &position, 0); + + if (returnValue < yIntersect) { + returnValue = yIntersect; + this->bgId = bgId; + } + } + return returnValue; +} + +s32 func_808B3F58(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + if (func_808B3CA0(this, globalCtx, 0)) { + return true; + } + if (func_808B3CA0(this, globalCtx, 1)) { + return true; + } + if (func_808B3CA0(this, globalCtx, 2)) { + return true; + } + if (func_808B3CA0(this, globalCtx, 3)) { + return true; + } + if (func_808B3CA0(this, globalCtx, 4)) { + return true; + } + return false; +} + +s32 func_808B4010(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + return !func_800435D8(globalCtx, &this->dyna, this->dyna.actor.scale.x * 290.0f, + this->dyna.actor.scale.x * 290.0f + 20.0f, 1.0f); +} + +void func_808B4084(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + this->actionFunc = func_808B40AC; + this->dyna.actor.gravity = 0.0f; + this->dyna.actor.velocity.x = 0.0f; + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.velocity.z = 0.0f; +} + +void func_808B40AC(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_168 <= 0 && fabsf(this->dyna.unk_150) > 0.001f) { + if (func_808B3AAC(this, globalCtx) && !func_808B4010(this, globalCtx)) { + this->unk_17C = this->dyna.unk_150; + func_808B4178(this, globalCtx); + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } +} + +void func_808B4178(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + this->actionFunc = func_808B4194; + this->dyna.actor.gravity = 0.0f; +} + +void func_808B4194(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + f32 sign; + Player* player = GET_PLAYER(globalCtx); + f32 tempUnk178; + s32 approxFResult; + Actor* actor = &this->dyna.actor; + + this->unk_174 += 0.5f; + + this->unk_174 = CLAMP_MAX(this->unk_174, 2.0f); + + approxFResult = Math_StepToF(&this->unk_178, 20.0f, this->unk_174); + + sign = this->unk_17C >= 0.0f ? 1.0f : -1.0f; + + tempUnk178 = (f32)sign * this->unk_178; + actor->world.pos.x = actor->home.pos.x + (tempUnk178 * this->unk_16C); + actor->world.pos.z = actor->home.pos.z + (tempUnk178 * this->unk_170); + + if (!func_808B3F58(this, globalCtx)) { + actor->home.pos.x = actor->world.pos.x; + actor->home.pos.z = actor->world.pos.z; + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + this->unk_178 = 0.0f; + this->unk_174 = 0.0f; + func_808B4380(this, globalCtx); + } else if (approxFResult) { + player = GET_PLAYER(globalCtx); + if (func_808B4010(this, globalCtx)) { + Audio_PlayActorSound2(actor, NA_SE_EV_WOOD_BOUND); + } + if (func_808B3A40(this, globalCtx)) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + actor->home.pos.x = actor->world.pos.x; + actor->home.pos.z = actor->world.pos.z; + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + this->unk_178 = 0.0f; + this->unk_174 = 0.0f; + this->unk_168 = 10; + func_808B4084(this, globalCtx); + } + Audio_PlayActorSound2(actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); +} + +void func_808B4380(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + this->dyna.actor.velocity.x = 0.0f; + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.velocity.z = 0.0f; + this->dyna.actor.gravity = -1.0f; + this->dyna.actor.floorHeight = func_808B3DDC(this, globalCtx); + this->actionFunc = func_808B43D0; +} + +void func_808B43D0(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + f32 floorHeight; + Player* player = GET_PLAYER(globalCtx); + Actor* actor = &this->dyna.actor; + + if (fabsf(this->dyna.unk_150) > 0.001f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } + + Actor_MoveForward(actor); + + if (actor->world.pos.y <= BGCHECK_Y_MIN + 10.0f) { + // "Lon Lon wooden crate fell too much" + osSyncPrintf("Warning : ロンロン木箱落ちすぎた(%s %d)(arg_data 0x%04x)\n", "../z_bg_spot15_rrbox.c", 599, + actor->params); + + Actor_Kill(actor); + + return; + } + + floorHeight = actor->floorHeight; + + if ((floorHeight - actor->world.pos.y) >= -0.001f) { + actor->world.pos.y = floorHeight; + func_808B4084(this, globalCtx); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WOOD_BOUND); + } +} + +void func_808B44B8(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + this->actionFunc = func_808B44CC; +} + +void func_808B44CC(BgSpot15Rrbox* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; +} + +void BgSpot15Rrbox_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot15Rrbox* this = (BgSpot15Rrbox*)thisx; + + if (this->unk_168 > 0) { + this->unk_168--; + } + this->dyna.actor.world.rot.y = this->dyna.unk_158; + this->unk_16C = Math_SinS(this->dyna.actor.world.rot.y); + this->unk_170 = Math_CosS(this->dyna.actor.world.rot.y); + this->actionFunc(this, globalCtx); +} + +void BgSpot15Rrbox_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gLonLonMilkCrateDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot15_Rrbox/z_bg_spot15_rrbox.h b/soh/src/overlays/actors/ovl_Bg_Spot15_Rrbox/z_bg_spot15_rrbox.h new file mode 100644 index 000000000..d1e8e86b7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot15_Rrbox/z_bg_spot15_rrbox.h @@ -0,0 +1,23 @@ +#ifndef Z_BG_SPOT15_RRBOX_H +#define Z_BG_SPOT15_RRBOX_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot15Rrbox; + +typedef void (*BgSpot15RrboxActionFunc)(struct BgSpot15Rrbox*, GlobalContext*); + +typedef struct BgSpot15Rrbox { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot15RrboxActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; + /* 0x016C */ f32 unk_16C; + /* 0x0170 */ f32 unk_170; + /* 0x0174 */ f32 unk_174; + /* 0x0178 */ f32 unk_178; + /* 0x017C */ f32 unk_17C; + /* 0x0180 */ s32 bgId; // Id of BgActor beneath the box +} BgSpot15Rrbox; // size = 0x0184 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.c b/soh/src/overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.c new file mode 100644 index 000000000..9f0e33bee --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.c @@ -0,0 +1,107 @@ +/* + * File: z_bg_spot15_saku.c + * Overlay: ovl_Bg_Spot15_Saku + * Description: Hyrule Castle Gate + */ + +#include "z_bg_spot15_saku.h" +#include "objects/object_spot15_obj/object_spot15_obj.h" + +#define FLAGS 0 + +void BgSpot15Saku_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot15Saku_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot15Saku_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot15Saku_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808B4930(BgSpot15Saku* this, GlobalContext* globalCtx); +void func_808B4978(BgSpot15Saku* this, GlobalContext* globalCtx); +void func_808B4A04(BgSpot15Saku* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot15_Saku_InitVars = { + ACTOR_BG_SPOT15_SAKU, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_SPOT15_OBJ, + sizeof(BgSpot15Saku), + (ActorFunc)BgSpot15Saku_Init, + (ActorFunc)BgSpot15Saku_Destroy, + (ActorFunc)BgSpot15Saku_Update, + (ActorFunc)BgSpot15Saku_Draw, + NULL, +}; + +void BgSpot15Saku_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSpot15Saku* this = (BgSpot15Saku*)thisx; + s32 pad2; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gLonLonCorralFenceCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->dyna.actor.scale.x = 0.1f; + this->dyna.actor.scale.y = 0.1f; + this->dyna.actor.scale.z = 0.1f; + this->unk_170.x = this->dyna.actor.world.pos.x; + this->unk_170.y = this->dyna.actor.world.pos.y; + this->unk_170.z = this->dyna.actor.world.pos.z; + if (gSaveContext.infTable[7] & 2) { + this->dyna.actor.world.pos.z = 2659.0f; + } + this->actionFunc = func_808B4930; +} + +void BgSpot15Saku_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot15Saku* this = (BgSpot15Saku*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808B4930(BgSpot15Saku* this, GlobalContext* globalCtx) { + if (this->unk_168 && !(gSaveContext.infTable[7] & 2)) { + this->timer = 2; + this->actionFunc = func_808B4978; + } +} + +void func_808B4978(BgSpot15Saku* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALGATE_OPEN - SFX_FLAG); + this->dyna.actor.world.pos.z -= 2.0f; + if (this->dyna.actor.world.pos.z < 2660.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BRIDGE_OPEN_STOP); + this->timer = 30; + this->actionFunc = func_808B4A04; + } + } +} + +void func_808B4A04(BgSpot15Saku* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + this->unk_168 = 0; + this->actionFunc = func_808B4930; + } +} + +void BgSpot15Saku_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot15Saku* this = (BgSpot15Saku*)thisx; + + if (this->timer != 0) { + this->timer--; + } + + this->actionFunc(this, globalCtx); +} + +void BgSpot15Saku_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot15_saku.c", 259); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot15_saku.c", 263), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gLonLonCorralFenceDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot15_saku.c", 268); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.h b/soh/src/overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.h new file mode 100644 index 000000000..d722b80e9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.h @@ -0,0 +1,19 @@ +#ifndef Z_BG_SPOT15_SAKU_H +#define Z_BG_SPOT15_SAKU_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot15Saku; + +typedef void (*BgSpot15SakuActionFunc)(struct BgSpot15Saku*, GlobalContext*); + +typedef struct BgSpot15Saku { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot15SakuActionFunc actionFunc; + /* 0x0168 */ u64 unk_168; + /* 0x0170 */ Vec3f unk_170; + /* 0x017C */ s16 timer; +} BgSpot15Saku; // size = 0x0180 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot16_Bombstone/z_bg_spot16_bombstone.c b/soh/src/overlays/actors/ovl_Bg_Spot16_Bombstone/z_bg_spot16_bombstone.c new file mode 100644 index 000000000..94f4a1546 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot16_Bombstone/z_bg_spot16_bombstone.c @@ -0,0 +1,560 @@ +#include "z_bg_spot16_bombstone.h" +#include "objects/object_spot16_obj/object_spot16_obj.h" +#include "objects/object_bombiwa/object_bombiwa.h" +#include "overlays/actors/ovl_En_Bombf/z_en_bombf.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot16Bombstone_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Bombstone_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Bombstone_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Bombstone_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Bombstone_Reset(void); + +void func_808B5A94(BgSpot16Bombstone* this, GlobalContext* globalCtx); +void func_808B5B04(BgSpot16Bombstone* this, GlobalContext* globalCtx); +void func_808B5B6C(BgSpot16Bombstone* this, GlobalContext* globalCtx); +void func_808B5B58(BgSpot16Bombstone* this); +void func_808B5950(BgSpot16Bombstone* this, GlobalContext* globalCtx); +void func_808B5934(BgSpot16Bombstone* this); +void func_808B5AF0(BgSpot16Bombstone* this); +void func_808B5A78(BgSpot16Bombstone* this); + +static EnBombf* sPlayerBomb = NULL; + +static s16 sTimer = 0; + +static s16 D_808B5DD8[][10] = { + { 0x0008, 0x0004, 0x0046, 0x07D0, 0xFCE0, 0x0000, 0x0064, 0x0000, 0x0000, 0x0000 }, + { 0x0006, 0x0003, 0x0032, 0x00C8, 0x0A28, 0xC350, 0x005A, 0x0000, 0x0000, 0x0000 }, + { 0x0005, 0x0003, 0x0028, 0xF63C, 0x0190, 0x30B0, 0x0032, 0x0000, 0x0000, 0x0000 }, + { 0x0003, 0x0001, 0x003C, 0x0258, 0xFF9C, 0xAFC8, 0x0032, 0x0000, 0x0000, 0x0000 }, + { 0x0003, 0x0001, 0x0028, 0xF2B8, 0xFF9C, 0x6590, 0x001E, 0x0000, 0x0000, 0x0000 }, + { 0x0006, 0x0009, 0x0028, 0x0000, 0x0BB8, 0xD8F0, 0x001E, 0x0000, 0x0000, 0x0000 }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC1FFF6, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 50, 0 }, 288 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 190, 80, 0, { 10, 0, 50 } }, +}; + +static s16 D_808B5EB0[][7] = { + { 0x0000, 0x000A, 0x003C, 0xFFF6, 0x0104, 0x01E0, 0x0007 }, + { 0x0000, 0x0000, 0x0032, 0x0000, 0x0104, 0x017C, 0x000D }, + { 0x0001, 0x001E, 0x0014, 0x0014, 0x00A0, 0x0104, 0x000A }, + { 0x0002, 0x0014, 0x0028, 0x0014, 0x00C8, 0x0096, 0x0007 }, + { 0x0003, 0xFFD8, 0x0064, 0x001E, 0x00D2, 0x0064, 0x0007 }, + { 0x0004, 0x000A, 0x0078, 0x000A, 0x00A0, 0x0028, 0x000A }, + { 0x0005, 0xFFA7, 0x006E, 0x0041, 0x0096, 0x0118, 0x0007 }, + { 0x0006, 0xFFC4, 0x0070, 0x006D, 0x006E, 0x0082, 0x0007 }, + { 0x0007, 0xFFF2, 0x0050, 0x007F, 0x008C, 0x00D2, 0x0007 }, + { 0x0008, 0x0014, 0x0032, 0x001E, 0x008C, 0x00C8, 0x0007 }, + { 0x0009, 0xFFEC, 0x0014, 0x0028, 0x00C8, 0x00B4, 0x0007 }, + { 0x000A, 0x001E, 0x0028, 0xFFE2, 0x0064, 0x0078, 0x0007 }, + { 0x000A, 0xFF92, 0x001D, 0x0078, 0x008C, 0x0118, 0x000A }, + { 0x000B, 0x001E, 0x0014, 0x001E, 0x0050, 0x00C8, 0x0006 }, + { 0x000C, 0x0028, 0x001E, 0xFFE2, 0x00AA, 0x010E, 0x0011 }, + { 0x000D, 0xFFD8, 0x0032, 0xFFEC, 0x008C, 0x0056, 0x0006 }, + { 0x000D, 0x0032, 0x0032, 0x0032, 0x0096, 0x00C8, 0x000A }, + { 0x000E, 0x0028, 0x0028, 0xFFD8, 0x003C, 0x00A0, 0x0006 }, + { 0x000F, 0xFFE2, 0x0014, 0x0032, 0x00AA, 0x012C, 0x0006 }, + { 0x0010, 0x0028, 0x0032, 0x0014, 0x00AA, 0x0078, 0x0007 }, + { 0x0010, 0x001E, 0x0032, 0x003C, 0x0096, 0x00C8, 0x0006 }, + { 0x0011, 0x000A, 0x000A, 0x0000, 0x0082, 0x00DC, 0x0006 }, + { 0x0012, 0x000A, 0x0028, 0x0014, 0x00B4, 0x00DC, 0x000B }, + { 0x0013, 0x000A, 0x0005, 0x0000, 0x006E, 0x0046, 0x0011 }, + { 0x0013, 0xFFEC, 0x0032, 0xFFE2, 0x0096, 0x00C8, 0x0006 }, + { 0x0014, 0x0050, 0x0032, 0x0000, 0x0096, 0x00C8, 0x0008 }, +}; + +const ActorInit Bg_Spot16_Bombstone_InitVars = { + ACTOR_BG_SPOT16_BOMBSTONE, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT16_OBJ, + sizeof(BgSpot16Bombstone), + (ActorFunc)BgSpot16Bombstone_Init, + (ActorFunc)BgSpot16Bombstone_Destroy, + (ActorFunc)BgSpot16Bombstone_Update, + (ActorFunc)BgSpot16Bombstone_Draw, + (ActorResetFunc)BgSpot16Bombstone_Reset +}; + +static InitChainEntry sInitChainBoulder[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static InitChainEntry sInitChainDebris[] = { + ICHAIN_F32(gravity, -1, ICHAIN_CONTINUE), + ICHAIN_F32(minVelocityY, -10, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static Vec3f sVelocity = { 0.0f, 0.0f, 0.0f }; + +static Vec3f sAcceleration = { 0.0f, 0.4f, 0.0f }; + +static f32 D_808B6074[] = { 66.0f, 51.0f, 48.0f, 36.0f, 21.0f }; + +static s16 D_808B6088[] = { 0, 1, 2, 3, 4 }; + +void func_808B4C30(BgSpot16Bombstone* this) { + this->switchFlag = (this->actor.params >> 8) & 0x3F; + this->actor.params = this->actor.params & 0xFF; +} + +void func_808B4C4C(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->colliderJntSph); + Collider_SetJntSph(globalCtx, &this->colliderJntSph, &this->actor, &sJntSphInit, this->colliderElements); + this->colliderJntSph.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x; + this->colliderJntSph.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y + 50.0f; + this->colliderJntSph.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z; + this->colliderJntSph.elements[0].dim.worldSphere.radius = 120; +} + +void func_808B4D04(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitCylinder(globalCtx, &this->colliderCylinder); + Collider_SetCylinder(globalCtx, &this->colliderCylinder, &this->actor, &sCylinderInit); + this->colliderCylinder.dim.pos.x += (s16)this->actor.world.pos.x; + this->colliderCylinder.dim.pos.y += (s16)this->actor.world.pos.y; + this->colliderCylinder.dim.pos.z += (s16)this->actor.world.pos.z; +} + +s32 func_808B4D9C(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + osSyncPrintf("Spot16 obj 爆弾石 破壊済み\n"); + return false; + } + Actor_ProcessInitChain(&this->actor, sInitChainBoulder); + Actor_SetScale(&this->actor, 0.4f); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + func_808B4C4C(this, globalCtx); + func_808B4D04(this, globalCtx); + this->sinRotation = Math_SinS(this->actor.shape.rot.y); + this->cosRotation = Math_CosS(this->actor.shape.rot.y); + this->dList = gDodongosCavernRock3DL; + + func_808B5934(this); + return true; +} + +s32 func_808B4E58(BgSpot16Bombstone* this, GlobalContext* globalctx) { + Actor* actor = &this->actor; + f32 scaleFactor = 1.0f / 600.0f; + f32 sinCosPosFactor = 50.0f; + f32 sinValue; + f32 cosValue; + + Actor_ProcessInitChain(actor, sInitChainDebris); + + actor->speedXZ = D_808B5DD8[actor->params][0]; + actor->velocity.y = D_808B5DD8[actor->params][1]; + + Actor_SetScale(actor, D_808B5DD8[actor->params][2] * scaleFactor); + + this->unk_210 = (f32)D_808B5DD8[actor->params][3]; + this->unk_212 = (f32)D_808B5DD8[actor->params][4]; + + actor->world.rot.y = D_808B5DD8[actor->params][5]; + + sinValue = Math_SinS(this->actor.world.rot.y); + cosValue = Math_CosS(this->actor.world.rot.y); + + actor->world.pos.x = (sinValue * sinCosPosFactor) + actor->home.pos.x; + actor->world.pos.y = D_808B5DD8[actor->params][6] + actor->home.pos.y; + actor->world.pos.z = (cosValue * sinCosPosFactor) + actor->home.pos.z; + + actor->shape.rot.x = D_808B5DD8[actor->params][7]; + actor->shape.rot.y = D_808B5DD8[actor->params][8]; + actor->shape.rot.z = D_808B5DD8[actor->params][9]; + + this->dList = object_bombiwa_DL_0009E0; + this->bombiwaBankIndex = Object_GetIndex(&globalctx->objectCtx, OBJECT_BOMBIWA); + + if (this->bombiwaBankIndex < 0) { + osSyncPrintf("Error : バンク危険!(arg_data 0x%04x)(%s %d)\n", actor->params, "../z_bg_spot16_bombstone.c", + 589); + return false; + } + + func_808B5AF0(this); + return true; +} + +void BgSpot16Bombstone_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Bombstone* this = (BgSpot16Bombstone*)thisx; + s16 shouldLive; + + func_808B4C30(this); + + switch (this->actor.params) { + case 0xFF: + // The boulder is intact + shouldLive = func_808B4D9C(this, globalCtx); + break; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + // The boulder is debris + shouldLive = func_808B4E58(this, globalCtx); + break; + default: + osSyncPrintf("Error : arg_data おかしいな(%s %d)(arg_data 0x%04x)\n", "../z_bg_spot16_bombstone.c", 668, + this->actor.params); + shouldLive = false; + break; + } + + if (!shouldLive) { + Actor_Kill(&this->actor); + return; + } + osSyncPrintf("Spot16 obj 爆弾石 (scaleX %f)(arg_data 0x%04x)\n", this->actor.scale.x, this->actor.params); +} + +void BgSpot16Bombstone_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Bombstone* this = (BgSpot16Bombstone*)thisx; + + if (this->actor.params == 0xFF) { + // Boulder is intact so remove its collider + Collider_DestroyJntSph(globalCtx, &this->colliderJntSph); + Collider_DestroyCylinder(globalCtx, &this->colliderCylinder); + } +} + +void BgSpot16Bombstone_SpawnDust(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + f32 scaleX1 = this->actor.scale.x * 150; + s16 scaleX2 = this->actor.scale.x * 250; + Vec3f world; + + world.x = this->actor.world.pos.x; + world.y = this->actor.world.pos.y + 50.0f; + world.z = this->actor.world.pos.z; + + func_80033480(globalCtx, &world, scaleX1, 2, scaleX2, 0xA0, 1); +} + +void func_808B5240(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + f32 tempUnk6; + f32 tempUnk2; + s16 index; + Vec3f position; + Vec3f* actorPosition = &this->actor.world.pos; + + if (1) {} + + while (true) { + if ((u32)this->unk_158 >= ARRAY_COUNTU(D_808B5EB0) || this->unk_154 < D_808B5EB0[this->unk_158][0]) { + break; + } + + index = this->unk_158; + + tempUnk2 = D_808B5EB0[index][1]; + tempUnk6 = D_808B5EB0[index][3]; + + position.x = ((this->sinRotation * tempUnk6) + (tempUnk2 * this->cosRotation)) + actorPosition->x; + position.y = D_808B5EB0[index][2] + actorPosition->y; + position.z = ((this->cosRotation * tempUnk6) - (tempUnk2 * this->sinRotation)) + actorPosition->z; + + func_800287AC(globalCtx, &position, &sVelocity, &sAcceleration, D_808B5EB0[index][4], D_808B5EB0[index][5], + D_808B5EB0[index][6]); + + this->unk_158 += 1; + } +} + +void BgSpot16Bombstone_SpawnFragments(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + f32 velocityYMultiplier = 1.3f; + Vec3f pos; + Vec3f velocity; + s32 index; + s16 scale; + + if (this->actor.params == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_SPOT16_BOMBSTONE, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 5); + index = 3; + } else { + index = 0; + } + + if (index < ARRAY_COUNT(D_808B6074)) { + do { + pos.x = ((Rand_ZeroOne() - 0.5f) * 8.0f) + this->actor.world.pos.x; + pos.y = ((Rand_ZeroOne() * 5.0f) + this->actor.world.pos.y) + 8.0f; + pos.z = ((Rand_ZeroOne() - 0.5f) * 8.0f) + this->actor.world.pos.z; + + velocity.x = (Rand_ZeroOne() - 0.5f) * 16.0f; + velocity.y = (Rand_ZeroOne() * 14.0) + (fabsf(this->actor.velocity.y) * velocityYMultiplier); + velocity.z = (Rand_ZeroOne() - 0.5f) * 16.0f; + + scale = D_808B6074[index] * this->actor.scale.x * 3; + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &this->actor.world.pos, -420, 0x31, 0xF, 0xF, 0, scale, 2, + 0x40, 160, KAKERA_COLOR_NONE, OBJECT_BOMBIWA, object_bombiwa_DL_0009E0); + index += 1; + } while (index != ARRAY_COUNT(D_808B6074)); + } +} + +void func_808B561C(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + s32 index; + PosRot* world; + + world = &this->actor.world; + for (index = 0; index < ARRAY_COUNT(D_808B6088); index++) { + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_SPOT16_BOMBSTONE, world->pos.x, world->pos.y, + world->pos.z, 0, 0, 0, D_808B6088[index]) == NULL) { + break; + } + } +} + +void func_808B56BC(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 sinValue; + s16 adjustedYawDiff; + s32 yawDiff; + s32 absYawDiff; + + if (this->actor.xzDistToPlayer < 130.0f && this->actor.yDistToPlayer < 160.0f && + this->actor.yDistToPlayer >= -10.0f) { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + absYawDiff = ABS(yawDiff); + + adjustedYawDiff = absYawDiff - 0x3FFF; + + if (adjustedYawDiff > 0) { + sinValue = Math_SinS(adjustedYawDiff) * this->actor.xzDistToPlayer; + + if (sinValue >= 0.0f) { + player->actor.world.pos.x += sinValue * this->sinRotation; + player->actor.world.pos.z += sinValue * this->cosRotation; + } else { + osSyncPrintf("Error 補正出来ない(%s %d)(arg_data 0x%04x)(hosei_angY %x)\n", + "../z_bg_spot16_bombstone.c", 935, this->actor.params, adjustedYawDiff); + } + } + } +} + +void func_808B57E0(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + Actor* playerHeldActor; + Player* player = GET_PLAYER(globalCtx); + EnBombf* currentBomb; + + if (sTimer > 0) { + sTimer--; + } + + if (sPlayerBomb != NULL) { + if (sPlayerBomb->actor.update == NULL) { + sPlayerBomb = NULL; + } else if (sTimer <= 0 && sPlayerBomb->actor.world.pos.y < 1400.0f && + Math3D_Dist1DSq(sPlayerBomb->actor.world.pos.x + 1579.0f, sPlayerBomb->actor.world.pos.z + 790.0f) < + SQ(400.0f) && + sPlayerBomb->actor.params == 0) { + currentBomb = sPlayerBomb; + if (currentBomb->timer > 0) { + sTimer = currentBomb->timer + 20; + OnePointCutscene_Init(globalCtx, 4180, sTimer, NULL, MAIN_CAM); + } + } + } else if (player->stateFlags1 & 0x800) { + playerHeldActor = player->heldActor; + if (playerHeldActor != NULL && playerHeldActor->category == ACTORCAT_EXPLOSIVE && + playerHeldActor->id == ACTOR_EN_BOMBF) { + sPlayerBomb = (EnBombf*)playerHeldActor; + } + } +} + +void func_808B5934(BgSpot16Bombstone* this) { + this->actor.draw = BgSpot16Bombstone_Draw; + this->actionFunc = func_808B5950; +} + +void func_808B5950(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + s32 pad; + + func_808B56BC(this, globalCtx); + func_808B57E0(this, globalCtx); + + if (globalCtx) {} + + if (this->colliderCylinder.base.acFlags & AC_HIT) { + this->colliderCylinder.base.acFlags &= ~AC_HIT; + + func_808B561C(this, globalCtx); + + OnePointCutscene_Init(globalCtx, 4180, 50, NULL, MAIN_CAM); + + Flags_SetSwitch(globalCtx, this->switchFlag); + gSaveContext.eventChkInf[2] |= 8; + + func_808B5A78(this); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + if (mREG(64) == 1) { + func_808B561C(this, globalCtx); + mREG(64) = -10; + } else if (mREG(64) < 0) { + mREG(64)++; + } +} + +void func_808B5A78(BgSpot16Bombstone* this) { + this->unk_154 = 0; + this->unk_158 = 0; + this->actor.draw = NULL; + this->actionFunc = func_808B5A94; +} + +void func_808B5A94(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + + func_808B5240(this, globalCtx); + + if (this->unk_154 == 56) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + + if (this->unk_154 > 60) { + Actor_Kill(&this->actor); + } +} + +void func_808B5AF0(BgSpot16Bombstone* this) { + this->actionFunc = func_808B5B04; + this->actor.draw = NULL; +} + +void func_808B5B04(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->bombiwaBankIndex)) { + func_808B5B58(this); + this->actor.draw = BgSpot16Bombstone_Draw; + } +} + +void func_808B5B58(BgSpot16Bombstone* this) { + this->unk_154 = 0; + this->actionFunc = func_808B5B6C; +} + +void func_808B5B6C(BgSpot16Bombstone* this, GlobalContext* globalCtx) { + Actor* actor = &this->actor; + + Actor_MoveForward(actor); + actor->shape.rot.x += this->unk_210; + actor->shape.rot.z += this->unk_212; + + if (this->unk_154 > 60) { + Actor_Kill(actor); + return; + } + + if (actor->bgCheckFlags & 8 || (actor->bgCheckFlags & 1 && actor->velocity.y < 0.0f)) { + BgSpot16Bombstone_SpawnFragments(this, globalCtx); + BgSpot16Bombstone_SpawnDust(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &actor->world.pos, 20, NA_SE_EV_ROCK_BROKEN); + Actor_Kill(actor); + return; + } + + Actor_UpdateBgCheckInfo(globalCtx, actor, 17.5f, 35.0f, 0.0f, 5); +} + +void BgSpot16Bombstone_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Bombstone* this = (BgSpot16Bombstone*)thisx; + + this->unk_154++; + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void BgSpot16Bombstone_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Bombstone* this = (BgSpot16Bombstone*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot16_bombstone.c", 1253); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot16_bombstone.c", 1257), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->actor.params == 0xFF) { + // The boulder is intact + gSPDisplayList(POLY_OPA_DISP++, this->dList); + } else { + // The boulder is debris + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->bombiwaBankIndex].segment); + gSPDisplayList(POLY_OPA_DISP++, this->dList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot16_bombstone.c", 1274); +} + +void BgSpot16Bombstone_Reset(void) { + sPlayerBomb = NULL; + sTimer = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Spot16_Bombstone/z_bg_spot16_bombstone.h b/soh/src/overlays/actors/ovl_Bg_Spot16_Bombstone/z_bg_spot16_bombstone.h new file mode 100644 index 000000000..924a0b1f7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot16_Bombstone/z_bg_spot16_bombstone.h @@ -0,0 +1,28 @@ +#ifndef Z_BG_SPOT16_BOMBSTONE_H +#define Z_BG_SPOT16_BOMBSTONE_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot16Bombstone; + +typedef void (*BgSpot16BombstoneActionFunc)(struct BgSpot16Bombstone*, GlobalContext*); + +typedef struct BgSpot16Bombstone { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgSpot16BombstoneActionFunc actionFunc; + /* 0x0150 */ Gfx* dList; + /* 0x0154 */ s16 unk_154; + /* 0x0156 */ s16 switchFlag; + /* 0x0158 */ s16 unk_158; + /* 0x015C */ f32 sinRotation; + /* 0x0160 */ f32 cosRotation; + /* 0x0164 */ ColliderJntSph colliderJntSph; + /* 0x0184 */ ColliderJntSphElement colliderElements[1]; + /* 0x01C4 */ ColliderCylinder colliderCylinder; + /* 0x0210 */ s16 unk_210; + /* 0x0212 */ s16 unk_212; + /* 0x0214 */ s8 bombiwaBankIndex; +} BgSpot16Bombstone; // size = 0x0218 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot16_Doughnut/z_bg_spot16_doughnut.c b/soh/src/overlays/actors/ovl_Bg_Spot16_Doughnut/z_bg_spot16_doughnut.c new file mode 100644 index 000000000..4ccef9840 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot16_Doughnut/z_bg_spot16_doughnut.c @@ -0,0 +1,163 @@ +/* + * File: z_bg_spot16_doughnut.c + * Overlay: ovl_Bg_Spot16_Doughnut + * Description: Death Mountain cloud circle + */ + +#include "z_bg_spot16_doughnut.h" +#include "objects/object_efc_doughnut/object_efc_doughnut.h" +#include "vt.h" + +#define FLAGS 0 + +void BgSpot16Doughnut_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Doughnut_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Doughnut_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Doughnut_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgSpot16Doughnut_UpdateExpanding(Actor* thisx, GlobalContext* globalCtx); +void BgSpot16Doughnut_DrawExpanding(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot16_Doughnut_InitVars = { + ACTOR_BG_SPOT16_DOUGHNUT, + ACTORCAT_PROP, + FLAGS, + OBJECT_EFC_DOUGHNUT, + sizeof(BgSpot16Doughnut), + (ActorFunc)BgSpot16Doughnut_Init, + (ActorFunc)BgSpot16Doughnut_Destroy, + (ActorFunc)BgSpot16Doughnut_Update, + (ActorFunc)BgSpot16Doughnut_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 5500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 5000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 5000, ICHAIN_STOP), +}; + +static s16 sScales[] = { + 0, 0, 70, 210, 300, +}; + +void BgSpot16Doughnut_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Doughnut* this = (BgSpot16Doughnut*)thisx; + s32 params; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.1f); + this->fireFlag = 0; + this->envColorAlpha = 255; + params = this->actor.params; + if (params == 1 || params == 2 || params == 3 || params == 4) { + Actor_SetScale(&this->actor, sScales[this->actor.params] * 1.0e-4f); + this->actor.draw = BgSpot16Doughnut_DrawExpanding; + this->actor.update = BgSpot16Doughnut_UpdateExpanding; + } else { + // Scales this actor for scenes where it is featured in the background, + // Death Mountain itself falls into the default case. + switch (globalCtx->sceneNum) { + case SCENE_SPOT01: + Actor_SetScale(&this->actor, 0.04f); + break; + case SCENE_SHRINE: + case SCENE_SHRINE_N: + case SCENE_SHRINE_R: + Actor_SetScale(&this->actor, 0.018f); + break; + default: + Actor_SetScale(&this->actor, 0.1f); + break; + } + osSyncPrintf(VT_FGCOL(CYAN) "%f" VT_RST "\n", this->actor.scale.x); + if (!LINK_IS_ADULT || gSaveContext.eventChkInf[2] & 0x8000) { + this->fireFlag &= ~1; + } else { + this->fireFlag |= 1; + } + osSyncPrintf("(spot16 ドーナツ雲)(arg_data 0x%04x)\n", this->actor.params); + } +} + +void BgSpot16Doughnut_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgSpot16Doughnut_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Doughnut* this = (BgSpot16Doughnut*)thisx; + + if (!(this->fireFlag & 1)) { + this->actor.shape.rot.y -= 0x20; + if (this->envColorAlpha < 255) { + this->envColorAlpha += 5; + } else { + this->envColorAlpha = 255; + } + } else if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[2] != NULL && + globalCtx->csCtx.npcActions[2]->action == 2) { + if (this->envColorAlpha >= 6) { + this->envColorAlpha -= 5; + } else { + this->envColorAlpha = 0; + this->fireFlag &= ~1; + } + } +} + +// Update function for outwardly expanding and dissipating +void BgSpot16Doughnut_UpdateExpanding(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Doughnut* this = (BgSpot16Doughnut*)thisx; + + if (this->envColorAlpha >= 6) { + this->envColorAlpha -= 5; + } else { + Actor_Kill(&this->actor); + } + this->actor.shape.rot.y -= 0x20; + Actor_SetScale(&this->actor, this->actor.scale.x + 0.0019999998f); +} + +void BgSpot16Doughnut_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Doughnut* this = (BgSpot16Doughnut*)thisx; + u32 scroll = globalCtx->gameplayFrames & 0xFFFF; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot16_doughnut.c", 210); + + func_80093D84(globalCtx->state.gfxCtx); + + if (1) {} + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot16_doughnut.c", 213), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->fireFlag & 1) { + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * (-1), 0, 16, 32, 1, scroll, scroll * (-2), 16, 32)); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, this->envColorAlpha); + gSPDisplayList(POLY_XLU_DISP++, gDeathMountainCloudCircleFieryDL); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->envColorAlpha); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gSPDisplayList(POLY_XLU_DISP++, gDeathMountainCloudCircleNormalDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot16_doughnut.c", 238); +} + +// Draw function for outwardly expanding and dissipating +void BgSpot16Doughnut_DrawExpanding(Actor* thisx, GlobalContext* globalCtx) { + BgSpot16Doughnut* this = (BgSpot16Doughnut*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot16_doughnut.c", 245); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot16_doughnut.c", 248), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->envColorAlpha); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gSPDisplayList(POLY_XLU_DISP++, gDeathMountainCloudCircleNormalDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot16_doughnut.c", 256); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot16_Doughnut/z_bg_spot16_doughnut.h b/soh/src/overlays/actors/ovl_Bg_Spot16_Doughnut/z_bg_spot16_doughnut.h new file mode 100644 index 000000000..051e45cf5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot16_Doughnut/z_bg_spot16_doughnut.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_SPOT16_DOUGHNUT_H +#define Z_BG_SPOT16_DOUGHNUT_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot16Doughnut; + +typedef struct BgSpot16Doughnut { + /* 0x0000 */ Actor actor; + /* 0x014C */ u16 fireFlag; + /* 0x014E */ char pad[0x02]; + /* 0x0150 */ u8 envColorAlpha; +} BgSpot16Doughnut; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot17_Bakudankabe/z_bg_spot17_bakudankabe.c b/soh/src/overlays/actors/ovl_Bg_Spot17_Bakudankabe/z_bg_spot17_bakudankabe.c new file mode 100644 index 000000000..dbcb1cdcc --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot17_Bakudankabe/z_bg_spot17_bakudankabe.c @@ -0,0 +1,156 @@ +/* + * File: z_bg_spot17_bakudankabe.c + * Overlay: ovl_Bg_Spot17_Bakudankabe + * Description: Death Mountain Crater Bombable Wall + */ + +#include "z_bg_spot17_bakudankabe.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_spot17_obj/object_spot17_obj.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS 0 + +void BgSpot17Bakudankabe_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot17Bakudankabe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot17Bakudankabe_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot17Bakudankabe_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot17_Bakudankabe_InitVars = { + ACTOR_BG_SPOT17_BAKUDANKABE, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT17_OBJ, + sizeof(BgSpot17Bakudankabe), + (ActorFunc)BgSpot17Bakudankabe_Init, + (ActorFunc)BgSpot17Bakudankabe_Destroy, + (ActorFunc)BgSpot17Bakudankabe_Update, + (ActorFunc)BgSpot17Bakudankabe_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 3000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void func_808B6BC0(BgSpot17Bakudankabe* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 i; + Vec3f burstDepthY; + Vec3f burstDepthX; + f32 sinY; + f32 cosY; + + sinY = Math_SinS(this->dyna.actor.shape.rot.y); + cosY = Math_CosS(this->dyna.actor.shape.rot.y); + + burstDepthX.z = 0.0f; + burstDepthX.x = 0.0f; + + for (i = 0; i < 20; i++) { + s16 gravityInfluence; + s16 scale; + f32 temp1; + f32 temp2; + s32 rotationSpeed; + + temp1 = (Rand_ZeroOne() - 0.5f) * 140.0f; + temp2 = (Rand_ZeroOne() - 0.5f) * 20.0f; + + burstDepthY.x = this->dyna.actor.world.pos.x + temp2 * sinY + (temp1 * cosY); + burstDepthY.y = this->dyna.actor.world.pos.y + 30.0f + (i * 6.5f); + burstDepthY.z = this->dyna.actor.world.pos.z + temp2 * cosY - (temp1 * sinY); + + burstDepthX.y = (Rand_ZeroOne() - 0.2f) * 12.0f; + scale = Rand_ZeroOne() * 55.0f + 8.0f; + + if (scale < 20) { + gravityInfluence = -300; + } else if (scale < 35) { + gravityInfluence = -360; + } else { + gravityInfluence = -420; + } + + if (Rand_ZeroOne() < 0.4f) { + rotationSpeed = 65; + } else { + rotationSpeed = 33; + } + EffectSsKakera_Spawn(globalCtx, &burstDepthY, &burstDepthX, &burstDepthY, gravityInfluence, rotationSpeed, 0x1E, + 4, 0, scale, 1, 3, 80, KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_FIELD_KEEP, gFieldKakeraDL); + } + Math_Vec3f_Copy(&burstDepthY, &this->dyna.actor.world.pos); + func_80033480(globalCtx, &burstDepthY, 60.0f, 4, 110, 160, 1); + burstDepthY.y += 40.0f; + func_80033480(globalCtx, &burstDepthY, 60.0f, 4, 120, 160, 1); + burstDepthY.y += 40.0f; + func_80033480(globalCtx, &burstDepthY, 60.0f, 4, 110, 160, 1); +} + +void BgSpot17Bakudankabe_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot17Bakudankabe* this = (BgSpot17Bakudankabe*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params & 0x3F))) { + Actor_Kill(&this->dyna.actor); + return; + } + + CollisionHeader_GetVirtual(&gCraterBombableWallCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); +} + +void BgSpot17Bakudankabe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot17Bakudankabe* this = (BgSpot17Bakudankabe*)thisx; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgSpot17Bakudankabe_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot17Bakudankabe* this = (BgSpot17Bakudankabe*)thisx; + if (this->dyna.actor.xzDistToPlayer < 650.0f && func_80033684(globalCtx, &this->dyna.actor) != NULL) { + func_808B6BC0(this, globalCtx); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params & 0x3F)); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 40, NA_SE_EV_WALL_BROKEN); + func_80078884(NA_SE_SY_CORRECT_CHIME); + Actor_Kill(&this->dyna.actor); + } +} + +void BgSpot17Bakudankabe_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + s8 r = coss(globalCtx->gameplayFrames * 1500) >> 8; + s8 g = coss(globalCtx->gameplayFrames * 1500) >> 8; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot17_bakudankabe.c", 269); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot17_bakudankabe.c", 273), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + r = (r >> 1) + 0xC0; + g = (g >> 1) + 0xC0; + + gDPSetEnvColor(POLY_OPA_DISP++, r, g, 255, 128); + + gSPDisplayList(POLY_OPA_DISP++, gCraterBombableWallDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot17_bakudankabe.c", 283); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot17_bakudankabe.c", 286); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot17_bakudankabe.c", 290), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCraterBombableWallCracksDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot17_bakudankabe.c", 295); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot17_Bakudankabe/z_bg_spot17_bakudankabe.h b/soh/src/overlays/actors/ovl_Bg_Spot17_Bakudankabe/z_bg_spot17_bakudankabe.h new file mode 100644 index 000000000..892843540 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot17_Bakudankabe/z_bg_spot17_bakudankabe.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_SPOT17_BAKUDANKABE_H +#define Z_BG_SPOT17_BAKUDANKABE_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot17Bakudankabe; + +typedef struct BgSpot17Bakudankabe { + /* 0x0000 */ DynaPolyActor dyna; +} BgSpot17Bakudankabe; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot17_Funen/z_bg_spot17_funen.c b/soh/src/overlays/actors/ovl_Bg_Spot17_Funen/z_bg_spot17_funen.c new file mode 100644 index 000000000..cba00884f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot17_Funen/z_bg_spot17_funen.c @@ -0,0 +1,75 @@ +/* + * File: z_bg_spot17_funen + * Overlay: ovl_Bg_Spot17_Funen + * Description: Crater Smoke Cone + */ + +#include "z_bg_spot17_funen.h" +#include "objects/object_spot17_obj/object_spot17_obj.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSpot17Funen_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot17Funen_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot17Funen_Update(Actor* thisx, GlobalContext* globalCtx); +void func_808B746C(Actor* thisx, GlobalContext* globalCtx); +void func_808B7478(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot17_Funen_InitVars = { + ACTOR_BG_SPOT17_FUNEN, + ACTORCAT_SWITCH, + FLAGS, + OBJECT_SPOT17_OBJ, + sizeof(BgSpot17Funen), + (ActorFunc)BgSpot17Funen_Init, + (ActorFunc)BgSpot17Funen_Destroy, + (ActorFunc)BgSpot17Funen_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot17Funen_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot17Funen* this = (BgSpot17Funen*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + osSyncPrintf("spot17 obj. 噴煙 (arg_data 0x%04x)\n", this->actor.params); +} + +void BgSpot17Funen_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgSpot17Funen_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot17Funen* this = (BgSpot17Funen*)thisx; + + this->actor.draw = func_808B7478; + this->actor.update = func_808B746C; +} + +void func_808B746C(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_808B7478(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot17_funen.c", 153); + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - thisx->shape.rot.y + 0x8000) * + 9.58738019108e-05f, + MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_spot17_funen.c", 161), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (0 - globalCtx->gameplayFrames) & 0x7F, 0x20, 0x20, 1, 0, + (0 - globalCtx->gameplayFrames) & 0x7F, 0x20, 0x20)); + gSPDisplayList(POLY_XLU_DISP++, gCraterSmokeConeDL); + + if (1) {} + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_spot17_funen.c", 176); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot17_Funen/z_bg_spot17_funen.h b/soh/src/overlays/actors/ovl_Bg_Spot17_Funen/z_bg_spot17_funen.h new file mode 100644 index 000000000..6a44fa14c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot17_Funen/z_bg_spot17_funen.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_SPOT17_FUNEN_H +#define Z_BG_SPOT17_FUNEN_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot17Funen; + +typedef struct BgSpot17Funen { + /* 0x0000 */ Actor actor; +} BgSpot17Funen; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Basket/z_bg_spot18_basket.c b/soh/src/overlays/actors/ovl_Bg_Spot18_Basket/z_bg_spot18_basket.c new file mode 100644 index 000000000..2e9297e4c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Basket/z_bg_spot18_basket.c @@ -0,0 +1,458 @@ +#include "z_bg_spot18_basket.h" +#include "objects/object_spot18_obj/object_spot18_obj.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgSpot18Basket_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Basket_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Basket_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Basket_Draw(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Basket_Reset(void); + +void func_808B7BCC(BgSpot18Basket* this, GlobalContext* globalCtx); +void func_808B7AEC(BgSpot18Basket* this); +void func_808B7B58(BgSpot18Basket* this); +void func_808B7BB0(BgSpot18Basket* this); +void func_808B7D38(BgSpot18Basket* this); +void func_808B7F74(BgSpot18Basket* this); +void func_808B818C(BgSpot18Basket* this); +void func_808B7AFC(BgSpot18Basket* this, GlobalContext* globalCtx); +void func_808B7B6C(BgSpot18Basket* this, GlobalContext* globalCtx); +void func_808B7D50(BgSpot18Basket* this, GlobalContext* globalCtx); +void func_808B7FC0(BgSpot18Basket* this, GlobalContext* globalCtx); +void func_808B81A0(BgSpot18Basket* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot18_Basket_InitVars = { + ACTOR_BG_SPOT18_BASKET, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT18_OBJ, + sizeof(BgSpot18Basket), + (ActorFunc)BgSpot18Basket_Init, + (ActorFunc)BgSpot18Basket_Destroy, + (ActorFunc)BgSpot18Basket_Update, + (ActorFunc)BgSpot18Basket_Draw, + (ActorResetFunc)BgSpot18Basket_Reset, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 2040, 0 }, 54 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 1, { { 0, 1400, 0 }, 13 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 2, + sJntSphElementsInit, +}; + +static s16 D_808B85C8[] = { 0x8000, 0x2AAA, 0xD555, 0x0000 }; + +void func_808B7710(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Basket* this = (BgSpot18Basket*)thisx; + + Collider_InitJntSph(globalCtx, &this->colliderJntSph); + Collider_SetJntSph(globalCtx, &this->colliderJntSph, &this->dyna.actor, &sJntSphInit, this->ColliderJntSphElements); + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; +} + +static s16 D_808B85D0 = 0; +void func_808B7770(BgSpot18Basket* this, GlobalContext* globalCtx, f32 arg2) { + Vec3f acceleration; + Vec3f velocity; + Vec3f position; + f32 cosValue; + s32 i; + f32 randomValue; + f32 sinValue; + s32 count; + + for (i = 0, count = 2; i != count; i++) { + if (globalCtx) {} + if (!(arg2 < Rand_ZeroOne())) { + D_808B85D0 += 0x7530; + + sinValue = Math_SinS(D_808B85D0); + cosValue = Math_CosS(D_808B85D0); + + randomValue = (Rand_ZeroOne() * 35.0f) + 35.0f; + + position.x = (randomValue * sinValue) + this->dyna.actor.world.pos.x; + position.y = this->dyna.actor.world.pos.y + 10.0f; + position.z = (randomValue * cosValue) + this->dyna.actor.world.pos.z; + + velocity.x = sinValue; + velocity.y = 0.0f; + velocity.z = cosValue; + + acceleration.x = 0.0f; + acceleration.y = 0.5f; + acceleration.z = 0.0f; + + func_800286CC(globalCtx, &position, &velocity, &acceleration, ((Rand_ZeroOne() * 16) + 80), + ((Rand_ZeroOne() * 30) + 80)); + } + } +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgSpot18Basket_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSpot18Basket* this = (BgSpot18Basket*)thisx; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK3); + func_808B7710(&this->dyna.actor, globalCtx); + CollisionHeader_GetVirtual(&gGoronCityVaseCol, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + ActorShape_Init(&this->dyna.actor.shape, 0.0f, ActorShadow_DrawCircle, 15.0f); + this->dyna.actor.home.pos.y += 0.01f; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + func_808B7BB0(this); + return; + } + + func_808B7AEC(this); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_BG_SPOT18_FUTA, + this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + this->dyna.actor.shape.rot.x, this->dyna.actor.shape.rot.y + 0x1555, + this->dyna.actor.shape.rot.z, -1); + + if (this->dyna.actor.child == NULL) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Error : 変化壷蓋発生失敗(%s %d)\n", "../z_bg_spot18_basket.c", 351); + osSyncPrintf(VT_RST); + Actor_Kill(&this->dyna.actor); + } +} + +void BgSpot18Basket_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Basket* this = (BgSpot18Basket*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyJntSph(globalCtx, &this->colliderJntSph); +} + +void func_808B7AEC(BgSpot18Basket* this) { + this->actionFunc = func_808B7AFC; +} + +void func_808B7AFC(BgSpot18Basket* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + OnePointCutscene_Init(globalCtx, 4220, 80, &this->dyna.actor, MAIN_CAM); + func_808B7B58(this); + } +} + +void func_808B7B58(BgSpot18Basket* this) { + this->actionFunc = func_808B7B6C; + this->unk_216 = 0; +} + +void func_808B7B6C(BgSpot18Basket* this, GlobalContext* globalCtx) { + if (this->unk_216 > 20) { + func_808B7BB0(this); + this->dyna.actor.child->parent = NULL; + this->dyna.actor.child = NULL; + } +} + +void func_808B7BB0(BgSpot18Basket* this) { + this->actionFunc = func_808B7BCC; + this->unk_210 = this->unk_20C = 0; +} + +void func_808B7BCC(BgSpot18Basket* this, GlobalContext* globalCtx) { + f32 positionDiff; + Actor* colliderBaseAc; + + Math_StepToS(&this->unk_210, 0x1F4, 0x1E); + + this->dyna.actor.shape.rot.y += this->unk_210; + + Math_StepToF(&this->unk_208, 50.0f, 1.5f); + Math_StepToS(&this->unk_20C, 400, 15); + + this->unk_20E += this->unk_20C; + + this->dyna.actor.world.pos.x = (Math_SinS(this->unk_20E) * this->unk_208) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->unk_20E) * this->unk_208) + this->dyna.actor.home.pos.z; + + if (this->colliderJntSph.base.acFlags & AC_HIT) { + colliderBaseAc = this->colliderJntSph.base.ac; + + if (colliderBaseAc != NULL) { + positionDiff = colliderBaseAc->world.pos.y - this->dyna.actor.world.pos.y; + + if (positionDiff > 120.0f && positionDiff < 200.0f) { + if (Math3D_Dist2DSq(colliderBaseAc->world.pos.z, this->colliderJntSph.base.ac->world.pos.x, + this->dyna.actor.world.pos.z, this->dyna.actor.world.pos.x) < SQ(32.0f)) { + OnePointCutscene_Init(globalCtx, 4210, 240, &this->dyna.actor, MAIN_CAM); + func_808B7D38(this); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + } + } + } + func_8002F974(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); +} + +void func_808B7D38(BgSpot18Basket* this) { + this->actionFunc = func_808B7D50; + this->unk_216 = 0; + this->unk_214 = 0; +} + +void func_808B7D50(BgSpot18Basket* this, GlobalContext* globalCtx) { + f32 tempValue2; + f32 tempValue; + + if (this->unk_216 > 120) { + Math_StepToS(&this->unk_210, 0x3E8, 0x32); + } else { + Math_StepToS(&this->unk_210, 0xBB8, 0x64); + } + + this->dyna.actor.shape.rot.y = this->dyna.actor.shape.rot.y + this->unk_210; + + if (this->unk_216 < 70) { + Math_StepToF(&this->unk_208, 100.0f, 2.0f); + } else { + Math_StepToF(&this->unk_208, 0.0f, 2.0f); + } + + Math_StepToS(&this->unk_20C, 1000, 20); + + this->unk_20E += this->unk_20C; + + this->dyna.actor.world.pos.x = (Math_SinS(this->unk_20E) * this->unk_208) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->unk_20E) * this->unk_208) + this->dyna.actor.home.pos.z; + + this->unk_212 += 0xBB8; + + Math_StepToS(&this->unk_214, 0x5DC, 0x1E); + + this->dyna.actor.shape.rot.x = Math_CosS(this->unk_212) * this->unk_214; + this->dyna.actor.shape.rot.z = -Math_SinS(this->unk_212) * this->unk_214; + + if (this->unk_216 > 140) { + func_808B7F74(this); + } + + if (this->unk_216 < 80) { + func_808B7770(this, globalCtx, 1.0f); + } else { + func_808B7770(this, globalCtx, 0.8f); + } + + tempValue2 = (this->unk_210 - 500) * 0.0006f; + + tempValue = CLAMP(tempValue2, 0.0f, 1.5f); + + func_800F436C(&this->dyna.actor.projectedPos, NA_SE_EV_WALL_MOVE_SP - SFX_FLAG, tempValue); +} + +void func_808B7F74(BgSpot18Basket* this) { + s16 shapeRotY; + + shapeRotY = this->dyna.actor.shape.rot.y; + this->actionFunc = func_808B7FC0; + + if ((shapeRotY < -0x2E93) || (shapeRotY >= 0x7C19)) { + this->unk_218 = 2; + } else if (shapeRotY < 0x26C2) { + this->unk_218 = 1; + } else { + this->unk_218 = 0; + } + + this->unk_216 = 0; +} + +void func_808B7FC0(BgSpot18Basket* this, GlobalContext* globalCtx) { + s32 pad; + s32 tempUnk214; + f32 tempUnk210; + s16 arrayValue; + f32 clampedTempUnk210; + + this->unk_212 += 0xBB8; + + if (this->unk_216 >= 13) { + tempUnk214 = Math_StepToS(&this->unk_214, 0, 55); + } else { + tempUnk214 = 0; + } + + this->dyna.actor.shape.rot.x = Math_CosS(this->unk_212) * this->unk_214; + this->dyna.actor.shape.rot.z = -Math_SinS(this->unk_212) * this->unk_214; + + Math_StepToS(&this->unk_210, 0x1F4, 0xA); + this->dyna.actor.shape.rot.y += this->unk_210; + + if (tempUnk214 != 0) { + arrayValue = D_808B85C8[this->unk_218]; + + if ((s16)(this->dyna.actor.shape.rot.y - arrayValue) >= 0) { + this->dyna.actor.shape.rot.y = arrayValue; + + func_808B818C(this); + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + } + + if (this->unk_216 < 30) { + func_808B7770(this, globalCtx, 0.5f); + } else { + func_808B7770(this, globalCtx, 0.3f); + } + + tempUnk210 = (this->unk_210 - 500) * 0.0006f; + + clampedTempUnk210 = CLAMP(tempUnk210, 0.0f, 1.5f); + + func_800F436C(&this->dyna.actor.projectedPos, NA_SE_EV_WALL_MOVE_SP - SFX_FLAG, clampedTempUnk210); +} + +void func_808B818C(BgSpot18Basket* this) { + this->actionFunc = func_808B81A0; + this->unk_216 = 0; +} + +static s16 D_808B85E4[] = { -0x0FA0, 0x0320, 0x0FA0 }; + +void func_808B81A0(BgSpot18Basket* this, GlobalContext* globalCtx) { + s32 i; + Actor* actor = &this->dyna.actor; + Vec3f tempVector; + EnItem00* collectible; + + if (this->unk_216 == 1) { + tempVector.x = actor->world.pos.x; + tempVector.y = actor->world.pos.y + 170.0f; + tempVector.z = actor->world.pos.z; + + if (this->unk_218 == 0) { + for (i = 0; i < ARRAY_COUNT(D_808B85E4); i++) { + collectible = Item_DropCollectible(globalCtx, &tempVector, ITEM00_BOMBS_A); + if (collectible != NULL) { + collectible->actor.velocity.y = 11.0f; + collectible->actor.world.rot.y = D_808B85E4[i]; + } + } + } else if (this->unk_218 == 1) { + for (i = 0; i < ARRAY_COUNT(D_808B85E4); i++) { + collectible = Item_DropCollectible(globalCtx, &tempVector, ITEM00_RUPEE_GREEN); + if (collectible != NULL) { + collectible->actor.velocity.y = 11.0f; + collectible->actor.world.rot.y = D_808B85E4[i]; + } + } + } else if (this->unk_218 == 2) { + if ((this->unk_21A != 0) || Flags_GetCollectible(globalCtx, (actor->params & 0x3F))) { + collectible = Item_DropCollectible(globalCtx, &tempVector, ITEM00_RUPEE_PURPLE); + if (collectible != NULL) { + collectible->actor.velocity.y = 11.0f; + collectible->actor.world.rot.y = D_808B85E4[1]; + } + } else { + collectible = + Item_DropCollectible(globalCtx, &tempVector, ((actor->params & 0x3F) << 8) | ITEM00_HEART_PIECE); + if (collectible != NULL) { + collectible->actor.velocity.y = 11.0f; + collectible->actor.world.rot.y = D_808B85E4[1]; + this->unk_21A = 1; + } + } + + collectible = Item_DropCollectible(globalCtx, &tempVector, ITEM00_RUPEE_RED); + if (collectible != NULL) { + collectible->actor.velocity.y = 11.0f; + collectible->actor.world.rot.y = D_808B85E4[0]; + } + + collectible = Item_DropCollectible(globalCtx, &tempVector, ITEM00_RUPEE_BLUE); + if (collectible != NULL) { + collectible->actor.velocity.y = 11.0f; + collectible->actor.world.rot.y = D_808B85E4[2]; + } + } + } else if (this->unk_216 == 2) { + if (this->unk_218 == 2) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } else { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } else if (this->unk_216 == 200) { + func_808B7BB0(this); + } +} + +void BgSpot18Basket_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSpot18Basket* this = (BgSpot18Basket*)thisx; + s32 bgId; + + this->unk_216++; + this->actionFunc(this, globalCtx); + this->dyna.actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->dyna.actor.floorPoly, &bgId, + &this->dyna.actor, &this->dyna.actor.world.pos); + if (this->actionFunc != func_808B7AFC) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + if (this->actionFunc != func_808B7B6C) { + this->colliderJntSph.base.acFlags &= ~AC_HIT; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + } +} + +void BgSpot18Basket_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Basket* this = (BgSpot18Basket*)thisx; + + Collider_UpdateSpheres(0, &this->colliderJntSph); + Collider_UpdateSpheres(1, &this->colliderJntSph); + Gfx_DrawDListOpa(globalCtx, gGoronCityVaseDL); +} + +void BgSpot18Basket_Reset(void) { + D_808B85D0 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Basket/z_bg_spot18_basket.h b/soh/src/overlays/actors/ovl_Bg_Spot18_Basket/z_bg_spot18_basket.h new file mode 100644 index 000000000..74aaa72b9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Basket/z_bg_spot18_basket.h @@ -0,0 +1,29 @@ +#ifndef Z_BG_SPOT18_BASKET_H +#define Z_BG_SPOT18_BASKET_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot18Basket; + +typedef void (*BgSpot18BasketActionFunc)(struct BgSpot18Basket*, GlobalContext*); + +typedef struct BgSpot18Basket { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderJntSph colliderJntSph; + /* 0x0184 */ ColliderJntSphElement ColliderJntSphElements[2]; + /* 0x0204 */ BgSpot18BasketActionFunc actionFunc; + /* 0x0208 */ f32 unk_208; + /* 0x020C */ s16 unk_20C; + /* 0x020E */ s16 unk_20E; + /* 0x0210 */ s16 unk_210; + /* 0x0212 */ s16 unk_212; + /* 0x0214 */ s16 unk_214; + /* 0x0216 */ s16 unk_216; + /* 0x0218 */ s16 unk_218; + /* 0x021A */ u8 unk_21A; + /* 0x021B */ u8 unk_21B; +} BgSpot18Basket; // size = 0x021C + +#endif + diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Futa/z_bg_spot18_futa.c b/soh/src/overlays/actors/ovl_Bg_Spot18_Futa/z_bg_spot18_futa.c new file mode 100644 index 000000000..da278ee77 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Futa/z_bg_spot18_futa.c @@ -0,0 +1,72 @@ +/* + * File: z_bg_spot18_futa.c + * Overlay: ovl_Bg_Spot18_Futa + * Description: The lid to the spinning goron vase. + */ + +#include "z_bg_spot18_futa.h" +#include "objects/object_spot18_obj/object_spot18_obj.h" + +#define FLAGS 0 + +void BgSpot18Futa_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Futa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Futa_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Futa_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Spot18_Futa_InitVars = { + ACTOR_BG_SPOT18_FUTA, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT18_OBJ, + sizeof(BgSpot18Futa), + (ActorFunc)BgSpot18Futa_Init, + (ActorFunc)BgSpot18Futa_Destroy, + (ActorFunc)BgSpot18Futa_Update, + (ActorFunc)BgSpot18Futa_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void BgSpot18Futa_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Futa* this = (BgSpot18Futa*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gGoronCityVaseLidCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); +} + +void BgSpot18Futa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Futa* this = (BgSpot18Futa*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgSpot18Futa_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Futa* this = (BgSpot18Futa*)thisx; + s32 iVar1; + + if (this->dyna.actor.parent == NULL) { + iVar1 = Math_StepToF(&this->dyna.actor.scale.x, 0, 0.005); + + if (iVar1 != 0) { + Actor_Kill(&this->dyna.actor); + } else { + this->dyna.actor.scale.z = this->dyna.actor.scale.x; + this->dyna.actor.scale.y = this->dyna.actor.scale.x; + } + } +} + +void BgSpot18Futa_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gGoronCityVaseLidDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Futa/z_bg_spot18_futa.h b/soh/src/overlays/actors/ovl_Bg_Spot18_Futa/z_bg_spot18_futa.h new file mode 100644 index 000000000..1150611cd --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Futa/z_bg_spot18_futa.h @@ -0,0 +1,12 @@ +#ifndef Z_BG_SPOT18_FUTA_H +#define Z_BG_SPOT18_FUTA_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot18Futa; + +typedef struct BgSpot18Futa { + /* 0x0000 */ DynaPolyActor dyna; +} BgSpot18Futa; // size = 0x0164 +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Obj/z_bg_spot18_obj.c b/soh/src/overlays/actors/ovl_Bg_Spot18_Obj/z_bg_spot18_obj.c new file mode 100644 index 000000000..5833196d1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Obj/z_bg_spot18_obj.c @@ -0,0 +1,287 @@ + +/* + * File: z_bg_spot18_obj.c + * Overlay: ovl_Bg_Spot18_Obj + * Description: + */ + +#include "z_bg_spot18_obj.h" +#include "objects/object_spot18_obj/object_spot18_obj.h" + +#define FLAGS 0 + +void BgSpot18Obj_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Obj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Obj_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Obj_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 func_808B8910(BgSpot18Obj* this, GlobalContext* globalCtx); +s32 func_808B8A5C(BgSpot18Obj* this, GlobalContext* globalCtx); +s32 func_808B8A98(BgSpot18Obj* this, GlobalContext* globalCtx); +s32 func_808B8B08(BgSpot18Obj* this, GlobalContext* globalCtx); +s32 func_808B8BB4(BgSpot18Obj* this, GlobalContext* globalCtx); +s32 func_808B8C90(BgSpot18Obj* this, GlobalContext* globalCtx); +void func_808B8DC0(BgSpot18Obj* this); +void func_808B8DD0(BgSpot18Obj* this, GlobalContext* globalCtx); +void func_808B8E64(BgSpot18Obj* this); +void func_808B8E7C(BgSpot18Obj* this, GlobalContext* globalCtx); +void func_808B8EE0(BgSpot18Obj* this); +void func_808B8F08(BgSpot18Obj* this, GlobalContext* globalCtx); +void func_808B9030(BgSpot18Obj* this); +void func_808B9040(BgSpot18Obj* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot18_Obj_InitVars = { + ACTOR_BG_SPOT18_OBJ, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT18_OBJ, + sizeof(BgSpot18Obj), + (ActorFunc)BgSpot18Obj_Init, + (ActorFunc)BgSpot18Obj_Destroy, + (ActorFunc)BgSpot18Obj_Update, + (ActorFunc)BgSpot18Obj_Draw, + NULL, +}; + +static u8 D_808B90F0[2][2] = { { 0x01, 0x01 }, { 0x01, 0x00 } }; + +static f32 D_808B90F4[] = { + 0.1f, + 0.1f, +}; + +static CollisionHeader* D_808B90FC[] = { + &gGoronCityStatueCol, + &gGoronCityStatueSpearCol, +}; + +static u32 D_808B9104[] = { + 0, + 0, +}; + +static BgSpot18ObjInitFunc D_808B910C[] = { + func_808B8A98, + func_808B8910, + func_808B8A5C, + func_808B8B08, +}; + +static InitChainEntry sInitChain1[] = { + ICHAIN_F32(minVelocityY, -10, ICHAIN_CONTINUE), ICHAIN_F32(gravity, -4, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1400, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), +}; + +static InitChainEntry sInitChain2[] = { + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 700, ICHAIN_STOP), +}; + +static BgSpot18ObjInitFunc D_808B913C[] = { + func_808B8BB4, + func_808B8C90, +}; + +static Gfx(*sDlists[]) = { + gGoronCityStatueDL, + gGoronCityStatueSpearDL, +}; + +s32 func_808B8910(BgSpot18Obj* this, GlobalContext* globalCtx) { + s32 age; + + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + age = 1; + } else if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + age = 0; + } else { + osSyncPrintf("Error : リンク年齢不詳 (%s %d)(arg_data 0x%04x)\n", "../z_bg_spot18_obj.c", 182, + this->dyna.actor.params); + return 0; + } + + switch (D_808B90F0[this->dyna.actor.params & 0xF][age]) { + case 0: + case 1: + if (D_808B90F0[this->dyna.actor.params & 0xF][age] == 0) { + osSyncPrintf("出現しない Object (0x%04x)\n", this->dyna.actor.params); + } + return D_808B90F0[this->dyna.actor.params & 0xF][age]; + case 2: + osSyncPrintf("Error : Obj出現判定が設定されていない(%s %d)(arg_data 0x%04x)\n", "../z_bg_spot18_obj.c", 202, + this->dyna.actor.params); + break; + default: + osSyncPrintf("Error : Obj出現判定失敗(%s %d)(arg_data 0x%04x)\n", "../z_bg_spot18_obj.c", 210, + this->dyna.actor.params); + } + return 0; +} + +s32 func_808B8A5C(BgSpot18Obj* this, GlobalContext* globalCtx) { + Actor_SetScale(&this->dyna.actor, D_808B90F4[this->dyna.actor.params & 0xF]); + return 1; +} + +s32 func_808B8A98(BgSpot18Obj* this, GlobalContext* globalCtx) { + s32 pad[2]; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(D_808B90FC[this->dyna.actor.params & 0xF], &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + return 1; +} + +s32 func_808B8B08(BgSpot18Obj* this, GlobalContext* globalCtx) { + this->dyna.actor.flags |= D_808B9104[this->dyna.actor.params & 0xF]; + return 1; +} + +s32 func_808B8B38(BgSpot18Obj* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(D_808B910C); i++) { + if (D_808B910C[i](this, globalCtx) == 0) { + return 0; + } + } + return 1; +} + +s32 func_808B8BB4(BgSpot18Obj* this, GlobalContext* globalCtx) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain1); + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + func_808B9030(this); + } else if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + func_808B9030(this); + this->dyna.actor.world.pos.x = (Math_SinS(this->dyna.actor.world.rot.y) * 80.0f) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->dyna.actor.world.rot.y) * 80.0f) + this->dyna.actor.home.pos.z; + } else { + func_808B8E64(this); + } + return 1; +} + +s32 func_808B8C90(BgSpot18Obj* this, GlobalContext* globalCtx) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain2); + func_808B8DC0(this); + return 1; +} + +s32 func_808B8CC8(BgSpot18Obj* this, GlobalContext* globalCtx) { + if ((D_808B913C[this->dyna.actor.params & 0xF] != NULL) && + (!D_808B913C[this->dyna.actor.params & 0xF](this, globalCtx))) { + return 0; + } + return 1; +} + +void BgSpot18Obj_Init(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Obj* this = (BgSpot18Obj*)thisx; + + osSyncPrintf("Spot18 Object [arg_data : 0x%04x]\n", this->dyna.actor.params); + if (!func_808B8B38(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } else if (!func_808B8CC8(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgSpot18Obj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Obj* this = (BgSpot18Obj*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808B8DC0(BgSpot18Obj* this) { + this->actionFunc = func_808B8DD0; +} + +void func_808B8DD0(BgSpot18Obj* this, GlobalContext* globalCtx) { +} + +void func_808B8DDC(BgSpot18Obj* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 20.0f, 46.0f, 0.0f, 28); +} + +void func_808B8E20(BgSpot18Obj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (fabsf(this->dyna.unk_150) > 0.001f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } +} + +void func_808B8E64(BgSpot18Obj* this) { + this->unk_168 = 20; + this->actionFunc = func_808B8E7C; +} + +void func_808B8E7C(BgSpot18Obj* this, GlobalContext* globalCtx) { + if (this->dyna.unk_150 < -0.001f) { + if (this->unk_168 <= 0) { + func_808B8EE0(this); + } + } else { + this->unk_168 = 20; + } + func_808B8E20(this, globalCtx); +} + +void func_808B8EE0(BgSpot18Obj* this) { + this->actionFunc = func_808B8F08; + this->dyna.actor.world.rot.y = 0; + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.velocity.z = 0.0f; + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.velocity.x = 0.0f; +} + +void func_808B8F08(BgSpot18Obj* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + Math_StepToF(&this->dyna.actor.speedXZ, 1.2f, 0.1f); + Actor_MoveForward(&this->dyna.actor); + func_808B8DDC(this, globalCtx); + + if (Math3D_Dist2DSq(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.z, this->dyna.actor.home.pos.x, + this->dyna.actor.home.pos.z) >= 6400.0f) { + func_808B9030(this); + this->dyna.actor.world.pos.x = (Math_SinS(this->dyna.actor.world.rot.y) * 80.0f) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_CosS(this->dyna.actor.world.rot.y) * 80.0f) + this->dyna.actor.home.pos.z; + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F); + func_80078884(NA_SE_SY_CORRECT_CHIME); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); + } +} + +void func_808B9030(BgSpot18Obj* this) { + this->actionFunc = func_808B9040; +} + +void func_808B9040(BgSpot18Obj* this, GlobalContext* globalCtx) { + func_808B8E20(this, globalCtx); +} + +void BgSpot18Obj_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Obj* this = (BgSpot18Obj*)thisx; + + if (this->unk_168 > 0) { + this->unk_168 -= 1; + } + this->actionFunc(this, globalCtx); +} + +void BgSpot18Obj_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, sDlists[thisx->params & 0xF]); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Obj/z_bg_spot18_obj.h b/soh/src/overlays/actors/ovl_Bg_Spot18_Obj/z_bg_spot18_obj.h new file mode 100644 index 000000000..ee257022e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Obj/z_bg_spot18_obj.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_SPOT18_OBJ_H +#define Z_BG_SPOT18_OBJ_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot18Obj; + +typedef void (*BgSpot18ObjActionFunc)(struct BgSpot18Obj*, GlobalContext*); +typedef s32 (*BgSpot18ObjInitFunc)(struct BgSpot18Obj*, GlobalContext*); + +typedef struct BgSpot18Obj { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot18ObjActionFunc actionFunc; + /* 0x0168 */ s16 unk_168; +} BgSpot18Obj; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Shutter/z_bg_spot18_shutter.c b/soh/src/overlays/actors/ovl_Bg_Spot18_Shutter/z_bg_spot18_shutter.c new file mode 100644 index 000000000..77fd4b730 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Shutter/z_bg_spot18_shutter.c @@ -0,0 +1,141 @@ +/* + * File: z_bg_spot18_shutter.c + * Overlay: Bg_Spot18_Shutter + * Description: + */ + +#include "z_bg_spot18_shutter.h" +#include "objects/object_spot18_obj/object_spot18_obj.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSpot18Shutter_Init(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Shutter_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Shutter_Update(Actor* thisx, GlobalContext* globalCtx); +void BgSpot18Shutter_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808B95AC(BgSpot18Shutter* this, GlobalContext* globalCtx); +void func_808B95B8(BgSpot18Shutter* this, GlobalContext* globalCtx); +void func_808B9618(BgSpot18Shutter* this, GlobalContext* globalCtx); +void func_808B9698(BgSpot18Shutter* this, GlobalContext* globalCtx); +void func_808B971C(BgSpot18Shutter* this, GlobalContext* globalCtx); + +const ActorInit Bg_Spot18_Shutter_InitVars = { + ACTOR_BG_SPOT18_SHUTTER, + ACTORCAT_PROP, + FLAGS, + OBJECT_SPOT18_OBJ, + sizeof(BgSpot18Shutter), + (ActorFunc)BgSpot18Shutter_Init, + (ActorFunc)BgSpot18Shutter_Destroy, + (ActorFunc)BgSpot18Shutter_Update, + (ActorFunc)BgSpot18Shutter_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgSpot18Shutter_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSpot18Shutter* this = (BgSpot18Shutter*)thisx; + s32 param = (this->dyna.actor.params >> 8) & 1; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + if (param == 0) { + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + if (gSaveContext.infTable[16] & 0x200) { + this->actionFunc = func_808B95AC; + this->dyna.actor.world.pos.y += 180.0f; + } else { + this->actionFunc = func_808B9618; + } + } else { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + this->actionFunc = func_808B95AC; + this->dyna.actor.world.pos.y += 180.0f; + } else { + this->actionFunc = func_808B95B8; + } + } + } else { + if (gSaveContext.infTable[16] & 0x200) { + this->dyna.actor.world.pos.x += 125.0f * Math_CosS(this->dyna.actor.world.rot.y); + this->dyna.actor.world.pos.z -= 125.0f * Math_SinS(this->dyna.actor.world.rot.y); + this->actionFunc = func_808B95AC; + } else { + this->actionFunc = func_808B9618; + } + } + + CollisionHeader_GetVirtual(&gGoronCityDoorCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgSpot18Shutter_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Shutter* this = (BgSpot18Shutter*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808B95AC(BgSpot18Shutter* this, GlobalContext* globalCtx) { +} + +void func_808B95B8(BgSpot18Shutter* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + Actor_SetFocus(&this->dyna.actor, 70.0f); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->actionFunc = func_808B9698; + } +} + +void func_808B9618(BgSpot18Shutter* this, GlobalContext* globalCtx) { + if (gSaveContext.infTable[16] & 0x200) { + Actor_SetFocus(&this->dyna.actor, 70.0f); + if (((this->dyna.actor.params >> 8) & 1) == 0) { + this->actionFunc = func_808B9698; + } else { + this->actionFunc = func_808B971C; + OnePointCutscene_Init(globalCtx, 4221, 140, &this->dyna.actor, MAIN_CAM); + } + } +} + +void func_808B9698(BgSpot18Shutter* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 180.0f, 1.44f)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONEDOOR_STOP); + this->actionFunc = func_808B95AC; + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_STONE_STATUE_OPEN - SFX_FLAG); + } +} + +void func_808B971C(BgSpot18Shutter* this, GlobalContext* globalCtx) { + f32 sin = Math_SinS(this->dyna.actor.world.rot.y); + f32 cos = Math_CosS(this->dyna.actor.world.rot.y); + s32 flag = true; + + flag &= Math_StepToF(&this->dyna.actor.world.pos.x, this->dyna.actor.home.pos.x + (125.0f * cos), fabsf(cos)); + flag &= Math_StepToF(&this->dyna.actor.world.pos.z, this->dyna.actor.home.pos.z - (125.0f * sin), fabsf(sin)); + + if (flag) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONEDOOR_STOP); + this->actionFunc = func_808B95AC; + } else { + func_8002F974(&this->dyna.actor, NA_SE_EV_STONE_STATUE_OPEN - SFX_FLAG); + } +} + +void BgSpot18Shutter_Update(Actor* thisx, GlobalContext* globalCtx) { + BgSpot18Shutter* this = (BgSpot18Shutter*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgSpot18Shutter_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gGoronCityDoorDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Spot18_Shutter/z_bg_spot18_shutter.h b/soh/src/overlays/actors/ovl_Bg_Spot18_Shutter/z_bg_spot18_shutter.h new file mode 100644 index 000000000..b24857b97 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Spot18_Shutter/z_bg_spot18_shutter.h @@ -0,0 +1,16 @@ +#ifndef Z_BG_SPOT18_SHUTTER_H +#define Z_BG_SPOT18_SHUTTER_H + +#include "ultra64.h" +#include "global.h" + +struct BgSpot18Shutter; + +typedef void (*BgSpot18ShutterActionFunc)(struct BgSpot18Shutter*, GlobalContext*); + +typedef struct BgSpot18Shutter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgSpot18ShutterActionFunc actionFunc; +} BgSpot18Shutter; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.c b/soh/src/overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.c new file mode 100644 index 000000000..d3438d21a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.c @@ -0,0 +1,139 @@ +/* + * File: z_bg_sst_floor.c + * Overlay: ovl_Bg_Sst_Floor + * Description: Bongo Bongo's drum + */ + +#include "z_bg_sst_floor.h" +#include "objects/object_sst/object_sst.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgSstFloor_Init(BgSstFloor* this, GlobalContext* globalCtx); +void BgSstFloor_Destroy(BgSstFloor* this, GlobalContext* globalCtx); +void BgSstFloor_Update(BgSstFloor* this, GlobalContext* globalCtx); +void BgSstFloor_Draw(BgSstFloor* this, GlobalContext* globalCtx); + +static s32 sUnkValues[] = { 0, 0, 0 }; // Unused, probably a zero vector + +const ActorInit Bg_Sst_Floor_InitVars = { + ACTOR_BG_SST_FLOOR, + ACTORCAT_BG, + FLAGS, + OBJECT_SST, + sizeof(BgSstFloor), + (ActorFunc)BgSstFloor_Init, + (ActorFunc)BgSstFloor_Destroy, + (ActorFunc)BgSstFloor_Update, + (ActorFunc)BgSstFloor_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale.x, 100, ICHAIN_STOP), +}; + +void BgSstFloor_Init(BgSstFloor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSstFloor* this = (BgSstFloor*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gBongoDrumCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgSstFloor_Destroy(BgSstFloor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSstFloor* this = (BgSstFloor*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgSstFloor_Update(BgSstFloor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgSstFloor* this = (BgSstFloor*)thisx; + Player* player = GET_PLAYER(globalCtx); + CollisionHeader* colHeader = SEGMENTED_TO_VIRTUAL(&gBongoDrumCol); + + colHeader = ResourceMgr_LoadColByName(colHeader); + + colHeader->vtxList = SEGMENTED_TO_VIRTUAL(colHeader->vtxList); + + if (1) {} + + if (func_80043590(&this->dyna) && (this->dyna.actor.yDistToPlayer < 1000.0f)) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_BOSS_BONGO); + } else { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + } + + if (func_8004356C(&this->dyna) && (player->fallDistance > 1000.0f)) { + this->dyna.actor.params = 1; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_SHADEST_TAIKO_HIGH); + } + + if (this->dyna.actor.params == BONGOFLOOR_HIT) { + Actor* item00 = globalCtx->actorCtx.actorLists[ACTORCAT_MISC].head; + f32 distFromRim; + f32 xzDist; + + this->drumAmp = 80; + this->dyna.actor.params = BONGOFLOOR_REST; + this->drumPhase = 28; + + if (func_8004356C(&this->dyna) && !(player->stateFlags1 & 0x6000)) { + distFromRim = 600.0f - this->dyna.actor.xzDistToPlayer; + if (distFromRim > 0.0f) { + if (distFromRim > 350.0f) { + distFromRim = 350.0f; + } + player->actor.bgCheckFlags &= ~1; + player->actor.velocity.y = 9.0f * distFromRim * (1.0f / 350.0f); + } + } + + while (item00 != NULL) { + if ((item00->id == ACTOR_EN_ITEM00) && (item00->world.pos.y == 0.0f)) { + xzDist = Actor_WorldDistXZToActor(&this->dyna.actor, item00); + distFromRim = 600.0f - xzDist; + if (xzDist < 600.0f) { + if (distFromRim > 350.0f) { + distFromRim = 350.0f; + } + item00->bgCheckFlags &= ~3; + item00->velocity.y = 9.0f * distFromRim * (1.0f / 350.0f); + } + } + item00 = item00->next; + } + } + this->drumHeight = sinf(this->drumPhase * (M_PI / 2)) * (-this->drumAmp); + Math_StepToS(&this->drumAmp, 0, 5); + + colHeader->vtxList[1].y = colHeader->vtxList[0].y = colHeader->vtxList[2].y = colHeader->vtxList[3].y = + colHeader->vtxList[4].y = colHeader->vtxList[7].y = colHeader->vtxList[9].y = colHeader->vtxList[11].y = + colHeader->vtxList[13].y = this->dyna.actor.home.pos.y + this->drumHeight; + + if (this->drumPhase != 0) { + this->drumPhase--; + } + if (1) {} + func_8003EE6C(globalCtx, &globalCtx->colCtx.dyna); +} + +void BgSstFloor_Draw(BgSstFloor* thisx, GlobalContext* globalCtx) { + BgSstFloor* this = (BgSstFloor*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_sst_floor.c", 277); + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Scale(1.0f, this->drumHeight * -0.0025f, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_sst_floor.c", 283), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gBongoDrumDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_sst_floor.c", 287); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h b/soh/src/overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h new file mode 100644 index 000000000..04c9dcab6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h @@ -0,0 +1,21 @@ +#ifndef Z_BG_SST_FLOOR_H +#define Z_BG_SST_FLOOR_H + +#include "ultra64.h" +#include "global.h" + +struct BgSstFloor; + +typedef struct BgSstFloor { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x014C */ s16 drumPhase; + /* 0x0166 */ s16 drumAmp; + /* 0x0168 */ s16 drumHeight; +} BgSstFloor; // size = 0x016C + +typedef enum { + /* 0 */ BONGOFLOOR_REST, + /* 1 */ BONGOFLOOR_HIT +} BgSstFloorParams; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Hikari/z_bg_toki_hikari.c b/soh/src/overlays/actors/ovl_Bg_Toki_Hikari/z_bg_toki_hikari.c new file mode 100644 index 000000000..290126bbf --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Hikari/z_bg_toki_hikari.c @@ -0,0 +1,185 @@ +/* + * File: z_bg_toki_hikari.c + * Overlay: ovl_Toki_Hikari + * Description: Temple of Time Windows + */ + +#include "z_bg_toki_hikari.h" +#include "objects/object_toki_objects/object_toki_objects.h" + +#define FLAGS ACTOR_FLAG_5 + +void BgTokiHikari_Init(Actor* thisx, GlobalContext* globalCtx); +void BgTokiHikari_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgTokiHikari_Update(Actor* thisx, GlobalContext* globalCtx); +void BgTokiHikari_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgTokiHikari_DoNothing(BgTokiHikari* this, GlobalContext* globalCtx); +void func_808BA018(BgTokiHikari* this, GlobalContext* globalCtx); +void func_808BA204(BgTokiHikari* this, GlobalContext* globalCtx); +void func_808BA22C(BgTokiHikari* this, GlobalContext* globalCtx); +void func_808BA274(BgTokiHikari* this, GlobalContext* globalCtx); +void func_808BA2CC(BgTokiHikari* this, GlobalContext* globalCtx); + +const ActorInit Bg_Toki_Hikari_InitVars = { + ACTOR_BG_TOKI_HIKARI, + ACTORCAT_BG, + FLAGS, + OBJECT_TOKI_OBJECTS, + sizeof(BgTokiHikari), + (ActorFunc)BgTokiHikari_Init, + (ActorFunc)BgTokiHikari_Destroy, + (ActorFunc)BgTokiHikari_Update, + (ActorFunc)BgTokiHikari_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void BgTokiHikari_Init(Actor* thisx, GlobalContext* globalCtx) { + BgTokiHikari* this = (BgTokiHikari*)thisx; + + switch (this->actor.params) { + case 0: + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actionFunc = BgTokiHikari_DoNothing; + break; + case 1: + if (!(gSaveContext.eventChkInf[4] & 0x800)) { + this->actionFunc = func_808BA204; + this->unk_14C = 0.0f; + } else { + Actor_Kill(&this->actor); + } + break; + } +} + +void BgTokiHikari_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgTokiHikari_DoNothing(BgTokiHikari* this, GlobalContext* globalCtx) { +} + +void BgTokiHikari_Update(Actor* thisx, GlobalContext* globalCtx) { + BgTokiHikari* this = (BgTokiHikari*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgTokiHikari_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgTokiHikari* this = (BgTokiHikari*)thisx; + + switch (this->actor.params) { + case 0: + func_808BA018(this, globalCtx); + break; + case 1: + func_808BA2CC(this, globalCtx); + break; + } +} + +void func_808BA018(BgTokiHikari* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 246); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 252), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (LINK_IS_ADULT) { + gSPDisplayList(POLY_OPA_DISP++, object_toki_objects_DL_008190); + } else { + gSPDisplayList(POLY_OPA_DISP++, object_toki_objects_DL_007E20); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames % 128, 64, 32)); + + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames % 128, 64, 32)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 278), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, object_toki_objects_DL_007EE0); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 284); +} + +void func_808BA204(BgTokiHikari* this, GlobalContext* globalCtx) { + if (globalCtx->roomCtx.unk_74[1] != 0) { + this->actionFunc = func_808BA22C; + } +} + +void func_808BA22C(BgTokiHikari* this, GlobalContext* globalCtx) { + if (this->unk_14C < 1.0f) { + this->unk_14C += 0.05f; + } else { + this->unk_14C = 1.0f; + this->actionFunc = func_808BA274; + } +} + +void func_808BA274(BgTokiHikari* this, GlobalContext* globalCtx) { + if (this->unk_14C > 0.2f) { + this->unk_14C -= 0.025f; + } else { + this->unk_14C = 0.0f; + Actor_Kill(&this->actor); + } +} + +void func_808BA2CC(BgTokiHikari* this, GlobalContext* globalCtx) { + s32 pad[2]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 350); + Matrix_Translate(0.0f, 276.0f, 1122.0f, MTXMODE_NEW); + Matrix_Scale(0.32f, 0.32f, this->unk_14C * 7.0f, MTXMODE_APPLY); + Matrix_RotateZ(M_PI, MTXMODE_APPLY); + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, this->unk_14C * 255.0f, (u8)(155.0f * this->unk_14C) + 100, + this->unk_14C * 255.0f, this->unk_14C * 255.0f); + + gDPSetEnvColor(POLY_XLU_DISP++, (u8)(this->unk_14C * 155.0f) + 100, (u8)(255.0f * this->unk_14C), 0, 128); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 382), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -2 * (globalCtx->gameplayFrames & 0x7F), 0, 0x20, 0x40, 1, + (globalCtx->gameplayFrames & 0x7F) * 4, 0, 0x20, 0x40)); + + gSPDisplayList(POLY_XLU_DISP++, object_toki_objects_DL_000880); + Matrix_Pop(); + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)(this->unk_14C * 200.0f)); + + gDPSetEnvColor(POLY_XLU_DISP++, (u8)(this->unk_14C * 255.0f), (u8)(this->unk_14C * 255.0f), + (u8)(this->unk_14C * 255.0f), (u8)(200.0f * this->unk_14C)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 415), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, object_toki_objects_DL_0009C0); + Matrix_Pop(); + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)(this->unk_14C * 200.0f)); + + gDPSetEnvColor(POLY_XLU_DISP++, (u8)(this->unk_14C * 255.0f), (u8)(this->unk_14C * 255.0f), + (u8)(this->unk_14C * 255.0f), (u8)(200.0f * this->unk_14C)); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 437), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayListOffset(POLY_XLU_DISP++, object_toki_objects_DL_0009C0, 10); + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_toki_hikari.c", 443); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Hikari/z_bg_toki_hikari.h b/soh/src/overlays/actors/ovl_Bg_Toki_Hikari/z_bg_toki_hikari.h new file mode 100644 index 000000000..865068b12 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Hikari/z_bg_toki_hikari.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_TOKI_HIKARI_H +#define Z_BG_TOKI_HIKARI_H + +#include "ultra64.h" +#include "global.h" + +struct BgTokiHikari; + +typedef void (*BgTokiHikariActionFunc)(struct BgTokiHikari*, GlobalContext*); + +typedef struct BgTokiHikari { + /* 0x0000 */ Actor actor; + /* 0x014C */ f32 unk_14C; + /* 0x0150 */ BgTokiHikariActionFunc actionFunc; +} BgTokiHikari; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c new file mode 100644 index 000000000..dd0b111a6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.c @@ -0,0 +1,180 @@ +/* + * File: z_bg_toki_swd.c + * Overlay: ovl_Bg_Toki_Swd + * Description: Master Sword (Contains Cutscenes) + */ + +#include "z_bg_toki_swd.h" +#include "objects/object_toki_objects/object_toki_objects.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgTokiSwd_Init(Actor* thisx, GlobalContext* globalCtx); +void BgTokiSwd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgTokiSwd_Update(Actor* thisx, GlobalContext* globalCtx); +void BgTokiSwd_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808BAF40(BgTokiSwd* this, GlobalContext* globalCtx); +void func_808BB0AC(BgTokiSwd* this, GlobalContext* globalCtx); +void func_808BB128(BgTokiSwd* this, GlobalContext* globalCtx); + +extern CutsceneData D_808BB2F0[]; +extern CutsceneData D_808BB7A0[]; +extern CutsceneData D_808BBD90[]; + +const ActorInit Bg_Toki_Swd_InitVars = { + ACTOR_BG_TOKI_SWD, + ACTORCAT_PROP, + FLAGS, + OBJECT_TOKI_OBJECTS, + sizeof(BgTokiSwd), + (ActorFunc)BgTokiSwd_Init, + (ActorFunc)BgTokiSwd_Destroy, + (ActorFunc)BgTokiSwd_Update, + (ActorFunc)BgTokiSwd_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 10, 70, 0, { 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 10, 35, 100, MASS_IMMOVABLE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 25, ICHAIN_STOP), +}; + +void BgTokiSwd_SetupAction(BgTokiSwd* this, BgTokiSwdActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgTokiSwd_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgTokiSwd* this = (BgTokiSwd*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.shape.yOffset = 800.0f; + BgTokiSwd_SetupAction(this, func_808BAF40); + + if (LINK_IS_ADULT) { + this->actor.draw = NULL; + } + + if (gSaveContext.sceneSetupIndex == 5) { + globalCtx->roomCtx.unk_74[0] = 0xFF; + } + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); +} + +void BgTokiSwd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgTokiSwd* this = (BgTokiSwd*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_808BAF40(BgTokiSwd* this, GlobalContext* globalCtx) { + if (((gSaveContext.eventChkInf[4] & 0x8000) == 0) && (gSaveContext.sceneSetupIndex < 4) && + Actor_IsFacingAndNearPlayer(&this->actor, 800.0f, 0x7530) && !Gameplay_InCsMode(globalCtx)) { + gSaveContext.eventChkInf[4] |= 0x8000; + globalCtx->csCtx.segment = D_808BBD90; + gSaveContext.cutsceneTrigger = 1; + } + if (!LINK_IS_ADULT || ((gSaveContext.eventChkInf[5] & 0x20))) { + if (Actor_HasParent(&this->actor, globalCtx)) { + if (!LINK_IS_ADULT) { + Item_Give(globalCtx, ITEM_SWORD_MASTER); + globalCtx->csCtx.segment = D_808BB2F0; + } else { + globalCtx->csCtx.segment = D_808BB7A0; + } + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_MASTER_SWORD); + gSaveContext.cutsceneTrigger = 1; + this->actor.parent = NULL; + BgTokiSwd_SetupAction(this, func_808BB0AC); + } else { + if (Actor_IsFacingPlayer(&this->actor, 0x2000)) { + func_8002F580(&this->actor, globalCtx); + } + } + } + if (gSaveContext.sceneSetupIndex == 5) { + if (globalCtx->roomCtx.unk_74[0] > 0) { + globalCtx->roomCtx.unk_74[0]--; + } else { + globalCtx->roomCtx.unk_74[0] = 0; + } + } +} + +void func_808BB0AC(BgTokiSwd* this, GlobalContext* globalCtx) { + Player* player; + + // if sword has a parent it has been pulled/placed from the pedestal + if (Actor_HasParent(&this->actor, globalCtx)) { + if (!LINK_IS_ADULT) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_PUTAWAY_STN); + this->actor.draw = NULL; // sword has been pulled, dont draw sword + } else { + this->actor.draw = BgTokiSwd_Draw; // sword has been placed, draw the master sword + } + BgTokiSwd_SetupAction(this, func_808BB128); + } else { + player = GET_PLAYER(globalCtx); + player->interactRangeActor = &this->actor; + } +} + +void func_808BB128(BgTokiSwd* this, GlobalContext* globalCtx) { + if (Flags_GetEnv(globalCtx, 1) && (globalCtx->roomCtx.unk_74[0] < 0xFF)) { + globalCtx->roomCtx.unk_74[0] += 5; + } +} + +void BgTokiSwd_Update(Actor* thisx, GlobalContext* globalCtx) { + BgTokiSwd* this = (BgTokiSwd*)thisx; + + this->actionFunc(this, globalCtx); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void BgTokiSwd_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BgTokiSwd* this = (BgTokiSwd*)thisx; + s32 pad[3]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_toki_swd.c", 727); + + func_80093D18(globalCtx->state.gfxCtx); + + func_8002EBCC(&this->actor, globalCtx, 0); + + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, -(globalCtx->gameplayFrames % 0x80), 32, 32)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_toki_swd.c", 742), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_toki_objects_DL_001BD0); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_toki_swd.c", 776); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.h b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.h new file mode 100644 index 000000000..1891110e7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd.h @@ -0,0 +1,17 @@ +#ifndef Z_BG_TOKI_SWD_H +#define Z_BG_TOKI_SWD_H + +#include "ultra64.h" +#include "global.h" + +struct BgTokiSwd; + +typedef void (*BgTokiSwdActionFunc)(struct BgTokiSwd*, GlobalContext*); + +typedef struct BgTokiSwd { + /* 0x0000 */ Actor actor; + /* 0x014C */ BgTokiSwdActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; +} BgTokiSwd; // size = 0x019C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_1.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_1.c new file mode 100644 index 000000000..16b6b2a8a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_1.c @@ -0,0 +1,79 @@ +#include "z_bg_toki_swd.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData D_808BB2F0[] = { + CS_BEGIN_CUTSCENE(9, 425), + CS_PLAYER_ACTION_LIST(1), + CS_PLAYER_ACTION(0x000C, 0, 256, 0x0000, 0x0000, 0x0000, 0, 54, 52, 0, 54, 52, 0.0f, 0.0f, 0.0f), + CS_LIGHTING_LIST(1), + CS_LIGHTING(0x0002, 110, 111, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFE3, 0xFFFFFFC5, 0x00000000, 0xFFFFFFE3, 0xFFFFFFC5), + CS_MISC_LIST(1), + CS_MISC(0x000A, 110, 111, 0x0000, 0x00000000, 0x00000000, 0x0000002E, 0xFFFFFFE6, 0x00000000, 0x0000002E, 0xFFFFFFE6, 0x00000000, 0x00000000, 0x00000000), + CS_TERMINATOR(TEMPLE_OF_TIME_AFTER_USE_MS, 230, 231), + CS_SCENE_TRANS_FX(0x0001, 210, 230), + CS_CAM_EYE_LIST(0, 241), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x018C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x019D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x01AE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x02A8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x007A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x0064), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -42, 72, -39, 0x0074), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -42, 72, -39, 0x005F), + CS_CAM_EYE_LIST(80, 406), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 95, -19, 0x2F73), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 95, -19, 0x6B2F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 95, -19, 0x6169), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 95, -19, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 95, -19, 0x0005), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 129, -34, 0x7961), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 173, -19, 0x742F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -17, 217, -20, 0x2E64), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -24, 328, -12, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -27, 509, -2, 0x2F73), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -41, 813, 10, 0x6B2F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -60, 1285, 38, 0x6D61), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -58, 1910, 91, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -53, 2526, 133, 0x0005), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -33, 3445, 193, 0x7961), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 3960, 225, 0x742F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 7, 4317, 245, 0x6565), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 7, 4316, 245, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 7, 4316, 245, 0x2F73), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, 7, 4316, 245, 0x6B2F), + CS_CAM_AT_LIST(0, 270), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x2F73), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x6B2F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x6169), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x0005), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x7961), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x742F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -20, 85, -9, 0x2E64), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, -20, 85, -9, 0x0000), + CS_CAM_AT_LIST(80, 425), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 112, 7, 0x2F73), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 112, 7, 0x6B2F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 112, 7, 0x6169), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 112, 7, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -1, 112, 7, 0x0005), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x7961), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 25, 60.0f, -1, 117, 7, 0x742F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 25, 60.0f, -1, 117, 7, 0x2E64), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x2F73), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x6B2F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x6D61), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x0005), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x7961), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x742F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x6565), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.0f, -1, 117, 7, 0x2F73), + CS_CAM_AT(CS_CMD_STOP, 0x00, 20, 60.0f, -1, 117, 7, 0x6B2F), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_2.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_2.c new file mode 100644 index 000000000..71abf191d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_2.c @@ -0,0 +1,99 @@ +#include "z_bg_toki_swd.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData D_808BB7A0[] = { + CS_BEGIN_CUTSCENE(9, 368), + CS_CAM_EYE_LIST(0, 126), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1, 101, -110, 0x616D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 101, -110, 0x6964), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 101, -110, 0x00FB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 101, -110, 0x0111), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 101, -109, 0x012D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 71, -110, 0x0033), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 72, -110, 0x00FB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 72, -110, 0x00FB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.0f, -1, 72, -110, 0x2F68), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 40.0f, -1, 72, -110, 0x612F), + CS_CAM_EYE_LIST(58, 339), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -9, 106, -3, 0xA1BC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 106, -3, 0xA5F3), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 106, -3, 0xA5EB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 106, -3, 0xA5A2), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -67, 108, -53, 0xBAEE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -67, 108, -53, 0xC9B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -67, 108, -53, 0x0020), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -67, 108, -53, 0xA5A4), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -67, 108, -53, 0x0020), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -83, 102, -42, 0xA5E1), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -116, 81, -19, 0xA5D0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -118, 81, 54, 0x0020), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -106, 78, 116, 0xA5A6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -27, 75, 226, 0xA5E5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 121, 82, 312, 0xC0AE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 296, 76, 374, 0xA5E5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 565, 80, 257, 0x0020), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 614, 80, -1, 0xA5EB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 594, 80, -145, 0xA5E1), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 475, 80, -380, 0x0020), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 323, 80, -513, 0xA5E1), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 192, 80, -574, 0xA5D0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -45, 80, -604, 0x0020), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -142, 80, -589, 0xA5A6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -228, 80, -562, 0xA5E5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -376, 93, -477, 0xC0AE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -584, 95, -183, 0xA5E5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -613, 95, -34, 0x0020), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -595, 95, 161, 0xA5EB), + CS_CAM_AT_LIST(0, 155), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 40.4f, -1, 111, 5, 0xA1BC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 40.0f, -1, 111, 5, 0xA5F3), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 40.0f, -1, 111, 5, 0xA5EB), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 40.0f, -1, 111, 5, 0xA5A2), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 40.0f, -1, 111, 5, 0xBAEE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 40.0f, -1, 104, 8, 0xC9B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 40.0f, -1, 104, 8, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.0f, -1, 104, 8, 0xA5A4), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.0f, -1, 104, 8, 0x0020), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 40.0f, -1, 104, 8, 0xA5E1), + CS_CAM_AT_LIST(58, 368), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.000004f, 0, 120, 12, 0xA1BC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, 0, 120, 12, 0xA5F3), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, 0, 120, 12, 0xA5EB), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, 0, 120, 12, 0xA5A2), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 95, 11, 0xBAEE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 95, 11, 0xC9B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 95, 11, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 95, 11, 0xA5A4), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 60.0f, -1, 95, 11, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5E1), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5D0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5A6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5E5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xC0AE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5E5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5EB), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0xA5E1), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, -3, 95, 8, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0xA5E1), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0xA5D0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0x0020), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0xA5A6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0xA5E5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0xC0AE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0xA5E5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0f, -3, 95, 8, 0x0020), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, -3, 95, 8, 0xA5EB), + CS_PLAYER_ACTION_LIST(1), + CS_PLAYER_ACTION(0x000C, 0, 180, 0x0000, 0x0000, 0x0000, 0, 28, -10, 0, -14, 9, 0.0f, -0.23333333f, 0.0f), + CS_LIGHTING_LIST(1), + CS_LIGHTING(0x0002, 90, 91, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFF2, 0x00000024, 0x00000000, 0xFFFFFFF2, 0x00000024), + CS_MISC_LIST(1), + CS_MISC(0x000A, 90, 91, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFF8, 0xFFFFFFDD, 0x00000000, 0xFFFFFFF8, 0xFFFFFFDD, 0x00000000, 0x00000000, 0x00000000), + CS_SCENE_TRANS_FX(0x0001, 190, 210), + CS_TERMINATOR(TEMPLE_OF_TIME_AFTER_USE_MS, 210, 211), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_3.c b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_3.c new file mode 100644 index 000000000..724dc8fe6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Toki_Swd/z_bg_toki_swd_cutscene_data_3.c @@ -0,0 +1,96 @@ +#include "z_bg_toki_swd.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData D_808BBD90[] = { + CS_BEGIN_CUTSCENE(11, 3000), + CS_UNK_DATA_LIST(0x00000021, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFF8, 0xFFFFFFFF, 0x00000000, 0xFFFFFFF8, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x0005, 0, 1, 0x0000, 0x8000, 0x0000, 0, 0, 820, 0, 0, 820, 0.0f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0002, 1, 16, 0x0000, 0x8000, 0x0000, 0, 0, 820, 0, 0, 720, 0.0f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0005, 16, 176, 0x0000, 0x8000, 0x0000, 0, 0, 720, 0, 0, 720, 0.0f, 0.0f, 1.4E-45f), + CS_NPC_ACTION_LIST(62, 3), + CS_NPC_ACTION(0x0004, 40, 70, 0x7D74, 0x0000, 0x0000, -1, 49, 719, 1, 47, 687, 0.06666667f, -0.06666667f, -0.06666667f), + CS_NPC_ACTION(0x0004, 70, 220, 0x8010, 0x0000, 0x0000, 1, 47, 687, 0, 134, 21, -0.006666667f, 0.58f, 0.006666667f), + CS_NPC_ACTION(0x0002, 220, 272, 0x8000, 0x0000, 0x0000, 0, 134, 21, 0, 101, 2, 0.0f, -0.63461536f, 0.0f), + CS_MISC_LIST(1), + CS_MISC(0x000C, 340, 341, 0x0000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFD, 0x00000000, 0x00000000, 0x00000000), + CS_TEXT_LIST(6), + CS_TEXT_NONE(0, 50), + CS_TEXT_DISPLAY_TEXTBOX(0x70E6, 50, 60, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(60, 250), + CS_TEXT_DISPLAY_TEXTBOX(0x70E7, 250, 260, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(260, 290), + CS_TEXT_DISPLAY_TEXTBOX(0x70E8, 290, 320, 0x0000, 0x0000, 0x0000), + CS_CAM_EYE_LIST(0, 251), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -59, 12, 690, 0x0222), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -59, 12, 690, 0x00FA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -59, 12, 690, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -59, 12, 690, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -59, 12, 690, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -31, 24, 701, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -31, 24, 701, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -31, 24, 701, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -31, 24, 701, 0x2D70), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -31, 24, 701, 0x0085), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -31, 24, 701, 0x01E6), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -31, 24, 701, 0x0000), + CS_CAM_EYE_LIST(90, 331), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0222), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x00FA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -154, 92, 236, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -154, 92, 236, 0x2D70), + CS_CAM_EYE_LIST(220, 491), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 122, 39, 0x0222), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 122, 39, 0x00FA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 102, 39, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 90, 39, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 81, 62, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 81, 62, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 81, 62, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 81, 62, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.000004f, -2, 81, 62, 0x2D70), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.000004f, -2, 81, 62, 0x0085), + CS_CAM_AT_LIST(0, 280), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 43, 52, 716, 0x0222), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 43, 52, 716, 0x00FA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, 43, 52, 716, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, 43, 52, 716, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, 43, 52, 716, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.0f, 63, 86, 722, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 63, 86, 722, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 63, 86, 722, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 63, 86, 722, 0x2D70), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 63, 85, 721, 0x0085), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 63, 85, 721, 0x01E6), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, 62, 85, 721, 0x0000), + CS_CAM_AT_LIST(90, 360), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -105, 83, 366, 0x0222), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -104, 83, 364, 0x00FA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -63, 83, 339, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -16, 99, 255, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -30, 111, 177, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -80, 112, 121, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -80, 112, 121, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -80, 112, 121, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, -80, 112, 121, 0x2D70), + CS_CAM_AT_LIST(220, 520), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 137, -55, 0x0222), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 137, -55, 0x00FA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 117, -55, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 105, -55, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 98, -31, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 98, -31, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 98, -31, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 98, -31, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.000004f, 0, 98, -31, 0x2D70), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.000004f, 0, 98, -31, 0x0085), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.c b/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.c new file mode 100644 index 000000000..090f21cc6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.c @@ -0,0 +1,252 @@ +/* + * File: z_bg_treemouth.c + * Overlay: ovl_Bg_Treemouth + * Description: Great Deku Tree's Mouth + */ + +#include "z_bg_treemouth.h" +#include "objects/object_spot04_objects/object_spot04_objects.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgTreemouth_Init(Actor* thisx, GlobalContext* globalCtx); +void BgTreemouth_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgTreemouth_Update(Actor* thisx, GlobalContext* globalCtx); +void BgTreemouth_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808BC65C(BgTreemouth* this, GlobalContext* globalCtx); +void func_808BC6F8(BgTreemouth* this, GlobalContext* globalCtx); +void func_808BC80C(BgTreemouth* this, GlobalContext* globalCtx); +void func_808BC864(BgTreemouth* this, GlobalContext* globalCtx); +void BgTreemouth_DoNothing(BgTreemouth* this, GlobalContext* globalCtx); +void func_808BC8B8(BgTreemouth* this, GlobalContext* globalCtx); +void func_808BC9EC(BgTreemouth* this, GlobalContext* globalCtx); +void func_808BCAF0(BgTreemouth* this, GlobalContext* globalCtx); + +extern CutsceneData D_808BCE20[]; +extern CutsceneData D_808BD2A0[]; +extern CutsceneData D_808BD520[]; +extern CutsceneData D_808BD790[]; + +const ActorInit Bg_Treemouth_InitVars = { + ACTOR_BG_TREEMOUTH, + ACTORCAT_BG, + FLAGS, + OBJECT_SPOT04_OBJECTS, + sizeof(BgTreemouth), + (ActorFunc)BgTreemouth_Init, + (ActorFunc)BgTreemouth_Destroy, + (ActorFunc)BgTreemouth_Update, + (ActorFunc)BgTreemouth_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 8000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 300, ICHAIN_STOP), +}; + +// unused +static f32 D_808BD9C4[] = { + -2746.0f, 545.0f, 4694.0f, -2654.0f, 146.0f, 4534.0f, +}; + +void BgTreemouth_SetupAction(BgTreemouth* this, BgTreemouthActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void BgTreemouth_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgTreemouth* this = (BgTreemouth*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gDekuTreeMouthCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + Actor_SetFocus(thisx, 50.0f); + + if ((gSaveContext.sceneSetupIndex < 4) && !LINK_IS_ADULT) { + BgTreemouth_SetupAction(this, func_808BC8B8); + } else if (LINK_IS_ADULT || (gSaveContext.sceneSetupIndex == 7)) { + this->unk_168 = 0.0f; + BgTreemouth_SetupAction(this, BgTreemouth_DoNothing); + } else { + this->unk_168 = 1.0f; + BgTreemouth_SetupAction(this, func_808BC6F8); + } + + thisx->textId = 0x905; +} + +void BgTreemouth_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgTreemouth* this = (BgTreemouth*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808BC65C(BgTreemouth* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE)) { + npcAction = globalCtx->csCtx.npcActions[0]; + if (npcAction != NULL) { + if (npcAction->action == 2) { + BgTreemouth_SetupAction(this, func_808BC80C); + } else if (npcAction->action == 3) { + Audio_PlaySoundGeneral(NA_SE_EV_WOODDOOR_OPEN, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + BgTreemouth_SetupAction(this, func_808BC6F8); + } + } + } +} + +void func_808BC6F8(BgTreemouth* this, GlobalContext* globalCtx) { + Vec3f sp34; + + if (this->unk_168 < 1.0f) { + this->unk_168 += 0.01f; + } else { + this->unk_168 = 1.0f; + } + + if ((gSaveContext.sceneSetupIndex == 6) && (globalCtx->csCtx.frames >= 0x2BD) && + (globalCtx->state.frames % 8 == 0)) { + sp34.x = (Rand_ZeroOne() * 1158.0f) + 3407.0f; + sp34.y = 970.0f; + sp34.z = (Rand_ZeroOne() * 2026.0f) + -2163.0f; + EffectSsHahen_SpawnBurst(globalCtx, &sp34, 0.8f, 0, 50, 30, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); + } +} + +void func_808BC80C(BgTreemouth* this, GlobalContext* globalCtx) { + this->unk_168 += 0.05f; + if (this->unk_168 >= 0.8f) { + BgTreemouth_SetupAction(this, func_808BC864); + } +} + +void func_808BC864(BgTreemouth* this, GlobalContext* globalCtx) { + this->unk_168 -= 0.03f; + if (this->unk_168 <= 0.0f) { + BgTreemouth_SetupAction(this, func_808BC65C); + } +} + +void func_808BC8B8(BgTreemouth* this, GlobalContext* globalCtx) { + if ((!(Flags_GetEventChkInf(5))) || LINK_IS_ADULT) { + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0xC)) { + if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 1658.0f, 0x7530)) { + this->dyna.actor.flags |= ACTOR_FLAG_0; + if (this->dyna.actor.isTargeted) { + this->dyna.actor.flags &= ~ACTOR_FLAG_0; + globalCtx->csCtx.segment = D_808BD2A0; + gSaveContext.cutsceneTrigger = 1; + BgTreemouth_SetupAction(this, func_808BC9EC); + } + } + } else if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 1658.0f, 0x4E20)) { + Flags_SetEventChkInf(0xC); + globalCtx->csCtx.segment = D_808BCE20; + gSaveContext.cutsceneTrigger = 1; + BgTreemouth_SetupAction(this, func_808BC9EC); + } + } + } else { + this->unk_168 = 1.0f; + } +} + +void func_808BC9EC(BgTreemouth* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->csCtx.state == CS_STATE_UNSKIPPABLE_INIT) { + if (Actor_IsFacingAndNearPlayer(&this->dyna.actor, 350.0f, 0x7530)) { + player->actor.world.pos.x = 3827.0f; + player->actor.world.pos.y = -161.0f; + player->actor.world.pos.z = -1142.0f; + } + + globalCtx->csCtx.frames = 0; + globalCtx->csCtx.unk_18 = 0xFFFF; + D_8015FCC0 = 0xFFFF; + D_8015FCC2 = 0xFFFF; + D_8015FCC4 = 0xFFFF; + globalCtx->csCtx.unk_1A = 0; + globalCtx->csCtx.unk_1B = 0; + globalCtx->csCtx.state = CS_STATE_SKIPPABLE_EXEC; + + if (globalCtx->msgCtx.choiceIndex == 0) { + globalCtx->csCtx.segment = D_808BD520; + Flags_SetEventChkInf(5); + BgTreemouth_SetupAction(this, func_808BCAF0); + } else { + globalCtx->csCtx.segment = D_808BD790; + globalCtx->csCtx.frames = 0; + BgTreemouth_SetupAction(this, func_808BC8B8); + } + } +} + +void func_808BCAF0(BgTreemouth* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = globalCtx->csCtx.npcActions[0]; + if (npcAction != NULL) { + if (npcAction->action == 2) { + BgTreemouth_SetupAction(this, func_808BC80C); + } else if (npcAction->action == 3) { + Audio_PlaySoundGeneral(NA_SE_EV_WOODDOOR_OPEN, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + BgTreemouth_SetupAction(this, func_808BC6F8); + } + } + } +} + +void BgTreemouth_DoNothing(BgTreemouth* this, GlobalContext* globalCtx) { +} + +void BgTreemouth_Update(Actor* thisx, GlobalContext* globalCtx) { + BgTreemouth* this = (BgTreemouth*)thisx; + f32 unk_168; + + this->actionFunc(this, globalCtx); + unk_168 = this->unk_168; + thisx->world.pos.x = (unk_168 * -160.0f) + 4029.0f; + thisx->world.pos.y = (unk_168 * -399.0f) + 136.0f; + thisx->world.pos.z = (unk_168 * 92.0f) + -1255.0f; +} + +void BgTreemouth_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + u16 alpha = 500; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_treemouth.c", 893); + + func_80093D18(globalCtx->state.gfxCtx); + + if ((gSaveContext.sceneSetupIndex < 4) || LINK_IS_ADULT) { + if (gSaveContext.eventChkInf[0] & 0x80) { + alpha = 2150; + } + } else { // neeeded to match + } + + if (gSaveContext.sceneSetupIndex == 6) { + alpha = (globalCtx->roomCtx.unk_74[0] + 0x1F4); + } + + gDPSetEnvColor(POLY_OPA_DISP++, 128, 128, 128, alpha * 0.1f); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_treemouth.c", 932), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuTreeMouthDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_treemouth.c", 937); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h b/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h new file mode 100644 index 000000000..7c704067e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_TREEMOUTH_H +#define Z_BG_TREEMOUTH_H + +#include "ultra64.h" +#include "global.h" + +struct BgTreemouth; + +typedef void (*BgTreemouthActionFunc)(struct BgTreemouth*, GlobalContext*); + +typedef struct BgTreemouth { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ char unk_164[0x4]; + /* 0x0168 */ f32 unk_168; + /* 0x016C */ BgTreemouthActionFunc actionFunc; +} BgTreemouth; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth_cutscene_data.c b/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth_cutscene_data.c new file mode 100644 index 000000000..a473dc025 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Treemouth/z_bg_treemouth_cutscene_data.c @@ -0,0 +1,169 @@ +#include "z_bg_treemouth.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData D_808BCE20[] = { + CS_BEGIN_CUTSCENE(12, 3000), + CS_UNK_DATA_LIST(0x00000015, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x00000000, 0x00000000), + CS_PLAYER_ACTION_LIST(2), + CS_PLAYER_ACTION(0x0002, 0, 33, 0x54B2, 0x0000, 0x0000, 2614, 0, -451, 2808, 0, -559, 5.878788f, 0.0f, -5.878788f), + CS_PLAYER_ACTION(0x0004, 33, 42, 0x5945, 0x0000, 0x0000, 2808, 0, -559, 2857, 0, -594, 5.4444447f, 0.0f, -5.4444447f), + CS_CAM_EYE_LIST(0, 1091), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x59A8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x20B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x6430), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 47.199955f, 2753, 46, -354, 0x0000), + CS_CAM_EYE_LIST(60, 1271), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x44B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2753, 46, -354, 0x8080), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2705, 67, -302, 0x2D9A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2596, 127, -195, 0x005E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2596, 127, -195, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2596, 127, -195, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 47.199955f, 2596, 127, -195, 0x5B80), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 47.199955f, 2596, 127, -195, 0x7805), + CS_CAM_AT_LIST(0, 1120), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2788, 23, -453, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2787, 23, -453, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 47.199955f, 2787, 23, -453, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2787, 23, -453, 0x2D9A), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 47.199955f, 2787, 23, -453, 0x005E), + CS_CAM_AT_LIST(60, 1300), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2788, 23, -453, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2787, 23, -453, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2787, 23, -453, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2777, 72, -378, 0x2D9A), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2673, 127, -267, 0x005E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2673, 127, -267, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 47.199955f, 2673, 127, -267, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 47.199955f, 2673, 127, -267, 0x5B80), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 47.199955f, 2673, 127, -267, 0x7805), + CS_TEXT_LIST(4), + CS_TEXT_NONE(0, 40), + CS_TEXT_DISPLAY_TEXTBOX(0x107D, 40, 60, 0x0000, 0xFFFF, 0xFFFF), + CS_TEXT_NONE(60, 160), + CS_TEXT_DISPLAY_TEXTBOX(0x1015, 160, 170, 0x0000, 0xFFFF, 0xFFFF), + CS_MISC_LIST(1), + CS_MISC(0x000C, 180, 200, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFF7, 0xFFFFFFDB, 0x00000000, 0xFFFFFFF7, 0xFFFFFFDB, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(46, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, -41, -28, 0, -41, -28, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(62, 4), + CS_NPC_ACTION(0x0001, 0, 1, 0x0000, 0x0000, 0x0000, 2668, 46, -490, 2668, 46, -490, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 1, 50, 0x5479, 0x0000, 0x0000, 2668, 46, -490, 2890, 43, -612, 4.5306125f, -0.06122449f, -4.5306125f), + CS_NPC_ACTION(0x0004, 50, 100, 0x505C, 0x0000, 0x0000, 2890, 43, -612, 3109, 121, -705, 4.38f, 1.56f, -4.38f), + CS_NPC_ACTION(0x0001, 100, 2084, 0x0000, 0x0000, 0x0000, 3109, 121, -705, 3109, 121, -705, 0.0f, 0.0f, 0.0f), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x004C, 140, 141, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFBA, 0x00000094, 0x00000000, 0xFFFFFFBA, 0x00000094), + CS_FADE_BGM_LIST(1), + CS_FADE_BGM(0x0004, 0, 20, 0x0000, 0x00000000, 0xFFFFFFA2, 0x00000000, 0x00000027, 0xFFFFFFA2, 0x00000000, 0x00000027), + CS_END(), +}; + +CutsceneData D_808BD2A0[] = { + CS_BEGIN_CUTSCENE(9, 3000), + CS_UNK_DATA_LIST(0x00000015, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x00000000, 0x00000000), + CS_MISC_LIST(1), + CS_MISC(0x000C, 90, 172, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFE3, 0x00000012, 0x00000000, 0xFFFFFFE3, 0x00000012, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(46, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, -27, 26, 0, -27, 26, 0.0f, 0.0f, 0.0f), + CS_CAM_EYE_LIST(0, 1091), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3740, -141, -530, 0x7065), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x6167), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x6D5D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0xF348), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 40.999928f, 3740, -141, -530, 0x9D94), + CS_CAM_EYE_REL_TO_PLAYER_LIST(60, 1151), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -4, 5, 49, 0x7065), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -4, 5, 49, 0x6167), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -4, 5, 49, 0x6D5D), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -4, 5, 49, 0xF348), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.0f, -4, 5, 49, 0x9D94), + CS_CAM_AT_LIST(0, 1120), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3777, -89, -605, 0x7065), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3777, -89, -605, 0x6167), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 40.999928f, 3776, -89, -605, 0x6D5D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3776, -89, -605, 0xF348), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 40.999928f, 3776, -89, -604, 0x9D94), + CS_CAM_AT_REL_TO_PLAYER_LIST(60, 1180), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 1, 35, -36, 0x7065), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 1, 35, -36, 0x6167), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 60.0f, 1, 35, -36, 0x6D5D), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 1, 35, -36, 0xF348), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.0f, 1, 35, -36, 0x9D94), + CS_TEXT_LIST(2), + CS_TEXT_NONE(0, 20), + CS_TEXT_DISPLAY_TEXTBOX(0x1016, 20, 80, 0x0000, 0xFFFF, 0xFFFF), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x004C, 0, 1, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFAC, 0x0000007E, 0x00000000, 0xFFFFFFAC, 0x0000007E), + CS_END(), +}; + +CutsceneData D_808BD520[] = { + CS_BEGIN_CUTSCENE(8, 3000), + CS_UNK_DATA_LIST(0x00000015, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(46, 2), + CS_NPC_ACTION(0x0001, 0, 20, 0x0000, 0x0000, 0x0000, 42, 0, 77, 42, 0, 77, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 20, 357, 0x0000, 0x0000, 0x0000, 42, 0, 77, 42, 0, 77, 0.0f, 0.0f, 0.0f), + CS_CAM_EYE_LIST(0, 1151), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3740, -141, -530, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x00FB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x010C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x0198), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x019A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.999928f, 3740, -141, -530, 0x01AB), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 40.999928f, 3739, -141, -530, 0x01BC), + CS_CAM_AT_LIST(0, 1180), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3777, -89, -605, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3777, -89, -605, 0x00FB), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3777, -89, -605, 0x010C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3763, -126, -621, 0x0198), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 40.999928f, 3763, -126, -621, 0x019A), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.999928f, 3763, -126, -621, 0x01AB), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 40.999928f, 3765, -118, -617, 0x01BC), + CS_TEXT_LIST(2), + CS_TEXT_NONE(0, 20), + CS_TEXT_DISPLAY_TEXTBOX(0x1017, 20, 60, 0x0000, 0xFFFF, 0xFFFF), + CS_MISC_LIST(1), + CS_MISC(0x000C, 100, 150, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFF5, 0x00000010, 0x00000000, 0xFFFFFFF5, 0x00000010, 0x00000000, 0x00000000, 0x00000000), + CS_STOP_BGM_LIST(1), + CS_STOP_BGM(0x004C, 90, 91, 0x0000, 0x00000000, 0xFFFFFFAC, 0x00000000, 0x00000034, 0xFFFFFFAC, 0x00000000, 0x00000034), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x003D, 99, 100, 0x0000, 0x00000000, 0xFFFFFFD0, 0x00000000, 0x00000041, 0xFFFFFFD0, 0x00000000, 0x00000041), + CS_END(), +}; + +CutsceneData D_808BD790[] = { + CS_BEGIN_CUTSCENE(8, 3000), + CS_UNK_DATA_LIST(0x00000015, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x0000000F, 0xFFFFFFDE, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(46, 1), + CS_NPC_ACTION(0x0001, 0, 119, 0x0000, 0x0000, 0x0000, 42, 0, 77, 42, 0, 77, 0.0f, 0.0f, 0.0f), + CS_CAM_EYE_LIST(0, 1091), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 75.59984f, 3716, 790, -1171, 0x54EC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 75.59984f, 3716, 790, -1171, 0x555C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 75.59984f, 3716, 790, -1171, 0x55CC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 75.59984f, 3716, 790, -1171, 0x563C), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 75.59984f, 3716, 790, -1171, 0x56AC), + CS_CAM_AT_LIST(0, 1120), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 75.59984f, 3769, 718, -1186, 0x54EC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 75.59984f, 3769, 718, -1186, 0x555C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 75.59984f, 3769, 718, -1186, 0x55CC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 75.59984f, 3769, 718, -1186, 0x563C), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 75.59984f, 3769, 718, -1186, 0x56AC), + CS_TEXT_LIST(2), + CS_TEXT_NONE(0, 20), + CS_TEXT_DISPLAY_TEXTBOX(0x1018, 20, 60, 0x0000, 0xFFFF, 0xFFFF), + CS_MISC_LIST(1), + CS_MISC(0x000C, 80, 110, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFEA, 0x00000006, 0x00000000, 0xFFFFFFEA, 0x00000006, 0x00000000, 0x00000000, 0x00000000), + CS_STOP_BGM_LIST(1), + CS_STOP_BGM(0x004C, 70, 71, 0x0000, 0x00000000, 0xFFFFFFB7, 0x00000000, 0x00000044, 0xFFFFFFB7, 0x00000000, 0x00000044), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x003D, 79, 80, 0x0000, 0x00000000, 0xFFFFFFE8, 0x00000000, 0x0000003A, 0xFFFFFFE8, 0x00000000, 0x0000003A), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Bg_Umajump/z_bg_umajump.c b/soh/src/overlays/actors/ovl_Bg_Umajump/z_bg_umajump.c new file mode 100644 index 000000000..d5be5da71 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Umajump/z_bg_umajump.c @@ -0,0 +1,64 @@ +/* + * File: z_bg_umajump.c + * Overlay: ovl_Bg_Umajump + * Description: Hoppable horse fence + */ + +#include "z_bg_umajump.h" +#include "objects/object_umajump/object_umajump.h" + +#define FLAGS 0 + +void BgUmaJump_Init(Actor* thisx, GlobalContext* globalCtx); +void BgUmaJump_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgUmaJump_Update(Actor* thisx, GlobalContext* globalCtx); +void BgUmaJump_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Umajump_InitVars = { + ACTOR_BG_UMAJUMP, + ACTORCAT_PROP, + FLAGS, + OBJECT_UMAJUMP, + sizeof(BgUmaJump), + (ActorFunc)BgUmaJump_Init, + (ActorFunc)BgUmaJump_Destroy, + (ActorFunc)BgUmaJump_Update, + (ActorFunc)BgUmaJump_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgUmaJump_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgUmaJump* this = (BgUmaJump*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gJumpableHorseFenceCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.actor.params == 1) { + if (!Flags_GetEventChkInf(0x18) && (DREG(1) == 0)) { + Actor_Kill(&this->dyna.actor); + return; + } + this->dyna.actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + } +} + +void BgUmaJump_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgUmaJump* this = (BgUmaJump*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgUmaJump_Update(Actor* thisx, GlobalContext* globalCtx) { +} + +void BgUmaJump_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gJumpableHorseFenceDL); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Umajump/z_bg_umajump.h b/soh/src/overlays/actors/ovl_Bg_Umajump/z_bg_umajump.h new file mode 100644 index 000000000..19142ec4c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Umajump/z_bg_umajump.h @@ -0,0 +1,13 @@ +#ifndef Z_BG_UMAJUMP_H +#define Z_BG_UMAJUMP_H + +#include "ultra64.h" +#include "global.h" + +struct BgUmaJump; + +typedef struct BgUmaJump { + /* 0x0000 */ DynaPolyActor dyna; +} BgUmaJump; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.c b/soh/src/overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.c new file mode 100644 index 000000000..80dc259b9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.c @@ -0,0 +1,159 @@ +/* + * File: z_bg_vb_sima.c + * Overlay: ovl_Bg_Vb_Sima + * Description: Volvagia's platform + */ + +#include "z_bg_vb_sima.h" +#include "objects/object_fd/object_fd.h" +#include "overlays/actors/ovl_Boss_Fd/z_boss_fd.h" + +#define FLAGS 0 + +void BgVbSima_Init(Actor* thisx, GlobalContext* globalCtx); +void BgVbSima_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgVbSima_Update(Actor* thisx, GlobalContext* globalCtx); +void BgVbSima_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Bg_Vb_Sima_InitVars = { + ACTOR_BG_VB_SIMA, + ACTORCAT_BG, + FLAGS, + OBJECT_FD, + sizeof(BgVbSima), + (ActorFunc)BgVbSima_Init, + (ActorFunc)BgVbSima_Destroy, + (ActorFunc)BgVbSima_Update, + (ActorFunc)BgVbSima_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgVbSima_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgVbSima* this = (BgVbSima*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&gVolvagiaPlatformCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void BgVbSima_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgVbSima* this = (BgVbSima*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgVbSima_SpawnEmber(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_EMBER; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->alpha = 255; + effect->timer1 = (s16)Rand_ZeroFloat(10.0f); + break; + } + } +} + +void BgVbSima_Update(Actor* thisx, GlobalContext* globalCtx) { + static Color_RGBA8 colorYellow = { 255, 255, 0, 255 }; + static Color_RGBA8 colorRed = { 255, 10, 0, 255 }; + s32 pad; + BgVbSima* this = (BgVbSima*)thisx; + BossFd* bossFd = (BossFd*)this->dyna.actor.parent; + f32 minus1 = -1.0f; + + this->shakeTimer++; + if (!Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + s32 signal = bossFd->platformSignal; + + if (signal == VBSIMA_COLLAPSE) { + Math_SmoothStepToF(&this->dyna.actor.world.pos.y, -1000.0f, 1.0f, 1.5f, 0.0f); + this->dyna.actor.world.pos.z += 2.0f * Math_CosS(this->shakeTimer * 0x8000); + this->dyna.actor.shape.rot.x = (s16)Math_SinS(this->shakeTimer * 0x7000) * 0x37; + this->dyna.actor.shape.rot.z = (s16)Math_SinS(this->shakeTimer * 0x5000) * 0x37; + Audio_PlaySoundGeneral(NA_SE_EV_BLOCKSINK - SFX_FLAG, &this->dyna.actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else if (signal == VBSIMA_KILL) { + Actor_Kill(&this->dyna.actor); + } + if (bossFd->platformSignal != VBSIMA_STAND) { + s16 i2; + s16 i1; + Vec3f splashVel; + Vec3f splashAcc; + Vec3f splashPos; + Vec3f emberPos; + Vec3f emberVel; + Vec3f emberAcc; + f32 edgeX; + f32 edgeZ; + + for (i1 = 0; i1 < 10; i1++) { + if (Rand_ZeroOne() < 0.33f) { + edgeX = -80.0f; + edgeZ = Rand_CenteredFloat(160.0f); + } else { + edgeZ = 80.0f; + if (Rand_ZeroOne() < 0.5f) { + edgeZ = 80.0f * minus1; + } + edgeX = Rand_CenteredFloat(160.0f); + } + + splashVel.x = edgeX * 0.05f; + splashVel.y = Rand_ZeroFloat(3.0f) + 3.0f; + splashVel.z = edgeZ * 0.05f; + + splashAcc.y = -0.3f; + splashAcc.x = splashVel.x; + splashAcc.z = splashVel.z; + + splashPos.x = this->dyna.actor.world.pos.x + edgeX; + splashPos.y = -80.0f; + splashPos.z = this->dyna.actor.world.pos.z + edgeZ; + + func_8002836C(globalCtx, &splashPos, &splashVel, &splashAcc, &colorYellow, &colorRed, + (s16)Rand_ZeroFloat(100.0f) + 500, 10, 20); + + for (i2 = 0; i2 < 3; i2++) { + emberVel.x = splashVel.x; + emberVel.y = Rand_ZeroFloat(5.0f); + emberVel.z = splashVel.z; + + emberAcc.y = 0.4f; + emberAcc.x = Rand_CenteredFloat(0.5f); + emberAcc.z = Rand_CenteredFloat(0.5f); + + emberPos.x = Rand_CenteredFloat(60.0f) + splashPos.x; + emberPos.y = Rand_ZeroFloat(40.0f) + splashPos.y; + emberPos.z = Rand_CenteredFloat(60.0f) + splashPos.z; + + BgVbSima_SpawnEmber(bossFd->effects, &emberPos, &emberVel, &emberAcc, + (s16)Rand_ZeroFloat(2.0f) + 8); + } + } + } + } +} + +void BgVbSima_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_vb_sima.c", 285); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_vb_sima.c", 291), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gVolvagiaPlatformDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_vb_sima.c", 296); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.h b/soh/src/overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.h new file mode 100644 index 000000000..42fdf5cfb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_VB_SIMA_H +#define Z_BG_VB_SIMA_H + +#include "ultra64.h" +#include "global.h" + +struct BgVbSima; + +typedef enum { + /* 0 */ VBSIMA_STAND, + /* 1 */ VBSIMA_COLLAPSE, + /* 2 */ VBSIMA_KILL +} BgVbSimaSignal; + +typedef struct BgVbSima { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ char unk_164[0x10]; + /* 0x0174 */ s16 shakeTimer; + /* 0x0176 */ char unk_176[6]; +} BgVbSima; // size = 0x017C + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ydan_Hasi/z_bg_ydan_hasi.c b/soh/src/overlays/actors/ovl_Bg_Ydan_Hasi/z_bg_ydan_hasi.c new file mode 100644 index 000000000..895762377 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ydan_Hasi/z_bg_ydan_hasi.c @@ -0,0 +1,193 @@ +/* + * File: z_bg_ydan_hasi.c + * Overlay: ovl_Bg_Ydan_Hasi + * Description: Deku Tree Puzzle elements. Water plane and floating block in B1, and 3 blocks on 2F + */ + +#include "z_bg_ydan_hasi.h" +#include "objects/object_ydan_objects/object_ydan_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BgYdanHasi_Init(Actor* thisx, GlobalContext* globalCtx); +void BgYdanHasi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgYdanHasi_Update(Actor* thisx, GlobalContext* globalCtx); +void BgYdanHasi_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgYdanHasi_InitWater(BgYdanHasi* this, GlobalContext* globalCtx); +void BgYdanHasi_UpdateFloatingBlock(BgYdanHasi* this, GlobalContext* globalCtx); +void BgYdanHasi_SetupThreeBlocks(BgYdanHasi* this, GlobalContext* globalCtx); +void BgYdanHasi_MoveWater(BgYdanHasi* this, GlobalContext* globalCtx); +void BgYdanHasi_DecWaterTimer(BgYdanHasi* this, GlobalContext* globalCtx); +void BgYdanHasi_UpdateThreeBlocks(BgYdanHasi* this, GlobalContext* globalCtx); + +const ActorInit Bg_Ydan_Hasi_InitVars = { + ACTOR_BG_YDAN_HASI, + ACTORCAT_BG, + FLAGS, + OBJECT_YDAN_OBJECTS, + sizeof(BgYdanHasi), + (ActorFunc)BgYdanHasi_Init, + (ActorFunc)BgYdanHasi_Destroy, + (ActorFunc)BgYdanHasi_Update, + (ActorFunc)BgYdanHasi_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgYdanHasi_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgYdanHasi* this = (BgYdanHasi*)thisx; + CollisionHeader* colHeader = NULL; + WaterBox* waterBox; + + Actor_ProcessInitChain(thisx, sInitChain); + this->type = ((thisx->params >> 8) & 0x3F); + thisx->params = thisx->params & 0xFF; + waterBox = &globalCtx->colCtx.colHeader->waterBoxes[1]; + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + if (thisx->params == HASI_WATER) { + // Water the moving platform floats on in B1. Never runs in Master Quest + waterBox->ySurface = thisx->world.pos.y = thisx->home.pos.y += -5.0f; + this->actionFunc = BgYdanHasi_InitWater; + } else { + if (thisx->params == HASI_WATER_BLOCK) { + // Moving platform on the water in B1 + CollisionHeader_GetVirtual(&gDTSlidingPlatformCol, &colHeader); + thisx->scale.z = 0.15f; + thisx->scale.x = 0.15f; + thisx->world.pos.y = (waterBox->ySurface + 20.0f); + this->actionFunc = BgYdanHasi_UpdateFloatingBlock; + } else { + // 3 platforms on 2F + CollisionHeader_GetVirtual(&gDTRisingPlatformsCol, &colHeader); + thisx->draw = NULL; + this->actionFunc = BgYdanHasi_SetupThreeBlocks; + Actor_SetFocus(&this->dyna.actor, 40.0f); + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } + this->timer = 0; +} + +void BgYdanHasi_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgYdanHasi* this = (BgYdanHasi*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void BgYdanHasi_UpdateFloatingBlock(BgYdanHasi* this, GlobalContext* globalCtx) { + WaterBox* waterBox; + f32 framesAfterMath; + + framesAfterMath = sinf((globalCtx->gameplayFrames & 0xFF) * (M_PI / 128)) * 165.0f; + this->dyna.actor.world.pos.x = + ((Math_SinS(this->dyna.actor.world.rot.y) * framesAfterMath) + this->dyna.actor.home.pos.x); + this->dyna.actor.world.pos.z = + ((Math_CosS(this->dyna.actor.world.rot.y) * framesAfterMath) + this->dyna.actor.home.pos.z); + waterBox = &globalCtx->colCtx.colHeader->waterBoxes[1]; + this->dyna.actor.world.pos.y = waterBox->ySurface + 20.0f; + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + this->timer = 50; + } + this->dyna.actor.world.pos.y += 2.0f * sinf(this->timer * (M_PI / 25)); +} + +void BgYdanHasi_InitWater(BgYdanHasi* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->type)) { + this->timer = 600; + this->actionFunc = BgYdanHasi_MoveWater; + } +} + +void BgYdanHasi_MoveWater(BgYdanHasi* this, GlobalContext* globalCtx) { + WaterBox* waterBox; + + if (this->timer == 0) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 1.0f) != 0) { + Flags_UnsetSwitch(globalCtx, this->type); + this->actionFunc = BgYdanHasi_InitWater; + } + func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } else { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y - 47.0f, 0.5f)) { + this->actionFunc = BgYdanHasi_DecWaterTimer; + } + func_8002F948(&this->dyna.actor, NA_SE_EV_WATER_LEVEL_DOWN - SFX_FLAG); + } + waterBox = &globalCtx->colCtx.colHeader->waterBoxes[1]; + waterBox->ySurface = this->dyna.actor.world.pos.y; +} + +void BgYdanHasi_DecWaterTimer(BgYdanHasi* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + func_8002F994(&this->dyna.actor, this->timer); + if (this->timer == 0) { + this->actionFunc = BgYdanHasi_MoveWater; + } +} + +void BgYdanHasi_SetupThreeBlocks(BgYdanHasi* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->type)) { + this->timer = 260; + this->dyna.actor.draw = BgYdanHasi_Draw; + this->actionFunc = BgYdanHasi_UpdateThreeBlocks; + OnePointCutscene_Init(globalCtx, 3040, 30, &this->dyna.actor, MAIN_CAM); + } +} + +void BgYdanHasi_UpdateThreeBlocks(BgYdanHasi* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 3.0f) != 0) { + Flags_UnsetSwitch(globalCtx, this->type); + this->dyna.actor.draw = NULL; + this->actionFunc = BgYdanHasi_SetupThreeBlocks; + } else { + func_8002F948(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + } + } else if (!Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 120.0f, 3.0f)) { + func_8002F948(&this->dyna.actor, NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG); + + } else { + func_8002F994(&this->dyna.actor, this->timer); + } +} + +void BgYdanHasi_Update(Actor* thisx, GlobalContext* globalCtx) { + BgYdanHasi* this = (BgYdanHasi*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgYdanHasi_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { gDTSlidingPlatformDL, gDTWaterPlaneDL, gDTRisingPlatformsDL }; + BgYdanHasi* this = (BgYdanHasi*)thisx; + + if (this->dyna.actor.params == HASI_WATER_BLOCK || this->dyna.actor.params == HASI_THREE_BLOCKS) { + Gfx_DrawDListOpa(globalCtx, dLists[this->dyna.actor.params]); + } else { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_ydan_hasi.c", 577); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -globalCtx->gameplayFrames % 128, + globalCtx->gameplayFrames % 128, 0x20, 0x20, 1, globalCtx->gameplayFrames % 128, + globalCtx->gameplayFrames % 128, 0x20, 0x20)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ydan_hasi.c", 592), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDTWaterPlaneDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_ydan_hasi.c", 597); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ydan_Hasi/z_bg_ydan_hasi.h b/soh/src/overlays/actors/ovl_Bg_Ydan_Hasi/z_bg_ydan_hasi.h new file mode 100644 index 000000000..4ffe1fad4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ydan_Hasi/z_bg_ydan_hasi.h @@ -0,0 +1,24 @@ +#ifndef Z_BG_YDAN_HASI_H +#define Z_BG_YDAN_HASI_H + +#include "ultra64.h" +#include "global.h" + +struct BgYdanHasi; + +typedef void (*BgYdanHasiActionFunc)(struct BgYdanHasi*, GlobalContext*); + +typedef struct BgYdanHasi { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgYdanHasiActionFunc actionFunc; + /* 0x0168 */ u8 type; + /* 0x016A */ s16 timer; //Also used as an offset for the water blocks Y position for a "bobbing" effect +} BgYdanHasi; // size = 0x016C + +typedef enum { + /* 0 */ HASI_WATER_BLOCK, + /* 1 */ HASI_WATER, + /* 2 */ HASI_THREE_BLOCKS +} HasiType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ydan_Maruta/z_bg_ydan_maruta.c b/soh/src/overlays/actors/ovl_Bg_Ydan_Maruta/z_bg_ydan_maruta.c new file mode 100644 index 000000000..27fbb485a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ydan_Maruta/z_bg_ydan_maruta.c @@ -0,0 +1,210 @@ +/* + * File: z_bg_ydan_maruta.c + * Overlay: ovl_Bg_Ydan_Maruta + * Description: Rotating spike log and falling ladder in Deku Tree + */ + +#include "z_bg_ydan_maruta.h" +#include "objects/object_ydan_objects/object_ydan_objects.h" + +#define FLAGS 0 + +void BgYdanMaruta_Init(Actor* thisx, GlobalContext* globalCtx); +void BgYdanMaruta_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgYdanMaruta_Update(Actor* thisx, GlobalContext* globalCtx); +void BgYdanMaruta_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808BEFF4(BgYdanMaruta* this, GlobalContext* globalCtx); +void BgYdanMaruta_DoNothing(BgYdanMaruta* this, GlobalContext* globalCtx); +void func_808BF078(BgYdanMaruta* this, GlobalContext* globalCtx); +void func_808BF108(BgYdanMaruta* this, GlobalContext* globalCtx); +void func_808BF1EC(BgYdanMaruta* this, GlobalContext* globalCtx); + +const ActorInit Bg_Ydan_Maruta_InitVars = { + ACTOR_BG_YDAN_MARUTA, + ACTORCAT_PROP, + FLAGS, + OBJECT_YDAN_OBJECTS, + sizeof(BgYdanMaruta), + (ActorFunc)BgYdanMaruta_Init, + (ActorFunc)BgYdanMaruta_Destroy, + (ActorFunc)BgYdanMaruta_Update, + (ActorFunc)BgYdanMaruta_Draw, + NULL, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x00, 0x04 }, + { 0x00000004, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_WOOD, + BUMP_ON, + OCELEM_NONE, + }, + { { { 220.0f, -10.0f, 0.0f }, { 220.0f, 10.0f, 0.0f }, { -220.0f, 10.0f, 0.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x00, 0x04 }, + { 0x00000004, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_WOOD, + BUMP_ON, + OCELEM_NONE, + }, + { { { 16.0f, 0.0f, 0.0f }, { 16.0f, 135.0f, 0.0f }, { -16.0f, 135.0f, 0.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgYdanMaruta_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BgYdanMaruta* this = (BgYdanMaruta*)thisx; + Vec3f sp4C[3]; + s32 i; + f32 sinRotY; + f32 cosRotY; + CollisionHeader* colHeader = NULL; + ColliderTrisElementInit* triInit; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + Collider_InitTris(globalCtx, &this->collider); + Collider_SetTris(globalCtx, &this->collider, &this->dyna.actor, &sTrisInit, this->elements); + + this->switchFlag = this->dyna.actor.params & 0xFFFF; + thisx->params = (thisx->params >> 8) & 0xFF; // thisx is required to match here + + if (this->dyna.actor.params == 0) { + triInit = &sTrisElementsInit[0]; + this->actionFunc = func_808BEFF4; + } else { + triInit = &sTrisElementsInit[1]; + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gDTFallingLadderCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + thisx->home.pos.y += -280.0f; + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + thisx->world.pos.y = thisx->home.pos.y; + this->actionFunc = BgYdanMaruta_DoNothing; + } else { + this->actionFunc = func_808BF078; + } + } + + sinRotY = Math_SinS(this->dyna.actor.shape.rot.y); + cosRotY = Math_CosS(this->dyna.actor.shape.rot.y); + + for (i = 0; i < 3; i++) { + sp4C[i].x = (triInit->dim.vtx[i].x * cosRotY) + this->dyna.actor.world.pos.x; + sp4C[i].y = triInit->dim.vtx[i].y + this->dyna.actor.world.pos.y; + sp4C[i].z = this->dyna.actor.world.pos.z - (triInit->dim.vtx[i].x * sinRotY); + } + + Collider_SetTrisVertices(&this->collider, 0, &sp4C[0], &sp4C[1], &sp4C[2]); + + sp4C[1].x = (triInit->dim.vtx[2].x * cosRotY) + this->dyna.actor.world.pos.x; + sp4C[1].y = triInit->dim.vtx[0].y + this->dyna.actor.world.pos.y; + sp4C[1].z = this->dyna.actor.world.pos.z - (triInit->dim.vtx[2].x * sinRotY); + + Collider_SetTrisVertices(&this->collider, 1, &sp4C[0], &sp4C[2], &sp4C[1]); +} + +void BgYdanMaruta_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgYdanMaruta* this = (BgYdanMaruta*)thisx; + + Collider_DestroyTris(globalCtx, &this->collider); + if (this->dyna.actor.params == 1) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void func_808BEFF4(BgYdanMaruta* this, GlobalContext* globalCtx) { + if (this->collider.base.atFlags & AT_HIT) { + func_8002F71C(globalCtx, &this->dyna.actor, 7.0f, this->dyna.actor.shape.rot.y, 6.0f); + } + this->dyna.actor.shape.rot.x += 0x360; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + func_8002F974(&this->dyna.actor, NA_SE_EV_TOGE_STICK_ROLLING - SFX_FLAG); +} + +void func_808BF078(BgYdanMaruta* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->unk_16A = 20; + Flags_SetSwitch(globalCtx, this->switchFlag); + func_80078884(NA_SE_SY_CORRECT_CHIME); + this->actionFunc = func_808BF108; + OnePointCutscene_Init(globalCtx, 3010, 50, &this->dyna.actor, MAIN_CAM); + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_808BF108(BgYdanMaruta* this, GlobalContext* globalCtx) { + s16 temp; + + if (this->unk_16A != 0) { + this->unk_16A--; + } + if (this->unk_16A == 0) { + this->actionFunc = func_808BF1EC; + } + + if (1) {} + + temp = (this->unk_16A % 4) - 2; + if (temp == -2) { + temp = 0; + } else { + temp *= 2; + } + + this->dyna.actor.world.pos.x = (Math_CosS(this->dyna.actor.shape.rot.y) * temp) + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = (Math_SinS(this->dyna.actor.shape.rot.y) * temp) + this->dyna.actor.home.pos.z; + + func_8002F974(&this->dyna.actor, NA_SE_EV_TRAP_OBJ_SLIDE - SFX_FLAG); +} + +void func_808BF1EC(BgYdanMaruta* this, GlobalContext* globalCtx) { + this->dyna.actor.velocity.y += 1.0f; + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, this->dyna.actor.velocity.y)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_LADDER_DOUND); + this->actionFunc = BgYdanMaruta_DoNothing; + } +} + +void BgYdanMaruta_DoNothing(BgYdanMaruta* this, GlobalContext* globalCtx) { +} + +void BgYdanMaruta_Update(Actor* thisx, GlobalContext* globalCtx) { + BgYdanMaruta* this = (BgYdanMaruta*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgYdanMaruta_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgYdanMaruta* this = (BgYdanMaruta*)thisx; + + if (this->dyna.actor.params == 0) { + Gfx_DrawDListOpa(globalCtx, gDTRollingSpikeTrapDL); + } else { + Gfx_DrawDListOpa(globalCtx, gDTFallingLadderDL); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ydan_Maruta/z_bg_ydan_maruta.h b/soh/src/overlays/actors/ovl_Bg_Ydan_Maruta/z_bg_ydan_maruta.h new file mode 100644 index 000000000..48aeb5df8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ydan_Maruta/z_bg_ydan_maruta.h @@ -0,0 +1,20 @@ +#ifndef Z_BG_YDAN_MARUTA_H +#define Z_BG_YDAN_MARUTA_H + +#include "ultra64.h" +#include "global.h" + +struct BgYdanMaruta; + +typedef void (*BgYdanMarutaActionFunc)(struct BgYdanMaruta*, GlobalContext*); + +typedef struct BgYdanMaruta { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgYdanMarutaActionFunc actionFunc; + /* 0x0168 */ u8 switchFlag; + /* 0x016A */ s16 unk_16A; + /* 0x016C */ ColliderTris collider; + /* 0x018C */ ColliderTrisElement elements[2]; +} BgYdanMaruta; // size = 0x0244 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Ydan_Sp/z_bg_ydan_sp.c b/soh/src/overlays/actors/ovl_Bg_Ydan_Sp/z_bg_ydan_sp.c new file mode 100644 index 000000000..79e5cb849 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ydan_Sp/z_bg_ydan_sp.c @@ -0,0 +1,460 @@ +/* + * File: z_bg_ydan_sp.c + * Overlay: ovl_Bg_Ydan_Sp + * Description: Webs + */ + +#include "z_bg_ydan_sp.h" +#include "objects/object_ydan_objects/object_ydan_objects.h" + +#define FLAGS 0 + +void BgYdanSp_Init(Actor* thisx, GlobalContext* globalCtx); +void BgYdanSp_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgYdanSp_Update(Actor* thisx, GlobalContext* globalCtx); +void BgYdanSp_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BgYdanSp_BurnFloorWeb(BgYdanSp* this, GlobalContext* globalCtx); +void BgYdanSp_FloorWebIdle(BgYdanSp* this, GlobalContext* globalCtx); +void BgYdanSp_BurnWallWeb(BgYdanSp* this, GlobalContext* globalCtx); +void BgYdanSp_WallWebIdle(BgYdanSp* this, GlobalContext* globalCtx); + +//extern CollisionHeader gDTWebWallCol; + +typedef enum { + /* 0 */ WEB_FLOOR, + /* 1 */ WEB_WALL +} BgYdanSpType; + +const ActorInit Bg_Ydan_Sp_InitVars = { + ACTOR_BG_YDAN_SP, + ACTORCAT_BG, + FLAGS, + OBJECT_YDAN_OBJECTS, + sizeof(BgYdanSp), + (ActorFunc)BgYdanSp_Init, + (ActorFunc)BgYdanSp_Destroy, + (ActorFunc)BgYdanSp_Update, + (ActorFunc)BgYdanSp_Draw, + NULL, +}; + +static ColliderTrisElementInit sTrisItemsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0x00020800, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 75.0f, -8.0f, 75.0f }, { -75.0f, -8.0f, 75.0f }, { -75.0f, -8.0f, -75.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0x00020800, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 140.0f, 288.8f, 0.0f }, { -140.0f, 288.0f, 0.0f }, { -140.0f, 0.0f, 0.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_TRIS, + }, + 2, + sTrisItemsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void BgYdanSp_Init(Actor* thisx, GlobalContext* globalCtx) { + BgYdanSp* this = (BgYdanSp*)thisx; + ColliderTrisElementInit* ti0 = &sTrisItemsInit[0]; + Vec3f tri[3]; + s32 i; + CollisionHeader* colHeader = NULL; + ColliderTrisElementInit* ti1 = &sTrisItemsInit[1]; + f32 cossY; + f32 sinsY; + f32 cossX; + f32 nSinsX; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->isDestroyedSwitchFlag = thisx->params & 0x3F; + this->burnSwitchFlag = (thisx->params >> 6) & 0x3F; + this->dyna.actor.params = (thisx->params >> 0xC) & 0xF; + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + Collider_InitTris(globalCtx, &this->trisCollider); + Collider_SetTris(globalCtx, &this->trisCollider, &this->dyna.actor, &sTrisInit, this->trisColliderItems); + if (this->dyna.actor.params == WEB_FLOOR) { + CollisionHeader_GetVirtual(&gDTWebFloorCol, &colHeader); + this->actionFunc = BgYdanSp_FloorWebIdle; + + for (i = 0; i < 3; i++) { + tri[i].x = ti0->dim.vtx[i].x + this->dyna.actor.world.pos.x; + tri[i].y = ti0->dim.vtx[i].y + this->dyna.actor.world.pos.y; + tri[i].z = ti0->dim.vtx[i].z + this->dyna.actor.world.pos.z; + } + + Collider_SetTrisVertices(&this->trisCollider, 0, &tri[0], &tri[1], &tri[2]); + tri[1].x = tri[0].x; + tri[1].z = tri[2].z; + Collider_SetTrisVertices(&this->trisCollider, 1, &tri[0], &tri[2], &tri[1]); + this->unk16C = 0.0f; + } else { + CollisionHeader_GetVirtual(&gDTWebWallCol, &colHeader); + this->actionFunc = BgYdanSp_WallWebIdle; + Actor_SetFocus(&this->dyna.actor, 30.0f); + sinsY = Math_SinS(this->dyna.actor.shape.rot.y); + cossY = Math_CosS(this->dyna.actor.shape.rot.y); + nSinsX = -Math_SinS(this->dyna.actor.shape.rot.x); + cossX = Math_CosS(this->dyna.actor.shape.rot.x); + + for (i = 0; i < 3; i++) { + tri[i].x = + this->dyna.actor.world.pos.x + (cossY * ti1->dim.vtx[i].x) - (sinsY * ti1->dim.vtx[i].y * nSinsX); + tri[i].y = this->dyna.actor.world.pos.y + (ti1->dim.vtx[i].y * cossX); + tri[i].z = + this->dyna.actor.world.pos.z - (sinsY * ti1->dim.vtx[i].x) + (ti1->dim.vtx[i].y * cossY * nSinsX); + } + + Collider_SetTrisVertices(&this->trisCollider, 0, &tri[0], &tri[1], &tri[2]); + + tri[1].x = this->dyna.actor.world.pos.x + (cossY * ti1->dim.vtx[0].x) - (ti1->dim.vtx[2].y * sinsY * nSinsX); + tri[1].y = this->dyna.actor.world.pos.y + (ti1->dim.vtx[2].y * cossX); + tri[1].z = this->dyna.actor.world.pos.z - (sinsY * ti1->dim.vtx[0].x) + (ti1->dim.vtx[2].y * cossY * nSinsX); + Collider_SetTrisVertices(&this->trisCollider, 1, &tri[0], &tri[2], &tri[1]); + } + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->timer = 0; + if (Flags_GetSwitch(globalCtx, this->isDestroyedSwitchFlag)) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgYdanSp_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgYdanSp* this = (BgYdanSp*)thisx; + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyTris(globalCtx, &this->trisCollider); +} + +void BgYdanSp_UpdateFloorWebCollision(BgYdanSp* this) { + s16 newY; + CollisionHeader* colHeader; + + colHeader = ResourceMgr_LoadColByName(SEGMENTED_TO_VIRTUAL(&gDTWebFloorCol)); + colHeader->vtxList = SEGMENTED_TO_VIRTUAL(colHeader->vtxList); + newY = (this->dyna.actor.home.pos.y - this->dyna.actor.world.pos.y) * 10; + colHeader->vtxList[14].y = newY; + colHeader->vtxList[12].y = newY; + colHeader->vtxList[10].y = newY; + colHeader->vtxList[9].y = newY; + colHeader->vtxList[6].y = newY; + colHeader->vtxList[5].y = newY; + colHeader->vtxList[1].y = newY; + colHeader->vtxList[0].y = newY; +} + +void BgYdanSp_BurnWeb(BgYdanSp* this, GlobalContext* globalCtx) { + this->timer = 30; + this = this; + func_80078884(NA_SE_SY_CORRECT_CHIME); + Flags_SetSwitch(globalCtx, this->isDestroyedSwitchFlag); + if (this->dyna.actor.params == WEB_FLOOR) { + this->actionFunc = BgYdanSp_BurnFloorWeb; + } else { + this->actionFunc = BgYdanSp_BurnWallWeb; + } +} + +void BgYdanSp_BurnFloorWeb(BgYdanSp* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0 }; + Vec3f velocity; + Vec3f pos2; + f32 distXZ; + f32 sins; + f32 coss; + s16 rot; + s16 rot2; + s32 i; + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + return; + } + if ((this->timer % 3) == 0) { + rot2 = Rand_ZeroOne() * 0x2AAA; + velocity.y = 0.0f; + pos2.y = this->dyna.actor.world.pos.y; + + for (i = 0; i < 6; i++) { + rot = Rand_CenteredFloat(0x2800) + rot2; + sins = Math_SinS(rot); + coss = Math_CosS(rot); + pos2.x = this->dyna.actor.world.pos.x + (120.0f * sins); + pos2.z = this->dyna.actor.world.pos.z + (120.0f * coss); + distXZ = Math_Vec3f_DistXZ(&this->dyna.actor.home.pos, &pos2) * (1.0f / 120.0f); + if (distXZ < 0.7f) { + sins = Math_SinS(rot + 0x8000); + coss = Math_CosS(rot + 0x8000); + pos2.x = this->dyna.actor.world.pos.x + (120.0f * sins); + pos2.z = this->dyna.actor.world.pos.z + (120.0f * coss); + distXZ = Math_Vec3f_DistXZ(&this->dyna.actor.home.pos, &pos2) * (1.0f / 120.0f); + } + velocity.x = (7.0f * sins) * distXZ; + velocity.y = 0.0f; + velocity.z = (7.0f * coss) * distXZ; + EffectSsDeadDb_Spawn(globalCtx, &this->dyna.actor.home.pos, &velocity, &accel, 60, 6, 255, 255, 150, 170, + 255, 0, 0, 1, 0xE, 1); + rot2 += 0x2AAA; + } + } +} + +void BgYdanSp_FloorWebBroken(BgYdanSp* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgYdanSp_FloorWebBreaking(BgYdanSp* this, GlobalContext* globalCtx) { + static Color_RGBA8 primColor = { 250, 250, 250, 255 }; + static Color_RGBA8 envColor = { 180, 180, 180, 255 }; + static Vec3f zeroVec = { 0 }; + s32 i; + Vec3f pos; + s16 rot; + + if (this->timer != 0) { + this->timer--; + } + + this->dyna.actor.world.pos.y = (sinf((f32)this->timer * (M_PI / 20)) * this->unk16C) + this->dyna.actor.home.pos.y; + if (this->dyna.actor.home.pos.y - this->dyna.actor.world.pos.y > 190.0f) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->timer = 40; + func_80078884(NA_SE_SY_CORRECT_CHIME); + Flags_SetSwitch(globalCtx, this->isDestroyedSwitchFlag); + this->actionFunc = BgYdanSp_FloorWebBroken; + pos.y = this->dyna.actor.world.pos.y - 60.0f; + rot = 0; + for (i = 0; i < 6; i++) { + pos.x = Math_SinS(rot) * 60.0f + this->dyna.actor.world.pos.x; + pos.z = Math_CosS(rot) * 60.0f + this->dyna.actor.world.pos.z; + func_8002829C(globalCtx, &pos, &zeroVec, &zeroVec, &primColor, &envColor, 1000, 10); + + rot += 0x2AAA; + } + } + BgYdanSp_UpdateFloorWebCollision(this); +} + +void BgYdanSp_FloorWebIdle(BgYdanSp* this, GlobalContext* globalCtx) { + Player* player; + Vec3f webPos; + f32 sqrtFallDistance; + f32 unk; + + player = GET_PLAYER(globalCtx); + webPos.x = this->dyna.actor.world.pos.x; + webPos.y = this->dyna.actor.world.pos.y - 50.0f; + webPos.z = this->dyna.actor.world.pos.z; + if (Player_IsBurningStickInRange(globalCtx, &webPos, 70.0f, 50.0f) != 0) { + this->dyna.actor.home.pos.x = player->swordInfo[0].tip.x; + this->dyna.actor.home.pos.z = player->swordInfo[0].tip.z; + BgYdanSp_BurnWeb(this, globalCtx); + return; + } + if ((this->trisCollider.base.acFlags & 2) != 0) { + BgYdanSp_BurnWeb(this, globalCtx); + return; + } + if (func_8004356C(&this->dyna)) { + sqrtFallDistance = sqrtf(CLAMP_MIN(player->fallDistance, 0.0f)); + if (player->fallDistance > 750.0f) { + if (this->dyna.actor.xzDistToPlayer < 80.0f) { + this->unk16C = 200.0f; + this->dyna.actor.room = -1; + this->dyna.actor.flags |= ACTOR_FLAG_4; + this->timer = 40; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WEB_BROKEN); + this->actionFunc = BgYdanSp_FloorWebBreaking; + return; + } + } + unk = sqrtFallDistance + sqrtFallDistance; + if (this->unk16C < unk) { + if (unk > 2.0f) { + this->unk16C = unk; + this->timer = 14; + } + } + if (player->actor.speedXZ != 0.0f) { + if (this->unk16C < 0.1f) { + this->timer = 14; + } + if (this->unk16C < 2.0f) { + this->unk16C = 2.0f; + } else { + this->unk16C = this->unk16C; + } + } + } + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + this->timer = 14; + } + this->dyna.actor.world.pos.y = sinf((f32)this->timer * (M_PI / 7)) * this->unk16C + this->dyna.actor.home.pos.y; + Math_ApproachZeroF(&this->unk16C, 1.0f, 0.8f); + if (this->timer == 13) { + if (this->unk16C > 3.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WEB_VIBRATION); + } else { + Audio_StopSfxById(NA_SE_EV_WEB_VIBRATION); + } + } + BgYdanSp_UpdateFloorWebCollision(this); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->trisCollider.base); +} + +void BgYdanSp_BurnWallWeb(BgYdanSp* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0 }; + Vec3f velocity; + Vec3f spC8; + f32 distXYZ; + f32 sins; + f32 coss; + f32 coss2; + s16 rot; + s16 rot2; + s32 i; + + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + Actor_Kill(&this->dyna.actor); + return; + } + if ((this->timer % 3) == 0) { + rot2 = Rand_ZeroOne() * 0x2AAA; + + for (i = 0; i < 6; i++) { + rot = Rand_CenteredFloat(0x2800) + rot2; + sins = Math_SinS(rot); + coss = Math_CosS(rot); + coss2 = Math_CosS(this->dyna.actor.shape.rot.y) * sins; + sins *= Math_SinS(this->dyna.actor.shape.rot.y); + + spC8.x = this->dyna.actor.world.pos.x + (140.0f * coss2); + spC8.y = this->dyna.actor.world.pos.y + (140.0f * (1.0f + coss)); + spC8.z = this->dyna.actor.world.pos.z - (140.0f * sins); + distXYZ = Math_Vec3f_DistXYZ(&this->dyna.actor.home.pos, &spC8) * (1.0f / 140.0f); + if (distXYZ < 0.65f) { + sins = Math_SinS(rot + 0x8000); + coss = Math_CosS(rot + 0x8000); + coss2 = Math_CosS(this->dyna.actor.shape.rot.y) * sins; + sins *= Math_SinS(this->dyna.actor.shape.rot.y); + spC8.x = this->dyna.actor.world.pos.x + (140.0f * coss2); + spC8.y = this->dyna.actor.world.pos.y + (140.0f * (1.0f + coss)); + spC8.z = this->dyna.actor.world.pos.z - (140.0f * sins); + distXYZ = Math_Vec3f_DistXYZ(&this->dyna.actor.home.pos, &spC8) * (1.0f / 140.0f); + } + velocity.x = 6.5f * coss2 * distXYZ; + velocity.y = 6.5f * coss * distXYZ; + velocity.z = -6.5f * sins * distXYZ; + EffectSsDeadDb_Spawn(globalCtx, &this->dyna.actor.home.pos, &velocity, &accel, 80, 6, 255, 255, 150, 170, + 255, 0, 0, 1, 0xE, 1); + rot2 += 0x2AAA; + } + } +} + +void BgYdanSp_WallWebIdle(BgYdanSp* this, GlobalContext* globalCtx) { + Player* player; + Vec3f sp30; + + player = GET_PLAYER(globalCtx); + if (Flags_GetSwitch(globalCtx, this->burnSwitchFlag) || (this->trisCollider.base.acFlags & 2)) { + this->dyna.actor.home.pos.y = this->dyna.actor.world.pos.y + 80.0f; + BgYdanSp_BurnWeb(this, globalCtx); + } else if (player->heldItemActionParam == PLAYER_AP_STICK && player->unk_860 != 0) { + func_8002DBD0(&this->dyna.actor, &sp30, &player->swordInfo[0].tip); + if (fabsf(sp30.x) < 100.0f && sp30.z < 1.0f && sp30.y < 200.0f) { + OnePointCutscene_Init(globalCtx, 3020, 40, &this->dyna.actor, MAIN_CAM); + Math_Vec3f_Copy(&this->dyna.actor.home.pos, &player->swordInfo[0].tip); + BgYdanSp_BurnWeb(this, globalCtx); + } + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->trisCollider.base); +} + +void BgYdanSp_Update(Actor* thisx, GlobalContext* globalCtx) { + BgYdanSp* this = (BgYdanSp*)thisx; + + this->actionFunc(this, globalCtx); +} + +void BgYdanSp_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgYdanSp* this = (BgYdanSp*)thisx; + s32 i; + MtxF mtxF; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_bg_ydan_sp.c", 781); + func_80093D84(globalCtx->state.gfxCtx); + if (thisx->params == WEB_WALL) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ydan_sp.c", 787), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDTWebWallDL); + } else if (this->actionFunc == BgYdanSp_FloorWebBroken) { + Matrix_Get(&mtxF); + if (this->timer == 40) { + Matrix_Translate(0.0f, (thisx->home.pos.y - thisx->world.pos.y) * 10.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(1.0f, ((thisx->home.pos.y - thisx->world.pos.y) + 10.0f) * 0.1f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ydan_sp.c", 808), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDTWebFloorDL); + } + for (i = 0; i < 8; i++) { + Matrix_Put(&mtxF); + Matrix_RotateZYX(-0x5A0, i * 0x2000, 0, MTXMODE_APPLY); + Matrix_Translate(0.0f, 700.0f, -900.0f, MTXMODE_APPLY); + Matrix_Scale(3.5f, 5.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ydan_sp.c", 830), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDTUnknownWebDL); + } + } else { + Matrix_Translate(0.0f, (thisx->home.pos.y - thisx->world.pos.y) * 10.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(1.0f, ((thisx->home.pos.y - thisx->world.pos.y) + 10.0f) * 0.1f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_bg_ydan_sp.c", 849), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDTWebFloorDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_bg_ydan_sp.c", 856); +} diff --git a/soh/src/overlays/actors/ovl_Bg_Ydan_Sp/z_bg_ydan_sp.h b/soh/src/overlays/actors/ovl_Bg_Ydan_Sp/z_bg_ydan_sp.h new file mode 100644 index 000000000..65f1faa8f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Ydan_Sp/z_bg_ydan_sp.h @@ -0,0 +1,22 @@ +#ifndef Z_BG_YDAN_SP_H +#define Z_BG_YDAN_SP_H + +#include "ultra64.h" +#include "global.h" + +struct BgYdanSp; + +typedef void (*BgYdanSpActionFunc)(struct BgYdanSp*, GlobalContext*); + +typedef struct BgYdanSp { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ BgYdanSpActionFunc actionFunc; + /* 0x0168 */ u8 isDestroyedSwitchFlag; + /* 0x0169 */ u8 burnSwitchFlag; + /* 0x016A */ s16 timer; + /* 0x016C */ f32 unk16C; + /* 0x0170 */ ColliderTris trisCollider; + /* 0x0190 */ ColliderTrisElement trisColliderItems[2]; +} BgYdanSp; // size = 0x0248 + +#endif diff --git a/soh/src/overlays/actors/ovl_Bg_Zg/z_bg_zg.c b/soh/src/overlays/actors/ovl_Bg_Zg/z_bg_zg.c new file mode 100644 index 000000000..1f85ce34a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Zg/z_bg_zg.c @@ -0,0 +1,146 @@ +/* + * File: z_bg_zg.c + * Overlay: ovl_Bg_Zg + * Description: Metal bars (Ganon's Castle) + */ + +#include "z_bg_zg.h" +#include "objects/object_zg/object_zg.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void BgZg_Init(Actor* thisx, GlobalContext* globalCtx); +void BgZg_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BgZg_Update(Actor* thisx, GlobalContext* globalCtx); +void BgZg_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_808C0C50(BgZg* this); +s32 func_808C0C98(BgZg* this, GlobalContext* globalCtx); +s32 func_808C0CC8(BgZg* this); +void func_808C0CD4(BgZg* this, GlobalContext* globalCtx); +void func_808C0D08(BgZg* this, GlobalContext* globalCtx); +void func_808C0EEC(BgZg* this, GlobalContext* globalCtx); + +static BgZgActionFunc sActionFuncs[] = { + func_808C0CD4, + func_808C0D08, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +static BgZgDrawFunc sDrawFuncs[] = { + func_808C0EEC, +}; + +const ActorInit Bg_Zg_InitVars = { + ACTOR_BG_ZG, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZG, + sizeof(BgZg), + (ActorFunc)BgZg_Init, + (ActorFunc)BgZg_Destroy, + (ActorFunc)BgZg_Update, + (ActorFunc)BgZg_Draw, + NULL, +}; + +void BgZg_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BgZg* this = (BgZg*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_808C0C50(BgZg* this) { + Audio_PlaySoundGeneral(NA_SE_EV_METALDOOR_OPEN, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); +} + +s32 func_808C0C98(BgZg* this, GlobalContext* globalCtx) { + s32 flag = (this->dyna.actor.params >> 8) & 0xFF; + + return Flags_GetSwitch(globalCtx, flag); +} + +s32 func_808C0CC8(BgZg* this) { + s32 flag = this->dyna.actor.params & 0xFF; + + return flag; +} + +void func_808C0CD4(BgZg* this, GlobalContext* globalCtx) { + if (func_808C0C98(this, globalCtx) != 0) { + this->action = 1; + func_808C0C50(this); + } +} + +void func_808C0D08(BgZg* this, GlobalContext* globalCtx) { + this->dyna.actor.world.pos.y += (kREG(16) + 20.0f) * 1.2f; + if ((((kREG(17) + 200.0f) * 1.2f) + this->dyna.actor.home.pos.y) <= this->dyna.actor.world.pos.y) { + Actor_Kill(&this->dyna.actor); + } +} + +void BgZg_Update(Actor* thisx, GlobalContext* globalCtx) { + BgZg* this = (BgZg*)thisx; + s32 action = this->action; + + if (((action < 0) || (1 < action)) || (sActionFuncs[action] == NULL)) { + // "Main Mode is wrong!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sActionFuncs[action](this, globalCtx); + } +} + +void BgZg_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad[2]; + BgZg* this = (BgZg*)thisx; + CollisionHeader* colHeader; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + colHeader = NULL; + CollisionHeader_GetVirtual(&gTowerCollapseBarsCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if ((func_808C0CC8(this) == 8) || (func_808C0CC8(this) == 9)) { + this->dyna.actor.scale.x = this->dyna.actor.scale.x * 1.3f; + this->dyna.actor.scale.z = this->dyna.actor.scale.z * 1.3f; + this->dyna.actor.scale.y = this->dyna.actor.scale.y * 1.2f; + } + + this->action = 0; + this->drawConfig = 0; + if (func_808C0C98(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +void func_808C0EEC(BgZg* this, GlobalContext* globalCtx) { + GraphicsContext* localGfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(localGfxCtx, "../z_bg_zg.c", 311); + + func_80093D18(localGfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(localGfxCtx, "../z_bg_zg.c", 315), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseBarsDL); + + CLOSE_DISPS(localGfxCtx, "../z_bg_zg.c", 320); +} + +void BgZg_Draw(Actor* thisx, GlobalContext* globalCtx) { + BgZg* this = (BgZg*)thisx; + s32 drawConfig = this->drawConfig; + + if (((drawConfig < 0) || (drawConfig > 0)) || sDrawFuncs[drawConfig] == NULL) { + // "Drawing mode is wrong !!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sDrawFuncs[drawConfig](this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Bg_Zg/z_bg_zg.h b/soh/src/overlays/actors/ovl_Bg_Zg/z_bg_zg.h new file mode 100644 index 000000000..63cf24ea5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Bg_Zg/z_bg_zg.h @@ -0,0 +1,18 @@ +#ifndef Z_BG_ZG_H +#define Z_BG_ZG_H + +#include "ultra64.h" +#include "global.h" + +struct BgZg; + +typedef void (*BgZgActionFunc)(struct BgZg*, GlobalContext*); +typedef void (*BgZgDrawFunc)(struct BgZg*, GlobalContext*); + +typedef struct BgZg { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s32 action; + /* 0x0168 */ s32 drawConfig; +} BgZg; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c new file mode 100644 index 000000000..3b130b229 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -0,0 +1,1721 @@ +#include "z_boss_dodongo.h" +#include "objects/object_kingdodongo/object_kingdodongo.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "scenes/dungeons/ddan_boss/ddan_boss_room_1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BossDodongo_Init(Actor* thisx, GlobalContext* globalCtx); +void BossDodongo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossDodongo_Update(Actor* thisx, GlobalContext* globalCtx); +void BossDodongo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BossDodongo_SetupIntroCutscene(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_IntroCutscene(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_Walk(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_Inhale(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_BlowFire(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_Roll(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_SpawnFire(BossDodongo* this, GlobalContext* globalCtx, s16 arg2); +void BossDodongo_Explode(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_LayDown(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_Vulnerable(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_GetUp(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_SetupWalk(BossDodongo* this); +void BossDodongo_DeathCutscene(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_SetupDeathCutscene(BossDodongo* this); +void BossDodongo_Damaged(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_UpdateDamage(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_PlayerPosCheck(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_PlayerYawCheck(BossDodongo* this, GlobalContext* globalCtx); +f32 func_808C4F6C(BossDodongo* this, GlobalContext* globalCtx); +f32 func_808C50A8(BossDodongo* this, GlobalContext* globalCtx); +void BossDodongo_DrawEffects(GlobalContext* globalCtx); +void BossDodongo_UpdateEffects(GlobalContext* globalCtx); + +const ActorInit Boss_Dodongo_InitVars = { + ACTOR_EN_DODONGO, + ACTORCAT_BOSS, + FLAGS, + OBJECT_KINGDODONGO, + sizeof(BossDodongo), + (ActorFunc)BossDodongo_Init, + (ActorFunc)BossDodongo_Destroy, + (ActorFunc)BossDodongo_Update, + (ActorFunc)BossDodongo_Draw, + NULL, +}; + +#include "z_boss_dodongo_data.c" + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x0C, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -3000.0f, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 8200.0f, ICHAIN_STOP), +}; + +void func_808C1190(s16* arg0, u8* arg1, s16 arg2) { + arg0 = ResourceMgr_LoadTexByName(arg0); + + if (arg2[arg1] != 0) { + arg0[arg2 / 2] = 0; + } +} + +void func_808C11D0(s16* arg0, u8* arg1, s16 arg2) { + arg0 = ResourceMgr_LoadTexByName(arg0); + + if (arg1[arg2] != 0) { + arg0[arg2] = 0; + } +} + +void func_808C1200(s16* arg0, u8* arg1, s16 arg2) { + arg0 = ResourceMgr_LoadTexByName(arg0); + + if (arg1[arg2] != 0) { + arg0[arg2] = 0; + } +} + +void func_808C1230(s16* arg0, u8* arg1, s16 arg2) { + s16 index; + + arg0 = ResourceMgr_LoadTexByName(arg0); + + if (arg1[arg2] != 0) { + index = ((arg2 & 0xF) + ((arg2 & 0xF0) * 2)); + arg0[index + 16] = 0; + arg0[index] = 0; + } +} + +void func_808C1278(s16* arg0, u8* arg1, s16 arg2) { + s16 index; + + arg0 = ResourceMgr_LoadTexByName(arg0); + + if (arg1[arg2] != 0) { + index = ((arg2 & 0xF) * 2) + ((arg2 & 0xF0) * 2); + arg0[index + 1] = 0; + arg0[index] = 0; + } +} + +void func_808C12C4(u8* arg1, s16 arg2) { + func_808C1190(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_015890), arg1, arg2); + func_808C1200(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_017210), arg1, arg2); + func_808C11D0(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_015D90), arg1, arg2); + func_808C11D0(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_016390), arg1, arg2); + func_808C11D0(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_016590), arg1, arg2); + func_808C11D0(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_016790), arg1, arg2); + func_808C1230(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_015990), arg1, arg2); + func_808C1230(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_015F90), arg1, arg2); + func_808C1278(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_016990), arg1, arg2); + func_808C1278(SEGMENTED_TO_VIRTUAL(object_kingdodongo_Tex_016E10), arg1, arg2); +} + +void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { + arg0 = ResourceMgr_LoadTexByName(arg0); + floorTex = ResourceMgr_LoadTexByName(floorTex); + + u16* temp_s3 = SEGMENTED_TO_VIRTUAL(arg0); + u16* temp_s1 = SEGMENTED_TO_VIRTUAL(floorTex); + s16 i; + s16 i2; + u16 sp54[2048]; + s16 temp; + s16 temp2; + + for (i = 0; i < 2048; i += 32) { + temp = sinf((((i / 32) + (s16)((arg2 * 50.0f) / 100.0f)) & 0x1F) * (M_PI / 16)) * arg3; + for (i2 = 0; i2 < 32; i2++) { + sp54[i + ((temp + i2) & 0x1F)] = temp_s1[i + i2]; + } + } + for (i = 0; i < 32; i++) { + temp = sinf(((i + (s16)((arg2 * 80.0f) / 100.0f)) & 0x1F) * (M_PI / 16)) * arg3; + temp *= 32; + for (i2 = 0; i2 < 2048; i2 += 32) { + temp2 = (temp + i2) & 0x7FF; + temp_s3[i + temp2] = sp54[i + i2]; + } + } +} + +void func_808C17C8(GlobalContext* globalCtx, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) { + s16 i; + BossDodongoEffect* eff = (BossDodongoEffect*)globalCtx->specialEffects; + + for (i = 0; i < arg5; i++, eff++) { + if (eff->unk_24 == 0) { + eff->unk_24 = 1; + eff->unk_00 = *arg1; + eff->unk_0C = *arg2; + eff->unk_18 = *arg3; + eff->unk_2C = arg4 / 1000.0f; + eff->alpha = 255; + eff->unk_25 = (s16)Rand_ZeroFloat(10.0f); + break; + } + } +} + +s32 BossDodongo_AteExplosive(BossDodongo* this, GlobalContext* globalCtx) { + f32 dx; + f32 dy; + f32 dz; + Actor* currentExplosive = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + Actor* thisx = &this->actor; + + while (currentExplosive != NULL) { + if (currentExplosive == thisx) { + currentExplosive = currentExplosive->next; + continue; + } + + dx = currentExplosive->world.pos.x - this->mouthPos.x; + dy = currentExplosive->world.pos.y - this->mouthPos.y; + dz = currentExplosive->world.pos.z - this->mouthPos.z; + + if ((fabsf(dx) < 40.0f) && (fabsf(dy) < 40.0f) && (fabsf(dz) < 40.0f)) { + Actor_Kill(currentExplosive); + return true; + } + + currentExplosive = currentExplosive->next; + } + + return false; +} + +void BossDodongo_Init(Actor* thisx, GlobalContext* globalCtx) { + BossDodongo* this = (BossDodongo*)thisx; + s16 i; + u16* temp_s1_3; + u16* temp_s2; + u32 temp_v0; + + globalCtx->specialEffects = &this->effects; + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 9200.0f, ActorShadow_DrawCircle, 250.0f); + Actor_SetScale(&this->actor, 0.01f); + SkelAnime_Init(globalCtx, &this->skelAnime, &object_kingdodongo_Skel_01B310, &object_kingdodongo_Anim_00F0D8, NULL, + NULL, 0); + Animation_PlayLoop(&this->skelAnime, &object_kingdodongo_Anim_00F0D8); + this->unk_1F8 = 1.0f; + BossDodongo_SetupIntroCutscene(this, globalCtx); + this->health = 12; + this->colorFilterMin = 995.0f; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->colorFilterMax = 1000.0f; + this->unk_224 = 2.0f; + this->unk_228 = 9200.0f; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->items); + + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { // KD is dead + temp_s1_3 = SEGMENTED_TO_VIRTUAL(gDodongosCavernBossLavaFloorTex); + temp_s2 = SEGMENTED_TO_VIRTUAL(sLavaFloorRockTex); + + Actor_Kill(&this->actor); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, + -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, 0x6000); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, -690.0f, -1523.76f, -3304.0f, 0, 0, 0, 0); + + for (i = 0; i < 2048; i++) { + temp_v0 = i; + temp_s1_3[temp_v0] = temp_s2[temp_v0]; + } + } + + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void BossDodongo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BossDodongo* this = (BossDodongo*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BossDodongo_SetupIntroCutscene(BossDodongo* this, GlobalContext* globalCtx) { + s16 frames = Animation_GetLastFrame(&object_kingdodongo_Anim_00F0D8); + + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_00F0D8, 1.0f, 0.0f, frames, ANIMMODE_LOOP, -10.0f); + this->actionFunc = BossDodongo_IntroCutscene; + this->csState = 0; + this->unk_1BC = 1; +} + +void BossDodongo_IntroCutscene(BossDodongo* this, GlobalContext* globalCtx) { + f32 phi_f0; + Camera* camera; + Player* player; + Vec3f sp60; + Vec3f sp54; + Vec3f sp48; + + player = GET_PLAYER(globalCtx); + camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + if (this->unk_196 != 0) { + this->unk_196--; + } + + if (this->unk_198 != 0) { + this->unk_198--; + } + + if (this->unk_19A != 0) { + this->unk_19A--; + } + + switch (this->csState) { + case 0: + if (player->actor.world.pos.y < -1223.76f) { + this->csState = 1; + this->actor.world.pos.x = -1390.0f; + this->actor.world.pos.z = -3374.0f; + this->unk_1A0 = 1; + } + break; + case 1: + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 1); + Gameplay_ClearAllSubCameras(globalCtx); + this->cutsceneCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, 1); + Gameplay_ChangeCameraStatus(globalCtx, this->cutsceneCamera, 7); + this->csState = 2; + this->unk_196 = 0x3C; + this->unk_198 = 160; + player->actor.world.pos.y = -1023.76f; + this->cameraEye.y = player->actor.world.pos.y - 480.0f + 50.0f; + case 2: + if (this->unk_198 >= 131) { + player->actor.world.pos.x = -890.0f; + player->actor.world.pos.z = -2804.0f; + + player->actor.speedXZ = 0.0f; + player->actor.shape.rot.y = player->actor.world.rot.y = 0x3FFF; + + this->cameraEye.x = -890.0f; + this->cameraEye.z = player->actor.world.pos.z - 100.0f; + + this->cameraAt.x = player->actor.world.pos.x; + this->cameraAt.y = player->actor.world.pos.y + 20.0f; + this->cameraAt.z = player->actor.world.pos.z; + } + + if (this->unk_198 == 110) { + func_8002DF54(globalCtx, &this->actor, 9); + } + + if (this->unk_198 == 5) { + func_8002DF54(globalCtx, &this->actor, 12); + } + + if (this->unk_198 < 6) { + player->actor.shape.rot.y = -0x4001; + } else { + player->actor.shape.rot.y = 0x3FFF; + } + + if (this->unk_198 < 60) { + this->unk_1BC = 1; + } else { + this->unk_1BC = 2; + } + + BossDodongo_Walk(this, globalCtx); + + if (this->unk_196 == 1) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + } + + if (this->unk_196 == 0) { + Math_SmoothStepToF(&this->cameraEye.x, this->vec.x + 30.0f, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.y, this->vec.y, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.z, this->vec.z + 10.0f, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->unk_204, 1.0f, 1.0f, 0.02f, 0.0f); + } else { + this->cameraAt.x = player->actor.world.pos.x; + this->cameraAt.y = player->actor.world.pos.y + 20.0f; + this->cameraAt.z = player->actor.world.pos.z; + } + + if (gSaveContext.eventChkInf[7] & 2) { + if (this->unk_198 == 100) { + this->actor.world.pos.x = -1114.0f; + this->actor.world.pos.z = -2804.0f; + this->actor.world.rot.y = 0x3FFF; + this->unk_1A2 = 0; + this->unk_1A0 = 2; + this->csState = 4; + this->unk_196 = 30; + this->unk_198 = 150; + this->unk_204 = 0.0f; + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_008EEC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_008EEC), ANIMMODE_ONCE, 0.0f); + SkelAnime_Update(&this->skelAnime); + } + } else if (this->unk_198 == 0) { + this->csState = 3; + this->unk_19E = 0x14; + this->unk_204 = 0.0f; + } + break; + case 3: + BossDodongo_Walk(this, globalCtx); + Math_SmoothStepToF(&this->unk_20C, sinf(this->unk_19E * 0.05f) * 0.1f, 1.0f, 0.01f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.x, this->vec.x + 90.0f, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.y, this->vec.y + 50.0f, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.z, this->vec.z, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.y, this->vec.y - 10.0f, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->unk_204, 1.0f, 1.0f, 0.02f, 0.0f); + if (fabsf(player->actor.world.pos.x - this->actor.world.pos.x) < 200.0f) { + this->csState = 4; + this->unk_196 = 0x1E; + this->unk_198 = 0x96; + this->unk_204 = 0.0f; + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_008EEC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_008EEC), ANIMMODE_ONCE, -5.0f); + } + break; + case 4: + Math_SmoothStepToF(&this->unk_20C, 0.0f, 1.0f, 0.01f, 0.0f); + + if (gSaveContext.eventChkInf[7] & 2) { + phi_f0 = -50.0f; + } else { + phi_f0 = 0.0f; + } + + Math_SmoothStepToF(&this->cameraEye.x, player->actor.world.pos.x + phi_f0 + 70.0f, 0.2f, + this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.y, player->actor.world.pos.y + 10.0f, 0.2f, this->unk_204 * 20.0f, + 0.0f); + Math_SmoothStepToF(&this->cameraEye.z, player->actor.world.pos.z - 60.0f, 0.2f, this->unk_204 * 20.0f, + 0.0f); + + Math_SmoothStepToF(&this->cameraAt.x, this->vec.x, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.y, this->vec.y, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.z, this->vec.z, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->unk_204, 1.0f, 1.0f, 0.02f, 0.0f); + + if (this->unk_196 == 0) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToF(&this->unk_208, 0.05f, 1.0f, 0.005f, 0.0f); + } + + if (this->unk_198 == 0x64) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_OTAKEBI); + } + + if (this->unk_198 == 0x5A) { + if (!(gSaveContext.eventChkInf[7] & 2)) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gKingDodongoTitleCardTex), 0xA0, 0xB4, 0x80, 0x28); + } + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_FIRE_BOSS); + } + + if (this->unk_198 == 0) { + camera->eye = this->cameraEye; + camera->eyeNext = this->cameraEye; + camera->at = this->cameraAt; + func_800C08AC(globalCtx, this->cutsceneCamera, 0); + this->cutsceneCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + BossDodongo_SetupWalk(this); + this->unk_1DA = 50; + this->unk_1BC = 0; + player->actor.shape.rot.y = -0x4002; + gSaveContext.eventChkInf[7] |= 2; + } + break; + } + + if (this->cutsceneCamera != 0) { + if (this->unk_1B6 != 0) { + this->unk_1B6--; + } + + sp60.x = this->cameraEye.x; + phi_f0 = sinf((this->unk_1B6 * 3.1415f * 90.0f) / 180.0f); + sp60.y = (this->unk_1B6 * phi_f0 * 0.7f) + this->cameraEye.y; + sp60.z = this->cameraEye.z; + + sp54.x = this->cameraAt.x; + phi_f0 = sinf((this->unk_1B6 * 3.1415f * 90.0f) / 180.0f); + sp54.y = (this->unk_1B6 * phi_f0 * 0.7f) + this->cameraAt.y; + sp54.z = this->cameraAt.z; + + sp48.x = this->unk_20C; + sp48.y = 1.0f; + sp48.z = this->unk_20C; + + Gameplay_CameraSetAtEyeUp(globalCtx, this->cutsceneCamera, &sp54, &sp60, &sp48); + } +} + +void BossDodongo_SetupDamaged(BossDodongo* this) { + if (this->actionFunc != BossDodongo_Damaged) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_001074, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_001074), ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_Damaged; + } + + this->unk_1DA = 100; +} + +void BossDodongo_SetupExplode(BossDodongo* this) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_00E848, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_00E848), ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_Explode; + this->unk_1B0 = 10; + this->unk_1C0 = 2; + this->unk_1DA = 35; + this->unk_1FC = 50.0f; + this->unk_200 = 300.0f; +} + +void BossDodongo_SetupWalk(BossDodongo* this) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_01D934, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_01D934), ANIMMODE_ONCE, -10.0f); + this->unk_1AA = 0; + this->actionFunc = BossDodongo_Walk; + this->unk_1DA = 0; + this->actor.flags |= ACTOR_FLAG_0; + this->unk_1E4 = 0.0f; +} + +void BossDodongo_SetupRoll(BossDodongo* this) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_00DF38, 1.0f, 0.0f, 59.0f, ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_Roll; + this->numWallCollisions = 0; + this->unk_1DA = 27; +} + +void BossDodongo_SetupBlowFire(BossDodongo* this) { + this->actor.speedXZ = 0.0f; + this->unk_1E4 = 0.0f; + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_0061D4, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_0061D4), ANIMMODE_ONCE, 0.0f); + this->actionFunc = BossDodongo_BlowFire; + this->unk_1DA = 50; + this->unk_1AE = 0; +} + +void BossDodongo_SetupInhale(BossDodongo* this) { + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_008EEC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_008EEC), ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_Inhale; + this->unk_1DA = 100; + this->unk_1AC = 0; + this->unk_1E2 = 1; +} + +void BossDodongo_Damaged(BossDodongo* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToF(&this->unk_1F8, 1.0f, 0.5f, 0.02f, 0.001f); + Math_SmoothStepToF(&this->unk_208, 0.05f, 1.0f, 0.005f, 0.0f); + + if (Animation_OnFrame(&this->skelAnime, Animation_GetLastFrame(&object_kingdodongo_Anim_001074))) { + BossDodongo_SetupRoll(this); + } +} + +void BossDodongo_Explode(BossDodongo* this, GlobalContext* globalCtx) { + static Color_RGBA8 dustPrimColor = { 255, 255, 0, 255 }; + static Color_RGBA8 dustEnvColor = { 255, 10, 0, 255 }; + s16 pad; + Vec3f dustVel; + Vec3f dustAcell; + Vec3f dustPos; + s16 i; + + Math_SmoothStepToF(&this->unk_208, 0.05f, 1.0f, 0.005f, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->unk_1DA == 0) { + for (i = 0; i < 30; i++) { + dustVel.x = Rand_CenteredFloat(20.0f); + dustVel.y = Rand_CenteredFloat(20.0f); + dustVel.z = Rand_CenteredFloat(20.0f); + + dustAcell.x = dustVel.x * -0.1f; + dustAcell.y = dustVel.y * -0.1f; + dustAcell.z = dustVel.z * -0.1f; + + dustPos.x = this->actor.world.pos.x + (dustVel.x * 3.0f); + dustPos.y = this->actor.world.pos.y + 90.0f + (dustVel.y * 3.0f); + dustPos.z = this->actor.world.pos.z + (dustVel.z * 3.0f); + + func_8002836C(globalCtx, &dustPos, &dustVel, &dustAcell, &dustPrimColor, &dustEnvColor, 500, 10, 10); + } + + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_004E0C, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_004E0C), ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_LayDown; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); + func_80033E88(&this->actor, globalCtx, 4, 10); + this->health -= 2; + + // make sure not to die from the bomb explosion + if (this->health <= 0) { + this->health = 1; + } + } +} + +void BossDodongo_LayDown(BossDodongo* this, GlobalContext* globalCtx) { + this->unk_1BE = 10; + Math_SmoothStepToF(&this->unk_1F8, 1.3f, 1.0f, 0.1f, 0.001f); + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, Animation_GetLastFrame(&object_kingdodongo_Anim_004E0C))) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_0042A8, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_0042A8), ANIMMODE_LOOP, -5.0f); + this->actionFunc = BossDodongo_Vulnerable; + this->unk_1DA = 100; + } +} + +void BossDodongo_Vulnerable(BossDodongo* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DOWN - SFX_FLAG); + this->unk_1BE = 10; + Math_SmoothStepToF(&this->unk_1F8, 1.0f, 0.5f, 0.02f, 0.001f); + Math_SmoothStepToF(&this->unk_208, 0.05f, 1.0f, 0.005f, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->unk_1DA == 0) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_009D10, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_009D10), ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_GetUp; + } +} + +void BossDodongo_GetUp(BossDodongo* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, Animation_GetLastFrame(&object_kingdodongo_Anim_009D10))) { + BossDodongo_SetupRoll(this); + } +} + +void BossDodongo_BlowFire(BossDodongo* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f unusedZeroVec1 = { 0.0f, 0.0f, 0.0f }; + Vec3f unusedZeroVec2 = { 0.0f, 0.0f, 0.0f }; + + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 12.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_CRY); + } + + if (Animation_OnFrame(&this->skelAnime, 17.0f)) { + this->unk_1C8 = 28; + } + + if ((this->skelAnime.curFrame > 17.0f) && (this->skelAnime.curFrame < 35.0f)) { + BossDodongo_SpawnFire(this, globalCtx, this->unk_1AE); + this->unk_1AE++; + Math_SmoothStepToF(&this->unk_244, 0.0f, 1.0f, 8.0f, 0.0f); + } + + if (this->unk_1DA == 0) { + BossDodongo_SetupRoll(this); + } +} + +void BossDodongo_Inhale(BossDodongo* this, GlobalContext* GlobalContext) { + this->unk_1E2 = 1; + + if (this->unk_1AC > 20) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_BREATH - SFX_FLAG); + } + + Math_SmoothStepToF(&this->unk_208, 0.05f, 1.0f, 0.005f, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->unk_1DA == 0) { + BossDodongo_SetupBlowFire(this); + } else { + this->unk_1AC++; + + if ((this->unk_1AC > 20) && (this->unk_1AC < 82) && BossDodongo_AteExplosive(this, GlobalContext)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DRINK); + BossDodongo_SetupExplode(this); + } + } +} + +static Vec3f sCornerPositions[] = { + { -1390.0f, 0.0f, -3804.0f }, + { -1390.0f, 0.0f, -2804.0f }, + { -390.0f, 0.0f, -2804.0f }, + { -390.0f, 0.0f, -3804.0f }, +}; + +void BossDodongo_Walk(BossDodongo* this, GlobalContext* globalCtx) { + Vec3f* sp4C; + f32 sp48; + f32 sp44; + + if (this->unk_1AA == 0) { + if (Animation_OnFrame(&this->skelAnime, 14.0f)) { + Animation_PlayLoop(&this->skelAnime, &object_kingdodongo_Anim_01CAE0); + this->unk_1AA = 1; + } + } else if (this->unk_1BC != 2) { + if (((s32)this->skelAnime.curFrame == 1) || ((s32)this->skelAnime.curFrame == 31)) { + if ((s32)this->skelAnime.curFrame == 1) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->unk_410, 25.0f, 0xA, 8.0f, 0x1F4, 0xA, 0); + } else { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->unk_404, 25.0f, 0xA, 8.0f, 0x1F4, 0xA, 0); + } + + if (this->unk_1BC != 0) { + func_80078884(NA_SE_EN_DODO_K_WALK); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_WALK); + } + + if (this->cutsceneCamera == 0) { + func_80033E88(&this->actor, globalCtx, 4, 10); + } else { + this->unk_1B6 = 10; + func_800A9F6C(0.0f, 180, 20, 100); + } + } + } + + SkelAnime_Update(&this->skelAnime); + sp4C = &sCornerPositions[this->unk_1A0]; + this->unk_1EC = 0.7f; + Math_SmoothStepToF(&this->unk_1E4, this->unk_1EC * 4.0f, 1.0f, this->unk_1EC * 0.25f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.x, sp4C->x, 0.3f, this->unk_1E4, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, sp4C->z, 0.3f, this->unk_1E4, 0.0f); + sp48 = sp4C->x - this->actor.world.pos.x; + sp44 = sp4C->z - this->actor.world.pos.z; + Math_SmoothStepToF(&this->unk_1E8, 2000.0f, 1.0f, this->unk_1EC * 80.0f, 0.0f); + Math_SmoothStepToS(&this->actor.world.rot.y, Math_FAtan2F(sp48, sp44) * (0x8000 / M_PI), 5, + (this->unk_1EC * this->unk_1E8), 5); + Math_SmoothStepToS(&this->unk_1C4, 0, 2, 2000, 0); + + if ((fabsf(sp48) <= 5.0f) && (fabsf(sp44) <= 5.0f)) { + this->unk_1E8 = 0.0f; + this->unk_1E4 = 0.0f; + if (this->unk_1A2 == 0) { + this->unk_1A0++; + if (this->unk_1A0 >= 4) { + this->unk_1A0 = 0; + } + } else { + this->unk_1A0--; + if (this->unk_1A0 < 0) { + this->unk_1A0 = 3; + } + } + } + + if ((this->unk_1DA == 0) && (this->unk_1BC == 0)) { + if ((this->actor.xzDistToPlayer < 500.0f) && (this->unk_1A4 != 0) && !this->playerPosInRange) { + BossDodongo_SetupInhale(this); + BossDodongo_SpawnFire(this, globalCtx, -1); + } + + if (!this->playerPosInRange && !this->playerYawInRange) { + BossDodongo_SetupRoll(this); + } + } +} + +void BossDodongo_Roll(BossDodongo* this, GlobalContext* globalCtx) { + Vec3f* sp5C; + Vec3f sp50; + f32 sp4C; + f32 sp48; + + this->actor.flags |= ACTOR_FLAG_24; + SkelAnime_Update(&this->skelAnime); + + if (this->unk_1DA == 10) { + this->actor.velocity.y = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_CRY); + } + + if (this->unk_1DA == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_COLI2); + } + + sp5C = &sCornerPositions[this->unk_1A0]; + this->unk_1EC = 3.0f; + + if (this->unk_1DA == 0) { + Math_SmoothStepToF(&this->unk_1E4, this->unk_1EC * 5.0f, 1.0f, this->unk_1EC * 0.25f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.x, sp5C->x, 1.0f, this->unk_1E4, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, sp5C->z, 1.0f, this->unk_1E4, 0.0f); + this->unk_1C4 += 2000; + + if (this->actor.bgCheckFlags & 1) { + this->unk_228 = 7700.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_ROLL - SFX_FLAG); + + if ((this->unk_19E & 7) == 0) { + Camera_AddQuake(&globalCtx->mainCamera, 2, 1, 8); + } + + if (!(this->unk_19E & 1)) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 40.0f, 3, 8.0f, 0x1F4, 0xA, + 0); + } + } + } + + sp4C = sp5C->x - this->actor.world.pos.x; + sp48 = sp5C->z - this->actor.world.pos.z; + Math_SmoothStepToF(&this->unk_1E8, 2000.0f, 1.0f, this->unk_1EC * 100.0f, 0.0f); + Math_SmoothStepToS(&this->actor.world.rot.y, Math_FAtan2F(sp4C, sp48) * (0x8000 / M_PI), 5, + this->unk_1EC * this->unk_1E8, 0); + + if (fabsf(sp4C) <= 15.0f && fabsf(sp48) <= 15.0f) { + this->numWallCollisions++; + + if (this->numWallCollisions >= 2) { + if (this->unk_1A6 != 0) { + this->unk_1A2 = 1 - this->unk_1A2; + } + + this->unk_1E8 = 0.0f; + this->unk_1E4 = 0.0f; + BossDodongo_SetupWalk(this); + this->unk_228 = 9200.0f; + this->actor.velocity.y = 20.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_COLI); + Camera_AddQuake(&globalCtx->mainCamera, 2, 6, 8); + sp50.x = this->actor.world.pos.x; + sp50.y = this->actor.world.pos.y + 60.0f; + sp50.z = this->actor.world.pos.z; + func_80033480(globalCtx, &sp50, 250.0f, 40, 800, 10, 0); + func_80033E88(&this->actor, globalCtx, 6, 15); + } else { + this->actor.velocity.y = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_COLI2); + } + + if (this->unk_1A2 == 0) { + this->unk_1A0++; + if (this->unk_1A0 >= 4) { + this->unk_1A0 = 0; + } + } else { + this->unk_1A0--; + if (this->unk_1A0 < 0) { + this->unk_1A0 = 3; + } + } + } +} + +void BossDodongo_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossDodongo* this = (BossDodongo*)thisx; + f32 temp_f0; + s16 i; + Player* player = GET_PLAYER(globalCtx); + Player* player2 = GET_PLAYER(globalCtx); + s32 pad; + + this->unk_1E2 = 0; + this->unk_19E++; + + if (this->unk_1DA != 0) { + this->unk_1DA--; + } + + if (this->unk_1DC != 0) { + this->unk_1DC--; + } + + if (this->unk_1DE != 0) { + this->unk_1DE--; + } + + if (this->unk_1C0 != 0) { + this->unk_1C0--; + } + + if (this->unk_1C8 != 0) { + this->unk_1C8--; + } + + temp_f0 = func_808C4F6C(this, globalCtx); + + if (temp_f0 > 0.0f) { + this->unk_1A4 = temp_f0; + } else { + this->unk_1A4 = 0; + } + + temp_f0 = func_808C50A8(this, globalCtx); + + if (temp_f0 > 0.0f) { + this->unk_1A6 = temp_f0; + } else { + this->unk_1A6 = 0; + } + + BossDodongo_PlayerYawCheck(this, globalCtx); + BossDodongo_PlayerPosCheck(this, globalCtx); + + this->actionFunc(this, globalCtx); + + thisx->shape.rot.y = thisx->world.rot.y; + + Math_SmoothStepToF(&thisx->shape.yOffset, this->unk_228, 1.0f, 100.0f, 0.0f); + Actor_MoveForward(thisx); + BossDodongo_UpdateDamage(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 10.0f, 10.0f, 20.0f, 4); + Math_SmoothStepToF(&this->unk_208, 0, 1, 0.001f, 0.0); + Math_SmoothStepToF(&this->unk_20C, 0, 1, 0.001f, 0.0); + + if ((this->unk_19E % 128) == 0) { + for (i = 0; i < 50; i++) { + this->unk_324[i] = (Rand_ZeroOne() * 0.25f) + 0.5f; + } + } + + for (i = 0; i < 50; i++) { + this->unk_25C[i] += this->unk_324[i]; + } + + if (this->unk_1C8 != 0) { + if (this->unk_1C8 >= 11) { + Math_SmoothStepToF(&this->unk_240, (this->unk_1C8 & 1) ? (40.0f) : (60.0f), 1.0f, 50.0f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_240, 0.0f, 1, 10.0f, 0.0); + } + + if ((globalCtx->envCtx.adjLight1Color[2] == 0) && (globalCtx->envCtx.adjAmbientColor[2] == 0)) { + globalCtx->envCtx.adjLight1Color[0] = (u8)this->unk_240; + globalCtx->envCtx.adjLight1Color[1] = (u8)(this->unk_240 * 0.1f); + globalCtx->envCtx.adjAmbientColor[0] = (u8)this->unk_240; + globalCtx->envCtx.adjAmbientColor[1] = (u8)(this->unk_240 * 0.1f); + } + } + + if (this->unk_1BE != 0) { + if (this->unk_1BE >= 1000) { + Math_SmoothStepToF(&this->colorFilterR, 30.0f, 1, 20.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterG, 10.0f, 1, 20.0f, 0.0); + } else { + this->unk_1BE--; + Math_SmoothStepToF(&this->colorFilterR, 255.0f, 1, 20.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterG, 0.0f, 1, 20.0f, 0.0); + } + + Math_SmoothStepToF(&this->colorFilterB, 0.0f, 1, 20.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterMin, 900.0f, 1, 10.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterMax, 1099.0f, 1, 10.0f, 0.0); + } else { + Math_SmoothStepToF(&this->colorFilterR, globalCtx->lightCtx.fogColor[0], 1, 5.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterG, globalCtx->lightCtx.fogColor[1], 1.0f, 5.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterB, globalCtx->lightCtx.fogColor[2], 1.0f, 5.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterMin, globalCtx->lightCtx.fogNear, 1.0, 5.0f, 0.0); + Math_SmoothStepToF(&this->colorFilterMax, 1000.0f, 1, 5.0f, 0.0); + } + + if (player->actor.world.pos.y < -1000.0f) { + s16 phi_s0_3; + s16 sp90; + s16 magma2DrawMode; + s16 magmaScale = 0; + + if (this->unk_224 > 1.9f) { + phi_s0_3 = 1; + magma2DrawMode = 0; + sp90 = 0; + } else if (this->unk_224 > 1.7f) { + phi_s0_3 = 3; + sp90 = 1; + if (globalCtx) {} + magma2DrawMode = 0; + } else if (this->unk_224 > 1.4f) { + phi_s0_3 = 7; + sp90 = 3; + magma2DrawMode = Rand_ZeroOne() * 1.9f; + } else if (this->unk_224 > 1.1f) { + phi_s0_3 = 7; + sp90 = 4095; + magma2DrawMode = Rand_ZeroOne() * 1.9f; + } else { + phi_s0_3 = 1; + sp90 = -1; + magma2DrawMode = 1; + magmaScale = ((s16)(Rand_ZeroOne() * 50)) - 50; + } + + if (player2->csMode >= 10) { + phi_s0_3 = -1; + } + + if ((this->unk_19E & phi_s0_3) == 0) { + static Color_RGBA8 magmaPrimColor[] = { { 255, 255, 0, 255 }, { 0, 0, 0, 150 } }; + static Color_RGBA8 magmaEnvColor[] = { { 255, 0, 0, 255 }, { 0, 0, 0, 0 } }; + Vec3f sp84; + f32 temp_f12; + f32 temp_f10; + + temp_f12 = Rand_ZeroOne() * 330.0f; + temp_f10 = Rand_ZeroOne() * 6.28f; + sp84.x = (sinf(temp_f10) * temp_f12) + (-890.0f); + sp84.y = -1523.76f; + sp84.z = (cosf(temp_f10) * temp_f12) + (-3304.0f); + EffectSsGMagma2_Spawn(globalCtx, &sp84, &magmaPrimColor[magma2DrawMode], &magmaEnvColor[magma2DrawMode], + 10 - (magma2DrawMode * 5), magma2DrawMode, magmaScale + 100); + } + + if ((this->unk_19E & sp90) == 0) { + Vec3f sp6C = { 0.0f, 0.0f, 0.0f }; + Vec3f sp60 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp54; + f32 sp50 = Rand_ZeroOne() * 330.0f; + f32 sp4C = Rand_ZeroOne() * 6.28f; + + sp54.x = sinf(sp4C) * sp50 + (-890.0f); + sp54.y = -1523.76f; + sp54.z = cosf(sp4C) * sp50 + (-3304.0f); + EffectSsGMagma_Spawn(globalCtx, &sp54); + for (i = 0; i < 4; i++) { + sp60.y = 0.4f; + sp60.x = Rand_CenteredFloat(0.5f); + sp60.z = Rand_CenteredFloat(0.5f); + sp50 = Rand_ZeroOne() * 330.0f; + sp4C = Rand_ZeroOne() * 6.28f; + sp54.x = sinf(sp4C) * sp50 + (-890.0f); + sp54.y = -1513.76f; + sp54.z = cosf(sp4C) * sp50 + (-3304.0f); + func_808C17C8(globalCtx, &sp54, &sp6C, &sp60, ((s16)Rand_ZeroFloat(2.0f)) + 6, 0x50); + } + } + + func_808C1554(gDodongosCavernBossLavaFloorTex, sLavaFloorLavaTex, this->unk_19E, this->unk_224); + } + + if (this->unk_1C6 != 0) { + u16* ptr1 = ResourceMgr_LoadTexByName(sLavaFloorLavaTex); + u16* ptr2 = ResourceMgr_LoadTexByName(sLavaFloorRockTex); + s16 i2; + + for (i2 = 0; i2 < 20; i2++) { + s16 new_var = this->unk_1C2 & 0x7FF; + + ptr1[new_var] = ptr2[new_var]; + this->unk_1C2 += 37; + } + Math_SmoothStepToF(&this->unk_224, 0.0f, 1.0f, 0.01f, 0.0f); + } + + if (this->unk_1BC == 0) { + if (this->actionFunc != BossDodongo_DeathCutscene) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->actionFunc == BossDodongo_Roll) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + this->collider.elements[0].dim.scale = (this->actionFunc == BossDodongo_Inhale) ? 0.0f : 1.0f; + + for (i = 6; i < 19; i++) { + if (i != 12) { + this->collider.elements[i].dim.scale = (this->actionFunc == BossDodongo_Roll) ? 0.0f : 1.0f; + } + } + + if (this->unk_244 != 0) { + MREG(64) = 1; + MREG(65) = 255; + MREG(66) = 80; + MREG(67) = 0; + MREG(68) = (u8)this->unk_244; + } else { + MREG(64) = 0; + } + + Math_SmoothStepToF(&this->unk_244, 0.0f, 1.0f, 2.0f, 0.0f); + BossDodongo_UpdateEffects(globalCtx); +} + +s32 BossDodongo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + f32 mtxScaleY; + f32 mtxScaleZ; + BossDodongo* this = (BossDodongo*)thisx; + + // required for matching + if ((limbIndex == 6) || (limbIndex == 7)) { + if (this->unk_25C) {} + goto block_1; + } +block_1: + Matrix_TranslateRotateZYX(pos, rot); + + if (*dList != NULL) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_dodongo.c", 3787); + + mtxScaleZ = 1.0f; + mtxScaleY = 1.0f; + + if ((limbIndex == 33) || (limbIndex == 48)) { + mtxScaleY = mtxScaleZ = this->unk_1F8; + } + + Matrix_Push(); + Matrix_Scale(1.0f, mtxScaleY, mtxScaleZ, MTXMODE_APPLY); + + if ((limbIndex != 6) && (limbIndex != 7)) { + Matrix_RotateX(this->unk_25C[limbIndex] * 0.115f, MTXMODE_APPLY); + Matrix_RotateY(this->unk_25C[limbIndex] * 0.13f, MTXMODE_APPLY); + Matrix_RotateZ(this->unk_25C[limbIndex] * 0.1f, MTXMODE_APPLY); + Matrix_Scale(1.0f - this->unk_208, this->unk_208 + 1.0f, 1.0f - this->unk_208, MTXMODE_APPLY); + Matrix_RotateZ(-(this->unk_25C[limbIndex] * 0.1f), MTXMODE_APPLY); + Matrix_RotateY(-(this->unk_25C[limbIndex] * 0.13f), MTXMODE_APPLY); + Matrix_RotateX(-(this->unk_25C[limbIndex] * 0.115f), MTXMODE_APPLY); + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_dodongo.c", 3822), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_dodongo.c", 3826); + } + { s32 pad; } // Required to match + return 1; +} + +void BossDodongo_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_808CA450 = { 5000.0f, -2500.0f, 0.0f }; + static Vec3f D_808CA45C = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_808CA468 = { 11500.0f, -3000.0f, 0.0f }; + static Vec3f D_808CA474 = { 5000.0f, -2000.0f, 0.0f }; + static Vec3f D_808CA480 = { 8000.0f, 0.0f, 0.0f }; + static Vec3f D_808CA48C = { 8000.0f, 0.0f, 0.0f }; + BossDodongo* this = (BossDodongo*)thisx; + + if (limbIndex == 6) { + Matrix_MultVec3f(&D_808CA45C, &this->vec); + Matrix_MultVec3f(&D_808CA450, &this->actor.focus.pos); + Matrix_MultVec3f(&D_808CA468, &this->firePos); + Matrix_MultVec3f(&D_808CA474, &this->mouthPos); + } else if (limbIndex == 39) { + Matrix_MultVec3f(&D_808CA480, &this->unk_410); + } else if (limbIndex == 46) { + Matrix_MultVec3f(&D_808CA48C, &this->unk_404); + } + Collider_UpdateSpheres(limbIndex, &this->collider); +} + +void BossDodongo_Draw(Actor* thisx, GlobalContext* globalCtx) { + BossDodongo* this = (BossDodongo*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_dodongo.c", 3922); + func_80093D18(globalCtx->state.gfxCtx); + + if ((this->unk_1C0 >= 2) && (this->unk_1C0 & 1)) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099); + } else { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, (u32)this->colorFilterR, (u32)this->colorFilterG, + (u32)this->colorFilterB, 0, this->colorFilterMin, this->colorFilterMax); + } + + Matrix_RotateZ(this->unk_23C, MTXMODE_APPLY); + Matrix_RotateX((this->unk_1C4 / 32768.0f) * 3.14159f, MTXMODE_APPLY); + + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, BossDodongo_OverrideLimbDraw, + BossDodongo_PostLimbDraw, this); + + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_dodongo.c", 3981); + + BossDodongo_DrawEffects(globalCtx); +} + +f32 func_808C4F6C(BossDodongo* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 zDiff; + f32 sp2C; + s32 pad; + f32 temp_f2; + f32 rotation; + Player* player = GET_PLAYER(globalCtx); + + xDiff = player->actor.world.pos.x - this->actor.world.pos.x; + zDiff = player->actor.world.pos.z - this->actor.world.pos.z; + + rotation = Math_CosS(-this->actor.world.rot.y); + sp2C = (Math_SinS(-this->actor.world.rot.y) * zDiff) + (rotation * xDiff); + rotation = Math_SinS(-this->actor.world.rot.y); + temp_f2 = (Math_CosS(-this->actor.world.rot.y) * zDiff) + (-rotation * xDiff); + + if ((fabsf(sp2C) < 150.0f) && (temp_f2 >= 100.0f) && (temp_f2 <= 2000.0f)) { + return temp_f2; + } + return -1.0f; +} + +f32 func_808C50A8(BossDodongo* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 zDiff; + f32 sp2C; + s32 pad; + f32 temp_f2; + f32 rotation; + Player* player = GET_PLAYER(globalCtx); + + xDiff = player->actor.world.pos.x - this->actor.world.pos.x; + zDiff = player->actor.world.pos.z - this->actor.world.pos.z; + + rotation = Math_CosS(-0x8000 - this->actor.world.rot.y); + sp2C = (Math_SinS(-0x8000 - this->actor.world.rot.y) * zDiff) + (rotation * xDiff); + rotation = Math_SinS(-0x8000 - this->actor.world.rot.y); + temp_f2 = (Math_CosS(-0x8000 - this->actor.world.rot.y) * zDiff) + (-rotation * xDiff); + + if ((fabsf(sp2C) < 150.0f) && (100.0f <= temp_f2) && (temp_f2 <= 2000.0f)) { + return temp_f2; + } + + return -1.0f; +} + +void BossDodongo_PlayerYawCheck(BossDodongo* this, GlobalContext* globalCtx) { + s16 yawDiff = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y; + + if ((yawDiff < 0x38E3) && (-0x38E3 < yawDiff)) { + this->playerYawInRange = true; + } else { + this->playerYawInRange = false; + } +} + +void BossDodongo_PlayerPosCheck(BossDodongo* this, GlobalContext* globalCtx) { + Vec3f* temp_v1; + s16 i; + + this->playerPosInRange = false; + + for (i = 0; i < 4; i++) { + temp_v1 = &sCornerPositions[i]; + + if ((fabsf(this->actor.world.pos.x - temp_v1->x) < 200.0f) && + (fabsf(this->actor.world.pos.z - temp_v1->z) < 200.0f)) { + this->playerPosInRange = true; + break; + } + } +} + +void BossDodongo_SpawnFire(BossDodongo* this, GlobalContext* globalCtx, s16 params) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_BDFIRE, this->vec.x, this->vec.y - 20.0f, + this->vec.z, 0, this->actor.shape.rot.y, 0, params); +} + +void BossDodongo_UpdateDamage(BossDodongo* this, GlobalContext* globalCtx) { + s32 pad; + ColliderInfo* item1; + u8 swordDamage; + s32 damage; + ColliderInfo* item2; + s16 i; + + if ((this->health <= 0) && (this->actionFunc != BossDodongo_DeathCutscene)) { + BossDodongo_SetupDeathCutscene(this); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + return; + } + + if (this->unk_1C0 == 0) { + if (this->actionFunc == BossDodongo_Inhale) { + for (i = 0; i < 19; i++) { + if (this->collider.elements[i].info.bumperFlags & 2) { + item1 = this->collider.elements[i].info.acHitInfo; + item2 = item1; + + if ((item2->toucher.dmgFlags & 0x10) || (item2->toucher.dmgFlags & 4)) { + this->collider.elements[i].info.bumperFlags &= ~2; + this->unk_1C0 = 2; + BossDodongo_SetupWalk(this); + this->unk_1DA = 0x32; + return; + } + } + } + } + + if (this->collider.elements->info.bumperFlags & 2) { + this->collider.elements->info.bumperFlags &= ~2; + item1 = this->collider.elements[0].info.acHitInfo; + if ((this->actionFunc == BossDodongo_Vulnerable) || (this->actionFunc == BossDodongo_LayDown)) { + swordDamage = damage = CollisionCheck_GetSwordDamage(item1->toucher.dmgFlags); + + if (damage != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DAMAGE); + BossDodongo_SetupDamaged(this); + this->unk_1C0 = 5; + this->health -= swordDamage; + } + } + } + } +} + +void BossDodongo_SetupDeathCutscene(BossDodongo* this) { + this->actor.speedXZ = 0.0f; + this->unk_1E4 = 0.0f; + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_002D0C, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_002D0C), ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossDodongo_DeathCutscene; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DEAD); + this->unk_1DA = 0; + this->csState = 0; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->unk_1BC = 1; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); +} + +void BossDodongo_DeathCutscene(BossDodongo* this, GlobalContext* globalCtx) { + Vec3f* cornerPos; + Vec3f sp198; + Vec3f sp184; + f32 tempSin; + f32 tempCos; + f32 sp178; + s16 i; + Vec3f effectPos; + Camera* camera; + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + + switch (this->csState) { + case 0: + this->csState = 5; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 1); + this->cutsceneCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_UNK3); + Gameplay_ChangeCameraStatus(globalCtx, this->cutsceneCamera, CAM_STAT_ACTIVE); + camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + this->cameraEye.x = camera->eye.x; + this->cameraEye.y = camera->eye.y; + this->cameraEye.z = camera->eye.z; + this->cameraAt.x = camera->at.x; + this->cameraAt.y = camera->at.y; + this->cameraAt.z = camera->at.z; + break; + case 5: + tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f; + tempCos = Math_CosS(this->actor.shape.rot.y - 0x1388) * 150.0f; + Math_SmoothStepToF(&player->actor.world.pos.x, this->actor.world.pos.x + tempSin, 0.5f, 5.0f, 0.0f); + Math_SmoothStepToF(&player->actor.world.pos.z, this->actor.world.pos.z + tempCos, 0.5f, 5.0f, 0.0f); + Math_SmoothStepToF(&this->unk_208, 0.07f, 1.0f, 0.005f, 0.0f); + tempSin = Math_SinS(this->actor.world.rot.y) * 230.0f; + tempCos = Math_CosS(this->actor.world.rot.y) * 230.0f; + Math_SmoothStepToF(&this->cameraEye.x, this->actor.world.pos.x + tempSin, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->cameraEye.y, this->actor.world.pos.y + 20.0f, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->cameraEye.z, this->actor.world.pos.z + tempCos, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->cameraAt.x, this->actor.world.pos.x, 0.2f, 30.0f, 0.1f); + Math_SmoothStepToF(&this->cameraAt.y, this->actor.focus.pos.y - 70.0f, 0.2f, 30.0f, 0.1f); + Math_SmoothStepToF(&this->cameraAt.z, this->actor.world.pos.z, 0.2f, 30.0f, 0.1f); + if (Animation_OnFrame(&this->skelAnime, Animation_GetLastFrame(&object_kingdodongo_Anim_002D0C))) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_003CF8, 1.0f, 0.0f, + Animation_GetLastFrame(&object_kingdodongo_Anim_003CF8), ANIMMODE_ONCE, -1.0f); + this->csState = 6; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_BREAKWALL, -890.0f, -1523.76f, -3304.0f, 0, 0, 0, + 0x6000); + } + break; + case 6: + Math_SmoothStepToF(&this->cameraAt.x, this->actor.world.pos.x, 0.2f, 30.0f, 0.1f); + Math_SmoothStepToF(&this->cameraAt.y, (this->actor.world.pos.y - 70.0f) + 130.0f, 0.2f, 20.0f, 0.1f); + Math_SmoothStepToF(&this->cameraAt.z, this->actor.world.pos.z, 0.2f, 30.0f, 0.1f); + + if (Animation_OnFrame(&this->skelAnime, Animation_GetLastFrame(&object_kingdodongo_Anim_003CF8))) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_00DF38, 1.0f, 30.0f, 59.0f, ANIMMODE_ONCE, + -1.0f); + this->csState = 7; + this->unk_228 = 7700.0f; + this->unk_204 = 0.0f; + this->unk_1E4 = 0.0f; + this->numWallCollisions = 0; + this->unk_19E = 0; + } + break; + case 7: + this->unk_1C4 += 0x7D0; + Math_SmoothStepToF(&this->cameraAt.x, this->actor.world.pos.x, 0.2f, 30.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.y, (this->actor.world.pos.y - 70.0f) + 130.0f, 0.2f, 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.z, this->actor.world.pos.z, 0.2f, 30.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.x, -890.0f, 0.1f, this->unk_204 * 5.0f, 0.1f); + Math_SmoothStepToF(&this->cameraEye.z, -3304.0f, 0.1f, this->unk_204 * 5.0f, 0.1f); + Math_SmoothStepToF(&this->unk_204, 1.0f, 1.0f, 0.1f, 0.0f); + if (this->unk_1DA == 1) { + this->csState = 8; + this->actor.speedXZ = this->unk_1E4 / 1.5f; + if (this->unk_1A2 == 0) { + this->unk_238 = 250.0f; + } else { + this->unk_238 = -250.0f; + } + this->unk_1DA = 1000; + this->unk_234 = 2000.0f; + } else { + cornerPos = &sCornerPositions[this->unk_1A0]; + this->unk_1EC = 3.0f; + Math_SmoothStepToF(&this->unk_1E4, this->unk_1EC * 5.0f, 1.0f, this->unk_1EC * 0.25f, 0.0f); + tempSin = cornerPos->x - this->actor.world.pos.x; + tempCos = cornerPos->z - this->actor.world.pos.z; + sp178 = sqrtf(SQ(tempSin) + SQ(tempCos)) - 200.0f; + if ((sqrtf(SQ(tempSin) + SQ(tempCos)) < 200.0f) || (this->unk_1DA != 0)) { + sp178 = 0.0f; + } + sp178 = CLAMP_MAX(sp178, 70.0f); + this->unk_23C = (Math_SinS(this->unk_19E * 1000) * -50.0f) / 100.0f; + + sp198.x = Math_SinS(this->unk_19E * 1000) * sp178; + sp198.y = sp198.z = 0.0f; + + Matrix_RotateY(this->actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_MultVec3f(&sp198, &sp184); + + Math_SmoothStepToF(&this->actor.world.pos.x, cornerPos->x + sp184.x, 1.0f, this->unk_1E4, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, cornerPos->z + sp184.z, 1.0f, this->unk_1E4, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_ROLL - SFX_FLAG); + if ((this->unk_19E & 7) == 0) { + Camera_AddQuake(&globalCtx->mainCamera, 2, 1, 8); + } + if (!(this->unk_19E & 1)) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 40.0f, 3, 8.0f, 0x1F4, + 0xA, 0); + } + tempSin = cornerPos->x - this->actor.world.pos.x; + tempCos = cornerPos->z - this->actor.world.pos.z; + Math_SmoothStepToF(&this->unk_1E8, 1500.0f, 1.0f, this->unk_1EC * 100.0f, 0.0f); + Math_SmoothStepToS(&this->actor.world.rot.y, (Math_FAtan2F(tempSin, tempCos) * (0x8000 / M_PI)), 5, + (this->unk_1EC * this->unk_1E8), 0); + + if ((fabsf(tempSin) <= 15.0f) && (fabsf(tempCos) <= 15.0f)) { + Vec3f dustPos; + + this->actor.velocity.y = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_COLI2); + if (this->unk_1A2 == 0) { + this->unk_1A0 = this->unk_1A0 + 1; + if (this->unk_1A0 >= 4) { + this->unk_1A0 = 0; + } + } else { + this->unk_1A0--; + if (this->unk_1A0 < 0) { + this->unk_1A0 = 3; + } + } + this->unk_1DA = 0xA; + dustPos.x = this->actor.world.pos.x; + dustPos.y = this->actor.world.pos.y + 60.0f; + dustPos.z = this->actor.world.pos.z; + func_80033480(globalCtx, &dustPos, 250.0f, 0x28, 0x320, 0xA, 0); + } + } + break; + case 8: + case 9: + if (this->unk_1DA == 884) { + Animation_Change(&this->skelAnime, &object_kingdodongo_Anim_0042A8, 1.0f, 0.0f, + (f32)Animation_GetLastFrame(&object_kingdodongo_Anim_0042A8), ANIMMODE_LOOP, -20.0f); + tempSin = this->cameraEye.x - this->actor.world.pos.x; + tempCos = this->cameraEye.z - this->actor.world.pos.z; + this->unk_22C = sqrtf(SQ(tempSin) + SQ(tempCos)); + this->unk_230 = Math_FAtan2F(tempSin, tempCos); + this->unk_1DC = 350; + this->csState = 9; + } + if (this->unk_1DA < 854) { + for (i = 0; i < 2; i++) { + func_808C12C4(D_808C7000, this->unk_1CC); + if (this->unk_1CC < 0xFF) { + this->unk_1CC++; + } + } + } + if (this->unk_1DA < 984) { + Math_SmoothStepToS(&this->unk_1C4, -0x4000, 0xA, 0x12C, 0); + } + if (this->unk_1DA == 904) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_END); + } + if (this->unk_1DA < 854) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_LAST - SFX_FLAG); + } + if (this->unk_1DA == 960) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_LAVA); + } + if (this->unk_1DA < 960) { + Math_SmoothStepToF(&this->actor.shape.shadowScale, 0.0f, 1.0f, 10.0f, 0.0f); + if (this->unk_1DA >= 710) { + + if (this->unk_1DA == 710) { + Vec3f sp124[] = { + { -440.0f, 0.0f, -3304.0f }, + { -890.0f, 0.0f, -3754.0f }, + { -1340.0f, 0.0f, -3304.0f }, + { -890.0f, 0.0f, -2854.0f }, + }; + Vec3f spF4[] = { + { -890.0f, 0.0f, -2854.0f }, + { -440.0f, 0.0f, -3304.0f }, + { -890.0f, 0.0f, -3754.0f }, + { -1340.0f, 0.0f, -3304.0f }, + }; + Vec3f* phi_v0_2; + + this->unk_1C6 = 1; + if (this->unk_1A2 == 0) { + phi_v0_2 = &sp124[this->unk_1A0]; + } else { + phi_v0_2 = &spF4[this->unk_1A0]; + } + player->actor.world.pos.x = phi_v0_2->x; + player->actor.world.pos.z = phi_v0_2->z; + this->unk_204 = 0.0f; + } + if (this->unk_1DA >= 885) { + Math_SmoothStepToF(&this->unk_228, 200.0, 0.2f, 100.0f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_228, -6600.0f, 0.2f, 30.0f, 0.0f); + } + { + static Vec3f dustVel = { 0.0f, 0.0f, 0.0f }; + static Vec3f dustAcell = { 0.0f, 1.0f, 0.0f }; + static Color_RGBA8 dustPrimColor = { 255, 255, 100, 255 }; + static Color_RGBA8 dustEnvColor = { 255, 100, 0, 255 }; + s16 colorIndex; + Color_RGBA8 magmaPrimColor2[] = { { 255, 255, 0, 255 }, { 0, 0, 0, 100 } }; + Color_RGBA8 magmaEnvColor2[] = { { 255, 0, 0, 255 }, { 0, 0, 0, 0 } }; + + effectPos.x = Rand_CenteredFloat(120.0f) + this->actor.focus.pos.x; + effectPos.y = Rand_ZeroFloat(50.0f) + this->actor.world.pos.y; + effectPos.z = Rand_CenteredFloat(120.0f) + this->actor.focus.pos.z; + func_8002836C(globalCtx, &effectPos, &dustVel, &dustAcell, &dustPrimColor, &dustEnvColor, 0x1F4, + 0xA, 0xA); + effectPos.x = Rand_CenteredFloat(120.0f) + this->actor.focus.pos.x; + effectPos.y = -1498.76f; + effectPos.z = Rand_CenteredFloat(120.0f) + this->actor.focus.pos.z; + colorIndex = (Rand_ZeroOne() * 1.9f); + EffectSsGMagma2_Spawn(globalCtx, &effectPos, &magmaPrimColor2[colorIndex], + &magmaEnvColor2[colorIndex], 10 - (colorIndex * 5), colorIndex, + (s16)(Rand_ZeroOne() * 100.0f) + 100); + } + } + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_ROLL - SFX_FLAG); + if (!(this->unk_19E & 1)) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 40.0f, 3, 8.0f, 0x1F4, + 0xA, 0); + } + } + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.2f, 0.1f, 0.0f); + this->actor.world.rot.y += (s16)this->unk_238; + this->unk_1C4 += (s16)this->unk_234; + if (this->unk_1DA >= 0x367) { + if (this->unk_1A2 == 0) { + if (this->unk_238 < 450.0f) { + this->unk_238 += 10.0f; + } + } else if (-450.0f < this->unk_238) { + this->unk_238 -= 10.0f; + } + } else { + Math_SmoothStepToF(&this->unk_238, 0.0f, 0.05f, 40.0f, 0.0f); + } + Math_SmoothStepToF(&this->unk_234, 0.0f, 0.2f, 17.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.x, this->actor.world.pos.x, 0.2f, 30.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.y, (this->actor.world.pos.y - 70.0f) + 130.0f, 0.2f, 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraAt.z, this->actor.world.pos.z, 0.2f, 30.0f, 0.0f); + if (this->csState == 9) { + if (this->unk_1DA < 0x2C6) { + Vec3f spAC[] = { { -390.0f, 0.0f, -3304.0f }, + { -890.0f, 0.0f, -3804.0f }, + { -1390.0f, 0.0f, -3304.0f }, + { -890.0f, 0.0f, -2804.0f } }; + + Vec3f sp7C[] = { { -890.0f, 0.0f, -2804.0f }, + { -390.0f, 0.0f, -3304.0f }, + { -890.0f, 0.0f, -3804.0f }, + { -1390.0f, 0.0f, -3304.0f } }; + Vec3f* sp78; + s32 pad74; + + if (this->unk_1A2 == 0) { + sp78 = &spAC[this->unk_1A0]; + } else { + sp78 = &sp7C[this->unk_1A0]; + } + + Math_SmoothStepToF(&this->cameraEye.x, sp78->x, 0.2f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.y, player->actor.world.pos.y + 30.0f, 0.1f, + this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.z, sp78->z, 0.1f, this->unk_204 * 20.0f, 0.0f); + Math_SmoothStepToF(&this->unk_204, 1.0f, 1.0f, 0.02f, 0.0f); + } else { + if (this->unk_1A2 == 0) { + this->unk_230 += 0.01f; + } else { + this->unk_230 -= 0.01f; + } + Math_SmoothStepToF(&this->unk_22C, 220.0f, 0.1f, 5.0f, 0.1f); + tempSin = sinf(this->unk_230) * (*this).unk_22C; + tempCos = cosf(this->unk_230) * (*this).unk_22C; + Math_SmoothStepToF(&this->cameraEye.x, this->actor.world.pos.x + tempSin, 0.2f, 50.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.y, this->actor.world.pos.y + 20.0f, 0.2f, 50.0f, 0.0f); + Math_SmoothStepToF(&this->cameraEye.z, this->actor.world.pos.z + tempCos, 0.2f, 50.0f, 0.0f); + Math_SmoothStepToF(&this->unk_23C, 0.0f, 0.2f, 0.01f, 0.0f); + } + } else { + + if (this->unk_1A2 == 0) { + Math_SmoothStepToF(&this->unk_23C, -0.5f, 0.2f, 0.05f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_23C, 0.5f, 0.2f, 0.05f, 0.0f); + } + + Math_SmoothStepToF(&this->cameraEye.x, -890.0f, 0.1f, this->unk_204 * 5.0f, 0.1f); + Math_SmoothStepToF(&this->cameraEye.z, -3304.0f, 0.1f, this->unk_204 * 5.0f, 0.1f); + Math_SmoothStepToF(&this->unk_204, 1.0f, 1.0f, 0.05f, 0.0f); + } + + if (this->unk_1DA == 820) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, + Math_SinS(this->actor.shape.rot.y) * -50.0f + this->actor.world.pos.x, + this->actor.world.pos.y, + Math_CosS(this->actor.shape.rot.y) * -50.0f + this->actor.world.pos.z, 0, 0, 0, 0); + } + if (this->unk_1DA == 600) { + camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + camera->eye = this->cameraEye; + camera->eyeNext = this->cameraEye; + camera->at = this->cameraAt; + func_800C08AC(globalCtx, this->cutsceneCamera, 0); + this->unk_1BC = 0; + this->cutsceneCamera = MAIN_CAM; + this->csState = 100; + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, -890.0f, -1523.76f, + -3304.0f, 0, 0, 0, WARP_DUNGEON_CHILD); + this->skelAnime.playSpeed = 0.0f; + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + case 100: + if ((this->unk_1DA < 0x2C6) && (Rand_ZeroOne() < 0.5f)) { + Vec3f sp68; + Color_RGBA8 D_808CA568 = { 0, 0, 0, 100 }; + Color_RGBA8 D_808CA56C = { 0, 0, 0, 0 }; + + sp68.x = Rand_CenteredFloat(60.0f) + this->actor.focus.pos.x; + sp68.y = (Rand_ZeroOne() * 50.0f) + -1498.76f; + sp68.z = Rand_CenteredFloat(60.0f) + this->actor.focus.pos.z; + EffectSsGMagma2_Spawn(globalCtx, &sp68, &D_808CA568, &D_808CA56C, 5, 1, + (s16)(Rand_ZeroOne() * 50.0f) + 50); + } + break; + } + if (this->cutsceneCamera != MAIN_CAM) { + Gameplay_CameraSetAtEye(globalCtx, this->cutsceneCamera, &this->cameraAt, &this->cameraEye); + } +} + +void BossDodongo_UpdateEffects(GlobalContext* globalCtx) { + BossDodongoEffect* eff = (BossDodongoEffect*)globalCtx->specialEffects; + Color_RGB8 effectColors[] = { { 255, 128, 0 }, { 255, 0, 0 }, { 255, 255, 0 }, { 255, 0, 0 } }; + s16 colorIndex; + s16 i; + + for (i = 0; i < 80; i++, eff++) { + if (eff->unk_24 != 0) { + eff->unk_00.x += eff->unk_0C.x; + eff->unk_00.y += eff->unk_0C.y; + eff->unk_00.z += eff->unk_0C.z; + eff->unk_25++; + eff->unk_0C.x += eff->unk_18.x; + eff->unk_0C.y += eff->unk_18.y; + eff->unk_0C.z += eff->unk_18.z; + if (eff->unk_24 == 1) { + colorIndex = eff->unk_25 % 4; + eff->color.r = effectColors[colorIndex].r; + eff->color.g = effectColors[colorIndex].g; + eff->color.b = effectColors[colorIndex].b; + eff->alpha -= 20; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->unk_24 = 0; + } + } + } + } +} + +void BossDodongo_DrawEffects(GlobalContext* globalCtx) { + MtxF* unkMtx; + s16 i; + u8 phi_s3 = 0; + BossDodongoEffect* eff; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + eff = (BossDodongoEffect*)globalCtx->specialEffects; + + OPEN_DISPS(gfxCtx, "../z_boss_dodongo.c", 5228); + + func_80093D84(globalCtx->state.gfxCtx); + unkMtx = &globalCtx->billboardMtxF; + + gSPInvalidateTexCache(POLY_XLU_DISP++, 0); + + for (i = 0; i < 80; i++, eff++) { + if (eff->unk_24 == 1) { + gDPPipeSync(POLY_XLU_DISP++); + + if (phi_s3 == 0) { + gSPDisplayList(POLY_XLU_DISP++, object_kingdodongo_DL_009D50); + phi_s3++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, eff->color.r, eff->color.g, eff->color.b, eff->alpha); + Matrix_Translate(eff->unk_00.x, eff->unk_00.y, eff->unk_00.z, MTXMODE_NEW); + Matrix_ReplaceRotation(unkMtx); + Matrix_Scale(eff->unk_2C, eff->unk_2C, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_dodongo.c", 5253), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_kingdodongo_DL_009DD0); + } + } + + CLOSE_DISPS(gfxCtx, "../z_boss_dodongo.c", 5258); +} diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h new file mode 100644 index 000000000..62aefe8cc --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h @@ -0,0 +1,101 @@ +#ifndef Z_BOSS_DODONGO_H +#define Z_BOSS_DODONGO_H + +#include "ultra64.h" +#include "global.h" + +struct BossDodongo; + +typedef void (*BossDodongoActionFunc)(struct BossDodongo*, GlobalContext*); + +typedef struct { + /* 0x00 */ Vec3f unk_00; + /* 0x0C */ Vec3f unk_0C; + /* 0x18 */ Vec3f unk_18; + /* 0x24 */ u8 unk_24; + /* 0x25 */ u8 unk_25; + /* 0x26 */ Color_RGB8 color; + /* 0x2A */ s16 alpha; + /* 0x2C */ f32 unk_2C; +} BossDodongoEffect; // Size = 0x30 + +typedef struct BossDodongo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ BossDodongoActionFunc actionFunc; + /* 0x0194 */ s16 health; + /* 0x0196 */ s16 unk_196; + /* 0x0198 */ s16 unk_198; + /* 0x019A */ s16 unk_19A; + /* 0x019C */ s16 csState; + /* 0x019E */ s16 unk_19E; + /* 0x01A0 */ s16 unk_1A0; + /* 0x01A2 */ s16 unk_1A2; + /* 0x01A4 */ s16 unk_1A4; + /* 0x01A6 */ s16 unk_1A6; + /* 0x01A8 */ s16 numWallCollisions; + /* 0x01AA */ s16 unk_1AA; + /* 0x01AC */ s16 unk_1AC; + /* 0x01AE */ s16 unk_1AE; + /* 0x01B0 */ s16 unk_1B0; + /* 0x01B2 */ char unk_1B2[0x2]; + /* 0x01B4 */ s16 cutsceneCamera; + /* 0x01B6 */ s16 unk_1B6; + /* 0x01B8 */ s16 playerYawInRange; + /* 0x01BA */ s16 playerPosInRange; + /* 0x01BC */ s16 unk_1BC; + /* 0x01BE */ s16 unk_1BE; + /* 0x01C0 */ s16 unk_1C0; + /* 0x01C2 */ s16 unk_1C2; + /* 0x01C4 */ s16 unk_1C4; // Some kind of angle + /* 0x01C6 */ s16 unk_1C6; + /* 0x01C8 */ s16 unk_1C8; + /* 0x01CA */ char unk_1CA[0x2]; + /* 0x01CC */ s16 unk_1CC; + /* 0x01CE */ char unk_1CE[0xC]; + /* 0x01DA */ s16 unk_1DA; + /* 0x01DC */ s16 unk_1DC; + /* 0x01DE */ s16 unk_1DE; + /* 0x01E0 */ s16 unk_1E0; + /* 0x01E2 */ u8 unk_1E2; + /* 0x01E3 */ s8 unk_1E3; + /* 0x01E4 */ f32 unk_1E4; + /* 0x01E8 */ f32 unk_1E8; + /* 0x01EC */ f32 unk_1EC; + /* 0x01F0 */ char unk_1F0[0x8]; + /* 0x01F8 */ f32 unk_1F8; + /* 0x01FC */ f32 unk_1FC; + /* 0x0200 */ f32 unk_200; + /* 0x0204 */ f32 unk_204; + /* 0x0208 */ f32 unk_208; + /* 0x020C */ f32 unk_20C; + /* 0x0210 */ f32 colorFilterR; + /* 0x0214 */ f32 colorFilterG; + /* 0x0214 */ f32 colorFilterB; + /* 0x021C */ f32 colorFilterMin; + /* 0x0220 */ f32 colorFilterMax; + /* 0x0224 */ f32 unk_224; + /* 0x0228 */ f32 unk_228; + /* 0x022C */ f32 unk_22C; + /* 0x0230 */ f32 unk_230; + /* 0x0234 */ f32 unk_234; + /* 0x0238 */ f32 unk_238; + /* 0x023C */ f32 unk_23C; + /* 0x0240 */ f32 unk_240; + /* 0x0244 */ f32 unk_244; + /* 0x0248 */ char unk_248[0x14]; + /* 0x025C */ f32 unk_25C[50]; + /* 0x0324 */ f32 unk_324[50]; + /* 0x03EC */ Vec3f vec; + /* 0x03F8 */ Vec3f firePos; + /* 0x0404 */ Vec3f unk_404; + /* 0x0410 */ Vec3f unk_410; + /* 0x041C */ Vec3f mouthPos; + /* 0x0428 */ Vec3f cameraEye; + /* 0x0434 */ Vec3f cameraAt; + /* 0x0440 */ ColliderJntSph collider; + /* 0x0460 */ ColliderJntSphElement items[19]; + /* 0x0920 */ BossDodongoEffect effects[80]; +} BossDodongo; // size = 0x1820 + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo_data.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo_data.c new file mode 100644 index 000000000..e9adde721 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo_data.c @@ -0,0 +1,240 @@ +#include "z_boss_dodongo.h" + +static u8 D_808C7000[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, + 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[19] = { + { + { + ELEMTYPE_UNK3, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 7, { { 8500, 1200, 0 }, 40 }, 100 }, + }, + { + { + ELEMTYPE_UNK3, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 33, { { 2000, -2000, 0 }, 60 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 60 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 16, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 17, { { 0, 0, 0 }, 40 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 22, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 23, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 24, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 29, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 30, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 31, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 32, { { 0, 0, 0 }, 50 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 38, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 39, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 40, { { 0, 0, 0 }, 40 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 45, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 46, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 47, { { 0, 0, 0 }, 40 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 19, + sJntSphElementsInit, +}; + +static Vec3f sUnkZeroVec = { 0.0f, 0.0f, 0.0f }; + +#include "overlays/ovl_Boss_Dodongo/ovl_Boss_Dodongo.h" diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c new file mode 100644 index 000000000..6553b3c11 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.c @@ -0,0 +1,1981 @@ +/* + * File: z_boss_fd.c + * Overlay: ovl_Boss_Fd + * Description: Volvagia, flying form + */ + +#include "z_boss_fd.h" +#include "objects/object_fd/object_fd.h" +#include "overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.h" +#include "overlays/actors/ovl_Bg_Vb_Sima/z_bg_vb_sima.h" +#include "overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ INTRO_FLY_EMERGE, + /* 1 */ INTRO_FLY_HOLE, + /* 2 */ INTRO_FLY_CAMERA, + /* 3 */ INTRO_FLY_RETRAT +} BossFdIntroFlyState; + +typedef enum { + /* 0 */ MANE_CENTER, + /* 1 */ MANE_RIGHT, + /* 2 */ MANE_LEFT +} BossFdManeIndex; + +typedef enum { + /* 0 */ EYE_OPEN, + /* 1 */ EYE_HALF, + /* 2 */ EYE_CLOSED +} BossFdEyeState; + +void BossFd_Init(Actor* thisx, GlobalContext* globalCtx); +void BossFd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossFd_Update(Actor* thisx, GlobalContext* globalCtx); +void BossFd_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BossFd_SetupFly(BossFd* this, GlobalContext* globalCtx); +void BossFd_Fly(BossFd* this, GlobalContext* globalCtx); +void BossFd_Wait(BossFd* this, GlobalContext* globalCtx); +void BossFd_UpdateEffects(BossFd* this, GlobalContext* globalCtx); +void BossFd_DrawBody(GlobalContext* globalCtx, BossFd* this); + +const ActorInit Boss_Fd_InitVars = { + ACTOR_BOSS_FD, + ACTORCAT_BOSS, + FLAGS, + OBJECT_FD, + sizeof(BossFd), + (ActorFunc)BossFd_Init, + (ActorFunc)BossFd_Destroy, + (ActorFunc)BossFd_Update, + (ActorFunc)BossFd_Draw, + NULL, +}; + +#include "z_boss_fd_colchk.c" + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x21, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void BossFd_SpawnEmber(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 150; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_EMBER; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->alpha = 255; + effect->timer1 = (s16)Rand_ZeroFloat(10.0f); + break; + } + } +} + +void BossFd_SpawnDebris(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 150; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_DEBRIS; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->vFdFxRotX = Rand_ZeroFloat(100.0f); + effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + break; + } + } +} + +void BossFd_SpawnDust(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 150; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_DUST; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->timer2 = 0; + effect->scale = scale / 400.0f; + break; + } + } +} + +void BossFd_SpawnFireBreath(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, f32 scale, + s16 alpha, s16 kbAngle) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_FIRE_BREATH; + effect->timer1 = 0; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->pos.x -= effect->velocity.x; + effect->pos.y -= effect->velocity.y; + effect->pos.z -= effect->velocity.z; + effect->vFdFxScaleMod = 0.0f; + effect->alpha = alpha; + effect->vFdFxYStop = Rand_ZeroFloat(10.0f); + effect->timer2 = 0; + effect->scale = scale / 400.0f; + effect->kbAngle = kbAngle; + break; + } + } +} + +void BossFd_SetCameraSpeed(BossFd* this, f32 speedMod) { + this->camData.eyeVel.x = fabsf(this->camData.eye.x - this->camData.nextEye.x) * speedMod; + this->camData.eyeVel.y = fabsf(this->camData.eye.y - this->camData.nextEye.y) * speedMod; + this->camData.eyeVel.z = fabsf(this->camData.eye.z - this->camData.nextEye.z) * speedMod; + this->camData.atVel.x = fabsf(this->camData.at.x - this->camData.nextAt.x) * speedMod; + this->camData.atVel.y = fabsf(this->camData.at.y - this->camData.nextAt.y) * speedMod; + this->camData.atVel.z = fabsf(this->camData.at.z - this->camData.nextAt.z) * speedMod; +} + +void BossFd_UpdateCamera(BossFd* this, GlobalContext* globalCtx) { + if (this->introCamera != SUBCAM_FREE) { + Math_ApproachF(&this->camData.eye.x, this->camData.nextEye.x, this->camData.eyeMaxVel.x, + this->camData.eyeVel.x * this->camData.speedMod); + Math_ApproachF(&this->camData.eye.y, this->camData.nextEye.y, this->camData.eyeMaxVel.y, + this->camData.eyeVel.y * this->camData.speedMod); + Math_ApproachF(&this->camData.eye.z, this->camData.nextEye.z, this->camData.eyeMaxVel.z, + this->camData.eyeVel.z * this->camData.speedMod); + Math_ApproachF(&this->camData.at.x, this->camData.nextAt.x, this->camData.atMaxVel.x, + this->camData.atVel.x * this->camData.speedMod); + Math_ApproachF(&this->camData.at.y, this->camData.nextAt.y, this->camData.atMaxVel.y, + this->camData.atVel.y * this->camData.speedMod); + Math_ApproachF(&this->camData.at.z, this->camData.nextAt.z, this->camData.atMaxVel.z, + this->camData.atVel.z * this->camData.speedMod); + Math_ApproachF(&this->camData.speedMod, 1.0f, 1.0f, this->camData.accel); + this->camData.at.y += this->camData.yMod; + Gameplay_CameraSetAtEye(globalCtx, this->introCamera, &this->camData.at, &this->camData.eye); + Math_ApproachZeroF(&this->camData.yMod, 1.0f, 0.1f); + } +} + +void BossFd_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossFd* this = (BossFd*)thisx; + s16 i; + + Flags_SetSwitch(globalCtx, 0x14); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BG_VB_SIMA, 680.0f, -100.0f, 0.0f, 0, 0, 0, + 100); + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + Actor_SetScale(&this->actor, 0.05f); + SkelAnime_Init(globalCtx, &this->skelAnimeHead, &gVolvagiaHeadSkel, &gVolvagiaHeadEmergeAnim, NULL, NULL, 0); + SkelAnime_Init(globalCtx, &this->skelAnimeRightArm, &gVolvagiaRightArmSkel, &gVolvagiaRightArmEmergeAnim, NULL, + NULL, 0); + SkelAnime_Init(globalCtx, &this->skelAnimeLeftArm, &gVolvagiaLeftArmSkel, &gVolvagiaLeftArmEmergeAnim, NULL, NULL, + 0); + this->introState = BFD_CS_WAIT; + if (this->introState == BFD_CS_NONE) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_FIRE_BOSS); + } + + this->actor.world.pos.x = this->actor.world.pos.z = 0.0f; + this->actor.world.pos.y = -200.0f; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->elements); + + for (i = 0; i < 100; i++) { + this->bodySegsPos[i].x = this->actor.world.pos.x; + this->bodySegsPos[i].y = this->actor.world.pos.y; + this->bodySegsPos[i].z = this->actor.world.pos.z; + if (i < 30) { + this->centerMane.pos[i].x = this->actor.world.pos.x; + this->centerMane.pos[i].y = this->actor.world.pos.y; + this->centerMane.pos[i].z = this->actor.world.pos.z; + } + } + + this->actor.colChkInfo.health = 24; + this->skinSegments = 18; + if (this->introState == BFD_CS_NONE) { + this->actionFunc = BossFd_Wait; + } else { + BossFd_SetupFly(this, globalCtx); + } + + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + Actor_Kill(&this->actor); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, 0, 0, 0, + WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, 0.0f, 100.0f, 200.0f, 0, 0, 0, 0); + } else { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_FD2, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, this->introState); + } +} + +void BossFd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossFd* this = (BossFd*)thisx; + + SkelAnime_Free(&this->skelAnimeHead, globalCtx); + SkelAnime_Free(&this->skelAnimeRightArm, globalCtx); + SkelAnime_Free(&this->skelAnimeLeftArm, globalCtx); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +s32 BossFd_IsFacingLink(BossFd* this) { + return ABS((s16)(this->actor.yawTowardsPlayer - this->actor.world.rot.y)) < 0x2000; +} + +void BossFd_SetupFly(BossFd* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnimeHead, &gVolvagiaHeadEmergeAnim); + Animation_PlayOnce(&this->skelAnimeRightArm, &gVolvagiaRightArmEmergeAnim); + Animation_PlayOnce(&this->skelAnimeLeftArm, &gVolvagiaLeftArmEmergeAnim); + this->actionFunc = BossFd_Fly; + this->fwork[BFD_TURN_RATE_MAX] = 1000.0f; +} + +static Vec3f sHoleLocations[] = { + { 0.0f, 90.0f, -243.0f }, { 0.0f, 90.0f, 0.0f }, { 0.0f, 90.0f, 243.0f }, + { -243.0f, 90.0f, -243.0f }, { -243.0f, 90.0f, 0.0f }, { -243.0f, 90.0f, 243.0f }, + { 243.0f, 90.0f, -243.0f }, { 243.0f, 90.0f, 0.0f }, { 243.0f, 90.0f, 243.0f }, +}; + +static Vec3f sCeilingTargets[] = { + { 0.0f, 900.0f, -243.0f }, { 243.0, 900.0f, -100.0f }, { 243.0f, 900.0f, 100.0f }, + { 0.0f, 900.0f, 243.0f }, { -243.0f, 900.0f, 100.0f }, { -243.0, 900.0f, -100.0f }, +}; + +void BossFd_Fly(BossFd* this, GlobalContext* globalCtx) { + u8 sp1CF = false; + u8 temp_rand; + s16 i1; + s16 i2; + s16 i3; + f32 dx; + f32 dy; + f32 dz; + Player* player = GET_PLAYER(globalCtx); + f32 angleToTarget; + f32 pitchToTarget; + Vec3f* holePosition1; + f32 temp_y; + f32 temp_x; + f32 temp_z; + f32 temp; + + SkelAnime_Update(&this->skelAnimeHead); + SkelAnime_Update(&this->skelAnimeRightArm); + SkelAnime_Update(&this->skelAnimeLeftArm); + dx = this->targetPosition.x - this->actor.world.pos.x; + dy = this->targetPosition.y - this->actor.world.pos.y; + dz = this->targetPosition.z - this->actor.world.pos.z; + dx += Math_SinS((2096.0f + this->fwork[BFD_FLY_WOBBLE_RATE]) * this->work[BFD_MOVE_TIMER]) * + this->fwork[BFD_FLY_WOBBLE_AMP]; + dy += Math_SinS((1096.0f + this->fwork[BFD_FLY_WOBBLE_RATE]) * this->work[BFD_MOVE_TIMER]) * + this->fwork[BFD_FLY_WOBBLE_AMP]; + dz += Math_SinS((1796.0f + this->fwork[BFD_FLY_WOBBLE_RATE]) * this->work[BFD_MOVE_TIMER]) * + this->fwork[BFD_FLY_WOBBLE_AMP]; + angleToTarget = (s16)(Math_FAtan2F(dx, dz) * (0x8000 / M_PI)); + pitchToTarget = (s16)(Math_FAtan2F(dy, sqrtf(SQ(dx) + SQ(dz))) * (0x8000 / M_PI)); + + osSyncPrintf("MODE %d\n", this->work[BFD_ACTION_STATE]); + + Math_ApproachF(&this->fwork[BFD_BODY_PULSE], 0.1f, 1.0f, 0.02); + + // Boss Intro Cutscene + + if (this->introState != BFD_CS_NONE) { + Player* player2 = GET_PLAYER(globalCtx); + Camera* mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + switch (this->introState) { + case BFD_CS_WAIT: + this->fogMode = 3; + this->targetPosition.x = 0.0f; + this->targetPosition.y = -110.0f; + this->targetPosition.z = 0.0; + this->fwork[BFD_TURN_RATE_MAX] = 10000.0f; + this->work[BFD_ACTION_STATE] = BOSSFD_WAIT_INTRO; + if ((fabsf(player2->actor.world.pos.z) < 80.0f) && + (fabsf(player2->actor.world.pos.x - 340.0f) < 60.0f)) { + + this->introState = BFD_CS_START; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->introCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->introCamera, CAM_STAT_ACTIVE); + player2->actor.world.pos.x = 380.0f; + player2->actor.world.pos.y = 100.0f; + player2->actor.world.pos.z = 0.0f; + player2->actor.shape.rot.y = player2->actor.world.rot.y = -0x4000; + player2->actor.speedXZ = 0.0f; + this->camData.eye.x = player2->actor.world.pos.x - 70.0f; + this->camData.eye.y = player2->actor.world.pos.y + 40.0f; + this->camData.eye.z = player2->actor.world.pos.z + 70.0f; + this->camData.at.x = player2->actor.world.pos.x; + this->camData.at.y = player2->actor.world.pos.y + 30.0f; + this->camData.at.z = player2->actor.world.pos.z; + this->camData.nextEye.x = player2->actor.world.pos.x - 50.0f + 18.0f; + this->camData.nextEye.y = player2->actor.world.pos.y + 40; + this->camData.nextEye.z = player2->actor.world.pos.z + 50.0f - 18.0f; + this->camData.nextAt.x = player2->actor.world.pos.x; + this->camData.nextAt.y = player2->actor.world.pos.y + 50.0f; + this->camData.nextAt.z = player2->actor.world.pos.z; + BossFd_SetCameraSpeed(this, 1.0f); + this->camData.atMaxVel.x = this->camData.atMaxVel.y = this->camData.atMaxVel.z = 0.05f; + this->camData.eyeMaxVel.x = this->camData.eyeMaxVel.y = this->camData.eyeMaxVel.z = 0.05f; + this->timers[0] = 0; + this->camData.speedMod = 0.0f; + this->camData.accel = 0.0f; + if (gSaveContext.eventChkInf[7] & 8) { + this->introState = BFD_CS_EMERGE; + this->camData.nextEye.x = player2->actor.world.pos.x + 100.0f + 300.0f - 600.0f; + this->camData.nextEye.y = player2->actor.world.pos.y + 100.0f - 50.0f; + this->camData.nextEye.z = player2->actor.world.pos.z + 200.0f - 150.0f; + this->camData.nextAt.x = 0.0f; + this->camData.nextAt.y = 120.0f; + this->camData.nextAt.z = 0.0f; + BossFd_SetCameraSpeed(this, 0.5f); + this->camData.eyeMaxVel.x = this->camData.eyeMaxVel.y = this->camData.eyeMaxVel.z = 0.1f; + this->camData.atMaxVel.x = this->camData.atMaxVel.y = this->camData.atMaxVel.z = 0.1f; + this->camData.accel = 0.005f; + this->timers[0] = 0; + this->holeIndex = 1; + this->targetPosition.x = sHoleLocations[this->holeIndex].x; + this->targetPosition.y = sHoleLocations[this->holeIndex].y - 200.0f; + this->targetPosition.z = sHoleLocations[this->holeIndex].z; + this->timers[0] = 50; + this->work[BFD_ACTION_STATE] = BOSSFD_EMERGE; + this->actor.world.rot.x = 0x4000; + this->work[BFD_MOVE_TIMER] = 0; + this->timers[3] = 250; + this->timers[2] = 470; + this->fwork[BFD_FLY_SPEED] = 5.0f; + } + } + break; + case BFD_CS_START: + if (this->timers[0] == 0) { + this->camData.accel = 0.0010000002f; + this->timers[0] = 100; + this->introState = BFD_CS_LOOK_LINK; + } + case BFD_CS_LOOK_LINK: + player2->actor.world.pos.x = 380.0f; + player2->actor.world.pos.y = 100.0f; + player2->actor.world.pos.z = 0.0f; + player2->actor.speedXZ = 0.0f; + player2->actor.shape.rot.y = player2->actor.world.rot.y = -0x4000; + if (this->timers[0] == 50) { + this->fogMode = 1; + } + if (this->timers[0] < 50) { + Audio_PlaySoundGeneral(NA_SE_EN_DODO_K_ROLL - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + this->camData.yMod = Math_CosS(this->work[BFD_MOVE_TIMER] * 0x8000) * this->camData.shake; + Math_ApproachF(&this->camData.shake, 2.0f, 1.0f, 0.8 * 0.01f); + } + if (this->timers[0] == 40) { + func_8002DF54(globalCtx, &this->actor, 0x13); + } + if (this->timers[0] == 0) { + this->introState = BFD_CS_LOOK_GROUND; + this->camData.nextAt.y = player2->actor.world.pos.y + 10.0f; + this->camData.atMaxVel.y = 0.2f; + this->camData.speedMod = 0.0f; + this->camData.accel = 0.02f; + this->timers[0] = 70; + this->work[BFD_MOVE_TIMER] = 0; + } + break; + case BFD_CS_LOOK_GROUND: + this->camData.yMod = Math_CosS(this->work[BFD_MOVE_TIMER] * 0x8000) * this->camData.shake; + Math_ApproachF(&this->camData.shake, 2.0f, 1.0f, 0.8 * 0.01f); + Audio_PlaySoundGeneral(NA_SE_EN_DODO_K_ROLL - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + if (this->timers[0] == 0) { + this->introState = BFD_CS_COLLAPSE; + this->camData.nextEye.x = player2->actor.world.pos.x + 100.0f + 300.0f; + this->camData.nextEye.y = player2->actor.world.pos.y + 100.0f; + this->camData.nextEye.z = player2->actor.world.pos.z + 200.0f; + this->camData.nextAt.x = player2->actor.world.pos.x; + this->camData.nextAt.y = player2->actor.world.pos.y - 150.0f; + this->camData.nextAt.z = player2->actor.world.pos.z - 50.0f; + BossFd_SetCameraSpeed(this, 0.1f); + this->timers[0] = 170; + this->camData.speedMod = 0.0f; + this->camData.accel = 0.0f; + func_8002DF54(globalCtx, &this->actor, 0x14); + } + break; + case BFD_CS_COLLAPSE: + this->camData.accel = 0.005f; + this->camData.yMod = Math_CosS(this->work[BFD_MOVE_TIMER] * 0x8000) * this->camData.shake; + Math_ApproachF(&this->camData.shake, 2.0f, 1.0f, 0.8 * 0.01f); + Audio_PlaySoundGeneral(NA_SE_EN_DODO_K_ROLL - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + if (this->timers[0] == 100) { + this->platformSignal = VBSIMA_COLLAPSE; + } + if (this->timers[0] == 0) { + this->introState = BFD_CS_EMERGE; + this->camData.speedMod = 0.0f; + this->camData.nextEye.x = player2->actor.world.pos.x + 100.0f + 300.0f - 600.0f; + this->camData.nextEye.y = player2->actor.world.pos.y + 100.0f - 50.0f; + this->camData.nextEye.z = player2->actor.world.pos.z + 200.0f - 150.0f; + this->camData.nextAt.x = 0.0f; + this->camData.nextAt.y = 120.0f; + this->camData.nextAt.z = 0.0f; + BossFd_SetCameraSpeed(this, 0.5f); + this->camData.atMaxVel.x = this->camData.atMaxVel.y = this->camData.atMaxVel.z = 0.1f; + this->camData.eyeMaxVel.x = this->camData.eyeMaxVel.y = this->camData.eyeMaxVel.z = 0.1f; + this->camData.accel = 0.005f; + this->timers[0] = 0; + this->holeIndex = 1; + this->targetPosition.x = sHoleLocations[this->holeIndex].x; + this->targetPosition.y = sHoleLocations[this->holeIndex].y - 200.0f; + this->targetPosition.z = sHoleLocations[this->holeIndex].z; + this->timers[0] = 50; + this->work[BFD_ACTION_STATE] = BOSSFD_EMERGE; + this->actor.world.rot.x = 0x4000; + this->work[BFD_MOVE_TIMER] = 0; + this->timers[3] = 250; + this->timers[2] = 470; + this->fwork[BFD_FLY_SPEED] = 5.0f; + } + break; + case BFD_CS_EMERGE: + osSyncPrintf("WAY_SPD X = %f\n", this->camData.atVel.x); + osSyncPrintf("WAY_SPD Y = %f\n", this->camData.atVel.y); + osSyncPrintf("WAY_SPD Z = %f\n", this->camData.atVel.z); + if ((this->timers[3] > 190) && !(gSaveContext.eventChkInf[7] & 8)) { + Audio_PlaySoundGeneral(NA_SE_EN_DODO_K_ROLL - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + if (this->timers[3] == 190) { + this->camData.atMaxVel.x = this->camData.atMaxVel.y = this->camData.atMaxVel.z = 0.05f; + this->platformSignal = VBSIMA_KILL; + func_8002DF54(globalCtx, &this->actor, 1); + } + if (this->actor.world.pos.y > 120.0f) { + this->camData.nextAt = this->actor.world.pos; + this->camData.atVel.x = 190.0f; + this->camData.atVel.y = 85.56f; + this->camData.atVel.z = 25.0f; + } else { + // the following `temp` stuff is probably fake but is required to match + // it's optimized to 1.0f because sp1CF is false at this point, but the 0.1f ends up in rodata + temp = 0.1f; + if (!sp1CF) { + temp = 1.0f; + } + Math_ApproachF(&this->camData.shake, 2.0f, temp, 0.1 * 0.08f); + this->camData.yMod = Math_CosS(this->work[BFD_MOVE_TIMER] * 0x8000) * this->camData.shake; + } + if (this->timers[3] == 160) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_FIRE_BOSS); + } + if ((this->timers[3] == 130) && !(gSaveContext.eventChkInf[7] & 8)) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gVolvagiaBossTitleCardTex), 0xA0, 0xB4, 0x80, 0x28); + } + if (this->timers[3] <= 100) { + this->camData.eyeVel.x = this->camData.eyeVel.y = this->camData.eyeVel.z = 2.0f; + this->camData.nextEye.x = player2->actor.world.pos.x + 50.0f; + this->camData.nextEye.y = player2->actor.world.pos.y + 50.0f; + this->camData.nextEye.z = player2->actor.world.pos.z + 50.0f; + } + if (this->work[BFD_ACTION_STATE] == BOSSFD_FLY_HOLE) { + switch (this->introFlyState) { + case INTRO_FLY_EMERGE: + this->timers[5] = 100; + this->introFlyState = INTRO_FLY_HOLE; + case INTRO_FLY_HOLE: + if (this->timers[5] == 0) { + this->introFlyState = INTRO_FLY_CAMERA; + this->timers[5] = 75; + } + break; + case INTRO_FLY_CAMERA: + this->targetPosition = this->camData.eye; + if (this->timers[5] == 0) { + this->timers[0] = 0; + this->holeIndex = 7; + this->targetPosition.x = sHoleLocations[this->holeIndex].x; + this->targetPosition.y = sHoleLocations[this->holeIndex].y + 200.0f + 50.0f; + this->targetPosition.z = sHoleLocations[this->holeIndex].z; + this->introFlyState = INTRO_FLY_RETRAT; + } + if (this->timers[5] == 30) { + this->work[BFD_ROAR_TIMER] = 40; + this->fireBreathTimer = 20; + } + case INTRO_FLY_RETRAT: + break; + } + } + osSyncPrintf("this->timer[2] = %d\n", this->timers[2]); + osSyncPrintf("this->timer[5] = %d\n", this->timers[5]); + if (this->timers[2] == 0) { + mainCam->eye = this->camData.eye; + mainCam->eyeNext = this->camData.eye; + mainCam->at = this->camData.at; + func_800C08AC(globalCtx, this->introCamera, 0); + this->introState = this->introFlyState = this->introCamera = BFD_CS_NONE; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->actionFunc = BossFd_Wait; + this->handoffSignal = FD2_SIGNAL_GROUND; + gSaveContext.eventChkInf[7] |= 8; + } + break; + } + BossFd_UpdateCamera(this, globalCtx); + } else { + this->fwork[BFD_FLY_SPEED] = 5.0f; + } + + // Attacks and Death Cutscene + + switch (this->work[BFD_ACTION_STATE]) { + case BOSSFD_FLY_MAIN: + sp1CF = true; + if (this->timers[0] == 0) { + if (this->actor.colChkInfo.health == 0) { + this->work[BFD_ACTION_STATE] = BOSSFD_DEATH_START; + this->timers[0] = 0; + this->timers[1] = 100; + } else { + if (this->introState != BFD_CS_NONE) { + this->holeIndex = 6; + } else { + do { + temp_rand = Rand_ZeroFloat(8.9f); + } while (temp_rand == this->holeIndex); + this->holeIndex = temp_rand; + } + this->targetPosition.x = sHoleLocations[this->holeIndex].x; + this->targetPosition.y = sHoleLocations[this->holeIndex].y + 200.0f + 50.0f; + this->targetPosition.z = sHoleLocations[this->holeIndex].z; + this->fwork[BFD_TURN_RATE] = 0.0f; + this->fwork[BFD_TURN_RATE_MAX] = 1000.0f; + if (this->introState != BFD_CS_NONE) { + this->timers[0] = 10050; + } else { + this->timers[0] = 20; + } + this->fwork[BFD_FLY_WOBBLE_AMP] = 100.0f; + this->work[BFD_ACTION_STATE] = BOSSFD_FLY_HOLE; + + if (this->work[BFD_START_ATTACK]) { + this->work[BFD_START_ATTACK] = false; + this->work[BFD_FLY_COUNT]++; + if (this->work[BFD_FLY_COUNT] & 1) { + this->work[BFD_ACTION_STATE] = BOSSFD_FLY_CHASE; + this->timers[0] = 300; + this->fwork[BFD_TURN_RATE_MAX] = 900.0f; + this->fwork[BFD_TARGET_Y_OFFSET] = 300.0f; + this->work[BFD_UNK_234] = this->work[BFD_UNK_236] = 0; + } else { + this->work[BFD_ACTION_STATE] = BOSSFD_FLY_CEILING; + } + } + } + } + break; + case BOSSFD_FLY_HOLE: + if ((this->timers[0] == 0) && (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 100.0f)) { + this->work[BFD_ACTION_STATE] = BOSSFD_BURROW; + this->targetPosition.y = sHoleLocations[this->holeIndex].y - 70.0f; + this->fwork[BFD_TURN_RATE_MAX] = 10000.0f; + this->fwork[BFD_FLY_WOBBLE_AMP] = 0.0f; + this->timers[0] = 150; + this->work[BFD_ROAR_TIMER] = 40; + this->holePosition.x = this->targetPosition.x; + this->holePosition.z = this->targetPosition.z; + } + break; + case BOSSFD_BURROW: + sp1CF = true; + if (this->timers[0] == 0) { + this->actionFunc = BossFd_Wait; + this->handoffSignal = FD2_SIGNAL_GROUND; + } + break; + case BOSSFD_EMERGE: + if ((this->timers[0] == 0) && (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 100.0f)) { + this->actor.world.pos = this->targetPosition; + this->work[BFD_ACTION_STATE] = BOSSFD_FLY_MAIN; + this->actor.world.rot.x = 0x4000; + this->targetPosition.y = sHoleLocations[this->holeIndex].y + 200.0f; + this->timers[4] = 80; + this->fwork[BFD_TURN_RATE_MAX] = 1000.0f; + this->fwork[BFD_FLY_WOBBLE_AMP] = 0.0f; + this->holePosition.x = this->targetPosition.x; + this->holePosition.z = this->targetPosition.z; + + func_80033E1C(globalCtx, 1, 0x50, 0x5000); + if (this->introState != BFD_CS_NONE) { + this->timers[0] = 50; + } else { + this->timers[0] = 50; + } + } + break; + case BOSSFD_FLY_CEILING: + this->fwork[BFD_FLY_SPEED] = 8; + this->targetPosition.x = 0.0f; + this->targetPosition.y = 700.0f; + this->targetPosition.z = -300.0f; + this->fwork[BFD_FLY_WOBBLE_AMP] = 200.0f; + this->fwork[BFD_TURN_RATE_MAX] = 3000.0f; + if (this->actor.world.pos.y > 700.0f) { + this->work[BFD_ACTION_STATE] = BOSSFD_DROP_ROCKS; + this->timers[0] = 25; + this->timers[2] = 150; + this->work[BFD_CEILING_TARGET] = 0; + } + break; + case BOSSFD_DROP_ROCKS: + this->fwork[BFD_FLY_SPEED] = 8; + this->fwork[BFD_FLY_WOBBLE_AMP] = 200.0f; + this->fwork[BFD_TURN_RATE_MAX] = 10000.0f; + this->targetPosition.x = sCeilingTargets[this->work[BFD_CEILING_TARGET]].x; + this->targetPosition.y = sCeilingTargets[this->work[BFD_CEILING_TARGET]].y + 900.0f; + this->targetPosition.z = sCeilingTargets[this->work[BFD_CEILING_TARGET]].z; + if (this->timers[0] == 0) { + this->timers[0] = 25; + this->work[BFD_CEILING_TARGET]++; + if (this->work[BFD_CEILING_TARGET] >= 6) { + this->work[BFD_CEILING_TARGET] = 0; + } + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 2); + if (this->timers[1] == 0) { + osSyncPrintf("BGCHECKKKKKKKKKKKKKKKKKKKKKKK\n"); + if (this->actor.bgCheckFlags & 0x10) { + this->fwork[BFD_CEILING_BOUNCE] = -18384.0f; + this->timers[1] = 10; + Audio_PlaySoundGeneral(NA_SE_EV_EXPLOSION, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_80033E1C(globalCtx, 3, 0xA, 0x7530); + this->work[BFD_ROCK_TIMER] = 300; + } + } else { + pitchToTarget = this->fwork[BFD_CEILING_BOUNCE]; + Math_ApproachZeroF(&this->fwork[BFD_CEILING_BOUNCE], 1.0f, 1000.0f); + } + if (this->timers[2] == 0) { + this->work[BFD_ACTION_STATE] = BOSSFD_FLY_MAIN; + this->timers[0] = 0; + this->work[BFD_START_ATTACK] = false; + } + break; + case BOSSFD_FLY_CHASE: + this->actor.flags |= ACTOR_FLAG_24; + temp_y = Math_SinS(this->work[BFD_MOVE_TIMER] * 2396.0f) * 30.0f + this->fwork[BFD_TARGET_Y_OFFSET]; + this->targetPosition.x = player->actor.world.pos.x; + this->targetPosition.y = player->actor.world.pos.y + temp_y + 30.0f; + this->targetPosition.z = player->actor.world.pos.z; + this->fwork[BFD_FLY_WOBBLE_AMP] = 0.0f; + if (((this->timers[0] % 64) == 0) && (this->timers[0] < 450)) { + this->work[BFD_ROAR_TIMER] = 40; + if (BossFd_IsFacingLink(this)) { + this->fireBreathTimer = 20; + } + } + if ((this->work[BFD_DAMAGE_FLASH_TIMER] != 0) || (this->timers[0] == 0) || + (player->actor.world.pos.y < 70.0f)) { + this->work[BFD_ACTION_STATE] = BOSSFD_FLY_MAIN; + this->timers[0] = 0; + this->work[BFD_START_ATTACK] = false; + } else { + Math_ApproachF(&this->fwork[BFD_TARGET_Y_OFFSET], 50.0, 1.0f, 2.0f); + } + break; + case BOSSFD_DEATH_START: + if (sqrtf(SQ(dx) + SQ(dz)) < 50.0f) { + this->timers[0] = 0; + } + if (this->timers[0] == 0) { + this->timers[0] = (s16)Rand_ZeroFloat(10.0f) + 10; + do { + this->targetPosition.x = Rand_CenteredFloat(200.0f); + this->targetPosition.y = 390.0f; + this->targetPosition.z = Rand_CenteredFloat(200.0f); + temp_x = this->targetPosition.x - this->actor.world.pos.x; + temp_z = this->targetPosition.z - this->actor.world.pos.z; + } while (!(sqrtf(SQ(temp_x) + SQ(temp_z)) > 100.0f)); + } + this->fwork[BFD_FLY_WOBBLE_AMP] = 200.0f; + this->fwork[BFD_FLY_WOBBLE_RATE] = 1000.0f; + this->fwork[BFD_TURN_RATE_MAX] = 10000.0f; + Math_ApproachF(&this->fwork[BFD_BODY_PULSE], 0.3f, 1.0f, 0.05f); + if (this->timers[1] == 0) { + this->work[BFD_ACTION_STATE] = BOSSFD_SKIN_BURN; + this->timers[0] = 30; + } + break; + case BOSSFD_SKIN_BURN: + this->targetPosition.x = 0.0f; + this->targetPosition.y = 390.0f; + this->targetPosition.z = 0.0f; + this->fwork[BFD_FLY_WOBBLE_AMP] = 200.0f; + this->fwork[BFD_FLY_WOBBLE_RATE] = 1000.0f; + this->fwork[BFD_TURN_RATE_MAX] = 2000.0f; + Math_ApproachF(&this->fwork[BFD_BODY_PULSE], 0.3f, 1.0f, 0.05f); + if ((this->timers[0] == 0) && ((this->work[BFD_MOVE_TIMER] % 4) == 0)) { + if (this->skinSegments != 0) { + this->skinSegments--; + if (this->skinSegments == 0) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + } + } else { + this->work[BFD_ACTION_STATE] = BOSSFD_BONES_FALL; + this->timers[0] = 30; + } + } + if ((this->work[BFD_MOVE_TIMER] % 32) == 0) { + this->work[BFD_ROAR_TIMER] = 40; + } + + if (this->skinSegments != 0) { + Vec3f sp188; + Vec3f sp17C = { 0.0f, 0.0f, 0.0f }; + Vec3f sp170; + Vec3f sp164 = { 0.0f, 0.03f, 0.0f }; + Vec3f sp158; + f32 pad154; + s16 temp_rand2; + s16 sp150; + + if (this->fogMode == 0) { + globalCtx->envCtx.unk_D8 = 0; + } + this->fogMode = 0xA; + + sp150 = 1; + if (this->work[BFD_MOVE_TIMER] & 0x1C) { + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_BURN - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + for (i1 = 0; i1 < sp150; i1++) { + if (sp150) { // Needed for matching + temp_rand2 = Rand_ZeroFloat(99.9f); + + sp188.x = this->bodySegsPos[temp_rand2].x; + sp188.y = this->bodySegsPos[temp_rand2].y - 10.0f; + sp188.z = this->bodySegsPos[temp_rand2].z; + + sp164.y = 0.03f; + + EffectSsKFire_Spawn(globalCtx, &sp188, &sp17C, &sp164, (s16)Rand_ZeroFloat(20.0f) + 40, 0x64); + + for (i2 = 0; i2 < 15; i2++) { + sp170.x = Rand_CenteredFloat(20.0f); + sp170.y = Rand_CenteredFloat(20.0f); + sp170.z = Rand_CenteredFloat(20.0f); + + sp158.y = 0.4f; + sp158.x = Rand_CenteredFloat(0.5f); + sp158.z = Rand_CenteredFloat(0.5f); + + BossFd_SpawnEmber(this->effects, &sp188, &sp170, &sp158, (s16)Rand_ZeroFloat(3.0f) + 8); + } + } + } + } + break; + case BOSSFD_BONES_FALL: + this->work[BFD_STOP_FLAG] = true; + this->fogMode = 3; + if (this->timers[0] < 18) { + this->bodyFallApart[this->timers[0]] = 1; + } + if (this->timers[0] == 0) { + this->work[BFD_ACTION_STATE] = BOSSFD_SKULL_PAUSE; + this->timers[0] = 15; + this->work[BFD_CEILING_TARGET] = 0; + player->actor.world.pos.y = 90.0f; + player->actor.world.pos.x = 40.0f; + player->actor.world.pos.z = 150.0f; + } + break; + case BOSSFD_SKULL_PAUSE: + if (this->timers[0] == 0) { + this->work[BFD_ACTION_STATE] = BOSSFD_SKULL_FALL; + this->timers[0] = 20; + this->work[BFD_STOP_FLAG] = false; + } + break; + case BOSSFD_SKULL_FALL: + this->fwork[BFD_TURN_RATE] = this->fwork[BFD_TURN_RATE_MAX] = this->actor.speedXZ = + this->fwork[BFD_FLY_SPEED] = 0; + + if (this->timers[0] == 1) { + this->actor.world.pos.x = 0; + this->actor.world.pos.y = 900.0f; + this->actor.world.pos.z = 150.0f; + this->actor.world.rot.x = this->actor.world.rot.y = 0; + this->actor.shape.rot.z = 0x1200; + this->actor.velocity.x = 0; + this->actor.velocity.z = 0; + } + if (this->timers[0] == 0) { + if (this->actor.world.pos.y <= 110.0f) { + this->actor.world.pos.y = 110.0f; + this->actor.velocity.y = 0; + if (this->work[BFD_CEILING_TARGET] == 0) { + this->work[BFD_CEILING_TARGET]++; + this->timers[1] = 60; + this->work[BFD_CAM_SHAKE_TIMER] = 20; + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_LAND2, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + func_8002DF54(globalCtx, &this->actor, 5); + for (i1 = 0; i1 < 15; i1++) { + Vec3f sp144 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp138 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp12C; + + sp144.x = Rand_CenteredFloat(8.0f); + sp144.y = Rand_ZeroFloat(1.0f); + sp144.z = Rand_CenteredFloat(8.0f); + + sp138.y = 0.3f; + + sp12C.x = Rand_CenteredFloat(10.0f) + this->actor.world.pos.x; + sp12C.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; + sp12C.z = Rand_CenteredFloat(10.0f) + this->actor.world.pos.z; + BossFd_SpawnDust(this->effects, &sp12C, &sp144, &sp138, Rand_ZeroFloat(100.0f) + 300); + } + } + } else { + this->actor.velocity.y -= 1.0f; + } + } else { + this->actor.velocity.y = 0; + } + if (this->timers[1] == 1) { + this->work[BFD_ACTION_STATE] = BOSSFD_SKULL_BURN; + this->timers[0] = 70; + } + break; + case BOSSFD_SKULL_BURN: + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = 110.0f; + this->fwork[BFD_TURN_RATE] = this->fwork[BFD_TURN_RATE_MAX] = this->actor.speedXZ = + this->fwork[BFD_FLY_SPEED] = 0.0f; + + if ((50 > this->timers[0]) && (this->timers[0] > 0)) { + Vec3f sp120; + Vec3f sp114 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp108 = { 0.0f, 0.03f, 0.0f }; + + Audio_PlaySoundGeneral(NA_SE_EN_GOMA_LAST - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + + sp120.x = Rand_CenteredFloat(40.0f) + this->actor.world.pos.x; + sp120.y = (Rand_CenteredFloat(10.0f) + this->actor.world.pos.y) - 10.0f; + sp120.z = (Rand_CenteredFloat(40.0f) + this->actor.world.pos.z) + 5.0f; + + sp108.y = 0.03f; + + EffectSsKFire_Spawn(globalCtx, &sp120, &sp114, &sp108, (s16)Rand_ZeroFloat(15.0f) + 30, 0); + } + if (this->timers[0] < 20) { + Math_ApproachZeroF(&this->actor.scale.x, 1.0f, 0.0025f); + Actor_SetScale(&this->actor, this->actor.scale.x); + } + if (this->timers[0] == 0) { + this->actionFunc = BossFd_Wait; + this->actor.world.pos.y -= 1000.0f; + } + if (this->timers[0] == 7) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + } + break; + case BOSSFD_WAIT_INTRO: + break; + } + + // Update body segments and mane + + if (!this->work[BFD_STOP_FLAG]) { + s16 i4; + Vec3f spE0[3]; + Vec3f spBC[3]; + f32 phi_f20; + f32 padB4; + f32 padB0; + f32 padAC; + + Math_ApproachS(&this->actor.world.rot.y, angleToTarget, 0xA, this->fwork[BFD_TURN_RATE]); + + if (((this->work[BFD_ACTION_STATE] == BOSSFD_FLY_CHASE) || + (this->work[BFD_ACTION_STATE] == BOSSFD_FLY_UNUSED)) && + (this->actor.world.pos.y < 110.0f) && (pitchToTarget < 0)) { + pitchToTarget = 0; + Math_ApproachF(&this->actor.world.pos.y, 110.0f, 1.0f, 5.0f); + } + + Math_ApproachS(&this->actor.world.rot.x, pitchToTarget, 0xA, this->fwork[BFD_TURN_RATE]); + Math_ApproachF(&this->fwork[BFD_TURN_RATE], this->fwork[BFD_TURN_RATE_MAX], 1.0f, 20000.0f); + Math_ApproachF(&this->actor.speedXZ, this->fwork[BFD_FLY_SPEED], 1.0f, 0.1f); + if (this->work[BFD_ACTION_STATE] < BOSSFD_SKULL_FALL) { + func_8002D908(&this->actor); + } + func_8002D7EC(&this->actor); + + this->work[BFD_LEAD_BODY_SEG]++; + if (this->work[BFD_LEAD_BODY_SEG] >= 100) { + this->work[BFD_LEAD_BODY_SEG] = 0; + } + i4 = this->work[BFD_LEAD_BODY_SEG]; + this->bodySegsPos[i4].x = this->actor.world.pos.x; + this->bodySegsPos[i4].y = this->actor.world.pos.y; + this->bodySegsPos[i4].z = this->actor.world.pos.z; + this->bodySegsRot[i4].x = (this->actor.world.rot.x / (f32)0x8000) * M_PI; + this->bodySegsRot[i4].y = (this->actor.world.rot.y / (f32)0x8000) * M_PI; + this->bodySegsRot[i4].z = (this->actor.world.rot.z / (f32)0x8000) * M_PI; + + this->work[BFD_LEAD_MANE_SEG]++; + if (this->work[BFD_LEAD_MANE_SEG] >= 30) { + this->work[BFD_LEAD_MANE_SEG] = 0; + } + i4 = this->work[BFD_LEAD_MANE_SEG]; + this->centerMane.scale[i4] = (Math_SinS(this->work[BFD_MOVE_TIMER] * 5596.0f) * 0.3f) + 1.0f; + this->rightMane.scale[i4] = (Math_SinS(this->work[BFD_MOVE_TIMER] * 5496.0f) * 0.3f) + 1.0f; + this->leftMane.scale[i4] = (Math_CosS(this->work[BFD_MOVE_TIMER] * 5696.0f) * 0.3f) + 1.0f; + this->centerMane.pos[i4] = this->centerMane.head; + this->fireManeRot[i4].x = (this->actor.world.rot.x / (f32)0x8000) * M_PI; + this->fireManeRot[i4].y = (this->actor.world.rot.y / (f32)0x8000) * M_PI; + this->fireManeRot[i4].z = (this->actor.world.rot.z / (f32)0x8000) * M_PI; + this->rightMane.pos[i4] = this->rightMane.head; + this->leftMane.pos[i4] = this->leftMane.head; + + if ((0x3000 > this->actor.world.rot.x) && (this->actor.world.rot.x > -0x3000)) { + Math_ApproachF(&this->flattenMane, 1.0f, 1.0f, 0.05f); + } else { + Math_ApproachF(&this->flattenMane, 0.5f, 1.0f, 0.05f); + } + + if (this->work[BFD_ACTION_STATE] < BOSSFD_SKULL_FALL) { + if ((this->actor.prevPos.y < 90.0f) && (90.0f <= this->actor.world.pos.y)) { + this->timers[4] = 80; + func_80033E1C(globalCtx, 1, 80, 0x5000); + this->work[BFD_ROAR_TIMER] = 40; + this->work[BFD_MANE_EMBERS_TIMER] = 30; + this->work[BFD_SPLASH_TIMER] = 10; + } + if ((this->actor.prevPos.y > 90.0f) && (90.0f >= this->actor.world.pos.y)) { + this->timers[4] = 80; + func_80033E1C(globalCtx, 1, 80, 0x5000); + this->work[BFD_MANE_EMBERS_TIMER] = 30; + this->work[BFD_SPLASH_TIMER] = 10; + } + } + + if (!sp1CF) { + spE0[0].x = spE0[0].y = Math_SinS(this->work[BFD_MOVE_TIMER] * 1500.0f) * 3000.0f; + spE0[1].x = Math_SinS(this->work[BFD_MOVE_TIMER] * 2000.0f) * 4000.0f; + spE0[1].y = Math_SinS(this->work[BFD_MOVE_TIMER] * 2200.0f) * 4000.0f; + spE0[2].x = Math_SinS(this->work[BFD_MOVE_TIMER] * 1700.0f) * 2000.0f; + spE0[2].y = Math_SinS(this->work[BFD_MOVE_TIMER] * 1900.0f) * 2000.0f; + spBC[0].x = spBC[0].y = Math_SinS(this->work[BFD_MOVE_TIMER] * 1500.0f) * -3000.0f; + spBC[1].x = Math_SinS(this->work[BFD_MOVE_TIMER] * 2200.0f) * -4000.0f; + spBC[1].y = Math_SinS(this->work[BFD_MOVE_TIMER] * 2000.0f) * -4000.0f; + spBC[2].x = Math_SinS(this->work[BFD_MOVE_TIMER] * 1900.0f) * -2000.0f; + spBC[2].y = Math_SinS(this->work[BFD_MOVE_TIMER] * 1700.0f) * -2000.0f; + + for (i3 = 0; i3 < 3; i3++) { + Math_ApproachF(&this->rightArmRot[i3].x, spE0[i3].x, 1.0f, 1000.0f); + Math_ApproachF(&this->rightArmRot[i3].y, spE0[i3].y, 1.0f, 1000.0f); + Math_ApproachF(&this->leftArmRot[i3].x, spBC[i3].x, 1.0f, 1000.0f); + Math_ApproachF(&this->leftArmRot[i3].y, spBC[i3].y, 1.0f, 1000.0f); + } + } else { + for (i2 = 0; i2 < 3; i2++) { + phi_f20 = 0.0f; + Math_ApproachZeroF(&this->rightArmRot[i2].y, 0.1f, 100.0f); + Math_ApproachZeroF(&this->leftArmRot[i2].y, 0.1f, 100.0f); + if (i2 == 0) { + phi_f20 = -3000.0f; + } + Math_ApproachF(&this->rightArmRot[i2].x, phi_f20, 0.1f, 100.0f); + Math_ApproachF(&this->leftArmRot[i2].x, -phi_f20, 0.1f, 100.0f); + } + } + } +} + +void BossFd_Wait(BossFd* this, GlobalContext* globalCtx) { + if (this->handoffSignal == FD2_SIGNAL_FLY) { // Set by BossFd2 + u8 temp_rand; + + this->handoffSignal = FD2_SIGNAL_NONE; + BossFd_SetupFly(this, globalCtx); + do { + temp_rand = Rand_ZeroFloat(8.9f); + } while (temp_rand == this->holeIndex); + this->holeIndex = temp_rand; + if (1) {} // Needed for matching + this->targetPosition.x = sHoleLocations[this->holeIndex].x; + this->targetPosition.y = sHoleLocations[this->holeIndex].y - 200.0f; + this->targetPosition.z = sHoleLocations[this->holeIndex].z; + this->actor.world.pos = this->targetPosition; + + this->timers[0] = 10; + this->work[BFD_ACTION_STATE] = BOSSFD_EMERGE; + this->work[BFD_START_ATTACK] = true; + } + if (this->handoffSignal == FD2_SIGNAL_DEATH) { + this->handoffSignal = FD2_SIGNAL_NONE; + BossFd_SetupFly(this, globalCtx); + this->holeIndex = 1; + this->targetPosition.x = sHoleLocations[1].x; + this->targetPosition.y = sHoleLocations[1].y - 200.0f; + this->targetPosition.z = sHoleLocations[1].z; + this->actor.world.pos = this->targetPosition; + this->timers[0] = 10; + this->work[BFD_ACTION_STATE] = BOSSFD_EMERGE; + } +} + +static Vec3f sFireAudioVec = { 0.0f, 0.0f, 50.0f }; + +void BossFd_Effects(BossFd* this, GlobalContext* globalCtx) { + static Color_RGBA8 colorYellow = { 255, 255, 0, 255 }; + static Color_RGBA8 colorRed = { 255, 10, 0, 255 }; + s16 breathOpacity = 0; + f32 jawAngle; + f32 jawSpeed; + f32 emberRate; + f32 emberSpeed; + s16 eyeStates[] = { EYE_OPEN, EYE_HALF, EYE_CLOSED, EYE_CLOSED, EYE_HALF }; + f32 temp_x; + f32 temp_z; + s16 i; + + if (1) {} // Needed for match + + if (this->fogMode == 0) { + globalCtx->envCtx.unk_BF = 0; + globalCtx->envCtx.unk_D8 = 0.5f + 0.5f * Math_SinS(this->work[BFD_VAR_TIMER] * 0x500); + globalCtx->envCtx.unk_DC = 2; + globalCtx->envCtx.unk_BD = 1; + globalCtx->envCtx.unk_BE = 0; + } else if (this->fogMode == 3) { + globalCtx->envCtx.unk_BF = 0; + globalCtx->envCtx.unk_DC = 2; + globalCtx->envCtx.unk_BD = 2; + globalCtx->envCtx.unk_BE = 0; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f); + } else if (this->fogMode == 2) { + this->fogMode--; + globalCtx->envCtx.unk_BF = 0; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.55f + 0.05f * Math_SinS(this->work[BFD_VAR_TIMER] * 0x3E00), 1.0f, + 0.15f); + globalCtx->envCtx.unk_DC = 2; + globalCtx->envCtx.unk_BD = 3; + globalCtx->envCtx.unk_BE = 0; + } else if (this->fogMode == 10) { + this->fogMode = 1; + globalCtx->envCtx.unk_BF = 0; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.21f + 0.07f * Math_SinS(this->work[BFD_VAR_TIMER] * 0xC00), 1.0f, + 0.05f); + globalCtx->envCtx.unk_DC = 2; + globalCtx->envCtx.unk_BD = 3; + globalCtx->envCtx.unk_BE = 0; + } else if (this->fogMode == 1) { + Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.0f, 1.0f, 0.03f); + if (globalCtx->envCtx.unk_D8 <= 0.01f) { + this->fogMode = 0; + } + } + + if (this->work[BFD_MANE_EMBERS_TIMER] != 0) { + this->work[BFD_MANE_EMBERS_TIMER]--; + emberSpeed = emberRate = 20.0f; + } else { + emberRate = 3.0f; + emberSpeed = 5.0f; + } + Math_ApproachF(&this->fwork[BFD_MANE_EMBER_RATE], emberRate, 1.0f, 0.1f); + Math_ApproachF(&this->fwork[BFD_MANE_EMBER_SPEED], emberSpeed, 1.0f, 0.5f); + + if (((this->work[BFD_VAR_TIMER] % 8) == 0) && (Rand_ZeroOne() < 0.3f)) { + this->work[BFD_BLINK_TIMER] = 4; + } + this->eyeState = eyeStates[this->work[BFD_BLINK_TIMER]]; + + if (this->work[BFD_BLINK_TIMER] != 0) { + this->work[BFD_BLINK_TIMER]--; + } + + if (this->work[BFD_ROAR_TIMER] != 0) { + if (this->work[BFD_ROAR_TIMER] == 37) { + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_ROAR, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + jawAngle = 6000.0f; + jawSpeed = 1300.0f; + } else { + jawAngle = (this->work[BFD_VAR_TIMER] & 0x10) ? 0.0f : 1000.0f; + jawSpeed = 500.0f; + } + Math_ApproachF(&this->jawOpening, jawAngle, 0.3f, jawSpeed); + + if (this->work[BFD_ROAR_TIMER] != 0) { + this->work[BFD_ROAR_TIMER]--; + } + + if (this->timers[4] != 0) { + Vec3f spawnVel1; + Vec3f spawnAccel1; + Vec3f spawnPos1; + s32 pad; + + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_APPEAR - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + if (this->work[BFD_SPLASH_TIMER] != 0) { + this->work[BFD_SPLASH_TIMER]--; + if ((this->actor.colChkInfo.health == 0) || + ((this->introState == BFD_CS_EMERGE) && (this->actor.world.rot.x > 0x3000))) { + if ((u8)this->fogMode == 0) { + globalCtx->envCtx.unk_D8 = 0.0f; + } + this->fogMode = 2; + } + for (i = 0; i < 5; i++) { + spawnVel1.x = Rand_CenteredFloat(20.0f); + spawnVel1.y = Rand_ZeroFloat(5.0f) + 4.0f; + spawnVel1.z = Rand_CenteredFloat(20.0f); + + spawnAccel1.x = spawnAccel1.z = 0.0f; + spawnAccel1.y = -0.3f; + + temp_x = (spawnVel1.x * 20) / 10.0f; + temp_z = (spawnVel1.z * 20) / 10.0f; + spawnPos1.x = temp_x + this->holePosition.x; + spawnPos1.y = 100.0f; + spawnPos1.z = temp_z + this->holePosition.z; + + func_8002836C(globalCtx, &spawnPos1, &spawnVel1, &spawnAccel1, &colorYellow, &colorRed, + (s16)Rand_ZeroFloat(150.0f) + 800, 10, (s16)Rand_ZeroFloat(5.0f) + 17); + } + } else { + for (i = 0; i < 2; i++) { + spawnVel1.x = Rand_CenteredFloat(10.0f); + spawnVel1.y = Rand_ZeroFloat(3.0f) + 3.0f; + spawnVel1.z = Rand_CenteredFloat(10.0f); + + spawnAccel1.x = spawnAccel1.z = 0.0f; + spawnAccel1.y = -0.3f; + temp_x = (spawnVel1.x * 50) / 10.0f; + temp_z = (spawnVel1.z * 50) / 10.0f; + + spawnPos1.x = temp_x + this->holePosition.x; + spawnPos1.y = 100.0f; + spawnPos1.z = temp_z + this->holePosition.z; + + func_8002836C(globalCtx, &spawnPos1, &spawnVel1, &spawnAccel1, &colorYellow, &colorRed, 500, 10, 20); + } + } + + for (i = 0; i < 8; i++) { + spawnVel1.x = Rand_CenteredFloat(20.0f); + spawnVel1.y = Rand_ZeroFloat(10.0f); + spawnVel1.z = Rand_CenteredFloat(20.0f); + + spawnAccel1.y = 0.4f; + spawnAccel1.x = Rand_CenteredFloat(0.5f); + spawnAccel1.z = Rand_CenteredFloat(0.5f); + + spawnPos1.x = Rand_CenteredFloat(60.0) + this->holePosition.x; + spawnPos1.y = Rand_ZeroFloat(40.0f) + 100.0f; + spawnPos1.z = Rand_CenteredFloat(60.0) + this->holePosition.z; + + BossFd_SpawnEmber(this->effects, &spawnPos1, &spawnVel1, &spawnAccel1, (s16)Rand_ZeroFloat(1.5f) + 6); + } + } + + if ((this->fireBreathTimer != 0) && (this->fireBreathTimer < 17)) { + breathOpacity = (this->fireBreathTimer >= 6) ? 255 : this->fireBreathTimer * 50; + } + if (breathOpacity != 0) { + f32 spawnAngleX; + f32 spawnAngleY; + Vec3f spawnSpeed2 = { 0.0f, 0.0f, 0.0f }; + Vec3f spawnVel2; + Vec3f spawnAccel2 = { 0.0f, 0.0f, 0.0f }; + Vec3f spawnPos2; + + this->fogMode = 2; + spawnSpeed2.z = 30.0f; + + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_FIRE - SFX_FLAG, &sFireAudioVec, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + spawnPos2 = this->headPos; + + spawnAngleY = (this->actor.world.rot.y / (f32)0x8000) * M_PI; + spawnAngleX = (((-this->actor.world.rot.x) / (f32)0x8000) * M_PI) + 0.3f; + Matrix_RotateY(spawnAngleY, MTXMODE_NEW); + Matrix_RotateX(spawnAngleX, MTXMODE_APPLY); + Matrix_MultVec3f(&spawnSpeed2, &spawnVel2); + + BossFd_SpawnFireBreath(this->effects, &spawnPos2, &spawnVel2, &spawnAccel2, + 50.0f * Math_SinS(this->work[BFD_VAR_TIMER] * 0x2000) + 300.0f, breathOpacity, + this->actor.world.rot.y); + + spawnPos2.x += spawnVel2.x * 0.5f; + spawnPos2.y += spawnVel2.y * 0.5f; + spawnPos2.z += spawnVel2.z * 0.5f; + + BossFd_SpawnFireBreath(this->effects, &spawnPos2, &spawnVel2, &spawnAccel2, + 50.0f * Math_SinS(this->work[BFD_VAR_TIMER] * 0x2000) + 300.0f, breathOpacity, + this->actor.world.rot.y); + spawnSpeed2.x = 0.0f; + spawnSpeed2.y = 17.0f; + spawnSpeed2.z = 0.0f; + + for (i = 0; i < 6; i++) { + spawnAngleY = Rand_ZeroFloat(2.0f * M_PI); + spawnAngleX = Rand_ZeroFloat(2.0f * M_PI); + Matrix_RotateY(spawnAngleY, MTXMODE_NEW); + Matrix_RotateX(spawnAngleX, MTXMODE_APPLY); + Matrix_MultVec3f(&spawnSpeed2, &spawnVel2); + + spawnAccel2.x = (spawnVel2.x * -10) / 100; + spawnAccel2.y = (spawnVel2.y * -10) / 100; + spawnAccel2.z = (spawnVel2.z * -10) / 100; + + BossFd_SpawnEmber(this->effects, &this->headPos, &spawnVel2, &spawnAccel2, (s16)Rand_ZeroFloat(2.0f) + 8); + } + } + + if ((this->actor.world.pos.y < 90.0f) || (700.0f < this->actor.world.pos.y) || (this->actionFunc == BossFd_Wait)) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + this->actor.flags |= ACTOR_FLAG_0; + } +} + +void BossFd_CollisionCheck(BossFd* this, GlobalContext* globalCtx) { + ColliderJntSphElement* headCollider = &this->collider.elements[0]; + ColliderInfo* hurtbox; + + if (headCollider->info.bumperFlags & BUMP_HIT) { + headCollider->info.bumperFlags &= ~BUMP_HIT; + hurtbox = headCollider->info.acHitInfo; + this->actor.colChkInfo.health -= 2; + if (hurtbox->toucher.dmgFlags & 0x1000) { + this->actor.colChkInfo.health -= 2; + } + if ((s8)this->actor.colChkInfo.health <= 2) { + this->actor.colChkInfo.health = 2; + } + this->work[BFD_DAMAGE_FLASH_TIMER] = 10; + this->work[BFD_INVINC_TIMER] = 20; + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_DAMAGE1, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void BossFd_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossFd* this = (BossFd*)thisx; + f32 headGlow; + f32 rManeGlow; + f32 lManeGlow; + s16 i; + + osSyncPrintf("FD MOVE START \n"); + this->work[BFD_VAR_TIMER]++; + this->work[BFD_MOVE_TIMER]++; + this->actionFunc(this, globalCtx); + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + if (this->fireBreathTimer != 0) { + this->fireBreathTimer--; + } + if (this->work[BFD_DAMAGE_FLASH_TIMER] != 0) { + this->work[BFD_DAMAGE_FLASH_TIMER]--; + } + if (this->work[BFD_INVINC_TIMER] != 0) { + this->work[BFD_INVINC_TIMER]--; + } + if (this->work[BFD_ACTION_STATE] < BOSSFD_DEATH_START) { + if (this->work[BFD_INVINC_TIMER] == 0) { + BossFd_CollisionCheck(this, globalCtx); + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + BossFd_Effects(this, globalCtx); + this->fwork[BFD_TEX1_SCROLL_X] += 4.0f; + this->fwork[BFD_TEX1_SCROLL_Y] = 120.0f; + this->fwork[BFD_TEX2_SCROLL_X] += 3.0f; + this->fwork[BFD_TEX2_SCROLL_Y] -= 2.0f; + + Math_ApproachF(&this->fwork[BFD_BODY_TEX2_ALPHA], (this->work[BFD_VAR_TIMER] & 0x10) ? 30.0f : 158.0f, 1.0f, 8.0f); + if (this->skinSegments == 0) { + this->fwork[BFD_HEAD_TEX2_ALPHA] = this->fwork[BFD_BODY_TEX2_ALPHA]; + } else { + headGlow = (this->work[BFD_VAR_TIMER] & 4) ? 0.0f : 255.0f; + Math_ApproachF(&this->fwork[BFD_HEAD_TEX2_ALPHA], headGlow, 1.0f, 64.0f); + } + + headGlow = (this->work[BFD_VAR_TIMER] & 8) ? 128.0f : 255.0f; + rManeGlow = ((this->work[BFD_VAR_TIMER] + 3) & 8) ? 128.0f : 255.0f; + lManeGlow = ((this->work[BFD_VAR_TIMER] + 6) & 8) ? 128.0f : 255.0f; + + Math_ApproachF(&this->fwork[BFD_MANE_COLOR_CENTER], headGlow, 1.0f, 16.0f); + Math_ApproachF(&this->fwork[BFD_MANE_COLOR_RIGHT], rManeGlow, 1.0f, 16.0f); + Math_ApproachF(&this->fwork[BFD_MANE_COLOR_LEFT], lManeGlow, 1.0f, 16.0f); + + if (this->work[BFD_ROCK_TIMER] != 0) { + this->work[BFD_ROCK_TIMER]--; + if ((this->work[BFD_ROCK_TIMER] % 16) == 0) { + EnVbBall* bossFdRock = (EnVbBall*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_VB_BALL, this->actor.world.pos.x, 1000.0f, + this->actor.world.pos.z, 0, 0, (s16)Rand_ZeroFloat(50.0f) + 130, 100); + + if (bossFdRock != NULL) { + for (i = 0; i < 10; i++) { + Vec3f debrisVel = { 0.0f, 0.0f, 0.0f }; + Vec3f debrisAccel = { 0.0f, -1.0f, 0.0f }; + Vec3f debrisPos; + + debrisPos.x = Rand_CenteredFloat(300.0f) + bossFdRock->actor.world.pos.x; + debrisPos.y = Rand_CenteredFloat(300.0f) + bossFdRock->actor.world.pos.y; + debrisPos.z = Rand_CenteredFloat(300.0f) + bossFdRock->actor.world.pos.z; + + BossFd_SpawnDebris(this->effects, &debrisPos, &debrisVel, &debrisAccel, + (s16)Rand_ZeroFloat(15.0f) + 20); + } + } + } + } + + if (1) { // Needed for matching, and also to define new variables + Vec3f emberVel = { 0.0f, 0.0f, 0.0f }; + Vec3f emberAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f emberPos; + s16 temp_rand; + + for (i = 0; i < 6; i++) { + emberAccel.y = 0.4f; + emberAccel.x = Rand_CenteredFloat(0.5f); + emberAccel.z = Rand_CenteredFloat(0.5f); + + temp_rand = Rand_ZeroFloat(8.9f); + + emberPos.x = sHoleLocations[temp_rand].x + Rand_CenteredFloat(60.0f); + emberPos.y = (sHoleLocations[temp_rand].y + 10.0f) + Rand_ZeroFloat(40.0f); + emberPos.z = sHoleLocations[temp_rand].z + Rand_CenteredFloat(60.0f); + + BossFd_SpawnEmber(this->effects, &emberPos, &emberVel, &emberAccel, (s16)Rand_ZeroFloat(2.0f) + 6); + } + + if (this->skinSegments != 0) { + for (i = 0; i < (s16)this->fwork[BFD_MANE_EMBER_RATE]; i++) { + temp_rand = Rand_ZeroFloat(29.9f); + emberPos.y = this->centerMane.pos[temp_rand].y + Rand_CenteredFloat(20.0f); + + if (emberPos.y >= 90.0f) { + emberPos.x = this->centerMane.pos[temp_rand].x + Rand_CenteredFloat(20.0f); + emberPos.z = this->centerMane.pos[temp_rand].z + Rand_CenteredFloat(20.0f); + + emberVel.x = Rand_CenteredFloat(this->fwork[BFD_MANE_EMBER_SPEED]); + emberVel.y = Rand_CenteredFloat(this->fwork[BFD_MANE_EMBER_SPEED]); + emberVel.z = Rand_CenteredFloat(this->fwork[BFD_MANE_EMBER_SPEED]); + + emberAccel.y = 0.4f; + emberAccel.x = Rand_CenteredFloat(0.5f); + emberAccel.z = Rand_CenteredFloat(0.5f); + + BossFd_SpawnEmber(this->effects, &emberPos, &emberVel, &emberAccel, (s16)Rand_ZeroFloat(2.0f) + 8); + } + } + } + } + osSyncPrintf("FD MOVE END 1\n"); + BossFd_UpdateEffects(this, globalCtx); + osSyncPrintf("FD MOVE END 2\n"); +} + +void BossFd_UpdateEffects(BossFd* this, GlobalContext* globalCtx) { + BossFdEffect* effect = this->effects; + Player* player = GET_PLAYER(globalCtx); + Color_RGB8 colors[4] = { { 255, 128, 0 }, { 255, 0, 0 }, { 255, 255, 0 }, { 255, 0, 0 } }; + Vec3f diff; + s16 i1; + s16 i2; + + for (i1 = 0; i1 < 180; i1++, effect++) { + if (effect->type != BFD_FX_NONE) { + effect->timer1++; + + effect->pos.x += effect->velocity.x; + effect->pos.y += effect->velocity.y; + effect->pos.z += effect->velocity.z; + + effect->velocity.x += effect->accel.x; + effect->velocity.y += effect->accel.y; + effect->velocity.z += effect->accel.z; + if (effect->type == BFD_FX_EMBER) { + s16 cInd = effect->timer1 % 4; + + effect->color.r = colors[cInd].r; + effect->color.g = colors[cInd].g; + effect->color.b = colors[cInd].b; + effect->alpha -= 20; + if (effect->alpha <= 0) { + effect->alpha = 0; + effect->type = 0; + } + } else if ((effect->type == BFD_FX_DEBRIS) || (effect->type == BFD_FX_SKULL_PIECE)) { + effect->vFdFxRotX += 0.55f; + effect->vFdFxRotY += 0.1f; + if (effect->pos.y <= 100.0f) { + effect->type = 0; + } + } else if (effect->type == BFD_FX_DUST) { + if (effect->timer2 >= 8) { + effect->timer2 = 8; + effect->type = 0; + } else if (((effect->timer1 % 2) != 0) || (Rand_ZeroOne() < 0.3f)) { + effect->timer2++; + } + } else if (effect->type == BFD_FX_FIRE_BREATH) { + diff.x = player->actor.world.pos.x - effect->pos.x; + diff.y = player->actor.world.pos.y + 30.0f - effect->pos.y; + diff.z = player->actor.world.pos.z - effect->pos.z; + if ((this->timers[3] == 0) && (sqrtf(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)) < 20.0f)) { + this->timers[3] = 50; + func_8002F6D4(globalCtx, NULL, 5.0f, effect->kbAngle, 0.0f, 0x30); + if (player->isBurning == false) { + for (i2 = 0; i2 < ARRAY_COUNT(player->flameTimers); i2++) { + player->flameTimers[i2] = Rand_S16Offset(0, 200); + } + player->isBurning = true; + } + } + if (effect->timer2 == 0) { + if (effect->scale < 2.5f) { + effect->scale += effect->vFdFxScaleMod; + effect->vFdFxScaleMod += 0.08f; + } + if ((effect->pos.y <= (effect->vFdFxYStop + 130.0f)) || (effect->timer1 >= 10)) { + effect->accel.y = 5.0f; + effect->timer2++; + effect->velocity.y = 0.0f; + effect->accel.x = (effect->velocity.x * -25.0f) / 100.0f; + effect->accel.z = (effect->velocity.z * -25.0f) / 100.0f; + } + } else { + if (effect->scale < 2.5f) { + Math_ApproachF(&effect->scale, 2.5f, 0.5f, 0.5f); + } + effect->timer2++; + if (effect->timer2 >= 9) { + effect->type = 0; + } + } + } + } + } +} + +void BossFd_DrawEffects(BossFdEffect* effect, GlobalContext* globalCtx) { + static void* dustTex[] = { + gDust1Tex, gDust1Tex, gDust2Tex, gDust3Tex, gDust4Tex, gDust5Tex, gDust6Tex, gDust7Tex, gDust8Tex, + }; + u8 flag = false; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 i; + BossFdEffect* firstEffect = effect; + + OPEN_DISPS(gfxCtx, "../z_boss_fd.c", 4023); + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_EMBER) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaEmberMaterialDL); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, effect->color.r, effect->color.g, effect->color.b, effect->alpha); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4046), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaEmberModelDL); + } + } + + effect = firstEffect; + flag = false; + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_DEBRIS) { + if (!flag) { + func_80093D18(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, gVolvagiaDebrisMaterialDL); + flag++; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateY(effect->vFdFxRotY, MTXMODE_APPLY); + Matrix_RotateX(effect->vFdFxRotX, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4068), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gVolvagiaDebrisModelDL); + } + } + + effect = firstEffect; + flag = false; + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_DUST) { + if (!flag) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustMaterialDL); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 90, 30, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 90, 30, 0, 0); + flag++; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4104), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[effect->timer2])); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustModelDL); + } + } + + effect = firstEffect; + flag = false; + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_FIRE_BREATH) { + if (!flag) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 10, 0, 255); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, effect->alpha); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4154), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[effect->timer2])); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaDustModelDL); + } + } + + effect = firstEffect; + flag = false; + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_SKULL_PIECE) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaSkullPieceMaterialDL); + flag++; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateY(effect->vFdFxRotY, MTXMODE_APPLY); + Matrix_RotateX(effect->vFdFxRotX, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_fd.c", 4192), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaSkullPieceModelDL); + } + } + + CLOSE_DISPS(gfxCtx, "../z_boss_fd.c", 4198); +} + +void BossFd_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossFd* this = (BossFd*)thisx; + + osSyncPrintf("FD DRAW START\n"); + if (this->actionFunc != BossFd_Wait) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4217); + func_80093D18(globalCtx->state.gfxCtx); + if (this->work[BFD_DAMAGE_FLASH_TIMER] & 2) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099); + } + + BossFd_DrawBody(globalCtx, this); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4243); + } + + osSyncPrintf("FD DRAW END\n"); + BossFd_DrawEffects(this->effects, globalCtx); + osSyncPrintf("FD DRAW END2\n"); +} + +s32 BossFd_OverrideRightArmDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossFd* this = (BossFd*)thisx; + + switch (limbIndex) { + case 1: + rot->y += 4000.0f + this->rightArmRot[0].x; + break; + case 2: + rot->y += this->rightArmRot[1].x; + rot->z += this->rightArmRot[1].y; + break; + case 3: + rot->y += this->rightArmRot[2].x; + rot->z += this->rightArmRot[2].y; + break; + } + if (this->skinSegments < limbIndex) { + *dList = NULL; + } + return false; +} + +s32 BossFd_OverrideLeftArmDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossFd* this = (BossFd*)thisx; + + switch (limbIndex) { + case 1: + rot->y += -4000.0f + this->leftArmRot[0].x; + break; + case 2: + rot->y += this->leftArmRot[1].x; + rot->z += this->leftArmRot[1].y; + break; + case 3: + rot->y += this->leftArmRot[2].x; + rot->z += this->leftArmRot[2].y; + break; + } + if (this->skinSegments < limbIndex) { + *dList = NULL; + } + return false; +} + +static s16 sBodyIndex[] = { 0, 95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 15, 10, 5 }; +static s16 sManeIndex[] = { 0, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 }; // Unused + +void BossFd_DrawMane(GlobalContext* globalCtx, BossFd* this, Vec3f* manePos, Vec3f* maneRot, f32* maneScale, u8 mode) { + f32 sp140[] = { 0.0f, 10.0f, 17.0f, 20.0f, 19.5f, 18.0f, 17.0f, 15.0f, 15.0f, 15.0f }; + f32 sp118[] = { 0.0f, 10.0f, 17.0f, 20.0f, 21.0f, 21.0f, 21.0f, 21.0f, 21.0f, 21.0f }; + f32 spF0[] = { 0.4636457f, 0.3366129f, 0.14879614f, 0.04995025f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + // arctan of {0.5, 0.35, 0.15, 0.05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + f32 spC8[] = { -0.4636457f, -0.3366129f, -0.14879614f, 0.024927188f, 0.07478157f, + 0.04995025f, 0.09961288f, 0.0f, 0.0f, 0.0f }; + // arctan of {-0.5, -0.35, -0.15, 0.025, 0.075, 0.05, 0.1, 0.0, 0.0} + s16 maneIndex; + s16 i; + s16 maneLength; + Vec3f spB4; + Vec3f spA8; + f32 phi_f20; + f32 phi_f22; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4419); + + maneLength = this->skinSegments; + maneLength = CLAMP_MAX(maneLength, 10); + + for (i = 0; i < maneLength; i++) { + maneIndex = (this->work[BFD_LEAD_MANE_SEG] - (i * 2) + 30) % 30; + + if (mode == 0) { + spB4.x = spB4.z = 0.0f; + spB4.y = ((sp140[i] * 0.1f) * 10.0f) * this->flattenMane; + phi_f20 = 0.0f; + phi_f22 = spC8[i] * this->flattenMane; + } else if (mode == 1) { + phi_f22 = (spC8[i] * this->flattenMane) * 0.7f; + phi_f20 = spF0[i] * this->flattenMane; + + spB4.y = (sp140[i] * this->flattenMane) * 0.7f; + spB4.x = -sp118[i] * this->flattenMane; + spB4.z = 0.0f; + } else { + phi_f22 = (spC8[i] * this->flattenMane) * 0.7f; + phi_f20 = -spF0[i] * this->flattenMane; + + spB4.y = (sp140[i] * this->flattenMane) * 0.7f; + spB4.x = sp118[i] * this->flattenMane; + spB4.z = 0.0f; + } + + Matrix_RotateY((maneRot + maneIndex)->y, MTXMODE_NEW); + Matrix_RotateX(-(maneRot + maneIndex)->x, MTXMODE_APPLY); + + Matrix_MultVec3f(&spB4, &spA8); + + Matrix_Translate((manePos + maneIndex)->x + spA8.x, (manePos + maneIndex)->y + spA8.y, + (manePos + maneIndex)->z + spA8.z, MTXMODE_NEW); + Matrix_RotateY((maneRot + maneIndex)->y + phi_f20, MTXMODE_APPLY); + Matrix_RotateX(-((maneRot + maneIndex)->x + phi_f22), MTXMODE_APPLY); + Matrix_Scale(maneScale[maneIndex] * (0.01f - (i * 0.0008f)), maneScale[maneIndex] * (0.01f - (i * 0.0008f)), + 0.01f, MTXMODE_APPLY); + Matrix_RotateX(-M_PI / 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4480), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaManeModelDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4483); +} + +s32 BossFd_OverrideHeadDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + BossFd* this = (BossFd*)thisx; + + switch (limbIndex) { + case 5: + case 6: + rot->z -= this->jawOpening * 0.1f; + break; + case 2: + rot->z += this->jawOpening; + break; + } + if ((this->faceExposed == true) && (limbIndex == 5)) { + *dList = gVolvagiaBrokenFaceDL; + } + if (this->skinSegments == 0) { + if (limbIndex == 6) { + *dList = gVolvagiaSkullDL; + } else if (limbIndex == 2) { + *dList = gVolvagiaJawboneDL; + } else { + *dList = NULL; + } + } + return false; +} + +void BossFd_PostHeadDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f targetMod = { 4500.0f, 0.0f, 0.0f }; + static Vec3f headMod = { 4000.0f, 0.0f, 0.0f }; + BossFd* this = (BossFd*)thisx; + + if (limbIndex == 5) { + Matrix_MultVec3f(&targetMod, &this->actor.focus.pos); + Matrix_MultVec3f(&headMod, &this->headPos); + } +} + +static void* sEyeTextures[] = { + gVolvagiaEyeOpenTex, + gVolvagiaEyeHalfTex, + gVolvagiaEyeClosedTex, +}; + +static Gfx* sBodyDLists[] = { + gVolvagiaBodySeg1DL, gVolvagiaBodySeg2DL, gVolvagiaBodySeg3DL, gVolvagiaBodySeg4DL, gVolvagiaBodySeg5DL, + gVolvagiaBodySeg6DL, gVolvagiaBodySeg7DL, gVolvagiaBodySeg8DL, gVolvagiaBodySeg9DL, gVolvagiaBodySeg10DL, + gVolvagiaBodySeg11DL, gVolvagiaBodySeg12DL, gVolvagiaBodySeg13DL, gVolvagiaBodySeg14DL, gVolvagiaBodySeg15DL, + gVolvagiaBodySeg16DL, gVolvagiaBodySeg17DL, gVolvagiaBodySeg18DL, +}; + +void BossFd_DrawBody(GlobalContext* globalCtx, BossFd* this) { + s16 segIndex; + s16 i; + f32 temp_float; + Mtx* tempMat = Graph_Alloc(globalCtx->state.gfxCtx, 18 * sizeof(Mtx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4589); + if (this->skinSegments != 0) { + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeState])); + } + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->fwork[BFD_TEX1_SCROLL_X], + (s16)this->fwork[BFD_TEX1_SCROLL_Y], 0x20, 0x20, 1, (s16)this->fwork[BFD_TEX2_SCROLL_X], + (s16)this->fwork[BFD_TEX2_SCROLL_Y], 0x20, 0x20)); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, (s8)this->fwork[BFD_BODY_TEX2_ALPHA]); + + osSyncPrintf("LH\n"); + Matrix_Push(); + segIndex = (this->work[BFD_LEAD_BODY_SEG] + sBodyIndex[2]) % 100; + Matrix_Translate(this->bodySegsPos[segIndex].x, this->bodySegsPos[segIndex].y, this->bodySegsPos[segIndex].z, + MTXMODE_NEW); + Matrix_RotateY(this->bodySegsRot[segIndex].y, MTXMODE_APPLY); + Matrix_RotateX(-this->bodySegsRot[segIndex].x, MTXMODE_APPLY); + Matrix_Translate(-13.0f, -5.0f, 13.0f, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x * 0.1f, this->actor.scale.y * 0.1f, this->actor.scale.z * 0.1f, MTXMODE_APPLY); + SkelAnime_DrawOpa(globalCtx, this->skelAnimeRightArm.skeleton, this->skelAnimeRightArm.jointTable, + BossFd_OverrideRightArmDraw, NULL, this); + Matrix_Pop(); + osSyncPrintf("RH\n"); + Matrix_Push(); + segIndex = (this->work[BFD_LEAD_BODY_SEG] + sBodyIndex[2]) % 100; + Matrix_Translate(this->bodySegsPos[segIndex].x, this->bodySegsPos[segIndex].y, this->bodySegsPos[segIndex].z, + MTXMODE_NEW); + Matrix_RotateY(this->bodySegsRot[segIndex].y, MTXMODE_APPLY); + Matrix_RotateX(-this->bodySegsRot[segIndex].x, MTXMODE_APPLY); + Matrix_Translate(13.0f, -5.0f, 13.0f, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x * 0.1f, this->actor.scale.y * 0.1f, this->actor.scale.z * 0.1f, MTXMODE_APPLY); + SkelAnime_DrawOpa(globalCtx, this->skelAnimeLeftArm.skeleton, this->skelAnimeLeftArm.jointTable, + BossFd_OverrideLeftArmDraw, NULL, this); + Matrix_Pop(); + osSyncPrintf("BD\n"); + gSPSegment(POLY_OPA_DISP++, 0x0D, tempMat); + + Matrix_Push(); + for (i = 0; i < 18; i++, tempMat++) { + segIndex = (this->work[BFD_LEAD_BODY_SEG] + sBodyIndex[i + 1]) % 100; + Matrix_Translate(this->bodySegsPos[segIndex].x, this->bodySegsPos[segIndex].y, this->bodySegsPos[segIndex].z, + MTXMODE_NEW); + Matrix_RotateY(this->bodySegsRot[segIndex].y, MTXMODE_APPLY); + Matrix_RotateX(-this->bodySegsRot[segIndex].x, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 35.0f, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + if (i < this->skinSegments) { + Matrix_Scale(1.0f + (Math_SinS((this->work[BFD_LEAD_BODY_SEG] * 5000.0f) + (i * 7000.0f)) * + this->fwork[BFD_BODY_PULSE]), + 1.0f + (Math_SinS((this->work[BFD_LEAD_BODY_SEG] * 5000.0f) + (i * 7000.0f)) * + this->fwork[BFD_BODY_PULSE]), + 1.0f, MTXMODE_APPLY); + Matrix_RotateY(M_PI / 2.0f, MTXMODE_APPLY); + Matrix_ToMtx(tempMat, "../z_boss_fd.c", 4719); + gSPMatrix(POLY_OPA_DISP++, tempMat, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sBodyDLists[i]); + } else { + MtxF spFC; + Vec3f spF0 = { 0.0f, 0.0f, 0.0f }; + Vec3f spE4; + Vec3s spDC; + f32 padD8; + + if (this->bodyFallApart[i] < 2) { + f32 spD4 = 0.1f; + + temp_float = 0.1f; + Matrix_Translate(0.0f, 0.0f, -1100.0f, MTXMODE_APPLY); + Matrix_RotateY(-M_PI, MTXMODE_APPLY); + if (i >= 14) { + f32 sp84 = 1.0f - ((i - 14) * 0.2f); + + Matrix_Scale(sp84, sp84, 1.0f, MTXMODE_APPLY); + spD4 = 0.1f * sp84; + temp_float = 0.1f * sp84; + } + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4768), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gVolvagiaRibsDL); + + if (this->bodyFallApart[i] == 1) { + EnVbBall* bones; + + this->bodyFallApart[i] = 2; + Matrix_MultVec3f(&spF0, &spE4); + Matrix_Get(&spFC); + Matrix_MtxFToYXZRotS(&spFC, &spDC, 0); + bones = + (EnVbBall*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_VB_BALL, + spE4.x, spE4.y, spE4.z, spDC.x, spDC.y, spDC.z, i + 200); + + bones->actor.scale.x = this->actor.scale.x * temp_float; + bones->actor.scale.y = this->actor.scale.y * spD4; + bones->actor.scale.z = this->actor.scale.z * 0.1f; + } + } + } + if (i > 0) { + Collider_UpdateSpheres(i + 1, &this->collider); + } + } + Matrix_Pop(); + osSyncPrintf("BH\n"); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, (s8)this->fwork[BFD_HEAD_TEX2_ALPHA]); + Matrix_Push(); + temp_float = + (this->work[BFD_ACTION_STATE] >= BOSSFD_SKULL_FALL) ? -20.0f : -10.0f - ((this->actor.speedXZ - 5.0f) * 10.0f); + segIndex = (this->work[BFD_LEAD_BODY_SEG] + sBodyIndex[0]) % 100; + Matrix_Translate(this->bodySegsPos[segIndex].x, this->bodySegsPos[segIndex].y, this->bodySegsPos[segIndex].z, + MTXMODE_NEW); + Matrix_RotateY(this->bodySegsRot[segIndex].y, MTXMODE_APPLY); + Matrix_RotateX(-this->bodySegsRot[segIndex].x, MTXMODE_APPLY); + Matrix_RotateZ((this->actor.shape.rot.z / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, temp_float, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Translate(0.0f, 0.0f, 25.0f, MTXMODE_APPLY); + osSyncPrintf("BHC\n"); + Collider_UpdateSpheres(0, &this->collider); + Matrix_Pop(); + osSyncPrintf("BHCE\n"); + Matrix_Scale(this->actor.scale.x * 0.1f, this->actor.scale.y * 0.1f, this->actor.scale.z * 0.1f, MTXMODE_APPLY); + SkelAnime_DrawOpa(globalCtx, this->skelAnimeHead.skeleton, this->skelAnimeHead.jointTable, BossFd_OverrideHeadDraw, + BossFd_PostHeadDraw, &this->actor); + osSyncPrintf("SK\n"); + { + Vec3f spB0 = { 0.0f, 1700.0f, 7000.0f }; + Vec3f spA4 = { -1000.0f, 700.0f, 7000.0f }; + + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gVolvagiaManeMaterialDL); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, this->fwork[BFD_MANE_COLOR_CENTER], 0, 255); + Matrix_Push(); + Matrix_MultVec3f(&spB0, &this->centerMane.head); + BossFd_DrawMane(globalCtx, this, this->centerMane.pos, this->fireManeRot, this->centerMane.scale, MANE_CENTER); + Matrix_Pop(); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, this->fwork[BFD_MANE_COLOR_RIGHT], 0, 255); + Matrix_Push(); + Matrix_MultVec3f(&spA4, &this->rightMane.head); + BossFd_DrawMane(globalCtx, this, this->rightMane.pos, this->fireManeRot, this->rightMane.scale, MANE_RIGHT); + Matrix_Pop(); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, this->fwork[BFD_MANE_COLOR_LEFT], 0, 255); + Matrix_Push(); + spA4.x *= -1.0f; + Matrix_MultVec3f(&spA4, &this->leftMane.head); + BossFd_DrawMane(globalCtx, this, this->leftMane.pos, this->fireManeRot, this->leftMane.scale, MANE_LEFT); + Matrix_Pop(); + } + + Matrix_Pop(); + osSyncPrintf("END\n"); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd.c", 4987); +} diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h new file mode 100644 index 000000000..ea24fd346 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd.h @@ -0,0 +1,185 @@ +#ifndef Z_BOSS_FD_H +#define Z_BOSS_FD_H + +#include "ultra64.h" +#include "global.h" + +struct BossFd; + +typedef void (*BossFdActionFunc)(struct BossFd*, GlobalContext*); + +typedef enum { + /* -1 */ BOSSFD_WAIT_INTRO = -1, + /* 0 */ BOSSFD_FLY_MAIN, + /* 1 */ BOSSFD_FLY_HOLE, + /* 2 */ BOSSFD_BURROW, + /* 3 */ BOSSFD_EMERGE, + /* 50 */ BOSSFD_FLY_CEILING = 50, + /* 51 */ BOSSFD_DROP_ROCKS, + /* 100 */ BOSSFD_FLY_CHASE = 100, + /* 101 */ BOSSFD_FLY_UNUSED, + /* 200 */ BOSSFD_DEATH_START = 200, + /* 201 */ BOSSFD_SKIN_BURN, + /* 202 */ BOSSFD_BONES_FALL, + /* 203 */ BOSSFD_SKULL_PAUSE, + /* 204 */ BOSSFD_SKULL_FALL, + /* 205 */ BOSSFD_SKULL_BURN +} BossFdActionState; + +typedef enum { + /* 0 */ BFD_CS_NONE, + /* 1 */ BFD_CS_WAIT, + /* 2 */ BFD_CS_START, + /* 3 */ BFD_CS_LOOK_LINK, + /* 4 */ BFD_CS_LOOK_GROUND, + /* 5 */ BFD_CS_COLLAPSE, + /* 6 */ BFD_CS_EMERGE +} BossFdCutsceneState; + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ u8 type; + /* 0x25 */ u8 timer1; + /* 0x26 */ Color_RGB8 color; + /* 0x2A */ s16 alpha; + /* 0x2C */ s16 timer2; + /* 0x2E */ s16 kbAngle; + /* 0x30 */ f32 scale; + /* 0x34 */ f32 bFdFxFloat1; + /* 0x38 */ f32 bFdFxFloat2; +} BossFdEffect; // size = 0x3C + +#define BOSSFD_EFFECT_COUNT 180 + +#define vFdFxRotX bFdFxFloat1 +#define vFdFxScaleMod bFdFxFloat1 +#define vFdFxRotY bFdFxFloat2 +#define vFdFxYStop bFdFxFloat2 + +typedef enum { + /* 0 */ BFD_FX_NONE, + /* 1 */ BFD_FX_EMBER, + /* 2 */ BFD_FX_DEBRIS, + /* 3 */ BFD_FX_DUST, + /* 4 */ BFD_FX_FIRE_BREATH, + /* 5 */ BFD_FX_SKULL_PIECE +} BossFdEffectType; + +typedef struct { + /* 0x000 */ Vec3f pos[30]; + /* 0x168 */ f32 scale[30]; + /* 0x1E0 */ Vec3f head; +} BossFdMane; // size = 0x1EC + +typedef struct { + /* 0x00 */ Vec3f eye; + /* 0x0C */ Vec3f at; + /* 0x18 */ Vec3f pad[2]; + /* 0x30 */ Vec3f eyeVel; + /* 0x3C */ Vec3f atVel; + /* 0x48 */ Vec3f nextEye; + /* 0x54 */ Vec3f eyeMaxVel; + /* 0x60 */ Vec3f nextAt; + /* 0x6C */ Vec3f atMaxVel; + /* 0x78 */ f32 speedMod; + /* 0x7C */ f32 accel; + /* 0x80 */ f32 yMod; + /* 0x84 */ f32 shake; +} BossFdCam; // size = 0x88 + +typedef enum { + /* 0 */ BFD_ACTION_STATE, + /* 1 */ BFD_MOVE_TIMER, + /* 2 */ BFD_VAR_TIMER, + /* 3 */ BFD_LEAD_BODY_SEG, + /* 4 */ BFD_LEAD_MANE_SEG, + /* 5 */ BFD_BLINK_TIMER, + /* 6 */ BFD_ROAR_TIMER, + /* 7 */ BFD_DAMAGE_FLASH_TIMER, + /* 8 */ BFD_START_ATTACK, + /* 9 */ BFD_UNK_234, + /* 10 */ BFD_UNK_236, + /* 11 */ BFD_MANE_EMBERS_TIMER, + /* 12 */ BFD_ROCK_TIMER, + /* 13 */ BFD_CEILING_TARGET, + /* 14 */ BFD_INVINC_TIMER, + /* 15 */ BFD_SPLASH_TIMER, + /* 16 */ BFD_CAM_SHAKE_TIMER, + /* 17 */ BFD_STOP_FLAG, + /* 18 */ BFD_FLY_COUNT, + /* 19 */ BFD_SHORT_COUNT +} BossFdS16Var; + +typedef enum { + /* 0 */ BFD_TEX1_SCROLL_X, + /* 1 */ BFD_TEX1_SCROLL_Y, + /* 2 */ BFD_TEX2_SCROLL_X, + /* 3 */ BFD_TEX2_SCROLL_Y, + /* 4 */ BFD_UNUSED_F4, + /* 5 */ BFD_UNUSED_F5, + /* 6 */ BFD_UNUSED_F6, + /* 7 */ BFD_BODY_TEX2_ALPHA, + /* 8 */ BFD_HEAD_TEX2_ALPHA, + /* 9 */ BFD_TARGET_Y_OFFSET, + /* 10 */ BFD_CEILING_BOUNCE, + /* 11 */ BFD_BODY_PULSE, + /* 12 */ BFD_MANE_COLOR_CENTER, + /* 13 */ BFD_MANE_COLOR_RIGHT, + /* 14 */ BFD_MANE_COLOR_LEFT, + /* 15 */ BFD_MANE_EMBER_SPEED, + /* 16 */ BFD_MANE_EMBER_RATE, + /* 17 */ BFD_UNUSED_F17, + /* 18 */ BFD_UNUSED_F18, + /* 19 */ BFD_UNUSED_F19, + /* 20 */ BFD_FLY_SPEED, + /* 21 */ BFD_TURN_RATE, + /* 22 */ BFD_TURN_RATE_MAX, + /* 23 */ BFD_FLY_WOBBLE_AMP, + /* 24 */ BFD_FLY_WOBBLE_RATE, + /* 25 */ BFD_UNUSED_F25, + /* 26 */ BFD_FLOAT_COUNT +} BossFdF32Var; + +typedef struct BossFd { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnimeHead; + /* 0x0190 */ SkelAnime skelAnimeRightArm; + /* 0x01D4 */ SkelAnime skelAnimeLeftArm; + /* 0x0218 */ BossFdActionFunc actionFunc; + /* 0x021C */ s16 fireBreathTimer; + /* 0x021E */ s16 skinSegments; + /* 0x0220 */ u8 fogMode; + /* 0x0222 */ s16 work[BFD_SHORT_COUNT]; + /* 0x0248 */ s16 timers[6]; + /* 0x0254 */ f32 fwork[BFD_FLOAT_COUNT]; + /* 0x02BC */ Vec3f targetPosition; + /* 0x02C8 */ Vec3f holePosition; + /* 0x02D4 */ u8 holeIndex; + /* 0x02D5 */ u8 eyeState; + /* 0x02D6 */ u8 platformSignal; + /* 0x02D7 */ u8 faceExposed; + /* 0x02D8 */ u8 handoffSignal; + /* 0x02DC */ Vec3f bodySegsRot[100]; + /* 0x078C */ Vec3f bodySegsPos[100]; + /* 0x0C3C */ Vec3f rightArmRot[4]; + /* 0x0C6C */ Vec3f leftArmRot[4]; + /* 0x0C9C */ Vec3f fireManeRot[30]; + /* 0x0E04 */ BossFdMane centerMane; + /* 0x0FF0 */ BossFdMane rightMane; + /* 0x11DC */ BossFdMane leftMane; + /* 0x13C8 */ f32 flattenMane; + /* 0x13CC */ f32 jawOpening; + /* 0x13D0 */ s16 bodyFallApart[18]; + /* 0x13F4 */ Vec3f headPos; + /* 0x1400 */ s16 introFlyState; + /* 0x1402 */ s16 introState; + /* 0x1404 */ s16 introCamera; + /* 0x1408 */ BossFdCam camData; + /* 0x1490 */ ColliderJntSph collider; + /* 0x14B0 */ ColliderJntSphElement elements[19]; + /* 0x1970 */ BossFdEffect effects[180]; +} BossFd; // size = 0x43A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd_colchk.c b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd_colchk.c new file mode 100644 index 000000000..c7b6a8b80 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Fd/z_boss_fd_colchk.c @@ -0,0 +1,226 @@ +#include "global.h" + +static ColliderJntSphElementInit sJntSphItemsInit[19] = { + { + { + ELEMTYPE_UNK3, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 2, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 3, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 4, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 5, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 6, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 7, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 8, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 9, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 10, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 11, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 12, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 14, { { 0, 0, 0 }, 18 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 15, { { 0, 0, 0 }, 16 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 16, { { 0, 0, 0 }, 14 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 17, { { 0, 0, 0 }, 12 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 18, { { 0, 0, 0 }, 10 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 19, + sJntSphItemsInit, +}; diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c new file mode 100644 index 000000000..20db1fb55 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -0,0 +1,1218 @@ +/* + * File: z_boss_fd2.c + * Overlay: ovl_Boss_Fd2 + * Description: Volvagia, hole form + */ + +#include "z_boss_fd2.h" +#include "objects/object_fd2/object_fd2.h" +#include "overlays/actors/ovl_Boss_Fd/z_boss_fd.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ DEATH_START, + /* 1 */ DEATH_RETREAT, + /* 2 */ DEATH_HANDOFF, + /* 3 */ DEATH_FD_BODY, + /* 4 */ DEATH_FD_SKULL, + /* 5 */ DEATH_FINISH +} BossFd2CutsceneState; + +typedef enum { + /* 0 */ EYE_OPEN, + /* 1 */ EYE_HALF, + /* 2 */ EYE_CLOSED +} BossFd2EyeState; + +void BossFd2_Init(Actor* thisx, GlobalContext* globalCtx); +void BossFd2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossFd2_Update(Actor* thisx, GlobalContext* globalCtx); +void BossFd2_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BossFd2_SetupEmerge(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Emerge(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_SetupIdle(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Idle(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Burrow(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_SetupBreatheFire(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_BreatheFire(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_SetupClawSwipe(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_ClawSwipe(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Vulnerable(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Damaged(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Death(BossFd2* this, GlobalContext* globalCtx); +void BossFd2_Wait(BossFd2* this, GlobalContext* globalCtx); + +const ActorInit Boss_Fd2_InitVars = { + ACTOR_BOSS_FD2, + ACTORCAT_BOSS, + FLAGS, + OBJECT_FD2, + sizeof(BossFd2), + (ActorFunc)BossFd2_Init, + (ActorFunc)BossFd2_Destroy, + (ActorFunc)BossFd2_Update, + (ActorFunc)BossFd2_Draw, + NULL, +}; + +#include "z_boss_fd2_colchk.c" + +static Vec3f sHoleLocations[] = { + { 0.0f, 90.0f, -243.0f }, { 0.0f, 90.0f, 0.0f }, { 0.0f, 90.0f, 243.0f }, + { -243.0f, 90.0f, -243.0f }, { -243.0f, 90.0f, 0.0f }, { -243.0f, 90.0f, 243.0f }, + { 243.0f, 90.0f, -243.0f }, { 243.0f, 90.0f, 0.0f }, { 243.0f, 90.0f, 243.0f }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x21, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void BossFd2_SpawnDebris(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* position, Vec3f* velocity, + Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_DEBRIS; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->vFdFxRotX = Rand_ZeroFloat(100.0f); + effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + break; + } + } +} + +void BossFd2_SpawnFireBreath(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* position, Vec3f* velocity, + Vec3f* acceleration, f32 scale, s16 alpha, s16 kbAngle) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_FIRE_BREATH; + effect->timer1 = 0; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->pos.x -= effect->velocity.x; + effect->pos.y -= effect->velocity.y; + effect->pos.z -= effect->velocity.z; + effect->vFdFxScaleMod = 0.0f; + effect->alpha = alpha; + effect->vFdFxYStop = Rand_ZeroFloat(10.0f); + effect->timer2 = 0; + effect->scale = scale / 400.0f; + effect->kbAngle = kbAngle; + break; + } + } +} + +void BossFd2_SpawnEmber(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* position, Vec3f* velocity, + Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == 0) { + effect->type = BFD_FX_EMBER; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->alpha = 255; + effect->timer1 = (s16)Rand_ZeroFloat(10.0f); + break; + } + } +} + +void BossFd2_SpawnSkullPiece(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* position, Vec3f* velocity, + Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_SKULL_PIECE; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->vFdFxRotX = Rand_ZeroFloat(100.0f); + effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + break; + } + } +} + +void BossFd2_SpawnDust(BossFdEffect* effect, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_DUST; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->timer2 = 0; + effect->scale = scale / 400.0f; + break; + } + } +} + +void BossFd2_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossFd2* this = (BossFd2*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.0069999993f); + this->actor.world.pos.y = -850.0f; + ActorShape_Init(&this->actor.shape, -580.0f / this->actor.scale.y, NULL, 0.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gHoleVolvagiaSkel, &gHoleVolvagiaIdleAnim, NULL, NULL, 0); + if (this->actor.params == BFD_CS_NONE) { + BossFd2_SetupEmerge(this, globalCtx); + } else { + this->actionFunc = BossFd2_Wait; + } + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->elements); +} + +void BossFd2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossFd2* this = (BossFd2*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void BossFd2_SetupEmerge(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + s16 temp_rand; + s8 health; + + osSyncPrintf("UP INIT 1\n"); + Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaEmergeAnim); + this->actionFunc = BossFd2_Emerge; + this->skelAnime.playSpeed = 0.0f; + temp_rand = Rand_ZeroFloat(8.9f); + this->actor.world.pos.x = sHoleLocations[temp_rand].x; + this->actor.world.pos.z = sHoleLocations[temp_rand].z; + this->work[FD2_ACTION_STATE] = 0; + osSyncPrintf("UP INIT 2\n"); + this->timers[0] = 10; + if (bossFd != NULL) { + health = bossFd->actor.colChkInfo.health; + if (health >= 18) { + this->work[FD2_FAKEOUT_COUNT] = 0; + } else if (health >= 12) { + this->work[FD2_FAKEOUT_COUNT] = 1; + } else if (health >= 6) { + this->work[FD2_FAKEOUT_COUNT] = 2; + } else { + this->work[FD2_FAKEOUT_COUNT] = 3; + } + } +} + +void BossFd2_Emerge(BossFd2* this, GlobalContext* globalCtx) { + s8 health; + BossFd* bossFd = (BossFd*)this->actor.parent; + Player* player = GET_PLAYER(globalCtx); + s16 i; + s16 holeTime; + + osSyncPrintf("UP 1 mode %d\n", this->work[FD2_ACTION_STATE]); + SkelAnime_Update(&this->skelAnime); + osSyncPrintf("UP 1.5 \n"); + switch (this->work[FD2_ACTION_STATE]) { + case 0: + osSyncPrintf("UP time %d \n", this->timers[0]); + osSyncPrintf("PL time %x \n", player); + osSyncPrintf("MT time %x \n", bossFd); + if ((this->timers[0] == 0) && (player->actor.world.pos.y > 70.0f)) { + osSyncPrintf("UP 1.6 \n"); + bossFd->faceExposed = 0; + bossFd->holePosition.x = this->actor.world.pos.x; + bossFd->holePosition.z = this->actor.world.pos.z; + func_80033E1C(globalCtx, 1, 0x32, 0x5000); + this->work[FD2_ACTION_STATE] = 1; + this->work[FD2_HOLE_COUNTER]++; + this->actor.world.pos.y = -200.0f; + health = bossFd->actor.colChkInfo.health; + if (health == 24) { + holeTime = 30; + } else if (health >= 18) { + holeTime = 25; + } else if (health >= 12) { + holeTime = 20; + } else if (health >= 6) { + holeTime = 10; + } else { + holeTime = 5; + } + this->timers[0] = holeTime; + bossFd->timers[4] = this->timers[0] + 10; + osSyncPrintf("UP 1.7 \n"); + } + break; + case 1: + if (this->timers[0] == 0) { + if (this->work[FD2_FAKEOUT_COUNT] != 0) { + this->work[FD2_FAKEOUT_COUNT]--; + i = Rand_ZeroFloat(8.9f); + this->actor.world.pos.x = sHoleLocations[i].x; + this->actor.world.pos.z = sHoleLocations[i].z; + this->work[FD2_ACTION_STATE] = 0; + this->timers[0] = 10; + } else { + this->skelAnime.playSpeed = 1.0f; + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaEmergeAnim); + this->work[FD2_ACTION_STATE] = 2; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_ROAR); + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->timers[0] = 15; + this->actor.world.pos.y = 150.0f; + for (i = 0; i < 10; i++) { + this->rightMane.pos[i].x += Rand_CenteredFloat(100.0f); + this->rightMane.pos[i].z += Rand_CenteredFloat(100.0f); + this->leftMane.pos[i].x += Rand_CenteredFloat(100.0f); + this->leftMane.pos[i].z += Rand_CenteredFloat(100.0f); + } + bossFd->work[BFD_SPLASH_TIMER] = 5; + } + } + break; + case 2: + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x7D0); + if ((this->timers[0] == 1) && (this->actor.xzDistToPlayer < 120.0f)) { + func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.yawTowardsPlayer, 2.0f, 0x20); + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + BossFd2_SetupIdle(this, globalCtx); + } + break; + } + osSyncPrintf("UP 2\n"); +} + +void BossFd2_SetupIdle(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + s8 health; + s16 idleTime; + + osSyncPrintf("UP INIT 1\n"); + Animation_PlayLoop(&this->skelAnime, &gHoleVolvagiaTurnAnim); + this->actionFunc = BossFd2_Idle; + health = bossFd->actor.colChkInfo.health; + if (health == 24) { + idleTime = 50; + } else if (health >= 18) { + idleTime = 40; + } else if (health >= 12) { + idleTime = 40; + } else if (health >= 6) { + idleTime = 30; + } else { + idleTime = 20; + } + this->timers[0] = idleTime; +} + +void BossFd2_Idle(BossFd2* this, GlobalContext* globalCtx) { + s16 prevToLink; + + SkelAnime_Update(&this->skelAnime); + prevToLink = this->work[FD2_TURN_TO_LINK]; + this->work[FD2_TURN_TO_LINK] = + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x7D0, 0); + osSyncPrintf("SW1 = %d\n", prevToLink); + osSyncPrintf("SW2 = %d\n", this->work[FD2_TURN_TO_LINK]); + if ((fabsf(prevToLink) <= 1000.0f) && (1000.0f < fabsf(this->work[FD2_TURN_TO_LINK]))) { + Animation_MorphToLoop(&this->skelAnime, &gHoleVolvagiaTurnAnim, -5.0f); + } + if ((1000.0f < fabsf(prevToLink)) && (fabsf(this->work[FD2_TURN_TO_LINK]) <= 1000.0f)) { + Animation_MorphToLoop(&this->skelAnime, &gHoleVolvagiaIdleAnim, -5.0f); + } + if (this->timers[0] == 0) { + if (this->actor.xzDistToPlayer < 200.0f) { + BossFd2_SetupClawSwipe(this, globalCtx); + } else { + BossFd2_SetupBreatheFire(this, globalCtx); + } + } +} + +void BossFd2_SetupBurrow(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + + Animation_MorphToPlayOnce(&this->skelAnime, &gHoleVolvagiaBurrowAnim, -5.0f); + this->actionFunc = BossFd2_Burrow; + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaBurrowAnim); + bossFd->timers[4] = 30; + this->work[FD2_ACTION_STATE] = 0; +} + +void BossFd2_Burrow(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + + if (this->work[FD2_ACTION_STATE] == 0) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + this->work[FD2_ACTION_STATE] = 1; + this->timers[0] = 25; + } + } else { + Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 10.0f); + if (this->timers[0] == 0) { + if ((this->work[FD2_HOLE_COUNTER] >= 3) && ((s8)bossFd->actor.colChkInfo.health < 24)) { + this->work[FD2_HOLE_COUNTER] = 0; + this->actionFunc = BossFd2_Wait; + bossFd->handoffSignal = FD2_SIGNAL_FLY; + } else { + BossFd2_SetupEmerge(this, globalCtx); + } + } + } +} + +void BossFd2_SetupBreatheFire(BossFd2* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &gHoleVolvagiaBreatheFireAnim, -5.0f); + this->actionFunc = BossFd2_BreatheFire; + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaBreatheFireAnim); + this->work[FD2_ACTION_STATE] = 0; +} + +static Vec3f sUnkVec = { 0.0f, 0.0f, 50.0f }; // Unused? BossFd uses a similar array for its fire breath sfx. + +void BossFd2_BreatheFire(BossFd2* this, GlobalContext* globalCtx) { + s16 i; + Vec3f toLink; + s16 angleX; + s16 angleY; + s16 breathOpacity = 0; + BossFd* bossFd = (BossFd*)this->actor.parent; + Player* player = GET_PLAYER(globalCtx); + f32 tempX; + f32 tempY; + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + BossFd2_SetupBurrow(this, globalCtx); + } + if ((25.0f <= this->skelAnime.curFrame) && (this->skelAnime.curFrame < 70.0f)) { + if (this->skelAnime.curFrame == 25.0f) { + globalCtx->envCtx.unk_D8 = 0.0f; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_FIRE - SFX_FLAG); + if (this->skelAnime.curFrame > 50) { + breathOpacity = (70.0f - this->skelAnime.curFrame) * 12.0f; + } else { + breathOpacity = 255; + } + toLink.x = player->actor.world.pos.x - this->headPos.x; + toLink.y = player->actor.world.pos.y - this->headPos.y; + toLink.z = player->actor.world.pos.z - this->headPos.z; + angleY = Math_Atan2S(toLink.z, toLink.x); + angleX = -Math_Atan2S(sqrtf(SQ(toLink.x) + SQ(toLink.z)), toLink.y); + angleY -= this->actor.shape.rot.y; + if (angleY > 0x1F40) { + angleY = 0x1F40; + } + if (angleY < -0x1F40) { + angleY = -0x1F40; + } + angleX += (-0x1B58); + if (angleX > 0x3E8) { + angleX = 0x3E8; + } + if (angleX < -0xFA0) { + angleX = -0xFA0; + } + Math_ApproachS(&this->headRot.y, angleY, 5, 0x7D0); + Math_ApproachS(&this->headRot.x, angleX, 5, 0x7D0); + } else { + Math_ApproachS(&this->headRot.y, 0, 5, 0x7D0); + Math_ApproachS(&this->headRot.x, 0, 5, 0x7D0); + } + if (breathOpacity != 0) { + f32 breathScale; + Vec3f spawnSpeed = { 0.0f, 0.0f, 0.0f }; + Vec3f spawnVel; + Vec3f spawnAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f spawnPos; + + bossFd->fogMode = 2; + spawnSpeed.z = 30.0f; + spawnPos = this->headPos; + + tempY = ((this->actor.shape.rot.y + this->headRot.y) / (f32)0x8000) * M_PI; + tempX = ((this->headRot.x / (f32)0x8000) * M_PI) + 1.0f / 2; + Matrix_RotateY(tempY, MTXMODE_NEW); + Matrix_RotateX(tempX, MTXMODE_APPLY); + Matrix_MultVec3f(&spawnSpeed, &spawnVel); + + breathScale = 300.0f + 50.0f * Math_SinS(this->work[FD2_VAR_TIMER] * 0x2000); + BossFd2_SpawnFireBreath(globalCtx, bossFd->effects, &spawnPos, &spawnVel, &spawnAccel, breathScale, + breathOpacity, this->actor.shape.rot.y + this->headRot.y); + + spawnPos.x += spawnVel.x * 0.5f; + spawnPos.y += spawnVel.y * 0.5f; + spawnPos.z += spawnVel.z * 0.5f; + + breathScale = 300.0f + 50.0f * Math_SinS(this->work[FD2_VAR_TIMER] * 0x2000); + BossFd2_SpawnFireBreath(globalCtx, bossFd->effects, &spawnPos, &spawnVel, &spawnAccel, breathScale, + breathOpacity, this->actor.shape.rot.y + this->headRot.y); + + spawnSpeed.x = 0.0f; + spawnSpeed.y = 17.0f; + spawnSpeed.z = 0.0f; + + for (i = 0; i < 6; i++) { + tempY = Rand_ZeroFloat(2.0f * M_PI); + tempX = Rand_ZeroFloat(2.0f * M_PI); + Matrix_RotateY(tempY, MTXMODE_NEW); + Matrix_RotateX(tempX, MTXMODE_APPLY); + Matrix_MultVec3f(&spawnSpeed, &spawnVel); + + spawnAccel.x = (spawnVel.x * -10.0f) / 100.0f; + spawnAccel.y = (spawnVel.y * -10.0f) / 100.0f; + spawnAccel.z = (spawnVel.z * -10.0f) / 100.0f; + + BossFd2_SpawnEmber(globalCtx, bossFd->effects, &this->headPos, &spawnVel, &spawnAccel, + (s16)Rand_ZeroFloat(2.0f) + 8); + } + } +} + +void BossFd2_SetupClawSwipe(BossFd2* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &gHoleVolvagiaClawSwipeAnim, -5.0f); + this->actionFunc = BossFd2_ClawSwipe; + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaClawSwipeAnim); +} + +void BossFd2_ClawSwipe(BossFd2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_ROAR); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_SW_NAIL); + } + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + BossFd2_SetupBurrow(this, globalCtx); + } +} + +void BossFd2_SetupVulnerable(BossFd2* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaKnockoutAnim); + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaKnockoutAnim); + this->actionFunc = BossFd2_Vulnerable; + this->work[FD2_ACTION_STATE] = 0; +} + +void BossFd2_Vulnerable(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + s16 i; + + this->disableAT = true; + this->actor.flags |= ACTOR_FLAG_10; + SkelAnime_Update(&this->skelAnime); + switch (this->work[FD2_ACTION_STATE]) { + case 0: + if (Animation_OnFrame(&this->skelAnime, 13.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_MAHI2); + } + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME] - 3.0f)) { + for (i = 0; i < 25; i++) { + Vec3f spawnVel; + Vec3f spawnAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f spawnPos; + + spawnVel.x = Rand_CenteredFloat(8.0f); + spawnVel.y = Rand_ZeroFloat(1.0f); + spawnVel.z = Rand_CenteredFloat(8.0f); + + spawnAccel.y = 0.5f; + + spawnPos.x = Rand_CenteredFloat(10.0f) + this->actor.focus.pos.x; + spawnPos.y = Rand_CenteredFloat(10.0f) + this->actor.focus.pos.y; + spawnPos.z = Rand_CenteredFloat(10.0f) + this->actor.focus.pos.z; + + BossFd2_SpawnDust(bossFd->effects, &spawnPos, &spawnVel, &spawnAccel, + Rand_ZeroFloat(100.0f) + 300.0f); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_LAND); + } + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + Animation_MorphToLoop(&this->skelAnime, &gHoleVolvagiaVulnerableAnim, -5.0f); + this->work[FD2_ACTION_STATE] = 1; + this->timers[0] = 60; + } + break; + case 1: + if ((this->work[FD2_VAR_TIMER] & 0xF) == 0xF) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_KNOCKOUT); + } + if (this->timers[0] == 0) { + BossFd2_SetupBurrow(this, globalCtx); + } + break; + } +} + +void BossFd2_SetupDamaged(BossFd2* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaHitAnim); + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaHitAnim); + this->actionFunc = BossFd2_Damaged; + this->work[FD2_ACTION_STATE] = 0; +} + +void BossFd2_Damaged(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + + SkelAnime_Update(&this->skelAnime); + this->disableAT = true; + if (this->work[FD2_ACTION_STATE] == 0) { + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + Animation_PlayOnce(&this->skelAnime, &gHoleVolvagiaDamagedAnim); + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaDamagedAnim); + this->work[FD2_ACTION_STATE] = 1; + } + } else if (this->work[FD2_ACTION_STATE] == 1) { + if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DAMAGE2); + } + if (Animation_OnFrame(&this->skelAnime, 20.0f)) { + bossFd->timers[4] = 30; + } + if (Animation_OnFrame(&this->skelAnime, this->fwork[FD2_END_FRAME])) { + this->work[FD2_ACTION_STATE] = 2; + this->timers[0] = 25; + } + } else { + Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 10.0f); + if (this->timers[0] == 0) { + this->actionFunc = BossFd2_Wait; + bossFd->handoffSignal = FD2_SIGNAL_FLY; + } + } +} + +void BossFd2_SetupDeath(BossFd2* this, GlobalContext* globalCtx) { + this->fwork[FD2_END_FRAME] = Animation_GetLastFrame(&gHoleVolvagiaDamagedAnim); + Animation_Change(&this->skelAnime, &gHoleVolvagiaDamagedAnim, 1.0f, 0.0f, this->fwork[FD2_END_FRAME], + ANIMMODE_ONCE_INTERP, -3.0f); + this->actionFunc = BossFd2_Death; + this->actor.flags &= ~ACTOR_FLAG_0; + this->deathState = DEATH_START; +} + +void BossFd2_UpdateCamera(BossFd2* this, GlobalContext* globalCtx) { + if (this->deathCamera != SUBCAM_FREE) { + Math_ApproachF(&this->camData.eye.x, this->camData.nextEye.x, this->camData.eyeMaxVel.x, + this->camData.eyeVel.x * this->camData.speedMod); + Math_ApproachF(&this->camData.eye.y, this->camData.nextEye.y, this->camData.eyeMaxVel.y, + this->camData.eyeVel.y * this->camData.speedMod); + Math_ApproachF(&this->camData.eye.z, this->camData.nextEye.z, this->camData.eyeMaxVel.z, + this->camData.eyeVel.z * this->camData.speedMod); + Math_ApproachF(&this->camData.at.x, this->camData.nextAt.x, this->camData.atMaxVel.x, + this->camData.atVel.x * this->camData.speedMod); + Math_ApproachF(&this->camData.at.y, this->camData.nextAt.y, this->camData.atMaxVel.y, + this->camData.atVel.y * this->camData.speedMod); + Math_ApproachF(&this->camData.at.z, this->camData.nextAt.z, this->camData.atMaxVel.z, + this->camData.atVel.z * this->camData.speedMod); + Math_ApproachF(&this->camData.speedMod, 1.0f, 1.0f, this->camData.accel); + this->camData.at.y += this->camData.yMod; + Gameplay_CameraSetAtEye(globalCtx, this->deathCamera, &this->camData.at, &this->camData.eye); + Math_ApproachF(&this->camData.yMod, 0.0f, 1.0f, 0.1f); + } +} + +void BossFd2_Death(BossFd2* this, GlobalContext* globalCtx) { + f32 retreatSpeed; + Vec3f sp70; + Vec3f sp64; + BossFd* bossFd = (BossFd*)this->actor.parent; + Camera* mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + f32 pad3; + f32 pad2; + f32 pad1; + f32 cameraShake; + SkelAnime* skelAnime = &this->skelAnime; + + SkelAnime_Update(skelAnime); + switch (this->deathState) { + case DEATH_START: + this->deathState = DEATH_RETREAT; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 1); + this->deathCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->deathCamera, CAM_STAT_ACTIVE); + this->camData.eye = mainCam->eye; + this->camData.at = mainCam->at; + this->camData.eyeVel.x = 100.0f; + this->camData.eyeVel.y = 100.0f; + this->camData.eyeVel.z = 100.0f; + this->camData.atVel.x = 100.0f; + this->camData.atVel.y = 100.0f; + this->camData.atVel.z = 100.0f; + this->camData.accel = 0.02f; + this->timers[0] = 0; + this->work[FD2_HOLE_COUNTER] = 0; + this->camData.eyeMaxVel.x = 0.1f; + this->camData.eyeMaxVel.y = 0.1f; + this->camData.eyeMaxVel.z = 0.1f; + this->camData.atMaxVel.x = 0.1f; + this->camData.atMaxVel.y = 0.1f; + this->camData.atMaxVel.z = 0.1f; + case DEATH_RETREAT: + this->work[FD2_HOLE_COUNTER]++; + if (this->work[FD2_HOLE_COUNTER] < 15) { + retreatSpeed = 1.0f; + } else if (this->work[FD2_HOLE_COUNTER] < 20) { + retreatSpeed = 0.5f; + } else { + retreatSpeed = 0.25f; + } + if ((this->work[FD2_HOLE_COUNTER] == 1) || (this->work[FD2_HOLE_COUNTER] == 40)) { + this->work[FD2_SCREAM_TIMER] = 20; + if (this->work[FD2_HOLE_COUNTER] == 40) { + Audio_StopSfxById(NA_SE_EN_VALVAISA_DEAD); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DAMAGE2); + } + Math_ApproachF(&this->skelAnime.playSpeed, retreatSpeed, 1.0f, 1.0f); + Matrix_RotateY(((this->actor.yawTowardsPlayer / (f32)0x8000) * M_PI) + 0.2f, MTXMODE_NEW); + sp70.x = 0.0f; + sp70.y = 0.0f; + sp70.z = 250.0f; + Matrix_MultVec3f(&sp70, &sp64); + this->camData.nextEye.x = this->actor.world.pos.x + sp64.x; + this->camData.nextEye.y = 140.0f; + this->camData.nextEye.z = this->actor.world.pos.z + sp64.z; + if (this->actor.focus.pos.y >= 90.0f) { + this->camData.nextAt.y = this->actor.focus.pos.y; + this->camData.nextAt.x = this->actor.focus.pos.x; + this->camData.nextAt.z = this->actor.focus.pos.z; + } + if (this->timers[0] == 0) { + if (Animation_OnFrame(skelAnime, 20.0f)) { + bossFd->timers[4] = 60; + } + if (this->work[FD2_HOLE_COUNTER] >= 100) { + this->deathState = DEATH_HANDOFF; + this->timers[0] = 50; + } + } else if (Animation_OnFrame(skelAnime, 15.0f)) { + Animation_MorphToPlayOnce(skelAnime, &gHoleVolvagiaDamagedAnim, -10.0f); + } + break; + case DEATH_HANDOFF: + if (this->timers[0] == 0) { + this->actor.draw = NULL; + this->deathState = DEATH_FD_BODY; + bossFd->handoffSignal = FD2_SIGNAL_DEATH; + this->work[FD2_ACTION_STATE] = 0; + this->camData.speedMod = 0.0f; + } else { + Math_ApproachF(&this->actor.world.pos.y, -100.0f, 1.0f, 5.0f); + } + break; + case DEATH_FD_BODY: + if (bossFd->actor.world.pos.y < 80.0f) { + if (bossFd->actor.world.rot.x > 0x3000) { + this->camData.nextAt = bossFd->actor.world.pos; + this->camData.nextAt.y = 80.0f; + this->camData.nextEye.x = bossFd->actor.world.pos.x; + this->camData.nextEye.y = 150.0f; + this->camData.nextEye.z = bossFd->actor.world.pos.z + 300.0f; + } + } else { + this->camData.nextAt = bossFd->actor.world.pos; + this->camData.nextEye.x = this->actor.world.pos.x; + Math_ApproachF(&this->camData.nextEye.y, 200.0f, 1.0f, 2.0f); + Math_ApproachF(&this->camData.nextEye.z, bossFd->actor.world.pos.z + 200.0f, 1.0f, 3.0f); + if (this->work[FD2_ACTION_STATE] == 0) { + this->work[FD2_ACTION_STATE]++; + this->camData.speedMod = 0.0f; + this->camData.accel = 0.02f; + func_8002DF54(globalCtx, &bossFd->actor, 1); + } + } + if ((bossFd->work[BFD_ACTION_STATE] == BOSSFD_BONES_FALL) && (bossFd->timers[0] == 5)) { + this->deathState = DEATH_FD_SKULL; + this->camData.speedMod = 0.0f; + this->camData.accel = 0.02f; + this->camData.nextEye.y = 150.0f; + this->camData.nextEye.z = bossFd->actor.world.pos.z + 300.0f; + } + break; + case DEATH_FD_SKULL: + Math_ApproachF(&this->camData.nextAt.y, 100.0, 1.0f, 100.0f); + this->camData.nextAt.x = 0.0f; + this->camData.nextAt.z = 0.0f; + this->camData.nextEye.x = 0.0f; + this->camData.nextEye.y = 140.0f; + Math_ApproachF(&this->camData.nextEye.z, 220.0f, 0.5f, 1.15f); + if (bossFd->work[BFD_CAM_SHAKE_TIMER] != 0) { + bossFd->work[BFD_CAM_SHAKE_TIMER]--; + cameraShake = bossFd->work[BFD_CAM_SHAKE_TIMER] / 0.5f; + if (cameraShake >= 20.0f) { + cameraShake = 20.0f; + } + this->camData.yMod = (bossFd->work[BFD_CAM_SHAKE_TIMER] & 1) ? cameraShake : -cameraShake; + } + if (bossFd->work[BFD_ACTION_STATE] == BOSSFD_SKULL_BURN) { + this->deathState = DEATH_FINISH; + mainCam->eye = this->camData.eye; + mainCam->eyeNext = this->camData.eye; + mainCam->at = this->camData.at; + func_800C08AC(globalCtx, this->deathCamera, 0); + this->deathCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 0.0f, 100.0f, 0.0f, + 0, 0, 0, WARP_DUNGEON_ADULT); + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + break; + case DEATH_FINISH: + break; + } + BossFd2_UpdateCamera(this, globalCtx); +} + +void BossFd2_Wait(BossFd2* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + + if (bossFd->handoffSignal == FD2_SIGNAL_GROUND) { + bossFd->handoffSignal = FD2_SIGNAL_NONE; + BossFd2_SetupEmerge(this, globalCtx); + this->timers[0] = 20; + this->work[FD2_HOLE_COUNTER] = 0; + } +} + +void BossFd2_CollisionCheck(BossFd2* this, GlobalContext* globalCtx) { + s16 i; + ColliderInfo* hurtbox; + BossFd* bossFd = (BossFd*)this->actor.parent; + + if (this->actionFunc == BossFd2_ClawSwipe) { + Player* player = GET_PLAYER(globalCtx); + + for (i = 0; i < ARRAY_COUNT(this->elements); i++) { + if (this->collider.elements[i].info.toucherFlags & TOUCH_HIT) { + this->collider.elements[i].info.toucherFlags &= ~TOUCH_HIT; + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + } + if (!bossFd->faceExposed) { + this->collider.elements[0].info.elemType = ELEMTYPE_UNK2; + this->collider.base.colType = COLTYPE_METAL; + } else { + this->collider.elements[0].info.elemType = ELEMTYPE_UNK3; + this->collider.base.colType = COLTYPE_HIT3; + } + + if (this->collider.elements[0].info.bumperFlags & BUMP_HIT) { + this->collider.elements[0].info.bumperFlags &= ~BUMP_HIT; + + hurtbox = this->collider.elements[0].info.acHitInfo; + if (!bossFd->faceExposed) { + if (hurtbox->toucher.dmgFlags & 0x40000040) { + bossFd->actor.colChkInfo.health -= 2; + if ((s8)bossFd->actor.colChkInfo.health <= 2) { + bossFd->actor.colChkInfo.health = 1; + } + bossFd->faceExposed = true; + BossFd2_SetupVulnerable(this, globalCtx); + this->work[FD2_INVINC_TIMER] = 30; + this->work[FD2_DAMAGE_FLASH_TIMER] = 5; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_MAHI1); + for (i = 0; i < 30; i++) { + Vec3f debrisVel = { 0.0f, 0.0f, 0.0f }; + Vec3f debrisAccel = { 0.0f, -1.0f, 0.0f }; + Vec3f debrisPos; + + debrisVel.x = Rand_CenteredFloat(10.0f); + debrisVel.y = Rand_ZeroFloat(5.0f) + 8.0f; + debrisVel.z = Rand_CenteredFloat(10.0f); + + debrisPos.x = this->actor.focus.pos.x; + debrisPos.y = this->actor.focus.pos.y; + debrisPos.z = this->actor.focus.pos.z; + + BossFd2_SpawnDebris(globalCtx, bossFd->effects, &debrisPos, &debrisVel, &debrisAccel, + (s16)Rand_ZeroFloat(10.0) + 10); + } + } + } else { + u8 canKill = false; + u8 damage; + + if ((damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags)) == 0) { + damage = (hurtbox->toucher.dmgFlags & 0x00001000) ? 4 : 2; + } else { + canKill = true; + } + if (hurtbox->toucher.dmgFlags & 0x80) { + damage = 0; + } + if (((s8)bossFd->actor.colChkInfo.health > 2) || canKill) { + bossFd->actor.colChkInfo.health -= damage; + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("damage %d\n", damage); + } + osSyncPrintf(VT_RST); + osSyncPrintf("hp %d\n", bossFd->actor.colChkInfo.health); + + if ((s8)bossFd->actor.colChkInfo.health <= 0) { + bossFd->actor.colChkInfo.health = 0; + BossFd2_SetupDeath(this, globalCtx); + this->work[FD2_DAMAGE_FLASH_TIMER] = 10; + this->work[FD2_INVINC_TIMER] = 30000; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else if (damage) { + BossFd2_SetupDamaged(this, globalCtx); + this->work[FD2_DAMAGE_FLASH_TIMER] = 10; + this->work[FD2_INVINC_TIMER] = 100; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DAMAGE1); + } + if (damage) { + for (i = 0; i < 30; i++) { + Vec3f pieceVel = { 0.0f, 0.0f, 0.0f }; + Vec3f pieceAccel = { 0.0f, -1.0f, 0.0f }; + Vec3f piecePos; + + pieceVel.x = Rand_CenteredFloat(6.0f); + pieceVel.y = Rand_ZeroFloat(4.0f) + 6.0f; + pieceVel.z = Rand_CenteredFloat(6.0f); + + piecePos.x = this->actor.focus.pos.x; + piecePos.y = this->actor.focus.pos.y; + piecePos.z = this->actor.focus.pos.z; + + BossFd2_SpawnSkullPiece(globalCtx, bossFd->effects, &piecePos, &pieceVel, &pieceAccel, + (s16)Rand_ZeroFloat(6.0f) + 10); + } + } + } + } +} + +void BossFd2_UpdateFace(BossFd2* this, GlobalContext* globalCtx) { + f32 maxOpen; + f32 openRate; + s16 eyeStates[5] = { EYE_OPEN, EYE_HALF, EYE_CLOSED, EYE_CLOSED, EYE_HALF }; + + if (((this->work[FD2_VAR_TIMER] % 8) == 0) && (Rand_ZeroOne() < 0.3f)) { + this->work[FD2_BLINK_TIMER] = 4; + } + if ((this->actionFunc == BossFd2_Vulnerable) || (this->actionFunc == BossFd2_Damaged)) { + if (this->work[FD2_VAR_TIMER] & 0x10) { + this->eyeState = EYE_HALF; + } else { + this->eyeState = EYE_CLOSED; + } + } else { + this->eyeState = eyeStates[this->work[FD2_BLINK_TIMER]]; + } + + if (this->work[FD2_BLINK_TIMER] != 0) { + this->work[FD2_BLINK_TIMER]--; + } + + if (this->work[FD2_SCREAM_TIMER] != 0) { + maxOpen = 6000.0f; + openRate = 1300.0f; + } else { + maxOpen = (this->work[FD2_VAR_TIMER] & 0x10) ? 1000.0f : 0.0f; + openRate = 700.0f; + } + Math_ApproachF(&this->jawOpening, maxOpen, 0.3f, openRate); + + if (this->work[FD2_SCREAM_TIMER] != 0) { + this->work[FD2_SCREAM_TIMER]--; + } +} + +void BossFd2_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossFd2* this = (BossFd2*)thisx; + s16 i; + + osSyncPrintf("FD2 move start \n"); + this->disableAT = false; + this->actor.flags &= ~ACTOR_FLAG_10; + this->work[FD2_VAR_TIMER]++; + this->work[FD2_UNK_TIMER]++; + + this->actionFunc(this, globalCtx); + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + if (this->work[FD2_DAMAGE_FLASH_TIMER] != 0) { + this->work[FD2_DAMAGE_FLASH_TIMER]--; + } + if (this->work[FD2_INVINC_TIMER] != 0) { + this->work[FD2_INVINC_TIMER]--; + } + + if (this->deathState == DEATH_START) { + if (this->work[FD2_INVINC_TIMER] == 0) { + BossFd2_CollisionCheck(this, globalCtx); + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (!this->disableAT) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + BossFd2_UpdateFace(this, globalCtx); + this->fwork[FD2_TEX1_SCROLL_X] += 4.0f; + this->fwork[FD2_TEX1_SCROLL_Y] = 120.0f; + this->fwork[FD2_TEX2_SCROLL_X] += 3.0f; + this->fwork[FD2_TEX2_SCROLL_Y] -= 2.0f; + if (this->actor.focus.pos.y < 90.0f) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + this->actor.flags |= ACTOR_FLAG_0; + } +} + +s32 BossFd2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossFd2* this = (BossFd2*)thisx; + BossFd* bossFd = (BossFd*)this->actor.parent; + + if (limbIndex == 31) { + rot->y -= (f32)this->headRot.y; + rot->z += (f32)this->headRot.x; + } + switch (limbIndex) { + case 35: + case 36: + rot->z -= this->jawOpening * 0.1f; + break; + case 32: + rot->z += this->jawOpening; + break; + } + if ((bossFd->faceExposed == 1) && (limbIndex == 35)) { + *dList = gHoleVolvagiaBrokenFaceDL; + } + + if ((limbIndex == 32) || (limbIndex == 35) || (limbIndex == 36)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2165); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, (s8)bossFd->fwork[BFD_HEAD_TEX2_ALPHA]); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2172); + } else { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2174); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, (s8)bossFd->fwork[BFD_BODY_TEX2_ALPHA]); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2181); + } + if ((0 < limbIndex) && (limbIndex < 16)) { + *dList = NULL; + } + return false; +} + +void BossFd2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f targetMod = { 4500.0f, 0.0f, 0.0f }; + static Vec3f headMod = { 4000.0f, 0.0f, 0.0f }; + static Vec3f centerManeMod = { 4000.0f, -2900.0, 2000.0f }; + static Vec3f rightManeMod = { 4000.0f, -1600.0, 0.0f }; + static Vec3f leftManeMod = { 4000.0f, -1600.0, -2000.0f }; + BossFd2* this = (BossFd2*)thisx; + + if (limbIndex == 35) { + Matrix_MultVec3f(&targetMod, &this->actor.focus.pos); + Matrix_MultVec3f(&headMod, &this->headPos); + Matrix_MultVec3f(¢erManeMod, &this->centerMane.head); + Matrix_MultVec3f(&rightManeMod, &this->rightMane.head); + Matrix_MultVec3f(&leftManeMod, &this->leftMane.head); + } + Collider_UpdateSpheres(limbIndex, &this->collider); +} + +void BossFd2_UpdateMane(BossFd2* this, GlobalContext* globalCtx, Vec3f* head, Vec3f* pos, Vec3f* rot, Vec3f* pull, + f32* scale) { + f32 sp138[10] = { 0.0f, 100.0f, 50.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + f32 sp110[10] = { 0.0f, 5.0f, -10.0f, 500.0f, 500.0f, 500.0f, 500.0f, 500.0f, 500.0f, 500.0f }; + f32 spE8[10] = { 0.4f, 0.6f, 0.8f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; + s16 i; + Vec3f temp_vec; + f32 temp_f2; + f32 phi_f0; + f32 temp_angleX; + f32 temp_angleY; + Vec3f spBC; + Vec3f spB0; + f32 xyScale; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2389); + Matrix_Push(); + gDPPipeSync(POLY_OPA_DISP++); + + for (i = 0; i < 10; i++) { + if (i == 0) { + (pos + i)->x = head->x; + (pos + i)->y = head->y; + (pos + i)->z = head->z; + } else { + Math_ApproachF(&(pull + i)->x, 0.0f, 1.0f, 1.0f); + Math_ApproachF(&(pull + i)->y, 0.0f, 1.0f, 1.0f); + Math_ApproachF(&(pull + i)->z, 0.0f, 1.0f, 1.0f); + } + } + + for (i = 1; i < 10; i++) { + temp_vec.x = (pos + i)->x + (pull + i)->x - (pos + i - 1)->x; + + phi_f0 = (pos + i)->y + (pull + i)->y - 2.0f + sp138[i]; + temp_f2 = (pos + i - 1)->y + sp110[i]; + if (phi_f0 > temp_f2) { + phi_f0 = temp_f2; + } + if ((head->y >= -910.0f) && (phi_f0 < 110.0f)) { + phi_f0 = 110.0f; + } + temp_vec.y = phi_f0 - (pos + i - 1)->y; + + temp_vec.z = (pos + i)->z + (pull + i)->z - (pos + i - 1)->z; + temp_angleY = Math_Atan2F(temp_vec.z, temp_vec.x); + temp_angleX = -Math_Atan2F(sqrtf(SQ(temp_vec.x) + SQ(temp_vec.z)), temp_vec.y); + (rot + i - 1)->y = temp_angleY; + (rot + i - 1)->x = temp_angleX; + spBC.x = 0.0f; + spBC.y = 0.0f; + spBC.z = spE8[i] * 25.0f; + Matrix_RotateY(temp_angleY, MTXMODE_NEW); + Matrix_RotateX(temp_angleX, MTXMODE_APPLY); + Matrix_MultVec3f(&spBC, &spB0); + temp_vec.x = (pos + i)->x; + temp_vec.y = (pos + i)->y; + temp_vec.z = (pos + i)->z; + (pos + i)->x = (pos + i - 1)->x + spB0.x; + (pos + i)->y = (pos + i - 1)->y + spB0.y; + (pos + i)->z = (pos + i - 1)->z + spB0.z; + (pull + i)->x = (((pos + i)->x - temp_vec.x) * 88.0f) / 100.0f; + (pull + i)->y = (((pos + i)->y - temp_vec.y) * 88.0f) / 100.0f; + (pull + i)->z = (((pos + i)->z - temp_vec.z) * 88.0f) / 100.0f; + if ((pull + i)->x > 30.0f) { + (pull + i)->x = 30.0f; + } + if ((pull + i)->x < -30.0f) { + (pull + i)->x = -30.0f; + } + if ((pull + i)->y > 30.0f) { + (pull + i)->y = 30.0f; + } + if ((pull + i)->y < -30.0f) { + (pull + i)->y = -30.0f; + } + if ((pull + i)->z > 30.0f) { + (pull + i)->z = 30.0f; + } + if ((pull + i)->z < -30.0f) { + (pull + i)->z = -30.0f; + } + } + + for (i = 0; i < 9; i++) { + Matrix_Translate((pos + i)->x, (pos + i)->y, (pos + i)->z, MTXMODE_NEW); + Matrix_RotateY((rot + i)->y, MTXMODE_APPLY); + Matrix_RotateX((rot + i)->x, MTXMODE_APPLY); + xyScale = (0.01f - (i * 0.0009f)) * spE8[i] * scale[i]; + Matrix_Scale(xyScale, xyScale, 0.01f * spE8[i], MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2498), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gHoleVolvagiaManeModelDL); + } + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2503); +} + +void BossFd2_DrawMane(BossFd2* this, GlobalContext* globalCtx) { + s32 pad; + BossFd* bossFd = (BossFd*)this->actor.parent; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2515); + if (1) {} + for (i = 0; i < 10; i++) { + this->centerMane.scale[i] = 1.5f + 0.3f * Math_SinS(5596.0f * this->work[FD2_VAR_TIMER] + i * 0x3200); + this->rightMane.scale[i] = 1.5f + 0.3f * Math_SinS(5496.0f * this->work[FD2_VAR_TIMER] + i * 0x3200); + this->leftMane.scale[i] = 1.5f + 0.3f * Math_CosS(5696.0f * this->work[FD2_VAR_TIMER] + i * 0x3200); + } + + func_80093D84(globalCtx->state.gfxCtx); + + gSPDisplayList(POLY_XLU_DISP++, gHoleVolvagiaManeMaterialDL); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, bossFd->fwork[BFD_MANE_COLOR_CENTER], 0, 255); + BossFd2_UpdateMane(this, globalCtx, &this->centerMane.head, this->centerMane.pos, this->centerMane.rot, + this->centerMane.pull, this->centerMane.scale); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, bossFd->fwork[BFD_MANE_COLOR_RIGHT], 0, 255); + BossFd2_UpdateMane(this, globalCtx, &this->rightMane.head, this->rightMane.pos, this->rightMane.rot, + this->rightMane.pull, this->rightMane.scale); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, bossFd->fwork[BFD_MANE_COLOR_LEFT], 0, 255); + BossFd2_UpdateMane(this, globalCtx, &this->leftMane.head, this->leftMane.pos, this->leftMane.rot, + this->leftMane.pull, this->leftMane.scale); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2601); +} + +void BossFd2_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gHoleVolvagiaEyeOpenTex, gHoleVolvagiaEyeHalfTex, gHoleVolvagiaEyeClosedTex }; + s32 pad; + BossFd2* this = (BossFd2*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2617); + osSyncPrintf("FD2 draw start \n"); + if (this->actionFunc != BossFd2_Wait) { + func_80093D18(globalCtx->state.gfxCtx); + if (this->work[FD2_DAMAGE_FLASH_TIMER] & 2) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099); + } + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeState])); + + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->fwork[FD2_TEX1_SCROLL_X], + (s16)this->fwork[FD2_TEX1_SCROLL_Y], 0x20, 0x20, 1, + (s16)this->fwork[FD2_TEX2_SCROLL_X], (s16)this->fwork[FD2_TEX2_SCROLL_Y], 0x20, + 0x20)); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 128); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossFd2_OverrideLimbDraw, BossFd2_PostLimbDraw, &this->actor); + BossFd2_DrawMane(this, globalCtx); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_fd2.c", 2688); +} diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h new file mode 100644 index 000000000..afafdbba3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.h @@ -0,0 +1,92 @@ +#ifndef Z_BOSS_FD2_H +#define Z_BOSS_FD2_H + +#include "ultra64.h" +#include "global.h" + +struct BossFd2; + +typedef void (*BossFd2ActionFunc)(struct BossFd2*, GlobalContext*); + +typedef enum { + /* 0 */ FD2_SIGNAL_NONE, + /* 1 */ FD2_SIGNAL_FLY, + /* 2 */ FD2_SIGNAL_DEATH, + /* 100 */ FD2_SIGNAL_GROUND = 100 +} BossFd2Signal; + +typedef struct { + /* 0x000 */ Vec3f rot[10]; + /* 0x078 */ Vec3f pos[10]; + /* 0x0F0 */ Vec3f pull[10]; + /* 0x168 */ f32 scale[10]; + /* 0x190 */ Vec3f head; +} BossFd2Mane; // size = 0x19C + +typedef struct { + /* 0x00 */ Vec3f eye; + /* 0x0C */ Vec3f at; + /* 0x18 */ Vec3f pad[2]; + /* 0x30 */ Vec3f eyeVel; + /* 0x3C */ Vec3f atVel; + /* 0x48 */ Vec3f nextEye; + /* 0x54 */ Vec3f eyeMaxVel; + /* 0x60 */ Vec3f nextAt; + /* 0x6C */ Vec3f atMaxVel; + /* 0x78 */ f32 speedMod; + /* 0x7C */ f32 accel; + /* 0x80 */ f32 yMod; + /* 0x84 */ f32 shake; +} BossFd2Cam; // size = 0x88 + +typedef enum { + /* 0 */ FD2_TURN_TO_LINK, + /* 1 */ FD2_ACTION_STATE, + /* 2 */ FD2_UNK_TIMER, + /* 3 */ FD2_VAR_TIMER, + /* 4 */ FD2_UNUSED_4, + /* 5 */ FD2_UNUSED_5, + /* 6 */ FD2_BLINK_TIMER, + /* 7 */ FD2_SCREAM_TIMER, + /* 8 */ FD2_DAMAGE_FLASH_TIMER, + /* 9 */ FD2_HOLE_COUNTER, + /* 10 */ FD2_INVINC_TIMER, + /* 11 */ FD2_FAKEOUT_COUNT, + /* 19 */ FD2_SHORT_COUNT = 19 +} BossFd2S16Var; + +typedef enum { + /* 0 */ FD2_TEX1_SCROLL_X, + /* 1 */ FD2_TEX1_SCROLL_Y, + /* 2 */ FD2_TEX2_SCROLL_X, + /* 3 */ FD2_TEX2_SCROLL_Y, + /* 10 */ FD2_END_FRAME = 10, + /* 18 */ FD2_FLOAT_COUNT = 18 +} BossFd2F32Var; + +typedef struct BossFd2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ BossFd2ActionFunc actionFunc; + /* 0x0194 */ u8 disableAT; + /* 0x0196 */ s16 work[FD2_SHORT_COUNT]; + /* 0x01BC */ s16 timers[5]; + /* 0x01C8 */ f32 fwork[FD2_FLOAT_COUNT]; + /* 0x0210 */ Vec3f headPos; + /* 0x021C */ Vec3s headRot; + /* 0x0222 */ char unk_222; + /* 0x0223 */ u8 eyeState; + /* 0x0224 */ char unk_224[0xC90]; // despite its size, seems to be unused. + /* 0x0EB4 */ BossFd2Mane centerMane; + /* 0x1050 */ BossFd2Mane rightMane; + /* 0x11EC */ BossFd2Mane leftMane; + /* 0x1388 */ char unk_1388[4]; + /* 0x138C */ f32 jawOpening; + /* 0x1390 */ s16 deathState; + /* 0x1392 */ s16 deathCamera; + /* 0x1394 */ BossFd2Cam camData; + /* 0x141C */ ColliderJntSph collider; + /* 0x143C */ ColliderJntSphElement elements[9]; +} BossFd2; // size = 0x167C + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2_colchk.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2_colchk.c new file mode 100644 index 000000000..f888dc486 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2_colchk.c @@ -0,0 +1,116 @@ +#include "global.h" + +static ColliderJntSphElementInit sJntSphElementsInit[9] = { + { + { + ELEMTYPE_UNK3, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 35, { { 6000, 0, 0 }, 21 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 18, { { 4000, 0, 0 }, 13 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 19, { { 3000, 0, 0 }, 13 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 20, { { 4000, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 24, { { 4000, 0, 0 }, 13 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 25, { { 3000, 0, 0 }, 13 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 26, { { 3500, 1500, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 17, { { 0, 0, 0 }, 26 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 30, { { 0, 0, 0 }, 17 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 9, + sJntSphElementsInit, +}; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c new file mode 100644 index 000000000..c5c19f864 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -0,0 +1,5047 @@ +#include "z_boss_ganon.h" +#include "overlays/ovl_Boss_Ganon/ovl_Boss_Ganon.h" +#include "overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.h" +#include "overlays/actors/ovl_En_Zl3/z_en_zl3.h" +#include "overlays/actors/ovl_Bg_Ganon_Otyuka/z_bg_ganon_otyuka.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "assets/objects/object_ganon/object_ganon.h" +#include "assets/objects/object_ganon_anime1/object_ganon_anime1.h" +#include "assets/objects/object_ganon_anime2/object_ganon_anime2.h" +#include "assets/scenes/dungeons/ganon_boss/ganon_boss_scene.h" + +#include + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BossGanon_Init(Actor* thisx, GlobalContext* globalCtx); +void BossGanon_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossGanon_Update(Actor* thisx, GlobalContext* globalCtx); +void BossGanon_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_808E1EB4(Actor* thisx, GlobalContext* globalCtx); // update +void func_808E2544(Actor* thisx, GlobalContext* globalCtx); // update +void BossGanon_LightBall_Update(Actor* thisx, GlobalContext* globalCtx); +void func_808E229C(Actor* thisx, GlobalContext* globalCtx); // draw +void func_808E324C(Actor* thisx, GlobalContext* globalCtx); // draw +void BossGanon_LightBall_Draw(Actor* thisx, GlobalContext* globalCtx); +void BossGanon_Reset(void); + + +void BossGanon_SetupIntroCutscene(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_SetupTowerCutscene(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_IntroCutscene(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_DeathAndTowerCutscene(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_Wait(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_ChargeLightBall(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_PlayTennis(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_PoundFloor(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_ChargeBigMagic(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_Block(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_HitByLightBall(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_Vulnerable(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_Damaged(BossGanon* this, GlobalContext* globalCtx); + +void BossGanon_SetupWait(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_SetupChargeLightBall(BossGanon* this, GlobalContext* globalCtx); +void BossGanon_SetupPlayTennis(BossGanon* this, GlobalContext* globalCtx); + +void BossGanon_DrawEffects(GlobalContext* globalCtx); +void BossGanon_UpdateEffects(GlobalContext* globalCtx); + +s32 BossGanon_CheckFallingPlatforms(BossGanon* this, GlobalContext* globalCtx, Vec3f* checkPos); + +const ActorInit Boss_Ganon_InitVars = { + ACTOR_BOSS_GANON, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GANON, + sizeof(BossGanon), + (ActorFunc)BossGanon_Init, + (ActorFunc)BossGanon_Destroy, + (ActorFunc)BossGanon_Update, + (ActorFunc)BossGanon_Draw, + (ActorResetFunc)BossGanon_Reset, +}; + +static ColliderCylinderInit sDorfCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 80, -50, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sLightBallCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK6, + { 0x00100700, 0x00, 0x08 }, + { 0x0D900740, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, 30, -15, { 0, 0, 0 } }, +}; + +static u8 D_808E4C58[] = { 0, 12, 10, 12, 14, 16, 12, 14, 16, 12, 14, 16, 12, 14, 16, 10, 16, 14 }; +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +static EnGanonMant* sCape; + +static s32 sSeed1; +static s32 sSeed2; +static s32 sSeed3; + +static BossGanon* sGanondorf; + +static EnZl3* sZelda; + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 timer; + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ Color_RGB8 color; + /* 0x2C */ s16 alpha; + /* 0x2E */ s16 unk_2E; + /* 0x30 */ s16 unk_30; + /* 0x34 */ f32 scale; + /* 0x38 */ f32 unk_38; // scale target mostly, but used for other things + /* 0x3C */ f32 unk_3C; // mostly z rot + /* 0x40 */ f32 unk_40; + /* 0x44 */ f32 unk_44; // mostly x rot + /* 0x48 */ f32 unk_48; // mostly y rot +} GanondorfEffect; // size = 0x4C + +GanondorfEffect sEffectBuf[200]; + +void BossGanonEff_SpawnWindowShard(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, f32 scale) { + static Color_RGB8 shardColors[] = { { 255, 175, 85 }, { 155, 205, 155 }, { 155, 125, 55 } }; + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + Color_RGB8* color; + + for (i = 0; i < 200; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_WINDOW_SHARD; + eff->pos = *pos; + eff->velocity = *velocity; + eff->accel = sZeroVec; + eff->scale = scale; + eff->accel.y = -1.5f; + eff->unk_44 = Rand_ZeroFloat(6.28f); + eff->unk_48 = Rand_ZeroFloat(6.28f); + color = &shardColors[(s16)Rand_ZeroFloat(2.99f)]; + eff->color.r = color->r; + eff->color.g = color->g; + eff->color.b = color->b; + eff->timer = (s16)Rand_ZeroFloat(20.0f); + break; + } + } +} + +void BossGanonEff_SpawnSparkle(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale, + s16 arg6) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_SPARKLE; + eff->pos = *pos; + eff->velocity = *velocity; + eff->accel = *accel; + eff->scale = scale / 1000.0f; + eff->unk_2E = (s16)Rand_ZeroFloat(100.0f) + 0xC8; + eff->unk_30 = arg6; + eff->timer = (s16)Rand_ZeroFloat(10.0f); + break; + } + } +} + +void BossGanonEff_SpawnLightRay(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale, + f32 arg5, s16 arg6) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_LIGHT_RAY; + eff->pos = *pos; + eff->velocity = *velocity; + eff->accel = *accel; + eff->scale = scale / 1000.0f; + eff->unk_38 = 1.0f; + eff->unk_40 = arg5; + eff->unk_2E = (s16)Rand_ZeroFloat(100.0f) + 0xC8; + eff->unk_30 = arg6; + eff->timer = (s16)Rand_ZeroFloat(10.0f); + eff->unk_48 = Math_Atan2F(eff->velocity.z, eff->velocity.x); + eff->unk_44 = -Math_Atan2F(sqrtf(SQXZ(eff->velocity)), eff->velocity.y); + break; + } + } +} + +void BossGanonEff_SpawnShock(GlobalContext* globalCtx, f32 scale, s16 shockType) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 75; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_SHOCK; + eff->pos = sZeroVec; + eff->pos.y = -2000.0f; + eff->velocity = sZeroVec; + eff->accel = sZeroVec; + eff->scale = scale / 1000.0f; + eff->unk_2E = shockType; + eff->timer = 0; + break; + } + } +} + +void BossGanonEff_SpawnLightning(GlobalContext* globalCtx, f32 scale, f32 arg2, f32 arg3) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_LIGHTNING; + eff->velocity = sZeroVec; + eff->accel = sZeroVec; + eff->unk_2E = 0; + eff->scale = scale; + eff->unk_48 = arg2; + eff->unk_3C = arg3; + eff->timer = 0; + break; + } + } +} + +void BossGanonEff_SpawnDustDark(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_IMPACT_DUST_DARK; + eff->pos = *pos; + eff->velocity = sZeroVec; + eff->accel = sZeroVec; + eff->scale = scale; + eff->unk_40 = 1.0f; + eff->unk_38 = arg3; + eff->unk_30 = (s16)Rand_ZeroFloat(100.0f); + eff->unk_2E = eff->timer = eff->alpha = 0; + break; + } + } +} + +void BossGanonEff_SpawnDustLight(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3, s16 bufIndex) { + GanondorfEffect* effArr = globalCtx->specialEffects; + + effArr[bufIndex].type = GDF_EFF_IMPACT_DUST_LIGHT; + effArr[bufIndex].pos = *pos; + effArr[bufIndex].velocity = sZeroVec; + effArr[bufIndex].accel = sZeroVec; + effArr[bufIndex].unk_40 = 1.0f; + effArr[bufIndex].scale = scale; + effArr[bufIndex].unk_38 = arg3; + effArr[bufIndex].unk_30 = Rand_ZeroFloat(100.0f); + effArr[bufIndex].unk_2E = effArr[bufIndex].timer = effArr[bufIndex].alpha = 0; +} + +void BossGanonEff_SpawnShockwave(GlobalContext* globalCtx, Vec3f* pos, f32 scale, f32 arg3) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_SHOCKWAVE; + eff->pos = *pos; + eff->velocity = sZeroVec; + eff->accel = sZeroVec; + eff->alpha = 255; + eff->unk_40 = 0.6f; + eff->scale = scale; + eff->unk_38 = arg3; + eff->unk_30 = (s16)Rand_ZeroFloat(100.0f); + eff->unk_2E = eff->timer = 0; + break; + } + } +} + +void BossGanonEff_SpawnBlackDot(GlobalContext* globalCtx, Vec3f* pos, f32 scale) { + s16 i; + GanondorfEffect* eff = globalCtx->specialEffects; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_NONE) { + eff->type = GDF_EFF_BLACK_DOT; + eff->pos = *pos; + eff->velocity = sZeroVec; + eff->accel = sZeroVec; + eff->unk_38 = 0.0f; + eff->scale = scale / 1000.0f; + eff->timer = 0; + eff->alpha = 0; + eff->unk_2E = 0; + break; + } + } +} + +void BossGanon_SetColliderPos(Vec3f* pos, ColliderCylinder* collider) { + collider->dim.pos.x = pos->x; + collider->dim.pos.y = pos->y; + collider->dim.pos.z = pos->z; +} + +void BossGanon_SetAnimationObject(BossGanon* this, GlobalContext* globalCtx, s32 objectId) { + this->animBankIndex = Object_GetIndex(&globalCtx->objectCtx, objectId); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x3D, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void BossGanon_Init(Actor* thisx, GlobalContext* globalCtx2) { + s16 i; + GlobalContext* globalCtx = globalCtx2; + BossGanon* this = (BossGanon*)thisx; + s32 cond; + f32 xDistFromPlayer; + f32 yDistFromPlayer; + f32 zDistFromPlayer; + Player* player = GET_PLAYER(globalCtx); + + if (thisx->params < 0x64) { + Flags_SetSwitch(globalCtx, 0x14); + globalCtx->specialEffects = sEffectBuf; + + for (i = 0; i < ARRAY_COUNT(sEffectBuf); i++) { + sEffectBuf[i].type = GDF_EFF_NONE; + } + + sGanondorf = this; + thisx->colChkInfo.health = 40; + Actor_ProcessInitChain(thisx, sInitChain); + ActorShape_Init(&thisx->shape, 0, NULL, 0); + Actor_SetScale(thisx, 0.01f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDorfSkel, NULL, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sDorfCylinderInit); + + if (thisx->params != 1) { + BossGanon_SetupIntroCutscene(this, globalCtx); + this->organAlpha = 255; + } else { + cond = Flags_GetSwitch(globalCtx, 0x37) && + ((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO)); + + if (!cond) { + BossGanon_SetupTowerCutscene(this, globalCtx); + } else { + Actor_Kill(thisx); + return; + } + + BossGanon_SetupTowerCutscene(this, globalCtx); + } + + sCape = (EnGanonMant*)Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_EN_GANON_MANT, 0.0f, + 0.0f, 0.0f, 0, 0, 0, 1); + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_BOSS); + } else { + thisx->flags &= ~ACTOR_FLAG_0; + this->fwork[GDF_FWORK_1] = 255.0f; + + if (thisx->params >= 0xC8) { + if (thisx->params == 0x12C) { + thisx->update = BossGanon_LightBall_Update; + thisx->draw = BossGanon_LightBall_Draw; + this->unk_1A8 = 2; + } else if (thisx->params == 0x190) { + thisx->update = BossGanon_LightBall_Update; + thisx->draw = BossGanon_LightBall_Draw; + this->unk_1A8 = 1; + } else if (thisx->params >= 0x104) { + // big magic light ball thrown + thisx->update = func_808E2544; + thisx->draw = func_808E324C; + this->unk_1C2 = 10; + this->unk_1A2 = 520 + (-thisx->params * 2); + + for (i = 0; i < 15; i++) { + this->unk_2EC[i] = thisx->world.pos; + } + + this->timers[1] = 3; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sLightBallCylinderInit); + } else if (thisx->params >= 0xFA) { + // big magic light ball charge + thisx->update = func_808E2544; + thisx->draw = func_808E324C; + this->unk_1A2 = Rand_ZeroFloat(10000.0f); + + for (i = 0; i < 15; i++) { + this->unk_2EC[i] = thisx->world.pos; + } + + this->fwork[GDF_FWORK_1] = 0; + } else { + thisx->update = func_808E1EB4; + thisx->draw = func_808E229C; + if (1) {} + thisx->speedXZ = 11.0f; + + if (thisx->params == 0xC8) { + this->timers[0] = 7; + } else { + this->timers[0] = (s16)Rand_ZeroFloat(3.0f) + 3; + } + + for (i = 0; i < 15; i++) { + this->unk_2EC[i].y = 5000.0f; + } + } + } else { + // light ball (anything from 0x64 - 0xC7) + thisx->update = BossGanon_LightBall_Update; + thisx->draw = BossGanon_LightBall_Draw; + thisx->speedXZ = 12.0f; + + xDistFromPlayer = player->actor.world.pos.x - thisx->world.pos.x; + yDistFromPlayer = (player->actor.world.pos.y + 30.0f) - thisx->world.pos.y; + zDistFromPlayer = player->actor.world.pos.z - thisx->world.pos.z; + + thisx->world.rot.y = Math_Atan2S(zDistFromPlayer, xDistFromPlayer); + thisx->world.rot.x = Math_Atan2S(sqrtf(SQ(xDistFromPlayer) + SQ(zDistFromPlayer)), yDistFromPlayer); + + if (Rand_ZeroOne() < 0) { + thisx->world.rot.y += (s16)Rand_CenteredFloat(5000.0f); + thisx->world.rot.x += (s16)Rand_CenteredFloat(5000.0f); + } + + this->timers[1] = 3; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sLightBallCylinderInit); + } + } +} + +void BossGanon_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BossGanon* this = (BossGanon*)thisx; + + if ((this->actor.params < 0xC8) || (this->actor.params >= 0x104)) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } + + if (this->actor.params < 0x64) { + SkelAnime_Free(&this->skelAnime, globalCtx); + } +} + +void BossGanon_SetupIntroCutscene(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + s32 animBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME2); + + if (animBankIndex < 0) { + Actor_Kill(&this->actor); + return; + } + + if (Object_IsLoaded(&globalCtx->objectCtx, animBankIndex)) { + this->actionFunc = BossGanon_IntroCutscene; + this->unk_198 = 1; + this->animBankIndex = animBankIndex; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[animBankIndex].segment); + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_005FFC, 0.0f); + } else { + this->actionFunc = BossGanon_SetupIntroCutscene; + } +} + +typedef struct { + /* 0x00 */ Vec3s eye; + /* 0x06 */ Vec3s at; +} CutsceneCameraPosition; // size = 0x12 + +static CutsceneCameraPosition sIntroCsCameraPositions[] = { + { { 0, 40, 0 }, { 0, 50, 430 } }, + { { -20, 30, 400 }, { 10, 55, 440 } }, + { { 0, 60, 300 }, { 0, 273, -150 } }, + { { 0, 180, -260 }, { 0, 155, -300 } }, + { { -30, 60, 440 }, { 20, 25, 390 } }, + { { -50, 140, -360 }, { 50, 92, -390 } }, + { { -10, 264, -121 }, { 5, 266, -160 } }, + { { -13, 200, -310 }, { 0, 125, -410 } }, + { { 0, 40, -50 }, { 0, 35, 230 } }, + { { 0, 140, -250 }, { 0, 115, -570 } }, + { { -410, 150, -130 }, { 50, 155, -170 } }, + { { 0, 130, -230 }, { 0, 125, -2000 } }, + { { -2, 147, -293 }, { -200, 345, -2000 } }, +}; + +void BossGanon_SetIntroCsCamera(BossGanon* this, u8 camPosIndex) { + CutsceneCameraPosition* camPos = &sIntroCsCameraPositions[camPosIndex]; + + this->csCamEye.x = camPos->eye.x; + this->csCamEye.y = camPos->eye.y; + this->csCamEye.z = camPos->eye.z; + + this->csCamAt.x = camPos->at.x; + this->csCamAt.y = camPos->at.y; + this->csCamAt.z = camPos->at.z; +} + +void BossGanon_IntroCutscene(BossGanon* this, GlobalContext* globalCtx) { + u8 moveCam = false; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + f32 sin; + f32 cos; + Camera* mainCam; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment); + + sCape->backPush = -2.0f; + sCape->backSwayMagnitude = 0.25f; + sCape->sideSwayMagnitude = -1.0f; + sCape->minDist = 0.0f; + + this->csTimer++; + + SkelAnime_Update(&this->skelAnime); + + switch (this->csState) { + case 0: + player->actor.world.pos.x = 0.0f; + player->actor.world.pos.y = 0.0f; + player->actor.world.pos.z = 430.0f; + + this->actor.world.pos.x = 0.0f; + this->actor.world.pos.y = 112.0f; + this->actor.world.pos.z = -333.0f; + + this->actor.shape.yOffset = -7000.0f; + this->actor.shape.rot.y = 0; + + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->csCamIndex = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->csCamIndex, CAM_STAT_ACTIVE); + this->csCamFov = 60.0f; + + if (gSaveContext.eventChkInf[7] & 0x100) { + // watched cutscene already, skip most of it + this->csState = 17; + this->csTimer = 0; + player->actor.world.pos.z = 20.0f; + this->useOpenHand = false; + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_0089F8, -5.0f); + this->fwork[GDF_FWORK_1] = 1000.0f; + BossGanon_SetIntroCsCamera(this, 11); + this->unk_198 = 2; + this->timers[2] = 110; + gSaveContext.healthAccumulator = 0x140; + Audio_QueueSeqCmd(NA_BGM_STOP); + } else { + this->useOpenHand = true; + BossGanon_SetIntroCsCamera(this, 0); + this->csState = 1; + sZelda = (EnZl3*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ZL3, 0.0f, + 220.0f, -150.0f, 0, 0, 0, 0x2000); + } + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_GANON_ORGAN, 0.0f, 0.0f, 0.0f, 0, + 0, 0, 1); + sCape->minY = 57.0f; + // fallthrough + case 1: + this->envLightMode = 3; + if (this->csTimer == 70) { + this->csState = 2; + this->csTimer = 0; + } + break; + + case 2: + BossGanon_SetIntroCsCamera(this, 1); + + if (this->csTimer == 10) { + func_8002DF54(globalCtx, &this->actor, 5); + } + + if (this->csTimer == 13) { + func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_SURPRISE); + } + + if (this->csTimer != 35) { + break; + } + + this->csState = 3; + this->csTimer = 0; + + this->csCamEye.x = 0.0f; + this->csCamEye.y = 60.0f; + this->csCamEye.z = 300.0f; + + this->csCamAt.x = 0.0f; + this->unk_704 = 1.2566371f; + // fallthrough + case 3: + this->envLightMode = 0; + globalCtx->envCtx.unk_D8 = 0.0f; + this->csCamAt.y = (sinf(this->unk_704) * 300.0f) + this->csCamEye.y; + this->csCamAt.z = (cosf(this->unk_704) * -300.0f) + this->csCamEye.z; + Math_ApproachF(&this->unk_704, 0.25f, 0.05f, this->csCamAtMaxStep.y); + Math_ApproachF(&this->csCamAtMaxStep.y, 0.01f, 1.0f, 0.0001f); + + if (this->csTimer != 200) { + break; + } + + func_8002DF54(globalCtx, &this->actor, 8); + this->csState = 4; + BossGanon_SetIntroCsCamera(this, 2); + this->csTimer = 0; + // fallthrough + case 4: + if ((this->csTimer == 0) || (this->csTimer == 10) || (this->csTimer == 20)) { + this->csCamEye.y += 68.0f; + this->csCamEye.z -= 142.0f; + } + + if (this->csTimer >= 20) { + this->envLightMode = 4; + } else { + this->envLightMode = 35; + } + + if (this->csTimer == 60) { + BossGanon_SetIntroCsCamera(this, 1); + this->csState = 5; + this->csTimer = 0; + } + break; + + case 5: + this->envLightMode = 5; + + if (this->csTimer < 50) { + globalCtx->envCtx.unk_D8 = 1.0f; + } + + if (this->csTimer == 10) { + func_8002DF54(globalCtx, &this->actor, 0x4B); + } + + if (this->csTimer == 70) { + BossGanon_SetIntroCsCamera(this, 3); + this->csState = 6; + this->csTimer = 0; + this->envLightMode = 3; + } + break; + + case 6: + this->envLightMode = 3; + + if (this->csTimer != 30) { + break; + } + + this->csState = 7; + this->csTimer = 0; + BossGanon_SetIntroCsCamera(this, 4); + this->triforceType = GDF_TRIFORCE_PLAYER; + this->fwork[GDF_TRIFORCE_SCALE] = 10.0f; + this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f; + this->fwork[GDF_TRIFORCE_PRIM_B] = 255.0f; + this->fwork[GDF_TRIFORCE_ENV_G] = 100.0f; + func_80078884(NA_SE_EV_TRIFORCE_MARK); + globalCtx->envCtx.unk_D8 = 0.0f; + // fallthrough + case 7: + this->envLightMode = 6; + // fade in links triforce + Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_A], 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_SCALE], 0.4f, 1.0f, 0.3f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_B], 170.0f, 1.0f, 2.55f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_ENV_G], 200.0f, 1.0f, 3.0f); + + if (this->csTimer >= 30) { + this->envLightMode = 65; + } + + if (this->csTimer == 30) { + globalCtx->envCtx.unk_D8 = 1.0f; + } + + BossGanon_SetIntroCsCamera(this, 4); + this->csCamEye.x += 5.0f; + this->csCamEye.z += -10.0f; + this->csCamAt.x += 18.0f; + + if (this->csTimer == 60) { + this->csState = 8; + this->csTimer = 0; + } + break; + + case 8: + this->envLightMode = 3; + BossGanon_SetIntroCsCamera(this, 5); + + if (this->csTimer != 30) { + break; + } + + this->csState = 9; + this->csTimer = 0; + func_8002DF54(globalCtx, &this->actor, 8); + sZelda->unk_3C8 = 0; + this->triforceType = GDF_TRIFORCE_ZELDA; + this->fwork[GDF_TRIFORCE_SCALE] = 10.0f; + this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f; + this->fwork[GDF_TRIFORCE_PRIM_B] = 255.0f; + this->fwork[GDF_TRIFORCE_ENV_G] = 100.0f; + func_80078884(NA_SE_EV_TRIFORCE_MARK); + globalCtx->envCtx.unk_D8 = 0.0f; + // fallthrough + case 9: + this->envLightMode = 7; + BossGanon_SetIntroCsCamera(this, 6); + // fade in zeldas triforce + Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_A], 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_SCALE], 0.4f, 1.0f, 0.3f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_B], 170.0f, 1.0f, 2.55f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_ENV_G], 200.0f, 1.0f, 3.0f); + + if (this->csTimer == 30) { + sZelda->unk_3C8 = 1; + } + + if (this->csTimer >= 32) { + this->envLightMode = 75; + } + + if (this->csTimer == 32) { + globalCtx->envCtx.unk_D8 = 1.0f; + } + + if (this->csTimer == 50) { + this->csState = 10; + this->csTimer = 0; + } + break; + + case 10: // top view of playing the organ + this->envLightMode = 3; + BossGanon_SetIntroCsCamera(this, 7); + + if (this->csTimer == 40) { + this->csState = 11; + this->csTimer = 0; + this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f; + } + break; + + case 11: // link is healed + this->envLightMode = 3; + BossGanon_SetIntroCsCamera(this, 8); + player->actor.world.pos.z = 20.0f; + + if (this->csTimer == 20) { + func_8002DF54(globalCtx, &this->actor, 0x17); + Interface_ChangeAlpha(11); // show hearts only + } + + if (this->csTimer == 25) { + gSaveContext.healthAccumulator = 0x140; + } + + if (this->csTimer == 100) { + Interface_ChangeAlpha(1); + } + + if (this->csTimer == 120) { + this->csState = 12; + this->csTimer = 0; + } + break; + + case 12: // first dialogue, ganondorf facing away from link + this->envLightMode = 3; + BossGanon_SetIntroCsCamera(this, 9); + + if (this->csTimer == 30) { + Audio_QueueSeqCmd(0x100100FF); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_004F64); + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_004F64, -5.0f); + } + + if ((this->csTimer > 30) && Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_006AF4, 0.0f); + this->fwork[GDF_FWORK_1] = 1000.0f; + } + + if (this->csTimer == 80) { + Message_StartTextbox(globalCtx, 0x70C8, NULL); + } + + if ((this->csTimer > 180) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 15; + this->csTimer = 0; + this->useOpenHand = false; + } + break; + + case 15: // side view of all 3 of them + this->envLightMode = 0; + globalCtx->envCtx.unk_D8 = 0.0f; + BossGanon_SetIntroCsCamera(this, 10); + + if (this->csTimer == 30) { + Message_StartTextbox(globalCtx, 0x70C9, NULL); + } + + if ((this->csTimer > 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 16; + this->csTimer = 0; + BossGanon_SetIntroCsCamera(this, 11); + this->unk_198 = 2; + sZelda->unk_3C8 = 2; + this->timers[2] = 110; + this->envLightMode = 3; + } + break; + + case 16: + this->envLightMode = 3; + + if (this->csTimer <= 20) { + if (this->csTimer == 20) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_004304, -5.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_004304); + } + } else if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + Message_StartTextbox(globalCtx, 0x70CA, NULL); + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_0089F8, -5.0f); + this->fwork[GDF_FWORK_1] = 1000.0f; + } + + if ((this->csTimer > 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 17; + this->csTimer = 0; + } + break; + + case 17: // turns around + this->envLightMode = 3; + + if (this->csTimer == 20) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_001F58, -5.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_001F58); + } + + if (this->csTimer > 10) { + if (this->csTimer == 62) { + sCape->attachRightArmTimer = 20.0f; + } + + if (this->csTimer == 57) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + } + + Math_ApproachF(&this->csCamFov, 110.0f, 0.1f, this->csCamMaxStepScale * 2.0f); + Math_ApproachF(&this->csCamEye.z, -290.0f, 0.1f, this->csCamMaxStepScale * 2.4f); + Math_ApproachF(&this->csCamMaxStepScale, 0.75f, 1.0f, 0.05f); + + if (this->csTimer == 70) { + this->csState = 18; + this->csTimer = 0; + this->csCamFov = 60.0f; + BossGanon_SetIntroCsCamera(this, 12); + Message_StartTextbox(globalCtx, 0x70CB, NULL); + } + } + break; + + case 18: // last dialog before triforce + this->envLightMode = 3; + BossGanon_SetIntroCsCamera(this, 12); + this->csCamEye.y += -6.0f; + this->csCamEye.z += 6.0f; + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1] - 5.0f)) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_003018, -5.0f); + this->fwork[GDF_FWORK_1] = 1000.0f; + } + + if ((this->csTimer <= 50) || (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) { + break; + } + + this->csState = 19; + this->csTimer = 0; + Message_StartTextbox(globalCtx, 0x70CC, NULL); + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_007268, -5.0f); + this->triforceType = GDF_TRIFORCE_DORF; + this->fwork[GDF_TRIFORCE_SCALE] = 10.0f; + this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f; + this->fwork[GDF_TRIFORCE_PRIM_B] = 255.0f; + this->fwork[GDF_TRIFORCE_ENV_G] = 100.0f; + globalCtx->envCtx.unk_D8 = 0.0f; + // fallthrough + case 19: // show triforce + this->envLightMode = 8; + + if (this->csTimer >= 60) { + this->envLightMode = 9; + + if (this->csTimer == 60) { + globalCtx->envCtx.unk_D8 = 1.0f; + } + } + + BossGanon_SetIntroCsCamera(this, 12); + this->csCamEye.y += -6.0f; + this->csCamEye.z += 6.0f; + + if (this->csTimer >= 30) { + if (this->csTimer == 30) { + func_80078884(NA_SE_EV_TRIFORCE_MARK); + } + + // fade in ganondorf's triforce + Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_A], 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_SCALE], 0.6f, 1.0f, 0.3f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_PRIM_B], 170.0f, 1.0f, 2.55f); + Math_ApproachF(&this->fwork[GDF_TRIFORCE_ENV_G], 200.0f, 1.0f, 3.0f); + } + + if (this->csTimer == 17) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_007A64, -5.0f); + } + + if ((this->csTimer > 80) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 20; + this->csTimer = 0; + + this->csCamTargetEye.x = this->csCamEye.x - 50.0f; + this->csCamTargetEye.y = this->csCamEye.y - 100.0f; + this->csCamTargetEye.z = this->csCamEye.z + 400.0f; + + this->csCamEyeMaxStep.x = 50.0f; + this->csCamEyeMaxStep.y = 100.0f; + this->csCamEyeMaxStep.z = 400.0f; + + this->csCamAtMaxStep.x = 400.0f; + this->csCamMaxStepScale = 0.0f; + + this->csCamTargetAt.x = this->csCamAt.x + 400.0f; + this->csCamTargetAt.y = this->csCamAt.y; + this->csCamTargetAt.z = this->csCamAt.z; + + this->csCamMovementScale = 0.2f; + + this->fwork[GDF_VORTEX_ALPHA] = 0.0f; + this->fwork[GDF_VORTEX_SCALE] = 0.1f; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE); + } + break; + + case 20: // zoom cam out + this->envLightMode = 10; + moveCam = true; + Math_ApproachF(&this->csCamMaxStepScale, 0.15f, 1.0f, 0.015f); + + if (this->csTimer <= 40) { + Math_ApproachF(&this->fwork[GDF_VORTEX_ALPHA], 255.0f, 1.0f, 6.5f); + Math_ApproachF(&this->fwork[GDF_VORTEX_SCALE], 0.2f, 1.0f, 0.025f); + } + + if (this->csTimer > 20) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE_M - SFX_FLAG); + } + + if (this->csTimer > 20) { + BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE); + BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE); + } + + if (this->csTimer == 30) { + func_8002DF54(globalCtx, &this->actor, 0x4A); + } + + if (this->csTimer <= 50) { + break; + } + + this->csState = 21; + this->csTimer = 0; + this->fwork[GDF_TRIFORCE_PRIM_A] = 0.0f; + this->fwork[GDF_VORTEX_SCALE] = 0.16f; + goto skip_sound_and_fx; + + case 21: // purple vortex + this->envLightMode = 11; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE_M - SFX_FLAG); + BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE); + BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_PURPLE); + + skip_sound_and_fx: + this->csCamEye.x = -30.0f; + this->csCamEye.y = 37.0f; + this->csCamEye.z = -30.0f; + + this->csCamAt.x = -10.0f; + this->csCamAt.y = 45.0f; + this->csCamAt.z = 0.0f; + + if (this->csTimer == 13) { + Message_StartTextbox(globalCtx, 0x70CD, NULL); + } + + if ((this->csTimer <= 120) || (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) { + break; + } + + this->csState = 22; + this->csTimer = 0; + this->timers[2] = 30; + this->organAlpha = 254; + this->csCamAt.x = this->unk_1FC.x - 10.0f; + this->csCamAt.y = this->unk_1FC.y + 30.0f; + this->csCamAt.z = this->unk_1FC.z; + this->fwork[GDF_VORTEX_ALPHA] = 255.0f; + this->fwork[GDF_VORTEX_SCALE] = 0.2f; + // fallthrough + case 22: // start floating, show title card, start fight + if (this->csTimer > 30) { + this->envLightMode = 0; + } else { + this->envLightMode = 12; + } + + Math_ApproachZeroF(&this->fwork[GDF_VORTEX_ALPHA], 1.0f, 10.0f); + + this->csCamEye.x = -30.0f; + this->csCamEye.y = 137.0f; + this->csCamEye.z = -110.0f; + + Math_ApproachF(&this->csCamAt.y, this->unk_1FC.y + 30.0f, 0.1f, 20.0f); + Math_ApproachF(&this->csCamAt.x, this->unk_1FC.x - 10.0f, 0.1f, 5.0f); + + if (this->csTimer == 20) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f); + SkelAnime_Update(&this->skelAnime); + this->actor.shape.yOffset = 0.0f; + sCape->attachShouldersTimer = 18.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + this->unk_198 = 0; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_GANONDORF_BOSS); + } + + if (this->csTimer == 50) { + gSegments[6] = VIRTUAL_TO_PHYSICAL( + globalCtx->objectCtx.status[Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON)].segment); + + if (!(gSaveContext.eventChkInf[7] & 0x100)) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gDorfTitleCardTex), 160, 180, 128, 40); + } + + gSaveContext.eventChkInf[7] |= 0x100; + } + + if (this->csTimer >= 20) { + this->legSwayEnabled = true; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_FLOAT - SFX_FLAG); + + Math_ApproachF(&this->actor.world.pos.y, 228.0f, 0.05f, 2.0f); + Math_ApproachF(&this->actor.world.pos.z, -230.0f, 0.05f, 4.0f); + + sCape->backPush = -3.0f; + sCape->backSwayMagnitude = 0.25f; + sCape->sideSwayMagnitude = -3.0f; + + sin = Math_SinS(this->csTimer * 1500); + this->actor.velocity.y = this->fwork[GDF_FWORK_0] * sin * 0.04f; + this->actor.world.pos.y += this->actor.velocity.y; + + cos = Math_CosS(this->csTimer * 1800); + this->actor.world.pos.x = this->fwork[GDF_FWORK_0] * cos * 0.5f; + this->actor.velocity.x = this->actor.world.pos.x - this->actor.prevPos.x; + + Math_ApproachF(&this->fwork[GDF_FWORK_0], 50.0f, 1.0f, 1.0f); + } + + if (this->csTimer > 30) { + this->organAlpha -= 5; + + if (this->organAlpha < 0) { + this->organAlpha = 0; + } + } + + if (this->csTimer == 120) { + mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + mainCam->eye = this->csCamEye; + mainCam->eyeNext = this->csCamEye; + mainCam->at = this->csCamAt; + func_800C08AC(globalCtx, this->csCamIndex, 0); + this->csState = this->csCamIndex = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + BossGanon_SetupWait(this, globalCtx); + } + + if (sZelda != NULL) { + sZelda->actor.world.pos.x = 0.0f; + sZelda->actor.world.pos.y = 350.0f; + sZelda->actor.world.pos.z = 0.0f; + } + } + + if (this->csCamIndex != 0) { + if (moveCam) { + Math_ApproachF(&this->csCamEye.x, this->csCamTargetEye.x, this->csCamMovementScale, + this->csCamEyeMaxStep.x * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamEye.y, this->csCamTargetEye.y, this->csCamMovementScale, + this->csCamEyeMaxStep.y * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamEye.z, this->csCamTargetEye.z, this->csCamMovementScale, + this->csCamEyeMaxStep.z * this->csCamMaxStepScale); + + Math_ApproachF(&this->csCamAt.x, this->csCamTargetAt.x, this->csCamMovementScale, + this->csCamAtMaxStep.x * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamAt.y, this->csCamTargetAt.y, this->csCamMovementScale, + this->csCamAtMaxStep.y * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamAt.z, this->csCamTargetAt.z, this->csCamMovementScale, + this->csCamAtMaxStep.z * this->csCamMaxStepScale); + } + + Gameplay_CameraSetAtEye(globalCtx, this->csCamIndex, &this->csCamAt, &this->csCamEye); + Gameplay_CameraSetFov(globalCtx, this->csCamIndex, this->csCamFov); + } +} + +void BossGanon_SetupDeathCutscene(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + s32 animBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME2); + + if (Object_IsLoaded(&globalCtx->objectCtx, animBankIndex)) { + this->actionFunc = BossGanon_DeathAndTowerCutscene; + this->csTimer = this->csState = 0; + this->unk_198 = 1; + this->animBankIndex = animBankIndex; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[animBankIndex].segment); + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00EA00, 0.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00EA00); + this->unk_508 = 0.0f; + } +} + +void BossGanon_SetupTowerCutscene(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + s32 animBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME2); + + if (Object_IsLoaded(&globalCtx->objectCtx, animBankIndex)) { + this->animBankIndex = animBankIndex; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[animBankIndex].segment); + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00EA00, 0.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00EA00); + this->actionFunc = BossGanon_DeathAndTowerCutscene; + this->csTimer = 0; + this->csState = 100; + this->unk_198 = 1; + gSaveContext.magic = gSaveContext.unk_13F4; + gSaveContext.health = gSaveContext.healthCapacity; + } else { + this->actionFunc = BossGanon_SetupTowerCutscene; + } +} + +void BossGanon_ShatterWindows(u8 windowShatterState) { + s16 i; + u8* tex1 = ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(ganon_boss_sceneTex_006C18)); + u8* tex2 = ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(ganon_boss_sceneTex_007418)); + + for (i = 0; i < 2048; i++) { + if ((tex1[i] != 0) && (Rand_ZeroOne() < 0.03f)) { + if ((((u8*)gDorfWindowShatterTemplateTex)[i] == 0) || (windowShatterState == GDF_WINDOW_SHATTER_FULL)) { + tex1[i] = tex2[i] = 1; + } + } + } +} + +void BossGanon_DeathAndTowerCutscene(BossGanon* this, GlobalContext* globalCtx) { + const bool originalBlood = CVar_GetS32("gOriginalBlood", 1); + + static Color_RGBA8 bloodPrimColor = { 120, 0, 0, 255 }; + static Color_RGBA8 bloodEnvColor = { 120, 0, 0, 255 }; + + if(!originalBlood) { + bloodPrimColor.r = 0; + bloodPrimColor.g = 120; + bloodPrimColor.b = 0; + + bloodEnvColor.r = 0; + bloodEnvColor.g = 120; + bloodEnvColor.b = 0; + } + + s16 i; + u8 moveCam = false; + Player* player = GET_PLAYER(globalCtx); + s16 pad; + Vec3f sp98; + Vec3f sp8C; + Vec3f sp80; + Vec3f sp74; + Camera* mainCam; + Vec3f sp64; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment); + + this->csTimer++; + SkelAnime_Update(&this->skelAnime); + + switch (this->csState) { + case 0: + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->csCamIndex = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->csCamIndex, CAM_STAT_ACTIVE); + + this->actor.world.pos.x = 0.0f; + this->actor.world.pos.y = 70.0f; + this->actor.world.pos.z = -80.0f; + + this->actor.shape.yOffset = -7000.0f; + + this->actor.shape.rot.y = 0; + this->csState = 1; + this->csTimer = 0; + this->useOpenHand = true; + // fallthrough + case 1: + player->actor.shape.rot.y = -0x8000; + + player->actor.world.pos.x = -10.0f; + player->actor.world.pos.y = 0.0f; + player->actor.world.pos.z = 115.0f; + + this->envLightMode = 13; + + if (this->csTimer < 30) { + globalCtx->envCtx.unk_D8 = 0.0f; + } + + if (this->csTimer >= 2) { + globalCtx->envCtx.fillScreen = false; + } + + this->csCamEye.x = -50.0f; + this->csCamEye.z = -50.0f; + this->csCamEye.y = 50.0f; + + this->csCamAt.x = this->unk_1FC.x; + this->csCamAt.y = this->unk_1FC.y + 30.0f; + this->csCamAt.z = this->unk_1FC.z; + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_00F19C, 0.0f); + this->csState = 2; + this->csTimer = 0; + } + break; + + case 2: + this->csCamEye.x = -100.0f; + this->csCamEye.y = 20.0f; + this->csCamEye.z = -130.0f; + + this->envLightMode = 13; + + this->csCamAt.x = this->unk_1FC.x; + this->csCamAt.y = this->unk_1FC.y; + this->csCamAt.z = this->unk_1FC.z + 40.0f; + + if (this->csTimer >= 30) { + this->csState = 3; + this->csTimer = 0; + Message_StartTextbox(globalCtx, 0x70CE, NULL); + this->fwork[GDF_FWORK_1] = 1000.0f; + } + + if ((this->unk_1A2 % 32) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BREATH); + } + break; + + case 3: + this->envLightMode = 14; + + if ((this->fwork[GDF_FWORK_1] > 100.0f) && ((this->unk_1A2 % 32) == 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BREATH); + } + + this->csCamEye.x = 7.0f; + this->csCamEye.y = 52.0f; + this->csCamEye.z = -15.0f; + + this->csCamAt.x = this->unk_1FC.x - 5.0f; + this->csCamAt.y = this->unk_1FC.y + 30.0f - 10.0f; + this->csCamAt.z = this->unk_1FC.z; + + if ((this->fwork[GDF_FWORK_1] > 100.0f) && (this->csTimer > 100) && + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00B668, 0.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00B668); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_TOKETU); + } else { + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1] - 16.0f)) { + for (i = 0; i < 40; i++) { + sp98.x = Rand_CenteredFloat(5.0f); + sp98.y = Rand_CenteredFloat(1.5f) + 1.0f; + sp98.z = Rand_ZeroFloat(5.0f) + 2.0f; + + sp8C.x = 0.0f; + sp8C.y = -1.0f; + sp8C.z = 0.0f; + + sp80.x = this->unk_208.x; + sp80.y = this->unk_208.y - 10.0f; + sp80.z = this->unk_208.z; + + func_8002836C(globalCtx, &sp80, &sp98, &sp8C, &bloodPrimColor, &bloodEnvColor, + (s16)Rand_ZeroFloat(50.0f) + 50, 0, 17); + } + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_00BE38, 0.0f); + this->csState = 4; + this->csTimer = 0; + } + } + break; + + case 4: + this->envLightMode = 14; + + if (this->csTimer == 30) { + Message_StartTextbox(globalCtx, 0x70CF, NULL); + this->csState = 5; + this->csTimer = 0; + } + break; + + case 5: + this->envLightMode = 14; + + if ((this->csTimer > 70) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 6; + this->csTimer = 0; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_010298, 0.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_010298); + + this->csCamMovementScale = 0.05f; + this->csCamMaxStepScale = 0.0f; + + this->csCamTargetEye.x = 7.0f; + this->csCamTargetEye.y = 12.0f; + this->csCamTargetEye.z = 70.0f; + + this->csCamTargetAt.x = this->unk_1FC.x - 5.0f; + this->csCamTargetAt.y = (this->unk_1FC.y + 30.0f) - 10.0f; + this->csCamTargetAt.z = this->unk_1FC.z; + + this->csCamEyeMaxStep.x = fabsf(this->csCamEye.x - this->csCamTargetEye.x); + this->csCamEyeMaxStep.y = fabsf(this->csCamEye.y - this->csCamTargetEye.y); + this->csCamEyeMaxStep.z = fabsf(this->csCamEye.z - this->csCamTargetEye.z); + + this->csCamAtMaxStep.x = fabsf(this->csCamAt.x - this->csCamTargetAt.x); + this->csCamAtMaxStep.y = fabsf(this->csCamAt.y - this->csCamTargetAt.y); + this->csCamAtMaxStep.z = fabsf(this->csCamAt.z - this->csCamTargetAt.z); + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CASBREAK); + } + break; + + case 6: + this->envLightMode = 14; + moveCam = true; + Math_ApproachF(&this->csCamMaxStepScale, 0.2f, 1.0f, 0.01f); + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime2_Anim_010514, 0.0f); + this->csState = 7; + this->csTimer = 0; + this->unk_2E8 = 0; + this->envLightMode = 15; + this->unk_508 = 0.0f; + this->fwork[GDF_FWORK_1] = 1000.0f; + globalCtx->envCtx.unk_D8 = 0.0f; + } + break; + + case 7: + if (this->csTimer < 10) { + globalCtx->envCtx.unk_D8 = 0.0f; + } + + if (this->csTimer == 30) { + this->csState = 8; + this->csTimer = 0; + this->unk_70C = 0.0f; + } + goto skip_cam_and_quake; + + case 8: + this->csCamEye.x = -60.0f; + this->csCamEye.y = 80.0f; + this->csCamEye.z = -130.0f; + + this->csCamAt.x = 0.0f; + this->csCamAt.y = 0.0f; + this->csCamAt.z = 70.0f; + + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.2f; + + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + skip_cam_and_quake: + this->envLightMode = 15; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BODY_SPARK - SFX_FLAG); + + for (i = 1; i < 15; i++) { + this->unk_4E4[i] = 0xA; + } + + this->unk_2E6 = 20000; + Math_ApproachF(&this->unk_508, 5.0f, 0.05f, 0.1f); + + if (this->csTimer == 30) { + this->csState = 9; + this->csTimer = 0; + + this->csCamEye.x = -30.0f; + this->csCamEye.y = 40.0f; + this->csCamEye.z = 60.0f; + + this->csCamAt.x = 492.0f; + this->csCamAt.y = 43.0f; + this->csCamAt.z = 580.0f; + + this->csCamMaxStepScale = 0.0f; + this->unk_710 = 10.0f; + } + break; + + case 9: + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BODY_SPARK - SFX_FLAG); + + if (this->csTimer == 2) { + func_8002DF54(globalCtx, &this->actor, 0x39); + } + + if (this->csTimer > 50) { + Math_ApproachZeroF(&this->unk_710, 1.0f, 0.2f); + Math_ApproachF(&this->csCamEye.x, 270.0f, 0.05f, this->csCamMaxStepScale * 30.0f); + Math_ApproachF(&this->csCamEye.z, 260.0f, 0.05f, this->csCamMaxStepScale * 20.0f); + Math_ApproachF(&this->csCamAt.y, 103.0f, 0.05f, this->csCamMaxStepScale * 6.0f); + Math_ApproachF(&this->csCamAt.z, 280.0f, 0.05f, this->csCamMaxStepScale * 20.0f); + Math_ApproachF(&this->csCamMaxStepScale, 1.0f, 1.0f, 0.01f); + } + + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * this->unk_710; + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + if (this->csTimer < 100) { + this->windowShatterState = GDF_WINDOW_SHATTER_PARTIAL; + this->envLightMode = 15; + } else { + this->envLightMode = 16; + this->windowShatterState = GDF_WINDOW_SHATTER_FULL; + } + + if (this->csTimer >= 130) { + Math_ApproachF(&this->whiteFillAlpha, 255.0f, 1.0f, 5.0f); + } + + if (this->csTimer == 180) { + globalCtx->sceneLoadFlag = 0x14; + globalCtx->nextEntranceIndex = 0x43F; + globalCtx->fadeTransition = 5; + } + break; + + case 100: + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->csCamIndex = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->csCamIndex, CAM_STAT_ACTIVE); + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime2_Anim_00ADDC, 0.0f); + this->fwork[1] = Animation_GetLastFrame(&object_ganon_anime2_Anim_00EA00); + this->csState = 101; + this->skelAnime.playSpeed = 0.0f; + sZelda = (EnZl3*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ZL3, 0.0f, + 6000.0f, 0.0f, 0, 0, 0, 0x2000); + + player->actor.world.pos.x = -472.0f; + player->actor.world.pos.y = 4102.0f; + player->actor.world.pos.z = -130.0f; + + player->actor.shape.rot.y = -0x8000; + + this->actor.world.pos.x = -472.0f; + this->actor.world.pos.y = 4172.0f; + this->actor.world.pos.z = -400.0f; + + this->actor.shape.yOffset = -7000.0f; + this->actor.shape.rot.y = 0; + + this->csCamEye.x = this->csCamAt.x = -472.0f; + this->csCamEye.y = this->csCamAt.y = 4152.0f; + this->csCamEye.z = -160.0f; + + this->csCamAt.z = -100.0f; + + sCape->backPush = -2.0f; + sCape->backSwayMagnitude = 0.25f; + sCape->sideSwayMagnitude = -1.0f; + sCape->minDist = 0.0f; + sCape->minY = 4104.0f; + sCape->tearTimer = 20; + + this->whiteFillAlpha = 255.0f; + globalCtx->envCtx.unk_D8 = 1.0f; + // fallthrough + case 101: + player->actor.world.pos.y = 4102.0f; + Math_ApproachZeroF(&this->whiteFillAlpha, 1.0f, 5.0f); + + if (this->csTimer > 40) { + Math_ApproachF(&this->csCamEye.z, -520.0f, 0.1f, this->csCamMaxStepScale); + Math_ApproachF(&this->csCamMaxStepScale, 5.0f, 1.0f, 0.1f); + + if (this->csTimer == 150) { + this->skelAnime.playSpeed = 1.0f; + } + + if (this->csTimer == 160) { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_BOUND_NOWEAPON); + } + + if (this->csTimer == 187) { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_BODY_HIT); + } + + if (this->csTimer == 180) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + } + + if (this->csTimer == 190) { + sp74 = this->actor.world.pos; + sp74.y = 4102.0f; + BossGanonEff_SpawnDustDark(globalCtx, &sp74, 0.2f, 0.7f); + } + + if (this->csTimer == 230) { + this->csState = 102; + this->csTimer = 0; + } + } + break; + + case 102: + player->actor.world.pos.y = 4102.0f; + + this->csCamEye.x = -442.0f; + this->csCamEye.y = 4152.0f; + this->csCamEye.z = -135.0f; + + this->csCamAt.x = -472.0f; + this->csCamAt.y = 4152.0f; + this->csCamAt.z = -135.0f; + + if (this->csTimer == 5) { + func_8002DF54(globalCtx, &this->actor, 0x4C); + } + + if (this->csTimer == 70) { + func_8002DF54(globalCtx, &this->actor, 0x4D); + } + + if (this->csTimer == 90) { + this->csState = 103; + this->csTimer = 0; + sZelda->actor.world.pos.x = -472.0f; + sZelda->actor.world.pos.y = 4352.0f; + sZelda->actor.world.pos.z = -200.0f; + sZelda->unk_3C8 = 3; + } + break; + + case 103: + Audio_PlayActorSound2(&sZelda->actor, NA_SE_EV_DOWN_TO_GROUND - SFX_FLAG); + Math_ApproachF(&sZelda->actor.world.pos.y, 4102.0f, 0.05f, 1.5f); + + this->csCamEye.x = -242.0f; + this->csCamEye.y = 4122.0f; + this->csCamEye.z = -190.0f; + + this->csCamAt.x = sZelda->actor.world.pos.x; + this->csCamAt.y = sZelda->actor.world.pos.y + 40.0f + 5.0f; + this->csCamAt.z = sZelda->actor.world.pos.z; + + if (this->csTimer == 200) { + sZelda->actor.world.pos.y = 4102.0f; + this->csState = 104; + this->csTimer = 0; + } else { + break; + } + // fallthrough + case 104: + this->csCamEye.x = -432.0f; + this->csCamEye.y = 4147.0f; + this->csCamEye.z = -200.0f; + + this->csCamAt.x = sZelda->actor.world.pos.x; + this->csCamAt.y = sZelda->actor.world.pos.y + 40.0f + 5.0f; + this->csCamAt.z = sZelda->actor.world.pos.z; + + if (this->csTimer >= 10) { + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + } + + if (this->csTimer == 10) { + sZelda->unk_3C8 = 8; + } + + if (this->csTimer == 50) { + sZelda->unk_3C8 = 4; + } + + if (this->csTimer == 100) { + this->csState = 105; + this->csTimer = 0; + } + break; + + case 105: + this->csCamEye.x = -450.0f; + this->csCamEye.y = 4154.0f; + this->csCamEye.z = -182.0f; + + this->csCamAt.x = sZelda->actor.world.pos.x - 5.0f; + this->csCamAt.y = sZelda->actor.world.pos.y + 40.0f + 5.0f; + this->csCamAt.z = sZelda->actor.world.pos.z - 25.0f; + + if (this->csTimer == 10) { + Message_StartTextbox(globalCtx, 0x70D0, NULL); + } + + if ((this->csTimer > 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 1055; + this->csTimer = 0; + } + break; + + case 1055: + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.3f; + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + if (this->csTimer == 20) { + sZelda->unk_3C8 = 5; + func_8002DF54(globalCtx, &this->actor, 0x39); + } + + if (this->csTimer == 40) { + this->csState = 1056; + this->csTimer = 0; + } + break; + + case 1056: + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.3f; + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + this->csCamEye.x = -503.0f; + this->csCamEye.y = 4128.0f; + this->csCamEye.z = -162.0f; + + this->csCamAt.x = -416.0f; + this->csCamAt.y = 4181.0f; + this->csCamAt.z = -75.0f; + + if (this->csTimer > 40) { + this->csState = 1057; + this->csTimer = 0; + } + break; + + case 1057: + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * (50.0f * this->csCamMovementScale); + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + Math_ApproachF(&this->csCamEye.x, -1200.0f, 0.1f, this->csCamMovementScale * 697.0f); + Math_ApproachF(&this->csCamEye.y, 4241.0f, 0.1f, this->csCamMovementScale * 113.0f); + Math_ApproachF(&this->csCamEye.z, -1048.0f, 0.1f, this->csCamMovementScale * 886.0f); + + Math_ApproachF(&this->csCamMovementScale, 0.05f, 1.0f, 0.001f); + + if (this->csTimer > 80) { + this->csState = 106; + this->csTimer = 60; + } + break; + + case 106: + this->csCamEye.x = -450.0f; + this->csCamEye.y = 4154.0f; + this->csCamEye.z = -182.0f; + + this->csCamAt.x = sZelda->actor.world.pos.x - 5.0f; + this->csCamAt.y = sZelda->actor.world.pos.y + 40.0f + 5.0f; + this->csCamAt.z = sZelda->actor.world.pos.z - 25.0f; + + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.3f; + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + if (this->csTimer == 70) { + sZelda->unk_3C8 = 6; + } + + if (this->csTimer == 90) { + Message_StartTextbox(globalCtx, 0x70D1, NULL); + } + + if ((this->csTimer > 150) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->csState = 107; + this->csTimer = 0; + Message_StartTextbox(globalCtx, 0x70D2, NULL); + func_8002DF54(globalCtx, &this->actor, 0x39); + } + break; + + case 107: + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.8f; + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + this->csCamEye.x = -380.0f; + this->csCamEye.y = 4154.0f; + this->csCamEye.z = -242.0f; + + this->csCamAt.x = (sZelda->actor.world.pos.x - 5.0f) - 30.0f; + this->csCamAt.y = (sZelda->actor.world.pos.y + 40.0f + 5.0f) - 20.0f; + this->csCamAt.z = (sZelda->actor.world.pos.z - 25.0f) + 80.0f; + + if ((this->csTimer > 50) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + sZelda->unk_3C8 = 7; + this->csState = 108; + this->csTimer = 0; + } + break; + + case 108: + this->unk_70C = Math_SinS(this->csTimer * 0x6300) * 0.8f; + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + + this->csCamAt.x = (sZelda->actor.world.pos.x - 5.0f) - 30.0f; + this->csCamAt.y = (sZelda->actor.world.pos.y + 40.0f + 5.0f) - 20.0f; + this->csCamAt.z = (sZelda->actor.world.pos.z - 25.0f) + 80.0f; + + if (this->csTimer > 50) { + mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + mainCam->eye = this->csCamEye; + mainCam->eyeNext = this->csCamEye; + mainCam->at = this->csCamAt; + + func_800C08AC(globalCtx, this->csCamIndex, 0); + this->csState = 109; + this->csCamIndex = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Flags_SetSwitch(globalCtx, 0x37); + } + break; + + case 109: + func_80078884(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + break; + } + + if (this->csState >= 100) { + this->envLightMode = 20; + } + + if (this->csCamIndex != 0) { + if (moveCam) { + Math_ApproachF(&this->csCamEye.x, this->csCamTargetEye.x, this->csCamMovementScale, + this->csCamEyeMaxStep.x * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamEye.y, this->csCamTargetEye.y, this->csCamMovementScale, + this->csCamEyeMaxStep.y * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamEye.z, this->csCamTargetEye.z, this->csCamMovementScale, + this->csCamEyeMaxStep.z * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamAt.x, this->csCamTargetAt.x, this->csCamMovementScale, + this->csCamAtMaxStep.x * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamAt.y, this->csCamTargetAt.y, this->csCamMovementScale, + this->csCamAtMaxStep.y * this->csCamMaxStepScale); + Math_ApproachF(&this->csCamAt.z, this->csCamTargetAt.z, this->csCamMovementScale, + this->csCamAtMaxStep.z * this->csCamMaxStepScale); + } + + sp64 = this->csCamAt; + sp64.y += this->unk_70C; + Gameplay_CameraSetAtEye(globalCtx, this->csCamIndex, &sp64, &this->csCamEye); + } +} + +void BossGanon_SetupPoundFloor(BossGanon* this, GlobalContext* globalCtx) { + this->unk_1C2 = 0; + this->timers[0] = 40; + this->actionFunc = BossGanon_PoundFloor; + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->fwork[GDF_CENTER_POS] = 100.0f; +} + +void BossGanon_PoundFloor(BossGanon* this, GlobalContext* globalCtx) { + s16 i; + f32 heightTarget; + f32 targetPosX; + f32 targetPosZ; + Vec3f sp6C; + Vec3f sp60; + Vec3f sp54; + Vec3f sp48; + + SkelAnime_Update(&this->skelAnime); + + switch (this->unk_1C2) { + case 0: + targetPosX = Math_SinS(this->unk_1A2 * 1280); + targetPosX = targetPosX * this->fwork[GDF_CENTER_POS]; + targetPosZ = Math_CosS(this->unk_1A2 * 1792); + targetPosZ = targetPosZ * this->fwork[GDF_CENTER_POS]; + + Math_ApproachF(&this->actor.world.pos.x, targetPosX, 0.05f, this->fwork[GDF_FWORK_0]); + Math_ApproachF(&this->actor.world.pos.z, targetPosZ, 0.05f, this->fwork[GDF_FWORK_0]); + Math_ApproachF(&this->fwork[GDF_CENTER_POS], 0.0f, 1, 1.5f); + + if (this->timers[0] == 5) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_HIT_GND); + } + + if (this->timers[0] < 14) { + heightTarget = 250.0f; + this->unk_258 += (Rand_ZeroFloat(M_PI / 2) + (M_PI / 2)); + Math_ApproachF(&this->handLightBallScale, 7.0f, 0.5f, 1.0f); + this->envLightMode = 1; + } else { + heightTarget = 200.0f; + } + + Math_ApproachF(&this->actor.world.pos.y, heightTarget, 0.1f, this->actor.velocity.y); + Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f); + + if (this->timers[0] == 14) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfPoundAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfPoundAnim, 0.0f); + this->actor.velocity.y = 0.0f; + } + + if (this->timers[0] == 0) { + this->unk_1C2 = 1; + this->actor.velocity.y = 0.0f; + } + break; + + case 1: + sCape->gravity = -1.0f; + this->envLightMode = 1; + Math_ApproachF(&this->actor.velocity.y, -50.0f, 1.0f, 10.0f); + this->actor.world.pos.y += this->actor.velocity.y; + + if (this->actor.world.pos.y < 60.0f) { + this->actor.world.pos.y = 60.0f; + this->unk_1C2 = 2; + this->timers[0] = 10; + func_80033E88(&this->actor, globalCtx, 0xA, 0x14); // rumble + this->unk_19C = 35; + this->unk_19E = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_HIT_GND_IMP); + this->handLightBallScale = 0.0f; + sp60 = this->unk_260; + sp60.y = 0.0f; + + for (i = 0; i < 80; i++) { + sp6C.x = Rand_CenteredFloat(25.0f); + sp6C.y = Rand_ZeroFloat(17.0f); + sp6C.z = Rand_CenteredFloat(25.0f); + BossGanonEff_SpawnLightRay(globalCtx, &sp60, &sp6C, &sZeroVec, Rand_ZeroFloat(300.0f) + 500.0f, + 13.0f, 0x1E); + } + } + break; + + case 2: + this->envLightMode = 1; + + if (this->timers[0] == 0) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfPoundEndAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfPoundEndAnim, 0.0f); + this->unk_1C2 = 3; + this->unk_19F = 1; + this->actor.velocity.y = 0.0f; + } + break; + + case 3: + Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.1f, this->actor.velocity.y); + Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f); + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp3Anim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f); + SkelAnime_Update(&this->skelAnime); + sCape->attachShouldersTimer = 18.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + this->unk_1C2 = 4; + } + break; + + case 4: + Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.1f, this->actor.velocity.y); + Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f); + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + BossGanon_SetupWait(this, globalCtx); + } + break; + } + + if ((this->unk_19C == 35) || (this->unk_19C == 30) || (this->unk_19C == 25)) { + sp54 = this->actor.world.pos; + sp54.y = 0.0f; + BossGanonEff_SpawnDustLight(globalCtx, &sp54, 0, 3.0f, this->unk_19C - 25); + } + + if (this->unk_19C == 35) { + sp48 = this->actor.world.pos; + sp48.y = 0.0f; + BossGanonEff_SpawnShockwave(globalCtx, &sp48, 0, 3.0f); + } +} + +void BossGanon_SetupChargeBigMagic(BossGanon* this, GlobalContext* globalCtx) { + this->unk_1C2 = 0; + this->timers[0] = 30; + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->fwork[GDF_CENTER_POS] = 100.0f; + this->unk_1AA = Rand_ZeroFloat(20000.0f); + this->unk_1AC = 0; + this->actionFunc = BossGanon_ChargeBigMagic; +} + +void BossGanon_ChargeBigMagic(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + f32 targetPosX; + f32 targetPosZ; + Vec3f sp80; + Vec3f sp74; + Vec3f sp68; + s16 i; + + SkelAnime_Update(&this->skelAnime); + + targetPosX = Math_SinS(this->unk_1A2 * 1280); + targetPosX = targetPosX * this->fwork[GDF_CENTER_POS]; + + targetPosZ = Math_CosS(this->unk_1A2 * 1792); + targetPosZ = targetPosZ * this->fwork[GDF_CENTER_POS]; + + Math_ApproachF(&this->actor.world.pos.x, targetPosX, 0.05f, this->fwork[GDF_FWORK_0]); + Math_ApproachF(&this->actor.world.pos.z, targetPosZ, 0.05, this->fwork[GDF_FWORK_0]); + + Math_ApproachF(&this->fwork[GDF_CENTER_POS], 0.0f, 1.0f, 1.5f); + Math_ApproachF(&this->actor.world.pos.y, 200.0f, 0.05f, this->actor.velocity.y); + Math_ApproachF(&this->actor.velocity.y, 20.0f, 1.0f, 1.0f); + + switch (this->unk_1C2) { + case 0: + if (this->timers[0] == 0) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicChargeStartAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBigMagicChargeStartAnim, 0.0f); + this->unk_1C2 = 1; + } + break; + + case 1: + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicChargeHoldAnim); + Animation_MorphToLoop(&this->skelAnime, &gDorfBigMagicChargeHoldAnim, 0.0f); + this->unk_1C2 = 2; + this->timers[0] = 100; + } + break; + + case 2: + this->envLightMode = 2; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CHARGE_MASIC - SFX_FLAG); + this->unk_278.x = this->unk_2EC[0].x; + this->unk_278.y = this->unk_2EC[0].y + 50.0f + 30.0f; + this->unk_278.z = this->unk_2EC[0].z; + + Math_ApproachF(&this->unk_284, 0.25f, 0.1f, 0.006f); + Math_ApproachF(&this->unk_288, 255.0f, 1.0f, 255.0f); + Math_ApproachF(&this->unk_28C, 0.25f, 0.1f, 0.006f); + + if ((this->timers[0] > 20) && (this->timers[0] < 60)) { + Math_ApproachF(&this->unk_290, 255.0f, 1.0f, 15.0f); + } + + if (this->timers[0] == 0) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicWindupAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBigMagicWindupAnim, 0.0f); + this->unk_1C2 = 3; + this->timers[0] = 6; + this->timers[1] = 15; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DARKWAVE); + break; + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x3E8); + + if (this->timers[0] < -4) { + for (i = 0; i < ARRAY_COUNT(this->unk_294); i++) { + Math_ApproachF(&this->unk_294[i], 0.0f, 1.0f, 40.0f); + } + } else if ((this->timers[0] >= 7) && (this->timers[0] < 26)) { + if (this->unk_1AC < ARRAY_COUNT(this->unk_294)) { + this->unk_1AC++; + } + + for (i = 0; i < this->unk_1AC; i++) { + Math_ApproachF(&this->unk_294[i], 200.0f, 1.0f, 40.0f); + } + } + + if (this->timers[0] <= 30) { + Math_ApproachF(&this->unk_284, 0.4f, 0.5f, 0.017f); + this->unk_28C = this->unk_284; + } + + if (this->timers[0] <= 30) { + Math_ApproachF(&this->unk_2D0, 45.0f, 0.1f, 10.0f); + this->lensFlareTimer = 1; + this->lensFlareMode = 2; + gCustomLensFlarePos = this->unk_278; + } + + if (this->timers[0] == 47) { + this->unk_274 = 1; + } + + if (this->timers[0] == 46) { + this->unk_274 = 2; + } + + if (this->timers[0] == 45) { + this->unk_274 = 3; + } + + if (this->timers[0] == 44) { + this->unk_274 = 4; + } + + if (this->timers[0] == 43) { + this->unk_274 = 5; + } + + if (this->timers[0] == 42) { + this->unk_274 = 6; + } + + if (this->timers[0] > 30) { + sp74.x = 0.0f; + sp74.y = Rand_ZeroFloat(10.0f) + 150.0f; + sp74.z = 0.0f; + + Matrix_RotateY(BINANG_TO_RAD(this->actor.yawTowardsPlayer), MTXMODE_NEW); + Matrix_RotateZ(Rand_ZeroFloat(65536.0f), MTXMODE_APPLY); + Matrix_MultVec3f(&sp74, &sp68); + + sp80.x = this->unk_278.x + sp68.x; + sp80.y = this->unk_278.y + sp68.y; + sp80.z = this->unk_278.z + sp68.z; + + BossGanonEff_SpawnBlackDot(globalCtx, &sp80, 20.0f); + } + break; + + case 3: + this->envLightMode = 2; + + for (i = 0; i < ARRAY_COUNT(this->unk_294); i++) { + Math_ApproachF(&this->unk_294[i], 0.0f, 1.0f, 40.0f); + } + + if (this->timers[0] == 1) { + sCape->attachLeftArmTimer = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + } + + if (this->timers[0] == 0) { + Math_ApproachZeroF(&this->unk_284, 1.0f, 0.08f); + this->unk_28C = this->unk_284; + Math_ApproachZeroF(&this->unk_2D0, 1.0f, 10.0f); + Math_ApproachF(&this->unk_278.x, this->unk_1FC.x, 0.5f, 30.0f); + Math_ApproachF(&this->unk_278.y, this->unk_1FC.y, 0.5f, 30.0f); + Math_ApproachF(&this->unk_278.z, this->unk_1FC.z, 0.5f, 30.0f); + } + + if (this->timers[1] == 0) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicThrowAnim); + Animation_MorphToLoop(&this->skelAnime, &gDorfBigMagicThrowAnim, 0.0f); + this->unk_1C2 = 4; + this->unk_288 = 0.0f; + this->unk_290 = 0.0f; + this->unk_284 = 0.0f; + this->unk_28C = 0.0f; + } + break; + + case 4: + this->envLightMode = 2; + + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + for (i = 0; i < 5; i++) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_1FC.x, + this->unk_1FC.y, this->unk_1FC.z, 0, this->actor.yawTowardsPlayer, 0, 0x104 + i); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BIGMASIC); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW_BIG); + } + + if (Animation_OnFrame(&this->skelAnime, 3.0f)) { + sCape->attachShouldersTimer = 26.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicThrowEndAnim); + Animation_MorphToLoop(&this->skelAnime, &gDorfBigMagicThrowEndAnim, 0.0f); + this->unk_1C2 = 5; + } + break; + + case 5: + this->envLightMode = 2; + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + BossGanon_SetupWait(this, globalCtx); + } + break; + } +} + +void BossGanon_SetupWait(BossGanon* this, GlobalContext* globalCtx) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + Animation_MorphToLoop(&this->skelAnime, &gDorfFloatAnim, -10.0f); + this->actionFunc = BossGanon_Wait; + this->fwork[GDF_FWORK_0] = 0.0f; + this->timers[0] = (s16)Rand_ZeroFloat(64.0f) + 30; + this->unk_1C2 = 0; + sCape->minY = 2.0f; +} + +void BossGanon_Wait(BossGanon* this, GlobalContext* globalCtx) { + f32 sin; + s32 pad; + f32 cos; + Player* player = GET_PLAYER(globalCtx); + + this->legSwayEnabled = true; + + sCape->backPush = -3.0f; + sCape->backSwayMagnitude = 0.25f; + sCape->sideSwayMagnitude = -3.0f; + sCape->minDist = 20.0f; + + SkelAnime_Update(&this->skelAnime); + + if ((this->unk_1C2 == 0) && !(player->actor.world.pos.y < 0.0f)) { + if (!(player->stateFlags1 & 0x2000) && (fabsf(player->actor.world.pos.x) < 110.0f) && + (fabsf(player->actor.world.pos.z) < 110.0f)) { + BossGanon_SetupPoundFloor(this, globalCtx); + } else if ((this->timers[0] == 0) && !(player->stateFlags1 & 0x2000)) { + this->timers[0] = (s16)Rand_ZeroFloat(30.0f) + 30; + + if ((s8)this->actor.colChkInfo.health >= 20) { + BossGanon_SetupChargeLightBall(this, globalCtx); + } else if (Rand_ZeroOne() >= 0.5f) { + if ((Rand_ZeroOne() >= 0.5f) || (this->actor.xzDistToPlayer > 350.0f)) { + BossGanon_SetupChargeBigMagic(this, globalCtx); + } else { + BossGanon_SetupPoundFloor(this, globalCtx); + } + } else { + BossGanon_SetupChargeLightBall(this, globalCtx); + } + } + } + + sin = Math_SinS(this->unk_1A2 * 1280) * 100.0f; + cos = Math_CosS(this->unk_1A2 * 1792) * 100.0f; + + Math_ApproachF(&this->actor.world.pos.x, sin, 0.05f, this->fwork[GDF_FWORK_0]); + Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.05f, this->fwork[GDF_FWORK_0] * 0.2f); + Math_ApproachF(&this->actor.world.pos.z, cos, 0.05f, this->fwork[GDF_FWORK_0]); + Math_ApproachF(&this->fwork[GDF_FWORK_0], 50.0f, 1.0f, 0.5f); + + this->actor.velocity.x = this->actor.world.pos.x - this->actor.prevPos.x; + this->actor.velocity.z = this->actor.world.pos.z - this->actor.prevPos.z; + + sin = Math_SinS(this->unk_1A2 * 1500); + this->actor.velocity.y = this->fwork[GDF_FWORK_0] * sin * 0.04f; + this->actor.world.pos.y += this->actor.velocity.y; + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0xBB8); + func_80078914(&this->actor.projectedPos, NA_SE_EN_FANTOM_FLOAT - SFX_FLAG); +} + +void BossGanon_SetupChargeLightBall(BossGanon* this, GlobalContext* globalCtx) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfChargeLightBallAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfChargeLightBallAnim, -3.0f); + this->actionFunc = BossGanon_ChargeLightBall; + this->timers[0] = 25; +} + +void BossGanon_ChargeLightBall(BossGanon* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + sCape->backPush = -3.0f; + sCape->backSwayMagnitude = 1.25f; + sCape->sideSwayMagnitude = -2.0f; + sCape->minDist = 10.0f; + + if (this->timers[0] < 17) { + this->envLightMode = 1; + } + + if (this->timers[0] == 17) { + this->unk_26C = 10; + this->unk_270 = Rand_ZeroFloat(M_PI); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_SPARK); + } + + if (this->timers[0] < 10) { + this->unk_258 += (Rand_ZeroFloat(M_PI / 2) + (M_PI / 2)); + Math_ApproachF(&this->handLightBallScale, 10.0f, 0.5f, 1.25f); + + if (this->timers[0] == 0) { + BossGanon_SetupPlayTennis(this, globalCtx); + } + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x7D0); + + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + + Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f); + Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f); + + this->actor.velocity.y = Math_SinS(this->unk_1A2 * 1500) * 2.0f; + this->actor.world.pos.y += this->actor.velocity.y; +} + +void BossGanon_SetupPlayTennis(BossGanon* this, GlobalContext* globalCtx) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfThrowAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfThrowAnim, 0.0f); + this->actionFunc = BossGanon_PlayTennis; +} + +void BossGanon_PlayTennis(BossGanon* this, GlobalContext* globalCtx) { + static AnimationHeader* volleyAnims[] = { &gDorfVolleyLeftAnim, &gDorfVolleyRightAnim }; + static s16 capeRightArmDurations[] = { 26, 20 }; + s16 rand; + + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->handLightBallScale, 1.0f, 0.2f); + + switch (this->unk_1C2) { + case 0: + this->envLightMode = 1; + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->unk_1C2 = 1; + Animation_MorphToLoop(&this->skelAnime, &gDorfFloatAnim, 0.0f); + } + + if (this->skelAnime.curFrame <= 12.0f) { + this->lensFlareTimer = 2; + this->lensFlareMode = 2; + gCustomLensFlarePos = this->unk_260; + } + + if (Animation_OnFrame(&this->skelAnime, 12.0f)) { + this->handLightBallScale = 0.0f; + } + + if (Animation_OnFrame(&this->skelAnime, 11.0f)) { + this->unk_25C = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW_MASIC); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_260.x, + this->unk_260.y, this->unk_260.z, 0, 0, 0, 0x64); + } + break; + + case 1: + if (this->startVolley) { + rand = Rand_ZeroOne() * 1.99f; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(volleyAnims[rand]); + Animation_MorphToPlayOnce(&this->skelAnime, volleyAnims[rand], 0.0f); + sCape->attachRightArmTimer = capeRightArmDurations[rand]; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + this->startVolley = false; + } + break; + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x7D0); + + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + + Math_SmoothStepToF(&this->actor.velocity.x, 0.0f, 1.0f, 0.5f, 0.0f); + Math_SmoothStepToF(&this->actor.velocity.z, 0.0f, 1.0f, 0.5f, 0.0f); + + this->actor.velocity.y = Math_SinS(this->unk_1A2 * 1500) * 2.0f; + this->actor.world.pos.y += this->actor.velocity.y; +} + +void BossGanon_SetupBlock(BossGanon* this, GlobalContext* globalCtx) { + if ((this->actionFunc != BossGanon_Block) || (this->unk_1C2 != 0)) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBlockAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBlockAnim, 0.0f); + this->actionFunc = BossGanon_Block; + } + + this->unk_1C2 = 0; + sCape->attachLeftArmTimer = this->timers[0] = 10; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + this->handLightBallScale = 0.0f; +} + +void BossGanon_Block(BossGanon* this, GlobalContext* globalCtx) { + this->collider.base.colType = 9; + SkelAnime_Update(&this->skelAnime); + sCape->backPush = -9.0f; + sCape->backSwayMagnitude = 0.25f; + sCape->sideSwayMagnitude = -2.0f; + sCape->minDist = 13.0f; + + if (this->unk_1C2 == 0) { + if (this->timers[0] == 0) { + this->unk_1C2 = 1; + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBlockReleaseAnim, 0.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBlockReleaseAnim); + SkelAnime_Update(&this->skelAnime); + sCape->attachShouldersTimer = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + } + } else { + sCape->sideSwayMagnitude = -13.0f; + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + BossGanon_SetupWait(this, globalCtx); + } + } + + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.world.pos.z += this->actor.velocity.z; + + Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f); + Math_ApproachZeroF(&this->actor.velocity.y, 1.0f, 0.5f); + Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f); +} + +void BossGanon_SetupHitByLightBall(BossGanon* this, GlobalContext* globalCtx) { + s16 i; + + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfBigMagicHitAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfBigMagicHitAnim, 0); + this->timers[0] = 70; + sCape->attachRightArmTimer = sCape->attachLeftArmTimer = 0; + + for (i = 1; i < 15; i++) { + this->unk_4E4[i] = D_808E4C58[i]; + } + + this->unk_2E6 = 80; + this->unk_2E8 = 0; + this->actionFunc = BossGanon_HitByLightBall; + this->actor.velocity.x = this->actor.velocity.z = 0.0f; + this->unk_1C2 = 0; + this->unk_1A6 = 15; + this->unk_508 = 6.0f; +} + +void BossGanon_HitByLightBall(BossGanon* this, GlobalContext* globalCtx) { + s16 i; + Vec3f sp50; + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_1C2 == 0) { + BossGanonEff_SpawnShock(globalCtx, 1500.0f, GDF_SHOCK_DORF_YELLOW); + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLightArrowWaitAnim); + Animation_MorphToLoop(&this->skelAnime, &gDorfLightArrowWaitAnim, 0.0f); + this->unk_1C2 = 1; + } + } else if (this->unk_1C2 == 1) { + BossGanonEff_SpawnShock(globalCtx, 1000.0f, GDF_SHOCK_DORF_YELLOW); + + if (this->timers[0] == 0) { + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp3Anim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f); + this->unk_1C2 = 2; + SkelAnime_Update(&this->skelAnime); + sCape->attachShouldersTimer = 18.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_RESTORE); + this->timers[2] = 130; + } + } else { + if (Animation_OnFrame(&this->skelAnime, 7.0f)) { + for (i = 0; i < 100; i++) { + sp50.x = Rand_CenteredFloat(25.0f); + sp50.y = Rand_CenteredFloat(25.0f); + sp50.z = Rand_CenteredFloat(25.0f); + + BossGanonEff_SpawnSparkle(globalCtx, &this->unk_1FC, &sp50, &sZeroVec, Rand_ZeroFloat(200.0f) + 500.0f, + 0x14); + } + Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_WATER2); + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + BossGanon_SetupWait(this, globalCtx); + } + } + + this->actor.velocity.y = Math_SinS(this->unk_1A2 * 1500) * 2.0f; + this->actor.world.pos.y += this->actor.velocity.y; +} + +void BossGanon_SetupVulnerable(BossGanon* this, GlobalContext* globalCtx) { + s16 i; + + if (this->actionFunc != BossGanon_Vulnerable) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLightArrowHitAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfLightArrowHitAnim, 0.0f); + sCape->attachRightArmTimer = sCape->attachLeftArmTimer = 0; + this->actionFunc = BossGanon_Vulnerable; + + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.z = 0.0f; + + this->unk_1C2 = 0; + sCape->backPush = -4.0f; + sCape->backSwayMagnitude = 0.75f; + sCape->sideSwayMagnitude = -3.0f; + sCape->minDist = 20.0f; + + for (i = 0; i < 10; i++) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_1FC.x, + this->unk_1FC.y, this->unk_1FC.z, Rand_CenteredFloat(0x8000), + (s16)Rand_CenteredFloat(0x8000) + this->actor.yawTowardsPlayer, 0, 0xC8 + i); + } + + this->unk_1A4 = 0; + this->unk_288 = 0.0f; + this->unk_290 = 0.0f; + this->unk_284 = 0.0f; + this->unk_28C = 0.0f; + } +} + +void BossGanon_Vulnerable(BossGanon* this, GlobalContext* globalCtx) { + s16 i; + Vec3f sp40; + + if (this->timers[3] == 0) { + this->actor.flags |= ACTOR_FLAG_0; + } + + SkelAnime_Update(&this->skelAnime); + + this->envLightMode = 1; + this->actor.world.pos.y += this->actor.velocity.y; + + if (this->unk_1A4 < 0x28) { + Math_ApproachF(&this->unk_508, 4.0f, 0.1f, 0.1f); + } + + if ((this->unk_1A4 >= 0x28) && (this->unk_1A4 < 0x37)) { + Math_ApproachF(&this->unk_508, 0.0f, 1.0f, 0.5f); + } + + if (this->unk_1A4 >= 0x37) { + Math_ApproachF(&this->unk_508, 5.0f, 0.1f, 0.15f); + this->shockGlow = true; + } + + switch (this->unk_1C2) { + case 0: + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->unk_1C2 = 1; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLightEnergyHitAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfLightEnergyHitAnim, 0.0f); + } + break; + + case 1: + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->unk_1C2 = 2; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfKneelVulnerableAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfKneelVulnerableAnim, 0.0f); + } + break; + + case 2: + sCape->minDist = 0.0f; + this->actor.velocity.y = this->actor.velocity.y - 0.5f; + + if (this->actor.world.pos.y < 40.0f) { + this->actor.world.pos.y = 40.0f; + this->actor.velocity.y = 0.0f; + this->unk_1C2 = 3; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfLandAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfLandAnim, 0.0f); + this->timers[0] = 70; + this->actor.flags |= ACTOR_FLAG_10; + } + break; + + case 3: + if (this->timers[0] == 68) { + this->unk_19F = 1; + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->unk_1C2 = 4; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfVulnerableAnim); + Animation_MorphToLoop(&this->skelAnime, &gDorfVulnerableAnim, 0.0f); + } + break; + + case 4: + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DOWN); + } + + if (this->timers[0] == 0) { + this->unk_1C2 = 5; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp1Anim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp1Anim, 0.0f); + this->unk_2D4 = 80; + + for (i = 1; i < 15; i++) { + this->unk_4E4[i] = Rand_ZeroFloat(10.0f); + } + + this->unk_2E6 = 80; + this->unk_2E8 = 0; + this->actor.flags &= ~ACTOR_FLAG_10; + } + break; + + case 5: + BossGanonEff_SpawnShock(globalCtx, 1000.0f, GDF_SHOCK_DORF_YELLOW); + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->unk_1C2 = 6; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp2Anim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp2Anim, 0.0f); + sCape->minDist = 20.0f; + this->unk_19F = 1; + } + break; + + case 6: + this->envLightMode = 0; + Math_ApproachF(&this->actor.world.pos.y, 200.0f, 0.1f, 1000.0f); + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->unk_1C2 = 7; + } + break; + + case 7: + this->envLightMode = 0; + Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.05f, 30.0f); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfGetUp3Anim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfGetUp3Anim, 0.0f); + this->unk_1C2 = 8; + SkelAnime_Update(&this->skelAnime); + sCape->attachShouldersTimer = 18.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_MANTLE); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_RESTORE); + break; + + case 8: + this->envLightMode = 0; + + if (Animation_OnFrame(&this->skelAnime, 7.0f)) { + for (i = 0; i < 100; i++) { + sp40.x = Rand_CenteredFloat(25.0f); + sp40.y = Rand_CenteredFloat(25.0f); + sp40.z = Rand_CenteredFloat(25.0f); + BossGanonEff_SpawnSparkle(globalCtx, &this->unk_1FC, &sp40, &sZeroVec, + Rand_ZeroFloat(200.0f) + 500.0f, 0x14); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_WATER2); + this->timers[3] = 50; + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + BossGanon_SetupWait(this, globalCtx); + } + break; + } +} + +void BossGanon_SetupDamaged(BossGanon* this, GlobalContext* globalCtx) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfDamageAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gDorfDamageAnim, 0.0f); + this->actionFunc = BossGanon_Damaged; +} + +void BossGanon_Damaged(BossGanon* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_0; + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_1A4 >= 0x37) { + Math_ApproachF(&this->unk_508, 5.0f, 0.1f, 0.15f); + this->shockGlow = true; + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GDF_FWORK_1])) { + this->actionFunc = BossGanon_Vulnerable; + this->unk_1C2 = 4; + this->fwork[GDF_FWORK_1] = Animation_GetLastFrame(&gDorfVulnerableAnim); + Animation_MorphToLoop(&this->skelAnime, &gDorfVulnerableAnim, 0.0f); + } +} + +void BossGanon_UpdateDamage(BossGanon* this, GlobalContext* globalCtx) { + s16 i; + s16 j; + ColliderInfo* acHitInfo; + + if (this->collider.base.acFlags & 2) { + this->unk_2D4 = 2; + this->collider.base.acFlags &= ~2; + acHitInfo = this->collider.info.acHitInfo; + + if ((this->actionFunc == BossGanon_HitByLightBall) || (this->actionFunc == BossGanon_ChargeBigMagic)) { + if (acHitInfo->toucher.dmgFlags & 0x2000) { + BossGanon_SetupVulnerable(this, globalCtx); + this->timers[2] = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE1); + this->unk_1A6 = 15; + } + } else if ((this->actionFunc == BossGanon_Vulnerable) && (this->unk_1C2 >= 3)) { + if (!(acHitInfo->toucher.dmgFlags & 0x80)) { + u8 hitWithSword = false; + u8 damage; + Vec3f sp50; + u32 flags; + + for (i = 0; i < 30; i++) { + sp50.x = Rand_CenteredFloat(20.0f); + sp50.y = Rand_CenteredFloat(20.0f); + sp50.z = Rand_CenteredFloat(20.0f); + BossGanonEff_SpawnSparkle(globalCtx, &this->unk_1FC, &sp50, &sZeroVec, + Rand_ZeroFloat(200.0f) + 500.0f, 0x1E); + } + + damage = flags = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags); + + if (flags == 0) { + damage = 2; + } else { + hitWithSword = true; + } + + if (((s8)this->actor.colChkInfo.health >= 3) || hitWithSword) { + this->actor.colChkInfo.health -= damage; + } + + for (i = 0; i < ARRAY_COUNT(sCape->strands); i++) { + for (j = 1; j < 12; j++) { + sCape->strands[i].velocities[j].x = Rand_CenteredFloat(15.0f); + sCape->strands[i].velocities[j].z = Rand_CenteredFloat(15.0f); + } + } + + if ((s8)this->actor.colChkInfo.health <= 0) { + BossGanon_SetupDeathCutscene(this, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DEAD); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DD_THUNDER); + func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE); + Audio_QueueSeqCmd(0x100100FF); + this->screenFlashTimer = 4; + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY); + BossGanon_SetupDamaged(this, globalCtx); + this->unk_1A6 = 15; + sCape->tearTimer = 1; + } + } + } else if (acHitInfo->toucher.dmgFlags & 0x1F8A4) { + Audio_PlayActorSound2(&this->actor, 0); + + for (i = 0; i < ARRAY_COUNT(sCape->strands); i++) { + for (j = 1; j < 12; j++) { + sCape->strands[i].velocities[j].x = Rand_CenteredFloat(15.0f); + sCape->strands[i].velocities[j].z = Rand_CenteredFloat(15.0f); + } + } + } + } +} + +static f32 D_808E4D44[] = { + 1.0f, 3.0f, 0.0f, 7.0f, 13.0f, 4.0f, 6.0f, 11.0f, 5.0f, 2.0f, 8.0f, 14.0f, 10.0f, 12.0f, 9.0f, +}; + +void BossGanon_Update(Actor* thisx, GlobalContext* globalCtx2) { + BossGanon* this = (BossGanon*)thisx; + GlobalContext* globalCtx = globalCtx2; + f32 legRotX; + f32 legRotY; + f32 legRotZ; + Player* player = GET_PLAYER(globalCtx); + s16 i; + f32 sin; + f32 cos; + Vec3f shardPos; + Vec3f shardVel; + Vec3f spE8; + s16 i2; + s16 j; + Vec3f spD8; + Vec3f platformCheckPos; + Actor* explosive; + Vec3f spBC; + Vec3f spB0; + Vec3f platCheckPosBomb; + Actor* prop; + BgGanonOtyuka* platform; + f32 targetLensFlareScale; + f32 xOffset; + f32 zOffset; + + if ((this->actionFunc != BossGanon_IntroCutscene) && (this->actionFunc != BossGanon_DeathAndTowerCutscene)) { + BossGanon_SetAnimationObject(this, globalCtx, OBJECT_GANON_ANIME1); + } else { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animBankIndex].segment); + } + + if (this->windowShatterState != GDF_WINDOW_SHATTER_OFF) { + BossGanon_ShatterWindows(this->windowShatterState); + shardVel.y = 0.0f; + + for (i = 0; i < 10; i++) { + shardPos.y = Rand_ZeroFloat(240.0f) + 20.0f; + + if (Rand_ZeroOne() < 0.5f) { + shardPos.x = 463; + shardPos.z = Rand_ZeroFloat(463.0f); + + shardVel.x = Rand_ZeroFloat(2.0f); + shardVel.z = Rand_ZeroFloat(1.0f); + } else { + shardPos.z = 463; + shardPos.x = Rand_ZeroFloat(463.0f); + + shardVel.z = Rand_ZeroFloat(2.0f); + shardVel.x = Rand_ZeroFloat(1.0f); + } + + BossGanonEff_SpawnWindowShard(globalCtx, &shardPos, &shardVel, Rand_ZeroFloat(0.075f) + 0.08f); + } + } + + this->collider.base.colType = 3; + sCape->gravity = -3.0f; + this->shockGlow = false; + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_1A2++; + this->unk_1A4++; + + // block players attack if hes shooting something + if ((this->actionFunc == BossGanon_Wait) || (this->actionFunc == BossGanon_Block)) { + if (player->unk_A73 != 0) { + BossGanon_SetupBlock(this, globalCtx); + } + } + + this->actionFunc(this, globalCtx); + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + if (this->unk_1A6 != 0) { + this->unk_1A6--; + } + + if (this->unk_2D4 != 0) { + this->unk_2D4--; + } + + if (this->unk_2E8 != 0) { + this->unk_2E8--; + } + + if (this->unk_2E6 != 0) { + this->unk_2E6--; + } + + if (this->unk_19C != 0) { + this->unk_19C--; + } + + if (this->csState == 0) { + BossGanon_UpdateDamage(this, globalCtx); + BossGanon_SetColliderPos(&this->unk_1FC, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->unk_2D4 == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if ((this->actionFunc != BossGanon_HitByLightBall) && (this->actionFunc != BossGanon_Vulnerable) && + (this->actionFunc != BossGanon_Damaged)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + } + + if (this->legSwayEnabled) { + sin = Math_SinS(-this->actor.shape.rot.y); + cos = Math_CosS(-this->actor.shape.rot.y); + + legRotX = ((this->actor.velocity.z * sin) + (cos * this->actor.velocity.x)) * 300.0f; + legRotY = ((-sin * this->actor.velocity.x) + (cos * this->actor.velocity.z)) * 300.0f; + legRotZ = (Math_SinS(this->unk_1A2 * 2268) * -500.0f) - 500.0f; + } else { + legRotY = legRotX = legRotZ = 0.0f; + } + + this->legSwayEnabled = false; + + Math_SmoothStepToF(&this->legRot.x, legRotX, 1.0f, 600.0f, 0.0f); + Math_SmoothStepToF(&this->legRot.y, legRotY, 1.0f, 600.0f, 0.0f); + Math_SmoothStepToF(&this->legRot.z, legRotZ, 1.0f, 100.0f, 0.0f); + + if (this->timers[2] == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_LAUGH); + } + + if (this->timers[2] == 100) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_ST_LAUGH); + this->timers[2] = 0; + } + + if ((this->unk_2E6 != 0) || (this->unk_2E8 != 0)) { + for (i = 1; i < ARRAY_COUNT(this->unk_49C); i++) { + if (this->unk_4E4[i] != 0) { + this->unk_4E4[i]--; + Math_ApproachF(&this->unk_49C[i], this->unk_508, 1.0f, 2.0f); + } else { + Math_ApproachZeroF(&this->unk_49C[i], 1.0f, 0.2f); + } + } + + // player hit, spawn shock and play sound + if (this->unk_2E8 != 0) { + func_80078914(&player->actor.projectedPos, NA_SE_PL_SPARK - SFX_FLAG); + BossGanonEff_SpawnShock(globalCtx, 700.0f, GDF_SHOCK_PLAYER_YELLOW); + } + } + + if (this->unk_19F != 0) { + this->unk_19F = 0; + spE8 = this->actor.world.pos; + spE8.y = 0.0f; + BossGanonEff_SpawnDustDark(globalCtx, &spE8, 0.2, 0.7f); + BossGanonEff_SpawnDustDark(globalCtx, &spE8, 0.3f, 0.8f); + } + + if (this->unk_26C != 0) { + this->unk_26C--; + + if (this->unk_26C == 0) { + BossGanonEff_SpawnLightning(globalCtx, 1.0f, 0.0f, 0.0f); + } + + BossGanonEff_SpawnLightning(globalCtx, 1.0f, D_808E4D44[this->unk_26C] * (M_PI / 5) + this->unk_270, + Rand_CenteredFloat(M_PI / 5) + (M_PI / 2)); + } + + // see if light ball hit and should knock platform down? + if ((this->unk_19C != 0) && (this->unk_19E < 4)) { + if ((this->unk_19A == 0) && (this->unk_19C == 20)) { + this->unk_19A = 1; + platformCheckPos.x = -180.0f; + platformCheckPos.y = 0.0f; + + for (i2 = 0; i2 < 4; i2++) { + for (j = 0, platformCheckPos.z = -180.0f; j < 4; j++) { + BossGanon_CheckFallingPlatforms(this, globalCtx, &platformCheckPos); + platformCheckPos.z += 120.0f; + } + + platformCheckPos.x += 120.0f; + } + } else if (this->unk_19C < 30) { + spD8.x = 0.0f; + spD8.y = 0.0f; + spD8.z = 15.0f * (30.0f - this->unk_19C); + + Matrix_RotateY(Rand_ZeroFloat(6.2831855f), MTXMODE_NEW); + Matrix_MultVec3f(&spD8, &platformCheckPos); + + this->unk_19E += BossGanon_CheckFallingPlatforms(this, globalCtx, &platformCheckPos); + } + } + + // see if a bomb exploded near a group of platforms and if they should fall + explosive = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + + while (explosive != NULL) { + if (explosive->params != BOMB_EXPLOSION) { + explosive = explosive->next; + } else { + for (i = 0; i < 8; i++) { + spBC.x = 0.0f; + spBC.y = 0.0f; + spBC.z = 60.0f; + + Matrix_RotateY(i * (M_PI / 4), MTXMODE_NEW); + Matrix_MultVec3f(&spBC, &spB0); + + platCheckPosBomb.x = explosive->world.pos.x + spB0.x; + platCheckPosBomb.y = explosive->world.pos.y; + platCheckPosBomb.z = explosive->world.pos.z + spB0.z; + + BossGanon_CheckFallingPlatforms(this, globalCtx, &platCheckPosBomb); + } + + explosive = explosive->next; + } + } + + BossGanon_UpdateEffects(globalCtx); + + prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + + // if a platform is lit up, change the room lighting + while (prop != NULL) { + if (prop->id != ACTOR_BG_GANON_OTYUKA) { + prop = prop->next; + } else { + platform = (BgGanonOtyuka*)prop; + + if (platform->flashState != 0) { + this->envLightMode = 1; + break; + } + + prop = prop->next; + } + } + + globalCtx->envCtx.unk_BF = 0; + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_DC = 2; + + switch (this->envLightMode) { + case -1: + break; + case 0: + Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.0f, 1.0f, 0.02f); + break; + case 1: + globalCtx->envCtx.unk_BD = 1; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.1f); + break; + case 2: + globalCtx->envCtx.unk_BD = 1; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.02f); + break; + case 3: + globalCtx->envCtx.unk_BD = 3; + globalCtx->envCtx.unk_D8 = 1.0f; + break; + case 35: + globalCtx->envCtx.unk_BD = 0; + globalCtx->envCtx.unk_D8 = 1.0f; + break; + case 4: + globalCtx->envCtx.unk_BD = 4; + globalCtx->envCtx.unk_D8 = 1.0f; + break; + case 5: + globalCtx->envCtx.unk_BE = 5; + globalCtx->envCtx.unk_BD = 3; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.075f); + break; + case 6: + globalCtx->envCtx.unk_BE = 5; + globalCtx->envCtx.unk_D8 = 0.0f; + break; + case 65: + globalCtx->envCtx.unk_BE = 3; + globalCtx->envCtx.unk_BD = 6; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + break; + case 7: + globalCtx->envCtx.unk_BE = 7; + globalCtx->envCtx.unk_D8 = 0.0f; + break; + case 75: + globalCtx->envCtx.unk_BE = 4; + globalCtx->envCtx.unk_BD = 8; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + break; + case 8: + globalCtx->envCtx.unk_BE = 3; + globalCtx->envCtx.unk_BD = 9; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f); + break; + case 9: + globalCtx->envCtx.unk_BE = 3; + globalCtx->envCtx.unk_BD = 0xA; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + break; + case 10: + globalCtx->envCtx.unk_BE = 3; + globalCtx->envCtx.unk_BD = 0xB; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f); + this->unk_1A4 = 0; + break; + case 11: + globalCtx->envCtx.unk_BE = 0xC; + globalCtx->envCtx.unk_BD = 0xB; + Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_CosS(this->unk_1A4 * 0x1800) * 0.5f) + 0.5f, 1.0f, 1.0f); + break; + case 12: + globalCtx->envCtx.unk_BE = 0xC; + globalCtx->envCtx.unk_BD = 3; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f); + break; + case 13: + globalCtx->envCtx.unk_BD = 0xD; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.025f); + break; + case 14: + globalCtx->envCtx.unk_BD = 0xE; + globalCtx->envCtx.unk_D8 = 1.0f; + break; + case 15: + globalCtx->envCtx.unk_BE = 0xE; + globalCtx->envCtx.unk_BD = 0xF; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.01f); + break; + case 16: + globalCtx->envCtx.unk_BE = 0x10; + globalCtx->envCtx.unk_BD = 0xF; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + break; + case 20: + globalCtx->envCtx.unk_BE = 2; + globalCtx->envCtx.unk_BD = 1; + break; + default: + break; + } + + this->envLightMode = 0; + + if (this->whiteFillAlpha != 0) { + globalCtx->envCtx.screenFillColor[3] = (s8)(u8)this->whiteFillAlpha; + globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] = + globalCtx->envCtx.screenFillColor[2] = 255; + globalCtx->envCtx.fillScreen = true; + } else if (this->screenFlashTimer != 0) { + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] = + globalCtx->envCtx.screenFillColor[2] = 255; + + globalCtx->envCtx.screenFillColor[3] = ((this->screenFlashTimer % 2) != 0) ? 100 : 0; + + this->screenFlashTimer--; + } else { + globalCtx->envCtx.fillScreen = globalCtx->envCtx.screenFillColor[3] = 0; + } + + if (this->lensFlareTimer != 0) { + this->lensFlareTimer--; + + if (this->lensFlareMode == 1) { + targetLensFlareScale = 40.0f; + } else if (this->lensFlareMode == 4) { + targetLensFlareScale = 25.0f; + } else { + targetLensFlareScale = 10.0f; + } + + Math_ApproachF(&this->lensFlareScale, targetLensFlareScale, 0.3f, 10.0f); + } else { + Math_ApproachZeroF(&this->lensFlareScale, 1.0f, 5.0f); + + if (this->lensFlareScale == 0.0f) { + this->lensFlareMode = 0; + } + } + + if (this->lensFlareMode != 0) { + gCustomLensFlareOn = true; + + if (this->lensFlareMode == 1) { + gCustomLensFlarePos = this->actor.world.pos; + } + + gLensFlareScale = this->lensFlareScale; + gLensFlareColorIntensity = 10.0f; + gLensFlareScreenFillAlpha = 0; + } else { + gCustomLensFlareOn = false; + } + + if (this->unk_274 != 0) { + i = this->unk_274 - 1; + + this->unk_278.x = this->unk_2EC[0].x; + this->unk_278.y = this->unk_2EC[0].y + 50.0f + 30.0f; + this->unk_278.z = this->unk_2EC[0].z; + + xOffset = (sinf(i * 1.2566371f) * 600.0f); + zOffset = (cosf(i * 1.2566371f) * 600.0f); + + // 5 or 6 light balls that go into the charge. not the same as the ones that he throws + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANON, this->unk_1FC.x + xOffset, + this->unk_1FC.y, this->unk_1FC.z + zOffset, 0, (s16)(i * 13107.2f) + 0x6000, 0, 0xFA + i); + this->unk_274 = 0; + } +} + +s32 BossGanon_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossGanon* this = (BossGanon*)thisx; + + switch (limbIndex) { + case 10: + if (this->useOpenHand) { + *dList = gDorfOpenHandDL; + } + break; + + case 20: + rot->y += this->legRot.x + this->legRot.z; + rot->z += this->legRot.y; + break; + + case 21: + if (this->legRot.y > 0.0f) { + rot->z += this->legRot.y; + } + break; + + case 22: + rot->y += this->legRot.x + this->legRot.z; + rot->z += this->legRot.y; + break; + + case 23: + rot->y += this->legRot.x - this->legRot.z; + rot->z += this->legRot.y; + break; + + case 24: + if (this->legRot.y > 0.0f) { + rot->z += this->legRot.y; + } + break; + + case 25: + rot->y += this->legRot.x - this->legRot.z; + rot->z += this->legRot.y; + break; + + default: + break; + } + + return 0; +} + +void BossGanon_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static s8 bodyPartLimbMap[] = { + -1, -1, 1, -1, 3, 4, 5, -1, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, 2, 12, 13, 14, 9, 10, 11, -1, -1, -1, -1, + }; + static Vec3f D_808E4DA0 = { -500.0f, 200.0f, -300.0f }; + static Vec3f D_808E4DAC = { -500.0f, 200.0f, 300.0f }; + static Vec3f D_808E4DB8 = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_808E4DC4 = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_808E4DD0 = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_808E4DDC = { 1300.0f, 0.0f, 0.0f }; + static Vec3f D_808E4DE8 = { 600.0f, 420.0f, 100.0f }; + s8 bodyPart; + BossGanon* this = (BossGanon*)thisx; + + bodyPart = bodyPartLimbMap[limbIndex]; + if (bodyPart >= 0) { + Matrix_MultVec3f(&D_808E4DB8, &this->unk_2EC[bodyPart]); + } + + if (limbIndex == 2) { + Matrix_MultVec3f(&D_808E4DB8, &this->unk_1FC); + } else if (limbIndex == 19) { + Matrix_MultVec3f(&D_808E4DB8, &this->actor.focus.pos); + } else if (limbIndex == 11) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7191); + + Matrix_MultVec3f(&D_808E4DB8, &this->unk_208); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7196), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_ganon_DL_00BE90)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7198); + } else if (limbIndex == 6) { + Matrix_MultVec3f(&D_808E4DC4, &this->unk_238); + } else if (limbIndex == 10) { + Matrix_MultVec3f(&D_808E4DD0, &this->unk_22C); + + if (this->unk_25C == 0) { + Matrix_MultVec3f(&D_808E4DDC, &this->unk_260); + } + + this->unk_25C = 0; + + if (this->triforceType == GDF_TRIFORCE_DORF) { + Matrix_MultVec3f(&D_808E4DE8, &this->triforcePos); + } + } else if (limbIndex == 4) { + Vec3f sp28 = D_808E4DA0; + + if (this->unk_198 == 1) { + sp28.x += -300.0f; + sp28.y += -300.0f; + sp28.z += 700.0f; + } else if (this->unk_198 == 2) { + sp28.x += -300.0f; + sp28.z += 700.0f; + } + + Matrix_MultVec3f(&sp28, &this->unk_220); + } else if (limbIndex == 8) { + Vec3f sp1C = D_808E4DAC; + + if (this->unk_198 == 1) { + sp1C.x += -300.0f; + sp1C.y += -300.0f; + sp1C.z += -700.0f; + } else if (this->unk_198 == 2) { + sp1C.x += -300.0f; + sp1C.y += 100.0f; + sp1C.z += -700.0f; + } + + Matrix_MultVec3f(&sp1C, &this->unk_214); + } +} + +void BossGanon_InitRand(s32 seedInit0, s32 seedInit1, s32 seedInit2) { + sSeed1 = seedInit0; + sSeed2 = seedInit1; + sSeed3 = seedInit2; +} + +f32 BossGanon_RandZeroOne(void) { + // Wichmann-Hill algorithm + f32 randFloat; + + sSeed1 = (sSeed1 * 171) % 30269; + sSeed2 = (sSeed2 * 172) % 30307; + sSeed3 = (sSeed3 * 170) % 30323; + + randFloat = (sSeed1 / 30269.0f) + (sSeed2 / 30307.0f) + (sSeed3 / 30323.0f); + + while (randFloat >= 1.0f) { + randFloat -= 1.0f; + } + + return fabsf(randFloat); +} + +void BossGanon_DrawShock(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 i; + + OPEN_DISPS(gfxCtx, "../z_boss_ganon.c", 7350); + + if ((this->unk_2E8 != 0) || (this->unk_2E6 != 0)) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + + if (this->unk_2E8 != 0) { + Player* player = GET_PLAYER(globalCtx); + + for (i = 0; i < ARRAY_COUNT(player->bodyPartsPos); i++) { + Matrix_Translate(player->bodyPartsPos[i].x, player->bodyPartsPos[i].y, player->bodyPartsPos[i].z, + MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_49C[i], this->unk_49C[i], this->unk_49C[i], MTXMODE_APPLY); + Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7384), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + } + } else { + for (i = 1; i < 15; i++) { + Matrix_Translate(this->unk_2EC[i].x, this->unk_2EC[i].y, this->unk_2EC[i].z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_49C[i], this->unk_49C[i], this->unk_49C[i], MTXMODE_APPLY); + + if (!this->shockGlow) { + Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY); + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7401), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->shockGlow) { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + (this->unk_1A2 + i) * -15, 32, 64)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 255, 170, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 128); + gSPDisplayList(POLY_XLU_DISP++, gDorfShockGlowDL); + } else { + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + } + } + } + } + + CLOSE_DISPS(gfxCtx, "../z_boss_ganon.c", 7465); +} + +void BossGanon_DrawHandLightBall(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 alpha; + + OPEN_DISPS(gfxCtx, "../z_boss_ganon.c", 7476); + + if (this->handLightBallScale > 0.0f) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + + if ((this->unk_1A2 % 2) != 0) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 100, 255, 0, 0); + } + + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + + Matrix_Translate(this->unk_260.x, this->unk_260.y, this->unk_260.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->handLightBallScale, this->handLightBallScale, this->handLightBallScale, MTXMODE_APPLY); + Matrix_RotateZ(this->unk_258, 1); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7510), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + + alpha = ((this->unk_1A2 % 2) != 0) ? 100 : 80; + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, alpha); + Matrix_Translate(this->unk_260.x, 0.0f, this->unk_260.z, MTXMODE_NEW); + Matrix_Scale(this->handLightBallScale * 0.75f, 1.0f, this->handLightBallScale * 0.75f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7531), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightCoreDL); + + CLOSE_DISPS(gfxCtx, "../z_boss_ganon.c", 7534); + } +} + +void BossGanon_DrawBigMagicCharge(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + f32 yRot; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 i; + + OPEN_DISPS(gfxCtx, "../z_boss_ganon.c", 7548); + + if (this->unk_284 > 0.0f) { + func_80093D84(globalCtx->state.gfxCtx); + + // light flecks + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, (s8)this->unk_290); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 128); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->unk_1A2 * -2, 0, 0x40, 0x40, 1, 0, + this->unk_1A2 * 0xA, 0x40, 0x40)); + Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_28C, this->unk_28C, this->unk_28C, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7588), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightFlecksDL); + + // background circle texture + Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_284, this->unk_284, this->unk_284, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7601), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 0, 100, (s8)this->unk_288); + gSPSegment( + POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0, this->unk_1A2 * -4, 0x20, 0x20)); + gSPDisplayList(POLY_XLU_DISP++, gDorfBigMagicBGCircleDL); + + // yellow background dot + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 170, 0, (s8)this->unk_288); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, this->unk_1A2 * 2, + this->unk_1A2 * -0x14, 0x40, 0x40)); + gSPDisplayList(POLY_XLU_DISP++, gDorfDotDL); + + // light ball material + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 100, 0); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + + // light ball geometry + Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_2D0, this->unk_2D0, this->unk_2D0, MTXMODE_APPLY); + Matrix_RotateZ((this->unk_1A2 * 10.0f) / 1000.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7673), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + + BossGanon_InitRand(this->unk_1AA + 1, 0x71AC, 0x263A); + Matrix_Translate(this->unk_278.x, this->unk_278.y, this->unk_278.z, MTXMODE_NEW); + Matrix_RotateY((this->unk_1A2 * 10.0f) / 1000.0f, MTXMODE_APPLY); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 0); + + yRot = BINANG_TO_RAD(this->actor.yawTowardsPlayer); + + for (i = 0; i < this->unk_1AC; i++) { + f32 xzRot = (BossGanon_RandZeroOne() - 0.5f) * M_PI * 1.5f; + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->unk_294[i]); + Matrix_Push(); + Matrix_RotateY(xzRot + yRot, MTXMODE_APPLY); + Matrix_RotateX((BossGanon_RandZeroOne() - 0.5f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ(xzRot, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 50.0f, MTXMODE_APPLY); + Matrix_Scale(4.0f, 4.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 7713), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightRayTriDL); + + Matrix_Pop(); + } + + CLOSE_DISPS(gfxCtx, "../z_boss_ganon.c", 7721); + } +} + +void BossGanon_DrawTriforce(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + + if (this->fwork[GDF_TRIFORCE_PRIM_A] > 0.0f) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7732); + + Matrix_Push(); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, (u8)this->fwork[GDF_TRIFORCE_PRIM_B], + (s8)this->fwork[GDF_TRIFORCE_PRIM_A]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, (u8)this->fwork[GDF_TRIFORCE_ENV_G], 0, 128); + + if (this->triforceType == GDF_TRIFORCE_PLAYER) { + Player* player = GET_PLAYER(globalCtx); + + this->triforcePos = player->bodyPartsPos[12]; + + this->triforcePos.x += -0.6f; + this->triforcePos.y += 3.0f; + this->triforcePos.z += -2.0f; + } else if (this->triforceType == GDF_TRIFORCE_ZELDA) { + this->triforcePos = sZelda->unk_31C; + + this->triforcePos.y += 1.8f; + this->triforcePos.z += 4.0f; + } + + Matrix_Translate(this->triforcePos.x, this->triforcePos.y, this->triforcePos.z, MTXMODE_NEW); + + if (this->triforceType == GDF_TRIFORCE_PLAYER) { + Matrix_RotateX(-1.4f, MTXMODE_APPLY); + Matrix_RotateZ(4.0f, MTXMODE_APPLY); + } else if (this->triforceType == GDF_TRIFORCE_ZELDA) { + Matrix_RotateY(1.5f, 1); + Matrix_RotateX(1.1f, 1); + Matrix_RotateZ(-0.99999994f, MTXMODE_APPLY); + } else { + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + } + + Matrix_Scale(this->fwork[GDF_TRIFORCE_SCALE], this->fwork[GDF_TRIFORCE_SCALE], 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7779), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gDorfTriforceDL)); + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7782); + } +} + +void BossGanon_DrawDarkVortex(BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + + if (this->fwork[GDF_VORTEX_ALPHA] > 0.0f) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7792); + + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->unk_1A2 * -8, 0, 0x20, 0x40, 1, + this->unk_1A2 * -4, this->unk_1A2 * -8, 0x20, 0x20)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 0, 200, (s8)this->fwork[GDF_VORTEX_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 130, 0, 0, 128); + + if (this->csState != 21) { + Matrix_Translate(0.0f, 105.0f, -400.0f, MTXMODE_NEW); + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + } else { + Matrix_Translate(-50.0f, 50.0f, -150.0f, MTXMODE_NEW); + Matrix_RotateY(M_PI / 10, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + } + + Matrix_Scale(this->fwork[GDF_VORTEX_SCALE], this->fwork[GDF_VORTEX_SCALE], this->fwork[GDF_VORTEX_SCALE], + MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7841), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gDorfVortexDL)); + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 7844); + } +} + +void func_808E0254(BossGanon* this, u8* tex, f32 arg2) { + static s16 D_808E4DF4[] = { 1, 2, 3, 3, 2, 1 }; + static s16 D_808E4E00[] = { 2, 3, 4, 4, 4, 3, 2 }; + static s16 D_808E4E10[] = { 2, 3, 4, 4, 4, 4, 3, 2 }; + static s16 D_808E4E20[] = { 2, 4, 5, 5, 6, 6, 6, 6, 5, 5, 4, 2 }; + static s16 D_808E4E38[] = { 1, -1, 1, 1, 3, 4, 1, 6, 7, 2, 9, 10, 2, 12, 13 }; + static u8 D_808E4E58[] = { 3, 2, 2, 1, 3, 3, 1, 3, 3, 1, 0, 3, 1, 0, 3 }; + s16 baseX; + s16 index; + s16 i; + s16 baseY; + s16 x; + s16 addY; + f32 lerpX; + s16 y; + f32 lerpY; + f32 lerpZ; + Vec3f sp68; + Vec3f sp5C; + + for (i = 0; i < 15; i++) { + if (arg2 == 0.0f || (y = D_808E4E38[i]) >= 0) { + if (arg2 > 0.0f) { + lerpX = this->unk_2EC[i].x + (this->unk_2EC[y].x - this->unk_2EC[i].x) * arg2; + lerpY = this->unk_2EC[i].y + (this->unk_2EC[y].y - this->unk_2EC[i].y) * arg2; + lerpZ = this->unk_2EC[i].z + (this->unk_2EC[y].z - this->unk_2EC[i].z) * arg2; + + sp68.x = lerpX - this->actor.world.pos.x; + sp68.y = lerpY - this->actor.world.pos.y + 76 + 30 + 30; + sp68.z = lerpZ - this->actor.world.pos.z; + } else { + + sp68.x = this->unk_2EC[i].x - this->actor.world.pos.x; + sp68.y = this->unk_2EC[i].y - this->actor.world.pos.y + 76 + 30 + 30; + sp68.z = this->unk_2EC[i].z - this->actor.world.pos.z; + } + Matrix_MultVec3f(&sp68, &sp5C); + + sp5C.x *= 0.4f; + sp5C.y *= 0.4f; + + baseX = (s16)(sp5C.x + 32.0f); + baseY = (s16)sp5C.y * 64; + + if (D_808E4E58[i] == 2) { + for (y = 0, addY = -0x180; y < 12; y++, addY += 0x40) { + for (x = -D_808E4E20[y]; x < D_808E4E20[y]; x++) { + index = baseX + x + baseY + addY; + if ((index >= 0) && (index < 0x1000)) { + tex[index] = 255; + } + } + } + } else if (D_808E4E58[i] == 1) { + for (y = 0, addY = -0x100; y < 8; y++, addY += 0x40) { + for (x = -D_808E4E10[y]; x < D_808E4E10[y]; x++) { + index = baseX + x + baseY + addY; + if ((index >= 0) && (index < 0x1000)) { + tex[index] = 255; + } + } + } + } else if (D_808E4E58[i] == 0) { + for (y = 0, addY = -0xC0; y < 7; y++, addY += 0x40) { + for (x = -D_808E4E00[y]; x < D_808E4E00[y] - 1; x++) { + index = baseX + x + baseY + addY; + if ((index >= 0) && (index < 0x1000)) { + tex[index] = 255; + } + } + } + } else { + for (y = 0, addY = -0x80; y < 6; y++, addY += 0x40) { + for (x = -D_808E4DF4[y]; x < D_808E4DF4[y] - 1; x++) { + index = baseX + x + baseY + addY; + if ((index >= 0) && (index < 0x1000)) { + tex[index] = 255; + } + } + } + } + } + } +} + +void BossGanon_GenShadowTexture(u8* tex, BossGanon* this, GlobalContext* globalCtx) { + s16 addY; + s16 baseX; + s16 baseY; + s16 i; + s16 j; + s16 y; + s16 x; + s16 index; + Vec3f sp7C; + Vec3f sp70; + s32* ptr = (s32*)tex; + + for (i = 0; i < 64 * 64 / 4; i++, ptr++) { + *ptr = 0; + } + + Matrix_RotateX(1.0f, MTXMODE_NEW); + + for (i = 0; i <= 5; i++) { + func_808E0254(this, tex, i / 5.0f); + } + + for (i = 0; i < 12; i++) { + for (j = 0; j < 12; j++) { + sp7C.x = sCape->strands[i].joints[j].x - this->actor.world.pos.x; + sp7C.y = sCape->strands[i].joints[j].y - this->actor.world.pos.y + 76.0f + 100.0f + 30.0f; + sp7C.z = sCape->strands[i].joints[j].z - this->actor.world.pos.z; + + Matrix_MultVec3f(&sp7C, &sp70); + + sp70.x = sp70.x * 0.28f; + sp70.y = sp70.y * 0.28f; + + baseX = (s32)(sp70.x + 32.0f); + baseY = (s16)sp70.y * 0x40; + + if (!sCape->strands[i].torn[j]) { + for (y = -1, addY = -0x40; y <= 1; y++, addY += 0x40) { + for (x = -3; x <= 3; x++) { + index = baseX + x + baseY + addY; + if (0 <= index && index < 0x1000) { + tex[index] = 255; + } + } + } + } else { + for (y = -1, addY = -0x40; y <= 1; y++, addY += 0x40) { + for (x = -1; x <= 1; x++) { + index = baseX + x + baseY + addY; + if (0 <= index && index < 0x1000) { + tex[index] = 255; + } + } + } + } + } + } +} + +void BossGanon_DrawShadowTexture(void* tex, BossGanon* this, GlobalContext* globalCtx) { + s32 pad; + f32 zOffset; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_boss_ganon.c", 8372); + + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, 50); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + if (this->csState < 100) { + zOffset = (((((this->actor.world.pos.y - 10) + 70.0f) * -5.0f) / 10.0f) + 10.0f); + Matrix_Translate(this->actor.world.pos.x, 0.0f, this->actor.world.pos.z + zOffset, MTXMODE_NEW); + } else { + Matrix_Translate(this->actor.world.pos.x, 4102.0f, this->actor.world.pos.z - 20.0f, MTXMODE_NEW); + } + + Matrix_Scale(0.95000005f, 1.0f, 0.95000005f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 8396), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDorfShadowSetupDL); + gDPLoadTextureBlock(POLY_OPA_DISP++, tex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); + gSPDisplayList(POLY_OPA_DISP++, gDorfShadowModelDL); + + CLOSE_DISPS(gfxCtx, "../z_boss_ganon.c", 8426); +} + +void BossGanon_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 i; + BossGanon* this = (BossGanon*)thisx; + void* shadowTex; + + shadowTex = Graph_Alloc(globalCtx->state.gfxCtx, 64 * 64); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9138); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + if ((this->unk_1A6 & 2) != 0) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099); + } + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDorfEyeTex)); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + BossGanon_OverrideLimbDraw, BossGanon_PostLimbDraw, &this->actor); + + this->unk_2EC[0].x = this->unk_2EC[1].x; + this->unk_2EC[0].y = this->unk_2EC[1].y + 30.0f; + this->unk_2EC[0].z = this->unk_2EC[1].z; + + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + + BossGanon_DrawEffects(globalCtx); + + sCape->actor.world.pos = this->actor.world.pos; + + sCape->rightForearmPos = this->unk_214; + sCape->leftForearmPos = this->unk_220; + + sCape->rightShoulderPos = this->unk_22C; + sCape->leftShoulderPos = this->unk_238; + + BossGanon_DrawShock(this, globalCtx); + BossGanon_DrawHandLightBall(this, globalCtx); + BossGanon_DrawBigMagicCharge(this, globalCtx); + BossGanon_DrawTriforce(this, globalCtx); + BossGanon_DrawDarkVortex(this, globalCtx); + + BossGanon_GenShadowTexture(shadowTex, this, globalCtx); + BossGanon_DrawShadowTexture(shadowTex, this, globalCtx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9393); +} + +s32 BossGanon_CheckFallingPlatforms(BossGanon* this, GlobalContext* globalCtx, Vec3f* checkPos) { + Actor* prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + + while (prop != NULL) { + if (((BossGanon*)prop == this) || (prop->id != ACTOR_BG_GANON_OTYUKA)) { + prop = prop->next; + } else { + BgGanonOtyuka* platform = (BgGanonOtyuka*)prop; + f32 xDiff = platform->dyna.actor.world.pos.x - checkPos->x; + f32 yDiff = platform->dyna.actor.world.pos.y - checkPos->y; + f32 zDiff = platform->dyna.actor.world.pos.z - checkPos->z; + + if ((fabsf(xDiff) < 60.0f) && (yDiff < 20.0f) && (yDiff > -20.0f) && (fabsf(zDiff) < 60.0f)) { + platform->isFalling = true; + platform->visibleSides = OTYUKA_SIDE_ALL; + + return 1; + } else { + prop = prop->next; + } + } + } + + return 0; +} + +void BossGanon_LightBall_Update(Actor* thisx, GlobalContext* globalCtx2) { + u8 hitWithBottle; + s16 i; + s16 spBA = 0; + Vec3f spAC; + Vec3f spA0; + Vec3f sp94; + BossGanon* this = (BossGanon*)thisx; + GlobalContext* globalCtx = globalCtx2; + f32 xDistFromLink; + f32 yDistFromLink; + f32 zDistFromLink; + f32 minReflectDist; + f32 xDistFromGanondorf; + f32 yDistFromGanondorf; + f32 zDistFromGanondorf; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + BossGanon* ganondorf = (BossGanon*)this->actor.parent; + s32 pad1; + + this->unk_1A2++; + ganondorf->envLightMode = 1; + + if (this->unk_1A8 != 0) { + if (this->unk_1A8 == 2) { + Math_ApproachZeroF(&this->fwork[GDF_FWORK_1], 1.0f, 10.0f); + Math_ApproachF(&this->actor.scale.x, 30.0f, 0.5f, 100.0f); + } else { + this->actor.shape.rot.y += 0x1000; + ganondorf->lensFlareTimer = 1; + gCustomLensFlarePos = this->actor.world.pos; + Math_ApproachZeroF(&this->fwork[GDF_FWORK_1], 1.0f, 30.0f); + Math_ApproachF(&this->actor.scale.x, 20.0f, 0.5f, 100.0f); + this->fwork[GDF_FWORK_0] += ((M_PI / 2) + Rand_ZeroFloat(M_PI / 4)); + } + + Actor_SetScale(&this->actor, this->actor.scale.x); + + if (this->fwork[GDF_FWORK_1] == 0.0f) { + Actor_Kill(&this->actor); + } + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_FIRE - SFX_FLAG); + + if ((this->unk_1A2 % 2) != 0) { + Actor_SetScale(&this->actor, 6.0f); + } else { + Actor_SetScale(&this->actor, 5.25f); + } + + this->actor.shape.rot.z += (s16)(Rand_ZeroOne() * 20000.0f) + 0x4000; + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + xDistFromGanondorf = ganondorf->unk_1FC.x - this->actor.world.pos.x; + yDistFromGanondorf = ganondorf->unk_1FC.y - this->actor.world.pos.y; + zDistFromGanondorf = ganondorf->unk_1FC.z - this->actor.world.pos.z; + + xDistFromLink = player->actor.world.pos.x - this->actor.world.pos.x; + yDistFromLink = (player->actor.world.pos.y + 40.0f) - this->actor.world.pos.y; + zDistFromLink = player->actor.world.pos.z - this->actor.world.pos.z; + + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + + switch (this->unk_1C2) { + case 0: + if ((player->stateFlags1 & 2) && + (ABS((s16)(player->actor.shape.rot.y - (s16)(ganondorf->actor.yawTowardsPlayer + 0x8000))) < + 0x2000) && + (sqrtf(SQ(xDistFromLink) + SQ(yDistFromLink) + SQ(zDistFromLink)) <= 25.0f)) { + hitWithBottle = true; + } else { + hitWithBottle = false; + } + + if ((this->collider.base.acFlags & 2) || hitWithBottle) { + ColliderInfo* acHitInfo = this->collider.info.acHitInfo; + + this->collider.base.acFlags &= ~2; + + if ((hitWithBottle == false) && (acHitInfo->toucher.dmgFlags & 0x100000)) { + spBA = 2; + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_REFLECT_MG, &player->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + func_800AA000(this->actor.xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + } else { + spBA = 1; + this->actor.world.rot.y = Math_Atan2S(zDistFromGanondorf, xDistFromGanondorf); + this->actor.world.rot.x = + Math_Atan2S(sqrtf(SQ(xDistFromGanondorf) + SQ(zDistFromGanondorf)), yDistFromGanondorf); + this->unk_1A4++; + this->timers[1] = 2; + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_REFLECT_MG, &player->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + func_800AA000(this->actor.xyzDistToPlayerSq, 0xB4, 0x14, 0x64); + + if (hitWithBottle == false) { + // if ganondorf is 250 units away from link, at least 3 volleys are required + if ((ganondorf->actor.xyzDistToPlayerSq > 62500.0f) && (this->unk_1A4 < 3)) { + this->unk_1C2 = 1; + } else if (Rand_ZeroOne() < 0.7f) { + this->unk_1C2 = 1; + } else { + this->unk_1C2 = 3; + } + + // if a spin attack is used + if (player->swordAnimation >= 0x18) { + this->actor.speedXZ = 20.0f; + } + break; + } else { + if (Rand_ZeroOne() < 0.9f) { + this->unk_1C2 = 1; + } else { + this->unk_1C2 = 3; + } + } + } + } else { + if (sqrtf(SQ(xDistFromLink) + SQ(yDistFromLink) + SQ(zDistFromLink)) <= 25.0f) { + spBA = 5; + func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.world.rot.y, 0.0f, 0x30); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, + NA_SE_EN_GANON_HIT_THUNDER); + ganondorf->timers[2] = 20; + + for (i = 0; i < ARRAY_COUNT(ganondorf->unk_4E4); i++) { + ganondorf->unk_4E4[i] = D_808E4C58[i]; + } + + ganondorf->unk_2E6 = 0; + ganondorf->unk_2E8 = 60; + ganondorf->unk_508 = 4.0f; + } + } + break; + + case 1: + if ((ganondorf->actionFunc == BossGanon_PlayTennis) && (ganondorf->unk_1C2 == 1)) { + minReflectDist = (this->actor.speedXZ >= 19.0f) ? 250.0f : 170.0f; + + if (sqrtf(SQ(xDistFromGanondorf) + SQ(yDistFromGanondorf) + SQ(zDistFromGanondorf)) < + minReflectDist) { + ganondorf->startVolley = true; + this->timers[0] = 8; + this->unk_1C2 = 2; + } + } + break; + + case 2: + if (this->timers[0] == 1) { + spBA = 1; + this->actor.world.rot.y = Math_Atan2S(zDistFromLink, xDistFromLink); + this->actor.world.rot.x = Math_Atan2S(sqrtf(SQ(xDistFromLink) + SQ(zDistFromLink)), yDistFromLink); + this->timers[1] = 2; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_REFLECT_MG); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_AT_RETURN); + this->unk_1C2 = 0; + break; + } + // fallthrough + case 4: + if (sqrtf(SQ(xDistFromGanondorf) + SQ(yDistFromGanondorf) + SQ(zDistFromGanondorf)) < 30.0f) { + spBA = 3; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_GANON_DAMAGE1); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, + NA_SE_EN_GANON_HIT_THUNDER); + } + break; + + case 3: + if (sqrtf(SQ(xDistFromGanondorf) + SQ(yDistFromGanondorf) + SQ(zDistFromGanondorf)) < 100.0f) { + ganondorf->startVolley = true; + this->unk_1C2 = 4; + } + break; + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (this->timers[1] == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + for (i = 0; i < 2; i++) { + spA0.x = spA0.z = 0.0f; + spA0.y = 0.2f; + + spAC.x = spAC.y = spAC.z = 0.0f; + + sp94.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x; + sp94.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y; + sp94.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z; + + BossGanonEff_SpawnSparkle(globalCtx, &sp94, &spAC, &spA0, Rand_ZeroFloat(500.0f) + 700.0f, 0x1E); + } + + if (this->actor.world.pos.y < 10.0f) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 20.0f, 20.0f, 4); + } + + if ((fabsf(this->actor.world.pos.x) > 465.0f) || (this->actor.world.pos.y > 500.0f) || + (fabsf(this->actor.world.pos.z) > 465.0f)) { + spBA = 4; + } + + if ((spBA != 0) || (this->actor.bgCheckFlags & 1)) { + f32 sp58; + f32 sp54; + f32 phi_f20; + s16 sp4E; + + if (spBA == 1) { + sp58 = Rand_ZeroFloat(100.0f) + 300.0f; + sp54 = 10.0f; + phi_f20 = 25.0f; + sp4E = 40; + } else { + sp58 = Rand_ZeroFloat(200.0f) + 500.0f; + sp54 = 15.0f; + phi_f20 = 30.0f; + sp4E = 70; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 80, NA_SE_EN_GANON_HIT_THUNDER); + } + + for (i = 0; i < sp4E; i++) { + if (spBA != 0) { + spAC.x = Rand_CenteredFloat(phi_f20); + spAC.y = Rand_CenteredFloat(phi_f20); + spAC.z = Rand_CenteredFloat(phi_f20); + } else { + spAC.x = Rand_CenteredFloat(phi_f20); + spAC.y = Rand_ZeroFloat(25.0f); + spAC.z = Rand_CenteredFloat(phi_f20); + } + + BossGanonEff_SpawnLightRay(globalCtx, &this->actor.world.pos, &spAC, &sZeroVec, sp58, sp54, 0x1E); + } + + if (spBA != 1) { + this->unk_1A8 = 1; + + if (spBA == 0) { + BossGanon_CheckFallingPlatforms(this, globalCtx, &this->actor.world.pos); + } + + if (spBA == 3) { + BossGanon_SetupHitByLightBall(ganondorf, globalCtx); + } else if (ganondorf->actionFunc == BossGanon_PlayTennis) { + BossGanon_SetupWait(ganondorf, globalCtx); + + if (spBA == 5) { + ganondorf->timers[0] = 125; + } + } + } + } + } +} + +void BossGanon_LightBall_Draw(Actor* thisx, GlobalContext* globalCtx) { + BossGanon* this = (BossGanon*)thisx; + s16 i; + f32 alpha; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9849); + + func_80093D84(globalCtx->state.gfxCtx); + + alpha = ((this->unk_1A2 % 2) != 0) ? this->fwork[GDF_FWORK_1] * 0.4f : this->fwork[GDF_FWORK_1] * 0.35f; + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, (s8)alpha); + Matrix_Push(); + Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(this->actor.scale.x * 0.75f, 1.0f, this->actor.scale.z * 0.75f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9875), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightCoreDL); + + Matrix_Pop(); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->fwork[GDF_FWORK_1]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + + if (this->unk_1A8 == 1) { + for (i = 0; i < 8; i++) { + Matrix_Push(); + Matrix_RotateY(i * (M_PI / 8), MTXMODE_APPLY); + Matrix_RotateZ(this->fwork[GDF_FWORK_0], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9899), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + Matrix_Pop(); + } + } else if (this->unk_1A8 == 0) { + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ((this->actor.shape.rot.z / 32768.0f) * 3.1416f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9907), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 9911); +} + +void func_808E1EB4(Actor* thisx, GlobalContext* globalCtx2) { + s16 i; + BossGanon* this = (BossGanon*)thisx; + GlobalContext* globalCtx = globalCtx2; + BossGanon* dorf = (BossGanon*)this->actor.parent; + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 xzDist; + s16 xRotTarget; + s16 yRotTarget; + Vec3f vel; + Vec3f accel; + + this->unk_1A2++; + dorf->envLightMode = 1; + Actor_SetScale(&this->actor, 6.0f); + this->actor.shape.rot.z += ((s16)(Rand_ZeroOne() * 20000.0f) + 0x4000); + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + + this->unk_1A6++; + + if (this->unk_1A6 >= 15) { + this->unk_1A6 = 0; + } + + this->unk_2EC[this->unk_1A6] = this->actor.world.pos; + + if (this->unk_1C2 == 0) { + if (1) {} + if (this->timers[0] == 0) { + this->unk_1C2 = 1; + } + } else if (this->unk_1C2 == 1) { + xDiff = dorf->unk_1FC.x - this->actor.world.pos.x; + yDiff = dorf->unk_1FC.y - this->actor.world.pos.y; + zDiff = dorf->unk_1FC.z - this->actor.world.pos.z; + + yRotTarget = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff)); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + xRotTarget = RADF_TO_BINANG(Math_FAtan2F(yDiff, xzDist)); + + Math_ApproachS(&this->actor.world.rot.x, xRotTarget, 1, 0x1000); + Math_ApproachS(&this->actor.world.rot.y, yRotTarget, 1, 0x1000); + + if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 40.0f) { + this->unk_1C2 = 2; + this->timers[0] = 30; + this->actor.speedXZ = 0.0f; + + if (this->actor.params == 0xC8) { + func_80078884(NA_SE_EN_GANON_DAMAGE2); + func_80078884(NA_SE_EN_GANON_DD_THUNDER); + + for (i = 0; i < 150; i++) { + + vel.x = Rand_CenteredFloat(25.0f); + vel.y = Rand_CenteredFloat(25.0f); + vel.z = Rand_CenteredFloat(25.0f); + + accel.x = vel.x * -0.03f; + accel.y = vel.y * -0.03f; + accel.z = vel.z * -0.03f; + + BossGanonEff_SpawnLightRay(globalCtx, &dorf->unk_1FC, &vel, &accel, + Rand_ZeroFloat(500.0f) + 1000.0f, 15.0f, 0x14); + } + + for (i = 1; i < 15; i++) { + dorf->unk_4E4[i] = 1000; + } + + dorf->unk_2E6 = 1000; + dorf->unk_2E8 = 0; + dorf->screenFlashTimer = 4; + dorf->lensFlareTimer = 10; + dorf->lensFlareMode = 1; + dorf->unk_508 = 10.0f; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &dorf->actor, globalCtx, ACTOR_BOSS_GANON, dorf->unk_1FC.x, + dorf->unk_1FC.y, dorf->unk_1FC.z, 0, 0, 0, 0x12C); + } + + this->actor.world.pos.y = 5000.0f; + } + } else if (this->timers[0] == 0) { + Actor_Kill(&this->actor); + } +} + +void func_808E229C(Actor* thisx, GlobalContext* globalCtx2) { + BossGanon* this = (BossGanon*)thisx; + GlobalContext* globalCtx = globalCtx2; + s16 i; + s32 temp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 10081); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + + for (i = 9; i >= 0; i--) { + temp = (s16)(((this->unk_1A6 - i) + 0xF) % 15); + Matrix_Translate(this->unk_2EC[temp].x, this->unk_2EC[temp].y, this->unk_2EC[temp].z, MTXMODE_NEW); + Matrix_Scale(this->actor.scale.x * (1.0f - (i * 0.07000001f)), this->actor.scale.y * (1.0f - (i * 0.07000001f)), + this->actor.scale.z * (1.0f - (i * 0.07000001f)), MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ(((2.0f * (i * M_PI)) / 10.0f) + BINANG_TO_RAD(this->actor.shape.rot.z), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 10109), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 10113); +} + +void func_808E2544(Actor* thisx, GlobalContext* globalCtx) { + u8 numEffects = 0; + s16 xRot; + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 xzDist; + f32 new_var; + f32 sp84; + s16 i; + s16 sp80; + BossGanon* this = (BossGanon*)thisx; + BossGanon* dorf = (BossGanon*)this->actor.parent; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + ColliderInfo* acHitInfo; + Vec3f sp60; + + this->unk_1A2++; + Actor_SetScale(&this->actor, 0.01f); + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + + this->unk_1A6++; + + if (this->unk_1A6 >= 15) { + this->unk_1A6 = 0; + } + + this->unk_2EC[this->unk_1A6] = this->actor.world.pos; + this->unk_3C4[this->unk_1A6].x = BINANG_TO_RAD(this->actor.world.rot.x); + this->unk_3C4[this->unk_1A6].y = BINANG_TO_RAD(this->actor.world.rot.y); + + switch (this->unk_1C2) { + if (1) {} + case 0: + this->actor.speedXZ = 40.0f; + Math_ApproachF(&this->fwork[1], 255.0f, 1.0f, 40.0f); + xDiff = dorf->unk_278.x - this->actor.world.pos.x; + yDiff = dorf->unk_278.y - this->actor.world.pos.y; + zDiff = dorf->unk_278.z - this->actor.world.pos.z; + sp80 = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff)); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + + xRot = RADF_TO_BINANG(Math_FAtan2F(yDiff, xzDist)); + sp84 = (xzDist * 700.0f) / 10.0f; + if (sp84 > 6144.0f) { + sp84 = 6144.0f; + } + + xRot += (Math_CosS(this->unk_1A2 * 0x2200) * sp84); + this->actor.world.rot.x = xRot; + Math_ApproachS(&this->actor.shape.rot.y, sp80, 1, this->csCamMaxStepScale); + Math_ApproachF(&this->csCamMaxStepScale, 4096.0f, 1.0f, 256.0f); + this->actor.world.rot.y = (Math_SinS(this->unk_1A2 * 0x1A00) * sp84) + this->actor.shape.rot.y; + + if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 45.0f) { + this->unk_1C2 = 1; + this->actor.speedXZ = 0.0f; + } + break; + + case 1: + Math_ApproachZeroF(&this->fwork[1], 1.0f, 40.0f); + + if (this->fwork[1] == 0.0f) { + Actor_Kill(&this->actor); + } + break; + + case 10: + this->unk_1C2 = 0xB; + this->timers[0] = 14; + + this->collider.dim.radius = 15; + this->collider.dim.height = 20; + this->collider.dim.yShift = -10; + + this->actor.speedXZ = 20.0f; + this->fwork[1] = 255.0f; + this->unk_1F0 = player->actor.world.pos; + new_var = this->unk_1F0.x - this->actor.world.pos.x; + this->actor.shape.rot.y = RADF_TO_BINANG(Math_FAtan2F(new_var, this->unk_1F0.z - this->actor.world.pos.z)) + + (this->actor.params << 0xD) - 0x20C000; + // fallthrough + case 11: + if (this->timers[0] != 0) { + this->unk_1F0 = player->actor.world.pos; + xDiff = this->unk_1F0.x - this->actor.world.pos.x; + yDiff = (this->unk_1F0.y + 30.0f) - this->actor.world.pos.y; + zDiff = this->unk_1F0.z - this->actor.world.pos.z; + + sp80 = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff)); + this->actor.shape.rot.x = RADF_TO_BINANG(Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff)))); + Math_ApproachS(&this->actor.shape.rot.y, sp80, 1, this->csCamMaxStepScale); + Math_ApproachF(&this->csCamMaxStepScale, 4096.0f, 1.0f, 256.0f); + } + + sp84 = (sqrtf(this->actor.xyzDistToPlayerSq) * 200.0f) / 10.0f; + if (sp84 > 13824.0f) { + sp84 = 13824.0f; + } + + this->actor.world.rot.x = (Math_CosS(this->unk_1A2 * 0x3400) * sp84 * 0.1f) + this->actor.shape.rot.x; + this->actor.world.rot.y = (Math_SinS(this->unk_1A2 * 0x1A00) * sp84) + this->actor.shape.rot.y; + + if ((player->swordState != 0) && (player->swordAnimation >= 0x18) && (this->actor.xzDistToPlayer < 80.0f)) { + this->unk_1C2 = 0xC; + this->actor.speedXZ = -30.0f; + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + this->unk_1F0 = dorf->unk_1FC; + numEffects = 10; + break; + } + + if (this->collider.base.acFlags & 2) { + acHitInfo = this->collider.info.acHitInfo; + + this->collider.base.acFlags &= ~2; + + if (!(acHitInfo->toucher.dmgFlags & 0x100000) || Player_HasMirrorShieldEquipped(globalCtx)) { + func_800AA000(this->actor.xyzDistToPlayerSq, 0xB4, 0x14, 0x64); + this->unk_1C2 = 0xC; + this->actor.speedXZ = -30.0f; + + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + + this->unk_1F0.x = Rand_CenteredFloat(700.0f) + dorf->unk_1FC.x; + this->unk_1F0.y = Rand_CenteredFloat(200.0f) + dorf->unk_1FC.y; + this->unk_1F0.z = Rand_CenteredFloat(700.0f) + dorf->unk_1FC.z; + + this->unk_1F0.x = this->unk_1F0.x + ((this->unk_1F0.x - this->actor.world.pos.x) * 100.0f); + this->unk_1F0.y = this->unk_1F0.y + ((this->unk_1F0.y - this->actor.world.pos.y) * 100.0f); + this->unk_1F0.z = this->unk_1F0.z + ((this->unk_1F0.z - this->actor.world.pos.z) * 100.0f); + + numEffects = 10; + break; + } + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (this->timers[1] == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + xDiff = player->actor.world.pos.x - this->actor.world.pos.x; + yDiff = (player->actor.world.pos.y + 30.0f) - this->actor.world.pos.y; + zDiff = player->actor.world.pos.z - this->actor.world.pos.z; + + if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 30.0f) { + this->unk_1C2 = 1; + this->actor.speedXZ = 0.0f; + + if (dorf->timers[2] == 0) { + func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.world.rot.y, 0.0f, 0x50); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, + NA_SE_EN_GANON_HIT_THUNDER); + dorf->timers[2] = 20; + + for (i = 0; i < ARRAY_COUNT(this->unk_4E4); i++) { + dorf->unk_4E4[i] = D_808E4C58[i]; + } + + dorf->unk_2E6 = 0; + dorf->unk_2E8 = 60; + dorf->unk_508 = 4.0f; + numEffects = 40; + } + } + break; + + case 12: + this->actor.speedXZ = 20.0f; + + xDiff = this->unk_1F0.x - this->actor.world.pos.x; + yDiff = this->unk_1F0.y - this->actor.world.pos.y; + zDiff = this->unk_1F0.z - this->actor.world.pos.z; + + sp80 = RADF_TO_BINANG(Math_FAtan2F(xDiff, zDiff)); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + xRot = RADF_TO_BINANG(Math_FAtan2F(yDiff, xzDist)); + sp84 = (xzDist * 700.0f) / 10.0f; + + if (sp84 > 6144.0f) { + sp84 = 6144.0f; + } + + sp80 += Math_SinS(this->unk_1A2 * 0x2200) * sp84; + + xRot += Math_CosS(this->unk_1A2 * 0x1800) * sp84; + + this->actor.world.rot.x = xRot; + this->actor.world.rot.y = sp80; + + xDiff = dorf->unk_1FC.x - this->actor.world.pos.x; + yDiff = dorf->unk_1FC.y - this->actor.world.pos.y; + zDiff = dorf->unk_1FC.z - this->actor.world.pos.z; + + if (sqrtf(SQ(xDiff) + SQ(zDiff) + SQ(yDiff)) < 45.0f) { + BossGanon_SetupHitByLightBall(dorf, globalCtx); + this->timers[0] = 150; + numEffects = 40; + this->unk_1C2 = 1; + this->actor.speedXZ = 0.0f; + } + break; + } + + if (this->unk_1C2 >= 0xB) { + xzDist = (this->unk_1C2 == 0xC) ? -65.0f : 0.0f; + + if ((fabsf(this->actor.world.pos.x) > (465.0f + xzDist)) || + (fabsf(this->actor.world.pos.z) > (465.0f + xzDist)) || ((this->actor.world.pos.y < 0.0f)) || + (this->actor.world.pos.y > 450.0f)) { + this->unk_1C2 = 1; + this->actor.speedXZ = 0.0f; + numEffects = 10; + BossGanon_CheckFallingPlatforms(this, globalCtx, &this->actor.world.pos); + Actor_SpawnAsChild(&globalCtx->actorCtx, &dorf->actor, globalCtx, ACTOR_BOSS_GANON, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0x190); + } + } + + if (numEffects) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 80, NA_SE_EN_FANTOM_THUNDER); + + for (i = 0; i < numEffects; i++) { + sp60.x = Rand_CenteredFloat(30.0f); + sp60.y = Rand_CenteredFloat(30.0f); + sp60.z = Rand_CenteredFloat(30.0); + + BossGanonEff_SpawnLightRay(globalCtx, &this->actor.world.pos, &sp60, &sZeroVec, + Rand_ZeroFloat(200.0f) + 500.0f, 15.0f, 0x1E); + } + } +} + +static Gfx* sBigMagicLightStreakDLists[] = { + gDorfLightStreak12DL, gDorfLightStreak11DL, gDorfLightStreak10DL, gDorfLightStreak9DL, + gDorfLightStreak8DL, gDorfLightStreak7DL, gDorfLightStreak6DL, gDorfLightStreak5DL, + gDorfLightStreak4DL, gDorfLightStreak3DL, gDorfLightStreak2DL, gDorfLightStreak1DL, +}; + +void func_808E324C(Actor* thisx, GlobalContext* globalCtx) { + BossGanon* this = (BossGanon*)thisx; + Mtx* mtx; + s16 i; + s32 temp; + + mtx = Graph_Alloc(globalCtx->state.gfxCtx, 12 * sizeof(Mtx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 10489); + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 255, (s8)this->fwork[GDF_FWORK_1]); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 255, 0, 128); + gSPSegment(POLY_XLU_DISP++, 0x0D, mtx); + + for (i = 0; i < 12; i++) { + temp = (s16)(((this->unk_1A6 - i) + 0xF) % 15); + Matrix_Translate(this->unk_2EC[temp].x, this->unk_2EC[temp].y, this->unk_2EC[temp].z, MTXMODE_NEW); + Matrix_RotateY(this->unk_3C4[temp].y, MTXMODE_APPLY); + Matrix_RotateX(-this->unk_3C4[temp].x, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + Matrix_RotateY(M_PI / 2, MTXMODE_APPLY); + Matrix_ToMtx(mtx, "../z_boss_ganon.c", 10520); + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sBigMagicLightStreakDLists[i]); + mtx++; + }; + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(10.0f, 10.0f, 10.0f, MTXMODE_APPLY); + Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 10534), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon.c", 10541); +} + +void BossGanon_UpdateEffects(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + GanondorfEffect* eff = globalCtx->specialEffects; + s16 i; + s32 pad; + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 yRot; + f32 xRot; + Vec3f spA0; + s16 bodyPart; + f32 distToPlayer; + s32 pad2; + s32 pad3; + + spA0.x = 0.0f; + spA0.y = 0.0f; + + for (i = 0; i < ARRAY_COUNT(sEffectBuf); i++, eff++) { + if (eff->type != GDF_EFF_NONE) { + eff->pos.x += eff->velocity.x; + eff->pos.y += eff->velocity.y; + eff->pos.z += eff->velocity.z; + + eff->timer++; + + eff->velocity.x += eff->accel.x; + eff->velocity.y += eff->accel.y; + eff->velocity.z += eff->accel.z; + + if (eff->type == GDF_EFF_WINDOW_SHARD) { + eff->unk_44 += 0.3f; + eff->unk_48 += 0.5f; + + if (eff->pos.y < 0.0f) { + eff->type = GDF_EFF_NONE; + } + } else if (eff->type == GDF_EFF_SPARKLE) { + eff->unk_3C += Rand_ZeroFloat(M_PI / 2) + M_PI / 2; + eff->unk_2E -= eff->unk_30; + + if (eff->unk_2E <= 0) { + eff->unk_2E = 0; + eff->type = GDF_EFF_NONE; + } + + eff->alpha = eff->unk_2E; + + if (eff->alpha > 255) { + eff->alpha = 255; + } + } else if (eff->type == GDF_EFF_BLACK_DOT) { + xDiff = sGanondorf->unk_278.x - eff->pos.x; + yDiff = sGanondorf->unk_278.y - eff->pos.y; + zDiff = sGanondorf->unk_278.z - eff->pos.z; + + yRot = Math_FAtan2F(xDiff, zDiff); + + xRot = -Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff))); + spA0.z = eff->unk_38; + Matrix_RotateY(yRot, MTXMODE_NEW); + Matrix_RotateX(xRot, MTXMODE_APPLY); + Matrix_MultVec3f(&spA0, &eff->velocity); + Math_ApproachF(&eff->unk_38, 10.0f, 1.0f, 0.5f); + + eff->alpha += 10; + + if (eff->alpha > 255) { + eff->alpha = 255; + } + + if ((sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)) < 20.0f) || (eff->timer > 70)) { + eff->type = GDF_EFF_NONE; + } + } else if (eff->type == GDF_EFF_LIGHT_RAY) { + eff->unk_3C += Rand_ZeroFloat(M_PI / 2) + M_PI / 2; + eff->unk_2E -= eff->unk_30; + + if (eff->unk_2E <= 0) { + eff->unk_2E = 0; + eff->type = GDF_EFF_NONE; + } + + eff->alpha = eff->unk_2E; + + if (eff->alpha > 255) { + eff->alpha = 255; + } + + Math_ApproachF(&eff->unk_38, eff->unk_40, 1.0f, (eff->unk_40 / 15.0f) * 4.0f); + } else if (eff->type == GDF_EFF_SHOCK) { + if (eff->unk_2E == GDF_SHOCK_DORF_YELLOW) { + bodyPart = (s16)Rand_ZeroFloat(13.9f) + 1; + + eff->pos.x = sGanondorf->unk_2EC[bodyPart].x + Rand_CenteredFloat(20.0f); + eff->pos.y = sGanondorf->unk_2EC[bodyPart].y + Rand_CenteredFloat(20.0f); + eff->pos.z = sGanondorf->unk_2EC[bodyPart].z + Rand_CenteredFloat(20.0f); + } else { + bodyPart = (s16)Rand_ZeroFloat(17.9f); + + eff->pos.x = player->bodyPartsPos[bodyPart].x + Rand_CenteredFloat(10.0f); + eff->pos.y = player->bodyPartsPos[bodyPart].y + Rand_CenteredFloat(15.0f); + eff->pos.z = player->bodyPartsPos[bodyPart].z + Rand_CenteredFloat(10.0f); + } + + eff->unk_3C += (Rand_ZeroFloat(M_PI / 2) + M_PI / 2); + + if (eff->timer > 20) { + eff->type = GDF_EFF_NONE; + } + } else if (eff->type == GDF_EFF_LIGHTNING) { + if (eff->unk_3C == 0.0f) { + eff->unk_44 = BINANG_TO_RAD(Camera_GetInputDirYaw(Gameplay_GetCamera(globalCtx, MAIN_CAM))); + } else { + eff->unk_44 = M_PI / 2; + } + + if (eff->timer > 12) { + eff->type = GDF_EFF_NONE; + } + } else if (eff->type == GDF_EFF_IMPACT_DUST_DARK) { + eff->unk_30++; // unused + + if (eff->unk_2E == 0) { + eff->alpha += 26; + + if (eff->alpha > 255) { + eff->alpha = 255; + eff->unk_2E = 1; + } + } else if (eff->unk_2E == 1) { + eff->unk_2E = 2; + } else if (eff->unk_2E == 2) { + eff->alpha -= 26; + + if (eff->alpha < 0) { + eff->alpha = 0; + eff->type = GDF_EFF_NONE; + } + } + + Math_ApproachF(&eff->scale, eff->unk_38, 1.0f, 0.01f); + Math_ApproachF(&eff->unk_40, 4.0f, 1.0f, 0.15f); + } else if (eff->type == GDF_EFF_IMPACT_DUST_LIGHT) { + if (i == 0) { + func_80078884(NA_SE_EN_GANON_WAVE_GND - SFX_FLAG); + } + + eff->unk_30++; // unused + + if (eff->unk_2E == 0) { + eff->alpha += 100; + + if (eff->alpha > 255) { + eff->alpha = 255; + eff->unk_2E = 1; + } + } else if (eff->unk_2E == 1) { + if (eff->timer >= 20) { + eff->unk_2E = 2; + } + } else if (eff->unk_2E == 2) { + eff->alpha -= 30; + + if (eff->alpha < 0) { + eff->alpha = 0; + eff->type = GDF_EFF_NONE; + } + } + + Math_ApproachF(&eff->scale, eff->unk_38, 1.0f, 0.1f); + Math_ApproachF(&eff->unk_40, 1.0f, 1.0f, 0.15f); + } else if (eff->type == GDF_EFF_SHOCKWAVE) { + eff->unk_30++; // unused + eff->alpha -= 30; + + if (eff->alpha < 0) { + eff->alpha = 0; + eff->type = GDF_EFF_NONE; + } + + Math_ApproachF(&eff->scale, eff->unk_38, 1.0f, 0.13f); + + if ((eff->timer < 150) && (fabsf(player->actor.world.pos.y) < 5.0f)) { + distToPlayer = + sqrtf(SQ(eff->pos.x - player->actor.world.pos.x) + SQ(eff->pos.z - player->actor.world.pos.z)); + + if (((eff->scale * 150.0f) < distToPlayer) && (distToPlayer < (eff->scale * 300.0f))) { + eff->timer = 150; + func_8002F6D4(globalCtx, &sGanondorf->actor, 7.0f, sGanondorf->actor.yawTowardsPlayer, 0.0f, + 0x20); + } + } + } + } + } +} + +static void* sLightningTextures[] = { + gDorfLightning1Tex, gDorfLightning1Tex, gDorfLightning2Tex, gDorfLightning3Tex, gDorfLightning4Tex, + gDorfLightning5Tex, gDorfLightning6Tex, gDorfLightning7Tex, gDorfLightning8Tex, gDorfLightning9Tex, + gDorfLightning10Tex, gDorfLightning11Tex, gDorfLightning12Tex, +}; + +static u8 sLightningPrimColors[] = { + 0, 0, 0, 255, 255, 255, 231, 250, 231, 208, 245, 208, 185, 240, 185, 162, 235, 162, 139, 230, + 139, 115, 225, 115, 92, 220, 92, 69, 215, 69, 46, 210, 46, 23, 205, 23, 0, 200, 0, +}; + +static u8 sLightningEnvColors[] = { + 0, 0, 0, 255, 255, 0, 240, 231, 23, 226, 208, 46, 212, 185, 69, 198, 162, 92, + 184, 139, 115, 170, 115, 139, 156, 92, 162, 142, 69, 185, 128, 46, 208, 114, 23, 231, + 100, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void BossGanon_DrawEffects(GlobalContext* globalCtx) { + u8 flag = 0; + s16 i; + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + GanondorfEffect* eff = globalCtx->specialEffects; + GanondorfEffect* effFirst = eff; + + OPEN_DISPS(gfxCtx, "../z_boss_ganon.c", 10865); + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < 200; i++, eff++) { + if (eff->type == GDF_EFF_WINDOW_SHARD) { + gDPPipeSync(POLY_OPA_DISP++); + if (flag == 0) { + gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardMaterialDL); + flag++; + } + if ((eff->timer & 7) != 0) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, eff->color.r, eff->color.g, eff->color.b, 255); + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + } + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_Scale(eff->scale, eff->scale, eff->scale, MTXMODE_APPLY); + Matrix_RotateY(eff->unk_48, MTXMODE_APPLY); + Matrix_RotateX(eff->unk_44, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10898), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDorfWindowShardModelDL); + } + } + + eff = effFirst; + flag = 0; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_SPARKLE) { + gDPPipeSync(POLY_XLU_DISP++); + if (flag == 0) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + flag++; + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10932), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + } + } + + eff = effFirst; + flag = 0; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_LIGHT_RAY) { + gDPPipeSync(POLY_XLU_DISP++); + if (flag == 0) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightBallMaterialDL); + flag++; + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_RotateY(eff->unk_48, MTXMODE_APPLY); + Matrix_RotateX(eff->unk_44, MTXMODE_APPLY); + Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY); + Matrix_Scale(eff->scale, eff->scale, eff->unk_38 * eff->scale, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 10971), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfSquareDL); + } + } + + eff = effFirst; + flag = 0; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_SHOCK) { + if (flag == 0) { + gDPPipeSync(POLY_XLU_DISP++); + if (eff->unk_2E == GDF_SHOCK_PLAYER_PURPLE) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 0, 200, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 130, 0, 0, 0); + } else { // GDF_SHOCK_DORF_YELLOW or GDF_SHOCK_PLAYER_YELLOW + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 0); + } + flag++; + } + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(eff->unk_3C * 1.3f, MTXMODE_APPLY); + Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11023), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfShockDL); + } + } + + eff = effFirst; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_LIGHTNING) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, sLightningPrimColors[(eff->timer * 3) + 0], + sLightningPrimColors[(eff->timer * 3) + 1], sLightningPrimColors[(eff->timer * 3) + 2], + 255); + gDPSetEnvColor(POLY_XLU_DISP++, sLightningEnvColors[(eff->timer * 3) + 0], + sLightningEnvColors[(eff->timer * 3) + 1], sLightningEnvColors[(eff->timer * 3) + 2], 0); + Matrix_Translate(sGanondorf->unk_260.x, sGanondorf->unk_260.y, sGanondorf->unk_260.z, MTXMODE_NEW); + Matrix_RotateY(eff->unk_48, MTXMODE_APPLY); + Matrix_RotateZ(eff->unk_3C, MTXMODE_APPLY); + Matrix_Scale(eff->scale, eff->scale, eff->scale, MTXMODE_APPLY); + Matrix_RotateY(eff->unk_44, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11074), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sLightningTextures[eff->timer])); + gSPDisplayList(POLY_XLU_DISP++, gDorfLightningDL); + } + } + + eff = effFirst; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_IMPACT_DUST_DARK) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, eff->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 70, 0, 128); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, eff->timer * 4, 0, 32, 64, 1, eff->timer * 2, + eff->timer * -20, 32, 32)); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_Scale(eff->scale, eff->unk_40 * eff->scale, eff->scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11121), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfImpactDarkDL); + } + } + + eff = effFirst; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_IMPACT_DUST_LIGHT) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, eff->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 100, 0, 128); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, eff->timer * 4, 0, 32, 64, 1, eff->timer * 2, + eff->timer * -20, 32, 32)); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_Scale(eff->scale, eff->unk_40 * eff->scale, eff->scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11165), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfImpactLightDL); + } + } + + eff = effFirst; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_SHOCKWAVE) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, eff->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 255, 0, 128); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (eff->timer * 100), 0, 64, 32, 1, + (eff->timer * 100), 0, 64, 32)); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_Scale((eff->scale * 200.0f) / 1500.0f, (eff->unk_40 * 200.0f) / 1500.0f, + (eff->scale * 200.0f) / 1500.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11209), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfShockwaveDL); + } + } + + eff = effFirst; + + for (i = 0; i < 150; i++, eff++) { + if (eff->type == GDF_EFF_BLACK_DOT) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 170, 0, eff->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, eff->timer * 2, eff->timer * -20, + 64, 64)); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_ganon.c", 11250), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gDorfDotDL); + } + } + + CLOSE_DISPS(gfxCtx, "../z_boss_ganon.c", 11255); +} + +#include "overlays/ovl_Boss_Ganon/ovl_Boss_Ganon.h" + +void BossGanon_Reset(void) { + +static EnGanonMant* sCape; + + sSeed1 = 0; + sSeed2 = 0; + sSeed3 = 0; + sGanondorf = NULL; + sZelda = NULL; + sCape = NULL; + + + memset(sEffectBuf, 0, sizeof(sEffectBuf)); +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h new file mode 100644 index 000000000..a3e3e503e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h @@ -0,0 +1,147 @@ +#ifndef Z_BOSS_GANON_H +#define Z_BOSS_GANON_H + +#include "ultra64.h" +#include "global.h" + +struct BossGanon; + +typedef void (*BossGanonActionFunc)(struct BossGanon*, GlobalContext*); + +typedef enum { + /* 0 */ GDF_FWORK_0, + /* 1 */ GDF_FWORK_1, + /* 2 */ GDF_CENTER_POS, + /* 3 */ GDF_TRIFORCE_PRIM_B, + /* 4 */ GDF_TRIFORCE_PRIM_A, + /* 5 */ GDF_TRIFORCE_ENV_G, + /* 6 */ GDF_TRIFORCE_SCALE, + /* 7 */ GDF_VORTEX_ALPHA, + /* 8 */ GDF_VORTEX_SCALE, + /* 9 */ GDF_FWORK_UNUSED_9, + /* 10 */ GDF_FWORK_MAX +} GanondorfFwork; + +typedef enum { + /* 0 */ GDF_WINDOW_SHATTER_OFF, + /* 1 */ GDF_WINDOW_SHATTER_PARTIAL, + /* 2 */ GDF_WINDOW_SHATTER_FULL +} WindowShatterState; + +typedef enum { + /* 0 */ GDF_SHOCK_DORF_YELLOW, + /* 1 */ GDF_SHOCK_PLAYER_YELLOW, + /* 2 */ GDF_SHOCK_PLAYER_PURPLE +} GanondorfShockType; + +typedef enum { + /* 0 */ GDF_EFF_NONE, + /* 1 */ GDF_EFF_SPARKLE, + /* 2 */ GDF_EFF_LIGHT_RAY, + /* 3 */ GDF_EFF_SHOCK, + /* 4 */ GDF_EFF_LIGHTNING, + /* 5 */ GDF_EFF_IMPACT_DUST_DARK, + /* 6 */ GDF_EFF_IMPACT_DUST_LIGHT, + /* 7 */ GDF_EFF_SHOCKWAVE, + /* 8 */ GDF_EFF_BLACK_DOT, + /* 9 */ GDF_EFF_WINDOW_SHARD +} GanondorfEffectType; + +typedef enum { + /* 0 */ GDF_TRIFORCE_PLAYER, + /* 1 */ GDF_TRIFORCE_ZELDA, + /* 2 */ GDF_TRIFORCE_DORF +} GanondorfTriforceType; + +typedef struct BossGanon { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 animBankIndex; + /* 0x0150 */ SkelAnime skelAnime; + /* 0x0194 */ BossGanonActionFunc actionFunc; + /* 0x0198 */ u8 unk_198; + /* 0x0199 */ u8 legSwayEnabled; + /* 0x019A */ u8 unk_19A; + /* 0x019C */ s16 unk_19C; // timer + /* 0x019E */ u8 unk_19E; + /* 0x019F */ u8 unk_19F; + /* 0x01A0 */ s8 envLightMode; + /* 0x01A2 */ s16 unk_1A2; + /* 0x01A4 */ s16 unk_1A4; + /* 0x01A6 */ s16 unk_1A6; + /* 0x01A8 */ s16 unk_1A8; + /* 0x01AA */ s16 unk_1AA; + /* 0x01AC */ s16 unk_1AC; + /* 0x01AE */ s16 triforceType; + /* 0x01B0 */ char unk_1B0[0x6]; + /* 0x01B6 */ s16 timers[5]; + /* 0x01C0 */ u8 startVolley; + /* 0x01C2 */ s16 unk_1C2; + /* 0x01C4 */ s16 screenFlashTimer; + /* 0x01C8 */ f32 fwork[GDF_FWORK_MAX]; + /* 0x01F0 */ Vec3f unk_1F0; + /* 0x01FC */ Vec3f unk_1FC; + /* 0x0208 */ Vec3f unk_208; + /* 0x0214 */ Vec3f unk_214; + /* 0x0220 */ Vec3f unk_220; + /* 0x022C */ Vec3f unk_22C; + /* 0x0238 */ Vec3f unk_238; + /* 0x0244 */ char unk_244[0x10]; + /* 0x0254 */ f32 handLightBallScale; + /* 0x0258 */ f32 unk_258; + /* 0x025C */ u8 unk_25C; + /* 0x0260 */ Vec3f unk_260; // hand position? + /* 0x026C */ s16 unk_26C; // timer? + /* 0x0270 */ f32 unk_270; + /* 0x0274 */ u8 unk_274; + /* 0x0278 */ Vec3f unk_278; + /* 0x0284 */ f32 unk_284; // scale for something + /* 0x0288 */ f32 unk_288; + /* 0x028C */ f32 unk_28C; // scale for something + /* 0x0290 */ f32 unk_290; + /* 0x0294 */ f32 unk_294[15]; + /* 0x02D0 */ f32 unk_2D0; // scale for something + /* 0x02D4 */ s16 unk_2D4; // timer + /* 0x02D8 */ Vec3f triforcePos; + /* 0x02E4 */ u8 shockGlow; + /* 0x02E6 */ s16 unk_2E6; // timer + /* 0x02E8 */ s16 unk_2E8; // timer + /* 0x02EC */ Vec3f unk_2EC[18]; // body parts pos for ganondorf, used for other things as well + /* 0x03C4 */ Vec3f unk_3C4[18]; + /* 0x049C */ f32 unk_49C[18]; + /* 0x04E4 */ s16 unk_4E4[18]; + /* 0x0508 */ f32 unk_508; + /* 0x050C */ Vec3f legRot; + /* 0x0518 */ char unk_518[0xF8]; + /* 0x0610 */ ColliderCylinder collider; + /* 0x065C */ char unk_65C[0x10]; + /* 0x066C */ u8 lensFlareMode; + /* 0x066E */ s16 lensFlareTimer; + /* 0x0670 */ f32 lensFlareScale; + /* 0x0674 */ u32 csTimer; + /* 0x0678 */ s16 csState; + /* 0x067A */ s16 csCamIndex; + /* 0x067C */ char unk_67C[0x4]; + /* 0x0680 */ Vec3f csCamEye; + /* 0x068C */ Vec3f csCamAt; + /* 0x0698 */ char unk_698[0xC]; + /* 0x06A4 */ Vec3f csCamEyeMaxStep; + /* 0x06B0 */ Vec3f csCamAtMaxStep; + /* 0x06BC */ Vec3f csCamTargetEye; + /* 0x06C8 */ char unk_6C8[0xC]; + /* 0x06D4 */ Vec3f csCamTargetAt; + /* 0x06E0 */ char unk_6E0[0xC]; + /* 0x06EC */ f32 csCamMaxStepScale; + /* 0x06F0 */ f32 csCamMovementScale; + /* 0x06F4 */ f32 csCamFov; + /* 0x06F8 */ char unk_6F8[0xC]; + /* 0x0704 */ f32 unk_704; + /* 0x0708 */ char unk_708[0x4]; + /* 0x070C */ f32 unk_70C; + /* 0x070C */ f32 unk_710; + /* 0x0714 */ f32 whiteFillAlpha; + /* 0x0718 */ s16 organAlpha; + /* 0x071A */ u8 useOpenHand; + /* 0x071B */ u8 windowShatterState; +} BossGanon; // size = 0x71C + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c new file mode 100644 index 000000000..74da86927 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -0,0 +1,3090 @@ +#include "z_boss_ganon2.h" +#include "overlays/actors/ovl_Demo_Gj/z_demo_gj.h" +#include "overlays/actors/ovl_En_Zl3/z_en_zl3.h" +#include "objects/object_ganon/object_ganon.h" +#include "objects/object_ganon2/object_ganon2.h" +#include "objects/object_ganon_anime3/object_ganon_anime3.h" +#include "objects/object_geff/object_geff.h" + +#include + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void BossGanon2_Init(Actor* thisx, GlobalContext* globalCtx); +void BossGanon2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossGanon2_Update(Actor* thisx, GlobalContext* globalCtx); +void BossGanon2_Draw(Actor* thisx, GlobalContext* globalCtx); +void BossGanon2_Reset(void); + +void func_808FD5C4(BossGanon2* this, GlobalContext* globalCtx); +void func_808FD5F4(BossGanon2* this, GlobalContext* globalCtx); +void func_808FFDB0(BossGanon2* this, GlobalContext* globalCtx); +void func_808FFEBC(BossGanon2* this, GlobalContext* globalCtx); +void func_808FFFE0(BossGanon2* this, GlobalContext* globalCtx); +void func_80900104(BossGanon2* this, GlobalContext* globalCtx); +void func_8090026C(BossGanon2* this, GlobalContext* globalCtx); +void func_809002CC(BossGanon2* this, GlobalContext* globalCtx); +void func_80900344(BossGanon2* this, GlobalContext* globalCtx); +void func_80900580(BossGanon2* this, GlobalContext* globalCtx); +void func_80900650(BossGanon2* this, GlobalContext* globalCtx); +void func_80900890(BossGanon2* this, GlobalContext* globalCtx); +void func_8090120C(BossGanon2* this, GlobalContext* globalCtx); +void func_80905DA8(BossGanon2* this, GlobalContext* globalCtx); +void func_809060E8(GlobalContext* globalCtx); +void BossGanon2_GenShadowTexture(void* shadowTexture, BossGanon2* this, GlobalContext* globalCtx); +void BossGanon2_DrawShadowTexture(void* shadowTexture, BossGanon2* this, GlobalContext* globalCtx); + +const ActorInit Boss_Ganon2_InitVars = { + ACTOR_BOSS_GANON2, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GANON2, + sizeof(BossGanon2), + (ActorFunc)BossGanon2_Init, + (ActorFunc)BossGanon2_Destroy, + (ActorFunc)BossGanon2_Update, + (ActorFunc)BossGanon2_Draw, + (ActorResetFunc)BossGanon2_Reset, +}; + +#include "z_boss_ganon2_data.c" + +void BossGanon2_InitRand(s32 seedInit0, s32 seedInit1, s32 seedInit2) { + sSeed1 = seedInit0; + sSeed2 = seedInit1; + sSeed3 = seedInit2; +} + +f32 BossGanon2_RandZeroOne(void) { + // Wichmann-Hill algorithm + f32 randFloat; + + sSeed1 = (sSeed1 * 171) % 30269; + sSeed2 = (sSeed2 * 172) % 30307; + sSeed3 = (sSeed3 * 170) % 30323; + + randFloat = (sSeed1 / 30269.0f) + (sSeed2 / 30307.0f) + (sSeed3 / 30323.0f); + while (randFloat >= 1.0f) { + randFloat -= 1.0f; + } + return fabsf(randFloat); +} + +void func_808FD080(s32 idx, ColliderJntSph* collider, Vec3f* arg2) { + collider->elements[idx].dim.worldSphere.center.x = arg2->x; + collider->elements[idx].dim.worldSphere.center.y = arg2->y; + collider->elements[idx].dim.worldSphere.center.z = arg2->z; + + collider->elements[idx].dim.worldSphere.radius = + collider->elements[idx].dim.modelSphere.radius * collider->elements[idx].dim.scale; +} + +void BossGanon2_SetObjectSegment(BossGanon2* this, GlobalContext* globalCtx, s32 objectId, u8 setRSPSegment) { + s32 pad; + s32 objectIdx = Object_GetIndex(&globalCtx->objectCtx, objectId); + + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[objectIdx].segment); + + if (setRSPSegment) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 790); + + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[objectIdx].segment); + gSPSegment(POLY_XLU_DISP++, 0x06, globalCtx->objectCtx.status[objectIdx].segment); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 799); + } +} + +void func_808FD210(GlobalContext* globalCtx, Vec3f* arg1) { + BossGanon2Effect* effect = globalCtx->specialEffects; + + effect->type = 1; + effect->position = *arg1; + effect->unk_2E = 0; + effect->unk_01 = 0; + effect->velocity.x = 25.0f; + effect->velocity.y = 15.0f; + effect->velocity.z = 0.0f; + effect->accel.x = 0.0f; + effect->accel.y = -1.0f; + effect->accel.z = 0.0f; +} + +void func_808FD27C(GlobalContext* globalCtx, Vec3f* position, Vec3f* velocity, f32 scale) { + BossGanon2Effect* effect = globalCtx->specialEffects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sParticles); i++, effect++) { + if (effect->type == 0) { + effect->type = 2; + effect->position = *position; + effect->velocity = *velocity; + effect->accel.x = 0.0; + effect->accel.y = -1.0f; + effect->accel.z = 0.0; + effect->unk_38.z = Rand_ZeroFloat(2 * M_PI); + effect->unk_38.y = Rand_ZeroFloat(2 * M_PI); + effect->unk_38.x = Rand_ZeroFloat(2 * M_PI); + effect->scale = scale; + break; + } + } +} + +void BossGanon2_Init(Actor* thisx, GlobalContext* globalCtx) { + BossGanon2* this = (BossGanon2*)thisx; + s32 pad; + s16 i; + + globalCtx->specialEffects = sParticles; + + for (i = 0; i < ARRAY_COUNT(sParticles); i++) { + sParticles[i].type = 0; + } + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.colChkInfo.health = 30; + Collider_InitJntSph(globalCtx, &this->unk_424); + Collider_SetJntSph(globalCtx, &this->unk_424, &this->actor, &sJntSphInit1, this->unk_464); + Collider_InitJntSph(globalCtx, &this->unk_444); + Collider_SetJntSph(globalCtx, &this->unk_444, &this->actor, &sJntSphInit2, this->unk_864); + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON, false); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDorfSkel, NULL, NULL, NULL, 0); + func_808FD5C4(this, globalCtx); + this->actor.naviEnemyId = 0x3E; + this->actor.gravity = 0.0f; +} + +void BossGanon2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BossGanon2* this = (BossGanon2*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyJntSph(globalCtx, &this->unk_424); + Collider_DestroyJntSph(globalCtx, &this->unk_444); +} + +void func_808FD4D4(BossGanon2* this, GlobalContext* globalCtx, s16 arg2, s16 arg3) { + if ((arg2 == 0) || (arg2 == 1)) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->unk_1D0, 25.0f, arg3, 8.0f, 0x1F4, 0xA, 1); + } + + if ((arg2 == 0) || (arg2 == 2)) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->unk_1DC, 25.0f, arg3, 8.0f, 0x1F4, 0xA, 1); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_WALK); + func_80033E88(&this->actor, globalCtx, 2, 0xA); +} + +void func_808FD5C4(BossGanon2* this, GlobalContext* globalCtx) { + this->actionFunc = func_808FD5F4; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.world.pos.y = -3000.0f; +} + +void func_808FD5F4(BossGanon2* this, GlobalContext* globalCtx) { + s16 pad; + u8 sp8D; + Player* player; + s32 objectIdx; + s32 zero = 0; + s32 pad2; + + sp8D = false; + player = GET_PLAYER(globalCtx); + this->unk_398++; + + switch (this->unk_39C) { + case 0: + objectIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON_ANIME3); + if (Object_IsLoaded(&globalCtx->objectCtx, objectIdx)) { + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->unk_39E = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_39E, CAM_STAT_ACTIVE); + this->unk_39C = 1; + sZelda = (EnZl3*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ZL3, 970.0f, + 1086.0f, -200.0f, 0, 0, 0, 1); + sZelda->unk_3C8 = 0; + sZelda->actor.world.pos.x = 970.0f; + sZelda->actor.world.pos.y = 1086.0f; + sZelda->actor.world.pos.z = -214.0f; + sZelda->actor.shape.rot.y = -0x7000; + this->unk_3BC.x = 0.0f; + this->unk_3BC.y = 1.0f; + this->unk_3BC.z = 0.0f; + this->unk_398 = 0; + this->unk_3A4.x = 0.0f; + this->unk_3A4.y = 1400.0f; + this->unk_3A4.z = 1600.0f; + player->actor.world.pos.x = 970.0f; + player->actor.world.pos.y = 1086.0f; + player->actor.world.pos.z = -186.0f; + player->actor.shape.rot.y = -0x5000; + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime3_Anim_002168, 0.0f); + globalCtx->envCtx.unk_D8 = 0.0f; + // fake, tricks the compiler into allocating more stack + if (zero) { + this->unk_3A4.x *= 2.0; + } + } else { + break; + } + case 1: + if (this->unk_398 < 70) { + globalCtx->envCtx.unk_D8 = 0.0f; + } + this->unk_339 = 3; + Math_ApproachF(&this->unk_3A4.x, 1500.0f, 0.1f, this->unk_410.x * 1500.0f); + Math_ApproachF(&this->unk_3A4.z, -160.0f, 0.1f, this->unk_410.x * 1760.0f); + Math_ApproachF(&this->unk_410.x, 0.0075f, 1.0f, 0.0001f); + this->unk_3B0.x = -200.0f; + this->unk_3B0.y = 1086.0f; + this->unk_3B0.z = -200.0f; + if (this->unk_398 == 150) { + Message_StartTextbox(globalCtx, 0x70D3, NULL); + } + if (this->unk_398 > 250 && Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + this->unk_39C = 2; + this->unk_398 = 0; + this->unk_410.x = 0.0f; + globalCtx->envCtx.unk_D8 = 1.0f; + } else { + break; + } + case 2: + this->unk_339 = 4; + player->actor.world.pos.x = 970.0f; + player->actor.world.pos.y = 1086.0f; + player->actor.world.pos.z = -166.0f; + sZelda->actor.world.pos.x = 974.0f; + sZelda->actor.world.pos.y = 1086.0f; + sZelda->actor.world.pos.z = -186.0f; + player->actor.shape.rot.y = -0x5000; + sZelda->actor.shape.rot.y = -0x5000; + if (this->unk_398 == 60) { + Message_StartTextbox(globalCtx, 0x70D4, NULL); + } + if (this->unk_398 == 40) { + sZelda->unk_3C8 = 1; + func_8002DF54(globalCtx, &this->actor, 0x4E); + } + if (this->unk_398 == 85) { + sZelda->unk_3C8 = 2; + func_8002DF54(globalCtx, &this->actor, 0x4F); + } + this->unk_3A4.x = 930.0f; + this->unk_3A4.y = 1129.0f; + this->unk_3A4.z = -181.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.z = (player->actor.world.pos.z - 15.0f) + 5.0f; + if (this->unk_398 > 104) { + Math_ApproachF(&this->unk_3B0.y, player->actor.world.pos.y + 47.0f + 7.0f + 15.0f, 0.1f, + this->unk_410.x); + Math_ApproachF(&this->unk_410.x, 2.0f, 1.0f, 0.1f); + } else { + this->unk_3B0.y = player->actor.world.pos.y + 47.0f + 7.0f; + } + if ((this->unk_398 > 170) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->unk_39C = 3; + this->unk_398 = 0; + this->unk_410.x = 0.0f; + } + break; + case 3: + Math_ApproachF(&this->unk_3B0.y, player->actor.world.pos.y + 47.0f + 7.0f, 0.1f, 2.0f); + this->unk_339 = 4; + if (this->unk_398 == 10) { + func_80078914(&D_80906D6C, NA_SE_EV_STONE_BOUND); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); + } + if (this->unk_398 == 20) { + sZelda->unk_3C8 = 3; + func_8002DF54(globalCtx, &this->actor, 0x50); + } + if (this->unk_398 == 55) { + this->unk_39C = 4; + this->unk_398 = 0; + this->unk_410.x = 0.0f; + sZelda->unk_3C8 = 4; + func_8002DF54(globalCtx, &this->actor, 0x50); + } + break; + case 4: + this->unk_339 = 4; + Math_ApproachF(&this->unk_3A4.x, -360.0f, 0.1f, this->unk_410.x * 1290.0f); + Math_ApproachF(&this->unk_3A4.z, -20.0f, 0.1f, this->unk_410.x * 170.0f); + Math_ApproachF(&this->unk_410.x, 0.04f, 1.0f, 0.0005f); + if (this->unk_398 == 100) { + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + camera->eye = this->unk_3A4; + camera->eyeNext = this->unk_3A4; + camera->at = this->unk_3B0; + func_800C08AC(globalCtx, this->unk_39E, 0); + this->unk_39E = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->unk_39C = 5; + this->unk_398 = 0; + } + break; + case 5: + this->unk_339 = 4; + if (this->actor.xzDistToPlayer < 500.0f) { + Message_CloseTextbox(globalCtx); + this->unk_39C = 10; + this->unk_398 = 0; + func_80064520(globalCtx, &globalCtx->csCtx); + this->unk_39E = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_39E, CAM_STAT_ACTIVE); + } else { + break; + } + case 10: + player->actor.world.pos.x = 490.0f; + player->actor.world.pos.y = 1086.0f; + player->actor.world.pos.z = -166.0f; + sZelda->actor.world.pos.x = 724.0f; + sZelda->actor.world.pos.y = 1086.0f; + sZelda->actor.world.pos.z = -186.0f; + player->actor.shape.rot.y = -0x4000; + sZelda->actor.shape.rot.y = -0x5000; + this->unk_3A4.x = 410.0f; + this->unk_3A4.y = 1096.0f; + this->unk_3A4.z = -110.0f; + this->unk_3B0.x = player->actor.world.pos.x + 10.0f; + this->unk_3B0.y = (player->actor.world.pos.y + 200.0f) - 160.0f; + this->unk_3B0.z = player->actor.world.pos.z; + if (this->unk_398 >= 20) { + func_80078884(NA_SE_EN_GOMA_LAST - SFX_FLAG); + Math_ApproachF(&this->unk_324, 255.0f, 1.0f, 10.0f); + this->unk_339 = 5; + if (this->unk_398 == 20) { + this->unk_33C = 0.0f; + globalCtx->envCtx.unk_D8 = 0.0f; + } + } else { + this->unk_339 = 4; + } + if (this->unk_398 == 30) { + sZelda->unk_3C8 = 5; + func_8002DF54(globalCtx, &this->actor, 0x51); + } + if (this->unk_398 == 50) { + this->unk_398 = 0; + this->unk_39C = 11; + } + break; + case 11: + this->unk_339 = 5; + func_80078884(NA_SE_EN_GOMA_LAST - SFX_FLAG); + player->actor.world.pos.x = 490.0f; + player->actor.world.pos.y = 1086.0f; + player->actor.world.pos.z = -166.0f; + sZelda->actor.world.pos.x = 724.0f; + sZelda->actor.world.pos.y = 1086.0f; + sZelda->actor.world.pos.z = -186.0f; + player->actor.shape.rot.y = -0x4000; + sZelda->actor.shape.rot.y = -0x5000; + this->unk_3A4.x = 450.0f; + this->unk_3A4.y = 1121.0f; + this->unk_3A4.z = -158.0f; + this->unk_3B0.x = (player->actor.world.pos.x - 20.0f) + 2.0f; + this->unk_3B0.y = ((player->actor.world.pos.y + 200.0f) - 151.0f) - 2.0f; + this->unk_3B0.z = player->actor.world.pos.z + 2.0f; + if (this->unk_398 == 10) { + func_80078914(&D_80906D6C, NA_SE_EV_STONE_BOUND); + } + if (this->unk_398 == 20) { + func_80078884(NA_SE_EV_STONE_BOUND); + } + if (this->unk_398 == 30) { + func_8002DF54(globalCtx, &this->actor, 0x52); + } + if (this->unk_398 == 50) { + this->unk_398 = 0; + this->unk_39C = 12; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_002168, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon_anime3_Anim_002168); + this->actor.world.pos.x = this->actor.world.pos.z = -200.0f; + this->actor.world.pos.y = 1009.0f; + this->actor.shape.yOffset = 7000.0f; + this->actor.world.rot.y = 0x5000; + this->unk_3A4.x = -60.0f; + this->unk_3A4.y = 1106.0f; + this->unk_3A4.z = -200.0f; + this->unk_3B0.x = this->unk_3B0.z = -200.0f; + this->unk_3B0.y = this->actor.world.pos.y + 70.0f; + globalCtx->envCtx.unk_D8 = 0.0f; + globalCtx->envCtx.unk_BE = globalCtx->envCtx.unk_BD = 0; + this->unk_339 = 0; + } else { + break; + } + case 12: + case 13: + SkelAnime_Update(&this->skelAnime); + if (this->unk_398 == 30) { + D_80906D78 = 1; + this->unk_314 = 1; + func_800A9F6C(0.0f, 0xC8, 0x14, 0x14); + } + if (this->unk_398 == 30) { + func_80078884(NA_SE_EV_GRAVE_EXPLOSION); + } + if (this->unk_398 >= 30) { + Math_ApproachF(&this->actor.world.pos.y, 1289.0f, 0.1f, 10.0f); + this->unk_3B0.y = this->actor.world.pos.y + 70.0f; + } + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime3_Anim_002E6C, 0.0f); + this->unk_39C = 14; + this->unk_398 = 0; + this->actor.world.pos.x = -200.0f; + this->actor.world.pos.y = this->actor.world.pos.y - 30.0f; + this->actor.world.pos.z = -200.0f; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_OPENING_GANON); + } else { + break; + } + case 14: + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.world.pos.y, 1289.0f, 0.05f, 1.0f); + player->actor.world.pos.x = 250.0f; + player->actor.world.pos.y = 1086.0f; + player->actor.world.pos.z = -266.0f; + player->actor.shape.rot.y = -0x4000; + sZelda->actor.world.pos.x = 724.0f; + sZelda->actor.world.pos.y = 1086.0f; + sZelda->actor.world.pos.z = -186.0f; + this->unk_3A4.x = this->actor.world.pos.x + -10.0f; + this->unk_3A4.y = this->actor.world.pos.y + 80.0f; + this->unk_3A4.z = this->actor.world.pos.z + 50.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = player->actor.world.pos.y; + this->unk_3B0.z = player->actor.world.pos.z - 200.0f; + if (this->unk_398 == 20) { + func_8002DF54(globalCtx, &this->actor, 0x1E); + } + if (this->unk_398 == 60) { + this->unk_3A4.x = (this->actor.world.pos.x + 200.0f) - 154.0f; + this->unk_3A4.y = this->actor.world.pos.y + 60.0f; + this->unk_3A4.z = this->actor.world.pos.z - 15.0f; + this->unk_39C = 15; + this->unk_398 = 0; + this->unk_3B0.y = this->actor.world.pos.y + 77.0f + 100.0f; + this->unk_314 = 2; + this->unk_3B0.z = this->actor.world.pos.z + 5.0f; + this->unk_3B0.x = this->actor.world.pos.x; + } + if ((globalCtx->gameplayFrames % 32) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BREATH); + } + break; + case 15: + if (((globalCtx->gameplayFrames % 32) == 0) && (this->unk_398 < 100)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BREATH); + } + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->unk_3B0.y, this->actor.world.pos.y + 77.0f, 0.05f, 5.0f); + if (this->unk_398 >= 50) { + if (this->unk_398 == 50) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_000BFC, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon_anime3_Anim_000BFC); + this->unk_314 = 3; + } + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon_anime3_Anim_003F38, 0.0f); + this->unk_194 = 1000.0f; + } + } + if (this->unk_398 > 70) { + Math_ApproachF(&this->unk_1B4, 255.0f, 1.0f, 10.0f); + } + if (this->unk_398 == 140) { + this->unk_39C = 16; + this->unk_398 = 0; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_003754, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon_anime3_Anim_003754); + this->unk_339 = 55; + globalCtx->envCtx.unk_D8 = 1.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CASBREAK); + } else { + break; + } + case 16: + if (this->unk_398 < 25) { + this->unk_339 = 55; + } else { + this->unk_339 = 6; + if (this->unk_194 > 100.0f) { + Math_ApproachF(&this->unk_30C, 15.0f, 1.0f, 2.0f); + } else { + Math_ApproachF(&this->unk_30C, 7.0f, 1.0f, 0.2f); + } + } + this->unk_1B4 = 0.0f; + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_0028A8, 0.0f); + this->unk_194 = 1000.0f; + } + Math_ApproachF(&this->unk_3A4.x, (this->actor.world.pos.x + 200.0f) - 90.0f, 0.1f, 6.3999996f); + Math_ApproachF(&this->unk_3A4.y, ((this->actor.world.pos.y + 60.0f) - 60.0f) - 70.0f, 0.1f, 13.0f); + Math_ApproachF(&this->unk_3B0.y, this->actor.world.pos.y + 40.0f, 0.1f, 3.6999998f); + if (this->unk_398 == 30) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BIGMASIC); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_THROW_BIG); + } + if (this->unk_398 <= 50) { + sp8D = true; + } + if (this->unk_398 >= 60) { + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + camera->eye = this->unk_3A4; + camera->eyeNext = this->unk_3A4; + camera->at = this->unk_3B0; + this->unk_39C = 17; + this->unk_398 = 0; + this->unk_337 = 2; + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON2, false); + SkelAnime_Free(&this->skelAnime, globalCtx); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ganon2_Skel_025970, NULL, NULL, NULL, 0); + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON_ANIME3, false); + func_8002DF54(globalCtx, &this->actor, 0x54); + this->unk_314 = 3; + } + // fake, tricks the compiler into using stack the way we need it to + if (zero) { + Math_ApproachF(&this->unk_3B0.y, 0.0f, 0.0f, 0.0f); + } + break; + case 17: + this->unk_339 = 6; + SkelAnime_Update(&this->skelAnime); + this->unk_3A4.x = player->actor.world.pos.x - 40.0f; + this->unk_3A4.y = player->actor.world.pos.y + 40.0f; + this->unk_3A4.z = player->actor.world.pos.z + 20.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = (player->actor.world.pos.y + 10.0f + 60.0f) - 30.0f; + this->unk_3B0.z = player->actor.world.pos.z; + if (this->unk_398 == 25) { + this->unk_39C = 18; + this->unk_398 = 0; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_010380, 0.0f); + this->skelAnime.playSpeed = 0.0f; + this->unk_3A4.x = ((this->actor.world.pos.x + 500.0f) - 350.0f) - 50.0f; + this->unk_3A4.y = this->actor.world.pos.y; + this->unk_3A4.z = this->actor.world.pos.z; + this->unk_3B0.x = this->actor.world.pos.x + 50.0f; + this->unk_3B0.y = this->actor.world.pos.y + 60.0f; + this->unk_3B0.z = this->actor.world.pos.z; + this->actor.world.rot.y = 0x4000; + } + break; + case 18: + this->unk_339 = 6; + if (this->unk_398 == 30) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_GANON_BOSS); + } + Math_ApproachF(&this->unk_30C, 7.0f, 1.0f, 0.1f); + Math_ApproachF(&this->unk_3A4.x, (this->actor.world.pos.x + 500.0f) - 350.0f, 0.1f, 1.0f); + Math_ApproachF(&this->unk_3B0.x, this->actor.world.pos.x, 0.1f, 1.0f); + Math_ApproachF(&this->unk_228, 1.0f, 0.1f, 0.02f); + if (this->unk_398 == 65) { + this->unk_39C = 19; + this->unk_398 = 0; + } + break; + case 19: + this->unk_394 += 0.5f; + this->unk_339 = 6; + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.velocity.y -= 1.0f; + if (this->unk_398 == 10) { + this->unk_39C = 20; + this->unk_398 = 0; + this->actor.world.pos.x += 250; + this->actor.world.pos.y = 1886.0f; + this->unk_394 = 0.0f; + func_8002DF54(globalCtx, &this->actor, 0x53); + this->unk_30C = 5.0f; + this->unk_228 = 1.0f; + } + break; + case 20: + this->unk_339 = 6; + SkelAnime_Update(&this->skelAnime); + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.velocity.y -= 1.0f; + player->actor.world.pos.x = 250.0f; + player->actor.world.pos.y = 1086.0f; + player->actor.world.pos.z = -266.0f; + player->actor.shape.rot.y = -0x4000; + this->unk_3A4.x = (player->actor.world.pos.x - 40.0f) - 200.0f; + this->unk_3A4.y = (player->actor.world.pos.y + 40.0f) - 30.0f; + this->unk_3A4.z = (player->actor.world.pos.z - 20.0f) + 100.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = ((player->actor.world.pos.y + 10.0f + 60.0f) - 20.0f) + 30.0f; + this->unk_3B0.z = player->actor.world.pos.z; + this->unk_3BC.x = 0.8f; + if (this->actor.world.pos.y <= 1099.0f) { + this->actor.world.pos.y = 1099.0f; + this->unk_39C = 21; + this->unk_398 = 0; + this->unk_420 = 10.0f; + this->actor.velocity.y = 0.0f; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_010380, 0.0f); + func_808FD4D4(this, globalCtx, 0, 3); + func_800A9F6C(0.0f, 0xC8, 0x14, 0x14); + } + break; + case 21: + this->unk_339 = 6; + SkelAnime_Update(&this->skelAnime); + this->unk_41C = Math_CosS(globalCtx->gameplayFrames * 0x8000) * this->unk_420; + Math_ApproachZeroF(&this->unk_420, 1.0f, 0.75f); + if (this->unk_398 == 30) { + this->unk_39C = 22; + this->unk_30C = 10.0f; + } else { + break; + } + case 22: + if (this->unk_398 < 60) { + this->unk_339 = 7; + } + this->unk_3BC.x = 0.0f; + this->actor.world.pos.y = 1099.0f; + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->unk_30C, 1.0f, 0.1f); + if (this->unk_398 > 50) { + Math_ApproachF(&this->unk_224, 1.0f, 1.0f, 0.025f); + } + if (this->unk_398 == 60) { + this->unk_336 = 2; + } + if (this->unk_398 == 80) { + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON2, false); + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(object_ganon2_Tex_021A90), 160, 180, 128, 40); + } + this->unk_3A4.x = ((this->actor.world.pos.x + 500.0f) - 350.0f) + 100.0f; + this->unk_3A4.y = this->actor.world.pos.y; + this->unk_3A4.z = this->actor.world.pos.z; + this->unk_3B0.x = this->actor.world.pos.x; + this->unk_3B0.z = this->actor.world.pos.z; + this->unk_3B0.y = (this->unk_1B8.y + 60.0f) - 40.0f; + if (this->unk_398 > 166 && this->unk_398 < 173) { + this->unk_312 = 2; + } + if (this->unk_398 > 186 && this->unk_398 < 196) { + this->unk_312 = 1; + } + if (this->unk_398 > 202 && this->unk_398 < 210) { + this->unk_312 = 2; + } + if ((this->unk_398 == 166) || (this->unk_398 == 185) || (this->unk_398 == 200)) { + func_80078884(NA_SE_EN_MGANON_SWORD); + func_80078884(NA_SE_EN_MGANON_ROAR); + } + if (this->unk_398 == 215) { + this->unk_39C = 23; + this->unk_224 = 0.0f; + func_8002DF54(globalCtx, &this->actor, 0x55); + } + break; + case 23: + SkelAnime_Update(&this->skelAnime); + if (this->unk_398 > 222 && this->unk_398 < 232) { + this->unk_312 = 2; + } + if (this->unk_398 == 222) { + func_80078884(NA_SE_EN_MGANON_SWORD); + func_80078884(NA_SE_EN_MGANON_ROAR); + } + this->unk_3A4.x = (player->actor.world.pos.x - 40.0f) + 6.0f; + this->unk_3A4.y = player->actor.world.pos.y + 40.0f; + this->unk_3A4.z = (player->actor.world.pos.z + 20.0f) - 7.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = ((player->actor.world.pos.y + 10.0f + 60.0f) - 20.0f) - 2.0f; + this->unk_3B0.z = player->actor.world.pos.z; + if (this->unk_398 == 228) { + func_80078884(NA_SE_IT_SHIELD_REFLECT_SW); + func_8002DF54(globalCtx, &this->actor, 0x56); + func_800A9F6C(0.0f, 0xFF, 0xA, 0x32); + } + if (this->unk_398 >= 229) { + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] = + globalCtx->envCtx.screenFillColor[2] = 255; + globalCtx->envCtx.screenFillColor[3] = 100; + if (this->unk_398 == 234) { + Vec3f sp68; + + globalCtx->envCtx.fillScreen = false; + this->unk_39C = 24; + this->unk_398 = 0; + sp68 = player->actor.world.pos; + sp68.y += 60.0f; + func_808FD210(globalCtx, &sp68); + globalCtx->envCtx.unk_D8 = 0.0f; + globalCtx->envCtx.unk_BE = 0; + this->unk_339 = 0; + } + } + break; + case 24: + SkelAnime_Update(&this->skelAnime); + if (1) { + BossGanon2Effect* effect = globalCtx->specialEffects; + + this->unk_3B0 = effect->position; + this->unk_3A4.x = effect->position.x + 70.0f; + this->unk_3A4.y = effect->position.y - 30.0f; + this->unk_3A4.z = effect->position.z + 70.0f; + } + if ((this->unk_398 & 3) == 0) { + func_80078884(NA_SE_IT_SWORD_SWING); + } + if (this->unk_398 == 25) { + func_8002DF54(globalCtx, &this->actor, 0x57); + this->unk_39C = 25; + this->unk_398 = 0; + } + break; + case 25: + SkelAnime_Update(&this->skelAnime); + this->unk_3A4.x = (player->actor.world.pos.x - 40.0f) + 80.0f; + this->unk_3A4.y = player->actor.world.pos.y + 40.0f + 10.0f; + this->unk_3A4.z = player->actor.world.pos.z + 20.0f + 10.0f; + this->unk_3B0.x = player->actor.world.pos.x - 20.0f; + this->unk_3B0.y = ((player->actor.world.pos.y + 10.0f + 60.0f) - 20.0f) - 3.0f; + this->unk_3B0.z = (player->actor.world.pos.z - 40.0f) - 10.0f; + if (this->unk_398 == 10) { + BossGanon2Effect* effect = globalCtx->specialEffects; + + effect->unk_2E = 1; + effect->position.x = sZelda->actor.world.pos.x + 50.0f + 10.0f; + effect->position.y = sZelda->actor.world.pos.y + 350.0f; + effect->position.z = sZelda->actor.world.pos.z - 25.0f; + effect->velocity.x = 0.0f; + effect->velocity.z = 0.0f; + effect->velocity.y = -30.0f; + this->unk_39C = 26; + this->unk_398 = 0; + } else { + break; + } + case 26: + this->unk_3A4.x = sZelda->actor.world.pos.x + 100.0f + 30.0f; + this->unk_3A4.y = sZelda->actor.world.pos.y + 10.0f; + this->unk_3A4.z = sZelda->actor.world.pos.z + 5.0f; + this->unk_3B0.x = sZelda->actor.world.pos.x; + this->unk_3B0.y = sZelda->actor.world.pos.y + 30.0f; + this->unk_3B0.z = sZelda->actor.world.pos.z - 20.0f; + this->unk_3BC.z = -0.5f; + if (this->unk_398 == 13) { + sZelda->unk_3C8 = 6; + } + if (this->unk_398 == 50) { + this->unk_39C = 27; + this->unk_398 = 0; + } + break; + case 27: + this->unk_3BC.z = 0.0f; + if (this->unk_398 == 4) { + func_8002DF54(globalCtx, &this->actor, 0x58); + } + this->unk_3A4.x = player->actor.world.pos.x - 20.0f; + this->unk_3A4.y = player->actor.world.pos.y + 50.0f; + this->unk_3A4.z = player->actor.world.pos.z; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = player->actor.world.pos.y + 50.0f; + this->unk_3B0.z = player->actor.world.pos.z; + if (this->unk_398 == 26) { + D_8090EB30 = globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + while (D_8090EB30 != NULL) { + if (D_8090EB30->id == ACTOR_EN_ELF) { + this->unk_3A4.x = D_8090EB30->world.pos.x - 30.0f; + this->unk_3A4.y = D_8090EB30->world.pos.y; + this->unk_3A4.z = D_8090EB30->world.pos.z; + this->unk_3B0.x = D_8090EB30->world.pos.x; + this->unk_3B0.y = D_8090EB30->world.pos.y; + this->unk_3B0.z = D_8090EB30->world.pos.z; + break; + } + D_8090EB30 = D_8090EB30->next; + } + this->unk_39C = 28; + this->unk_398 = 0; + } + break; + case 28: + if (this->unk_398 == 5) { + Message_StartTextbox(globalCtx, 0x70D6, NULL); + } + if (D_8090EB30 != NULL) { + this->unk_3A4.x = D_8090EB30->world.pos.x - 20.0f; + this->unk_3A4.y = D_8090EB30->world.pos.y; + this->unk_3A4.z = D_8090EB30->world.pos.z; + Math_ApproachF(&this->unk_3B0.x, D_8090EB30->world.pos.x, 0.2f, 50.0f); + Math_ApproachF(&this->unk_3B0.y, D_8090EB30->world.pos.y, 0.2f, 50.0f); + Math_ApproachF(&this->unk_3B0.z, D_8090EB30->world.pos.z, 0.2f, 50.0f); + if ((this->unk_398 > 40) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->unk_39C = 29; + this->unk_398 = 0; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon_anime3_Anim_0147E0, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon_anime3_Anim_0147E0); + this->actor.shape.yOffset = 0.0f; + this->actor.world.pos.y = 1086.0f; + this->actor.gravity = -1.0f; + this->unk_335 = 1; + this->unk_224 = 1.0f; + } + } + break; + case 29: + SkelAnime_Update(&this->skelAnime); + this->unk_3A4.x = (((this->actor.world.pos.x + 500.0f) - 350.0f) + 100.0f) - 60.0f; + this->unk_3B0.x = this->actor.world.pos.x; + this->unk_3B0.z = this->actor.world.pos.z; + this->unk_3A4.y = this->actor.world.pos.y; + this->unk_3A4.z = this->actor.world.pos.z + 10.0f; + this->unk_3B0.y = (this->unk_1B8.y + 60.0f) - 40.0f; + player->actor.shape.rot.y = -0x4000; + player->actor.world.pos.x = 140.0f; + player->actor.world.pos.z = -196.0f; + if (this->unk_398 == 50) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_ROAR); + } + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + camera->eye = this->unk_3A4; + camera->eyeNext = this->unk_3A4; + camera->at = this->unk_3B0; + func_800C08AC(globalCtx, this->unk_39E, 0); + this->unk_39E = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->unk_39C = 0; + this->unk_337 = 1; + func_808FFDB0(this, globalCtx); + this->unk_1A2[1] = 50; + this->actor.flags |= ACTOR_FLAG_0; + sZelda->unk_3C8 = 7; + } + break; + } + + if ((this->unk_30C > 4.0f) && !sp8D) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_BODY_SPARK - SFX_FLAG); + } + + if (this->unk_39E != 0) { + // fake, tricks the compiler into putting some pointers on the stack + if (zero) { + osSyncPrintf(NULL, 0, 0); + } + this->unk_3B0.y += this->unk_41C; + Gameplay_CameraSetAtEyeUp(globalCtx, this->unk_39E, &this->unk_3B0, &this->unk_3A4, &this->unk_3BC); + } +} + +void func_808FF898(BossGanon2* this, GlobalContext* globalCtx) { + if ((this->unk_312 != 0) && (this->unk_39E == 0)) { + Actor* actor = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + while (actor != NULL) { + if (actor->id == ACTOR_DEMO_GJ) { + DemoGj* gj = (DemoGj*)actor; + + if (((actor->params & 0xFF) == 0x10) || ((actor->params & 0xFF) == 0x11) || + ((actor->params & 0xFF) == 0x16)) { + if (SQ(this->unk_218.x - gj->dyna.actor.world.pos.x) + + SQ(this->unk_218.z - gj->dyna.actor.world.pos.z) < + SQ(100.0f)) { + s32 pad; + Vec3f sp28; + + Matrix_RotateY(((this->actor.shape.rot.y / (f32)0x8000) * M_PI) + 0.5f, MTXMODE_NEW); + sp28.x = 0.0f; + sp28.y = 0.0f; + sp28.z = 1.0f; + Matrix_MultVec3f(&sp28, &gj->unk_26C); + gj->killFlag = true; + func_800A9F6C(0.0f, 0x96, 0x14, 0x32); + this->unk_392 = 6; + return; + } + } + } + + actor = actor->next; + } + + if (this->unk_392 == 4) { + func_80078884(NA_SE_EV_GRAVE_EXPLOSION); + } + + if (this->unk_392 == 3) { + func_80078884(NA_SE_EN_MGANON_SWDIMP); + } + } +} + +s32 func_808FFA24(BossGanon2* this, GlobalContext* globalCtx) { + Actor* actor = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + + while (actor != NULL) { + if (actor->id == ACTOR_DEMO_GJ) { + DemoGj* gj = (DemoGj*)actor; + + if (((actor->params & 0xFF) == 0x10) || ((actor->params & 0xFF) == 0x11) || + ((actor->params & 0xFF) == 0x16)) { + if (SQ(this->actor.world.pos.x - gj->dyna.actor.world.pos.x) + + SQ(this->actor.world.pos.z - gj->dyna.actor.world.pos.z) < + SQ(200.0f)) { + return true; + } + } + } + + actor = actor->next; + } + + return false; +} + +void func_808FFAC8(BossGanon2* this, GlobalContext* globalCtx, u8 arg2) { + s16 temp_v1; + s16 phi_a1; + + if (this->unk_313 || (arg2 != 0)) { + phi_a1 = this->actor.shape.rot.y - this->actor.yawTowardsPlayer; + + if (phi_a1 > 0x3000) { + phi_a1 = 0x3000; + } else if (phi_a1 < -0x3000) { + phi_a1 = -0x3000; + } + } else if (this->unk_19C & 0x20) { + phi_a1 = 0x3000; + } else { + phi_a1 = -0x3000; + } + + Math_ApproachS(&this->unk_31A, phi_a1, 5, 0x7D0); + + temp_v1 = Math_Atan2S(this->actor.xzDistToPlayer, 150.0f) - 0xBB8; + temp_v1 = CLAMP_MAX(temp_v1, 0x1B58); + temp_v1 = CLAMP_MIN(temp_v1, -0x1B58); + + Math_ApproachS(&this->unk_31C, temp_v1, 5, 0x7D0); +} + +void func_808FFBBC(BossGanon2* this, GlobalContext* globalCtx, u8 arg2) { + if (arg2 != 0 || this->unk_313) { + f32 phi_f0; + f32 phi_f2; + + Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 5, this->unk_320); + + if (this->unk_334 != 0) { + phi_f0 = 5000.0f; + phi_f2 = 200.0f; + } else { + phi_f0 = 3000.0f; + phi_f2 = 30.0f; + } + + Math_ApproachF(&this->unk_320, phi_f0, 1.0f, phi_f2); + } else { + this->unk_320 = 0.0f; + } +} + +void func_808FFC84(BossGanon2* this) { + if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x2800) { + this->unk_313 = true; + this->actor.focus.pos = this->unk_1B8; + } else { + this->unk_313 = false; + this->actor.focus.pos = this->unk_1C4; + } +} + +void func_808FFCFC(BossGanon2* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer < 150.0f && + ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x2800) { + this->unk_311 = false; + func_80900580(this, globalCtx); + Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); + } else if ((this->actor.bgCheckFlags & 8) && func_808FFA24(this, globalCtx)) { + this->unk_311 = false; + func_80900580(this, globalCtx); + Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); + } +} + +void func_808FFDB0(BossGanon2* this, GlobalContext* globalCtx) { + s32 sp28; + s32 objectIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GANON2); + + if (Object_IsLoaded(&globalCtx->objectCtx, objectIdx)) { + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[objectIdx].segment); + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_00FFE4, -10.0f); + this->actionFunc = func_808FFEBC; + + if (this->unk_334 != 0) { + this->unk_1A2[0] = Rand_ZeroFloat(30.0f); + } else { + this->unk_1A2[0] = 40; + } + + this->unk_336 = 1; + this->actor.flags |= ACTOR_FLAG_0; + this->unk_228 = 1.0f; + this->unk_224 = 1.0f; + } else { + this->actionFunc = func_808FFDB0; + } +} + +void func_808FFEBC(BossGanon2* this, GlobalContext* globalCtx) { + if (this->unk_390 == 0) { + this->unk_390 = (s16)Rand_ZeroFloat(50.0f) + 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_UNARI); + } + + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 1.0f); + + if (this->unk_1A2[0] == 0) { + func_809002CC(this, globalCtx); + } else if (this->unk_1A2[1] == 0) { + func_808FFCFC(this, globalCtx); + } + + func_808FFAC8(this, globalCtx, 0); + func_808FFBBC(this, globalCtx, 0); +} + +void func_808FFF90(BossGanon2* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_00FFE4, -10.0f); + this->actionFunc = func_808FFFE0; + this->unk_1A2[0] = 40; +} + +void func_808FFFE0(BossGanon2* this, GlobalContext* globalCtx) { + s16 target; + + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 1.0f); + + if (this->unk_1A2[0] == 0) { + func_809002CC(this, globalCtx); + } + + if (this->unk_1A2[0] < 30 && this->unk_1A2[0] >= 10) { + target = Math_SinS(this->unk_1A2[0] * 0x3000) * (f32)0x2000; + } else { + target = 0; + } + + Math_ApproachS(&this->unk_31A, target, 2, 0x4000); +} + +void func_809000A0(BossGanon2* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_026510, -2.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_026510); + this->unk_1AC = 0; + this->actionFunc = func_80900104; +} + +void func_80900104(BossGanon2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 1.0f); + + switch (this->unk_1AC) { + case 0: + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + this->unk_1AC = 1; + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_026AF4, 0.0f); + this->unk_1A2[0] = 80; + } + break; + case 1: + if (this->unk_1A2[0] == 0) { + this->unk_1AC = 2; + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_027824, -5.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_027824); + } + break; + case 2: + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + func_809002CC(this, globalCtx); + } + break; + } +} + +void func_80900210(BossGanon2* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_00DFF0, -3.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_00DFF0); + this->actionFunc = func_8090026C; +} + +void func_8090026C(BossGanon2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + func_809002CC(this, globalCtx); + } +} + +void func_809002CC(BossGanon2* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_00E8EC, -10.0f); + this->actionFunc = func_80900344; + this->unk_338 = 0; + this->unk_1A2[0] = 100; + this->unk_390 = (s16)Rand_ZeroFloat(50.0f) + 50; +} + +void func_80900344(BossGanon2* this, GlobalContext* globalCtx) { + f32 phi_f0; + + if (this->unk_390 == 0) { + this->unk_390 = (s16)Rand_ZeroFloat(50.0f) + 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_UNARI); + } + + Math_ApproachF(&this->unk_324, 255.0f, 1.0f, 10.0f); + + if (this->unk_338 != 0) { + if (Animation_OnFrame(&this->skelAnime, 13.0f)) { + func_808FD4D4(this, globalCtx, 1, 3); + } else if (Animation_OnFrame(&this->skelAnime, 28.0f)) { + func_808FD4D4(this, globalCtx, 2, 3); + } + if (this->actor.xzDistToPlayer < 200.0f) { + this->unk_338 = 0; + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_00E8EC, -10.0f); + } else { + this->skelAnime.playSpeed = ((this->actor.xzDistToPlayer - 300.0f) * 0.005f) + 1.0f; + if (this->skelAnime.playSpeed > 2.0f) { + this->skelAnime.playSpeed = 2.0f; + } + if (this->unk_334 != 0) { + this->skelAnime.playSpeed *= 1.5f; + } + } + phi_f0 = this->skelAnime.playSpeed * 3.0f; + } else { + phi_f0 = 2.0f; + if (this->actor.xzDistToPlayer >= 200.0f) { + this->unk_338 = 1; + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_0353C0, -10.0f); + } + } + + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.speedXZ, phi_f0, 0.5f, 1.0f); + + if (this->unk_1A2[0] == 0) { + func_808FFDB0(this, globalCtx); + } else { + func_808FFCFC(this, globalCtx); + } + + func_808FFAC8(this, globalCtx, 1); + func_808FFBBC(this, globalCtx, 1); +} + +void func_80900580(BossGanon2* this, GlobalContext* globalCtx) { + if (this->unk_311 == 0) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_00ADD0, -5.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_00ADD0); + this->unk_198 = (this->unk_194 - 15.0f) - 5.0f; + } else { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_00CAF8, -5.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_00CAF8); + this->unk_198 = (this->unk_194 - 15.0f) - 5.0f; + } + + this->actionFunc = func_80900650; +} + +void func_80900650(BossGanon2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, this->unk_198)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_SWORD); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_ROAR); + } + + if (this->unk_311 == 0) { + if (((this->unk_198 - 4.0f) < this->skelAnime.curFrame) && + (this->skelAnime.curFrame < (this->unk_198 + 6.0f))) { + this->unk_312 = 1; + } + } else if ((((this->unk_198 - 4.0f) + 4.0f) < this->skelAnime.curFrame) && + (this->skelAnime.curFrame < (this->unk_198 + 6.0f))) { + this->unk_312 = 2; + } + + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 1.0f); + + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + this->unk_311 = 1 - this->unk_311; + + if ((this->unk_311 == 1) && (this->actor.xzDistToPlayer < 250.0f) && this->unk_313) { + func_80900580(this, globalCtx); + } else { + func_808FFDB0(this, globalCtx); + } + } + + func_808FFAC8(this, globalCtx, 0); + + if ((this->unk_334 == 0) && (this->unk_311 == 0)) { + this->unk_320 = 0.0f; + } else { + func_808FFBBC(this, globalCtx, 0); + } +} + +void func_80900818(BossGanon2* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_02A848, -5.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_02A848); + this->actionFunc = func_80900890; + this->unk_1AC = 0; + this->unk_39C = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DEAD1); + this->unk_336 = 0; +} + +void func_80900890(BossGanon2* this, GlobalContext* globalCtx) { + Vec3f sp5C; + Vec3f sp50; + Camera* sp4C; + Player* player; + Camera* temp_v0; + Camera* temp_v0_2; + s32 pad; + f32 temp_f12; + f32 temp_f2; + + sp4C = Gameplay_GetCamera(globalCtx, MAIN_CAM); + player = GET_PLAYER(globalCtx); + SkelAnime_Update(&this->skelAnime); + this->unk_398++; + this->unk_339 = 20; + + switch (this->unk_39C) { + case 0: + func_80064520(globalCtx, &globalCtx->csCtx); + this->unk_39E = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_39E, CAM_STAT_ACTIVE); + func_8002DF54(globalCtx, &this->actor, 8); + this->unk_39C = 1; + this->unk_3A4 = sp4C->eye; + this->unk_3B0 = sp4C->at; + this->unk_1A2[0] = 300; + this->unk_1A2[1] = 100; + globalCtx->envCtx.unk_D8 = 0.0f; + case 1: + if (this->unk_1A2[1] == 50) { + func_80078884(NA_SE_EN_MGANON_WALK); + } + Matrix_RotateY(((this->actor.shape.rot.y / (f32)0x8000) * M_PI) + 0.3f, MTXMODE_NEW); + sp5C.x = 0.0f; + sp5C.y = 0.0f; + sp5C.z = 250.0f; + Matrix_MultVec3f(&sp5C, &sp50); + Math_ApproachF(&this->unk_3A4.x, this->actor.world.pos.x + sp50.x, 0.2f, 100.0f); + Math_ApproachF(&this->unk_3A4.y, 1136.0f, 0.2f, 100.0f); + Math_ApproachF(&this->unk_3A4.z, this->actor.world.pos.z + sp50.z, 0.2f, 100.0f); + Math_ApproachF(&this->unk_3B0.x, this->unk_1B8.x, 0.2f, 100.0f); + Math_ApproachF(&this->unk_3B0.y, this->unk_1B8.y, 0.2f, 100.0f); + Math_ApproachF(&this->unk_3B0.z, this->unk_1B8.z, 0.2f, 100.0f); + if (this->unk_1A2[1] == 0) { + this->unk_39C = 2; + this->unk_1A2[1] = 90; + } + break; + case 2: + this->unk_1A2[0] = 300; + this->unk_3A4.x = sZelda->actor.world.pos.x - 100.0f; + this->unk_3A4.y = sZelda->actor.world.pos.y + 30.0f; + this->unk_3A4.z = (sZelda->actor.world.pos.z + 30.0f) - 60.0f; + this->unk_3B0.x = sZelda->actor.world.pos.x; + this->unk_3B0.y = sZelda->actor.world.pos.y + 30.0f; + this->unk_3B0.z = sZelda->actor.world.pos.z - 10.0f; + Math_ApproachZeroF(&this->unk_324, 1.0f, 5.0f); + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 1.0f / 51); + if (this->unk_1A2[1] == 80) { + Message_StartTextbox(globalCtx, 0x70D7, NULL); + } + if ((this->unk_1A2[1] < 30) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + temp_v0 = Gameplay_GetCamera(globalCtx, MAIN_CAM); + temp_v0->eye = this->unk_3A4; + temp_v0->eyeNext = this->unk_3A4; + temp_v0->at = this->unk_3B0; + func_800C08AC(globalCtx, this->unk_39E, 0); + this->unk_39E = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->unk_39C = 3; + } + break; + case 10: + func_80064520(globalCtx, &globalCtx->csCtx); + this->unk_39E = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_39E, CAM_STAT_ACTIVE); + this->unk_39C = 11; + this->unk_334 = 1; + func_8002DF54(globalCtx, &this->actor, 0x60); + this->unk_398 = 0; + case 11: + player->actor.world.pos.x = sZelda->actor.world.pos.x + 50.0f + 10.0f; + player->actor.world.pos.z = sZelda->actor.world.pos.z - 25.0f; + player->actor.shape.rot.y = -0x8000; + this->unk_3A4.x = (player->actor.world.pos.x + 100.0f) - 80.0f; + this->unk_3A4.y = (player->actor.world.pos.y + 60.0f) - 40.0f; + this->unk_3A4.z = player->actor.world.pos.z - 110.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = (player->actor.world.pos.y + 60.0f) - 25.0f; + this->unk_3B0.z = player->actor.world.pos.z; + if (this->unk_398 == 80) { + temp_v0_2 = Gameplay_GetCamera(globalCtx, MAIN_CAM); + temp_v0_2->eye = this->unk_3A4; + temp_v0_2->eyeNext = this->unk_3A4; + temp_v0_2->at = this->unk_3B0; + this->unk_39C = 3; + func_800C08AC(globalCtx, this->unk_39E, 0); + this->unk_39E = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + } + break; + } + + if (this->unk_39E != 0) { + Gameplay_CameraSetAtEye(globalCtx, this->unk_39E, &this->unk_3B0, &this->unk_3A4); + } + + switch (this->unk_1AC) { + case 0: + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_034278, 0.0f); + this->unk_1AC = 1; + } + break; + case 1: + if ((globalCtx->gameplayFrames % 32) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_BREATH); + } + + if ((this->unk_1A2[0] == 0) || (this->unk_334 != 0)) { + temp_f2 = -200.0f - player->actor.world.pos.x; + temp_f12 = -200.0f - player->actor.world.pos.z; + + if (sqrtf(SQ(temp_f2) + SQ(temp_f12)) <= 784.0f) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_0334F8, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_0334F8); + this->unk_1AC = 2; + this->unk_1A2[0] = 40; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_ROAR); + } + } + break; + case 2: + Math_ApproachF(&this->unk_324, 255.0f, 1.0f, 10.0f); + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 2.0f / 51.0f); + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + func_808FFDB0(this, globalCtx); + if (this->unk_334 == 0) { + this->actor.colChkInfo.health = 25; + } + this->unk_336 = 1; + } + break; + } + + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 1.0f); +} + +void func_80901020(BossGanon2* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_02A848, -5.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_02A848); + this->actionFunc = func_8090120C; + this->unk_1AC = 0; + this->unk_39C = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DEAD1); + this->unk_314 = 4; +} + +void func_8090109C(BossGanon2* this, GlobalContext* globalCtx) { + u8 i; + + for (i = 0; i < 70; i++) { + Vec3f velocity; + Vec3f accel; + Vec3f pos; + + velocity.x = Rand_CenteredFloat(50.0f); + velocity.y = Rand_CenteredFloat(10.0f) + 5.0f; + velocity.z = Rand_CenteredFloat(50.0f); + accel.x = 0.0f; + accel.y = -1.0f; + accel.z = 0.0f; + pos.x = this->unk_1B8.x; + pos.y = this->unk_1B8.y; + pos.z = this->unk_1B8.z; + func_8002836C(globalCtx, &pos, &velocity, &accel, &sPrimColor, &sEnvColor, (s16)Rand_ZeroFloat(50.0f) + 50, 0, + 17); + } +} + +void func_8090120C(BossGanon2* this, GlobalContext* globalCtx) { + Player* player; + f32 temp_f14; + f32 temp_f12; + Camera* temp_v0_2; + s16 temp_a0_2; + f32 phi_f0; + s32 phi_a1; + + player = GET_PLAYER(globalCtx); + this->unk_398++; + SkelAnime_Update(&this->skelAnime); + + this->unk_3BC.x = 0.0f; + this->unk_3BC.y = 1.0f; + this->unk_3BC.z = 0.0f; + + switch (this->unk_39C) { + case 0: + func_80064520(globalCtx, &globalCtx->csCtx); + this->unk_39E = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_39E, CAM_STAT_ACTIVE); + func_8002DF54(globalCtx, &this->actor, 8); + this->unk_39C = 1; + this->unk_398 = 0; + sZelda->unk_3C8 = 9; + this->unk_31C = 0; + this->unk_1A2[2] = 0; + this->unk_336 = 0; + this->unk_324 = 0.0f; + this->actor.speedXZ = 0.0f; + this->unk_31A = this->unk_31C; + globalCtx->envCtx.unk_D8 = 0.0f; + case 1: + if (this->unk_398 < 90) { + this->unk_339 = 20; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.1f); + } else if (this->unk_398 >= 90) { + this->unk_339 = 21; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.08f); + } + if (this->unk_398 == 50) { + func_80078884(NA_SE_EN_MGANON_WALK); + } + if (this->unk_398 > 90) { + Math_ApproachF(&this->unk_380, 0.25f, 1.0f, 0.0125f); + this->unk_37C = 200.0f; + func_80078884(NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); + } + if (this->unk_398 >= 110) { + if (this->unk_398 == 110) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_HIT_THUNDER); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); + } + Math_ApproachF(&this->unk_30C, 10.0f, 0.2f, 5.0f); + this->skelAnime.playSpeed = 3.0f; + } + if (this->unk_398 == 120) { + func_8002DF54(globalCtx, &this->actor, 0x63); + } + this->actor.world.rot.y = 0x4000; + this->actor.world.pos.x = this->actor.world.pos.z = 0.0f; + player->actor.shape.rot.y = -0x4000; + player->actor.world.pos.x = 200.0f; + player->actor.world.pos.z = 30.0f; + sZelda->actor.world.pos.x = 340.0f; + sZelda->actor.world.pos.z = -250.0f; + sZelda->actor.world.rot.y = sZelda->actor.shape.rot.y = -0x2000; + this->unk_3A4.x = 250; + this->unk_3A4.y = 1150.0f; + this->unk_3A4.z = 0.0f; + this->unk_3B0.x = this->unk_1B8.x; + this->unk_3B0.y = this->unk_1B8.y; + this->unk_3B0.z = this->unk_1B8.z; + if (this->unk_398 > 135) { + this->unk_39C = 2; + this->unk_398 = 0; + } + break; + case 2: + this->unk_339 = 22; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.1f); + func_80078884(NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); + this->unk_3A4.x = 250; + this->unk_3A4.y = 1150.0f; + this->unk_3A4.z = 0.0f; + Math_ApproachF(&this->unk_3B0.x, sZelda->actor.world.pos.x, 0.2f, 20.0f); + Math_ApproachF(&this->unk_3B0.y, sZelda->actor.world.pos.y + 50.0f, 0.2f, 10.0f); + Math_ApproachF(&this->unk_3B0.z, sZelda->actor.world.pos.z, 0.2f, 20.0f); + if (this->unk_398 == 50) { + this->unk_39C = 3; + this->unk_398 = 0; + } + break; + case 3: + this->unk_339 = 22; + func_80078884(NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); + this->unk_3A4.x = 330.0f; + this->unk_3A4.y = 1120.0f; + this->unk_3A4.z = -150.0f; + this->unk_3B0.x = sZelda->actor.world.pos.x; + this->unk_3B0.y = sZelda->actor.world.pos.y + 40.0f; + this->unk_3B0.z = sZelda->actor.world.pos.z; + if (this->unk_398 == 10) { + Message_StartTextbox(globalCtx, 0x70D8, NULL); + } + if ((this->unk_398 > 80) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + this->unk_39C = 4; + this->unk_398 = 0; + } + break; + case 4: + if (this->unk_398 > 10) { + Math_ApproachZeroF(&this->unk_37C, 1.0f, 10.0f); + if (this->unk_398 == 30) { + sZelda->unk_3C8 = 10; + } + this->unk_339 = 23; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + } else { + this->unk_339 = 22; + } + if (this->unk_398 == 100) { + this->unk_39C = 5; + this->unk_398 = 40; + this->skelAnime.playSpeed = 1.0f; + func_8002DF54(globalCtx, &this->actor, 0x64); + } + break; + case 5: + this->unk_339 = 23; + if ((this->unk_398 >= 60) && (this->unk_398 <= 90)) { + if (this->unk_398 == 62) { + func_80078884(NA_SE_EV_TRIFORCE_FLASH); + } + Math_ApproachF(&this->unk_38C, 200.0f, 1.0f, 8.0f); + } else { + Math_ApproachZeroF(&this->unk_38C, 1.0f, 8.0f); + } + if (this->unk_398 == 70) { + func_8002DF54(globalCtx, &this->actor, 0x65); + } + if (this->unk_398 == 150) { + func_8002DF54(globalCtx, &this->actor, 0x66); + } + this->unk_30C = 10.0f; + player->actor.world.pos.x = 250.0f; + player->actor.world.pos.z = 30.0f; + this->unk_3A4.x = player->actor.world.pos.x - 50.0f; + this->unk_3A4.y = player->actor.world.pos.y + 50.0f; + this->unk_3A4.z = player->actor.world.pos.z + 40.0f; + this->unk_3B0.x = player->actor.world.pos.x; + this->unk_3B0.y = player->actor.world.pos.y + 40.0f; + this->unk_3B0.z = player->actor.world.pos.z; + if (this->unk_398 == 166) { + temp_v0_2 = Gameplay_GetCamera(globalCtx, MAIN_CAM); + temp_v0_2->eye = this->unk_3A4; + temp_v0_2->eyeNext = this->unk_3A4; + temp_v0_2->at = this->unk_3B0; + func_800C08AC(globalCtx, this->unk_39E, 0); + this->unk_39E = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->unk_39C = 6; + } + break; + case 6: + this->unk_339 = 23; + temp_f14 = this->unk_1B8.x - player->actor.world.pos.x; + temp_f12 = this->unk_1B8.z - player->actor.world.pos.z; + temp_a0_2 = Math_Atan2S(temp_f12, temp_f14) - player->actor.shape.rot.y; + if ((ABS(temp_a0_2) < 0x2000) && (sqrtf(SQ(temp_f14) + SQ(temp_f12)) < 70.0f) && + (player->swordState != 0) && (player->heldItemActionParam == PLAYER_AP_SWORD_MASTER)) { + func_80064520(globalCtx, &globalCtx->csCtx); + this->unk_39E = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_39E, CAM_STAT_ACTIVE); + this->unk_39C = 7; + this->unk_398 = 0; + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_003B1C, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_003B1C); + globalCtx->startPlayerCutscene(globalCtx, &this->actor, 0x61); + } else { + break; + } + case 7: + this->unk_339 = 23; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.2f); + player->actor.world.pos.x = 250.0f; + player->actor.shape.rot.y = -0x4000; + player->actor.world.pos.z = 30.0f; + if ((this->unk_398 == 20) || (this->unk_398 == 30) || (this->unk_398 == 65) || (this->unk_398 == 40)) { + func_80078884(NA_SE_VO_LI_SWORD_N); + func_80078884(NA_SE_IT_SWORD_SWING_HARD); + } + if ((this->unk_398 == 22) || (this->unk_398 == 35) || (this->unk_398 == 72) || (this->unk_398 == 45)) { + func_80078884(NA_SE_EN_MGANON_DAMAGE); + func_80078884(NA_SE_IT_SHIELD_BOUND); + globalCtx->envCtx.unk_D8 = 1.0f; + } + if ((this->unk_398 == 22) || (this->unk_398 == 35) || (this->unk_398 == 72) || (this->unk_398 == 45)) { + func_8090109C(this, globalCtx); + } + if ((this->unk_398 >= 34) && (this->unk_398 < 40)) { + this->unk_3A4.x = 269.0f; + this->unk_3A4.y = 1112.0f; + this->unk_3A4.z = -28.0f; + this->unk_3B0.x = 234.0f; + this->unk_3B0.y = 1117.0f; + this->unk_3B0.z = -11.0f; + } else { + if (this->unk_398 < 30) { + phi_a1 = 0; + } else if (this->unk_398 < 43) { + phi_a1 = 1; + } else { + this->unk_3BC.z = -0.8f; + player->actor.world.pos.x = 200.0f; + player->actor.world.pos.z = 10.0f; + phi_a1 = 2; + } + this->unk_3A4.x = D_8090702C[phi_a1].x + (player->actor.world.pos.x - 50.0f); + this->unk_3A4.y = D_8090702C[phi_a1].y + (player->actor.world.pos.y + 50.0f); + this->unk_3A4.z = D_8090702C[phi_a1].z + (player->actor.world.pos.z + 40.0f); + this->unk_3B0.x = D_80907050[phi_a1].x + player->actor.world.pos.x; + this->unk_3B0.y = D_80907050[phi_a1].y + (player->actor.world.pos.y + 40.0f); + this->unk_3B0.z = D_80907050[phi_a1].z + player->actor.world.pos.z; + } + if (this->unk_398 > 80) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + this->unk_39C = 75; + this->unk_398 = 0; + this->unk_3A4.x = 112.0f; + this->unk_3A4.y = 1146.0f; + this->unk_3A4.z = 202.0f; + this->unk_3B0.x = 110.0f; + this->unk_3B0.y = 1144.0f; + this->unk_3B0.z = 177.0f; + player->actor.world.pos.x = 200.0f; + this->unk_3BC.z = 0.0f; + } + break; + case 75: + this->unk_339 = 23; + if (this->unk_398 == 55) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_007288, 0.0f); + this->unk_194 = Animation_GetLastFrame(&object_ganon2_Anim_007288); + func_8002DF54(globalCtx, &this->actor, 0x62); + this->unk_39C = 8; + this->unk_398 = 1000; + } + break; + case 8: + if (this->unk_398 == 1025) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_STAND); + } + if (this->unk_398 >= 1000) { + if (this->unk_398 < 1040) { + this->unk_339 = 23; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.2f); + } + } + if (this->unk_398 == 1040) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DEAD2); + this->unk_336 = 2; + this->unk_339 = 0; + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_D8 = 0.0f; + } + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_ganon2_Anim_008EB8, 0.0f); + this->unk_398 = 0; + this->unk_194 = 1000.0f; + } + this->unk_3A4.x = 250; + this->unk_3A4.y = 1150.0f; + this->unk_3A4.z = 0.0f; + this->unk_3B0.x = this->unk_1B8.x; + this->unk_3B0.y = this->unk_1B8.y; + this->unk_3B0.z = this->unk_1B8.z; + if ((this->unk_398 < 1000) && ((this->unk_398 % 16) == 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_SWORD); + } + if (this->unk_398 == 40) { + this->unk_39C = 9; + this->unk_398 = 0; + sZelda->unk_3C8 = 11; + Message_StartTextbox(globalCtx, 0x70D9, NULL); + this->unk_336 = 0; + globalCtx->envCtx.unk_D8 = 0.0f; + } + break; + case 9: + this->unk_339 = 24; + this->unk_3A4.x = 330.0f; + this->unk_3A4.y = 1120.0f; + this->unk_3A4.z = -150.0f; + this->unk_3B0.x = sZelda->actor.world.pos.x; + this->unk_3B0.y = sZelda->actor.world.pos.y + 40.0f; + this->unk_3B0.z = sZelda->actor.world.pos.z; + if (this->unk_398 > 60) { + this->unk_39C = 10; + this->unk_398 = 0; + this->unk_410.x = 0.0f; + } + break; + case 10: + this->unk_339 = 24; + Math_ApproachF(&this->unk_3A4.x, 290.0f, 0.05f, this->unk_410.x); + Math_ApproachF(&this->unk_3A4.y, 1130.0f, 0.05f, this->unk_410.x * 0.25f); + Math_ApproachF(&this->unk_3A4.z, -260.0f, 0.05f, this->unk_410.x * 1.25f); + if ((this->unk_398 >= 40) && (this->unk_398 <= 110)) { + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.02f); + Math_ApproachF(&this->unk_384, 10.0f, 0.1f, 0.2f); + Audio_PlayActorSound2(&sZelda->actor, NA_SE_EV_GOD_LIGHTBALL_2 - SFX_FLAG); + } else { + Math_ApproachZeroF(&this->unk_384, 1.0f, 0.2f); + } + if (this->unk_398 > 130) { + Math_ApproachF(&this->unk_3B0.y, (sZelda->actor.world.pos.y + 40.0f + 10.0f) - 20.0f, 0.1f, + this->unk_410.x); + } else { + Math_ApproachF(&this->unk_3B0.y, sZelda->actor.world.pos.y + 40.0f + 10.0f, 0.05f, + this->unk_410.x * 0.25f); + } + Math_ApproachF(&this->unk_410.x, 1.0f, 1.0f, 0.01f); + if (this->unk_398 == 10) { + sZelda->unk_3C8 = 12; + } + if (this->unk_398 == 110) { + sZelda->unk_3C8 = 13; + } + if (this->unk_398 == 140) { + Audio_PlayActorSound2(&sZelda->actor, NA_SE_EV_HUMAN_BOUND); + } + if (this->unk_398 < 160) { + break; + } + case 20: + globalCtx->nextEntranceIndex = 0x6B; + gSaveContext.nextCutsceneIndex = 0xFFF2; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + globalCtx->linkAgeOnLoad = 1; + break; + } + + if (this->unk_39E != 0) { + Gameplay_CameraSetAtEyeUp(globalCtx, this->unk_39E, &this->unk_3B0, &this->unk_3A4, &this->unk_3BC); + } + + switch (this->unk_1AC) { + case 0: + if (Animation_OnFrame(&this->skelAnime, this->unk_194)) { + Animation_MorphToLoop(&this->skelAnime, &object_ganon2_Anim_034278, 0.0f); + this->unk_1AC = 1; + } + break; + case 1: + if ((this->unk_39C < 7) && ((globalCtx->gameplayFrames % 32) == 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_BREATH); + } + break; + } +} + +void func_80902348(BossGanon2* this, GlobalContext* globalCtx) { + Player* player; + f32 temp_f2; + f32 temp_f12; + s16 i; + s16 j; + s16 phi_v0_2; + + if (this->unk_316 == 0) { + for (i = 0; i < ARRAY_COUNT(this->unk_864); i++) { + if (this->unk_444.elements[i].info.bumperFlags & 2) { + this->unk_444.elements[i].info.bumperFlags &= ~2; + } else if (this->unk_444.elements[i].info.toucherFlags & 2) { + this->unk_444.elements[i].info.toucherFlags &= ~2; + + if (this->unk_312 == 1) { + phi_v0_2 = 0x1800; + } else { + phi_v0_2 = 0; + } + + func_8002F6D4(globalCtx, &this->actor, 15.0f, this->actor.yawTowardsPlayer + phi_v0_2, 2.0f, 0); + sZelda->unk_3C8 = 8; + this->unk_316 = 10; + break; + } + } + } + + if (this->unk_324 > 0.0f) { + player = GET_PLAYER(globalCtx); + temp_f2 = -200.0f - player->actor.world.pos.x; + temp_f12 = -200.0f - player->actor.world.pos.z; + + if (sqrtf(SQ(temp_f2) + SQ(temp_f12)) > 784.0f) { + for (j = 0; j < ARRAY_COUNT(player->flameTimers); j++) { + player->flameTimers[j] = Rand_S16Offset(0, 200); + } + + player->isBurning = true; + func_8002F6D4(globalCtx, &this->actor, 10.0f, Math_Atan2S(temp_f12, temp_f2), 0.0f, 0x10); + sZelda->unk_3C8 = 8; + } + } +} + +void func_80902524(BossGanon2* this, GlobalContext* globalCtx) { + s8 temp_v0_4; + ColliderInfo* acHitInfo; + s16 i; + u8 phi_v1_2; + + osSyncPrintf("this->no_hit_time %d\n", this->unk_316); + if (this->unk_316 != 0 || ((this->unk_334 == 0) && (this->actionFunc == func_80900890))) { + for (i = 0; i < ARRAY_COUNT(this->unk_464); i++) { + this->unk_424.elements[i].info.bumperFlags &= ~2; + } + } + + osSyncPrintf("this->look_on %d\n", this->unk_313); + if (this->unk_313) { + if (this->actionFunc != func_808FFFE0) { + if (this->unk_424.elements[0].info.bumperFlags & 2) { + this->unk_424.elements[0].info.bumperFlags &= ~2; + acHitInfo = this->unk_424.elements[0].info.acHitInfo; + if ((acHitInfo->toucher.dmgFlags & 0x2000) && (this->actionFunc != func_80900890)) { + func_809000A0(this, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_HIT_THUNDER); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); + Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); + } else if ((this->actionFunc == func_80900890) && (acHitInfo->toucher.dmgFlags & 0x9000200)) { + this->unk_316 = 60; + this->unk_342 = 5; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); + Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); + this->actor.colChkInfo.health -= 2; + temp_v0_4 = this->actor.colChkInfo.health; + if (temp_v0_4 < 0x15 && this->unk_334 == 0) { + func_80900818(this, globalCtx); + } else { + if (temp_v0_4 <= 0) { + func_80901020(this, globalCtx); + } else { + func_80900210(this, globalCtx); + } + } + } else if (this->actionFunc != func_80900890) { + func_808FFF90(this, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_HOOKSHOT_REFLECT); + } + } + } + } else { + if (this->unk_424.elements[15].info.bumperFlags & 2) { + this->unk_424.elements[15].info.bumperFlags &= ~2; + acHitInfo = this->unk_424.elements[15].info.acHitInfo; + this->unk_316 = 60; + this->unk_344 = 0x32; + this->unk_342 = 5; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MGANON_DAMAGE); + Audio_StopSfxById(NA_SE_EN_MGANON_UNARI); + phi_v1_2 = 1; + if (acHitInfo->toucher.dmgFlags & 0x9000200) { + if (acHitInfo->toucher.dmgFlags & 0x8000000) { + phi_v1_2 = 4; + } else { + phi_v1_2 = 2; + } + } + this->actor.colChkInfo.health -= phi_v1_2; + temp_v0_4 = this->actor.colChkInfo.health; + if ((temp_v0_4 < 0x15) && (this->unk_334 == 0)) { + func_80900818(this, globalCtx); + } else if ((temp_v0_4 <= 0) && (phi_v1_2 >= 2)) { + func_80901020(this, globalCtx); + } else { + if (temp_v0_4 <= 0) { + this->actor.colChkInfo.health = 1; + } + func_80900210(this, globalCtx); + } + } + } +} + +void BossGanon2_Update(Actor* thisx, GlobalContext* globalCtx) { + BossGanon2* this = (BossGanon2*)thisx; + s32 pad; + s16 i; + f32 phi_f2; + u16 i2; + Vec3f sp58; + Vec3f sp4C; + f32 angle; + f32 sp44; + + if ((this->unk_337 == 0) || (this->unk_337 == 2)) { + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON_ANIME3, false); + } else { + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON2, false); + Math_ApproachZeroF(&this->unk_30C, 1.0f, 0.5f); + } + func_808FFC84(this); + this->unk_312 = 0; + this->unk_19C++; + Actor_SetScale(&this->actor, 0.01f); + this->actionFunc(this, globalCtx); + for (i = 0; i < ARRAY_COUNT(this->unk_1A2); i++) { + if (this->unk_1A2[i] != 0) { + this->unk_1A2[i]--; + } + } + if (this->unk_316 != 0) { + this->unk_316--; + } + if (this->unk_342 != 0) { + this->unk_342--; + } + if (this->unk_390 != 0) { + this->unk_390--; + } + if (this->unk_392 != 0) { + this->unk_392--; + } + Actor_MoveForward(&this->actor); + this->actor.shape.rot = this->actor.world.rot; + if (this->unk_335 != 0) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 60.0f, 60.0f, 100.0f, 5); + if (this->actor.bgCheckFlags & 1) { + if (this->actor.velocity.y < -5.0f) { + func_80033E88(&this->actor, globalCtx, 5, 20); + func_80078884(NA_SE_IT_BOMB_EXPLOSION); + } + this->actor.velocity.y = 0.0f; + } + } + if (((this->unk_19C & 0x1F) == 0) && (Rand_ZeroOne() < 0.3f)) { + this->unk_318 = 4; + } + this->unk_310 = D_80907074[this->unk_318]; + if (this->unk_318 != 0) { + this->unk_318--; + } + this->unk_1B0 = (Math_SinS(this->unk_19C * 0x2AAA) * 64.0f) + 191.0f; + if (this->unk_344 != 0) { + this->unk_344--; + Math_ApproachF(&this->unk_360.x, 5000.0f, 0.5f, 3000.0f); + Math_ApproachF(&this->unk_370.x, 5500.0f, 0.5f, 3000.0f); + Math_ApproachF(&this->unk_360.z, 8000.0f, 0.1f, 4000.0f); + Math_ApproachF(&this->unk_370.z, 8000.0f, 0.1f, 4000.0f); + Math_ApproachS(&this->unk_346, 0xFA0, 0xA, 0x7D0); + } else { + this->unk_360.y = 14000.0f; + Math_ApproachF(&this->unk_360.x, 2000.0f, 0.1f, 100.0f); + this->unk_370.y = 12000.0f; + Math_ApproachF(&this->unk_370.x, 1500.0f, 0.1f, 100.0f); + if ((this->actionFunc == func_808FFEBC) || (this->actionFunc == func_808FFFE0) || + (this->actionFunc == func_80900104)) { + Math_ApproachF(&this->unk_360.z, 1000.0f, 0.1f, 100.0f); + Math_ApproachF(&this->unk_370.z, 1000.0f, 0.1f, 100.0f); + Math_ApproachS(&this->unk_346, -0xFA0, 0xA, 0x64); + } else { + Math_ApproachF(&this->unk_360.z, 5000.0f, 0.1f, 200.0f); + Math_ApproachF(&this->unk_370.z, 5000.0f, 0.1f, 200.0f); + Math_ApproachS(&this->unk_346, 0, 0xA, 0x64); + } + } + if (this->unk_39C != 75) { + this->unk_35C += this->unk_360.x; + this->unk_36C += this->unk_370.x; + } + if (this->unk_337 == 2) { + this->unk_370.z = 0.0f; + this->unk_360.z = 0.0f; + } + + for (i = 0; i < ARRAY_COUNT(this->unk_348); i++) { + if (i == 0) { + phi_f2 = 0.2f; + } else if (i == 1) { + phi_f2 = 0.5f; + } else { + phi_f2 = 1.0f; + } + + this->unk_348[i] = Math_SinS(((s16)this->unk_35C + (i * (s16)this->unk_360.y))) * phi_f2 * this->unk_360.z; + this->unk_352[i] = Math_SinS(((s16)this->unk_36C + (i * (s16)this->unk_370.y))) * phi_f2 * this->unk_370.z; + } + + func_808FF898(this, globalCtx); + func_80902348(this, globalCtx); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->unk_424.base); + if (this->actionFunc != func_8090120C) { + func_80902524(this, globalCtx); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->unk_424.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->unk_444.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->unk_444.base); + if (this->unk_39E == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->unk_444.base); + } + } + if ((this->unk_332 == 0) && (this->unk_336 != 0)) { + if (this->unk_336 == 2) { + this->unk_332 = (s16)Rand_ZeroFloat(30.0f) + 8; + } else { + this->unk_332 = (s16)Rand_ZeroFloat(60.0f) + 0xA; + } + this->unk_339 = 0; + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_BD = (s8)Rand_ZeroFloat(1.9f) + 1; + globalCtx->envCtx.unk_D8 = 1.0f; + D_8090EB20.y = 0.0f; + D_8090EB20.x = D_8090EB20.y; + D_8090EB20.z = D_8090EB20.x; + if (Rand_ZeroOne() < 0.5f) { + D_8090EB20.z = Rand_ZeroFloat(1000.0f); + } + func_80078914(&D_8090EB20, NA_SE_EV_LIGHTNING); + this->unk_328 = 0xFF; + this->unk_330 = 5; + this->unk_32C = 0.0f; + this->unk_340 = (s16)Rand_ZeroFloat(10000.0f); + } else if (this->unk_332 != 0) { + this->unk_332--; + } + if ((globalCtx->envCtx.unk_D8 > 0.0f) && (this->unk_336 != 0)) { + globalCtx->envCtx.customSkyboxFilter = 1; + globalCtx->envCtx.skyboxFilterColor[0] = 255; + globalCtx->envCtx.skyboxFilterColor[1] = 255; + globalCtx->envCtx.skyboxFilterColor[2] = 255; + globalCtx->envCtx.skyboxFilterColor[3] = (s16)(globalCtx->envCtx.unk_D8 * 200.0f); + } else { + globalCtx->envCtx.customSkyboxFilter = 0; + } + globalCtx->envCtx.unk_BF = 0; + globalCtx->envCtx.unk_DC = 2; + + switch (this->unk_339) { + case 0: + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f); + break; + case 3: + globalCtx->envCtx.unk_BE = 3; + globalCtx->envCtx.unk_BD = 4; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.0125f); + break; + case 4: + globalCtx->envCtx.unk_BE = 5; + globalCtx->envCtx.unk_BD = 6; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.0125f); + break; + case 5: + globalCtx->envCtx.unk_BE = 6; + globalCtx->envCtx.unk_BD = 7; + Math_ApproachF(&this->unk_33C, 0.69f, 1.0f, 0.05f); + globalCtx->envCtx.unk_D8 = + (Math_SinS(globalCtx->gameplayFrames * 0x5000) * 0.15f) + (0.15f + this->unk_33C); + break; + case 55: + globalCtx->envCtx.unk_BE = 2; + globalCtx->envCtx.unk_BD = 0; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.05f); + break; + case 6: + globalCtx->envCtx.unk_BE = 2; + globalCtx->envCtx.unk_BD = 8; + Math_ApproachF(&this->unk_33C, 0.69f, 1.0f, 0.05f); + globalCtx->envCtx.unk_D8 = + (Math_SinS(globalCtx->gameplayFrames * 0x7000) * 0.15f) + (0.15f + this->unk_33C); + break; + case 7: + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_BD = 8; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.02f); + break; + case 20: + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_BD = 9; + break; + case 21: + globalCtx->envCtx.unk_BE = 0xA; + globalCtx->envCtx.unk_BD = 9; + break; + case 22: + globalCtx->envCtx.unk_BE = 0xA; + globalCtx->envCtx.unk_BD = 0xB; + break; + case 23: + globalCtx->envCtx.unk_BE = 9; + globalCtx->envCtx.unk_BD = 0xB; + break; + case 24: + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_BD = 0xC; + break; + case -1: + break; + } + + if (this->unk_339 >= 0) { + this->unk_339 = 0; + } + if (D_80906D78 != 0) { + D_80906D78 = 0; + + for (i2 = 0; i2 < ARRAY_COUNT(sParticles); i2++) { + angle = Rand_ZeroFloat(2 * M_PI); + sp44 = Rand_ZeroFloat(40.0f) + 10.0f; + sp58 = this->actor.world.pos; + sp58.y = 1200.0f; + sp4C.x = cosf(angle) * sp44; + sp4C.z = sinf(angle) * sp44; + sp4C.y = Rand_ZeroFloat(15.0f) + 15.0f; + sp58.x += sp4C.x * 10.0f * 0.1f; + sp58.z += sp4C.z * 10.0f * 0.1f; + func_808FD27C(globalCtx, &sp58, &sp4C, Rand_ZeroFloat(0.3f) + 0.2f); + } + } + this->unk_388 += 0.15f; + func_80905DA8(this, globalCtx); +} + +void func_809034E4(Vec3f* arg0, Vec3f* arg1) { + Vtx* vtx; + Vec3f sp2D0; + s16 temp_s1; + s16 temp_a1; + s16 sp2CA; + s16 sp2C8; + s16 i; + u8 phi_s2; + u8 temp_s4; + u8 temp_s4_2; + f32 temp_f12; + Vec3f temp_f20; + Vec3f temp_f2; + Vec3f temp_f22; + f32 sp294; + f32 phi_f30; + f32 temp_f28; + f32 temp_f26; + s32 pad[3]; + Vec3f sp18C[20]; + Vec3f sp9C[20]; + + for (i = 0; i < 20; i++) { + sp18C[i] = *arg0; + sp9C[i] = *arg1; + } + + temp_s4 = 0; + + D_809105D8[3] = D_809105D8[2]; + D_809105D8[2] = D_809105D8[1]; + D_809105D8[1] = D_809105D8[0]; + D_809105D8[0] = *arg0; + + sp2D0 = D_809105D8[0]; + + temp_f20.x = D_809105D8[1].x - sp2D0.x; + temp_f20.y = D_809105D8[1].y - sp2D0.y; + temp_f20.z = D_809105D8[1].z - sp2D0.z; + + sp2CA = Math_Atan2S(temp_f20.z, temp_f20.x); + sp2C8 = Math_Atan2S(sqrtf(SQXZ(temp_f20)), temp_f20.y); + + temp_f2.x = D_809105D8[2].x - D_809105D8[1].x; + temp_f2.y = D_809105D8[2].y - D_809105D8[1].y; + temp_f2.z = D_809105D8[2].z - D_809105D8[1].z; + + temp_f22.x = D_809105D8[3].x - D_809105D8[2].x; + temp_f22.y = D_809105D8[3].y - D_809105D8[2].y; + temp_f22.z = D_809105D8[3].z - D_809105D8[2].z; + + temp_f12 = sqrtf(SQXYZ(temp_f20)) + sqrtf(SQXYZ(temp_f2)) + sqrtf(SQXYZ(temp_f22)); + if (temp_f12 <= 1.0f) { + temp_f12 = 1.0f; + } + + temp_f28 = temp_f12 * 0.083f; + phi_f30 = sqrtf(SQXYZ(temp_f20)) / 2.0f; + sp294 = sqrtf(SQXYZ(temp_f2)) / 2.0f; + + phi_s2 = 1; + + while (true) { + temp_f20.x = D_809105D8[phi_s2].x - sp2D0.x; + temp_f20.y = D_809105D8[phi_s2].y - sp2D0.y; + temp_f20.z = D_809105D8[phi_s2].z - sp2D0.z; + + temp_s1 = Math_Atan2S(temp_f20.z, temp_f20.x); + temp_a1 = Math_Atan2S(sqrtf(SQXZ(temp_f20)), temp_f20.y); + + Math_ApproachS(&sp2C8, temp_a1, 1, 0x1000); + Math_ApproachS(&sp2CA, temp_s1, 1, 0x1000); + + temp_f26 = temp_f28 * Math_CosS(sp2C8); + + sp18C[temp_s4] = sp2D0; + + sp2D0.x += temp_f26 * Math_SinS(sp2CA); + sp2D0.y += temp_f28 * Math_SinS(sp2C8); + sp2D0.z += temp_f26 * Math_CosS(sp2CA); + + temp_f20.x = D_809105D8[phi_s2].x - sp2D0.x; + temp_f20.y = D_809105D8[phi_s2].y - sp2D0.y; + temp_f20.z = D_809105D8[phi_s2].z - sp2D0.z; + + if (phi_s2 < 3) { + if (sqrtf(SQXYZ(temp_f20)) <= phi_f30) { + phi_f30 = sp294; + phi_s2++; + } + } else { + if (sqrtf(SQXYZ(temp_f20)) <= (temp_f28 + 1.0f)) { + phi_s2++; + } + } + + temp_s4++; + + if ((temp_s4 >= 20) || (phi_s2 >= 4)) { + break; + } + } + + temp_s4_2 = 0; + + D_80910608[3] = D_80910608[2]; + D_80910608[2] = D_80910608[1]; + D_80910608[1] = D_80910608[0]; + D_80910608[0] = *arg1; + + sp2D0 = D_80910608[0]; + + temp_f20.x = D_80910608[1].x - sp2D0.x; + temp_f20.y = D_80910608[1].y - sp2D0.y; + temp_f20.z = D_80910608[1].z - sp2D0.z; + + sp2CA = Math_Atan2S(temp_f20.z, temp_f20.x); + sp2C8 = Math_Atan2S(sqrtf(SQXZ(temp_f20)), temp_f20.y); + + temp_f2.x = D_80910608[2].x - D_80910608[1].x; + temp_f2.y = D_80910608[2].y - D_80910608[1].y; + temp_f2.z = D_80910608[2].z - D_80910608[1].z; + + temp_f22.x = D_80910608[3].x - D_80910608[2].x; + temp_f22.y = D_80910608[3].y - D_80910608[2].y; + temp_f22.z = D_80910608[3].z - D_80910608[2].z; + + temp_f12 = sqrtf(SQXYZ(temp_f20)) + sqrtf(SQXYZ(temp_f2)) + sqrtf(SQXYZ(temp_f22)); + if (temp_f12 <= 1.0f) { + temp_f12 = 1.0f; + } + + temp_f28 = temp_f12 * 0.083f; + phi_f30 = sqrtf(SQXYZ(temp_f20)) / 2.0f; + sp294 = sqrtf(SQXYZ(temp_f2)) / 2.0f; + + phi_s2 = 1; + + while (true) { + temp_f20.x = D_80910608[phi_s2].x - sp2D0.x; + temp_f20.y = D_80910608[phi_s2].y - sp2D0.y; + temp_f20.z = D_80910608[phi_s2].z - sp2D0.z; + + temp_s1 = Math_Atan2S(temp_f20.z, temp_f20.x); + temp_a1 = Math_Atan2S(sqrtf(SQXZ(temp_f20)), temp_f20.y); + + Math_ApproachS(&sp2C8, temp_a1, 1, 0x1000); + Math_ApproachS(&sp2CA, temp_s1, 1, 0x1000); + + temp_f26 = temp_f28 * Math_CosS(sp2C8); + + sp9C[temp_s4_2] = sp2D0; + + sp2D0.x += temp_f26 * Math_SinS(sp2CA); + sp2D0.y += temp_f28 * Math_SinS(sp2C8); + sp2D0.z += temp_f26 * Math_CosS(sp2CA); + + temp_f20.x = D_80910608[phi_s2].x - sp2D0.x; + temp_f20.y = D_80910608[phi_s2].y - sp2D0.y; + temp_f20.z = D_80910608[phi_s2].z - sp2D0.z; + + if (phi_s2 < 3) { + if (sqrtf(SQXYZ(temp_f20)) <= phi_f30) { + phi_f30 = sp294; + phi_s2++; + } + } else { + if (sqrtf(SQXYZ(temp_f20)) <= (temp_f28 + 1.0f)) { + phi_s2++; + } + } + + temp_s4_2++; + + if ((temp_s4_2 >= 20) || (phi_s2 >= 4)) { + break; + } + } + + vtx = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_Vtx_00BA20)); + for (i = 0; i < 11; i++) { + if ((temp_s4 - i) > 0) { + vtx[D_80907084[i]].n.ob[0] = sp18C[temp_s4 - i - 1].x; + vtx[D_80907084[i]].n.ob[1] = sp18C[temp_s4 - i - 1].y; + vtx[D_80907084[i]].n.ob[2] = sp18C[temp_s4 - i - 1].z; + } + if ((temp_s4_2 - i) > 0) { + vtx[D_80907090[i]].n.ob[0] = sp9C[temp_s4_2 - i - 1].x; + vtx[D_80907090[i]].n.ob[1] = sp9C[temp_s4_2 - i - 1].y; + vtx[D_80907090[i]].n.ob[2] = sp9C[temp_s4_2 - i - 1].z; + } + } +} + +void func_80903F38(BossGanon2* this, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5083); + + if (this->unk_312 != 0) { + func_809034E4(&this->unk_200, &this->unk_20C); + D_80907080 = 0xFF; + } + + if (D_80910638 >= 4) { + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, globalCtx->gameplayFrames * 18, 0, 32, 32)); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, D_80907080); + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_NEW); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5117), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, ovl_Boss_Ganon2_DL_00BB80); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5122); +} + +void func_80904108(BossGanon2* this, GlobalContext* globalCtx) { + s32 pad; + + if (this->unk_324 > 0.0f) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5131); + + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s32)globalCtx->gameplayFrames, 0, 32, 64, 1, + -globalCtx->gameplayFrames * 2, -globalCtx->gameplayFrames * 8, 32, 32)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 200, 0, (s8)this->unk_324); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 128); + Matrix_Translate(-200.0f, 1086.0f, -200.0f, MTXMODE_NEW); + Matrix_Scale(0.098000005f, 0.1f, 0.098000005f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5183), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00E1C0)); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5186); + } +} + +void func_80904340(BossGanon2* this, GlobalContext* globalCtx) { + s16 i; + f32 rand; + f32 angle; + f32 sin; + f32 cos; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5196); + Matrix_Push(); + + if ((this->unk_330 != 0) || (this->unk_328 != 0)) { + if (this->unk_330 != 0) { + this->unk_330--; + } else { + this->unk_328 -= 70; + + if (this->unk_328 < 0) { + this->unk_328 = 0; + } + } + + Math_ApproachF(&this->unk_32C, 0.13f, 1.0f, 0.065f); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, this->unk_328); + BossGanon2_InitRand(this->unk_340 + 1, 0x71AC - this->unk_340, 0x263A); + rand = BossGanon2_RandZeroOne(); + if (1) {} + + for (i = 0; i < 5; i++) { + angle = (i * (2 * M_PI / 5)) + (rand * M_PI); + sin = 5000.0f * sinf(angle); + cos = 5000.0f * cosf(angle); + Matrix_Translate(-200.0f + sin, 4786.0f, -200.0f + cos, MTXMODE_NEW); + Matrix_Scale(this->unk_32C, this->unk_32C, this->unk_32C, MTXMODE_APPLY); + Matrix_RotateY(angle, MTXMODE_APPLY); + Matrix_RotateZ((BossGanon2_RandZeroOne() - 0.5f) * 100.0f * 0.01f, MTXMODE_APPLY); + + if (BossGanon2_RandZeroOne() < 0.5f) { + Matrix_RotateY(M_PI, MTXMODE_APPLY); + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5250), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00D798)); + } + } + + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5255); +} + +void func_8090464C(BossGanon2* this, GlobalContext* globalCtx) { + s32 pad; + + if (this->unk_1B4 > 0.0f) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5264); + + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, (s16)this->unk_1B4); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 200, 0, 128); + Matrix_Translate(this->unk_1B8.x, this->unk_1B8.y, this->unk_1B8.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ(-0.2f, MTXMODE_APPLY); + Matrix_Scale(0.6f, 0.6f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5290), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00CCD8)); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5293); + } +} + +s32 BossGanon2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + s32 pad; + BossGanon2* this = (BossGanon2*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5355); + + if (limbIndex == 15) { + rot->y += this->unk_31A; + rot->z += this->unk_31C; + } + + if (limbIndex >= 42) { + rot->x += this->unk_2F4[limbIndex] + this->unk_346; + rot->y += this->unk_2FE[limbIndex]; + + if (this->unk_342 & 1) { + gDPSetEnvColor(POLY_OPA_DISP++, 255, 0, 0, 255); + } else { + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->unk_1B0, (s16)this->unk_1B0, (s16)(*this).unk_1B0, 255); + } + } + + if ((limbIndex == 7) || (limbIndex == 13) || (limbIndex == 33) || (limbIndex == 34)) { + *dList = NULL; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5431); + return 0; +} + +void BossGanon2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + s8 pad; + s8 temp_v0; + BossGanon2* this = (BossGanon2*)thisx; + Vec3f sp4C; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5459); + + D_80907120.z = 17000.0f; + D_8090712C.z = 3000.0f; + + if (D_809070CC[limbIndex] >= 0) { + Matrix_MultVec3f(&D_80906D60, &this->unk_234[D_809070CC[limbIndex]]); + } + + if (limbIndex == 15) { + Matrix_MultVec3f(&D_80906D60, &this->unk_1B8); + } else if (limbIndex == 3) { + Matrix_MultVec3f(&D_80907108, &this->unk_1F4); + } else if (limbIndex == 9) { + Matrix_MultVec3f(&D_80907114, &this->unk_1E8); + } else if (limbIndex == 38) { + Matrix_MultVec3f(&D_80906D60, &this->unk_1DC); + } else if (limbIndex == 41) { + Matrix_MultVec3f(&D_80906D60, &this->unk_1D0); + } else if (limbIndex == 45) { + Matrix_MultVec3f(&D_80907138, &this->unk_1C4); + } + + temp_v0 = D_8090709C[limbIndex]; + if (temp_v0 >= 0) { + Matrix_MultVec3f(&D_80906D60, &sp4C); + func_808FD080(temp_v0, &this->unk_424, &sp4C); + } + + if ((limbIndex == 7) || (limbIndex == 13)) { + Matrix_Push(); + Matrix_Scale(this->unk_224, this->unk_224, this->unk_224, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5522), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + } else if ((limbIndex == 33) || (limbIndex == 34)) { + Matrix_Push(); + Matrix_Scale(this->unk_228, this->unk_228, this->unk_228, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5533), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + } + + if (*dList != NULL) { + if ((limbIndex == 7) && (this->unk_312 == 1)) { + Matrix_MultVec3f(&D_809070FC, &this->unk_218); + func_808FD080(0, &this->unk_444, &this->unk_218); + Matrix_MultVec3f(&D_80907120, &this->unk_200); + Matrix_MultVec3f(&D_8090712C, &this->unk_20C); + } else if ((limbIndex == 13) && (this->unk_312 == 2)) { + Matrix_MultVec3f(&D_809070FC, &this->unk_218); + func_808FD080(1, &this->unk_444, &this->unk_218); + Matrix_MultVec3f(&D_80907120, &this->unk_200); + Matrix_MultVec3f(&D_8090712C, &this->unk_20C); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5566); +} + +void func_80904D88(BossGanon2* this, GlobalContext* globalCtx) { + s32 pad; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5575); + + if (this->unk_30C > 0.0f) { + func_80093D84(globalCtx->state.gfxCtx); + if (this->unk_380 > 0.0f) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 200, 0, 0); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 255, 255, 0); + } + gSPDisplayList(POLY_XLU_DISP++, ovl_Boss_Ganon2_DL_00B308); + + for (i = 0; i < 15; i++) { + Matrix_Translate(this->unk_234[i].x, this->unk_234[i].y, this->unk_234[i].z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_30C, this->unk_30C, this->unk_30C, MTXMODE_APPLY); + Matrix_RotateZ(Rand_CenteredFloat(M_PI), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5618), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, ovl_Boss_Ganon2_DL_00B378); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5622); +} + +void func_80904FC8(BossGanon2* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5632); + + if (this->unk_384 > 0.0f) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 200); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 200, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, ovl_Boss_Ganon2_DL_00B308); + Matrix_Translate(sZelda->actor.world.pos.x, sZelda->actor.world.pos.y + 80.0f, sZelda->actor.world.pos.z, + MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(this->unk_384, this->unk_384, this->unk_384, MTXMODE_APPLY); + Matrix_RotateZ(this->unk_388, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5661), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00B378)); + Matrix_RotateZ(this->unk_388 * -2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5664), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00B378)); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5667); +} + +void func_8090523C(BossGanon2* this, GlobalContext* globalCtx) { + Player* player; + f32 phi_f20; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5675); + + if (this->unk_38C > 0.0f) { + s8 i; + + player = GET_PLAYER(globalCtx); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)this->unk_38C); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, 0); + gSPDisplayList(POLY_XLU_DISP++, ovl_Boss_Ganon2_DL_00B308); + + for (i = 0; i < 11; i++) { + Matrix_Mult(&player->mf_9E0, MTXMODE_NEW); + Matrix_Translate((i * 250.0f) + 900.0f, 350.0f, 0.0f, MTXMODE_APPLY); + + if (i < 7) { + phi_f20 = 1.0f; + } else { + phi_f20 = 1.0f - ((i - 7) * 0.2333333f); // 7 / 30 + } + + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(200.0f * phi_f20, 200.0f * phi_f20, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ(Rand_ZeroFloat(2.0f * M_PI), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5721), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00B378)); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5725); +} + +void BossGanon2_PostLimbDraw2(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + s8 temp_v1 = D_80907144[limbIndex]; + BossGanon2* this = (BossGanon2*)thisx; + + if (temp_v1 >= 0) { + Matrix_MultVec3f(&D_80906D60, &this->unk_234[temp_v1]); + } + if (limbIndex == 11) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5749); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5752), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_ganon_DL_00BE90)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5754); + } else if (limbIndex == 10) { + Matrix_MultVec3f(&D_80907164, &this->unk_1B8); + } +} + +void func_80905674(BossGanon2* this, GlobalContext* globalCtx) { + s32 pad; + + if (this->unk_380 > 0.0f) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5772); + + Matrix_Push(); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->unk_19C * -8, 0, 32, 64, 1, this->unk_19C * -4, + this->unk_19C * -8, 32, 32)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, (s16)this->unk_37C); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 200, 0, 128); + Matrix_Translate(sZelda->actor.world.pos.x + 100.0f, sZelda->actor.world.pos.y + 35.0f + 7.0f, + sZelda->actor.world.pos.z - 100.0f, MTXMODE_NEW); + Matrix_RotateY(-M_PI / 4.0f, MTXMODE_APPLY); + Matrix_Scale(0.040000003f, 0.040000003f, this->unk_380, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5814), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(ovl_Boss_Ganon2_DL_00EC40)); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5817); + } +} + +void BossGanon2_Draw(Actor* thisx, GlobalContext* globalCtx) { + void* shadowTexture = Graph_Alloc(globalCtx->state.gfxCtx, 4096); + BossGanon2* this = (BossGanon2*)thisx; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5840); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + switch (this->unk_337) { + case 0: + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON, true); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(object_ganon_Tex_00A8E0)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(object_ganon_Tex_00A8E0)); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, NULL, BossGanon2_PostLimbDraw2, this); + break; + case 1: + case 2: + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON2, true); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->unk_310])); + func_808FD080(0, &this->unk_444, &D_8090717C); + func_808FD080(1, &this->unk_444, &D_8090717C); + this->unk_218 = D_8090717C; + if (this->unk_342 & 1) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 0xFF, 0, 0, 0xFF, 0x384, 0x44B); + } + Matrix_Translate(0.0f, -4000.0f, 4000.0f, MTXMODE_APPLY); + Matrix_RotateX(this->unk_394, MTXMODE_APPLY); + Matrix_Translate(0.0f, 4000.0f, -4000.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5910), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossGanon2_OverrideLimbDraw, BossGanon2_PostLimbDraw, + this); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + BossGanon2_GenShadowTexture(shadowTexture, this, globalCtx); + BossGanon2_DrawShadowTexture(shadowTexture, this, globalCtx); + break; + } + + BossGanon2_SetObjectSegment(this, globalCtx, OBJECT_GANON2, true); + func_80904340(this, globalCtx); + func_80904108(this, globalCtx); + func_80904D88(this, globalCtx); + func_8090464C(this, globalCtx); + func_80905674(this, globalCtx); + func_80904FC8(this, globalCtx); + func_8090523C(this, globalCtx); + + if ((this->unk_312 != 0) || (D_80907080 != 0)) { + func_80903F38(this, globalCtx); + if (this->unk_312 == 0) { + s32 pad; + + D_80907080 -= 40; + if (D_80907080 <= 0) { + D_80907080 = 0; + } + } + + D_80910638++; + } else { + for (i = 0; i < 3; i++) { + D_809105D8[i] = this->unk_200; + D_80910608[i] = this->unk_20C; + } + + D_80910638 = 0; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 5983); + + func_809060E8(globalCtx); +} + +void func_80905DA8(BossGanon2* this, GlobalContext* globalCtx) { + s32 pad[5]; + Player* player = GET_PLAYER(globalCtx); + BossGanon2Effect* effect = globalCtx->specialEffects; + Vec3f sp78; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sParticles); i++, effect++) { + if (effect->type != 0) { + effect->position.x += effect->velocity.x; + effect->position.y += effect->velocity.y; + effect->position.z += effect->velocity.z; + effect->unk_01++; + effect->velocity.x += effect->accel.x; + effect->velocity.y += effect->accel.y; + effect->velocity.z += effect->accel.z; + if (effect->type == 1) { + if (effect->unk_2E == 0) { + effect->unk_38.z += 1.0f; + effect->unk_38.y = (2.0f * M_PI) / 5.0f; + } else { + effect->unk_38.z = M_PI / 2.0f; + effect->unk_38.y = 0.0f; + if (effect->position.y <= 1098.0f) { + effect->position.y = 1098.0f; + if (effect->velocity.y < -10.0f) { + sp78 = effect->position; + sp78.y = 1086.0f; + func_80078884(NA_SE_IT_SHIELD_REFLECT_SW); + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, &sp78); + } + effect->velocity.y = 0.0f; + } + if ((SQ(player->actor.world.pos.x - effect->position.x) + + SQ(player->actor.world.pos.z - effect->position.z)) < SQ(25.0f)) { + effect->type = 0; + this->unk_39C = 10; + } + } + } else if (effect->type == 2) { + effect->unk_38.x += 0.1f; + effect->unk_38.y += 0.4f; + if ((sqrtf(SQ(-200.0f - effect->position.x) + SQ(-200.0f - effect->position.z)) < 1000.0f)) { + if (effect->position.y < 1186.0f) { + if (effect->unk_2E == 0) { + effect->unk_2E++; + effect->position.y = 1186.0f; + effect->velocity.x *= 0.75f; + effect->velocity.z *= 0.75f; + effect->velocity.y *= -0.2f; + } else { + effect->type = 0; + } + } + } else if ((effect->position.y < 0.0f)) { + effect->type = 0; + } + } + } + } +} + +void func_809060E8(GlobalContext* globalCtx) { + s16 alpha; + u8 usingObjectGEff = false; + BossGanon2Effect* effect; + s16 i; + BossGanon2Effect* effects; + + effects = effect = globalCtx->specialEffects; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 6086); + + func_80093D18(globalCtx->state.gfxCtx); + + for (i = 0; i < 1; i++) { + if (effect->type == 1) { + Vec3f spA0; + f32 temp_f0; + f32 angle; + + func_80093D84(globalCtx->state.gfxCtx); + spA0.x = globalCtx->envCtx.dirLight1.params.dir.x; + spA0.y = globalCtx->envCtx.dirLight1.params.dir.y; + spA0.z = globalCtx->envCtx.dirLight1.params.dir.z; + func_8002EABC(&effect->position, &globalCtx->view.eye, &spA0, globalCtx->state.gfxCtx); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_Scale(0.03f, 0.03f, 0.03f, MTXMODE_APPLY); + Matrix_RotateY(effect->unk_38.z, MTXMODE_APPLY); + Matrix_RotateX(effect->unk_38.y, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 6116), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, 0 - (globalCtx->gameplayFrames & 0x7F), 32, 32)); + gSPDisplayList(POLY_OPA_DISP++, ovl_Boss_Ganon2_DL_0103A8); + if ((globalCtx->envCtx.unk_BD == 1) || (globalCtx->envCtx.unk_BD == 2)) { + alpha = (s16)(globalCtx->envCtx.unk_D8 * 150.0f) + 50; + angle = M_PI / 5.0f; + } else { + alpha = 100; + angle = M_PI / 2.0f; + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, alpha); + temp_f0 = effect->position.y - 1098.0f; + Matrix_Translate(effect->position.x + temp_f0, 1086.0f, (effect->position.z - 1.0f) + temp_f0, MTXMODE_NEW); + Matrix_RotateY(angle, MTXMODE_APPLY); + Matrix_Scale(1.0f, 0.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 6155), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, ovl_Boss_Ganon2_DL_00F188); + } + } + + effect = effects; + + for (i = 0; i < ARRAY_COUNT(sParticles); i++, effect++) { + if (effect->type == 2) { + if (!usingObjectGEff) { + BossGanon2_SetObjectSegment(NULL, globalCtx, OBJECT_GEFF, true); + usingObjectGEff++; + } + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_RotateY(effect->unk_38.z, MTXMODE_APPLY); + Matrix_RotateX(effect->unk_38.y, MTXMODE_APPLY); + Matrix_RotateZ(effect->unk_38.x, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 6179), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGanonRubbleDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 6185); +} + +void func_80906538(BossGanon2* this, u8* shadowTexture, f32 arg2) { + s16 temp_t0; + s16 temp_v0; + s16 temp_a3; + s16 phi_v1; + s16 phi_a1; + s16 i; + f32 lerpx; + s16 j; + f32 lerpy; + f32 lerpz; + Vec3f sp70; + Vec3f sp64; + + for (i = 0; i < 15; i++) { + if ((arg2 == 0.0f) || ((j = D_809071CC[i]) >= 0)) { + if (arg2 > 0.0f) { + lerpx = this->unk_234[i].x + (this->unk_234[j].x - this->unk_234[i].x) * arg2; + lerpy = this->unk_234[i].y + (this->unk_234[j].y - this->unk_234[i].y) * arg2; + lerpz = this->unk_234[i].z + (this->unk_234[j].z - this->unk_234[i].z) * arg2; + + sp70.x = lerpx - this->actor.world.pos.x; + sp70.y = lerpy - this->actor.world.pos.y + 76.0f + 30.0f + 30.0f + 100.0f; + sp70.z = lerpz - this->actor.world.pos.z; + } else { + sp70.x = this->unk_234[i].x - this->actor.world.pos.x; + sp70.y = this->unk_234[i].y - this->actor.world.pos.y + 76.0f + 30.0f + 30.0f + 100.0f; + sp70.z = this->unk_234[i].z - this->actor.world.pos.z; + } + + Matrix_MultVec3f(&sp70, &sp64); + sp64.x *= 0.2f; + sp64.y *= 0.2f; + temp_a3 = sp64.x + 32.0f; + temp_t0 = (s16)sp64.y * 64; + + if (D_809071EC[i] == 2) { + for (j = 0, phi_a1 = -0x180; j < 12; j++, phi_a1 += 0x40) { + for (phi_v1 = -D_809071B4[j]; phi_v1 < D_809071B4[j]; phi_v1++) { + temp_v0 = temp_a3 + phi_v1 + temp_t0 + phi_a1; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v0] = 0xFF; + } + } + } + } else if (D_809071EC[i] == 1) { + for (j = 0, phi_a1 = -0x100; j < 8; j++, phi_a1 += 0x40) { + for (phi_v1 = -D_809071A4[j]; phi_v1 < D_809071A4[j]; phi_v1++) { + temp_v0 = temp_a3 + phi_v1 + temp_t0 + phi_a1; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v0] = 0xFF; + } + } + } + } else if (D_809071EC[i] == 0) { + for (j = 0, phi_a1 = -0xC0; j < 7; j++, phi_a1 += 0x40) { + for (phi_v1 = -D_80907194[j]; phi_v1 < D_80907194[j] - 1; phi_v1++) { + temp_v0 = temp_a3 + phi_v1 + temp_t0 + phi_a1; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v0] = 0xFF; + } + } + } + } else { + for (j = 0, phi_a1 = -0x80; j < 6; j++, phi_a1 += 0x40) { + for (phi_v1 = -D_80907188[j]; phi_v1 < D_80907188[j] - 1; phi_v1++) { + temp_v0 = temp_a3 + phi_v1 + temp_t0 + phi_a1; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v0] = 0xFF; + } + } + } + } + } + } +} + +void BossGanon2_GenShadowTexture(void* shadowTexture, BossGanon2* this, GlobalContext* globalCtx) { + s16 i; + u32* p = shadowTexture; + + for (i = 0; i < 1024; i++, p++) { + *p = 0; + } + + Matrix_RotateX(1.0f, MTXMODE_NEW); + + for (i = 0; i < 6; i++) { + func_80906538(this, shadowTexture, i / 5.0f); + } +} + +void BossGanon2_DrawShadowTexture(void* shadowTexture, BossGanon2* this, GlobalContext* globalCtx) { + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 alpha; + + OPEN_DISPS(gfxCtx, "../z_boss_ganon2.c", 6430); + + func_80093D18(globalCtx->state.gfxCtx); + + if ((globalCtx->envCtx.unk_BD == 1) || (globalCtx->envCtx.unk_BD == 2)) { + alpha = (s16)(globalCtx->envCtx.unk_D8 * 180.0f) + 30; + } else { + alpha = 120; + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, alpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z - 20.0f, MTXMODE_NEW); + Matrix_Scale(1.65f, 1.0f, 1.65f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_ganon2.c", 6457), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, ovl_Boss_Ganon2_DL_00B3D0); + gDPLoadTextureBlock(POLY_OPA_DISP++, shadowTexture, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); + gSPDisplayList(POLY_OPA_DISP++, ovl_Boss_Ganon2_DL_00B3F0); + + CLOSE_DISPS(gfxCtx, "../z_boss_ganon2.c", 6479); +} + +void BossGanon2_Reset(void) { + D_8090EB20.x = 0; + D_8090EB20.y = 0; + D_8090EB20.z = 0; + D_80910638 = 0; + sZelda = NULL; + D_8090EB30 = NULL; + sSeed1 = 0; + sSeed2 = 0; + sSeed3 = 0; + memset(D_809105D8, 0, sizeof(D_809105D8)); + memset(D_80910608, 0, sizeof(D_80910608)); + memset(sParticles, 0, sizeof(sParticles)); +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.h b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.h new file mode 100644 index 000000000..3d2625231 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.h @@ -0,0 +1,97 @@ +#ifndef Z_BOSS_GANON2_H +#define Z_BOSS_GANON2_H + +#include "ultra64.h" +#include "global.h" + +struct BossGanon2; + +typedef void (*BossGanon2ActionFunc)(struct BossGanon2*, GlobalContext*); + +typedef struct BossGanon2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ BossGanon2ActionFunc actionFunc; + /* 0x0194 */ f32 unk_194; + /* 0x0198 */ f32 unk_198; + /* 0x019C */ s16 unk_19C; + /* 0x019E */ char unk_19E[0x4]; + /* 0x01A2 */ s16 unk_1A2[5]; + /* 0x01AC */ s16 unk_1AC; + /* 0x01B0 */ f32 unk_1B0; + /* 0x01B4 */ f32 unk_1B4; + /* 0x01B8 */ Vec3f unk_1B8; + /* 0x01C4 */ Vec3f unk_1C4; + /* 0x01D0 */ Vec3f unk_1D0; + /* 0x01D0 */ Vec3f unk_1DC; + /* 0x01E8 */ Vec3f unk_1E8; + /* 0x01F4 */ Vec3f unk_1F4; + /* 0x0200 */ Vec3f unk_200; + /* 0x020C */ Vec3f unk_20C; + /* 0x0218 */ Vec3f unk_218; + /* 0x0224 */ f32 unk_224; + /* 0x0228 */ f32 unk_228; + /* 0x022C */ char unk_22C[0x8]; + /* 0x0234 */ Vec3f unk_234[16]; + /* 0x02F4 */ s16 unk_2F4[5]; + /* 0x02FE */ s16 unk_2FE[5]; + /* 0x0308 */ char unk_308[4]; + /* 0x030C */ f32 unk_30C; + /* 0x0310 */ u8 unk_310; + /* 0x0311 */ u8 unk_311; + /* 0x0312 */ u8 unk_312; + /* 0x0313 */ u8 unk_313; // "look_on" + /* 0x0314 */ u8 unk_314; + /* 0x0316 */ s16 unk_316; // "no_hit_time" + /* 0x0318 */ s16 unk_318; + /* 0x031A */ s16 unk_31A; + /* 0x031C */ s16 unk_31C; + /* 0x0320 */ f32 unk_320; + /* 0x0324 */ f32 unk_324; + /* 0x0328 */ s16 unk_328; + /* 0x032C */ f32 unk_32C; + /* 0x0330 */ s16 unk_330; + /* 0x0332 */ s16 unk_332; + /* 0x0334 */ u8 unk_334; + /* 0x0335 */ u8 unk_335; + /* 0x0336 */ u8 unk_336; + /* 0x0337 */ u8 unk_337; + /* 0x0338 */ u8 unk_338; + /* 0x0339 */ s8 unk_339; + /* 0x033C */ f32 unk_33C; + /* 0x0340 */ s16 unk_340; + /* 0x0342 */ s16 unk_342; + /* 0x0344 */ s16 unk_344; + /* 0x0346 */ s16 unk_346; + /* 0x0348 */ s16 unk_348[5]; + /* 0x0352 */ s16 unk_352[5]; + /* 0x035C */ f32 unk_35C; + /* 0x0360 */ Vec3f unk_360; + /* 0x036C */ f32 unk_36C; + /* 0x0370 */ Vec3f unk_370; + /* 0x037C */ f32 unk_37C; + /* 0x0380 */ f32 unk_380; + /* 0x0384 */ f32 unk_384; + /* 0x0388 */ f32 unk_388; + /* 0x038C */ f32 unk_38C; + /* 0x0390 */ s16 unk_390; + /* 0x0392 */ s16 unk_392; + /* 0x0394 */ f32 unk_394; + /* 0x0398 */ u32 unk_398; + /* 0x039C */ s16 unk_39C; + /* 0x039E */ s16 unk_39E; + /* 0x03A0 */ char unk_3A0[0x4]; + /* 0x03A4 */ Vec3f unk_3A4; + /* 0x03B0 */ Vec3f unk_3B0; + /* 0x03BC */ Vec3f unk_3BC; + /* 0x03C8 */ char unk_3C8[0x48]; + /* 0x0410 */ Vec3f unk_410; + /* 0x041C */ f32 unk_41C; + /* 0x0420 */ f32 unk_420; + /* 0x0424 */ ColliderJntSph unk_424; + /* 0x0444 */ ColliderJntSph unk_444; + /* 0x0464 */ ColliderJntSphElement unk_464[16]; + /* 0x0864 */ ColliderJntSphElement unk_864[2]; +} BossGanon2; // size = 0x08E4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2_data.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2_data.c new file mode 100644 index 000000000..47201ed0d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2_data.c @@ -0,0 +1,356 @@ +#include "z_boss_ganon2.h" +#include "overlays/actors/ovl_En_Zl3/z_en_zl3.h" +#include "objects/object_ganon2/object_ganon2.h" + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 unk_01; + /* 0x04 */ Vec3f position; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ char unk_28[0x6]; + /* 0x2E */ s16 unk_2E; + /* 0x30 */ char unk_30[0x4]; + /* 0x34 */ f32 scale; + /* 0x38 */ Vec3f unk_38; +} BossGanon2Effect; // size = 0x44 + +static Vec3f D_80906D60 = { 0.0f, 0.0f, 0.0f }; + +static Vec3f D_80906D6C = { 0.0f, 0.0f, 500.0f }; + +static u8 D_80906D78 = 0; + +static ColliderJntSphElementInit sJntSphItemsInit1[] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 2, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 3, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 4, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 5, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 6, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 7, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 8, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 9, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 10, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 11, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 12, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 14, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { 0, 0, 0 }, 30 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit1 = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_FIRST_ONLY | OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphItemsInit1), + sJntSphItemsInit1, +}; + +static ColliderJntSphElementInit sJntSphItemsInit2[] = { + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x40 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { 0, 0, 0 }, 45 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x40 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 16, { { 0, 0, 0 }, 45 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit2 = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphItemsInit2), + sJntSphItemsInit2, +}; + +static Color_RGBA8 sPrimColor = { 0, 120, 0, 255 }; + +static Color_RGBA8 sEnvColor = { 0, 120, 0, 255 }; + +static Vec3f D_8090702C[] = { + { 10.0f, -10.0f, 0.0f }, + { 0.0f, 0.0f, -60.0f }, + { 70.0f, -30.0f, 10.0f }, +}; + +static Vec3f D_80907050[] = { + { -20.0f, 0.0f, 0.0f }, + { -15.0f, 0.0f, 10.0f }, + { -16.0f, -12.0f, 40.0f }, +}; + +static s16 D_80907074[] = { 0, 1, 2, 2, 1, 0 }; + +static s16 D_80907080 = 0; + +static u8 D_80907084[] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }; + +static u8 D_80907090[] = { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21 }; + +static s8 D_8090709C[] = { + 0xFF, 0xFF, 0x01, 0xFF, 0x03, 0x04, 0xFF, 0xFF, 0x05, 0xFF, 0x06, 0x07, 0xFF, 0xFF, 0x08, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0x02, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0x00, +}; + +static s8 D_809070CC[] = { + 0xFF, 0xFF, 0x01, 0xFF, 0x03, 0x04, 0xFF, 0xFF, 0x05, 0xFF, 0x06, 0x07, 0xFF, 0xFF, 0x08, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0x02, 0x0C, 0x0D, 0x0E, 0x09, 0x0A, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, +}; + +static Vec3f D_809070FC = { 0.0f, 0.0f, 5000.0f }; + +static Vec3f D_80907108 = { 0.0f, 2000.0f, 0.0f }; + +static Vec3f D_80907114 = { 0.0f, 2000.0f, 0.0f }; + +static Vec3f D_80907120 = { 0.0f, 0.0f, 17000.0f }; + +static Vec3f D_8090712C = { 0.0f, 0.0f, 3000.0f }; + +static Vec3f D_80907138 = { 0.0f, 0.0f, 0.0f }; + +static s8 D_80907144[] = { + 0xFF, 0xFF, 0x01, 0xFF, 0x03, 0x04, 0x05, 0xFF, 0x06, 0x07, 0x08, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x02, 0x0C, 0x0D, 0x0E, 0x09, 0x0A, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, +}; + +static Vec3f D_80907164 = { 800.0f, 420.0f, 100.0f }; + +static void* sEyeTextures[] = { + object_ganon2_Tex_01E188, + object_ganon2_Tex_01E988, + object_ganon2_Tex_01EA08, +}; + +static Vec3f D_8090717C = { 0.0f, -2000.0f, 0.0f }; + +static s16 D_80907188[] = { 1, 2, 3, 3, 2, 1 }; + +static s16 D_80907194[] = { 2, 3, 4, 4, 4, 3, 2 }; + +static s16 D_809071A4[] = { 2, 3, 4, 4, 4, 4, 3, 2 }; + +static s16 D_809071B4[] = { 2, 4, 5, 5, 6, 6, 6, 6, 5, 5, 4, 2 }; + +static s16 D_809071CC[] = { 1, -1, 1, 1, 3, 4, 1, 6, 7, 2, 9, 10, 2, 12, 13 }; + +static u8 D_809071EC[] = { 3, 2, 2, 1, 3, 3, 1, 3, 3, 1, 0, 3, 1, 0, 3 }; + +// padding +static u32 D_809071FC[2] = { 0 }; + +#include "overlays/ovl_Boss_Ganon2/ovl_Boss_Ganon2.h" + +static Vec3f D_8090EB20; + +static EnZl3* sZelda; + +static Actor* D_8090EB30; + +// unused +static UNK_TYPE D_8090EB34; + +static BossGanon2Effect sParticles[100]; + +static s32 sSeed1; +static s32 sSeed2; +static s32 sSeed3; + +// unused +static UNK_TYPE D_809105DC; + +static Vec3f D_809105D8[4]; + +static Vec3f D_80910608[4]; + +static s8 D_80910638; diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c new file mode 100644 index 000000000..6f0c3dc30 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -0,0 +1,1542 @@ +/* + * File: z_boss_ganondrof.c + * Overlay: ovl_Boss_Ganondrof + * Description: Phantom Ganon + */ + +#include "z_boss_ganondrof.h" +#include "objects/object_gnd/object_gnd.h" +#include "overlays/actors/ovl_En_fHG/z_en_fhg.h" +#include "overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.h" +#include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ NOT_DEAD, + /* 1 */ DEATH_START, + /* 2 */ DEATH_THROES, + /* 3 */ DEATH_WARP, + /* 4 */ DEATH_SCREAM, + /* 5 */ DEATH_DISINTEGRATE, + /* 6 */ DEATH_FINISH +} BossGanondrofDeathState; + +typedef enum { + /* 0 */ THROW_NORMAL, + /* 1 */ THROW_SLOW +} BossGanondrofThrowAction; + +typedef enum { + /* 0 */ STUNNED_FALL, + /* 1 */ STUNNED_GROUND +} BossGanondrofStunnedAction; + +typedef enum { + /* 0 */ CHARGE_WINDUP, + /* 1 */ CHARGE_START, + /* 2 */ CHARGE_ATTACK, + /* 3 */ CHARGE_FINISH +} BossGanondrofChargeAction; + +typedef enum { + /* 0 */ DEATH_SPASM, + /* 1 */ DEATH_LIMP, + /* 2 */ DEATH_HUNCHED +} BossGanondrofDeathAction; + +void BossGanondrof_Init(Actor* thisx, GlobalContext* globalCtx); +void BossGanondrof_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossGanondrof_Update(Actor* thisx, GlobalContext* globalCtx); +void BossGanondrof_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BossGanondrof_SetupIntro(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Intro(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_SetupPaintings(BossGanondrof* this); +void BossGanondrof_Paintings(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_SetupNeutral(BossGanondrof* this, f32 arg1); +void BossGanondrof_Neutral(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_SetupThrow(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Throw(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_SetupBlock(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Block(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_SetupReturn(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Return(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_SetupCharge(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Charge(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Stunned(BossGanondrof* this, GlobalContext* globalCtx); +void BossGanondrof_Death(BossGanondrof* this, GlobalContext* globalCtx); + +const ActorInit Boss_Ganondrof_InitVars = { + ACTOR_BOSS_GANONDROF, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GND, + sizeof(BossGanondrof), + (ActorFunc)BossGanondrof_Init, + (ActorFunc)BossGanondrof_Destroy, + (ActorFunc)BossGanondrof_Update, + (ActorFunc)BossGanondrof_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInitBody = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 30, 90, -50, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInitSpear = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, 30, -20, { 0, 0, 0 } }, +}; + +// clang-format off +static u8 sDecayMaskHigh[16 * 16] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1, + 1,0,1,1,0,0,0,0,1,1,1,1,1,1,0,1, + 1,0,1,1,1,0,0,0,0,1,1,1,1,1,0,1, + 1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,1, + 1,0,0,1,1,1,1,1,0,0,0,1,1,0,0,1, + 1,0,1,1,1,1,0,0,0,0,0,1,0,0,0,1, + 1,1,1,1,1,1,1,1,0,0,0,0,1,1,0,1, + 1,0,1,1,1,1,1,0,0,0,0,1,1,1,0,1, + 1,0,0,1,1,1,0,0,0,1,1,1,1,1,0,1, + 1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1, + 1,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1, + 1,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1, + 1,0,1,1,1,1,1,0,0,1,1,1,1,1,0,1, + 1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +}; + +static u8 sDecayMaskLow[16 * 16] = { + 1,1,1,0,1,0,0,1,0,0,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0, + 1,0,1,1,0,0,0,0,0,1,1,0,0,1,0,0, + 1,0,0,1,1,0,0,0,0,0,1,1,1,0,0,0, + 0,0,0,1,1,1,0,0,0,0,0,1,1,0,0,1, + 0,0,0,1,0,0,1,0,0,0,0,0,1,0,0,1, + 1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0, + 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0, + 1,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1, + 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1, + 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1, + 1,0,0,0,0,1,0,0,0,0,0,1,1,1,0,0, + 1,0,0,0,0,1,0,0,0,0,1,0,1,1,0,0, + 0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0, + 1,0,0,0,1,0,1,0,0,0,1,1,0,0,0,1, + 1,0,0,1,1,1,0,0,0,1,1,1,0,0,0,1, +}; + +static u8 sDecayMaskTotal[16 * 16] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +}; +// clang-format on + +// These are Phantom Ganon's body textures, but I don't know which is which. +static void* sLimbTex_rgba16_8x8[] = { + gPhantomGanonLimbTex_00A800, gPhantomGanonLimbTex_00AE80, gPhantomGanonLimbTex_00AF00, + gPhantomGanonLimbTex_00C180, gPhantomGanonLimbTex_00C400, +}; +static void* sLimbTex_rgba16_16x8[] = { + gPhantomGanonLimbTex_00B980, gPhantomGanonLimbTex_00C480, gPhantomGanonLimbTex_00BC80, + gPhantomGanonLimbTex_00BD80, gPhantomGanonLimbTex_00C080, +}; +static void* sLimbTex_rgba16_16x16[] = { + gPhantomGanonLimbTex_00C200, gPhantomGanonLimbTex_00A000, gPhantomGanonLimbTex_00A200, + gPhantomGanonLimbTex_00A400, gPhantomGanonLimbTex_00A600, gPhantomGanonLimbTex_00A880, + gPhantomGanonLimbTex_00B780, gPhantomGanonLimbTex_00BA80, gPhantomGanonLimbTex_00BE80, +}; +static void* sLimbTex_rgba16_16x32[] = { gPhantomGanonLimbTex_00AA80, gPhantomGanonLimbTex_00AF80 }; + +static void* sMouthTex_ci8_16x16[] = { gPhantomGanonMouthTex, gPhantomGanonSmileTex }; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x2B, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +static Vec3f sAudioVec = { 0.0f, 0.0f, 50.0f }; + +// OTRTODO: This code appears to cause the game to gradually crash... +// Might be an OoB write. For now it's disabled. +void BossGanondrof_ClearPixels8x8(s16* texture, u8* mask, s16 index) +{ + //texture = ResourceMgr_LoadTexByName(texture); + if (mask[index]) { + //texture[index / 4] = 0; + ResourceMgr_WriteTexS16ByName(texture, index / 4, 0); + } +} + +void BossGanondrof_ClearPixels16x8(s16* texture, u8* mask, s16 index) { + //texture = ResourceMgr_LoadTexByName(texture); + if (mask[index]) { + //texture[index / 2] = 0; + ResourceMgr_WriteTexS16ByName(texture, index / 2, 0); + + } +} + +void BossGanondrof_ClearPixels16x16(s16* texture, u8* mask, s16 index, s16 bpp) { + //texture = ResourceMgr_LoadTexByName(texture); + if (mask[index]) { + //texture[index] = 0; + ResourceMgr_WriteTexS16ByName(texture, index, 0); + } +} + +void BossGanondrof_ClearPixels32x16(s16* texture, u8* mask, s16 index) { + //texture = ResourceMgr_LoadTexByName(texture); + if (mask[index]) { + s16 i = (index & 0xF) + ((index & 0xF0) << 1); + + ResourceMgr_WriteTexS16ByName(texture, i + 0x10, 0); + ResourceMgr_WriteTexS16ByName(texture, i, 0); + //texture[i + 0x10] = 0; + //texture[i] = 0; + } +} + +void BossGanondrof_ClearPixels16x32(s16* texture, u8* mask, s16 index) { + //texture = ResourceMgr_LoadTexByName(texture); + if (mask[index]) { + s16 i = ((index & 0xF) * 2) + ((index & 0xF0) * 2); + + ResourceMgr_WriteTexS16ByName(texture, i + 1, 0); + ResourceMgr_WriteTexS16ByName(texture, i, 0); + + //texture[i + 1] = 0; + //texture[i] = 0; + } + +} + +void BossGanondrof_ClearPixels(u8* mask, s16 index) { + s16 i; + + for (i = 0; i < 5; i++) { + // ARRAY_COUNT can't be used here because the arrays aren't guaranteed to be the same size. + BossGanondrof_ClearPixels8x8(SEGMENTED_TO_VIRTUAL(sLimbTex_rgba16_8x8[i]), mask, index); + BossGanondrof_ClearPixels16x8(SEGMENTED_TO_VIRTUAL(sLimbTex_rgba16_16x8[i]), mask, index); + } + + for (i = 0; i < ARRAY_COUNT(sLimbTex_rgba16_16x16); i++) { + BossGanondrof_ClearPixels16x16(SEGMENTED_TO_VIRTUAL(sLimbTex_rgba16_16x16[i]), mask, index, 2); + } + + for (i = 0; i < ARRAY_COUNT(sLimbTex_rgba16_16x32); i++) { + BossGanondrof_ClearPixels16x32(SEGMENTED_TO_VIRTUAL(sLimbTex_rgba16_16x32[i]), mask, index); + } + + BossGanondrof_ClearPixels32x16(SEGMENTED_TO_VIRTUAL(gPhantomGanonLimbTex_00B380), mask, index); + BossGanondrof_ClearPixels16x32(SEGMENTED_TO_VIRTUAL(gPhantomGanonEyeTex), mask, index); + for (i = 0; i < ARRAY_COUNT(sMouthTex_ci8_16x16); i++) { + BossGanondrof_ClearPixels16x16(SEGMENTED_TO_VIRTUAL(sMouthTex_ci8_16x16[i]), mask, index, 1); + } +} + +void BossGanondrof_SetColliderPos(Vec3f* pos, ColliderCylinder* collider) { + collider->dim.pos.x = pos->x; + collider->dim.pos.y = pos->y; + collider->dim.pos.z = pos->z; +} + +void BossGanondrof_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossGanondrof* this = (BossGanondrof*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + Actor_SetScale(&this->actor, 0.01f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gPhantomGanonSkel, &gPhantomGanonRideAnim, NULL, NULL, 0); + if (this->actor.params < GND_FAKE_BOSS) { + this->actor.params = GND_REAL_BOSS; + this->actor.colChkInfo.health = 30; + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, 255); + BossGanondrof_SetupIntro(this, globalCtx); + } else { + BossGanondrof_SetupPaintings(this); + } + + Collider_InitCylinder(globalCtx, &this->colliderBody); + Collider_InitCylinder(globalCtx, &this->colliderSpear); + Collider_SetCylinder(globalCtx, &this->colliderBody, &this->actor, &sCylinderInitBody); + Collider_SetCylinder(globalCtx, &this->colliderSpear, &this->actor, &sCylinderInitSpear); + this->actor.flags &= ~ACTOR_FLAG_0; + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + Actor_Kill(&this->actor); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y, + GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, 200.0f + GND_BOSSROOM_CENTER_X, + GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, 0); + } else { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, this->actor.params); + } +} + +void BossGanondrof_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossGanondrof* this = (BossGanondrof*)thisx; + + osSyncPrintf("DT1\n"); + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->colliderBody); + Collider_DestroyCylinder(globalCtx, &this->colliderSpear); + if (this->actor.params == GND_REAL_BOSS) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + } + + osSyncPrintf("DT2\n"); +} + +void BossGanondrof_SetupIntro(BossGanondrof* this, GlobalContext* globalCtx) { + Animation_PlayLoop(&this->skelAnime, &gPhantomGanonRidePoseAnim); + this->actionFunc = BossGanondrof_Intro; + this->work[GND_MASK_OFF] = true; +} + +void BossGanondrof_Intro(BossGanondrof* this, GlobalContext* globalCtx) { + s16 i; + s32 pad; + EnfHG* horse = (EnfHG*)this->actor.child; + + SkelAnime_Update(&this->skelAnime); + this->actor.world.pos = horse->actor.world.pos; + this->actor.shape.rot.y = this->actor.world.rot.y = horse->actor.world.rot.y; + + osSyncPrintf("SW %d------------------------------------------------\n", horse->bossGndSignal); + + if ((this->timers[1] != 0) && (this->timers[1] < 25)) { + Vec3f pos; + Vec3f vel = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + pos.x = this->bodyPartsPos[14].x + Rand_CenteredFloat(10.0f); + pos.y = this->bodyPartsPos[14].y + Rand_ZeroFloat(-5.0f); + pos.z = this->bodyPartsPos[14].z + Rand_CenteredFloat(10.0f) + 5.0f; + accel.y = 0.03f; + EffectSsKFire_Spawn(globalCtx, &pos, &vel, &accel, (s16)Rand_ZeroFloat(10.0f) + 5, 0); + } + + if (this->timers[1] == 20) { + this->work[GND_MASK_OFF] = false; + } + + if (this->timers[1] == 30) { + func_80078914(&sAudioVec, NA_SE_EN_FANTOM_TRANSFORM); + } + + if (horse->bossGndSignal == FHG_LIGHTNING) { + Animation_Change(&this->skelAnime, &gPhantomGanonMaskOnAnim, 0.5f, 0.0f, + Animation_GetLastFrame(&gPhantomGanonMaskOnAnim), ANIMMODE_ONCE_INTERP, 0.0f); + this->timers[1] = 40; + } + + if (horse->bossGndSignal == FHG_REAR) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonHorseRearingAnim, -3.0f); + } + + if (horse->bossGndSignal == FHG_RIDE) { + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonRidePoseAnim, -13.0f); + } + + if (horse->bossGndSignal == FHG_SPUR) { + EnfHG* horseTemp; + + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearRaiseAnim, -7.0f); + horseTemp = (EnfHG*)this->actor.child; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, this->spearTip.x, + this->spearTip.y, this->spearTip.z, 50, FHGFIRE_LIGHT_GREEN, 0, FHGFIRE_SPEAR_LIGHT); + this->actor.child = &horseTemp->actor; + } + + if (horse->bossGndSignal == FHG_FINISH) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearResetAnim, -5.0f); + } + + switch (this->work[GND_EYE_STATE]) { + case GND_EYESTATE_FADE: + this->fwork[GND_EYE_ALPHA] += 40.0f; + if (this->fwork[GND_EYE_ALPHA] >= 255.0f) { + this->fwork[GND_EYE_ALPHA] = 255.0f; + } + break; + + case GND_EYESTATE_BRIGHTEN: + this->fwork[GND_EYE_BRIGHTNESS] += 20.0f; + if (this->fwork[GND_EYE_BRIGHTNESS] > 255.0f) { + this->fwork[GND_EYE_BRIGHTNESS] = 255.0f; + } + break; + } + + this->armRotY = Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x6E8) * 0; + this->armRotZ = Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x8DC) * 300.0f; + for (i = 0; i < 30; i++) { + this->rideRotY[i] = Math_SinS(this->work[GND_VARIANCE_TIMER] * ((i * 50) + 0x7B0)) * 100.0f; + this->rideRotZ[i] = Math_CosS(this->work[GND_VARIANCE_TIMER] * ((i * 50) + 0x8DC)) * 100.0f; + } + + if (horse->bossGndSignal == FHG_START_FIGHT) { + BossGanondrof_SetupPaintings(this); + for (i = 0; i < 30; i++) { + this->rideRotY[i] = this->rideRotZ[i] = 0.0f; + } + } + + horse->bossGndSignal = FHG_NO_SIGNAL; +} + +void BossGanondrof_SetupPaintings(BossGanondrof* this) { + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonRideAnim, -5.0f); + this->actionFunc = BossGanondrof_Paintings; +} + +void BossGanondrof_Paintings(BossGanondrof* this, GlobalContext* globalCtx) { + EnfHG* horse = (EnfHG*)this->actor.child; + + osSyncPrintf("RUN 1\n"); + SkelAnime_Update(&this->skelAnime); + osSyncPrintf("RUN 2\n"); + + if (horse->bossGndSignal == FHG_RAISE_SPEAR) { + EnfHG* horseTemp; + + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearRaiseAnim, -2.0f); + this->actor.flags |= ACTOR_FLAG_0; + horseTemp = (EnfHG*)this->actor.child; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, this->spearTip.x, + this->spearTip.y, this->spearTip.z, 30, FHGFIRE_LIGHT_GREEN, 0, FHGFIRE_SPEAR_LIGHT); + this->actor.child = &horseTemp->actor; + } else if (horse->bossGndSignal == FHG_LIGHTNING) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearStrikeAnim, -2.0f); + } else if (horse->bossGndSignal == FHG_RESET) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonRideSpearResetAnim, -2.0f); + } else if (horse->bossGndSignal == FHG_RIDE) { + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonRideAnim, -2.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + } + + osSyncPrintf("RUN 3\n"); + this->actor.world.pos = horse->actor.world.pos; + this->actor.world.pos.y = horse->actor.world.pos.y; + this->actor.shape.rot.y = this->actor.world.rot.y = horse->actor.world.rot.y; + if (this->flyMode != GND_FLY_PAINTING) { + BossGanondrof_SetupNeutral(this, -20.0f); + this->timers[0] = 100; + this->colliderBody.dim.radius = 20; + this->colliderBody.dim.height = 60; + this->colliderBody.dim.yShift = -33; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAUGH); + this->actor.naviEnemyId = 0x1A; + } else { + horse->bossGndSignal = FHG_NO_SIGNAL; + this->actor.scale.x = horse->actor.scale.x / 1.15f; + this->actor.scale.y = horse->actor.scale.y / 1.15f; + this->actor.scale.z = horse->actor.scale.z / 1.15f; + osSyncPrintf("RUN 4\n"); + } +} + +void BossGanondrof_SetupNeutral(BossGanondrof* this, f32 arg1) { + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonNeutralAnim, arg1); + this->actionFunc = BossGanondrof_Neutral; + this->actor.flags |= ACTOR_FLAG_0; + this->fwork[GND_FLOAT_SPEED] = 0.0f; + this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; +} + +void BossGanondrof_Neutral(BossGanondrof* this, GlobalContext* globalCtx) { + f32 targetX; + f32 targetY; + f32 targetZ; + Player* player = GET_PLAYER(globalCtx); + Actor* playerx = &player->actor; + Actor* thisx = &this->actor; + f32 rand01; + s16 i; + + SkelAnime_Update(&this->skelAnime); + switch (this->flyMode) { + case GND_FLY_NEUTRAL: + if (this->timers[0] == 0) { + this->timers[0] = (s16)(Rand_ZeroOne() * 64.0f) + 30; + rand01 = Rand_ZeroOne(); + if (thisx->colChkInfo.health < 5) { + if (rand01 < 0.25f) { + BossGanondrof_SetupThrow(this, globalCtx); + } else if (rand01 >= 0.8f) { + this->flyMode = GND_FLY_CHARGE; + this->timers[0] = 60; + this->fwork[GND_FLOAT_SPEED] = 0.0f; + Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_LAUGH); + } else { + this->flyMode = GND_FLY_VOLLEY; + this->timers[0] = 60; + this->fwork[GND_FLOAT_SPEED] = 0.0f; + Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_LAUGH); + } + } else if ((rand01 < 0.5f) || (this->work[GND_THROW_COUNT] < 5)) { + BossGanondrof_SetupThrow(this, globalCtx); + } else { + this->flyMode = GND_FLY_VOLLEY; + this->timers[0] = 60; + this->fwork[GND_FLOAT_SPEED] = 0.0f; + Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_LAUGH); + } + } + + if (this->timers[1] != 0) { + targetX = GND_BOSSROOM_CENTER_X; + targetZ = GND_BOSSROOM_CENTER_Z; + } else { + targetX = playerx->world.pos.x + (180.0f * Math_SinS(playerx->shape.rot.y)); + targetZ = playerx->world.pos.z + (180.0f * Math_CosS(playerx->shape.rot.y)); + if (sqrtf(SQ(targetX - GND_BOSSROOM_CENTER_X) + SQ(targetZ - GND_BOSSROOM_CENTER_Z)) > 280.0f) { + this->timers[1] = 50; + this->fwork[GND_FLOAT_SPEED] = 0.0f; + } + } + + targetY = playerx->world.pos.y + 100.0f + 0.0f; + targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 100.0f; + targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 100.0f; + break; + case GND_FLY_VOLLEY: + targetX = GND_BOSSROOM_CENTER_X - 14.0f; + targetZ = GND_BOSSROOM_CENTER_Z + 265.0f; + + targetY = playerx->world.pos.y + 100.0f + 100.0f; + targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 100.0f; + targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 100.0f; + if (this->timers[0] == 0) { + this->flyMode = GND_FLY_RETURN; + this->returnSuccess = false; + BossGanondrof_SetupThrow(this, globalCtx); + this->timers[0] = 80; + } + break; + case GND_FLY_RETURN: + targetX = GND_BOSSROOM_CENTER_X - 14.0f; + targetZ = GND_BOSSROOM_CENTER_Z + 265.0f; + + targetY = playerx->world.pos.y + 100.0f + 100.0f; + targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 50.0f; + targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 50.0f; + if (this->returnSuccess) { + this->returnSuccess = false; + BossGanondrof_SetupReturn(this, globalCtx); + this->timers[0] = 80; + } + + if (this->timers[0] == 0) { + this->flyMode = GND_FLY_NEUTRAL; + } + break; + case GND_FLY_CHARGE: + targetX = GND_BOSSROOM_CENTER_X - 14.0f; + targetZ = GND_BOSSROOM_CENTER_Z + 215.0f; + + targetY = playerx->world.pos.y + 100.0f + 50.0f; + targetX += Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x500) * 100.0f; + targetZ += Math_CosS(this->work[GND_VARIANCE_TIMER] * 0x700) * 100.0f; + if (this->timers[0] == 0) { + BossGanondrof_SetupCharge(this, globalCtx); + } + break; + } + + Math_ApproachF(&thisx->world.pos.x, targetX, 0.05f, this->fwork[GND_FLOAT_SPEED]); + if (this->timers[2] != 0) { + Math_ApproachF(&thisx->world.pos.y, targetY + 100.0f, 0.1f, 50.0f); + } else { + Math_ApproachF(&thisx->world.pos.y, targetY, 0.05f, 10.0f); + } + + Math_ApproachF(&thisx->world.pos.z, targetZ, 0.05f, this->fwork[GND_FLOAT_SPEED]); + Math_ApproachF(&this->fwork[GND_FLOAT_SPEED], 50.0f, 1.0f, 0.5f); + thisx->velocity.x = thisx->world.pos.x - thisx->prevPos.x; + thisx->velocity.z = thisx->world.pos.z - thisx->prevPos.z; + thisx->world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); + Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0xBB8); + if ((this->work[GND_VARIANCE_TIMER] & 1) == 0) { + Vec3f pos; + Vec3f vel = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < 3; i++) { + pos.x = Rand_CenteredFloat(20.0f) + this->spearTip.x; + pos.y = Rand_CenteredFloat(20.0f) + this->spearTip.y; + pos.z = Rand_CenteredFloat(20.0f) + this->spearTip.z; + accel.y = -0.08f; + EffectSsFhgFlash_SpawnLightBall(globalCtx, &pos, &vel, &accel, (s16)(Rand_ZeroOne() * 80.0f) + 150, + FHGFLASH_LIGHTBALL_GREEN); + } + } + + if (player->unk_A73 != 0) { + BossGanondrof_SetupBlock(this, globalCtx); + } + + Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_FLOAT - SFX_FLAG); +} + +void BossGanondrof_SetupThrow(BossGanondrof* this, GlobalContext* globalCtx) { + EnfHG* horseTemp; + s16 lightTime; + + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonThrowAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonThrowAnim, -5.0f); + this->actionFunc = BossGanondrof_Throw; + if ((Rand_ZeroOne() <= 0.1f) && (this->work[GND_THROW_COUNT] >= 10) && (this->flyMode == GND_FLY_NEUTRAL)) { + this->work[GND_ACTION_STATE] = THROW_SLOW; + this->work[GND_THROW_FRAME] = 1000; + lightTime = 32; + } else { + this->work[GND_ACTION_STATE] = THROW_NORMAL; + this->work[GND_THROW_FRAME] = 25; + lightTime = 25; + } + + horseTemp = (EnfHG*)this->actor.child; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, this->spearTip.x, + this->spearTip.y, this->spearTip.z, lightTime, FHGFIRE_LIGHT_GREEN, 0, FHGFIRE_SPEAR_LIGHT); + this->actor.child = &horseTemp->actor; + this->work[GND_THROW_COUNT]++; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_STICK); +} + +void BossGanondrof_Throw(BossGanondrof* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + osSyncPrintf("this->fwork[GND_END_FRAME] = %d\n", (s16)this->fwork[GND_END_FRAME]); + osSyncPrintf("this->work[GND_SHOT_FRAME] = %d\n", this->work[GND_THROW_FRAME]); + if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { + BossGanondrof_SetupNeutral(this, -6.0f); + } + + if ((this->work[GND_ACTION_STATE] != THROW_NORMAL) && Animation_OnFrame(&this->skelAnime, 21.0f)) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonThrowEndAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonThrowEndAnim, 0.0f); + this->work[GND_THROW_FRAME] = 10; + } + + if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { + if (this->flyMode <= GND_FLY_NEUTRAL) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC2); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC1); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); + } + + if (Animation_OnFrame(&this->skelAnime, this->work[GND_THROW_FRAME])) { + EnfHG* horseTemp = (EnfHG*)this->actor.child; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, this->spearTip.x, + this->spearTip.y, this->spearTip.z, this->work[GND_ACTION_STATE], 0, 0, FHGFIRE_ENERGY_BALL); + this->actor.child = &horseTemp->actor; + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x7D0); + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f); + Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f); + this->actor.world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); +} + +void BossGanondrof_SetupReturn(BossGanondrof* this, GlobalContext* globalCtx) { + static AnimationHeader* returnAnim[] = { &gPhantomGanonReturn1Anim, &gPhantomGanonReturn2Anim }; + s16 rand = Rand_ZeroOne() * 1.99f; + + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(returnAnim[rand]); + Animation_MorphToPlayOnce(&this->skelAnime, returnAnim[rand], 0.0f); + this->actionFunc = BossGanondrof_Return; +} + +void BossGanondrof_Return(BossGanondrof* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); + osSyncPrintf("VOISE 2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + osSyncPrintf("VOISE 2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + } + + if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { + BossGanondrof_SetupNeutral(this, 0.0f); + } + + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f); + Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f); + this->actor.world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); + if (this->returnSuccess) { + this->returnSuccess = false; + BossGanondrof_SetupReturn(this, globalCtx); + this->timers[0] = 80; + } +} + +void BossGanondrof_SetupStunned(BossGanondrof* this, GlobalContext* globalCtx) { + if (this->actionFunc != BossGanondrof_Stunned) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.0f); + this->timers[0] = 50; + this->shockTimer = 60; + } else { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonGroundDamageAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonGroundDamageAnim, 0.0f); + } + + this->actionFunc = BossGanondrof_Stunned; + this->work[GND_ACTION_STATE] = STUNNED_FALL; + this->actor.velocity.x = 0.0f; + this->actor.velocity.z = 0.0f; +} + +void BossGanondrof_Stunned(BossGanondrof* this, GlobalContext* globalCtx) { + osSyncPrintf("DAMAGE .................................\n"); + SkelAnime_Update(&this->skelAnime); + this->actor.gravity = -0.2f; + if (this->actor.world.pos.y <= 5.0f) { + if (this->work[GND_ACTION_STATE] == STUNNED_FALL) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonStunnedAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonStunnedAnim, -10.0f); + this->work[GND_ACTION_STATE] = STUNNED_GROUND; + } + + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE2); + } + + this->actor.flags |= ACTOR_FLAG_10; + } + + osSyncPrintf("TIME0 %d ********************************************\n", this->timers[0]); + if (this->timers[0] == 0) { + BossGanondrof_SetupNeutral(this, -5.0f); + this->timers[0] = 30; + this->timers[2] = 30; + this->flyMode = GND_FLY_NEUTRAL; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + } + + Actor_MoveForward(&this->actor); +} + +void BossGanondrof_SetupBlock(BossGanondrof* this, GlobalContext* globalCtx) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonBlockAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonBlockAnim, -3.0f); + this->actionFunc = BossGanondrof_Block; + this->timers[0] = 10; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_STICK); +} + +void BossGanondrof_Block(BossGanondrof* this, GlobalContext* globalCtx) { + this->colliderBody.base.colType = COLTYPE_METAL; + SkelAnime_Update(&this->skelAnime); + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + Math_ApproachZeroF(&this->actor.velocity.x, 1.0f, 0.5f); + Math_ApproachZeroF(&this->actor.velocity.z, 1.0f, 0.5f); + this->actor.world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); + if (this->timers[0] == 0) { + BossGanondrof_SetupNeutral(this, -5.0f); + this->timers[0] = 10; + this->flyMode = GND_FLY_NEUTRAL; + } +} + +void BossGanondrof_SetupCharge(BossGanondrof* this, GlobalContext* globalCtx) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonChargeWindupAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonChargeWindupAnim, -3.0f); + this->actionFunc = BossGanondrof_Charge; + this->timers[0] = 20; + this->work[GND_ACTION_STATE] = CHARGE_WINDUP; +} + +void BossGanondrof_Charge(BossGanondrof* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* playerx = &player->actor; + Actor* thisx = &this->actor; + f32 dxCenter = thisx->world.pos.x - GND_BOSSROOM_CENTER_X; + f32 dzCenter = thisx->world.pos.z - GND_BOSSROOM_CENTER_Z; + + this->colliderBody.base.colType = COLTYPE_METAL; + SkelAnime_Update(&this->skelAnime); + switch (this->work[GND_ACTION_STATE]) { + case CHARGE_WINDUP: + if (this->timers[0] == 218) { + Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_STICK); + } + + if (this->timers[0] == 19) { + Audio_PlayActorSound2(thisx, NA_SE_EN_FANTOM_ATTACK); + } + + thisx->world.pos.x += thisx->velocity.x; + thisx->world.pos.z += thisx->velocity.z; + Math_ApproachZeroF(&thisx->velocity.x, 1.0f, 0.5f); + Math_ApproachZeroF(&thisx->velocity.z, 1.0f, 0.5f); + if (this->timers[0] == 0) { + this->work[GND_ACTION_STATE] = CHARGE_START; + this->timers[0] = 10; + thisx->speedXZ = 0.0f; + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonChargeStartAnim); + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonChargeStartAnim, 0.0f); + } + + Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0x7D0); + break; + case CHARGE_START: + if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonChargeAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonChargeAnim, 0.0f); + this->work[GND_ACTION_STATE] = CHARGE_ATTACK; + } + case CHARGE_ATTACK: + if (this->timers[0] != 0) { + Vec3f vecToLink; + + Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0x7D0); + vecToLink.x = playerx->world.pos.x - thisx->world.pos.x; + vecToLink.y = playerx->world.pos.y + 40.0f - thisx->world.pos.y; + vecToLink.z = playerx->world.pos.z - thisx->world.pos.z; + thisx->world.rot.y = thisx->shape.rot.y; + thisx->world.rot.x = + Math_FAtan2F(vecToLink.y, sqrtf(SQ(vecToLink.x) + SQ(vecToLink.z))) * (0x8000 / M_PI); + } + + func_8002D908(thisx); + func_8002D7EC(thisx); + Math_ApproachF(&thisx->speedXZ, 10.0f, 1.0f, 0.5f); + if ((sqrtf(SQ(dxCenter) + SQ(dzCenter)) > 280.0f) || (thisx->xyzDistToPlayerSq < SQ(100.0f))) { + this->work[GND_ACTION_STATE] = CHARGE_FINISH; + this->timers[0] = 20; + } + break; + case CHARGE_FINISH: + thisx->gravity = 0.2f; + Actor_MoveForward(thisx); + osSyncPrintf("YP %f @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", thisx->world.pos.y); + if (thisx->world.pos.y < 5.0f) { + thisx->world.pos.y = 5.0f; + thisx->velocity.y = 0.0f; + } + + if (sqrtf(SQ(dxCenter) + SQ(dzCenter)) > 280.0f) { + Math_ApproachZeroF(&thisx->speedXZ, 1.0f, 2.0f); + this->timers[0] = 0; + } + + if (this->timers[0] == 0) { + Math_ApproachZeroF(&thisx->speedXZ, 1.0f, 2.0f); + Math_ApproachZeroF(&thisx->velocity.y, 1.0f, 2.0f); + Math_ApproachS(&thisx->shape.rot.y, thisx->yawTowardsPlayer, 5, 0x7D0); + if ((thisx->speedXZ <= 0.5f) && (fabsf(thisx->velocity.y) <= 0.1f)) { + BossGanondrof_SetupNeutral(this, -10.0f); + this->timers[0] = 30; + this->flyMode = GND_FLY_NEUTRAL; + } + } + break; + } + + if (thisx->world.pos.y > (GND_BOSSROOM_CENTER_Y + 83.0f)) { + thisx->world.pos.y += 2.0f * Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); + } + { + s16 i; + Vec3f pos; + Vec3f vel = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f baseOffset = { 0.0f, 50.0f, 0.0f }; + Vec3f offset; + + baseOffset.y = 10.0f; + for (i = 0; i < 10; i++) { + Matrix_Push(); + Matrix_RotateY((thisx->shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_RotateX((thisx->shape.rot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((this->work[GND_PARTICLE_ANGLE] / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_MultVec3f(&baseOffset, &offset); + Matrix_Pop(); + pos.x = this->spearTip.x + offset.x; + pos.y = this->spearTip.y + offset.y; + pos.z = this->spearTip.z + offset.z; + vel.x = (offset.x * 500.0f) / 1000.0f; + vel.y = (offset.y * 500.0f) / 1000.0f; + vel.z = (offset.z * 500.0f) / 1000.0f; + accel.x = (offset.x * -50.0f) / 1000.0f; + accel.y = (offset.y * -50.0f) / 1000.0f; + accel.z = (offset.z * -50.0f) / 1000.0f; + EffectSsFhgFlash_SpawnLightBall(globalCtx, &pos, &vel, &accel, 150, i % 7); + this->work[GND_PARTICLE_ANGLE] += 0x1A5C; + } + } + + if (!(this->work[GND_VARIANCE_TIMER] & 7)) { + EnfHG* horse = (EnfHG*)thisx->child; + + Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_EN_FHG_FIRE, this->spearTip.x, + this->spearTip.y, this->spearTip.z, 8, FHGFIRE_LIGHT_BLUE, 0, FHGFIRE_SPEAR_LIGHT); + thisx->child = &horse->actor; + } +} + +void BossGanondrof_SetupDeath(BossGanondrof* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gPhantomGanonDeathBlowAnim); + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonDeathBlowAnim); + this->actionFunc = BossGanondrof_Death; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DEAD); + this->deathState = DEATH_START; + this->actor.flags &= ~ACTOR_FLAG_0; + this->work[GND_VARIANCE_TIMER] = 0; + this->shockTimer = 50; +} + +void BossGanondrof_Death(BossGanondrof* this, GlobalContext* globalCtx) { + u8 holdCamera = false; + u8 bodyDecayLevel = 0; + f32 camX; + f32 camZ; + f32 pad; + Player* player = GET_PLAYER(globalCtx); + Camera* camera = Gameplay_GetCamera(globalCtx, 0); + + osSyncPrintf("PYP %f\n", player->actor.floorHeight); + SkelAnime_Update(&this->skelAnime); + this->work[GND_DEATH_SFX_TIMER]++; + if (((60 < this->work[GND_DEATH_SFX_TIMER]) && (this->work[GND_DEATH_SFX_TIMER] < 500)) || + ((501 < this->work[GND_DEATH_SFX_TIMER]) && (this->work[GND_DEATH_SFX_TIMER] < 620))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_LAST - SFX_FLAG); + } + + switch (this->deathState) { + case DEATH_START: + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 1); + this->deathCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + osSyncPrintf("7\n"); + Gameplay_ChangeCameraStatus(globalCtx, this->deathCamera, CAM_STAT_ACTIVE); + osSyncPrintf("8\n"); + this->deathState = DEATH_THROES; + player->actor.speedXZ = 0.0f; + this->timers[0] = 50; + this->cameraEye = camera->eye; + this->cameraAt = camera->at; + this->cameraNextEye.x = this->targetPos.x; + this->cameraNextEye.y = GND_BOSSROOM_CENTER_Y + 83.0f; + this->cameraNextEye.z = (this->targetPos.z + 100.0f) + 50; + this->cameraNextAt.x = this->targetPos.x; + this->cameraNextAt.y = this->targetPos.y - 10.0f; + this->cameraNextAt.z = this->targetPos.z; + this->cameraEyeVel.x = fabsf(camera->eye.x - this->cameraNextEye.x); + this->cameraEyeVel.y = fabsf(camera->eye.y - this->cameraNextEye.y); + this->cameraEyeVel.z = fabsf(camera->eye.z - this->cameraNextEye.z); + this->cameraAtVel.x = fabsf(camera->at.x - this->cameraNextAt.x); + this->cameraAtVel.y = fabsf(camera->at.y - this->cameraNextAt.y); + this->cameraAtVel.z = fabsf(camera->at.z - this->cameraNextAt.z); + this->cameraAccel = 0.02f; + this->cameraEyeMaxVel.x = this->cameraEyeMaxVel.y = this->cameraEyeMaxVel.z = 0.05f; + this->work[GND_ACTION_STATE] = DEATH_SPASM; + this->timers[0] = 150; + this->cameraAtMaxVel.x = 0.2f; + this->cameraAtMaxVel.y = 0.2f; + this->cameraAtMaxVel.z = 0.2f; + case DEATH_THROES: + switch (this->work[GND_ACTION_STATE]) { + case DEATH_SPASM: + if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonAirDamageAnim); + Animation_Change(&this->skelAnime, &gPhantomGanonAirDamageAnim, 0.5f, 0.0f, + this->fwork[GND_END_FRAME], ANIMMODE_ONCE_INTERP, 0.0f); + this->work[GND_ACTION_STATE] = DEATH_LIMP; + } + break; + case DEATH_LIMP: + if (Animation_OnFrame(&this->skelAnime, this->fwork[GND_END_FRAME])) { + this->fwork[GND_END_FRAME] = Animation_GetLastFrame(&gPhantomGanonLimpAnim); + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonLimpAnim, -20.0f); + this->work[GND_ACTION_STATE] = DEATH_HUNCHED; + } + case DEATH_HUNCHED: + bodyDecayLevel = 1; + break; + } + Math_ApproachS(&this->actor.shape.rot.y, this->work[GND_VARIANCE_TIMER] * -100, 5, 0xBB8); + Math_ApproachF(&this->cameraNextEye.z, this->targetPos.z + 60.0f, 0.02f, 0.5f); + Math_ApproachF(&this->actor.world.pos.y, GND_BOSSROOM_CENTER_Y + 133.0f, 0.05f, 100.0f); + this->actor.world.pos.y += Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); + this->cameraNextAt.x = this->targetPos.x; + this->cameraNextAt.y = this->targetPos.y - 10.0f; + this->cameraNextAt.z = this->targetPos.z; + if (this->timers[0] == 0) { + this->deathState = DEATH_WARP; + this->timers[0] = 350; + this->timers[1] = 50; + this->fwork[GND_CAMERA_ZOOM] = 300.0f; + this->cameraNextEye.y = GND_BOSSROOM_CENTER_Y + 233.0f; + player->actor.world.pos.x = GND_BOSSROOM_CENTER_X - 200.0f; + player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; + holdCamera = true; + bodyDecayLevel = 1; + } + break; + case DEATH_WARP: + if (this->timers[1] == 1) { + EnfHG* horseTemp = (EnfHG*)this->actor.child; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + GND_BOSSROOM_CENTER_X, GND_BOSSROOM_CENTER_Y + 3.0f, GND_BOSSROOM_CENTER_Z, 0x4000, + 0, 0, FHGFIRE_WARP_DEATH); + this->actor.child = &horseTemp->actor; + Message_StartTextbox(globalCtx, 0x108E, NULL); + } + + this->actor.shape.rot.y -= 0xC8; + this->actor.world.pos.y += Math_SinS(this->work[GND_VARIANCE_TIMER] * 1500); + this->fwork[GND_CAMERA_ANGLE] += 0x78; + camX = Math_SinS(this->fwork[GND_CAMERA_ANGLE]) * this->fwork[GND_CAMERA_ZOOM]; + camZ = Math_CosS(this->fwork[GND_CAMERA_ANGLE]) * this->fwork[GND_CAMERA_ZOOM]; + this->cameraEye.x = GND_BOSSROOM_CENTER_X + camX; + this->cameraEye.y = this->cameraNextEye.y; + this->cameraEye.z = GND_BOSSROOM_CENTER_Z + camZ; + this->cameraAt.x = GND_BOSSROOM_CENTER_X; + this->cameraAt.y = GND_BOSSROOM_CENTER_Y + 23.0f; + this->cameraAt.z = GND_BOSSROOM_CENTER_Z; + Math_ApproachF(&this->cameraNextEye.y, GND_BOSSROOM_CENTER_Y + 33.0f, 0.05f, 0.5f); + Math_ApproachF(&this->fwork[GND_CAMERA_ZOOM], 170.0f, 0.05f, 1.0f); + Math_ApproachF(&this->actor.world.pos.x, GND_BOSSROOM_CENTER_X, 0.05f, 1.5f); + Math_ApproachF(&this->actor.world.pos.y, GND_BOSSROOM_CENTER_Y + 83.0f, 0.05f, 1.0f); + Math_ApproachF(&this->actor.world.pos.z, GND_BOSSROOM_CENTER_Z, 0.05f, 1.5f); + if (this->timers[0] == 0) { + this->deathState = DEATH_SCREAM; + this->timers[0] = 50; + Animation_MorphToLoop(&this->skelAnime, &gPhantomGanonScreamAnim, -10.0f); + this->actor.world.pos.x = GND_BOSSROOM_CENTER_X; + this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f; + this->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; + this->actor.shape.rot.y = 0; + this->work[GND_BODY_DECAY_INDEX] = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAST); + } + + holdCamera = true; + bodyDecayLevel = 1; + break; + case DEATH_SCREAM: + holdCamera = true; + bodyDecayLevel = 2; + this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 83.0f; + this->cameraEye.x = GND_BOSSROOM_CENTER_X; + this->cameraEye.y = GND_BOSSROOM_CENTER_Y + 83.0f; + this->cameraEye.z = GND_BOSSROOM_CENTER_Z + 50.0f; + this->cameraAt.x = GND_BOSSROOM_CENTER_X; + this->cameraAt.y = GND_BOSSROOM_CENTER_Y + 103.0f; + this->cameraAt.z = GND_BOSSROOM_CENTER_Z; + if (this->timers[0] == 0) { + this->deathState = DEATH_DISINTEGRATE; + Animation_MorphToPlayOnce(&this->skelAnime, &gPhantomGanonLastPoseAnim, -10.0f); + this->work[GND_BODY_DECAY_INDEX] = 0; + this->timers[0] = 40; + } + break; + case DEATH_DISINTEGRATE: + holdCamera = true; + bodyDecayLevel = 3; + Math_ApproachZeroF(&this->cameraEye.y, 0.05f, 1.0f); // approaches GND_BOSSROOM_CENTER_Y + 33.0f + Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 170.0f, 0.05f, 2.0f); + Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 53.0f, 0.05f, 1.0f); + if (this->timers[0] == 0) { + this->timers[0] = 250; + this->deathState = DEATH_FINISH; + } + break; + case DEATH_FINISH: + holdCamera = true; + bodyDecayLevel = 10; + if (this->timers[0] == 150) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, GND_BOSSROOM_CENTER_X, + GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z, 0, 0, 0, WARP_DUNGEON_ADULT); + } + + Math_ApproachZeroF(&this->cameraEye.y, 0.05f, 1.0f); // GND_BOSSROOM_CENTER_Y + 33.0f + Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 170.0f, 0.05f, 2.0f); + Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 53.0f, 0.05f, 1.0f); + if (this->timers[0] == 0) { + EnfHG* horse = (EnfHG*)this->actor.child; + + camera->eye = this->cameraEye; + camera->eyeNext = this->cameraEye; + camera->at = this->cameraAt; + func_800C08AC(globalCtx, this->deathCamera, 0); + this->deathCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, GND_BOSSROOM_CENTER_X, + GND_BOSSROOM_CENTER_Y, GND_BOSSROOM_CENTER_Z + 200.0f, 0, 0, 0, 0); + this->actor.child = &horse->actor; + this->killActor = true; + horse->killActor = true; + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + Flags_SetSwitch(globalCtx, 0x22); + } + break; + } + + if (bodyDecayLevel) { + Vec3f pos; + Vec3f vel = { 0.0f, 0.0f, 0.0f }; + Vec3f accelKFire = { 0.0f, 0.0f, 0.0f }; + Vec3f accelHahen = { 0.0f, -0.5f, 0.0f }; + s16 limbDecayIndex; + s16 i; + + vel.x = this->actor.world.pos.x - this->actor.prevPos.x; + vel.z = this->actor.world.pos.z - this->actor.prevPos.z; + if (bodyDecayLevel < 10) { + if (this->work[GND_DEATH_ENV_TIMER] == 0) { + if (globalCtx->envCtx.unk_BF == 0) { + globalCtx->envCtx.unk_BF = 3; + this->work[GND_DEATH_ENV_TIMER] = (s16)Rand_ZeroFloat(5.0f) + 4.0f; + globalCtx->envCtx.unk_D6 = 0x28; + } else { + globalCtx->envCtx.unk_BF = 0; + this->work[GND_DEATH_ENV_TIMER] = (s16)Rand_ZeroFloat(2.0f) + 2.0f; + globalCtx->envCtx.unk_D6 = 0x14; + } + } else { + this->work[GND_DEATH_ENV_TIMER]--; + } + + for (i = 0; i <= 0; i++) { + limbDecayIndex = this->work[GND_LIMB_DECAY_INDEX]; + this->work[GND_LIMB_DECAY_INDEX]++; + this->work[GND_LIMB_DECAY_INDEX] %= 25; + pos.x = this->bodyPartsPos[limbDecayIndex].x + Rand_CenteredFloat(5.0f); + pos.y = this->bodyPartsPos[limbDecayIndex].y + Rand_CenteredFloat(5.0f); + pos.z = this->bodyPartsPos[limbDecayIndex].z + Rand_CenteredFloat(5.0f); + accelKFire.y = 0.0f; + + if (bodyDecayLevel == 3) { + accelKFire.y = -0.2f; + accelKFire.x = (GND_BOSSROOM_CENTER_X - pos.x) * 0.002f; + accelKFire.z = (GND_BOSSROOM_CENTER_Z - pos.z) * 0.002f; + accelHahen.x = (GND_BOSSROOM_CENTER_X - pos.x) * 0.001f; + accelHahen.y = -1.0f; + accelHahen.z = (GND_BOSSROOM_CENTER_Z - pos.z) * 0.001f; + } + + EffectSsKFire_Spawn(globalCtx, &pos, &vel, &accelKFire, (s16)Rand_ZeroFloat(20.0f) + 15, + bodyDecayLevel); + if ((Rand_ZeroOne() < 0.5f) || (bodyDecayLevel == 3)) { + EffectSsHahen_Spawn(globalCtx, &pos, &vel, &accelHahen, 0, (s16)Rand_ZeroFloat(4.0f) + 7, + HAHEN_OBJECT_DEFAULT, 10, NULL); + } + } + } else { + globalCtx->envCtx.unk_BF = 0; + globalCtx->envCtx.unk_D6 = 0x14; + } + + this->work[GND_BODY_DECAY_FLAG] = true; + for (i = 0; i < 5; i++) { + if (bodyDecayLevel == 1) { + BossGanondrof_ClearPixels(sDecayMaskLow, this->work[GND_BODY_DECAY_INDEX]); + } else if (bodyDecayLevel == 2) { + BossGanondrof_ClearPixels(sDecayMaskHigh, this->work[GND_BODY_DECAY_INDEX]); + } else { + BossGanondrof_ClearPixels(sDecayMaskTotal, this->work[GND_BODY_DECAY_INDEX]); + } + + if (this->work[GND_BODY_DECAY_INDEX] < 0xFF) { + this->work[GND_BODY_DECAY_INDEX]++; + } + } + } + + if (this->deathCamera != 0) { + if (!holdCamera) { + Math_ApproachF(&this->cameraEye.x, this->cameraNextEye.x, this->cameraEyeMaxVel.x, + this->cameraEyeVel.x * this->cameraSpeedMod); + Math_ApproachF(&this->cameraEye.y, this->cameraNextEye.y, this->cameraEyeMaxVel.y, + this->cameraEyeVel.y * this->cameraSpeedMod); + Math_ApproachF(&this->cameraEye.z, this->cameraNextEye.z, this->cameraEyeMaxVel.z, + this->cameraEyeVel.z * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.x, this->cameraNextAt.x, this->cameraAtMaxVel.x, + this->cameraAtVel.x * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.y, this->cameraNextAt.y, this->cameraAtMaxVel.y, + this->cameraAtVel.y * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.z, this->cameraNextAt.z, this->cameraAtMaxVel.z, + this->cameraAtVel.z * this->cameraSpeedMod); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, this->cameraAccel); + } + + Gameplay_CameraSetAtEye(globalCtx, this->deathCamera, &this->cameraAt, &this->cameraEye); + } +} + +void BossGanondrof_CollisionCheck(BossGanondrof* this, GlobalContext* globalCtx) { + s32 acHit; + EnfHG* horse = (EnfHG*)this->actor.child; + ColliderInfo* hurtbox; + + if (this->work[GND_INVINC_TIMER] != 0) { + this->work[GND_INVINC_TIMER]--; + this->returnCount = 0; + this->colliderBody.base.acFlags &= ~AC_HIT; + } else { + acHit = this->colliderBody.base.acFlags & AC_HIT; + if ((acHit && ((s8)this->actor.colChkInfo.health > 0)) || (this->returnCount != 0)) { + if (acHit) { + this->colliderBody.base.acFlags &= ~AC_HIT; + hurtbox = this->colliderBody.info.acHitInfo; + } + if (this->flyMode != GND_FLY_PAINTING) { + if (acHit && (this->actionFunc != BossGanondrof_Stunned) && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_GROUND - SFX_FLAG); + osSyncPrintf("hit != 0 \n"); + } else if (this->actionFunc != BossGanondrof_Charge) { + if (this->returnCount == 0) { + u8 dmg; + u8 canKill = false; + s32 dmgFlags = hurtbox->toucher.dmgFlags; + + if (dmgFlags & 0x80) { + return; + } + dmg = CollisionCheck_GetSwordDamage(dmgFlags); + (dmg == 0) ? (dmg = 2) : (canKill = true); + if (((s8)this->actor.colChkInfo.health > 2) || canKill) { + this->actor.colChkInfo.health -= dmg; + } + + if ((s8)this->actor.colChkInfo.health <= 0 || 1) { + BossGanondrof_SetupDeath(this, globalCtx); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + return; + } + } + BossGanondrof_SetupStunned(this, globalCtx); + if (this->returnCount >= 2) { + this->timers[0] = 120; + } + this->work[GND_INVINC_TIMER] = 10; + horse->hitTimer = 20; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_GROUND - SFX_FLAG); + } + } else if (acHit && (hurtbox->toucher.dmgFlags & 0x0001F8A4)) { + this->work[GND_INVINC_TIMER] = 10; + this->actor.colChkInfo.health -= 2; + horse->hitTimer = 20; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_DAMAGE); + } + this->returnCount = 0; + } + } +} + +void BossGanondrof_Update(Actor* thisx, GlobalContext* globalCtx) { + f32 cs; + f32 sn; + f32 legRotTargetY; + f32 legRotTargetZ; + f32 legSplitTarget; + s32 pad2; + s16 i; + s32 pad; + BossGanondrof* this = (BossGanondrof*)thisx; + EnfHG* horse; + + osSyncPrintf("MOVE START %d\n", this->actor.params); + this->actor.flags &= ~ACTOR_FLAG_10; + this->colliderBody.base.colType = COLTYPE_HIT3; + if (this->killActor) { + Actor_Kill(&this->actor); + return; + } + this->work[GND_VARIANCE_TIMER]++; + horse = (EnfHG*)this->actor.child; + osSyncPrintf("MOVE START EEEEEEEEEEEEEEEEEEEEEE%d\n", this->actor.params); + + this->actionFunc(this, globalCtx); + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i]) { + this->timers[i]--; + } + } + if (this->work[GND_UNKTIMER_1]) { + this->work[GND_UNKTIMER_1]--; + } + if (this->work[GND_UNKTIMER_2]) { + this->work[GND_UNKTIMER_2]--; + } + + if (this->actionFunc != BossGanondrof_Death) { + BossGanondrof_CollisionCheck(this, globalCtx); + } + + osSyncPrintf("MOVE END\n"); + BossGanondrof_SetColliderPos(&this->targetPos, &this->colliderBody); + BossGanondrof_SetColliderPos(&this->spearTip, &this->colliderSpear); + if ((this->flyMode == GND_FLY_PAINTING) && !horse->bossGndInPainting) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + if ((this->actionFunc == BossGanondrof_Stunned) && (this->timers[0] > 1)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } else if (this->actionFunc == BossGanondrof_Block) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } else if (this->actionFunc == BossGanondrof_Charge) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSpear.base); + } + + this->actor.focus.pos = this->targetPos; + + sn = Math_SinS(-this->actor.shape.rot.y); + cs = Math_CosS(-this->actor.shape.rot.y); + legRotTargetY = ((sn * this->actor.velocity.z) + (cs * this->actor.velocity.x)) * 300.0f; + legRotTargetZ = ((-sn * this->actor.velocity.x) + (cs * this->actor.velocity.z)) * 300.0f; + Math_ApproachF(&this->legRotY, legRotTargetY, 1.0f, 600.0f); + Math_ApproachF(&this->legRotZ, legRotTargetZ, 1.0f, 600.0f); + if ((this->flyMode != GND_FLY_PAINTING) && (this->actionFunc != BossGanondrof_Stunned) && + (this->deathState == NOT_DEAD)) { + legSplitTarget = (Math_SinS(this->work[GND_VARIANCE_TIMER] * 0x8DC) * -500.0f) - 500.0f; + } else { + legSplitTarget = 0.0f; + } + + Math_ApproachF(&this->legSplitY, legSplitTarget, 1.0f, 100.0f); + if (this->shockTimer != 0) { + s16 j; + + this->shockTimer--; + osSyncPrintf("F 1\n"); + for (j = 0; j < 7; j++) { + osSyncPrintf("F 15\n"); + EffectSsFhgFlash_SpawnShock(globalCtx, &this->actor, &this->actor.world.pos, 45, FHGFLASH_SHOCK_PG); + } + osSyncPrintf("F 2\n"); + } + + if (this->actor.params == GND_REAL_BOSS) { + Lights_PointNoGlowSetInfo(&this->lightInfo, this->spearTip.x, this->spearTip.y, this->spearTip.z, 255, 255, 255, + 200); + } +} + +s32 BossGanondrof_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossGanondrof* this = (BossGanondrof*)thisx; + + switch (limbIndex) { + case 15: + if ((this->actionFunc == BossGanondrof_Intro) && this->work[GND_MASK_OFF]) { + *dList = gPhantomGanonFaceDL; + } + rot->y += this->rideRotY[limbIndex]; + rot->z += this->rideRotZ[limbIndex]; + break; + + case 19: + rot->y += this->legRotY + this->legSplitY; + rot->z += this->legRotZ; + break; + + case 20: + rot->y += this->legRotY + this->legSplitY; + rot->z += this->legRotZ; + break; + + case 21: + rot->y += this->legRotY + this->legSplitY; + rot->z += this->legRotZ; + break; + + case 23: + rot->y += this->legRotY - this->legSplitY; + rot->z += this->legRotZ; + break; + + case 24: + rot->y += this->legRotY - this->legSplitY; + rot->z += this->legRotZ; + break; + + case 25: + rot->y += this->legRotY - this->legSplitY; + rot->z += this->legRotZ; + break; + + case 5: + case 6: + case 7: + rot->y += this->armRotY; + rot->z += this->armRotZ; + break; + + case 8: + case 9: + case 10: + rot->y += this->armRotY; + rot->z += this->armRotZ; + break; + + case 13: + if (this->deathState != NOT_DEAD) { + *dList = NULL; + } + default: + rot->y += this->rideRotY[limbIndex]; + rot->z += this->rideRotZ[limbIndex]; + break; + } + + return 0; +} + +void BossGanondrof_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + static Vec3f spearVec = { 0.0f, 0.0f, 6000.0f }; + + BossGanondrof* this = (BossGanondrof*)thisx; + + if (limbIndex == 14) { + Matrix_MultVec3f(&zeroVec, &this->targetPos); + } else if (limbIndex == 13) { + Matrix_MultVec3f(&spearVec, &this->spearTip); + } + + if (((this->flyMode != GND_FLY_PAINTING) || (this->actionFunc == BossGanondrof_Intro)) && (limbIndex <= 25)) { + Matrix_MultVec3f(&zeroVec, &this->bodyPartsPos[limbIndex - 1]); + } +} + +Gfx* BossGanondrof_GetClearPixelDList(GraphicsContext* gfxCtx) { + Gfx* dList = (Gfx*)Graph_Alloc(gfxCtx, sizeof(Gfx) * 4); + Gfx* dListHead = dList; + + gDPPipeSync(dListHead++); + gDPSetRenderMode(dListHead++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_TEX_EDGE2); + gSPClearGeometryMode(dListHead++, G_CULL_BACK); + gSPEndDisplayList(dListHead++); + + return dList; +} + +Gfx* BossGanondrof_GetNullDList(GraphicsContext* gfxCtx) { + Gfx* dList = (Gfx*)Graph_Alloc(gfxCtx, sizeof(Gfx) * 1); + Gfx* dListHead = dList; + + gSPEndDisplayList(dListHead++); + return dList; +} + +void BossGanondrof_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossGanondrof* this = (BossGanondrof*)thisx; + EnfHG* horse; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganondrof.c", 3716); + osSyncPrintf("MOVE P = %x\n", this->actor.update); + osSyncPrintf("STOP TIMER = %d ==============\n", this->actor.freezeTimer); + horse = (EnfHG*)this->actor.child; + if (this->flyMode == GND_FLY_PAINTING) { + Matrix_RotateY((horse->turnRot * 3.1416f) / (f32)0x8000, MTXMODE_APPLY); + } + + osSyncPrintf("YP %f\n", this->actor.world.pos.y); + func_80093D18(globalCtx->state.gfxCtx); + if (this->work[GND_INVINC_TIMER] & 4) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099); + } else { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, (u32)horse->warpColorFilterR, (u32)horse->warpColorFilterG, + (u32)horse->warpColorFilterB, 0, (s32)horse->warpColorFilterUnk1 + 995, + (s32)horse->warpColorFilterUnk2 + 1000); + } + + osSyncPrintf("DRAW 11\n"); + osSyncPrintf("EYE_COL %d\n", (s16)this->fwork[GND_EYE_BRIGHTNESS]); + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->fwork[GND_EYE_BRIGHTNESS], (s16)this->fwork[GND_EYE_BRIGHTNESS], + (s16)this->fwork[GND_EYE_BRIGHTNESS], (s16)this->fwork[GND_EYE_ALPHA]); + if (this->work[GND_BODY_DECAY_FLAG]) { + gSPSegment(POLY_OPA_DISP++, 0x08, BossGanondrof_GetClearPixelDList(globalCtx->state.gfxCtx)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, BossGanondrof_GetNullDList(globalCtx->state.gfxCtx)); + } + + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, BossGanondrof_OverrideLimbDraw, + BossGanondrof_PostLimbDraw, this); + osSyncPrintf("DRAW 22\n"); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_ganondrof.c", 3814); + osSyncPrintf("DRAW END %d\n", this->actor.params); +} diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h new file mode 100644 index 000000000..8b0616bac --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h @@ -0,0 +1,104 @@ +#ifndef Z_BOSS_GANONDROF_H +#define Z_BOSS_GANONDROF_H + +#include "ultra64.h" +#include "global.h" + +struct BossGanondrof; + +typedef void (*BossGanondrofActionFunc)(struct BossGanondrof*, GlobalContext*); + +#define GND_REAL_BOSS 1 +#define GND_FAKE_BOSS 10 + +#define GND_BOSSROOM_CENTER_X 14.0f +#define GND_BOSSROOM_CENTER_Y -33.0f +#define GND_BOSSROOM_CENTER_Z -3315.0f + +typedef enum { + /* 0 */ GND_FLY_PAINTING, + /* 1 */ GND_FLY_NEUTRAL, + /* 2 */ GND_FLY_VOLLEY, + /* 3 */ GND_FLY_RETURN, + /* 4 */ GND_FLY_CHARGE +} BossGanondrofFlyMode; + +typedef enum { + /* 0 */ GND_EYESTATE_NONE, + /* 1 */ GND_EYESTATE_FADE, + /* 2 */ GND_EYESTATE_BRIGHTEN +} BossGanondrofEyeState; + +typedef enum { + /* 0 */ GND_VARIANCE_TIMER, + /* 1 */ GND_US_1, + /* 2 */ GND_US_2, + /* 3 */ GND_US_3, + /* 4 */ GND_UNKTIMER_1, + /* 5 */ GND_UNKTIMER_2, + /* 6 */ GND_INVINC_TIMER, + /* 7 */ GND_ACTION_STATE, + /* 8 */ GND_THROW_FRAME, + /* 9 */ GND_THROW_COUNT, + /* 10 */ GND_MASK_OFF, + /* 11 */ GND_EYE_STATE, + /* 12 */ GND_PARTICLE_ANGLE, + /* 13 */ GND_BODY_DECAY_INDEX, + /* 14 */ GND_BODY_DECAY_FLAG, + /* 15 */ GND_LIMB_DECAY_INDEX, + /* 16 */ GND_DEATH_ENV_TIMER, + /* 17 */ GND_DEATH_SFX_TIMER, + /* 20 */ GND_SHORT_COUNT = 20 +} BossGanondrofS16Var; + +typedef enum { + /* 0 */ GND_FLOAT_SPEED, + /* 1 */ GND_END_FRAME, + /* 2 */ GND_EYE_BRIGHTNESS, + /* 3 */ GND_CAMERA_ZOOM, + /* 4 */ GND_CAMERA_ANGLE, + /* 5 */ GND_EYE_ALPHA, + /* 13 */ GND_FLOAT_COUNT = 13 +} BossGanondrofF32Var; + +typedef struct BossGanondrof { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ BossGanondrofActionFunc actionFunc; + /* 0x0194 */ s16 work[GND_SHORT_COUNT]; + /* 0x01BC */ s16 timers[5]; + /* 0x01C6 */ u8 killActor; + /* 0x01C7 */ u8 returnCount; + /* 0x01C8 */ u8 shockTimer; + /* 0x01C9 */ u8 flyMode; + /* 0x01CA */ u8 returnSuccess; + /* 0x01CC */ f32 fwork[GND_FLOAT_COUNT]; + /* 0x0200 */ Vec3f spearTip; + /* 0x020C */ Vec3f targetPos; + /* 0x0218 */ Vec3f bodyPartsPos[27]; // only 25 used + /* 0x035C */ s16 deathCamera; + /* 0x035E */ s16 deathState; + /* 0x0360 */ Vec3f cameraEye; + /* 0x036C */ Vec3f cameraAt; + /* 0x0378 */ Vec3f cameraEyeVel; + /* 0x0384 */ Vec3f cameraAtVel; + /* 0x0390 */ Vec3f cameraNextEye; + /* 0x039C */ Vec3f cameraEyeMaxVel; + /* 0x03A8 */ Vec3f cameraNextAt; + /* 0x03B4 */ Vec3f cameraAtMaxVel; + /* 0x03C0 */ f32 cameraSpeedMod; + /* 0x03C4 */ f32 cameraAccel; + /* 0x03C8 */ f32 legRotY; + /* 0x03CC */ f32 legRotZ; + /* 0x03D0 */ f32 legSplitY; + /* 0x03D4 */ f32 armRotY; + /* 0x03D8 */ f32 armRotZ; + /* 0x03DC */ f32 rideRotZ[30]; // possibly only 25 used + /* 0x0454 */ f32 rideRotY[30]; // possibly only 25 used + /* 0x04CC */ LightNode* lightNode; + /* 0x04D0 */ LightInfo lightInfo; + /* 0x04E0 */ ColliderCylinder colliderBody; + /* 0x052C */ ColliderCylinder colliderSpear; +} BossGanondrof; // size = 0x0578 + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c new file mode 100644 index 000000000..ad8e4537d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -0,0 +1,2158 @@ +#include "z_boss_goma.h" +#include "objects/object_goma/object_goma.h" +#include "overlays/actors/ovl_En_Goma/z_en_goma.h" +#include "overlays/actors/ovl_Door_Shutter/z_door_shutter.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +// IRIS_FOLLOW: gohma looks towards the player (iris rotation) +// BONUS_IFRAMES: gain invincibility frames when the player does something (throwing things?), or +// randomly (see BossGoma_UpdateEye) +typedef enum { + EYESTATE_IRIS_FOLLOW_BONUS_IFRAMES, // default, allows not drawing lens and iris when eye is closed + EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES, + EYESTATE_IRIS_FOLLOW_NO_IFRAMES +} GohmaEyeState; + +typedef enum { + VISUALSTATE_RED, // main/eye: red + VISUALSTATE_DEFAULT, // main: greenish cyan, blinks with dark gray every 16 frames; eye: white + VISUALSTATE_DEFEATED, // main/eye: dark gray + VISUALSTATE_STUNNED = 4, // main: greenish cyan, alternates with blue; eye: greenish cyan + VISUALSTATE_HIT // main: greenish cyan, alternates with red; eye: greenish cyan +} GohmaVisualState; + +void BossGoma_Init(Actor* thisx, GlobalContext* globalCtx); +void BossGoma_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossGoma_Update(Actor* thisx, GlobalContext* globalCtx); +void BossGoma_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BossGoma_SetupEncounter(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_Encounter(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_Defeated(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorAttackPosture(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorPrepareAttack(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorAttack(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorDamaged(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorLandStruckDown(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorLand(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorStunned(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FallJump(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FallStruckDown(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_CeilingSpawnGohmas(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_CeilingPrepareSpawnGohmas(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorIdle(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_CeilingIdle(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_FloorMain(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_WallClimb(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_CeilingMoveToCenter(BossGoma* this, GlobalContext* globalCtx); +void BossGoma_SpawnChildGohma(BossGoma* this, GlobalContext* globalCtx, s16 i); + +const ActorInit Boss_Goma_InitVars = { + ACTOR_BOSS_GOMA, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GOMA, + sizeof(BossGoma), + (ActorFunc)BossGoma_Init, + (ActorFunc)BossGoma_Destroy, + (ActorFunc)BossGoma_Update, + (ActorFunc)BossGoma_Draw, + NULL, +}; + +static ColliderJntSphElementInit sColliderJntSphElementInit[13] = { + { + { + ELEMTYPE_UNK3, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_EYE, { { 0, 0, 1200 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_TAIL4, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_TAIL3, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_TAIL2, { { 0, 0, 0 }, 12 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_TAIL1, { { 0, 0, 0 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_R_FEET, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_R_SHIN, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_R_THIGH_SHELL, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_L_ANTENNA_CLAW, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_R_ANTENNA_CLAW, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_L_FEET, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_L_SHIN, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK2, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { BOSSGOMA_LIMB_L_THIGH_SHELL, { { 0, 0, 0 }, 15 }, 100 }, + }, +}; + +static ColliderJntSphInit sColliderJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 13, + sColliderJntSphElementInit, +}; + +static u8 sClearPixelTableFirstPass[16 * 16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 +}; + +static u8 sClearPixelTableSecondPass[16 * 16] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +// indexed by limb (where the root limb is 1) +static u8 sDeadLimbLifetime[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 30, // tail end/last part + 40, // tail 2nd to last part + 0, 0, 0, 0, 0, 0, 0, 0, + 10, // back of right claw/hand + 15, // front of right claw/hand + 21, // part of right arm (inner) + 0, 0, + 25, // part of right arm (shell) + 0, 0, + 31, // part of right arm (shell on shoulder) + 35, // part of right arm (shoulder) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 43, // end of left antenna + 48, // middle of left antenna + 53, // start of left antenna + 0, 0, 0, 0, + 42, // end of right antenna + 45, // middle of right antenna + 53, // start of right antenna + 0, 0, 0, 0, 0, 0, + 11, // back of left claw/hand + 15, // front of left claw/hand + 21, // part of left arm (inner) + 0, 0, + 25, // part of left arm (shell) + 0, 0, + 30, // part of left arm (shell on shoulder) + 35, // part of left arm (shoulder) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/** + * Sets the `i`th pixel of a 16x16 RGBA16 image to 0 (transparent black) + * according to the `clearPixelTable` + */ +void BossGoma_ClearPixels16x16Rgba16(s16* rgba16image, u8* clearPixelTable, s16 i) +{ + rgba16image = ResourceMgr_LoadTexByName(rgba16image); + if (clearPixelTable[i]) { + rgba16image[i] = 0; + } +} + +/** + * Sets the `i`th 2x2 pixels block of a 32x32 RGBA16 image to 0 (transparent black) + * according to the `clearPixelTable` + */ +void BossGoma_ClearPixels32x32Rgba16(s16* rgba16image, u8* clearPixelTable, s16 i) { + s16* targetPixel; + + rgba16image = ResourceMgr_LoadTexByName(rgba16image); + + if (clearPixelTable[i]) { + // address of the top left pixel in a 2x2 pixels block located at + // (i & 0xF, i >> 4) in a 16x16 grid of 2x2 pixels + targetPixel = (s32)rgba16image + (s16)((i & 0xF) * 2 + (i & 0xF0) * 4) * 2; + // set the 2x2 block of pixels to 0 + targetPixel[0] = 0; + targetPixel[1] = 0; + targetPixel[32 + 0] = 0; + targetPixel[32 + 1] = 0; + } +} + +/** + * Clear pixels from Gohma's textures + */ +void BossGoma_ClearPixels(u8* clearPixelTable, s16 i) { + BossGoma_ClearPixels16x16Rgba16(SEGMENTED_TO_VIRTUAL(gGohmaBodyTex), clearPixelTable, i); + BossGoma_ClearPixels16x16Rgba16(SEGMENTED_TO_VIRTUAL(gGohmaShellUndersideTex), clearPixelTable, i); + BossGoma_ClearPixels16x16Rgba16(SEGMENTED_TO_VIRTUAL(gGohmaDarkShellTex), clearPixelTable, i); + BossGoma_ClearPixels16x16Rgba16(SEGMENTED_TO_VIRTUAL(gGohmaEyeTex), clearPixelTable, i); + + BossGoma_ClearPixels32x32Rgba16(SEGMENTED_TO_VIRTUAL(gGohmaShellTex), clearPixelTable, i); + BossGoma_ClearPixels32x32Rgba16(SEGMENTED_TO_VIRTUAL(gGohmaIrisTex), clearPixelTable, i); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x01, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -2000, ICHAIN_STOP), +}; + +void BossGoma_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossGoma* this = (BossGoma*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 4000.0f, ActorShadow_DrawCircle, 150.0f); + SkelAnime_Init(globalCtx, &this->skelanime, &gGohmaSkel, &gGohmaIdleCrouchedAnim, NULL, NULL, 0); + Animation_PlayLoop(&this->skelanime, &gGohmaIdleCrouchedAnim); + this->actor.shape.rot.x = -0x8000; // upside-down + this->eyeIrisScaleX = 1.0f; + this->eyeIrisScaleY = 1.0f; + this->unusedInitX = this->actor.world.pos.x; + this->unusedInitZ = this->actor.world.pos.z; + this->actor.world.pos.y = -300.0f; // ceiling + this->actor.gravity = 0.0f; + BossGoma_SetupEncounter(this, globalCtx); + this->actor.colChkInfo.health = 10; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderJntSphInit, this->colliderItems); + + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + Actor_Kill(&this->actor); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 0.0f, -640.0f, 0.0f, 0, 0, + 0, WARP_DUNGEON_CHILD); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, 141.0f, -640.0f, -84.0f, 0, 0, 0, 0); + } +} + +void BossGoma_PlayEffectsAndSfx(BossGoma* this, GlobalContext* globalCtx, s16 arg2, s16 amountMinus1) { + if (arg2 == 0 || arg2 == 1 || arg2 == 3) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightHandBackLimbWorldPos, 25.0f, amountMinus1, 8.0f, + 500, 10, 1); + } + + if (arg2 == 0 || arg2 == 2 || arg2 == 3) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftHandBackLimbWorldPos, 25.0f, amountMinus1, 8.0f, + 500, 10, 1); + } + + if (arg2 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DOWN); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_WALK); + } +} + +void BossGoma_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BossGoma* this = (BossGoma*)thisx; + + SkelAnime_Free(&this->skelanime, globalCtx); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +/** + * When Gohma is hit and its health drops to 0 + */ +void BossGoma_SetupDefeated(BossGoma* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelanime, &gGohmaDeathAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaDeathAnim), + ANIMMODE_ONCE, -2.0f); + this->actionFunc = BossGoma_Defeated; + this->disableGameplayLogic = true; + this->decayingProgress = 0; + this->noBackfaceCulling = false; + this->framesUntilNextAction = 1200; + this->actionState = 0; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actor.speedXZ = 0.0f; + this->actor.shape.shadowScale = 0.0f; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DEAD); +} + +/** + * Initial action setup, with Gohma waiting on the ceiling for the fight to start. + */ +void BossGoma_SetupEncounter(BossGoma* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gGohmaWalkAnim); + + Animation_Change(&this->skelanime, &gGohmaWalkAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP, -15.0f); + this->actionFunc = BossGoma_Encounter; + this->actionState = 0; + this->disableGameplayLogic = true; + globalCtx->envCtx.unk_BF = 4; + globalCtx->envCtx.unk_D6 = 0xFF; +} + +/** + * On the floor and not doing anything for 20-30 frames, before going back to BossGoma_FloorMain + */ +void BossGoma_SetupFloorIdle(BossGoma* this) { + f32 lastFrame = Animation_GetLastFrame(&gGohmaIdleCrouchedAnim); + + this->framesUntilNextAction = Rand_S16Offset(20, 30); + Animation_Change(&this->skelanime, &gGohmaIdleCrouchedAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP, -5.0f); + this->actionFunc = BossGoma_FloorIdle; +} + +/** + * On the ceiling and not doing anything for 20-30 frames, leads to spawning children gohmas + */ +void BossGoma_SetupCeilingIdle(BossGoma* this) { + this->framesUntilNextAction = Rand_S16Offset(20, 30); + Animation_Change(&this->skelanime, &gGohmaHangAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaHangAnim), + ANIMMODE_LOOP, -5.0f); + this->actionFunc = BossGoma_CeilingIdle; +} + +/** + * When the player killed all children gohmas + */ +void BossGoma_SetupFallJump(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaLandAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossGoma_FallJump; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -2.0f; +} + +/** + * When the player successfully hits Gohma on the ceiling + */ +void BossGoma_SetupFallStruckDown(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaCrashAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, -5.0f); + this->actionFunc = BossGoma_FallStruckDown; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -2.0f; +} + +void BossGoma_SetupCeilingSpawnGohmas(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaLayEggsAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaLayEggsAnim), + ANIMMODE_LOOP, -15.0f); + this->actionFunc = BossGoma_CeilingSpawnGohmas; + this->spawnGohmasActionTimer = 0; +} + +void BossGoma_SetupCeilingPrepareSpawnGohmas(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaPrepareEggsAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaPrepareEggsAnim), ANIMMODE_LOOP, -10.0f); + this->actionFunc = BossGoma_CeilingPrepareSpawnGohmas; + this->framesUntilNextAction = 70; +} + +void BossGoma_SetupWallClimb(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaClimbAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaClimbAnim), + ANIMMODE_LOOP, -10.0f); + this->actionFunc = BossGoma_WallClimb; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; +} + +/** + * Gohma either reached the ceiling after climbing a wall, or is waiting for the player to kill the (children) Gohmas. + */ +void BossGoma_SetupCeilingMoveToCenter(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaWalkAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaWalkAnim), + ANIMMODE_LOOP, -5.0f); + this->actionFunc = BossGoma_CeilingMoveToCenter; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + this->framesUntilNextAction = Rand_S16Offset(30, 60); +} + +/** + * Root action when on the floor, leads to attacking or climbing. + */ +void BossGoma_SetupFloorMain(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaWalkCrouchedAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaWalkCrouchedAnim), ANIMMODE_LOOP, -5.0f); + this->actionFunc = BossGoma_FloorMain; + this->framesUntilNextAction = Rand_S16Offset(70, 110); +} + +/** + * Gohma jumped to the floor on its own, after the player has killed its children Gohmas. + */ +void BossGoma_SetupFloorLand(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaLandAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaLandAnim), + ANIMMODE_ONCE, -2.0f); + this->actionFunc = BossGoma_FloorLand; + this->currentAnimFrameCount = Animation_GetLastFrame(&gGohmaLandAnim); +} + +/** + * Gohma was shot by the player down from the ceiling. + */ +void BossGoma_SetupFloorLandStruckDown(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaCrashAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaCrashAnim), + ANIMMODE_ONCE, -2.0f); + this->currentAnimFrameCount = Animation_GetLastFrame(&gGohmaCrashAnim); + this->actionFunc = BossGoma_FloorLandStruckDown; + this->currentAnimFrameCount = Animation_GetLastFrame(&gGohmaCrashAnim); +} + +/** + * Gohma is vulnerable, from being struck down from the ceiling or on the ground. + */ +void BossGoma_SetupFloorStunned(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaStunnedAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaStunnedAnim), + ANIMMODE_LOOP, -2.0f); + this->actionFunc = BossGoma_FloorStunned; +} + +/** + * Take an attack posture, when the player is close enough. + */ +void BossGoma_SetupFloorAttackPosture(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaPrepareAttackAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaPrepareAttackAnim), ANIMMODE_ONCE, -10.0f); + this->actionFunc = BossGoma_FloorAttackPosture; +} + +/** + * Leads to BossGoma_FloorAttack after 1 frame + */ +void BossGoma_SetupFloorPrepareAttack(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaStandAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaStandAnim), + ANIMMODE_LOOP, -10.0f); + this->actionFunc = BossGoma_FloorPrepareAttack; + this->framesUntilNextAction = 0; +} + +void BossGoma_SetupFloorAttack(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaAttackAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaAttackAnim), + ANIMMODE_ONCE, -10.0f); + this->actionFunc = BossGoma_FloorAttack; + this->actionState = 0; + this->framesUntilNextAction = 0; +} + +/** + * Plays an animation for Gohma being hit (while stunned) + * The setup and the action preserve timers apart from the patience one, notably `framesUntilNextAction` which is used + * as the stun duration + */ +void BossGoma_SetupFloorDamaged(BossGoma* this) { + Animation_Change(&this->skelanime, &gGohmaDamageAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaDamageAnim), + ANIMMODE_ONCE, -2.0f); + this->actionFunc = BossGoma_FloorDamaged; +} + +void BossGoma_UpdateCeilingMovement(BossGoma* this, GlobalContext* globalCtx, f32 dz, f32 targetSpeedXZ, + s16 rotateTowardsCenter) { + static Vec3f velInit = { 0.0f, 0.0f, 0.0f }; + static Vec3f accelInit = { 0.0f, -0.5f, 0.0f }; + static Vec3f roomCenter = { -150.0f, 0.0f, -350.0f }; + Vec3f* basePos = NULL; + s16 i; + Vec3f vel; + Vec3f accel; + Vec3f pos; + + roomCenter.z += dz; // dz is always 0 + SkelAnime_Update(&this->skelanime); + Math_ApproachF(&this->actor.speedXZ, targetSpeedXZ, 0.5f, 2.0f); + + if (rotateTowardsCenter) { + Math_ApproachS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &roomCenter) + 0x8000, 3, + 0x3E8); + } + + if (Animation_OnFrame(&this->skelanime, 9.0f)) { + basePos = &this->rightHandBackLimbWorldPos; + } else if (Animation_OnFrame(&this->skelanime, 1.0f)) { + basePos = &this->leftHandBackLimbWorldPos; + } + + if (basePos != NULL) { + for (i = 0; i < 5; i++) { + vel = velInit; + accel = accelInit; + pos.x = Rand_CenteredFloat(70.0f) + basePos->x; + pos.y = Rand_ZeroFloat(30.0f) + basePos->y; + pos.z = Rand_CenteredFloat(70.0f) + basePos->z; + EffectSsHahen_Spawn(globalCtx, &pos, &vel, &accel, 0, (s16)(Rand_ZeroOne() * 5.0f) + 10, -1, 10, NULL); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_HIGH); + } +} + +void BossGoma_SetupEncounterState4(BossGoma* this, GlobalContext* globalCtx) { + Player* player; + Camera* camera; + + camera = Gameplay_GetCamera(globalCtx, 0); + player = GET_PLAYER(globalCtx); + this->actionState = 4; + this->actor.flags |= ACTOR_FLAG_0; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 1); + this->subCameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, 3); + Gameplay_ChangeCameraStatus(globalCtx, this->subCameraId, 7); + Animation_Change(&this->skelanime, &gGohmaEyeRollAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaEyeRollAnim), + ANIMMODE_ONCE, 0.0f); + this->currentAnimFrameCount = Animation_GetLastFrame(&gGohmaEyeRollAnim); + + // room center (todo: defines for hardcoded positions relative to room center) + this->actor.world.pos.x = -150.0f; + this->actor.world.pos.z = -350.0f; + + // room entrance, towards center + player->actor.world.pos.x = 150.0f; + player->actor.world.pos.z = 300.0f; + + player->actor.world.rot.y = player->actor.shape.rot.y = -0x705C; + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) + 0x8000; + + // room entrance, closer to room center + this->subCameraEye.x = 90.0f; + this->subCameraEye.z = 170.0f; + this->subCameraEye.y = camera->eye.y + 20.0f; + + this->framesUntilNextAction = 50; + + this->subCameraAt.x = this->actor.world.pos.x; + this->subCameraAt.y = this->actor.world.pos.y; + this->subCameraAt.z = this->actor.world.pos.z; + + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); +} + +/** + * Spawns the door once the player entered + * Wait for the player to look at Gohma on the ceiling + * Handles the "meeting Gohma" cutscene, including boss card + * + * Skips the door and look-at-Gohma puzzle if the player already reached the boss card part before + */ +void BossGoma_Encounter(BossGoma* this, GlobalContext* globalCtx) { + Camera* cam; + Player* player = GET_PLAYER(globalCtx); + s32 pad[2]; + + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + + switch (this->actionState) { + case 0: // wait for the player to enter the room + // entrance of the boss room + if (fabsf(player->actor.world.pos.x - 150.0f) < 60.0f && + fabsf(player->actor.world.pos.z - 350.0f) < 60.0f) { + if (gSaveContext.eventChkInf[7] & 1) { + BossGoma_SetupEncounterState4(this, globalCtx); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_SHUTTER, 164.72f, + -480.0f, 397.68002f, 0, -0x705C, 0, 0x180); + } else { + func_8002DF54(globalCtx, &this->actor, 8); + this->actionState = 1; + } + } + break; + + case 1: // player entered the room + func_80064520(globalCtx, &globalCtx->csCtx); + this->subCameraId = Gameplay_CreateSubCamera(globalCtx); + osSyncPrintf("MAKE CAMERA !!! 1 !!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + Gameplay_ChangeCameraStatus(globalCtx, 0, 1); + Gameplay_ChangeCameraStatus(globalCtx, this->subCameraId, 7); + this->actionState = 2; + // ceiling center + this->actor.world.pos.x = -150.0f; + this->actor.world.pos.y = -320.0f; + this->actor.world.pos.z = -350.0f; + // room entrance + player->actor.world.pos.x = 150.0f; + player->actor.world.pos.z = 300.0f; + // near ceiling center + this->subCameraEye.x = -350.0f; + this->subCameraEye.y = -310.0f; + this->subCameraEye.z = -350.0f; + // below room entrance + this->subCameraAt.x = player->actor.world.pos.x; + this->subCameraAt.y = player->actor.world.pos.y - 200.0f + 25.0f; + this->subCameraAt.z = player->actor.world.pos.z; + this->framesUntilNextAction = 50; + this->timer = 80; + this->frameCount = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + // fall-through + case 2: // zoom on player from room center + // room entrance, towards center + player->actor.shape.rot.y = -0x705C; + player->actor.world.pos.x = 150.0f; + player->actor.world.pos.z = 300.0f; + player->actor.world.rot.y = player->actor.shape.rot.y; + player->actor.speedXZ = 0.0f; + + if (this->framesUntilNextAction == 0) { + // (-20, 25, -65) is towards room center + Math_ApproachF(&this->subCameraEye.x, player->actor.world.pos.x - 20.0f, 0.049999997f, + this->subCameraFollowSpeed * 50.0f); + Math_ApproachF(&this->subCameraEye.y, player->actor.world.pos.y + 25.0f, 0.099999994f, + this->subCameraFollowSpeed * 130.0f); + Math_ApproachF(&this->subCameraEye.z, player->actor.world.pos.z - 65.0f, 0.049999997f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraFollowSpeed, 0.29999998f, 1.0f, 0.0050000004f); + if (this->timer == 0) { + Math_ApproachF(&this->subCameraAt.y, player->actor.world.pos.y + 35.0f, 0.099999994f, + this->subCameraFollowSpeed * 30.0f); + } + this->subCameraAt.x = player->actor.world.pos.x; + this->subCameraAt.z = player->actor.world.pos.z; + } + + Gameplay_CameraSetAtEye(globalCtx, 0, &this->subCameraAt, &this->subCameraEye); + + if (this->frameCount == 176) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_SHUTTER, 164.72f, -480.0f, + 397.68002f, 0, -0x705C, 0, SHUTTER_GOHMA_BLOCK << 6); + } + + if (this->frameCount == 176) { + globalCtx->envCtx.unk_BF = 3; + globalCtx->envCtx.unk_D6 = 0xFFFF; + } + + if (this->frameCount == 190) { + func_8002DF54(globalCtx, &this->actor, 2); + } + + if (this->frameCount >= 228) { + cam = Gameplay_GetCamera(globalCtx, 0); + cam->eye = this->subCameraEye; + cam->eyeNext = this->subCameraEye; + cam->at = this->subCameraAt; + func_800C08AC(globalCtx, this->subCameraId, 0); + this->subCameraId = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->actionState = 3; + } + break; + + case 3: // wait for the player to look at Gohma + if (fabsf(this->actor.projectedPos.x) < 150.0f && fabsf(this->actor.projectedPos.y) < 250.0f && + this->actor.projectedPos.z < 800.0f && this->actor.projectedPos.z > 0.0f) { + this->lookedAtFrames++; + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + Math_ApproachS(&this->actor.world.rot.y, + Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) + 0x8000, 2, + 0xBB8); + this->eyeLidBottomRotX = this->eyeLidTopRotX = this->eyeIrisRotX = this->eyeIrisRotY = 0; + } else { + this->lookedAtFrames = 0; + BossGoma_UpdateCeilingMovement(this, globalCtx, 0.0f, -5.0f, true); + } + + if (this->lookedAtFrames > 15) { + BossGoma_SetupEncounterState4(this, globalCtx); + } + break; + + case 4: // focus Gohma on the ceiling + if (Animation_OnFrame(&this->skelanime, 15.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DEMO_EYE); + } + + if (this->framesUntilNextAction <= 40) { + // (22, -25, 45) is towards room entrance + Math_ApproachF(&this->subCameraEye.x, this->actor.world.pos.x + 22.0f, 0.2f, 100.0f); + Math_ApproachF(&this->subCameraEye.y, this->actor.world.pos.y - 25.0f, 0.2f, 100.0f); + Math_ApproachF(&this->subCameraEye.z, this->actor.world.pos.z + 45.0f, 0.2f, 100.0f); + Math_ApproachF(&this->subCameraAt.x, this->actor.world.pos.x, 0.2f, 100.0f); + Math_ApproachF(&this->subCameraAt.y, this->actor.world.pos.y + 5.0f, 0.2f, 100.0f); + Math_ApproachF(&this->subCameraAt.z, this->actor.world.pos.z, 0.2f, 100.0f); + + if (this->framesUntilNextAction == 30) { + globalCtx->envCtx.unk_BF = 4; + } + + if (this->framesUntilNextAction < 20) { + SkelAnime_Update(&this->skelanime); + Math_ApproachF(&this->eyeIrisScaleX, 1.0f, 0.8f, 0.4f); + Math_ApproachF(&this->eyeIrisScaleY, 1.0f, 0.8f, 0.4f); + + if (Animation_OnFrame(&this->skelanime, 36.0f)) { + this->eyeIrisScaleX = 1.8f; + this->eyeIrisScaleY = 1.8f; + } + + if (Animation_OnFrame(&this->skelanime, this->currentAnimFrameCount)) { + this->actionState = 5; + Animation_Change(&this->skelanime, &gGohmaWalkAnim, 2.0f, 0.0f, + Animation_GetLastFrame(&gGohmaWalkAnim), ANIMMODE_LOOP, -5.0f); + this->framesUntilNextAction = 30; + this->subCameraFollowSpeed = 0.0f; + } + } + } + break; + + case 5: // running on the ceiling + // (98, 0, 85) is towards room entrance + Math_ApproachF(&this->subCameraEye.x, this->actor.world.pos.x + 8.0f + 90.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraEye.y, player->actor.world.pos.y, 0.1f, this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraEye.z, this->actor.world.pos.z + 45.0f + 40.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraFollowSpeed, 1.0f, 1.0f, 0.05f); + this->subCameraAt.x = this->actor.world.pos.x; + this->subCameraAt.y = this->actor.world.pos.y; + this->subCameraAt.z = this->actor.world.pos.z; + + if (this->framesUntilNextAction < 0) { + //! @bug ? unreachable, timer is >= 0 + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 2.0f); + } else { + BossGoma_UpdateCeilingMovement(this, globalCtx, 0.0f, -7.5f, false); + } + + if (this->framesUntilNextAction == 0) { + Animation_Change(&this->skelanime, &gGohmaHangAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGohmaHangAnim), + ANIMMODE_LOOP, -5.0f); + } + + if (this->framesUntilNextAction == 0) { + this->actionState = 9; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -2.0f; + Animation_Change(&this->skelanime, &gGohmaInitialLandingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaInitialLandingAnim), ANIMMODE_ONCE, -5.0f); + player->actor.world.pos.x = 0.0f; + player->actor.world.pos.z = -30.0f; + } + break; + + case 9: // falling from the ceiling + Math_ApproachF(&this->subCameraEye.x, this->actor.world.pos.x + 8.0f + 90.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraEye.y, player->actor.world.pos.y + 10.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraEye.z, this->actor.world.pos.z + 45.0f + 40.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + this->subCameraAt.x = this->actor.world.pos.x; + this->subCameraAt.y = this->actor.world.pos.y; + this->subCameraAt.z = this->actor.world.pos.z; + SkelAnime_Update(&this->skelanime); + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + Math_ApproachS(&this->actor.world.rot.y, + Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor), 2, 0x7D0); + + if (this->actor.bgCheckFlags & 1) { + this->actionState = 130; + this->actor.velocity.y = 0.0f; + Animation_Change(&this->skelanime, &gGohmaInitialLandingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaInitialLandingAnim), ANIMMODE_ONCE, -2.0f); + this->currentAnimFrameCount = Animation_GetLastFrame(&gGohmaInitialLandingAnim); + BossGoma_PlayEffectsAndSfx(this, globalCtx, 0, 5); + this->framesUntilNextAction = 15; + func_800A9F6C(0.0f, 0xC8, 0x14, 0x14); + } + break; + + case 130: // focus Gohma on the ground + Math_ApproachF(&this->subCameraEye.x, this->actor.world.pos.x + 8.0f + 90.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraEye.y, player->actor.world.pos.y + 10.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachF(&this->subCameraEye.z, this->actor.world.pos.z + 45.0f + 40.0f, 0.1f, + this->subCameraFollowSpeed * 30.0f); + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + Math_ApproachS(&this->actor.world.rot.y, + Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor), 2, 0x7D0); + SkelAnime_Update(&this->skelanime); + this->subCameraAt.x = this->actor.world.pos.x; + this->subCameraAt.z = this->actor.world.pos.z; + + if (this->framesUntilNextAction != 0) { + f32 s = sinf(this->framesUntilNextAction * 3.1415f * 0.5f); + + this->subCameraAt.y = this->framesUntilNextAction * s * 0.7f + this->actor.world.pos.y; + } else { + Math_ApproachF(&this->subCameraAt.y, this->actor.focus.pos.y, 0.1f, 10.0f); + } + + if (Animation_OnFrame(&this->skelanime, 40.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_CRY1); + + if (!(gSaveContext.eventChkInf[7] & 1)) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gGohmaTitleCardTex), 0xA0, 0xB4, 0x80, 0x28); + } + + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + gSaveContext.eventChkInf[7] |= 1; + } + + if (Animation_OnFrame(&this->skelanime, this->currentAnimFrameCount)) { + this->actionState = 140; + Animation_Change(&this->skelanime, &gGohmaStandAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaStandAnim), ANIMMODE_LOOP, -10.0f); + this->framesUntilNextAction = 20; + } + break; + + case 140: + SkelAnime_Update(&this->skelanime); + Math_ApproachF(&this->subCameraAt.y, this->actor.focus.pos.y, 0.1f, 10.0f); + + if (this->framesUntilNextAction == 0) { + this->framesUntilNextAction = 30; + this->actionState = 150; + Gameplay_ChangeCameraStatus(globalCtx, 0, 3); + } + break; + + case 150: + SkelAnime_Update(&this->skelanime); + Math_SmoothStepToF(&this->subCameraEye.x, this->actor.world.pos.x + 150.0f, 0.2f, 100.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraEye.y, this->actor.world.pos.y + 20.0f, 0.2f, 100.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraEye.z, this->actor.world.pos.z + 220.0f, 0.2f, 100.0f, 0.1f); + + if (this->framesUntilNextAction == 0) { + cam = Gameplay_GetCamera(globalCtx, 0); + cam->eye = this->subCameraEye; + cam->eyeNext = this->subCameraEye; + cam->at = this->subCameraAt; + func_800C08AC(globalCtx, this->subCameraId, 0); + this->subCameraId = 0; + BossGoma_SetupFloorMain(this); + this->disableGameplayLogic = false; + this->patienceTimer = 200; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + } + break; + } + + if (this->subCameraId != 0) { + Gameplay_CameraSetAtEye(globalCtx, this->subCameraId, &this->subCameraAt, &this->subCameraEye); + } +} + +/** + * Handles the "Gohma defeated" cutscene and effects + * Spawns the heart container and blue warp actors + */ +void BossGoma_Defeated(BossGoma* this, GlobalContext* globalCtx) { + static Vec3f roomCenter = { -150.0f, 0.0f, -350.0f }; + f32 dx; + f32 dz; + s16 j; + Vec3f vel1 = { 0.0f, 0.0f, 0.0f }; + Vec3f accel1 = { 0.0f, 1.0f, 0.0f }; + Color_RGBA8 color1 = { 255, 255, 255, 255 }; + Color_RGBA8 color2 = { 0, 100, 255, 255 }; + Vec3f vel2 = { 0.0f, 0.0f, 0.0f }; + Vec3f accel2 = { 0.0f, -0.5f, 0.0f }; + Vec3f pos; + Camera* camera; + Player* player = GET_PLAYER(globalCtx); + Vec3f childPos; + s16 i; + + SkelAnime_Update(&this->skelanime); + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + + if (Animation_OnFrame(&this->skelanime, 107.0f)) { + BossGoma_PlayEffectsAndSfx(this, globalCtx, 0, 8); + func_800A9F6C(0.0f, 0x96, 0x14, 0x14); + } + + this->visualState = VISUALSTATE_DEFEATED; + this->eyeState = EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES; + + if (this->framesUntilNextAction == 1001) { + for (i = 0; i < 90; i++) { + if (sDeadLimbLifetime[i] != 0) { + this->deadLimbsState[i] = 1; + } + } + } + + if (this->framesUntilNextAction < 1200 && this->framesUntilNextAction > 1100 && + this->framesUntilNextAction % 8 == 0) { + EffectSsSibuki_SpawnBurst(globalCtx, &this->actor.focus.pos); + } + + if (this->framesUntilNextAction < 1080 && this->actionState < 3) { + if (this->framesUntilNextAction < 1070) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_LAST - SFX_FLAG); + } + + for (i = 0; i < 4; i++) { + //! @bug this 0-indexes into this->defeatedLimbPositions which is initialized with + // this->defeatedLimbPositions[limb], but limb is 1-indexed in skelanime callbacks, this means effects + // should spawn at this->defeatedLimbPositions[0] too, which is uninitialized, so map origin? + j = (s16)(Rand_ZeroOne() * (BOSSGOMA_LIMB_MAX - 1)); + if (this->defeatedLimbPositions[j].y < 10000.0f) { + pos.x = Rand_CenteredFloat(20.0f) + this->defeatedLimbPositions[j].x; + pos.y = Rand_CenteredFloat(10.0f) + this->defeatedLimbPositions[j].y; + pos.z = Rand_CenteredFloat(20.0f) + this->defeatedLimbPositions[j].z; + func_8002836C(globalCtx, &pos, &vel1, &accel1, &color1, &color2, 500, 10, 10); + } + } + + for (i = 0; i < 15; i++) { + //! @bug same as above + j = (s16)(Rand_ZeroOne() * (BOSSGOMA_LIMB_MAX - 1)); + if (this->defeatedLimbPositions[j].y < 10000.0f) { + pos.x = Rand_CenteredFloat(20.0f) + this->defeatedLimbPositions[j].x; + pos.y = Rand_CenteredFloat(10.0f) + this->defeatedLimbPositions[j].y; + pos.z = Rand_CenteredFloat(20.0f) + this->defeatedLimbPositions[j].z; + EffectSsHahen_Spawn(globalCtx, &pos, &vel2, &accel2, 0, (s16)(Rand_ZeroOne() * 5.0f) + 10, -1, 10, + NULL); + } + } + } + + switch (this->actionState) { + case 0: + this->actionState = 1; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 1); + this->subCameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, 3); + Gameplay_ChangeCameraStatus(globalCtx, this->subCameraId, 7); + camera = Gameplay_GetCamera(globalCtx, 0); + this->subCameraEye.x = camera->eye.x; + this->subCameraEye.y = camera->eye.y; + this->subCameraEye.z = camera->eye.z; + this->subCameraAt.x = camera->at.x; + this->subCameraAt.y = camera->at.y; + this->subCameraAt.z = camera->at.z; + dx = this->subCameraEye.x - this->actor.world.pos.x; + dz = this->subCameraEye.z - this->actor.world.pos.z; + this->defeatedCameraEyeDist = sqrtf(SQ(dx) + SQ(dz)); + this->defeatedCameraEyeAngle = Math_FAtan2F(dx, dz); + this->timer = 270; + break; + + case 1: + dx = Math_SinS(this->actor.shape.rot.y) * 100.0f; + dz = Math_CosS(this->actor.shape.rot.y) * 100.0f; + Math_ApproachF(&player->actor.world.pos.x, this->actor.world.pos.x + dx, 0.5f, 5.0f); + Math_ApproachF(&player->actor.world.pos.z, this->actor.world.pos.z + dz, 0.5f, 5.0f); + + if (this->framesUntilNextAction < 1080) { + this->noBackfaceCulling = true; + + for (i = 0; i < 4; i++) { + BossGoma_ClearPixels(sClearPixelTableFirstPass, this->decayingProgress); + //! @bug this allows this->decayingProgress = 0x100 = 256 which is out of bounds when accessing + // sClearPixelTableFirstPass, though timers may prevent this from ever happening? + if (this->decayingProgress < 0xFF) { + this->decayingProgress++; + } + } + } + + if (this->framesUntilNextAction < 1070 && this->frameCount % 4 == 0 && Rand_ZeroOne() < 0.5f) { + this->blinkTimer = 3; + } + + this->defeatedCameraEyeAngle += 0.022f; + Math_ApproachF(&this->defeatedCameraEyeDist, 150.0f, 0.1f, 5.0f); + dx = sinf(this->defeatedCameraEyeAngle); + dx = dx * this->defeatedCameraEyeDist; + dz = cosf(this->defeatedCameraEyeAngle); + dz = dz * this->defeatedCameraEyeDist; + Math_SmoothStepToF(&this->subCameraEye.x, this->actor.world.pos.x + dx, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraEye.y, this->actor.world.pos.y + 20.0f, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraEye.z, this->actor.world.pos.z + dz, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraAt.x, this->firstTailLimbWorldPos.x, 0.2f, 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraAt.y, this->actor.focus.pos.y, 0.5f, 100.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraAt.z, this->firstTailLimbWorldPos.z, 0.2f, 50.0f, 0.1f); + + if (this->timer == 80) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + } + + if (this->timer == 0) { + this->actionState = 2; + Gameplay_ChangeCameraStatus(globalCtx, 0, 3); + this->timer = 70; + this->decayingProgress = 0; + this->subCameraFollowSpeed = 0.0f; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + } + break; + + case 2: + camera = Gameplay_GetCamera(globalCtx, 0); + Math_SmoothStepToF(&this->subCameraEye.x, camera->eye.x, 0.2f, this->subCameraFollowSpeed * 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraEye.y, camera->eye.y, 0.2f, this->subCameraFollowSpeed * 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraEye.z, camera->eye.z, 0.2f, this->subCameraFollowSpeed * 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraAt.x, camera->at.x, 0.2f, this->subCameraFollowSpeed * 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraAt.y, camera->at.y, 0.2f, this->subCameraFollowSpeed * 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraAt.z, camera->at.z, 0.2f, this->subCameraFollowSpeed * 50.0f, 0.1f); + Math_SmoothStepToF(&this->subCameraFollowSpeed, 1.0f, 1.0f, 0.02f, 0.0f); + + if (this->timer == 0) { + childPos = roomCenter; + this->timer = 30; + this->actionState = 3; + + for (i = 0; i < 10000; i++) { + if ((fabsf(childPos.x - player->actor.world.pos.x) < 100.0f && + fabsf(childPos.z - player->actor.world.pos.z) < 100.0f) || + (fabsf(childPos.x - this->actor.world.pos.x) < 150.0f && + fabsf(childPos.z - this->actor.world.pos.z) < 150.0f)) { + childPos.x = Rand_CenteredFloat(400.0f) + -150.0f; + childPos.z = Rand_CenteredFloat(400.0f) + -350.0f; + } else { + break; + } + } + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, childPos.x, + this->actor.world.pos.y, childPos.z, 0, 0, 0, WARP_DUNGEON_CHILD); + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + + for (i = 0; i < 4; i++) { + BossGoma_ClearPixels(sClearPixelTableSecondPass, this->decayingProgress); + //! @bug same as sClearPixelTableFirstPass + if (this->decayingProgress < 0xFF) { + this->decayingProgress++; + } + } + break; + + case 3: + for (i = 0; i < 4; i++) { + BossGoma_ClearPixels(sClearPixelTableSecondPass, this->decayingProgress); + //! @bug same as sClearPixelTableFirstPass + if (this->decayingProgress < 0xFF) { + this->decayingProgress++; + } + } + + if (this->timer == 0) { + if (Math_SmoothStepToF(&this->actor.scale.y, 0, 1.0f, 0.00075f, 0.0f) <= 0.001f) { + camera = Gameplay_GetCamera(globalCtx, 0); + camera->eye = this->subCameraEye; + camera->eyeNext = this->subCameraEye; + camera->at = this->subCameraAt; + func_800C08AC(globalCtx, this->subCameraId, 0); + this->subCameraId = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Actor_Kill(&this->actor); + } + + this->actor.scale.x = this->actor.scale.z = this->actor.scale.y; + } + break; + } + + if (this->subCameraId != 0) { + Gameplay_CameraSetAtEye(globalCtx, this->subCameraId, &this->subCameraAt, &this->subCameraEye); + } + + if (this->blinkTimer != 0) { + this->blinkTimer--; + globalCtx->envCtx.adjAmbientColor[0] += 40; + globalCtx->envCtx.adjAmbientColor[1] += 40; + globalCtx->envCtx.adjAmbientColor[2] += 80; + globalCtx->envCtx.adjFogColor[0] += 10; + globalCtx->envCtx.adjFogColor[1] += 10; + globalCtx->envCtx.adjFogColor[2] += 20; + } else { + globalCtx->envCtx.adjAmbientColor[0] -= 20; + globalCtx->envCtx.adjAmbientColor[1] -= 20; + globalCtx->envCtx.adjAmbientColor[2] -= 40; + globalCtx->envCtx.adjFogColor[0] -= 5; + globalCtx->envCtx.adjFogColor[1] -= 5; + globalCtx->envCtx.adjFogColor[2] -= 10; + } + + if (globalCtx->envCtx.adjAmbientColor[0] > 200) { + globalCtx->envCtx.adjAmbientColor[0] = 200; + } + if (globalCtx->envCtx.adjAmbientColor[1] > 200) { + globalCtx->envCtx.adjAmbientColor[1] = 200; + } + if (globalCtx->envCtx.adjAmbientColor[2] > 200) { + globalCtx->envCtx.adjAmbientColor[2] = 200; + } + if (globalCtx->envCtx.adjFogColor[0] > 70) { + globalCtx->envCtx.adjFogColor[0] = 70; + } + if (globalCtx->envCtx.adjFogColor[1] > 70) { + globalCtx->envCtx.adjFogColor[1] = 70; + } + if (globalCtx->envCtx.adjFogColor[2] > 140) { + globalCtx->envCtx.adjFogColor[2] = 140; + } + + if (globalCtx->envCtx.adjAmbientColor[0] < 0) { + globalCtx->envCtx.adjAmbientColor[0] = 0; + } + if (globalCtx->envCtx.adjAmbientColor[1] < 0) { + globalCtx->envCtx.adjAmbientColor[1] = 0; + } + if (globalCtx->envCtx.adjAmbientColor[2] < 0) { + globalCtx->envCtx.adjAmbientColor[2] = 0; + } + if (globalCtx->envCtx.adjFogColor[0] < 0) { + globalCtx->envCtx.adjFogColor[0] = 0; + } + if (globalCtx->envCtx.adjFogColor[1] < 0) { + globalCtx->envCtx.adjFogColor[1] = 0; + } + if (globalCtx->envCtx.adjFogColor[2] < 0) { + globalCtx->envCtx.adjFogColor[2] = 0; + } +} + +/** + * If the player backs off, cancel the attack, or attack. + */ +void BossGoma_FloorAttackPosture(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + + if (this->skelanime.curFrame >= (19.0f + 1.0f / 3.0f) && this->skelanime.curFrame <= 30.0f) { + Math_ApproachS(&this->actor.world.rot.y, Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor), + 3, 0xBB8); + } + + if (Animation_OnFrame(&this->skelanime, Animation_GetLastFrame(&gGohmaPrepareAttackAnim))) { + if (this->actor.xzDistToPlayer < 250.0f) { + BossGoma_SetupFloorPrepareAttack(this); + } else { + BossGoma_SetupFloorMain(this); + } + } + + this->eyeState = EYESTATE_IRIS_FOLLOW_NO_IFRAMES; + this->visualState = VISUALSTATE_RED; +} + +/** + * Only lasts 1 frame. Plays a sound. + */ +void BossGoma_FloorPrepareAttack(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->framesUntilNextAction == 0) { + BossGoma_SetupFloorAttack(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_CRY1); + } + + this->eyeState = EYESTATE_IRIS_FOLLOW_NO_IFRAMES; + this->visualState = VISUALSTATE_RED; +} + +/** + * Gohma attacks, then the action eventually goes back to BossGoma_FloorMain + */ +void BossGoma_FloorAttack(BossGoma* this, GlobalContext* globalCtx) { + s16 i; + + this->actor.flags |= ACTOR_FLAG_24; + SkelAnime_Update(&this->skelanime); + + switch (this->actionState) { + case 0: + for (i = 0; i < this->collider.count; i++) { + if (this->collider.elements[i].info.toucherFlags & 2) { + this->framesUntilNextAction = 10; + break; + } + } + + if (Animation_OnFrame(&this->skelanime, 10.0f)) { + BossGoma_PlayEffectsAndSfx(this, globalCtx, 3, 5); + func_80033E88(&this->actor, globalCtx, 5, 15); + } + + if (Animation_OnFrame(&this->skelanime, Animation_GetLastFrame(&gGohmaAttackAnim))) { + this->actionState = 1; + Animation_Change(&this->skelanime, &gGohmaRestAfterAttackAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaRestAfterAttackAnim), ANIMMODE_LOOP, -1.0f); + + if (this->framesUntilNextAction == 0) { + this->timer = (s16)(Rand_ZeroOne() * 30.0f) + 30; + } + } + break; + + case 1: + if (Animation_OnFrame(&this->skelanime, 3.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_UNARI2); + } + + if (this->timer == 0) { + this->actionState = 2; + Animation_Change(&this->skelanime, &gGohmaRecoverAfterAttackAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGohmaRecoverAfterAttackAnim), ANIMMODE_ONCE, -5.0f); + } + break; + + case 2: + if (Animation_OnFrame(&this->skelanime, Animation_GetLastFrame(&gGohmaRecoverAfterAttackAnim))) { + BossGoma_SetupFloorIdle(this); + } + break; + } + + this->eyeState = EYESTATE_IRIS_FOLLOW_NO_IFRAMES; + this->visualState = VISUALSTATE_RED; +} + +/** + * Plays the animation to its end, then goes back to BossGoma_FloorStunned + */ +void BossGoma_FloorDamaged(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (Animation_OnFrame(&this->skelanime, Animation_GetLastFrame(&gGohmaDamageAnim))) { + BossGoma_SetupFloorStunned(this); + this->patienceTimer = 0; + } + + this->eyeState = EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES; + Math_ApproachF(&this->eyeIrisScaleX, 0.4f, 0.5f, 0.2f); + this->visualState = VISUALSTATE_HIT; +} + +/** + * Gohma is back on the floor after the player struck it down from the ceiling. + * Sets patience to 0 + * Gohma is then stunned (BossGoma_FloorStunned) + */ +void BossGoma_FloorLandStruckDown(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (Animation_OnFrame(&this->skelanime, this->currentAnimFrameCount)) { + BossGoma_SetupFloorStunned(this); + this->sfxFaintTimer = 92; + this->patienceTimer = 0; + this->framesUntilNextAction = 150; + } + + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 55.0f, 4, 8.0f, 500, 10, 1); +} + +/** + * Gohma is back on the floor after the player has killed its children Gohmas. + * Plays an animation then goes to usual floor behavior, with refilled patience. + */ +void BossGoma_FloorLand(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (Animation_OnFrame(&this->skelanime, this->currentAnimFrameCount)) { + BossGoma_SetupFloorIdle(this); + this->patienceTimer = 200; + } +} + +/** + * Gohma is stunned and vulnerable. It can only be damaged during this action. + */ +void BossGoma_FloorStunned(BossGoma* this, GlobalContext* globalCtx) { + if (this->sfxFaintTimer <= 90) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_FAINT - 0x800); + } + SkelAnime_Update(&this->skelanime); + + if (this->timer == 1) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 55.0f, 4, 8.0f, 500, 10, 1); + } + + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 1.0f); + + if (this->framesUntilNextAction == 0) { + BossGoma_SetupFloorMain(this); + if (this->patienceTimer == 0 && this->actor.xzDistToPlayer < 130.0f) { + this->timer = 20; + } + } + + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + this->eyeState = EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES; + Math_ApproachF(&this->eyeIrisScaleX, 0.4f, 0.5f, 0.2f); + this->visualState = VISUALSTATE_STUNNED; +} + +/** + * Gohma goes back to the floor after the player killed the three gohmas it spawned + */ +void BossGoma_FallJump(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + Math_ApproachS(&this->actor.world.rot.y, Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor), 2, + 0x7D0); + + if (this->actor.bgCheckFlags & 1) { + BossGoma_SetupFloorLand(this); + this->actor.velocity.y = 0.0f; + BossGoma_PlayEffectsAndSfx(this, globalCtx, 0, 8); + func_80033E88(&this->actor, globalCtx, 5, 0xF); + } +} + +/** + * Gohma falls to the floor after the player hit it + */ +void BossGoma_FallStruckDown(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + Math_ApproachS(&this->actor.world.rot.y, Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor), 3, + 0x7D0); + + if (this->actor.bgCheckFlags & 1) { + BossGoma_SetupFloorLandStruckDown(this); + this->actor.velocity.y = 0.0f; + BossGoma_PlayEffectsAndSfx(this, globalCtx, 0, 8); + func_80033E88(&this->actor, globalCtx, 0xA, 0xF); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM1); + } +} + +/** + * Spawn three gohmas, one after the other. Cannot be interrupted + */ +void BossGoma_CeilingSpawnGohmas(BossGoma* this, GlobalContext* globalCtx) { + s16 i; + + SkelAnime_Update(&this->skelanime); + + if (this->frameCount % 16 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_UNARI); + } + + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + this->spawnGohmasActionTimer++; + + switch (this->spawnGohmasActionTimer) { + case 24: + // BOSSGOMA_LIMB_TAIL1, the tail limb closest to the body + this->tailLimbsScaleTimers[3] = 10; + break; + case 32: + // BOSSGOMA_LIMB_TAIL2 + this->tailLimbsScaleTimers[2] = 10; + break; + case 40: + // BOSSGOMA_LIMB_TAIL3 + this->tailLimbsScaleTimers[1] = 10; + break; + case 48: + // BOSSGOMA_LIMB_TAIL4, the furthest from the body + this->tailLimbsScaleTimers[0] = 10; + break; + } + + if (this->tailLimbsScaleTimers[0] == 2) { + for (i = 0; i < ARRAY_COUNT(this->childrenGohmaState); i++) { + if (this->childrenGohmaState[i] == 0) { + BossGoma_SpawnChildGohma(this, globalCtx, i); + break; + } + } + + if (this->childrenGohmaState[0] == 0 || this->childrenGohmaState[1] == 0 || this->childrenGohmaState[2] == 0) { + this->spawnGohmasActionTimer = 23; + } + } + + if (this->spawnGohmasActionTimer >= 64) { + BossGoma_SetupCeilingIdle(this); + } + + this->eyeState = EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES; +} + +/** + * Prepare to spawn children gohmas, red eye for 70 frames + * During this time, the player can interrupt by hitting Gohma and make it fall from the ceiling + */ +void BossGoma_CeilingPrepareSpawnGohmas(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->framesUntilNextAction == 0) { + BossGoma_SetupCeilingSpawnGohmas(this); + } + + this->eyeState = EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES; + this->visualState = VISUALSTATE_RED; +} + +/** + * On the floor, not doing anything special. + */ +void BossGoma_FloorIdle(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + Math_ApproachS(&this->actor.shape.rot.x, 0, 2, 0xBB8); + + if (this->framesUntilNextAction == 0) { + BossGoma_SetupFloorMain(this); + } +} + +/** + * On the ceiling, not doing anything special. + * Eventually spawns children gohmas, jumping down to the floor when they are killed, or staying on the ceiling as long + * as any is still alive. + */ +void BossGoma_CeilingIdle(BossGoma* this, GlobalContext* globalCtx) { + s16 i; + + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + + if (this->framesUntilNextAction == 0) { + if (this->childrenGohmaState[0] == 0 && this->childrenGohmaState[1] == 0 && this->childrenGohmaState[2] == 0) { + // if no child gohma has been spawned + BossGoma_SetupCeilingPrepareSpawnGohmas(this); + } else if (this->childrenGohmaState[0] < 0 && this->childrenGohmaState[1] < 0 && + this->childrenGohmaState[2] < 0) { + // if all children gohmas are dead + BossGoma_SetupFallJump(this); + } else { + for (i = 0; i < ARRAY_COUNT(this->childrenGohmaState); i++) { + if (this->childrenGohmaState[i] == 0) { + // if any child gohma hasn't been spawned + // this seems unreachable since BossGoma_CeilingSpawnGohmas spawns all three and can't be + // interrupted + BossGoma_SetupCeilingSpawnGohmas(this); + return; + } + } + // if all children gohmas have been spawned + BossGoma_SetupCeilingMoveToCenter(this); + } + } +} + +/** + * Gohma approaches the player as long as it has patience (see patienceTimer), then moves away from the player + * Gohma climbs any wall it collides with + * Uses the "walk cautiously" animation + */ +void BossGoma_FloorMain(BossGoma* this, GlobalContext* globalCtx) { + s16 rot; + + SkelAnime_Update(&this->skelanime); + + if (Animation_OnFrame(&this->skelanime, 1.0f)) { + this->doNotMoveThisFrame = true; + } else if (Animation_OnFrame(&this->skelanime, 30.0f)) { + this->doNotMoveThisFrame = true; + } else if (Animation_OnFrame(&this->skelanime, 15.0f)) { + this->doNotMoveThisFrame = true; + } else if (Animation_OnFrame(&this->skelanime, 16.0f)) { + this->doNotMoveThisFrame = true; + } + + if (Animation_OnFrame(&this->skelanime, 15.0f)) { + BossGoma_PlayEffectsAndSfx(this, globalCtx, 1, 3); + } else if (Animation_OnFrame(&this->skelanime, 30.0f)) { + BossGoma_PlayEffectsAndSfx(this, globalCtx, 2, 3); + } + + if (this->frameCount % 64 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_CRY2); + } + + if (!this->doNotMoveThisFrame) { + rot = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + + if (this->patienceTimer != 0) { + this->patienceTimer--; + + if (this->actor.xzDistToPlayer < 150.0f) { + BossGoma_SetupFloorAttackPosture(this); + } + + Math_ApproachF(&this->actor.speedXZ, 10.0f / 3.0f, 0.5f, 2.0f); + Math_ApproachS(&this->actor.world.rot.y, rot, 5, 0x3E8); + } else { + if (this->timer != 0) { + // move away from the player, walking backwards + Math_ApproachF(&this->actor.speedXZ, -10.0f, 0.5f, 2.0f); + this->skelanime.playSpeed = -3.0f; + if (this->timer == 1) { + this->actor.speedXZ = 0.0f; + } + } else { + // move away from the player, walking forwards + Math_ApproachF(&this->actor.speedXZ, 20.0f / 3.0f, 0.5f, 2.0f); + this->skelanime.playSpeed = 2.0f; + rot += 0x8000; + } + + Math_ApproachS(&this->actor.world.rot.y, rot, 3, 0x9C4); + } + } + + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 0.0f; + } + + if (this->actor.bgCheckFlags & 8) { + BossGoma_SetupWallClimb(this); + } + + if (this->framesUntilNextAction == 0 && this->patienceTimer != 0) { + BossGoma_SetupFloorIdle(this); + } +} + +/** + * Gohma moves up until it reaches the ceiling + */ +void BossGoma_WallClimb(BossGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->frameCount % 8 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_CLIM); + } + + Math_ApproachF(&this->actor.velocity.y, 5.0f, 0.5f, 2.0f); + Math_ApproachS(&this->actor.shape.rot.x, -0x4000, 2, 0x7D0); + Math_ApproachS(&this->actor.world.rot.y, this->actor.wallYaw + 0x8000, 2, 0x5DC); + + // -320 is a bit below boss room ceiling + if (this->actor.world.pos.y > -320.0f) { + BossGoma_SetupCeilingMoveToCenter(this); + // allow new spawns + this->childrenGohmaState[0] = this->childrenGohmaState[1] = this->childrenGohmaState[2] = 0; + } +} + +/** + * Goes to BossGoma_CeilingIdle after enough time and after being close enough to the center of the ceiling. + */ +void BossGoma_CeilingMoveToCenter(BossGoma* this, GlobalContext* globalCtx) { + s16 angle; + s16 absDiff; + + BossGoma_UpdateCeilingMovement(this, globalCtx, 0.0f, -5.0f, true); + + if (this->frameCount % 64 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_CRY2); + } + + Math_ApproachS(&this->actor.shape.rot.x, -0x8000, 3, 0x3E8); + + // avoid walking into a wall? + if (this->actor.bgCheckFlags & 8) { + angle = this->actor.shape.rot.y + 0x8000; + + if (angle < this->actor.wallYaw) { + absDiff = this->actor.wallYaw - angle; + angle = angle + absDiff / 2; + } else { + absDiff = angle - this->actor.wallYaw; + angle = this->actor.wallYaw + absDiff / 2; + } + + this->actor.world.pos.z += Math_CosS(angle) * (5.0f + Rand_ZeroOne() * 5.0f) + Rand_CenteredFloat(2.0f); + this->actor.world.pos.x += Math_SinS(angle) * (5.0f + Rand_ZeroOne() * 5.0f) + Rand_CenteredFloat(2.0f); + } + + // timer setup to 30-60 + if (this->framesUntilNextAction == 0 && fabsf(-150.0f - this->actor.world.pos.x) < 100.0f && + fabsf(-350.0f - this->actor.world.pos.z) < 100.0f) { + BossGoma_SetupCeilingIdle(this); + } +} + +/** + * Update eye-related properties + * - open/close (eye lid rotation) + * - look at the player (iris rotation) + * - iris scale, when menacing or damaged + */ +void BossGoma_UpdateEye(BossGoma* this, GlobalContext* globalCtx) { + s16 targetEyeIrisRotX; + s16 targetEyeIrisRotY; + + if (!this->disableGameplayLogic) { + Player* player = GET_PLAYER(globalCtx); + + if (this->eyeState == EYESTATE_IRIS_FOLLOW_BONUS_IFRAMES) { + // player + 0xA73 seems to be related to "throwing something" + if (player->unk_A73 != 0) { + player->unk_A73 = 0; + this->eyeClosedTimer = 12; + } + + if (this->frameCount % 16 == 0 && Rand_ZeroOne() < 0.3f) { + this->eyeClosedTimer = 7; + } + } + + if (this->childrenGohmaState[0] > 0 || this->childrenGohmaState[1] > 0 || this->childrenGohmaState[2] > 0) { + this->eyeClosedTimer = 7; + } + + if (this->eyeClosedTimer != 0) { + this->eyeClosedTimer--; + // close eye + Math_ApproachS(&this->eyeLidBottomRotX, -0xA98, 1, 0x7D0); + Math_ApproachS(&this->eyeLidTopRotX, 0x1600, 1, 0x7D0); + } else { + // open eye + Math_ApproachS(&this->eyeLidBottomRotX, 0, 1, 0x7D0); + Math_ApproachS(&this->eyeLidTopRotX, 0, 1, 0x7D0); + } + + if (this->eyeState != EYESTATE_IRIS_NO_FOLLOW_NO_IFRAMES) { + targetEyeIrisRotY = + Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.shape.rot.y; + targetEyeIrisRotX = + Actor_WorldPitchTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.shape.rot.x; + + if (this->actor.shape.rot.x > 0x4000 || this->actor.shape.rot.x < -0x4000) { + targetEyeIrisRotY = -(s16)(targetEyeIrisRotY + 0x8000); + targetEyeIrisRotX = -0xBB8; + } + + if (targetEyeIrisRotY > 0x1770) { + targetEyeIrisRotY = 0x1770; + } + + if (targetEyeIrisRotY < -0x1770) { + targetEyeIrisRotY = -0x1770; + } + + Math_ApproachS(&this->eyeIrisRotY, targetEyeIrisRotY, 3, 0x7D0); + Math_ApproachS(&this->eyeIrisRotX, targetEyeIrisRotX, 3, 0x7D0); + } else { + Math_ApproachS(&this->eyeIrisRotY, 0, 3, 0x3E8); + Math_ApproachS(&this->eyeIrisRotX, 0, 3, 0x3E8); + } + + Math_ApproachF(&this->eyeIrisScaleX, 1.0f, 0.2f, 0.07f); + Math_ApproachF(&this->eyeIrisScaleY, 1.0f, 0.2f, 0.07f); + } +} + +/** + * Part of achieving visual effects when spawning children gohmas, + * inflating each tail limb one after the other. + */ +void BossGoma_UpdateTailLimbsScale(BossGoma* this) { + s16 i; + + if (this->frameCount % 128 == 0) { + this->unusedTimer++; + if (this->unusedTimer >= 3) { + this->unusedTimer = 0; + } + } + + // See BossGoma_CeilingSpawnGohmas for `tailLimbsScaleTimers` usage + for (i = 0; i < ARRAY_COUNT(this->tailLimbsScaleTimers); i++) { + if (this->tailLimbsScaleTimers[i] != 0) { + this->tailLimbsScaleTimers[i]--; + Math_ApproachF(&this->tailLimbsScale[i], 1.5f, 0.2f, 0.1f); + } else { + Math_ApproachF(&this->tailLimbsScale[i], 1.0f, 0.2f, 0.1f); + } + } +} + +void BossGoma_UpdateHit(BossGoma* this, GlobalContext* globalCtx) { + if (this->invincibilityFrames != 0) { + this->invincibilityFrames--; + } else { + ColliderInfo* acHitInfo = this->collider.elements[0].info.acHitInfo; + s32 damage; + + if (this->eyeClosedTimer == 0 && this->actionFunc != BossGoma_CeilingSpawnGohmas && + (this->collider.elements[0].info.bumperFlags & BUMP_HIT)) { + this->collider.elements[0].info.bumperFlags &= ~BUMP_HIT; + + if (this->actionFunc == BossGoma_CeilingMoveToCenter || this->actionFunc == BossGoma_CeilingIdle || + this->actionFunc == BossGoma_CeilingPrepareSpawnGohmas) { + BossGoma_SetupFallStruckDown(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM2); + } else if (this->actionFunc == BossGoma_FloorStunned && + (damage = CollisionCheck_GetSwordDamage(acHitInfo->toucher.dmgFlags)) != 0) { + this->actor.colChkInfo.health -= damage; + + if ((s8)this->actor.colChkInfo.health > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM1); + BossGoma_SetupFloorDamaged(this); + EffectSsSibuki_SpawnBurst(globalCtx, &this->actor.focus.pos); + } else { + BossGoma_SetupDefeated(this, globalCtx); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } + + this->invincibilityFrames = 10; + } else if (this->actionFunc != BossGoma_FloorStunned && this->patienceTimer != 0 && + (acHitInfo->toucher.dmgFlags & 0x00000005)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_DAM2); + Audio_StopSfxById(NA_SE_EN_GOMA_CRY1); + this->invincibilityFrames = 10; + BossGoma_SetupFloorStunned(this); + this->sfxFaintTimer = 100; + + if (acHitInfo->toucher.dmgFlags & 1) { + this->framesUntilNextAction = 40; + } else { + this->framesUntilNextAction = 90; + } + + this->timer = 4; + func_80033E88(&this->actor, globalCtx, 4, 0xC); + } + } + } +} + +void BossGoma_UpdateMainEnvColor(BossGoma* this) { + static f32 colors1[][3] = { + { 255.0f, 17.0f, 0.0f }, { 0.0f, 255.0f, 170.0f }, { 50.0f, 50.0f, 50.0f }, + { 0.0f, 255.0f, 170.0f }, { 0.0f, 255.0f, 170.0f }, { 0.0f, 255.0f, 170.0f }, + }; + static f32 colors2[][3] = { + { 255.0f, 17.0f, 0.0f }, { 0.0f, 255.0f, 170.0f }, { 50.0f, 50.0f, 50.0f }, + { 0.0f, 255.0f, 170.0f }, { 0.0f, 0.0f, 255.0f }, { 255.0f, 17.0f, 0.0f }, + }; + + if (this->visualState == VISUALSTATE_DEFAULT && this->frameCount & 0x10) { + Math_ApproachF(&this->mainEnvColor[0], 50.0f, 0.5f, 20.0f); + Math_ApproachF(&this->mainEnvColor[1], 50.0f, 0.5f, 20.0f); + Math_ApproachF(&this->mainEnvColor[2], 50.0f, 0.5f, 20.0f); + } else if (this->invincibilityFrames != 0) { + if (this->invincibilityFrames & 2) { + this->mainEnvColor[0] = colors2[this->visualState][0]; + this->mainEnvColor[1] = colors2[this->visualState][1]; + this->mainEnvColor[2] = colors2[this->visualState][2]; + } else { + this->mainEnvColor[0] = colors1[this->visualState][0]; + this->mainEnvColor[1] = colors1[this->visualState][1]; + this->mainEnvColor[2] = colors1[this->visualState][2]; + } + } else { + Math_ApproachF(&this->mainEnvColor[0], colors1[this->visualState][0], 0.5f, 20.0f); + Math_ApproachF(&this->mainEnvColor[1], colors1[this->visualState][1], 0.5f, 20.0f); + Math_ApproachF(&this->mainEnvColor[2], colors1[this->visualState][2], 0.5f, 20.0f); + } +} + +void BossGoma_UpdateEyeEnvColor(BossGoma* this) { + static f32 targetEyeEnvColors[][3] = { + { 255.0f, 17.0f, 0.0f }, { 255.0f, 255.0f, 255.0f }, { 50.0f, 50.0f, 50.0f }, + { 0.0f, 255.0f, 170.0f }, { 0.0f, 255.0f, 170.0f }, { 0.0f, 255.0f, 170.0f }, + }; + + Math_ApproachF(&this->eyeEnvColor[0], targetEyeEnvColors[this->visualState][0], 0.5f, 20.0f); + Math_ApproachF(&this->eyeEnvColor[1], targetEyeEnvColors[this->visualState][1], 0.5f, 20.0f); + Math_ApproachF(&this->eyeEnvColor[2], targetEyeEnvColors[this->visualState][2], 0.5f, 20.0f); +} + +void BossGoma_Update(Actor* thisx, GlobalContext* globalCtx) { + BossGoma* this = (BossGoma*)thisx; + s32 pad; + + this->visualState = VISUALSTATE_DEFAULT; + this->frameCount++; + + if (this->framesUntilNextAction != 0) { + this->framesUntilNextAction--; + } + + if (this->timer != 0) { + this->timer--; + } + + if (this->sfxFaintTimer != 0) { + this->sfxFaintTimer--; + } + + if (1) {} + + this->eyeState = EYESTATE_IRIS_FOLLOW_BONUS_IFRAMES; + this->actionFunc(this, globalCtx); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (!this->doNotMoveThisFrame) { + Actor_MoveForward(&this->actor); + } else { + this->doNotMoveThisFrame = false; + } + + if (this->actor.world.pos.y < -400.0f) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 30.0f, 80.0f, 5); + } else { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 30.0f, 80.0f, 1); + } + + BossGoma_UpdateEye(this, globalCtx); + BossGoma_UpdateMainEnvColor(this); + BossGoma_UpdateEyeEnvColor(this); + BossGoma_UpdateTailLimbsScale(this); + + if (!this->disableGameplayLogic) { + BossGoma_UpdateHit(this, globalCtx); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->actionFunc != BossGoma_FloorStunned && this->actionFunc != BossGoma_FloorDamaged && + (this->actionFunc != BossGoma_FloorMain || this->timer == 0)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +s32 BossGoma_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossGoma* this = (BossGoma*)thisx; + s32 doNotDrawLimb = false; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_goma.c", 4685); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->mainEnvColor[0], (s16)this->mainEnvColor[1], (s16)this->mainEnvColor[2], + 255); + + if (this->deadLimbsState[limbIndex] >= 2) { + *dList = NULL; + } + + switch (limbIndex) { + case BOSSGOMA_LIMB_EYE: + if (this->eyeState == EYESTATE_IRIS_FOLLOW_BONUS_IFRAMES && this->eyeLidBottomRotX < -0xA8C) { + *dList = NULL; + } else if (this->invincibilityFrames != 0) { + gDPSetEnvColor(POLY_OPA_DISP++, (s16)(Rand_ZeroOne() * 255.0f), (s16)(Rand_ZeroOne() * 255.0f), + (s16)(Rand_ZeroOne() * 255.0f), 63); + } else { + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->eyeEnvColor[0], (s16)this->eyeEnvColor[1], + (s16)this->eyeEnvColor[2], 63); + } + break; + + case BOSSGOMA_LIMB_EYE_LID_BOTTOM_ROOT2: + rot->x += this->eyeLidBottomRotX; + break; + + case BOSSGOMA_LIMB_EYE_LID_TOP_ROOT2: + rot->x += this->eyeLidTopRotX; + break; + + case BOSSGOMA_LIMB_IRIS_ROOT2: + rot->x += this->eyeIrisRotX; + rot->y += this->eyeIrisRotY; + break; + + case BOSSGOMA_LIMB_IRIS: + if (this->eyeState == EYESTATE_IRIS_FOLLOW_BONUS_IFRAMES && this->eyeLidBottomRotX < -0xA8C) { + *dList = NULL; + } else { + if (this->visualState == VISUALSTATE_DEFEATED) { + gDPSetEnvColor(POLY_OPA_DISP++, 50, 50, 50, 255); + } else { + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + } + + Matrix_TranslateRotateZYX(pos, rot); + + if (*dList != NULL) { + Matrix_Push(); + Matrix_Scale(this->eyeIrisScaleX, this->eyeIrisScaleY, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_goma.c", 4815), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + } + + doNotDrawLimb = true; + } + break; + + case BOSSGOMA_LIMB_TAIL4: + case BOSSGOMA_LIMB_TAIL3: + case BOSSGOMA_LIMB_TAIL2: + case BOSSGOMA_LIMB_TAIL1: + Matrix_TranslateRotateZYX(pos, rot); + + if (*dList != NULL) { + Matrix_Push(); + Matrix_Scale(this->tailLimbsScale[limbIndex - BOSSGOMA_LIMB_TAIL4], + this->tailLimbsScale[limbIndex - BOSSGOMA_LIMB_TAIL4], + this->tailLimbsScale[limbIndex - BOSSGOMA_LIMB_TAIL4], MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_goma.c", 4836), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + } + + doNotDrawLimb = true; + break; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_goma.c", 4858); + + return doNotDrawLimb; +} + +void BossGoma_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f tailZero = { 0.0f, 0.0f, 0.0f }; + static Vec3f clawBackLocalPos = { 0.0f, 0.0f, 0.0f }; + static Vec3f focusEyeLocalPos = { 0.0f, 300.0f, 2650.0f }; // in the center of the surface of the lens + static Vec3f zero = { 0.0f, 0.0f, 0.0f }; + Vec3f childPos; + Vec3s childRot; + EnGoma* babyGohma; + BossGoma* this = (BossGoma*)thisx; + s32 pad; + MtxF mtx; + + if (limbIndex == BOSSGOMA_LIMB_TAIL4) { // tail end/last part + Matrix_MultVec3f(&tailZero, &this->lastTailLimbWorldPos); + } else if (limbIndex == BOSSGOMA_LIMB_TAIL1) { // tail start/first part + Matrix_MultVec3f(&tailZero, &this->firstTailLimbWorldPos); + } else if (limbIndex == BOSSGOMA_LIMB_EYE) { + Matrix_MultVec3f(&focusEyeLocalPos, &this->actor.focus.pos); + } else if (limbIndex == BOSSGOMA_LIMB_R_FEET_BACK) { + Matrix_MultVec3f(&clawBackLocalPos, &this->rightHandBackLimbWorldPos); + } else if (limbIndex == BOSSGOMA_LIMB_L_FEET_BACK) { + Matrix_MultVec3f(&clawBackLocalPos, &this->leftHandBackLimbWorldPos); + } + + if (this->visualState == VISUALSTATE_DEFEATED) { + if (*dList != NULL) { + Matrix_MultVec3f(&clawBackLocalPos, &this->defeatedLimbPositions[limbIndex]); + } else { + this->defeatedLimbPositions[limbIndex].y = 10000.0f; + } + } + + if (this->deadLimbsState[limbIndex] == 1) { + this->deadLimbsState[limbIndex] = 2; + Matrix_MultVec3f(&zero, &childPos); + Matrix_Get(&mtx); + Matrix_MtxFToYXZRotS(&mtx, &childRot, 0); + // These are the pieces of Gohma as it falls apart. It appears to use the same actor as the baby gohmas. + babyGohma = (EnGoma*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_GOMA, + childPos.x, childPos.y, childPos.z, childRot.x, childRot.y, childRot.z, + sDeadLimbLifetime[limbIndex] + 100); + if (babyGohma != NULL) { + babyGohma->bossLimbDl = *dList; + babyGohma->actor.objBankIndex = this->actor.objBankIndex; + } + } + + Collider_UpdateSpheres(limbIndex, &this->collider); +} + +Gfx* BossGoma_EmptyDlist(GraphicsContext* gfxCtx) { + Gfx* dListHead; + Gfx* dList; + + dList = dListHead = Graph_Alloc(gfxCtx, sizeof(Gfx) * 1); + + gSPEndDisplayList(dListHead++); + + return dList; +} + +Gfx* BossGoma_NoBackfaceCullingDlist(GraphicsContext* gfxCtx) { + Gfx* dListHead; + Gfx* dList; + + dList = dListHead = Graph_Alloc(gfxCtx, sizeof(Gfx) * 4); + + gDPPipeSync(dListHead++); + gDPSetRenderMode(dListHead++, G_RM_PASS, G_RM_AA_ZB_TEX_EDGE2); + gSPClearGeometryMode(dListHead++, G_CULL_BACK); + gSPEndDisplayList(dListHead++); + + return dList; +} + +void BossGoma_Draw(Actor* thisx, GlobalContext* globalCtx) { + BossGoma* this = (BossGoma*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_goma.c", 4991); + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Translate(0.0f, -4000.0f, 0.0f, MTXMODE_APPLY); + + // Invalidate Texture Cache since Goma modifies her own texture + gSPInvalidateTexCache(POLY_OPA_DISP++, gGohmaBodyTex); + gSPInvalidateTexCache(POLY_OPA_DISP++, gGohmaShellUndersideTex); + gSPInvalidateTexCache(POLY_OPA_DISP++, gGohmaDarkShellTex); + gSPInvalidateTexCache(POLY_OPA_DISP++, gGohmaEyeTex); + gSPInvalidateTexCache(POLY_OPA_DISP++, gGohmaShellTex); + gSPInvalidateTexCache(POLY_OPA_DISP++, gGohmaIrisTex); + + if (this->noBackfaceCulling) { + gSPSegment(POLY_OPA_DISP++, 0x08, BossGoma_NoBackfaceCullingDlist(globalCtx->state.gfxCtx)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, BossGoma_EmptyDlist(globalCtx->state.gfxCtx)); + } + + SkelAnime_DrawOpa(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, BossGoma_OverrideLimbDraw, + BossGoma_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_goma.c", 5012); +} + +void BossGoma_SpawnChildGohma(BossGoma* this, GlobalContext* globalCtx, s16 i) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_GOMA, this->lastTailLimbWorldPos.x, + this->lastTailLimbWorldPos.y - 50.0f, this->lastTailLimbWorldPos.z, 0, i * (0x10000 / 3), 0, i); + + this->childrenGohmaState[i] = 1; +} diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.h b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.h new file mode 100644 index 000000000..22143f1c6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.h @@ -0,0 +1,156 @@ +#ifndef Z_BOSS_GOMA_H +#define Z_BOSS_GOMA_H + +#include "ultra64.h" +#include "global.h" + +struct BossGoma; + +typedef void (*BossGomaActionFunc)(struct BossGoma*, GlobalContext*); + +typedef enum { + /* 0 */ BOSSGOMA_LIMB_NONE, + /* 1 */ BOSSGOMA_LIMB_ROOT1, + /* 2 */ BOSSGOMA_LIMB_ROOT2, + /* 3 */ BOSSGOMA_LIMB_BODY, + /* 4 */ BOSSGOMA_LIMB_BODY_SHELL, + /* 5 */ BOSSGOMA_LIMB_EYE, + /* 6 */ BOSSGOMA_LIMB_TAIL_ROOT, + /* 7 */ BOSSGOMA_LIMB_TAIL1_ROOT, + /* 8 */ BOSSGOMA_LIMB_TAIL2_ROOT, + /* 9 */ BOSSGOMA_LIMB_TAIL3_ROOT, + /* 10 */ BOSSGOMA_LIMB_TAIL4_ROOT, + /* 11 */ BOSSGOMA_LIMB_TAIL4, + /* 12 */ BOSSGOMA_LIMB_TAIL3, + /* 13 */ BOSSGOMA_LIMB_TAIL2, + /* 14 */ BOSSGOMA_LIMB_TAIL1, + /* 15 */ BOSSGOMA_LIMB_R_LEG_ROOT, + /* 16 */ BOSSGOMA_LIMB_R_THIGH_ROOT, + /* 17 */ BOSSGOMA_LIMB_R_LEG_LOWER_ROOT, + /* 18 */ BOSSGOMA_LIMB_R_FEET_ROOT, + /* 19 */ BOSSGOMA_LIMB_R_FEET_BACK_ROOT1, + /* 20 */ BOSSGOMA_LIMB_R_FEET_BACK_ROOT2, + /* 21 */ BOSSGOMA_LIMB_R_FEET_BACK, + /* 22 */ BOSSGOMA_LIMB_R_FEET, + /* 23 */ BOSSGOMA_LIMB_R_SHIN, + /* 24 */ BOSSGOMA_LIMB_R_KNEE_ROOT1, + /* 25 */ BOSSGOMA_LIMB_R_KNEE_ROOT2, + /* 26 */ BOSSGOMA_LIMB_R_KNEE, + /* 27 */ BOSSGOMA_LIMB_R_THIGH_SHELL_ROOT1, + /* 28 */ BOSSGOMA_LIMB_R_THIGH_SHELL_ROOT2, + /* 29 */ BOSSGOMA_LIMB_R_THIGH_SHELL, + /* 30 */ BOSSGOMA_LIMB_R_THIGH, + /* 31 */ BOSSGOMA_LIMB_EYE_LID_BOTTOM_ROOT1, + /* 32 */ BOSSGOMA_LIMB_EYE_LID_BOTTOM_ROOT2, + /* 33 */ BOSSGOMA_LIMB_EYE_LID_BOTTOM, + /* 34 */ BOSSGOMA_LIMB_EYE_LID_TOP_ROOT1, + /* 35 */ BOSSGOMA_LIMB_EYE_LID_TOP_ROOT2, + /* 36 */ BOSSGOMA_LIMB_EYE_LID_TOP, + /* 37 */ BOSSGOMA_LIMB_IRIS_ROOT1, + /* 38 */ BOSSGOMA_LIMB_IRIS_ROOT2, + /* 39 */ BOSSGOMA_LIMB_IRIS, + /* 40 */ BOSSGOMA_LIMB_MANDIBLES_ROOT1, + /* 41 */ BOSSGOMA_LIMB_MANDIBLES_ROOT2, + /* 42 */ BOSSGOMA_LIMB_MANDIBLES_BODY, + /* 43 */ BOSSGOMA_LIMB_L_MANDIBLES_ROOT, + /* 44 */ BOSSGOMA_LIMB_L_MANDIBLES1_ROOT, + /* 45 */ BOSSGOMA_LIMB_L_MANDIBLES2_ROOT, + /* 46 */ BOSSGOMA_LIMB_L_MANDIBLES2, + /* 47 */ BOSSGOMA_LIMB_L_MANDIBLES1, + /* 48 */ BOSSGOMA_LIMB_R_MANDIBLES_ROOT, + /* 49 */ BOSSGOMA_LIMB_R_MANDIBLES1_ROOT, + /* 50 */ BOSSGOMA_LIMB_R_MANDIBLES2_ROOT, + /* 51 */ BOSSGOMA_LIMB_R_MANDIBLES2, + /* 52 */ BOSSGOMA_LIMB_R_MANDIBLES1, + /* 53 */ BOSSGOMA_LIMB_L_ANTENNA_ROOT, + /* 54 */ BOSSGOMA_LIMB_L_ANTENNA_BODY_ROOT, + /* 55 */ BOSSGOMA_LIMB_L_ANTENNA_SHELL_ROOT, + /* 56 */ BOSSGOMA_LIMB_L_ANTENNA_CLAW_ROOT, + /* 57 */ BOSSGOMA_LIMB_L_ANTENNA_CLAW, + /* 58 */ BOSSGOMA_LIMB_L_ANTENNA_SHELL, + /* 59 */ BOSSGOMA_LIMB_L_ANTENNA_BODY, + /* 60 */ BOSSGOMA_LIMB_R_ANTENNA_ROOT, + /* 61 */ BOSSGOMA_LIMB_R_ANTENNA_BODY_ROOT, + /* 62 */ BOSSGOMA_LIMB_R_ANTENNA_SHELL_ROOT, + /* 63 */ BOSSGOMA_LIMB_R_ANTENNA_CLAW_ROOT, + /* 64 */ BOSSGOMA_LIMB_R_ANTENNA_CLAW, + /* 65 */ BOSSGOMA_LIMB_R_ANTENNA_SHELL, + /* 66 */ BOSSGOMA_LIMB_R_ANTENNA_BODY, + /* 67 */ BOSSGOMA_LIMB_L_LEG_ROOT, + /* 68 */ BOSSGOMA_LIMB_L_THIGH_ROOT, + /* 69 */ BOSSGOMA_LIMB_L_LEG_LOWER_ROOT, + /* 70 */ BOSSGOMA_LIMB_L_FEET_ROOT, + /* 71 */ BOSSGOMA_LIMB_L_FEET_BACK_ROOT1, + /* 72 */ BOSSGOMA_LIMB_L_FEET_BACK_ROOT2, + /* 73 */ BOSSGOMA_LIMB_L_FEET_BACK, + /* 74 */ BOSSGOMA_LIMB_L_FEET, + /* 75 */ BOSSGOMA_LIMB_L_SHIN, + /* 76 */ BOSSGOMA_LIMB_L_KNEE_ROOT1, + /* 77 */ BOSSGOMA_LIMB_L_KNEE_ROOT2, + /* 78 */ BOSSGOMA_LIMB_L_KNEE, + /* 79 */ BOSSGOMA_LIMB_L_THIGH_SHELL_ROOT1, + /* 80 */ BOSSGOMA_LIMB_L_THIGH_SHELL_ROOT2, + /* 81 */ BOSSGOMA_LIMB_L_THIGH_SHELL, + /* 82 */ BOSSGOMA_LIMB_L_THIGH, + /* 83 */ BOSSGOMA_LIMB_BODY_SHELL_BACK_ROOT1, + /* 84 */ BOSSGOMA_LIMB_BODY_SHELL_BACK_ROOT2, + /* 85 */ BOSSGOMA_LIMB_BODY_SHELL_BACK, + /* 86 */ BOSSGOMA_LIMB_MAX +} BossGomaLimb; + +typedef struct BossGoma { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelanime; + /* 0x0190 */ BossGomaActionFunc actionFunc; + /* 0x0194 */ s16 frameCount; // also used as a timer + /* 0x0196 */ s16 patienceTimer; // when non-0: walk towards player, can be stunned + /* 0x0198 */ s16 eyeLidBottomRotX; + /* 0x019A */ s16 eyeLidTopRotX; + /* 0x019C */ s16 eyeClosedTimer; // also used as a second invincibility frames source + /* 0x019E */ s16 eyeIrisRotX; + /* 0x01A0 */ s16 eyeIrisRotY; + /* 0x01A2 */ s16 unusedTimer; + /* 0x01A4 */ s16 childrenGohmaState[3]; // 0 not spawned, 1 spawned, -1 dead (-1 set by child gohma) + /* 0x01AA */ s16 tailLimbsScaleTimers[4]; + /* 0x01B2 */ s16 spawnGohmasActionTimer; + /* 0x01B4 */ s16 eyeState; + /* 0x01B6 */ s16 doNotMoveThisFrame; + /* 0x01B8 */ s16 visualState; + /* 0x01BA */ s16 invincibilityFrames; + /* 0x01BC */ s16 subCameraId; + /* 0x01BE */ s16 disableGameplayLogic; + /* 0x01C0 */ s16 decayingProgress; // when defeated, textures are progressively cleared + /* 0x01C2 */ s16 noBackfaceCulling; + /* 0x01C4 */ s16 blinkTimer; + /* 0x01C6 */ s16 lookedAtFrames; + /* 0x01C8 */ char unk_1C8[0x8]; + /* 0x01D0 */ s16 actionState; + /* 0x01D2 */ s16 framesUntilNextAction; // not always used as named + /* 0x01D4 */ s16 timer; + /* 0x01D6 */ s16 sfxFaintTimer; + /* 0x01D8 */ char unk_1D8[0x10]; + /* 0x01E8 */ f32 tailLimbsScale[4]; + /* 0x01F8 */ f32 eyeIrisScaleX; + /* 0x01FC */ f32 unusedInitX; + /* 0x0200 */ f32 unusedInitZ; + /* 0x0204 */ f32 mainEnvColor[3]; + /* 0x0210 */ f32 eyeEnvColor[3]; + /* 0x021C */ f32 currentAnimFrameCount; // not used consistently + /* 0x0220 */ f32 subCameraFollowSpeed; + /* 0x0224 */ f32 eyeIrisScaleY; + /* 0x0228 */ f32 defeatedCameraEyeDist; + /* 0x022C */ f32 defeatedCameraEyeAngle; + /* 0x0230 */ char unk_230[0x30]; + /* 0x0260 */ Vec3f lastTailLimbWorldPos; + /* 0x026C */ Vec3f firstTailLimbWorldPos; + /* 0x0278 */ Vec3f rightHandBackLimbWorldPos; + /* 0x0284 */ Vec3f leftHandBackLimbWorldPos; + /* 0x0290 */ Vec3f subCameraEye; + /* 0x029C */ Vec3f subCameraAt; + /* 0x02A8 */ Vec3f defeatedLimbPositions[100]; // only 85/86 first indices actually used + /* 0x0758 */ u8 deadLimbsState[100]; // only 85/90 first indices actually used + /* 0x07BC */ ColliderJntSph collider; + /* 0x07DC */ ColliderJntSphElement colliderItems[13]; +} BossGoma; // size = 0x0B1C + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c new file mode 100644 index 000000000..7bcfd7c50 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -0,0 +1,3594 @@ +/* + * File: z_boss_mo.c + * Overlay: ovl_Boss_Mo + * Description: Morpha + */ + +#include "z_boss_mo.h" +#include "objects/object_mo/object_mo.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "vt.h" + +#include + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +#define MO_WATER_LEVEL(globalCtx) globalCtx->colCtx.colHeader->waterBoxes[0].ySurface + +#define HAS_LINK(tent) \ + ((tent != NULL) && \ + ((tent->work[MO_TENT_ACTION_STATE] == MO_TENT_GRAB) || (tent->work[MO_TENT_ACTION_STATE] == MO_TENT_SHAKE))) + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f vel; + /* 0x18 */ Vec3f accel; + /* 0x24 */ u8 type; + /* 0x25 */ u8 timer; + /* 0x26 */ u8 stopTimer; + /* 0x28 */ s16 unk_28; // unused? + /* 0x2A */ s16 alpha; + /* 0x2C */ s16 rippleMode; + /* 0x2E */ s16 maxAlpha; + /* 0x30 */ f32 scale; + /* 0x30 */ f32 fwork[2]; + /* 0x3C */ Vec3f* targetPos; +} BossMoEffect; // size = 0x40 + +#define MO_FX_MAX_SIZE 0 +#define MO_FX_SHIMMER 0 +#define MO_FX_SUCTION 0 + +#define MO_FX_SPREAD_RATE 1 +#define MO_FX_STRETCH 1 +#define MO_FX_MAX_SCALE 1 + +void BossMo_Init(Actor* thisx, GlobalContext* globalCtx); +void BossMo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossMo_UpdateCore(Actor* thisx, GlobalContext* globalCtx); +void BossMo_UpdateTent(Actor* thisx, GlobalContext* globalCtx); +void BossMo_DrawCore(Actor* thisx, GlobalContext* globalCtx); +void BossMo_DrawTent(Actor* thisx, GlobalContext* globalCtx); + +void BossMo_UpdateEffects(BossMo* this, GlobalContext* globalCtx); +void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx); + +void BossMo_SetupTentacle(BossMo* this, GlobalContext* globalCtx); +void BossMo_Tentacle(BossMo* this, GlobalContext* globalCtx); + +void BossMo_Unknown(void); + +typedef enum { + /* 0 */ MO_FX_NONE, + /* 1 */ MO_FX_SMALL_RIPPLE, + /* 2 */ MO_FX_BIG_RIPPLE, + /* 3 */ MO_FX_DROPLET, + /* 4 */ MO_FX_SPLASH, + /* 5 */ MO_FX_SPLASH_TRAIL, + /* 6 */ MO_FX_WET_SPOT, + /* 7 */ MO_FX_BUBBLE +} BossMoEffectType; + +typedef enum { + /* 0 */ MO_TENT_READY, + /* 1 */ MO_TENT_SWING, + /* 2 */ MO_TENT_ATTACK, + /* 3 */ MO_TENT_CURL, + /* 4 */ MO_TENT_GRAB, + /* 5 */ MO_TENT_SHAKE, + /* 10 */ MO_TENT_WAIT = 10, + /* 11 */ MO_TENT_SPAWN, + /* 100 */ MO_TENT_CUT = 100, + /* 101 */ MO_TENT_RETREAT, + /* 102 */ MO_TENT_DESPAWN, + /* 200 */ MO_TENT_DEATH_START = 200, + /* 201 */ MO_TENT_DEATH_1, + /* 202 */ MO_TENT_DEATH_2, + /* 203 */ MO_TENT_DEATH_3, + /* 205 */ MO_TENT_DEATH_5 = 205, + /* 206 */ MO_TENT_DEATH_6 +} BossMoTentState; + +typedef enum { + /* -11 */ MO_CORE_UNUSED = -11, + /* 0 */ MO_CORE_MOVE = 0, + /* 1 */ MO_CORE_MAKE_TENT, + /* 2 */ MO_CORE_UNDERWATER, + /* 5 */ MO_CORE_STUNNED = 5, + /* 10 */ MO_CORE_ATTACK = 10, + /* 11 */ MO_CORE_RETREAT, + /* 20 */ MO_CORE_INTRO_WAIT = 20, + /* 21 */ MO_CORE_INTRO_REVEAL +} BossMoCoreState; + +typedef enum { + /* 0 */ MO_BATTLE, + /* 1 */ MO_INTRO_WAIT, + /* 2 */ MO_INTRO_START, + /* 3 */ MO_INTRO_SWIM, + /* 4 */ MO_INTRO_REVEAL, + /* 5 */ MO_INTRO_FINISH, + /* 100 */ MO_DEATH_START = 100, + /* 101 */ MO_DEATH_DRAIN_WATER_1, + /* 102 */ MO_DEATH_DRAIN_WATER_2, + /* 103 */ MO_DEATH_CEILING, + /* 104 */ MO_DEATH_DROPLET, + /* 105 */ MO_DEATH_FINISH, + /* 150 */ MO_DEATH_MO_CORE_BURST = 150 +} BossMoCsState; + +const ActorInit Boss_Mo_InitVars = { + ACTOR_BOSS_MO, + ACTORCAT_BOSS, + FLAGS, + OBJECT_MO, + sizeof(BossMo), + (ActorFunc)BossMo_Init, + (ActorFunc)BossMo_Destroy, + (ActorFunc)BossMo_UpdateTent, + (ActorFunc)BossMo_DrawTent, + NULL, +}; + +static BossMo* sMorphaCore = NULL; +static BossMo* sMorphaTent1 = NULL; +static BossMo* sMorphaTent2 = NULL; + +static f32 sFlatWidth[41] = { + 15.0f, 12.0f, 9.0f, 6.5f, 4.8f, 4.0f, 3.4f, 3.1f, 3.0f, 3.1f, 3.2f, 3.4f, 3.6f, 3.8f, + 4.0f, 4.6f, 5.1f, 5.5f, 6.1f, 6.6f, 7.3f, 7.7f, 8.4f, 8.5f, 8.7f, 8.8f, 8.8f, 8.7f, + 8.6f, 8.3f, 8.2f, 8.1f, 7.2f, 6.7f, 5.9f, 4.9f, 2.7f, 0.0f, 0.0f, 0.0f, 0.0f, +}; + +#include "z_boss_mo_colchk.c" + +static BossMoEffect sEffects[300]; +static s32 sSeed1; +static s32 sSeed2; +static s32 sSeed3; + +void BossMo_InitRand(s32 seedInit0, s32 seedInit1, s32 seedInit2) { + sSeed1 = seedInit0; + sSeed2 = seedInit1; + sSeed3 = seedInit2; +} + +f32 BossMo_RandZeroOne(void) { + // Wichmann-Hill algorithm + f32 randFloat; + + sSeed1 = (sSeed1 * 171) % 30269; + sSeed2 = (sSeed2 * 172) % 30307; + sSeed3 = (sSeed3 * 170) % 30323; + + randFloat = (sSeed1 / 30269.0f) + (sSeed2 / 30307.0f) + (sSeed3 / 30323.0f); + while (randFloat >= 1.0f) { + randFloat -= 1.0f; + } + return fabsf(randFloat); +} + +s32 BossMo_NearLand(Vec3f* pos, f32 margin) { + if (450.0f - margin <= fabsf(pos->x)) { + return true; + } + if (450.0f - margin <= fabsf(pos->z)) { + return true; + } + if ((fabsf(pos->x - 180.0f) < 90.0f + margin) || (fabsf(pos->x - -180.0f) < 90.0f + margin)) { + if (fabsf(pos->z - 180.0f) < 90.0f + margin) { + return true; + } + if (fabsf(pos->z - -180.0f) < 90.0f + margin) { + return true; + } + } + return false; +} + +void BossMo_SpawnRipple(BossMoEffect* effect, Vec3f* pos, f32 scale, f32 maxScale, s16 maxAlpha, s16 partLimit, + u8 type) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s16 i; + + for (i = 0; i < partLimit; i++, effect++) { + if (effect->type == MO_FX_NONE) { + effect->stopTimer = 0; + effect->type = type; + effect->pos = *pos; + effect->vel = zeroVec; + effect->accel = zeroVec; + effect->scale = scale * 0.0025f; + effect->fwork[MO_FX_MAX_SIZE] = maxScale * 0.0025f; + if (scale > 300.0f) { + effect->alpha = 0; + effect->maxAlpha = maxAlpha; + effect->rippleMode = 0; + effect->fwork[MO_FX_SPREAD_RATE] = (effect->fwork[MO_FX_MAX_SIZE] - effect->scale) * 0.05f; + } else { + effect->alpha = maxAlpha; + effect->rippleMode = 1; + effect->fwork[MO_FX_SPREAD_RATE] = (effect->fwork[MO_FX_MAX_SIZE] - effect->scale) * 0.1f; + } + break; + } + } +} + +void BossMo_SpawnDroplet(s16 type, BossMoEffect* effect, Vec3f* pos, Vec3f* vel, f32 scale) { + s16 i; + Vec3f gravity = { 0.0f, -1.0f, 0.0f }; + + for (i = 0; i < 290; i++, effect++) { + if (effect->type == MO_FX_NONE) { + effect->type = type; + effect->pos = *pos; + effect->vel = *vel; + effect->accel = gravity; + if (type == MO_FX_SPLASH_TRAIL) { + effect->accel.y = 0.0f; + } + effect->scale = scale; + effect->fwork[MO_FX_SPREAD_RATE] = 1.0f; + effect->stopTimer = 0; + break; + } + } +} + +void BossMo_SpawnStillDroplet(BossMoEffect* effect, Vec3f* pos, f32 scale) { + s16 i; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < 290; i++, effect++) { + if (effect->type == MO_FX_NONE) { + effect->type = MO_FX_DROPLET; + effect->stopTimer = 2; + effect->pos = *pos; + effect->vel = zeroVec; + effect->accel = zeroVec; + effect->scale = scale; + effect->fwork[MO_FX_SPREAD_RATE] = 1.0f; + break; + } + } +} + +void BossMo_SpawnBubble(BossMoEffect* effect, Vec3f* pos, Vec3f* vel, Vec3f* accel, f32 scale, Vec3f* targetPos) { + s16 i; + + for (i = 0; i < 280; i++, effect++) { + if (effect->type == MO_FX_NONE) { + effect->type = MO_FX_BUBBLE; + effect->stopTimer = 0; + effect->pos = *pos; + effect->vel = *vel; + effect->accel = *accel; + effect->scale = scale; + effect->fwork[MO_FX_SUCTION] = 0.0f; + effect->targetPos = targetPos; + if (targetPos == NULL) { + effect->alpha = 255; + } else { + effect->alpha = 0; + } + effect->timer = 0; + break; + } + } +} + +static s16 sCurlRot[41] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 12, 15, 15, 15, 15, 15, 15, 15, 20, 20, 20, 0, 0, 0, 0, 0, 0, +}; +static s16 sGrabRot[41] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5, -5, -5, + 0, 5, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, +}; +static s16 sAttackRot[41] = { + 0, 5, 6, 7, 8, 8, 7, 6, 6, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x25, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +static Vec3f sAudioZeroVec = { 0.0f, 0.0f, 0.0f }; + +static u8 sTentSpawnIndex[21] = { 0, 1, 2, 3, 4, 15, 19, 5, 14, 16, 17, 18, 6, 13, 20, 7, 12, 11, 10, 9, 8 }; + +static Vec2f sTentSpawnPos[21] = { + { -360.0f, -360.0f }, { -180.0f, -360.0f }, { 0.0f, -360.0f }, { 180.0f, -360.0f }, { 360.0f, -360.0f }, + { -360.0f, -180.0f }, { 0.0f, -180.0f }, { 360.0f, -180.0f }, { -360.0f, 0.0f }, { -180.0f, 0.0f }, + { 0.0f, 0.0f }, { 180.0f, 0.0f }, { 360.0f, 0.0f }, { -360.0f, 180.0f }, { 0.0f, 180.0f }, + { 360.0f, 180.0f }, { -360.0f, 360.0f }, { -180.0f, 360.0f }, { 0.0f, 360.0f }, { 180.0f, 360.0f }, + { 360.0f, 360.0f }, +}; + +static f32 sTentWidth[41] = { + 3.56f, 3.25f, 2.96f, 2.69f, 2.44f, 2.21f, 2.0f, 1.81f, 1.64f, 1.49f, 1.36f, 1.25f, 1.16f, 1.09f, + 1.04f, 1.01f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 0.98f, 0.95f, 0.9f, 0.8f, 0.6f, 1.0f, 1.0f, 1.0f, 1.0f, +}; + +static f32 sDropletWidth[41] = { + 0.0f, 2.95804f, 4.123106f, 4.974937f, 5.656854f, 6.22495f, 6.708204f, 7.123903f, 7.483315f, + 7.794229f, 8.062258f, 8.291562f, 8.485281f, 8.645808f, 8.774964f, 8.87412f, 8.944272f, 8.9861f, + 9.0f, 8.9861f, 8.944272f, 8.87412f, 8.774964f, 8.645808f, 8.485281f, 8.291562f, 8.062258f, + 7.794229f, 7.483315f, 7.123903f, 6.708204f, 6.22495f, 5.656854f, 4.974937f, 4.123106f, 2.95804f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, +}; // These are sqrt(9^2 - (i/2 - 9)^2), a sphere of radius 9. + +void BossMo_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossMo* this = (BossMo*)thisx; + u16 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + if (this->actor.params != BOSSMO_TENTACLE) { + Flags_SetSwitch(globalCtx, 0x14); + sMorphaCore = this; + MO_WATER_LEVEL(globalCtx) = this->waterLevel = MO_WATER_LEVEL(globalCtx); + globalCtx->roomCtx.unk_74[0] = 0xA0; + globalCtx->specialEffects = sEffects; + for (i = 0; i < ARRAY_COUNT(sEffects); i++) { + sEffects[i].type = MO_FX_NONE; + } + this->actor.world.pos.x = 200.0f; + this->actor.world.pos.y = MO_WATER_LEVEL(globalCtx) + 50.0f; + this->fwork[MO_TENT_SWING_SIZE_X] = 5.0f; + this->drawActor = true; + this->actor.colChkInfo.health = 20; + this->actor.colChkInfo.mass = 0; + this->actor.params = 0; + Actor_SetScale(&this->actor, 0.01f); + Collider_InitCylinder(globalCtx, &this->coreCollider); + Collider_SetCylinder(globalCtx, &this->coreCollider, &this->actor, &sCylinderInit); + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + Actor_Kill(&this->actor); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 0.0f, -280.0f, 0.0f, 0, + 0, 0, WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, -200.0f, -280.0f, 0.0f, 0, 0, 0, 0); + globalCtx->roomCtx.unk_74[0] = 0xFF; + MO_WATER_LEVEL(globalCtx) = -500; + return; + } + if (gSaveContext.eventChkInf[7] & 0x10) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + this->tentMaxAngle = 5.0f; + this->timers[0] = 50; + } else { + this->csState = MO_INTRO_WAIT; + this->work[MO_TENT_ACTION_STATE] = MO_CORE_INTRO_WAIT; + this->actor.world.pos.x = 1000.0f; + this->timers[0] = 60; + } + sMorphaTent1 = (BossMo*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_MO, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, BOSSMO_TENTACLE); + this->actor.draw = BossMo_DrawCore; + this->actor.update = BossMo_UpdateCore; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_BOSS); + } else { + Actor_SetScale(&this->actor, 0.01f); + BossMo_SetupTentacle(this, globalCtx); + this->actor.colChkInfo.mass = 0xFF; + MO_WATER_LEVEL(globalCtx) = -50; + this->waterTexAlpha = 90.0f; + this->actor.world.pos.y = MO_WATER_LEVEL(globalCtx); + this->actor.prevPos = this->targetPos = this->actor.world.pos; + Collider_InitJntSph(globalCtx, &this->tentCollider); + Collider_SetJntSph(globalCtx, &this->tentCollider, &this->actor, &sJntSphInit, this->tentElements); + this->tentMaxAngle = 1.0f; + } +} + +void BossMo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossMo* this = (BossMo*)thisx; + + if (this->actor.params >= BOSSMO_TENTACLE) { + Collider_DestroyJntSph(globalCtx, &this->tentCollider); + } else { + Collider_DestroyCylinder(globalCtx, &this->coreCollider); + } +} + +void BossMo_SetupTentacle(BossMo* this, GlobalContext* globalCtx) { + this->actionFunc = BossMo_Tentacle; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_WAIT; + this->timers[0] = 50 + (s16)Rand_ZeroFloat(20.0f); +} + +void BossMo_Tentacle(BossMo* this, GlobalContext* globalCtx) { + s16 tentXrot; + s16 sp1B4 = 0; + s32 buttons; + Player* player = GET_PLAYER(globalCtx); + s16 indS0; + s16 indS1; + Camera* camera1; + Camera* camera2; + BossMo* otherTent = (BossMo*)this->otherTent; + f32 maxSwingRateX; + f32 maxSwingLagX; + f32 maxSwingSizeX; + f32 maxSwingRateZ; + f32 maxSwingLagZ; + f32 maxSwingSizeZ; + f32 swingRateAccel; + f32 swingSizeAccel; + s16 rippleCount; + s16 indT5; + Vec3f ripplePos; + f32 randAngle; + f32 randFloat; + f32 tempf1; + f32 tempf2; + f32 sin; + f32 cos; + f32 temp; + f32 dx; + f32 dy; + f32 dz; + Vec3f sp138; + Vec3f sp12C; + Vec3f sp120; + s32 pad11C; + s32 pad118; + s32 pad114; + s32 pad110; + s32 pad10C; + s32 pad108; + Vec3f spFC; + Vec3f spF0; + f32 padEC; + Vec3f spE0; + Vec3f spD4; + Vec3f spC8; + + if (this->work[MO_TENT_ACTION_STATE] <= MO_TENT_DEATH_3) { + this->actor.world.pos.y = MO_WATER_LEVEL(globalCtx); + } + if ((this->work[MO_TENT_ACTION_STATE] == MO_TENT_READY) || + (this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_START) || + (this->work[MO_TENT_ACTION_STATE] == MO_TENT_RETREAT) || (this->work[MO_TENT_ACTION_STATE] == MO_TENT_SWING) || + (this->work[MO_TENT_ACTION_STATE] == MO_TENT_SHAKE)) { + if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_READY) { + if (sMorphaCore->csState != MO_BATTLE) { + maxSwingRateX = 2000.0f; + maxSwingLagX = 3000.0f; + maxSwingSizeX = 1000.0f; + maxSwingRateZ = 1500.0f; + maxSwingLagZ = 2500.0f; + maxSwingSizeZ = 1000.0f; + swingRateAccel = 10.0f; + swingSizeAccel = 10.0f; + } else { + maxSwingRateX = 2000.0f; + maxSwingLagX = 3000.0f; + maxSwingSizeX = 1000.0f; + maxSwingRateZ = 1500.0f; + maxSwingLagZ = 2500.0f; + maxSwingSizeZ = 1000.0f; + swingRateAccel = 20.0f; + swingSizeAccel = 30.0f; + } + } else if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_SWING) { + maxSwingRateX = 2500.0f; + maxSwingLagX = -1000.0f; + maxSwingSizeX = 3000.0f; + maxSwingRateZ = 1500.0f; + maxSwingLagZ = 2500.0f; + maxSwingSizeZ = 0.0; + swingRateAccel = 30.0f; + swingSizeAccel = 60.0f; + if (((this->sfxTimer % 16) == 0) && (this->timers[0] < 30)) { + Audio_PlaySoundIncreasinglyTransposed(&this->tentTipPos, NA_SE_EN_MOFER_WAVE, gMorphaTransposeTable); + } + } else if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_SHAKE) { + if (this->timers[0] > 40) { + maxSwingRateX = 1300.0f; + maxSwingLagX = -3200.0f; + maxSwingSizeX = 7000.0f; + maxSwingRateZ = 800.0f; + maxSwingLagZ = 2500.0f; + maxSwingSizeZ = 5000.0f; + swingRateAccel = 30.0f; + swingSizeAccel = 60.0f; + if ((this->sfxTimer % 32) == 0) { + Audio_PlaySoundIncreasinglyTransposed(&this->tentTipPos, NA_SE_EN_MOFER_WAVE, + gMorphaTransposeTable); + func_800AA000(0, 100, 5, 2); + func_8002F7DC(&player->actor, NA_SE_VO_LI_FREEZE + player->ageProperties->unk_92); + } + } else { + maxSwingRateX = 2000.0f; + maxSwingLagX = -1000.0f; + maxSwingSizeX = 5000.0f; + maxSwingRateZ = 1500.0f; + maxSwingLagZ = 2500.0f; + maxSwingSizeZ = 100.0f; + swingRateAccel = 70.0f; + swingSizeAccel = 70.0f; + if ((this->sfxTimer % 16) == 0) { + Audio_PlaySoundIncreasinglyTransposed(&this->tentTipPos, NA_SE_EN_MOFER_WAVE, + gMorphaTransposeTable); + func_800AA000(0, 160, 5, 4); + func_8002F7DC(&player->actor, NA_SE_VO_LI_FREEZE + player->ageProperties->unk_92); + } + } + } else if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_RETREAT) { + maxSwingRateX = 1300.0f; + maxSwingLagX = 3200.0f; + maxSwingSizeX = 7000.0f; + maxSwingRateZ = 800.0f; + maxSwingLagZ = 2500.0f; + maxSwingSizeZ = 5000.0f; + swingRateAccel = 30.0f; + swingSizeAccel = 30.0f; + } else if (this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_START) { + maxSwingRateX = -400.0f; + maxSwingLagX = -3200.0f; + maxSwingSizeX = 0.0f; + maxSwingRateZ = 2300.0f; + maxSwingLagZ = 3200.0f; + maxSwingSizeZ = 1000.0; + swingRateAccel = 30.0f; + swingSizeAccel = 60.0f; + } + Math_ApproachF(&this->fwork[MO_TENT_SWING_RATE_X], maxSwingRateX, 1.0f, swingRateAccel); + Math_ApproachF(&this->fwork[MO_TENT_SWING_LAG_X], maxSwingLagX, 1.0f, 30.0f); + Math_ApproachF(&this->fwork[MO_TENT_SWING_SIZE_X], maxSwingSizeX, 1.0f, swingSizeAccel); + Math_ApproachF(&this->fwork[MO_TENT_SWING_RATE_Z], maxSwingRateZ, 1.0f, swingRateAccel); + Math_ApproachF(&this->fwork[MO_TENT_SWING_LAG_Z], maxSwingLagZ, 1.0f, 30.0f); + Math_ApproachF(&this->fwork[MO_TENT_SWING_SIZE_Z], maxSwingSizeZ, 1.0f, swingSizeAccel); + this->xSwing += (s16)this->fwork[MO_TENT_SWING_RATE_X]; + this->zSwing += (s16)this->fwork[MO_TENT_SWING_RATE_Z]; + } + switch (this->work[MO_TENT_ACTION_STATE]) { + case MO_TENT_WAIT: + this->actor.flags &= ~ACTOR_FLAG_0; + if (this == sMorphaTent2) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_SPAWN; + this->timers[0] = 70; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + } + break; + case MO_TENT_SPAWN: + this->drawActor = true; + this->baseBubblesTimer = 20; + if (this->timers[0] < 20) { + Math_ApproachF(&this->tentRippleSize, 0.15f, 0.5f, 0.01); + Math_ApproachF(&this->baseAlpha, 150.0f, 1.0f, 5.0f); + if (this->baseAlpha >= 150.0f) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_READY; + this->timers[0] = 60; + } + } + if (this->timers[0] > 50) { + rippleCount = 1; + } else if (this->timers[0] > 40) { + rippleCount = 3; + } else if (this->timers[0] > 30) { + rippleCount = 5; + } else if (this->timers[0] > 20) { + rippleCount = 8; + } else { + rippleCount = 3; + } + for (indS1 = 0; indS1 < rippleCount; indS1++) { + randFloat = Rand_ZeroFloat(50.0f); + randAngle = Rand_ZeroFloat(0x10000); + ripplePos = this->actor.world.pos; + ripplePos.x += sinf(randAngle) * randFloat; + ripplePos.z += cosf(randAngle) * randFloat; + ripplePos.y = MO_WATER_LEVEL(globalCtx); + BossMo_SpawnRipple(globalCtx->specialEffects, &ripplePos, 40.0f, 110.0f, 80, 290, MO_FX_SMALL_RIPPLE); + } + break; + case MO_TENT_READY: + case MO_TENT_SWING: + if (sMorphaCore->csState == MO_BATTLE) { + func_80078914(&this->tentTipPos, NA_SE_EN_MOFER_APPEAR - SFX_FLAG); + } + Math_ApproachF(&this->waterLevelMod, -5.0f, 0.1f, 0.4f); + for (indS1 = 0; indS1 < 41; indS1++) { + + sin = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_X] * indS1) + this->xSwing); + tempf1 = this->fwork[MO_TENT_SWING_SIZE_X] * (indS1 * 0.025f * sin); + + cos = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_Z] * indS1) + this->zSwing); + tempf2 = this->fwork[MO_TENT_SWING_SIZE_Z] * (indS1 * 0.025f * cos); + + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.1f, 0.4f); + if (indS1 == 28) { + sp1B4 = this->tentRot[indS1].x; + } + Math_ApproachS(&this->tentRot[indS1].x, tempf1, 1.0f / this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, tempf2, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + this->targetPos = this->actor.world.pos; + Math_ApproachF(&this->actor.speedXZ, 0.75f, 1.0f, 0.04f); + if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_SWING) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + this->attackAngleMod, 0xA, + 0x1F4); + } + Math_ApproachF(&this->fwork[MO_TENT_MAX_STRETCH], 1.0f, 0.5f, 0.04); + if (sMorphaCore->csState != MO_BATTLE) { + Math_ApproachF(&this->tentMaxAngle, 1.0f, 1.0f, 0.001f); + Math_ApproachF(&this->tentSpeed, 240.0f, 1.0f, 3.0); + } else { + Math_ApproachF(&this->tentMaxAngle, 1.0f, 1.0f, 0.002f); + Math_ApproachF(&this->tentSpeed, 400.0f, 1.0f, 6.0f); + } + if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_READY) { + if ((this->timers[0] == 0) && !HAS_LINK(otherTent)) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_SWING; + this->timers[0] = 50; + Audio_ResetIncreasingTranspose(); + this->attackAngleMod = Rand_CenteredFloat(0x1000); + } + } else { + tentXrot = this->tentRot[28].x; + if ((this->timers[0] == 0) && (tentXrot >= 0) && (sp1B4 < 0)) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_ATTACK; + if (this == sMorphaTent1) { + this->timers[0] = 175; + } else { + this->timers[0] = 55; + } + } + } + break; + case MO_TENT_ATTACK: + this->actor.flags |= ACTOR_FLAG_24; + func_80078914(&this->tentTipPos, NA_SE_EN_MOFER_ATTACK - SFX_FLAG); + Math_ApproachF(&this->waterLevelMod, -5.0f, 0.1f, 0.4f); + for (indS1 = 0; indS1 < 41; indS1++) { + Math_ApproachF(&this->tentStretch[indS1].y, + this->fwork[MO_TENT_MAX_STRETCH] * ((((40 - indS1) * 25.0f) / 100.0f) + 5.0f), 0.5f, + 0.7f); + Math_ApproachS(&this->tentRot[indS1].x, sAttackRot[indS1] * 0x100, 1.0f / this->tentMaxAngle, + this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, 0, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + this->targetPos = this->actor.world.pos; + Math_ApproachF(&this->tentMaxAngle, 0.5f, 1.0f, 0.01); + Math_ApproachF(&this->tentSpeed, 160.0f, 1.0f, 50.0f); + if ((this->timers[0] == 0) || (this->linkHitTimer != 0)) { + dx = this->tentPos[22].x - player->actor.world.pos.x; + dy = this->tentPos[22].y - player->actor.world.pos.y; + dz = this->tentPos[22].z - player->actor.world.pos.z; + if ((fabsf(dy) < 50.0f) && !HAS_LINK(otherTent) && (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 120.0f)) { + this->tentMaxAngle = .001f; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_CURL; + this->timers[0] = 40; + this->tentSpeed = 0; + if ((s16)(this->actor.shape.rot.y - this->actor.yawTowardsPlayer) >= 0) { + this->linkToLeft = false; + } else { + this->linkToLeft = true; + } + } else { + this->tentMaxAngle = .001f; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_READY; + this->tentSpeed = 0; + this->fwork[MO_TENT_SWING_RATE_X] = 0; + this->fwork[MO_TENT_SWING_RATE_Z] = 0; + this->fwork[MO_TENT_SWING_SIZE_X] = 0; + this->fwork[MO_TENT_SWING_SIZE_Z] = 0; + this->timers[0] = 30; + if ((fabsf(player->actor.world.pos.x - this->actor.world.pos.x) > 300.0f) || + (player->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) || HAS_LINK(otherTent) || + (fabsf(player->actor.world.pos.z - this->actor.world.pos.z) > 300.0f)) { + + this->work[MO_TENT_ACTION_STATE] = MO_TENT_RETREAT; + this->timers[0] = 75; + } + } + } + break; + case MO_TENT_CURL: + case MO_TENT_GRAB: + Math_ApproachF(&this->waterLevelMod, -5.0f, 0.1f, 0.4f); + if (this->timers[0] == 125) { + this->tentMaxAngle = .001f; + this->tentSpeed = 0; + } + for (indS1 = 0; indS1 < 41; indS1++) { + if (this->timers[0] > 25) { + if (!this->linkToLeft) { + Math_ApproachS(&this->tentRot[indS1].z, sCurlRot[indS1] * 0x100, 1.0f / this->tentMaxAngle, + this->tentSpeed); + } else { + Math_ApproachS(&this->tentRot[indS1].z, sCurlRot[indS1] * -0x100, 1.0f / this->tentMaxAngle, + this->tentSpeed); + } + } else { + if (!this->linkToLeft) { + Math_ApproachS(&this->tentRot[indS1].z, sGrabRot[indS1] * 0x100, 1.0f / this->tentMaxAngle, + this->tentSpeed); + } else { + Math_ApproachS(&this->tentRot[indS1].z, sGrabRot[indS1] * -0x100, 1.0f / this->tentMaxAngle, + this->tentSpeed); + } + } + } + Math_ApproachF(&this->tentMaxAngle, 0.1f, 1.0f, 0.01f); + Math_ApproachF(&this->tentSpeed, 960.0f, 1.0f, 30.0f); + if (this->timers[0] >= 30) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0xC8); + } + if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_CURL) { + if ((this->timers[0] >= 5) && (this->linkHitTimer != 0) && (player->actor.parent == NULL)) { + if (globalCtx->grabPlayer(globalCtx, player)) { + player->actor.parent = &this->actor; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_GRAB; + func_80078914(&this->tentTipPos, NA_SE_EN_MOFER_CATCH); + Audio_PlaySoundGeneral(NA_SE_VO_LI_DAMAGE_S, &player->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_READY; + this->tentMaxAngle = .001f; + this->tentSpeed = 0; + this->fwork[MO_TENT_SWING_SIZE_Z] = 0; + this->fwork[MO_TENT_SWING_SIZE_X] = 0; + this->fwork[MO_TENT_SWING_RATE_Z] = 0; + this->fwork[MO_TENT_SWING_RATE_X] = 0; + this->timers[0] = 30; + } + } + if (this->timers[0] == 4) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_READY; + this->tentMaxAngle = .001f; + this->tentSpeed = 0; + this->fwork[MO_TENT_SWING_SIZE_Z] = 0; + this->fwork[MO_TENT_SWING_SIZE_X] = 0; + this->fwork[MO_TENT_SWING_RATE_Z] = 0; + this->fwork[MO_TENT_SWING_RATE_X] = 0; + this->timers[0] = 30; + } + } + if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_GRAB) { + player->unk_850 = 0xA; + player->actor.speedXZ = player->actor.velocity.y = 0; + Math_ApproachF(&player->actor.world.pos.x, this->grabPosRot.pos.x, 0.5f, 20.0f); + Math_ApproachF(&player->actor.world.pos.y, this->grabPosRot.pos.y, 0.5f, 20.0f); + Math_ApproachF(&player->actor.world.pos.z, this->grabPosRot.pos.z, 0.5f, 20.0f); + Math_ApproachS(&player->actor.shape.rot.x, this->grabPosRot.rot.x, 2, 0x7D0); + Math_ApproachS(&player->actor.shape.rot.y, this->grabPosRot.rot.y, 2, 0x7D0); + Math_ApproachS(&player->actor.shape.rot.z, this->grabPosRot.rot.z, 2, 0x7D0); + if (this->timers[0] == 0) { + camera1 = Gameplay_GetCamera(globalCtx, MAIN_CAM); + this->work[MO_TENT_ACTION_STATE] = MO_TENT_SHAKE; + this->tentMaxAngle = .001f; + this->fwork[MO_TENT_SWING_RATE_X] = this->fwork[MO_TENT_SWING_RATE_Z] = + this->fwork[MO_TENT_SWING_SIZE_X] = this->fwork[MO_TENT_SWING_SIZE_Z] = this->tentSpeed = 0; + this->timers[0] = 150; + this->mashCounter = 0; + this->sfxTimer = 30; + Audio_ResetIncreasingTranspose(); + func_80064520(globalCtx, &globalCtx->csCtx); + this->csCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->csCamera, CAM_STAT_ACTIVE); + this->cameraEye = camera1->eye; + this->cameraAt = camera1->at; + this->cameraYaw = Math_FAtan2F(this->cameraEye.x - this->actor.world.pos.x, + this->cameraEye.z - this->actor.world.pos.z); + this->cameraYawRate = 0; + goto tent_shake; + } + } + break; + tent_shake: + case MO_TENT_SHAKE: + if (this->timers[0] == 138) { + ShrinkWindow_SetVal(0); + Interface_ChangeAlpha(0xB); + } + if ((this->timers[0] % 8) == 0) { + globalCtx->damagePlayer(globalCtx, -1); + } + Math_ApproachF(&this->waterLevelMod, -5.0f, 0.1f, 0.4f); + sp1B4 = this->tentRot[15].x; + buttons = globalCtx->state.input[0].press.button; + if (CHECK_BTN_ALL(buttons, BTN_A) || CHECK_BTN_ALL(buttons, BTN_B)) { + this->mashCounter++; + } + for (indS1 = 0; indS1 < 41; indS1++) { + if (indS1 < 20) { + sin = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_X] * indS1) + this->xSwing); + tempf1 = this->fwork[MO_TENT_SWING_SIZE_X] * (indS1 * 0.025f * sin); + cos = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_Z] * indS1) + this->zSwing); + tempf2 = this->fwork[MO_TENT_SWING_SIZE_Z] * (indS1 * 0.025f * cos); + temp = ((((40 - indS1) * 25.0f) / 100.0f) + 5.0f); + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * temp, 0.1f, 0.1f); + Math_ApproachS(&this->tentRot[indS1].x, tempf1, 1.0f / this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, tempf2, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + } + player->unk_850 = 0xA; + player->actor.world.pos.x = this->grabPosRot.pos.x; + player->actor.world.pos.y = this->grabPosRot.pos.y; + player->actor.world.pos.z = this->grabPosRot.pos.z; + player->actor.world.rot.x = player->actor.shape.rot.x = this->grabPosRot.rot.x; + player->actor.world.rot.y = player->actor.shape.rot.y = this->grabPosRot.rot.y; + player->actor.world.rot.z = player->actor.shape.rot.z = this->grabPosRot.rot.z; + player->actor.velocity.y = 0; + player->actor.speedXZ = 0; + Math_ApproachF(&this->fwork[MO_TENT_MAX_STRETCH], 1.0f, 0.5f, 0.01); + Math_ApproachF(&this->tentMaxAngle, 0.5f, 1.0f, 0.005f); + Math_ApproachF(&this->tentSpeed, 480.0f, 1.0f, 10.0f); + Math_ApproachF(&this->tentPulse, 0.3f, 0.5f, 0.03f); + if ((this->mashCounter >= 40) || (this->timers[0] == 0)) { + tentXrot = this->tentRot[15].x; + if ((tentXrot < 0) && (sp1B4 >= 0)) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_RETREAT; + this->work[MO_TENT_INVINC_TIMER] = 50; + if (&this->actor == player->actor.parent) { + player->unk_850 = 0x65; + player->actor.parent = NULL; + player->csMode = 0; + if (this->timers[0] == 0) { + func_8002F6D4(globalCtx, &this->actor, 20.0f, this->actor.shape.rot.y + 0x8000, 10.0f, 0); + } + } + this->timers[0] = 75; + } + } + if (this->csCamera != 0) { + sp138.x = 0; + sp138.y = 100.0f; + sp138.z = 200.0f; + this->cameraYaw -= this->cameraYawRate; + Math_ApproachF(&this->cameraYawRate, 0.01, 1.0f, 0.002f); + Matrix_RotateY(this->cameraYaw, MTXMODE_NEW); + Matrix_MultVec3f(&sp138, &sp12C); + Math_ApproachF(&this->cameraEye.x, this->actor.world.pos.x + sp12C.x, 0.1f, 10.0f); + Math_ApproachF(&this->cameraEye.y, this->actor.world.pos.y + sp12C.y, 0.1f, 10.0f); + Math_ApproachF(&this->cameraEye.z, this->actor.world.pos.z + sp12C.z, 0.1f, 10.0f); + Math_ApproachF(&this->cameraAt.x, player->actor.world.pos.x, 0.5f, 50.0f); + Math_ApproachF(&this->cameraAt.y, player->actor.world.pos.y, 0.5f, 50.0f); + Math_ApproachF(&this->cameraAt.z, player->actor.world.pos.z, 0.5f, 50.0f); + Gameplay_CameraSetAtEye(globalCtx, this->csCamera, &this->cameraAt, &this->cameraEye); + } + break; + case MO_TENT_CUT: + func_80078914(&this->tentTipPos, NA_SE_EV_WATER_WALL - SFX_FLAG); + if (&this->actor == player->actor.parent) { + player->unk_850 = 0x65; + player->actor.parent = NULL; + player->csMode = 0; + } + Math_ApproachF(&this->tentRippleSize, 0.15f, 0.5f, 0.01); + if (this->meltIndex < 41) { + for (indS0 = 0; indS0 < 10; indS0++) { + sp120 = this->tentPos[this->meltIndex]; + sp120.x += Rand_CenteredFloat(30.0f); + sp120.y += Rand_CenteredFloat(30.0f); + sp120.z += Rand_CenteredFloat(30.0f); + BossMo_SpawnStillDroplet(globalCtx->specialEffects, &sp120, Rand_ZeroFloat(0.1f) + .2f); + } + this->meltIndex++; + } + Math_ApproachF(&this->cutScale, 0.0, 1.0f, 0.2f); + if ((this->meltIndex >= 41) || (this->timers[0] == 0)) { + this->work[MO_TENT_ACTION_STATE] = MO_TENT_RETREAT; + this->timers[0] = 75; + this->tentMaxAngle = 0.005f; + this->tentSpeed = 50.0f; + this->fwork[MO_TENT_SWING_SIZE_X] = 7000.0f; + this->fwork[MO_TENT_SWING_SIZE_Z] = 5000.0f; + } + break; + case MO_TENT_RETREAT: + if (this->csCamera != 0) { + Math_ApproachF(&this->cameraAt.x, player->actor.world.pos.x, 0.5f, 50.0f); + Math_ApproachF(&this->cameraAt.y, player->actor.world.pos.y, 0.5f, 50.0f); + Math_ApproachF(&this->cameraAt.z, player->actor.world.pos.z, 0.5f, 50.0f); + Gameplay_CameraSetAtEye(globalCtx, this->csCamera, &this->cameraAt, &this->cameraEye); + if (player->actor.world.pos.y <= 42.0f) { + camera2 = Gameplay_GetCamera(globalCtx, MAIN_CAM); + camera2->eye = this->cameraEye; + camera2->eyeNext = this->cameraEye; + camera2->at = this->cameraAt; + func_800C08AC(globalCtx, this->csCamera, 0); + this->csCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + } + } + for (indS1 = 0; indS1 < 41; indS1++) { + sin = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_X] * indS1) + this->xSwing); + tempf1 = (indS1 * 0.025f * sin * this->fwork[MO_TENT_SWING_SIZE_X]) * this->fwork[MO_TENT_MAX_STRETCH]; + cos = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_Z] * indS1) + this->zSwing); + tempf2 = (indS1 * 0.025f * cos * this->fwork[MO_TENT_SWING_SIZE_Z]) * this->fwork[MO_TENT_MAX_STRETCH]; + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.5f, 0.2f); + Math_ApproachS(&this->tentRot[indS1].x, tempf1, 1.0f / this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, tempf2, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + Math_ApproachF(&this->fwork[MO_TENT_MAX_STRETCH], 0, 0.5f, 0.02f); + Math_ApproachF(&this->tentMaxAngle, 0.5f, 1.0f, 0.01); + Math_ApproachF(&this->tentSpeed, 320.0f, 1.0f, 50.0f); + if (this->timers[0] == 0) { + this->actor.flags &= ~ACTOR_FLAG_0; + Math_ApproachF(&this->baseAlpha, 0.0, 1.0f, 5.0f); + for (indS1 = 0; indS1 < 40; indS1++) { + if (sMorphaTent2->tentSpawnPos) {} + indT5 = Rand_ZeroFloat(20.9f); + indS0 = sTentSpawnIndex[indT5]; + spFC.x = 0; + spFC.y = 0; + spFC.z = 0; + Matrix_RotateY((player->actor.world.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&spFC, &spF0); + spF0.x = player->actor.world.pos.x + spF0.x; + spF0.z = player->actor.world.pos.z + spF0.z; + if ((fabsf(spF0.x - sTentSpawnPos[indS0].x) <= 320) && + (fabsf(spF0.z - sTentSpawnPos[indS0].y) <= 320) && + ((sMorphaTent2 == NULL) || (sMorphaTent2->tentSpawnPos != indS0))) { + this->targetPos.x = sTentSpawnPos[indS0].x; + this->targetPos.z = sTentSpawnPos[indS0].y; + this->tentSpawnPos = indS0; + this->timers[0] = (s16)Rand_ZeroFloat(20.0f) + 30; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_DESPAWN; + break; + } + } + } + if ((this == sMorphaTent1) && (sMorphaCore->hitCount >= 3) && (sMorphaTent2 == NULL)) { + sMorphaTent2 = + (BossMo*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BOSS_MO, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, BOSSMO_TENTACLE); + + sMorphaTent2->tentSpawnPos = this->tentSpawnPos; + if (sMorphaTent2->tentSpawnPos > 10) { + sMorphaTent2->tentSpawnPos--; + } else { + sMorphaTent2->tentSpawnPos++; + } + sMorphaTent2->targetPos.x = sTentSpawnPos[sMorphaTent2->tentSpawnPos].x; + sMorphaTent2->targetPos.z = sTentSpawnPos[sMorphaTent2->tentSpawnPos].y; + sMorphaTent2->timers[0] = 100; + sMorphaTent2->work[MO_TENT_ACTION_STATE] = MO_TENT_DESPAWN; + sMorphaTent2->otherTent = &sMorphaTent1->actor; + sMorphaTent1->otherTent = &sMorphaTent2->actor; + } + break; + case MO_TENT_DESPAWN: + this->actor.flags &= ~ACTOR_FLAG_0; + Math_ApproachF(&this->baseAlpha, 0, 1.0f, 5.0f); + if ((this->baseAlpha <= 0.5f) && (this->timers[0] == 0)) { + this->meltIndex = 0; + this->actor.world.pos.x = this->targetPos.x; + this->actor.world.pos.z = this->targetPos.z; + this->actor.prevPos = this->actor.world.pos; + this->cutScale = 1.0f; + this->cutIndex = this->meltIndex; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_WAIT; + this->timers[0] = (s16)Rand_ZeroFloat(20.0f) + 10; + + this->tentSpeed = 0; + this->fwork[MO_TENT_SWING_RATE_X] = 0; + this->fwork[MO_TENT_SWING_RATE_Z] = 0; + this->fwork[MO_TENT_SWING_SIZE_X] = 0; + this->fwork[MO_TENT_SWING_SIZE_Z] = 0; + + this->tentMaxAngle = .001f; + } + break; + case MO_TENT_DEATH_START: + this->actor.shape.rot.y = 0x4000; + break; + case MO_TENT_DEATH_3: + this->baseBubblesTimer = 20; + Math_ApproachF(&sMorphaCore->waterLevel, -300.0f, 0.1f, 0.8f); + this->actor.flags &= ~ACTOR_FLAG_0; + for (indS1 = 0; indS1 < 41; indS1++) { + sin = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_X] * indS1) + this->xSwing); + tempf1 = this->fwork[MO_TENT_SWING_SIZE_X] * (indS1 * 0.025f * sin); + cos = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_Z] * indS1) + this->zSwing); + tempf2 = this->fwork[MO_TENT_SWING_SIZE_Z] * (indS1 * 0.025f * cos); + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.1f, 0.4f); + Math_ApproachS(&this->tentRot[indS1].x, tempf1, 1.0f / this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, tempf2, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + this->actor.speedXZ = 0.0; + Math_ApproachF(&this->fwork[MO_TENT_MAX_STRETCH], 4.3f, 0.5f, 0.04); + Math_ApproachF(&this->tentPulse, 1.3f, 0.5f, 0.05f); + break; + case MO_TENT_DEATH_1: + this->baseBubblesTimer = 20; + this->actor.shape.rot.y = 0x4000; + this->actor.shape.rot.x = -0x8000; + this->actor.world.pos.y = sMorphaCore->waterLevel + 650.0f; + Math_ApproachF(&sMorphaCore->waterLevel, -300.0f, 0.1f, 1.3f); + for (indS1 = 0; indS1 < 41; indS1++) { + sin = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_X] * indS1) + this->xSwing); + tempf1 = this->fwork[MO_TENT_SWING_SIZE_X] * (indS1 * 0.025f * sin); + cos = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_Z] * indS1) + this->zSwing); + tempf2 = this->fwork[MO_TENT_SWING_SIZE_Z] * (indS1 * 0.025f * cos); + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.1f, 0.4f); + Math_ApproachS(&this->tentRot[indS1].x, tempf1, 1.0f / this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, tempf2, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + this->actor.speedXZ = 0.0; + Math_ApproachF(&this->tentPulse, 1.3f, 0.5f, 0.05f); + break; + case MO_TENT_DEATH_2: + this->baseBubblesTimer = 20; + Math_ApproachF(&sMorphaCore->waterLevel, -295.0f, 0.1f, 1.3f); + this->actor.world.pos.y = sMorphaCore->waterLevel + 650.0f; + for (indS1 = 0; indS1 < 41; indS1++) { + sin = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_X] * indS1) + this->xSwing); + tempf1 = this->fwork[MO_TENT_SWING_SIZE_X] * (indS1 * 0.025f * sin); + cos = Math_SinS(((s16)this->fwork[MO_TENT_SWING_LAG_Z] * indS1) + this->zSwing); + tempf2 = this->fwork[MO_TENT_SWING_SIZE_Z] * (indS1 * 0.025f * cos); + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.1f, 0.4f); + Math_ApproachS(&this->tentRot[indS1].x, tempf1, 1.0f / this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->tentRot[indS1].z, tempf2, 1.0f / this->tentMaxAngle, this->tentSpeed); + } + this->actor.speedXZ = 0.0; + this->noBubbles--; + Math_ApproachF(&this->fwork[MO_TENT_MAX_STRETCH], 0.1f, 0.1f, 0.03); + Math_ApproachF(&this->tentPulse, 0.02f, 0.5f, 0.015f); + if ((this->timers[0] > 0) && (this->timers[0] < 40)) { + Math_ApproachF(&this->actor.scale.x, 0.035f, 0.05f, this->flattenRate); + if (this->timers[0] == 1) { + this->flattenRate = 0.0; + } + } else if (this->timers[0] == 0) { + Math_ApproachF(&this->actor.scale.x, .001f, 0.05f, this->flattenRate); + } + Math_ApproachF(&this->flattenRate, 0.00045f, 0.1f, 0.00001f); + break; + case MO_TENT_DEATH_5: + for (indS1 = 0; indS1 < 41; indS1++) { + if (this->timers[0] != 0) { + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.05f, + this->tentSpeed); + } else { + Math_ApproachF(&this->tentStretch[indS1].y, this->fwork[MO_TENT_MAX_STRETCH] * 5.0f, 0.3f, 100.0f); + } + this->tentRot[indS1].x = this->tentRot[indS1].z = 0; + } + this->tentPulse = 0.0; + if (this->timers[0] != 0) { + this->actor.world.pos.y = sMorphaCore->waterLevel + 650.0f; + this->fwork[MO_TENT_MAX_STRETCH] = 0.5f; + Math_ApproachF(&this->actor.scale.x, 0.0015f, 0.05f, this->tentMaxAngle); + Math_ApproachF(&this->tentMaxAngle, 0.00035f, 1.0f, 0.0000175f); + Math_ApproachF(&this->tentSpeed, 0.1f, 1.0f, 0.005f); + this->actor.velocity.y = 0.0; + } else { + this->fwork[MO_TENT_MAX_STRETCH] = 0.2f; + this->fwork[MO_TENT_MAX_STRETCH] += Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 0x2000) * 0.05f; + padEC = Math_CosS(this->work[MO_TENT_MOVE_TIMER] * 0x2000) * 0.0005f; + Math_ApproachF(&this->actor.scale.x, 0.002f + padEC, 0.5f, 0.0005f); + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.velocity.y -= 1.0f; + if (this->actor.world.pos.y < -250.0f) { + this->actor.world.pos.y = -250.0f; + this->actor.velocity.y = 0.0; + this->drawActor = false; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_DEATH_6; + this->timers[0] = 60; + func_80078914(&this->tentTipPos, NA_SE_EN_MOFER_CORE_JUMP); + for (indS1 = 0; indS1 < 300; indS1++) { + spC8.x = 0.0; + spC8.y = 0.0; + spC8.z = indS1 * 0.03f; + Matrix_RotateY(indS1 * 0.23f, MTXMODE_NEW); + Matrix_MultVec3f(&spC8, &spE0); + spE0.y = Rand_ZeroFloat(7.0f) + 4.0f; + spD4 = this->actor.world.pos; + spD4.x += spE0.x * 3.0f; + spD4.y += (spE0.y * 3.0f) - 30.0f; + if (spD4.y < -280.0f) { + spD4.y = -280.0f; + } + spD4.z += spE0.z * 3.0f; + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &spD4, &spE0, + ((300 - indS1) * .0015f) + 0.13f); + } + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, + this->actor.world.pos.x, -280.0f, this->actor.world.pos.z, 0, 0, 0, + WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 200.0f, + -280.0f, this->actor.world.pos.z, 0, 0, 0, 0); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + } + break; + case MO_TENT_DEATH_6: + break; + } + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + if (((this->work[MO_TENT_ACTION_STATE] == MO_TENT_ATTACK) || + (this->work[MO_TENT_ACTION_STATE] == MO_TENT_DEATH_2) || (this->work[MO_TENT_ACTION_STATE] == MO_TENT_CURL) || + (this->work[MO_TENT_ACTION_STATE] == MO_TENT_GRAB)) && + (Rand_ZeroOne() < 0.8f) && (this->actor.scale.x > 0.001f)) { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + f32 scale; + f32 temp; + + if (this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_2) { + indS1 = 38; + scale = Rand_ZeroFloat(0.1f) + 0.1f; + pos.y = this->tentPos[indS1].y; + } else { + indS1 = (s16)Rand_ZeroFloat(20.0f) + 18; + scale = Rand_ZeroFloat(0.02f) + .05f; + pos.y = this->tentPos[indS1].y - 10.0f; + } + temp = (this->actor.scale.x * 100.0f) * 20.0f; + pos.x = this->tentPos[indS1].x + Rand_CenteredFloat(temp); + pos.z = this->tentPos[indS1].z + Rand_CenteredFloat(temp); + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &pos, &velocity, scale); + } +} + +void BossMo_TentCollisionCheck(BossMo* this, GlobalContext* globalCtx) { + s16 i1; + + for (i1 = 0; i1 < ARRAY_COUNT(this->tentElements); i1++) { + if (this->tentCollider.elements[i1].info.bumperFlags & BUMP_HIT) { + s16 i2; + ColliderInfo* hurtbox; + + for (i2 = 0; i2 < 19; i2++) { + this->tentCollider.elements[i2].info.bumperFlags &= ~BUMP_HIT; + this->tentCollider.elements[i2].info.toucherFlags &= ~TOUCH_HIT; + } + hurtbox = this->tentCollider.elements[i1].info.acHitInfo; + this->work[MO_TENT_INVINC_TIMER] = 5; + if (hurtbox->toucher.dmgFlags & 0x00020000) { + func_80078914(&this->tentTipPos, NA_SE_EN_MOFER_CUT); + this->cutIndex = 15; + this->meltIndex = this->cutIndex + 1; + this->work[MO_TENT_ACTION_STATE] = MO_TENT_CUT; + this->timers[0] = 40; + this->cutScale = 1.0f; + } else if (hurtbox->toucher.dmgFlags & 0x0D800600) { + this->linkHitTimer = 5; + } + this->tentRippleSize = 0.2f; + for (i2 = 0; i2 < 10; i2++) { + Vec3f pos; + Vec3f velocity; + + velocity.x = Rand_CenteredFloat(8.0f); + velocity.y = Rand_ZeroFloat(7.0f) + 4.0f; + velocity.z = Rand_CenteredFloat(8.0f); + pos = this->tentPos[2 * i1]; + pos.x += velocity.x * 3.0f; + pos.z += velocity.z * 3.0f; + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &pos, &velocity, + Rand_ZeroFloat(0.08f) + 0.13f); + } + break; + } else if (this->tentCollider.elements[i1].info.toucherFlags & TOUCH_HIT) { + this->tentCollider.elements[i1].info.toucherFlags &= ~TOUCH_HIT; + this->linkHitTimer = 5; + break; + } + } +} + +void BossMo_IntroCs(BossMo* this, GlobalContext* globalCtx) { + static Vec3f cutsceneTargets[6] = { + { -360.0f, -190.0f, 0.0f }, { 250.0f, -190.0f, 0.0f }, { 300.0f, -120.0f, -278.0f }, + { 180.0f, -80.0f, -340.0f }, { 180.0f, 0.0f, -340.0f }, { 180.0f, 60.0f, -230.0f }, + }; + u8 sp9F = 0; + f32 dx; + f32 dy; + f32 dz; + f32 tempX; + f32 tempY; + s32 pad84; + f32 sp80; + f32 sp7C; + f32 sp78; + Player* player = GET_PLAYER(globalCtx); + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + Vec3f bubblePos; + Vec3f bubblePos2; + Camera* camera2; + f32 pad50; + f32 pad4C; + f32 pad48; + + if (this->csState < MO_INTRO_REVEAL) { + this->cameraZoom = 80.0f; + } + switch (this->csState) { + case MO_INTRO_WAIT: + if (this->timers[0] == 1) { + Message_StartTextbox(globalCtx, 0x403F, NULL); + } + if (((fabsf(player->actor.world.pos.z - 180.0f) < 40.0f) && + (fabsf(player->actor.world.pos.x - 180.0f) < 40.0f)) || + ((fabsf(player->actor.world.pos.z - -180.0f) < 40.0f) && + (fabsf(player->actor.world.pos.x - 180.0f) < 40.0f)) || + ((fabsf(player->actor.world.pos.z - 180.0f) < 40.0f) && + (fabsf(player->actor.world.pos.x - -180.0f) < 40.0f)) || + ((fabsf(player->actor.world.pos.z - -180.0f) < 40.0f) && + (fabsf(player->actor.world.pos.x - -180.0f) < 40.0f))) { + // checks if Link is on one of the four platforms + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->csCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->csCamera, CAM_STAT_ACTIVE); + this->actor.speedXZ = 0.0f; + this->csState = MO_INTRO_START; + this->timers[2] = 50; + this->work[MO_TENT_VAR_TIMER] = this->work[MO_TENT_MOVE_TIMER] = 0; + this->actor.world.rot.y = 0x721A; + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_READY; + sMorphaTent1->timers[0] = 30000; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x3200FF); + Message_CloseTextbox(globalCtx); + } else { + break; + } + case MO_INTRO_START: + player->actor.world.pos.x = 180.0f; + player->actor.world.pos.z = -130.0f; + player->actor.shape.rot.y = player->actor.world.rot.y = 0; + player->actor.speedXZ = 0.0f; + this->cameraEye.x = -424.0f; + this->cameraEye.y = -190.0f; + this->cameraEye.z = 180.0f; + this->cameraAt.x = player->actor.world.pos.x; + this->cameraAt.y = -330.0f; + this->cameraAt.z = 0.0f; + if (this->timers[2] == 0) { + this->csState = MO_INTRO_SWIM; + this->work[MO_TENT_MOVE_TIMER] = 0; + } else if (this->timers[2] < 50) { + bubblePos.x = (this->cameraEye.x + 20.0f) + 10.0f; + bubblePos.y = -250.0f; + bubblePos.z = this->cameraEye.z; + EffectSsBubble_Spawn(globalCtx, &bubblePos, 0.0f, 10.0f, 50.0f, Rand_ZeroFloat(0.05f) + 0.13f); + } + if (this->timers[2] == 40) { + func_80078914(&sAudioZeroVec, NA_SE_EN_MOFER_BUBLE_DEMO); + } + break; + case MO_INTRO_SWIM: + Math_ApproachF(&this->cameraYawShake, 0.1f, 1.0f, 0.002f); + this->targetPos = cutsceneTargets[this->targetIndex]; + if (this->targetIndex == 5) { + tempY = Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 0x500) * 20.0f; + } else { + tempY = Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 0x500) * 5.0f; + } + dx = this->targetPos.x - this->cameraEye.x; + dy = this->targetPos.y - this->cameraEye.y + tempY; + dz = this->targetPos.z - this->cameraEye.z; + tempY = Math_FAtan2F(dx, dz); + tempX = Math_FAtan2F(dy, sqrtf(SQ(dx) + SQ(dz))); + Math_ApproachS(&this->actor.world.rot.y, tempY * (0x8000 / M_PI), 5, this->cameraYawRate); + Math_ApproachS(&this->actor.world.rot.x, tempX * (0x8000 / M_PI), 5, this->cameraYawRate); + if (this->work[MO_TENT_MOVE_TIMER] == 150) { + this->cameraAtVel.x = fabsf(this->cameraAt.x - player->actor.world.pos.x); + this->cameraAtVel.y = fabsf(this->cameraAt.y - player->actor.world.pos.y); + this->cameraAtVel.z = fabsf(this->cameraAt.z - player->actor.world.pos.z); + } + if (this->work[MO_TENT_MOVE_TIMER] >= 150) { + Math_ApproachF(&this->cameraAt.x, player->actor.world.pos.x, 0.1f, + this->cameraAtVel.x * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.y, player->actor.world.pos.y + 50.0f, 0.1f, + this->cameraAtVel.y * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.z, player->actor.world.pos.z, 0.1f, + this->cameraAtVel.z * this->cameraSpeedMod); + Math_ApproachF(&this->cameraSpeedMod, 0.02f, 1.0f, 0.001f); + } + if (this->work[MO_TENT_MOVE_TIMER] == 190) { + func_80078914(&sAudioZeroVec, NA_SE_EN_MOFER_BUBLE_DEMO); + } + if ((this->work[MO_TENT_MOVE_TIMER] > 150) && (this->work[MO_TENT_MOVE_TIMER] < 180)) { + bubblePos2.x = (this->cameraEye.x + 20.0f) + 10.0f; + bubblePos2.y = -250.0f; + bubblePos2.z = this->cameraEye.z; + EffectSsBubble_Spawn(globalCtx, &bubblePos2, 0.0f, 10.0f, 50.0f, Rand_ZeroFloat(0.05f) + 0.13f); + } + sp7C = (f32)0x1000; + sp78 = 0.1f; + if ((this->work[MO_TENT_MOVE_TIMER] > 100) && (this->work[MO_TENT_MOVE_TIMER] < 220)) { + sp80 = 0.0f; + } else if (this->work[MO_TENT_MOVE_TIMER] > 350) { + sp80 = 2.0f; + sp78 = 0.4f; + } else if (this->work[MO_TENT_MOVE_TIMER] > 220) { + sp80 = 7.0f; + sp78 = 0.3f; + sp7C = (f32)0x2000; + } else { + sp80 = 4.0f; + } + + if (this->work[MO_TENT_MOVE_TIMER] > 250) { + Math_ApproachF(&this->fwork[MO_CORE_INTRO_WATER_ALPHA], 100.0f, 1.0f, 1.0f); + } + if (this->targetIndex < 5) { + if (sqrtf(SQ(dx) + SQ(dz) + SQ(dy)) < 40.0f) { + this->targetIndex++; + this->cameraYawRate = 0.0f; + } + } else { + sp80 = 1.5f; + sp7C = (f32)0x600; + } + Math_ApproachF(&this->actor.speedXZ, sp80, 1.0f, sp78); + Math_ApproachF(&this->cameraYawRate, sp7C, 1.0f, 128.0f); + if (this->work[MO_TENT_MOVE_TIMER] == 525) { + func_8002DF54(globalCtx, &this->actor, 2); + } + if (this->work[MO_TENT_MOVE_TIMER] > 540) { + this->csState = MO_INTRO_REVEAL; + func_8002DF54(globalCtx, &this->actor, 1); + sMorphaTent1->drawActor = true; + player->actor.world.pos.x = 180.0f; + player->actor.world.pos.z = -210.0f; + player->actor.world.rot.y = -0x8000; + player->actor.shape.rot.y = player->actor.world.rot.y; + this->cameraYawShake = 0.0f; + sMorphaTent1->baseAlpha = 150.0; + this->actor.speedXZ = 0.0f; + this->timers[2] = 200; + this->cameraZoom = 60.0f; + this->actor.world.pos = sMorphaTent1->actor.world.pos; + this->work[MO_TENT_ACTION_STATE] = MO_CORE_INTRO_REVEAL; + this->actor.flags &= ~ACTOR_FLAG_0; + sMorphaTent1->actor.flags |= ACTOR_FLAG_0; + } else { + sMorphaTent1->xSwing = 0xCEC; + sMorphaTent1->fwork[MO_TENT_SWING_RATE_X] = 0.0f; + sMorphaTent1->fwork[MO_TENT_SWING_LAG_X] = 1000.0f; + sMorphaTent1->fwork[MO_TENT_SWING_SIZE_X] = 2500.0f; + break; + } + case MO_INTRO_REVEAL: + if (this->timers[2] >= 160) { + this->cameraEye.x = 150.0f; + this->cameraEye.y = 60.0f; + this->cameraEye.z = -230.0f; + this->cameraAt.x = 170.0f; + this->cameraAt.y = 40.0; + this->cameraAt.z = -280.0f; + sMorphaTent1->xSwing = 0xCEC; + sMorphaTent1->fwork[MO_TENT_SWING_RATE_X] = 0.0f; + sMorphaTent1->fwork[MO_TENT_SWING_LAG_X] = 1000.0f; + sMorphaTent1->fwork[MO_TENT_SWING_SIZE_X] = 2500.0f; + if (this->timers[2] == 160) { + this->cameraNextAt.y = 65.0f; + this->cameraNextAt.z = -280.0f; + this->cameraEyeVel.x = fabsf(this->cameraEye.x - 150.0f) * 0.1f; + this->cameraEyeVel.y = fabsf(this->cameraEye.y - 60.0f) * 0.1f; + this->cameraEyeVel.z = fabsf(this->cameraEye.z - -260.0f) * 0.1f; + this->cameraNextEye.x = 150.0f; + this->cameraNextEye.y = 60.0f; + this->cameraNextEye.z = -260.0f; + this->cameraNextAt.x = 155.0f; + this->cameraAtMaxVel.x = this->cameraAtMaxVel.y = this->cameraAtMaxVel.z = 0.1f; + this->cameraAtVel.x = fabsf(this->cameraAt.x - this->cameraNextAt.x) * 0.1f; + this->cameraAtVel.y = fabsf(this->cameraAt.y - this->cameraNextAt.y) * 0.1f; + this->cameraAtVel.z = fabsf(this->cameraAt.z - this->cameraNextAt.z) * 0.1f; + this->cameraEyeMaxVel.x = this->cameraEyeMaxVel.y = this->cameraEyeMaxVel.z = 0.1f; + this->cameraSpeedMod = 0.0f; + this->cameraAccel = 0.01f; + this->tentMaxAngle = 0.001f; + this->tentSpeed = 0.0f; + sp9F = 1; + } + } else { + sp9F = 1; + } + if (this->timers[2] == 50) { + this->cameraNextAt.x = 160.0f; + this->cameraNextAt.y = 58.0f; + this->cameraNextAt.z = -247.0f; + this->cameraEyeVel.x = fabsf(this->cameraEye.x - 111.0f) * 0.1f; + this->cameraEyeVel.y = fabsf(this->cameraEye.y - 133.0f) * 0.1f; + this->cameraEyeVel.z = fabsf(this->cameraEye.z - -191.0f) * 0.1f; + if (1) {} + this->csState = MO_INTRO_FINISH; + this->timers[2] = 110; + this->cameraNextEye.x = 111.0f; + this->cameraNextEye.y = 133.0f; + this->cameraNextEye.z = -191.0f; + this->cameraAtVel.x = fabsf(this->cameraAt.x - this->cameraNextAt.x) * 0.1f; + this->cameraAtVel.y = fabsf(this->cameraAt.y - this->cameraNextAt.y) * 0.1f; + this->cameraAtVel.z = fabsf(this->cameraAt.z - this->cameraNextAt.z) * 0.1f; + this->cameraEyeMaxVel.y = 0.03f; + this->cameraAtMaxVel.y = 0.03f; + this->cameraSpeedMod = 0.0f; + this->cameraAccel = 0.01f; + } + if (this->timers[2] == 150) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } + if (this->timers[2] == 130) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gMorphaTitleCardTex), 0xA0, 0xB4, 0x80, 0x28); + gSaveContext.eventChkInf[7] |= 0x10; + } + break; + case MO_INTRO_FINISH: + sp9F = 1; + this->cameraNextEye.x = 111.0f; + this->cameraNextEye.y = 133.0f; + this->cameraNextEye.z = -191.0f; + this->cameraNextAt.x = 160.0f; + this->cameraNextAt.y = 58.0f; + this->cameraNextAt.z = -247.0f; + if (this->timers[2] == 100) { + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_RETREAT; + sMorphaTent1->timers[0] = 50; + } + if (this->timers[2] == 20) { + camera2 = Gameplay_GetCamera(globalCtx, MAIN_CAM); + camera2->eye = this->cameraEye; + camera2->eyeNext = this->cameraEye; + camera2->at = this->cameraAt; + func_800C08AC(globalCtx, this->csCamera, 0); + this->csState = this->csCamera = MO_BATTLE; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + } + break; + } + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_READY) { + sMorphaTent1->actor.world.pos.x = 180.0f; + sMorphaTent1->actor.world.pos.z = -360.0f; + sMorphaTent1->actor.prevPos = sMorphaTent1->actor.world.pos; + sMorphaTent1->actor.speedXZ = 0.0f; + sMorphaTent1->actor.shape.rot.y = sMorphaTent1->actor.yawTowardsPlayer; + } + if (this->csCamera != 0) { + if (sp9F) { + Math_ApproachF(&this->cameraEye.x, this->cameraNextEye.x, this->cameraEyeMaxVel.x, + this->cameraEyeVel.x * this->cameraSpeedMod); + Math_ApproachF(&this->cameraEye.y, this->cameraNextEye.y, this->cameraEyeMaxVel.y, + this->cameraEyeVel.y * this->cameraSpeedMod); + Math_ApproachF(&this->cameraEye.z, this->cameraNextEye.z, this->cameraEyeMaxVel.z, + this->cameraEyeVel.z * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.x, this->cameraNextAt.x, this->cameraAtMaxVel.x, + this->cameraAtVel.x * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.y, this->cameraNextAt.y, this->cameraAtMaxVel.y, + this->cameraAtVel.y * this->cameraSpeedMod); + Math_ApproachF(&this->cameraAt.z, this->cameraNextAt.z, this->cameraAtMaxVel.z, + this->cameraAtVel.z * this->cameraSpeedMod); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, this->cameraAccel); + } else if (this->csState < MO_INTRO_REVEAL) { + func_8002D908(&this->actor); + this->cameraEye.x += this->actor.velocity.x; + this->cameraEye.y += this->actor.velocity.y; + this->cameraEye.z += this->actor.velocity.z; + } + this->cameraUp.x = this->cameraUp.z = + sinf(this->work[MO_TENT_VAR_TIMER] * 0.03f) * this->cameraYawShake * (-2.0f); + this->cameraUp.y = 1.0f; + Gameplay_CameraSetAtEyeUp(globalCtx, this->csCamera, &this->cameraAt, &this->cameraEye, &this->cameraUp); + camera->eye = this->cameraEye; + camera->eyeNext = this->cameraEye; + camera->at = this->cameraAt; + Gameplay_CameraSetFov(globalCtx, this->csCamera, this->cameraZoom); + } + + if ((this->csState > MO_INTRO_START) && (this->work[MO_TENT_MOVE_TIMER] > 540)) { + func_80078914(&sMorphaTent1->tentTipPos, NA_SE_EN_MOFER_APPEAR - SFX_FLAG); + } else if (this->csState >= MO_INTRO_START) { + func_80078914(&sAudioZeroVec, NA_SE_EN_MOFER_MOVE_DEMO - SFX_FLAG); + } +} + +void BossMo_DeathCs(BossMo* this, GlobalContext* globalCtx) { + s16 i; + s16 one; + f32 dx; + f32 dz; + f32 sp80; + f32 sp7C; + Vec3f sp70; + Vec3f sp64; + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + Vec3f velocity; + Vec3f pos; + + switch (this->csState) { + case MO_DEATH_START: + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->csCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->csCamera, CAM_STAT_ACTIVE); + this->csState = MO_DEATH_MO_CORE_BURST; + this->cameraEye = camera->eye; + this->timers[0] = 90; + dx = this->actor.world.pos.x - this->cameraEye.x; + dz = this->actor.world.pos.z - this->cameraEye.z; + this->cameraYaw = Math_FAtan2F(dx, dz); + this->cameraDist = sqrtf(SQ(dx) + SQ(dz)); + this->cameraYawRate = 0.0f; + case MO_DEATH_MO_CORE_BURST: + this->baseAlpha = 0.0f; + if (this->timers[0] & 4) { + sp80 = 0.005f; + sp7C = 0.015f; + } else { + sp80 = 0.015f; + sp7C = 0.005f; + } + Math_ApproachF(&this->actor.scale.x, sp80, 0.5f, 0.002f); + this->actor.scale.z = this->actor.scale.x; + Math_ApproachF(&this->actor.scale.y, sp7C, 0.5f, 0.002f); + this->cameraYaw += this->cameraYawRate; + if (this->timers[0] >= 30) { + Math_ApproachF(&this->cameraYawRate, 0.05f, 1.0f, 0.002f); + } else { + Math_ApproachF(&this->cameraYawRate, 0.0f, 1.0f, 0.002f); + } + Math_ApproachF(&this->actor.world.pos.y, 150.0f, 0.05f, 5.0f); + Math_ApproachF(&this->cameraEye.y, 100.0f, 0.05f, 2.0f); + this->cameraAt = this->cameraNextAt = this->actor.world.pos; + if (this->timers[0] > 20) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_DEAD - SFX_FLAG); + } + if (this->timers[0] == 20) { + for (i = 0; i < 300; i++) { + velocity.x = Rand_CenteredFloat(10.0f); + velocity.y = Rand_CenteredFloat(10.0f); + velocity.z = Rand_CenteredFloat(10.0f); + pos = this->actor.world.pos; + pos.x += 2.0f * velocity.x; + pos.y += 2.0f * velocity.y; + pos.z += 2.0f * velocity.z; + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &pos, &velocity, + Rand_ZeroFloat(0.08f) + 0.13f); + } + this->drawActor = false; + this->actor.flags &= ~ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_JUMP); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 70, NA_SE_EN_MOFER_LASTVOICE); + } + if (this->timers[0] == 0) { + this->csState = MO_DEATH_DRAIN_WATER_1; + this->cameraDist = 490.0f; + this->actor.world.pos.y = -1000.0f; + this->fwork[MO_TENT_SWING_SIZE_X] = 15.0f; + this->cameraYaw = 0.0f; + this->cameraEye.x = 490.0f; + this->cameraEye.y = 50.0f; + this->cameraEye.z = 0.0f; + this->cameraAt.x = 0; + this->cameraAt.y = -100.0f; + this->cameraAt.z = 0.0f; + this->work[MO_TENT_VAR_TIMER] = this->work[MO_TENT_MOVE_TIMER] = 0; + this->cameraAtMaxVel.y = 0.05f; + this->cameraAtVel.y = 4.0f; + this->cameraSpeedMod = 0.0f; + this->cameraAccel = 0.02f; + this->cameraNextAt.y = 320.0f; + if (1) {} + this->timers[0] = 100; + sMorphaTent1->drawActor = true; + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_DEATH_3; + sMorphaTent1->actor.shape.rot.x = 0; + sMorphaTent1->actor.world.pos.x = 0.0f; + sMorphaTent1->actor.world.pos.y = -50.0f; + sMorphaTent1->actor.world.pos.z = 0.0f; + sMorphaTent1->fwork[MO_TENT_MAX_STRETCH] = 1.0f; + sMorphaTent1->tentPulse = 0.2f; + sMorphaCore->waterLevel = -50.0f; + sMorphaTent1->flattenRate = 0.0f; + sMorphaTent1->noBubbles = 0; + for (i = 0; i < 41; i++) { + sMorphaTent1->tentStretch[i].y = 5.0f; + } + sMorphaTent1->fwork[MO_TENT_SWING_RATE_X] = -400.0f; + sMorphaTent1->fwork[MO_TENT_SWING_LAG_X] = -3200.0f; + sMorphaTent1->fwork[MO_TENT_SWING_SIZE_X] = .0f; + sMorphaTent1->fwork[MO_TENT_SWING_RATE_Z] = 3000.0f; + sMorphaTent1->fwork[MO_TENT_SWING_LAG_Z] = 2500.0f; + sMorphaTent1->fwork[MO_TENT_SWING_SIZE_Z] = 4000.0f; + sMorphaTent1->tentMaxAngle = 1.0f; + sMorphaTent1->tentSpeed = 20480.0f; + sMorphaTent1->baseAlpha = 150.0f; + sMorphaTent1->cutIndex = sMorphaTent1->meltIndex = 0; + sMorphaTent1->cutScale = 1.0f; + Actor_SetScale(&sMorphaTent1->actor, 0.01f); + } + break; + case MO_DEATH_DRAIN_WATER_1: + if (this->timers[0] == 0) { + this->csState = MO_DEATH_DRAIN_WATER_2; + this->cameraAt.y = -200.0f; + this->cameraNextAt.y = 320.0f; + this->cameraAtMaxVel.y = 0.05f; + this->cameraAtVel.y = 4.0f; + this->cameraSpeedMod = 0.0f; + this->cameraAccel = 0.0f; + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_DEATH_1; + this->timers[0] = 125; + sMorphaTent1->fwork[MO_TENT_MAX_STRETCH] = 3.7000003f; + this->cameraYaw = 0.5f; + this->cameraDist = 200.0f; + return; + } + break; + case MO_DEATH_DRAIN_WATER_2: + if (this->timers[0] == 0) { + this->cameraAccel = 0.02f; + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_DEATH_2; + this->csState = MO_DEATH_CEILING; + sMorphaTent1->timers[0] = 120; + this->timers[0] = 150; + } + case MO_DEATH_CEILING: + Math_ApproachF(&this->cameraYaw, 0.0f, 0.05f, 0.0029999996f); + Math_ApproachF(&this->cameraDist, 490.0f, 0.1f, 1.0f); + if (this->timers[0] == 0) { + this->csState = MO_DEATH_DROPLET; + this->timers[0] = 140; + this->cameraYawRate = 0.0f; + this->cameraSpeed = 0.0f; + } + break; + case MO_DEATH_DROPLET: + if (this->timers[0] == 30) { + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_DEATH_5; + sMorphaTent1->timers[0] = 30; + sMorphaTent1->tentMaxAngle = 0.0f; + sMorphaTent1->tentSpeed = sMorphaTent1->tentMaxAngle; + } + if (this->timers[0] == 0) { + if (-100.0f < this->cameraEye.y) { + Math_ApproachF(&this->cameraEye.y, sMorphaTent1->actor.world.pos.y - 100.0f, 0.1f, 2000.0f); + } else { + Math_ApproachF(&this->cameraEye.y, -200.0f, 0.1f, 2000.0f); + } + + Math_ApproachF(&this->cameraAt.y, (sMorphaTent1->actor.world.pos.y - 50.0f) + 30.0f, 0.5f, 2000.0f); + this->cameraNextAt.y = this->cameraAt.y; + } else { + Math_ApproachF(&this->cameraEye.y, 300.0f, 0.05f, this->cameraSpeed); + } + Math_ApproachF(&this->cameraYaw, -M_PI / 2.0f, 0.05f, this->cameraYawRate); + Math_ApproachF(&this->cameraSpeed, 3.0f, 1.0f, 0.05f); + Math_ApproachF(&this->cameraYawRate, 0.012999999f, 1.0f, 0.0005f); + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_DEATH_6) { + Math_ApproachF(&this->cameraDist, 200.0f, 0.02f, this->cameraSpeed); + if (sMorphaTent1->timers[0] == 0) { + this->csState = MO_DEATH_FINISH; + camera->eye = this->cameraEye; + camera->eyeNext = this->cameraEye; + camera->at = this->cameraAt; + func_800C08AC(globalCtx, this->csCamera, 0); + this->csCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + sMorphaTent1->actor.world.pos.y = -1000.0f; + } + } else { + Math_ApproachF(&this->cameraDist, 150.0f, 0.05f, this->cameraSpeed); + } + break; + case MO_DEATH_FINISH: + break; + } + if ((this->csState > MO_DEATH_START) && (this->csState < MO_DEATH_FINISH)) { + if (this->work[MO_TENT_MOVE_TIMER] < 500) { + func_80078914(&sAudioZeroVec, NA_SE_EN_MOFER_APPEAR - SFX_FLAG); + } + if ((this->work[MO_TENT_MOVE_TIMER] < 490) && (this->work[MO_TENT_MOVE_TIMER] > 230)) { + func_80078914(&sAudioZeroVec, NA_SE_EV_DROP_FALL - SFX_FLAG); + } + if (this->work[MO_TENT_MOVE_TIMER] < 220) { + func_80078914(&sAudioZeroVec, NA_SE_EV_SCOOPUP_WATER - SFX_FLAG); + } + } + if (sMorphaCore->waterLevel < -200.0f) { + globalCtx->roomCtx.unk_74[0]++; + if (globalCtx->roomCtx.unk_74[0] >= 0xFF) { + globalCtx->roomCtx.unk_74[0] = 0xFF; + } + } + if (sMorphaCore->waterLevel < -250.0f) { + Math_ApproachF(&sMorphaTent1->waterTexAlpha, 0.0f, 1.0f, 3.0f); + } + Math_ApproachF(&this->fwork[MO_TENT_SWING_SIZE_X], 0.0f, 0.1f, 0.05f); + + sp70.x = this->cameraDist; + sp70.y = 0.0f; + sp70.z = 0.0f; + Matrix_RotateY(this->cameraYaw, MTXMODE_NEW); + Matrix_MultVec3f(&sp70, &sp64); + this->cameraEye.x = sp64.x + this->cameraAt.x; + this->cameraEye.z = sp64.z + this->cameraAt.z; + one = 1; // Super fake, but it works + if (this->csCamera != 0) { + if (one) { + Math_ApproachF(&this->cameraAt.y, this->cameraNextAt.y, this->cameraAtMaxVel.y, + this->cameraAtVel.y * this->cameraSpeedMod); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, this->cameraAccel); + } + Gameplay_CameraSetAtEye(globalCtx, this->csCamera, &this->cameraAt, &this->cameraEye); + } +} + +void BossMo_CoreCollisionCheck(BossMo* this, GlobalContext* globalCtx) { + s16 i; + Player* player = GET_PLAYER(globalCtx); + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("Core_Damage_check START\n"); + if (this->coreCollider.base.atFlags & AT_HIT) { + this->coreCollider.base.atFlags &= ~AT_HIT; + if (this->work[MO_TENT_ACTION_STATE] == MO_CORE_UNDERWATER) { + this->work[MO_CORE_WAIT_IN_WATER] = true; + this->timers[0] = 150; + } + } + if (this->coreCollider.base.acFlags & AC_HIT) { + ColliderInfo* hurtbox = this->coreCollider.info.acHitInfo; + // "hit!!" + osSyncPrintf("Core_Damage_check 当り!!\n"); + this->coreCollider.base.acFlags &= ~AC_HIT; + if ((hurtbox->toucher.dmgFlags & 0x00020000) && (this->work[MO_TENT_ACTION_STATE] == MO_CORE_ATTACK)) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_RETREAT; + } + // "hit 2 !!" + osSyncPrintf("Core_Damage_check 当り 2 !!\n"); + if ((this->work[MO_TENT_ACTION_STATE] != MO_CORE_UNDERWATER) && (this->work[MO_TENT_INVINC_TIMER] == 0)) { + u8 damage = CollisionCheck_GetSwordDamage(hurtbox->toucher.dmgFlags); + + if ((damage != 0) && (this->work[MO_TENT_ACTION_STATE] < MO_CORE_ATTACK)) { + // "sword hit !!" + osSyncPrintf("Core_Damage_check 剣 当り!!\n"); + this->work[MO_TENT_ACTION_STATE] = MO_CORE_STUNNED; + this->timers[0] = 25; + + this->actor.speedXZ = 15.0f; + + this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; + this->work[MO_CORE_DMG_FLASH_TIMER] = 15; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_DAMAGE); + this->actor.colChkInfo.health -= damage; + this->hitCount++; + if ((s8)this->actor.colChkInfo.health <= 0) { + if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) || + ((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + this->csState = MO_DEATH_START; + sMorphaTent1->drawActor = false; + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_DEATH_START; + sMorphaTent1->baseAlpha = 0.0f; + if (sMorphaTent2 != NULL) { + sMorphaTent2->tent2KillTimer = 1; + } + if (player->actor.parent != NULL) { + player->unk_850 = 0x65; + player->actor.parent = NULL; + player->csMode = 0; + } + } else { + this->actor.colChkInfo.health = 1; + } + } + this->work[MO_TENT_INVINC_TIMER] = 10; + } else if (!(hurtbox->toucher.dmgFlags & 0x00100000) && (hurtbox->toucher.dmgFlags & 0x80)) { + if (this->work[MO_TENT_ACTION_STATE] >= MO_CORE_ATTACK) { + func_80078914(&sMorphaTent1->tentTipPos, NA_SE_EN_MOFER_CUT); + sMorphaTent1->cutIndex = this->work[MO_CORE_POS_IN_TENT]; + sMorphaTent1->meltIndex = sMorphaTent1->cutIndex + 1; + sMorphaTent1->cutScale = 1.0f; + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_CUT; + sMorphaTent1->timers[0] = 40; + sMorphaTent1->actor.flags &= ~ACTOR_FLAG_0; + if (player->actor.parent == &sMorphaTent1->actor) { + player->unk_850 = 0x65; + player->actor.parent = NULL; + player->csMode = 0; + } + } + this->work[MO_TENT_ACTION_STATE] = MO_CORE_STUNNED; + this->timers[0] = 30; + this->work[MO_TENT_INVINC_TIMER] = 10; + this->actor.speedXZ = 0.0f; + } + for (i = 0; i < 10; i++) { + Vec3f pos; + Vec3f velocity; + + velocity.x = Rand_CenteredFloat(4.0f); + velocity.y = Rand_ZeroFloat(2.0f) + 3.0f; + velocity.z = Rand_CenteredFloat(4.0f); + pos = this->actor.world.pos; + pos.x += (velocity.x * 3.0f); + pos.z += (velocity.z * 3.0f); + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &pos, &velocity, + Rand_ZeroFloat(0.08f) + 0.13f); + } + } + } + // "end !!" + osSyncPrintf("Core_Damage_check 終わり !!\n"); + osSyncPrintf(VT_RST); +} + +void BossMo_Core(BossMo* this, GlobalContext* globalCtx) { + static f32 coreBulge[11] = { + 0.1f, 0.15f, 0.2f, 0.3f, 0.4f, 0.43f, 0.4f, 0.3f, 0.2f, 0.15f, 0.1f, + }; + u8 nearLand; + s16 i; // not on stack + Player* player = GET_PLAYER(globalCtx); // not on stack + f32 spDC; + f32 spD8; + f32 spD4; + f32 spD0; + f32 spCC; + s32 padC8; + s32 temp; // not on stack + f32 xScaleTarget; // not on stack + f32 yScaleTarget; + Vec3f effectPos; + Vec3f effectVelocity; + Vec3f effectAccel; + s32 pad94; + s32 pad90; + s16 j; + s16 index; // not on stack + f32 sp88; + s32 pad84; + f32 sp80; + f32 sp7C; + Vec3f sp70; + Vec3f sp64; + f32 sp60; + f32 sp5C; + f32 sp58; + + this->waterTex1x += -1.0f; + this->waterTex1y += -1.0f; + this->waterTex2x = this->waterTex2x; + this->waterTex2y++; + + Math_ApproachF(&this->baseAlpha, 255.0f, 1.0f, 10.0f); + if ((this->csState != MO_BATTLE) && (this->csState < MO_DEATH_START)) { + BossMo_IntroCs(this, globalCtx); + if (this->work[MO_TENT_ACTION_STATE] == MO_CORE_INTRO_WAIT) { + this->actor.flags &= ~ACTOR_FLAG_0; + return; + } + } else if (this->csState >= MO_DEATH_START) { + BossMo_DeathCs(this, globalCtx); + return; + } + if ((this->work[MO_TENT_ACTION_STATE] < MO_CORE_ATTACK) && (this->work[MO_TENT_ACTION_STATE] >= MO_CORE_MOVE) && + (this->actor.world.pos.y > MO_WATER_LEVEL(globalCtx))) { + if (this->actor.velocity.y > 0.0f) { + xScaleTarget = 0.005f; + yScaleTarget = 0.015f; + } else { + xScaleTarget = 0.015f; + yScaleTarget = 0.005f; + } + } else { + xScaleTarget = yScaleTarget = 0.008f; + } + Math_ApproachF(&this->actor.scale.x, xScaleTarget, 0.2f, 0.001f); + this->actor.scale.z = this->actor.scale.x; + Math_ApproachF(&this->actor.scale.y, yScaleTarget, 0.2f, 0.001f); + this->work[MO_CORE_DRAW_SHADOW] = BossMo_NearLand(&this->actor.world.pos, 15.0f); + nearLand = BossMo_NearLand(&this->actor.world.pos, 0.0f); + if ((player->actor.world.pos.y < (MO_WATER_LEVEL(globalCtx) - 50.0f)) && + ((this->work[MO_TENT_ACTION_STATE] == MO_CORE_MOVE) || + (this->work[MO_TENT_ACTION_STATE] == MO_CORE_MAKE_TENT))) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_UNDERWATER; + this->actor.speedXZ = 0.0f; + this->work[MO_CORE_WAIT_IN_WATER] = 0; + } + switch (this->work[MO_TENT_ACTION_STATE]) { + case MO_CORE_MOVE: + this->actor.flags |= ACTOR_FLAG_0; + if ((this->timers[0] == 0) && + ((sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_WAIT) || + (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_READY)) && + (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx))) { + this->actor.speedXZ = 0.0f; + this->work[MO_TENT_ACTION_STATE] = MO_CORE_MAKE_TENT; + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_WAIT) { + sMorphaTent1->work[MO_TENT_ACTION_STATE] = MO_TENT_SPAWN; + sMorphaTent1->timers[0] = 70; + sMorphaTent1->actor.shape.rot.y = sMorphaTent1->actor.yawTowardsPlayer; + } + } + break; + case MO_CORE_MAKE_TENT: + if ((sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_DESPAWN) || + (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_WAIT)) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_MOVE; + this->timers[0] = 70; + } + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_CUT) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_ATTACK; + this->work[MO_CORE_POS_IN_TENT] = 0; + this->timers[0] = 0; + } + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_ATTACK) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_ATTACK; + this->work[MO_CORE_POS_IN_TENT] = 0; + this->timers[0] = 0; + this->actor.speedXZ = 0.0f; + } + break; + case MO_CORE_UNDERWATER: + if (player->actor.world.pos.y >= MO_WATER_LEVEL(globalCtx)) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_MOVE; + this->actor.speedXZ = 0.0f; + } + break; + case MO_CORE_STUNNED: + this->actor.flags |= ACTOR_FLAG_0; + if (this->timers[0] == 0) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_MOVE; + this->timers[0] = 30; + } + if (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_MAKE_TENT; + this->timers[0] = 50; + this->actor.speedXZ = 0.0f; + } + break; + case MO_CORE_UNUSED: + break; + } + if (this->timers[0] == 0) { + switch (this->work[MO_TENT_ACTION_STATE]) { + case MO_CORE_ATTACK: + this->actor.flags |= ACTOR_FLAG_0; + this->work[MO_CORE_POS_IN_TENT]++; + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] == MO_TENT_ATTACK) { + temp = (s16)(Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 0x300) * 10.0f) + 15; + if (this->work[MO_CORE_POS_IN_TENT] >= temp) { + this->work[MO_CORE_POS_IN_TENT] = temp; + } + } + if ((sMorphaTent1->work[MO_TENT_ACTION_STATE] != MO_TENT_ATTACK) && + (sMorphaTent1->work[MO_TENT_ACTION_STATE] != MO_TENT_CUT)) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_RETREAT; + this->timers[0] = 0; + } + break; + case MO_CORE_RETREAT: + this->work[MO_CORE_POS_IN_TENT]--; + if (this->work[MO_CORE_POS_IN_TENT] <= 0) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_MAKE_TENT; + this->timers[0] = 100; + this->tentSpeed = 0.0f; + this->actor.speedXZ = 0.0f; + } + this->timers[0] = 0; + break; + case MO_CORE_INTRO_REVEAL: + this->actor.flags &= ~ACTOR_FLAG_0; + this->work[MO_CORE_POS_IN_TENT]++; + temp = (s16)(Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 0x500) * 10.0f) + 15; + if (this->work[MO_CORE_POS_IN_TENT] >= temp) { + this->work[MO_CORE_POS_IN_TENT] = temp; + } + if (sMorphaTent1->work[MO_TENT_ACTION_STATE] != MO_TENT_READY) { + this->work[MO_TENT_ACTION_STATE] = MO_CORE_RETREAT; + this->timers[0] = 0; + } + break; + } + } + if (this->work[MO_TENT_ACTION_STATE] >= MO_CORE_ATTACK) { + if (this->work[MO_CORE_POS_IN_TENT] < 0) { + this->work[MO_CORE_POS_IN_TENT] = 0; + } else if (this->work[MO_CORE_POS_IN_TENT] >= 41) { + this->work[MO_CORE_POS_IN_TENT] = 40; + } + index = (300 - (this->work[MO_CORE_POS_IN_TENT] * 2) + sMorphaTent1->widthIndex) % 300; + sp88 = sMorphaTent1->tentWidth[index] * sTentWidth[this->work[MO_CORE_POS_IN_TENT]]; + for (j = -5; j < 6; j++) { + index = (this->work[MO_CORE_POS_IN_TENT] + j) - 2; + if ((0 <= index) && (index < 41)) { + Math_ApproachF(&sMorphaTent1->tentScale[index].x, ((coreBulge[j + 5] * 300.0f) / 100.0f) + sp88, 0.75f, + 5.0f); + } + } + this->targetPos.x = sMorphaTent1->tentPos[this->work[MO_CORE_POS_IN_TENT]].x; + this->targetPos.y = sMorphaTent1->tentPos[this->work[MO_CORE_POS_IN_TENT]].y; + this->targetPos.z = sMorphaTent1->tentPos[this->work[MO_CORE_POS_IN_TENT]].z; + if (this->work[MO_CORE_POS_IN_TENT] <= 1) { + this->targetPos.y -= 20.0f; + } + Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.5f, this->actor.speedXZ); + Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.5f, this->actor.speedXZ); + Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.5f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 30.0f, 1.0f, 1.0f); + } else { + switch (this->work[MO_TENT_ACTION_STATE]) { + case MO_CORE_MOVE: + sp80 = Math_SinS(this->work[MO_TENT_VAR_TIMER] * 0x800) * 100.0f; + sp7C = Math_CosS(this->work[MO_TENT_VAR_TIMER] * 0x800) * 100.0f; + Math_ApproachF(&this->actor.world.pos.x, sMorphaTent1->targetPos.x + sp80, 0.05f, this->actor.speedXZ); + Math_ApproachF(&this->actor.world.pos.z, sMorphaTent1->targetPos.z + sp7C, 0.05f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 0.5f); + break; + case MO_CORE_STUNNED: + this->actor.velocity.x = Math_SinS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.velocity.z = Math_CosS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + break; + } + if ((this->work[MO_TENT_ACTION_STATE] == MO_CORE_MOVE) || + (this->work[MO_TENT_ACTION_STATE] == MO_CORE_STUNNED)) { + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.velocity.y -= 1.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 20.0f, 100.0f, 1); + effectVelocity.x = effectVelocity.y = effectVelocity.z = 0.0f; + for (i = 0; i < 1; i++) { + effectPos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + effectPos.y = Rand_CenteredFloat(20.0f) + this->actor.world.pos.y; + effectPos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &effectPos, + &effectVelocity, Rand_ZeroFloat(0.02f) + 0.05f); + }; + + if (nearLand) { + if (this->actor.world.pos.y <= 10) { + this->actor.world.pos.y = 10; + this->actor.velocity.y = -0.01f; + if (this->timers[1] != 0) { + if (this->timers[1] == 1) { + this->actor.velocity.y = 6.0f; + } + } else { + this->timers[1] = 2; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_LAND); + for (i = 0; i < 10; i++) { + effectVelocity.x = Rand_CenteredFloat(4.0f); + effectVelocity.y = Rand_ZeroFloat(2.0f) + 3.0f; + effectVelocity.z = Rand_CenteredFloat(4.0f); + effectPos = this->actor.world.pos; + effectPos.x += effectVelocity.x; + effectPos.z += effectVelocity.z; + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &effectPos, + &effectVelocity, Rand_ZeroFloat(0.08f) + 0.13f); + } + effectVelocity.x = effectVelocity.y = effectVelocity.z = 0.0f; + effectPos = this->actor.world.pos; + effectPos.y = 0.0f; + BossMo_SpawnDroplet(MO_FX_DROPLET, (BossMoEffect*)globalCtx->specialEffects, &effectPos, + &effectVelocity, 0.4f); + } + } + } else if (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) { + this->actor.velocity.y = BossMo_NearLand(&this->actor.world.pos, 40.0f) ? 15.0f : 6.0f; + if ((this->actor.world.pos.y + 15.0f) >= MO_WATER_LEVEL(globalCtx)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_JUMP); + } + } + } else if (this->work[MO_TENT_ACTION_STATE] >= MO_CORE_MOVE) { + if (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) { + if (this->work[MO_TENT_ACTION_STATE] == MO_CORE_MAKE_TENT) { + this->targetPos.x = sMorphaTent1->targetPos.x; + this->targetPos.y = sMorphaTent1->actor.world.pos.y - 40.0f; + this->targetPos.z = sMorphaTent1->targetPos.z; + Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 0.5f); + } else if (this->work[MO_TENT_ACTION_STATE] == MO_CORE_UNDERWATER) { + switch (this->work[MO_CORE_WAIT_IN_WATER]) { + case false: + this->targetPos = player->actor.world.pos; + this->targetPos.y += 30.0f; + sp70.x = 0.0f; + sp70.y = 0.0f; + sp70.z = 100.0f; + Matrix_RotateY((player->actor.world.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&sp70, &sp64); + this->targetPos.x = player->actor.world.pos.x + sp64.x; + this->targetPos.y = player->actor.world.pos.y + 30.0f; + this->targetPos.z = player->actor.world.pos.z + sp64.z; + Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 1.0f); + if (this->timers[0] == 0) { + this->work[MO_CORE_WAIT_IN_WATER] = true; + this->timers[0] = (s16)Rand_ZeroFloat(50.0f) + 50; + } + break; + case true: + Math_ApproachF(&this->actor.speedXZ, 1.0f, 1.0f, 0.5f); + if (this->timers[0] == 0) { + this->work[MO_CORE_WAIT_IN_WATER] = false; + this->timers[0] = (s16)Rand_ZeroFloat(20.0f) + 20; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_MOVE_WT); + } + break; + } + } + this->targetPos.x += Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 3096.0f) * 30.0f; + this->targetPos.y += Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 2096.0f) * 30.0f; + this->targetPos.z += Math_SinS(this->work[MO_TENT_MOVE_TIMER] * 2796.0f) * 30.0f; + this->tentMaxAngle = 5.0f; + this->tentSpeed = 4000.0f; + spDC = this->targetPos.x - this->actor.world.pos.x; + spD8 = this->targetPos.y - this->actor.world.pos.y; + spD4 = this->targetPos.z - this->actor.world.pos.z; + spCC = (s16)(Math_FAtan2F(spDC, spD4) * (0x8000 / M_PI)); + spD0 = (s16)(Math_FAtan2F(spD8, sqrtf(SQ(spDC) + SQ(spD4))) * (0x8000 / M_PI)); + Math_ApproachS(&this->actor.world.rot.y, spCC, this->tentMaxAngle, this->tentSpeed); + Math_ApproachS(&this->actor.world.rot.x, spD0, this->tentMaxAngle, this->tentSpeed); + func_8002D908(&this->actor); + } else { + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.velocity.y -= 1.0f; + } + func_8002D7EC(&this->actor); + temp = (this->actor.world.pos.y < -200.0f) ? 5 : 1; + this->actor.world.pos.y -= 20.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 20.0f, 100.0f, temp); + this->actor.world.pos.y += 20.0f; + } + } + if ((this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) && (MO_WATER_LEVEL(globalCtx) <= this->actor.prevPos.y)) { + if (this->actor.velocity.y < -5.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_JUMP); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MOFER_CORE_SMJUMP); + } + if ((this->timers[3] != 0) || ((sMorphaTent1->fwork[MO_TENT_MAX_STRETCH] > 0.2f) && + (fabsf(this->actor.world.pos.x - sMorphaTent1->actor.world.pos.x) < 30.0f) && + (fabsf(this->actor.world.pos.z - sMorphaTent1->actor.world.pos.z) < 30.0f))) { + // This space intentionally left blank. + } else { + this->timers[3] = 8; + for (i = 0; i < 10; i++) { + sp5C = Rand_ZeroFloat(3.14f); + sp60 = Rand_ZeroFloat(0.6f) + 1.6f; + effectVelocity.x = Math_SinS(((i * (f32)0x10000) / 10.0f) + sp5C) * sp60; + effectVelocity.z = Math_CosS(((i * (f32)0x10000) / 10.0f) + sp5C) * sp60; + effectVelocity.y = Rand_ZeroFloat(0.3f) + 3.0f; + + effectPos = this->actor.world.pos; + effectPos.x += effectVelocity.x * 3.0f; + effectPos.y = MO_WATER_LEVEL(globalCtx); + effectPos.z += effectVelocity.z * 3.0f; + BossMo_SpawnDroplet(MO_FX_SPLASH, (BossMoEffect*)globalCtx->specialEffects, &effectPos, &effectVelocity, + Rand_ZeroFloat(0.075f) + 0.15f); + } + effectPos = this->actor.world.pos; + effectPos.y = MO_WATER_LEVEL(globalCtx); + BossMo_SpawnRipple(globalCtx->specialEffects, &effectPos, 100.0f, 800.0f, 100, 290, MO_FX_SMALL_RIPPLE); + BossMo_SpawnRipple(globalCtx->specialEffects, &effectPos, 50.0f, 600.0f, 70, 290, MO_FX_SMALL_RIPPLE); + BossMo_SpawnRipple(globalCtx->specialEffects, &effectPos, 0, 400.0f, 50, 290, MO_FX_SMALL_RIPPLE); + } + } + if ((this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) || (this->work[MO_TENT_ACTION_STATE] >= MO_CORE_ATTACK)) { + for (i = 0; i < 3; i++) { + effectAccel.x = effectAccel.z = 0.0f; + effectVelocity.x = effectVelocity.y = effectVelocity.z = 0.0f; + if (this->work[MO_TENT_ACTION_STATE] >= MO_CORE_ATTACK) { + effectAccel.y = 0.0f; + sp58 = 10.0f; + } else { + effectAccel.y = 0.1f; + sp58 = 20.0f; + } + effectPos.x = Rand_CenteredFloat(sp58) + this->actor.world.pos.x; + effectPos.y = Rand_CenteredFloat(sp58) + this->actor.world.pos.y; + effectPos.z = Rand_CenteredFloat(sp58) + this->actor.world.pos.z; + BossMo_SpawnBubble(globalCtx->specialEffects, &effectPos, &effectVelocity, &effectAccel, + Rand_ZeroFloat(0.05f) + 0.1f, NULL); + } + } + BossMo_CoreCollisionCheck(this, globalCtx); +} + +void BossMo_UpdateCore(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossMo* this = (BossMo*)thisx; + s16 i; + Player* player = GET_PLAYER(globalCtx); + + osSyncPrintf("CORE mode = <%d>\n", this->work[MO_TENT_ACTION_STATE]); + if (sMorphaTent2 == NULL) { + MO_WATER_LEVEL(globalCtx) = sMorphaTent1->waterLevelMod + (s16)this->waterLevel; + } else { + MO_WATER_LEVEL(globalCtx) = sMorphaTent2->waterLevelMod + ((s16)this->waterLevel + sMorphaTent1->waterLevelMod); + } + this->actor.flags |= ACTOR_FLAG_9; + this->actor.focus.pos = this->actor.world.pos; + this->work[MO_TENT_VAR_TIMER]++; + + if (this->work[MO_CORE_DMG_FLASH_TIMER] != 0) { + this->work[MO_CORE_DMG_FLASH_TIMER]--; + } + if (this->work[MO_TENT_INVINC_TIMER] != 0) { + this->work[MO_TENT_INVINC_TIMER]--; + } + this->work[MO_TENT_MOVE_TIMER]++; + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + BossMo_Core(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->coreCollider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->coreCollider.base); + if ((this->work[MO_TENT_ACTION_STATE] != MO_CORE_STUNNED) || + (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx))) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->coreCollider.base); + } else { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->coreCollider.base); + } + BossMo_UpdateEffects(this, globalCtx); + if (player->actor.parent != NULL) { + this->actor.flags &= ~ACTOR_FLAG_0; + } + BossMo_Unknown(); +} + +void BossMo_UpdateTent(Actor* thisx, GlobalContext* globalCtx) { + s16 i; + s16 index; + s32 pad; + BossMo* this = (BossMo*)thisx; + Player* player = GET_PLAYER(globalCtx); + f32 phi_f0; + + if ((this == sMorphaTent2) && (this->tent2KillTimer != 0)) { + this->tent2KillTimer++; + this->actor.draw = NULL; + if (this->tent2KillTimer > 20) { + Actor_Kill(&this->actor); + Audio_StopSfxByPos(&this->tentTipPos); + sMorphaTent2 = NULL; + } + return; + } + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->tentPos[40], &this->tentTipPos, + &this->actor.projectedW); + osSyncPrintf("MO : Move mode = <%d>\n", this->work[MO_TENT_ACTION_STATE]); + Math_ApproachS(&player->actor.shape.rot.x, 0, 5, 0x3E8); + Math_ApproachS(&player->actor.shape.rot.z, 0, 5, 0x3E8); + this->work[MO_TENT_VAR_TIMER]++; + this->sfxTimer++; + this->work[MO_TENT_MOVE_TIMER]++; + this->widthIndex++; + if (this->widthIndex >= 300) { + this->widthIndex = 0; + } + this->pulsePhase -= 3000; + index = this->widthIndex; + this->tentWidth[index] = (Math_SinS(this->pulsePhase) * this->tentPulse) + (1.0f + this->tentPulse); + for (i = 0; i < 41; i++) { + if (this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_START) { + if (this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_1) { + if (this->work[MO_TENT_ACTION_STATE] == MO_TENT_DEATH_5) { + phi_f0 = (this->timers[0] != 0) ? sFlatWidth[i] : sDropletWidth[i]; + Math_ApproachF(&this->tentScale[i].x, phi_f0, 0.5f, 100.0f); + } else { + index = ((this->widthIndex + (i * 2)) + 220) % 300; + phi_f0 = this->tentWidth[index] + SQ(sTentWidth[i]); + Math_ApproachF(&this->tentScale[i].x, phi_f0, 0.5f, 0.3f); + } + } else { + index = ((this->widthIndex - (i * 2)) + 300) % 300; + phi_f0 = this->tentWidth[index] * sTentWidth[i]; + this->tentScale[i].x = phi_f0; + } + } else { + index = ((this->widthIndex - (i * 2)) + 300) % 300; + phi_f0 = this->tentWidth[index] * sTentWidth[i]; + Math_ApproachF(&this->tentScale[i].x, phi_f0, 0.5f, 0.3f); + } + phi_f0 = Math_SinS((this->work[MO_TENT_VAR_TIMER] * 12000.0f) + (i * 20000.0f)); + this->tentRipple[i].x = (1.0f * phi_f0) * this->tentRippleSize; + this->tentScale[i].y = this->tentScale[i].z = this->tentScale[i].x; + this->tentRipple[i].y = this->tentRipple[i].z = this->tentRipple[i].x; + } + + Math_ApproachF(&this->tentRippleSize, 0.0f, 0.1f, 0.005f); + Math_ApproachF(&this->tentPulse, 0.2f, 0.5f, 0.01f); + this->actionFunc(this, globalCtx); + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0xA, 0xC8); + Actor_MoveForward(&this->actor); + Math_ApproachF(&this->actor.speedXZ, 0.0, 1.0f, 0.02f); + + if (BossMo_NearLand(&this->actor.world.pos, 40)) { + this->actor.world.pos = this->actor.prevPos; + } + if ((this->work[MO_TENT_VAR_TIMER] % 8) == 0) { + f32 rippleScale; + Vec3f pos = this->actor.world.pos; + + if (this->work[MO_TENT_ACTION_STATE] < MO_TENT_DEATH_START) { + rippleScale = 400.0f; + } else { + rippleScale = 0.0; + if (this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_1) { + pos = this->tentPos[38]; + } + } + BossMo_SpawnRipple(globalCtx->specialEffects, &pos, rippleScale, rippleScale * 3.0f, this->baseAlpha * 0.6666f, + 300, MO_FX_BIG_RIPPLE); + } + if (this->baseBubblesTimer != 0) { + Vec3f sp88; + Vec3f sp7C; + Vec3f bubblePos; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 pad; + + this->baseBubblesTimer--; + sp88.x = 0.0; + sp88.y = 0.0; + sp88.z = 100.0f; + Matrix_RotateY(Rand_ZeroFloat(2.0f * M_PI), MTXMODE_NEW); + Matrix_MultVec3f(&sp88, &sp7C); + if ((this->work[MO_TENT_ACTION_STATE] >= MO_TENT_DEATH_1) && + (this->work[MO_TENT_ACTION_STATE] != MO_TENT_DEATH_3)) { + i = 38; + } else { + i = 0; + if (this->work[MO_TENT_ACTION_STATE] < MO_TENT_CUT) { + func_80078914(&this->tentTipPos, NA_SE_EN_MOFER_CORE_ROLL - SFX_FLAG); + } + } + bubblePos.x = this->tentPos[i].x + sp7C.x; + bubblePos.y = (MO_WATER_LEVEL(globalCtx) - 40.0f) + Rand_ZeroFloat(20.0f); + bubblePos.z = this->tentPos[i].z + sp7C.z; + BossMo_SpawnBubble(globalCtx->specialEffects, &bubblePos, &zeroVec, &zeroVec, Rand_ZeroFloat(0.05f) + 0.2f, + &this->tentPos[i]); + } + + if (this->work[MO_CORE_DMG_FLASH_TIMER] != 0) { + this->work[MO_CORE_DMG_FLASH_TIMER]--; + } + if (this->work[MO_TENT_INVINC_TIMER] != 0) { + this->work[MO_TENT_INVINC_TIMER]--; + } + if (this->linkHitTimer != 0) { + this->linkHitTimer--; + } + + if (this->drawActor) { + BossMo_TentCollisionCheck(this, globalCtx); + if ((this->work[MO_TENT_INVINC_TIMER] == 0) && (this->work[MO_TENT_ACTION_STATE] != MO_TENT_GRAB) && + (this->work[MO_TENT_ACTION_STATE] != MO_TENT_SHAKE)) { + BossMo* otherTent = (BossMo*)this->otherTent; + + if (!HAS_LINK(otherTent) && (this->cutIndex == 0)) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->tentCollider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->tentCollider.base); + } + } + if (this->cutIndex == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->tentCollider.base); + } + } + this->work[MO_TENT_BASE_TEX1_X]++; + this->work[MO_TENT_BASE_TEX1_Y]++; + this->work[MO_TENT_BASE_TEX2_X] -= 3; + this->work[MO_TENT_BASE_TEX2_Y]++; + Math_ApproachZeroF(&this->waterLevelMod, 0.1f, 0.2f); +} + +void BossMo_UpdateTentColliders(BossMo* this, s32 item, ColliderJntSph* tentCollider, Vec3f* center) { + tentCollider->elements[item].dim.worldSphere.center.x = center->x; + tentCollider->elements[item].dim.worldSphere.center.y = center->y; + tentCollider->elements[item].dim.worldSphere.center.z = center->z; + if (this->work[MO_TENT_ACTION_STATE] <= MO_TENT_SHAKE) { + tentCollider->elements[item].dim.worldSphere.radius = + tentCollider->elements[item].dim.modelSphere.radius * tentCollider->elements[item].dim.scale; + } else { + tentCollider->elements[item].dim.worldSphere.radius = 0; + } +} + +static Gfx* sTentDLists[41] = { + gMorphaTentaclePart0DL, gMorphaTentaclePart1DL, gMorphaTentaclePart2DL, gMorphaTentaclePart3DL, + gMorphaTentaclePart4DL, gMorphaTentaclePart5DL, gMorphaTentaclePart6DL, gMorphaTentaclePart7DL, + gMorphaTentaclePart8DL, gMorphaTentaclePart9DL, gMorphaTentaclePart10DL, gMorphaTentaclePart11DL, + gMorphaTentaclePart12DL, gMorphaTentaclePart13DL, gMorphaTentaclePart14DL, gMorphaTentaclePart15DL, + gMorphaTentaclePart16DL, gMorphaTentaclePart17DL, gMorphaTentaclePart18DL, gMorphaTentaclePart19DL, + gMorphaTentaclePart20DL, gMorphaTentaclePart21DL, gMorphaTentaclePart22DL, gMorphaTentaclePart23DL, + gMorphaTentaclePart24DL, gMorphaTentaclePart25DL, gMorphaTentaclePart26DL, gMorphaTentaclePart27DL, + gMorphaTentaclePart28DL, gMorphaTentaclePart29DL, gMorphaTentaclePart30DL, gMorphaTentaclePart31DL, + gMorphaTentaclePart32DL, gMorphaTentaclePart33DL, gMorphaTentaclePart34DL, gMorphaTentaclePart35DL, + gMorphaTentaclePart36DL, gMorphaTentaclePart37DL, gMorphaTentaclePart38DL, gMorphaTentaclePart39DL, + gMorphaTentaclePart40DL, +}; + +void BossMo_DrawTentacle(BossMo* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s16 i; + s16 notCut; + s16 index; + Mtx* matrix = Graph_Alloc(globalCtx->state.gfxCtx, 41 * sizeof(Mtx)); + f32 phi_f20; + f32 phi_f22; + Vec3f sp110; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6366); + + sp110.x = globalCtx->envCtx.dirLight1.params.dir.x; + sp110.y = globalCtx->envCtx.dirLight1.params.dir.y; + sp110.z = globalCtx->envCtx.dirLight1.params.dir.z; + + Matrix_Push(); + + gDPPipeSync(POLY_XLU_DISP++); + + gSPSegment(POLY_XLU_DISP++, 0x0C, matrix); + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((this->actor.shape.rot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + + BossMo_InitRand(1, 29100, 9786); + + for (i = 0; i < 41; i++, matrix++) { + s32 pad; + s32 pad2; + + if (i < 2) { + Matrix_Push(); + Matrix_Scale(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + notCut = true; + } else { + if (i >= 3) { + Matrix_Translate(0.0f, this->tentStretch[i - 2].y, 0.0f, MTXMODE_APPLY); + Matrix_RotateX((this->tentRot[i - 2].x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((this->tentRot[i - 2].z / (f32)0x8000) * M_PI, MTXMODE_APPLY); + } + Matrix_Push(); + Matrix_Scale((this->tentScale[i - 2].x + this->tentRipple[i - 2].x) * this->actor.scale.x, + (this->tentScale[i - 2].y + this->tentRipple[i - 2].y) * this->actor.scale.y, + (this->tentScale[i - 2].z + this->tentRipple[i - 2].z) * this->actor.scale.z, MTXMODE_APPLY); + notCut = true; + if ((i >= this->cutIndex) && (this->meltIndex >= i)) { + Matrix_Scale(this->cutScale, this->cutScale, this->cutScale, MTXMODE_APPLY); + notCut = false; + } + } + + index = ((this->widthIndex - (i * 2)) + 300) % 300; + if (this->work[MO_TENT_ACTION_STATE] < MO_TENT_DEATH_START) { + Matrix_RotateY((((this->tentWidth[index] - 1.0f - this->tentPulse) * 1000) / 1000.0f) * + this->fwork[MO_TENT_MAX_STRETCH], + MTXMODE_APPLY); + } + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + Matrix_ToMtx(matrix, "../z_boss_mo.c", 6452); + + gSPMatrix(POLY_XLU_DISP++, matrix, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (i == 0) { + func_8002EB44(&this->tentPos[i], &globalCtx->view.eye, &sp110, globalCtx->state.gfxCtx); + } + + if (i == 0) { + gSPDisplayList(POLY_XLU_DISP++, gMorphaTentacleBaseDL); + } else { + gSPDisplayList(POLY_XLU_DISP++, sTentDLists[i]); + } + + Matrix_Pop(); + + if ((i >= 2) && notCut && (i < (this->noBubbles + 38))) { + if ((this->work[MO_TENT_ACTION_STATE] == MO_TENT_DEATH_1) || + (this->work[MO_TENT_ACTION_STATE] == MO_TENT_DEATH_2)) { + phi_f20 = this->work[MO_TENT_MOVE_TIMER] & 3; + phi_f20 *= -15.0f; + phi_f22 = ((0.18f + BossMo_RandZeroOne() * 0.1f) * this->actor.scale.x) * 100.0f; + } else { + phi_f20 = 0.0f; + phi_f22 = (((BossMo_RandZeroOne() * (0.08f)) + .08f) * this->actor.scale.x) * 100.0f; + } + Matrix_Push(); + Matrix_Translate(((BossMo_RandZeroOne() - 0.5f) * 10.0f) * this->tentScale[i - 2].x, + ((BossMo_RandZeroOne() - 0.5f) * 3.0f) + phi_f20, + ((BossMo_RandZeroOne() - 0.5f) * 10.0f) * this->tentScale[i - 2].z, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(phi_f22, phi_f22, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6511), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gMorphaBubbleDL); + + Matrix_Pop(); + } + + Matrix_MultVec3f(&zeroVec, &this->tentPos[i]); + if (i == 36) { + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); + } + if (i == 24) { + MtxF sp98; + Vec3f sp8C = { -16.0f, 13.0f, 30.0f }; + Vec3s sp84; + + Matrix_Push(); + if (this->linkToLeft) { + sp8C.x *= -1.0f; + } + Matrix_MultVec3f(&sp8C, &this->grabPosRot.pos); + Matrix_RotateX(-35 * M_PI / 64, MTXMODE_APPLY); + Matrix_Get(&sp98); + Matrix_MtxFToYXZRotS(&sp98, &sp84, 0); + this->grabPosRot.rot.x = sp84.x; + this->grabPosRot.rot.y = sp84.y; + this->grabPosRot.rot.z = sp84.z; + Matrix_Pop(); + } + if ((i < 38) && ((i & 1) == 1)) { + BossMo_UpdateTentColliders(this, i / 2, &this->tentCollider, &this->tentPos[i]); + } + } + + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6571); +} + +void BossMo_DrawWater(BossMo* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6582); + if (1) {} + + Matrix_Push(); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Translate(0.0f, MO_WATER_LEVEL(globalCtx), 0.0f, MTXMODE_NEW); + + gSPSegment(POLY_XLU_DISP++, 0x0D, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->waterTex1x, (s16)this->waterTex1y, 32, 32, 1, + (s16)this->waterTex2x, (s16)this->waterTex2y, 32, 32)); + + gDPPipeSync(POLY_XLU_DISP++); + + gDPSetPrimColor(POLY_XLU_DISP++, 0xFF, 0xFF, 200, 255, 255, (s8)sMorphaTent1->waterTexAlpha); + + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 80); + + Matrix_Scale(0.5f, 1.0f, 0.5f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6675), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gMorphaWaterDL); + + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6680); +} + +void BossMo_DrawCore(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossMo* this = (BossMo*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6688); + if (this->actor.world.pos.y > MO_WATER_LEVEL(globalCtx)) { + BossMo_DrawWater(this, globalCtx); + } + if (this->drawActor) { + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, sMorphaTent1->work[MO_TENT_VAR_TIMER] * 3, + sMorphaTent1->work[MO_TENT_VAR_TIMER] * 3, 32, 32, 1, + sMorphaTent1->work[MO_TENT_VAR_TIMER] * -3, + sMorphaTent1->work[MO_TENT_VAR_TIMER] * -3, 32, 32)); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, sMorphaTent1->work[MO_TENT_VAR_TIMER] * 5, 0, 32, 32, 1, + 0, sMorphaTent1->work[MO_TENT_VAR_TIMER] * -10, 32, 32)); + + Matrix_RotateX(this->work[MO_TENT_MOVE_TIMER] * 0.5f, MTXMODE_APPLY); + Matrix_RotateZ(this->work[MO_TENT_MOVE_TIMER] * 0.8f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6735), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 255, (s8)this->baseAlpha); + + func_8002ED80(&this->actor, globalCtx, 0); + + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gMorphaCoreMembraneDL)); + + gDPPipeSync(POLY_XLU_DISP++); + + gDPSetEnvColor(POLY_XLU_DISP++, 0, 220, 255, 128); + if ((this->work[MO_CORE_DMG_FLASH_TIMER] % 2) != 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 60, 0, 255); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 255, 255); + } + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gMorphaCoreNucleusDL)); + + if ((this->work[MO_CORE_DRAW_SHADOW] && (this->actor.world.pos.y >= 0.0f)) || + (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx))) { + f32 groundLevel; + s16 shadowAlpha; + + if (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) { + groundLevel = -280.0f; + shadowAlpha = 100; + } else { + groundLevel = 0.0f; + shadowAlpha = 160; + } + + func_80094044(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, shadowAlpha); + + Matrix_Translate(this->actor.world.pos.x, groundLevel, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(0.23f, 1.0f, 0.23f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6820), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gCircleShadowDL)); + } + } + + if (this->actor.world.pos.y < MO_WATER_LEVEL(globalCtx)) { + BossMo_DrawWater(this, globalCtx); + } + + if ((this->csCamera != 0) && (this->csState < MO_INTRO_REVEAL)) { + f32 sp8C; + f32 sp88; + f32 sp84; + f32 temp; + f32 sp7C; + f32 sp78; + Vec3f sp6C; + Vec3f sp60; + + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0xFF, 0xFF, 200, 255, 255, (s8)this->fwork[MO_CORE_INTRO_WATER_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, (s8)this->fwork[MO_CORE_INTRO_WATER_ALPHA]); + + gSPSegment(POLY_XLU_DISP++, 0x0D, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)sMorphaTent1->waterTex1x, + (s16)sMorphaTent1->waterTex1y, 32, 32, 1, (s16)sMorphaTent1->waterTex2x, + (s16)sMorphaTent1->waterTex2y, 32, 32)); + + sp8C = this->cameraAt.x - this->cameraEye.x; + sp88 = this->cameraAt.y - this->cameraEye.y; + sp84 = this->cameraAt.z - this->cameraEye.z; + temp = SQ(sp8C) + SQ(sp84); + sp7C = Math_FAtan2F(sp8C, sp84); + sp78 = -Math_FAtan2F(sp88, sqrtf(temp)); + + sp6C.x = 0.0f; + sp6C.y = 0.0f; + sp6C.z = 10.0f; + + Matrix_RotateY(sp7C, MTXMODE_NEW); + Matrix_RotateX(sp78, MTXMODE_APPLY); + Matrix_MultVec3f(&sp6C, &sp60); + sp8C = sp60.x + this->cameraEye.x; + sp88 = sp60.y + this->cameraEye.y; + sp84 = sp60.z + this->cameraEye.z; + Matrix_Translate(sp8C, sp88, sp84, MTXMODE_NEW); + Matrix_RotateY(sp7C, MTXMODE_APPLY); + Matrix_RotateX(sp78, MTXMODE_APPLY); + Matrix_RotateZ(-(0.01f * this->work[MO_TENT_VAR_TIMER]), MTXMODE_APPLY); + Matrix_RotateZ(0.1f * this->work[MO_TENT_VAR_TIMER], MTXMODE_APPLY); + Matrix_Scale(0.825f, 1.175f, 0.825f, MTXMODE_APPLY); + Matrix_RotateZ(-(this->work[MO_TENT_VAR_TIMER] * 0.1f), MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + Matrix_Scale(0.05f, 1.0f, 0.05f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6941), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gMorphaWaterDL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6945); + + BossMo_DrawEffects(globalCtx->specialEffects, globalCtx); +} + +void BossMo_DrawTent(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossMo* this = (BossMo*)thisx; + u16 scroll; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 6958); + if (1) {} + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, (s8)(this->baseAlpha * 1.5f)); + gDPSetEnvColor(POLY_OPA_DISP++, 150, 150, 150, 0); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->work[MO_TENT_BASE_TEX1_X], + this->work[MO_TENT_BASE_TEX1_Y], 32, 32, 1, this->work[MO_TENT_BASE_TEX2_X], + this->work[MO_TENT_BASE_TEX2_Y], 32, 32)); + gDPSetPrimColor(POLY_XLU_DISP++, 0xFF, 0xFF, 200, 255, 255, (s8)((this->baseAlpha * 12.0f) / 10.0f)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, (s8)this->baseAlpha); + scroll = (s16)(Math_SinS(this->work[MO_TENT_VAR_TIMER] * 0xB00) * 30.0f) + 350; + gSPTexture(POLY_XLU_DISP++, scroll, scroll, 0, G_TX_RENDERTILE, G_ON); + + if (this->drawActor) { + BossMo_DrawTentacle(this, globalCtx); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_mo.c", 7023); +} + +void BossMo_UpdateEffects(BossMo* this, GlobalContext* globalCtx) { + BossMoEffect* effect = globalCtx->specialEffects; + s16 i; + Vec3f* targetPos; + f32 dx; + f32 dz; + Vec3f bubbleSpeed = { 0.0f, 0.0f, 0.0f }; + Vec3f bubbleVel; + + for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { + if (effect->type != MO_FX_NONE) { + effect->timer++; + if (effect->stopTimer == 0) { + effect->pos.x += effect->vel.x; + effect->pos.y += effect->vel.y; + effect->pos.z += effect->vel.z; + effect->vel.x += effect->accel.x; + effect->vel.y += effect->accel.y; + effect->vel.z += effect->accel.z; + } else { + effect->stopTimer--; + } + if (effect->type <= MO_FX_BIG_RIPPLE) { + if (this->csState >= MO_DEATH_START) { + effect->pos.y = MO_WATER_LEVEL(globalCtx); + } + Math_ApproachF(&effect->scale, effect->fwork[MO_FX_MAX_SIZE], 0.2f, effect->fwork[MO_FX_SPREAD_RATE]); + if (effect->rippleMode == 0) { + effect->alpha += 15; + if (effect->alpha >= effect->maxAlpha) { + effect->alpha = effect->maxAlpha; + effect->rippleMode++; + } + } else { + effect->alpha -= 5; + if (effect->alpha <= 0) { + effect->alpha = 0; + effect->type = MO_FX_NONE; + } + } + } else if (effect->type == MO_FX_BUBBLE) { + if (effect->targetPos == NULL) { + if ((effect->accel.y > 0.0f) && (effect->pos.y >= MO_WATER_LEVEL(globalCtx))) { + effect->type = MO_FX_NONE; + } else { + if (effect->vel.y > 2.0f) { + effect->vel.y = 2.0f; + } + effect->alpha -= 20; + if (effect->alpha <= 0) { + effect->alpha = 0; + effect->type = MO_FX_NONE; + } + } + } else { + if ((effect->timer % 4) == 0) { + targetPos = effect->targetPos; + dx = targetPos->x - effect->pos.x; + dz = targetPos->z - effect->pos.z; + bubbleSpeed.z = effect->fwork[MO_FX_SUCTION]; + Matrix_RotateY(Math_FAtan2F(dx, dz), MTXMODE_NEW); + Matrix_MultVec3f(&bubbleSpeed, &bubbleVel); + effect->vel.x = bubbleVel.x; + effect->vel.z = bubbleVel.z; + } + Math_ApproachF(&effect->fwork[MO_FX_SUCTION], 5.0f, 1.0f, 0.5f); + if (effect->timer > 20) { + effect->alpha -= 30; + effect->accel.y = 1.5f; + if ((effect->alpha <= 0) || (effect->pos.y >= MO_WATER_LEVEL(globalCtx))) { + effect->alpha = 0; + effect->type = MO_FX_NONE; + } + } else { + effect->alpha += 30; + if (effect->alpha >= 255) { + effect->alpha = 255; + } + } + } + } else if ((effect->type == MO_FX_DROPLET) || (effect->type == MO_FX_SPLASH) || + (effect->type == MO_FX_SPLASH_TRAIL) || (effect->type == MO_FX_WET_SPOT)) { + f32 shimmer = (effect->timer & 6) ? 80.0f : 200.0f; + + Math_ApproachF(&effect->fwork[MO_FX_SHIMMER], shimmer, 1.0f, 80.0f); + if (effect->type == MO_FX_WET_SPOT) { + Math_ApproachF(&effect->scale, effect->fwork[MO_FX_MAX_SCALE], 0.1f, 0.6f); + effect->alpha -= 15; + if (effect->alpha <= 0) { + effect->alpha = 0; + effect->type = MO_FX_NONE; + } + } else { + effect->alpha = effect->fwork[MO_FX_SHIMMER]; + if (effect->type == MO_FX_SPLASH_TRAIL) { + Math_ApproachF(&effect->scale, 0.0f, 1.0f, 0.02f); + if (effect->scale <= 0.0f) { + effect->type = MO_FX_NONE; + } + } else { + if (effect->type == MO_FX_SPLASH) { + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + + BossMo_SpawnDroplet(MO_FX_SPLASH_TRAIL, (BossMoEffect*)globalCtx->specialEffects, + &effect->pos, &velocity, effect->scale); + } + if (effect->vel.y < -20.0f) { + effect->vel.y = -20.0f; + effect->accel.y = 0.0f; + } + if (effect->stopTimer == 0) { + if (effect->vel.y < -5.0f) { + Math_ApproachF(&effect->fwork[MO_FX_STRETCH], 5.0f, 0.1f, 0.15f); + } + } else if (effect->stopTimer == 1) { + effect->vel.x = Rand_CenteredFloat(3.0f); + effect->vel.z = Rand_CenteredFloat(3.0f); + effect->accel.y = -1.0f; + } + if ((effect->pos.y <= -280.0f) || ((1.0f >= effect->pos.y) && (effect->pos.y >= -20.0f) && + BossMo_NearLand(&effect->pos, 0.0f))) { + effect->accel.y = 0.0f; + effect->vel.z = 0.0f; + effect->vel.y = 0.0f; + effect->vel.x = 0.0f; + if (effect->pos.y <= -280.0f) { + effect->pos.y = -280.0f; + } else { + effect->pos.y = 0.0f; + } + effect->type = MO_FX_WET_SPOT; + effect->alpha = 150; + effect->fwork[MO_FX_STRETCH] = (effect->scale * 15.0f) * 0.15f; + } else if (effect->pos.y <= MO_WATER_LEVEL(globalCtx)) { + Vec3f pos = effect->pos; + + pos.y = MO_WATER_LEVEL(globalCtx); + if (effect->type == MO_FX_SPLASH) { + BossMo_SpawnRipple(globalCtx->specialEffects, &pos, 60.0f, 160.0f, 80, 290, + MO_FX_SMALL_RIPPLE); + } else { + BossMo_SpawnRipple(globalCtx->specialEffects, &pos, 40.0f, 110.0f, 80, 290, + MO_FX_SMALL_RIPPLE); + } + effect->type = MO_FX_NONE; + } + } + } + } + } + } +} + +void BossMo_DrawEffects(BossMoEffect* effect, GlobalContext* globalCtx) { + u8 flag = 0; + s16 i; + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + BossMoEffect* effectHead = effect; + + OPEN_DISPS(gfxCtx, "../z_boss_mo.c", 7264); + Matrix_Push(); + + for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { + if (effect->type == MO_FX_BIG_RIPPLE) { + if (flag == 0) { + func_80094BC4(gfxCtx); + + gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 255, 0); + + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, 1.0f, effect->scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_mo.c", 7294), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gEffWaterRippleDL); + } + } + + effect = effectHead; + flag = 0; + for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { + if (effect->type == MO_FX_SMALL_RIPPLE) { + if (flag == 0) { + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 255, 0); + + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, 1.0f, effect->scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_mo.c", 7330), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gEffShockwaveDL); + } + } + + effect = effectHead; + flag = 0; + for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { + if (((effect->type == MO_FX_DROPLET) || (effect->type == MO_FX_SPLASH)) || + (effect->type == MO_FX_SPLASH_TRAIL)) { + if (flag == 0) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDust1Tex)); + gSPDisplayList(POLY_XLU_DISP++, gMorphaDropletMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 250, 250, 255, 0); + + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (s16)effect->fwork[MO_FX_SHIMMER], (s16)effect->fwork[MO_FX_SHIMMER], + 255, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale / effect->fwork[MO_FX_STRETCH], effect->fwork[MO_FX_STRETCH] * effect->scale, + 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_mo.c", 7373), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gMorphaDropletModelDL); + } + } + + effect = effectHead; + flag = 0; + for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { + if (effect->type == MO_FX_WET_SPOT) { + if (flag == 0) { + func_80094044(gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDust1Tex)); + gDPSetEnvColor(POLY_XLU_DISP++, 250, 250, 255, 0); + gSPDisplayList(POLY_XLU_DISP++, gMorphaDropletMaterialDL); + + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (s16)effect->fwork[MO_FX_SHIMMER], (s16)effect->fwork[MO_FX_SHIMMER], + 0xFF, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, 1.0f, effect->scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_mo.c", 7441), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gMorphaWetSpotModelDL); + } + } + + effect = effectHead; + flag = 0; + for (i = 0; i < ARRAY_COUNT(sEffects); i++, effect++) { + if (effect->type == MO_FX_BUBBLE) { + if (flag == 0) { + func_80093D18(globalCtx->state.gfxCtx); + + gDPSetEnvColor(POLY_OPA_DISP++, 150, 150, 150, 0); + + flag++; + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_mo.c", 7476), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gMorphaBubbleDL); + } + } + + Matrix_Pop(); + CLOSE_DISPS(gfxCtx, "../z_boss_mo.c", 7482); +} + +void BossMo_Unknown(void) { + // Appears to be a test function for sound effects. + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + static u16 unkSfx[] = { + NA_SE_PL_WALK_GROUND, + NA_SE_PL_WALK_GROUND, + NA_SE_PL_WALK_GROUND, + NA_SE_PL_WALK_SAND, + NA_SE_PL_WALK_CONCRETE, + NA_SE_PL_WALK_DIRT, + NA_SE_PL_WALK_WATER0, + NA_SE_PL_WALK_WATER1, + NA_SE_PL_WALK_WATER2, + NA_SE_PL_WALK_MAGMA, + NA_SE_PL_WALK_GRASS, + NA_SE_PL_WALK_GLASS, + NA_SE_PL_WALK_LADDER, + NA_SE_PL_WALK_GLASS, + NA_SE_PL_WALK_WALL, + NA_SE_PL_WALK_HEAVYBOOTS, + NA_SE_PL_WALK_ICE, + NA_SE_PL_JUMP, + NA_SE_PL_JUMP, + NA_SE_PL_JUMP_SAND, + NA_SE_PL_JUMP_CONCRETE, + NA_SE_PL_JUMP_DIRT, + NA_SE_PL_JUMP_WATER0, + NA_SE_PL_JUMP_WATER1, + NA_SE_PL_JUMP_WATER2, + NA_SE_PL_JUMP_MAGMA, + NA_SE_PL_JUMP_GRASS, + NA_SE_PL_JUMP_GLASS, + NA_SE_PL_JUMP_LADDER, + NA_SE_PL_JUMP_GLASS, + NA_SE_PL_JUMP_HEAVYBOOTS, + NA_SE_PL_JUMP_ICE, + NA_SE_PL_LAND, + NA_SE_PL_LAND, + NA_SE_PL_LAND_SAND, + NA_SE_PL_LAND_CONCRETE, + NA_SE_PL_LAND_DIRT, + NA_SE_PL_LAND_WATER0, + NA_SE_PL_LAND_WATER1, + NA_SE_PL_LAND_WATER2, + NA_SE_PL_LAND_MAGMA, + NA_SE_PL_LAND_GRASS, + NA_SE_PL_LAND_GLASS, + NA_SE_PL_LAND_LADDER, + NA_SE_PL_LAND_GLASS, + NA_SE_PL_LAND_HEAVYBOOTS, + NA_SE_PL_LAND_ICE, + NA_SE_PL_SLIPDOWN, + NA_SE_PL_CLIMB_CLIFF, + NA_SE_PL_CLIMB_CLIFF, + NA_SE_PL_SIT_ON_HORSE, + NA_SE_PL_GET_OFF_HORSE, + NA_SE_PL_TAKE_OUT_SHIELD, + NA_SE_PL_TAKE_OUT_SHIELD, + NA_SE_PL_TAKE_OUT_SHIELD, + NA_SE_PL_TAKE_OUT_SHIELD, + NA_SE_PL_TAKE_OUT_SHIELD, + NA_SE_PL_TAKE_OUT_SHIELD, + NA_SE_PL_CHANGE_ARMS, + NA_SE_PL_CHANGE_ARMS, + NA_SE_PL_CHANGE_ARMS, + NA_SE_PL_CHANGE_ARMS, + NA_SE_PL_CHANGE_ARMS, + NA_SE_PL_CHANGE_ARMS, + NA_SE_PL_CATCH_BOOMERANG, + NA_SE_EV_DIVE_INTO_WATER, + NA_SE_EV_JUMP_OUT_WATER, + NA_SE_PL_SWIM, + NA_SE_PL_THROW, + NA_SE_PL_BODY_BOUND, + NA_SE_PL_ROLL, + NA_SE_PL_SKIP, + NA_SE_PL_BODY_HIT, + NA_SE_PL_DAMAGE, + NA_SE_PL_SLIP, + NA_SE_PL_SLIP, + NA_SE_PL_SLIP, + NA_SE_PL_SLIP_SAND, + NA_SE_PL_SLIP_CONCRETE, + NA_SE_PL_SLIP_DIRT, + NA_SE_PL_SLIP_WATER0, + NA_SE_PL_SLIP_WATER1, + NA_SE_PL_SLIP_WATER2, + NA_SE_PL_SLIP_MAGMA, + NA_SE_PL_SLIP_GRASS, + NA_SE_PL_SLIP_GLASS, + NA_SE_PL_SLIP_LADDER, + NA_SE_PL_SLIP_GLASS, + NA_SE_PL_SLIP_HEAVYBOOTS, + NA_SE_PL_SLIP_ICE, + NA_SE_PL_BOUND, + NA_SE_PL_BOUND, + NA_SE_PL_BOUND_SAND, + NA_SE_PL_BOUND_CONCRETE, + NA_SE_PL_BOUND_DIRT, + NA_SE_PL_BOUND_WATER0, + NA_SE_PL_BOUND_WATER1, + NA_SE_PL_BOUND_WATER2, + NA_SE_PL_BOUND_MAGMA, + NA_SE_PL_BOUND_GRASS, + NA_SE_PL_BOUND_WOOD, + NA_SE_PL_BOUND_LADDER, + NA_SE_PL_BOUND_WOOD, + NA_SE_PL_BOUND_HEAVYBOOTS, + NA_SE_PL_BOUND_ICE, + NA_SE_PL_FACE_UP, + NA_SE_PL_DIVE_BUBBLE, + NA_SE_PL_MOVE_BUBBLE, + NA_SE_PL_METALEFFECT_KID, + NA_SE_PL_METALEFFECT_ADULT, + NA_SE_PL_SPARK - SFX_FLAG, + NA_SE_IT_SWORD_IMPACT, + NA_SE_IT_SWORD_SWING, + NA_SE_IT_SWORD_PUTAWAY, + NA_SE_IT_SWORD_PICKOUT, + NA_SE_IT_ARROW_SHOT, + NA_SE_IT_BOOMERANG_THROW, + NA_SE_IT_SHIELD_BOUND, + NA_SE_IT_SHIELD_BOUND, + NA_SE_IT_BOW_DRAW, + NA_SE_IT_SHIELD_REFLECT_SW, + NA_SE_IT_ARROW_STICK_HRAD, + NA_SE_IT_HAMMER_HIT, + NA_SE_IT_HOOKSHOT_CHAIN - SFX_FLAG, + NA_SE_IT_SHIELD_REFLECT_MG, + NA_SE_IT_BOMB_IGNIT - SFX_FLAG, + NA_SE_IT_BOMB_EXPLOSION, + NA_SE_IT_BOMB_UNEXPLOSION, + NA_SE_IT_BOOMERANG_FLY - SFX_FLAG, + NA_SE_IT_SWORD_STRIKE, + NA_SE_IT_HAMMER_SWING, + NA_SE_IT_HOOKSHOT_REFLECT, + NA_SE_IT_ARROW_STICK_CRE, + NA_SE_IT_ARROW_STICK_CRE, + NA_SE_IT_ARROW_STICK_OBJ, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_IT_SWORD_SWING_HARD, + NA_SE_IT_WALL_HIT_HARD, + NA_SE_IT_WALL_HIT_SOFT, + NA_SE_IT_WALL_HIT_SOFT, + NA_SE_IT_STONE_HIT, + NA_SE_IT_WOODSTICK_BROKEN, + NA_SE_IT_LASH, + NA_SE_IT_SHIELD_POSTURE, + NA_SE_IT_SLING_SHOT, + NA_SE_IT_SLING_DRAW, + NA_SE_IT_SWORD_CHARGE - SFX_FLAG, + NA_SE_IT_ROLLING_CUT, + NA_SE_IT_SWORD_STRIKE_HARD, + NA_SE_IT_SLING_REFLECT, + NA_SE_IT_SHIELD_REMOVE, + NA_SE_IT_HOOKSHOT_READY, + NA_SE_IT_HOOKSHOT_RECEIVE, + NA_SE_IT_HOOKSHOT_STICK_OBJ, + NA_SE_IT_SWORD_REFLECT_MG, + NA_SE_IT_DEKU, + NA_SE_IT_BOW_FLICK, + NA_SE_IT_BOW_FLICK, + NA_SE_IT_BOW_FLICK, + NA_SE_IT_BOMBCHU_MOVE, + NA_SE_IT_SHIELD_CHARGE_LV1, + NA_SE_IT_SHIELD_CHARGE_LV2, + NA_SE_IT_SHIELD_CHARGE_LV3, + NA_SE_IT_SLING_FLICK, + NA_SE_IT_SWORD_STICK_STN, + NA_SE_IT_REFLECTION_WOOD, + NA_SE_IT_SHIELD_REFLECT_MG2, + NA_SE_IT_MAGIC_ARROW_SHOT, + NA_SE_IT_EXPLOSION_FRAME, + NA_SE_IT_EXPLOSION_ICE, + NA_SE_IT_YOBI19 - SFX_FLAG, + NA_SE_FISHING_REEL_SLOW2 - SFX_FLAG, + NA_SE_OC_DOOR_OPEN, + NA_SE_EV_DOOR_CLOSE, + NA_SE_EV_EXPLOSION, + NA_SE_EV_HORSE_WALK, + NA_SE_EV_HORSE_RUN, + NA_SE_EV_HORSE_NEIGH, + NA_SE_EV_RIVER_STREAM - SFX_FLAG, + NA_SE_EV_WATER_WALL_BIG - SFX_FLAG, + NA_SE_EV_DIVE_WATER, + NA_SE_EV_OUT_OF_WATER, + NA_SE_EV_ROCK_SLIDE - SFX_FLAG, + NA_SE_EV_MAGMA_LEVEL - SFX_FLAG, + NA_SE_EV_MAGMA_LEVEL - SFX_FLAG, + NA_SE_EV_BRIDGE_OPEN - SFX_FLAG, + NA_SE_EV_BRIDGE_CLOSE - SFX_FLAG, + NA_SE_EV_BRIDGE_OPEN_STOP, + NA_SE_EV_BRIDGE_CLOSE_STOP, + NA_SE_EV_WALL_BROKEN, + NA_SE_EV_CHICKEN_CRY_N, + NA_SE_EV_CHICKEN_CRY_A, + NA_SE_EV_CHICKEN_CRY_M, + NA_SE_EV_SLIDE_DOOR_OPEN, + NA_SE_EV_FOOT_SWITCH, + NA_SE_EV_HORSE_GROAN, + NA_SE_EV_BOMB_DROP_WATER, + NA_SE_EV_BOMB_DROP_WATER, + NA_SE_EV_HORSE_JUMP, + NA_SE_EV_HORSE_LAND, + NA_SE_EV_HORSE_SLIP, + NA_SE_EV_FAIRY_DASH, + NA_SE_EV_SLIDE_DOOR_CLOSE, + NA_SE_EV_STONE_BOUND, + NA_SE_EV_STONE_STATUE_OPEN - SFX_FLAG, + NA_SE_EV_TBOX_UNLOCK, + NA_SE_EV_TBOX_OPEN, + NA_SE_SY_TIMER - SFX_FLAG, + NA_SE_EV_FLAME_IGNITION, + NA_SE_EV_SPEAR_HIT, + NA_SE_EV_ELEVATOR_MOVE - SFX_FLAG, + NA_SE_EV_WARP_HOLE - SFX_FLAG, + NA_SE_EV_LINK_WARP, + NA_SE_EV_PILLAR_SINK - SFX_FLAG, + NA_SE_EV_WATER_WALL - SFX_FLAG, + NA_SE_EV_RIVER_STREAM_S - SFX_FLAG, + NA_SE_EV_RIVER_STREAM_F - SFX_FLAG, + NA_SE_EV_HORSE_LAND2, + NA_SE_EV_HORSE_SANDDUST, + NA_SE_EV_BOMB_BOUND, + NA_SE_EV_BOMB_BOUND, + NA_SE_EV_WATERDROP - SFX_FLAG, + NA_SE_EV_TORCH - SFX_FLAG, + NA_SE_EV_MAGMA_LEVEL_M - SFX_FLAG, + NA_SE_EV_FIRE_PILLAR - SFX_FLAG, + NA_SE_EV_FIRE_PLATE - SFX_FLAG, + NA_SE_EV_BLOCK_BOUND, + NA_SE_EV_METALDOOR_SLIDE - SFX_FLAG, + NA_SE_EV_METALDOOR_STOP, + NA_SE_EV_BLOCK_SHAKE, + NA_SE_EV_BOX_BREAK, + NA_SE_EV_HAMMER_SWITCH, + NA_SE_EV_MAGMA_LEVEL_L - SFX_FLAG, + NA_SE_EV_SPEAR_FENCE, + NA_SE_EV_GANON_HORSE_NEIGH, + NA_SE_EV_GANON_HORSE_GROAN, + NA_SE_EV_FANTOM_WARP_S, + NA_SE_EV_FANTOM_WARP_L - SFX_FLAG, + NA_SE_EV_FOUNTAIN - SFX_FLAG, + NA_SE_EV_KID_HORSE_WALK, + NA_SE_EV_KID_HORSE_RUN, + NA_SE_EV_KID_HORSE_NEIGH, + NA_SE_EV_KID_HORSE_GROAN, + NA_SE_EV_WHITE_OUT, + NA_SE_EV_LIGHT_GATHER - SFX_FLAG, + NA_SE_EV_TREE_CUT, + NA_SE_EV_WATERDROP, + NA_SE_EV_TORCH, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_EN_DODO_J_WALK, + NA_SE_EN_DODO_J_CRY, + NA_SE_EN_DODO_J_FIRE - SFX_FLAG, + NA_SE_EN_DODO_J_DAMAGE, + NA_SE_EN_DODO_J_DEAD, + NA_SE_EN_DODO_K_CRY, + NA_SE_EN_DODO_K_DAMAGE, + NA_SE_EN_DODO_K_DEAD, + NA_SE_EN_DODO_K_WALK, + NA_SE_EN_DODO_K_FIRE - SFX_FLAG, + NA_SE_EN_GOMA_WALK, + NA_SE_EN_GOMA_HIGH, + NA_SE_EN_GOMA_CLIM, + NA_SE_EN_GOMA_DOWN, + NA_SE_EN_GOMA_CRY1, + NA_SE_EN_GOMA_CRY2, + NA_SE_EN_GOMA_DAM1, + NA_SE_EN_GOMA_DAM2, + NA_SE_EN_GOMA_DEAD, + NA_SE_EN_GOMA_UNARI, + NA_SE_EN_GOMA_EGG1, + NA_SE_EN_GOMA_EGG2, + NA_SE_EN_GOMA_JR_WALK, + NA_SE_EN_GOMA_JR_CRY, + NA_SE_EN_GOMA_JR_DAM1, + NA_SE_EN_GOMA_JR_DAM2, + NA_SE_EN_GOMA_JR_DEAD, + NA_SE_EN_GOMA_DEMO_EYE, + NA_SE_EN_GOMA_LAST - SFX_FLAG, + NA_SE_EN_GOMA_UNARI2, + NA_SE_EN_DODO_M_CRY, + NA_SE_EN_DODO_M_DEAD, + NA_SE_EN_DODO_M_MOVE, + NA_SE_EN_DODO_M_DOWN, + NA_SE_EN_DODO_M_UP, + NA_SE_EN_DODO_M_GND, + NA_SE_EN_RIZA_CRY, + NA_SE_EN_RIZA_ATTACK, + NA_SE_EN_RIZA_DAMAGE, + NA_SE_EN_RIZA_WARAU, + NA_SE_EN_RIZA_DEAD, + NA_SE_EN_RIZA_WALK, + NA_SE_EN_RIZA_JUMP, + NA_SE_EN_RIZA_ONGND, + NA_SE_EN_RIZA_DOWN, + NA_SE_EN_STAL_WARAU, + NA_SE_EN_STAL_SAKEBI, + NA_SE_EN_STAL_DAMAGE, + NA_SE_EN_STAL_DEAD, + NA_SE_EN_STAL_JUMP, + NA_SE_EN_STAL_WALK, + NA_SE_EN_RIZA_DOWN, + NA_SE_EN_FFLY_ATTACK, + NA_SE_EN_FFLY_FLY, + NA_SE_EN_FFLY_DEAD, + NA_SE_EN_AMOS_WALK, + NA_SE_EN_AMOS_WAVE, + NA_SE_EN_AMOS_DEAD, + NA_SE_EN_AMOS_DAMAGE, + NA_SE_EN_AMOS_VOICE, + NA_SE_EN_DODO_K_COLI, + NA_SE_EN_DODO_K_COLI2, + NA_SE_EN_DODO_K_ROLL - SFX_FLAG, + NA_SE_EN_DODO_K_BREATH - SFX_FLAG, + NA_SE_EN_DODO_K_DRINK, + NA_SE_EN_DODO_K_DOWN - SFX_FLAG, + NA_SE_EN_DODO_K_OTAKEBI, + NA_SE_EN_DODO_K_END, + NA_SE_EN_DODO_K_LAST - SFX_FLAG, + NA_SE_EN_DODO_K_LAVA, + NA_SE_EN_DODO_J_BREATH - SFX_FLAG, + NA_SE_EN_DODO_J_TAIL, + NA_SE_EN_RIZA_DOWN, + NA_SE_EN_DEKU_MOUTH, + NA_SE_EN_DEKU_ATTACK, + NA_SE_EN_DEKU_DAMAGE, + NA_SE_EN_DEKU_DEAD, + NA_SE_EN_DEKU_JR_MOUTH, + NA_SE_EN_DEKU_JR_ATTACK, + NA_SE_EN_DEKU_JR_DEAD, + NA_SE_EN_DODO_M_GND, + NA_SE_EN_TAIL_FLY - SFX_FLAG, + NA_SE_EN_TAIL_CRY, + NA_SE_EN_STALTU_DOWN, + NA_SE_EN_STALTU_UP, + NA_SE_EN_STALTU_LAUGH, + NA_SE_EN_STALTU_DAMAGE, + NA_SE_EN_STAL_JUMP, + NA_SE_EN_DODO_M_GND, + NA_SE_EN_TEKU_DEAD, + NA_SE_EN_TEKU_WALK, + NA_SE_EN_PO_KANTERA, + NA_SE_EN_PO_FLY - SFX_FLAG, + NA_SE_EN_PO_AWAY - SFX_FLAG, + NA_SE_EN_PO_APPEAR, + NA_SE_EN_PO_DISAPPEAR, + NA_SE_EN_PO_DAMAGE, + NA_SE_EN_PO_DEAD, + NA_SE_EN_PO_DEAD2, + NA_SE_EN_EXTINCT, + NA_SE_EN_NUTS_UP, + NA_SE_EN_NUTS_DOWN, + NA_SE_EN_NUTS_THROW, + NA_SE_EN_NUTS_WALK, + NA_SE_EN_NUTS_DAMAGE, + NA_SE_EN_NUTS_DEAD, + NA_SE_EN_STALTU_ROLL, + NA_SE_EN_STALWALL_DEAD, + NA_SE_EN_TEKU_DAMAGE, + NA_SE_EN_FALL_AIM, + NA_SE_EN_FALL_UP, + NA_SE_EN_FALL_CATCH, + NA_SE_EN_FALL_LAND, + NA_SE_EN_FALL_WALK, + NA_SE_EN_FALL_DAMAGE, + NA_SE_EN_BIRI_FLY, + NA_SE_EN_BIRI_JUMP, + NA_SE_EN_BIRI_SPARK - SFX_FLAG, + NA_SE_EN_FANTOM_TRANSFORM, + NA_SE_EN_FANTOM_TRANSFORM, + NA_SE_EN_FANTOM_THUNDER, + NA_SE_EN_FANTOM_SPARK, + NA_SE_EN_FANTOM_FLOAT - SFX_FLAG, + NA_SE_EN_FANTOM_MASIC1, + NA_SE_EN_FANTOM_MASIC2, + NA_SE_EN_FANTOM_FIRE - SFX_FLAG, + NA_SE_EN_FANTOM_HIT_THUNDER, + NA_SE_EN_FANTOM_ATTACK, + NA_SE_EN_FANTOM_STICK, + NA_SE_EN_FANTOM_EYE, + NA_SE_EN_FANTOM_LAST, + NA_SE_EN_FANTOM_THUNDER_GND, + NA_SE_EN_FANTOM_DAMAGE, + NA_SE_EN_FANTOM_DEAD, + NA_SE_EN_FANTOM_LAUGH, + NA_SE_EN_FANTOM_DAMAGE2, + NA_SE_EN_FANTOM_VOICE, + NA_SE_EN_MORIBLIN_WALK, + NA_SE_EN_MORIBLIN_SLIDE, + NA_SE_EN_MORIBLIN_ATTACK, + NA_SE_EN_MORIBLIN_VOICE, + NA_SE_EN_MORIBLIN_SPEAR_AT, + NA_SE_EN_MORIBLIN_SPEAR_NORM, + NA_SE_EN_MORIBLIN_DEAD, + NA_SE_EN_NUTS_THROW, + NA_SE_EN_OCTAROCK_FLOAT, + NA_SE_EN_OCTAROCK_JUMP, + NA_SE_EN_OCTAROCK_LAND, + NA_SE_EN_OCTAROCK_SINK, + NA_SE_EN_OCTAROCK_BUBLE, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_SY_WIN_OPEN, + NA_SE_SY_WIN_CLOSE, + NA_SE_SY_CORRECT_CHIME, + NA_SE_SY_GET_RUPY, + NA_SE_SY_MESSAGE_WOMAN, + NA_SE_SY_MESSAGE_MAN, + NA_SE_SY_ERROR, + NA_SE_SY_TRE_BOX_APPEAR, + NA_SE_SY_TRE_BOX_APPEAR, + NA_SE_SY_DECIDE, + NA_SE_SY_CURSOR, + NA_SE_SY_CANCEL, + NA_SE_SY_HP_RECOVER, + NA_SE_SY_ATTENTION_ON, + NA_SE_SY_ATTENTION_ON, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_SY_LOCK_ON, + NA_SE_SY_LOCK_ON, + NA_SE_SY_LOCK_OFF, + NA_SE_SY_LOCK_ON_HUMAN, + NA_SE_SY_CAMERA_ZOOM_UP, + NA_SE_SY_CAMERA_ZOOM_DOWN, + NA_SE_SY_ATTENTION_ON_OLD, + NA_SE_SY_ATTENTION_URGENCY, + NA_SE_SY_MESSAGE_PASS, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_SY_PIECE_OF_HEART, + NA_SE_SY_GET_ITEM, + NA_SE_SY_WIN_SCROLL_LEFT, + NA_SE_SY_WIN_SCROLL_RIGHT, + NA_SE_SY_OCARINA_ERROR, + NA_SE_SY_CAMERA_ZOOM_UP_2, + NA_SE_SY_CAMERA_ZOOM_DOWN_2, + NA_SE_SY_GLASSMODE_ON, + NA_SE_SY_GLASSMODE_OFF, + NA_SE_SY_ATTENTION_ON, + NA_SE_SY_ATTENTION_URGENCY, + NA_SE_OC_OCARINA, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_LAND - SFX_FLAG, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_N, + NA_SE_VO_LI_SWORD_L, + NA_SE_VO_LI_SWORD_L, + NA_SE_VO_LI_MAGIC_ATTACK, + NA_SE_VO_LI_LASH, + NA_SE_VO_LI_HANG, + NA_SE_VO_LI_AUTO_JUMP, + NA_SE_VO_LI_CLIMB_END, + NA_SE_VO_LI_CLIMB_END, + NA_SE_VO_LI_CLIMB_END, + NA_SE_VO_LI_CLIMB_END, + NA_SE_VO_LI_DAMAGE_S, + NA_SE_VO_LI_DAMAGE_S, + NA_SE_VO_LI_FALL_L, + NA_SE_VO_LI_FALL_S, + NA_SE_VO_LI_FALL_L, + NA_SE_VO_LI_FALL_L, + NA_SE_VO_LI_BREATH_REST, + NA_SE_VO_LI_BREATH_REST, + NA_SE_VO_LI_DOWN, + NA_SE_VO_LI_TAKEN_AWAY, + NA_SE_VO_LI_HELD, + NA_SE_VO_NAVY_HELLO, + NA_SE_VO_NAVY_HEAR, + NA_SE_VO_NAVY_ENEMY, + NA_SE_VO_NAVY_HELLO, + NA_SE_VO_NAVY_HEAR, + NA_SE_VO_NAVY_ENEMY, + NA_SE_VO_TA_SLEEP, + NA_SE_EN_VALVAISA_APPEAR - SFX_FLAG, + NA_SE_EN_VALVAISA_ROAR, + NA_SE_EN_VALVAISA_MAHI1, + NA_SE_EN_VALVAISA_MAHI2, + NA_SE_EN_VALVAISA_KNOCKOUT, + NA_SE_EN_VALVAISA_DAMAGE1, + NA_SE_EN_VALVAISA_DAMAGE2, + NA_SE_EN_VALVAISA_ROCK, + NA_SE_EN_VALVAISA_LAND, + NA_SE_EN_VALVAISA_DEAD, + NA_SE_EN_VALVAISA_BURN - SFX_FLAG, + NA_SE_EN_VALVAISA_FIRE - SFX_FLAG, + NA_SE_EN_VALVAISA_LAND2, + NA_SE_EN_MONBLIN_HAM_LAND, + NA_SE_EN_MONBLIN_HAM_DOWN, + NA_SE_EN_MONBLIN_HAM_UP, + NA_SE_EN_REDEAD_CRY, + NA_SE_EN_REDEAD_AIM, + NA_SE_EN_REDEAD_DAMAGE, + NA_SE_EN_RIZA_DOWN, + NA_SE_EN_REDEAD_DEAD, + NA_SE_EN_REDEAD_ATTACK, + NA_SE_EN_PO_LAUGH, + NA_SE_EN_PO_CRY, + NA_SE_EN_PO_ROLL, + NA_SE_EN_PO_LAUGH2, + NA_SE_EN_MOFER_APPEAR - SFX_FLAG, + NA_SE_EN_MOFER_ATTACK - SFX_FLAG, + NA_SE_EN_MOFER_WAVE, + NA_SE_EN_MOFER_CATCH, + NA_SE_EN_MOFER_CORE_DAMAGE, + NA_SE_EN_MOFER_CUT, + NA_SE_EN_MOFER_MOVE_DEMO - SFX_FLAG, + NA_SE_EN_MOFER_BUBLE_DEMO, + NA_SE_EN_MOFER_CORE_JUMP, + NA_SE_EN_GOLON_WAKE_UP, + NA_SE_EN_GOLON_SIT_DOWN, + NA_SE_EN_DODO_M_GND, + NA_SE_EN_DEADHAND_BITE, + NA_SE_EN_DEADHAND_WALK, + NA_SE_EN_DEADHAND_GRIP, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + NA_SE_PL_WALK_GROUND - SFX_FLAG, + }; + + if (BREG(32) != 0) { + BREG(32)--; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + func_80078914(&zeroVec, unkSfx[BREG(33)]); + } + if (BREG(34) != 0) { + BREG(34) = 0; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | (u16)BREG(35)); + } +} + +void BossMo_Reset(void) { + sMorphaCore = NULL; + sMorphaTent1 = NULL; + sMorphaTent2 = NULL; + memset(sEffects, 0, sizeof(sEffects)); + sSeed1 = 0; + sSeed2 = 0; + sSeed3 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.h b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.h new file mode 100644 index 000000000..881dbe222 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.h @@ -0,0 +1,135 @@ +#ifndef Z_BOSS_MO_H +#define Z_BOSS_MO_H + +#include "ultra64.h" +#include "global.h" + +struct BossMo; + +typedef void (*BossMoActionFunc)(struct BossMo*, GlobalContext*); + +typedef enum { + /* 0 */ MO_TENT_ACTION_STATE, + /* 1 */ MO_TENT_MOVE_TIMER, + /* 2 */ MO_TENT_VAR_TIMER, + /* 3 */ MO_TENT_UNK_TIMER, + /* 4 */ MO_TENT_INVINC_TIMER, + /* 5 */ MO_TENT_BASE_TEX1_X, + /* 6 */ MO_TENT_BASE_TEX1_Y, + /* 7 */ MO_TENT_BASE_TEX2_X, + /* 8 */ MO_TENT_BASE_TEX2_Y, + /* 9 */ MO_TENT_SHORT_MAX +} BossMoTentS16Var; + +typedef enum { + /* 0 */ MO_CORE_ACTION_STATE, + /* 1 */ MO_CORE_MOVE_TIMER, + /* 2 */ MO_CORE_VAR_TIMER, + /* 3 */ MO_CORE_DMG_FLASH_TIMER, + /* 4 */ MO_CORE_INVINC_TIMER, + /* 5 */ MO_CORE_SHORT_5, + /* 6 */ MO_CORE_POS_IN_TENT, + /* 7 */ MO_CORE_DRAW_SHADOW, + /* 8 */ MO_CORE_WAIT_IN_WATER, + /* 9 */ MO_CORE_SHORT_MAX +} BossMoCoreS16Var; + +typedef enum { + /* 0 */ MO_TENT_SWING_LAG_X, + /* 1 */ MO_TENT_SWING_SIZE_X, + /* 2 */ MO_TENT_SWING_RATE_X, + /* 3 */ MO_TENT_SWING_LAG_Z, + /* 4 */ MO_TENT_SWING_SIZE_Z, + /* 5 */ MO_TENT_SWING_RATE_Z, + /* 6 */ MO_TENT_MAX_STRETCH, + /* 7 */ MO_TENT_FLOAT_MAX +} BossMoTentF32Var; + +typedef enum { + /* 0 */ MO_CORE_INTRO_WATER_ALPHA, + /* 1 */ MO_CORE_FLOAT_MAX +} BossMoCoreF32Var; + +#define MO_SHORT_MAX MAX((s32)MO_TENT_SHORT_MAX, (s32)MO_CORE_SHORT_MAX) +#define MO_FLOAT_MAX MAX((s32)MO_TENT_FLOAT_MAX, (s32)MO_CORE_FLOAT_MAX) + +typedef struct BossMo { + /* 0x0000 */ Actor actor; + /* 0x014C */ Actor* otherTent; + /* 0x0150 */ BossMoActionFunc actionFunc; + /* 0x0154 */ u8 tent2KillTimer; + /* 0x0155 */ u8 hitCount; + /* 0x0156 */ u8 tentSpawnPos; + /* 0x0158 */ s16 work[MO_SHORT_MAX]; + /* 0x016A */ s16 widthIndex; + /* 0x016C */ s16 pulsePhase; + /* 0x016E */ s16 xSwing; + /* 0x0170 */ s16 zSwing; + /* 0x0172 */ s16 cutIndex; + /* 0x0174 */ s16 meltIndex; + /* 0x0176 */ s16 linkToLeft; + /* 0x0178 */ s16 mashCounter; + /* 0x017A */ s16 noBubbles; + /* 0x017C */ s16 sfxTimer; + /* 0x017E */ s16 timers[5]; + /* 0x0188 */ f32 fwork[MO_FLOAT_MAX]; + /* 0x01A4 */ f32 baseAlpha; + /* 0x01A8 */ f32 cutScale; + /* 0x01AC */ f32 waterTex1x; + /* 0x01B0 */ f32 waterTex1y; + /* 0x01B4 */ f32 waterTex2x; + /* 0x01B8 */ f32 waterTex2y; + /* 0x01BC */ f32 waterLevel; + /* 0x01C0 */ f32 flattenRate; + /* 0x01C4 */ f32 waterTexAlpha; + /* 0x01C8 */ f32 waterLevelMod; + /* 0x01CC */ s16 baseBubblesTimer; + /* 0x01CE */ s16 attackAngleMod; + /* 0x01D0 */ u8 unk_1D0; // unused? + /* 0x01D1 */ u8 drawActor; + /* 0x01D2 */ u8 linkHitTimer; + /* 0x01D4 */ Vec3f targetPos; + /* 0x01E0 */ f32 tentRippleSize; + /* 0x01E4 */ PosRot grabPosRot; + /* 0x01F8 */ f32 tentWidth[300]; + /* 0x06A8 */ Vec3f tentStretch[41]; + /* 0x0894 */ Vec3f tentScale[41]; + /* 0x0A80 */ Vec3f tentRipple[41]; + /* 0x0C6C */ Vec3s tentRot[41]; + /* 0x0D64 */ f32 tentMaxAngle; + /* 0x0D68 */ f32 tentSpeed; + /* 0x0D6C */ f32 tentPulse; + /* 0x0D70 */ Vec3f tentPos[41]; + /* 0x0F5C */ f32 cameraZoom; + /* 0x0F60 */ s16 csState; + /* 0x0F62 */ s16 csCamera; + /* 0x0F64 */ s16 targetIndex; + /* 0x0F68 */ Vec3f cameraEye; + /* 0x0F74 */ Vec3f cameraAt; + /* 0x0F80 */ Vec3f cameraUp; + /* 0x0F8C */ char unk_F8C[0x18]; + /* 0x0FA4 */ Vec3f cameraEyeVel; + /* 0x0FB0 */ Vec3f cameraAtVel; + /* 0x0FBC */ Vec3f cameraNextEye; + /* 0x0FC8 */ Vec3f cameraEyeMaxVel; + /* 0x0FD4 */ Vec3f cameraNextAt; + /* 0x0FE0 */ Vec3f cameraAtMaxVel; + /* 0x0FEC */ f32 cameraSpeedMod; + /* 0x0FF0 */ f32 cameraAccel; + /* 0x0FF4 */ char unk_FF4[8]; + /* 0x0FFC */ f32 cameraDist; + /* 0x1000 */ f32 cameraSpeed; + /* 0x1004 */ f32 cameraYaw; + /* 0x1008 */ f32 cameraYawRate; + /* 0x100C */ f32 cameraYawShake; + /* 0x1010 */ Vec3f tentTipPos; + /* 0x101C */ ColliderJntSph tentCollider; + /* 0x103C */ ColliderJntSphElement tentElements[19]; + /* 0x14FC */ ColliderCylinder coreCollider; + /* 0x1548 */ char unk_1548[0x44]; +} BossMo; // size = 0x158C + +#define BOSSMO_CORE -1 +#define BOSSMO_TENTACLE 100 + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo_colchk.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo_colchk.c new file mode 100644 index 000000000..3826a4485 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo_colchk.c @@ -0,0 +1,247 @@ +#include "global.h" +#include "z_boss_mo.h" + +static ColliderJntSphElementInit sJntSphElementsInit[19] = { + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 0 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 0 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 2, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 3, { { 0, 0, 0 }, 24 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 4, { { 0, 0, 0 }, 22 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 5, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 6, { { 0, 0, 0 }, 18 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 7, { { 0, 0, 0 }, 16 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 8, { { 0, 0, 0 }, 14 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 9, { { 0, 0, 0 }, 12 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 10, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 11, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 12, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 14, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 16, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 17, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK4, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 18, { { 0, 0, 0 }, 10 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 19, + sJntSphElementsInit, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 40, -20, { 0, 0, 0 } }, +}; diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c new file mode 100644 index 000000000..6fae3778b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -0,0 +1,3248 @@ +/* + * File: z_boss_sst.c + * Overlay: ovl_Boss_Sst + * Description: Bongo Bongo + */ + +#include "z_boss_sst.h" +#include "objects/object_sst/object_sst.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/actors/ovl_Bg_Sst_Floor/z_bg_sst_floor.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_10) + +#define vParity actionVar +#define vVanish actionVar + +#define LEFT 0 +#define RIGHT 1 +#define OTHER_HAND(hand) ((BossSst*)hand->actor.child) +#define HAND_STATE(hand) sHandState[hand->actor.params] + +#define ROOM_CENTER_X -50.0f +#define ROOM_CENTER_Y 0.0f +#define ROOM_CENTER_Z 0.0f + +typedef enum { + /* 0 */ HAND_WAIT, + /* 1 */ HAND_BEAT, + /* 2 */ HAND_RETREAT, + /* 3 */ HAND_SLAM, + /* 4 */ HAND_SWEEP, + /* 5 */ HAND_PUNCH, + /* 6 */ HAND_CLAP, + /* 7 */ HAND_GRAB, + /* 8 */ HAND_DAMAGED, + /* 9 */ HAND_FROZEN, + /* 10 */ HAND_BREAK_ICE, + /* 11 */ HAND_DEATH +} BossSstHandState; + +typedef enum { + /* 0 */ BONGO_NULL, + /* 1 */ BONGO_ICE, + /* 2 */ BONGO_SHOCKWAVE, + /* 3 */ BONGO_SHADOW +} BossSstEffectMode; + +void BossSst_Init(Actor* thisx, GlobalContext* globalCtx); +void BossSst_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossSst_UpdateHand(Actor* thisx, GlobalContext* globalCtx); +void BossSst_UpdateHead(Actor* thisx, GlobalContext* globalCtx); +void BossSst_DrawHand(Actor* thisx, GlobalContext* globalCtx); +void BossSst_DrawHead(Actor* thisx, GlobalContext* globalCtx); +void BossSst_UpdateEffect(Actor* thisx, GlobalContext* globalCtx); +void BossSst_DrawEffect(Actor* thisx, GlobalContext* globalCtx); +void BossSst_Reset(void); + + +void BossSst_HeadSfx(BossSst* this, u16 sfxId); + +void BossSst_HeadSetupLurk(BossSst* this); +void BossSst_HeadLurk(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupIntro(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadIntro(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupNeutral(BossSst* this); +void BossSst_HeadNeutral(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadWait(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HeadSetupDamagedHand(BossSst* this, s32 bothHands); +void BossSst_HeadDamagedHand(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupReadyCharge(BossSst* this); +void BossSst_HeadReadyCharge(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupCharge(BossSst* this); +void BossSst_HeadCharge(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupEndCharge(BossSst* this); +void BossSst_HeadEndCharge(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HeadSetupFrozenHand(BossSst* this); +void BossSst_HeadFrozenHand(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupUnfreezeHand(BossSst* this); +void BossSst_HeadUnfreezeHand(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HeadStunned(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupVulnerable(BossSst* this); +void BossSst_HeadVulnerable(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadDamage(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupRecover(BossSst* this); +void BossSst_HeadRecover(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HeadDeath(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupThrash(BossSst* this); +void BossSst_HeadThrash(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupDarken(BossSst* this); +void BossSst_HeadDarken(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupFall(BossSst* this); +void BossSst_HeadFall(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupMelt(BossSst* this); +void BossSst_HeadMelt(BossSst* this, GlobalContext* globalCtx); +void BossSst_HeadSetupFinish(BossSst* this); +void BossSst_HeadFinish(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandGrabPlayer(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandReleasePlayer(BossSst* this, GlobalContext* globalCtx, s32 dropPlayer); +void BossSst_HandSelectAttack(BossSst* this); +void BossSst_HandSetDamage(BossSst* this, s32 damage); +void BossSst_HandSetInvulnerable(BossSst* this, s32 isInv); + +void BossSst_HandSetupWait(BossSst* this); +void BossSst_HandWait(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupDownbeat(BossSst* this); +void BossSst_HandDownbeat(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupOffbeat(BossSst* this); +void BossSst_HandOffbeat(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupDownbeatEnd(BossSst* this); +void BossSst_HandDownbeatEnd(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupOffbeatEnd(BossSst* this); +void BossSst_HandOffbeatEnd(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandReadySlam(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupSlam(BossSst* this); +void BossSst_HandSlam(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandEndSlam(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandReadySweep(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupSweep(BossSst* this); +void BossSst_HandSweep(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandReadyPunch(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupPunch(BossSst* this); +void BossSst_HandPunch(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandReadyClap(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupClap(BossSst* this); +void BossSst_HandClap(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupEndClap(BossSst* this); +void BossSst_HandEndClap(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandReadyGrab(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupGrab(BossSst* this); +void BossSst_HandGrab(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupCrush(BossSst* this); +void BossSst_HandCrush(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupEndCrush(BossSst* this); +void BossSst_HandEndCrush(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupSwing(BossSst* this); +void BossSst_HandSwing(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandSetupRetreat(BossSst* this); +void BossSst_HandRetreat(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandSetupReel(BossSst* this); +void BossSst_HandReel(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupReadyShake(BossSst* this); +void BossSst_HandReadyShake(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupShake(BossSst* this); +void BossSst_HandShake(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupReadyCharge(BossSst* this); +void BossSst_HandReadyCharge(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandSetupFrozen(BossSst* this); +void BossSst_HandFrozen(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupReadyBreakIce(BossSst* this); +void BossSst_HandReadyBreakIce(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupBreakIce(BossSst* this); +void BossSst_HandBreakIce(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandStunned(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandDamage(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupRecover(BossSst* this); +void BossSst_HandRecover(BossSst* this, GlobalContext* globalCtx); + +void BossSst_HandSetupThrash(BossSst* this); +void BossSst_HandThrash(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupDarken(BossSst* this); +void BossSst_HandDarken(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupFall(BossSst* this); +void BossSst_HandFall(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupMelt(BossSst* this); +void BossSst_HandMelt(BossSst* this, GlobalContext* globalCtx); +void BossSst_HandSetupFinish(BossSst* this); +void BossSst_HandFinish(BossSst* this, GlobalContext* globalCtx); + +void BossSst_SpawnHeadShadow(BossSst* this); +void BossSst_SpawnHandShadow(BossSst* this); +void BossSst_SpawnShockwave(BossSst* this); +void BossSst_SpawnIceCrystal(BossSst* this, s32 index); +void BossSst_SpawnIceShard(BossSst* this); +void BossSst_IceShatter(BossSst* this); + +#include "overlays/ovl_Boss_Sst/ovl_Boss_Sst.h" + +static BossSst* sHead; +static BossSst* sHands[2]; +static BgSstFloor* sFloor; + +static Vec3f sRoomCenter = { ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z }; +static Vec3f sHandOffsets[2]; +static s16 sHandYawOffsets[2]; + +static s16 sCutsceneCamera; +static Vec3f sCameraAt = { ROOM_CENTER_X + 50.0f, ROOM_CENTER_Y + 0.0f, ROOM_CENTER_Z + 0.0f }; +static Vec3f sCameraEye = { ROOM_CENTER_X + 150.0f, ROOM_CENTER_Y + 100.0f, ROOM_CENTER_Z + 0.0f }; +static Vec3f sCameraAtVel = { 0.0f, 0.0f, 0.0f }; +static Vec3f sCameraEyeVel = { 0.0f, 0.0f, 0.0f }; + +static Vec3f sCameraAtPoints[] = { + { ROOM_CENTER_X - 50.0f, ROOM_CENTER_Y + 300.0f, ROOM_CENTER_Z + 0.0f }, + { ROOM_CENTER_X + 150.0f, ROOM_CENTER_Y + 300.0f, ROOM_CENTER_Z + 100.0f }, + { ROOM_CENTER_X + 0.0f, ROOM_CENTER_Y + 600.0f, ROOM_CENTER_Z + 100.0f }, + { ROOM_CENTER_X + 50.0f, ROOM_CENTER_Y + 400.0f, ROOM_CENTER_Z + 200.0f }, + { ROOM_CENTER_X + 50.0f, ROOM_CENTER_Y + 200.0f, ROOM_CENTER_Z + 200.0f }, + { ROOM_CENTER_X - 50.0f, ROOM_CENTER_Y + 0.0f, ROOM_CENTER_Z + 200.0f }, + { ROOM_CENTER_X - 150.0f, ROOM_CENTER_Y + 0.0f, ROOM_CENTER_Z + 100.0f }, + { ROOM_CENTER_X - 60.0f, ROOM_CENTER_Y + 180.0f, ROOM_CENTER_Z + 730.0f }, +}; + +static Vec3f sCameraEyePoints[] = { + { ROOM_CENTER_X + 250.0f, ROOM_CENTER_Y + 800.0f, ROOM_CENTER_Z + 800.0f }, + { ROOM_CENTER_X - 150.0f, ROOM_CENTER_Y + 700.0f, ROOM_CENTER_Z + 1400.0f }, + { ROOM_CENTER_X + 250.0f, ROOM_CENTER_Y + 100.0f, ROOM_CENTER_Z + 750.0f }, + { ROOM_CENTER_X + 50.0f, ROOM_CENTER_Y + 200.0f, ROOM_CENTER_Z + 900.0f }, + { ROOM_CENTER_X + 50.0f, ROOM_CENTER_Y + 200.0f, ROOM_CENTER_Z + 900.0f }, + { ROOM_CENTER_X + 350.0f, ROOM_CENTER_Y + 400.0f, ROOM_CENTER_Z + 1200.0f }, + { ROOM_CENTER_X - 50.0f, ROOM_CENTER_Y + 200.0f, ROOM_CENTER_Z + 800.0f }, + { ROOM_CENTER_X - 50.0f, ROOM_CENTER_Y + 200.0f, ROOM_CENTER_Z + 800.0f }, +}; + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; +static u32 sBodyStatic = false; + +// Unreferenced. Maybe two zero vectors? +static u32 sUnkValues[] = { 0, 0, 0, 0, 0, 0 }; + +static Color_RGBA8 sBodyColor = { 255, 255, 255, 255 }; +static Color_RGBA8 sStaticColor = { 0, 0, 0, 255 }; +static s32 sHandState[] = { HAND_WAIT, HAND_WAIT }; + +const ActorInit Boss_Sst_InitVars = { + ACTOR_BOSS_SST, + ACTORCAT_BOSS, + FLAGS, + OBJECT_SST, + sizeof(BossSst), + (ActorFunc)BossSst_Init, + (ActorFunc)BossSst_Destroy, + (ActorFunc)BossSst_UpdateHand, + (ActorFunc)BossSst_DrawHand, + NULL, +}; + +#include "z_boss_sst_colchk.c" + +static AnimationHeader* sHandIdleAnims[] = { &gBongoLeftHandIdleAnim, &gBongoRightHandIdleAnim }; +static AnimationHeader* sHandFlatPoses[] = { &gBongoLeftHandFlatPoseAnim, &gBongoRightHandFlatPoseAnim }; +static AnimationHeader* sHandOpenPoses[] = { &gBongoLeftHandOpenPoseAnim, &gBongoRightHandOpenPoseAnim }; +static AnimationHeader* sHandFistPoses[] = { &gBongoLeftHandFistPoseAnim, &gBongoRightHandFistPoseAnim }; +static AnimationHeader* sHandClenchAnims[] = { &gBongoLeftHandClenchAnim, &gBongoRightHandClenchAnim }; +static AnimationHeader* sHandDamagePoses[] = { &gBongoLeftHandDamagePoseAnim, &gBongoRightHandDamagePoseAnim }; +static AnimationHeader* sHandPushoffPoses[] = { &gBongoLeftHandPushoffPoseAnim, &gBongoRightHandPushoffPoseAnim }; +static AnimationHeader* sHandHangPoses[] = { &gBongoLeftHandHangPoseAnim, &gBongoRightHandHangPoseAnim }; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x29, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 20, ICHAIN_STOP), +}; + +void BossSst_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossSst* this = (BossSst*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitCylinder(globalCtx, &this->colliderCyl); + Collider_InitJntSph(globalCtx, &this->colliderJntSph); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + Flags_SetSwitch(globalCtx, 0x14); + if (this->actor.params == BONGO_HEAD) { + sFloor = (BgSstFloor*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_SST_FLOOR, sRoomCenter.x, + sRoomCenter.y, sRoomCenter.z, 0, 0, 0, BONGOFLOOR_REST); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBongoHeadSkel, &gBongoHeadEyeOpenIdleAnim, this->jointTable, + this->morphTable, 45); + ActorShape_Init(&this->actor.shape, 70000.0f, ActorShadow_DrawCircle, 95.0f); + Collider_SetJntSph(globalCtx, &this->colliderJntSph, &this->actor, &sJntSphInitHead, this->colliderItems); + Collider_SetCylinder(globalCtx, &this->colliderCyl, &this->actor, &sCylinderInitHead); + sHead = this; + this->actor.world.pos.x = ROOM_CENTER_X + 50.0f; + this->actor.world.pos.y = ROOM_CENTER_Y + 0.0f; + this->actor.world.pos.z = ROOM_CENTER_Z - 650.0f; + this->actor.home.pos = this->actor.world.pos; + this->actor.shape.rot.y = 0; + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, + ROOM_CENTER_Z + 400.0f, 0, 0, 0, WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, ROOM_CENTER_X, ROOM_CENTER_Y, + ROOM_CENTER_Z - 200.0f, 0, 0, 0, 0); + Actor_Kill(&this->actor); + } else { + sHands[LEFT] = + (BossSst*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BOSS_SST, this->actor.world.pos.x + 200.0f, + this->actor.world.pos.y, this->actor.world.pos.z + 400.0f, 0, + this->actor.shape.rot.y, 0, BONGO_LEFT_HAND); + sHands[RIGHT] = (BossSst*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BOSS_SST, + this->actor.world.pos.x + (-200.0f), this->actor.world.pos.y, + this->actor.world.pos.z + 400.0f, 0, this->actor.shape.rot.y, 0, + BONGO_RIGHT_HAND); + sHands[LEFT]->actor.child = &sHands[RIGHT]->actor; + sHands[RIGHT]->actor.child = &sHands[LEFT]->actor; + + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.update = BossSst_UpdateHead; + this->actor.draw = BossSst_DrawHead; + this->radius = -650.0f; + this->actor.targetArrowOffset = 4000.0f; + BossSst_HeadSetupLurk(this); + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_BOSS); + } + } else { + Collider_SetJntSph(globalCtx, &this->colliderJntSph, &this->actor, &sJntSphInitHand, this->colliderItems); + Collider_SetCylinder(globalCtx, &this->colliderCyl, &this->actor, &sCylinderInitHand); + if (this->actor.params == BONGO_LEFT_HAND) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBongoLeftHandSkel, &gBongoLeftHandIdleAnim, + this->jointTable, this->morphTable, 27); + this->vParity = -1; + this->colliderJntSph.elements[0].dim.modelSphere.center.z *= -1; + } else { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBongoRightHandSkel, &gBongoRightHandIdleAnim, + this->jointTable, this->morphTable, 27); + this->vParity = 1; + } + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 95.0f); + this->handZPosMod = -3500; + this->actor.targetArrowOffset = 5000.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + BossSst_HandSetupWait(this); + } +} + +void BossSst_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossSst* this = (BossSst*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->colliderJntSph); + Collider_DestroyCylinder(globalCtx, &this->colliderCyl); + Audio_StopSfxByPos(&this->center); +} + +void BossSst_HeadSetupLurk(BossSst* this) { + this->actor.draw = NULL; + sHands[LEFT]->actor.draw = NULL; + sHands[RIGHT]->actor.draw = NULL; + this->vVanish = false; + this->actionFunc = BossSst_HeadLurk; +} + +void BossSst_HeadLurk(BossSst* this, GlobalContext* globalCtx) { + if (this->actor.yDistToPlayer < 1000.0f) { + BossSst_HeadSetupIntro(this, globalCtx); + } +} + +void BossSst_HeadSetupIntro(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->timer = 611; + this->ready = false; + player->actor.world.pos.x = sRoomCenter.x; + player->actor.world.pos.y = ROOM_CENTER_Y + 1000.0f; + player->actor.world.pos.z = sRoomCenter.z; + player->linearVelocity = player->actor.velocity.y = 0.0f; + player->actor.shape.rot.y = -0x8000; + player->targetYaw = -0x8000; + player->currentYaw = -0x8000; + player->fallStartHeight = 0; + player->stateFlags1 |= 0x20; + + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + sCutsceneCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCutsceneCamera, CAM_STAT_ACTIVE); + Math_Vec3f_Copy(&sCameraAt, &player->actor.world.pos); + if (gSaveContext.eventChkInf[7] & 0x80) { + sCameraEye.z = ROOM_CENTER_Z - 100.0f; + } + + Gameplay_CameraSetAtEye(globalCtx, sCutsceneCamera, &sCameraAt, &sCameraEye); + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + this->actionFunc = BossSst_HeadIntro; +} + +void BossSst_HeadIntro(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 tempo; + s32 introStateTimer; + s32 revealStateTimer; + + if (this->timer != 0) { + this->timer--; + } + + if (SkelAnime_Update(&this->skelAnime)) { + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadEyeCloseIdleAnim, -3.0f); + } + + if (this->timer == 0) { + sHands[RIGHT]->actor.flags |= ACTOR_FLAG_0; + sHands[LEFT]->actor.flags |= ACTOR_FLAG_0; + player->stateFlags1 &= ~0x20; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + sCameraAt.y += 30.0f; + sCameraAt.z += 300.0f; + Gameplay_CameraSetAtEye(globalCtx, sCutsceneCamera, &sCameraAt, &sCameraEye); + Gameplay_CopyCamera(globalCtx, MAIN_CAM, sCutsceneCamera); + Gameplay_ChangeCameraStatus(globalCtx, sCutsceneCamera, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, sCutsceneCamera); + gSaveContext.eventChkInf[7] |= 0x80; + BossSst_HeadSetupNeutral(this); + this->colliderJntSph.base.ocFlags1 |= OC1_ON; + sHands[LEFT]->colliderJntSph.base.ocFlags1 |= OC1_ON; + sHands[RIGHT]->colliderJntSph.base.ocFlags1 |= OC1_ON; + this->timer = 112; + } else if (this->timer >= 546) { + if (player->actor.world.pos.y > 100.0f) { + player->actor.world.pos.x = sRoomCenter.x; + player->actor.world.pos.z = sRoomCenter.z; + player->linearVelocity = 0; + player->actor.shape.rot.y = -0x8000; + player->targetYaw = -0x8000; + player->currentYaw = -0x8000; + } + + Math_Vec3f_Copy(&sCameraAt, &player->actor.world.pos); + if (player->actor.bgCheckFlags & 2) { + if (!this->ready) { + sFloor->dyna.actor.params = BONGOFLOOR_HIT; + this->ready = true; + func_800AA000(this->actor.xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + Audio_PlayActorSound2(&sFloor->dyna.actor, NA_SE_EN_SHADEST_TAIKO_HIGH); + } else if (gSaveContext.eventChkInf[7] & 0x80) { + sHands[RIGHT]->actor.draw = BossSst_DrawHand; + sHands[LEFT]->actor.draw = BossSst_DrawHand; + this->actor.draw = BossSst_DrawHead; + this->timer = 178; + sCameraAt.x = ROOM_CENTER_X - 23.0f; + sCameraAt.y = ROOM_CENTER_Y + 0.0f; + sCameraAt.z = ROOM_CENTER_Z + 0.0f; + } else { + this->timer = 546; + } + } + } else if (this->timer >= 478) { + sCameraEye.x += 10.0f; + sCameraEye.y += 10.0f; + sCameraEye.z -= 10.0f; + } else if (this->timer >= 448) { + if (this->timer == 460) { + sHands[RIGHT]->actor.draw = BossSst_DrawHand; + sHands[LEFT]->actor.draw = BossSst_DrawHand; + this->actor.draw = BossSst_DrawHead; + player->actor.world.pos.x = sRoomCenter.x; + player->actor.world.pos.z = sRoomCenter.z; + BossSst_HandSetupDownbeat(sHands[RIGHT]); + } + if (this->timer > 460) { + sCameraEye.x -= 40.0f; + sCameraEye.y -= 40.0f; + sCameraEye.z += 20.0f; + } else if (this->timer == 460) { + sCameraAt.x = sHands[RIGHT]->actor.home.pos.x + 0.0f; + sCameraAt.y = sHands[RIGHT]->actor.home.pos.y - 20.0f; + sCameraAt.z = sHands[RIGHT]->actor.home.pos.z + 10.0f; + sCameraEye.x = sHands[RIGHT]->actor.home.pos.x + 150.0f; + sCameraEye.y = sHands[RIGHT]->actor.home.pos.y + 100.0f; + sCameraEye.z = sHands[RIGHT]->actor.home.pos.z + 80.0f; + } + } else { + if (this->timer >= 372) { + introStateTimer = this->timer - 372; + tempo = 6; + if (this->timer == 447) { + sCameraAt = player->actor.world.pos; + sCameraEye.x = ROOM_CENTER_X - 200.0f; + sCameraEye.y = ROOM_CENTER_Y + 160.0f; + sCameraEye.z = ROOM_CENTER_Z - 190.0f; + } else if (introStateTimer == 11) { + sCameraAt.x = sHands[RIGHT]->actor.home.pos.x + 30.0f; + sCameraAt.y = sHands[RIGHT]->actor.home.pos.y + 0.0f; + sCameraAt.z = sHands[RIGHT]->actor.home.pos.z + 20.0f; + sCameraEye.x = sHands[RIGHT]->actor.home.pos.x + 100.0f; + sCameraEye.y = sHands[RIGHT]->actor.home.pos.y + 10.0f; + sCameraEye.z = sHands[RIGHT]->actor.home.pos.z - 210.0f; + } else if (introStateTimer == 62) { + sCameraAt.x = sHands[LEFT]->actor.home.pos.x + 0.0f; + sCameraAt.y = sHands[LEFT]->actor.home.pos.y + 50.0f; + sCameraAt.z = sHands[LEFT]->actor.home.pos.z + 100.0f; + sCameraEye.x = sHands[LEFT]->actor.home.pos.x + 110.0f; + sCameraEye.y = sHands[LEFT]->actor.home.pos.y + 180.0f; + sCameraEye.z = sHands[LEFT]->actor.home.pos.z - 70.0f; + } + } else if (this->timer >= 304) { + introStateTimer = this->timer - 304; + tempo = 5; + if (introStateTimer == 11) { + sCameraAt.x = sHands[RIGHT]->actor.home.pos.x + 40.0f; + sCameraAt.y = sHands[RIGHT]->actor.home.pos.y - 90.0f; + sCameraAt.z = sHands[RIGHT]->actor.home.pos.z - 40.0f; + sCameraEye.x = sHands[RIGHT]->actor.home.pos.x - 20.0f; + sCameraEye.y = sHands[RIGHT]->actor.home.pos.y + 210.0f; + sCameraEye.z = sHands[RIGHT]->actor.home.pos.z + 170.0f; + } else if (this->timer == 368) { + sCameraAt.x = sHands[LEFT]->actor.home.pos.x - 20.0f; + sCameraAt.y = sHands[LEFT]->actor.home.pos.y + 0.0f; + sCameraAt.z = sHands[LEFT]->actor.home.pos.z + 0.0f; + sCameraEye.x = sHands[LEFT]->actor.home.pos.x - 70.0f; + sCameraEye.y = sHands[LEFT]->actor.home.pos.y + 170.0f; + sCameraEye.z = sHands[LEFT]->actor.home.pos.z + 150.0f; + } + } else if (this->timer >= 244) { + introStateTimer = this->timer - 244; + tempo = 4; + if (introStateTimer == 11) { + sCameraAt.x = sHands[RIGHT]->actor.home.pos.x + 30.0f; + sCameraAt.y = sHands[RIGHT]->actor.home.pos.y + 70.0f; + sCameraAt.z = sHands[RIGHT]->actor.home.pos.z + 40.0f; + sCameraEye.x = sHands[RIGHT]->actor.home.pos.x + 110.0f; + sCameraEye.y = sHands[RIGHT]->actor.home.pos.y - 140.0f; + sCameraEye.z = sHands[RIGHT]->actor.home.pos.z - 10.0f; + } else if (this->timer == 300) { + sCameraAt.x = sHands[LEFT]->actor.home.pos.x - 20.0f; + sCameraAt.y = sHands[LEFT]->actor.home.pos.y - 80.0f; + sCameraAt.z = sHands[LEFT]->actor.home.pos.z + 320.0f; + sCameraEye.x = sHands[LEFT]->actor.home.pos.x - 130.0f; + sCameraEye.y = sHands[LEFT]->actor.home.pos.y + 130.0f; + sCameraEye.z = sHands[LEFT]->actor.home.pos.z - 150.0f; + } + } else if (this->timer >= 192) { + introStateTimer = this->timer - 192; + tempo = 3; + if (this->timer == 240) { + sCameraAt.x = sHands[LEFT]->actor.home.pos.x - 190.0f; + sCameraAt.y = sHands[LEFT]->actor.home.pos.y - 110.0f; + sCameraAt.z = sHands[LEFT]->actor.home.pos.z + 40.0f; + sCameraEye.x = sHands[LEFT]->actor.home.pos.x + 120.0f; + sCameraEye.y = sHands[LEFT]->actor.home.pos.y + 130.0f; + sCameraEye.z = sHands[LEFT]->actor.home.pos.z + 50.0f; + } else if (introStateTimer == 12) { + sCameraAt.x = sRoomCenter.x + 50.0f; + sCameraAt.y = sRoomCenter.y - 90.0f; + sCameraAt.z = sRoomCenter.z - 200.0f; + sCameraEye.x = sRoomCenter.x + 50.0f; + sCameraEye.y = sRoomCenter.y + 350.0f; + sCameraEye.z = sRoomCenter.z + 150.0f; + } + } else if (this->timer >= 148) { + introStateTimer = this->timer - 148; + tempo = 2; + } else if (this->timer >= 112) { + introStateTimer = this->timer - 112; + tempo = 1; + } else { + introStateTimer = this->timer % 28; + tempo = 0; + } + if (this->timer <= 198) { + revealStateTimer = 198 - this->timer; + if ((gSaveContext.eventChkInf[7] & 0x80) && (revealStateTimer <= 44)) { + sCameraAt.x += 492.0f * 0.01f; + sCameraAt.y += 200.0f * 0.01f; + sCameraEye.x -= 80.0f * 0.01f; + sCameraEye.y -= 360.0f * 0.01f; + sCameraEye.z += 1000.0f * 0.01f; + } else if (this->timer <= 20) { + sCameraAt.y -= 700.0f * 0.01f; + sCameraAt.z += 900.0f * 0.01f; + sCameraEye.x += 650.0f * 0.01f; + sCameraEye.y += 400.0f * 0.01f; + sCameraEye.z += 1550.0f * 0.01f; + this->vVanish = true; + this->actor.flags |= ACTOR_FLAG_7; + } else if (revealStateTimer < 40) { + sCameraAt.x += 125.0f * 0.01f; + sCameraAt.y += 350.0f * 0.01f; + sCameraAt.z += 500.0f * 0.01f; + sCameraEye.x += 200.0f * 0.01f; + sCameraEye.y -= 850.0f * 0.01f; + } else if (revealStateTimer >= 45) { + if (revealStateTimer < 85) { + sCameraAt.x -= 250.0f * 0.01f; + sCameraAt.y += 425.0f * 0.01f; + sCameraAt.z -= 1200.0f * 0.01f; + sCameraEye.x -= 650.0f * 0.01f; + sCameraEye.y += 125.0f * 0.01f; + sCameraEye.z -= 350.0f * 0.01f; + } else if (revealStateTimer == 85) { + if (!(gSaveContext.eventChkInf[7] & 0x80)) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gBongoTitleCardTex), 160, 180, 128, 40); + } + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadEyeCloseAnim, -5.0f); + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_DISAPPEAR); + } + } + } + if (introStateTimer == 12) { + BossSst_HandSetupDownbeat(sHands[RIGHT]); + } + if ((introStateTimer != 5) && ((introStateTimer % ((tempo * 2) + 7)) == 5)) { + BossSst_HandSetupOffbeat(sHands[LEFT]); + } + } + + if (this->actionFunc != BossSst_HeadNeutral) { + Gameplay_CameraSetAtEye(globalCtx, sCutsceneCamera, &sCameraAt, &sCameraEye); + } +} + +void BossSst_HeadSetupWait(BossSst* this) { + if (this->skelAnime.animation != &gBongoHeadEyeCloseIdleAnim) { + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadEyeCloseIdleAnim, -5.0f); + } + this->actionFunc = BossSst_HeadWait; +} + +void BossSst_HeadWait(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((HAND_STATE(sHands[LEFT]) == HAND_WAIT) && (HAND_STATE(sHands[RIGHT]) == HAND_WAIT)) { + BossSst_HeadSetupNeutral(this); + } +} + +void BossSst_HeadSetupNeutral(BossSst* this) { + this->timer = 127; + this->ready = false; + this->actionFunc = BossSst_HeadNeutral; +} + +void BossSst_HeadNeutral(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (!this->ready && ((HAND_STATE(sHands[LEFT]) == HAND_BEAT) || (HAND_STATE(sHands[LEFT]) == HAND_WAIT)) && + ((HAND_STATE(sHands[RIGHT]) == HAND_BEAT) || (HAND_STATE(sHands[RIGHT]) == HAND_WAIT))) { + this->ready = true; + } + + if (this->ready) { + if (this->timer != 0) { + this->timer--; + } + } + + if (this->timer == 0) { + if ((GET_PLAYER(globalCtx)->actor.world.pos.y > -50.0f) && !(GET_PLAYER(globalCtx)->stateFlags1 & 0x6080)) { + sHands[Rand_ZeroOne() <= 0.5f]->ready = true; + BossSst_HeadSetupWait(this); + } else { + this->timer = 28; + } + } else { + Math_ApproachS(&this->actor.shape.rot.y, + Actor_WorldYawTowardPoint(&GET_PLAYER(globalCtx)->actor, &sRoomCenter) + 0x8000, 4, 0x400); + if ((this->timer == 28) || (this->timer == 84)) { + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_PRAY); + } + } +} + +void BossSst_HeadSetupDamagedHand(BossSst* this, s32 bothHands) { + if (bothHands) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadEyeOpenAnim, -5.0f); + } else { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadDamagedHandAnim, -5.0f); + } + this->actionFunc = BossSst_HeadDamagedHand; +} + +void BossSst_HeadDamagedHand(BossSst* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if ((HAND_STATE(sHands[LEFT]) == HAND_DAMAGED) && (HAND_STATE(sHands[RIGHT]) == HAND_DAMAGED)) { + BossSst_HeadSetupReadyCharge(this); + } else if ((HAND_STATE(sHands[LEFT]) == HAND_FROZEN) || (HAND_STATE(sHands[RIGHT]) == HAND_FROZEN)) { + BossSst_HeadSetupFrozenHand(this); + } else if (this->skelAnime.animation == &gBongoHeadEyeOpenAnim) { + BossSst_HeadSetupUnfreezeHand(this); + } else { + BossSst_HeadSetupWait(this); + } + } +} + +void BossSst_HeadSetupReadyCharge(BossSst* this) { + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadEyeOpenIdleAnim, -5.0f); + this->actor.speedXZ = 0.0f; + this->colliderCyl.base.acFlags |= AC_ON; + this->actionFunc = BossSst_HeadReadyCharge; +} + +void BossSst_HeadReadyCharge(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (sHands[LEFT]->ready && (sHands[LEFT]->actionFunc == BossSst_HandReadyCharge) && sHands[RIGHT]->ready && + (sHands[RIGHT]->actionFunc == BossSst_HandReadyCharge)) { + BossSst_HeadSetupCharge(this); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 4, 0x800, 0x400); + } +} + +void BossSst_HeadSetupCharge(BossSst* this) { + Animation_Change(&this->skelAnime, &gBongoHeadChargeAnim, 0.5f, 0.0f, Animation_GetLastFrame(&gBongoHeadChargeAnim), + ANIMMODE_ONCE_INTERP, -5.0f); + BossSst_HandSetDamage(sHands[LEFT], 0x20); + BossSst_HandSetDamage(sHands[RIGHT], 0x20); + this->colliderJntSph.base.atFlags |= AT_ON; + this->actor.speedXZ = 3.0f; + this->radius = -650.0f; + this->ready = false; + this->actionFunc = BossSst_HeadCharge; +} + +void BossSst_HeadCharge(BossSst* this, GlobalContext* globalCtx) { + f32 chargeDist; + s32 animFinish = SkelAnime_Update(&this->skelAnime); + + if (!this->ready && Animation_OnFrame(&this->skelAnime, 6.0f)) { + this->ready = true; + this->actor.speedXZ = 0.25f; + this->skelAnime.playSpeed = 0.2f; + } + + this->actor.speedXZ *= 1.25f; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 45.0f); + + if (this->ready) { + if (Math_SmoothStepToF(&this->radius, 650.0f, 0.4f, this->actor.speedXZ, 1.0f) < 10.0f) { + this->radius = 650.0f; + BossSst_HeadSetupEndCharge(this); + } else { + chargeDist = (650.0f - this->radius) * 3.0f; + if (chargeDist > 180.0f) { + chargeDist = 180.0f; + } + + this->actor.world.pos.y = this->actor.home.pos.y - chargeDist; + } + + if (!animFinish) { + sHandOffsets[LEFT].z += 5.0f; + sHandOffsets[RIGHT].z += 5.0f; + } + } else { + Math_ApproachF(&this->radius, -700.0f, 0.4f, this->actor.speedXZ); + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 180.0f, 20.0f); + sHandOffsets[LEFT].y += 5.0f; + sHandOffsets[RIGHT].y += 5.0f; + } + + if (this->colliderJntSph.base.atFlags & AT_HIT) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + sHands[LEFT]->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + sHands[RIGHT]->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + func_8002F71C(globalCtx, &this->actor, 10.0f, this->actor.shape.rot.y, 5.0f); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + } +} + +void BossSst_HeadSetupEndCharge(BossSst* this) { + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadEyeCloseIdleAnim, -20.0f); + this->targetYaw = Actor_WorldYawTowardPoint(&this->actor, &sRoomCenter); + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->colliderCyl.base.acFlags &= ~AC_ON; + this->radius *= -1.0f; + this->actionFunc = BossSst_HeadEndCharge; +} + +void BossSst_HeadEndCharge(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Math_SmoothStepToS(&this->actor.shape.rot.y, this->targetYaw, 4, 0x800, 0x100) == 0) { + BossSst_HandSetupRetreat(sHands[LEFT]); + BossSst_HandSetupRetreat(sHands[RIGHT]); + BossSst_HeadSetupNeutral(this); + } +} + +void BossSst_HeadSetupFrozenHand(BossSst* this) { + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadEyeOpenIdleAnim, -5.0f); + this->ready = false; + this->colliderCyl.base.acFlags |= AC_ON; + this->actionFunc = BossSst_HeadFrozenHand; +} + +void BossSst_HeadFrozenHand(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->ready) { + BossSst_HeadSetupUnfreezeHand(this); + } +} + +void BossSst_HeadSetupUnfreezeHand(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadEyeCloseAnim, -5.0f); + this->colliderCyl.base.acFlags &= ~AC_ON; + this->actionFunc = BossSst_HeadUnfreezeHand; +} + +void BossSst_HeadUnfreezeHand(BossSst* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + BossSst_HeadSetupWait(this); + } +} + +void BossSst_HeadSetupStunned(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadKnockoutAnim, -5.0f); + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, Animation_GetLastFrame(&gBongoHeadKnockoutAnim)); + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->colliderCyl.base.acFlags &= ~AC_ON; + this->vVanish = false; + this->actor.flags &= ~ACTOR_FLAG_7; + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_FREEZE); + this->actionFunc = BossSst_HeadStunned; +} + +void BossSst_HeadStunned(BossSst* this, GlobalContext* globalCtx) { + f32 bounce; + s32 animFinish; + f32 currentFrame; + + Math_StepToF(&sHandOffsets[LEFT].z, 600.0f, 20.0f); + Math_StepToF(&sHandOffsets[RIGHT].z, 600.0f, 20.0f); + Math_StepToF(&sHandOffsets[LEFT].x, 200.0f, 20.0f); + Math_StepToF(&sHandOffsets[RIGHT].x, -200.0f, 20.0f); + this->actor.velocity.y += this->actor.gravity; + animFinish = SkelAnime_Update(&this->skelAnime); + currentFrame = this->skelAnime.curFrame; + if (currentFrame <= 6.0f) { + bounce = (sinf((M_PI / 11) * currentFrame) * 100.0f) + (this->actor.home.pos.y - 180.0f); + if (this->actor.world.pos.y < bounce) { + this->actor.world.pos.y = bounce; + } + } else if (currentFrame <= 11.0f) { + this->actor.world.pos.y = (sinf((M_PI / 11) * currentFrame) * 170.0f) + (this->actor.home.pos.y - 250.0f); + } else { + this->actor.world.pos.y = + (sinf((currentFrame - 11.0f) * (M_PI / 5)) * 50.0f) + (this->actor.home.pos.y - 250.0f); + } + + if ((animFinish) || Animation_OnFrame(&this->skelAnime, 11.0f)) { + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_LAND); + } + + if (this->radius < -500.0f) { + Math_SmoothStepToF(&this->radius, -500.0f, 1.0f, 50.0f, 5.0f); + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.5f, 15.0f, 3.0f); + this->radius += this->actor.speedXZ; + } + + this->radius = CLAMP_MAX(this->radius, 400.0f); + + this->actor.world.pos.y += this->actor.velocity.y; + if (animFinish) { + BossSst_HeadSetupVulnerable(this); + } +} + +void BossSst_HeadSetupVulnerable(BossSst* this) { + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadStunnedAnim, -5.0f); + this->colliderCyl.base.acFlags |= AC_ON; + this->colliderCyl.info.bumper.dmgFlags = 0x0FC00702; // Sword-type damage + this->actor.speedXZ = 0.0f; + this->colliderJntSph.elements[10].info.bumperFlags |= (BUMP_ON | BUMP_HOOKABLE); + this->colliderJntSph.elements[0].info.bumperFlags &= ~BUMP_ON; + if (this->actionFunc != BossSst_HeadDamage) { + this->timer = 50; + } + + this->actionFunc = BossSst_HeadVulnerable; +} + +void BossSst_HeadVulnerable(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&sHandOffsets[LEFT].z, 600.0f, 20.0f); + Math_StepToF(&sHandOffsets[RIGHT].z, 600.0f, 20.0f); + Math_StepToF(&sHandOffsets[LEFT].x, 200.0f, 20.0f); + Math_StepToF(&sHandOffsets[RIGHT].x, -200.0f, 20.0f); + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { + this->timer += 2; + this->timer = CLAMP_MAX(this->timer, 50); + } else { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + BossSst_HandSetupRecover(sHands[LEFT]); + BossSst_HandSetupRecover(sHands[RIGHT]); + BossSst_HeadSetupRecover(this); + } + } +} + +void BossSst_HeadSetupDamage(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadDamageAnim, -3.0f); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, Animation_GetLastFrame(&gBongoHeadDamageAnim)); + Actor_SetColorFilter(&sHands[LEFT]->actor, 0x4000, 0xFF, 0, Animation_GetLastFrame(&gBongoHeadDamageAnim)); + Actor_SetColorFilter(&sHands[RIGHT]->actor, 0x4000, 0xFF, 0, Animation_GetLastFrame(&gBongoHeadDamageAnim)); + this->colliderCyl.base.acFlags &= ~AC_ON; + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_DAMAGE); + this->actionFunc = BossSst_HeadDamage; +} + +void BossSst_HeadDamage(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (SkelAnime_Update(&this->skelAnime)) { + BossSst_HeadSetupVulnerable(this); + } +} + +void BossSst_HeadSetupRecover(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadRecoverAnim, -5.0f); + this->colliderCyl.base.acFlags &= ~AC_ON; + this->colliderCyl.info.bumper.dmgFlags = 0xFFCFFFFF; + this->colliderJntSph.elements[10].info.bumperFlags &= ~(BUMP_ON | BUMP_HOOKABLE); + this->colliderJntSph.elements[0].info.bumperFlags |= BUMP_ON; + this->vVanish = true; + this->actor.speedXZ = 5.0f; + this->actionFunc = BossSst_HeadRecover; +} + +void BossSst_HeadRecover(BossSst* this, GlobalContext* globalCtx) { + s32 animFinish; + f32 currentFrame; + f32 diff; + + animFinish = SkelAnime_Update(&this->skelAnime); + currentFrame = this->skelAnime.curFrame; + if (currentFrame < 10.0f) { + this->actor.world.pos.y += 10.0f; + sHandOffsets[LEFT].y -= 10.0f; + sHandOffsets[RIGHT].y -= 10.0f; + Math_SmoothStepToF(&this->radius, -750.0f, 1.0f, this->actor.speedXZ, 2.0f); + } else { + this->actor.speedXZ *= 1.25f; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 50.0f); + diff = Math_SmoothStepToF(&this->radius, -650.0f, 1.0f, this->actor.speedXZ, 2.0f); + diff += Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f, 30.0f, 3.0f); + } + if (animFinish && (diff < 10.0f)) { + this->actor.world.pos.y = this->actor.home.pos.y; + this->radius = -650.0f; + BossSst_HandSetupRetreat(sHands[LEFT]); + BossSst_HandSetupRetreat(sHands[RIGHT]); + BossSst_HeadSetupNeutral(this); + } +} + +void BossSst_SetCameraTargets(f32 cameraSpeedMod, s32 targetIndex) { + Vec3f* nextAt = &sCameraAtPoints[targetIndex]; + Vec3f* nextEye = &sCameraEyePoints[targetIndex]; + + if (targetIndex != 0) { + Math_Vec3f_Copy(&sCameraAt, &sCameraAtPoints[targetIndex - 1]); + Math_Vec3f_Copy(&sCameraEye, &sCameraEyePoints[targetIndex - 1]); + } + + sCameraAtVel.x = (nextAt->x - sCameraAt.x) * cameraSpeedMod; + sCameraAtVel.y = (nextAt->y - sCameraAt.y) * cameraSpeedMod; + sCameraAtVel.z = (nextAt->z - sCameraAt.z) * cameraSpeedMod; + + sCameraEyeVel.x = (nextEye->x - sCameraEye.x) * cameraSpeedMod; + sCameraEyeVel.y = (nextEye->y - sCameraEye.y) * cameraSpeedMod; + sCameraEyeVel.z = (nextEye->z - sCameraEye.z) * cameraSpeedMod; +} + +void BossSst_UpdateDeathCamera(BossSst* this, GlobalContext* globalCtx) { + Vec3f cameraAt; + Vec3f cameraEye; + f32 sn; + f32 cs; + + sCameraAt.x += sCameraAtVel.x; + sCameraAt.y += sCameraAtVel.y; + sCameraAt.z += sCameraAtVel.z; + sCameraEye.x += sCameraEyeVel.x; + sCameraEye.y += sCameraEyeVel.y; + sCameraEye.z += sCameraEyeVel.z; + + sn = Math_SinS(this->actor.shape.rot.y); + cs = Math_CosS(this->actor.shape.rot.y); + cameraAt.x = this->actor.world.pos.x + (sCameraAt.z * sn) + (sCameraAt.x * cs); + cameraAt.y = this->actor.home.pos.y - 140.0f + sCameraAt.y; + cameraAt.z = this->actor.world.pos.z + (sCameraAt.z * cs) - (sCameraAt.x * sn); + cameraEye.x = this->actor.world.pos.x + (sCameraEye.z * sn) + (sCameraEye.x * cs); + cameraEye.y = this->actor.home.pos.y - 140.0f + sCameraEye.y; + cameraEye.z = this->actor.world.pos.z + (sCameraEye.z * cs) - (sCameraEye.x * sn); + Gameplay_CameraSetAtEye(globalCtx, sCutsceneCamera, &cameraAt, &cameraEye); +} + +void BossSst_HeadSetupDeath(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Animation_MorphToLoop(&this->skelAnime, &gBongoHeadEyeOpenIdleAnim, -5.0f); + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_DEAD); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 60); + Actor_SetColorFilter(&sHands[LEFT]->actor, 0x4000, 0xFF, 0, 60); + Actor_SetColorFilter(&sHands[RIGHT]->actor, 0x4000, 0xFF, 0, 60); + this->timer = 60; + this->colliderCyl.base.acFlags &= ~AC_ON; + this->colliderJntSph.base.ocFlags1 &= ~OC1_ON; + sHands[LEFT]->colliderJntSph.base.ocFlags1 &= ~OC1_ON; + sHands[RIGHT]->colliderJntSph.base.ocFlags1 &= ~OC1_ON; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + sCutsceneCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCutsceneCamera, CAM_STAT_ACTIVE); + Gameplay_CopyCamera(globalCtx, sCutsceneCamera, MAIN_CAM); + func_8002DF54(globalCtx, &player->actor, 8); + func_80064520(globalCtx, &globalCtx->csCtx); + Math_Vec3f_Copy(&sCameraEye, &GET_ACTIVE_CAM(globalCtx)->eye); + this->actionFunc = BossSst_HeadDeath; +} + +void BossSst_HeadDeath(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 140.0f, 20.0f); + if (this->timer == 0) { + BossSst_HandSetupThrash(sHands[LEFT]); + BossSst_HandSetupThrash(sHands[RIGHT]); + BossSst_HeadSetupThrash(this); + } else if (this->timer > 48) { + Gameplay_CameraSetAtEye(globalCtx, sCutsceneCamera, &this->actor.focus.pos, &sCameraEye); + Math_StepToF(&this->radius, -350.0f, 10.0f); + } else if (this->timer == 48) { + Player* player = GET_PLAYER(globalCtx); + + player->actor.world.pos.x = sRoomCenter.x + (400.0f * Math_SinS(this->actor.shape.rot.y)) + + (Math_CosS(this->actor.shape.rot.y) * -120.0f); + player->actor.world.pos.z = sRoomCenter.z + (400.0f * Math_CosS(this->actor.shape.rot.y)) - + (Math_SinS(this->actor.shape.rot.y) * -120.0f); + player->actor.shape.rot.y = Actor_WorldYawTowardPoint(&player->actor, &sRoomCenter); + func_8002DBD0(&this->actor, &sCameraEye, &GET_ACTIVE_CAM(globalCtx)->eye); + func_8002DBD0(&this->actor, &sCameraAt, &GET_ACTIVE_CAM(globalCtx)->at); + this->radius = -350.0f; + this->actor.world.pos.x = sRoomCenter.x - (Math_SinS(this->actor.shape.rot.y) * 350.0f); + this->actor.world.pos.z = sRoomCenter.z - (Math_CosS(this->actor.shape.rot.y) * 350.0f); + BossSst_SetCameraTargets(1.0 / 48, 0); + BossSst_UpdateDeathCamera(this, globalCtx); + } else { + BossSst_UpdateDeathCamera(this, globalCtx); + } +} + +void BossSst_HeadSetupThrash(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBongoHeadEyeOpenIdleAnim, -5.0f); + this->timer = 160; + this->targetYaw = this->actor.shape.rot.y; + BossSst_SetCameraTargets(1.0 / 80, 1); + this->actionFunc = BossSst_HeadThrash; +} + +void BossSst_HeadThrash(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + if ((this->timer == 0) && (this->actor.shape.rot.y == this->targetYaw)) { + BossSst_HeadSetupDarken(this); + } else if (this->timer >= 80) { + BossSst_UpdateDeathCamera(this, globalCtx); + } +} + +void BossSst_HeadSetupDarken(BossSst* this) { + this->timer = 160; + BossSst_SetCameraTargets(1.0 / 80, 2); + this->actionFunc = BossSst_HeadDarken; +} + +void BossSst_HeadDarken(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (1) {} + + if (this->timer >= 80) { + if (this->timer == 80) { + sBodyStatic = true; + } + BossSst_UpdateDeathCamera(this, globalCtx); + sBodyColor.r = sBodyColor.g = sBodyColor.b = (this->timer * 3) - 240; + if (this->timer == 80) { + BossSst_SetCameraTargets(1.0 / 80, 3); + } + } else { + sBodyColor.b = (80 - this->timer) / 1.0f; + sBodyColor.r = sBodyColor.g = sStaticColor.r = sStaticColor.g = sStaticColor.b = (80 - this->timer) / 8.0f; + BossSst_UpdateDeathCamera(this, globalCtx); + if (this->timer == 0) { + BossSst_HeadSetupFall(this); + } + } +} + +void BossSst_HeadSetupFall(BossSst* this) { + this->actor.speedXZ = 1.0f; + Math_Vec3f_Copy(&sCameraAt, &sCameraAtPoints[3]); + Math_Vec3f_Copy(&sCameraEye, &sCameraEyePoints[3]); + sCameraAtVel.x = 0.0f; + sCameraAtVel.z = 0.0f; + sCameraAtVel.y = -50.0f; + Math_Vec3f_Copy(&sCameraEyeVel, &sZeroVec); + this->actionFunc = BossSst_HeadFall; +} + +void BossSst_HeadFall(BossSst* this, GlobalContext* globalCtx) { + this->actor.speedXZ *= 1.5f; + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 230.0f, this->actor.speedXZ)) { + BossSst_HeadSetupMelt(this); + } + + if (sCameraAt.y > 200.0f) { + BossSst_UpdateDeathCamera(this, globalCtx); + } +} + +void BossSst_HeadSetupMelt(BossSst* this) { + BossSst_SpawnHeadShadow(this); + this->timer = 80; + BossSst_SetCameraTargets(1.0 / 60, 5); + this->actionFunc = BossSst_HeadMelt; +} + +void BossSst_HeadMelt(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + this->actor.scale.y -= 0.00025f; + this->actor.scale.x += 0.000075f; + this->actor.scale.z += 0.000075f; + this->actor.world.pos.y = this->actor.home.pos.y - 11500.0f * this->actor.scale.y; + if (this->timer == 0) { + BossSst_HeadSetupFinish(this); + } else if (this->timer >= 20.0f) { + BossSst_UpdateDeathCamera(this, globalCtx); + } +} + +void BossSst_HeadSetupFinish(BossSst* this) { + this->actor.draw = BossSst_DrawEffect; + this->timer = 40; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + BossSst_SetCameraTargets(1.0 / 40, 6); + this->actionFunc = BossSst_HeadFinish; +} + +void BossSst_HeadFinish(BossSst* this, GlobalContext* globalCtx) { + static Color_RGBA8 colorIndigo = { 80, 80, 150, 255 }; + static Color_RGBA8 colorDarkIndigo = { 40, 40, 80, 255 }; + static Color_RGBA8 colorUnused[2] = { + { 0, 0, 0, 255 }, + { 100, 100, 100, 0 }, + }; + Vec3f spawnPos; + s32 i; + + this->timer--; + if (this->effectMode == BONGO_NULL) { + if (this->timer < -170) { + BossSst_UpdateDeathCamera(this, globalCtx); + Gameplay_CopyCamera(globalCtx, MAIN_CAM, sCutsceneCamera); + Gameplay_ChangeCameraStatus(globalCtx, sCutsceneCamera, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, sCutsceneCamera); + func_8002DF54(globalCtx, &GET_PLAYER(globalCtx)->actor, 7); + func_80064534(globalCtx, &globalCtx->csCtx); + Actor_Kill(&this->actor); + Actor_Kill(&sHands[LEFT]->actor); + Actor_Kill(&sHands[RIGHT]->actor); + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + } else if (this->effects[0].alpha == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, ROOM_CENTER_X, ROOM_CENTER_Y, ROOM_CENTER_Z, 0, + 0, 0, WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, + (Math_SinS(this->actor.shape.rot.y) * 200.0f) + ROOM_CENTER_X, ROOM_CENTER_Y, + Math_CosS(this->actor.shape.rot.y) * 200.0f + ROOM_CENTER_Z, 0, 0, 0, 0); + BossSst_SetCameraTargets(1.0f, 7); + this->effectMode = BONGO_NULL; + } else if (this->timer == 0) { + this->effects[0].status = 0; + this->effects[1].status = -1; + this->effects[2].status = -1; + } else if (this->timer > 0) { + this->effects[0].status += 5; + BossSst_UpdateDeathCamera(this, globalCtx); + } + + colorIndigo.a = this->effects[0].alpha; + colorDarkIndigo.a = this->effects[0].alpha; + + for (i = 0; i < 5; i++) { + spawnPos.x = sRoomCenter.x + 0.0f + Rand_CenteredFloat(800.0f); + spawnPos.y = sRoomCenter.y + (-28.0f) + (Rand_ZeroOne() * 5.0f); + spawnPos.z = sRoomCenter.z + 0.0f + Rand_CenteredFloat(800.0f); + EffectSsGSplash_Spawn(globalCtx, &spawnPos, &colorIndigo, &colorDarkIndigo, 0, 0x3E8); + } +} + +void BossSst_HandSetupWait(BossSst* this) { + HAND_STATE(this) = HAND_WAIT; + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + Animation_MorphToLoop(&this->skelAnime, sHandIdleAnims[this->actor.params], 5.0f); + this->ready = false; + this->timer = 20; + this->actionFunc = BossSst_HandWait; +} + +void BossSst_HandWait(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, 20.0f); + Math_StepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 1.0f); + Math_StepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 1.0f); + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer != 0) { + this->timer--; + } + + if ((this->timer == 0) && (player->actor.world.pos.y > -50.0f) && !(player->stateFlags1 & 0x6080)) { + BossSst_HandSelectAttack(this); + } + } else if (sHead->actionFunc == BossSst_HeadNeutral) { + if ((this->actor.params == BONGO_RIGHT_HAND) && ((sHead->timer % 28) == 12)) { + BossSst_HandSetupDownbeat(this); + } else if ((this->actor.params == BONGO_LEFT_HAND) && ((sHead->timer % 7) == 5) && (sHead->timer < 112)) { + BossSst_HandSetupOffbeat(this); + } + } +} + +void BossSst_HandSetupDownbeat(BossSst* this) { + HAND_STATE(this) = HAND_BEAT; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 5.0f); + this->actor.shape.rot.x = 0; + this->timer = 12; + this->actionFunc = BossSst_HandDownbeat; +} + +void BossSst_HandDownbeat(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + BossSst_HandSetupWait(this); + } else { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer >= 3) { + this->actor.shape.rot.x -= 0x100; + Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 180.0f, 20.0f); + } else { + this->actor.shape.rot.x += 0x300; + Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 0.0f, 60.0f); + } + + if (this->timer == 0) { + sFloor->dyna.actor.params = BONGOFLOOR_HIT; + if (sHead->actionFunc == BossSst_HeadWait) { + if (this->ready) { + BossSst_HandSelectAttack(this); + } else { + BossSst_HandSetupWait(this); + } + } else { + BossSst_HandSetupDownbeatEnd(this); + } + func_800AA000(this->actor.xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_TAIKO_HIGH); + } + } +} + +void BossSst_HandSetupDownbeatEnd(BossSst* this) { + sFloor->dyna.actor.params = BONGOFLOOR_HIT; + Animation_PlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params]); + this->actionFunc = BossSst_HandDownbeatEnd; +} + +void BossSst_HandDownbeatEnd(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + BossSst_HandSetupWait(this); + } else { + Math_SmoothStepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 40.0f, 0.5f, 20.0f, 3.0f); + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x800, 0x100); + Math_StepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 1.0f); + Math_StepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 1.0f); + if ((sHead->actionFunc != BossSst_HeadIntro) && ((sHead->timer % 28) == 12)) { + BossSst_HandSetupDownbeat(this); + } + } +} + +void BossSst_HandSetupOffbeat(BossSst* this) { + HAND_STATE(this) = HAND_BEAT; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 5.0f); + this->actor.shape.rot.x = 0; + this->timer = 5; + this->actionFunc = BossSst_HandOffbeat; +} + +void BossSst_HandOffbeat(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + BossSst_HandSetupWait(this); + } else { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer != 0) { + this->actor.shape.rot.x -= 0x140; + Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 60.0f, 15.0f); + } else { + this->actor.shape.rot.x += 0x500; + Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 0.0f, 60.0f); + } + + if (this->timer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_TAIKO_LOW); + BossSst_HandSetupOffbeatEnd(this); + } + } +} + +void BossSst_HandSetupOffbeatEnd(BossSst* this) { + Animation_PlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params]); + this->actionFunc = BossSst_HandOffbeatEnd; +} + +void BossSst_HandOffbeatEnd(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + BossSst_HandSetupWait(this); + } else { + Math_SmoothStepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 40.0f, 0.5f, 20.0f, 3.0f); + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x400, 0xA0); + Math_StepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 1.0f); + Math_StepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 1.0f); + if (sHead->actionFunc == BossSst_HeadWait) { + if (this->ready) { + BossSst_HandSelectAttack(this); + } else { + BossSst_HandSetupWait(this); + } + } else if ((sHead->actionFunc != BossSst_HeadIntro) && ((sHead->timer % 7) == 5) && + ((sHead->timer % 28) != 5)) { + BossSst_HandSetupOffbeat(this); + } + } +} + +void BossSst_HandSetupEndSlam(BossSst* this) { + HAND_STATE(this) = HAND_RETREAT; + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + Animation_MorphToPlayOnce(&this->skelAnime, sHandPushoffPoses[this->actor.params], 6.0f); + this->actionFunc = BossSst_HandEndSlam; +} + +void BossSst_HandEndSlam(BossSst* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + BossSst_HandSetupRetreat(this); + } +} + +void BossSst_HandSetupRetreat(BossSst* this) { + HAND_STATE(this) = HAND_RETREAT; + Animation_MorphToPlayOnce(&this->skelAnime, sHandHangPoses[this->actor.params], 10.0f); + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->colliderJntSph.base.acFlags |= AC_ON; + this->actor.flags |= ACTOR_FLAG_0; + BossSst_HandSetInvulnerable(this, false); + this->timer = 0; + this->actionFunc = BossSst_HandRetreat; + this->actor.speedXZ = 3.0f; +} + +void BossSst_HandRetreat(BossSst* this, GlobalContext* globalCtx) { + f32 diff; + s32 inPosition; + + SkelAnime_Update(&this->skelAnime); + this->actor.speedXZ = this->actor.speedXZ * 1.2f; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 50.0f); + + diff = Math_SmoothStepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 0.3f, this->actor.speedXZ, 1.0f); + diff += Math_SmoothStepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 0.3f, this->actor.speedXZ, 1.0f); + if (this->timer != 0) { + if (this->timer != 0) { + this->timer--; + } + + this->actor.world.pos.y = (sinf((this->timer * M_PI) / 16.0f) * 250.0f) + this->actor.home.pos.y; + if (this->timer == 0) { + BossSst_HandSetupWait(this); + } else if (this->timer == 4) { + Animation_MorphToLoop(&this->skelAnime, sHandIdleAnims[this->actor.params], 4.0f); + } + } else { + inPosition = Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 0x200); + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.z, this->actor.home.rot.z, 0x200); + inPosition &= Math_ScaledStepToS(&this->handYRotMod, 0, 0x800); + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); + if ((Math_SmoothStepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 250.0f, 0.5f, 70.0f, 5.0f) < 1.0f) && + inPosition && (diff < 10.0f)) { + this->timer = 8; + } + } +} + +void BossSst_HandSetupReadySlam(BossSst* this) { + HAND_STATE(this) = HAND_SLAM; + this->timer = 0; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 10.0f); + this->actionFunc = BossSst_HandReadySlam; +} + +void BossSst_HandReadySlam(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + BossSst_HandSetupSlam(this); + } + } else { + Player* player = GET_PLAYER(globalCtx); + + if (Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 300.0f, 30.0f) && + (this->actor.xzDistToPlayer < 140.0f)) { + this->timer = 20; + } + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x1000, 0x100); + Math_ApproachF(&this->actor.world.pos.x, player->actor.world.pos.x, 0.5f, 40.0f); + Math_ApproachF(&this->actor.world.pos.z, player->actor.world.pos.z, 0.5f, 40.0f); + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); + } +} + +void BossSst_HandSetupSlam(BossSst* this) { + HAND_STATE(this) = HAND_SLAM; + this->actor.velocity.y = 1.0f; + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 10.0f); + BossSst_HandSetDamage(this, 0x20); + this->ready = false; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_FLY_ATTACK); + this->actionFunc = BossSst_HandSlam; +} + +void BossSst_HandSlam(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToS(&this->handZPosMod, -0xDAC, 0x1F4); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x1000); + Math_ScaledStepToS(&this->handYRotMod, 0, 0x1000); + if (this->timer != 0) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + if (this->colliderJntSph.base.acFlags & AC_ON) { + BossSst_HandSetupEndSlam(this); + } else { + this->colliderJntSph.base.acFlags |= AC_ON; + BossSst_HandSetupWait(this); + } + } + } else { + if (this->ready) { + this->timer = 30; + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + } else { + this->actor.velocity.y *= 1.5f; + if (Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, this->actor.velocity.y)) { + this->ready = true; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_TAIKO_LOW); + BossSst_SpawnShockwave(this); + this->colliderCyl.base.atFlags |= AT_ON; + Collider_UpdateCylinder(&this->actor, &this->colliderCyl); + this->colliderCyl.dim.radius = sCylinderInitHand.dim.radius; + } + } + + if (this->colliderJntSph.base.atFlags & AT_HIT) { + Player* player = GET_PLAYER(globalCtx); + + player->actor.world.pos.x = (Math_SinS(this->actor.yawTowardsPlayer) * 100.0f) + this->actor.world.pos.x; + player->actor.world.pos.z = (Math_CosS(this->actor.yawTowardsPlayer) * 100.0f) + this->actor.world.pos.z; + + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + func_8002F71C(globalCtx, &this->actor, 5.0f, this->actor.yawTowardsPlayer, 0.0f); + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x200); + } +} + +void BossSst_HandSetupReadySweep(BossSst* this) { + HAND_STATE(this) = HAND_SWEEP; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 10.0f); + this->radius = Actor_WorldDistXZToPoint(&this->actor, &sHead->actor.world.pos); + this->actor.world.rot.y = Actor_WorldYawTowardPoint(&sHead->actor, &this->actor.world.pos); + this->targetYaw = this->actor.home.rot.y + (this->vParity * 0x2000); + this->actionFunc = BossSst_HandReadySweep; +} + +void BossSst_HandReadySweep(BossSst* this, GlobalContext* globalCtx) { + s32 inPosition; + + SkelAnime_Update(&this->skelAnime); + inPosition = Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 50.0f, 4.0f); + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.y, this->targetYaw, 0x200); + inPosition &= Math_ScaledStepToS(&this->actor.world.rot.y, this->targetYaw, 0x400); + inPosition &= (Math_SmoothStepToF(&this->radius, sHead->actor.xzDistToPlayer, 0.5f, 60.0f, 1.0f) < 10.0f); + + this->actor.world.pos.x = (Math_SinS(this->actor.world.rot.y) * this->radius) + sHead->actor.world.pos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.world.rot.y) * this->radius) + sHead->actor.world.pos.z; + if (inPosition) { + BossSst_HandSetupSweep(this); + } else { + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); + } +} + +void BossSst_HandSetupSweep(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 5.0f); + BossSst_HandSetDamage(this, 0x10); + this->targetYaw = this->actor.home.rot.y - (this->vParity * 0x2000); + this->handMaxSpeed = 0x300; + this->handAngSpeed = 0; + this->ready = false; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_FLY_ATTACK); + this->actionFunc = BossSst_HandSweep; +} + +void BossSst_HandSweep(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 newTargetYaw; + + SkelAnime_Update(&this->skelAnime); + this->handAngSpeed += 0x60; + this->handAngSpeed = CLAMP_MAX(this->handAngSpeed, this->handMaxSpeed); + + if (!Math_SmoothStepToS(&this->actor.shape.rot.y, this->targetYaw, 4, this->handAngSpeed, 0x10)) { + this->colliderJntSph.base.ocFlags1 &= ~OC1_NO_PUSH; + BossSst_HandSetupRetreat(this); + } else if (this->colliderJntSph.base.atFlags & AT_HIT) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->ready = true; + func_8002F71C(globalCtx, &this->actor, 5.0f, this->actor.shape.rot.y - (this->vParity * 0x3800), 0.0f); + func_8002F7DC(&player->actor, NA_SE_PL_BODY_HIT); + newTargetYaw = this->actor.shape.rot.y - (this->vParity * 0x1400); + if (((s16)(newTargetYaw - this->targetYaw) * this->vParity) > 0) { + this->targetYaw = newTargetYaw; + } + } + + if (!this->ready && ((player->cylinder.dim.height > 40.0f) || (player->actor.world.pos.y > 1.0f))) { + this->colliderJntSph.base.atFlags |= AT_ON; + this->colliderJntSph.base.ocFlags1 &= ~OC1_NO_PUSH; + } else { + this->colliderJntSph.base.atFlags &= ~AT_ON; + this->colliderJntSph.base.ocFlags1 |= OC1_NO_PUSH; + } + + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y) * this->radius) + sHead->actor.world.pos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y) * this->radius) + sHead->actor.world.pos.z; +} + +void BossSst_HandSetupReadyPunch(BossSst* this) { + HAND_STATE(this) = HAND_PUNCH; + Animation_MorphToPlayOnce(&this->skelAnime, sHandPushoffPoses[this->actor.params], 10.0f); + this->actionFunc = BossSst_HandReadyPunch; +} + +void BossSst_HandReadyPunch(BossSst* this, GlobalContext* globalCtx) { + s32 inPosition = Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x400); + + if (SkelAnime_Update(&this->skelAnime) && inPosition) { + BossSst_HandSetupPunch(this); + } +} + +void BossSst_HandSetupPunch(BossSst* this) { + this->actor.speedXZ = 0.5f; + Animation_MorphToPlayOnce(&this->skelAnime, sHandFistPoses[this->actor.params], 5.0f); + BossSst_HandSetInvulnerable(this, true); + this->targetRoll = this->vParity * 0x3F00; + BossSst_HandSetDamage(this, 0x10); + this->actionFunc = BossSst_HandPunch; +} + +void BossSst_HandPunch(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 80.0f, 20.0f); + if (Math_ScaledStepToS(&this->actor.shape.rot.z, this->targetRoll, 0x400)) { + this->targetRoll *= -1; + } + + this->actor.speedXZ *= 1.25f; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 50.0f); + + this->actor.world.pos.x += this->actor.speedXZ * Math_SinS(this->actor.shape.rot.y); + this->actor.world.pos.z += this->actor.speedXZ * Math_CosS(this->actor.shape.rot.y); + if (this->actor.bgCheckFlags & 8) { + BossSst_HandSetupRetreat(this); + } else if (this->colliderJntSph.base.atFlags & AT_HIT) { + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + func_8002F71C(globalCtx, &this->actor, 10.0f, this->actor.shape.rot.y, 5.0f); + BossSst_HandSetupRetreat(this); + } + + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); +} + +void BossSst_HandSetupReadyClap(BossSst* this) { + HAND_STATE(this) = HAND_CLAP; + if (HAND_STATE(OTHER_HAND(this)) != HAND_CLAP) { + BossSst_HandSetupReadyClap(OTHER_HAND(this)); + } + + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 10.0f); + this->radius = Actor_WorldDistXZToPoint(&this->actor, &sHead->actor.world.pos); + this->actor.world.rot.y = Actor_WorldYawTowardPoint(&sHead->actor, &this->actor.world.pos); + this->targetYaw = this->actor.home.rot.y - (this->vParity * 0x1800); + this->targetRoll = this->vParity * 0x4000; + this->timer = 0; + this->ready = false; + OTHER_HAND(this)->ready = false; + this->actionFunc = BossSst_HandReadyClap; +} + +void BossSst_HandReadyClap(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + BossSst_HandSetupClap(this); + BossSst_HandSetupClap(OTHER_HAND(this)); + OTHER_HAND(this)->radius = this->radius; + } + } else if (!this->ready) { + this->ready = SkelAnime_Update(&this->skelAnime); + this->ready &= Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x600); + this->ready &= Math_ScaledStepToS(&this->actor.shape.rot.z, this->targetRoll, 0x600); + this->ready &= Math_ScaledStepToS(&this->actor.shape.rot.y, this->targetYaw, 0x200); + this->ready &= Math_ScaledStepToS(&this->actor.world.rot.y, this->targetYaw, 0x400); + this->ready &= Math_SmoothStepToF(&this->radius, sHead->actor.xzDistToPlayer, 0.5f, 50.0f, 1.0f) < 10.0f; + this->ready &= Math_SmoothStepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 95.0f, 0.5f, 30.0f, 1.0f) < 1.0f; + + this->actor.world.pos.x = Math_SinS(this->actor.world.rot.y) * this->radius + sHead->actor.world.pos.x; + this->actor.world.pos.z = Math_CosS(this->actor.world.rot.y) * this->radius + sHead->actor.world.pos.z; + } else if (OTHER_HAND(this)->ready) { + this->timer = 20; + } +} + +void BossSst_HandSetupClap(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 3.0f); + this->timer = 0; + this->handMaxSpeed = 0x240; + this->handAngSpeed = 0; + this->ready = false; + BossSst_HandSetDamage(this, 0x20); + this->actionFunc = BossSst_HandClap; +} + +void BossSst_HandClap(BossSst* this, GlobalContext* globalCtx) { + static s32 dropFlag = false; + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + if (dropFlag) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, + (Rand_ZeroOne() < 0.5f) ? ITEM00_ARROWS_SMALL : ITEM00_MAGIC_SMALL); + dropFlag = false; + } + + BossSst_HandReleasePlayer(this, globalCtx, true); + BossSst_HandSetupEndClap(this); + } + } else { + if (this->colliderJntSph.base.atFlags & AT_HIT) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + OTHER_HAND(this)->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + BossSst_HandGrabPlayer(this, globalCtx); + } + + if (this->ready) { + this->timer = 30; + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + if (!(player->stateFlags2 & 0x80)) { + dropFlag = true; + } + } else { + this->handAngSpeed += 0x40; + this->handAngSpeed = CLAMP_MAX(this->handAngSpeed, this->handMaxSpeed); + + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, this->handAngSpeed)) { + if (this->actor.params == BONGO_LEFT_HAND) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CLAP); + } + this->ready = true; + } else { + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); + } + + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y) * this->radius) + sHead->actor.world.pos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y) * this->radius) + sHead->actor.world.pos.z; + } + } + + if (player->actor.parent == &this->actor) { + player->unk_850 = 0; + player->actor.world.pos = this->actor.world.pos; + } +} + +void BossSst_HandSetupEndClap(BossSst* this) { + this->targetYaw = this->actor.home.rot.y - (this->vParity * 0x1000); + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 10.0f); + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->actionFunc = BossSst_HandEndClap; +} + +void BossSst_HandEndClap(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.z, 0, 0x200); + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->targetYaw, 0x100)) { + BossSst_HandSetupRetreat(this); + } + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y) * this->radius) + sHead->actor.world.pos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y) * this->radius) + sHead->actor.world.pos.z; +} + +void BossSst_HandSetupReadyGrab(BossSst* this) { + HAND_STATE(this) = HAND_GRAB; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 10.0f); + this->targetYaw = this->vParity * -0x5000; + this->targetRoll = this->vParity * 0x4000; + this->actionFunc = BossSst_HandReadyGrab; +} + +void BossSst_HandReadyGrab(BossSst* this, GlobalContext* globalCtx) { + s32 inPosition; + + SkelAnime_Update(&this->skelAnime); + inPosition = Math_SmoothStepToS(&this->actor.shape.rot.z, this->targetRoll, 4, 0x800, 0x100) == 0; + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + this->targetYaw, 0xA00); + Math_ApproachF(&this->actor.world.pos.y, ROOM_CENTER_Y + 95.0f, 0.5f, 20.0f); + if (inPosition) { + BossSst_HandSetupGrab(this); + } +} + +void BossSst_HandSetupGrab(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFistPoses[this->actor.params], 5.0f); + this->actor.world.rot.y = this->actor.shape.rot.y + (this->vParity * 0x4000); + this->targetYaw = this->actor.world.rot.y; + this->timer = 30; + this->actor.speedXZ = 0.5f; + BossSst_HandSetDamage(this, 0x20); + this->actionFunc = BossSst_HandGrab; +} + +void BossSst_HandGrab(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer != 0) { + this->timer--; + } + + this->actor.world.rot.y = + ((1.0f - sinf(this->timer * (M_PI / 60.0f))) * (this->vParity * 0x2000)) + this->targetYaw; + this->actor.shape.rot.y = this->actor.world.rot.y - (this->vParity * 0x4000); + if (this->timer < 5) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.5f, 25.0f, 5.0f); + if (SkelAnime_Update(&this->skelAnime)) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->actor.speedXZ = 0.0f; + if (player->stateFlags2 & 0x80) { + if (Rand_ZeroOne() < 0.5f) { + BossSst_HandSetupCrush(this); + } else { + BossSst_HandSetupSwing(this); + } + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, + (Rand_ZeroOne() < 0.5f) ? ITEM00_ARROWS_SMALL : ITEM00_MAGIC_SMALL); + BossSst_HandSetupRetreat(this); + } + } + } else { + this->actor.speedXZ *= 1.26f; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 70.0f); + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); + } + + if (this->colliderJntSph.base.atFlags & AT_HIT) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); + BossSst_HandGrabPlayer(this, globalCtx); + this->timer = CLAMP_MAX(this->timer, 5); + } + + this->actor.world.pos.x += this->actor.speedXZ * Math_SinS(this->actor.world.rot.y); + this->actor.world.pos.z += this->actor.speedXZ * Math_CosS(this->actor.world.rot.y); + if (player->stateFlags2 & 0x80) { + player->unk_850 = 0; + player->actor.world.pos = this->actor.world.pos; + player->actor.shape.rot.y = this->actor.shape.rot.y; + } +} + +void BossSst_HandSetupCrush(BossSst* this) { + Animation_MorphToLoop(&this->skelAnime, sHandClenchAnims[this->actor.params], -10.0f); + this->timer = 20; + this->actionFunc = BossSst_HandCrush; +} + +void BossSst_HandCrush(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + if (!(player->stateFlags2 & 0x80)) { + BossSst_HandReleasePlayer(this, globalCtx, true); + BossSst_HandSetupEndCrush(this); + } else { + player->actor.world.pos = this->actor.world.pos; + if (this->timer == 0) { + this->timer = 20; + if (!LINK_IS_ADULT) { + func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S_KID); + } else { + func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S); + } + + globalCtx->damagePlayer(globalCtx, -8); + } + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); + } + } +} + +void BossSst_HandSetupEndCrush(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 10.0f); + this->actionFunc = BossSst_HandEndCrush; +} + +void BossSst_HandEndCrush(BossSst* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + BossSst_HandSetupRetreat(this); + } +} + +void BossSst_HandSetupSwing(BossSst* this) { + this->amplitude = -0x4000; + this->timer = 1; + this->center.x = this->actor.world.pos.x - (Math_SinS(this->actor.shape.rot.y) * 200.0f); + this->center.y = this->actor.world.pos.y; + this->center.z = this->actor.world.pos.z - (Math_CosS(this->actor.shape.rot.y) * 200.0f); + this->actionFunc = BossSst_HandSwing; +} + +void BossSst_HandSwing(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 offXZ; + + if (Math_ScaledStepToS(&this->actor.shape.rot.x, this->amplitude, this->timer * 0xE4 + 0x1C8)) { + if (this->amplitude != 0) { + this->amplitude = 0; + if (this->timer == 4) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 4.0f); + } + } else { + if (this->timer == 4) { + player->actor.shape.rot.x = 0; + player->actor.shape.rot.z = 0; + BossSst_HandSetupRetreat(this); + return; + } + this->amplitude = (this->timer == 3) ? -0x6000 : -0x4000; + this->timer++; + } + } + + this->actor.world.pos.y = (Math_CosS(this->actor.shape.rot.x + 0x4000) * 200.0f) + this->center.y; + offXZ = Math_SinS(this->actor.shape.rot.x + 0x4000) * 200.0f; + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y) * offXZ) + this->center.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y) * offXZ) + this->center.z; + if (this->timer != 4) { + this->actor.shape.rot.z = (this->actor.shape.rot.x + 0x4000) * this->vParity; + } else { + Math_ScaledStepToS(&this->actor.shape.rot.z, 0, 0x800); + } + + if (player->stateFlags2 & 0x80) { + player->unk_850 = 0; + Math_Vec3f_Copy(&player->actor.world.pos, &this->actor.world.pos); + player->actor.shape.rot.x = this->actor.shape.rot.x; + player->actor.shape.rot.z = (this->vParity * -0x4000) + this->actor.shape.rot.z; + } else { + Math_ScaledStepToS(&player->actor.shape.rot.x, 0, 0x600); + Math_ScaledStepToS(&player->actor.shape.rot.z, 0, 0x600); + player->actor.world.pos.x += 20.0f * Math_SinS(this->actor.shape.rot.y); + player->actor.world.pos.z += 20.0f * Math_CosS(this->actor.shape.rot.y); + } + + if ((this->timer == 4) && (this->amplitude == 0) && SkelAnime_Update(&this->skelAnime) && + (player->stateFlags2 & 0x80)) { + BossSst_HandReleasePlayer(this, globalCtx, false); + player->actor.world.pos.x += 70.0f * Math_SinS(this->actor.shape.rot.y); + player->actor.world.pos.z += 70.0f * Math_CosS(this->actor.shape.rot.y); + func_8002F71C(globalCtx, &this->actor, 15.0f, this->actor.shape.rot.y, 2.0f); + func_8002F7DC(&player->actor, NA_SE_PL_BODY_HIT); + } + + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); +} + +void BossSst_HandSetupReel(BossSst* this) { + HAND_STATE(this) = HAND_DAMAGED; + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 4.0f); + this->timer = 36; + Math_Vec3f_Copy(&this->center, &this->actor.world.pos); + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 200); + this->actionFunc = BossSst_HandReel; +} + +void BossSst_HandReel(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + if (!(this->timer % 4)) { + if (this->timer % 8) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 4.0f); + } else { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFistPoses[this->actor.params], 6.0f); + } + } + + this->actor.colorFilterTimer = 200; + this->actor.world.pos.x += Rand_CenteredFloat(20.0f); + this->actor.world.pos.y += Rand_CenteredFloat(20.0f); + this->actor.world.pos.z += Rand_CenteredFloat(20.0f); + + if (this->actor.world.pos.y < (this->actor.floorHeight + 100.0f)) { + Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight + 100.0f, 20.0f); + } + + if (this->timer == 0) { + BossSst_HandSetupReadyShake(this); + } +} + +void BossSst_HandSetupReadyShake(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandDamagePoses[this->actor.params], 8.0f); + this->actionFunc = BossSst_HandReadyShake; +} + +void BossSst_HandReadyShake(BossSst* this, GlobalContext* globalCtx) { + f32 diff; + s32 inPosition; + + diff = Math_SmoothStepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 0.5f, 25.0f, 1.0f); + diff += Math_SmoothStepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 0.5f, 25.0f, 1.0f); + diff += Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 200.0f, 0.2f, 30.0f, 1.0f); + inPosition = Math_ScaledStepToS(&this->actor.shape.rot.x, 0x4000, 0x400); + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.z, 0, 0x1000); + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 0x800); + inPosition &= Math_StepToS(&this->handZPosMod, -0x5DC, 0x1F4); + inPosition &= Math_ScaledStepToS(&this->handYRotMod, this->vParity * -0x2000, 0x800); + this->actor.colorFilterTimer = 200; + if ((diff < 30.0f) && inPosition) { + BossSst_HandSetupShake(this); + } else { + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); + } +} + +void BossSst_HandSetupShake(BossSst* this) { + this->timer = 200; + this->actionFunc = BossSst_HandShake; +} + +void BossSst_HandShake(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + this->actor.shape.rot.x = 0x4000 + (sinf(this->timer * (M_PI / 5)) * 0x2000); + this->handYRotMod = (this->vParity * -0x2000) + (sinf(this->timer * (M_PI / 4)) * 0x2800); + + if (!(this->timer % 8)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_SHAKEHAND); + } + + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + if ((OTHER_HAND(this)->actionFunc == BossSst_HandShake) || + (OTHER_HAND(this)->actionFunc == BossSst_HandReadyCharge)) { + BossSst_HandSetupReadyCharge(this); + } else if (this->timer == 0) { + this->timer = 80; + } + } else if (this->timer == 0) { + this->actor.flags |= ACTOR_FLAG_0; + BossSst_HandSetupSlam(this); + } +} + +void BossSst_HandSetupReadyCharge(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFistPoses[this->actor.params], 10.0f); + this->ready = false; + this->actionFunc = BossSst_HandReadyCharge; +} + +void BossSst_HandReadyCharge(BossSst* this, GlobalContext* globalCtx) { + if (!this->ready) { + this->ready = SkelAnime_Update(&this->skelAnime); + this->ready &= Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x800); + this->ready &= + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y + (this->vParity * 0x1000), 0x800); + this->ready &= Math_ScaledStepToS(&this->handYRotMod, 0, 0x800); + this->ready &= Math_ScaledStepToS(&this->actor.shape.rot.z, this->vParity * 0x2800, 0x800); + this->ready &= Math_StepToS(&this->handZPosMod, -0xDAC, 0x1F4); + if (this->ready) { + this->actor.colorFilterTimer = 0; + } + } else if (this->colliderJntSph.base.atFlags & AT_HIT) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + OTHER_HAND(this)->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + sHead->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + func_8002F71C(globalCtx, &this->actor, 10.0f, this->actor.shape.rot.y, 5.0f); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + } +} + +void BossSst_HandSetupStunned(BossSst* hand) { + Animation_MorphToPlayOnce(&hand->skelAnime, sHandIdleAnims[hand->actor.params], 10.0f); + if (hand->actionFunc != BossSst_HandDamage) { + hand->ready = false; + } + + hand->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + hand->colliderJntSph.base.acFlags |= AC_ON; + BossSst_HandSetInvulnerable(hand, true); + Actor_SetColorFilter(&hand->actor, 0, 0xFF, 0, Animation_GetLastFrame(&gBongoHeadKnockoutAnim)); + hand->actionFunc = BossSst_HandStunned; +} + +void BossSst_HandStunned(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.world.pos.z, (Math_CosS(sHead->actor.shape.rot.y) * 200.0f) + this->actor.home.pos.z, + 0.5f, 25.0f); + Math_ApproachF(&this->actor.world.pos.x, (Math_SinS(sHead->actor.shape.rot.y) * 200.0f) + this->actor.home.pos.x, + 0.5f, 25.0f); + if (!this->ready) { + Math_ScaledStepToS(&this->handYRotMod, 0, 0x800); + Math_StepToS(&this->handZPosMod, -0xDAC, 0x1F4); + Math_ScaledStepToS(&this->actor.shape.rot.x, this->actor.home.rot.x, 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.z, this->actor.home.rot.z, 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 0x800); + if (sHead->actionFunc == BossSst_HeadVulnerable) { + this->ready = true; + Animation_MorphToPlayOnce(&this->skelAnime, sHandDamagePoses[this->actor.params], 10.0f); + } + } else { + Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, 30.0f); + } +} + +void BossSst_HandSetupDamage(BossSst* hand) { + hand->actor.shape.rot.x = 0; + Animation_MorphToPlayOnce(&hand->skelAnime, sHandOpenPoses[hand->actor.params], 3.0f); + hand->timer = 6; + hand->actionFunc = BossSst_HandDamage; +} + +void BossSst_HandDamage(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + SkelAnime_Update(&this->skelAnime); + if (this->timer >= 2) { + this->actor.shape.rot.x -= 0x200; + Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight + 200.0f, 50.0f); + } else { + this->actor.shape.rot.x += 0x400; + Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, 100.0f); + } + + if (this->timer == 0) { + if (this->actor.floorHeight >= 0.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_TAIKO_HIGH); + } + BossSst_HandSetupStunned(this); + } +} + +void BossSst_HandSetupThrash(BossSst* this) { + HAND_STATE(this) = HAND_DEATH; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 2.0f); + this->actor.shape.rot.x = 0; + this->timer = 160; + if (this->actor.params == BONGO_LEFT_HAND) { + this->amplitude = -0x800; + } else { + this->amplitude = 0; + this->actor.shape.rot.x = -0x800; + } + + this->handAngSpeed = 0x180; + this->actionFunc = BossSst_HandThrash; +} + +void BossSst_HandThrash(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.world.pos.z, (Math_CosS(sHead->actor.shape.rot.y) * 200.0f) + this->actor.home.pos.z, + 0.5f, 25.0f); + Math_ApproachF(&this->actor.world.pos.x, (Math_SinS(sHead->actor.shape.rot.y) * 200.0f) + this->actor.home.pos.x, + 0.5f, 25.0f); + if (Math_ScaledStepToS(&this->actor.shape.rot.x, this->amplitude, this->handAngSpeed)) { + if (this->amplitude != 0) { + this->amplitude = 0; + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 5.0f); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_TAIKO_HIGH); + this->amplitude = -0x800; + Animation_MorphToPlayOnce(&this->skelAnime, sHandOpenPoses[this->actor.params], 5.0f); + } + + if (this->timer < 80.0f) { + this->handAngSpeed -= 0x40; + this->handAngSpeed = CLAMP_MIN(this->handAngSpeed, 0x40); + } + } + + this->actor.world.pos.y = + (((this->handAngSpeed / 256.0f) + 0.5f) * 150.0f) * (-1.0f / 0x800) * this->actor.shape.rot.x; + if (this->timer == 0) { + BossSst_HandSetupDarken(this); + } +} + +void BossSst_HandSetupDarken(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 5.0f); + this->actionFunc = BossSst_HandDarken; +} + +void BossSst_HandDarken(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x800, this->handAngSpeed); + Math_StepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 90.0f, 5.0f); + if (sHead->actionFunc == BossSst_HeadFall) { + BossSst_HandSetupFall(this); + } +} + +void BossSst_HandSetupFall(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandFlatPoses[this->actor.params], 3.0f); + this->actionFunc = BossSst_HandFall; +} + +void BossSst_HandFall(BossSst* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x400); + this->actor.world.pos.y = sHead->actor.world.pos.y + 230.0f; + if (sHead->actionFunc == BossSst_HeadMelt) { + BossSst_HandSetupMelt(this); + } +} + +void BossSst_HandSetupMelt(BossSst* this) { + BossSst_SpawnHandShadow(this); + this->actor.shape.shadowDraw = NULL; + this->timer = 80; + this->actionFunc = BossSst_HandMelt; +} + +void BossSst_HandMelt(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + this->actor.scale.y -= 0.00025f; + this->actor.scale.x += 0.000025f; + this->actor.scale.z += 0.000025f; + this->actor.world.pos.y = ROOM_CENTER_Y + 0.0f; + if (this->timer == 0) { + BossSst_HandSetupFinish(this); + } +} + +void BossSst_HandSetupFinish(BossSst* this) { + this->actor.draw = BossSst_DrawEffect; + this->timer = 20; + this->effects[0].status = 0; + this->actionFunc = BossSst_HandFinish; +} + +void BossSst_HandFinish(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + this->effectMode = BONGO_NULL; + } +} + +void BossSst_HandSetupRecover(BossSst* this) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandPushoffPoses[this->actor.params], 10.0f); + this->ready = false; + this->actionFunc = BossSst_HandRecover; +} + +void BossSst_HandRecover(BossSst* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.world.pos.y, ROOM_CENTER_Y + 250.0f, 0.5f, 70.0f, 5.0f); + if (SkelAnime_Update(&this->skelAnime)) { + if (!this->ready) { + Animation_MorphToPlayOnce(&this->skelAnime, sHandHangPoses[this->actor.params], 10.0f); + this->ready = true; + } + } + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); +} + +void BossSst_HandSetupFrozen(BossSst* this) { + s32 i; + + HAND_STATE(this) = HAND_FROZEN; + Math_Vec3f_Copy(&this->center, &this->actor.world.pos); + BossSst_HandSetupReadyBreakIce(OTHER_HAND(this)); + this->ready = false; + this->effectMode = BONGO_ICE; + this->timer = 35; + for (i = 0; i < 18; i++) { + this->effects[i].move = false; + } + + BossSst_SpawnIceCrystal(this, 0); + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0xA); + this->handAngSpeed = 0; + this->actionFunc = BossSst_HandFrozen; +} + +void BossSst_HandFrozen(BossSst* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if ((this->timer % 2) != 0) { + BossSst_SpawnIceCrystal(this, (this->timer >> 1) + 1); + } + + if (this->ready) { + BossSst_IceShatter(this); + BossSst_HandSetupRetreat(this); + sHead->ready = true; + } else { + this->actor.colorFilterTimer = 10; + if (this->handAngSpeed != 0) { + f32 offY = Math_SinS(OTHER_HAND(this)->actor.shape.rot.x) * 5.0f; + f32 offXZ = Math_CosS(OTHER_HAND(this)->actor.shape.rot.x) * 5.0f; + + if ((this->handAngSpeed % 2) != 0) { + offY *= -1.0f; + offXZ *= -1.0f; + } + + this->actor.world.pos.x = this->center.x + (Math_CosS(OTHER_HAND(this)->actor.shape.rot.y) * offXZ); + this->actor.world.pos.y = this->center.y + offY; + this->actor.world.pos.z = this->center.z + (Math_SinS(OTHER_HAND(this)->actor.shape.rot.y) * offXZ); + this->handAngSpeed--; + } + } +} + +void BossSst_HandSetupReadyBreakIce(BossSst* this) { + HAND_STATE(this) = HAND_BREAK_ICE; + Animation_MorphToPlayOnce(&this->skelAnime, sHandFistPoses[this->actor.params], 5.0f); + this->ready = false; + this->actor.colorFilterTimer = 0; + if (this->effectMode == BONGO_ICE) { + this->effectMode = BONGO_NULL; + } + + this->radius = Actor_WorldDistXZToPoint(&this->actor, &OTHER_HAND(this)->center); + this->targetYaw = Actor_WorldYawTowardPoint(&this->actor, &OTHER_HAND(this)->center); + BossSst_HandSetInvulnerable(this, true); + this->actionFunc = BossSst_HandReadyBreakIce; +} + +void BossSst_HandReadyBreakIce(BossSst* this, GlobalContext* globalCtx) { + s32 inPosition; + + inPosition = Math_ScaledStepToS(&this->actor.shape.rot.y, this->targetYaw, 0x400); + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.x, 0x1000, 0x400); + inPosition &= Math_ScaledStepToS(&this->actor.shape.rot.z, 0, 0x800); + inPosition &= Math_ScaledStepToS(&this->handYRotMod, 0, 0x400); + inPosition &= Math_StepToF(&this->actor.world.pos.y, OTHER_HAND(this)->center.y + 200.0f, 50.0f); + inPosition &= Math_StepToF(&this->radius, 400.0f, 60.0f); + this->actor.world.pos.x = OTHER_HAND(this)->center.x - (Math_SinS(this->targetYaw) * this->radius); + this->actor.world.pos.z = OTHER_HAND(this)->center.z - (Math_CosS(this->targetYaw) * this->radius); + if (SkelAnime_Update(&this->skelAnime) && inPosition) { + BossSst_HandSetupBreakIce(this); + } +} + +void BossSst_HandSetupBreakIce(BossSst* this) { + this->timer = 9; + this->actionFunc = BossSst_HandBreakIce; + this->actor.speedXZ = 0.5f; +} + +void BossSst_HandBreakIce(BossSst* this, GlobalContext* globalCtx) { + if ((this->timer % 2) != 0) { + this->actor.speedXZ *= 1.5f; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 60.0f); + + if (Math_StepToF(&this->radius, 100.0f, this->actor.speedXZ)) { + BossSst_SpawnIceShard(this); + if (this->timer != 0) { + this->timer--; + } + + if (this->timer != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_ICE_BROKEN); + } + + OTHER_HAND(this)->handAngSpeed = 5; + } + } else { + this->actor.speedXZ *= 0.8f; + Math_StepToF(&this->radius, 500.0f, this->actor.speedXZ); + if (this->actor.speedXZ < 2.0f) { + if (this->timer != 0) { + this->timer--; + } + } + } + + this->actor.world.pos.x = OTHER_HAND(this)->center.x - (Math_SinS(this->targetYaw) * this->radius); + this->actor.world.pos.z = OTHER_HAND(this)->center.z - (Math_CosS(this->targetYaw) * this->radius); + this->actor.world.pos.y = OTHER_HAND(this)->center.y + (this->radius * 0.4f); + if (this->timer == 0) { + OTHER_HAND(this)->ready = true; + BossSst_HandSetupRetreat(this); + } + + func_8002F974(&this->actor, NA_SE_EN_SHADEST_HAND_FLY - SFX_FLAG); +} + +void BossSst_HandGrabPlayer(BossSst* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->grabPlayer(globalCtx, player)) { + player->actor.parent = &this->actor; + if (player->actor.colChkInfo.health > 0) { + this->colliderJntSph.base.ocFlags1 &= ~OC1_ON; + if (HAND_STATE(this) == HAND_CLAP) { + OTHER_HAND(this)->colliderJntSph.base.ocFlags1 &= ~OC1_ON; + } + } + } +} + +void BossSst_HandReleasePlayer(BossSst* this, GlobalContext* globalCtx, s32 dropPlayer) { + Player* player = GET_PLAYER(globalCtx); + + if (player->actor.parent == &this->actor) { + player->actor.parent = NULL; + player->unk_850 = 100; + this->colliderJntSph.base.ocFlags1 |= OC1_ON; + OTHER_HAND(this)->colliderJntSph.base.ocFlags1 |= OC1_ON; + if (dropPlayer) { + func_8002F71C(globalCtx, &this->actor, 0.0f, this->actor.shape.rot.y, 0.0f); + } + } +} + +void BossSst_MoveAround(BossSst* this) { + BossSst* hand; + Vec3f* vec; + f32 sn; + f32 cs; + s32 i; + + sn = Math_SinS(this->actor.shape.rot.y); + cs = Math_CosS(this->actor.shape.rot.y); + if (this->actionFunc != BossSst_HeadEndCharge) { + this->actor.world.pos.x = sRoomCenter.x + (this->radius * sn); + this->actor.world.pos.z = sRoomCenter.z + (this->radius * cs); + } + + for (i = 0; i < 2; i++) { + hand = sHands[i]; + vec = &sHandOffsets[i]; + + hand->actor.world.pos.x = this->actor.world.pos.x + (vec->z * sn) + (vec->x * cs); + hand->actor.world.pos.y = this->actor.world.pos.y + vec->y; + hand->actor.world.pos.z = this->actor.world.pos.z + (vec->z * cs) - (vec->x * sn); + + hand->actor.home.pos.x = this->actor.world.pos.x + (400.0f * sn) + (-200.0f * hand->vParity * cs); + hand->actor.home.pos.y = this->actor.world.pos.y; + hand->actor.home.pos.z = this->actor.world.pos.z + (400.0f * cs) - (-200.0f * hand->vParity * sn); + + hand->actor.home.rot.y = this->actor.shape.rot.y; + hand->actor.shape.rot.y = sHandYawOffsets[i] + this->actor.shape.rot.y; + + if (hand->actor.world.pos.y < hand->actor.floorHeight) { + hand->actor.world.pos.y = hand->actor.floorHeight; + } + } +} + +void BossSst_HandSelectAttack(BossSst* this) { + f32 rand = Rand_ZeroOne() * 6.0f; + s32 randInt; + + if (HAND_STATE(OTHER_HAND(this)) == HAND_DAMAGED) { + rand *= 5.0f / 6; + if (rand > 4.0f) { + rand = 4.0f; + } + } + + randInt = rand; + if (randInt == 0) { + BossSst_HandSetupReadySlam(this); + } else if (randInt == 1) { + BossSst_HandSetupReadySweep(this); + } else if (randInt == 2) { + BossSst_HandSetupReadyPunch(this); + } else if (randInt == 5) { + BossSst_HandSetupReadyClap(this); + } else { // randInt == 3 || randInt == 4 + BossSst_HandSetupReadyGrab(this); + } +} + +void BossSst_HandSetDamage(BossSst* this, s32 damage) { + s32 i; + + this->colliderJntSph.base.atFlags |= AT_ON; + for (i = 0; i < 11; i++) { + this->colliderJntSph.elements[i].info.toucher.damage = damage; + } +} + +void BossSst_HandSetInvulnerable(BossSst* this, s32 isInv) { + this->colliderJntSph.base.acFlags &= ~AC_HIT; + if (isInv) { + this->colliderJntSph.base.colType = COLTYPE_HARD; + this->colliderJntSph.base.acFlags |= AC_HARD; + } else { + this->colliderJntSph.base.colType = COLTYPE_HIT0; + this->colliderJntSph.base.acFlags &= ~AC_HARD; + } +} + +void BossSst_HeadSfx(BossSst* this, u16 sfxId) { + func_80078914(&this->center, sfxId); +} + +void BossSst_HandCollisionCheck(BossSst* this, GlobalContext* globalCtx) { + if ((this->colliderJntSph.base.acFlags & AC_HIT) && (this->colliderJntSph.base.colType != COLTYPE_HARD)) { + s32 bothHands = true; + + this->colliderJntSph.base.acFlags &= ~AC_HIT; + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + this->colliderJntSph.base.atFlags &= ~(AT_ON | AT_HIT); + this->colliderJntSph.base.acFlags &= ~AC_ON; + this->colliderJntSph.base.ocFlags1 &= ~OC1_NO_PUSH; + BossSst_HandReleasePlayer(this, globalCtx, true); + if (HAND_STATE(OTHER_HAND(this)) == HAND_CLAP) { + BossSst_HandReleasePlayer(OTHER_HAND(this), globalCtx, true); + BossSst_HandSetupRetreat(OTHER_HAND(this)); + } + + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->actor.colChkInfo.damageEffect == 3) { + BossSst_HandSetupFrozen(this); + } else { + BossSst_HandSetupReel(this); + if (HAND_STATE(OTHER_HAND(this)) != HAND_DAMAGED) { + bothHands = false; + } + } + + BossSst_HeadSetupDamagedHand(sHead, bothHands); + Item_DropCollectible(globalCtx, &this->actor.world.pos, + (Rand_ZeroOne() < 0.5f) ? ITEM00_ARROWS_SMALL : ITEM00_MAGIC_SMALL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_DAMAGE_HAND); + } + } +} + +void BossSst_HeadCollisionCheck(BossSst* this, GlobalContext* globalCtx) { + if (this->colliderCyl.base.acFlags & AC_HIT) { + this->colliderCyl.base.acFlags &= ~AC_HIT; + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (this->actionFunc == BossSst_HeadVulnerable) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + BossSst_HeadSetupDeath(this, globalCtx); + } else { + BossSst_HeadSetupDamage(this); + } + + BossSst_HandSetupDamage(sHands[LEFT]); + BossSst_HandSetupDamage(sHands[RIGHT]); + } else { + BossSst_HeadSetupStunned(this); + if (HAND_STATE(sHands[RIGHT]) == HAND_FROZEN) { + BossSst_IceShatter(sHands[RIGHT]); + } else if (HAND_STATE(sHands[LEFT]) == HAND_FROZEN) { + BossSst_IceShatter(sHands[LEFT]); + } + + BossSst_HandSetupStunned(sHands[RIGHT]); + BossSst_HandSetupStunned(sHands[LEFT]); + } + } + } +} + +void BossSst_UpdateHand(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossSst* this = (BossSst*)thisx; + BossSstHandTrail* trail; + + if (this->colliderCyl.base.atFlags & AT_ON) { + if ((this->effects[0].move < 5) || + (this->actor.xzDistToPlayer < ((this->effects[2].scale * 0.01f) * sCylinderInitHand.dim.radius)) || + (this->colliderCyl.base.atFlags & AT_HIT)) { + this->colliderCyl.base.atFlags &= ~(AT_ON | AT_HIT); + } else { + this->colliderCyl.dim.radius = (this->effects[0].scale * 0.01f) * sCylinderInitHand.dim.radius; + } + } + + BossSst_HandCollisionCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 130.0f, 0.0f, 5); + Actor_SetFocus(&this->actor, 0.0f); + if (this->colliderJntSph.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + if ((sHead->actionFunc != BossSst_HeadLurk) && (sHead->actionFunc != BossSst_HeadIntro) && + (this->colliderJntSph.base.acFlags & AC_ON)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + if (this->colliderJntSph.base.ocFlags1 & OC1_ON) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + if (this->colliderCyl.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderCyl.base); + } + + if ((HAND_STATE(this) != HAND_DEATH) && (HAND_STATE(this) != HAND_WAIT) && (HAND_STATE(this) != HAND_BEAT) && + (HAND_STATE(this) != HAND_FROZEN)) { + this->trailCount++; + this->trailCount = CLAMP_MAX(this->trailCount, 7); + } else { + this->trailCount--; + this->trailCount = CLAMP_MIN(this->trailCount, 0); + } + + trail = &this->handTrails[this->trailIndex]; + Math_Vec3f_Copy(&trail->world.pos, &this->actor.world.pos); + trail->world.rot = this->actor.shape.rot; + trail->zPosMod = this->handZPosMod; + trail->yRotMod = this->handYRotMod; + + this->trailIndex = (this->trailIndex + 1) % 7; + BossSst_UpdateEffect(&this->actor, globalCtx); +} + +void BossSst_UpdateHead(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossSst* this = (BossSst*)thisx; + + func_8002DBD0(&this->actor, &sHandOffsets[RIGHT], &sHands[RIGHT]->actor.world.pos); + func_8002DBD0(&this->actor, &sHandOffsets[LEFT], &sHands[LEFT]->actor.world.pos); + + sHandYawOffsets[LEFT] = sHands[LEFT]->actor.shape.rot.y - thisx->shape.rot.y; + sHandYawOffsets[RIGHT] = sHands[RIGHT]->actor.shape.rot.y - thisx->shape.rot.y; + + BossSst_HeadCollisionCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + if (this->vVanish) { + if ((globalCtx->actorCtx.unk_03 == 0) || (thisx->colorFilterTimer != 0)) { + this->actor.flags &= ~ACTOR_FLAG_7; + } else { + this->actor.flags |= ACTOR_FLAG_7; + } + } + + if (this->colliderJntSph.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + if ((this->actionFunc != BossSst_HeadLurk) && (this->actionFunc != BossSst_HeadIntro)) { + if (this->colliderCyl.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCyl.base); + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + if (this->colliderJntSph.base.ocFlags1 & OC1_ON) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); + } + + BossSst_MoveAround(this); + if ((!this->vVanish || CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) && + ((this->actionFunc == BossSst_HeadReadyCharge) || (this->actionFunc == BossSst_HeadCharge) || + (this->actionFunc == BossSst_HeadFrozenHand) || (this->actionFunc == BossSst_HeadStunned) || + (this->actionFunc == BossSst_HeadVulnerable) || (this->actionFunc == BossSst_HeadDamage))) { + this->actor.flags |= ACTOR_FLAG_0; + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + } + + if (this->actionFunc == BossSst_HeadCharge) { + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_MOVE - SFX_FLAG); + } + + BossSst_UpdateEffect(&this->actor, globalCtx); +} + +s32 BossSst_OverrideHandDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossSst* this = (BossSst*)thisx; + + if (limbIndex == 1) { + pos->z += this->handZPosMod; + rot->y += this->handYRotMod; + } + return false; +} + +void BossSst_PostHandDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + BossSst* this = (BossSst*)thisx; + + Collider_UpdateSpheres(limbIndex, &this->colliderJntSph); +} + +s32 BossSst_OverrideHandTrailDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* data, Gfx** gfx) { + BossSstHandTrail* trail = (BossSstHandTrail*)data; + + if (limbIndex == 1) { + pos->z += trail->zPosMod; + rot->y += trail->yRotMod; + } + return false; +} + +void BossSst_DrawHand(Actor* thisx, GlobalContext* globalCtx) { + BossSst* this = (BossSst*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_sst.c", 6563); + + func_80093D18(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x80, sBodyColor.r, sBodyColor.g, sBodyColor.b, 255); + + if (!sBodyStatic) { + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + } else { + gDPSetEnvColor(POLY_OPA_DISP++, sStaticColor.r, sStaticColor.g, sStaticColor.b, 0); + gSPSegment(POLY_OPA_DISP++, 0x08, sBodyStaticDList); + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + BossSst_OverrideHandDraw, BossSst_PostHandDraw, this); + if (this->trailCount >= 2) { + BossSstHandTrail* trail; + BossSstHandTrail* trail2; + s32 i; + s32 idx; + s32 end; + s32 pad; + + func_80093D84(globalCtx->state.gfxCtx); + + end = this->trailCount >> 1; + idx = (this->trailIndex + 4) % 7; + trail = &this->handTrails[idx]; + trail2 = &this->handTrails[(idx + 2) % 7]; + + for (i = 0; i < end; i++) { + if (Math3D_Vec3fDistSq(&trail2->world.pos, &trail->world.pos) > 900.0f) { + Matrix_SetTranslateRotateYXZ(trail->world.pos.x, trail->world.pos.y, trail->world.pos.z, + &trail->world.rot); + Matrix_Scale(0.02f, 0.02f, 0.02f, MTXMODE_APPLY); + + gSPSegment(POLY_XLU_DISP++, 0x08, sHandTrailDList); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x00, ((3 - i) * 10) + 20, 0, ((3 - i) * 20) + 50, + ((3 - i) * 30) + 70); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossSst_OverrideHandTrailDraw, NULL, + trail, POLY_XLU_DISP); + } + idx = (idx + 5) % 7; + trail2 = trail; + trail = &this->handTrails[idx]; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_sst.c", 6654); + + BossSst_DrawEffect(&this->actor, globalCtx); +} + +s32 BossSst_OverrideHeadDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + BossSst* this = (BossSst*)thisx; + s32 shakeAmp; + s32 pad; + s32 timer12; + f32 shakeMod; + + if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7) && this->vVanish) { + *dList = NULL; + } else if (this->actionFunc == BossSst_HeadThrash) { // Animation modifications for death cutscene + shakeAmp = (this->timer / 10) + 1; + if ((limbIndex == 3) || (limbIndex == 39) || (limbIndex == 42)) { + + shakeMod = sinf(this->timer * (M_PI / 5)); + rot->x += ((0x500 * Rand_ZeroOne() + 0xA00) / 0x10) * shakeAmp * shakeMod; + + shakeMod = sinf((this->timer % 5) * (M_PI / 5)); + rot->z -= ((0x800 * Rand_ZeroOne() + 0x1000) / 0x10) * shakeAmp * shakeMod + 0x1000; + + if (limbIndex == 3) { + + shakeMod = sinf(this->timer * (M_PI / 5)); + rot->y += ((0x500 * Rand_ZeroOne() + 0xA00) / 0x10) * shakeAmp * shakeMod; + } + } else if ((limbIndex == 5) || (limbIndex == 6)) { + + shakeMod = sinf((this->timer % 5) * (M_PI / 5)); + rot->z -= ((0x280 * Rand_ZeroOne() + 0x500) / 0x10) * shakeAmp * shakeMod + 0x500; + + if (limbIndex == 5) { + + shakeMod = sinf(this->timer * (M_PI / 5)); + rot->x += ((0x500 * Rand_ZeroOne() + 0xA00) / 0x10) * shakeAmp * shakeMod; + + shakeMod = sinf(this->timer * (M_PI / 5)); + rot->y += ((0x500 * Rand_ZeroOne() + 0xA00) / 0x10) * shakeAmp * shakeMod; + } + } else if (limbIndex == 2) { + shakeMod = sinf(this->timer * (M_PI / 5)); + rot->x += ((0x200 * Rand_ZeroOne() + 0x400) / 0x10) * shakeAmp * shakeMod; + + shakeMod = sinf(this->timer * (M_PI / 5)); + rot->y += ((0x200 * Rand_ZeroOne() + 0x400) / 0x10) * shakeAmp * shakeMod; + + shakeMod = sinf((this->timer % 5) * (M_PI / 5)); + rot->z -= ((0x100 * Rand_ZeroOne() + 0x200) / 0x10) * shakeAmp * shakeMod + 0x200; + } + } else if (this->actionFunc == BossSst_HeadDeath) { + if (this->timer > 48) { + timer12 = this->timer - 36; + } else { + pad = ((this->timer > 6) ? 6 : this->timer); + timer12 = pad * 2; + } + + if ((limbIndex == 3) || (limbIndex == 39) || (limbIndex == 42)) { + rot->z -= 0x2000 * sinf(timer12 * (M_PI / 24)); + } else if ((limbIndex == 5) || (limbIndex == 6)) { + rot->z -= 0xA00 * sinf(timer12 * (M_PI / 24)); + } else if (limbIndex == 2) { + rot->z -= 0x400 * sinf(timer12 * (M_PI / 24)); + } + } else if ((this->actionFunc == BossSst_HeadDarken) || (this->actionFunc == BossSst_HeadFall) || + (this->actionFunc == BossSst_HeadMelt)) { + if ((limbIndex == 3) || (limbIndex == 39) || (limbIndex == 42)) { + rot->z -= 0x1000; + } else if ((limbIndex == 5) || (limbIndex == 6)) { + rot->z -= 0x500; + } else if (limbIndex == 2) { + rot->z -= 0x200; + } + } + return false; +} + +void BossSst_PostHeadDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + static Vec3f headVec = { 1000.0f, 0.0f, 0.0f }; + BossSst* this = (BossSst*)thisx; + Vec3f headPos; + + if (limbIndex == 8) { + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); + Matrix_MultVec3f(&headVec, &headPos); + this->colliderCyl.dim.pos.x = headPos.x; + this->colliderCyl.dim.pos.y = headPos.y; + this->colliderCyl.dim.pos.z = headPos.z; + } + + Collider_UpdateSpheres(limbIndex, &this->colliderJntSph); +} + +void BossSst_DrawHead(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossSst* this = (BossSst*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_sst.c", 6810); + + if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x80, sBodyColor.r, sBodyColor.g, sBodyColor.b, 255); + if (!sBodyStatic) { + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + } else { + gDPSetEnvColor(POLY_OPA_DISP++, sStaticColor.r, sStaticColor.g, sStaticColor.b, 0); + gSPSegment(POLY_OPA_DISP++, 0x08, sBodyStaticDList); + } + } else { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, 255); + gSPSegment(POLY_XLU_DISP++, 0x08, &D_80116280[2]); + } + + if (this->actionFunc == BossSst_HeadThrash) { + f32 randPitch = Rand_ZeroOne() * (2 * M_PI); + f32 randYaw = Rand_ZeroOne() * (2 * M_PI); + + Matrix_RotateY(randYaw, MTXMODE_APPLY); + Matrix_RotateX(randPitch, MTXMODE_APPLY); + Matrix_Scale((this->timer * 0.000375f) + 1.0f, 1.0f - (this->timer * 0.00075f), + (this->timer * 0.000375f) + 1.0f, MTXMODE_APPLY); + Matrix_RotateX(-randPitch, MTXMODE_APPLY); + Matrix_RotateY(-randYaw, MTXMODE_APPLY); + } + + if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossSst_OverrideHeadDraw, BossSst_PostHeadDraw, + this, POLY_OPA_DISP); + } else { + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossSst_OverrideHeadDraw, BossSst_PostHeadDraw, + this, POLY_XLU_DISP); + } + + if ((this->actionFunc == BossSst_HeadIntro) && (113 >= this->timer) && (this->timer > 20)) { + s32 yOffset; + Vec3f vanishMaskPos; + Vec3f vanishMaskOffset; + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x00, 0, 0, 18, 255); + + yOffset = 113 * 8 - this->timer * 8; + vanishMaskPos.x = ROOM_CENTER_X + 85.0f; + vanishMaskPos.y = ROOM_CENTER_Y - 250.0f + yOffset; + vanishMaskPos.z = ROOM_CENTER_Z + 190.0f; + if (vanishMaskPos.y > 450.0f) { + vanishMaskPos.y = 450.0f; + } + + Matrix_MultVec3fExt(&vanishMaskPos, &vanishMaskOffset, &globalCtx->billboardMtxF); + Matrix_Translate(this->actor.world.pos.x + vanishMaskOffset.x, this->actor.world.pos.y + vanishMaskOffset.y, + this->actor.world.pos.z + vanishMaskOffset.z, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_sst.c", 6934), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sIntroVanishDList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_sst.c", 6941); + + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &this->actor.focus.pos, &this->center); + BossSst_DrawEffect(&this->actor, globalCtx); +} + +void BossSst_SpawnHeadShadow(BossSst* this) { + static Vec3f shadowOffset[] = { + { 0.0f, 0.0f, 340.0f }, + { -160.0f, 0.0f, 250.0f }, + { 160.0f, 0.0f, 250.0f }, + }; + s32 pad; + s32 i; + f32 sn; + f32 cs; + + this->effectMode = BONGO_SHADOW; + sn = Math_SinS(this->actor.shape.rot.y); + cs = Math_CosS(this->actor.shape.rot.y); + + for (i = 0; i < 3; i++) { + BossSstEffect* shadow = &this->effects[i]; + Vec3f* offset = &shadowOffset[i]; + + shadow->pos.x = this->actor.world.pos.x + (sn * offset->z) + (cs * offset->x); + shadow->pos.y = 0.0f; + shadow->pos.z = this->actor.world.pos.z + (cs * offset->z) - (sn * offset->x); + + shadow->scale = 1450; + shadow->alpha = 254; + shadow->status = 65; + } + + this->effects[3].status = -1; +} + +void BossSst_SpawnHandShadow(BossSst* this) { + this->effectMode = BONGO_SHADOW; + this->effects[0].pos.x = this->actor.world.pos.x + (Math_CosS(this->actor.shape.rot.y) * 30.0f * this->vParity); + this->effects[0].pos.z = this->actor.world.pos.z - (Math_SinS(this->actor.shape.rot.y) * 30.0f * this->vParity); + this->effects[0].pos.y = this->actor.world.pos.y; + this->effects[0].scale = 2300; + this->effects[0].alpha = 254; + this->effects[0].status = 5; + this->effects[1].status = -1; +} + +void BossSst_SpawnShockwave(BossSst* this) { + s32 i; + s32 scale = 120; + s32 alpha = 250; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_HAND_WAVE); + this->effectMode = BONGO_SHOCKWAVE; + + for (i = 0; i < 3; i++) { + BossSstEffect* shockwave = &this->effects[i]; + + Math_Vec3f_Copy(&shockwave->pos, &this->actor.world.pos); + shockwave->move = (i + 9) * 2; + shockwave->scale = scale; + shockwave->alpha = alpha / shockwave->move; + scale -= 50; + alpha -= 25; + } +} + +void BossSst_SpawnIceCrystal(BossSst* this, s32 index) { + BossSstEffect* ice = &this->effects[index]; + Sphere16* sphere; + + if (index < 11) { + sphere = &this->colliderJntSph.elements[index].dim.worldSphere; + + ice->pos.x = sphere->center.x; + ice->pos.y = sphere->center.y; + ice->pos.z = sphere->center.z; + if (index == 0) { + ice->pos.x -= 25.0f; + ice->pos.y -= 25.0f; + ice->pos.z -= 25.0f; + } + } else { + sphere = &this->colliderJntSph.elements[0].dim.worldSphere; + + ice->pos.x = ((((index - 11) & 1) ? 1 : -1) * 25.0f) + sphere->center.x; + ice->pos.y = ((((index - 11) & 2) ? 1 : -1) * 25.0f) + sphere->center.y; + ice->pos.z = ((((index - 11) & 4) ? 1 : -1) * 25.0f) + sphere->center.z; + } + + ice->pos.x -= this->actor.world.pos.x; + ice->pos.y -= this->actor.world.pos.y; + ice->pos.z -= this->actor.world.pos.z; + + ice->status = 0; + + ice->rot.x = Rand_ZeroOne() * 0x10000; + ice->rot.y = Rand_ZeroOne() * 0x10000; + ice->rot.z = Rand_ZeroOne() * 0x10000; + + ice->alpha = 120; + ice->move = true; + + ice->vel.x = (Rand_ZeroOne() * 0.06f + 0.12f) * ice->pos.x; + ice->vel.y = (Rand_ZeroOne() * 15.0f + 5.0f); + ice->vel.z = (Rand_ZeroOne() * 0.06f + 0.12f) * ice->pos.z; + ice->scale = 4000; + + if ((index % 2) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_FREEZE_S); + } +} + +void BossSst_SpawnIceShard(BossSst* this) { + s32 i; + Vec3f spawnPos; + f32 offXZ; + + this->effectMode = BONGO_ICE; + offXZ = Math_CosS(this->actor.shape.rot.x) * 50.0f; + spawnPos.x = Math_CosS(this->actor.shape.rot.y) * offXZ + this->actor.world.pos.x; + spawnPos.y = Math_SinS(this->actor.shape.rot.x) * 50.0f + this->actor.world.pos.y - 10.0f; + spawnPos.z = Math_SinS(this->actor.shape.rot.y) * offXZ + this->actor.world.pos.z; + + for (i = 0; i < 18; i++) { + BossSstEffect* ice = &this->effects[i]; + + Math_Vec3f_Copy(&ice->pos, &spawnPos); + ice->status = 1; + ice->rot.x = Rand_ZeroOne() * 0x10000; + ice->rot.y = Rand_ZeroOne() * 0x10000; + ice->rot.z = Rand_ZeroOne() * 0x10000; + + ice->alpha = 120; + ice->move = true; + + ice->vel.x = Rand_CenteredFloat(20.0f); + ice->vel.y = Rand_ZeroOne() * 10.0f + 3.0f; + ice->vel.z = Rand_CenteredFloat(20.0f); + + ice->scale = Rand_ZeroOne() * 200.0f + 400.0f; + } +} + +void BossSst_IceShatter(BossSst* this) { + s32 i; + + this->effects[0].status = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_PL_ICE_BROKEN); + + for (i = 0; i < 18; i++) { + BossSstEffect* ice = &this->effects[i]; + + if (ice->move) { + ice->pos.x += this->actor.world.pos.x; + ice->pos.y += this->actor.world.pos.y; + ice->pos.z += this->actor.world.pos.z; + } + } +} + +void BossSst_UpdateEffect(Actor* thisx, GlobalContext* globalCtx) { + BossSst* this = (BossSst*)thisx; + BossSstEffect* effect; + s32 i; + + if (this->effectMode != BONGO_NULL) { + if (this->effectMode == BONGO_ICE) { + if (this->effects[0].status) { + for (i = 0; i < 18; i++) { + effect = &this->effects[i]; + + if (effect->move) { + effect->pos.x += effect->vel.x; + effect->pos.y += effect->vel.y; + effect->pos.z += effect->vel.z; + effect->alpha -= 3; + effect->vel.y -= 1.0f; + effect->rot.x += 0xD00; + effect->rot.y += 0x1100; + effect->rot.z += 0x1500; + } + } + } + if (this->effects[0].alpha == 0) { + this->effectMode = BONGO_NULL; + } + } else if (this->effectMode == BONGO_SHOCKWAVE) { + for (i = 0; i < 3; i++) { + BossSstEffect* effect2 = &this->effects[i]; + s32 scale = effect2->move * 2; + + effect2->scale += CLAMP_MAX(scale, 20) + i; + if (effect2->move != 0) { + effect2->move--; + } + } + + if (this->effects[0].move == 0) { + this->effectMode = BONGO_NULL; + } + } else if (this->effectMode == BONGO_SHADOW) { + effect = &this->effects[0]; + + if (this->actor.params == BONGO_HEAD) { + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &this->actor.focus.pos, &this->center); + BossSst_HeadSfx(this, NA_SE_EN_SHADEST_LAST - SFX_FLAG); + } + while (effect->status != -1) { + if (effect->status == 0) { + effect->alpha -= 2; + } else { + effect->scale += effect->status; + } + + effect->scale = CLAMP_MAX(effect->scale, 10000); + effect++; + } + } + } +} + +void BossSst_DrawEffect(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + BossSst* this = (BossSst*)thisx; + s32 i; + BossSstEffect* effect; + + if (this->effectMode != BONGO_NULL) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_sst.c", 7302); + + func_80093D84(globalCtx->state.gfxCtx); + if (this->effectMode == BONGO_ICE) { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, globalCtx->gameplayFrames % 256, 0x20, 0x10, 1, + 0, (globalCtx->gameplayFrames * 2) % 256, 0x40, 0x20)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 50, 100, this->effects[0].alpha); + gSPDisplayList(POLY_XLU_DISP++, gBongoIceCrystalDL); + + for (i = 0; i < 18; i++) { + effect = &this->effects[i]; + if (effect->move) { + func_8003435C(&effect->pos, globalCtx); + if (this->effects[0].status != 0) { + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + } else { + Matrix_Translate(effect->pos.x + this->actor.world.pos.x, + effect->pos.y + this->actor.world.pos.y, + effect->pos.z + this->actor.world.pos.z, MTXMODE_NEW); + } + + Matrix_RotateZYX(effect->rot.x, effect->rot.y, effect->rot.z, MTXMODE_APPLY); + Matrix_Scale(effect->scale * 0.001f, effect->scale * 0.001f, effect->scale * 0.001f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_sst.c", 7350), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBongoIceShardDL); + } + } + } else if (this->effectMode == BONGO_SHOCKWAVE) { + f32 scaleY = 0.005f; + + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames % 128, 0, 0x20, 0x40, 1, + 0, (globalCtx->gameplayFrames * -15) % 256, 0x20, 0x40)); + + for (i = 0; i < 3; i++, scaleY -= 0.001f) { + effect = &this->effects[i]; + + if (effect->move != 0) { + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale * 0.001f, scaleY, effect->scale * 0.001f, MTXMODE_APPLY); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 30, 0, 30, effect->alpha * effect->move); + gDPSetEnvColor(POLY_XLU_DISP++, 30, 0, 30, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_sst.c", 7396), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFireCircleDL); + } + } + } else if (this->effectMode == BONGO_SHADOW) { + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 10, 10, 80, 0); + gDPSetEnvColor(POLY_XLU_DISP++, 10, 10, 10, this->effects[0].alpha); + + effect = &this->effects[0]; + while (effect->status != -1) { + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale * 0.001f, 1.0f, effect->scale * 0.001f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_sst.c", 7423), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sShadowDList); + effect++; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_sst.c", 7433); + } +} + +void BossSst_Reset(void) { + sHead = NULL; + sHands[0] = NULL; + sHands[1] = NULL; + sFloor = NULL; + sHandOffsets[0].x = 0.0f; + sHandOffsets[0].y = 0.0f; + sHandOffsets[0].z = 0.0f; + + sHandOffsets[1].x = 0.0f; + sHandOffsets[1].y = 0.0f; + sHandOffsets[1].z = 0.0f; + + sHandYawOffsets[0] = 0; + sHandYawOffsets[1] = 0; + + sCutsceneCamera= 0; + sBodyStatic = false; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h new file mode 100644 index 000000000..8b2b311d6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.h @@ -0,0 +1,62 @@ +#ifndef Z_BOSS_SST_H +#define Z_BOSS_SST_H + +#include "ultra64.h" +#include "global.h" + +struct BossSst; + +typedef void (*BossSstActionFunc)(struct BossSst*, GlobalContext*); + + +typedef struct { + /* 0x0000 */ Vec3f pos; + /* 0x0010 */ Vec3f vel; + /* 0x0018 */ Vec3s rot; + /* 0x001E */ u16 scale; + /* 0x0020 */ s16 move; + /* 0x0022 */ s16 status; + /* 0x0024 */ u8 alpha; +} BossSstEffect; // size = 0x28 + +typedef struct { + /* 0x0000 */ PosRot world; + /* 0x0014 */ f32 zPosMod; + /* 0x0018 */ s16 yRotMod; +} BossSstHandTrail; // size = 0x1C + +typedef struct BossSst { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ BossSstActionFunc actionFunc; + /* 0x0194 */ s8 actionVar; // head: flag for visible without lens; hand: 1 for right, -1 for left + /* 0x0195 */ s8 ready; + /* 0x0196 */ u8 effectMode; + /* 0x0198 */ s16 timer; + /* 0x019A */ s16 handAngSpeed; + /* 0x019C */ s16 handMaxSpeed; + /* 0x019E */ s16 handZPosMod; + /* 0x01A0 */ s16 handYRotMod; + /* 0x01A2 */ s16 amplitude; + /* 0x01A4 */ s16 targetYaw; + /* 0x01A6 */ s16 targetRoll; + /* 0x01A8 */ Vec3s jointTable[45]; + /* 0x02B6 */ Vec3s morphTable[45]; + /* 0x03C4 */ f32 radius; + /* 0x03C8 */ Vec3f center; + /* 0x03D4 */ ColliderJntSph colliderJntSph; + /* 0x03F4 */ ColliderJntSphElement colliderItems[11]; + /* 0x06B4 */ ColliderCylinder colliderCyl; + /* 0x0700 */ BossSstEffect effects[18]; + /* 0x09D0 */ s16 trailIndex; + /* 0x09D2 */ s16 trailCount; + /* 0x09D4 */ BossSstHandTrail handTrails[7]; +} BossSst; // size = 0x0A98 + +typedef enum { + /* -1 */ BONGO_HEAD = -1, + /* 0 */ BONGO_LEFT_HAND, + /* 1 */ BONGO_RIGHT_HAND +} BossSstType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst_colchk.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst_colchk.c new file mode 100644 index 000000000..16b7c4e8d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst_colchk.c @@ -0,0 +1,352 @@ +#include "z_boss_sst.h" + +static ColliderJntSphElementInit sJntSphItemsInitHand[11] = { + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 2, { { 2000, -1500, 250 }, 65 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 10, { { 0, 0, 0 }, 22 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 11, { { 500, 0, 0 }, 22 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { -250, -250, 0 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 16, { { 500, -250, 0 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, { { 250, -250, 0 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 21, { { 500, -250, 0 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 25, { { 0, 0, 0 }, 27 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 26, { { 750, 0, 0 }, 26 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 5, { { 750, -150, 0 }, 21 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 6, { { 750, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInitHand = { + { + COLTYPE_HIT0, + AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 11, + sJntSphItemsInitHand, +}; + +static ColliderJntSphElementInit sJntSphItemsInitHead[11] = { + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 7, { { 1500, 0, 0 }, 70 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 6, { { 0, 0, 0 }, 75 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 4, { { 5000, 0, 0 }, 120 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 3, { { -2500, 0, 0 }, 150 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 43, { { 1500, 0, 0 }, 80 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 43, { { 7500, 0, 0 }, 70 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 44, { { 3000, 0, 0 }, 60 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 40, { { 1500, 0, 0 }, 80 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 40, { { 7500, 0, 0 }, 70 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 41, { { 3000, 0, 0 }, 60 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x30 }, + { 0x00000080, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 8, { { 1500, 0, 0 }, 70 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInitHead = { + { + COLTYPE_HARD, + AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 11, + sJntSphItemsInitHead, +}; + +static ColliderCylinderInit sCylinderInitHead = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 85, 100, -50, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInitHand = { + { + COLTYPE_NONE, + AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x04, 0x10 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 85, 1, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 36, 100, 100, 200 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x3), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(4, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x3), + /* Light magic */ DMG_ENTRY(4, 0x4), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c new file mode 100644 index 000000000..38e4996db --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -0,0 +1,5472 @@ +#include "z_boss_tw.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_tw/object_tw.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + +#include + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ TWEFF_NONE, + /* 1 */ TWEFF_DOT, + /* 2 */ TWEFF_2, + /* 3 */ TWEFF_3, + /* 4 */ TWEFF_RING, + /* 5 */ TWEFF_PLYR_FRZ, + /* 6 */ TWEFF_FLAME, + /* 7 */ TWEFF_MERGEFLAME, + /* 8 */ TWEFF_SHLD_BLST, + /* 9 */ TWEFF_SHLD_DEFL, + /* 10 */ TWEFF_SHLD_HIT +} TwEffType; + +typedef enum { + /* 0 */ EFF_ARGS, + /* 1 */ EFF_UNKS1, + /* 2 */ EFF_WORK_MAX +} EffectWork; + +typedef enum { + /* 0 */ EFF_SCALE, + /* 1 */ EFF_DIST, + /* 2 */ EFF_ROLL, + /* 3 */ EFF_YAW, + /* 4 */ EFF_FWORK_MAX +} EffectFWork; + +typedef enum { + /* 0x00 */ TW_KOTAKE, + /* 0x01 */ TW_KOUME, + /* 0x02 */ TW_TWINROVA, + /* 0x64 */ TW_FIRE_BLAST = 0x64, + /* 0x65 */ TW_FIRE_BLAST_GROUND, + /* 0x66 */ TW_ICE_BLAST, + /* 0x67 */ TW_ICE_BLAST_GROUND, + /* 0x68 */ TW_DEATHBALL_KOTAKE, + /* 0x69 */ TW_DEATHBALL_KOUME +} TwinrovaType; + +typedef struct { + /* 0x0000 */ u8 type; + /* 0x0001 */ u8 frame; + /* 0x0004 */ Vec3f pos; + /* 0x0010 */ Vec3f curSpeed; + /* 0x001C */ Vec3f accel; + /* 0x0028 */ Color_RGB8 color; + /* 0x002C */ s16 alpha; + /* 0x002E */ s16 work[EFF_WORK_MAX]; + /* 0x0034 */ f32 workf[EFF_FWORK_MAX]; + /* 0x0044 */ Actor* target; +} BossTwEffect; + +void BossTw_Init(Actor* thisx, GlobalContext* globalCtx); +void BossTw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossTw_Update(Actor* thisx, GlobalContext* globalCtx); +void BossTw_Draw(Actor* thisx, GlobalContext* globalCtx); +void BossTw_Reset(void); + +void BossTw_TwinrovaDamage(BossTw* this, GlobalContext* globalCtx, u8 arg2); +void BossTw_TwinrovaSetupFly(BossTw* this, GlobalContext* globalCtx); +void BossTw_DrawEffects(GlobalContext* globalCtx); +void BossTw_TwinrovaLaugh(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaFly(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaGetUp(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupGetUp(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupLaugh(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaDoneBlastShoot(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupDoneBlastShoot(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupShootBlast(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupChargeBlast(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaArriveAtTarget(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaDeathCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaIntroCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_CSWait(BossTw* this, GlobalContext* globalCtx); +void BossTw_DeathCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaMergeCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupMergeCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_MergeCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_Spin(BossTw* this, GlobalContext* globalCtx); +void BossTw_Laugh(BossTw* this, GlobalContext* globalCtx); +void BossTw_SetupLaugh(BossTw* this, GlobalContext* globalCtx); +void BossTw_FinishBeamShoot(BossTw* this, GlobalContext* globalCtx); +void BossTw_SetupFinishBeamShoot(BossTw* this, GlobalContext* globalCtx); +void BossTw_SetupHitByBeam(BossTw* this, GlobalContext* globalCtx); +void BossTw_HitByBeam(BossTw* this, GlobalContext* globalCtx); +void BossTw_Wait(BossTw* this, GlobalContext* globalCtx); +void BossTw_ShootBeam(BossTw* this, GlobalContext* globalCtx); +void BossTw_FlyTo(BossTw* this, GlobalContext* globalCtx); +void BossTw_SetupShootBeam(BossTw* this, GlobalContext* globalCtx); +void BossTw_TurnToPlayer(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaUpdate(Actor* thisx, GlobalContext* globalCtx); +void BossTw_TwinrovaDraw(Actor* thisx, GlobalContext* globalCtx); +void BossTw_SetupWait(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupIntroCS(BossTw* this, GlobalContext* globalCtx); +void BossTw_SetupFlyTo(BossTw* this, GlobalContext* globalCtx); +void BossTw_SetupCSWait(BossTw* this, GlobalContext* globalCtx); +void BossTw_BlastUpdate(Actor* thisx, GlobalContext* globalCtx); +void BossTw_BlastDraw(Actor* thisx, GlobalContext* globalCtx); +void BossTw_BlastFire(BossTw* this, GlobalContext* globalCtx); +void BossTw_BlastIce(BossTw* this, GlobalContext* globalCtx); +void BossTw_DeathBall(BossTw* this, GlobalContext* globalCtx); +void BossTw_DrawDeathBall(Actor* thisx, GlobalContext* globalCtx); +void BossTw_TwinrovaStun(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSpin(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaShootBlast(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaChargeBlast(BossTw* this, GlobalContext* globalCtx); +void BossTw_TwinrovaSetupSpin(BossTw* this, GlobalContext* globalCtx); +void BossTw_UpdateEffects(GlobalContext* globalCtx); + +const ActorInit Boss_Tw_InitVars = { + ACTOR_BOSS_TW, + ACTORCAT_BOSS, + FLAGS, + OBJECT_TW, + sizeof(BossTw), + (ActorFunc)BossTw_Init, + (ActorFunc)BossTw_Destroy, + (ActorFunc)BossTw_Update, + (ActorFunc)BossTw_Draw, + (ActorResetFunc)BossTw_Reset, +}; + +static Vec3f D_8094A7D0 = { 0.0f, 0.0f, 1000.0f }; +static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f }; + +static ColliderCylinderInit sCylinderInitBlasts = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ALL, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x30 }, + { 0x00100000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 25, 35, -17, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInitKoumeKotake = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 45, 120, -30, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInitTwinrova = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x20 }, + { 0xFFCDFFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 45, 120, -30, { 0, 0, 0 } }, +}; + +static Vec3f sTwinrovaPillarPos[] = { + { 580.0f, 380.0f, 0.0f }, + { 0.0f, 380.0f, 580.0f }, + { -580.0f, 380.0f, 0.0f }, + { 0.0f, 380.0f, -580.0f }, +}; + +static u8 sTwInitalized = false; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +static s8 sEnvType; +static u8 sGroundBlastType; +static BossTw* sKotakePtr; +static BossTw* sKoumePtr; +static BossTw* sTwinrovaPtr; +static u8 sShieldFireCharge; +static u8 sShieldIceCharge; +static f32 D_8094C854; +static f32 D_8094C858; +static u8 sTwinrovaBlastType; +static u8 sFixedBlastType; +static u8 sFixedBlatSeq; +static u8 sFreezeState; +static Vec3f sShieldHitPos; +static s16 sShieldHitYaw; +static u8 sBeamDivertTimer; +static u8 D_8094C86F; +static u8 D_8094C870; +static s16 D_8094C872; +static s16 D_8094C874; +static s16 D_8094C876; +static u8 D_8094C878; +static s16 D_8094C87A; +static s16 D_8094C87C; +static u8 D_8094C87E; +static BossTwEffect sTwEffects[150]; + +void BossTw_AddDotEffect(GlobalContext* globalCtx, Vec3f* initalPos, Vec3f* initalSpeed, Vec3f* accel, f32 scale, + s16 args, s16 countLimit) { + s16 i; + BossTwEffect* eff; + + for (i = 0, eff = globalCtx->specialEffects; i < countLimit; i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_DOT; + eff->pos = *initalPos; + eff->curSpeed = *initalSpeed; + eff->accel = *accel; + eff->workf[EFF_SCALE] = scale / 1000.0f; + eff->alpha = 255; + eff->frame = (s16)Rand_ZeroFloat(10.0f); + eff->work[EFF_ARGS] = args; + break; + } + } +} + +void BossTw_AddDmgCloud(GlobalContext* globalCtx, s16 type, Vec3f* initialPos, Vec3f* initalSpeed, Vec3f* accel, + f32 scale, s16 alpha, s16 args, s16 countLimit) { + s16 i; + BossTwEffect* eff; + + for (i = 0, eff = globalCtx->specialEffects; i < countLimit; i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = type; + eff->pos = *initialPos; + eff->curSpeed = *initalSpeed; + eff->accel = *accel; + eff->workf[EFF_SCALE] = scale / 1000.0f; + eff->work[EFF_ARGS] = args; + eff->alpha = alpha; + eff->frame = (s16)Rand_ZeroFloat(100.0f); + break; + } + } +} + +void BossTw_AddRingEffect(GlobalContext* globalCtx, Vec3f* initalPos, f32 scale, f32 arg3, s16 alpha, s16 args, + s16 arg6, s16 arg7) { + s16 i; + BossTwEffect* eff; + + for (i = 0, eff = globalCtx->specialEffects; i < arg7; i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_RING; + eff->pos = *initalPos; + eff->curSpeed = sZeroVector; + eff->accel = sZeroVector; + eff->workf[EFF_SCALE] = scale * 0.0025f; + eff->workf[EFF_DIST] = arg3 * 0.0025f; + eff->work[EFF_ARGS] = args; + eff->work[EFF_UNKS1] = arg6; + eff->alpha = alpha; + eff->workf[EFF_ROLL] = Rand_ZeroFloat(M_PI); + eff->frame = 0; + break; + } + } +} + +void BossTw_AddPlayerFreezeEffect(GlobalContext* globalCtx, Actor* target) { + BossTwEffect* eff; + s16 i; + + for (eff = globalCtx->specialEffects, i = 0; i < ARRAY_COUNT(sTwEffects); i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_PLYR_FRZ; + eff->curSpeed = sZeroVector; + eff->accel = sZeroVector; + eff->frame = 0; + eff->target = target; + eff->workf[EFF_DIST] = 0.0f; + eff->workf[EFF_SCALE] = 0.0f; + eff->workf[EFF_ROLL] = 0.0f; + if (target == NULL) { + eff->work[EFF_ARGS] = 100; + } else { + eff->work[EFF_ARGS] = 20; + } + break; + } + } +} + +void BossTw_AddFlameEffect(GlobalContext* globalCtx, Vec3f* initalPos, Vec3f* initalSpeed, Vec3f* accel, f32 scale, + s16 args) { + s16 i; + BossTwEffect* eff; + + for (i = 0, eff = globalCtx->specialEffects; i < ARRAY_COUNT(sTwEffects); i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_FLAME; + eff->pos = *initalPos; + eff->curSpeed = *initalSpeed; + eff->accel = *accel; + eff->workf[EFF_SCALE] = scale / 1000.0f; + eff->work[EFF_ARGS] = args; + eff->work[EFF_UNKS1] = 0; + eff->alpha = 0; + eff->frame = (s16)Rand_ZeroFloat(1000.0f); + break; + } + } +} + +void BossTw_AddMergeFlameEffect(GlobalContext* globalCtx, Vec3f* initialPos, f32 scale, f32 dist, s16 args) { + s16 i; + BossTwEffect* eff; + + for (i = 0, eff = globalCtx->specialEffects; i < ARRAY_COUNT(sTwEffects); i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_MERGEFLAME; + eff->pos = *initialPos; + eff->curSpeed = sZeroVector; + eff->accel = sZeroVector; + eff->workf[EFF_SCALE] = scale / 1000.0f; + eff->work[EFF_ARGS] = args; + eff->work[EFF_UNKS1] = 0; + eff->workf[EFF_DIST] = dist; + eff->workf[EFF_ROLL] = Rand_ZeroFloat(2.0f * M_PI); + eff->alpha = 0; + eff->frame = (s16)Rand_ZeroFloat(1000.0f); + break; + } + } +} + +void BossTw_AddShieldBlastEffect(GlobalContext* globalCtx, Vec3f* initalPos, Vec3f* initalSpeed, Vec3f* accel, + f32 scale, f32 arg5, s16 alpha, s16 args) { + s16 i; + BossTwEffect* eff; + + for (i = 0, eff = globalCtx->specialEffects; i < ARRAY_COUNT(sTwEffects); i++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_SHLD_BLST; + eff->pos = *initalPos; + eff->curSpeed = *initalSpeed; + eff->accel = *accel; + eff->workf[EFF_SCALE] = scale / 1000.0f; + eff->workf[EFF_DIST] = arg5 / 1000.0f; + eff->work[EFF_ARGS] = args; + eff->work[EFF_UNKS1] = 0; + eff->alpha = alpha; + eff->frame = (s16)Rand_ZeroFloat(1000.0f); + break; + } + } +} + +void BossTw_AddShieldDeflectEffect(GlobalContext* globalCtx, f32 arg1, s16 arg2) { + s16 i; + s16 j; + BossTwEffect* eff; + Player* player = GET_PLAYER(globalCtx); + + sShieldHitPos = player->bodyPartsPos[15]; + sShieldHitYaw = player->actor.shape.rot.y; + + for (i = 0; i < 8; i++) { + for (eff = globalCtx->specialEffects, j = 0; j < ARRAY_COUNT(sTwEffects); j++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_SHLD_DEFL; + eff->pos = sShieldHitPos; + eff->curSpeed = sZeroVector; + eff->accel = sZeroVector; + eff->workf[EFF_ROLL] = i * (M_PI / 4.0f); + eff->workf[EFF_YAW] = M_PI / 2.0f; + eff->workf[EFF_DIST] = 0.0f; + eff->workf[EFF_SCALE] = arg1 / 1000.0f; + eff->work[EFF_ARGS] = arg2; + eff->work[EFF_UNKS1] = 0; + eff->alpha = 255; + eff->frame = (s16)Rand_ZeroFloat(1000.0f); + break; + } + } + } +} + +void BossTw_AddShieldHitEffect(GlobalContext* globalCtx, f32 arg1, s16 arg2) { + s16 i; + s16 j; + BossTwEffect* eff; + Player* player = GET_PLAYER(globalCtx); + + sShieldHitPos = player->bodyPartsPos[15]; + sShieldHitYaw = player->actor.shape.rot.y; + + for (i = 0; i < 8; i++) { + for (eff = globalCtx->specialEffects, j = 0; j < ARRAY_COUNT(sTwEffects); j++, eff++) { + if (eff->type == TWEFF_NONE) { + eff->type = TWEFF_SHLD_HIT; + eff->pos = sShieldHitPos; + eff->curSpeed = sZeroVector; + eff->accel = sZeroVector; + eff->workf[EFF_ROLL] = i * (M_PI / 4.0f); + eff->workf[EFF_YAW] = M_PI / 2.0f; + eff->workf[EFF_DIST] = 0.0f; + eff->workf[EFF_SCALE] = arg1 / 1000.0f; + eff->work[EFF_ARGS] = arg2; + eff->work[EFF_UNKS1] = 0; + eff->alpha = 255; + eff->frame = (s16)Rand_ZeroFloat(1000.0f); + break; + } + } + } +} + +void BossTw_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossTw* this = (BossTw*)thisx; + s16 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + + if (this->actor.params >= TW_FIRE_BLAST) { + // Blasts + Actor_SetScale(&this->actor, 0.01f); + this->actor.update = BossTw_BlastUpdate; + this->actor.draw = BossTw_BlastDraw; + this->actor.flags &= ~ACTOR_FLAG_0; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitBlasts); + + if (this->actor.params == TW_FIRE_BLAST || this->actor.params == TW_FIRE_BLAST_GROUND) { + this->actionFunc = BossTw_BlastFire; + this->collider.info.toucher.effect = 1; + } else if (this->actor.params == TW_ICE_BLAST || this->actor.params == TW_ICE_BLAST_GROUND) { + this->actionFunc = BossTw_BlastIce; + } else if (this->actor.params >= TW_DEATHBALL_KOTAKE) { + this->actionFunc = BossTw_DeathBall; + this->actor.draw = BossTw_DrawDeathBall; + this->workf[TAIL_ALPHA] = 128.0f; + + if (thisx->params == TW_DEATHBALL_KOTAKE) { + thisx->world.rot.y = sTwinrovaPtr->actor.world.rot.y + 0x4000; + } else { + thisx->world.rot.y = sTwinrovaPtr->actor.world.rot.y - 0x4000; + } + } + + this->timers[1] = 150; + return; + } + + Actor_SetScale(&this->actor, 2.5 * 0.01f); + this->actor.colChkInfo.mass = 255; + this->actor.colChkInfo.health = 0; + Collider_InitCylinder(globalCtx, &this->collider); + + if (!sTwInitalized) { + sTwInitalized = true; + globalCtx->envCtx.unk_BF = 1; + globalCtx->envCtx.unk_BE = 1; + globalCtx->envCtx.unk_BD = 1; + globalCtx->envCtx.unk_D8 = 0.0f; + + D_8094C874 = D_8094C876 = D_8094C878 = D_8094C87A = D_8094C87C = D_8094C87E = D_8094C870 = D_8094C86F = + D_8094C872 = sBeamDivertTimer = sEnvType = sGroundBlastType = sFreezeState = sTwinrovaBlastType = + sFixedBlatSeq = sShieldFireCharge = sShieldIceCharge = 0; + + D_8094C858 = D_8094C854 = 0.0f; + sFixedBlastType = Rand_ZeroFloat(1.99f); + globalCtx->specialEffects = sTwEffects; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + sTwEffects[i].type = TWEFF_NONE; + } + } + + if (this->actor.params == TW_KOTAKE) { + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitKoumeKotake); + this->actor.naviEnemyId = 0x33; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tw_Skel_0070E0, &object_tw_Anim_006F28, NULL, NULL, 0); + + if (gSaveContext.eventChkInf[7] & 0x20) { + // began twinrova battle + BossTw_SetupFlyTo(this, globalCtx); + this->actor.world.pos.x = -600.0f; + this->actor.world.pos.y = 400.0f; + this->actor.world.pos.z = 0.0f; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } else { + BossTw_SetupCSWait(this, globalCtx); + } + + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -3.0f); + this->visible = true; + } else if (this->actor.params == TW_KOUME) { + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitKoumeKotake); + this->actor.naviEnemyId = 0x32; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tw_Skel_01F888, &object_tw_Anim_006F28, NULL, NULL, 0); + + if (gSaveContext.eventChkInf[7] & 0x20) { + // began twinrova battle + BossTw_SetupFlyTo(this, globalCtx); + this->actor.world.pos.x = 600.0f; + this->actor.world.pos.y = 400.0f; + this->actor.world.pos.z = 0.0f; + } else { + BossTw_SetupCSWait(this, globalCtx); + } + + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -3.0f); + this->visible = true; + } else { + // Twinrova + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitTwinrova); + this->actor.naviEnemyId = 0x5B; + this->actor.colChkInfo.health = 24; + this->actor.update = BossTw_TwinrovaUpdate; + this->actor.draw = BossTw_TwinrovaDraw; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tw_Skel_032020, &object_tw_Anim_0244B4, NULL, NULL, 0); + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_0244B4, -3.0f); + + if (gSaveContext.eventChkInf[7] & 0x20) { + // began twinrova battle + BossTw_SetupWait(this, globalCtx); + } else { + BossTw_TwinrovaSetupIntroCS(this, globalCtx); + this->actor.world.pos.x = 0.0f; + this->actor.world.pos.y = 1000.0f; + this->actor.world.pos.z = 0.0f; + } + + this->actor.params = TW_TWINROVA; + sTwinrovaPtr = this; + + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + // twinrova has been defeated. + Actor_Kill(&this->actor); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 600.0f, 230.0f, 0.0f, 0, + 0, 0, WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, -600.0f, 230.0f, 0.0f, 0, 0, 0, 0); + } else { + sKotakePtr = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, TW_KOTAKE); + sKoumePtr = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, TW_KOUME); + sKotakePtr->actor.parent = &sKoumePtr->actor; + sKoumePtr->actor.parent = &sKotakePtr->actor; + } + } + + this->fogR = globalCtx->lightCtx.fogColor[0]; + this->fogG = globalCtx->lightCtx.fogColor[1]; + this->fogB = globalCtx->lightCtx.fogColor[2]; + this->fogNear = globalCtx->lightCtx.fogNear; + this->fogFar = 1000.0f; +} + +void BossTw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BossTw* this = (BossTw*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + if (thisx->params < TW_FIRE_BLAST) { + SkelAnime_Free(&this->skelAnime, globalCtx); + } + + if (thisx->params == TW_TWINROVA) { + sTwInitalized = false; + } +} + +void BossTw_SetupTurnToPlayer(BossTw* this, GlobalContext* globalCtx) { + BossTw* otherTw = (BossTw*)this->actor.parent; + + this->actionFunc = BossTw_TurnToPlayer; + + if ((otherTw != NULL) && (otherTw->actionFunc == BossTw_ShootBeam)) { + this->timers[0] = 40; + } else { + this->timers[0] = 60; + } + + this->rotateSpeed = 0.0f; +} + +void BossTw_TurnToPlayer(BossTw* this, GlobalContext* globalCtx) { + BossTw* otherTw = (BossTw*)this->actor.parent; + + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f); + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->rotateSpeed); + Math_ApproachS(&this->actor.shape.rot.x, 0, 5, this->rotateSpeed); + Math_ApproachF(&this->rotateSpeed, 4096.0f, 1.0f, 200.0f); + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + if (this->timers[0] == 0) { + if ((otherTw->actionFunc != BossTw_ShootBeam) && this->work[CAN_SHOOT]) { + this->work[CAN_SHOOT] = false; + BossTw_SetupShootBeam(this, globalCtx); + this->actor.speedXZ = 0.0f; + } else { + BossTw_SetupFlyTo(this, globalCtx); + } + } +} + +void BossTw_SetupFlyTo(BossTw* this, GlobalContext* globalCtx) { + static Vec3f sPillarPositions[] = { + { 600.0f, 400.0f, 0.0f }, { 0.0f, 400.0f, 600.0f }, { -600.0f, 400.0f, 0.0f }, { 0.0f, 400.0f, -600.0f } + }; + BossTw* otherTw = (BossTw*)this->actor.parent; + + this->unk_5F8 = 1; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = BossTw_FlyTo; + this->rotateSpeed = 0.0f; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -10.0f); + if ((Rand_ZeroOne() < 0.5f) && (otherTw != NULL && otherTw->actionFunc == BossTw_ShootBeam)) { + // Other Sister is shooting a beam, go near them. + this->targetPos.x = otherTw->actor.world.pos.x + Rand_CenteredFloat(200.0f); + this->targetPos.y = Rand_ZeroFloat(200.0f) + 340.0f; + this->targetPos.z = otherTw->actor.world.pos.z + Rand_CenteredFloat(200.0f); + this->timers[0] = (s16)Rand_ZeroFloat(50.0f) + 50; + } else if (Rand_ZeroOne() < 0.5f) { + // Fly to a random spot. + this->targetPos.x = Rand_CenteredFloat(800.0f); + this->targetPos.y = Rand_ZeroFloat(200.0f) + 340.0f; + this->targetPos.z = Rand_CenteredFloat(800.0f); + this->timers[0] = (s16)Rand_ZeroFloat(50.0f) + 50; + } else { + // fly to a random pillar. + s16 idx = Rand_ZeroFloat(ARRAY_COUNT(sPillarPositions) - 0.01f); + + this->targetPos = sPillarPositions[idx]; + this->timers[0] = 200; + this->work[CAN_SHOOT] = true; + } +} + +void BossTw_FlyTo(BossTw* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 pitchTarget; + f32 yawTarget; + f32 xzDist; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG); + Math_ApproachF(&this->scepterAlpha, 0.0f, 1.0f, 10.0f); + SkelAnime_Update(&this->skelAnime); + + xDiff = this->targetPos.x - this->actor.world.pos.x; + yDiff = this->targetPos.y - this->actor.world.pos.y; + zDiff = this->targetPos.z - this->actor.world.pos.z; + + yawTarget = (s16)(Math_FAtan2F(xDiff, zDiff) * (32768.0f / M_PI)); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + pitchTarget = (s16)(Math_FAtan2F(yDiff, xzDist) * (32768.0f / M_PI)); + + Math_ApproachS(&this->actor.world.rot.x, pitchTarget, 0xA, this->rotateSpeed); + Math_ApproachS(&this->actor.world.rot.y, yawTarget, 0xA, this->rotateSpeed); + Math_ApproachS(&this->actor.shape.rot.y, yawTarget, 0xA, this->rotateSpeed); + Math_ApproachS(&this->actor.shape.rot.x, pitchTarget, 0xA, this->rotateSpeed); + Math_ApproachF(&this->rotateSpeed, 4096.0f, 1.0f, 100.0f); + Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 1.0f); + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + + if ((this->timers[0] == 0) || (xzDist < 70.0f)) { + BossTw_SetupTurnToPlayer(this, globalCtx); + } +} + +void BossTw_SetupShootBeam(BossTw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->actionFunc = BossTw_ShootBeam; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_007688, -5.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_007688); + this->timers[1] = 70; + this->targetPos = player->actor.world.pos; + this->csState1 = 0; + this->beamDist = 0.0f; + this->beamReflectionDist = 0.0f; + this->beamShootState = -1; + this->beamScale = 0.01f; + this->beamReflectionOrigin = this->beamOrigin; + this->flameAlpha = 0.0f; + this->spawnPortalAlpha = 0.0f; + this->spawnPortalScale = 2000.0f; + this->updateRate1 = 0.0f; + this->portalRotation = 0.0f; + this->updateRate2 = 0.0f; +} + +void BossTw_SpawnGroundBlast(BossTw* this, GlobalContext* globalCtx, s16 blastType) { + BossTw* groundBlast; + s16 i; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_ZeroFloat(10.0f); + velocity.z = Rand_CenteredFloat(20.0f); + accel.y = 0.2f; + accel.x = Rand_CenteredFloat(0.25f); + accel.z = Rand_CenteredFloat(0.25f); + pos = this->groundBlastPos; + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8, blastType, 75); + } + + if (blastType == 1) { + sGroundBlastType = 1; + groundBlast = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, + this->groundBlastPos.x, this->groundBlastPos.y, + this->groundBlastPos.z, 0, 0, 0, TW_FIRE_BLAST_GROUND); + if (groundBlast != NULL) { + if (sTwinrovaPtr->actionFunc == BossTw_Wait) { + groundBlast->timers[0] = 100; + } else { + groundBlast->timers[0] = 50; + } + sKoumePtr->workf[KM_GD_FLM_A] = sKoumePtr->workf[KM_GD_SMOKE_A] = sKoumePtr->workf[KM_GRND_CRTR_A] = 255.0f; + sKoumePtr->workf[KM_GD_FLM_SCL] = 1.0f; + sKoumePtr->workf[KM_GD_CRTR_SCL] = 0.005f; + sKoumePtr->groundBlastPos2 = groundBlast->actor.world.pos; + sEnvType = 4; + } + } else { + sGroundBlastType = 2; + groundBlast = (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, + this->groundBlastPos.x, this->groundBlastPos.y, + this->groundBlastPos.z, 0, 0, 0, TW_ICE_BLAST_GROUND); + if (groundBlast != NULL) { + if (sTwinrovaPtr->actionFunc == BossTw_Wait) { + groundBlast->timers[0] = 100; + } else { + groundBlast->timers[0] = 50; + } + + sKotakePtr->workf[UNK_F11] = 50.0f; + sKotakePtr->workf[UNK_F9] = 250.0f; + sKotakePtr->workf[UNK_F12] = 0.005f; + sKotakePtr->workf[UNK_F14] = 1.0f; + sKotakePtr->workf[UNK_F16] = 70.0f; + sKotakePtr->groundBlastPos2 = groundBlast->actor.world.pos; + sEnvType = 3; + } + } +} + +s32 BossTw_BeamHitPlayerCheck(BossTw* this, GlobalContext* globalCtx) { + Vec3f offset; + Vec3f beamDistFromPlayer; + Player* player = GET_PLAYER(globalCtx); + s16 i; + + offset.x = player->actor.world.pos.x - this->beamOrigin.x; + offset.y = player->actor.world.pos.y - this->beamOrigin.y; + offset.z = player->actor.world.pos.z - this->beamOrigin.z; + + Matrix_RotateX(-this->beamPitch, MTXMODE_NEW); + Matrix_RotateY(-this->beamYaw, MTXMODE_APPLY); + Matrix_MultVec3f(&offset, &beamDistFromPlayer); + + if (fabsf(beamDistFromPlayer.x) < 20.0f && fabsf(beamDistFromPlayer.y) < 50.0f && beamDistFromPlayer.z > 100.0f && + beamDistFromPlayer.z <= this->beamDist) { + if (sTwinrovaPtr->timers[2] == 0) { + sTwinrovaPtr->timers[2] = 150; + this->beamDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z)); + func_8002F6D4(globalCtx, &this->actor, 3.0f, this->actor.shape.rot.y, 0.0f, 0x20); + + if (this->actor.params == 0) { + if (sFreezeState == 0) { + sFreezeState = 1; + } + } else if (!player->isBurning) { + for (i = 0; i < ARRAY_COUNT(player->flameTimers); i++) { + player->flameTimers[i] = Rand_S16Offset(0, 200); + } + + player->isBurning = true; + func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_DEMO_DAMAGE); + } + } + + return true; + } + return false; +} + +/** + * Checks if the beam shot by `this` will be reflected + * returns 0 if the beam will not be reflected, + * returns 1 if the beam will be reflected, + * and returns 2 if the beam will be diverted backwards + */ +s32 BossTw_CheckBeamReflection(BossTw* this, GlobalContext* globalCtx) { + Vec3f offset; + Vec3f vec; + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags1 & 0x400000 && + (s16)(player->actor.shape.rot.y - this->actor.shape.rot.y + 0x8000) < 0x2000 && + (s16)(player->actor.shape.rot.y - this->actor.shape.rot.y + 0x8000) > -0x2000) { + // player is shielding and facing angles are less than 45 degrees in either direction + offset.x = 0.0f; + offset.y = 0.0f; + offset.z = 10.0f; + + // set beam check point to 10 units in front of link. + Matrix_RotateY(player->actor.shape.rot.y / 32768.0f * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&offset, &vec); + + // calculates a vector where the origin is at the beams origin, + // and the positive z axis is pointing in the direction the beam + // is shooting + offset.x = player->actor.world.pos.x + vec.x - this->beamOrigin.x; + offset.y = player->actor.world.pos.y + vec.y - this->beamOrigin.y; + offset.z = player->actor.world.pos.z + vec.z - this->beamOrigin.z; + + Matrix_RotateX(-this->beamPitch, MTXMODE_NEW); + Matrix_RotateY(-this->beamYaw, MTXMODE_APPLY); + Matrix_MultVec3f(&offset, &vec); + + if (fabsf(vec.x) < 30.0f && fabsf(vec.y) < 70.0f && vec.z > 100.0f && vec.z <= this->beamDist) { + // if the beam's origin is within 30 x units, 70 y units, is farther than 100 units + // and the distance from the beams origin to 10 units in front of link is less than the beams + // current distance (the distance of the beam is equal to or longer than the distance to 10 units + // in front of link) + if (Player_HasMirrorShieldEquipped(globalCtx)) { + // player has mirror shield equipped + this->beamDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z)); + return 1; + } + + if (sBeamDivertTimer > 10) { + return 0; + } + + if (sBeamDivertTimer == 0) { + // beam hit the shield, normal shield equipped, + // divert the beam backwards from link's Y rotation + BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, this->actor.params); + globalCtx->envCtx.unk_D8 = 1.0f; + this->timers[0] = 10; + func_80078884(NA_SE_IT_SHIELD_REFLECT_MG2); + } + + sBeamDivertTimer++; + this->beamDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z)); + return 2; + } + } + + return 0; +} + +s32 BossTw_BeamReflHitCheck(BossTw* this, Vec3f* pos) { + Vec3f offset; + Vec3f beamDistFromTarget; + + offset.x = pos->x - this->beamReflectionOrigin.x; + offset.y = pos->y - this->beamReflectionOrigin.y; + offset.z = pos->z - this->beamReflectionOrigin.z; + + Matrix_RotateX(-this->beamReflectionPitch, MTXMODE_NEW); + Matrix_RotateY(-this->beamReflectionYaw, MTXMODE_APPLY); + Matrix_MultVec3f(&offset, &beamDistFromTarget); + + if (fabsf(beamDistFromTarget.x) < 50.0f && fabsf(beamDistFromTarget.y) < 50.0f && beamDistFromTarget.z > 100.0f && + beamDistFromTarget.z <= this->beamReflectionDist) { + this->beamReflectionDist = sqrtf(SQ(offset.x) + SQ(offset.y) + SQ(offset.z)) * 1.1f; + return true; + } else { + return false; + } +} + +f32 BossTw_GetFloorY(Vec3f* pos) { + Vec3f posRotated; + + if (fabsf(pos->x) < 350.0f && fabsf(pos->z) < 350.0f && pos->y < 240.0f) { + if (pos->y > 200.0f) { + return 240.0f; + } + return 35.0f; + } + + if (fabsf(pos->x) < 110.0f && ((fabsf(pos->z - 600.0f) < 110.0f) || (fabsf(pos->z + 600.0f) < 110.0f)) && + (pos->y < 230.0f)) { + if (pos->y > 190.0f) { + return 230.0f; + } + return 35.0f; + } + + if (fabsf(pos->z) < 110.0f && ((fabsf(pos->x - 600.0f) < 110.0f) || (fabsf(pos->x + 600.0f) < 110.0f)) && + (pos->y < 230.0f)) { + if (pos->y > 190.0f) { + return 230.0f; + } + return 35.0f; + } + + if (pos->y < -20.0f) { + return 0.0f; + } + + if (fabsf(pos->x) > 1140.0f || fabsf(pos->z) > 1140.0f) { + return 35.0f; + } + + Matrix_Push(); + Matrix_RotateY((45.0f * (M_PI / 180.0f)), MTXMODE_NEW); + Matrix_MultVec3f(pos, &posRotated); + Matrix_Pop(); + + if (fabsf(posRotated.x) > 920.0f || fabsf(posRotated.z) > 920.0f) { + return 35.0f; + } + + return -100.0f; +} + +void BossTw_ShootBeam(BossTw* this, GlobalContext* globalCtx) { + s16 i; + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 floorY; + Vec3f sp130; + Vec3s sp128; + Player* player = GET_PLAYER(globalCtx); + BossTw* otherTw = (BossTw*)this->actor.parent; + Input* input = &globalCtx->state.input[0]; + + Math_ApproachF(&this->actor.world.pos.y, 400.0f, 0.05f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 5.0f, 1.0f, 0.25f); + SkelAnime_Update(&this->skelAnime); + this->beamRoll += -0.3f; + + if (this->timers[1] != 0) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->rotateSpeed); + if ((player->stateFlags1 & 0x400000) && + ((s16)((player->actor.shape.rot.y - this->actor.shape.rot.y) + 0x8000) < 0x2000) && + ((s16)((player->actor.shape.rot.y - this->actor.shape.rot.y) + 0x8000) > -0x2000)) { + Math_ApproachF(&this->targetPos.x, player->bodyPartsPos[15].x, 1.0f, 400.0f); + Math_ApproachF(&this->targetPos.y, player->bodyPartsPos[15].y, 1.0f, 400.0f); + Math_ApproachF(&this->targetPos.z, player->bodyPartsPos[15].z, 1.0f, 400.0f); + } else { + Math_ApproachF(&this->targetPos.x, player->actor.world.pos.x, 1.0f, 400.0f); + Math_ApproachF(&this->targetPos.y, player->actor.world.pos.y + 30.0f, 1.0f, 400.0f); + Math_ApproachF(&this->targetPos.z, player->actor.world.pos.z, 1.0f, 400.0f); + } + + this->timers[0] = 70; + this->groundBlastPos.x = this->groundBlastPos.y = this->groundBlastPos.z = 0.0f; + this->portalRotation += this->updateRate2 * 0.0025f; + Math_ApproachF(&this->spawnPortalAlpha, 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->updateRate2, 50.0f, 1.0f, 2.0f); + + if (this->timers[1] < 50) { + if (this->timers[1] < 10) { + if (this->timers[1] == 9) { + globalCtx->envCtx.unk_D8 = 0.5f; + globalCtx->envCtx.unk_BD = 3 - this->actor.params; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_MASIC_SET); + } + + if (this->timers[1] == 5) { + this->scepterAlpha = 255; + } + + if (this->timers[1] > 4) { + s16 j; + for (j = 0; j < 2; j++) { + for (i = 0; i < ARRAY_COUNT(this->scepterFlamePos); i++) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + pos.x = this->scepterFlamePos[i].x; + pos.y = this->scepterFlamePos[i].y; + pos.z = this->scepterFlamePos[i].z; + velocity.x = Rand_CenteredFloat(10.0f); + velocity.y = Rand_CenteredFloat(10.0f); + velocity.z = Rand_CenteredFloat(10.0f); + accel.x = 0.0f; + accel.y = 0.0f; + accel.z = 0.0f; + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 25.0f, + this->actor.params); + } + } + } + } + + if (this->timers[1] < 20) { + Math_ApproachF(&this->flameAlpha, 0, 1.0f, 20.0f); + Math_ApproachF(&this->spawnPortalAlpha, 0, 1.0f, 30.0f); + } else { + Math_ApproachF(&this->flameAlpha, 255.0f, 1.0f, 10.0f); + if (this->actor.params == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_MS_FIRE - SFX_FLAG); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_MS_FREEZE - SFX_FLAG); + } + } + + this->flameRotation += this->updateRate1 * 0.0025f; + Math_ApproachF(&this->spawnPortalScale, 0.0f, 0.1f, this->updateRate1); + Math_ApproachF(&this->updateRate1, 50.0f, 1.0f, 2.0f); + } + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_009398, 0.0f); + this->workf[ANIM_SW_TGT] = 10000.0f; + } + + if (this->timers[1] == 1) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_003614, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_003614); + this->unk_4DC = 0.0f; + this->spawnPortalAlpha = 0.0f; + this->flameAlpha = 0.0f; + sBeamDivertTimer = 0; + } + } else { + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_003E34, 0.0f); + this->workf[ANIM_SW_TGT] = 10000.0f; + } + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT] - 5.0f)) { + this->beamShootState = 0; + sEnvType = this->actor.params + 1; + } + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT] - 13.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_THROW_MASIC); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_VOICE); + } + + xDiff = this->targetPos.x - this->beamOrigin.x; + yDiff = this->targetPos.y - this->beamOrigin.y; + zDiff = this->targetPos.z - this->beamOrigin.z; + + this->beamYaw = Math_FAtan2F(xDiff, zDiff); + this->beamPitch = -Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff))); + + switch (this->beamShootState) { + case -1: + break; + case 0: + if (this->timers[0] != 0) { + s32 beamReflection = BossTw_CheckBeamReflection(this, globalCtx); + + if (beamReflection == 1) { + Vec3f pos; + Vec3f velocity; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + velocity.x = Rand_CenteredFloat(15.0f); + velocity.y = Rand_CenteredFloat(15.0f); + velocity.z = Rand_CenteredFloat(15.0f); + pos = player->bodyPartsPos[15]; + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 5, + this->actor.params, 150); + } + + this->beamShootState = 1; + func_80078914(&player->actor.projectedPos, NA_SE_IT_SHIELD_REFLECT_MG); + Matrix_MtxFToYXZRotS(&player->shieldMf, &sp128, 0); + sp128.y += 0x8000; + sp128.x = -sp128.x; + this->magicDir.x = sp128.x; + this->magicDir.y = sp128.y; + this->groundBlastPos.x = 0.0f; + this->groundBlastPos.y = 0.0f; + this->groundBlastPos.z = 0.0f; + globalCtx->envCtx.unk_D8 = 1.0f; + func_800AA000(0.0f, 0x64, 5, 4); + } else if (beamReflection == 0) { + BossTw_BeamHitPlayerCheck(this, globalCtx); + + if (this->csState1 == 0) { + Math_ApproachF(&this->beamDist, 2.0f * sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)), 1.0f, + 40.0f); + } + } + } + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->beamReflectionOrigin, + &this->unk_54C, &this->actor.projectedW); + + if (this->actor.params == 1) { + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FIRE - SFX_FLAG, &this->unk_54C, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG, &this->unk_54C, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + break; + + case 1: + if (CHECK_BTN_ALL(input->cur.button, BTN_R)) { + Player* player = GET_PLAYER(globalCtx); + + this->beamDist = sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)); + Math_ApproachF(&this->beamReflectionDist, 2000.0f, 1.0f, 40.0f); + Math_ApproachF(&this->targetPos.x, player->bodyPartsPos[15].x, 1.0f, 400.0f); + Math_ApproachF(&this->targetPos.y, player->bodyPartsPos[15].y, 1.0f, 400.0f); + Math_ApproachF(&this->targetPos.z, player->bodyPartsPos[15].z, 1.0f, 400.0f); + if ((this->work[CS_TIMER_1] % 4) == 0) { + BossTw_AddRingEffect(globalCtx, &player->bodyPartsPos[15], 0.5f, 3.0f, 0xFF, this->actor.params, + 1, 150); + } + } else { + this->beamShootState = 0; + this->beamReflectionDist = 0.0f; + } + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->unk_530, &this->unk_558, + &this->actor.projectedW); + + if (this->actor.params == 1) { + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FIRE - SFX_FLAG, &this->unk_558, 4U, &D_801333E0, + &D_801333E0, &D_801333E8); + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_REFL_FIRE - SFX_FLAG, &this->unk_558, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG, &this->unk_558, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_REFL_FREEZE - SFX_FLAG, &this->unk_558, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + break; + } + + if (this->timers[0] == 0 && (sEnvType == 1 || sEnvType == 2)) { + sEnvType = 0; + } + + if (this->timers[0] == 0) { + Math_ApproachF(&this->beamScale, 0.0f, 1.0f, 0.0005f); + + if (this->beamScale == 0.0f) { + BossTw_SetupFinishBeamShoot(this, globalCtx); + this->beamReflectionDist = 0.0f; + this->beamDist = 0.0f; + } + } + } + + Matrix_Translate(this->beamOrigin.x, this->beamOrigin.y, this->beamOrigin.z, MTXMODE_NEW); + Matrix_RotateY(this->beamYaw, MTXMODE_APPLY); + Matrix_RotateX(this->beamPitch, MTXMODE_APPLY); + + sp130.x = 0.0f; + sp130.y = 0.0f; + sp130.z = this->beamDist + -5.0f; + + Matrix_MultVec3f(&sp130, &this->beamReflectionOrigin); + + if ((this->csState1 == 0) && (this->beamShootState == 0) && (this->timers[0] != 0)) { + this->groundBlastPos.y = BossTw_GetFloorY(&this->beamReflectionOrigin); + + if (this->groundBlastPos.y >= 0.0f) { + this->csState1 = 1; + this->groundBlastPos.x = this->beamReflectionOrigin.x; + this->groundBlastPos.z = this->beamReflectionOrigin.z; + BossTw_SpawnGroundBlast(this, globalCtx, this->actor.params); + this->timers[0] = 20; + } + } + + if (this->beamShootState == 1) { + if (this->csState1 == 0) { + Matrix_MtxFToYXZRotS(&player->shieldMf, &sp128, 0); + sp128.y += 0x8000; + sp128.x = -sp128.x; + Math_ApproachS(&this->magicDir.x, sp128.x, 5, 0x2000); + Math_ApproachS(&this->magicDir.y, sp128.y, 5, 0x2000); + this->beamReflectionPitch = (this->magicDir.x / 32768.0f) * M_PI; + this->beamReflectionYaw = (this->magicDir.y / 32768.0f) * M_PI; + } + + Matrix_Translate(this->beamReflectionOrigin.x, this->beamReflectionOrigin.y, this->beamReflectionOrigin.z, + MTXMODE_NEW); + Matrix_RotateY(this->beamReflectionYaw, MTXMODE_APPLY); + Matrix_RotateX(this->beamReflectionPitch, MTXMODE_APPLY); + + sp130.x = 0.0f; + sp130.y = 0.0f; + sp130.z = this->beamReflectionDist + -170.0f; + + Matrix_MultVec3f(&sp130, &this->unk_530); + + if (this->csState1 == 0) { + sp130.z = 0.0f; + + for (i = 0; i < 200; i++) { + Vec3f spBC; + + Matrix_MultVec3f(&sp130, &spBC); + floorY = BossTw_GetFloorY(&spBC); + this->groundBlastPos.y = floorY; + + if (floorY >= 0.0f) { + if ((this->groundBlastPos.y != 35.0f) && (0.0f < this->beamReflectionPitch) && + (this->timers[0] != 0)) { + this->csState1 = 1; + this->groundBlastPos.x = spBC.x; + this->groundBlastPos.z = spBC.z; + BossTw_SpawnGroundBlast(this, globalCtx, this->actor.params); + this->timers[0] = 20; + } else { + for (i = 0; i < 5; i++) { + Vec3f velocity; + Vec3f accel; + + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + + accel.x = 0.0f; + accel.y = 0.0f; + accel.z = 0.0f; + + BossTw_AddFlameEffect(globalCtx, &this->unk_530, &velocity, &accel, + Rand_ZeroFloat(10.0f) + 25.0f, this->actor.params); + } + + this->beamReflectionDist = sp130.z; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.8f, 1.0f, 0.2f); + } + break; + } + + sp130.z += 20.0f; + + if (this->beamReflectionDist < sp130.z) { + break; + } + } + } + + if (BossTw_BeamReflHitCheck(this, &this->actor.world.pos) && (this->work[CS_TIMER_1] % 4) == 0) { + BossTw_AddRingEffect(globalCtx, &this->unk_530, 0.5f, 3.0f, 255, this->actor.params, 1, 150); + } + + if (BossTw_BeamReflHitCheck(this, &otherTw->actor.world.pos) && otherTw->actionFunc != BossTw_HitByBeam) { + for (i = 0; i < 50; i++) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + pos.x = otherTw->actor.world.pos.x + Rand_CenteredFloat(50.0f); + pos.y = otherTw->actor.world.pos.y + Rand_CenteredFloat(50.0f); + pos.z = otherTw->actor.world.pos.z + Rand_CenteredFloat(50.0f); + + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + + accel.x = 0.0f; + accel.y = 0.0f; + accel.z = 0.0f; + + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 25.0f, + this->actor.params); + } + + BossTw_SetupHitByBeam(otherTw, globalCtx); + Audio_PlayActorSound2(&otherTw->actor, NA_SE_EN_TWINROBA_DAMAGE_VOICE); + globalCtx->envCtx.unk_D8 = 1.0f; + otherTw->actor.colChkInfo.health++; + } + } +} + +void BossTw_SetupFinishBeamShoot(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_FinishBeamShoot; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_004548, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_004548); +} + +void BossTw_FinishBeamShoot(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->scepterAlpha, 0.0f, 1.0f, 10.0f); + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + if (sTwinrovaPtr->timers[2] == 0) { + BossTw_SetupFlyTo(this, globalCtx); + } else { + BossTw_SetupLaugh(this, globalCtx); + } + + this->scepterAlpha = 0.0f; + } +} + +void BossTw_SetupHitByBeam(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_HitByBeam; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_00578C, 0.0f); + this->timers[0] = 53; + this->actor.speedXZ = 0.0f; + + if (this->actor.params == 0) { + this->work[FOG_TIMER] = 20; + } +} + +void BossTw_HitByBeam(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((this->work[CS_TIMER_1] % 4) == 0) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(80.0f); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(80.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(80.0f); + + velocity.x = 0.0f; + velocity.y = 0.0f; + velocity.z = 0.0f; + + accel.x = 0.0f; + accel.y = 0.1f; + accel.z = 0.0f; + + BossTw_AddDmgCloud(globalCtx, this->actor.params + 2, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 15.0f, 0, + 0, 150); + } + + if (this->actor.params == 1) { + Math_ApproachF(&this->fogR, 255.0f, 1.0f, 30.0f); + Math_ApproachF(&this->fogG, 255.0f, 1.0f, 30.0f); + Math_ApproachF(&this->fogB, 255.0f, 1.0f, 30.0f); + Math_ApproachF(&this->fogNear, 900.0f, 1.0f, 30.0f); + Math_ApproachF(&this->fogFar, 1099.0f, 1.0f, 30.0f); + } + + Math_ApproachF(&this->actor.world.pos.y, ((Math_SinS(this->work[CS_TIMER_1] * 1500) * 20.0f) + 350.0f) + 50.0f, + 0.1f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 5.0f, 1.0f, 1.0f); + + this->actor.world.pos.y -= 50.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4); + this->actor.world.pos.y += 50.0f; + + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = 0.0f; + } + + if (this->timers[0] == 1) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_006530, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_006530); + } + + if ((this->timers[0] == 0) && Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + BossTw_SetupFlyTo(this, globalCtx); + } +} + +void BossTw_SetupLaugh(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_Laugh; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0088C8, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0088C8); + this->actor.speedXZ = 0.0f; +} + +void BossTw_Laugh(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 10.0f)) { + if (this->actor.params == TW_KOUME) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_LAUGH); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_LAUGH2); + } + } + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + BossTw_SetupFlyTo(this, globalCtx); + } +} + +void BossTw_SetupSpin(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_Spin; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_007CA8, -3.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_007CA8); + this->actor.speedXZ = 0.0f; + SkelAnime_Update(&this->skelAnime); + this->timers[0] = 20; +} + +void BossTw_Spin(BossTw* this, GlobalContext* globalCtx) { + if (this->timers[0] != 0) { + this->collider.base.colType = COLTYPE_METAL; + this->actor.shape.rot.y -= 0x3000; + + if ((this->timers[0] % 4) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_ROLL); + } + } else { + SkelAnime_Update(&this->skelAnime); + Math_ApproachS(&this->actor.shape.rot.y, this->actor.world.rot.y, 3, 0x2000); + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + BossTw_SetupFlyTo(this, globalCtx); + } + } +} + +void BossTw_SetupMergeCS(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_MergeCS; + this->rotateSpeed = 0.0f; + this->actor.speedXZ = 0.0f; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_006F28, -10.0f); +} + +void BossTw_MergeCS(BossTw* this, GlobalContext* globalCtx) { + Math_ApproachF(&this->scepterAlpha, 0.0f, 1.0f, 10.0f); + SkelAnime_Update(&this->skelAnime); +} + +void BossTw_SetupWait(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_Wait; + this->visible = false; + this->actor.world.pos.y = -2000.0f; + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void BossTw_Wait(BossTw* this, GlobalContext* globalCtx) { + if ((this->actor.params == TW_TWINROVA) && (sKoumePtr->actionFunc == BossTw_FlyTo) && + (sKotakePtr->actionFunc == BossTw_FlyTo) && + ((sKoumePtr->actor.colChkInfo.health + sKotakePtr->actor.colChkInfo.health) >= 4)) { + + BossTw_TwinrovaSetupMergeCS(this, globalCtx); + BossTw_SetupMergeCS(sKotakePtr, globalCtx); + BossTw_SetupMergeCS(sKoumePtr, globalCtx); + } +} + +void BossTw_TwinrovaSetupMergeCS(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaMergeCS; + this->csState2 = 0; + this->csState1 = 0; +} + +void BossTw_TwinrovaMergeCS(BossTw* this, GlobalContext* globalCtx) { + s16 i; + Vec3f spB0; + Vec3f spA4; + Player* player = GET_PLAYER(globalCtx); + + switch (this->csState2) { + case 0: + this->csState2 = 1; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 0x39); + this->subCamId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE); + this->subCamDist = 800.0f; + this->subCamYaw = M_PI; + sKoumePtr->actor.world.rot.x = 0; + sKoumePtr->actor.shape.rot.x = 0; + sKotakePtr->actor.world.rot.x = 0; + sKotakePtr->actor.shape.rot.x = 0; + this->workf[UNK_F9] = 0.0f; + this->workf[UNK_F10] = 0.0f; + this->workf[UNK_F11] = 600.0f; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xC800FF); + this->work[CS_TIMER_2] = 0; + // fallthrough + case 1: + if (this->work[CS_TIMER_2] == 20) { + Message_StartTextbox(globalCtx, 0x6059, NULL); + } + + if (this->work[CS_TIMER_2] == 80) { + Message_StartTextbox(globalCtx, 0x605A, NULL); + } + + this->subCamAt.x = 0.0f; + this->subCamAt.y = 440.0f; + this->subCamAt.z = 0.0f; + + spB0.x = 0.0f; + spB0.y = 0.0f; + spB0.z = this->subCamDist; + + Matrix_RotateY(this->subCamYaw, MTXMODE_NEW); + Matrix_MultVec3f(&spB0, &spA4); + + this->subCamEye.x = spA4.x; + this->subCamEye.y = 300.0f; + this->subCamEye.z = spA4.z; + + Math_ApproachF(&this->subCamYaw, 0.3f, 0.02f, 0.03f); + Math_ApproachF(&this->subCamDist, 200.0f, 0.1f, 5.0f); + break; + + case 2: + spB0.x = 0.0f; + spB0.y = 0.0f; + spB0.z = this->subCamDist; + Matrix_RotateY(this->subCamYaw, MTXMODE_NEW); + Matrix_MultVec3f(&spB0, &spA4); + this->subCamEye.x = spA4.x; + this->subCamEye.z = spA4.z; + Math_ApproachF(&this->subCamEye.y, 420.0f, 0.1f, this->subCamUpdateRate * 20.0f); + Math_ApproachF(&this->subCamAt.y, 470.0f, 0.1f, this->subCamUpdateRate * 6.0f); + Math_ApproachF(&this->subCamYaw, 0.3f, 0.02f, 0.03f); + Math_ApproachF(&this->subCamDist, 60.0f, 0.1f, this->subCamUpdateRate * 32.0f); + Math_ApproachF(&this->subCamUpdateRate, 1, 1, 0.1f); + break; + } + + if (this->subCamId != 0) { + if (this->unk_5F9 == 0) { + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye); + } else { + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt2, &this->subCamEye2); + } + } + + switch (this->csState1) { + case 0: + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG); + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG); + spB0.x = this->workf[UNK_F11]; + spB0.y = 400.0f; + spB0.z = 0.0f; + Matrix_RotateY(this->workf[UNK_F9], MTXMODE_NEW); + Matrix_MultVec3f(&spB0, &spA4); + sKoumePtr->actor.world.pos.x = spA4.x; + sKoumePtr->actor.world.pos.y = spA4.y; + sKoumePtr->actor.world.pos.z = spA4.z; + sKoumePtr->actor.shape.rot.y = (this->workf[UNK_F9] / M_PI) * 32768.0f; + sKotakePtr->actor.world.pos.x = -spA4.x; + sKotakePtr->actor.world.pos.y = spA4.y; + sKotakePtr->actor.world.pos.z = -spA4.z; + sKotakePtr->actor.shape.rot.y = ((this->workf[UNK_F9] / M_PI) * 32768.0f) + 32768.0f; + Math_ApproachF(&this->workf[UNK_F11], 0.0f, 0.1f, 7.0f); + this->workf[UNK_F9] -= this->workf[UNK_F10]; + Math_ApproachF(&this->workf[UNK_F10], 0.5f, 1, 0.0039999997f); + if (this->workf[UNK_F11] < 10.0f) { + if (!this->work[PLAYED_CHRG_SFX]) { + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_POWERUP); + this->work[PLAYED_CHRG_SFX] = true; + } + + Math_ApproachF(&sKoumePtr->actor.scale.x, 0.005000001f, 1, 0.0003750001f); + + for (i = 0; i < 4; i++) { + Vec3f pos; + f32 yOffset; + f32 xScale; + + xScale = sKoumePtr->actor.scale.x * 3000.0f; + yOffset = Rand_CenteredFloat(xScale * 2.0f); + pos.x = 3000.0f; + pos.y = 400.0f + yOffset; + pos.z = 0.0f; + BossTw_AddMergeFlameEffect(globalCtx, &pos, Rand_ZeroFloat(5.0f) + 10.0f, + sqrtf(SQ(xScale) - SQ(yOffset)), Rand_ZeroFloat(1.99f)); + } + + if (sKoumePtr->actor.scale.x <= 0.0051f) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + this->actor.world.pos.y = 400.0f; + + for (i = 0; i < 50; i++) { + pos = this->actor.world.pos; + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + pos.x += velocity.x; + pos.y += velocity.y; + pos.z += velocity.z; + accel.z = accel.y = accel.x = 0.0f; + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(10.0f) + 25.0f, + velocity.x < 0.0f); + } + + this->csState1 = 1; + this->visible = true; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.shape.rot.y = 0; + BossTw_SetupWait(sKotakePtr, globalCtx); + BossTw_SetupWait(sKoumePtr, globalCtx); + Actor_SetScale(&this->actor, 0.0f); + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_038E2C, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_038E2C); + this->timers[0] = 50; + func_8002DF54(globalCtx, &this->actor, 2); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_TRANSFORM); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } + } + + sKotakePtr->actor.scale.x = sKotakePtr->actor.scale.y = sKotakePtr->actor.scale.z = + sKoumePtr->actor.scale.y = sKoumePtr->actor.scale.z = sKoumePtr->actor.scale.x; + break; + + case 1: + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -15.0f); + } + + sEnvType = -1; + globalCtx->envCtx.unk_BD = 4; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1, 1, 0.1f); + // fallthrough + case 2: + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.scale.x, 0.0069999993f, 1, 0.0006999999f); + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + + if (this->timers[0] == 1) { + this->csState2 = 2; + this->subCamUpdateRate = 0.0f; + this->timers[1] = 65; + this->timers[2] = 90; + this->timers[3] = 50; + player->actor.world.pos.x = 0.0f; + player->actor.world.pos.y = 240.0f; + player->actor.world.pos.z = 270.0f; + player->actor.world.rot.y = player->actor.shape.rot.y = -0x8000; + this->subCamEye2.x = 0.0f; + this->subCamEye2.y = 290.0f; + this->subCamEye2.z = 222.0f; + this->subCamAt2.x = player->actor.world.pos.x; + this->subCamAt2.y = player->actor.world.pos.y + 54.0f; + this->subCamAt2.z = player->actor.world.pos.z; + } + + if (this->timers[3] == 19) { + func_8002DF54(globalCtx, &this->actor, 5); + } + + if (this->timers[3] == 16) { + func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_SURPRISE); + } + + if ((this->timers[3] != 0) && (this->timers[3] < 20)) { + this->unk_5F9 = 1; + Math_ApproachF(&this->subCamEye2.z, 242.0f, 0.2f, 100.0f); + } else { + this->unk_5F9 = 0; + } + + if (this->timers[1] == 8) { + this->work[TW_BLINK_IDX] = 8; + func_80078884(NA_SE_EN_TWINROBA_YOUNG_WINK); + } + if (this->timers[2] == 4) { + sEnvType = 0; + globalCtx->envCtx.unk_BE = 5; + } + + if (this->timers[2] == 1) { + Camera* cam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + cam->eye = this->subCamEye; + cam->eyeNext = this->subCamEye; + cam->at = this->subCamAt; + func_800C08AC(globalCtx, this->subCamId, 0); + this->subCamId = 0; + this->csState2 = this->subCamId; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->work[TW_PLLR_IDX] = 0; + this->targetPos = sTwinrovaPillarPos[0]; + BossTw_TwinrovaSetupFly(this, globalCtx); + } + break; + } +} + +void BossTw_SetupDeathCS(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_DeathCS; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_0004A4, -3.0f); + this->unk_5F8 = 0; + this->work[CS_TIMER_2] = Rand_ZeroFloat(20.0f); +} + +void BossTw_DeathCS(BossTw* this, GlobalContext* globalCtx) { + if (this->timers[0] == 0) { + SkelAnime_Update(&this->skelAnime); + } + + Math_ApproachS(&this->actor.shape.rot.y, this->work[YAW_TGT], 5, this->rotateSpeed); + Math_ApproachF(&this->rotateSpeed, 20480.0f, 1.0f, 1000.0f); + + if (sTwinrovaPtr->work[CS_TIMER_2] > 140) { + Math_ApproachF(&this->fogR, 100.0f, 1.0f, 15.0f); + Math_ApproachF(&this->fogG, 255.0f, 1.0f, 15.0f); + Math_ApproachF(&this->fogB, 255.0f, 1.0f, 15.0f); + Math_ApproachF(&this->fogNear, 850.0f, 1.0f, 15.0f); + Math_ApproachF(&this->fogFar, 1099.0f, 1.0f, 15.0f); + } +} + +void BossTw_SetupCSWait(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_CSWait; + this->visible = false; + this->actor.world.pos.y = -2000.0f; + this->actor.flags &= ~ACTOR_FLAG_0; +} + +/** + * Do nothing while waiting for the inital cutscene to start + */ +void BossTw_CSWait(BossTw* this, GlobalContext* globalCtx) { +} + +void BossTw_TwinrovaSetupIntroCS(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaIntroCS; + this->visible = false; + this->actor.world.pos.y = -2000.0f; + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void BossTw_TwinrovaIntroCS(BossTw* this, GlobalContext* globalCtx) { + u8 updateCam = 0; + s16 i; + Vec3f sp90; + Vec3f sp84; + Player* player = GET_PLAYER(globalCtx); + + if (this->csSfxTimer > 220 && this->csSfxTimer < 630) { + func_80078884(NA_SE_EN_TWINROBA_UNARI - SFX_FLAG); + } + + if (this->csSfxTimer == 180) { + func_80078914(&D_8094A7D0, NA_SE_EN_TWINROBA_LAUGH); + func_80078914(&D_8094A7D0, NA_SE_EN_TWINROBA_LAUGH2); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KOTAKE_KOUME); + } + + this->csSfxTimer++; + + switch (this->csState2) { + case 0: + this->csSfxTimer = 0; + + if (SQ(player->actor.world.pos.x) + SQ(player->actor.world.pos.z) < SQ(150.0f)) { + player->actor.world.pos.x = player->actor.world.pos.z = .0f; + this->csState2 = 1; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 0x39); + this->subCamId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE); + this->subCamEye.x = 0.0f; + this->subCamEye.y = 350; + this->subCamEye.z = 200; + + this->subCamEyeTarget.x = 450; + this->subCamEyeTarget.y = 900; + + this->subCamAt.x = 0; + this->subCamAt.y = 270; + this->subCamAt.z = 0; + + this->subCamAtTarget.x = 0; + this->subCamAtTarget.y = 240; + this->subCamAtTarget.z = 140; + + this->subCamEyeTarget.z = 530; + this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x); + this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y); + this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z); + this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x); + this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y); + this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z); + + this->subCamDistStep = 0.05f; + this->work[CS_TIMER_1] = 0; + } + break; + + case 1: + updateCam = 1; + + if (this->work[CS_TIMER_1] == 30) { + Message_StartTextbox(globalCtx, 0x6048, NULL); + } + + Math_ApproachF(&this->subCamUpdateRate, 0.01f, 1.0f, 0.0001f); + + if (this->work[CS_TIMER_1] > 100) { + globalCtx->envCtx.unk_BD = 0; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.03f); + } + + if (this->work[CS_TIMER_1] == 180) { + func_80078884(NA_SE_EN_TWINROBA_APPEAR_MS); + } + + if (this->work[CS_TIMER_1] > 180) { + this->spawnPortalScale = 0.05f; + Math_ApproachF(&this->spawnPortalAlpha, 255.0f, 1.0f, 5.f); + + if (this->work[CS_TIMER_1] >= 236) { + this->csState2 = 2; + sKoumePtr->visible = 1; + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_0004A4, 0.0f); + sKoumePtr->actor.world.pos.x = 0.0f; + sKoumePtr->actor.world.pos.y = 80.0f; + sKoumePtr->actor.world.pos.z = 600.0f; + sKoumePtr->actor.shape.rot.y = sKoumePtr->actor.world.rot.y = -0x8000; + + this->subCamEye.x = -30; + this->subCamEye.y = 260; + this->subCamEye.z = 470; + + this->subCamAt.x = 0.0F; + this->subCamAt.y = 270; + this->subCamAt.z = 600.0F; + + this->work[CS_TIMER_1] = 0; + + Actor_SetScale(&sKoumePtr->actor, 0.014999999f); + } + } + break; + + case 2: + SkelAnime_Update(&sKoumePtr->skelAnime); + Math_ApproachF(&sKoumePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f); + this->subCamEye.x -= 0.2f; + this->subCamEye.z += 0.2f; + + if (this->work[CS_TIMER_1] > 50) { + this->csState2 = 3; + + this->subCamEyeTarget.x = -30; + this->subCamEyeTarget.y = 260; + this->subCamEyeTarget.z = 530; + + this->subCamAtTarget.x = 0.0f; + this->subCamAtTarget.y = 265; + this->subCamAtTarget.z = 580; + + this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x); + this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y); + this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z); + this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x); + this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y); + this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z); + this->subCamUpdateRate = 0; + this->subCamDistStep = 0.1f; + this->work[CS_TIMER_1] = 0; + } + break; + + case 3: + SkelAnime_Update(&sKoumePtr->skelAnime); + updateCam = 1; + Math_ApproachF(&sKoumePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f); + Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f); + + if (this->work[CS_TIMER_1] == 30) { + Message_StartTextbox(globalCtx, 0x6049, NULL); + } + + if (this->work[CS_TIMER_1] > 80) { + this->csState2 = 4; + this->actor.speedXZ = 0; + + this->subCamEyeTarget.x = -80.0f; + this->subCamEyeTarget.y = 260.0f; + this->subCamEyeTarget.z = 430.0f; + + this->subCamAtTarget.x = sKoumePtr->actor.world.pos.x; + this->subCamAtTarget.y = sKoumePtr->actor.world.pos.y + 20.0f; + this->subCamAtTarget.z = sKoumePtr->actor.world.pos.z; + + this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x); + this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y); + this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z); + this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x); + this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y); + this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z); + this->subCamUpdateRate = 0.0f; + this->subCamDistStep = 0.05f; + Animation_MorphToPlayOnce(&sKoumePtr->skelAnime, &object_tw_Anim_000AAC, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_000AAC); + this->work[CS_TIMER_1] = 0; + } + break; + + case 4: + updateCam = 1; + SkelAnime_Update(&sKoumePtr->skelAnime); + this->subCamAtTarget.y = 20.0f + sKoumePtr->actor.world.pos.y; + Math_ApproachF(&sKoumePtr->actor.world.pos.y, 350, 0.1f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 9.0f, 1.0f, 0.9f); + Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f); + + if (this->work[CS_TIMER_1] >= 30) { + if (this->work[CS_TIMER_1] < 45) { + globalCtx->envCtx.unk_BE = 0; + globalCtx->envCtx.unk_BD = 2; + globalCtx->envCtx.unk_D8 = 1.0f; + } else { + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f); + } + + if (this->work[CS_TIMER_1] == 30) { + for (i = 0; i < 50; i++) { + Vec3f pos; + Vec3f velocity; + + pos.x = sKoumePtr->actor.world.pos.x + Rand_CenteredFloat(50.0f); + pos.y = sKoumePtr->actor.world.pos.y + Rand_CenteredFloat(50.0f); + pos.z = sKoumePtr->actor.world.pos.z + Rand_CenteredFloat(50.0f); + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &sZeroVector, Rand_ZeroFloat(10.0f) + 25.0f, + 1); + } + + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_TRANSFORM); + globalCtx->envCtx.unk_D8 = 0; + } + + if (this->work[CS_TIMER_1] >= 35) { + if (this->work[CS_TIMER_1] < 50) { + Math_ApproachF(&sKoumePtr->actor.scale.x, + ((Math_SinS(this->work[CS_TIMER_1] * 0x4200) * 20.0f) / 10000.0f) + 0.024999999f, + 1.0f, 0.005f); + } else { + if (this->work[CS_TIMER_1] == 50) { + Animation_MorphToPlayOnce(&sKoumePtr->skelAnime, &object_tw_Anim_0088C8, -5); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0088C8); + } + + if (this->work[CS_TIMER_1] == 60) { + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_LAUGH); + } + + if (Animation_OnFrame(&sKoumePtr->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_006F28, 0.f); + this->workf[ANIM_SW_TGT] = 1000.0f; + } + + Math_ApproachF(&sKoumePtr->actor.scale.x, 0.024999999f, 0.1f, 0.005f); + } + + Actor_SetScale(&sKoumePtr->actor, sKoumePtr->actor.scale.x); + sKoumePtr->actor.shape.rot.y = -0x8000; + sKoumePtr->unk_5F8 = 1; + + if (this->work[CS_TIMER_1] == 0x64) { + this->csState2 = 10; + this->work[CS_TIMER_1] = 0; + this->subCamYawStep = 0.0f; + sKotakePtr->visible = 1; + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_0004A4, 0.0f); + sKotakePtr->actor.world.pos.x = 0.0f; + sKotakePtr->actor.world.pos.y = 80.0f; + sKotakePtr->actor.world.pos.z = -600.0f; + sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.world.rot.y = 0; + this->work[CS_TIMER_1] = 0; + + this->subCamEye.x = -30.0f; + this->subCamEye.y = 260.0f; + this->subCamEye.z = -470.0f; + + this->subCamAt.x = 0; + this->subCamAt.y = 270.0f; + this->subCamAt.z = -600.0f; + Actor_SetScale(&sKotakePtr->actor, 0.014999999f); + } + } else { + sKoumePtr->actor.shape.rot.y = sKoumePtr->actor.shape.rot.y + (s16)this->subCamYawStep; + } + } else { + if ((this->work[CS_TIMER_1] % 8) == 0) { + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_ROLL); + } + + sKoumePtr->actor.shape.rot.y = sKoumePtr->actor.shape.rot.y + (s16)this->subCamYawStep; + Math_ApproachF(&this->subCamYawStep, 12288.0f, 1.0f, 384.0f); + + if (Animation_OnFrame(&sKoumePtr->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_006F28, 0.0f); + this->workf[ANIM_SW_TGT] = 1000.0f; + } + } + break; + + case 10: + SkelAnime_Update(&sKotakePtr->skelAnime); + Math_ApproachF(&sKotakePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f); + this->subCamEye.x -= 0.2f; + this->subCamEye.z -= 0.2f; + + if (this->work[CS_TIMER_1] >= 0x33) { + this->csState2 = 11; + this->subCamEyeTarget.x = -30; + this->subCamEyeTarget.y = 260; + this->subCamEyeTarget.z = -530; + this->subCamAtTarget.x = 0; + this->subCamAtTarget.y = 265; + this->subCamAtTarget.z = -580; + this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x); + this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y); + this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z); + this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x); + this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y); + this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z); + this->subCamUpdateRate = 0; + this->subCamDistStep = 0.1f; + this->work[CS_TIMER_1] = 0; + } + break; + + case 11: + SkelAnime_Update(&sKotakePtr->skelAnime); + updateCam = 1; + Math_ApproachF(&sKotakePtr->actor.world.pos.y, 240.0f, 0.05f, 5.0f); + Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f); + + if (this->work[CS_TIMER_1] == 30) { + Message_StartTextbox(globalCtx, 0x604A, NULL); + } + + if (this->work[CS_TIMER_1] > 80) { + this->csState2 = 12; + this->actor.speedXZ = 0; + + this->subCamEyeTarget.y = 260.0f; + this->subCamEyeTarget.x = -80.0f; + this->subCamEyeTarget.z = -430.0f; + + this->subCamAtTarget.x = sKotakePtr->actor.world.pos.x; + this->subCamAtTarget.y = sKotakePtr->actor.world.pos.y + 20.0f; + this->subCamAtTarget.z = sKotakePtr->actor.world.pos.z; + + this->subCamEyeStep.x = fabsf(this->subCamEyeTarget.x - this->subCamEye.x); + this->subCamEyeStep.y = fabsf(this->subCamEyeTarget.y - this->subCamEye.y); + this->subCamEyeStep.z = fabsf(this->subCamEyeTarget.z - this->subCamEye.z); + this->subCamAtStep.x = fabsf(this->subCamAtTarget.x - this->subCamAt.x); + this->subCamAtStep.y = fabsf(this->subCamAtTarget.y - this->subCamAt.y); + this->subCamAtStep.z = fabsf(this->subCamAtTarget.z - this->subCamAt.z); + this->subCamUpdateRate = 0; + this->subCamDistStep = 0.05f; + Animation_MorphToPlayOnce(&sKotakePtr->skelAnime, &object_tw_Anim_000AAC, 0); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_000AAC); + this->work[CS_TIMER_1] = 0; + } + break; + + case 12: + updateCam = 1; + SkelAnime_Update(&sKotakePtr->skelAnime); + this->subCamAtTarget.y = sKotakePtr->actor.world.pos.y + 20.0f; + Math_ApproachF(&sKotakePtr->actor.world.pos.y, 350, 0.1f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 9.0f, 1.0f, 0.9f); + Math_ApproachF(&this->subCamUpdateRate, 1.0f, 1.0f, 0.02f); + + if (this->work[CS_TIMER_1] >= 30) { + if (this->work[CS_TIMER_1] < 45) { + globalCtx->envCtx.unk_BD = 3; + globalCtx->envCtx.unk_D8 = 1.0f; + } else { + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f); + } + + if (this->work[CS_TIMER_1] == 30) { + for (i = 0; i < 50; i++) { + Vec3f pos; + Vec3f velocity; + pos.x = sKotakePtr->actor.world.pos.x + Rand_CenteredFloat(50.0f); + pos.y = sKotakePtr->actor.world.pos.y + Rand_CenteredFloat(50.0f); + pos.z = sKotakePtr->actor.world.pos.z + Rand_CenteredFloat(50.0f); + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &sZeroVector, Rand_ZeroFloat(10.f) + 25.0f, + 0); + } + + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_TRANSFORM); + globalCtx->envCtx.unk_D8 = 0.0f; + } + + if (this->work[CS_TIMER_1] >= 35) { + if (this->work[CS_TIMER_1] < 50) { + Math_ApproachF(&sKotakePtr->actor.scale.x, + ((Math_SinS(this->work[CS_TIMER_1] * 0x4200) * 20.0f) / 10000.0f) + 0.024999999f, + 1.0f, 0.005f); + } else { + if (this->work[CS_TIMER_1] == 50) { + Animation_MorphToPlayOnce(&sKotakePtr->skelAnime, &object_tw_Anim_0088C8, -5.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0088C8); + } + + if (this->work[CS_TIMER_1] == 60) { + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_LAUGH2); + } + + if (Animation_OnFrame(&sKotakePtr->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_006F28, 0.0f); + this->workf[ANIM_SW_TGT] = 1000.0f; + } + + Math_ApproachF(&sKotakePtr->actor.scale.x, 0.024999999f, 0.1f, 0.005f); + } + + Actor_SetScale(&sKotakePtr->actor, sKotakePtr->actor.scale.x); + sKotakePtr->actor.shape.rot.y = 0; + sKotakePtr->unk_5F8 = 1; + + if (this->work[CS_TIMER_1] == 100) { + this->csState2 = 20; + this->work[CS_TIMER_1] = 0; + + this->workf[UNK_F11] = 600.0f; + + this->subCamEye.x = 800.0f; + this->subCamEye.y = 300.0f; + this->subCamEye.z = 0; + + this->subCamAt.x = 0.0f; + this->subCamAt.y = 400.0f; + this->subCamAt.z = 0; + + this->workf[UNK_F9] = -M_PI / 2.0f; + this->workf[UNK_F10] = 0.0f; + + this->subCamEyeStep.x = 0.0f; + this->spawnPortalAlpha = 0.0f; + } + } else { + sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.shape.rot.y + (s16)this->subCamYawStep; + } + } else { + if ((this->work[CS_TIMER_1] % 8) == 0) { + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_ROLL); + } + + sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.shape.rot.y + (s16)this->subCamYawStep; + Math_ApproachF(&this->subCamYawStep, 12288.0f, 1.0f, 384.0f); + + if (Animation_OnFrame(&sKotakePtr->skelAnime, this->workf[ANIM_SW_TGT])) { + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_006F28, 0.0f); + this->workf[ANIM_SW_TGT] = 1000.0f; + } + } + break; + + case 20: + if (this->work[CS_TIMER_1] > 20 && this->work[CS_TIMER_1] < 120) { + globalCtx->envCtx.unk_BD = 1; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.015f); + } + + if (this->work[CS_TIMER_1] == 90) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x5A00FF); + } + + if (this->work[CS_TIMER_1] == 120) { + sEnvType = 0; + globalCtx->envCtx.unk_BE = 1; + globalCtx->envCtx.unk_BD = 1; + globalCtx->envCtx.unk_D8 = 0.0f; + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, SEGMENTED_TO_VIRTUAL(gTwinrovaTitleCardTex), 160, 180, 128, 40); // OTRTODO + gSaveContext.eventChkInf[7] |= 0x20; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } + + if (this->work[CS_TIMER_1] >= 160) { + if (this->work[CS_TIMER_1] == 160) { + this->subCamEyeStep.x = 0.0f; + } + Math_ApproachF(&this->subCamEye.x, 0.0f, 0.05f, this->subCamEyeStep.x * 0.5f); + Math_ApproachF(&this->subCamEye.z, 1000.0f, 0.05f, this->subCamEyeStep.x); + Math_ApproachF(&this->subCamEyeStep.x, 40.0f, 1.0f, 1); + } else { + Math_ApproachF(&this->subCamEye.x, 300.0f, 0.05f, this->subCamEyeStep.x); + Math_ApproachF(&this->subCamEyeStep.x, 5.0f, 1.0f, 0.5f); + } + + if (this->work[CS_TIMER_1] < 200) { + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG); + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG); + sp90.x = this->workf[UNK_F11]; + sp90.y = 400.0f; + sp90.z = 0.0f; + Matrix_RotateY(this->workf[UNK_F9], MTXMODE_NEW); + Matrix_MultVec3f(&sp90, &sp84); + sKoumePtr->actor.world.pos.x = sp84.x; + sKoumePtr->actor.world.pos.y = sp84.y; + sKoumePtr->actor.world.pos.z = sp84.z; + sKoumePtr->actor.world.rot.y = sKoumePtr->actor.shape.rot.y = (this->workf[UNK_F9] / M_PI) * 32768.0f; + sKotakePtr->actor.world.pos.x = -sp84.x; + sKotakePtr->actor.world.pos.y = sp84.y; + sKotakePtr->actor.world.pos.z = -sp84.z; + sKotakePtr->actor.shape.rot.y = sKotakePtr->actor.world.rot.y = + ((this->workf[UNK_F9] / M_PI) * 32768.0f) + 32768.0f; + Math_ApproachF(&this->workf[UNK_F11], 80.0f, 0.1f, 5.0f); + this->workf[UNK_F9] -= this->workf[UNK_F10]; + Math_ApproachF(&this->workf[UNK_F10], 0.19999999f, 1.0f, 0.0019999994f); + } + + if (this->work[CS_TIMER_1] == 200) { + sKoumePtr->actionFunc = BossTw_FlyTo; + sKotakePtr->actionFunc = BossTw_FlyTo; + sKoumePtr->targetPos.x = 600.0f; + sKoumePtr->targetPos.y = 400.0f; + sKoumePtr->targetPos.z = 0.0f; + sKoumePtr->timers[0] = 100; + sKotakePtr->targetPos.x = -600.0f; + sKotakePtr->targetPos.y = 400.0f; + sKotakePtr->targetPos.z = 0.0f; + sKotakePtr->timers[0] = 100; + } + + if (this->work[CS_TIMER_1] == 260) { + Camera* cam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + cam->eye = this->subCamEye; + cam->eyeNext = this->subCamEye; + cam->at = this->subCamAt; + func_800C08AC(globalCtx, this->subCamId, 0); + this->subCamId = 0; + this->csState2 = this->subCamId; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + BossTw_SetupWait(this, globalCtx); + } + break; + } + + if (this->subCamId != 0) { + if (updateCam) { + Math_ApproachF(&this->subCamEye.x, this->subCamEyeTarget.x, this->subCamDistStep, + this->subCamEyeStep.x * this->subCamUpdateRate); + Math_ApproachF(&this->subCamEye.y, this->subCamEyeTarget.y, this->subCamDistStep, + this->subCamEyeStep.y * this->subCamUpdateRate); + Math_ApproachF(&this->subCamEye.z, this->subCamEyeTarget.z, this->subCamDistStep, + this->subCamEyeStep.z * this->subCamUpdateRate); + Math_ApproachF(&this->subCamAt.x, this->subCamAtTarget.x, this->subCamDistStep, + this->subCamAtStep.x * this->subCamUpdateRate); + Math_ApproachF(&this->subCamAt.y, this->subCamAtTarget.y, this->subCamDistStep, + this->subCamAtStep.y * this->subCamUpdateRate); + Math_ApproachF(&this->subCamAt.z, this->subCamAtTarget.z, this->subCamDistStep, + this->subCamAtStep.z * this->subCamUpdateRate); + } + + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye); + } +} + +void BossTw_DeathBall(BossTw* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 yDiff; + f32 zDiff; + s32 pad; + s16 i; + s16 yaw; + + if ((this->work[CS_TIMER_1] % 16) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FB_FLY); + } + + if (sTwinrovaPtr->csState2 < 2) { + if (this->timers[0] == 0) { + this->timers[0] = 20; + this->targetPos.x = Rand_CenteredFloat(100.0f) + sTwinrovaPtr->actor.world.pos.x; + this->targetPos.y = Rand_CenteredFloat(50.0f) + 400.0f; + this->targetPos.z = Rand_CenteredFloat(100.0f) + sTwinrovaPtr->actor.world.pos.z; + } + + this->timers[1] = 10; + this->rotateSpeed = 8192.0f; + this->actor.speedXZ = 5.0f; + } else { + if (this->timers[1] == 9) { + this->targetPos.y = 413.0f; + this->actor.world.pos.z = 0.0f; + this->actor.world.pos.x = 0.0f; + for (i = 0; i < ARRAY_COUNT(this->blastTailPos); i++) { + this->blastTailPos[i] = this->actor.world.pos; + } + } + + if (this->actor.params == 0x69) { + this->targetPos.x = sKoumePtr->actor.world.pos.x; + this->targetPos.z = sKoumePtr->actor.world.pos.z; + } else { + this->targetPos.x = sKotakePtr->actor.world.pos.x; + this->targetPos.z = sKotakePtr->actor.world.pos.z; + } + + Math_ApproachF(&this->targetPos.y, 263.0f, 1.0f, 2.0f); + + if (this->targetPos.y == 263.0f) { + Math_ApproachF(&this->actor.speedXZ, 0.0f, 1.0f, 0.2f); + if (sTwinrovaPtr->csState2 == 3) { + Actor_Kill(&this->actor); + } + } + } + + xDiff = this->targetPos.x - this->actor.world.pos.x; + yDiff = this->targetPos.y - this->actor.world.pos.y; + zDiff = this->targetPos.z - this->actor.world.pos.z; + + yaw = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI); + Math_ApproachS(&this->actor.world.rot.x, Math_FAtan2F(yDiff, sqrtf(SQ(xDiff) + SQ(zDiff))) * (32768 / M_PI), 5, + this->rotateSpeed); + Math_ApproachS(&this->actor.world.rot.y, yaw, 5, this->rotateSpeed); + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); +} + +void BossTw_TwinrovaSetupDeathCS(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaDeathCS; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_024374, -3.0f); + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.flags &= ~ACTOR_FLAG_0; + this->csState2 = this->csState1 = 0; + this->work[CS_TIMER_1] = this->work[CS_TIMER_2] = 0; + this->work[INVINC_TIMER] = 10000; + BossTw_SetupDeathCS(sKoumePtr, globalCtx); + BossTw_SetupDeathCS(sKotakePtr, globalCtx); + sKotakePtr->timers[0] = 8; + this->workf[UNK_F19] = 1.0f; +} + +void BossTw_DeathCSMsgSfx(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + s32 pad3; + s16 msgId2; + s16 msgId1; + u8 kotakeAnim; + u8 koumeAnim; + u8 sp35; + + msgId2 = 0; + msgId1 = 0; + kotakeAnim = 0; + koumeAnim = 0; + sp35 = 0; + + if (this->work[CS_TIMER_2] == 80) { + koumeAnim = 1; + } + + if (this->work[CS_TIMER_2] == 80) { + msgId2 = 0x604B; + sp35 = 50; + } + + if (this->work[CS_TIMER_2] == 140) { + kotakeAnim = koumeAnim = 2; + } + + if (this->work[CS_TIMER_2] == 170) { + kotakeAnim = 3; + sKotakePtr->work[YAW_TGT] = -0x4000; + sKotakePtr->rotateSpeed = 0.0f; + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_SENSE); + msgId2 = 0x604C; + } + + if (this->work[CS_TIMER_2] == 210) { + D_8094C874 = 30; + } + + if (this->work[CS_TIMER_2] == 270) { + koumeAnim = 3; + sKoumePtr->work[YAW_TGT] = 0x4000; + sKoumePtr->rotateSpeed = 0.0f; + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_SENSE); + } + + if (this->work[CS_TIMER_2] == 290) { + msgId2 = 0x604D; + sp35 = 35; + } + + if (this->work[CS_TIMER_2] == 350) { + koumeAnim = kotakeAnim = 2; + sKoumePtr->work[YAW_TGT] = sKotakePtr->work[YAW_TGT] = 0; + sKoumePtr->rotateSpeed = sKotakePtr->rotateSpeed = 0.0f; + } + + if (this->work[CS_TIMER_2] == 380) { + koumeAnim = kotakeAnim = 3; + } + + if (this->work[CS_TIMER_2] == 400) { + koumeAnim = kotakeAnim = 2; + } + + if (this->work[CS_TIMER_2] == 430) { + koumeAnim = 4; + D_8094C874 = 435; + D_8094C878 = 1; + } + + if (this->work[CS_TIMER_2] > 440 && this->work[CS_TIMER_2] < 860) { + func_80078884(NA_SE_EN_TWINROBA_FIGHT - SFX_FLAG); + } + + if (this->work[CS_TIMER_2] == 430) { + msgId2 = 0x604E; + } + + if (this->work[CS_TIMER_2] == 480) { + kotakeAnim = 4; + sKotakePtr->work[YAW_TGT] = -0x4000; + } + + if (this->work[CS_TIMER_2] == 500) { + koumeAnim = 2; + } + + if (this->work[CS_TIMER_2] == 480) { + msgId1 = 0x604F; + } + + if (this->work[CS_TIMER_2] == 530) { + koumeAnim = 4; + sKoumePtr->work[YAW_TGT] = 0x4000; + D_8094C87A = 335; + D_8094C87E = 1; + } + + if (this->work[CS_TIMER_2] == 530) { + msgId2 = 0x6050; + } + + if (this->work[CS_TIMER_2] == 580) { + msgId1 = 0x6051; + } + + if (this->work[CS_TIMER_2] == 620) { + msgId2 = 0x6052; + } + + if (this->work[CS_TIMER_2] == 660) { + msgId1 = 0x6053; + } + + if (this->work[CS_TIMER_2] == 700) { + msgId2 = 0x6054; + } + + if (this->work[CS_TIMER_2] == 740) { + msgId1 = 0x6055; + } + + if (this->work[CS_TIMER_2] == 780) { + msgId2 = 0x6056; + } + + if (this->work[CS_TIMER_2] == 820) { + msgId1 = 0x6057; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x5000FF); + } + + if (this->work[CS_TIMER_2] == 860) { + koumeAnim = kotakeAnim = 3; + } + + if (this->work[CS_TIMER_2] == 900) { + Audio_PlayActorSound2(&sKoumePtr->actor, NA_SE_EN_TWINROBA_DIE); + Audio_PlayActorSound2(&sKotakePtr->actor, NA_SE_EN_TWINROBA_DIE); + } + + if (this->work[CS_TIMER_2] == 930) { + msgId2 = 0x6058; + } + + if (msgId2 != 0) { + Message_StartTextbox(globalCtx, msgId2, NULL); + + if (sp35) { + D_8094C876 = 10; + D_8094C874 = sp35; + D_8094C878 = 0; + } + } + + if (msgId1 != 0) { + Message_StartTextbox(globalCtx, msgId1, NULL); + } + + switch (kotakeAnim) { + case 1: + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_00230C, -5.0f); + break; + case 2: + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_001D10, -5.0f); + break; + case 3: + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_0017E0, -5.0f); + break; + case 4: + Animation_MorphToLoop(&sKotakePtr->skelAnime, &object_tw_Anim_0012A4, -5.0f); + break; + } + + switch (koumeAnim) { + case 1: + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_00230C, -5.0f); + break; + case 2: + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_001D10, -5.0f); + break; + case 3: + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_0017E0, -5.0f); + break; + case 4: + Animation_MorphToLoop(&sKoumePtr->skelAnime, &object_tw_Anim_0012A4, -5.0f); + break; + } + + if (this->work[CS_TIMER_2] >= 120 && this->work[CS_TIMER_2] < 500) { + Math_ApproachF(&this->workf[UNK_F18], 255.0f, 0.1f, 5.0f); + } + + if (this->work[CS_TIMER_2] >= 150) { + Math_ApproachF(&sKoumePtr->workf[UNK_F17], (Math_SinS(this->work[CS_TIMER_1] * 2000) * 0.05f) + 0.4f, 0.1f, + 0.01f); + Math_ApproachF(&sKotakePtr->workf[UNK_F17], (Math_CosS(this->work[CS_TIMER_1] * 1700) * 0.05f) + 0.4f, 0.1f, + 0.01f); + + if (this->work[CS_TIMER_2] >= 880) { + Math_ApproachF(&sKotakePtr->actor.world.pos.y, 2000.0f, 1.0f, this->actor.speedXZ); + Math_ApproachF(&sKoumePtr->actor.world.pos.y, 2000.0f, 1.0f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 10.0f, 1.0f, 0.25f); + + if (this->work[CS_TIMER_2] >= 930) { + Math_ApproachF(&this->workf[UNK_F19], 5.0f, 1.0f, 0.05f); + Math_ApproachF(&this->workf[UNK_F18], 0.0f, 1.0f, 3.0f); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GOTO_HEAVEN - SFX_FLAG); + } else { + f32 yTarget = Math_CosS(this->work[CS_TIMER_2] * 1700) * 4.0f; + Math_ApproachF(&sKotakePtr->actor.world.pos.y, 20.0f + (263.0f + yTarget), 0.1f, this->actor.speedXZ); + yTarget = Math_SinS(this->work[CS_TIMER_2] * 1500) * 4.0f; + Math_ApproachF(&sKoumePtr->actor.world.pos.y, 20.0f + (263.0f + yTarget), 0.1f, this->actor.speedXZ); + Math_ApproachF(&this->actor.speedXZ, 1.0f, 1.0f, 0.05f); + } + } +} + +void BossTw_TwinrovaDeathCS(BossTw* this, GlobalContext* globalCtx) { + s16 i; + Vec3f spD0; + Player* player = GET_PLAYER(globalCtx); + Camera* mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + SkelAnime_Update(&this->skelAnime); + this->work[UNK_S8] += 20; + + if (this->work[UNK_S8] > 255) { + this->work[UNK_S8] = 255; + } + + Math_ApproachF(&this->workf[UNK_F12], 0.0f, 1.0f, 0.05f); + this->unk_5F8 = 1; + + switch (this->csState1) { + case 0: + if (this->work[CS_TIMER_1] == 15) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0216DC, -3.0f); + } + + if (this->work[CS_TIMER_1] >= 15) { + Math_ApproachF(&this->actor.world.pos.y, 400.0f, 0.05f, 10.0f); + } + + if (this->work[CS_TIMER_1] >= 55) { + if (this->work[CS_TIMER_1] == 55) { + globalCtx->envCtx.unk_D8 = 0; + } + + sEnvType = -1; + globalCtx->envCtx.unk_BE = 5; + globalCtx->envCtx.unk_BD = 0; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.015f); + Math_ApproachF(&this->actor.scale.x, 0.00024999998f, 0.1f, 0.00005f); + this->actor.shape.rot.y += (s16)this->actor.speedXZ; + this->workf[UNK_F13] += this->actor.speedXZ; + if (this->workf[UNK_F13] > 65536.0f) { + this->workf[UNK_F13] -= 65536.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_ROLL); + } + Math_ApproachF(&this->actor.speedXZ, 12288.0f, 1.0f, 256.0f); + if (this->work[CS_TIMER_1] == 135) { + Vec3f spBC; + Vec3f spB0; + Vec3f spA4 = { 0.0f, 0.0f, 0.0f }; + func_80078884(NA_SE_EN_TWINROBA_TRANSFORM); + for (i = 0; i < 100; i++) { + spB0.x = Rand_CenteredFloat(5.0f); + spB0.y = Rand_CenteredFloat(5.0f); + spB0.z = Rand_CenteredFloat(5.0f); + spBC = this->actor.world.pos; + spBC.x += spB0.x; + spBC.y += spB0.y; + spBC.z += spB0.z; + BossTw_AddFlameEffect(globalCtx, &spBC, &spB0, &spA4, Rand_ZeroFloat(2.0f) + 5, + Rand_ZeroFloat(1.99f)); + } + this->csState1 = 1; + this->visible = false; + this->actor.scale.x = 0.0f; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, + 0, TW_DEATHBALL_KOUME); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, + 0, TW_DEATHBALL_KOTAKE); + this->actor.flags &= ~ACTOR_FLAG_0; + } + } + Actor_SetScale(&this->actor, this->actor.scale.x); + break; + case 1: + break; + } + + switch (this->csState2) { + case 0: + this->csState2 = 1; + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->subCamId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE); + this->subCamEye = mainCam->eye; + this->subCamAt = mainCam->at; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + break; + case 1: + spD0.x = Math_SinS(this->actor.world.rot.y) * 200.0f; + spD0.z = Math_CosS(this->actor.world.rot.y) * 200.0f; + Math_ApproachF(&this->subCamEye.x, spD0.x + this->actor.world.pos.x, 0.1f, 50.0f); + Math_ApproachF(&this->subCamEye.y, 300.0f, 0.1f, 50.0f); + Math_ApproachF(&this->subCamEye.z, spD0.z + this->actor.world.pos.z, 0.1f, 50.0f); + Math_ApproachF(&this->subCamAt.x, this->actor.world.pos.x, 0.1f, 50.0f); + Math_ApproachF(&this->subCamAt.y, this->actor.world.pos.y, 0.1f, 50.0f); + Math_ApproachF(&this->subCamAt.z, this->actor.world.pos.z, 0.1f, 50.0f); + if (this->work[CS_TIMER_1] == 170) { + this->csState2 = 2; + this->work[CS_TIMER_2] = 0; + this->subCamEye.z = 170.0f; + this->subCamDist = 170.0f; + this->subCamEye.x = 0.0f; + this->subCamAt.x = 0.0f; + this->subCamAt.z = 0.0f; + this->subCamEye.y = 260.0f; + player->actor.shape.rot.y = -0x8000; + player->actor.world.pos.x = -40.0f; + player->actor.world.pos.y = 240.0f; + player->actor.world.pos.z = 90.0f; + sKoumePtr->actor.world.pos.x = -37.0f; + sKotakePtr->actor.world.pos.x = 37.0f; + sKotakePtr->actor.world.pos.y = 263.0f; + sKoumePtr->actor.world.pos.y = sKotakePtr->actor.world.pos.y; + this->subCamAt.y = sKoumePtr->actor.world.pos.y + 17.0f; + sKotakePtr->actor.world.pos.z = 0.0f; + sKoumePtr->actor.world.pos.z = sKotakePtr->actor.world.pos.z; + sKoumePtr->work[YAW_TGT] = sKotakePtr->work[YAW_TGT] = sKoumePtr->actor.shape.rot.x = + sKotakePtr->actor.shape.rot.x = sKoumePtr->actor.shape.rot.y = sKotakePtr->actor.shape.rot.y = 0; + func_8002DF54(globalCtx, &sKoumePtr->actor, 1); + sKoumePtr->actor.flags |= ACTOR_FLAG_0; + } + break; + case 2: + if (this->work[CS_TIMER_2] == 100) { + Vec3f pos; + Vec3f velocity; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + s32 zero = 0; + + for (i = 0; i < 50; i++) { + velocity.x = Rand_CenteredFloat(3.0f); + velocity.y = Rand_CenteredFloat(3.0f); + velocity.z = Rand_CenteredFloat(3.0f); + pos = sKoumePtr->actor.world.pos; + pos.x += velocity.x * 2.0f; + pos.y += velocity.y * 2.0f; + pos.z += velocity.z * 2.0f; + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(2.0f) + 5, 1); + + // fake code needed to match, tricks the compiler into allocating more stack + if (1) {} + if (zero) { + accel.x *= 2.0; + } + + velocity.x = Rand_CenteredFloat(3.0f); + velocity.y = Rand_CenteredFloat(3.0f); + velocity.z = Rand_CenteredFloat(3.0f); + pos = sKotakePtr->actor.world.pos; + pos.x += velocity.x * 2.0f; + pos.y += velocity.y * 2.0f; + pos.z += velocity.z * 2.0f; + BossTw_AddFlameEffect(globalCtx, &pos, &velocity, &accel, Rand_ZeroFloat(2.0f) + 5, 0); + } + + Actor_SetScale(&sKoumePtr->actor, 0.0f); + Actor_SetScale(&sKotakePtr->actor, 0.0f); + sKoumePtr->visible = 1; + sKotakePtr->visible = 1; + func_80078884(NA_SE_EN_TWINROBA_TRANSFORM); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KOTAKE_KOUME); + this->csState2 = 3; + this->work[CS_TIMER_2] = 0; + this->subCamYaw = this->subCamYawStep = this->actor.speedXZ = this->subCamDistStep = 0.0f; + } + break; + case 3: + BossTw_DeathCSMsgSfx(this, globalCtx); + if (this->work[CS_TIMER_2] < 150) { + globalCtx->envCtx.unk_BE = 1; + globalCtx->envCtx.unk_BD = 0; + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.1f); + } else { + globalCtx->envCtx.unk_BE = 1; + globalCtx->envCtx.unk_BD = 6; + Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_SinS(this->work[CS_TIMER_2] * 4096) / 4.0f) + 0.75f, + 1.0f, 0.1f); + } + + Math_ApproachF(&this->subCamAt.y, sKoumePtr->actor.world.pos.y + 17.0f, 0.05f, 10.0f); + + if (this->work[CS_TIMER_2] >= 50) { + Math_ApproachF(&this->subCamDist, 110.0f, 0.05f, this->subCamDistStep); + Math_ApproachF(&this->subCamDistStep, 1.0f, 1.0f, 0.025f); + this->subCamEye.x = this->subCamDist * sinf(this->subCamYaw); + this->subCamEye.z = this->subCamDist * cosf(this->subCamYaw); + if (this->work[CS_TIMER_2] >= 151) { + this->subCamYaw += this->subCamYawStep; + if (this->work[CS_TIMER_2] >= 800) { + Math_ApproachF(&this->subCamYawStep, 0.0f, 1.0f, 0.0001f); + } else { + Math_ApproachF(&this->subCamYawStep, 0.015f, 1.0f, 0.0001f); + } + } + } + Math_ApproachF(&sKoumePtr->actor.scale.x, 0.009999999f, 0.1f, 0.001f); + Actor_SetScale(&sKoumePtr->actor, sKoumePtr->actor.scale.x); + Actor_SetScale(&sKotakePtr->actor, sKoumePtr->actor.scale.x); + if (this->work[CS_TIMER_2] >= 1020) { + mainCam = Gameplay_GetCamera(globalCtx, MAIN_CAM); + mainCam->eye = this->subCamEye; + mainCam->eyeNext = this->subCamEye; + mainCam->at = this->subCamAt; + func_800C08AC(globalCtx, this->subCamId, 0); + this->csState2 = 4; + this->subCamId = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, 600.0f, 230.0f, + 0.0f, 0, 0, 0, WARP_DUNGEON_ADULT); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, -600.0f, 230.f, 0.0f, 0, 0, 0, 0); + this->actor.world.pos.y = -2000.0f; + this->workf[UNK_F18] = 0.0f; + sKoumePtr->visible = sKotakePtr->visible = false; + if (&this->subCamEye) {} // fixes regalloc, may be fake + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + break; + case 4: + sEnvType = 0; + break; + } + + if (this->subCamId) { + if (1) {} + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye); + } +} + +static s16 D_8094A900[] = { + 0, 1, 2, 2, 1, +}; + +static s16 D_8094A90C[] = { + 0, 1, 2, 2, 2, 2, 2, 2, 1, +}; + +void BossTw_Update(Actor* thisx, GlobalContext* globalCtx) { + BossTw* this = (BossTw*)thisx; + Player* player = GET_PLAYER(globalCtx); + s16 i; + s32 pad; + + this->collider.base.colType = COLTYPE_HIT3; + Math_ApproachF(&this->fogR, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->fogG, globalCtx->lightCtx.fogColor[1], 1.0f, 10.0f); + Math_ApproachF(&this->fogB, globalCtx->lightCtx.fogColor[2], 1.0f, 10.0f); + Math_ApproachF(&this->fogNear, globalCtx->lightCtx.fogNear, 1.0f, 10.0f); + Math_ApproachF(&this->fogFar, 1000.0f, 1.0f, 10.0f); + this->work[CS_TIMER_1]++; + this->work[CS_TIMER_2]++; + this->work[TAIL_IDX]++; + + if (this->work[TAIL_IDX] >= ARRAY_COUNT(this->blastTailPos)) { + this->work[TAIL_IDX] = 0; + } + + this->blastTailPos[this->work[TAIL_IDX]] = this->actor.world.pos; + + if (1) {} + if (1) {} + + for (i = 0; i < 5; i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + if (this->work[INVINC_TIMER] != 0) { + this->work[INVINC_TIMER]--; + } + + if (this->work[FOG_TIMER] != 0) { + this->work[FOG_TIMER]--; + } + + if (this->actionFunc == BossTw_FlyTo || this->actionFunc == BossTw_Spin || + this->actionFunc == BossTw_TurnToPlayer) { + if ((s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) < 0x1000 && + (s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) > -0x1000 && player->unk_A73) { + BossTw_SetupSpin(this, globalCtx); + } + } + + this->actionFunc(this, globalCtx); + + if (this->actionFunc != BossTw_Wait) { + this->collider.dim.radius = 45; + + if (this->actionFunc == BossTw_Spin) { + this->collider.dim.radius *= 2; + } + + this->collider.dim.height = 120; + this->collider.dim.yShift = -30; + + if (this->work[INVINC_TIMER] == 0) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->actor.params == 0) { + this->workf[OUTR_CRWN_TX_X2] += 1.0f; + this->workf[OUTR_CRWN_TX_Y2] -= 7.0f; + this->workf[INNR_CRWN_TX_Y1] += 1.0f; + } else { + this->workf[OUTR_CRWN_TX_X2] += 0.0f; + this->workf[INNR_CRWN_TX_X2] += 0.0f; + this->workf[OUTR_CRWN_TX_Y2] += -15.0f; + this->workf[INNR_CRWN_TX_Y2] += -10.0f; + } + + if (((this->work[CS_TIMER_2] % 32) == 0) && (Rand_ZeroOne() < 0.3f)) { + this->work[BLINK_IDX] = 4; + } + + this->eyeTexIdx = D_8094A900[this->work[BLINK_IDX]]; + + if (this->work[BLINK_IDX] != 0) { + this->work[BLINK_IDX]--; + } + + if (this->actionFunc != BossTw_MergeCS && this->unk_5F8 != 0) { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + if (this->scepterAlpha > 0.0f) { + for (i = 0; i <= 0; i++) { + pos = this->scepterFlamePos[0]; + pos.x += Rand_CenteredFloat(70.0f); + pos.y += Rand_CenteredFloat(70.0f); + pos.z += Rand_CenteredFloat(70.0f); + accel.y = 0.4f; + accel.x = Rand_CenteredFloat(0.5f); + accel.z = Rand_CenteredFloat(0.5f); + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8, + this->actor.params, 37); + } + } + + for (i = 0; i <= 0; i++) { + pos = this->crownPos; + pos.x += Rand_CenteredFloat(70.0f); + pos.y += Rand_CenteredFloat(70.0f); + pos.z += Rand_CenteredFloat(70.0f); + accel.y = 0.4f; + accel.x = Rand_CenteredFloat(0.5f); + accel.z = Rand_CenteredFloat(0.5f); + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8, + this->actor.params, 37); + } + } + } +} + +void BossTw_TwinrovaUpdate(Actor* thisx, GlobalContext* globalCtx2) { + s16 i; + GlobalContext* globalCtx = globalCtx2; + BossTw* this = (BossTw*)thisx; + Player* player = GET_PLAYER(globalCtx); + + this->actor.flags &= ~ACTOR_FLAG_10; + this->unk_5F8 = 0; + this->collider.base.colType = COLTYPE_HIT3; + + Math_ApproachF(&this->fogR, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->fogG, globalCtx->lightCtx.fogColor[1], 1.0f, 10.0f); + Math_ApproachF(&this->fogB, globalCtx->lightCtx.fogColor[2], 1.0f, 10.0f); + Math_ApproachF(&this->fogNear, globalCtx->lightCtx.fogNear, 1.0f, 10.0f); + Math_ApproachF(&this->fogFar, 1000.0f, 1.0f, 10.0f); + + this->work[CS_TIMER_1]++; + this->work[CS_TIMER_2]++; + + for (i = 0; i < 5; i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + if (this->work[INVINC_TIMER] != 0) { + this->work[INVINC_TIMER]--; + } + + if (this->work[FOG_TIMER] != 0) { + this->work[FOG_TIMER]--; + } + + this->actionFunc(this, globalCtx); + + if (this->actionFunc != BossTw_TwinrovaShootBlast && this->actionFunc != BossTw_TwinrovaChargeBlast && + this->visible && this->unk_5F8 == 0 && + (s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) < 0x1000 && + (s16)(player->actor.shape.rot.y - this->actor.yawTowardsPlayer + 0x8000) > -0x1000 && player->unk_A73 != 0) { + BossTw_TwinrovaSetupSpin(this, globalCtx); + } + + this->eyeTexIdx = D_8094A900[this->work[BLINK_IDX]]; + if (this->work[BLINK_IDX] != 0) { + this->work[BLINK_IDX]--; + } + + if ((this->work[CS_TIMER_2] % 32) == 0) { + if (this->actionFunc != BossTw_TwinrovaMergeCS) { + if (Rand_ZeroOne() < 0.3f) { + this->work[BLINK_IDX] = 4; + } + } + } + + if (this->actionFunc == BossTw_TwinrovaMergeCS) { + this->leftEyeTexIdx = D_8094A90C[this->work[TW_BLINK_IDX]]; + if (this->work[TW_BLINK_IDX] != 0) { + this->work[TW_BLINK_IDX]--; + } + } else { + if (this->actionFunc == BossTw_TwinrovaStun) { + this->eyeTexIdx = 1; + } + + if (this->actionFunc == BossTw_TwinrovaDeathCS) { + this->eyeTexIdx = 2; + } + + this->leftEyeTexIdx = this->eyeTexIdx; + } + + if (this->visible && this->unk_5F8 == 0) { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel; + + if (this->work[UNK_S8] != 0) { + this->work[UNK_S8] -= 20; + if (this->work[UNK_S8] < 0) { + this->work[UNK_S8] = 0; + } + } + + Math_ApproachF(&this->workf[UNK_F12], 1.0f, 1.0f, 0.05f); + accel.y = 0.4f; + + for (i = 0; i < 2; i++) { + pos = this->leftScepterPos; + pos.x += Rand_CenteredFloat(30.0f); + pos.y += Rand_CenteredFloat(30.0f); + pos.z += Rand_CenteredFloat(30.0f); + accel.x = Rand_CenteredFloat(0.5f); + accel.z = Rand_CenteredFloat(0.5f); + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 7, 0, 75); + } + + for (i = 0; i < 2; i++) { + pos = this->rightScepterPos; + pos.x += Rand_CenteredFloat(30.0f); + pos.y += Rand_CenteredFloat(30.0f); + pos.z += Rand_CenteredFloat(30.0f); + accel.x = Rand_CenteredFloat(0.5f); + accel.z = Rand_CenteredFloat(0.5f); + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 7, 1, 75); + } + } + + this->collider.dim.radius = 35; + + if (this->actionFunc == BossTw_TwinrovaSpin) { + this->collider.dim.radius *= 2; + } + + this->collider.dim.height = 150; + this->collider.dim.yShift = -60; + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (this->work[INVINC_TIMER] == 0) { + if (this->actionFunc != BossTw_TwinrovaStun) { + if (this->twinrovaStun != 0) { + this->twinrovaStun = 0; + this->work[FOG_TIMER] = 10; + BossTw_TwinrovaDamage(this, globalCtx, 0); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DAMAGE); + } else if (this->collider.base.acFlags & AC_HIT) { + ColliderInfo* info = this->collider.info.acHitInfo; + + this->collider.base.acFlags &= ~AC_HIT; + if (info->toucher.dmgFlags & (DMG_SLINGSHOT | DMG_ARROW)) {} + } + } else if (this->collider.base.acFlags & AC_HIT) { + u8 damage; + u8 swordDamage; + ColliderInfo* info = this->collider.info.acHitInfo; + + this->collider.base.acFlags &= ~AC_HIT; + swordDamage = false; + damage = CollisionCheck_GetSwordDamage(info->toucher.dmgFlags); + + if (damage == 0) { + damage = 2; + } else { + swordDamage = true; + } + + if (!(info->toucher.dmgFlags & DMG_HOOKSHOT)) { + if (((s8)this->actor.colChkInfo.health < 3) && !swordDamage) { + damage = 0; + } + + BossTw_TwinrovaDamage(this, globalCtx, damage); + } + } + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + osSyncPrintf("OooooooooooooooooooooooooooooooooCC\n"); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + globalCtx->envCtx.unk_DC = 2; + + switch (sEnvType) { + case 0: + Math_ApproachZeroF(&globalCtx->envCtx.unk_D8, 1.0f, 0.02f); + break; + case 1: + globalCtx->envCtx.unk_BD = 3; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 0.5f, 1.0f, 0.05f); + break; + case 2: + globalCtx->envCtx.unk_BD = 2; + Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_SinS(this->work[CS_TIMER_1] * 0x3000) * 0.03f) + 0.5f, 1.0f, + 0.05f); + break; + case 3: + globalCtx->envCtx.unk_BD = 3; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.1f); + break; + case 4: + globalCtx->envCtx.unk_BD = 2; + Math_ApproachF(&globalCtx->envCtx.unk_D8, (Math_SinS(this->work[CS_TIMER_1] * 0x3E00) * 0.05f) + 0.95f, + 1.0f, 0.1f); + break; + case 5: + globalCtx->envCtx.unk_BD = 0; + Math_ApproachF(&globalCtx->envCtx.unk_D8, 1.0f, 1.0f, 0.05f); + break; + case -1: + break; + } + + BossTw_UpdateEffects(globalCtx); + + if (sFreezeState == 1) { + sFreezeState = 2; + BossTw_AddPlayerFreezeEffect(globalCtx, NULL); + func_80078914(&player->actor.projectedPos, NA_SE_VO_LI_FREEZE); + func_80078914(&player->actor.projectedPos, NA_SE_PL_FREEZE); + + if (sShieldFireCharge != 0) { + sShieldFireCharge = 4; + } + } + + if (player->isBurning && sShieldIceCharge != 0) { + sShieldIceCharge = 4; + } +} + +s32 BossTw_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + BossTw* this = (BossTw*)thisx; + + if (limbIndex == 21) { + if (this->unk_5F8 == 0) { + if (this->actor.params == 0) { + *dList = object_tw_DL_012CE0; + } else { + *dList = object_tw_DL_0134B8; + } + } + } + + if (limbIndex == 14) { + if (this->actionFunc == BossTw_DeathCS) { + *dList = NULL; + } else if (this->scepterAlpha == 0.0f) { + if (this->actor.params == 0) { + *dList = object_tw_DL_012B38; + } else { + *dList = object_tw_DL_013310; + } + } + } + + return false; +} + +void BossTw_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_8094A944 = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_8094A950 = { 0.0f, 2000.0f, -2000.0f }; + static Vec3f D_8094A95C[] = { + { 0.0f, 0.0f, -10000.0f }, { 0.0f, 0.0f, -8000.0f }, { 0.0f, 0.0f, -9000.0f }, + { 0.0f, 0.0f, -11000.0f }, { 0.0f, 0.0f, -12000.0f }, + }; + BossTw* this = (BossTw*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6168); + + switch (limbIndex) { + case 21: + Matrix_MultVec3f(&D_8094A944, &this->actor.focus.pos); + Matrix_MultVec3f(&D_8094A950, &this->crownPos); + + if (this->unk_5F8 != 0) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6190), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + if (this->actor.params == 0) { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013AE8)); + } else { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013D68)); + } + } + break; + case 14: + Matrix_MultVec3f(&D_8094A95C[0], &this->scepterFlamePos[0]); + Matrix_MultVec3f(&D_8094A95C[1], &this->scepterFlamePos[1]); + Matrix_MultVec3f(&D_8094A95C[2], &this->scepterFlamePos[2]); + Matrix_MultVec3f(&D_8094A95C[3], &this->scepterFlamePos[3]); + Matrix_MultVec3f(&D_8094A95C[4], &this->scepterFlamePos[4]); + + if (this->scepterAlpha > 0.0f) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6221), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + if (this->actor.params == 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 225, 255, (s16)this->scepterAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013E98)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s16)this->scepterAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_013F98)); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 20, 0, (s16)this->scepterAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_014070)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 70, 0, (s16)this->scepterAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_014158)); + } + } + break; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6236); +} + +void func_80941BC0(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6341); + + Matrix_Push(); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Translate(this->groundBlastPos2.x, this->groundBlastPos2.y, this->groundBlastPos2.z, MTXMODE_NEW); + Matrix_Scale(this->workf[UNK_F12], this->workf[UNK_F12], this->workf[UNK_F12], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6358), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)this->workf[UNK_F11]); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 40, 30, 80); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01BC00)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 215, 215, 215, (s16)this->workf[UNK_F11] * this->workf[UNK_F14]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, (u32)this->workf[UNK_F16] & 0x3F, + (this->work[CS_TIMER_2] * 4) & 0x3F, 0x10, 0x10)); + Matrix_Push(); + Matrix_RotateY(this->workf[UNK_F15], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6423), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01C1C0)); + Matrix_Pop(); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6427), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPSegment(POLY_XLU_DISP++, 0xD, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->work[CS_TIMER_2] & 0x7F, + (this->work[CS_TIMER_2] * 8) & 0xFF, 0x20, 0x40, 1, + (-this->work[CS_TIMER_2] * 2) & 0x3F, 0, 0x10, 0x10)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s16)this->workf[UNK_F9]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + gDPSetRenderMode(POLY_XLU_DISP++, + Z_CMP | IM_RD | CVG_DST_SAVE | ZMODE_DEC | FORCE_BL | + GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA), + G_RM_ZB_OVL_SURF2); + gSPSetGeometryMode(POLY_XLU_DISP++, G_CULL_BACK | G_FOG); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A790)); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6461); +} + +void func_80942180(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6468); + + Matrix_Push(); + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Translate(this->groundBlastPos2.x, this->groundBlastPos2.y, this->groundBlastPos2.z, MTXMODE_NEW); + Matrix_Scale(this->workf[KM_GD_CRTR_SCL], this->workf[KM_GD_CRTR_SCL], this->workf[KM_GD_CRTR_SCL], MTXMODE_APPLY); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (-this->work[CS_TIMER_1]) & 0x7F, 0, 0x20, 0x20, 1, + (this->work[CS_TIMER_1] * 2) & 0x7F, 0, 0x20, 0x20)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6497), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 40, 00, (s16)this->workf[KM_GRND_CRTR_A]); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 245, 255, 128); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_019D40)); + + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6514), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->work[CS_TIMER_1] & 0x7F, + (-this->work[CS_TIMER_1] * 6) & 0xFF, 0x20, 0x40, 1, + (this->work[CS_TIMER_1] * 2) & 0x7F, (-this->work[CS_TIMER_1] * 6) & 0xFF, 0x20, 0x40)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 80, 0, 0, (s16)this->workf[KM_GD_SMOKE_A]); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 100); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_018FC0)); + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (-this->work[CS_TIMER_1] * 3) & 0x7F, 0, 0x20, 0x20, 1, 0, + (-this->work[CS_TIMER_1] * 10) & 0xFF, 0x20, 0x40)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 50, 0, (s16)(this->workf[KM_GD_FLM_A] * 0.7f)); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 235, 240, 128); + Matrix_Scale(this->workf[KM_GD_FLM_SCL], this->workf[KM_GD_FLM_SCL], this->workf[KM_GD_FLM_SCL], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6575), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_019938)); + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6579); +} + +void func_809426F0(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6587); + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (u8)(-this->work[CS_TIMER_2] * 15), 0x20, 0x40, 1, 0, 0, + 0x40, 0x40)); + Matrix_Push(); + Matrix_Translate(0.0f, 0.0f, 5000.0f, MTXMODE_APPLY); + Matrix_Scale(this->spawnPortalScale / 2000.0f, this->spawnPortalScale / 2000.0f, this->spawnPortalScale / 2000.0f, + MTXMODE_APPLY); + Matrix_RotateZ(this->portalRotation, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6614), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + + if (this->actor.params == 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 135, 175, 165, (s16)this->spawnPortalAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01CEE0)); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, (s16)this->spawnPortalAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DBE8)); + } + + Matrix_Pop(); + + if (this->actor.params == 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s16)this->flameAlpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998)); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s16)this->flameAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + } + + for (i = 0; i < 8; i++) { + Matrix_Push(); + Matrix_Translate(0.0f, 0.0f, 5000.0f, MTXMODE_APPLY); + Matrix_RotateZ(((i * M_PI) * 2.0f * 0.125f) + this->flameRotation, MTXMODE_APPLY); + Matrix_Translate(0.0f, this->spawnPortalScale * 1.5f, 0.0f, MTXMODE_APPLY); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, ((this->work[CS_TIMER_2] * 3) + (i * 10)) & 0x7F, + (u8)((-this->work[CS_TIMER_2] * 15) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + Matrix_Scale(0.4f, 0.4f, 0.4f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6751), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430)); + Matrix_Pop(); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6756); +} + +void func_80942C70(Actor* thisx, GlobalContext* globalCtx) { + BossTw* this = (BossTw*)thisx; + s16 alpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6765); + + if (this->beamDist != 0.0f) { + Matrix_Push(); + gSPSegment(POLY_XLU_DISP++, 0xC, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (u8)(this->work[CS_TIMER_1] * -0xF), 0x20, 0x40)); + alpha = this->beamScale * 100.0f * 255.0f; + + if (this->actor.params == 1) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 60, alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 128); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 100, 255, 128); + } + + Matrix_Translate(this->beamOrigin.x, this->beamOrigin.y, this->beamOrigin.z, MTXMODE_NEW); + Matrix_RotateY(this->beamYaw, MTXMODE_APPLY); + Matrix_RotateX(this->beamPitch, MTXMODE_APPLY); + Matrix_RotateZ(this->beamRoll, MTXMODE_APPLY); + Matrix_Scale(this->beamScale, this->beamScale, (this->beamDist * 0.01f * 98.0f) / 20000.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6846), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DDF0)); + + if (this->beamReflectionDist > 10.0f) { + Matrix_Translate(this->beamReflectionOrigin.x, this->beamReflectionOrigin.y, this->beamReflectionOrigin.z, + MTXMODE_NEW); + Matrix_RotateY(this->beamReflectionYaw, MTXMODE_APPLY); + Matrix_RotateX(this->beamReflectionPitch, MTXMODE_APPLY); + Matrix_RotateZ(this->beamRoll, MTXMODE_APPLY); + Matrix_Scale(this->beamScale, this->beamScale, (this->beamReflectionDist * 0.01f * 100.0f) / 20000.0f, + MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6870), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DDF0)); + } + + Matrix_Pop(); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6878); +} + +void func_80943028(Actor* thisx, GlobalContext* globalCtx) { + BossTw* this = (BossTw*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6885); + + Matrix_Push(); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y + 57.0f, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(this->workf[UNK_F17], this->workf[UNK_F17], this->workf[UNK_F17], MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6908), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01F608)); + func_80094044(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 200); + Matrix_Translate(this->actor.world.pos.x, 240.0f, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale((this->actor.scale.x * 4000.0f) / 100.0f, 1.0f, (this->actor.scale.x * 4000.0f) / 100.0f, + MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6926), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gCircleShadowDL)); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6933); +} + +static void* sEyeTextures[] = { + object_tw_Tex_00A438, + object_tw_Tex_00B238, + object_tw_Tex_00B638, +}; + +void BossTw_Draw(Actor* thisx, GlobalContext* globalCtx2) { + static Vec3f D_8094A9A4 = { 0.0f, 200.0f, 2000.0f }; + GlobalContext* globalCtx = globalCtx2; + BossTw* this = (BossTw*)thisx; + Player* player = GET_PLAYER(globalCtx); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 6947); + + if (this->visible) { + gSPSegment(POLY_OPA_DISP++, 10, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeTexIdx])); + gSPSegment(POLY_XLU_DISP++, 10, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeTexIdx])); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->workf[OUTR_CRWN_TX_X1] & 0x7F, + (s16)this->workf[OUTR_CRWN_TX_Y1] & 0x7F, 0x20, 0x20, 1, + (s16)this->workf[OUTR_CRWN_TX_X2] & 0x7F, (s16)this->workf[OUTR_CRWN_TX_Y2] & 0xFF, + 0x20, 0x40)); + + if (this->actor.params == TW_KOTAKE) { + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TexScroll(globalCtx->state.gfxCtx, (s16)this->workf[INNR_CRWN_TX_X1] & 0x7F, + (s16)this->workf[INNR_CRWN_TX_Y1] & 0xFF, 0x20, 0x40)); + } else { + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->workf[INNR_CRWN_TX_X1] & 0x7F, + (s16)this->workf[INNR_CRWN_TX_Y1] & 0x7F, 0x20, 0x20, 1, + (s16)this->workf[INNR_CRWN_TX_X2] & 0x7F, + (s16)this->workf[INNR_CRWN_TX_Y2] & 0xFF, 0x20, 0x40)); + } + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + if (this->work[FOG_TIMER] & 2) { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099); + } else { + POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, (u32)this->fogR, (u32)this->fogG, (u32)this->fogB, 0, + this->fogNear, this->fogFar); + } + + Matrix_Push(); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossTw_OverrideLimbDraw, BossTw_PostLimbDraw, this); + Matrix_Pop(); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + } + + if (this->actor.params == TW_KOTAKE) { + if (this->workf[UNK_F9] > 0.0f) { + if (this->workf[UNK_F11] > 0.0f) { + Vec3f diff; + diff.x = this->groundBlastPos2.x - player->actor.world.pos.x; + diff.y = this->groundBlastPos2.y - player->actor.world.pos.y; + diff.z = this->groundBlastPos2.z - player->actor.world.pos.z; + + if ((fabsf(diff.y) < 10.0f) && (player->actor.bgCheckFlags & 1) && + (sqrtf(SQ(diff.x) + SQ(diff.z)) < (this->workf[UNK_F12] * 4600.0f)) && (sFreezeState == 0) && + (this->workf[UNK_F11] > 200.0f)) { + sFreezeState = 1; + sTwinrovaPtr->timers[2] = 100; + } + } + + func_80941BC0(this, globalCtx); + } + } else { + func_80942180(this, globalCtx); + } + + if (this->visible) { + if (this->actionFunc == BossTw_DeathCS) { + func_80943028(&this->actor, globalCtx); + } else { + func_809426F0(this, globalCtx); + Matrix_MultVec3f(&D_8094A9A4, &this->beamOrigin); + func_80942C70(&this->actor, globalCtx); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7123); +} + +void* D_8094A9B0[] = { + object_tw_Tex_02A9B0, + object_tw_Tex_02A070, + object_tw_Tex_02A470, +}; + +s32 BossTw_TwinrovaOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossTw* this = (BossTw*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7139); + + switch (limbIndex) { + case 21: + gSPSegment(POLY_OPA_DISP++, 0xC, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (s16)(f32)this->work[CS_TIMER_1], 8, 8)); + gSPSegment(POLY_OPA_DISP++, 8, SEGMENTED_TO_VIRTUAL(D_8094A9B0[this->eyeTexIdx])); + gSPSegment(POLY_OPA_DISP++, 9, SEGMENTED_TO_VIRTUAL(D_8094A9B0[this->leftEyeTexIdx])); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, this->work[UNK_S8]); + break; + case 17: + case 41: + *dList = NULL; + gSPSegment(POLY_XLU_DISP++, 0xA, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0, + -this->work[CS_TIMER_1] * 0xF, 0x20, 0x40)); + break; + case 18: + case 42: + *dList = NULL; + gSPSegment(POLY_XLU_DISP++, 0xB, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0, + -this->work[CS_TIMER_1] * 0xA, 0x20, 0x40)); + break; + case 16: + case 32: + *dList = NULL; + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, this->work[CS_TIMER_1], + -this->work[CS_TIMER_1] * 7, 0x20, 0x40)); + break; + case 15: + case 31: + *dList = NULL; + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, this->work[CS_TIMER_1], 0x20, 0x40)); + break; + case 19: + if (this->unk_5F8 != 0) { + *dList = object_tw_DL_02D940; + } + break; + + case 20: + if (this->unk_5F8 != 0) { + *dList = object_tw_DL_02D890; + } + break; + } + + if (this->unk_5F8 != 0 && ((limbIndex == 34) || (limbIndex == 40))) { + *dList = NULL; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7251); + + return false; +} + +void BossTw_TwinrovaPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_8094A9BC = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_8094A9C8 = { 0.0f, 2000.0f, -2000.0f }; + static Vec3f D_8094A9D4 = { 13000.0f, 0.0f, 0.0f }; + static Vec3f D_8094A9E0 = { 13000.0f, 0.0f, 0.0f }; + + BossTw* this = (BossTw*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7262); + + switch (limbIndex) { + case 34: + Matrix_MultVec3f(&D_8094A9D4, &this->leftScepterPos); + break; + case 40: + Matrix_MultVec3f(&D_8094A9E0, &this->rightScepterPos); + break; + case 21: + Matrix_MultVec3f(&D_8094A9BC, &this->actor.focus.pos); + Matrix_MultVec3f(&D_8094A9C8, &this->crownPos); + break; + case 15: + case 16: + case 17: + case 18: + case 31: + case 32: + case 41: + case 42: + Matrix_Push(); + Matrix_Scale(this->workf[UNK_F12], this->workf[UNK_F12], this->workf[UNK_F12], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7295), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + Matrix_Pop(); + gSPDisplayList(POLY_XLU_DISP++, *dList); + break; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7301); +} + +void BossTw_ShieldChargeDraw(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s16 temp_t0; + s16 temp_a0; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7311); + + Matrix_Push(); + + temp_t0 = sShieldFireCharge | sShieldIceCharge; + + if (temp_t0 == 1) { + func_80078884(NA_SE_IT_SHIELD_CHARGE_LV1 & ~SFX_FLAG); + } else if (temp_t0 == 2) { + func_80078884(NA_SE_IT_SHIELD_CHARGE_LV2 & ~SFX_FLAG); + } else if (temp_t0 == 3) { + func_80078884(NA_SE_IT_SHIELD_CHARGE_LV3 & ~SFX_FLAG); + } + + if (temp_t0 != 0 && temp_t0 < 4) { + Math_ApproachF(&D_8094C854, 255.0f, 1.0f, 20.0f); + if (temp_t0 == 3) { + temp_t0 *= 3; + } + } else if (temp_t0 == 0) { + D_8094C854 = 0.0f; + } else { + Math_ApproachF(&D_8094C854, 0.0f, 1.0f, 10.0f); + if (D_8094C854 == 0.0f) { + sShieldIceCharge = 0; + sShieldFireCharge = 0; + } + + temp_t0 = 1; + } + + if (Player_HasMirrorShieldEquipped(globalCtx)) { + if (temp_t0 != 0) { + Matrix_Mult(&player->shieldMf, MTXMODE_NEW); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7362), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + temp_a0 = (Math_SinS(this->work[CS_TIMER_1] * 2730 * temp_t0) * D_8094C854 * 0.5f) + (D_8094C854 * 0.5f); + if (sShieldFireCharge != 0) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 245, 255, temp_a0); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E0E0)); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (this->work[CS_TIMER_1] * 2) * temp_t0, 0, 0x20, + 0x20, 1, (-this->work[CS_TIMER_1] * 2) * temp_t0, 0, 0x20, 0x20)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 20, 0, (s16)D_8094C854); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E020)); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 225, 255, 255, temp_a0); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E3A0)); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (-this->work[CS_TIMER_1] * 5) * temp_t0, + 0x20, 0x40, 1, (this->work[CS_TIMER_1] * 4) * temp_t0, 0, 0x20, 0x20)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 175, 205, 195, (s16)D_8094C854); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E2C0)); + } + } + } + + if (D_8094C86F != 0) { + f32 step = D_8094C872 > 0 ? 100.0f : 60.0f; + + D_8094C86F--; + Math_ApproachF(&D_8094C858, 255.0f, 1.0f, step); + } else { + f32 step = D_8094C872 > 0 ? 40.0f : 20.0f; + + Math_ApproachF(&D_8094C858, 0.0f, 1.0f, step); + } + + if (Player_HasMirrorShieldEquipped(globalCtx) && D_8094C858 > 0.0f) { + f32 scale = D_8094C872 > 0 ? 1.3f : 1.0f; + + Matrix_Mult(&player->shieldMf, MTXMODE_NEW); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7486), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + if (sShieldFireCharge != 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 220, 20, (s16)D_8094C858); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 20, 110); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)D_8094C858); + gDPSetEnvColor(POLY_XLU_DISP++, 185, 225, 205, 150); + } + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, this->work[CS_TIMER_1] * D_8094C872, 0x20, 0x40, 1, + 0, this->work[CS_TIMER_1] * D_8094C872, 0x20, 0x20)); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01E9F0)); + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7531); +} + +void BossTw_SpawnPortalDraw(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7546); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment( + POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, -this->work[CS_TIMER_1] * 15, 0x20, 0x40, 1, 0, 0, 0x40, 0x40)); + + Matrix_Push(); + + Matrix_Translate(0.0f, 232.0f, -600.0f, MTXMODE_NEW); + Matrix_Scale(this->spawnPortalScale, this->spawnPortalScale, this->spawnPortalScale, MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s16)this->spawnPortalAlpha); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7582), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01EC68)); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 135, 175, 165, (s16)this->spawnPortalAlpha); + Matrix_Translate(0.0f, 2.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7596), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01CEE0)); + + Matrix_Translate(0.0f, 232.0f, 600.0f, MTXMODE_NEW); + Matrix_Scale(this->spawnPortalScale, this->spawnPortalScale, this->spawnPortalScale, MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s16)this->spawnPortalAlpha); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7617), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01EC68)); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, (s16)this->spawnPortalAlpha); + Matrix_Translate(0.0f, 2.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7631), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01DBE8)); + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7635); +} + +void func_80944C50(BossTw* this, GlobalContext* globalCtx) { + s32 pad; + f32 scale; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7645); + + Matrix_Push(); + Matrix_Translate(0.0f, 750.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.35f, 0.35f, 0.35f, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Scale(this->workf[UNK_F19], this->workf[UNK_F19], this->workf[UNK_F19], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7671), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01F390)); + + Matrix_Pop(); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -sKoumePtr->work[CS_TIMER_1] * 2, 0, 0x20, 0x20, 1, + -sKoumePtr->work[CS_TIMER_1] * 2, 0, 0x20, 0x40)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)this->workf[UNK_F18] / 2); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7694), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01F238)); + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, -sKoumePtr->work[CS_TIMER_1] * 5, + -sKoumePtr->work[CS_TIMER_1] * 2, 0x20, 0x40, 1, 0, -sKoumePtr->work[CS_TIMER_1] * 2, + 0x10, 0x10)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s16)(this->workf[UNK_F18] * 0.3f)); + + scale = this->workf[UNK_F18] / 150.0f; + scale = CLAMP_MAX(scale, 1.0f); + + Matrix_Scale(scale, 1.0f, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7728), + G_MTX_LOAD | G_MTX_MODELVIEW | G_MTX_NOPUSH); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01EEB0)); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7732); +} + +void BossTw_TwinrovaDraw(Actor* thisx, GlobalContext* globalCtx2) { + static Vec3f D_8094A9EC = { 0.0f, 200.0f, 2000.0f }; + GlobalContext* globalCtx = globalCtx2; + BossTw* this = (BossTw*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7748); + + if (this->visible) { + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + POLY_OPA_DISP = (this->work[FOG_TIMER] & 2) ? Gfx_SetFog2(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099) + : Gfx_SetFog2(POLY_OPA_DISP, (u32)this->fogR, (u32)this->fogG, + (u32)this->fogB, 0, this->fogNear, this->fogFar); + + Matrix_Push(); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossTw_TwinrovaOverrideLimbDraw, BossTw_TwinrovaPostLimbDraw, + thisx); + Matrix_Pop(); + + Matrix_MultVec3f(&D_8094A9EC, &this->beamOrigin); + POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP, globalCtx->lightCtx.fogColor[0], globalCtx->lightCtx.fogColor[1], + globalCtx->lightCtx.fogColor[2], 0, globalCtx->lightCtx.fogNear, 1000); + } + + BossTw_DrawEffects(globalCtx); + BossTw_ShieldChargeDraw(this, globalCtx); + + if (this->spawnPortalAlpha > 0.0f) { + BossTw_SpawnPortalDraw(this, globalCtx); + } + + if (this->workf[UNK_F18] > 0.0f) { + func_80944C50(this, globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 7804); +} + +void BossTw_BlastFire(BossTw* this, GlobalContext* globalCtx) { + s16 i; + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 distXZ; + Player* player = GET_PLAYER(globalCtx); + Player* player2 = player; + + switch (this->actor.params) { + case TW_FIRE_BLAST: + switch (this->csState1) { + case 0: + Actor_SetScale(&this->actor, 0.03f); + this->csState1 = 1; + xDiff = player->actor.world.pos.x - this->actor.world.pos.x; + yDiff = (player->actor.world.pos.y + 30.0f) - this->actor.world.pos.y; + zDiff = player->actor.world.pos.z - this->actor.world.pos.z; + // yaw + this->actor.world.rot.y = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI); + // pitch + distXZ = sqrtf(SQ(xDiff) + SQ(zDiff)); + this->actor.world.rot.x = Math_FAtan2F(yDiff, distXZ) * (32768 / M_PI); + this->actor.speedXZ = 20.0f; + + for (i = 0; i < 50; i++) { + this->blastTailPos[i] = this->actor.world.pos; + } + this->workf[TAIL_ALPHA] = 255.0f; + // fallthrough + case 1: + case 10: + this->blastActive = true; + if (this->timers[0] == 0) { + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FIRE & ~SFX_FLAG); + } else { + Vec3f velocity; + Vec3f velDir; + Vec3s blastDir; + s16 alpha; + + this->actor.world.pos = player2->bodyPartsPos[15]; + this->actor.world.pos.y = -2000.0f; + Matrix_MtxFToYXZRotS(&player2->shieldMf, &blastDir, 0); + blastDir.x = -blastDir.x; + blastDir.y = blastDir.y + 0x8000; + Math_ApproachS(&this->magicDir.x, blastDir.x, 0xA, 0x800); + Math_ApproachS(&this->magicDir.y, blastDir.y, 0xA, 0x800); + + if (this->timers[0] == 50) { + D_8094C86F = 10; + D_8094C872 = 7; + globalCtx->envCtx.unk_D8 = 1.0f; + } + + if (this->timers[0] <= 50) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FIRE & ~SFX_FLAG); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_REFL_FIRE & ~SFX_FLAG); + Matrix_RotateY((this->magicDir.y / 32678.0f) * M_PI, MTXMODE_NEW); + Matrix_RotateX((this->magicDir.x / 32678.0f) * M_PI, MTXMODE_APPLY); + velDir.x = 0.0f; + velDir.y = 0.0f; + velDir.z = 50.0f; + Matrix_MultVec3f(&velDir, &velocity); + alpha = this->timers[0] * 10; + alpha = CLAMP_MAX(alpha, 255); + + BossTw_AddShieldBlastEffect(globalCtx, &player2->bodyPartsPos[15], &velocity, &sZeroVector, + 10.0f, 80.0f, alpha, 1); + } + + if (this->timers[0] == 1) { + sEnvType = 0; + sShieldFireCharge++; + Actor_Kill(&this->actor); + } + + return; + } + + this->groundBlastPos.y = BossTw_GetFloorY(&this->actor.world.pos); + + if (this->groundBlastPos.y >= 0.0f) { + if (this->groundBlastPos.y != 35.0f) { + this->groundBlastPos.x = this->actor.world.pos.x; + this->groundBlastPos.z = this->actor.world.pos.z; + BossTw_SpawnGroundBlast(this, globalCtx, 1); + } else { + Vec3f velocity; + Vec3f accel; + + for (i = 0; i < 50; i++) { + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + accel.x = 0.0f; + accel.y = 0.0f; + accel.z = 0.0f; + BossTw_AddFlameEffect(globalCtx, &this->actor.world.pos, &velocity, &accel, + Rand_ZeroFloat(10.0f) + 25.0f, this->blastType); + } + + globalCtx->envCtx.unk_D8 = 0.5f; + } + + this->csState1 = 2; + this->timers[0] = 20; + } else { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < 10; i++) { + pos = this->blastTailPos[(s16)Rand_ZeroFloat(29.9f)]; + pos.x += Rand_CenteredFloat(40.0f); + pos.y += Rand_CenteredFloat(40.0f); + pos.z += Rand_CenteredFloat(40.0f); + accel.y = 0.4f; + accel.x = Rand_CenteredFloat(0.5f); + accel.z = Rand_CenteredFloat(0.5f); + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 8, 1, + 75); + } + } + break; + case 2: + Math_ApproachF(&this->workf[TAIL_ALPHA], 0.0f, 1.0f, 15.0f); + if (this->timers[0] == 0) { + Actor_Kill(&this->actor); + } + break; + } + break; + + case TW_FIRE_BLAST_GROUND: + if (this->timers[0] != 0) { + if (this->timers[0] == 1) { + sEnvType = 0; + } + + if (sGroundBlastType == 2) { + this->timers[0] = 0; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FIRE_EXP - SFX_FLAG); + + xDiff = sKoumePtr->groundBlastPos2.x - player->actor.world.pos.x; + yDiff = sKoumePtr->groundBlastPos2.y - player->actor.world.pos.y; + zDiff = sKoumePtr->groundBlastPos2.z - player->actor.world.pos.z; + + if (!player->isBurning && (player->actor.bgCheckFlags & 1) && (fabsf(yDiff) < 10.0f) && + (sqrtf(SQ(xDiff) + SQ(zDiff)) < (sKoumePtr->workf[UNK_F13] * 4550.0f))) { + s16 j; + + for (j = 0; j < 18; j++) { + player->flameTimers[j] = Rand_S16Offset(0, 200); + } + + player->isBurning = 1; + + if (this->work[BURN_TMR] == 0) { + func_8002F7DC(&player->actor, player->ageProperties->unk_92 + NA_SE_VO_LI_DEMO_DAMAGE); + this->work[BURN_TMR] = 40; + } + + sTwinrovaPtr->timers[2] = 100; + } + + Math_ApproachF(&sKoumePtr->workf[UNK_F13], 0.04f, 0.1f, 0.002f); + break; + } + + { + f32 sp4C = sGroundBlastType == 2 ? 3.0f : 1.0f; + + Math_ApproachF(&sKoumePtr->workf[UNK_F9], 0.0f, 1.0f, 10.0f * sp4C); + Math_ApproachF(&sKoumePtr->workf[UNK_F12], 0.0f, 1.0f, 0.03f * sp4C); + Math_ApproachF(&sKoumePtr->workf[TAIL_ALPHA], 0.0f, 1.0f, 3.0f * sp4C); + Math_ApproachF(&sKoumePtr->workf[UNK_F11], 0.0f, 1.0f, 6.0f * sp4C); + } + + if (sKoumePtr->workf[TAIL_ALPHA] <= 0.0f) { + Actor_Kill(&this->actor); + } + + break; + } +} + +void BossTw_BlastIce(BossTw* this, GlobalContext* globalCtx) { + s16 i; + f32 xDiff; + f32 yDiff; + f32 zDiff; + f32 xzDist; + Player* player = GET_PLAYER(globalCtx); + Player* player2 = player; + + switch (this->actor.params) { + case TW_ICE_BLAST: + switch (this->csState1) { + case 0: + Actor_SetScale(&this->actor, 0.03f); + this->csState1 = 1; + xDiff = player->actor.world.pos.x - this->actor.world.pos.x; + yDiff = (player->actor.world.pos.y + 30.0f) - this->actor.world.pos.y; + zDiff = player->actor.world.pos.z - this->actor.world.pos.z; + this->actor.world.rot.y = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + this->actor.world.rot.x = Math_FAtan2F(yDiff, xzDist) * (32768 / M_PI); + this->actor.speedXZ = 20.0f; + for (i = 0; i < 50; i++) { + this->blastTailPos[i] = this->actor.world.pos; + } + + this->workf[TAIL_ALPHA] = 255.0f; + // fallthrough + case 1: + case 10: + this->blastActive = true; + + if (this->timers[0] == 0) { + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG); + } else { + Vec3f velocity; + Vec3f spF4; + Vec3s reflDir; + s16 alpha; + + this->actor.world.pos = player2->bodyPartsPos[15]; + this->actor.world.pos.y = -2000.0f; + Matrix_MtxFToYXZRotS(&player2->shieldMf, &reflDir, 0); + reflDir.x = -reflDir.x; + reflDir.y += 0x8000; + Math_ApproachS(&this->magicDir.x, reflDir.x, 0xA, 0x800); + Math_ApproachS(&this->magicDir.y, reflDir.y, 0xA, 0x800); + + if (this->timers[0] == 50) { + D_8094C86F = 10; + D_8094C872 = 7; + globalCtx->envCtx.unk_D8 = 1.0f; + } + + if (this->timers[0] <= 50) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_SHOOT_FREEZE - SFX_FLAG); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_REFL_FREEZE - SFX_FLAG); + Matrix_RotateY((this->magicDir.y / 32678.0f) * M_PI, MTXMODE_NEW); + Matrix_RotateX((this->magicDir.x / 32678.0f) * M_PI, MTXMODE_APPLY); + spF4.x = 0.0f; + spF4.y = 0.0f; + spF4.z = 50.0f; + Matrix_MultVec3f(&spF4, &velocity); + alpha = this->timers[0] * 10; + alpha = CLAMP_MAX(alpha, 255); + + BossTw_AddShieldBlastEffect(globalCtx, &player2->bodyPartsPos[15], &velocity, &sZeroVector, + 10.0f, 80.0f, alpha, 0); + } + + if (this->timers[0] == 1) { + sEnvType = 0; + sShieldIceCharge++; + Actor_Kill(&this->actor); + } + + break; + } + + this->groundBlastPos.y = BossTw_GetFloorY(&this->actor.world.pos); + + if (this->groundBlastPos.y >= 0.0f) { + if (this->groundBlastPos.y != 35.0f) { + this->groundBlastPos.x = this->actor.world.pos.x; + this->groundBlastPos.z = this->actor.world.pos.z; + BossTw_SpawnGroundBlast(this, globalCtx, 0); + } else { + for (i = 0; i < 50; i++) { + Vec3f velocity; + Vec3f accel; + + velocity.x = Rand_CenteredFloat(20.0f); + velocity.y = Rand_CenteredFloat(20.0f); + velocity.z = Rand_CenteredFloat(20.0f); + accel.x = 0.0f; + accel.y = 0.0f; + accel.z = 0.0f; + BossTw_AddFlameEffect(globalCtx, &this->actor.world.pos, &velocity, &accel, + Rand_ZeroFloat(10.0f) + 25.0f, this->blastType); + } + + globalCtx->envCtx.unk_D8 = 0.5f; + } + + this->csState1 = 2; + this->timers[0] = 20; + } else { + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < 10; i++) { + pos = this->blastTailPos[(s16)Rand_ZeroFloat(29.9f)]; + pos.x += Rand_CenteredFloat(40.0f); + pos.y += Rand_CenteredFloat(40.0f); + pos.z += Rand_CenteredFloat(40.0f); + accel.y = 0.4f; + accel.x = Rand_CenteredFloat(0.5f); + accel.z = Rand_CenteredFloat(0.5f); + BossTw_AddDotEffect(globalCtx, &pos, &velocity, &accel, ((s16)Rand_ZeroFloat(2.0f) + 8), 0, + 75); + } + } + break; + + case 2: + Math_ApproachF(&this->workf[TAIL_ALPHA], 0.0f, 1.0f, 15.0f); + if (this->timers[0] == 0) { + Actor_Kill(&this->actor); + } + break; + } + break; + + case TW_ICE_BLAST_GROUND: + if (this->timers[0] != 0) { + if (this->timers[0] == 1) { + sEnvType = 0; + } + + if (sGroundBlastType == 1) { + this->timers[0] = 0; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_ICE_FREEZE - SFX_FLAG); + + if (this->timers[0] > (sTwinrovaPtr->actionFunc == BossTw_Wait ? 70 : 20)) { + s32 pad; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + pos.x = sKotakePtr->groundBlastPos2.x + Rand_CenteredFloat(320.0f); + pos.z = sKotakePtr->groundBlastPos2.z + Rand_CenteredFloat(320.0f); + pos.y = sKotakePtr->groundBlastPos2.y; + velocity.x = 0.0f; + velocity.y = 0.0f; + velocity.z = 0.0f; + accel.x = 0.0f; + accel.y = 0.13f; + accel.z = 0.0f; + BossTw_AddDmgCloud(globalCtx, 3, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 20.0f, 0, 0, 80); + velocity.x = Rand_CenteredFloat(10.0f); + velocity.z = Rand_CenteredFloat(10.0f); + velocity.y = Rand_ZeroFloat(3.0f) + 3.0f; + pos.x = sKotakePtr->groundBlastPos2.x + (velocity.x * 0.5f); + pos.z = sKotakePtr->groundBlastPos2.z + (velocity.z * 0.5f); + BossTw_AddDmgCloud(globalCtx, 3, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 15.0f, 255, 2, + 130); + } + + Math_ApproachF(&sKotakePtr->workf[UNK_F9], 80.0f, 1.0f, 3.0f); + Math_ApproachF(&sKotakePtr->workf[UNK_F11], 255.0f, 1.0f, 10.0f); + Math_ApproachF(&sKotakePtr->workf[UNK_F12], 0.04f, 0.1f, 0.002f); + Math_ApproachF(&sKotakePtr->workf[UNK_F16], 70.0f, 1.0f, 5.0f); + + if ((this->timers[0] == 70) || (this->timers[0] == 30)) { + sKotakePtr->workf[UNK_F16] = 10.0f; + } + + if ((this->timers[0] % 4) == 0) { + sKotakePtr->workf[UNK_F15] = (2.0f * (s16)Rand_ZeroFloat(9.9f) * M_PI) / 10.0f; + } + } else { + f32 sp80; + + if (sGroundBlastType == 1) { + if (sKotakePtr->workf[UNK_F11] > 1.0f) { + for (i = 0; i < 3; i++) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + pos.x = Rand_CenteredFloat(280.0f) + sKotakePtr->groundBlastPos2.x; + pos.z = Rand_CenteredFloat(280.0f) + sKotakePtr->groundBlastPos2.z; + pos.y = sKotakePtr->groundBlastPos2.y + 30.0f; + velocity.x = 0.0f; + velocity.y = 0.0f; + velocity.z = 0.0f; + accel.x = 0.0f; + accel.y = 0.13f; + accel.z = 0.0f; + BossTw_AddDmgCloud(globalCtx, 3, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 20, 0, 0, + 80); + } + } + sp80 = 3.0f; + } else { + sp80 = 1.0f; + } + + Math_ApproachF(&sKotakePtr->workf[UNK_F14], 0.0f, 1.0f, 0.2f * sp80); + Math_ApproachF(&sKotakePtr->workf[UNK_F11], 0.0f, 1.0f, 5.0f * sp80); + Math_ApproachF(&sKotakePtr->workf[UNK_F9], 0.0f, 1.0f, sp80); + + if (sKotakePtr->workf[UNK_F9] <= 0.0f) { + Actor_Kill(&this->actor); + } + } + break; + } +} + +s32 BossTw_BlastShieldCheck(BossTw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 ret = false; + ColliderInfo* info; + + if (1) {} + + if (this->csState1 == 1) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + this->collider.base.atFlags &= ~AT_HIT; + info = this->collider.info.acHitInfo; + + if (info->toucher.dmgFlags & DMG_SHIELD) { + this->work[INVINC_TIMER] = 7; + globalCtx->envCtx.unk_D8 = 1.0f; + func_800AA000(0.0f, 100, 5, 4); + + if (Player_HasMirrorShieldEquipped(globalCtx)) { + if (this->blastType == 1) { + if (sShieldIceCharge != 0) { + sShieldIceCharge = 0; + BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, 1); + } else { + BossTw_AddShieldHitEffect(globalCtx, 10.0f, 1); + sShieldFireCharge++; + D_8094C86F = (sShieldFireCharge * 2) + 8; + D_8094C872 = -7; + } + } else { + if (sShieldFireCharge != 0) { + sShieldFireCharge = 0; + if (1) {} + BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, 0); + } else { + BossTw_AddShieldHitEffect(globalCtx, 10.0f, 0); + sShieldIceCharge++; + D_8094C86F = (sShieldIceCharge * 2) + 8; + D_8094C872 = -7; + } + } + + if ((sShieldIceCharge >= 3) || (sShieldFireCharge >= 3)) { + this->timers[0] = 80; + this->csState1 = 10; + Matrix_MtxFToYXZRotS(&player->shieldMf, &this->magicDir, 0); + this->magicDir.y += 0x8000; + this->magicDir.x = -this->magicDir.x; + D_8094C86F = 8; + } else { + this->csState1 = 2; + this->timers[0] = 20; + sEnvType = 0; + } + } else { + BossTw_AddShieldDeflectEffect(globalCtx, 10.0f, this->blastType); + this->csState1 = 2; + this->timers[0] = 20; + sEnvType = 0; + sShieldIceCharge = 0; + sShieldFireCharge = 0; + func_80078884(NA_SE_IT_SHIELD_REFLECT_MG2); + } + + ret = true; + } + } + } + + return ret; +} + +void BossTw_BlastUpdate(Actor* thisx, GlobalContext* globalCtx) { + BossTw* this = (BossTw*)thisx; + ColliderCylinder* collider; + s16 i; + + this->work[CS_TIMER_1]++; + this->work[CS_TIMER_2]++; + this->work[TAIL_IDX]++; + + if (this->work[TAIL_IDX] > 29) { + this->work[TAIL_IDX] = 0; + } + + this->blastTailPos[this->work[TAIL_IDX]] = this->actor.world.pos; + + this->actionFunc(this, globalCtx); + + for (i = 0; i < 5; i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + if (this->work[INVINC_TIMER] != 0) { + this->work[INVINC_TIMER]--; + } + + if (this->work[BURN_TMR] != 0) { + this->work[BURN_TMR]--; + } + + this->actor.focus.pos = this->actor.world.pos; + collider = &this->collider; + Collider_UpdateCylinder(&this->actor, collider); + + if (this->blastActive && this->work[INVINC_TIMER] == 0 && !BossTw_BlastShieldCheck(this, globalCtx)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &collider->base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &collider->base); + } + + this->blastActive = false; +} + +void BossTw_BlastDraw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossTw* this = (BossTw*)thisx; + f32 scaleFactor; + s16 tailIdx; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 8818); + + func_80093D84(globalCtx->state.gfxCtx); + + switch (this->actor.params) { + case TW_FIRE_BLAST: + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s8)this->workf[TAIL_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + for (i = 9; i >= 0; i--) { + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll( + globalCtx->state.gfxCtx, 0, ((this->work[CS_TIMER_1] * 3) + (i * 10)) & 0x7F, + ((-this->work[CS_TIMER_1] * 15) + (i * 50)) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30; + Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y, + this->blastTailPos[tailIdx].z, MTXMODE_NEW); + scaleFactor = 1.0f - (i * 0.09f); + Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor, + this->actor.scale.z * scaleFactor, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 8865), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430)); + } + break; + + case TW_FIRE_BLAST_GROUND: + break; + + case TW_ICE_BLAST: + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s8)this->workf[TAIL_ALPHA]); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998)); + for (i = 9; i >= 0; i--) { + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll( + globalCtx->state.gfxCtx, 0, ((this->work[CS_TIMER_1] * 3) + (i * 0xA)) & 0x7F, + (u8)((-this->work[CS_TIMER_1] * 0xF) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30; + Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y, + this->blastTailPos[tailIdx].z, MTXMODE_NEW); + scaleFactor = 1.0f - (i * 0.09f); + Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor, + this->actor.scale.z * scaleFactor, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 9004), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00)); + } + break; + + case TW_ICE_BLAST_GROUND: + break; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 9013); +} + +void BossTw_DrawDeathBall(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossTw* this = (BossTw*)thisx; + f32 scaleFactor; + s16 tailIdx; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 9028); + + func_80093D84(globalCtx->state.gfxCtx); + + if (this->actor.params == TW_DEATHBALL_KOUME) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s8)this->workf[TAIL_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + + for (i = 9; i >= 0; i--) { + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (((this->work[CS_TIMER_1] * 3) + (i * 0xA))) & 0x7F, + (u8)((-this->work[CS_TIMER_1] * 0xF) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20, + 0x20)); + tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30; + Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y, + this->blastTailPos[tailIdx].z, MTXMODE_NEW); + scaleFactor = (1.0f - (i * 0.09f)); + Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor, + this->actor.scale.z * scaleFactor, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 9071), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430)); + } + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, (s8)this->workf[TAIL_ALPHA]); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998)); + + for (i = 9; i >= 0; i--) { + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (((this->work[CS_TIMER_1] * 3) + (i * 0xA))) & 0x7F, + (u8)((-this->work[CS_TIMER_1] * 0xF) + (i * 50)), 0x20, 0x40, 1, 0, 0, 0x20, + 0x20)); + tailIdx = ((this->work[TAIL_IDX] - i) + 30) % 30; + Matrix_Translate(this->blastTailPos[tailIdx].x, this->blastTailPos[tailIdx].y, + this->blastTailPos[tailIdx].z, MTXMODE_NEW); + scaleFactor = (1.0f - (i * 0.09f)); + Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor, + this->actor.scale.z * scaleFactor, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_tw.c", 9107), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00)); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_tw.c", 9111); +} + +void BossTw_UpdateEffects(GlobalContext* globalCtx) { + static Color_RGB8 sDotColors[] = { + { 255, 128, 0 }, { 255, 0, 0 }, { 255, 255, 0 }, { 255, 0, 0 }, + { 100, 100, 100 }, { 255, 255, 255 }, { 150, 150, 150 }, { 255, 255, 255 }, + }; + Vec3f sp11C; + BossTwEffect* eff = globalCtx->specialEffects; + Player* player = GET_PLAYER(globalCtx); + u8 sp113 = 0; + s16 i; + s16 j; + s16 colorIdx; + Vec3f off; + Vec3f spF4; + Vec3f spE8; + Vec3f spDC; + Vec3f spD0; + f32 phi_f22; + Vec3f spC0; + Vec3f spB4; + Vec3f spA8; + s16 spA6; + f32 phi_f0; + Actor* unk44; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + if (eff->type != 0) { + eff->pos.x += eff->curSpeed.x; + eff->pos.y += eff->curSpeed.y; + eff->pos.z += eff->curSpeed.z; + eff->frame++; + eff->curSpeed.x += eff->accel.x; + eff->curSpeed.y += eff->accel.y; + eff->curSpeed.z += eff->accel.z; + + if (eff->type == 1) { + colorIdx = eff->frame % 4; + + if (eff->work[EFF_ARGS] == 0) { + colorIdx += 4; + } + + eff->color.r = sDotColors[colorIdx].r; + eff->color.g = sDotColors[colorIdx].g; + eff->color.b = sDotColors[colorIdx].b; + eff->alpha -= 20; + + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + + } else if ((eff->type == 3) || (eff->type == 2)) { + if (eff->work[EFF_ARGS] == 2) { + eff->alpha -= 20; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } else if (eff->work[EFF_ARGS] == 0) { + eff->alpha += 10; + if (eff->alpha >= 100) { + eff->work[EFF_ARGS]++; + } + } else { + eff->alpha -= 3; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } + } else if (eff->type == TWEFF_FLAME) { + if (eff->work[EFF_UNKS1] != 0) { + eff->alpha = (eff->alpha - (i & 7)) - 0xD; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } else { + eff->alpha += 300; + if (eff->alpha >= 255) { + eff->alpha = 255; + eff->work[EFF_UNKS1]++; + } + } + } else if (eff->type == TWEFF_SHLD_BLST) { + D_8094C870 = 1; + eff->work[EFF_UNKS1]++; + if (eff->work[EFF_UNKS1] > 30) { + eff->alpha -= 10; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } + + Math_ApproachF(&eff->workf[EFF_SCALE], eff->workf[EFF_DIST], 0.1f, 0.003f); + off.x = sTwinrovaPtr->actor.world.pos.x - eff->pos.x; + off.y = (sTwinrovaPtr->actor.world.pos.y - eff->pos.y) * 0.5f; + off.z = sTwinrovaPtr->actor.world.pos.z - eff->pos.z; + + if (sTwinrovaPtr->actionFunc != BossTw_TwinrovaStun) { + if ((SQ(off.x) + SQ(off.y) + SQ(off.z)) < SQ(60.0f)) { + for (j = 0; j < 50; j++) { + spF4.x = sTwinrovaPtr->actor.world.pos.x + Rand_CenteredFloat(35.0f); + spF4.y = sTwinrovaPtr->actor.world.pos.y + Rand_CenteredFloat(70.0f); + spF4.z = sTwinrovaPtr->actor.world.pos.z + Rand_CenteredFloat(35.0f); + spE8.x = Rand_CenteredFloat(20.0f); + spE8.y = Rand_CenteredFloat(20.0f); + spE8.z = Rand_CenteredFloat(20.0f); + spDC.x = 0.0f; + spDC.y = 0.0f; + spDC.z = 0.0f; + BossTw_AddFlameEffect(globalCtx, &spF4, &spE8, &spDC, Rand_ZeroFloat(10.0f) + 25.0f, + eff->work[EFF_ARGS]); + } + + sTwinrovaPtr->twinrovaStun = 1; + globalCtx->envCtx.unk_D8 = 1.0f; + eff->type = TWEFF_NONE; + } + } + } else if (eff->type == TWEFF_MERGEFLAME) { + sp11C.x = 0.0f; + sp11C.y = eff->pos.y; + sp11C.z = eff->workf[EFF_DIST]; + Matrix_RotateY(sTwinrovaPtr->workf[UNK_F9] + eff->workf[EFF_ROLL], MTXMODE_NEW); + Matrix_MultVec3f(&sp11C, &eff->pos); + + if (eff->work[EFF_UNKS1] != 0) { + eff->alpha -= 60; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } else { + eff->alpha += 60; + if (eff->alpha >= 255) { + eff->alpha = 255; + eff->work[EFF_UNKS1]++; + } + } + } else if (eff->type == TWEFF_SHLD_DEFL) { + eff->work[EFF_UNKS1]++; + sp11C.x = 0.0f; + sp11C.y = 0.0f; + sp11C.z = -eff->workf[EFF_DIST]; + Matrix_RotateY((sShieldHitYaw / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_RotateX(-0.2f, MTXMODE_APPLY); + Matrix_RotateZ(eff->workf[EFF_ROLL], MTXMODE_APPLY); + Matrix_RotateY(eff->workf[EFF_YAW], MTXMODE_APPLY); + Matrix_MultVec3f(&sp11C, &eff->pos); + eff->pos.x += sShieldHitPos.x; + eff->pos.y += sShieldHitPos.y; + eff->pos.z += sShieldHitPos.z; + + if (eff->work[EFF_UNKS1] < 10) { + Math_ApproachF(&eff->workf[EFF_DIST], 50.0f, 0.5f, 100.0f); + } else { + Math_ApproachF(&eff->workf[EFF_YAW], 0.0f, 0.5f, 10.0f); + Math_ApproachF(&eff->workf[EFF_DIST], 1000.0f, 1.0f, 10.0f); + if (eff->work[EFF_UNKS1] >= 0x10) { + if ((eff->work[EFF_UNKS1] == 16) && (sp113 == 0)) { + sp113 = 1; + spD0 = eff->pos; + if (eff->pos.y > 40.0f) { + spD0.y = 220.0f; + } else { + spD0.y = -50.0f; + } + sTwinrovaPtr->groundBlastPos.y = phi_f0 = BossTw_GetFloorY(&spD0); + if (phi_f0 >= 0.0f) { + if (sTwinrovaPtr->groundBlastPos.y != 35.0f) { + sTwinrovaPtr->groundBlastPos.x = eff->pos.x; + sTwinrovaPtr->groundBlastPos.z = eff->pos.z; + BossTw_SpawnGroundBlast(sTwinrovaPtr, globalCtx, eff->work[EFF_ARGS]); + } + } + } + eff->alpha -= 300; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } + } + + BossTw_AddFlameEffect(globalCtx, &eff->pos, &sZeroVector, &sZeroVector, 10, eff->work[EFF_ARGS]); + } else if (eff->type == TWEFF_SHLD_HIT) { + eff->work[EFF_UNKS1]++; + sp11C.x = 0.0f; + sp11C.y = 0.0f; + sp11C.z = -eff->workf[EFF_DIST]; + Matrix_RotateY((sShieldHitYaw / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_RotateX(-0.2f, MTXMODE_APPLY); + Matrix_RotateZ(eff->workf[EFF_ROLL], MTXMODE_APPLY); + Matrix_RotateY(eff->workf[EFF_YAW], MTXMODE_APPLY); + Matrix_MultVec3f(&sp11C, &eff->pos); + eff->pos.x += sShieldHitPos.x; + eff->pos.y += sShieldHitPos.y; + eff->pos.z += sShieldHitPos.z; + + if (eff->work[EFF_UNKS1] < 5) { + Math_ApproachF(&eff->workf[EFF_DIST], 40.0f, 0.5f, 100.0f); + } else { + Math_ApproachF(&eff->workf[EFF_DIST], 0.0f, 0.2f, 5.0f); + if (eff->work[EFF_UNKS1] >= 11) { + eff->alpha -= 30; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } + } + + BossTw_AddFlameEffect(globalCtx, &eff->pos, &sZeroVector, &sZeroVector, 10, eff->work[EFF_ARGS]); + } else if (eff->type == 4) { + if (eff->work[EFF_UNKS1] == 0) { + Math_ApproachF(&eff->workf[EFF_SCALE], eff->workf[EFF_DIST], 0.05f, 1.0f); + + if (eff->frame >= 16) { + eff->alpha -= 10; + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } + } else { + Math_ApproachF(&eff->workf[EFF_SCALE], eff->workf[EFF_DIST], 0.1f, 2.0f); + eff->alpha -= 15; + + if (eff->alpha <= 0) { + eff->alpha = 0; + eff->type = TWEFF_NONE; + } + } + } else if (eff->type == TWEFF_PLYR_FRZ) { + if (eff->work[EFF_ARGS] < eff->frame) { + phi_f0 = 1.0f; + + if (eff->target != NULL || sGroundBlastType == 1) { + phi_f0 *= 3.0f; + } + + Math_ApproachF(&eff->workf[EFF_SCALE], 0.0f, 1.0f, 0.0005f * phi_f0); + + if (eff->workf[EFF_SCALE] == 0.0f) { + eff->type = TWEFF_NONE; + if (eff->target == NULL) { + player->stateFlags2 &= ~0x8000; + sFreezeState = 0; + } + } + } else { + if (sGroundBlastType == 1) { + eff->frame = 100; + } + Math_ApproachF(&eff->workf[EFF_DIST], 0.8f, 0.2f, 0.04f); + + if (eff->target == NULL) { + Math_ApproachF(&eff->workf[EFF_SCALE], 0.012f, 1.0f, 0.002f); + eff->workf[EFF_ROLL] += eff->workf[EFF_DIST]; + + if (eff->workf[EFF_ROLL] >= 0.8f) { + eff->workf[EFF_ROLL] -= 0.8f; + player->stateFlags2 |= 0x8000; + } else { + player->stateFlags2 &= ~0x8000; + } + + if ((sKotakePtr->workf[UNK_F11] > 10.0f) && (sKotakePtr->workf[UNK_F11] < 200.0f)) { + eff->frame = 100; + } + + if (!(globalCtx->gameplayFrames & 1)) { + globalCtx->damagePlayer(globalCtx, -1); + } + } else { + Math_ApproachF(&eff->workf[EFF_SCALE], 0.042f, 1.0f, 0.002f); + } + + if ((eff->workf[EFF_DIST] > 0.4f) && ((eff->frame & 7) == 0)) { + spA6 = Rand_ZeroFloat(17.9f); + + if (eff->target == NULL) { + spC0.x = player->bodyPartsPos[spA6].x + Rand_CenteredFloat(5.0f); + spC0.y = player->bodyPartsPos[spA6].y + Rand_CenteredFloat(5.0f); + spC0.z = player->bodyPartsPos[spA6].z + Rand_CenteredFloat(5.0f); + phi_f22 = 10.0f; + } else { + unk44 = eff->target; + spC0.x = unk44->world.pos.x + Rand_CenteredFloat(40.0f); + spC0.y = unk44->world.pos.y + Rand_CenteredFloat(40.0f); + spC0.z = unk44->world.pos.z + Rand_CenteredFloat(40.0f); + phi_f22 = 20.0f; + } + + spB4.x = 0.0f; + spB4.y = 0.0f; + spB4.z = 0.0f; + spA8.x = 0.0f; + spA8.y = 0.1f; + spA8.z = 0.0f; + + BossTw_AddDmgCloud(globalCtx, 3, &spC0, &spB4, &spA8, phi_f22 + Rand_ZeroFloat(phi_f22 * 0.5f), + 0, 0, 150); + } + } + } + } + eff++; + } +} + +static s32 sRandSeed0; +static s32 sRandSeed1; +static s32 sRandSeed2; + +void BossTw_InitRand(s32 seed0, s32 seed1, s32 seed2) { + sRandSeed0 = seed0; + sRandSeed1 = seed1; + sRandSeed2 = seed2; +} + +f32 BossTw_RandZeroOne(void) { + f32 rand; + + // Wichmann-Hill algorithm + sRandSeed0 = (sRandSeed0 * 171) % 30269; + sRandSeed1 = (sRandSeed1 * 172) % 30307; + sRandSeed2 = (sRandSeed2 * 170) % 30323; + + rand = (sRandSeed0 / 30269.0f) + (sRandSeed1 / 30307.0f) + (sRandSeed2 / 30323.0f); + while (rand >= 1.0f) { + rand -= 1.0f; + } + + return fabsf(rand); +} + +void BossTw_DrawEffects(GlobalContext* globalCtx) { + u8 sp18F = 0; + s16 i; + s16 j; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s16 phi_s4; + BossTwEffect* currentEffect = globalCtx->specialEffects; + BossTwEffect* effectHead; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + effectHead = currentEffect; + + OPEN_DISPS(gfxCtx, "../z_boss_tw.c", 9592); + + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + if (currentEffect->type == 1) { + if (sp18F == 0) { + gSPDisplayList(POLY_XLU_DISP++, object_tw_DL_01A528); + sp18F++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, currentEffect->color.r, currentEffect->color.g, + currentEffect->color.b, currentEffect->alpha); + Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_tw.c", 9617), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_tw_DL_01A5A8); + } + + currentEffect++; + } + + sp18F = 0; + currentEffect = effectHead; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + if (currentEffect->type == 3) { + if (sp18F == 0) { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998)); + sp18F++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, currentEffect->alpha); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F, + (currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_tw.c", 9660), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00)); + } + currentEffect++; + } + + sp18F = 0; + currentEffect = effectHead; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + if (currentEffect->type == 2) { + if (sp18F == 0) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + sp18F++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, currentEffect->alpha); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F, + (currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_tw.c", 9709), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430)); + } + + currentEffect++; + } + + sp18F = 0; + currentEffect = effectHead; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + if (currentEffect->type == 4) { + if (sp18F == 0) { + sp18F++; + } + + gSPSegment(POLY_XLU_DISP++, 0xD, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, currentEffect->frame & 0x7F, + (currentEffect->frame * 8) & 0xFF, 0x20, 0x40, 1, + (currentEffect->frame * -2) & 0x7F, 0, 0x10, 0x10)); + + if (currentEffect->work[EFF_ARGS] == 1) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 65, 0, currentEffect->alpha); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 128); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, currentEffect->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 128); + } + + Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + if (currentEffect->work[EFF_UNKS1] == 0) { + Matrix_Translate(0.0f, 0.0f, 60.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + + Matrix_RotateZ(currentEffect->workf[EFF_ROLL], MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + Matrix_Scale(currentEffect->workf[EFF_SCALE], 1.0f, currentEffect->workf[EFF_SCALE], MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_tw.c", 9775), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2); + gSPClearGeometryMode(POLY_XLU_DISP++, G_CULL_BACK | G_FOG); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A790)); + } + + currentEffect++; + } + + sp18F = 0; + currentEffect = effectHead; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + Actor* actor; + Vec3f off; + + if (currentEffect->type == TWEFF_PLYR_FRZ) { + if (sp18F == 0) { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AA50)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, 255); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + sp18F++; + BossTw_InitRand(1, 0x71AC, 0x263A); + } + + actor = currentEffect->target; + phi_s4 = actor == NULL ? 70 : 20; + + for (j = 0; j < phi_s4; j++) { + off.x = (BossTw_RandZeroOne() - 0.5f) * 30.0f; + off.y = currentEffect->workf[EFF_DIST] * j; + off.z = (BossTw_RandZeroOne() - 0.5f) * 30.0f; + + if (actor != NULL) { + Matrix_Translate(actor->world.pos.x + off.x, actor->world.pos.y + off.y, actor->world.pos.z + off.z, + MTXMODE_NEW); + } else { + Matrix_Translate(player->actor.world.pos.x + off.x, player->actor.world.pos.y + off.y, + player->actor.world.pos.z + off.z, MTXMODE_NEW); + } + + Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], + currentEffect->workf[EFF_SCALE], MTXMODE_APPLY); + Matrix_RotateY(BossTw_RandZeroOne() * M_PI, MTXMODE_APPLY); + Matrix_RotateX((BossTw_RandZeroOne() - 0.5f) * M_PI * 0.5f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_tw.c", 9855), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00)); + } + } + + currentEffect++; + } + + sp18F = 0; + currentEffect = effectHead; + + for (i = 0; i < ARRAY_COUNT(sTwEffects); i++) { + if (currentEffect->type >= 6) { + if (currentEffect->work[EFF_ARGS] == 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, currentEffect->alpha); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A998)); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, currentEffect->alpha); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + } + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (currentEffect->frame * 3) & 0x7F, + (-currentEffect->frame * 15) & 0xFF, 0x20, 0x40, 1, 0, 0, 0x20, 0x20)); + Matrix_Translate(currentEffect->pos.x, currentEffect->pos.y, currentEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(currentEffect->workf[EFF_SCALE], currentEffect->workf[EFF_SCALE], 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_tw.c", 9911), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (currentEffect->work[EFF_ARGS] == 0) { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01AB00)); + } else { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_tw_DL_01A430)); + } + } + + currentEffect++; + } + + CLOSE_DISPS(gfxCtx, "../z_boss_tw.c", 9920); +} + +void BossTw_TwinrovaSetupArriveAtTarget(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaArriveAtTarget; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -3.0f); + this->work[CS_TIMER_1] = Rand_ZeroFloat(100.0f); + this->timers[1] = 25; + this->rotateSpeed = 0.0f; +} + +void BossTw_TwinrovaArriveAtTarget(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.1f, fabsf(this->actor.velocity.x) * 1.5f); + Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.1f, fabsf(this->actor.velocity.y) * 1.5f); + Math_ApproachF(&this->targetPos.y, 380.0f, 1.0f, 2.0f); + Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.1f, fabsf(this->actor.velocity.z) * 1.5f); + + if (this->timers[1] == 1) { + BossTw_TwinrovaSetupChargeBlast(this, globalCtx); + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->rotateSpeed); + Math_ApproachF(&this->rotateSpeed, 4096.0f, 1.0f, 350.0f); +} + +void BossTw_TwinrovaSetupChargeBlast(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaChargeBlast; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_036FBC, -5.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_036FBC); + this->csState1 = 0; +} + +void BossTw_TwinrovaChargeBlast(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.03f, fabsf(this->actor.velocity.x) * 1.5f); + Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.03f, fabsf(this->actor.velocity.y) * 1.5f); + Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.03f, fabsf(this->actor.velocity.z) * 1.5f); + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + if ((s8)this->actor.colChkInfo.health < 10) { + sTwinrovaBlastType = Rand_ZeroFloat(1.99f); + } else { + if (++sFixedBlatSeq >= 4) { + sFixedBlatSeq = 1; + sFixedBlastType = !sFixedBlastType; + } + + sTwinrovaBlastType = sFixedBlastType; + } + + BossTw_TwinrovaSetupShootBlast(this, globalCtx); + } +} + +void BossTw_TwinrovaSetupShootBlast(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaShootBlast; + + if (sTwinrovaBlastType == 0) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_022700, 0.0f); + } else { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_023750, 0.0f); + } + + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_023750); +} + +void BossTw_TwinrovaShootBlast(BossTw* this, GlobalContext* globalCtx) { + BossTw* twMagic; + Vec3f* magicSpawnPos; + s32 magicParams; + s16 i; + + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 8.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_THROW_MASIC); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_SHOOTVC); + } + + if (Animation_OnFrame(&this->skelAnime, 12.0f)) { + if (sTwinrovaBlastType != 0) { + magicParams = TW_FIRE_BLAST; + magicSpawnPos = &this->rightScepterPos; + } else { + magicParams = TW_ICE_BLAST; + magicSpawnPos = &this->leftScepterPos; + } + + twMagic = + (BossTw*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_TW, magicSpawnPos->x, + magicSpawnPos->y, magicSpawnPos->z, 0, 0, 0, magicParams); + + if (twMagic != NULL) { + twMagic->blastType = magicParams == TW_ICE_BLAST ? 0 : 1; + } + + sEnvType = twMagic->blastType + 1; + + { + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + for (i = 0; i < 100; i++) { + velocity.x = Rand_CenteredFloat(30.0f); + velocity.y = Rand_CenteredFloat(30.0f); + velocity.z = Rand_CenteredFloat(30.0f); + BossTw_AddDotEffect(globalCtx, magicSpawnPos, &velocity, &accel, (s16)Rand_ZeroFloat(2.0f) + 11, + twMagic->blastType, 75); + } + } + } + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + BossTw_TwinrovaSetupDoneBlastShoot(this, globalCtx); + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); +} + +void BossTw_TwinrovaSetupDoneBlastShoot(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaDoneBlastShoot; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -10.0f); + this->timers[1] = 60; +} + +void BossTw_TwinrovaDoneBlastShoot(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->timers[1] == 0 && D_8094C870 == 0) { + if (sTwinrovaPtr->timers[2] == 0) { + BossTw_TwinrovaSetupFly(this, globalCtx); + } else { + BossTw_TwinrovaSetupLaugh(this, globalCtx); + } + } + + D_8094C870 = 0; + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x1000); +} + +void BossTw_TwinrovaDamage(BossTw* this, GlobalContext* globalCtx, u8 damage) { + if (this->actionFunc != BossTw_TwinrovaStun) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_0338F0, -15.0f); + this->timers[0] = 150; + this->timers[1] = 20; + this->csState1 = 0; + this->actor.velocity.y = 0.0f; + } else { + this->work[FOG_TIMER] = 10; + this->work[INVINC_TIMER] = 20; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_024374, -3.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_024374); + this->csState1 = 1; + + if ((s8)(this->actor.colChkInfo.health -= damage) < 0) { + this->actor.colChkInfo.health = 0; + } + + if ((s8)this->actor.colChkInfo.health <= 0) { + BossTw_TwinrovaSetupDeathCS(this, globalCtx); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD); + return; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DAMAGE2); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_CUTBODY); + } + + this->actionFunc = BossTw_TwinrovaStun; +} + +void BossTw_TwinrovaStun(BossTw* this, GlobalContext* globalCtx) { + s16 cloudType; + + this->unk_5F8 = 1; + this->actor.flags |= ACTOR_FLAG_10; + + cloudType = sTwinrovaBlastType == 0 ? 3 : 2; + + if ((this->work[CS_TIMER_1] % 8) == 0) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(20.0f); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(40.0f) + 20; + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(20.0f); + velocity.x = 0.0f; + velocity.y = 0.0f; + velocity.z = 0.0f; + accel.x = 0.0f; + accel.y = 0.1f; + accel.z = 0.0f; + BossTw_AddDmgCloud(globalCtx, cloudType, &pos, &velocity, &accel, Rand_ZeroFloat(5.0f) + 10.0f, 0, 0, 150); + } + + SkelAnime_Update(&this->skelAnime); + this->work[UNK_S8] += 20; + + if (this->work[UNK_S8] > 255) { + this->work[UNK_S8] = 255; + } + + Math_ApproachF(&this->workf[UNK_F12], 0.0f, 1.0f, 0.05f); + this->actor.world.pos.y += this->actor.velocity.y; + Math_ApproachF(&this->actor.velocity.y, -5.0f, 1.0f, 0.5f); + this->actor.world.pos.y -= 30.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4); + this->actor.world.pos.y += 30.0f; + + if (this->csState1 == 0) { + if (this->timers[1] == 0) { + this->csState1 = 1; + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_0343B4); + Animation_Change(&this->skelAnime, &object_tw_Anim_0343B4, 1.0f, 0.0f, this->workf[ANIM_SW_TGT], 3, 0.0f); + } + } else if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + this->workf[ANIM_SW_TGT] = 1000.0f; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_035030, 0.0f); + } + + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 0.0f; + } + + if (this->timers[0] == 0) { + BossTw_TwinrovaSetupGetUp(this, globalCtx); + } +} + +void BossTw_TwinrovaSetupGetUp(BossTw* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_035988, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_035988); + this->actionFunc = BossTw_TwinrovaGetUp; + this->timers[0] = 50; +} + +void BossTw_TwinrovaGetUp(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.05f, 5.0f); + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + this->workf[ANIM_SW_TGT] = 1000.0f; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, 0.0f); + } + + if (this->timers[0] == 0) { + BossTw_TwinrovaSetupFly(this, globalCtx); + } +} + +void BossTw_TwinrovaSetupFly(BossTw* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 zDiff; + f32 yDiff; + f32 xzDist; + Player* player = GET_PLAYER(globalCtx); + + do { + this->work[TW_PLLR_IDX] += (s16)(((s16)Rand_ZeroFloat(2.99f)) + 1); + this->work[TW_PLLR_IDX] %= 4; + this->targetPos = sTwinrovaPillarPos[this->work[TW_PLLR_IDX]]; + xDiff = this->targetPos.x - player->actor.world.pos.x; + zDiff = this->targetPos.z - player->actor.world.pos.z; + xzDist = SQ(xDiff) + SQ(zDiff); + } while (!(xzDist > SQ(300.0f))); + + this->targetPos.y = 480.0f; + xDiff = this->targetPos.x - this->actor.world.pos.x; + yDiff = this->targetPos.y - this->actor.world.pos.y; + zDiff = this->targetPos.z - this->actor.world.pos.z; + this->actionFunc = BossTw_TwinrovaFly; + this->rotateSpeed = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = Math_FAtan2F(xDiff, zDiff) * (32768 / M_PI); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + this->actor.world.rot.x = Math_FAtan2F(yDiff, xzDist) * (32768 / M_PI); + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, -10.0f); +} + +void BossTw_TwinrovaFly(BossTw* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 yDiff; + f32 zDiff; + s32 pad; + f32 yaw; + f32 xzDist; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_FLY - SFX_FLAG); + SkelAnime_Update(&this->skelAnime); + xDiff = this->targetPos.x - this->actor.world.pos.x; + yDiff = this->targetPos.y - this->actor.world.pos.y; + zDiff = this->targetPos.z - this->actor.world.pos.z; + // Convert from radians to degrees, then degrees to binary angle + yaw = (s16)(Math_FAtan2F(xDiff, zDiff) * ((180.0f / M_PI) * (65536.0f / 360.0f))); + xzDist = sqrtf(SQ(xDiff) + SQ(zDiff)); + Math_ApproachS(&this->actor.world.rot.x, + (f32)(s16)(Math_FAtan2F(yDiff, xzDist) * ((180.0f / M_PI) * (65536.0f / 360.0f))), 0xA, + this->rotateSpeed); + Math_ApproachS(&this->actor.world.rot.y, yaw, 0xA, this->rotateSpeed); + Math_ApproachS(&this->actor.shape.rot.y, yaw, 0xA, this->rotateSpeed); + Math_ApproachF(&this->rotateSpeed, 2000.0f, 1.0f, 100.0f); + Math_ApproachF(&this->actor.speedXZ, 30.0f, 1.0f, 2.0f); + func_8002D908(&this->actor); + Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.1f, fabsf(this->actor.velocity.x) * 1.5f); + Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.1f, fabsf(this->actor.velocity.y) * 1.5f); + Math_ApproachF(&this->targetPos.y, 380.0f, 1.0f, 2.0f); + Math_ApproachF(&this->actor.world.pos.z, this->targetPos.z, 0.1f, fabsf(this->actor.velocity.z) * 1.5f); + + if (xzDist < 200.0f) { + BossTw_TwinrovaSetupArriveAtTarget(this, globalCtx); + } +} + +void BossTw_TwinrovaSetupSpin(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaSpin; + Animation_MorphToLoop(&this->skelAnime, &object_tw_Anim_032BF8, 0.0f); + this->timers[0] = 20; + this->actor.speedXZ = 0.0f; +} + +void BossTw_TwinrovaSpin(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timers[0] != 0) { + this->collider.base.colType = COLTYPE_METAL; + this->actor.shape.rot.y -= 0x3000; + + if ((this->timers[0] % 4) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_ROLL); + } + } else { + BossTw_TwinrovaSetupFly(this, globalCtx); + } +} + +void BossTw_TwinrovaSetupLaugh(BossTw* this, GlobalContext* globalCtx) { + this->actionFunc = BossTw_TwinrovaLaugh; + Animation_MorphToPlayOnce(&this->skelAnime, &object_tw_Anim_03A2D0, 0.0f); + this->workf[ANIM_SW_TGT] = Animation_GetLastFrame(&object_tw_Anim_03A2D0); + this->actor.speedXZ = 0.0f; +} + +void BossTw_TwinrovaLaugh(BossTw* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 10.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_LAUGH); + } + + if (Animation_OnFrame(&this->skelAnime, this->workf[ANIM_SW_TGT])) { + BossTw_TwinrovaSetupFly(this, globalCtx); + } +} + +void BossTw_Reset(void) { + sTwInitalized = false; + memset(sTwEffects, 0, sizeof(sTwEffects)); +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h new file mode 100644 index 000000000..575997c26 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.h @@ -0,0 +1,138 @@ +#ifndef Z_BOSS_TW_H +#define Z_BOSS_TW_H + +#include "ultra64.h" +#include "global.h" + +struct BossTw; + +typedef void (*BossTwActionFunc)(struct BossTw* this, GlobalContext* globalCtx); + +typedef enum { + /* 0 */ CS_TIMER_1, + /* 1 */ CS_TIMER_2, + /* 2 */ TW_PLLR_IDX, + /* 3 */ TAIL_IDX, + /* 4 */ BLINK_IDX, + /* 5 */ INVINC_TIMER, + /* 6 */ FOG_TIMER, + /* 7 */ CAN_SHOOT, + /* 8 */ UNK_S8, + /* 9 */ TW_BLINK_IDX, + /* 10 */ YAW_TGT, + /* 11 */ PLAYED_CHRG_SFX, + /* 12 */ BURN_TMR, + /* 13 */ WORK_MAX +} TwWork; + +typedef enum { + /* 0 */ OUTR_CRWN_TX_X1, + /* 1 */ OUTR_CRWN_TX_X2, + /* 2 */ INNR_CRWN_TX_X1, + /* 3 */ INNR_CRWN_TX_X2, + /* 4 */ OUTR_CRWN_TX_Y1, + /* 5 */ OUTR_CRWN_TX_Y2, + /* 6 */ INNR_CRWN_TX_Y1, + /* 7 */ INNR_CRWN_TX_Y2, + /* 8 */ ANIM_SW_TGT, + /* 9 */ UNK_F9, + /* 9 */ KM_GD_FLM_A = 9, + /* 10 */ UNK_F10 = 10, + /* 10 */ TAIL_ALPHA = 10, + /* 10 */ KM_GD_SMOKE_A = 10, + /* 14 */ UNK_F11 = 11, + /* 15 */ KM_GRND_CRTR_A = 11, + /* 16 */ UNK_F12 = 12, + /* 17 */ KM_GD_FLM_SCL = 12, + /* 18 */ UNK_F13 = 13, + /* 19 */ KM_GD_CRTR_SCL = 13, + /* 20 */ UNK_F14, + /* 21 */ UNK_F15, + /* 22 */ UNK_F16, + /* 23 */ UNK_F17, + /* 24 */ UNK_F18, + /* 25 */ UNK_F19, + /* 26 */ FWORK_MAX +} TwFwork; + +typedef struct BossTw { + /* 0x0000 */ Actor actor; + /* 0x014C */ BossTwActionFunc actionFunc; + /* 0x0150 */ s16 work[WORK_MAX]; + /* 0x0168 */ char unused_170[0xE]; // Likely unused Work variables + /* 0x0178 */ s16 timers[5]; + /* 0x0184 */ f32 workf[FWORK_MAX]; + /* 0x01D4 */ f32 fogR; + /* 0x01D8 */ f32 fogG; + /* 0x01DC */ f32 fogB; + /* 0x01E0 */ f32 fogNear; + /* 0x01E4 */ f32 fogFar; + /* 0x01E8 */ Vec3f blastTailPos[50]; + /* 0x0440 */ s16 csState1; + /* 0x0444 */ Vec3f crownPos; + /* 0x0450 */ Vec3f scepterFlamePos[5]; + /* 0x048C */ Vec3f beamOrigin; + /* 0x0498 */ Vec3f leftScepterPos; + /* 0x04A4 */ Vec3f rightScepterPos; + /* 0x04B0 */ Vec3f targetPos; + /* 0x04BC */ Vec3f groundBlastPos2; + /* 0x04C8 */ f32 rotateSpeed; + /* 0x04CC */ s16 eyeTexIdx; + /* 0x04CE */ s16 leftEyeTexIdx; + /* 0x04D0 */ f32 scepterAlpha; + /* 0x04D4 */ f32 flameAlpha; + /* 0x04D8 */ f32 spawnPortalAlpha; + /* 0x04DC */ f32 unk_4DC; + /* 0x04E0 */ f32 spawnPortalScale; + /* 0x04E4 */ f32 updateRate1; + /* 0x04E8 */ f32 flameRotation; + /* 0x04EC */ f32 portalRotation; + /* 0x04F0 */ f32 updateRate2; + /* 0x04F4 */ u8 twinrovaStun; + /* 0x04F8 */ f32 beamScale; + /* 0x04FC */ s16 beamShootState; + /* 0x0500 */ Vec3f groundBlastPos; + /* 0x050C */ Vec3f beamReflectionOrigin; + /* 0x0518 */ f32 beamPitch; + /* 0x051C */ f32 beamYaw; + /* 0x0520 */ f32 beamRoll; + /* 0x0524 */ Vec3s magicDir; + /* 0x052C */ f32 beamDist; + /* 0x0530 */ Vec3f unk_530; + /* 0x053C */ f32 beamReflectionPitch; + /* 0x0540 */ f32 beamReflectionYaw; + /* 0x0544 */ f32 unused_544; + /* 0x0548 */ f32 beamReflectionDist; + /* 0x054C */ Vec3f unk_54C; + /* 0x0558 */ Vec3f unk_558; + /* 0x0564 */ u8 visible; + /* 0x0565 */ u8 blastActive; + /* 0x0566 */ s16 blastType; + /* 0x0568 */ SkelAnime skelAnime; + /* 0x05AC */ ColliderCylinder collider; + /* 0x05F8 */ u8 unk_5F8; + /* 0x05F9 */ u8 unk_5F9; + /* 0x05FA */ s16 csState2; + /* 0x05FC */ s16 subCamId; + /* 0x05FE */ s16 csSfxTimer; + /* 0x0600 */ Vec3f subCamEye; + /* 0x060C */ Vec3f subCamAt; + /* 0x0618 */ char unused_618[0xC]; + /* 0x0624 */ Vec3f subCamEye2; + /* 0x0630 */ Vec3f subCamAt2; + /* 0x063C */ char unused_63C[0x18]; + /* 0x0654 */ Vec3f subCamEyeStep; + /* 0x0660 */ Vec3f subCamAtStep; + /* 0x066C */ Vec3f subCamEyeTarget; + /* 0x0678 */ char unused_678[0xC]; + /* 0x0684 */ Vec3f subCamAtTarget; + /* 0x0690 */ char unused_690[0xC]; + /* 0x069C */ f32 subCamUpdateRate; + /* 0x06A0 */ f32 subCamDistStep; + /* 0x06A4 */ f32 subCamDist; + /* 0x06A8 */ char unused_6A8[4]; + /* 0x06AC */ f32 subCamYaw; + /* 0x06B0 */ f32 subCamYawStep; +} BossTw; // size = 0x06B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c new file mode 100644 index 000000000..071baa106 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -0,0 +1,4031 @@ +/* + * File: z_boss_va.c + * Overlay: ovl_Boss_Va + * Description: Barinade + */ + +#include "z_boss_va.h" + +#include + +#include "objects/object_bv/object_bv.h" +#include "overlays/actors/ovl_En_Boom/z_en_boom.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +#define GET_BODY(this) ((BossVa*)(this)->actor.parent) +#define vaGorePulse offset.x +#define vaGorePulseRate offset.y +#define vaBariUnused headRot +#define vaCamRotMod headRot.x +#define vaBodySpinRate headRot.y + +#define PHASE_2 3 +#define PHASE_3 9 +#define PHASE_4 15 +#define PHASE_DEATH 18 + +typedef struct BossVaEffect { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ u8 type; + /* 0x26 */ u16 timer; + /* 0x28 */ s16 mode; + /* 0x2A */ Vec3s rot; + /* 0x30 */ s16 primColor[4]; + /* 0x38 */ s16 envColor[4]; + /* 0x40 */ f32 scale; + /* 0x44 */ f32 scaleMod; + /* 0x48 */ Vec3f offset; + /* 0x54 */ struct BossVa* parent; +} BossVaEffect; // size = 0x58 + +typedef enum { + /* 0 */ VA_NONE, + /* 1 */ VA_LARGE_SPARK, + /* 2 */ VA_BLAST_SPARK, + /* 3 */ VA_SMALL_SPARK, + /* 4 */ VA_SPARK_BALL, + /* 5 */ VA_ZAP_CHARGE, + /* 6 */ VA_BLOOD, + /* 7 */ VA_TUMOR, + /* 8 */ VA_GORE +} BossVaEffectType; + +typedef enum { + /* 1 */ SPARK_TETHER = 1, + /* 2 */ SPARK_BARI, + /* 3 */ SPARK_BLAST, + /* 4 */ SPARK_UNUSED, + /* 5 */ SPARK_BODY, + /* 6 */ SPARK_LINK +} BossVaSparkMode; + +typedef enum { + /* 0 */ BLOOD_DROPLET, + /* 1 */ BLOOD_SPLATTER, + /* 2 */ BLOOD_SPOT +} BossVaBloodMode; + +typedef enum { + /* 0 */ TUMOR_UNUSED, + /* 1 */ TUMOR_BODY, + /* 2 */ TUMOR_ARM +} BossVaTumorMode; + +typedef enum { + /* 0 */ GORE_PERMANENT, + /* 1 */ GORE_FLOOR, + /* 2 */ GORE_FADING +} BossVaGoreMode; + +typedef enum { + /* -5 */ INTRO_UNUSED_START = -5, + /* -4 */ INTRO_START, + /* -3 */ INTRO_LOOK_DOOR, + /* -2 */ INTRO_CLOSE_DOOR, + /* -1 */ INTRO_DOOR_SHUT, + /* 0 */ INTRO_CRACKLE, + /* 1 */ INTRO_SPAWN_BARI, + /* 2 */ INTRO_LOOK_BARI, + /* 3 */ INTRO_REVERSE_CAMERA, + /* 4 */ INTRO_SUPPORT_CAMERA, + /* 5 */ INTRO_BODY_SOUND, + /* 6 */ INTRO_LOOK_SUPPORT, + /* 7 */ INTRO_UNUSED_CALL_BARI, + /* 8 */ INTRO_CALL_BARI, + /* 9 */ INTRO_ATTACH_BARI, + /* 10 */ INTRO_TITLE, + /* 11 */ INTRO_BRIGHTEN, + /* 12 */ INTRO_FINISH, + /* 13 */ BOSSVA_BATTLE, + /* 14 */ DEATH_START, + /* 15 */ DEATH_BODY_TUMORS, + /* 16 */ DEATH_ZAPPER_1, + /* 17 */ DEATH_ZAPPER_2, + /* 18 */ DEATH_ZAPPER_3, + /* 19 */ DEATH_SHELL_BURST, + /* 20 */ DEATH_CORE_TUMORS, + /* 21 */ DEATH_CORE_DEAD, + /* 22 */ DEATH_CORE_BURST, + /* 23 */ DEATH_MUSIC, + /* 24 */ DEATH_FINISH +} BossVaCutscene; + +void BossVa_Init(Actor* thisx, GlobalContext* globalCtx); +void BossVa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void BossVa_Update(Actor* thisx, GlobalContext* globalCtx); +void BossVa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void BossVa_UpdateEffects(GlobalContext* globalCtx); +void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx); +void BossVa_DrawDoor(GlobalContext* globalCtx, s16 scale); + +void BossVa_SetupIntro(BossVa* this); +void BossVa_SetupBodyPhase1(BossVa* this); +void BossVa_SetupBodyPhase2(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupBodyPhase3(BossVa* this); +void BossVa_SetupBodyPhase4(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupBodyDeath(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SetupSupportIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupSupportAttached(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupSupportCut(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SetupZapperIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupZapperAttack(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupZapperEnraged(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupZapperDamaged(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupZapperHold(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SetupStump(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SetupDoor(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SetupBariIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupBariPhase2Attack(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupBariPhase3Attack(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupBariPhase3Stunned(BossVa* this, GlobalContext* globalCtx); +void BossVa_SetupBariDeath(BossVa* this); + +void BossVa_BodyIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_BodyPhase1(BossVa* this, GlobalContext* globalCtx); +void BossVa_BodyPhase2(BossVa* this, GlobalContext* globalCtx); +void BossVa_BodyPhase3(BossVa* this, GlobalContext* globalCtx); +void BossVa_BodyPhase4(BossVa* this, GlobalContext* globalCtx); +void BossVa_BodyDeath(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SupportIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_SupportAttached(BossVa* this, GlobalContext* globalCtx); +void BossVa_SupportCut(BossVa* this, GlobalContext* globalCtx); + +void BossVa_ZapperIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_ZapperAttack(BossVa* this, GlobalContext* globalCtx); +void BossVa_ZapperEnraged(BossVa* this, GlobalContext* globalCtx); +void BossVa_ZapperDamaged(BossVa* this, GlobalContext* globalCtx); +void BossVa_ZapperHold(BossVa* this, GlobalContext* globalCtx); +void BossVa_ZapperDeath(BossVa* this, GlobalContext* globalCtx); + +void BossVa_Stump(BossVa* this, GlobalContext* globalCtx); + +void BossVa_Door(BossVa* this, GlobalContext* globalCtx); + +void BossVa_BariIntro(BossVa* this, GlobalContext* globalCtx); +void BossVa_BariPhase3Attack(BossVa* this, GlobalContext* globalCtx); +void BossVa_BariPhase2Attack(BossVa* this, GlobalContext* globalCtx); +void BossVa_BariPhase3Stunned(BossVa* this, GlobalContext* globalCtx); +void BossVa_BariDeath(BossVa* this, GlobalContext* globalCtx); + +void BossVa_SpawnBloodSplatter(GlobalContext* globalCtx, BossVaEffect* effect, Vec3f* pos, s16 yaw, s16 scale); +void BossVa_SpawnGore(GlobalContext* globalCtx, BossVaEffect* effect, Vec3f* pos, s16 yaw, s16 scale); +void BossVa_SpawnSpark(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* offset, s16 scale, u8 mode); +void BossVa_SpawnZapperCharge(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* pos, Vec3s* rot, + s16 scale, u8 mode); +void BossVa_SpawnTumor(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* offset, s16 scale, u8 mode); +void BossVa_SpawnSparkBall(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* offset, s16 scale, + u8 mode); +void BossVa_SpawnBloodDroplets(GlobalContext* globalCtx, BossVaEffect* effect, Vec3f* pos, s16 scale, s16 phase, + s16 yaw); +void BossVa_Tumor(GlobalContext* globalCtx, BossVa* this, s32 count, s16 scale, f32 xzSpread, f32 ySpread, u8 mode, + f32 range, u8 fixed); + +const ActorInit Boss_Va_InitVars = { + ACTOR_BOSS_VA, + ACTORCAT_BOSS, + FLAGS, + OBJECT_BV, + sizeof(BossVa), + (ActorFunc)BossVa_Init, + (ActorFunc)BossVa_Destroy, + (ActorFunc)BossVa_Update, + (ActorFunc)BossVa_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFEF, 0x03, 0x08 }, + { 0x00000010, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 85, 120, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInitSupport[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000010, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 25 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInitSupport = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInitSupport), + sJntSphElementsInitSupport, +}; + +static ColliderJntSphElementInit sJntSphElementsInitBari[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x03, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInitBari = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInitBari), + sJntSphElementsInitBari, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x03, 0x04 }, + { 0x00000010, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static Vec3f sInitPosOffsets[] = { + { 0.0f, 175.35f, 0.0f }, // Support 1 + { 0.0f, 175.35f, 0.0f }, // Support 2 + { 0.0f, 175.35f, 0.0f }, // Support 3 + { 120.0f, 103.425f, -67.0f }, // Zapper 1 + { 0.0f, 103.425f, 140.0f }, // Zapper 2 + { -120.0f, 103.425f, -70.0f }, // Zapper 3 + { -2.0f, 16.0f, 50.0f }, // Upper Bari 1 + { 48.0f, 16.0f, 15.0f }, // Upper Bari 2 + { 25.0f, 16.0f, -36.0f }, // Upper Bari 3 + { -29.0f, 16.0f, -36.0f }, // Upper Bari 4 + { -63.0f, 16.0f, 22.0f }, // Upper Bari 5 + { 0.0f, -10.0f, -64.0f }, // Lower Bari 1 + { 63.0f, -10.0f, -22.0f }, // Lower Bari 2 + { 35.0f, -10.0f, 46.0f }, // Lower Bari 3 + { -36.0f, -10.0f, 46.0f }, // Lower Bari 4 + { -49.0f, -10.0f, -17.0f }, // Lower Bari 5 + { 0.0f, 160.0f, 370.0f }, // Upper Bari 1 CS + { 65.0f, 35.0f, 370.0f }, // Upper Bari 2 CS + { 80.0f, 70.0f, -130.0f }, // Upper Bari 3 CS + { -160.0f, 100.0f, -130.0f }, // Upper Bari 4 CS + { -150.0f, 130.0f, 0.0f }, // Upper Bari 5 CS + { 230.0f, 0.0f, 0.0f }, // Lower Bari 1 CS + { 60.0f, 140.0f, 0.0f }, // Lower Bari 2 CS + { 0.0f, 40.0f, 270.0f }, // Lower Bari 3 CS + { -100.0f, 10.0f, 200.0f }, // Lower Bari 4 CS + { -90.0f, 70.0f, -310.0f }, // Lower Bari 5 CS +}; + +static Vec3s sInitRot[] = { + { 0x1FFE, 0x0000, 0x0000 }, { 0x1FFE, 0x5550, 0x0000 }, { 0x1FFE, 0xAAB0, 0x0000 }, { 0xD558, 0x5550, 0x0000 }, + { 0xD558, 0x0000, 0x0000 }, { 0xD558, 0xAAB0, 0x0000 }, { 0x2AA8, 0xFCCC, 0x0000 }, { 0x2AA8, 0x3330, 0x0000 }, + { 0x2AA8, 0x6660, 0x0000 }, { 0x2AA8, 0x99A0, 0x0000 }, { 0x2AA8, 0xCCD0, 0x0000 }, { 0x4C98, 0x81D0, 0x0000 }, + { 0x4C98, 0x4F70, 0x0000 }, { 0x4C98, 0x1758, 0x0000 }, { 0x4C98, 0xE8A8, 0x0000 }, { 0x4C98, 0xB648, 0x0000 }, +}; + +static Vec3f sWarpPos[] = { + { 10.0f, 0.0f, 30.0f }, + { 260.0f, 0.0f, -470.0f }, + { -240.0f, 0.0f, -470.0f }, +}; + +static DamageTable sDamageTable[] = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0xE), + /* Ice magic */ DMG_ENTRY(0, 0x6), + /* Light magic */ DMG_ENTRY(0, 0xD), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; +static u8 sKillBari = 0; +static u8 sBodyBari[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static s16 sCsCamera = 0; + +static BossVaEffect sVaEffects[400]; +static u8 sBodyState; +static u8 sFightPhase; +static s8 sCsState; +static Vec3f sCameraEye; +static Vec3f sCameraAt; +static Vec3f sCameraNextEye; +static Vec3f sCameraNextAt; +static Vec3f sCameraEyeMaxVel; +static Vec3f sCameraAtMaxVel; +static s16 sDoorState; +static u8 sPhase3StopMoving; +static Vec3s sZapperRot; +static u16 sPhase2Timer; +static s8 sPhase4HP; + +void BossVa_SetupAction(BossVa* this, BossVaActionFunc func) { + this->actionFunc = func; +} + +void BossVa_AttachToBody(BossVa* this) { + BossVa* vaBody = GET_BODY(this); + + Matrix_Translate(vaBody->actor.world.pos.x, vaBody->actor.world.pos.y, vaBody->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(vaBody->actor.shape.rot.x, 0, vaBody->actor.shape.rot.z, MTXMODE_APPLY); + Matrix_MultVec3f(&sInitPosOffsets[this->actor.params], &this->actor.world.pos); + + switch (this->actor.params) { + case BOSSVA_SUPPORT_1: + case BOSSVA_SUPPORT_2: + case BOSSVA_SUPPORT_3: + if (!this->onCeiling) { + this->actor.shape.rot.x = sInitRot[this->actor.params].x + vaBody->actor.shape.rot.x; + this->actor.shape.rot.y = sInitRot[this->actor.params].y; + this->actor.shape.rot.z = sInitRot[this->actor.params].z + vaBody->actor.shape.rot.z; + } + break; + + case BOSSVA_ZAPPER_1: + case BOSSVA_ZAPPER_2: + case BOSSVA_ZAPPER_3: + this->actor.shape.rot.y = sInitRot[this->actor.params].y; + this->actor.shape.rot.x = (sInitRot[this->actor.params].x + + (s16)(Math_CosS(sInitRot[this->actor.params].y) * vaBody->actor.shape.rot.x)) - + (s16)(Math_SinS(sInitRot[this->actor.params].y) * vaBody->actor.shape.rot.z); + this->actor.shape.rot.z = (s16)(Math_CosS(sInitRot[this->actor.params].y) * vaBody->actor.shape.rot.z) + + (sInitRot[this->actor.params].z + + (s16)(Math_SinS(sInitRot[this->actor.params].y) * vaBody->actor.shape.rot.x)); + break; + } + + this->actor.world.rot = this->actor.shape.rot; + this->actor.shape.yOffset = GET_BODY(this)->actor.shape.yOffset; +} + +void BossVa_BloodDroplets(GlobalContext* globalCtx, Vec3f* pos, s16 phase, s16 yaw) { + s32 i; + Vec3f spawnPos; + + for (i = 2; i > 0; i--) { + spawnPos.x = Rand_CenteredFloat(10.0f) + pos->x; + spawnPos.y = pos->y - (Rand_ZeroOne() * 15.0f); + spawnPos.z = Rand_CenteredFloat(10.0f) + pos->z; + BossVa_SpawnBloodDroplets(globalCtx, sVaEffects, &spawnPos, 65, phase, yaw); + } +} + +void BossVa_BloodSplatter(GlobalContext* globalCtx, BossVaEffect* src, s16 yaw, s16 scale, s32 count) { + s32 i; + Vec3f pos; + + for (i = count; i > 0; i--) { + pos.x = Rand_CenteredFloat(10.0f) + src->pos.x; + pos.y = src->pos.y - (Rand_ZeroOne() * 15.0f); + pos.z = Rand_CenteredFloat(10.0f) + src->pos.z; + BossVa_SpawnBloodSplatter(globalCtx, sVaEffects, &pos, (s16)Rand_CenteredFloat(0x6590) + yaw, scale); + } +} + +void BossVa_Gore(GlobalContext* globalCtx, BossVaEffect* src, s16 yaw, s16 scale) { + s32 i; + Vec3f pos; + + for (i = (sCsState <= DEATH_SHELL_BURST) ? 2 : 1; i > 0; i--) { + pos.x = Rand_CenteredFloat(10.0f) + src->pos.x; + pos.y = Rand_CenteredFloat(10.0f) + src->pos.y; + pos.z = Rand_CenteredFloat(10.0f) + src->pos.z; + BossVa_SpawnGore(globalCtx, sVaEffects, &pos, (s16)Rand_CenteredFloat(0x6590) + yaw, scale); + } +} + +void BossVa_Spark(GlobalContext* globalCtx, BossVa* this, s32 count, s16 scale, f32 xzSpread, f32 ySpread, u8 mode, + f32 range, u8 fixed) { + s32 i; + s16 index; + Vec3f offset; + + for (i = count; i > 0; i--) { + if (!fixed) { + index = Rand_ZeroOne() * (range - 0.6f); + } else { + index = range - 0.6f; + } + offset.x = Rand_CenteredFloat(xzSpread) + this->effectPos[index].x - this->actor.world.pos.x; + offset.y = Rand_CenteredFloat(ySpread) + this->effectPos[index].y - this->actor.world.pos.y; + offset.z = Rand_CenteredFloat(xzSpread) + this->effectPos[index].z - this->actor.world.pos.z; + BossVa_SpawnSpark(globalCtx, sVaEffects, this, &offset, scale, mode); + } +} + +void BossVa_Tumor(GlobalContext* globalCtx, BossVa* this, s32 count, s16 scale, f32 xzSpread, f32 ySpread, u8 mode, + f32 range, u8 fixed) { + s16 index; + s32 i; + Vec3f offset; + + for (i = count; i > 0; i--) { + if (!fixed) { + index = Rand_ZeroOne() * (range - 0.6f); + } else { + index = range - 0.6f; + } + + offset.x = Rand_CenteredFloat(xzSpread) + this->effectPos[index].x - this->actor.world.pos.x; + offset.y = Rand_CenteredFloat(ySpread) + this->effectPos[index].y - this->actor.world.pos.y; + offset.z = Rand_CenteredFloat(xzSpread) + this->effectPos[index].z - this->actor.world.pos.z; + BossVa_SpawnTumor(globalCtx, sVaEffects, this, &offset, scale, mode); + } +} + +void BossVa_SetSparkEnv(GlobalContext* globalCtx) { + globalCtx->envCtx.adjAmbientColor[0] = 0xA; + globalCtx->envCtx.adjAmbientColor[1] = 0xA; + globalCtx->envCtx.adjAmbientColor[2] = 0xA; + globalCtx->envCtx.adjLight1Color[0] = 0x73; + globalCtx->envCtx.adjLight1Color[1] = 0x41; + globalCtx->envCtx.adjLight1Color[2] = 0x64; + globalCtx->envCtx.adjFogColor[0] = 0x78; + globalCtx->envCtx.adjFogColor[1] = 0x78; + globalCtx->envCtx.adjFogColor[2] = 0x46; +} + +void BossVa_SetDeathEnv(GlobalContext* globalCtx) { + globalCtx->envCtx.adjFogColor[0] = 0xDC; + globalCtx->envCtx.adjFogColor[1] = 0xDC; + globalCtx->envCtx.adjFogColor[2] = 0x96; + globalCtx->envCtx.adjFogNear = -0x3E8; + globalCtx->envCtx.adjFogFar = -0x384; + globalCtx->envCtx.adjAmbientColor[0] = 0xC8; + globalCtx->envCtx.adjAmbientColor[1] = 0xC8; + globalCtx->envCtx.adjAmbientColor[2] = 0xC8; + globalCtx->envCtx.adjLight1Color[0] = 0xD7; + globalCtx->envCtx.adjLight1Color[1] = 0xA5; + globalCtx->envCtx.adjLight1Color[2] = 0xC8; + globalCtx->envCtx.screenFillColor[0] = 0xDC; + globalCtx->envCtx.screenFillColor[1] = 0xDC; + globalCtx->envCtx.screenFillColor[2] = 0x96; + globalCtx->envCtx.screenFillColor[3] = 0x64; +} + +EnBoom* BossVa_FindBoomerang(GlobalContext* globalCtx) { + Actor* actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_MISC].head; + + while (actorIt != NULL) { + if (actorIt->id != ACTOR_EN_BOOM) { + actorIt = actorIt->next; + continue; + } + return (EnBoom*)actorIt; + } + return NULL; +} + +void BossVa_KillBari(BossVa* this, GlobalContext* globalCtx) { + s32 i; + s16 scale; + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + for (i = 7; i >= 0; i--) { + pos.x = Rand_CenteredFloat(60.0f) + this->actor.world.pos.x; + pos.y = + Rand_CenteredFloat(50.0f) + (this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y)); + pos.z = Rand_CenteredFloat(60.0f) + this->actor.world.pos.z; + velocity.y = Rand_ZeroOne() + 1.0f; + scale = Rand_S16Offset(80, 100); + if (Rand_ZeroOne() < 0.7f) { + EffectSsDtBubble_SpawnColorProfile(globalCtx, &pos, &velocity, &accel, scale, 25, 2, 1); + } else { + EffectSsDtBubble_SpawnColorProfile(globalCtx, &pos, &velocity, &accel, scale, 25, 0, 1); + } + } + + sFightPhase++; + BossVa_SetupBariDeath(this); +} + +void BossVa_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossVa* this = (BossVa*)thisx; + s32 i; + s16 warpId; + + Actor_SetScale(&this->actor, 0.1f); + this->actor.targetMode = 5; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + + switch (this->actor.params) { + case BOSSVA_BODY: + SkelAnime_Init(globalCtx, &this->skelAnime, &gBarinadeBodySkel, &gBarinadeBodyAnim, NULL, NULL, 0); + this->actor.flags |= ACTOR_FLAG_24; + break; + case BOSSVA_SUPPORT_1: + case BOSSVA_SUPPORT_2: + case BOSSVA_SUPPORT_3: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBarinadeSupportSkel, &gBarinadeSupportAttachedAnim, NULL, + NULL, 0); + break; + case BOSSVA_ZAPPER_1: + case BOSSVA_ZAPPER_2: + case BOSSVA_ZAPPER_3: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBarinadeZapperSkel, &gBarinadeZapperIdleAnim, NULL, NULL, + 0); + break; + case BOSSVA_STUMP_1: + case BOSSVA_STUMP_2: + case BOSSVA_STUMP_3: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBarinadeStumpSkel, &gBarinadeStumpAnim, NULL, NULL, 0); + break; + default: + this->actor.flags |= ACTOR_FLAG_24; + SkelAnime_Init(globalCtx, &this->skelAnime, &gBarinadeBariSkel, &gBarinadeBariAnim, NULL, NULL, 0); + this->actor.shape.yOffset = 400.0f; + break; + case BOSSVA_DOOR: + break; + } + + this->actor.focus.pos = this->actor.world.pos; + this->onCeiling = false; + this->actor.naviEnemyId = 0x14; + + switch (this->actor.params) { + case BOSSVA_BODY: + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_VA, 0.0f, 80.0f, 400.0f, 0, 0, + 0, BOSSVA_DOOR); + if (Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + warpId = ACTOR_EN_RU1; + if (gSaveContext.eventChkInf[3] & 0x80) { + warpId = ACTOR_DOOR_WARP1; + } + Actor_Spawn(&globalCtx->actorCtx, globalCtx, warpId, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, + 0); //! params could be WARP_DUNGEON_CHILD however this can also spawn Ru1 + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, this->actor.world.pos.x + 160.0f, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + sDoorState = 100; + Actor_Kill(&this->actor); + } else { + this->actor.colChkInfo.damageTable = sDamageTable; + sPhase2Timer = 0xFFFF; + if (gSaveContext.eventChkInf[7] & 0x40) { + sCsState = INTRO_CALL_BARI; + sDoorState = 100; + func_8002DF54(globalCtx, &this->actor, 1); + globalCtx->envCtx.screenFillColor[0] = 0xDC; + globalCtx->envCtx.screenFillColor[1] = 0xDC; + globalCtx->envCtx.screenFillColor[2] = 0xBE; + globalCtx->envCtx.screenFillColor[3] = 0xD2; + func_80064520(globalCtx, &globalCtx->csCtx); + sCsCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCsCamera, CAM_STAT_ACTIVE); + sCameraNextEye.x = sCameraEye.x = 140.0f; + sCameraNextEye.y = sCameraEye.y = 205.0f; + sCameraNextEye.z = sCameraEye.z = -20.0f; + sCameraNextAt.x = sCameraAt.x = 10.0f; + sCameraNextAt.y = sCameraAt.y = 50.0f; + sCameraNextAt.z = sCameraAt.z = -220.0f; + Gameplay_CameraSetAtEye(globalCtx, sCsCamera, &sCameraAt, &sCameraEye); + this->timer = 20; + + for (i = BOSSVA_BARI_LOWER_5; i >= BOSSVA_BARI_UPPER_1; i--) { + Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_VA, + sInitPosOffsets[i].x + this->actor.world.pos.x, + sInitPosOffsets[i].y + this->actor.world.pos.y, + sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x, + sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i); + } + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + + } else { + sCsState = INTRO_START; + sDoorState = 5; + } + + this->zapHeadPos.x = 1.0f; + Collider_InitCylinder(globalCtx, &this->colliderBody); + Collider_SetCylinder(globalCtx, &this->colliderBody, &this->actor, &sCylinderInit); + + for (i = BOSSVA_ZAPPER_3; i >= BOSSVA_SUPPORT_1; i--) { + Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_VA, + sInitPosOffsets[i].x + this->actor.world.pos.x, sInitPosOffsets[i].y + this->actor.world.pos.y, + sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x, + sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i); + } + + memset((u8*)sVaEffects, 0, ARRAY_COUNT(sVaEffects) * sizeof(BossVaEffect)); + if (sCsState < BOSSVA_BATTLE) { + BossVa_SetupIntro(this); + } else { + BossVa_SetupBodyPhase1(this); + } + } + break; + case BOSSVA_SUPPORT_1: + case BOSSVA_SUPPORT_2: + case BOSSVA_SUPPORT_3: + Collider_InitJntSph(globalCtx, &this->colliderSph); + Collider_SetJntSph(globalCtx, &this->colliderSph, &this->actor, &sJntSphInitSupport, this->elements); + if (sCsState < BOSSVA_BATTLE) { + BossVa_SetupSupportIntro(this, globalCtx); + } else { + BossVa_SetupSupportAttached(this, globalCtx); + } + this->onCeiling++; + break; + case BOSSVA_ZAPPER_1: + case BOSSVA_ZAPPER_2: + case BOSSVA_ZAPPER_3: + Collider_InitQuad(globalCtx, &this->colliderLightning); + Collider_SetQuad(globalCtx, &this->colliderLightning, &this->actor, &sQuadInit); + if (sCsState < BOSSVA_BATTLE) { + BossVa_SetupZapperIntro(this, globalCtx); + } else { + BossVa_SetupZapperAttack(this, globalCtx); + } + break; + case BOSSVA_STUMP_1: + case BOSSVA_STUMP_2: + case BOSSVA_STUMP_3: + BossVa_SetupStump(this, globalCtx); + break; + case BOSSVA_DOOR: + BossVa_SetupDoor(this, globalCtx); + break; + default: + Collider_InitJntSph(globalCtx, &this->colliderSph); + Collider_SetJntSph(globalCtx, &this->colliderSph, &this->actor, &sJntSphInitBari, this->elements); + Collider_InitQuad(globalCtx, &this->colliderLightning); + Collider_SetQuad(globalCtx, &this->colliderLightning, &this->actor, &sQuadInit); + this->unk_1D8.x = 1.0f; + this->unk_1D8.y = 1.0f; + if (sCsState < BOSSVA_BATTLE) { + BossVa_SetupBariIntro(this, globalCtx); + } else if (sFightPhase >= PHASE_3) { + BossVa_SetupBariPhase3Attack(this, globalCtx); + } else { + BossVa_SetupBariPhase2Attack(this, globalCtx); + } + break; + } +} + +void BossVa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + BossVa* this = (BossVa*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyJntSph(globalCtx, &this->colliderSph); + Collider_DestroyCylinder(globalCtx, &this->colliderBody); +} + +void BossVa_SetupIntro(BossVa* this) { + f32 lastFrame = Animation_GetLastFrame(&gBarinadeBodyAnim); + + Animation_Change(&this->skelAnime, &gBarinadeBodyAnim, 1.0f, lastFrame, lastFrame, ANIMMODE_ONCE, 0.0f); + this->actor.shape.yOffset = -450.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_BodyIntro); +} + +void BossVa_BodyIntro(BossVa* this, GlobalContext* globalCtx) { + s32 i; + Player* player = GET_PLAYER(globalCtx); + + this->unk_1AC += 0xC31; + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.1f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.05f) + 1.0f; + + switch (sCsState) { + case INTRO_UNUSED_START: + this->timer--; + if (this->timer == 0) { + sCsState = INTRO_CLOSE_DOOR; + this->timer = 10; + } + break; + case INTRO_START: + globalCtx->envCtx.screenFillColor[0] = 0xDC; + globalCtx->envCtx.screenFillColor[1] = 0xDC; + globalCtx->envCtx.screenFillColor[2] = 0xBE; + globalCtx->envCtx.screenFillColor[3] = 0xD2; + func_8002DF54(globalCtx, &this->actor, 8); + player->actor.world.rot.y = player->actor.shape.rot.y = 0x7FFF; + sCsState++; + break; + case INTRO_LOOK_DOOR: + func_80064520(globalCtx, &globalCtx->csCtx); + if (sCsCamera == SUBCAM_FREE) { + sCsCamera = Gameplay_CreateSubCamera(globalCtx); + } + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCsCamera, CAM_STAT_ACTIVE); + + sCameraNextEye.x = sCameraEye.x = 13.0f; + sCameraNextEye.y = sCameraEye.y = 124.0f; + sCameraNextEye.z = sCameraEye.z = 167.0f; + + sCameraNextAt.x = sCameraAt.x = player->actor.world.pos.x; + sCameraNextAt.y = sCameraAt.y = player->actor.world.pos.y; + sCameraNextAt.z = sCameraAt.z = player->actor.world.pos.z; + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + + this->timer = 10; + sCsState++; + break; + case INTRO_CLOSE_DOOR: + this->timer--; + if (this->timer == 0) { + func_8002DF54(globalCtx, &this->actor, 2); + sCsState++; + this->timer = 30; + } + break; + case INTRO_DOOR_SHUT: + this->timer--; + if (this->timer == 0) { + sCsState++; + } + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + break; + case INTRO_CRACKLE: + func_8002DF54(globalCtx, &this->actor, 1); + sCsState++; + break; + case INTRO_SPAWN_BARI: + func_80064520(globalCtx, &globalCtx->csCtx); + if (sCsCamera == SUBCAM_FREE) { + sCsCamera = Gameplay_CreateSubCamera(globalCtx); + } + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCsCamera, CAM_STAT_ACTIVE); + + sCameraNextEye.x = sCameraEye.x = 13.0f; + sCameraNextEye.y = sCameraEye.y = 124.0f; + sCameraNextEye.z = sCameraEye.z = 167.0f; + + sCameraNextAt.x = sCameraAt.x = player->actor.world.pos.x; + sCameraNextAt.y = sCameraAt.y = player->actor.world.pos.y; + sCameraNextAt.z = sCameraAt.z = player->actor.world.pos.z; + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + + for (i = BOSSVA_BARI_LOWER_5; i >= BOSSVA_BARI_UPPER_1; i--) { + Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_VA, + sInitPosOffsets[i].x + this->actor.world.pos.x, sInitPosOffsets[i].y + this->actor.world.pos.y, + sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x, + sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i); + } + + this->timer = 90; + sCsState++; + break; + case INTRO_REVERSE_CAMERA: + sCameraNextEye.x = -92.0f; + sCameraNextEye.y = 22.0f; + sCameraNextEye.z = 360.0f; + sCameraNextAt.x = 63.0f; + sCameraNextAt.y = 104.0f; + sCameraNextAt.z = 248.0f; + Math_SmoothStepToF(&sCameraEyeMaxVel.x, 7.0f, 0.3f, 0.7f, 0.05f); + sCameraEyeMaxVel.z = sCameraEyeMaxVel.x; + sCameraEyeMaxVel.y = sCameraEyeMaxVel.z; + sCameraAtMaxVel = sCameraEyeMaxVel; + + this->timer--; + if (this->timer == 0) { + sCsState++; + this->timer = 60; + } + break; + case INTRO_SUPPORT_CAMERA: + sCameraNextEye.x = sCameraEye.x = 140.0f; + sCameraNextEye.y = sCameraEye.y = 205.0f; + sCameraNextEye.z = sCameraEye.z = -20.0f; + + sCameraNextAt.x = sCameraAt.x = 10.0f; + sCameraNextAt.y = sCameraAt.y = 247.0f; + sCameraNextAt.z = sCameraAt.z = -220.0f; + + sCsState++; + this->timer = 1; + break; + case INTRO_BODY_SOUND: + sCameraNextAt.x = 10.0f; + sCameraNextAt.y = 247.0f; + sCameraNextAt.z = -220.0f; + Math_SmoothStepToF(&sCameraEyeMaxVel.x, 7.0f, 0.3f, 0.7f, 0.05f); + sCameraEyeMaxVel.z = sCameraEyeMaxVel.x; + sCameraEyeMaxVel.y = sCameraEyeMaxVel.z; + sCameraAtMaxVel = sCameraEyeMaxVel; + + this->timer--; + if (this->timer == 0) { + sCsState++; + this->timer = 40; + } + break; + case INTRO_LOOK_SUPPORT: + this->timer--; + if (this->timer == 0) { + sCameraNextAt.x = 10.0f; + sCameraNextAt.y = 50.0f; + sCameraNextAt.z = -220.0f; + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + + sCsState++; + sCsState++; + this->timer = 20; + } + break; + case INTRO_CALL_BARI: + Math_SmoothStepToF(&sCameraEyeMaxVel.x, 14.0f, 0.3f, 1.0f, 0.25f); + + sCameraEyeMaxVel.y = sCameraEyeMaxVel.x * 0.7f; + sCameraEyeMaxVel.z = sCameraEyeMaxVel.x; + + sCameraAtMaxVel = sCameraEyeMaxVel; + sCameraAtMaxVel.z = sCameraAtMaxVel.z * 1.75f; + + this->timer--; + if (this->timer == 0) { + sCsState++; + this->timer = 7500; + this->unk_1F2 = 0; + } + break; + case INTRO_ATTACH_BARI: + for (i = 10; i >= 1; i--) { + if (sBodyBari[i - 1]) { + if (sBodyBari[i - 1] == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_STICK); + BossVa_SetSparkEnv(globalCtx); + if (this->onCeiling == 0) { + this->onCeiling = 2; // Not used by body + } + } else if (sBodyBari[i - 1] == 2) { + BossVa_Spark(globalCtx, this, 6, 140, 50.0f, 30.0f, SPARK_BARI, i, true); + } + + if (sBodyBari[i - 1] <= 2) { + sBodyBari[i - 1]++; + } + } + } + Math_SmoothStepToS(&this->unk_1F2, 0x280, 1, 0x32, 0); + Math_SmoothStepToF(&sCameraEyeMaxVel.x, 14.0f, 0.3f, 1.0f, 0.25f); + sCameraEyeMaxVel.z = sCameraEyeMaxVel.x; + sCameraAtMaxVel = sCameraEyeMaxVel; + if (this->timer >= 45000) { + globalCtx->envCtx.unk_BF = 1; + func_8002DF54(globalCtx, &this->actor, 8); + } else if (this->timer >= 35000) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } + + this->timer += this->unk_1F2; + if (this->timer >= 65536) { + sCameraEyeMaxVel.y = sCameraAtMaxVel.y = 9.8f; + sCsState++; + + sCameraNextEye.x = 10.0f; + sCameraNextEye.z = 0.0f; + + sCameraNextAt.x = 10.0f; + sCameraNextAt.y = 140.0f; + sCameraNextAt.z = -200.0f; + + if (!(gSaveContext.eventChkInf[7] & 0x40)) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gBarinadeTitleCardTex), 0xA0, 0xB4, 0x80, 0x28); + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + this->timer = 40; + } else { + sCameraEyeMaxVel.y = 1.6f; + sCameraNextEye.y = 5.0f; + sCameraNextEye.x = Math_SinS(this->timer) * 200.0f; + sCameraNextEye.z = (Math_CosS(this->timer) * 200.0f) + -200.0f; + } + break; + case INTRO_TITLE: + BossVa_Spark(globalCtx, this, 3, 140, 50.0f, 30.0f, SPARK_BARI, 10.0f, false); + this->timer--; + if (this->timer == 0) { + sCsState++; + this->timer = 45; + } + break; + case INTRO_BRIGHTEN: + BossVa_Spark(globalCtx, this, 3, 140, 50.0f, 30.0f, SPARK_BARI, 10.0f, false); + this->timer--; + if (this->timer == 0) { + sCsState++; + this->timer = 11; + } + break; + case INTRO_FINISH: + this->timer--; + if (this->timer == 0) { + Gameplay_ClearCamera(globalCtx, sCsCamera); + sCsCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + func_8002DF54(globalCtx, &this->actor, 7); + sCsState++; + gSaveContext.eventChkInf[7] |= 0x40; + player->actor.shape.rot.y = player->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; + } + break; + case BOSSVA_BATTLE: + BossVa_SetupBodyPhase1(this); + break; + } + + if (sCsState >= INTRO_BODY_SOUND) { + func_800F436C(&this->actor.projectedPos, NA_SE_EN_BALINADE_LEVEL - SFX_FLAG, 1.0f); + if ((sCsState >= INTRO_CALL_BARI) && ((globalCtx->gameplayFrames % 4) == 0)) { + BossVa_Spark(globalCtx, this, 1, 100, 50.0f, 10.0f, SPARK_BODY, 10.0f, false); + } + } + + this->unk_1B0 += 0xCE4; + this->bodyGlow = (s16)(Math_SinS(this->unk_1B0) * 50.0f) + 150; + if ((sCsCamera != 0) && (sCsState <= INTRO_TITLE)) { + Math_SmoothStepToF(&sCameraEye.x, sCameraNextEye.x, 0.3f, sCameraEyeMaxVel.x, 0.075f); + Math_SmoothStepToF(&sCameraEye.y, sCameraNextEye.y, 0.3f, sCameraEyeMaxVel.y, 0.075f); + Math_SmoothStepToF(&sCameraEye.z, sCameraNextEye.z, 0.3f, sCameraEyeMaxVel.z, 0.075f); + Math_SmoothStepToF(&sCameraAt.x, sCameraNextAt.x, 0.3f, sCameraAtMaxVel.x, 0.075f); + Math_SmoothStepToF(&sCameraAt.y, sCameraNextAt.y, 0.3f, sCameraAtMaxVel.y, 0.075f); + Math_SmoothStepToF(&sCameraAt.z, sCameraNextAt.z, 0.3f, sCameraAtMaxVel.z, 0.075f); + Gameplay_CameraSetAtEye(globalCtx, sCsCamera, &sCameraAt, &sCameraEye); + } +} + +void BossVa_SetupBodyPhase1(BossVa* this) { + f32 lastFrame = Animation_GetLastFrame(&gBarinadeBodyAnim); + + Animation_Change(&this->skelAnime, &gBarinadeBodyAnim, 1.0f, lastFrame, lastFrame, ANIMMODE_ONCE, 0.0f); + this->actor.shape.yOffset = -450.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->timer = 25; + sBodyState = 0x80; + BossVa_SetupAction(this, BossVa_BodyPhase1); +} + +void BossVa_BodyPhase1(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_1B0 += 0xCE4; + this->bodyGlow = (s16)(Math_SinS(this->unk_1B0) * 50.0f) + 150; + if (this->timer != 0) { + this->timer--; + if (this->timer == 0) { + sBodyState &= (u8)~0x80; + } + } + + if (this->colliderBody.base.atFlags & AT_HIT) { + this->colliderBody.base.atFlags &= ~AT_HIT; + if (this->colliderBody.base.at == &player->actor) { + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); + } + } + + if (sBodyState & 0x7F) { + this->skelAnime.curFrame = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 255, 0, 12); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_DAMAGE); + } + + if (SkelAnime_Update(&this->skelAnime) && (sFightPhase >= PHASE_2)) { + BossVa_SetupBodyPhase2(this, globalCtx); + } + + Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x, 1, 0xC8, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->actor.world.rot.z, 1, 0xC8, 0); + this->unk_1AC += 0xC31; + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.1f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.05f) + 1.0f; + if ((globalCtx->gameplayFrames % 4) == 0) { + BossVa_Spark(globalCtx, this, 1, 100, 50.0f, 10.0f, SPARK_BARI, 10.0f, false); + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + Collider_UpdateCylinder(&this->actor, &this->colliderBody); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + func_800F436C(&this->actor.projectedPos, NA_SE_EN_BALINADE_LEVEL - SFX_FLAG, 1.0f); +} + +void BossVa_SetupBodyPhase2(BossVa* this, GlobalContext* globalCtx) { + s32 i; + + sFightPhase++; + for (i = BOSSVA_BARI_UPPER_5; i >= BOSSVA_BARI_UPPER_1; i--) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_VA, + sInitPosOffsets[i].x + this->actor.world.pos.x, + sInitPosOffsets[i].y + this->actor.world.pos.y, + sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x, + sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i); + } + + this->invincibilityTimer = 0; + this->actor.flags |= ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_BodyPhase2); +} + +void BossVa_BodyPhase2(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f sp48; + + if (this->actor.colorFilterTimer == 0) { + sPhase2Timer++; + if ((this->invincibilityTimer != 0) && (this->actor.colorFilterParams & 0x4000)) { + Actor_SetColorFilter(&this->actor, 0, 255, 0, 160); + this->actor.colorFilterTimer = this->invincibilityTimer; + } else { + this->colliderBody.info.bumper.dmgFlags = 0x10; + } + } + + if (this->colliderBody.base.acFlags & AC_HIT) { + this->colliderBody.base.acFlags &= ~AC_HIT; + + if (this->colliderBody.base.ac->id == ACTOR_EN_BOOM) { + sPhase2Timer &= 0xFE00; + Actor_SetColorFilter(&this->actor, 0, 255, 0, 160); + this->colliderBody.info.bumper.dmgFlags = 0xFC00712; + } else { + sKillBari++; + if ((this->actor.colorFilterTimer != 0) && !(this->actor.colorFilterParams & 0x4000)) { + this->invincibilityTimer = this->actor.colorFilterTimer - 5; + if (this->invincibilityTimer > 160) { + this->invincibilityTimer = 0; + } + } + + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_FAINT); + } + + if (this->colliderBody.base.atFlags & AT_HIT) { + this->colliderBody.base.atFlags &= ~AT_HIT; + + sPhase2Timer = (sPhase2Timer + 0x18) & 0xFFF0; + if (this->colliderBody.base.at == &player->actor) { + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + + if ((sPhase2Timer > 10) && !(sPhase2Timer & 7) && (this->actor.speedXZ == 1.0f)) { + sp48 = this->actor.world.pos; + sp48.y += 310.0f + (this->actor.shape.yOffset * this->actor.scale.y); + sp48.x += -10.0f; + sp48.z += 220.0f; + BossVa_SpawnSparkBall(globalCtx, sVaEffects, this, &sp48, 4, 0); + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x, 1, 0xC8, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->actor.world.rot.z, 1, 0xC8, 0); + Math_SmoothStepToF(&this->actor.shape.yOffset, -1000.0f, 1.0f, 20.0f, 0.0f); + if (!(sPhase2Timer & 0x100)) { + this->actor.flags |= ACTOR_FLAG_0; + this->actor.speedXZ = 1.0f; + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skelAnime) && (sFightPhase >= PHASE_3)) { + BossVa_SetupBodyPhase3(this); + } + + this->unk_1AC += 0xC31; + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.1f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.05f) + 1.0f; + if ((globalCtx->gameplayFrames % 4) == 0) { + BossVa_Spark(globalCtx, this, 1, 100, 50.0f, 10.0f, SPARK_BODY, 10.0f, false); + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 45.0f; + + Collider_UpdateCylinder(&this->actor, &this->colliderBody); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + if (this->actor.colorFilterTimer == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + + if ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + + func_800F436C(&this->actor.projectedPos, NA_SE_EN_BALINADE_LEVEL - SFX_FLAG, + (this->vaBodySpinRate * 0.00025f) + 1.0f); +} + +void BossVa_SetupBodyPhase3(BossVa* this) { + this->colliderBody.info.bumper.dmgFlags = 0x10; + this->actor.speedXZ = 0.0f; + sPhase3StopMoving = false; + BossVa_SetupAction(this, BossVa_BodyPhase3); +} + +void BossVa_BodyPhase3(BossVa* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 i; + s16 sp62; + + sp62 = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + this->unk_1B0 += 0xCE4; + this->bodyGlow = (s16)(Math_SinS(this->unk_1B0) * 50.0f) + 150; + if (this->colliderBody.base.atFlags & AT_HIT) { + this->colliderBody.base.atFlags &= ~AT_HIT; + if (this->colliderBody.base.at == &player->actor) { + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); + this->actor.world.rot.y += (s16)Rand_CenteredFloat(0x2EE0) + 0x8000; + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + + if (this->colliderBody.base.acFlags & AC_HIT) { + this->skelAnime.curFrame = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 255, 0, 12); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_FAINT); + sBodyState = 1; + this->timer = 131; + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + sBodyState = 0; + if (this->timer == 0) { + if (Math_SmoothStepToS(&this->vaBodySpinRate, 0xFA0, 1, 0x12C, 0) == 0) { + if (this->actor.speedXZ == 0.0f) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + } + Math_SmoothStepToF(&this->actor.speedXZ, 3.0f, 1.0f, 0.15f, 0.0f); + } + this->actor.flags |= ACTOR_FLAG_0; + } else { + this->timer--; + if (this->timer < 35) { + sBodyState = 0x80; + } + Math_SmoothStepToS(&this->vaBodySpinRate, 0, 1, 0x12C, 0); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.2f, 0.0f); + Math_SmoothStepToF(&this->actor.shape.yOffset, -1420.0f, 1.0f, 30.0f, 0.0f); + } + } + + if (Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) >= 400.0f) { + Math_SmoothStepToS(&this->actor.world.rot.y, sp62, 1, 0x3E8, 0); + } else if (player->invincibilityTimer != 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x8000, 1, 0x12C, 0); + } else if ((globalCtx->gameplayFrames & 0x80) == 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0x12C, 0); + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, sp62, 1, 0x258, 0); + } + + if (sPhase3StopMoving) { + this->actor.speedXZ = 0.0f; + } + + Actor_MoveForward(&this->actor); + if (SkelAnime_Update(&this->skelAnime) && (sFightPhase >= PHASE_4)) { + BossVa_SetupBodyPhase4(this, globalCtx); + } + + this->actor.shape.rot.y += this->vaBodySpinRate; + if (sFightPhase == PHASE_3) { + Math_SmoothStepToF(&this->actor.shape.yOffset, -450.0f, 1.0f, 15.0f, 0.0f); + } else { + Math_SmoothStepToF(&this->actor.shape.yOffset, -810.0f, 1.0f, 15.0f, 0.0f); + } + + if ((this->actor.shape.yOffset >= -500.0f) && (sFightPhase == PHASE_3)) { + for (i = BOSSVA_BARI_LOWER_5; i >= BOSSVA_BARI_LOWER_1; i--) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_VA, + sInitPosOffsets[i].x + this->actor.world.pos.x, + sInitPosOffsets[i].y + this->actor.world.pos.y, + sInitPosOffsets[i].z + this->actor.world.pos.z, sInitRot[i].x + this->actor.world.rot.x, + sInitRot[i].y + this->actor.world.rot.y, sInitRot[i].z + this->actor.world.rot.z, i); + } + sFightPhase++; + } + + this->unk_1AC += 0xC31; + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.1f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.05f) + 1.0f; + if ((globalCtx->gameplayFrames % 4) == 0) { + BossVa_Spark(globalCtx, this, 1, 0x64, 50.0f, 10.0f, SPARK_BODY, 10.0f, false); + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 20.0f; + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + Collider_UpdateCylinder(&this->actor, &this->colliderBody); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + if (this->timer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + + func_800F436C(&this->actor.projectedPos, NA_SE_EN_BALINADE_LEVEL - SFX_FLAG, + (this->vaBodySpinRate * 0.00025f) + 1.0f); +} + +void BossVa_SetupBodyPhase4(BossVa* this, GlobalContext* globalCtx) { + this->unk_1AC = 0; + this->actor.flags |= ACTOR_FLAG_0; + this->vaBodySpinRate = this->unk_1AC; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 300; + sBodyState = 1; + sPhase4HP = 4; + if (this->actor.shape.yOffset != 0.0f) { + this->timer = -30; + } + + this->colliderBody.dim.radius = 55; + BossVa_SetupAction(this, BossVa_BodyPhase4); +} + +void BossVa_BodyPhase4(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 tmpf1; + EnBoom* boomerang; + + this->unk_1B0 = (this->unk_1B0 + (s16)((sFightPhase - PHASE_4 + 1) * 1000.0f)) + 0xCE4; + this->bodyGlow = (s16)(Math_SinS(this->unk_1B0) * 50.0f) + 150; + if (this->colliderBody.base.atFlags & AT_HIT) { + this->colliderBody.base.atFlags &= ~AT_HIT; + if (this->colliderBody.base.at == &player->actor) { + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); + this->actor.world.rot.y += (s16)Rand_CenteredFloat(0x2EE0) + 0x8000; + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + if (this->colliderBody.base.acFlags & AC_HIT) { + this->colliderBody.base.acFlags &= ~AC_HIT; + this->skelAnime.curFrame = 0.0f; + if (this->timer >= 0) { + if (this->invincibilityTimer == 0) { + this->invincibilityTimer = 8; + if (this->actor.colChkInfo.damageEffect != 1) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_DAMAGE); + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); + sPhase4HP -= this->actor.colChkInfo.damage; + if (sPhase4HP <= 0) { + this->timer = 0; + sFightPhase++; + sPhase4HP += 3; + if (sFightPhase >= PHASE_DEATH) { + BossVa_SetupBodyDeath(this, globalCtx); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + return; + } + this->actor.speedXZ = -10.0f; + this->timer = -170 - (s16)(Rand_ZeroOne() * 150.0f); + } + } else { + this->timer = (s16)Rand_CenteredFloat(40.0f) + 160; + this->vaBodySpinRate = 0; + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 125, 0, 255); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_FAINT); + } + } + } else if (this->colliderBody.base.ac->id == ACTOR_EN_BOOM) { + boomerang = (EnBoom*)this->colliderBody.base.ac; + boomerang->returnTimer = 0; + boomerang->moveTo = &player->actor; + boomerang->actor.world.rot.y = boomerang->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SHIELD_REFLECT_SW); + } + } else if ((this->timer2 == 0) && (this->actor.shape.yOffset == 0.0f)) { + this->timer = -220 - (s16)(Rand_ZeroOne() * 200.0f); + } else if (this->timer2 != 0) { + this->timer2--; + } + + SkelAnime_Update(&this->skelAnime); + if (this->timer == 0) { + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 1.0f, ((sFightPhase - PHASE_4 + 1) * 5.0f) + 10.0f, 0.0f); + if (Math_SmoothStepToS(&this->vaBodySpinRate, (s16)((sFightPhase - PHASE_4 + 1) * 500.0f) + 0xFA0, 1, 0x12C, + 0) == 0) { + if (this->actor.speedXZ == 0.0f) { + this->actor.colorFilterTimer = 0; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 300; + } + Math_SmoothStepToF(&this->actor.speedXZ, ((sFightPhase - PHASE_4 + 1) * 1.5f) + 4.0f, 1.0f, 0.25f, 0.0f); + } + this->colliderBody.info.bumper.dmgFlags = 0x10; + } else { + Math_SmoothStepToS(&this->vaBodySpinRate, 0, 1, 0x96, 0); + if (this->timer > 0) { + if ((player->stateFlags1 & 0x4000000) && (this->timer > 35)) { + this->timer = 35; + } + Math_SmoothStepToF(&this->actor.shape.yOffset, -480.0f, 1.0f, 30.0f, 0.0f); + this->colliderBody.info.bumper.dmgFlags = 0xFC00712; + this->timer--; + } else { + if ((player->stateFlags1 & 0x4000000) && (this->timer < -60)) { + this->timer = -59; + } + if ((globalCtx->gameplayFrames % 4) == 0) { + BossVa_Spark(globalCtx, this, 2, 0x64, 220.0f, 5.0f, SPARK_BODY, 12.0f, true); + } + if (this->timer < -30) { + if (this->actor.speedXZ > 0.0f) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + } + Math_SmoothStepToF(&this->actor.shape.yOffset, -1400.0f, 1.0f, 60.0f, 0.0f); + } else { + if (this->actor.speedXZ == 0.0f) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; + this->timer2 = (s16)(Rand_ZeroOne() * 150.0f) + 330; + } + Math_SmoothStepToS(&this->vaBodySpinRate, 0xFA0, 1, 0x1F4, 0); + tmpf1 = sFightPhase - PHASE_4 + 1; + Math_SmoothStepToF(&this->actor.speedXZ, (tmpf1 + tmpf1) + 4.0f, 1.0f, 0.25f, 0.0f); + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 1.0f, 20.0f, 0.0f); + } + this->timer++; + } + } + + this->actor.shape.rot.y += this->vaBodySpinRate; + if (this->actor.speedXZ < 0.0f) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + } + + this->unk_1AC += 0xC31; + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.1f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.05f) + 1.0f; + if (this->actor.bgCheckFlags & 8) { + this->actor.bgCheckFlags &= ~8; + this->actor.world.rot.y = (s16)Rand_CenteredFloat(30 * (0x10000 / 360)) + this->actor.wallYaw; + } + + if (sFightPhase <= PHASE_4) { + if (Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) >= 400.0f) { + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos), + 1, 0x5DC, 0); + } else if (player->invincibilityTimer != 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x8000, 1, 0x12C, 0); + } else if ((globalCtx->gameplayFrames & 0x80) == 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, + (s16)((sFightPhase - PHASE_4 + 1) * 100.0f) + 0x64, 0); + } + } + + Actor_MoveForward(&this->actor); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 60.0f; + if (((globalCtx->gameplayFrames % 2) == 0) && (this->timer == 0)) { + BossVa_Spark(globalCtx, this, 2, 125, 40.0f, 10.0f, SPARK_BODY, 10.0f, false); + BossVa_Spark(globalCtx, this, 1, 100, 15.0f, 10.0f, SPARK_BARI, 11.0f, true); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 70.0f, 0.0f, 1); + Collider_UpdateCylinder(&this->actor, &this->colliderBody); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + if (this->invincibilityTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + if ((this->vaBodySpinRate > 0x3E8) || (this->actor.shape.yOffset < -1200.0f)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + func_800F436C(&this->actor.projectedPos, NA_SE_EN_BALINADE_LEVEL - SFX_FLAG, + (this->vaBodySpinRate * 0.00025f) + 1.0f); + if (this->invincibilityTimer != 0) { + this->invincibilityTimer--; + sBodyState = (sBodyState & 0x80) | 2; + } else { + sBodyState = (sBodyState & 0x80) | 1; + } +} + +void BossVa_SetupBodyDeath(BossVa* this, GlobalContext* globalCtx) { + func_800F436C(&this->actor.projectedPos, NA_SE_EN_BALINADE_LEVEL - SFX_FLAG, 1.0f); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + this->vaCamRotMod = 0xC31; + sCsState = DEATH_START; + this->actor.speedXZ = 0.0f; + this->unk_1A8 = 0.0f; + Flags_SetClear(globalCtx, globalCtx->roomCtx.curRoom.num); + BossVa_SetupAction(this, BossVa_BodyDeath); +} + +void BossVa_BodyDeath(BossVa* this, GlobalContext* globalCtx) { + s32 i; + Camera* camera = Gameplay_GetCamera(globalCtx, 0); + s32 sp7C; + Player* player = GET_PLAYER(globalCtx); + s16 tmp16; + + switch (sCsState) { + case DEATH_START: + func_8002DF54(globalCtx, &this->actor, 1); + func_80064520(globalCtx, &globalCtx->csCtx); + sCsCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCsCamera, CAM_STAT_ACTIVE); + + sCameraNextAt.x = this->actor.world.pos.x; + sCameraNextAt.y = this->actor.world.pos.y; + sCameraNextAt.z = this->actor.world.pos.z; + + sCameraAt = camera->at; + + sCameraNextEye = sCameraEye = camera->eye; + + sCameraNextEye.y = 40.0f; + sCameraNextAt.y = 140.0f; + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + + this->unk_1AC = Math_Vec3f_Yaw(&sCameraEye, &sCameraNextAt) - 0x100; + this->unk_1B0 = 15; + globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] = + globalCtx->envCtx.screenFillColor[2] = 0xFF; + globalCtx->envCtx.screenFillColor[3] = 0; + globalCtx->envCtx.fillScreen = true; + sCsState++; + case DEATH_BODY_TUMORS: + this->unk_1AC += 0x100; + sCameraNextEye.x = (Math_SinS(this->unk_1AC) * (160.0f + this->unk_1A8)) + sCameraNextAt.x; + sCameraNextEye.z = (Math_CosS(this->unk_1AC) * (160.0f + this->unk_1A8)) + sCameraNextAt.z; + Math_SmoothStepToF(&sCameraEyeMaxVel.x, 16.0f, 0.4f, 1.5f, 0.5f); + sCameraEyeMaxVel.z = sCameraEyeMaxVel.x; + sCameraEyeMaxVel.y = sCameraEyeMaxVel.x * 0.5f; + sCameraAtMaxVel = sCameraEyeMaxVel; + tmp16 = Rand_CenteredFloat(0.5f) + ((sCameraEyeMaxVel.x * 0.5f) + 0.6f); + if (((globalCtx->gameplayFrames % 4) == 0) && (this->unk_1B0 != 0)) { + for (i = 6; i > 1; i--) { + BossVa_Tumor(globalCtx, this, 1, tmp16, 0.0f, 0.0f, TUMOR_BODY, i, true); + } + + BossVa_Tumor(globalCtx, this, 1, tmp16, 0.0f, 0.0f, TUMOR_BODY, 11.0f, true); + this->unk_1B0--; + } + + if (this->unk_1B0 == 0) { + sCsState++; + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + } + break; + case DEATH_CORE_DEAD: + this->unk_1AC += 0x1862; + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.12f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.075f) + 1.0f; + if (!this->isDead) { + this->burst++; + this->isDead++; + this->timer = 30; + sCsState++; + EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_BALINADE_DEAD, 1, 1, + 0x28); + this->onCeiling = 2; // Not used by body + BossVa_SetDeathEnv(globalCtx); + func_8002DF54(globalCtx, &this->actor, 8); + } + break; + case DEATH_CORE_BURST: + if (this->timer == 13) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + } + + this->timer--; + if (this->timer == 0) { + sCameraNextAt.x = this->actor.world.pos.x; + sCameraNextAt.y = this->actor.world.pos.y + 30.0f; + sCameraNextAt.z = this->actor.world.pos.z; + + sCameraNextEye.x = (Math_SinS(player->actor.shape.rot.y) * -130.0f) + player->actor.world.pos.x; + sCameraNextEye.z = (Math_CosS(player->actor.shape.rot.y) * -130.0f) + player->actor.world.pos.z; + sCameraNextEye.y = player->actor.world.pos.y + 55.0f; + + sCameraAtMaxVel = sCameraEyeMaxVel = sZeroVec; + + sCsState++; + this->timer = 133; + } + break; + case DEATH_MUSIC: + Math_SmoothStepToF(&sCameraEyeMaxVel.x, 1.5f, 0.3f, 0.05f, 0.015f); + sCameraEyeMaxVel.z = sCameraEyeMaxVel.x; + sCameraEyeMaxVel.y = sCameraEyeMaxVel.z; + sCameraAtMaxVel = sCameraEyeMaxVel; + + this->timer--; + if (this->timer == 0) { + Gameplay_ClearCamera(globalCtx, sCsCamera); + sCsCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + + camera->eyeNext = camera->eye = sCameraEye; + + camera->at = sCameraAt; + + func_8002DF54(globalCtx, &this->actor, 7); + sCsState++; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_B_HEART, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + + for (i = 2, sp7C = 2; i > 0; i--) { + if (Math_Vec3f_DistXYZ(&sWarpPos[i], &player->actor.world.pos) < + Math_Vec3f_DistXYZ(&sWarpPos[i - 1], &player->actor.world.pos)) { + sp7C = i - 1; + } + } + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_RU1, sWarpPos[sp7C].x, sWarpPos[sp7C].y, + sWarpPos[sp7C].z, 0, 0, 0, 0); + } + case DEATH_FINISH: + Rand_CenteredFloat(0.5f); + globalCtx->envCtx.fillScreen = false; + break; + } + + if (sCsCamera != 0) { + Math_SmoothStepToF(&sCameraEye.x, sCameraNextEye.x, 0.3f, sCameraEyeMaxVel.x, 0.15f); + Math_SmoothStepToF(&sCameraEye.y, sCameraNextEye.y, 0.3f, sCameraEyeMaxVel.y, 0.15f); + Math_SmoothStepToF(&sCameraEye.z, sCameraNextEye.z, 0.3f, sCameraEyeMaxVel.z, 0.15f); + Math_SmoothStepToF(&sCameraAt.x, sCameraNextAt.x, 0.3f, sCameraAtMaxVel.x, 0.15f); + Math_SmoothStepToF(&sCameraAt.y, sCameraNextAt.y, 0.3f, sCameraAtMaxVel.y, 0.15f); + Math_SmoothStepToF(&sCameraAt.z, sCameraNextAt.z, 0.3f, sCameraAtMaxVel.z, 0.15f); + Gameplay_CameraSetAtEye(globalCtx, sCsCamera, &sCameraAt, &sCameraEye); + } + + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToF(&this->actor.shape.yOffset, -480.0f, 1.0f, 30.0f, 0.0f); + Math_SmoothStepToS(&this->vaBodySpinRate, 0, 1, 0xC8, 0); + Math_SmoothStepToS(&this->vaCamRotMod, 0, 1, 0xC8, 0); + Math_SmoothStepToS(&this->bodyGlow, 200, 1, 10, 0); + if (globalCtx->envCtx.screenFillColor[3] != 0) { + globalCtx->envCtx.screenFillColor[3] -= 50; + } + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + this->actor.shape.rot.y += this->vaBodySpinRate; + this->unk_1AC += this->vaCamRotMod; + + this->unk_1A0 = (Math_CosS(this->unk_1AC) * 0.1f) + 1.0f; + this->unk_1A4 = (Math_SinS(this->unk_1AC) * 0.05f) + 1.0f; +} + +void BossVa_SetupSupportIntro(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeSupportAttachedAnim, 0.0f, 0.0f, + Animation_GetLastFrame(&gBarinadeSupportAttachedAnim), ANIMMODE_LOOP_INTERP, 0.0f); + this->timer = 0; + BossVa_SetupAction(this, BossVa_SupportIntro); +} + +void BossVa_SupportIntro(BossVa* this, GlobalContext* globalCtx) { + BossVa_AttachToBody(this); + if (sCsState == BOSSVA_BATTLE) { + BossVa_SetupSupportAttached(this, globalCtx); + } else if (sCsState >= INTRO_REVERSE_CAMERA) { + this->timer++; + if ((this->timer % 2) == 0) { + BossVa_Spark(globalCtx, this, 2, 90, 5.0f, 0.0f, SPARK_BODY, ((this->timer & 0x20) >> 5) + 1, true); + } + + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToF(&this->skelAnime.playSpeed, 1.0f, 1.0f, 0.05f, 0.0f); + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + } +} + +void BossVa_SetupSupportAttached(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeSupportAttachedAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gBarinadeSupportAttachedAnim), ANIMMODE_LOOP, 0.0f); + this->timer = this->actor.params * 10; + BossVa_SetupAction(this, BossVa_SupportAttached); +} + +void BossVa_SupportAttached(BossVa* this, GlobalContext* globalCtx) { + this->timer++; + if (sBodyState & 0x7F) { + Actor_SetColorFilter(&this->actor, 0, 255, 0, 12); + if (Rand_ZeroOne() > 0.5f) { + Animation_Change(&this->skelAnime, &gBarinadeSupportDamage1Anim, 1.0f, 0.0f, + Animation_GetLastFrame(&gBarinadeSupportDamage1Anim), ANIMMODE_ONCE, 0.0f); + } else { + Animation_Change(&this->skelAnime, &gBarinadeSupportDamage2Anim, 1.0f, 0.0f, + Animation_GetLastFrame(&gBarinadeSupportDamage2Anim), ANIMMODE_ONCE, 0.0f); + } + } + + if (SkelAnime_Update(&this->skelAnime)) { + Animation_Change(&this->skelAnime, &gBarinadeSupportAttachedAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gBarinadeSupportAttachedAnim), ANIMMODE_LOOP, 0.0f); + } + + BossVa_AttachToBody(this); + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + if (this->colliderSph.base.acFlags & AC_HIT) { + BossVa_SetupSupportCut(this, globalCtx); + } else { + if (this->actor.colorFilterTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + } + + if ((this->timer % 2) == 0) { + BossVa_Spark(globalCtx, this, 1, 100, 5.0f, 0.0f, SPARK_BODY, ((this->timer & 0x20) >> 5) + 1, true); + } + } +} + +void BossVa_SetupSupportCut(BossVa* this, GlobalContext* globalCtx) { + s32 stumpParams = this->actor.params + BOSSVA_STUMP_1; + + sBodyState++; + sFightPhase++; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BOSS_VA, this->armTip.x, this->armTip.y + 20.0f, this->armTip.z, + 0, this->actor.shape.rot.y, 0, stumpParams); + Camera_AddQuake(&globalCtx->mainCamera, 2, 11, 8); + this->burst = false; + this->timer2 = 0; + BossVa_SetupAction(this, BossVa_SupportCut); +} + +void BossVa_SupportCut(BossVa* this, GlobalContext* globalCtx) { + BossVa* vaBody = GET_BODY(this); + f32 lastFrame; + + BossVa_AttachToBody(this); + + if (this->onCeiling) { + lastFrame = Animation_GetLastFrame(&gBarinadeSupportCutAnim); + this->onCeiling = false; + this->timer = (s32)(Rand_ZeroOne() * 10.0f) + 5; + SkelAnime_Free(&this->skelAnime, globalCtx); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBarinadeCutSupportSkel, &gBarinadeSupportCutAnim, NULL, NULL, + 0); + Animation_Change(&this->skelAnime, &gBarinadeSupportCutAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + sBodyState = 0; + GET_BODY(this)->actor.shape.yOffset -= 60.0f; + + switch (this->actor.params) { + case BOSSVA_SUPPORT_1: + GET_BODY(this)->actor.world.rot.x += 0x4B0; + break; + case BOSSVA_SUPPORT_2: + GET_BODY(this)->actor.world.rot.x -= 0x258; + GET_BODY(this)->actor.world.rot.z -= 0x4E2; + break; + case BOSSVA_SUPPORT_3: + GET_BODY(this)->actor.world.rot.x -= 0x258; + GET_BODY(this)->actor.world.rot.z += 0x4E2; + break; + } + } + + Math_SmoothStepToS(&this->headRot.x, vaBody->vaBodySpinRate * -3, 1, 0x4B0, 0); + if (SkelAnime_Update(&this->skelAnime)) { + lastFrame = Animation_GetLastFrame(&gBarinadeSupportDetachedAnim); + Animation_Change(&this->skelAnime, &gBarinadeSupportDetachedAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, + 0.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + } + + if ((this->timer == 0) && (sCsState < DEATH_START)) { + this->timer = (s32)(Rand_ZeroOne() * 10.0f) + 10; + BossVa_BloodDroplets(globalCtx, &this->armTip, this->headRot.x, this->actor.shape.rot.y); + } + + if (sCsState >= DEATH_START) { + Math_SmoothStepToF(&this->skelAnime.playSpeed, 0.0f, 0.3f, 0.25f, 0.125f); + } + + switch (sCsState) { + case DEATH_SHELL_BURST: + sCameraEye = sCameraNextEye; + sCameraAt = sCameraNextAt; + Math_SmoothStepToF(&sCameraEye.x, sCameraNextAt.x, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToF(&sCameraEye.z, sCameraNextAt.z, 1.0f, 10.0f, 0.0f); + sCameraEye.y += 20.0f; + sCsState++; + + case DEATH_CORE_TUMORS: + case DEATH_CORE_DEAD: + case DEATH_CORE_BURST: + if (!this->burst) { + if ((globalCtx->gameplayFrames % 2) != 0) { + BossVa_Tumor(globalCtx, this, 1, (s16)Rand_CenteredFloat(5.0f) + 6, 7.0f, 5.0f, TUMOR_ARM, + (this->timer2 >> 3) + 1, true); + } + + this->timer2++; + if (this->timer2 >= 32) { + this->burst++; + this->isDead = true; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BREAK2); + if (this->actor.params == BOSSVA_SUPPORT_3) { + sCsState++; + } + } + } else { + this->timer2--; + if (this->timer2 == 0) { + Actor_Kill(&this->actor); + } + } + break; + } + + this->timer--; +} + +void BossVa_SetupStump(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeStumpAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gBarinadeStumpAnim), + ANIMMODE_ONCE, 0.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_Stump); +} + +void BossVa_Stump(BossVa* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) && (Rand_ZeroOne() < 0.3f)) { + this->skelAnime.curFrame -= Rand_ZeroOne() * 3.0f; + } + + if (sCsState >= DEATH_START) { + Actor_Kill(&this->actor); + } +} + +void BossVa_SetupZapperIntro(BossVa* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gBarinadeZapperIdleAnim); + + Animation_Change(&this->skelAnime, &gBarinadeZapperIdleAnim, 1.0f, lastFrame - 1.0f, lastFrame, + ANIMMODE_LOOP_INTERP, -6.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_ZapperIntro); +} + +void BossVa_ZapperIntro(BossVa* this, GlobalContext* globalCtx) { + BossVa_AttachToBody(this); + + switch (sCsState) { + case INTRO_TITLE: + case INTRO_BRIGHTEN: + case INTRO_FINISH: + SkelAnime_Update(&this->skelAnime); + break; + case BOSSVA_BATTLE: + BossVa_SetupZapperAttack(this, globalCtx); + break; + } + + Math_SmoothStepToS(&this->unk_1F2, this->actor.shape.rot.y - this->actor.shape.rot.x, 1, 0x2EE, 0); + Math_SmoothStepToS(&this->unk_1F0, this->skelAnime.jointTable[7].z, 1, 0x2EE, 0); +} + +void BossVa_SetupZapperAttack(BossVa* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gBarinadeZapperIdleAnim); + + Animation_Change(&this->skelAnime, &gBarinadeZapperIdleAnim, 1.0f, lastFrame - 1.0f, lastFrame, + ANIMMODE_LOOP_INTERP, -6.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_ZapperAttack); +} + +void BossVa_ZapperAttack(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnBoom* boomerang; + Actor* boomTarget; + s16 yaw; + s16 sp98; + s16 sp96; + s16 sp94; + s16 tmp17; + s16 sp90 = 0x1F4; + s16 sp8E; + u32 sp88; + Vec3f sp7C; + s32 pad3; + f32 sp74; + s32 i; + s16 sp6E; + s16 sp6C; + f32 sp68; + f32 sp64; + f32 sp60; + f32 sp5C; + s16 sp5A; + s16 sp58; + s16 sp56; + s16 sp54; + f32 sp50; + + boomerang = BossVa_FindBoomerang(globalCtx); + + if ((boomerang == NULL) || (boomerang->moveTo == NULL) || (boomerang->moveTo == &player->actor)) { + sp7C = player->actor.world.pos; + sp7C.y += 10.0f; + sp8E = 0x3E80; + } else { + sp74 = R_UPDATE_RATE * 0.5f; + sp8E = 0x4650; + + boomTarget = boomerang->moveTo; + sp7C = boomerang->actor.world.pos; + sp6C = boomerang->actor.world.rot.y; + sp56 = boomerang->actor.world.rot.x; + + for (i = boomerang->returnTimer; i >= 3; i--) { + sp6E = Math_Vec3f_Yaw(&sp7C, &boomTarget->focus.pos); + sp5A = sp6C - sp6E; + sp58 = Math_Vec3f_Pitch(&sp7C, &boomTarget->focus.pos); + sp54 = sp56 - sp58; + + sp50 = (200.0f - Math_Vec3f_DistXYZ(&sp7C, &boomTarget->focus.pos)) * 0.005f; + if (sp50 < 0.12f) { + sp50 = 0.12f; + } + + if (sp5A < 0) { + sp5A = -sp5A; + } + + if (sp54 < 0) { + sp54 = -sp54; + } + + Math_ScaledStepToS(&sp6C, sp6E, sp5A * sp50); + Math_ScaledStepToS(&sp56, sp58, sp54 * sp50); + + sp68 = -Math_SinS(sp56) * 12.0f; + sp5C = Math_CosS(sp56) * 12.0f; + sp64 = Math_SinS(sp6C) * sp5C; + sp60 = Math_CosS(sp6C); + sp7C.x += sp64 * sp74; + sp7C.y += sp68 * sp74; + sp7C.z += sp60 * sp5C * sp74; + } + sp90 = 0x3E80; + } + + SkelAnime_Update(&this->skelAnime); + BossVa_AttachToBody(this); + if (sFightPhase >= PHASE_4) { + BossVa_SetupZapperEnraged(this, globalCtx); + return; + } + + if (sBodyState & 0x7F) { + BossVa_SetupZapperDamaged(this, globalCtx); + return; + } + + if ((sFightPhase < PHASE_4) && (GET_BODY(this)->actor.speedXZ != 0.0f)) { + BossVa_SetupZapperHold(this, globalCtx); + return; + } + + sp98 = Math_Vec3f_Yaw(&sp7C, &this->armTip); + tmp17 = sp98 - this->actor.shape.rot.y; + + if ((sp8E >= ABS(tmp17) || this->burst) && !(sBodyState & 0x80) && !(player->stateFlags1 & 0x04000000)) { + + if (!this->burst) { + sp94 = sp98 - this->actor.shape.rot.y; + + if (ABS(sp94) > 0x1770) { + sp94 = (sp94 > 0) ? 0x1770 : -0x1770; + } + + tmp17 = Math_SmoothStepToS(&this->unk_1E6, sp94, 1, 0x6D6, 0); + sp88 = ABS(tmp17); + + sp94 = sp98 - sp94; + + if (ABS(sp94) > 0x1770) { + sp94 = (sp94 > 0) ? 0x1770 : -0x1770; + } + + tmp17 = Math_SmoothStepToS(&this->unk_1EC, sp94, 1, 0x6D6, 0); + sp88 += ABS(tmp17); + + yaw = Math_Vec3f_Yaw(&this->zapHeadPos, &sp7C); + tmp17 = Math_SmoothStepToS(&this->unk_1F2, yaw - 0x4000, 1, 0x9C4, 0); + sp88 += ABS(tmp17); + + sp96 = this->actor.shape.rot.x + this->skelAnime.jointTable[1].z + this->skelAnime.jointTable[2].z + + this->skelAnime.jointTable[3].z + this->skelAnime.jointTable[4].z + this->skelAnime.jointTable[5].z; + + yaw = Math_Vec3f_Pitch(&sp7C, &this->zapNeckPos); + tmp17 = Math_SmoothStepToS(&this->unk_1EA, yaw - sp96, 1, 0xFA0, 0); + sp88 += ABS(tmp17); + + yaw = Math_Vec3f_Pitch(&this->zapHeadPos, &sp7C); + tmp17 = Math_SmoothStepToS(&this->unk_1F0, -yaw, 1, 0xFA0, 0); + sp88 += ABS(tmp17); + + this->skelAnime.playSpeed = 0.0f; + if (Math_SmoothStepToF(&this->skelAnime.curFrame, 0.0f, 1.0f, 2.0f, 0.0f) == 0.0f) { + if (sp88 < sp90) { + this->timer2 = 0; + this->burst++; + this->unk_1D8 = sp7C; + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + } + } + } else { + if (this->burst || (this->timer2 < 0)) { + if (this->colliderLightning.base.atFlags & AT_HIT) { + if (this->timer2 > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_HIT_RINK); + BossVa_SetSparkEnv(globalCtx); + this->timer2 = -1; + GET_BODY(this)->onCeiling = 6; // not used by body + } + } else if (this->timer2 > 0) { + this->timer2 = 0; + } + + if ((this->timer2 < 0) && (player->stateFlags1 & 0x4000000)) { + BossVa_Spark(globalCtx, this, 1, 30, 0.0f, 0.0f, SPARK_LINK, 0.0f, true); + } + } + + Math_SmoothStepToS(&this->unk_1E6, 0, 1, 0x6D6, 0); + Math_SmoothStepToS(&this->unk_1EC, 0, 1, 0x6D6, 0); + Math_SmoothStepToS(&this->unk_1EA, 0, 1, 0x6D6, 0); + Math_SmoothStepToS(&this->unk_1F2, this->actor.shape.rot.y - this->actor.shape.rot.x, 1, 0x6D6, 0); + Math_SmoothStepToS(&this->unk_1F0, this->skelAnime.jointTable[7].z, 1, 0x6D6, 0); + Math_SmoothStepToF(&this->skelAnime.playSpeed, 1.0f, 1.0f, 0.05f, 0.0f); + this->burst = false; + } + + if (this->burst && (this->burst != 2)) { // burst can never be 2 + if (this->timer2 >= 32) { + if (this->timer2 == 32) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_THUNDER); + } + BossVa_Spark(globalCtx, this, 2, 110, 15.0f, 15.0f, SPARK_BLAST, 5.0f, true); + BossVa_Spark(globalCtx, this, 2, 110, 15.0f, 15.0f, SPARK_BLAST, 6.0f, true); + BossVa_Spark(globalCtx, this, 2, 110, 15.0f, 15.0f, SPARK_BLAST, 7.0f, true); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderLightning.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderLightning.base); + } else { + BossVa_Spark(globalCtx, this, 2, 50, 15.0f, 0.0f, SPARK_BODY, (this->timer2 >> 3) + 1, true); + if (this->timer2 == 30) { + BossVa_SetSparkEnv(globalCtx); + } + if (this->timer2 == 20) { + Vec3f sp44 = this->zapHeadPos; + + BossVa_SpawnZapperCharge(globalCtx, sVaEffects, this, &sp44, &this->headRot, 100, 0); + } + } + + this->timer2++; + if (this->timer2 >= 40) { + this->burst = false; + } + } +} + +void BossVa_SetupZapperDamaged(BossVa* this, GlobalContext* globalCtx) { + if (Rand_ZeroOne() > 0.5f) { + Animation_Change(&this->skelAnime, &gBarinadeZapperDamage1Anim, 0.5f, 0.0f, + Animation_GetLastFrame(&gBarinadeZapperDamage1Anim), ANIMMODE_ONCE_INTERP, 4.0f); + } else { + Animation_Change(&this->skelAnime, &gBarinadeZapperDamage2Anim, 0.5f, 0.0f, + Animation_GetLastFrame(&gBarinadeZapperDamage2Anim), ANIMMODE_ONCE_INTERP, 4.0f); + } + + Actor_SetColorFilter(&this->actor, 0, 255, 0, 12); + this->burst = false; + BossVa_SetupAction(this, BossVa_ZapperDamaged); +} + +void BossVa_ZapperDamaged(BossVa* this, GlobalContext* globalCtx) { + BossVa_AttachToBody(this); + Math_SmoothStepToS(&this->unk_1E6, 0, 1, 0xFA0, 0); + Math_SmoothStepToS(&this->unk_1E4, 0, 1, 0xFA0, 0); + Math_SmoothStepToS(&this->unk_1EC, 0, 1, 0xFA0, 0); + Math_SmoothStepToS(&this->unk_1EA, 0, 1, 0xFA0, 0); + Math_SmoothStepToS(&this->unk_1F2, this->actor.shape.rot.y - this->actor.shape.rot.x, 1, 0x2EE, 0); + Math_SmoothStepToS(&this->unk_1F0, this->skelAnime.jointTable[7].z, 1, 0x2EE, 0); + if (SkelAnime_Update(&this->skelAnime)) { + if (sFightPhase >= PHASE_4) { + BossVa_SetupZapperEnraged(this, globalCtx); + } else { + BossVa_SetupZapperAttack(this, globalCtx); + } + } +} + +void BossVa_SetupZapperDeath(BossVa* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gBarinadeZapperIdleAnim); + + Animation_Change(&this->skelAnime, &gBarinadeZapperIdleAnim, Rand_ZeroOne() + 0.25f, Rand_ZeroOne() * 3.0f, + lastFrame, ANIMMODE_LOOP_INTERP, -6.0f); + this->burst = false; + this->timer2 = (this->actor.params * -6) + 18; + this->unk_1B0 = 0; + BossVa_SetupAction(this, BossVa_ZapperDeath); +} + +void BossVa_ZapperDeath(BossVa* this, GlobalContext* globalCtx) { + f32 sp3C = 55.0f; + f32 tmpf1; + f32 tmpf2; + + BossVa_AttachToBody(this); + if (((globalCtx->gameplayFrames % 32) == 0) && (sCsState <= DEATH_BODY_TUMORS)) { + this->unk_1E8 = Rand_CenteredFloat(0x4000); + this->unk_1EE = Rand_CenteredFloat(0x4000); + this->unk_1F4 = (s16)Rand_CenteredFloat(0x4000) + this->actor.shape.rot.y - this->actor.shape.rot.x; + } else { + Math_SmoothStepToF(&this->skelAnime.playSpeed, 0.0f, 1.0f, 0.025f, 0.0f); + } + + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->unk_1E6, this->unk_1E8, 1, (s16)Rand_CenteredFloat(500.0f) + 0x1F4, 0); + Math_SmoothStepToS(&this->unk_1E4, 0, 1, 0x1F4, 0); + Math_SmoothStepToS(&this->unk_1EC, this->unk_1EE, 1, (s16)Rand_CenteredFloat(500.0f) + 0x1F4, 0); + Math_SmoothStepToS(&this->unk_1EA, 0, 1, 0x1F4, 0); + Math_SmoothStepToS(&this->unk_1F2, this->unk_1F4, 1, (s16)Rand_CenteredFloat(500.0f) + 0x1F4, 0); + + switch (sCsState) { + case DEATH_ZAPPER_2: + sp3C = -55.0f; + case DEATH_ZAPPER_1: + case DEATH_ZAPPER_3: + if (!this->burst) { + if (((this->actor.params == BOSSVA_ZAPPER_1) && (this->timer2 < 16)) || + ((this->actor.params == BOSSVA_ZAPPER_2) && (this->timer2 < 24)) || + (this->actor.params == BOSSVA_ZAPPER_3)) { + + if ((this->timer2 % 2) == 0 && (this->timer2 >= 0)) { + if (this->timer2 < 8) { + BossVa_Tumor(globalCtx, this, 1, (s16)Rand_CenteredFloat(5.0f) + 0xD, 0.0f, 0.0f, TUMOR_ARM, + 0.6f, true); + } else { + BossVa_Tumor(globalCtx, this, 1, (s16)Rand_CenteredFloat(5.0f) + 6, 0.0f, 7.0f, TUMOR_ARM, + (this->timer2 >> 3) + 1, true); + } + + BossVa_Spark(globalCtx, this, 2, 50, 15.0f, 0.0f, SPARK_BODY, (this->timer2 >> 3) + 1, true); + } + + this->timer2++; + if (this->timer2 >= 32) { + this->burst++; + this->isDead = true; + BossVa_SetDeathEnv(globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BREAK2); + } + } else { + this->burst++; + this->isDead = true; + this->timer2 = 32; + sCsState++; + } + + if ((this->actor.params - BOSSVA_ZAPPER_1 + DEATH_ZAPPER_1) == sCsState) { + sCameraAt.x = this->zapNeckPos.x; + sCameraEye.y = sCameraAt.y = this->zapNeckPos.y; + sCameraAt.z = this->zapNeckPos.z; + sCameraEye.x = (Math_CosS(-(this->actor.shape.rot.y + this->unk_1B0)) * sp3C) + this->zapNeckPos.x; + sCameraEye.z = (Math_SinS(-(this->actor.shape.rot.y + this->unk_1B0)) * sp3C) + this->zapNeckPos.z; + this->unk_1B0 += 0x15E; + } + } else { + this->timer2--; + if (this->timer2 == 0) { + if (this->actor.params == BOSSVA_ZAPPER_3) { + sCsState++; + } + Actor_Kill(&this->actor); + } + } + break; + } +} + +void BossVa_SetupZapperEnraged(BossVa* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gBarinadeZapperIdleAnim); + + Animation_Change(&this->skelAnime, &gBarinadeZapperIdleAnim, 1.0f, lastFrame - 1.0f, lastFrame, + ANIMMODE_LOOP_INTERP, -6.0f); + this->burst = false; + BossVa_SetupAction(this, BossVa_ZapperEnraged); +} + +void BossVa_ZapperEnraged(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 tmp16; + s16 sp6C; + s16 sp6A; + s16 sp68; + s16 yaw; + u32 sp60; + Vec3f sp54 = player->actor.world.pos; + + sp54.y += 10.0f; + SkelAnime_Update(&this->skelAnime); + BossVa_AttachToBody(this); + if (sFightPhase >= PHASE_DEATH) { + BossVa_SetupZapperDeath(this, globalCtx); + return; + } + + if (sBodyState & 0x7E) { + BossVa_SetupZapperDamaged(this, globalCtx); + return; + } + + sp54.y += 25.0; + + sp6C = Math_Vec3f_Yaw(&sp54, &this->armTip); + tmp16 = sp6C - this->actor.shape.rot.y; + + if ((ABS(tmp16) <= 0x4650 || this->burst) && !(sBodyState & 0x80) && !(player->stateFlags1 & 0x04000000)) { + if (!this->burst) { + + sp68 = sp6C - this->actor.shape.rot.y; + if (ABS(sp68) > 0x1770) { + sp68 = (sp68 > 0) ? 0x1770 : -0x1770; + } + + tmp16 = Math_SmoothStepToS(&this->unk_1E6, sp68, 1, 0xDAC, 0); + sp60 = ABS(tmp16); + + sp68 = sp6C - sp68; + if (ABS(sp68) > 0x1770) { + sp68 = sp68 > 0 ? 0x1770 : -0x1770; + } + + tmp16 = Math_SmoothStepToS(&this->unk_1EC, sp68, 1, 0xDAC, 0); + sp60 += ABS(tmp16); + + yaw = Math_Vec3f_Yaw(&this->zapHeadPos, &sp54); + tmp16 = Math_SmoothStepToS(&this->unk_1F2, yaw - 0x4000, 1, 0xEA6, 0); + sp60 += ABS(tmp16); + + sp6A = this->actor.shape.rot.x + this->skelAnime.jointTable[1].x + this->skelAnime.jointTable[2].x + + this->skelAnime.jointTable[3].x + this->skelAnime.jointTable[4].x + this->skelAnime.jointTable[5].x; + + yaw = Math_Vec3f_Pitch(&sp54, &this->zapNeckPos); + tmp16 = Math_SmoothStepToS(&this->unk_1EA, yaw - sp6A, 1, 0x1B58, 0); + sp60 += ABS(tmp16); + + yaw = Math_Vec3f_Pitch(&this->zapHeadPos, &sp54); + tmp16 = Math_SmoothStepToS(&this->unk_1F0, -yaw, 1, 0x1B58, 0); + sp60 += ABS(tmp16); + + this->skelAnime.playSpeed = 0.0f; + if ((Math_SmoothStepToF(&this->skelAnime.curFrame, 0.0f, 1.0f, 3.0f, 0.0f) == 0.0f) && (sp60 < 0x258)) { + this->timer2 = 0; + this->burst++; + this->unk_1D8 = sp54; + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + } + } + } else { + if (this->burst || (this->timer2 < 0)) { + if (this->colliderLightning.base.atFlags & AT_HIT) { + if (this->timer2 > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_HIT_RINK); + BossVa_SetSparkEnv(globalCtx); + this->timer2 = -1; + GET_BODY(this)->onCeiling = 6; // not used by body + } + } else if (this->timer2 > 0) { + this->timer2 = 0; + } + + if ((this->timer2 < 0) && (player->stateFlags1 & 0x4000000)) { + BossVa_Spark(globalCtx, this, 1, 30, 0.0f, 0, SPARK_LINK, 0.0f, true); + } + } + + Math_SmoothStepToS(&this->unk_1E6, 0, 1, 0xEA6, 0); + Math_SmoothStepToS(&this->unk_1EC, 0, 1, 0xEA6, 0); + Math_SmoothStepToS(&this->unk_1EA, 0, 1, 0xEA6, 0); + Math_SmoothStepToS(&this->unk_1F2, this->actor.shape.rot.y - this->actor.shape.rot.x, 1, 0xEA6, 0); + Math_SmoothStepToS(&this->unk_1F0, this->skelAnime.jointTable[7].z, 1, 0xEA6, 0); + Math_SmoothStepToF(&this->skelAnime.playSpeed, 1.0f, 1.0f, 0.05f, 0.0f); + this->burst = false; + } + + if (this->burst && (this->burst != 2)) { // burst can never be 2 + if (this->timer2 >= 16) { + if (this->timer2 == 18) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_THUNDER); + } + + BossVa_Spark(globalCtx, this, 2, 110, 15.0f, 15.0f, SPARK_BLAST, 5.0f, true); + BossVa_Spark(globalCtx, this, 2, 110, 15.0f, 15.0f, SPARK_BLAST, 6.0f, true); + BossVa_Spark(globalCtx, this, 2, 110, 15.0f, 15.0f, SPARK_BLAST, 7.0f, true); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderLightning.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderLightning.base); + } else { + BossVa_Spark(globalCtx, this, 2, 50, 15.0f, 0.0f, SPARK_BODY, (this->timer2 >> 1) + 1, true); + if (this->timer2 == 14) { + BossVa_SetSparkEnv(globalCtx); + } + if (this->timer2 == 4) { + Vec3f sp48 = this->zapHeadPos; + + BossVa_SpawnZapperCharge(globalCtx, sVaEffects, this, &sp48, &this->headRot, 100, 0); + } + } + + this->timer2++; + if (this->timer2 >= 24) { + this->burst = false; + } + } +} + +void BossVa_SetupZapperHold(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeZapperDamage2Anim, 0.0f, 0.0f, + Animation_GetLastFrame(&gBarinadeZapperDamage2Anim), ANIMMODE_ONCE_INTERP, -6.0f); + this->burst = false; + BossVa_SetupAction(this, BossVa_ZapperHold); +} + +void BossVa_ZapperHold(BossVa* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + BossVa_AttachToBody(this); + Math_SmoothStepToS(&this->unk_1E6, 0, 1, 0x1770, 0); + Math_SmoothStepToS(&this->unk_1E4, 0, 1, 0x1770, 0); + Math_SmoothStepToS(&this->unk_1EC, 0, 1, 0x1770, 0); + Math_SmoothStepToS(&this->unk_1EA, 0, 1, 0x1770, 0); + Math_SmoothStepToS(&this->unk_1F2, this->actor.shape.rot.y - 0x4000, 1, 0x2710, 0); + Math_SmoothStepToS(&this->unk_1F0, this->skelAnime.jointTable[7].z - 0x1388, 1, 0x1770, 0); + if (GET_BODY(this)->actor.speedXZ == 0.0f) { + BossVa_SetupZapperAttack(this, globalCtx); + } +} + +void BossVa_SetupBariIntro(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeBariAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gBarinadeBariAnim), + ANIMMODE_LOOP, 0.0f); + this->unk_1A0 = 60.0f; + this->unk_1A4 = Rand_ZeroOne() * 360.0f; + this->timer2 = 64; + this->unk_1F0 = 120; + this->unk_1A8 = 0.0f; + this->actor.world.pos.x = sInitPosOffsets[this->actor.params + 10].x + this->actor.home.pos.x; + this->actor.world.pos.y = sInitPosOffsets[this->actor.params + 10].y + this->actor.home.pos.y; + this->actor.world.pos.z = sInitPosOffsets[this->actor.params + 10].z + this->actor.home.pos.z; + this->timer = 45; + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_BariIntro); +} + +void BossVa_BariIntro(BossVa* this, GlobalContext* globalCtx) { + Vec3f sp54 = this->actor.home.pos; + f32 sp50 = 40.0f; + s16 sp4E; + s16 tmp; + + if (this->actor.home.pos.y >= 0.0f) { + sp54.y += 25.0f; + } + + this->unk_1A4 += Rand_ZeroOne() * 0.25f; + + switch (sCsState) { + case INTRO_LOOK_BARI: + if (this->actor.params == BOSSVA_BARI_UPPER_1) { + func_8002DF54(globalCtx, &this->actor, 1); + if (Math_SmoothStepToF(&this->actor.world.pos.y, 60.0f, 0.3f, 1.0f, 0.15f) == 0.0f) { + this->timer--; + if (this->timer == 0) { + sCsState++; + } + } + } + this->actor.shape.rot.x = 0; + break; + case INTRO_REVERSE_CAMERA: + case INTRO_SUPPORT_CAMERA: + case INTRO_BODY_SOUND: + case INTRO_LOOK_SUPPORT: + if (this->actor.params != BOSSVA_BARI_UPPER_1) { + Math_SmoothStepToF(&this->actor.world.pos.y, + sInitPosOffsets[this->actor.params + 10].y + this->actor.home.pos.y, 0.3f, 1.0f, + 0.15f); + this->actor.world.pos.x += (Math_SinF(this->unk_1A4 * 0.25f) * 0.5f); + } else { + Math_SmoothStepToF(&this->actor.world.pos.y, 60.0f, 0.3f, 1.0f, 0.15f); + } + this->actor.world.pos.y += Math_SinF(this->unk_1A4) * (2.0f - Math_SinF(this->unk_1A4)); + break; + case INTRO_CALL_BARI: + case INTRO_ATTACH_BARI: + if ((this->timer2 > 15) && (this->timer < 0)) { + Math_SmoothStepToF(&this->actor.world.pos.x, sp54.x, 1.0f, 6.5f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, sp54.y, 1.0f, 6.5f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, sp54.z, 1.0f, 6.5f, 0.0f); + + sp50 = Math_Vec3f_DistXYZ(&sp54, &this->actor.world.pos); + if (sp50 <= 60.0f) { + tmp = Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.home.rot.x, 1, 0x7D0, 0); + sp4E = ABS(tmp); + + tmp = Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 1, 0x7D0, 0); + sp4E += ABS(tmp); + + if ((sp50 == 0.0f) && (sp4E == 0)) { + if (!this->isDead) { + if (this->actor.params >= BOSSVA_BARI_LOWER_1) { + if (this->actor.params == BOSSVA_BARI_LOWER_1) { + sBodyBari[0]++; + } else { + sBodyBari[this->actor.params - BOSSVA_BARI_UPPER_1]++; + } + } else { + sBodyBari[this->actor.params - BOSSVA_BARI_UPPER_1 + 1]++; + } + this->timer = -30; + this->isDead++; + } else { + this->timer++; + if (this->timer == 0) { + Actor_Kill(&this->actor); + } + } + return; + } + } + } + case INTRO_UNUSED_CALL_BARI: + this->timer--; + if (this->timer == 0) { + this->timer2 = 0; + } else { + func_80035844(&GET_BODY(this)->actor.world.pos, &this->actor.world.pos, &this->actor.world.rot, false); + this->unk_1A0 = Math_Vec3f_DistXYZ(&GET_BODY(this)->actor.world.pos, &this->actor.world.pos); + if (sp50 > 30.0f) { + BossVa_Spark(globalCtx, this, 1, 80, 15.0f, 0.0f, SPARK_BARI, 1.0f, true); + } + } + break; + case BOSSVA_BATTLE: + this->timer++; + if (this->timer == 0) { + Actor_Kill(&this->actor); + } + return; + case INTRO_TITLE: + case INTRO_BRIGHTEN: + case INTRO_FINISH: + break; + } + + if (((globalCtx->gameplayFrames % 4) == 0) && (sCsState < INTRO_ATTACH_BARI)) { + BossVa_Spark(globalCtx, this, 1, 70, 25.0f, 20.0f, SPARK_BARI, 2.0f, true); + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } +} + +void BossVa_SetupBariPhase3Attack(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeBariAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gBarinadeBariAnim), + ANIMMODE_LOOP, 0.0f); + this->timer2 = 0x80; + this->unk_1F0 = 0x78; + this->unk_1A0 = 60.0f; + this->unk_1A8 = 0.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_BariPhase3Attack); +} + +void BossVa_BariPhase3Attack(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnBoom* boomerang; + Vec3f sp54 = GET_BODY(this)->unk_1D8; + s16 sp52; + s32 pad; + + this->unk_1A4 += Rand_ZeroOne() * 0.5f; + sp52 = this->timer2 & 0x1FF; + + if ((globalCtx->gameplayFrames % 128) == 0) { + this->vaBariUnused.x = (s16)(Rand_ZeroOne() * 100.0f) + 100; + } + + Math_SmoothStepToS(&this->vaBariUnused.z, this->vaBariUnused.x, 1, 0x1E, 0); + this->vaBariUnused.y += this->vaBariUnused.z; + if ((this->colliderLightning.base.atFlags & AT_HIT) || (this->colliderSph.base.atFlags & AT_HIT)) { + if ((this->colliderLightning.base.at == &player->actor) || (this->colliderSph.base.at == &player->actor)) { + func_8002F71C(globalCtx, &this->actor, 8.0f, GET_BODY(this)->actor.yawTowardsPlayer, 8.0f); + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + this->colliderSph.base.at = NULL; + this->colliderLightning.base.at = NULL; + } + + this->colliderLightning.base.atFlags &= ~AT_HIT; + this->colliderSph.base.atFlags &= ~AT_HIT; + } + + if (this->colliderSph.base.acFlags & AC_HIT) { + this->colliderSph.base.acFlags &= ~AC_HIT; + if ((this->colliderSph.base.ac->id == ACTOR_EN_BOOM) && (sp52 >= 128)) { + boomerang = (EnBoom*)this->colliderSph.base.ac; + boomerang->returnTimer = 0; + boomerang->moveTo = &player->actor; + boomerang->actor.world.rot.y = boomerang->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SHIELD_REFLECT_SW); + } + } + + this->actor.world.pos.x = (Math_SinS(this->actor.world.rot.y) * this->unk_1A0) + sp54.x; + this->actor.world.pos.z = (Math_CosS(this->actor.world.rot.y) * this->unk_1A0) + sp54.z; + Math_SmoothStepToF(&this->actor.world.pos.y, 4.0f, 1.0f, 2.0f, 0.0f); + this->actor.world.pos.y += 2.0f * Math_SinF(this->unk_1A4); + this->actor.world.rot.x = Math_Vec3f_Pitch(&sp54, &this->actor.world.pos); + Math_SmoothStepToF(&this->unk_1A0, 160.0f, 1.0f, 2.0f, 0.0f); + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 0x5DC, 0); + if (!(this->timer2 & 0x200)) { + this->unk_1AC = 0xBB8; + } else { + this->unk_1AC = -0xBB8; + } + + if (sp52 >= 128) { + BossVa_Spark(globalCtx, this, 1, 75, 15.0f, 7.0f, SPARK_TETHER, 1.0f, true); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + sPhase3StopMoving = false; + } else { + sPhase3StopMoving = true; + } + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderLightning.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + if ((globalCtx->gameplayFrames % 4) == 0) { + Math_SmoothStepToS(&this->unk_1F0, 0x78, 1, 0xA, 0); + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + this->actor.world.rot.y += this->unk_1AC; + if (sBodyState & 0x7F) { + BossVa_SetupBariPhase3Stunned(this, globalCtx); + } +} + +void BossVa_SetupBariPhase2Attack(BossVa* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gBarinadeBariAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gBarinadeBariAnim), + ANIMMODE_LOOP, 0.0f); + this->timer2 = 0x40; + this->unk_1F0 = 0x78; + this->unk_1A0 = 60.0f; + this->unk_1A8 = 0.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_BariPhase2Attack); +} + +void BossVa_BariPhase2Attack(BossVa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnBoom* boomerang; + Vec3f sp54 = GET_BODY(this)->unk_1D8; + s16 sp52; + s16 sp50; + f32 sp4C; + s32 pad; + + this->unk_1A4 += Rand_ZeroOne() * 0.5f; + sp52 = this->timer2 & 0x1FF; + if ((globalCtx->gameplayFrames % 128) == 0) { + this->vaBariUnused.x = (s16)(Rand_ZeroOne() * 100.0f) + 100; + } + + sp50 = (sFightPhase * 70) - 280; + Math_SmoothStepToS(&this->vaBariUnused.z, this->vaBariUnused.x, 1, 0x1E, 0); + this->vaBariUnused.y += this->vaBariUnused.z; + if (sKillBari != 0) { + sKillBari--; + BossVa_KillBari(this, globalCtx); + return; + } + + if ((this->colliderLightning.base.atFlags & AT_HIT) || (this->colliderSph.base.atFlags & AT_HIT)) { + if ((this->colliderLightning.base.at == &player->actor) || (this->colliderSph.base.at == &player->actor)) { + func_8002F71C(globalCtx, &this->actor, 8.0f, GET_BODY(this)->actor.yawTowardsPlayer, 8.0f); + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + this->colliderSph.base.at = NULL; + this->colliderLightning.base.at = NULL; + } + + this->colliderLightning.base.atFlags &= ~AT_HIT; + this->colliderSph.base.atFlags &= ~AT_HIT; + } + + Math_SmoothStepToF(&this->actor.world.pos.y, 4.0f, 1.0f, 2.0f, 0.0f); + this->actor.world.rot.x = Math_Vec3f_Pitch(&sp54, &this->actor.world.pos); + if ((globalCtx->gameplayFrames % 8) == 0) { + Math_SmoothStepToS(&this->unk_1F0, 0x28, 1, 0xA, 0); + BossVa_Spark(globalCtx, this, 1, this->unk_1F0, 25.0f, 20.0f, 2, 2.0f, true); + } + + if (!(sPhase2Timer & 0x100) && (GET_BODY(this)->actor.colorFilterTimer == 0)) { + sp4C = 200.0f; + BossVa_Spark(globalCtx, this, 1, 125, 15.0f, 7.0f, SPARK_TETHER, 1.0f, true); + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->actor.params & 1) { + sp4C = -200.0f; + } + + Math_SmoothStepToF(&this->unk_1A0, (Math_SinS(sPhase2Timer * 0x190) * sp4C) + 320.0f, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToS(&this->unk_1AC, sp50 + 0x1F4, 1, 0x3C, 0); + this->actor.world.pos.y += 2.0f * Math_SinF(this->unk_1A4); + if (this->colliderSph.base.acFlags & AC_HIT) { + this->colliderSph.base.acFlags &= ~AC_HIT; + + if ((this->colliderSph.base.ac->id == ACTOR_EN_BOOM) && (sp52 >= 64)) { + boomerang = (EnBoom*)this->colliderSph.base.ac; + boomerang->returnTimer = 0; + boomerang->moveTo = &player->actor; + boomerang->actor.world.rot.y = boomerang->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SHIELD_REFLECT_SW); + } + } + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderLightning.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + } else { + this->actor.flags |= ACTOR_FLAG_0; + Math_SmoothStepToS(&this->unk_1AC, sp50 + 150, 1, 0x3C, 0); + if (GET_BODY(this)->actor.colorFilterTimer == 0) { + Math_SmoothStepToF(&this->unk_1A0, 180.0f, 1.0f, 1.5f, 0.0f); + } else { + this->unk_1AC = 0; + if (this->actor.colorFilterTimer == 0) { + Actor_SetColorFilter(&this->actor, 0, 255, 0x2000, GET_BODY(this)->actor.colorFilterTimer); + } + } + + this->actor.world.pos.y += Math_SinF(this->unk_1A4) * 4.0f; + if (this->colliderSph.base.acFlags & AC_HIT) { + BossVa_KillBari(this, globalCtx); + } + } + + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 0x5DC, 0); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + if ((globalCtx->gameplayFrames % 4) == 0) { + Math_SmoothStepToS(&this->unk_1F0, 0x78, 1, 0xA, 0); + } + + if (Rand_ZeroOne() < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_SPARK - SFX_FLAG); + } + + if (GET_BODY(this)->actor.colorFilterTimer == 0) { + if (!(this->timer2 & 0x400)) { + this->actor.world.rot.y += this->unk_1AC; + } else { + this->actor.world.rot.y -= this->unk_1AC; + } + + this->actor.world.pos.x = (Math_SinS(this->actor.world.rot.y) * this->unk_1A0) + sp54.x; + this->actor.world.pos.z = (Math_CosS(this->actor.world.rot.y) * this->unk_1A0) + sp54.z; + } +} + +void BossVa_SetupBariPhase3Stunned(BossVa* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_0; + this->timer = GET_BODY(this)->timer; + Actor_SetColorFilter(&this->actor, 0, 255, 0x2000, this->timer); + BossVa_SetupAction(this, BossVa_BariPhase3Stunned); +} + +void BossVa_BariPhase3Stunned(BossVa* this, GlobalContext* globalCtx) { + s32 sp44_pad; + Vec3f sp40 = GET_BODY(this)->unk_1D8; + + this->actor.world.rot.x = Math_Vec3f_Pitch(&GET_BODY(this)->actor.world.pos, &this->actor.world.pos); + if (this->colliderSph.base.acFlags & AC_HIT) { + BossVa_KillBari(this, globalCtx); + return; + } + + this->unk_1A4 += Rand_ZeroOne() * 0.5f; + Math_SmoothStepToF(&this->actor.world.pos.y, 4.0f, 1.0f, 2.0f, 0.0f); + this->actor.world.pos.y += Math_SinF(this->unk_1A4) * 3.0f; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + if ((globalCtx->gameplayFrames % 4) == 0) { + Math_SmoothStepToS(&this->unk_1F0, 0x28, 1, 0xA, 0); + BossVa_Spark(globalCtx, this, 1, this->unk_1F0, 25.0f, 20.0f, SPARK_BARI, 2.0f, true); + } + + this->timer--; + this->actor.world.rot.x = Math_Vec3f_Pitch(&sp40, &this->actor.world.pos); + if (this->timer <= 0) { + if (this->timer == 0) { + this->timer2 = 0; + } else { + BossVa_Spark(globalCtx, this, 1, 85, 15.0f, 0.0f, SPARK_TETHER, 1.0f, true); + if (this->timer2 >= 0x10) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->timer2 = 0x80; + BossVa_SetupAction(this, BossVa_BariPhase3Attack); + } + } + } +} + +void BossVa_SetupBariDeath(BossVa* this) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->timer = 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_BL_DEAD); + this->isDead++; + BossVa_SetupAction(this, BossVa_BariDeath); +} + +void BossVa_BariDeath(BossVa* this, GlobalContext* globalCtx) { + this->timer--; + if (this->timer == 0) { + Actor_Kill(&this->actor); + } +} + +void BossVa_SetupDoor(BossVa* this, GlobalContext* globalCtx) { + if (sCsState >= INTRO_SPAWN_BARI) { + sDoorState = 100; + } + this->actor.flags &= ~ACTOR_FLAG_0; + BossVa_SetupAction(this, BossVa_Door); +} + +void BossVa_Door(BossVa* this, GlobalContext* globalCtx) { + if (sDoorState == 29) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BUYODOOR_CLOSE); + } + + if (sCsState <= INTRO_DOOR_SHUT) { + if (sDoorState < 100) { + sDoorState += 8; + } else { + sDoorState = 100; + } + } +} + +void BossVa_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + BossVa* this = (BossVa*)thisx; + EnBoom* boomerang; + s32 i; + + this->actionFunc(this, globalCtx); + + switch (this->actor.params) { + case BOSSVA_BODY: + if (this->colliderBody.base.acFlags & AC_HIT) { + this->colliderBody.base.acFlags &= ~AC_HIT; + if (this->colliderBody.base.ac->id == ACTOR_EN_BOOM) { + boomerang = (EnBoom*)this->colliderBody.base.ac; + boomerang->returnTimer = 0; + } + } + + BossVa_UpdateEffects(globalCtx); + + for (i = 2; i >= 0; i--) { + if ((globalCtx->envCtx.adjAmbientColor[i] - 1) > 0) { + globalCtx->envCtx.adjAmbientColor[i] -= 1; + } else { + globalCtx->envCtx.adjAmbientColor[i] = 0; + } + + if ((globalCtx->envCtx.adjLight1Color[i] - 10) > 0) { + globalCtx->envCtx.adjLight1Color[i] -= 10; + } else { + globalCtx->envCtx.adjLight1Color[i] = 0; + } + + if ((globalCtx->envCtx.adjFogColor[i] - 10) > 0) { + globalCtx->envCtx.adjFogColor[i] -= 10; + } else { + globalCtx->envCtx.adjFogColor[i] = 0; + } + } + + if (this->onCeiling > 0) { + this->onCeiling--; // not used by body + } + break; + + default: + this->timer2++; + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 45.0f; + this->unk_1D8.y = (Math_CosS(this->timer2 * 0xFA4) * 0.24f) + 0.76f; + this->unk_1D8.x = (Math_SinS(this->timer2 * 0xFA4) * 0.2f) + 1.0f; + break; + + case BOSSVA_SUPPORT_1: + case BOSSVA_SUPPORT_2: + case BOSSVA_SUPPORT_3: + case BOSSVA_ZAPPER_1: + case BOSSVA_ZAPPER_2: + case BOSSVA_ZAPPER_3: + case BOSSVA_DOOR: + break; + } +} + +s32 BossVa_BodyOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossVa* this = (BossVa*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4156); + + if (limbIndex == 20) { + gDPPipeSync(POLY_OPA_DISP++); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 8, 16, 1, 0, + (globalCtx->gameplayFrames * -2) % 64, 16, 16)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->bodyGlow); + Matrix_RotateX(-M_PI / 2, MTXMODE_APPLY); + } else if ((limbIndex >= 10) && (limbIndex < 20)) { + rot->x -= 0x4000; + *dList = NULL; + } else if (limbIndex == 6) { + Matrix_Scale(this->unk_1A4, this->unk_1A4, this->unk_1A4, MTXMODE_APPLY); + } else if (limbIndex == 61) { + Matrix_Scale(this->unk_1A0, this->unk_1A0, this->unk_1A0, MTXMODE_APPLY); + } else if (limbIndex == 7) { + rot->x -= 0xCCC; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4183); + return false; +} + +void BossVa_BodyPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + BossVa* this = (BossVa*)thisx; + Vec3f sp78 = { 0.0f, 0.0f, 0.0f }; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4192); + + if (limbIndex == 6) { + if (sFightPhase < PHASE_3) { + sp78.x = -1000.0f; + } else { + sp78.x = 200.0f; + } + Matrix_MultVec3f(&sp78, &this->unk_1D8); + } else if ((limbIndex >= 10) && (limbIndex < 20) && (sBodyBari[limbIndex - 10] != 0)) { + if (((limbIndex >= 16) || (limbIndex == 10)) && (sFightPhase <= PHASE_3)) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 4208), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008BB8); + } else if ((limbIndex >= 11) && (sFightPhase <= PHASE_2)) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 4212), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008BB8); + } + + if (sCsState >= DEATH_START) { + sp78.x = Rand_CenteredFloat(530.0f); + sp78.y = Rand_CenteredFloat(530.0f); + sp78.z = -60.0f; + } + Matrix_MultVec3f(&sp78, &this->effectPos[limbIndex - 10]); + } else if (limbIndex == 25) { + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (globalCtx->gameplayFrames * 10) % 128, 16, 32, 1, 0, + (globalCtx->gameplayFrames * 5) % 128, 16, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 4232), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008D70); + } else if ((*dList != NULL) && (limbIndex >= 29) && (limbIndex < 56)) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 4236), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, *dList); + } else if ((limbIndex == 24) && (sCsState < DEATH_START)) { + sp78.x = (this->actor.shape.yOffset + 450.0f) + -140.0f; + Matrix_MultVec3f(&sp78, &this->unk_280); + sp78.x = 200.0f; + Matrix_MultVec3f(&sp78, &this->unk_274); + } + + if ((limbIndex == 7) && (sCsState >= DEATH_START)) { + sp78.x = Rand_CenteredFloat(320.0f) + -250.0f; + sp78.y = Rand_CenteredFloat(320.0f); + sp78.z = Rand_CenteredFloat(320.0f); + + if (sp78.y < 0.0f) { + sp78.y -= 150.0f; + } else { + sp78.y += 150.0f; + } + + if (sp78.z < 0.0f) { + sp78.z -= 150.0f; + } else { + sp78.z += 150.0f; + } + Matrix_MultVec3f(&sp78, &this->unk_274); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4264); +} + +s32 BossVa_SupportOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossVa* this = (BossVa*)thisx; + + if (!this->onCeiling && (limbIndex == 4)) { + rot->z += this->headRot.x; + } + return false; +} + +void BossVa_SupportPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + BossVa* this = (BossVa*)thisx; + Vec3f sp20 = { 0.0f, 0.0f, 0.0f }; + s32 pad; + + if (this->onCeiling) { + switch (limbIndex) { + case 4: + Matrix_MultVec3f(&sZeroVec, &this->actor.focus.pos); + Collider_UpdateSpheres(0, &this->colliderSph); + break; + case 7: + Matrix_MultVec3f(&sZeroVec, &this->armTip); + sp20.x = ((this->timer & 0x1F) >> 1) * -40.0f; + sp20.y = ((this->timer & 0x1F) >> 1) * -7.0f; + Matrix_MultVec3f(&sp20, &this->effectPos[0]); + break; + case 9: + sp20.x = ((this->timer & 0x1F) >> 1) * -60.0f; + sp20.y = ((this->timer & 0x1F) >> 1) * -45.0f; + Matrix_MultVec3f(&sp20, &this->effectPos[1]); + break; + } + } else { + switch (limbIndex) { + case 5: + Matrix_MultVec3f(&sZeroVec, &this->armTip); + break; + case 8: + sp20.x = (this->timer2 & 7) * 90.0f; + Matrix_MultVec3f(&sp20, &this->effectPos[2]); + break; + case 9: + sp20.x = (this->timer2 & 7) * 50.0f; + Matrix_MultVec3f(&sp20, &this->effectPos[1]); + break; + case 10: + sp20.x = (this->timer2 & 7) * 46.0f; + Matrix_MultVec3f(&sp20, &this->effectPos[0]); + break; + } + } +} + +s32 BossVa_ZapperOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossVa* this = (BossVa*)thisx; + MtxF zapperMtx; + + switch (limbIndex) { + case 4: + rot->y += this->unk_1E6; + rot->z += this->unk_1E4; + break; + case 5: + rot->y += this->unk_1EC; + rot->z += this->unk_1EA; + break; + case 7: + Matrix_Translate(pos->x, pos->y, pos->z, MTXMODE_APPLY); + Matrix_Get(&zapperMtx); + Matrix_MtxFToZYXRotS(&zapperMtx, &sZapperRot, false); + Matrix_RotateX(-sZapperRot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateY(-sZapperRot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(-sZapperRot.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateY(this->unk_1F2 * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(this->unk_1F0 * (M_PI / 0x8000), MTXMODE_APPLY); + pos->x = pos->y = pos->z = 0.0f; + rot->x = rot->y = rot->z = 0; + break; + } + return false; +} + +void BossVa_ZapperPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + BossVa* this = (BossVa*)thisx; + Vec3f sp70 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp64 = { 15.0f, 0.0f, 0.0f }; + Vec3f sp58 = { -15.0f, 0.0f, 0.0f }; + Vec3f sp4C = { 15.0f, 0.0f, 0.0f }; + Vec3f sp40 = { -15.0f, 0.0f, 0.0f }; + s16 sp3E; + s16 sp3C; + + switch (limbIndex) { + case 3: + sp70.x = (this->timer2 & 7) * 30.0f; + Matrix_MultVec3f(&sp70, &this->effectPos[0]); + break; + + case 4: + Matrix_MultVec3f(&sZeroVec, &this->armTip); + sp70.x = (this->timer2 & 7) * 30.0f; + Matrix_MultVec3f(&sp70, &this->effectPos[1]); + break; + + case 5: + Matrix_MultVec3f(&sZeroVec, &this->zapNeckPos); + sp70.x = (this->timer2 & 7) * 46.0f; + Matrix_MultVec3f(&sp70, &this->effectPos[2]); + break; + + case 7: + Matrix_MultVec3f(&sZeroVec, &this->zapHeadPos); + sp70.x = (this->timer2 & 7) * 46.0f; + Matrix_MultVec3f(&sp70, &this->effectPos[3]); + sp70.x = 20.0f; + Matrix_MultVec3f(&sp70, &this->effectPos[9]); + func_80035844(&this->effectPos[9], &this->unk_1D8, &this->headRot, false); + sp3E = this->headRot.x; + sp3C = this->headRot.y; + Matrix_Push(); + Matrix_Translate(this->effectPos[9].x, this->effectPos[9].y, this->effectPos[9].z, MTXMODE_NEW); + Matrix_RotateZYX(sp3E, sp3C, 0, MTXMODE_APPLY); + sp70.x = 0.0f; + if (sFightPhase >= PHASE_4) { + sp70.z = ((this->timer2 - 16) & 7) * 120.0f; + } else { + sp70.z = ((this->timer2 - 32) & 0xF) * 80.0f; + } + sp4C.z = sp40.z = sp70.z += 40.0f; + sp70.z += 50.0f; + Matrix_MultVec3f(&sp70, &this->effectPos[4]); + if (sFightPhase >= PHASE_4) { + sp70.z -= 33.0f; + if (sp70.z < 0.0f) { + sp70.z = 0.0f; + } + Matrix_MultVec3f(&sp70, &this->effectPos[6]); + sp70.z -= 33.0f; + if (sp70.z < 0.0f) { + sp70.z = 0.0f; + } + } else { + sp70.z -= 22.0f; + if (sp70.z < 0.0f) { + sp70.z = 0.0f; + } + Matrix_MultVec3f(&sp70, &this->effectPos[6]); + sp70.z -= 22.0f; + if (sp70.z < 0.0f) { + sp70.z = 0.0f; + } + } + Matrix_MultVec3f(&sp70, &this->effectPos[5]); + Matrix_MultVec3f(&sp64, &this->colliderLightning.dim.quad[1]); + Matrix_MultVec3f(&sp58, &this->colliderLightning.dim.quad[0]); + Matrix_MultVec3f(&sp4C, &this->colliderLightning.dim.quad[3]); + Matrix_MultVec3f(&sp40, &this->colliderLightning.dim.quad[2]); + Collider_SetQuadVertices(&this->colliderLightning, &this->colliderLightning.dim.quad[0], + &this->colliderLightning.dim.quad[1], &this->colliderLightning.dim.quad[2], + &this->colliderLightning.dim.quad[3]); + Matrix_Pop(); + break; + } +} + +s32 BossVa_BariOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + BossVa* this = (BossVa*)thisx; + + switch (limbIndex) { + case 2: + *dList = NULL; + break; + case 3: + Matrix_Scale(this->unk_1D8.x, 1.0f, this->unk_1D8.x, MTXMODE_APPLY); + break; + case 4: + Matrix_Scale(1.0f, this->unk_1D8.y, 1.0f, MTXMODE_APPLY); + break; + } + return false; +} + +void BossVa_BariPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + BossVa* this = (BossVa*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4494); + + if (limbIndex == 2) { + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (globalCtx->gameplayFrames * 10) % 32, 16, 32, 1, 0, + (globalCtx->gameplayFrames * -5) % 32, 16, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 4508), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_000FA0); + } else if ((limbIndex == 3) || (limbIndex == 4)) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 4512), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, *dList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4517); +} + +void BossVa_Draw(Actor* thisx, GlobalContext* globalCtx) { + s16* paramsPtr; // This stack slot is almost certainly actually globalCtx2, but can't make it match + BossVa* this = (BossVa*)thisx; + Vec3f spBC; + Vec3f spB0 = { 0.0f, 45.0f, 0.0f }; + Vec3f spA4 = { 0.4f, 0.4f, 0.4f }; + Vec3f sp98 = { 15.0f, 40.0f, 0.0f }; + Vec3f sp8C = { -15.0f, 40.0f, 0.0f }; + Vec3f sp80 = { 15.0f, 40.0f, 0.0f }; + Vec3f sp74 = { -15.0f, 40.0f, 0.0f }; + Color_RGBA8 unused = { 250, 250, 230, 200 }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4542); + + func_80093D18(globalCtx->state.gfxCtx); + paramsPtr = &this->actor.params; + func_80093D84(globalCtx->state.gfxCtx); + + switch (this->actor.params) { + case BOSSVA_BODY: + if (globalCtx->envCtx.adjFogNear != 0) { + globalCtx->envCtx.adjFogNear += 0x15E; + if (globalCtx->envCtx.adjFogNear > 0) { + globalCtx->envCtx.adjFogNear = 0; + } + } + + if (globalCtx->envCtx.adjFogFar != 0) { + globalCtx->envCtx.adjFogFar += 0x15E; + if (globalCtx->envCtx.adjFogFar > 0) { + globalCtx->envCtx.adjFogFar = 0; + } + } + + if (!this->isDead) { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 8, 16, 1, 0, + (globalCtx->gameplayFrames * -10) % 16, 16, 16)); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (globalCtx->gameplayFrames * -10) % 32, 16, + 0x20, 1, 0, (globalCtx->gameplayFrames * -5) % 32, 16, 32)); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + BossVa_BodyOverrideLimbDraw, BossVa_BodyPostLimbDraw, this); + } + break; + case BOSSVA_SUPPORT_1: + case BOSSVA_SUPPORT_2: + case BOSSVA_SUPPORT_3: + if (!this->isDead) { + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossVa_SupportOverrideLimbDraw, + BossVa_SupportPostLimbDraw, this); + } + break; + case BOSSVA_ZAPPER_1: + case BOSSVA_ZAPPER_2: + case BOSSVA_ZAPPER_3: + if (!this->isDead) { + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, BossVa_ZapperOverrideLimbDraw, + BossVa_ZapperPostLimbDraw, this); + } + break; + case BOSSVA_STUMP_1: + case BOSSVA_STUMP_2: + case BOSSVA_STUMP_3: + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, NULL, NULL, NULL); + break; + default: + if (!this->isDead) { + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + BossVa_BariOverrideLimbDraw, BossVa_BariPostLimbDraw, this); + Collider_UpdateSpheres(0, &this->colliderSph); + if (sCsState < BOSSVA_BATTLE) { + spBC = GET_BODY(this)->actor.world.pos; + } else { + spBC = GET_BODY(this)->unk_1D8; + } + Matrix_MultVec3f(&sZeroVec, &this->effectPos[1]); + Matrix_Push(); + Matrix_Translate(spBC.x, spBC.y, spBC.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, 0, MTXMODE_APPLY); + sp80.z = sp74.z = this->unk_1A0; + spB0.z = (this->timer2 & 0xF) * (this->unk_1A0 * 0.0625f); + Matrix_MultVec3f(&spB0, &this->effectPos[0]); + Matrix_MultVec3f(&sp98, &this->colliderLightning.dim.quad[1]); + Matrix_MultVec3f(&sp8C, &this->colliderLightning.dim.quad[0]); + Matrix_MultVec3f(&sp80, &this->colliderLightning.dim.quad[3]); + Matrix_MultVec3f(&sp74, &this->colliderLightning.dim.quad[2]); + Collider_SetQuadVertices(&this->colliderLightning, &this->colliderLightning.dim.quad[0], + &this->colliderLightning.dim.quad[1], &this->colliderLightning.dim.quad[2], + &this->colliderLightning.dim.quad[3]); + Matrix_Pop(); + spBC = this->actor.world.pos; + spBC.y += 9.0f; + if (this->actor.colorFilterTimer != 0) { + func_80026A6C(globalCtx); + } + func_80033C30(&spBC, &spA4, 0xFF, globalCtx); + if (this->actor.colorFilterTimer != 0) { + Color_RGBA8 blue = { 0, 0, 255, 255 }; + + func_80026860(globalCtx, &blue, this->actor.colorFilterTimer, this->actor.colorFilterParams & 0xFF); + } + } + break; + case BOSSVA_DOOR: + break; + } + + if (*paramsPtr == BOSSVA_BODY) { + BossVa_DrawEffects(sVaEffects, globalCtx); + } else if (*paramsPtr == BOSSVA_DOOR) { + BossVa_DrawDoor(globalCtx, sDoorState); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 4673); +} + +static s32 sUnkValue = 0x009B0000; // Unreferenced? Possibly a color + +void BossVa_UpdateEffects(GlobalContext* globalCtx) { + BossVaEffect* effect = sVaEffects; + Player* player = GET_PLAYER(globalCtx); + s16 spB6; + s16 i; + f32 floorY; + s32 padAC; + s16 pitch; + s16 yaw; + BossVa* refActor2; + BossVa* refActor; + Vec3f sp94; + CollisionPoly* sp90; + f32 pad8C; + Vec3f sp80; + CollisionPoly* sp7C; + f32 pad78; + f32 pad74; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type != VA_NONE) { + effect->timer--; + + effect->pos.x += effect->velocity.x; + effect->pos.y += effect->velocity.y; + effect->pos.z += effect->velocity.z; + + effect->velocity.x += effect->accel.x; + effect->velocity.y += effect->accel.y; + effect->velocity.z += effect->accel.z; + + if ((effect->type == VA_LARGE_SPARK) || (effect->type == VA_SMALL_SPARK)) { + refActor = effect->parent; + + effect->rot.z += (s16)(Rand_ZeroOne() * 0x4E20) + 0x2000; + effect->rot.y += (s16)(Rand_ZeroOne() * 0x2710) + 0x2000; + + if ((effect->mode == SPARK_TETHER) || (effect->mode == SPARK_UNUSED)) { + pitch = effect->rot.x - Math_Vec3f_Pitch(&refActor->actor.world.pos, &GET_BODY(refActor)->unk_1D8); + pad8C = Math_SinS(refActor->actor.world.rot.y); + effect->pos.x = refActor->actor.world.pos.x - (effect->offset.x * pad8C); + pad74 = Math_CosS(refActor->actor.world.rot.y); + effect->pos.z = refActor->actor.world.pos.z - (effect->offset.x * pad74); + pad78 = Math_CosS(-pitch); + effect->pos.y = (effect->offset.y * pad78) + refActor->actor.world.pos.y; + } else if ((effect->mode == SPARK_BARI) || (effect->mode == SPARK_BODY)) { + effect->pos.x = effect->offset.x + refActor->actor.world.pos.x; + effect->pos.y = effect->offset.y + refActor->actor.world.pos.y; + effect->pos.z = effect->offset.z + refActor->actor.world.pos.z; + } else { + spB6 = Rand_ZeroFloat(ARRAY_COUNT(player->bodyPartsPos) - 0.1f); + effect->pos.x = player->bodyPartsPos[spB6].x + Rand_CenteredFloat(10.0f); + effect->pos.y = player->bodyPartsPos[spB6].y + Rand_CenteredFloat(15.0f); + effect->pos.z = player->bodyPartsPos[spB6].z + Rand_CenteredFloat(10.0f); + } + + if (effect->timer < 100) { + effect->primColor[3] -= 50; + if (effect->primColor[3] < 0) { + effect->primColor[3] = 0; + effect->timer = 0; + effect->type = VA_NONE; + } + } + } + + if (effect->type == VA_BLAST_SPARK) { + effect->rot.z += (s16)(Rand_ZeroOne() * 0x4E20) + 0x4000; + if (effect->timer < 100) { + effect->primColor[3] -= 50; + if (effect->primColor[3] < 0) { + effect->primColor[3] = 0; + effect->timer = 0; + effect->type = VA_NONE; + } + } + } + + if (effect->type == VA_SPARK_BALL) { + refActor2 = effect->parent; + + effect->rot.z += (s16)(Rand_ZeroOne() * 0x2710) + 0x24A8; + effect->pos.x = effect->offset.x + refActor2->actor.world.pos.x; + effect->pos.y = + refActor2->actor.world.pos.y + 310.0f + (refActor2->actor.shape.yOffset * refActor2->actor.scale.y); + effect->pos.z = effect->offset.z + refActor2->actor.world.pos.z; + effect->mode = (effect->mode + 1) & 7; + + if (effect->timer < 100) { + effect->primColor[3] -= 50; + if (effect->primColor[3] < 0) { + effect->primColor[3] = 0; + effect->timer = 0; + effect->type = VA_NONE; + } + } + } + + if (effect->type == VA_ZAP_CHARGE) { + effect->mode = (effect->mode + 1) & 7; + effect->primColor[3] -= 20; + if (effect->primColor[3] <= 0) { + effect->primColor[3] = 0; + effect->timer = 0; + effect->type = VA_NONE; + } + } + + if (effect->type == VA_BLOOD) { + if (effect->mode < BLOOD_SPOT) { + sp94 = effect->pos; + sp94.y -= effect->velocity.y + 4.0f; + floorY = BgCheck_EntityRaycastFloor1(&globalCtx->colCtx, &sp90, &sp94); + if ((sp90 != NULL) && (effect->pos.y <= floorY)) { + effect->mode = BLOOD_SPOT; + effect->pos.y = floorY + 1.0f; + if (sCsState <= DEATH_SHELL_BURST) { + effect->timer = 80; + } else { + effect->timer = 60000; + } + + effect->accel = effect->velocity = sZeroVec; + } + if (!effect->timer) { + effect->type = VA_NONE; + } + } else { + if (effect->timer < 20) { + effect->envColor[3] = effect->timer * 5; + effect->primColor[3] = effect->timer * 10; + } else if (effect->timer > 50000) { + effect->timer++; + } + } + + if (!effect->timer) { + effect->type = VA_NONE; + } + } + + if (effect->type == VA_GORE) { + if (effect->mode == GORE_PERMANENT) { + sp80 = effect->pos; + sp80.y -= effect->velocity.y + 4.0f; + effect->rot.x += 0x1770; + floorY = BgCheck_EntityRaycastFloor1(&globalCtx->colCtx, &sp7C, &sp80); + if ((sp7C != NULL) && (effect->pos.y <= floorY)) { + effect->mode = GORE_FLOOR; + effect->timer = 30; + effect->pos.y = floorY + 1.0f; + effect->accel = effect->velocity = sZeroVec; + effect->rot.x = -0x4000; + } + + if (!effect->timer) { + effect->type = VA_NONE; + } + + } else if (effect->mode == GORE_FADING) { + if (effect->timer == 0) { + effect->type = VA_NONE; + if (1) {} + } + + } else { + Math_SmoothStepToF(&effect->scaleMod, 0.075f, 1.0f, 0.005f, 0.0f); + Math_SmoothStepToF(&effect->vaGorePulseRate, 0.0f, 0.6f, 0.005f, 0.0013f); + if ((globalCtx->gameplayFrames % 4) == 0) { + Math_SmoothStepToS(&effect->primColor[0], 95, 1, 1, 0); + } + } + effect->vaGorePulse += effect->vaGorePulseRate; + } + + if (effect->type == VA_TUMOR) { + refActor = effect->parent; + + effect->rot.z += 0x157C; + effect->envColor[3] = (s16)(Math_SinS(effect->rot.z) * 50.0f) + 80; + Math_SmoothStepToF(&effect->scale, effect->scaleMod, 1.0f, 0.01f, 0.005f); + effect->pos.x = effect->offset.x + refActor->actor.world.pos.x; + effect->pos.y = effect->offset.y + refActor->actor.world.pos.y; + effect->pos.z = effect->offset.z + refActor->actor.world.pos.z; + + switch (effect->mode) { + case TUMOR_UNUSED: + if (effect->timer == 0) { + yaw = Math_Vec3f_Yaw(&refActor->actor.world.pos, &effect->pos); + effect->type = VA_NONE; + BossVa_BloodSplatter(globalCtx, effect, yaw, effect->scale * 4500.0f, 1); + BossVa_Gore(globalCtx, effect, yaw, effect->scale * 1.2f); + } + break; + case TUMOR_BODY: + case TUMOR_ARM: + if (refActor->burst) { + effect->type = VA_NONE; + yaw = Math_Vec3f_Yaw(&refActor->actor.world.pos, &effect->pos); + BossVa_BloodSplatter(globalCtx, effect, yaw, effect->scale * 4500.0f, 1); + BossVa_Gore(globalCtx, effect, yaw, effect->scale * 1.2f); + } + break; + } + if (1) {} + } + } + } +} + +void BossVa_DrawEffects(BossVaEffect* effect, GlobalContext* globalCtx) { + static void* sSparkBallTex[] = { + gBarinadeSparkBall1Tex, gBarinadeSparkBall2Tex, gBarinadeSparkBall3Tex, gBarinadeSparkBall4Tex, + gBarinadeSparkBall5Tex, gBarinadeSparkBall6Tex, gBarinadeSparkBall7Tex, gBarinadeSparkBall8Tex, + }; + s16 i; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u8 flag = 0; + BossVaEffect* effectHead = effect; + Camera* camera = Gameplay_GetCamera(globalCtx, sCsCamera); + + OPEN_DISPS(gfxCtx, "../z_boss_va.c", 4953); + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_LARGE_SPARK) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 130, 130, 30, 0); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_0156A0); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 230, 230, 230, effect->primColor[3]); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ((effect->rot.z / (f32)0x8000) * 3.1416f, MTXMODE_APPLY); + Matrix_Scale(effect->scale * 0.0185f, effect->scale * 0.0185f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 4976), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_015710); + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_SPARK_BALL) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_011738); + flag++; + } + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_RotateZ((effect->rot.z / (f32)0x8000) * 3.1416f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5002), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sSparkBallTex[effect->mode])); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, effect->primColor[0], effect->primColor[1], effect->primColor[2], + effect->primColor[3]); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, effect->envColor[0], effect->envColor[1], effect->envColor[2], + effect->envColor[3]); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_011768); + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_BLOOD) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_009430); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gEffBubble1Tex)); + flag++; + } + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 0, effect->envColor[3]); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 150, 0, effect->primColor[3]); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + if (effect->mode == BLOOD_SPOT) { + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + } else { + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + } + + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + + gDPPipeSync(POLY_XLU_DISP++); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5052), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_009468); + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_TUMOR) { + BossVa* parent = effect->parent; + + if (!flag) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, effect->envColor[3]); + gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_0128B8); + flag++; + } + + if ((effect->mode != TUMOR_BODY) || ((Math_Vec3f_DistXZ(&camera->eye, &effect->pos) - + Math_Vec3f_DistXZ(&camera->eye, &parent->actor.world.pos)) < 10.0f)) { + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5080), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012948); + } + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_GORE) { + if (!flag) { + func_80093D18(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012BA0); + flag++; + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, effect->primColor[3]); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, effect->primColor[0], effect->primColor[1], effect->primColor[2], + effect->primColor[3]); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateZYX(effect->rot.x, effect->rot.y, 0, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_RotateX(effect->offset.x * 0.115f, MTXMODE_APPLY); + Matrix_RotateY(effect->offset.x * 0.13f, MTXMODE_APPLY); + Matrix_RotateZ(effect->offset.x * 0.1f, MTXMODE_APPLY); + Matrix_Scale(1.0f - effect->scaleMod, effect->scaleMod + 1.0f, 1.0f - effect->scaleMod, MTXMODE_APPLY); + Matrix_RotateZ(-(effect->offset.x * 0.1f), MTXMODE_APPLY); + Matrix_RotateY(-(effect->offset.x * 0.13f), MTXMODE_APPLY); + Matrix_RotateX(-(effect->offset.x * 0.115f), MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5124), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBarinadeDL_012C50); + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_ZAP_CHARGE) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_0135B0); + flag++; + } + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 50, effect->primColor[3]); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, effect->primColor[3]); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateZYX(effect->rot.x, effect->rot.y, 0, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5152), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_013638); + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_BLAST_SPARK) { + if (!flag) { + func_80093C14(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 130, 130, 30, 0); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_0156A0); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 230, 230, 230, effect->primColor[3]); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ((effect->rot.z / (f32)0x8000) * 3.1416f, MTXMODE_APPLY); + Matrix_Scale(effect->scale * 0.02f, effect->scale * 0.02f, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5180), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_015710); + } + } + + effect = effectHead; + for (i = 0, flag = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_SMALL_SPARK) { + if (!flag) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 100, 0); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008F08); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, effect->primColor[3]); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateZ((effect->rot.z / (f32)0x8000) * 3.1416f, MTXMODE_APPLY); + Matrix_RotateY((effect->rot.y / (f32)0x8000) * 3.1416f, MTXMODE_APPLY); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_boss_va.c", 5208), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBarinadeDL_008F70); + } + } + + CLOSE_DISPS(gfxCtx, "../z_boss_va.c", 5215); +} + +void BossVa_SpawnSpark(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* offset, s16 scale, + u8 mode) { + Player* player = GET_PLAYER(globalCtx); + s16 index; + Vec3f pos = { 0.0f, -1000.0f, 0.0f }; + Vec3f tempVec; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_LARGE_SPARK; + effect->parent = this; + effect->pos = pos; + effect->timer = (s16)(Rand_ZeroOne() * 10.0f) + 111; + effect->velocity = effect->accel = sZeroVec; + effect->mode = mode; + + switch (mode) { + case SPARK_UNUSED: + effect->type = VA_SMALL_SPARK; + case SPARK_TETHER: + tempVec = *offset; + tempVec.x += this->actor.world.pos.x; + tempVec.z += this->actor.world.pos.z; + effect->offset.x = Math_Vec3f_DistXZ(&this->actor.world.pos, &tempVec); + effect->rot.x = Math_Vec3f_Pitch(&this->actor.world.pos, &GET_BODY(this)->unk_1D8); + break; + + case SPARK_BODY: + effect->type = VA_SMALL_SPARK; + case SPARK_BARI: + effect->offset.x = offset->x; + effect->offset.z = offset->z; + break; + + case SPARK_BLAST: + effect->type = VA_BLAST_SPARK; + effect->pos.x = offset->x + this->actor.world.pos.x; + effect->pos.y = offset->y + this->actor.world.pos.y; + effect->pos.z = offset->z + this->actor.world.pos.z; + effect->timer = 111; + break; + + case SPARK_LINK: + effect->type = VA_SMALL_SPARK; + index = Rand_ZeroFloat(ARRAY_COUNT(player->bodyPartsPos) - 0.1f); + effect->pos.x = player->bodyPartsPos[index].x + Rand_CenteredFloat(10.0f); + effect->pos.y = player->bodyPartsPos[index].y + Rand_CenteredFloat(15.0f); + effect->pos.z = player->bodyPartsPos[index].z + Rand_CenteredFloat(10.0f); + break; + } + + effect->offset.y = offset->y; + effect->scale = (Rand_ZeroFloat(scale) + scale) * 0.01f; + effect->primColor[3] = 255; + break; + } + } +} + +void BossVa_SpawnSparkBall(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* offset, s16 scale, + u8 mode) { + Vec3f pos = { 0.0f, -1000.0f, 0.0f }; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_SPARK_BALL; + effect->parent = this; + + effect->pos = pos; + + effect->velocity = effect->accel = sZeroVec; + + effect->mode = 0; + effect->offset.x = offset->x; + effect->offset.z = offset->z; + effect->offset.y = offset->y; + effect->timer = (s16)(Rand_ZeroOne() * 10.0f) + 111; + effect->primColor[0] = effect->primColor[1] = effect->primColor[2] = effect->primColor[3] = 230; + effect->envColor[0] = 0; + effect->envColor[1] = 100; + effect->envColor[2] = 220; + effect->envColor[3] = 160; + + effect->scale = (Rand_ZeroFloat(scale) + scale) * 0.01f; + return; + } + } +} + +void BossVa_SpawnBloodDroplets(GlobalContext* globalCtx, BossVaEffect* effect, Vec3f* pos, s16 scale, s16 phase, + s16 yaw) { + s32 i; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + f32 xzVel; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_BLOOD; + effect->pos = *pos; + effect->mode = BLOOD_DROPLET; + + xzVel = Math_SinS(phase) * 6.0f; + velocity.x = Rand_CenteredFloat(1.0f) + (-Math_SinS(yaw) * xzVel); + velocity.z = Rand_CenteredFloat(1.0f) + (-Math_CosS(yaw) * xzVel); + + effect->velocity = velocity; + + accel.y = Rand_CenteredFloat(0.3f) - 1.0f; + effect->accel = accel; + + effect->timer = 20; + effect->envColor[3] = 100; + effect->primColor[3] = 200; + effect->scale = (Rand_ZeroFloat(scale) + scale) * 0.01f; + break; + } + } +} + +void BossVa_SpawnBloodSplatter(GlobalContext* globalCtx, BossVaEffect* effect, Vec3f* pos, s16 yaw, s16 scale) { + s32 i; + f32 xzVel; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_BLOOD; + effect->pos = *pos; + + effect->mode = BLOOD_SPLATTER; + + xzVel = Rand_ZeroOne() * 7.0f; + velocity.x = Math_SinS(yaw) * xzVel; + velocity.y = Rand_CenteredFloat(4.0f) + 4.0f; + velocity.z = Math_CosS(yaw) * xzVel; + effect->velocity = velocity; + + accel.y = Rand_CenteredFloat(0.3f) - 1.0f; + effect->accel = accel; + + if (sCsState <= DEATH_SHELL_BURST) { + effect->timer = 20; + } else { + effect->timer = 60; + } + effect->envColor[3] = 100; + effect->primColor[3] = 200; + effect->scale = scale * 0.01f; + break; + } + } +} + +void BossVa_SpawnTumor(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* offset, s16 scale, + u8 mode) { + Vec3f pos = { 0.0f, -1000.0f, 0.0f }; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_TUMOR; + effect->parent = this; + effect->pos = pos; + + effect->velocity = effect->accel = sZeroVec; + + effect->mode = mode; + effect->rot.z = 0; + + effect->offset.x = offset->x; + effect->offset.z = offset->z; + effect->offset.y = offset->y; + + effect->timer = (s16)(Rand_ZeroOne() * 10.0f) + 10; + effect->envColor[3] = 100; + effect->scaleMod = scale * 0.01f; + effect->scale = 0.0f; + + if (((i % 4) == 0) || (mode == 2)) { + Audio_PlaySoundGeneral(NA_SE_EN_BALINADE_BREAK, &effect->pos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + break; + } + } +} + +void BossVa_SpawnGore(GlobalContext* globalCtx, BossVaEffect* effect, Vec3f* pos, s16 yaw, s16 scale) { + s32 i; + f32 xzVel; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_GORE; + effect->pos = *pos; + effect->scaleMod = 0.0f; + + xzVel = (Rand_ZeroOne() * 4.0f) + 4.0f; + velocity.x = Math_SinS(yaw) * xzVel; + velocity.y = Rand_CenteredFloat(8.0f); + velocity.z = Math_CosS(yaw) * xzVel; + effect->velocity = velocity; + + accel.y = Rand_CenteredFloat(0.3f) - 1.0f; + effect->accel = accel; + + effect->timer = 20; + if (sCsState <= DEATH_SHELL_BURST) { + effect->mode = GORE_FADING; + } else { + effect->mode = GORE_PERMANENT; + } + + effect->primColor[3] = effect->envColor[0] = effect->envColor[1] = effect->envColor[2] = + effect->envColor[3] = 255; + + effect->primColor[0] = 155; + effect->primColor[1] = effect->primColor[2] = 55; + + effect->rot.x = Rand_CenteredFloat(0x10000); + effect->rot.y = Rand_CenteredFloat(0x10000); + effect->scale = (Rand_ZeroFloat(scale) + scale) * 0.01f; + effect->vaGorePulseRate = (Rand_ZeroOne() * 0.25f) + 0.9f; + break; + } + } +} + +void BossVa_SpawnZapperCharge(GlobalContext* globalCtx, BossVaEffect* effect, BossVa* this, Vec3f* pos, Vec3s* rot, + s16 scale, u8 mode) { + Vec3f unused = { 0.0f, -1000.0f, 0.0f }; + s16 i; + + for (i = 0; i < ARRAY_COUNT(sVaEffects); i++, effect++) { + if (effect->type == VA_NONE) { + effect->type = VA_ZAP_CHARGE; + effect->parent = this; + effect->pos = *pos; + + effect->velocity = effect->accel = sZeroVec; + + effect->mode = mode; + effect->rot.x = rot->x + 0x4000; + effect->rot.y = rot->y; + effect->timer = (s16)(Rand_ZeroOne() * 10.0f) + 10; + effect->primColor[3] = 240; + effect->scale = scale * 0.01f; + break; + } + } +} + +void BossVa_DrawDoor(GlobalContext* globalCtx, s16 scale) { + static Gfx* doorPieceDispList[] = { + gBarinadeDoorPiece1DL, gBarinadeDoorPiece2DL, gBarinadeDoorPiece3DL, gBarinadeDoorPiece4DL, + gBarinadeDoorPiece5DL, gBarinadeDoorPiece6DL, gBarinadeDoorPiece7DL, gBarinadeDoorPiece8DL, + }; + static s16 doorPieceLength[] = { 836, 900, 836, 1016, 800, 1016, 836, 900 }; + MtxF doorMtx; + f32 yScale; + f32 segAngle = 0.0f; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 5600); + + Matrix_Translate(0.0f, 80.0f, 400.0f, MTXMODE_NEW); + Matrix_RotateY(M_PI, MTXMODE_APPLY); + yScale = (scale * 0.01f) * 0.1f; + Matrix_Scale(0.1f, yScale, 0.1f, MTXMODE_APPLY); + + if (yScale != 0.0f) { + yScale = 0.1f / yScale; + } else { + yScale = 0.0f; + } + + Matrix_Get(&doorMtx); + + for (i = 0; i < 8; i++, segAngle -= M_PI / 4) { + Matrix_Put(&doorMtx); + Matrix_RotateZ(segAngle, MTXMODE_APPLY); + Matrix_Translate(0.0f, doorPieceLength[i] * yScale, 0.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_boss_va.c", 5621), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, doorPieceDispList[i]); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_boss_va.c", 5629); +} + +void BossVa_Reset(void) { + sKillBari = 0; + sCsCamera = 0; + memset(sVaEffects, 0, sizeof(sVaEffects)); + sBodyState = 0; + sFightPhase = 0; + sCsState = 0; + sDoorState = 0; + sPhase3StopMoving = 0; + sZapperRot.x = 0; + sZapperRot.y = 0; + sZapperRot.z = 0; + sPhase2Timer = 0; + sPhase4HP = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.h b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.h new file mode 100644 index 000000000..5d5b1b433 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.h @@ -0,0 +1,74 @@ +#ifndef Z_BOSS_VA_H +#define Z_BOSS_VA_H + +#include "ultra64.h" +#include "global.h" + +struct BossVa; + +typedef void (*BossVaActionFunc)(struct BossVa*, GlobalContext*); + +typedef struct BossVa { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ BossVaActionFunc actionFunc; + /* 0x0194 */ u8 onCeiling; + /* 0x0195 */ u8 burst; + /* 0x0196 */ s8 invincibilityTimer; + /* 0x0197 */ u8 isDead; + /* 0x0198 */ s32 timer; + /* 0x019C */ s16 timer2; + /* 0x01A0 */ f32 unk_1A0; // Upper body section pulse, Bari spin radius + /* 0x01A4 */ f32 unk_1A4; // Lower body segment pulse, Bari bob phase + /* 0x01A8 */ f32 unk_1A8; // Death camera zoom. Always 0. + /* 0x01AC */ s16 unk_1AC; // Body pulse phase, death camera rotation, Bari radius + /* 0x01AE */ s16 bodyGlow; + /* 0x01B0 */ s16 unk_1B0; // Body glow pulse rate, death growths counter, zapper death camera + /* 0x01B4 */ Vec3f armTip; + /* 0x01C0 */ Vec3f zapNeckPos; + /* 0x01CC */ Vec3f zapHeadPos; + /* 0x01D8 */ Vec3f unk_1D8; // Bari scale, Attach point for Bari spin, zapper head + /* 0x01E4 */ s16 unk_1E4; // Various Zapper rotations + /* 0x01E6 */ s16 unk_1E6; + /* 0x01E8 */ s16 unk_1E8; + /* 0x01EA */ s16 unk_1EA; + /* 0x01EC */ s16 unk_1EC; + /* 0x01EE */ s16 unk_1EE; + /* 0x01F0 */ s16 unk_1F0; // Bari lightning scale + /* 0x01F2 */ s16 unk_1F2; // Intro camera rotation rate + /* 0x01F4 */ s16 unk_1F4; + /* 0x01F6 */ Vec3s headRot; + /* 0x01FC */ Vec3f effectPos[10]; + /* 0x0274 */ Vec3f unk_274; // Unused body position + /* 0x0280 */ Vec3f unk_280; // Unused body position + /* 0x028C */ ColliderCylinder colliderBody; + /* 0x02D8 */ ColliderJntSph colliderSph; + /* 0x02F8 */ ColliderJntSphElement elements[1]; + /* 0x0338 */ ColliderQuad colliderLightning; +} BossVa; // size = 0x03B8 + +typedef enum { + /* -1 */ BOSSVA_BODY = -1, + /* 0 */ BOSSVA_SUPPORT_1, + /* 1 */ BOSSVA_SUPPORT_2, + /* 2 */ BOSSVA_SUPPORT_3, + /* 3 */ BOSSVA_ZAPPER_1, + /* 4 */ BOSSVA_ZAPPER_2, + /* 5 */ BOSSVA_ZAPPER_3, + /* 6 */ BOSSVA_BARI_UPPER_1, + /* 7 */ BOSSVA_BARI_UPPER_2, + /* 8 */ BOSSVA_BARI_UPPER_3, + /* 9 */ BOSSVA_BARI_UPPER_4, + /* 10 */ BOSSVA_BARI_UPPER_5, + /* 11 */ BOSSVA_BARI_LOWER_1, + /* 12 */ BOSSVA_BARI_LOWER_2, + /* 13 */ BOSSVA_BARI_LOWER_3, + /* 14 */ BOSSVA_BARI_LOWER_4, + /* 15 */ BOSSVA_BARI_LOWER_5, + /* 16 */ BOSSVA_STUMP_1, + /* 17 */ BOSSVA_STUMP_2, + /* 18 */ BOSSVA_STUMP_3, + /* 19 */ BOSSVA_DOOR +} BossVaParam; + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c b/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c new file mode 100644 index 000000000..4f75a03cd --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.c @@ -0,0 +1,826 @@ +/* + * File: z_demo_6k.c + * Overlay: ovl_Demo_6K + * Description: Sages, balls of light (cutscene) + */ + +#include "z_demo_6k.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_demo_6k/object_demo_6k.h" +#include "objects/object_gnd_magic/object_gnd_magic.h" +#include "overlays/actors/ovl_Eff_Dust/z_eff_dust.h" + +#define FLAGS ACTOR_FLAG_4 + +void Demo6K_Init(Actor* thisx, GlobalContext* globalCtx); +void Demo6K_Destroy(Actor* thisx, GlobalContext* globalCtx); +void Demo6K_Update(Actor* thisx, GlobalContext* globalCtx); +void Demo6K_Reset(void); + + +void func_80966DB0(Demo6K* this, GlobalContext* globalCtx); +void func_80966E04(Demo6K* this, GlobalContext* globalCtx); +void func_80966E98(Demo6K* this, GlobalContext* globalCtx); +void func_80966F84(Demo6K* this, GlobalContext* globalCtx); +void func_8096712C(Demo6K* this, GlobalContext* globalCtx); +void func_80967410(Demo6K* this, GlobalContext* globalCtx); +void func_809674E0(Demo6K* this, GlobalContext* globalCtx); +void func_8096784C(Demo6K* this, GlobalContext* globalCtx); +void func_80967A04(Demo6K* this, s32 i); +void func_80967AD0(Demo6K* this, GlobalContext* globalCtx); +void func_80967DBC(Demo6K* this, GlobalContext* globalCtx); +void func_80967F10(Demo6K* this, GlobalContext* globalCtx); +void func_80967FFC(Actor* thisx, GlobalContext* globalCtx); +void func_80968298(Actor* thisx, GlobalContext* globalCtx); +void func_8096865C(Actor* thisx, GlobalContext* globalCtx); +void func_809688C4(Actor* thisx, GlobalContext* globalCtx); +void func_80968B70(Actor* thisx, GlobalContext* globalCtx); +void func_80968FB0(Actor* thisx, GlobalContext* globalCtx); +void func_809691BC(Demo6K* this, GlobalContext* globalCtx, s32 params); + +const ActorInit Demo_6K_InitVars = { + ACTOR_DEMO_6K, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(Demo6K), + (ActorFunc)Demo6K_Init, + (ActorFunc)Demo6K_Destroy, + (ActorFunc)Demo6K_Update, + NULL, + Demo6K_Reset, +}; + +static s16 sObjectIds[] = { + OBJECT_GAMEPLAY_KEEP, OBJECT_DEMO_6K, OBJECT_DEMO_6K, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, + OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, + OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GND_MAGIC, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, + OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, +}; +static Color_RGB8 sEnvColors[] = { + { 255, 50, 0 }, { 0, 200, 0 }, { 200, 255, 0 }, { 200, 50, 255 }, { 255, 150, 0 }, { 0, 150, 255 }, +}; +static f32 D_8096930C[] = { 1.0f, 1.04f, 1.0f, 0.96f }; +static f32 D_8096931C[] = { 1.1f, 1.0f, 0.9f, 0.8f }; + +void Demo6K_SetupAction(Demo6K* this, Demo6KActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void Demo6K_Init(Actor* thisx, GlobalContext* globalCtx) { + Demo6K* this = (Demo6K*)thisx; + s32 pad; + s32 params = this->actor.params; + s32 objBankIndex; + s32 i; + + osSyncPrintf("no = %d\n", params); + + if (sObjectIds[params] != OBJECT_GAMEPLAY_KEEP) { + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectIds[params]); + } else { + objBankIndex = 0; + } + + osSyncPrintf("bank_ID = %d\n", objBankIndex); + + if (objBankIndex < 0) { + ASSERT(0, "0", "../z_demo_6k.c", 334); + } else { + this->objBankIndex = objBankIndex; + } + + Demo6K_SetupAction(this, func_80966DB0); + this->timer1 = 0; + this->flags = 0; + this->timer2 = 0; + + switch (params) { + case 0: + this->drawFunc = func_809688C4; + this->initActionFunc = func_80967AD0; + Actor_SetScale(&this->actor, 1.0f); + + for (i = 0; i < 16; i++) { + func_80967A04(this, i); + this->unk_1B4[i] = 0.0f; + } + + this->unk_170 = 0.0f; + break; + case 1: + this->drawFunc = func_80967FFC; + this->initActionFunc = func_80966E04; + Actor_SetScale(&this->actor, 0.228f); + break; + case 2: + this->drawFunc = func_80968298; + this->initActionFunc = func_80966F84; + Actor_SetScale(&this->actor, 0.1f); + this->unk_164 = 1.0f; + this->unk_168 = 1.0f; + this->unk_16C = 0.0f; + this->unk_170 = 0.0f; + break; + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + this->drawFunc = func_8096865C; + this->initActionFunc = func_8096712C; + Actor_SetScale(&this->actor, 0.0f); + this->unk_293 = params - 3; + break; + case 9: + case 10: + this->drawFunc = func_8096865C; + Actor_SetScale(&this->actor, 0.0f); + this->initActionFunc = func_809674E0; + break; + case 11: + this->drawFunc = func_8096865C; + Actor_SetScale(&this->actor, 0.0f); + this->initActionFunc = func_8096784C; + this->actor.velocity.x = this->actor.velocity.y = this->actor.velocity.z = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_NABALL_VANISH); + break; + case 12: + Actor_SetScale(&this->actor, 0.0f); + this->initActionFunc = func_80967F10; + this->drawFunc = func_80968B70; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ITEMACTION); + break; + case 13: + Actor_SetScale(&this->actor, 0.14f); + Demo6K_SetupAction(this, func_80967DBC); + this->actor.draw = func_80968FB0; + this->unk_293 = 0; + break; + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + this->actor.flags |= ACTOR_FLAG_5; + this->drawFunc = func_8096865C; + this->initActionFunc = func_80967410; + this->flags |= 1; + Actor_SetScale(&this->actor, 0.2f); + this->unk_293 = params - 14; + break; + default: + ASSERT(0, "0", "../z_demo_6k.c", 435); + break; + } + + switch (params) { + case 9: + this->unk_293 = 0; + break; + case 10: + this->unk_293 = 5; + break; + case 11: + this->unk_293 = 4; + break; + } + + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, 100); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); +} + +void Demo6K_Destroy(Actor* thisx, GlobalContext* globalCtx) { + Demo6K* this = (Demo6K*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); +} + +void func_80966DB0(Demo6K* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->actor.objBankIndex = this->objBankIndex; + this->actor.draw = this->drawFunc; + this->actionFunc = this->initActionFunc; + } +} + +void func_80966E04(Demo6K* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames > 214) { + func_8002F948(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + } + + if (globalCtx->csCtx.frames > 264) { + func_8002F948(&this->actor, NA_SE_EV_GOD_LIGHTBALL_2 - SFX_FLAG); + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[6] != NULL) && + (globalCtx->csCtx.npcActions[6]->action == 2)) { + Demo6K_SetupAction(this, func_80966E98); + } +} + +void func_80966E98(Demo6K* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames < 353) { + func_8002F948(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + func_8002F948(&this->actor, NA_SE_EV_GOD_LIGHTBALL_2 - SFX_FLAG); + } + + if (globalCtx->csCtx.frames == 342) { + func_800F3F3C(2); + } + + if (this->timer1 == 39) { + func_800788CC(NA_SE_EV_CONSENTRATION); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + this->actor.world.pos.y + 10.0f, this->actor.world.pos.z, 0, 0, 0, 2); + } + + if (this->timer1 == 64) { + Actor_Kill(&this->actor); + } + + this->timer1++; +} + +void func_80966F84(Demo6K* this, GlobalContext* globalCtx) { + if (this->timer1 < 5) { + this->unk_168 = D_8096930C[this->timer1 & 3]; + } else if (this->timer1 < 15) { + this->actor.scale.x += 0.012f; + Actor_SetScale(&this->actor, this->actor.scale.x); + Math_StepToF(&this->unk_170, 0.6f, 0.05f); + this->unk_168 = 1.0f; + } else { + if (this->timer1 == 15) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EFF_DUST, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, EFF_DUST_TYPE_1); + } + Math_StepToF(&this->unk_16C, 1.0f, 0.02f); + this->unk_168 = D_8096930C[this->timer1 & 1]; + } + + this->timer1++; +} + +void func_809670AC(Demo6K* this, GlobalContext* globalCtx) { + this->timer2++; + + if (this->timer1 < 10) { + this->timer1++; + } else if (this->actor.scale.x > 0.0f) { + this->actor.scale.x -= 1.0f / 120.0f; + Actor_SetScale(&this->actor, this->actor.scale.x); + } else { + Actor_Kill(&this->actor); + } +} + +void func_8096712C(Demo6K* this, GlobalContext* globalCtx) { + static u16 D_8096932C[] = { 275, 275, 275, 275, 275, 275 }; + u32 frames = globalCtx->state.frames; + + if (this->actor.scale.x < 0.1f) { + this->actor.scale.x += 0.0017f; + } else if (frames & 1) { + this->actor.scale.x = 0.1f * 1.04f; + } else { + this->actor.scale.x = 0.1f; + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[6] != NULL) && + (globalCtx->csCtx.npcActions[6]->action == 2)) { + Demo6K_SetupAction(this, func_809670AC); + this->timer1 = 0; + this->actor.scale.x = 0.1f; + } + + Actor_SetScale(&this->actor, this->actor.scale.x); + + this->timer2++; + + if ((globalCtx->sceneNum == SCENE_GANONTIKA) && (globalCtx->csCtx.frames < D_8096932C[this->actor.params - 3])) { + func_8002F974(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + } +} + +static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; +void func_80967244(Demo6K* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 0 }; + static Color_RGBA8 envColor = { 255, 150, 0, 0 }; + Vec3f pos; + s16 rand1; + s16 rand2; + s32 scale; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z; + + rand1 = Rand_ZeroFloat(0xFFFF); + rand2 = Rand_ZeroFloat(0xFFFF); + + velocity.x = Math_SinS(rand2) * Math_CosS(rand1) * 20.0f; + velocity.z = Math_CosS(rand2) * Math_CosS(rand1) * 20.0f; + velocity.y = Math_SinS(rand1) * 20.0f; + + accel.y = 0.0f; + + envColor.r = sEnvColors[this->unk_293].r; + envColor.g = sEnvColors[this->unk_293].g; + envColor.b = sEnvColors[this->unk_293].b; + + if (globalCtx->sceneNum == SCENE_TOKINOMA) { + scale = 6000; + } else if (globalCtx->csCtx.frames < 419) { + scale = 6000; + } else { + scale = 18000; + } + + EffectSsKiraKira_SpawnFocused(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, scale, 20); +} + +void func_80967410(Demo6K* this, GlobalContext* globalCtx) { + s32 params = this->actor.params - 14; + + this->timer2++; + + Actor_SetScale(&this->actor, 0.2f); + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[params] != NULL)) { + func_809691BC(this, globalCtx, params); + + if (globalCtx->csCtx.npcActions[params]->action == 3) { + this->flags &= ~1; + func_80967244(this, globalCtx); + } else { + this->flags |= 1; + } + } else { + this->flags |= 1; + } +} + +void func_809674E0(Demo6K* this, GlobalContext* globalCtx) { + u32 frames = globalCtx->state.frames; + + if (this->actor.scale.x < 0.05f) { + this->actor.scale.x += 0.005f; + } else if (frames & 1) { + this->actor.scale.x = 0.05f * 1.04f; + } else { + this->actor.scale.x = 0.05f; + } + + Actor_SetScale(&this->actor, this->actor.scale.x); + + this->timer2++; + + if (this->timer2 > 47) { + Actor_Kill(&this->actor); + } else if (this->timer2 > 39) { + f32 dTimer = this->timer2 - 39; + f32 temp = 1.0f / (9.0f - dTimer); + + this->actor.world.pos.x += (-1611.0f - this->actor.world.pos.x) * temp; + this->actor.world.pos.y += (19.0f - this->actor.world.pos.y) * temp; + this->actor.world.pos.z += (1613.0f - this->actor.world.pos.z) * temp; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_FIRE - SFX_FLAG); + } + + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, sEnvColors[this->unk_293].r, sEnvColors[this->unk_293].g, + sEnvColors[this->unk_293].b, this->actor.scale.x * 4000.0f); +} + +void func_809676A4(Demo6K* this, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 0 }; + static Color_RGBA8 envColor = { 255, 150, 0, 0 }; + Vec3f pos; + f32 temp = this->actor.scale.x * 500.0f; + s32 i; + + for (i = 0; i < 8; i++) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(temp); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(temp); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(temp); + + velocity.x = Rand_CenteredFloat(2.0f); + velocity.y = (Rand_ZeroFloat(-10.0f) - 5.0f) * 0.1f; + velocity.z = Rand_CenteredFloat(2.0f); + + accel.y = 0.0f; + + EffectSsKiraKira_SpawnFocused(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 500, 20); + } +} + +void func_8096784C(Demo6K* this, GlobalContext* globalCtx) { + u32 frames = globalCtx->state.frames; + + this->timer2++; + + if (this->timer2 > 24) { + Actor_Kill(&this->actor); + } else if (this->timer2 > 4) { + this->actor.velocity.x += Rand_CenteredFloat(0.2f); + this->actor.velocity.y += 0.12f; + this->actor.velocity.z += Rand_CenteredFloat(0.2f); + + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.world.pos.z += this->actor.velocity.z; + + this->actor.scale.x -= 0.0015f; + + func_809676A4(this, globalCtx); + } else if (frames & 1) { + this->actor.scale.x = 0.033f; + } else { + this->actor.scale.x = 0.03f; + } + + Actor_SetScale(&this->actor, this->actor.scale.x); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, sEnvColors[this->unk_293].r, sEnvColors[this->unk_293].g, + sEnvColors[this->unk_293].b, this->actor.scale.x * 4000.0f); +} + +void func_80967A04(Demo6K* this, s32 i) { + this->unk_174[i] = (i * 10.0f) - 80.0f; + this->unk_1B4[i] = Rand_ZeroFloat(100.0f) + 100.0f; + this->unk_1F4[i] = -3.0f - Rand_ZeroFloat(6.0f); + this->unk_274[i] = (s32)Rand_ZeroFloat(6.0f); + this->unk_234[i] = Rand_ZeroFloat(0.02f) + 0.01f; +} + +void func_80967AD0(Demo6K* this, GlobalContext* globalCtx) { + s32 i; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[1] != NULL)) { + if (globalCtx->csCtx.npcActions[1]->action == 2) { + this->unk_170++; + func_8002F948(&this->actor, NA_SE_EV_RAINBOW_SHOWER - SFX_FLAG); + } + + func_809691BC(this, globalCtx, 1); + } + + for (i = 0; (i < (s32)this->unk_170) && (i < 16); i++) { + this->unk_1B4[i] += this->unk_1F4[i]; + if (this->unk_1B4[i] < 0.0f) { + func_80967A04(this, i); + } + } + + this->timer1++; +} + +void func_80967BF8(Player* player, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 0 }; + static Color_RGBA8 envColor = { 255, 200, 0, 0 }; + Vec3f pos; + s32 i; + + for (i = 0; i < 150; i++) { + pos.x = Rand_CenteredFloat(15.0f) + player->actor.world.pos.x; + pos.y = Rand_CenteredFloat(15.0f) + player->actor.world.pos.y + 30.0f; + pos.z = Rand_CenteredFloat(15.0f) + player->actor.world.pos.z; + + velocity.x = Rand_CenteredFloat(8.0f) + 1.0f; + velocity.y = Rand_CenteredFloat(4.0f); + velocity.z = Rand_CenteredFloat(8.0f) + 2.0f; + + accel.y = 0.0f; + + EffectSsKiraKira_SpawnFocused(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 1000, + (s32)Rand_ZeroFloat(60.0f) + 60); + } +} + +void func_80967DBC(Demo6K* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_ATTACK_DEMO - SFX_FLAG); + + this->timer2++; + + if (this->timer2 > 44) { + if (this->unk_293 < 236) { + this->unk_293 += 20; + } else { + this->unk_293 = 255; + } + + if (this->timer2 > 104) { + func_80967BF8(GET_PLAYER(globalCtx), globalCtx); + Actor_Kill(&this->actor); + Audio_PlayActorSound2(&GET_PLAYER(globalCtx)->actor, NA_SE_EN_FANTOM_HIT_THUNDER); + } else if (this->timer2 > 94) { + Actor_SetScale(&this->actor, this->actor.scale.x + 0.03f); + + if (this->timer2 == 95) { + osSyncPrintf(VT_FGCOL(CYAN) " NA_SE_EN_GANON_FIRE_DEMO\n" VT_RST); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_FIRE_DEMO); + } + } + + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 200, 0, this->unk_293); + } +} + +void func_80967F10(Demo6K* this, GlobalContext* globalCtx) { + if (this->timer2 == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 13); + } + + this->timer2++; + + if (this->timer2 > 60) { + Actor_Kill(&this->actor); + } + + Actor_SetScale(&this->actor, 0.05f - (this->timer2 * 0.00075f)); +} + +void Demo6K_Update(Actor* thisx, GlobalContext* globalCtx) { + Demo6K* this = (Demo6K*)thisx; + + this->actionFunc(this, globalCtx); +} + +void func_80967FFC(Actor* thisx, GlobalContext* globalCtx) { + Demo6K* this = (Demo6K*)thisx; + s32 pad; + u16 timer1 = this->timer1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1070); + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_RotateX(-M_PI / 2, MTXMODE_APPLY); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0x7FFF - ((timer1 * 8) & 0x7FFF), 16, 512, 1, 0, + 0x7FFF - ((timer1 * 8) & 0x7FFF), 16, 32)); + + { + s32 i; + s32 pad; + Color_RGB8 colors[6][2] = { + { { 255, 170, 255 }, { 255, 0, 100 } }, { { 255, 255, 170 }, { 0, 255, 0 } }, + { { 255, 255, 170 }, { 255, 255, 0 } }, { { 255, 170, 255 }, { 50, 0, 255 } }, + { { 255, 255, 170 }, { 255, 100, 0 } }, { { 170, 255, 255 }, { 0, 100, 255 } }, + }; + + Matrix_RotateZ(-M_PI / 2, MTXMODE_APPLY); + + for (i = 0; i < 6; i++) { + Matrix_RotateZ(M_PI / 3, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1115), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, colors[i][0].r, colors[i][0].g, colors[i][0].b, 255); + gDPSetEnvColor(POLY_XLU_DISP++, colors[i][1].r, colors[i][1].g, colors[i][1].b, 255); + gSPDisplayList(POLY_XLU_DISP++, object_demo_6k_DL_0022B0); + } + + // required to avoid optimizing out i + if ((s16)i) {} + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1127); +} + +void func_80968298(Actor* thisx, GlobalContext* globalCtx) { + static u8 skipIndices[] = { 6, 7, 11, 16, 20, 24, 28, 33, 35, 41, 45, 50, 57, 58, 62, 255 }; + Demo6K* this = (Demo6K*)thisx; + s32 pad; + u32 timer1 = this->timer1; + f32 scale = this->unk_164 * this->unk_168; + Vtx* vertices = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(object_demo_6kVtx_0035E0)); + s32 i; + s32 i2; + u8 alpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1145); + + alpha = (s32)(this->unk_170 * 255.0f); + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, alpha); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); + func_80093D84(globalCtx->state.gfxCtx); + + alpha = (s32)(this->unk_16C * 255.0f); + for (i2 = 0, i = 0; i < 63; i++) { + if (i == skipIndices[i2]) { + i2++; + } else { + vertices[i].v.cn[3] = alpha; + } + } + + Matrix_RotateX(-M_PI / 2, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1170), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 210, 210, 210, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 100, 100, 255); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (0xFFF - (timer1 * 6)) & 0xFFF, (timer1 * 12) & 0xFFF, 128, + 64, 1, (0xFFF - (timer1 * 6)) & 0xFFF, (timer1 * 12) & 0xFFF, 64, 32)); + gSPDisplayList(POLY_XLU_DISP++, object_demo_6k_DL_0039D0); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1189), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 50, 50, 50, 255); + gSPDisplayList(POLY_XLU_DISP++, object_demo_6k_DL_001040); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1198); +} + +void func_8096865C(Actor* thisx, GlobalContext* globalCtx) { + Demo6K* this = (Demo6K*)thisx; + s32 pad; + Gfx* displayList; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1208); + + if (!(this->flags & 1)) { + if (this->actor.params > 8) { + displayList = gEffFlash1DL; + } else { + displayList = gEffFlash2DL; + } + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, sEnvColors[this->unk_293].r, sEnvColors[this->unk_293].g, + sEnvColors[this->unk_293].b, 255); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_Push(); + Matrix_RotateZ((f32)(this->timer2 * 6) * (M_PI / 180.0f), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1230), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, displayList); + Matrix_Pop(); + Matrix_RotateZ(-(f32)(this->timer2 * 6) * (M_PI / 180.0f), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1236), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, displayList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1242); +} + +void func_809688C4(Actor* thisx, GlobalContext* globalCtx2) { + Demo6K* this = (Demo6K*)thisx; + GlobalContext* globalCtx = globalCtx2; + u32 frames = globalCtx->state.frames; + s32 i; + + if ((i = (globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[1] != NULL)) && + (globalCtx->csCtx.npcActions[1]->action != 1)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1277); + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 255, 255); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * (M_PI / 0x8000), MTXMODE_APPLY); + + for (i = 0; i < 16; i++) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, sEnvColors[this->unk_274[i]].r, sEnvColors[this->unk_274[i]].g, + sEnvColors[this->unk_274[i]].b, 255); + Matrix_Push(); + Matrix_Translate(this->unk_174[i], this->unk_1B4[i], 0.0f, MTXMODE_APPLY); + Matrix_Scale(this->unk_234[i] * D_8096931C[(frames + i) & 3], + this->unk_234[i] * D_8096931C[(frames + i) & 3], + this->unk_234[i] * D_8096931C[(frames + i) & 3], MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1297), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + Matrix_Pop(); + } + + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1305); + } +} + +void func_80968B70(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + Demo6K* this = (Demo6K*)thisx; + u32 timer2 = this->timer2; + u8 primColor[4]; + u8 envColor[3]; + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1316); + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1322), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0xFF - ((timer2 * 2) & 0xFF), 0, 32, 32, 1, + 0xFF - ((timer2 * 2) & 0xFF), (timer2 * 15) & 0x3FF, 16, 64)); + + if (this->timer2 < 40) { + primColor[0] = primColor[2] = 100 - (this->timer2 * 2.5f); + primColor[1] = envColor[1] = 0; + envColor[0] = 100 - primColor[2]; + primColor[3] = this->timer2 * 6.375f; + envColor[2] = envColor[0] * 2; + } else if (this->timer2 < 50) { + primColor[0] = (this->timer2 * 5) - 200; + primColor[1] = primColor[2] = 0; + primColor[3] = 255; + envColor[0] = 100 - primColor[0]; + envColor[1] = primColor[0] * 2; + envColor[2] = 200 - (primColor[0] * 4); + } else { + primColor[2] = (this->timer2 * 5) - 250; + envColor[2] = 0; + primColor[1] = primColor[2] * 3; + primColor[3] = 255; + primColor[0] = envColor[0] = (primColor[2] * 2) + 50; + envColor[1] = 100 - primColor[2]; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, primColor[0], primColor[1], primColor[2], primColor[3]); + gDPSetEnvColor(POLY_XLU_DISP++, envColor[0], envColor[1], envColor[2], 128); + gSPDisplayList(POLY_XLU_DISP++, object_gnd_magic_DL_001190); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1368); +} + +void func_80968FB0(Actor* thisx, GlobalContext* globalCtx) { + static u8 D_809693CC[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1 }; + Demo6K* this = (Demo6K*)thisx; + Gfx* displayList = Graph_Alloc(globalCtx->state.gfxCtx, 4 * sizeof(Gfx)); + u16 frames = globalCtx->gameplayFrames; + f32 scaleFactor; + s32 pad; + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1386); + + func_80093D84(globalCtx->state.gfxCtx); + scaleFactor = ((s16)D_809693CC[(frames * 4) & 0xF] * 0.01f) + 1.0f; + Matrix_Scale(this->actor.scale.x * scaleFactor, this->actor.scale.y * scaleFactor, + this->actor.scale.z * scaleFactor, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1394), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, displayList); + gDPPipeSync(displayList++); + gDPSetPrimColor(displayList++, 0, 0x80, 255, 255, 255, this->unk_293); + gDPSetRenderMode(displayList++, G_RM_PASS, G_RM_ZB_CLD_SURF2); + gSPEndDisplayList(displayList++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 200, 0, 255); + gSPDisplayList(POLY_XLU_DISP++, gGlowCircleSmallDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_6k.c", 1411); +} + +void func_809691BC(Demo6K* this, GlobalContext* globalCtx, s32 params) { + Vec3f startPos; + Vec3f endPos; + f32 temp; + CsCmdActorAction* csAction = globalCtx->csCtx.npcActions[params]; + + startPos.x = csAction->startPos.x; + startPos.y = csAction->startPos.y; + startPos.z = csAction->startPos.z; + + endPos.x = csAction->endPos.x; + endPos.y = csAction->endPos.y; + endPos.z = csAction->endPos.z; + + temp = Environment_LerpWeight(csAction->endFrame, csAction->startFrame, globalCtx->csCtx.frames); + + this->actor.world.pos.x = (((endPos.x - startPos.x) * temp) + startPos.x); + this->actor.world.pos.y = (((endPos.y - startPos.y) * temp) + startPos.y); + this->actor.world.pos.z = (((endPos.z - startPos.z) * temp) + startPos.z); +} + +void Demo6K_Reset(void) { + velocity.x = 0.0f; + velocity.y = 0.0f; + velocity.z = 0.0f; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.h b/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.h new file mode 100644 index 000000000..f144bb9f8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_6K/z_demo_6k.h @@ -0,0 +1,34 @@ +#ifndef Z_DEMO_6K_H +#define Z_DEMO_6K_H + +#include "ultra64.h" +#include "global.h" + +struct Demo6K; + +typedef void (*Demo6KActionFunc)(struct Demo6K*, GlobalContext*); + +typedef struct Demo6K { + /* 0x0000 */ Actor actor; + /* 0x014C */ Demo6KActionFunc initActionFunc; + /* 0x0150 */ LightInfo lightInfo; + /* 0x0160 */ LightNode* lightNode; + /* 0x0164 */ f32 unk_164; + /* 0x0168 */ f32 unk_168; + /* 0x016C */ f32 unk_16C; + /* 0x0170 */ f32 unk_170; + /* 0x0174 */ f32 unk_174[16]; + /* 0x01B4 */ f32 unk_1B4[16]; + /* 0x01F4 */ f32 unk_1F4[16]; + /* 0x0234 */ f32 unk_234[16]; + /* 0x0274 */ u8 unk_274[16]; + /* 0x0284 */ ActorFunc drawFunc; + /* 0x0288 */ Demo6KActionFunc actionFunc; + /* 0x028C */ u16 flags; + /* 0x028E */ u16 timer1; + /* 0x0290 */ u16 timer2; + /* 0x0292 */ u8 objBankIndex; + /* 0x0293 */ u8 unk_293; +} Demo6K; // size = 0x0294 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du.c b/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du.c new file mode 100644 index 000000000..16f3daec0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du.c @@ -0,0 +1,1053 @@ +#include "z_demo_du.h" +#include "objects/object_du/object_du.h" +#include "overlays/actors/ovl_Demo_Effect/z_demo_effect.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef void (*DemoDuActionFunc)(DemoDu*, GlobalContext*); +typedef void (*DemoDuDrawFunc)(Actor*, GlobalContext*); + +void DemoDu_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoDu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoDu_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoDu_Draw(Actor* thisx, GlobalContext* globalCtx); +void DemoDu_Reset(void); + +//static s32 sUnused = 0; + +#include "z_demo_du_cutscene_data.c" EARLY + +static void* sEyeTextures[] = { gDaruniaEyeOpenTex, gDaruniaEyeOpeningTex, gDaruniaEyeShutTex, gDaruniaEyeClosingTex }; +static void* sMouthTextures[] = { gDaruniaMouthSeriousTex, gDaruniaMouthGrinningTex, gDaruniaMouthOpenTex, + gDaruniaMouthHappyTex }; + +/** + * Cs => Cutscene + * + * FM => Fire Medallion + * GR => Goron's Ruby + * AG => In the chamber of sages, just After the final blow on Ganon. + * CR => Credits + * + */ + +// Each macro maps its argument to an index of sUpdateFuncs. +#define CS_FIREMEDALLION_SUBSCENE(x) (0 + (x)) // DEMO_DU_CS_FIREMEDALLION +#define CS_GORONSRUBY_SUBSCENE(x) (7 + (x)) // DEMO_DU_CS_GORONS_RUBY +#define CS_CHAMBERAFTERGANON_SUBSCENE(x) (21 + (x)) // DEMO_DU_CS_CHAMBER_AFTER_GANON +#define CS_CREDITS_SUBSCENE(x) (24 + (x)) // DEMO_DU_CS_CREDITS + +void DemoDu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoDu* this = (DemoDu*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void DemoDu_UpdateEyes(DemoDu* this) { + s16* blinkTimer = &this->blinkTimer; + s16* eyeTexIndex = &this->eyeTexIndex; + s32 pad[3]; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + + *eyeTexIndex = *blinkTimer; + if (*eyeTexIndex >= 3) { + *eyeTexIndex = 0; + } +} + +void DemoDu_SetEyeTexIndex(DemoDu* this, s16 eyeTexIndex) { + this->eyeTexIndex = eyeTexIndex; +} + +void DemoDu_SetMouthTexIndex(DemoDu* this, s16 mouthTexIndex) { + this->mouthTexIndex = mouthTexIndex; +} + +// Resets all the values used in this cutscene. +void DemoDu_CsAfterGanon_Reset(DemoDu* this) { + this->updateIndex = CS_CHAMBERAFTERGANON_SUBSCENE(0); + this->drawIndex = 0; + this->shadowAlpha = 0; + this->demo6KSpawned = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_1A4 = 0.0f; +} + +s32 D_8096CE94 = false; +void DemoDu_CsAfterGanon_CheckIfShouldReset(DemoDu* this, GlobalContext* globalCtx) { + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + if (D_8096CE94) { + if (this->actor.params == DEMO_DU_CS_CHAMBER_AFTER_GANON) { + DemoDu_CsAfterGanon_Reset(this); + } + D_8096CE94 = false; + return; + } + } else if (!D_8096CE94) { + D_8096CE94 = true; + } +} + +s32 DemoDu_UpdateSkelAnime(DemoDu* this) { + return SkelAnime_Update(&this->skelAnime); +} + +void DemoDu_UpdateBgCheckInfo(DemoDu* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +CsCmdActorAction* DemoDu_GetNpcAction(GlobalContext* globalCtx, s32 idx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[idx]; + } + return NULL; +} + +s32 DemoDu_IsNpcDoingThisAction(DemoDu* this, GlobalContext* globalCtx, u16 action, s32 idx) { + CsCmdActorAction* npcAction = DemoDu_GetNpcAction(globalCtx, idx); + + if ((npcAction != NULL) && (npcAction->action == action)) { + return true; + } + return false; +} + +s32 DemoDu_IsNpcNotDoingThisAction(DemoDu* this, GlobalContext* globalCtx, u16 action, s32 idx) { + CsCmdActorAction* npcAction = DemoDu_GetNpcAction(globalCtx, idx); + + if ((npcAction != NULL) && (npcAction->action != action)) { + return true; + } + return false; +} + +void DemoDu_MoveToNpcPos(DemoDu* this, GlobalContext* globalCtx, s32 idx) { + CsCmdActorAction* npcAction = DemoDu_GetNpcAction(globalCtx, idx); + s32 pad; + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +void func_80969DDC(DemoDu* this, AnimationHeader* animation, u8 mode, f32 morphFrames, s32 arg4) { + f32 startFrame; + s16 lastFrame = Animation_GetLastFrame(animation); + f32 endFrame; + f32 playSpeed; + + if (arg4 == 0) { + startFrame = 0.0f; + endFrame = lastFrame; + playSpeed = 1.0f; + } else { + endFrame = 0.0f; + playSpeed = -1.0f; + startFrame = lastFrame; + } + Animation_Change(&this->skelAnime, animation, playSpeed, startFrame, endFrame, mode, morphFrames); +} + +void DemoDu_InitCs_FireMedallion(DemoDu* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDaruniaSkel, &gDaruniaIdleAnim, NULL, NULL, 0); + this->actor.shape.yOffset = -10000.0f; + DemoDu_SetEyeTexIndex(this, 1); + DemoDu_SetMouthTexIndex(this, 3); +} + +// A.k.a Warp portal +void DemoDu_CsFireMedallion_SpawnDoorWarp(DemoDu* this, GlobalContext* globalCtx) { + f32 posX = this->actor.world.pos.x; + f32 posY = this->actor.world.pos.y; + f32 posZ = this->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, + WARP_SAGES); +} + +// Gives the Fire Medallion to Link. +void func_80969F38(DemoDu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 posX = player->actor.world.pos.x; + f32 posY = player->actor.world.pos.y + 80.0f; + f32 posZ = player->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, + DEMO_EFFECT_MEDAL_FIRE); + Item_Give(globalCtx, ITEM_MEDALLION_FIRE); +} + +void func_80969FB4(DemoDu* this, GlobalContext* globalCtx) { + this->actor.shape.yOffset += 250.0f / 3.0f; +} + +// Gives the Fire Medallion to Link too. +void DemoDu_CsFireMedallion_AdvanceTo01(DemoDu* this, GlobalContext* globalCtx) { + s32 pad[2]; + + if ((gSaveContext.chamberCutsceneNum == 1) && (gSaveContext.sceneSetupIndex < 4)) { + Player* player = GET_PLAYER(globalCtx); + + this->updateIndex = CS_FIREMEDALLION_SUBSCENE(1); + globalCtx->csCtx.segment = D_8096C1A4; + gSaveContext.cutsceneTrigger = 2; + Item_Give(globalCtx, ITEM_MEDALLION_FIRE); + + player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + } +} + +void DemoDu_CsFireMedallion_AdvanceTo02(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 1)) { + this->updateIndex = CS_FIREMEDALLION_SUBSCENE(2); + this->drawIndex = 1; + DemoDu_CsFireMedallion_SpawnDoorWarp(this, globalCtx); + } + } +} + +void DemoDu_CsFireMedallion_AdvanceTo03(DemoDu* this) { + if (this->actor.shape.yOffset >= 0.0f) { + this->updateIndex = CS_FIREMEDALLION_SUBSCENE(3); + this->actor.shape.yOffset = 0.0f; + } +} + +void DemoDu_CsFireMedallion_AdvanceTo04(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 2)) { + Animation_Change(&this->skelAnime, &gDaruniaItemGiveAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaItemGiveAnim), 2, 0.0f); + this->updateIndex = CS_FIREMEDALLION_SUBSCENE(4); + } + } +} + +void DemoDu_CsFireMedallion_AdvanceTo05(DemoDu* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gDaruniaItemGiveIdleAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaItemGiveIdleAnim), 0, 0.0f); + this->updateIndex = CS_FIREMEDALLION_SUBSCENE(5); + } +} + +void DemoDu_CsFireMedallion_AdvanceTo06(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[6]; + + if ((npcAction != NULL) && (npcAction->action == 2)) { + this->updateIndex = CS_FIREMEDALLION_SUBSCENE(6); + func_80969F38(this, globalCtx); + } + } +} + +void DemoDu_UpdateCs_FM_00(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsFireMedallion_AdvanceTo01(this, globalCtx); +} + +void DemoDu_UpdateCs_FM_01(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsFireMedallion_AdvanceTo02(this, globalCtx); +} + +void DemoDu_UpdateCs_FM_02(DemoDu* this, GlobalContext* globalCtx) { + func_80969FB4(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsFireMedallion_AdvanceTo03(this); +} + +void DemoDu_UpdateCs_FM_03(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsFireMedallion_AdvanceTo04(this, globalCtx); +} + +void DemoDu_UpdateCs_FM_04(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_CsFireMedallion_AdvanceTo05(this, animFinished); +} + +void DemoDu_UpdateCs_FM_05(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsFireMedallion_AdvanceTo06(this, globalCtx); +} + +void DemoDu_UpdateCs_FM_06(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); +} + +void DemoDu_InitCs_GoronsRuby(DemoDu* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDaruniaSkel, NULL, NULL, NULL, 0); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(0); +} + +// Cutscene: Darunia gives Link the Goron's Ruby. +// Sfx played when Darunia lands at the floor at the start of the cutscene. +void DemoDu_CsPlaySfx_GoronLanding(DemoDu* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EN_GOLON_LAND_BIG); +} + +// Cutscene: Darunia gives Link the Goron's Ruby. +// Sfx played when Darunia is falling at the start of the cutscene. +void DemoDu_CsPlaySfx_DaruniaFalling(GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 160) { + func_800788CC(NA_SE_EV_OBJECT_FALL); + } +} + +// Cutscene: Darunia gives Link the Goron's Ruby. +void DemoDu_CsPlaySfx_DaruniaHitsLink(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + func_80078914(&player->actor.projectedPos, NA_SE_EN_DARUNIA_HIT_LINK); + Audio_PlaySoundGeneral(NA_SE_VO_LI_DAMAGE_S_KID, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); +} + +// Cutscene: Darunia gives Link the Goron's Ruby. +void DemoDu_CsPlaySfx_HitBreast(DemoDu* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EN_DARUNIA_HIT_BREAST - SFX_FLAG); +} + +// Cutscene: Darunia gives Link the Goron's Ruby. +// Sfx played when Link is escaping from the gorons at the end of the scene. +void DemoDu_CsPlaySfx_LinkEscapeFromGorons(GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 1400) { + Player* player = GET_PLAYER(globalCtx); + + Audio_PlaySoundGeneral(NA_SE_VO_LI_FALL_L_KID, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +// Cutscene: Darunia gives Link the Goron's Ruby. +// Sfx played when Link is surprised by Darunia falling from the sky. +void DemoDu_CsPlaySfx_LinkSurprised(GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 174) { + Player* player = GET_PLAYER(globalCtx); + + Audio_PlaySoundGeneral(NA_SE_VO_LI_SURPRISE_KID, &player->actor.projectedPos, 4U, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void DemoDu_CsGoronsRuby_UpdateFaceTextures(DemoDu* this, GlobalContext* globalCtx) { + u16* frames = &globalCtx->csCtx.frames; + + if (*frames < 260) { + DemoDu_UpdateEyes(this); + DemoDu_SetMouthTexIndex(this, 0); + } else if (*frames < 335) { + DemoDu_UpdateEyes(this); + DemoDu_SetMouthTexIndex(this, 3); + } else if (*frames < 365) { + DemoDu_SetEyeTexIndex(this, 3); + DemoDu_SetMouthTexIndex(this, 1); + } else if (*frames < 395) { + DemoDu_SetEyeTexIndex(this, 0); + DemoDu_SetMouthTexIndex(this, 3); + } else if (*frames < 410) { + DemoDu_UpdateEyes(this); + DemoDu_SetMouthTexIndex(this, 0); + } else { + DemoDu_UpdateEyes(this); + DemoDu_SetMouthTexIndex(this, 3); + } +} + +void func_8096A630(DemoDu* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f pos = this->actor.world.pos; + + pos.y += kREG(5); + func_80033480(globalCtx, &pos, kREG(1) + 100.0f, kREG(2) + 10, kREG(3) + 300, kREG(4), 0); + DemoDu_CsPlaySfx_GoronLanding(this); +} + +void DemoDu_CsGoronsRuby_SpawnDustWhenHittingLink(DemoDu* this, GlobalContext* globalCtx) { + static Vec3f dustPosOffsets[] = { + { 11.0f, -11.0f, -6.0f }, { 0.0f, 14.0f, -13.0f }, { 14.0f, -2.0f, -10.0f }, { 10.0f, -6.0f, -8.0f }, + { 8.0f, 6.0f, 8.0f }, { 13.0f, 8.0f, -10.0f }, { -14.0f, 1.0f, -14.0f }, { 5.0f, 12.0f, -9.0f }, + { 11.0f, 6.0f, -7.0f }, { 14.0f, 14.0f, -14.0f }, + }; + + if (Animation_OnFrame(&this->skelAnime, 31.0f) || Animation_OnFrame(&this->skelAnime, 41.0f)) { + s32 pad[2]; + s32 i; + Player* player = GET_PLAYER(globalCtx); + Vec3f* headPos = &player->bodyPartsPos[PLAYER_LIMB_HEAD]; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + s32 pad2; + + for (i = 4; i >= 0; --i) { + Color_RGBA8 primColor = { 190, 150, 110, 255 }; + Color_RGBA8 envColor = { 120, 80, 40, 255 }; + s32 colorDelta; + Vec3f position; + + if (Animation_OnFrame(&this->skelAnime, 31.0f)) { + position.x = dustPosOffsets[i + 5].x + headPos->x; + position.y = dustPosOffsets[i + 5].y + headPos->y; + position.z = dustPosOffsets[i + 5].z + headPos->z; + } else { + position.x = dustPosOffsets[i + 0].x + headPos->x; + position.y = dustPosOffsets[i + 0].y + headPos->y; + position.z = dustPosOffsets[i + 0].z + headPos->z; + } + + colorDelta = Rand_ZeroOne() * 20.0f - 10.0f; + + primColor.r += colorDelta; + primColor.g += colorDelta; + primColor.b += colorDelta; + envColor.r += colorDelta; + envColor.g += colorDelta; + envColor.b += colorDelta; + + func_8002829C(globalCtx, &position, &velocity, &accel, &primColor, &envColor, + Rand_ZeroOne() * 40.0f + 200.0f, 0); + } + + DemoDu_CsPlaySfx_DaruniaHitsLink(globalCtx); + } +} + +void DemoDu_CsGoronsRuby_DaruniaFalling(DemoDu* this, GlobalContext* globalCtx) { + s32 pad; + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = csCtx->npcActions[2]; + Vec3f startPos; + Vec3f endPos; + Vec3f* pos = &this->actor.world.pos; + + if (npcAction != NULL) { + f32 traveledPercent = Environment_LerpWeight(npcAction->endFrame, npcAction->startFrame, csCtx->frames); + + startPos.x = npcAction->startPos.x; + startPos.y = npcAction->startPos.y; + startPos.z = npcAction->startPos.z; + + endPos.x = npcAction->endPos.x; + endPos.y = npcAction->endPos.y; + endPos.z = npcAction->endPos.z; + + pos->x = ((endPos.x - startPos.x) * traveledPercent) + startPos.x; + pos->y = ((endPos.y - startPos.y) * traveledPercent) + startPos.y; + pos->z = ((endPos.z - startPos.z) * traveledPercent) + startPos.z; + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo01(DemoDu* this, GlobalContext* globalCtx) { + this->updateIndex = CS_GORONSRUBY_SUBSCENE(1); +} + +void DemoDu_CsGoronsRuby_AdvanceTo02(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 1)) { + Animation_Change(&this->skelAnime, &gDaruniaStandUpAfterFallingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaStandUpAfterFallingAnim), 2, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(2); + this->drawIndex = 1; + DemoDu_CsGoronsRuby_DaruniaFalling(this, globalCtx); + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo03(DemoDu* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = csCtx->npcActions[2]; + + if ((npcAction != NULL) && (csCtx->frames >= npcAction->endFrame)) { + this->updateIndex = CS_GORONSRUBY_SUBSCENE(3); + func_8096A630(this, globalCtx); + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo04(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 2)) { + this->updateIndex = CS_GORONSRUBY_SUBSCENE(4); + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo05(DemoDu* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gDaruniaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gDaruniaIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(5); + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo06(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 3)) { + Animation_Change(&this->skelAnime, &gDaruniaHitBreastAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaHitBreastAnim), 2, -4.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(6); + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo07(DemoDu* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gDaruniaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gDaruniaIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(7); + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo08(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 4)) { + Animation_Change(&this->skelAnime, &gDaruniaHitLinkAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaHitLinkAnim), 2, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(8); + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo09(DemoDu* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gDaruniaHitBreastAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaHitBreastAnim), 2, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(9); + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo10(DemoDu* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gDaruniaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gDaruniaIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(10); + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo11(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 5)) { + Animation_Change(&this->skelAnime, &gDaruniaItemGiveAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaItemGiveAnim), 2, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(11); + } + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo12(DemoDu* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gDaruniaItemGiveIdleAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gDaruniaItemGiveIdleAnim), 0, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(12); + } +} + +void DemoDu_CsGoronsRuby_AdvanceTo13(DemoDu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[2]; + + if ((npcAction != NULL) && (npcAction->action != 6)) { + Animation_Change(&this->skelAnime, &gDaruniaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gDaruniaIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->updateIndex = CS_GORONSRUBY_SUBSCENE(13); + } + } +} + +void DemoDu_UpdateCs_GR_00(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsPlaySfx_DaruniaFalling(globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo01(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_01(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsPlaySfx_DaruniaFalling(globalCtx); + DemoDu_CsPlaySfx_LinkSurprised(globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo02(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_02(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsGoronsRuby_DaruniaFalling(this, globalCtx); + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_CsPlaySfx_DaruniaFalling(globalCtx); + DemoDu_CsPlaySfx_LinkSurprised(globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo03(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_03(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_CsPlaySfx_LinkSurprised(globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo04(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_04(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo05(this, animFinished); +} + +void DemoDu_UpdateCs_GR_05(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo06(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_06(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_CsPlaySfx_HitBreast(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo07(this, animFinished); +} + +void DemoDu_UpdateCs_GR_07(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo08(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_08(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_SpawnDustWhenHittingLink(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo09(this, animFinished); +} + +void DemoDu_UpdateCs_GR_09(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_CsPlaySfx_HitBreast(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo10(this, animFinished); +} + +void DemoDu_UpdateCs_GR_10(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo11(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_11(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo12(this, animFinished); +} + +void DemoDu_UpdateCs_GR_12(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsGoronsRuby_AdvanceTo13(this, globalCtx); +} + +void DemoDu_UpdateCs_GR_13(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_CsGoronsRuby_UpdateFaceTextures(this, globalCtx); + DemoDu_CsPlaySfx_LinkEscapeFromGorons(globalCtx); +} + +void DemoDu_InitCs_AfterGanon(DemoDu* this, GlobalContext* globalCtx) { + s32 pad[3]; + f32 lastFrame = Animation_GetLastFrame(&gDaruniaSageFormationAnim); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDaruniaSkel, NULL, NULL, NULL, 0); + Animation_Change(&this->skelAnime, &gDaruniaSageFormationAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + this->updateIndex = CS_CHAMBERAFTERGANON_SUBSCENE(0); + this->actor.shape.shadowAlpha = 0; +} + +void DemoDu_CsPlaySfx_WhiteOut() { + func_800788CC(NA_SE_SY_WHITE_OUT_T); +} + +void DemoDu_CsAfterGanon_SpawnDemo6K(DemoDu* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + kREG(16) + 22.0f + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 3); +} + +void DemoDu_CsAfterGanon_AdvanceTo01(DemoDu* this, GlobalContext* globalCtx) { + if (DemoDu_IsNpcDoingThisAction(this, globalCtx, 4, 2)) { + this->updateIndex = CS_CHAMBERAFTERGANON_SUBSCENE(1); + this->drawIndex = 2; + this->shadowAlpha = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_1A4 = 0.0f; + DemoDu_CsPlaySfx_WhiteOut(); + } +} + +void DemoDu_CsAfterGanon_AdvanceTo02(DemoDu* this, GlobalContext* globalCtx) { + f32* unk_1A4 = &this->unk_1A4; + s32 shadowAlpha = 255; + + if (DemoDu_IsNpcDoingThisAction(this, globalCtx, 4, 2)) { + *unk_1A4 += 1.0f; + if (*unk_1A4 >= kREG(5) + 10.0f) { + this->updateIndex = CS_CHAMBERAFTERGANON_SUBSCENE(2); + this->drawIndex = 1; + *unk_1A4 = kREG(5) + 10.0f; + this->shadowAlpha = shadowAlpha; + this->actor.shape.shadowAlpha = shadowAlpha; + return; + } + } else { + *unk_1A4 -= 1.0f; + if (*unk_1A4 <= 0.0f) { + this->updateIndex = CS_CHAMBERAFTERGANON_SUBSCENE(0); + this->drawIndex = 0; + *unk_1A4 = 0.0f; + this->shadowAlpha = 0; + this->actor.shape.shadowAlpha = 0; + return; + } + } + shadowAlpha = (*unk_1A4 / (kREG(5) + 10.0f)) * 255.0f; + this->shadowAlpha = shadowAlpha; + this->actor.shape.shadowAlpha = shadowAlpha; +} + +void DemoDu_CsAfterGanon_BackTo01(DemoDu* this, GlobalContext* globalCtx) { + if (DemoDu_IsNpcNotDoingThisAction(this, globalCtx, 4, 2)) { + this->updateIndex = CS_CHAMBERAFTERGANON_SUBSCENE(1); + this->drawIndex = 2; + this->unk_1A4 = kREG(5) + 10.0f; + this->shadowAlpha = 255; + if (!this->demo6KSpawned) { + DemoDu_CsAfterGanon_SpawnDemo6K(this, globalCtx); + this->demo6KSpawned = 1; + } + this->actor.shape.shadowAlpha = 255; + } +} + +void DemoDu_UpdateCs_AG_00(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsAfterGanon_AdvanceTo01(this, globalCtx); + DemoDu_CsAfterGanon_CheckIfShouldReset(this, globalCtx); +} + +void DemoDu_UpdateCs_AG_01(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_UpdateEyes(this); + DemoDu_CsAfterGanon_AdvanceTo02(this, globalCtx); + DemoDu_CsAfterGanon_CheckIfShouldReset(this, globalCtx); +} + +void DemoDu_UpdateCs_AG_02(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_UpdateEyes(this); + DemoDu_CsAfterGanon_BackTo01(this, globalCtx); + DemoDu_CsAfterGanon_CheckIfShouldReset(this, globalCtx); +} + +// Similar to DemoDu_Draw_01, but this uses POLY_XLU_DISP. Also uses this->shadowAlpha for setting the env color. +void DemoDu_Draw_02(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + DemoDu* this = (DemoDu*)thisx; + s16 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = sEyeTextures[eyeTexIndex]; + s32 pad; + s16 mouthTexIndex = this->mouthTexIndex; + void* mouthTexture = sMouthTextures[mouthTexIndex]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_du_inKenjyanomaDemo02.c", 275); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTexture)); + gSPSegment(POLY_XLU_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gDaruniaNoseSeriousTex)); + + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->shadowAlpha); + + gSPSegment(POLY_XLU_DISP++, 0x0C, &D_80116280[0]); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, 0, + 0, 0, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_du_inKenjyanomaDemo02.c", 304); +} + +void DemoDu_InitCs_Credits(DemoDu* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDaruniaSkel, &gDaruniaCreditsIdleAnim, NULL, NULL, 0); + this->updateIndex = CS_CREDITS_SUBSCENE(0); + this->drawIndex = 0; + this->actor.shape.shadowAlpha = 0; + DemoDu_SetMouthTexIndex(this, 3); +} + +void DemoDu_CsCredits_UpdateShadowAlpha(DemoDu* this) { + s32 shadowAlpha = 255; + f32 temp_f0; + f32* unk_1A4; + + this->unk_1A4 += 1.0f; + temp_f0 = kREG(17) + 10.0f; + unk_1A4 = &this->unk_1A4; + + if (temp_f0 <= *unk_1A4) { + this->shadowAlpha = shadowAlpha; + this->actor.shape.shadowAlpha = shadowAlpha; + } else { + shadowAlpha = *unk_1A4 / temp_f0 * 255.0f; + this->shadowAlpha = shadowAlpha; + this->actor.shape.shadowAlpha = shadowAlpha; + } +} + +void DemoDu_CsCredits_AdvanceTo01(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_MoveToNpcPos(this, globalCtx, 2); + this->updateIndex = CS_CREDITS_SUBSCENE(1); + this->drawIndex = 2; +} + +void DemoDu_CsCredits_AdvanceTo02(DemoDu* this) { + if (this->unk_1A4 >= kREG(17) + 10.0f) { + this->updateIndex = CS_CREDITS_SUBSCENE(2); + this->drawIndex = 1; + } +} + +void DemoDu_CsCredits_AdvanceTo03(DemoDu* this) { + func_80969DDC(this, &gDaruniaLookingUpToSariaAnim, ANIMMODE_ONCE, -8.0f, 0); + this->updateIndex = CS_CREDITS_SUBSCENE(3); +} + +void DemoDu_CsCredits_AdvanceTo04(DemoDu* this) { + func_80969DDC(this, &gDaruniaCreditsHitBreastAnim, ANIMMODE_ONCE, 0.0f, 0); + this->updateIndex = CS_CREDITS_SUBSCENE(4); +} + +void DemoDu_CsCredits_BackTo02(DemoDu* this, s32 animFinished) { + if (animFinished) { + func_80969DDC(this, &gDaruniaCreditsIdleAnim, ANIMMODE_LOOP, 0.0f, 0); + this->updateIndex = CS_CREDITS_SUBSCENE(2); + } +} + +void DemoDu_CsCredits_HandleSubscenesByNpcAction(DemoDu* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = DemoDu_GetNpcAction(globalCtx, 2); + + if (npcAction != NULL) { + s32 action = npcAction->action; + s32 lastAction = this->lastAction; + + if (action != lastAction) { + switch (action) { + case 9: + DemoDu_CsCredits_AdvanceTo01(this, globalCtx); + break; + case 10: + DemoDu_CsCredits_AdvanceTo03(this); + break; + case 11: + DemoDu_CsCredits_AdvanceTo04(this); + break; + default: + // "Demo_Du_inEnding_Check_DemoMode:There is no such operation!!!!!!!!" + osSyncPrintf("Demo_Du_inEnding_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->lastAction = action; + } + } +} + +void DemoDu_UpdateCs_CR_00(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_CsCredits_HandleSubscenesByNpcAction(this, globalCtx); +} + +void DemoDu_UpdateCs_CR_01(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_UpdateEyes(this); + DemoDu_CsCredits_UpdateShadowAlpha(this); + DemoDu_CsCredits_AdvanceTo02(this); +} + +void DemoDu_UpdateCs_CR_02(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_UpdateEyes(this); + DemoDu_CsCredits_HandleSubscenesByNpcAction(this, globalCtx); +} + +void DemoDu_UpdateCs_CR_03(DemoDu* this, GlobalContext* globalCtx) { + DemoDu_UpdateBgCheckInfo(this, globalCtx); + DemoDu_UpdateSkelAnime(this); + DemoDu_UpdateEyes(this); + DemoDu_CsCredits_HandleSubscenesByNpcAction(this, globalCtx); +} + +void DemoDu_UpdateCs_CR_04(DemoDu* this, GlobalContext* globalCtx) { + s32 animFinished; + + DemoDu_UpdateBgCheckInfo(this, globalCtx); + animFinished = DemoDu_UpdateSkelAnime(this); + DemoDu_UpdateEyes(this); + DemoDu_CsCredits_BackTo02(this, animFinished); +} + +static DemoDuActionFunc sUpdateFuncs[] = { + DemoDu_UpdateCs_FM_00, DemoDu_UpdateCs_FM_01, DemoDu_UpdateCs_FM_02, DemoDu_UpdateCs_FM_03, DemoDu_UpdateCs_FM_04, + DemoDu_UpdateCs_FM_05, DemoDu_UpdateCs_FM_06, DemoDu_UpdateCs_GR_00, DemoDu_UpdateCs_GR_01, DemoDu_UpdateCs_GR_02, + DemoDu_UpdateCs_GR_03, DemoDu_UpdateCs_GR_04, DemoDu_UpdateCs_GR_05, DemoDu_UpdateCs_GR_06, DemoDu_UpdateCs_GR_07, + DemoDu_UpdateCs_GR_08, DemoDu_UpdateCs_GR_09, DemoDu_UpdateCs_GR_10, DemoDu_UpdateCs_GR_11, DemoDu_UpdateCs_GR_12, + DemoDu_UpdateCs_GR_13, DemoDu_UpdateCs_AG_00, DemoDu_UpdateCs_AG_01, DemoDu_UpdateCs_AG_02, DemoDu_UpdateCs_CR_00, + DemoDu_UpdateCs_CR_01, DemoDu_UpdateCs_CR_02, DemoDu_UpdateCs_CR_03, DemoDu_UpdateCs_CR_04, +}; + +void DemoDu_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoDu* this = (DemoDu*)thisx; + + if (this->updateIndex < 0 || this->updateIndex >= 29 || sUpdateFuncs[this->updateIndex] == NULL) { + // "The main mode is abnormal!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sUpdateFuncs[this->updateIndex](this, globalCtx); +} + +void DemoDu_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoDu* this = (DemoDu*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + switch (this->actor.params) { + case DEMO_DU_CS_GORONS_RUBY: + DemoDu_InitCs_GoronsRuby(this, globalCtx); + break; + + case DEMO_DU_CS_CHAMBER_AFTER_GANON: + DemoDu_InitCs_AfterGanon(this, globalCtx); + break; + + case DEMO_DU_CS_CREDITS: + DemoDu_InitCs_Credits(this, globalCtx); + break; + + default: + DemoDu_InitCs_FireMedallion(this, globalCtx); + break; + } +} + +void DemoDu_Draw_NoDraw(Actor* thisx, GlobalContext* globalCtx2) { +} + +// Similar to DemoDu_Draw_02, but this uses POLY_OPA_DISP. Sets the env color to 255. +void DemoDu_Draw_01(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + DemoDu* this = (DemoDu*)thisx; + s16 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = sEyeTextures[eyeTexIndex]; + s32 pad; + s16 mouthTexIndex = this->mouthTexIndex; + void* mouthTexture = sMouthTextures[mouthTexIndex]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_du.c", 615); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTexture)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gDaruniaNoseSeriousTex)); + + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, NULL, NULL, + this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_du.c", 638); +} + +static DemoDuDrawFunc sDrawFuncs[] = { + DemoDu_Draw_NoDraw, + DemoDu_Draw_01, + DemoDu_Draw_02, +}; + +void DemoDu_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoDu* this = (DemoDu*)thisx; + + if (this->drawIndex < 0 || this->drawIndex >= 3 || sDrawFuncs[this->drawIndex] == NULL) { + // "The drawing mode is abnormal!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawIndex](thisx, globalCtx); +} + +const ActorInit Demo_Du_InitVars = { + ACTOR_DEMO_DU, + ACTORCAT_NPC, + FLAGS, + OBJECT_DU, + sizeof(DemoDu), + (ActorFunc)DemoDu_Init, + (ActorFunc)DemoDu_Destroy, + (ActorFunc)DemoDu_Update, + (ActorFunc)DemoDu_Draw, + (ActorResetFunc)DemoDu_Reset, +}; + +void DemoDu_Reset(void) { + D_8096CE94 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du.h b/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du.h new file mode 100644 index 000000000..12e509ff7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du.h @@ -0,0 +1,41 @@ +#ifndef Z_DEMO_DU_H +#define Z_DEMO_DU_H + +#include "ultra64.h" +#include "global.h" + +struct DemoDu; + +typedef struct DemoDu { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ s16 eyeTexIndex; + /* 0x0192 */ s16 blinkTimer; + /* 0x0194 */ s16 mouthTexIndex; + /* 0x0198 */ s32 updateIndex; + /* 0x019C */ s32 drawIndex; + /* 0x01A0 */ s32 unused; + /* 0x01A4 */ f32 unk_1A4; + /* 0x01A8 */ s32 shadowAlpha; + /* 0x01AC */ s32 demo6KSpawned; + /* 0x01B0 */ s32 lastAction; +} DemoDu; // size = 0x01B4 + +// This is the parameter of this actor, +typedef enum DemoDu_Cutscene { + /* 0x00 */ DEMO_DU_CS_FIREMEDALLION, // default + /* 0x01 */ DEMO_DU_CS_GORONS_RUBY, + /* 0x02 */ DEMO_DU_CS_CHAMBER_AFTER_GANON, + /* 0x03 */ DEMO_DU_CS_CREDITS +} DemoDu_Cutscene; + +/** + * To see each one of the cutscenes on the debug rom: + * DEMO_DU_CS_FIREMEDALLION: I couldn't find how to trigger this one from the map selector, but you can go to Map 78 and beat Volvagia (or use the gameshark 8015E98B 0002 so the game thinks you already has beaten it). + * DEMO_DU_CS_GORONS_RUBY: Map selector -> Map 16 (SPOT16) -> Stage 01 + * DEMO_DU_CS_CHAMBER_AFTER_GANON: Map selector -> Map 21 -> Stage 02 + * DEMO_DU_CS_CREDITS: Map selector -> Map 16 (SPOT16) -> Stage 04 + * + */ + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du_cutscene_data.c b/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du_cutscene_data.c new file mode 100644 index 000000000..a937b655b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Du/z_demo_du_cutscene_data.c @@ -0,0 +1,187 @@ +#include "z_demo_du.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_8096C1A4[] = { + CS_BEGIN_CUTSCENE(31, 3000), + CS_UNK_DATA_LIST(0x00000020, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(31, 5), + CS_NPC_ACTION(0x0001, 0, 546, 0x0000, 0x0000, 0x0000, 0, 216, -10, 0, 216, -10, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 546, 547, 0x0000, 0x0000, 0x0000, 0, 216, -10, 0, 216, -10, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 547, 616, 0x0000, 0x0000, 0x0000, 0, 216, -10, 0, 216, -10, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 616, 667, 0x0000, 0x0000, 0x0000, 0, 216, -10, 0, 82, 0, 0.0f, -2.627451f, 0.0f), + CS_NPC_ACTION(0x0003, 667, 2834, 0x0000, 0x0000, 0x0000, 0, 82, 0, 0, 82, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x000D, 0, 280, 0x0000, 0x6AAA, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0005, 280, 531, 0x0000, 0x6AAA, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0013, 531, 1716, 0x0000, 0xEAAA, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_NPC_ACTION_LIST(41, 3), + CS_NPC_ACTION(0x0001, 0, 170, 0x0000, 0x0000, 0x0000, 98, 6, -169, 98, 6, -169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 170, 465, 0x0000, 0x0000, 0x0000, 98, 6, -169, 98, 6, -169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 465, 2915, 0x0000, 0x0000, 0x0000, 98, 6, -169, 98, 6, -169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(49, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, -16, -121, 0, -16, -121, 0.0f, 0.0f, 0.0f), + CS_SCENE_TRANS_FX(0x0001, 530, 539), + CS_SCENE_TRANS_FX(0x0005, 540, 570), + CS_LIGHTING_LIST(2), + CS_LIGHTING(0x0001, 0, 10, 0x0000, 0x00000000, 0xFFFFFFDC, 0x00000000, 0x00000018, 0xFFFFFFDC, 0x00000000, 0x00000018), + CS_LIGHTING(0x0001, 10, 3000, 0x0000, 0x00000000, 0xFFFFFFDC, 0x00000000, 0x00000018, 0xFFFFFFDC, 0x00000000, 0x00000018), + CS_NPC_ACTION_LIST(39, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, 0, -2, 0, 0, -2, 0.0f, 0.0f, 0.0f), + CS_SCENE_TRANS_FX(0x0001, 805, 835), + CS_NPC_ACTION_LIST(62, 1), + CS_NPC_ACTION(0x0004, 0, 3000, 0x0000, 0x0000, 0x0000, 32, 80, -51, 32, 80, -51, 0.0f, 0.0f, 0.0f), + CS_TERMINATOR(DEATH_MOUNTAIN_CRATER_AFTER_FIRE_BLUE_WARP, 905, 1030), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0044, 615, 616, 0x0000, 0x00000000, 0xFFFFFF97, 0x00000000, 0x00000030, 0xFFFFFF97, 0x00000000, 0x00000030), + CS_FADE_BGM_LIST(1), + CS_FADE_BGM(0x0004, 500, 550, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC3, 0x0000006E, 0x00000000, 0xFFFFFFC3, 0x0000006E), + CS_TEXT_LIST(10), + CS_TEXT_NONE(0, 310), + CS_TEXT_DISPLAY_TEXTBOX(0x303C, 310, 323, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(323, 344), + CS_TEXT_DISPLAY_TEXTBOX(0x3045, 344, 394, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(394, 415), + CS_TEXT_DISPLAY_TEXTBOX(0x3046, 415, 465, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(465, 800), + CS_TEXT_DISPLAY_TEXTBOX(0x003C, 800, 805, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(805, 865), + CS_TEXT_DISPLAY_TEXTBOX(0x303D, 865, 875, 0x0000, 0x0000, 0x0000), + CS_CAM_EYE_LIST(0, 1361), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, -85, 3211, 795, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, -85, 3211, 795, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, -85, 2925, 795, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, 70, 974, 497, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, 320, 268, 296, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, 312, 190, 150, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, 261, 61, -65, 0x013F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, 261, 61, -65, 0x014E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4f, 261, 61, -65, 0x015F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.4f, 261, 61, -65, 0x0161), + CS_CAM_EYE_LIST(263, 509), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 29.399885f, 89, 30, -103, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.2f, 89, 30, -103, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.2f, 89, 30, -103, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.2f, 89, 30, -103, 0x00E8), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.2f, 89, 30, -103, 0x00EA), + CS_CAM_EYE_LIST(333, 1424), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.999947f, 114, 50, -116, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.999947f, 114, 50, -116, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.999947f, 114, 50, -116, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.999947f, 114, 50, -116, 0x00E8), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.999947f, 114, 50, -116, 0x00EA), + CS_CAM_EYE_LIST(403, 1494), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 26, 45, -10, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 26, 45, -10, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 26, 45, -10, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 26, 45, -10, 0x00E8), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.599945f, 26, 45, -10, 0x00EA), + CS_CAM_EYE_LIST(443, 1624), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 11, 23, -17, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 11, 23, -17, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 27, 31, -45, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 45, 40, -76, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 45, 40, -76, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 45, 40, -76, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 45, 40, -76, 0x013F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, 45, 40, -76, 0x002E), + CS_CAM_EYE_LIST(473, 1604), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, 192, 29, -246, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, 192, 29, -246, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, 192, 29, -246, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, 192, 29, -246, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.39992f, 192, 278, -246, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.39992f, 192, 278, -246, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.39992f, 192, 278, -246, 0x013F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.39992f, 192, 278, -246, 0x002E), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 70.39992f, 192, 278, -246, 0x0063), + CS_CAM_EYE_LIST(539, 881), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 13, 854, 2, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 853, 5, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -3, 853, 5, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 853, -6, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -2, 852, -17, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, -17, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 16, 852, -6, 0x013F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, 5, 0x002E), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -3, 851, 5, 0x0063), + CS_CAM_EYE_REL_TO_PLAYER_LIST(615, 1796), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x00C6), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x00C8), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 68, -26, 0x00D7), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x00E8), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x00EA), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x013D), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x013F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 68.599945f, 0, 103, -26, 0x002E), + CS_CAM_AT_LIST(0, 1390), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 60, 60.4f, -115, 3163, 585, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 60, 60.4f, -115, 3163, 585, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 60, 60.4f, -115, 2877, 585, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4f, 27, 824, 348, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.4f, 197, 143, 174, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.4f, 147, 100, 55, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.4f, 55, 35, -65, 0x013F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 60.4f, 55, 35, -65, 0x014E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4f, 55, 35, -65, 0x015F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.4f, 56, 35, -65, 0x0161), + CS_CAM_AT_LIST(263, 538), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 78.99979f, 87, 157, -391, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 82, 78.79979f, 87, 157, -391, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 103, 70.79991f, 87, 157, -391, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.2f, 87, 157, -391, 0x00E8), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.2f, 87, 157, -391, 0x00EA), + CS_CAM_AT_LIST(333, 1453), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.999947f, -52, 127, -309, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.999947f, -52, 127, -309, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.999947f, -52, 127, -309, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.999947f, -52, 127, -309, 0x00E8), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.999947f, -52, 127, -309, 0x00EA), + CS_CAM_AT_LIST(403, 1523), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -269, 186, 13, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -269, 186, 13, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.599945f, -269, 186, 13, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -269, 186, 13, 0x00E8), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.599945f, -269, 186, 13, 0x00EA), + CS_CAM_AT_LIST(443, 1653), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 163, 70, -283, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 163, 70, -283, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 177, 74, -309, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 193, 92, -337, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 193, 92, -337, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.399944f, 192, 91, -336, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 192, 91, -336, 0x013F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, 192, 91, -336, 0x002E), + CS_CAM_AT_LIST(473, 1633), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 45.199944f, -9, 57, -53, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 45.199944f, -9, 57, -53, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 45.199944f, -9, 57, -54, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 45.999947f, -8, 57, -54, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 70.39992f, 149, 547, -205, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 70.39992f, 149, 547, -205, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 70.39992f, 149, 547, -205, 0x013F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.39992f, 149, 547, -205, 0x002E), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 70.39992f, 149, 547, -205, 0x0063), + CS_CAM_AT_LIST(539, 930), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.0f, 3, 6, -6, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.0f, 3, 6, -6, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.999966f, 3, 6, -6, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 20.59985f, 3, 6, -6, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 51, 10.799838f, 3, 6, -6, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x013F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.199839f, 3, 6, -6, 0x002E), + CS_CAM_AT(CS_CMD_STOP, 0x00, 50, 10.999838f, 3, 6, -6, 0x0063), + CS_CAM_AT_REL_TO_PLAYER_LIST(615, 1825), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 100, 5, 0x00C6), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 101, 6, 0x00C8), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 1, 99, 41, 0x00D7), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x00E8), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x00EA), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 68.599945f, 0, 42, 16, 0x013D), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x013F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 68.599945f, 0, 42, 16, 0x002E), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Ec/z_demo_ec.c b/soh/src/overlays/actors/ovl_Demo_Ec/z_demo_ec.c new file mode 100644 index 000000000..1bd572366 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Ec/z_demo_ec.c @@ -0,0 +1,1374 @@ +/* + * File: z_demo_ec.c + * Overlay: ovl_Demo_Ec + * Description: Credits revelers in Lon Lon + */ + +#include "z_demo_ec.h" +#include "vt.h" +#include "objects/object_zo/object_zo.h" +#include "objects/object_ec/object_ec.h" +#include "objects/object_ma2/object_ma2.h" +#include "objects/object_in/object_in.h" +#include "objects/object_ge1/object_ge1.h" +#include "objects/object_fu/object_fu.h" +#include "objects/object_fish/object_fish.h" +#include "objects/object_ta/object_ta.h" +#include "objects/object_oF1d_map/object_oF1d_map.h" +#include "objects/object_ma2/object_ma2.h" +#include "objects/object_in/object_in.h" +#include "objects/object_ta/object_ta.h" +#include "objects/object_fu/object_fu.h" +#include "objects/object_toryo/object_toryo.h" +#include "objects/object_daiku/object_daiku.h" +#include "objects/object_ge1/object_ge1.h" +#include "objects/object_kz/object_kz.h" +#include "objects/object_md/object_md.h" +#include "objects/object_niw/object_niw.h" +#include "objects/object_ds2/object_ds2.h" +#include "objects/object_os/object_os.h" +#include "objects/object_rs/object_rs.h" +#include "objects/object_gm/object_gm.h" +#include "objects/object_km1/object_km1.h" +#include "objects/object_kw1/object_kw1.h" +#include "objects/object_bji/object_bji.h" +#include "objects/object_ahg/object_ahg.h" +#include "objects/object_bob/object_bob.h" +#include "objects/object_bba/object_bba.h" +#include "objects/object_ane/object_ane.h" + +#define FLAGS ACTOR_FLAG_4 + +void DemoEc_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoEc_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoEc_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoEc_Draw(Actor* thisx, GlobalContext* globalCtx); + +typedef enum { + /* 00 */ EC_UPDATE_COMMON, + /* 01 */ EC_UPDATE_INGO, + /* 02 */ EC_UPDATE_TALON, + /* 03 */ EC_UPDATE_WINDMILL_MAN, + /* 04 */ EC_UPDATE_KOKIRI_BOY, + /* 05 */ EC_UPDATE_KOKIRI_GIRL, + /* 06 */ EC_UPDATE_OLD_MAN, + /* 07 */ EC_UPDATE_BEARDED_MAN, + /* 08 */ EC_UPDATE_WOMAN, + /* 09 */ EC_UPDATE_OLD_WOMAN, + /* 10 */ EC_UPDATE_BOSS_CARPENTER, + /* 11 */ EC_UPDATE_CARPENTER, + /* 12 */ EC_UPDATE_DANCING_KOKIRI_BOY, + /* 13 */ EC_UPDATE_DANCING_KOKIRI_GIRL, + /* 14 */ EC_UPDATE_GERUDO, + /* 15 */ EC_UPDATE_DANCING_ZORA, + /* 16 */ EC_UPDATE_KING_ZORA, + /* 17 */ EC_UPDATE_17, + /* 18 */ EC_UPDATE_18, + /* 19 */ EC_UPDATE_MIDO, + /* 20 */ EC_UPDATE_20, + /* 21 */ EC_UPDATE_CUCCO, + /* 22 */ EC_UPDATE_CUCCO_LADY, + /* 23 */ EC_UPDATE_POTION_SHOP_OWNER, + /* 24 */ EC_UPDATE_MASK_SHOP_OWNER, + /* 25 */ EC_UPDATE_FISHING_MAN, + /* 26 */ EC_UPDATE_BOMBCHU_SHOP_OWNER, + /* 27 */ EC_UPDATE_GORON, + /* 28 */ EC_UPDATE_MALON +} DemoEcUpdateMode; + +typedef enum { + /* 00 */ EC_DRAW_COMMON, + /* 01 */ EC_DRAW_INGO, + /* 02 */ EC_DRAW_TALON, + /* 03 */ EC_DRAW_WINDMILL_MAN, + /* 04 */ EC_DRAW_KOKIRI_BOY, + /* 05 */ EC_DRAW_KOKIRI_GIRL, + /* 06 */ EC_DRAW_OLD_MAN, + /* 07 */ EC_DRAW_BEARDED_MAN, + /* 08 */ EC_DRAW_WOMAN, + /* 09 */ EC_DRAW_OLD_WOMAN, + /* 10 */ EC_DRAW_BOSS_CARPENTER, + /* 11 */ EC_DRAW_CARPENTER, + /* 12 */ EC_DRAW_GERUDO, + /* 13 */ EC_DRAW_DANCING_ZORA, + /* 14 */ EC_DRAW_KING_ZORA, + /* 15 */ EC_DRAW_MIDO, + /* 16 */ EC_DRAW_CUCCO, + /* 17 */ EC_DRAW_CUCCO_LADY, + /* 18 */ EC_DRAW_POTION_SHOP_OWNER, + /* 19 */ EC_DRAW_MASK_SHOP_OWNER, + /* 20 */ EC_DRAW_FISHING_MAN, + /* 21 */ EC_DRAW_BOMBCHU_SHOP_OWNER, + /* 22 */ EC_DRAW_GORON, + /* 23 */ EC_DRAW_MALON +} DemoEcDrawconfig; + +static s16 sDrawObjects[] = { + /* 0 */ OBJECT_IN, + /* 1 */ OBJECT_TA, + /* 2 */ OBJECT_FU, + /* 3 */ OBJECT_KM1, + /* 4 */ OBJECT_KW1, + /* 5 */ OBJECT_BJI, + /* 6 */ OBJECT_AHG, + /* 7 */ OBJECT_BOB, + /* 8 */ OBJECT_BBA, + /* 9 */ OBJECT_TORYO, + /* 10 */ OBJECT_DAIKU, + /* 11 */ OBJECT_DAIKU, + /* 12 */ OBJECT_DAIKU, + /* 13 */ OBJECT_DAIKU, + /* 14 */ OBJECT_KM1, + /* 15 */ OBJECT_KW1, + /* 16 */ OBJECT_GE1, + /* 17 */ OBJECT_GE1, + /* 18 */ OBJECT_GE1, + /* 19 */ OBJECT_ZO, + /* 20 */ OBJECT_KZ, + /* 21 */ OBJECT_MD, + /* 22 */ OBJECT_NIW, + /* 23 */ OBJECT_NIW, + /* 24 */ OBJECT_NIW, + /* 25 */ OBJECT_ANE, + /* 26 */ OBJECT_DS2, + /* 27 */ OBJECT_OS, + /* 28 */ OBJECT_FISH, + /* 29 */ OBJECT_RS, + /* 30 */ OBJECT_OF1D_MAP, + /* 31 */ OBJECT_OF1D_MAP, + /* 32 */ OBJECT_OF1D_MAP, + /* 33 */ OBJECT_OF1D_MAP, + /* 34 */ OBJECT_MA2, +}; + +static s16 sAnimationObjects[] = { + OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, + OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, + OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, + OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_EC, OBJECT_GM, OBJECT_MA2, +}; + +void DemoEc_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoEc* this = (DemoEc*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void DemoEc_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoEc* this = (DemoEc*)thisx; + + if ((this->actor.params < 0) || (this->actor.params > 34)) { + osSyncPrintf(VT_FGCOL(RED) "Demo_Ec_Actor_ct:arg_dataがおかしい!!!!!!!!!!!!\n" VT_RST); + Actor_Kill(&this->actor); + } else { + this->updateMode = EC_UPDATE_COMMON; + this->drawConfig = EC_DRAW_COMMON; + } +} + +s32 DemoEc_UpdateSkelAnime(DemoEc* this) { + return SkelAnime_Update(&this->skelAnime); +} + +void DemoEc_UpdateBgFlags(DemoEc* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 25.0f, 30.0f, 7); +} + +void func_8096D594(DemoEc* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_8096D5D4(DemoEc* this, GlobalContext* globalCtx) { + this->skelAnime.baseTransl = this->skelAnime.jointTable[0]; + this->skelAnime.prevTransl = this->skelAnime.jointTable[0]; + this->skelAnime.moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_8096D64C(DemoEc* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void DemoEc_UpdateEyes(DemoEc* this) { + s32 pad[3]; + s16* blinkTimer = &this->blinkTimer; + s16* eyeTexIndex = &this->eyeTexIndex; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + + *eyeTexIndex = *blinkTimer; + + if (*eyeTexIndex >= 3) { + *eyeTexIndex = 0; + } +} + +void DemoEc_SetEyeTexIndex(DemoEc* this, s16 texIndex) { + this->eyeTexIndex = texIndex; +} + +void DemoEc_InitSkelAnime(DemoEc* this, GlobalContext* globalCtx, FlexSkeletonHeader* skeletonHeader) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, SEGMENTED_TO_VIRTUAL(skeletonHeader), NULL, NULL, NULL, 0); +} + +void DemoEc_ChangeAnimation(DemoEc* this, AnimationHeader* animation, u8 mode, f32 transitionRate, s32 reverse) { + f32 frameCount; + f32 startFrame; + AnimationHeader* anim; + f32 playbackSpeed; + s16 frameCountS; + + anim = SEGMENTED_TO_VIRTUAL(animation); + frameCountS = Animation_GetLastFrame(anim); + + if (!reverse) { + startFrame = 0.0f; + frameCount = frameCountS; + playbackSpeed = 1.0f; + } else { + frameCount = 0.0f; + startFrame = frameCountS; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, anim, playbackSpeed, startFrame, frameCount, mode, transitionRate); +} + +Gfx* DemoEc_AllocColorDList(GraphicsContext* gfxCtx, u8* color) { + Gfx* dList; + + dList = Graph_Alloc(gfxCtx, sizeof(Gfx) * 2); + gDPSetEnvColor(dList, color[0], color[1], color[2], color[3]); + gSPEndDisplayList(dList + 1); + + return dList; +} + +void DemoEc_DrawSkeleton(DemoEc* this, GlobalContext* globalCtx, void* eyeTexture, void* arg3, + OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + SkelAnime* skelAnime = &this->skelAnime; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_demo_ec.c", 565); + + func_80093D18(gfxCtx); + + if (eyeTexture != NULL) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + } + + if (arg3 != NULL) { + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(arg3)); + } + + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + overrideLimbDraw, postLimbDraw, &this->actor, POLY_OPA_DISP); + CLOSE_DISPS(gfxCtx, "../z_demo_ec.c", 595); +} + +void DemoEc_DrawSkeletonCustomColor(DemoEc* this, GlobalContext* globalCtx, Gfx* arg2, Gfx* arg3, u8* color1, + u8* color2, OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw) { + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(gfxCtx, "../z_demo_ec.c", 609); + + func_80093D18(gfxCtx); + + if (arg2 != 0) { + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(arg2)); + } + + if (arg3 != 0) { + gSPSegment(POLY_OPA_DISP++, 0x0B, SEGMENTED_TO_VIRTUAL(arg3)); + } + + if (color1 != NULL) { + //! @bug DemoEc_AllocColorDList is called twice in SEGMENTED_TO_VIRTUAL, allocating two display lists + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(DemoEc_AllocColorDList(gfxCtx, color1))); + } + + if (color2 != NULL) { + //! @bug DemoEc_AllocColorDList is called twice in SEGMENTED_TO_VIRTUAL, allocating two display lists + //! @bug meant to pass color2 instead of color1? + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(DemoEc_AllocColorDList(gfxCtx, color1))); + } + + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + overrideLimbDraw, postLimbDraw, &this->actor, POLY_OPA_DISP); + + CLOSE_DISPS(gfxCtx, "../z_demo_ec.c", 646); +} + +void DemoEc_UseDrawObject(DemoEc* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 drawObjBankIndex = this->drawObjBankIndex; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_demo_ec.c", 662); + + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[drawObjBankIndex].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[drawObjBankIndex].segment); + if (!globalCtx) {} + + CLOSE_DISPS(gfxCtx, "../z_demo_ec.c", 670); +} + +void DemoEc_UseAnimationObject(DemoEc* this, GlobalContext* globalCtx) { + s32 animObjBankIndex = this->animObjBankIndex; + + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[animObjBankIndex].segment); +} + +CsCmdActorAction* DemoEc_GetNpcAction(GlobalContext* globalCtx, s32 actionIndex) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[actionIndex]; + } else { + return NULL; + } +} + +void DemoEc_SetNpcActionPosRot(DemoEc* this, GlobalContext* globalCtx, s32 actionIndex) { + CsCmdActorAction* npcAction = DemoEc_GetNpcAction(globalCtx, actionIndex); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +void DemoEc_InitIngo(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gIngoSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcIngoAnim, 0, 0.0f, false); + func_8096D64C(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_INGO; + this->drawConfig = EC_DRAW_INGO; +} + +void DemoEc_UpdateIngo(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawIngo(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, gIngoEyeClosed2Tex, gIngoRedTex, 0, 0); +} + +void DemoEc_InitTalon(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gTalonSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcTalonAnim, 0, 0.0f, false); + func_8096D64C(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_TALON; + this->drawConfig = EC_DRAW_TALON; +} + +void DemoEc_UpdateTalon(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawTalon(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, gTalonEyeClosed2Tex, gTalonRedTex, NULL, NULL); +} + +void DemoEc_InitWindmillMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gWindmillManSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcWindmillManAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_WINDMILL_MAN; + this->drawConfig = EC_DRAW_WINDMILL_MAN; +} + +void DemoEc_UpdateWindmillMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawWindmillMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, gWindmillManEyeClosedTex, gWindmillManMouthAngryTex, NULL, NULL); +} + +void DemoEc_InitKokiriBoy(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gKm1Skel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcKokiriAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_KOKIRI_BOY; + this->drawConfig = EC_DRAW_KOKIRI_BOY; +} + +void DemoEc_InitDancingKokiriBoy(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gKm1Skel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcDancingKokiriAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_DANCING_KOKIRI_BOY; + this->drawConfig = EC_DRAW_KOKIRI_BOY; +} + +void DemoEc_UpdateKokiriBoy(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_UpdateDancingKokiriBoy(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateKokiriBoy(this, globalCtx); +} + +void DemoEc_DrawKokiriBoy(DemoEc* this, GlobalContext* globalCtx) { + static u8 color1[] = { 0, 130, 70, 255 }; + static u8 color2[] = { 110, 170, 20, 255 }; + + DemoEc_DrawSkeletonCustomColor(this, globalCtx, NULL, NULL, color1, color2, NULL, NULL); +} + +void DemoEc_InitKokiriGirl(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gKw1Skel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcKokiriAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_KOKIRI_GIRL; + this->drawConfig = EC_DRAW_KOKIRI_GIRL; +} + +void DemoEc_InitDancingKokiriGirl(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gKw1Skel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcDancingKokiriAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_DANCING_KOKIRI_GIRL; + this->drawConfig = EC_DRAW_KOKIRI_GIRL; +} + +void DemoEc_UpdateKokiriGirl(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_UpdateDancingKokiriGirl(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateKokiriGirl(this, globalCtx); +} + +void DemoEc_DrawKokiriGirl(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gKw1EyeOpenTex, gKw1EyeHalfTex, gKw1EyeClosedTex }; + static u8 color1[] = { 70, 190, 60, 255 }; + static u8 color2[] = { 100, 30, 0, 255 }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeletonCustomColor(this, globalCtx, eyeTexture, NULL, color1, color2, NULL, NULL); +} +void DemoEc_InitOldMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_bji_Skel_0000F0); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcOldManAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_OLD_MAN; + this->drawConfig = EC_DRAW_OLD_MAN; +} + +void DemoEc_UpdateOldMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawOldMan(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + object_bji_Tex_0005FC, + object_bji_Tex_0009FC, + object_bji_Tex_000DFC, + }; + static u8 color1[] = { 0, 50, 100, 255 }; + static u8 color2[] = { 0, 50, 160, 255 }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeletonCustomColor(this, globalCtx, eyeTexture, NULL, color1, color2, NULL, NULL); +} + +void DemoEc_InitBeardedMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_ahg_Skel_0000F0); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcOldManAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_BEARDED_MAN; + this->drawConfig = EC_DRAW_BEARDED_MAN; +} + +void DemoEc_UpdateBeardedMan(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawBeardedMan(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + object_ahg_Tex_0005FC, + object_ahg_Tex_0006FC, + object_ahg_Tex_0007FC, + }; + static u8 color1[] = { 255, 255, 255, 255 }; + static u8 color2[] = { 255, 255, 255, 255 }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeletonCustomColor(this, globalCtx, eyeTexture, NULL, color1, color2, NULL, NULL); +} + +void DemoEc_InitWoman(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_bob_Skel_0000F0); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcOldManAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_WOMAN; + this->drawConfig = EC_DRAW_WOMAN; +} + +void DemoEc_UpdateWoman(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawWoman(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + object_bob_Tex_0007C8, + object_bob_Tex_000FC8, + object_bob_Tex_0017C8, + }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitOldWoman(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_bba_Skel_0000F0); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcOldManAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_OLD_WOMAN; + this->drawConfig = EC_DRAW_OLD_WOMAN; +} + +void DemoEc_UpdateOldWoman(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawOldWoman(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, &object_bba_Tex_0004C8, NULL, NULL, NULL); +} + +void DemoEc_InitBossCarpenter(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_toryo_Skel_007150); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcCarpenterAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_BOSS_CARPENTER; + this->drawConfig = EC_DRAW_BOSS_CARPENTER; +} + +void DemoEc_UpdateBossCarpenter(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawBossCarpenter(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, NULL, NULL, NULL, NULL); +} + +void DemoEc_InitCarpenter(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_daiku_Skel_007958); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcCarpenterAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_CARPENTER; + this->drawConfig = EC_DRAW_CARPENTER; +} + +void DemoEc_UpdateCarpenter(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +s32 DemoEc_CarpenterOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx, Gfx** gfx) { + DemoEc* this = (DemoEc*)thisx; + + if (limbIndex == 1) { + gDPPipeSync((*gfx)++); + switch (this->actor.params) { + case 10: + gDPSetEnvColor((*gfx)++, 170, 10, 70, 255); + break; + case 11: + gDPSetEnvColor((*gfx)++, 170, 200, 255, 255); + break; + case 12: + gDPSetEnvColor((*gfx)++, 0, 230, 70, 255); + break; + case 13: + gDPSetEnvColor((*gfx)++, 200, 0, 150, 255); + break; + } + } + + return false; +} + +Gfx* DemoEc_GetCarpenterPostLimbDList(DemoEc* this) { + switch (this->actor.params) { + case 10: + return object_daiku_DL_005BD0; + case 11: + return object_daiku_DL_005AC0; + case 12: + return object_daiku_DL_005990; + case 13: + return object_daiku_DL_005880; + default: + osSyncPrintf(VT_FGCOL(RED) "かつらが無い!!!!!!!!!!!!!!!!\n" VT_RST); + return NULL; + } +} + +void DemoEc_CarpenterPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, + Gfx** gfx) { + DemoEc* this = (DemoEc*)thisx; + Gfx* postLimbDList; + + if (limbIndex == 15) { + postLimbDList = DemoEc_GetCarpenterPostLimbDList(this); + gSPDisplayList((*gfx)++, SEGMENTED_TO_VIRTUAL(postLimbDList)); + } +} + +void DemoEc_DrawCarpenter(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, NULL, 0, DemoEc_CarpenterOverrideLimbDraw, DemoEc_CarpenterPostLimbDraw); +} + +void DemoEc_InitGerudo(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gGerudoWhiteSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcGerudoAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_GERUDO; + this->drawConfig = EC_DRAW_GERUDO; +} + +void DemoEc_UpdateGerudo(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +Gfx* DemoEc_GetGerudoPostLimbDList(DemoEc* this) { + switch (this->actor.params) { + case 16: + return gGerudoWhiteHairstyleBobDL; + case 17: + return gGerudoWhiteHairstyleStraightFringeDL; + case 18: + return gGerudoWhiteHairstyleSpikyDL; + default: + osSyncPrintf(VT_FGCOL(RED) "かつらが無い!!!!!!!!!!!!!!!!\n" VT_RST); + return NULL; + } +} + +void DemoEc_GerudoPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, + Gfx** gfx) { + DemoEc* this = (DemoEc*)thisx; + Gfx* postLimbDList; + + if (limbIndex == 15) { + postLimbDList = DemoEc_GetGerudoPostLimbDList(this); + gSPDisplayList((*gfx)++, SEGMENTED_TO_VIRTUAL(postLimbDList)); + } +} + +void DemoEc_DrawGerudo(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gGerudoWhiteEyeOpenTex, + gGerudoWhiteEyeHalfTex, + gGerudoWhiteEyeClosedTex, + }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, DemoEc_GerudoPostLimbDraw); +} + +void DemoEc_InitDancingZora(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gZoraSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcDancingZoraAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_DANCING_ZORA; + this->drawConfig = EC_DRAW_DANCING_ZORA; +} + +void DemoEc_UpdateDancingZora(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawDancingZora(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gZoraEyeOpenTex, gZoraEyeHalfTex, gZoraEyeClosedTex }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitKingZora(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gKzSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcKingZoraAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_KING_ZORA; + this->drawConfig = EC_DRAW_KING_ZORA; + DemoEc_SetEyeTexIndex(this, 3); +} + +void func_8096F1D4(DemoEc* this) { + f32 currentFrame = this->skelAnime.curFrame; + + if (currentFrame <= 32.0f) { + DemoEc_SetEyeTexIndex(this, 3); + } else { + DemoEc_UpdateEyes(this); + } +} + +void func_8096F224(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcAnim_006930, 2, -8.0f, false); + this->updateMode = EC_UPDATE_17; +} + +void func_8096F26C(DemoEc* this, s32 arg1) { + if (arg1 != 0) { + DemoEc_ChangeAnimation(this, &gDemoEcAnim_006220, 0, 0.0f, false); + this->updateMode = EC_UPDATE_18; + } +} + +void func_8096F2B0(DemoEc* this, GlobalContext* globalCtx, s32 arg2) { + CsCmdActorAction* npcAction; + s32 sp18; + + npcAction = DemoEc_GetNpcAction(globalCtx, arg2); + + if (npcAction != NULL) { + sp18 = npcAction->action; + if ((sp18 != this->npcAction)) { + if (this->npcAction) {} + if (sp18 == 2) { + func_8096F224(this, globalCtx); + } + this->npcAction = sp18; + } + } +} + +void DemoEc_UpdateKingZora(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_SetNpcActionPosRot(this, globalCtx, 6); + DemoEc_UpdateBgFlags(this, globalCtx); + func_8096F2B0(this, globalCtx, 6); +} + +void func_8096F378(DemoEc* this, GlobalContext* globalCtx) { + s32 animDone = DemoEc_UpdateSkelAnime(this); + + func_8096D594(this, globalCtx); + func_8096F1D4(this); + DemoEc_UpdateBgFlags(this, globalCtx); + func_8096F26C(this, animDone); +} + +void func_8096F3D4(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawKingZora(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gKzEyeOpenTex, gKzEyeHalfTex, gKzEyeClosedTex, gKzEyeOpen2Tex }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitMido(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gMidoSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcMidoAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_MIDO; + this->drawConfig = EC_DRAW_MIDO; + DemoEc_SetEyeTexIndex(this, 3); +} + +void func_8096F4FC(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcAnim_008D1C, 2, -8.0f, false); + this->updateMode = EC_UPDATE_20; +} + +void func_8096F544(DemoEc* this, s32 changeAnim) { + if (changeAnim) { + DemoEc_ChangeAnimation(this, &gDemoEcAnim_009234, 0, 0.0f, false); + } +} + +void func_8096F578(DemoEc* this, GlobalContext* globalCtx, s32 arg2) { + CsCmdActorAction* npcAction; + s32 sp18; + + npcAction = DemoEc_GetNpcAction(globalCtx, arg2); + if (npcAction != NULL) { + sp18 = npcAction->action; + if ((sp18 != this->npcAction)) { + if (this->npcAction) {} + if (sp18 == 2) { + func_8096F4FC(this, globalCtx); + } + this->npcAction = sp18; + } + } +} + +void DemoEc_UpdateMido(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_SetNpcActionPosRot(this, globalCtx, 7); + DemoEc_UpdateBgFlags(this, globalCtx); + func_8096F578(this, globalCtx, 7); +} + +void func_8096F640(DemoEc* this, GlobalContext* globalCtx) { + s32 animDone = DemoEc_UpdateSkelAnime(this); + + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); + func_8096F544(this, animDone); +} + +void DemoEc_DrawMido(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gMidoEyeOpenTex, + gMidoEyeHalfTex, + gMidoEyeClosedTex, + gMidoEyeAngryTex, + }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitCucco(DemoEc* this, GlobalContext* globalCtx) { + AnimationHeader* animation; + + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gCuccoSkel); + DemoEc_UseAnimationObject(this, globalCtx); + + if (this->actor.params == 22) { + animation = &gDemoEcJumpingCuccoAnim; + } else if (this->actor.params == 23) { + animation = &gDemoEcJumpingCucco2Anim; + } else { + animation = &gDemoEcWalkingCuccoAnim; + } + + DemoEc_ChangeAnimation(this, animation, 0, 0.0f, false); + func_8096D64C(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_CUCCO; + this->drawConfig = EC_DRAW_CUCCO; +} + +void DemoEc_UpdateCucco(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawCucco(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, NULL, NULL, NULL, NULL); +} + +void DemoEc_InitCuccoLady(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gCuccoLadySkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcCuccoLadyAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_CUCCO_LADY; + this->drawConfig = EC_DRAW_CUCCO_LADY; +} + +void DemoEc_UpdateCuccoLady(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawCuccoLady(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gCuccoLadyEyeOpenTex, + gCuccoLadyEyeHalfTex, + gCuccoLadyEyeClosedTex, + }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitPotionShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_ds2_Skel_004258); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcPotionShopOwnerAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_POTION_SHOP_OWNER; + this->drawConfig = EC_DRAW_POTION_SHOP_OWNER; +} + +void DemoEc_UpdatePotionShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawPotionShopOwner(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gPotionShopkeeperEyeOpenTex, + gPotionShopkeeperEyeHalfTex, + gPotionShopkeeperEyeClosedTex, + }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitMaskShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_os_Skel_004658); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcPotionShopOwnerAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_MASK_SHOP_OWNER; + this->drawConfig = EC_DRAW_MASK_SHOP_OWNER; +} + +void DemoEc_UpdateMaskShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawMaskShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_DrawSkeleton(this, globalCtx, gOsEyeClosedTex, NULL, NULL, NULL); +} + +void DemoEc_InitFishingOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gFishingOwnerSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcPotionShopOwnerAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_FISHING_MAN; + this->drawConfig = EC_DRAW_FISHING_MAN; +} + +void DemoEc_UpdateFishingOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_FishingOwnerPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, + Gfx** gfx) { + DemoEc* this = (DemoEc*)thisx; + + if ((limbIndex == 8) && !(HIGH_SCORE(HS_FISHING) & 0x1000)) { + gSPDisplayList((*gfx)++, SEGMENTED_TO_VIRTUAL(gFishingOwnerHatDL)); + } +} + +void DemoEc_DrawFishingOwner(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gFishingOwnerEyeOpenTex, + gFishingOwnerEyeHalfTex, + gFishingOwnerEyeClosedTex, + }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, DemoEc_FishingOwnerPostLimbDraw); +} + +void DemoEc_InitBombchuShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &object_rs_Skel_004868); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gDemoEcPotionShopOwnerAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_BOMBCHU_SHOP_OWNER; + this->drawConfig = EC_DRAW_BOMBCHU_SHOP_OWNER; +} + +void DempEc_UpdateBombchuShopOwner(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawBombchuShopOwner(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gBombchuShopkeeperEyeOpenTex, gBombchuShopkeeperEyeHalfTex, + gBombchuShopkeeperEyeClosedTex }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, NULL, NULL, NULL); +} + +void DemoEc_InitGorons(DemoEc* this, GlobalContext* globalCtx) { + s32 pad[2]; + AnimationHeader* animation; + f32 goronScale; + Vec3f* scale = &this->actor.scale; + + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gGoronSkel); + DemoEc_UseAnimationObject(this, globalCtx); + + if (this->actor.params == 30) { + animation = &gDemoEcGoronAnim; + goronScale = 1.0f; + } else if (this->actor.params == 31) { + animation = &gDemoEcGoron2Anim; + goronScale = 1.0f; + } else if (this->actor.params == 32) { + animation = &gDemoEcGoronAnim; + goronScale = 15.0f; + } else { + goronScale = 5.0f; + animation = &object_gm_Anim_0002B8; + } + + DemoEc_ChangeAnimation(this, animation, 0, 0.0f, false); + + scale->x *= goronScale; + scale->y *= goronScale; + scale->z *= goronScale; + + func_8096D64C(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_GORON; + this->drawConfig = EC_DRAW_GORON; +} + +void DemoEc_UpdateGorons(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawGorons(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gGoronCsEyeOpenTex, gGoronCsEyeHalfTex, gGoronCsEyeClosedTex }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, gGoronCsMouthNeutralTex, NULL, NULL); +} + +void DemoEc_InitMalon(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UseDrawObject(this, globalCtx); + DemoEc_InitSkelAnime(this, globalCtx, &gMalonAdultSkel); + DemoEc_UseAnimationObject(this, globalCtx); + DemoEc_ChangeAnimation(this, &gMalonAdultSingAnim, 0, 0.0f, false); + func_8096D5D4(this, globalCtx); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->updateMode = EC_UPDATE_MALON; + this->drawConfig = EC_DRAW_MALON; +} + +void DemoEc_UpdateMalon(DemoEc* this, GlobalContext* globalCtx) { + DemoEc_UpdateSkelAnime(this); + func_8096D594(this, globalCtx); + DemoEc_UpdateEyes(this); + DemoEc_UpdateBgFlags(this, globalCtx); +} + +void DemoEc_DrawMalon(DemoEc* this, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gMalonAdultEyeOpenTex, gMalonAdultEyeHalfTex, gMalonAdultEyeClosedTex }; + s32 eyeTexIndex = this->eyeTexIndex; + void* eyeTexture = eyeTextures[eyeTexIndex]; + + DemoEc_DrawSkeleton(this, globalCtx, eyeTexture, gMalonAdultMouthHappyTex, NULL, NULL); +} + +static DemoEcInitFunc sInitFuncs[] = { + /* 0 */ DemoEc_InitIngo, + /* 1 */ DemoEc_InitTalon, + /* 2 */ DemoEc_InitWindmillMan, + /* 3 */ DemoEc_InitKokiriBoy, + /* 4 */ DemoEc_InitKokiriGirl, + /* 5 */ DemoEc_InitOldMan, + /* 6 */ DemoEc_InitBeardedMan, + /* 7 */ DemoEc_InitWoman, + /* 8 */ DemoEc_InitOldWoman, + /* 9 */ DemoEc_InitBossCarpenter, + /* 10 */ DemoEc_InitCarpenter, + /* 11 */ DemoEc_InitCarpenter, + /* 12 */ DemoEc_InitCarpenter, + /* 13 */ DemoEc_InitCarpenter, + /* 14 */ DemoEc_InitDancingKokiriBoy, + /* 15 */ DemoEc_InitDancingKokiriGirl, + /* 16 */ DemoEc_InitGerudo, + /* 17 */ DemoEc_InitGerudo, + /* 18 */ DemoEc_InitGerudo, + /* 19 */ DemoEc_InitDancingZora, + /* 20 */ DemoEc_InitKingZora, + /* 21 */ DemoEc_InitMido, + /* 22 */ DemoEc_InitCucco, + /* 23 */ DemoEc_InitCucco, + /* 24 */ DemoEc_InitCucco, + /* 25 */ DemoEc_InitCuccoLady, + /* 26 */ DemoEc_InitPotionShopOwner, + /* 27 */ DemoEc_InitMaskShopOwner, + /* 28 */ DemoEc_InitFishingOwner, + /* 29 */ DemoEc_InitBombchuShopOwner, + /* 30 */ DemoEc_InitGorons, + /* 31 */ DemoEc_InitGorons, + /* 32 */ DemoEc_InitGorons, + /* 33 */ DemoEc_InitGorons, + /* 34 */ DemoEc_InitMalon, +}; + +void DemoEc_InitNpc(DemoEc* this, GlobalContext* globalCtx) { + s16 type = this->actor.params; + + if (sInitFuncs[type] == NULL) { + // "Demo_Ec_main_init: Initialization process is wrong arg_data" + osSyncPrintf(VT_FGCOL(RED) " Demo_Ec_main_init:初期化処理がおかしいarg_data = %d!\n" VT_RST, type); + Actor_Kill(&this->actor); + return; + } + + sInitFuncs[type](this, globalCtx); +} + +void DemoEc_InitCommon(DemoEc* this, GlobalContext* globalCtx) { + s32 pad; + s16 primary; + s32 type; + s16 pad2; + s16 sp28; + s32 primaryBankIndex; + s32 secondaryBankIndex; + + type = this->actor.params; + primary = sDrawObjects[type]; + sp28 = sAnimationObjects[type]; + primaryBankIndex = Object_GetIndex(&globalCtx->objectCtx, primary); + secondaryBankIndex = Object_GetIndex(&globalCtx->objectCtx, sp28); + + if ((secondaryBankIndex < 0) || (primaryBankIndex < 0)) { + // "Demo_Ec_main_bank: Bank unreadable arg_data = %d!" + osSyncPrintf(VT_FGCOL(RED) "Demo_Ec_main_bank:バンクを読めない arg_data = %d!\n" VT_RST, type); + Actor_Kill(&this->actor); + return; + } + + if (Object_IsLoaded(&globalCtx->objectCtx, primaryBankIndex) && + Object_IsLoaded(&globalCtx->objectCtx, secondaryBankIndex)) { + + this->drawObjBankIndex = primaryBankIndex; + this->animObjBankIndex = secondaryBankIndex; + + DemoEc_InitNpc(this, globalCtx); + } +} + +static DemoEcUpdateFunc sUpdateFuncs[] = { + DemoEc_InitCommon, + DemoEc_UpdateIngo, + DemoEc_UpdateTalon, + DemoEc_UpdateWindmillMan, + DemoEc_UpdateKokiriBoy, + DemoEc_UpdateKokiriGirl, + DemoEc_UpdateOldMan, + DemoEc_UpdateBeardedMan, + DemoEc_UpdateWoman, + DemoEc_UpdateOldWoman, + DemoEc_UpdateBossCarpenter, + DemoEc_UpdateCarpenter, + DemoEc_UpdateDancingKokiriBoy, + DemoEc_UpdateDancingKokiriGirl, + DemoEc_UpdateGerudo, + DemoEc_UpdateDancingZora, + DemoEc_UpdateKingZora, + func_8096F378, + func_8096F3D4, + DemoEc_UpdateMido, + func_8096F640, + DemoEc_UpdateCucco, + DemoEc_UpdateCuccoLady, + DemoEc_UpdatePotionShopOwner, + DemoEc_UpdateMaskShopOwner, + DemoEc_UpdateFishingOwner, + DempEc_UpdateBombchuShopOwner, + DemoEc_UpdateGorons, + DemoEc_UpdateMalon, +}; + +void DemoEc_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoEc* this = (DemoEc*)thisx; + s32 updateMode = this->updateMode; + + if ((updateMode < 0) || (updateMode >= ARRAY_COUNT(sUpdateFuncs)) || sUpdateFuncs[updateMode] == NULL) { + // "The main mode is strange !!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + if (updateMode != EC_UPDATE_COMMON) { + DemoEc_UseAnimationObject(this, globalCtx); + } + sUpdateFuncs[updateMode](this, globalCtx); + } +} + +void DemoEc_DrawCommon(DemoEc* this, GlobalContext* globalCtx) { +} + +static DemoEcDrawFunc sDrawFuncs[] = { + DemoEc_DrawCommon, DemoEc_DrawIngo, + DemoEc_DrawTalon, DemoEc_DrawWindmillMan, + DemoEc_DrawKokiriBoy, DemoEc_DrawKokiriGirl, + DemoEc_DrawOldMan, DemoEc_DrawBeardedMan, + DemoEc_DrawWoman, DemoEc_DrawOldWoman, + DemoEc_DrawBossCarpenter, DemoEc_DrawCarpenter, + DemoEc_DrawGerudo, DemoEc_DrawDancingZora, + DemoEc_DrawKingZora, DemoEc_DrawMido, + DemoEc_DrawCucco, DemoEc_DrawCuccoLady, + DemoEc_DrawPotionShopOwner, DemoEc_DrawMaskShopOwner, + DemoEc_DrawFishingOwner, DemoEc_DrawBombchuShopOwner, + DemoEc_DrawGorons, DemoEc_DrawMalon, +}; + +void DemoEc_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoEc* this = (DemoEc*)thisx; + s32 drawConfig = this->drawConfig; + + if ((drawConfig < 0) || (drawConfig >= ARRAY_COUNT(sDrawFuncs)) || sDrawFuncs[drawConfig] == NULL) { + // "The main mode is strange !!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + if (drawConfig != EC_DRAW_COMMON) { + DemoEc_UseDrawObject(this, globalCtx); + } + sDrawFuncs[drawConfig](this, globalCtx); + } +} + +const ActorInit Demo_Ec_InitVars = { + ACTOR_DEMO_EC, + ACTORCAT_NPC, + FLAGS, + OBJECT_EC, + sizeof(DemoEc), + (ActorFunc)DemoEc_Init, + (ActorFunc)DemoEc_Destroy, + (ActorFunc)DemoEc_Update, + (ActorFunc)DemoEc_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_Demo_Ec/z_demo_ec.h b/soh/src/overlays/actors/ovl_Demo_Ec/z_demo_ec.h new file mode 100644 index 000000000..4ba2e2953 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Ec/z_demo_ec.h @@ -0,0 +1,25 @@ +#ifndef Z_DEMO_EC_H +#define Z_DEMO_EC_H + +#include "ultra64.h" +#include "global.h" + +struct DemoEc; + +typedef void (*DemoEcInitFunc)(struct DemoEc*, GlobalContext*); +typedef void (*DemoEcUpdateFunc)(struct DemoEc*, GlobalContext*); +typedef void (*DemoEcDrawFunc)(struct DemoEc*, GlobalContext*); + +typedef struct DemoEc { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ s16 eyeTexIndex; + /* 0x0192 */ s16 blinkTimer; + /* 0x0194 */ s32 updateMode; + /* 0x0198 */ s32 drawConfig; + /* 0x019C */ s32 npcAction; + /* 0x01A0 */ s32 drawObjBankIndex; + /* 0x01A4 */ s32 animObjBankIndex; +} DemoEc; // size = 0x01A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c b/soh/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c new file mode 100644 index 000000000..d06eb2ce0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.c @@ -0,0 +1,2151 @@ +#include "z_demo_effect.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_efc_crystal_light/object_efc_crystal_light.h" +#include "objects/object_efc_fire_ball/object_efc_fire_ball.h" +#include "objects/object_efc_lgt_shower/object_efc_lgt_shower.h" +#include "objects/object_god_lgt/object_god_lgt.h" +#include "objects/object_light_ring/object_light_ring.h" +#include "objects/object_triforce_spot/object_triforce_spot.h" +#include "objects/object_efc_tw/object_efc_tw.h" +#include "objects/object_gi_jewel/object_gi_jewel.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoEffect_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_Update(Actor* thisx, GlobalContext* globalCtx); + +void DemoEffect_DrawCrystalLight(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawFireBall(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawBlueOrb(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawLgtShower(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawGodLgt(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawLightRing(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawTriforceSpot(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawGetItem(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawLightEffect(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawTimeWarp(Actor* thisx, GlobalContext* globalCtx); +void DemoEffect_DrawJewel(Actor* thisx, GlobalContext* globalCtx); + +void DemoEffect_Wait(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_InitTimeWarp(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_InitTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_InitCreationFireball(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_InitJewel(GlobalContext* globalCtx, DemoEffect* this); +void DemoEffect_InitJewelColor(DemoEffect* this); + +void DemoEffect_UpdateCrystalLight(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdatePositionToParent(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateBlueOrbGrow(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateBlueOrbShrink(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateLgtShower(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateGodLgtDin(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateGodLgtNayru(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateGodLgtFarore(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateLightRingExpanding(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateTriforceSpot(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateGetItem(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateLightRingShrinking(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateLightRingTriforce(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateLightEffect(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateJewelChild(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateJewelAdult(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateDust(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateCreationFireball(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateTimeWarpReturnFromChamberOfSages(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateTimeWarpPullMasterSword(DemoEffect* this, GlobalContext* globalCtx); +void DemoEffect_UpdateTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx); + +s32 DemoEffect_CheckCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionCompareId); +void DemoEffect_InitPositionFromCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionIndex); +void DemoEffect_MoveToCsEndpoint(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, s32 shouldUpdateFacing); +void DemoEffect_MoveGetItem(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, f32 speed); + +const ActorInit Demo_Effect_InitVars = { + ACTOR_DEMO_EFFECT, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(DemoEffect), + (ActorFunc)DemoEffect_Init, + (ActorFunc)DemoEffect_Destroy, + (ActorFunc)DemoEffect_Update, + NULL, + NULL, +}; + +// This variable assures only one jewel will play SFX +static s16 sSfxJewelId[] = { 0 }; + +// The object used by the effectType +static s16 sEffectTypeObjects[] = { + /* 0x00 */ OBJECT_EFC_CRYSTAL_LIGHT, + /* 0x01 */ OBJECT_EFC_FIRE_BALL, + /* 0x02 */ OBJECT_GAMEPLAY_KEEP, + /* 0x03 */ OBJECT_EFC_LGT_SHOWER, + /* 0x04 */ OBJECT_GOD_LGT, + /* 0x05 */ OBJECT_GOD_LGT, + /* 0x06 */ OBJECT_GOD_LGT, + /* 0x07 */ OBJECT_LIGHT_RING, + /* 0x08 */ OBJECT_TRIFORCE_SPOT, + /* 0x09 */ OBJECT_GI_MEDAL, + /* 0x0A */ OBJECT_GI_MEDAL, + /* 0x0B */ OBJECT_GI_MEDAL, + /* 0x0C */ OBJECT_GI_MEDAL, + /* 0x0D */ OBJECT_GI_MEDAL, + /* 0x0E */ OBJECT_GI_MEDAL, + /* 0x0F */ OBJECT_EFC_TW, + /* 0x10 */ OBJECT_LIGHT_RING, + /* 0x11 */ OBJECT_LIGHT_RING, + /* 0x12 */ OBJECT_GAMEPLAY_KEEP, + /* 0x13 */ OBJECT_GI_JEWEL, + /* 0x14 */ OBJECT_GI_JEWEL, + /* 0x15 */ OBJECT_GI_JEWEL, + /* 0x16 */ OBJECT_GI_JEWEL, + /* 0x17 */ OBJECT_GI_M_ARROW, + /* 0x18 */ OBJECT_EFC_TW, + /* 0x19 */ OBJECT_EFC_TW, +}; + +static u8 sTimewarpVertexSizeIndices[] = { 1, 1, 2, 0, 1, 1, 2, 0, 1, 2, 0, 2, 1, 0, 1, 0, 2, 0, 2, 2, 0 }; + +static Color_RGB8 sJewelSparkleColors[5][2] = { + { { 255, 255, 255 }, { 100, 255, 0 } }, { { 255, 255, 255 }, { 200, 0, 150 } }, + { { 255, 255, 255 }, { 0, 100, 255 } }, { { 0, 0, 0 }, { 0, 0, 0 } }, + { { 223, 0, 0 }, { 0, 0, 0 } }, +}; + +/** + * Sets up the update function. + */ +void DemoEffect_SetupUpdate(DemoEffect* this, DemoEffectFunc updateFunc) { + this->updateFunc = updateFunc; +} + +/** + * Gives a number on the range of 0.0f - 1.0f representing current cutscene action completion percentage. + */ +f32 DemoEffect_InterpolateCsFrames(GlobalContext* globalCtx, s32 csActionId) { + f32 interpolated = + Environment_LerpWeight(globalCtx->csCtx.npcActions[csActionId]->endFrame, + globalCtx->csCtx.npcActions[csActionId]->startFrame, globalCtx->csCtx.frames); + if (interpolated > 1.0f) { + interpolated = 1.0f; + } + return interpolated; +} + +/** + * Initializes information for Jewel/Spritual Stone actors. + */ +void DemoEffect_InitJewel(GlobalContext* globalCtx, DemoEffect* this) { + this->initDrawFunc = DemoEffect_DrawJewel; + if (!LINK_IS_ADULT) { + this->initUpdateFunc = DemoEffect_UpdateJewelChild; + } else { + this->initUpdateFunc = DemoEffect_UpdateJewelAdult; + } + if (globalCtx->sceneNum == SCENE_TOKINOMA) { + Actor_SetScale(&this->actor, 0.35f); + } else { + Actor_SetScale(&this->actor, 0.10f); + } + this->csActionId = 1; + this->actor.shape.rot.x = 16384; + DemoEffect_InitJewelColor(this); + this->jewel.alpha = 0; + this->jewelCsRotation.x = this->jewelCsRotation.y = this->jewelCsRotation.z = 0; + sSfxJewelId[0] = 0; +} + +/** + * Initializes information for Get Item actors. + * These are the Medal and Light Arrow actors. + */ +void DemoEffect_InitGetItem(DemoEffect* this) { + this->getItem.isPositionInit = 0; + this->getItem.isLoaded = 0; + this->initDrawFunc = DemoEffect_DrawGetItem; + this->initUpdateFunc = DemoEffect_UpdateGetItem; + Actor_SetScale(&this->actor, 0.25f); + this->csActionId = 6; +} + +/** + * Main Actor Init function + */ +void DemoEffect_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + DemoEffect* this = (DemoEffect*)thisx; + s32 effectType; + s32 lightEffect; + s32 objectIndex; + DemoEffect* crystalLight; + DemoEffect* lightRing; + + effectType = (this->actor.params & 0x00FF); + lightEffect = ((this->actor.params & 0xF000) >> 12); + + osSyncPrintf(VT_FGCOL(CYAN) " no = %d\n" VT_RST, effectType); + + objectIndex = sEffectTypeObjects[effectType] == OBJECT_GAMEPLAY_KEEP + ? 0 + : Object_GetIndex(&globalCtx->objectCtx, sEffectTypeObjects[effectType]); + + osSyncPrintf(VT_FGCOL(CYAN) " bank_ID = %d\n" VT_RST, objectIndex); + + if (objectIndex < 0) { + ASSERT(0, "0", "../z_demo_effect.c", 723); + } else { + this->initObjectBankIndex = objectIndex; + } + + this->effectFlags = 0; + Actor_SetScale(&this->actor, 0.2f); + + switch (effectType) { + case DEMO_EFFECT_CRYSTAL_LIGHT: + this->initDrawFunc = DemoEffect_DrawCrystalLight; + this->initUpdateFunc = DemoEffect_UpdateCrystalLight; + break; + + case DEMO_EFFECT_FIRE_BALL: + this->initDrawFunc = DemoEffect_DrawFireBall; + this->initUpdateFunc = DemoEffect_UpdatePositionToParent; + Actor_SetScale(&this->actor, 0.1f); + break; + + case DEMO_EFFECT_BLUE_ORB: + this->initDrawFunc = DemoEffect_DrawBlueOrb; + this->initUpdateFunc = DemoEffect_UpdateBlueOrbGrow; + this->blueOrb.alpha = 255; + this->blueOrb.scale = 5; + this->blueOrb.rotation = 0; + Actor_SetScale(&this->actor, 0.05f); + this->primXluColor[0] = 188; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 100; + this->envXluColor[2] = 255; + this->envXluColor[0] = 0; + break; + + case DEMO_EFFECT_LIGHT: + this->initDrawFunc = DemoEffect_DrawLightEffect; + this->initUpdateFunc = DemoEffect_UpdateLightEffect; + this->light.alpha = 255; + this->light.scaleFlag = 0; + this->light.flicker = 0; + this->light.rotation = 0; + switch (lightEffect) { + case DEMO_EFFECT_LIGHT_RED: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 50; + this->envXluColor[0] = 255; + this->envXluColor[2] = 0; + break; + + case DEMO_EFFECT_LIGHT_BLUE: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 150; + this->envXluColor[0] = 0; + this->envXluColor[2] = 255; + break; + + case DEMO_EFFECT_LIGHT_GREEN: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 200; + this->envXluColor[0] = 0; + this->envXluColor[2] = 0; + break; + + case DEMO_EFFECT_LIGHT_ORANGE: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 150; + this->envXluColor[0] = 255; + this->envXluColor[2] = 0; + break; + + case DEMO_EFFECT_LIGHT_YELLOW: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[0] = 200; + this->envXluColor[1] = 255; + this->envXluColor[2] = 0; + break; + + case DEMO_EFFECT_LIGHT_PURPLE: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + // clang-format off + this->envXluColor[0] = 200; this->envXluColor[1] = 50; this->envXluColor[2] = 255; // Sameline prevents reordering + // clang-format on + break; + + case DEMO_EFFECT_LIGHT_GREEN2: + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 200; + this->envXluColor[0] = 0; + this->envXluColor[2] = 0; + break; + } + this->csActionId = 7; + Actor_SetScale(thisx, 0.0f); + break; + + case DEMO_EFFECT_LGT_SHOWER: + this->lgtShower.alpha = 255; + this->initDrawFunc = DemoEffect_DrawLgtShower; + this->initUpdateFunc = DemoEffect_UpdateLgtShower; + break; + + case DEMO_EFFECT_GOD_LGT_DIN: + Actor_SetScale(&this->actor, 0.1f); + this->initDrawFunc = DemoEffect_DrawGodLgt; + this->primXluColor[1] = 170; + this->primXluColor[0] = 255; + this->primXluColor[2] = 255; + this->envXluColor[0] = 255; + this->envXluColor[2] = 255; + this->envXluColor[1] = 0; + this->godLgt.type = GOD_LGT_DIN; + this->godLgt.rotation = 0; + this->initUpdateFunc = DemoEffect_UpdateGodLgtDin; + this->csActionId = 0; + break; + + case DEMO_EFFECT_GOD_LGT_NAYRU: + if (gSaveContext.entranceIndex == 0x013D) { + Actor_SetScale(&this->actor, 1.0f); + } else { + Actor_SetScale(&this->actor, 0.1f); + } + this->initDrawFunc = DemoEffect_DrawGodLgt; + this->primXluColor[0] = 170; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[1] = 40; + this->envXluColor[2] = 255; + this->envXluColor[0] = 0; + this->godLgt.type = GOD_LGT_NAYRU; + this->godLgt.lightRingSpawnDelay = 4; + this->godLgt.rotation = 0; + this->godLgt.lightRingSpawnTimer = 0; + this->initUpdateFunc = DemoEffect_UpdateGodLgtNayru; + this->csActionId = 1; + break; + + case DEMO_EFFECT_GOD_LGT_FARORE: + if (gSaveContext.entranceIndex == 0x00EE) { + Actor_SetScale(&this->actor, 2.4f); + } else { + Actor_SetScale(&this->actor, 0.1f); + } + this->initDrawFunc = DemoEffect_DrawGodLgt; + this->primXluColor[0] = 170; + this->primXluColor[2] = 170; + this->primXluColor[1] = 255; + this->envXluColor[1] = 200; + this->envXluColor[0] = 0; + this->envXluColor[2] = 0; + this->godLgt.type = GOD_LGT_FARORE; + this->godLgt.rotation = 0; + this->initUpdateFunc = DemoEffect_UpdateGodLgtFarore; + this->csActionId = 2; + break; + + case DEMO_EFFECT_LIGHTRING_EXPANDING: + this->initDrawFunc = DemoEffect_DrawLightRing; + this->initUpdateFunc = DemoEffect_UpdateLightRingExpanding; + this->lightRing.timer = 20; + this->lightRing.timerIncrement = 4; + this->lightRing.alpha = 255; + break; + + case DEMO_EFFECT_LIGHTRING_TRIFORCE: + this->initDrawFunc = DemoEffect_DrawLightRing; + this->initUpdateFunc = DemoEffect_UpdateLightRingTriforce; + this->lightRing.timer = 20; + this->lightRing.timerIncrement = 4; + this->lightRing.alpha = 0; + this->csActionId = 4; + break; + + case DEMO_EFFECT_LIGHTRING_SHRINKING: + this->initDrawFunc = DemoEffect_DrawLightRing; + this->initUpdateFunc = DemoEffect_UpdateLightRingShrinking; + this->lightRing.timer = 351; + this->lightRing.timerIncrement = 2; + this->lightRing.alpha = 0; + break; + + case DEMO_EFFECT_TRIFORCE_SPOT: + this->initDrawFunc = DemoEffect_DrawTriforceSpot; + this->initUpdateFunc = DemoEffect_UpdateTriforceSpot; + this->triforceSpot.crystalLightOpacity = 0; + this->triforceSpot.lightColumnOpacity = 0; + this->triforceSpot.triforceSpotOpacity = 0; + this->triforceSpot.rotation = 0; + this->primXluColor[0] = 0; + this->csActionId = 3; + + Actor_SetScale(&this->actor, 0.020f); + + crystalLight = (DemoEffect*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_CRYSTAL_LIGHT); + + if (crystalLight != NULL) { + Actor_SetScale(&crystalLight->actor, 0.6f); + } + + lightRing = (DemoEffect*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &crystalLight->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_LIGHTRING_TRIFORCE); + + if (lightRing != NULL) { + Actor_SetScale(&lightRing->actor, 0.4f); + } + break; + + case DEMO_EFFECT_MEDAL_FIRE: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_MEDALLION_FIRE; + break; + + case DEMO_EFFECT_MEDAL_WATER: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_MEDALLION_WATER; + break; + + case DEMO_EFFECT_MEDAL_FOREST: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_MEDALLION_FOREST; + break; + + case DEMO_EFFECT_MEDAL_SPIRIT: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_MEDALLION_SPIRIT; + break; + + case DEMO_EFFECT_MEDAL_SHADOW: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_MEDALLION_SHADOW; + break; + + case DEMO_EFFECT_MEDAL_LIGHT: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_MEDALLION_LIGHT; + break; + + case DEMO_EFFECT_LIGHTARROW: + DemoEffect_InitGetItem(this); + this->getItem.drawId = GID_ARROW_LIGHT; + break; + + case DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE: + case DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL: + this->actor.flags |= ACTOR_FLAG_25; + case DEMO_EFFECT_TIMEWARP_MASTERSWORD: + this->initDrawFunc = DemoEffect_DrawTimeWarp; + this->initUpdateFunc = DemoEffect_InitTimeWarp; + this->envXluColor[0] = 0; + this->envXluColor[1] = 100; + this->envXluColor[2] = 255; + SkelCurve_Clear(&this->skelCurve); + this->timeWarp.shrinkTimer = 0; + break; + + case DEMO_EFFECT_JEWEL_KOKIRI: + this->jewelDisplayList = gGiKokiriEmeraldGemDL; + this->jewelHolderDisplayList = gGiKokiriEmeraldSettingDL; + this->jewel.type = DEMO_EFFECT_JEWEL_KOKIRI; + this->jewel.isPositionInit = 0; + DemoEffect_InitJewel(globalCtx, this); + break; + + case DEMO_EFFECT_JEWEL_GORON: + this->jewelDisplayList = gGiGoronRubyGemDL; + this->jewelHolderDisplayList = gGiGoronRubySettingDL; + this->jewel.type = DEMO_EFFECT_JEWEL_GORON; + this->jewel.isPositionInit = 0; + DemoEffect_InitJewel(globalCtx, this); + break; + + case DEMO_EFFECT_JEWEL_ZORA: + this->jewelDisplayList = gGiZoraSapphireGemDL; + this->jewelHolderDisplayList = gGiZoraSapphireSettingDL; + this->jewel.type = DEMO_EFFECT_JEWEL_ZORA; + this->jewel.isPositionInit = 0; + DemoEffect_InitJewel(globalCtx, this); + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTOR_EN_DOOR); + if ((globalCtx->sceneNum == SCENE_BDAN) && (gSaveContext.infTable[20] & 0x20)) { + Actor_Kill(&this->actor); + return; + } + break; + + case DEMO_EFFECT_DUST: + this->initDrawFunc = NULL; + this->initUpdateFunc = DemoEffect_UpdateDust; + this->dust.timer = 0; + this->csActionId = 2; + break; + + default: + ASSERT(0, "0", "../z_demo_effect.c", 1062); + break; + } + + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + DemoEffect_SetupUpdate(this, DemoEffect_Wait); +} + +/** + * Main Actor Destroy function + */ +void DemoEffect_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + s32 effectType = (this->actor.params & 0x00FF); + + if (effectType == DEMO_EFFECT_TIMEWARP_MASTERSWORD || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE || + effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL) { + SkelCurve_Destroy(globalCtx, &this->skelCurve); + } +} + +/** + * This update function waits until the associate object is loaded. + * Once the object is loaded, it will copy over the initUpdateFunc/initDrawFunc funcs to be active. + * They are copied to actor.draw and updateFunc. + * initUpdateFunc/initDrawFunc are set during initialization and are NOT executed. + */ +void DemoEffect_Wait(DemoEffect* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->initObjectBankIndex)) { + this->actor.objBankIndex = this->initObjectBankIndex; + this->actor.draw = this->initDrawFunc; + this->updateFunc = this->initUpdateFunc; + + osSyncPrintf(VT_FGCOL(CYAN) " 転送終了 move_wait " VT_RST); + } +} + +/** + * Copies the current Actor's position to the parent Actor's position. + */ +void DemoEffect_UpdatePositionToParent(DemoEffect* this, GlobalContext* globalCtx) { + if (this->actor.parent != NULL) { + // Struct copy affects regalloc + this->actor.world.pos.x = this->actor.parent->world.pos.x; + this->actor.world.pos.y = this->actor.parent->world.pos.y; + this->actor.world.pos.z = this->actor.parent->world.pos.z; + } +} + +/** + * Update function for the Crystal Light actor. + * The Crystal Light actor is the three beams of light under the Triforce that converge on it. + * The Crystal Light's position is set to the parent Actor (Triforce) each frame. + * If the Crystal Light has no parent Actor, then it will raise into the sky. + */ +void DemoEffect_UpdateCrystalLight(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect_UpdatePositionToParent(this, globalCtx); + this->actor.world.pos.y += 14.0f; +} + +/** + * Spawns sparkle effects for Medals + */ +void DemoEffect_MedalSparkle(DemoEffect* this, GlobalContext* globalCtx, s32 isSmallSpawner) { + Vec3f velocity; + Vec3f accel; + Vec3f pos; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + + if (isSmallSpawner != 1 || (globalCtx->gameplayFrames & 1) == 0) { + primColor.r = 255; + primColor.g = 255; + primColor.b = 255; + envColor.r = 255; + envColor.g = 255; + envColor.b = 100; + primColor.a = 0; + + velocity.y = 0.0f; + + accel.x = 0.0f; + accel.y = -0.1f; + accel.z = 0.0f; + + if (isSmallSpawner) { + velocity.x = Rand_ZeroOne() - 0.5f; + velocity.z = Rand_ZeroOne() - 0.5f; + } else { + velocity.x = (Rand_ZeroOne() - 0.5f) * 2.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 2.0f; + } + + pos.x = Rand_CenteredFloat(10.0f) + this->actor.world.pos.x; + pos.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(10.0f) + this->actor.world.pos.z; + + EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 1000, 16); + } +} + +/** + * Update function for the GetItem Actors. + * Medals and Light Arrows. + * It spawns Medal Sparkle Effects and scales/moves the Actor based on the current Cutscene Action + */ +void DemoEffect_UpdateGetItem(DemoEffect* this, GlobalContext* globalCtx) { + Actor* thisx = &this->actor; + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { + if (this->getItem.isPositionInit) { + DemoEffect_MoveGetItem(this, globalCtx, this->csActionId, 0.1f); + } else { + DemoEffect_InitPositionFromCsAction(this, globalCtx, this->csActionId); + this->getItem.isPositionInit = 1; + } + + if (this->getItem.drawId != GID_ARROW_LIGHT) { + this->actor.shape.rot.x = 0xE0C0; + } else { + this->actor.shape.rot.y += 0x0400; + } + + Actor_SetScale(thisx, 0.20f); + + if (gSaveContext.entranceIndex == 0x0053) { + switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { + case 2: + DemoEffect_MedalSparkle(this, globalCtx, 0); + break; + case 3: + DemoEffect_MedalSparkle(this, globalCtx, 1); + break; + } + } + switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { + case 2: + if (gSaveContext.entranceIndex == 0x0053) { + Audio_PlayActorSound2(thisx, NA_SE_EV_MEDAL_APPEAR_L - SFX_FLAG); + } else { + func_800788CC(NA_SE_EV_MEDAL_APPEAR_S - SFX_FLAG); + } + if (this->getItem.drawId != GID_ARROW_LIGHT) { + this->actor.shape.rot.y += 0x3E80; + } + this->getItem.rotation = 0x3E80; + break; + case 3: + this->getItem.rotation -= (s16)((this->getItem.rotation - 0x03E8) * 0.10f); + if (this->getItem.drawId != GID_ARROW_LIGHT) { + this->actor.shape.rot.y += this->getItem.rotation; + } + if (gSaveContext.entranceIndex == 0x0053) { + Audio_PlayActorSound2(thisx, NA_SE_EV_MEDAL_APPEAR_L - SFX_FLAG); + } else { + func_800788CC(NA_SE_EV_MEDAL_APPEAR_S - SFX_FLAG); + } + break; + case 4: + Audio_PlayActorSound2(thisx, NA_SE_EV_MEDAL_APPEAR_S - SFX_FLAG); + break; + } + } +} + +/** + * Initializes Timewarp Actors. + * This is an Update Function that is only ran for one frame. + * Timewarp actors are spawned when Link... + * 1) Pulls the Master Sword + * 2) Returns from the Chamber of Sages for the first time + * 3) Timeblock is cleared with the Song of Time (Large and Small have different versions of Timewarp) + */ +void DemoEffect_InitTimeWarp(DemoEffect* this, GlobalContext* globalCtx) { + s32 effectType = (this->actor.params & 0x00FF); + + if (!SkelCurve_Init(globalCtx, &this->skelCurve, &gTimeWarpSkel, &gTimeWarpAnim)) { + ASSERT(0, "0", "../z_demo_effect.c", 1283); + } + + if (effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL) { + SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 1.0f, 1.7f); + SkelCurve_Update(globalCtx, &this->skelCurve); + this->updateFunc = DemoEffect_InitTimeWarpTimeblock; + + if (effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE) { + Actor_SetScale(&this->actor, 0.14f); + } else { + Actor_SetScale(&this->actor, 84 * 0.001f); + } + } else if (gSaveContext.sceneSetupIndex == 5 || gSaveContext.sceneSetupIndex == 4 || + (gSaveContext.entranceIndex == 0x0324 && !((gSaveContext.eventChkInf[12] & 0x200)))) { + SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 59.0f, 0.0f); + SkelCurve_Update(globalCtx, &this->skelCurve); + this->updateFunc = DemoEffect_UpdateTimeWarpReturnFromChamberOfSages; + osSyncPrintf(VT_FGCOL(CYAN) " 縮むバージョン \n" VT_RST); + } else { + SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 59.0f, 1.0f, 1.0f); + SkelCurve_Update(globalCtx, &this->skelCurve); + this->updateFunc = DemoEffect_UpdateTimeWarpPullMasterSword; + osSyncPrintf(VT_FGCOL(CYAN) " 通常 バージョン \n" VT_RST); + } +} + +/** + * Update function for the Timewarp Actor that is used when Link pulls the Mastersword + * It changes the Background Music and updates its SkelCurve animation. + */ +void DemoEffect_UpdateTimeWarpPullMasterSword(DemoEffect* this, GlobalContext* globalCtx) { + if (Flags_GetEnv(globalCtx, 1)) { + if (!(this->effectFlags & 0x2)) { + func_800F3F3C(0); + this->effectFlags |= 0x2; + } + + if (SkelCurve_Update(globalCtx, &this->skelCurve)) { + SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 60.0f, 59.0f, 0.0f); + } + } +} + +/** + * Shrinks the Timewarp object vertices. + * Used by the Chamber of Sages return timewarp and Timeblock clear timewarp. + */ +void DemoEffect_TimewarpShrink(f32 size) { + Vtx* vertices; + s32 i; + u8 sizes[3]; + + // This function uses the data in obj_efc_tw offset 0x0060 to 0x01B0 + vertices = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gTimeWarpVtx)); + + sizes[0] = 0; + sizes[1] = (s32)(202.0f * size); + sizes[2] = (s32)(255.0f * size); + + for (i = 0; i < 21; i++) { + if (sTimewarpVertexSizeIndices[i] != 0) { + vertices[i].v.cn[3] = sizes[sTimewarpVertexSizeIndices[i]]; + } + } +} + +/** + * Update function for the Timewarp Actor that is used when Link returns from the Chamber of Sages for the first time. + * It shrinks the timewarp vertices and scales the Actor. + */ +void DemoEffect_UpdateTimeWarpReturnFromChamberOfSages(DemoEffect* this, GlobalContext* globalCtx) { + f32 shrinkProgress; + + this->timeWarp.shrinkTimer++; + + if (this->timeWarp.shrinkTimer > 250) { + if (gSaveContext.entranceIndex == 0x0324) { + gSaveContext.eventChkInf[12] |= 0x200; + } + + Actor_Kill(&this->actor); + return; + } + + if (this->timeWarp.shrinkTimer > 100) { + shrinkProgress = (250 - this->timeWarp.shrinkTimer) * (1.0f / 750.0f); + this->actor.scale.x = shrinkProgress; + this->actor.scale.z = shrinkProgress; + DemoEffect_TimewarpShrink(shrinkProgress * 5.0f); + } + + func_8002F948(&this->actor, NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); +} + +/** + * Update function for the Timewarp Actor that is used when a Timeblock is cleared. + * It shrinks the timewarp vertices and scales the Actor. + */ +void DemoEffect_UpdateTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx) { + f32 shrinkProgress; + f32 scale; + + this->timeWarp.shrinkTimer++; + + if (this->timeWarp.shrinkTimer <= 100) { + shrinkProgress = (100 - this->timeWarp.shrinkTimer) * 0.010f; + scale = shrinkProgress * 0.14f; + + if ((this->actor.params & 0x00FF) == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL) { + scale *= 0.6f; + } + + this->actor.scale.x = scale; + this->actor.scale.z = scale; + DemoEffect_TimewarpShrink(shrinkProgress); + func_8002F948(&this->actor, NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); + return; + } + + DemoEffect_TimewarpShrink(1.0f); + Actor_Kill(&this->actor); +} + +/** + * Initializes information for the Timewarp Actor used for the Timeblock clear effect. + * This is an Update Func that is only ran for one frame. + */ +void DemoEffect_InitTimeWarpTimeblock(DemoEffect* this, GlobalContext* globalCtx) { + func_8002F948(&this->actor, NA_SE_EV_TIMETRIP_LIGHT - SFX_FLAG); + + if (SkelCurve_Update(globalCtx, &this->skelCurve)) { + SkelCurve_SetAnim(&this->skelCurve, &gTimeWarpAnim, 1.0f, 60.0f, 59.0f, 0.0f); + this->updateFunc = DemoEffect_UpdateTimeWarpTimeblock; + this->timeWarp.shrinkTimer = 0; + } +} + +/** + * Update function for the Triforce Actor. + * It rotates and updates the alpha of the Triforce and child actors. + */ +void DemoEffect_UpdateTriforceSpot(DemoEffect* this, GlobalContext* globalCtx) { + this->triforceSpot.rotation += 0x03E8; + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); + + if (globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { + if (this->primXluColor[0] < 140) { + this->primXluColor[0]++; + } + + if (this->primXluColor[0] < 30) { + this->triforceSpot.triforceSpotOpacity = ((s32)this->primXluColor[0]) * 8.5f; + } else { + this->triforceSpot.triforceSpotOpacity = 255; + + if (this->primXluColor[0] < 60) { + this->triforceSpot.lightColumnOpacity = (((s32)this->primXluColor[0]) - 30) * 8.5f; + } else { + if (this->primXluColor[0] <= 140) { + this->triforceSpot.lightColumnOpacity = 255; + this->triforceSpot.crystalLightOpacity = (((s32)this->primXluColor[0]) - 60) * 3.1875f; + } + } + } + } + + if (gSaveContext.entranceIndex == 0x00A0 && gSaveContext.sceneSetupIndex == 6 && + globalCtx->csCtx.frames == 143) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_RING_EXPLOSION); + } + } +} + +/** + * Update function for the LightRing actor that shrinks. + * This is used in the creation cutscene when Din leaves a fireball that explodes into Death Mountain. + */ +void DemoEffect_UpdateLightRingShrinking(DemoEffect* this, GlobalContext* globalCtx) { + if (this->lightRing.timer < this->lightRing.timerIncrement) { + Actor_Kill(&this->actor); + this->lightRing.timer = 0; + } else { + this->lightRing.timer -= this->lightRing.timerIncrement; + } + + if (this->lightRing.timer <= 255) { + if (this->lightRing.timer >= 225) { + this->lightRing.alpha = (-this->lightRing.timer * 8) + 2048; + } else { + this->lightRing.alpha = 255; + } + } + + if (this->lightRing.timer == 255) { + func_800F3F3C(5); + } +} + +/** + * Update function for the Lightring Actor that expands. + * These are spawned by Nayru. + * These are also used by Din in the creation cutscene when she leaves a fireball that explodes into Death Mountain. + */ +void DemoEffect_UpdateLightRingExpanding(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect_UpdatePositionToParent(this, globalCtx); + this->lightRing.timer += this->lightRing.timerIncrement; + + if (this->lightRing.timer >= 225) { + this->lightRing.alpha = (-this->lightRing.timer * 8) + 2048; + } + if (this->lightRing.timer > 255) { + this->lightRing.timer = 255; + Actor_Kill(&this->actor); + this->lightRing.timer = 0; + } +} + +/** + * Update function for the Lightring Actor that expands. This is a special version for the Triforce Actor. + * This version spawns a blue orb when the cutscene action state is set to 2. + * Once the Blue Orb Actor is spawned the Update Function is changed to the regular Light Ring Expanding Update Func. + */ +void DemoEffect_UpdateLightRingTriforce(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect* blueOrb; + + DemoEffect_UpdatePositionToParent(this, globalCtx); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[this->csActionId] != NULL && + globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { + blueOrb = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_BLUE_ORB); + + if (blueOrb != NULL) { + Actor_SetScale(&blueOrb->actor, 0.0f); + } + + this->updateFunc = DemoEffect_UpdateLightRingExpanding; + this->lightRing.alpha = 255; + } + } +} + +/** + * Update function for the FireBall Actor. + * This is a special version that is used in the creation cutscene. + * It moves based on gravity and decrements a timer until zero. Once the timer is zero it will spawn other Actors: + * A Blue Orb Actor, and a Light Ring Expanding Actor, and a Light Ring Shrinking Actor. + */ +void DemoEffect_UpdateCreationFireball(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect* effect; + + Actor_MoveForward(&this->actor); + this->actor.speedXZ = this->actor.speedXZ + (this->actor.gravity * 0.5f); + + if (this->fireBall.timer != 0) { + this->fireBall.timer--; + return; + } + + effect = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_BLUE_ORB); + if (effect != NULL) { + Actor_SetScale(&effect->actor, 0.0f); + } + + effect = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, + DEMO_EFFECT_LIGHTRING_EXPANDING); + if (effect != NULL) { + Actor_SetScale(&effect->actor, 0.1f); + } + + effect = (DemoEffect*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, + DEMO_EFFECT_LIGHTRING_SHRINKING); + if (effect != NULL) { + Actor_SetScale(&effect->actor, 0.2f); + } + + func_800788CC(NA_SE_IT_DM_RING_EXPLOSION); + Actor_Kill(&this->actor); +} + +/** + * Initialization function for the FireBall Actor. + * This is a special version that is used in the creation cutscene. + * It is an Update Function only executed for one frame. The Update Function is then changed to UpdateCreationFireball. + */ +void DemoEffect_InitCreationFireball(DemoEffect* this, GlobalContext* globalCtx) { + Actor* parent = this->actor.parent; + + this->actor.world.rot.y = parent->shape.rot.y; + this->fireBall.timer = 50; + this->actor.speedXZ = 1.5f; + this->actor.minVelocityY = -1.5f; + this->actor.gravity = -0.03f; + this->updateFunc = DemoEffect_UpdateCreationFireball; +} + +/** + * Update action for the Blue Orb Actor. + * This Update Function is run while the Blue Orb is Shrinking. + * The Blue Orb Actor is the blue light sparkle that is in Din's creation cutscene. + * It's spawned in the middle of the expanding Light Ring. + * The Blue Orb Actor shrinks after it grows to max size. + */ +void DemoEffect_UpdateBlueOrbShrink(DemoEffect* this, GlobalContext* globalCtx) { + this->blueOrb.alpha = this->blueOrb.scale * 16; + this->blueOrb.scale--; + Actor_SetScale(&this->actor, this->actor.scale.x * 0.9f); + if (this->blueOrb.scale == 0) { + Actor_Kill(&this->actor); + } +} + +/** + * Update action for the Blue Orb Actor. + * This Update Function is run while the Blue Orb is Growing. + * The Blue Orb Actor is the blue light sparkle that is in Din's creation cutscene. + * It's spawned in the middle of the expanding Light Ring. + * When the scale timer value reaches 0 the Blue Orb's Update Function changes to UpdateBlueOrbShrink. + */ +void DemoEffect_UpdateBlueOrbGrow(DemoEffect* this, GlobalContext* globalCtx) { + if (this->actor.parent != NULL) { + // s32 cast necessary to match codegen. Without the explicit cast to u32 the compiler generates complex cast of + // u8 to float + Actor_SetScale(&this->actor, + (((5.0f - (s32)this->blueOrb.scale) * 0.01f) * 10.0f) * this->actor.parent->scale.x); + } else { + Actor_SetScale(&this->actor, (5.0f - (s32)this->blueOrb.scale) * 0.01f); + } + + if (this->blueOrb.scale != 0) { + this->blueOrb.scale--; + } else { + this->blueOrb.scale = 15; + this->updateFunc = DemoEffect_UpdateBlueOrbShrink; + } +} + +/** + * Update action for the Light Effect Actor. + * The Light Effect has various use cases. + * This function updates the position and scale of the actor based on the current cutscene command. + */ +void DemoEffect_UpdateLightEffect(DemoEffect* this, GlobalContext* globalCtx) { + u16 action; + s32 isLargeSize; + + isLargeSize = ((this->actor.params & 0x0F00) >> 8); + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); + switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { + case 2: + if (this->light.rotation < 240) { + if (!isLargeSize) { + if (this->actor.scale.x < 0.23f) { + this->actor.scale.x += 0.001f; + Actor_SetScale(&this->actor, this->actor.scale.x); + } + } else { + if (this->actor.scale.x < 2.03f) { + this->actor.scale.x += 0.05f; + Actor_SetScale(&this->actor, this->actor.scale.x); + } + } + } + this->light.rotation += 6; + this->light.scaleFlag += 1; + break; + + case 3: + Math_SmoothStepToF(&this->actor.scale.x, 0.0f, 0.1f, 0.1f, 0.005f); + Actor_SetScale(&this->actor, this->actor.scale.x); + break; + + default: + break; + } + + if (globalCtx->sceneNum == SCENE_SPOT04 && gSaveContext.sceneSetupIndex == 6 && + globalCtx->csCtx.frames == 197) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WHITE_OUT); + } + + if (globalCtx->sceneNum == SCENE_SPOT16 && gSaveContext.sceneSetupIndex == 5) { + if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + } + if (globalCtx->csCtx.frames == 640) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WHITE_OUT); + } + + if (0) {} + } + + if (globalCtx->sceneNum == SCENE_SPOT08 && gSaveContext.sceneSetupIndex == 4) { + if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + } + if (globalCtx->csCtx.frames == 648) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WHITE_OUT); + } + + // Necessary to match + if (0) {} + } + + if (globalCtx->sceneNum == SCENE_TOKINOMA && gSaveContext.sceneSetupIndex == 14) { + if (1) {} + + if (globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + } + } + + if (globalCtx->sceneNum == SCENE_DAIYOUSEI_IZUMI || globalCtx->sceneNum == SCENE_YOUSEI_IZUMI_YOKO) { + if (globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_LIGHT_GATHER - SFX_FLAG); + } + } + } +} + +/** + * Update action for the Lgt Shower Actor. + * The Lgt Shower Actor is the green light effect spawned by Farore in the Kokiri Forst creation cutscene. + * This function updates the scale and alpha of the Actor. + */ +void DemoEffect_UpdateLgtShower(DemoEffect* this, GlobalContext* globalCtx) { + if (this->lgtShower.alpha > 3) { + this->lgtShower.alpha -= 3; + this->actor.scale.x *= 1.05f; + this->actor.scale.y *= 1.05f; + this->actor.scale.z *= 1.05f; + } else { + Actor_Kill(&this->actor); + } +} + +/** + * Update action for the God Lgt Din Actor. + * This is the Goddess Din. + * This function moves God Lgt Din based on the current cutscene command. + * This function also spawns a Fireball Actor and sets its update function to the special InitCreationFireball. + * The spawned Fireball Actor is also scaled to be smaller than regular by this function. + */ +void DemoEffect_UpdateGodLgtDin(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect* fireBall; + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 1); + + if (globalCtx->csCtx.npcActions[this->csActionId]->action == 3) { + fireBall = (DemoEffect*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_FIRE_BALL); + + if (fireBall != NULL) { + fireBall->initUpdateFunc = DemoEffect_InitCreationFireball; + Actor_SetScale(&fireBall->actor, 0.020f); + } + } + + if (gSaveContext.entranceIndex == 0x00A0) { + switch (gSaveContext.sceneSetupIndex) { + case 4: + if (globalCtx->csCtx.frames == 288) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); + } + if (globalCtx->csCtx.frames == 635) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); + } + break; + + case 6: + if (globalCtx->csCtx.frames == 55) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + break; + + case 11: + if (globalCtx->csCtx.frames == 350) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + break; + } + } + } +} + +/** + * Update action for the God Lgt Nayru Actor. + * This is the Goddess Nayru. + * This function moves God Lgt Nayure based on the current cutscene command. + * This function also spawns expanding light rings around Nayru in the creation cutscene + */ +void DemoEffect_UpdateGodLgtNayru(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect* lightRing; + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 1); + + if (globalCtx->csCtx.npcActions[this->csActionId]->action == 3) { + if (this->godLgt.lightRingSpawnTimer != 0) { + this->godLgt.lightRingSpawnTimer--; + } else { + this->godLgt.lightRingSpawnTimer = this->godLgt.lightRingSpawnDelay; + lightRing = (DemoEffect*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, this->actor.world.rot.x + 0x4000, + this->actor.world.rot.y, this->actor.world.rot.z, DEMO_EFFECT_LIGHTRING_EXPANDING); + + if (lightRing != NULL) { + Actor_SetScale(&lightRing->actor, 1.0f); + } + } + } + + if (gSaveContext.entranceIndex == 0x00A0) { + switch (gSaveContext.sceneSetupIndex) { + case 4: + if (globalCtx->csCtx.frames == 298) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); + } + break; + + case 6: + if (globalCtx->csCtx.frames == 105) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + break; + + case 11: + if (globalCtx->csCtx.frames == 360) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + break; + } + } + + if (gSaveContext.entranceIndex == 0x013D && gSaveContext.sceneSetupIndex == 4) { + if (globalCtx->csCtx.frames == 72) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + if (globalCtx->csCtx.frames == 80) { + func_800F3F3C(4); + } + } + } +} + +/** + * Update action for the God Lgt Farore Actor. + * This is the Goddess Farore. + * This function moves God Lgt Farore based on the current cutscene command. + * This function also spawns an Lgt Shower Actor during the Kokiri creation cutscene. + */ +void DemoEffect_UpdateGodLgtFarore(DemoEffect* this, GlobalContext* globalCtx) { + DemoEffect* lgtShower; + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL) { + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 1); + + if (globalCtx->csCtx.npcActions[this->csActionId]->action == 3) { + lgtShower = (DemoEffect*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y - 150.0f, this->actor.world.pos.z, 0, 0, 0, DEMO_EFFECT_LGT_SHOWER); + + if (lgtShower != NULL) { + lgtShower->actor.scale.x = 0.23f; + lgtShower->actor.scale.y = 0.15f; + lgtShower->actor.scale.z = 0.23f; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + func_800F3F3C(3); + } + + if (gSaveContext.entranceIndex == 0x00A0) { + switch (gSaveContext.sceneSetupIndex) { + case 4: + if (globalCtx->csCtx.frames == 315) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_PASS); + } + break; + + case 6: + if (globalCtx->csCtx.frames == 80) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + break; + + case 11: + if (globalCtx->csCtx.frames == 370) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_FLYING_GOD_DASH); + } + break; + } + } + } +} + +/** + * Moves this actor towards the target position with a given speed. + */ +void DemoEffect_MoveTowardTarget(Vec3f targetPos, DemoEffect* this, f32 speed) { + this->actor.world.pos.x += (targetPos.x - this->actor.world.pos.x) * speed; + this->actor.world.pos.y += (targetPos.y - this->actor.world.pos.y) * speed; + this->actor.world.pos.z += (targetPos.z - this->actor.world.pos.z) * speed; +} + +/** + * Initializes Jewel colors. + */ +void DemoEffect_InitJewelColor(DemoEffect* this) { + u8 jewelType = this->jewel.type; + + switch (jewelType) { + case DEMO_EFFECT_JEWEL_KOKIRI: + this->primXluColor[2] = 160; + this->primXluColor[0] = 255; + this->primXluColor[1] = 255; + this->envXluColor[0] = 0; + this->envXluColor[1] = 255; + this->envXluColor[2] = 0; + this->primOpaColor[2] = 170; + this->primOpaColor[0] = 255; + this->primOpaColor[1] = 255; + this->envOpaColor[1] = 120; + this->envOpaColor[0] = 150; + this->envOpaColor[2] = 0; + break; + + case DEMO_EFFECT_JEWEL_GORON: + this->primXluColor[1] = 170; + this->primXluColor[0] = 255; + this->primXluColor[2] = 255; + this->envXluColor[2] = 100; + this->envXluColor[0] = 255; + this->envXluColor[1] = 0; + this->primOpaColor[2] = 170; + this->primOpaColor[0] = 255; + this->primOpaColor[1] = 255; + this->envOpaColor[1] = 120; + this->envOpaColor[0] = 150; + this->envOpaColor[2] = 0; + break; + + case DEMO_EFFECT_JEWEL_ZORA: + this->primXluColor[0] = 50; + this->primXluColor[1] = 255; + this->primXluColor[2] = 255; + this->envXluColor[2] = 150; + this->envXluColor[0] = 50; + this->envXluColor[1] = 0; + this->primOpaColor[2] = 170; + this->primOpaColor[0] = 255; + this->primOpaColor[1] = 255; + this->envOpaColor[1] = 120; + this->envOpaColor[0] = 150; + this->envOpaColor[2] = 0; + break; + } +} + +/** + * Sets the Jewel color based on the alpha variable. + * This function if a value of less than 1.0f is supplied will drain the color from the Jewels. + * This effect can be seen in prerelease screenshots. + */ +void DemoEffect_SetJewelColor(DemoEffect* this, f32 alpha) { + DemoEffect_InitJewelColor(this); + + this->primXluColor[0] = (((s32)this->primXluColor[0]) * alpha) + (255.0f * (1.0f - alpha)); + this->primXluColor[1] = (((s32)this->primXluColor[1]) * alpha) + (255.0f * (1.0f - alpha)); + this->primXluColor[2] = (((s32)this->primXluColor[2]) * alpha) + (255.0f * (1.0f - alpha)); + this->primOpaColor[0] = (((s32)this->primOpaColor[0]) * alpha) + (255.0f * (1.0f - alpha)); + this->primOpaColor[1] = (((s32)this->primOpaColor[1]) * alpha) + (255.0f * (1.0f - alpha)); + this->primOpaColor[2] = (((s32)this->primOpaColor[2]) * alpha) + (255.0f * (1.0f - alpha)); + this->envXluColor[0] = ((s32)this->envXluColor[0]) * alpha; + this->envXluColor[1] = ((s32)this->envXluColor[1]) * alpha; + this->envXluColor[2] = ((s32)this->envXluColor[2]) * alpha; + this->envOpaColor[0] = ((s32)this->envOpaColor[0]) * alpha; + this->envOpaColor[1] = ((s32)this->envOpaColor[1]) * alpha; + this->envOpaColor[2] = ((s32)this->envOpaColor[2]) * alpha; +} + +/** + * Moves the Jewel Actor during the activation of the Door of Time cutscene. + * This is used once the Jewel Actor is done orbiting Link and split up to move into the pedastal slots. + */ +void DemoEffect_MoveJewelSplit(PosRot* world, DemoEffect* this) { + switch (this->jewel.type) { + case DEMO_EFFECT_JEWEL_KOKIRI: + world->pos.x -= 40.0f; + break; + case DEMO_EFFECT_JEWEL_GORON: + break; + case DEMO_EFFECT_JEWEL_ZORA: + world->pos.x += 40.0f; + break; + } +} + +/** + * Moves the Jewel Actor spherically from startPos to endPos. + * This is used by the Jewel Actor during the Door of Time activation cutscene. + * This is run when the Jewels merge from Link and begin orbiting him. + */ +void DemoEffect_MoveJewelSpherical(f32 degrees, f32 frameDivisor, Vec3f startPos, Vec3f endPos, f32 radius, + Vec3s rotation, DemoEffect* this) { + s32 pad; + s32 pad2; + f32 distance; + f32 xPos; + f32 ySpherical; + f32 xzSpherical; + + distance = frameDivisor * sqrtf(SQ(endPos.x - startPos.x) + SQ(endPos.y - startPos.y) + SQ(endPos.z - startPos.z)); + + this->actor.world.pos.x = radius * cosf(degrees * (M_PI / 180.0f)); + this->actor.world.pos.y = distance; + this->actor.world.pos.z = radius * sinf(degrees * (M_PI / 180.0f)); + + xPos = this->actor.world.pos.x; + ySpherical = (this->actor.world.pos.y * cosf(rotation.x * (M_PI / 0x8000))) - + (sinf(rotation.x * (M_PI / 0x8000)) * this->actor.world.pos.z); + xzSpherical = (this->actor.world.pos.z * cosf(rotation.x * (M_PI / 0x8000))) + + (sinf(rotation.x * (M_PI / 0x8000)) * this->actor.world.pos.y); + + this->actor.world.pos.x = + (xPos * cosf(rotation.y * (M_PI / 0x8000))) - (sinf(rotation.y * (M_PI / 0x8000)) * xzSpherical); + this->actor.world.pos.y = ySpherical; + this->actor.world.pos.z = + (xzSpherical * cosf(rotation.y * (M_PI / 0x8000))) + (sinf(rotation.y * (M_PI / 0x8000)) * xPos); + + this->actor.world.pos.x += startPos.x; + this->actor.world.pos.y += startPos.y; + this->actor.world.pos.z += startPos.z; +} + +/** + * Moves the Jewel Actor spherically from startPos to endPos. + * This is used by the Jewel Actor during the Door of Time activation cutscene. + * This is run when the Jewels merge from Link and begin orbiting him. + */ +void DemoEffect_MoveJewelActivateDoorOfTime(DemoEffect* this, GlobalContext* globalCtx) { + Vec3f startPos; + Vec3f endPos; + f32 frameDivisor; + f32 degrees; + f32 radius; + s32 csActionId; + + csActionId = this->csActionId; + startPos.x = globalCtx->csCtx.npcActions[csActionId]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[csActionId]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[csActionId]->startPos.z; + endPos.x = globalCtx->csCtx.npcActions[csActionId]->endPos.x; + endPos.y = globalCtx->csCtx.npcActions[csActionId]->endPos.y; + endPos.z = globalCtx->csCtx.npcActions[csActionId]->endPos.z; + + frameDivisor = DemoEffect_InterpolateCsFrames(globalCtx, csActionId); + + switch (this->jewel.type) { + case DEMO_EFFECT_JEWEL_KOKIRI: + degrees = 0.0f; + break; + case DEMO_EFFECT_JEWEL_GORON: + degrees = 120.0f; + break; + case DEMO_EFFECT_JEWEL_ZORA: + degrees = 240.0f; + break; + } + + radius = 50.0f * frameDivisor; + if (radius > 30.0f) { + radius = 30.0f; + } + + if (startPos.x != endPos.x || startPos.y != endPos.y || startPos.z != endPos.z) { + this->jewelCsRotation.x = Math_Atan2F(endPos.z - startPos.z, -(endPos.x - startPos.x)) * (0x8000 / M_PI); + this->jewelCsRotation.y = Math_Vec3f_Yaw(&startPos, &endPos); + } + + this->jewelCsRotation.z += 0x0400; + + degrees += this->jewelCsRotation.z * (360.0f / 65536.0f); + DemoEffect_MoveJewelSpherical(degrees, frameDivisor, startPos, endPos, radius, this->jewelCsRotation, this); +} + +/** + * Spawns Sparkle Effects for the Jewel Actor. + */ +void DemoEffect_JewelSparkle(DemoEffect* this, GlobalContext* globalCtx, s32 spawnerCount) { + Vec3f velocity; + Vec3f accel; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + Color_RGB8* sparkleColors; + s32 i; + + velocity.y = 0.0f; + + accel.x = 0.0f; + accel.y = -0.1f; + accel.z = 0.0f; + + sparkleColors = sJewelSparkleColors[this->jewel.type - DEMO_EFFECT_JEWEL_KOKIRI]; + + primColor.r = sparkleColors[0].r; + primColor.g = sparkleColors[0].g; + primColor.b = sparkleColors[0].b; + envColor.r = sparkleColors[1].r; + envColor.g = sparkleColors[1].g; + envColor.b = sparkleColors[1].b; + primColor.a = 0; + + for (i = 0; i < spawnerCount; i++) { + velocity.x = (Rand_ZeroOne() - 0.5f) * 1.5f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 1.5f; + + EffectSsKiraKira_SpawnDispersed(globalCtx, &this->actor.world.pos, &velocity, &accel, &primColor, &envColor, + 3000, 16); + } +} + +/** + * Plays Jewel sound effects. + * The sSfxJewelId global variable is used to ensure only one Jewel Actor is playing SFX when all are spawned. + */ +void DemoEffect_PlayJewelSfx(DemoEffect* this, GlobalContext* globalCtx) { + if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { + if (this->actor.params == sSfxJewelId[0]) { + func_8002F974(&this->actor, NA_SE_EV_SPIRIT_STONE - SFX_FLAG); + } else if (sSfxJewelId[0] == 0) { + sSfxJewelId[0] = this->actor.params; + func_8002F974(&this->actor, NA_SE_EV_SPIRIT_STONE - SFX_FLAG); + } + } +} + +/** + * Update Function for the Jewel Actor that is run when Link is an adult. + * This rotates the Jewel and updates a timer that is used to scroll Jewel textures. + * There is a call SetJewelColor that does nothing since 1.0f is passed. + * If a value of less than 1.0f were passed to SetJewelColor, then it would appear to drain the Jewel's color. + * This can be seen in preprelease screenshots. + */ +void DemoEffect_UpdateJewelAdult(DemoEffect* this, GlobalContext* globalCtx) { + this->jewel.timer++; + this->actor.shape.rot.y += 0x0400; + DemoEffect_PlayJewelSfx(this, globalCtx); + DemoEffect_SetJewelColor(this, 1.0f); +} + +/** + * Update Function for the Jewel Actor that is run when Link is a child. + * This rotates the Jewel and updates a timer that is used to scroll Jewel textures. + * This also updates the Jewel's position based on different cutscenes. + */ +void DemoEffect_UpdateJewelChild(DemoEffect* this, GlobalContext* globalCtx) { + s32 hasCmdAction; + Actor* thisx = &this->actor; + + this->jewel.timer++; + + if (globalCtx->csCtx.state && globalCtx->csCtx.npcActions[this->csActionId]) { + switch (globalCtx->csCtx.npcActions[this->csActionId]->action) { + case 3: + if (gSaveContext.eventChkInf[4] & 0x800) { + gSaveContext.eventChkInf[4] |= 0x800; + } + DemoEffect_MoveJewelActivateDoorOfTime(this, globalCtx); + if ((globalCtx->gameplayFrames & 1) == 0) { + DemoEffect_JewelSparkle(this, globalCtx, 1); + } + break; + case 4: + if (this->jewel.isPositionInit) { + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); + DemoEffect_MoveJewelSplit(&thisx->world, this); + if ((globalCtx->gameplayFrames & 1) == 0) { + DemoEffect_JewelSparkle(this, globalCtx, 1); + } + } else { + DemoEffect_InitPositionFromCsAction(this, globalCtx, this->csActionId); + DemoEffect_MoveJewelSplit(&thisx->world, this); + this->jewel.isPositionInit = 1; + } + break; + case 6: + Actor_Kill(thisx); + return; + default: + DemoEffect_MoveToCsEndpoint(this, globalCtx, this->csActionId, 0); + if (gSaveContext.entranceIndex == 0x0053) { + DemoEffect_MoveJewelSplit(&thisx->world, this); + } + break; + } + } + + if (gSaveContext.entranceIndex == 0x0053) { + if (!(gSaveContext.eventChkInf[4] & 0x800)) { + hasCmdAction = globalCtx->csCtx.state && globalCtx->csCtx.npcActions[this->csActionId]; + if (!hasCmdAction) { + this->effectFlags |= 0x1; + return; + } + } + } + + thisx->shape.rot.y += 0x0400; + DemoEffect_PlayJewelSfx(this, globalCtx); + this->effectFlags &= ~1; +} + +/** + * Update Function for the Dust Actor. + * This is the dust that is spawned in the Temple of Time during the Light Arrows cutscene. + * This spawns the dust particles and increments a timer + */ +void DemoEffect_UpdateDust(DemoEffect* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL && + globalCtx->csCtx.npcActions[this->csActionId]->action == 2) { + pos = this->actor.world.pos; + + pos.y += 600.0f; + pos.x += Rand_CenteredFloat(300.0f); + pos.z += 200.0f + Rand_CenteredFloat(300.0f); + + velocity.z = 0.0f; + velocity.x = 0.0f; + velocity.y = -20.0f; + + accel.z = 0.0f; + accel.x = 0.0f; + accel.y = 0.2f; + + func_8002873C(globalCtx, &pos, &velocity, &accel, 300, 0, 30); + + this->dust.timer++; + } +} + +/** + * This is the main Actor Update Function. + */ +void DemoEffect_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + this->updateFunc(this, globalCtx); +} + +/** + * Check if the current cutscene action matches the passed in cutscene action ID. + */ +s32 DemoEffect_CheckCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionCompareId) { + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->csActionId] != NULL && + globalCtx->csCtx.npcActions[this->csActionId]->action == csActionCompareId) { + return 1; + } + + return 0; +} + +/** + * Draw function for the Jewel Actor. + */ +void DemoEffect_DrawJewel(Actor* thisx, GlobalContext* globalCtx2) { + DemoEffect* this = (DemoEffect*)thisx; + GlobalContext* globalCtx = globalCtx2; + u32 frames = this->jewel.timer; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2543); + + if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { + if (1) {} + + if (!(this->effectFlags & 0x1)) { + switch (this->jewel.type) { + case DEMO_EFFECT_JEWEL_KOKIRI: + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 256, + (256 - ((frames * 2) % 256)) - 1, 64, 64, 1, (frames * 2) % 256, + (256 - (frames % 256)) - 1, 16, 16)); + break; + + case DEMO_EFFECT_JEWEL_GORON: + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 128, + (256 - ((frames * 2) % 256)) - 1, 32, 64, 1, (frames * 2) % 256, + (256 - (frames % 256)) - 1, 16, 8)); + break; + + case DEMO_EFFECT_JEWEL_ZORA: + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 256, + (256 - ((frames * 2) % 256)) - 1, 32, 32, 1, (frames * 2) % 256, + (256 - (frames % 256)) - 1, 16, 16)); + break; + } + + if (!frames) {} + + gSPSegment(POLY_OPA_DISP++, 8, Gfx_TexScroll(globalCtx->state.gfxCtx, (u8)frames, (u8)frames, 16, 16)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2597), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2599), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + func_8002ED80(&this->actor, globalCtx, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, this->primXluColor[0], this->primXluColor[1], + this->primXluColor[2], 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); + gSPDisplayList(POLY_XLU_DISP++, this->jewelDisplayList); + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 128, this->primOpaColor[0], this->primOpaColor[1], + this->primOpaColor[2], 255); + gDPSetEnvColor(POLY_OPA_DISP++, this->envOpaColor[0], this->envOpaColor[1], this->envOpaColor[2], 255); + gSPDisplayList(POLY_OPA_DISP++, this->jewelHolderDisplayList); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2620); +} + +/** + * Draw function for the Crystal Light Actor. + */ +void DemoEffect_DrawCrystalLight(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + DemoEffect* parent = (DemoEffect*)this->actor.parent; + u32 frames = globalCtx->gameplayFrames & 0xFFFF; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2634); + + if (parent != NULL) { + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 170, parent->triforceSpot.crystalLightOpacity); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 170, 255); + } + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 2) % 512, 512 - (frames % 512) - 1, 128, 128, 1, + 512 - ((frames * 2) % 512) - 1, 0, 64, 64)); + Matrix_Push(); + Matrix_RotateY(0.0f, MTXMODE_APPLY); + Matrix_RotateX((11.0 * M_PI) / 180.0, MTXMODE_APPLY); + Matrix_Translate(0.0f, 150.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2661), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCrystalLightDL); + Matrix_Pop(); + Matrix_Push(); + Matrix_RotateY((2.0f * M_PI) / 3.0f, MTXMODE_APPLY); + Matrix_RotateX((11.0 * M_PI) / 180.0, MTXMODE_APPLY); + Matrix_Translate(0.0f, 150.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2672), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCrystalLightDL); + Matrix_Pop(); + Matrix_Push(); + Matrix_RotateY((4.0f * M_PI) / 3.0f, MTXMODE_APPLY); + Matrix_RotateX((11.0 * M_PI) / 180.0, MTXMODE_APPLY); + Matrix_Translate(0.0f, 150.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2683), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCrystalLightDL); + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2688); +} + +/** + * Draw function for the Fire Ball Actor. + */ +void DemoEffect_DrawFireBall(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + u32 frames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2701); + gDPSetPrimColor(POLY_XLU_DISP++, 64, 64, 255, 200, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 255); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2709), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(POLY_XLU_DISP++, globalCtx->billboardMtx, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPSegment( + POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 32, 1, 0, 128 - ((frames * 20) % 128) - 1, 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, gCreationFireBallDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2723); +} + +/** + * Draw function for the God Lgt Actors. + * This draws either Din, Nayru, or Farore based on the colors set in the DemoEffect struct. + */ +void DemoEffect_DrawGodLgt(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + s32 pad; + u32 frames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2737); + + if (!DemoEffect_CheckCsAction(this, globalCtx, 2)) { + if (gSaveContext.entranceIndex == 0x00A0) { + if (gSaveContext.sceneSetupIndex == 4) { + if (globalCtx->csCtx.frames <= 680) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_GOD_FLYING - SFX_FLAG); + } + } else { + func_80078914(&this->actor.projectedPos, NA_SE_EV_GOD_FLYING - SFX_FLAG); + } + } else { + func_80078914(&this->actor.projectedPos, NA_SE_EV_GOD_FLYING - SFX_FLAG); + } + + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 4) % 512, 0, 128, 64, 1, (frames * 2) % 256, + 512 - ((frames * 70) % 512) - 1, 64, 32)); + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 16, 96, 1, (frames * 10) % 256, + 256 - ((frames * 30) % 512) - 1, 8, 32)); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, this->primXluColor[0], this->primXluColor[1], this->primXluColor[2], + 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Push(); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2801), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gGoldenGoddessAuraDL); + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + Matrix_Pop(); + + this->godLgt.rotation++; + if (this->godLgt.rotation > 120) { + this->godLgt.rotation = 0; + if (1) {} + } + + Matrix_RotateZ((((s32)this->godLgt.rotation) * 3.0f) * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2.0f, MTXMODE_APPLY); + Matrix_Translate(0.0f, -140.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(0.03f, 0.03f, 0.03f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2824), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGoldenGoddessBodyDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2829); +} + +/** + * Draw function for the Light Effect Actor. + */ +void DemoEffect_DrawLightEffect(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + u8* alpha; + Gfx* disp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2842); + + if (!DemoEffect_CheckCsAction(this, globalCtx, 1)) { + + if (this->light.flicker == 0) { + this->light.flicker = 1; + } else { + disp = (uintptr_t)gEffFlash1DL; // necessary to match, should be able to remove after fake matches are fixed + alpha = &this->light.alpha; + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, this->primXluColor[0], this->primXluColor[1], + this->primXluColor[2], *alpha); + gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); + Matrix_Scale(((this->light.scaleFlag & 1) * 0.05f) + 1.0f, ((this->light.scaleFlag & 1) * 0.05f) + 1.0f, + ((this->light.scaleFlag & 1) * 0.05f) + 1.0f, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(this->light.rotation * (M_PI / 180.0f), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2866), + G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); + if (disp) {}; + gSPDisplayList(POLY_XLU_DISP++, disp); + Matrix_Pop(); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(-(f32)this->light.rotation * (M_PI / 180.0f), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2874), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, disp); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2881); +} + +/** + * Draw function for the Blue Orb Actor. + */ +void DemoEffect_DrawBlueOrb(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + s32 pad2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2892); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 188, 255, 255, this->blueOrb.alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 255); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(this->blueOrb.rotation * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2901), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + this->blueOrb.rotation += 0x01F4; + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2907); +} + +/** + * Draw function for the Lgt Shower Actor. + */ +void DemoEffect_DrawLgtShower(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + s32 pad; + u32 frames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2921); + gDPSetPrimColor(POLY_XLU_DISP++, 64, 64, 255, 255, 160, this->lgtShower.alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 50, 200, 0, 255); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2927), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 5) % 1024, 0, 256, 64, 1, (frames * 10) % 128, + 512 - ((frames * 50) % 512), 32, 16)); + gSPDisplayList(POLY_XLU_DISP++, gEnliveningLightDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2942); +} + +/** + * Draw function for the Light Ring Actor. + */ +void DemoEffect_DrawLightRing(Actor* thisx, GlobalContext* globalCtx2) { + DemoEffect* this = (DemoEffect*)thisx; + GlobalContext* globalCtx = globalCtx2; + u32 frames = this->lightRing.timer; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2956); + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 170, 255, 255, this->lightRing.alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2963), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 5) % 64, 512 - ((frames * 2) % 512) - 1, 16, 128, + 1, 0, 0, 8, 1024)); + gSPDisplayList(POLY_XLU_DISP++, gGoldenGoddessLightRingDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2978); +} + +/** + * Draw function for the Triforce Spot Actor. + */ +void DemoEffect_DrawTriforceSpot(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + s32 pad; + Vtx* vertices = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gTriforceVtx)); + u32 frames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 2994); + if (gSaveContext.entranceIndex != 0x0400 || globalCtx->csCtx.frames < 885) { + func_80093D84(globalCtx->state.gfxCtx); + + if (this->triforceSpot.lightColumnOpacity > 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_AURORA - SFX_FLAG); + Matrix_Push(); + Matrix_Scale(1.0f, 2.4f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3011), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 256 - ((frames * 4) % 256) - 1, 64, 64, 1, 0, + 256 - ((frames * 2) % 256) - 1, 64, 32)); + vertices[86].n.a = vertices[87].n.a = vertices[88].n.a = vertices[89].n.a = vertices[92].n.a = + vertices[93].n.a = vertices[94].n.a = vertices[95].n.a = (s8)this->triforceSpot.lightColumnOpacity; + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 180, 255, 255, this->triforceSpot.lightColumnOpacity); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 150, 255); + gSPDisplayList(POLY_XLU_DISP++, gTriforceLightColumnDL); + Matrix_Pop(); + } + + if (this->triforceSpot.triforceSpotOpacity != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_TRIFORCE - SFX_FLAG); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3042), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->triforceSpot.triforceSpotOpacity < 250) { + func_8002ED80(&this->actor, globalCtx, 0); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2); + Matrix_RotateY(this->triforceSpot.rotation * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3053), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 16, 1, 0, 0, 16, 8)); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 160, this->triforceSpot.triforceSpotOpacity); + gDPSetEnvColor(POLY_XLU_DISP++, 170, 140, 0, 255); + gSPDisplayList(POLY_XLU_DISP++, gTriforceDL); + } else { + func_8002EBCC(&this->actor, globalCtx, 0); + func_80093D18(globalCtx->state.gfxCtx); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_PASS, G_RM_AA_ZB_OPA_SURF2); + Matrix_RotateY(this->triforceSpot.rotation * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3085), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 16, 1, 0, 0, 16, 8)); + gDPSetPrimColor(POLY_OPA_DISP++, 128, 128, 255, 255, 160, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 170, 140, 0, 255); + gSPDisplayList(POLY_OPA_DISP++, gTriforceDL); + } + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3112); +} + +/** + * Draw function for the Get Item Actors. + * This is either Medals or Light Arrows based on the drawId. + */ +void DemoEffect_DrawGetItem(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + if (!DemoEffect_CheckCsAction(this, globalCtx, 1) && !DemoEffect_CheckCsAction(this, globalCtx, 4)) { + if (!this->getItem.isLoaded) { + this->getItem.isLoaded = 1; + return; + } + func_8002EBCC(thisx, globalCtx, 0); + func_8002ED80(thisx, globalCtx, 0); + GetItem_Draw(globalCtx, this->getItem.drawId); + } +} + +/** + * Callback for the SkelCurve system to draw the animated limbs. + */ +s32 DemoEffect_DrawTimewarpLimbs(GlobalContext* globalCtx, SkelAnimeCurve* skelCuve, s32 limbIndex, void* thisx) { + s32 pad; + DemoEffect* this = (DemoEffect*)thisx; + u32 frames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3154); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, 170, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->envXluColor[0], this->envXluColor[1], this->envXluColor[2], 255); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (frames * 6) % 1024, 256 - ((frames * 16) % 256) - 1, 256, + 64, 1, (frames * 4) % 512, 128 - ((frames * 12) % 128) - 1, 128, 32)); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_effect.c", 3172); + + if (limbIndex == 0) { + LimbTransform* transform = &skelCuve->transforms[0]; + + transform->scale.y = 1024; + transform->scale.z = transform->scale.x = 1024; + } + + return 1; +} + +/** + * Draw function for the Time Warp Actors. + */ +void DemoEffect_DrawTimeWarp(Actor* thisx, GlobalContext* globalCtx) { + DemoEffect* this = (DemoEffect*)thisx; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u8 effectType = (this->actor.params & 0x00FF); + + if (effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE || effectType == DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL || + Flags_GetEnv(globalCtx, 1) || gSaveContext.sceneSetupIndex >= 4 || gSaveContext.entranceIndex == 0x0324) { + OPEN_DISPS(gfxCtx, "../z_demo_effect.c", 3201); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 25); + Matrix_Scale(2.0f, 2.0f, 2.0f, MTXMODE_APPLY); + SkelCurve_Draw(thisx, globalCtx, &this->skelCurve, DemoEffect_DrawTimewarpLimbs, NULL, 1, this); + CLOSE_DISPS(gfxCtx, "../z_demo_effect.c", 3216); + } +} + +/** + * Faces/rotates the Actor towards the current cutscene action end point. + */ +void DemoEffect_FaceToCsEndpoint(DemoEffect* this, Vec3f startPos, Vec3f endPos) { + s32 pad; + f32 x = endPos.x - startPos.x; + f32 z = endPos.z - startPos.z; + f32 xzDistance = sqrtf(SQ(x) + SQ(z)); + + this->actor.shape.rot.y = Math_FAtan2F(x, z) * (32768.0f / M_PI); + this->actor.shape.rot.x = Math_FAtan2F(-(endPos.y - startPos.y), xzDistance) * (32768.0f / M_PI); +} + +/** + * Moves the Actor towards the current cutscene action end point. + * Will only update the Actor's facing/rotation if the shouldUpdateFacing argument is true. + * The speed is based on the current progress in the cutscene action. + */ +void DemoEffect_MoveToCsEndpoint(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, s32 shouldUpdateFacing) { + Vec3f startPos; + Vec3f endPos; + f32 speed; + + startPos.x = globalCtx->csCtx.npcActions[csActionId]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[csActionId]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[csActionId]->startPos.z; + endPos.x = globalCtx->csCtx.npcActions[csActionId]->endPos.x; + endPos.y = globalCtx->csCtx.npcActions[csActionId]->endPos.y; + endPos.z = globalCtx->csCtx.npcActions[csActionId]->endPos.z; + + speed = DemoEffect_InterpolateCsFrames(globalCtx, csActionId); + + this->actor.world.pos.x = ((endPos.x - startPos.x) * speed) + startPos.x; + this->actor.world.pos.y = ((endPos.y - startPos.y) * speed) + startPos.y; + this->actor.world.pos.z = ((endPos.z - startPos.z) * speed) + startPos.z; + + if (shouldUpdateFacing) { + DemoEffect_FaceToCsEndpoint(this, startPos, endPos); + } +} + +/** + * Moves a GetItem actor towards the current cutscene action's endpoint. + */ +void DemoEffect_MoveGetItem(DemoEffect* this, GlobalContext* globalCtx, s32 csActionId, f32 speed) { + Vec3f endPos; + endPos.x = globalCtx->csCtx.npcActions[csActionId]->endPos.x; + endPos.y = globalCtx->csCtx.npcActions[csActionId]->endPos.y; + endPos.z = globalCtx->csCtx.npcActions[csActionId]->endPos.z; + DemoEffect_MoveTowardTarget(endPos, this, speed); +} + +/** + * Initializes the Actor's position to the current cutscene action's start point. + */ +void DemoEffect_InitPositionFromCsAction(DemoEffect* this, GlobalContext* globalCtx, s32 csActionIndex) { + f32 x = globalCtx->csCtx.npcActions[csActionIndex]->startPos.x; + f32 y = globalCtx->csCtx.npcActions[csActionIndex]->startPos.y; + f32 z = globalCtx->csCtx.npcActions[csActionIndex]->startPos.z; + + this->actor.world.pos.x = x; + this->actor.world.pos.y = y; + this->actor.world.pos.z = z; +} diff --git a/soh/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h b/soh/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h new file mode 100644 index 000000000..5434b1d36 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Effect/z_demo_effect.h @@ -0,0 +1,162 @@ +#ifndef Z_DEMO_EFFECT_H +#define Z_DEMO_EFFECT_H + +#include "ultra64.h" +#include "global.h" + +struct DemoEffect; + +typedef void (*DemoEffectFunc)(struct DemoEffect*, GlobalContext*); + +typedef struct { + /* 0x00 */ u8 timer; +} DemoEffectFireBall; + +typedef struct { + /* 0x00 */ u8 alpha; + /* 0x01 */ u8 scale; + /* 0x02 */ u8 pad; + /* 0x04 */ s16 rotation; +} DemoEffectBlueOrb; + +typedef struct { + /* 0x00 */ u8 alpha; + /* 0x01 */ u8 scaleFlag; + /* 0x02 */ u8 flicker; + /* 0x04 */ s16 rotation; +} DemoEffectLight; + +typedef struct { + /* 0x00 */ u8 alpha; +} DemoEffectLgtShower; + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 lightRingSpawnDelay; + /* 0x02 */ u8 rotation; + /* 0x04 */ s16 lightRingSpawnTimer; +} DemoEffectGodLgt; + +typedef struct { + /* 0x00 */ u8 timerIncrement; + /* 0x01 */ u8 alpha; + /* 0x02 */ u8 pad; + /* 0x04 */ s16 timer; +} DemoEffectLightRing; + +typedef struct { + /* 0x00 */ u8 triforceSpotOpacity; + /* 0x01 */ u8 lightColumnOpacity; + /* 0x02 */ u8 crystalLightOpacity; + /* 0x04 */ s16 rotation; +} DemoEffectTriforceSpot; + +typedef struct { + /* 0x00 */ u8 isPositionInit; + /* 0x01 */ u8 isLoaded; + /* 0x02 */ u8 drawId; + /* 0x04 */ s16 rotation; +} DemoEffectGetItem; + +typedef struct { + /* 0x00 */ u8 pad; + /* 0x01 */ u8 pad2; + /* 0x02 */ u8 pad3; + /* 0x04 */ s16 shrinkTimer; +} DemoEffectTimeWarp; + +typedef struct { + /* 0x00 */ u8 type; + /* 0x01 */ u8 isPositionInit; + /* 0x02 */ u8 alpha; + /* 0x04 */ s16 timer; +} DemoEffectJewel; + +typedef struct { + /* 0x00 */ u8 timer; +} DemoEffectDust; + +typedef struct DemoEffect { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnimeCurve skelCurve; + /* 0x016C */ u8 initObjectBankIndex; + /* 0x0170 */ Gfx* jewelDisplayList; + /* 0x0174 */ Gfx* jewelHolderDisplayList; + /* 0x0178 */ u8 primXluColor[3]; + /* 0x017B */ u8 envXluColor[3]; + /* 0x017E */ u8 primOpaColor[3]; + /* 0x0181 */ u8 envOpaColor[3]; + /* 0x0184 */ union { + DemoEffectFireBall fireBall; + DemoEffectBlueOrb blueOrb; + DemoEffectLight light; + DemoEffectLgtShower lgtShower; + DemoEffectGodLgt godLgt; + DemoEffectLightRing lightRing; + DemoEffectTriforceSpot triforceSpot; + DemoEffectGetItem getItem; + DemoEffectTimeWarp timeWarp; + DemoEffectJewel jewel; + DemoEffectDust dust; + }; + /* 0x018A */ s16 effectFlags; + /* 0x018C */ s16 csActionId; + /* 0x018E */ Vec3s jewelCsRotation; + /* 0x0194 */ DemoEffectFunc initUpdateFunc; + /* 0x0198 */ ActorFunc initDrawFunc; + /* 0x019C */ DemoEffectFunc updateFunc; +} DemoEffect; // size = 0x01A0 + +// These names come from the objects that correspond to this actor type. +typedef enum { + /* 0x00 */ DEMO_EFFECT_CRYSTAL_LIGHT, + /* 0x01 */ DEMO_EFFECT_FIRE_BALL, + /* 0x02 */ DEMO_EFFECT_BLUE_ORB, // Object is in GAMEPLAY_KEEP. Not a name from object. It's a blue orb. + /* 0x03 */ DEMO_EFFECT_LGT_SHOWER, + /* 0x04 */ DEMO_EFFECT_GOD_LGT_DIN, + /* 0x05 */ DEMO_EFFECT_GOD_LGT_NAYRU, + /* 0x06 */ DEMO_EFFECT_GOD_LGT_FARORE, + /* 0x07 */ DEMO_EFFECT_LIGHTRING_EXPANDING, + /* 0x08 */ DEMO_EFFECT_TRIFORCE_SPOT, + /* 0x09 */ DEMO_EFFECT_MEDAL_FIRE, + /* 0x0A */ DEMO_EFFECT_MEDAL_WATER, + /* 0x0B */ DEMO_EFFECT_MEDAL_FOREST, + /* 0x0C */ DEMO_EFFECT_MEDAL_SPIRIT, + /* 0x0D */ DEMO_EFFECT_MEDAL_SHADOW, + /* 0x0E */ DEMO_EFFECT_MEDAL_LIGHT, + /* 0x0F */ DEMO_EFFECT_TIMEWARP_MASTERSWORD, + /* 0x10 */ DEMO_EFFECT_LIGHTRING_SHRINKING, + /* 0x11 */ DEMO_EFFECT_LIGHTRING_TRIFORCE, + /* 0x12 */ DEMO_EFFECT_LIGHT, + /* 0x13 */ DEMO_EFFECT_JEWEL_KOKIRI, + /* 0x14 */ DEMO_EFFECT_JEWEL_GORON, + /* 0x15 */ DEMO_EFFECT_JEWEL_ZORA, + /* 0x16 */ DEMO_EFFECT_DUST, // Object is jewel, but this is really the dust in the ToT light arrow cutscene. + /* 0x17 */ DEMO_EFFECT_LIGHTARROW, + /* 0x18 */ DEMO_EFFECT_TIMEWARP_TIMEBLOCK_LARGE, + /* 0x19 */ DEMO_EFFECT_TIMEWARP_TIMEBLOCK_SMALL, + /* 0x1A */ DEMO_EFFECT_MAX_TYPE +} DemoEffectType; + +typedef enum { + /* 0x00 */ DEMO_EFFECT_LIGHT_RED, + /* 0x01 */ DEMO_EFFECT_LIGHT_BLUE, + /* 0x02 */ DEMO_EFFECT_LIGHT_GREEN, + /* 0x03 */ DEMO_EFFECT_LIGHT_ORANGE, + /* 0x04 */ DEMO_EFFECT_LIGHT_YELLOW, + /* 0x05 */ DEMO_EFFECT_LIGHT_PURPLE, + /* 0x06 */ DEMO_EFFECT_LIGHT_GREEN2 +} DemoEffectLightColor; + +typedef enum { + /* 0x00 */ GOD_LGT_DIN, + /* 0x01 */ GOD_LGT_NAYRU, + /* 0x02 */ GOD_LGT_FARORE +} DemoEffectGodLgtType; + +// params info +// type: (params & 0x00FF) +// light size: ((params & 0x0F00) >> 8) +// light color: ((params & 0xF000) >> 12) + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Ext/z_demo_ext.c b/soh/src/overlays/actors/ovl_Demo_Ext/z_demo_ext.c new file mode 100644 index 000000000..d5c514dda --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Ext/z_demo_ext.c @@ -0,0 +1,247 @@ +/* + * File: z_demo_ext.c + * Overlay: Demo_Ext + * Description: Magic Vortex in Silver Gauntlets Cutscene + */ + +#include "z_demo_ext.h" +#include "vt.h" +#include "objects/object_fhg/object_fhg.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0x00 */ EXT_WAIT, + /* 0x01 */ EXT_MAINTAIN, + /* 0x02 */ EXT_DISPELL +} DemoExtAction; + +typedef enum { + /* 0x00 */ EXT_DRAW_NOTHING, + /* 0x01 */ EXT_DRAW_VORTEX +} DemoExtDrawMode; + +void DemoExt_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoExt_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoExt_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoExt_Draw(Actor* thisx, GlobalContext* globalCtx); + +void DemoExt_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void DemoExt_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoExt* this = (DemoExt*)thisx; + + this->scrollIncr[0] = 25; + this->scrollIncr[1] = 40; + this->scrollIncr[2] = 5; + this->scrollIncr[3] = 30; + this->primAlpha = kREG(28) + 255; + this->envAlpha = kREG(32) + 255; + this->scale.x = kREG(19) + 400.0f; + this->scale.y = kREG(20) + 100.0f; + this->scale.z = kREG(21) + 400.0f; +} + +void DemoExt_PlayVortexSFX(DemoExt* this) { + if (this->alphaTimer <= (kREG(35) + 40.0f) - 15.0f) { + Audio_PlaySoundGeneral(NA_SE_EV_FANTOM_WARP_L - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } +} + +CsCmdActorAction* DemoExt_GetNpcAction(GlobalContext* globalCtx, s32 npcActionIndex) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[npcActionIndex]; + } + return NULL; +} + +void DemoExt_SetupWait(DemoExt* this) { + this->action = EXT_WAIT; + this->drawMode = EXT_DRAW_NOTHING; +} + +void DemoExt_SetupMaintainVortex(DemoExt* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = DemoExt_GetNpcAction(globalCtx, 5); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } + this->action = EXT_MAINTAIN; + this->drawMode = EXT_DRAW_VORTEX; +} + +void DemoExt_SetupDispellVortex(DemoExt* this) { + this->action = EXT_DISPELL; + this->drawMode = EXT_DRAW_VORTEX; +} + +void DemoExt_FinishClosing(DemoExt* this) { + this->alphaTimer += 1.0f; + if ((kREG(35) + 40.0f) <= this->alphaTimer) { + Actor_Kill(&this->actor); + } +} + +void DemoExt_CheckCsMode(DemoExt* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction = DemoExt_GetNpcAction(globalCtx, 5); + s32 csAction; + s32 previousCsAction; + + if (csCmdNPCAction != NULL) { + csAction = csCmdNPCAction->action; + previousCsAction = this->previousCsAction; + + if (csAction != previousCsAction) { + switch (csAction) { + case 1: + DemoExt_SetupWait(this); + break; + case 2: + DemoExt_SetupMaintainVortex(this, globalCtx); + break; + case 3: + DemoExt_SetupDispellVortex(this); + break; + default: + // "Demo_Ext_Check_DemoMode: there is no such action!" + osSyncPrintf("Demo_Ext_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->previousCsAction = csAction; + } + } +} + +void DemoExt_SetScrollAndRotation(DemoExt* this) { + s16* scrollIncr = this->scrollIncr; + s16* curScroll = this->curScroll; + s32 i; + + for (i = 3; i != 0; i--) { + curScroll[i] += scrollIncr[i]; + } + this->rotationPitch += (s16)(kREG(34) + 1000); +} + +void DemoExt_SetColorsAndScales(DemoExt* this) { + Vec3f* scale = &this->scale; + f32 shrinkFactor; + + shrinkFactor = ((kREG(35) + 40.0f) - this->alphaTimer) / (kREG(35) + 40.0f); + if (shrinkFactor < 0.0f) { + shrinkFactor = 0.0f; + } + + this->primAlpha = (u32)(kREG(28) + 255) * shrinkFactor; + this->envAlpha = (u32)(kREG(32) + 255) * shrinkFactor; + scale->x = (kREG(19) + 400.0f) * shrinkFactor; + scale->y = (kREG(20) + 100.0f) * shrinkFactor; + scale->z = (kREG(21) + 400.0f) * shrinkFactor; +} + +void DemoExt_Wait(DemoExt* this, GlobalContext* globalCtx) { + DemoExt_CheckCsMode(this, globalCtx); +} + +void DemoExt_MaintainVortex(DemoExt* this, GlobalContext* globalCtx) { + DemoExt_PlayVortexSFX(this); + DemoExt_SetScrollAndRotation(this); + DemoExt_CheckCsMode(this, globalCtx); +} + +void DemoExt_DispellVortex(DemoExt* this, GlobalContext* globalCtx) { + DemoExt_PlayVortexSFX(this); + DemoExt_SetScrollAndRotation(this); + DemoExt_SetColorsAndScales(this); + DemoExt_FinishClosing(this); +} + +static DemoExtActionFunc sActionFuncs[] = { + DemoExt_Wait, + DemoExt_MaintainVortex, + DemoExt_DispellVortex, +}; + +void DemoExt_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoExt* this = (DemoExt*)thisx; + + if ((this->action < EXT_WAIT) || (this->action > EXT_DISPELL) || sActionFuncs[this->action] == NULL) { + // "Main mode is abnormal!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sActionFuncs[this->action](this, globalCtx); + } +} + +void DemoExt_DrawNothing(Actor* thisx, GlobalContext* globalCtx) { +} + +void DemoExt_DrawVortex(Actor* thisx, GlobalContext* globalCtx) { + DemoExt* this = (DemoExt*)thisx; + Mtx* mtx; + GraphicsContext* gfxCtx; + s16* curScroll; + Vec3f* scale; + + scale = &this->scale; + gfxCtx = globalCtx->state.gfxCtx; + mtx = Graph_Alloc(gfxCtx, sizeof(Mtx)); + + OPEN_DISPS(gfxCtx, "../z_demo_ext.c", 460); + Matrix_Push(); + Matrix_Scale(scale->x, scale->y, scale->z, MTXMODE_APPLY); + Matrix_RotateZYX((s16)(kREG(16) + 0x4000), this->rotationPitch, kREG(18), MTXMODE_APPLY); + Matrix_Translate(kREG(22), kREG(23), kREG(24), MTXMODE_APPLY); + Matrix_ToMtx(mtx, "../z_demo_ext.c", 476); + Matrix_Pop(); + func_80093D84(gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, kREG(33) + 128, kREG(25) + 140, kREG(26) + 80, kREG(27) + 140, this->primAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, kREG(29) + 90, kREG(30) + 50, kREG(31) + 95, this->envAlpha); + + curScroll = this->curScroll; + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(gfxCtx, 0, curScroll[0], curScroll[1], 0x40, 0x40, 1, curScroll[2], curScroll[3], 0x40, 0x40)); + + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gPhantomWarpDL); + gSPPopMatrix(POLY_XLU_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_ext.c", 512); +} + +static DemoExtDrawFunc sDrawFuncs[] = { + DemoExt_DrawNothing, + DemoExt_DrawVortex, +}; + +void DemoExt_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoExt* this = (DemoExt*)thisx; + + if ((this->drawMode < EXT_DRAW_NOTHING) || (this->drawMode > EXT_DRAW_VORTEX) || + sDrawFuncs[this->drawMode] == NULL) { + // "Draw mode is abnormal!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sDrawFuncs[this->drawMode](thisx, globalCtx); + } +} + +const ActorInit Demo_Ext_InitVars = { + ACTOR_DEMO_EXT, + ACTORCAT_NPC, + FLAGS, + OBJECT_FHG, + sizeof(DemoExt), + (ActorFunc)DemoExt_Init, + (ActorFunc)DemoExt_Destroy, + (ActorFunc)DemoExt_Update, + (ActorFunc)DemoExt_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_Demo_Ext/z_demo_ext.h b/soh/src/overlays/actors/ovl_Demo_Ext/z_demo_ext.h new file mode 100644 index 000000000..9a446b7c0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Ext/z_demo_ext.h @@ -0,0 +1,26 @@ +#ifndef Z_DEMO_EXT_H +#define Z_DEMO_EXT_H + +#include "ultra64.h" +#include "global.h" + +struct DemoExt; + +typedef void (*DemoExtActionFunc)(struct DemoExt*, GlobalContext*); +typedef void (*DemoExtDrawFunc)(Actor*, GlobalContext*); + +typedef struct DemoExt { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; + /* 0x014C */ s32 drawMode; + /* 0x0154 */ s32 previousCsAction; + /* 0x015E */ s16 scrollIncr[4]; + /* 0x0160 */ s16 curScroll[4]; + /* 0x0168 */ s16 rotationPitch; + /* 0x016C */ f32 alphaTimer; + /* 0x0170 */ s32 primAlpha; + /* 0x0174 */ s32 envAlpha; + /* 0x0178 */ Vec3f scale; +} DemoExt; // size = 0x0184 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Geff/z_demo_geff.c b/soh/src/overlays/actors/ovl_Demo_Geff/z_demo_geff.c new file mode 100644 index 000000000..b19bf92f7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Geff/z_demo_geff.c @@ -0,0 +1,232 @@ +/* + * File: z_demo_geff.c + * Overlay: Demo_Geff + * Description: Ganon's Lair Rubble Fragment + */ + +#include "z_demo_geff.h" +#include "objects/object_geff/object_geff.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoGeff_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoGeff_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoGeff_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoGeff_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80978030(DemoGeff* this, GlobalContext* globalCtx); + +void func_809783D4(DemoGeff* this, GlobalContext* globalCtx); +void func_80978308(DemoGeff* this, GlobalContext* globalCtx); + +void func_809784D4(DemoGeff* this, GlobalContext* globalCtx); +void func_80978344(DemoGeff* this, GlobalContext* globalCtx); + +static s16 sObjectIDs[] = { + OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, OBJECT_GEFF, +}; + +static DemoGeffInitFunc sInitFuncs[] = { + func_80978030, func_80978030, func_80978030, func_80978030, func_80978030, + func_80978030, func_80978030, func_80978030, func_80978030, +}; + +static DemoGeffActionFunc sActionFuncs[] = { + func_809783D4, + func_80978308, +}; + +static DemoGeffDrawFunc sDrawFuncs[] = { + func_809784D4, + func_80978344, +}; + +const ActorInit Demo_Geff_InitVars = { + ACTOR_DEMO_GEFF, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GEFF, + sizeof(DemoGeff), + (ActorFunc)DemoGeff_Init, + (ActorFunc)DemoGeff_Destroy, + (ActorFunc)DemoGeff_Update, + (ActorFunc)DemoGeff_Draw, + NULL, +}; + +void DemoGeff_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void DemoGeff_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoGeff* this = (DemoGeff*)thisx; + + if (this->actor.params < 0 || this->actor.params >= 9) { + osSyncPrintf(VT_FGCOL(RED) "Demo_Geff_Actor_ct:arg_dataがおかしい!!!!!!!!!!!!\n" VT_RST); + Actor_Kill(&this->actor); + return; + } + this->action = 0; + this->drawConfig = 0; +} + +void func_80977EA8(GlobalContext* globalCtx, Gfx* dlist) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_demo_geff.c", 181); + + func_80093D18(gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_geff.c", 183), + G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, dlist); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_geff.c", 188); +} + +void func_80977F80(DemoGeff* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 objBankIndex = this->objBankIndex; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_demo_geff.c", 204); + + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[objBankIndex].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[objBankIndex].segment); + + // Necessary to match + if (!globalCtx) {} + + CLOSE_DISPS(gfxCtx, "../z_demo_geff.c", 212); +} + +void func_80978030(DemoGeff* this, GlobalContext* globalCtx) { + Vec3f* thisScale = &this->actor.scale; + + this->action = 1; + this->drawConfig = 1; + + switch (this->actor.params) { + case 0: + case 3: + case 6: + thisScale->x = (kREG(7) * 0.01f) + 0.3f; + thisScale->y = (kREG(8) * 0.01f) + 0.3f; + thisScale->z = (kREG(9) * 0.01f) + 0.3f; + break; + case 1: + case 4: + case 7: + thisScale->x = (kREG(10) * 0.01f) + 0.15f; + thisScale->y = (kREG(11) * 0.01f) + 0.29f; + thisScale->z = (kREG(12) * 0.01f) + 0.12f; + break; + default: + thisScale->x = (kREG(13) * 0.01f) + 0.1f; + thisScale->y = (kREG(14) * 0.01f) + 0.15f; + thisScale->z = (kREG(15) * 0.01f) + 0.2f; + break; + } +} + +void func_809781FC(DemoGeff* this, GlobalContext* globalCtx) { + s32 targetParams = 2; + Actor* propIt; + + if (this->demoGt == NULL) { + propIt = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + if ((this->actor.params != 0) && (this->actor.params != 1) && (this->actor.params != 2)) { + targetParams = 2; + } else { + targetParams = 1; + } + while (propIt != NULL) { + if (propIt->id == ACTOR_DEMO_GT && propIt->params == targetParams) { + this->deltaPosX = this->actor.world.pos.x - propIt->world.pos.x; + this->deltaPosY = this->actor.world.pos.y - propIt->world.pos.y; + this->deltaPosZ = this->actor.world.pos.z - propIt->world.pos.z; + this->demoGt = (DemoGt*)propIt; + } + propIt = propIt->next; + } + } +} + +void func_809782A0(DemoGeff* this, GlobalContext* globalCtx) { + DemoGt* demoGt = this->demoGt; + s16 params = this->actor.params; + + if (demoGt != NULL && (params != 6) && (params != 7) && (params != 8)) { + this->actor.world.pos.x = demoGt->dyna.actor.world.pos.x + this->deltaPosX; + this->actor.world.pos.y = demoGt->dyna.actor.world.pos.y + this->deltaPosY; + this->actor.world.pos.z = demoGt->dyna.actor.world.pos.z + this->deltaPosZ; + } +} + +void func_80978308(DemoGeff* this, GlobalContext* globalCtx) { + func_809781FC(this, globalCtx); + func_809782A0(this, globalCtx); + func_80978030(this, globalCtx); +} + +void func_80978344(DemoGeff* this, GlobalContext* globalCtx) { + func_80977EA8(globalCtx, gGanonRubbleDL); +} + +void func_80978370(DemoGeff* this, GlobalContext* globalCtx) { + s16 params = this->actor.params; + DemoGeffInitFunc initFunc = sInitFuncs[params]; + if (initFunc == NULL) { + osSyncPrintf(VT_FGCOL(RED) " Demo_Geff_main_init:初期化処理がおかしいarg_data = %d!\n" VT_RST, params); + Actor_Kill(&this->actor); + return; + } + initFunc(this, globalCtx); +} + +void func_809783D4(DemoGeff* this, GlobalContext* globalCtx) { + ObjectContext* objCtx = &globalCtx->objectCtx; + Actor* thisx = &this->actor; + s32 params = thisx->params; + s16 objectId = sObjectIDs[params]; + s32 objBankIndex = Object_GetIndex(objCtx, objectId); + s32 pad; + + if (objBankIndex < 0) { + osSyncPrintf(VT_FGCOL(RED) "Demo_Geff_main_bank:バンクを読めない arg_data = %d!\n" VT_RST, params); + Actor_Kill(thisx); + return; + } + if (Object_IsLoaded(objCtx, objBankIndex)) { + this->objBankIndex = objBankIndex; + func_80978370(this, globalCtx); + } +} + +void DemoGeff_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoGeff* this = (DemoGeff*)thisx; + + if (this->action < 0 || this->action >= 2 || sActionFuncs[this->action] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void func_809784D4(DemoGeff* this, GlobalContext* globalCtx) { +} + +void DemoGeff_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoGeff* this = (DemoGeff*)thisx; + s32 drawConfig = this->drawConfig; + + if (drawConfig < 0 || drawConfig >= 2 || sDrawFuncs[drawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + if (drawConfig != 0) { + func_80977F80(this, globalCtx); + } + sDrawFuncs[drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Geff/z_demo_geff.h b/soh/src/overlays/actors/ovl_Demo_Geff/z_demo_geff.h new file mode 100644 index 000000000..a16705a83 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Geff/z_demo_geff.h @@ -0,0 +1,26 @@ +#ifndef Z_DEMO_GEFF_H +#define Z_DEMO_GEFF_H + +#include "ultra64.h" +#include "global.h" + +#include "../ovl_Demo_Gt/z_demo_gt.h" + +struct DemoGeff; + +typedef void (*DemoGeffInitFunc)(struct DemoGeff*, GlobalContext*); +typedef void (*DemoGeffActionFunc)(struct DemoGeff*, GlobalContext*); +typedef void (*DemoGeffDrawFunc)(struct DemoGeff*, GlobalContext*); + +typedef struct DemoGeff { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; + /* 0x0150 */ s32 drawConfig; + /* 0x0154 */ s32 objBankIndex; + /* 0x0158 */ DemoGt* demoGt; + /* 0x015C */ f32 deltaPosX; + /* 0x0160 */ f32 deltaPosY; + /* 0x0164 */ f32 deltaPosZ; +} DemoGeff; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c b/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c new file mode 100644 index 000000000..66413e661 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c @@ -0,0 +1,1463 @@ +/* + * File: z_demo_gj.c + * Overlay: Demo_Gj + * Description: Ganon battle rubble. + */ + +#include "z_demo_gj.h" +#include "objects/object_gj/object_gj.h" +#include "objects/object_geff/object_geff.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoGj_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoGj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoGj_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoGj_Draw(Actor* thisx, GlobalContext* globalCtx); + +static ColliderCylinderInitType1 sCylinderInit1 = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 30, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInitType1 sCylinderInit2 = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 25, 110, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInitType1 sCylinderInit3 = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 25, 200, 0, { 0, 0, 0 } }, +}; + +typedef void (*DemoGjUpdateFunc)(DemoGj*, GlobalContext*); +typedef void (*DemoGjDrawFunc)(DemoGj*, GlobalContext*); + +// bits 11-15 +s32 DemoGj_GetCollectibleType(DemoGj* this) { + s32 params = this->dyna.actor.params >> 0xB; + + return params & 0x1F; +} + +// bits 8-10 +s32 DemoGj_GetCollectibleAmount(DemoGj* this) { + s32 params = this->dyna.actor.params >> 0x8; + + return params & 7; +} + +// bits 0-7 +s32 DemoGj_GetType(DemoGj* this) { + s32 params = this->dyna.actor.params; + + return params & 0xFF; +} + +void DemoGj_InitCylinder(DemoGj* this, GlobalContext* globalCtx, ColliderCylinder* cylinder, + ColliderCylinderInitType1* cylinderInit) { + Collider_InitCylinder(globalCtx, cylinder); + Collider_SetCylinderType1(globalCtx, cylinder, &this->dyna.actor, cylinderInit); +} + +s32 DemoGj_HitByExplosion(DemoGj* this, GlobalContext* globalCtx, ColliderCylinder* cylinder) { + if (Actor_GetCollidedExplosive(globalCtx, &cylinder->base) != NULL) { + return true; + } + return false; +} + +void DemoGj_DestroyCylinder(DemoGj* this, GlobalContext* globalCtx) { + switch (DemoGj_GetType(this)) { + case DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_1: + Collider_DestroyCylinder(globalCtx, &this->cylinders[0]); + Collider_DestroyCylinder(globalCtx, &this->cylinders[1]); + Collider_DestroyCylinder(globalCtx, &this->cylinders[2]); + break; + + case DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_2: + Collider_DestroyCylinder(globalCtx, &this->cylinders[0]); + Collider_DestroyCylinder(globalCtx, &this->cylinders[1]); + Collider_DestroyCylinder(globalCtx, &this->cylinders[2]); + break; + + case DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_TALL: + Collider_DestroyCylinder(globalCtx, &this->cylinders[0]); + break; + } +} + +void DemoGj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoGj* this = (DemoGj*)thisx; + + DemoGj_DestroyCylinder(this, globalCtx); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void DemoGj_PlayExplosionSfx(DemoGj* this, GlobalContext* globalCtx) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 50, NA_SE_EV_GRAVE_EXPLOSION); +} + +void DemoGj_SpawnSmoke(GlobalContext* globalCtx, Vec3f* pos, f32 arg2) { + static Vec3f velocity = { 0.0f, 6.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 0, 0, 0, 0 }; + static Color_RGBA8 envColor = { 0, 0, 0, 0 }; + f32 temp = arg2 * 0.2f; + + if (1) {} + func_800283D4(globalCtx, pos, &velocity, &accel, &primColor, &envColor, temp * Rand_ZeroOne() + arg2, 15, 90); +} + +void DemoGj_DropCollectible(DemoGj* this, GlobalContext* globalCtx) { + Vec3f* pos = &this->dyna.actor.world.pos; + s16 collectible = DemoGj_GetCollectibleType(this); + s32 amount = DemoGj_GetCollectibleAmount(this); + s32 i; + + for (i = 0; i < amount; i++) { + Item_DropCollectible(globalCtx, pos, collectible); + } +} + +void DemoGj_Explode(DemoGj* this, GlobalContext* globalCtx, Vec3f* initialPos, Vec3f* direction) { + Vec3f explosionPos; + Vec3f velocity; + s32 phi_s0; + f32 aux; + s16 theta = 0; + s32 i; + + for (i = 0; i < 6; i++) { + explosionPos.x = Math_SinS(theta) * 16.0f; + explosionPos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + explosionPos.z = Math_CosS(theta) * 16.0f; + + velocity.x = (explosionPos.x * 0.6f) + (12.0f * direction->x); + velocity.y = (Rand_ZeroOne() * 36.0f) + 6.0f; + velocity.z = (explosionPos.z * 0.6f) + (12.0f * direction->z); + + explosionPos.x += initialPos->x; + explosionPos.y += initialPos->y; + explosionPos.z += initialPos->z; + + aux = Rand_ZeroOne(); + if (aux < 0.1f) { + phi_s0 = 0x61; + } else if (aux < 0.7f) { + phi_s0 = 0x41; + } else { + phi_s0 = 0x21; + } + + Gfx* gfx = ResourceMgr_LoadGfxByName(gGanonRubbleDL); + + EffectSsKakera_Spawn(globalCtx, &explosionPos, &velocity, initialPos, -200, phi_s0, 10, 10, 0, + Rand_ZeroOne() * 20.0f + 20.0f, 20, 300, (s32)(Rand_ZeroOne() * 30.0f) + 30, -1, + OBJECT_GEFF, gfx); + + theta += 0x2AAA; + } + + DemoGj_PlayExplosionSfx(this, globalCtx); +} + +s32 DemoGj_IsSceneInvalid(void) { + if (gSaveContext.sceneSetupIndex < 4) { + return false; + } + return true; +} + +s32 DemoGj_FindGanon(DemoGj* this, GlobalContext* globalCtx) { + Actor* actor; + + if (this->ganon == NULL) { + actor = globalCtx->actorCtx.actorLists[ACTORCAT_BOSS].head; + + while (actor != NULL) { + if (actor->id == ACTOR_BOSS_GANON2) { + this->ganon = (BossGanon2*)actor; + + // "Demo_Gj_Search_Boss_Ganon %d: Discover Ganon !!!!" + osSyncPrintf("Demo_Gj_Search_Boss_Ganon %d:ガノン発見!!!!\n", this->dyna.actor.params); + return true; + } + actor = actor->next; + } + + // "Demo_Gj_Search_Boss_Ganon %d: I couldn't find Ganon" + osSyncPrintf("Demo_Gj_Search_Boss_Ganon %d:ガノン発見出来ず\n", this->dyna.actor.params); + return false; + } + //! @bug: Missing return value when `this->ganon` is already set. +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void DemoGj_InitCommon(DemoGj* this, GlobalContext* globalCtx, CollisionHeader* header) { + s32 pad[3]; + CollisionHeader* newHeader; + + if (header != NULL) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + newHeader = NULL; + CollisionHeader_GetVirtual(header, &newHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, newHeader); + } +} + +// TODO: find a better name +s32 DemoGj_InitSetIndexes(DemoGj* this, GlobalContext* globalCtx, s32 updateMode, s32 drawConfig, + CollisionHeader* header) { + if (!DemoGj_IsSceneInvalid()) { + this->updateMode = updateMode; + this->drawConfig = drawConfig; + DemoGj_InitCommon(this, globalCtx, header); + return true; + } + Actor_Kill(&this->dyna.actor); + return false; +} + +void DemoGj_DrawCommon(DemoGj* this, GlobalContext* globalCtx, Gfx* displayList) { + if (kREG(0) == 0) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_demo_gj.c", 1163); + + func_80093D18(gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_gj.c", 1165), + G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, displayList); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gj.c", 1169); + } +} + +void DemoGj_DrawRotated(DemoGj* this, GlobalContext* globalCtx, Gfx* displayList) { + s32 pad; + GraphicsContext* gfxCtx; + s16 x = this->rotationVec.x; + s16 y = this->rotationVec.y; + s16 z = this->rotationVec.z; + s32 pad2; + Mtx* matrix; + + gfxCtx = globalCtx->state.gfxCtx; + matrix = Graph_Alloc(gfxCtx, sizeof(Mtx)); + + OPEN_DISPS(gfxCtx, "../z_demo_gj.c", 1187); + + Matrix_Push(); + Matrix_RotateZYX(x, y, z, MTXMODE_APPLY); + Matrix_ToMtx(matrix, "../z_demo_gj.c", 1193); + Matrix_Pop(); + + func_80093D18(gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, matrix, G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, displayList); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gj.c", 1201); +} + +void DemoGj_SetupRotation(DemoGj* this, GlobalContext* globalCtx) { + f32 yPosition = this->dyna.actor.world.pos.y; + f32* yVelocity = &this->dyna.actor.velocity.y; + f32* speedXZ = &this->dyna.actor.speedXZ; + Vec3s* unk_172 = &this->unk_172; + f32 verticalTranslation; + Vec3f vec; + f32 verticalFactor; + f32 xzPlaneFactor; + + switch (DemoGj_GetType(this)) { + case DEMOGJ_TYPE_RUBBLE_PILE_1: + verticalTranslation = kREG(23); + vec.x = kREG(24) * 0.01f + 1.0f; + vec.y = kREG(25) * 0.01f + 1.0f; + vec.z = kREG(26) * 0.01f + 1.0f; + verticalFactor = kREG(27) * 0.01f + -1.0f; + xzPlaneFactor = kREG(28) * 0.01f + 1.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_2: + verticalTranslation = kREG(36); + vec.x = kREG(37) * 0.01f + 1.0f; + vec.y = kREG(38) * 0.01f + 1.0f; + vec.z = kREG(39) * 0.01f + 1.0f; + verticalFactor = kREG(40) * 0.01f + -1.0f; + xzPlaneFactor = kREG(41) * 0.01f + 1.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_3: + verticalTranslation = kREG(49); + vec.x = kREG(50) * 0.01f + 1.0f; + vec.y = kREG(51) * 0.01f + 1.0f; + vec.z = kREG(52) * 0.01f + 1.0f; + verticalFactor = kREG(53) * 0.01f + -1.0f; + xzPlaneFactor = kREG(54) * 0.01f + 1.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_4: + verticalTranslation = kREG(62); + vec.x = kREG(63) * 0.01f + 1.0f; + vec.y = kREG(64) * 0.01f + 1.0f; + vec.z = kREG(65) * 0.01f + 1.0f; + verticalFactor = kREG(66) * 0.01f + -1.0f; + xzPlaneFactor = kREG(67) * 0.01f + 1.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_5: + verticalTranslation = kREG(75); + vec.x = kREG(76) * 0.01f + 1.0f; + vec.y = kREG(77) * 0.01f + 1.0f; + vec.z = kREG(78) * 0.01f + 1.0f; + verticalFactor = kREG(79) * 0.01f + -1.0f; + xzPlaneFactor = kREG(80) * 0.01f + 1.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_6: + verticalTranslation = kREG(88); + vec.x = kREG(89) * 0.01f + 1.0f; + vec.y = kREG(90) * 0.01f + 1.0f; + vec.z = kREG(91) * 0.01f + 1.0f; + verticalFactor = kREG(92) * 0.01f + -1.0f; + xzPlaneFactor = kREG(93) * 0.01f + 1.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_7: + verticalTranslation = kREG(10) + -190.0f; + vec.x = kREG(9) * 0.01f + 1.0f; + vec.y = kREG(8) * 0.01f + 1.0f; + vec.z = kREG(7) * 0.01f + 1.0f; + verticalFactor = kREG(6) * 0.01f + -1.0f; + xzPlaneFactor = kREG(5) * 0.01f + 1.0f; + break; + + default: + // "Demo_Gj_common_Reflect : This arg_data is not supported = %d" + osSyncPrintf(VT_FGCOL(RED) "Demo_Gj_common_Reflect : そんなarg_dataには対応していない = %d\n" VT_RST, + this->dyna.actor.params); + return; + } + + yPosition += verticalTranslation; + if (yPosition <= 1086.0f && (*yVelocity < 0.0f)) { + if (!this->isRotated) { + *yVelocity *= verticalFactor; + *speedXZ *= xzPlaneFactor; + + unk_172->x *= vec.x; + unk_172->y *= vec.y; + unk_172->z *= vec.z; + + if (*yVelocity <= -this->dyna.actor.gravity) { + *yVelocity = 0.0f; + *speedXZ = 0.0f; + + unk_172->x = 0; + unk_172->y = 0; + unk_172->z = 0; + } + + this->isRotated = true; + } + } +} + +/* + * Returns true if `ganon->unk_314` is equals to `arg1`. + * `ganon->unk_314` can have the following values: + * 0: Before the battle has started. + * 1: When is set: Ganondorf starts rising from the rubble. + * What is happening: Ganondorf is moving vertically and has vertical velocity. + * Proposed name: BOSSGANON2_MODE_GANONDORF_RISING + * 2: When is set: Ganondorf has stopped rising in air. + * What is happening: The camera is in front of him, focusing the clouds and going down to focus him. + * Proposed name: BOSSGANON2_MODE_GANONDORF_FLOATING + * 3: When is set: The camera has stopped moving and is focusing him. + * What is happening: Ganondorf raises his hand, shows the triforce and transforms into Ganon. The battle starts. + * This value is set during the whole real fight against Ganon. Without and with Master Sword. + * Proposed name: BOSSGANON2_MODE_GANON_FIGHTING + * 4: When is set: Link has hit Ganon's tail for last time with Master Sword. + * What is happening: Ganon falls to the floor, Zelda uses her magic and tells Link to kill him. + * Proposed name: BOSSGANON2_MODE_GANON_DEFEATED + * + * Those values should probably be defined as macros or enums in `ovl_Boss_Ganon2/z_boss_ganon2.h`. + * Proposed name for the function: `s32 DemoGj_CheckGanonMode(DemoGj* this, u8 mode)` + */ +s32 func_809797E4(DemoGj* this, u8 arg1) { + BossGanon2* ganon = this->ganon; + + if ((ganon != NULL) && (ganon->unk_314 == arg1)) { + return true; + } + return false; +} + +s32 DemoGj_IsGanondorfRisingFromRubble(DemoGj* this, GlobalContext* globalCtx) { + return func_809797E4(this, 1); +} + +// Ganondorf has stopped rising into the air and is just floating. Just before he transforms. +s32 DemoGj_IsGanondorfFloatingInAir(DemoGj* this, GlobalContext* globalCtx) { + return func_809797E4(this, 2); +} + +void DemoGj_SetupMovement(DemoGj* this, GlobalContext* globalCtx) { + Actor* actor = &this->dyna.actor; + Player* player; + Vec3f* pos = &actor->world.pos; + Vec3s* unk_172; + f32 xDistance; + f32 zDistance; + + if (this->ganon != NULL) { + xDistance = actor->world.pos.x - this->ganon->actor.world.pos.x; + zDistance = actor->world.pos.z - this->ganon->actor.world.pos.z; + unk_172 = &this->unk_172; + + switch (DemoGj_GetType(this)) { + case DEMOGJ_TYPE_RUBBLE_PILE_1: + actor->speedXZ = kREG(16) + 10.0f; + actor->velocity.y = kREG(17) + 40.0f; + unk_172->x = kREG(18); + unk_172->y = kREG(19) + 0x3E8; + unk_172->z = kREG(20) + 0xBB8; + actor->minVelocityY = kREG(21) * 0.01f + -29.0f; + actor->gravity = kREG(22) * 0.01f + -5.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_2: + actor->speedXZ = kREG(29) + 10.0f; + actor->velocity.y = kREG(30) + 40.0f; + unk_172->x = kREG(31); + unk_172->y = kREG(32) + 0x3E8; + unk_172->z = kREG(33) + 0xBB8; + actor->minVelocityY = kREG(34) * 0.01f + -29.0f; + actor->gravity = kREG(35) * 0.01f + -5.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_3: + actor->speedXZ = kREG(42) + 10.0f; + actor->velocity.y = kREG(43) + 40.0f; + unk_172->x = kREG(44); + unk_172->y = kREG(45) + 0x3E8; + unk_172->z = kREG(46) + 0xBB8; + actor->minVelocityY = kREG(47) * 0.01f + -29.0f; + actor->gravity = kREG(48) * 0.01f + -5.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_4: + actor->speedXZ = kREG(55) + 10.0f; + actor->velocity.y = kREG(56) + 40.0f; + unk_172->x = kREG(57); + unk_172->y = kREG(58) + 0x3E8; + unk_172->z = kREG(59) + 0xBB8; + actor->minVelocityY = kREG(60) * 0.01f + -29.0f; + actor->gravity = kREG(61) * 0.01f + -5.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_5: + actor->speedXZ = kREG(68) + 10.0f; + actor->velocity.y = kREG(69) + 40.0f; + unk_172->x = kREG(70); + unk_172->y = kREG(71) + 0x3E8; + unk_172->z = kREG(72) + 0xBB8; + actor->minVelocityY = kREG(73) * 0.01f + -29.0f; + actor->gravity = kREG(74) * 0.01f + -5.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_6: + actor->speedXZ = kREG(81) + 10.0f; + actor->velocity.y = kREG(82) + 40.0f; + unk_172->x = kREG(83); + unk_172->y = kREG(84) + 0x3E8; + unk_172->z = kREG(85) + 0xBB8; + actor->minVelocityY = kREG(86) * 0.01f + -29.0f; + actor->gravity = kREG(87) * 0.01f + -5.0f; + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_7: + actor->speedXZ = kREG(94) + 10.0f; + actor->velocity.y = kREG(95) + 70.0f; + unk_172->x = kREG(15); + unk_172->y = kREG(14) + 0x3E8; + unk_172->z = kREG(13) + 0xBB8; + actor->minVelocityY = kREG(12) * 0.01f + -29.0f; + actor->gravity = kREG(11) * 0.01f + -5.0f; + break; + + default: + // "Demo_Gj_Setup_Move_common : This arg_data is not supported = %d" + osSyncPrintf(VT_FGCOL(RED) "Demo_Gj_Setup_Move_common : そんなarg_dataには対応していない = %d\n" VT_RST, + actor->params); + break; + } + + if (xDistance == 0.0f && zDistance == 0.0f) { + player = GET_PLAYER(globalCtx); + xDistance = player->actor.world.pos.x - pos->x; + zDistance = player->actor.world.pos.z - pos->z; + + if (xDistance != 0.0f || zDistance != 0.0f) { + actor->world.rot.y = (Math_FAtan2F(xDistance, zDistance) * (0x8000 / M_PI)); + } + } else { + actor->world.rot.y = (Math_FAtan2F(xDistance, zDistance) * (0x8000 / M_PI)); + } + } +} + +void DemoGj_CheckIfTransformedIntoGanon(DemoGj* this) { + if (func_809797E4(this, 3)) { + this->isTransformedIntoGanon = true; + } +} + +void DemoGj_InitRubblePile1(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 1, 2, &gGanonsCastleRubble2Col); +} + +void func_8097A000(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(18)); + this->rotationVec.y += (s16)(kREG(19) + 1000); + this->rotationVec.z += (s16)(kREG(20) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void DemoGj_SpawnSmokePreBattle1(DemoGj* this, GlobalContext* globalCtx) { + static Vec3f pos = { -371.0f, 1188.0f, -303.0f }; + u32 gameplayFrames; + + if (!this->isTransformedIntoGanon) { + gameplayFrames = globalCtx->gameplayFrames % 3; + + if (1) {} + if (gameplayFrames == 0) { + if (!globalCtx->gameplayFrames) {} + DemoGj_SpawnSmoke(globalCtx, &pos, 300.0f); + } + + DemoGj_CheckIfTransformedIntoGanon(this); + } +} + +void func_8097A0E4(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 8; + this->drawConfig = 9; + } +} + +void func_8097A130(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097A160 +void DemoGj_Update01(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097A0E4(this, globalCtx); +} + +// func_8097A190 +void DemoGj_Update08(DemoGj* this, GlobalContext* globalCtx) { + func_8097A000(this, globalCtx); + func_8097A130(this, globalCtx); +} + +void DemoGj_DrawRubble2(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble2DL); +} + +void DemoGj_DrawRotatedRubble2(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubble2DL); +} + +void DemoGj_InitRubblePile2(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 2, 3, &gGanonsCastleRubble3Col); +} + +void func_8097A238(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(31)); + this->rotationVec.y += (s16)(kREG(32) + 1000); + this->rotationVec.z += (s16)(kREG(33) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void DemoGj_SpawnSmokePreBattle2(DemoGj* this, GlobalContext* globalCtx) { + static Vec3f pos = { -119.0f, 1056.0f, -147.0f }; + u32 gameplayFrames; + + if (!this->isTransformedIntoGanon) { + gameplayFrames = globalCtx->gameplayFrames % 3; + + if (1) {} + if (gameplayFrames == 1) { + if (!globalCtx->gameplayFrames) {} + DemoGj_SpawnSmoke(globalCtx, &pos, 300.0f); + } + + DemoGj_CheckIfTransformedIntoGanon(this); + } +} + +void func_8097A320(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 9; + this->drawConfig = 10; + } +} + +void func_8097A36C(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097A39C +void DemoGj_Update02(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097A320(this, globalCtx); +} + +// func_8097A3CC +void DemoGj_Update09(DemoGj* this, GlobalContext* globalCtx) { + func_8097A238(this, globalCtx); + func_8097A36C(this, globalCtx); +} + +void DemoGj_DrawRubble3(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble3DL); +} + +void DemoGj_DrawRotatedRubble3(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubble3DL); +} + +void DemoGj_InitRubblePile3(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 3, 4, &gGanonsCastleRubble4Col); +} + +void func_8097A474(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(44)); + this->rotationVec.y += (s16)(kREG(45) + 1000); + this->rotationVec.z += (s16)(kREG(46) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void func_8097A4F0(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 10; + this->drawConfig = 11; + } +} + +void func_8097A53C(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097A56C +void DemoGj_Update03(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097A4F0(this, globalCtx); +} + +// func_8097A59C +void DemoGj_Update10(DemoGj* this, GlobalContext* globalCtx) { + func_8097A474(this, globalCtx); + func_8097A53C(this, globalCtx); +} + +void DemoGj_DrawRubble4(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble4DL); +} + +void DemoGj_DrawRotatedRubble4(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubble4DL); +} + +void DemoGj_InitRubblePile4(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 4, 5, &gGanonsCastleRubble5Col); +} + +void func_8097A644(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(57)); + this->rotationVec.y += (s16)(kREG(58) + 1000); + this->rotationVec.z += (s16)(kREG(59) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void func_8097A6C0(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 11; + this->drawConfig = 12; + } +} + +void func_8097A70C(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097A73C +void DemoGj_Update04(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097A6C0(this, globalCtx); +} + +// func_8097A76C +void DemoGj_Update11(DemoGj* this, GlobalContext* globalCtx) { + func_8097A644(this, globalCtx); + func_8097A70C(this, globalCtx); +} + +void DemoGj_DrawRubble5(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble5DL); +} + +void DemoGj_DrawRotatedRubble5(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubble5DL); +} + +void DemoGj_InitRubblePile5(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 5, 6, &gGanonsCastleRubble6Col); +} + +void func_8097A814(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(70)); + this->rotationVec.y += (s16)(kREG(71) + 1000); + this->rotationVec.z += (s16)(kREG(72) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void func_8097A890(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 12; + this->drawConfig = 13; + } +} + +void func_8097A8DC(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097A90C +void DemoGj_Update05(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097A890(this, globalCtx); +} + +// func_8097A93C +void DemoGj_Update12(DemoGj* this, GlobalContext* globalCtx) { + func_8097A814(this, globalCtx); + func_8097A8DC(this, globalCtx); +} + +void DemoGj_DrawRubble6(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble6DL); +} + +void DemoGj_DrawRotatedRubble6(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubble6DL); +} + +void DemoGj_InitRubblePile6(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 6, 7, &gGanonsCastleRubble7Col); +} + +void func_8097A9E4(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(83)); + this->rotationVec.y += (s16)(kREG(84) + 1000); + this->rotationVec.z += (s16)(kREG(85) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void func_8097AA60(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 13; + this->drawConfig = 14; + } +} + +void func_8097AAAC(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097AADC +void DemoGj_Update06(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097AA60(this, globalCtx); +} + +// func_8097AB0C +void DemoGj_Update13(DemoGj* this, GlobalContext* globalCtx) { + func_8097A9E4(this, globalCtx); + func_8097AAAC(this, globalCtx); +} + +void DemoGj_DrawRubble7(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble7DL); +} + +void DemoGj_DrawRotatedRubble7(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubble7DL); +} + +void DemoGj_InitRubblePile7(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 7, 8, &gGanonsCastleRubbleTallCol); +} + +void func_8097ABB4(DemoGj* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + + this->rotationVec.x += (s16)(kREG(15)); + this->rotationVec.y += (s16)(kREG(14) + 1000); + this->rotationVec.z += (s16)(kREG(13) + 3000); + + DemoGj_SetupRotation(this, globalCtx); +} + +void DemoGj_SpawnSmokePreBattle3(DemoGj* this, GlobalContext* globalCtx) { + static Vec3f pos = { -6.0f, 1053.0f, -473.0f }; + u32 gameplayFrames; + + if (!this->isTransformedIntoGanon) { + gameplayFrames = globalCtx->gameplayFrames % 3; + + if (1) {} + if (gameplayFrames == 2) { + if (!globalCtx->gameplayFrames) {} + DemoGj_SpawnSmoke(globalCtx, &pos, 300.0f); + } + + DemoGj_CheckIfTransformedIntoGanon(this); + } +} + +void func_8097AC9C(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfRisingFromRubble(this, globalCtx)) { + DemoGj_SetupMovement(this, globalCtx); + this->updateMode = 14; + this->drawConfig = 15; + } +} + +void func_8097ACE8(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Actor_Kill(&this->dyna.actor); + } +} + +// func_8097AD18 +void DemoGj_Update07(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097AC9C(this, globalCtx); +} + +// func_8097AD48 +void DemoGj_Update14(DemoGj* this, GlobalContext* globalCtx) { + func_8097ABB4(this, globalCtx); + func_8097ACE8(this, globalCtx); +} + +void DemoGj_DrawRubbleTall(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubbleTallDL); +} + +void DemoGj_DrawRotatedRubbleTall(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawRotated(this, globalCtx, gGanonsCastleRubbleTallDL); +} + +void DemoGj_InitRubbleAroundArena(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 0, 1, &gGanonsCastleRubbleAroundArenaCol); +} + +// func_8097ADF0 +void DemoGj_UpdateRubbleAroundArena(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + DemoGj_SpawnSmokePreBattle1(this, globalCtx); + DemoGj_SpawnSmokePreBattle2(this, globalCtx); + DemoGj_SpawnSmokePreBattle3(this, globalCtx); +} + +void DemoGj_DrawRubbleAroundArena(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubbleAroundArenaDL); +} + +// Inits the three cylinders with `sCylinderInit1` +void DemoGj_InitDestructableRubble1(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 15, 0, NULL); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[0], &sCylinderInit1); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[1], &sCylinderInit1); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[2], &sCylinderInit1); +} + +void DemoGj_DoNothing1(DemoGj* this, GlobalContext* globalCtx) { +} + +/* + * Moves the ColliderCylinder's relative to the actor's position. + * Used by DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_1 + */ +void func_8097AEE8(DemoGj* this, GlobalContext* globalCtx) { + ColliderCylinder* cylinder0 = &this->cylinders[0]; + ColliderCylinder* cylinder1 = &this->cylinders[1]; + ColliderCylinder* cylinder2 = &this->cylinders[2]; + Vec3f* actorPos = &this->dyna.actor.world.pos; + s32 pad; + s16 theta = this->dyna.actor.world.rot.y; + f32 cos_theta = Math_CosS(theta); + f32 sin_theta = Math_SinS(theta); + + cylinder0->dim.pos.z = actorPos->z + (20.0f * cos_theta) - (-20.0f * sin_theta); + cylinder0->dim.pos.x = actorPos->x + (20.0f * sin_theta) + (-20.0f * cos_theta); + cylinder0->dim.pos.y = actorPos->y; + + cylinder1->dim.pos.z = actorPos->z + (-20.0f * cos_theta) - (20.0f * sin_theta); + cylinder1->dim.pos.x = actorPos->x + (-20.0f * sin_theta) + (20.0f * cos_theta); + cylinder1->dim.pos.y = actorPos->y; + + cylinder2->dim.pos.z = actorPos->z + (-60.0f * cos_theta) - (60.0f * sin_theta); + cylinder2->dim.pos.x = actorPos->x + (-60.0f * sin_theta) + (60.0f * cos_theta); + cylinder2->dim.pos.y = actorPos->y; +} + +void DemoGj_SetCylindersAsAC(DemoGj* this, GlobalContext* globalCtx) { + s32 pad[2]; + Collider* cylinder0 = &this->cylinders[0].base; + Collider* cylinder1 = &this->cylinders[1].base; + Collider* cylinder2 = &this->cylinders[2].base; + s32 pad2[3]; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, cylinder0); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, cylinder1); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, cylinder2); +} + +void DemoGj_DirectedExplosion(DemoGj* this, GlobalContext* globalCtx, Vec3f* direction) { + Vec3f pos; + + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y; + pos.z = this->dyna.actor.world.pos.z; + + DemoGj_Explode(this, globalCtx, &pos, direction); +} + +void func_8097B128(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Vec3f* scale = &this->dyna.actor.scale; + + DemoGj_InitCommon(this, globalCtx, &gGanonsCastleRubble2Col); + this->updateMode = 18; + this->drawConfig = 16; + scale->x *= 0.8f; + scale->y *= 0.8f; + scale->z *= 0.8f; + } +} + +s32 DemoGj_HasCylinderAnyExploded(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_HitByExplosion(this, globalCtx, &this->cylinders[0])) { + return true; + } + if (DemoGj_HitByExplosion(this, globalCtx, &this->cylinders[1])) { + return true; + } + if (DemoGj_HitByExplosion(this, globalCtx, &this->cylinders[2])) { + return true; + } + return false; +} + +/* + * Checks if should kill the actor and drop collectibles + * Kills the actor if ganon->unk_314==4 (Ganon killed), this rubble was hit by an explosion or killFlag==true + * Used by DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_1 + */ +void func_8097B22C(DemoGj* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (func_809797E4(this, 4)) { + Actor_Kill(thisx); + } else if (DemoGj_HasCylinderAnyExploded(this, globalCtx)) { + Vec3f vec1 = { 0.0f, 0.0f, 0.0f }; + + DemoGj_DropCollectible(this, globalCtx); + DemoGj_DirectedExplosion(this, globalCtx, &vec1); + + Actor_Kill(thisx); + } else if (this->killFlag) { + Vec3f vec2 = this->unk_26C; + vec2.y = 0.0f; + + DemoGj_DropCollectible(this, globalCtx); + DemoGj_DirectedExplosion(this, globalCtx, &vec2); + + Actor_Kill(thisx); + } + + func_8097AEE8(this, globalCtx); + DemoGj_SetCylindersAsAC(this, globalCtx); +} + +// func_8097B340 +void DemoGj_Update15(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097B128(this, globalCtx); +} + +// func_8097B370 +void DemoGj_Update18(DemoGj* this, GlobalContext* globalCtx) { + func_8097B22C(this, globalCtx); + DemoGj_DoNothing1(this, globalCtx); +} + +void DemoGj_DrawDestructableRubble1(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble2DL); +} + +// Inits the three cylinders with `sCylinderInit2` +void DemoGj_InitDestructableRubble2(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 16, 0, NULL); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[0], &sCylinderInit2); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[1], &sCylinderInit2); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[2], &sCylinderInit2); +} + +void DemoGj_DoNothing2(DemoGj* this, GlobalContext* globalCtx) { +} + +// Moves the ColliderCylinder's relative to the actor's position. +void func_8097B450(DemoGj* this, GlobalContext* globalCtx) { + ColliderCylinder* cylinder0 = &this->cylinders[0]; + ColliderCylinder* cylinder1 = &this->cylinders[1]; + ColliderCylinder* cylinder2 = &this->cylinders[2]; + Vec3f* actorPos = &this->dyna.actor.world.pos; + s32 pad; + s16 theta = this->dyna.actor.world.rot.y; + f32 cos_theta = Math_CosS(theta); + f32 sin_theta = Math_SinS(theta); + + cylinder0->dim.pos.z = actorPos->z - (35.0f * sin_theta); + cylinder0->dim.pos.x = actorPos->x + (35.0f * cos_theta); + cylinder0->dim.pos.y = actorPos->y; + + cylinder1->dim.pos.z = actorPos->z - (-10.0f * sin_theta); + cylinder1->dim.pos.x = actorPos->x + (-10.0f * cos_theta); + cylinder1->dim.pos.y = actorPos->y; + + cylinder2->dim.pos.z = actorPos->z - (-55.0f * sin_theta); + cylinder2->dim.pos.x = actorPos->x + (-55.0f * cos_theta); + cylinder2->dim.pos.y = actorPos->y; +} + +void DemoGj_SetCylindersAsAC2(DemoGj* this, GlobalContext* globalCtx) { + s32 pad[2]; + Collider* cylinder0 = &this->cylinders[0].base; + Collider* cylinder1 = &this->cylinders[1].base; + Collider* cylinder2 = &this->cylinders[2].base; + s32 pad2[3]; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, cylinder0); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, cylinder1); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, cylinder2); +} + +// Does the same as `DemoGj_HasCylinderAnyExploded` +s32 DemoGj_HasCylinderAnyExploded2(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_HitByExplosion(this, globalCtx, &this->cylinders[0])) { + return true; + } + if (DemoGj_HitByExplosion(this, globalCtx, &this->cylinders[1])) { + return true; + } + if (DemoGj_HitByExplosion(this, globalCtx, &this->cylinders[2])) { + return true; + } + return false; +} + +void DemoGj_DirectedExplosion2(DemoGj* this, GlobalContext* globalCtx, Vec3f* direction) { + Vec3f pos; + + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y; + pos.z = this->dyna.actor.world.pos.z; + + DemoGj_Explode(this, globalCtx, &pos, direction); +} + +void func_8097B6C4(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Vec3f* scale = &this->dyna.actor.scale; + + DemoGj_InitCommon(this, globalCtx, &gGanonsCastleRubble3Col); + this->updateMode = 19; + this->drawConfig = 17; + scale->x *= 0.8f; + scale->y *= 0.8f; + scale->z *= 0.8f; + } +} + +/* + * Checks if should kill the actor and drop collectibles + * Kills the actor if ganon->unk_314==4 (Ganon killed), this rubble was hit by an explosion or killFlag==true + * Used by DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_2 + */ +void func_8097B750(DemoGj* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (func_809797E4(this, 4)) { + Actor_Kill(thisx); + } else if (DemoGj_HasCylinderAnyExploded2(this, globalCtx)) { + Vec3f vec1 = { 0.0f, 0.0f, 0.0f }; + + DemoGj_DropCollectible(this, globalCtx); + DemoGj_DirectedExplosion2(this, globalCtx, &vec1); + + Actor_Kill(thisx); + } else if (this->killFlag) { + Vec3f vec2 = this->unk_26C; + vec2.y = 0.0f; + + DemoGj_DropCollectible(this, globalCtx); + DemoGj_DirectedExplosion2(this, globalCtx, &vec2); + + Actor_Kill(thisx); + } + + func_8097B450(this, globalCtx); + DemoGj_SetCylindersAsAC2(this, globalCtx); +} + +// func_8097B864 +void DemoGj_Update16(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097B6C4(this, globalCtx); +} + +// func_8097B894 +void DemoGj_Update19(DemoGj* this, GlobalContext* globalCtx) { + func_8097B750(this, globalCtx); + DemoGj_DoNothing2(this, globalCtx); +} + +void DemoGj_DemoGj_InitDestructableRubble2(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubble3DL); +} + +// Inits the first cylinder (only that one) with `sCylinderInit3` +void DemoGj_InitDestructableRubbleTall(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_InitSetIndexes(this, globalCtx, 17, 0, NULL); + DemoGj_InitCylinder(this, globalCtx, &this->cylinders[0], &sCylinderInit3); +} + +void DemoGj_DoNothing3(DemoGj* this, GlobalContext* globalCtx) { +} + +void DemoGj_DirectedDoubleExplosion(DemoGj* this, GlobalContext* globalCtx, Vec3f* direction) { + Vec3f pos; + + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y; + pos.z = this->dyna.actor.world.pos.z; + DemoGj_Explode(this, globalCtx, &pos, direction); + + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + 100.0f; + pos.z = this->dyna.actor.world.pos.z; + DemoGj_Explode(this, globalCtx, &pos, direction); +} + +void func_8097B9BC(DemoGj* this, GlobalContext* globalCtx) { + if (DemoGj_IsGanondorfFloatingInAir(this, globalCtx)) { + Vec3f* scale = &this->dyna.actor.scale; + + DemoGj_InitCommon(this, globalCtx, &gGanonsCastleRubbleTallCol); + this->updateMode = 20; + this->drawConfig = 18; + scale->x *= 0.8f; + scale->y *= 0.8f; + scale->z *= 0.8f; + } +} + +/* + * Checks if should kill the actor and drop collectibles + * Kills the actor if ganon->unk_314==4 (Ganon killed), this rubble was hit by an explosion or killFlag==true + * Used by DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_TALL + */ +void func_8097BA48(DemoGj* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + ColliderCylinder* cylinder = &this->cylinders[0]; + s32 pad[2]; + + if (func_809797E4(this, 4)) { + Actor_Kill(thisx); + } else if (DemoGj_HitByExplosion(this, globalCtx, cylinder)) { + Vec3f vec1 = { 0.0f, 0.0f, 0.0f }; + + DemoGj_DropCollectible(this, globalCtx); + DemoGj_DirectedDoubleExplosion(this, globalCtx, &vec1); + + Actor_Kill(thisx); + } else if (this->killFlag) { + Vec3f vec2 = this->unk_26C; + vec2.y = 0.0f; + + DemoGj_DropCollectible(this, globalCtx); + DemoGj_DirectedDoubleExplosion(this, globalCtx, &vec2); + + Actor_Kill(thisx); + } + + Collider_UpdateCylinder(thisx, cylinder); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &cylinder->base); +} + +// func_8097BB78 +void DemoGj_Update17(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_FindGanon(this, globalCtx); + func_8097B9BC(this, globalCtx); +} + +// func_8097BBA8 +void DemoGj_Update20(DemoGj* this, GlobalContext* globalCtx) { + func_8097BA48(this, globalCtx); + DemoGj_DoNothing3(this, globalCtx); +} + +void DemoGj_DemoGj_InitDestructableRubbleTall(DemoGj* this, GlobalContext* globalCtx) { + DemoGj_DrawCommon(this, globalCtx, gGanonsCastleRubbleTallDL); +} + +static DemoGjUpdateFunc sUpdateFuncs[] = { + DemoGj_UpdateRubbleAroundArena, + DemoGj_Update01, + DemoGj_Update02, + DemoGj_Update03, + DemoGj_Update04, + DemoGj_Update05, + DemoGj_Update06, + DemoGj_Update07, + DemoGj_Update08, + DemoGj_Update09, + DemoGj_Update10, + DemoGj_Update11, + DemoGj_Update12, + DemoGj_Update13, + DemoGj_Update14, + DemoGj_Update15, + DemoGj_Update16, + DemoGj_Update17, + DemoGj_Update18, + DemoGj_Update19, + DemoGj_Update20, +}; + +void DemoGj_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoGj* this = (DemoGj*)thisx; + + if (this->updateMode < 0 || this->updateMode >= ARRAY_COUNT(sUpdateFuncs) || + sUpdateFuncs[this->updateMode] == NULL) { + // "The main mode is abnormal!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sUpdateFuncs[this->updateMode](this, globalCtx); +} + +void DemoGj_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoGj* this = (DemoGj*)thisx; + + switch (DemoGj_GetType(this)) { + case DEMOGJ_TYPE_AROUNDARENA: + DemoGj_InitRubbleAroundArena(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_1: + DemoGj_InitRubblePile1(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_2: + DemoGj_InitRubblePile2(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_3: + DemoGj_InitRubblePile3(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_4: + DemoGj_InitRubblePile4(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_5: + DemoGj_InitRubblePile5(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_6: + DemoGj_InitRubblePile6(this, globalCtx); + break; + + case DEMOGJ_TYPE_RUBBLE_PILE_7: + DemoGj_InitRubblePile7(this, globalCtx); + break; + + case DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_1: + DemoGj_InitDestructableRubble1(this, globalCtx); + break; + + case DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_2: + DemoGj_InitDestructableRubble2(this, globalCtx); + break; + + case DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_TALL: + DemoGj_InitDestructableRubbleTall(this, globalCtx); + break; + + default: + // "Demo_Gj_Actor_ct There is no such argument!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "Demo_Gj_Actor_ct そんな引数は無い!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST, globalCtx, + this); + Actor_Kill(&this->dyna.actor); + } +} + +void DemoGj_DrawNothing(DemoGj* this, GlobalContext* globalCtx) { +} + +static DemoGjDrawFunc sDrawFuncs[] = { + DemoGj_DrawNothing, + DemoGj_DrawRubbleAroundArena, + DemoGj_DrawRubble2, + DemoGj_DrawRubble3, + DemoGj_DrawRubble4, + DemoGj_DrawRubble5, + DemoGj_DrawRubble6, + DemoGj_DrawRubble7, + DemoGj_DrawRubbleTall, + DemoGj_DrawRotatedRubble2, + DemoGj_DrawRotatedRubble3, + DemoGj_DrawRotatedRubble4, + DemoGj_DrawRotatedRubble5, + DemoGj_DrawRotatedRubble6, + DemoGj_DrawRotatedRubble7, + DemoGj_DrawRotatedRubbleTall, + DemoGj_DrawDestructableRubble1, + DemoGj_DemoGj_InitDestructableRubble2, + DemoGj_DemoGj_InitDestructableRubbleTall, +}; + +void DemoGj_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoGj* this = (DemoGj*)thisx; + + if (this->drawConfig < 0 || this->drawConfig >= ARRAY_COUNT(sDrawFuncs) || sDrawFuncs[this->drawConfig] == NULL) { + // "The drawing mode is abnormal!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sDrawFuncs[this->drawConfig](this, globalCtx); +} + +const ActorInit Demo_Gj_InitVars = { + ACTOR_DEMO_GJ, + ACTORCAT_PROP, + FLAGS, + OBJECT_GJ, + sizeof(DemoGj), + (ActorFunc)DemoGj_Init, + (ActorFunc)DemoGj_Destroy, + (ActorFunc)DemoGj_Update, + (ActorFunc)DemoGj_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.h b/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.h new file mode 100644 index 000000000..0ff2f1bfb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.h @@ -0,0 +1,45 @@ +#ifndef Z_DEMO_GJ_H +#define Z_DEMO_GJ_H + +#include "ultra64.h" +#include "global.h" + +#include "overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.h" + +struct DemoGj; + +typedef struct DemoGj { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s32 updateMode; + /* 0x0168 */ s32 drawConfig; + /* 0x016C */ Vec3s rotationVec; + /* 0x0172 */ Vec3s unk_172; // It is assigned only. @see DemoGj_SetupTranslation and @see DemoGj_SetupMovement + /* 0x0178 */ BossGanon2* ganon; + /* 0x017C */ s32 isTransformedIntoGanon; // flag + /* 0x0180 */ s32 isRotated; // flag + /* 0x0184 */ ColliderCylinder cylinders[3]; + /* 0x0268 */ s32 killFlag; // This actor never sets this flag, but it reads it. If set to `true` and the actor type is DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_1, DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_2 or DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_TALL, then the actor will be killed and will drop the specified amount of collectibles. + /* 0x026C */ Vec3f unk_26C; // This actor never sets this. Specifies which direction will this actor explode when killed using `killFlag`. +} DemoGj; // size = 0x0278 + +/** + * The format of this actor's params is the following: + * bits 11-15: The collectible that will be dropped when killed. + * bits 8-10: The amount of collectible that will be dropped when killed. + * bits 0- 7: A value of the enum DemoGjType. + */ +typedef enum { + /* 04 */ DEMOGJ_TYPE_AROUNDARENA = 4, // This is the indestructible rubble around the arena. This actor keeps alive the whole fight. + /* 08 */ DEMOGJ_TYPE_RUBBLE_PILE_1 = 8, // DEMOGJ_TYPE_RUBBLE_PILE_X are the rubbles from where Ganondorf rises. When he transforms into Ganon these are removed from the scene (Actor_Kill). + /* 09 */ DEMOGJ_TYPE_RUBBLE_PILE_2, + /* 10 */ DEMOGJ_TYPE_RUBBLE_PILE_3, + /* 11 */ DEMOGJ_TYPE_RUBBLE_PILE_4, + /* 12 */ DEMOGJ_TYPE_RUBBLE_PILE_5, + /* 13 */ DEMOGJ_TYPE_RUBBLE_PILE_6, + /* 14 */ DEMOGJ_TYPE_RUBBLE_PILE_7, + /* 16 */ DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_1 = 16, // This three rubbles are the ones that Ganon can destroy and drop collectables. + /* 17 */ DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_2, // They are spawned as soon as the others, but they have collision and are rendered only when Ganondorf has transformed into Ganon. + /* 22 */ DEMOGJ_TYPE_DESTRUCTABLE_RUBBLE_TALL = 22 // +} DemoGjType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Go/z_demo_go.c b/soh/src/overlays/actors/ovl_Demo_Go/z_demo_go.c new file mode 100644 index 000000000..9b64c994b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Go/z_demo_go.c @@ -0,0 +1,354 @@ +/* + * File: z_demo_go.c + * Overlay: Demo_Go + * Description: Gorons (Cutscene) + */ + +#include "z_demo_go.h" +#include "objects/object_oF1d_map/object_oF1d_map.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void DemoGo_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoGo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoGo_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoGo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8097CE10(DemoGo* this, GlobalContext* globalCtx); +void func_8097CFDC(DemoGo* this, GlobalContext* globalCtx); +void func_8097CFFC(DemoGo* this, GlobalContext* globalCtx); +void func_8097D01C(DemoGo* this, GlobalContext* globalCtx); +void func_8097D058(DemoGo* this, GlobalContext* globalCtx); +void func_8097D088(DemoGo* this, GlobalContext* globalCtx); +void func_8097D0D0(DemoGo* this, GlobalContext* globalCtx); +void func_8097D130(DemoGo* this, GlobalContext* globalCtx); +void func_8097D290(DemoGo* this, GlobalContext* globalCtx); +void func_8097D29C(DemoGo* this, GlobalContext* globalCtx); + +static void* sEyeTextures[] = { gGoronCsEyeOpenTex, gGoronCsEyeHalfTex, gGoronCsEyeClosedTex }; + +static DemoGoActionFunc D_8097D44C[] = { + func_8097CFDC, func_8097CFFC, func_8097D01C, func_8097D058, func_8097D088, func_8097D0D0, func_8097D130, +}; + +static DemoGoDrawFunc D_8097D468[] = { + func_8097D290, + func_8097D29C, +}; + +const ActorInit Demo_Go_InitVars = { + ACTOR_DEMO_GO, + ACTORCAT_NPC, + FLAGS, + OBJECT_OF1D_MAP, + sizeof(DemoGo), + (ActorFunc)DemoGo_Init, + (ActorFunc)DemoGo_Destroy, + (ActorFunc)DemoGo_Update, + (ActorFunc)DemoGo_Draw, + NULL, +}; + +s32 func_8097C870(DemoGo* this) { + s32 ret; + + switch (this->actor.params) { + case 0: + ret = 3; + break; + case 1: + ret = 4; + break; + default: + if (1) { + ret = 5; + } + break; + } + return ret; +} + +void func_8097C8A8(DemoGo* this, GlobalContext* globalCtx) { + Actor* thisx = &this->actor; + Vec3f sp20; + f32 sp1C; + + if ((thisx->params == 0) || (thisx->params == 1)) { + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &thisx->world.pos, &sp20, &sp1C); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &sp20, 20, NA_SE_EV_OBJECT_FALL); + } +} + +void DemoGo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoGo* this = (DemoGo*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void func_8097C930(DemoGo* this) { + s16* something = &this->unk_192; + s16* other = &this->unk_190; + s32 pad[3]; + + if (DECR(*something) == 0) { + *something = Rand_S16Offset(60, 60); + } + *other = *something; + if (*other >= 3) { + *other = 0; + } +} + +void func_8097C9B8(DemoGo* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EN_DODO_M_GND); +} + +void func_8097C9DC(DemoGo* this) { + s32 pad[2]; + + if (Animation_OnFrame(&this->skelAnime, 12.0f) || Animation_OnFrame(&this->skelAnime, 25.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_EN_MORIBLIN_WALK); + } +} + +void func_8097CA30(DemoGo* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +void func_8097CA78(DemoGo* this, GlobalContext* globalCtx) { + s16 pad; + Vec3f vec = this->actor.world.pos; + func_80033480(globalCtx, &vec, kREG(11) + 100.0f, kREG(12) + 0xA, kREG(13) + 0x12C, kREG(14), 0); + func_8097C9B8(this); +} + +void func_8097CB0C(DemoGo* this, GlobalContext* globalCtx) { + Actor* thisx = &this->actor; + PosRot* world = &thisx->world; + CutsceneContext* csCtx = &globalCtx->csCtx; + CsCmdActorAction* npcAction; + f32 temp_ret; + s32 pad; + Vec3f startPos; + Vec3f endPos; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = csCtx->npcActions[func_8097C870(this)]; + if (npcAction != NULL) { + temp_ret = Environment_LerpWeight(npcAction->endFrame, npcAction->startFrame, csCtx->frames); + startPos.x = npcAction->startPos.x; + startPos.y = npcAction->startPos.y; + startPos.z = npcAction->startPos.z; + endPos.x = npcAction->endPos.x; + endPos.y = npcAction->endPos.y; + endPos.z = npcAction->endPos.z; + world->pos.x = (endPos.x - startPos.x) * temp_ret + startPos.x; + world->pos.y = (endPos.y - startPos.y) * temp_ret + startPos.y; + world->pos.z = (endPos.z - startPos.z) * temp_ret + startPos.z; + world->rot.y = thisx->shape.rot.y = npcAction->rot.y; + } + } +} + +void func_8097CC08(DemoGo* this) { + f32 something = this->unk_19C; + + if (something < 8.0f) { + this->actor.speedXZ = (((kREG(15) * 0.01f) + 1.2f) / 8.0f) * something; + } else { + this->actor.speedXZ = (kREG(15) * 0.01f) + 1.2f; + } + Actor_MoveForward(&this->actor); +} + +void func_8097CCC0(DemoGo* this) { + Actor_MoveForward(&this->actor); +} + +void func_8097CCE0(DemoGo* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + Actor* thisx = &this->actor; + s32 rotYDelta; + s32 newRotY; + s32 thisRotY; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = globalCtx->csCtx.npcActions[func_8097C870(this)]; + if (npcAction != NULL) { + thisRotY = thisx->world.rot.y; + rotYDelta = npcAction->rot.y - thisRotY; + if ((rotYDelta > -(kREG(16) + 0x96)) && (rotYDelta < kREG(16) + 0x96)) { + newRotY = npcAction->rot.y; + } else if (rotYDelta > 0) { + newRotY = (thisRotY + kREG(16)) + 0x96; + } else { + newRotY = (thisRotY - kREG(16)) - 0x96; + } + thisx->shape.rot.y = newRotY; + thisx->world.rot.y = newRotY; + } + } +} + +s32 DemoGo_UpdateSkelAnime(DemoGo* this) { + return SkelAnime_Update(&this->skelAnime); +} + +s32 func_8097CDB0(DemoGo* this, GlobalContext* globalCtx, u16 npcAction) { + CutsceneContext* csCtx = &globalCtx->csCtx; + s32 actionIdx = func_8097C870(this); + + if ((csCtx->state != CS_STATE_IDLE) && (csCtx->npcActions[actionIdx] != NULL) && + (csCtx->npcActions[actionIdx]->action == npcAction)) { + return 1; + } + return 0; +} + +void func_8097CE10(DemoGo* this, GlobalContext* globalCtx) { + this->action = 1; +} + +void func_8097CE20(DemoGo* this, GlobalContext* globalCtx) { + if (func_8097CDB0(this, globalCtx, 2)) { + this->action = 2; + this->drawConfig = 1; + func_8097CB0C(this, globalCtx); + func_8097C8A8(this, globalCtx); + } +} + +void func_8097CE78(DemoGo* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + CsCmdActorAction* npcAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = csCtx->npcActions[func_8097C870(this)]; + if (npcAction != NULL && csCtx->frames >= npcAction->endFrame) { + func_8097CA78(this, globalCtx); + this->action = 3; + } + } +} + +void func_8097CEEC(DemoGo* this, GlobalContext* globalCtx) { + if (func_8097CDB0(this, globalCtx, 3)) { + this->action = 4; + } +} + +void func_8097CF20(DemoGo* this, GlobalContext* globalCtx, s32 arg2) { + AnimationHeader* animation = &gGoronAnim_0029A8; + if (arg2 != 0) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + -8.0f); + this->action = 5; + this->unk_19C = 0.0f; + } +} + +void func_8097CF9C(DemoGo* this) { + this->unk_19C += 1.0f; + if (this->unk_19C >= 8.0f) { + this->action = 6; + } +} + +void func_8097CFDC(DemoGo* this, GlobalContext* globalCtx) { + func_8097CE10(this, globalCtx); +} + +void func_8097CFFC(DemoGo* this, GlobalContext* globalCtx) { + func_8097CE20(this, globalCtx); +} + +void func_8097D01C(DemoGo* this, GlobalContext* globalCtx) { + func_8097CB0C(this, globalCtx); + func_8097CA30(this, globalCtx); + func_8097CE78(this, globalCtx); +} + +void func_8097D058(DemoGo* this, GlobalContext* globalCtx) { + func_8097CA30(this, globalCtx); + func_8097CEEC(this, globalCtx); +} + +void func_8097D088(DemoGo* this, GlobalContext* globalCtx) { + s32 something; + + func_8097CA30(this, globalCtx); + something = DemoGo_UpdateSkelAnime(this); + func_8097C930(this); + func_8097CF20(this, globalCtx, something); +} + +void func_8097D0D0(DemoGo* this, GlobalContext* globalCtx) { + func_8097CCE0(this, globalCtx); + func_8097CCC0(this); + func_8097CA30(this, globalCtx); + DemoGo_UpdateSkelAnime(this); + func_8097C930(this); + func_8097C9DC(this); + func_8097CF9C(this); +} +void func_8097D130(DemoGo* this, GlobalContext* globalCtx) { + func_8097CCE0(this, globalCtx); + func_8097CC08(this); + func_8097CA30(this, globalCtx); + DemoGo_UpdateSkelAnime(this); + func_8097C930(this); + func_8097C9DC(this); +} + +void DemoGo_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoGo* this = (DemoGo*)thisx; + + if (this->action < 0 || this->action >= 7 || D_8097D44C[this->action] == 0) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + D_8097D44C[this->action](this, globalCtx); +} + +void DemoGo_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoGo* this = (DemoGo*)thisx; + AnimationHeader* animation = &gGoronAnim_004930; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGoronSkel, NULL, NULL, NULL, 0); + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, 0.0f); + this->action = 0; +} + +void func_8097D290(DemoGo* this, GlobalContext* globalCtx) { +} + +void func_8097D29C(DemoGo* this, GlobalContext* globalCtx) { + s32 pad; + s16 eyeTexIdx = this->unk_190; + SkelAnime* skelAnime = &this->skelAnime; + void* eyeTexture = sEyeTextures[eyeTexIdx]; + void* mouthTexture = gGoronCsMouthSmileTex; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_go.c", 732); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTexture)); + + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, NULL, NULL, + this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_go.c", 746); +} + +void DemoGo_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoGo* this = (DemoGo*)thisx; + + if (this->drawConfig < 0 || this->drawConfig >= 2 || D_8097D468[this->drawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + D_8097D468[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Go/z_demo_go.h b/soh/src/overlays/actors/ovl_Demo_Go/z_demo_go.h new file mode 100644 index 000000000..688a8a99c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Go/z_demo_go.h @@ -0,0 +1,22 @@ +#ifndef Z_DEMO_GO_H +#define Z_DEMO_GO_H + +#include "ultra64.h" +#include "global.h" + +struct DemoGo; + +typedef void (*DemoGoActionFunc)(struct DemoGo*, GlobalContext*); +typedef void (*DemoGoDrawFunc)(struct DemoGo*, GlobalContext*); + +typedef struct DemoGo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ s16 unk_190; + /* 0x0192 */ s16 unk_192; + /* 0x0194 */ s32 action; + /* 0x0198 */ s32 drawConfig; + /* 0x019C */ f32 unk_19C; +} DemoGo; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Gt/z_demo_gt.c b/soh/src/overlays/actors/ovl_Demo_Gt/z_demo_gt.c new file mode 100644 index 000000000..764493b3b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Gt/z_demo_gt.c @@ -0,0 +1,1784 @@ +#include "z_demo_gt.h" +#include "objects/object_gt/object_gt.h" +#include "objects/object_geff/object_geff.h" +#include "vt.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoGt_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoGt_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoGt_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoGt_Draw(Actor* thisx, GlobalContext* globalCtx); + +void DemoGt_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoGt* this = (DemoGt*)thisx; + + if ((this->dyna.actor.params == 1) || (this->dyna.actor.params == 2)) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void DemoGt_PlayEarthquakeSfx() { + func_800788CC(NA_SE_EV_EARTHQUAKE - SFX_FLAG); +} + +void DemoGt_PlayExplosion1Sfx(GlobalContext* globalCtx, Vec3f* pos) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, pos, 60, NA_SE_IT_BOMB_EXPLOSION); +} + +void DemoGt_PlayExplosion2Sfx(GlobalContext* globalCtx, Vec3f* pos) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, pos, 60, NA_SE_EV_GRAVE_EXPLOSION); +} + +void DemoGt_Rumble(GlobalContext* globalCtx) { + func_800AA000(0.0f, 0x32, 0xA, 5); +} + +void DemoGt_SpawnDust(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale, s16 scaleStep, + s16 life) { + static Color_RGBA8 brownPrim = { 100, 80, 100, 0 }; + static Color_RGBA8 redEnv = { 255, 110, 96, 0 }; + + func_8002843C(globalCtx, pos, velocity, accel, &brownPrim, &redEnv, ((Rand_ZeroOne() * (scale * 0.2f)) + scale), + scaleStep, life); +} + +void func_8097D7D8(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velOffset, f32 scale, s32 arg4, s32 arg5, s16 life) { + s32 pad; + + if (!FrameAdvance_IsEnabled(globalCtx)) { + s32 frames = globalCtx->gameplayFrames; + + if (ABS(frames % arg4) == arg5) { + s32 pad[2]; + Vec3f velocity = { 0.0f, 6.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + velocity.x *= scale; + velocity.y *= scale; + velocity.z *= scale; + + velocity.x += velOffset->x; + velocity.y += velOffset->y; + velocity.z += velOffset->z; + + if (0) {} + + accel.x *= scale; + accel.y *= scale; + accel.z *= scale; + + DemoGt_SpawnDust(globalCtx, pos, &velocity, &accel, (300.0f * scale), (15.0f * scale), life); + } + } +} + +Actor* DemoGt_SpawnCloudRing(GlobalContext* globalCtx, Vec3f* pos, s16 params) { + return Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_SPOT16_DOUGHNUT, pos->x, pos->y, pos->z, 0, 0, 0, + params); +} + +void DemoGt_SpawnExplosionWithSound(GlobalContext* globalCtx, Vec3f* pos, f32 scale) { + s32 pad; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + EffectSsBomb2_SpawnLayered(globalCtx, pos, &velocity, &accel, (100.0f * scale), (15.0f * scale)); + DemoGt_PlayExplosion1Sfx(globalCtx, pos); +} + +void DemoGt_SpawnExplosionNoSound(GlobalContext* globalCtx, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 scale) { + EffectSsBomb2_SpawnLayered(globalCtx, pos, velocity, accel, (100.0f * scale), (25.0f * scale)); +} + +void func_8097DAC8(DemoGt* this, GlobalContext* globalCtx, Vec3f* spawnerPos) { + Vec3f pos; + Vec3f velocity; + f32 temp_f0; + s16 angle; + s16 phi_s0; + s32 i; + + angle = 0; + + for (i = 0; i < 12; i++) { + + pos.x = Math_SinS(angle) * 46.0f; + pos.y = (Rand_ZeroOne() * 75.0f) + 2.0f; + pos.z = Math_CosS(angle) * 46.0f; + + velocity.x = (pos.x * 0.1f) + 20.0f; + velocity.y = Rand_ZeroOne() * 16.0f; + velocity.z = pos.z * 0.1f; + + pos.x += spawnerPos->x; + pos.y += spawnerPos->y; + pos.z += spawnerPos->z; + + temp_f0 = Rand_ZeroOne(); + + if (temp_f0 < 0.1f) { + phi_s0 = 96; + } else if (temp_f0 < 0.7f) { + phi_s0 = 64; + } else { + phi_s0 = 32; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, spawnerPos, -247, phi_s0, 3, 0, 0, + (s32)(Rand_ZeroOne() * 10.0f + 30.0f), 2, 300, (s32)(Rand_ZeroOne() * 0.0f) + 30, + KAKERA_COLOR_NONE, OBJECT_GEFF, gGanonRubbleDL); + angle += 0x1555; + } +} + +void func_8097DD28(DemoGt* this, GlobalContext* globalCtx, Vec3f* spawnerPos) { + Vec3f pos; + Vec3f velocity; + f32 temp_f0; + s16 angle; + s16 phi_s0; + s32 i; + + angle = 0; + + for (i = 0; i < 8; i++) { + + pos.x = Math_SinS(angle) * 30.0f; + pos.y = (Rand_ZeroOne() * 75.0f) + 2.0f; + pos.z = Math_CosS(angle) * 30.0f; + + velocity.x = 0.0f; + velocity.y = Rand_ZeroOne() * -4.0f; + velocity.z = pos.z * 0.1f; + + pos.x += spawnerPos->x; + pos.y += spawnerPos->y; + pos.z += spawnerPos->z; + + temp_f0 = Rand_ZeroOne(); + + if (temp_f0 < 0.1f) { + phi_s0 = 96; + } else if (temp_f0 < 0.7f) { + phi_s0 = 64; + } else { + phi_s0 = 32; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, spawnerPos, -247, phi_s0, 3, 0, 0, + (s32)((Rand_ZeroOne() * 10.0f) + 30.0f), 2, 300, (s32)(Rand_ZeroOne() * 0.0f) + 0x1E, + KAKERA_COLOR_NONE, OBJECT_GEFF, gGanonRubbleDL); + + angle += 0x2000; + } +} + +void func_8097DF70(DemoGt* this, GlobalContext* globalCtx, Vec3f* spawnerPos) { + Vec3f pos; + Vec3f velocity; + f32 temp_f0; + s16 angle; + s16 phi_s0; + s32 i; + + angle = 0; + + for (i = 0; i < 12; i++) { + + pos.x = Math_SinS(angle) * 16.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = Math_CosS(angle) * 16.0f; + + velocity.x = pos.x * 0.6f; + velocity.y = (Rand_ZeroOne() * 36.0f) + 6.0f; + velocity.z = pos.z * 0.6f; + + pos.x += spawnerPos->x; + pos.y += spawnerPos->y; + pos.z += spawnerPos->z; + + temp_f0 = Rand_ZeroOne(); + + if (temp_f0 < 0.1f) { + phi_s0 = 97; + } else if (temp_f0 < 0.7f) { + phi_s0 = 65; + } else { + phi_s0 = 33; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, spawnerPos, -200, phi_s0, 10, 10, 0, + Rand_ZeroOne() * 30.0f + 30.0f, 2, 300, (s32)(Rand_ZeroOne() * 30.0f) + 30, + KAKERA_COLOR_NONE, OBJECT_GEFF, gGanonRubbleDL); + angle += 0x1555; + } +} + +void func_8097E1D4(GlobalContext* globalCtx, Vec3f* arg1, s16 arg2) { + Vec3f pos; + Vec3f velocity; + f32 temp_f0; + s16 angle; + s16 phi_s0; + s32 i; + + angle = 0; + + for (i = 0; i < 1; i++) { + + pos.x = Math_SinS(angle) * 46.0f; + pos.y = (Rand_ZeroOne() * 75.0f) - 28.0f; + pos.z = Math_CosS(angle) * 46.0f; + + velocity.x = Math_SinS(arg2) * 3.0f; + velocity.y = (Rand_ZeroOne() * -4.0f) + 10.0f; + velocity.z = Math_CosS(arg2) * 3.0f; + + pos.x += arg1->x; + pos.y += arg1->y; + pos.z += arg1->z; + + temp_f0 = Rand_ZeroOne(); + + if (temp_f0 < 0.1f) { + phi_s0 = 97; + } else if (temp_f0 < 0.7f) { + phi_s0 = 65; + } else { + phi_s0 = 33; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, arg1, -247, phi_s0, 3, 0, 0, + (s32)((Rand_ZeroOne() * 10.0f) + 30.0f), 2, 300, (s32)(Rand_ZeroOne() * 0.0f) + 30, + KAKERA_COLOR_NONE, OBJECT_GEFF, gGanonRubbleDL); + + angle += 0x10000; + } +} + +void func_8097E454(GlobalContext* globalCtx, Vec3f* spawnerPos, Vec3f* velocity, Vec3f* accel, f32 arg4, f32 scale, + s32 arg6, s32 arg7, s16 life) { + s32 pad2[3]; + s16 increment; + s32 frames; + s32 i; + s16 phi_s0; + s16 dustScaleStep = 15.0f * scale; + f32 dustScale = 300.0f * scale; + Vec3f pos; + + if ((!FrameAdvance_IsEnabled(globalCtx)) && (arg7 > 0) && (arg6 > 0)) { + frames = (ABS((s32)globalCtx->gameplayFrames) % arg7); + phi_s0 = 0x10000 * frames / arg6; + increment = 0x10000 / arg6; + + for (i = frames; i < arg6; i += arg7) { + + pos.x = (Math_SinS(phi_s0) * arg4) + spawnerPos->x; + pos.y = spawnerPos->y; + pos.z = (Math_CosS(phi_s0) * arg4) + spawnerPos->z; + + DemoGt_SpawnDust(globalCtx, &pos, velocity, accel, dustScale, dustScaleStep, life); + + if (Rand_ZeroOne() <= 0.05f) { + func_8097E1D4(globalCtx, &pos, phi_s0); + } + + phi_s0 += increment; + } + } +} + +u8 func_8097E69C(GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + return true; + } else { + return false; + } +} + +CsCmdActorAction* DemoGt_GetNpcAction(GlobalContext* globalCtx, u32 actionIdx) { + s32 pad[2]; + CsCmdActorAction* ret = NULL; + + if (!func_8097E69C(globalCtx)) { + ret = globalCtx->csCtx.npcActions[actionIdx]; + } + + return ret; +} + +u8 func_8097E704(GlobalContext* globalCtx, u16 arg1, s32 arg2) { + CsCmdActorAction* action = DemoGt_GetNpcAction(globalCtx, arg2); + + if ((action != NULL) && (action->action == arg1)) { + return true; + } else { + return false; + } +} + +void func_8097E744(DemoGt* this, GlobalContext* globalCtx, u32 actionIdx) { + CsCmdActorAction* npcAction = DemoGt_GetNpcAction(globalCtx, actionIdx); + Vec3f* pos = &this->dyna.actor.world.pos; + f32 startX; + f32 startY; + f32 startZ; + f32 endX; + f32 endY; + f32 endZ; + f32 someFloat; + + if (npcAction != NULL) { + someFloat = + Environment_LerpWeightAccelDecel(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames, 8, 0); + startX = npcAction->startPos.x; + startY = npcAction->startPos.y; + startZ = npcAction->startPos.z; + endX = npcAction->endPos.x; + endY = npcAction->endPos.y; + endZ = npcAction->endPos.z; + + pos->x = ((endX - startX) * someFloat) + startX; + pos->y = ((endY - startY) * someFloat) + startY; + pos->z = ((endZ - startZ) * someFloat) + startZ; + } +} + +void func_8097E824(DemoGt* this, s32 arg1) { + s16 phi_a1; + s16 phi_a2; + s16 phi_a3; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3s* unk16C = &this->unk_16C; + s32 pad; + f32 tempf3; + f32 tempf2; + f32 tempf1; + f32 phi_f2; + f32 phi_f12; + f32 phi_f14; + + if (arg1 == 1) { + phi_a1 = kREG(19) + 0x8000; + phi_a2 = kREG(20) + 0x8000; + phi_a3 = kREG(21) + 0x8000; + phi_f14 = kREG(16) * 0.1f; + phi_f12 = (kREG(17) * 0.1f) + 0.5f; + phi_f2 = kREG(18) * 0.1f; + } else if (arg1 == 2) { + phi_a1 = kREG(25) + 0x8000; + phi_a2 = kREG(26) + 0x8000; + phi_a3 = kREG(27) + 0x8000; + phi_f14 = kREG(22) * 0.1f; + phi_f12 = (kREG(23) * 0.1f) + 0.5f; + phi_f2 = kREG(24) * 0.1f; + } else if (arg1 == 3) { + phi_a1 = kREG(31) + 0x8000; + phi_a2 = kREG(32) + 0x8000; + phi_a3 = kREG(33) + 0x8000; + phi_f14 = kREG(28) * 0.1f; + phi_f12 = (kREG(29) * 0.1f) + 0.5f; + phi_f2 = kREG(30) * 0.1f; + } else if (arg1 == 4) { + phi_a1 = kREG(37) + 0x8000; + phi_a2 = kREG(38) + 0x8000; + phi_a3 = kREG(39) + 0x8000; + phi_f14 = kREG(34) * 0.1f; + phi_f12 = (kREG(35) * 0.1f) + 0.5f; + phi_f2 = kREG(36) * 0.1f; + } else if (arg1 == 5) { + phi_a1 = kREG(43) + 0x8000; + phi_a2 = kREG(44) + 0x8000; + phi_a3 = kREG(45) + 0x8000; + phi_f14 = kREG(40) * 0.1f; + phi_f12 = (kREG(41) * 0.1f) + 0.5f; + phi_f2 = kREG(42) * 0.1f; + } else if (arg1 == 6) { + phi_a1 = kREG(49) + 0x8000; + phi_a2 = kREG(50) + 0x8000; + phi_a3 = kREG(51) + 0x8000; + phi_f14 = kREG(46) * 0.1f; + phi_f12 = (kREG(47) * 0.1f) + 0.5f; + phi_f2 = kREG(48) * 0.1f; + } else if (arg1 == 7) { + phi_a1 = kREG(85) + 0x8000; + phi_a2 = kREG(86) + 0x8000; + phi_a3 = kREG(87) + 0x8000; + phi_f14 = kREG(82) * 0.1f; + phi_f12 = (kREG(83) * 0.1f) + 0.5f; + phi_f2 = kREG(84) * 0.1f; + } else { + phi_a1 = kREG(91) + 0x8000; + phi_a2 = kREG(92) + 0x8000; + phi_a3 = kREG(93) + 0x8000; + phi_f14 = kREG(88) * 0.1f; + phi_f12 = (kREG(89) * 0.1f) + 0.5f; + phi_f2 = kREG(90) * 0.1f; + } + + unk16C->x += phi_a1; + unk16C->y += phi_a2; + unk16C->z += phi_a3; + + tempf1 = Math_CosS(unk16C->x) * phi_f14; + tempf2 = Math_CosS(unk16C->y) * phi_f12; + tempf3 = Math_CosS(unk16C->z) * phi_f2; + + pos->x += tempf1; + pos->y += tempf2; + pos->z += tempf3; +} + +void func_8097ED64(DemoGt* this, GlobalContext* globalCtx, s32 actionIdx) { + func_8097E744(this, globalCtx, actionIdx); + func_8097E824(this, actionIdx); +} + +u8 func_8097ED94() { + if (kREG(2) != 0) { + return true; + } else if (gSaveContext.sceneSetupIndex < 4) { + return false; + } else { + return true; + } +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void func_8097EDD8(DemoGt* this, GlobalContext* globalCtx, CollisionHeader* collision) { + s32 pad[3]; + CollisionHeader* colHeader; + + if (collision != NULL) { + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + colHeader = NULL; + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + } +} + +u8 func_8097EE44(DemoGt* this, GlobalContext* globalCtx, s32 updateMode, s32 drawConfig, CollisionHeader* colHeader) { + + if (func_8097ED94()) { + this->updateMode = updateMode; + this->drawConfig = drawConfig; + func_8097EDD8(this, globalCtx, colHeader); + return true; + } else { + Actor_Kill(&this->dyna.actor); + return false; + } +} + +void func_8097EEA8_Init0(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + + func_8097EE44(this, globalCtx, 0, 1, NULL); +} + +void func_8097EF00(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + + if (frames == 527) { + func_800F3F3C(13); + } +} + +void func_8097EF34(DemoGt* this, GlobalContext* globalCtx) { +} + +void func_8097EF40(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + s32 pad1[3]; + Vec3f dustPos; + Vec3f velocity = { 0.0f, -16.0f, 0.0f }; + Vec3f accel = { 0.0f, 1.2f, 0.0f }; + Vec3f* pos = &this->dyna.actor.world.pos; + s32 pad; + + if ((kREG(1) == 20) || (frames == 220)) { + dustPos.x = pos->x + 256.0f; + dustPos.y = pos->y + 679.0f; + dustPos.z = pos->z + 82.0f; + + DemoGt_SpawnDust(globalCtx, &dustPos, &velocity, &accel, 1700.0f, 15, 30); + + dustPos.x = pos->x + 256.0f; + dustPos.y = pos->y + 679.0f; + dustPos.z = pos->z - 60.0f; + + DemoGt_SpawnDust(globalCtx, &dustPos, &velocity, &accel, 1700.0f, 15, 30); + } +} + +void func_8097F0AC(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f sp38; + s16 pad1[3]; + Vec3f sp24; + u16 frames = globalCtx->csCtx.frames; + s32 pad2; + + if ((frames == 140) || (kREG(1) == 19)) { + sp38.x = this->dyna.actor.world.pos.x + 260.0f; + sp38.y = this->dyna.actor.world.pos.y + 340.0f; + sp38.z = this->dyna.actor.world.pos.z + 45.0f; + DemoGt_SpawnExplosionWithSound(globalCtx, &sp38, 2.0f); + } + + if (frames == 176) { + sp24.x = this->dyna.actor.world.pos.x + 260.0f; + sp24.y = this->dyna.actor.world.pos.y + 840.0f; + sp24.z = this->dyna.actor.world.pos.z + 45.0f; + DemoGt_SpawnExplosionWithSound(globalCtx, &sp24, 2.0f); + } +} + +void func_8097F19C(DemoGt* this, GlobalContext* globalCtx) { + func_8097EF34(this, globalCtx); + func_8097EF40(this, globalCtx); + func_8097F0AC(this, globalCtx); +} + +void func_8097F1D8(DemoGt* this) { + f32 temp_v0 = this->unk_172; + + this->unk_174 = (temp_v0 * ((kREG(64) * 0.001f) + 0.048f)) + (kREG(72) + 10.0f); + this->unk_172 += this->unk_174; + + if (this->unk_172 > (s16)(kREG(73) + 0x250)) { + this->unk_172 = kREG(73) + 0x250; + } +} + +void func_8097F280(DemoGt* this, GlobalContext* globalCtx) { + s32* unk178 = this->unk_178; + s32* unk188 = this->unk_188; + s32* unk198 = this->unk_198; + f32 temp_f0; + + if (globalCtx->csCtx.frames < 160) { + unk178[0] = 100; + unk178[1] = 255; + unk178[2] = 200; + + unk188[0] = 255; + unk188[1] = 120; + unk188[2] = 100; + + unk198[0]++; + unk198[1]--; + } else if (globalCtx->csCtx.frames < 170) { + temp_f0 = Environment_LerpWeightAccelDecel(170, 160, globalCtx->csCtx.frames, 0, 0); + + unk178[0] = (temp_f0 * -63.0f) + 163.0f; + unk178[1] = (temp_f0 * -155.0f) + 255.0f; + unk178[2] = temp_f0 * -100.0f + 200.0f; + + unk188[0] = (temp_f0 * -155.0f) + 255.0f; + unk188[1] = (temp_f0 * -20.0f) + 120.0f; + unk188[2] = 100; + } else { + unk178[0] = 100; + unk178[1] = 100; + unk178[2] = 100; + + unk188[0] = 100; + unk188[1] = 100; + unk188[2] = 100; + } +} + +void func_8097F3EC(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 1)) { + this->updateMode = 8; + } +} + +void DemoGt_Update0(DemoGt* this, GlobalContext* globalCtx) { + func_8097F280(this, globalCtx); + func_8097E824(this, 1); + func_8097F19C(this, globalCtx); + func_8097F3EC(this, globalCtx); + DemoGt_PlayEarthquakeSfx(); + DemoGt_Rumble(globalCtx); + func_8097EF00(this, globalCtx); +} + +void DemoGt_Update8(DemoGt* this, GlobalContext* globalCtx) { + func_8097F280(this, globalCtx); + func_8097F1D8(this); + func_8097ED64(this, globalCtx, 1); + func_8097F19C(this, globalCtx); + DemoGt_PlayEarthquakeSfx(); + DemoGt_Rumble(globalCtx); + func_8097EF00(this, globalCtx); +} + +void DemoGt_Draw1(DemoGt* this, GlobalContext* globalCtx) { + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u32 gameplayFrames = globalCtx->gameplayFrames; + s16 pad2[2]; + s16 spC6; + f32 spC0; + f32 spBC; + s16 spBA; + s16 spB8; + Mtx* spB4; + Vec3f spA8; + Vec3f sp9C; + f32 sp98; + s32* unk198; + s32* unk188; + s32* unk178; + + spC6 = this->unk_172; + spC0 = fabsf(spC6 * (M_PI / 0x8000)); + spBC = kREG(71); + spB8 = (s16)((s32)kREG(70)) + 0x4000; + spBA = kREG(70); + spB4 = Graph_Alloc(gfxCtx, sizeof(Mtx)); + sp98 = 1.0f - Math_CosS(spC6); + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part1.c", 458); + + spA8.x = Math_CosS(spB8); + spA8.y = 0.0f; + spA8.z = Math_SinS(spB8); + sp9C.x = Math_CosS(spBA) * spBC * sp98; + sp9C.y = Math_SinS(spC6) * spBC; + sp9C.z = Math_SinS(spBA) * spBC * sp98; + + Matrix_Push(); + + Matrix_RotateAxis(spC0, &spA8, MTXMODE_APPLY); + Matrix_Translate(sp9C.x, sp9C.y, sp9C.z, MTXMODE_APPLY); + Matrix_ToMtx(spB4, "../z_demo_gt_part1.c", 474); + unk198 = this->unk_198; + unk188 = this->unk_188; + unk178 = this->unk_178; + + Matrix_Pop(); + + func_80093D18(gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(gfxCtx, 0, 0, unk198[0], 0x20, 0x40, 1, 0, unk198[1], 0x20, 0x40, unk178[0], + unk178[1], unk178[2], 0x80)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_TwoTexScrollEnvColor(gfxCtx, 0, 0, unk198[0], 0x20, 0x40, 1, 0, unk198[1], 0x20, 0x40, unk188[0], + unk188[1], unk188[2], 0x80)); + gSPMatrix(POLY_OPA_DISP++, spB4, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsExteriorStructureDL); + func_80093D84(gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 128); + gSPSegment( + POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(gfxCtx, 0, 0, gameplayFrames * 0x14, 0x10, 0x200, 1, 0, gameplayFrames * 0x1E, 0x10, 0x200)); + gSPMatrix(POLY_XLU_DISP++, spB4, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gTowerCollapseCsFlameSmokeDL); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part1.c", 557); +} + +void func_8097F904_Init1(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + func_8097EE44(this, globalCtx, 1, 2, &gTowerCollapseCsCollapsedStructureInnerCol); +} + +void func_8097F960(DemoGt* this, GlobalContext* globalCtx) { +} + +void func_8097F96C(DemoGt* this, GlobalContext* globalCtx) { + static Actor* cloudRing = NULL; + s32 pad[4]; + Vec3f pos; + Actor* actor; + u16 frames = globalCtx->csCtx.frames; + + if (((frames > 1059) && (frames < 1062)) || kREG(1) == 17) { + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + 612.0f; + pos.z = this->dyna.actor.world.pos.z; + + if (cloudRing == NULL) { + cloudRing = DemoGt_SpawnCloudRing(globalCtx, &pos, 2); + } else { + actor = cloudRing; + actor->world.pos.x = pos.x; + actor->world.pos.y = pos.y; + actor->world.pos.z = pos.z; + } + } +} + +void func_8097FA1C(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { -12.0f, -17.0, 5.0 }; + s32 pad1[3]; + + if (((frames > 502) && !(frames >= 581)) || (kREG(1) == 5)) { + dustPos.x = pos->x + 300.0f; + dustPos.y = pos->y + 360.0f; + dustPos.z = pos->z - 377.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 6.0f, 6, 1, 35); + } +} + +void func_8097FAFC(DemoGt* this, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 1.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static f32 arg4 = 280.0f; + static f32 scale = 8.0f; + static s32 arg6 = 11; + static s32 arg7 = 1; + static s16 life = 3; + s32 pad[2]; + u16 frames = globalCtx->csCtx.frames; + Vec3f pos; + f32 new_var = -200.0; + + if (((frames > 582) && (frames < 683)) || (kREG(1) == 6)) { + pos = this->dyna.actor.world.pos; + pos.y += 680.0f; + + if (frames == 682) { + velocity.y += new_var; + } else if (frames == 681) { + accel.y += new_var; + } + + func_8097E454(globalCtx, &pos, &velocity, &accel, arg4, scale, arg6, arg7, life); + } +} + +void func_8097FC1C(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { 5.0f, -16.0f, -16.0f }; + s32 pad1[3]; + + if (frames > 682 || kREG(1) == 7) { + dustPos.x = pos->x + 260.0f; + dustPos.y = pos->y + 360.0f; + dustPos.z = pos->z + 260.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 6.0f, 6, 0, 35); + } +} + +void func_8097FCE4(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f vec; + u16 frames = globalCtx->csCtx.frames; + + if (frames == 0x1F7 || kREG(1) == 4) { + vec.x = this->dyna.actor.world.pos.x + 300.0f; + vec.y = this->dyna.actor.world.pos.y + 560.0f; + vec.z = this->dyna.actor.world.pos.z - 377.0f; + DemoGt_SpawnExplosionWithSound(globalCtx, &vec, 2.0f); + } +} + +void func_8097FD70(DemoGt* this, GlobalContext* globalCtx) { + func_8097F960(this, globalCtx); + func_8097F96C(this, globalCtx); + func_8097FA1C(this, globalCtx); + func_8097FAFC(this, globalCtx); + func_8097FC1C(this, globalCtx); + func_8097FCE4(this, globalCtx); +} + +void func_8097FDDC(DemoGt* this, GlobalContext* globalCtx) { + s32* unk178 = this->unk_178; + s32* unk198 = this->unk_198; + + if (globalCtx->csCtx.frames < 610) { + unk178[0] = 163; + unk178[1] = 193; + unk178[2] = 193; + unk198[0]++; + unk198[1]--; + } else if (globalCtx->csCtx.frames < 620) { + f32 temp_f0 = Environment_LerpWeightAccelDecel(620, 610, globalCtx->csCtx.frames, 0, 0); + + unk178[0] = (temp_f0 * (-13.0f)) + 163.0f; + unk178[1] = (temp_f0 * (-43.0f)) + 193.0f; + unk178[2] = (temp_f0 * (-43.0f)) + 193.0f; + } else { + unk178[0] = 150; + unk178[1] = 150; + unk178[2] = 150; + } +} + +void func_8097FED8(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 2)) { + this->updateMode = 9; + } +} + +void DemoGt_Update1(DemoGt* this, GlobalContext* globalCtx) { + func_8097FDDC(this, globalCtx); + func_8097E824(this, 2); + func_8097FD70(this, globalCtx); + func_8097FED8(this, globalCtx); +} + +void DemoGt_Update9(DemoGt* this, GlobalContext* globalCtx) { + func_8097FDDC(this, globalCtx); + func_8097ED64(this, globalCtx, 2); + func_8097FD70(this, globalCtx); +} + +void DemoGt_Draw2(DemoGt* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32* unk198; + s32* unk178; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part2.c", 470); + + func_80093D18(gfxCtx); + unk198 = this->unk_198; + unk178 = this->unk_178; + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScrollEnvColor(gfxCtx, 0, 0, unk198[0], 0x20, 0x40, 1, 0, unk198[1], 0x20, 0x40, unk178[0], + unk178[1], unk178[2], 128)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_gt_part2.c", 485), + G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsCollapsedStructureInnerDL); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part2.c", 489); +} + +void func_80980110_Init2(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + func_8097EE44(this, globalCtx, 2, 3, &gTowerCollapseCsCollapsedStructureOuterCol); +} + +void func_8098016C(DemoGt* this, GlobalContext* globalCtx) { +} + +void func_80980178(DemoGt* this, GlobalContext* globalCtx) { +} + +void func_80980184(DemoGt* this, GlobalContext* globalCtx) { + static Actor* cloudRing = NULL; + s32 pad[4]; + Vec3f pos; + Actor* actor; + + if ((globalCtx->csCtx.frames > 1027) && (globalCtx->csCtx.frames < 1031)) { + pos.x = this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y + 247.0f; + pos.z = this->dyna.actor.world.pos.z; + + if (cloudRing == NULL) { + cloudRing = DemoGt_SpawnCloudRing(globalCtx, &pos, 3); + } else { + actor = cloudRing; + actor->world.pos.x = pos.x; + actor->world.pos.y = pos.y; + actor->world.pos.z = pos.z; + } + } +} + +void func_80980218(DemoGt* this, GlobalContext* globalCtx) { + static Actor* cloudRing = NULL; + s32 pad[4]; + Vec3f pos; + Actor* actor; + + if ((globalCtx->csCtx.frames > 997) && (globalCtx->csCtx.frames < 1001)) { + pos.x = this->dyna.actor.home.pos.x; + pos.y = this->dyna.actor.home.pos.y + 38.0f; + pos.z = this->dyna.actor.home.pos.z; + + if (cloudRing == NULL) { + cloudRing = DemoGt_SpawnCloudRing(globalCtx, &pos, 4); + } else { + actor = cloudRing; + actor->world.pos.x = pos.x; + actor->world.pos.y = pos.y; + actor->world.pos.z = pos.z; + } + } +} + +void func_809802AC(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { 0.0f, 0.0f, -10.0f }; + s32 pad1[3]; + + if (frames > 109 && frames < 140) { + dustPos.x = pos->x - 100.0f; + dustPos.y = pos->y + 1260.0f; + dustPos.z = pos->z - 323.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 4.0f, 3, 0, 20); + } +} + +void func_8098036C(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + Vec3f* world = &this->dyna.actor.world.pos; + Vec3f velOffset = { 5.0f, -3.0f, 0.0f }; + s32 pad1[3]; + + if ((frames > 284) && (frames < 421)) { + dustPos.x = world->x + 760.0f; + dustPos.y = world->y - 40.0f; + dustPos.z = world->z - 240.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 6.0f, 6, 1, 35); + } +} + +void func_80980430(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + s32 frames = globalCtx->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { 5.0f, -3.0f, 0.0f }; + s32 pad1[3]; + + if (frames > 709 || kREG(1) == 8) { + dustPos.x = pos->x + 760.0f; + dustPos.y = pos->y - 40.0f; + dustPos.z = pos->z - 240.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 6.0f, 6, 1, 35); + } +} + +void func_80980504(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { 5.0f, -16.0f, -16.0f }; + s32 pad1[3]; + + if ((frames > 704) || kREG(1) == 9) { + dustPos.x = pos->x + 830.0f; + dustPos.y = pos->y + 60.0f; + dustPos.z = pos->z + 390.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 6.0f, 6, 2, 35); + } +} + +void func_809805D8(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + Vec3f* homePos = &this->dyna.actor.home.pos; + Vec3f velOffset = { 15.0f, -26.0, 0.0f }; + s32 pad1[3]; + + if (((frames > 739) && (frames < 781)) || kREG(1) == 11) { + dustPos.x = homePos->x + 550.0f; + dustPos.y = homePos->y - 110.0f; + dustPos.z = homePos->z + 50.0f; + func_8097D7D8(globalCtx, &dustPos, &velOffset, 6.0f, 6, 4, 35); + } +} + +void func_809806B8(DemoGt* this, GlobalContext* globalContext) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalContext->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { 5.0f, -16.0f, -16.0f }; + s32 pad1[3]; + + if ((frames > 964) || (kREG(1) == 12)) { + dustPos.x = pos->x + 460.0f; + dustPos.y = pos->y + 60.0f; + dustPos.z = pos->z + 760.0f; + func_8097D7D8(globalContext, &dustPos, &velOffset, 6.0f, 6, 3, 35); + } +} + +void func_8098078C(DemoGt* this, GlobalContext* globalContext) { + s32 pad[3]; + Vec3f dustPos; + u16 frames = globalContext->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + Vec3f velOffset = { 5.0f, -16.0f, -16.0f }; + s32 pad1[3]; + + if ((frames > 939) || (kREG(1) == 14)) { + dustPos.x = pos->x + 360.0f; + dustPos.y = pos->y + 70.0f; + dustPos.z = pos->z - 640.0f; + func_8097D7D8(globalContext, &dustPos, &velOffset, 6.0, 6, 0, 35); + } +} + +void func_8098085C(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f sp28; + u16 frames = globalCtx->csCtx.frames; + Vec3f* pos = &this->dyna.actor.world.pos; + + if ((frames == 58) || (kREG(1) == 1)) { + sp28.x = pos->x + 900.0f; + sp28.y = pos->y - 50.0f; + sp28.z = pos->z + 93.0f; + DemoGt_SpawnExplosionWithSound(globalCtx, &sp28, 2.0f); + } else if (frames == 80) { + sp28.x = pos->x + 810.0f; + sp28.y = pos->y + 200.0f; + sp28.z = pos->z - 37.0f; + DemoGt_SpawnExplosionWithSound(globalCtx, &sp28, 0.9f); + } else if (frames == 90) { + sp28.x = pos->x - 220.0f; + sp28.y = pos->y + 1350.0f; + sp28.z = pos->z - 287.0f; + DemoGt_SpawnExplosionWithSound(globalCtx, &sp28, 2.0f); + } +} + +void func_809809C0(DemoGt* this, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + DemoGt* this2 = this; + s32 gameplayFrames = globalCtx->gameplayFrames; + u16 frames = globalCtx->csCtx.frames; + Vec3f sp54; + s16 pad[3]; + + if (((frames > 469) && (frames < 481)) || (kREG(1) == 3)) { + Vec3f sp40 = { 20.0f, 6.0f, 0.0f }; + Vec3f sp34 = { 0.0f, 0.0f, 0.0f }; + s16 pad2[3]; + + sp54.x = this2->dyna.actor.world.pos.x + 790.0f; + sp54.y = this2->dyna.actor.world.pos.y + 60.0f; + sp54.z = this2->dyna.actor.world.pos.z + 23.0f; + + if (ABS(gameplayFrames % 12) == 0) { + DemoGt_SpawnExplosionNoSound(globalCtx, &sp54, &sp40, &sp34, 2.0f); + } + } +} + +void func_80980AD4(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[4]; + Vec3f pos; + u16 frames = globalCtx->csCtx.frames; + + if ((frames == 477) || (kREG(2) == 1)) { + pos.x = this->dyna.actor.world.pos.x + 790.0f; + pos.y = this->dyna.actor.world.pos.y + 60.0f; + pos.z = this->dyna.actor.world.pos.z + 23.0f; + + func_8097DAC8(this, globalCtx, &pos); + DemoGt_PlayExplosion2Sfx(globalCtx, &pos); + } +} + +void func_80980B68(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[4]; + Vec3f pos; + u16 frames = globalCtx->csCtx.frames; + + if ((frames == 317) || (kREG(3) == 1)) { + pos.x = this->dyna.actor.world.pos.x + 980.0f; + pos.y = this->dyna.actor.world.pos.y + 410.0f; + pos.z = this->dyna.actor.world.pos.z - 177.0f; + func_8097DD28(this, globalCtx, &pos); + DemoGt_PlayExplosion2Sfx(globalCtx, &pos); + } +} + +void func_80980BFC(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[4]; + Vec3f pos; + u16 frames = globalCtx->csCtx.frames; + + if ((frames == 740) || (kREG(4) == 1)) { + pos.x = this->dyna.actor.world.pos.x + 790.0f; + pos.y = this->dyna.actor.world.pos.y + 60.0f; + pos.z = this->dyna.actor.world.pos.z + 23.0f; + + func_8097DF70(this, globalCtx, &pos); + DemoGt_PlayExplosion2Sfx(globalCtx, &pos); + } +} + +void func_80980C90(DemoGt* this, GlobalContext* globalCtx) { + func_8098016C(this, globalCtx); + func_80980178(this, globalCtx); + func_80980184(this, globalCtx); + func_80980218(this, globalCtx); + func_809802AC(this, globalCtx); + func_8098036C(this, globalCtx); + func_80980430(this, globalCtx); + func_80980504(this, globalCtx); + func_809805D8(this, globalCtx); + func_809806B8(this, globalCtx); + func_8098078C(this, globalCtx); + func_8098085C(this, globalCtx); + func_809809C0(this, globalCtx); + func_80980AD4(this, globalCtx); + func_80980B68(this, globalCtx); + func_80980BFC(this, globalCtx); +} + +void func_80980D74(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 3)) { + this->updateMode = 10; + } +} + +void DemoGt_Update2(DemoGt* this, GlobalContext* globalCtx) { + func_8097E824(this, 3); + func_80980C90(this, globalCtx); + func_80980D74(this, globalCtx); +} + +void DemoGt_Update10(DemoGt* this, GlobalContext* globalCtx) { + func_8097ED64(this, globalCtx, 3); + func_80980C90(this, globalCtx); +} + +void DemoGt_Draw3(DemoGt* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part3.c", 1026); + + func_80093D18(gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_gt_part3.c", 1028), + G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsCollapsedStructureOuterDL); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part3.c", 1032); +} + +void func_80980F00_Init5(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + + func_8097EE44(this, globalCtx, 3, 4, NULL); +} + +void func_80980F58(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + + if (frames == 244) { + func_80078914(&this->dyna.actor.projectedPos, NA_SE_EV_TOWER_PARTS_BROKEN - SFX_FLAG); + } +} + +void func_80980F8C(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f sp58; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + + if ((frames > 259) && (frames < 289)) { + Vec3f velOfset = { 0.0f, -17.0f, 0.0f }; + s32 pad[3]; + + sp58.x = 640.0f; + sp58.y = 2100.0f; + sp58.z = -170.0f; + + Matrix_MultVec3f(&sp58, &dustPos); + func_8097D7D8(globalCtx, &dustPos, &velOfset, 3.0f, 5, 0, 30); + } +} + +void func_8098103C(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 4)) { + this->updateMode = 11; + } else if (func_8097E704(globalCtx, 3, 4)) { + this->updateMode = 16; + } +} + +void DemoGt_Update3(DemoGt* this, GlobalContext* globalCtx) { + func_8097E824(this, 4); + func_80980F58(this, globalCtx); + func_8098103C(this, globalCtx); +} + +void DemoGt_Update11(DemoGt* this, GlobalContext* globalCtx) { + func_8097ED64(this, globalCtx, 4); + func_80980F58(this, globalCtx); +} + +void DemoGt_Update16(DemoGt* this, GlobalContext* globalCtx) { + f32 temp = this->unk_172; + + this->unk_174 = (temp * ((kREG(64) * 0.001f) + 0.048f)) + (kREG(65) + 98.0f); + this->unk_172 += this->unk_174; + + if (this->unk_172 > 0x4000) { + this->unk_172 = 0x4000; + } +} + +void DemoGt_Draw4(DemoGt* this, GlobalContext* globalCtx2) { + GraphicsContext* gfxCtx; + GlobalContext* globalCtx = globalCtx2; + u16 frames = globalCtx->csCtx.frames; + s32 pad; + s16 sp76; + f32 sp70; + f32 sp6C; + s16 sp6A; + s16 sp68; + s16 pad2; + Mtx* sp60; + Vec3f sp54; + Vec3f sp48; + f32 sp44; + + if (frames < 301) { + + sp76 = this->unk_172; + sp70 = fabsf(sp76 * (M_PI / 0x8000)); + sp6C = kREG(61); + sp68 = (s16)((s32)kREG(58)) + 0x4000; + sp6A = kREG(58); + gfxCtx = globalCtx->state.gfxCtx; + sp60 = Graph_Alloc(gfxCtx, sizeof(Mtx)); + sp44 = 1.0f - Math_CosS(sp76); + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part4_1.c", 217); + + sp54.x = Math_CosS(sp68); + sp54.y = 0.0f; + sp54.z = Math_SinS(sp68); + + sp48.x = (Math_CosS(sp6A) * sp6C) * sp44; + sp48.y = Math_SinS(sp76) * sp6C; + sp48.z = (Math_SinS(sp6A) * sp6C) * sp44; + + Matrix_Push(); + + Matrix_RotateAxis(sp70, &sp54, MTXMODE_APPLY); + Matrix_Translate(sp48.x, sp48.y, sp48.z, MTXMODE_APPLY); + Matrix_ToMtx(sp60, "../z_demo_gt_part4_1.c", 232); + + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80980F8C(this, globalCtx); + } + + Matrix_Pop(); + + func_80093D18(gfxCtx); + gSPMatrix(POLY_OPA_DISP++, sp60, (G_MTX_PUSH | G_MTX_LOAD) | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsStandalonePillarDL); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part4_1.c", 246); + } +} + +void func_809813CC_Init6(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + + func_8097EE44(this, globalCtx, 4, 5, NULL); +} + +void func_80981424(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + + if (frames == 789) { + func_80078914(&this->dyna.actor.projectedPos, NA_SE_EV_TOWER_PARTS_BROKEN - SFX_FLAG); + } +} + +void func_80981458(DemoGt* this, GlobalContext* globalCtx) { + s32 pad[3]; + Vec3f sp58; + Vec3f dustPos; + u16 frames = globalCtx->csCtx.frames; + + if (((frames > 855) && (frames < 891)) || (kREG(1) == 13)) { + Vec3f velOffset = { 0.0f, -30.0f, 0.0f }; + s32 pad1[3]; + + sp58.x = 0.0f; + sp58.y = 1170.0f; + sp58.z = -1100.0f; + + Matrix_MultVec3f(&sp58, &dustPos); + func_8097D7D8(globalCtx, &dustPos, &velOffset, 7.1f, 5, 1, 30); + } +} + +void func_80981524(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 5)) { + this->updateMode = 12; + } else if (func_8097E704(globalCtx, 3, 5)) { + this->updateMode = 17; + } +} + +void DemoGt_Update4(DemoGt* this, GlobalContext* globalCtx) { + func_8097E824(this, 5); + func_80981424(this, globalCtx); + func_80981524(this, globalCtx); +} + +void DemoGt_Update12(DemoGt* this, GlobalContext* globalCtx) { + func_8097ED64(this, globalCtx, 5); + func_80981424(this, globalCtx); +} + +void DemoGt_Update17(DemoGt* this, GlobalContext* globalCtx) { + f32 temp = this->unk_172; + + this->unk_174 = temp * ((kREG(66) * 0.001f) + 0.048f) + (kREG(67) + 50.0f); + this->unk_172 += this->unk_174; + + if (this->unk_172 > 0x4000) { + this->unk_172 = 0x4000; + } +} + +void DemoGt_Draw5(DemoGt* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx; + s32 pad; + s16 sp76; + f32 sp70; + f32 sp6C; + s16 sp6A; + s16 sp68; + s16 pad1; + Mtx* sp60; + Vec3f sp54; + Vec3f sp48; + f32 sp44; + + sp76 = this->unk_172; + sp70 = fabsf(sp76 * (M_PI / 0x8000)); + sp6C = kREG(62); + sp6A = kREG(59) - 0x4000; + sp68 = (s16)(kREG(59) - 0x4000) + 0x4000; + gfxCtx = globalCtx->state.gfxCtx; + sp60 = Graph_Alloc(gfxCtx, sizeof(Mtx)); + sp44 = 1 - Math_CosS(sp76); + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part4_2.c", 212); + + sp54.x = Math_CosS(sp68); + sp54.y = 0.0f; + sp54.z = Math_SinS(sp68); + + sp48.x = Math_CosS(sp6A) * sp6C * sp44; + sp48.y = Math_SinS(sp76) * sp6C; + sp48.z = Math_SinS(sp6A) * sp6C * sp44; + + Matrix_Push(); + + Matrix_RotateAxis(sp70, &sp54, MTXMODE_APPLY); + Matrix_Translate(sp48.x, sp48.y, sp48.z, MTXMODE_APPLY); + Matrix_ToMtx(sp60, "../z_demo_gt_part4_2.c", 227); + + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80981458(this, globalCtx); + } + + Matrix_Pop(); + + func_80093D18(gfxCtx); + gSPMatrix(POLY_OPA_DISP++, sp60, G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsStandalonePillarDL); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part4_2.c", 241); +} + +void func_809818A4_Init7(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + + func_8097EE44(this, globalCtx, 5, 6, NULL); +} + +void func_809818FC(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + + if (frames == 845) { + func_80078914(&this->dyna.actor.projectedPos, NA_SE_EV_TOWER_PARTS_BROKEN - SFX_FLAG); + } +} +void func_80981930(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 6)) { + this->updateMode = 13; + } else if (func_8097E704(globalCtx, 3, 6)) { + this->updateMode = 18; + } +} +void DemoGt_Update5(DemoGt* this, GlobalContext* globalCtx) { + func_8097E824(this, 6); + func_809818FC(this, globalCtx); + func_80981930(this, globalCtx); +} + +void DemoGt_Update13(DemoGt* this, GlobalContext* globalCtx) { + func_8097ED64(this, globalCtx, 6); + func_809818FC(this, globalCtx); +} + +void DemoGt_Update18(DemoGt* this, GlobalContext* globalCtx) { + f32 temp = this->unk_172; + + this->unk_174 = (temp * ((kREG(68) * 0.001f) + 0.005f)) + (kREG(69) + 50.0f); + this->unk_172 += this->unk_174; + + if (this->unk_172 > 0x4000) { + this->unk_172 = 0x4000; + } +} + +void DemoGt_Draw6(DemoGt* this, GlobalContext* globalCtx) { + DemoGt* this2 = this; + s16 sp78 = this2->unk_172; + f32 sp74; + f32 sp70; + s16 sp6E; + s16 sp6C; + GraphicsContext* gfxCtx; + Mtx* sp64; + Vec3f sp58; + Vec3f sp4C; + f32 sp48; + + sp74 = fabsf(sp78 * (M_PI / 0x8000)); + sp70 = kREG(63); + sp6E = kREG(60) + 0x4000; + sp6C = kREG(60) + 0x4000; + sp6C += 0x4000; + gfxCtx = globalCtx->state.gfxCtx; + sp64 = Graph_Alloc(gfxCtx, sizeof(Mtx)); + sp48 = 1.0f - Math_CosS(sp78); + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part4_3.c", 276); + + sp58.x = Math_CosS(sp6C); + sp58.y = 0.0f; + sp58.z = Math_SinS(sp6C); + + sp4C.x = Math_CosS(sp6E) * sp70 * sp48; + sp4C.y = Math_SinS(sp78) * sp70; + sp4C.z = Math_SinS(sp6E) * sp70 * sp48; + + Matrix_Push(); + + Matrix_RotateAxis(sp74, &sp58, MTXMODE_APPLY); + Matrix_Translate(sp4C.x, sp4C.y, sp4C.z, MTXMODE_APPLY); + Matrix_ToMtx(sp64, "../z_demo_gt_part4_3.c", 291); + + Matrix_Pop(); + + func_80093D18(gfxCtx); + gSPMatrix(POLY_OPA_DISP++, sp64, G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsStandalonePillarDL); + gSPPopMatrix(POLY_OPA_DISP++, G_MTX_MODELVIEW); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part4_3.c", 307); +} + +void func_80981C94_Init23(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + func_8097EE44(this, globalCtx, 6, 7, NULL); +} + +void func_80981CEC(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + + if (frames == 183) { + func_80078914(&this->dyna.actor.projectedPos, NA_SE_EV_TOWER_PARTS_BROKEN - SFX_FLAG); + } +} + +void func_80981D20(DemoGt* this) { + f32 temp = this->unk_172; + + this->unk_174 = temp * ((kREG(64) * 0.001f) + 0.048f) + (kREG(76) + 100.0f); + this->unk_172 += this->unk_174; + + if (this->unk_172 > (s16)(kREG(80) + 0x4000)) { + this->unk_172 = kREG(80) + 0x4000; + } +} + +void func_80981DC8(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 7)) { + this->updateMode = 0xE; + } +} + +void DemoGt_Update6(DemoGt* this, GlobalContext* globalCtx) { + func_8097E824(this, 7); + func_80981CEC(this, globalCtx); + func_80981DC8(this, globalCtx); +} + +void DemoGt_Update14(DemoGt* this, GlobalContext* globalCtx) { + func_80981D20(this); + func_8097ED64(this, globalCtx, 7); + func_80981CEC(this, globalCtx); +} + +void DemoGt_Draw7(DemoGt* this, GlobalContext* globalCtx) { + DemoGt* this2 = this; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 sp6E; + f32 sp68; + f32 sp64; + s16 sp62; + s16 sp60; + Mtx* sp5C; + Vec3f sp50; + Vec3f sp44; + f32 sp40; + + sp6E = this2->unk_172; + sp68 = fabsf(sp6E * (M_PI / 0x8000)); + sp64 = kREG(75); + sp62 = kREG(74) + 0x7FEC; + sp60 = kREG(74) + 0x7FEC; + sp60 = sp60 + 0x4000; + sp5C = Graph_Alloc(gfxCtx, sizeof(Mtx)); + sp40 = 1.0f - Math_CosS(sp6E); + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part5.c", 136); + + sp50.x = Math_CosS(sp60); + sp50.y = 0.0f; + sp50.z = Math_SinS(sp60); + + sp44.x = (Math_CosS(sp62) * sp64) * sp40; + sp44.y = Math_SinS(sp6E) * sp64; + sp44.z = (Math_SinS(sp62) * sp64) * sp40; + + Matrix_Push(); + + Matrix_RotateAxis(sp68, &sp50, MTXMODE_APPLY); + Matrix_Translate(sp44.x, sp44.y, sp44.z, MTXMODE_APPLY); + Matrix_ToMtx(sp5C, "../z_demo_gt_part5.c", 152); + + Matrix_Pop(); + + func_80093D18(gfxCtx); + gSPMatrix(POLY_OPA_DISP++, sp5C, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsWalkwayDL); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part5.c", 160); +} + +void func_80982054_Init24(DemoGt* this, GlobalContext* globalCtx) { + this->dyna.actor.scale.x *= 10.0f; + this->dyna.actor.scale.y *= 10.0f; + this->dyna.actor.scale.z *= 10.0f; + func_8097EE44(this, globalCtx, 7, 8, NULL); +} + +void func_809820AC(DemoGt* this, GlobalContext* globalCtx) { + u16 frames = globalCtx->csCtx.frames; + + if (frames == 154) { + func_80078914(&this->dyna.actor.projectedPos, NA_SE_EV_TOWER_PARTS_BROKEN - SFX_FLAG); + } +} + +void func_809820E0(DemoGt* this) { + f32 temp = this->unk_172; + + this->unk_174 = (temp * ((kREG(64) * 0.001f) + 0.048f)) + (kREG(79) + 100.0f); + this->unk_172 += this->unk_174; + + if (this->unk_172 > (s16)(kREG(81) + 0x4000)) { + this->unk_172 = kREG(81) + 0x4000; + } +} + +void func_80982188(DemoGt* this, GlobalContext* globalCtx) { + if (func_8097E704(globalCtx, 2, 9) != 0) { + this->updateMode = 15; + } +} + +void DemoGt_Update7(DemoGt* this, GlobalContext* globalCtx) { + func_8097E824(this, 9); + func_809820AC(this, globalCtx); + func_80982188(this, globalCtx); +} + +void DemoGt_Update15(DemoGt* this, GlobalContext* globalCtx) { + func_809820E0(this); + func_8097ED64(this, globalCtx, 9); + func_809820AC(this, globalCtx); +} + +void DemoGt_Draw8(DemoGt* this, GlobalContext* globalCtx) { + DemoGt* this2 = this; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 sp6E; + f32 sp68; + f32 sp64; + s16 sp62; + s16 sp60; + Mtx* sp5C; + Vec3f sp50; + Vec3f sp44; + f32 sp40; + + sp6E = this2->unk_172; + sp68 = fabsf(sp6E * (M_PI / 0x8000)); + sp64 = kREG(78); + sp62 = kREG(77) + 0xBE80; + sp60 = kREG(77) + 0xBE80; + sp60 += 0x4000; + sp5C = Graph_Alloc(gfxCtx, sizeof(Mtx)); + sp40 = 1.0f - Math_CosS(sp6E); + + OPEN_DISPS(gfxCtx, "../z_demo_gt_part6.c", 137); + + sp50.x = Math_CosS(sp60); + sp50.y = 0.0f; + sp50.z = Math_SinS(sp60); + + sp44.x = Math_CosS(sp62) * sp64 * sp40; + sp44.y = Math_SinS(sp6E) * sp64; + sp44.z = Math_SinS(sp62) * sp64 * sp40; + + Matrix_Push(); + + Matrix_RotateAxis(sp68, &sp50, MTXMODE_APPLY); + Matrix_Translate(sp44.x, sp44.y, sp44.z, MTXMODE_APPLY); + Matrix_ToMtx(sp5C, "../z_demo_gt_part6.c", 153); + + Matrix_Pop(); + + func_80093D18(gfxCtx); + gSPMatrix(POLY_OPA_DISP++, sp5C, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTowerCollapseCsAlternativeWalkwayDL); + + CLOSE_DISPS(gfxCtx, "../z_demo_gt_part6.c", 163); +} + +static DemoGtUpdateFunc sUpdateFuncs[] = { + DemoGt_Update0, DemoGt_Update1, DemoGt_Update2, DemoGt_Update3, DemoGt_Update4, + DemoGt_Update5, DemoGt_Update6, DemoGt_Update7, DemoGt_Update8, DemoGt_Update9, + DemoGt_Update10, DemoGt_Update11, DemoGt_Update12, DemoGt_Update13, DemoGt_Update14, + DemoGt_Update15, DemoGt_Update16, DemoGt_Update17, DemoGt_Update18, +}; + +void DemoGt_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoGt* this = (DemoGt*)thisx; + DemoGtUpdateFunc updateFunc; + + if ((this->updateMode < 0) || (this->updateMode >= 19) || (updateFunc = sUpdateFuncs[this->updateMode]) == NULL) { + // "The main mode is strange!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + updateFunc(this, globalCtx); +} + +void DemoGt_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoGt* this = (DemoGt*)thisx; + + switch (this->dyna.actor.params) { + case 0: + func_8097EEA8_Init0(this, globalCtx); + break; + case 1: + func_8097F904_Init1(this, globalCtx); + break; + case 2: + func_80980110_Init2(this, globalCtx); + break; + case 5: + func_80980F00_Init5(this, globalCtx); + break; + case 6: + func_809813CC_Init6(this, globalCtx); + break; + case 7: + func_809818A4_Init7(this, globalCtx); + break; + case 23: + func_80981C94_Init23(this, globalCtx); + break; + case 24: + func_80982054_Init24(this, globalCtx); + break; + default: + // "Demo_Gt_Actor_ct There is no such argument !" + osSyncPrintf("Demo_Gt_Actor_ct そんな引数は無い!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + Actor_Kill(&this->dyna.actor); + } +} + +void DemoGt_Draw0(DemoGt* this, GlobalContext* globalCtx) { +} + +static DemoGtDrawFunc sDrawFuncs[] = { + DemoGt_Draw0, DemoGt_Draw1, DemoGt_Draw2, DemoGt_Draw3, DemoGt_Draw4, + DemoGt_Draw5, DemoGt_Draw6, DemoGt_Draw7, DemoGt_Draw8, +}; + +void DemoGt_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoGt* this = (DemoGt*)thisx; + DemoGtDrawFunc drawFunc; + + if ((this->drawConfig < 0) || (this->drawConfig >= 9) || (drawFunc = sDrawFuncs[this->drawConfig]) == NULL) { + // "The drawing mode is strange !!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + drawFunc(this, globalCtx); +} + +const ActorInit Demo_Gt_InitVars = { + ACTOR_DEMO_GT, + ACTORCAT_PROP, + FLAGS, + OBJECT_GT, + sizeof(DemoGt), + (ActorFunc)DemoGt_Init, + (ActorFunc)DemoGt_Destroy, + (ActorFunc)DemoGt_Update, + (ActorFunc)DemoGt_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_Demo_Gt/z_demo_gt.h b/soh/src/overlays/actors/ovl_Demo_Gt/z_demo_gt.h new file mode 100644 index 000000000..842e3ff46 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Gt/z_demo_gt.h @@ -0,0 +1,24 @@ +#ifndef Z_DEMO_GT_H +#define Z_DEMO_GT_H + +#include "ultra64.h" +#include "global.h" + +struct DemoGt; + +typedef void (*DemoGtUpdateFunc)(struct DemoGt*, GlobalContext*); +typedef void (*DemoGtDrawFunc)(struct DemoGt*, GlobalContext*); + +typedef struct DemoGt { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s32 updateMode; + /* 0x0168 */ s32 drawConfig; + /* 0x016C */ Vec3s unk_16C; + /* 0x0172 */ s16 unk_172; + /* 0x0174 */ s16 unk_174; + /* 0x0178 */ s32 unk_178[4]; + /* 0x0188 */ s32 unk_188[4]; + /* 0x0198 */ s32 unk_198[4]; +} DemoGt; // size = 0x01A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c b/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c new file mode 100644 index 000000000..28f1f455f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.c @@ -0,0 +1,526 @@ +#include "z_demo_ik.h" +#include "vt.h" +#include "objects/object_ik/object_ik.h" + +#define FLAGS ACTOR_FLAG_4 + +void DemoIk_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoIk_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoIk_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoIk_Draw(Actor* thisx, GlobalContext* globalCtx); + +void DemoIk_Type1Init(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type2Init(DemoIk* this, GlobalContext* globalCtx); + +void DemoIk_Type1Action0(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type1Action1(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type1Action2(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type2Action0(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type2Action1(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type2Action2(DemoIk* this, GlobalContext* globalCtx); + +void DemoIk_DrawNothing(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type1Draw(DemoIk* this, GlobalContext* globalCtx); +void DemoIk_Type2Draw(DemoIk* this, GlobalContext* globalCtx); + +void DemoIk_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void DemoIk_BgCheck(DemoIk* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +s32 DemoIk_UpdateSkelAnime(DemoIk* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* DemoIk_GetCue(GlobalContext* globalCtx, s32 index) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[index]; + } + return NULL; +} + +s32 DemoIk_CheckCue(GlobalContext* globalCtx, u16 action, s32 index) { + CsCmdActorAction* cue = DemoIk_GetCue(globalCtx, index); + + if ((cue != NULL) && (cue->action == action)) { + return 1; + } + return 0; +} + +void DemoIk_SetMove(DemoIk* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 1; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void DemoIk_EndMove(DemoIk* this) { + this->skelAnime.moveFlags &= ~1; +} + +f32 DemoIk_GetCurFrame(DemoIk* this) { + return this->skelAnime.curFrame; +} + +Gfx* DemoIk_SetColors(GraphicsContext* gfxCtx, u8 primR, u8 primG, u8 primB, u8 envR, u8 envG, u8 envB) { + Gfx* head = Graph_Alloc(gfxCtx, 3 * sizeof(Gfx)); + Gfx* entry = head; + + gDPSetPrimColor(entry++, 0x00, 0x00, primR, primG, primB, 255); + gDPSetEnvColor(entry++, envR, envG, envB, 255); + gSPEndDisplayList(entry++); + return head; +} + +s32 DemoIk_GetIndexFromParams(s32 params) { + s32 ret; + + if (params == 0) { + ret = 5; + } else if (params == 1) { + ret = 6; + } else { + ret = 7; + } + return ret; +} + +void DemoIk_Type1PlaySound(DemoIk* this) { + switch (this->actor.params) { + case 0: + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_ARMOR_LAND1_DEMO, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + break; + case 1: + if (Animation_OnFrame(&this->skelAnime, 10.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_ARMOR_LAND3_DEMO, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + break; + case 2: + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_ARMOR_LAND2_DEMO, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + break; + } +} + +void DemoIk_SpawnDeadDb(DemoIk* this, GlobalContext* globalCtx) { + static Vec3f deadDbOffsets[] = { + { -14.0f, 5.0f, 5.0f }, { -20.0f, 12.0f, 0.0f }, { -5.0f, 10.0f, -1.0f }, { -10.0f, 8.0f, 14.0f }, + { -3.0f, 10.0f, 7.0f }, { -10.0f, 11.0f, 0.0f }, { 9.0f, 10.0f, -8.0f }, { 4.0f, 10.0f, 3.0f }, + { -6.0f, 13.0f, -5.0f }, { 1.0f, 9.0f, 3.0f }, { -10.0f, 9.0f, 1.0f }, + }; + s32 i; + s32 index = DemoIk_GetIndexFromParams(this->actor.params); + + if (DemoIk_CheckCue(globalCtx, 5, index)) { + Vec3f pos; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 startIndex; + s32 endIndex; + + if (index == 5) { + startIndex = 0; + endIndex = 4; + } else if (index == 7) { + startIndex = 4; + endIndex = 8; + } else { + startIndex = 8; + endIndex = 11; + } + for (i = startIndex; i < endIndex; i++) { + pos.x = deadDbOffsets[i].x + this->actor.world.pos.x; + pos.y = deadDbOffsets[i].y + this->actor.world.pos.y; + pos.z = deadDbOffsets[i].z + this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &pos, &zeroVec, &zeroVec, 10, 7, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); + } + } +} + +void DemoIk_MoveToStartPos(DemoIk* this, GlobalContext* globalCtx, s32 index) { + CsCmdActorAction* cue = DemoIk_GetCue(globalCtx, index); + + if (cue != NULL) { + this->actor.world.pos.x = cue->startPos.x; + this->actor.world.pos.y = cue->startPos.y; + this->actor.world.pos.z = cue->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = cue->rot.y; + } +} + +void DemoIk_Type1Init(DemoIk* this, GlobalContext* globalCtx) { + s32 pad[3]; + SkeletonHeader* skeleton; + AnimationHeader* animation; + f32 phi_f0; + + switch (this->actor.params) { + case 0: + skeleton = &object_ik_Skel_000C90; + animation = &object_ik_Anim_000C6C; + phi_f0 = 30.0f; + break; + case 1: + skeleton = &object_ik_Skel_000660; + animation = &object_ik_Anim_000634; + phi_f0 = 10.0f; + break; + default: + skeleton = &object_ik_Skel_000380; + animation = &object_ik_Anim_00035C; + phi_f0 = 20.0f; + // No break is required for matching + } + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, phi_f0); + SkelAnime_Init(globalCtx, &this->skelAnime, skeleton, NULL, this->jointTable, this->morphTable, 2); + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, 0.0f); +} + +void func_8098393C(DemoIk* this) { + this->actionMode = 0; + this->drawMode = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_8098394C(DemoIk* this, GlobalContext* globalCtx) { + DemoIk_EndMove(this); + DemoIk_MoveToStartPos(this, globalCtx, DemoIk_GetIndexFromParams(this->actor.params)); + this->actionMode = 1; + this->drawMode = 1; + this->actor.shape.shadowAlpha = 255; + this->skelAnime.curFrame = 0.0f; +} + +void func_809839AC(DemoIk* this) { + this->actionMode = 2; + this->drawMode = 1; + this->actor.shape.shadowAlpha = 255; + this->skelAnime.curFrame = 0.0f; +} + +void func_809839D0(DemoIk* this, GlobalContext* globalCtx) { + CsCmdActorAction* cue = DemoIk_GetCue(globalCtx, DemoIk_GetIndexFromParams(this->actor.params)); + + if (cue != NULL) { + s32 nextCsAction = cue->action; + s32 csAction = this->csAction; + + if (nextCsAction != csAction) { + switch (nextCsAction) { + case 1: + func_8098393C(this); + break; + case 2: + func_8098394C(this, globalCtx); + break; + case 3: + func_809839AC(this); + break; + case 4: + Actor_Kill(&this->actor); + break; + case 5: + case 6: + break; + default: + // "there is no such action" + osSyncPrintf("Demo_Ik_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->csAction = nextCsAction; + } + } +} + +void DemoIk_Type1Action0(DemoIk* this, GlobalContext* globalCtx) { + func_809839D0(this, globalCtx); +} + +void DemoIk_Type1Action1(DemoIk* this, GlobalContext* globalCtx) { + DemoIk_BgCheck(this, globalCtx); + func_809839D0(this, globalCtx); +} + +void DemoIk_Type1Action2(DemoIk* this, GlobalContext* globalCtx) { + DemoIk_UpdateSkelAnime(this); + DemoIk_Type1PlaySound(this); + DemoIk_SetMove(this, globalCtx); + DemoIk_BgCheck(this, globalCtx); + DemoIk_SpawnDeadDb(this, globalCtx); + func_809839D0(this, globalCtx); +} + +void DemoIk_Type1PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + DemoIk* this = (DemoIk*)thisx; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_demo_ik_inArmer.c", 385); + if (limbIndex == 1) { + switch (this->actor.params) { + case 0: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_ik_inArmer.c", 390), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016BE0); + break; + case 2: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_ik_inArmer.c", 396), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016F88); + break; + } + } + CLOSE_DISPS(gfxCtx, "../z_demo_ik_inArmer.c", 404); +} + +void DemoIk_Type1Draw(DemoIk* this, GlobalContext* globalCtx) { + s32 pad[2]; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(gfxCtx, "../z_demo_ik_inArmer.c", 422); + func_8002EBCC(&this->actor, globalCtx, 0); + func_80093D18(gfxCtx); + func_80093D84(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, DemoIk_SetColors(gfxCtx, 245, 225, 155, 30, 30, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, DemoIk_SetColors(gfxCtx, 255, 40, 0, 40, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, DemoIk_SetColors(gfxCtx, 255, 255, 255, 20, 40, 30)); + SkelAnime_DrawOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, NULL, DemoIk_Type1PostLimbDraw, this); + CLOSE_DISPS(gfxCtx, "../z_demo_ik_inArmer.c", 444); +} + +void DemoIk_Type2Init(DemoIk* this, GlobalContext* globalCtx) { + s32 pad[2]; + FlexSkeletonHeader* skeleton; + AnimationHeader* animation; + + switch (this->actor.params) { + case 3: + skeleton = &object_ik_Skel_01EB40; + animation = &object_ik_Anim_01EB14; + break; + case 4: + skeleton = &object_ik_Skel_01EE60; + animation = &object_ik_Anim_01EE34; + break; + case 5: + skeleton = &object_ik_Skel_000F30; + animation = &object_ik_Anim_000F0C; + break; + default: + skeleton = &object_ik_Skel_000900; + animation = &object_ik_Anim_0008DC; + } + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, skeleton, NULL, this->jointTable, this->morphTable, 2); + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, 0.0f); + this->actionMode = 3; + this->drawMode = 0; +} + +void DemoIk_Type2PlaySoundOnFrame(DemoIk* this, f32 frame) { + if (Animation_OnFrame(&this->skelAnime, frame)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_ARMOR_OFF_DEMO, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void DemoIk_Type2PlaySound(DemoIk* this) { + switch (this->actor.params) { + case 3: + DemoIk_Type2PlaySoundOnFrame(this, 33.0f); + break; + case 5: + DemoIk_Type2PlaySoundOnFrame(this, 44.0f); + break; + } +} + +void func_80983FDC(DemoIk* this) { + this->actionMode = 3; + this->drawMode = 0; +} + +void func_80983FEC(DemoIk* this, GlobalContext* globalCtx) { + DemoIk_MoveToStartPos(this, globalCtx, 4); + this->actionMode = 4; + this->drawMode = 2; + this->skelAnime.curFrame = 0.0f; +} + +void func_8098402C(DemoIk* this) { + this->actionMode = 5; + this->drawMode = 2; + this->skelAnime.curFrame = 0.0f; +} + +void func_80984048(DemoIk* this, GlobalContext* globalCtx) { + CsCmdActorAction* cue = DemoIk_GetCue(globalCtx, 4); + + if (cue != NULL) { + s32 nextCsAction = cue->action; + s32 csAction = this->csAction; + + if (nextCsAction != csAction) { + switch (nextCsAction) { + case 1: + func_80983FDC(this); + break; + case 5: + func_80983FEC(this, globalCtx); + break; + case 6: + func_8098402C(this); + break; + case 7: + Actor_Kill(&this->actor); + break; + default: + // "there is no such action" + osSyncPrintf("Demo_Ik_inFace_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->csAction = nextCsAction; + } + } +} + +void DemoIk_Type2Action0(DemoIk* this, GlobalContext* globalCtx) { + func_80984048(this, globalCtx); +} + +void DemoIk_Type2Action1(DemoIk* this, GlobalContext* globalCtx) { + func_80984048(this, globalCtx); +} + +void DemoIk_Type2Action2(DemoIk* this, GlobalContext* globalCtx) { + DemoIk_UpdateSkelAnime(this); + DemoIk_Type2PlaySound(this); + func_80984048(this, globalCtx); +} + +s32 DemoIk_Type2OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + DemoIk* this = (DemoIk*)thisx; + + if ((limbIndex == 1) && (DemoIk_GetCurFrame(this) < 30.0f)) { + *dList = NULL; + } + return 0; +} + +void DemoIk_Type2PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + DemoIk* this = (DemoIk*)thisx; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 frame = DemoIk_GetCurFrame(this); + + OPEN_DISPS(gfxCtx, "../z_demo_ik_inFace.c", 268); + if (limbIndex == 1 && (frame >= 30.0f)) { + switch (this->actor.params) { + case 3: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_ik_inFace.c", 274), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_017028); + break; + case 4: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_ik_inFace.c", 280), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_017170); + break; + case 5: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_ik_inFace.c", 286), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016BE0); + break; + default: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_demo_ik_inFace.c", 292), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016CD8); + break; + } + } + CLOSE_DISPS(gfxCtx, "../z_demo_ik_inFace.c", 300); +} + +void DemoIk_Type2Draw(DemoIk* this, GlobalContext* globalCtx) { + s32 pad[2]; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(gfxCtx, "../z_demo_ik_inFace.c", 318); + func_8002EBCC(&this->actor, globalCtx, 0); + func_80093D18(gfxCtx); + func_80093D84(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, DemoIk_SetColors(gfxCtx, 245, 225, 155, 30, 30, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, DemoIk_SetColors(gfxCtx, 255, 40, 0, 40, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, DemoIk_SetColors(gfxCtx, 255, 255, 255, 20, 40, 30)); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + DemoIk_Type2OverrideLimbDraw, DemoIk_Type2PostLimbDraw, this); + CLOSE_DISPS(gfxCtx, "../z_demo_ik_inFace.c", 341); +} + +static DemoIkActionFunc sActionFuncs[] = { + DemoIk_Type1Action0, DemoIk_Type1Action1, DemoIk_Type1Action2, + DemoIk_Type2Action0, DemoIk_Type2Action1, DemoIk_Type2Action2, +}; + +void DemoIk_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoIk* this = (DemoIk*)thisx; + + if (this->actionMode < 0 || this->actionMode >= ARRAY_COUNT(sActionFuncs) || + sActionFuncs[this->actionMode] == NULL) { + // "The main mode is strange" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->actionMode](this, globalCtx); +} + +void DemoIk_DrawNothing(DemoIk* this, GlobalContext* globalCtx) { +} + +static DemoIkDrawFunc sDrawFuncs[] = { + DemoIk_DrawNothing, + DemoIk_Type1Draw, + DemoIk_Type2Draw, +}; + +void DemoIk_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoIk* this = (DemoIk*)thisx; + + if (this->drawMode < 0 || this->drawMode >= ARRAY_COUNT(sDrawFuncs) || sDrawFuncs[this->drawMode] == NULL) { + // "The draw mode is strange" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawMode](this, globalCtx); +} + +const ActorInit Demo_Ik_InitVars = { + ACTOR_DEMO_IK, + ACTORCAT_NPC, + FLAGS, + OBJECT_IK, + sizeof(DemoIk), + (ActorFunc)DemoIk_Init, + (ActorFunc)DemoIk_Destroy, + (ActorFunc)DemoIk_Update, + (ActorFunc)DemoIk_Draw, + NULL, +}; + +void DemoIk_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoIk* this = (DemoIk*)thisx; + + if (this->actor.params == 0 || this->actor.params == 1 || this->actor.params == 2) { + DemoIk_Type1Init(this, globalCtx); + } else { + DemoIk_Type2Init(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.h b/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.h new file mode 100644 index 000000000..24fe5d094 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Ik/z_demo_ik.h @@ -0,0 +1,22 @@ +#ifndef Z_DEMO_IK_H +#define Z_DEMO_IK_H + +#include "ultra64.h" +#include "global.h" + +struct DemoIk; + +typedef void (*DemoIkActionFunc)(struct DemoIk* this, GlobalContext* globalCtx); +typedef void (*DemoIkDrawFunc)(struct DemoIk* this, GlobalContext* globalCtx); + +typedef struct DemoIk { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[2]; + /* 0x019C */ Vec3s morphTable[2]; + /* 0x01A8 */ s32 actionMode; + /* 0x01AC */ s32 drawMode; + /* 0x01B0 */ s32 csAction; +} DemoIk; // size = 0x01B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c new file mode 100644 index 000000000..7570cc0e0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.c @@ -0,0 +1,1213 @@ +/* + * File: z_demo_im.c + * Overlay: Demo_Im + * Description: Impa + */ + +#include "z_demo_im.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "scenes/indoors/nakaniwa/nakaniwa_scene.h" +#include "objects/object_im/object_im.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4) + +void DemoIm_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoIm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoIm_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoIm_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_809856F8(DemoIm* this, GlobalContext* globalCtx); +void func_80985718(DemoIm* this, GlobalContext* globalCtx); +void func_80985738(DemoIm* this, GlobalContext* globalCtx); +void func_80985770(DemoIm* this, GlobalContext* globalCtx); +void func_809857B0(DemoIm* this, GlobalContext* globalCtx); +void func_809857F0(DemoIm* this, GlobalContext* globalCtx); +void func_80985830(DemoIm* this, GlobalContext* globalCtx); +void func_80985C10(DemoIm* this, GlobalContext* globalCtx); +void func_80985C40(DemoIm* this, GlobalContext* globalCtx); +void func_80985C94(DemoIm* this, GlobalContext* globalCtx); +void DemoIm_DrawTranslucent(DemoIm* this, GlobalContext* globalCtx); +void func_809863BC(DemoIm* this, GlobalContext* globalCtx); +void func_809863DC(DemoIm* this, GlobalContext* globalCtx); +void func_80986430(DemoIm* this, GlobalContext* globalCtx); +void func_80986494(DemoIm* this, GlobalContext* globalCtx); +void func_809864D4(DemoIm* this, GlobalContext* globalCtx); +void func_809868E8(DemoIm* this, GlobalContext* globalCtx); +void func_80986908(DemoIm* this, GlobalContext* globalCtx); +void func_80986948(DemoIm* this, GlobalContext* globalCtx); +void func_80986D40(DemoIm* this, GlobalContext* globalCtx); +void func_80986DC8(DemoIm* this, GlobalContext* globalCtx); +void func_80986E20(DemoIm* this, GlobalContext* globalCtx); +void func_80986E40(DemoIm* this, GlobalContext* globalCtx); +void func_80986EAC(DemoIm* this, GlobalContext* globalCtx); +void func_80986F08(DemoIm* this, GlobalContext* globalCtx); +void func_80986F28(DemoIm* this, GlobalContext* globalCtx); +void func_80986F88(DemoIm* this, GlobalContext* globalCtx); +void func_80986FA8(DemoIm* this, GlobalContext* globalCtx); +void func_80987288(DemoIm* this, GlobalContext* globalCtx); +void func_809872A8(DemoIm* this, GlobalContext* globalCtx); +void func_809872F0(DemoIm* this, GlobalContext* globalCtx); +void func_80987330(DemoIm* this, GlobalContext* globalCtx); +void DemoIm_DrawNothing(DemoIm* this, GlobalContext* globalCtx); +void DemoIm_DrawSolid(DemoIm* this, GlobalContext* globalCtx); + +static void* sEyeTextures[] = { + gImpaEyeOpenTex, + gImpaEyeHalfTex, + gImpaEyeClosedTex, +}; + +static u32 D_8098783C = 0; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0x00000000, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, 0x00, 0x00, 0x01 }, + { 25, 80, 0, { 0, 0, 0 } }, +}; + +#include "z_demo_im_cutscene_data.c" EARLY + +static DemoImActionFunc sActionFuncs[] = { + func_809856F8, func_80985718, func_80985738, func_80985770, func_809857B0, func_809857F0, func_80985830, + func_80985C10, func_80985C40, func_80985C94, func_809863BC, func_809863DC, func_80986430, func_80986494, + func_809864D4, func_809868E8, func_80986908, func_80986948, func_80986D40, func_80986DC8, func_80986E20, + func_80986E40, func_80986EAC, func_80986F08, func_80986F28, func_80986F88, func_80986FA8, func_80987288, + func_809872A8, func_809872F0, func_80987330, +}; + +static Vec3f D_809887D8 = { 0.0f, 10.0f, 0.0f }; + +static DemoImDrawFunc sDrawFuncs[] = { + DemoIm_DrawNothing, + DemoIm_DrawSolid, + DemoIm_DrawTranslucent, +}; + +const ActorInit Demo_Im_InitVars = { + ACTOR_DEMO_IM, + ACTORCAT_NPC, + FLAGS, + OBJECT_IM, + sizeof(DemoIm), + (ActorFunc)DemoIm_Init, + (ActorFunc)DemoIm_Destroy, + (ActorFunc)DemoIm_Update, + (ActorFunc)DemoIm_Draw, + NULL, +}; + +void func_80984BE0(DemoIm* this) { + s32 pad[3]; + s16* blinkTimer = &this->blinkTimer; + s16* eyeIndex = &this->eyeIndex; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + + *eyeIndex = *blinkTimer; + if (*eyeIndex >= 3) { + *eyeIndex = 0; + } +} + +void func_80984C68(DemoIm* this) { + this->action = 7; + this->drawConfig = 0; + this->alpha = 0; + this->unk_270 = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_268 = 0.0f; +} + +void func_80984C8C(DemoIm* this, GlobalContext* globalCtx) { + u32* something = &D_8098783C; + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + if (*something != 0) { + if (this->actor.params == 2) { + func_80984C68(this); + } + *something = 0; + } + } else { + if (*something == 0) { + *something = 1; + } + } +} + +void DemoIm_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + DemoIm* this = (DemoIm*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); +} + +void DemoIm_DestroyCollider(Actor* thisx, GlobalContext* globalCtx) { + DemoIm* this = (DemoIm*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void DemoIm_UpdateCollider(DemoIm* this, GlobalContext* globalCtx) { + s32 pad[5]; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void func_80984DB8(DemoIm* this) { + s32 pad[2]; + Vec3s* vec1 = &this->unk_2D4.unk_08; + Vec3s* vec2 = &this->unk_2D4.unk_0E; + + Math_SmoothStepToS(&vec1->x, 0, 20, 6200, 100); + Math_SmoothStepToS(&vec1->y, 0, 20, 6200, 100); + + Math_SmoothStepToS(&vec2->x, 0, 20, 6200, 100); + Math_SmoothStepToS(&vec2->y, 0, 20, 6200, 100); +} + +void func_80984E58(DemoIm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff; + s16 phi_a3; + + this->unk_2D4.unk_18 = player->actor.world.pos; + this->unk_2D4.unk_14 = kREG(16) + 4.0f; + + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + phi_a3 = (ABS(yawDiff) < 0x18E3) ? 2 : 1; + func_80034A14(&this->actor, &this->unk_2D4, kREG(17) + 0xC, phi_a3); +} + +void func_80984F10(DemoIm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_2D4.unk_18 = player->actor.world.pos; + this->unk_2D4.unk_14 = kREG(16) + 12.0f; + + func_80034A14(&this->actor, &this->unk_2D4, kREG(17) + 0xC, 2); +} + +void func_80984F94(DemoIm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_2D4.unk_18 = player->actor.world.pos; + this->unk_2D4.unk_14 = kREG(16) + 4.0f; + func_80034A14(&this->actor, &this->unk_2D4, kREG(17) + 0xC, 4); +} + +void DemoIm_UpdateBgCheckInfo(DemoIm* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +s32 DemoIm_UpdateSkelAnime(DemoIm* this) { + return SkelAnime_Update(&this->skelAnime); +} + +s32 DemoIm_IsCsStateIdle(GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + return true; + } else { + return false; + } +} + +CsCmdActorAction* DemoIm_GetNpcAction(GlobalContext* globalCtx, s32 actionIdx) { + s32 pad[2]; + CsCmdActorAction* ret = NULL; + + if (!DemoIm_IsCsStateIdle(globalCtx)) { + ret = globalCtx->csCtx.npcActions[actionIdx]; + } + return ret; +} + +s32 func_809850E8(DemoIm* this, GlobalContext* globalCtx, u16 action, s32 actionIdx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, actionIdx); + + if (npcAction != NULL) { + if (npcAction->action == action) { + return true; + } + } + return false; +} + +s32 func_80985134(DemoIm* this, GlobalContext* globalCtx, u16 action, s32 actionIdx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, actionIdx); + + if (npcAction != NULL) { + if (npcAction->action != action) { + return true; + } + } + return false; +} + +void func_80985180(DemoIm* this, GlobalContext* globalCtx, s32 actionIdx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, actionIdx); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +void func_80985200(DemoIm* this, GlobalContext* globalCtx, s32 actionIdx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, actionIdx); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +void DemoIm_ChangeAnim(DemoIm* this, AnimationHeader* animHeaderSeg, u8 animMode, f32 transitionRate, + s32 playBackwards) { + f32 frameCount = Animation_GetLastFrame(animHeaderSeg); + f32 playbackSpeed; + f32 startFrame; + f32 endFrame; + + if (!playBackwards) { + startFrame = 0.0f; + endFrame = frameCount; + playbackSpeed = 1.0f; + } else { + endFrame = 0.0f; + startFrame = frameCount; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animHeaderSeg, playbackSpeed, startFrame, endFrame, animMode, transitionRate); +} + +void func_80985310(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_ChangeAnim(this, &gImpaIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->actor.shape.yOffset = -10000.0f; +} + +void func_80985358(DemoIm* this, GlobalContext* globalCtx) { + f32 posX = this->actor.world.pos.x; + f32 posY = this->actor.world.pos.y; + f32 posZ = this->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, + WARP_SAGES); +} + +void func_809853B4(DemoIm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 playerX = player->actor.world.pos.x; + f32 playerY = player->actor.world.pos.y + 80.0f; + f32 playerZ = player->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, playerX, playerY, playerZ, 0, + 0, 0, 0xD); + Item_Give(globalCtx, ITEM_MEDALLION_SHADOW); +} + +void func_80985430(DemoIm* this, GlobalContext* globalCtx) { + this->actor.shape.yOffset += 250.0f / 3.0f; +} + +void func_8098544C(DemoIm* this, GlobalContext* globalCtx) { + s32 pad[2]; + + if ((gSaveContext.chamberCutsceneNum == 4) && (gSaveContext.sceneSetupIndex < 4)) { + Player* player = GET_PLAYER(globalCtx); + + this->action = 1; + globalCtx->csCtx.segment = D_8098786C; + gSaveContext.cutsceneTrigger = 2; + Item_Give(globalCtx, ITEM_MEDALLION_SHADOW); + player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + } +} + +void func_809854DC(DemoIm* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[5] != NULL) && + (globalCtx->csCtx.npcActions[5]->action == 2)) { + Animation_Change(&this->skelAnime, &gImpaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->action = 2; + this->drawConfig = 1; + func_80985358(this, globalCtx); + } +} + +void func_8098557C(DemoIm* this) { + if (this->actor.shape.yOffset >= 0.0f) { + this->action = 3; + this->actor.shape.yOffset = 0.0f; + } +} + +void func_809855A8(DemoIm* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[5] != NULL) && + (globalCtx->csCtx.npcActions[5]->action == 3)) { + Animation_Change(&this->skelAnime, &gImpaRaiseArmsAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaRaiseArmsAnim), + ANIMMODE_ONCE, 4.0f); + this->action = 4; + } +} + +void func_80985640(DemoIm* this, s32 arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gImpaPresentShadowMedallionAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gImpaPresentShadowMedallionAnim), ANIMMODE_LOOP, 0.0f); + this->action = 5; + } +} + +void func_809856AC(DemoIm* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[6] != NULL) && + (globalCtx->csCtx.npcActions[6]->action == 2)) { + this->action = 6; + func_809853B4(this, globalCtx); + } +} + +void func_809856F8(DemoIm* this, GlobalContext* globalCtx) { + func_8098544C(this, globalCtx); +} + +void func_80985718(DemoIm* this, GlobalContext* globalCtx) { + func_809854DC(this, globalCtx); +} + +void func_80985738(DemoIm* this, GlobalContext* globalCtx) { + func_80985430(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_8098557C(this); +} + +void func_80985770(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_809855A8(this, globalCtx); +} + +void func_809857B0(DemoIm* this, GlobalContext* globalCtx) { + s32 sp1C; + + DemoIm_UpdateBgCheckInfo(this, globalCtx); + sp1C = DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80985640(this, sp1C); +} + +void func_809857F0(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_809856AC(this, globalCtx); +} + +void func_80985830(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); +} + +void func_80985860(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_ChangeAnim(this, &gImpaIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = 7; + this->actor.shape.shadowAlpha = 0; +} + +void func_809858A8(void) { + func_800788CC(NA_SE_SY_WHITE_OUT_T); +} + +void DemoIm_SpawnLightBall(DemoIm* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + (kREG(17) + 24.0f) + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 6); +} + +void func_80985948(DemoIm* this, GlobalContext* globalCtx) { + if (func_809850E8(this, globalCtx, 4, 5)) { + Animation_Change(&this->skelAnime, &gImpaPrepareSealGanonAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gImpaPrepareSealGanonAnim), ANIMMODE_ONCE, 0.0f); + this->action = 8; + this->drawConfig = 2; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_268 = 0.0f; + func_809858A8(); + } +} + +void func_809859E0(DemoIm* this, GlobalContext* globalCtx) { + f32* unk_268 = &this->unk_268; + s32 alpha = 255; + + if (func_809850E8(this, globalCtx, 4, 5)) { + *unk_268 += 1.0f; + if (*unk_268 >= kREG(5) + 10.0f) { + this->action = 9; + this->drawConfig = 1; + *unk_268 = kREG(5) + 10.0f; + this->alpha = this->actor.shape.shadowAlpha = alpha; + return; + } + } else { + *unk_268 -= 1.0f; + if (*unk_268 <= 0.0f) { + this->action = 7; + this->drawConfig = 0; + *unk_268 = 0.0f; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + return; + } + } + this->actor.shape.shadowAlpha = this->alpha = (*unk_268 / (kREG(5) + 10.0f)) * 255.0f; +} + +void func_80985B34(DemoIm* this, GlobalContext* globalCtx) { + if (func_80985134(this, globalCtx, 4, 5)) { + Animation_Change(&this->skelAnime, &gImpaSealGanonAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaSealGanonAnim), + ANIMMODE_ONCE, -8.0f); + this->action = 8; + this->drawConfig = 2; + this->unk_268 = kREG(5) + 10.0f; + this->alpha = 255; + if (this->unk_270 == 0) { + DemoIm_SpawnLightBall(this, globalCtx); + this->unk_270 = 1; + } + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80985C10(DemoIm* this, GlobalContext* globalCtx) { + func_80985948(this, globalCtx); + func_80984C8C(this, globalCtx); +} + +void func_80985C40(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_809859E0(this, globalCtx); + func_80984C8C(this, globalCtx); +} + +void func_80985C94(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80985B34(this, globalCtx); + func_80984C8C(this, globalCtx); +} + +void DemoIm_DrawTranslucent(DemoIm* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeIndex = this->eyeIndex; + void* eyeTex = sEyeTextures[eyeIndex]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_im_inKenjyanomaDemo02.c", 281); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, &D_80116280[0]); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + NULL, NULL, NULL, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_im_inKenjyanomaDemo02.c", 308); +} + +void func_80985E60(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_ChangeAnim(this, &gImpaIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = 10; + this->unk_280 = 1; +} + +void func_80985EAC(DemoIm* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.frames >= 80) && (globalCtx->csCtx.frames < 243)) { + func_80984F10(this, globalCtx); + } else { + func_80984DB8(this); + } +} + +void func_80985EF4(DemoIm* this) { + if (!Animation_OnFrame(&this->skelAnime, Animation_GetLastFrame(&gImpaWhistlingAnim) - 1.0f)) { + DemoIm_UpdateSkelAnime(this); + } +} + +void func_80985F54(DemoIm* this) { + this->action = 10; + this->drawConfig = 0; +} + +void func_80985F64(DemoIm* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gImpaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaIdleAnim), + ANIMMODE_LOOP, 0.0f); + func_80985180(this, globalCtx, 5); + this->action = 11; + this->drawConfig = 1; +} + +void func_80985FE8(DemoIm* this, s32 arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gImpaWhistlingAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaWhistlingAnim), + ANIMMODE_LOOP, -8.0f); + } +} + +void func_8098604C(DemoIm* this) { + f32 frameCount = Animation_GetLastFrame(&gImpaStartWhistlingAnim); + + Animation_Change(&this->skelAnime, &gImpaStartWhistlingAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, -8.0f); + this->action = 12; + this->drawConfig = 1; + this->unk_2D0 = 1; +} + +void func_809860C8(DemoIm* this) { + this->action = 13; + this->drawConfig = 1; +} + +void func_809860DC(DemoIm* this, s32 arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gImpaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaIdleAnim), + ANIMMODE_LOOP, -8.0f); + this->unk_2D0 = 0; + } +} + +void func_80986148(DemoIm* this) { + Animation_Change(&this->skelAnime, &gImpaStartWhistlingAnim, -1.0f, + Animation_GetLastFrame(&gImpaStartWhistlingAnim), 0.0f, ANIMMODE_ONCE, -8.0f); + this->action = 14; + this->drawConfig = 1; +} + +void func_809861C4(DemoIm* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, 5); + + if (npcAction != NULL) { + u32 action = npcAction->action; + u32 unk_274 = this->unk_274; + + if (action != unk_274) { + switch (action) { + case 9: + func_80986148(this); + break; + case 7: + Animation_Change(&this->skelAnime, &gImpaWhistlingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gImpaWhistlingAnim), ANIMMODE_LOOP, -8.0f); + this->action = 12; + break; + default: + osSyncPrintf("Demo_Im_Ocarina_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_274 = action; + } + } +} + +void func_8098629C(DemoIm* this, GlobalContext* globalCtx) { + if (DemoIm_IsCsStateIdle(globalCtx)) { + this->action = 21; + this->drawConfig = 1; + this->unk_280 = 1; + } +} + +void func_809862E0(DemoIm* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, 5); + + if (npcAction != NULL) { + u32 action = npcAction->action; + u32 unk_274 = this->unk_274; + + if (action != unk_274) { + switch (action) { + case 1: + func_80985F54(this); + break; + case 2: + func_80985F64(this, globalCtx); + break; + case 7: + func_8098604C(this); + break; + case 8: + func_809860C8(this); + break; + case 9: + func_80986148(this); + break; + default: + osSyncPrintf("Demo_Im_Ocarina_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_274 = action; + } + } +} + +void func_809863BC(DemoIm* this, GlobalContext* globalCtx) { + func_809862E0(this, globalCtx); +} + +void func_809863DC(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80985EAC(this, globalCtx); + func_80984BE0(this); + func_809862E0(this, globalCtx); +} + +void func_80986430(DemoIm* this, GlobalContext* globalCtx) { + s32 sp24; + + DemoIm_UpdateBgCheckInfo(this, globalCtx); + sp24 = DemoIm_UpdateSkelAnime(this); + func_80985EAC(this, globalCtx); + func_80984BE0(this); + func_80985FE8(this, sp24); + func_809862E0(this, globalCtx); +} + +void func_80986494(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + func_80985EF4(this); + func_80984BE0(this); + func_809861C4(this, globalCtx); +} + +void func_809864D4(DemoIm* this, GlobalContext* globalCtx) { + s32 sp24; + + DemoIm_UpdateBgCheckInfo(this, globalCtx); + sp24 = DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_809860DC(this, sp24); + func_8098629C(this, globalCtx); +} + +void func_8098652C(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_ChangeAnim(this, &gImpaIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = 15; +} + +void func_80986570(DemoIm* this, GlobalContext* globalCtx) { + if (Animation_OnFrame(&this->skelAnime, 7.0f) && (this->actor.bgCheckFlags & 1)) { + u32 sfxId = SFX_FLAG; + + sfxId += SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + Audio_PlaySoundGeneral(sfxId, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void func_809865F8(DemoIm* this, GlobalContext* globalCtx, s32 arg2) { + s32 pad[2]; + + if (arg2 != 0) { + f32* unk_278 = &this->unk_278; + + if (*unk_278 >= 0.0f) { + if (this->unk_27C == 0) { + Vec3f* thisPos = &this->actor.world.pos; + s16 shapeRotY = this->actor.shape.rot.y; + f32 spawnPosX = thisPos->x + (Math_SinS(shapeRotY) * 30.0f); + f32 spawnPosY = thisPos->y; + f32 spawnPosZ = thisPos->z + (Math_CosS(shapeRotY) * 30.0f); + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ARROW, spawnPosX, spawnPosY, spawnPosZ, 0xFA0, + this->actor.shape.rot.y, 0, ARROW_CS_NUT); + this->unk_27C = 1; + } + } else { + *unk_278 += 1.0f; + } + } +} + +void func_80986700(DemoIm* this) { + this->action = 15; + this->drawConfig = 0; +} + +void func_80986710(DemoIm* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gImpaIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gImpaIdleAnim), + ANIMMODE_LOOP, 0.0f); + func_80985180(this, globalCtx, 5); + this->action = 16; + this->drawConfig = 1; +} + +void func_80986794(DemoIm* this) { + Animation_Change(&this->skelAnime, &gImpaThrowDekuNutAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gImpaThrowDekuNutAnim), ANIMMODE_ONCE, -8.0f); + this->action = 17; + this->drawConfig = 1; +} + +void func_8098680C(DemoIm* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, 5); + + if (npcAction != NULL) { + u32 action = npcAction->action; + u32 unk_274 = this->unk_274; + + if (action != unk_274) { + switch (action) { + case 1: + func_80986700(this); + break; + case 2: + func_80986710(this, globalCtx); + break; + case 10: + func_80986794(this); + break; + case 11: + Actor_Kill(&this->actor); + break; + default: + osSyncPrintf("Demo_Im_Spot00_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_274 = action; + } + } +} + +void func_809868E8(DemoIm* this, GlobalContext* globalCtx) { + func_8098680C(this, globalCtx); +} + +void func_80986908(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_8098680C(this, globalCtx); +} + +void func_80986948(DemoIm* this, GlobalContext* globalCtx) { + s32 sp24; + + DemoIm_UpdateBgCheckInfo(this, globalCtx); + sp24 = DemoIm_UpdateSkelAnime(this); + func_80986570(this, globalCtx); + func_80984BE0(this); + func_809865F8(this, globalCtx, sp24); + func_8098680C(this, globalCtx); +} + +void func_809869B0(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_ChangeAnim(this, &gImpaIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = 18; + this->actor.shape.shadowAlpha = 0; +} + +s32 func_809869F8(DemoIm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 playerPosX = player->actor.world.pos.x; + f32 thisPosX = this->actor.world.pos.x; + + if ((thisPosX - (kREG(16) + 30.0f) > playerPosX) && !(this->actor.flags & ACTOR_FLAG_6)) { + return true; + } else { + return false; + } +} + +s32 func_80986A5C(DemoIm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 playerPosX = player->actor.world.pos.x; + f32 thisPosX = this->actor.world.pos.x; + + if ((thisPosX - (kREG(17) + 130.0f) < playerPosX) && (!Gameplay_InCsMode(globalCtx))) { + return true; + } else { + return false; + } +} + +s32 func_80986AD0(DemoIm* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + if (!Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actor.textId = 0x708E; + func_8002F2F4(&this->actor, globalCtx); + } else { + return true; + } + return false; +} + +void func_80986B2C(GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + Player* player = GET_PLAYER(globalCtx); + + globalCtx->nextEntranceIndex = 0xCD; + globalCtx->fadeTransition = 38; + globalCtx->sceneLoadFlag = 0x14; + func_8002DF54(globalCtx, &player->actor, 8); + } +} + +void func_80986BA0(DemoIm* this, GlobalContext* globalCtx) { + if (func_809869F8(this, globalCtx)) { + this->action = 21; + this->drawConfig = 1; + this->unk_280 = 1; + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80986BE4(DemoIm* this, s32 arg1) { + if (arg1 != 0) { + this->action = 22; + } +} + +void func_80986BF8(DemoIm* this, GlobalContext* globalCtx) { + if (gSaveContext.eventChkInf[4] & 1) { + this->action = 24; + this->drawConfig = 1; + this->unk_280 = 1; + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80986C30(DemoIm* this, GlobalContext* globalCtx) { + if (func_80986A5C(this, globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gZeldasCourtyardLullabyCs); + gSaveContext.cutsceneTrigger = 1; + gSaveContext.eventChkInf[5] |= 0x200; + Item_Give(globalCtx, ITEM_SONG_LULLABY); + func_80985F54(this); + } +} + +void func_80986CC8(DemoIm* this) { + if (gSaveContext.eventChkInf[4] & 1) { + this->action = 26; + this->drawConfig = 1; + this->unk_280 = 1; + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80986CFC(DemoIm* this, GlobalContext* globalCtx) { + if (func_80986A5C(this, globalCtx)) { + gSaveContext.eventChkInf[4] |= 0x1000; + this->action = 19; + } +} + +void func_80986D40(DemoIm* this, GlobalContext* globalCtx) { + if (gSaveContext.sceneSetupIndex == 6) { + this->action = 19; + this->drawConfig = 1; + } else if (gSaveContext.eventChkInf[8] & 1) { + Actor_Kill(&this->actor); + } else if (!(gSaveContext.eventChkInf[5] & 0x200)) { + this->action = 23; + } else { + this->action = 20; + } +} + +void func_80986DC8(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80984E58(this, globalCtx); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); +} + +void func_80986E20(DemoIm* this, GlobalContext* globalCtx) { + func_80986BA0(this, globalCtx); +} + +void func_80986E40(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80984E58(this, globalCtx); + DemoIm_UpdateCollider(this, globalCtx); + func_80986BE4(this, func_80986AD0(this, globalCtx)); +} + +void func_80986EAC(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80984F94(this, globalCtx); + DemoIm_UpdateCollider(this, globalCtx); + func_80986B2C(globalCtx); +} + +void func_80986F08(DemoIm* this, GlobalContext* globalCtx) { + func_80986BF8(this, globalCtx); +} + +void func_80986F28(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80984E58(this, globalCtx); + DemoIm_UpdateCollider(this, globalCtx); + func_80986C30(this, globalCtx); +} + +void func_80986F88(DemoIm* this, GlobalContext* globalCtx) { + func_80986CC8(this); +} + +void func_80986FA8(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80984E58(this, globalCtx); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + DemoIm_UpdateCollider(this, globalCtx); + func_80986CFC(this, globalCtx); +} + +void func_80987018(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_ChangeAnim(this, &gImpaIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = 27; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80987064(DemoIm* this) { + f32* unk_268 = &this->unk_268; + f32 temp; + s32 alpha = 255; + + *unk_268 += 1.0f; + temp = kREG(17) + 10.0f; + + if (*unk_268 >= temp) { + this->actor.shape.shadowAlpha = this->alpha = alpha; + } else { + this->actor.shape.shadowAlpha = this->alpha = (*unk_268 / temp) * 255.0f; + } +} + +void func_809870F0(DemoIm* this, GlobalContext* globalCtx) { + func_80985200(this, globalCtx, 5); + this->action = 28; + this->drawConfig = 2; +} + +void func_80987128(DemoIm* this) { + if (this->unk_268 >= kREG(17) + 10.0f) { + this->action = 29; + this->drawConfig = 1; + } +} + +void func_80987174(DemoIm* this) { + DemoIm_ChangeAnim(this, &object_im_Anim_0101C8, ANIMMODE_ONCE, -8.0f, false); + this->action = 30; +} + +void func_809871B4(DemoIm* this, s32 arg1) { + if (arg1 != 0) { + DemoIm_ChangeAnim(this, &object_im_Anim_00FB10, ANIMMODE_LOOP, 0.0f, false); + } +} + +void func_809871E8(DemoIm* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = DemoIm_GetNpcAction(globalCtx, 5); + + if (npcAction != NULL) { + u32 action = npcAction->action; + u32 unk_274 = this->unk_274; + + if (action != unk_274) { + switch (action) { + case 12: + func_809870F0(this, globalCtx); + break; + case 13: + func_80987174(this); + break; + default: + osSyncPrintf("Demo_Im_inEnding_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_274 = action; + } + } +} + +void func_80987288(DemoIm* this, GlobalContext* globalCtx) { + func_809871E8(this, globalCtx); +} + +void func_809872A8(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_80987064(this); + func_80987128(this); +} + +void func_809872F0(DemoIm* this, GlobalContext* globalCtx) { + DemoIm_UpdateBgCheckInfo(this, globalCtx); + DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_809871E8(this, globalCtx); +} + +void func_80987330(DemoIm* this, GlobalContext* globalCtx) { + s32 sp1C; + + DemoIm_UpdateBgCheckInfo(this, globalCtx); + sp1C = DemoIm_UpdateSkelAnime(this); + func_80984BE0(this); + func_809871B4(this, sp1C); +} + +void DemoIm_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoIm* this = (DemoIm*)thisx; + + if ((this->action < 0) || (this->action >= 31) || (sActionFuncs[this->action] == NULL)) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void DemoIm_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoIm* this = (DemoIm*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + DemoIm_InitCollider(thisx, globalCtx); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gImpaSkel, NULL, this->jointTable, this->morphTable, 17); + thisx->flags &= ~ACTOR_FLAG_0; + + switch (this->actor.params) { + case 2: + func_80985860(this, globalCtx); + break; + case 3: + func_80985E60(this, globalCtx); + break; + case 4: + func_8098652C(this, globalCtx); + break; + case 5: + func_809869B0(this, globalCtx); + break; + case 6: + func_80987018(this, globalCtx); + break; + default: + func_80985310(this, globalCtx); + } +} + +void DemoIm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoIm_DestroyCollider(thisx, globalCtx); +} + +s32 DemoIm_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + DemoIm* this = (DemoIm*)thisx; + s32* unk_2D0 = &this->unk_2D0; + + if (this->unk_280 != 0) { + Vec3s* unk_2D4_unk_0E = &this->unk_2D4.unk_0E; + Vec3s* unk_2D4_unk_08 = &this->unk_2D4.unk_08; + + switch (limbIndex) { + case IMPA_LIMB_CHEST: + rot->x += unk_2D4_unk_0E->y; + rot->y -= unk_2D4_unk_0E->x; + break; + case IMPA_LIMB_HEAD: + rot->x += unk_2D4_unk_08->y; + rot->z += unk_2D4_unk_08->x; + break; + } + } + + if ((*unk_2D0 != 0) && (limbIndex == IMPA_LIMB_RIGHT_HAND)) { + *dList = gImpaHandPointingDL; + } + + return false; +} + +void DemoIm_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + DemoIm* this = (DemoIm*)thisx; + + if (limbIndex == IMPA_LIMB_HEAD) { + Vec3f sp28 = D_809887D8; + Vec3f dest; + + Matrix_MultVec3f(&sp28, &dest); + this->actor.focus.pos.x = dest.x; + this->actor.focus.pos.y = dest.y; + this->actor.focus.pos.z = dest.z; + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + } +} + +void DemoIm_DrawNothing(DemoIm* this, GlobalContext* globalCtx) { +} + +void DemoIm_DrawSolid(DemoIm* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeIndex = this->eyeIndex; + void* eyeTexture = sEyeTextures[eyeIndex]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_im.c", 904); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + DemoIm_OverrideLimbDraw, DemoIm_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_im.c", 925); +} + +void DemoIm_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoIm* this = (DemoIm*)thisx; + + if ((this->drawConfig < 0) || (this->drawConfig >= 3) || (sDrawFuncs[this->drawConfig] == NULL)) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.h b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.h new file mode 100644 index 000000000..12e898e27 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im.h @@ -0,0 +1,54 @@ +#ifndef Z_DEMO_IM_H +#define Z_DEMO_IM_H + +#include "ultra64.h" +#include "global.h" + +struct DemoIm; + +typedef void (*DemoImActionFunc)(struct DemoIm*, GlobalContext*); +typedef void (*DemoImDrawFunc)(struct DemoIm*, GlobalContext*); + +typedef enum { + /* 0x00 */ IMPA_LIMB_NONE, + /* 0x01 */ IMPA_LIMB_ROOT, + /* 0x02 */ IMPA_LIMB_TORSO, + /* 0x03 */ IMPA_LIMB_LEFT_THIGH, + /* 0x04 */ IMPA_LIMB_LEFT_LEG, + /* 0x05 */ IMPA_LIMB_LEFT_FOOT, + /* 0x06 */ IMPA_LIMB_RIGHT_THIGH, + /* 0x07 */ IMPA_LIMB_RIGHT_LEG, + /* 0x08 */ IMPA_LIMB_RIGHT_FOOT, + /* 0x09 */ IMPA_LIMB_CHEST, + /* 0x0A */ IMPA_LIMB_LEFT_SHOULDER, + /* 0x0B */ IMPA_LIMB_LEFT_ARM, + /* 0x0C */ IMPA_LIMB_LEFT_HAND, + /* 0x0D */ IMPA_LIMB_RIGHT_SHOULDER, + /* 0x0E */ IMPA_LIMB_RIGHT_ARM, + /* 0x0F */ IMPA_LIMB_RIGHT_HAND, + /* 0x10 */ IMPA_LIMB_HEAD, + /* 0x11 */ IMPA_LIMB_MAX +} ImpaLimb; + +typedef struct DemoIm { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[IMPA_LIMB_MAX]; + /* 0x01F6 */ Vec3s morphTable[IMPA_LIMB_MAX]; + /* 0x025C */ s16 eyeIndex; + /* 0x025E */ s16 blinkTimer; + /* 0x0260 */ s32 action; + /* 0x0264 */ s32 drawConfig; + /* 0x0268 */ f32 unk_268; + /* 0x026C */ s32 alpha; + /* 0x0270 */ s32 unk_270; + /* 0x0274 */ s32 unk_274; + /* 0x0278 */ f32 unk_278; + /* 0x027C */ s32 unk_27C; + /* 0x0280 */ s32 unk_280; + /* 0x0284 */ ColliderCylinder collider; + /* 0x02D0 */ s32 unk_2D0; + /* 0x02D4 */ struct_80034A14_arg1 unk_2D4; +} DemoIm; // size = 0x02FC + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im_cutscene_data.c b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im_cutscene_data.c new file mode 100644 index 000000000..585032fbb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Im/z_demo_im_cutscene_data.c @@ -0,0 +1,222 @@ +#include "z_demo_im.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_8098786C[] = { + CS_BEGIN_CUTSCENE(32, 3000), + CS_UNK_DATA_LIST(0x00000020, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(31, 5), + CS_NPC_ACTION(0x0001, 0, 697, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 697, 698, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 698, 768, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 768, 817, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 82, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 817, 2666, 0x0000, 0x0000, 0x0000, 0, 82, 0, 0, 82, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x000D, 0, 300, 0x0000, 0x0000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0005, 300, 661, 0x0000, 0xEAAA, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0013, 661, 1934, 0x0000, 0x6AAA, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(44, 3), + CS_NPC_ACTION(0x0001, 0, 145, 0x0000, 0x0000, 0x0000, -97, 6, 169, -97, 6, 169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 145, 615, 0x0000, 0x0000, 0x0000, -97, 6, 169, -97, 6, 169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 615, 1906, 0x0000, 0x0000, 0x0000, -97, 6, 169, -97, 6, 169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(49, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, -22, 0, -55, -22, 0, -55, 0.0f, 0.0f, 0.0f), + CS_LIGHTING_LIST(2), + CS_LIGHTING(0x0002, 0, 10, 0x0000, 0x00000000, 0xFFFFFFFE, 0x00000000, 0x0000000D, 0xFFFFFFFE, 0x00000000, 0x0000000D), + CS_LIGHTING(0x0002, 10, 3000, 0x0000, 0x00000000, 0xFFFFFFFE, 0x00000000, 0x0000000D, 0xFFFFFFFE, 0x00000000, 0x0000000D), + CS_SCENE_TRANS_FX(0x0005, 694, 724), + CS_SCENE_TRANS_FX(0x0001, 960, 990), + CS_SCENE_TRANS_FX(0x0001, 683, 692), + CS_NPC_ACTION_LIST(62, 2), + CS_NPC_ACTION(0x0001, 0, 10, 0x0000, 0x0000, 0x0000, 64, 80, 130, 64, 80, 130, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 10, 3000, 0x0000, 0x0000, 0x0000, 64, 80, 130, 64, 80, 130, 0.0f, 0.0f, 0.0f), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0044, 770, 771, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC9, 0x0000005C, 0x00000000, 0xFFFFFFC9, 0x0000005C), + CS_TEXT_LIST(14), + CS_TEXT_NONE(0, 340), + CS_TEXT_DISPLAY_TEXTBOX(0x5022, 340, 353, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(353, 374), + CS_TEXT_DISPLAY_TEXTBOX(0x5025, 374, 404, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(404, 424), + CS_TEXT_DISPLAY_TEXTBOX(0x502B, 424, 474, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(474, 494), + CS_TEXT_DISPLAY_TEXTBOX(0x502C, 494, 543, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(543, 564), + CS_TEXT_DISPLAY_TEXTBOX(0x5026, 564, 613, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(613, 955), + CS_TEXT_DISPLAY_TEXTBOX(0x0041, 955, 959, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(959, 1020), + CS_TEXT_DISPLAY_TEXTBOX(0x5023, 1020, 1029, 0x0000, 0x0000, 0x0000), + CS_TERMINATOR(GRAVEYARD_AFTER_SHADOW_BLUE_WARP, 1060, 1061), + CS_FADE_BGM_LIST(1), + CS_FADE_BGM(0x0004, 673, 723, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC1, 0x00000058, 0x00000000, 0xFFFFFFC1, 0x00000058), + CS_CAM_EYE_LIST(0, 341), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 33, 225, -58, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 33, 225, -58, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 33, 225, -58, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 33, 106, -58, 0x7C50), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 33, 23, -58, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 11, 10, -18, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 11, 10, -18, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 11, 10, -18, 0xE6A0), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.600002f, 11, 10, -18, 0x7C53), + CS_CAM_EYE_LIST(263, 504), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 22.626957f, -49, 13, 158, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 22.626957f, -49, 13, 158, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 22.626957f, -49, 13, 158, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 22.626957f, -21, 22, 150, 0x7C50), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 33.226997f, -21, 22, 150, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 33.226997f, -21, 22, 150, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 33.226997f, -21, 22, 150, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 33.226997f, -21, 22, 150, 0xE6A0), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 33.226997f, -21, 22, 150, 0x7C53), + CS_CAM_EYE_LIST(363, 824), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -67, 8, 117, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -67, 8, 117, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -67, 8, 117, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -67, 8, 117, 0x7C50), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -67, 8, 117, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -67, 8, 117, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.79991f, -67, 8, 117, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.79991f, -67, 8, 117, 0xE6A0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.79991f, -67, 8, 117, 0x7C53), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.79991f, -67, 8, 117, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 70.79991f, -67, 8, 117, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER_LIST(413, 1504), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 19.99985f, -53, 28, 45, 0x20BA), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 19.99985f, -53, 28, 45, 0xD5E0), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 19.99985f, -53, 28, 45, 0x0950), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 19.99985f, -53, 28, 45, 0x7C50), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 19.99985f, -53, 28, 45, 0x0000), + CS_CAM_EYE_LIST(483, 1684), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -67, 8, 117, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -67, 8, 117, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -67, 8, 117, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -71, 15, 124, 0x7C50), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -73, 29, 131, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -78, 40, 140, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -78, 40, 140, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 68.91906f, -78, 40, 140, 0xE6A0), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 68.91906f, -78, 40, 140, 0x7C53), + CS_CAM_EYE_LIST(553, 1644), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -39, 34, 201, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -39, 34, 201, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -39, 34, 201, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -39, 34, 201, 0x7C50), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.599945f, -39, 34, 201, 0x0000), + CS_CAM_EYE_LIST(623, 819), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 9, 13, -17, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 9, 13, -17, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 9, 13, -17, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 9, 71, -17, 0x7C50), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 126.999054f, 9, 385, -17, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 126.999054f, 9, 385, -17, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 126.999054f, 9, 385, -17, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 126.999054f, 9, 385, -17, 0xE6A0), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 126.999054f, 9, 385, -17, 0x7C53), + CS_CAM_EYE_LIST(693, 1035), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 13, 854, 2, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 853, 5, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -3, 853, 5, 0x0950), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 853, -6, 0x7C50), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -2, 852, -17, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, -17, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 16, 852, -6, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, 5, 0xE6A0), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -3, 851, 5, 0x7C53), + CS_CAM_EYE_REL_TO_PLAYER_LIST(769, 1950), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x20BA), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0xD5E0), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 68, -26, 0x0950), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x7C50), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0xFFFF), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 68.599945f, 0, 103, -26, 0xE6A0), + CS_CAM_AT_LIST(0, 370), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -21, 21, 42, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.600002f, -21, 21, 42, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.600002f, -21, 21, 42, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.600002f, -78, 86, 144, 0x7C50), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.600002f, -80, 21, 142, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.600002f, -99, 31, 177, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -99, 31, 177, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -99, 31, 177, 0xE6A0), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.600002f, -99, 31, 177, 0x7C53), + CS_CAM_AT_LIST(263, 533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 22.626957f, -309, 11, 229, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 22.626957f, -309, 11, 229, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 22.626957f, -309, 11, 229, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 33.226997f, -258, 133, 191, 0x7C50), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 33.226997f, -258, 133, 191, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 33.226997f, -258, 133, 191, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 33.226997f, -258, 133, 191, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 33.226997f, -258, 133, 191, 0xE6A0), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 33.226997f, -258, 133, 191, 0x7C53), + CS_CAM_AT_LIST(363, 853), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 68.79994f, 68, 109, -103, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 68.99994f, 68, 109, -103, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 68.79994f, 67, 109, -103, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 68.599945f, 189, 109, 92, 0x7C50), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 68.599945f, 57, 108, 341, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 70.79991f, -186, 107, 341, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 70.79991f, -186, 107, 341, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 70.79991f, -186, 107, 341, 0xE6A0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.79991f, -186, 107, 341, 0x7C53), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.79991f, -186, 107, 341, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 70.79991f, -186, 107, 341, 0x0000), + CS_CAM_AT_REL_TO_PLAYER_LIST(413, 1533), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 19.99985f, 171, 122, -106, 0x20BA), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 19.99985f, 171, 122, -106, 0xD5E0), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 19.99985f, 171, 122, -106, 0x0950), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 19.99985f, 170, 122, -106, 0x7C50), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 19.99985f, 170, 122, -106, 0x0000), + CS_CAM_AT_LIST(483, 1713), + CS_CAM_AT(CS_CMD_CONTINUE, 0x01, 20, 68.91906f, -191, 132, 327, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 68.91906f, -191, 132, 327, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0xFF, 30, 68.91906f, -190, 131, 326, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 68.91906f, -189, 154, 324, 0x7C50), + CS_CAM_AT(CS_CMD_CONTINUE, 0x01, 30, 68.91906f, -194, 162, 331, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 68.91906f, -199, 172, 339, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 68.91906f, -199, 172, 339, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 68.91906f, -199, 172, 339, 0xE6A0), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 68.91906f, -199, 172, 339, 0x7C53), + CS_CAM_AT_LIST(553, 1673), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -234, 123, 37, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -234, 123, 37, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.599945f, -234, 123, 37, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -234, 123, 37, 0x7C50), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.599945f, -233, 123, 37, 0x0000), + CS_CAM_AT_LIST(623, 848), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, -52, 17, 91, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, -52, 17, 91, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 70.599915f, -52, 17, 91, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 70.599915f, -52, 75, 91, 0x7C50), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 126.999054f, -5, 503, 9, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 126.999054f, -5, 503, 9, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 126.999054f, -5, 503, 9, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 126.999054f, -5, 503, 9, 0xE6A0), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 126.999054f, -5, 503, 9, 0x7C53), + CS_CAM_AT_LIST(693, 1084), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.0f, 3, 6, -6, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.0f, 3, 6, -6, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.999966f, 3, 6, -6, 0x0950), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 20.59985f, 3, 6, -6, 0x7C50), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 51, 10.799838f, 3, 6, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.199839f, 3, 6, -6, 0xE6A0), + CS_CAM_AT(CS_CMD_STOP, 0x00, 50, 10.999838f, 3, 6, -6, 0x7C53), + CS_CAM_AT_REL_TO_PLAYER_LIST(769, 1979), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 100, 5, 0x20BA), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 101, 6, 0xD5E0), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 1, 99, 41, 0x0950), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x7C50), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 68.599945f, 0, 42, 16, 0xFFFF), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 68.599945f, 0, 42, 16, 0xE6A0), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c new file mode 100644 index 000000000..a9efbe4fc --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c @@ -0,0 +1,992 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_efc_star_field/object_efc_star_field.h" +#include "objects/object_toki_objects/object_toki_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoKankyo_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void DemoKankyo_SetupType(DemoKankyo* this, GlobalContext* globalCtx); +void DemoKankyo_UpdateClouds(DemoKankyo* this, GlobalContext* globalCtx); +void DemoKankyo_UpdateRock(DemoKankyo* this, GlobalContext* globalCtx); +void DemoKankyo_DoNothing2(DemoKankyo* this, GlobalContext* globalCtx); +void DemoKankyo_UpdateDoorOfTime(DemoKankyo* this, GlobalContext* globalCtx); +void DemoKankyo_DoNothing(DemoKankyo* this, GlobalContext* globalCtx); +void DemoKankyo_KillDoorOfTimeCollision(DemoKankyo* this, GlobalContext* globalCtx); + +void DemoKankyo_DrawRain(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_DrawRock(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_DrawClouds(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_DrawDoorOfTime(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_DrawLightPlane(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_DrawWarpSparkles(Actor* thisx, GlobalContext* globalCtx); +void DemoKankyo_DrawSparkles(Actor* thisx, GlobalContext* globalCtx); + +// adult warp songs cutscenes +extern CutsceneData gAdultWarpInCS[]; +extern CutsceneData gAdultWarpOutCS[]; +// adult warp songs cutscenes in temple of time +extern CutsceneData gAdultWarpInToTCS[]; +extern CutsceneData gAdultWarpOutToTCS[]; +// child warp songs cutscenes +extern CutsceneData gChildWarpInCS[]; +extern CutsceneData gChildWarpOutCS[]; +// child warp songs cutscenes in temple of time +extern CutsceneData gChildWarpInToTCS[]; +extern CutsceneData gChildWarpOutToTCS[]; + +const ActorInit Demo_Kankyo_InitVars = { + ACTOR_DEMO_KANKYO, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(DemoKankyo), + (ActorFunc)DemoKankyo_Init, + (ActorFunc)DemoKankyo_Destroy, + (ActorFunc)DemoKankyo_Update, + (ActorFunc)DemoKankyo_Draw, + NULL, +}; + +static s16 sObjIds[] = { + OBJECT_EFC_STAR_FIELD, OBJECT_EFC_STAR_FIELD, OBJECT_EFC_STAR_FIELD, OBJECT_EFC_STAR_FIELD, OBJECT_EFC_STAR_FIELD, + OBJECT_EFC_STAR_FIELD, OBJECT_EFC_STAR_FIELD, OBJECT_GAMEPLAY_KEEP, OBJECT_GI_MELODY, OBJECT_GI_MELODY, + OBJECT_GI_MELODY, OBJECT_GI_MELODY, OBJECT_GI_MELODY, OBJECT_TOKI_OBJECTS, OBJECT_TOKI_OBJECTS, + OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, OBJECT_GAMEPLAY_KEEP, +}; + +// unused, presumed to be floats +static f32 D_8098C314[] = { + 0.0f, + 150.0f, +}; + +static Color_RGB8 sWarpSparkleEnvColors[] = { + { 0, 200, 0 }, // minuet + { 255, 50, 0 }, // bolero + { 0, 150, 255 }, // serenade + { 255, 150, 0 }, // requiem + { 200, 50, 255 }, // nocturne + { 200, 255, 0 }, // prelude +}; + +static CutsceneCameraPoint sWarpOutCameraPoints[] = { + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0000, 0xFFE5 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0000, 0xFFE5 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0xFFE6, 0x0000, 0x0000 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0017, 0x0024 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x001C, 0x0032, 0xFFFF } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0001, 0x0018, 0xFFD9 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0xFFE6, 0xFFFA, 0x0003 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0025, 0x0037 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x004F, 0x0066, 0x0029 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x00A6, 0x00AD, 0x0006 } }, + { CS_CMD_CONTINUE, 0, 5, 45.0f, { 0x010D, 0x015A, 0xFF4C } }, + { CS_CMD_CONTINUE, 0, 5, 45.0f, { 0x019F, 0x0245, 0xFE35 } }, + { CS_CMD_STOP, 0, 5, 45.0f, { 0x01CE, 0x036F, 0xFCC2 } }, + { CS_CMD_STOP, 0, 5, 45.0f, { 0x01CE, 0x036F, 0xFCC2 } }, +}; + +static CutsceneCameraPoint sWarpInCameraPoints[] = { + { CS_CMD_CONTINUE, 0, 5, 45.0f, { 0x019F, 0x0245, 0xFE35 } }, + { CS_CMD_CONTINUE, 0, 5, 45.0f, { 0x010D, 0x015A, 0xFF4C } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x00A6, 0x00AD, 0x0006 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x004F, 0x0066, 0x0029 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0025, 0x0037 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0xFFE6, 0xFFFA, 0x0003 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0001, 0x0018, 0xFFD9 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x001C, 0x0032, 0xFFFF } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0017, 0x0024 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0xFFE6, 0x0000, 0x0000 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0000, 0xFFE5 } }, + { CS_CMD_CONTINUE, 0, 8, 45.0f, { 0x0000, 0x0000, 0xFFE5 } }, + { CS_CMD_STOP, 0, 5, 45.0f, { 0x01CE, 0x036F, 0xFCC2 } }, + { CS_CMD_STOP, 0, 5, 45.0f, { 0x01CE, 0x036F, 0xFCC2 } }, +}; + +static Color_RGB8 sSparkleEnvColors[] = { + { 0, 200, 0 }, { 255, 50, 0 }, { 0, 150, 255 }, { 255, 150, 0 }, // only this one is used + { 0, 255, 255 }, { 200, 255, 0 }, +}; + +static CutsceneCameraPoint sSparklesCameraPoints[] = { + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF7, 0x0000, 0xFFD0 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF7, 0x0000, 0xFFD0 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF7, 0x0000, 0xFFD0 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF7, 0x0000, 0xFFD0 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD7, 0x0000, 0xFFE9 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD3, 0x0000, 0x000A } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFE8, 0x0001, 0x0027 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0015, 0x0000, 0x002B } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x002F, 0x0005, 0x000E } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0031, 0x0005, 0xFFF5 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0020, 0x0005, 0xFFDA } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF5, 0x0005, 0xFFD1 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD7, 0x0006, 0xFFEA } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD5, 0x0009, 0x000D } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFE9, 0x0009, 0x0027 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0014, 0x000B, 0x0029 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x002D, 0x000B, 0x000F } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x002E, 0x000B, 0xFFF0 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x001E, 0x000B, 0xFFDA } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFFA, 0x000E, 0xFFD3 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFDA, 0x000E, 0xFFEB } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD7, 0x0010, 0x0008 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFE9, 0x0010, 0x0024 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0011, 0x0010, 0x0028 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x002C, 0x0010, 0x000D } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x002C, 0x0012, 0xFFF5 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x001F, 0x0011, 0xFFDE } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFFB, 0x0014, 0xFFD5 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFDD, 0x0014, 0xFFEC } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFDA, 0x0017, 0x0008 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFE8, 0x0014, 0x001F } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x000C, 0x0018, 0x0026 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0027, 0x0018, 0x000D } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0027, 0x001B, 0xFFF6 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x001C, 0x001A, 0xFFE2 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFFA, 0x000E, 0xFFD4 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD9, 0x001B, 0xFFEF } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD7, 0x001B, 0x000A } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFE6, 0x001B, 0x0022 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x000F, 0x001F, 0x002C } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0032, 0x0020, 0x0009 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0030, 0x0021, 0xFFF0 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x001C, 0x0025, 0xFFD9 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFFA, 0x0028, 0xFFD4 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD8, 0x002B, 0xFFF5 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFD7, 0x002B, 0x0006 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFDF, 0x002B, 0x0019 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x000E, 0x002E, 0x002C } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x0032, 0x002E, 0x0003 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0x002A, 0x0030, 0xFFE7 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF6, 0x002B, 0xFFD4 } }, + { CS_CMD_CONTINUE, 0, 2, 45.0f, { 0xFFF6, 0x002B, 0xFFD4 } }, + { CS_CMD_STOP, 0, 2, 45.0f, { 0xFFF6, 0x002B, 0xFFD4 } }, + { CS_CMD_STOP, 0, 2, 45.0f, { 0xFFF6, 0x002B, 0xFFD4 } }, +}; + +static s16 D_8098CF80; +static s16 sRainScale; +static s16 D_8098CF84; + +void DemoKankyo_SetupAction(DemoKankyo* this, DemoKankyoActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void DemoKankyo_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + s16 i; + s32 objBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjIds[this->actor.params]); + + osSyncPrintf("bank_ID = %d\n", objBankIndex); + if (objBankIndex < 0) { + ASSERT(0, "0", "../z_demo_kankyo.c", 521); + } else { + this->objBankIndex = objBankIndex; + } + + switch (this->actor.params) { + case DEMOKANKYO_BLUE_RAIN: + case DEMOKANKYO_BLUE_RAIN_2: + switch (globalCtx->sceneNum) { + case SCENE_HIRAL_DEMO: + globalCtx->roomCtx.curRoom.segment = NULL; + D_8098CF80 = 10; + sRainScale = 8; + break; + case SCENE_TOKINOMA: + D_8098CF80 = 14; + sRainScale = 8; + break; + case SCENE_SPOT00: + D_8098CF80 = 1; + sRainScale = 5; + break; + default: + Actor_Kill(&this->actor); + break; + } + break; + case DEMOKANKYO_ROCK_1: + case DEMOKANKYO_ROCK_2: + case DEMOKANKYO_ROCK_3: + case DEMOKANKYO_ROCK_4: + case DEMOKANKYO_ROCK_5: + globalCtx->roomCtx.curRoom.segment = NULL; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = Rand_ZeroOne() * 0.5f + 0.5f; + this->unk_150[0].unk_0.x = Rand_ZeroOne() * 3.0f + 1.0f; + this->unk_150[0].unk_0.y = Rand_ZeroOne() * 3.0f + 1.0f; + this->unk_150[0].unk_0.z = Rand_ZeroOne() * 3.0f + 1.0f; + break; + case DEMOKANKYO_CLOUDS: + for (i = 0; i < 30; i++) { + this->unk_150[i].unk_20 = Rand_ZeroOne() * 65535.0f; + this->unk_150[i].unk_18 = Rand_ZeroOne() * 100.0f + 60.0f; + } + break; + case DEMOKANKYO_DOOR_OF_TIME: + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 1.0f; + this->unk_150[0].unk_18 = 0.0f; + if (!(gSaveContext.eventChkInf[4] & 0x800)) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_TOKI, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, + 0x0000); + } else { + globalCtx->roomCtx.unk_74[1] = 0xFF; + Actor_Kill(&this->actor); + } + break; + case DEMOKANKYO_LIGHT_PLANE: + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 1.0f; + this->unk_150[0].unk_18 = 0.0f; + break; + case DEMOKANKYO_WARP_OUT: + case DEMOKANKYO_WARP_IN: + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ITEMACTION); + this->actor.flags |= ACTOR_FLAG_25; + this->actor.room = -1; + this->warpTimer = 35; + this->sparkleCounter = 0; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 1.0f; + if (this->actor.params == DEMOKANKYO_WARP_OUT) { + Audio_PlaySoundGeneral(NA_SE_EV_SARIA_MELODY, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + break; + case DEMOKANKYO_SPARKLES: + this->warpTimer = 35; + this->sparkleCounter = 0; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 1.0f; + break; + default: + break; + } + for (i = 0; i < 30; i++) { + this->unk_150[i].unk_22 = 0; + } + DemoKankyo_SetupAction(this, DemoKankyo_SetupType); +} + +void DemoKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + if (thisx) {} +} + +void DemoKankyo_SetupType(DemoKankyo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 temp; + + if (this->actor.objBankIndex == this->objBankIndex) { + switch (this->actor.params) { + case DEMOKANKYO_ROCK_1: + case DEMOKANKYO_ROCK_2: + case DEMOKANKYO_ROCK_3: + case DEMOKANKYO_ROCK_4: + case DEMOKANKYO_ROCK_5: + DemoKankyo_SetupAction(this, DemoKankyo_UpdateRock); + break; + case DEMOKANKYO_CLOUDS: + DemoKankyo_SetupAction(this, DemoKankyo_UpdateClouds); + break; + case DEMOKANKYO_DOOR_OF_TIME: + if (Flags_GetEnv(globalCtx, 2)) { + DemoKankyo_SetupAction(this, DemoKankyo_UpdateDoorOfTime); + } + break; + case DEMOKANKYO_WARP_OUT: + globalCtx->envCtx.screenFillColor[0] = 0xFF; + globalCtx->envCtx.screenFillColor[1] = 0xFF; + globalCtx->envCtx.screenFillColor[2] = 0xFF; + globalCtx->envCtx.fillScreen = false; + if (this->warpTimer < 21 && this->warpTimer >= 15) { + temp = (this->warpTimer - 15.0f) / 5.0f; + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[3] = 255 - 255 * temp; + } + if (this->warpTimer < 15 && this->warpTimer >= 4) { + temp = (this->warpTimer - 4.0f) / 10.0f; + globalCtx->envCtx.fillScreen = true; + globalCtx->envCtx.screenFillColor[3] = 255 * temp; + } + if (this->warpTimer == 15) { + player->actor.draw = NULL; + } + if ((u32)this->warpTimer != 0) { + this->warpTimer--; + } + if (this->warpTimer == 1) { + if (globalCtx->sceneNum == SCENE_TOKINOMA) { + D_8098CF84 = 25; + if (!LINK_IS_ADULT) { + globalCtx->csCtx.segment = gChildWarpInToTCS; + } else { + globalCtx->csCtx.segment = gAdultWarpInToTCS; + } + } else { + D_8098CF84 = 32; + if (!LINK_IS_ADULT) { + globalCtx->csCtx.segment = gChildWarpInCS; + } else { + globalCtx->csCtx.segment = gAdultWarpInCS; + } + } + if (func_800C0CB8(globalCtx) != 0) { + gSaveContext.cutsceneTrigger = 1; + } + DemoKankyo_SetupAction(this, DemoKankyo_DoNothing); + } + break; + case DEMOKANKYO_WARP_IN: + if (globalCtx->sceneNum == SCENE_TOKINOMA) { + if (!LINK_IS_ADULT) { + globalCtx->csCtx.segment = gChildWarpOutToTCS; + } else { + globalCtx->csCtx.segment = gAdultWarpOutToTCS; + } + } else { + if (!LINK_IS_ADULT) { + globalCtx->csCtx.segment = gChildWarpOutCS; + } else { + globalCtx->csCtx.segment = gAdultWarpOutCS; + } + } + gSaveContext.cutsceneTrigger = 1; + DemoKankyo_SetupAction(this, DemoKankyo_DoNothing2); + break; + case DEMOKANKYO_BLUE_RAIN: + case DEMOKANKYO_SPARKLES: + break; + } + } +} + +void DemoKankyo_DoNothing(DemoKankyo* this, GlobalContext* globalCtx) { +} + +void DemoKankyo_DoNothing2(DemoKankyo* this, GlobalContext* globalCtx) { + DemoKankyo_SetupAction(this, DemoKankyo_DoNothing); +} + +void DemoKankyo_SetRockPos(DemoKankyo* this, GlobalContext* globalCtx, s32 params) { + Vec3f startPos; + Vec3f endPos; + CsCmdActorAction* csAction = globalCtx->csCtx.npcActions[params]; + f32 temp_f0; + + startPos.x = csAction->startPos.x; + startPos.y = csAction->startPos.y; + startPos.z = csAction->startPos.z; + endPos.x = csAction->endPos.x; + endPos.y = csAction->endPos.y; + endPos.z = csAction->endPos.z; + temp_f0 = Environment_LerpWeight(csAction->endFrame, csAction->startFrame, globalCtx->csCtx.frames); + this->actor.world.pos.x = ((endPos.x - startPos.x) * temp_f0) + startPos.x; + this->actor.world.pos.y = ((endPos.y - startPos.y) * temp_f0) + startPos.y; + this->actor.world.pos.z = ((endPos.z - startPos.z) * temp_f0) + startPos.z; +} + +void DemoKankyo_UpdateRock(DemoKankyo* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[this->actor.params - 2] != NULL) { + DemoKankyo_SetRockPos(this, globalCtx, this->actor.params - 2); + } + this->unk_150[0].unk_C.x += this->unk_150[0].unk_0.x; + this->unk_150[0].unk_C.y += this->unk_150[0].unk_0.y; + this->unk_150[0].unk_C.z += this->unk_150[0].unk_0.z; +} + +void DemoKankyo_UpdateClouds(DemoKankyo* this, GlobalContext* globalCtx) { + u8 i; + + for (i = 0; i < 30; i++) { + this->unk_150[i].unk_20 += (s16)this->unk_150[i].unk_18; + } +} + +void DemoKankyo_UpdateDoorOfTime(DemoKankyo* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_STONE_STATUE_OPEN - SFX_FLAG); + this->unk_150[0].unk_18 += 1.0f; + if (this->unk_150[0].unk_18 >= 102.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_STONEDOOR_STOP); + gSaveContext.eventChkInf[4] |= 0x800; + Actor_Kill(this->actor.child); + DemoKankyo_SetupAction(this, DemoKankyo_KillDoorOfTimeCollision); + } +} + +void DemoKankyo_KillDoorOfTimeCollision(DemoKankyo* this, GlobalContext* globalCtx) { + Actor_Kill(this->actor.child); +} + +void DemoKankyo_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + this->actionFunc(this, globalCtx); +} + +void DemoKankyo_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + + if (this->actor.objBankIndex == this->objBankIndex) { + switch (this->actor.params) { + case DEMOKANKYO_BLUE_RAIN: + case DEMOKANKYO_BLUE_RAIN_2: + if (globalCtx->sceneNum == SCENE_TOKINOMA) { + if (!Flags_GetEnv(globalCtx, 1)) { + break; + } else if (!Actor_IsFacingAndNearPlayer(&this->actor, 300.0f, 0x7530)) { + break; + } else { + if (!LINK_IS_ADULT) { + if (globalCtx->csCtx.frames < 170 || globalCtx->csCtx.state == CS_STATE_IDLE) { + break; + } + } else { + if (globalCtx->csCtx.frames < 120 || globalCtx->csCtx.state == CS_STATE_IDLE) { + break; + } + } + } + } + DemoKankyo_DrawRain(thisx, globalCtx); + break; + case DEMOKANKYO_ROCK_1: + case DEMOKANKYO_ROCK_2: + case DEMOKANKYO_ROCK_3: + case DEMOKANKYO_ROCK_4: + case DEMOKANKYO_ROCK_5: + DemoKankyo_DrawRock(thisx, globalCtx); + break; + case DEMOKANKYO_CLOUDS: + DemoKankyo_DrawClouds(thisx, globalCtx); + break; + case DEMOKANKYO_DOOR_OF_TIME: + DemoKankyo_DrawDoorOfTime(thisx, globalCtx); + break; + case DEMOKANKYO_LIGHT_PLANE: + DemoKankyo_DrawLightPlane(thisx, globalCtx); + break; + case DEMOKANKYO_WARP_OUT: + case DEMOKANKYO_WARP_IN: + DemoKankyo_DrawWarpSparkles(thisx, globalCtx); + break; + case DEMOKANKYO_SPARKLES: + DemoKankyo_DrawSparkles(thisx, globalCtx); + break; + } + } + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->actor.objBankIndex = this->objBankIndex; + } +} + +// transform relating to blue rain +void func_80989B54(Actor* thisx, GlobalContext* globalCtx, s16 i) { + DemoKankyo* this = (DemoKankyo*)thisx; + + switch (globalCtx->sceneNum) { + case SCENE_HIRAL_DEMO: + this->unk_150[i].unk_0.x = (Rand_ZeroOne() - 0.5f) * 500.0f; + this->unk_150[i].unk_0.y = 500.0f; + this->unk_150[i].unk_0.z = (Rand_ZeroOne() - 0.5f) * 500.0f; + break; + case SCENE_TOKINOMA: + this->unk_150[i].unk_C.x = 0.0f; + this->unk_150[i].unk_C.y = 0.0f; + this->unk_150[i].unk_C.z = 0.0f; + this->unk_150[i].unk_0.x = (Rand_ZeroOne() - 0.5f) * 180.0f; + this->unk_150[i].unk_0.y = 10.0f; + this->unk_150[i].unk_0.z = (Rand_ZeroOne() - 0.5f) * 180.0f; + break; + case SCENE_SPOT00: + this->unk_150[i].unk_0.x = (Rand_ZeroOne() - 0.5f) * 600.0f; + this->unk_150[i].unk_0.y = -500.0f; + this->unk_150[i].unk_0.z = (Rand_ZeroOne() - 0.5f) * 600.0f; + break; + } + this->unk_150[i].unk_18 = Rand_ZeroOne() * (D_8098CF80 * 4.0f) + D_8098CF80; +} + +void DemoKankyo_DrawRain(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + f32 temp_f12_2; + s16 i; + f32 dx; + f32 dy; + f32 dz; + f32 norm; + f32 translateX; + f32 translateY; + f32 translateZ; + s16 j; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1186); + + for (i = 0; i < 30; i++) { + s32 pad[2]; + + dx = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + dy = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + dz = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + norm = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); + + if (globalCtx->sceneNum != SCENE_TOKINOMA) { + this->unk_150[i].unk_C.x = globalCtx->view.eye.x + (dx / norm) * 350.0f; + this->unk_150[i].unk_C.y = globalCtx->view.eye.y + (dy / norm) * 80.0f; + this->unk_150[i].unk_C.z = globalCtx->view.eye.z + (dz / norm) * 350.0f; + } + + switch (this->unk_150[i].unk_22) { + case 0: + func_80989B54(thisx, globalCtx, i); + if (gSaveContext.entranceIndex == 0x00A0) { // Cutscene Map + this->unk_150[i].unk_0.y = Rand_ZeroOne() * 500.0f; + } else { + this->unk_150[i].unk_0.y = Rand_ZeroOne() * -500.0f; + } + this->unk_150[i].unk_22++; + break; + case 1: + temp_f12_2 = globalCtx->view.eye.y + (dy / norm) * 150.0f; + if (gSaveContext.entranceIndex == 0x00A0) { // Cutscene Map + this->unk_150[i].unk_0.y -= this->unk_150[i].unk_18; + } else { + this->unk_150[i].unk_0.y += this->unk_150[i].unk_18; + } + if (gSaveContext.entranceIndex == 0x00A0) { // Cutscene Map + if (this->unk_150[i].unk_C.y + this->unk_150[i].unk_0.y < temp_f12_2 - 300.0f) { + this->unk_150[i].unk_22++; + } + } else if (gSaveContext.entranceIndex == 0x00CD) { // Hyrule Field + if (temp_f12_2 + 300.0f < this->unk_150[i].unk_C.y + this->unk_150[i].unk_0.y) { + this->unk_150[i].unk_22++; + } + } else { + if (1000.0f < this->unk_150[i].unk_C.y + this->unk_150[i].unk_0.y) { + this->unk_150[i].unk_22++; + } + } + break; + case 2: + func_80989B54(thisx, globalCtx, i); + this->unk_150[i].unk_22--; + break; + } + + Matrix_Translate(this->unk_150[i].unk_C.x + this->unk_150[i].unk_0.x, + this->unk_150[i].unk_C.y + this->unk_150[i].unk_0.y, + this->unk_150[i].unk_C.z + this->unk_150[i].unk_0.z, MTXMODE_NEW); + if (gSaveContext.entranceIndex != 0x00A0) { // Cutscene Map + Matrix_RotateX(M_PI, MTXMODE_APPLY); + } + + gDPPipeSync(POLY_XLU_DISP++); + if (gSaveContext.entranceIndex == 0x00CD) { // Hyrule Field + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 0, 255); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 150, 255, 255); + } + + Matrix_Scale(sRainScale * 0.001f, sRainScale * 0.001f, sRainScale * 0.001f, MTXMODE_APPLY); + + for (j = 0; j < 5; j++) { + s32 pad1; + + if (globalCtx->sceneNum != SCENE_TOKINOMA) { + if (this->unk_150[i].unk_0.x >= 0.0f) { + translateX = -j * 1500.0f; + } else { + translateX = j * 1500.0f; + } + if (this->unk_150[i].unk_0.z >= 0.0f) { + translateZ = -j * 1500.0f; + } else { + translateZ = j * 1500.0f; + } + if (j % 2 != 0) { + translateY = j * 4000.0f; + } else { + translateY = -j * 4000.0f; + } + } else { + translateX = 0.0f; + translateY = j * 10.0f; + translateZ = 0.0f; + } + + Matrix_Translate(translateX, translateY, translateZ, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1344), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + gSPDisplayList(POLY_XLU_DISP++, object_efc_star_field_DL_000080); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1358); +} + +void DemoKankyo_DrawRock(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1376); + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateX(DEG_TO_RAD(this->unk_150[0].unk_C.x), MTXMODE_APPLY); + Matrix_RotateY(DEG_TO_RAD(this->unk_150[0].unk_C.y), MTXMODE_APPLY); + Matrix_RotateZ(DEG_TO_RAD(this->unk_150[0].unk_C.z), MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 155, 55, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 155, 255, 55, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1404), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_efc_star_field_DL_000DE0); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1409); +} + +void DemoKankyo_DrawClouds(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + s16 i; + s32 pad; + f32 dx; + f32 dy; + f32 dz; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1425); + + for (i = 0; i < 30; i++) { + dx = -(Math_SinS(this->unk_150[i].unk_20 - 0x8000) * 120.0f) * (30.0f + (i / 30.0f) * 10.0f); + dy = Math_CosS(this->unk_150[i].unk_20 - 0x8000) * 5.0f + 1200.0f; + dz = (Math_CosS(this->unk_150[i].unk_20 - 0x8000) * 120.0f) * (30.0f + (i / 30.0f) * 10.0f); + + Matrix_Translate(globalCtx->view.eye.x + dx, globalCtx->view.eye.y + dy + ((i - 12.0f) * 300.0f), + globalCtx->view.eye.z + dz, MTXMODE_NEW); + Matrix_Scale(125.0f, 60.0f, 125.0f, MTXMODE_APPLY); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 210, 210, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, 255); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_AD_NOTPATTERN | G_CD_MAGICSQ); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1461), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDust5Tex)); + + func_80094C50(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffDustDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1474); +} + +void DemoKankyo_DrawDoorOfTime(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1487); + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Translate(-this->unk_150[0].unk_18, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1492), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_toki_objects_DL_007440); + Matrix_Translate(this->unk_150[0].unk_18 + this->unk_150[0].unk_18, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1497), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_toki_objects_DL_007578); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1501); +} + +void DemoKankyo_DrawLightPlane(Actor* thisx, GlobalContext* globalCtx) { + DemoKankyo* this = (DemoKankyo*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1514); + + if (globalCtx->csCtx.state == CS_STATE_IDLE || gSaveContext.sceneSetupIndex >= 4) { + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, globalCtx->state.frames & 0x7F, 64, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1529), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_toki_objects_DL_008390); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1534); +} + +void DemoKankyo_Vec3fCopy(Vec3f* src, Vec3f* dst) { + dst->x = src->x; + dst->y = src->y; + dst->z = src->z; +} + +Vec3f* DemoKankyo_Vec3fAddVecSph(Vec3f* dst, Vec3f* vec, VecSph* sph) { + Vec3f result; + Vec3f sphVec; + + OLib_VecSphGeoToVec3f(&sphVec, sph); + result.x = vec->x + sphVec.x; + result.y = vec->y + sphVec.y; + result.z = vec->z + sphVec.z; + *dst = result; + return dst; +} + +void DemoKankyo_Vec3fAddPosRot(PosRot* posRot, Vec3f* vec, Vec3f* dst) { + VecSph sph; + Vec3f vecCopy; + + DemoKankyo_Vec3fCopy(vec, &vecCopy); + OLib_Vec3fToVecSphGeo(&sph, &vecCopy); + sph.yaw += posRot->rot.y; + DemoKankyo_Vec3fAddVecSph(dst, &posRot->pos, &sph); +} + +void DemoKankyo_DrawWarpSparkles(Actor* thisx, GlobalContext* globalCtx) { + static f32 sWarpRoll; + static f32 sWarpFoV; + // the following 2 vars are unused + static u32 D_8098CF90; + static u32 D_8098CF94; + static Vec3f D_8098CF98; + + s16 i; + f32 temp_f22; + DemoKankyo* this = (DemoKankyo*)thisx; + Gfx* disp; + Player* player = GET_PLAYER(globalCtx); + Vec3f camPos; + f32 translateX; + f32 translateY; + f32 translateZ; + PosRot posRot; + u8 linkAge = gSaveContext.linkAge; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 1824); + + if (this->sparkleCounter < 30) { + this->sparkleCounter += 2; + } + for (i = this->sparkleCounter - 1; i >= 0; i--) { + temp_f22 = 1.0f - (i / (f32)this->sparkleCounter); + + switch (this->unk_150[i].unk_22) { + case 0: + this->unk_150[i].unk_20 = 0; + this->unk_150[i].unk_1C = 0; + this->unk_150[i].unk_0.x = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f22); + this->unk_150[i].unk_0.y = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f22); + this->unk_150[i].unk_0.z = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f22); + this->unk_150[i].unk_23 = 0; + this->unk_150[i].unk_22++; + case 1: + if (this->actor.params == DEMOKANKYO_WARP_OUT) { + if (func_800BB2B4(&camPos, &sWarpRoll, &sWarpFoV, sWarpOutCameraPoints, &this->unk_150[i].unk_20, + &this->unk_150[i].unk_1C) != 0) { + this->unk_150[i].unk_22++; + } + if (globalCtx->sceneNum == SCENE_TOKINOMA && globalCtx->csCtx.frames == 25) { + this->unk_150[i].unk_22++; + } + } else { + Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP_OUT - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (func_800BB2B4(&camPos, &sWarpRoll, &sWarpFoV, sWarpInCameraPoints, &this->unk_150[i].unk_20, + &this->unk_150[i].unk_1C) != 0) { + this->unk_150[i].unk_22++; + } + if (D_8098CF84 < globalCtx->csCtx.frames && this->actor.params == DEMOKANKYO_WARP_OUT) { + this->unk_150[i].unk_22++; + } + } + Actor_GetWorld(&posRot, &player->actor); + DemoKankyo_Vec3fAddPosRot(&posRot, &camPos, &D_8098CF98); + break; + case 2: + if (this->actor.params == DEMOKANKYO_WARP_OUT) { + if (i == 0) { + Environment_WarpSongLeave(globalCtx); + this->unk_150[i].unk_22++; + } + } else if (i + 1 == this->sparkleCounter && globalCtx->csCtx.state == CS_STATE_IDLE) { + func_80088AF0(globalCtx); + Actor_Kill(&this->actor); + } + break; + } + + this->unk_150[i].unk_C.x = D_8098CF98.x; + this->unk_150[i].unk_C.y = D_8098CF98.y; + this->unk_150[i].unk_C.z = D_8098CF98.z; + + switch (this->unk_150[i].unk_23) { + case 0: + this->unk_150[i].unk_18 = Rand_ZeroOne(); + this->unk_150[i].unk_23++; + case 1: + Math_SmoothStepToF(&this->unk_150[i].unk_18, 1.0f, 0.5f, 0.4f, 0.2f); + if (this->unk_150[i].unk_18 >= 1.0f) { + this->unk_150[i].unk_23 = 2; + } + break; + case 2: + Math_SmoothStepToF(&this->unk_150[i].unk_18, 0.0f, 0.5f, 0.3f, 0.2f); + if (this->unk_150[i].unk_18 <= 0.0f) { + this->unk_150[i].unk_0.x = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f22); + this->unk_150[i].unk_0.y = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f22); + this->unk_150[i].unk_0.z = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f22); + this->unk_150[i].unk_18 = 0.0f; + this->unk_150[i].unk_23 = 1; + } + break; + } + + translateX = this->unk_150[i].unk_C.x + this->unk_150[i].unk_0.x; + translateY = this->unk_150[i].unk_C.y + this->unk_150[i].unk_0.y; + translateZ = this->unk_150[i].unk_C.z + this->unk_150[i].unk_0.z; + + if (this->unk_150[i].unk_22 < 2) { + disp = (uintptr_t)gEffFlash1DL; //This is probably fake + if (linkAge != 0) { + Matrix_Translate(translateX, translateY, translateZ, MTXMODE_NEW); + } else { + if (translateY) {} + Matrix_Translate(translateX, translateY + 15.0f, translateZ, MTXMODE_NEW); + } + Matrix_Scale(this->unk_150[i].unk_18 * (0.018f * temp_f22), this->unk_150[i].unk_18 * (0.018f * temp_f22), + this->unk_150[i].unk_18 * (0.018f * temp_f22), MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 255, 255); + if (this->actor.params == DEMOKANKYO_WARP_OUT) { + gDPSetEnvColor(POLY_XLU_DISP++, sWarpSparkleEnvColors[globalCtx->msgCtx.lastPlayedSong].r, + sWarpSparkleEnvColors[globalCtx->msgCtx.lastPlayedSong].g, + sWarpSparkleEnvColors[globalCtx->msgCtx.lastPlayedSong].b, 255); + } else { + s8 respawnData = gSaveContext.respawn[1].data; + + gDPSetEnvColor(POLY_XLU_DISP++, sWarpSparkleEnvColors[respawnData].r, + sWarpSparkleEnvColors[respawnData].g, sWarpSparkleEnvColors[respawnData].b, 255); + } + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(DEG_TO_RAD(this->unk_150[i].unk_24), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 2011), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, disp); + this->unk_150[i].unk_24 += 0x190; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 2019); +} + +void DemoKankyo_DrawSparkles(Actor* thisx, GlobalContext* globalCtx) { + static f32 sSparklesRoll; + static f32 sSparklesFoV; + // the following 3 vars are unused + static u32 D_8098CFAC; + static u32 D_8098CFB0; + static u32 D_8098CFB4; + static Vec3f D_8098CFB8; + + DemoKankyo* this = (DemoKankyo*)thisx; + f32 translateX; + f32 translateY; + f32 translateZ; + Vec3f camPos; + f32 temp_f20; + f32 scale; + s16 i; + PosRot posRot; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 2434); + + if (this->sparkleCounter < 20) { + this->sparkleCounter++; + } + + for (i = this->sparkleCounter - 1; i >= 0; i--) { + temp_f20 = 1.0f - (i / (f32)this->sparkleCounter); + + switch (this->unk_150[i].unk_22) { + case 0: + this->unk_150[i].unk_20 = 0; + this->unk_150[i].unk_1C = 0; + this->unk_150[i].unk_0.x = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f20); + this->unk_150[i].unk_0.y = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f20); + this->unk_150[i].unk_0.z = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f20); + this->unk_150[i].unk_23 = 0; + this->unk_150[i].unk_22++; + case 1: + if (func_800BB2B4(&camPos, &sSparklesRoll, &sSparklesFoV, sSparklesCameraPoints, + &this->unk_150[i].unk_20, &this->unk_150[i].unk_1C) != 0) { + this->unk_150[i].unk_22++; + } + Actor_GetWorld(&posRot, &this->actor); + DemoKankyo_Vec3fAddPosRot(&posRot, &camPos, &D_8098CFB8); + break; + case 2: + if (i + 1 == this->sparkleCounter && globalCtx->csCtx.state == CS_STATE_IDLE) { + Actor_Kill(&this->actor); + } + break; + } + + this->unk_150[i].unk_C.x = D_8098CFB8.x; + this->unk_150[i].unk_C.y = D_8098CFB8.y; + this->unk_150[i].unk_C.z = D_8098CFB8.z; + + switch (this->unk_150[i].unk_23) { + case 0: + this->unk_150[i].unk_18 = Rand_ZeroOne(); + this->unk_150[i].unk_23++; + case 1: + Math_SmoothStepToF(&this->unk_150[i].unk_18, 1.0f, 0.5f, 0.4f, 0.2f); + if (1.0f <= this->unk_150[i].unk_18) { + this->unk_150[i].unk_23 = 2; + } + break; + case 2: + Math_SmoothStepToF(&this->unk_150[i].unk_18, 0.0f, 0.5f, 0.3f, 0.2f); + if (this->unk_150[i].unk_18 <= 0.0f) { + this->unk_150[i].unk_0.x = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f20); + this->unk_150[i].unk_0.y = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f20); + this->unk_150[i].unk_0.z = (s16)((Rand_ZeroOne() - 0.5f) * 16.0f * temp_f20); + this->unk_150[i].unk_18 = 0.0f; + this->unk_150[i].unk_23 = 1; + } + break; + } + + translateX = this->unk_150[i].unk_C.x + this->unk_150[i].unk_0.x; + translateY = this->unk_150[i].unk_C.y + this->unk_150[i].unk_0.y; + translateZ = this->unk_150[i].unk_C.z + this->unk_150[i].unk_0.z; + + if (this->unk_150[i].unk_22 < 2) { + Matrix_Translate(translateX, translateY, translateZ, MTXMODE_NEW); + scale = this->unk_150[i].unk_18 * (0.02f * temp_f20); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, sSparkleEnvColors[3].r, sSparkleEnvColors[3].g, sSparkleEnvColors[3].b, + 255); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(DEG_TO_RAD(this->unk_150[i].unk_24), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 2572), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + this->unk_150[i].unk_24 += 0x190; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kankyo.c", 2579); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.h b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.h new file mode 100644 index 000000000..efc63ab73 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.h @@ -0,0 +1,52 @@ +#ifndef Z_DEMO_KANKYO_H +#define Z_DEMO_KANKYO_H + +#include "ultra64.h" +#include "global.h" + +struct DemoKankyo; + +typedef void (*DemoKankyoActionFunc)(struct DemoKankyo*, GlobalContext*); + +typedef enum { + /* 0x00 */ DEMOKANKYO_BLUE_RAIN, + /* 0x01 */ DEMOKANKYO_BLUE_RAIN_2, + /* 0x02 */ DEMOKANKYO_ROCK_1, + /* 0x03 */ DEMOKANKYO_ROCK_2, + /* 0x04 */ DEMOKANKYO_ROCK_3, + /* 0x05 */ DEMOKANKYO_ROCK_4, + /* 0x06 */ DEMOKANKYO_ROCK_5, + /* 0x07 */ DEMOKANKYO_CLOUDS, + /* 0x08 */ DEMOKANKYO_8, // These unnamed types are removed types that would have used object_gi_melody + /* 0x09 */ DEMOKANKYO_9, + /* 0x0A */ DEMOKANKYO_A, + /* 0x0B */ DEMOKANKYO_B, + /* 0x0C */ DEMOKANKYO_C, + /* 0x0D */ DEMOKANKYO_DOOR_OF_TIME, + /* 0x0E */ DEMOKANKYO_LIGHT_PLANE, + /* 0x0F */ DEMOKANKYO_WARP_OUT, + /* 0x10 */ DEMOKANKYO_WARP_IN, + /* 0x11 */ DEMOKANKYO_SPARKLES +} DemoKankyoType; + +typedef struct { + /* 0x00 */ Vec3f unk_0; + /* 0x0C */ Vec3f unk_C; + /* 0x18 */ f32 unk_18; // For Door of Time, this is the amount to translate it by used for when it's opening + /* 0x1C */ f32 unk_1C; + /* 0x20 */ s16 unk_20; + /* 0x22 */ u8 unk_22; // mode ? + /* 0x23 */ u8 unk_23; + /* 0x24 */ s16 unk_24; +} DemoKankyoUnk150; // size = 0x28 + +typedef struct DemoKankyo { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 objBankIndex; + /* 0x014D */ u8 sparkleCounter; + /* 0x014E */ u8 warpTimer; + /* 0x0150 */ DemoKankyoUnk150 unk_150[30]; + /* 0x0600 */ DemoKankyoActionFunc actionFunc; +} DemoKankyo; // size = 0x0604 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data1.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data1.c new file mode 100644 index 000000000..b0a4138a7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data1.c @@ -0,0 +1,25 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gAdultWarpInCS[] = { + CS_BEGIN_CUTSCENE(2, 164), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 135), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 79, 59, 0x010F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 78, 60, 0x0120), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 32, 78, 60, 0x0131), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 30, 79, 59, 0x01F4), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 30, 78, 60, 0x01F6), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 30, 78, 60, 0x0207), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.324856f, 30, 78, 60, 0x0000), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 164), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 11, 50, 23, 0x010F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 11, 48, 22, 0x0120), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 60.324856f, 11, 48, 22, 0x0131), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 60.324856f, 57, 101, 21, 0x01F4), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 57, 101, 21, 0x01F6), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 57, 101, 21, 0x0207), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.324856f, 57, 101, 21, 0x0000), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data2.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data2.c new file mode 100644 index 000000000..3e41c6d68 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data2.c @@ -0,0 +1,31 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gAdultWarpOutCS[] = { + CS_BEGIN_CUTSCENE(5, 1167), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 1138), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0x20BA), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0xA1BC), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0xA5E1), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0xA5CB), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0xA5EB), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0x20BA), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 31, 82, 61, 0xA1BC), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.32486f, 31, 82, 61, 0xA5E1), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 1167), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 55, 99, 31, 0x20BA), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 55, 99, 31, 0xA1BC), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 10, 60.32486f, 55, 99, 31, 0xA5E1), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 60.32486f, 17, 59, 31, 0xA5CB), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 17, 59, 31, 0xA5EB), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 60.32486f, 17, 59, 31, 0x20BA), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 17, 59, 31, 0xA1BC), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.32486f, 17, 59, 31, 0xA5E1), + CS_SCENE_TRANS_FX(0x0005, 36, 46), + CS_SCENE_TRANS_FX(0x0001, 30, 35), + CS_MISC_LIST(1), + CS_MISC(0x000C, 95, 97, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFE3, 0xFFFFFFF9, 0x00000000, 0xFFFFFFE3, 0xFFFFFFF9, 0x00000000, 0x00000000, 0x00000000), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data3.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data3.c new file mode 100644 index 000000000..24771268f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data3.c @@ -0,0 +1,25 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gAdultWarpInToTCS[] = { + CS_BEGIN_CUTSCENE(2, 118), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 89), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 71.32476f, 53, 53, 40, 0x010F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 53, 53, 40, 0x0120), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 53, 53, 40, 0x0131), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 102, 48, 0x01F4), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 102, 48, 0x01F6), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 102, 48, 0x0207), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 70.599915f, 58, 102, 48, 0x0047), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 118), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.52477f, 11, 29, 10, 0x010F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.39992f, 11, 29, 10, 0x0120), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.599915f, 11, 29, 10, 0x0131), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.599915f, 29, 71, 25, 0x01F4), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, 29, 71, 25, 0x01F6), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, 29, 71, 25, 0x0207), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 70.599915f, 29, 71, 25, 0x0047), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data4.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data4.c new file mode 100644 index 000000000..e54f6eeb2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data4.c @@ -0,0 +1,25 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gAdultWarpOutToTCS[] = { + CS_BEGIN_CUTSCENE(5, 1120), + CS_SCENE_TRANS_FX(0x0005, 36, 46), + CS_SCENE_TRANS_FX(0x0001, 30, 35), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 1091), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 42, 89, 50, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 42, 89, 50, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 42, 89, 50, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 42, 89, 50, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.32486f, 42, 89, 50, 0x29D0), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 1120), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 24, 66, 29, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 24, 66, 29, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 60.32486f, 24, 66, 29, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 24, 66, 29, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.32486f, 24, 66, 29, 0x29D0), + CS_MISC_LIST(1), + CS_MISC(0x000C, 95, 96, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFEE, 0xFFFFFFF3, 0x00000000, 0xFFFFFFEE, 0xFFFFFFF3, 0x00000000, 0x00000000, 0x00000000), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data5.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data5.c new file mode 100644 index 000000000..73fbdd583 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data5.c @@ -0,0 +1,27 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gChildWarpInCS[] = { + CS_BEGIN_CUTSCENE(2, 1164), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 1135), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 68, 59, 0x010F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 32, 68, 60, 0x0120), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 69, 59, 0x0131), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 64, 59, 0x01F4), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 64, 59, 0x01F6), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 64, 59, 0x0207), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324856f, 31, 64, 59, 0xB46C), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.324856f, 31, 64, 59, 0x05BC), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 1164), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 12, 40, 22, 0x010F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 11, 38, 22, 0x0120), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 60.324856f, 11, 39, 22, 0x0131), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 60.324856f, 57, 86, 21, 0x01F4), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 57, 86, 21, 0x01F6), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 60.324856f, 57, 86, 21, 0x0207), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324856f, 57, 86, 21, 0xB46C), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.324856f, 57, 86, 21, 0x05BC), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data6.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data6.c new file mode 100644 index 000000000..356de57e9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data6.c @@ -0,0 +1,31 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gChildWarpOutCS[] = { + CS_BEGIN_CUTSCENE(5, 1167), + CS_SCENE_TRANS_FX(0x0005, 36, 46), + CS_SCENE_TRANS_FX(0x0001, 30, 35), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 1138), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 30, 63, 61, 0xA8A5), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 30, 63, 61, 0xA3D9), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.32486f, 30, 63, 61, 0xF3A5), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324863f, 29, 61, 59, 0xA5D5), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324863f, 29, 61, 59, 0xA5E7), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324863f, 29, 61, 59, 0xA5EC), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324863f, 29, 61, 59, 0xBCA5), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.324863f, 29, 61, 59, 0xEEC0), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 1167), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 54, 79, 31, 0xA8A5), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.32486f, 54, 80, 31, 0xA3D9), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 10, 60.32486f, 54, 79, 31, 0xF3A5), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 60.324863f, 15, 42, 30, 0xA5D5), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324863f, 15, 42, 30, 0xA5E7), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 60.324863f, 15, 42, 30, 0xA5EC), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324863f, 15, 42, 30, 0xBCA5), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.324863f, 15, 42, 30, 0xEEC0), + CS_MISC_LIST(1), + CS_MISC(0x000C, 95, 96, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFEF, 0xFFFFFFCD, 0x00000000, 0xFFFFFFEF, 0xFFFFFFCD, 0x00000000, 0x00000000, 0x00000000), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data7.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data7.c new file mode 100644 index 000000000..26cbef970 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data7.c @@ -0,0 +1,27 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gChildWarpInToTCS[] = { + CS_BEGIN_CUTSCENE(2, 1118), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 1089), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 71.32476f, 53, 53, 40, 0x010F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 53, 53, 40, 0x0120), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 53, 53, 40, 0x0131), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 89, 47, 0x01F4), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 89, 47, 0x01F6), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 89, 47, 0x0207), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 70.599915f, 58, 89, 47, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 70.599915f, 58, 89, 47, 0x0000), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 1118), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.52477f, 11, 29, 10, 0x010F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.39992f, 11, 29, 10, 0x0120), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.599915f, 11, 29, 10, 0x0131), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 7, 70.599915f, 29, 58, 25, 0x01F4), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, 29, 58, 25, 0x01F6), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 70.599915f, 29, 58, 25, 0x0207), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, 29, 58, 25, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 70.599915f, 29, 58, 25, 0x0000), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data8.c b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data8.c new file mode 100644 index 000000000..a8f122825 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo_cutscene_data8.c @@ -0,0 +1,25 @@ +#include "z_demo_kankyo.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData gChildWarpOutToTCS[] = { + CS_BEGIN_CUTSCENE(5, 1120), + CS_SCENE_TRANS_FX(0x0005, 36, 46), + CS_SCENE_TRANS_FX(0x0001, 30, 35), + CS_CAM_EYE_REL_TO_PLAYER_LIST(0, 1091), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324867f, 41, 75, 49, 0x1F1C), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324867f, 41, 75, 49, 0x1F8C), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324867f, 41, 75, 49, 0x1FFC), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 60.324867f, 41, 75, 49, 0x206C), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 60.324867f, 41, 75, 49, 0x20DC), + CS_CAM_AT_REL_TO_PLAYER_LIST(0, 1120), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324867f, 24, 52, 29, 0x1F1C), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324867f, 24, 52, 29, 0x1F8C), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 60.324867f, 24, 52, 29, 0x1FFC), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 60.324867f, 24, 52, 29, 0x206C), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 60.324867f, 24, 52, 29, 0x20DC), + CS_MISC_LIST(1), + CS_MISC(0x000C, 95, 96, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFDF, 0x00000019, 0x00000000, 0xFFFFFFDF, 0x00000019, 0x00000000, 0x00000000, 0x00000000), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c new file mode 100644 index 000000000..a94fa36af --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.c @@ -0,0 +1,344 @@ +/** + * File: z_demo_kekkai.c + * Overlay: ovl_Demo_Kekkai + * Description: Ganon's castle barriers + */ + +#include "z_demo_kekkai.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" +#include "scenes/dungeons/ganontika/ganontika_scene.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoKekkai_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoKekkai_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoKekkai_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoKekkai_DrawTowerBarrier(Actor* thisx, GlobalContext* globalCtx); +void DemoKekkai_Reset(void); + +void DemoKekkai_TrialBarrierDispel(Actor* thisx, GlobalContext* globalCtx); +void DemoKekkai_TrialBarrierIdle(Actor* thisx, GlobalContext* globalCtx); +void DemoKekkai_DrawTrialBarrier(Actor* thisx, GlobalContext* globalCtx); + +void DemoKekkai_TowerBarrier(DemoKekkai* this, GlobalContext* globalCtx); + +const ActorInit Demo_Kekkai_InitVars = { + ACTOR_DEMO_KEKKAI, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_DEMO_KEKKAI, + sizeof(DemoKekkai), + (ActorFunc)DemoKekkai_Init, + (ActorFunc)DemoKekkai_Destroy, + (ActorFunc)DemoKekkai_Update, + (ActorFunc)DemoKekkai_DrawTowerBarrier, + (ActorResetFunc)DemoKekkai_Reset, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x07, 0x04 }, + { 0x00002000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 680, 220, 120, { 0, 0, 0 } }, +}; + +static u8 sEnergyColors[] = { + /* Water prim */ 170, 255, 255, /* env */ 0, 50, 255, + /* Light prim */ 255, 255, 170, /* env */ 200, 255, 0, + /* Fire prim */ 255, 255, 170, /* env */ 200, 0, 0, + /* Shadow prim */ 255, 170, 255, /* env */ 100, 0, 200, + /* Spirit prim */ 255, 255, 170, /* env */ 255, 120, 0, + /* Forest prim */ 255, 255, 170, /* env */ 0, 200, 0, +}; + +s32 DemoKekkai_CheckEventFlag(s32 params) { + static s32 eventFlags[] = { 0xC3, 0xBC, 0xBF, 0xBE, 0xBD, 0xAD, 0xBB }; + + if ((params < KEKKAI_TOWER) || (params > KEKKAI_FOREST)) { + return true; + } + return Flags_GetEventChkInf(eventFlags[params]); +} + +void DemoKekkai_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoKekkai* this = (DemoKekkai*)thisx; + + this->sfxFlag = 0; + this->energyAlpha = 1.0f; + Actor_SetScale(thisx, 0.1f); + thisx->colChkInfo.mass = MASS_IMMOVABLE; + Collider_InitCylinder(globalCtx, &this->collider1); + Collider_SetCylinder(globalCtx, &this->collider1, thisx, &sCylinderInit); + Collider_InitCylinder(globalCtx, &this->collider2); + Collider_SetCylinder(globalCtx, &this->collider2, thisx, &sCylinderInit); + Collider_UpdateCylinder(thisx, &this->collider1); + Collider_UpdateCylinder(thisx, &this->collider2); + this->timer = 0; + this->barrierScrollRate = 1.0f; + this->barrierScroll = 0.0f; + switch (thisx->params) { + case KEKKAI_TOWER: + this->updateFunc = DemoKekkai_TowerBarrier; + this->collider2.dim.radius = thisx->scale.x * 6100.0f; + this->collider2.dim.height = thisx->scale.y * 5000.0f; + this->collider2.dim.yShift = 300; + break; + case KEKKAI_WATER: + case KEKKAI_LIGHT: + case KEKKAI_FIRE: + case KEKKAI_SHADOW: + case KEKKAI_SPIRIT: + case KEKKAI_FOREST: + this->energyAlpha = 1.0f; + this->orbScale = 1.0f; + Actor_SetScale(thisx, 0.1f); + thisx->update = DemoKekkai_TrialBarrierIdle; + thisx->draw = DemoKekkai_DrawTrialBarrier; + this->collider1.dim.radius = thisx->scale.x * 120.0f; + this->collider1.dim.height = thisx->scale.y * 2000.0f; + this->collider1.dim.yShift = 0; + this->collider2.dim.radius = thisx->scale.x * 320.0f; + this->collider2.dim.height = thisx->scale.y * 510.0f; + this->collider2.dim.yShift = 95; + break; + } + if (DemoKekkai_CheckEventFlag(thisx->params)) { + if (thisx->params == KEKKAI_TOWER) { + globalCtx->envCtx.unk_BF = 1; + } + Actor_Kill(thisx); + } +} + +void DemoKekkai_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoKekkai* this = (DemoKekkai*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider1); + Collider_DestroyCylinder(globalCtx, &this->collider2); +} + +static Vec3f vel = { 0.0f, 0.0f, 0.0f }; +void DemoKekkai_SpawnParticles(DemoKekkai* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 lightYellow = { 255, 255, 170, 0 }; + static Color_RGBA8 darkRed = { 200, 0, 0, 0 }; + Vec3f pos; + s32 i; + + for (i = 0; i < 85; i++) { + s16 roll = Rand_ZeroFloat(65535.0f); + s16 yaw = Rand_ZeroFloat(65535.0f); + + vel.x = Math_SinS(yaw) * Math_CosS(roll) * Rand_ZeroFloat(8.0f); + vel.z = Math_CosS(yaw) * Math_CosS(roll) * Rand_ZeroFloat(8.0f); + vel.y = Math_SinS(roll) * Rand_ZeroFloat(3.0f); + + pos.x = (vel.x * 7.0f) + this->actor.world.pos.x; + pos.y = (vel.y * 20.0f) + this->actor.world.pos.y + 120.0f; + pos.z = (vel.z * 7.0f) + this->actor.world.pos.z; + + EffectSsKiraKira_SpawnFocused(globalCtx, &pos, &vel, &accel, &lightYellow, &darkRed, 3000, + (s32)Rand_ZeroFloat(40.0f) + 45); + } +} + +void DemoKekkai_TowerBarrier(DemoKekkai* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[0] != NULL) && + (globalCtx->csCtx.npcActions[0]->action != 1) && (globalCtx->csCtx.npcActions[0]->action == 2)) { + if (!(this->sfxFlag & 1)) { + func_800F3F3C(0xC); + this->sfxFlag |= 1; + } + if (this->barrierScrollRate < 7.0f) { + this->barrierScrollRate += 0.2f; + } else { + this->timer++; + if (this->timer > 100) { + Flags_SetEventChkInf(0xC3); + Actor_Kill(&this->actor); + return; + } else if (this->timer > 40) { + this->actor.scale.z = this->actor.scale.x += 0.003f; + } + } + } + if (!(this->sfxFlag & 1)) { + func_8002F974(&this->actor, NA_SE_EV_TOWER_BARRIER - SFX_FLAG); + } +} + +void DemoKekkai_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + DemoKekkai* this = (DemoKekkai*)thisx; + + if (this->energyAlpha > 0.99f) { + if ((this->collider1.base.atFlags & AT_HIT) || (this->collider2.base.atFlags & AT_HIT)) { + func_8002F71C(globalCtx, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); + } + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + } + this->updateFunc(this, globalCtx); + this->barrierScroll += this->barrierScrollRate; + if (this->barrierScroll > 65536.0f) { + this->barrierScroll -= 65536.0f; + } +} + +void DemoKekkai_TrialBarrierDispel(Actor* thisx, GlobalContext* globalCtx) { + static u16 csFrames[] = { 0, 280, 280, 280, 280, 280, 280 }; + s32 pad; + DemoKekkai* this = (DemoKekkai*)thisx; + + if (globalCtx->csCtx.frames == csFrames[this->actor.params]) { + func_800F3F3C(0xA); + } + if (this->energyAlpha >= 0.05f) { + this->energyAlpha -= 0.05f; + } else { + this->energyAlpha = 0.0f; + } + if (this->timer < 40) { + this->orbScale = ((80 - this->timer) * (f32)this->timer * 0.000625f) + 1.0f; + } else if (this->timer < 50) { + this->orbScale = 2.0f; + } else if (this->timer == 50) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_DM_RING_EXPLOSION); + DemoKekkai_SpawnParticles(this, globalCtx); + } else { + this->orbScale = 0.0f; + } + if (this->orbScale != 0.0f) { + func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG); + } + this->timer++; +} + +static CutsceneData* sSageCutscenes[] = { + NULL, + gWaterTrialSageCs, + gLightTrialSageCs, + gFireTrialSageCs, + gShadowTrialSageCs, + gSpiritTrialSageCs, + gForestTrialSageCs, +}; + +void DemoKekkai_TrialBarrierIdle(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoKekkai* this = (DemoKekkai*)thisx; + + if (this->collider1.base.atFlags & AT_HIT) { + func_8002F71C(globalCtx, &this->actor, 5.0f, this->actor.yawTowardsPlayer, 5.0f); + } + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + if (this->collider2.base.acFlags & AC_HIT) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + // "I got it" + LOG_STRING("当ったよ", "../z_demo_kekkai.c", 572); + this->actor.update = DemoKekkai_TrialBarrierDispel; + this->timer = 0; + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(sSageCutscenes[this->actor.params]); + gSaveContext.cutsceneTrigger = 1; + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + func_8002F974(&this->actor, NA_SE_EV_TOWER_ENERGY - SFX_FLAG); +} + +void DemoKekkai_DrawTrialBarrier(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + s32 frames = globalCtx->gameplayFrames & 0xFFFF; + u8 alphaIndex[102] = { + 1, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 1, 2, 2, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 0, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 1, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 0, 0, + }; + s32 colorIndex; + DemoKekkai* this = (DemoKekkai*)thisx; + u8 alphas[3]; + Vtx* energyVtx = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gTrialBarrierEnergyVtx)); + s32 i; + + if (this->orbScale != 0.0f) { + if (1) {} + alphas[2] = (s32)(this->energyAlpha * 202.0f); + alphas[1] = (s32)(this->energyAlpha * 126.0f); + alphas[0] = 0; + for (i = 0; i < 102; i++) { + energyVtx[i].v.cn[3] = alphas[alphaIndex[i]]; + } + colorIndex = (this->actor.params - 1) * 6; + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 632); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Push(); + Matrix_Translate(0.0f, 1200.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(this->orbScale, this->orbScale, this->orbScale, MTXMODE_APPLY); + Matrix_Translate(0.0f, -1200.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 639), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frames * 5, frames * -10, 0x20, 0x20, 1, frames * 5, + frames * -10, 0x20, 0x20)); + gSPDisplayList(POLY_XLU_DISP++, gTrialBarrierOrbDL); + Matrix_Pop(); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 656), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 50, 0, 100, 255); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, frames, frames, 0x20, 0x20)); + gSPDisplayList(POLY_XLU_DISP++, gTrialBarrierFloorDL); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, sEnergyColors[colorIndex + 0], sEnergyColors[colorIndex + 1], + sEnergyColors[colorIndex + 2], 255); + gDPSetEnvColor(POLY_XLU_DISP++, sEnergyColors[colorIndex + 3], sEnergyColors[colorIndex + 4], + sEnergyColors[colorIndex + 5], 128); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frames * 5, frames * -10, 0x20, 0x20, 1, frames * 5, + frames * -10, 0x20, 0x40)); + gSPDisplayList(POLY_XLU_DISP++, gTrialBarrierEnergyDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 696); + } +} + +void DemoKekkai_DrawTowerBarrier(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DemoKekkai* this = (DemoKekkai*)thisx; + s32 scroll; + + scroll = (s32)this->barrierScroll & 0xFFFF; + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 705); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 707), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 170, 255, 255); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 2, scroll * -4, 0x20, 0x40, 1, scroll * 2, + scroll * -4, 0x20, 0x40)); + gSPDisplayList(POLY_XLU_DISP++, gTowerBarrierDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_kekkai.c", 722); +} + +void DemoKekkai_Reset(void) { + vel.x = 0.0f; + vel.y = 0.0f; + vel.z = 0.0f; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h new file mode 100644 index 000000000..b8e85058d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Kekkai/z_demo_kekkai.h @@ -0,0 +1,34 @@ +#ifndef Z_DEMO_KEKKAI_H +#define Z_DEMO_KEKKAI_H + +#include "ultra64.h" +#include "global.h" + +struct DemoKekkai; + +typedef void (*DemoKekkaiUpdateFunc)(struct DemoKekkai* this, GlobalContext* globalCtx); + +typedef struct DemoKekkai { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider1; + /* 0x0198 */ ColliderCylinder collider2; + /* 0x01E4 */ f32 energyAlpha; + /* 0x01E8 */ f32 orbScale; + /* 0x01EC */ f32 barrierScroll; + /* 0x01F0 */ f32 barrierScrollRate; + /* 0x01F4 */ u16 timer; + /* 0x01F6 */ u16 sfxFlag; + /* 0x01F8 */ DemoKekkaiUpdateFunc updateFunc; +} DemoKekkai; // size = 0x01FC + +typedef enum { + /* 0 */ KEKKAI_TOWER, + /* 1 */ KEKKAI_WATER, + /* 2 */ KEKKAI_LIGHT, + /* 3 */ KEKKAI_FIRE, + /* 4 */ KEKKAI_SHADOW, + /* 5 */ KEKKAI_SPIRIT, + /* 6 */ KEKKAI_FOREST +} DemoKekkaiType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c new file mode 100644 index 000000000..ecd20ac7f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.c @@ -0,0 +1,829 @@ +/* + * File: z_demo_sa.c + * Overlay: Demo_Sa + * Description: Saria (Cutscene) + */ + +#include "z_demo_sa.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "objects/object_sa/object_sa.h" + +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void DemoSa_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoSa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoSa_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoSa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8098EBB8(DemoSa* this, GlobalContext* globalCtx); +void func_8098EBD8(DemoSa* this, GlobalContext* globalCtx); +void func_8098EBF8(DemoSa* this, GlobalContext* globalCtx); +void func_8098EC28(DemoSa* this, GlobalContext* globalCtx); +void func_8098EC60(DemoSa* this, GlobalContext* globalCtx); +void func_8098EC94(DemoSa* this, GlobalContext* globalCtx); +void func_8098ECCC(DemoSa* this, GlobalContext* globalCtx); +void func_8098F0E8(DemoSa* this, GlobalContext* globalCtx); +void func_8098F118(DemoSa* this, GlobalContext* globalCtx); +void func_8098F16C(DemoSa* this, GlobalContext* globalCtx); +void func_8098F3F0(DemoSa* this, GlobalContext* globalCtx); +void func_8098F714(DemoSa* this, GlobalContext* globalCtx); +void func_8098F734(DemoSa* this, GlobalContext* globalCtx); +void func_8098F77C(DemoSa* this, GlobalContext* globalCtx); +void func_8098F7BC(DemoSa* this, GlobalContext* globalCtx); +void func_8098F7FC(DemoSa* this, GlobalContext* globalCtx); +void func_8098FC44(DemoSa* this, GlobalContext* globalCtx); +void func_8098FC64(DemoSa* this, GlobalContext* globalCtx); +void func_8098FC9C(DemoSa* this, GlobalContext* globalCtx); +void func_8098FCD4(DemoSa* this, GlobalContext* globalCtx); +void func_8098FD0C(DemoSa* this, GlobalContext* globalCtx); + +void DemoSa_DrawNothing(DemoSa* this, GlobalContext* globalCtx); +void DemoSa_DrawOpa(DemoSa* this, GlobalContext* globalCtx); +void DemoSa_DrawXlu(DemoSa* this, GlobalContext* globalCtx); + +typedef enum { + /* 0 */ SARIA_EYE_OPEN, + /* 1 */ SARIA_EYE_HALF, + /* 2 */ SARIA_EYE_CLOSED, + /* 3 */ SARIA_EYE_SUPRISED, + /* 4 */ SARIA_EYE_SAD +} SariaEyeState; + +typedef enum { + /* 0 */ SARIA_MOUTH_CLOSED2, + /* 1 */ SARIA_MOUTH_SUPRISED, + /* 2 */ SARIA_MOUTH_CLOSED, + /* 3 */ SARIA_MOUTH_SMILING_OPEN, + /* 4 */ SARIA_MOUTH_FROWNING +} SariaMouthState; + +static void* sEyeTextures[] = { + gSariaEyeOpenTex, gSariaEyeHalfTex, gSariaEyeClosedTex, gSariaEyeSuprisedTex, gSariaEyeSadTex, +}; + +static void* sMouthTextures[] = { + gSariaMouthClosed2Tex, gSariaMouthSuprisedTex, gSariaMouthClosedTex, + gSariaMouthSmilingOpenTex, gSariaMouthFrowningTex, +}; + +static u32 D_80990108 = 0; + +#include "z_demo_sa_cutscene_data.c" EARLY + +static DemoSaActionFunc sActionFuncs[] = { + func_8098EBB8, func_8098EBD8, func_8098EBF8, func_8098EC28, func_8098EC60, func_8098EC94, func_8098ECCC, + func_8098F0E8, func_8098F118, func_8098F16C, func_8098F3F0, func_8098F714, func_8098F734, func_8098F77C, + func_8098F7BC, func_8098F7FC, func_8098FC44, func_8098FC64, func_8098FC9C, func_8098FCD4, func_8098FD0C, +}; + +static DemoSaDrawFunc sDrawFuncs[] = { + DemoSa_DrawNothing, + DemoSa_DrawOpa, + DemoSa_DrawXlu, +}; + +const ActorInit Demo_Sa_InitVars = { + ACTOR_DEMO_SA, + ACTORCAT_NPC, + FLAGS, + OBJECT_SA, + sizeof(DemoSa), + (ActorFunc)DemoSa_Init, + (ActorFunc)DemoSa_Destroy, + (ActorFunc)DemoSa_Update, + (ActorFunc)DemoSa_Draw, + NULL, +}; + +void DemoSa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoSa* this = (DemoSa*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void func_8098E480(DemoSa* this) { + s32 pad[2]; + s16* eyeIndex = &this->eyeIndex; + s16* blinkTimer = &this->blinkTimer; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(0x3C, 0x3C); + } + + *eyeIndex = *blinkTimer; + if (*eyeIndex >= 3) { + *eyeIndex = 0; + } +} + +void DemoSa_SetEyeIndex(DemoSa* this, s16 eyeIndex) { + this->eyeIndex = eyeIndex; +} + +void DemoSa_SetMouthIndex(DemoSa* this, s16 mouthIndex) { + this->mouthIndex = mouthIndex; +} + +void func_8098E530(DemoSa* this) { + this->action = 7; + this->drawConfig = 0; + this->alpha = 0; + this->unk_1A8 = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_1A0 = 0.0f; +} + +void func_8098E554(DemoSa* this, GlobalContext* globalCtx) { + u32* something = &D_80990108; + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + if (*something != 0) { + if (this->actor.params == 2) { + func_8098E530(this); + } + *something = 0; + } + } else if (*something == 0) { + *something = 1; + } +} + +void func_8098E5C8(DemoSa* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +s32 DemoSa_UpdateSkelAnime(DemoSa* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* DemoSa_GetNpcAction(GlobalContext* globalCtx, s32 idx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[idx]; + } + return NULL; +} + +s32 func_8098E654(DemoSa* this, GlobalContext* globalCtx, u16 arg2, s32 arg3) { + CsCmdActorAction* npcAction = DemoSa_GetNpcAction(globalCtx, arg3); + + if ((npcAction != NULL) && (npcAction->action == arg2)) { + return 1; + } + return 0; +} + +s32 func_8098E6A0(DemoSa* this, GlobalContext* globalCtx, u16 arg2, s32 arg3) { + CsCmdActorAction* npcAction = DemoSa_GetNpcAction(globalCtx, arg3); + + if ((npcAction != NULL) && (npcAction->action != arg2)) { + return 1; + } + return 0; +} + +void func_8098E6EC(DemoSa* this, GlobalContext* globalCtx, s32 actionIdx) { + CsCmdActorAction* npcAction = DemoSa_GetNpcAction(globalCtx, actionIdx); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +void func_8098E76C(DemoSa* this, AnimationHeader* animHeaderSeg, u8 arg2, f32 transitionRate, s32 arg4) { + s32 pad[2]; + f32 frameCount = Animation_GetLastFrame(animHeaderSeg); + f32 playbackSpeed; + f32 unk0; + f32 fc; + + if (arg4 == 0) { + unk0 = 0.0f; + fc = frameCount; + playbackSpeed = 1.0f; + } else { + fc = 0.0f; + unk0 = frameCount; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animHeaderSeg, playbackSpeed, unk0, fc, arg2, transitionRate); +} + +void func_8098E7FC(DemoSa* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSariaSkel, &gSariaWaitArmsToSideAnim, NULL, NULL, 0); + this->actor.shape.yOffset = -10000.0f; + DemoSa_SetEyeIndex(this, SARIA_EYE_HALF); + DemoSa_SetMouthIndex(this, SARIA_MOUTH_CLOSED2); +} + +void func_8098E86C(DemoSa* this, GlobalContext* globalCtx) { + Vec3f* world = &this->actor.world.pos; + f32 posX = world->x; + f32 posY = world->y; + f32 posZ = world->z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, + WARP_SAGES); +} + +void func_8098E8C8(DemoSa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 posX = player->actor.world.pos.x; + f32 posY = player->actor.world.pos.y + 80.0f; + f32 posZ = player->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, + 0xB); + Item_Give(globalCtx, ITEM_MEDALLION_FOREST); +} + +void func_8098E944(DemoSa* this, GlobalContext* globalCtx) { + this->actor.shape.yOffset += (250.0f / 3.0f); +} + +void func_8098E960(DemoSa* this, GlobalContext* globalCtx) { + s32 pad[2]; + Player* player; + + if ((gSaveContext.chamberCutsceneNum == 0) && (gSaveContext.sceneSetupIndex < 4)) { + player = GET_PLAYER(globalCtx); + this->action = 1; + globalCtx->csCtx.segment = D_8099010C; + gSaveContext.cutsceneTrigger = 2; + Item_Give(globalCtx, ITEM_MEDALLION_FOREST); + player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + } +} + +void func_8098E9EC(DemoSa* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = globalCtx->csCtx.npcActions[4]; + if ((npcAction != NULL) && (npcAction->action == 2)) { + this->action = 2; + this->drawConfig = 1; + func_8098E86C(this, globalCtx); + } + } +} + +void func_8098EA3C(DemoSa* this) { + if (this->actor.shape.yOffset >= 0.0f) { + this->action = 3; + this->actor.shape.yOffset = 0.0f; + } +} + +void func_8098EA68(DemoSa* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = globalCtx->csCtx.npcActions[4]; + if ((npcAction != NULL) && (npcAction->action == 3)) { + Animation_Change(&this->skelAnime, &gSariaGiveForestMedallionAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gSariaGiveForestMedallionAnim), ANIMMODE_ONCE, -4.0f); + this->action = 4; + } + } +} + +void func_8098EB00(DemoSa* this, s32 arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gSariaGiveForestMedallionStandAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gSariaGiveForestMedallionStandAnim), ANIMMODE_LOOP, 0.0f); + this->action = 5; + } +} + +void func_8098EB6C(DemoSa* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + npcAction = globalCtx->csCtx.npcActions[6]; + if ((npcAction != NULL) && (npcAction->action == 2)) { + this->action = 6; + func_8098E8C8(this, globalCtx); + } + } +} + +void func_8098EBB8(DemoSa* this, GlobalContext* globalCtx) { + func_8098E960(this, globalCtx); +} + +void func_8098EBD8(DemoSa* this, GlobalContext* globalCtx) { + func_8098E9EC(this, globalCtx); +} + +void func_8098EBF8(DemoSa* this, GlobalContext* globalCtx) { + func_8098E944(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098EA3C(this); +} + +void func_8098EC28(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098EA68(this, globalCtx); +} + +void func_8098EC60(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + func_8098EB00(this, DemoSa_UpdateSkelAnime(this)); +} + +void func_8098EC94(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098EB6C(this, globalCtx); +} + +void func_8098ECCC(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); +} + +void func_8098ECF4(DemoSa* this, GlobalContext* globalCtx) { + s32 pad[2]; + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSariaSealGanonAnim); + + SkelAnime_InitFlex(globalCtx, skelAnime, &gSariaSkel, NULL, NULL, NULL, 0); + Animation_Change(skelAnime, &gSariaSealGanonAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, 0.0f); + this->action = 7; + this->actor.shape.shadowAlpha = 0; + DemoSa_SetEyeIndex(this, SARIA_EYE_CLOSED); + DemoSa_SetMouthIndex(this, SARIA_MOUTH_CLOSED); +} + +void func_8098EDB0(DemoSa* this) { + f32 curFrame = this->skelAnime.curFrame; + + if ((this->skelAnime.mode == 2) && (curFrame >= 32.0f)) { + DemoSa_SetEyeIndex(this, SARIA_EYE_HALF); + DemoSa_SetMouthIndex(this, SARIA_MOUTH_CLOSED2); + } +} + +void func_8098EE08(void) { + func_800788CC(NA_SE_SY_WHITE_OUT_T); +} + +void func_8098EE28(DemoSa* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + (kREG(23) + 25.0f) + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 4); +} + +void func_8098EEA8(DemoSa* this, GlobalContext* globalCtx) { + if (func_8098E654(this, globalCtx, 4, 4)) { + this->action = 8; + this->drawConfig = 2; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_1A0 = 0.0f; + func_8098EE08(); + } +} + +void func_8098EEFC(DemoSa* this, GlobalContext* globalCtx) { + s32 alpha = 255; + f32* unk_1A0 = &this->unk_1A0; + + if (func_8098E654(this, globalCtx, 4, 4)) { + *unk_1A0 += 1.0f; + if ((kREG(5) + 10.0f) <= *unk_1A0) { + this->action = 9; + this->drawConfig = 1; + *unk_1A0 = kREG(5) + 10.0f; + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; + return; + } + } else { + *unk_1A0 -= 1.0f; + if (*unk_1A0 <= 0.0f) { + this->action = 7; + this->drawConfig = 0; + *unk_1A0 = 0.0f; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + return; + } + } + this->actor.shape.shadowAlpha = this->alpha = (*unk_1A0 / (kREG(5) + 10.0f)) * 255.0f; +} + +void func_8098F050(DemoSa* this, GlobalContext* globalCtx) { + if (func_8098E6A0(this, globalCtx, 4, 4)) { + this->action = 8; + this->drawConfig = 2; + this->unk_1A0 = kREG(5) + 10.0f; + this->alpha = 255; + if (this->unk_1A8 == 0) { + func_8098EE28(this, globalCtx); + this->unk_1A8 = 1; + } + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_8098F0E8(DemoSa* this, GlobalContext* globalCtx) { + func_8098EEA8(this, globalCtx); + func_8098E554(this, globalCtx); +} + +void func_8098F118(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098E480(this); + func_8098EEFC(this, globalCtx); + func_8098E554(this, globalCtx); +} + +void func_8098F16C(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098EDB0(this); + func_8098F050(this, globalCtx); + func_8098E554(this, globalCtx); +} + +void DemoSa_DrawXlu(DemoSa* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeIndex = this->eyeIndex; + void* sp78 = sEyeTextures[eyeIndex]; + s16 mouthIndex = this->mouthIndex; + s32 pad2; + void* sp6C = sMouthTextures[mouthIndex]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_sa_inKenjyanomaDemo02.c", 296); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sp78)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sp78)); + gSPSegment(POLY_XLU_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(sp6C)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + NULL, NULL, NULL, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_sa_inKenjyanomaDemo02.c", 325); +} + +void func_8098F390(DemoSa* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSariaSkel, &gSariaWaitArmsToSideAnim, NULL, NULL, 0); + this->action = 10; + this->drawConfig = 1; +} + +void func_8098F3F0(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098E480(this); +} + +void func_8098F420(DemoSa* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSariaSkel, &gSariaSitting3Anim, NULL, NULL, 0); + this->action = 11; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_8098F480(DemoSa* this) { + s32 alpha = 255; + f32* unk_1A0 = &this->unk_1A0; + f32 temp_f0; + + *unk_1A0 += 1.0f; + temp_f0 = kREG(17) + 10.0f; + + if (temp_f0 <= *unk_1A0) { + this->actor.shape.shadowAlpha = this->alpha = alpha; + } else { + this->actor.shape.shadowAlpha = this->alpha = (*unk_1A0 / temp_f0) * 255.0f; + } +} + +void func_8098F50C(DemoSa* this, GlobalContext* globalCtx) { + func_8098E6EC(this, globalCtx, 4); + this->action = 12; + this->drawConfig = 2; +} + +void func_8098F544(DemoSa* this) { + if (this->unk_1A0 >= kREG(17) + 10.0f) { + this->action = 13; + this->drawConfig = 1; + } +} + +void func_8098F590(DemoSa* this) { + func_8098E76C(this, &gSariaSitting1Anim, 2, -8.0f, 0); + this->action = 14; +} + +void func_8098F5D0(DemoSa* this) { + func_8098E76C(this, &gSariaSitting2Anim, 2, 0.0f, 0); + this->action = 15; +} + +void func_8098F610(DemoSa* this, s32 arg1) { + if (arg1 != 0) { + func_8098E76C(this, &gSariaSitting3Anim, 0, 0.0f, 0); + this->action = 13; + } +} + +void func_8098F654(DemoSa* this, GlobalContext* globalCtx) { + s32 unk_1AC; + s32 action; + CsCmdActorAction* npcAction = DemoSa_GetNpcAction(globalCtx, 4); + + if (npcAction != NULL) { + action = npcAction->action; + unk_1AC = this->unk_1AC; + if (action != unk_1AC) { + switch (action) { + case 7: + func_8098F50C(this, globalCtx); + break; + case 8: + func_8098F590(this); + break; + case 9: + func_8098F5D0(this); + break; + default: + osSyncPrintf("Demo_Sa_inEnding_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_1AC = action; + } + } +} + +void func_8098F714(DemoSa* this, GlobalContext* globalCtx) { + func_8098F654(this, globalCtx); +} + +void func_8098F734(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098E480(this); + func_8098F480(this); + func_8098F544(this); +} + +void func_8098F77C(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098E480(this); + func_8098F654(this, globalCtx); +} + +void func_8098F7BC(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098E480(this); + func_8098F654(this, globalCtx); +} + +void func_8098F7FC(DemoSa* this, GlobalContext* globalCtx) { + s32 sp1C; + + func_8098E5C8(this, globalCtx); + sp1C = DemoSa_UpdateSkelAnime(this); + func_8098E480(this); + func_8098F610(this, sp1C); +} + +void func_8098F83C(DemoSa* this, GlobalContext* globalCtx) { + Vec3f* thisPos = &this->actor.world.pos; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSariaSkel, &gSariaWaitOnBridgeAnim, NULL, NULL, 0); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ELF, thisPos->x, thisPos->y, thisPos->z, + 0, 0, 0, FAIRY_KOKIRI); + this->action = 16; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; + DemoSa_SetEyeIndex(this, SARIA_EYE_SAD); + DemoSa_SetMouthIndex(this, SARIA_MOUTH_CLOSED); +} + +void func_8098F8F8(DemoSa* this) { + s32 alpha = 255; + f32* unk_1A0 = &this->unk_1A0; + f32 temp_f0; + + *unk_1A0 += 1.0f; + temp_f0 = kREG(17) + 10.0f; + + if (temp_f0 <= *unk_1A0) { + this->actor.shape.shadowAlpha = this->alpha = alpha; + } else { + this->actor.shape.shadowAlpha = this->alpha = (*unk_1A0 / temp_f0) * 255.0f; + } +} + +void func_8098F984(DemoSa* this) { + this->action = 16; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_8098F998(DemoSa* this, GlobalContext* globalCtx) { + if (this->unk_1AC == 4) { + func_8098E6EC(this, globalCtx, 1); + this->action = 17; + this->drawConfig = 2; + this->unk_1B0 = 0; + this->actor.shape.shadowAlpha = 0; + } else { + func_8098E76C(this, &gSariaWaitOnBridgeAnim, 0, 0.0f, 0); + this->action = 18; + this->drawConfig = 1; + this->unk_1B0 = 0; + this->actor.shape.shadowAlpha = 0xFF; + } + DemoSa_SetEyeIndex(this, SARIA_EYE_SAD); +} + +void func_8098FA2C(DemoSa* this) { + if (this->unk_1A0 >= kREG(17) + 10.0f) { + this->action = 18; + this->drawConfig = 1; + this->unk_1B0 = 0; + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_8098FA84(DemoSa* this) { + func_8098E76C(this, &gSariaHoldOcarinaAnim, 0, 0.0f, 0); + this->action = 19; + this->drawConfig = 1; + this->unk_1B0 = 1; + this->actor.shape.shadowAlpha = 0xFF; + DemoSa_SetEyeIndex(this, SARIA_EYE_CLOSED); +} + +void func_8098FAE0(DemoSa* this) { + func_8098E76C(this, &gSariaGiveLinkOcarinaAnim, 2, -8.0f, 0); + this->action = 20; + this->drawConfig = 1; + this->unk_1B0 = 1; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_8098FB34(DemoSa* this, s32 arg1) { + if (arg1 != 0) { + func_8098E76C(this, &gSariaHoldOutOcarinaAnim, 0, 0, 0); + } +} + +void func_8098FB68(DemoSa* this, GlobalContext* globalCtx) { + s32 unk_1AC; + s32 action; + CsCmdActorAction* npcAction = DemoSa_GetNpcAction(globalCtx, 1); + + if (npcAction != NULL) { + action = npcAction->action; + unk_1AC = this->unk_1AC; + if (action != unk_1AC) { + switch (action) { + case 4: + func_8098F984(this); + break; + case 12: + func_8098F998(this, globalCtx); + break; + case 13: + func_8098FA84(this); + break; + case 14: + func_8098FAE0(this); + break; + default: + osSyncPrintf("Demo_Sa_inPresent_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_1AC = action; + } + } +} + +void func_8098FC44(DemoSa* this, GlobalContext* globalCtx) { + func_8098FB68(this, globalCtx); +} + +void func_8098FC64(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098F8F8(this); + func_8098FA2C(this); +} + +void func_8098FC9C(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098FB68(this, globalCtx); +} + +void func_8098FCD4(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + DemoSa_UpdateSkelAnime(this); + func_8098FB68(this, globalCtx); +} + +void func_8098FD0C(DemoSa* this, GlobalContext* globalCtx) { + func_8098E5C8(this, globalCtx); + func_8098FB34(this, DemoSa_UpdateSkelAnime(this)); + func_8098FB68(this, globalCtx); +} + +void DemoSa_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoSa* this = (DemoSa*)thisx; + + if (this->action < 0 || this->action >= 21 || sActionFuncs[this->action] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void DemoSa_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoSa* this = (DemoSa*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + + switch (this->actor.params) { + case 2: + func_8098ECF4(this, globalCtx); + break; + case 3: + func_8098F390(this, globalCtx); + break; + case 4: + func_8098F420(this, globalCtx); + break; + case 5: + func_8098F83C(this, globalCtx); + break; + default: + func_8098E7FC(this, globalCtx); + } +} + +s32 DemoSa_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + DemoSa* this = (DemoSa*)thisx; + + if ((limbIndex == 15) && (this->unk_1B0 != 0)) { + *dList = gSariaRightHandAndOcarinaDL; + } + return false; +} + +void DemoSa_DrawNothing(DemoSa* this, GlobalContext* globalCtx) { +} + +void DemoSa_DrawOpa(DemoSa* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeIndex = this->eyeIndex; + void* eyeTex = sEyeTextures[eyeIndex]; + s32 pad2; + s16 mouthIndex = this->mouthIndex; + void* mouthTex = sMouthTextures[mouthIndex]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_sa.c", 602); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + DemoSa_OverrideLimbDraw, NULL, &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_sa.c", 626); +} + +void DemoSa_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoSa* this = (DemoSa*)thisx; + + if (this->drawConfig < 0 || this->drawConfig >= 3 || sDrawFuncs[this->drawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.h b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.h new file mode 100644 index 000000000..dd3fe27db --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa.h @@ -0,0 +1,27 @@ +#ifndef Z_DEMO_SA_H +#define Z_DEMO_SA_H + +#include "ultra64.h" +#include "global.h" + +struct DemoSa; + +typedef void (*DemoSaActionFunc)(struct DemoSa*, GlobalContext*); +typedef void (*DemoSaDrawFunc)(struct DemoSa*, GlobalContext*); + +typedef struct DemoSa { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ s16 eyeIndex; + /* 0x0192 */ s16 blinkTimer; + /* 0x0194 */ s16 mouthIndex; + /* 0x0198 */ s32 action; + /* 0x019C */ s32 drawConfig; + /* 0x01A0 */ f32 unk_1A0; + /* 0x01A4 */ s32 alpha; + /* 0x01A8 */ s32 unk_1A8; + /* 0x01AC */ s32 unk_1AC; + /* 0x01B0 */ s32 unk_1B0; +} DemoSa; // size = 0x01B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa_cutscene_data.c b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa_cutscene_data.c new file mode 100644 index 000000000..9527c4681 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Sa/z_demo_sa_cutscene_data.c @@ -0,0 +1,184 @@ +#include "z_demo_sa.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_8099010C[] = { + CS_BEGIN_CUTSCENE(29, 3001), + CS_UNK_DATA_LIST(0x00000020, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(31, 5), + CS_NPC_ACTION(0x0001, 0, 612, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 612, 613, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 613, 684, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 684, 732, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 82, 0, 0.0f, -2.7916667f, 0.0f), + CS_NPC_ACTION(0x0003, 732, 2912, 0x0000, 0x0000, 0x0000, 0, 82, 0, 0, 82, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x000D, 0, 261, 0x0000, 0x0000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0005, 261, 600, 0x0000, 0x9555, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0013, 600, 1243, 0x0000, 0x1555, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(43, 3), + CS_NPC_ACTION(0x0001, 0, 165, 0x0000, 0x0000, 0x0000, -98, 6, -169, -98, 6, -169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 165, 466, 0x0000, 0x0000, 0x0000, -98, 6, -169, -98, 6, -169, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 466, 3001, 0x0000, 0x0000, 0x0000, -98, 6, -169, -98, 6, -169, 0.0f, 0.0f, 0.0f), + CS_SCENE_TRANS_FX(0x0001, 590, 607), + CS_SCENE_TRANS_FX(0x0005, 617, 647), + CS_SCENE_TRANS_FX(0x0001, 875, 905), + CS_NPC_ACTION_LIST(49, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, -98, 0, 98, -98, 0, 98, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(62, 1), + CS_NPC_ACTION(0x0004, 0, 3000, 0x0000, 0x0000, 0x0000, -35, 97, -60, -35, 97, -60, 0.0f, 0.0f, 0.0f), + CS_TERMINATOR(KOKIRI_FOREST_AFTER_FOREST_BLUE_WARP, 974, 1050), + CS_TEXT_LIST(10), + CS_TEXT_NONE(0, 303), + CS_TEXT_DISPLAY_TEXTBOX(0x106A, 303, 323, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(323, 344), + CS_TEXT_DISPLAY_TEXTBOX(0x108F, 344, 394, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(394, 415), + CS_TEXT_DISPLAY_TEXTBOX(0x1090, 415, 465, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(465, 871), + CS_TEXT_DISPLAY_TEXTBOX(0x003E, 871, 875, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(875, 936), + CS_TEXT_DISPLAY_TEXTBOX(0x106B, 936, 946, 0x0000, 0x0000, 0x0000), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0044, 686, 687, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC5, 0x00000057, 0x00000000, 0xFFFFFFC5, 0x00000057), + CS_FADE_BGM_LIST(1), + CS_FADE_BGM(0x0004, 550, 600, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC4, 0x00000066, 0x00000000, 0xFFFFFFC4, 0x00000066), + CS_CAM_EYE_LIST(0, 1241), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 170.1984f, 159, 2758, 43, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 170.1984f, 159, 2758, 43, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 170.1984f, 159, 2409, 43, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 170.1984f, 159, 202, 43, 0x0073), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 158, 222, 42, 0x0061), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 158, 149, 42, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 158, 111, 42, 0x006D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 158, 111, 42, 0x0065), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 158, 111, 42, 0x0061), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, 158, 111, 42, 0x0061), + CS_CAM_EYE_LIST(190, 391), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.99993f, -91, 18, -158, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.99993f, -90, 17, -157, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.99993f, -90, 31, -157, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.99993f, -90, 37, -157, 0x0073), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.99993f, -90, 37, -157, 0x0061), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.99993f, -90, 37, -157, 0x006F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 40.99993f, -90, 37, -157, 0x006D), + CS_CAM_EYE_LIST(263, 1354), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 24.399864f, 7, 97, 127, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 24.399864f, 7, 97, 127, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 24.399864f, 7, 97, 127, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 24.399864f, 7, 97, 127, 0x0073), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 24.399864f, 7, 97, 127, 0x0061), + CS_CAM_EYE_LIST(333, 1424), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -279, 103, 68, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -279, 103, 68, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -279, 103, 68, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -279, 103, 68, 0x0073), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, -279, 103, 68, 0x0061), + CS_CAM_EYE_LIST(403, 1494), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -52, 35, -83, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -52, 35, -83, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -52, 35, -83, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -52, 35, -83, 0x0073), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.39995f, -52, 35, -83, 0x0061), + CS_CAM_EYE_LIST(473, 1716), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -65, 61, -111, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -65, 61, -111, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, -51, 74, -86, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, 0, 136, 11, 0x0073), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, 386, 514, 736, 0x0061), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 80.399765f, 579, 156, 1099, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 80.399765f, 579, 156, 1099, 0x006D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 50.599964f, 579, 156, 1099, 0x0065), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 50.599964f, 579, 156, 1099, 0x0061), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 50.599964f, 579, 156, 1099, 0x0061), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 50.599964f, 579, 156, 1099, 0x0072), + CS_CAM_EYE_LIST(609, 951), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 13, 854, 2, 0x0072), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 853, 5, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -3, 853, 5, 0x002F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 853, -6, 0x0073), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -2, 852, -17, 0x0061), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, -17, 0x006F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 16, 852, -6, 0x006D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, 5, 0x0065), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -3, 851, 5, 0x0061), + CS_CAM_EYE_REL_TO_PLAYER_LIST(685, 1866), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x0072), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x006F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 68, -26, 0x002F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0073), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0061), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x006F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x006D), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 68.599945f, 0, 103, -26, 0x0065), + CS_CAM_AT_LIST(0, 1270), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 170.1984f, 154, 2596, 41, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 170.1984f, 154, 2596, 41, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 170.1984f, 154, 2248, 41, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 170.1984f, 154, 42, 41, 0x0073), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.2f, 82, 94, 23, 0x0061), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.999966f, 33, 79, 0, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 30, 62, -14, 0x006D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.399944f, 30, 62, -14, 0x0065), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 30, 62, -14, 0x0061), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, 31, 62, -14, 0x0061), + CS_CAM_AT_LIST(190, 420), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 40.99993f, 13, 42, 20, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.99993f, 12, 47, 18, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.99993f, 11, 50, 20, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.99993f, 11, 53, 20, 0x0073), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.99993f, 11, 53, 20, 0x0061), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.99993f, 11, 53, 20, 0x006F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 40.99993f, 11, 53, 20, 0x006D), + CS_CAM_AT_LIST(263, 1383), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 24.399864f, -42, 17, -150, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 24.399864f, -42, 17, -150, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 24.399864f, -42, 17, -150, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 24.399864f, -42, 17, -150, 0x0073), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 24.399864f, -42, 17, -150, 0x0061), + CS_CAM_AT_LIST(333, 1453), + CS_CAM_AT(CS_CMD_CONTINUE, 0x01, 30, 45.199944f, -26, 13, -85, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -26, 13, -85, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.399944f, -26, 13, -85, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -26, 13, -85, 0x0073), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, -26, 13, -85, 0x0061), + CS_CAM_AT_LIST(403, 1523), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.799892f, -226, 10, -419, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.999893f, -226, 10, -419, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 35.59991f, -226, 10, -419, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.39993f, -226, 10, -418, 0x0073), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.39995f, -226, 10, -418, 0x0061), + CS_CAM_AT_LIST(473, 1745), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.39995f, -218, -88, -396, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.39995f, -218, -88, -396, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.39995f, -204, -75, -370, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 65.399994f, -149, -10, -269, 0x0073), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 25, 70.79991f, 287, 239, 551, 0x0061), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 80.399765f, 570, 493, 1083, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 50.399963f, 578, 492, 1097, 0x006D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.599964f, 578, 492, 1097, 0x0065), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 50.599964f, 578, 491, 1097, 0x0061), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 27, 50.599964f, 578, 491, 1097, 0x0061), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 50.599964f, 578, 491, 1097, 0x0072), + CS_CAM_AT_LIST(609, 1000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.0f, 3, 6, -6, 0x0072), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.0f, 3, 6, -6, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.999966f, 3, 6, -6, 0x002F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 20.59985f, 3, 6, -6, 0x0073), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 51, 10.799838f, 3, 6, -6, 0x0061), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x006F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x006D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.199839f, 3, 6, -6, 0x0065), + CS_CAM_AT(CS_CMD_STOP, 0x00, 50, 10.999838f, 3, 6, -6, 0x0061), + CS_CAM_AT_REL_TO_PLAYER_LIST(685, 1895), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 100, 5, 0x0072), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 101, 6, 0x006F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 1, 99, 41, 0x002F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x0073), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x0061), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 68.599945f, 0, 42, 16, 0x006F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x006D), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 68.599945f, 0, 42, 16, 0x0065), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_Demo_Shd/z_demo_shd.c b/soh/src/overlays/actors/ovl_Demo_Shd/z_demo_shd.c new file mode 100644 index 000000000..ff0770cb0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Shd/z_demo_shd.c @@ -0,0 +1,126 @@ +/* + * File: z_demo_shd.c + * Overlay: Demo_Shd + * Description: Bongo Bongo's Shadow + */ + +#include "z_demo_shd.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void DemoShd_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoShd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoShd_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoShd_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80991298(DemoShd* this, GlobalContext* globalCtx); + +const ActorInit Demo_Shd_InitVars = { + ACTOR_DEMO_SHD, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(DemoShd), + (ActorFunc)DemoShd_Init, + (ActorFunc)DemoShd_Destroy, + (ActorFunc)DemoShd_Update, + (ActorFunc)DemoShd_Draw, + NULL, +}; + +#include "overlays/ovl_Demo_Shd/ovl_Demo_Shd.h" + +void DemoShd_SetupAction(DemoShd* this, DemoShdActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void DemoShd_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoShd* this = (DemoShd*)thisx; + + this->unk_14C = 0; + DemoShd_SetupAction(this, func_80991298); + Actor_SetScale(&this->actor, 0.4f); + this->actor.world.pos.y = 0.0f; + this->actor.world.pos.x = 0.0f; +} + +void DemoShd_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_80991298(DemoShd* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[0] != NULL) || + (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[1] != NULL)) { + if (globalCtx->csCtx.frames == 800) { + func_800F3F3C(9); + } + if (globalCtx->csCtx.frames == 1069) { + func_800F3F3C(8); + } + } + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction0 = globalCtx->csCtx.npcActions[0]; + + if (npcAction0 != NULL) { + if (npcAction0->action == 2) { + if (!(this->unk_14C & 1)) { + this->unk_14E = npcAction0->startPos.x; + } + this->unk_14C |= 1; + } else { + this->unk_14C &= ~1; + } + } + } + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + CsCmdActorAction* npcAction1 = globalCtx->csCtx.npcActions[1]; + + if (npcAction1 != NULL) { + if (npcAction1->action == 2) { + if (!(this->unk_14C & 2)) { + this->unk_14E = npcAction1->startPos.x; + } + this->unk_14C |= 2; + } else { + this->unk_14C &= ~2; + } + } + } + + this->unk_14E++; +} + +void DemoShd_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoShd* this = (DemoShd*)thisx; + + this->actionFunc(this, globalCtx); +} + +void DemoShd_Draw(Actor* thisx, GlobalContext* globalCtx) { + DemoShd* this = (DemoShd*)thisx; + s32 pad; + u32 unk_14E = this->unk_14E; + + if (1) {} // Necessary to match, can be anywhere in the function + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_shd.c", 726); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_demo_shd.c", 729), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_809932D0); + + if (this->unk_14C & 1) { + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0x3FF - ((unk_14E * 5) & 0x3FF), + 16, 256, 1, 0, 255 - ((unk_14E * 5) & 255), 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, D_80993390); + } else if (this->unk_14C & 2) { + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0x3FF - ((unk_14E * 5) & 0x3FF), + 16, 256, 1, 0, 255 - ((unk_14E * 5) & 255), 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, D_809934B8); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_shd.c", 762); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Shd/z_demo_shd.h b/soh/src/overlays/actors/ovl_Demo_Shd/z_demo_shd.h new file mode 100644 index 000000000..563d8b31e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Shd/z_demo_shd.h @@ -0,0 +1,18 @@ +#ifndef Z_DEMO_SHD_H +#define Z_DEMO_SHD_H + +#include "ultra64.h" +#include "global.h" + +struct DemoShd; + +typedef void (*DemoShdActionFunc)(struct DemoShd*, GlobalContext*); + +typedef struct DemoShd { + /* 0x0000 */ Actor actor; + /* 0x014C */ u16 unk_14C; + /* 0x014E */ u16 unk_14E; + /* 0x0150 */ DemoShdActionFunc actionFunc; +} DemoShd; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c b/soh/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c new file mode 100644 index 000000000..205eaffa7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.c @@ -0,0 +1,168 @@ +#include "z_demo_tre_lgt.h" +#include "overlays/actors/ovl_En_Box/z_en_box.h" +#include "objects/object_box/object_box.h" + +#define FLAGS ACTOR_FLAG_4 + +void DemoTreLgt_Init(Actor* thisx, GlobalContext* globalCtx); +void DemoTreLgt_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DemoTreLgt_Update(Actor* thisx, GlobalContext* globalCtx); +void DemoTreLgt_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80993848(DemoTreLgt* this, GlobalContext* globalCtx); +void func_80993754(DemoTreLgt* this); +void func_8099375C(DemoTreLgt* this, GlobalContext* globalCtx); +void func_809937B4(DemoTreLgt* this, GlobalContext* globalCtx, f32 currentFrame); + +typedef struct { + /* 0x00 */ f32 startFrame; + /* 0x04 */ f32 endFrame; + /* 0x08 */ f32 unk_08; + /* 0x0C */ f32 unk_0C; +} DemoTreLgtInfo; // size = 0x10 + +static DemoTreLgtInfo sDemoTreLgtInfo[] = { + { 1.0f, 136.0f, 190.0f, 40.0f }, + { 1.0f, 136.0f, 220.0f, 50.0f }, +}; + +const ActorInit Demo_Tre_Lgt_InitVars = { + ACTOR_DEMO_TRE_LGT, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_BOX, + sizeof(DemoTreLgt), + (ActorFunc)DemoTreLgt_Init, + (ActorFunc)DemoTreLgt_Destroy, + (ActorFunc)DemoTreLgt_Update, + (ActorFunc)DemoTreLgt_Draw, + NULL, +}; + +static TransformUpdateIndex* sTransformUpdIdx[] = { &gTreasureChestCurveAnim_4B60, &gTreasureChestCurveAnim_4F70 }; + +static DemoTreLgtActionFunc sActionFuncs[] = { + func_8099375C, + func_80993848, +}; + +void DemoTreLgt_Init(Actor* thisx, GlobalContext* globalCtx) { + DemoTreLgt* this = (DemoTreLgt*)thisx; + + if (!SkelCurve_Init(globalCtx, &this->skelCurve, &gTreasureChestCurveSkel, sTransformUpdIdx[0])) { + // "Demo_Tre_Lgt_Actor_ct (); Construct failed" + osSyncPrintf("Demo_Tre_Lgt_Actor_ct();コンストラクト失敗\n"); + } + + ASSERT(true, "1", "../z_demo_tre_lgt.c", UNK_LINE); + + this->unk_170 = 255; + this->unk_174 = 255; + this->status = 0; + func_80993754(this); +} + +void DemoTreLgt_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DemoTreLgt* this = (DemoTreLgt*)thisx; + + SkelCurve_Destroy(globalCtx, &this->skelCurve); +} + +void func_80993754(DemoTreLgt* this) { + this->action = DEMO_TRE_LGT_ACTION_WAIT; +} + +void func_8099375C(DemoTreLgt* this, GlobalContext* globalCtx) { + EnBox* treasureChest = (EnBox*)this->actor.parent; + + if (treasureChest != NULL && Animation_OnFrame(&treasureChest->skelanime, 10.0f)) { + func_809937B4(this, globalCtx, treasureChest->skelanime.curFrame); + } +} + +void func_809937B4(DemoTreLgt* this, GlobalContext* globalCtx, f32 currentFrame) { + SkelAnimeCurve* skelCurve = &this->skelCurve; + s32 pad[2]; + + this->action = DEMO_TRE_LGT_ACTION_ANIMATE; + + SkelCurve_SetAnim(skelCurve, sTransformUpdIdx[gSaveContext.linkAge], 1.0f, + sDemoTreLgtInfo[gSaveContext.linkAge].endFrame + sDemoTreLgtInfo[gSaveContext.linkAge].unk_08, + currentFrame, 1.0f); + SkelCurve_Update(globalCtx, skelCurve); +} + +void func_80993848(DemoTreLgt* this, GlobalContext* globalCtx) { + f32 currentFrame = this->skelCurve.animCurFrame; + + if (currentFrame < sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].endFrame) { + this->unk_170 = 255; + } else { + if (currentFrame <= (sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].endFrame + + sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].unk_08)) { + this->unk_170 = ((((sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].endFrame - currentFrame) / + sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].unk_08) * + 255.0f) + + 255.0f); + } else { + this->unk_170 = 0; + } + } + if (currentFrame < sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].unk_0C) { + this->unk_174 = 255; + } else if (currentFrame < (sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].unk_0C + 10.0f)) { + this->unk_174 = + ((((sDemoTreLgtInfo[((void)0, gSaveContext.linkAge)].unk_0C - currentFrame) / 10.0f) * 255.0f) + 255.0f); + } else { + this->unk_174 = 0; + } + if ((currentFrame > 30.0f) && !(this->status & 1)) { + this->status |= 1; + Audio_PlaySoundGeneral(NA_SE_EV_TRE_BOX_FLASH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + if (SkelCurve_Update(globalCtx, &this->skelCurve)) { + Actor_Kill(&this->actor); + } +} + +void DemoTreLgt_Update(Actor* thisx, GlobalContext* globalCtx) { + DemoTreLgt* this = (DemoTreLgt*)thisx; + + sActionFuncs[this->action](this, globalCtx); +} + +s32 DemoTreLgt_PostLimbDraw(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void* thisx) { + s32 pad; + DemoTreLgt* this = (DemoTreLgt*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_demo_tre_lgt.c", 423); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (globalCtx->state.frames * 2) % 256, 0, 64, 32, 1, + (globalCtx->state.frames * -2) % 256, 0, 64, 32)); + + if (limbIndex == 1) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 180, this->unk_170); + } else if ((limbIndex == 13) || (limbIndex == 7) || (limbIndex == 4) || (limbIndex == 10)) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 180, this->unk_174); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_demo_tre_lgt.c", 448); +} + +void DemoTreLgt_Draw(Actor* thisx, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + DemoTreLgt* this = (DemoTreLgt*)thisx; + + OPEN_DISPS(gfxCtx, "../z_demo_tre_lgt.c", 461); + + if (this->action != DEMO_TRE_LGT_ACTION_ANIMATE) { + return; + } + + func_80093D84(gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 0); + SkelCurve_Draw(&this->actor, globalCtx, &this->skelCurve, DemoTreLgt_PostLimbDraw, NULL, 1, thisx); + + CLOSE_DISPS(gfxCtx, "../z_demo_tre_lgt.c", 476); +} diff --git a/soh/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h b/soh/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h new file mode 100644 index 000000000..d34056ab8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Demo_Tre_Lgt/z_demo_tre_lgt.h @@ -0,0 +1,25 @@ +#ifndef Z_DEMO_TRE_LGT_H +#define Z_DEMO_TRE_LGT_H + +#include "ultra64.h" +#include "global.h" + +struct DemoTreLgt; + +typedef void (*DemoTreLgtActionFunc)(struct DemoTreLgt*, GlobalContext*); + +typedef struct DemoTreLgt { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnimeCurve skelCurve; + /* 0x016C */ s32 action; + /* 0x0170 */ u32 unk_170; // some sort of alpha + /* 0x0174 */ u32 unk_174; // another sort of alpha + /* 0x0178 */ u8 status; +} DemoTreLgt; // size = 0x017C + +typedef enum { + /* 0x00 */ DEMO_TRE_LGT_ACTION_WAIT, // wait until animation is needed + /* 0x01 */ DEMO_TRE_LGT_ACTION_ANIMATE +} DemoTreLgtAction; + +#endif diff --git a/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c b/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c new file mode 100644 index 000000000..e0c2f8b35 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.c @@ -0,0 +1,185 @@ +/* + * File: z_door_ana.c + * Overlay: ovl_Door_Ana + * Description: Grottos Entrances/Exits + */ + +#include "z_door_ana.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS ACTOR_FLAG_25 + +void DoorAna_Init(Actor* thisx, GlobalContext* globalCtx); +void DoorAna_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DoorAna_Update(Actor* thisx, GlobalContext* globalCtx); +void DoorAna_Draw(Actor* thisx, GlobalContext* globalCtx); + +void DoorAna_WaitClosed(DoorAna* this, GlobalContext* globalCtx); +void DoorAna_WaitOpen(DoorAna* this, GlobalContext* globalCtx); +void DoorAna_GrabPlayer(DoorAna* this, GlobalContext* globalCtx); + +const ActorInit Door_Ana_InitVars = { + ACTOR_DOOR_ANA, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(DoorAna), + (ActorFunc)DoorAna_Init, + (ActorFunc)DoorAna_Destroy, + (ActorFunc)DoorAna_Update, + (ActorFunc)DoorAna_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0x00000048, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 50, 10, 0, { 0 } }, +}; + +// array of entrance table entries to grotto destinations +static s16 entrances[] = { + 0x036D, 0x003F, 0x0598, 0x059C, 0x05A0, 0x05A4, 0x05A8, 0x05AC, + 0x05B0, 0x05B4, 0x05B8, 0x05BC, 0x05C0, 0x05C4, 0x05FC, +}; + +void DoorAna_SetupAction(DoorAna* this, DoorAnaActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void DoorAna_Init(Actor* thisx, GlobalContext* globalCtx) { + DoorAna* this = (DoorAna*)thisx; + + this->actor.shape.rot.z = 0; + this->actor.shape.rot.y = this->actor.shape.rot.z; + // init block for grottos that are initially "hidden" (require explosives/hammer/song of storms to open) + if ((this->actor.params & 0x300) != 0) { + // only allocate collider for grottos that need bombing/hammering open + if ((this->actor.params & 0x200) != 0) { + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + } else { + this->actor.flags |= ACTOR_FLAG_4; + } + Actor_SetScale(&this->actor, 0); + DoorAna_SetupAction(this, DoorAna_WaitClosed); + } else { + DoorAna_SetupAction(this, DoorAna_WaitOpen); + } + this->actor.targetMode = 0; +} + +void DoorAna_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DoorAna* this = (DoorAna*)thisx; + + // free collider if it has one + if ((this->actor.params & 0x200) != 0) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +// update routine for grottos that are currently "hidden"/unopened +void DoorAna_WaitClosed(DoorAna* this, GlobalContext* globalCtx) { + u32 openGrotto = false; + + if (!(this->actor.params & 0x200)) { + // opening with song of storms + if (this->actor.xyzDistToPlayerSq < 40000.0f && Flags_GetEnv(globalCtx, 5)) { + openGrotto = true; + this->actor.flags &= ~ACTOR_FLAG_4; + } + } else { + // bombing/hammering open a grotto + if (this->collider.base.acFlags & AC_HIT) { + openGrotto = true; + Collider_DestroyCylinder(globalCtx, &this->collider); + } else { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + // open the grotto + if (openGrotto) { + this->actor.params &= ~0x0300; + DoorAna_SetupAction(this, DoorAna_WaitOpen); + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + func_8002F5F0(&this->actor, globalCtx); +} + +// update routine for grottos that are open +void DoorAna_WaitOpen(DoorAna* this, GlobalContext* globalCtx) { + Player* player; + s32 destinationIdx; + + player = GET_PLAYER(globalCtx); + if (Math_StepToF(&this->actor.scale.x, 0.01f, 0.001f)) { + if ((this->actor.targetMode != 0) && (globalCtx->sceneLoadFlag == 0) && (player->stateFlags1 & 0x80000000) && + (player->unk_84F == 0)) { + destinationIdx = ((this->actor.params >> 0xC) & 7) - 1; + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_RETURN, 0x4FF); + gSaveContext.respawn[RESPAWN_MODE_RETURN].pos.y = this->actor.world.pos.y; + gSaveContext.respawn[RESPAWN_MODE_RETURN].yaw = this->actor.home.rot.y; + gSaveContext.respawn[RESPAWN_MODE_RETURN].data = this->actor.params & 0xFFFF; + if (destinationIdx < 0) { + destinationIdx = this->actor.home.rot.z + 1; + } + globalCtx->nextEntranceIndex = entrances[destinationIdx]; + DoorAna_SetupAction(this, DoorAna_GrabPlayer); + } else { + if (!Player_InCsMode(globalCtx) && !(player->stateFlags1 & 0x8800000) && + this->actor.xzDistToPlayer <= 15.0f && -50.0f <= this->actor.yDistToPlayer && + this->actor.yDistToPlayer <= 15.0f) { + player->stateFlags1 |= 0x80000000; + this->actor.targetMode = 1; + } else { + this->actor.targetMode = 0; + } + } + } + Actor_SetScale(&this->actor, this->actor.scale.x); +} + +// update function for after the player has triggered the grotto +void DoorAna_GrabPlayer(DoorAna* this, GlobalContext* globalCtx) { + Player* player; + + if (this->actor.yDistToPlayer <= 0.0f && 15.0f < this->actor.xzDistToPlayer) { + player = GET_PLAYER(globalCtx); + player->actor.world.pos.x = Math_SinS(this->actor.yawTowardsPlayer) * 15.0f + this->actor.world.pos.x; + player->actor.world.pos.z = Math_CosS(this->actor.yawTowardsPlayer) * 15.0f + this->actor.world.pos.z; + } +} + +void DoorAna_Update(Actor* thisx, GlobalContext* globalCtx) { + DoorAna* this = (DoorAna*)thisx; + + this->actionFunc(this, globalCtx); + // changes the grottos facing angle based on camera angle + this->actor.shape.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000; +} + +void DoorAna_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_ana.c", 440); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_ana.c", 446), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gGrottoDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_ana.c", 449); +} diff --git a/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.h b/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.h new file mode 100644 index 000000000..3ad932b62 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Ana/z_door_ana.h @@ -0,0 +1,17 @@ +#ifndef Z_DOOR_ANA_H +#define Z_DOOR_ANA_H + +#include "ultra64.h" +#include "global.h" + +struct DoorAna; + +typedef void (*DoorAnaActionFunc)(struct DoorAna*, GlobalContext*); + +typedef struct DoorAna { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ DoorAnaActionFunc actionFunc; +} DoorAna; // size = 0x019C + +#endif diff --git a/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c new file mode 100644 index 000000000..fa7e84b80 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c @@ -0,0 +1,161 @@ +/* + * File: z_door_gerudo.c + * Overlay: ovl_Door_Gerudo + * Description: Metal grate door + */ + +#include "z_door_gerudo.h" +#include "objects/object_door_gerudo/object_door_gerudo.h" + +#define FLAGS 0 + +void DoorGerudo_Init(Actor* thisx, GlobalContext* globalCtx); +void DoorGerudo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DoorGerudo_Update(Actor* thisx, GlobalContext* globalCtx); +void DoorGerudo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8099485C(DoorGerudo* this, GlobalContext* globalCtx); +s32 func_80994750(DoorGerudo* this, GlobalContext* globalCtx); +void func_8099496C(DoorGerudo* this, GlobalContext* globalCtx); +void func_809949C8(DoorGerudo* this, GlobalContext* globalCtx); + +const ActorInit Door_Gerudo_InitVars = { + ACTOR_DOOR_GERUDO, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_DOOR_GERUDO, + sizeof(DoorGerudo), + (ActorFunc)DoorGerudo_Init, + (ActorFunc)DoorGerudo_Destroy, + (ActorFunc)DoorGerudo_Update, + (ActorFunc)DoorGerudo_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_STOP), +}; + +void DoorGerudo_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DoorGerudo* this = (DoorGerudo*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + CollisionHeader_GetVirtual(&gGerudoCellDoorCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + + if (Flags_GetSwitch(globalCtx, thisx->params & 0x3F)) { + this->actionFunc = func_8099485C; + thisx->world.pos.y = thisx->home.pos.y + 200.0f; + } else { + this->actionFunc = func_8099485C; + this->unk_166 = 10; + } +} + +void DoorGerudo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DoorGerudo* this = (DoorGerudo*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +f32 func_809946BC(GlobalContext* globalCtx, DoorGerudo* this, f32 arg2, f32 arg3, f32 arg4) { + Player* player = GET_PLAYER(globalCtx); + Vec3f playerPos; + Vec3f sp1C; + + playerPos.x = player->actor.world.pos.x; + playerPos.y = player->actor.world.pos.y + arg2; + playerPos.z = player->actor.world.pos.z; + func_8002DBD0(&this->dyna.actor, &sp1C, &playerPos); + + if ((arg3 < fabsf(sp1C.x)) || (arg4 < fabsf(sp1C.y))) { + return FLT_MAX; + } else { + return sp1C.z; + } +} + +s32 func_80994750(DoorGerudo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 temp_f0; + s16 rotYDiff; + + if (!Player_InCsMode(globalCtx)) { + temp_f0 = func_809946BC(globalCtx, this, 0.0f, 20.0f, 15.0f); + if (fabsf(temp_f0) < 40.0f) { + rotYDiff = player->actor.shape.rot.y - this->dyna.actor.shape.rot.y; + if (temp_f0 > 0.0f) { + rotYDiff = 0x8000 - rotYDiff; + } + if (ABS(rotYDiff) < 0x2000) { + return (temp_f0 >= 0.0f) ? 1.0f : -1.0f; + } + } + } + return 0; +} + +void func_8099485C(DoorGerudo* this, GlobalContext* globalCtx) { + if (this->unk_164 != 0) { + this->actionFunc = func_8099496C; + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] -= 1; + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK); + } else { + s32 direction = func_80994750(this, globalCtx); + + if (direction != 0) { + Player* player = GET_PLAYER(globalCtx); + + if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] <= 0) { + player->naviTextId = -0x203; + } else if (!Flags_GetCollectible(globalCtx, (this->dyna.actor.params >> 8) & 0x1F)) { + player->naviTextId = -0x225; + } else { + player->doorType = PLAYER_DOORTYPE_SLIDING; + player->doorDirection = direction; + player->doorActor = &this->dyna.actor; + player->doorTimer = 10; + } + } + } +} + +void func_8099496C(DoorGerudo* this, GlobalContext* globalCtx) { + if (DECR(this->unk_166) == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_SLIDE_DOOR_OPEN); + this->actionFunc = func_809949C8; + } +} + +void func_809949C8(DoorGerudo* this, GlobalContext* globalCtx) { + Math_StepToF(&this->dyna.actor.velocity.y, 15.0f, 3.0f); + Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 200.0f, this->dyna.actor.velocity.y); +} + +void DoorGerudo_Update(Actor* thisx, GlobalContext* globalCtx) { + DoorGerudo* this = (DoorGerudo*)thisx; + + this->actionFunc(this, globalCtx); +} + +void DoorGerudo_Draw(Actor* thisx, GlobalContext* globalCtx) { + DoorGerudo* this = (DoorGerudo*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_gerudo.c", 361); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_gerudo.c", 365), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGerudoCellDoorDL); + + if (this->unk_166 != 0) { + Matrix_Scale(0.01f, 0.01f, 0.025f, MTXMODE_APPLY); + Actor_DrawDoorLock(globalCtx, this->unk_166, DOORLOCK_NORMAL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_gerudo.c", 377); +} diff --git a/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h new file mode 100644 index 000000000..3c6fac394 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.h @@ -0,0 +1,18 @@ +#ifndef Z_DOOR_GERUDO_H +#define Z_DOOR_GERUDO_H + +#include "ultra64.h" +#include "global.h" + +struct DoorGerudo; + +typedef void (*DoorGerudoActionFunc)(struct DoorGerudo*, GlobalContext*); + +typedef struct DoorGerudo { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s16 unk_164; + /* 0x0166 */ u8 unk_166; + /* 0x0168 */ DoorGerudoActionFunc actionFunc; +} DoorGerudo; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c new file mode 100644 index 000000000..e028731fb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c @@ -0,0 +1,525 @@ +/* + * File: z_door_killer.c + * Overlay: ovl_Door_Killer + * Description: Fake doors which attack player + */ + +#include "z_door_killer.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" +#include "objects/object_haka_door/object_haka_door.h" +#include "objects/object_door_killer/object_door_killer.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0 */ DOOR_KILLER_DOOR, + /* 1 */ DOOR_KILLER_RUBBLE_PIECE_1, + /* 2 */ DOOR_KILLER_RUBBLE_PIECE_2, + /* 3 */ DOOR_KILLER_RUBBLE_PIECE_3, + /* 4 */ DOOR_KILLER_RUBBLE_PIECE_4 +} DoorKillerBehaviour; + +void DoorKiller_Init(Actor* thisx, GlobalContext* globalCtx); +void DoorKiller_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DoorKiller_Update(Actor* thisx, GlobalContext* globalCtx); +void DoorKiller_Wait(DoorKiller* this, GlobalContext* globalCtx); +void DoorKiller_SetProperties(DoorKiller* this, GlobalContext* globalCtx); +void DoorKiller_DrawDoor(Actor* thisx, GlobalContext* globalCtx); +void DoorKiller_DrawRubble(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Door_Killer_InitVars = { + ACTOR_DOOR_KILLER, + ACTORCAT_BG, + FLAGS, + OBJECT_DOOR_KILLER, + sizeof(DoorKiller), + (ActorFunc)DoorKiller_Init, + (ActorFunc)DoorKiller_Destroy, + (ActorFunc)DoorKiller_Update, + NULL, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0x0001FFEE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 20, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphItemsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 100 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphItemsInit, +}; + +static DoorKillerTextureEntry sDoorTextures[4] = { + { OBJECT_HIDAN_OBJECTS, gFireTempleDoorKillerTex }, + { OBJECT_MIZU_OBJECTS, object_mizu_objects_Tex_0035C0 }, + { OBJECT_HAKA_DOOR, object_haka_door_Tex_000000 }, + { OBJECT_GAMEPLAY_KEEP, gWoodenDoorTex }, +}; + +void DoorKiller_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + f32 randF; + DoorKiller* this = (DoorKiller*)thisx; + s32 bankIndex; + s32 i; + + // Look in the object bank for one of the four objects containing door textures + bankIndex = -1; + for (i = 0; bankIndex < 0; i++) { + bankIndex = Object_GetIndex(&globalCtx->objectCtx, sDoorTextures[i].objectId); + this->textureEntryIndex = i; + } + osSyncPrintf("bank_ID = %d\n", bankIndex); + osSyncPrintf("status = %d\n", this->textureEntryIndex); + this->doorObjBankIndex = bankIndex; + this->texture = sDoorTextures[this->textureEntryIndex].texture; + + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + Actor_SetScale(&this->actor, 0.01f); + this->timer = 0; + this->hasHitPlayerOrGround = 0; + this->animStyle = 0; + this->playerIsOpening = 0; + + switch ((u8)(this->actor.params & 0xFF)) { + case DOOR_KILLER_DOOR: + // `jointTable` is used for both the `jointTable` and `morphTable` args here. Because this actor doesn't + // play any animations it does not cause problems, but it would need to be changed otherwise. + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_door_killer_Skel_001BC8, NULL, this->jointTable, + this->jointTable, 9); + this->actionFunc = DoorKiller_SetProperties; + DoorKiller_SetProperties(this, globalCtx); + + // manually set the overall rotation of the door + this->jointTable[1].x = this->jointTable[1].z = 0x4000; + + // Set a cylinder collider to detect link attacks and larger sphere collider to detect explosions + Collider_InitCylinder(globalCtx, &this->colliderCylinder); + Collider_SetCylinder(globalCtx, &this->colliderCylinder, &this->actor, &sCylinderInit); + Collider_InitJntSph(globalCtx, &this->colliderJntSph); + Collider_SetJntSph(globalCtx, &this->colliderJntSph, &this->actor, &sJntSphInit, this->colliderJntSphItems); + this->colliderJntSph.elements[0].dim.worldSphere.radius = 80; + this->colliderJntSph.elements[0].dim.worldSphere.center.x = (s16)this->actor.world.pos.x; + this->colliderJntSph.elements[0].dim.worldSphere.center.y = (s16)this->actor.world.pos.y + 50; + this->colliderJntSph.elements[0].dim.worldSphere.center.z = (s16)this->actor.world.pos.z; + + // If tied to a switch flag and that switch flag is already set, kill the actor. + if ((((this->actor.params >> 8) & 0x3F) != 0x3F) && + Flags_GetSwitch(globalCtx, ((this->actor.params >> 8) & 0x3F))) { + Actor_Kill(&this->actor); + } + break; + case DOOR_KILLER_RUBBLE_PIECE_1: + case DOOR_KILLER_RUBBLE_PIECE_2: + case DOOR_KILLER_RUBBLE_PIECE_3: + case DOOR_KILLER_RUBBLE_PIECE_4: + this->actionFunc = DoorKiller_SetProperties; + DoorKiller_SetProperties(this, globalCtx); + + this->actor.gravity = -0.6f; + this->actor.minVelocityY = -6.0f; + + // Random trajectories for rubble pieces + randF = Rand_CenteredFloat(8.0f); + this->actor.velocity.z = Rand_ZeroFloat(8.0f); + this->actor.velocity.x = (Math_CosS(this->actor.world.rot.y) * randF) + + (Math_SinS(this->actor.world.rot.y) * this->actor.velocity.z); + this->actor.velocity.z = (-Math_SinS(this->actor.world.rot.y) * randF) + + (Math_CosS(this->actor.world.rot.y) * this->actor.velocity.z); + this->actor.velocity.y = Rand_ZeroFloat(4.0f) + 4.0f; + + // These are used as the x,y,z rotational velocities in DoorKiller_FallAsRubble + this->actor.world.rot.x = Rand_CenteredFloat(0x1000); + this->actor.world.rot.y = Rand_CenteredFloat(0x1000); + this->actor.world.rot.z = Rand_CenteredFloat(0x1000); + this->timer = 80; + break; + } +} + +void DoorKiller_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DoorKiller* this = (DoorKiller*)thisx; + + if ((thisx->params & 0xFF) == DOOR_KILLER_DOOR) { + Collider_DestroyCylinder(globalCtx, &this->colliderCylinder); + Collider_DestroyJntSph(globalCtx, &this->colliderJntSph); + } +} + +void DoorKiller_SpawnRubble(Actor* thisx, GlobalContext* globalCtx) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_KILLER, thisx->world.pos.x, thisx->world.pos.y + 9.0f, + thisx->world.pos.z, thisx->shape.rot.x, thisx->shape.rot.y, thisx->shape.rot.z, + DOOR_KILLER_RUBBLE_PIECE_1); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_KILLER, thisx->world.pos.x + 7.88f, + thisx->world.pos.y + 39.8f, thisx->world.pos.z, thisx->shape.rot.x, thisx->shape.rot.y, + thisx->shape.rot.z, DOOR_KILLER_RUBBLE_PIECE_2); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_KILLER, thisx->world.pos.x - 15.86f, + thisx->world.pos.y + 61.98f, thisx->world.pos.z, thisx->shape.rot.x, thisx->shape.rot.y, + thisx->shape.rot.z, DOOR_KILLER_RUBBLE_PIECE_3); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_KILLER, thisx->world.pos.x + 3.72f, + thisx->world.pos.y + 85.1f, thisx->world.pos.z, thisx->shape.rot.x, thisx->shape.rot.y, + thisx->shape.rot.z, DOOR_KILLER_RUBBLE_PIECE_4); +} + +/** + * action function for the individual door pieces that spawn and fall down when the door is destroyed + */ +void DoorKiller_FallAsRubble(DoorKiller* this, GlobalContext* globalCtx) { + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + + this->actor.velocity.x *= 0.98f; + this->actor.velocity.z *= 0.98f; + + // world.rot is repurposed to be the rotation velocity for the rubble pieces + this->actor.shape.rot.x += this->actor.world.rot.x; + this->actor.shape.rot.y += this->actor.world.rot.y; + this->actor.shape.rot.z += this->actor.world.rot.z; + + if (this->timer != 0) { + this->timer--; + } else { + Actor_Kill(&this->actor); + } + func_8002D7EC(&this->actor); +} + +s32 DoorKiller_IsHit(Actor* thisx, GlobalContext* globalCtx) { + DoorKiller* this = (DoorKiller*)thisx; + if ((this->colliderCylinder.base.acFlags & 2) && (this->colliderCylinder.info.acHitInfo != NULL)) { + return true; + } + return false; +} + +void DoorKiller_SetAC(DoorKiller* this, GlobalContext* globalCtx) { + Collider_UpdateCylinder(&this->actor, &this->colliderCylinder); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderJntSph.base); +} + +void DoorKiller_Die(DoorKiller* this, GlobalContext* globalCtx) { + s32 switchFlag = (this->actor.params >> 8) & 0x3F; + + // Can set a switch flag on death based on params + if (switchFlag != 0x3F) { + Flags_SetSwitch(globalCtx, switchFlag); + } + Actor_Kill(&this->actor); +} + +/** + * After slamming on the floor, rise back upright + */ +void DoorKiller_RiseBackUp(DoorKiller* this, GlobalContext* globalCtx) { + s32 i; + s16 rotation; + + if (this->timer > 0) { + this->timer--; + } else { + this->actionFunc = DoorKiller_Wait; + this->timer = 16; + DoorKiller_SetAC(this, globalCtx); + return; + } + + this->actor.shape.rot.x = (this->timer >= 8) ? (this->timer * 0x800) - 0x4000 : 0; + + if (this->timer >= 12) { + rotation = (-this->timer * -500) - 8000; + } else if (this->timer >= 8) { + rotation = -2000; + } else if (this->timer >= 5) { + rotation = (this->timer * -500) + 2000; + } else { + rotation = 0; + } + + for (i = 2; i < 9; i++) { + this->jointTable[i].z = -rotation; + } + + if (this->timer < 8) { + rotation = Math_SinS(this->timer * 0x2000) * this->timer * 100.0f; + for (i = 2; i < 9; i++) { + this->jointTable[i].y = rotation; + } + } +} + +/** + * After wobbling, fall over and slam onto the floor, damaging the player if they are in the way. Uses manual distance + * check for damaging player, not AT system. + */ +void DoorKiller_FallOver(DoorKiller* this, GlobalContext* globalCtx) { + s32 i; + s16 rotation; + + if (this->timer > 0) { + this->timer--; + } else { + this->actionFunc = DoorKiller_RiseBackUp; + this->timer = 16; + return; + } + + this->actor.shape.rot.x = (this->timer >= 4) ? 0x8000 + (-this->timer * 0x1000) : 0x4000; + + if (this->timer >= 6) { + rotation = (-this->timer * -500) - 4000; + } else if (this->timer >= 4) { + rotation = -1000; + } else if (this->timer >= 3) { + rotation = (this->timer * -500) + 1000; + } else { + rotation = 0; + } + + for (i = 2; i < 9; i++) { + this->jointTable[i].z = rotation; + } + + if (this->timer == 4) { + // spawn 20 random dust particles just before slamming down + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 1.0f, 0.0f }; + Vec3f pos; + s32 j; + f32 randF; + + for (j = 0; j != 20; j++) { + pos.y = 0.0f; + randF = Rand_CenteredFloat(40.0f); + pos.z = Rand_ZeroFloat(100.0f); + pos.x = (Math_CosS(this->actor.world.rot.y) * randF) + (Math_SinS(this->actor.world.rot.y) * pos.z); + pos.z = (-Math_SinS(this->actor.world.rot.y) * randF) + (Math_CosS(this->actor.world.rot.y) * pos.z); + velocity.x = pos.x * 0.2f; + velocity.z = pos.z * 0.2f; + accel.x = -(velocity.x) * 0.1f; + accel.z = -(velocity.z) * 0.1f; + pos.x += this->actor.world.pos.x; + pos.y += this->actor.world.pos.y; + pos.z += this->actor.world.pos.z; + func_8002865C(globalCtx, &pos, &velocity, &accel, 300, 30); + } + } + if (!(this->hasHitPlayerOrGround & 1)) { + Vec3f playerPosRelToDoor; + Player* player = GET_PLAYER(globalCtx); + func_8002DBD0(&this->actor, &playerPosRelToDoor, &player->actor.world.pos); + if ((fabsf(playerPosRelToDoor.y) < 20.0f) && (fabsf(playerPosRelToDoor.x) < 20.0f) && + (playerPosRelToDoor.z < 100.0f) && (playerPosRelToDoor.z > 0.0f)) { + this->hasHitPlayerOrGround |= 1; + func_8002F6D4(globalCtx, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f, 16); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KDOOR_HIT); + func_8002F7DC(&player->actor, NA_SE_PL_BODY_HIT); + } + } + if (!(this->hasHitPlayerOrGround & 1) && (this->timer == 2)) { + this->hasHitPlayerOrGround |= 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KDOOR_HIT_GND); + } +} + +/** + * Wobble around, signifying the door is about to fall over. Does not set AC and so cannot be destroyed during this. + */ +void DoorKiller_Wobble(DoorKiller* this, GlobalContext* globalCtx) { + s16 rotation; + s32 i; + + if ((this->timer == 16) || (this->timer == 8)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KDOOR_WAVE); + } + + if (this->timer > 0) { + this->timer--; + } else { + this->actionFunc = DoorKiller_FallOver; + this->timer = 8; + this->hasHitPlayerOrGround &= ~1; + return; + } + + rotation = Math_SinS(this->timer * 0x2000) * this->timer * 100.0f; + for (i = 2; i < 9; i++) { + this->jointTable[i].y = rotation; + } + rotation = (u16)(s32)(-Math_CosS(this->timer * 0x1000) * 1000.0f) + 1000; + for (i = 2; i < 9; i++) { + this->jointTable[i].z = rotation; + } +} + +/** + * Idle while the player attempts to open the door and then begin to wobble + */ +void DoorKiller_WaitBeforeWobble(DoorKiller* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + } else { + this->timer = 16; + this->actionFunc = DoorKiller_Wobble; + } +} + +void DoorKiller_Wait(DoorKiller* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f playerPosRelToDoor; + s16 angleToFacingPlayer; + + func_8002DBD0(&this->actor, &playerPosRelToDoor, &player->actor.world.pos); + + // playerIsOpening is set by player + if (this->playerIsOpening) { + this->actionFunc = DoorKiller_WaitBeforeWobble; + this->timer = 10; + this->playerIsOpening = 0; + return; + } + + if (DoorKiller_IsHit(&this->actor, globalCtx)) { + // AC cylinder: wobble if hit by most weapons, die if hit by explosives or hammer + if ((this->colliderCylinder.info.acHitInfo->toucher.dmgFlags & 0x1FFA6) != 0) { + this->timer = 16; + this->actionFunc = DoorKiller_Wobble; + } else if ((this->colliderCylinder.info.acHitInfo->toucher.dmgFlags & 0x48) != 0) { + DoorKiller_SpawnRubble(&this->actor, globalCtx); + this->actionFunc = DoorKiller_Die; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EN_KDOOR_BREAK); + } + } else if (Actor_GetCollidedExplosive(globalCtx, &this->colliderJntSph.base) != NULL) { + // AC sphere: die if hit by explosive + DoorKiller_SpawnRubble(&this->actor, globalCtx); + this->actionFunc = DoorKiller_Die; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EN_KDOOR_BREAK); + } else if (!Player_InCsMode(globalCtx) && (fabsf(playerPosRelToDoor.y) < 20.0f) && + (fabsf(playerPosRelToDoor.x) < 20.0f) && (playerPosRelToDoor.z < 50.0f) && + (playerPosRelToDoor.z > 0.0f)) { + // Set player properties to make the door openable if within range + angleToFacingPlayer = player->actor.shape.rot.y - this->actor.shape.rot.y; + if (playerPosRelToDoor.z > 0.0f) { + angleToFacingPlayer = 0x8000 - angleToFacingPlayer; + } + if (ABS(angleToFacingPlayer) < 0x3000) { + player->doorType = PLAYER_DOORTYPE_FAKE; + player->doorDirection = (playerPosRelToDoor.z >= 0.0f) ? 1.0f : -1.0f; + player->doorActor = &this->actor; + } + } + + DoorKiller_SetAC(this, globalCtx); +} + +/** + * Grabs the virtual address of the texture from the relevant door object + */ +void DoorKiller_UpdateTexture(Actor* thisx, GlobalContext* globalCtx) { + DoorKiller* this = (DoorKiller*)thisx; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->doorObjBankIndex].segment); + this->texture = SEGMENTED_TO_VIRTUAL(this->texture); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[thisx->objBankIndex].segment); +} + +/** + * Gets the correct door texture, defines the appropriate draw fucntion and action function based on type behaviour + * (door or rubble). + */ +void DoorKiller_SetProperties(DoorKiller* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->doorObjBankIndex)) { + DoorKiller_UpdateTexture(&this->actor, globalCtx); + switch (this->actor.params & 0xFF) { + case DOOR_KILLER_DOOR: + this->actionFunc = DoorKiller_Wait; + this->actor.draw = DoorKiller_DrawDoor; + break; + case DOOR_KILLER_RUBBLE_PIECE_1: + case DOOR_KILLER_RUBBLE_PIECE_2: + case DOOR_KILLER_RUBBLE_PIECE_3: + case DOOR_KILLER_RUBBLE_PIECE_4: + this->actionFunc = DoorKiller_FallAsRubble; + this->actor.draw = DoorKiller_DrawRubble; + break; + } + } +} + +void DoorKiller_Update(Actor* thisx, GlobalContext* globalCtx) { + DoorKiller* this = (DoorKiller*)thisx; + + this->actionFunc(this, globalCtx); +} + +void DoorKiller_SetTexture(Actor* thisx, GlobalContext* globalCtx) { + DoorKiller* this = (DoorKiller*)thisx; + void* doorTexture = this->texture; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_killer.c", 883); + gSPSegment(POLY_OPA_DISP++, 0x08, doorTexture); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_killer.c", 885); +} + +void DoorKiller_DrawDoor(Actor* thisx, GlobalContext* globalCtx) { + DoorKiller* this = (DoorKiller*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + DoorKiller_SetTexture(&this->actor, globalCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, NULL); +} + +void DoorKiller_DrawRubble(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { object_door_killer_DL_001250, object_door_killer_DL_001550, object_door_killer_DL_0017B8, + object_door_killer_DL_001A58 }; + s32 rubblePieceIndex = (thisx->params & 0xFF) - 1; + DoorKiller* this = (DoorKiller*)thisx; + + if ((this->timer >= 20) || ((this->timer & 1) == 0)) { + DoorKiller_SetTexture(thisx, globalCtx); + Gfx_DrawDListOpa(globalCtx, dLists[rubblePieceIndex]); + } +} diff --git a/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.h b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.h new file mode 100644 index 000000000..246552f13 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.h @@ -0,0 +1,38 @@ +#ifndef Z_DOOR_KILLER_H +#define Z_DOOR_KILLER_H + +#include "ultra64.h" +#include "global.h" + +/* + * Associated switch flag: (params >> 8) & 0x3F + * ((params >> 8) & 0x3F) == 0x3F means no switch flag is checked / set +*/ + +typedef struct { + /* 0x00 */ s16 objectId; + /* 0x04 */ void* texture; +} DoorKillerTextureEntry; // size 0x8 + +struct DoorKiller; + +typedef void (*DoorKillerActionFunc)(struct DoorKiller*, GlobalContext*); + +typedef struct DoorKiller { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ u8 animStyle; // Must be at same offset as animStyle in EnDoor due to the cast in func_80839800 + /* 0x0191 */ u8 playerIsOpening; // Must be at same offset as playerIsOpening in EnDoor + /* 0x0192 */ Vec3s jointTable[9]; + /* 0x01C8 */ ColliderCylinder colliderCylinder; + /* 0x0214 */ void* texture; + /* 0x0218 */ u16 hasHitPlayerOrGround; + /* 0x021A */ u16 timer; + /* 0x021C */ u8 doorObjBankIndex; + /* 0x021D */ u8 textureEntryIndex; + /* 0x0220 */ ColliderJntSph colliderJntSph; + /* 0x0240 */ ColliderJntSphElement colliderJntSphItems[1]; + /* 0x0280 */ DoorKillerActionFunc actionFunc; +} DoorKiller; // size = 0x0284 + +#endif diff --git a/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c new file mode 100644 index 000000000..9ada2795c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c @@ -0,0 +1,775 @@ +/* + * File: z_door_shutter.c + * Overlay: ovl_Door_Shutter + * Description: Sliding doors, Phantom Ganon room bars, Gohma room rock slab + */ + +#include "z_door_shutter.h" +#include "overlays/actors/ovl_Boss_Goma/z_boss_goma.h" + +#include "objects/object_gnd/object_gnd.h" +#include "objects/object_goma/object_goma.h" +#include "objects/object_ydan_objects/object_ydan_objects.h" +#include "objects/object_ddan_objects/object_ddan_objects.h" +#include "objects/object_bdan_objects/object_bdan_objects.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_bdoor/object_bdoor.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" +#include "objects/object_ganon_objects/object_ganon_objects.h" +#include "objects/object_jya_door/object_jya_door.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" +#include "objects/object_haka_door/object_haka_door.h" +#include "objects/object_ice_objects/object_ice_objects.h" +#include "objects/object_menkuri_objects/object_menkuri_objects.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" +#include "objects/object_ouke_haka/object_ouke_haka.h" + +#define FLAGS ACTOR_FLAG_4 + +void DoorShutter_Init(Actor* thisx, GlobalContext* globalCtx); +void DoorShutter_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DoorShutter_Update(Actor* thisx, GlobalContext* globalCtx); +void DoorShutter_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_8099803C(GlobalContext* globalCtx, s16 y, s16 countdown, s16 arg3); +void DoorShutter_SetupType(DoorShutter* this, GlobalContext* globalCtx); +void func_80996A54(DoorShutter* this, GlobalContext* globalCtx); +void func_80996B00(DoorShutter* this, GlobalContext* globalCtx); +void func_80996B0C(DoorShutter* this, GlobalContext* globalCtx); +void func_80996EE8(DoorShutter* this, GlobalContext* globalCtx); +void func_80996F98(DoorShutter* this, GlobalContext* globalCtx); +void func_80997004(DoorShutter* this, GlobalContext* globalCtx); +void func_80997150(DoorShutter* this, GlobalContext* globalCtx); +void func_809973E8(DoorShutter* this, GlobalContext* globalCtx); +void func_80997528(DoorShutter* this, GlobalContext* globalCtx); +void func_80997568(DoorShutter* this, GlobalContext* globalCtx); +void func_809975C0(DoorShutter* this, GlobalContext* globalCtx); +void func_809976B8(DoorShutter* this, GlobalContext* globalCtx); +void func_80997744(DoorShutter* this, GlobalContext* globalCtx); + +const ActorInit Door_Shutter_InitVars = { + ACTOR_DOOR_SHUTTER, + ACTORCAT_DOOR, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(DoorShutter), + (ActorFunc)DoorShutter_Init, + (ActorFunc)DoorShutter_Destroy, + (ActorFunc)DoorShutter_Update, + (ActorFunc)DoorShutter_Draw, + NULL, +}; + +typedef struct { + s16 objectId; + u8 index1; + u8 index2; +} ShutterObjectInfo; + +static ShutterObjectInfo sObjectInfo[] = { + { OBJECT_GND, 4, 4 }, + { OBJECT_GOMA, 5, 5 }, + { OBJECT_YDAN_OBJECTS, 0, 1 }, + { OBJECT_DDAN_OBJECTS, 2, 2 }, + { OBJECT_BDAN_OBJECTS, 3, 3 }, + { OBJECT_GAMEPLAY_KEEP, 8, 8 }, + { OBJECT_BDOOR, 7, 7 }, + { OBJECT_GAMEPLAY_KEEP, 8, 8 }, + { OBJECT_HIDAN_OBJECTS, 9, 10 }, + { OBJECT_GANON_OBJECTS, 11, 11 }, + { OBJECT_JYA_DOOR, 6, 6 }, + { OBJECT_MIZU_OBJECTS, 12, 13 }, + { OBJECT_HAKA_DOOR, 14, 15 }, + { OBJECT_ICE_OBJECTS, 16, 16 }, + { OBJECT_MENKURI_OBJECTS, 17, 17 }, + { OBJECT_DEMO_KEKKAI, 18, 18 }, + { OBJECT_OUKE_HAKA, 19, 19 }, +}; + +typedef struct { + /* 0x0000 */ Gfx* a; + /* 0x0004 */ Gfx* b; + /* 0x0008 */ u8 c; + /* 0x0009 */ u8 translateZ; + /* 0x000A */ u8 e; + /* 0x000B */ u8 f; +} ShutterInfo; + +static ShutterInfo sShutterInfo[] = { + { gDTDungeonDoor1DL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gDTDungeonDoor2DL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gDodongoDoorDL, gDodongoBarsDL, 240, 14, 70, 15 }, + { gJabuDoorSection1DL, gJabuWebDoorDL, 0, 110, 50, 15 }, + { gPhantomGanonBarsDL, NULL, 130, 12, 50, 15 }, + { gGohmaDoorDL, NULL, 130, 12, 50, 15 }, + { gSpiritDoorDL, gJyaDoorMetalBarsDL, 240, 14, 50, 15 }, + { object_bdoor_DL_0010C0, NULL, 130, 12, 50, 15 }, + { gDungeonDoorDL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gFireTempleDoorFrontDL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gFireTempleDoorBackDL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { object_ganon_objects_DL_0000C0, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gObjectMizuObjectsDoorShutterDL_005D90, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gObjectMizuObjectsDoorShutterDL_007000, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { object_haka_door_DL_002620, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { object_haka_door_DL_003890, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { object_ice_objects_DL_001D10, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gGTGDoorDL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { gGanonsCastleDoorDL, gDoorMetalBarsDL, 130, 12, 20, 15 }, + { object_ouke_haka_DL_0000C0, gDoorMetalBarsDL, 130, 12, 20, 15 }, +}; + +static s8 D_80998224[] = { + -1, -1, -1, -1, 0, 6, 1, -1, 0, -1, -1, -1, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), +}; + +typedef struct { + s16 sceneNum; + u8 index; +} ShutterSceneInfo; + +static ShutterSceneInfo sSceneInfo[] = { + { SCENE_YDAN, 0x02 }, { SCENE_DDAN, 0x03 }, { SCENE_DDAN_BOSS, 0x03 }, + { SCENE_BDAN, 0x04 }, { SCENE_BMORI1, 0x05 }, { SCENE_HIDAN, 0x08 }, + { SCENE_GANON, 0x09 }, { SCENE_GANON_BOSS, 0x09 }, { SCENE_JYASINZOU, 0x0A }, + { SCENE_JYASINBOSS, 0x0A }, { SCENE_MIZUSIN, 0x0B }, { SCENE_HAKADAN, 0x0C }, + { SCENE_HAKADANCH, 0x0C }, { SCENE_ICE_DOUKUTO, 0x0D }, { SCENE_MEN, 0x0E }, + { SCENE_GANONTIKA, 0x0F }, { SCENE_HAKAANA_OUKE, 0x10 }, { -1, 0x07 }, +}; + +typedef struct { + s16 dungeonScene; + s16 bossScene; + u8 index; +} BossDoorInfo; + +static BossDoorInfo D_80998288[] = { + { SCENE_HIDAN, SCENE_FIRE_BS, 0x01 }, + { SCENE_MIZUSIN, SCENE_MIZUSIN_BS, 0x02 }, + { SCENE_HAKADAN, SCENE_HAKADAN_BS, 0x03 }, + { SCENE_GANON, SCENE_GANON_BOSS, 0x04 }, + { SCENE_BMORI1, SCENE_MORIBOSSROOM, 0x05 }, + { SCENE_JYASINZOU, SCENE_JYASINBOSS, 0x06 }, + { -1, -1, 0x00 }, +}; + +static Gfx* sJabuDoorDLists[] = { + gJabuDoorSection1DL, gJabuDoorSection2DL, gJabuDoorSection7DL, gJabuDoorSection4DL, + gJabuDoorSection5DL, gJabuDoorSection4DL, gJabuDoorSection3DL, gJabuDoorSection2DL, +}; + +static void* D_809982D4[] = { + object_bdoor_Tex_0065C0, object_bdoor_Tex_0035C0, object_bdoor_Tex_0055C0, object_bdoor_Tex_0045C0, + object_bdoor_Tex_000000, object_bdoor_Tex_0025C0, object_bdoor_Tex_0015C0, +}; + +void DoorShutter_SetupAction(DoorShutter* this, DoorShutterActionFunc actionFunc) { + this->actionFunc = actionFunc; + this->unk_16F = 0; +} + +s32 DoorShutter_SetupDoor(DoorShutter* this, GlobalContext* globalCtx) { + TransitionActorEntry* transitionEntry = &globalCtx->transiActorCtx.list[(u16)this->dyna.actor.params >> 0xA]; + s8 frontRoom = transitionEntry->sides[0].room; + s32 doorType = this->doorType; + ShutterObjectInfo* temp_t0 = &sObjectInfo[this->unk_16B]; + + if (doorType != SHUTTER_KEY_LOCKED) { + if (frontRoom == transitionEntry->sides[1].room) { + if (ABS((s16)(this->dyna.actor.shape.rot.y - this->dyna.actor.yawTowardsPlayer)) < 0x4000) { + frontRoom = -1; + } + } + if (frontRoom == this->dyna.actor.room) { + if (doorType == SHUTTER_FRONT_SWITCH_BACK_CLEAR) { // Swap the back clear to the front clear + doorType = SHUTTER_FRONT_CLEAR; + } else { + doorType = (doorType == SHUTTER_BOSS) ? SHUTTER_BACK_LOCKED : SHUTTER; + } + } + } + this->unk_16C = (doorType == SHUTTER) ? temp_t0->index1 : temp_t0->index2; + + if (doorType == SHUTTER_FRONT_CLEAR) { + if (!Flags_GetClear(globalCtx, this->dyna.actor.room)) { + DoorShutter_SetupAction(this, func_80996A54); + this->unk_170 = 1.0f; + return true; + } + } else if (doorType == SHUTTER_FRONT_SWITCH || doorType == SHUTTER_FRONT_SWITCH_BACK_CLEAR) { + if (!Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + DoorShutter_SetupAction(this, func_80996EE8); + this->unk_170 = 1.0f; + return true; + } + DoorShutter_SetupAction(this, func_80996F98); + return false; + } else if (doorType == SHUTTER_BACK_LOCKED) { + DoorShutter_SetupAction(this, func_80996B00); + return false; + } + DoorShutter_SetupAction(this, func_80996B0C); + return false; +} + +void DoorShutter_Init(Actor* thisx, GlobalContext* globalCtx2) { + DoorShutter* this = (DoorShutter*)thisx; + GlobalContext* globalCtx = globalCtx2; + s32 phi_a3; + s32 pad; + s32 objectIndex; + s32 i; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->dyna.actor.home.pos.z = this->dyna.actor.shape.yOffset; + DynaPolyActor_Init(&this->dyna, DPM_UNK); + this->doorType = (this->dyna.actor.params >> 6) & 0xF; + phi_a3 = D_80998224[this->doorType]; + if (phi_a3 < 0) { + ShutterSceneInfo* phi_v1; + + for (phi_v1 = &sSceneInfo[0], i = 0; i < ARRAY_COUNT(sSceneInfo) - 1; i++, phi_v1++) { + if (globalCtx->sceneNum == phi_v1->sceneNum) { + break; + } + } + phi_a3 = phi_v1->index; + } else if (phi_a3 == 6) { + BossDoorInfo* phi_v1_2; + + for (phi_v1_2 = &D_80998288[0], i = 0; i < ARRAY_COUNT(D_80998288) - 1; i++, phi_v1_2++) { + if (globalCtx->sceneNum == phi_v1_2->dungeonScene || globalCtx->sceneNum == phi_v1_2->bossScene) { + break; + } + } + this->unk_168 = phi_v1_2->index; + } else { + this->dyna.actor.room = -1; + } + if (this->requiredObjBankIndex = objectIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectInfo[phi_a3].objectId), + (s8)objectIndex < 0) { + Actor_Kill(&this->dyna.actor); + return; + } + DoorShutter_SetupAction(this, DoorShutter_SetupType); + this->unk_16B = phi_a3; + if (this->doorType == SHUTTER_KEY_LOCKED || this->doorType == SHUTTER_BOSS) { + if (!Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + this->unk_16E = 10; + } + Actor_SetFocus(&this->dyna.actor, 60.0f); + } else if (phi_a3 == 4) { + Actor_SetScale(&this->dyna.actor, 0.1f); + this->unk_166 = 100; + this->dyna.actor.uncullZoneScale = 200.0f; + Actor_SetFocus(&this->dyna.actor, 0.0f); + } else { + Actor_SetFocus(&this->dyna.actor, 60.0f); + } +} + +void DoorShutter_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DoorShutter* this = (DoorShutter*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if (this->dyna.actor.room >= 0) { + s32 transitionActorId = (u16)this->dyna.actor.params >> 0xA; + + globalCtx->transiActorCtx.list[transitionActorId].id *= -1; + } +} + +void DoorShutter_SetupType(DoorShutter* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->requiredObjBankIndex)) { + this->dyna.actor.objBankIndex = this->requiredObjBankIndex; + if (this->doorType == SHUTTER_PG_BARS || this->doorType == SHUTTER_GOHMA_BLOCK) { + // Init dynapoly for shutters of the type that uses it + CollisionHeader* colHeader = NULL; + + Actor_SetObjectDependency(globalCtx, &this->dyna.actor); + this->unk_16C = sObjectInfo[this->unk_16B].index1; + CollisionHeader_GetVirtual((this->doorType == SHUTTER_GOHMA_BLOCK) ? &gGohmaDoorCol : &gPhantomGanonBarsCol, + &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->doorType == SHUTTER_GOHMA_BLOCK) { + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.gravity = -2.0f; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_SLIDE_DOOR_CLOSE); + DoorShutter_SetupAction(this, func_809975C0); + } else { + DoorShutter_SetupAction(this, func_80997744); + this->unk_164 = 7; + } + } else { + DoorShutter_SetupDoor(this, globalCtx); + } + } +} + +f32 func_80996840(GlobalContext* globalCtx, DoorShutter* this, f32 arg2, f32 arg3, f32 arg4) { + s32 pad; + Vec3f sp28; + Vec3f sp1C; + Player* player = GET_PLAYER(globalCtx); + + sp28.x = player->actor.world.pos.x; + sp28.y = player->actor.world.pos.y + arg2; + sp28.z = player->actor.world.pos.z; + func_8002DBD0(&this->dyna.actor, &sp1C, &sp28); + if (arg3 < fabsf(sp1C.x) || arg4 < fabsf(sp1C.y)) { + return FLT_MAX; + } else { + return sp1C.z; + } +} + +s32 func_809968D4(DoorShutter* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (!Player_InCsMode(globalCtx)) { + ShutterInfo* temp_v1 = &sShutterInfo[this->unk_16C]; + f32 temp_f2 = func_80996840(globalCtx, this, (this->unk_16C != 3) ? 0.0f : 80.0f, temp_v1->e, temp_v1->f); + + if (fabsf(temp_f2) < 50.0f) { + s16 phi_v0 = player->actor.shape.rot.y - this->dyna.actor.shape.rot.y; + + if (temp_f2 > 0.0f) { + phi_v0 = 0x8000 - phi_v0; + } + if (ABS(phi_v0) < 0x3000) { + return (temp_f2 >= 0.0f) ? 1.0f : -1.0f; + } + } + } + return 0.0f; +} + +void func_80996A54(DoorShutter* this, GlobalContext* globalCtx) { + if (Flags_GetClear(globalCtx, this->dyna.actor.room) || Flags_GetTempClear(globalCtx, this->dyna.actor.room)) { + Flags_SetClear(globalCtx, this->dyna.actor.room); + DoorShutter_SetupAction(this, func_80997150); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + OnePointCutscene_Attention(globalCtx, &GET_PLAYER(globalCtx)->actor); + this->unk_16F = -100; + } else if (func_809968D4(this, globalCtx) != 0) { + Player* player = GET_PLAYER(globalCtx); + + player->naviTextId = -0x202; + } +} + +void func_80996B00(DoorShutter* this, GlobalContext* globalCtx) { +} + +void func_80996B0C(DoorShutter* this, GlobalContext* globalCtx) { + if (this->unk_164 != 0) { + DoorShutter_SetupAction(this, func_80997004); + this->dyna.actor.velocity.y = 0.0f; + if (this->unk_16E != 0) { + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + if (this->doorType != SHUTTER_BOSS) { + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]--; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK); + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK_B); + } + } + } else { + s32 doorDirection = func_809968D4(this, globalCtx); + + if (doorDirection != 0) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_16E != 0) { + if (this->doorType == SHUTTER_BOSS) { + if (!CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, gSaveContext.mapIndex)) { + player->naviTextId = -0x204; + return; + } + } else if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] <= 0) { + player->naviTextId = -0x203; + return; + } + player->doorTimer = 10; + } + player->doorType = PLAYER_DOORTYPE_SLIDING; + player->doorDirection = doorDirection; + player->doorActor = &this->dyna.actor; + } + } +} + +void func_80996C60(DoorShutter* this, GlobalContext* globalCtx) { + if (this->dyna.actor.category == ACTORCAT_DOOR) { + Player* player = GET_PLAYER(globalCtx); + s32 sp38 = this->unk_16C; + s32 sp34 = 0xF; + + if (DoorShutter_SetupDoor(this, globalCtx)) { + sp34 = 0x20; + } + DoorShutter_SetupAction(this, func_80997004); + this->unk_16C = sp38; + this->unk_170 = 0.0f; + Camera_ChangeDoorCam(globalCtx->cameraPtrs[MAIN_CAM], &this->dyna.actor, player->unk_46A, 0.0f, 12, sp34, 10); + } +} + +s32 func_80996D14(DoorShutter* this, GlobalContext* globalCtx) { + if (this->unk_16C != 3) { + if (this->dyna.actor.velocity.y == 0.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_SLIDE_DOOR_OPEN); + func_80996C60(this, globalCtx); + } + Math_StepToF(&this->dyna.actor.velocity.y, 15.0f, 3.0f); + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y + 200.0f, + this->dyna.actor.velocity.y)) { + return true; + } + } else { + if (this->unk_166 == 100) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYODOOR_OPEN); + func_80996C60(this, globalCtx); + } + if (Math_StepToS(&this->unk_166, 0, 10)) { + return true; + } + } + return false; +} + +s32 func_80996E08(DoorShutter* this, GlobalContext* globalCtx, f32 arg2) { + if (this->unk_170 == 1.0f - arg2) { + if (this->unk_16C != 3) { + if (arg2 == 1.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_CLOSE); + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_METALDOOR_OPEN); + } + } else { + if (arg2 == 1.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSHUTTER_CLOSE); + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSHUTTER_OPEN); + } + } + } + if (Math_StepToF(&this->unk_170, arg2, 0.2f)) { + return true; + } + return false; +} + +void func_80996EE8(DoorShutter* this, GlobalContext* globalCtx) { + if (func_80996E08(this, globalCtx, 1.0f)) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + DoorShutter_SetupAction(this, func_80997150); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->unk_16F = -100; + } else if (func_809968D4(this, globalCtx)) { + Player* player = GET_PLAYER(globalCtx); + // Jabu navi text for switch doors is different + player->naviTextId = (globalCtx->sceneNum == SCENE_BDAN) ? -0x20B : -0x202; + } + } +} + +void func_80996F98(DoorShutter* this, GlobalContext* globalCtx) { + if (this->unk_164 == 0 && !Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + DoorShutter_SetupAction(this, func_80996EE8); + } else { + func_80996B0C(this, globalCtx); + } +} + +void func_80997004(DoorShutter* this, GlobalContext* globalCtx) { + if (DECR(this->unk_16E) == 0 && globalCtx->roomCtx.status == 0 && func_80996D14(this, globalCtx) != 0) { + if (((this->doorType == SHUTTER_BOSS) ? 20.0f : 50.0f) < this->dyna.actor.xzDistToPlayer) { + if (DoorShutter_SetupDoor(this, globalCtx)) { + this->dyna.actor.velocity.y = 30.0f; + } + if (this->unk_16C != 3) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_SLIDE_DOOR_CLOSE); + DoorShutter_SetupAction(this, func_809973E8); + } else { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYODOOR_CLOSE); + if ((this->doorType == SHUTTER_FRONT_SWITCH || this->doorType == SHUTTER_FRONT_SWITCH_BACK_CLEAR) && + !Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUYOSHUTTER_CLOSE); + } + DoorShutter_SetupAction(this, func_80997528); + } + } + } +} + +void func_80997150(DoorShutter* this, GlobalContext* globalCtx) { + if (this->unk_16F != 0) { + if (this->unk_16F < 0) { + if (globalCtx->state.frames % 2 != 0) { + this->unk_16F++; + } + if (this->dyna.actor.category == func_8005B198() || this->unk_16F == 0) { + this->unk_16F = 5; + } + } else { + this->unk_16F--; + } + } else if (func_80996E08(this, globalCtx, 0.0f)) { + if (!(this->doorType == SHUTTER || this->doorType == SHUTTER_FRONT_CLEAR)) { + DoorShutter_SetupAction(this, func_80996F98); + } else { + DoorShutter_SetupAction(this, func_80996B0C); + } + func_800F5B58(); + } +} + +void func_80997220(DoorShutter* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s8 room = this->dyna.actor.room; + + if (this->dyna.actor.room >= 0) { + Vec3f vec; + + func_8002DBD0(&this->dyna.actor, &vec, &player->actor.world.pos); + this->dyna.actor.room = + globalCtx->transiActorCtx.list[(u16)this->dyna.actor.params >> 0xA].sides[(vec.z < 0.0f) ? 0 : 1].room; + if (room != this->dyna.actor.room) { + Room tempRoom = globalCtx->roomCtx.curRoom; + + globalCtx->roomCtx.curRoom = globalCtx->roomCtx.prevRoom; + globalCtx->roomCtx.prevRoom = tempRoom; + globalCtx->roomCtx.unk_30 ^= 1; + } + func_80097534(globalCtx, &globalCtx->roomCtx); + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_DOWN, 0x0EFF); + } + this->unk_164 = 0; + this->dyna.actor.velocity.y = 0.0f; + if (DoorShutter_SetupDoor(this, globalCtx) && !(player->stateFlags1 & 0x800)) { + DoorShutter_SetupAction(this, func_80997568); + func_8002DF54(globalCtx, NULL, 2); + } +} + +void func_809973E8(DoorShutter* this, GlobalContext* globalCtx) { + s32 quakeId; + + if (this->dyna.actor.velocity.y < 20.0f) { + Math_StepToF(&this->dyna.actor.velocity.y, 20.0f, 8.0f); + } + if (Math_StepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, this->dyna.actor.velocity.y)) { + if (this->dyna.actor.velocity.y > 20.0f) { + this->dyna.actor.floorHeight = this->dyna.actor.home.pos.y; + Actor_SpawnFloorDustRing(globalCtx, &this->dyna.actor, &this->dyna.actor.world.pos, 45.0f, 0xA, 8.0f, 0x1F4, + 0xA, 0); + } + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + quakeId = Quake_Add(Gameplay_GetCamera(globalCtx, 0), 3); + Quake_SetSpeed(quakeId, -32536); + Quake_SetQuakeValues(quakeId, 2, 0, 0, 0); + Quake_SetCountdown(quakeId, 10); + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 0xB4, 0x14, 0x64); + func_80997220(this, globalCtx); + } +} + +void func_80997528(DoorShutter* this, GlobalContext* globalCtx) { + if (Math_StepToS(&this->unk_166, 0x64, 0xA)) { + func_80997220(this, globalCtx); + } +} + +void func_80997568(DoorShutter* this, GlobalContext* globalCtx) { + if (this->unk_16F++ > 30) { + func_8002DF54(globalCtx, NULL, 7); + DoorShutter_SetupDoor(this, globalCtx); + } +} + +void func_809975C0(DoorShutter* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->dyna.actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 0.0f, 0.0f, 0.0f, 4); + if (this->dyna.actor.bgCheckFlags & 1) { + DoorShutter_SetupAction(this, func_809976B8); + if (!(gSaveContext.eventChkInf[7] & 1)) { + BossGoma* parent = (BossGoma*)this->dyna.actor.parent; + + this->unk_164 = 10; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_STONE_BOUND); + func_8099803C(globalCtx, 2, 10, parent->subCameraId); + Actor_SpawnFloorDustRing(globalCtx, &this->dyna.actor, &this->dyna.actor.world.pos, 70.0f, 20, 8.0f, 500, + 10, 1); + } + } +} + +void func_809976B8(DoorShutter* this, GlobalContext* globalCtx) { + f32 mult; + + if (this->unk_164 != 0) { + this->unk_164--; + mult = sinf(this->unk_164 * 250.0f / 100.0f); + this->dyna.actor.shape.yOffset = this->unk_164 * 3.0f / 10.0f * mult; + } +} + +void func_80997744(DoorShutter* this, GlobalContext* globalCtx) { + f32 phi_f0; + + osSyncPrintf("FHG SAKU START !!\n"); + if (this->unk_164 != 0) { + this->unk_164--; + } + phi_f0 = (this->unk_164 % 2 != 0) ? -3.0f : 0.0f; + Math_SmoothStepToF(&this->dyna.actor.world.pos.y, -34.0f + phi_f0, 1.0f, 20.0f, 0.0f); + osSyncPrintf("FHG SAKU END !!\n"); +} + +void DoorShutter_Update(Actor* thisx, GlobalContext* globalCtx) { + DoorShutter* this = (DoorShutter*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if (!(player->stateFlags1 & 0x100004C0) || (this->actionFunc == DoorShutter_SetupType)) { + this->actionFunc(this, globalCtx); + } +} + +Gfx* func_80997838(GlobalContext* globalCtx, DoorShutter* this, Gfx* p) { + MtxF mtx; + f32 angle = 0.0f; + f32 yScale = this->unk_166 * 0.01f; + s32 i; + + Matrix_Get(&mtx); + for (i = 0; i < ARRAY_COUNT(sJabuDoorDLists); i++) { + Matrix_RotateZ(angle, MTXMODE_APPLY); + if (i % 2 == 0) { + Matrix_Translate(0.0f, 800.0f, 0.0f, MTXMODE_APPLY); + } else if (i == 1 || i == 7) { + Matrix_Translate(0.0f, 848.52f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 989.94f, 0.0f, MTXMODE_APPLY); + } + if (this->unk_166 != 100) { + Matrix_Scale(1.0f, yScale, 1.0f, MTXMODE_APPLY); + } + gSPMatrix(p++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_shutter.c", 1991), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(p++, sJabuDoorDLists[i]); + angle -= M_PI / 4; + Matrix_Put(&mtx); + } + return p; +} + +s32 func_80997A34(DoorShutter* this, GlobalContext* globalCtx) { + s32 phi_a1; + s32 phi_a0; + + if (Player_InCsMode(globalCtx)) { + return true; + } + phi_a0 = (s16)(Actor_WorldYawTowardPoint(&this->dyna.actor, &globalCtx->view.eye) - this->dyna.actor.shape.rot.y); + phi_a1 = (s16)(this->dyna.actor.yawTowardsPlayer - this->dyna.actor.shape.rot.y); + phi_a0 = ABS(phi_a0); + phi_a1 = ABS(phi_a1); + if ((phi_a1 < 0x4000 && phi_a0 > 0x4000) || (phi_a1 > 0x4000 && phi_a0 < 0x4000)) { + return false; + } + return true; +} + +void DoorShutter_Draw(Actor* thisx, GlobalContext* globalCtx) { + DoorShutter* this = (DoorShutter*)thisx; + + //! @bug This actor is not fully initialized until the required object dependency is loaded. + //! In most cases, the check for objBankIndex to equal requiredObjBankIndex prevents the actor + //! from drawing until initialization is complete. However if the required object is the same as the + //! object dependency listed in init vars (gameplay_keep in this case), the check will pass even though + //! initialization has not completed. When this happens, it will try to draw the display list of the + //! first entry in `sShutterInfo`, which will likely crash the game. + //! This only matters in very specific scenarios, when the door is unculled on the first possible frame + //! after spawning. It will try to draw without having run update yet. + //! + //! The best way to fix this issue (and what was done in Majora's Mask) is to null out the draw function in + //! the init vars for the actor, and only set draw after initialization is complete. + + if (this->dyna.actor.objBankIndex == this->requiredObjBankIndex && + (this->unk_16B == 0 || func_80997A34(this, globalCtx) != 0)) { + s32 pad[2]; + ShutterInfo* sp70 = &sShutterInfo[this->unk_16C]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_shutter.c", 2048); + + func_80093D18(globalCtx->state.gfxCtx); + + if (this->unk_16C == 3) { + POLY_OPA_DISP = func_80997838(globalCtx, this, POLY_OPA_DISP); + if (this->unk_170 != 0.0f) { + f32 sp58 = (this->unk_166 * 0.01f) * this->unk_170; + + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255.0f * sp58); + Matrix_Translate(0, 0, sp70->translateZ, MTXMODE_APPLY); + Matrix_Scale(sp58, sp58, sp58, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_shutter.c", 2069), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sp70->b); + } + } else { + if (sp70->b != NULL) { + TransitionActorEntry* transitionEntry = + &globalCtx->transiActorCtx.list[(u16)this->dyna.actor.params >> 0xA]; + + if (globalCtx->roomCtx.prevRoom.num >= 0 || + transitionEntry->sides[0].room == transitionEntry->sides[1].room) { + s32 yaw = Math_Vec3f_Yaw(&globalCtx->view.eye, &this->dyna.actor.world.pos); + + if (ABS((s16)(this->dyna.actor.shape.rot.y - yaw)) < 0x4000) { + Matrix_RotateY(M_PI, MTXMODE_APPLY); + } + } else if (this->dyna.actor.room == transitionEntry->sides[0].room) { + Matrix_RotateY(M_PI, MTXMODE_APPLY); + } + } else if (this->doorType == SHUTTER_BOSS) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_809982D4[this->unk_168])); + } + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_shutter.c", 2109), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sp70->a); + if (this->unk_170 != 0.0f && sp70->b != NULL) { + Matrix_Translate(0, sp70->c * (1.0f - this->unk_170), sp70->translateZ, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_shutter.c", 2119), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sp70->b); + } + } + + if (this->unk_16E != 0) { + Matrix_Scale(0.01f, 0.01f, 0.025f, MTXMODE_APPLY); + Actor_DrawDoorLock(globalCtx, this->unk_16E, + (this->doorType == SHUTTER_BOSS) + ? DOORLOCK_BOSS + : ((this->unk_16C == 6) ? DOORLOCK_NORMAL_SPIRIT : DOORLOCK_NORMAL)); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_shutter.c", 2135); + } +} + +void func_8099803C(GlobalContext* globalCtx, s16 y, s16 countdown, s16 camId) { + s16 quakeId = Quake_Add(Gameplay_GetCamera(globalCtx, camId), 3); + + func_800A9F6C(0.0f, 180, 20, 100); + Quake_SetSpeed(quakeId, 20000); + Quake_SetQuakeValues(quakeId, y, 0, 0, 0); + Quake_SetCountdown(quakeId, countdown); +} diff --git a/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h new file mode 100644 index 000000000..d9a17f07b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h @@ -0,0 +1,61 @@ +#ifndef Z_DOOR_SHUTTER_H +#define Z_DOOR_SHUTTER_H + +#include "ultra64.h" +#include "global.h" + +/** + * Actor Parameters + * + * | | | + * | Transition Index | Type | Switch Flag + * |------------------|---------|------------- + * | 0 0 0 0 0 0 | 0 0 0 0 | 0 0 0 0 0 0 + * | 6 | 4 | 6 + * | + * + * Transition Index 1111110000000000 Set by the actor engine when the door is spawned + * Type 0000001111000000 + * Switch Flag 0000000000111111 + * + */ + +typedef enum { + /* 0x00 */ SHUTTER, + /* 0x01 */ SHUTTER_FRONT_CLEAR, + /* 0x02 */ SHUTTER_FRONT_SWITCH, + /* 0x03 */ SHUTTER_BACK_LOCKED, + /* 0x04 */ SHUTTER_PG_BARS, + /* 0x05 */ SHUTTER_BOSS, + /* 0x06 */ SHUTTER_GOHMA_BLOCK, + /* 0x07 */ SHUTTER_FRONT_SWITCH_BACK_CLEAR, + /* 0x08 */ SHUTTER_8, + /* 0x09 */ SHUTTER_9, + /* 0x0A */ SHUTTER_A, + /* 0x0B */ SHUTTER_KEY_LOCKED, + /* 0x0C */ SHUTTER_C, + /* 0x0D */ SHUTTER_D, + /* 0x0E */ SHUTTER_E, + /* 0x0F */ SHUTTER_F +} DoorShutterType; + +struct DoorShutter; + +typedef void (*DoorShutterActionFunc)(struct DoorShutter*, GlobalContext*); + +typedef struct DoorShutter { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s16 unk_164; + /* 0x0166 */ s16 unk_166; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ u8 doorType; + /* 0x016B */ u8 unk_16B; + /* 0x016C */ u8 unk_16C; + /* 0x016D */ s8 requiredObjBankIndex; + /* 0x016E */ s8 unk_16E; + /* 0x016F */ s8 unk_16F; + /* 0x0170 */ f32 unk_170; + /* 0x0174 */ DoorShutterActionFunc actionFunc; +} DoorShutter; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Door_Toki/z_door_toki.c b/soh/src/overlays/actors/ovl_Door_Toki/z_door_toki.c new file mode 100644 index 000000000..6a8ae1568 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Toki/z_door_toki.c @@ -0,0 +1,58 @@ +/* + * File: z_door_toki.c + * Overlay: ovl_Door_Toki + * Description: Door of Time Collision + */ + +#include "z_door_toki.h" +#include "objects/object_toki_objects/object_toki_objects.h" + +#define FLAGS 0 + +void DoorToki_Init(Actor* thisx, GlobalContext* globalCtx); +void DoorToki_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DoorToki_Update(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Door_Toki_InitVars = { + ACTOR_DOOR_TOKI, + ACTORCAT_BG, + FLAGS, + OBJECT_TOKI_OBJECTS, + sizeof(DoorToki), + (ActorFunc)DoorToki_Init, + (ActorFunc)DoorToki_Destroy, + (ActorFunc)DoorToki_Update, + NULL, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_STOP), +}; + +void DoorToki_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + DoorToki* this = (DoorToki*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gDoorTokiCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); +} + +void DoorToki_Destroy(Actor* thisx, GlobalContext* globalCtx) { + DoorToki* this = (DoorToki*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void DoorToki_Update(Actor* thisx, GlobalContext* globalCtx) { + DoorToki* this = (DoorToki*)thisx; + + if (gSaveContext.eventChkInf[4] & 0x800) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} diff --git a/soh/src/overlays/actors/ovl_Door_Toki/z_door_toki.h b/soh/src/overlays/actors/ovl_Door_Toki/z_door_toki.h new file mode 100644 index 000000000..35d4d4f91 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Toki/z_door_toki.h @@ -0,0 +1,14 @@ +#ifndef Z_DOOR_TOKI_H +#define Z_DOOR_TOKI_H + +#include "ultra64.h" +#include "global.h" + +struct DoorToki; + +typedef struct DoorToki { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ char unk_164[0x4]; +} DoorToki; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c new file mode 100644 index 000000000..46f3d1857 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.c @@ -0,0 +1,1058 @@ +#include "z_door_warp1.h" +#include "objects/object_warp1/object_warp1.h" + +#define FLAGS 0 + +void DoorWarp1_Init(Actor* thisx, GlobalContext* globalCtx); +void DoorWarp1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void DoorWarp1_Update(Actor* thisx, GlobalContext* globalCtx); +void DoorWarp1_Draw(Actor* thisx, GlobalContext* globalCtx); +void DoorWarp1_Reset(void); + +void DoorWarp1_WarpAppear(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_Destination(DoorWarp1* this, GlobalContext* globalCtx); +void func_8099B020(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_AwaitClearFlag(DoorWarp1* this, GlobalContext* globalCtx); +void func_8099A3A4(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_BlueCrystal(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_PurpleCrystal(DoorWarp1* this, GlobalContext* globalCtx); +void func_80999214(DoorWarp1* this, GlobalContext* globalCtx); +void func_80999348(DoorWarp1* this, GlobalContext* globalCtx); +void func_809995D4(DoorWarp1* this, GlobalContext* globalCtx); +void func_809998A4(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_ChildWarpIdle(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_RutoWarpIdle(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_ChildWarpOut(DoorWarp1* this, GlobalContext* globalCtx); +void func_80999EE0(DoorWarp1* this, GlobalContext* globalCtx); +void func_80999FE4(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_RutoWarpOut(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_AdultWarpIdle(DoorWarp1* this, GlobalContext* globalCtx); +void func_8099A508(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_AdultWarpOut(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_DoNothing(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_ChooseInitialAction(DoorWarp1* this, GlobalContext* globalCtx); +void DoorWarp1_FloatPlayer(DoorWarp1* this, GlobalContext* globalCtx); + +const ActorInit Door_Warp1_InitVars = { + ACTOR_DOOR_WARP1, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_WARP1, + sizeof(DoorWarp1), + (ActorFunc)DoorWarp1_Init, + (ActorFunc)DoorWarp1_Destroy, + (ActorFunc)DoorWarp1_Update, + (ActorFunc)DoorWarp1_Draw, + (ActorResetFunc)DoorWarp1_Reset, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 4000, ICHAIN_STOP), +}; + +static s16 sWarpTimerTarget; + +void DoorWarp1_SetupAction(DoorWarp1* this, DoorWarp1ActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void DoorWarp1_Init(Actor* thisx, GlobalContext* globalCtx) { + DoorWarp1* this = (DoorWarp1*)thisx; + GlobalContext* globalCtx2 = globalCtx; + + + this->unk_1B8 = 0; + this->unk_1B4 = 0.0f; + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + + if (this->actor.params != WARP_SAGES && this->actor.params != WARP_BLUE_CRYSTAL && + this->actor.params != WARP_YELLOW && this->actor.params != WARP_DESTINATION) { + + Lights_PointNoGlowSetInfo(&this->upperLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0); + this->upperLight = LightContext_InsertLight(globalCtx2, &globalCtx2->lightCtx, &this->upperLightInfo); + + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0); + this->lowerLight = LightContext_InsertLight(globalCtx2, &globalCtx2->lightCtx, &this->lowerLightInfo); + } + osSyncPrintf("\nBOSSWARP arg_data=[%d]", this->actor.params); + + DoorWarp1_ChooseInitialAction(this, globalCtx2); +} + +void DoorWarp1_Destroy(Actor* thisx, GlobalContext* globalCtx) { + u8 i; + DoorWarp1* this = (DoorWarp1*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->upperLight); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lowerLight); + + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjAmbientColor[i] = globalCtx->envCtx.adjFogColor[i] = globalCtx->envCtx.adjLight1Color[i] = + 0; + } + //! @bug SkelAnime_Free is not called for crystal variants +} + +void DoorWarp1_SetupWarp(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->scale = 0; + this->unk_1AE = -140; + this->unk_1B0 = -80; + sWarpTimerTarget = 100; + this->unk_1BC = 1.0f; + this->lightRayAlpha = 0.0f; + this->warpAlpha = 0.0f; + this->crystalAlpha = 0.0f; + + switch (this->actor.params) { + case WARP_YELLOW: + case WARP_ORANGE: + case WARP_GREEN: + case WARP_RED: + this->unk_194 = 0.23f; + this->unk_198 = 0.6f; + break; + case WARP_DESTINATION: + this->unk_194 = 0.0f; + this->unk_198 = 0.0f; + break; + case WARP_UNK_7: + this->scale = 100; + this->unk_1AE = 120; + this->unk_1B0 = 230; + this->unk_194 = 0.3f; + this->unk_198 = 0.3f; + break; + case WARP_BLUE_RUTO: + default: + this->unk_194 = 0.3f; + this->unk_198 = 0.3f; + break; + } + + this->unk_19C = 0.0f; + this->actor.shape.yOffset = 1.0f; + this->warpTimer = 0; + + switch (this->actor.params) { + case WARP_PURPLE_CRYSTAL: + case WARP_BLUE_RUTO: + case WARP_UNK_7: + default: + Lights_PointNoGlowSetInfo(&this->upperLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 200, 255, 255, 255); + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 200, 255, 255, 255); + break; + case WARP_CLEAR_FLAG: + case WARP_SAGES: + case WARP_YELLOW: + case WARP_DESTINATION: + case WARP_ORANGE: + case WARP_GREEN: + case WARP_RED: + break; + } + + switch (this->actor.params) { + case WARP_CLEAR_FLAG: + DoorWarp1_SetupAction(this, DoorWarp1_AwaitClearFlag); + break; + case WARP_DESTINATION: + if ((!(gSaveContext.entranceIndex == 0x608 || // sacred forest meadow + gSaveContext.entranceIndex == 0x564 || // death mountain crater + gSaveContext.entranceIndex == 0x60C || // lake hylia + gSaveContext.entranceIndex == 0x610 || // desert colossus + gSaveContext.entranceIndex == 0x580) && // graveyard + gSaveContext.sceneSetupIndex < 4) || + (GET_PLAYER(globalCtx)->actor.params & 0xF00) != 0x200) { + Actor_Kill(&this->actor); + } + if (Actor_WorldDistXZToActor(&player->actor, &this->actor) > 100.0f) { + Actor_Kill(&this->actor); + } + DoorWarp1_SetupAction(this, DoorWarp1_Destination); + break; + case WARP_UNK_7: + DoorWarp1_SetupAction(this, func_8099B020); + break; + default: + DoorWarp1_SetupAction(this, DoorWarp1_WarpAppear); + break; + } +} + +void DoorWarp1_SetupAdultDungeonWarp(DoorWarp1* this, GlobalContext* globalCtx) { + SkelAnime_Init(globalCtx, &this->skelAnime, &gWarpCrystalSkel, &gWarpCrystalAnim, NULL, NULL, 0); + Animation_ChangeImpl(&this->skelAnime, &gWarpCrystalAnim, 1.0f, 1.0f, 1.0f, ANIMMODE_ONCE, 40.0f, 1); + + this->scale = 0; + this->unk_1AE = -140; + this->unk_1B0 = -80; + sWarpTimerTarget = 160; + this->actor.shape.yOffset = -400.0f; + this->warpTimer = 0; + this->unk_1BC = 1.0f; + this->unk_194 = 0.3f; + this->unk_198 = 0.3f; + this->lightRayAlpha = 0.0f; + this->warpAlpha = 0.0f; + this->crystalAlpha = 0.0f; + this->unk_19C = 0.0f; + + Lights_PointNoGlowSetInfo(&this->upperLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 200, 255, 255, 255); + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 200, 255, 255, 255); + + DoorWarp1_SetupAction(this, func_8099A3A4); +} + +void DoorWarp1_SetupBlueCrystal(DoorWarp1* this, GlobalContext* globalCtx) { + s16 i; + + SkelAnime_Init(globalCtx, &this->skelAnime, &gWarpCrystalSkel, &gWarpCrystalAnim, NULL, NULL, 0); + Animation_ChangeImpl(&this->skelAnime, &gWarpCrystalAnim, 0, Animation_GetLastFrame(&gWarpCrystalAnim), + Animation_GetLastFrame(&gWarpCrystalAnim), ANIMMODE_ONCE, 0.0f, 1); + + this->skelAnime.curFrame = Animation_GetLastFrame(&gWarpCrystalAnim); + this->scale = 10; + this->unk_1AE = 120; + this->unk_1B0 = 230; + this->warpTimer = 0; + this->unk_194 = 0.3f; + this->unk_198 = 0.3f; + this->lightRayAlpha = 0.0f; + this->warpAlpha = 0.0f; + this->crystalAlpha = 0.0f; + this->unk_19C = 0.0f; + this->unk_1BC = 1.0f; + this->actor.shape.yOffset = 800.0f; + + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjAmbientColor[i] = globalCtx->envCtx.adjFogColor[i] = globalCtx->envCtx.adjLight1Color[i] = + -255; + } + + globalCtx->envCtx.adjFogNear = -500; + this->warpTimer = 30; + this->unk_1B8 = 4000; + DoorWarp1_SetupAction(this, DoorWarp1_BlueCrystal); +} + +void DoorWarp1_SetupPurpleCrystal(DoorWarp1* this, GlobalContext* globalCtx) { + SkelAnime_Init(globalCtx, &this->skelAnime, &gWarpCrystalSkel, &gWarpCrystalAnim, NULL, NULL, 0); + Animation_ChangeImpl(&this->skelAnime, &gWarpCrystalAnim, 0, Animation_GetLastFrame(&gWarpCrystalAnim), + Animation_GetLastFrame(&gWarpCrystalAnim), ANIMMODE_ONCE, 0.0f, 1); + + this->skelAnime.curFrame = Animation_GetLastFrame(&gWarpCrystalAnim); + this->unk_1AE = 120; + this->unk_1B0 = 230; + this->warpTimer = 200; + this->unk_1B8 = 4000; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 1.0f; + this->unk_194 = 0.3f; + this->unk_198 = 0.3f; + this->lightRayAlpha = 0.0f; + this->warpAlpha = 0.0f; + this->crystalAlpha = 0.0f; + this->unk_19C = 0.0f; + this->unk_1BC = 1.f; + this->actor.shape.yOffset = 800.0f; + + if (gSaveContext.entranceIndex != 0x53) { + this->actor.scale.x = 0.0499f; + this->actor.scale.y = 0.077f; + this->actor.scale.z = 0.09f; + this->crystalAlpha = 255.0f; + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SHUT_BY_CRYSTAL); + } + DoorWarp1_SetupAction(this, DoorWarp1_PurpleCrystal); +} + +void DoorWarp1_SetPlayerPos(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + player->actor.velocity.y = 0.0f; + player->actor.world.pos.x = this->actor.world.pos.x; + player->actor.world.pos.y = this->actor.world.pos.y + 55.0f; + player->actor.world.pos.z = this->actor.world.pos.z; +} + +void DoorWarp1_BlueCrystal(DoorWarp1* this, GlobalContext* globalCtx) { + if (this->warpTimer != 0) { + this->warpTimer--; + } else { + DoorWarp1_SetupAction(this, func_80999214); + } + DoorWarp1_SetPlayerPos(this, globalCtx); +} + +void func_80999214(DoorWarp1* this, GlobalContext* globalCtx) { + s32 temp_f4; + f32 darkness; + s16 i; + + Math_SmoothStepToF(&this->crystalAlpha, 255.0f, 0.2f, 5.0f, 0.1f); + + darkness = (f32)(40 - this->warpTimer) / 40.0f; + darkness = CLAMP_MIN(darkness, 0); + + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjAmbientColor[i] = globalCtx->envCtx.adjFogColor[i] = globalCtx->envCtx.adjLight1Color[i] = + -255.0f * darkness; + } + globalCtx->envCtx.adjFogNear = -500.0f * darkness; + + this->warpTimer++; + if (darkness <= 0) { + DoorWarp1_SetupAction(this, func_80999348); + } + this->actor.shape.rot.y += 0x320; + DoorWarp1_SetPlayerPos(this, globalCtx); +} + +void func_80999348(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + DoorWarp1_SetPlayerPos(this, globalCtx); + + if (this->warpTimer == 0) { + Math_SmoothStepToF(&this->crystalAlpha, 0.0f, 0.1f, 4.0f, 1.0f); + if (this->crystalAlpha <= 150.0f) { + player->actor.gravity = -0.1f; + } + if (this->crystalAlpha <= 0.0f) { + DoorWarp1_SetupAction(this, DoorWarp1_FloatPlayer); + } + } else { + this->warpTimer--; + } + this->actor.shape.rot.y += 0x320; +} + +void DoorWarp1_FloatPlayer(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + player->actor.gravity = -0.1f; +} + +void DoorWarp1_PurpleCrystal(DoorWarp1* this, GlobalContext* globalCtx) { + if (this->warpTimer != 0) { + this->warpTimer--; + Math_SmoothStepToF(&this->actor.scale.x, 0.0499f, 0.2f, 0.05f, 0.001f); + Math_SmoothStepToF(&this->actor.scale.y, 0.077f, 0.2f, 0.05f, 0.001f); + Math_SmoothStepToF(&this->actor.scale.z, 0.09f, 0.2f, 0.05f, 0.001f); + Math_SmoothStepToF(&this->crystalAlpha, 255.0f, 0.2f, 5.0f, 0.1f); + } +} + +void DoorWarp1_ChooseInitialAction(DoorWarp1* this, GlobalContext* globalCtx) { + switch (this->actor.params) { + case WARP_DUNGEON_CHILD: + case WARP_CLEAR_FLAG: + case WARP_SAGES: + case WARP_YELLOW: + case WARP_BLUE_RUTO: + case WARP_DESTINATION: + case WARP_UNK_7: + case WARP_ORANGE: + case WARP_GREEN: + case WARP_RED: + DoorWarp1_SetupWarp(this, globalCtx); + break; + case WARP_DUNGEON_ADULT: + DoorWarp1_SetupAdultDungeonWarp(this, globalCtx); + break; + case WARP_BLUE_CRYSTAL: + DoorWarp1_SetupBlueCrystal(this, globalCtx); + break; + case WARP_PURPLE_CRYSTAL: + DoorWarp1_SetupPurpleCrystal(this, globalCtx); + break; + } +} + +void DoorWarp1_AwaitClearFlag(DoorWarp1* this, GlobalContext* globalCtx) { + if (Flags_GetTempClear(globalCtx, this->actor.room)) { + this->warpTimer = 200; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS_CLEAR); + DoorWarp1_SetupAction(this, func_809995D4); + } +} + +void func_809995D4(DoorWarp1* this, GlobalContext* globalCtx) { + if (this->warpTimer == 0) { + if (this->actor.xzDistToPlayer < 100.0f) { + this->actor.world.pos.x = -98.0f; + this->actor.world.pos.y = 827.0f; + this->actor.world.pos.z = -3228.0f; + } + Lights_PointNoGlowSetInfo(&this->upperLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 200, 255, 255, 255); + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 200, 255, 255, 255); + DoorWarp1_SetupAction(this, DoorWarp1_WarpAppear); + } + this->warpTimer--; +} + +void DoorWarp1_WarpAppear(DoorWarp1* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); + Math_SmoothStepToF(&this->lightRayAlpha, 255.0f, 0.4f, 10.0f, 0.01f); + Math_SmoothStepToF(&this->warpAlpha, 255.0f, 0.4f, 10.0f, 0.01f); + + if (this->actor.params != WARP_YELLOW && this->actor.params != WARP_ORANGE && this->actor.params != WARP_GREEN && + this->actor.params != WARP_RED) { + if (this->scale < 100) { + this->scale += 2; + } + if (this->unk_1AE < 120) { + this->unk_1AE += 4; + } + if (this->unk_1B0 < 230) { + this->unk_1B0 += 4; + } else if (this->actor.params == WARP_BLUE_RUTO) { + DoorWarp1_SetupAction(this, DoorWarp1_RutoWarpIdle); + } else if (this->actor.params != WARP_SAGES && this->actor.params != WARP_YELLOW) { + DoorWarp1_SetupAction(this, DoorWarp1_ChildWarpIdle); + } else { + DoorWarp1_SetupAction(this, func_809998A4); + } + } else { + if (this->unk_1AE < -50) { + this->unk_1AE += 4; + } + if (this->unk_1B0 < 70) { + this->unk_1B0 += 4; + } else { + DoorWarp1_SetupAction(this, func_809998A4); + } + } +} + +void func_809998A4(DoorWarp1* this, GlobalContext* globalCtx) { + if (this->lightRayAlpha != 0.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); + } + Math_SmoothStepToF(&this->lightRayAlpha, 0.0f, 0.1f, 2.0f, 0.01f); + Math_SmoothStepToF(&this->warpAlpha, 0.0f, 0.1f, 2.0f, 0.01f); +} + +s32 DoorWarp1_PlayerInRange(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 ret = false; + + if (fabsf(this->actor.xzDistToPlayer) < 60.0f) { + if ((player->actor.world.pos.y - 20.0f) < this->actor.world.pos.y) { + if (this->actor.world.pos.y < (player->actor.world.pos.y + 20.0f)) { + ret = true; + } + } + } + return ret; +} + +void DoorWarp1_ChildWarpIdle(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player; + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); + + if (DoorWarp1_PlayerInRange(this, globalCtx)) { + player = GET_PLAYER(globalCtx); + + Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + OnePointCutscene_Init(globalCtx, 0x25E7, 999, &this->actor, MAIN_CAM); + func_8002DF54(globalCtx, &this->actor, 10); + + player->unk_450.x = this->actor.world.pos.x; + player->unk_450.z = this->actor.world.pos.z; + this->unk_1B2 = 1; + DoorWarp1_SetupAction(this, DoorWarp1_ChildWarpOut); + } +} + +void DoorWarp1_ChildWarpOut(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_1B2 >= 101) { + if (player->actor.velocity.y < 10.0f) { + player->actor.gravity = 0.1f; + } else { + player->actor.gravity = 0.0f; + } + } else { + this->unk_1B2++; + } + + Math_SmoothStepToF(&this->lightRayAlpha, 0.0f, 0.2f, 6.0f, 0.01f); + this->warpTimer++; + + if (sWarpTimerTarget < this->warpTimer && gSaveContext.nextCutsceneIndex == 0xFFEF) { + osSyncPrintf("\n\n\nじかんがきたからおーしまい fade_direction=[%d]", globalCtx->sceneLoadFlag, 0x14); + + if (globalCtx->sceneNum == SCENE_DDAN_BOSS) { + if (!Flags_GetEventChkInf(0x25)) { + Flags_SetEventChkInf(0x25); + Item_Give(globalCtx, ITEM_GORON_RUBY); + globalCtx->nextEntranceIndex = 0x13D; + gSaveContext.nextCutsceneIndex = 0xFFF1; + } else { + globalCtx->nextEntranceIndex = 0x47A; + gSaveContext.nextCutsceneIndex = 0; + } + } else if (globalCtx->sceneNum == SCENE_YDAN_BOSS) { + if (!Flags_GetEventChkInf(7)) { + Flags_SetEventChkInf(7); + Flags_SetEventChkInf(9); + Item_Give(globalCtx, ITEM_KOKIRI_EMERALD); + globalCtx->nextEntranceIndex = 0xEE; + gSaveContext.nextCutsceneIndex = 0xFFF1; + } else { + globalCtx->nextEntranceIndex = 0x457; + gSaveContext.nextCutsceneIndex = 0; + } + } else if (globalCtx->sceneNum == SCENE_BDAN_BOSS) { + globalCtx->nextEntranceIndex = 0x10E; + gSaveContext.nextCutsceneIndex = 0; + } + osSyncPrintf("\n\n\nおわりおわり"); + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 7; + gSaveContext.nextTransition = 3; + } + + Math_StepToF(&this->unk_194, 2.0f, 0.01f); + Math_StepToF(&this->unk_198, 10.0f, 0.02f); + Lights_PointNoGlowSetInfo(&this->upperLightInfo, (s16)player->actor.world.pos.x + 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z + 10.0f, 235, 255, + 255, 255); + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, (s16)player->actor.world.pos.x - 10.0f, + (s16)player->actor.world.pos.y - 10.0f, (s16)player->actor.world.pos.z - 10.0f, 235, 255, + 255, 255); + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 0.5f, 2.0f, 0.1f); +} + +void DoorWarp1_RutoWarpIdle(DoorWarp1* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); + + if (this->rutoWarpState != WARP_BLUE_RUTO_STATE_INITIAL && DoorWarp1_PlayerInRange(this, globalCtx)) { + this->rutoWarpState = WARP_BLUE_RUTO_STATE_ENTERED; + func_8002DF54(globalCtx, &this->actor, 10); + this->unk_1B2 = 1; + DoorWarp1_SetupAction(this, func_80999EE0); + } +} + +static s16 sRutoWarpSubCamId; + +void func_80999EE0(DoorWarp1* this, GlobalContext* globalCtx) { + Vec3f at; + Vec3f eye; + Player* player = GET_PLAYER(globalCtx); + + if (this->rutoWarpState == WARP_BLUE_RUTO_STATE_3) { + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + sRutoWarpSubCamId = Gameplay_CreateSubCamera(globalCtx); + + Gameplay_ChangeCameraStatus(globalCtx, sRutoWarpSubCamId, CAM_STAT_ACTIVE); + at.x = this->actor.world.pos.x; + at.y = 49.0f; + at.z = this->actor.world.pos.z; + eye.x = player->actor.world.pos.x; + eye.y = 43.0f; + eye.z = player->actor.world.pos.z; + + Gameplay_CameraSetAtEye(globalCtx, sRutoWarpSubCamId, &at, &eye); + Gameplay_CameraSetFov(globalCtx, sRutoWarpSubCamId, 90.0f); + this->rutoWarpState = WARP_BLUE_RUTO_STATE_TALKING; + Message_StartTextbox(globalCtx, 0x4022, NULL); + DoorWarp1_SetupAction(this, func_80999FE4); + } +} + +void func_80999FE4(DoorWarp1* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + OnePointCutscene_Init(globalCtx, 0x25E9, 999, &this->actor, MAIN_CAM); + Gameplay_CopyCamera(globalCtx, -1, sRutoWarpSubCamId); + Gameplay_ChangeCameraStatus(globalCtx, sRutoWarpSubCamId, CAM_STAT_WAIT); + this->rutoWarpState = WARP_BLUE_RUTO_STATE_WARPING; + DoorWarp1_SetupAction(this, DoorWarp1_RutoWarpOut); + } +} + +void DoorWarp1_RutoWarpOut(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_1B2 >= 61) { + if (player->actor.velocity.y < 10.f) { + player->actor.gravity = 0.02f; + } else { + player->actor.gravity = 0.0f; + } + } else { + this->unk_1B2++; + } + Math_SmoothStepToF(&this->lightRayAlpha, 0.0f, 0.2f, 6.0f, 0.01f); + this->warpTimer++; + + if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) { + gSaveContext.eventChkInf[3] |= 0x80; + Item_Give(globalCtx, ITEM_ZORA_SAPPHIRE); + globalCtx->nextEntranceIndex = 0x10E; + gSaveContext.nextCutsceneIndex = 0xFFF0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 7; + } + + Math_StepToF(&this->unk_194, 2.0f, 0.01f); + Math_StepToF(&this->unk_198, 10.f, 0.02f); + Lights_PointNoGlowSetInfo(&this->upperLightInfo, (s16)player->actor.world.pos.x + 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z + 10.0f, 235, 255, + 255, 255); + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, (s16)player->actor.world.pos.x - 10.0f, + (s16)player->actor.world.pos.y - 10.0f, (s16)player->actor.world.pos.z - 10.0f, 235, 255, + 255, 255); + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 0.5f, 2.0f, 0.1f); +} + +void func_8099A3A4(DoorWarp1* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); + Math_SmoothStepToF(&this->lightRayAlpha, 255.0f, 0.2f, 2.0f, 0.1f); + Math_SmoothStepToF(&this->warpAlpha, 255.0f, 0.2f, 2.0f, 0.1f); + + if (this->scale < 10) { + this->scale += 2; + } + if (this->unk_1AE < 120) { + this->unk_1AE += 4; + } + if (this->unk_1B0 < 230) { + this->unk_1B0 += 4; + } else { + DoorWarp1_SetupAction(this, DoorWarp1_AdultWarpIdle); + } +} + +void DoorWarp1_AdultWarpIdle(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player; + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); + + if (DoorWarp1_PlayerInRange(this, globalCtx)) { + player = GET_PLAYER(globalCtx); + + OnePointCutscene_Init(globalCtx, 0x25E8, 999, &this->actor, MAIN_CAM); + func_8002DF54(globalCtx, &this->actor, 10); + player->unk_450.x = this->actor.world.pos.x; + player->unk_450.z = this->actor.world.pos.z; + this->unk_1B2 = 20; + DoorWarp1_SetupAction(this, func_8099A508); + } +} + +void func_8099A508(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_1B2 != 0) { + this->unk_1B2--; + return; + } + Audio_PlaySoundGeneral(NA_SE_EV_LINK_WARP, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Animation_ChangeImpl(&this->skelAnime, &gWarpCrystalAnim, 1.0f, Animation_GetLastFrame(&gWarpCrystalAnim), + Animation_GetLastFrame(&gWarpCrystalAnim), ANIMMODE_ONCE, 40.0f, 1); + + this->unk_1B2 = 0x32; + DoorWarp1_SetupAction(this, DoorWarp1_AdultWarpOut); +} + +void DoorWarp1_AdultWarpOut(DoorWarp1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 temp_f0_2; + + if (this->unk_1B2 != 0) { + this->unk_1B2--; + } + + if (this->unk_1B2 < 31) { + u32 phi_v0 = (LINK_IS_ADULT) ? 35 : 45; + + if ((player->actor.world.pos.y - this->actor.world.pos.y) <= phi_v0) { + player->actor.gravity = 0.0139999995f; + } else { + player->actor.gravity = 0.0f; + player->actor.velocity.y = 0.0f; + } + if (this->unk_1B2 <= 0) { + if (this->unk_1B8 < 4000) { + this->unk_1B8 += 40; + } + player->actor.world.rot.y -= this->unk_1B8; + player->actor.shape.rot.y -= this->unk_1B8; + } + Math_SmoothStepToF(&player->actor.world.pos.x, this->actor.world.pos.x, 0.5f, 0.1f, 0.01f); + Math_SmoothStepToF(&player->actor.world.pos.z, this->actor.world.pos.z, 0.5f, 0.1f, 0.01f); + } + this->warpTimer++; + + if (this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF) { + if (globalCtx->sceneNum == SCENE_MORIBOSSROOM) { + if (!(gSaveContext.eventChkInf[4] & 0x100)) { + gSaveContext.eventChkInf[4] |= 0x100; + Item_Give(globalCtx, ITEM_MEDALLION_FOREST); + globalCtx->nextEntranceIndex = 0x6B; + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.chamberCutsceneNum = CHAMBER_CS_FOREST; + } else { + if (!LINK_IS_ADULT) { + globalCtx->nextEntranceIndex = 0x600; + } else { + globalCtx->nextEntranceIndex = 0x608; + } + gSaveContext.nextCutsceneIndex = 0; + } + } else if (globalCtx->sceneNum == SCENE_FIRE_BS) { + if (!(gSaveContext.eventChkInf[4] & 0x200)) { + gSaveContext.eventChkInf[4] |= 0x200; + Item_Give(globalCtx, ITEM_MEDALLION_FIRE); + globalCtx->nextEntranceIndex = 0xDB; + gSaveContext.nextCutsceneIndex = 0xFFF3; + } else { + if (!LINK_IS_ADULT) { + globalCtx->nextEntranceIndex = 0x4F6; + } else { + globalCtx->nextEntranceIndex = 0x564; + } + gSaveContext.nextCutsceneIndex = 0; + } + } else if (globalCtx->sceneNum == SCENE_MIZUSIN_BS) { + if (!(gSaveContext.eventChkInf[4] & 0x400)) { + gSaveContext.eventChkInf[4] |= 0x400; + Item_Give(globalCtx, ITEM_MEDALLION_WATER); + globalCtx->nextEntranceIndex = 0x6B; + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.chamberCutsceneNum = CHAMBER_CS_WATER; + } else { + if (!LINK_IS_ADULT) { + globalCtx->nextEntranceIndex = 0x604; + } else { + globalCtx->nextEntranceIndex = 0x60C; + } + gSaveContext.nextCutsceneIndex = 0; + } + } else if (globalCtx->sceneNum == SCENE_JYASINBOSS) { + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + Item_Give(globalCtx, ITEM_MEDALLION_SPIRIT); + globalCtx->nextEntranceIndex = 0x6B; + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.chamberCutsceneNum = CHAMBER_CS_SPIRIT; + } else { + if (!LINK_IS_ADULT) { + globalCtx->nextEntranceIndex = 0x1F1; + } else { + globalCtx->nextEntranceIndex = 0x610; + } + gSaveContext.nextCutsceneIndex = 0; + } + } else if (globalCtx->sceneNum == SCENE_HAKADAN_BS) { + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { + Item_Give(globalCtx, ITEM_MEDALLION_SHADOW); + globalCtx->nextEntranceIndex = 0x6B; + gSaveContext.nextCutsceneIndex = 0; + gSaveContext.chamberCutsceneNum = CHAMBER_CS_SHADOW; + } else { + if (!LINK_IS_ADULT) { + globalCtx->nextEntranceIndex = 0x568; + } else { + globalCtx->nextEntranceIndex = 0x580; + } + gSaveContext.nextCutsceneIndex = 0; + } + } + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 7; + } + if (this->warpTimer >= 141) { + f32 screenFillAlpha; + + globalCtx->envCtx.fillScreen = true; + screenFillAlpha = (f32)(this->warpTimer - 140) / 20.0f; + + if (screenFillAlpha > 1.0f) { + screenFillAlpha = 1.0f; + } + globalCtx->envCtx.screenFillColor[0] = 160; + globalCtx->envCtx.screenFillColor[1] = 160; + globalCtx->envCtx.screenFillColor[2] = 160; + globalCtx->envCtx.screenFillColor[3] = (u32)(255.0f * screenFillAlpha); + + osSyncPrintf("\nparcent=[%f]", screenFillAlpha); + } + Lights_PointNoGlowSetInfo(&this->upperLightInfo, (s16)player->actor.world.pos.x + 10.0f, + (s16)player->actor.world.pos.y + 10.0f, (s16)player->actor.world.pos.z + 10.0f, 235, 255, + 255, 255); + Lights_PointNoGlowSetInfo(&this->lowerLightInfo, (s16)player->actor.world.pos.x - 10.0f, + (s16)player->actor.world.pos.y - 10.0f, (s16)player->actor.world.pos.z - 10.0f, 235, 255, + 255, 255); + + Math_SmoothStepToF(&this->actor.shape.yOffset, 800.0f, 0.5f, 15.0f, 0.1f); + this->actor.shape.rot.y += 0x320; + + Math_SmoothStepToF(&this->unk_1BC, 1.13f, 0.2f, 0.1f, 0.01f); + Math_StepToF(&this->unk_194, 2.0f, 0.003f); + Math_StepToF(&this->unk_198, 10.0f, 0.006f); + Math_SmoothStepToF(&this->lightRayAlpha, 0.0f, 0.2f, 3.0f, 0.01f); + Math_SmoothStepToF(&this->warpAlpha, 0.0f, 0.2f, 2.0f, 0.01f); + Math_SmoothStepToF(&this->crystalAlpha, 255.0f, 0.1f, 1.0f, 0.01f); + + temp_f0_2 = 1.0f - (f32)(sWarpTimerTarget - this->warpTimer) / (sWarpTimerTarget - (sWarpTimerTarget - 100)); + if (temp_f0_2 > 0.0f) { + s16 i; + + for (i = 0; i < 3; i++) { + globalCtx->envCtx.adjAmbientColor[i] = globalCtx->envCtx.adjFogColor[i] = + globalCtx->envCtx.adjLight1Color[i] = -255.0f * temp_f0_2; + } + + globalCtx->envCtx.adjFogNear = -500.0f * temp_f0_2; + if (globalCtx->envCtx.adjFogNear < -300) { + globalCtx->roomCtx.curRoom.segment = NULL; + } + } +} + +void DoorWarp1_Destination(DoorWarp1* this, GlobalContext* globalCtx) { + f32 alphaFrac; + + this->warpTimer++; + this->unk_194 = 5.0f; + + alphaFrac = 1.0f; + if (this->warpTimer < 20) { + alphaFrac = this->warpTimer / 20.f; + } else if (this->warpTimer >= 60) { + alphaFrac = 1.0f - ((this->warpTimer - 60.0f) / 20.f); + } + this->warpAlpha = 255.0f * alphaFrac; + this->lightRayAlpha = 0.0f; + + if (this->warpTimer >= 80.0f) { + this->warpAlpha = 0.0f; + DoorWarp1_SetupAction(this, DoorWarp1_DoNothing); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); +} + +void DoorWarp1_DoNothing(DoorWarp1* this, GlobalContext* globalCtx) { +} + +void func_8099B020(DoorWarp1* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->lightRayAlpha, 128.0f, 0.2f, 2.0f, 0.1f); + Math_SmoothStepToF(&this->warpAlpha, 128.0f, 0.2f, 2.0f, 0.1f); + + if (this->lightRayAlpha >= 128.0f) { + Math_StepToF(&this->unk_194, 2.0f, 0.01f); + Math_StepToF(&this->unk_198, 10.0f, 0.02f); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WARP_HOLE - SFX_FLAG); +} + +void DoorWarp1_Update(Actor* thisx, GlobalContext* globalCtx) { + DoorWarp1* this = (DoorWarp1*)thisx; + + this->actionFunc(this, globalCtx); + + if (this->actor.params != WARP_PURPLE_CRYSTAL) { + Actor_SetScale(&this->actor, this->scale / 100.0f); + } +} + +void DoorWarp1_DrawBlueCrystal(DoorWarp1* this, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2078); + + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0xFF, 0xFF, 200, 255, 255, (u8)this->crystalAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, (u8)this->crystalAlpha); + + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, + &this->actor, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2098); + + SkelAnime_Update(&this->skelAnime); +} + +void DoorWarp1_DrawPurpleCrystal(DoorWarp1* this, GlobalContext* globalCtx) { + s32 pad[2]; + Vec3f eye; + + eye.x = -(Math_SinS(globalCtx->state.frames * 200) * 120.0f) * 80.0f; + eye.y = (Math_CosS(globalCtx->state.frames * 200) * 120.0f) * 80.0f; + eye.z = (Math_CosS(globalCtx->state.frames * 200) * 120.0f) * 80.0f; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2122); + + func_80093D84(globalCtx->state.gfxCtx); + func_8002EB44(&this->actor.world.pos, &eye, &eye, globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (u8)this->crystalAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 0, 100, (u8)this->crystalAlpha); + + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, + &this->actor, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2152); + + SkelAnime_Update(&this->skelAnime); +} + +void DoorWarp1_DrawWarp(DoorWarp1* this, GlobalContext* globalCtx) { + s32 pad; + u32 pad1; + u32 spEC = globalCtx->state.frames * 10; + f32 spE8 = (this->unk_194 >= 1.0f) ? 0.0f : 1.0f - this->unk_194; + f32 spE4 = (this->unk_198 >= 1.0f) ? 0.0f : 1.0f - this->unk_198; + f32 xzScale; + f32 temp_f0; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2173); + + temp_f0 = 1.0f - (2.0f - this->unk_194) / 1.7f; + if (this->actor.params != WARP_YELLOW && this->actor.params != WARP_DESTINATION && + this->actor.params != WARP_ORANGE && this->actor.params != WARP_GREEN && this->actor.params != WARP_RED) { + this->unk_19C += (s16)(temp_f0 * 15.0f); + } + if (this->actor.params == WARP_DESTINATION) { + this->unk_19C -= (s16)(temp_f0 * 2.0f); + } + func_80093D84(globalCtx->state.gfxCtx); + + switch (this->actor.params) { + case WARP_YELLOW: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 255); + break; + case WARP_ORANGE: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 150, 0, 255); + break; + case WARP_GREEN: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 200, 0, 255); + break; + case WARP_RED: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 0, 255); + break; + default: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255 * temp_f0, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255 * temp_f0, 255, 255); + break; + } + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_AD_NOTPATTERN | G_CD_MAGICSQ); + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y + 1.0f, this->actor.world.pos.z, MTXMODE_NEW); + gSPSegment(POLY_XLU_DISP++, 0x0A, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2247)); + Matrix_Push(); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, spEC & 0xFF, -((s16)(this->unk_19C + this->unk_19C) & 511), + 0x100, 0x100, 1, spEC & 0xFF, -((s16)(this->unk_19C + this->unk_19C) & 511), 0x100, + 0x100)); + + Matrix_Translate(0.0f, this->unk_194 * 230.0f, 0.0f, MTXMODE_APPLY); + xzScale = (((f32)this->unk_1AE * spE8) / 100.0f) + 1.0f; + Matrix_Scale(xzScale, 1.0f, xzScale, MTXMODE_APPLY); + gSPSegment(POLY_XLU_DISP++, 0x09, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2267)); + gSPDisplayList(POLY_XLU_DISP++, gWarpPortalDL); + Matrix_Pop(); + + if (this->lightRayAlpha > 0.0f) { + switch (this->actor.params) { + case WARP_YELLOW: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 255, 0, 255); + break; + case WARP_ORANGE: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 150, 0, 255); + break; + case WARP_GREEN: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 200, 0, 255); + break; + case WARP_RED: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255, 255, 255, (u8)this->warpAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 0, 255); + break; + default: + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x80, 255 * temp_f0, 255, 255, (u8)this->lightRayAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255 * temp_f0, 255, 255); + break; + } + spEC *= 2; + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, spEC & 0xFF, -((s16)this->unk_19C & 511), 0x100, 0x100, + 1, spEC & 0xFF, -((s16)this->unk_19C & 511), 0x100, 0x100)); + + Matrix_Translate(0.0f, this->unk_198 * 60.0f, 0.0f, MTXMODE_APPLY); + + xzScale = (((f32)this->unk_1B0 * spE4) / 100.0f) + 1.0f; + Matrix_Scale(xzScale, 1.0f, xzScale, MTXMODE_APPLY); + + gSPSegment(POLY_XLU_DISP++, 0x09, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2336)); + gSPDisplayList(POLY_XLU_DISP++, gWarpPortalDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_door_warp1.c", 2340); +} + +void DoorWarp1_Draw(Actor* thisx, GlobalContext* globalCtx) { + DoorWarp1* this = (DoorWarp1*)thisx; + + switch (this->actor.params) { + case WARP_DUNGEON_ADULT: + DoorWarp1_DrawBlueCrystal(this, globalCtx); + DoorWarp1_DrawWarp(this, globalCtx); + break; + case WARP_DUNGEON_CHILD: + case WARP_CLEAR_FLAG: + case WARP_SAGES: + case WARP_YELLOW: + case WARP_BLUE_RUTO: + case WARP_DESTINATION: + case WARP_UNK_7: + case WARP_ORANGE: + case WARP_GREEN: + case WARP_RED: + DoorWarp1_DrawWarp(this, globalCtx); + break; + case WARP_BLUE_CRYSTAL: + DoorWarp1_DrawBlueCrystal(this, globalCtx); + break; + case WARP_PURPLE_CRYSTAL: + DoorWarp1_DrawPurpleCrystal(this, globalCtx); + break; + } +} + +void DoorWarp1_Reset(void) { + sWarpTimerTarget = 0; + sRutoWarpSubCamId = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h new file mode 100644 index 000000000..4f335c81d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Door_Warp1/z_door_warp1.h @@ -0,0 +1,63 @@ +#ifndef Z_DOOR_WARP1_H +#define Z_DOOR_WARP1_H + +#include "ultra64.h" +#include "global.h" + +struct DoorWarp1; + +typedef enum { + /* -2 */ WARP_BLUE_CRYSTAL = -2, + /* -1 */ WARP_DUNGEON_ADULT, + /* 0 */ WARP_DUNGEON_CHILD, + /* 1 */ WARP_CLEAR_FLAG, // Activate on temp clear flag + /* 2 */ WARP_SAGES, // Used by sages warping into chamber of sages during their cutscene + /* 3 */ WARP_PURPLE_CRYSTAL, + /* 4 */ WARP_YELLOW, // The colored variants don't warp, they are cutscene setpieces + /* 5 */ WARP_BLUE_RUTO, + /* 6 */ WARP_DESTINATION, // Spawning in after having taken a warp + /* 7 */ WARP_UNK_7, + /* 8 */ WARP_ORANGE, + /* 9 */ WARP_GREEN, + /* 10 */ WARP_RED +} DoorWarp1Type; + +typedef enum { + /* 0 */ WARP_BLUE_RUTO_STATE_INITIAL, // initial, warp doesn't work yet + /* 1 */ WARP_BLUE_RUTO_STATE_READY, // set by ruto, warp can work now + /* 2 */ WARP_BLUE_RUTO_STATE_ENTERED, // set by warp, player has stepped into the warp + /* 3 */ WARP_BLUE_RUTO_STATE_3, // set by ruto, folding arms + /* 4 */ WARP_BLUE_RUTO_STATE_TALKING, // set by warp, dialog started + /* 5 */ WARP_BLUE_RUTO_STATE_WARPING // set by warp, after closing dialog +} DoorWarp1RutoState; + +typedef void (*DoorWarp1ActionFunc)(struct DoorWarp1*, GlobalContext*); + +typedef struct DoorWarp1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ char unk_190[0x2]; + /* 0x0192 */ u16 warpTimer; + /* 0x0194 */ f32 unk_194; + /* 0x0198 */ f32 unk_198; + /* 0x019C */ f32 unk_19C; + /* 0x01A0 */ f32 lightRayAlpha; + /* 0x01A4 */ f32 warpAlpha; + /* 0x01A8 */ f32 crystalAlpha; + /* 0x01AC */ s16 scale; + /* 0x01AE */ s16 unk_1AE; + /* 0x01B0 */ s16 unk_1B0; + /* 0x01B2 */ s16 unk_1B2; + /* 0x01B4 */ f32 unk_1B4; + /* 0x01B8 */ s16 unk_1B8; + /* 0x01BA */ u16 unk_1BA; + /* 0x01BC */ f32 unk_1BC; + /* 0x01C0 */ DoorWarp1ActionFunc actionFunc; + /* 0x01C4 */ LightNode* upperLight; + /* 0x01C8 */ LightInfo upperLightInfo; + /* 0x01D8 */ LightNode* lowerLight; + /* 0x01DC */ LightInfo lowerLightInfo; + /* 0x01EC */ s32 rutoWarpState; // for state communication with En_Ru1 using DoorWarp1RutoState values +} DoorWarp1; // size = 0x01F0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.c b/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.c new file mode 100644 index 000000000..50ddabcd1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.c @@ -0,0 +1,253 @@ +#include "z_efc_erupc.h" +#include "objects/object_efc_erupc/object_efc_erupc.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EfcErupc_Init(Actor* thisx, GlobalContext* globalCtx); +void EfcErupc_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EfcErupc_Update(Actor* thisx, GlobalContext* globalCtx); +void EfcErupc_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EfcErupc_UpdateAction(EfcErupc* this, GlobalContext* globalCtx); +void EfcErupc_DrawParticles(EfcErupcParticles* particles, GlobalContext* globalCtx); +void EfcErupc_UpdateParticles(EfcErupc* this, GlobalContext* globalCtx); +void EfcErupc_AddParticle(EfcErupcParticles* particles, Vec3f* pos, Vec3f* vel, Vec3f* accel, f32 scaleFactor); +void EfcErupc_InitParticles(EfcErupcParticles* particles); + +const ActorInit Efc_Erupc_InitVars = { + ACTOR_EFC_ERUPC, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_EFC_ERUPC, + sizeof(EfcErupc), + (ActorFunc)EfcErupc_Init, + (ActorFunc)EfcErupc_Destroy, + (ActorFunc)EfcErupc_Update, + (ActorFunc)EfcErupc_Draw, + NULL, +}; + +void EfcErupc_SetupAction(EfcErupc* this, EfcErupcActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EfcErupc_Init(Actor* thisx, GlobalContext* globalCtx) { + EfcErupc* this = (EfcErupc*)thisx; + + EfcErupc_SetupAction(this, EfcErupc_UpdateAction); + Actor_SetScale(&this->actor, 1.0f); + EfcErupc_InitParticles(this->particles); + this->unk14C = this->unk14E = this->unk150 = 0; + this->unk152 = 5; + this->unk154 = -100; +} + +void EfcErupc_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EfcErupc_UpdateAction(EfcErupc* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f vel; + Vec3f accel; + s32 i; + + if (globalCtx->csCtx.state != 0) { + if (globalCtx->csCtx.npcActions[1] != NULL) { + if (globalCtx->csCtx.npcActions[1]->action == 2) { + if (this->unk150 == 30) { + func_800788CC(NA_SE_IT_EARTHQUAKE); + } + if (this->unk150 <= 64) { + if (this->unk154 < 200) { + this->unk154 += 10; + } + } else { + if (this->unk154 > -100) { + this->unk154 -= 10; + } + } + this->unk150++; + } else { + if (this->unk154 > -100) { + this->unk154 -= 10; + } + } + } + } + if (globalCtx->csCtx.state != 0) { + if (globalCtx->csCtx.npcActions[2] != NULL) { + switch (globalCtx->csCtx.npcActions[2]->action) { + case 2: + if (this->unk14E == 0) { + func_800F3F3C(6); + gSaveContext.eventChkInf[2] |= 0x8000; + } + this->unk14E++; + break; + case 3: + this->unk14E = 30; + } + this->unk14C++; + } + } + accel.z = 0.0f; + accel.x = 0.0f; + pos.y = this->actor.world.pos.y + 300.0f; + for (i = 0; i < this->unk152; i++) { + pos.x = Rand_CenteredFloat(100.0f) + this->actor.world.pos.x; + pos.z = Rand_CenteredFloat(100.0f) + this->actor.world.pos.z; + vel.x = Rand_CenteredFloat(100.0f); + vel.y = Rand_ZeroFloat(100.0f); + vel.z = Rand_CenteredFloat(100.0f); + accel.y = this->unk154 * 0.1f; + EfcErupc_AddParticle(this->particles, &pos, &vel, &accel, 80.0f); + } +} + +void EfcErupc_Update(Actor* thisx, GlobalContext* globalCtx) { + EfcErupc* this = (EfcErupc*)thisx; + + this->actionFunc(this, globalCtx); + EfcErupc_UpdateParticles(this, globalCtx); +} + +void EfcErupc_Draw(Actor* thisx, GlobalContext* globalCtx) { + EfcErupc* this = (EfcErupc*)thisx; + u16 csAction; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_efc_erupc.c", 282); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->unk14C * 1, this->unk14E * -4, 32, 64, 1, + this->unk14C * 4, this->unk14E * -20, 64, 64)); + + gSPSegment( + POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, this->unk150 * -4, 16, 128, 1, 0, this->unk150 * 12, 32, 32)); + + gSPSegment( + POLY_XLU_DISP++, 0x0A, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, this->unk150 * -4, 16, 128, 1, 0, this->unk150 * 12, 32, 32)); + + Matrix_Push(); + Matrix_Scale(0.8f, 0.8f, 0.8f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_efc_erupc.c", 321), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (globalCtx->csCtx.state != 0) { + if ((globalCtx->csCtx.npcActions[1] != 0) && (globalCtx->csCtx.npcActions[1]->action == 2)) { + gSPDisplayList(POLY_XLU_DISP++, object_efc_erupc_DL_002570); + } + } + Matrix_Pop(); + Matrix_Scale(3.4f, 3.4f, 3.4f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_efc_erupc.c", 333), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (globalCtx->csCtx.state != 0) { + CsCmdActorAction* csActorAction = globalCtx->csCtx.npcActions[2]; + if (csActorAction != 0) { + csAction = csActorAction->action; + if ((csAction == 2) || (csAction == 3)) { + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 200, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 0, 0, 255); + gSPDisplayList(POLY_XLU_DISP++, object_efc_erupc_DL_001720); + } + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_efc_erupc.c", 356); + EfcErupc_DrawParticles(this->particles, globalCtx); +} + +void EfcErupc_DrawParticles(EfcErupcParticles* particles, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 i; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_efc_erupc.c", 368); + for (i = 0; i < EFC_ERUPC_NUM_PARTICLES; i++, particles++) { + if (particles->isActive) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, object_efc_erupc_DL_002760); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, particles->color.r, particles->color.g, particles->color.b, + particles->alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 0, 0, 0); + gDPPipeSync(POLY_XLU_DISP++); + Matrix_Translate(particles->pos.x, particles->pos.y, particles->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(particles->scale, particles->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_efc_erupc.c", 393), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_efc_erupc_DL_0027D8); + } + } + CLOSE_DISPS(gfxCtx, "../z_efc_erupc.c", 399); +} + +static Color_RGB8 D_8099D770[] = { + { 255, 128, 0 }, + { 255, 0, 0 }, + { 255, 255, 0 }, + { 255, 0, 0 }, +}; + +void EfcErupc_UpdateParticles(EfcErupc* this, GlobalContext* globalCtx) { + s16 i; + s16 index; + Color_RGB8 particleColors[] = { + { 255, 128, 0 }, + { 255, 0, 0 }, + { 0, 0, 0 }, + { 100, 0, 0 }, + }; + Color_RGB8* color; + EfcErupcParticles* cur = this->particles; + + for (i = 0; i < EFC_ERUPC_NUM_PARTICLES; i++, cur++) { + if (cur->isActive) { + cur->pos.x += cur->vel.x; + cur->pos.y += cur->vel.y; + cur->pos.z += cur->vel.z; + cur->vel.x += cur->accel.x; + cur->vel.y += cur->accel.y; + cur->vel.z += cur->accel.z; + cur->animTimer += 1; + index = cur->animTimer % 4; + color = &particleColors[index]; + cur->color.r = color->r; + cur->color.g = color->g; + cur->color.b = color->b; + cur->alpha -= 20; + if (cur->alpha <= 0) { + cur->alpha = 0; + cur->isActive = false; + } + } + } +} + +void EfcErupc_AddParticle(EfcErupcParticles* particles, Vec3f* pos, Vec3f* vel, Vec3f* accel, f32 scaleFactor) { + s16 i; + + for (i = 0; i < EFC_ERUPC_NUM_PARTICLES; i++, particles++) { + if (!particles->isActive) { + particles->isActive = true; + particles->pos = *pos; + particles->vel = *vel; + particles->accel = *accel; + particles->scale = scaleFactor / 1000.0f; + particles->alpha = 255; + particles->animTimer = (s16)Rand_ZeroFloat(10.0f); + return; + } + } +} + +void EfcErupc_InitParticles(EfcErupcParticles* particles) { + s16 i; + + for (i = 0; i < EFC_ERUPC_NUM_PARTICLES; i++, particles++) { + particles->isActive = false; + } +} diff --git a/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h b/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h new file mode 100644 index 000000000..6b201cf5d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Efc_Erupc/z_efc_erupc.h @@ -0,0 +1,37 @@ +#ifndef Z_EFC_ERUPC_H +#define Z_EFC_ERUPC_H + +#include "ultra64.h" +#include "global.h" + +struct EfcErupc; + +typedef void (*EfcErupcActionFunc)(struct EfcErupc*, GlobalContext*); + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f vel; + /* 0x18 */ Vec3f accel; + /* 0x24 */ u8 isActive; + /* 0x25 */ u8 animTimer; + /* 0x28 */ Color_RGB8 color; + /* 0x2C */ s16 alpha; + /* 0x30 */ char unk_2C[4]; + /* 0x34 */ f32 scale; + /* 0x38 */ char unk_34[8]; +} EfcErupcParticles; // size 0x3C + +#define EFC_ERUPC_NUM_PARTICLES 100 + +typedef struct EfcErupc { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 unk14C; + /* 0x014E */ s16 unk14E; + /* 0x0150 */ s16 unk150; + /* 0x0152 */ s16 unk152; + /* 0x0154 */ s16 unk154; + /* 0x0158 */ EfcErupcParticles particles[EFC_ERUPC_NUM_PARTICLES]; + /* 0x18C8 */ EfcErupcActionFunc actionFunc; +} EfcErupc; // size = 0x18CC + +#endif diff --git a/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c b/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c new file mode 100644 index 000000000..1c52a6764 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.c @@ -0,0 +1,373 @@ +/** + * File z_eff_dust.c + * Overlay: ovl_Eff_Dust + * Description: Dust effects + */ + +#include "z_eff_dust.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EffDust_Init(Actor* thisx, GlobalContext* globalCtx); +void EffDust_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EffDust_Update(Actor* thisx, GlobalContext* globalCtx); +void EffDust_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EffDust_InitPosAndDistance(EffDust* this); + +void EffDust_UpdateFunc_8099DB28(EffDust* this, GlobalContext* globalCtx); +void EffDust_UpdateFunc_8099DD74(EffDust* this, GlobalContext* globalCtx); +void EffDust_UpdateFunc_8099DFC0(EffDust* this, GlobalContext* globalCtx); +void EffDust_DrawFunc_8099E4F4(Actor* thisx, GlobalContext* globalCtx); +void EffDust_DrawFunc_8099E784(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Eff_Dust_InitVars = { + ACTOR_EFF_DUST, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EffDust), + (ActorFunc)EffDust_Init, + (ActorFunc)EffDust_Destroy, + (ActorFunc)EffDust_Update, + (ActorFunc)EffDust_Draw, + NULL, +}; + +static Gfx sEmptyDL[] = { + gsSPEndDisplayList(), +}; + +void EffDust_SetupAction(EffDust* this, EffDustActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EffDust_SetupDraw(EffDust* this, EffDustDrawFunc drawFunc) { + this->drawFunc = drawFunc; +} + +void EffDust_InitPosAndDistance(EffDust* this) { + s32 i; + + for (i = 0; i < 64; i++) { + this->initialPositions[i].z = 0.0f; + this->initialPositions[i].y = 0.0f; + this->initialPositions[i].x = 0.0f; + + this->distanceTraveled[i] = 1.0f; + } + this->index = 0; +} + +void EffDust_Init(Actor* thisx, GlobalContext* globalCtx) { + EffDust* this = (EffDust*)thisx; + EffDustType dustEffect = this->actor.params; + + EffDust_InitPosAndDistance(this); + + switch (dustEffect) { + case EFF_DUST_TYPE_0: + EffDust_SetupAction(this, EffDust_UpdateFunc_8099DB28); + EffDust_SetupDraw(this, EffDust_DrawFunc_8099E4F4); + this->dy = 0.8f; + this->dz = 0.8f; + this->dx = 1.0f; + this->scalingFactor = 0.1f; + break; + case EFF_DUST_TYPE_1: + EffDust_SetupAction(this, EffDust_UpdateFunc_8099DD74); + EffDust_SetupDraw(this, EffDust_DrawFunc_8099E4F4); + this->dx = 0.8f; + this->dz = 0.8f; + this->dy = 1.0f; + this->scalingFactor = 0.5f; + break; + case EFF_DUST_TYPE_2: + EffDust_SetupAction(this, EffDust_UpdateFunc_8099DFC0); + EffDust_SetupDraw(this, EffDust_DrawFunc_8099E784); + this->dx = 0.5f; + this->scalingFactor = 15.0f; + break; + case EFF_DUST_TYPE_3: + EffDust_SetupAction(this, EffDust_UpdateFunc_8099DFC0); + EffDust_SetupDraw(this, EffDust_DrawFunc_8099E784); + this->dx = 0.5f; + this->scalingFactor = 10.0f; + break; + case EFF_DUST_TYPE_4: + EffDust_SetupAction(this, EffDust_UpdateFunc_8099DFC0); + EffDust_SetupDraw(this, EffDust_DrawFunc_8099E784); + this->actor.room = -1; + this->dx = 0.5f; + this->scalingFactor = 20.0f; + break; + default: + SystemArena_FreeDebug(this, "../z_eff_dust.c", 202); + break; + } + + this->life = 10; +} + +void EffDust_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EffDust_UpdateFunc_8099DB28(EffDust* this, GlobalContext* globalCtx) { + s16 theta; + s16 fi; + f32* distanceTraveled = this->distanceTraveled; + s32 i; + s32 j; + + for (i = 0; i < 64; i++) { + if ((*distanceTraveled) < 1.0f) { + *distanceTraveled += 0.05f; + } + distanceTraveled++; + } + + for (j = 0; j < 3; j++) { + i = this->index & 0x3F; + if (this->distanceTraveled[i] >= 1.0f) { + // Spherical coordinate system. + fi = Rand_CenteredFloat(8192.0f); + theta = Rand_CenteredFloat(4096.0f); + this->initialPositions[i].x = -800.0f * Math_CosS(fi) * Math_CosS(theta); + this->initialPositions[i].y = -800.0f * Math_SinS(theta); + this->initialPositions[i].z = -800.0f * Math_SinS(fi) * Math_CosS(theta); + this->distanceTraveled[i] = 0.0f; + this->index += 1; + } + } +} + +void EffDust_UpdateFunc_8099DD74(EffDust* this, GlobalContext* globalCtx) { + s16 theta; + s16 fi; + f32* distanceTraveled = this->distanceTraveled; + s32 i; + s32 j; + + for (i = 0; i < 64; i++) { + if ((*distanceTraveled) < 1.0f) { + *distanceTraveled += 0.03f; + } + distanceTraveled++; + } + + for (j = 0; j < 2; j++) { + i = this->index & 0x3F; + if (this->distanceTraveled[i] >= 1.0f) { + // Spherical coordinate system. + fi = Rand_CenteredFloat(65536.0f); + theta = Rand_ZeroFloat(8192.0f); + this->initialPositions[i].x = 400.0f * Math_CosS(fi) * Math_CosS(theta); + this->initialPositions[i].y = 400.0f * Math_SinS(theta); + this->initialPositions[i].z = 400.0f * Math_SinS(fi) * Math_CosS(theta); + this->distanceTraveled[i] = 0.0f; + this->index += 1; + } + } +} + +void EffDust_UpdateFunc_8099DFC0(EffDust* this, GlobalContext* globalCtx) { + s16 theta; + Player* player = GET_PLAYER(globalCtx); + Actor* parent = this->actor.parent; + f32* distanceTraveled = this->distanceTraveled; + s32 i; + s32 j; + + if (parent == NULL || parent->update == NULL || !(player->stateFlags1 & 0x1000)) { + if (this->life != 0) { + this->life -= 1; + } else { + Actor_Kill(&this->actor); + } + + for (i = 0; i < 64; i++) { + if ((*distanceTraveled) < 1.0f) { + *distanceTraveled += 0.2f; + } + distanceTraveled++; + } + + return; + } + + for (i = 0; i < 64; i++) { + if ((*distanceTraveled) < 1.0f) { + *distanceTraveled += 0.1f; + } + distanceTraveled++; + } + + this->actor.world.pos = parent->world.pos; + + for (j = 0; j < 3; j++) { + i = this->index & 0x3F; + if (this->distanceTraveled[i] >= 1.0f) { + + theta = Rand_CenteredFloat(65536.0f); + switch (this->actor.params) { + case EFF_DUST_TYPE_2: + this->initialPositions[i].x = (Rand_ZeroOne() * 4500.0f) + 700.0f; + if (this->initialPositions[i].x > 3000.0f) { + this->initialPositions[i].y = (3000.0f * Rand_ZeroOne()) * Math_SinS(theta); + this->initialPositions[i].z = (3000.0f * Rand_ZeroOne()) * Math_CosS(theta); + } else { + this->initialPositions[i].y = 3000.0f * Math_SinS(theta); + this->initialPositions[i].z = 3000.0f * Math_CosS(theta); + } + break; + + case EFF_DUST_TYPE_3: + this->initialPositions[i].x = (Rand_ZeroOne() * 2500.0f) + 700.0f; + if (this->initialPositions[i].x > 2000.0f) { + this->initialPositions[i].y = (2000.0f * Rand_ZeroOne()) * Math_SinS(theta); + this->initialPositions[i].z = (2000.0f * Rand_ZeroOne()) * Math_CosS(theta); + } else { + this->initialPositions[i].y = 2000.0f * Math_SinS(theta); + this->initialPositions[i].z = 2000.0f * Math_CosS(theta); + } + break; + + case EFF_DUST_TYPE_4: + this->initialPositions[i].x = (Rand_ZeroOne() * 8500.0f) + 1700.0f; + if (this->initialPositions[i].x > 5000.0f) { + this->initialPositions[i].y = (4000.0f * Rand_ZeroOne()) * Math_SinS(theta); + this->initialPositions[i].z = (4000.0f * Rand_ZeroOne()) * Math_CosS(theta); + } else { + this->initialPositions[i].y = 4000.0f * Math_SinS(theta); + this->initialPositions[i].z = 4000.0f * Math_CosS(theta); + } + + break; + default: + break; + } + + this->distanceTraveled[i] = 0.0f; + this->index += 1; + } + } +} + +void EffDust_Update(Actor* thisx, GlobalContext* globalCtx) { + EffDust* this = (EffDust*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EffDust_DrawFunc_8099E4F4(Actor* thisx, GlobalContext* globalCtx2) { + EffDust* this = (EffDust*)thisx; + GlobalContext* globalCtx = globalCtx2; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + Vec3f* initialPositions; + f32* distanceTraveled; + s32 i; + f32 aux; + + OPEN_DISPS(gfxCtx, "../z_eff_dust.c", 425); + + func_80093D18(gfxCtx); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 128, 128, 128, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 128, 128, 128, 0); + + initialPositions = this->initialPositions; + distanceTraveled = this->distanceTraveled; + + gSPSegment(POLY_XLU_DISP++, 0x08, sEmptyDL); + + for (i = 0; i < 64; i++) { + if (*distanceTraveled < 1.0f) { + aux = 1.0f - (*distanceTraveled * *distanceTraveled); + Matrix_Translate(this->actor.world.pos.x + (initialPositions->x * ((this->dx * aux) + (1.0f - this->dx))), + this->actor.world.pos.y + (initialPositions->y * ((this->dy * aux) + (1.0f - this->dy))), + this->actor.world.pos.z + (initialPositions->z * ((this->dz * aux) + (1.0f - this->dz))), + MTXMODE_NEW); + + Matrix_Scale(this->scalingFactor, this->scalingFactor, this->scalingFactor, MTXMODE_APPLY); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_dust.c", 449), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gEffSparklesDL)); + } + + initialPositions++; + distanceTraveled++; + // Needed for matching. + if (0) {} + } + + CLOSE_DISPS(gfxCtx, "../z_eff_dust.c", 458); +} + +void EffDust_DrawFunc_8099E784(Actor* thisx, GlobalContext* globalCtx2) { + EffDust* this = (EffDust*)thisx; + GlobalContext* globalCtx = globalCtx2; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32* distanceTraveled; + Vec3f* initialPositions; + s32 i; + f32 aux; + Player* player = GET_PLAYER(globalCtx); + + OPEN_DISPS(gfxCtx, "../z_eff_dust.c", 472); + + func_80093D18(gfxCtx); + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + if (player->unk_858 >= 0.85f) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 255, 0); + } + + initialPositions = this->initialPositions; + distanceTraveled = this->distanceTraveled; + + gSPSegment(POLY_XLU_DISP++, 0x08, sEmptyDL); + + for (i = 0; i < 64; i++) { + if (*distanceTraveled < 1.0f) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, *distanceTraveled * 255); + + // Needed to match. + if (!this) {} + + aux = 1.0f - (*distanceTraveled * *distanceTraveled); + + Matrix_Mult(&player->mf_9E0, MTXMODE_NEW); + + Matrix_Translate(initialPositions->x * ((this->dx * aux) + (1.0f - this->dx)), + initialPositions->y * (1.0f - *distanceTraveled) + 320.0f, + initialPositions->z * (1.0f - *distanceTraveled) + -20.0f, MTXMODE_APPLY); + + Matrix_Scale(*distanceTraveled * this->scalingFactor, *distanceTraveled * this->scalingFactor, + *distanceTraveled * this->scalingFactor, MTXMODE_APPLY); + + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_dust.c", 506), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gEffSparklesDL)); + } + + initialPositions++; + distanceTraveled++; + } + + CLOSE_DISPS(gfxCtx, "../z_eff_dust.c", 515); +} + +void EffDust_Draw(Actor* thisx, GlobalContext* globalCtx) { + EffDust* this = (EffDust*)thisx; + + this->drawFunc(thisx, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.h b/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.h new file mode 100644 index 000000000..a53866e55 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Eff_Dust/z_eff_dust.h @@ -0,0 +1,34 @@ +#ifndef Z_EFF_DUST_H +#define Z_EFF_DUST_H + +#include "ultra64.h" +#include "global.h" + +struct EffDust; + +typedef void (*EffDustActionFunc)(struct EffDust*, GlobalContext*); +typedef void (*EffDustDrawFunc)(Actor*, GlobalContext*); + +typedef struct EffDust { + /* 0x0000 */ Actor actor; + /* 0x014C */ f32 distanceTraveled[64]; // For each particle. Normalized. From 0.0f to 1.0f + /* 0x024C */ Vec3f initialPositions[64]; // Array of position for each dust particle. + /* 0x054C */ u8 index; + /* 0x054D */ u8 life; // Only considered if actor.params is 2, 3 or 4. + /* 0x0550 */ f32 dx; // Normalized. 0.0f to 1.0f + /* 0x0554 */ f32 dy; // Normalized. 0.0f to 1.0f + /* 0x0558 */ f32 dz; // Normalized. 0.0f to 1.0f + /* 0x055C */ f32 scalingFactor; + /* 0x0560 */ EffDustActionFunc actionFunc; + /* 0x0564 */ EffDustDrawFunc drawFunc; +} EffDust; // size = 0x0568 + +typedef enum { + /* 0x00 */ EFF_DUST_TYPE_0, + /* 0x01 */ EFF_DUST_TYPE_1, + /* 0x02 */ EFF_DUST_TYPE_2, + /* 0x03 */ EFF_DUST_TYPE_3, + /* 0x04 */ EFF_DUST_TYPE_4 +} EffDustType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c b/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c new file mode 100644 index 000000000..2e8858a94 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c @@ -0,0 +1,194 @@ +/* + * File: z_elf_msg.c + * Overlay: ovl_Elf_Msg + * Description: Readable Navi call spot + */ + +#include "z_elf_msg.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" + +#define FLAGS ACTOR_FLAG_4 + +void ElfMsg_Init(Actor* thisx, GlobalContext* globalCtx); +void ElfMsg_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ElfMsg_Update(Actor* thisx, GlobalContext* globalCtx); +void ElfMsg_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ElfMsg_CallNaviCuboid(ElfMsg* this, GlobalContext* globalCtx); +void ElfMsg_CallNaviCylinder(ElfMsg* this, GlobalContext* globalCtx); + +const ActorInit Elf_Msg_InitVars = { + ACTOR_ELF_MSG, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ElfMsg), + (ActorFunc)ElfMsg_Init, + (ActorFunc)ElfMsg_Destroy, + (ActorFunc)ElfMsg_Update, + (ActorFunc)ElfMsg_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_STOP), +}; + +void ElfMsg_SetupAction(ElfMsg* this, ElfMsgActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +/** + * Checks a scene flag - if flag is set, the actor is killed and function returns 1. Otherwise returns 0. + * Can also set a switch flag from params while killing. + */ +s32 ElfMsg_KillCheck(ElfMsg* this, GlobalContext* globalCtx) { + if ((this->actor.world.rot.y > 0) && (this->actor.world.rot.y < 0x41) && + Flags_GetSwitch(globalCtx, this->actor.world.rot.y - 1)) { + LOG_STRING("共倒れ", "../z_elf_msg.c", 161); // "Mutual destruction" + if (((this->actor.params >> 8) & 0x3F) != 0x3F) { + Flags_SetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F); + } + Actor_Kill(&this->actor); + return 1; + } else if ((this->actor.world.rot.y == -1) && Flags_GetClear(globalCtx, this->actor.room)) { + LOG_STRING("共倒れ", "../z_elf_msg.c", 172); // "Mutual destruction" + if (((this->actor.params >> 8) & 0x3F) != 0x3F) { + Flags_SetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F); + } + Actor_Kill(&this->actor); + return 1; + } else if (((this->actor.params >> 8) & 0x3F) == 0x3F) { + return 0; + } else if (Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + Actor_Kill(&this->actor); + return 1; + } + return 0; +} + +void ElfMsg_Init(Actor* thisx, GlobalContext* globalCtx) { + ElfMsg* this = (ElfMsg*)thisx; + + // "Conditions for Elf Tag disappearing" + osSyncPrintf(VT_FGCOL(CYAN) "\nエルフ タグ 消える条件 %d" VT_RST "\n", (thisx->params >> 8) & 0x3F); + osSyncPrintf(VT_FGCOL(CYAN) "\nthisx->shape.angle.sy = %d\n" VT_RST, thisx->shape.rot.y); + if (thisx->shape.rot.y >= 0x41) { + // "Conditions for Elf Tag appearing" + osSyncPrintf(VT_FGCOL(CYAN) "\nエルフ タグ 出現条件 %d" VT_RST "\n", thisx->shape.rot.y - 0x41); + } + + if (!ElfMsg_KillCheck(this, globalCtx)) { + Actor_ProcessInitChain(thisx, sInitChain); + if (thisx->world.rot.x == 0) { + thisx->scale.z = 0.4f; + thisx->scale.x = 0.4f; + } else { + thisx->scale.x = thisx->scale.z = thisx->world.rot.x * 0.04f; + } + + if (thisx->world.rot.z == 0) { + thisx->scale.y = 0.4f; + } else { + thisx->scale.y = thisx->world.rot.z * 0.04f; + } + + if (thisx->params & 0x4000) { + ElfMsg_SetupAction(this, ElfMsg_CallNaviCuboid); + } else { + ElfMsg_SetupAction(this, ElfMsg_CallNaviCylinder); + } + + thisx->shape.rot.x = thisx->shape.rot.y = thisx->shape.rot.z = 0; + } +} + +void ElfMsg_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +s32 ElfMsg_GetMessageId(ElfMsg* this) { + // Negative message ID forces link to talk to Navi + if (this->actor.params & 0x8000) { + return (this->actor.params & 0xFF) + 0x100; + } else { + return -((this->actor.params & 0xFF) + 0x100); + } +} + +void ElfMsg_CallNaviCuboid(ElfMsg* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnElf* navi = (EnElf*)player->naviActor; + + if ((fabsf(player->actor.world.pos.x - this->actor.world.pos.x) < (100.0f * this->actor.scale.x)) && + (this->actor.world.pos.y <= player->actor.world.pos.y) && + ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y)) && + (fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))) { + player->naviTextId = ElfMsg_GetMessageId(this); + navi->elfMsg = this; + } +} + +s32 ElfMsg_WithinXZDistance(Vec3f* pos1, Vec3f* pos2, f32 distance) { + return (SQ(pos2->x - pos1->x) + SQ(pos2->z - pos1->z)) < SQ(distance); +} + +void ElfMsg_CallNaviCylinder(ElfMsg* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnElf* navi = (EnElf*)player->naviActor; + + if (ElfMsg_WithinXZDistance(&player->actor.world.pos, &this->actor.world.pos, this->actor.scale.x * 100.0f) && + (this->actor.world.pos.y <= player->actor.world.pos.y) && + ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y))) { + player->naviTextId = ElfMsg_GetMessageId(this); + navi->elfMsg = this; + } +} + +void ElfMsg_Update(Actor* thisx, GlobalContext* globalCtx) { + ElfMsg* this = (ElfMsg*)thisx; + + if (!ElfMsg_KillCheck(this, globalCtx)) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (((this->actor.params >> 8) & 0x3F) != 0x3F) { + Flags_SetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F); + } + Actor_Kill(&this->actor); + return; + } + if ((this->actor.world.rot.y <= 0x41) || (this->actor.world.rot.y > 0x80) || + Flags_GetSwitch(globalCtx, this->actor.world.rot.y - 0x41)) { + this->actionFunc(this, globalCtx); + } + } +} + +#include "overlays/ovl_Elf_Msg/ovl_Elf_Msg.h" + +void ElfMsg_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_elf_msg.c", 436); + + if (R_NAVI_MSG_REGION_ALPHA == 0) { + return; + } + + func_80093D18(globalCtx->state.gfxCtx); + if (thisx->params & 0x8000) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 100, 100, R_NAVI_MSG_REGION_ALPHA); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, R_NAVI_MSG_REGION_ALPHA); + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_elf_msg.c", 448), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_809AD278); + + if (thisx->params & 0x4000) { + gSPDisplayList(POLY_XLU_DISP++, sCubeDL); + } else { + gSPDisplayList(POLY_XLU_DISP++, sCylinderDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_elf_msg.c", 457); +} diff --git a/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.h b/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.h new file mode 100644 index 000000000..1e7fe65a6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.h @@ -0,0 +1,16 @@ +#ifndef Z_ELF_MSG_H +#define Z_ELF_MSG_H + +#include "ultra64.h" +#include "global.h" + +struct ElfMsg; + +typedef void (*ElfMsgActionFunc)(struct ElfMsg*, GlobalContext*); + +typedef struct ElfMsg { + /* 0x0000 */ Actor actor; + /* 0x014C */ ElfMsgActionFunc actionFunc; +} ElfMsg; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Elf_Msg2/z_elf_msg2.c b/soh/src/overlays/actors/ovl_Elf_Msg2/z_elf_msg2.c new file mode 100644 index 000000000..f23f49e10 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Elf_Msg2/z_elf_msg2.c @@ -0,0 +1,168 @@ +/* + * File: z_elf_msg2.c + * Overlay: ovl_Elf_Msg2 + * Description: Targetable Navi check spot + */ + +#include "z_elf_msg2.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void ElfMsg2_Init(Actor* thisx, GlobalContext* globalCtx); +void ElfMsg2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ElfMsg2_Update(Actor* thisx, GlobalContext* globalCtx); +void ElfMsg2_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 ElfMsg2_GetMessageId(ElfMsg2* this); +void ElfMsg2_WaitUntilActivated(ElfMsg2* this, GlobalContext* globalCtx); +void ElfMsg2_WaitForTextRead(ElfMsg2* this, GlobalContext* globalCtx); + +const ActorInit Elf_Msg2_InitVars = { + ACTOR_ELF_MSG2, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ElfMsg2), + (ActorFunc)ElfMsg2_Init, + (ActorFunc)ElfMsg2_Destroy, + (ActorFunc)ElfMsg2_Update, + (ActorFunc)ElfMsg2_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_STOP), +}; + +void ElfMsg2_SetupAction(ElfMsg2* this, ElfMsg2ActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +/** + * Checks a scene flag - if flag is set, the actor is killed and function returns 1. Otherwise returns 0. + * Can also set a switch flag from params while killing. + */ +s32 ElfMsg2_KillCheck(ElfMsg2* this, GlobalContext* globalCtx) { + if ((this->actor.world.rot.y > 0) && (this->actor.world.rot.y < 0x41) && + Flags_GetSwitch(globalCtx, this->actor.world.rot.y - 1)) { + LOG_STRING("共倒れ", "../z_elf_msg2.c", 171); // "Mutual destruction" + if (((this->actor.params >> 8) & 0x3F) != 0x3F) { + Flags_SetSwitch(globalCtx, ((this->actor.params >> 8) & 0x3F)); + } + Actor_Kill(&this->actor); + return 1; + } else if ((this->actor.world.rot.y == -1) && Flags_GetClear(globalCtx, this->actor.room)) { + LOG_STRING("共倒れ2", "../z_elf_msg2.c", 182); // "Mutual destruction 2" + if (((this->actor.params >> 8) & 0x3F) != 0x3F) { + Flags_SetSwitch(globalCtx, ((this->actor.params >> 8) & 0x3F)); + } + Actor_Kill(&this->actor); + return 1; + } else if (((this->actor.params >> 8) & 0x3F) == 0x3F) { + return 0; + } else if (Flags_GetSwitch(globalCtx, ((this->actor.params >> 8) & 0x3F))) { + LOG_STRING("共倒れ", "../z_elf_msg2.c", 192); // "Mutual destruction" + Actor_Kill(&this->actor); + return 1; + } + return 0; +} + +void ElfMsg2_Init(Actor* thisx, GlobalContext* globalCtx) { + ElfMsg2* this = (ElfMsg2*)thisx; + + osSyncPrintf(VT_FGCOL(CYAN) " Elf_Msg2_Actor_ct %04x\n\n" VT_RST, this->actor.params); + if (!ElfMsg2_KillCheck(this, globalCtx)) { + if ((this->actor.world.rot.x > 0) && (this->actor.world.rot.x < 8)) { + this->actor.targetMode = this->actor.world.rot.x - 1; + } + Actor_ProcessInitChain(thisx, sInitChain); + if (this->actor.world.rot.y >= 0x41) { + ElfMsg2_SetupAction(this, ElfMsg2_WaitUntilActivated); + } else { + ElfMsg2_SetupAction(this, ElfMsg2_WaitForTextRead); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_18; // Make actor targetable and Navi-checkable + this->actor.textId = ElfMsg2_GetMessageId(this); + } + this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = 0; + } +} + +void ElfMsg2_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +s32 ElfMsg2_GetMessageId(ElfMsg2* this) { + return (this->actor.params & 0xFF) + 0x100; +} + +/** + * Runs while Navi text is up. Kills the actor upon closing the text box unless rot.z == 1, can also set a switch flag + * from params. + */ +void ElfMsg2_WaitForTextClose(ElfMsg2* this, GlobalContext* globalCtx) { + s32 switchFlag; + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + if (this->actor.world.rot.z != 1) { + Actor_Kill(&this->actor); + switchFlag = (this->actor.params >> 8) & 0x3F; + if (switchFlag != 0x3F) { + Flags_SetSwitch(globalCtx, switchFlag); + } + } else { + ElfMsg2_SetupAction(this, ElfMsg2_WaitForTextRead); + } + } +} + +/** + * Runs while Navi text is not up. + */ +void ElfMsg2_WaitForTextRead(ElfMsg2* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + ElfMsg2_SetupAction(this, ElfMsg2_WaitForTextClose); + } +} + +/** + * Idles until a switch flag is set, at which point the actor becomes interactable + */ +void ElfMsg2_WaitUntilActivated(ElfMsg2* this, GlobalContext* globalCtx) { + // If (y >= 0x41) && (y <= 0x80), Idles until switch flag (actor.world.rot.y - 0x41) is set + // If (y > 0x80), Idles forever (Bug?) + if ((this->actor.world.rot.y >= 0x41) && (this->actor.world.rot.y <= 0x80) && + (Flags_GetSwitch(globalCtx, (this->actor.world.rot.y - 0x41)))) { + ElfMsg2_SetupAction(this, ElfMsg2_WaitForTextRead); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_18; // Make actor targetable and Navi-checkable + this->actor.textId = ElfMsg2_GetMessageId(this); + } +} + +void ElfMsg2_Update(Actor* thisx, GlobalContext* globalCtx) { + ElfMsg2* this = (ElfMsg2*)thisx; + + if (!ElfMsg2_KillCheck(this, globalCtx)) { + this->actionFunc(this, globalCtx); + } +} + +#include "overlays/ovl_Elf_Msg2/ovl_Elf_Msg2.h" + +void ElfMsg2_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_elf_msg2.c", 355); + + if (R_NAVI_MSG_REGION_ALPHA == 0) { + return; + } + + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 100, 100, 255, R_NAVI_MSG_REGION_ALPHA); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_elf_msg2.c", 362), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_809ADC38); + gSPDisplayList(POLY_XLU_DISP++, sCubeDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_elf_msg2.c", 367); +} diff --git a/soh/src/overlays/actors/ovl_Elf_Msg2/z_elf_msg2.h b/soh/src/overlays/actors/ovl_Elf_Msg2/z_elf_msg2.h new file mode 100644 index 000000000..741e72cbb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Elf_Msg2/z_elf_msg2.h @@ -0,0 +1,16 @@ +#ifndef Z_ELF_MSG2_H +#define Z_ELF_MSG2_H + +#include "ultra64.h" +#include "global.h" + +struct ElfMsg2; + +typedef void (*ElfMsg2ActionFunc)(struct ElfMsg2*, GlobalContext*); + +typedef struct ElfMsg2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ElfMsg2ActionFunc actionFunc; +} ElfMsg2; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Am/z_en_am.c b/soh/src/overlays/actors/ovl_En_Am/z_en_am.c new file mode 100644 index 000000000..32ea5d706 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Am/z_en_am.c @@ -0,0 +1,975 @@ +/* + * File: z_en_am.c + * Overlay: ovl_En_Am + * Description: Armos + */ + +#include "z_en_am.h" +#include "objects/object_am/object_am.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_26) + +void EnAm_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAm_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAm_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnAm_SetupStatue(EnAm* this); +void EnAm_SetupSleep(EnAm* this); +void EnAm_Statue(EnAm* this, GlobalContext* globalCtx); +void EnAm_Sleep(EnAm* this, GlobalContext* globalCtx); +void EnAm_Lunge(EnAm* this, GlobalContext* globalCtx); +void EnAm_RotateToHome(EnAm* this, GlobalContext* globalCtx); +void EnAm_MoveToHome(EnAm* this, GlobalContext* globalCtx); +void EnAm_RotateToInit(EnAm* this, GlobalContext* globalCtx); +void EnAm_Cooldown(EnAm* this, GlobalContext* globalCtx); +void EnAm_Ricochet(EnAm* this, GlobalContext* globalCtx); +void EnAm_Stunned(EnAm* this, GlobalContext* globalCtx); +void EnAm_RecoilFromDamage(EnAm* this, GlobalContext* globalCtx); + +typedef enum { + /* 00 */ AM_BEHAVIOR_NONE, + /* 01 */ AM_BEHAVIOR_DAMAGED, + /* 03 */ AM_BEHAVIOR_DO_NOTHING = 3, + /* 05 */ AM_BEHAVIOR_5 = 5, // checked but never set + /* 06 */ AM_BEHAVIOR_STUNNED, + /* 07 */ AM_BEHAVIOR_GO_HOME, + /* 08 */ AM_BEHAVIOR_RICOCHET, + /* 10 */ AM_BEHAVIOR_AGGRO = 10 +} ArmosBehavior; + +const ActorInit En_Am_InitVars = { + ACTOR_EN_AM, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_AM, + sizeof(EnAm), + (ActorFunc)EnAm_Init, + (ActorFunc)EnAm_Destroy, + (ActorFunc)EnAm_Update, + (ActorFunc)EnAm_Draw, + NULL, +}; + +static ColliderCylinderInit sHurtCylinderInit = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 15, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sBlockCylinderInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00400106, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 15, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +typedef enum { + /* 00 */ AM_DMGEFF_NONE, // used by anything that cant kill the armos + /* 01 */ AM_DMGEFF_NUT, + /* 06 */ AM_DMGEFF_STUN = 6, // doesnt include deku nuts + /* 13 */ AM_DMGEFF_ICE = 13, + /* 14 */ AM_DMGEFF_MAGIC_FIRE_LIGHT, + /* 15 */ AM_DMGEFF_KILL // any damage source that can kill the armos (and isnt a special case) +} ArmosDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, AM_DMGEFF_NUT), + /* Deku stick */ DMG_ENTRY(2, AM_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(1, AM_DMGEFF_NONE), + /* Explosive */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Boomerang */ DMG_ENTRY(0, AM_DMGEFF_STUN), + /* Normal arrow */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Hammer swing */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Hookshot */ DMG_ENTRY(0, AM_DMGEFF_STUN), + /* Kokiri sword */ DMG_ENTRY(1, AM_DMGEFF_NONE), + /* Master sword */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Giant's Knife */ DMG_ENTRY(4, AM_DMGEFF_KILL), + /* Fire arrow */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Ice arrow */ DMG_ENTRY(4, AM_DMGEFF_ICE), + /* Light arrow */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Unk arrow 1 */ DMG_ENTRY(2, AM_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(2, AM_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(2, AM_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(0, AM_DMGEFF_MAGIC_FIRE_LIGHT), + /* Ice magic */ DMG_ENTRY(3, AM_DMGEFF_ICE), + /* Light magic */ DMG_ENTRY(0, AM_DMGEFF_MAGIC_FIRE_LIGHT), + /* Shield */ DMG_ENTRY(0, AM_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, AM_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, AM_DMGEFF_NONE), + /* Giant spin */ DMG_ENTRY(4, AM_DMGEFF_KILL), + /* Master spin */ DMG_ENTRY(2, AM_DMGEFF_KILL), + /* Kokiri jump */ DMG_ENTRY(2, AM_DMGEFF_NONE), + /* Giant jump */ DMG_ENTRY(8, AM_DMGEFF_KILL), + /* Master jump */ DMG_ENTRY(4, AM_DMGEFF_KILL), + /* Unknown 1 */ DMG_ENTRY(0, AM_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, AM_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, AM_DMGEFF_KILL), + /* Unknown 2 */ DMG_ENTRY(0, AM_DMGEFF_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x13, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -4000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 5300, ICHAIN_STOP), +}; + +void EnAm_SetupAction(EnAm* this, EnAmActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +/** + * Checks bgCheckFlags in the direction of current yaw at the specified distance. + * + * Returns true if the armos would land on the ground in the resulting position. + * + * If it won't land on the ground, it will return true anyway if the floor the armos will be on + * is no more than 20 units lower than the home position. This prevents the armos from going down steep slopes. + */ +s32 EnAm_CanMove(EnAm* this, GlobalContext* globalCtx, f32 distance, s16 yaw) { + s16 ret; + s16 curBgCheckFlags; + f32 sin; + f32 cos; + Vec3f curPos; + + // save current position and bgCheckFlags to restore later + curPos = this->dyna.actor.world.pos; + curBgCheckFlags = this->dyna.actor.bgCheckFlags; + + sin = Math_SinS(yaw) * distance; + cos = Math_CosS(yaw) * distance; + + this->dyna.actor.world.pos.x += sin; + this->dyna.actor.world.pos.z += cos; + + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 0.0f, 0.0f, 0.0f, 4); + this->dyna.actor.world.pos = curPos; + ret = this->dyna.actor.bgCheckFlags & 1; + + if (!ret && (this->dyna.actor.floorHeight >= (this->dyna.actor.home.pos.y - 20.0f))) { + ret = true; + } + + this->dyna.actor.bgCheckFlags = curBgCheckFlags; + + return ret; +} + +void EnAm_Init(Actor* thisx, GlobalContext* globalCtx) { + CollisionHeader* colHeader = NULL; + s32 pad; + EnAm* this = (EnAm*)thisx; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + ActorShape_Init(&this->dyna.actor.shape, 0.0f, ActorShadow_DrawCircle, 48.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gArmosSkel, &gArmosRicochetAnim, this->jointTable, this->morphTable, + 14); + Actor_SetScale(&this->dyna.actor, 0.01f); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + Collider_InitCylinder(globalCtx, &this->hurtCollider); + Collider_InitCylinder(globalCtx, &this->blockCollider); + Collider_SetCylinder(globalCtx, &this->hurtCollider, &this->dyna.actor, &sHurtCylinderInit); + + if (this->dyna.actor.params == ARMOS_STATUE) { + this->dyna.actor.colChkInfo.health = 1; + Collider_SetCylinder(globalCtx, &this->blockCollider, &this->dyna.actor, &sHurtCylinderInit); + this->hurtCollider.base.ocFlags1 = (OC1_ON | OC1_NO_PUSH | OC1_TYPE_1 | OC1_TYPE_2); + this->blockCollider.base.ocFlags1 = (OC1_ON | OC1_NO_PUSH | OC1_TYPE_PLAYER); + CollisionHeader_GetVirtual(&gArmosCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->dyna.actor, ACTORCAT_BG); + EnAm_SetupStatue(this); + } else { + Collider_SetCylinder(globalCtx, &this->blockCollider, &this->dyna.actor, &sBlockCylinderInit); + Collider_InitQuad(globalCtx, &this->hitCollider); + Collider_SetQuad(globalCtx, &this->hitCollider, &this->dyna.actor, &sQuadInit); + this->dyna.actor.colChkInfo.health = 1; + this->dyna.actor.colChkInfo.damageTable = &sDamageTable; + EnAm_SetupSleep(this); + this->unk_258 = 0; + } + + this->dyna.actor.colChkInfo.mass = MASS_HEAVY; +} + +void EnAm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnAm* this = (EnAm*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->hurtCollider); + Collider_DestroyCylinder(globalCtx, &this->blockCollider); + //! @bug Quad collider is not destroyed (though destroy doesnt really do anything anyway) +} + +void EnAm_SpawnEffects(EnAm* this, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, -1.5f, 0.0f }; + static Vec3f accel = { 0.0f, -0.2f, 0.0f }; + s32 i; + Vec3f pos; + Color_RGBA8 primColor = { 100, 100, 100, 0 }; + Color_RGBA8 envColor = { 40, 40, 40, 0 }; + s32 pad; + + for (i = 4; i > 0; i--) { + pos.x = this->dyna.actor.world.pos.x + ((Rand_ZeroOne() - 0.5f) * 65.0f); + pos.y = (this->dyna.actor.world.pos.y + 40.0f) + ((Rand_ZeroOne() - 0.5f) * 10.0f); + pos.z = this->dyna.actor.world.pos.z + ((Rand_ZeroOne() - 0.5f) * 65.0f); + + EffectSsKiraKira_SpawnSmall(globalCtx, &pos, &velocity, &accel, &primColor, &envColor); + } + + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_AMOS_WALK); + Actor_SpawnFloorDustRing(globalCtx, &this->dyna.actor, &this->dyna.actor.world.pos, 4.0f, 3, 8.0f, 0x12C, 0xF, 0); +} + +void EnAm_SetupSleep(EnAm* this) { + f32 lastFrame = Animation_GetLastFrame(&gArmosRicochetAnim); + + Animation_Change(&this->skelAnime, &gArmosRicochetAnim, 0.0f, lastFrame, lastFrame, ANIMMODE_LOOP, 0.0f); + this->behavior = AM_BEHAVIOR_DO_NOTHING; + this->dyna.actor.speedXZ = 0.0f; + this->unk_258 = (this->textureBlend == 255) ? 0 : 1; + EnAm_SetupAction(this, EnAm_Sleep); +} + +void EnAm_SetupStatue(EnAm* this) { + f32 lastFrame = Animation_GetLastFrame(&gArmosRicochetAnim); + + Animation_Change(&this->skelAnime, &gArmosRicochetAnim, 0.0f, lastFrame, lastFrame, ANIMMODE_LOOP, 0.0f); + this->dyna.actor.flags &= ~ACTOR_FLAG_0; + this->behavior = AM_BEHAVIOR_DO_NOTHING; + this->dyna.actor.speedXZ = 0.0f; + EnAm_SetupAction(this, EnAm_Statue); +} + +void EnAm_SetupLunge(EnAm* this) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gArmosHopAnim, 4.0f); + this->unk_258 = 3; + this->behavior = AM_BEHAVIOR_AGGRO; + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.world.rot.y = this->dyna.actor.shape.rot.y; + EnAm_SetupAction(this, EnAm_Lunge); +} + +void EnAm_SetupCooldown(EnAm* this) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gArmosHopAnim, 4.0f); + this->unk_258 = 3; + this->cooldownTimer = 40; + this->behavior = AM_BEHAVIOR_AGGRO; + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.world.rot.y = this->dyna.actor.shape.rot.y; + EnAm_SetupAction(this, EnAm_Cooldown); +} + +void EnAm_SetupMoveToHome(EnAm* this) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gArmosHopAnim, 4.0f); + this->behavior = AM_BEHAVIOR_GO_HOME; + this->unk_258 = 1; + this->dyna.actor.speedXZ = 0.0f; + EnAm_SetupAction(this, EnAm_MoveToHome); +} + +void EnAm_SetupRotateToInit(EnAm* this) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gArmosHopAnim, 4.0f); + this->behavior = AM_BEHAVIOR_GO_HOME; + this->unk_258 = 1; + this->dyna.actor.speedXZ = 0.0f; + EnAm_SetupAction(this, EnAm_RotateToInit); +} + +void EnAm_SetupRotateToHome(EnAm* this) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gArmosHopAnim, 4.0f); + this->behavior = AM_BEHAVIOR_GO_HOME; + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.world.rot.y = this->dyna.actor.shape.rot.y; + EnAm_SetupAction(this, EnAm_RotateToHome); +} + +void EnAm_SetupRecoilFromDamage(EnAm* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gArmosDamagedAnim, 1.0f, 4.0f, + Animation_GetLastFrame(&gArmosDamagedAnim) - 6.0f, ANIMMODE_ONCE, 0.0f); + this->behavior = AM_BEHAVIOR_DAMAGED; + this->dyna.actor.world.rot.y = this->dyna.actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_AMOS_DAMAGE); + + if (EnAm_CanMove(this, globalCtx, -6.0f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = -6.0f; + } + + this->dyna.actor.colorFilterTimer = 0; + Enemy_StartFinishingBlow(globalCtx, &this->dyna.actor); + EnAm_SetupAction(this, EnAm_RecoilFromDamage); +} + +void EnAm_SetupRicochet(EnAm* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gArmosRicochetAnim, 1.0f, 0.0f, 8.0f, ANIMMODE_ONCE, 0.0f); + this->dyna.actor.world.rot.y = this->dyna.actor.yawTowardsPlayer; + + if (EnAm_CanMove(this, globalCtx, -6.0f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = -6.0f; + } + + this->unk_264 = 0; + this->unk_258 = 0; + this->cooldownTimer = 5; + this->behavior = AM_BEHAVIOR_RICOCHET; + EnAm_SetupAction(this, EnAm_Ricochet); +} + +void EnAm_Sleep(EnAm* this, GlobalContext* globalCtx) { + f32 cos; + s32 pad; + f32 rand; + f32 sin; + Player* player = GET_PLAYER(globalCtx); + + if ((this->unk_258 != 0) || + ((this->hurtCollider.base.ocFlags1 & OC1_HIT) && (this->hurtCollider.base.oc == &player->actor)) || + (this->hurtCollider.base.acFlags & AC_HIT)) { + this->hurtCollider.base.acFlags &= ~AC_HIT; + + if (this->textureBlend == 0) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_AMOS_WAVE); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_AMOS_VOICE); + Actor_SetColorFilter(&this->dyna.actor, 0x4000, 255, 0, 8); + } + + if (this->textureBlend >= 240) { + this->attackTimer = 200; + this->textureBlend = 255; + this->dyna.actor.flags |= ACTOR_FLAG_0; + this->dyna.actor.shape.yOffset = 0.0f; + EnAm_SetupLunge(this); + } else { + // shake randomly about the origin while waking up + rand = (Rand_ZeroOne() - 0.5f) * 10.0f; + + cos = Math_CosS(this->dyna.actor.shape.rot.y) * rand; + sin = Math_SinS(this->dyna.actor.shape.rot.y) * rand; + + this->dyna.actor.world.pos.x = this->shakeOrigin.x + cos; + this->dyna.actor.world.pos.z = this->shakeOrigin.z + sin; + + this->textureBlend += 20; + this->unk_258 = 1; + } + } else { + if (this->textureBlend > 10) { + this->textureBlend -= 10; + } else { + this->textureBlend = 0; + this->dyna.actor.flags &= ~ACTOR_FLAG_0; + + if (this->dyna.bgId < 0) { + this->unk_264 = 0; + } + + this->dyna.actor.speedXZ += this->dyna.unk_150; + this->shakeOrigin = this->dyna.actor.world.pos; + this->dyna.actor.world.rot.y = this->dyna.unk_158; + this->dyna.actor.speedXZ = CLAMP(this->dyna.actor.speedXZ, -2.5f, 2.5f); + Math_SmoothStepToF(&this->dyna.actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + + if (this->dyna.actor.speedXZ != 0.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); + } + + this->dyna.unk_154 = 0.0f; + this->dyna.unk_150 = 0.0f; + } + } +} + +/** + * Spin toward the direction of the home position to start moving back to it. + */ +void EnAm_RotateToHome(EnAm* this, GlobalContext* globalCtx) { + s16 yawToHome = Math_Vec3f_Yaw(&this->dyna.actor.world.pos, &this->dyna.actor.home.pos); + + if (this->skelAnime.curFrame == 8.0f) { + Math_SmoothStepToS(&this->dyna.actor.world.rot.y, yawToHome, 1, 0x1F40, 0); + this->dyna.actor.velocity.y = 12.0f; + } else if (this->skelAnime.curFrame > 11.0f) { + if (!(this->dyna.actor.bgCheckFlags & 1)) { + this->skelAnime.curFrame = 11; + } else { + EnAm_SpawnEffects(this, globalCtx); + + if (this->dyna.actor.world.rot.y == yawToHome) { + this->unk_258 = 0; + } + + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + } + } + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_258 == 0) { + EnAm_SetupMoveToHome(this); + } + + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; +} + +/** + * After reaching the home position, spin back to the starting rotation. + */ +void EnAm_RotateToInit(EnAm* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame == 8.0f) { + if ((this->dyna.actor.world.pos.x == this->dyna.actor.home.pos.x) && + (this->dyna.actor.world.pos.z == this->dyna.actor.home.pos.z)) { + Math_SmoothStepToS(&this->dyna.actor.world.rot.y, this->dyna.actor.home.rot.y, 1, 0x1F40, 0); + } + this->unk_258 = 2; + this->dyna.actor.velocity.y = 12.0f; + } else if (this->skelAnime.curFrame > 11.0f) { + if (!(this->dyna.actor.bgCheckFlags & 1)) { + this->skelAnime.curFrame = 11; + } else { + this->unk_258 = 1; + EnAm_SpawnEffects(this, globalCtx); + + if (this->dyna.actor.home.rot.y == this->dyna.actor.world.rot.y) { + this->unk_258 = 0; + } + + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + } + } + + if (this->unk_258 == 2) { + Math_SmoothStepToF(&this->dyna.actor.world.pos.x, this->dyna.actor.home.pos.x, 1.0f, 8.0f, 0.0f); + Math_SmoothStepToF(&this->dyna.actor.world.pos.z, this->dyna.actor.home.pos.z, 1.0f, 8.0f, 0.0f); + } + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_258 == 0) { + EnAm_SetupSleep(this); + } + + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; +} + +void EnAm_MoveToHome(EnAm* this, GlobalContext* globalCtx) { + s16 yawToHome = Math_Vec3f_Yaw(&this->dyna.actor.world.pos, &this->dyna.actor.home.pos); + + if (this->skelAnime.curFrame == 8.0f) { + this->dyna.actor.velocity.y = 12.0f; + this->dyna.actor.speedXZ = 6.0f; + } else if (this->skelAnime.curFrame > 11.0f) { + if (!(this->dyna.actor.bgCheckFlags & 1)) { + this->skelAnime.curFrame = 11; + } else { + Math_SmoothStepToS(&this->dyna.actor.world.rot.y, yawToHome, 1, 0xBB8, 0); + + if (this->dyna.actor.bgCheckFlags & 2) { + this->unk_258--; + } + + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + EnAm_SpawnEffects(this, globalCtx); + + if (Actor_WorldDistXYZToPoint(&this->dyna.actor, &this->dyna.actor.home.pos) < 80.0f) { + EnAm_SetupRotateToInit(this); + } + } + } + + // turn away from a wall if touching one + if ((this->dyna.actor.speedXZ != 0.0f) && (this->dyna.actor.bgCheckFlags & 8)) { + this->dyna.actor.world.rot.y = this->dyna.actor.wallYaw; + Actor_MoveForward(&this->dyna.actor); + } + + SkelAnime_Update(&this->skelAnime); + + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; +} + +void EnAm_RecoilFromDamage(EnAm* this, GlobalContext* globalCtx) { + if (this->dyna.actor.speedXZ < 0.0f) { + this->dyna.actor.speedXZ += 0.5f; + } + + if ((this->dyna.actor.velocity.y <= 0.0f) && !EnAm_CanMove(this, globalCtx, -8.0f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skelAnime)) { + EnAm_SetupLunge(this); + this->deathTimer = 64; + this->panicSpinRot = 0; + } +} + +/** + * After doing 3 lunges, wait for 2 seconds before attacking again. + * Turn toward the player before lunging again. + */ +void EnAm_Cooldown(EnAm* this, GlobalContext* globalCtx) { + s16 yawDiff = this->dyna.actor.yawTowardsPlayer - this->dyna.actor.world.rot.y; + + yawDiff = ABS(yawDiff); + + if (this->cooldownTimer != 0) { + this->cooldownTimer--; + } else { + if (this->skelAnime.curFrame == 8.0f) { + Math_SmoothStepToS(&this->dyna.actor.world.rot.y, this->dyna.actor.yawTowardsPlayer, 1, 0x1F40, 0); + this->dyna.actor.velocity.y = 12.0f; + } else if (this->skelAnime.curFrame > 11.0f) { + if (!(this->dyna.actor.bgCheckFlags & 1)) { + this->skelAnime.curFrame = 11; + } else { + if (yawDiff < 3500) { + this->unk_258 = 0; + } + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + EnAm_SpawnEffects(this, globalCtx); + } + } + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_258 == 0) { + EnAm_SetupLunge(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_AMOS_VOICE); + } + + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; + } +} + +/** + * Lunge toward the player in an attempt to deal damage. Hop 3 times. + * Used for both normal attacks and the death sequence. + */ +void EnAm_Lunge(EnAm* this, GlobalContext* globalCtx) { + if (this->deathTimer < 52) { + if (this->skelAnime.curFrame == 8.0f) { + this->dyna.actor.velocity.y = 12.0f; + + if (EnAm_CanMove(this, globalCtx, 80.0f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = 6.0f; + } else { + this->dyna.actor.speedXZ = 0.0f; + } + + this->unk_264 = 1; + this->hitCollider.base.atFlags &= ~(AT_HIT | AT_BOUNCED); + } else if (this->skelAnime.curFrame > 11.0f) { + if (!(this->dyna.actor.bgCheckFlags & 1)) { + this->skelAnime.curFrame = 11; + } else { + Math_SmoothStepToS(&this->dyna.actor.world.rot.y, this->dyna.actor.yawTowardsPlayer, 1, 0x1770, 0); + + if (this->dyna.actor.bgCheckFlags & 2) { + this->unk_258--; + } + + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.speedXZ = 0.0f; + this->unk_264 = 0; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + EnAm_SpawnEffects(this, globalCtx); + + if (((Actor_WorldDistXZToPoint(&this->dyna.actor, &this->dyna.actor.home.pos) > 240.0f) || + (this->attackTimer == 0)) && + (this->deathTimer == 0)) { + EnAm_SetupRotateToHome(this); + } + } + } + + // turn and move away from a wall if contact is made with one + if ((this->dyna.actor.speedXZ != 0.0f) && (this->dyna.actor.bgCheckFlags & 8)) { + this->dyna.actor.world.rot.y = + (this->dyna.actor.wallYaw - this->dyna.actor.world.rot.y) + this->dyna.actor.wallYaw; + Actor_MoveForward(&this->dyna.actor); + this->dyna.actor.bgCheckFlags &= ~8; + } + + SkelAnime_Update(&this->skelAnime); + + if ((this->unk_258 == 0) && (this->deathTimer == 0)) { + EnAm_SetupCooldown(this); + } + + if (this->deathTimer == 0) { + this->dyna.actor.shape.rot.y = this->dyna.actor.world.rot.y; + } else { + if (this->panicSpinRot < 8000) { + this->panicSpinRot += 800; + } + this->dyna.actor.shape.rot.y += this->panicSpinRot; + } + } +} + +void EnAm_Statue(EnAm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 temp158f = this->dyna.unk_158; + s16 moveDir = 0; + + if (this->unk_258 == 0) { + if (this->dyna.unk_150 != 0.0f) { + this->unk_258 = 0x8000; + } + } else { + this->unk_258 -= 0x800; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); + + if (this->dyna.unk_150 < 0.0f) { + temp158f = this->dyna.unk_158 + 0x8000; + } + + if (this->hurtCollider.base.ocFlags1 & OC1_HIT) { + moveDir = Math_Vec3f_Yaw(&this->dyna.actor.world.pos, &this->hurtCollider.base.oc->world.pos) - temp158f; + } + + if ((this->dyna.unk_150 == 0.0f) || (this->unk_258 == 0) || !(this->dyna.actor.bgCheckFlags & 1) || + !func_800435D8(globalCtx, &this->dyna, 0x14, + (Math_SinS(this->unk_258) * (this->dyna.unk_150 * 0.5f)) + 40.0f, 0xA) || + ((this->hurtCollider.base.ocFlags1 & OC1_HIT) && (ABS(moveDir) <= 0x2000))) { + + this->unk_258 = 0; + player->stateFlags2 &= ~0x151; + player->actor.speedXZ = 0.0f; + this->dyna.unk_150 = this->dyna.unk_154 = 0.0f; + } + + this->dyna.actor.world.rot.y = this->dyna.unk_158; + this->dyna.actor.speedXZ = Math_SinS(this->unk_258) * (this->dyna.unk_150 * 0.5f); + } + + if (this->dyna.actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + } + + this->dyna.unk_150 = this->dyna.unk_154 = 0.0f; +} + +void EnAm_SetupStunned(EnAm* this, GlobalContext* globalCtx) { + // animation is set but SkelAnime_Update is not called in the action + // likely copy pasted from EnAm_SetupRecoilFromDamage + Animation_Change(&this->skelAnime, &gArmosDamagedAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gArmosDamagedAnim), + ANIMMODE_ONCE, 0.0f); + + this->dyna.actor.world.rot.y = this->dyna.actor.yawTowardsPlayer; + + if (EnAm_CanMove(this, globalCtx, -6.0f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = -6.0f; + } + + Actor_SetColorFilter(&this->dyna.actor, 0, 120, 0, 100); + + if (this->damageEffect == AM_DMGEFF_ICE) { + this->iceTimer = 48; + } + + this->behavior = AM_BEHAVIOR_STUNNED; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_GOMA_JR_FREEZE); + EnAm_SetupAction(this, EnAm_Stunned); +} + +void EnAm_Stunned(EnAm* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->dyna.actor.shape.rot.y, this->dyna.actor.world.rot.y, 1, 0xFA0, 0); + + if (this->dyna.actor.speedXZ < 0.0f) { + this->dyna.actor.speedXZ += 0.5f; + } + + if ((this->dyna.actor.velocity.y <= 0.0f) && !EnAm_CanMove(this, globalCtx, -9.0f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = 0.0f; + } + + if (this->dyna.actor.colorFilterTimer == 0) { + if (this->dyna.actor.colChkInfo.health != 0) { + EnAm_SetupLunge(this); + } else { + EnAm_SetupRecoilFromDamage(this, globalCtx); + } + } +} + +void EnAm_Ricochet(EnAm* this, GlobalContext* globalCtx) { + if (this->dyna.actor.speedXZ < 0.0f) { + this->dyna.actor.speedXZ += 0.5f; + } + + if ((this->dyna.actor.velocity.y <= 0.0f) && + !EnAm_CanMove(this, globalCtx, this->dyna.actor.speedXZ * 1.5f, this->dyna.actor.world.rot.y)) { + this->dyna.actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skelAnime)) { + this->dyna.actor.speedXZ = 0.0f; + EnAm_SetupLunge(this); + } +} + +void EnAm_TransformSwordHitbox(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f D_809B0074 = { 2500.0f, 7000.0f, 0.0f }; + static Vec3f D_809B0080 = { -2500.0f, 0.0f, 0.0f }; + static Vec3f D_809B008C = { 2500.0f, 7000.0f, 4000.0f }; + static Vec3f D_809B0098 = { -2500.0f, 0.0f, 4000.0f }; + EnAm* this = (EnAm*)thisx; + + Matrix_MultVec3f(&D_809B0074, &this->hitCollider.dim.quad[1]); + Matrix_MultVec3f(&D_809B0080, &this->hitCollider.dim.quad[0]); + Matrix_MultVec3f(&D_809B008C, &this->hitCollider.dim.quad[3]); + Matrix_MultVec3f(&D_809B0098, &this->hitCollider.dim.quad[2]); + + Collider_SetQuadVertices(&this->hitCollider, &this->hitCollider.dim.quad[0], &this->hitCollider.dim.quad[1], + &this->hitCollider.dim.quad[2], &this->hitCollider.dim.quad[3]); +} + +void EnAm_UpdateDamage(EnAm* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f sparkPos; + + if (this->deathTimer == 0) { + if (this->blockCollider.base.acFlags & AC_BOUNCED) { + this->blockCollider.base.acFlags &= ~(AC_HIT | AC_BOUNCED); + this->hurtCollider.base.acFlags &= ~AC_HIT; + + if (this->behavior >= AM_BEHAVIOR_5) { + EnAm_SetupRicochet(this, globalCtx); + } + } else if ((this->hurtCollider.base.acFlags & AC_HIT) && (this->behavior >= AM_BEHAVIOR_5)) { + this->hurtCollider.base.acFlags &= ~AC_HIT; + + if (this->dyna.actor.colChkInfo.damageEffect != AM_DMGEFF_MAGIC_FIRE_LIGHT) { + this->unk_264 = 0; + this->damageEffect = this->dyna.actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->dyna.actor, &this->hurtCollider.info, 0); + + if ((this->dyna.actor.colChkInfo.damageEffect == AM_DMGEFF_NUT) || + (this->dyna.actor.colChkInfo.damageEffect == AM_DMGEFF_STUN) || + (this->dyna.actor.colChkInfo.damageEffect == AM_DMGEFF_ICE)) { + if (this->behavior != AM_BEHAVIOR_STUNNED) { + EnAm_SetupStunned(this, globalCtx); + + if (this->dyna.actor.colChkInfo.damage != 0) { + this->dyna.actor.colChkInfo.health = 0; + } + } else if (this->dyna.actor.colChkInfo.damageEffect == AM_DMGEFF_STUN) { + sparkPos = this->dyna.actor.world.pos; + sparkPos.y += 50.0f; + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, &sparkPos); + } + } else if ((this->dyna.actor.colChkInfo.damageEffect == AM_DMGEFF_KILL) || + (this->behavior == AM_BEHAVIOR_STUNNED)) { + this->dyna.actor.colChkInfo.health = 0; + + EnAm_SetupRecoilFromDamage(this, globalCtx); + } else { + EnAm_SetupRicochet(this, globalCtx); + } + } + } + } +} + +void EnAm_Update(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 dustPrimColor = { 150, 150, 150, 255 }; + static Color_RGBA8 dustEnvColor = { 100, 100, 100, 150 }; + s32 pad; + EnAm* this = (EnAm*)thisx; + EnBom* bomb; + Vec3f dustPos; + s32 i; + f32 dustPosScale; + s32 pad1; + + if (this->dyna.actor.params != ARMOS_STATUE) { + EnAm_UpdateDamage(this, globalCtx); + } + + if (this->dyna.actor.colChkInfo.damageEffect != AM_DMGEFF_MAGIC_FIRE_LIGHT) { + if (this->attackTimer != 0) { + this->attackTimer--; + } + + this->actionFunc(this, globalCtx); + + if (this->deathTimer != 0) { + this->deathTimer--; + + if (this->deathTimer == 0) { + dustPosScale = globalCtx->gameplayFrames * 10; + + EnAm_SpawnEffects(this, globalCtx); + bomb = + (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, 0, 2, BOMB_BODY); + if (bomb != NULL) { + bomb->timer = 0; + } + + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_AMOS_DEAD); + Item_DropCollectibleRandom(globalCtx, &this->dyna.actor, &this->dyna.actor.world.pos, 0xA0); + + for (i = 9; i >= 0; i--) { + dustPos.x = (sinf(dustPosScale) * 7.0f) + this->dyna.actor.world.pos.x; + dustPos.y = (Rand_CenteredFloat(10.0f) * 6.0f) + (this->dyna.actor.world.pos.y + 40.0f); + dustPos.z = (cosf(dustPosScale) * 7.0f) + this->dyna.actor.world.pos.z; + + func_8002836C(globalCtx, &dustPos, &zeroVec, &zeroVec, &dustPrimColor, &dustEnvColor, 200, 45, 12); + dustPosScale += 60.0f; + } + + Actor_Kill(&this->dyna.actor); + return; + } + + if ((this->deathTimer % 4) == 0) { + Actor_SetColorFilter(&this->dyna.actor, 0x4000, 255, 0, 4); + } + } + + Actor_MoveForward(&this->dyna.actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 20.0f, 28.0f, 80.0f, 0x1D); + } + + Collider_UpdateCylinder(&this->dyna.actor, &this->hurtCollider); + Collider_UpdateCylinder(&this->dyna.actor, &this->blockCollider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->hurtCollider.base); + + if (this->dyna.actor.params != ARMOS_STATUE) { + Actor_SetFocus(&this->dyna.actor, this->dyna.actor.scale.x * 4500.0f); + + if (this->dyna.actor.colorFilterTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->blockCollider.base); + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->hurtCollider.base); + + if ((this->behavior >= 4) && (this->unk_264 > 0)) { + if (!(this->hitCollider.base.atFlags & AT_BOUNCED)) { + if (this->hitCollider.base.atFlags & AT_HIT) { + Player* player = GET_PLAYER(globalCtx); + + if (this->hitCollider.base.at == &player->actor) { + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->hitCollider.base); + } else { + this->hitCollider.base.atFlags &= ~(AT_HIT | AT_BOUNCED); + this->hitCollider.base.at = NULL; + EnAm_SetupRicochet(this, globalCtx); + } + } + } else { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->blockCollider.base); + } +} + +static Vec3f sUnused1 = { 1100.0f, -700.0f, 0.0f }; +static Vec3f sUnused2 = { 0.0f, 0.0f, 0.0f }; + +void EnAm_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnAm* this = (EnAm*)thisx; + + if ((limbIndex == 1) && (this->unk_264 != 0)) { + EnAm_TransformSwordHitbox(&this->dyna.actor, globalCtx); + } +} + +static Vec3f sIcePosOffsets[] = { + { 20.0f, 40.0f, 0.0f }, { 10.0f, 60.0f, 10.0f }, { -10.0f, 60.0f, 10.0f }, { -20.0f, 40.0f, 0.0f }, + { 10.0f, 60.0f, -10.0f }, { -10.0f, 60.0f, -10.0f }, { 0.0f, 40.0f, -20.0f }, { 10.0f, 20.0f, 10.0f }, + { 10.0f, 20.0f, -10.0f }, { 0.0f, 40.0f, 20.0f }, { -10.0f, 20.0f, 10.0f }, { -10.0f, 20.0f, -10.0f }, +}; + +void EnAm_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + Vec3f sp68; + EnAm* this = (EnAm*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_am.c", 1580); + + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->textureBlend); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, EnAm_PostLimbDraw, this); + + if (this->iceTimer != 0) { + this->dyna.actor.colorFilterTimer++; + if (1) {}; + this->iceTimer--; + + if ((this->iceTimer % 4) == 0) { + s32 index; + + index = this->iceTimer >> 2; + + sp68.x = this->dyna.actor.world.pos.x + sIcePosOffsets[index].x; + sp68.y = this->dyna.actor.world.pos.y + sIcePosOffsets[index].y; + sp68.z = this->dyna.actor.world.pos.z + sIcePosOffsets[index].z; + + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->dyna.actor, &sp68, 150, 150, 150, 250, 235, 245, 255, + 1.4f); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_am.c", 1605); +} diff --git a/soh/src/overlays/actors/ovl_En_Am/z_en_am.h b/soh/src/overlays/actors/ovl_En_Am/z_en_am.h new file mode 100644 index 000000000..6cb4c40ec --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Am/z_en_am.h @@ -0,0 +1,38 @@ +#ifndef Z_EN_AM_H +#define Z_EN_AM_H + +#include "ultra64.h" +#include "global.h" + +struct EnAm; + +typedef void (*EnAmActionFunc)(struct EnAm*, GlobalContext*); + +typedef struct EnAm { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ SkelAnime skelAnime; + /* 0x01A8 */ s32 behavior; + /* 0x01AC */ Vec3s jointTable[14]; + /* 0x0200 */ Vec3s morphTable[14]; + /* 0x0254 */ EnAmActionFunc actionFunc; + /* 0x0258 */ s16 unk_258; + /* 0x025A */ s16 cooldownTimer; + /* 0x025C */ s16 attackTimer; // start going back home after reaching 0 + /* 0x025E */ s16 iceTimer; + /* 0x0260 */ s16 deathTimer; // explode after reaching 0 + /* 0x0262 */ s16 panicSpinRot; // used when panicking before death + /* 0x0264 */ s16 unk_264; + /* 0x0266 */ u8 textureBlend; // 0 = statue textures; 255 = enemy textures + /* 0x0267 */ u8 damageEffect; + /* 0x0267 */ Vec3f shakeOrigin; // center point to shake around when waking up + /* 0x0274 */ ColliderCylinder hurtCollider; + /* 0x02C0 */ ColliderCylinder blockCollider; + /* 0x030C */ ColliderQuad hitCollider; +} EnAm; // size = 0x038C + +typedef enum { + /* 0 */ ARMOS_STATUE, + /* 1 */ ARMOS_ENEMY +} ArmosType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c b/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c new file mode 100644 index 000000000..40b26c790 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.c @@ -0,0 +1,332 @@ +/* + * File: z_en_ani.c + * Overlay: ovl_En_Ani + * Description: Kakariko Roof Guy + */ + +#include "z_en_ani.h" +#include "objects/object_ani/object_ani.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnAni_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAni_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAni_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAni_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnAni_SetText(EnAni* this, GlobalContext* globalCtx, u16 textId); +void func_809B04F0(EnAni* this, GlobalContext* globalCtx); +void func_809B0524(EnAni* this, GlobalContext* globalCtx); +void func_809B0558(EnAni* this, GlobalContext* globalCtx); +void func_809B05F0(EnAni* this, GlobalContext* globalCtx); +void func_809B064C(EnAni* this, GlobalContext* globalCtx); +void func_809B07F8(EnAni* this, GlobalContext* globalCtx); +void func_809B0988(EnAni* this, GlobalContext* globalCtx); +void func_809B0994(EnAni* this, GlobalContext* globalCtx); +void func_809B0A28(EnAni* this, GlobalContext* globalCtx); +void func_809B0A6C(EnAni* this, GlobalContext* globalCtx); + +const ActorInit En_Ani_InitVars = { + ACTOR_EN_ANI, + ACTORCAT_NPC, + FLAGS, + OBJECT_ANI, + sizeof(EnAni), + (ActorFunc)EnAni_Init, + (ActorFunc)EnAni_Destroy, + (ActorFunc)EnAni_Update, + (ActorFunc)EnAni_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0 } }, +}; + +void EnAni_SetupAction(EnAni* this, EnAniActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 850, ICHAIN_STOP), +}; + +void EnAni_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnAni* this = (EnAni*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, -2800.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gRoofManSkel, &gRoofManIdleAnim, this->jointTable, + this->morphTable, 0x10); + Animation_PlayOnce(&this->skelAnime, &gRoofManIdleAnim); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + if (!LINK_IS_ADULT) { + EnAni_SetupAction(this, func_809B064C); + } else { + EnAni_SetupAction(this, func_809B07F8); + } + this->unk_2AA = 0; + this->unk_2A8 = 0; + this->actor.minVelocityY = -1.0f; + this->actor.velocity.y = -1.0f; +} + +void EnAni_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnAni* this = (EnAni*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnAni_SetText(EnAni* this, GlobalContext* globalCtx, u16 textId) { + this->actor.textId = textId; + this->unk_2A8 |= 1; + func_8002F2CC(&this->actor, globalCtx, 100.0f); + return 0; +} + +void func_809B04F0(EnAni* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + EnAni_SetupAction(this, func_809B064C); + } +} + +void func_809B0524(EnAni* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + EnAni_SetupAction(this, func_809B07F8); + } +} + +void func_809B0558(EnAni* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + if (!LINK_IS_ADULT) { + EnAni_SetupAction(this, func_809B04F0); + } else { + EnAni_SetupAction(this, func_809B0524); + } + gSaveContext.itemGetInf[1] |= 0x20; + } else { + func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, 10000.0f, 200.0f); + } +} + +void func_809B05F0(EnAni* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + EnAni_SetupAction(this, func_809B0558); + } + func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, 10000.0f, 200.0f); +} + +void func_809B064C(EnAni* this, GlobalContext* globalCtx) { + u16 textId; + s16 yawDiff; + u16 textId2; + + textId2 = Text_GetFaceReaction(globalCtx, 0xA); + textId = textId2 & 0xFFFF; + + if (!textId) {} + + if (textId2 == 0) { + textId = !IS_DAY ? 0x5051 : 0x5050; + } + + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->actor.textId == 0x5056) { + EnAni_SetupAction(this, func_809B04F0); + } else if (this->actor.textId == 0x5055) { + EnAni_SetupAction(this, func_809B05F0); + } else { + EnAni_SetupAction(this, func_809B04F0); + } + } else if (yawDiff >= -0x36AF && yawDiff < 0 && this->actor.xzDistToPlayer < 150.0f && + -80.0f < this->actor.yDistToPlayer) { + if (gSaveContext.itemGetInf[1] & 0x20) { + EnAni_SetText(this, globalCtx, 0x5056); + } else { + EnAni_SetText(this, globalCtx, 0x5055); + } + } else if (yawDiff >= -0x3E7 && yawDiff < 0x36B0 && this->actor.xzDistToPlayer < 350.0f) { + EnAni_SetText(this, globalCtx, textId); + } +} + +void func_809B07F8(EnAni* this, GlobalContext* globalCtx) { + s16 pad; + s16 yawDiff; + u16 textId; + + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->actor.textId == 0x5056) { + EnAni_SetupAction(this, func_809B0524); + } else if (this->actor.textId == 0x5055) { + EnAni_SetupAction(this, func_809B05F0); + } else { + EnAni_SetupAction(this, func_809B0524); + } + } else if (yawDiff > -0x36B0 && yawDiff < 0 && this->actor.xzDistToPlayer < 150.0f && + -80.0f < this->actor.yDistToPlayer) { + if (gSaveContext.itemGetInf[1] & 0x20) { + EnAni_SetText(this, globalCtx, 0x5056); + } else { + EnAni_SetText(this, globalCtx, 0x5055); + } + } else if (yawDiff > -0x3E8 && yawDiff < 0x36B0 && this->actor.xzDistToPlayer < 350.0f) { + if (!(gSaveContext.eventChkInf[2] & 0x8000)) { + textId = 0x5052; + } else { + textId = (gSaveContext.itemGetInf[1] & 0x20) ? 0x5054 : 0x5053; + } + EnAni_SetText(this, globalCtx, textId); + } +} + +void func_809B0988(EnAni* this, GlobalContext* globalCtx) { +} + +void func_809B0994(EnAni* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.npcActions[0]->action == 4) { + Animation_Change(&this->skelAnime, &gRoofManGettingUpAfterKnockbackAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gRoofManGettingUpAfterKnockbackAnim), ANIMMODE_ONCE, -4.0f); + this->unk_2AA++; + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + } +} + +void func_809B0A28(EnAni* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_2AA++; + } +} + +void func_809B0A6C(EnAni* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->skelAnime.curFrame = 0.0f; + } + if (globalCtx->csCtx.npcActions[0]->action == 2) { + Animation_Change(&this->skelAnime, &gRoofManKnockbackAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gRoofManKnockbackAnim), ANIMMODE_ONCE, 0.0f); + this->actor.shape.shadowDraw = NULL; + this->unk_2AA++; + } +} + +void EnAni_Update(Actor* thisx, GlobalContext* globalCtx) { + EnAni* this = (EnAni*)thisx; + s32 pad[2]; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[0] != NULL)) { + switch (this->unk_2AA) { + case 0: + func_809B0A6C(this, globalCtx); + break; + case 1: + func_809B0A28(this, globalCtx); + break; + case 2: + func_809B0994(this, globalCtx); + break; + case 3: + func_809B0A28(this, globalCtx); + break; + case 4: + func_809B0988(this, globalCtx); + break; + } + + if (globalCtx->csCtx.frames == 100) { + func_800788CC(NA_SE_IT_EARTHQUAKE); + } + } else { + if (SkelAnime_Update(&this->skelAnime) != 0) { + this->skelAnime.curFrame = 0.0f; + } + this->actionFunc(this, globalCtx); + } + + if (this->unk_2A8 & 1) { + func_80038290(globalCtx, &this->actor, &this->unk_29C, &this->unk_2A2, this->actor.focus.pos); + this->unk_2A2.z = 0; + this->unk_2A2.y = this->unk_2A2.z; + this->unk_2A2.x = this->unk_2A2.z; + } else { + Math_SmoothStepToS(&this->unk_29C.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_29C.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.y, 0, 6, 6200, 100); + } + + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + this->eyeIndex = this->blinkTimer; + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + } +} + +s32 EnAni_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnAni* this = (EnAni*)thisx; + + if (limbIndex == 15) { + rot->x += this->unk_29C.y; + rot->z += this->unk_29C.x; + } + return false; +} + +void EnAni_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f sMultVec = { 800.0f, 500.0f, 0.0f }; + EnAni* this = (EnAni*)thisx; + + if (limbIndex == 15) { + Matrix_MultVec3f(&sMultVec, &this->actor.focus.pos); + } +} + +void EnAni_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gRoofManEyeOpenTex, + gRoofManEyeHalfTex, + gRoofManEyeClosedTex, + }; + EnAni* this = (EnAni*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ani.c", 719); + + func_800943C8(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnAni_OverrideLimbDraw, EnAni_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ani.c", 736); +} diff --git a/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.h b/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.h new file mode 100644 index 000000000..6ab185cb8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ani/z_en_ani.h @@ -0,0 +1,26 @@ +#ifndef Z_EN_ANI_H +#define Z_EN_ANI_H + +#include "ultra64.h" +#include "global.h" + +struct EnAni; + +typedef void (*EnAniActionFunc)(struct EnAni*, GlobalContext*); + +typedef struct EnAni { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[16]; + /* 0x023C */ Vec3s morphTable[16]; + /* 0x029C */ Vec3s unk_29C; + /* 0x02A2 */ Vec3s unk_2A2; + /* 0x02A8 */ u16 unk_2A8; + /* 0x02AA */ u16 unk_2AA; + /* 0x02AC */ s16 eyeIndex; + /* 0x02AE */ s16 blinkTimer; + /* 0x02B0 */ EnAniActionFunc actionFunc; +} EnAni; // size = 0x02B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c b/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c new file mode 100644 index 000000000..45f7117ec --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.c @@ -0,0 +1,498 @@ +/* + * File: z_en_anubice.c + * Overlay: ovl_En_Anubice + * Description: Anubis Body + */ + +#include "z_en_anubice.h" +#include "objects/object_anubice/object_anubice.h" +#include "overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.h" +#include "overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnAnubice_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAnubice_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAnubice_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAnubice_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnAnubice_FindFlameCircles(EnAnubice* this, GlobalContext* globalCtx); +void EnAnubice_SetupIdle(EnAnubice* this, GlobalContext* globalCtx); +void EnAnubice_Idle(EnAnubice* this, GlobalContext* globalCtx); +void EnAnubice_GoToHome(EnAnubice* this, GlobalContext* globalCtx); +void EnAnubice_SetupShootFireball(EnAnubice* this, GlobalContext* globalCtx); +void EnAnubice_ShootFireball(EnAnubice* this, GlobalContext* globalCtx); +void EnAnubice_Die(EnAnubice* this, GlobalContext* globalCtx); + +const ActorInit En_Anubice_InitVars = { + ACTOR_EN_ANUBICE, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_ANUBICE, + sizeof(EnAnubice), + (ActorFunc)EnAnubice_Init, + (ActorFunc)EnAnubice_Destroy, + (ActorFunc)EnAnubice_Update, + (ActorFunc)EnAnubice_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 29, 103, 0, { 0, 0, 0 } }, +}; + +typedef enum { + /* 0x0 */ ANUBICE_DMGEFF_NONE, + /* 0x2 */ ANUBICE_DMGEFF_FIRE = 2, + /* 0xF */ ANUBICE_DMGEFF_0xF = 0xF // Treated the same as ANUBICE_DMGEFF_NONE in code +} AnubiceDamageEffect; + +static DamageTable sDamageTable[] = { + /* Deku nut */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Deku stick */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Slingshot */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Explosive */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Boomerang */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Normal arrow */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Hammer swing */ DMG_ENTRY(1, ANUBICE_DMGEFF_0xF), + /* Hookshot */ DMG_ENTRY(2, ANUBICE_DMGEFF_0xF), + /* Kokiri sword */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Master sword */ DMG_ENTRY(2, ANUBICE_DMGEFF_0xF), + /* Giant's Knife */ DMG_ENTRY(6, ANUBICE_DMGEFF_0xF), + /* Fire arrow */ DMG_ENTRY(2, ANUBICE_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Light arrow */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Unk arrow 1 */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Unk arrow 2 */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Unk arrow 3 */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Fire magic */ DMG_ENTRY(3, ANUBICE_DMGEFF_FIRE), + /* Ice magic */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Light magic */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Shield */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Giant spin */ DMG_ENTRY(6, ANUBICE_DMGEFF_0xF), + /* Master spin */ DMG_ENTRY(2, ANUBICE_DMGEFF_0xF), + /* Kokiri jump */ DMG_ENTRY(0, ANUBICE_DMGEFF_0xF), + /* Giant jump */ DMG_ENTRY(12, ANUBICE_DMGEFF_0xF), + /* Master jump */ DMG_ENTRY(4, ANUBICE_DMGEFF_0xF), + /* Unknown 1 */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, ANUBICE_DMGEFF_NONE), +}; + +void EnAnubice_Hover(EnAnubice* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->hoverVelocityTimer += 1500.0f; + this->targetHeight = player->actor.world.pos.y + this->playerHeightOffset; + Math_ApproachF(&this->actor.world.pos.y, this->targetHeight, 0.1f, 10.0f); + Math_ApproachF(&this->playerHeightOffset, 10.0f, 0.1f, 0.5f); + this->actor.velocity.y = Math_SinS(this->hoverVelocityTimer); +} + +void EnAnubice_SetFireballRot(EnAnubice* this, GlobalContext* globalCtx) { + f32 xzdist; + f32 x; + f32 y; + f32 z; + Player* player = GET_PLAYER(globalCtx); + + x = player->actor.world.pos.x - this->fireballPos.x; + y = player->actor.world.pos.y + 10.0f - this->fireballPos.y; + z = player->actor.world.pos.z - this->fireballPos.z; + xzdist = sqrtf(SQ(x) + SQ(z)); + + this->fireballRot.x = -RADF_TO_BINANG(Math_FAtan2F(y, xzdist)); + this->fireballRot.y = RADF_TO_BINANG(Math_FAtan2F(x, z)); +} + +void EnAnubice_Init(Actor* thisx, GlobalContext* globalCtx) { + EnAnubice* this = (EnAnubice*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gAnubiceSkel, &gAnubiceIdleAnim, this->jointTable, this->morphTable, + ANUBICE_LIMB_MAX); + + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Anubis occurence ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ アヌビス発生 ☆☆☆☆☆ \n" VT_RST); + + this->actor.naviEnemyId = 0x3A; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + Actor_SetScale(&this->actor, 0.015f); + + this->actor.colChkInfo.damageTable = sDamageTable; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.shape.yOffset = -4230.0f; + this->focusHeightOffset = 0.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->home = this->actor.world.pos; + this->actor.targetMode = 3; + this->actionFunc = EnAnubice_FindFlameCircles; +} + +void EnAnubice_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnAnubice* this = (EnAnubice*)thisx; + EnAnubiceTag* tag; + + Collider_DestroyCylinder(globalCtx, &this->collider); + + if (this->actor.params != 0) { + if (this->actor.parent) {} + + tag = (EnAnubiceTag*)this->actor.parent; + if (tag != NULL && tag->actor.update != NULL) { + tag->anubis = NULL; + } + } +} + +void EnAnubice_FindFlameCircles(EnAnubice* this, GlobalContext* globalCtx) { + Actor* currentProp; + s32 flameCirclesFound; + + if (this->isMirroringLink) { + if (!this->hasSearchedForFlameCircles) { + flameCirclesFound = 0; + currentProp = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + while (currentProp != NULL) { + if (currentProp->id != ACTOR_BG_HIDAN_CURTAIN) { + currentProp = currentProp->next; + } else { + this->flameCircles[flameCirclesFound] = (BgHidanCurtain*)currentProp; + // "☆☆☆☆☆ How many fires? ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 火は幾つ? ☆☆☆☆☆ %d\n" VT_RST, flameCirclesFound); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 火は幾つ? ☆☆☆☆☆ %x\n" VT_RST, + this->flameCircles[flameCirclesFound]); + if (flameCirclesFound < ARRAY_COUNT(this->flameCircles) - 1) { + flameCirclesFound++; + } + currentProp = currentProp->next; + } + } + this->hasSearchedForFlameCircles = true; + } + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = EnAnubice_SetupIdle; + } +} + +void EnAnubice_SetupIdle(EnAnubice* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gAnubiceIdleAnim); + + Animation_Change(&this->skelAnime, &gAnubiceIdleAnim, 1.0f, 0.0f, (s16)lastFrame, ANIMMODE_LOOP, -10.0f); + + this->actionFunc = EnAnubice_Idle; + this->actor.velocity.x = this->actor.velocity.z = this->actor.gravity = 0.0f; +} + +void EnAnubice_Idle(EnAnubice* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->actor.shape.yOffset, 0.5f, 300.0f); + Math_ApproachF(&this->focusHeightOffset, 70.0f, 0.5f, 5.0f); + + if (!this->isKnockedback) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 3000, 0); + } + + if (this->actor.shape.yOffset > -2.0f) { + this->actor.shape.yOffset = 0.0f; + + if (player->swordState != 0) { + this->actionFunc = EnAnubice_SetupShootFireball; + } else if (this->isLinkOutOfRange) { + this->actor.velocity.y = 0.0f; + this->actor.gravity = -1.0f; + this->actionFunc = EnAnubice_GoToHome; + } + } +} + +void EnAnubice_GoToHome(EnAnubice* this, GlobalContext* globalCtx) { + f32 xzdist; + f32 xRatio; + f32 zRatio; + f32 x; + f32 z; + + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.shape.yOffset, -4230.0f, 0.5f, 300.0f); + Math_ApproachZeroF(&this->focusHeightOffset, 0.5f, 5.0f); + + if (!this->isKnockedback) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 3000, 0); + } + + if ((fabsf(this->home.x - this->actor.world.pos.x) > 3.0f) && + (fabsf(this->home.z - this->actor.world.pos.z) > 3.0f)) { + x = this->home.x - this->actor.world.pos.x; + z = this->home.z - this->actor.world.pos.z; + xzdist = sqrtf(SQ(x) + SQ(z)); + xRatio = x / xzdist; + zRatio = z / xzdist; + this->actor.world.pos.x += xRatio * 8; + this->actor.world.pos.z += zRatio * 8.0f; + } else if (this->actor.shape.yOffset < -4220.0f) { + this->actor.shape.yOffset = -4230.0f; + this->isMirroringLink = this->isLinkOutOfRange = false; + this->actionFunc = EnAnubice_FindFlameCircles; + this->actor.gravity = 0.0f; + } +} + +void EnAnubice_SetupShootFireball(EnAnubice* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gAnubiceAttackingAnim); + + this->animLastFrame = lastFrame; + Animation_Change(&this->skelAnime, &gAnubiceAttackingAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, -10.0f); + this->actionFunc = EnAnubice_ShootFireball; + this->actor.velocity.x = this->actor.velocity.z = 0.0f; +} + +void EnAnubice_ShootFireball(EnAnubice* this, GlobalContext* globalCtx) { + f32 curFrame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + + if (!this->isKnockedback) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 3000, 0); + } + + EnAnubice_SetFireballRot(this, globalCtx); + + if (curFrame == 12.0f) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ANUBICE_FIRE, this->fireballPos.x, + this->fireballPos.y + 15.0f, this->fireballPos.z, this->fireballRot.x, this->fireballRot.y, 0, 0); + } + + if (this->animLastFrame <= curFrame) { + this->actionFunc = EnAnubice_SetupIdle; + } +} + +void EnAnubice_SetupDie(EnAnubice* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gAnubiceFallDownAnim); + + this->animLastFrame = lastFrame; + Animation_Change(&this->skelAnime, &gAnubiceFallDownAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, -20.0f); + + this->isFallingOver = false; + this->fallTargetPitch = 0; + this->deathTimer = 20; + this->actor.velocity.x = this->actor.velocity.z = 0.0f; + this->actor.gravity = -1.0f; + + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &this->fireballPos, 70.0f)) { + this->isFallingOver = true; + this->fallTargetPitch = this->actor.shape.rot.x - 0x7F00; + } + + this->actionFunc = EnAnubice_Die; +} + +void EnAnubice_Die(EnAnubice* this, GlobalContext* globalCtx) { + f32 curFrame; + f32 rotX; + Vec3f fireEffectInitialPos = { 0.0f, 0.0f, 0.0f }; + Vec3f fireEffectPos = { 0.0f, 0.0f, 0.0f }; + s32 pad; + + SkelAnime_Update(&this->skelAnime); + Math_ApproachZeroF(&this->actor.shape.shadowScale, 0.4f, 0.25f); + + if (this->isFallingOver) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->fallTargetPitch, 1, 10000, 0); + if (fabsf(this->actor.shape.rot.y - this->fallTargetPitch) < 100.0f) { + this->isFallingOver = false; + } + } + + curFrame = this->skelAnime.curFrame; + rotX = curFrame * -3000.0f; + rotX = CLAMP_MIN(rotX, -11000.0f); + + Matrix_RotateY(BINANG_TO_RAD(this->actor.shape.rot.y), MTXMODE_NEW); + Matrix_RotateX(BINANG_TO_RAD(rotX), MTXMODE_APPLY); + fireEffectInitialPos.y = Rand_CenteredFloat(10.0f) + 30.0f; + Matrix_MultVec3f(&fireEffectInitialPos, &fireEffectPos); + fireEffectPos.x += this->actor.world.pos.x + Rand_CenteredFloat(40.0f); + fireEffectPos.y += this->actor.world.pos.y + Rand_CenteredFloat(40.0f); + fireEffectPos.z += this->actor.world.pos.z + Rand_CenteredFloat(30.0f); + Actor_SetColorFilter(&this->actor, 0x4000, 128, 0, 8); + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &fireEffectPos, 100, 0, 0, -1); + + if ((this->animLastFrame <= curFrame) && (this->actor.bgCheckFlags & 1)) { + Math_ApproachF(&this->actor.shape.yOffset, -4230.0f, 0.5f, 300.0f); + if (this->actor.shape.yOffset < -2000.0f) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xC0); + Actor_Kill(&this->actor); + } + } +} + +void EnAnubice_Update(Actor* thisx, GlobalContext* globalCtx) { + f32 zero; + BgHidanCurtain* flameCircle; + s32 i; + Vec3f sp48; + Vec3f sp3C; + EnAnubice* this = (EnAnubice*)thisx; + + if ((this->actionFunc != EnAnubice_SetupDie) && (this->actionFunc != EnAnubice_Die) && + (this->actor.shape.yOffset == 0.0f)) { + EnAnubice_Hover(this, globalCtx); + for (i = 0; i < ARRAY_COUNT(this->flameCircles); i++) { + flameCircle = this->flameCircles[i]; + + if ((flameCircle != NULL) && (fabsf(flameCircle->actor.world.pos.x - this->actor.world.pos.x) < 60.0f) && + (fabsf(this->flameCircles[i]->actor.world.pos.z - this->actor.world.pos.z) < 60.0f) && + (flameCircle->timer != 0)) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + this->actor.flags &= ~ACTOR_FLAG_0; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_ANUBIS_DEAD); + this->actionFunc = EnAnubice_SetupDie; + return; + } + } + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect == ANUBICE_DMGEFF_FIRE) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + this->actor.flags &= ~ACTOR_FLAG_0; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_ANUBIS_DEAD); + this->actionFunc = EnAnubice_SetupDie; + return; + } + + if (!this->isKnockedback) { + this->knockbackTimer = 10; + this->isKnockedback = true; + + sp48.x = 0.0f; + sp48.y = 0.0f; + sp48.z = -10.0f; + sp3C.x = 0.0f; + sp3C.y = 0.0f; + sp3C.z = 0.0f; + + Matrix_RotateY(BINANG_TO_RAD(this->actor.shape.rot.y), MTXMODE_NEW); + Matrix_MultVec3f(&sp48, &sp3C); + + this->actor.velocity.x = sp3C.x; + this->actor.velocity.z = sp3C.z; + this->knockbackRecoveryVelocity.x = -sp3C.x; + this->knockbackRecoveryVelocity.z = -sp3C.z; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_CUTBODY); + } + } + + if (this->isKnockedback) { + this->actor.shape.rot.y += 6500; + Math_ApproachF(&this->actor.velocity.x, this->knockbackRecoveryVelocity.x, 0.3f, 1.0f); + Math_ApproachF(&this->actor.velocity.z, this->knockbackRecoveryVelocity.z, 0.3f, 1.0f); + + zero = 0.0f; + if (zero) {} + + if (this->knockbackTimer == 0) { + this->actor.velocity.x = this->actor.velocity.z = 0.0f; + this->knockbackRecoveryVelocity.x = this->knockbackRecoveryVelocity.z = 0.0f; + this->isKnockedback = false; + } + } + } + + this->timeAlive++; + + if (this->knockbackTimer != 0) { + this->knockbackTimer--; + } + + if (this->deathTimer != 0) { + this->deathTimer--; + } + + this->actionFunc(this, globalCtx); + + this->actor.velocity.y += this->actor.gravity; + func_8002D7EC(&this->actor); + + if (!this->isLinkOutOfRange) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 5.0f, 10.0f, 0x1D); + } else { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 5.0f, 10.0f, 0x1C); + } + + if ((this->actionFunc != EnAnubice_SetupDie) && (this->actionFunc != EnAnubice_Die)) { + Actor_SetFocus(&this->actor, this->focusHeightOffset); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (!this->isKnockedback && (this->actor.shape.yOffset == 0.0f)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +s32 EnAnubice_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnAnubice* this = (EnAnubice*)thisx; + + if (limbIndex == ANUBICE_LIMB_HEAD) { + rot->z += this->headRot; + } + + return false; +} + +void EnAnubice_PostLimbDraw(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnAnubice* this = (EnAnubice*)thisx; + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == ANUBICE_LIMB_HEAD) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_anubice.c", 853); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_anubice.c", 856), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gAnubiceEyesDL); + Matrix_MultVec3f(&pos, &this->fireballPos); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_anubice.c", 868); + } +} + +void EnAnubice_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnAnubice* this = (EnAnubice*)thisx; + + func_80093D84(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnAnubice_OverrideLimbDraw, + EnAnubice_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.h b/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.h new file mode 100644 index 000000000..0796c0fb5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Anubice/z_en_anubice.h @@ -0,0 +1,62 @@ +#ifndef Z_EN_ANUBICE_H +#define Z_EN_ANUBICE_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_Bg_Hidan_Curtain/z_bg_hidan_curtain.h" + +struct EnAnubice; + +typedef void (*EnAnubiceActionFunc)(struct EnAnubice*, struct GlobalContext*); + +typedef enum { + /* 0 */ ANUBICE_LIMB_NONE, + /* 1 */ ANUBICE_LIMB_ROOT, + /* 2 */ ANUBICE_LIMB_BODY_ROOT, + /* 3 */ ANUBICE_LIMB_CHEST, + /* 4 */ ANUBICE_LIMB_ABDOMEN_ROOT, + /* 5 */ ANUBICE_LIMB_LOWER_ABDOMEN, + /* 6 */ ANUBICE_LIMB_UPPER_ABDOMEN, + /* 7 */ ANUBICE_LIMB_TAIL_ROOT, + /* 8 */ ANUBICE_LIMB_TAIL_BASE, + /* 9 */ ANUBICE_LIMB_TAIL_TIP, + /* 10 */ ANUBICE_LIMB_JEWEL_ROOT, + /* 11 */ ANUBICE_LIMB_JEWEL, + /* 12 */ ANUBICE_LIMB_HEAD_ROOT, + /* 13 */ ANUBICE_LIMB_HEAD, + /* 14 */ ANUBICE_LIMB_JAW_ROOT, + /* 15 */ ANUBICE_LIMB_JAW, + /* 16 */ ANUBICE_LIMB_MAX +} AnubiceLimbs; + +typedef struct EnAnubice { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[ANUBICE_LIMB_MAX]; + /* 0x01F0 */ Vec3s morphTable[ANUBICE_LIMB_MAX]; + /* 0x0250 */ EnAnubiceActionFunc actionFunc; + /* 0x0254 */ s16 timeAlive; + /* 0x0256 */ s16 isFallingOver; + /* 0x0258 */ s16 fallTargetPitch; + /* 0x025A */ s16 deathTimer; + /* 0x025C */ s16 knockbackTimer; + /* 0x025E */ s16 isMirroringLink; + /* 0x0260 */ s16 isLinkOutOfRange; + /* 0x0262 */ s16 isKnockedback; + /* 0x0264 */ s16 hasSearchedForFlameCircles; + /* 0x0268 */ f32 hoverVelocityTimer; + /* 0x026C */ f32 animLastFrame; + /* 0x0270 */ f32 targetHeight; + /* 0x0274 */ f32 playerHeightOffset; // How high above the player to hover at + /* 0x0278 */ f32 headRot; + /* 0x027C */ f32 focusHeightOffset; + /* 0x0280 */ Vec3f fireballPos; + /* 0x028C */ Vec3f fireballRot; + /* 0x0298 */ Vec3f home; + /* 0x02A4 */ Vec3f knockbackRecoveryVelocity; + /* 0x02B0 */ BgHidanCurtain* flameCircles[5]; + /* 0x02C4 */ char unk_2C4[0x4]; + /* 0x02C8 */ ColliderCylinder collider; +} EnAnubice; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.c b/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.c new file mode 100644 index 000000000..9edda475e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.c @@ -0,0 +1,260 @@ +/* + * File: z_en_anubice_fire.c + * Overlay: ovl_En_Anubice_Fire + * Description: Anubis Fire Attack + */ + +#include "z_en_anubice_fire.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_anubice/object_anubice.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnAnubiceFire_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAnubiceFire_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAnubiceFire_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAnubiceFire_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809B26EC(EnAnubiceFire* this, GlobalContext* globalCtx); +void func_809B27D8(EnAnubiceFire* this, GlobalContext* globalCtx); +void func_809B2B48(EnAnubiceFire* this, GlobalContext* globalCtx); + +const ActorInit En_Anubice_Fire_InitVars = { + ACTOR_EN_ANUBICE_FIRE, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_ANUBICE, + sizeof(EnAnubiceFire), + (ActorFunc)EnAnubiceFire_Init, + (ActorFunc)EnAnubiceFire_Destroy, + (ActorFunc)EnAnubiceFire_Update, + (ActorFunc)EnAnubiceFire_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 0, 0, 0, { 0, 0, 0 } }, +}; + +void EnAnubiceFire_Init(Actor* thisx, GlobalContext* globalCtx) { + EnAnubiceFire* this = (EnAnubiceFire*)thisx; + s32 i; + + Collider_InitCylinder(globalCtx, &this->cylinder); + Collider_SetCylinder(globalCtx, &this->cylinder, &this->actor, &sCylinderInit); + + this->unk_15A = 30; + this->unk_154 = 2.0f; + this->scale = 0.0f; + + for (i = 0; i < 6; i++) { + this->unk_160[i] = this->actor.world.pos; + } + + this->unk_15E = 0; + this->actionFunc = func_809B26EC; +} + +void EnAnubiceFire_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnAnubiceFire* this = (EnAnubiceFire*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->cylinder); +} + +void func_809B26EC(EnAnubiceFire* this, GlobalContext* globalCtx) { + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + + Matrix_Push(); + Matrix_RotateY(BINANG_TO_RAD(this->actor.world.rot.y), MTXMODE_NEW); + Matrix_RotateX(BINANG_TO_RAD(this->actor.world.rot.x), MTXMODE_APPLY); + velocity.z = 15.0f; + Matrix_MultVec3f(&velocity, &this->actor.velocity); + Matrix_Pop(); + + this->actionFunc = func_809B27D8; + this->actor.world.rot.x = this->actor.world.rot.y = this->actor.world.rot.z = 0; +} + +void func_809B27D8(EnAnubiceFire* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + Color_RGBA8 primColor = { 255, 255, 0, 255 }; + Color_RGBA8 envColor = { 255, 0, 0, 255 }; + Vec3f sp84 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp78 = { 0.0f, 0.0f, 0.0f }; + + this->actor.world.rot.z += 5000; + if (this->unk_15A == 0) { + this->unk_154 = 0.0f; + } + + Math_ApproachF(&this->scale, this->unk_154, 0.2f, 0.4f); + if ((this->unk_15A == 0) && (this->scale < 0.1f)) { + Actor_Kill(&this->actor); + } else if ((this->actor.params == 0) && (this->cylinder.base.atFlags & 4)) { + if (Player_HasMirrorShieldEquipped(globalCtx)) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SHIELD_REFLECT_SW); + this->cylinder.base.atFlags &= 0xFFE9; + this->cylinder.base.atFlags |= 8; + this->cylinder.info.toucher.dmgFlags = 2; + this->unk_15A = 30; + this->actor.params = 1; + this->actor.velocity.x *= -1.0f; + this->actor.velocity.y *= -0.5f; + this->actor.velocity.z *= -1.0f; + } else { + this->unk_15A = 0; + EffectSsBomb2_SpawnLayered(globalCtx, &this->actor.world.pos, &sp78, &sp84, 10, 5); + this->actor.velocity.x = this->actor.velocity.y = this->actor.velocity.z = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_ANUBIS_FIREBOMB); + this->actionFunc = func_809B2B48; + } + } else if (!(this->scale < .4f)) { + f32 scale = 1000.0f; + f32 life = 10.0f; + s32 i; + + for (i = 0; i < 10; i++) { + pos.x = this->actor.world.pos.x + (Rand_ZeroOne() - 0.5f) * (this->scale * 20.0f); + pos.y = this->actor.world.pos.y + (Rand_ZeroOne() - 0.5f) * (this->scale * 20.0f); + pos.z = this->actor.world.pos.z; + EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, scale, life); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_ANUBIS_FIRE - SFX_FLAG); + } +} + +void func_809B2B48(EnAnubiceFire* this, GlobalContext* globalCtx) { + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + Color_RGBA8 primColor = { 255, 255, 0, 255 }; + Color_RGBA8 envColor = { 255, 0, 0, 255 }; + s32 pad; + s32 i; + + if (this->unk_15C == 0) { + for (i = 0; i < 20; i++) { + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z; + accel.x = Rand_CenteredFloat(8.0f); + accel.y = Rand_CenteredFloat(2.0f); + accel.z = Rand_CenteredFloat(8.0f); + EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 2000, 10); + } + + this->unk_15C = 2; + this->unk_15E++; + if (this->unk_15E >= 6) { + Actor_Kill(&this->actor); + } + } +} + +void EnAnubiceFire_Update(Actor* thisx, GlobalContext* globalCtx) { + EnAnubiceFire* this = (EnAnubiceFire*)thisx; + s32 pad; + s32 i; + + Actor_SetScale(&this->actor, this->scale); + this->actionFunc(this, globalCtx); + func_8002D7EC(&this->actor); + this->unk_160[0] = this->actor.world.pos; + + if (1) {} + + for (i = 4; i >= 0; i--) { + this->unk_160[i + 1] = this->unk_160[i]; + } + + if (this->unk_15A != 0) { + this->unk_15A--; + } + + if (this->unk_15C != 0) { + this->unk_15C--; + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 5.0f, 10.0f, 0x1D); + if (!(this->scale < 0.6f || this->actionFunc == func_809B2B48)) { + this->cylinder.dim.radius = this->scale * 15.0f + 5.0f; + this->cylinder.dim.height = this->scale * 15.0f + 5.0f; + this->cylinder.dim.yShift = this->scale * -0.75f + -15.0f; + + if (this->unk_15A != 0) { + Collider_UpdateCylinder(&this->actor, &this->cylinder); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->cylinder.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder.base); + } + + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &this->actor.world.pos, 30.0f)) { + this->actor.velocity.x = this->actor.velocity.y = this->actor.velocity.z = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_ANUBIS_FIREBOMB); + this->actionFunc = func_809B2B48; + } + } +} + +void EnAnubiceFire_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* D_809B3270[] = { + gDust4Tex, gDust5Tex, gDust6Tex, gDust7Tex, gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, + }; + EnAnubiceFire* this = (EnAnubiceFire*)thisx; + s32 pad[2]; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_anubice_fire.c", 503); + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_809B3270[0])); + + Matrix_Push(); + for (i = this->unk_15E; i < 6; ++i) { + f32 scale = this->actor.scale.x - (i * 0.2f); + + if (scale < 0.0f) { + scale = 0.0f; + } + + if (scale >= 0.1f) { + Matrix_Translate(this->unk_160[i].x, this->unk_160[i].y, this->unk_160[i].z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ(this->actor.world.rot.z + i * 1000.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_anubice_fire.c", 546), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gAnubiceFireAttackDL); + } + + if (this->scale < 0.1f) { + break; + } + } + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_anubice_fire.c", 556); +} diff --git a/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h b/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h new file mode 100644 index 000000000..1ea831ffc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Anubice_Fire/z_en_anubice_fire.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_ANUBICE_FIRE_H +#define Z_EN_ANUBICE_FIRE_H + +#include "ultra64.h" +#include "global.h" + +struct EnAnubiceFire; + +typedef void (*EnAnubiceFireActionFunc)(struct EnAnubiceFire*, GlobalContext*); + +typedef struct EnAnubiceFire { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnAnubiceFireActionFunc actionFunc; + /* 0x0150 */ f32 scale; + /* 0x0154 */ f32 unk_154; + /* 0x0158 */ s16 unused; + /* 0x015A */ s16 unk_15A; + /* 0x015C */ s16 unk_15C; + /* 0x015E */ s16 unk_15E; + /* 0x0178 */ Vec3f unk_160[6]; + /* 0x01A8 */ ColliderCylinder cylinder; +} EnAnubiceFire; // size = 0x01F4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.c b/soh/src/overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.c new file mode 100644 index 000000000..73e462a67 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.c @@ -0,0 +1,113 @@ +/* + * File: z_en_anubice_tag.c + * Overlay: ovl_En_Anubice_Tag + * Description: Spawns and manages the Anubis enemy + */ + +#include "z_en_anubice_tag.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnAnubiceTag_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAnubiceTag_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAnubiceTag_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAnubiceTag_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnAnubiceTag_SpawnAnubis(EnAnubiceTag* this, GlobalContext* globalCtx); +void EnAnubiceTag_ManageAnubis(EnAnubiceTag* this, GlobalContext* globalCtx); + +const ActorInit En_Anubice_Tag_InitVars = { + ACTOR_EN_ANUBICE_TAG, + ACTORCAT_SWITCH, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnAnubiceTag), + (ActorFunc)EnAnubiceTag_Init, + (ActorFunc)EnAnubiceTag_Destroy, + (ActorFunc)EnAnubiceTag_Update, + (ActorFunc)EnAnubiceTag_Draw, + NULL, +}; + +void EnAnubiceTag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnAnubiceTag* this = (EnAnubiceTag*)thisx; + + osSyncPrintf("\n\n"); + // "Anubis control tag generated" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ アヌビス制御タグ発生 ☆☆☆☆☆ %d\n" VT_RST, this->actor.params); + + if (this->actor.params < -1) { + this->actor.params = 0; + } + if (this->actor.params != 0) { + this->triggerRange = this->actor.params * 40.0f; + } + this->actionFunc = EnAnubiceTag_SpawnAnubis; +} + +void EnAnubiceTag_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnAnubiceTag_SpawnAnubis(EnAnubiceTag* this, GlobalContext* globalCtx) { + this->anubis = (EnAnubice*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ANUBICE, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, this->actor.yawTowardsPlayer, 0, 0); + + if (this->anubis != NULL) { + this->actionFunc = EnAnubiceTag_ManageAnubis; + } +} + +void EnAnubiceTag_ManageAnubis(EnAnubiceTag* this, GlobalContext* globalCtx) { + EnAnubice* anubis; + Vec3f offset; + + if (this->anubis != NULL) { + anubis = this->anubis; + if (anubis->actor.update == NULL) { + return; + } + } else { + Actor_Kill(&this->actor); + return; + } + + if (anubis->deathTimer != 0) { + Actor_Kill(&this->actor); + return; + } + + if (this->actor.xzDistToPlayer < (200.0f + this->triggerRange)) { + if (!anubis->isLinkOutOfRange) { + if (!anubis->isKnockedback) { + anubis->isMirroringLink = true; + offset.x = -Math_SinS(this->actor.yawTowardsPlayer) * this->actor.xzDistToPlayer; + offset.z = -Math_CosS(this->actor.yawTowardsPlayer) * this->actor.xzDistToPlayer; + Math_ApproachF(&anubis->actor.world.pos.x, (this->actor.world.pos.x + offset.x), 0.3f, 10.0f); + Math_ApproachF(&anubis->actor.world.pos.z, (this->actor.world.pos.z + offset.z), 0.3f, 10.0f); + return; + } + } + } else { + if (anubis->isMirroringLink) { + anubis->isLinkOutOfRange = true; + } + } +} + +void EnAnubiceTag_Update(Actor* thisx, GlobalContext* globalCtx) { + EnAnubiceTag* this = (EnAnubiceTag*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EnAnubiceTag_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnAnubiceTag* this = (EnAnubiceTag*)thisx; + + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 0xFF, 0, 0, 0xFF, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.h b/soh/src/overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.h new file mode 100644 index 000000000..45dabd96f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Anubice_Tag/z_en_anubice_tag.h @@ -0,0 +1,19 @@ +#ifndef Z_EN_ANUBICE_TAG_H +#define Z_EN_ANUBICE_TAG_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Anubice/z_en_anubice.h" + +struct EnAnubiceTag; + +typedef void (*EnAnubiceTagActionFunc)(struct EnAnubiceTag*, GlobalContext*); + +typedef struct EnAnubiceTag { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnAnubiceTagActionFunc actionFunc; + /* 0x0150 */ EnAnubice* anubis; + /* 0x0154 */ f32 triggerRange; +} EnAnubiceTag; // size = 0x0158 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Arow_Trap/z_en_arow_trap.c b/soh/src/overlays/actors/ovl_En_Arow_Trap/z_en_arow_trap.c new file mode 100644 index 000000000..73b87aaa6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Arow_Trap/z_en_arow_trap.c @@ -0,0 +1,53 @@ +/* + * File: z_en_arow_trap.c + * Overlay: ovl_En_Arow_Trap + * Description: An unused trap that reflects arrows. + */ + +#include "z_en_arow_trap.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#define FLAGS ACTOR_FLAG_4 + +void EnArowTrap_Init(Actor* thisx, GlobalContext* globalCtx); +void EnArowTrap_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnArowTrap_Update(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Arow_Trap_InitVars = { + ACTOR_EN_AROW_TRAP, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnArowTrap), + (ActorFunc)EnArowTrap_Init, + (ActorFunc)EnArowTrap_Destroy, + (ActorFunc)EnArowTrap_Update, + NULL, + NULL, +}; + +void EnArowTrap_Init(Actor* thisx, GlobalContext* globalCtx) { + EnArowTrap* this = (EnArowTrap*)thisx; + + Actor_SetScale(&this->actor, 0.01); + this->unk_14C = 0; + this->attackTimer = 80; + this->actor.focus.pos = this->actor.world.pos; +} + +void EnArowTrap_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnArowTrap_Update(Actor* thisx, GlobalContext* globalCtx) { + EnArowTrap* this = (EnArowTrap*)thisx; + + if (this->actor.xzDistToPlayer <= 400) { + this->attackTimer--; + + if (this->attackTimer == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ARROW, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, this->actor.shape.rot.x, + this->actor.shape.rot.y, this->actor.shape.rot.z, ARROW_NORMAL_SILENT); + this->attackTimer = 80; + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Arow_Trap/z_en_arow_trap.h b/soh/src/overlays/actors/ovl_En_Arow_Trap/z_en_arow_trap.h new file mode 100644 index 000000000..e29f84d2b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Arow_Trap/z_en_arow_trap.h @@ -0,0 +1,16 @@ +#ifndef Z_EN_AROW_TRAP_H +#define Z_EN_AROW_TRAP_H + +#include "ultra64.h" +#include "global.h" +#include "z64.h" + +struct EnArowTrap; + +typedef struct EnArowTrap { + /* 0x0000 */ Actor actor; + /* 0x014C */ u32 unk_14C; + /* 0x0150 */ u32 attackTimer; +} EnArowTrap; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c b/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c new file mode 100644 index 000000000..504293094 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c @@ -0,0 +1,496 @@ +/* + * File: z_en_arrow.c + * Overlay: ovl_En_Arrow + * Description: Arrow, Deku Seed, and Deku Nut Projectile + */ + +#include "z_en_arrow.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnArrow_Init(Actor* thisx, GlobalContext* globalCtx); +void EnArrow_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnArrow_Update(Actor* thisx, GlobalContext* globalCtx); +void EnArrow_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnArrow_Shoot(EnArrow* this, GlobalContext* globalCtx); +void EnArrow_Fly(EnArrow* this, GlobalContext* globalCtx); +void func_809B45E0(EnArrow* this, GlobalContext* globalCtx); +void func_809B4640(EnArrow* this, GlobalContext* globalCtx); + +const ActorInit En_Arrow_InitVars = { + ACTOR_EN_ARROW, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnArrow), + (ActorFunc)EnArrow_Init, + (ActorFunc)EnArrow_Destroy, + (ActorFunc)EnArrow_Update, + (ActorFunc)EnArrow_Draw, + NULL, +}; + +static ColliderQuadInit sColliderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_PLAYER, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK2, + { 0x00000020, 0x00, 0x01 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_NEAREST | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(minVelocityY, -150, ICHAIN_STOP), +}; + +void EnArrow_SetupAction(EnArrow* this, EnArrowActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnArrow_Init(Actor* thisx, GlobalContext* globalCtx) { + static EffectBlureInit2 blureNormal = { + 0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16, + 0, 1, 0, { 255, 255, 170, 255 }, { 0, 150, 0, 0 }, + }; + static EffectBlureInit2 blureFire = { + 0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16, + 0, 1, 0, { 255, 200, 0, 255 }, { 255, 0, 0, 0 }, + }; + static EffectBlureInit2 blureIce = { + 0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16, + 0, 1, 0, { 170, 255, 255, 255 }, { 0, 100, 255, 0 }, + }; + static EffectBlureInit2 blureLight = { + 0, 4, 0, { 0, 255, 200, 255 }, { 0, 255, 255, 255 }, { 0, 255, 200, 0 }, { 0, 255, 255, 0 }, 16, + 0, 1, 0, { 255, 255, 170, 255 }, { 255, 255, 0, 0 }, + }; + static u32 dmgFlags[] = { + 0x00000800, 0x00000020, 0x00000020, 0x00000800, 0x00001000, + 0x00002000, 0x00010000, 0x00004000, 0x00008000, 0x00000004, + }; + EnArrow* this = (EnArrow*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + + if (this->actor.params == ARROW_CS_NUT) { + this->isCsNut = true; + this->actor.params = ARROW_NUT; + } + + if (this->actor.params <= ARROW_SEED) { + + if (this->actor.params <= ARROW_0E) { + SkelAnime_Init(globalCtx, &this->skelAnime, &gArrowSkel, &gArrow2Anim, NULL, NULL, 0); + } + + if (this->actor.params <= ARROW_NORMAL) { + if (this->actor.params == ARROW_NORMAL_HORSE) { + blureNormal.elemDuration = 4; + } else { + blureNormal.elemDuration = 16; + } + + Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureNormal); + + } else if (this->actor.params == ARROW_FIRE) { + + Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureFire); + + } else if (this->actor.params == ARROW_ICE) { + + Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureIce); + + } else if (this->actor.params == ARROW_LIGHT) { + + Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE2, 0, 0, &blureLight); + } + + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->actor, &sColliderInit); + + if (this->actor.params <= ARROW_NORMAL) { + this->collider.info.toucherFlags &= ~0x18; + this->collider.info.toucherFlags |= 0; + } + + if (this->actor.params < 0) { + this->collider.base.atFlags = (AT_ON | AT_TYPE_ENEMY); + } else if (this->actor.params <= ARROW_SEED) { + this->collider.info.toucher.dmgFlags = dmgFlags[this->actor.params]; + LOG_HEX("this->at_info.cl_elem.at_btl_info.at_type", this->collider.info.toucher.dmgFlags, + "../z_en_arrow.c", 707); + } + } + + EnArrow_SetupAction(this, EnArrow_Shoot); +} + +void EnArrow_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnArrow* this = (EnArrow*)thisx; + + if (this->actor.params <= ARROW_LIGHT) { + Effect_Delete(globalCtx, this->effectIndex); + } + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyQuad(globalCtx, &this->collider); + + if ((this->hitActor != NULL) && (this->hitActor->update != NULL)) { + this->hitActor->flags &= ~ACTOR_FLAG_15; + } +} + +void EnArrow_Shoot(EnArrow* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->actor.parent == NULL) { + if ((this->actor.params != ARROW_NUT) && (player->unk_A73 == 0)) { + Actor_Kill(&this->actor); + return; + } + + switch (this->actor.params) { + case ARROW_SEED: + func_8002F7DC(&player->actor, NA_SE_IT_SLING_SHOT); + break; + + case ARROW_NORMAL_LIT: + case ARROW_NORMAL_HORSE: + case ARROW_NORMAL: + func_8002F7DC(&player->actor, NA_SE_IT_ARROW_SHOT); + break; + + case ARROW_FIRE: + case ARROW_ICE: + case ARROW_LIGHT: + func_8002F7DC(&player->actor, NA_SE_IT_MAGIC_ARROW_SHOT); + break; + } + + EnArrow_SetupAction(this, EnArrow_Fly); + Math_Vec3f_Copy(&this->unk_210, &this->actor.world.pos); + + if (this->actor.params >= ARROW_SEED) { + func_8002D9A4(&this->actor, 80.0f); + this->timer = 15; + this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = 0; + } else { + func_8002D9A4(&this->actor, 150.0f); + this->timer = 12; + } + } +} + +void func_809B3CEC(GlobalContext* globalCtx, EnArrow* this) { + EnArrow_SetupAction(this, func_809B4640); + Animation_PlayOnce(&this->skelAnime, &gArrow1Anim); + this->actor.world.rot.y += (s32)(24576.0f * (Rand_ZeroOne() - 0.5f)) + 0x8000; + this->actor.velocity.y += (this->actor.speedXZ * (0.4f + (0.4f * Rand_ZeroOne()))); + this->actor.speedXZ *= (0.04f + 0.3f * Rand_ZeroOne()); + this->timer = 50; + this->actor.gravity = -1.5f; +} + +void EnArrow_CarryActor(EnArrow* this, GlobalContext* globalCtx) { + CollisionPoly* hitPoly; + Vec3f posDiffLastFrame; + Vec3f actorNextPos; + Vec3f hitPos; + f32 temp_f12; + f32 scale; + s32 bgId; + + Math_Vec3f_Diff(&this->actor.world.pos, &this->unk_210, &posDiffLastFrame); + + temp_f12 = ((this->actor.world.pos.x - this->hitActor->world.pos.x) * posDiffLastFrame.x) + + ((this->actor.world.pos.y - this->hitActor->world.pos.y) * posDiffLastFrame.y) + + ((this->actor.world.pos.z - this->hitActor->world.pos.z) * posDiffLastFrame.z); + + if (!(temp_f12 < 0.0f)) { + scale = Math3D_Vec3fMagnitudeSq(&posDiffLastFrame); + + if (!(scale < 1.0f)) { + scale = temp_f12 / scale; + Math_Vec3f_Scale(&posDiffLastFrame, scale); + Math_Vec3f_Sum(&this->hitActor->world.pos, &posDiffLastFrame, &actorNextPos); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->hitActor->world.pos, &actorNextPos, &hitPos, + &hitPoly, true, true, true, true, &bgId)) { + this->hitActor->world.pos.x = hitPos.x + ((actorNextPos.x <= hitPos.x) ? 1.0f : -1.0f); + this->hitActor->world.pos.y = hitPos.y + ((actorNextPos.y <= hitPos.y) ? 1.0f : -1.0f); + this->hitActor->world.pos.z = hitPos.z + ((actorNextPos.z <= hitPos.z) ? 1.0f : -1.0f); + } else { + Math_Vec3f_Copy(&this->hitActor->world.pos, &actorNextPos); + } + } + } +} + +void EnArrow_Fly(EnArrow* this, GlobalContext* globalCtx) { + CollisionPoly* hitPoly; + s32 bgId; + Vec3f hitPoint; + Vec3f posCopy; + s32 atTouched; + u16 sfxId; + Actor* hitActor; + Vec3f sp60; + Vec3f sp54; + + if (DECR(this->timer) == 0) { + Actor_Kill(&this->actor); + return; + } + + if (this->timer < 7.2000003f) { + this->actor.gravity = -0.4f; + } + + atTouched = (this->actor.params != ARROW_NORMAL_LIT) && (this->actor.params <= ARROW_SEED) && + (this->collider.base.atFlags & AT_HIT); + + if (atTouched || this->touchedPoly) { + if (this->actor.params >= ARROW_SEED) { + if (atTouched) { + this->actor.world.pos.x = (this->actor.world.pos.x + this->actor.prevPos.x) * 0.5f; + this->actor.world.pos.y = (this->actor.world.pos.y + this->actor.prevPos.y) * 0.5f; + this->actor.world.pos.z = (this->actor.world.pos.z + this->actor.prevPos.z) * 0.5f; + } + + if (this->actor.params == ARROW_NUT) { + iREG(50) = -1; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_M_FIRE1, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + sfxId = NA_SE_IT_DEKU; + } else { + sfxId = NA_SE_IT_SLING_REFLECT; + } + + EffectSsStone1_Spawn(globalCtx, &this->actor.world.pos, 0); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, sfxId); + Actor_Kill(&this->actor); + } else { + EffectSsHitMark_SpawnCustomScale(globalCtx, 0, 150, &this->actor.world.pos); + + if (atTouched && (this->collider.info.atHitInfo->elemType != ELEMTYPE_UNK4)) { + hitActor = this->collider.base.at; + + if ((hitActor->update != NULL) && (!(this->collider.base.atFlags & AT_BOUNCED)) && + (hitActor->flags & ACTOR_FLAG_14)) { + this->hitActor = hitActor; + EnArrow_CarryActor(this, globalCtx); + Math_Vec3f_Diff(&hitActor->world.pos, &this->actor.world.pos, &this->unk_250); + hitActor->flags |= ACTOR_FLAG_15; + this->collider.base.atFlags &= ~AT_HIT; + this->actor.speedXZ /= 2.0f; + this->actor.velocity.y /= 2.0f; + } else { + this->hitFlags |= 1; + this->hitFlags |= 2; + + if (this->collider.info.atHitInfo->bumperFlags & 2) { + this->actor.world.pos.x = this->collider.info.atHitInfo->bumper.hitPos.x; + this->actor.world.pos.y = this->collider.info.atHitInfo->bumper.hitPos.y; + this->actor.world.pos.z = this->collider.info.atHitInfo->bumper.hitPos.z; + } + + func_809B3CEC(globalCtx, this); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_ARROW_STICK_CRE); + } + } else if (this->touchedPoly) { + EnArrow_SetupAction(this, func_809B45E0); + Animation_PlayOnce(&this->skelAnime, &gArrow2Anim); + + if (this->actor.params >= ARROW_NORMAL_LIT) { + this->timer = 60; + } else { + this->timer = 20; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_IT_ARROW_STICK_OBJ); + this->hitFlags |= 1; + } + } + } else { + Math_Vec3f_Copy(&this->unk_210, &this->actor.world.pos); + Actor_MoveForward(&this->actor); + + if ((this->touchedPoly = + BgCheck_ProjectileLineTest(&globalCtx->colCtx, &this->actor.prevPos, &this->actor.world.pos, &hitPoint, + &this->actor.wallPoly, true, true, true, true, &bgId))) { + func_8002F9EC(globalCtx, &this->actor, this->actor.wallPoly, bgId, &hitPoint); + Math_Vec3f_Copy(&posCopy, &this->actor.world.pos); + Math_Vec3f_Copy(&this->actor.world.pos, &hitPoint); + } + + if (this->actor.params <= ARROW_0E) { + this->actor.shape.rot.x = Math_Atan2S(this->actor.speedXZ, -this->actor.velocity.y); + } + } + + if (this->hitActor != NULL) { + if (this->hitActor->update != NULL) { + Math_Vec3f_Sum(&this->unk_210, &this->unk_250, &sp60); + Math_Vec3f_Sum(&this->actor.world.pos, &this->unk_250, &sp54); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp60, &sp54, &hitPoint, &hitPoly, true, true, true, true, + &bgId)) { + this->hitActor->world.pos.x = hitPoint.x + ((sp54.x <= hitPoint.x) ? 1.0f : -1.0f); + this->hitActor->world.pos.y = hitPoint.y + ((sp54.y <= hitPoint.y) ? 1.0f : -1.0f); + this->hitActor->world.pos.z = hitPoint.z + ((sp54.z <= hitPoint.z) ? 1.0f : -1.0f); + Math_Vec3f_Diff(&this->hitActor->world.pos, &this->actor.world.pos, &this->unk_250); + this->hitActor->flags &= ~ACTOR_FLAG_15; + this->hitActor = NULL; + } else { + Math_Vec3f_Sum(&this->actor.world.pos, &this->unk_250, &this->hitActor->world.pos); + } + + if (this->touchedPoly && (this->hitActor != NULL)) { + this->hitActor->flags &= ~ACTOR_FLAG_15; + this->hitActor = NULL; + } + } else { + this->hitActor = NULL; + } + } +} + +void func_809B45E0(EnArrow* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (DECR(this->timer) == 0) { + Actor_Kill(&this->actor); + } +} + +void func_809B4640(EnArrow* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Actor_MoveForward(&this->actor); + + if (DECR(this->timer) == 0) { + Actor_Kill(&this->actor); + } +} + +void EnArrow_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnArrow* this = (EnArrow*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if (this->isCsNut || ((this->actor.params >= ARROW_NORMAL_LIT) && (player->unk_A73 != 0)) || + !Player_InBlockingCsMode(globalCtx, player)) { + this->actionFunc(this, globalCtx); + } + + if ((this->actor.params >= ARROW_FIRE) && (this->actor.params <= ARROW_0E)) { + s16 elementalActorIds[] = { ACTOR_ARROW_FIRE, ACTOR_ARROW_ICE, ACTOR_ARROW_LIGHT, + ACTOR_ARROW_FIRE, ACTOR_ARROW_FIRE, ACTOR_ARROW_FIRE }; + + if (this->actor.child == NULL) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, elementalActorIds[this->actor.params - 3], + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + } + } else if (this->actor.params == ARROW_NORMAL_LIT) { + static Vec3f velocity = { 0.0f, 0.5f, 0.0f }; + static Vec3f accel = { 0.0f, 0.5f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 100, 255 }; + static Color_RGBA8 envColor = { 255, 50, 0, 0 }; + // spawn dust for the flame + func_8002836C(globalCtx, &this->unk_21C, &velocity, &accel, &primColor, &envColor, 100, 0, 8); + } +} + +void func_809B4800(EnArrow* this, GlobalContext* globalCtx) { + static Vec3f D_809B4E88 = { 0.0f, 400.0f, 1500.0f }; + static Vec3f D_809B4E94 = { 0.0f, -400.0f, 1500.0f }; + static Vec3f D_809B4EA0 = { 0.0f, 0.0f, -300.0f }; + Vec3f sp44; + Vec3f sp38; + s32 addBlureVertex; + + Matrix_MultVec3f(&D_809B4EA0, &this->unk_21C); + + if (EnArrow_Fly == this->actionFunc) { + Matrix_MultVec3f(&D_809B4E88, &sp44); + Matrix_MultVec3f(&D_809B4E94, &sp38); + + if (this->actor.params <= ARROW_SEED) { + addBlureVertex = this->actor.params <= ARROW_LIGHT; + + if (this->hitActor == NULL) { + addBlureVertex &= func_80090480(globalCtx, &this->collider, &this->weaponInfo, &sp44, &sp38); + } else { + if (addBlureVertex) { + if ((sp44.x == this->weaponInfo.tip.x) && (sp44.y == this->weaponInfo.tip.y) && + (sp44.z == this->weaponInfo.tip.z) && (sp38.x == this->weaponInfo.base.x) && + (sp38.y == this->weaponInfo.base.y) && (sp38.z == this->weaponInfo.base.z)) { + addBlureVertex = false; + } + } + } + + if (addBlureVertex) { + EffectBlure_AddVertex(Effect_GetByIndex(this->effectIndex), &sp44, &sp38); + } + } + } +} + +void EnArrow_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnArrow* this = (EnArrow*)thisx; + u8 alpha; + f32 scale; + + if (this->actor.params <= ARROW_0E) { + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawLod(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, this, + (this->actor.projectedPos.z < MREG(95)) ? 0 : 1); + } else if (this->actor.speedXZ != 0.0f) { + alpha = (Math_CosS(this->timer * 5000) * 127.5f) + 127.5f; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_arrow.c", 1346); + + func_80093C14(globalCtx->state.gfxCtx); + + if (this->actor.params == ARROW_SEED) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, alpha); + scale = 50.0f; + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 12, 0, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 250, 250, 0, alpha); + scale = 150.0f; + } + + Matrix_Push(); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + // redundant check because this is contained in an if block for non-zero speed + Matrix_RotateZ((this->actor.speedXZ == 0.0f) ? 0.0f + : ((globalCtx->gameplayFrames & 0xFF) * 4000) * (M_PI / 0x8000), + MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_arrow.c", 1374), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffSparklesDL); + Matrix_Pop(); + Matrix_RotateY(this->actor.world.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_arrow.c", 1381); + } + + func_809B4800(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.h b/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.h new file mode 100644 index 000000000..2cb30afcc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.h @@ -0,0 +1,44 @@ +#ifndef Z_EN_ARROW_H +#define Z_EN_ARROW_H + +#include "ultra64.h" +#include "global.h" + +struct EnArrow; + +typedef void (*EnArrowActionFunc)(struct EnArrow*, GlobalContext*); + +typedef struct EnArrow { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ ColliderQuad collider; + /* 0x0210 */ Vec3f unk_210; + /* 0x021C */ Vec3f unk_21C; + /* 0x0228 */ s32 effectIndex; + /* 0x022C */ WeaponInfo weaponInfo; + /* 0x0248 */ u8 timer; // used for dissapearing when flying or hitting a wall + /* 0x0249 */ u8 hitFlags; + /* 0x024A */ u8 touchedPoly; + /* 0x024B */ u8 isCsNut; + /* 0x024C */ Actor* hitActor; + /* 0x0250 */ Vec3f unk_250; + /* 0x025C */ EnArrowActionFunc actionFunc; +} EnArrow; // size = 0x0260 + +typedef enum { + /* -10 */ ARROW_CS_NUT = -10, // cutscene deku nuts are allowed to update in blocking mode + /* -1 */ ARROW_NORMAL_SILENT = -1, // normal arrow that does not make a sound when being shot + /* 0 */ ARROW_NORMAL_LIT, // normal arrow lit on fire + /* 1 */ ARROW_NORMAL_HORSE, // normal arrow shot while riding a horse + /* 2 */ ARROW_NORMAL, + /* 3 */ ARROW_FIRE, + /* 4 */ ARROW_ICE, + /* 5 */ ARROW_LIGHT, + /* 6 */ ARROW_0C, + /* 7 */ ARROW_0D, + /* 8 */ ARROW_0E, + /* 9 */ ARROW_SEED, + /* 10 */ ARROW_NUT +} ArrowType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c b/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c new file mode 100644 index 000000000..c67f39524 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.c @@ -0,0 +1,404 @@ +/* + * File: z_en_attack_niw.c + * Overlay: ovl_En_Attack_Niw + * Description: Attacking Cucco, not solid + */ + +#include "z_en_attack_niw.h" +#include "objects/object_niw/object_niw.h" +#include "overlays/actors/ovl_En_Niw/z_en_niw.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnAttackNiw_Init(Actor* thisx, GlobalContext* globalCtx); +void EnAttackNiw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnAttackNiw_Update(Actor* thisx, GlobalContext* globalCtx); +void EnAttackNiw_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809B5670(EnAttackNiw* this, GlobalContext* globalCtx); +void func_809B5C18(EnAttackNiw* this, GlobalContext* globalCtx); +void func_809B59B0(EnAttackNiw* this, GlobalContext* globalCtx); + +const ActorInit En_Attack_Niw_InitVars = { + ACTOR_EN_ATTACK_NIW, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_NIW, + sizeof(EnAttackNiw), + (ActorFunc)EnAttackNiw_Init, + (ActorFunc)EnAttackNiw_Destroy, + (ActorFunc)EnAttackNiw_Update, + (ActorFunc)EnAttackNiw_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 1, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -2000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void EnAttackNiw_Init(Actor* thisx, GlobalContext* globalCtx) { + EnAttackNiw* this = (EnAttackNiw*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 25.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCuccoSkel, &gCuccoAnim, this->jointTable, this->morphTable, 16); + if (this->actor.params < 0) { + this->actor.params = 0; + } + Actor_SetScale(&this->actor, 0.01f); + this->actor.gravity = 0.0f; + this->unk_298.x = Rand_CenteredFloat(100.0f); + this->unk_298.y = Rand_CenteredFloat(10.0f); + this->unk_298.z = Rand_CenteredFloat(100.0f); + Actor_SetScale(&this->actor, 0.01f); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.shape.rot.y = this->actor.world.rot.y = (Rand_ZeroOne() - 0.5f) * 60000.0f; + this->actionFunc = func_809B5670; +} + +void EnAttackNiw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnAttackNiw* this = (EnAttackNiw*)thisx; + EnNiw* cucco = (EnNiw*)this->actor.parent; + + if (this->actor.parent != NULL) { + if ((cucco->actor.update != NULL) && (cucco->unk_296 > 0)) { + cucco->unk_296--; + } + } +} + +void func_809B5268(EnAttackNiw* this, GlobalContext* globalCtx, s16 arg2) { + if (this->unk_254 == 0) { + if (arg2 == 0) { + this->unk_264 = 0.0f; + } else { + this->unk_264 = -10000.0f; + } + this->unk_28E++; + this->unk_254 = 3; + if ((this->unk_28E & 1) == 0) { + this->unk_264 = 0.0f; + if (arg2 == 0) { + this->unk_254 = Rand_ZeroFloat(30.0f); + } + } + } + + if (this->unk_258 == 0) { + this->unk_292++; + this->unk_292 &= 1; + switch (arg2) { + case 0: + this->unk_26C = 0.0f; + this->unk_268 = 0.0f; + break; + case 1: + this->unk_258 = 3; + this->unk_26C = 7000.0f; + this->unk_268 = 7000.0f; + if (this->unk_292 == 0) { + this->unk_26C = 0.0f; + this->unk_268 = 0.0f; + } + break; + case 2: + this->unk_258 = 2; + this->unk_268 = this->unk_26C = -10000.0f; + this->unk_280 = this->unk_278 = 25000.0f; + this->unk_284 = this->unk_27C = 6000.0f; + if (this->unk_292 == 0) { + this->unk_278 = 8000.0f; + this->unk_280 = 8000.0f; + } + break; + case 3: + this->unk_258 = 2; + this->unk_278 = 10000.0f; + this->unk_280 = 10000.0f; + if (this->unk_292 == 0) { + this->unk_278 = 3000.0f; + this->unk_280 = 3000.0f; + } + break; + case 4: + this->unk_254 = this->unk_256 = 5; + break; + case 5: + this->unk_258 = 5; + this->unk_278 = 14000.0f; + this->unk_280 = 14000.0f; + if (this->unk_292 == 0) { + this->unk_278 = 10000.0f; + this->unk_280 = 10000.0f; + } + break; + } + } + + if (this->unk_288 != this->unk_2C0) { + Math_ApproachF(&this->unk_2C0, this->unk_288, 0.5f, 4000.0f); + } + if (this->unk_264 != this->unk_2BC) { + Math_ApproachF(&this->unk_2BC, this->unk_264, 0.5f, 4000.0f); + } + if (this->unk_26C != this->unk_2A4.x) { + Math_ApproachF(&this->unk_2A4.x, this->unk_26C, 0.8f, 7000.0f); + } + if (this->unk_280 != this->unk_2A4.y) { + Math_ApproachF(&this->unk_2A4.y, this->unk_280, 0.8f, 7000.0f); + } + if (this->unk_284 != this->unk_2A4.z) { + Math_ApproachF(&this->unk_2A4.z, this->unk_284, 0.8f, 7000.0f); + } + if (this->unk_268 != this->unk_2B0.x) { + Math_ApproachF(&this->unk_2B0.x, this->unk_268, 0.8f, 7000.0f); + } + if (this->unk_278 != this->unk_2B0.y) { + Math_ApproachF(&this->unk_2B0.y, this->unk_278, 0.8f, 7000.0f); + } + if (this->unk_27C != this->unk_2B0.z) { + Math_ApproachF(&this->unk_2B0.z, this->unk_27C, 0.8f, 7000.0f); + } +} + +s32 func_809B55EC(EnAttackNiw* this, GlobalContext* globalCtx) { + s16 sp1E; + s16 sp1C; + + Actor_SetFocus(&this->actor, this->unk_2E4); + Actor_GetScreenPos(globalCtx, &this->actor, &sp1E, &sp1C); + if ((this->actor.projectedPos.z < -20.0f) || (sp1E < 0) || (sp1E > SCREEN_WIDTH) || (sp1C < 0) || + (sp1C > SCREEN_HEIGHT)) { + return 0; + } else { + return 1; + } +} + +void func_809B5670(EnAttackNiw* this, GlobalContext* globalCtx) { + s16 sp4E; + s16 sp4C; + f32 tmpf1; + f32 tmpf2; + f32 tmpf3; + Vec3f sp34; + + this->actor.speedXZ = 10.0f; + + tmpf1 = (this->unk_298.x + globalCtx->view.lookAt.x) - globalCtx->view.eye.x; + tmpf2 = (this->unk_298.y + globalCtx->view.lookAt.y) - globalCtx->view.eye.y; + tmpf3 = (this->unk_298.z + globalCtx->view.lookAt.z) - globalCtx->view.eye.z; + + sp34.x = globalCtx->view.lookAt.x + tmpf1; + sp34.y = globalCtx->view.lookAt.y + tmpf2; + sp34.z = globalCtx->view.lookAt.z + tmpf3; + + this->unk_2D4 = Math_Vec3f_Yaw(&this->actor.world.pos, &sp34); + this->unk_2D0 = Math_Vec3f_Pitch(&this->actor.world.pos, &sp34) * -1.0f; + + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_2D4, 5, this->unk_2DC, 0); + Math_SmoothStepToS(&this->actor.world.rot.x, this->unk_2D0, 5, this->unk_2DC, 0); + Math_ApproachF(&this->unk_2DC, 5000.0f, 1.0f, 100.0f); + + Actor_SetFocus(&this->actor, this->unk_2E4); + Actor_GetScreenPos(globalCtx, &this->actor, &sp4E, &sp4C); + + if (this->actor.bgCheckFlags & 8) { + this->unk_2D4 = this->actor.yawTowardsPlayer; + this->unk_2D0 = this->actor.world.rot.x - 3000.0f; + this->unk_2DC = 0.0f; + this->unk_284 = 0.0f; + this->unk_27C = 0.0f; + this->unk_254 = this->unk_256 = this->unk_258 = this->unk_25A = 0; + this->unk_25C = 0x64; + this->actor.gravity = -0.2f; + this->unk_2E0 = 5.0f; + this->unk_288 = 0.0f; + this->actionFunc = func_809B59B0; + } else if (((this->actor.projectedPos.z > 0.0f) && (fabsf(sp34.x - this->actor.world.pos.x) < 50.0f) && + (fabsf(sp34.y - this->actor.world.pos.y) < 50.0f) && + (fabsf(sp34.z - this->actor.world.pos.z) < 50.0f)) || + (this->actor.bgCheckFlags & 1)) { + + this->unk_2D4 = this->actor.yawTowardsPlayer; + this->unk_2D0 = this->actor.world.rot.x - 2000.0f; + this->unk_2DC = 0.0f; + this->unk_27C = 0.0f; + this->unk_284 = 0.0f; + this->unk_254 = this->unk_256 = this->unk_258 = this->unk_25A = 0; + this->actor.gravity = -0.2f; + this->unk_2E0 = 5.0f; + this->unk_288 = 0.0f; + this->actionFunc = func_809B59B0; + } else { + this->unk_254 = 10; + this->unk_264 = -10000.0f; + this->unk_288 = -3000.0f; + func_809B5268(this, globalCtx, 2); + } +} + +void func_809B59B0(EnAttackNiw* this, GlobalContext* globalCtx) { + if (!func_809B55EC(this, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + + if (this->actor.bgCheckFlags & 1) { + if (this->unk_25A == 0) { + this->unk_25A = 3; + this->actor.velocity.y = 3.5f; + } + if (this->actor.gravity != -2.0f) { + this->unk_280 = this->unk_278 = 14000.0f; + this->unk_2D0 = this->unk_26C = this->unk_268 = this->unk_284 = this->unk_27C = 0.0f; + this->unk_2D4 = this->actor.yawTowardsPlayer; + this->unk_262 = 0x32; + this->unk_25C = 0x64; + this->actor.gravity = -2.0f; + } + } + if (this->unk_25C == 0x32) { + this->unk_2D4 = Rand_CenteredFloat(200.0f) + this->actor.yawTowardsPlayer; + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_2D4, 2, this->unk_2DC, 0); + Math_SmoothStepToS(&this->actor.world.rot.x, this->unk_2D0, 2, this->unk_2DC, 0); + Math_ApproachF(&this->unk_2DC, 10000.0f, 1.0f, 1000.0f); + Math_ApproachF(&this->actor.speedXZ, this->unk_2E0, 0.9f, 1.0f); + if ((this->actor.gravity == -2.0f) && (this->unk_262 == 0) && + ((this->actor.bgCheckFlags & 8) || (this->unk_25C == 0))) { + this->unk_2E0 = 0.0f; + this->actor.gravity = 0.0f; + this->unk_2DC = 0.0f; + this->unk_2D0 = this->actor.world.rot.x - 5000.0f; + this->actionFunc = func_809B5C18; + } else if (this->actor.bgCheckFlags & 1) { + func_809B5268(this, globalCtx, 5); + } else { + func_809B5268(this, globalCtx, 2); + } +} + +void func_809B5C18(EnAttackNiw* this, GlobalContext* globalCtx) { + if (!func_809B55EC(this, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + Math_SmoothStepToS(&this->actor.world.rot.x, this->unk_2D0, 5, this->unk_2DC, 0); + Math_ApproachF(&this->unk_2DC, 5000.0f, 1.0f, 100.0f); + Math_ApproachF(&this->actor.velocity.y, 5.0f, 0.3f, 1.0f); + func_809B5268(this, globalCtx, 2); +} + +void EnAttackNiw_Update(Actor* thisx, GlobalContext* globalCtx) { + f32 tmpf1; + EnAttackNiw* this = (EnAttackNiw*)thisx; + EnNiw* cucco; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + Vec3f sp30; + GlobalContext* globalCtx2 = globalCtx; + + this->unk_28C++; + if (this->unk_254 != 0) { + this->unk_254--; + } + if (this->unk_258 != 0) { + this->unk_258--; + } + if (this->unk_25A != 0) { + this->unk_25A--; + } + if (this->unk_25E != 0) { + this->unk_25E--; + } + if (this->unk_260 != 0) { + this->unk_260--; + } + if (this->unk_25C != 0) { + this->unk_25C--; + } + if (this->unk_262 != 0) { + this->unk_262--; + } + + this->actor.shape.rot = this->actor.world.rot; + this->actor.shape.shadowScale = 15.0f; + this->actionFunc(this, globalCtx2); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 0x1D); + + if (this->actionFunc == func_809B5670) { + func_8002D97C(&this->actor); + } else { + Actor_MoveForward(&this->actor); + } + + if (this->actor.floorHeight <= BGCHECK_Y_MIN) { + Actor_Kill(&this->actor); + return; + } + + if ((this->actor.bgCheckFlags & 0x20) && (this->actionFunc != func_809B5C18)) { + Math_Vec3f_Copy(&sp30, &this->actor.world.pos); + sp30.y += this->actor.yDistToWater; + EffectSsGSplash_Spawn(globalCtx, &sp30, 0, 0, 0, 0x190); + this->unk_2DC = 0.0f; + this->actor.gravity = 0.0f; + this->unk_2E0 = 0.0f; + this->unk_2D0 = this->actor.world.rot.x - 5000.0f; + this->actionFunc = func_809B5C18; + return; + } + + tmpf1 = 20.0f; + if (this->actor.xyzDistToPlayerSq < SQ(tmpf1)) { + cucco = (EnNiw*)this->actor.parent; + if ((this->actor.parent->update != NULL) && (this->actor.parent != NULL) && (cucco != NULL) && + (cucco->timer9 == 0) && (player->invincibilityTimer == 0)) { + func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.world.rot.y, 0.0f, 0x10); + cucco->timer9 = 0x46; + } + } + if (this->unk_25E == 0) { + this->unk_25E = 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_A); + } + if (this->unk_260 == 0) { + this->unk_260 = 7; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_WAKEUP); + } +} + +s32 func_809B5F98(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnAttackNiw* this = (EnAttackNiw*)thisx; + Vec3f sp0 = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == 13) { + rot->y += (s16)this->unk_2BC; + } + if (limbIndex == 15) { + rot->z += (s16)this->unk_2C0; + } + if (limbIndex == 11) { + rot->x += (s16)this->unk_2B0.z; + rot->y += (s16)this->unk_2B0.y; + rot->z += (s16)this->unk_2B0.x; + } + if (limbIndex == 7) { + rot->x += (s16)this->unk_2A4.z; + rot->y += (s16)this->unk_2A4.y; + rot->z += (s16)this->unk_2A4.x; + } + return 0; +} + +void EnAttackNiw_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnAttackNiw* this = (EnAttackNiw*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + func_809B5F98, NULL, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.h b/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.h new file mode 100644 index 000000000..746ecb23c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.h @@ -0,0 +1,54 @@ +#ifndef Z_EN_ATTACK_NIW_H +#define Z_EN_ATTACK_NIW_H + +#include "ultra64.h" +#include "global.h" + +struct EnAttackNiw; + +typedef void (*EnAttackNiwActionFunc)(struct EnAttackNiw*, GlobalContext*); + +typedef struct EnAttackNiw { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s morphTable[16]; + /* 0x0250 */ EnAttackNiwActionFunc actionFunc; + /* 0x0254 */ s16 unk_254; + /* 0x0256 */ s16 unk_256; + /* 0x0258 */ s16 unk_258; + /* 0x025A */ s16 unk_25A; + /* 0x025C */ s16 unk_25C; + /* 0x025E */ s16 unk_25E; + /* 0x0260 */ s16 unk_260; + /* 0x0262 */ s16 unk_262; + /* 0x0264 */ f32 unk_264; + /* 0x0268 */ f32 unk_268; + /* 0x026C */ f32 unk_26C; + /* 0x0270 */ char unk_270[0x8]; + /* 0x0278 */ f32 unk_278; + /* 0x027C */ f32 unk_27C; + /* 0x0280 */ f32 unk_280; + /* 0x0284 */ f32 unk_284; + /* 0x0284 */ f32 unk_288; + /* 0x028C */ s16 unk_28C; + /* 0x028E */ s16 unk_28E; + /* 0x0290 */ char unk_290[0x2]; + /* 0x0292 */ s16 unk_292; + /* 0x0294 */ char unk_294[0x2]; + /* 0x0296 */ s16 unk_296; + /* 0x0298 */ Vec3f unk_298; + /* 0x02A4 */ Vec3f unk_2A4; + /* 0x02B0 */ Vec3f unk_2B0; + /* 0x02BC */ f32 unk_2BC; + /* 0x02C0 */ f32 unk_2C0; + /* 0x02C4 */ char unk_2C4[0xC]; + /* 0x02D0 */ f32 unk_2D0; + /* 0x02D4 */ f32 unk_2D4; + /* 0x02D8 */ char unk_2D8[0x4]; + /* 0x02DC */ f32 unk_2DC; + /* 0x02E0 */ f32 unk_2E0; + /* 0x02E4 */ f32 unk_2E4; +} EnAttackNiw; // size = 0x02E8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c new file mode 100644 index 000000000..9ec3b9065 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.c @@ -0,0 +1,524 @@ +/* + * File: z_en_ba.c + * Overlay: ovl_En_Ba + * Description: Tentacle from inside Lord Jabu-Jabu + */ + +#include "z_en_ba.h" +#include "objects/object_bxa/object_bxa.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnBa_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBa_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBa_SetupIdle(EnBa* this); +void EnBa_SetupFallAsBlob(EnBa* this); +void EnBa_Idle(EnBa* this, GlobalContext* globalCtx); +void EnBa_FallAsBlob(EnBa* this, GlobalContext* globalCtx); +void EnBa_SwingAtPlayer(EnBa* this, GlobalContext* globalCtx); +void EnBa_RecoilFromDamage(EnBa* this, GlobalContext* globalCtx); +void EnBa_Die(EnBa* this, GlobalContext* globalCtx); +void EnBa_SetupSwingAtPlayer(EnBa* this); + +const ActorInit En_Ba_InitVars = { + ACTOR_EN_BA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BXA, + sizeof(EnBa), + (ActorFunc)EnBa_Init, + (ActorFunc)EnBa_Destroy, + (ActorFunc)EnBa_Update, + (ActorFunc)EnBa_Draw, + NULL, +}; + +static Vec3f D_809B8080 = { 0.0f, 0.0f, 32.0f }; + +static ColliderJntSphElementInit sJntSphElementInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000010, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 8, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { 13, { { 0, 0, 0 }, 25 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 2, + sJntSphElementInit, +}; + +void EnBa_SetupAction(EnBa* this, EnBaActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +static Vec3f D_809B80E4 = { 0.01f, 0.01f, 0.01f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x15, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2500, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void EnBa_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBa* this = (EnBa*)thisx; + Vec3f sp38 = D_809B80E4; + s32 pad; + s16 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.world.pos.y = this->actor.home.pos.y + 100.0f; + for (i = 13; i >= 0; i--) { + this->unk200[i] = sp38; + this->unk2A8[i].x = -0x4000; + this->unk158[i] = this->actor.world.pos; + this->unk158[i].y = this->actor.world.pos.y - (i + 1) * 32.0f; + } + + this->actor.targetMode = 4; + this->upperParams = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + + if (this->actor.params < EN_BA_DEAD_BLOB) { + if (Flags_GetSwitch(globalCtx, this->upperParams)) { + Actor_Kill(&this->actor); + return; + } + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 48.0f); + Actor_SetScale(&this->actor, 0.01f); + EnBa_SetupIdle(this); + this->actor.colChkInfo.health = 4; + this->actor.colChkInfo.mass = MASS_HEAVY; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + } else { + Actor_SetScale(&this->actor, 0.021f); + EnBa_SetupFallAsBlob(this); + } +} + +void EnBa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBa* this = (EnBa*)thisx; + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnBa_SetupIdle(EnBa* this) { + this->unk14C = 2; + this->unk31C = 1500; + this->actor.speedXZ = 10.0f; + EnBa_SetupAction(this, EnBa_Idle); +} + +void EnBa_Idle(EnBa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 i; + s32 pad; + Vec3s sp5C; + + if ((this->actor.colChkInfo.mass == MASS_IMMOVABLE) && (this->actor.xzDistToPlayer > 175.0f)) { + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 330.0f, 1.0f, 7.0f, 0.0f); + } else { + this->actor.flags |= ACTOR_FLAG_0; + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 100.0f, 1.0f, 10.0f, 0.0f); + } + this->unk2FC = this->actor.world.pos; + if (globalCtx->gameplayFrames % 16 == 0) { + this->unk308.z += Rand_CenteredFloat(180.0f); + this->unk314 += Rand_CenteredFloat(180.0f); + this->unk308.x = Math_SinF(this->unk308.z) * 80.0f; + this->unk308.y = Math_CosF(this->unk314) * 80.0f; + } + this->unk2FC.y -= 448.0f; + this->unk2FC.x += this->unk308.x; + this->unk2FC.z += this->unk308.y; + func_80033AEC(&this->unk2FC, &this->unk158[13], 1.0f, this->actor.speedXZ, 0.0f, 0.0f); + for (i = 12; i >= 0; i--) { + func_80035844(&this->unk158[i + 1], &this->unk158[i], &sp5C, 0); + Matrix_Translate(this->unk158[i + 1].x, this->unk158[i + 1].y, this->unk158[i + 1].z, MTXMODE_NEW); + Matrix_RotateZYX(sp5C.x, sp5C.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i]); + } + func_80035844(&this->unk158[0], &this->unk2FC, &sp5C, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->unk2A8[0].y, 3, this->unk31C, 182); + Math_SmoothStepToS(&this->actor.shape.rot.x, this->unk2A8[0].x, 3, this->unk31C, 182); + Matrix_RotateZYX(this->actor.shape.rot.x - 0x8000, this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[0]); + this->unk2A8[13].y = sp5C.y; + this->unk2A8[13].x = sp5C.x + 0x8000; + + for (i = 0; i < 13; i++) { + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Math_SmoothStepToS(&this->unk2A8[i].y, this->unk2A8[i + 1].y, 3, this->unk31C, 182); + Math_SmoothStepToS(&this->unk2A8[i].x, this->unk2A8[i + 1].x, 3, this->unk31C, 182); + Matrix_RotateZYX(this->unk2A8[i].x - 0x8000, this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i + 1]); + } + this->unk2A8[13].x = this->unk2A8[12].x; + this->unk2A8[13].y = this->unk2A8[12].y; + if (!(player->stateFlags1 & 0x4000000) && (this->actor.xzDistToPlayer <= 175.0f) && + (this->actor.world.pos.y == this->actor.home.pos.y + 100.0f)) { + EnBa_SetupSwingAtPlayer(this); + } +} + +void EnBa_SetupFallAsBlob(EnBa* this) { + this->unk14C = 0; + this->actor.speedXZ = Rand_CenteredFloat(8.0f); + this->actor.world.rot.y = Rand_CenteredFloat(65535.0f); + this->unk318 = 20; + this->actor.gravity = -2.0f; + EnBa_SetupAction(this, EnBa_FallAsBlob); +} + +/** + * Action function of the pink fleshy blobs that spawn and fall to the floor when a tentacle dies + */ +void EnBa_FallAsBlob(EnBa* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1) { + this->actor.scale.y -= 0.001f; + this->actor.scale.x += 0.0005f; + this->actor.scale.z += 0.0005f; + this->unk318--; + if (this->unk318 == 0) { + Actor_Kill(&this->actor); + } + } else { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 28.0f, 80.0f, 5); + } +} + +void EnBa_SetupSwingAtPlayer(EnBa* this) { + this->unk14C = 3; + this->unk318 = 20; + this->unk31A = 0; + this->unk31C = 1500; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.speedXZ = 20.0f; + EnBa_SetupAction(this, EnBa_SwingAtPlayer); +} + +void EnBa_SwingAtPlayer(EnBa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 temp; + s16 i; + Vec3s sp58; + s16 phi_fp; + + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 60.0f, 1.0f, 10.0f, 0.0f); + if ((this->actor.xzDistToPlayer <= 175.0f) || (this->unk31A != 0)) { + if (this->unk318 == 20) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_HAND_UP); + this->unk31C = 1500; + } + if (this->unk318 != 0) { + this->unk31A = 10; + this->unk318--; + if (this->unk318 >= 11) { + this->unk2FC = player->actor.world.pos; + this->unk2FC.y += 30.0f; + phi_fp = this->actor.yawTowardsPlayer; + } else { + phi_fp = Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk2FC); + } + Math_SmoothStepToS(&this->unk31C, 1500, 1, 30, 0); + func_80035844(&this->actor.world.pos, &this->unk158[0], &sp58, 0); + Math_SmoothStepToS(&this->actor.shape.rot.y, sp58.y, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->actor.shape.rot.x, (sp58.x + 0x8000), 1, this->unk31C, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateZYX((this->actor.shape.rot.x - 0x8000), this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[0]); + + for (i = 0; i < 13; i++) { + Math_SmoothStepToS(&this->unk2A8[i].x, (i * 1200) - 0x4000, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->unk2A8[i].y, phi_fp, 1, this->unk31C, 0); + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Matrix_RotateZYX((this->unk2A8[i].x - 0x8000), this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i + 1]); + } + } else { + if (this->unk31A == 10) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_HAND_DOWN); + } + if (this->unk31A != 0) { + this->unk31C = 8000; + this->actor.speedXZ = 30.0f; + phi_fp = Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk2FC); + temp = Math_Vec3f_Pitch(&this->actor.world.pos, &this->unk158[0]) + 0x8000; + Math_SmoothStepToS(&this->actor.shape.rot.y, phi_fp, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->actor.shape.rot.x, temp, 1, this->unk31C, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateZYX(this->actor.shape.rot.x - 0x8000, this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, this->unk158); + + for (i = 0; i < 13; i++) { + temp = -Math_CosS(this->unk31A * 0xCCC) * (i * 1200); + Math_SmoothStepToS(&this->unk2A8[i].x, temp - 0x4000, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->unk2A8[i].y, phi_fp, 1, this->unk31C, 0); + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Matrix_RotateZYX(this->unk2A8[i].x - 0x8000, this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i + 1]); + } + this->unk31A--; + } else if ((this->actor.xzDistToPlayer > 175.0f) || (player->stateFlags1 & 0x4000000)) { + EnBa_SetupIdle(this); + } else { + EnBa_SetupSwingAtPlayer(this); + this->unk318 = 27; + this->unk31C = 750; + } + } + this->unk2A8[13].x = this->unk2A8[12].x; + this->unk2A8[13].y = this->unk2A8[12].y; + if (this->collider.base.atFlags & 2) { + this->collider.base.atFlags &= ~2; + if (this->collider.base.at == &player->actor) { + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); + } + } + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + return; + } + if ((this->actor.xzDistToPlayer > 175.0f) || (player->stateFlags1 & 0x4000000)) { + EnBa_SetupIdle(this); + } else { + EnBa_SetupSwingAtPlayer(this); + this->unk318 = 27; + this->unk31C = 750; + } +} + +void func_809B7174(EnBa* this) { + this->unk14C = 1; + this->unk31C = 1500; + this->unk318 = 20; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.speedXZ = 10.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BALINADE_HAND_DAMAGE); + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 12); + EnBa_SetupAction(this, EnBa_RecoilFromDamage); +} + +void EnBa_RecoilFromDamage(EnBa* this, GlobalContext* globalCtx) { + s32 i; + Vec3s sp6C; + + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 330.0f, 1.0f, 30.0f, 0.0f); + this->unk2FC = this->actor.world.pos; + if (globalCtx->gameplayFrames % 16 == 0) { + this->unk308.z += Rand_CenteredFloat(180.0f); + this->unk314 += Rand_CenteredFloat(180.0f); + this->unk308.x = Math_SinF(this->unk308.z) * 80.0f; + this->unk308.y = Math_CosF(this->unk314) * 80.0f; + } + this->unk2FC.y -= 448.0f; + this->unk2FC.x += this->unk308.x; + this->unk2FC.z += this->unk308.y; + func_80033AEC(&this->unk2FC, &this->unk158[13], 1.0f, this->actor.speedXZ, 0.0f, 0.0f); + for (i = 12; i >= 0; i--) { + func_80035844(&this->unk158[i + 1], &this->unk158[i], &sp6C, 0); + Matrix_Translate(this->unk158[i + 1].x, this->unk158[i + 1].y, this->unk158[i + 1].z, MTXMODE_NEW); + Matrix_RotateZYX(sp6C.x, sp6C.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i]); + } + func_80035844(&this->actor.world.pos, &this->unk158[0], &sp6C, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Math_SmoothStepToS(&this->actor.shape.rot.y, sp6C.y, 3, this->unk31C, 182); + Math_SmoothStepToS(&this->actor.shape.rot.x, sp6C.x + 0x8000, 3, this->unk31C, 182); + Matrix_RotateZYX(this->actor.shape.rot.x - 0x8000, this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[0]); + + for (i = 0; i < 13; i++) { + func_80035844(&this->unk158[i], &this->unk158[i + 1], &sp6C, 0); + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Math_SmoothStepToS(&this->unk2A8[i].y, sp6C.y, 3, this->unk31C, 182); + Math_SmoothStepToS(&this->unk2A8[i].x, sp6C.x + 0x8000, 3, this->unk31C, 182); + Matrix_RotateZYX(this->unk2A8[i].x - 0x8000, this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i + 1]); + } + + this->unk2A8[13].x = this->unk2A8[12].x; + this->unk2A8[13].y = this->unk2A8[12].y; + this->unk318--; + if (this->unk318 == 0) { + EnBa_SetupIdle(this); + } +} + +void func_809B75A0(EnBa* this, GlobalContext* globalCtx2) { + s16 unk_temp; + s32 i; + Vec3f sp74 = { 0.0f, 0.0f, 0.0f }; + GlobalContext* globalCtx = globalCtx2; + + this->unk31C = 2500; + EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_BALINADE_HAND_DEAD, 1, 1, 40); + this->unk14C = 0; + + for (i = 7; i < 14; i++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BA, this->unk158[i].x, this->unk158[i].y, + this->unk158[i].z, 0, 0, 0, EN_BA_DEAD_BLOB); + } + unk_temp = Math_Vec3f_Pitch(&this->actor.world.pos, &this->unk158[0]) + 0x8000; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->actor.shape.rot.x, unk_temp, 1, this->unk31C, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.shape.rot.x - 0x8000, this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[0]); + this->actor.flags &= ~ACTOR_FLAG_0; + for (i = 5; i < 13; i++) { + Math_SmoothStepToS(&this->unk2A8[i].x, this->unk2A8[5].x, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->unk2A8[i].y, this->unk2A8[5].y, 1, this->unk31C, 0); + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Matrix_RotateZYX(this->unk2A8[i].x - 0x8000, this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&sp74, &this->unk158[i + 1]); + } + this->unk31A = 15; + EnBa_SetupAction(this, EnBa_Die); +} + +void EnBa_Die(EnBa* this, GlobalContext* globalCtx) { + Vec3f sp6C = { 0.0f, 0.0f, 0.0f }; + s16 temp; + s32 i; + + if (this->unk31A != 0) { + this->actor.speedXZ = 30.0f; + this->unk31C = 8000; + this->actor.world.pos.y += 8.0f; + temp = Math_Vec3f_Pitch(&this->actor.world.pos, &this->unk158[0]) + 0x8000; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->actor.shape.rot.x, temp, 1, this->unk31C, 0); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.shape.rot.x - 0x8000, this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[0]); + for (i = 0; i < 5; i++) { + temp = -Math_CosS(this->unk31A * 0x444) * (i * 400); + Math_SmoothStepToS(&this->unk2A8[i].x, temp - 0x4000, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->unk2A8[i].y, this->actor.yawTowardsPlayer, 1, this->unk31C, 0); + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Matrix_RotateZYX(this->unk2A8[i].x - 0x8000, this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&D_809B8080, &this->unk158[i + 1]); + } + for (i = 5; i < 13; i++) { + Math_SmoothStepToS(&this->unk2A8[i].x, this->unk2A8[5].x, 1, this->unk31C, 0); + Math_SmoothStepToS(&this->unk2A8[i].y, this->unk2A8[5].y, 1, this->unk31C, 0); + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Matrix_RotateZYX(this->unk2A8[i].x - 0x8000, this->unk2A8[i].y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&sp6C, &this->unk158[i + 1]); + } + this->unk31A--; + } else { + Flags_SetSwitch(globalCtx, this->upperParams); + Actor_Kill(&this->actor); + } +} + +void EnBa_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBa* this = (EnBa*)thisx; + + if ((this->actor.params < EN_BA_DEAD_BLOB) && (this->collider.base.acFlags & 2)) { + this->collider.base.acFlags &= ~2; + this->actor.colChkInfo.health--; + if (this->actor.colChkInfo.health == 0) { + func_809B75A0(this, globalCtx); + } else { + func_809B7174(this); + } + } + this->actionFunc(this, globalCtx); + if (this->actor.params < EN_BA_DEAD_BLOB) { + this->actor.focus.pos = this->unk158[6]; + } + if (this->unk14C >= 2) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +static void* D_809B8118[] = { + object_bxa_Tex_0024F0, + object_bxa_Tex_0027F0, + object_bxa_Tex_0029F0, +}; + +void EnBa_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBa* this = (EnBa*)thisx; + s32 pad; + s16 i; + Mtx* mtx = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx) * 14); + Vec3f unused = { 0.0f, 0.0f, 448.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ba.c", 933); + func_80093D18(globalCtx->state.gfxCtx); + if (this->actor.params < EN_BA_DEAD_BLOB) { + Matrix_Push(); + gSPSegment(POLY_OPA_DISP++, 0x0C, mtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_809B8118[this->actor.params])); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 16, 16, 1, 0, + (globalCtx->gameplayFrames * -10) % 128, 32, 32)); + for (i = 0; i < 14; i++, mtx++) { + Matrix_Translate(this->unk158[i].x, this->unk158[i].y, this->unk158[i].z, MTXMODE_NEW); + Matrix_RotateZYX(this->unk2A8[i].x, this->unk2A8[i].y, this->unk2A8[i].z, MTXMODE_APPLY); + Matrix_Scale(this->unk200[i].x, this->unk200[i].y, this->unk200[i].z, MTXMODE_APPLY); + if ((i == 6) || (i == 13)) { + switch (i) { + case 13: + Collider_UpdateSpheres(i, &this->collider); + break; + default: + Matrix_Scale(0.5f, 0.5f, 1.0f, MTXMODE_APPLY); + Collider_UpdateSpheres(8, &this->collider); + break; + } + } + Matrix_ToMtx(mtx, "../z_en_ba.c", 970); + } + Matrix_Pop(); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ba.c", 973), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_bxa_DL_000890); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (globalCtx->gameplayFrames * 2) % 128, + (globalCtx->gameplayFrames * 2) % 128, 32, 32, 1, + (globalCtx->gameplayFrames * -5) % 128, (globalCtx->gameplayFrames * -5) % 128, 32, + 32)); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 125, 100, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ba.c", 991), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_bxa_DL_001D80); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ba.c", 995); +} diff --git a/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h new file mode 100644 index 000000000..008ec9597 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ba/z_en_ba.h @@ -0,0 +1,37 @@ +#ifndef Z_EN_BA_H +#define Z_EN_BA_H + +#include "ultra64.h" +#include "global.h" + +struct EnBa; + +typedef void (*EnBaActionFunc)(struct EnBa*, GlobalContext*); + +typedef enum { + /* 0x00 */ EN_BA_TENTACLE_RED, + /* 0x01 */ EN_BA_TENTACLE_GREEN, + /* 0x02 */ EN_BA_TENTACLE_GRAY, + /* 0x03 */ EN_BA_DEAD_BLOB +} EnBaType; + +typedef struct EnBa { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 unk14C; + /* 0x0150 */ EnBaActionFunc actionFunc; + /* 0x0154 */ s16 upperParams; + /* 0x0156 */ s16 unk156; + /* 0x0158 */ Vec3f unk158[14]; + /* 0x0200 */ Vec3f unk200[14]; + /* 0x02A8 */ Vec3s unk2A8[14]; + /* 0x02FC */ Vec3f unk2FC; + /* 0x0308 */ Vec3f unk308; + /* 0x0314 */ f32 unk314; + /* 0x0318 */ s16 unk318; + /* 0x031A */ s16 unk31A; + /* 0x031C */ s16 unk31C; + /* 0x0320 */ ColliderJntSph collider; + /* 0x0340 */ ColliderJntSphElement colliderItems[2]; +} EnBa; // size = 0x03C0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c new file mode 100644 index 000000000..f29263898 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.c @@ -0,0 +1,1352 @@ +/* + * File: z_en_bb.c + * Overlay: ovl_En_Bb + * Description: Bubble (Flying Skull Enemy) + */ + +#include "z_en_bb.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_Bb/object_Bb.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_24) + +#define vBombHopPhase actionVar1 +#define vTrailIdx actionVar1 +#define vTrailMaxAlpha actionVar2 +#define vMoveAngleY actionVar2 +#define vFlameTimer actionVar2 + +typedef enum { + /* 0 */ BB_DAMAGE, + /* 1 */ BB_KILL, + /* 2 */ BB_FLAME_TRAIL, + /* 3 */ BB_DOWN, + /* 4 */ BB_STUNNED, + /* 5 */ BB_UNUSED, + /* 6 */ BB_BLUE, + /* 7 */ BB_RED, + /* 8 */ BB_WHITE, + /* 9 */ BB_GREEN +} EnBbAction; + +typedef enum { + /* 0 */ BBMOVE_NORMAL, + /* 1 */ BBMOVE_NOCLIP, + /* 2 */ BBMOVE_HIDDEN +} EnBbMoveMode; + +typedef enum { + /* 0 */ BBBLUE_NORMAL, + /* 1 */ BBBLUE_AGGRO +} EnBbBlueActionState; + +typedef enum { + /* 0 */ BBRED_WAIT, + /* 1 */ BBRED_ATTACK, + /* 2 */ BBRED_HIDE +} EnBbRedActionState; + +typedef enum { + /* 0 */ BBGREEN_FLAME_ON, + /* 1 */ BBGREEN_FLAME_OFF +} EnBbGreenActionState; + +// Main functions + +void EnBb_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBb_Update(Actor* this, GlobalContext* globalCtx); +void EnBb_Draw(Actor* this, GlobalContext* globalCtx); + +// Helper functions + +void EnBb_FaceWaypoint(EnBb* this); +void EnBb_SetWaypoint(EnBb* this, GlobalContext* globalCtx); + +// Action functions + +void EnBb_SetupFlameTrail(EnBb* this); +void EnBb_FlameTrail(EnBb* this, GlobalContext* globalCtx); + +void EnBb_SetupDeath(EnBb* this, GlobalContext* globalCtx); +void EnBb_Death(EnBb* this, GlobalContext* globalCtx); + +void EnBb_Damage(EnBb* this, GlobalContext* globalCtx); + +void EnBb_SetupBlue(EnBb* this); +void EnBb_Blue(EnBb* this, GlobalContext* globalCtx); + +void EnBb_SetupDown(EnBb* this); +void EnBb_Down(EnBb* this, GlobalContext* globalCtx); + +void EnBb_SetupRed(GlobalContext* globalCtx, EnBb* this); +void EnBb_Red(EnBb* this, GlobalContext* globalCtx); + +void EnBb_SetupWhite(GlobalContext* globalCtx, EnBb* this); +void EnBb_White(EnBb* this, GlobalContext* globalCtx); + +void EnBb_InitGreen(EnBb* this, GlobalContext* globalCtx); +void EnBb_Green(EnBb* this, GlobalContext* globalCtx); + +void EnBb_Stunned(EnBb* this, GlobalContext* globalCtx); + +static DamageTable sDamageTableBlueGreen = { + /* Deku nut */ DMG_ENTRY(0, 0xF), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0xA), + /* Boomerang */ DMG_ENTRY(0, 0xF), + /* Normal arrow */ DMG_ENTRY(2, 0xE), + /* Hammer swing */ DMG_ENTRY(2, 0xA), + /* Hookshot */ DMG_ENTRY(0, 0xF), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0xE), + /* Ice arrow */ DMG_ENTRY(4, 0xC), + /* Light arrow */ DMG_ENTRY(4, 0xB), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x6), + /* Ice magic */ DMG_ENTRY(3, 0x9), + /* Light magic */ DMG_ENTRY(3, 0x8), + /* Shield */ DMG_ENTRY(0, 0xA), + /* Mirror Ray */ DMG_ENTRY(0, 0xA), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x6), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xA), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static DamageTable sDamageTableRed = { + /* Deku nut */ DMG_ENTRY(0, 0xD), + /* Deku stick */ DMG_ENTRY(0, 0xD), + /* Slingshot */ DMG_ENTRY(0, 0xD), + /* Explosive */ DMG_ENTRY(2, 0xA), + /* Boomerang */ DMG_ENTRY(0, 0xD), + /* Normal arrow */ DMG_ENTRY(2, 0xE), + /* Hammer swing */ DMG_ENTRY(2, 0xA), + /* Hookshot */ DMG_ENTRY(0, 0xD), + /* Kokiri sword */ DMG_ENTRY(0, 0xD), + /* Master sword */ DMG_ENTRY(2, 0xE), + /* Giant's Knife */ DMG_ENTRY(4, 0xE), + /* Fire arrow */ DMG_ENTRY(2, 0xE), + /* Ice arrow */ DMG_ENTRY(4, 0x9), + /* Light arrow */ DMG_ENTRY(2, 0xE), + /* Unk arrow 1 */ DMG_ENTRY(4, 0xE), + /* Unk arrow 2 */ DMG_ENTRY(2, 0xE), + /* Unk arrow 3 */ DMG_ENTRY(2, 0xE), + /* Fire magic */ DMG_ENTRY(0, 0x6), + /* Ice magic */ DMG_ENTRY(3, 0x9), + /* Light magic */ DMG_ENTRY(0, 0x6), + /* Shield */ DMG_ENTRY(0, 0xA), + /* Mirror Ray */ DMG_ENTRY(0, 0xA), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0xE), + /* Master spin */ DMG_ENTRY(2, 0xE), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0xE), + /* Master jump */ DMG_ENTRY(4, 0xE), + /* Unknown 1 */ DMG_ENTRY(0, 0x6), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xA), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static DamageTable sDamageTableWhite = { + /* Deku nut */ DMG_ENTRY(0, 0xF), + /* Deku stick */ DMG_ENTRY(2, 0xE), + /* Slingshot */ DMG_ENTRY(1, 0xE), + /* Explosive */ DMG_ENTRY(2, 0xA), + /* Boomerang */ DMG_ENTRY(0, 0xF), + /* Normal arrow */ DMG_ENTRY(2, 0xE), + /* Hammer swing */ DMG_ENTRY(2, 0xA), + /* Hookshot */ DMG_ENTRY(0, 0xF), + /* Kokiri sword */ DMG_ENTRY(1, 0xE), + /* Master sword */ DMG_ENTRY(2, 0xE), + /* Giant's Knife */ DMG_ENTRY(4, 0xE), + /* Fire arrow */ DMG_ENTRY(4, 0x5), + /* Ice arrow */ DMG_ENTRY(2, 0xE), + /* Light arrow */ DMG_ENTRY(2, 0xE), + /* Unk arrow 1 */ DMG_ENTRY(4, 0xE), + /* Unk arrow 2 */ DMG_ENTRY(2, 0xE), + /* Unk arrow 3 */ DMG_ENTRY(2, 0xE), + /* Fire magic */ DMG_ENTRY(4, 0x7), + /* Ice magic */ DMG_ENTRY(0, 0x6), + /* Light magic */ DMG_ENTRY(0, 0x6), + /* Shield */ DMG_ENTRY(0, 0xA), + /* Mirror Ray */ DMG_ENTRY(0, 0xA), + /* Kokiri spin */ DMG_ENTRY(1, 0xE), + /* Giant spin */ DMG_ENTRY(4, 0xE), + /* Master spin */ DMG_ENTRY(2, 0xE), + /* Kokiri jump */ DMG_ENTRY(2, 0xE), + /* Giant jump */ DMG_ENTRY(8, 0xE), + /* Master jump */ DMG_ENTRY(4, 0xE), + /* Unknown 1 */ DMG_ENTRY(0, 0x6), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xA), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +const ActorInit En_Bb_InitVars = { + ACTOR_EN_BB, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BB, + sizeof(EnBb), + (ActorFunc)EnBb_Init, + (ActorFunc)EnBb_Destroy, + (ActorFunc)EnBb_Update, + (ActorFunc)EnBb_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, -120, 0 }, 4 }, 300 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 10, ICHAIN_STOP), +}; + +void EnBb_SetupAction(EnBb* this, EnBbActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +Actor* EnBb_FindExplosive(GlobalContext* globalCtx, EnBb* this, f32 range) { + Actor* explosive = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + f32 dist; + + while (explosive != NULL) { + if (explosive->params != 0) { + explosive = explosive->next; + continue; + } + dist = Actor_WorldDistXYZToActor(&this->actor, explosive); + if ((explosive->params == 0) && (dist <= range)) { + return explosive; + } + explosive = explosive->next; + } + return NULL; +} + +void EnBb_SpawnFlameTrail(GlobalContext* globalCtx, EnBb* this, s16 startAtZero) { + EnBb* now = this; + EnBb* next; + s32 i; + + for (i = 0; i < 5; i++) { + next = (EnBb*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BB, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (next != NULL) { + now->actor.child = &next->actor; + next->actor.parent = &now->actor; + next->targetActor = &this->actor; + next->vTrailIdx = i + 1; + next->actor.scale.x = 1.0f; + next->vTrailMaxAlpha = next->flamePrimAlpha = 255 - (i * 40); + next->flameScaleY = next->actor.scale.y = 0.8f - (i * 0.075f); + next->flameScaleX = next->actor.scale.z = 1.0f - (i * 0.094f); + if (startAtZero) { + next->flamePrimAlpha = 0; + next->flameScaleY = next->flameScaleX = 0.0f; + } + next->flameScrollMod = i + 1; + next->timer = 2 * i + 2; + next->flameEnvColor.r = 255; + now = next; + } + } +} + +void EnBb_KillFlameTrail(EnBb* this) { + Actor* actor = &this->actor; + + while (actor->child != NULL) { + Actor* nextActor = actor->child; + + if (nextActor->id == ACTOR_EN_BB) { + nextActor->parent = NULL; + actor->child = NULL; + nextActor->params = ENBB_KILL_TRAIL; + } + actor = nextActor; + } + this->actor.child = NULL; +} + +void EnBb_Init(Actor* thisx, GlobalContext* globalCtx) { + EffectBlureInit1 blureInit; + s32 pad; + EnBb* this = (EnBb*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + SkelAnime_Init(globalCtx, &this->skelAnime, &object_Bb_Skel_001A30, &object_Bb_Anim_000444, this->jointTable, + this->morphTable, 16); + this->unk_254 = 0; + thisx->colChkInfo.health = 4; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, thisx, &sJntSphInit, this->elements); + + this->actionState = thisx->params >> 8; + + if (thisx->params & 0x80) { + thisx->params |= 0xFF00; + } + if (thisx->params <= ENBB_BLUE) { + ActorShape_Init(&thisx->shape, 200.0f, ActorShadow_DrawCircle, 35.0f); + } + if (thisx->params & 0xFF00) { + this->timer = 0; + this->flameScaleY = 80.0f; + this->flameScaleX = 100.0f; + this->collider.elements[0].info.toucherFlags = TOUCH_ON | TOUCH_SFX_HARD; + this->collider.elements[0].info.toucher.dmgFlags = 0xFFCFFFFF; + this->collider.elements[0].info.toucher.damage = 8; + this->bobSize = this->actionState * 20.0f; + this->flamePrimAlpha = 255; + this->moveMode = BBMOVE_NORMAL; + Actor_SetScale(thisx, 0.01f); + switch (thisx->params) { + case ENBB_BLUE: + thisx->naviEnemyId = 0x1C; + thisx->colChkInfo.damageTable = &sDamageTableBlueGreen; + this->flamePrimBlue = this->flameEnvColor.b = 255; + thisx->world.pos.y += 50.0f; + EnBb_SetupBlue(this); + thisx->flags |= ACTOR_FLAG_14; + break; + case ENBB_RED: + thisx->naviEnemyId = 0x24; + thisx->colChkInfo.damageTable = &sDamageTableRed; + this->flameEnvColor.r = 255; + this->collider.elements[0].info.toucher.effect = 1; + EnBb_SetupRed(globalCtx, this); + break; + case ENBB_WHITE: + thisx->naviEnemyId = 0x1D; + thisx->colChkInfo.damageTable = &sDamageTableWhite; + this->path = this->actionState; + blureInit.p1StartColor[0] = blureInit.p1StartColor[1] = blureInit.p1StartColor[2] = + blureInit.p1StartColor[3] = blureInit.p2StartColor[0] = blureInit.p2StartColor[1] = + blureInit.p2StartColor[2] = blureInit.p2StartColor[3] = blureInit.p1EndColor[0] = + blureInit.p1EndColor[1] = blureInit.p1EndColor[2] = blureInit.p2EndColor[0] = + blureInit.p2EndColor[1] = blureInit.p2EndColor[2] = 255; + + blureInit.p1EndColor[3] = 0; + blureInit.p2EndColor[3] = 0; + blureInit.elemDuration = 16; + blureInit.unkFlag = 0; + blureInit.calcMode = 2; + + Effect_Add(globalCtx, &this->blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); + EnBb_SetupWhite(globalCtx, this); + EnBb_SetWaypoint(this, globalCtx); + EnBb_FaceWaypoint(this); + thisx->flags |= ACTOR_FLAG_14; + break; + case ENBB_GREEN_BIG: + this->path = this->actionState >> 4; + this->collider.elements[0].dim.modelSphere.radius = 0x16; + Actor_SetScale(thisx, 0.03f); + case ENBB_GREEN: + thisx->naviEnemyId = 0x1E; + this->bobSize = (this->actionState & 0xF) * 20.0f; + thisx->colChkInfo.damageTable = &sDamageTableBlueGreen; + this->flameEnvColor.g = 255; + thisx->colChkInfo.health = 1; + + EnBb_InitGreen(this, globalCtx); + break; + } + thisx->focus.pos = thisx->world.pos; + } else { + EnBb_SetupFlameTrail(this); + } + this->collider.elements[0].dim.worldSphere.radius = + this->collider.elements[0].dim.modelSphere.radius * this->collider.elements[0].dim.scale; +} + +void EnBb_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBb* this = (EnBb*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnBb_SetupFlameTrail(EnBb* this) { + this->action = BB_FLAME_TRAIL; + this->moveMode = BBMOVE_NOCLIP; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 0.0f; + EnBb_SetupAction(this, EnBb_FlameTrail); +} + +void EnBb_FlameTrail(EnBb* this, GlobalContext* globalCtx) { + if (this->actor.params == ENBB_KILL_TRAIL) { + if (this->actor.parent == NULL) { + EnBb_SetupDeath(this, globalCtx); + } + } else { + if (this->timer == 0) { + if (((EnBb*)this->targetActor)->flameScaleY != 0.0f) { + Math_SmoothStepToF(&this->flameScaleY, this->actor.scale.y, 1.0f, this->actor.scale.y * 0.1f, 0.0f); + Math_SmoothStepToF(&this->flameScaleX, this->actor.scale.z, 1.0f, this->actor.scale.z * 0.1f, 0.0f); + if (this->flamePrimAlpha != this->vTrailMaxAlpha) { + this->flamePrimAlpha += 10; + if (this->vTrailMaxAlpha < this->flamePrimAlpha) { + this->flamePrimAlpha = this->vTrailMaxAlpha; + } + } + } else { + if (!this->flamePrimAlpha) { + Actor_Kill(&this->actor); + return; + } else if (this->flamePrimAlpha <= 20) { + this->flamePrimAlpha = 0; + } else { + this->flamePrimAlpha -= 20; + } + } + this->actor.world.pos = this->actor.parent->prevPos; + } else { + this->timer--; + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.parent->world.rot.y; + } + } + if (this->actor.parent != NULL) { + this->actor.velocity.y = this->actor.parent->velocity.y; + } +} + +void EnBb_SetupDeath(EnBb* this, GlobalContext* globalCtx) { + if (this->actor.params <= ENBB_BLUE) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = -7.0f; + this->timer = 5; + this->actor.shape.rot.x += 0x4E20; + EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_BUBLE_DEAD, 1, 1, 0x28); + } + this->action = BB_KILL; + EnBb_SetupAction(this, EnBb_Death); +} + +void EnBb_Death(EnBb* this, GlobalContext* globalCtx) { + s16 enpartType = 3; + Vec3f sp40 = { 0.0f, 0.5f, 0.0f }; + Vec3f sp34 = { 0.0f, 0.0f, 0.0f }; + + if (this->actor.params <= ENBB_BLUE) { + Math_SmoothStepToF(&this->flameScaleY, 0.0f, 1.0f, 30.0f, 0.0f); + Math_SmoothStepToF(&this->flameScaleX, 0.0f, 1.0f, 30.0f, 0.0f); + if (this->timer != 0) { + this->timer--; + this->actor.shape.rot.x -= 0x4E20; + return; + } + + if (this->bodyBreak.val == BODYBREAK_STATUS_FINISHED) { + BodyBreak_Alloc(&this->bodyBreak, 12, globalCtx); + } + + if ((this->dmgEffect == 7) || (this->dmgEffect == 5)) { + enpartType = 11; + } + + if (!BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, enpartType)) { + return; + } + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xD0); + } else { + if (this->flamePrimAlpha) { + if (this->flamePrimAlpha <= 20) { + this->flamePrimAlpha = 0; + } else { + this->flamePrimAlpha -= 20; + } + return; + } + } + Actor_Kill(&this->actor); +} + +void EnBb_SetupDamage(EnBb* this) { + this->action = BB_DAMAGE; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_DAMAGE); + if (this->actor.params > ENBB_GREEN) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if ((this->actor.bgCheckFlags & 8) == 0) { + this->actor.speedXZ = -7.0f; + } + this->actor.shape.yOffset = 1500.0f; + } + if (this->actor.params == ENBB_RED) { + EnBb_KillFlameTrail(this); + } + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); + this->timer = 5; + EnBb_SetupAction(this, EnBb_Damage); +} + +void EnBb_Damage(EnBb* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + if (this->actor.speedXZ == 0.0f) { + this->actor.shape.yOffset = 200.0f; + EnBb_SetupDown(this); + } +} + +void EnBb_SetupBlue(EnBb* this) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + this->actor.speedXZ = (Rand_ZeroOne() * 0.5f) + 0.5f; + this->timer = (Rand_ZeroOne() * 20.0f) + 40.0f; + this->unk_264 = (Rand_ZeroOne() * 30.0f) + 180.0f; + this->targetActor = NULL; + this->action = BB_BLUE; + EnBb_SetupAction(this, EnBb_Blue); +} + +void EnBb_Blue(EnBb* this, GlobalContext* globalCtx) { + Actor* explosive; + s16 moveYawToWall; + s16 thisYawToWall; + s16 afterHitAngle; + + Math_SmoothStepToF(&this->flameScaleY, 80.0f, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToF(&this->flameScaleX, 100.0f, 1.0f, 10.0f, 0.0f); + if (this->actor.floorHeight > BGCHECK_Y_MIN) { + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight + 50.0f + this->flyHeightMod, 1.0f, 0.5f, + 0.0f); + } + SkelAnime_Update(&this->skelAnime); + if (Math_CosF(this->bobPhase) == 0.0f) { + if (this->charge) { + this->bobSpeedMod = Rand_ZeroOne() * 2.0f; + } else { + this->bobSpeedMod = Rand_ZeroOne() * 4.0f; + } + } + this->actor.world.pos.y += Math_CosF(this->bobPhase) * (1.0f + this->bobSpeedMod); + this->bobPhase += 0.2f; + Math_SmoothStepToF(&this->actor.speedXZ, this->maxSpeed, 1.0f, 0.5f, 0.0f); + + if (Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) > 300.0f) { + this->vMoveAngleY = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_SmoothStepToS(&this->actor.world.rot.y, this->vMoveAngleY, 1, 0x7D0, 0); + } else { + this->timer--; + if (this->timer <= 0) { + this->charge ^= true; + this->flyHeightMod = (s16)(Math_CosF(this->bobPhase) * 10.0f); + this->actor.speedXZ = 0.0f; + if (this->charge && (this->targetActor == NULL)) { + this->vMoveAngleY = this->actor.world.rot.y; + if (this->actor.xzDistToPlayer < 200.0f) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000184); + this->vMoveAngleY = this->actor.yawTowardsPlayer; + } + this->maxSpeed = (Rand_ZeroOne() * 1.5f) + 6.0f; + this->timer = (Rand_ZeroOne() * 5.0f) + 20.0f; + this->actionState = BBBLUE_NORMAL; + } else { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + this->maxSpeed = (Rand_ZeroOne() * 1.5f) + 1.0f; + this->timer = (Rand_ZeroOne() * 20.0f) + 40.0f; + this->vMoveAngleY = Math_SinF(this->bobPhase) * 65535.0f; + } + } + if ((this->actor.xzDistToPlayer < 150.0f) && (this->actionState != BBBLUE_NORMAL)) { + if (!this->charge) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000184); + this->maxSpeed = (Rand_ZeroOne() * 1.5f) + 6.0f; + this->timer = (Rand_ZeroOne() * 5.0f) + 20.0f; + this->vMoveAngleY = this->actor.yawTowardsPlayer; + this->actionState = this->charge = true; // Sets actionState to BBBLUE_AGGRO + } + } else if (this->actor.xzDistToPlayer < 200.0f) { + this->vMoveAngleY = this->actor.yawTowardsPlayer; + } + if (this->targetActor == NULL) { + explosive = EnBb_FindExplosive(globalCtx, this, 300.0f); + } else if (this->targetActor->params == 0) { + explosive = this->targetActor; + } else { + explosive = NULL; + } + if (explosive != NULL) { + this->vMoveAngleY = Actor_WorldYawTowardActor(&this->actor, explosive); + if ((this->vBombHopPhase == 0) && (explosive != this->targetActor)) { + this->vBombHopPhase = -0x8000; + this->targetActor = explosive; + this->actor.speedXZ *= 0.5f; + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->vMoveAngleY, 1, 0x1388, 0); + Math_SmoothStepToF(&this->actor.world.pos.x, explosive->world.pos.x, 1.0f, 1.5f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, explosive->world.pos.y + 40.0f, 1.0f, 1.5f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, explosive->world.pos.z, 1.0f, 1.5f, 0.0f); + } else { + this->targetActor = NULL; + } + if (this->vBombHopPhase != 0) { + this->actor.world.pos.y += -Math_CosS(this->vBombHopPhase) * 10.0f; + this->vBombHopPhase += 0x1000; + Math_SmoothStepToS(&this->actor.world.rot.y, this->vMoveAngleY, 1, 0x7D0, 0); + } + thisYawToWall = this->actor.wallYaw - this->actor.world.rot.y; + moveYawToWall = this->actor.wallYaw - this->vMoveAngleY; + if ((this->targetActor == NULL) && (this->actor.bgCheckFlags & 8) && + (ABS(thisYawToWall) > 0x4000 || ABS(moveYawToWall) > 0x4000)) { + this->vMoveAngleY = this->actor.wallYaw + this->actor.wallYaw - this->actor.world.rot.y - 0x8000; + Math_SmoothStepToS(&this->actor.world.rot.y, this->vMoveAngleY, 1, 0xBB8, 0); + } + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->vMoveAngleY, 1, 0x3E8, 0); + if ((this->collider.base.acFlags & AC_HIT) || (this->collider.base.atFlags & AT_HIT)) { + this->vMoveAngleY = this->actor.yawTowardsPlayer + 0x8000; + if (this->collider.base.acFlags & AC_HIT) { + afterHitAngle = -0x8000; + } else { + afterHitAngle = 0x4000; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_BITE); + if (globalCtx->gameplayFrames & 1) { + afterHitAngle = -0x4000; + } + } + this->actor.world.rot.y = this->actor.yawTowardsPlayer + afterHitAngle; + this->collider.base.acFlags &= ~AC_HIT; + this->collider.base.atFlags &= ~AT_HIT; + } + + if (this->maxSpeed >= 6.0f) { + if ((s32)this->skelAnime.curFrame == 0 || (s32)this->skelAnime.curFrame == 5) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_MOUTH); + } else if ((s32)this->skelAnime.curFrame == 2 || (s32)this->skelAnime.curFrame == 7) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_WING); + } + } else { + if ((s32)this->skelAnime.curFrame == 5) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_WING); + } + } + if (((s32)this->skelAnime.curFrame == 0) && (Rand_ZeroOne() < 0.1f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_LAUGH); + } + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnBb_SetupDown(EnBb* this) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + this->action = BB_DOWN; + this->timer = 200; + this->actor.colorFilterTimer = 0; + this->actor.bgCheckFlags &= ~1; + this->actor.speedXZ = 3.0f; + this->flameScaleX = 0.0f; + this->flameScaleY = 0.0f; + this->actor.gravity = -2.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_DOWN); + EnBb_SetupAction(this, EnBb_Down); +} + +void EnBb_Down(EnBb* this, GlobalContext* globalCtx) { + s16 yawDiff = this->actor.world.rot.y - this->actor.wallYaw; + + SkelAnime_Update(&this->skelAnime); + if (this->actor.bgCheckFlags & 8) { + if (ABS(yawDiff) > 0x4000) { + this->actor.world.rot.y = this->actor.wallYaw + this->actor.wallYaw - this->actor.world.rot.y - 0x8000; + } + this->actor.bgCheckFlags &= ~8; + } + if (this->actor.bgCheckFlags & 3) { + if (this->actor.params == ENBB_RED) { + s32 floorType = func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + + if ((floorType == 2) || (floorType == 3) || (floorType == 9)) { + this->moveMode = BBMOVE_HIDDEN; + this->timer = 10; + this->actionState++; + this->actor.flags &= ~ACTOR_FLAG_0; + this->action = BB_RED; + EnBb_SetupAction(this, EnBb_Red); + return; + } + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + if (this->actor.velocity.y < -14.0f) { + this->actor.velocity.y *= -0.7f; + } else { + this->actor.velocity.y = 10.0f; + } + this->actor.bgCheckFlags &= ~1; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 7.0f, 2, 2.0f, 0, 0, 0); + Math_SmoothStepToS(&this->actor.world.rot.y, -this->actor.yawTowardsPlayer, 1, 0xBB8, 0); + } + this->actor.shape.rot.y = this->actor.world.rot.y; + if ((s32)this->skelAnime.curFrame == 5) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_WING); + } + if (this->timer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_UP); + switch (this->actor.params) { + case ENBB_BLUE: + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + EnBb_SetupBlue(this); + break; + case ENBB_RED: + if (this->actor.velocity.y == 10.0f) { + EnBb_SetupRed(globalCtx, this); + EnBb_SpawnFlameTrail(globalCtx, this, true); + } + break; + case ENBB_WHITE: + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + EnBb_SetupWhite(globalCtx, this); + this->actor.world.pos.y -= 60.0f; + break; + } + } else { + this->timer--; + } +} + +void EnBb_SetupRed(GlobalContext* globalCtx, EnBb* this) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000184); + if (this->action == BB_DOWN) { + this->actor.speedXZ = 5.0f; + this->actor.gravity = -1.0f; + this->actor.velocity.y = 16.0f; + this->actionState = BBRED_ATTACK; + this->timer = 0; + this->moveMode = BBMOVE_NORMAL; + this->actor.bgCheckFlags &= ~1; + } else { + this->actor.colChkInfo.health = 4; + this->timer = 0; + this->actionState = BBRED_WAIT; + this->moveMode = BBMOVE_HIDDEN; + this->actor.world.pos.y -= 80.0f; + this->actor.home.pos = this->actor.world.pos; + this->actor.velocity.y = this->actor.gravity = this->actor.speedXZ = 0.0f; + this->actor.bgCheckFlags &= ~1; + this->actor.flags &= ~ACTOR_FLAG_0; + } + this->action = BB_RED; + EnBb_SetupAction(this, EnBb_Red); +} + +void EnBb_Red(EnBb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 floorType; + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + switch (this->actionState) { + case BBRED_WAIT: + if ((Actor_WorldDistXYZToActor(&this->actor, &player->actor) <= 250.0f) && (ABS(yawDiff) <= 0x4000) && + (this->timer == 0)) { + this->actor.speedXZ = 5.0f; + this->actor.gravity = -1.0f; + this->actor.velocity.y = 18.0f; + this->moveMode = BBMOVE_NOCLIP; + this->timer = 7; + this->actor.bgCheckFlags &= ~1; + this->actionState++; + EnBb_SpawnFlameTrail(globalCtx, this, false); + } + break; + case BBRED_ATTACK: + if (this->timer == 0) { + this->moveMode = BBMOVE_NORMAL; + this->actor.flags |= ACTOR_FLAG_0; + } + this->bobPhase += Rand_ZeroOne(); + Math_SmoothStepToF(&this->flameScaleY, 80.0f, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToF(&this->flameScaleX, 100.0f, 1.0f, 10.0f, 0.0f); + if (this->actor.bgCheckFlags & 8) { + yawDiff = this->actor.world.rot.y - this->actor.wallYaw; + if (ABS(yawDiff) > 0x4000) { + this->actor.world.rot.y = + this->actor.wallYaw + this->actor.wallYaw - this->actor.world.rot.y - 0x8000; + } + this->actor.bgCheckFlags &= ~8; + } + if (this->actor.bgCheckFlags & 1) { + floorType = func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + if ((floorType == 2) || (floorType == 3) || (floorType == 9)) { + this->moveMode = BBMOVE_HIDDEN; + this->timer = 10; + this->actionState++; + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + this->actor.velocity.y *= -1.06f; + if (this->actor.velocity.y > 13.0f) { + this->actor.velocity.y = 13.0f; + } + this->actor.world.rot.y = Math_SinF(this->bobPhase) * 65535.0f; + } + this->actor.bgCheckFlags &= ~1; + } + this->actor.shape.rot.y = this->actor.world.rot.y; + if (Actor_GetCollidedExplosive(globalCtx, &this->collider.base) != NULL) { + EnBb_SetupDown(this); + } + break; + case BBRED_HIDE: + if (this->timer == 0) { + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actionState = BBRED_WAIT; + this->timer = 120; + this->actor.world.pos = this->actor.home.pos; + this->actor.shape.rot = this->actor.world.rot = this->actor.home.rot; + EnBb_KillFlameTrail(this); + } + break; + } + if (this->actionState != BBRED_WAIT) { + if (((s32)this->skelAnime.curFrame == 0) || ((s32)this->skelAnime.curFrame == 5)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_MOUTH); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEFALL_FIRE - SFX_FLAG); + } +} + +void EnBb_FaceWaypoint(EnBb* this) { + this->actor.world.rot.y = this->actor.shape.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos); +} + +void EnBb_SetWaypoint(EnBb* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->path]; + Vec3s* point; + + if (this->waypoint == (s16)(path->count - 1)) { + this->waypoint = 0; + } else { + this->waypoint++; + } + point = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->waypoint; + this->waypointPos.x = point->x; + this->waypointPos.y = point->y; + this->waypointPos.z = point->z; +} + +void EnBb_SetupWhite(GlobalContext* globalCtx, EnBb* this) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + this->actor.speedXZ = 0.0f; + this->actor.world.pos.y += 60.0f; + this->flameScaleX = 100.0f; + this->action = BB_WHITE; + this->waypoint = 0; + this->timer = (Rand_ZeroOne() * 30.0f) + 40.0f; + this->maxSpeed = 7.0f; + EnBb_SetupAction(this, EnBb_White); +} + +void EnBb_White(EnBb* this, GlobalContext* globalCtx) { + if (this->actor.speedXZ == 0.0f) { + f32 distL1; + f32 vx; + f32 vz; + s16 pitch = Math_Vec3f_Pitch(&this->actor.world.pos, &this->waypointPos); + f32 vy = Math_SinS(pitch) * this->maxSpeed; + f32 vxz = Math_CosS(pitch) * this->maxSpeed; + + vx = Math_SinS(this->actor.shape.rot.y) * vxz; + vz = Math_CosS(this->actor.shape.rot.y) * vxz; + distL1 = Math_SmoothStepToF(&this->actor.world.pos.x, this->waypointPos.x, 1.0f, ABS(vx), 0.0f); + distL1 += Math_SmoothStepToF(&this->actor.world.pos.y, this->waypointPos.y, 1.0f, ABS(vy), 0.0f); + distL1 += Math_SmoothStepToF(&this->actor.world.pos.z, this->waypointPos.z, 1.0f, ABS(vz), 0.0f); + this->bobPhase += (0.05f + (Rand_ZeroOne() * 0.01f)); + if (distL1 == 0.0f) { + this->timer--; + if (this->timer == 0) { + EnBb_SetWaypoint(this, globalCtx); + EnBb_FaceWaypoint(this); + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000184); + this->timer = Rand_ZeroOne() * 30.0f + 40.0f; + } else { + if (this->moveMode != BBMOVE_NORMAL) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + } + this->actor.world.rot.y += 0x1F40; + } + this->moveMode = BBMOVE_NORMAL; + this->maxSpeed = 0.0f; + } else { + this->moveMode = BBMOVE_NOCLIP; + this->maxSpeed = 10.0f; + } + if (this->collider.base.atFlags & AT_HIT) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_BITE); + this->collider.base.atFlags &= ~AT_HIT; + } + this->actor.shape.rot.y = this->actor.world.rot.y; + } else if (Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f) == 0.0f) { + EnBb_FaceWaypoint(this); + } + SkelAnime_Update(&this->skelAnime); + if (((s32)this->skelAnime.curFrame == 0) && (Rand_ZeroOne() <= 0.1f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_LAUGH); + } + + if ((this->maxSpeed != 0.0f) && (((s32)this->skelAnime.curFrame == 0) || ((s32)this->skelAnime.curFrame == 5))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_MOUTH); + } else if (((s32)this->skelAnime.curFrame == 2) || ((s32)this->skelAnime.curFrame == 7)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_WING); + } +} + +void EnBb_InitGreen(EnBb* this, GlobalContext* globalCtx) { + Vec3f bobOffset = { 0.0f, 0.0f, 0.0f }; + + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + this->moveMode = BBMOVE_NOCLIP; + this->actionState = BBGREEN_FLAME_ON; + this->bobPhase = Rand_ZeroOne(); + this->actor.shape.rot.x = this->actor.shape.rot.z = 0; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + if (this->actor.params == ENBB_GREEN_BIG) { + EnBb_SetWaypoint(this, globalCtx); + EnBb_FaceWaypoint(this); + } + Matrix_Translate(this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, 0, MTXMODE_APPLY); + Matrix_RotateZ(this->bobPhase, MTXMODE_APPLY); + bobOffset.y = this->bobSize; + Matrix_MultVec3f(&bobOffset, &this->actor.world.pos); + this->targetActor = NULL; + this->action = BB_GREEN; + this->actor.speedXZ = 0.0f; + this->vFlameTimer = (Rand_ZeroOne() * 30.0f) + 180.0f; + EnBb_SetupAction(this, EnBb_Green); +} + +void EnBb_SetupGreen(EnBb* this) { + Animation_PlayLoop(&this->skelAnime, &object_Bb_Anim_000444); + this->moveMode = BBMOVE_NOCLIP; + this->actionState = BBGREEN_FLAME_ON; + this->targetActor = NULL; + this->action = BB_GREEN; + this->actor.speedXZ = 0.0f; + this->vFlameTimer = (Rand_ZeroOne() * 30.0f) + 180.0f; + this->actor.shape.rot.z = 0; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + EnBb_SetupAction(this, EnBb_Green); +} + +void EnBb_Green(EnBb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f bobOffset = { 0.0f, 0.0f, 0.0f }; + Vec3f nextPos = player->actor.world.pos; + + nextPos.y += 30.0f; + if (this->actor.params == ENBB_GREEN_BIG) { + if (this->actor.speedXZ == 0.0f) { + s16 pitch = Math_Vec3f_Pitch(&this->actor.home.pos, &this->waypointPos); + s16 yaw = Math_Vec3f_Yaw(&this->actor.home.pos, &this->waypointPos); + f32 vy = Math_SinS(pitch) * this->maxSpeed; + f32 vxz = Math_CosS(pitch) * this->maxSpeed; + f32 vz; + f32 vx; + f32 distL1; + + Math_SmoothStepToS(&this->actor.world.rot.y, yaw, 1, 0x3E8, 0); + vx = Math_SinS(this->actor.world.rot.y) * vxz; + distL1 = Math_CosS(this->actor.world.rot.y) * vxz; + vz = Math_SmoothStepToF(&this->actor.home.pos.x, this->waypointPos.x, 1.0f, ABS(vx), 0.0f); + vz += Math_SmoothStepToF(&this->actor.home.pos.y, this->waypointPos.y, 1.0f, ABS(vy), 0.0f); + vz += Math_SmoothStepToF(&this->actor.home.pos.z, this->waypointPos.z, 1.0f, ABS(distL1), 0.0f); + this->bobPhase += (0.05f + (Rand_ZeroOne() * 0.01f)); + if (vz == 0.0f) { + EnBb_SetWaypoint(this, globalCtx); + } + this->moveMode = BBMOVE_NOCLIP; + this->maxSpeed = 10.0f; + if (this->collider.base.atFlags & AT_HIT) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_BITE); + this->collider.base.atFlags &= ~AT_HIT; + } + if (Math_CosF(this->bobPhase) == 0.0f) { + if (this->charge) { + this->bobSpeedMod = Rand_ZeroOne(); + } else { + this->bobSpeedMod = Rand_ZeroOne() * 3.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_LAUGH); + } + } + this->actor.shape.rot.y = this->actor.world.rot.y; + } else if (Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f) == 0.0f) { + EnBb_FaceWaypoint(this); + } + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 0); + Math_SmoothStepToS(&this->actor.shape.rot.x, Math_Vec3f_Pitch(&this->actor.world.pos, &nextPos), 1, 0xFA0, 0); + } + SkelAnime_Update(&this->skelAnime); + if (Math_CosF(this->bobPhase) <= 0.002f) { + this->bobSpeedMod = Rand_ZeroOne() * 0.05f; + } + Matrix_Translate(this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, 0, MTXMODE_APPLY); + Matrix_RotateZ(this->bobPhase, MTXMODE_APPLY); + bobOffset.y = this->bobSize; + Matrix_MultVec3f(&bobOffset, &nextPos); + Math_SmoothStepToF(&this->actor.world.pos.x, nextPos.x, 1.0f, this->bobPhase * 0.75f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, nextPos.y, 1.0f, this->bobPhase * 0.75f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, nextPos.z, 1.0f, this->bobPhase * 0.75f, 0.0f); + this->bobPhase += 0.1f + this->bobSpeedMod; + if (Actor_GetCollidedExplosive(globalCtx, &this->collider.base) || (--this->vFlameTimer == 0)) { + this->actionState++; + this->timer = (Rand_ZeroOne() * 30.0f) + 60.0f; + if (this->vFlameTimer != 0) { + this->collider.base.acFlags &= ~AC_HIT; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_DOWN); + } + if (this->actionState != BBGREEN_FLAME_ON) { + this->timer--; + if (this->timer == 0) { + this->actionState = BBGREEN_FLAME_ON; + this->vFlameTimer = (Rand_ZeroOne() * 30.0f) + 180.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_UP); + } + Math_SmoothStepToF(&this->flameScaleY, 0.0f, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToF(&this->flameScaleX, 0.0f, 1.0f, 10.0f, 0.0f); + } else { + Math_SmoothStepToF(&this->flameScaleY, 80.0f, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToF(&this->flameScaleX, 100.0f, 1.0f, 10.0f, 0.0f); + } + if ((s32)this->skelAnime.curFrame == 5) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_WING); + } + if (((s32)this->skelAnime.curFrame == 0) && (Rand_ZeroOne() < 0.1f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_LAUGH); + } +} + +void EnBb_SetupStunned(EnBb* this) { + this->action = BB_STUNNED; + if (this->actor.params != ENBB_WHITE) { + if (this->actor.params != ENBB_RED) { + if (this->actor.params > ENBB_GREEN) { + this->actor.gravity = -2.0f; + this->actor.shape.yOffset = 1500.0f; + } + this->actor.speedXZ = 0.0f; + this->flameScaleX = 0.0f; + this->flameScaleY = 0.0f; + } else { + EnBb_KillFlameTrail(this); + } + } + switch (this->dmgEffect) { + case 8: + Actor_SetColorFilter(&this->actor, -0x8000, 0xC8, 0, 0x50); + break; + case 9: + this->fireIceTimer = 0x30; + case 15: + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Actor_SetColorFilter(&this->actor, 0, 0xB4, 0, 0x50); + break; + } + this->actor.bgCheckFlags &= ~1; + EnBb_SetupAction(this, EnBb_Stunned); +} + +void EnBb_Stunned(EnBb* this, GlobalContext* globalCtx) { + s16 yawDiff = this->actor.world.rot.y - this->actor.wallYaw; + + if (this->actor.bgCheckFlags & 8) { + if (ABS(yawDiff) > 0x4000) { + this->actor.world.rot.y = this->actor.wallYaw + this->actor.wallYaw - this->actor.world.rot.y - 0x8000; + } + this->actor.bgCheckFlags &= ~8; + } + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + if (this->actor.velocity.y < -14.0f) { + this->actor.velocity.y *= -0.4f; + } else { + this->actor.velocity.y = 0.0f; + } + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 7.0f, 2, 2.0f, 0, 0, 0); + } + if (this->actor.colorFilterTimer == 0) { + this->actor.shape.yOffset = 200.0f; + if (this->actor.colChkInfo.health != 0) { + if ((this->actor.params == ENBB_GREEN) || (this->actor.params == ENBB_GREEN_BIG)) { + EnBb_SetupGreen(this); + } else if (this->actor.params == ENBB_WHITE) { + this->action = BB_WHITE; + EnBb_SetupAction(this, EnBb_White); + } else { + EnBb_SetupDown(this); + } + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + EnBb_SetupDeath(this, globalCtx); + } + } +} + +void EnBb_CollisionCheck(EnBb* this, GlobalContext* globalCtx) { + if (this->collider.base.atFlags & AT_BOUNCED) { + this->collider.base.atFlags &= ~AT_BOUNCED; + if (this->action != BB_DOWN) { + if (this->actor.params >= ENBB_RED) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer + 0x8000; + if (this->actor.params == ENBB_RED) { + EnBb_KillFlameTrail(this); + } + EnBb_SetupDown(this); + return; + } + this->actionVar2 = 1; + } + } + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + this->dmgEffect = this->actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 0); + switch (this->dmgEffect) { + case 7: + this->actor.freezeTimer = this->collider.elements[0].info.acHitInfo->toucher.damage; + case 5: + this->fireIceTimer = 0x30; + //! @bug + //! Setting fireIceTimer here without calling Actor_SetColorFilter causes a crash if the bubble is + //! killed in a single hit by an attack with damage effect 5 or 7 while actor updating is halted. Using + //! Din's Fire on a white bubble will do just that. The mechanism is complex and described below. + goto block_15; + case 6: + this->actor.freezeTimer = this->collider.elements[0].info.acHitInfo->toucher.damage; + break; + case 8: + case 9: + case 15: + if (this->action != BB_STUNNED) { + Actor_ApplyDamage(&this->actor); + EnBb_SetupStunned(this); + } + break; + default: + block_15: + if ((this->dmgEffect == 14) || (this->dmgEffect == 12) || (this->dmgEffect == 11) || + (this->dmgEffect == 10) || (this->dmgEffect == 7) || (this->dmgEffect == 5)) { + if ((this->action != BB_DOWN) || (this->timer < 190)) { + Actor_ApplyDamage(&this->actor); + } + if ((this->action != BB_DOWN) && (this->actor.params != ENBB_WHITE)) { + EnBb_SetupDown(this); + } + } else { + if (((this->action == BB_DOWN) && (this->timer < 190)) || + ((this->actor.params != ENBB_WHITE) && (this->flameScaleX < 20.0f))) { + Actor_ApplyDamage(&this->actor); + } else { + this->collider.base.acFlags |= AC_HIT; + } + } + if (this->actor.colChkInfo.health == 0) { + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->actor.params == ENBB_RED) { + EnBb_KillFlameTrail(this); + } + EnBb_SetupDeath(this, globalCtx); + //! @bug + //! Because Din's Fire kills the bubble in a single hit, Actor_SetColorFilter is never called and + //! colorFilterParams is never set. And because Din's Fire halts updating during its cutscene, + //! EnBb_Death doesn't kill the bubble on the next frame like it should. This combines with + //! the bug in EnBb_Draw below to crash the game. + } else if ((this->actor.params == ENBB_WHITE) && + ((this->action == BB_WHITE) || (this->action == BB_STUNNED))) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); + this->actor.speedXZ = -8.0f; + this->maxSpeed = 0.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_DAMAGE); + } else if (((this->action == BB_DOWN) && (this->timer < 190)) || + ((this->actor.params != ENBB_WHITE) && (this->flameScaleX < 20.0f))) { + EnBb_SetupDamage(this); + } + case 13: + break; + } + } +} + +void EnBb_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnBb* this = (EnBb*)thisx; + Vec3f sp4C = { 0.0f, 0.0f, 0.0f }; + Vec3f sp40 = { 0.0f, -0.6f, 0.0f }; + Color_RGBA8 sp3C = { 0, 0, 255, 255 }; + Color_RGBA8 sp38 = { 0, 0, 0, 0 }; + f32 sp34 = -15.0f; + + if (this->actor.params <= ENBB_BLUE) { + EnBb_CollisionCheck(this, globalCtx); + } + if (this->actor.colChkInfo.damageEffect != 0xD) { + this->actionFunc(this, globalCtx); + if ((this->actor.params <= ENBB_BLUE) && (this->actor.speedXZ >= -6.0f) && + ((this->actor.flags & ACTOR_FLAG_15) == 0)) { + Actor_MoveForward(&this->actor); + } + if (this->moveMode == BBMOVE_NORMAL) { + if ((this->actor.world.pos.y - 20.0f) <= this->actor.floorHeight) { + sp34 = 20.0f; + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, sp34, 25.0f, 20.0f, 5); + } + this->actor.focus.pos = this->actor.world.pos; + this->collider.elements->dim.worldSphere.center.x = this->actor.world.pos.x; + this->collider.elements->dim.worldSphere.center.y = + this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y); + this->collider.elements->dim.worldSphere.center.z = this->actor.world.pos.z; + + if ((this->action > BB_KILL) && ((this->actor.speedXZ != 0.0f) || (this->action == BB_GREEN))) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if ((this->action > BB_FLAME_TRAIL) && + ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000)) && + (this->moveMode != BBMOVE_HIDDEN)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +void EnBb_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnBb* this = (EnBb*)thisx; + + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 4, 15, 15, dList, BODYBREAK_OBJECT_DEFAULT); +} + +static Vec3f sFireIceOffsets[] = { + { 13.0f, 10.0f, 0.0f }, { 5.0f, 25.0f, 5.0f }, { -5.0f, 25.0f, 5.0f }, { -13.0f, 10.0f, 0.0f }, + { 5.0f, 25.0f, -5.0f }, { -5.0f, 25.0f, -5.0f }, { 0.0f, 10.0f, -13.0f }, { 5.0f, 0.0f, 5.0f }, + { 5.0f, 0.0f, -5.0f }, { 0.0f, 10.0f, 13.0f }, { -5.0f, 0.0f, 5.0f }, { -5.0f, 0.0f, -5.0f }, +}; + +void EnBb_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBb* this = (EnBb*)thisx; + Vec3f blureBase1 = { 0.0f, 5000.0f, 0.0f }; + Vec3f blureBase2 = { 0.0f, 2000.0f, 0.0f }; + Vec3f blureVtx1; + Vec3f blureVtx2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bb.c", 2044); + + blureBase1.z = this->maxSpeed * 80.0f; + blureBase2.z = this->maxSpeed * 80.0f; + if (this->moveMode != BBMOVE_HIDDEN) { + if (this->actor.params <= ENBB_BLUE) { + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, EnBb_PostLimbDraw, + this); + + if (this->fireIceTimer != 0) { + this->actor.colorFilterTimer++; + //! @bug: + //! The purpose of this is to counteract Actor_UpdateAll decrementing colorFilterTimer. However, + //! the above bugs mean unk_2A8 can be nonzero without damage effects ever having been set. + //! This routine will then increment colorFilterTimer, and on the next frame Actor_Draw will try + //! to draw the unset colorFilterParams. This causes a divide-by-zero error, crashing the game. + if (1) {} + this->fireIceTimer--; + if ((this->fireIceTimer % 4) == 0) { + Vec3f sp70; + s32 index = this->fireIceTimer >> 2; + + sp70.x = this->actor.world.pos.x + sFireIceOffsets[index].x; + sp70.y = this->actor.world.pos.y + sFireIceOffsets[index].y; + sp70.z = this->actor.world.pos.z + sFireIceOffsets[index].z; + + if ((this->dmgEffect != 7) && (this->dmgEffect != 5)) { + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &sp70, 0x96, 0x96, 0x96, 0xFA, 0xEB, + 0xF5, 0xFF, 0.8f); + } else { + sp70.y -= 17.0f; + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &sp70, 0x28, 1, 0, -1); + } + } + } + Matrix_Translate(0.0f, this->flameScaleX * -40.0f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, -40.0f, 0.0f, MTXMODE_APPLY); + } + if (this->actor.params != ENBB_WHITE) { + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + ((globalCtx->gameplayFrames + (this->flameScrollMod * 10)) * + (-20 - (this->flameScrollMod * -2))) % + 0x200, + 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, this->flamePrimBlue, this->flamePrimAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, this->flameEnvColor.r, this->flameEnvColor.g, this->flameEnvColor.b, 0); + Matrix_RotateY(((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y + 0x8000)) * + (M_PI / 0x8000), + MTXMODE_APPLY); + Matrix_Scale(this->flameScaleX * 0.01f, this->flameScaleY * 0.01f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bb.c", 2106), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } else { + Matrix_MultVec3f(&blureBase1, &blureVtx1); + Matrix_MultVec3f(&blureBase2, &blureVtx2); + if ((this->maxSpeed != 0.0f) && (this->action == BB_WHITE) && !(globalCtx->gameplayFrames & 1) && + (this->actor.colChkInfo.health != 0)) { + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIdx), &blureVtx1, &blureVtx2); + } else if (this->action != BB_WHITE) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIdx)); + } + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bb.c", 2127); +} diff --git a/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.h b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.h new file mode 100644 index 000000000..e12306fd2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bb/z_en_bb.h @@ -0,0 +1,59 @@ +#ifndef Z_EN_BB_H +#define Z_EN_BB_H + +#include "ultra64.h" +#include "global.h" + +struct EnBb; + +typedef void (*EnBbActionFunc)(struct EnBb*, GlobalContext*); + +typedef struct EnBb { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s morphTable[16]; + /* 0x0250 */ s32 action; + /* 0x0254 */ s32 unk_254; // unused + /* 0x0258 */ EnBbActionFunc actionFunc; + /* 0x025C */ s32 moveMode; + /* 0x0260 */ s32 timer; + /* 0x0264 */ s32 unk_264; // unused + /* 0x0268 */ s16 actionState; + /* 0x026A */ s16 charge; + /* 0x026C */ s16 actionVar1; // index for flame trail, phase of small hop when blue finds a bomb + /* 0x026E */ s16 actionVar2; // move yaw for blue, flame timer for green, maxAlpha for flame trail + /* 0x0270 */ s16 flameScrollMod; + /* 0x0274 */ f32 bobPhase; + /* 0x0278 */ f32 bobSize; + /* 0x027C */ f32 maxSpeed; + /* 0x0280 */ f32 flyHeightMod; + /* 0x027C */ f32 bobSpeedMod; // y speed for blue, phase speed for green + /* 0x0288 */ f32 flameScaleY; + /* 0x028C */ f32 flameScaleX; + /* 0x0290 */ Vec3f waypointPos; + /* 0x029C */ u8 path; + /* 0x029D */ u8 waypoint; + /* 0x029E */ u8 flamePrimBlue; + /* 0x029F */ u8 flamePrimAlpha; + /* 0x02A0 */ Color_RGB8 flameEnvColor; + /* 0x02A4 */ s32 blureIdx; + /* 0x02A8 */ s16 fireIceTimer; + /* 0x02AA */ u8 dmgEffect; + /* 0x02AC */ ColliderJntSph collider; + /* 0x02CC */ ColliderJntSphElement elements[1]; + /* 0x030C */ BodyBreak bodyBreak; + /* 0x0324 */ Actor* targetActor; +} EnBb; // size = 0x0328 + +typedef enum { + ENBB_GREEN_BIG = -5, + ENBB_GREEN, + ENBB_WHITE, + ENBB_RED, + ENBB_BLUE, + ENBB_FLAME_TRAIL, + ENBB_KILL_TRAIL = 11 +} EnBbType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c new file mode 100644 index 000000000..f88ec0917 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.c @@ -0,0 +1,230 @@ +/* + * File: z_en_bdfire.c + * Overlay: ovl_En_Bdfire + * Description: King Dodongo's Fire Breath + */ + +#include "z_en_bdfire.h" +#include "objects/object_kingdodongo/object_kingdodongo.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnBdfire_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBdfire_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBdfire_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBdfire_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBdfire_DrawFire(EnBdfire* this, GlobalContext* globalCtx); +void func_809BC2A4(EnBdfire* this, GlobalContext* globalCtx); +void func_809BC598(EnBdfire* this, GlobalContext* globalCtx); + +const ActorInit En_Bdfire_InitVars = { + 0, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_KINGDODONGO, + sizeof(EnBdfire), + (ActorFunc)EnBdfire_Init, + (ActorFunc)EnBdfire_Destroy, + (ActorFunc)EnBdfire_Update, + (ActorFunc)EnBdfire_Draw, + NULL, +}; + +void EnBdfire_SetupAction(EnBdfire* this, EnBdfireActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnbdFire_SetupDraw(EnBdfire* this, EnBdfireDrawFunc drawFunc) { + this->drawFunc = drawFunc; +} + +void EnBdfire_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBdfire* this = (EnBdfire*)thisx; + s32 pad; + + Actor_SetScale(&this->actor, 0.6f); + EnbdFire_SetupDraw(this, EnBdfire_DrawFire); + if (this->actor.params < 0) { + EnBdfire_SetupAction(this, func_809BC2A4); + this->actor.scale.x = 2.8f; + this->unk_154 = 90; + Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, 300); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfoNoGlow); + } else { + EnBdfire_SetupAction(this, func_809BC598); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + this->actor.speedXZ = 30.0f; + this->unk_154 = (25 - (s32)(this->actor.params * 0.8f)); + if (this->unk_154 < 0) { + this->unk_154 = 0; + } + this->unk_188 = 4.2000003f - (this->actor.params * 0.25f * 0.6f); + + if (this->unk_188 < 0.90000004f) { + this->unk_188 = 0.90000004f; + } + this->unk_18C = 255.0f - (this->actor.params * 10.0f); + if (this->unk_18C < 20.0f) { + this->unk_18C = 20.0f; + } + this->unk_156 = (Rand_ZeroOne() * 8.0f); + } +} + +void EnBdfire_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBdfire* this = (EnBdfire*)thisx; + + if (this->actor.params < 0) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + } +} + +void func_809BC2A4(EnBdfire* this, GlobalContext* globalCtx) { + BossDodongo* kingDodongo; + s32 temp; + + kingDodongo = (BossDodongo*)this->actor.parent; + this->actor.world.pos.x = kingDodongo->firePos.x; + this->actor.world.pos.y = kingDodongo->firePos.y; + this->actor.world.pos.z = kingDodongo->firePos.z; + if (kingDodongo->unk_1E2 == 0) { + Math_SmoothStepToF(&this->actor.scale.x, 0.0f, 1.0f, 0.6f, 0.0f); + if (Math_SmoothStepToF(&this->unk_18C, 0.0f, 1.0f, 20.0f, 0.0f) == 0.0f) { + Actor_Kill(&this->actor); + } + } else { + if (this->unk_154 < 70) { + Math_SmoothStepToF(&this->unk_18C, 128.0f, 0.1f, 1.5f, 0.0f); + Math_SmoothStepToF(&this->unk_190, 255.0f, 1.0f, 3.8249998f, 0.0f); + Math_SmoothStepToF(&this->unk_194, 100.0f, 1.0f, 1.5f, 0.0f); + } + if (this->unk_154 == 0) { + temp = 0; + } else { + this->unk_154--; + temp = this->unk_154; + } + if (temp == 0) { + Math_SmoothStepToF(&this->actor.scale.x, 0.0f, 1.0f, 0.3f, 0.0f); + Math_SmoothStepToF(&this->unk_190, 0.0f, 1.0f, 25.5f, 0.0f); + Math_SmoothStepToF(&this->unk_194, 0.0f, 1.0f, 10.0f, 0.0f); + if (Math_SmoothStepToF(&this->unk_18C, 0.0f, 1.0f, 10.0f, 0.0f) == 0.0f) { + Actor_Kill(&this->actor); + } + } + Actor_SetScale(&this->actor, this->actor.scale.x); + Lights_PointSetColorAndRadius(&this->lightInfoNoGlow, this->unk_190, this->unk_194, 0, 300); + } +} + +void func_809BC598(EnBdfire* this, GlobalContext* globalCtx) { + s16 phi_v1_2; + Player* player = GET_PLAYER(globalCtx); + f32 distToBurn; + BossDodongo* bossDodongo; + s16 i; + s16 phi_v1; + s32 temp; + + bossDodongo = ((BossDodongo*)this->actor.parent); + this->unk_158 = bossDodongo->unk_1A2; + phi_v1_2 = 0; + if (this->actor.params == 0) { + Audio_PlaySoundGeneral(NA_SE_EN_DODO_K_FIRE - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + Math_SmoothStepToF(&this->actor.scale.x, this->unk_188, 0.3f, 0.5f, 0.0f); + Actor_SetScale(&this->actor, this->actor.scale.x); + if (this->actor.world.pos.x < -1390.0f) { + if (this->actor.velocity.x < -10.0f) { + this->actor.world.pos.x = -1390.0f; + phi_v1_2 = 1; + } + } + if ((this->actor.world.pos.x > -390.0f) && (this->actor.velocity.x > 10.0f)) { + this->actor.world.pos.x = -390.0f; + phi_v1_2 = 1; + } + if ((this->actor.world.pos.z > -2804.0f) && (this->actor.velocity.z > 10.0f)) { + this->actor.world.pos.z = -2804.0f; + phi_v1_2 = 1; + } + if ((this->actor.world.pos.z < -3804.0f) && (this->actor.velocity.z < -10.0f)) { + this->actor.world.pos.z = -3804.0f; + phi_v1_2 = 1; + } + if (phi_v1_2 != 0) { + if (this->unk_158 == 0) { + this->actor.world.rot.y += 0x4000; + } else { + this->actor.world.rot.y -= 0x4000; + } + } + if (this->unk_154 == 0) { + temp = 0; + } else { + this->unk_154--; + temp = this->unk_154; + } + if (temp == 0) { + Math_SmoothStepToF(&this->unk_18C, 0.0f, 1.0f, 10.0f, 0.0f); + if (this->unk_18C < 10.0f) { + Actor_Kill(&this->actor); + return; + } + } else if (!player->isBurning) { + distToBurn = (this->actor.scale.x * 130.0f) / 4.2000003f; + if (this->actor.xyzDistToPlayerSq < SQ(distToBurn)) { + for (i = 0; i < 18; i++) { + player->flameTimers[i] = Rand_S16Offset(0, 200); + } + player->isBurning = true; + func_8002F6D4(globalCtx, &this->actor, 20.0f, this->actor.world.rot.y, 0.0f, 8); + osSyncPrintf("POWER\n"); + } + } +} + +void EnBdfire_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBdfire* this = (EnBdfire*)thisx; + + this->unk_156++; + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); +} + +void EnBdfire_DrawFire(EnBdfire* this, GlobalContext* globalCtx) { + static void* D_809BCB10[] = { + object_kingdodongo_Tex_0264E0, object_kingdodongo_Tex_0274E0, object_kingdodongo_Tex_0284E0, + object_kingdodongo_Tex_0294E0, object_kingdodongo_Tex_02A4E0, object_kingdodongo_Tex_02B4E0, + object_kingdodongo_Tex_02C4E0, object_kingdodongo_Tex_02D4E0, + }; + s16 temp; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bdfire.c", 612); + temp = this->unk_156 & 7; + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + func_80094BC4(globalCtx->state.gfxCtx); + POLY_XLU_DISP = func_80094968(POLY_XLU_DISP); + gDPSetCombineLERP(POLY_XLU_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, + ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, + ENVIRONMENT); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 100, (s8)this->unk_18C); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 0, 0, 0); + gSPSegment(POLY_XLU_DISP++, 8, SEGMENTED_TO_VIRTUAL(D_809BCB10[temp])); + Matrix_Translate(0.0f, 11.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bdfire.c", 647), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_kingdodongo_DL_01D950); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bdfire.c", 651); +} + +void EnBdfire_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBdfire* this = (EnBdfire*)thisx; + + this->drawFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.h b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.h new file mode 100644 index 000000000..190de9b49 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bdfire/z_en_bdfire.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_BDFIRE_H +#define Z_EN_BDFIRE_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.h" + +struct EnBdfire; + +typedef void (*EnBdfireActionFunc)(struct EnBdfire*, GlobalContext*); +typedef void (*EnBdfireDrawFunc)(struct EnBdfire*, GlobalContext*); + +typedef struct EnBdfire { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnBdfireActionFunc actionFunc; + /* 0x0150 */ EnBdfireDrawFunc drawFunc; + /* 0x0154 */ s16 unk_154; + /* 0x0156 */ s16 unk_156; + /* 0x0158 */ s16 unk_158; + /* 0x015A */ char unk_15A[0x2E]; + /* 0x0188 */ f32 unk_188; + /* 0x018C */ f32 unk_18C; + /* 0x0190 */ f32 unk_190; + /* 0x0194 */ f32 unk_194; + /* 0x0198 */ char unk_198[0x38]; + /* 0x01D0 */ LightNode* lightNode; + /* 0x01D4 */ LightInfo lightInfoNoGlow; +} EnBdfire; // size = 0x01E4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c new file mode 100644 index 000000000..3e825d06f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.c @@ -0,0 +1,901 @@ +#include "z_en_bigokuta.h" +#include "objects/object_bigokuta/object_bigokuta.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnBigokuta_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBigokuta_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBigokuta_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBigokuta_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809BD318(EnBigokuta* this); +void func_809BD3E0(EnBigokuta* this); +void func_809BDF34(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BD84C(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BD8DC(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BDAE8(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BDB90(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BDC08(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BE3E4(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BE4A4(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BE518(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BCF68(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BDFC8(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BE26C(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BE180(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BE058(EnBigokuta* this, GlobalContext* globalCtx); +void func_809BD1C8(EnBigokuta* this, GlobalContext* globalCtx); + +static Color_RGBA8 sEffectPrimColor = { 255, 255, 255, 255 }; +static Color_RGBA8 sEffectEnvColor = { 100, 255, 255, 255 }; +static Vec3f sEffectPosAccel = { 0.0f, 0.0f, 0.0f }; + +const ActorInit En_Bigokuta_InitVars = { + ACTOR_EN_BIGOKUTA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BIGOKUTA, + sizeof(EnBigokuta), + (ActorFunc)EnBigokuta_Init, + (ActorFunc)EnBigokuta_Destroy, + (ActorFunc)EnBigokuta_Update, + (ActorFunc)EnBigokuta_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementInit[1] = { + { + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 45, -30 }, 75 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementInit), + sJntSphElementInit, +}; + +static ColliderCylinderInit sCylinderInit[] = { + { { + COLTYPE_HARD, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x08 }, + { 0xFFCFFFE7, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 50, 100, 0, { 30, 0, 12 } } }, + { { + COLTYPE_HARD, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x20000000, 0x00, 0x08 }, + { 0xFFCFFFE7, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 50, 100, 0, { -30, 0, 12 } } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit[] = { 4, 130, 120, 200 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(gravity, -1, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x59, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 33, ICHAIN_STOP), +}; + +// possibly color data +static s32 sUnused[] = { 0xFFFFFFFF, 0x969696FF }; + +void EnBigokuta_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBigokuta* this = (EnBigokuta*)thisx; + s32 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_bigokuta_Skel_006BC0, &object_bigokuta_Anim_0014B8, + this->jointTable, this->morphTable, 20); + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, &this->element); + + this->collider.elements->dim.worldSphere.radius = this->collider.elements->dim.modelSphere.radius; + + for (i = 0; i < ARRAY_COUNT(sCylinderInit); i++) { + Collider_InitCylinder(globalCtx, &this->cylinder[i]); + Collider_SetCylinder(globalCtx, &this->cylinder[i], &this->actor, &sCylinderInit[i]); + } + + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, sColChkInfoInit); + + this->unk_194 = 1; + + if (this->actor.params == 0) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + func_809BD318(this); + } else { + func_809BD3E0(this); + this->unk_19A = 0; + this->unk_196 = 1; + this->actor.home.pos.y = -1025.0f; + } +} + +void EnBigokuta_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBigokuta* this = (EnBigokuta*)thisx; + s32 i; + + Collider_DestroyJntSph(globalCtx, &this->collider); + for (i = 0; i < ARRAY_COUNT(this->cylinder); i++) { + Collider_DestroyCylinder(globalCtx, &this->cylinder[i]); + } +} + +void func_809BCE3C(EnBigokuta* this) { + this->actor.world.rot.y = this->actor.shape.rot.y + this->unk_194 * -0x4000; + this->actor.world.pos.x = Math_SinS(this->actor.world.rot.y) * 263.0f + this->actor.home.pos.x; + this->actor.world.pos.z = Math_CosS(this->actor.world.rot.y) * 263.0f + this->actor.home.pos.z; +} + +void func_809BCEBC(EnBigokuta* this, GlobalContext* globalCtx) { + Vec3f pos; + f32 yDistFromHome = this->actor.world.pos.y - this->actor.home.pos.y; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.home.pos.y + 3.0f; + pos.z = this->actor.world.pos.z; + + if (((globalCtx->gameplayFrames % 7) == 0) && (yDistFromHome <= 0.0f) && (yDistFromHome > -100.0f)) { + EffectSsGRipple_Spawn(globalCtx, &pos, 800, 1300, 0); + } +} + +void func_809BCF68(EnBigokuta* this, GlobalContext* globalCtx) { + Vec3f effectPos; + s16 rot; + + if (globalCtx->gameplayFrames & 1) { + rot = Rand_S16Offset(0x1200, 0xC00) + this->actor.shape.rot.y - this->unk_194 * 0xA00; + } else { + rot = this->actor.shape.rot.y - this->unk_194 * 0xA00 - Rand_S16Offset(0x1200, 0xC00); + } + if (this->actionFunc != func_809BE4A4) { + if (this->actionFunc == func_809BE3E4 || (globalCtx->gameplayFrames & 2)) { + effectPos.x = this->actor.world.pos.x - Math_SinS(rot) * 80.0f; + effectPos.z = this->actor.world.pos.z - Math_CosS(rot) * 80.0f; + effectPos.y = this->actor.home.pos.y + 1.0f; + EffectSsGRipple_Spawn(globalCtx, &effectPos, 100, 500, 0); + } else { + effectPos.x = this->actor.world.pos.x - Math_SinS(rot) * 120.0f; + effectPos.z = this->actor.world.pos.z - Math_CosS(rot) * 120.0f; + effectPos.y = this->actor.home.pos.y + 5.0f; + } + } else { + effectPos.x = this->actor.world.pos.x - Math_SinS(rot) * 50.0f; + effectPos.z = this->actor.world.pos.z - Math_CosS(rot) * 50.0f; + effectPos.y = this->actor.home.pos.y + 1.0f; + EffectSsGRipple_Spawn(globalCtx, &effectPos, 100, 500, 0); + } + EffectSsGSplash_Spawn(globalCtx, &effectPos, NULL, NULL, 1, 800); + if (this->actionFunc != func_809BE4A4) { + func_8002F974(&this->actor, NA_SE_EN_DAIOCTA_SPLASH - SFX_FLAG); + } +} + +void func_809BD1C8(EnBigokuta* this, GlobalContext* globalCtx) { + s32 i; + Vec3f effectPos; + + effectPos.y = this->actor.world.pos.y; + + for (i = 0; i < 4; i++) { + effectPos.x = ((i >= 2) ? 1 : -1) * 60.0f + this->actor.world.pos.x; + effectPos.z = ((i & 1) ? 1 : -1) * 60.0f + this->actor.world.pos.z; + EffectSsGSplash_Spawn(globalCtx, &effectPos, NULL, NULL, 1, 2000); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_LAND_WATER); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOLON_LAND_BIG); + func_80033E88(&this->actor, globalCtx, 0xA, 8); +} + +void func_809BD2E4(EnBigokuta* this) { + Actor* parent = this->actor.parent; + + if (parent != NULL) { + Math_ScaledStepToS(&parent->world.rot.y, this->unk_19A, 0x10); + } +} + +void func_809BD318(EnBigokuta* this) { + Animation_PlayLoop(&this->skelAnime, &object_bigokuta_Anim_0014B8); + this->unk_19A = 0; + this->actor.home.pos.y = -1025.0f; + this->unk_196 = 121; + this->actionFunc = func_809BD84C; +} + +void func_809BD370(EnBigokuta* this) { + this->unk_196 = 21; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actionFunc = func_809BD8DC; +} + +void func_809BD3AC(EnBigokuta* this) { + this->actor.world.pos.x = this->actor.home.pos.x + 263.0f; + this->unk_196 = 10; + this->actionFunc = func_809BDAE8; + this->actor.world.pos.y = this->actor.home.pos.y; +} + +void func_809BD3E0(EnBigokuta* this) { + this->unk_196 = 40; + this->actionFunc = func_809BDB90; +} + +void func_809BD3F8(EnBigokuta* this) { + Animation_MorphToLoop(&this->skelAnime, &object_bigokuta_Anim_001CA4, -5.0f); + this->unk_196 = 350; + this->unk_198 = 80; + this->unk_19A = this->unk_194 * -0x200; + func_809BCE3C(this); + this->cylinder[0].base.atFlags |= AT_ON; + this->collider.base.acFlags |= AC_ON; + this->actionFunc = func_809BDC08; +} + +void func_809BD47C(EnBigokuta* this) { + this->unk_196 = 16; + this->collider.base.acFlags &= ~AC_ON; + this->actor.colorFilterTimer = 0; + this->actionFunc = func_809BDF34; +} + +void func_809BD4A4(EnBigokuta* this) { + Animation_MorphToLoop(&this->skelAnime, &object_bigokuta_Anim_0014B8, -5.0f); + this->unk_195 = true; + this->actor.world.rot.x = this->actor.shape.rot.y + 0x8000; + this->unk_19A = this->unk_194 * 0x200; + this->collider.base.acFlags &= ~AC_ON; + this->cylinder[0].base.atFlags |= AT_ON; + this->actionFunc = func_809BDFC8; +} + +void func_809BD524(EnBigokuta* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_bigokuta_Anim_000D1C, -5.0f); + this->unk_196 = 80; + this->unk_19A = 0; + this->cylinder[0].base.atFlags |= AT_ON; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_MAHI); + if (this->collider.elements->info.acHitInfo->toucher.dmgFlags & 1) { + this->unk_195 = true; + this->unk_196 = 20; + } else { + this->unk_195 = false; + this->unk_196 = 80; + } + Actor_SetColorFilter(&this->actor, 0, 255, 0, this->unk_196); + this->actionFunc = func_809BE058; +} + +void func_809BD5E0(EnBigokuta* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_bigokuta_Anim_000444, -5.0f); + this->unk_196 = 24; + this->unk_19A = 0; + this->cylinder[0].base.atFlags &= ~AT_ON; + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 24); + this->actionFunc = func_809BE180; +} +void func_809BD658(EnBigokuta* this) { + + Animation_MorphToPlayOnce(&this->skelAnime, &object_bigokuta_Anim_000A74, -5.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_DEAD2); + this->unk_196 = 38; + this->unk_198 = 10; + this->actionFunc = func_809BE26C; +} + +void func_809BD6B8(EnBigokuta* this) { + if (!this->unk_195) { + if (Rand_ZeroOne() < 0.5f) { + this->unk_196 = 24; + } else { + this->unk_196 = 28; + } + } else { + if (ABS(this->actor.shape.rot.y - this->actor.yawTowardsPlayer) >= 0x4000) { + this->unk_196 = 4; + } else { + this->unk_196 = 0; + } + } + this->unk_19A = 0; + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = func_809BE3E4; +} + +void func_809BD768(EnBigokuta* this) { + this->unk_194 = Rand_ZeroOne() < 0.5f ? -1 : 1; + this->unk_19A = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->cylinder[0].base.atFlags &= ~AT_ON; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_SINK); + this->actionFunc = func_809BE4A4; +} + +void func_809BD7F0(EnBigokuta* this, GlobalContext* globalCtx) { + this->actor.world.rot.y = Actor_WorldYawTowardPoint(&GET_PLAYER(globalCtx)->actor, &this->actor.home.pos); + this->actor.shape.rot.y = this->actor.world.rot.y + (this->unk_194 * 0x4000); + func_809BCE3C(this); + this->actionFunc = func_809BE518; +} + +void func_809BD84C(EnBigokuta* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + this->unk_196--; + + if (this->unk_196 == 13 || this->unk_196 == -20) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_VOICE); + } + if (this->unk_196 == 1) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + if (this->actor.params == 1) { + func_809BD370(this); + } +} + +void func_809BD8DC(EnBigokuta* this, GlobalContext* globalCtx) { + Vec3f effectPos; + + this->unk_196--; + + if (this->unk_196 >= 0) { + Math_StepToF(&this->actor.world.pos.x, this->actor.home.pos.x + 263.0f, 263.0f / 21); + + if (this->unk_196 < 14) { + this->actor.world.pos.y = sinf(this->unk_196 * (M_PI / 28)) * 200.0f + this->actor.home.pos.y; + } else { + this->actor.world.pos.y = + sinf((this->unk_196 - 7) * (M_PI / 14)) * 130.0f + (this->actor.home.pos.y + 70.0f); + } + if (this->unk_196 == 0) { + effectPos.x = this->actor.world.pos.x + 40.0f; + effectPos.y = this->actor.world.pos.y; + effectPos.z = this->actor.world.pos.z - 70.0f; + EffectSsGSplash_Spawn(globalCtx, &effectPos, NULL, NULL, 1, 2000); + effectPos.x = this->actor.world.pos.x - 40.0f; + EffectSsGSplash_Spawn(globalCtx, &effectPos, NULL, NULL, 1, 2000); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_LAND_WATER); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOLON_LAND_BIG); + func_800AA000(0.0f, 0xFF, 0x14, 0x96); + } + } else if (this->unk_196 < -1) { + this->actor.world.pos.y = this->actor.home.pos.y - (sinf((this->unk_196 + 1) * (M_PI / 10)) * 20.0f); + if (this->unk_196 == -10) { + func_809BD3AC(this); + } + } +} + +void func_809BDAE8(EnBigokuta* this, GlobalContext* globalCtx) { + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y + 0x4000, 0x400)) { + if (this->unk_196 != 0) { + this->unk_196--; + } + if (this->unk_196 == 0) { + func_809BCE3C(this); + this->actor.home.pos.y = this->actor.world.pos.y; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + this->actor.params = 2; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_VOICE); + func_809BD3E0(this); + } + } +} + +void func_809BDB90(EnBigokuta* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_196 != 0) { + this->unk_196--; + } + if (this->unk_196 == 0) { + if (this->actor.params == 3) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + func_809BD3F8(this); + } +} + +void func_809BDC08(EnBigokuta* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_v0; + s16 pad; + s16 phi_v1; + Vec3f sp28; + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_BUBLE); + } + + if (this->unk_196 < 0) { + this->actor.shape.rot.y += this->unk_194 * 0x200; + func_809BCE3C(this); + this->unk_196++; + if (this->unk_196 == 0) { + this->unk_196 = 350; + } + func_809BCF68(this, globalCtx); + return; + } + + phi_v1 = (Actor_WorldDistXZToPoint(&player->actor, &this->actor.home.pos) - 180.0f) * (8.0f / 15); + func_8002DBD0(&this->actor, &sp28, &player->actor.world.pos); + if (fabsf(sp28.x) > 263.0f || ((sp28.z > 0.0f) && !Actor_IsFacingPlayer(&this->actor, 0x1B00) && + !Player_IsFacingActor(&this->actor, 0x2000, globalCtx))) { + phi_v1 -= 0x80; + if (this->unk_196 != 0) { + this->unk_196--; + } + } + + if ((this->actor.xzDistToPlayer < 250.0f) && !Actor_IsFacingPlayer(&this->actor, 0x6000)) { + if (this->unk_198 != 0) { + this->unk_198--; + } + if (this->actor.xzDistToPlayer < 180.0f) { + phi_v1 += 0x20; + } + } else { + this->unk_198 = 80; + } + if (this->actor.colChkInfo.health == 1) { + phi_v1 = (phi_v1 + 0x130) * 1.1f; + } else { + phi_v1 += 0x130; + } + this->actor.shape.rot.y += phi_v1 * this->unk_194; + func_809BCE3C(this); + func_809BCF68(this, globalCtx); + if (this->unk_198 == 0) { + func_809BD768(this); + } else if (this->unk_196 == 0) { + func_809BD4A4(this); + } else if (this->unk_195) { + phi_v0 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (phi_v0 < 0) { + phi_v0 = -phi_v0; + } + if (phi_v0 < 0x4100 && phi_v0 > 0x3F00) { + if (Rand_ZeroOne() < 0.6f) { + this->unk_196 = 0; + func_809BD4A4(this); + } + this->unk_195 = false; + } + } +} + +void func_809BDF34(EnBigokuta* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_196 != 0) { + this->unk_196--; + } + this->actor.world.pos.y = (sinf(this->unk_196 * (M_PI / 16)) * 100.0f) + this->actor.home.pos.y; + if (this->unk_196 == 0) { + func_809BD1C8(this, globalCtx); + func_809BD3F8(this); + } +} + +void func_809BDFC8(EnBigokuta* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_196 != 0) { + this->unk_196--; + } + if (this->unk_196 == 20) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_VOICE); + } + if ((this->unk_196 == 0) && Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.world.rot.x, 0x800)) { + this->unk_194 = -this->unk_194; + func_809BD3F8(this); + } +} + +void func_809BE058(EnBigokuta* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 speedXZ; + + if (this->unk_196 != 0) { + this->unk_196--; + } + + SkelAnime_Update(&this->skelAnime); + + if ((this->collider.base.ocFlags1 & OC1_HIT) || (this->cylinder[0].base.ocFlags1 & OC1_HIT) || + (this->cylinder[1].base.ocFlags1 & OC1_HIT)) { + speedXZ = CLAMP_MIN(player->actor.speedXZ, 1.0f); + if (!(this->collider.base.ocFlags1 & OC1_HIT)) { + this->cylinder[0].base.ocFlags1 &= ~OC1_HIT; + this->cylinder[1].base.ocFlags1 &= ~OC1_HIT; + speedXZ *= -1.0f; + } + player->actor.world.pos.x -= speedXZ * Math_SinS(this->actor.shape.rot.y); + player->actor.world.pos.z -= speedXZ * Math_CosS(this->actor.shape.rot.y); + } + if (this->unk_196 == 0) { + func_809BD6B8(this); + } +} + +void func_809BE180(EnBigokuta* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_196 != 0) { + this->unk_196--; + } + if (this->unk_196 == 0) { + if (this->actor.colChkInfo.health != 0) { + func_809BD4A4(this); + } else { + func_809BD658(this); + } + } else if (this->unk_196 >= 8) { + this->actor.shape.rot.y += this->unk_194 * 0x200; + this->actor.world.pos.y = sinf((this->unk_196 - 8) * (M_PI / 16)) * 100.0f + this->actor.home.pos.y; + func_809BCE3C(this); + if (this->unk_196 == 8) { + func_809BD1C8(this, globalCtx); + } + } +} +void func_809BE26C(EnBigokuta* this, GlobalContext* globalCtx) { + Vec3f effectPos; + + if (this->unk_196 != 0) { + if (this->unk_196 != 0) { + this->unk_196--; + } + if (this->unk_196 >= 10) { + this->actor.shape.rot.y += 0x2000; + } + } else if (SkelAnime_Update(&this->skelAnime)) { + if (this->unk_198 != 0) { + this->unk_198--; + } + if (this->unk_198 == 6) { + effectPos.x = this->actor.world.pos.x; + effectPos.y = this->actor.world.pos.y + 150.0f; + effectPos.z = this->actor.world.pos.z; + func_8002829C(globalCtx, &effectPos, &sEffectPosAccel, &sEffectPosAccel, &sEffectPrimColor, + &sEffectEnvColor, 1200, 20); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_DEAD2); + } + if (this->unk_198 == 0 && Math_StepToF(&this->actor.scale.y, 0.0f, 0.001f)) { + Flags_SetClear(globalCtx, this->actor.room); + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_DUNGEON0); + func_8005ACFC(globalCtx->cameraPtrs[MAIN_CAM], 4); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 50, NA_SE_EN_OCTAROCK_BUBLE); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xB0); + Actor_Kill(&this->actor); + } + } +} + +void func_809BE3E4(EnBigokuta* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->unk_196 != 0) { + if (this->unk_196 != 0) { + this->unk_196--; + } + this->actor.shape.rot.y += 0x2000; + } + if (this->unk_196 == 0) { + if ((s16)(this->actor.shape.rot.y - this->actor.world.rot.y) > 0) { + this->unk_194 = 1; + } else { + this->unk_194 = -1; + } + + func_809BD3F8(this); + if (!this->unk_195) { + this->unk_196 = -40; + } + } + func_809BCF68(this, globalCtx); +} + +void func_809BE4A4(EnBigokuta* this, GlobalContext* globalCtx) { + this->actor.world.pos.y -= 10.0f; + this->actor.shape.rot.y += 0x2000; + if (this->actor.world.pos.y < (this->actor.home.pos.y + -200.0f)) { + func_809BD7F0(this, globalCtx); + } + func_809BCF68(this, globalCtx); +} + +void func_809BE518(EnBigokuta* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 10.0f)) { + this->actor.flags |= ACTOR_FLAG_0; + func_809BD3F8(this); + } +} + +void func_809BE568(EnBigokuta* this) { + s32 i; + f32 sin = Math_SinS(this->actor.shape.rot.y); + f32 cos = Math_CosS(this->actor.shape.rot.y); + + this->collider.elements->dim.worldSphere.center.x = + (this->collider.elements->dim.modelSphere.center.z * sin) + + (this->actor.world.pos.x + (this->collider.elements->dim.modelSphere.center.x * cos)); + this->collider.elements->dim.worldSphere.center.z = + (this->actor.world.pos.z + (this->collider.elements->dim.modelSphere.center.z * cos)) - + (this->collider.elements->dim.modelSphere.center.x * sin); + this->collider.elements->dim.worldSphere.center.y = + this->collider.elements->dim.modelSphere.center.y + this->actor.world.pos.y; + + for (i = 0; i < ARRAY_COUNT(this->cylinder); i++) { + this->cylinder[i].dim.pos.x = + this->actor.world.pos.x + sCylinderInit[i].dim.pos.z * sin + sCylinderInit[i].dim.pos.x * cos; + this->cylinder[i].dim.pos.z = + this->actor.world.pos.z + sCylinderInit[i].dim.pos.z * cos - sCylinderInit[i].dim.pos.x * sin; + this->cylinder[i].dim.pos.y = this->actor.world.pos.y; + } +} + +void func_809BE798(EnBigokuta* this, GlobalContext* globalCtx) { + s16 effectRot; + s16 yawDiff; + + if ((this->cylinder[0].base.atFlags & AT_HIT) || (this->cylinder[1].base.atFlags & AT_HIT) || + (this->collider.base.atFlags & AT_HIT)) { + this->cylinder[0].base.atFlags &= ~AT_HIT; + this->cylinder[1].base.atFlags &= ~AT_HIT; + this->collider.base.atFlags &= ~AT_HIT; + yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y; + if (yawDiff > 0x4000) { + effectRot = 0x4000; + } else if (yawDiff > 0) { + effectRot = 0x6000; + } else if (yawDiff < -0x4000) { + effectRot = -0x4000; + } else { + effectRot = -0x6000; + } + func_8002F71C(globalCtx, &this->actor, 10.0f, this->actor.world.rot.y + effectRot, 5.0f); + if (this->actionFunc == func_809BDC08) { + func_809BD4A4(this); + this->unk_196 = 40; + } else if (this->actionFunc == func_809BE3E4) { + if ((effectRot * this->unk_194) > 0) { + this->unk_194 = 0 - this->unk_194; + this->unk_196 += 4; + } + } + } +} + +void EnBigokuta_UpdateDamage(EnBigokuta* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) { + if (this->actor.colChkInfo.damageEffect == 1) { + if (this->actionFunc != func_809BE058) { + func_809BD524(this); + } + } else if (this->actor.colChkInfo.damageEffect == 0xF) { + func_809BD47C(this); + } else if (!Actor_IsFacingPlayer(&this->actor, 0x4000)) { + if (Actor_ApplyDamage(&this->actor) == 0) { // Dead + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_DEAD); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DAIOCTA_DAMAGE); + } + func_809BD5E0(this); + } + } + } +} + +void EnBigokuta_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnBigokuta* this = (EnBigokuta*)thisx; + s32 i; + GlobalContext* globalCtx = globalCtx2; + + func_809BE798(this, globalCtx); + EnBigokuta_UpdateDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + func_809BD2E4(this); + func_809BE568(this); + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_BIG_OCTO); + func_8005AD1C(globalCtx->cameraPtrs[MAIN_CAM], 4); + + if (this->cylinder[0].base.atFlags & AT_ON) { + if (this->actionFunc != func_809BE058) { + for (i = 0; i < ARRAY_COUNT(this->cylinder); i++) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->cylinder[i].base); + } + this->actor.flags |= ACTOR_FLAG_24; + } else { + for (i = 0; i < ARRAY_COUNT(this->cylinder); i++) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cylinder[i].base); + } + } + for (i = 0; i < ARRAY_COUNT(this->cylinder); i++) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder[i].base); + } + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } else { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Actor_SetFocus(&this->actor, this->actor.scale.y * 25.0f * 100.0f); + func_809BCEBC(this, globalCtx); +} + +s32 EnBigokuta_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnBigokuta* this = (EnBigokuta*)thisx; + u8 intensity; + f32 temp_f0; + s32 temp_hi; + + if (limbIndex == 15) { + if (this->actionFunc == func_809BE058 || this->actionFunc == func_809BE180) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bigokuta.c", 1914); + + if (this->actionFunc == func_809BE058) { + temp_hi = this->unk_196 % 12; + if (temp_hi >= 8) { + temp_f0 = (12 - temp_hi) * (M_PI / 8); + } else { + temp_f0 = temp_hi * (M_PI / 16); + } + } else { + temp_hi = this->unk_196 % 6; + if (temp_hi >= 4) { + temp_f0 = (6 - temp_hi) * (M_PI / 4); + } else { + temp_f0 = temp_hi * (M_PI / 8); + } + } + + temp_f0 = sinf(temp_f0) * 125.0f + 130.0f; + gDPPipeSync(POLY_OPA_DISP++); + + gDPSetEnvColor(POLY_OPA_DISP++, temp_f0, temp_f0, temp_f0, 255); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bigokuta.c", 1945); + } + } else if (limbIndex == 10) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bigokuta.c", 1950); + if (this->actionFunc == func_809BE26C) { + intensity = this->unk_196 * (255.0f / 38); + } else { + intensity = 255; + } + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, intensity, intensity, intensity, intensity); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bigokuta.c", 1972); + } else if (limbIndex == 17 && this->actionFunc == func_809BE26C) { + if (this->unk_198 < 5) { + Matrix_Scale((this->unk_198 * 0.2f * 0.25f) + 1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + } else if (this->unk_198 < 8) { + temp_f0 = (this->unk_198 - 5) * (1.0f / 12); + Matrix_Scale(1.25f - temp_f0, 1.0f + temp_f0, 1.0f + temp_f0, MTXMODE_APPLY); + } else { + temp_f0 = ((this->unk_198 - 8) * 0.125f); + Matrix_Scale(1.0f, 1.25f - temp_f0, 1.25f - temp_f0, MTXMODE_APPLY); + } + } + return false; +} + +void EnBigokuta_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBigokuta* this = (EnBigokuta*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bigokuta.c", 2017); + + if ((this->actionFunc != func_809BE26C) || (this->unk_196 != 0) || (this->unk_198 != 0)) { + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + if (this->unk_196 & 1) { + if ((this->actionFunc == func_809BE180 && this->unk_196 >= 8) || + (this->actionFunc == func_809BE26C && this->unk_196 >= 10)) { + f32 rotX = Rand_ZeroOne() * (M_PI * 2.0f); + f32 rotY = Rand_ZeroOne() * (M_PI * 2.0f); + + Matrix_RotateY(rotY, MTXMODE_APPLY); + Matrix_RotateX(rotX, MTXMODE_APPLY); + Matrix_Scale(0.78999996f, 1.3f, 0.78999996f, MTXMODE_APPLY); + Matrix_RotateX(-rotX, MTXMODE_APPLY); + Matrix_RotateY(-rotY, MTXMODE_APPLY); + } + } + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnBigokuta_OverrideLimbDraw, NULL, this); + } else { + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, (this->actor.scale.y * (255 / 0.033f))); + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, NULL, NULL, NULL, POLY_XLU_DISP); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bigokuta.c", 2076); +} diff --git a/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.h b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.h new file mode 100644 index 000000000..2044a1865 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.h @@ -0,0 +1,27 @@ +#ifndef Z_EN_BIGOKUTA_H +#define Z_EN_BIGOKUTA_H + +#include "ultra64.h" +#include "global.h" + +struct EnBigokuta; + +typedef void (*EnBigokutaActionFunc)(struct EnBigokuta*, GlobalContext*); + +typedef struct EnBigokuta { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnBigokutaActionFunc actionFunc; + /* 0x0194 */ s8 unk_194; + /* 0x0195 */ u8 unk_195; + /* 0x0196 */ s16 unk_196; + /* 0x0198 */ s16 unk_198; + /* 0x019A */ s16 unk_19A; + /* 0x019C */ Vec3s jointTable[20]; + /* 0x0214 */ Vec3s morphTable[20]; + /* 0x028C */ ColliderJntSph collider; + /* 0x02AC */ ColliderJntSphElement element; + /* 0x02EC */ ColliderCylinder cylinder[2]; +} EnBigokuta; // size = 0x0384 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c new file mode 100644 index 000000000..6c6b70fd4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.c @@ -0,0 +1,770 @@ +/* + * File: z_en_bili.c + * Overlay: ovl_En_Bili + * Description: Biri (small jellyfish-like enemy) + */ + +#include "z_en_bili.h" +#include "objects/object_bl/object_bl.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_12 | ACTOR_FLAG_14) + +void EnBili_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBili_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBili_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBili_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBili_SetupFloatIdle(EnBili* this); +void EnBili_SetupSpawnedFlyApart(EnBili* this); +void EnBili_FloatIdle(EnBili* this, GlobalContext* globalCtx); +void EnBili_SpawnedFlyApart(EnBili* this, GlobalContext* globalCtx); +void EnBili_DischargeLightning(EnBili* this, GlobalContext* globalCtx); +void EnBili_Climb(EnBili* this, GlobalContext* globalCtx); +void EnBili_ApproachPlayer(EnBili* this, GlobalContext* globalCtx); +void EnBili_SetNewHomeHeight(EnBili* this, GlobalContext* globalCtx); +void EnBili_Recoil(EnBili* this, GlobalContext* globalCtx); +void EnBili_Burnt(EnBili* this, GlobalContext* globalCtx); +void EnBili_Die(EnBili* this, GlobalContext* globalCtx); +void EnBili_Stunned(EnBili* this, GlobalContext* globalCtx); +void EnBili_Frozen(EnBili* this, GlobalContext* globalCtx); + +const ActorInit En_Bili_InitVars = { + ACTOR_EN_BILI, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BL, + sizeof(EnBili), + (ActorFunc)EnBili_Init, + (ActorFunc)EnBili_Destroy, + (ActorFunc)EnBili_Update, + (ActorFunc)EnBili_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT8, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x03, 0x08 }, + { 0xFFCFFFFF, 0x01, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 9, 28, -20, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 1, 9, 28, -20, 30 }; + +typedef enum { + /* 0x0 */ BIRI_DMGEFF_NONE, + /* 0x1 */ BIRI_DMGEFF_DEKUNUT, + /* 0x2 */ BIRI_DMGEFF_FIRE, + /* 0x3 */ BIRI_DMGEFF_ICE, + /* 0xE */ BIRI_DMGEFF_SLINGSHOT = 0xE, + /* 0xF */ BIRI_DMGEFF_SWORD +} BiriDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, BIRI_DMGEFF_DEKUNUT), + /* Deku stick */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(0, BIRI_DMGEFF_SLINGSHOT), + /* Explosive */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(1, BIRI_DMGEFF_NONE), + /* Normal arrow */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Kokiri sword */ DMG_ENTRY(1, BIRI_DMGEFF_SWORD), + /* Master sword */ DMG_ENTRY(2, BIRI_DMGEFF_SWORD), + /* Giant's Knife */ DMG_ENTRY(4, BIRI_DMGEFF_SWORD), + /* Fire arrow */ DMG_ENTRY(4, BIRI_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(4, BIRI_DMGEFF_ICE), + /* Light arrow */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Unk arrow 1 */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(4, BIRI_DMGEFF_FIRE), + /* Ice magic */ DMG_ENTRY(4, BIRI_DMGEFF_ICE), + /* Light magic */ DMG_ENTRY(0, BIRI_DMGEFF_NONE), + /* Shield */ DMG_ENTRY(0, BIRI_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, BIRI_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, BIRI_DMGEFF_NONE), + /* Giant spin */ DMG_ENTRY(4, BIRI_DMGEFF_NONE), + /* Master spin */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Kokiri jump */ DMG_ENTRY(2, BIRI_DMGEFF_NONE), + /* Giant jump */ DMG_ENTRY(8, BIRI_DMGEFF_NONE), + /* Master jump */ DMG_ENTRY(4, BIRI_DMGEFF_NONE), + /* Unknown 1 */ DMG_ENTRY(0, BIRI_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, BIRI_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, BIRI_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, BIRI_DMGEFF_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x17, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_STOP), +}; + +void EnBili_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBili* this = (EnBili*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 17.0f); + this->actor.shape.shadowAlpha = 155; + SkelAnime_Init(globalCtx, &this->skelAnime, &gBiriSkel, &gBiriDefaultAnim, this->jointTable, this->morphTable, + EN_BILI_LIMB_MAX); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + this->playFlySound = false; + + if (this->actor.params == EN_BILI_TYPE_NORMAL) { + EnBili_SetupFloatIdle(this); + } else { + EnBili_SetupSpawnedFlyApart(this); + } +} + +void EnBili_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBili* this = (EnBili*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +// Setup Action Functions + +void EnBili_SetupFloatIdle(EnBili* this) { + this->actor.speedXZ = 0.7f; + this->collider.info.bumper.effect = 1; // Shock? + this->timer = 32; + this->collider.base.atFlags |= AT_ON; + this->collider.base.acFlags |= AC_ON; + this->actionFunc = EnBili_FloatIdle; + this->actor.home.pos.y = this->actor.world.pos.y; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; +} + +/** + * Separates the Biri spawned by a dying EnVali. + */ +void EnBili_SetupSpawnedFlyApart(EnBili* this) { + Animation_PlayLoop(&this->skelAnime, &gBiriDefaultAnim); + this->timer = 25; + this->actor.velocity.y = 6.0f; + this->actor.gravity = -0.3f; + this->collider.base.atFlags &= ~AT_ON; + this->actionFunc = EnBili_SpawnedFlyApart; + this->actor.speedXZ = 3.0f; +} + +/** + * Used for both touching player/player's shield and being hit with sword. What to do next is determined by params. + */ +void EnBili_SetupDischargeLightning(EnBili* this) { + Animation_PlayLoop(&this->skelAnime, &gBiriDischargeLightningAnim); + this->timer = 10; + this->actionFunc = EnBili_DischargeLightning; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = -1.0f; +} + +void EnBili_SetupClimb(EnBili* this) { + Animation_PlayOnce(&this->skelAnime, &gBiriClimbAnim); + this->collider.base.atFlags &= ~AT_ON; + this->actionFunc = EnBili_Climb; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; +} + +void EnBili_SetupApproachPlayer(EnBili* this) { + this->actor.speedXZ = 1.2f; + this->actionFunc = EnBili_ApproachPlayer; +} + +void EnBili_SetupSetNewHomeHeight(EnBili* this) { + Animation_PlayLoop(&this->skelAnime, &gBiriDefaultAnim); + this->timer = 96; + this->actor.speedXZ = 0.9f; + this->collider.base.atFlags |= AT_ON; + this->actionFunc = EnBili_SetNewHomeHeight; + this->actor.home.pos.y = this->actor.world.pos.y; +} + +void EnBili_SetupRecoil(EnBili* this) { + if (this->skelAnime.animation != &gBiriDefaultAnim) { + Animation_PlayLoop(&this->skelAnime, &gBiriDefaultAnim); + } + + this->actor.world.rot.y = Actor_WorldYawTowardPoint(&this->actor, &this->collider.base.ac->prevPos) + 0x8000; + this->actor.world.rot.x = Actor_WorldPitchTowardPoint(&this->actor, &this->collider.base.ac->prevPos); + this->actionFunc = EnBili_Recoil; + this->actor.speedXZ = 5.0f; +} + +/** + * Used for both fire damage and generic damage + */ +void EnBili_SetupBurnt(EnBili* this) { + if (this->actionFunc == EnBili_Climb) { + Animation_PlayLoop(&this->skelAnime, &gBiriDefaultAnim); + } + + this->timer = 20; + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->actor.flags |= ACTOR_FLAG_4; + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0x2000, 0x14); + this->actionFunc = EnBili_Burnt; +} + +void EnBili_SetupDie(EnBili* this) { + this->timer = 18; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnBili_Die; + this->actor.speedXZ = 0.0f; +} + +/** + * Falls to ground + */ +void EnBili_SetupStunned(EnBili* this) { + this->timer = 80; + this->collider.info.bumper.effect = 0; + this->actor.gravity = -1.0f; + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 0x96, 0x2000, 0x50); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->collider.base.atFlags &= ~AT_ON; + this->actionFunc = EnBili_Stunned; +} + +void EnBili_SetupFrozen(EnBili* this, GlobalContext* globalCtx) { + s32 i; + Vec3f effectPos; + + if (!(this->actor.flags & ACTOR_FLAG_15)) { + this->actor.gravity = -1.0f; + } + + this->actor.velocity.y = 0.0f; + effectPos.y = this->actor.world.pos.y - 15.0f; + + for (i = 0; i < 8; i++) { + + effectPos.x = this->actor.world.pos.x + ((i & 1) ? 7.0f : -7.0f); + effectPos.y += 2.5f; + effectPos.z = this->actor.world.pos.z + ((i & 4) ? 7.0f : -7.0f); + + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &effectPos, 150, 150, 150, 250, 235, 245, 255, + (Rand_ZeroOne() * 0.2f) + 0.7f); + } + + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 0x96, 0x2000, 0xA); + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->timer = 300; + this->actionFunc = EnBili_Frozen; +} + +// Miscellaneous + +/** + * Changes the texture displayed on the oral arms limb using the current frame. + */ +void EnBili_UpdateTentaclesIndex(EnBili* this) { + s16 curFrame = this->skelAnime.curFrame; + s16 temp; // Not strictly necessary, but avoids a few s16 casts + + if (this->actionFunc == EnBili_DischargeLightning) { + temp = 3 - curFrame; + this->tentaclesTexIndex = (ABS(temp) + 5) % 8; + } else if (this->actionFunc == EnBili_Climb) { + if (curFrame <= 9) { + temp = curFrame >> 1; + this->tentaclesTexIndex = CLAMP_MAX(temp, 3); + } else if (curFrame <= 18) { + temp = 17 - curFrame; + this->tentaclesTexIndex = CLAMP_MIN(temp, 0) >> 1; + } else if (curFrame <= 36) { + this->tentaclesTexIndex = ((36 - curFrame) / 3) + 2; + } else { + this->tentaclesTexIndex = (40 - curFrame) >> 1; + } + } else { + this->tentaclesTexIndex = curFrame >> 1; + } +} + +/** + * Tracks Player height, with oscillation, and moves away from walls + */ +void EnBili_UpdateFloating(EnBili* this) { + f32 playerHeight = this->actor.world.pos.y + this->actor.yDistToPlayer; + f32 heightOffset = ((this->actionFunc == EnBili_SetNewHomeHeight) ? 100.0f : 40.0f); + f32 baseHeight = CLAMP_MIN(this->actor.floorHeight, playerHeight); + + Math_StepToF(&this->actor.home.pos.y, baseHeight + heightOffset, 1.0f); + this->actor.world.pos.y = this->actor.home.pos.y + (sinf(this->timer * (M_PI / 16)) * 3.0f); + + // Turn around if touching wall + if (this->actor.bgCheckFlags & 8) { + this->actor.world.rot.y = this->actor.wallYaw; + } +} + +// Action functions + +void EnBili_FloatIdle(EnBili* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->timer != 0) { + this->timer--; + } + + if (!(this->timer % 4)) { + this->actor.world.rot.y += Rand_CenteredFloat(1820.0f); + } + + EnBili_UpdateFloating(this); + + if (this->timer == 0) { + this->timer = 32; + } + + if ((this->actor.xzDistToPlayer < 160.0f) && (fabsf(this->actor.yDistToPlayer) < 45.0f)) { + EnBili_SetupApproachPlayer(this); + } +} + +void EnBili_SpawnedFlyApart(EnBili* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + EnBili_SetupFloatIdle(this); + } +} + +void EnBili_DischargeLightning(EnBili* this, GlobalContext* globalCtx) { + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 200, 255, 255, 255 }; + s32 i; + Vec3f effectPos; + s16 effectYaw; + + for (i = 0; i < 4; i++) { + if (!((this->timer + (i << 1)) % 4)) { + effectYaw = (s16)Rand_CenteredFloat(12288.0f) + (i * 0x4000) + 0x2000; + effectPos.x = Rand_CenteredFloat(5.0f) + this->actor.world.pos.x; + effectPos.y = (Rand_ZeroOne() * 5.0f) + this->actor.world.pos.y + 2.5f; + effectPos.z = Rand_CenteredFloat(5.0f) + this->actor.world.pos.z; + EffectSsLightning_Spawn(globalCtx, &effectPos, &primColor, &envColor, 15, effectYaw, 6, 2); + } + } + + SkelAnime_Update(&this->skelAnime); + func_8002F974(&this->actor, NA_SE_EN_BIRI_SPARK - SFX_FLAG); + + if (this->timer != 0) { + this->timer--; + } + + this->actor.velocity.y *= -1.0f; + + if ((this->timer == 0) && Animation_OnFrame(&this->skelAnime, 0.0f)) { + if (this->actor.params == EN_BILI_TYPE_DYING) { + EnBili_SetupDie(this); + } else { + EnBili_SetupClimb(this); + } + } +} + +void EnBili_Climb(EnBili* this, GlobalContext* globalCtx) { + s32 skelAnimeUpdate = SkelAnime_Update(&this->skelAnime); + f32 curFrame = this->skelAnime.curFrame; + + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIRI_JUMP); + } + + if (curFrame > 9.0f) { + Math_ApproachF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToPlayer + 100.0f, 0.5f, + 5.0f); + } + + if (skelAnimeUpdate) { + EnBili_SetupSetNewHomeHeight(this); + } +} + +void EnBili_ApproachPlayer(EnBili* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 2, 1820); + + if (this->timer != 0) { + this->timer--; + } + + EnBili_UpdateFloating(this); + + if (this->timer == 0) { + this->timer = 32; + } + + if (this->actor.xzDistToPlayer > 200.0f) { + EnBili_SetupFloatIdle(this); + } +} + +void EnBili_SetNewHomeHeight(EnBili* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->timer != 0) { + this->timer--; + } + + Math_ScaledStepToS(&this->actor.world.rot.y, (s16)(this->actor.yawTowardsPlayer + 0x8000), 910); + EnBili_UpdateFloating(this); + + if (this->timer == 0) { + EnBili_SetupFloatIdle(this); + } +} + +void EnBili_Recoil(EnBili* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Math_StepToF(&this->actor.speedXZ, 0.0f, 0.3f)) { + this->actor.world.rot.y += 0x8000; + EnBili_SetupFloatIdle(this); + } +} + +void EnBili_Burnt(EnBili* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->actor.flags & ACTOR_FLAG_15) { + this->actor.colorFilterTimer = 20; + } else { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + EnBili_SetupDie(this); + } + } +} + +void EnBili_Die(EnBili* this, GlobalContext* globalCtx) { + static Vec3f effectVelocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f effectAccel = { 0.0f, 0.0f, 0.0f }; + s16 effectScale; + Vec3f effectPos; + s32 i; + + if (this->actor.draw != NULL) { + if (this->actor.flags & ACTOR_FLAG_15) { + return; + } + this->actor.draw = NULL; + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x50); + } + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer != 0) { + for (i = 0; i < 2; i++) { + effectPos.x = ((Rand_ZeroOne() * 10.0f) + this->actor.world.pos.x) - 5.0f; + effectPos.y = ((Rand_ZeroOne() * 5.0f) + this->actor.world.pos.y) - 2.5f; + effectPos.z = ((Rand_ZeroOne() * 10.0f) + this->actor.world.pos.z) - 5.0f; + + effectVelocity.y = Rand_ZeroOne() + 1.0f; + effectScale = Rand_S16Offset(40, 40); + + if (Rand_ZeroOne() < 0.7f) { + EffectSsDtBubble_SpawnColorProfile(globalCtx, &effectPos, &effectVelocity, &effectAccel, effectScale, + 25, 2, 1); + } else { + EffectSsDtBubble_SpawnColorProfile(globalCtx, &effectPos, &effectVelocity, &effectAccel, effectScale, + 25, 0, 1); + } + } + } else { + Actor_Kill(&this->actor); + } + + if (this->timer == 14) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_BIRI_BUBLE); + } +} + +void EnBili_Stunned(EnBili* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + + if (this->timer == 0) { + EnBili_SetupFloatIdle(this); + } +} + +void EnBili_Frozen(EnBili* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (!(this->actor.flags & ACTOR_FLAG_15)) { + this->actor.gravity = -1.0f; + } + + if ((this->actor.bgCheckFlags & 1) || (this->actor.floorHeight == BGCHECK_Y_MIN)) { + this->actor.colorFilterTimer = 0; + EnBili_SetupDie(this); + } else { + this->actor.colorFilterTimer = 10; + } +} + +void EnBili_UpdateDamage(EnBili* this, GlobalContext* globalCtx) { + u8 damageEffect; + + if ((this->actor.colChkInfo.health != 0) && (this->collider.base.acFlags & AC_HIT)) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIRI_DEAD); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_0; + } + + damageEffect = this->actor.colChkInfo.damageEffect; + + if (damageEffect == BIRI_DMGEFF_DEKUNUT) { + if (this->actionFunc != EnBili_Stunned) { + EnBili_SetupStunned(this); + } + } else if (damageEffect == BIRI_DMGEFF_SWORD) { + if (this->actionFunc != EnBili_Stunned) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0x2000, 0xA); + + if (this->actor.colChkInfo.health == 0) { + this->actor.params = EN_BILI_TYPE_DYING; + } + EnBili_SetupDischargeLightning(this); + } else { + EnBili_SetupBurnt(this); + } + } else if (damageEffect == BIRI_DMGEFF_FIRE) { + EnBili_SetupBurnt(this); + this->timer = 2; + } else if (damageEffect == BIRI_DMGEFF_ICE) { + EnBili_SetupFrozen(this, globalCtx); + } else if (damageEffect == BIRI_DMGEFF_SLINGSHOT) { + EnBili_SetupRecoil(this); + } else { + EnBili_SetupBurnt(this); + } + + if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F820) { // DMG_ARROW + this->actor.flags |= ACTOR_FLAG_4; + } + } + } +} + +void EnBili_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnBili* this = (EnBili*)thisx; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + EnBili_SetupDischargeLightning(this); + } + + EnBili_UpdateDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + + if (this->actionFunc != EnBili_Die) { + EnBili_UpdateTentaclesIndex(this); + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + if ((this->actionFunc == EnBili_FloatIdle) || (this->actionFunc == EnBili_SetNewHomeHeight) || + (this->actionFunc == EnBili_ApproachPlayer) || (this->actionFunc == EnBili_Recoil)) { + if (this->playFlySound) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIRI_FLY); + this->playFlySound = false; + } else { + this->playFlySound = true; + } + } + } + if (this->actionFunc == EnBili_Recoil) { + func_8002D97C(&this->actor); + } else { + Actor_MoveForward(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, this->collider.dim.radius, this->collider.dim.height, 7); + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (this->collider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_SetFocus(&this->actor, 0.0f); + } +} + +// Draw and associated functions + +void EnBili_PulseLimb3(EnBili* this, f32 frame, Vec3f* arg2) { + f32 cos; + f32 sin; + + if (this->actionFunc == EnBili_DischargeLightning) { + arg2->y = 1.0f - (sinf((M_PI * 0.16667f) * frame) * 0.26f); + } else if (this->actionFunc == EnBili_Climb) { + if (frame <= 8.0f) { + arg2->y = (cosf((M_PI * 0.125f) * frame) * 0.15f) + 0.85f; + } else if (frame <= 18.0f) { + cos = cosf((frame - 8.0f) * (M_PI * 0.1f)); + arg2->y = 1.0f - (0.3f * cos); + arg2->x = (0.2f * cos) + 0.8f; + } else { + cos = cosf((frame - 18.0f) * (M_PI * 0.0227f)); + arg2->y = (0.31f * cos) + 1.0f; + arg2->x = 1.0f - (0.4f * cos); + } + + arg2->z = arg2->x; + } else if (this->actionFunc == EnBili_Stunned) { + sin = sinf((M_PI * 0.1f) * this->timer) * 0.08f; + arg2->x -= sin; + arg2->y += sin; + arg2->z -= sin; + } else { + arg2->y = (cosf((M_PI * 0.125f) * frame) * 0.13f) + 0.87f; + } +} + +void EnBili_PulseLimb2(EnBili* this, f32 frame, Vec3f* arg2) { + f32 cos; + f32 sin; + + if (this->actionFunc == EnBili_DischargeLightning) { + arg2->y = (sinf((M_PI * 0.16667f) * frame) * 0.2f) + 1.0f; + } else if (this->actionFunc == EnBili_Climb) { + if (frame <= 8.0f) { + arg2->x = 1.125f - (cosf((M_PI * 0.125f) * frame) * 0.125f); + } else if (frame <= 18.0f) { + cos = cosf((frame - 8.0f) * (M_PI * 0.1f)); + arg2->x = (0.275f * cos) + 0.975f; + arg2->y = 1.25f - (0.25f * cos); + } else { + cos = cosf((frame - 18.0f) * (M_PI * 0.0227f)); + arg2->x = 1.0f - (0.3f * cos); + arg2->y = (0.48f * cos) + 1.0f; + } + arg2->z = arg2->x; + } else if (this->actionFunc == EnBili_Stunned) { + sin = sinf((M_PI * 0.1f) * this->timer) * 0.08f; + arg2->x += sin; + arg2->y -= sin; + arg2->z += sin; + } else { + arg2->y = 1.1f - (cosf((M_PI * 0.125f) * frame) * 0.1f); + } +} + +void EnBili_PulseLimb4(EnBili* this, f32 frame, Vec3f* arg2) { + f32 cos; + + if (this->actionFunc == EnBili_Climb) { + if (frame <= 8.0f) { + cos = cosf((M_PI * 0.125f) * frame); + arg2->x = 1.125f - (0.125f * cos); + arg2->y = (0.3f * cos) + 0.7f; + } else if (frame <= 18.0f) { + cos = cosf((frame - 8.0f) * (M_PI * 0.1f)); + arg2->x = (0.325f * cos) + 0.925f; + arg2->y = 0.95f - (0.55f * cos); + } else { + cos = cosf((frame - 18.0f) * (M_PI * 0.0227f)); + arg2->x = 1.0f - (0.4f * cos); + arg2->y = (0.52f * cos) + 1.0f; + } + arg2->z = arg2->x; + } +} + +s32 EnBili_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnBili* this = (EnBili*)thisx; + Vec3f limbScale = { 1.0f, 1.0f, 1.0f }; + f32 curFrame = this->skelAnime.curFrame; + + if (limbIndex == EN_BILI_LIMB_OUTER_HOOD) { + EnBili_PulseLimb3(this, curFrame, &limbScale); + } else if (limbIndex == EN_BILI_LIMB_INNER_HOOD) { + EnBili_PulseLimb2(this, curFrame, &limbScale); + } else if (limbIndex == EN_BILI_LIMB_TENTACLES) { + EnBili_PulseLimb4(this, curFrame, &limbScale); + rot->y = (Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y) + 0x8000; + } + + Matrix_Scale(limbScale.x, limbScale.y, limbScale.z, MTXMODE_APPLY); + return false; +} + +static void* sTentaclesTextures[] = { + gBiriTentacles0Tex, gBiriTentacles1Tex, gBiriTentacles2Tex, gBiriTentacles3Tex, + gBiriTentacles4Tex, gBiriTentacles5Tex, gBiriTentacles6Tex, gBiriTentacles7Tex, +}; + +#include "overlays/ovl_En_Bili/ovl_En_Bili.h" + +void EnBili_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBili* this = (EnBili*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bili.c", 1521); + func_80093D84(globalCtx->state.gfxCtx); + + this->tentaclesTexIndex = CLAMP_MAX(this->tentaclesTexIndex, 7); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTentaclesTextures[this->tentaclesTexIndex])); + + if ((this->actionFunc == EnBili_DischargeLightning) && ((this->timer & 1) != 0)) { + gSPSegment(POLY_XLU_DISP++, 0x09, D_809C16F0); + } else { + gSPSegment(POLY_XLU_DISP++, 0x09, D_809C1700); + } + + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnBili_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bili.c", 1552); +} diff --git a/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.h b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.h new file mode 100644 index 000000000..5e5f6815f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bili/z_en_bili.h @@ -0,0 +1,38 @@ +#ifndef Z_EN_BILI_H +#define Z_EN_BILI_H + +#include "ultra64.h" +#include "global.h" + +struct EnBili; + +typedef void (*EnBiliActionFunc)(struct EnBili*, GlobalContext*); + +typedef enum { + /* 0 */ EN_BILI_LIMB_NONE, + /* 1 */ EN_BILI_LIMB_ROOT, + /* 2 */ EN_BILI_LIMB_INNER_HOOD, + /* 3 */ EN_BILI_LIMB_OUTER_HOOD, + /* 4 */ EN_BILI_LIMB_TENTACLES, + /* 5 */ EN_BILI_LIMB_MAX +} EnBiliLimb; + +typedef struct EnBili { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnBiliActionFunc actionFunc; + /* 0x0194 */ u8 tentaclesTexIndex; + /* 0x0195 */ u8 playFlySound; + /* 0x0196 */ s16 timer; + /* 0x0198 */ Vec3s jointTable[EN_BILI_LIMB_MAX]; + /* 0x01B6 */ Vec3s morphTable[EN_BILI_LIMB_MAX]; + /* 0x01D4 */ ColliderCylinder collider; +} EnBili; // size = 0x0220 + +typedef enum { + /* -1 */ EN_BILI_TYPE_NORMAL = -1, + /* 0 */ EN_BILI_TYPE_VALI_SPAWNED, + /* 1 */ EN_BILI_TYPE_DYING +} EnBiliType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c b/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c new file mode 100644 index 000000000..f56ff53a6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.c @@ -0,0 +1,133 @@ +/* + * File: z_en_bird.c + * Overlay: ovl_En_Bird + * Description: An unused brown bird + */ + +#include "z_en_bird.h" +#include "objects/object_bird/object_bird.h" + +#define FLAGS 0 + +void EnBird_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBird_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBird_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBird_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809C1E00(EnBird* this, s16 params); +void func_809C1E40(EnBird* this, GlobalContext* globalCtx); +void func_809C1D60(EnBird* this, GlobalContext* globalCtx); +void func_809C1CAC(EnBird* this, s16 params); + +const ActorInit En_Bird_InitVars = { + ACTOR_EN_BIRD, + ACTORCAT_PROP, + FLAGS, + OBJECT_BIRD, + sizeof(EnBird), + (ActorFunc)EnBird_Init, + (ActorFunc)EnBird_Destroy, + (ActorFunc)EnBird_Update, + (ActorFunc)EnBird_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 5600, ICHAIN_STOP), +}; + +void EnBird_SetupAction(EnBird* this, EnBirdActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnBird_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBird* this = (EnBird*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.01); + SkelAnime_Init(globalCtx, &this->skelAnime, &gBirdSkel, &gBirdFlyAnim, NULL, NULL, 0); + ActorShape_Init(&this->actor.shape, 5500, ActorShadow_DrawCircle, 4); + this->unk_194 = 0; + this->unk_198 = 0; + this->unk_1C0 = 0x9C4; + this->actor.colChkInfo.mass = 0; + this->unk_1A8 = 1.5f; + this->unk_1AC = 0.5f; + this->unk_1A0 = 0.0f; + this->unk_1A4 = 0.0f; + this->unk_1B8 = 0.0f; + this->unk_1B0 = 40.0f; + this->unk_1BC = 70.0f; + func_809C1CAC(this, this->actor.params); +} + +void EnBird_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_809C1CAC(EnBird* this, s16 params) { + f32 frameCount = Animation_GetLastFrame(&gBirdFlyAnim); + f32 playbackSpeed = this->unk_19C ? 0.0f : 1.0f; + AnimationHeader* anim = &gBirdFlyAnim; + + this->unk_198 = Rand_S16Offset(5, 0x23); + Animation_Change(&this->skelAnime, anim, playbackSpeed, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + EnBird_SetupAction(this, func_809C1D60); +} + +void func_809C1D60(EnBird* this, GlobalContext* globalCtx) { + f32 fVar2 = sinf(this->unk_1B4); + + this->actor.shape.yOffset = this->actor.shape.yOffset + fVar2 * this->unk_1A0; + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 0.5f, 0.0f); + + if (this->unk_19C != 0) { + this->skelAnime.playSpeed = this->actor.speedXZ + this->actor.speedXZ; + } + + SkelAnime_Update(&this->skelAnime); + this->unk_198 -= 1; + + if (this->unk_198 <= 0) { + func_809C1E00(this, this->actor.params); + } +} + +void func_809C1E00(EnBird* this, s16 params) { + this->unk_198 = Rand_S16Offset(0x14, 0x2D); + EnBird_SetupAction(this, func_809C1E40); +} + +void func_809C1E40(EnBird* this, GlobalContext* globalCtx) { + f32 fVar4 = sinf(this->unk_1B4); + + this->actor.shape.yOffset += fVar4 * this->unk_1A0; + Math_SmoothStepToF(&this->actor.speedXZ, this->unk_1A8, 0.1f, this->unk_1AC, 0.0f); + + if (this->unk_1B0 < Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) || this->unk_198 < 4) { + Math_StepToAngleS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos), + this->unk_1C0); + } else { + fVar4 = sinf(this->unk_1B4); + this->actor.world.rot.y += (s16)(fVar4 * this->unk_1A4); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + SkelAnime_Update(&this->skelAnime); + this->unk_198 -= 1; + if (this->unk_198 < 0) { + func_809C1CAC(this, this->actor.params); + } +} + +void EnBird_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBird* this = (EnBird*)thisx; + + this->unk_1B4 += this->unk_1B8; + this->actionFunc(this, globalCtx); +} + +void EnBird_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBird* this = (EnBird*)thisx; + + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, NULL); +} diff --git a/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.h b/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.h new file mode 100644 index 000000000..bd5c23298 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bird/z_en_bird.h @@ -0,0 +1,31 @@ +#ifndef Z_EN_BIRD_H +#define Z_EN_BIRD_H + +#include "ultra64.h" +#include "global.h" + +struct EnBird; + +typedef void (*EnBirdActionFunc)(struct EnBird*, GlobalContext*); + +typedef struct EnBird { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnBirdActionFunc actionFunc; + /* 0x0194 */ u32 unk_194; + /* 0x0198 */ s32 unk_198; + /* 0x019C */ s16 unk_19C; + /* 0x019E */ char unk_19E[0x2]; + /* 0x01A0 */ f32 unk_1A0; + /* 0x01A4 */ f32 unk_1A4; + /* 0x01A8 */ f32 unk_1A8; + /* 0x01AC */ f32 unk_1AC; + /* 0x01B0 */ f32 unk_1B0; + /* 0x01B4 */ f32 unk_1B4; + /* 0x01B8 */ f32 unk_1B8; + /* 0x01BC */ f32 unk_1BC; + /* 0x01C0 */ s16 unk_1C0; + /* 0x01C2 */ char unk_1C2[0x1A]; +} EnBird; // size = 0x01DC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c b/soh/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c new file mode 100644 index 000000000..fa650bb21 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c @@ -0,0 +1,175 @@ +/* + * File: z_en_blkobj.c + * Overlay: ovl_En_Blkobj + * Description: Dark Link's Illusion Room + */ + +#include "z_en_blkobj.h" +#include "objects/object_blkobj/object_blkobj.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnBlkobj_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBlkobj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBlkobj_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBlkobj_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBlkobj_Wait(EnBlkobj* this, GlobalContext* globalCtx); +void EnBlkobj_SpawnDarkLink(EnBlkobj* this, GlobalContext* globalCtx); +void EnBlkobj_DarkLinkFight(EnBlkobj* this, GlobalContext* globalCtx); +void EnBlkobj_DoNothing(EnBlkobj* this, GlobalContext* globalCtx); + +const ActorInit En_Blkobj_InitVars = { + ACTOR_EN_BLKOBJ, + ACTORCAT_PROP, + FLAGS, + OBJECT_BLKOBJ, + sizeof(EnBlkobj), + (ActorFunc)EnBlkobj_Init, + (ActorFunc)EnBlkobj_Destroy, + (ActorFunc)EnBlkobj_Update, + (ActorFunc)EnBlkobj_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 300, ICHAIN_STOP), +}; + +static Gfx sSetupOpaDL[] = { + gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2), + gsSPEndDisplayList(), +}; + +static Gfx sSetupXluDL[] = { + gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2), + gsSPEndDisplayList(), +}; + +void EnBlkobj_SetupAction(EnBlkobj* this, EnBlkobjActionFunc actionFunc) { + this->actionFunc = actionFunc; + this->timer = 0; +} + +void EnBlkobj_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBlkobj* this = (EnBlkobj*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + if (Flags_GetClear(globalCtx, this->dyna.actor.room)) { + this->alpha = 255; + EnBlkobj_SetupAction(this, EnBlkobj_DoNothing); + } else { + CollisionHeader_GetVirtual(&gIllusionRoomCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + EnBlkobj_SetupAction(this, EnBlkobj_Wait); + } +} + +void EnBlkobj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBlkobj* this = (EnBlkobj*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void EnBlkobj_Wait(EnBlkobj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->dyna.actor.xzDistToPlayer < 120.0f) { + EnBlkobj_SetupAction(this, EnBlkobj_SpawnDarkLink); + } + player->stateFlags2 |= 0x04000000; +} + +void EnBlkobj_SpawnDarkLink(EnBlkobj* this, GlobalContext* globalCtx) { + if (!(this->dyna.actor.flags & ACTOR_FLAG_6)) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_TORCH2, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, this->dyna.actor.yawTowardsPlayer, 0, + 0); + EnBlkobj_SetupAction(this, EnBlkobj_DarkLinkFight); + } +} + +void EnBlkobj_DarkLinkFight(EnBlkobj* this, GlobalContext* globalCtx) { + s32 alphaMod; + + if (this->timer == 0) { + if (Actor_Find(&globalCtx->actorCtx, ACTOR_EN_TORCH2, ACTORCAT_BOSS) == NULL) { + Flags_SetClear(globalCtx, this->dyna.actor.room); + this->timer++; + } + } else if (this->timer++ > 100) { + alphaMod = (this->timer - 100) >> 2; + if (alphaMod > 5) { + alphaMod = 5; + } + this->alpha += alphaMod; + if (this->alpha > 255) { + this->alpha = 255; + EnBlkobj_SetupAction(this, EnBlkobj_DoNothing); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + } +} + +void EnBlkobj_DoNothing(EnBlkobj* this, GlobalContext* globalCtx) { +} + +void EnBlkobj_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBlkobj* this = (EnBlkobj*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EnBlkobj_DrawAlpha(GlobalContext* globalCtx, Gfx* dList, s32 alpha) { + Gfx* segment; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 322); + + if (alpha == 255) { + segment = sSetupOpaDL; + } else { + segment = sSetupXluDL; + } + + gSPSegment(POLY_XLU_DISP++, 0x08, segment); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, alpha); + gSPDisplayList(POLY_XLU_DISP++, dList); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 330); +} + +void EnBlkobj_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBlkobj* this = (EnBlkobj*)thisx; + s32 illusionAlpha; + u32 gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 349); + + func_80093D84(globalCtx->state.gfxCtx); + + gameplayFrames = globalCtx->gameplayFrames % 128; + + gSPSegment(POLY_XLU_DISP++, 0x0D, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames, 0, 32, 32, 1, gameplayFrames, 0, 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 363), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->alpha != 0) { + EnBlkobj_DrawAlpha(globalCtx, gIllusionRoomNormalDL, this->alpha); + } + illusionAlpha = 255 - this->alpha; + if (illusionAlpha != 0) { + EnBlkobj_DrawAlpha(globalCtx, gIllusionRoomIllusionDL, illusionAlpha); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 375); +} diff --git a/soh/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h b/soh/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h new file mode 100644 index 000000000..ada8f21de --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h @@ -0,0 +1,18 @@ +#ifndef Z_EN_BLKOBJ_H +#define Z_EN_BLKOBJ_H + +#include "ultra64.h" +#include "global.h" + +struct EnBlkobj; + +typedef void (*EnBlkobjActionFunc)(struct EnBlkobj*, GlobalContext*); + +typedef struct EnBlkobj { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ s16 alpha; + /* 0x0166 */ s16 timer; + /* 0x0168 */ EnBlkobjActionFunc actionFunc; +} EnBlkobj; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c new file mode 100644 index 000000000..b5ee3290c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.c @@ -0,0 +1,386 @@ +/* + * File: z_en_bom.c + * Overlay: ovl_En_Bom + * Description: Bomb + */ + +#include "z_en_bom.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnBom_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBom_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBom_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBom_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBom_Move(EnBom* this, GlobalContext* globalCtx); +void EnBom_WaitForRelease(EnBom* this, GlobalContext* globalCtx); + +const ActorInit En_Bom_InitVars = { + ACTOR_EN_BOM, + ACTORCAT_EXPLOSIVE, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnBom), + (ActorFunc)EnBom_Init, + (ActorFunc)EnBom_Destroy, + (ActorFunc)EnBom_Update, + (ActorFunc)EnBom_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER | AC_TYPE_OTHER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0x0003F828, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 6, 11, 14, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000008, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 0 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ALL, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -4000, ICHAIN_STOP), +}; + +void EnBom_SetupAction(EnBom* this, EnBomActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnBom_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBom* this = (EnBom*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + ActorShape_Init(&thisx->shape, 700.0f, ActorShadow_DrawCircle, 16.0f); + thisx->colChkInfo.mass = 200; + thisx->colChkInfo.cylRadius = 5; + thisx->colChkInfo.cylHeight = 10; + this->timer = 70; + this->flashSpeedScale = 7; + Collider_InitCylinder(globalCtx, &this->bombCollider); + Collider_InitJntSph(globalCtx, &this->explosionCollider); + Collider_SetCylinder(globalCtx, &this->bombCollider, thisx, &sCylinderInit); + Collider_SetJntSph(globalCtx, &this->explosionCollider, thisx, &sJntSphInit, &this->explosionColliderItems[0]); + this->explosionColliderItems[0].info.toucher.damage += (thisx->shape.rot.z & 0xFF00) >> 8; + + thisx->shape.rot.z &= 0xFF; + if (thisx->shape.rot.z & 0x80) { + thisx->shape.rot.z |= 0xFF00; + } + + EnBom_SetupAction(this, EnBom_Move); +} + +void EnBom_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBom* this = (EnBom*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->explosionCollider); + Collider_DestroyCylinder(globalCtx, &this->bombCollider); +} + +void EnBom_Move(EnBom* this, GlobalContext* globalCtx) { + // if bomb has a parent actor, the bomb hasnt been released yet + if (Actor_HasParent(&this->actor, globalCtx)) { + EnBom_SetupAction(this, EnBom_WaitForRelease); + this->actor.room = -1; + return; + } + + if ((this->actor.velocity.y > 0.0f) && (this->actor.bgCheckFlags & 0x10)) { + this->actor.velocity.y = -this->actor.velocity.y; + } + + // rebound bomb off the wall it hits + if ((this->actor.speedXZ != 0.0f) && (this->actor.bgCheckFlags & 8)) { + if (ABS((s16)(this->actor.wallYaw - this->actor.world.rot.y)) > 0x4000) { + this->actor.world.rot.y = ((this->actor.wallYaw - this->actor.world.rot.y) + this->actor.wallYaw) - 0x8000; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BOMB_BOUND); + Actor_MoveForward(&this->actor); + this->actor.speedXZ *= 0.7f; + this->actor.bgCheckFlags &= ~8; + } + + if (!(this->actor.bgCheckFlags & 1)) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.08f); + } else { + Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); + if ((this->actor.bgCheckFlags & 2) && (this->actor.velocity.y < -3.0f)) { + func_8002F850(globalCtx, &this->actor); + this->actor.velocity.y *= -0.3f; + this->actor.bgCheckFlags &= ~2; + } else if (this->timer >= 4) { + func_8002F580(&this->actor, globalCtx); + } + } + + Actor_MoveForward(&this->actor); +} + +void EnBom_WaitForRelease(EnBom* this, GlobalContext* globalCtx) { + // if parent is NULL bomb has been released + if (Actor_HasNoParent(&this->actor, globalCtx)) { + EnBom_SetupAction(this, EnBom_Move); + EnBom_Move(this, globalCtx); + } +} + +void EnBom_Explode(EnBom* this, GlobalContext* globalCtx) { + Player* player; + + if (this->explosionCollider.elements[0].dim.modelSphere.radius == 0) { + this->actor.flags |= ACTOR_FLAG_5; + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + } + + this->explosionCollider.elements[0].dim.worldSphere.radius += this->actor.shape.rot.z + 8; + + if (this->actor.params == BOMB_EXPLOSION) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->explosionCollider.base); + } + + if (globalCtx->envCtx.adjLight1Color[0] != 0) { + globalCtx->envCtx.adjLight1Color[0] -= 25; + } + + if (globalCtx->envCtx.adjLight1Color[1] != 0) { + globalCtx->envCtx.adjLight1Color[1] -= 25; + } + + if (globalCtx->envCtx.adjLight1Color[2] != 0) { + globalCtx->envCtx.adjLight1Color[2] -= 25; + } + + if (globalCtx->envCtx.adjAmbientColor[0] != 0) { + globalCtx->envCtx.adjAmbientColor[0] -= 25; + } + + if (globalCtx->envCtx.adjAmbientColor[1] != 0) { + globalCtx->envCtx.adjAmbientColor[1] -= 25; + } + + if (globalCtx->envCtx.adjAmbientColor[2] != 0) { + globalCtx->envCtx.adjAmbientColor[2] -= 25; + } + + if (this->timer == 0) { + player = GET_PLAYER(globalCtx); + + if ((player->stateFlags1 & 0x800) && (player->heldActor == &this->actor)) { + player->actor.child = NULL; + player->heldActor = NULL; + player->interactRangeActor = NULL; + player->stateFlags1 &= ~0x800; + } + + Actor_Kill(&this->actor); + } +} + +void EnBom_Update(Actor* thisx, GlobalContext* globalCtx2) { + Vec3f effVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f bomb2Accel = { 0.0f, 0.1f, 0.0f }; + Vec3f effAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f effPos; + Vec3f dustAccel = { 0.0f, 0.6f, 0.0f }; + Color_RGBA8 dustColor = { 255, 255, 255, 255 }; + s32 pad; + GlobalContext* globalCtx = globalCtx2; + EnBom* this = (EnBom*)thisx; + + thisx->gravity = -1.2f; + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 67) { + Audio_PlayActorSound2(thisx, NA_SE_PL_TAKE_OUT_SHIELD); + Actor_SetScale(thisx, 0.01f); + } + + if ((thisx->xzDistToPlayer >= 20.0f) || (ABS(thisx->yDistToPlayer) >= 80.0f)) { + this->bumpOn = true; + } + + this->actionFunc(this, globalCtx); + + Actor_UpdateBgCheckInfo(globalCtx, thisx, 5.0f, 10.0f, 15.0f, 0x1F); + + if (thisx->params == BOMB_BODY) { + if (this->timer < 63) { + dustAccel.y = 0.2f; + + // spawn spark effect on even frames + effPos = thisx->world.pos; + effPos.y += 17.0f; + if ((globalCtx->gameplayFrames % 2) == 0) { + EffectSsGSpk_SpawnFuse(globalCtx, thisx, &effPos, &effVelocity, &effAccel); + } + + Audio_PlayActorSound2(thisx, NA_SE_IT_BOMB_IGNIT - SFX_FLAG); + + effPos.y += 3.0f; + func_8002829C(globalCtx, &effPos, &effVelocity, &dustAccel, &dustColor, &dustColor, 50, 5); + } + + if ((this->bombCollider.base.acFlags & AC_HIT) || ((this->bombCollider.base.ocFlags1 & OC1_HIT) && + (this->bombCollider.base.oc->category == ACTORCAT_ENEMY))) { + this->timer = 0; + thisx->shape.rot.z = 0; + } else { + // if a lit stick touches the bomb, set timer to 100 + // these bombs never have a timer over 70, so this isnt used + if ((this->timer > 100) && Player_IsBurningStickInRange(globalCtx, &thisx->world.pos, 30.0f, 50.0f)) { + this->timer = 100; + } + } + + dustAccel.y = 0.2f; + effPos = thisx->world.pos; + effPos.y += 10.0f; + + // double bomb flash speed and adjust red color at certain times during the countdown + if ((this->timer == 3) || (this->timer == 20) || (this->timer == 40)) { + thisx->shape.rot.z = 0; + this->flashSpeedScale >>= 1; + } + + if ((this->timer < 100) && ((this->timer & (this->flashSpeedScale + 1)) != 0)) { + Math_SmoothStepToF(&this->flashIntensity, 140.0f, 1.0f, 140.0f / this->flashSpeedScale, 0.0f); + } else { + Math_SmoothStepToF(&this->flashIntensity, 0.0f, 1.0f, 140.0f / this->flashSpeedScale, 0.0f); + } + + if (this->timer < 3) { + Actor_SetScale(thisx, thisx->scale.x + 0.002f); + } + + if (this->timer == 0) { + effPos = thisx->world.pos; + + effPos.y += 10.0f; + if (Actor_HasParent(thisx, globalCtx)) { + effPos.y += 30.0f; + } + + EffectSsBomb2_SpawnLayered(globalCtx, &effPos, &effVelocity, &bomb2Accel, 100, + (thisx->shape.rot.z * 6) + 19); + + effPos.y = thisx->floorHeight; + if (thisx->floorHeight > BGCHECK_Y_MIN) { + EffectSsBlast_SpawnWhiteShockwave(globalCtx, &effPos, &effVelocity, &effAccel); + } + + Audio_PlayActorSound2(thisx, NA_SE_IT_BOMB_EXPLOSION); + + globalCtx->envCtx.adjLight1Color[0] = globalCtx->envCtx.adjLight1Color[1] = + globalCtx->envCtx.adjLight1Color[2] = 250; + + globalCtx->envCtx.adjAmbientColor[0] = globalCtx->envCtx.adjAmbientColor[1] = + globalCtx->envCtx.adjAmbientColor[2] = 250; + + Camera_AddQuake(&globalCtx->mainCamera, 2, 0xB, 8); + thisx->params = BOMB_EXPLOSION; + this->timer = 10; + thisx->flags |= ACTOR_FLAG_5; + EnBom_SetupAction(this, EnBom_Explode); + } + } + + Actor_SetFocus(thisx, 20.0f); + + if (thisx->params <= BOMB_BODY) { + Collider_UpdateCylinder(thisx, &this->bombCollider); + + // if link is not holding the bomb anymore and bump conditions are met, subscribe to OC + if (!Actor_HasParent(thisx, globalCtx) && this->bumpOn) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bombCollider.base); + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bombCollider.base); + } + + if ((thisx->scale.x >= 0.01f) && (thisx->params != BOMB_EXPLOSION)) { + if (thisx->yDistToWater >= 20.0f) { + EffectSsDeadSound_SpawnStationary(globalCtx, &thisx->projectedPos, NA_SE_IT_BOMB_UNEXPLOSION, true, + DEADSOUND_REPEAT_MODE_OFF, 10); + Actor_Kill(thisx); + return; + } + if (thisx->bgCheckFlags & 0x40) { + thisx->bgCheckFlags &= ~0x40; + Audio_PlayActorSound2(thisx, NA_SE_EV_BOMB_DROP_WATER); + } + } +} + +void EnBom_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBom* this = (EnBom*)thisx; + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bom.c", 913); + + if (thisx->params == BOMB_BODY) { + func_80093D18(globalCtx->state.gfxCtx); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + func_8002EBCC(thisx, globalCtx, 0); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bom.c", 928), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBombCapDL); + Matrix_RotateZYX(0x4000, 0, 0, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bom.c", 934), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->flashIntensity, 0, 40, 255); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, (s16)this->flashIntensity, 0, 40, 255); + gSPDisplayList(POLY_OPA_DISP++, gBombBodyDL); + Collider_UpdateSpheres(0, &this->explosionCollider); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bom.c", 951); +} diff --git a/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.h b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.h new file mode 100644 index 000000000..f6d6f8855 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom/z_en_bom.h @@ -0,0 +1,28 @@ +#ifndef Z_EN_BOM_H +#define Z_EN_BOM_H + +#include "ultra64.h" +#include "global.h" + +struct EnBom; + +typedef void (*EnBomActionFunc)(struct EnBom*, struct GlobalContext*); + +typedef struct EnBom { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder bombCollider; + /* 0x0198 */ ColliderJntSph explosionCollider; + /* 0x01B8 */ ColliderJntSphElement explosionColliderItems[1]; + /* 0x01F8 */ s16 timer; + /* 0x01FA */ s16 flashSpeedScale; + /* 0x01FC */ f32 flashIntensity; + /* 0x0200 */ u8 bumpOn; + /* 0x0204 */ EnBomActionFunc actionFunc; +} EnBom; // size = 0x0208 + +typedef enum { + /* 0x00 */ BOMB_BODY, + /* 0x01 */ BOMB_EXPLOSION +} EnBomType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c new file mode 100644 index 000000000..59f328a77 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c @@ -0,0 +1,537 @@ +#include "z_en_bom_bowl_man.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" +#include "objects/object_bg/object_bg.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_27) + +typedef enum { + /* 0 */ CHU_GIRL_EYES_ASLEEP, + /* 1 */ CHU_GIRL_EYES_OPEN_SLOWLY, + /* 2 */ CHU_GIRL_EYES_BLINK_RAPIDLY, + /* 3 */ CHU_GIRL_EYES_AWAKE +} BombchuGirlEyeMode; + +void EnBomBowlMan_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBomBowlMan_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBomBowlMan_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBomBowlMan_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBomBowMan_SetupWaitAsleep(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_WaitAsleep(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_TalkAsleep(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_WakeUp(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_BlinkAwake(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_CheckBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_WaitNotBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_TalkNotBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_SetupRunGame(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_RunGame(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowlMan_HandlePlayChoice(EnBomBowlMan* this, GlobalContext* globalCtx); +void func_809C41FC(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_SetupChooseShowPrize(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowMan_ChooseShowPrize(EnBomBowlMan* this, GlobalContext* globalCtx); +void EnBomBowlMan_BeginPlayGame(EnBomBowlMan* this, GlobalContext* globalCtx); + +const ActorInit En_Bom_Bowl_Man_InitVars = { + ACTOR_EN_BOM_BOWL_MAN, + ACTORCAT_NPC, + FLAGS, + OBJECT_BG, + sizeof(EnBomBowlMan), + (ActorFunc)EnBomBowlMan_Init, + (ActorFunc)EnBomBowlMan_Destroy, + (ActorFunc)EnBomBowlMan_Update, + (ActorFunc)EnBomBowlMan_Draw, + NULL, +}; + +void EnBomBowlMan_Init(Actor* thisx, GlobalContext* globalCtx2) { + static f32 cuccoColliderDims[][3] = { { 16.0f, 46.0f, 0.0f }, { 36.0f, 56.0f, 0.0f } }; + static Vec3f cuccoSpawnPos[] = { { 60, -60, -430 }, { 0, -120, -620 } }; + static f32 cuccoScales[] = { 0.01f, 0.03f }; + EnBomBowlMan* this = (EnBomBowlMan*)thisx; + EnSyatekiNiw* cucco; + s32 i; + GlobalContext* globalCtx = globalCtx2; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gChuGirlSkel, &gChuGirlNoddingOffAnim, this->jointTable, + this->morphTable, 11); + // "☆ Man, my shoulders hurt~ ☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆ もー 肩こっちゃうよねぇ〜 \t\t ☆ \n" VT_RST); + // "☆ Isn't there some sort of job that will pay better and be more relaxing? ☆ %d" + osSyncPrintf(VT_FGCOL(GREEN) "☆ もっとラクしてもうかるバイトないかしら? ☆ %d\n" VT_RST, + globalCtx->bombchuBowlingStatus); + this->posCopy = this->actor.world.pos; + this->actor.shape.yOffset = -60.0f; + Actor_SetScale(&this->actor, 0.013f); + + for (i = 0; i < 2; i++) { + cucco = (EnSyatekiNiw*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_SYATEKI_NIW, cuccoSpawnPos[i].x, + cuccoSpawnPos[i].y, cuccoSpawnPos[i].z, 0, 0, 0, 1); + + if (cucco != NULL) { + cucco->unk_2F4 = cuccoScales[i]; + cucco->collider.dim.radius = (s16)cuccoColliderDims[i][0]; + cucco->collider.dim.height = (s16)cuccoColliderDims[i][1]; + } + } + + this->prizeSelect = (s16)Rand_ZeroFloat(4.99f); + this->actor.targetMode = 1; + this->actionFunc = EnBomBowMan_SetupWaitAsleep; +} + +void EnBomBowlMan_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnBomBowMan_SetupWaitAsleep(EnBomBowlMan* this, GlobalContext* globalCtx) { + this->frameCount = (f32)Animation_GetLastFrame(&gChuGirlNoddingOffAnim); + Animation_Change(&this->skelAnime, &gChuGirlNoddingOffAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, -10.0f); + this->actor.textId = 0xC0; + this->dialogState = TEXT_STATE_EVENT; + this->actionFunc = EnBomBowMan_WaitAsleep; +} + +void EnBomBowMan_WaitAsleep(EnBomBowlMan* this, GlobalContext* globalCtx) { + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnBomBowMan_TalkAsleep; + } else { + yawDiff = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)); + + if (!(this->actor.xzDistToPlayer > 120.0f) && (yawDiff < 0x4300)) { + func_8002F2CC(&this->actor, globalCtx, 120.0f); + } + } +} + +void EnBomBowMan_TalkAsleep(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actionFunc = EnBomBowMan_WakeUp; + } +} + +void EnBomBowMan_WakeUp(EnBomBowlMan* this, GlobalContext* globalCtx) { + this->frameCount = (f32)Animation_GetLastFrame(&gChuGirlWakeUpAnim); + Animation_Change(&this->skelAnime, &gChuGirlWakeUpAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_ONCE, -10.0f); + this->eyeMode = CHU_GIRL_EYES_OPEN_SLOWLY; + this->actionFunc = EnBomBowMan_BlinkAwake; +} + +void EnBomBowMan_BlinkAwake(EnBomBowlMan* this, GlobalContext* globalCtx) { + f32 frameCount = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (frameCount == 30.0f) { + this->dialogState = TEXT_STATE_EVENT; + + // Check for beaten Dodongo's Cavern + if ((gSaveContext.eventChkInf[2] & 0x20) || BREG(2)) { + this->actor.textId = 0xBF; + } else { + this->actor.textId = 0x7058; + } + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + + if ((this->eyeTextureIndex == 0) && (this->eyeMode == CHU_GIRL_EYES_BLINK_RAPIDLY) && (this->blinkTimer == 0)) { + // Blink twice, then move on + this->eyeTextureIndex = 2; + this->blinkCount++; + if (this->blinkCount >= 3) { + this->actionFunc = EnBomBowMan_CheckBeatenDC; + } + } +} + +void EnBomBowMan_CheckBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->frameCount = (f32)Animation_GetLastFrame(&gChuGirlLeanOverCounterAnim); + Animation_Change(&this->skelAnime, &gChuGirlLeanOverCounterAnim, 1.0f, 0.0f, this->frameCount, ANIMMODE_LOOP, + -10.0f); + this->eyeMode = CHU_GIRL_EYES_AWAKE; + this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + + // Check for beaten Dodongo's Cavern + if (!((gSaveContext.eventChkInf[2] & 0x20) || BREG(2))) { + this->actionFunc = EnBomBowMan_WaitNotBeatenDC; + } else { + this->actor.textId = 0x18; + this->dialogState = TEXT_STATE_CHOICE; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->actionFunc = EnBomBowlMan_HandlePlayChoice; + } + } +} + +void EnBomBowMan_WaitNotBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnBomBowMan_TalkNotBeatenDC; + } else { + func_8002F2CC(&this->actor, globalCtx, 120.0f); + } +} + +void EnBomBowMan_TalkNotBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = EnBomBowMan_WaitNotBeatenDC; + } +} + +void EnBomBowMan_SetupRunGame(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->minigamePlayStatus == 0) { + if (!this->startedPlaying) { + this->actor.textId = 0x18; + } else { + this->actor.textId = 0x1A; + } + + this->dialogState = TEXT_STATE_CHOICE; + } else { + this->actor.textId = 0x19; + this->dialogState = TEXT_STATE_EVENT; + } + + this->actionFunc = EnBomBowMan_RunGame; +} + +void EnBomBowMan_RunGame(EnBomBowlMan* this, GlobalContext* globalCtx) { + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + + if (BREG(3)) { + osSyncPrintf(VT_FGCOL(RED) "☆ game_play->bomchu_game_flag ☆ %d\n" VT_RST, globalCtx->bombchuBowlingStatus); + // "HOW'S THE FIRST WALL DOING?" + osSyncPrintf(VT_FGCOL(RED) "☆ 壁1の状態どう? ☆ %d\n" VT_RST, this->wallStatus[0]); + // "HOW'S THE SECOND WALL DOING?" + osSyncPrintf(VT_FGCOL(RED) "☆ 壁2の状態どう? ☆ %d\n" VT_RST, this->wallStatus[1]); + // "HOLE INFORMATION" + osSyncPrintf(VT_FGCOL(RED) "☆ 穴情報\t ☆ %d\n" VT_RST, this->bowlPit->status); + osSyncPrintf("\n\n"); + } + + this->gameResult = 0; + + if (this->bowlPit != NULL) { + if ((this->wallStatus[0] != 1) && (this->wallStatus[1] != 1) && (this->bowlPit->status == 2)) { + this->gameResult = 1; // Won + this->bowlPit->status = 0; + // "Center HIT!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 中央HIT!!!! ☆☆☆☆☆ \n" VT_RST); + } + + if ((globalCtx->bombchuBowlingStatus == -1) && + (globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].length == 0) && (this->bowlPit->status == 0) && + (this->wallStatus[0] != 1) && (this->wallStatus[1] != 1)) { + this->gameResult = 2; // Lost + // "Bombchu lost" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ ボムチュウ消化 ☆☆☆☆☆ \n" VT_RST); + } + } + + if (this->gameResult != 0) { // won or lost + this->actor.textId = 0x1A; + this->dialogState = TEXT_STATE_CHOICE; + this->minigamePlayStatus = 0; + + if ((this->exItem != NULL) && (this->exItem->actor.update != NULL)) { + this->exItem->killItem = true; + this->exItem = NULL; + } + + globalCtx->bombchuBowlingStatus = 0; + this->playingAgain = true; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + + if (this->gameResult == 2) { + func_8002DF54(globalCtx, NULL, 8); + } + this->actionFunc = EnBomBowlMan_HandlePlayChoice; + } else { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->minigamePlayStatus == 0) { + this->actionFunc = EnBomBowlMan_HandlePlayChoice; + } else { + this->actionFunc = func_809C41FC; + } + } else { + yawDiff = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)); + + if (!(this->actor.xzDistToPlayer > 120.0f) && (yawDiff < 0x4300)) { + func_8002F2CC(&this->actor, globalCtx, 120.0f); + } + } + } +} + +void EnBomBowlMan_HandlePlayChoice(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // Yes + if (gSaveContext.rupees >= 30) { + Rupees_ChangeBy(-30); + this->minigamePlayStatus = 1; + this->wallStatus[0] = this->wallStatus[1] = 0; + globalCtx->bombchuBowlingStatus = 10; + Flags_SetSwitch(globalCtx, 0x38); + + if (!this->startedPlaying && !this->playingAgain) { + this->actor.textId = 0x19; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + this->actionFunc = func_809C41FC; + } else { + this->actor.textId = 0x1B; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + OnePointCutscene_Init(globalCtx, 8010, -99, NULL, MAIN_CAM); + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = EnBomBowMan_SetupChooseShowPrize; + } + } else { + this->playingAgain = false; + this->actor.textId = 0x85; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + this->actionFunc = func_809C41FC; + } + break; + + case 1: // No + this->playingAgain = false; + this->actor.textId = 0x2D; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + this->actionFunc = func_809C41FC; + } + } +} + +void func_809C41FC(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + if (((this->actor.textId == 0x2D) || (this->actor.textId == 0x85)) && Flags_GetSwitch(globalCtx, 0x38)) { + Flags_UnsetSwitch(globalCtx, 0x38); + } + + if (this->minigamePlayStatus == 1) { + this->actor.textId = 0x1B; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + OnePointCutscene_Init(globalCtx, 8010, -99, NULL, MAIN_CAM); + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = EnBomBowMan_SetupChooseShowPrize; + } else { + if (this->gameResult == 2) { + func_8002DF54(globalCtx, NULL, 7); + } + this->actionFunc = EnBomBowMan_SetupRunGame; + } + } +} + +void EnBomBowMan_SetupChooseShowPrize(EnBomBowlMan* this, GlobalContext* globalCtx) { + Vec3f accel = { 0.0f, 0.1f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + + SkelAnime_Update(&this->skelAnime); + + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + pos.x = 148.0f; + pos.y = 40.0f; + pos.z = 300.0f; + EffectSsBomb2_SpawnLayered(globalCtx, &pos, &velocity, &accel, 50, 15); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_GOODS_APPEAR); + this->prizeRevealTimer = 10; + this->actionFunc = EnBomBowMan_ChooseShowPrize; + } +} + +static Vec3f sPrizePosOffset[] = { + { 0.0f, 22.0f, 0.0f }, { 0.0f, 22.0f, 0.0f }, { 0.0f, 8.0f, 0.0f }, { 0.0f, 9.0f, 0.0f }, { 0.0f, -2.0f, 0.0f }, +}; + +static s16 sPrizeRot[] = { 0x4268, 0x4268, -0x03E8, 0x0000, 0x4268, 0x0000 }; + +void EnBomBowMan_ChooseShowPrize(EnBomBowlMan* this, GlobalContext* globalCtx) { + s16 prizeTemp; + s32 pad; + + SkelAnime_Update(&this->skelAnime); + + if (this->prizeRevealTimer == 0) { + switch (this->prizeSelect) { + case 0: + prizeTemp = EXITEM_BOMB_BAG_BOWLING; + if (gSaveContext.itemGetInf[1] & 2) { + prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING; + } + break; + case 1: + prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING; + break; + case 2: + prizeTemp = EXITEM_BOMBCHUS_BOWLING; + break; + case 3: + prizeTemp = EXITEM_HEART_PIECE_BOWLING; + if (gSaveContext.itemGetInf[1] & 4) { + prizeTemp = EXITEM_PURPLE_RUPEE_BOWLING; + } + break; + case 4: + prizeTemp = EXITEM_BOMBS_BOWLING; + break; + } + + this->prizeIndex = prizeTemp; + + if (BREG(7)) { + this->prizeIndex = BREG(7) - 1; + } + + this->exItem = (EnExItem*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_ITEM, + sPrizePosOffset[this->prizeIndex].x + 148.0f, + sPrizePosOffset[this->prizeIndex].y + 40.0f, + sPrizePosOffset[this->prizeIndex].z + 300.0f, 0, + sPrizeRot[this->prizeIndex], 0, this->prizeIndex + EXITEM_COUNTER); + + if (!this->startedPlaying) { + this->bowlPit = (EnBomBowlPit*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, + ACTOR_EN_BOM_BOWL_PIT, 0.0f, 90.0f, -860.0f, 0, 0, 0, 0); + if (this->bowlPit != NULL) { + this->bowlPit->prizeIndex = this->prizeIndex; + } + } else { + this->bowlPit->prizeIndex = this->prizeIndex; + } + + this->bowlPit->start = 1; + this->minigamePlayStatus = 2; + this->actor.textId = 0x405A; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + + // Cycle through prizes in order + this->prizeSelect++; + if (this->prizeSelect >= 5) { + this->prizeSelect = 0; + } + this->actionFunc = EnBomBowlMan_BeginPlayGame; + } +} + +void EnBomBowlMan_BeginPlayGame(EnBomBowlMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((Message_GetState(&globalCtx->msgCtx) == this->dialogState) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + this->startedPlaying = true; + + if (BREG(2)) { + BREG(2) = 0; + } + + // "Wow" + osSyncPrintf(VT_FGCOL(YELLOW) "☆ わー ☆ %d\n" VT_RST, globalCtx->bombchuBowlingStatus); + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = EnBomBowMan_SetupRunGame; + } +} + +void EnBomBowlMan_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBomBowlMan* this = (EnBomBowlMan*)thisx; + + this->timer++; + this->actor.focus.pos.y = 60.0f; + Actor_SetFocus(&this->actor, 60.0f); + + switch (this->eyeMode) { + case CHU_GIRL_EYES_ASLEEP: + this->eyeTextureIndex = 2; + break; + case CHU_GIRL_EYES_OPEN_SLOWLY: + if (this->eyeTextureIndex > 0) { + this->eyeTextureIndex--; + } else { + this->blinkTimer = 30; + this->eyeMode = CHU_GIRL_EYES_BLINK_RAPIDLY; + } + break; + case CHU_GIRL_EYES_BLINK_RAPIDLY: + if ((this->blinkTimer == 0) && (this->eyeTextureIndex > 0)) { + this->eyeTextureIndex--; + } + break; + default: + if (this->blinkTimer == 0) { + this->eyeTextureIndex++; + if (this->eyeTextureIndex >= 3) { + this->eyeTextureIndex = 0; + this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + + func_80038290(globalCtx, &this->actor, &this->unk_218, &this->unk_224, this->actor.focus.pos); + break; + } + + DECR(this->prizeRevealTimer); + DECR(this->blinkTimer); + + this->actionFunc(this, globalCtx); +} + +s32 EnBomBowlMan_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnBomBowlMan* this = (EnBomBowlMan*)thisx; + + if (limbIndex == 4) { // head + rot->x += this->unk_218.y; + rot->z += this->unk_218.z; + } + + return false; +} + +void EnBomBowlMan_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gChuGirlEyeOpenTex, gChuGirlEyeHalfTex, gChuGirlEyeClosedTex }; + EnBomBowlMan* this = (EnBomBowlMan*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bom_bowl_man.c", 907); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTextureIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnBomBowlMan_OverrideLimbDraw, NULL, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bom_bowl_man.c", 923); +} diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h new file mode 100644 index 000000000..83b6217cd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h @@ -0,0 +1,42 @@ +#ifndef Z_EN_BOM_BOWL_MAN_H +#define Z_EN_BOM_BOWL_MAN_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" +#include "overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h" + +struct EnBomBowlMan; + +typedef void (*EnBomBowlManActionFunc)(struct EnBomBowlMan*, GlobalContext*); + +typedef struct EnBomBowlMan { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[11]; + /* 0x01D2 */ Vec3s morphTable[11]; + /* 0x0214 */ EnBomBowlManActionFunc actionFunc; + /* 0x0218 */ Vec3s unk_218; + /* 0x021E */ char unk_21E[0x6]; + /* 0x0224 */ Vec3s unk_224; + /* 0x022A */ s16 prizeRevealTimer; + /* 0x022C */ s16 timer; + /* 0x022E */ s16 dialogState; + /* 0x0230 */ s16 prizeIndex; + /* 0x0232 */ s16 startedPlaying; // set to true after starting the first round + /* 0x0234 */ s16 eyeTextureIndex; + /* 0x0236 */ s16 blinkTimer; + /* 0x0238 */ s16 eyeMode; + /* 0x023A */ s16 blinkCount; + /* 0x023C */ s16 playingAgain; // whether player is playing again after a game + /* 0x023E */ s16 wallStatus[2]; + /* 0x0242 */ s16 prizeSelect; + /* 0x0244 */ s16 gameResult; // 0 = default, 1 = lost, 2 = won + /* 0x0248 */ Vec3f posCopy; // Set and not used + /* 0x0254 */ f32 frameCount; + /* 0x0258 */ u8 minigamePlayStatus; // 0 = default, 1 = paid, 2 = playing + /* 0x025C */ EnBomBowlPit* bowlPit; + /* 0x0260 */ EnExItem* exItem; +} EnBomBowlMan; // size = 0x0264 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c new file mode 100644 index 000000000..1eca90f68 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.c @@ -0,0 +1,223 @@ +#include "z_en_bom_bowl_pit.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnBomBowlPit_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBomBowlPit_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBomBowlPit_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnBomBowlPit_SetupDetectHit(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_DetectHit(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_CameraDollyIn(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_SpawnPrize(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_SetupGivePrize(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_GivePrize(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_WaitTillPrizeGiven(EnBomBowlPit* this, GlobalContext* globalCtx); +void EnBomBowlPit_Reset(EnBomBowlPit* this, GlobalContext* globalCtx); + +static s32 sGetItemIds[] = { GI_BOMB_BAG_30, GI_HEART_PIECE, GI_BOMBCHUS_10, GI_BOMBS_1, GI_RUPEE_PURPLE }; + +const ActorInit En_Bom_Bowl_Pit_InitVars = { + ACTOR_EN_BOM_BOWL_PIT, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnBomBowlPit), + (ActorFunc)EnBomBowlPit_Init, + (ActorFunc)EnBomBowlPit_Destroy, + (ActorFunc)EnBomBowlPit_Update, + NULL, + NULL, +}; + +void EnBomBowlPit_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBomBowlPit* this = (EnBomBowlPit*)thisx; + + this->actionFunc = EnBomBowlPit_SetupDetectHit; +} + +void EnBomBowlPit_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnBomBowlPit_SetupDetectHit(EnBomBowlPit* this, GlobalContext* globalCtx) { + if (this->start != 0) { + this->start = this->status = 0; + this->actionFunc = EnBomBowlPit_DetectHit; + } +} + +void EnBomBowlPit_DetectHit(EnBomBowlPit* this, GlobalContext* globalCtx) { + EnBomChu* chu; + Vec3f chuPosDiff; + + if (globalCtx->cameraPtrs[MAIN_CAM]->setting == CAM_SET_CHU_BOWLING) { + chu = (EnBomChu*)globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + + while (chu != NULL) { + if ((&chu->actor == &this->actor) || (chu->actor.id != ACTOR_EN_BOM_CHU)) { + chu = (EnBomChu*)chu->actor.next; + continue; + } + + chuPosDiff.x = chu->actor.world.pos.x - this->actor.world.pos.x; + chuPosDiff.y = chu->actor.world.pos.y - this->actor.world.pos.y; + chuPosDiff.z = chu->actor.world.pos.z - this->actor.world.pos.z; + + if (((fabsf(chuPosDiff.x) < 40.0f) || (BREG(2))) && ((fabsf(chuPosDiff.y) < 40.0f) || (BREG(2))) && + ((fabsf(chuPosDiff.z) < 40.0f) || (BREG(2)))) { + func_8002DF54(globalCtx, NULL, 8); + chu->timer = 1; + + this->camId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->camId, CAM_STAT_ACTIVE); + + this->unk_1C8.x = this->unk_1C8.y = this->unk_1C8.z = 0.1f; + this->unk_1A4.x = this->unk_1A4.y = this->unk_1A4.z = 0.1f; + + this->unk_180.x = this->unk_168.x = globalCtx->view.lookAt.x; + this->unk_180.y = this->unk_168.y = globalCtx->view.lookAt.y; + this->unk_180.z = this->unk_168.z = globalCtx->view.lookAt.z; + + this->unk_18C.x = this->unk_174.x = globalCtx->view.eye.x; + this->unk_18C.y = this->unk_174.y = globalCtx->view.eye.y; + this->unk_18C.z = this->unk_174.z = globalCtx->view.eye.z; + + this->unk_1BC.x = 20.0f; + this->unk_1BC.y = 100.0f; + this->unk_1BC.z = -800.0f; + + this->unk_198.x = 20.0f; + this->unk_198.y = 50.0f; + this->unk_198.z = -485.0f; + + this->unk_1B0.x = fabsf(this->unk_18C.x - this->unk_198.x) * 0.02f; + this->unk_1B0.y = fabsf(this->unk_18C.y - this->unk_198.y) * 0.02f; + this->unk_1B0.z = fabsf(this->unk_18C.z - this->unk_198.z) * 0.02f; + + this->unk_1D4.x = fabsf(this->unk_180.x - this->unk_1BC.x) * 0.02f; + this->unk_1D4.y = fabsf(this->unk_180.y - this->unk_1BC.y) * 0.02f; + this->unk_1D4.z = fabsf(this->unk_180.z - this->unk_1BC.z) * 0.02f; + + Gameplay_CameraSetAtEye(globalCtx, this->camId, &this->unk_180, &this->unk_18C); + this->actor.textId = 0xF; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_154 = TEXT_STATE_EVENT; + func_80078884(NA_SE_EV_HIT_SOUND); + func_8002DF54(globalCtx, NULL, 8); + this->status = 1; + this->actionFunc = EnBomBowlPit_CameraDollyIn; + break; + } else { + chu = (EnBomChu*)chu->actor.next; + } + } + } +} + +void EnBomBowlPit_CameraDollyIn(EnBomBowlPit* this, GlobalContext* globalCtx) { + if (this->camId != SUBCAM_FREE) { + Math_ApproachF(&this->unk_180.x, this->unk_1BC.x, this->unk_1C8.x, this->unk_1D4.x); + Math_ApproachF(&this->unk_180.y, this->unk_1BC.y, this->unk_1C8.y, this->unk_1D4.y); + Math_ApproachF(&this->unk_180.z, this->unk_1BC.z, this->unk_1C8.z, this->unk_1D4.z); + Math_ApproachF(&this->unk_18C.x, this->unk_198.x, this->unk_1A4.x, this->unk_1B0.x); + Math_ApproachF(&this->unk_18C.y, this->unk_198.y, this->unk_1A4.y, this->unk_1B0.y); + Math_ApproachF(&this->unk_18C.z, this->unk_198.z, this->unk_1A4.z, this->unk_1B0.z); + } + + Gameplay_CameraSetAtEye(globalCtx, this->camId, &this->unk_180, &this->unk_18C); + + if ((this->unk_154 == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + } + + if ((fabsf(this->unk_18C.x - this->unk_198.x) < 5.0f) && (fabsf(this->unk_18C.y - this->unk_198.y) < 5.0f) && + (fabsf(this->unk_18C.z - this->unk_198.z) < 5.0f) && (fabsf(this->unk_180.x - this->unk_1BC.x) < 5.0f) && + (fabsf(this->unk_180.y - this->unk_1BC.y) < 5.0f) && (fabsf(this->unk_180.z - this->unk_1BC.z) < 5.0f)) { + Message_CloseTextbox(globalCtx); + this->timer = 30; + this->actionFunc = EnBomBowlPit_SpawnPrize; + } +} + +void EnBomBowlPit_SpawnPrize(EnBomBowlPit* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + this->exItem = (EnExItem*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_ITEM, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z - 70.0f, 0, 0, 0, this->prizeIndex); + if (this->exItem != NULL) { + this->actionFunc = EnBomBowlPit_SetupGivePrize; + } + } +} + +void EnBomBowlPit_SetupGivePrize(EnBomBowlPit* this, GlobalContext* globalCtx) { + if (this->exItemDone != 0) { + switch (this->prizeIndex) { + case EXITEM_BOMB_BAG_BOWLING: + gSaveContext.itemGetInf[1] |= 2; + break; + case EXITEM_HEART_PIECE_BOWLING: + gSaveContext.itemGetInf[1] |= 4; + break; + } + + Gameplay_ClearCamera(globalCtx, this->camId); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = EnBomBowlPit_GivePrize; + } +} + +void EnBomBowlPit_GivePrize(EnBomBowlPit* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + func_8002DF54(globalCtx, NULL, 7); + this->getItemId = sGetItemIds[this->prizeIndex]; + + if ((this->getItemId == GI_BOMB_BAG_30) && (CUR_CAPACITY(UPG_BOMB_BAG) == 30)) { + this->getItemId = GI_BOMB_BAG_40; + } + + player->stateFlags1 &= ~0x20000000; + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, this->getItemId, 2000.0f, 1000.0f); + player->stateFlags1 |= 0x20000000; + this->actionFunc = EnBomBowlPit_WaitTillPrizeGiven; +} + +void EnBomBowlPit_WaitTillPrizeGiven(EnBomBowlPit* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = EnBomBowlPit_Reset; + } else { + func_8002F434(&this->actor, globalCtx, this->getItemId, 2000.0f, 1000.0f); + } +} + +void EnBomBowlPit_Reset(EnBomBowlPit* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + // "Normal termination"/"completion" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); + if (this->getItemId == GI_HEART_PIECE) { + gSaveContext.healthAccumulator = 0x140; + // "Ah recovery!" (?) + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ あぁ回復! ☆☆☆☆☆ \n" VT_RST); + } + this->exItemDone = 0; + this->status = 2; + this->actionFunc = EnBomBowlPit_SetupDetectHit; + } +} + +void EnBomBowlPit_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBomBowlPit* this = (EnBomBowlPit*)thisx; + + this->actionFunc(this, globalCtx); + + if (this->timer != 0) { + this->timer--; + } +} diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h new file mode 100644 index 000000000..af5d8d3e1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h @@ -0,0 +1,38 @@ +#ifndef Z_EN_BOM_BOWL_PIT_H +#define Z_EN_BOM_BOWL_PIT_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" + +struct EnBomBowlPit; + +typedef void (*EnBomBowlPitActionFunc)(struct EnBomBowlPit*, GlobalContext*); + +typedef struct EnBomBowlPit { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnBomBowlPitActionFunc actionFunc; + /* 0x0150 */ u16 unk_150; + /* 0x0152 */ s16 camId; + /* 0x0154 */ s16 unk_154; + /* 0x0156 */ s16 exItemDone; // Set by EnExItem? + /* 0x0158 */ s16 timer; + /* 0x015A */ s16 prizeIndex; + /* 0x015C */ s16 start; + /* 0x0160 */ s32 getItemId; + /* 0x0164 */ u8 status; + /* 0x0168 */ Vec3f unk_168; // set and not used? + /* 0x0174 */ Vec3f unk_174; // set and not used? + /* 0x0180 */ Vec3f unk_180; // camera at (start) + /* 0x018C */ Vec3f unk_18C; // camera eye (start) + /* 0x0198 */ Vec3f unk_198; // camera eye (end) + /* 0x01A4 */ Vec3f unk_1A4; // camera eye (scales) + /* 0x01B0 */ Vec3f unk_1B0; // camera eye (maxsteps) + /* 0x01BC */ Vec3f unk_1BC; // camera at (end) + /* 0x01C8 */ Vec3f unk_1C8; // camera at (scales) + /* 0x01D4 */ Vec3f unk_1D4; // camera eye (maxsteps) + /* 0x01E0 */ EnExItem* exItem; + /* 0x01E4 */ char unk_1E4[0x3520]; +} EnBomBowlPit; // size = 0x3704 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c b/soh/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c new file mode 100644 index 000000000..715ca552e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.c @@ -0,0 +1,517 @@ +#include "z_en_bom_chu.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +#define BOMBCHU_SCALE 0.01f + +void EnBomChu_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBomChu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBomChu_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBomChu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBomChu_WaitForRelease(EnBomChu* this, GlobalContext* globalCtx); +void EnBomChu_Move(EnBomChu* this, GlobalContext* globalCtx); +void EnBomChu_WaitForKill(EnBomChu* this, GlobalContext* globalCtx); + +const ActorInit En_Bom_Chu_InitVars = { + ACTOR_EN_BOM_CHU, + ACTORCAT_EXPLOSIVE, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnBomChu), + (ActorFunc)EnBomChu_Init, + (ActorFunc)EnBomChu_Destroy, + (ActorFunc)EnBomChu_Update, + (ActorFunc)EnBomChu_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElemInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 12 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_1 | OC1_TYPE_2, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElemInit), + sJntSphElemInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 1000 * BOMBCHU_SCALE, ICHAIN_STOP), +}; + +void EnBomChu_Init(Actor* thisx, GlobalContext* globalCtx) { + static u8 p1StartColor[] = { 250, 0, 0, 250 }; + static u8 p2StartColor[] = { 200, 0, 0, 130 }; + static u8 p1EndColor[] = { 150, 0, 0, 100 }; + static u8 p2EndColor[] = { 100, 0, 0, 50 }; + EnBomChu* this = (EnBomChu*)thisx; + EffectBlureInit1 blureInit; + s32 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderElements); + + this->collider.elements[0].dim.worldSphere.radius = this->collider.elements[0].dim.modelSphere.radius; + + for (i = 0; i < 4; i++) { + blureInit.p1StartColor[i] = p1StartColor[i]; + blureInit.p2StartColor[i] = p2StartColor[i]; + blureInit.p1EndColor[i] = p1EndColor[i]; + blureInit.p2EndColor[i] = p2EndColor[i]; + } + + blureInit.elemDuration = 16; + blureInit.unkFlag = 0; + blureInit.calcMode = 0; + + Effect_Add(globalCtx, &this->blure1Index, EFFECT_BLURE1, 0, 0, &blureInit); + Effect_Add(globalCtx, &this->blure2Index, EFFECT_BLURE1, 0, 0, &blureInit); + + this->actor.room = -1; + this->timer = 120; + this->actionFunc = EnBomChu_WaitForRelease; +} + +void EnBomChu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBomChu* this = (EnBomChu*)thisx; + + Effect_Delete(globalCtx, this->blure1Index); + Effect_Delete(globalCtx, this->blure2Index); + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnBomChu_Explode(EnBomChu* this, GlobalContext* globalCtx) { + EnBom* bomb; + s32 i; + + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, BOMB_BODY); + if (bomb != NULL) { + bomb->timer = 0; + } + + this->timer = 1; + this->actor.speedXZ = 0.0f; + + if (this->actor.yDistToWater > 0.0f) { + for (i = 0; i < 40; i++) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 1.0f, 5.0f, 30.0f, 0.25f); + } + } + + this->actionFunc = EnBomChu_WaitForKill; +} + +void EnBomChu_CrossProduct(Vec3f* a, Vec3f* b, Vec3f* dest) { + dest->x = (a->y * b->z) - (a->z * b->y); + dest->y = (a->z * b->x) - (a->x * b->z); + dest->z = (a->x * b->y) - (a->y * b->x); +} + +void EnBomChu_UpdateFloorPoly(EnBomChu* this, CollisionPoly* floorPoly, GlobalContext* globalCtx) { + Vec3f normal; + Vec3f vec; + f32 angle; + f32 magnitude; + f32 normDotUp; + MtxF mf; + + this->actor.floorPoly = floorPoly; + + normal.x = COLPOLY_GET_NORMAL(floorPoly->normal.x); + normal.y = COLPOLY_GET_NORMAL(floorPoly->normal.y); + normal.z = COLPOLY_GET_NORMAL(floorPoly->normal.z); + + normDotUp = DOTXYZ(normal, this->axisUp); + + if (!(fabsf(normDotUp) >= 1.0f)) { + angle = Math_FAcosF(normDotUp); + + if (!(angle < 0.001f)) { + EnBomChu_CrossProduct(&this->axisUp, &normal, &vec); + //! @bug this function expects a unit vector but `vec` is not normalized + Matrix_RotateAxis(angle, &vec, MTXMODE_NEW); + + Matrix_MultVec3f(&this->axisLeft, &vec); + this->axisLeft = vec; + + EnBomChu_CrossProduct(&this->axisLeft, &normal, &this->axisForwards); + + magnitude = Math3D_Vec3fMagnitude(&this->axisForwards); + + if (magnitude < 0.001f) { + EnBomChu_Explode(this, globalCtx); + return; + } + + this->axisForwards.x *= 1.0f / magnitude; + this->axisForwards.y *= 1.0f / magnitude; + this->axisForwards.z *= 1.0f / magnitude; + + this->axisUp = normal; + + if (1) {} + + // mf = (axisLeft | axisUp | axisForwards) + + mf.xx = this->axisLeft.x; + mf.yx = this->axisLeft.y; + mf.zx = this->axisLeft.z; + + mf.xy = normal.x; + mf.yy = normal.y; + mf.zy = normal.z; + + mf.xz = this->axisForwards.x; + mf.yz = this->axisForwards.y; + mf.zz = this->axisForwards.z; + + Matrix_MtxFToYXZRotS(&mf, &this->actor.world.rot, 0); + + // A hack for preventing bombchus from sticking to ledges. + // The visual rotation reverts the sign inversion (shape.rot.x = -world.rot.x). + // The better fix would be making func_8002D908 compute XYZ velocity better, + // or not using it and make the bombchu compute its own velocity. + this->actor.world.rot.x = -this->actor.world.rot.x; + } + } +} + +void EnBomChu_WaitForRelease(EnBomChu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + EnBomChu_Explode(this, globalCtx); + return; + } + + if (Actor_HasNoParent(&this->actor, globalCtx)) { + this->actor.world.pos = player->actor.world.pos; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actor.shape.rot.y = player->actor.shape.rot.y; + + // rot.y = 0 -> +z (forwards in model space) + this->axisForwards.x = Math_SinS(this->actor.shape.rot.y); + this->axisForwards.y = 0.0f; + this->axisForwards.z = Math_CosS(this->actor.shape.rot.y); + + // +y (up in model space) + this->axisUp.x = 0.0f; + this->axisUp.y = 1.0f; + this->axisUp.z = 0.0f; + + // rot.y = 0 -> +x (left in model space) + this->axisLeft.x = Math_SinS(this->actor.shape.rot.y + 0x4000); + this->axisLeft.y = 0; + this->axisLeft.z = Math_CosS(this->actor.shape.rot.y + 0x4000); + + this->actor.speedXZ = 8.0f; + //! @bug there is no NULL check on the floor poly. If the player is out of bounds the floor poly will be NULL + //! and will cause a crash inside this function. + EnBomChu_UpdateFloorPoly(this, this->actor.floorPoly, globalCtx); + this->actor.flags |= ACTOR_FLAG_0; // make chu targetable + func_8002F850(globalCtx, &this->actor); + this->actionFunc = EnBomChu_Move; + } +} + +void EnBomChu_Move(EnBomChu* this, GlobalContext* globalCtx) { + CollisionPoly* polySide; + CollisionPoly* polyUpDown; + s32 bgIdSide; + s32 bgIdUpDown; + s32 i; + f32 lineLength; + Vec3f posA; + Vec3f posB; + Vec3f posSide; + Vec3f posUpDown; + + this->actor.speedXZ = 8.0f; + lineLength = this->actor.speedXZ * 2.0f; + + if (this->timer != 0) { + this->timer--; + } + + if ((this->timer == 0) || (this->collider.base.acFlags & AC_HIT) || + ((this->collider.base.ocFlags1 & OC1_HIT) && (this->collider.base.oc->category != ACTORCAT_PLAYER))) { + EnBomChu_Explode(this, globalCtx); + return; + } + + posA.x = this->actor.world.pos.x + (this->axisUp.x * 2.0f); + posA.y = this->actor.world.pos.y + (this->axisUp.y * 2.0f); + posA.z = this->actor.world.pos.z + (this->axisUp.z * 2.0f); + + posB.x = this->actor.world.pos.x - (this->axisUp.x * 4.0f); + posB.y = this->actor.world.pos.y - (this->axisUp.y * 4.0f); + posB.z = this->actor.world.pos.z - (this->axisUp.z * 4.0f); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &posA, &posB, &posUpDown, &polyUpDown, true, true, true, true, + &bgIdUpDown) && + !(func_80041DB8(&globalCtx->colCtx, polyUpDown, bgIdUpDown) & 0x30) && // && not crawl space? + !SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, polyUpDown, bgIdUpDown)) { + // forwards + posB.x = (this->axisForwards.x * lineLength) + posA.x; + posB.y = (this->axisForwards.y * lineLength) + posA.y; + posB.z = (this->axisForwards.z * lineLength) + posA.z; + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &posA, &posB, &posSide, &polySide, true, true, true, true, + &bgIdSide) && + !(func_80041DB8(&globalCtx->colCtx, polySide, bgIdSide) & 0x30) && + !SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, polySide, bgIdSide)) { + EnBomChu_UpdateFloorPoly(this, polySide, globalCtx); + this->actor.world.pos = posSide; + this->actor.floorBgId = bgIdSide; + this->actor.speedXZ = 0.0f; + } else { + if (this->actor.floorPoly != polyUpDown) { + EnBomChu_UpdateFloorPoly(this, polyUpDown, globalCtx); + } + + this->actor.world.pos = posUpDown; + this->actor.floorBgId = bgIdUpDown; + } + } else { + this->actor.speedXZ = 0.0f; + lineLength *= 3.0f; + posA = posB; + + for (i = 0; i < 3; i++) { + if (i == 0) { + // backwards + posB.x = posA.x - (this->axisForwards.x * lineLength); + posB.y = posA.y - (this->axisForwards.y * lineLength); + posB.z = posA.z - (this->axisForwards.z * lineLength); + } else if (i == 1) { + // left + posB.x = posA.x + (this->axisLeft.x * lineLength); + posB.y = posA.y + (this->axisLeft.y * lineLength); + posB.z = posA.z + (this->axisLeft.z * lineLength); + } else { + // right + posB.x = posA.x - (this->axisLeft.x * lineLength); + posB.y = posA.y - (this->axisLeft.y * lineLength); + posB.z = posA.z - (this->axisLeft.z * lineLength); + } + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &posA, &posB, &posSide, &polySide, true, true, true, true, + &bgIdSide) && + !(func_80041DB8(&globalCtx->colCtx, polySide, bgIdSide) & 0x30) && + !SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, polySide, bgIdSide)) { + EnBomChu_UpdateFloorPoly(this, polySide, globalCtx); + this->actor.world.pos = posSide; + this->actor.floorBgId = bgIdSide; + break; + } + } + + if (i == 3) { + // no collision nearby + EnBomChu_Explode(this, globalCtx); + } + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, -this->actor.world.rot.x, 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 0x800); + Math_ScaledStepToS(&this->actor.shape.rot.z, this->actor.world.rot.z, 0x800); + + func_8002F8F0(&this->actor, NA_SE_IT_BOMBCHU_MOVE - SFX_FLAG); +} + +void EnBomChu_WaitForKill(EnBomChu* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + Actor_Kill(&this->actor); + } +} + +/** + * Transform coordinates from model space to world space, according to current orientation. + * `posModel` is expected to already be at world scale (1/100 compared to model scale) + */ +void EnBomChu_ModelToWorld(EnBomChu* this, Vec3f* posModel, Vec3f* dest) { + f32 x = posModel->x + this->visualJitter; + + dest->x = this->actor.world.pos.x + (this->axisLeft.x * x) + (this->axisUp.x * posModel->y) + + (this->axisForwards.x * posModel->z); + dest->y = this->actor.world.pos.y + (this->axisLeft.y * x) + (this->axisUp.y * posModel->y) + + (this->axisForwards.y * posModel->z); + dest->z = this->actor.world.pos.z + (this->axisLeft.z * x) + (this->axisUp.z * posModel->y) + + (this->axisForwards.z * posModel->z); +} + +void EnBomChu_SpawnRipples(EnBomChu* this, GlobalContext* globalCtx, f32 y) { + Vec3f pos; + + pos.x = this->actor.world.pos.x; + pos.y = y; + pos.z = this->actor.world.pos.z; + + EffectSsGRipple_Spawn(globalCtx, &pos, 70, 500, 0); + EffectSsGRipple_Spawn(globalCtx, &pos, 70, 500, 4); + EffectSsGRipple_Spawn(globalCtx, &pos, 70, 500, 8); +} + +void EnBomChu_Update(Actor* thisx, GlobalContext* globalCtx2) { + static Vec3f blureP1Model = { 0.0f, 7.0f, -6.0f }; + static Vec3f blureP2LeftModel = { 12.0f, 0.0f, -5.0f }; + static Vec3f blureP2RightModel = { -12.0f, 0.0f, -5.0f }; + GlobalContext* globalCtx = globalCtx2; + EnBomChu* this = (EnBomChu*)thisx; + s16 yaw; + f32 sin; + f32 cos; + f32 tempX; + Vec3f blureP1; + Vec3f blureP2; + WaterBox* waterBox; + f32 waterY; + + if (this->actor.floorBgId != BGCHECK_SCENE) { + yaw = this->actor.shape.rot.y; + func_800433A4(&globalCtx->colCtx, this->actor.floorBgId, &this->actor); + + if (yaw != this->actor.shape.rot.y) { + yaw = this->actor.shape.rot.y - yaw; + + sin = Math_SinS(yaw); + cos = Math_CosS(yaw); + + tempX = this->axisForwards.x; + this->axisForwards.x = (this->axisForwards.z * sin) + (cos * tempX); + this->axisForwards.z = (this->axisForwards.z * cos) - (sin * tempX); + + tempX = this->axisUp.x; + this->axisUp.x = (this->axisUp.z * sin) + (cos * tempX); + this->axisUp.z = (this->axisUp.z * cos) - (sin * tempX); + + tempX = this->axisLeft.x; + this->axisLeft.x = (this->axisLeft.z * sin) + (cos * tempX); + this->axisLeft.z = (this->axisLeft.z * cos) - (sin * tempX); + } + } + + this->actionFunc(this, globalCtx); + func_8002D97C(&this->actor); + + this->collider.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x; + this->collider.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y; + this->collider.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->actionFunc != EnBomChu_WaitForRelease) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + Actor_SetFocus(&this->actor, 0.0f); + + if (this->actionFunc == EnBomChu_Move) { + this->visualJitter = + (5.0f + (Rand_ZeroOne() * 3.0f)) * Math_SinS(((Rand_ZeroOne() * (f32)0x200) + (f32)0x3000) * this->timer); + + EnBomChu_ModelToWorld(this, &blureP1Model, &blureP1); + + EnBomChu_ModelToWorld(this, &blureP2LeftModel, &blureP2); + EffectBlure_AddVertex(Effect_GetByIndex(this->blure1Index), &blureP1, &blureP2); + + EnBomChu_ModelToWorld(this, &blureP2RightModel, &blureP2); + EffectBlure_AddVertex(Effect_GetByIndex(this->blure2Index), &blureP1, &blureP2); + + waterY = this->actor.world.pos.y; + + if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, + &waterY, &waterBox)) { + this->actor.yDistToWater = waterY - this->actor.world.pos.y; + + if (this->actor.yDistToWater < 0.0f) { + if (this->actor.bgCheckFlags & 0x20) { + EnBomChu_SpawnRipples(this, globalCtx, waterY); + } + + this->actor.bgCheckFlags &= ~0x20; + } else { + if (!(this->actor.bgCheckFlags & 0x20) && (this->timer != 120)) { + EnBomChu_SpawnRipples(this, globalCtx, waterY); + } else { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 0.0f, 3.0f, 15.0f, 0.25f); + } + + this->actor.bgCheckFlags |= 0x20; + } + } else { + this->actor.bgCheckFlags &= ~0x20; + this->actor.yDistToWater = BGCHECK_Y_MIN; + } + } +} + +void EnBomChu_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBomChu* this = (EnBomChu*)thisx; + f32 colorIntensity; + s32 blinkHalfPeriod; + s32 blinkTime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bom_chu.c", 921); + + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + + if (this->timer >= 40) { + blinkTime = this->timer % 20; + blinkHalfPeriod = 10; + } else if (this->timer >= 10) { + blinkTime = this->timer % 10; + blinkHalfPeriod = 5; + } else { + blinkTime = this->timer & 1; + blinkHalfPeriod = 1; + } + + if (blinkTime > blinkHalfPeriod) { + blinkTime = 2 * blinkHalfPeriod - blinkTime; + } + + colorIntensity = blinkTime / (f32)blinkHalfPeriod; + + gDPSetEnvColor(POLY_OPA_DISP++, 9.0f + (colorIntensity * 209.0f), 9.0f + (colorIntensity * 34.0f), + 35.0f + (colorIntensity * -35.0f), 255); + Matrix_Translate(this->visualJitter * (1.0f / BOMBCHU_SCALE), 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bom_chu.c", 956), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBombchuDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bom_chu.c", 961); +} diff --git a/soh/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h b/soh/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h new file mode 100644 index 000000000..9efc1e0d5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_BOM_CHU_H +#define Z_EN_BOM_CHU_H + +#include "ultra64.h" +#include "global.h" + +struct EnBomChu; + +typedef void (*EnBomChuActionFunc)(struct EnBomChu*, GlobalContext*); + +typedef struct EnBomChu { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnBomChuActionFunc actionFunc; + /* 0x0150 */ s16 timer; + /* 0x0154 */ Vec3f axisForwards; + /* 0x0160 */ Vec3f axisUp; + /* 0x016C */ Vec3f axisLeft; + /* 0x0178 */ f32 visualJitter; + /* 0x017C */ s32 blure1Index; + /* 0x0180 */ s32 blure2Index; + /* 0x0184 */ ColliderJntSph collider; + /* 0x01A4 */ ColliderJntSphElement colliderElements[1]; +} EnBomChu; // size = 0x01E4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bombf/z_en_bombf.c b/soh/src/overlays/actors/ovl_En_Bombf/z_en_bombf.c new file mode 100644 index 000000000..5ec38a3fb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bombf/z_en_bombf.c @@ -0,0 +1,515 @@ +/* + * File: z_en_bombf.c + * Overlay: ovl_En_Bombf + * Description: Bomb Flower + */ + +#include "z_en_bombf.h" +#include "objects/object_bombf/object_bombf.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4) + +void EnBombf_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBombf_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBombf_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBombf_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBombf_Move(EnBombf* this, GlobalContext* globalCtx); +void EnBombf_GrowBomb(EnBombf* this, GlobalContext* globalCtx); +void EnBombf_WaitForRelease(EnBombf* this, GlobalContext* globalCtx); +void EnBombf_Explode(EnBombf* this, GlobalContext* globalCtx); +void EnBombf_SetupGrowBomb(EnBombf* this, s16 params); + +const ActorInit En_Bombf_InitVars = { + ACTOR_EN_BOMBF, + ACTORCAT_PROP, + FLAGS, + OBJECT_BOMBF, + sizeof(EnBombf), + (ActorFunc)EnBombf_Init, + (ActorFunc)EnBombf_Destroy, + (ActorFunc)EnBombf_Update, + (ActorFunc)EnBombf_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER | AC_TYPE_OTHER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0x0003F828, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 9, 18, 10, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000008, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 0 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ALL, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +void EnBombf_SetupAction(EnBombf* this, EnBombfActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnBombf_Init(Actor* thisx, GlobalContext* globalCtx) { + f32 shapeUnk10 = 0.0f; + s32 pad; + EnBombf* this = (EnBombf*)thisx; + + Actor_SetScale(thisx, 0.01f); + this->unk_200 = 1; + Collider_InitCylinder(globalCtx, &this->bombCollider); + Collider_InitJntSph(globalCtx, &this->explosionCollider); + Collider_SetCylinder(globalCtx, &this->bombCollider, thisx, &sCylinderInit); + Collider_SetJntSph(globalCtx, &this->explosionCollider, thisx, &sJntSphInit, &this->explosionColliderItems[0]); + + if (thisx->params == BOMBFLOWER_BODY) { + shapeUnk10 = 1000.0f; + } + + ActorShape_Init(&thisx->shape, shapeUnk10, ActorShadow_DrawCircle, 12.0f); + thisx->focus.pos = thisx->world.pos; + + if (Actor_FindNearby(globalCtx, thisx, ACTOR_BG_DDAN_KD, ACTORCAT_BG, 10000.0f) != NULL) { + thisx->flags |= ACTOR_FLAG_5; + } + + thisx->colChkInfo.cylRadius = 10.0f; + thisx->colChkInfo.cylHeight = 10; + thisx->targetMode = 0; + + if (thisx->params == BOMBFLOWER_BODY) { + this->timer = 140; + this->flashSpeedScale = 15; + thisx->gravity = -1.5f; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_EXPLOSIVE); + thisx->colChkInfo.mass = 200; + thisx->flags &= ~ACTOR_FLAG_0; + EnBombf_SetupAction(this, EnBombf_Move); + } else { + thisx->colChkInfo.mass = MASS_IMMOVABLE; + this->bumpOn = true; + this->flowerBombScale = 1.0f; + EnBombf_SetupGrowBomb(this, thisx->params); + } + + thisx->uncullZoneScale += 31000.0f; + thisx->uncullZoneForward += 31000.0f; +} + +void EnBombf_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBombf* this = (EnBombf*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->bombCollider); + Collider_DestroyJntSph(globalCtx, &this->explosionCollider); +} + +void EnBombf_SetupGrowBomb(EnBombf* this, s16 params) { + EnBombf_SetupAction(this, EnBombf_GrowBomb); +} + +void EnBombf_GrowBomb(EnBombf* this, GlobalContext* globalCtx) { + EnBombf* bombFlower; + s32 pad; + s32 pad1; + Player* player = GET_PLAYER(globalCtx); + s32 pad2; + + if (this->flowerBombScale >= 1.0f) { + if (Actor_HasParent(&this->actor, globalCtx)) { + bombFlower = (EnBombf*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOMBF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (bombFlower != NULL) { + func_8002F5C4(&this->actor, &bombFlower->actor, globalCtx); + this->timer = 180; + this->flowerBombScale = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_PL_PULL_UP_ROCK); + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + player->actor.child = NULL; + player->heldActor = NULL; + player->interactRangeActor = NULL; + this->actor.parent = NULL; + player->stateFlags1 &= ~0x800; + } + } else if (this->bombCollider.base.acFlags & AC_HIT) { + this->bombCollider.base.acFlags &= ~AC_HIT; + + if (this->bombCollider.base.ac->category != ACTORCAT_BOSS) { + bombFlower = + (EnBombf*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOMBF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (bombFlower != NULL) { + bombFlower->unk_200 = 1; + bombFlower->timer = 0; + this->timer = 180; + this->actor.flags &= ~ACTOR_FLAG_0; + this->flowerBombScale = 0.0f; + } + } + } else { + if (Player_IsBurningStickInRange(globalCtx, &this->actor.world.pos, 30.0f, 50.0f)) { + bombFlower = + (EnBombf*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOMBF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (bombFlower != NULL) { + bombFlower->timer = 100; + this->timer = 180; + this->actor.flags &= ~ACTOR_FLAG_0; + this->flowerBombScale = 0.0f; + } + } else { + if (!Actor_HasParent(&this->actor, globalCtx)) { + func_8002F580(&this->actor, globalCtx); + } else { + player->actor.child = NULL; + player->heldActor = NULL; + player->interactRangeActor = NULL; + this->actor.parent = NULL; + player->stateFlags1 &= ~0x800; + this->actor.world.pos = this->actor.home.pos; + } + } + } + } else { + if (this->timer == 0) { + this->flowerBombScale += 0.05f; + if (this->flowerBombScale >= 1.0f) { + this->actor.flags |= ACTOR_FLAG_0; + } + } + + if (Actor_HasParent(&this->actor, globalCtx)) { + player->actor.child = NULL; + player->heldActor = NULL; + player->interactRangeActor = NULL; + this->actor.parent = NULL; + player->stateFlags1 &= ~0x800; + this->actor.world.pos = this->actor.home.pos; + } + } +} + +void EnBombf_Move(EnBombf* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + // setting flowerBombScale does not do anything in the context of a bomb that link picks up + // this and the assignment below are probably left overs + this->flowerBombScale = 0.0f; + EnBombf_SetupAction(this, EnBombf_WaitForRelease); + this->actor.room = -1; + return; + } + + this->flowerBombScale = 1.0f; + + if (!(this->actor.bgCheckFlags & 1)) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.025f, 0.0f); + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.5f, 0.0f); + if ((this->actor.bgCheckFlags & 2) && (this->actor.velocity.y < -6.0f)) { + func_8002F850(globalCtx, &this->actor); + this->actor.velocity.y *= -0.5f; + } else if (this->timer >= 4) { + func_8002F580(&this->actor, globalCtx); + } + } +} + +void EnBombf_WaitForRelease(EnBombf* this, GlobalContext* globalCtx) { + // if parent is NULL bomb has been released + if (Actor_HasNoParent(&this->actor, globalCtx)) { + EnBombf_SetupAction(this, EnBombf_Move); + EnBombf_Move(this, globalCtx); + } else { + this->actor.velocity.y = 0.0f; + } +} + +void EnBombf_Explode(EnBombf* this, GlobalContext* globalCtx) { + Player* player; + + if (this->explosionCollider.elements[0].dim.modelSphere.radius == 0) { + this->actor.flags |= ACTOR_FLAG_5; + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + } + + this->explosionCollider.elements[0].dim.modelSphere.radius += 8; + this->explosionCollider.elements[0].dim.worldSphere.radius = + this->explosionCollider.elements[0].dim.modelSphere.radius; + + if (this->actor.params == BOMBFLOWER_EXPLOSION) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->explosionCollider.base); + } + + if (globalCtx->envCtx.adjLight1Color[0] != 0) { + globalCtx->envCtx.adjLight1Color[0] -= 25; + } + + if (globalCtx->envCtx.adjLight1Color[1] != 0) { + globalCtx->envCtx.adjLight1Color[1] -= 25; + } + + if (globalCtx->envCtx.adjLight1Color[2] != 0) { + globalCtx->envCtx.adjLight1Color[2] -= 25; + } + + if (globalCtx->envCtx.adjAmbientColor[0] != 0) { + globalCtx->envCtx.adjAmbientColor[0] -= 25; + } + + if (globalCtx->envCtx.adjAmbientColor[1] != 0) { + globalCtx->envCtx.adjAmbientColor[1] -= 25; + } + + if (globalCtx->envCtx.adjAmbientColor[2] != 0) { + globalCtx->envCtx.adjAmbientColor[2] -= 25; + } + + if (this->timer == 0) { + player = GET_PLAYER(globalCtx); + + if ((player->stateFlags1 & 0x800) && (player->heldActor == &this->actor)) { + player->actor.child = NULL; + player->heldActor = NULL; + player->interactRangeActor = NULL; + player->stateFlags1 &= ~0x800; + } + + Actor_Kill(&this->actor); + } +} + +void EnBombf_Update(Actor* thisx, GlobalContext* globalCtx) { + Vec3f effVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f bomb2Accel = { 0.0f, 0.1f, 0.0f }; + Vec3f effAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f effPos; + Vec3f dustAccel = { 0.0f, 0.6f, 0.0f }; + Color_RGBA8 dustColor = { 255, 255, 255, 255 }; + s32 pad[2]; + EnBombf* this = (EnBombf*)thisx; + + if ((this->unk_200 != 0) && (this->timer != 0)) { + this->timer--; + } + + if ((!this->bumpOn) && (!Actor_HasParent(thisx, globalCtx)) && + ((thisx->xzDistToPlayer >= 20.0f) || (ABS(thisx->yDistToPlayer) >= 80.0f))) { + this->bumpOn = true; + } + + this->actionFunc(this, globalCtx); + + if (thisx->params == BOMBFLOWER_BODY) { + Actor_MoveForward(thisx); + } + + if (thisx->gravity != 0.0f) { + DREG(6) = 1; + Actor_UpdateBgCheckInfo(globalCtx, thisx, 5.0f, 10.0f, 0.0f, 0x1F); + DREG(6) = 0; + } + + if (thisx->params == BOMBFLOWER_BODY) { + + if ((thisx->velocity.y > 0.0f) && (thisx->bgCheckFlags & 0x10)) { + thisx->velocity.y = -thisx->velocity.y; + } + + // rebound bomb off the wall it hits + if ((thisx->speedXZ != 0.0f) && (thisx->bgCheckFlags & 8)) { + + if (ABS((s16)(thisx->wallYaw - thisx->world.rot.y)) > 0x4000) { + if (1) {} + thisx->world.rot.y = ((thisx->wallYaw - thisx->world.rot.y) + thisx->wallYaw) - 0x8000; + } + Audio_PlayActorSound2(thisx, NA_SE_EV_BOMB_BOUND); + Actor_MoveForward(thisx); + DREG(6) = 1; + Actor_UpdateBgCheckInfo(globalCtx, thisx, 5.0f, 10.0f, 0.0f, 0x1F); + DREG(6) = 0; + thisx->speedXZ *= 0.7f; + thisx->bgCheckFlags &= ~8; + } + + if ((this->bombCollider.base.acFlags & AC_HIT) || ((this->bombCollider.base.ocFlags1 & OC1_HIT) && + (this->bombCollider.base.oc->category == ACTORCAT_ENEMY))) { + this->unk_200 = 1; + this->timer = 0; + } else { + // if a lit stick touches the bomb, set timer to 100 + if ((this->timer > 100) && Player_IsBurningStickInRange(globalCtx, &thisx->world.pos, 30.0f, 50.0f)) { + this->timer = 100; + } + } + + if (this->unk_200 != 0) { + dustAccel.y = 0.2f; + effPos = thisx->world.pos; + effPos.y += 25.0f; + if (this->timer < 127) { + // spawn spark effect on even frames + if ((globalCtx->gameplayFrames % 2) == 0) { + EffectSsGSpk_SpawnFuse(globalCtx, thisx, &effPos, &effVelocity, &effAccel); + } + Audio_PlayActorSound2(thisx, NA_SE_IT_BOMB_IGNIT - SFX_FLAG); + + effPos.y += 3.0f; + func_8002829C(globalCtx, &effPos, &effVelocity, &dustAccel, &dustColor, &dustColor, 50, 5); + } + + // double bomb flash speed and adjust red color at certain times during the countdown + if ((this->timer == 3) || (this->timer == 30) || (this->timer == 50) || (this->timer == 70)) { + this->flashSpeedScale >>= 1; + } + + if ((this->timer < 100) && ((this->timer & (this->flashSpeedScale + 1)) != 0)) { + Math_SmoothStepToF(&this->flashIntensity, 150.0f, 1.0f, 150.0f / this->flashSpeedScale, 0.0f); + } else { + Math_SmoothStepToF(&this->flashIntensity, 0.0f, 1.0f, 150.0f / this->flashSpeedScale, 0.0f); + } + + if (this->timer < 3) { + Actor_SetScale(thisx, thisx->scale.x + 0.002f); + } + + if (this->timer == 0) { + effPos = thisx->world.pos; + + effPos.y += 10.0f; + + if (Actor_HasParent(thisx, globalCtx)) { + effPos.y += 30.0f; + } + + EffectSsBomb2_SpawnLayered(globalCtx, &effPos, &effVelocity, &bomb2Accel, 100, 19); + + effPos.y = thisx->floorHeight; + if (thisx->floorHeight > BGCHECK_Y_MIN) { + EffectSsBlast_SpawnWhiteShockwave(globalCtx, &effPos, &effVelocity, &effAccel); + } + + Audio_PlayActorSound2(thisx, NA_SE_IT_BOMB_EXPLOSION); + globalCtx->envCtx.adjLight1Color[0] = globalCtx->envCtx.adjLight1Color[1] = + globalCtx->envCtx.adjLight1Color[2] = 250; + globalCtx->envCtx.adjAmbientColor[0] = globalCtx->envCtx.adjAmbientColor[1] = + globalCtx->envCtx.adjAmbientColor[2] = 250; + Camera_AddQuake(&globalCtx->mainCamera, 2, 0xB, 8); + thisx->params = BOMBFLOWER_EXPLOSION; + this->timer = 10; + thisx->flags |= ACTOR_FLAG_5; + EnBombf_SetupAction(this, EnBombf_Explode); + } + } + } + + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 10.0f; + + if (thisx->params <= BOMBFLOWER_BODY) { + + Collider_UpdateCylinder(thisx, &this->bombCollider); + + if ((this->flowerBombScale >= 1.0f) && (this->bumpOn)) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bombCollider.base); + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bombCollider.base); + } + + if ((thisx->scale.x >= 0.01f) && (thisx->params != BOMBFLOWER_EXPLOSION)) { + if (thisx->yDistToWater >= 20.0f) { + EffectSsDeadSound_SpawnStationary(globalCtx, &thisx->projectedPos, NA_SE_IT_BOMB_UNEXPLOSION, true, + DEADSOUND_REPEAT_MODE_OFF, 10); + Actor_Kill(thisx); + return; + } + if (thisx->bgCheckFlags & 0x40) { + thisx->bgCheckFlags &= ~0x40; + Audio_PlayActorSound2(thisx, NA_SE_EV_BOMB_DROP_WATER); + } + } +} + +Gfx* EnBombf_NewMtxDList(GraphicsContext* gfxCtx, GlobalContext* globalCtx) { + Gfx* displayList; + Gfx* displayListHead; + + displayList = Graph_Alloc(gfxCtx, 5 * sizeof(Gfx)); + displayListHead = displayList; + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(displayListHead++, Matrix_NewMtx(gfxCtx, "../z_en_bombf.c", 1021), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPEndDisplayList(displayListHead); + return displayList; +} + +void EnBombf_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBombf* this = (EnBombf*)thisx; + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bombf.c", 1034); + + if (thisx->params <= BOMBFLOWER_BODY) { + func_80093D18(globalCtx->state.gfxCtx); + + if (thisx->params != BOMBFLOWER_BODY) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bombf.c", 1041), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBombFlowerLeavesDL); + gSPDisplayList(POLY_OPA_DISP++, gBombFlowerBaseLeavesDL); + + Matrix_Translate(0.0f, 1000.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(this->flowerBombScale, this->flowerBombScale, this->flowerBombScale, MTXMODE_APPLY); + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 200, 255, 200, 255); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->flashIntensity, 20, 10, 0); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bombf.c", 1054), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, + SEGMENTED_TO_VIRTUAL(EnBombf_NewMtxDList(globalCtx->state.gfxCtx, globalCtx))); + gSPDisplayList(POLY_OPA_DISP++, gBombFlowerBombAndSparkDL); + } else { + Collider_UpdateSpheres(0, &this->explosionCollider); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bombf.c", 1063); +} diff --git a/soh/src/overlays/actors/ovl_En_Bombf/z_en_bombf.h b/soh/src/overlays/actors/ovl_En_Bombf/z_en_bombf.h new file mode 100644 index 000000000..e33880451 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bombf/z_en_bombf.h @@ -0,0 +1,31 @@ +#ifndef Z_EN_BOMBF_H +#define Z_EN_BOMBF_H + +#include "ultra64.h" +#include "global.h" + +struct EnBombf; + +typedef void (*EnBombfActionFunc)(struct EnBombf*, struct GlobalContext*); + +typedef struct EnBombf { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder bombCollider; + /* 0x0198 */ ColliderJntSph explosionCollider; + /* 0x01B8 */ ColliderJntSphElement explosionColliderItems[1]; + /* 0x01F8 */ s16 timer; + /* 0x01FC */ EnBombfActionFunc actionFunc; + /* 0x0200 */ s32 unk_200; + /* 0x0204 */ u8 bumpOn; + /* 0x0206 */ s16 flashSpeedScale; + /* 0x0208 */ f32 flashIntensity; + /* 0x020C */ f32 flowerBombScale; +} EnBombf; // size = 0x0210 + +typedef enum { + /* 0xFFFF */ BOMBFLOWER_FLOWER = -1, + /* 0x0000 */ BOMBFLOWER_BODY, + /* 0x0001 */ BOMBFLOWER_EXPLOSION +} EnBombfType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c b/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c new file mode 100644 index 000000000..f06ba43f4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.c @@ -0,0 +1,278 @@ +/* + * File: z_en_boom.c + * Overlay: ovl_En_Boom + * Description: Thrown Boomerang. Actor spawns when thrown and is killed when caught. + */ + +#include "z_en_boom.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnBoom_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBoom_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBoom_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBoom_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBoom_Fly(EnBoom* this, GlobalContext* globalCtx); + +const ActorInit En_Boom_InitVars = { + ACTOR_EN_BOOM, + ACTORCAT_MISC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnBoom), + (ActorFunc)EnBoom_Init, + (ActorFunc)EnBoom_Destroy, + (ActorFunc)EnBoom_Update, + (ActorFunc)EnBoom_Draw, + NULL, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_PLAYER, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK2, + { 0x00000010, 0x00, 0x01 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_NEAREST | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_VEC3S(shape.rot, 0, ICHAIN_STOP), +}; + +void EnBoom_SetupAction(EnBoom* this, EnBoomActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnBoom_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBoom* this = (EnBoom*)thisx; + EffectBlureInit1 blure; + + this->actor.room = -1; + + Actor_ProcessInitChain(&this->actor, sInitChain); + + blure.p1StartColor[0] = 255; + blure.p1StartColor[1] = 255; + blure.p1StartColor[2] = 100; + blure.p1StartColor[3] = 255; + + blure.p2StartColor[0] = 255; + blure.p2StartColor[1] = 255; + blure.p2StartColor[2] = 100; + blure.p2StartColor[3] = 64; + + blure.p1EndColor[0] = 255; + blure.p1EndColor[1] = 255; + blure.p1EndColor[2] = 100; + blure.p1EndColor[3] = 0; + + blure.p2EndColor[0] = 255; + blure.p2EndColor[1] = 255; + blure.p2EndColor[2] = 100; + blure.p2EndColor[3] = 0; + + blure.elemDuration = 8; + blure.unkFlag = 0; + blure.calcMode = 0; + + Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE1, 0, 0, &blure); + + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->actor, &sQuadInit); + + EnBoom_SetupAction(this, EnBoom_Fly); +} + +void EnBoom_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBoom* this = (EnBoom*)thisx; + + Effect_Delete(globalCtx, this->effectIndex); + Collider_DestroyQuad(globalCtx, &this->collider); +} + +void EnBoom_Fly(EnBoom* this, GlobalContext* globalCtx) { + Actor* target; + Player* player; + s32 collided; + s16 yawTarget; + s16 yawDiff; + s16 pitchTarget; + s16 pitchDiff; + s32 pad1; + f32 distXYZScale; + f32 distFromLink; + DynaPolyActor* hitActor; + s32 hitDynaID; + Vec3f hitPoint; + s32 pad2; + + player = GET_PLAYER(globalCtx); + target = this->moveTo; + + // If the boomerang is moving toward a targeted actor, handle setting the proper x and y angle to fly toward it. + if (target != NULL) { + yawTarget = Actor_WorldYawTowardPoint(&this->actor, &target->focus.pos); + yawDiff = this->actor.world.rot.y - yawTarget; + + pitchTarget = Actor_WorldPitchTowardPoint(&this->actor, &target->focus.pos); + pitchDiff = this->actor.world.rot.x - pitchTarget; + + distXYZScale = (200.0f - Math_Vec3f_DistXYZ(&this->actor.world.pos, &target->focus.pos)) * 0.005f; + if (distXYZScale < 0.12f) { + distXYZScale = 0.12f; + } + + if ((target != &player->actor) && ((target->update == NULL) || (ABS(yawDiff) > 0x4000))) { + //! @bug This condition is why the boomerang will randomly fly off in a the down left direction sometimes. + // If the actor targetted is not Link and the difference between the 2 y angles is greater than 0x4000, + // the moveTo pointer is nulled and it flies off in a seemingly random direction. + this->moveTo = NULL; + } else { + Math_ScaledStepToS(&this->actor.world.rot.y, yawTarget, (s16)(ABS(yawDiff) * distXYZScale)); + Math_ScaledStepToS(&this->actor.world.rot.x, pitchTarget, (s16)(ABS(pitchDiff) * distXYZScale)); + } + } + + // Set xyz speed, move forward, and play the boomerang sound + func_8002D9A4(&this->actor, 12.0f); + Actor_MoveForward(&this->actor); + func_8002F974(&this->actor, NA_SE_IT_BOOMERANG_FLY - SFX_FLAG); + + // If the boomerang collides with EnItem00 or a Skulltula token, set grabbed pointer to pick it up + collided = this->collider.base.atFlags & AT_HIT; + collided = !!(collided); + if (collided) { + if (((this->collider.base.at->id == ACTOR_EN_ITEM00) || (this->collider.base.at->id == ACTOR_EN_SI))) { + this->grabbed = this->collider.base.at; + if (this->collider.base.at->id == ACTOR_EN_SI) { + this->collider.base.at->flags |= ACTOR_FLAG_13; + } + } + } + + // Decrement the return timer and check if it's 0. If it is, check if Link can catch it and handle accordingly. + // Otherwise handle grabbing and colliding. + if (DECR(this->returnTimer) == 0) { + distFromLink = Math_Vec3f_DistXYZ(&this->actor.world.pos, &player->actor.focus.pos); + this->moveTo = &player->actor; + + // If the boomerang is less than 40 units away from Link, he can catch it. + if (distFromLink < 40.0f) { + target = this->grabbed; + if (target != NULL) { + Math_Vec3f_Copy(&target->world.pos, &player->actor.world.pos); + + // If the grabbed actor is EnItem00 (HP/Key etc) set gravity and flags so it falls in front of Link. + // Otherwise if it's a Skulltula Token, just set flags so he collides with it to collect it. + if (target->id == ACTOR_EN_ITEM00) { + target->gravity = -0.9f; + target->bgCheckFlags &= ~0x03; + } else { + target->flags &= ~ACTOR_FLAG_13; + } + } + // Set player flags and kill the boomerang beacause Link caught it. + player->stateFlags1 &= ~0x02000000; + Actor_Kill(&this->actor); + } + } else { + collided = (this->collider.base.atFlags & AT_HIT); + collided = (!!(collided)); + if (collided) { + // Copy the position from the prevous frame to the boomerang to start the bounce back. + Math_Vec3f_Copy(&this->actor.world.pos, &this->actor.prevPos); + } else { + collided = BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.prevPos, &this->actor.world.pos, + &hitPoint, &this->actor.wallPoly, true, true, true, true, &hitDynaID); + + if (collided) { + // If the boomerang collides with something and it's is a Jabu Object actor with params equal to 0, then + // set collided to 0 so that the boomerang will go through the wall. + // Otherwise play a clank sound and keep collided set to bounce back. + if (func_8002F9EC(globalCtx, &this->actor, this->actor.wallPoly, hitDynaID, &hitPoint) != 0 || + (hitDynaID != BGCHECK_SCENE && + ((hitActor = DynaPoly_GetActor(&globalCtx->colCtx, hitDynaID)) != NULL) && + hitActor->actor.id == ACTOR_BG_BDAN_OBJECTS && hitActor->actor.params == 0)) { + collided = false; + } else { + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, &hitPoint); + } + } + } + + // If the boomerang needs to bounce back, set x and y angle accordingly. + // Set timer to 0 and set return actor to player so it goes back to Link. + if (collided) { + this->actor.world.rot.x = -this->actor.world.rot.x; + this->actor.world.rot.y += 0x8000; + this->moveTo = &player->actor; + this->returnTimer = 0; + } + } + + // If the actor the boomerang is holding has a null update function, set grabbed to null. + // Otherwise, copy the position from the boomerang to the actor to move it. + target = this->grabbed; + if (target != NULL) { + if (target->update == NULL) { + this->grabbed = NULL; + } else { + Math_Vec3f_Copy(&target->world.pos, &this->actor.world.pos); + } + } +} + +void EnBoom_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBoom* this = (EnBoom*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if (!(player->stateFlags1 & 0x20000000)) { + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, 0.0f); + this->activeTimer = this->activeTimer + 1; + } +} + +void EnBoom_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f sMultVec1 = { -960.0f, 0.0f, 0.0f }; + static Vec3f sMultVec2 = { 960.0f, 0.0f, 0.0f }; + EnBoom* this = (EnBoom*)thisx; + Vec3f vec1; + Vec3f vec2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_boom.c", 567); + + Matrix_RotateY(this->actor.world.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(0x1F40 * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(this->actor.world.rot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_MultVec3f(&sMultVec1, &vec1); + Matrix_MultVec3f(&sMultVec2, &vec2); + + if (func_80090480(globalCtx, &this->collider, &this->boomerangInfo, &vec1, &vec2) != 0) { + EffectBlure_AddVertex(Effect_GetByIndex(this->effectIndex), &vec1, &vec2); + } + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_RotateY((this->activeTimer * 12000) * (M_PI / 0x8000), MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_boom.c", 601), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBoomerangRefDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_boom.c", 604); +} diff --git a/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.h b/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.h new file mode 100644 index 000000000..cb93b27dd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Boom/z_en_boom.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_BOOM_H +#define Z_EN_BOOM_H + +#include "ultra64.h" +#include "global.h" + +struct EnBoom; + +typedef void (*EnBoomActionFunc)(struct EnBoom*, GlobalContext*); + +typedef struct EnBoom { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderQuad collider; + /* 0x01CC */ Actor* moveTo; // actor boomerang moves toward + /* 0x01D0 */ Actor* grabbed; // actor grabbed by the boomerang + /* 0x01D4 */ u8 returnTimer; // returns to Link when 0 + /* 0x01D5 */ u8 activeTimer; // increments once every update + /* 0x01D8 */ s32 effectIndex; + /* 0x01DC */ WeaponInfo boomerangInfo; + /* 0x01F8 */ EnBoomActionFunc actionFunc; +} EnBoom; // size = 0x01FC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c new file mode 100644 index 000000000..7d8886953 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -0,0 +1,648 @@ +#include "z_en_box.h" +#include "objects/object_box/object_box.h" + +#define FLAGS 0 + +// movement flags + +/* +set on init unless treasure flag is set +if clear, chest moves (Actor_MoveForward) (falls, likely) +ends up cleared from SWITCH_FLAG_FALL types when switch flag is set +*/ +#define ENBOX_MOVE_IMMOBILE (1 << 0) +/* +set in the logic for SWITCH_FLAG_FALL types +otherwise unused +*/ +#define ENBOX_MOVE_UNUSED (1 << 1) +/* +set with 50% chance on init for SWITCH_FLAG_FALL types +only used for SWITCH_FLAG_FALL types +ends up "blinking" (set/clear every frame) once switch flag is set, +if some collision-related condition (?) is met +only used for signum of z rotation +*/ +#define ENBOX_MOVE_FALL_ANGLE_SIDE (1 << 2) +/* +when set, gets cleared next EnBox_Update call and clip to the floor +*/ +#define ENBOX_MOVE_STICK_TO_GROUND (1 << 4) + +typedef enum { + ENBOX_STATE_0, // waiting for player near / player available / player ? (IDLE) + ENBOX_STATE_1, // used only temporarily, maybe "player is ready" ? + ENBOX_STATE_2 // waiting for something message context-related +} EnBoxStateUnk1FB; + +void EnBox_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBox_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBox_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBox_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBox_FallOnSwitchFlag(EnBox*, GlobalContext*); +void func_809C9700(EnBox*, GlobalContext*); +void EnBox_AppearOnSwitchFlag(EnBox*, GlobalContext*); +void EnBox_AppearOnRoomClear(EnBox*, GlobalContext*); +void EnBox_AppearInit(EnBox*, GlobalContext*); +void EnBox_AppearAnimation(EnBox*, GlobalContext*); +void EnBox_WaitOpen(EnBox*, GlobalContext*); +void EnBox_Open(EnBox*, GlobalContext*); + +const ActorInit En_Box_InitVars = { + ACTOR_EN_BOX, + ACTORCAT_CHEST, + FLAGS, + OBJECT_BOX, + sizeof(EnBox), + (ActorFunc)EnBox_Init, + (ActorFunc)EnBox_Destroy, + (ActorFunc)EnBox_Update, + (ActorFunc)EnBox_Draw, + NULL, +}; + +static AnimationHeader* sAnimations[4] = { &gTreasureChestAnim_00024C, &gTreasureChestAnim_000128, + &gTreasureChestAnim_00043C, &gTreasureChestAnim_00043C }; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 0, ICHAIN_STOP), +}; + +static UNK_TYPE sUnused; + +void EnBox_SetupAction(EnBox* this, EnBoxActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnBox_ClipToGround(EnBox* this, GlobalContext* globalCtx) { + f32 newY; + CollisionPoly* poly; + s32 bgId; + Vec3f pos; + + pos = this->dyna.actor.world.pos; + pos.y += 1.0f; + newY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &poly, &bgId, &this->dyna.actor, &pos); + if (newY != BGCHECK_Y_MIN) { + this->dyna.actor.world.pos.y = newY; + } +} + +void EnBox_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnBox* this = (EnBox*)thisx; + AnimationHeader* anim; + CollisionHeader* colHeader; + f32 animFrameStart; + f32 endFrame; + + animFrameStart = 0.0f; + anim = sAnimations[((void)0, gSaveContext.linkAge)]; + colHeader = NULL; + endFrame = Animation_GetLastFrame(anim); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gTreasureChestCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + func_8003ECA8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + + this->movementFlags = 0; + this->type = thisx->params >> 12 & 0xF; + this->iceSmokeTimer = 0; + this->unk_1FB = ENBOX_STATE_0; + this->dyna.actor.gravity = -5.5f; + this->switchFlag = this->dyna.actor.world.rot.z; + this->dyna.actor.minVelocityY = -50.0f; + + if (globalCtx) {} // helps the compiler store globalCtx2 into s1 + + if (Flags_GetTreasure(globalCtx, this->dyna.actor.params & 0x1F)) { + this->alpha = 255; + this->iceSmokeTimer = 100; + EnBox_SetupAction(this, EnBox_Open); + this->movementFlags |= ENBOX_MOVE_STICK_TO_GROUND; + animFrameStart = endFrame; + } else if ((this->type == ENBOX_TYPE_SWITCH_FLAG_FALL_BIG || this->type == ENBOX_TYPE_SWITCH_FLAG_FALL_SMALL) && + !Flags_GetSwitch(globalCtx, this->switchFlag)) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + if (Rand_ZeroOne() < 0.5f) { + this->movementFlags |= ENBOX_MOVE_FALL_ANGLE_SIDE; + } + this->unk_1A8 = -12; + EnBox_SetupAction(this, EnBox_FallOnSwitchFlag); + this->alpha = 0; + this->movementFlags |= ENBOX_MOVE_IMMOBILE; + this->dyna.actor.flags |= ACTOR_FLAG_4; + } else if ((this->type == ENBOX_TYPE_ROOM_CLEAR_BIG || this->type == ENBOX_TYPE_ROOM_CLEAR_SMALL) && + !Flags_GetClear(globalCtx, this->dyna.actor.room)) { + EnBox_SetupAction(this, EnBox_AppearOnRoomClear); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->movementFlags |= ENBOX_MOVE_IMMOBILE; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - 50.0f; + this->alpha = 0; + this->dyna.actor.flags |= ACTOR_FLAG_4; + } else if (this->type == ENBOX_TYPE_9 || this->type == ENBOX_TYPE_10) { + EnBox_SetupAction(this, func_809C9700); + this->dyna.actor.flags |= ACTOR_FLAG_25; + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->movementFlags |= ENBOX_MOVE_IMMOBILE; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - 50.0f; + this->alpha = 0; + this->dyna.actor.flags |= ACTOR_FLAG_4; + } else if (this->type == ENBOX_TYPE_SWITCH_FLAG_BIG && !Flags_GetSwitch(globalCtx, this->switchFlag)) { + EnBox_SetupAction(this, EnBox_AppearOnSwitchFlag); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->movementFlags |= ENBOX_MOVE_IMMOBILE; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - 50.0f; + this->alpha = 0; + this->dyna.actor.flags |= ACTOR_FLAG_4; + } else { + if (this->type == ENBOX_TYPE_4 || this->type == ENBOX_TYPE_6) { + this->dyna.actor.flags |= ACTOR_FLAG_7; + } + EnBox_SetupAction(this, EnBox_WaitOpen); + this->movementFlags |= ENBOX_MOVE_IMMOBILE; + this->movementFlags |= ENBOX_MOVE_STICK_TO_GROUND; + } + + this->dyna.actor.world.rot.y += 0x8000; + this->dyna.actor.home.rot.z = this->dyna.actor.world.rot.z = this->dyna.actor.shape.rot.z = 0; + + SkelAnime_Init(globalCtx, &this->skelanime, &gTreasureChestSkel, anim, this->jointTable, this->morphTable, 5); + Animation_Change(&this->skelanime, anim, 1.5f, animFrameStart, endFrame, ANIMMODE_ONCE, 0.0f); + + switch (this->type) { + case ENBOX_TYPE_SMALL: + case ENBOX_TYPE_6: + case ENBOX_TYPE_ROOM_CLEAR_SMALL: + case ENBOX_TYPE_SWITCH_FLAG_FALL_SMALL: + Actor_SetScale(&this->dyna.actor, 0.005f); + Actor_SetFocus(&this->dyna.actor, 20.0f); + break; + default: + Actor_SetScale(&this->dyna.actor, 0.01f); + Actor_SetFocus(&this->dyna.actor, 40.0f); + } +} + +void EnBox_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBox* this = (EnBox*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void EnBox_RandomDustKinematic(EnBox* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel) { + f32 randomRadius = Rand_ZeroOne() * 25.0f; + s16 randomAngle = Rand_ZeroOne() * 0x10000; + + *pos = this->dyna.actor.world.pos; + pos->x += Math_SinS(randomAngle) * randomRadius; + pos->z += Math_CosS(randomAngle) * randomRadius; + + velocity->y = 1.0f; + velocity->x = Math_SinS(randomAngle); + velocity->z = Math_CosS(randomAngle); + + accel->x = 0.0f; + accel->y = 0.0f; + accel->z = 0.0f; +} + +/** + * Spawns dust randomly around the chest when the chest hits the ground after falling (FALL types) + */ +void EnBox_SpawnDust(EnBox* this, GlobalContext* globalCtx) { + s32 i; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + for (i = 0; i < 20; i++) { + EnBox_RandomDustKinematic(this, &pos, &velocity, &accel); + func_8002873C(globalCtx, &pos, &velocity, &accel, 100, 30, 15); + } +} + +/** + * Used while the chest is falling (FALL types) + */ +void EnBox_Fall(EnBox* this, GlobalContext* globalCtx) { + f32 yDiff; + + this->alpha = 255; + this->movementFlags &= ~ENBOX_MOVE_IMMOBILE; + if (this->dyna.actor.bgCheckFlags & 1) { + this->movementFlags |= ENBOX_MOVE_UNUSED; + if (this->movementFlags & ENBOX_MOVE_FALL_ANGLE_SIDE) { + this->movementFlags &= ~ENBOX_MOVE_FALL_ANGLE_SIDE; + } else { + this->movementFlags |= ENBOX_MOVE_FALL_ANGLE_SIDE; + } + if (this->type == ENBOX_TYPE_SWITCH_FLAG_FALL_BIG) { + this->dyna.actor.velocity.y = -this->dyna.actor.velocity.y * 0.55f; + } else { + this->dyna.actor.velocity.y = -this->dyna.actor.velocity.y * 0.65f; + } + if (this->dyna.actor.velocity.y < 5.5f) { + this->dyna.actor.shape.rot.z = 0; + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + EnBox_SetupAction(this, EnBox_WaitOpen); + OnePointCutscene_EndCutscene(globalCtx, this->unk_1AC); + } + Audio_PlaySoundGeneral(NA_SE_EV_COFFIN_CAP_BOUND, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + EnBox_SpawnDust(this, globalCtx); + } + yDiff = this->dyna.actor.world.pos.y - this->dyna.actor.floorHeight; + if (this->movementFlags & ENBOX_MOVE_FALL_ANGLE_SIDE) { + this->dyna.actor.shape.rot.z = yDiff * 50.0f; + } else { + this->dyna.actor.shape.rot.z = -yDiff * 50.0f; + } +} + +void EnBox_FallOnSwitchFlag(EnBox* this, GlobalContext* globalCtx) { + s32 treasureFlag = this->dyna.actor.params & 0x1F; + + if (treasureFlag >= ENBOX_TREASURE_FLAG_UNK_MIN && treasureFlag < ENBOX_TREASURE_FLAG_UNK_MAX) { + func_8002F5F0(&this->dyna.actor, globalCtx); + } + + if (this->unk_1A8 >= 0) { + EnBox_SetupAction(this, EnBox_Fall); + this->unk_1AC = OnePointCutscene_Init(globalCtx, 4500, 9999, &this->dyna.actor, MAIN_CAM); + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else if (this->unk_1A8 >= -11) { + this->unk_1A8++; + } else if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->unk_1A8++; + } +} + +// used for types 9, 10 +void func_809C9700(EnBox* this, GlobalContext* globalCtx) { + s32 treasureFlag = this->dyna.actor.params & 0x1F; + Player* player = GET_PLAYER(globalCtx); + + if (treasureFlag >= ENBOX_TREASURE_FLAG_UNK_MIN && treasureFlag < ENBOX_TREASURE_FLAG_UNK_MAX) { + func_8002F5F0(&this->dyna.actor, globalCtx); + } + + if (Math3D_Vec3fDistSq(&this->dyna.actor.world.pos, &player->actor.world.pos) > 22500.0f) { + this->unk_1FB = ENBOX_STATE_0; + } else { + if (this->unk_1FB == ENBOX_STATE_0) { + if (!(player->stateFlags2 & 0x1000000)) { + player->stateFlags2 |= 0x800000; + return; + } + this->unk_1FB = ENBOX_STATE_1; + } + + if (this->unk_1FB == ENBOX_STATE_1) { + func_8010BD58(globalCtx, OCARINA_ACTION_FREE_PLAY); + this->unk_1FB = ENBOX_STATE_2; + } else if (this->unk_1FB == ENBOX_STATE_2 && globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + if ((globalCtx->msgCtx.lastPlayedSong == OCARINA_SONG_LULLABY && this->type == ENBOX_TYPE_9) || + (globalCtx->msgCtx.lastPlayedSong == OCARINA_SONG_SUNS && this->type == ENBOX_TYPE_10)) { + this->dyna.actor.flags &= ~ACTOR_FLAG_25; + EnBox_SetupAction(this, EnBox_AppearInit); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->unk_1A8 = 0; + this->unk_1FB = ENBOX_STATE_0; + } else { + this->unk_1FB = ENBOX_STATE_0; + } + } + } +} + +void EnBox_AppearOnSwitchFlag(EnBox* this, GlobalContext* globalCtx) { + s32 treasureFlag = this->dyna.actor.params & 0x1F; + + if (treasureFlag >= ENBOX_TREASURE_FLAG_UNK_MIN && treasureFlag < ENBOX_TREASURE_FLAG_UNK_MAX) { + func_8002F5F0(&this->dyna.actor, globalCtx); + } + + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + EnBox_SetupAction(this, EnBox_AppearInit); + this->unk_1A8 = -30; + } +} + +void EnBox_AppearOnRoomClear(EnBox* this, GlobalContext* globalCtx) { + s32 treasureFlag = this->dyna.actor.params & 0x1F; + + if (treasureFlag >= ENBOX_TREASURE_FLAG_UNK_MIN && treasureFlag < ENBOX_TREASURE_FLAG_UNK_MAX) { + func_8002F5F0(&this->dyna.actor, globalCtx); + } + + if (Flags_GetTempClear(globalCtx, this->dyna.actor.room) && !Player_InCsMode(globalCtx)) { + Flags_SetClear(globalCtx, this->dyna.actor.room); + EnBox_SetupAction(this, EnBox_AppearInit); + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + if (OnePointCutscene_CheckForCategory(globalCtx, this->dyna.actor.category)) { + this->unk_1A8 = 0; + } else { + this->unk_1A8 = -30; + } + } +} + +/** + * The chest is ready to appear, possibly waiting for camera/cutscene-related stuff to happen + */ +void EnBox_AppearInit(EnBox* this, GlobalContext* globalCtx) { + if (func_8005B198() == this->dyna.actor.category || this->unk_1A8 != 0) { + EnBox_SetupAction(this, EnBox_AppearAnimation); + this->unk_1A8 = 0; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_KANKYO, this->dyna.actor.home.pos.x, + this->dyna.actor.home.pos.y, this->dyna.actor.home.pos.z, 0, 0, 0, 0x0011); + Audio_PlaySoundGeneral(NA_SE_EV_TRE_BOX_APPEAR, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void EnBox_AppearAnimation(EnBox* this, GlobalContext* globalCtx) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + + if (this->unk_1A8 < 0) { + this->unk_1A8++; + } else if (this->unk_1A8 < 40) { + this->unk_1A8++; + this->dyna.actor.world.pos.y += 1.25f; + } else if (this->unk_1A8 < 60) { + this->alpha += 12; + this->unk_1A8++; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + } else { + EnBox_SetupAction(this, EnBox_WaitOpen); + } +} + +/** + * Chest is ready to be open + */ +void EnBox_WaitOpen(EnBox* this, GlobalContext* globalCtx) { + f32 frameCount; + AnimationHeader* anim; + s32 linkAge; + s32 pad; + Vec3f sp4C; + Player* player; + + this->alpha = 255; + this->movementFlags |= ENBOX_MOVE_IMMOBILE; + if (this->unk_1F4 != 0) { // unk_1F4 is modified by player code + linkAge = gSaveContext.linkAge; + anim = sAnimations[(this->unk_1F4 < 0 ? 2 : 0) + linkAge]; + frameCount = Animation_GetLastFrame(anim); + Animation_Change(&this->skelanime, anim, 1.5f, 0, frameCount, ANIMMODE_ONCE, 0.0f); + EnBox_SetupAction(this, EnBox_Open); + if (this->unk_1F4 > 0) { + switch (this->type) { + case ENBOX_TYPE_SMALL: + case ENBOX_TYPE_6: + case ENBOX_TYPE_ROOM_CLEAR_SMALL: + case ENBOX_TYPE_SWITCH_FLAG_FALL_SMALL: + break; + default: + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_DEMO_TRE_LGT, + this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, + this->dyna.actor.world.pos.z, this->dyna.actor.shape.rot.x, + this->dyna.actor.shape.rot.y, this->dyna.actor.shape.rot.z, 0xFFFF); + Audio_PlayFanfare(NA_BGM_OPEN_TRE_BOX | 0x900); + } + } + osSyncPrintf("Actor_Environment_Tbox_On() %d\n", this->dyna.actor.params & 0x1F); + Flags_SetTreasure(globalCtx, this->dyna.actor.params & 0x1F); + } else { + player = GET_PLAYER(globalCtx); + func_8002DBD0(&this->dyna.actor, &sp4C, &player->actor.world.pos); + if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f && + Player_IsFacingActor(&this->dyna.actor, 0x3000, globalCtx)) { + func_8002F554(&this->dyna.actor, globalCtx, 0 - (this->dyna.actor.params >> 5 & 0x7F)); + } + if (Flags_GetTreasure(globalCtx, this->dyna.actor.params & 0x1F)) { + EnBox_SetupAction(this, EnBox_Open); + } + } +} + +/** + * Plays an animation to its end, playing sounds at key points + */ +void EnBox_Open(EnBox* this, GlobalContext* globalCtx) { + u16 sfxId; + + this->dyna.actor.flags &= ~ACTOR_FLAG_7; + + if (SkelAnime_Update(&this->skelanime)) { + if (this->unk_1F4 > 0) { + if (this->unk_1F4 < 120) { + this->unk_1F4++; + } else { + Math_StepToF(&this->unk_1B0, 0.0f, 0.05f); + } + } else { + if (this->unk_1F4 > -120) { + this->unk_1F4--; + } else { + Math_StepToF(&this->unk_1B0, 0.0f, 0.05f); + } + } + } else { + sfxId = 0; + + if (Animation_OnFrame(&this->skelanime, 30.0f)) { + sfxId = NA_SE_EV_TBOX_UNLOCK; + } else if (Animation_OnFrame(&this->skelanime, 90.0f)) { + sfxId = NA_SE_EV_TBOX_OPEN; + } + + if (sfxId != 0) { + Audio_PlaySoundGeneral(sfxId, &this->dyna.actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + if (this->skelanime.jointTable[3].z > 0) { + this->unk_1B0 = (0x7D00 - this->skelanime.jointTable[3].z) * 0.00006f; + if (this->unk_1B0 < 0.0f) { + this->unk_1B0 = 0.0f; + } else if (this->unk_1B0 > 1.0f) { + this->unk_1B0 = 1.0f; + } + } + } +} + +void EnBox_SpawnIceSmoke(EnBox* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f vel = { 0.0f, 1.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + f32 f0; + + this->iceSmokeTimer++; + func_8002F974(&this->dyna.actor, NA_SE_EN_MIMICK_BREATH - SFX_FLAG); + if (Rand_ZeroOne() < 0.3f) { + f0 = 2.0f * Rand_ZeroOne() - 1.0f; + pos = this->dyna.actor.world.pos; + if (this->type == ENBOX_TYPE_SMALL || this->type == ENBOX_TYPE_6 || this->type == ENBOX_TYPE_ROOM_CLEAR_SMALL || + this->type == ENBOX_TYPE_SWITCH_FLAG_FALL_SMALL) { + pos.x += f0 * 10.0f * Math_SinS(this->dyna.actor.world.rot.y + 0x4000); + pos.z += f0 * 10.0f * Math_CosS(this->dyna.actor.world.rot.y + 0x4000); + f0 = 2.0f * Rand_ZeroOne() - 1.0f; + vel.x = f0 * 0.8f * Math_SinS(this->dyna.actor.world.rot.y); + vel.y = 1.8f; + vel.z = f0 * 0.8f * Math_CosS(this->dyna.actor.world.rot.y); + } else { + pos.x += f0 * 20.0f * Math_SinS(this->dyna.actor.world.rot.y + 0x4000); + pos.z += f0 * 20.0f * Math_CosS(this->dyna.actor.world.rot.y + 0x4000); + f0 = 2.0f * Rand_ZeroOne() - 1.0f; + vel.x = f0 * 1.6f * Math_SinS(this->dyna.actor.world.rot.y); + vel.y = 1.8f; + vel.z = f0 * 1.6f * Math_CosS(this->dyna.actor.world.rot.y); + } + EffectSsIceSmoke_Spawn(globalCtx, &pos, &vel, &accel, 150); + } +} + +void EnBox_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBox* this = (EnBox*)thisx; + + if (this->movementFlags & ENBOX_MOVE_STICK_TO_GROUND) { + this->movementFlags &= ~ENBOX_MOVE_STICK_TO_GROUND; + EnBox_ClipToGround(this, globalCtx); + } + + this->actionFunc(this, globalCtx); + + if (!(this->movementFlags & ENBOX_MOVE_IMMOBILE)) { + Actor_MoveForward(&this->dyna.actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 0.0f, 0.0f, 0.0f, 0x1C); + } + + switch (this->type) { + case ENBOX_TYPE_SMALL: + case ENBOX_TYPE_6: + case ENBOX_TYPE_ROOM_CLEAR_SMALL: + case ENBOX_TYPE_SWITCH_FLAG_FALL_SMALL: + Actor_SetFocus(&this->dyna.actor, 20.0f); + break; + default: + Actor_SetFocus(&this->dyna.actor, 40.0f); + } + + if ((this->dyna.actor.params >> 5 & 0x7F) == 0x7C && this->actionFunc == EnBox_Open && + this->skelanime.curFrame > 45 && this->iceSmokeTimer < 100) { + EnBox_SpawnIceSmoke(this, globalCtx); + } +} + +void EnBox_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnBox* this = (EnBox*)thisx; + s32 pad; + + if (limbIndex == 1) { + gSPMatrix((*gfx)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_box.c", 1492), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->type != ENBOX_TYPE_DECORATED_BIG) { + gSPDisplayList((*gfx)++, gTreasureChestChestFrontDL); + } else { + gSPDisplayList((*gfx)++, gTreasureChestBossKeyChestFrontDL); + } + } else if (limbIndex == 3) { + gSPMatrix((*gfx)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_box.c", 1502), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->type != ENBOX_TYPE_DECORATED_BIG) { + gSPDisplayList((*gfx)++, gTreasureChestChestSideAndLidDL); + } else { + gSPDisplayList((*gfx)++, gTreasureChestBossKeyChestSideAndTopDL); + } + } +} + +Gfx* EnBox_EmptyDList(GraphicsContext* gfxCtx) { + Gfx* dListHead; + Gfx* dList; + + dList = Graph_Alloc(gfxCtx, sizeof(Gfx)); + ASSERT(dList != NULL, "gfxp != NULL", "../z_en_box.c", 1528); + + dListHead = dList; + gSPEndDisplayList(dListHead++); + + return dList; +} + +// set render mode with a focus on transparency +Gfx* func_809CA4A0(GraphicsContext* gfxCtx) { + Gfx* dList; + Gfx* dListHead; + + dListHead = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + ASSERT(dListHead != NULL, "gfxp != NULL", "../z_en_box.c", 1546); + + dList = dListHead; + gDPSetRenderMode(dListHead++, + AA_EN | Z_CMP | Z_UPD | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | ZMODE_XLU | FORCE_BL | + GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA), + AA_EN | Z_CMP | Z_UPD | IM_RD | CLR_ON_CVG | CVG_DST_WRAP | ZMODE_XLU | FORCE_BL | + GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)); + gSPEndDisplayList(dListHead++); + + return dList; +} + +Gfx* func_809CA518(GraphicsContext* gfxCtx) { + Gfx* dList; + Gfx* dListHead; + + dListHead = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + ASSERT(dListHead != NULL, "gfxp != NULL", "../z_en_box.c", 1564); + + dList = dListHead; + gDPSetRenderMode(dListHead++, + AA_EN | Z_CMP | Z_UPD | IM_RD | CVG_DST_CLAMP | ZMODE_OPA | ALPHA_CVG_SEL | + GBL_c1(G_BL_CLR_FOG, G_BL_A_SHADE, G_BL_CLR_IN, G_BL_1MA), + G_RM_AA_ZB_OPA_SURF2); + gSPEndDisplayList(dListHead++); + + return dList; +} + +void EnBox_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBox* this = (EnBox*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_box.c", 1581); + + /* + this->dyna.actor.flags & ACTOR_FLAG_7 is set by Init (if type is 4 or 6) + and cleared by Open + */ + if ((this->alpha == 255 && !(this->type == ENBOX_TYPE_4 || this->type == ENBOX_TYPE_6)) || + (!CHECK_FLAG_ALL(this->dyna.actor.flags, ACTOR_FLAG_7) && + (this->type == ENBOX_TYPE_4 || this->type == ENBOX_TYPE_6))) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x08, EnBox_EmptyDList(globalCtx->state.gfxCtx)); + func_80093D18(globalCtx->state.gfxCtx); + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, NULL, + EnBox_PostLimbDraw, this, POLY_OPA_DISP); + } else if (this->alpha != 0) { + gDPPipeSync(POLY_XLU_DISP++); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + if (this->type == ENBOX_TYPE_4 || this->type == ENBOX_TYPE_6) { + gSPSegment(POLY_XLU_DISP++, 0x08, func_809CA518(globalCtx->state.gfxCtx)); + } else { + gSPSegment(POLY_XLU_DISP++, 0x08, func_809CA4A0(globalCtx->state.gfxCtx)); + } + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, NULL, + EnBox_PostLimbDraw, this, POLY_XLU_DISP); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_box.c", 1639); +} diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.h b/soh/src/overlays/actors/ovl_En_Box/z_en_box.h new file mode 100644 index 000000000..f6e698ed5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.h @@ -0,0 +1,50 @@ +#ifndef Z_EN_BOX_H +#define Z_EN_BOX_H + +#include "ultra64.h" +#include "global.h" + +#define ENBOX_TREASURE_FLAG_UNK_MIN 20 +#define ENBOX_TREASURE_FLAG_UNK_MAX 32 + +struct EnBox; + +typedef void (*EnBoxActionFunc)(struct EnBox*, GlobalContext*); + +typedef enum { + /* + only values 1-11 are used explicitly, other values (like 0) default to another separate behavior + */ + /* 0 */ ENBOX_TYPE_BIG_DEFAULT, + /* 1 */ ENBOX_TYPE_ROOM_CLEAR_BIG, // appear on room clear, store temp clear as permanent clear + /* 2 */ ENBOX_TYPE_DECORATED_BIG, // boss key chest, different look, same as ENBOX_TYPE_BIG_DEFAULT otherwise + /* 3 */ ENBOX_TYPE_SWITCH_FLAG_FALL_BIG, // falling, appear on switch flag set + /* 4 */ ENBOX_TYPE_4, // big, drawn differently + /* 5 */ ENBOX_TYPE_SMALL, // same as ENBOX_TYPE_BIG_DEFAULT but small + /* 6 */ ENBOX_TYPE_6, // small, drawn differently + /* 7 */ ENBOX_TYPE_ROOM_CLEAR_SMALL, // use room clear, store temp clear as perm clear + /* 8 */ ENBOX_TYPE_SWITCH_FLAG_FALL_SMALL, // falling, appear on switch flag set + /* 9 */ ENBOX_TYPE_9, // big, has something more to do with player and message context? + /* 10 */ ENBOX_TYPE_10, // like 9 + /* 11 */ ENBOX_TYPE_SWITCH_FLAG_BIG // big, appear on switch flag set +} EnBoxType; + +typedef struct EnBox { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ SkelAnime skelanime; + /* 0x01A8 */ s32 unk_1A8; // related to animation delays for types 3 and 8 + /* 0x01AC */ s32 unk_1AC; + /* 0x01B0 */ f32 unk_1B0; // 0-1, rotation-related, apparently unused (in z_en_box.c at least) + /* 0x01B4 */ EnBoxActionFunc actionFunc; + /* 0x01B8 */ Vec3s jointTable[5]; + /* 0x01D6 */ Vec3s morphTable[5]; + /* 0x01F4 */ s16 unk_1F4; // probably a frame count? set by player code + /* 0x01F6 */ u8 movementFlags; + /* 0x01F7 */ u8 alpha; + /* 0x01F8 */ u8 switchFlag; + /* 0x01F9 */ u8 type; + /* 0x01FA */ u8 iceSmokeTimer; + /* 0x01FB */ u8 unk_1FB; +} EnBox; // size = 0x01FC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c b/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c new file mode 100644 index 000000000..8b105fd30 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.c @@ -0,0 +1,329 @@ +/* + * File: z_en_brob.c + * Overlay: ovl_En_Brob + * Description: Flobbery Muscle Block (Jabu-Jabu's Belly) + */ + +#include "z_en_brob.h" +#include "objects/object_brob/object_brob.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnBrob_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBrob_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBrob_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBrob_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809CADDC(EnBrob* this, GlobalContext* globalCtx); +void func_809CB054(EnBrob* this, GlobalContext* globalCtx); +void func_809CB114(EnBrob* this, GlobalContext* globalCtx); +void func_809CB218(EnBrob* this, GlobalContext* globalCtx); +void func_809CB2B8(EnBrob* this, GlobalContext* globalCtx); +void func_809CB354(EnBrob* this, GlobalContext* globalCtx); +void func_809CB458(EnBrob* this, GlobalContext* globalCtx); + +const ActorInit En_Brob_InitVars = { + ACTOR_EN_BROB, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BROB, + sizeof(EnBrob), + (ActorFunc)EnBrob_Init, + (ActorFunc)EnBrob_Destroy, + (ActorFunc)EnBrob_Update, + (ActorFunc)EnBrob_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0xFFCFFFFF, 0x03, 0x08 }, + { 0xFFCFFFFF, 0x01, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 8000, 11000, -5000, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 60, 120, MASS_IMMOVABLE }; + +void EnBrob_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBrob* this = (EnBrob*)thisx; + CollisionHeader* colHeader = NULL; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_brob_Skel_0015D8, &object_brob_Anim_001750, + this->jointTable, this->morphTable, 10); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&object_brob_Col_001A70, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + Collider_InitCylinder(globalCtx, &this->colliders[0]); + Collider_SetCylinder(globalCtx, &this->colliders[0], &this->dyna.actor, &sCylinderInit); + Collider_InitCylinder(globalCtx, &this->colliders[1]); + Collider_SetCylinder(globalCtx, &this->colliders[1], &this->dyna.actor, &sCylinderInit); + CollisionCheck_SetInfo(&thisx->colChkInfo, NULL, &sColChkInfoInit); + if (((thisx->params >> 8) & 0xFF) == 0) { + Actor_SetScale(&this->dyna.actor, 0.01f); + thisx->params &= 0xFF; + if (thisx->params != 0xFF) { + thisx->scale.y *= (thisx->params & 0xFF) * (1.0f / 30.0f); + } + } else { + Actor_SetScale(&this->dyna.actor, 0.005f); + thisx->params &= 0xFF; + if (thisx->params != 0xFF) { + thisx->scale.y *= (thisx->params & 0xFF) * (2.0f / 30.0f); + } + } + this->colliders[0].dim.radius *= thisx->scale.x; + this->colliders[0].dim.height = thisx->scale.y * 12000.0f; + this->colliders[0].dim.yShift = 0; + this->colliders[1].dim.radius *= thisx->scale.x; + this->colliders[1].dim.height *= thisx->scale.y; + this->colliders[1].dim.yShift *= thisx->scale.y; + this->actionFunc = NULL; + thisx->flags &= ~ACTOR_FLAG_0; + func_809CADDC(this, globalCtx); +} + +void EnBrob_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBrob* this = (EnBrob*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->colliders[0]); + Collider_DestroyCylinder(globalCtx, &this->colliders[1]); +} + +void func_809CADDC(EnBrob* this, GlobalContext* globalCtx) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->timer = this->actionFunc == func_809CB2B8 ? 200 : 0; + this->unk_1AE = 0; + this->actionFunc = func_809CB054; +} + +void func_809CAE44(EnBrob* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &object_brob_Anim_001750); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->unk_1AE = 1000; + this->actionFunc = func_809CB114; +} + +void func_809CAEA0(EnBrob* this) { + Animation_MorphToLoop(&this->skelAnime, &object_brob_Anim_001958, -5.0f); + this->unk_1AE = 8000; + this->timer = 1200; + this->actionFunc = func_809CB218; +} + +void func_809CAEF4(EnBrob* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_brob_Anim_000290, -5.0f); + this->unk_1AE -= 125.0f; + Actor_SetColorFilter(&this->dyna.actor, 0, 0xFF, 0, 0x50); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actionFunc = func_809CB2B8; +} + +void func_809CAF88(EnBrob* this) { + Animation_Change(&this->skelAnime, &object_brob_Anim_001750, -1.0f, + Animation_GetLastFrame(&object_brob_Anim_001750), 0.0f, ANIMMODE_ONCE, -5.0f); + this->unk_1AE = 8250; + this->actionFunc = func_809CB354; +} + +void func_809CB008(EnBrob* this) { + Animation_MorphToLoop(&this->skelAnime, &object_brob_Anim_001678, -5.0f); + this->timer = 10; + this->actionFunc = func_809CB458; +} + +void func_809CB054(EnBrob* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + if (func_8004356C(&this->dyna) != 0) { + func_8002F71C(globalCtx, &this->dyna.actor, 5.0f, this->dyna.actor.yawTowardsPlayer, 1.0f); + func_809CAE44(this, globalCtx); + } else if (this->dyna.actor.xzDistToPlayer < 300.0f) { + func_809CAE44(this, globalCtx); + } + } else if (this->timer >= 81) { + this->dyna.actor.colorFilterTimer = 80; + } +} + +void func_809CB114(EnBrob* this, GlobalContext* globalCtx) { + f32 curFrame; + + if (SkelAnime_Update(&this->skelAnime)) { + func_809CAEA0(this); + } else { + curFrame = this->skelAnime.curFrame; + if (curFrame < 8.0f) { + this->unk_1AE += 1000.0f; + } else if (curFrame < 12.0f) { + this->unk_1AE += 250.0f; + } else { + this->unk_1AE -= 250.0f; + } + } +} + +void func_809CB218(EnBrob* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 6.0f) || Animation_OnFrame(&this->skelAnime, 15.0f)) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EN_BROB_WAVE); + } + if (this->timer != 0) { + this->timer--; + } + if ((this->timer == 0) && (this->dyna.actor.xzDistToPlayer > 500.0f)) { + func_809CAF88(this); + } +} + +void func_809CB2B8(EnBrob* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + func_809CADDC(this, globalCtx); + } else if (this->skelAnime.curFrame < 8.0f) { + this->unk_1AE -= 1250.0f; + } + this->dyna.actor.colorFilterTimer = 0x50; +} + +void func_809CB354(EnBrob* this, GlobalContext* globalCtx) { + f32 curFrame; + + if (SkelAnime_Update(&this->skelAnime)) { + func_809CADDC(this, globalCtx); + } else { + curFrame = this->skelAnime.curFrame; + if (curFrame < 8.0f) { + this->unk_1AE -= 1000.0f; + } else if (curFrame < 12.0f) { + this->unk_1AE -= 250.0f; + } else { + this->unk_1AE += 250.0f; + } + } +} + +void func_809CB458(EnBrob* this, GlobalContext* globalCtx) { + Vec3f pos; + f32 dist1; + f32 dist2; + s32 i; + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0) && (this->timer != 0)) { + this->timer--; + } + + dist1 = globalCtx->gameplayFrames % 2 ? 0.0f : this->dyna.actor.scale.x * 5500.0f; + dist2 = this->dyna.actor.scale.x * 5500.0f; + + for (i = 0; i < 4; i++) { + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 200, 255, 255, 255 }; + + if (i % 2) { + pos.x = this->dyna.actor.world.pos.x + dist1; + pos.z = this->dyna.actor.world.pos.z + dist2; + } else { + pos.x = this->dyna.actor.world.pos.x + dist2; + pos.z = this->dyna.actor.world.pos.z + dist1; + dist1 = -dist1; + dist2 = -dist2; + } + pos.y = (((Rand_ZeroOne() * 15000.0f) + 1000.0f) * this->dyna.actor.scale.y) + this->dyna.actor.world.pos.y; + EffectSsLightning_Spawn(globalCtx, &pos, &primColor, &envColor, this->dyna.actor.scale.y * 8000.0f, + Rand_ZeroOne() * 65536.0f, 4, 1); + } + + if (this->timer == 0) { + func_809CAEA0(this); + } +} + +void EnBrob_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnBrob* this = (EnBrob*)thisx; + s32 i; + s32 acHits[2]; + + acHits[0] = (this->colliders[0].base.acFlags & AC_HIT) != 0; + acHits[1] = (this->colliders[1].base.acFlags & AC_HIT) != 0; + if ((acHits[0] && (this->colliders[0].info.acHitInfo->toucher.dmgFlags & 0x10)) || + (acHits[1] && (this->colliders[1].info.acHitInfo->toucher.dmgFlags & 0x10))) { + + for (i = 0; i < 2; i++) { + this->colliders[i].base.atFlags &= ~(AT_HIT | AT_BOUNCED); + this->colliders[i].base.acFlags &= ~AC_HIT; + } + + func_809CAEF4(this); + } else if ((this->colliders[0].base.atFlags & AT_HIT) || (this->colliders[1].base.atFlags & AT_HIT) || + (acHits[0] && (this->colliders[0].info.acHitInfo->toucher.dmgFlags & 0x100)) || + (acHits[1] && (this->colliders[1].info.acHitInfo->toucher.dmgFlags & 0x100))) { + + if (this->actionFunc == func_809CB114 && !(this->colliders[0].base.atFlags & AT_BOUNCED) && + !(this->colliders[1].base.atFlags & AT_BOUNCED)) { + func_8002F71C(globalCtx, &this->dyna.actor, 5.0f, this->dyna.actor.yawTowardsPlayer, 1.0f); + } else if (this->actionFunc != func_809CB114) { + func_809CB008(this); + } + + for (i = 0; i < 2; i++) { + this->colliders[i].base.atFlags &= ~(AT_HIT | AT_BOUNCED); + this->colliders[i].base.acFlags &= ~AC_HIT; + } + } + this->actionFunc(this, globalCtx); + if (this->actionFunc != func_809CB054 && this->actionFunc != func_809CB354) { + if (this->actionFunc != func_809CB2B8) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliders[0].base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliders[1].base); + if (this->actionFunc != func_809CB114) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliders[0].base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliders[1].base); + } + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliders[0].base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliders[1].base); + } +} + +void EnBrob_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnBrob* this = (EnBrob*)thisx; + MtxF mtx; + + Matrix_Get(&mtx); + if (limbIndex == 3) { + this->colliders[0].dim.pos.x = mtx.xw; + this->colliders[0].dim.pos.y = mtx.yw; + this->colliders[0].dim.pos.z = mtx.zw; + } else if (limbIndex == 8) { + this->colliders[1].dim.pos.x = mtx.xw; + this->colliders[1].dim.pos.y = (mtx.yw + 7.0f); + this->colliders[1].dim.pos.z = mtx.zw; + } +} + +void EnBrob_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBrob* this = (EnBrob*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Translate(0.0f, this->unk_1AE, 0.0f, MTXMODE_APPLY); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, EnBrob_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.h b/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.h new file mode 100644 index 000000000..4f71d489e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Brob/z_en_brob.h @@ -0,0 +1,22 @@ +#ifndef Z_EN_BROB_H +#define Z_EN_BROB_H + +#include "ultra64.h" +#include "global.h" + +struct EnBrob; + +typedef void (*EnBrobActionFunc)(struct EnBrob* this, GlobalContext* globalCtx); + +typedef struct EnBrob { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ SkelAnime skelAnime; + /* 0x01A8 */ EnBrobActionFunc actionFunc; + /* 0x01AC */ s16 timer; + /* 0x01AE */ s16 unk_1AE; + /* 0x01B0 */ Vec3s jointTable[10]; + /* 0x01EC */ Vec3s morphTable[10]; + /* 0x0228 */ ColliderCylinder colliders[2]; +} EnBrob; // size = 0x02C0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c new file mode 100644 index 000000000..9b36b859e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c @@ -0,0 +1,436 @@ +#include "z_en_bubble.h" +#include "objects/object_bubble/object_bubble.h" + +#define FLAGS ACTOR_FLAG_0 + +void EnBubble_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBubble_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBubble_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBubble_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnBubble_Wait(EnBubble* this, GlobalContext* globalCtx); +void EnBubble_Pop(EnBubble* this, GlobalContext* globalCtx); +void EnBubble_Regrow(EnBubble* this, GlobalContext* globalCtx); + +const ActorInit En_Bubble_InitVars = { + ACTOR_EN_BUBBLE, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BUBBLE, + sizeof(EnBubble), + (ActorFunc)EnBubble_Init, + (ActorFunc)EnBubble_Destroy, + (ActorFunc)EnBubble_Update, + (ActorFunc)EnBubble_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x04 }, + { 0xFFCFD753, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 16 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00002824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 16 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 2, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit2 = { + 1, 2, 25, 25, MASS_IMMOVABLE, +}; + +static Vec3f sEffectAccel = { 0.0f, -0.5f, 0.0f }; + +static Color_RGBA8 sEffectPrimColor = { 255, 255, 255, 255 }; + +static Color_RGBA8 sEffectEnvColor = { 150, 150, 150, 0 }; + +void EnBubble_SetDimensions(EnBubble* this, f32 dim) { + f32 a; + f32 b; + f32 c; + f32 d; + + this->actor.flags |= ACTOR_FLAG_0; + Actor_SetScale(&this->actor, 1.0f); + this->actor.shape.yOffset = 16.0f; + this->graphicRotSpeed = 16.0f; + this->graphicEccentricity = 0.08f; + this->expansionWidth = dim; + this->expansionHeight = dim; + a = Rand_ZeroOne(); + b = Rand_ZeroOne(); + c = Rand_ZeroOne(); + this->unk_218 = 1.0f; + this->unk_21C = 1.0f; + d = (a * a) + (b * b) + (c * c); + this->unk_1FC.x = a / d; + this->unk_1FC.y = b / d; + this->unk_1FC.z = c / d; +} + +u32 func_809CBCBC(EnBubble* this) { + ColliderInfo* info = &this->colliderSphere.elements[0].info; + + info->toucher.dmgFlags = 0x8; + info->toucher.effect = 0; + info->toucher.damage = 4; + info->toucherFlags = TOUCH_ON; + this->actor.velocity.y = 0.0f; + return 6; +} + +// only called in an unused actionFunc +u32 func_809CBCEC(EnBubble* this) { + EnBubble_SetDimensions(this, -1.0f); + return 12; +} + +void EnBubble_DamagePlayer(EnBubble* this, GlobalContext* globalCtx) { + s32 damage = -this->colliderSphere.elements[0].info.toucher.damage; + + globalCtx->damagePlayer(globalCtx, damage); + func_8002F7A0(globalCtx, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); +} + +s32 EnBubble_Explosion(EnBubble* this, GlobalContext* globalCtx) { + u32 i; + Vec3f effectAccel; + Vec3f effectVel; + Vec3f effectPos; + + effectAccel = sEffectAccel; + Math_SmoothStepToF(&this->expansionWidth, 4.0f, 0.1f, 1000.0f, 0.0f); + Math_SmoothStepToF(&this->expansionHeight, 4.0f, 0.1f, 1000.0f, 0.0f); + Math_SmoothStepToF(&this->graphicRotSpeed, 54.0f, 0.1f, 1000.0f, 0.0f); + Math_SmoothStepToF(&this->graphicEccentricity, 0.2f, 0.1f, 1000.0f, 0.0f); + this->actor.shape.yOffset = ((this->expansionHeight + 1.0f) * 16.0f); + + if (DECR(this->explosionCountdown) != 0) { + return -1; + } + effectPos.x = this->actor.world.pos.x; + effectPos.y = this->actor.world.pos.y + this->actor.shape.yOffset; + effectPos.z = this->actor.world.pos.z; + for (i = 0; i < 20; i++) { + effectVel.x = (Rand_ZeroOne() - 0.5f) * 7.0f; + effectVel.y = Rand_ZeroOne() * 7.0f; + effectVel.z = (Rand_ZeroOne() - 0.5f) * 7.0f; + EffectSsDtBubble_SpawnCustomColor(globalCtx, &effectPos, &effectVel, &effectAccel, &sEffectPrimColor, + &sEffectEnvColor, Rand_S16Offset(100, 50), 0x19, 0); + } + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0x50); + this->actor.flags &= ~ACTOR_FLAG_0; + return Rand_S16Offset(90, 60); +} + +// only called in an unused actionFunc +u32 func_809CBFD4(EnBubble* this) { + if (DECR(this->explosionCountdown) != 0) { + return -1; + } + return func_809CBCEC(this); +} + +// only called in an unused actionFunc +s32 func_809CC020(EnBubble* this) { + this->expansionWidth += 1.0f / 12.0f; + this->expansionHeight += 1.0f / 12.0f; + + if (DECR(this->explosionCountdown) != 0) { + return false; + } + return true; +} + +void EnBubble_Vec3fNormalizedRelfect(Vec3f* vec1, Vec3f* vec2, Vec3f* ret) { + f32 norm; + + Math3D_Vec3fReflect(vec1, vec2, ret); + norm = sqrtf((ret->x * ret->x) + (ret->y * ret->y) + (ret->z * ret->z)); + if (norm != 0.0f) { + ret->x /= norm; + ret->y /= norm; + ret->z /= norm; + } else { + ret->x = ret->y = ret->z = 0.0f; + } +} + +void EnBubble_Vec3fNormalize(Vec3f* vec) { + f32 norm = sqrt((vec->x * vec->x) + (vec->y * vec->y) + (vec->z * vec->z)); + + if (norm != 0.0f) { + vec->x /= norm; + vec->y /= norm; + vec->z /= norm; + } else { + vec->x = vec->y = vec->z = 0.0f; + } +} + +void EnBubble_Fly(EnBubble* this, GlobalContext* globalCtx) { + CollisionPoly* sp94; + Actor* bumpActor; + Vec3f sp84; + Vec3f sp78; + Vec3f sp6C; + Vec3f sp60; + Vec3f sp54; + f32 bounceSpeed; + s32 bgId; + u8 bounceCount; + + if (this->colliderSphere.elements[1].info.bumperFlags & BUMP_HIT) { + bumpActor = this->colliderSphere.base.ac; + this->normalizedBumpVelocity = bumpActor->velocity; + EnBubble_Vec3fNormalize(&this->normalizedBumpVelocity); + this->velocityFromBump.x += (this->normalizedBumpVelocity.x * 3.0f); + this->velocityFromBump.y += (this->normalizedBumpVelocity.y * 3.0f); + this->velocityFromBump.z += (this->normalizedBumpVelocity.z * 3.0f); + } + this->sinkSpeed -= 0.1f; + if (this->sinkSpeed < this->actor.minVelocityY) { + this->sinkSpeed = this->actor.minVelocityY; + } + sp54.x = this->velocityFromBounce.x + this->velocityFromBump.x; + sp54.y = this->velocityFromBounce.y + this->velocityFromBump.y + this->sinkSpeed; + sp54.z = this->velocityFromBounce.z + this->velocityFromBump.z; + EnBubble_Vec3fNormalize(&sp54); + + sp78.x = this->actor.world.pos.x; + sp78.y = this->actor.world.pos.y + this->actor.shape.yOffset; + sp78.z = this->actor.world.pos.z; + sp6C = sp78; + + sp6C.x += (sp54.x * 24.0f); + sp6C.y += (sp54.y * 24.0f); + sp6C.z += (sp54.z * 24.0f); + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp78, &sp6C, &sp84, &sp94, true, true, true, false, &bgId)) { + sp60.x = COLPOLY_GET_NORMAL(sp94->normal.x); + sp60.y = COLPOLY_GET_NORMAL(sp94->normal.y); + sp60.z = COLPOLY_GET_NORMAL(sp94->normal.z); + EnBubble_Vec3fNormalizedRelfect(&sp54, &sp60, &sp54); + this->bounceDirection = sp54; + bounceCount = this->bounceCount; + this->bounceCount = ++bounceCount; + if (bounceCount > (s16)(Rand_ZeroOne() * 10.0f)) { + this->bounceCount = 0; + } + bounceSpeed = (this->bounceCount == 0) ? 3.6000001f : 3.0f; + this->velocityFromBump.x = this->velocityFromBump.y = this->velocityFromBump.z = 0.0f; + this->velocityFromBounce.x = (this->bounceDirection.x * bounceSpeed); + this->velocityFromBounce.y = (this->bounceDirection.y * bounceSpeed); + this->velocityFromBounce.z = (this->bounceDirection.z * bounceSpeed); + this->sinkSpeed = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_AWA_BOUND); + this->graphicRotSpeed = 128.0f; + this->graphicEccentricity = 0.48f; + } else if (this->actor.bgCheckFlags & 0x20 && sp54.y < 0.0f) { + sp60.x = sp60.z = 0.0f; + sp60.y = 1.0f; + EnBubble_Vec3fNormalizedRelfect(&sp54, &sp60, &sp54); + this->bounceDirection = sp54; + bounceCount = this->bounceCount; + this->bounceCount = ++bounceCount; + if (bounceCount > (s16)(Rand_ZeroOne() * 10.0f)) { + this->bounceCount = 0; + } + bounceSpeed = (this->bounceCount == 0) ? 3.6000001f : 3.0f; + this->velocityFromBump.x = this->velocityFromBump.y = this->velocityFromBump.z = 0.0f; + this->velocityFromBounce.x = (this->bounceDirection.x * bounceSpeed); + this->velocityFromBounce.y = (this->bounceDirection.y * bounceSpeed); + this->velocityFromBounce.z = (this->bounceDirection.z * bounceSpeed); + this->sinkSpeed = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_AWA_BOUND); + this->graphicRotSpeed = 128.0f; + this->graphicEccentricity = 0.48f; + } + this->actor.velocity.x = this->velocityFromBounce.x + this->velocityFromBump.x; + this->actor.velocity.y = this->velocityFromBounce.y + this->velocityFromBump.y + this->sinkSpeed; + this->actor.velocity.z = this->velocityFromBounce.z + this->velocityFromBump.z; + Math_ApproachF(&this->velocityFromBump.x, 0.0f, 0.3f, 0.1f); + Math_ApproachF(&this->velocityFromBump.y, 0.0f, 0.3f, 0.1f); + Math_ApproachF(&this->velocityFromBump.z, 0.0f, 0.3f, 0.1f); +} + +u32 func_809CC648(EnBubble* this) { + if (((this->colliderSphere.base.acFlags & AC_HIT) != 0) == false) { + return false; + } + this->colliderSphere.base.acFlags &= ~AC_HIT; + if (this->colliderSphere.elements[1].info.bumperFlags & BUMP_HIT) { + this->unk_1F0.x = this->colliderSphere.base.ac->velocity.x / 10.0f; + this->unk_1F0.y = this->colliderSphere.base.ac->velocity.y / 10.0f; + this->unk_1F0.z = this->colliderSphere.base.ac->velocity.z / 10.0f; + this->graphicRotSpeed = 128.0f; + this->graphicEccentricity = 0.48f; + return false; + } + this->unk_208 = 8; + return true; +} + +u32 EnBubble_DetectPop(EnBubble* this, GlobalContext* globalCtx) { + if (DECR(this->unk_208) != 0 || this->actionFunc == EnBubble_Pop) { + return false; + } + if (this->colliderSphere.base.ocFlags2 & OC2_HIT_PLAYER) { + this->colliderSphere.base.ocFlags2 &= ~OC2_HIT_PLAYER; + EnBubble_DamagePlayer(this, globalCtx); + this->unk_208 = 8; + return true; + } + return func_809CC648(this); +} + +void func_809CC774(EnBubble* this) { + ColliderJntSphElementDim* dim; + Vec3f src; + Vec3f dest; + + dim = &this->colliderSphere.elements[0].dim; + src.x = dim->modelSphere.center.x; + src.y = dim->modelSphere.center.y; + src.z = dim->modelSphere.center.z; + + Matrix_MultVec3f(&src, &dest); + dim->worldSphere.center.x = dest.x; + dim->worldSphere.center.y = dest.y; + dim->worldSphere.center.z = dest.z; + dim->worldSphere.radius = dim->modelSphere.radius * (1.0f + this->expansionWidth); + this->colliderSphere.elements[1].dim = *dim; +} + +void EnBubble_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBubble* this = (EnBubble*)thisx; + u32 pad; + + ActorShape_Init(&this->actor.shape, 16.0f, ActorShadow_DrawCircle, 0.2f); + Collider_InitJntSph(globalCtx, &this->colliderSphere); + Collider_SetJntSph(globalCtx, &this->colliderSphere, &this->actor, &sJntSphInit, this->colliderSphereItems); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(9), &sColChkInfoInit2); + this->actor.naviEnemyId = 0x16; + this->bounceDirection.x = Rand_ZeroOne(); + this->bounceDirection.y = Rand_ZeroOne(); + this->bounceDirection.z = Rand_ZeroOne(); + EnBubble_Vec3fNormalize(&this->bounceDirection); + this->velocityFromBounce.x = this->bounceDirection.x * 3.0f; + this->velocityFromBounce.y = this->bounceDirection.y * 3.0f; + this->velocityFromBounce.z = this->bounceDirection.z * 3.0f; + EnBubble_SetDimensions(this, 0.0f); + this->actionFunc = EnBubble_Wait; +} + +void EnBubble_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBubble* this = (EnBubble*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->colliderSphere); +} + +void EnBubble_Wait(EnBubble* this, GlobalContext* globalCtx) { + if (EnBubble_DetectPop(this, globalCtx)) { + this->explosionCountdown = func_809CBCBC(this); + this->actionFunc = EnBubble_Pop; + } else { + EnBubble_Fly(this, globalCtx); + this->actor.shape.yOffset = ((this->expansionHeight + 1.0f) * 16.0f); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSphere.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderSphere.base); + } +} + +void EnBubble_Pop(EnBubble* this, GlobalContext* globalCtx) { + if (EnBubble_Explosion(this, globalCtx) >= 0) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 60, NA_SE_EN_AWA_BREAK); + Actor_Kill(&this->actor); + } +} + +// unused +void EnBubble_Disappear(EnBubble* this, GlobalContext* globalCtx) { + s32 temp_v0; + + temp_v0 = func_809CBFD4(this); + if (temp_v0 >= 0) { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + this->explosionCountdown = temp_v0; + this->actionFunc = EnBubble_Regrow; + } +} + +// unused +void EnBubble_Regrow(EnBubble* this, GlobalContext* globalCtx) { + if (func_809CC020(this)) { + this->actionFunc = EnBubble_Wait; + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSphere.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderSphere.base); +} + +void EnBubble_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBubble* this = (EnBubble*)thisx; + + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 16.0f, 16.0f, 0.0f, 7); + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, this->actor.shape.yOffset); +} + +void EnBubble_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnBubble* this = (EnBubble*)thisx; + u32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bubble.c", 1175); + + if (this->actionFunc != EnBubble_Disappear) { + func_80093D84(globalCtx->state.gfxCtx); + Math_SmoothStepToF(&this->graphicRotSpeed, 16.0f, 0.2f, 1000.0f, 0.0f); + Math_SmoothStepToF(&this->graphicEccentricity, 0.08f, 0.2f, 1000.0f, 0.0f); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + Matrix_Scale(this->expansionWidth + 1.0f, this->expansionHeight + 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ(((f32)globalCtx->state.frames * (M_PI / 180.0f)) * this->graphicRotSpeed, MTXMODE_APPLY); + Matrix_Scale(this->graphicEccentricity + 1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ((-(f32)globalCtx->state.frames * (M_PI / 180.0f)) * this->graphicRotSpeed, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bubble.c", 1220), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBubbleDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bubble.c", 1226); + + if (this->actionFunc != EnBubble_Disappear) { + this->actor.shape.shadowScale = (f32)((this->expansionWidth + 1.0f) * 0.2f); + func_809CC774(this); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.h b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.h new file mode 100644 index 000000000..37594d694 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_BUBBLE_H +#define Z_EN_BUBBLE_H + +#include "ultra64.h" +#include "global.h" + +struct EnBubble; + +typedef void (*EnBubbleActionFunc)(struct EnBubble*, GlobalContext*); + +typedef struct EnBubble { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnBubbleActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph colliderSphere; + /* 0x0170 */ ColliderJntSphElement colliderSphereItems[2]; + /* 0x01F0 */ Vec3f unk_1F0; // set but never used + /* 0x01FC */ Vec3f unk_1FC; // randomly generated, set but never used + /* 0x0208 */ s16 unk_208; // set to 8 when about to pop + /* 0x020A */ s16 explosionCountdown; + /* 0x020C */ char unk_20C[4]; // unused + /* 0x0210 */ f32 graphicRotSpeed; + /* 0x0214 */ f32 graphicEccentricity; + /* 0x0218 */ f32 unk_218; // set to 1.0f, never used + /* 0x021C */ f32 unk_21C; // set to 1.0f, never used + /* 0x0220 */ f32 expansionWidth; + /* 0x0224 */ f32 expansionHeight; + /* 0x0228 */ u8 bounceCount; + /* 0x022C */ Vec3f bounceDirection; + /* 0x0238 */ Vec3f velocityFromBounce; + /* 0x0244 */ Vec3f normalizedBumpVelocity; + /* 0x0250 */ Vec3f velocityFromBump; + /* 0x025C */ f32 sinkSpeed; +} EnBubble; // size = 0x0260 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c new file mode 100644 index 000000000..27b8b5228 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c @@ -0,0 +1,435 @@ +/* + * File: z_en_butte.c + * Overlay: ovl_En_Butte + * Description: Butterfly + */ + +#include "z_en_butte.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS 0 + +void EnButte_Init(Actor* thisx, GlobalContext* globalCtx); +void EnButte_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnButte_Update(Actor* thisx, GlobalContext* globalCtx); +void EnButte_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnButte_SetupFlyAround(EnButte* this); +void EnButte_FlyAround(EnButte* this, GlobalContext* globalCtx); +void EnButte_SetupFollowLink(EnButte* this); +void EnButte_FollowLink(EnButte* this, GlobalContext* globalCtx); +void EnButte_SetupTransformIntoFairy(EnButte* this); +void EnButte_TransformIntoFairy(EnButte* this, GlobalContext* globalCtx); +void EnButte_SetupWaitToDie(EnButte* this); +void EnButte_WaitToDie(EnButte* this, GlobalContext* globalCtx); + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x000, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 5 }, 100 } }, +}; +static ColliderJntSphInit sColliderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER | OC1_TYPE_1, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +const ActorInit En_Butte_InitVars = { + ACTOR_EN_BUTTE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(EnButte), + (ActorFunc)EnButte_Init, + (ActorFunc)EnButte_Destroy, + (ActorFunc)EnButte_Update, + (ActorFunc)EnButte_Draw, + NULL, +}; + +typedef struct { + /* 0x00 */ s16 minTime; + /* 0x02 */ s16 maxTime; + /* 0x04 */ f32 speedXZTarget; + /* 0x08 */ f32 speedXZScale; + /* 0x0C */ f32 speedXZStep; + /* 0x10 */ s16 rotYStep; +} EnButteFlightParams; // size = 0x14 + +static EnButteFlightParams sFlyAroundParams[] = { + { 5, 35, 0.0f, 0.1f, 0.5f, 0 }, + { 10, 45, 1.1f, 0.1f, 0.25f, 1000 }, + { 10, 40, 1.5f, 0.1f, 0.3f, 2000 }, +}; +static EnButteFlightParams sFollowLinkParams[] = { + { 3, 3, 0.8f, 0.1f, 0.2f, 0 }, + { 10, 20, 2.0f, 0.3f, 1.0f, 0 }, + { 10, 20, 2.4f, 0.3f, 1.0f, 0 }, +}; + +void EnButte_SelectFlightParams(EnButte* this, EnButteFlightParams* flightParams) { + if (this->flightParamsIdx == 0) { + if (Rand_ZeroOne() < 0.6f) { + this->flightParamsIdx = 1; + } else { + this->flightParamsIdx = 2; + } + } else { + this->flightParamsIdx = 0; + } + + this->timer = Rand_S16Offset(flightParams->minTime, flightParams->maxTime); +} + +static f32 sTransformationEffectScale = 0.0f; +static s16 sTransformationEffectAlpha = 0; + +void EnButte_ResetTransformationEffect(void) { + sTransformationEffectScale = 0.0f; + sTransformationEffectAlpha = 0; +} + +void EnButte_UpdateTransformationEffect(void) { + sTransformationEffectScale += 0.003f; + sTransformationEffectAlpha += 4000; +} + +void EnButte_DrawTransformationEffect(EnButte* this, GlobalContext* globalCtx) { + static Vec3f D_809CE3C4 = { 0.0f, 0.0f, -3.0f }; + Vec3f sp5C; + s32 alpha; + Vec3s camDir; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_choo.c", 295); + + func_80093C14(globalCtx->state.gfxCtx); + + alpha = Math_SinS(sTransformationEffectAlpha) * 250; + alpha = CLAMP(alpha, 0, 255); + + Camera_GetCamDir(&camDir, GET_ACTIVE_CAM(globalCtx)); + Matrix_RotateY(camDir.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_RotateX(camDir.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(camDir.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_MultVec3f(&D_809CE3C4, &sp5C); + Matrix_SetTranslateRotateYXZ(this->actor.focus.pos.x + sp5C.x, this->actor.focus.pos.y + sp5C.y, + this->actor.focus.pos.z + sp5C.z, &camDir); + Matrix_Scale(sTransformationEffectScale, sTransformationEffectScale, sTransformationEffectScale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_choo.c", 317), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 200, 200, 180, alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 210, 255); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gEffFlash1DL)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_choo.c", 326); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 700, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 20, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 600, ICHAIN_STOP), +}; + +void EnButte_Init(Actor* thisx, GlobalContext* globalCtx) { + EnButte* this = (EnButte*)thisx; + + if (this->actor.params == -1) { + this->actor.params = 0; + } + + Actor_ProcessInitChain(&this->actor, sInitChain); + + if ((this->actor.params & 1) == 1) { + this->actor.uncullZoneScale = 200.0f; + } + + SkelAnime_Init(globalCtx, &this->skelAnime, &gButterflySkel, &gButterflyAnim, this->jointTable, this->morphTable, + 8); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderInit, this->colliderItems); + this->actor.colChkInfo.mass = 0; + this->unk_25C = Rand_ZeroOne() * 0xFFFF; + this->unk_25E = Rand_ZeroOne() * 0xFFFF; + this->unk_260 = Rand_ZeroOne() * 0xFFFF; + Animation_Change(&this->skelAnime, &gButterflyAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP_INTERP, 0.0f); + EnButte_SetupFlyAround(this); + this->actor.shape.rot.x -= 0x2320; + this->drawSkelAnime = true; + // "field keep butterfly" + osSyncPrintf("(field keep 蝶)(%x)(arg_data 0x%04x)\n", this, this->actor.params); +} + +void EnButte_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnButte* this = (EnButte*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void func_809CD56C(EnButte* this) { + static f32 D_809CE3E0[] = { 50.0f, 80.0f, 100.0f }; + static f32 D_809CE3EC[] = { 30.0f, 40.0f, 50.0f }; + + this->actor.shape.yOffset += Math_SinS(this->unk_25C) * D_809CE3E0[this->flightParamsIdx] + + Math_SinS(this->unk_25E) * D_809CE3EC[this->flightParamsIdx]; + this->actor.shape.yOffset = CLAMP(this->actor.shape.yOffset, -2000.0f, 2000.0f); +} + +void func_809CD634(EnButte* this) { + static f32 D_809CE3F8[] = { 15.0f, 20.0f, 25.0f }; + static f32 D_809CE404[] = { 7.5f, 10.0f, 12.5f }; + + this->actor.shape.yOffset += Math_SinS(this->unk_25C) * D_809CE3F8[this->flightParamsIdx] + + Math_SinS(this->unk_25E) * D_809CE404[this->flightParamsIdx]; + this->actor.shape.yOffset = CLAMP(this->actor.shape.yOffset, -500.0f, 500.0f); +} + +void EnButte_Turn(EnButte* this) { + s16 target = this->actor.world.rot.y + 0x8000; + s16 diff = target - this->actor.shape.rot.y; + + Math_ScaledStepToS(&this->actor.shape.rot.y, target, ABS(diff) >> 3); + this->actor.shape.rot.x = (s16)(sinf(this->unk_260) * 600.0f) - 0x2320; +} + +void EnButte_SetupFlyAround(EnButte* this) { + EnButte_SelectFlightParams(this, &sFlyAroundParams[this->flightParamsIdx]); + this->actionFunc = EnButte_FlyAround; +} + +void EnButte_FlyAround(EnButte* this, GlobalContext* globalCtx) { + EnButteFlightParams* flightParams = &sFlyAroundParams[this->flightParamsIdx]; + s16 yaw; + Player* player = GET_PLAYER(globalCtx); + f32 distSqFromHome; + f32 maxDistSqFromHome; + f32 minAnimSpeed; + f32 animSpeed; + s16 rotStep; + + distSqFromHome = Math3D_Dist2DSq(this->actor.world.pos.x, this->actor.world.pos.z, this->actor.home.pos.x, + this->actor.home.pos.z); + func_809CD56C(this); + Math_SmoothStepToF(&this->actor.speedXZ, flightParams->speedXZTarget, flightParams->speedXZScale, + flightParams->speedXZStep, 0.0f); + + if (this->unk_257 == 1) { + maxDistSqFromHome = SQ(100.0f); + rotStep = 1000; + } else { + maxDistSqFromHome = SQ(35.0f); + rotStep = 600; + } + + minAnimSpeed = 0.0f; + this->posYTarget = this->actor.home.pos.y; + + if ((this->flightParamsIdx != 0) && ((distSqFromHome > maxDistSqFromHome) || (this->timer < 4))) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + if (Math_ScaledStepToS(&this->actor.world.rot.y, yaw, flightParams->rotYStep) == 0) { + minAnimSpeed = 0.5f; + } + } else if ((this->unk_257 == 0) && (this->actor.child != NULL) && (this->actor.child != &this->actor)) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.child->world.pos); + if (Math_ScaledStepToS(&this->actor.world.rot.y, yaw, rotStep) == 0) { + minAnimSpeed = 0.3f; + } + } else if (this->unk_257 == 1) { + yaw = this->actor.yawTowardsPlayer + 0x8000 + (s16)((Rand_ZeroOne() - 0.5f) * 0x6000); + if (Math_ScaledStepToS(&this->actor.world.rot.y, yaw, rotStep) == 0) { + minAnimSpeed = 0.4f; + } + } else { + this->actor.world.rot.y += (s16)(sinf(this->unk_25C) * 100.0f); + } + + EnButte_Turn(this); + + animSpeed = this->actor.speedXZ / 2.0f + Rand_ZeroOne() * 0.2f + (1.0f - Math_SinS(this->unk_260)) * 0.15f + + (1.0f - Math_SinS(this->unk_25E)) * 0.3f + minAnimSpeed; + this->skelAnime.playSpeed = CLAMP(animSpeed, 0.2f, 1.5f); + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + EnButte_SelectFlightParams(this, &sFlyAroundParams[this->flightParamsIdx]); + } + + if (((this->actor.params & 1) == 1) && (player->heldItemActionParam == PLAYER_AP_STICK) && + (this->swordDownTimer <= 0) && + ((Math3D_Dist2DSq(player->actor.world.pos.x, player->actor.world.pos.z, this->actor.home.pos.x, + this->actor.home.pos.z) < SQ(120.0f)) || + (this->actor.xzDistToPlayer < 60.0f))) { + EnButte_SetupFollowLink(this); + this->unk_257 = 2; + } else if (this->actor.xzDistToPlayer < 120.0) { + this->unk_257 = 1; + } else { + this->unk_257 = 0; + } +} + +void EnButte_SetupFollowLink(EnButte* this) { + EnButte_SelectFlightParams(this, &sFollowLinkParams[this->flightParamsIdx]); + this->actionFunc = EnButte_FollowLink; +} + +void EnButte_FollowLink(EnButte* this, GlobalContext* globalCtx) { + static s32 D_809CE410 = 1500; + EnButteFlightParams* flightParams = &sFollowLinkParams[this->flightParamsIdx]; + Player* player = GET_PLAYER(globalCtx); + f32 distSqFromHome; + Vec3f swordTip; + f32 animSpeed; + f32 minAnimSpeed; + f32 distSqFromSword; + s16 yaw; + + func_809CD634(this); + Math_SmoothStepToF(&this->actor.speedXZ, flightParams->speedXZTarget, flightParams->speedXZScale, + flightParams->speedXZStep, 0.0f); + minAnimSpeed = 0.0f; + + if ((this->flightParamsIdx != 0) && (this->timer < 12)) { + swordTip.x = player->swordInfo[0].tip.x + Math_SinS(player->actor.shape.rot.y) * 10.0f; + swordTip.y = player->swordInfo[0].tip.y; + swordTip.z = player->swordInfo[0].tip.z + Math_CosS(player->actor.shape.rot.y) * 10.0f; + + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &swordTip) + (s16)(Rand_ZeroOne() * D_809CE410); + if (Math_ScaledStepToS(&this->actor.world.rot.y, yaw, 2000) != 0) { + if (globalCtx->gameplayFrames % 2) { + this->actor.world.rot.y += (s16)(sinf(this->unk_25C) * 60.0f); + } + } else { + minAnimSpeed = 0.3f; + } + } + + this->posYTarget = MAX(player->actor.world.pos.y + 30.0f, player->swordInfo[0].tip.y); + + EnButte_Turn(this); + + animSpeed = this->actor.speedXZ / 2.0f + Rand_ZeroOne() * 0.2f + (1.0f - Math_SinS(this->unk_260)) * 0.15f + + (1.0f - Math_SinS(this->unk_25E)) * 0.3f + minAnimSpeed; + this->skelAnime.playSpeed = CLAMP(animSpeed, 0.2f, 1.5f); + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + EnButte_SelectFlightParams(this, &sFollowLinkParams[this->flightParamsIdx]); + D_809CE410 = -D_809CE410; + } + + distSqFromHome = Math3D_Dist2DSq(this->actor.world.pos.x, this->actor.world.pos.z, this->actor.home.pos.x, + this->actor.home.pos.z); + if (!((player->heldItemActionParam == PLAYER_AP_STICK) && (fabsf(player->actor.speedXZ) < 1.8f) && + (this->swordDownTimer <= 0) && (distSqFromHome < SQ(320.0f)))) { + EnButte_SetupFlyAround(this); + } else if (distSqFromHome > SQ(240.0f)) { + distSqFromSword = Math3D_Dist2DSq(player->swordInfo[0].tip.x, player->swordInfo[0].tip.z, + this->actor.world.pos.x, this->actor.world.pos.z); + if (distSqFromSword < SQ(60.0f)) { + EnButte_SetupTransformIntoFairy(this); + } + } +} + +void EnButte_SetupTransformIntoFairy(EnButte* this) { + this->timer = 9; + this->actor.flags |= ACTOR_FLAG_4; + this->skelAnime.playSpeed = 1.0f; + EnButte_ResetTransformationEffect(); + this->actionFunc = EnButte_TransformIntoFairy; +} + +void EnButte_TransformIntoFairy(EnButte* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + EnButte_UpdateTransformationEffect(); + + if (this->timer == 5) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 60, NA_SE_EV_BUTTERFRY_TO_FAIRY); + } else if (this->timer == 4) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, this->actor.focus.pos.x, this->actor.focus.pos.y, + this->actor.focus.pos.z, 0, this->actor.shape.rot.y, 0, FAIRY_HEAL_TIMED); + this->drawSkelAnime = false; + } else if (this->timer <= 0) { + EnButte_SetupWaitToDie(this); + } +} + +void EnButte_SetupWaitToDie(EnButte* this) { + this->timer = 64; + this->actionFunc = EnButte_WaitToDie; + this->actor.draw = NULL; +} + +void EnButte_WaitToDie(EnButte* this, GlobalContext* globalCtx) { + if (this->timer <= 0) { + Actor_Kill(&this->actor); + } +} + +void EnButte_Update(Actor* thisx, GlobalContext* globalCtx) { + EnButte* this = (EnButte*)thisx; + + if ((this->actor.child != NULL) && (this->actor.child->update == NULL) && (this->actor.child != &this->actor)) { + this->actor.child = NULL; + } + + if (this->timer > 0) { + this->timer--; + } + + this->unk_25C += 0x222; + this->unk_25E += 0x1000; + this->unk_260 += 0x600; + + if ((this->actor.params & 1) == 1) { + if (GET_PLAYER(globalCtx)->swordState == 0) { + if (this->swordDownTimer > 0) { + this->swordDownTimer--; + } + } else { + this->swordDownTimer = 80; + } + } + + this->actionFunc(this, globalCtx); + + if (this->actor.update != NULL) { + Actor_MoveForward(&this->actor); + Math_StepToF(&this->actor.world.pos.y, this->posYTarget, 0.6f); + if (this->actor.xyzDistToPlayerSq < 5000.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Actor_SetFocus(&this->actor, this->actor.shape.yOffset * this->actor.scale.y); + } +} + +void EnButte_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnButte* this = (EnButte*)thisx; + + if (this->drawSkelAnime) { + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, NULL); + Collider_UpdateSpheres(0, &this->collider); + } + + if (((this->actor.params & 1) == 1) && (this->actionFunc == EnButte_TransformIntoFairy)) { + EnButte_DrawTransformationEffect(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.h b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.h new file mode 100644 index 000000000..9443e5081 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_BUTTE_H +#define Z_EN_BUTTE_H + +#include "ultra64.h" +#include "global.h" + +struct EnButte; + +typedef void (*EnButteActionFunc)(struct EnButte*, GlobalContext*); + +typedef struct EnButte { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderJntSph collider; + /* 0x016C */ ColliderJntSphElement colliderItems[1]; + /* 0x01AC */ SkelAnime skelAnime; + /* 0x01F0 */ Vec3s jointTable[8]; + /* 0x0220 */ Vec3s morphTable[8]; + /* 0x0250 */ EnButteActionFunc actionFunc; + /* 0x0254 */ s16 timer; + /* 0x0256 */ u8 flightParamsIdx; + /* 0x0257 */ u8 unk_257; + /* 0x0258 */ u8 drawSkelAnime; + /* 0x025A */ s16 swordDownTimer; + /* 0x025C */ s16 unk_25C; + /* 0x025E */ s16 unk_25E; + /* 0x0260 */ s16 unk_260; + /* 0x0264 */ f32 posYTarget; +} EnButte; // size = 0x0268 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c new file mode 100644 index 000000000..14e9ff15c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.c @@ -0,0 +1,910 @@ +/* + * File: z_en_bw.c + * Overlay: ovl_En_Bw + * Description: Torch slug + */ + +#include "z_en_bw.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_bw/object_bw.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnBw_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBw_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBw_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnBw_Reset(void); + +void func_809CE884(EnBw* this, GlobalContext* globalCtx); +void func_809CE9A8(EnBw* this); +void func_809CEA24(EnBw* this, GlobalContext* globalCtx); +void func_809CF72C(EnBw* this); +void func_809CF7AC(EnBw* this, GlobalContext* globalCtx); +void func_809CF8F0(EnBw* this); +void func_809CF984(EnBw* this, GlobalContext* globalCtx); +void func_809CFBA8(EnBw* this); +void func_809CFC4C(EnBw* this, GlobalContext* globalCtx); +void func_809CFF10(EnBw* this); +void func_809CFF98(EnBw* this, GlobalContext* globalCtx); +void func_809D00F4(EnBw* this); +void func_809D014C(EnBw* this, GlobalContext* globalCtx); +void func_809D01CC(EnBw* this); +void func_809D0268(EnBw* this, GlobalContext* globalCtx); +void func_809D03CC(EnBw* this); +void func_809D0424(EnBw* this, GlobalContext* globalCtx); + +const ActorInit En_Bw_InitVars = { + ACTOR_EN_BW, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BW, + sizeof(EnBw), + (ActorFunc)EnBw_Init, + (ActorFunc)EnBw_Destroy, + (ActorFunc)EnBw_Update, + (ActorFunc)EnBw_Draw, + (ActorResetFunc)EnBw_Reset, +}; + +static ColliderCylinderInit sCylinderInit1 = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { 30, 65, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInit2 = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 35, 0, { 0, 0, 0 } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0xF), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(2, 0xF), + /* Ice arrow */ DMG_ENTRY(4, 0xE), + /* Light arrow */ DMG_ENTRY(2, 0xF), + /* Unk arrow 1 */ DMG_ENTRY(2, 0xF), + /* Unk arrow 2 */ DMG_ENTRY(2, 0xF), + /* Unk arrow 3 */ DMG_ENTRY(2, 0xF), + /* Fire magic */ DMG_ENTRY(0, 0x6), + /* Ice magic */ DMG_ENTRY(3, 0xE), + /* Light magic */ DMG_ENTRY(0, 0x6), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0xF), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(2, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xF), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static s32 sSlugGroup = 0; + +void EnBw_SetupAction(EnBw* this, EnBwActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnBw_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBw* this = (EnBw*)thisx; + + Actor_SetScale(&this->actor, 0.012999999f); + this->actor.naviEnemyId = 0x23; + this->actor.gravity = -2.0f; + SkelAnime_Init(globalCtx, &this->skelAnime, &object_bw_Skel_0020F0, &object_bw_Anim_000228, this->jointTable, + this->morphTable, 12); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 40.0f); + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.colChkInfo.health = 6; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.focus.pos = this->actor.world.pos; + func_809CE9A8(this); + this->color1.a = this->color1.r = 255; + this->color1.g = this->color1.b = 0; + this->unk_248 = 0.6f; + this->unk_221 = 3; + Collider_InitCylinder(globalCtx, &this->collider1); + //! this->collider2 should have Init called on it, but it doesn't matter since the heap is zeroed before use. + Collider_SetCylinder(globalCtx, &this->collider1, &this->actor, &sCylinderInit1); + Collider_SetCylinder(globalCtx, &this->collider2, &this->actor, &sCylinderInit2); + this->unk_236 = this->actor.world.rot.y; + this->actor.params = sSlugGroup; + sSlugGroup = (sSlugGroup + 1) & 3; +} + +void EnBw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnBw* this = (EnBw*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider1); + Collider_DestroyCylinder(globalCtx, &this->collider2); +} + +void func_809CE884(EnBw* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + this->unk_222 -= 250; + this->actor.scale.x = 0.013f + Math_SinF(this->unk_222 * 0.001f) * 0.0069999998f; + this->actor.scale.y = 0.013f - Math_SinF(this->unk_222 * 0.001f) * 0.0069999998f; + this->actor.scale.z = 0.013f + Math_SinF(this->unk_222 * 0.001f) * 0.0069999998f; + if (this->unk_222 == 0) { + this->actor.world.rot.y = this->actor.shape.rot.y; + func_809CE9A8(this); + } +} + +void func_809CE9A8(EnBw* this) { + Animation_MorphToLoop(&this->skelAnime, &object_bw_Anim_000228, -2.0f); + this->unk_220 = 2; + this->unk_222 = Rand_ZeroOne() * 200.0f + 200.0f; + this->unk_232 = 0; + this->actor.speedXZ = 0.0f; + EnBw_SetupAction(this, func_809CEA24); +} + +void func_809CEA24(EnBw* this, GlobalContext* globalCtx) { + CollisionPoly* sp74 = NULL; + Vec3f sp68; + u32 sp64 = 0; + s16 sp62; + s16 sp60; + f32 sp5C; + f32 sp58; + Player* player = GET_PLAYER(globalCtx); + Player* player2 = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + this->unk_244 = this->unk_250 + 0.1f; + sp58 = Math_CosF(this->unk_240); + this->unk_240 += this->unk_244; + if (this->unk_24C < 0.8f) { + this->unk_24C += 0.1f; + } + this->actor.scale.x = 0.013f - Math_SinF(this->unk_240) * (this->unk_24C * 0.004f); + this->actor.scale.y = 0.013f - Math_SinF(this->unk_240) * (this->unk_24C * 0.004f); + this->actor.scale.z = 0.013f + Math_SinF(this->unk_240) * (this->unk_24C * 0.004f); + sp5C = Math_CosF(this->unk_240); + if (this->unk_232 == 0) { + if (ABS(sp58) < ABS(sp5C)) { + this->unk_232++; + } + } else { + if (ABS(sp58) > ABS(sp5C)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_WALK); + this->unk_232 = 0; + } + } + sp5C *= this->unk_24C * (10.0f * this->unk_244); + this->actor.speedXZ = ABS(sp5C); + if (this->unk_221 != 1) { + sp58 = Math_SinF(this->unk_240); + sp60 = ABS(sp58) * 85.0f; + this->color1.g = sp60; + } + if ((((globalCtx->gameplayFrames % 4) == (u32)this->actor.params) && (this->actor.speedXZ != 0.0f) && + (sp64 = BgCheck_AnyLineTest2(&globalCtx->colCtx, &this->actor.world.pos, &this->unk_264, &sp68, &sp74, 1, 0, 0, + 1))) || + (this->unk_222 == 0)) { + if (sp74 != NULL) { + sp74 = SEGMENTED_TO_VIRTUAL(sp74); + sp62 = Math_FAtan2F(sp74->normal.x, sp74->normal.z) * ((f32)0x8000 / M_PI); + } else { + sp62 = this->actor.world.rot.y + 0x8000; + } + if ((this->unk_236 != sp62) || (sp64 == 0)) { + if (BgCheck_AnyLineTest2(&globalCtx->colCtx, &this->unk_270, &this->unk_288, &sp68, &sp74, 1, 0, 0, 1)) { + sp64 |= 2; + } + if (BgCheck_AnyLineTest2(&globalCtx->colCtx, &this->unk_270, &this->unk_27C, &sp68, &sp74, 1, 0, 0, 1)) { + sp64 |= 4; + } + switch (sp64) { + case 0: + this->unk_236 += this->unk_238; + case 1: + if (this->unk_221 == 3) { + if (globalCtx->gameplayFrames & 2) { + this->unk_238 = 0x4000; + } else { + this->unk_238 = -0x4000; + } + } else { + if ((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y) >= 0.0f) { + this->unk_238 = 0x4000; + } else { + this->unk_238 = -0x4000; + } + if (this->unk_221 == 1) { + this->unk_238 = -this->unk_238; + } + } + break; + case 2: + this->unk_236 += this->unk_238; + case 3: + this->unk_238 = 0x4000; + break; + case 4: + this->unk_236 += this->unk_238; + case 5: + this->unk_238 = -0x4000; + break; + case 7: + this->unk_238 = 0; + break; + } + if (sp64 != 6) { + this->unk_236 = sp62; + } + this->unk_222 = (Rand_ZeroOne() * 200.0f) + 200.0f; + } + } else if ((this->actor.speedXZ != 0.0f) && (this->actor.bgCheckFlags & 8)) { + if (this->unk_236 != this->actor.wallYaw) { + sp64 = 1; + this->unk_236 = this->actor.wallYaw; + if (this->unk_221 == 3) { + if (globalCtx->gameplayFrames & 0x20) { + this->unk_238 = 0x4000; + } else { + this->unk_238 = -0x4000; + } + this->actor.bgCheckFlags &= ~8; + this->unk_222 = (Rand_ZeroOne() * 20.0f) + 160.0f; + } else { + if ((s16)(this->actor.yawTowardsPlayer - this->unk_236) >= 0) { + this->unk_238 = 0x4000; + } else { + this->unk_238 = -0x4000; + } + if (this->unk_221 == 1) { + this->unk_238 = -this->unk_238; + } + } + } else if (this->unk_221 == 0) { + sp64 = BgCheck_AnyLineTest2(&globalCtx->colCtx, &this->actor.world.pos, &player->actor.world.pos, &sp68, + &sp74, 1, 0, 0, 1); + if (sp64 != 0) { + sp74 = SEGMENTED_TO_VIRTUAL(sp74); + sp60 = Math_FAtan2F(sp74->normal.x, sp74->normal.z) * ((f32)0x8000 / M_PI); + if (this->unk_236 != sp60) { + if ((s16)(this->actor.yawTowardsPlayer - sp60) >= 0) { + this->unk_238 = 0x4000; + } else { + this->unk_238 = -0x4000; + } + this->unk_236 = sp60; + } + } + } + } + this->unk_222--; + if (this->unk_224 != 0) { + this->unk_224--; + } + if ((this->unk_234 == 0) && + !Actor_TestFloorInDirection(&this->actor, globalCtx, 50.0f, this->unk_236 + this->unk_238)) { + if (this->unk_238 != 0x4000) { + this->unk_238 = 0x4000; + } else { + this->unk_238 = -0x4000; + } + } + switch (this->unk_221) { + case 3: + Math_SmoothStepToF(&this->unk_248, 0.6f, 1.0f, 0.05f, 0.0f); + if ((this->unk_224 == 0) && (this->actor.xzDistToPlayer < 200.0f) && + (ABS(this->actor.yDistToPlayer) < 50.0f) && Actor_IsFacingPlayer(&this->actor, 0x1C70)) { + func_809CF72C(this); + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_236 + this->unk_238, 1, + this->actor.speedXZ * 1000.0f, 0); + } + break; + case 0: + Math_SmoothStepToF(&this->unk_248, 0.6f, 1.0f, 0.05f, 0.0f); + if (sp64 == 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, + this->actor.speedXZ * 1000.0f, 0); + if ((this->actor.xzDistToPlayer < 90.0f) && (this->actor.yDistToPlayer < 50.0f) && + Actor_IsFacingPlayer(&this->actor, 0x1554) && + Actor_TestFloorInDirection(&this->actor, globalCtx, 71.24802f, this->actor.yawTowardsPlayer)) { + func_809CF8F0(this); + } + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_236 + this->unk_238, 1, + this->actor.speedXZ * 1000.0f, 0); + } + if ((this->unk_224 == 0) || (ABS(this->actor.yDistToPlayer) > 60.0f) || (player2->stateFlags1 & 0x6000)) { + this->unk_221 = 3; + this->unk_224 = 150; + this->unk_250 = 0.0f; + } + break; + case 1: + if (((sp64 == 0) && !(this->actor.bgCheckFlags & 8)) || Actor_IsFacingPlayer(&this->actor, 0x1C70)) { + if (Actor_IsFacingPlayer(&this->actor, 0x1C70)) { + this->unk_238 = -this->unk_238; + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer - 0x8000, 1, + this->actor.speedXZ * 1000.0f, 0); + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_236 + this->unk_238, 1, + this->actor.speedXZ * 1000.0f, 0); + } + if (this->unk_224 <= 200) { + sp60 = Math_SinS(this->unk_224 * (0x960 - this->unk_224)) * 55.0f; + this->color1.r = 255 - ABS(sp60); + sp60 = Math_SinS(this->unk_224 * (0x960 - this->unk_224)) * 115.0f; + this->color1.g = ABS(sp60) + 85; + sp60 = Math_SinS(0x960 - this->unk_224) * 255.0f; + this->color1.b = ABS(sp60); + if (this->unk_224 == 0) { + this->unk_221 = 3; + this->unk_250 = 0.0f; + } + } + break; + } + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void func_809CF72C(EnBw* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_bw_Anim_0021A0, -2.0f); + this->unk_220 = 3; + this->unk_221 = 0; + this->unk_250 = 0.6f; + this->unk_222 = 20; + this->unk_224 = 0xBB8; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_AIM); + EnBw_SetupAction(this, func_809CF7AC); +} + +void func_809CF7AC(EnBw* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (this->unk_222 > 0) { + this->unk_222--; + } else { + this->unk_222 = 100; + } + } + if (this->unk_222 >= 20) { + this->unk_224 -= 250; + } + this->actor.scale.x = 0.013f - Math_SinF(this->unk_224 * 0.001f) * 0.0034999999f; + this->actor.scale.y = 0.013f + Math_SinF(this->unk_224 * 0.001f) * 0.0245f; + this->actor.scale.z = 0.013f - Math_SinF(this->unk_224 * 0.001f) * 0.0034999999f; + if (this->unk_224 == 0) { + func_809CE9A8(this); + this->unk_224 = 200; + } +} + +void func_809CF8F0(EnBw* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_bw_Anim_002250, -1.0f); + this->actor.speedXZ = 7.0f; + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->unk_220 = 4; + this->unk_222 = 1000; + this->actor.velocity.y = 11.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actor.flags |= ACTOR_FLAG_24; + EnBw_SetupAction(this, func_809CF984); +} + +void func_809CF984(EnBw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 floorPolyType; + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + this->unk_222 += 250; + this->actor.scale.x = 0.013f - Math_SinF(this->unk_222 * 0.001f) * 0.0034999999f; + this->actor.scale.y = 0.013f + Math_SinF(this->unk_222 * 0.001f) * 0.0245f; + this->actor.scale.z = 0.013f - Math_SinF(this->unk_222 * 0.001f) * 0.0034999999f; + if (this->collider1.base.atFlags & AT_HIT) { + this->collider1.base.atFlags &= ~AT_HIT; + this->actor.speedXZ = -6.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if ((&player->actor == this->collider1.base.at) && !(this->collider1.base.atFlags & AT_BOUNCED)) { + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + SkelAnime_Update(&this->skelAnime); + if (this->actor.bgCheckFlags & 3) { + floorPolyType = func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + if ((floorPolyType == 2) || (floorPolyType == 3) || (floorPolyType == 9)) { + Actor_Kill(&this->actor); + return; + } + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 30.0f, 0xB, 4.0f, 0, 0, 0); + this->unk_222 = 3000; + this->actor.flags &= ~ACTOR_FLAG_24; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + EnBw_SetupAction(this, func_809CE884); + } +} + +void func_809CFBA8(EnBw* this) { + Animation_MorphToLoop(&this->skelAnime, &object_bw_Anim_002250, -1.0f); + this->unk_220 = 5; + this->unk_222 = 1000; + this->unk_260 = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 11.0f; + this->unk_25C = Rand_ZeroOne() * 0.25f + 1.0f; + this->unk_224 = 0xBB8; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_REVERSE); + EnBw_SetupAction(this, func_809CFC4C); +} + +void func_809CFC4C(EnBw* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.z, 0x7FFF, 1, 0xFA0, 0); + Math_SmoothStepToF(&this->unk_248, 0.0f, 1.0f, 0.05f, 0.0f); + SkelAnime_Update(&this->skelAnime); + if (this->actor.bgCheckFlags & 3) { + if ((globalCtx->gameplayFrames % 0x80) == 0) { + this->unk_25C = (Rand_ZeroOne() * 0.25f) + 0.7f; + } + this->unk_221 = 4; + this->unk_258 += this->unk_25C; + Math_SmoothStepToF(&this->unk_260, 0.075f, 1.0f, 0.005f, 0.0f); + if (this->actor.bgCheckFlags & 2) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 30.0f, 0xB, 4.0f, 0, 0, 0); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + if (this->unk_224 != 0) { + this->unk_224 -= 250; + this->actor.scale.x = 0.013f + Math_SinF(this->unk_224 * 0.001f) * 0.0069999998f; + this->actor.scale.y = 0.013f - Math_SinF(this->unk_224 * 0.001f) * 0.0069999998f; + this->actor.scale.z = 0.013f + Math_SinF(this->unk_224 * 0.001f) * 0.0069999998f; + } + this->unk_222--; + if (this->unk_222 == 0) { + func_809CFF10(this); + } + } else { + this->color1.r -= 8; + this->color1.g += 32; + if (this->color1.r < 200) { + this->color1.r = 200; + } + if (this->color1.g > 200) { + this->color1.g = 200; + } + if (this->color1.b > 235) { + this->color1.b = 255; + } else { + this->color1.b += 40; + } + if (this->actor.shape.yOffset < 1000.0f) { + this->actor.shape.yOffset += 200.0f; + } + } +} + +void func_809CFF10(EnBw* this) { + Animation_MorphToLoop(&this->skelAnime, &object_bw_Anim_002250, -1.0f); + this->unk_220 = 6; + this->unk_222 = 1000; + this->unk_221 = 3; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 11.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_REVERSE); + this->actor.bgCheckFlags &= ~1; + EnBw_SetupAction(this, func_809CFF98); +} + +void func_809CFF98(EnBw* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.z, 0, 1, 0xFA0, 0); + Math_SmoothStepToF(&this->unk_248, 0.6f, 1.0f, 0.05f, 0.0f); + SkelAnime_Update(&this->skelAnime); + if (this->actor.bgCheckFlags & 3) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 30.0f, 0xB, 4.0f, 0, 0, 0); + this->unk_222 = 0xBB8; + this->unk_250 = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + EnBw_SetupAction(this, func_809CE884); + } + if (this->color1.r < 247) { + this->color1.r += 8; + } else { + this->color1.r = 255; + } + if (this->color1.g < 32) { + this->color1.g = 0; + } else { + this->color1.g -= 32; + } + if (this->color1.b < 40) { + this->color1.b = 0; + } else { + this->color1.b -= 40; + } + if (this->actor.shape.yOffset > 0.0f) { + this->actor.shape.yOffset -= 200.0f; + } +} + +void func_809D00F4(EnBw* this) { + this->unk_220 = 0; + this->unk_222 = 40; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_DEAD); + EnBw_SetupAction(this, func_809D014C); +} + +void func_809D014C(EnBw* this, GlobalContext* globalCtx) { + if (this->unk_230) { + this->actor.scale.x += 0.0002f; + this->actor.scale.y -= 0.0002f; + this->actor.scale.z += 0.0002f; + } + this->actor.shape.shadowAlpha = this->color1.a -= 6; + this->unk_222--; + if (this->unk_222 <= 0) { + Actor_Kill(&this->actor); + } +} + +void func_809D01CC(EnBw* this) { + this->unk_220 = 1; + this->actor.speedXZ = 0.0f; + this->unk_25C = (Rand_ZeroOne() * 0.25f) + 1.0f; + this->unk_260 = 0.0f; + if (this->damageEffect == 0xE) { + this->iceTimer = 0x50; + } + this->unk_222 = (this->actor.colorFilterParams & 0x4000) ? 25 : 80; + EnBw_SetupAction(this, func_809D0268); +} + +void func_809D0268(EnBw* this, GlobalContext* globalCtx) { + this->color1.r -= 1; + this->color1.g += 4; + this->color1.b += 5; + if (this->color1.r < 200) { + this->color1.r = 200; + } + if (this->color1.g > 200) { + this->color1.g = 200; + } + if (this->color1.b > 230) { + this->color1.b = 230; + } + if (this->actor.colorFilterParams & 0x4000) { + if ((globalCtx->gameplayFrames % 0x80) == 0) { + this->unk_25C = 0.5f + Rand_ZeroOne() * 0.25f; + } + this->unk_258 += this->unk_25C; + Math_SmoothStepToF(&this->unk_260, 0.075f, 1.0f, 0.005f, 0.0f); + } + this->unk_222--; + if (this->unk_222 == 0) { + func_809CE9A8(this); + this->color1.r = this->color1.g = 200; + this->color1.b = 255; + this->unk_224 = 0x258; + this->unk_221 = 1; + this->unk_250 = 0.7f; + this->unk_236++; + } +} + +void func_809D03CC(EnBw* this) { + this->actor.speedXZ = 0.0f; + if (this->damageEffect == 0xE) { + this->iceTimer = 32; + } + this->unk_23C = this->actor.colorFilterTimer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + EnBw_SetupAction(this, func_809D0424); +} + +void func_809D0424(EnBw* this, GlobalContext* globalCtx) { + if (this->actor.colorFilterTimer == 0) { + this->unk_23C = 0; + if (this->actor.colChkInfo.health != 0) { + if ((this->unk_220 != 5) && (this->unk_220 != 6)) { + func_809CE9A8(this); + this->color1.r = this->color1.g = 200; + this->color1.b = 255; + this->unk_224 = 0x258; + this->unk_221 = 1; + this->unk_250 = 0.7f; + this->unk_236++; + } else if (this->unk_220 != 5) { + EnBw_SetupAction(this, func_809CFF98); + } else { + func_809CFF10(this); + } + } else { + if (func_800355E4(globalCtx, &this->collider2.base)) { + this->unk_230 = 0; + this->actor.scale.y -= 0.009f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 30.0f, 0xB, 4.0f, 0, 0, 0); + } else { + this->unk_230 = 1; + } + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x90); + func_809D00F4(this); + } + } +} + +void func_809D0584(EnBw* this, GlobalContext* globalCtx) { + if ((this->actor.bgCheckFlags & 0x10) && (this->actor.bgCheckFlags & 1)) { + this->unk_230 = 0; + this->actor.scale.y -= 0.009f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 30.0f, 0xB, 4.0f, 0, 0, 0); + func_809D00F4(this); + } else { + if (this->collider2.base.acFlags & AC_HIT) { + this->collider2.base.acFlags &= ~AC_HIT; + if ((this->actor.colChkInfo.damageEffect == 0) || (this->unk_220 == 6)) { + return; + } + this->damageEffect = this->actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->actor, &this->collider2.info, 0); + if ((this->damageEffect == 1) || (this->damageEffect == 0xE)) { + if (this->unk_23C == 0) { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0, 0x78, 0, 0x50); + func_809D03CC(this); + this->unk_248 = 0.0f; + } + return; + } + if (this->unk_248 == 0.0f) { + Actor_ApplyDamage(&this->actor); + } + if (((this->unk_221 == 1) || (this->unk_221 == 4)) && (this->actor.colChkInfo.health == 0)) { + if (this->unk_220 != 0) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + if (func_800355E4(globalCtx, &this->collider2.base)) { + this->unk_230 = 0; + this->actor.scale.y -= 0.009f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 30.0f, 0xB, 4.0f, 0, + 0, 0); + } else { + this->unk_230 = 1; + } + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x90); + func_809D00F4(this); + } + } else if ((this->unk_220 != 1) && (this->unk_220 != 6)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLEWALK_DAMAGE); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + if (this->unk_220 != 5) { + func_809D01CC(this); + } + this->unk_248 = 0.0f; + } + } + if ((globalCtx->actorCtx.unk_02 != 0) && (this->actor.xzDistToPlayer <= 400.0f) && + (this->actor.bgCheckFlags & 1)) { + if (this->unk_220 == 5) { + this->unk_23C = 0; + func_809CFF10(this); + } else if (this->unk_220 != 0) { + this->unk_23C = 0; + func_809CFBA8(this); + } + } + } +} + +void EnBw_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnBw* this = (EnBw*)thisx; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + s32 pad[3]; // probably an unused Vec3f + Color_RGBA8 sp50 = { 255, 200, 0, 255 }; + Color_RGBA8 sp4C = { 255, 80, 0, 255 }; + Color_RGBA8 sp48 = { 0, 0, 0, 255 }; + Color_RGBA8 sp44 = { 0, 0, 0, 220 }; + + func_809D0584(this, globalCtx); + if (thisx->colChkInfo.damageEffect != 6) { + this->actionFunc(this, globalCtx); + if (this->unk_23C == 0) { + this->unk_23A = (this->unk_23A + 4) & 0x7F; + } + if ((globalCtx->gameplayFrames & this->unk_221) == 0) { + accel.y = -3.0f; + velocity.x = Rand_CenteredFloat(this->unk_248 * 24.0f); + velocity.y = this->unk_248 * 30.0f; + velocity.z = Rand_CenteredFloat(this->unk_248 * 24.0f); + accel.x = velocity.x * -0.075f; + accel.z = velocity.z * -0.075f; + func_8002836C(globalCtx, &thisx->world.pos, &velocity, &accel, &sp50, &sp4C, 0x3C, 0, 0x14); + } + if (this->unk_248 <= 0.4f) { + this->collider1.info.toucher.effect = 0; + if (((globalCtx->gameplayFrames & 1) == 0) && (this->unk_220 < 5) && (this->unk_23C == 0)) { + accel.y = -0.1f; + velocity.x = Rand_CenteredFloat(4.0f); + velocity.y = Rand_CenteredFloat(2.0f) + 6.0f; + velocity.z = Rand_CenteredFloat(4.0f); + accel.x = velocity.x * -0.1f; + accel.z = velocity.z * -0.1f; + Rand_CenteredFloat(4.0f); + Rand_CenteredFloat(4.0f); + sp48.a = this->color1.a; + if (sp48.a >= 30) { + sp44.a = sp48.a - 30; + } else { + sp44.a = 0; + } + func_8002836C(globalCtx, &thisx->world.pos, &velocity, &accel, &sp48, &sp44, 0xB4, 0x28, + 20.0f - (this->unk_248 * 40.0f)); + } + } else { + this->collider1.info.toucher.effect = 1; + } + + this->unk_234 = Actor_TestFloorInDirection(thisx, globalCtx, 50.0f, thisx->world.rot.y); + if ((this->unk_220 == 4) || (this->unk_220 == 6) || (this->unk_220 == 5) || (this->unk_220 == 1) || + (this->unk_234 != 0)) { + Actor_MoveForward(thisx); + } + Actor_UpdateBgCheckInfo(globalCtx, thisx, 20.0f, 30.0f, 21.0f, 0x1F); + } + Collider_UpdateCylinder(thisx, &this->collider2); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + if ((this->unk_220 != 0) && ((thisx->colorFilterTimer == 0) || !(thisx->colorFilterParams & 0x4000))) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + } + if ((this->unk_221 != 1) && (this->unk_220 < 5) && (this->unk_248 > 0.4f)) { + Collider_UpdateCylinder(thisx, &this->collider1); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + } + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 5.0f; +} + +s32 EnBw_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnBw* this = (EnBw*)thisx; + + if (limbIndex == 1) { + gSPSegment((*gfx)++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x20, 1, 0, this->unk_23A, 0x20, 0x20)); + if ((this->unk_220 == 1) || (this->unk_220 == 5)) { + Matrix_Push(); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(this->unk_258 * 0.115f, MTXMODE_APPLY); + Matrix_RotateY(this->unk_258 * 0.13f, MTXMODE_APPLY); + Matrix_RotateZ(this->unk_258 * 0.1f, MTXMODE_APPLY); + Matrix_Scale(1.0f - this->unk_260, 1.0f + this->unk_260, 1.0f - this->unk_260, MTXMODE_APPLY); + Matrix_RotateZ(-(this->unk_258 * 0.1f), MTXMODE_APPLY); + Matrix_RotateY(-(this->unk_258 * 0.13f), MTXMODE_APPLY); + Matrix_RotateX(-(this->unk_258 * 0.115f), MTXMODE_APPLY); + gSPMatrix((*gfx)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bw.c", 1388), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList((*gfx)++, *dList); + Matrix_Pop(); + return 1; + } + } + return 0; +} + +static Vec3f sIceOffsets[] = { + { 20.0f, 10.0f, 0.0f }, { -20.0f, 10.0f, 0.0f }, { 0.0f, 10.0f, -25.0f }, { 10.0f, 0.0f, 15.0f }, + { 10.0f, 0.0f, -15.0f }, { 0.0f, 10.0f, 25.0f }, { -10.0f, 0.0f, 15.0f }, { -10.0f, 0.0f, -15.0f }, +}; + +void EnBw_Draw(Actor* thisx, GlobalContext* globalCtx2) { + Vec3f spAC = { 0.0f, 0.0f, 0.0f }; + GlobalContext* globalCtx = globalCtx2; + EnBw* this = (EnBw*)thisx; + Vec3f icePos; + s32 iceIndex; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bw.c", 1423); + + if (this->color1.a == 0xFF) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, this->color1.r, this->color1.g, this->color1.b, this->color1.a); + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnBw_OverrideLimbDraw, NULL, this, POLY_OPA_DISP); + } else { + func_80093D84(globalCtx->state.gfxCtx); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 0, 0, 0, this->color1.a); + gDPSetEnvColor(POLY_XLU_DISP++, this->color1.r, this->color1.g, this->color1.b, this->color1.a); + gSPSegment(POLY_XLU_DISP++, 0x08, &D_80116280[0]); + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnBw_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + } + + if (((globalCtx->gameplayFrames + 1) % 4) == (u32)thisx->params) { + spAC.z = thisx->scale.z * 375000.0f; + Matrix_MultVec3f(&spAC, &this->unk_264); + spAC.z = thisx->scale.z * 150000.0f; + Matrix_MultVec3f(&spAC, &this->unk_270); + spAC.x = thisx->scale.x * 550000.0f; + Matrix_MultVec3f(&spAC, &this->unk_288); + spAC.x = -spAC.x; + Matrix_MultVec3f(&spAC, &this->unk_27C); + } + + Matrix_Translate(thisx->world.pos.x, thisx->world.pos.y + ((thisx->scale.y - 0.013f) * 1000.0f), thisx->world.pos.z, + MTXMODE_NEW); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + (globalCtx->gameplayFrames * -20) % 0x200, 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 0, 255); + Matrix_Scale(this->unk_248 * 0.01f, this->unk_248 * 0.01f, this->unk_248 * 0.01f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bw.c", 1500), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + + if (this->iceTimer != 0) { + thisx->colorFilterTimer++; + this->iceTimer--; + if ((this->iceTimer & 3) == 0) { + iceIndex = this->iceTimer >> 2; + + icePos.x = sIceOffsets[iceIndex].x + thisx->world.pos.x; + icePos.y = sIceOffsets[iceIndex].y + thisx->world.pos.y; + icePos.z = sIceOffsets[iceIndex].z + thisx->world.pos.z; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, thisx, &icePos, 0x96, 0x96, 0x96, 0xFA, 0xEB, 0xF5, 0xFF, 1.3f); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bw.c", 1521); +} + +void EnBw_Reset(void) { + sSlugGroup = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.h b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.h new file mode 100644 index 000000000..3f0ae5ea9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bw/z_en_bw.h @@ -0,0 +1,47 @@ +#ifndef Z_EN_BW_H +#define Z_EN_BW_H + +#include "ultra64.h" +#include "global.h" + +struct EnBw; +typedef void (*EnBwActionFunc)(struct EnBw*, GlobalContext*); + +typedef struct EnBw { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[12]; + /* 0x01D8 */ Vec3s morphTable[12]; + /* 0x0220 */ u8 unk_220; + /* 0x0221 */ u8 unk_221; + /* 0x0222 */ s16 unk_222; + /* 0x0224 */ s16 unk_224; + /* 0x0228 */ EnBwActionFunc actionFunc; + /* 0x022C */ Color_RGBA8 color1; + /* 0x0230 */ u8 unk_230; + /* 0x0231 */ u8 damageEffect; + /* 0x0232 */ u8 unk_232; + /* 0x0234 */ s16 unk_234; + /* 0x0236 */ s16 unk_236; + /* 0x0238 */ s16 unk_238; + /* 0x023A */ u8 unk_23A; + /* 0x022B */ u8 iceTimer; + /* 0x023C */ u8 unk_23C; + /* 0x0240 */ f32 unk_240; + /* 0x0244 */ f32 unk_244; + /* 0x0248 */ f32 unk_248; + /* 0x024C */ f32 unk_24C; + /* 0x0250 */ f32 unk_250; + /* 0x0254 */ char unk_254[4]; + /* 0x0258 */ f32 unk_258; + /* 0x025C */ f32 unk_25C; + /* 0x0260 */ f32 unk_260; + /* 0x0264 */ Vec3f unk_264; + /* 0x0270 */ Vec3f unk_270; + /* 0x027C */ Vec3f unk_27C; + /* 0x0288 */ Vec3f unk_288; + /* 0x0294 */ ColliderCylinder collider1; + /* 0x02E0 */ ColliderCylinder collider2; +} EnBw; // size = 0x032C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c new file mode 100644 index 000000000..2767ed6ab --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c @@ -0,0 +1,247 @@ +/* + * File: z_en_bx.c + * Overlay: ovl_En_Bx + * Description: Jabu-Jabu Electrified Tentacle + */ + +#include "z_en_bx.h" +#include "objects/object_bxa/object_bxa.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnBx_Init(Actor* thisx, GlobalContext* globalCtx); +void EnBx_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnBx_Update(Actor* thisx, GlobalContext* globalCtx); +void EnBx_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Bx_InitVars = { + ACTOR_EN_BX, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_BXA, + sizeof(EnBx), + (ActorFunc)EnBx_Init, + (ActorFunc)EnBx_Destroy, + (ActorFunc)EnBx_Update, + (ActorFunc)EnBx_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0xFFCFFFFF, 0x03, 0x04 }, + { 0xFFCFFFFF, 0x01, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 60, 100, 100, { 0, 0, 0 } }, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x03, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +void EnBx_Init(Actor* thisx, GlobalContext* globalCtx) { + EnBx* this = (EnBx*)thisx; + Vec3f sp48 = { 0.015f, 0.015f, 0.015f }; + Vec3f sp3C = { 0.0f, 0.0f, 0.0f }; + static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 5300, ICHAIN_STOP), + }; + s32 i; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + thisx->scale.x = thisx->scale.z = 0.01f; + thisx->scale.y = 0.03f; + + thisx->world.pos.y = thisx->world.pos.y - 100.0f; + for (i = 0; i < 4; i++) { + this->unk_184[i] = sp48; + if (i == 0) { + this->unk_1B4[i].x = thisx->shape.rot.x - 0x4000; + } + this->unk_154[i] = thisx->world.pos; + this->unk_154[i].y = thisx->world.pos.y + ((i + 1) * 140.0f); + } + + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 48.0f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_InitQuad(globalCtx, &this->colliderQuad); + Collider_SetQuad(globalCtx, &this->colliderQuad, &this->actor, &sQuadInit); + thisx->colChkInfo.mass = MASS_IMMOVABLE; + this->unk_14C = 0; + thisx->uncullZoneDownward = 2000.0f; + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0xFF)) { + Actor_Kill(&this->actor); + } + thisx->params &= 0xFF; +} + +void EnBx_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnBx* this = (EnBx*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_809D1D0C(Actor* thisx, GlobalContext* globalCtx) { + Vec3f sp5C = { 8000.0f, 15000.0f, 2500.0f }; + Vec3f sp50 = { 8000.0f, 10000.0f, 2500.0f }; + static Vec3f D_809D2540 = { -8000.0f, 15000.0f, 2500.0f }; + static Vec3f D_809D254C = { -8000.0f, 10000.0f, 2500.0f }; + Vec3f sp44; + Vec3f sp38; + EnBx* this = (EnBx*)thisx; + + Matrix_MultVec3f(&D_809D2540, &sp44); + Matrix_MultVec3f(&D_809D254C, &sp38); + Matrix_MultVec3f(&sp5C, &this->colliderQuad.dim.quad[1]); + Matrix_MultVec3f(&sp50, &this->colliderQuad.dim.quad[0]); + Collider_SetQuadVertices(&this->colliderQuad, &sp38, &sp44, &this->colliderQuad.dim.quad[0], + &this->colliderQuad.dim.quad[1]); +} + +void EnBx_Update(Actor* thisx, GlobalContext* globalCtx) { + EnBx* this = (EnBx*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 i; + s16 tmp32; + s32 tmp33; + + if ((thisx->xzDistToPlayer <= 70.0f) || (this->collider.base.atFlags & AT_HIT) || + (this->collider.base.acFlags & AC_HIT) || (this->colliderQuad.base.atFlags & AT_HIT)) { + if ((thisx->xzDistToPlayer <= 70.0f) || (&player->actor == this->collider.base.at) || + (&player->actor == this->collider.base.ac) || (&player->actor == this->colliderQuad.base.at)) { + tmp33 = player->invincibilityTimer & 0xFF; + tmp32 = thisx->world.rot.y; + if (!(thisx->params & 0x80)) { + tmp32 = thisx->yawTowardsPlayer; + } + if ((&player->actor != this->collider.base.at) && (&player->actor != this->collider.base.ac) && + (&player->actor != this->colliderQuad.base.at) && (player->invincibilityTimer <= 0)) { + if (player->invincibilityTimer < -39) { + player->invincibilityTimer = 0; + } else { + player->invincibilityTimer = 0; + globalCtx->damagePlayer(globalCtx, -4); + } + } + func_8002F71C(globalCtx, &this->actor, 6.0f, tmp32, 6.0f); + player->invincibilityTimer = tmp33; + } + + this->collider.base.atFlags &= ~AT_HIT; + this->collider.base.acFlags &= ~AC_HIT; + this->colliderQuad.base.atFlags &= ~AT_HIT; + this->colliderQuad.base.at = NULL; + this->collider.base.ac = NULL; + this->collider.base.at = NULL; + this->unk_14C = 0x14; + } + + if (this->unk_14C != 0) { + this->unk_14C--; + for (i = 0; i < 4; i++) { + if (!((this->unk_14C + (i << 1)) % 4)) { + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 200, 255, 255, 255 }; + Vec3f pos; + s16 yaw; + + yaw = (s32)Rand_CenteredFloat(12288.0f); + yaw = (yaw + (i * 0x4000)) + 0x2000; + pos.x = Rand_CenteredFloat(5.0f) + thisx->world.pos.x; + pos.y = Rand_CenteredFloat(30.0f) + thisx->world.pos.y + 170.0f; + pos.z = Rand_CenteredFloat(5.0f) + thisx->world.pos.z; + EffectSsLightning_Spawn(globalCtx, &pos, &primColor, &envColor, 230, yaw, 6, 0); + } + } + + Audio_PlayActorSound2(thisx, NA_SE_EN_BIRI_SPARK - SFX_FLAG); + } + thisx->focus.pos = thisx->world.pos; + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (thisx->params & 0x80) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderQuad.base); + } +} + +void EnBx_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* D_809D2560[] = { + object_bxa_Tex_0024F0, + object_bxa_Tex_0027F0, + object_bxa_Tex_0029F0, + }; + EnBx* this = (EnBx*)thisx; + s32 pad; + Mtx* mtx = Graph_Alloc(globalCtx->state.gfxCtx, 4 * sizeof(Mtx)); + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_bx.c", 464); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x0C, mtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_809D2560[this->actor.params & 0x7F])); + gSPSegment(POLY_OPA_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 16, 16, 1, 0, (globalCtx->gameplayFrames * -10) % 128, + 32, 32)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_bx.c", 478), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->actor.params & 0x80) { + func_809D1D0C(&this->actor, globalCtx); + } + + this->unk_14E -= 0xBB8; + thisx->scale.z = thisx->scale.x = (Math_CosS(this->unk_14E) * 0.0075f) + 0.015f; + + for (i = 3; i >= 0; i--) { + s16 off = (0x2000 * i); + + this->unk_184[i].z = this->unk_184[i].x = (Math_CosS(this->unk_14E + off) * 0.0075f) + 0.015f; + this->unk_1B4[i].x = thisx->shape.rot.x; + this->unk_1B4[i].y = thisx->shape.rot.y; + this->unk_1B4[i].z = thisx->shape.rot.z; + } + + for (i = 0; i < 4; i++, mtx++) { + Matrix_Translate(this->unk_154[i].x, this->unk_154[i].y, this->unk_154[i].z, MTXMODE_NEW); + Matrix_RotateZYX(this->unk_1B4[i].x, this->unk_1B4[i].y, this->unk_1B4[i].z, MTXMODE_APPLY); + Matrix_Scale(this->unk_184[i].x, this->unk_184[i].y, this->unk_184[i].z, MTXMODE_APPLY); + Matrix_ToMtx(mtx, "../z_en_bx.c", 507); + } + + gSPDisplayList(POLY_OPA_DISP++, object_bxa_DL_0022F0); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_bx.c", 511); +} diff --git a/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h new file mode 100644 index 000000000..189349a0f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.h @@ -0,0 +1,21 @@ +#ifndef Z_EN_BX_H +#define Z_EN_BX_H + +#include "ultra64.h" +#include "global.h" + +struct EnBx; + +typedef struct EnBx { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 unk_14C; + /* 0x014E */ s16 unk_14E; + /* 0x0150 */ char unk_150[0x4]; + /* 0x0154 */ Vec3f unk_154[4]; + /* 0x0184 */ Vec3f unk_184[4]; + /* 0x01B4 */ Vec3s unk_1B4[4]; + /* 0x01CC */ ColliderCylinder collider; + /* 0x0218 */ ColliderQuad colliderQuad; +} EnBx; // size = 0x0298 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c b/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c new file mode 100644 index 000000000..3f306c64e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.c @@ -0,0 +1,301 @@ +/* + * File: z_en_changer.c + * Overlay: ovl_En_Changer + * Description: Treasure Box Shop Minigame + */ + +#include "z_en_changer.h" +#include "vt.h" +#include "overlays/actors/ovl_Item_Etcetera/z_item_etcetera.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" + +#define FLAGS 0 + +typedef enum { + /* 0 */ CHEST_LEFT, + /* 1 */ CHEST_RIGHT +} ChangerChestSide; + +void EnChanger_Init(Actor* thisx, GlobalContext* globalCtx); +void EnChanger_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnChanger_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnChanger_Wait(EnChanger* this, GlobalContext* globalCtx); +void EnChanger_OpenChests(EnChanger* this, GlobalContext* globalCtx); +void EnChanger_SetHeartPieceFlag(EnChanger* this, GlobalContext* globalCtx); + +const ActorInit En_Changer_InitVars = { + ACTOR_EN_CHANGER, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnChanger), + (ActorFunc)EnChanger_Init, + (ActorFunc)EnChanger_Destroy, + (ActorFunc)EnChanger_Update, + NULL, + NULL, +}; + +static Vec3f sLeftChestPos[] = { + { 0.0f, 0.0f, 0.0f }, { -100.0f, 20.0f, -245.0f }, { -100.0f, 20.0f, -685.0f }, + { -100.0f, 20.0f, -1125.0f }, { -100.0f, 20.0f, -1565.0f }, { -100.0f, 20.0f, -2005.0f }, +}; + +static Vec3f sRightChestPos[] = { + { 0.0f, 0.0f, 0.0f }, { 140.0f, 20.0f, -245.0f }, { 140.0f, 20.0f, -685.0f }, + { 140.0f, 20.0f, -1125.0f }, { 140.0f, 20.0f, -1565.0f }, { 140.0f, 20.0f, -2005.0f }, +}; + +static s32 sLoserGetItemIds[] = { + GI_NONE, GI_RUPEE_GREEN_LOSE, GI_RUPEE_GREEN_LOSE, GI_RUPEE_BLUE_LOSE, GI_RUPEE_BLUE_LOSE, GI_RUPEE_RED_LOSE, +}; + +static s32 sItemEtcTypes[] = { + 0, + ITEM_ETC_RUPEE_GREEN_CHEST_GAME, + ITEM_ETC_RUPEE_GREEN_CHEST_GAME, + ITEM_ETC_RUPEE_BLUE_CHEST_GAME, + ITEM_ETC_RUPEE_BLUE_CHEST_GAME, + ITEM_ETC_RUPEE_RED_CHEST_GAME, +}; + +static s32 sTreasureFlags[] = { 0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000A }; + +void EnChanger_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnChanger_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnChanger* this = (EnChanger*)thisx; + GlobalContext* globalCtx = globalCtx2; + s16 leftChestParams; + s16 rightChestParams; + s16 rewardChestParams; + s16 minigameRoomNum; + s16 rightChestItem; + s16 leftChestItem; + s16 temp_v1_3; + s16 new_var; + s32 rewardParams; + + if (1) {} + + minigameRoomNum = globalCtx->roomCtx.curRoom.num - 1; + if (minigameRoomNum < 0) { + minigameRoomNum = 0; + } + if (Flags_GetTreasure(globalCtx, sTreasureFlags[minigameRoomNum])) { + this->roomChestsOpened = true; + } + + osSyncPrintf("\n\n"); + // "Treasure generation (which room is it?)" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 宝発生(部屋はどれ?) %d\n" VT_RST, globalCtx->roomCtx.curRoom.num); + // "How is the Bit?" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ビットは? \t %x\n" VT_RST, globalCtx->actorCtx.flags.chest); + // "How is the Save BIT?" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ セーブBITは? %x\n" VT_RST, sTreasureFlags[minigameRoomNum]); + // "Is it already a zombie?" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ もう、ゾンビ?\t %d\n" VT_RST, this->roomChestsOpened); + osSyncPrintf("\n\n"); + + minigameRoomNum *= 2; + // Spawn Heart Piece in chest (or Purple Rupee if won Heart Piece) + if (globalCtx->roomCtx.curRoom.num >= 6) { + rewardChestParams = ((gSaveContext.itemGetInf[1] & 0x800) ? (0x4EA0) : (0x4EC0)); + rewardChestParams = sTreasureFlags[5] | rewardChestParams; + this->finalChest = (EnBox*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_BOX, + 20.0f, 20.0f, -2500.0f, 0, 0x7FFF, 0, rewardChestParams); + if (this->finalChest != NULL) { + if (this->roomChestsOpened) { + Flags_SetTreasure(globalCtx, rewardChestParams & 0x1F); + Actor_Kill(&this->actor); + return; + } else { + rewardParams = ((gSaveContext.itemGetInf[1] & 0x800) ? (ITEM_ETC_RUPEE_PURPLE_CHEST_GAME) + : (ITEM_ETC_HEART_PIECE_CHEST_GAME)) & + 0xFF; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_ETCETERA, 20.0f, 20.0f, -2500.0f, 0, 0, 0, + ((sTreasureFlags[5] & 0x1F) << 8) + rewardParams); + // "Central treasure instance/occurrence (GREAT)" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 中央宝発生(GREAT) ☆☆☆☆☆ %x\n" VT_RST, rewardChestParams); + this->actionFunc = EnChanger_SetHeartPieceFlag; + return; + } + } + } + + temp_v1_3 = minigameRoomNum; + // Set up items in chests, swap them round with probability 1/2 + leftChestParams = (sLoserGetItemIds[globalCtx->roomCtx.curRoom.num] << 5) | 0x4000; + new_var = temp_v1_3; + this->leftChestNum = new_var; + this->leftChestGetItemId = sLoserGetItemIds[globalCtx->roomCtx.curRoom.num]; + leftChestItem = sItemEtcTypes[globalCtx->roomCtx.curRoom.num]; + leftChestParams |= new_var; + rightChestParams = new_var | 0x4E21; + this->rightChestNum = new_var | 1; + this->rightChestGetItemId = GI_DOOR_KEY; + rightChestItem = ITEM_ETC_KEY_SMALL_CHEST_GAME; + + if (Rand_ZeroFloat(1.99f) < 1.0f) { + rightChestParams = (sLoserGetItemIds[globalCtx->roomCtx.curRoom.num] << 5) | 0x4000; + this->rightChestNum = new_var; + this->rightChestGetItemId = sLoserGetItemIds[globalCtx->roomCtx.curRoom.num]; + rightChestItem = sItemEtcTypes[globalCtx->roomCtx.curRoom.num]; + leftChestParams = new_var | 0x4E21; + rightChestParams |= new_var; + this->leftChestNum = temp_v1_3 | 1; + this->leftChestGetItemId = GI_DOOR_KEY; + leftChestItem = ITEM_ETC_KEY_SMALL_CHEST_GAME; + } + + this->leftChest = (EnBox*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_BOX, sLeftChestPos[globalCtx->roomCtx.curRoom.num].x, + sLeftChestPos[globalCtx->roomCtx.curRoom.num].y, sLeftChestPos[globalCtx->roomCtx.curRoom.num].z, 0, -0x3FFF, 0, + leftChestParams); + + if (this->leftChest != NULL) { + // "Left treasure generation (what does it contain?)" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 左宝発生(ナニがはいってるの?) ☆☆☆☆☆ %x\n" VT_RST, leftChestParams); + // "What is the room number?" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 部屋番号は? %x\n" VT_RST, globalCtx->roomCtx.curRoom.num); + // "What is the bit?" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ ビットはなぁに? %x\n" VT_RST, this->rightChestNum); + // "Sukesuke-kun" (something to do with being invisible) + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ すけすけ君? %x\n" VT_RST, rightChestItem); + osSyncPrintf("\n\n"); + if (this->roomChestsOpened) { + Flags_SetTreasure(globalCtx, this->leftChestNum & 0x1F); + } else { + Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_ITEM_ETCETERA, sLeftChestPos[globalCtx->roomCtx.curRoom.num].x, + sLeftChestPos[globalCtx->roomCtx.curRoom.num].y, sLeftChestPos[globalCtx->roomCtx.curRoom.num].z, 0, 0, + 0, ((this->leftChestNum & 0x1F) << 8) + (leftChestItem & 0xFF)); + } + } + + this->rightChest = (EnBox*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_BOX, sRightChestPos[globalCtx->roomCtx.curRoom.num].x, + sRightChestPos[globalCtx->roomCtx.curRoom.num].y, sRightChestPos[globalCtx->roomCtx.curRoom.num].z, 0, 0x3FFF, + 0, rightChestParams); + + if (this->rightChest != NULL) { + // "Right treasure generation (what does it contain?)" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 右宝発生(ナニがはいってるの?) ☆☆☆☆☆ %x\n" VT_RST, rightChestParams); + // "What is the room number?" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 部屋番号は? %d\n" VT_RST, globalCtx->roomCtx.curRoom.num); + // "What is the bit?" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ ビットはなぁに? %x\n" VT_RST, this->leftChestNum); + // "Sukesuke-kun" (something to do with being invisible) + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ すけすけ君? %x\n" VT_RST, leftChestItem); + osSyncPrintf("\n\n"); + + if (this->roomChestsOpened) { + Flags_SetTreasure(globalCtx, this->rightChestNum & 0x1F); + Actor_Kill(&this->actor); + return; + } + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_ETCETERA, + sRightChestPos[globalCtx->roomCtx.curRoom.num].x, sRightChestPos[globalCtx->roomCtx.curRoom.num].y, + sRightChestPos[globalCtx->roomCtx.curRoom.num].z, 0, 0, 0, + ((this->rightChestNum & 0x1F) << 8) + (rightChestItem & 0xFF)); + } + + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnChanger_Wait; +} + +void EnChanger_Wait(EnChanger* this, GlobalContext* globalCtx) { + if (this->leftChest->unk_1F4 != 0) { + this->timer = 80; + Flags_SetTreasure(globalCtx, this->rightChestNum & 0x1F); + this->actionFunc = EnChanger_OpenChests; + } else if (this->rightChest->unk_1F4 != 0) { + this->chestOpened = CHEST_RIGHT; + this->timer = 80; + Flags_SetTreasure(globalCtx, this->leftChestNum & 0x1F); + this->actionFunc = EnChanger_OpenChests; + } +} + +// Spawns the EnExItem showing what was in the other chest +void EnChanger_OpenChests(EnChanger* this, GlobalContext* globalCtx) { + f32 zPos; + f32 yPos; + f32 xPos; + s16 temp_s0; + s16 temp_s0_2; + EnBox* left; + EnBox* right; + + left = this->leftChest; + right = this->rightChest; + + if (this->timer == 0) { + temp_s0_2 = temp_s0 = this->chestOpened; // Required to use the right registers + + switch (temp_s0_2) { + case CHEST_LEFT: + xPos = right->dyna.actor.world.pos.x; + yPos = right->dyna.actor.world.pos.y; + zPos = right->dyna.actor.world.pos.z; + + if (this->rightChestGetItemId == GI_DOOR_KEY) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_EX_ITEM, xPos, yPos, zPos, 0, 0, 0, 0xF); + Flags_SetSwitch(globalCtx, 0x32); + } else { + temp_s0_2 = (s16)(this->rightChestGetItemId - GI_RUPEE_GREEN_LOSE) + EXITEM_CHEST; + // "Open right treasure (chest)" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 右宝開く ☆☆☆☆☆ %d\n" VT_RST, temp_s0_2); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_EX_ITEM, xPos, yPos, zPos, 0, 0, 0, + temp_s0_2); + } + break; + case CHEST_RIGHT: + xPos = left->dyna.actor.world.pos.x; + yPos = left->dyna.actor.world.pos.y; + zPos = left->dyna.actor.world.pos.z; + + if (this->leftChestGetItemId == GI_DOOR_KEY) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_EX_ITEM, xPos, yPos, zPos, 0, 0, 0, 0xF); + Flags_SetSwitch(globalCtx, 0x32); + } else { + temp_s0_2 = (s16)(this->leftChestGetItemId - 0x72) + 0xA; + // "Open left treasure (chest)" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 左宝開く ☆☆☆☆☆ %d\n" VT_RST, temp_s0_2); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_EX_ITEM, xPos, yPos, zPos, 0, 0, 0, + temp_s0_2); + } + break; + } + + Actor_Kill(&this->actor); + } +} + +void EnChanger_SetHeartPieceFlag(EnChanger* this, GlobalContext* globalCtx) { + if (this->finalChest->unk_1F4 != 0) { + if (!(gSaveContext.itemGetInf[1] & 0x800)) { + gSaveContext.itemGetInf[1] |= 0x800; + } + Actor_Kill(&this->actor); + } +} + +void EnChanger_Update(Actor* thisx, GlobalContext* globalCtx) { + EnChanger* this = (EnChanger*)thisx; + + this->actionFunc(this, globalCtx); + + if (this->timer != 0) { + this->timer--; + } + + if (BREG(0)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 255, 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.h b/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.h new file mode 100644 index 000000000..6b5560e56 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Changer/z_en_changer.h @@ -0,0 +1,27 @@ +#ifndef Z_EN_CHANGER_H +#define Z_EN_CHANGER_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Box/z_en_box.h" + +struct EnChanger; + +typedef void (*EnChangerActionFunc)(struct EnChanger*, GlobalContext*); + +typedef struct EnChanger { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnChangerActionFunc actionFunc; + /* 0x0150 */ EnBox* leftChest; + /* 0x0154 */ EnBox* rightChest; + /* 0x0158 */ EnBox* finalChest; + /* 0x015C */ s16 leftChestNum; + /* 0x015E */ s16 rightChestNum; + /* 0x0160 */ s16 leftChestGetItemId; + /* 0x0162 */ s16 rightChestGetItemId; + /* 0x0164 */ s16 chestOpened; + /* 0x0166 */ s16 timer; + /* 0x0168 */ s16 roomChestsOpened; +} EnChanger; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c new file mode 100644 index 000000000..e68eaabb1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -0,0 +1,1031 @@ +#include "z_en_clear_tag.h" + +#include + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnClearTag_Init(Actor* thisx, GlobalContext* globalCtx); +void EnClearTag_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnClearTag_Update(Actor* thisx, GlobalContext* globalCtx); +void EnClearTag_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnClearTag_Reset(void); + +void EnClearTag_UpdateEffects(GlobalContext* globalCtx); +void EnClearTag_DrawEffects(GlobalContext* globalCtx); + +void EnClearTag_CreateDebrisEffect(GlobalContext* globalCtx, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, + f32 scale, f32 floorHeight); +void EnClearTag_CreateFireEffect(GlobalContext* globalCtx, Vec3f* pos, f32 scale); +void EnClearTag_CreateSmokeEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale); +void EnClearTag_CreateFlashEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale, f32 floorHeight, + Vec3f* floorTangent); + +void EnClearTag_CalculateFloorTangent(EnClearTag* this); + +const ActorInit En_Clear_Tag_InitVars = { + ACTOR_EN_CLEAR_TAG, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnClearTag), + (ActorFunc)EnClearTag_Init, + (ActorFunc)EnClearTag_Destroy, + (ActorFunc)EnClearTag_Update, + (ActorFunc)EnClearTag_Draw, + (ActorResetFunc)EnClearTag_Reset, +}; + +static u8 sIsEffectsInitialized = false; + +static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f }; + +static ColliderCylinderInit sArwingCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, 30, 10, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sLaserCylinderInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, 30, 10, { 0, 0, 0 } }, +}; + +static UNK_TYPE4 D_809D5C98 = 0; // unused +static UNK_TYPE4 D_809D5C9C = 0; // unused + +static EnClearTagEffect sClearTagEffects[CLEAR_TAG_EFFECT_MAX_COUNT]; + +#include "overlays/ovl_En_Clear_Tag/ovl_En_Clear_Tag.h" + +/** + * Creates a debris effect. + * Debris effects are spawned when the Arwing dies. It spawns fire effects. + */ +void EnClearTag_CreateDebrisEffect(GlobalContext* globalCtx, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, + f32 scale, f32 floorHeight) { + s16 i; + EnClearTagEffect* effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a Debris effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + effect->type = CLEAR_TAG_EFFECT_DEBRIS; + + effect->position = *position; + effect->velocity = *velocity; + effect->acceleration = *acceleration; + + effect->scale = scale; + + // Set the debris effects to spawn in a circle. + effect->rotationY = Rand_ZeroFloat(M_PI * 2); + effect->rotationX = Rand_ZeroFloat(M_PI * 2); + + effect->timer = effect->bounces = 0; + + effect->floorHeight = floorHeight; + + effect->random = (s16)Rand_ZeroFloat(10.0f); + + return; + } + } +} + +/** + * Creates a fire effect. + * Fire effects are spawned by debris effects. Fire effects spawn smoke effects + */ +void EnClearTag_CreateFireEffect(GlobalContext* globalCtx, Vec3f* pos, f32 scale) { + s16 i; + EnClearTagEffect* effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a fire effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + effect->random = (s16)Rand_ZeroFloat(100.0f); + effect->type = CLEAR_TAG_EFFECT_FIRE; + + effect->position = *pos; + effect->velocity = sZeroVector; + effect->acceleration = sZeroVector; + effect->acceleration.y = 0.15f; + + effect->scale = scale; + + effect->primColor.a = 200.0f; + + return; + } + } +} + +/** + * Creates a smoke effect. + * Smoke effects are spawned by fire effects. + */ +void EnClearTag_CreateSmokeEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale) { + s16 i; + EnClearTagEffect* effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a smoke effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + effect->random = (s16)Rand_ZeroFloat(100.0f); + effect->type = CLEAR_TAG_EFFECT_SMOKE; + + effect->position = *position; + effect->velocity = sZeroVector; + effect->acceleration = sZeroVector; + + effect->scale = scale; + effect->maxScale = scale * 2.0f; + + effect->primColor.r = 200.0f; + effect->primColor.g = 20.0f; + effect->primColor.b = 0.0f; + effect->primColor.a = 255.0f; + effect->envColor.r = 255.0f; + effect->envColor.g = 215.0f; + effect->envColor.b = 255.0f; + + return; + } + } +} + +/** + * Creates a flash effect. + * Flash effects are spawned when the Arwing dies. + * Flash effects two components: 1) a billboard flash, and 2) a light effect on the ground. + */ +void EnClearTag_CreateFlashEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale, f32 floorHeight, + Vec3f* floorTangent) { + s16 i; + EnClearTagEffect* effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a flash effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + effect->type = CLEAR_TAG_EFFECT_FLASH; + + effect->position = *position; + effect->velocity = sZeroVector; + effect->acceleration = sZeroVector; + + effect->scale = 0.0f; + effect->maxScale = scale * 2.0f; + + effect->floorHeight = floorHeight; + effect->floorTangent = *floorTangent; + + effect->primColor.a = 180.0f; + + return; + } + } +} + +/** + * EnClear_Tag destructor. + * This just destroys the collider. + */ +void EnClearTag_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnClearTag* this = (EnClearTag*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +/** + * EnClear_Tag constructor. + * This allocates a collider, initializes effects, and sets up ClearTag instance data. + */ +void EnClearTag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnClearTag* this = (EnClearTag*)thisx; + s32 defaultCutsceneTimer = 100; + s16 i; + s16 j; + + Collider_InitCylinder(globalCtx, &this->collider); + + // Initialize the Arwing laser. + if (this->actor.params == CLEAR_TAG_LASER) { + this->state = CLEAR_TAG_STATE_LASER; + this->timers[CLEAR_TAG_TIMER_LASER_DEATH] = 70; + this->actor.speedXZ = 35.0f; + func_8002D908(&this->actor); + for (j = 0; j <= 0; j++) { + func_8002D7EC(&this->actor); + } + this->actor.scale.x = 0.4f; + this->actor.scale.y = 0.4f; + this->actor.scale.z = 2.0f; + this->actor.speedXZ = 70.0f; + this->actor.shape.rot.x = -this->actor.shape.rot.x; + + func_8002D908(&this->actor); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sLaserCylinderInit); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_REFLECT_MG); + } else { // Initialize the Arwing. + this->actor.flags |= ACTOR_FLAG_0; + this->actor.targetMode = 5; + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sArwingCylinderInit); + this->actor.colChkInfo.health = 3; + + // Update the Arwing to play the intro cutscene. + if (this->actor.params == CLEAR_TAG_CUTSCENE_ARWING) { + this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = 70; + this->timers[CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON] = 250; + this->state = CLEAR_TAG_STATE_DEMO; + this->actor.world.rot.x = 0x4000; + this->cutsceneMode = CLEAR_TAG_CUTSCENE_MODE_SETUP; + this->cutsceneTimer = defaultCutsceneTimer; + this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_BG_INFO] = 20; + } + + // Initialize all effects to available if effects have not been initialized. + if (!sIsEffectsInitialized) { + sIsEffectsInitialized = true; + globalCtx->specialEffects = &sClearTagEffects[0]; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++) { + sClearTagEffects[i].type = CLEAR_TAG_EFFECT_AVAILABLE; + } + this->drawMode = CLEAR_TAG_DRAW_MODE_ALL; + } + } +} + +/** + * Calculate a floor tangent. + * This is used for the ground flash display lists and Arwing shadow display lists to snap onto the floor. + */ +void EnClearTag_CalculateFloorTangent(EnClearTag* this) { + // If there is a floor poly below the Arwing, calculate the floor tangent. + if (this->actor.floorPoly != NULL) { + f32 x = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.x); + f32 y = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); + f32 z = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.z); + + this->floorTangent.x = -Math_FAtan2F(-z * y, 1.0f); + this->floorTangent.z = Math_FAtan2F(-x * y, 1.0f); + } +} + +/** + * EnClear_Tag update function. + * + * ClearTag has three variants: + * CLEAR_TAG_CUTSCENE_ARWING which is the regular Arwing and plays a cutscene on spawn. + * CLEAR_TAG_ARWING which is a regular Arwing. + * CLEAR_TAG_LASER which are the lasers fired by the Arwing. + * + * This function controls the Arwing flying. A target position is set and the Arwing flies toward it based on + * calculated directions from the current position. + * This function fires Arwing lasers when the state is CLEAR_TAG_STATE_TARGET_LOCKED. + * This function controls the cutscene that plays when the Arwing has params for + * cutscene. The cutscene stops playing when the Arwing is a specified distance from the starting point. + */ +void EnClearTag_Update(Actor* thisx, GlobalContext* globalCtx2) { + u8 hasAtHit = false; + s16 i; + s16 xRotationTarget; + s16 rotationScale; + GlobalContext* globalCtx = globalCtx2; + EnClearTag* this = (EnClearTag*)thisx; + Player* player = GET_PLAYER(globalCtx); + + this->frameCounter++; + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_EFFECT) { + for (i = 0; i < 3; i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + if (this->cutsceneTimer != 0) { + this->cutsceneTimer--; + } + + switch (this->state) { + case CLEAR_TAG_STATE_DEMO: + case CLEAR_TAG_STATE_TARGET_LOCKED: + case CLEAR_TAG_STATE_FLYING: { + f32 vectorToTargetX; + f32 vectorToTargetY; + f32 vectorToTargetZ; + s16 worldRotationTargetX; + s16 worldRotationTargetY; + f32 loseTargetLockDistance; + s16 worldRotationTargetZ; + s32 pad; + + // Check if the Arwing should crash. + if (this->collider.base.acFlags & AC_HIT) { + + this->collider.base.acFlags &= ~AC_HIT; + this->crashingTimer = 20; + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); + this->acceleration.x = Rand_CenteredFloat(15.0f); + this->acceleration.y = Rand_CenteredFloat(15.0f); + this->acceleration.z = Rand_CenteredFloat(15.0f); + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); + this->actor.colChkInfo.health--; + if ((s8)this->actor.colChkInfo.health <= 0) { + this->state = CLEAR_TAG_STATE_CRASHING; + this->actor.velocity.y = 0.0f; + goto state_crashing; + } + } + Actor_SetScale(&this->actor, 0.2f); + this->actor.speedXZ = 7.0f; + + if (this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] == 0) { + if (this->timers[CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON] == 0) { + this->state = CLEAR_TAG_STATE_TARGET_LOCKED; + this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = 300; + } else { + this->state = CLEAR_TAG_STATE_FLYING; + this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = (s16)Rand_ZeroFloat(50.0f) + 20; + + if (this->actor.params == CLEAR_TAG_ARWING) { + // Set the Arwing to fly in a circle around the player. + f32 targetCircleX = Math_SinS(player->actor.shape.rot.y) * 400.0f; + f32 targetCircleZ = Math_CosS(player->actor.shape.rot.y) * 400.0f; + + this->targetPosition.x = + Rand_CenteredFloat(700.0f) + (player->actor.world.pos.x + targetCircleX); + this->targetPosition.y = Rand_ZeroFloat(200.0f) + player->actor.world.pos.y + 150.0f; + this->targetPosition.z = + Rand_CenteredFloat(700.0f) + (player->actor.world.pos.z + targetCircleZ); + } else { + // Set the Arwing to fly to a random position. + this->targetPosition.x = Rand_CenteredFloat(700.0f); + this->targetPosition.y = Rand_ZeroFloat(200.0f) + 150.0f; + this->targetPosition.z = Rand_CenteredFloat(700.0f); + } + } + + this->targetDirection.x = this->targetDirection.y = this->targetDirection.z = 0.0f; + } + + rotationScale = 10; + xRotationTarget = 0x800; + loseTargetLockDistance = 100.0f; + if (this->state == CLEAR_TAG_STATE_TARGET_LOCKED) { + // Set the Arwing to fly towards the player. + this->targetPosition.x = player->actor.world.pos.x; + this->targetPosition.y = player->actor.world.pos.y + 40.0f; + this->targetPosition.z = player->actor.world.pos.z; + rotationScale = 7; + xRotationTarget = 0x1000; + loseTargetLockDistance = 150.0f; + } else if (this->state == CLEAR_TAG_STATE_DEMO) { + // Move the Arwing for the intro cutscene. + + // Do a Barrel Roll! + this->roll += 0.5f; + if (this->roll > M_PI * 2) { + this->roll -= M_PI * 2; + } + + // Set the Arwing to fly to a hardcoded position. + this->targetPosition.x = 0.0f; + this->targetPosition.y = 300.0f; + this->targetPosition.z = 0.0f; + loseTargetLockDistance = 100.0f; + } + + // If the Arwing is not in cutscene state, smoothly update the roll to zero. + // This will reset the Arwing to be right side up after the cutscene is done. + // The cutscene will set the Arwing to do a barrel roll and doesn't end on right side up. + if (this->state != CLEAR_TAG_STATE_DEMO) { + Math_ApproachZeroF(&this->roll, 0.1f, 0.2f); + } + + // Calculate a vector towards the targetted position. + vectorToTargetX = this->targetPosition.x - this->actor.world.pos.x; + vectorToTargetY = this->targetPosition.y - this->actor.world.pos.y; + vectorToTargetZ = this->targetPosition.z - this->actor.world.pos.z; + + // If the Arwing is within a certain distance to the target position, it will be updated to flymode + if (sqrtf(SQ(vectorToTargetX) + SQ(vectorToTargetY) + SQ(vectorToTargetZ)) < loseTargetLockDistance) { + this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = 0; + if (this->state == CLEAR_TAG_STATE_TARGET_LOCKED) { + this->timers[CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON] = (s16)Rand_ZeroFloat(100.0f) + 100; + } + this->state = CLEAR_TAG_STATE_FLYING; + } + + // Calculate the direction for the Arwing to fly and the rotation for the Arwing + // based on the Arwing's direction, and current rotation. + worldRotationTargetY = Math_FAtan2F(vectorToTargetX, vectorToTargetZ) * (0x8000 / M_PI); + worldRotationTargetX = + Math_FAtan2F(vectorToTargetY, sqrtf(SQ(vectorToTargetX) + SQ(vectorToTargetZ))) * (0x8000 / M_PI); + if ((worldRotationTargetX < 0) && (this->actor.world.pos.y < this->actor.floorHeight + 20.0f)) { + worldRotationTargetX = 0; + } + Math_ApproachS(&this->actor.world.rot.x, worldRotationTargetX, rotationScale, this->targetDirection.x); + worldRotationTargetZ = Math_SmoothStepToS(&this->actor.world.rot.y, worldRotationTargetY, rotationScale, + this->targetDirection.y, 0); + Math_ApproachF(&this->targetDirection.x, xRotationTarget, 1.0f, 0x100); + this->targetDirection.y = this->targetDirection.x; + if (ABS(worldRotationTargetZ) < 0x1000) { + Math_ApproachS(&this->actor.world.rot.z, 0, 15, this->targetDirection.z); + Math_ApproachF(&this->targetDirection.z, 0x500, 1.0f, 0x100); + + // Check if the Arwing should fire its laser. + if ((this->frameCounter % 4) == 0 && (Rand_ZeroOne() < 0.75f) && + (this->state == CLEAR_TAG_STATE_TARGET_LOCKED)) { + this->shouldShootLaser = true; + } + } else { + worldRotationTargetZ = worldRotationTargetZ > 0 ? -0x2500 : 0x2500; + Math_ApproachS(&this->actor.world.rot.z, worldRotationTargetZ, rotationScale, + this->targetDirection.z); + Math_ApproachF(&this->targetDirection.z, 0x1000, 1.0f, 0x200); + } + this->actor.shape.rot = this->actor.world.rot; + this->actor.shape.rot.x = -this->actor.shape.rot.x; + + // Update the Arwing's velocity. + func_8002D908(&this->actor); + this->actor.velocity.x += this->acceleration.x; + this->actor.velocity.y += this->acceleration.y; + this->actor.velocity.z += this->acceleration.z; + Math_ApproachZeroF(&this->acceleration.x, 1.0f, 1.0f); + Math_ApproachZeroF(&this->acceleration.y, 1.0f, 1.0f); + Math_ApproachZeroF(&this->acceleration.z, 1.0f, 1.0f); + + // Fire the Arwing laser. + if (this->shouldShootLaser) { + this->shouldShootLaser = false; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_CLEAR_TAG, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, this->actor.world.rot.x, + this->actor.world.rot.y, this->actor.world.rot.z, CLEAR_TAG_STATE_LASER); + } + } + case CLEAR_TAG_STATE_CRASHING: + state_crashing: + if (this->crashingTimer != 0) { + this->crashingTimer--; + } + + func_8002D7EC(&this->actor); + + Actor_SetFocus(&this->actor, 0.0f); + + // Update Arwing collider to better match a ground collision. + this->collider.dim.radius = 20; + this->collider.dim.height = 15; + this->collider.dim.yShift = -5; + Collider_UpdateCylinder(&this->actor, &this->collider); + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->timers[CLEAR_TAG_TIMER_ARWING_UPDATE_BG_INFO] == 0) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 30.0f, 100.0f, 5); + EnClearTag_CalculateFloorTangent(this); + } + + if (this->state == CLEAR_TAG_STATE_CRASHING) { + // Create fire effects while the Arwing crashes. + EnClearTag_CreateFireEffect(globalCtx, &this->actor.world.pos, 1.0f); + + // Causes the Arwing to roll around seemingly randomly while crashing. + this->roll -= 0.5f; + this->actor.velocity.y -= 0.2f; + this->actor.shape.rot.x += 0x10; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_BREATH - SFX_FLAG); + + // Check if the Arwing has hit the ground. + if (this->actor.bgCheckFlags & 9) { + this->shouldExplode = true; + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_ARWING) { + this->drawMode = CLEAR_TAG_DRAW_MODE_EFFECT; + this->deathTimer = 70; + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + Actor_Kill(&this->actor); + } + } + } + break; + + case CLEAR_TAG_STATE_LASER: + func_8002D7EC(&this->actor); + + // Check if the laser has hit a target. + if (this->collider.base.atFlags & AT_HIT) { + hasAtHit = true; + } + + // Set laser collider properties. + this->collider.dim.radius = 23; + this->collider.dim.height = 25; + this->collider.dim.yShift = -10; + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 80.0f, 100.0f, 5); + + // Check if the laser has hit a target, timed out, or hit the ground. + if (this->actor.bgCheckFlags & 9 || hasAtHit || this->timers[CLEAR_TAG_TIMER_LASER_DEATH] == 0) { + // Kill the laser. + Actor_Kill(&this->actor); + // Player laser sound effect if the laser did not time out. + if (this->timers[CLEAR_TAG_TIMER_LASER_DEATH] != 0) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, + NA_SE_EN_FANTOM_THUNDER_GND); + } + } + break; + } + + if (this->state < CLEAR_TAG_STATE_LASER) { + // Play the Arwing cutscene. + osSyncPrintf("DEMO_MODE %d\n", this->cutsceneMode); + osSyncPrintf("CAMERA_NO %d\n", this->cameraId); + + if (this->cutsceneMode != CLEAR_TAG_CUTSCENE_MODE_NONE) { + f32 cutsceneCameraCircleX; + f32 cutsceneCameraCircleZ; + s16 cutsceneTimer; + Vec3f cutsceneCameraAtTarget; + Vec3f cutsceneCameraEyeTarget; + + switch (this->cutsceneMode) { + case CLEAR_TAG_CUTSCENE_MODE_SETUP: + // Initializes Arwing cutscene camera data. + this->cutsceneMode = CLEAR_TAG_CUTSCENE_MODE_PLAY; + func_80064520(globalCtx, &globalCtx->csCtx); + this->cameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->cameraId, CAM_STAT_ACTIVE); + case CLEAR_TAG_CUTSCENE_MODE_PLAY: + // Update the Arwing cutscene camera to spin around in a circle. + cutsceneTimer = this->frameCounter * 128; + cutsceneCameraCircleX = Math_SinS(cutsceneTimer) * 200.0f; + cutsceneCameraCircleZ = Math_CosS(cutsceneTimer) * 200.0f; + cutsceneCameraAtTarget.x = this->actor.world.pos.x + cutsceneCameraCircleX; + cutsceneCameraAtTarget.y = 200.0f; + cutsceneCameraAtTarget.z = this->actor.world.pos.z + cutsceneCameraCircleZ; + cutsceneCameraEyeTarget = this->actor.world.pos; + break; + } + + // Make the Arwing cutscene camera approach the target. + if (this->cameraId != SUBCAM_FREE) { + Math_ApproachF(&this->cutsceneCameraAt.x, cutsceneCameraAtTarget.x, 0.1f, 500.0f); + Math_ApproachF(&this->cutsceneCameraAt.y, cutsceneCameraAtTarget.y, 0.1f, 500.0f); + Math_ApproachF(&this->cutsceneCameraAt.z, cutsceneCameraAtTarget.z, 0.1f, 500.0f); + Math_ApproachF(&this->cutsceneCameraEye.x, cutsceneCameraEyeTarget.x, 0.2f, 500.0f); + Math_ApproachF(&this->cutsceneCameraEye.y, cutsceneCameraEyeTarget.y, 0.2f, 500.0f); + Math_ApproachF(&this->cutsceneCameraEye.z, cutsceneCameraEyeTarget.z, 0.2f, 500.0f); + Gameplay_CameraSetAtEye(globalCtx, this->cameraId, &this->cutsceneCameraEye, + &this->cutsceneCameraAt); + } + + // Cutscene has finished. + if (this->cutsceneTimer == 1) { + func_800C08AC(globalCtx, this->cameraId, 0); + this->cutsceneMode = this->cameraId = SUBCAM_FREE; + func_80064534(globalCtx, &globalCtx->csCtx); + } + } + } + } + + // Explode the Arwing + if (this->shouldExplode) { + Vec3f crashEffectLocation; + Vec3f crashEffectVelocity; + Vec3f debrisEffectAcceleration; + + this->shouldExplode = false; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_IT_BOMB_EXPLOSION); + + // Spawn flash effect. + crashEffectLocation.x = this->actor.world.pos.x; + crashEffectLocation.y = (this->actor.world.pos.y + 40.0f) - 30.0f; + crashEffectLocation.z = this->actor.world.pos.z; + EnClearTag_CreateFlashEffect(globalCtx, &crashEffectLocation, 6.0f, this->actor.floorHeight, + &this->floorTangent); + + // Spawn smoke effect. + crashEffectLocation.y = (this->actor.world.pos.y + 30.0f) - 50.0f; + EnClearTag_CreateSmokeEffect(globalCtx, &crashEffectLocation, 3.0f); + crashEffectLocation.y = this->actor.world.pos.y; + + // Spawn debris effects. + for (i = 0; i < 15; i++) { + crashEffectVelocity.x = sinf(i * 1.65f) * i * 0.3f; + crashEffectVelocity.z = cosf(i * 1.65f) * i * 0.3f; + crashEffectVelocity.y = Rand_ZeroFloat(6.0f) + 5.0f; + crashEffectVelocity.x += Rand_CenteredFloat(0.5f); + crashEffectVelocity.z += Rand_CenteredFloat(0.5f); + + debrisEffectAcceleration.x = 0.0f; + debrisEffectAcceleration.y = -1.0f; + debrisEffectAcceleration.z = 0.0f; + + EnClearTag_CreateDebrisEffect(globalCtx, &crashEffectLocation, &crashEffectVelocity, + &debrisEffectAcceleration, Rand_ZeroFloat(0.15f) + 0.075f, + this->actor.floorHeight); + } + } + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_ARWING) { + // Check if the Arwing should be removed. + if ((this->drawMode == CLEAR_TAG_DRAW_MODE_EFFECT) && (DECR(this->deathTimer) == 0)) { + Actor_Kill(&this->actor); + } + + EnClearTag_UpdateEffects(globalCtx); + } +} + +/** + * EnClear_Tag draw function. + * Laser clear tag type will draw two lasers. + * Arwing clear tage types will draw the Arwing, the backfire, and a shadow. + */ +void EnClearTag_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnClearTag* this = (EnClearTag*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 983); + if (this->drawMode != CLEAR_TAG_DRAW_MODE_EFFECT) { + func_80093D84(globalCtx->state.gfxCtx); + + if (this->state >= CLEAR_TAG_STATE_LASER) { + // Draw Arwing lasers. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 255, 0, 255); + + Matrix_Translate(25.0f, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1004), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingLaserDL); + + Matrix_Translate(-50.0f, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1011), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingLaserDL); + } else { + // Draw the Arwing itself. + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + if (this->crashingTimer != 0) { + f32 xRotation; + f32 yRotation; + f32 scaledCrashingTimer = this->crashingTimer * 0.05f; + + xRotation = Math_SinS(this->frameCounter * 0x3000) * scaledCrashingTimer; + yRotation = Math_SinS(this->frameCounter * 0x3700) * scaledCrashingTimer; + Matrix_RotateX(xRotation, MTXMODE_APPLY); + Matrix_RotateY(yRotation, MTXMODE_APPLY); + } + Matrix_RotateZ(this->roll, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1030), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gArwingDL); + + // Draw the Arwing Backfire + Matrix_Translate(0.0f, 0.0f, -60.0f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(2.5f, 1.3f, 0.0f, MTXMODE_APPLY); + if ((this->frameCounter % 2) != 0) { + Matrix_Scale(1.15f, 1.15f, 1.15f, MTXMODE_APPLY); + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, 155); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 0, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1067), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingBackfireDL); + + // Draw the Arwing shadow. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 130); + Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateX(this->floorTangent.x, MTXMODE_APPLY); + Matrix_RotateZ(this->floorTangent.z, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x + 0.35f, 0.0f, this->actor.scale.z + 0.35f, MTXMODE_APPLY); + Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((this->actor.shape.rot.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((this->actor.shape.rot.z / 32768.0f) * M_PI, MTXMODE_APPLY); + if (this->crashingTimer != 0) { + f32 xRotation; + f32 yRotation; + f32 scaledCrashingTimer = this->crashingTimer * 0.05f; + + xRotation = Math_SinS(this->frameCounter * 0x3000) * scaledCrashingTimer; + yRotation = Math_SinS(this->frameCounter * 0x3700) * scaledCrashingTimer; + Matrix_RotateX(xRotation, MTXMODE_APPLY); + Matrix_RotateY(yRotation, MTXMODE_APPLY); + } + Matrix_RotateZ(this->roll, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1104), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingShadowDL); + } + } + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_ARWING) { + EnClearTag_DrawEffects(globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1119); +} + +/** + * Updates the Arwing effects. + * Performs effect physics. + * Moves and bounces debris effects. + * Fades most effects out of view. When effects are completely faded away they are removed. + */ +void EnClearTag_UpdateEffects(GlobalContext* globalCtx) { + EnClearTagEffect* effect = (EnClearTagEffect*)globalCtx->specialEffects; + s16 i; + f32 originalYPosition; + Vec3f sphereCenter; + + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type != CLEAR_TAG_EFFECT_AVAILABLE) { + effect->random++; + + // Perform effect physics. + effect->position.x += effect->velocity.x; + originalYPosition = effect->position.y; + effect->position.y += effect->velocity.y; + effect->position.z += effect->velocity.z; + effect->velocity.x += effect->acceleration.x; + effect->velocity.y += effect->acceleration.y; + effect->velocity.z += effect->acceleration.z; + + if (effect->type == CLEAR_TAG_EFFECT_DEBRIS) { + // Clamp the velocity to -5.0 + if (effect->velocity.y < -5.0f) { + effect->velocity.y = -5.0f; + } + + // While the effect is falling check if it has hit the ground. + if (effect->velocity.y < 0.0f) { + sphereCenter = effect->position; + sphereCenter.y += 5.0f; + + // Check if the debris has hit the ground. + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &sphereCenter, 11.0f)) { + effect->position.y = originalYPosition; + + // Bounce the debris effect. + if (effect->bounces <= 0) { + effect->bounces++; + effect->velocity.y *= -0.5f; + effect->timer = ((s16)Rand_ZeroFloat(20)) + 25; + } else { + // The Debris effect is done bouncing. Set its velocity and acceleration to 0. + effect->velocity.x = effect->velocity.z = effect->acceleration.y = effect->velocity.y = + 0.0f; + } + } + } + + // Rotate the debris effect. + if (effect->acceleration.y != 0.0f) { + effect->rotationY += 0.5f; + effect->rotationX += 0.35f; + } + + // If the timer is completed, unload the debris effect. + if (effect->timer == 1) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + + // Spawn a fire effect every 3 frames. + if (effect->random >= 3) { + effect->random = 0; + EnClearTag_CreateFireEffect(globalCtx, &effect->position, effect->scale * 8.0f); + } + } else if (effect->type == CLEAR_TAG_EFFECT_FIRE) { + // Fade the fire effect. + Math_ApproachZeroF(&effect->primColor.a, 1.0f, 15.0f); + // If the fire effect is fully faded, unload it. + if (effect->primColor.a <= 0.0f) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + } else if (effect->type == CLEAR_TAG_EFFECT_SMOKE) { + // Fade the smoke effects. + Math_ApproachZeroF(&effect->primColor.r, 1.0f, 20.0f); + Math_ApproachZeroF(&effect->primColor.g, 1.0f, 2.0f); + Math_ApproachZeroF(&effect->envColor.r, 1.0f, 25.5f); + Math_ApproachZeroF(&effect->envColor.g, 1.0f, 21.5f); + Math_ApproachZeroF(&effect->envColor.b, 1.0f, 25.5f); + + // Smooth scale the smoke effects. + Math_ApproachF(&effect->scale, effect->maxScale, 0.05f, 0.1f); + + if (effect->primColor.r == 0.0f) { + // Fade the smoke effects. + Math_ApproachZeroF(&effect->primColor.a, 1.0f, 3.0f); + + // If the smoke effect has fully faded, unload it. + if (effect->primColor.a <= 0.0f) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + } + } else if (effect->type == CLEAR_TAG_EFFECT_FLASH) { + // Smooth scale the flash effects. + Math_ApproachF(&effect->scale, effect->maxScale, 1.0f, 3.0f); + // Fade the flash effects. + Math_ApproachZeroF(&effect->primColor.a, 1.0f, 10.0f); + + // If the flash effect has fully faded, unload it. + if (effect->primColor.a <= 0.0f) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + } + + if (effect->timer != 0) { + effect->timer--; + } + } + } +} + +/** + * Draws the Arwing effects. + * Each effect type is drawn before the next. The function will apply a material that applies to all effects of that + * type while drawing the first effect of that type. + */ +void EnClearTag_DrawEffects(GlobalContext* globalCtx) { + s16 i; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u8 isMaterialApplied = false; + EnClearTagEffect* effect = (EnClearTagEffect*)globalCtx->specialEffects; + EnClearTagEffect* firstEffect = effect; + + OPEN_DISPS(gfxCtx, "../z_en_clear_tag.c", 1288); + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + // Draw all Debris effects. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_DEBRIS) { + // Apply the debris effect material if it has not already been applied. + if (!isMaterialApplied) { + isMaterialApplied++; + gSPDisplayList(POLY_OPA_DISP++, gArwingDebrisEffectMaterialDL); + } + + // Draw the debris effect. + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_RotateY(effect->rotationY, MTXMODE_APPLY); + Matrix_RotateX(effect->rotationX, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1307), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gArwingDebrisEffectDL); + } + } + + // Draw all ground flash effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_FLASH) { + // Apply the flash ground effect material if it has not already been applied. + if (!isMaterialApplied) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 200, 0); + isMaterialApplied++; + } + + // Draw the ground flash effect. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, (s8)effect->primColor.a); + Matrix_Translate(effect->position.x, effect->floorHeight, effect->position.z, MTXMODE_NEW); + Matrix_RotateX(effect->floorTangent.x, MTXMODE_APPLY); + Matrix_RotateZ(effect->floorTangent.z, MTXMODE_APPLY); + Matrix_Scale(effect->scale + effect->scale, 1.0f, effect->scale * 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1342), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFlashEffectGroundDL); + } + } + + // Draw all smoke effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_SMOKE) { + // Apply the smoke effect material if it has not already been applied. + if (!isMaterialApplied) { + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectMaterialDL); + isMaterialApplied++; + } + + // Draw the smoke effect. + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, (s8)effect->envColor.r, (s8)effect->envColor.g, (s8)effect->envColor.b, + 128); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (s8)effect->primColor.r, (s8)effect->primColor.g, + (s8)effect->primColor.b, (s8)effect->primColor.a); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, effect->random * -5, 32, 64, 1, 0, 0, 32, 32)); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + Matrix_Translate(0.0f, 20.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1392), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectDL); + } + } + + // Draw all fire effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_FIRE) { + // Apply the fire effect material if it has not already been applied. + if (!isMaterialApplied) { + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + isMaterialApplied++; + } + + // Draw the fire effect. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s8)effect->primColor.a); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (effect->random * -15) & 0xFF, 32, 64, 1, 0, 0, + 32, 32)); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1439), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectDL); + } + } + + // Draw all flash billboard effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_FLASH) { + // Apply the flash billboard effect material if it has not already been applied. + if (!isMaterialApplied) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 200, 0); + isMaterialApplied++; + } + + // Draw the flash billboard effect. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, (s8)effect->primColor.a); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1470), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFlashEffectDL); + } + } + + CLOSE_DISPS(gfxCtx, "../z_en_clear_tag.c", 1477); +} + +void EnClearTag_Reset(void) { + memset(sClearTagEffects, 0, sizeof(sClearTagEffects)); + sIsEffectsInitialized = false; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h new file mode 100644 index 000000000..707fbb4a5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h @@ -0,0 +1,95 @@ +#ifndef Z_EN_CLEAR_TAG_H +#define Z_EN_CLEAR_TAG_H + +#include "ultra64.h" +#include "global.h" + +struct EnClearTag; + +typedef enum { + /* 0x00 */ CLEAR_TAG_CUTSCENE_ARWING = 0, + /* 0x01 */ CLEAR_TAG_ARWING = 1, + /* 0x64 */ CLEAR_TAG_LASER = 100 +} ClearTagType; + +typedef enum { + /* 0x00 */ CLEAR_TAG_STATE_FLYING = 0, + /* 0x01 */ CLEAR_TAG_STATE_TARGET_LOCKED = 1, + /* 0x02 */ CLEAR_TAG_STATE_DEMO = 2, + /* 0x0A */ CLEAR_TAG_STATE_CRASHING = 10, + /* 0x64 */ CLEAR_TAG_STATE_LASER = 100 +} ClearTagState; + +typedef enum { + /* 0x00 */ CLEAR_TAG_CUTSCENE_MODE_NONE, + /* 0x01 */ CLEAR_TAG_CUTSCENE_MODE_SETUP, + /* 0x02 */ CLEAR_TAG_CUTSCENE_MODE_PLAY +} ClearTagDemoMode; + +typedef enum { + /* 0x00 */ CLEAR_TAG_DRAW_MODE_ARWING, + /* 0x01 */ CLEAR_TAG_DRAW_MODE_ALL, + /* 0x02 */ CLEAR_TAG_DRAW_MODE_EFFECT +} ClearTagDrawMode; + +typedef enum { + /* 0x00 */ CLEAR_TAG_EFFECT_AVAILABLE, + /* 0x01 */ CLEAR_TAG_EFFECT_DEBRIS, + /* 0x02 */ CLEAR_TAG_EFFECT_FIRE, + /* 0x03 */ CLEAR_TAG_EFFECT_SMOKE, + /* 0x04 */ CLEAR_TAG_EFFECT_FLASH +} ClearTagEffectType; + +typedef enum { + /* 0x00 */ CLEAR_TAG_TIMER_ARWING_UPDATE_STATE = 0, + /* 0x00 */ CLEAR_TAG_TIMER_LASER_DEATH = 0, + /* 0x01 */ CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON, + /* 0x02 */ CLEAR_TAG_TIMER_ARWING_UPDATE_BG_INFO, + /* 0x03 */ CLEAR_TAG_TIMER_COUNT +} ClearTagTimers; + +typedef struct EnClearTag { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 shouldExplode; + /* 0x014D */ u8 drawMode; + /* 0x014E */ u8 state; + /* 0x0150 */ s16 timers[CLEAR_TAG_TIMER_COUNT]; + /* 0x0158 */ Vec3f targetPosition; + /* 0x0164 */ Vec3f targetDirection; + /* 0x0170 */ Vec3f acceleration; + /* 0x017C */ u8 frameCounter; + /* 0x017D */ u8 shouldShootLaser; + /* 0x0180 */ f32 roll; + /* 0x0184 */ s16 crashingTimer; + /* 0x0186 */ s16 deathTimer; + /* 0x0188 */ Vec3f floorTangent; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ u8 cutsceneMode; + /* 0x01E2 */ s16 cameraId; + /* 0x01E4 */ Vec3f cutsceneCameraAt; + /* 0x01F0 */ Vec3f cutsceneCameraEye; + /* 0x01FC */ s16 cutsceneTimer; + /* 0x01FE */ char unk_1FE[0x06]; +} EnClearTag; // size = 0x0204 + +typedef struct EnClearTagEffect { + /* 0x0000 */ u8 type; + /* 0x0001 */ u8 random; + /* 0x0004 */ Vec3f position; + /* 0x0010 */ Vec3f velocity; + /* 0x001C */ Vec3f acceleration; + /* 0x0028 */ Color_RGBAf primColor; + /* 0x0038 */ Color_RGBAf envColor; + /* 0x0048 */ s16 bounces; + /* 0x004A */ s16 timer; + /* 0x004C */ f32 scale; + /* 0x0050 */ f32 maxScale; + /* 0x0054 */ f32 rotationY; + /* 0x0058 */ f32 rotationX; + /* 0x005C */ f32 floorHeight; + /* 0x0060 */ Vec3f floorTangent; +} EnClearTagEffect; // size = 0x6C + +#define CLEAR_TAG_EFFECT_MAX_COUNT 100 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c b/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c new file mode 100644 index 000000000..655dfb6e1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.c @@ -0,0 +1,394 @@ +/* + * File: z_en_cow.c + * Overlay: ovl_En_Cow + * Description: Cow + */ + +#include "z_en_cow.h" +#include "objects/object_cow/object_cow.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnCow_Init(Actor* thisx, GlobalContext* globalCtx); +void EnCow_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnCow_Update(Actor* thisx, GlobalContext* globalCtx); +void EnCow_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_809DFE98(Actor* thisx, GlobalContext* globalCtx); +void func_809E0070(Actor* thisx, GlobalContext* globalCtx); + +void func_809DF494(EnCow* this, GlobalContext* globalCtx); +void func_809DF6BC(EnCow* this, GlobalContext* globalCtx); +void func_809DF778(EnCow* this, GlobalContext* globalCtx); +void func_809DF7D8(EnCow* this, GlobalContext* globalCtx); +void func_809DF870(EnCow* this, GlobalContext* globalCtx); +void func_809DF8FC(EnCow* this, GlobalContext* globalCtx); +void func_809DF96C(EnCow* this, GlobalContext* globalCtx); +void func_809DFA84(EnCow* this, GlobalContext* globalCtx); + +const ActorInit En_Cow_InitVars = { + ACTOR_EN_COW, + ACTORCAT_NPC, + FLAGS, + OBJECT_COW, + sizeof(EnCow), + (ActorFunc)EnCow_Init, + (ActorFunc)EnCow_Destroy, + (ActorFunc)EnCow_Update, + (ActorFunc)EnCow_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +static Vec3f D_809E010C = { 0.0f, -1300.0f, 1100.0f }; + +void func_809DEE00(Vec3f* vec, s16 rotY) { + f32 xCalc; + f32 rotCalcTemp; + + rotCalcTemp = Math_CosS(rotY); + xCalc = (Math_SinS(rotY) * vec->z) + (rotCalcTemp * vec->x); + rotCalcTemp = Math_SinS(rotY); + vec->z = (Math_CosS(rotY) * vec->z) + (-rotCalcTemp * vec->x); + vec->x = xCalc; +} + +void func_809DEE9C(EnCow* this) { + Vec3f vec; + + vec.y = 0.0f; + vec.x = 0.0f; + vec.z = 30.0f; + func_809DEE00(&vec, this->actor.shape.rot.y); + this->colliders[0].dim.pos.x = this->actor.world.pos.x + vec.x; + this->colliders[0].dim.pos.y = this->actor.world.pos.y; + this->colliders[0].dim.pos.z = this->actor.world.pos.z + vec.z; + + vec.x = 0.0f; + vec.y = 0.0f; + vec.z = -20.0f; + func_809DEE00(&vec, this->actor.shape.rot.y); + this->colliders[1].dim.pos.x = this->actor.world.pos.x + vec.x; + this->colliders[1].dim.pos.y = this->actor.world.pos.y; + this->colliders[1].dim.pos.z = this->actor.world.pos.z + vec.z; +} + +void func_809DEF94(EnCow* this) { + Vec3f vec; + + VEC_SET(vec, 0.0f, 57.0f, -36.0f); + + func_809DEE00(&vec, this->actor.shape.rot.y); + this->actor.world.pos.x += vec.x; + this->actor.world.pos.y += vec.y; + this->actor.world.pos.z += vec.z; +} + +void EnCow_Init(Actor* thisx, GlobalContext* globalCtx) { + EnCow* this = (EnCow*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 72.0f); + switch (this->actor.params) { + case 0: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCowBodySkel, NULL, this->jointTable, this->morphTable, 6); + Animation_PlayLoop(&this->skelAnime, &gCowBodyChewAnim); + Collider_InitCylinder(globalCtx, &this->colliders[0]); + Collider_SetCylinder(globalCtx, &this->colliders[0], &this->actor, &sCylinderInit); + Collider_InitCylinder(globalCtx, &this->colliders[1]); + Collider_SetCylinder(globalCtx, &this->colliders[1], &this->actor, &sCylinderInit); + func_809DEE9C(this); + this->actionFunc = func_809DF96C; + if (globalCtx->sceneNum == SCENE_LINK_HOME) { + if (!LINK_IS_ADULT) { + Actor_Kill(&this->actor); + return; + } + if (!(gSaveContext.eventChkInf[1] & 0x4000)) { + Actor_Kill(&this->actor); + return; + } + } + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_COW, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, 1); + this->unk_278 = Rand_ZeroFloat(1000.0f) + 40.0f; + this->unk_27A = 0; + this->actor.targetMode = 6; + DREG(53) = 0; + break; + case 1: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCowTailSkel, NULL, this->jointTable, this->morphTable, 6); + Animation_PlayLoop(&this->skelAnime, &gCowTailIdleAnim); + this->actor.update = func_809DFE98; + this->actor.draw = func_809E0070; + this->actionFunc = func_809DFA84; + func_809DEF94(this); + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_278 = ((u32)(Rand_ZeroFloat(1000.0f)) & 0xFFFF) + 40.0f; + break; + } + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + this->unk_276 = 0; +} + +void EnCow_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnCow* this = (EnCow*)thisx; + + if (this->actor.params == 0) { + Collider_DestroyCylinder(globalCtx, &this->colliders[0]); + Collider_DestroyCylinder(globalCtx, &this->colliders[1]); + } +} + +void func_809DF494(EnCow* this, GlobalContext* globalCtx) { + if (this->unk_278 > 0) { + this->unk_278 -= 1; + } else { + this->unk_278 = Rand_ZeroFloat(500.0f) + 40.0f; + Animation_Change(&this->skelAnime, &gCowBodyChewAnim, 1.0f, this->skelAnime.curFrame, + Animation_GetLastFrame(&gCowBodyChewAnim), ANIMMODE_ONCE, 1.0f); + } + + if ((this->actor.xzDistToPlayer < 150.0f) && (!(this->unk_276 & 2))) { + this->unk_276 |= 2; + if (this->skelAnime.animation == &gCowBodyChewAnim) { + this->unk_278 = 0; + } + } + + this->unk_27A += 1; + if (this->unk_27A >= 0x31) { + this->unk_27A = 0; + } + + // (1.0f / 100.0f) instead of 0.01f below is necessary so 0.01f doesn't get reused mistakenly + if (this->unk_27A < 0x20) { + this->actor.scale.x = ((Math_SinS(this->unk_27A << 0xA) * (1.0f / 100.0f)) + 1.0f) * 0.01f; + } else { + this->actor.scale.x = 0.01f; + } + + if (this->unk_27A >= 0x11) { + this->actor.scale.y = ((Math_SinS((this->unk_27A << 0xA) - 0x4000) * (1.0f / 100.0f)) + 1.0f) * 0.01f; + } else { + this->actor.scale.y = 0.01f; + } +} + +void func_809DF6BC(EnCow* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + Message_CloseTextbox(globalCtx); + this->actionFunc = func_809DF96C; + } +} + +void func_809DF730(EnCow* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = func_809DF96C; + } +} + +void func_809DF778(EnCow* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_809DF730; + } else { + func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 100.0f); + } +} + +void func_809DF7D8(EnCow* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + Message_CloseTextbox(globalCtx); + this->actionFunc = func_809DF778; + func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 100.0f); + } +} + +void func_809DF870(EnCow* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + if (Inventory_HasEmptyBottle()) { + Message_ContinueTextbox(globalCtx, 0x2007); + this->actionFunc = func_809DF7D8; + } else { + Message_ContinueTextbox(globalCtx, 0x2013); + this->actionFunc = func_809DF6BC; + } + } +} + +void func_809DF8FC(EnCow* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_809DF870; + } else { + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2CC(&this->actor, globalCtx, 170.0f); + this->actor.textId = 0x2006; + } + func_809DF494(this, globalCtx); +} + +void func_809DF96C(EnCow* this, GlobalContext* globalCtx) { + if ((globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_00) || (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04)) { + if (DREG(53) != 0) { + if (this->unk_276 & 4) { + this->unk_276 &= ~0x4; + DREG(53) = 0; + } else { + if ((this->actor.xzDistToPlayer < 150.0f) && + (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x61A8)) { + DREG(53) = 0; + this->actionFunc = func_809DF8FC; + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2CC(&this->actor, globalCtx, 170.0f); + this->actor.textId = 0x2006; + } else { + this->unk_276 |= 4; + } + } + } else { + this->unk_276 &= ~0x4; + } + } + func_809DF494(this, globalCtx); +} + +void func_809DFA84(EnCow* this, GlobalContext* globalCtx) { + if (this->unk_278 > 0) { + this->unk_278--; + } else { + this->unk_278 = Rand_ZeroFloat(200.0f) + 40.0f; + Animation_Change(&this->skelAnime, &gCowTailIdleAnim, 1.0f, this->skelAnime.curFrame, + Animation_GetLastFrame(&gCowTailIdleAnim), ANIMMODE_ONCE, 1.0f); + } + + if ((this->actor.xzDistToPlayer < 150.0f) && + (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) >= 0x61A9) && (!(this->unk_276 & 2))) { + this->unk_276 |= 2; + if (this->skelAnime.animation == &gCowTailIdleAnim) { + this->unk_278 = 0; + } + } +} + +void EnCow_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnCow* this = (EnCow*)thisx; + GlobalContext* globalCtx = globalCtx2; + s16 targetX; + s16 targetY; + Player* player = GET_PLAYER(globalCtx); + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliders[0].base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliders[1].base); + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 0.0f, 0.0f, 0.0f, 4); + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->skelAnime.animation == &gCowBodyChewAnim) { + Audio_PlayActorSound2(thisx, NA_SE_EV_COW_CRY); + Animation_Change(&this->skelAnime, &gCowBodyMoveHeadAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gCowBodyMoveHeadAnim), ANIMMODE_ONCE, 1.0f); + } else { + Animation_Change(&this->skelAnime, &gCowBodyChewAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gCowBodyChewAnim), + ANIMMODE_LOOP, 1.0f); + } + } + this->actionFunc(this, globalCtx); + if ((thisx->xzDistToPlayer < 150.0f) && + (ABS(Math_Vec3f_Yaw(&thisx->world.pos, &player->actor.world.pos)) < 0xC000)) { + targetX = Math_Vec3f_Pitch(&thisx->focus.pos, &player->actor.focus.pos); + targetY = Math_Vec3f_Yaw(&thisx->focus.pos, &player->actor.focus.pos) - thisx->shape.rot.y; + + if (targetX > 0x1000) { + targetX = 0x1000; + } else if (targetX < -0x1000) { + targetX = -0x1000; + } + + if (targetY > 0x2500) { + targetY = 0x2500; + } else if (targetY < -0x2500) { + targetY = -0x2500; + } + + } else { + targetY = 0; + targetX = 0; + } + Math_SmoothStepToS(&this->someRot.x, targetX, 0xA, 0xC8, 0xA); + Math_SmoothStepToS(&this->someRot.y, targetY, 0xA, 0xC8, 0xA); +} + +void func_809DFE98(Actor* thisx, GlobalContext* globalCtx) { + EnCow* this = (EnCow*)thisx; + s32 pad; + + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->skelAnime.animation == &gCowTailIdleAnim) { + Animation_Change(&this->skelAnime, &gCowTailSwishAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gCowTailSwishAnim), ANIMMODE_ONCE, 1.0f); + } else { + Animation_Change(&this->skelAnime, &gCowTailIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gCowTailIdleAnim), + ANIMMODE_LOOP, 1.0f); + } + } + this->actionFunc(this, globalCtx); +} + +s32 EnCow_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnCow* this = (EnCow*)thisx; + + if (limbIndex == 2) { + rot->y += this->someRot.y; + rot->x += this->someRot.x; + } + if (limbIndex == 5) { + *dList = NULL; + } + return false; +} + +void EnCow_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnCow* this = (EnCow*)thisx; + + if (limbIndex == 2) { + Matrix_MultVec3f(&D_809E010C, &this->actor.focus.pos); + } +} + +void EnCow_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnCow* this = (EnCow*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnCow_OverrideLimbDraw, EnCow_PostLimbDraw, this); +} + +void func_809E0070(Actor* thisx, GlobalContext* globalCtx) { + EnCow* this = (EnCow*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.h b/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.h new file mode 100644 index 000000000..ccb165bf7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Cow/z_en_cow.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_COW_H +#define Z_EN_COW_H + +#include "ultra64.h" +#include "global.h" + +struct EnCow; + +typedef void (*EnCowActionFunc)(struct EnCow*, GlobalContext*); + +typedef struct EnCow { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder colliders[2]; + /* 0x01E4 */ SkelAnime skelAnime; + /* 0x0228 */ Vec3s jointTable[6]; + /* 0x024C */ Vec3s morphTable[6]; + /* 0x0270 */ Vec3s someRot; + /* 0x0276 */ u16 unk_276; + /* 0x0278 */ u16 unk_278; + /* 0x027A */ u16 unk_27A; + /* 0x027C */ EnCowActionFunc actionFunc; +} EnCow; // size = 0x0280 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c new file mode 100644 index 000000000..fb4088156 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.c @@ -0,0 +1,510 @@ +#include "z_en_crow.h" +#include "objects/object_crow/object_crow.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_12 | ACTOR_FLAG_14) + +void EnCrow_Init(Actor* thisx, GlobalContext* globalCtx); +void EnCrow_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnCrow_Update(Actor* thisx, GlobalContext* globalCtx); +void EnCrow_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnCrow_SetupFlyIdle(EnCrow* this); +void EnCrow_FlyIdle(EnCrow* this, GlobalContext* globalCtx); +void EnCrow_Respawn(EnCrow* this, GlobalContext* globalCtx); +void EnCrow_DiveAttack(EnCrow* this, GlobalContext* globalCtx); +void EnCrow_Die(EnCrow* this, GlobalContext* globalCtx); +void EnCrow_TurnAway(EnCrow* this, GlobalContext* globalCtx); +void EnCrow_Damaged(EnCrow* this, GlobalContext* globalCtx); + +static Vec3f sZeroVecAccel = { 0.0f, 0.0f, 0.0f }; + +const ActorInit En_Crow_InitVars = { + ACTOR_EN_CROW, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_CROW, + sizeof(EnCrow), + (ActorFunc)EnCrow_Init, + (ActorFunc)EnCrow_Destroy, + (ActorFunc)EnCrow_Update, + (ActorFunc)EnCrow_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 15, 30, 30 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0x3), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(4, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static u32 sDeathCount = 0; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 3000, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x58, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -200, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_STOP), +}; + +static Vec3f sHeadVec = { 2500.0f, 0.0f, 0.0f }; + +void EnCrow_Init(Actor* thisx, GlobalContext* globalCtx) { + EnCrow* this = (EnCrow*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGuaySkel, &gGuayFlyAnim, this->jointTable, this->morphTable, 9); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + this->collider.elements[0].dim.worldSphere.radius = sJntSphInit.elements[0].dim.modelSphere.radius; + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + ActorShape_Init(&this->actor.shape, 2000.0f, ActorShadow_DrawCircle, 20.0f); + sDeathCount = 0; + EnCrow_SetupFlyIdle(this); +} + +void EnCrow_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnCrow* this = (EnCrow*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +// Setup Action functions + +void EnCrow_SetupFlyIdle(EnCrow* this) { + this->timer = 100; + this->collider.base.acFlags |= AC_ON; + this->actionFunc = EnCrow_FlyIdle; + this->skelAnime.playSpeed = 1.0f; +} + +void EnCrow_SetupDiveAttack(EnCrow* this) { + this->timer = 300; + this->actor.speedXZ = 4.0f; + this->skelAnime.playSpeed = 2.0f; + this->actionFunc = EnCrow_DiveAttack; +} + +void EnCrow_SetupDamaged(EnCrow* this, GlobalContext* globalCtx) { + s32 i; + f32 scale; + Vec3f iceParticlePos; + + this->actor.speedXZ *= Math_CosS(this->actor.world.rot.x); + this->actor.velocity.y = 0.0f; + Animation_Change(&this->skelAnime, &gGuayFlyAnim, 0.4f, 0.0f, 0.0f, ANIMMODE_LOOP_INTERP, -3.0f); + scale = this->actor.scale.x * 100.0f; + this->actor.world.pos.y += 20.0f * scale; + this->actor.bgCheckFlags &= ~1; + this->actor.shape.yOffset = 0.0f; + this->actor.targetArrowOffset = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_DEAD); + + if (this->actor.colChkInfo.damageEffect == 3) { // Ice arrows + Actor_SetColorFilter(&this->actor, 0, 255, 0, 40); + for (i = 0; i < 8; i++) { + iceParticlePos.x = ((i & 1 ? 7.0f : -7.0f) * scale) + this->actor.world.pos.x; + iceParticlePos.y = ((i & 2 ? 7.0f : -7.0f) * scale) + this->actor.world.pos.y; + iceParticlePos.z = ((i & 4 ? 7.0f : -7.0f) * scale) + this->actor.world.pos.z; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &iceParticlePos, 150, 150, 150, 250, 235, 245, 255, + ((Rand_ZeroOne() * 0.15f) + 0.85f) * scale); + } + } else if (this->actor.colChkInfo.damageEffect == 2) { // Fire arrows and Din's Fire + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 40); + + for (i = 0; i < 4; i++) { + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &this->actor.world.pos, 50.0f * scale, 0, 0, i); + } + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 40); + } + + if (this->actor.flags & ACTOR_FLAG_15) { + this->actor.speedXZ = 0.0f; + } + + this->collider.base.acFlags &= ~AC_ON; + this->actor.flags |= ACTOR_FLAG_4; + + this->actionFunc = EnCrow_Damaged; +} + +void EnCrow_SetupDie(EnCrow* this) { + this->actor.colorFilterTimer = 0; + this->actionFunc = EnCrow_Die; +} + +void EnCrow_SetupTurnAway(EnCrow* this) { + this->timer = 100; + this->actor.speedXZ = 3.5f; + this->aimRotX = -0x1000; + this->aimRotY = this->actor.yawTowardsPlayer + 0x8000; + this->skelAnime.playSpeed = 2.0f; + Actor_SetColorFilter(&this->actor, 0, 255, 0, 5); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actionFunc = EnCrow_TurnAway; +} + +void EnCrow_SetupRespawn(EnCrow* this) { + if (sDeathCount == 10) { + this->actor.params = 1; + sDeathCount = 0; + this->collider.elements[0].dim.worldSphere.radius = + sJntSphInit.elements[0].dim.modelSphere.radius * 0.03f * 100.0f; + } else { + this->actor.params = 0; + this->collider.elements[0].dim.worldSphere.radius = sJntSphInit.elements[0].dim.modelSphere.radius; + } + + Animation_PlayLoop(&this->skelAnime, &gGuayFlyAnim); + Math_Vec3f_Copy(&this->actor.world.pos, &this->actor.home.pos); + this->actor.shape.rot.x = 0; + this->actor.shape.rot.z = 0; + this->timer = 300; + this->actor.shape.yOffset = 2000; + this->actor.targetArrowOffset = 2000.0f; + this->actor.draw = NULL; + this->actionFunc = EnCrow_Respawn; +} + +// Action functions + +void EnCrow_FlyIdle(EnCrow* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 skelanimeUpdated; + s16 var; + + SkelAnime_Update(&this->skelAnime); + skelanimeUpdated = Animation_OnFrame(&this->skelAnime, 0.0f); + this->actor.speedXZ = (Rand_ZeroOne() * 1.5f) + 3.0f; + + if (this->actor.bgCheckFlags & 8) { + this->aimRotY = this->actor.wallYaw; + } else if (Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) > 300.0f) { + this->aimRotY = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + } + + if ((Math_SmoothStepToS(&this->actor.shape.rot.y, this->aimRotY, 5, 0x300, 0x10) == 0) && skelanimeUpdated && + (Rand_ZeroOne() < 0.1f)) { + var = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos) - this->actor.shape.rot.y; + if (var > 0) { + this->aimRotY += 0x1000 + (0x1000 * Rand_ZeroOne()); + } else { + this->aimRotY -= 0x1000 + (0x1000 * Rand_ZeroOne()); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_CRY); + } + + if (this->actor.yDistToWater > -40.0f) { + this->aimRotX = -0x1000; + } else if (this->actor.world.pos.y < (this->actor.home.pos.y - 50.0f)) { + this->aimRotX = -0x800 - (Rand_ZeroOne() * 0x800); + } else if (this->actor.world.pos.y > (this->actor.home.pos.y + 50.0f)) { + this->aimRotX = 0x800 + (Rand_ZeroOne() * 0x800); + } + + if ((Math_SmoothStepToS(&this->actor.shape.rot.x, this->aimRotX, 10, 0x100, 8) == 0) && (skelanimeUpdated) && + (Rand_ZeroOne() < 0.1f)) { + if (this->actor.home.pos.y < this->actor.world.pos.y) { + this->aimRotX -= (0x400 * Rand_ZeroOne()) + 0x400; + } else { + this->aimRotX += (0x400 * Rand_ZeroOne()) + 0x400; + } + this->aimRotX = CLAMP(this->aimRotX, -0x1000, 0x1000); + } + + if (this->actor.bgCheckFlags & 1) { + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x100, 0x400); + } + + if (this->timer != 0) { + this->timer--; + } + if ((this->timer == 0) && (this->actor.xzDistToPlayer < 300.0f) && !(player->stateFlags1 & 0x00800000) && + (this->actor.yDistToWater < -40.0f) && (Player_GetMask(globalCtx) != PLAYER_MASK_SKULL)) { + EnCrow_SetupDiveAttack(this); + } +} + +void EnCrow_DiveAttack(EnCrow* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 facingPlayer; + Vec3f pos; + s16 target; + + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + facingPlayer = Actor_IsFacingPlayer(&this->actor, 0x2800); + + if (facingPlayer) { + pos.x = player->actor.world.pos.x; + pos.y = player->actor.world.pos.y + 20.0f; + pos.z = player->actor.world.pos.z; + target = Actor_WorldPitchTowardPoint(&this->actor, &pos); + if (target > 0x3000) { + target = 0x3000; + } + Math_ApproachS(&this->actor.shape.rot.x, target, 2, 0x400); + } else { + Math_ApproachS(&this->actor.shape.rot.x, -0x1000, 2, 0x100); + } + + if (facingPlayer || (this->actor.xzDistToPlayer > 80.0f)) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 4, 0xC00); + } + + if ((this->timer == 0) || (Player_GetMask(globalCtx) == PLAYER_MASK_SKULL) || + (this->collider.base.atFlags & AT_HIT) || (this->actor.bgCheckFlags & 9) || + (player->stateFlags1 & 0x00800000) || (this->actor.yDistToWater > -40.0f)) { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_ATTACK); + } + + EnCrow_SetupFlyIdle(this); + } +} + +void EnCrow_Damaged(EnCrow* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + this->actor.colorFilterTimer = 40; + + if (!(this->actor.flags & ACTOR_FLAG_15)) { + if (this->actor.colorFilterParams & 0x4000) { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x4000, 0x200); + this->actor.shape.rot.z += 0x1780; + } + if ((this->actor.bgCheckFlags & 1) || (this->actor.floorHeight == BGCHECK_Y_MIN)) { + EffectSsDeadDb_Spawn(globalCtx, &this->actor.world.pos, &sZeroVecAccel, &sZeroVecAccel, + this->actor.scale.x * 10000.0f, 0, 255, 255, 255, 255, 255, 0, 0, 1, 9, 1); + EnCrow_SetupDie(this); + } + } +} + +void EnCrow_Die(EnCrow* this, GlobalContext* globalCtx) { + f32 step; + + if (this->actor.params != 0) { + step = 0.006f; + } else { + step = 0.002f; + } + + if (Math_StepToF(&this->actor.scale.x, 0.0f, step)) { + if (this->actor.params == 0) { + sDeathCount++; + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0); + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_RUPEE_RED); + } + EnCrow_SetupRespawn(this); + } + + this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; +} + +void EnCrow_TurnAway(EnCrow* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->actor.bgCheckFlags & 8) { + this->aimRotY = this->actor.wallYaw; + } else { + this->aimRotY = this->actor.yawTowardsPlayer + 0x8000; + } + + Math_ApproachS(&this->actor.shape.rot.y, this->aimRotY, 3, 0xC00); + Math_ApproachS(&this->actor.shape.rot.x, this->aimRotX, 5, 0x100); + + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + EnCrow_SetupFlyIdle(this); + } +} + +void EnCrow_Respawn(EnCrow* this, GlobalContext* globalCtx) { + f32 target; + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + SkelAnime_Update(&this->skelAnime); + this->actor.draw = EnCrow_Draw; + if (this->actor.params != 0) { + target = 0.03f; + } else { + target = 0.01f; + } + if (Math_StepToF(&this->actor.scale.x, target, target * 0.1f)) { + this->actor.flags |= ACTOR_FLAG_0; + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.colChkInfo.health = 1; + EnCrow_SetupFlyIdle(this); + } + this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; + } +} + +void EnCrow_UpdateDamage(EnCrow* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 1); + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (this->actor.colChkInfo.damageEffect == 1) { // Deku Nuts + EnCrow_SetupTurnAway(this); + } else { + Actor_ApplyDamage(&this->actor); + this->actor.flags &= ~ACTOR_FLAG_0; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + EnCrow_SetupDamaged(this, globalCtx); + } + } + } +} + +void EnCrow_Update(Actor* thisx, GlobalContext* globalCtx) { + EnCrow* this = (EnCrow*)thisx; + f32 pad; + f32 height; + f32 scale; + + EnCrow_UpdateDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + scale = this->actor.scale.x * 100.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.world.rot.x = -this->actor.shape.rot.x; + + if (this->actionFunc != EnCrow_Respawn) { + if (this->actor.colChkInfo.health != 0) { + height = 20.0f * scale; + func_8002D97C(&this->actor); + } else { + height = 0.0f; + Actor_MoveForward(&this->actor); + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 12.0f * scale, 25.0f * scale, 50.0f * scale, 7); + } else { + height = 0.0f; + } + + this->collider.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x; + this->collider.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y + height; + this->collider.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z; + + if (this->actionFunc == EnCrow_DiveAttack) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->actionFunc != EnCrow_Respawn) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + Actor_SetFocus(&this->actor, height); + + if (this->actor.colChkInfo.health != 0 && Animation_OnFrame(&this->skelAnime, 3.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_KAICHO_FLUTTER); + } +} + +s32 EnCrow_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnCrow* this = (EnCrow*)thisx; + + if (this->actor.colChkInfo.health != 0) { + if (limbIndex == 7) { + rot->y += 0xC00 * sinf(this->skelAnime.curFrame * (M_PI / 4)); + } else if (limbIndex == 8) { + rot->y += 0x1400 * sinf((this->skelAnime.curFrame + 2.5f) * (M_PI / 4)); + } + } + return false; +} + +void EnCrow_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnCrow* this = (EnCrow*)thisx; + Vec3f* vec; + + if (limbIndex == 2) { + Matrix_MultVec3f(&sHeadVec, &this->bodyPartsPos[0]); + this->bodyPartsPos[0].y -= 20.0f; + } else if ((limbIndex == 4) || (limbIndex == 6) || (limbIndex == 8)) { + vec = &this->bodyPartsPos[(limbIndex >> 1) - 1]; + Matrix_MultVec3f(&sZeroVecAccel, vec); + vec->y -= 20.0f; + } +} + +void EnCrow_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnCrow* this = (EnCrow*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnCrow_OverrideLimbDraw, EnCrow_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.h b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.h new file mode 100644 index 000000000..59447f9ce --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Crow/z_en_crow.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_CROW_H +#define Z_EN_CROW_H + +#include "ultra64.h" +#include "global.h" + +struct EnCrow; + +typedef void (*EnCrowActionFunc)(struct EnCrow*, GlobalContext*); + +typedef struct EnCrow { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3f bodyPartsPos[4]; + /* 0x017C */ SkelAnime skelAnime; + /* 0x01C0 */ EnCrowActionFunc actionFunc; + /* 0x01C4 */ s16 timer; + /* 0x01C6 */ s16 aimRotX; + /* 0x01C8 */ s16 aimRotY; + /* 0x01CA */ Vec3s jointTable[9]; + /* 0x0200 */ Vec3s morphTable[9]; + /* 0x0238 */ ColliderJntSph collider; + /* 0x0258 */ ColliderJntSphElement colliderItems[1]; +} EnCrow; // size = 0x0298 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c b/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c new file mode 100644 index 000000000..cace48e3f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.c @@ -0,0 +1,523 @@ +#include "z_en_cs.h" +#include "objects/object_cs/object_cs.h" +#include "objects/object_link_child/object_link_child.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnCs_Init(Actor* thisx, GlobalContext* globalCtx); +void EnCs_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnCs_Update(Actor* thisx, GlobalContext* globalCtx); +void EnCs_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnCs_Walk(EnCs* this, GlobalContext* globalCtx); +void EnCs_Talk(EnCs* this, GlobalContext* globalCtx); +void EnCs_Wait(EnCs* this, GlobalContext* globalCtx); +s32 EnCs_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx); +void EnCs_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx); + +const ActorInit En_Cs_InitVars = { + ACTOR_EN_CS, + ACTORCAT_NPC, + FLAGS, + OBJECT_CS, + sizeof(EnCs), + (ActorFunc)EnCs_Init, + (ActorFunc)EnCs_Destroy, + (ActorFunc)EnCs_Update, + (ActorFunc)EnCs_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 63, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit2 = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable[] = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +typedef enum { + /* 0 */ ENCS_ANIM_0, + /* 1 */ ENCS_ANIM_1, + /* 2 */ ENCS_ANIM_2, + /* 3 */ ENCS_ANIM_3 +} EnCsAnimation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &gGraveyardKidWalkAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gGraveyardKidSwingStickUpAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gGraveyardKidGrabStickTwoHandsAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gGraveyardKidIdleAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, +}; + +void EnCs_ChangeAnim(EnCs* this, s32 index, s32* currentIndex) { + f32 morphFrames; + + if ((*currentIndex < 0) || (index == *currentIndex)) { + morphFrames = 0.0f; + } else { + morphFrames = sAnimationInfo[index].morphFrames; + } + + if (sAnimationInfo[index].frameCount >= 0.0f) { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, sAnimationInfo[index].frameCount, 0.0f, + Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, + morphFrames); + } else { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, sAnimationInfo[index].frameCount, + Animation_GetLastFrame(sAnimationInfo[index].animation), 0.0f, sAnimationInfo[index].mode, + morphFrames); + } + + *currentIndex = index; +} + +void EnCs_Init(Actor* thisx, GlobalContext* globalCtx) { + EnCs* this = (EnCs*)thisx; + s32 pad; + + if (!IS_DAY) { + Actor_Kill(&this->actor); + return; + } + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 19.0f); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGraveyardKidSkel, NULL, this->jointTable, this->morphTable, 16); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + CollisionCheck_SetInfo2(&this->actor.colChkInfo, sDamageTable, &sColChkInfoInit2); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + Animation_Change(&this->skelAnime, sAnimationInfo[ENCS_ANIM_0].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[ENCS_ANIM_0].animation), sAnimationInfo[ENCS_ANIM_0].mode, + sAnimationInfo[ENCS_ANIM_0].morphFrames); + + this->actor.targetMode = 6; + this->path = this->actor.params & 0xFF; + this->unk_1EC = 0; // This variable is unused anywhere else + this->talkState = 0; + this->currentAnimIndex = -1; + this->actor.gravity = -1.0f; + + EnCs_ChangeAnim(this, ENCS_ANIM_0, &this->currentAnimIndex); + + this->actionFunc = EnCs_Walk; + this->walkSpeed = 1.0f; +} + +void EnCs_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnCs* this = (EnCs*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnCs_GetTalkState(EnCs* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + s32 talkState = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + this->actor.textId = 0x2026; + EnCs_ChangeAnim(this, ENCS_ANIM_3, &this->currentAnimIndex); + talkState = 2; + } else { + this->actor.textId = 0x2024; + EnCs_ChangeAnim(this, ENCS_ANIM_1, &this->currentAnimIndex); + talkState = 2; + } + } + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + if (this->actor.textId == 0x2026) { + Player_UnsetMask(globalCtx); + Item_Give(globalCtx, ITEM_SOLD_OUT); + gSaveContext.itemGetInf[3] |= 0x400; + Rupees_ChangeBy(30); + this->actor.textId = 0x2027; + talkState = 2; + } else { + talkState = 0; + } + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_CLOSING: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_EVENT: + break; + } + + return talkState; +} + +s32 EnCs_GetTextID(EnCs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 textId = Text_GetFaceReaction(globalCtx, 15); + + if (gSaveContext.itemGetInf[3] & 0x400) { + if (textId == 0) { + textId = 0x2028; + } + } else if (player->currentMask == PLAYER_MASK_SPOOKY) { + textId = 0x2023; + } else { + if (textId == 0) { + textId = 0x2022; + } + } + + return textId; +} + +void EnCs_HandleTalking(EnCs* this, GlobalContext* globalCtx) { + s32 pad; + s16 sp2A; + s16 sp28; + + if (this->talkState == 2) { + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->talkState = 1; + } else if (this->talkState == 1) { + this->talkState = EnCs_GetTalkState(this, globalCtx); + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if ((this->actor.textId == 0x2022) || ((this->actor.textId != 0x2022) && (this->actor.textId != 0x2028))) { + EnCs_ChangeAnim(this, ENCS_ANIM_3, &this->currentAnimIndex); + } + + if ((this->actor.textId == 0x2023) || (this->actor.textId == 0x2028)) { + EnCs_ChangeAnim(this, ENCS_ANIM_1, &this->currentAnimIndex); + } + + if (this->actor.textId == 0x2023) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + + this->talkState = 1; + } else { + Actor_GetScreenPos(globalCtx, &this->actor, &sp2A, &sp28); + + if ((sp2A >= 0) && (sp2A <= 320) && (sp28 >= 0) && (sp28 <= 240) && + (func_8002F2CC(&this->actor, globalCtx, 100.0f))) { + this->actor.textId = EnCs_GetTextID(this, globalCtx); + } + } +} + +s32 EnCs_GetwaypointCount(Path* pathList, s32 pathIndex) { + Path* path = &pathList[pathIndex]; + + return path->count; +} + +s32 EnCs_GetPathPoint(Path* pathList, Vec3f* dest, s32 pathIndex, s32 waypoint) { + Path* path = pathList; + Vec3s* pathPos; + + path += pathIndex; + pathPos = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[waypoint]; + + dest->x = pathPos->x; + dest->y = pathPos->y; + dest->z = pathPos->z; + + return 0; +} + +s32 EnCs_HandleWalking(EnCs* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 zDiff; + Vec3f pathPos; + s32 waypointCount; + s16 walkAngle1; + s16 walkAngle2; + + EnCs_GetPathPoint(globalCtx->setupPathList, &pathPos, this->path, this->waypoint); + xDiff = pathPos.x - this->actor.world.pos.x; + zDiff = pathPos.z - this->actor.world.pos.z; + walkAngle1 = Math_FAtan2F(xDiff, zDiff) * (32768.0f / M_PI); + this->walkAngle = walkAngle1; + this->walkDist = sqrtf((xDiff * xDiff) + (zDiff * zDiff)); + + while (this->walkDist <= 10.44f) { + this->waypoint++; + waypointCount = EnCs_GetwaypointCount(globalCtx->setupPathList, this->path); + + if ((this->waypoint < 0) || (!(this->waypoint < waypointCount))) { + this->waypoint = 0; + } + + EnCs_GetPathPoint(globalCtx->setupPathList, &pathPos, this->path, this->waypoint); + xDiff = pathPos.x - this->actor.world.pos.x; + zDiff = pathPos.z - this->actor.world.pos.z; + walkAngle2 = Math_FAtan2F(xDiff, zDiff) * (32768.0f / M_PI); + this->walkAngle = walkAngle2; + this->walkDist = sqrtf((xDiff * xDiff) + (zDiff * zDiff)); + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->walkAngle, 1, 2500, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.speedXZ = this->walkSpeed; + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + return 0; +} + +void EnCs_Walk(EnCs* this, GlobalContext* globalCtx) { + s32 rnd; + s32 animIndex; + s32 curAnimFrame; + + if (this->talkState != 0) { + this->actionFunc = EnCs_Talk; + return; + } + + if (SkelAnime_Update(&this->skelAnime)) { + animIndex = this->currentAnimIndex; + + if (this->talkState == 0) { + if (gSaveContext.itemGetInf[3] & 0x400) { + rnd = Rand_ZeroOne() * 10.0f; + } else { + rnd = Rand_ZeroOne() * 5.0f; + } + + if (rnd == 0) { + if (gSaveContext.itemGetInf[3] & 0x400) { + animIndex = 2.0f * Rand_ZeroOne(); + animIndex = (animIndex == 0) ? ENCS_ANIM_2 : ENCS_ANIM_1; + } else { + animIndex = ENCS_ANIM_2; + } + + this->actionFunc = EnCs_Wait; + } else { + animIndex = ENCS_ANIM_0; + } + } + + EnCs_ChangeAnim(this, animIndex, &this->currentAnimIndex); + } + + if (this->talkState == 0) { + curAnimFrame = this->skelAnime.curFrame; + + if (((curAnimFrame >= 8) && (curAnimFrame < 16)) || ((curAnimFrame >= 23) && (curAnimFrame < 30)) || + (curAnimFrame == 0)) { + this->walkSpeed = 0.0f; + } else { + this->walkSpeed = 1.0f; + } + + EnCs_HandleWalking(this, globalCtx); + } +} + +void EnCs_Wait(EnCs* this, GlobalContext* globalCtx) { + s32 animIndex; + + if (this->talkState != 0) { + this->actionFunc = EnCs_Talk; + return; + } + + if (SkelAnime_Update(&this->skelAnime)) { + animIndex = this->currentAnimIndex; + + if (this->talkState == 0) { + if (this->animLoopCount > 0) { + this->animLoopCount--; + animIndex = this->currentAnimIndex; + } else { + animIndex = ENCS_ANIM_0; + this->actionFunc = EnCs_Walk; + } + } + + EnCs_ChangeAnim(this, animIndex, &this->currentAnimIndex); + } +} + +void EnCs_Talk(EnCs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnCs_ChangeAnim(this, this->currentAnimIndex, &this->currentAnimIndex); + } + + this->flag |= 1; + this->npcInfo.unk_18.x = player->actor.focus.pos.x; + this->npcInfo.unk_18.y = player->actor.focus.pos.y; + this->npcInfo.unk_18.z = player->actor.focus.pos.z; + func_80034A14(&this->actor, &this->npcInfo, 0, 4); + + if (this->talkState == 0) { + EnCs_ChangeAnim(this, ENCS_ANIM_0, &this->currentAnimIndex); + this->actionFunc = EnCs_Walk; + this->flag &= ~1; + } +} + +void EnCs_Update(Actor* thisx, GlobalContext* globalCtx) { + static s32 eyeBlinkFrames[] = { 70, 1, 1 }; + EnCs* this = (EnCs*)thisx; + s32 pad; + + if (this->currentAnimIndex == 0) { + if (((s32)this->skelAnime.curFrame == 9) || ((s32)this->skelAnime.curFrame == 23)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHIBI_WALK); + } + } else if (this->currentAnimIndex == 1) { + if (((s32)this->skelAnime.curFrame == 10) || ((s32)this->skelAnime.curFrame == 25)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHIBI_WALK); + } + } else if ((this->currentAnimIndex == 2) && ((s32)this->skelAnime.curFrame == 20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHIBI_WALK); + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + this->actionFunc(this, globalCtx); + + EnCs_HandleTalking(this, globalCtx); + + this->eyeBlinkTimer--; + + if (this->eyeBlinkTimer < 0) { + this->eyeIndex++; + + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + } + + this->eyeBlinkTimer = eyeBlinkFrames[this->eyeIndex]; + } +} + +void EnCs_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gGraveyardKidEyesOpenTex, + gGraveyardKidEyesHalfTex, + gGraveyardKidEyesClosedTex, + }; + EnCs* this = (EnCs*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_cs.c", 968); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnCs_OverrideLimbDraw, EnCs_PostLimbDraw, &this->actor); + + if (gSaveContext.itemGetInf[3] & 0x400) { + s32 childLinkObjectIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_LINK_CHILD); + + // Handle attaching the Spooky Mask to the boy's face + if (childLinkObjectIndex >= 0) { + Mtx* mtx; + + Matrix_Put(&this->spookyMaskMtx); + mtx = Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_cs.c", 1000); + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[childLinkObjectIndex].segment); + gSPSegment(POLY_OPA_DISP++, 0x0D, mtx - 7); + gSPDisplayList(POLY_OPA_DISP++, gLinkChildSpookyMaskDL); + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_cs.c", 1015); +} + +s32 EnCs_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnCs* this = (EnCs*)thisx; + + if (this->flag & 1) { + switch (limbIndex) { + case 8: + rot->x += this->npcInfo.unk_0E.y; + rot->y -= this->npcInfo.unk_0E.x; + break; + case 15: + rot->x += this->npcInfo.unk_08.y; + rot->z += this->npcInfo.unk_08.x; + break; + } + } + + return 0; +} + +void EnCs_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_809E2970 = { 500.0f, 800.0f, 0.0f }; + EnCs* this = (EnCs*)thisx; + + if (limbIndex == 15) { + Matrix_MultVec3f(&D_809E2970, &this->actor.focus.pos); + Matrix_Translate(0.0f, -200.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateY(0.0f, MTXMODE_APPLY); + Matrix_RotateX(0.0f, MTXMODE_APPLY); + Matrix_RotateZ(5.0 * M_PI / 9.0, MTXMODE_APPLY); + Matrix_Get(&this->spookyMaskMtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.h b/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.h new file mode 100644 index 000000000..6b2b0d109 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Cs/z_en_cs.h @@ -0,0 +1,37 @@ +#ifndef Z_EN_CS_H +#define Z_EN_CS_H + +#include "ultra64.h" +#include "global.h" + +struct EnCs; + +typedef void (*EnCsActionFunc)(struct EnCs*, GlobalContext*); + +typedef struct EnCs { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnCsActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s32 talkState; + /* 0x01E4 */ s32 eyeIndex; + /* 0x01E8 */ s32 eyeBlinkTimer; + /* 0x01EC */ s32 unk_1EC; + /* 0x01F0 */ char unk_1F0[4]; + /* 0x01F4 */ s32 path; + /* 0x01F8 */ char unk_1F8[4]; + /* 0x01FC */ s32 waypoint; + /* 0x0200 */ s32 animLoopCount; // How many time the current animation should loop before changing + /* 0x0204 */ f32 walkAngle; + /* 0x0208 */ f32 walkDist; + /* 0x020C */ f32 walkSpeed; + /* 0x0210 */ s32 currentAnimIndex; + /* 0x0214 */ char unk_214[4]; + /* 0x0218 */ MtxF spookyMaskMtx; + /* 0x0258 */ struct_80034A14_arg1 npcInfo; + /* 0x0280 */ s32 flag; + /* 0x0284 */ Vec3s jointTable[16]; + /* 0x02E4 */ Vec3s morphTable[16]; +} EnCs; // size = 0x0344 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c new file mode 100644 index 000000000..d576e5153 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.c @@ -0,0 +1,643 @@ +#include "z_en_daiku.h" +#include "overlays/actors/ovl_En_GeldB/z_en_geldb.h" +#include "objects/object_daiku/object_daiku.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +typedef struct { + Vec3f eyePosDeltaLocal; + s32 maxFramesActive; +} EnDaikuEscapeSubCamParam; + +// state flags + +// probably related to animating torso and head to look towards the player +#define ENDAIKU_STATEFLAG_1 (1 << 1) +// same +#define ENDAIKU_STATEFLAG_2 (1 << 2) +// the gerudo guard appeared (after talking to the carpenter) +#define ENDAIKU_STATEFLAG_GERUDOFIGHTING (1 << 3) +// the gerudo guard was defeated +#define ENDAIKU_STATEFLAG_GERUDODEFEATED (1 << 4) + +typedef enum { + /* 0 */ ENDAIKU_STATE_CAN_TALK, + /* 2 */ ENDAIKU_STATE_TALKING = 2, + /* 3 */ ENDAIKU_STATE_NO_TALK +} EnDaikuTalkState; + +void EnDaiku_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDaiku_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDaiku_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDaiku_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDaiku_TentIdle(EnDaiku* this, GlobalContext* globalCtx); +void EnDaiku_Jailed(EnDaiku* this, GlobalContext* globalCtx); +void EnDaiku_WaitFreedom(EnDaiku* this, GlobalContext* globalCtx); +void EnDaiku_InitEscape(EnDaiku* this, GlobalContext* globalCtx); +void EnDaiku_EscapeRotate(EnDaiku* this, GlobalContext* globalCtx); +void EnDaiku_InitSubCamera(EnDaiku* this, GlobalContext* globalCtx); +void EnDaiku_EscapeRun(EnDaiku* this, GlobalContext* globalCtx); +s32 EnDaiku_OverrideLimbDraw(GlobalContext* globalCtx, s32 limb, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx); +void EnDaiku_PostLimbDraw(GlobalContext* globalCtx, s32 limb, Gfx** dList, Vec3s* rot, void* thisx); + +const ActorInit En_Daiku_InitVars = { + ACTOR_EN_DAIKU, + ACTORCAT_NPC, + FLAGS, + OBJECT_DAIKU, + sizeof(EnDaiku), + (ActorFunc)EnDaiku_Init, + (ActorFunc)EnDaiku_Destroy, + (ActorFunc)EnDaiku_Update, + (ActorFunc)EnDaiku_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 66, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit2 = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +typedef enum { + /* 0 */ ENDAIKU_ANIM_SHOUT, + /* 1 */ ENDAIKU_ANIM_STAND, + /* 2 */ ENDAIKU_ANIM_CELEBRATE, + /* 3 */ ENDAIKU_ANIM_RUN, + /* 4 */ ENDAIKU_ANIM_SIT +} EnDaikuAnimation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &object_daiku_Anim_001AB0, 1.0f, 0, 0 }, { &object_daiku_Anim_007DE0, 1.0f, 0, 0 }, + { &object_daiku_Anim_00885C, 1.0f, 0, 0 }, { &object_daiku_Anim_000C44, 1.0f, 0, 0 }, + { &object_daiku_Anim_008164, 1.0f, 0, 0 }, +}; + +static EnDaikuEscapeSubCamParam sEscapeSubCamParams[] = { + { { 0, 130, 220 }, 100 }, + { { -20, 22, 280 }, 110 }, + { { 50, 180, 350 }, 100 }, + { { -40, 60, 60 }, 120 }, +}; + +void EnDaiku_ChangeAnim(EnDaiku* this, s32 index, s32* currentIndex) { + f32 morphFrames; + + if (*currentIndex < 0 || *currentIndex == index) { + morphFrames = 0.0f; + } else { + morphFrames = sAnimationInfo[index].morphFrames; + } + + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, morphFrames); + + *currentIndex = index; +} + +void EnDaiku_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDaiku* this = (EnDaiku*)thisx; + s32 pad; + s32 noKill = true; + s32 isFree = false; + + if ((this->actor.params & 3) == 0 && (gSaveContext.eventChkInf[9] & 1)) { + isFree = true; + } else if ((this->actor.params & 3) == 1 && (gSaveContext.eventChkInf[9] & 2)) { + isFree = true; + } else if ((this->actor.params & 3) == 2 && (gSaveContext.eventChkInf[9] & 4)) { + isFree = true; + } else if ((this->actor.params & 3) == 3 && (gSaveContext.eventChkInf[9] & 8)) { + isFree = true; + } + + if (isFree == true && globalCtx->sceneNum == SCENE_GERUDOWAY) { + noKill = false; + } else if (isFree == false && globalCtx->sceneNum == SCENE_TENT) { + noKill = false; + } + + this->startFightSwitchFlag = this->actor.shape.rot.z & 0x3F; + this->actor.shape.rot.z = 0; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 40.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_daiku_Skel_007958, NULL, this->jointTable, this->morphTable, + 17); + + if (!noKill) { + Actor_Kill(&this->actor); + return; + } + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit2); + + Animation_Change(&this->skelAnime, sAnimationInfo[ENDAIKU_ANIM_SHOUT].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[ENDAIKU_ANIM_SHOUT].animation), + sAnimationInfo[ENDAIKU_ANIM_SHOUT].mode, sAnimationInfo[ENDAIKU_ANIM_SHOUT].morphFrames); + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + this->actor.targetMode = 6; + this->currentAnimIndex = -1; + this->runSpeed = 5.0f; + this->initRot = this->actor.world.rot; + this->initPos = this->actor.world.pos; + + if (globalCtx->sceneNum == SCENE_GERUDOWAY) { + EnDaiku_ChangeAnim(this, ENDAIKU_ANIM_STAND, &this->currentAnimIndex); + this->stateFlags |= ENDAIKU_STATEFLAG_1 | ENDAIKU_STATEFLAG_2; + this->actionFunc = EnDaiku_Jailed; + } else { + if ((this->actor.params & 3) == 1 || (this->actor.params & 3) == 3) { + EnDaiku_ChangeAnim(this, ENDAIKU_ANIM_SIT, &this->currentAnimIndex); + this->stateFlags |= ENDAIKU_STATEFLAG_1; + } else { + EnDaiku_ChangeAnim(this, ENDAIKU_ANIM_SHOUT, &this->currentAnimIndex); + this->stateFlags |= ENDAIKU_STATEFLAG_1 | ENDAIKU_STATEFLAG_2; + } + + this->skelAnime.curFrame = (s32)(Rand_ZeroOne() * this->skelAnime.endFrame); + this->actionFunc = EnDaiku_TentIdle; + } +} + +void EnDaiku_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDaiku* this = (EnDaiku*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnDaiku_UpdateTalking(EnDaiku* this, GlobalContext* globalCtx) { + s32 newTalkState = ENDAIKU_STATE_TALKING; + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) { + if (globalCtx->sceneNum == SCENE_GERUDOWAY) { + if (Message_ShouldAdvance(globalCtx)) { + if (this->actor.textId == 0x6007) { + Flags_SetSwitch(globalCtx, this->startFightSwitchFlag); + newTalkState = ENDAIKU_STATE_CAN_TALK; + } else { + this->actionFunc = EnDaiku_InitEscape; + newTalkState = ENDAIKU_STATE_NO_TALK; + } + } + } else if (globalCtx->sceneNum == SCENE_TENT) { + if (Message_ShouldAdvance(globalCtx)) { + switch (this->actor.textId) { + case 0x6061: + gSaveContext.infTable[23] |= 0x40; + break; + case 0x6064: + gSaveContext.infTable[23] |= 0x100; + break; + } + + newTalkState = ENDAIKU_STATE_CAN_TALK; + } + } + } + + return newTalkState; +} + +void EnDaiku_UpdateText(EnDaiku* this, GlobalContext* globalCtx) { + s32 carpenterType; + s32 freedCount; + s16 sp2E; + s16 sp2C; + + if (this->talkState == ENDAIKU_STATE_TALKING) { + this->talkState = EnDaiku_UpdateTalking(this, globalCtx); + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->talkState = ENDAIKU_STATE_TALKING; + } else { + Actor_GetScreenPos(globalCtx, &this->actor, &sp2E, &sp2C); + if (sp2E >= 0 && sp2E <= 320 && sp2C >= 0 && sp2C <= 240 && this->talkState == ENDAIKU_STATE_CAN_TALK && + func_8002F2CC(&this->actor, globalCtx, 100.0f) == 1) { + if (globalCtx->sceneNum == SCENE_GERUDOWAY) { + if (this->stateFlags & ENDAIKU_STATEFLAG_GERUDODEFEATED) { + freedCount = 0; + for (carpenterType = 0; carpenterType < 4; carpenterType++) { + if (gSaveContext.eventChkInf[9] & (1 << carpenterType)) { + freedCount++; + } + } + + switch (freedCount) { + case 0: + this->actor.textId = 0x605B; + break; + case 1: + this->actor.textId = 0x605C; + break; + case 2: + this->actor.textId = 0x605D; + break; + case 3: + this->actor.textId = 0x605E; + break; + } + } else if (!(this->stateFlags & + (ENDAIKU_STATEFLAG_GERUDOFIGHTING | ENDAIKU_STATEFLAG_GERUDODEFEATED))) { + this->actor.textId = 0x6007; + } + } else if (globalCtx->sceneNum == SCENE_TENT) { + switch (this->actor.params & 3) { + case 0: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + this->actor.textId = 0x6060; + } else { + this->actor.textId = 0x605F; + } + break; + case 1: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + this->actor.textId = 0x6063; + } else { + if (!(gSaveContext.infTable[23] & 0x40)) { + this->actor.textId = 0x6061; + } else { + this->actor.textId = 0x6062; + } + } + break; + case 2: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + this->actor.textId = 0x6066; + } else { + if (!(gSaveContext.infTable[23] & 0x100)) { + this->actor.textId = 0x6064; + } else { + this->actor.textId = 0x6065; + } + } + break; + case 3: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + this->actor.textId = 0x6068; + } else { + this->actor.textId = 0x6067; + } + break; + } + } + } + } +} + +/** + * The carpenter is idling in the tent. + */ +void EnDaiku_TentIdle(EnDaiku* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + EnDaiku_UpdateText(this, globalCtx); +} + +/** + * The carpenter is jailed in a Gerudo fortress cell, talking to him starts a fight against a gerudo guard + */ +void EnDaiku_Jailed(EnDaiku* this, GlobalContext* globalCtx) { + EnGeldB* gerudo; + s32 temp_t9; + s32 temp_v1; + + if (!(this->stateFlags & ENDAIKU_STATEFLAG_GERUDOFIGHTING)) { + EnDaiku_UpdateText(this, globalCtx); + } + SkelAnime_Update(&this->skelAnime); + + gerudo = (EnGeldB*)Actor_Find(&globalCtx->actorCtx, ACTOR_EN_GELDB, ACTORCAT_ENEMY); + if (gerudo == NULL) { + this->stateFlags |= ENDAIKU_STATEFLAG_GERUDODEFEATED; + this->stateFlags &= ~ENDAIKU_STATEFLAG_GERUDOFIGHTING; + EnDaiku_ChangeAnim(this, ENDAIKU_ANIM_CELEBRATE, &this->currentAnimIndex); + this->actionFunc = EnDaiku_WaitFreedom; + } else if (!(this->stateFlags & ENDAIKU_STATEFLAG_GERUDOFIGHTING) && !gerudo->invisible) { + this->stateFlags |= ENDAIKU_STATEFLAG_GERUDOFIGHTING; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + } +} + +/** + * The player defeated the gerudo guard and the carpenter is waiting for the cell door to be opened, and for the player + * to then talk to him + */ +void EnDaiku_WaitFreedom(EnDaiku* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Flags_GetSwitch(globalCtx, this->actor.params >> 8 & 0x3F)) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + EnDaiku_UpdateText(this, globalCtx); + } +} + +/** + * The carpenter is free, initializes his running away animation + */ +void EnDaiku_InitEscape(EnDaiku* this, GlobalContext* globalCtx) { + Path* path; + f32 dxz; + f32 dx; + f32 dz; + Vec3s* pointPos; + s32 exitLoop; + + Audio_PlayFanfare(NA_BGM_APPEAR); + EnDaiku_ChangeAnim(this, ENDAIKU_ANIM_RUN, &this->currentAnimIndex); + this->stateFlags &= ~(ENDAIKU_STATEFLAG_1 | ENDAIKU_STATEFLAG_2); + + gSaveContext.eventChkInf[9] |= 1 << (this->actor.params & 3); + + this->actor.gravity = -1.0f; + this->escapeSubCamTimer = sEscapeSubCamParams[this->actor.params & 3].maxFramesActive; + EnDaiku_InitSubCamera(this, globalCtx); + + exitLoop = false; + path = &globalCtx->setupPathList[this->actor.params >> 4 & 0xF]; + while (!exitLoop) { + pointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->waypoint; + dx = pointPos->x - this->actor.world.pos.x; + dz = pointPos->z - this->actor.world.pos.z; + this->rotYtowardsPath = Math_FAtan2F(dx, dz) * (0x8000 / M_PI); + dxz = sqrtf(SQ(dx) + SQ(dz)); + if (dxz > 10.0f) { + exitLoop = true; + } else { + this->waypoint++; + } + } + + this->actionFunc = EnDaiku_EscapeRotate; +} + +/** + * The carpenter is rotating towards where he is going next + */ +void EnDaiku_EscapeRotate(EnDaiku* this, GlobalContext* globalCtx) { + s16 diff; + + diff = Math_SmoothStepToS(&this->actor.shape.rot.y, this->rotYtowardsPath, 1, 0x1388, 0); + SkelAnime_Update(&this->skelAnime); + if (diff == 0) { + this->actionFunc = EnDaiku_EscapeRun; + this->actionFunc(this, globalCtx); + } +} + +void EnDaiku_InitSubCamera(EnDaiku* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f eyePosDeltaLocal; + Vec3f eyePosDeltaWorld; + + this->subCamActive = true; + this->escapeSubCamTimer = sEscapeSubCamParams[this->actor.params & 3].maxFramesActive; + + eyePosDeltaLocal.x = sEscapeSubCamParams[this->actor.params & 3].eyePosDeltaLocal.x; + eyePosDeltaLocal.y = sEscapeSubCamParams[this->actor.params & 3].eyePosDeltaLocal.y; + eyePosDeltaLocal.z = sEscapeSubCamParams[this->actor.params & 3].eyePosDeltaLocal.z; + Matrix_RotateY(this->actor.world.rot.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_MultVec3f(&eyePosDeltaLocal, &eyePosDeltaWorld); + + this->subCamEyeInit.x = this->subCamEye.x = this->actor.world.pos.x + eyePosDeltaWorld.x; + this->subCamEyeInit.y = this->subCamEye.y = this->actor.world.pos.y + eyePosDeltaWorld.y; + if (1) {} + this->subCamEyeInit.z = this->subCamEye.z = this->actor.world.pos.z + eyePosDeltaWorld.z; + + if (1) {} + this->subCamAtTarget.x = this->subCamAt.x = this->actor.world.pos.x; + this->subCamAtTarget.y = this->subCamAt.y = this->actor.world.pos.y + 60.0f; + if (1) {} + this->subCamAtTarget.z = this->subCamAt.z = this->actor.world.pos.z; + + this->subCamId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE); + + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye); + Gameplay_CameraSetFov(globalCtx, this->subCamId, globalCtx->mainCamera.fov); + func_8002DF54(globalCtx, &this->actor, 1); +} + +void EnDaiku_UpdateSubCamera(EnDaiku* this, GlobalContext* globalCtx) { + s32 pad; + + this->subCamAtTarget.x = this->actor.world.pos.x; + this->subCamAtTarget.y = this->actor.world.pos.y + 60.0f; + this->subCamAtTarget.z = this->actor.world.pos.z; + + Math_SmoothStepToF(&this->subCamAt.x, this->subCamAtTarget.x, 1.0f, 1000.0f, 0.0f); + Math_SmoothStepToF(&this->subCamAt.y, this->subCamAtTarget.y, 1.0f, 1000.0f, 0.0f); + Math_SmoothStepToF(&this->subCamAt.z, this->subCamAtTarget.z, 1.0f, 1000.0f, 0.0f); + + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->subCamAt, &this->subCamEye); +} + +void EnDaiku_EscapeSuccess(EnDaiku* this, GlobalContext* globalCtx) { + static Vec3f D_809E4148 = { 0.0f, 0.0f, 120.0f }; + Actor* gerudoGuard; + Vec3f vec; + + Gameplay_ClearCamera(globalCtx, this->subCamId); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + this->subCamActive = false; + + if ((gSaveContext.eventChkInf[9] & 0xF) == 0xF) { + Matrix_RotateY(this->initRot.y * (M_PI / 0x8000), MTXMODE_NEW); + Matrix_MultVec3f(&D_809E4148, &vec); + gerudoGuard = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_GE3, this->initPos.x + vec.x, this->initPos.y + vec.y, + this->initPos.z + vec.z, 0, Math_FAtan2F(-vec.x, -vec.z) * (0x8000 / M_PI), 0, 2); + + if (gerudoGuard == NULL) { + Actor_Kill(&this->actor); + } + } else { + func_8002DF54(globalCtx, &this->actor, 7); + } +} + +/** + * The carpenter is running away + */ +void EnDaiku_EscapeRun(EnDaiku* this, GlobalContext* globalCtx) { + s32 pad1; + Path* path; + s16 ry; + f32 dx; + f32 dz; + s32 pad2; + f32 dxz; + Vec3s* pointPos; + + path = &globalCtx->setupPathList[this->actor.params >> 4 & 0xF]; + pointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->waypoint; + dx = pointPos->x - this->actor.world.pos.x; + dz = pointPos->z - this->actor.world.pos.z; + ry = Math_FAtan2F(dx, dz) * (0x8000 / M_PI); + dxz = sqrtf(SQ(dx) + SQ(dz)); + if (dxz <= 20.88f) { + this->waypoint++; + if (this->waypoint >= path->count) { + if (this->subCamActive) { + EnDaiku_EscapeSuccess(this, globalCtx); + } + Actor_Kill(&this->actor); + return; + } + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, ry, 1, 0xFA0, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + Math_SmoothStepToF(&this->actor.speedXZ, this->runSpeed, 0.6f, dxz, 0.0f); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + if (this->subCamActive) { + EnDaiku_UpdateSubCamera(this, globalCtx); + if (this->escapeSubCamTimer-- <= 0) { + EnDaiku_EscapeSuccess(this, globalCtx); + } + } + + SkelAnime_Update(&this->skelAnime); +} + +void EnDaiku_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDaiku* this = (EnDaiku*)thisx; + s32 curFrame; + Player* player = GET_PLAYER(globalCtx); + + if (this->currentAnimIndex == ENDAIKU_ANIM_RUN) { + curFrame = this->skelAnime.curFrame; + if (curFrame == 6 || curFrame == 15) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + this->actionFunc(this, globalCtx); + + if (this->stateFlags & ENDAIKU_STATEFLAG_1) { + this->unk_244.unk_18.x = player->actor.focus.pos.x; + this->unk_244.unk_18.y = player->actor.focus.pos.y; + this->unk_244.unk_18.z = player->actor.focus.pos.z; + + if (this->stateFlags & ENDAIKU_STATEFLAG_2) { + func_80034A14(&this->actor, &this->unk_244, 0, 4); + } else { + func_80034A14(&this->actor, &this->unk_244, 0, 2); + } + } +} + +void EnDaiku_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDaiku* this = (EnDaiku*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku.c", 1227); + + func_80093D18(globalCtx->state.gfxCtx); + + if ((thisx->params & 3) == 0) { + gDPSetEnvColor(POLY_OPA_DISP++, 170, 10, 70, 255); + } else if ((thisx->params & 3) == 1) { + gDPSetEnvColor(POLY_OPA_DISP++, 170, 200, 255, 255); + } else if ((thisx->params & 3) == 2) { + gDPSetEnvColor(POLY_OPA_DISP++, 0, 230, 70, 255); + } else if ((thisx->params & 3) == 3) { + gDPSetEnvColor(POLY_OPA_DISP++, 200, 0, 150, 255); + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDaiku_OverrideLimbDraw, EnDaiku_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku.c", 1255); +} + +s32 EnDaiku_OverrideLimbDraw(GlobalContext* globalCtx, s32 limb, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnDaiku* this = (EnDaiku*)thisx; + + switch (limb) { + case 8: // torso + rot->x += this->unk_244.unk_0E.y; + rot->y -= this->unk_244.unk_0E.x; + break; + case 15: // head + rot->x += this->unk_244.unk_08.y; + rot->z += this->unk_244.unk_08.x; + break; + } + + return false; +} + +void EnDaiku_PostLimbDraw(GlobalContext* globalCtx, s32 limb, Gfx** dList, Vec3s* rot, void* thisx) { + static Gfx* hairDLists[] = { object_daiku_DL_005BD0, object_daiku_DL_005AC0, object_daiku_DL_005990, + object_daiku_DL_005880 }; + static Vec3f targetPosHeadLocal = { 700, 1100, 0 }; + EnDaiku* this = (EnDaiku*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku.c", 1323); + + if (limb == 15) { // head + Matrix_MultVec3f(&targetPosHeadLocal, &this->actor.focus.pos); + gSPDisplayList(POLY_OPA_DISP++, hairDLists[this->actor.params & 3]); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku.c", 1330); +} diff --git a/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.h b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.h new file mode 100644 index 000000000..f76c9756c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Daiku/z_en_daiku.h @@ -0,0 +1,40 @@ +#ifndef Z_EN_DAIKU_H +#define Z_EN_DAIKU_H + +#include "ultra64.h" +#include "global.h" + +struct EnDaiku; + +typedef void (*EnDaikuActionFunc)(struct EnDaiku*, GlobalContext*); + +typedef struct EnDaiku { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnDaikuActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ char unk_1E0[4]; + /* 0x01E4 */ s32 talkState; + /* 0x01E8 */ char unk_1E8[4]; + /* 0x01EC */ s32 waypoint; + /* 0x01F0 */ f32 runSpeed; + /* 0x01F4 */ s32 currentAnimIndex; + /* 0x01F8 */ char unk_1F8[4]; + /* 0x01FC */ s32 subCamActive; + /* 0x0200 */ s32 escapeSubCamTimer; + /* 0x0204 */ s32 subCamId; + /* 0x0208 */ s16 rotYtowardsPath; + /* 0x020C */ Vec3f subCamEyeInit; + /* 0x0218 */ Vec3f subCamAtTarget; + /* 0x0224 */ Vec3f subCamEye; + /* 0x0230 */ Vec3f subCamAt; + /* 0x023C */ s32 stateFlags; + /* 0x0240 */ s32 startFightSwitchFlag; + /* 0x0244 */ struct_80034A14_arg1 unk_244; // probably related to animating torso and head towards the player + /* 0x026C */ Vec3s jointTable[17]; + /* 0x02D2 */ Vec3s morphTable[17]; + /* 0x0338 */ Vec3s initRot; + /* 0x0340 */ Vec3f initPos; +} EnDaiku; // size = 0x034C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c b/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c new file mode 100644 index 000000000..6c121b3e9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.c @@ -0,0 +1,564 @@ +/* + * File: z_en_daiku_kakariko.c + * Overlay: ovl_En_Daiku_Kakariko + * Description: Kakariko Village Carpenters + */ + +#include "z_en_daiku_kakariko.h" +#include "objects/object_daiku/object_daiku.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +typedef enum { + /* 0x0 */ CARPENTER_ICHIRO, // Red and purple pants, normal hair + /* 0x1 */ CARPENTER_SABOORO, // Light blue pants + /* 0x2 */ CARPENTER_JIRO, // Green pants + /* 0x3 */ CARPENTER_SHIRO // Pink and purple pants, two-spiked hair +} KakarikoCarpenterType; + +void EnDaikuKakariko_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDaikuKakariko_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDaikuKakariko_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDaikuKakariko_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDaikuKakariko_Wait(EnDaikuKakariko* this, GlobalContext* globalCtx); +void EnDaikuKakariko_Run(EnDaikuKakariko* this, GlobalContext* globalCtx); + +const ActorInit En_Daiku_Kakariko_InitVars = { + ACTOR_EN_DAIKU_KAKARIKO, + ACTORCAT_NPC, + FLAGS, + OBJECT_DAIKU, + sizeof(EnDaikuKakariko), + (ActorFunc)EnDaikuKakariko_Init, + (ActorFunc)EnDaikuKakariko_Destroy, + (ActorFunc)EnDaikuKakariko_Update, + (ActorFunc)EnDaikuKakariko_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 66, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +typedef enum { + /* 0 */ ENDAIKUKAKARIKO_ANIM_0, + /* 1 */ ENDAIKUKAKARIKO_ANIM_1, + /* 2 */ ENDAIKUKAKARIKO_ANIM_2, + /* 3 */ ENDAIKUKAKARIKO_ANIM_3, + /* 4 */ ENDAIKUKAKARIKO_ANIM_4, + /* 5 */ ENDAIKUKAKARIKO_ANIM_5 +} EnDaikuKakarikoAnimation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &object_daiku_Anim_001AB0, 1.0f, 2, -7.0f }, { &object_daiku_Anim_007DE0, 1.0f, 0, -7.0f }, + { &object_daiku_Anim_00885C, 1.0f, 0, -7.0f }, { &object_daiku_Anim_000C44, 1.0f, 0, -7.0f }, + { &object_daiku_Anim_000600, 1.0f, 0, -7.0f }, { &object_daiku_Anim_008164, 1.0f, 0, -7.0f }, +}; + +void EnDaikuKakariko_ChangeAnim(EnDaikuKakariko* this, s32 index, s32* currentIndex) { + f32 morphFrames; + + if ((*currentIndex < 0) || (index == *currentIndex)) { + morphFrames = 0.0f; + } else { + morphFrames = sAnimationInfo[index].morphFrames; + } + + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, morphFrames); + + *currentIndex = index; +} + +void EnDaikuKakariko_Init(Actor* thisx, GlobalContext* globalCtx) { + static u16 initFlags[] = { 0x0080, 0x00B0, 0x0070, 0x0470 }; // List of inital values for this->flags + EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; + s32 pad; + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + switch (globalCtx->sceneNum) { + case SCENE_SPOT01: + if (IS_DAY) { + this->flags |= 1; + this->flags |= initFlags[this->actor.params & 3]; + } + break; + case SCENE_KAKARIKO: + if (IS_NIGHT) { + this->flags |= 2; + } + break; + case SCENE_DRAG: + this->flags |= 4; + break; + } + } + + if (!(this->flags & 7)) { + Actor_Kill(&this->actor); + } + + if (IS_NIGHT) { + this->flags |= 8; + } + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 40.0f); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_daiku_Skel_007958, NULL, this->jointTable, this->morphTable, + 17); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInit); + + Animation_Change(&this->skelAnime, sAnimationInfo[ENDAIKUKAKARIKO_ANIM_0].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[ENDAIKUKAKARIKO_ANIM_0].animation), + sAnimationInfo[ENDAIKUKAKARIKO_ANIM_0].mode, sAnimationInfo[ENDAIKUKAKARIKO_ANIM_0].morphFrames); + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + this->actor.gravity = 0.0f; + this->runSpeed = 3.0f; + this->actor.uncullZoneForward = 1200.0f; + this->actor.targetMode = 6; + this->currentAnimIndex = -1; + + if (this->flags & 0x40) { + this->actor.gravity = -1.0f; + } + + if (this->flags & 0x10) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_3, &this->currentAnimIndex); + this->actionFunc = EnDaikuKakariko_Run; + } else { + if (this->flags & 8) { + if (((this->actor.params & 3) == CARPENTER_SABOORO) || ((this->actor.params & 3) == CARPENTER_SHIRO)) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_5, &this->currentAnimIndex); + this->flags |= 0x800; + } else { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_1, &this->currentAnimIndex); + } + + this->skelAnime.curFrame = (s32)(Rand_ZeroOne() * this->skelAnime.endFrame); + } else { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_0, &this->currentAnimIndex); + this->skelAnime.curFrame = (s32)(Rand_ZeroOne() * this->skelAnime.endFrame); + } + + this->flags |= 0x100; + this->actionFunc = EnDaikuKakariko_Wait; + } +} + +void EnDaikuKakariko_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnDaikuKakariko_GetTalkState(EnDaikuKakariko* this, GlobalContext* globalCtx) { + s32 talkState = 2; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + switch (this->actor.textId) { + case 0x6061: + gSaveContext.infTable[23] |= 0x40; + break; + case 0x6064: + gSaveContext.infTable[23] |= 0x100; + break; + } + talkState = 0; + } + return talkState; +} + +void EnDaikuKakariko_HandleTalking(EnDaikuKakariko* this, GlobalContext* globalCtx) { + static s32 maskReactionSets[] = { 1, 2, 3, 4 }; + s16 sp26; + s16 sp24; + + if (this->talkState == 2) { + this->talkState = EnDaikuKakariko_GetTalkState(this, globalCtx); + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->talkState = 2; + } else { + Actor_GetScreenPos(globalCtx, &this->actor, &sp26, &sp24); + + if ((sp26 >= 0) && (sp26 <= 320) && (sp24 >= 0) && (sp24 <= 240) && (this->talkState == 0) && + (func_8002F2CC(&this->actor, globalCtx, 100.0f) == 1)) { + this->actor.textId = Text_GetFaceReaction(globalCtx, maskReactionSets[this->actor.params & 3]); + + if (this->actor.textId == 0) { + switch (this->actor.params & 3) { + case 0: + if (this->flags & 8) { + this->actor.textId = 0x5076; + } else { + this->actor.textId = 0x5075; + } + break; + case 1: + if (this->flags & 1) { + this->actor.textId = 0x502A; + } else { + this->actor.textId = 0x5074; + } + break; + case 2: + if (this->flags & 1) { + this->actor.textId = 0x506A; + } else { + this->actor.textId = 0x506B; + } + break; + case 3: + if (this->flags & 1) { + this->actor.textId = 0x5077; + } else { + this->actor.textId = 0x5078; + } + break; + } + } + } + } +} + +void EnDaikuKakariko_Talk(EnDaikuKakariko* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_3, &this->currentAnimIndex); + } + + EnDaikuKakariko_HandleTalking(this, globalCtx); + + if (this->talkState == 0) { + if (this->flags & 0x10) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_3, &this->currentAnimIndex); + this->flags &= ~0x0300; + this->actionFunc = EnDaikuKakariko_Run; + return; + } + + if (!(this->flags & 8)) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_0, &this->currentAnimIndex); + } + + if ((this->flags & 0x800) == 0) { + this->flags &= ~0x0200; + this->flags |= 0x100; + } + + this->actionFunc = EnDaikuKakariko_Wait; + } +} + +void EnDaikuKakariko_Wait(EnDaikuKakariko* this, GlobalContext* globalCtx) { + EnDaikuKakariko_HandleTalking(this, globalCtx); + + if (SkelAnime_Update(&this->skelAnime)) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_0, &this->currentAnimIndex); + } + + if (this->talkState != 0) { + if (!(this->flags & 8)) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_4, &this->currentAnimIndex); + } + + if (!(this->flags & 0x800)) { + this->flags |= 0x200; + this->flags &= ~0x0100; + } + + this->actionFunc = EnDaikuKakariko_Talk; + } +} + +void EnDaikuKakariko_StopRunning(EnDaikuKakariko* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->timer--; + + if (this->timer <= 0) { + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_3, &this->currentAnimIndex); + this->actionFunc = EnDaikuKakariko_Run; + } else { + this->skelAnime.curFrame = this->skelAnime.startFrame; + } + } + + EnDaikuKakariko_HandleTalking(this, globalCtx); + + if (this->talkState != 0) { + this->flags |= 0x200; + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_4, &this->currentAnimIndex); + this->actionFunc = EnDaikuKakariko_Talk; + } +} + +void EnDaikuKakariko_Run(EnDaikuKakariko* this, GlobalContext* globalCtx) { + s32 pad; + Path* path; + Vec3s* pathPos; + f32 xDist; + f32 zDist; + s16 runAngle; + f32 runDist; + s16 angleStepDiff; + s32 run; + + do { + path = &globalCtx->setupPathList[(this->actor.params >> 8) & 0xFF]; + pathPos = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[this->waypoint]; + xDist = pathPos->x - this->actor.world.pos.x; + zDist = pathPos->z - this->actor.world.pos.z; + runAngle = Math_FAtan2F(xDist, zDist) * (32768.0f / M_PI); + runDist = sqrtf((xDist * xDist) + (zDist * zDist)); + + run = false; + + if (runDist <= 10.0f) { + if (this->pathContinue == false) { + this->waypoint++; + + if (this->waypoint >= path->count) { + if (this->flags & 0x20) { + this->waypoint = path->count - 2; + this->pathContinue = true; + this->run = run = false; + + if (this->flags & 0x400) { + this->timer = 2; + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_0, &this->currentAnimIndex); + this->actionFunc = EnDaikuKakariko_StopRunning; + return; + } + } else { + this->waypoint = 0; + run = true; + } + } else { + this->run = run = true; + } + } else { + this->waypoint--; + + if (this->waypoint < 0) { + this->waypoint = 1; + this->pathContinue = false; + this->run = run = false; + + if (this->flags & 0x400) { + this->timer = 2; + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_0, &this->currentAnimIndex); + this->actionFunc = EnDaikuKakariko_StopRunning; + return; + } + } else { + run = true; + } + } + } + } while (run); + + angleStepDiff = Math_SmoothStepToS(&this->actor.shape.rot.y, runAngle, 1, 5000, 0); + + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (this->run == false) { + if (angleStepDiff == 0) { + this->run = true; + } else { + this->actor.speedXZ = 0.0f; + } + } + + if (this->run == true) { + Math_SmoothStepToF(&this->actor.speedXZ, this->runSpeed, 0.8f, runDist, 0.0f); + } + + Actor_MoveForward(&this->actor); + + if (this->flags & 0x40) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + } else if (this->flags & 0x80) { + this->runFlag |= 1; + this->flags &= ~0x0080; + } else if (this->runFlag & 1) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->runFlag &= ~1; + } + + SkelAnime_Update(&this->skelAnime); + EnDaikuKakariko_HandleTalking(this, globalCtx); + + if (this->talkState != 0) { + this->flags |= 0x200; + EnDaikuKakariko_ChangeAnim(this, ENDAIKUKAKARIKO_ANIM_4, &this->currentAnimIndex); + this->actionFunc = EnDaikuKakariko_Talk; + } +} + +void EnDaikuKakariko_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 pad2; + + if (this->currentAnimIndex == 3) { + if (((s32)this->skelAnime.curFrame == 6) || ((s32)this->skelAnime.curFrame == 15)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (this->flags & 4) { + this->collider.dim.pos.x -= 27; + this->collider.dim.pos.z -= 27; + this->collider.dim.radius = 63; + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + this->actionFunc(this, globalCtx); + + this->npcInfo.unk_18.x = player->actor.focus.pos.x; + this->npcInfo.unk_18.y = player->actor.focus.pos.y; + this->npcInfo.unk_18.z = player->actor.focus.pos.z; + + if (this->flags & 0x100) { + this->neckAngleTarget.x = 5900; + this->flags |= 0x1000; + func_80034A14(&this->actor, &this->npcInfo, 0, 2); + } else if (this->flags & 0x200) { + this->neckAngleTarget.x = 5900; + this->flags |= 0x1000; + func_80034A14(&this->actor, &this->npcInfo, 0, 4); + } + + Math_SmoothStepToS(&this->neckAngle.x, this->neckAngleTarget.x, 1, 1820, 0); +} + +s32 EnDaikuKakariko_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; + Vec3s angle; + + switch (limbIndex) { + case 8: + angle = this->npcInfo.unk_0E; + Matrix_RotateX(-(angle.y * (M_PI / 32768.0f)), MTXMODE_APPLY); + Matrix_RotateZ(-(angle.x * (M_PI / 32768.0f)), MTXMODE_APPLY); + break; + case 15: + Matrix_Translate(1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + angle = this->npcInfo.unk_08; + + if (this->flags & 0x1000) { + osSyncPrintf("<%d>\n", this->neckAngle.x); + Matrix_RotateX((angle.y + this->neckAngle.y) * (M_PI / 32768.0f), MTXMODE_APPLY); + Matrix_RotateZ((angle.x + this->neckAngle.x) * (M_PI / 32768.0f), MTXMODE_APPLY); + } else { + Matrix_RotateX(angle.y * (M_PI / 32768.0f), MTXMODE_APPLY); + Matrix_RotateZ(angle.x * (M_PI / 32768.0f), MTXMODE_APPLY); + } + + Matrix_Translate(-1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + break; + } + + return 0; +} + +void EnDaikuKakariko_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Gfx* carpenterHeadDLists[] = { object_daiku_DL_005BD0, object_daiku_DL_005AC0, object_daiku_DL_005990, + object_daiku_DL_005880 }; + static Vec3f unkVec = { 700.0f, 1100.0f, 0.0f }; + EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku_kakariko.c", 1104); + + if (limbIndex == 15) { + Matrix_MultVec3f(&unkVec, &this->actor.focus.pos); + gSPDisplayList(POLY_OPA_DISP++, carpenterHeadDLists[this->actor.params & 3]); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku_kakariko.c", 1113); +} + +void EnDaikuKakariko_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDaikuKakariko* this = (EnDaikuKakariko*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku_kakariko.c", 1124); + + func_80093D18(globalCtx->state.gfxCtx); + + if ((thisx->params & 3) == CARPENTER_ICHIRO) { + gDPSetEnvColor(POLY_OPA_DISP++, 170, 10, 70, 255); + } else if ((thisx->params & 3) == CARPENTER_SABOORO) { + gDPSetEnvColor(POLY_OPA_DISP++, 170, 200, 255, 255); + } else if ((thisx->params & 3) == CARPENTER_JIRO) { + gDPSetEnvColor(POLY_OPA_DISP++, 0, 230, 70, 255); + } else if ((thisx->params & 3) == CARPENTER_SHIRO) { + gDPSetEnvColor(POLY_OPA_DISP++, 200, 0, 150, 255); + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDaikuKakariko_OverrideLimbDraw, EnDaikuKakariko_PostLimbDraw, thisx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_daiku_kakariko.c", 1151); +} diff --git a/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.h b/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.h new file mode 100644 index 000000000..ebb5187c2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Daiku_Kakariko/z_en_daiku_kakariko.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_DAIKU_KAKARIKO_H +#define Z_EN_DAIKU_KAKARIKO_H + +#include "ultra64.h" +#include "global.h" + +struct EnDaikuKakariko; + +typedef void (*EnDaikuKakarikoActionFunc)(struct EnDaikuKakariko*, GlobalContext*); + +typedef struct EnDaikuKakariko { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnDaikuKakarikoActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s32 talkState; + /* 0x01E4 */ s32 waypoint; + /* 0x01E8 */ f32 runSpeed; + /* 0x01EC */ s32 currentAnimIndex; + /* 0x01F0 */ char unk_1F0[8]; // Unused variables? + /* 0x01F8 */ s32 pathContinue; // If true, continue looping through the path data + /* 0x01FC */ s32 run; // If true the carpenter will run + /* 0x0200 */ u16 flags; + /* 0x0202 */ u16 runFlag; + /* 0x0204 */ struct_80034A14_arg1 npcInfo; // Info related to NPCs and turning their head towards the player + /* 0x022C */ Vec3s jointTable[17]; + /* 0x0292 */ Vec3s morphTable[17]; + /* 0x02F8 */ s32 timer; + /* 0x02FC */ Vec3s neckAngle; + /* 0x0302 */ Vec3s neckAngleTarget; +} EnDaikuKakariko; // size = 0x0308 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c new file mode 100644 index 000000000..b858298d8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -0,0 +1,1326 @@ +#include "z_en_dekubaba.h" +#include "objects/object_dekubaba/object_dekubaba.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnDekubaba_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDekubaba_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDekubaba_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDekubaba_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnDekuBaba_Reset(void); + +void EnDekubaba_SetupWait(EnDekubaba* this); +void EnDekubaba_SetupGrow(EnDekubaba* this); +void EnDekubaba_Wait(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_Grow(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_Retract(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_DecideLunge(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_Lunge(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_PrepareLunge(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_PullBack(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_Recover(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_Hit(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_StunnedVertical(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_Sway(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_PrunedSomersault(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_ShrinkDie(EnDekubaba* this, GlobalContext* globalCtx); +void EnDekubaba_DeadStickDrop(EnDekubaba* this, GlobalContext* globalCtx); + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +const ActorInit En_Dekubaba_InitVars = { + ACTOR_EN_DEKUBABA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DEKUBABA, + sizeof(EnDekubaba), + (ActorFunc)EnDekubaba_Init, + (ActorFunc)EnDekubaba_Destroy, + (ActorFunc)EnDekubaba_Update, + (ActorFunc)EnDekubaba_Draw, + (ActorResetFunc)EnDekuBaba_Reset, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[7] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 100, 1000 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 51, { { 0, 0, 1500 }, 8 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 52, { { 0, 0, 500 }, 8 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 53, { { 0, 0, 1500 }, 8 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 54, { { 0, 0, 500 }, 8 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 55, { { 0, 0, 1500 }, 8 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 56, { { 0, 0, 500 }, 8 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 7, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 2, 25, 25, MASS_IMMOVABLE }; + +typedef enum { + /* 0x0 */ DEKUBABA_DMGEFF_NONE, + /* 0x1 */ DEKUBABA_DMGEFF_DEKUNUT, + /* 0x2 */ DEKUBABA_DMGEFF_FIRE, + /* 0xE */ DEKUBABA_DMGEFF_BOOMERANG = 14, + /* 0xF */ DEKUBABA_DMGEFF_SWORD +} DekuBabaDamageEffect; + +static DamageTable sDekuBabaDamageTable = { + /* Deku nut */ DMG_ENTRY(0, DEKUBABA_DMGEFF_DEKUNUT), + /* Deku stick */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(1, DEKUBABA_DMGEFF_NONE), + /* Explosive */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(2, DEKUBABA_DMGEFF_BOOMERANG), + /* Normal arrow */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Kokiri sword */ DMG_ENTRY(1, DEKUBABA_DMGEFF_SWORD), + /* Master sword */ DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD), + /* Giant's Knife */ DMG_ENTRY(4, DEKUBABA_DMGEFF_SWORD), + /* Fire arrow */ DMG_ENTRY(4, DEKUBABA_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Light arrow */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Unk arrow 1 */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(4, DEKUBABA_DMGEFF_FIRE), + /* Ice magic */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Light magic */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Shield */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, DEKUBABA_DMGEFF_SWORD), + /* Giant spin */ DMG_ENTRY(4, DEKUBABA_DMGEFF_SWORD), + /* Master spin */ DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD), + /* Kokiri jump */ DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD), + /* Giant jump */ DMG_ENTRY(8, DEKUBABA_DMGEFF_SWORD), + /* Master jump */ DMG_ENTRY(4, DEKUBABA_DMGEFF_SWORD), + /* Unknown 1 */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, DEKUBABA_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), +}; + +// The only difference is that for Big Deku Babas, Hookshot will act the same as Deku Nuts: i.e. it will stun, but +// cannot kill. +static DamageTable sBigDekuBabaDamageTable = { + /* Deku nut */ DMG_ENTRY(0, DEKUBABA_DMGEFF_DEKUNUT), + /* Deku stick */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(1, DEKUBABA_DMGEFF_NONE), + /* Explosive */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(2, DEKUBABA_DMGEFF_BOOMERANG), + /* Normal arrow */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(0, DEKUBABA_DMGEFF_DEKUNUT), + /* Kokiri sword */ DMG_ENTRY(1, DEKUBABA_DMGEFF_SWORD), + /* Master sword */ DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD), + /* Giant's Knife */ DMG_ENTRY(4, DEKUBABA_DMGEFF_SWORD), + /* Fire arrow */ DMG_ENTRY(4, DEKUBABA_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Light arrow */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Unk arrow 1 */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(2, DEKUBABA_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(4, DEKUBABA_DMGEFF_FIRE), + /* Ice magic */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Light magic */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Shield */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, DEKUBABA_DMGEFF_SWORD), + /* Giant spin */ DMG_ENTRY(4, DEKUBABA_DMGEFF_SWORD), + /* Master spin */ DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD), + /* Kokiri jump */ DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD), + /* Giant jump */ DMG_ENTRY(8, DEKUBABA_DMGEFF_SWORD), + /* Master jump */ DMG_ENTRY(4, DEKUBABA_DMGEFF_SWORD), + /* Unknown 1 */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, DEKUBABA_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, DEKUBABA_DMGEFF_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 1500, ICHAIN_STOP), +}; + +void EnDekubaba_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDekubaba* this = (EnDekubaba*)thisx; + s32 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 22.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gDekuBabaSkel, &gDekuBabaFastChompAnim, this->jointTable, + this->morphTable, 8); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderElements); + + if (this->actor.params == DEKUBABA_BIG) { + this->size = 2.5f; + + for (i = 0; i < sJntSphInit.count; i++) { + this->collider.elements[i].dim.worldSphere.radius = this->collider.elements[i].dim.modelSphere.radius = + (sJntSphElementsInit[i].dim.modelSphere.radius * 2.50f); + } + + // This and its counterpart below mean that a Deku Stick jumpslash will not trigger the Deku Stick drop route. + // (Of course they reckoned without each age being able to use the other's items, so Stick and Master Sword + // jumpslash can give the Stick drop as adult, and neither will as child.) + if (!LINK_IS_ADULT) { + sBigDekuBabaDamageTable.table[0x1B] = DMG_ENTRY(4, DEKUBABA_DMGEFF_NONE); // DMG_JUMP_MASTER + } + + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sBigDekuBabaDamageTable, &sColChkInfoInit); + this->actor.colChkInfo.health = 4; + this->actor.naviEnemyId = 0x08; // Big Deku Baba + this->actor.targetMode = 2; + } else { + this->size = 1.0f; + + for (i = 0; i < sJntSphInit.count; i++) { + this->collider.elements[i].dim.worldSphere.radius = this->collider.elements[i].dim.modelSphere.radius; + } + + if (!LINK_IS_ADULT) { + sDekuBabaDamageTable.table[0x1B] = DMG_ENTRY(4, DEKUBABA_DMGEFF_NONE); // DMG_JUMP_MASTER + } + + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDekuBabaDamageTable, &sColChkInfoInit); + this->actor.naviEnemyId = 0x07; // Deku Baba + this->actor.targetMode = 1; + } + + EnDekubaba_SetupWait(this); + this->timer = 0; + this->boundFloor = NULL; + this->bodyPartsPos[3] = this->actor.home.pos; +} + +void EnDekubaba_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDekubaba* this = (EnDekubaba*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnDekubaba_DisableHitboxes(EnDekubaba* this) { + s32 i; + + for (i = 1; i < ARRAY_COUNT(this->colliderElements); i++) { + this->collider.elements[i].info.bumperFlags &= ~BUMP_ON; + } +} + +void EnDekubaba_SetupWait(EnDekubaba* this) { + s32 i; + ColliderJntSphElement* element; + + this->actor.shape.rot.x = -0x4000; + this->stemSectionAngle[0] = this->stemSectionAngle[1] = this->stemSectionAngle[2] = this->actor.shape.rot.x; + + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.z = this->actor.home.pos.z; + this->actor.world.pos.y = this->actor.home.pos.y + 14.0f * this->size; + + Actor_SetScale(&this->actor, this->size * 0.01f * 0.5f); + + this->collider.base.colType = COLTYPE_HARD; + this->collider.base.acFlags |= AC_HARD; + this->timer = 45; + + for (i = 1; i < ARRAY_COUNT(this->colliderElements); i++) { + element = &this->collider.elements[i]; + element->dim.worldSphere.center.x = this->actor.world.pos.x; + element->dim.worldSphere.center.y = (s16)this->actor.world.pos.y - 7; + element->dim.worldSphere.center.z = this->actor.world.pos.z; + } + + this->actionFunc = EnDekubaba_Wait; +} + +void EnDekubaba_SetupGrow(EnDekubaba* this) { + s32 i; + + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, + Animation_GetLastFrame(&gDekuBabaFastChompAnim) * (1.0f / 15), 0.0f, + Animation_GetLastFrame(&gDekuBabaFastChompAnim), ANIMMODE_ONCE, 0.0f); + + this->timer = 15; + + for (i = 2; i < ARRAY_COUNT(this->colliderElements); i++) { + this->collider.elements[i].info.ocElemFlags |= OCELEM_ON; + } + + this->collider.base.colType = COLTYPE_HIT6; + this->collider.base.acFlags &= ~AC_HARD; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DUMMY482); + this->actionFunc = EnDekubaba_Grow; +} + +void EnDekubaba_SetupRetract(EnDekubaba* this) { + s32 i; + + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, -1.5f, Animation_GetLastFrame(&gDekuBabaFastChompAnim), + 0.0f, ANIMMODE_ONCE, -3.0f); + + this->timer = 15; + + for (i = 2; i < ARRAY_COUNT(this->colliderElements); i++) { + this->collider.elements[i].info.ocElemFlags &= ~OCELEM_ON; + } + + this->actionFunc = EnDekubaba_Retract; +} + +void EnDekubaba_SetupDecideLunge(EnDekubaba* this) { + this->timer = Animation_GetLastFrame(&gDekuBabaFastChompAnim) * 2; + Animation_MorphToLoop(&this->skelAnime, &gDekuBabaFastChompAnim, -3.0f); + this->actionFunc = EnDekubaba_DecideLunge; +} + +void EnDekubaba_SetupPrepareLunge(EnDekubaba* this) { + this->timer = 8; + this->skelAnime.playSpeed = 0.0f; + this->actionFunc = EnDekubaba_PrepareLunge; +} + +void EnDekubaba_SetupLunge(EnDekubaba* this) { + Animation_PlayOnce(&this->skelAnime, &gDekuBabaPauseChompAnim); + this->timer = 0; + this->actionFunc = EnDekubaba_Lunge; +} + +void EnDekubaba_SetupPullBack(EnDekubaba* this) { + Animation_Change(&this->skelAnime, &gDekuBabaPauseChompAnim, 1.0f, 15.0f, + Animation_GetLastFrame(&gDekuBabaPauseChompAnim), ANIMMODE_ONCE, -3.0f); + this->timer = 0; + this->actionFunc = EnDekubaba_PullBack; +} + +void EnDekubaba_SetupRecover(EnDekubaba* this) { + this->timer = 9; + this->collider.base.acFlags |= AC_ON; + this->skelAnime.playSpeed = -1.0f; + this->actionFunc = EnDekubaba_Recover; +} + +void EnDekubaba_SetupHit(EnDekubaba* this, s32 arg1) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDekuBabaPauseChompAnim, -5.0f); + this->timer = arg1; + this->collider.base.acFlags &= ~AC_ON; + Actor_SetScale(&this->actor, this->size * 0.01f); + + if (arg1 == 2) { + Actor_SetColorFilter(&this->actor, 0, 155, 0, 62); + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 42); + } + + this->actionFunc = EnDekubaba_Hit; +} + +void EnDekubaba_SetupPrunedSomersault(EnDekubaba* this) { + this->timer = 0; + this->skelAnime.playSpeed = 0.0f; + this->actor.gravity = -0.8f; + this->actor.velocity.y = 4.0f; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x8000; + this->collider.base.acFlags &= ~AC_ON; + this->actor.speedXZ = this->size * 3.0f; + this->actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + this->actionFunc = EnDekubaba_PrunedSomersault; +} + +void EnDekubaba_SetupShrinkDie(EnDekubaba* this) { + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, -1.5f, Animation_GetLastFrame(&gDekuBabaFastChompAnim), + 0.0f, ANIMMODE_ONCE, -3.0f); + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnDekubaba_ShrinkDie; +} + +void EnDekubaba_SetupStunnedVertical(EnDekubaba* this) { + s32 i; + + for (i = 1; i < ARRAY_COUNT(this->colliderElements); i++) { + this->collider.elements[i].info.bumperFlags |= BUMP_ON; + } + + if (this->timer == 1) { + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, 4.0f, 0.0f, + Animation_GetLastFrame(&gDekuBabaFastChompAnim), ANIMMODE_LOOP, -3.0f); + this->timer = 40; + } else { + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, 0.0f, 0.0f, + Animation_GetLastFrame(&gDekuBabaFastChompAnim), ANIMMODE_LOOP, -3.0f); + this->timer = 60; + } + + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.y = this->actor.home.pos.y + (60.0f * this->size); + this->actor.world.pos.z = this->actor.home.pos.z; + this->actionFunc = EnDekubaba_StunnedVertical; +} + +void EnDekubaba_SetupSway(EnDekubaba* this) { + this->targetSwayAngle = -0x6000; + this->stemSectionAngle[2] = -0x5000; + this->stemSectionAngle[1] = -0x4800; + + EnDekubaba_DisableHitboxes(this); + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 35); + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnDekubaba_Sway; +} + +void EnDekubaba_SetupDeadStickDrop(EnDekubaba* this, GlobalContext* globalCtx) { + Actor_SetScale(&this->actor, 0.03f); + this->actor.shape.rot.x -= 0x4000; + this->actor.shape.yOffset = 1000.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.shape.shadowScale = 3.0f; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_MISC); + this->actor.flags &= ~ACTOR_FLAG_5; + this->timer = 200; + this->actionFunc = EnDekubaba_DeadStickDrop; +} + +// Action functions + +void EnDekubaba_Wait(EnDekubaba* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.z = this->actor.home.pos.z; + this->actor.world.pos.y = this->actor.home.pos.y + 14.0f * this->size; + + if ((this->timer == 0) && (this->actor.xzDistToPlayer < 200.0f * this->size) && + (fabsf(this->actor.yDistToPlayer) < 30.0f * this->size)) { + EnDekubaba_SetupGrow(this); + } +} + +void EnDekubaba_Grow(EnDekubaba* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 headDistHorizontal; + f32 headDistVertical; + f32 headShiftX; + f32 headShiftZ; + + if (this->timer != 0) { + this->timer--; + } + + SkelAnime_Update(&this->skelAnime); + + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = + this->size * 0.01f * (0.5f + (15 - this->timer) * 0.5f / 15.0f); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x1800, 0x800); + + headDistVertical = sinf(CLAMP_MAX((15 - this->timer) * (1.0f / 15), 0.7f) * M_PI) * 32.0f + 14.0f; + + if (this->actor.shape.rot.x < -0x38E3) { + headDistHorizontal = 0.0f; + } else if (this->actor.shape.rot.x < -0x238E) { + Math_ScaledStepToS(&this->stemSectionAngle[0], -0x5555, 0x38E); + headDistHorizontal = Math_CosS(this->stemSectionAngle[0]) * 20.0f; + } else if (this->actor.shape.rot.x < -0xE38) { + Math_ScaledStepToS(&this->stemSectionAngle[0], -0xAAA, 0x38E); + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x5555, 0x38E); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x5555, 0x222); + + headDistHorizontal = 20.0f * (Math_CosS(this->stemSectionAngle[0]) + Math_CosS(this->stemSectionAngle[1])) + + (headDistVertical - + 20.0f * (-Math_SinS(this->stemSectionAngle[0]) - Math_SinS(this->stemSectionAngle[1]))) * + Math_CosS(this->stemSectionAngle[2]) / -Math_SinS(this->stemSectionAngle[2]); + } else { + Math_ScaledStepToS(&this->stemSectionAngle[0], -0xAAA, 0x38E); + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x31C7, 0x222); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x5555, 0x222); + + headDistHorizontal = 20.0f * (Math_CosS(this->stemSectionAngle[0]) + Math_CosS(this->stemSectionAngle[1])) + + (headDistVertical - + 20.0f * (-Math_SinS(this->stemSectionAngle[0]) - Math_SinS(this->stemSectionAngle[1]))) * + Math_CosS(this->stemSectionAngle[2]) / -Math_SinS(this->stemSectionAngle[2]); + } + + if (this->timer < 10) { + Math_ApproachS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos), 2, + 0xE38); + if (headShiftZ) {} // One way of fake-matching + } + + this->actor.world.pos.y = this->actor.home.pos.y + (headDistVertical * this->size); + headShiftX = headDistHorizontal * this->size * Math_SinS(this->actor.shape.rot.y); + headShiftZ = headDistHorizontal * this->size * Math_CosS(this->actor.shape.rot.y); + this->actor.world.pos.x = this->actor.home.pos.x + headShiftX; + this->actor.world.pos.z = this->actor.home.pos.z + headShiftZ; + + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.home.pos, this->size * 3.0f, 0, this->size * 12.0f, + this->size * 5.0f, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); + + if (this->timer == 0) { + if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 240.0f * this->size) { + EnDekubaba_SetupPrepareLunge(this); + } else { + EnDekubaba_SetupRetract(this); + } + } +} + +void EnDekubaba_Retract(EnDekubaba* this, GlobalContext* globalCtx) { + f32 headDistHorizontal; + f32 headDistVertical; + f32 xShift; + f32 zShift; + + if (this->timer != 0) { + this->timer--; + } + + SkelAnime_Update(&this->skelAnime); + + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = + this->size * 0.01f * (0.5f + this->timer * (1.0f / 30)); + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x4000, 0x300); + + headDistVertical = (sinf(CLAMP_MAX(this->timer * 0.033f, 0.7f) * M_PI) * 32.0f) + 14.0f; + + if (this->actor.shape.rot.x < -0x38E3) { + headDistHorizontal = 0.0f; + } else if (this->actor.shape.rot.x < -0x238E) { + Math_ScaledStepToS(&this->stemSectionAngle[0], -0x4000, 0x555); + headDistHorizontal = Math_CosS(this->stemSectionAngle[0]) * 20.0f; + } else if (this->actor.shape.rot.x < -0xE38) { + Math_ScaledStepToS(&this->stemSectionAngle[0], -0x5555, 0x555); + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x4000, 0x555); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x4000, 0x333); + + headDistHorizontal = 20.0f * (Math_CosS(this->stemSectionAngle[0]) + Math_CosS(this->stemSectionAngle[1])) + + (headDistVertical - + 20.0f * (-Math_SinS(this->stemSectionAngle[0]) - Math_SinS(this->stemSectionAngle[1]))) * + Math_CosS(this->stemSectionAngle[2]) / -Math_SinS(this->stemSectionAngle[2]); + } else { + Math_ScaledStepToS(&this->stemSectionAngle[0], -0x5555, 0x555); + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x5555, 0x333); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x4000, 0x333); + + headDistHorizontal = 20.0f * (Math_CosS(this->stemSectionAngle[0]) + Math_CosS(this->stemSectionAngle[1])) + + (headDistVertical - + 20.0f * (-Math_SinS(this->stemSectionAngle[0]) - Math_SinS(this->stemSectionAngle[1]))) * + Math_CosS(this->stemSectionAngle[2]) / -Math_SinS(this->stemSectionAngle[2]); + } + + this->actor.world.pos.y = this->actor.home.pos.y + (headDistVertical * this->size); + xShift = headDistHorizontal * this->size * Math_SinS(this->actor.shape.rot.y); + zShift = headDistHorizontal * this->size * Math_CosS(this->actor.shape.rot.y); + this->actor.world.pos.x = this->actor.home.pos.x + xShift; + this->actor.world.pos.z = this->actor.home.pos.z + zShift; + + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.home.pos, this->size * 3.0f, 0, this->size * 12.0f, + this->size * 5.0f, 1, HAHEN_OBJECT_DEFAULT, 0xA, NULL); + + if (this->timer == 0) { + EnDekubaba_SetupWait(this); + } +} + +void EnDekubaba_UpdateHeadPosition(EnDekubaba* this) { + f32 horizontalHeadShift = (Math_CosS(this->stemSectionAngle[0]) + Math_CosS(this->stemSectionAngle[1]) + + Math_CosS(this->stemSectionAngle[2])) * + 20.0f; + + this->actor.world.pos.x = + this->actor.home.pos.x + Math_SinS(this->actor.shape.rot.y) * (horizontalHeadShift * this->size); + this->actor.world.pos.y = + this->actor.home.pos.y - (Math_SinS(this->stemSectionAngle[0]) + Math_SinS(this->stemSectionAngle[1]) + + Math_SinS(this->stemSectionAngle[2])) * + 20.0f * this->size; + this->actor.world.pos.z = + this->actor.home.pos.z + Math_CosS(this->actor.shape.rot.y) * (horizontalHeadShift * this->size); +} + +void EnDekubaba_DecideLunge(EnDekubaba* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f)) { + if (this->actor.params == DEKUBABA_BIG) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_MOUTH); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_MOUTH); + } + } + + if (this->timer != 0) { + this->timer--; + } + + Math_ApproachS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos), 2, + (this->timer % 5) * 0x222); + + if (this->timer < 10) { + this->stemSectionAngle[0] += 0x16C; + this->stemSectionAngle[1] += 0x16C; + this->stemSectionAngle[2] += 0xB6; + this->actor.shape.rot.x += 0x222; + } else if (this->timer < 20) { + this->stemSectionAngle[0] -= 0x16C; + this->stemSectionAngle[1] += 0x111; + this->actor.shape.rot.x += 0x16C; + } else if (this->timer < 30) { + this->stemSectionAngle[1] -= 0x111; + this->actor.shape.rot.x -= 0xB6; + } else { + this->stemSectionAngle[1] -= 0xB6; + this->stemSectionAngle[2] += 0xB6; + this->actor.shape.rot.x -= 0x16C; + } + + EnDekubaba_UpdateHeadPosition(this); + + if (240.0f * this->size < Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos)) { + EnDekubaba_SetupRetract(this); + } else if ((this->timer == 0) || (this->actor.xzDistToPlayer < 80.0f * this->size)) { + EnDekubaba_SetupPrepareLunge(this); + } +} + +void EnDekubaba_Lunge(EnDekubaba* this, GlobalContext* globalCtx) { + static Color_RGBA8 primColor = { 105, 255, 105, 255 }; + static Color_RGBA8 envColor = { 150, 250, 150, 0 }; + s32 allStepsDone; + s16 curFrame10; + Vec3f velocity; + + SkelAnime_Update(&this->skelAnime); + + if (this->timer == 0) { + if (Animation_OnFrame(&this->skelAnime, 1.0f)) { + if (this->actor.params == DEKUBABA_BIG) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_ATTACK); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_ATTACK); + } + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x222); + + curFrame10 = this->skelAnime.curFrame * 10.0f; + + allStepsDone = true; + allStepsDone &= Math_ScaledStepToS(&this->stemSectionAngle[0], -0xE38, curFrame10 + 0x38E); + allStepsDone &= Math_ScaledStepToS(&this->stemSectionAngle[1], -0xE38, curFrame10 + 0x71C); + allStepsDone &= Math_ScaledStepToS(&this->stemSectionAngle[2], -0xE38, curFrame10 + 0xE38); + + if (allStepsDone) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gDekuBabaFastChompAnim, 4.0f); + velocity.x = Math_SinS(this->actor.shape.rot.y) * 5.0f; + velocity.y = 0.0f; + velocity.z = Math_CosS(this->actor.shape.rot.y) * 5.0f; + + func_8002829C(globalCtx, &this->actor.world.pos, &velocity, &sZeroVec, &primColor, &envColor, 1, + this->size * 100.0f); + this->timer = 1; + this->collider.base.acFlags |= AC_ON; + } + } else if (this->timer > 10) { + EnDekubaba_SetupPullBack(this); + } else { + this->timer++; + + if ((this->timer >= 4) && !Actor_IsFacingPlayer(&this->actor, 0x16C)) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xF, 0x71C); + } + + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f)) { + if (this->actor.params == DEKUBABA_BIG) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_MOUTH); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_MOUTH); + } + } + } + + EnDekubaba_UpdateHeadPosition(this); +} + +void EnDekubaba_PrepareLunge(EnDekubaba* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer != 0) { + this->timer--; + } + + Math_SmoothStepToS(&this->actor.shape.rot.x, 0x1800, 2, 0xE38, 0x71C); + Math_ApproachS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos), 2, 0xE38); + Math_ScaledStepToS(&this->stemSectionAngle[0], 0xAAA, 0x444); + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x4718, 0x888); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x6AA4, 0x888); + + if (this->timer == 0) { + EnDekubaba_SetupLunge(this); + } + + EnDekubaba_UpdateHeadPosition(this); +} + +void EnDekubaba_PullBack(EnDekubaba* this, GlobalContext* globalCtx) { + Vec3f dustPos; + f32 xIncr; + f32 zIncr; + s32 i; + + SkelAnime_Update(&this->skelAnime); + + if (this->timer == 0) { + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x93E, 0x38E); + Math_ScaledStepToS(&this->stemSectionAngle[0], -0x888, 0x16C); + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x888, 0x16C); + if (Math_ScaledStepToS(&this->stemSectionAngle[2], -0x888, 0x16C)) { + xIncr = Math_SinS(this->actor.shape.rot.y) * 30.0f * this->size; + zIncr = Math_CosS(this->actor.shape.rot.y) * 30.0f * this->size; + dustPos = this->actor.home.pos; + + for (i = 0; i < 3; i++) { + func_800286CC(globalCtx, &dustPos, &sZeroVec, &sZeroVec, this->size * 500.0f, this->size * 50.0f); + dustPos.x += xIncr; + dustPos.z += zIncr; + } + + this->timer = 1; + } + } else if (this->timer == 11) { + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x93E, 0x200); + Math_ScaledStepToS(&this->stemSectionAngle[0], -0xAAA, 0x200); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x5C71, 0x200); + + if (Math_ScaledStepToS(&this->stemSectionAngle[1], 0x238C, 0x200)) { + this->timer = 12; + } + } else if (this->timer == 18) { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x2AA8, 0xAAA); + + if (Math_ScaledStepToS(&this->stemSectionAngle[0], 0x1554, 0x5B0)) { + this->timer = 25; + } + + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x38E3, 0xAAA); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x5C71, 0x2D8); + } else if (this->timer == 25) { + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x5550, 0xAAA); + + if (Math_ScaledStepToS(&this->stemSectionAngle[0], -0x6388, 0x93E)) { + this->timer = 26; + } + + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x3FFC, 0x4FA); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x238C, 0x444); + } else if (this->timer == 26) { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x1800, 0x93E); + + if (Math_ScaledStepToS(&this->stemSectionAngle[0], -0x1555, 0x71C)) { + this->timer = 27; + } + + Math_ScaledStepToS(&this->stemSectionAngle[1], -0x38E3, 0x2D8); + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x5C71, 0x5B0); + } else if (this->timer >= 27) { + this->timer++; + + if (this->timer > 30) { + if (this->actor.xzDistToPlayer < 80.0f * this->size) { + EnDekubaba_SetupPrepareLunge(this); + } else { + EnDekubaba_SetupDecideLunge(this); + } + } + } else { + this->timer++; + + if (this->timer == 10) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_SCRAPE); + } + + if (this->timer >= 12) { + Math_ScaledStepToS(&this->stemSectionAngle[2], -0x5C71, 0x88); + } + } + + EnDekubaba_UpdateHeadPosition(this); +} + +void EnDekubaba_Recover(EnDekubaba* this, GlobalContext* globalCtx) { + s32 anyStepsDone; + + SkelAnime_Update(&this->skelAnime); + + if (this->timer > 8) { + anyStepsDone = Math_SmoothStepToS(&this->actor.shape.rot.x, 0x1800, 1, 0x11C6, 0x71C); + anyStepsDone |= Math_SmoothStepToS(&this->stemSectionAngle[0], -0x1555, 1, 0xAAA, 0x71C); + anyStepsDone |= Math_SmoothStepToS(&this->stemSectionAngle[1], -0x38E3, 1, 0xE38, 0x71C); + anyStepsDone |= Math_SmoothStepToS(&this->stemSectionAngle[2], -0x5C71, 1, 0x11C6, 0x71C); + + if (!anyStepsDone) { + this->timer = 8; + } + } else { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + EnDekubaba_SetupDecideLunge(this); + } + } + + EnDekubaba_UpdateHeadPosition(this); +} + +/** + * Hit by a weapon or hit something when lunging. + */ +void EnDekubaba_Hit(EnDekubaba* this, GlobalContext* globalCtx) { + s32 allStepsDone; + + SkelAnime_Update(&this->skelAnime); + + allStepsDone = true; + allStepsDone &= Math_ScaledStepToS(&this->actor.shape.rot.x, -0x4000, 0xE38); + allStepsDone &= Math_ScaledStepToS(&this->stemSectionAngle[0], -0x4000, 0xE38); + allStepsDone &= Math_ScaledStepToS(&this->stemSectionAngle[1], -0x4000, 0xE38); + allStepsDone &= Math_ScaledStepToS(&this->stemSectionAngle[2], -0x4000, 0xE38); + + if (allStepsDone) { + if (this->actor.colChkInfo.health == 0) { + EnDekubaba_SetupShrinkDie(this); + } else { + this->collider.base.acFlags |= AC_ON; + if (this->timer == 0) { + if (this->actor.xzDistToPlayer < 80.0f * this->size) { + EnDekubaba_SetupPrepareLunge(this); + } else { + EnDekubaba_SetupRecover(this); + } + } else { + EnDekubaba_SetupStunnedVertical(this); + } + } + } + + EnDekubaba_UpdateHeadPosition(this); +} + +void EnDekubaba_StunnedVertical(EnDekubaba* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + EnDekubaba_DisableHitboxes(this); + + if (this->actor.xzDistToPlayer < 80.0f * this->size) { + EnDekubaba_SetupPrepareLunge(this); + } else { + EnDekubaba_SetupRecover(this); + } + } +} + +/** + * Sway back and forth with decaying amplitude until close enough to vertical. + */ +void EnDekubaba_Sway(EnDekubaba* this, GlobalContext* globalCtx) { + s16 angleToVertical; + + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, this->stemSectionAngle[0], 0x71C); + Math_ScaledStepToS(&this->stemSectionAngle[0], this->stemSectionAngle[1], 0x71C); + Math_ScaledStepToS(&this->stemSectionAngle[1], this->stemSectionAngle[2], 0x71C); + + if (Math_ScaledStepToS(&this->stemSectionAngle[2], this->targetSwayAngle, 0x71C)) { + this->targetSwayAngle = -0x4000 - (this->targetSwayAngle + 0x4000) * 0.8f; + } + angleToVertical = this->targetSwayAngle + 0x4000; + + if (ABS(angleToVertical) < 0x100) { + this->collider.base.acFlags |= AC_ON; + if (this->actor.xzDistToPlayer < 80.0f * this->size) { + EnDekubaba_SetupPrepareLunge(this); + } else { + EnDekubaba_SetupRecover(this); + } + } + + EnDekubaba_UpdateHeadPosition(this); +} + +void EnDekubaba_PrunedSomersault(EnDekubaba* this, GlobalContext* globalCtx) { + s32 i; + Vec3f dustPos; + f32 deltaX; + f32 deltaZ; + f32 deltaY; + + Math_StepToF(&this->actor.speedXZ, 0.0f, this->size * 0.1f); + + if (this->timer == 0) { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x4800, 0x71C); + Math_ScaledStepToS(&this->stemSectionAngle[0], 0x4800, 0x71C); + Math_ScaledStepToS(&this->stemSectionAngle[1], 0x4800, 0x71C); + + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, this->size * 3.0f, 0, this->size * 12.0f, + this->size * 5.0f, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); + + if ((this->actor.scale.x > 0.005f) && ((this->actor.bgCheckFlags & 2) || (this->actor.bgCheckFlags & 8))) { + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, this->size * 3.0f, 0, this->size * 12.0f, + this->size * 5.0f, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + } + + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + this->timer = 1; + } + } else if (this->timer == 1) { + dustPos = this->actor.world.pos; + + deltaY = 20.0f * Math_SinS(this->actor.shape.rot.x); + deltaX = -20.0f * Math_CosS(this->actor.shape.rot.x) * Math_SinS(this->actor.shape.rot.y); + deltaZ = -20.0f * Math_CosS(this->actor.shape.rot.x) * Math_CosS(this->actor.shape.rot.y); + + for (i = 0; i < 4; i++) { + func_800286CC(globalCtx, &dustPos, &sZeroVec, &sZeroVec, 500, 50); + dustPos.x += deltaX; + dustPos.y += deltaY; + dustPos.z += deltaZ; + } + + func_800286CC(globalCtx, &this->actor.home.pos, &sZeroVec, &sZeroVec, this->size * 500.0f, this->size * 100.0f); + EnDekubaba_SetupDeadStickDrop(this, globalCtx); + } +} + +/** + * Die and drop Deku Nuts (Stick drop is handled elsewhere) + */ +void EnDekubaba_ShrinkDie(EnDekubaba* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, this->size * 5.0f); + + if (Math_StepToF(&this->actor.scale.x, this->size * 0.1f * 0.01f, this->size * 0.1f * 0.01f)) { + func_800286CC(globalCtx, &this->actor.home.pos, &sZeroVec, &sZeroVec, this->size * 500.0f, this->size * 100.0f); + if (this->actor.dropFlag == 0) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_NUTS); + + if (this->actor.params == DEKUBABA_BIG) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_NUTS); + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_NUTS); + } + } else { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x30); + } + Actor_Kill(&this->actor); + } + + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + this->actor.shape.rot.z += 0x1C70; + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.home.pos, this->size * 3.0f, 0, this->size * 12.0f, + this->size * 5.0f, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); +} + +void EnDekubaba_DeadStickDrop(EnDekubaba* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (Actor_HasParent(&this->actor, globalCtx) || (this->timer == 0)) { + Actor_Kill(&this->actor); + return; + } + + func_8002F554(&this->actor, globalCtx, GI_STICKS_1); +} + +// Update and associated functions + +void EnDekubaba_UpdateDamage(EnDekubaba* this, GlobalContext* globalCtx) { + Vec3f* firePos; + f32 fireScale; + s32 phi_s0; // Used for both health and iterator + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlagJntSph(&this->actor, &this->collider, 1); + + if ((this->collider.base.colType != COLTYPE_HARD) && + ((this->actor.colChkInfo.damageEffect != DEKUBABA_DMGEFF_NONE) || (this->actor.colChkInfo.damage != 0))) { + + phi_s0 = this->actor.colChkInfo.health - this->actor.colChkInfo.damage; + + if (this->actionFunc != EnDekubaba_StunnedVertical) { + if ((this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) || + (this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_DEKUNUT)) { + if (this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) { + phi_s0 = this->actor.colChkInfo.health; + } + + EnDekubaba_SetupHit(this, 2); + } else if (this->actionFunc == EnDekubaba_PullBack) { + if (phi_s0 <= 0) { + phi_s0 = 1; + } + + EnDekubaba_SetupHit(this, 1); + } else { + EnDekubaba_SetupHit(this, 0); + } + } else if ((this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_BOOMERANG) || + (this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_SWORD)) { + if (phi_s0 > 0) { + EnDekubaba_SetupSway(this); + } else { + EnDekubaba_SetupPrunedSomersault(this); + } + } else if (this->actor.colChkInfo.damageEffect != DEKUBABA_DMGEFF_DEKUNUT) { + EnDekubaba_SetupHit(this, 0); + } else { + return; + } + + this->actor.colChkInfo.health = CLAMP_MIN(phi_s0, 0); + + if (this->actor.colChkInfo.damageEffect == DEKUBABA_DMGEFF_FIRE) { + firePos = &this->actor.world.pos; + fireScale = (this->size * 70.0f); + + for (phi_s0 = 0; phi_s0 < 4; phi_s0++) { + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, firePos, fireScale, 0, 0, phi_s0); + } + } + } else { + return; + } + } else if ((globalCtx->actorCtx.unk_02 != 0) && (this->collider.base.colType != COLTYPE_HARD) && + (this->actionFunc != EnDekubaba_StunnedVertical) && (this->actionFunc != EnDekubaba_Hit) && + (this->actor.colChkInfo.health != 0)) { + this->actor.colChkInfo.health--; + this->actor.dropFlag = 0x00; + EnDekubaba_SetupHit(this, 1); + } else { + return; + } + + if (this->actor.colChkInfo.health != 0) { + if (this->timer == 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_DAMAGE); + } + } else { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + if (this->actor.params == DEKUBABA_BIG) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_DEAD); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_DEAD); + } + } +} + +void EnDekubaba_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDekubaba* this = (EnDekubaba*)thisx; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + EnDekubaba_SetupRecover(this); + } + + EnDekubaba_UpdateDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + + if (this->actionFunc == EnDekubaba_PrunedSomersault) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, this->size * 15.0f, 10.0f, 5); + } else if (this->actionFunc != EnDekubaba_DeadStickDrop) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if (this->boundFloor == NULL) { + this->boundFloor = this->actor.floorPoly; + } + } + if (this->actionFunc == EnDekubaba_Lunge) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + this->actor.flags |= ACTOR_FLAG_24; + } + + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->actionFunc != EnDekubaba_DeadStickDrop) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +// Draw functions + +void EnDekubaba_DrawStemRetracted(EnDekubaba* this, GlobalContext* globalCtx) { + f32 horizontalScale; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2445); + + horizontalScale = this->size * 0.01f; + + Matrix_Translate(this->actor.home.pos.x, this->actor.home.pos.y + (-6.0f * this->size), this->actor.home.pos.z, + MTXMODE_NEW); + Matrix_RotateZYX(this->stemSectionAngle[0], this->actor.shape.rot.y, 0, MTXMODE_APPLY); + Matrix_Scale(horizontalScale, horizontalScale, horizontalScale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2461), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaStemTopDL); + + Actor_SetFocus(&this->actor, 0.0f); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2468); +} + +void EnDekubaba_DrawStemExtended(EnDekubaba* this, GlobalContext* globalCtx) { + static Gfx* stemDLists[] = { gDekuBabaStemTopDL, gDekuBabaStemMiddleDL, gDekuBabaStemBaseDL }; + MtxF mtx; + s32 i; + f32 horizontalStepSize; + f32 spA4; + f32 scale; + s32 stemSections; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2487); + + if (this->actionFunc == EnDekubaba_PrunedSomersault) { + stemSections = 2; + } else { + stemSections = 3; + } + + scale = this->size * 0.01f; + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_Get(&mtx); + if (this->actor.colorFilterTimer != 0) { + spA4 = this->size * 20.0f; + this->bodyPartsPos[2].x = this->actor.world.pos.x; + this->bodyPartsPos[2].y = this->actor.world.pos.y - spA4; + this->bodyPartsPos[2].z = this->actor.world.pos.z; + } + + for (i = 0; i < stemSections; i++) { + mtx.yw += 20.0f * Math_SinS(this->stemSectionAngle[i]) * this->size; + horizontalStepSize = 20.0f * Math_CosS(this->stemSectionAngle[i]) * this->size; + mtx.xw -= horizontalStepSize * Math_SinS(this->actor.shape.rot.y); + mtx.zw -= horizontalStepSize * Math_CosS(this->actor.shape.rot.y); + + Matrix_Put(&mtx); + Matrix_RotateZYX(this->stemSectionAngle[i], this->actor.shape.rot.y, 0, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2533), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, stemDLists[i]); + + Collider_UpdateSpheres(51 + 2 * i, &this->collider); + Collider_UpdateSpheres(52 + 2 * i, &this->collider); + + if (i == 0) { + if (this->actionFunc != EnDekubaba_Sway) { + this->actor.focus.pos.x = mtx.xw; + this->actor.focus.pos.y = mtx.yw; + this->actor.focus.pos.z = mtx.zw; + } else { + this->actor.focus.pos.x = this->actor.home.pos.x; + this->actor.focus.pos.y = this->actor.home.pos.y + (40.0f * this->size); + this->actor.focus.pos.z = this->actor.home.pos.z; + } + } + + if ((i < 2) && (this->actor.colorFilterTimer != 0)) { + // checking colorFilterTimer ensures that spA4 has been initialized earlier, so not a bug + this->bodyPartsPos[i].x = mtx.xw; + this->bodyPartsPos[i].y = mtx.yw - spA4; + this->bodyPartsPos[i].z = mtx.zw; + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2569); +} + +void EnDekubaba_DrawStemBasePruned(EnDekubaba* this, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2579); + + Matrix_RotateZYX(this->stemSectionAngle[2], this->actor.shape.rot.y, 0, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2586), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaStemBaseDL); + + Collider_UpdateSpheres(55, &this->collider); + Collider_UpdateSpheres(56, &this->collider); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2596); +} + +void EnDekubaba_DrawBaseShadow(EnDekubaba* this, GlobalContext* globalCtx) { + MtxF mtx; + f32 horizontalScale; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2692); + func_80094044(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 255); + + func_80038A28(this->boundFloor, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, &mtx); + Matrix_Mult(&mtx, MTXMODE_NEW); + + horizontalScale = this->size * 0.15f; + Matrix_Scale(horizontalScale, 1.0f, horizontalScale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2710), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCircleShadowDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2715); +} + +void EnDekubaba_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnDekubaba* this = (EnDekubaba*)thisx; + + if (limbIndex == 1) { + Collider_UpdateSpheres(limbIndex, &this->collider); + } +} + +void EnDekubaba_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDekubaba* this = (EnDekubaba*)thisx; + f32 scale; + + if (1) {} + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2752); + func_80093D18(globalCtx->state.gfxCtx); + + if (this->actionFunc != EnDekubaba_DeadStickDrop) { + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, + EnDekubaba_PostLimbDraw, this); + + if (this->actionFunc == EnDekubaba_Wait) { + EnDekubaba_DrawStemRetracted(this, globalCtx); + } else { + EnDekubaba_DrawStemExtended(this, globalCtx); + } + + scale = this->size * 0.01f; + Matrix_Translate(this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, MTXMODE_NEW); + Matrix_RotateY(this->actor.home.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2780), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaBaseLeavesDL); + + if (this->actionFunc == EnDekubaba_PrunedSomersault) { + EnDekubaba_DrawStemBasePruned(this, globalCtx); + } + + if (this->boundFloor != NULL) { + EnDekubaba_DrawBaseShadow(this, globalCtx); + } + + // Display solid until 40 frames left, then blink until killed. + } else if ((this->timer > 40) || ((this->timer % 2) != 0)) { + Matrix_Translate(0.0f, 0.0f, 200.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2797), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaStickDropDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dekubaba.c", 2804); +} + +//OTRTODO fix this one +void EnDekuBaba_Reset(void) { + // DMG_ENTRY(2, DEKUBABA_DMGEFF_SWORD) + +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h new file mode 100644 index 000000000..fa80ce82d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_DEKUBABA_H +#define Z_EN_DEKUBABA_H + +#include "ultra64.h" +#include "global.h" + +struct EnDekubaba; + +typedef void (*EnDekubabaActionFunc)(struct EnDekubaba*, GlobalContext*); + +typedef enum { + /* 0 */ DEKUBABA_NORMAL, + /* 1 */ DEKUBABA_BIG +} DekuBabaType; + +typedef struct EnDekubaba { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3f bodyPartsPos[4]; + /* 0x017C */ SkelAnime skelAnime; + /* 0x01C0 */ EnDekubabaActionFunc actionFunc; + /* 0x01C4 */ char pad[0x2]; + /* 0x01C6 */ s16 timer; + /* 0x01C8 */ s16 targetSwayAngle; + /* 0x01CA */ s16 stemSectionAngle[3]; // Used to calculate the position of the stem sections and head with spherical trigonometry + /* 0x01D0 */ Vec3s jointTable[8]; + /* 0x0200 */ Vec3s morphTable[8]; + /* 0x0230 */ f32 size; // Used everywhere to rescale offsets etc. for Big ones + /* 0x0234 */ CollisionPoly* boundFloor; + /* 0x0238 */ ColliderJntSph collider; + /* 0x0258 */ ColliderJntSphElement colliderElements[7]; +} EnDekubaba; // size = 0x0418 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c new file mode 100644 index 000000000..e84ee67f3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.c @@ -0,0 +1,537 @@ +/* + * File: z_en_dekunuts.c + * Overlay: ovl_En_Dekunuts + * Description: Mad Scrub + */ + +#include "z_en_dekunuts.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "objects/object_dekunuts/object_dekunuts.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +#define DEKUNUTS_FLOWER 10 + +void EnDekunuts_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDekunuts_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDekunuts_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDekunuts_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDekunuts_SetupWait(EnDekunuts* this); +void EnDekunuts_Wait(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_LookAround(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_Stand(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_ThrowNut(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_Burrow(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_BeginRun(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_Run(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_Gasp(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_BeDamaged(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_BeStunned(EnDekunuts* this, GlobalContext* globalCtx); +void EnDekunuts_Die(EnDekunuts* this, GlobalContext* globalCtx); + +const ActorInit En_Dekunuts_InitVars = { + ACTOR_EN_DEKUNUTS, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DEKUNUTS, + sizeof(EnDekunuts), + (ActorFunc)EnDekunuts_Init, + (ActorFunc)EnDekunuts_Destroy, + (ActorFunc)EnDekunuts_Update, + (ActorFunc)EnDekunuts_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 18, 32, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0x01, 0x0012, 0x0020, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x4D, ICHAIN_CONTINUE), + ICHAIN_F32(gravity, -1, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2600, ICHAIN_STOP), +}; + +void EnDekunuts_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDekunuts* this = (EnDekunuts*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + if (thisx->params == DEKUNUTS_FLOWER) { + thisx->flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + } else { + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 35.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gDekuNutsSkel, &gDekuNutsStandAnim, this->jointTable, + this->morphTable, 25); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&thisx->colChkInfo, &sDamageTable, &sColChkInfoInit); + this->shotsPerRound = ((thisx->params >> 8) & 0xFF); + thisx->params &= 0xFF; + if ((this->shotsPerRound == 0xFF) || (this->shotsPerRound == 0)) { + this->shotsPerRound = 1; + } + EnDekunuts_SetupWait(this); + Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_EN_DEKUNUTS, thisx->world.pos.x, + thisx->world.pos.y, thisx->world.pos.z, 0, thisx->world.rot.y, 0, DEKUNUTS_FLOWER); + } +} + +void EnDekunuts_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDekunuts* this = (EnDekunuts*)thisx; + + if (this->actor.params != DEKUNUTS_FLOWER) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnDekunuts_SetupWait(EnDekunuts* this) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gDekuNutsUpAnim, 0.0f); + this->animFlagAndTimer = Rand_S16Offset(100, 50); + this->collider.dim.height = 5; + Math_Vec3f_Copy(&this->actor.world.pos, &this->actor.home.pos); + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnDekunuts_Wait; +} + +void EnDekunuts_SetupLookAround(EnDekunuts* this) { + Animation_PlayLoop(&this->skelAnime, &gDekuNutsLookAroundAnim); + this->animFlagAndTimer = 2; + this->actionFunc = EnDekunuts_LookAround; +} + +void EnDekunuts_SetupThrowNut(EnDekunuts* this) { + Animation_PlayOnce(&this->skelAnime, &gDekuNutsSpitAnim); + this->animFlagAndTimer = this->shotsPerRound; + this->actionFunc = EnDekunuts_ThrowNut; +} + +void EnDekunuts_SetupStand(EnDekunuts* this) { + Animation_MorphToLoop(&this->skelAnime, &gDekuNutsStandAnim, -3.0f); + if (this->actionFunc == EnDekunuts_ThrowNut) { + this->animFlagAndTimer = 2 | 0x1000; // sets timer and flag + } else { + this->animFlagAndTimer = 1; + } + this->actionFunc = EnDekunuts_Stand; +} + +void EnDekunuts_SetupBurrow(EnDekunuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDekuNutsBurrowAnim, -5.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DOWN); + this->actionFunc = EnDekunuts_Burrow; +} + +void EnDekunuts_SetupBeginRun(EnDekunuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDekuNutsUnburrowAnim, -3.0f); + this->collider.dim.height = 37; + this->actor.colChkInfo.mass = 0x32; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DAMAGE); + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnDekunuts_BeginRun; +} + +void EnDekunuts_SetupRun(EnDekunuts* this) { + Animation_PlayLoop(&this->skelAnime, &gDekuNutsRunAnim); + this->animFlagAndTimer = 2; + this->playWalkSound = false; + this->collider.base.acFlags |= AC_ON; + this->actionFunc = EnDekunuts_Run; +} + +void EnDekunuts_SetupGasp(EnDekunuts* this) { + Animation_PlayLoop(&this->skelAnime, &gDekuNutsGaspAnim); + this->animFlagAndTimer = 3; + this->actor.speedXZ = 0.0f; + if (this->runAwayCount != 0) { + this->runAwayCount--; + } + this->actionFunc = EnDekunuts_Gasp; +} + +void EnDekunuts_SetupBeDamaged(EnDekunuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDekuNutsDamageAnim, -3.0f); + if ((this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F824) != 0) { + this->actor.world.rot.y = this->collider.base.ac->world.rot.y; + } else { + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, this->collider.base.ac) + 0x8000; + } + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnDekunuts_BeDamaged; + this->actor.speedXZ = 10.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DAMAGE); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_CUTBODY); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, Animation_GetLastFrame(&gDekuNutsDamageAnim)); +} + +void EnDekunuts_SetupBeStunned(EnDekunuts* this) { + Animation_MorphToLoop(&this->skelAnime, &gDekuNutsDamageAnim, -3.0f); + this->animFlagAndTimer = 5; + this->actionFunc = EnDekunuts_BeStunned; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, + Animation_GetLastFrame(&gDekuNutsDamageAnim) * this->animFlagAndTimer); +} + +void EnDekunuts_SetupDie(EnDekunuts* this) { + Animation_PlayOnce(&this->skelAnime, &gDekuNutsDieAnim); + this->actionFunc = EnDekunuts_Die; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DEAD); +} + +void EnDekunuts_Wait(EnDekunuts* this, GlobalContext* globalCtx) { + s32 hasSlowPlaybackSpeed = false; + + if (this->skelAnime.playSpeed < 0.5f) { + hasSlowPlaybackSpeed = true; + } + if (hasSlowPlaybackSpeed && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + this->collider.base.acFlags |= AC_ON; + } else if (Animation_OnFrame(&this->skelAnime, 8.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + } + + this->collider.dim.height = ((CLAMP(this->skelAnime.curFrame, 9.0f, 12.0f) - 9.0f) * 9.0f) + 5.0f; + if (!hasSlowPlaybackSpeed && (this->actor.xzDistToPlayer < 120.0f)) { + EnDekunuts_SetupBurrow(this); + } else if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.xzDistToPlayer < 120.0f) { + EnDekunuts_SetupBurrow(this); + } else if ((this->animFlagAndTimer == 0) && (this->actor.xzDistToPlayer > 320.0f)) { + EnDekunuts_SetupLookAround(this); + } else { + EnDekunuts_SetupStand(this); + } + } + if (hasSlowPlaybackSpeed && + ((this->actor.xzDistToPlayer > 160.0f) && (fabsf(this->actor.yDistToPlayer) < 120.0f)) && + ((this->animFlagAndTimer == 0) || (this->actor.xzDistToPlayer < 480.0f))) { + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnDekunuts_LookAround(EnDekunuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if ((this->actor.xzDistToPlayer < 120.0f) || (this->animFlagAndTimer == 0)) { + EnDekunuts_SetupBurrow(this); + } +} + +void EnDekunuts_Stand(EnDekunuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (!(this->animFlagAndTimer & 0x1000)) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + } + if (this->animFlagAndTimer == 0x1000) { + if ((this->actor.xzDistToPlayer > 480.0f) || (this->actor.xzDistToPlayer < 120.0f)) { + EnDekunuts_SetupBurrow(this); + } else { + EnDekunuts_SetupThrowNut(this); + } + } else if (this->animFlagAndTimer == 0) { + EnDekunuts_SetupThrowNut(this); + } +} + +void EnDekunuts_ThrowNut(EnDekunuts* this, GlobalContext* globalCtx) { + Vec3f spawnPos; + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + if (SkelAnime_Update(&this->skelAnime)) { + EnDekunuts_SetupStand(this); + } else if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + spawnPos.x = this->actor.world.pos.x + (Math_SinS(this->actor.shape.rot.y) * 23.0f); + spawnPos.y = this->actor.world.pos.y + 12.0f; + spawnPos.z = this->actor.world.pos.z + (Math_CosS(this->actor.shape.rot.y) * 23.0f); + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_NUTSBALL, spawnPos.x, spawnPos.y, spawnPos.z, + this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, 0) != NULL) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_THROW); + } + } else if ((this->animFlagAndTimer > 1) && Animation_OnFrame(&this->skelAnime, 12.0f)) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDekuNutsSpitAnim, -3.0f); + if (this->animFlagAndTimer != 0) { + this->animFlagAndTimer--; + } + } +} + +void EnDekunuts_Burrow(EnDekunuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnDekunuts_SetupWait(this); + } else { + this->collider.dim.height = ((3.0f - CLAMP(this->skelAnime.curFrame, 1.0f, 3.0f)) * 12.0f) + 5.0f; + } + if (Animation_OnFrame(&this->skelAnime, 4.0f)) { + this->collider.base.acFlags &= ~AC_ON; + } + Math_ApproachF(&this->actor.world.pos.x, this->actor.home.pos.x, 0.5f, 3.0f); + Math_ApproachF(&this->actor.world.pos.z, this->actor.home.pos.z, 0.5f, 3.0f); +} + +void EnDekunuts_BeginRun(EnDekunuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->runDirection = this->actor.yawTowardsPlayer + 0x8000; + this->runAwayCount = 3; + EnDekunuts_SetupRun(this); + } + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); +} + +void EnDekunuts_Run(EnDekunuts* this, GlobalContext* globalCtx) { + s16 diffRotInit; + s16 diffRot; + f32 phi_f0; + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (this->playWalkSound) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + this->playWalkSound = false; + } else { + this->playWalkSound = true; + } + + Math_StepToF(&this->actor.speedXZ, 7.5f, 1.0f); + if (Math_SmoothStepToS(&this->actor.world.rot.y, this->runDirection, 1, 0xE38, 0xB6) == 0) { + if (this->actor.bgCheckFlags & 0x20) { + this->runDirection = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + } else if (this->actor.bgCheckFlags & 8) { + this->runDirection = this->actor.wallYaw; + } else if (this->runAwayCount == 0) { + diffRotInit = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + diffRot = diffRotInit - this->actor.yawTowardsPlayer; + if (ABS(diffRot) > 0x2000) { + this->runDirection = diffRotInit; + } else { + phi_f0 = (diffRot >= 0.0f) ? 1.0f : -1.0f; + this->runDirection = (phi_f0 * -0x2000) + this->actor.yawTowardsPlayer; + } + } else { + this->runDirection = this->actor.yawTowardsPlayer + 0x8000; + } + } + + this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + if ((this->runAwayCount == 0) && Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) < 20.0f && + fabsf(this->actor.world.pos.y - this->actor.home.pos.y) < 2.0f) { + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.speedXZ = 0.0f; + EnDekunuts_SetupBurrow(this); + } else if (this->animFlagAndTimer == 0) { + EnDekunuts_SetupGasp(this); + } +} + +void EnDekunuts_Gasp(EnDekunuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (this->animFlagAndTimer == 0) { + EnDekunuts_SetupRun(this); + } +} + +void EnDekunuts_BeDamaged(EnDekunuts* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); + if (SkelAnime_Update(&this->skelAnime)) { + EnDekunuts_SetupDie(this); + } +} + +void EnDekunuts_BeStunned(EnDekunuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + if (this->animFlagAndTimer != 0) { + this->animFlagAndTimer--; + } + if (this->animFlagAndTimer == 0) { + EnDekunuts_SetupRun(this); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_FAINT); + } + } +} + +void EnDekunuts_Die(EnDekunuts* this, GlobalContext* globalCtx) { + static Vec3f effectVelAndAccel = { 0.0f, 0.0f, 0.0f }; + + s32 pad; + Vec3f effectPos; + + if (SkelAnime_Update(&this->skelAnime)) { + effectPos.x = this->actor.world.pos.x; + effectPos.y = this->actor.world.pos.y + 18.0f; + effectPos.z = this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &effectPos, &effectVelAndAccel, &effectVelAndAccel, 200, 0, 255, 255, 255, 255, + 150, 150, 150, 1, 13, 1); + effectPos.y = this->actor.world.pos.y + 10.0f; + EffectSsHahen_SpawnBurst(globalCtx, &effectPos, 3.0f, 0, 12, 3, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x30); + if (this->actor.child != NULL) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, this->actor.child, ACTORCAT_PROP); + } + Actor_Kill(&this->actor); + } +} + +void EnDekunuts_ColliderCheck(EnDekunuts* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if (this->actor.colChkInfo.mass == 0x32) { + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (this->actor.colChkInfo.damageEffect != 1) { + if (this->actor.colChkInfo.damageEffect == 2) { + EffectSsFCircle_Spawn(globalCtx, &this->actor, &this->actor.world.pos, 40, 50); + } + EnDekunuts_SetupBeDamaged(this); + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } + } else if (this->actionFunc != EnDekunuts_BeStunned) { + EnDekunuts_SetupBeStunned(this); + } + } + } else { + EnDekunuts_SetupBeginRun(this); + } + } else if ((this->actor.colChkInfo.mass == MASS_IMMOVABLE) && (globalCtx->actorCtx.unk_02 != 0)) { + EnDekunuts_SetupBeginRun(this); + } +} + +void EnDekunuts_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDekunuts* this = (EnDekunuts*)thisx; + s32 pad; + + if (this->actor.params != DEKUNUTS_FLOWER) { + EnDekunuts_ColliderCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, this->collider.dim.radius, this->collider.dim.height, + 0x1D); + Collider_UpdateCylinder(&this->actor, &this->collider); + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actionFunc == EnDekunuts_Wait) { + Actor_SetFocus(&this->actor, this->skelAnime.curFrame); + } else if (this->actionFunc == EnDekunuts_Burrow) { + Actor_SetFocus(&this->actor, + 20.0f - ((this->skelAnime.curFrame * 20.0f) / Animation_GetLastFrame(&gDekuNutsBurrowAnim))); + } else { + Actor_SetFocus(&this->actor, 20.0f); + } + } +} + +s32 EnDekunuts_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnDekunuts* this = (EnDekunuts*)thisx; + f32 x; + f32 y; + f32 z; + f32 curFrame; + + if ((limbIndex == 7) && (this->actionFunc == EnDekunuts_ThrowNut)) { + curFrame = this->skelAnime.curFrame; + if (curFrame <= 6.0f) { + x = 1.0f - (curFrame * 0.0833f); + z = 1.0f + (curFrame * 0.1167f); + y = 1.0f + (curFrame * 0.1167f); + } else if (curFrame <= 7.0f) { + curFrame -= 6.0f; + x = 0.5f + curFrame; + z = 1.7f - (curFrame * 0.7f); + y = 1.7f - (curFrame * 0.7f); + } else if (curFrame <= 10.0f) { + x = 1.5f - ((curFrame - 7.0f) * 0.1667f); + z = 1.0f; + y = 1.0f; + } else { + return false; + } + Matrix_Scale(x, y, z, MTXMODE_APPLY); + } + return false; +} + +void EnDekunuts_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDekunuts* this = (EnDekunuts*)thisx; + + if (this->actor.params == DEKUNUTS_FLOWER) { + Gfx_DrawDListOpa(globalCtx, gDekuNutsFlowerDL); + } else { + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnDekunuts_OverrideLimbDraw, + NULL, this); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.h b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.h new file mode 100644 index 000000000..f80727ac7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dekunuts/z_en_dekunuts.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_DEKUNUTS_H +#define Z_EN_DEKUNUTS_H + +#include "ultra64.h" +#include "global.h" + +struct EnDekunuts; + +typedef void (*EnDekunutsActionFunc)(struct EnDekunuts*, GlobalContext*); + +typedef struct EnDekunuts { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnDekunutsActionFunc actionFunc; + /* 0x0194 */ u8 playWalkSound; + /* 0x0195 */ u8 runAwayCount; + /* 0x0196 */ s16 animFlagAndTimer; + /* 0x0198 */ s16 runDirection; + /* 0x019A */ s16 shotsPerRound; + /* 0x019C */ Vec3s jointTable[25]; + /* 0x0232 */ Vec3s morphTable[25]; + /* 0x02C8 */ ColliderCylinder collider; +} EnDekunuts; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c new file mode 100644 index 000000000..53a5fdfab --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.c @@ -0,0 +1,582 @@ +#include "z_en_dh.h" +#include "objects/object_dh/object_dh.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_10) + +typedef enum { + /* 0 */ DH_WAIT, + /* 1 */ DH_RETREAT, + /* 2 */ DH_BURROW, + /* 3 */ DH_WALK, + /* 4 */ DH_ATTACK, + /* 5 */ DH_DEATH, + /* 6 */ DH_DAMAGE +} EnDhAction; + +void EnDh_Init(Actor* this, GlobalContext* globalCtx); +void EnDh_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDh_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDh_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDh_SetupWait(EnDh* this); +void EnDh_SetupWalk(EnDh* this); +void EnDh_SetupAttack(EnDh* this); +void EnDh_SetupBurrow(EnDh* this); + +void EnDh_Wait(EnDh* this, GlobalContext* globalCtx); +void EnDh_Walk(EnDh* this, GlobalContext* globalCtx); +void EnDh_Retreat(EnDh* this, GlobalContext* globalCtx); +void EnDh_Attack(EnDh* this, GlobalContext* globalCtx); +void EnDh_Burrow(EnDh* this, GlobalContext* globalCtx); +void EnDh_Damage(EnDh* this, GlobalContext* globalCtx); +void EnDh_Death(EnDh* this, GlobalContext* globalCtx); + +const ActorInit En_Dh_InitVars = { + ACTOR_EN_DH, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DH, + sizeof(EnDh), + (ActorFunc)EnDh_Init, + (ActorFunc)EnDh_Destroy, + (ActorFunc)EnDh_Update, + (ActorFunc)EnDh_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 35, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON | OCELEM_UNK3, + }, + { 1, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static DamageTable D_809EC620 = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0xF), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(2, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(2, 0xF), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(4, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xF), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x2F, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -3500, ICHAIN_STOP), +}; + +void EnDh_SetupAction(EnDh* this, EnDhActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnDh_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDh* this = (EnDh*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = &D_809EC620; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_dh_Skel_007E88, &object_dh_Anim_005880, this->jointTable, + this->limbRotTable, 16); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 64.0f); + this->actor.params = ENDH_WAIT_UNDERGROUND; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.colChkInfo.health = LINK_IS_ADULT ? 14 : 20; + this->alpha = this->unk_258 = 255; + this->actor.flags &= ~ACTOR_FLAG_0; + Collider_InitCylinder(globalCtx, &this->collider1); + Collider_SetCylinder(globalCtx, &this->collider1, &this->actor, &sCylinderInit); + Collider_InitJntSph(globalCtx, &this->collider2); + Collider_SetJntSph(globalCtx, &this->collider2, &this->actor, &sJntSphInit, this->elements); + EnDh_SetupWait(this); +} + +void EnDh_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDh* this = (EnDh*)thisx; + + func_800F5B58(); + Collider_DestroyCylinder(globalCtx, &this->collider1); + Collider_DestroyJntSph(globalCtx, &this->collider2); +} + +void EnDh_SpawnDebris(GlobalContext* globalCtx, EnDh* this, Vec3f* spawnPos, f32 spread, s32 arg4, f32 accelXZ, + f32 scale) { + Vec3f pos; + Vec3f vel = { 0.0f, 8.0f, 0.0f }; + Vec3f accel = { 0.0f, -1.5f, 0.0f }; + f32 spreadAngle; + f32 scaleMod; + + spreadAngle = (Rand_ZeroOne() - 0.5f) * 6.28f; + pos.y = this->actor.floorHeight; + pos.x = (Math_SinF(spreadAngle) * spread) + spawnPos->x; + pos.z = (Math_CosF(spreadAngle) * spread) + spawnPos->z; + accel.x = (Rand_ZeroOne() - 0.5f) * accelXZ; + accel.z = (Rand_ZeroOne() - 0.5f) * accelXZ; + vel.y += (Rand_ZeroOne() - 0.5f) * 4.0f; + scaleMod = (Rand_ZeroOne() * 5.0f) + 12.0f; + EffectSsHahen_Spawn(globalCtx, &pos, &vel, &accel, arg4, scaleMod * scale, -1, 10, NULL); +} + +void EnDh_SetupWait(EnDh* this) { + Animation_PlayLoop(&this->skelAnime, &object_dh_Anim_003A8C); + this->curAction = DH_WAIT; + this->actor.world.pos.x = Rand_CenteredFloat(600.0f) + this->actor.home.pos.x; + this->actor.world.pos.z = Rand_CenteredFloat(600.0f) + this->actor.home.pos.z; + this->actor.shape.yOffset = -15000.0f; + this->dirtWaveSpread = this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.flags |= ACTOR_FLAG_7; + this->dirtWavePhase = this->actionState = this->actor.params = ENDH_WAIT_UNDERGROUND; + EnDh_SetupAction(this, EnDh_Wait); +} + +void EnDh_Wait(EnDh* this, GlobalContext* globalCtx) { + if ((s32)this->skelAnime.curFrame == 5) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + if (Actor_GetCollidedExplosive(globalCtx, &this->collider1.base)) { + this->actor.params = ENDH_START_ATTACK_BOMB; + } + if ((this->actor.params >= ENDH_START_ATTACK_GRAB) || (this->actor.params <= ENDH_HANDS_KILLED_4)) { + switch (this->actionState) { + case 0: + this->actor.flags |= ACTOR_FLAG_0; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.flags &= ~ACTOR_FLAG_7; + this->actionState++; + this->drawDirtWave++; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_HIDE); + case 1: + this->dirtWavePhase += 0x3A7; + Math_SmoothStepToF(&this->dirtWaveSpread, 300.0f, 1.0f, 5.0f, 0.0f); + this->dirtWaveHeight = Math_SinS(this->dirtWavePhase) * 55.0f; + this->dirtWaveAlpha = (s16)(Math_SinS(this->dirtWavePhase) * 255.0f); + EnDh_SpawnDebris(globalCtx, this, &this->actor.world.pos, this->dirtWaveSpread, 4, 2.05f, 1.2f); + if (this->actor.shape.yOffset == 0.0f) { + this->drawDirtWave = false; + this->actionState++; + } else if (this->dirtWavePhase > 0x12C0) { + this->actor.shape.yOffset += 500.0f; + } + break; + case 2: + EnDh_SetupWalk(this); + break; + } + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x7D0, 0); + SkelAnime_Update(&this->skelAnime); + if (this->actor.params != ENDH_START_ATTACK_BOMB) { + func_8008EEAC(globalCtx, &this->actor); + } + } +} + +void EnDh_SetupWalk(EnDh* this) { + Animation_Change(&this->skelAnime, &object_dh_Anim_003A8C, 1.0f, 0.0f, + Animation_GetLastFrame(&object_dh_Anim_003A8C) - 3.0f, ANIMMODE_LOOP, -6.0f); + this->curAction = DH_WALK; + this->timer = 300; + this->actor.speedXZ = 1.0f; + EnDh_SetupAction(this, EnDh_Walk); +} + +void EnDh_Walk(EnDh* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + SkelAnime_Update(&this->skelAnime); + if (((s32)this->skelAnime.curFrame % 8) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_WALK); + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_LAUGH); + } + if (this->actor.xzDistToPlayer <= 100.0f) { + this->actor.speedXZ = 0.0f; + if (Actor_IsFacingPlayer(&this->actor, 60 * 0x10000 / 360)) { + EnDh_SetupAttack(this); + } + } else if (--this->timer == 0) { + EnDh_SetupBurrow(this); + } +} + +void EnDh_SetupRetreat(EnDh* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &object_dh_Anim_005880, -4.0f); + this->curAction = DH_RETREAT; + this->timer = 70; + this->actor.speedXZ = 1.0f; + EnDh_SetupAction(this, EnDh_Retreat); +} + +void EnDh_Retreat(EnDh* this, GlobalContext* globalCtx) { + this->timer--; + if (this->timer == 0) { + this->retreat = false; + EnDh_SetupBurrow(this); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, (s16)(this->actor.yawTowardsPlayer + 0x8000), 1, 0xBB8, 0); + } + this->actor.world.rot.y = this->actor.shape.rot.y; + SkelAnime_Update(&this->skelAnime); +} + +void EnDh_SetupAttack(EnDh* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_dh_Anim_004658, -6.0f); + this->timer = this->actionState = 0; + this->curAction = DH_ATTACK; + this->actor.speedXZ = 0.0f; + EnDh_SetupAction(this, EnDh_Attack); +} + +void EnDh_Attack(EnDh* this, GlobalContext* globalCtx) { + s32 pad; + + if (SkelAnime_Update(&this->skelAnime)) { + this->actionState++; + } else if ((this->actor.xzDistToPlayer > 100.0f) || !Actor_IsFacingPlayer(&this->actor, 60 * 0x10000 / 360)) { + Animation_Change(&this->skelAnime, &object_dh_Anim_004658, -1.0f, this->skelAnime.curFrame, 0.0f, ANIMMODE_ONCE, + -4.0f); + this->actionState = 4; + this->collider2.base.atFlags = this->collider2.elements[0].info.toucherFlags = AT_NONE; // also TOUCH_NONE + this->collider2.elements[0].info.toucher.dmgFlags = this->collider2.elements[0].info.toucher.damage = 0; + } + switch (this->actionState) { + case 1: + Animation_PlayOnce(&this->skelAnime, &object_dh_Anim_001A3C); + this->actionState++; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_BITE); + case 0: + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x5DC, 0); + break; + case 2: + if (this->skelAnime.curFrame >= 4.0f) { + this->collider2.base.atFlags = this->collider2.elements[0].info.toucherFlags = + AT_ON | AT_TYPE_ENEMY; // also TOUCH_ON | TOUCH_SFX_WOOD + this->collider2.elements[0].info.toucher.dmgFlags = 0xFFCFFFFF; + this->collider2.elements[0].info.toucher.damage = 8; + } + if (this->collider2.base.atFlags & AT_BOUNCED) { + this->collider2.base.atFlags &= ~(AT_HIT | AT_BOUNCED); + this->collider2.base.atFlags = this->collider2.elements[0].info.toucherFlags = + AT_NONE; // also TOUCH_NONE + this->collider2.elements[0].info.toucher.dmgFlags = this->collider2.elements[0].info.toucher.damage = 0; + this->actionState++; + } else if (this->collider2.base.atFlags & AT_HIT) { + this->collider2.base.atFlags &= ~AT_HIT; + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.shape.rot.y, 8.0f); + } + break; + case 3: + if ((this->actor.xzDistToPlayer <= 100.0f) && (Actor_IsFacingPlayer(&this->actor, 60 * 0x10000 / 360))) { + Animation_Change(&this->skelAnime, &object_dh_Anim_004658, 1.0f, 20.0f, + Animation_GetLastFrame(&object_dh_Anim_004658), ANIMMODE_ONCE, -6.0f); + this->actionState = 0; + } else { + Animation_Change(&this->skelAnime, &object_dh_Anim_004658, -1.0f, + Animation_GetLastFrame(&object_dh_Anim_004658), 0.0f, ANIMMODE_ONCE, -4.0f); + this->actionState++; + this->collider2.base.atFlags = this->collider2.elements[0].info.toucherFlags = + AT_NONE; // also TOUCH_NONE + this->collider2.elements[0].info.toucher.dmgFlags = this->collider2.elements[0].info.toucher.damage = 0; + } + break; + case 5: + EnDh_SetupWalk(this); + break; + case 4: + break; + } + this->actor.world.rot.y = this->actor.shape.rot.y; +} + +void EnDh_SetupBurrow(EnDh* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_dh_Anim_002148, -6.0f); + this->curAction = DH_BURROW; + this->dirtWaveSpread = this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->dirtWavePhase = 0; + this->actionState = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_HIDE); + EnDh_SetupAction(this, EnDh_Burrow); +} + +void EnDh_Burrow(EnDh* this, GlobalContext* globalCtx) { + switch (this->actionState) { + case 0: + this->actionState++; + this->drawDirtWave++; + this->collider1.base.atFlags = this->collider1.info.toucherFlags = + AT_ON | AT_TYPE_ENEMY; // also TOUCH_ON | TOUCH_SFX_WOOD + this->collider1.info.toucher.dmgFlags = 0xFFCFFFFF; + this->collider1.info.toucher.damage = 4; + case 1: + this->dirtWavePhase += 0x47E; + Math_SmoothStepToF(&this->dirtWaveSpread, 300.0f, 1.0f, 8.0f, 0.0f); + this->dirtWaveHeight = Math_SinS(this->dirtWavePhase) * 55.0f; + this->dirtWaveAlpha = (s16)(Math_SinS(this->dirtWavePhase) * 255.0f); + EnDh_SpawnDebris(globalCtx, this, &this->actor.world.pos, this->dirtWaveSpread, 4, 2.05f, 1.2f); + this->collider1.dim.radius = this->dirtWaveSpread * 0.6f; + if (SkelAnime_Update(&this->skelAnime)) { + this->actionState++; + } + break; + case 2: + this->drawDirtWave = false; + this->collider1.dim.radius = 35; + this->collider1.base.atFlags = this->collider1.info.toucherFlags = AT_NONE; // Also TOUCH_NONE + this->collider1.info.toucher.dmgFlags = this->collider1.info.toucher.damage = 0; + EnDh_SetupWait(this); + break; + } +} + +void EnDh_SetupDamage(EnDh* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_dh_Anim_003D6C, -6.0f); + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = -1.0f; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_DAMAGE); + this->curAction = DH_DAMAGE; + EnDh_SetupAction(this, EnDh_Damage); +} + +void EnDh_Damage(EnDh* this, GlobalContext* globalCtx) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.15f; + } + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->retreat) { + EnDh_SetupRetreat(this, globalCtx); + } else if ((this->actor.xzDistToPlayer <= 105.0f) && Actor_IsFacingPlayer(&this->actor, 60 * 0x10000 / 360)) { + f32 frames = Animation_GetLastFrame(&object_dh_Anim_004658); + + EnDh_SetupAttack(this); + Animation_Change(&this->skelAnime, &object_dh_Anim_004658, 1.0f, 20.0f, frames, ANIMMODE_ONCE, -6.0f); + } else { + EnDh_SetupWalk(this); + } + this->unk_258 = 255; + } +} + +void EnDh_SetupDeath(EnDh* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_dh_Anim_0032BC, -1.0f); + this->curAction = DH_DEATH; + this->timer = 300; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + func_800F5B58(); + this->actor.params = ENDH_DEATH; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_DEAD); + EnDh_SetupAction(this, EnDh_Death); +} + +void EnDh_Death(EnDh* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) || (this->timer != 300)) { + if (this->timer == 300) { + Animation_PlayLoop(&this->skelAnime, &object_dh_Anim_00375C); + } + this->timer--; + if (this->timer < 150) { + if (this->alpha != 0) { + this->actor.scale.y -= 0.000075f; + this->actor.shape.shadowAlpha = this->alpha -= 5; + } else { + Actor_Kill(&this->actor); + return; + } + } + } else { + if (((s32)this->skelAnime.curFrame == 53) || ((s32)this->skelAnime.curFrame == 56) || + ((s32)this->skelAnime.curFrame == 61)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } + if ((s32)this->skelAnime.curFrame == 61) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + } + } +} + +void EnDh_CollisionCheck(EnDh* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 lastHealth; + + if ((this->collider2.base.acFlags & AC_HIT) && !this->retreat) { + this->collider2.base.acFlags &= ~AC_HIT; + if ((this->actor.colChkInfo.damageEffect != 0) && (this->actor.colChkInfo.damageEffect != 6)) { + this->collider2.base.atFlags = this->collider2.elements[0].info.toucherFlags = AT_NONE; // also TOUCH_NONE + this->collider2.elements[0].info.toucher.dmgFlags = this->collider2.elements[0].info.toucher.damage = 0; + if (player->unk_844 != 0) { + this->unk_258 = player->unk_845; + } + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + lastHealth = this->actor.colChkInfo.health; + if (Actor_ApplyDamage(&this->actor) == 0) { + EnDh_SetupDeath(this); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x90); + } else { + if (((lastHealth >= 15) && (this->actor.colChkInfo.health < 15)) || + ((lastHealth >= 9) && (this->actor.colChkInfo.health < 9)) || + ((lastHealth >= 3) && (this->actor.colChkInfo.health < 3))) { + + this->retreat++; + } + EnDh_SetupDamage(this); + } + } + } +} + +void EnDh_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDh* this = (EnDh*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 pad40; + + EnDh_CollisionCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 45.0f, 45.0f, 0x1D); + this->actor.focus.pos = this->headPos; + Collider_UpdateCylinder(&this->actor, &this->collider1); + if (this->actor.colChkInfo.health > 0) { + if (this->curAction == DH_WAIT) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + } else { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + } + if (((this->curAction != DH_DAMAGE) && (this->actor.shape.yOffset == 0.0f)) || + ((player->unk_844 != 0) && (player->unk_845 != this->unk_258))) { + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + } + } else { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + } +} + +void EnDh_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + Vec3f headOffset = { 2000.0f, 1000.0f, 0.0f }; + EnDh* this = (EnDh*)thisx; + + if (limbIndex == 13) { + Matrix_MultVec3f(&headOffset, &this->headPos); + Matrix_Push(); + Matrix_Translate(headOffset.x, headOffset.y, headOffset.z, MTXMODE_APPLY); + Collider_UpdateSpheres(1, &this->collider2); + Matrix_Pop(); + } +} + +void EnDh_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDh* this = (EnDh*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dh.c", 1099); + if (this->alpha == 255) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + POLY_OPA_DISP = + SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, NULL, EnDh_PostLimbDraw, &this->actor, POLY_OPA_DISP); + } else { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x08, &D_80116280[0]); + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, NULL, NULL, &this->actor, POLY_XLU_DISP); + } + if (this->drawDirtWave) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 85, 55, 0, 130); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (globalCtx->state.frames * -3) % 0x80, 0, 0x20, 0x40, 1, + (globalCtx->state.frames * -10) % 0x80, (globalCtx->state.frames * -20) % 0x100, + 0x20, 0x40)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 0, 0, 0, this->dirtWaveAlpha); + + Matrix_Translate(0.0f, -this->actor.shape.yOffset, 0.0f, MTXMODE_APPLY); + Matrix_Scale(this->dirtWaveSpread * 0.01f, this->dirtWaveHeight * 0.01f, this->dirtWaveSpread * 0.01f, + MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dh.c", 1160), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_dh_DL_007FC0); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dh.c", 1166); +} diff --git a/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.h b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.h new file mode 100644 index 000000000..2e8265447 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dh/z_en_dh.h @@ -0,0 +1,46 @@ +#ifndef Z_EN_DH_H +#define Z_EN_DH_H + +#include "ultra64.h" +#include "global.h" + +struct EnDh; + +typedef void (*EnDhActionFunc)(struct EnDh*, GlobalContext*); + +typedef struct EnDh { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0170 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s limbRotTable[16]; + /* 0x0250 */ u8 actionState; + /* 0x0251 */ u8 retreat; + /* 0x0252 */ u8 drawDirtWave; + /* 0x0254 */ EnDhActionFunc actionFunc; + /* 0x0258 */ u8 unk_258; // Related to player->unk_845 + /* 0x0259 */ u8 unused_259; + /* 0x025A */ u8 alpha; + /* 0x025B */ u8 curAction; + /* 0x025C */ s16 timer; + /* 0x025E */ s16 dirtWavePhase; + /* 0x0260 */ ColliderCylinder collider1; + /* 0x02AC */ ColliderJntSph collider2; + /* 0x02CC */ ColliderJntSphElement elements[1]; + /* 0x030C */ Vec3f headPos; + /* 0x0318 */ f32 dirtWaveSpread; + /* 0x031C */ f32 dirtWaveHeight; + /* 0x0320 */ f32 dirtWaveAlpha; +} EnDh; // size = 0x0324 + +typedef enum { + ENDH_HANDS_KILLED_4 = -4, + ENDH_HANDS_KILLED_3, + ENDH_HANDS_KILLED_2, + ENDH_HANDS_KILLED_1, + ENDH_WAIT_UNDERGROUND, + ENDH_START_ATTACK_GRAB, + ENDH_START_ATTACK_BOMB = 5, + ENDH_DEATH = 10 +} EnDhParams; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c b/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c new file mode 100644 index 000000000..740f855f0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.c @@ -0,0 +1,466 @@ +/* + * File: z_en_dha.c + * Overlay: ovl_En_Dha + * Description: Dead Hand's Hand + */ + +#include "z_en_dha.h" +#include "overlays/actors/ovl_En_Dh/z_en_dh.h" +#include "objects/object_dh/object_dh.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnDha_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDha_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDha_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDha_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDha_SetupWait(EnDha* this); +void EnDha_Wait(EnDha* this, GlobalContext* globalCtx); +void EnDha_SetupTakeDamage(EnDha* this); +void EnDha_TakeDamage(EnDha* this, GlobalContext* globalCtx); +void EnDha_SetupDeath(EnDha* this); +void EnDha_Die(EnDha* this, GlobalContext* globalCtx); +void EnDha_UpdateHealth(EnDha* this, GlobalContext* globalCtx); + +const ActorInit En_Dha_InitVars = { + ACTOR_EN_DHA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DH, + sizeof(EnDha), + (ActorFunc)EnDha_Init, + (ActorFunc)EnDha_Destroy, + (ActorFunc)EnDha_Update, + (ActorFunc)EnDha_Draw, + NULL, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0xF), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(2, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(2, 0xF), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(4, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xF), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 1, { { 0, 0, 0 }, 12 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 2, { { 3200, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 3, { { 1200, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 4, { { 2700, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 5, { { 1200, 0, 0 }, 10 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER | OC1_TYPE_1, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 5, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x2E, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_STOP), +}; + +void EnDha_SetupAction(EnDha* this, EnDhaActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnDha_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDha* this = (EnDha*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = &sDamageTable; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_dh_Skel_000BD8, &object_dh_Anim_0015B0, this->jointTable, + this->morphTable, 4); + ActorShape_Init(&this->actor.shape, 0, ActorShadow_DrawFeet, 90.0f); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 50.0f; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.colChkInfo.health = 8; + this->limbAngleX[0] = -0x4000; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItem); + this->actor.flags &= ~ACTOR_FLAG_0; + + EnDha_SetupWait(this); +} + +void EnDha_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDha* this = (EnDha*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnDha_SetupWait(EnDha* this) { + Animation_PlayLoop(&this->skelAnime, &object_dh_Anim_0015B0); + this->unk_1C0 = 0; + this->actionTimer = ((Rand_ZeroOne() * 10.0f) + 5.0f); + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.home.rot.z = 1; + EnDha_SetupAction(this, EnDha_Wait); +} + +void EnDha_Wait(EnDha* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; // unused + Vec3f armPosMultiplier1 = { 0.0f, 0.0f, 55.0f }; + Vec3f armPosMultiplier2 = { 0.0f, 0.0f, -54.0f }; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s32 pad2; + Vec3f playerPos = player->actor.world.pos; + Vec3s angle; + s16 yaw; + + playerPos.x += Math_SinS(player->actor.shape.rot.y) * -5.0f; + playerPos.z += Math_CosS(player->actor.shape.rot.y) * -5.0f; + + if (!LINK_IS_ADULT) { + playerPos.y += 38.0f; + } else { + playerPos.y += 56.0f; + } + + if (this->actor.xzDistToPlayer <= 100.0f) { + this->handAngle.y = this->handAngle.x = this->limbAngleY = 0; + + if (Math_Vec3f_DistXYZ(&playerPos, &this->handPos[0]) <= 12.0f) { + if (this->unk_1CC == 0) { + if (globalCtx->grabPlayer(globalCtx, player)) { + this->timer = 0; + this->unk_1CC++; + + if (this->actor.parent != NULL) { + this->actor.parent->params = ENDH_START_ATTACK_GRAB; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_GRIP); + } + } else { + this->timer += 0x1194; + this->limbAngleY = Math_SinS(this->timer) * 1820.0f; + + if (!(player->stateFlags2 & 0x80)) { + this->unk_1CC = 0; + EnDha_SetupTakeDamage(this); + return; + } + + if (this->timer < -0x6E6B) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_GRIP); + } + } + + func_80035844(&this->handPos[1], &playerPos, &this->handAngle, 0); + this->handAngle.y -= this->actor.shape.rot.y + this->limbAngleY; + this->handAngle.x -= this->actor.shape.rot.x + this->limbAngleX[0] + this->limbAngleX[1]; + } else { + if ((player->stateFlags2 & 0x80) && (&this->actor == player->actor.parent)) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + } + + if (this->actor.home.rot.z != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_HAND_AT); + this->actor.home.rot.z = 0; + } + } + + this->actor.shape.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &playerPos); + + Math_SmoothStepToF(&this->handPos[0].x, playerPos.x, 1.0f, 16.0f, 0.0f); + Math_SmoothStepToF(&this->handPos[0].y, playerPos.y, 1.0f, 16.0f, 0.0f); + Math_SmoothStepToF(&this->handPos[0].z, playerPos.z, 1.0f, 16.0f, 0.0f); + + func_80035844(&this->armPos, &this->handPos[0], &angle, 0); + Matrix_Translate(this->handPos[0].x, this->handPos[0].y, this->handPos[0].z, MTXMODE_NEW); + Matrix_RotateZYX(angle.x, angle.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&armPosMultiplier2, &this->armPos); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + func_80035844(&this->actor.world.pos, &this->armPos, &angle, 0); + Matrix_RotateZYX(angle.x, angle.y, 0, MTXMODE_APPLY); + Matrix_MultVec3f(&armPosMultiplier1, &this->armPos); + this->limbAngleX[0] = Math_Vec3f_Pitch(&this->actor.world.pos, &this->armPos); + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->armPos) - this->actor.shape.rot.y; + + if (ABS(yaw) >= 0x4000) { + this->limbAngleX[0] = -0x8000 - this->limbAngleX[0]; + } + + this->limbAngleX[1] = (Math_Vec3f_Pitch(&this->armPos, &this->handPos[0]) - this->limbAngleX[0]); + + if (this->limbAngleX[1] < 0) { + this->limbAngleX[0] += this->limbAngleX[1] * 2; + this->limbAngleX[1] *= -2; + } + } else { + if ((player->stateFlags2 & 0x80) && (&this->actor == player->actor.parent)) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + } + + this->actor.home.rot.z = 1; + Math_SmoothStepToS(&this->limbAngleX[1], 0, 1, 0x3E8, 0); + Math_SmoothStepToS(&this->limbAngleX[0], -0x4000, 1, 0x3E8, 0); + SkelAnime_Update(&this->skelAnime); + } +} + +void EnDha_SetupTakeDamage(EnDha* this) { + this->actionTimer = 15; + EnDha_SetupAction(this, EnDha_TakeDamage); +} + +void EnDha_TakeDamage(EnDha* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags2 & 0x80) && (&this->actor == player->actor.parent)) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + } + + Math_SmoothStepToS(&this->limbAngleX[1], 0, 1, 2000, 0); + Math_SmoothStepToS(&this->limbAngleY, 0, 1, 600, 0); + Math_SmoothStepToS(&this->limbAngleX[0], -0x4000, 1, 2000, 0); + SkelAnime_Update(&this->skelAnime); + this->actionTimer--; + + if (this->actionTimer == 0) { + EnDha_SetupWait(this); + } +} + +void EnDha_SetupDeath(EnDha* this) { + this->unk_1C0 = 8; + this->actionTimer = 300; + + if (this->actor.parent != NULL) { + if (this->actor.parent->params != ENDH_DEATH) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_HAND_DEAD); + } + if (this->actor.parent->params <= ENDH_WAIT_UNDERGROUND) { + this->actor.parent->params--; + } + } + + EnDha_SetupAction(this, EnDha_Die); +} + +void EnDha_Die(EnDha* this, GlobalContext* globalCtx) { + s16 angle; + Vec3f vec; + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags2 & 0x80) && (&this->actor == player->actor.parent)) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + } + + Math_SmoothStepToS(&this->limbAngleX[1], 0, 1, 0x7D0, 0); + angle = Math_SmoothStepToS(&this->limbAngleX[0], -0x4000, 1, 0x7D0, 0); + SkelAnime_Update(&this->skelAnime); + + if (angle == 0) { + vec = this->actor.world.pos; + + if (this->actionTimer != 0) { + if (-12000.0f < this->actor.shape.yOffset) { + this->actor.shape.yOffset -= 1000.0f; + func_80033480(globalCtx, &vec, 7.0f, 1, 0x5A, 0x14, 1); + } else { + this->actionTimer--; + + if ((this->actor.parent != NULL) && (this->actor.parent->params == ENDH_DEATH)) { + Actor_Kill(&this->actor); + } + } + } else { + this->actor.shape.yOffset += 500.0f; + func_80033480(globalCtx, &vec, 7.0f, 1, 0x5A, 0x14, 1); + + if (this->actor.shape.yOffset == 0.0f) { + EnDha_SetupWait(this); + } + } + } +} + +void EnDha_UpdateHealth(EnDha* this, GlobalContext* globalCtx) { + if (!((this->unk_1C0 >= 8) || !(this->collider.base.acFlags & AC_HIT))) { + this->collider.base.acFlags &= ~AC_HIT; + + if (this->actor.colChkInfo.damageEffect == 0 || this->actor.colChkInfo.damageEffect == 6) { + return; + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + if (Actor_ApplyDamage(&this->actor) == 0) { + EnDha_SetupDeath(this); + this->actor.colChkInfo.health = 8; + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xE0); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEADHAND_DAMAGE); + this->unk_1C0 = 9; + EnDha_SetupTakeDamage(this); + } + } + } + + if ((this->actor.parent != NULL) && (this->actor.parent->params == ENDH_DEATH)) { + EnDha_SetupDeath(this); + } +} + +void EnDha_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDha* this = (EnDha*)thisx; + + if (this->actor.parent == NULL) { + this->actor.parent = Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_DH, ACTORCAT_ENEMY, 10000.0f); + } + + EnDha_UpdateHealth(this, globalCtx); + this->actionFunc(this, globalCtx); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnDha_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnDha* this = (EnDha*)thisx; + + if (limbIndex == 1) { + rot->y = -(s16)(this->limbAngleX[0] + 0x4000); + rot->z += this->limbAngleY; + } else if (limbIndex == 2) { + rot->z = this->limbAngleX[1]; + rot->y -= this->limbAngleY; + } else if (limbIndex == 3) { + rot->y = -this->handAngle.y; + rot->z = -this->handAngle.x; + } + + return false; +} + +void EnDha_OverridePostDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + Vec3f handVec = { 1100.0f, 0.0f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + EnDha* this = (EnDha*)thisx; + + switch (limbIndex) { + case 1: + Collider_UpdateSpheres(2, &this->collider); + Collider_UpdateSpheres(3, &this->collider); + break; + case 2: + Collider_UpdateSpheres(4, &this->collider); + Collider_UpdateSpheres(5, &this->collider); + Matrix_MultVec3f(&zeroVec, &this->armPos); + break; + case 3: + Collider_UpdateSpheres(1, &this->collider); + Matrix_MultVec3f(&handVec, &this->handPos[0]); + Matrix_MultVec3f(&zeroVec, &this->handPos[1]); + break; + } +} + +void EnDha_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDha* this = (EnDha*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDha_OverrideLimbDraw, EnDha_OverridePostDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.h b/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.h new file mode 100644 index 000000000..7bc146f92 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dha/z_en_dha.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_DHA_H +#define Z_EN_DHA_H + +#include "ultra64.h" +#include "global.h" + +struct EnDha; + +typedef void (*EnDhaActionFunc)(struct EnDha*, GlobalContext*); + +typedef struct EnDha { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[4]; + /* 0x01A8 */ Vec3s morphTable[4]; + /* 0x01C0 */ u8 unk_1C0; + /* 0x01C4 */ EnDhaActionFunc actionFunc; + /* 0x01C8 */ s16 actionTimer; + /* 0x01CA */ s16 timer; + /* 0x01CC */ u8 unk_1CC; + /* 0x01CE */ s16 limbAngleX[2]; + /* 0x01D2 */ s16 limbAngleY; + /* 0x01D4 */ Vec3s handAngle; + /* 0x01DC */ Vec3f handPos[2]; + /* 0x01F4 */ Vec3f armPos; + /* 0x0200 */ ColliderJntSph collider; + /* 0x0220 */ ColliderJntSphElement colliderItem[5]; +} EnDha; // size = 0x0360 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c b/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c new file mode 100644 index 000000000..8475b4196 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.c @@ -0,0 +1,574 @@ +/* + * File: z_en_diving_game.c + * Overlay: ovl_En_Diving_Game + * Description: Diving minigame + */ + +#include "z_en_diving_game.h" +#include "overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h" +#include "objects/object_zo/object_zo.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnDivingGame_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDivingGame_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDivingGame_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDivingGame_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809EDCB0(EnDivingGame* this, GlobalContext* globalCtx); +void EnDivingGame_Talk(EnDivingGame* this, GlobalContext* globalCtx); +void EnDivingGame_HandlePlayChoice(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE048(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE0FC(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE194(EnDivingGame* this, GlobalContext* globalCtx); +void EnDivingGame_SetupRupeeThrow(EnDivingGame* this, GlobalContext* globalCtx); +void EnDivingGame_RupeeThrow(EnDivingGame* this, GlobalContext* globalCtx); +void EnDivingGame_SetupUnderwaterViewCs(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE780(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE800(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE8F0(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EE96C(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EEA00(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EEA90(EnDivingGame* this, GlobalContext* globalCtx); +void func_809EEAF8(EnDivingGame* this, GlobalContext* globalCtx); + +const ActorInit En_Diving_Game_InitVars = { + ACTOR_EN_DIVING_GAME, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZO, + sizeof(EnDivingGame), + (ActorFunc)EnDivingGame_Init, + (ActorFunc)EnDivingGame_Destroy, + (ActorFunc)EnDivingGame_Update, + (ActorFunc)EnDivingGame_Draw, + NULL, +}; + +// used to ensure there's only one instance of this actor. +static u8 sHasSpawned = false; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 10, 10, 0, { 0, 0, 0 } }, +}; + +static void* sEyeTextures[] = { + gZoraEyeOpenTex, + gZoraEyeHalfTex, + gZoraEyeClosedTex, +}; + +void EnDivingGame_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDivingGame* this = (EnDivingGame*)thisx; + + this->actor.gravity = -3.0f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZoraSkel, &gZoraIdleAnim, this->jointTable, this->morphTable, 20); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 素もぐりGO ☆☆☆☆☆ \n" VT_RST); + this->actor.room = -1; + this->actor.scale.x = 0.01f; + this->actor.scale.y = 0.012999999f; + this->actor.scale.z = 0.0139999995f; + if (sHasSpawned) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ もういてる原 ☆☆☆☆☆ \n" VT_RST); + this->unk_31F = 1; + Actor_Kill(&this->actor); + } else { + sHasSpawned = true; + this->actor.targetMode = 0; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actionFunc = func_809EDCB0; + } +} + +void EnDivingGame_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDivingGame* this = (EnDivingGame*)thisx; + + if (this->unk_31F == 0) { + sHasSpawned = false; + gSaveContext.timer1State = 0; + } + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnDivingGame_SpawnRuppy(EnDivingGame* this, GlobalContext* globalCtx) { + EnExRuppy* rupee; + Vec3f rupeePos; + + rupeePos.x = (Rand_ZeroOne() - 0.5f) * 30.0f + this->actor.world.pos.x; + rupeePos.y = (Rand_ZeroOne() - 0.5f) * 20.0f + (this->actor.world.pos.y + 30.0f); + rupeePos.z = (Rand_ZeroOne() - 0.5f) * 20.0f + this->actor.world.pos.z; + rupee = (EnExRuppy*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_RUPPY, rupeePos.x, + rupeePos.y, rupeePos.z, 0, (s16)Rand_CenteredFloat(3500.0f) - 1000, + this->rupeesLeftToThrow, 0); + if (rupee != NULL) { + rupee->actor.speedXZ = 12.0f; + rupee->actor.velocity.y = 6.0f; + } +} + +s32 EnDivingGame_HasMinigameFinished(EnDivingGame* this, GlobalContext* globalCtx) { + if (gSaveContext.timer1State == 10 && !Gameplay_InCsMode(globalCtx)) { + // Failed. + gSaveContext.timer1State = 0; + func_800F5B58(); + func_80078884(NA_SE_SY_FOUND); + this->actor.textId = 0x71AD; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_292 = TEXT_STATE_EVENT; + this->allRupeesThrown = this->state = this->phase = this->unk_2A2 = this->grabbedRupeesCounter = 0; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_809EE048; + return true; + } else { + s32 rupeesNeeded = 5; + + if (gSaveContext.eventChkInf[3] & 0x100) { + rupeesNeeded = 10; + } + if (this->grabbedRupeesCounter >= rupeesNeeded) { + // Won. + gSaveContext.timer1State = 0; + this->allRupeesThrown = this->state = this->phase = this->unk_2A2 = this->grabbedRupeesCounter = 0; + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + this->actor.textId = 0x4055; + } else { + this->actor.textId = 0x405D; + if (this->extraWinCount < 100) { + this->extraWinCount++; + } + } + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_292 = TEXT_STATE_EVENT; + func_800F5B58(); + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + func_8002DF54(globalCtx, NULL, 8); + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + this->actionFunc = func_809EE96C; + } else { + this->actionFunc = func_809EE048; + } + return true; + } + } + return false; +} + +// EnDivingGame_FinishMinigame ? // Reset probably +void func_809EDCB0(EnDivingGame* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gZoraIdleAnim); + + Animation_Change(&this->skelAnime, &gZoraIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->notPlayingMinigame = true; + this->actionFunc = EnDivingGame_Talk; +} + +void EnDivingGame_Talk(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->state != ENDIVINGGAME_STATE_PLAYING || !EnDivingGame_HasMinigameFinished(this, globalCtx)) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->unk_292 != TEXT_STATE_DONE) { + switch (this->state) { + case ENDIVINGGAME_STATE_NOTPLAYING: + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = EnDivingGame_HandlePlayChoice; + break; + case ENDIVINGGAME_STATE_AWARDPRIZE: + this->actionFunc = func_809EEA00; + break; + case ENDIVINGGAME_STATE_PLAYING: + this->actionFunc = func_809EE8F0; + break; + } + } + } else { + if (Text_GetFaceReaction(globalCtx, 0x1D) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 0x1D); + this->unk_292 = TEXT_STATE_DONE; + } else { + switch (this->state) { + case ENDIVINGGAME_STATE_NOTPLAYING: + this->unk_292 = TEXT_STATE_CHOICE; + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + this->actor.textId = 0x4053; + this->phase = ENDIVINGGAME_PHASE_1; + } else { + this->actor.textId = 0x405C; + this->phase = ENDIVINGGAME_PHASE_2; + } + break; + case ENDIVINGGAME_STATE_AWARDPRIZE: + this->actor.textId = 0x4056; + this->unk_292 = TEXT_STATE_EVENT; + break; + case ENDIVINGGAME_STATE_PLAYING: + this->actor.textId = 0x405B; + this->unk_292 = TEXT_STATE_EVENT; + break; + } + } + func_8002F2CC(&this->actor, globalCtx, 80.0f); + } + } +} + +void EnDivingGame_HandlePlayChoice(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_292 == Message_GetState(&globalCtx->msgCtx) && + Message_ShouldAdvance(globalCtx)) { // Did player selected an answer? + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // Yes + if (gSaveContext.rupees >= 20) { + Rupees_ChangeBy(-20); + this->actor.textId = 0x4054; + } else { + this->actor.textId = 0x85; + this->allRupeesThrown = this->state = this->phase = this->unk_2A2 = this->grabbedRupeesCounter = 0; + } + break; + case 1: // No + this->actor.textId = 0x2D; + this->allRupeesThrown = this->state = this->phase = this->unk_2A2 = this->grabbedRupeesCounter = 0; + break; + } + if (!(gSaveContext.eventChkInf[3] & 0x100) || this->actor.textId == 0x85 || this->actor.textId == 0x2D) { + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_292 = TEXT_STATE_EVENT; + this->actionFunc = func_809EE048; + } else { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_809EE0FC; + } + } +} + +// Waits for the message to close +void func_809EE048(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_292 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + if (this->phase == ENDIVINGGAME_PHASE_ENDED) { + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = func_809EDCB0; + } else { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_809EE0FC; + } + } +} + +// another "start minigame" step +void func_809EE0FC(EnDivingGame* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gZoraThrowRupeesAnim); + + Animation_Change(&this->skelAnime, &gZoraThrowRupeesAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_ONCE, -10.0f); + this->notPlayingMinigame = false; + this->actionFunc = func_809EE194; +} + +// Wait a bit before start throwing the rupees. +void func_809EE194(EnDivingGame* this, GlobalContext* globalCtx) { + f32 currentFrame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (currentFrame >= 15.0f) { + this->actionFunc = EnDivingGame_SetupRupeeThrow; + } +} + +void EnDivingGame_SetupRupeeThrow(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + this->subCamId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->subCamId, CAM_STAT_ACTIVE); + this->spawnRuppyTimer = 10; + this->unk_2F4.x = -210.0f; + this->unk_2F4.y = -80.0f; + this->unk_2F4.z = -1020.0f; + this->unk_2D0.x = -280.0f; + this->unk_2D0.y = -20.0f; + this->unk_2D0.z = -240.0f; + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + this->rupeesLeftToThrow = 5; + } else { + this->rupeesLeftToThrow = 10; + } + this->unk_2DC.x = this->unk_2DC.y = this->unk_2DC.z = this->unk_300.x = this->unk_300.y = this->unk_300.z = 0.1f; + this->camLookAt.x = globalCtx->view.lookAt.x; + this->camLookAt.y = globalCtx->view.lookAt.y; + this->camLookAt.z = globalCtx->view.lookAt.z; + this->camEye.x = globalCtx->view.eye.x; + this->camEye.y = globalCtx->view.eye.y + 80.0f; + this->camEye.z = globalCtx->view.eye.z + 250.0f; + this->unk_2E8.x = fabsf(this->camEye.x - this->unk_2D0.x) * 0.04f; + this->unk_2E8.y = fabsf(this->camEye.y - this->unk_2D0.y) * 0.04f; + this->unk_2E8.z = fabsf(this->camEye.z - this->unk_2D0.z) * 0.04f; + this->unk_30C.x = fabsf(this->camLookAt.x - this->unk_2F4.x) * 0.04f; + this->unk_30C.y = fabsf(this->camLookAt.y - this->unk_2F4.y) * 0.04f; + this->unk_30C.z = fabsf(this->camLookAt.z - this->unk_2F4.z) * 0.04f; + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->camLookAt, &this->camEye); + Gameplay_CameraSetFov(globalCtx, this->subCamId, globalCtx->mainCamera.fov); + this->csCameraTimer = 60; + this->actionFunc = EnDivingGame_RupeeThrow; + this->unk_318 = 0.0f; +} + +// Throws rupee when this->spawnRuppyTimer == 0 +void EnDivingGame_RupeeThrow(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (func_800C0DB4(globalCtx, &this->actor.projectedPos)) { + Audio_SetExtraFilter(0); + } + if (this->subCamId != 0) { + Math_ApproachF(&this->camEye.x, this->unk_2D0.x, this->unk_2DC.x, this->unk_2E8.x * this->unk_318); + Math_ApproachF(&this->camEye.z, this->unk_2D0.z, this->unk_2DC.z, this->unk_2E8.z * this->unk_318); + Math_ApproachF(&this->camLookAt.x, this->unk_2F4.x, this->unk_300.x, this->unk_30C.x * this->unk_318); + Math_ApproachF(&this->camLookAt.y, this->unk_2F4.y, this->unk_300.y, this->unk_30C.y * this->unk_318); + Math_ApproachF(&this->camLookAt.z, this->unk_2F4.z, this->unk_300.z, this->unk_30C.z * this->unk_318); + Math_ApproachF(&this->unk_318, 1.0f, 1.0f, 0.02f); + } + Gameplay_CameraSetAtEye(globalCtx, this->subCamId, &this->camLookAt, &this->camEye); + if (!this->allRupeesThrown && this->spawnRuppyTimer == 0) { + this->spawnRuppyTimer = 5; + EnDivingGame_SpawnRuppy(this, globalCtx); + this->rupeesLeftToThrow--; + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + this->unk_296 = 30; + } else { + this->unk_296 = 5; + } + if (this->rupeesLeftToThrow <= 0) { + this->rupeesLeftToThrow = 0; + this->allRupeesThrown = true; + } + } + if (this->csCameraTimer == 0 || + ((fabsf(this->camEye.x - this->unk_2D0.x) < 2.0f) && (fabsf(this->camEye.y - this->unk_2D0.y) < 2.0f) && + (fabsf(this->camEye.z - this->unk_2D0.z) < 2.0f) && (fabsf(this->camLookAt.x - this->unk_2F4.x) < 2.0f) && + (fabsf(this->camLookAt.y - this->unk_2F4.y) < 2.0f) && (fabsf(this->camLookAt.z - this->unk_2F4.z) < 2.0f))) { + if (this->unk_2A2 != 0) { + this->csCameraTimer = 70; + this->unk_2A2 = 2; + this->actionFunc = func_809EE780; + } else { + this->actionFunc = EnDivingGame_SetupUnderwaterViewCs; + } + } +} + +// Called just before changing the camera to focus the underwater rupees. +void EnDivingGame_SetupUnderwaterViewCs(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_296 == 0) { + this->unk_2A2 = 1; + this->csCameraTimer = 100; + this->actionFunc = EnDivingGame_RupeeThrow; + this->camLookAt.x = this->unk_2F4.x = -210.0f; + this->camLookAt.y = this->unk_2F4.y = -80.0f; + this->camLookAt.z = this->unk_2F4.z = -1020.0f; + this->camEye.x = this->unk_2D0.x = -280.0f; + this->camEye.y = this->unk_2D0.y = -20.0f; + this->camEye.z = this->unk_2D0.z = -240.0f; + } +} + +// EnDivingGame_SayStartAndWait ? +void func_809EE780(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->csCameraTimer == 0) { + Gameplay_ClearCamera(globalCtx, this->subCamId); + Gameplay_ChangeCameraStatus(globalCtx, 0, CAM_STAT_ACTIVE); + this->actor.textId = 0x405A; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_292 = TEXT_STATE_EVENT; + this->actionFunc = func_809EE800; + } +} + +// EnDivingGame_TalkDuringMinigame +void func_809EE800(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_292 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + func_80088B34(BREG(2) + 50); + } else { + func_80088B34(BREG(2) + 50); + } + func_800F5ACC(NA_BGM_TIMED_MINI_GAME); + func_8002DF54(globalCtx, NULL, 7); + this->actor.textId = 0x405B; + this->unk_292 = TEXT_STATE_EVENT; + this->state = ENDIVINGGAME_STATE_PLAYING; + this->actionFunc = EnDivingGame_Talk; + } +} + +void func_809EE8F0(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((this->unk_292 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx))) { + Message_CloseTextbox(globalCtx); + this->actionFunc = EnDivingGame_Talk; + } else { + EnDivingGame_HasMinigameFinished(this, globalCtx); + } +} + +// EnDivingGame_SayCongratsAndWait ? // EnDivingGame_PlayerWonPhase1 +void func_809EE96C(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((this->unk_292 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx))) { + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, NULL, 7); + this->actor.textId = 0x4056; + this->unk_292 = TEXT_STATE_EVENT; + this->state = ENDIVINGGAME_STATE_AWARDPRIZE; + this->actionFunc = EnDivingGame_Talk; + } +} + +void func_809EEA00(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((this->unk_292 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx))) { + Message_CloseTextbox(globalCtx); + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, GI_SCALE_SILVER, 90.0f, 10.0f); + this->actionFunc = func_809EEA90; + } +} + +void func_809EEA90(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = func_809EEAF8; + } else { + func_8002F434(&this->actor, globalCtx, GI_SCALE_SILVER, 90.0f, 10.0f); + } +} + +// Award the scale? +void func_809EEAF8(EnDivingGame* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + // "Successful completion" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); + this->allRupeesThrown = this->state = this->phase = this->unk_2A2 = this->grabbedRupeesCounter = 0; + gSaveContext.eventChkInf[3] |= 0x100; + this->actionFunc = func_809EDCB0; + } +} + +void EnDivingGame_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnDivingGame* this = (EnDivingGame*)thisx; + Player* player = GET_PLAYER(globalCtx); + Vec3f pos; + + if (this->csCameraTimer != 0) { + this->csCameraTimer--; + } + if (this->unk_296 != 0) { + this->unk_296--; + } + if (this->eyeTimer != 0) { + this->eyeTimer--; + } + if (this->spawnRuppyTimer != 0) { + this->spawnRuppyTimer--; + } + + if (1) {} + + if (gSaveContext.timer1Value == 10) { + func_800F5918(); + } + if (this->eyeTimer == 0) { + this->eyeTimer = 2; + this->eyeTexIndex++; + if (this->eyeTexIndex >= 3) { + this->eyeTexIndex = 0; + this->eyeTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, 80.0f); + this->unk_324.unk_18 = player->actor.world.pos; + this->unk_324.unk_18.y = player->actor.world.pos.y; + func_80034A14(&this->actor, &this->unk_324, 2, 4); + this->vec_284 = this->unk_324.unk_08; + this->vec_28A = this->unk_324.unk_0E; + if ((globalCtx->gameplayFrames % 16) == 0) { + pos = this->actor.world.pos; + pos.y += 20.0f; + EffectSsGRipple_Spawn(globalCtx, &pos, 100, 500, 30); + } + this->unk_290++; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 29); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +Gfx* EnDivingGame_EmptyDList(GraphicsContext* gfxCtx) { + Gfx* displayList = Graph_Alloc(gfxCtx, sizeof(Gfx)); + + gSPEndDisplayList(displayList); + return displayList; +} + +s32 EnDivingGame_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnDivingGame* this = (EnDivingGame*)thisx; + s32 pad; + + if (limbIndex == 6) { + rot->x += this->vec_28A.y; + } + + if (limbIndex == 15) { + rot->x += this->vec_284.y; + rot->z += this->vec_284.z; + } + + if (this->notPlayingMinigame && (limbIndex == 8 || limbIndex == 9 || limbIndex == 12)) { + rot->y += Math_SinS((globalCtx->state.frames * (limbIndex * 50 + 0x814))) * 200.0f; + rot->z += Math_CosS((globalCtx->state.frames * (limbIndex * 50 + 0x940))) * 200.0f; + } + + return 0; +} + +void EnDivingGame_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDivingGame* this = (EnDivingGame*)thisx; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_diving_game.c", 1212); + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, EnDivingGame_EmptyDList(globalCtx->state.gfxCtx)); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeTexIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDivingGame_OverrideLimbDraw, NULL, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_diving_game.c", 1232); +} diff --git a/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.h b/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.h new file mode 100644 index 000000000..ee361adaa --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Diving_Game/z_en_diving_game.h @@ -0,0 +1,64 @@ +#ifndef Z_EN_DIVING_GAME_H +#define Z_EN_DIVING_GAME_H + +#include "ultra64.h" +#include "global.h" + +struct EnDivingGame; + +typedef void (*EnDivingGameActionFunc)(struct EnDivingGame*, GlobalContext*); + +typedef struct EnDivingGame { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[20]; + /* 0x0208 */ Vec3s morphTable[20]; + /* 0x0280 */ EnDivingGameActionFunc actionFunc; + /* 0x0284 */ Vec3s vec_284; + /* 0x028A */ Vec3s vec_28A; + /* 0x0290 */ s16 unk_290; // counter that only goes up + /* 0x0292 */ s16 unk_292; + /* 0x0294 */ s16 csCameraTimer; + /* 0x0296 */ s16 unk_296; // read by ExRuppy // timer? + /* 0x0298 */ s16 eyeTimer; + /* 0x029A */ s16 spawnRuppyTimer; + /* 0x029C */ s16 phase; + /* 0x029E */ s16 eyeTexIndex; + /* 0x02A0 */ s16 subCamId; + /* 0x02A2 */ s16 unk_2A2; // 0: , 1: , 2: Tells rupees to sink in water + /* 0x02A4 */ s16 grabbedRupeesCounter; + /* 0x02A6 */ s16 rupeesLeftToThrow; + /* 0x02A8 */ s16 state; // minigameState? 0: default, 1: waiting to give the scale, 2: minigame started + /* 0x02AA */ s16 extraWinCount; // counts how many times you have beaten the minigame **after** you got the scale. ExRuppy will reset it to zero if a 500 rupee is spawned. + /* 0x02AC */ char unk_2AC[0xC]; // probably another Vec3f, but unused. + /* 0x02B8 */ Vec3f camLookAt; + /* 0x02C4 */ Vec3f camEye; + /* 0x02D0 */ Vec3f unk_2D0; + /* 0x02DC */ Vec3f unk_2DC; + /* 0x02E8 */ Vec3f unk_2E8; + /* 0x02F4 */ Vec3f unk_2F4; + /* 0x0300 */ Vec3f unk_300; + /* 0x030C */ Vec3f unk_30C; + /* 0x0318 */ f32 unk_318; + /* 0x031C */ char unk_31C; // unused + /* 0x031D */ u8 notPlayingMinigame; // flag + /* 0x031E */ u8 allRupeesThrown; // flag + /* 0x031F */ u8 unk_31F; // flag + /* 0x0320 */ char unk_320[0x4]; // unused + /* 0x0324 */ struct_80034A14_arg1 unk_324; + /* 0x034C */ ColliderCylinder collider; +} EnDivingGame; // size = 0x0398 + +typedef enum { + /* 0 */ ENDIVINGGAME_PHASE_ENDED, + /* 1 */ ENDIVINGGAME_PHASE_1, // Player has not received the scale. + /* 2 */ ENDIVINGGAME_PHASE_2 // Player got the scale and there are 10 rupees thrown. +} EnDivingGamePhase; + +typedef enum { + /* 0 */ ENDIVINGGAME_STATE_NOTPLAYING, + /* 1 */ ENDIVINGGAME_STATE_AWARDPRIZE, // Waiting to give the scale to player. + /* 2 */ ENDIVINGGAME_STATE_PLAYING +} EnDivingGameState; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c b/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c new file mode 100644 index 000000000..678556b41 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.c @@ -0,0 +1,501 @@ +/* + * File: z_en_dns.c + * Overlay: En_Dns + * Description: Deku Salesman + */ + +#include "z_en_dns.h" +#include "objects/object_shopnuts/object_shopnuts.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnDns_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDns_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDns_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDns_Draw(Actor* thisx, GlobalContext* globalCtx); + +u32 func_809EF5A4(EnDns* this); +u32 func_809EF658(EnDns* this); +u32 func_809EF70C(EnDns* this); +u32 func_809EF73C(EnDns* this); +u32 func_809EF800(EnDns* this); +u32 func_809EF854(EnDns* this); +u32 func_809EF8F4(EnDns* this); +u32 func_809EF9A4(EnDns* this); + +void func_809EF9F8(EnDns* this); +void func_809EFA28(EnDns* this); +void func_809EFA58(EnDns* this); +void func_809EFA9C(EnDns* this); +void func_809EFACC(EnDns* this); +void func_809EFAFC(EnDns* this); +void func_809EFB40(EnDns* this); + +void EnDns_SetupWait(EnDns* this, GlobalContext* globalCtx); +void EnDns_Wait(EnDns* this, GlobalContext* globalCtx); +void EnDns_Talk(EnDns* this, GlobalContext* globalCtx); +void func_809EFDD0(EnDns* this, GlobalContext* globalCtx); +void func_809EFEE8(EnDns* this, GlobalContext* globalCtx); +void func_809EFF50(EnDns* this, GlobalContext* globalCtx); +void func_809EFF98(EnDns* this, GlobalContext* globalCtx); +void func_809F008C(EnDns* this, GlobalContext* globalCtx); +void EnDns_SetupBurrow(EnDns* this, GlobalContext* globalCtx); +void EnDns_Burrow(EnDns* this, GlobalContext* globalCtx); + +const ActorInit En_Dns_InitVars = { + ACTOR_EN_DNS, + ACTORCAT_BG, + FLAGS, + OBJECT_SHOPNUTS, + sizeof(EnDns), + (ActorFunc)EnDns_Init, + (ActorFunc)EnDns_Destroy, + (ActorFunc)EnDns_Update, + (ActorFunc)EnDns_Draw, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 18, 32, 0, { 0, 0, 0 } }, +}; + +static u16 D_809F040C[] = { + 0x10A0, 0x10A1, 0x10A2, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF, 0x10DC, 0x10DD, +}; + +// Debug text: "sells" { "Deku Nuts", "Deku Sticks", "Piece of Heart", "Deku Seeds", +// "Deku Shield", "Bombs", "Arrows", "Red Potion", +// "Green Potion", "Deku Stick Upgrade", "Deku Nut Upgrade" } +static char* D_809F0424[] = { + "デクの実売り ", "デクの棒売り ", "ハートの欠片売り ", "デクの種売り ", + "デクの盾売り ", "バクダン売り ", "矢売り ", "赤のくすり売り ", + "緑のくすり売り ", "デクの棒持てる数を増やす", "デクの実持てる数を増やす", +}; + +static DnsItemEntry D_809F0450 = { 20, 5, GI_NUTS_5_2, func_809EF5A4, func_809EFA28 }; + +static DnsItemEntry D_809F0460 = { 15, 1, GI_STICKS_1, func_809EF658, func_809EF9F8 }; + +static DnsItemEntry D_809F0470 = { 10, 1, GI_HEART_PIECE, func_809EF70C, func_809EFA58 }; + +static DnsItemEntry D_809F0480 = { 40, 30, GI_SEEDS_30, func_809EF73C, func_809EF9F8 }; + +static DnsItemEntry D_809F0490 = { 50, 1, GI_SHIELD_DEKU, func_809EF800, func_809EF9F8 }; + +static DnsItemEntry D_809F04A0 = { 40, 5, GI_BOMBS_5, func_809EF854, func_809EFA9C }; + +static DnsItemEntry D_809F04B0 = { 70, 20, GI_ARROWS_LARGE, func_809EF8F4, func_809EFACC }; + +static DnsItemEntry D_809F04C0 = { 40, 1, GI_POTION_RED, func_809EF9A4, func_809EF9F8 }; + +static DnsItemEntry D_809F04D0 = { 40, 1, GI_POTION_GREEN, func_809EF9A4, func_809EF9F8 }; + +static DnsItemEntry D_809F04E0 = { 40, 1, GI_STICK_UPGRADE_20, func_809EF70C, func_809EFAFC }; + +static DnsItemEntry D_809F04F0 = { 40, 1, GI_NUT_UPGRADE_30, func_809EF70C, func_809EFB40 }; + +static DnsItemEntry* sItemEntries[] = { + &D_809F0450, &D_809F0460, &D_809F0470, &D_809F0480, &D_809F0490, &D_809F04A0, + &D_809F04B0, &D_809F04C0, &D_809F04D0, &D_809F04E0, &D_809F04F0, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x4E, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +typedef enum { + /* 0 */ ENDNS_ANIM_0, + /* 1 */ ENDNS_ANIM_1, + /* 2 */ ENDNS_ANIM_2 +} EnDnsAnimation; + +static AnimationMinimalInfo sAnimationInfo[] = { + { &gBusinessScrubNervousIdleAnim, ANIMMODE_LOOP, 0.0f }, + { &gBusinessScrubAnim_4404, ANIMMODE_ONCE, 0.0f }, + { &gBusinessScrubNervousTransitionAnim, ANIMMODE_ONCE, 0.0f }, +}; + +void EnDns_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDns* this = (EnDns*)thisx; + + if (this->actor.params < 0) { + // "Function Error (Deku Salesman)" + osSyncPrintf(VT_FGCOL(RED) "引数エラー(売りナッツ)[ arg_data = %d ]" VT_RST "\n", this->actor.params); + Actor_Kill(&this->actor); + return; + } + // Sell Seeds instead of Arrows if Link is child + if ((this->actor.params == 0x0006) && (LINK_AGE_IN_YEARS == YEARS_CHILD)) { + this->actor.params = 0x0003; + } + // "Deku Salesman" + osSyncPrintf(VT_FGCOL(GREEN) "◆◆◆ 売りナッツ『%s』 ◆◆◆" VT_RST "\n", D_809F0424[this->actor.params], + this->actor.params); + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBusinessScrubSkel, &gBusinessScrubNervousTransitionAnim, + this->jointTable, this->morphTable, 18); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f); + this->actor.textId = D_809F040C[this->actor.params]; + Actor_SetScale(&this->actor, 0.01f); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->maintainCollider = 1; + this->standOnGround = 1; + this->dropCollectible = 0; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -1.0f; + this->dnsItemEntry = sItemEntries[this->actor.params]; + this->actionFunc = EnDns_SetupWait; +} + +void EnDns_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDns* this = (EnDns*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnDns_ChangeAnim(EnDns* this, u8 index) { + s16 frameCount; + + frameCount = Animation_GetLastFrame(sAnimationInfo[index].animation); + this->unk_2BA = index; // Not used anywhere else? + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, (f32)frameCount, + sAnimationInfo[index].mode, sAnimationInfo[index].morphFrames); +} + +/* Item give checking functions */ + +u32 func_809EF5A4(EnDns* this) { + if ((CUR_CAPACITY(UPG_NUTS) != 0) && (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS))) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + if (Item_CheckObtainability(ITEM_NUT) == ITEM_NONE) { + return 2; + } + return 4; +} + +u32 func_809EF658(EnDns* this) { + if ((CUR_CAPACITY(UPG_STICKS) != 0) && (AMMO(ITEM_STICK) >= CUR_CAPACITY(UPG_STICKS))) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + if (Item_CheckObtainability(ITEM_STICK) == ITEM_NONE) { + return 2; + } + return 4; +} + +u32 func_809EF70C(EnDns* this) { + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + return 4; +} + +u32 func_809EF73C(EnDns* this) { + if (INV_CONTENT(ITEM_SLINGSHOT) == ITEM_NONE) { + return 3; + } + if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + if (Item_CheckObtainability(ITEM_SEEDS) == ITEM_NONE) { + return 2; + } + return 4; +} + +u32 func_809EF800(EnDns* this) { + if (gBitFlags[4] & gSaveContext.inventory.equipment) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + return 4; +} + +u32 func_809EF854(EnDns* this) { + if (!CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 3; + } + if (AMMO(ITEM_BOMB) >= CUR_CAPACITY(UPG_BOMB_BAG)) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + return 4; +} + +u32 func_809EF8F4(EnDns* this) { + if (Item_CheckObtainability(ITEM_BOW) == ITEM_NONE) { + return 3; + } + if (AMMO(ITEM_BOW) >= CUR_CAPACITY(UPG_QUIVER)) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + return 4; +} + +u32 func_809EF9A4(EnDns* this) { + if (!Inventory_HasEmptyBottle()) { + return 1; + } + if (gSaveContext.rupees < this->dnsItemEntry->itemPrice) { + return 0; + } + return 4; +} + +/* Paying and flagging functions */ + +void func_809EF9F8(EnDns* this) { + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void func_809EFA28(EnDns* this) { + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void func_809EFA58(EnDns* this) { + gSaveContext.itemGetInf[0] |= 0x800; + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void func_809EFA9C(EnDns* this) { + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void func_809EFACC(EnDns* this) { + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void func_809EFAFC(EnDns* this) { + gSaveContext.infTable[25] |= 0x4; + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void func_809EFB40(EnDns* this) { + gSaveContext.infTable[25] |= 0x8; + Rupees_ChangeBy(-this->dnsItemEntry->itemPrice); +} + +void EnDns_SetupWait(EnDns* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame == this->skelAnime.endFrame) { + this->actionFunc = EnDns_Wait; + EnDns_ChangeAnim(this, ENDNS_ANIM_0); + } +} + +void EnDns_Wait(EnDns* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 2000, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnDns_Talk; + } else { + if ((this->collider.base.ocFlags1 & OC1_HIT) || this->actor.isTargeted) { + this->actor.flags |= ACTOR_FLAG_16; + } else { + this->actor.flags &= ~ACTOR_FLAG_16; + } + if (this->actor.xzDistToPlayer < 130.0f) { + func_8002F2F4(&this->actor, globalCtx); + } + } +} + +void EnDns_Talk(EnDns* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // OK + switch (this->dnsItemEntry->purchaseableCheck(this)) { + case 0: + Message_ContinueTextbox(globalCtx, 0x10A5); + this->actionFunc = func_809F008C; + break; + case 1: + Message_ContinueTextbox(globalCtx, 0x10A6); + this->actionFunc = func_809F008C; + break; + case 3: + Message_ContinueTextbox(globalCtx, 0x10DE); + this->actionFunc = func_809F008C; + break; + case 2: + case 4: + Message_ContinueTextbox(globalCtx, 0x10A7); + this->actionFunc = func_809EFEE8; + break; + } + break; + case 1: // No way + Message_ContinueTextbox(globalCtx, 0x10A4); + this->actionFunc = func_809F008C; + } + } +} + +void func_809EFDD0(EnDns* this, GlobalContext* globalCtx) { + if (this->actor.params == 0x9) { + if (CUR_UPG_VALUE(UPG_STICKS) < 2) { + func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_20, 130.0f, 100.0f); + } else { + func_8002F434(&this->actor, globalCtx, GI_STICK_UPGRADE_30, 130.0f, 100.0f); + } + } else if (this->actor.params == 0xA) { + if (CUR_UPG_VALUE(UPG_NUTS) < 2) { + func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_30, 130.0f, 100.0f); + } else { + func_8002F434(&this->actor, globalCtx, GI_NUT_UPGRADE_40, 130.0f, 100.0f); + } + } else { + func_8002F434(&this->actor, globalCtx, this->dnsItemEntry->getItemId, 130.0f, 100.0f); + } +} + +void func_809EFEE8(EnDns* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + func_809EFDD0(this, globalCtx); + this->actionFunc = func_809EFF50; + } +} + +void func_809EFF50(EnDns* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_809EFF98; + } else { + func_809EFDD0(this, globalCtx); + } +} + +void func_809EFF98(EnDns* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags1 & 0x400) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->dnsItemEntry->setRupeesAndFlags(this); + this->dropCollectible = 1; + this->maintainCollider = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + EnDns_ChangeAnim(this, ENDNS_ANIM_1); + this->actionFunc = EnDns_SetupBurrow; + } + } else { + this->dnsItemEntry->setRupeesAndFlags(this); + this->dropCollectible = 1; + this->maintainCollider = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + EnDns_ChangeAnim(this, ENDNS_ANIM_1); + this->actionFunc = EnDns_SetupBurrow; + } +} + +void func_809F008C(EnDns* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->maintainCollider = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + EnDns_ChangeAnim(this, ENDNS_ANIM_1); + this->actionFunc = EnDns_SetupBurrow; + } +} + +void EnDns_SetupBurrow(EnDns* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gBusinessScrubAnim_4404); + + if (this->skelAnime.curFrame == frameCount) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_AKINDONUTS_HIDE); + this->actionFunc = EnDns_Burrow; + this->standOnGround = 0; + this->yInitPos = this->actor.world.pos.y; + } +} + +void EnDns_Burrow(EnDns* this, GlobalContext* globalCtx) { + f32 depth; + Vec3f initPos; + s32 i; + + depth = this->yInitPos - this->actor.world.pos.y; + if ((this->dustTimer & 3) == 0) { + initPos.x = this->actor.world.pos.x; + initPos.y = this->yInitPos; + initPos.z = this->actor.world.pos.z; + func_80028990(globalCtx, 20.0f, &initPos); + } + this->actor.shape.rot.y += 0x2000; + // Drops only if you bought its item + if (depth > 400.0f) { + if (this->dropCollectible) { + initPos.x = this->actor.world.pos.x; + initPos.y = this->yInitPos; + initPos.z = this->actor.world.pos.z; + for (i = 0; i < 3; i++) { + Item_DropCollectible(globalCtx, &initPos, ITEM00_HEART); + } + } + Actor_Kill(&this->actor); + } +} + +void EnDns_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDns* this = (EnDns*)thisx; + s16 pad; + + this->dustTimer++; + this->actor.textId = D_809F040C[this->actor.params]; + Actor_SetFocus(&this->actor, 60.0f); + Actor_SetScale(&this->actor, 0.01f); + SkelAnime_Update(&this->skelAnime); + Actor_MoveForward(&this->actor); + this->actionFunc(this, globalCtx); + if (this->standOnGround) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 4); + } + if (this->maintainCollider) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnDns_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDns* this = (EnDns*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, &this->actor); +} diff --git a/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.h b/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.h new file mode 100644 index 000000000..90bd3d095 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dns/z_en_dns.h @@ -0,0 +1,37 @@ +#ifndef Z_EN_DNS_H +#define Z_EN_DNS_H + +#include "ultra64.h" +#include "global.h" + +struct EnDns; + +typedef void (*EnDnsActionFunc)(struct EnDns*, GlobalContext*); +typedef u32 (*EnDnsPurchaseableCheck)(struct EnDns*); +typedef void (*EnDnsSetRupeesAndFlags)(struct EnDns*); + +typedef struct { + /* 0x00 */ s16 itemPrice; + /* 0x02 */ u16 itemAmount; + /* 0x04 */ s32 getItemId; + /* 0x08 */ EnDnsPurchaseableCheck purchaseableCheck; + /* 0x0C */ EnDnsSetRupeesAndFlags setRupeesAndFlags; +} DnsItemEntry; // size = 0x10 + +typedef struct EnDns { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[18]; + /* 0x01FC */ Vec3s morphTable[18]; + /* 0x0268 */ EnDnsActionFunc actionFunc; + /* 0x026C */ ColliderCylinder collider; + /* 0x02B8 */ s16 dustTimer; + /* 0x02BA */ u8 unk_2BA; + /* 0x02BB */ u8 maintainCollider; + /* 0x02BC */ u8 standOnGround; + /* 0x02BD */ u8 dropCollectible; + /* 0x02C0 */ DnsItemEntry* dnsItemEntry; + /* 0x02C4 */ f32 yInitPos; +} EnDns; // size = 0x02C8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c new file mode 100644 index 000000000..1fd696b1d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.c @@ -0,0 +1,339 @@ +/* + * File: z_en_dnt_demo.c + * Overlay: ovl_En_Dnt_Demo + * Description: Forest Stage minigame + */ + +#include "z_en_dnt_demo.h" + +#include "overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.h" +#include "overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h" +#include "vt.h" + +#define FLAGS 0 + +typedef enum { + /* 0 */ DNT_LIKE, + /* 1 */ DNT_HATE, + /* 2 */ DNT_LOVE +} EnDntDemoResults; + +void EnDntDemo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDntDemo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDntDemo_Update(Actor* this, GlobalContext* globalCtx); + +void EnDntDemo_Judge(EnDntDemo* this, GlobalContext* globalCtx); +void EnDntDemo_Results(EnDntDemo* this, GlobalContext* globalCtx); +void EnDntDemo_Prize(EnDntDemo* this, GlobalContext* globalCtx); + +const ActorInit En_Dnt_Demo_InitVars = { + ACTOR_EN_DNT_DEMO, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnDntDemo), + (ActorFunc)EnDntDemo_Init, + (ActorFunc)EnDntDemo_Destroy, + (ActorFunc)EnDntDemo_Update, + NULL, + NULL, +}; + +//! @bug +//! This table is missing a column for the Mask of Truth, so it reads the first value of the next row. In the last row, +//! it reads the first entry of sResultValues (4), which is an invalid result. The scrubs have no reaction in this case. +static s16 sResultTable[8][7] = { + /* Keaton Skull Spooky Bunny Goron Zora Gerudo Truth */ + { DNT_LIKE, DNT_HATE, DNT_LIKE, DNT_HATE, DNT_LOVE, DNT_LIKE, DNT_HATE }, /* DNT_HATE */ + { DNT_HATE, DNT_LIKE, DNT_HATE, DNT_LIKE, DNT_HATE, DNT_HATE, DNT_LOVE }, /* DNT_LOVE */ + { DNT_LOVE, DNT_HATE, DNT_HATE, DNT_HATE, DNT_LIKE, DNT_LIKE, DNT_LIKE }, /* DNT_HATE */ + { DNT_HATE, DNT_LOVE, DNT_HATE, DNT_HATE, DNT_HATE, DNT_HATE, DNT_LIKE }, /* DNT_LIKE */ + { DNT_LIKE, DNT_LIKE, DNT_LOVE, DNT_LIKE, DNT_LIKE, DNT_LIKE, DNT_HATE }, /* DNT_LIKE */ + { DNT_LIKE, DNT_LIKE, DNT_LIKE, DNT_LOVE, DNT_HATE, DNT_LOVE, DNT_LIKE }, /* DNT_HATE */ + { DNT_HATE, DNT_HATE, DNT_HATE, DNT_HATE, DNT_HATE, DNT_HATE, DNT_HATE }, /* DNT_LOVE */ + { DNT_LOVE, DNT_LOVE, DNT_LOVE, DNT_LOVE, DNT_LOVE, DNT_LOVE, DNT_LOVE }, /* INVALID */ +}; + +static s16 sResultValues[3][2] = { + /* DNT_LIKE */ { DNT_SIGNAL_HIDE, DNT_ACTION_LOW_RUPEES }, + /* DNT_HATE */ { DNT_SIGNAL_HIDE, DNT_ACTION_ATTACK }, + /* DNT_LOVE */ { DNT_SIGNAL_DANCE, DNT_ACTION_DANCE }, +}; + +static Vec3f sScrubPos[] = { + { 3810.0f, -20.0f, 1010.0f }, { 3890.0f, -20.0f, 990.0f }, { 3730.0f, -20.0f, 950.0f }, + { 3840.0f, -20.0f, 930.0f }, { 3910.0f, -20.0f, 870.0f }, { 3780.0f, -20.0f, 860.0f }, + { 3710.0f, -20.0f, 840.0f }, { 3860.0f, -20.0f, 790.0f }, { 3750.0f, -20.0f, 750.0f }, +}; + +void EnDntDemo_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnDntDemo_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnDntDemo* this = (EnDntDemo*)thisx; + s32 i; + s32 pad; + + osSyncPrintf("\n\n"); + // "Deku Scrub mask show start" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ デグナッツお面品評会開始 ☆☆☆☆☆ \n" VT_RST); + for (i = 0; i < 9; i++) { + this->scrubPos[i] = sScrubPos[i]; + this->scrubs[i] = (EnDntNomal*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, + ACTOR_EN_DNT_NOMAL, this->scrubPos[i].x, this->scrubPos[i].y, + this->scrubPos[i].z, 0, 0, 0, i + ENDNTNOMAL_STAGE); + if (this->scrubs[i] != NULL) { + // "zako zako" [small fries] + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ザコザコ ☆☆☆☆☆ %x\n" VT_RST, this->scrubs[i]); + } + } + + this->leaderPos.x = 4050.0f; + this->leaderPos.y = -20.0f; + this->leaderPos.z = 1000.0f; + this->leader = (EnDntJiji*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_DNT_JIJI, + this->leaderPos.x, this->leaderPos.y, this->leaderPos.z, 0, 0, 0, 0); + if (this->leader != NULL) { + // "jiji jiji jiji jiji jiji" [onomatopoeia for the scrub sound?] + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ じじじじじじじじじじい ☆☆☆☆☆ %x\n" VT_RST, this->leader); + } + this->subCamera = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnDntDemo_Judge; +} + +void EnDntDemo_Judge(EnDntDemo* this, GlobalContext* globalCtx) { + s16 delay; + s16 reaction; + s16 rand9; + s16 maskIdx; + s16 resultIdx; + u8 ignore; + s32 i; + + if (this->leaderSignal != DNT_SIGNAL_NONE) { + for (i = 0; i < 9; i++) { + this->scrubs[i]->stageSignal = this->leaderSignal; + this->scrubs[i]->action = this->action; + this->scrubs[i]->stagePrize = DNT_PRIZE_NONE; + } + if (this->leader->isSolid) { + this->leader->stageSignal = DNT_LEADER_SIGNAL_BURROW; + } + this->leaderSignal = DNT_SIGNAL_NONE; + this->actionFunc = EnDntDemo_Results; + } else if ((this->actor.xzDistToPlayer > 30.0f) || (Player_GetMask(globalCtx) == 0)) { + this->debugArrowTimer++; + if (this->subCamera != SUBCAM_FREE) { + this->subCamera = SUBCAM_FREE; + } + if (this->judgeTimer != 0) { + for (i = 0; i < 9; i++) { + this->scrubs[i]->stageSignal = DNT_SIGNAL_HIDE; + } + this->judgeTimer = 0; + } + } else { + if ((Player_GetMask(globalCtx) != 0) && (this->subCamera == SUBCAM_FREE)) { + this->subCamera = OnePointCutscene_Init(globalCtx, 2220, -99, &this->scrubs[3]->actor, MAIN_CAM); + } + this->debugArrowTimer = 0; + if (this->judgeTimer == 40) { + for (i = 0; i < 9; i++) { + this->scrubs[i]->stageSignal = DNT_SIGNAL_LOOK; + } + } + if (this->judgeTimer > 40) { + // "gera gera" [onomatopoeia for loud giggling] + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ げらげら ☆☆☆☆☆ \n" VT_RST); + func_800F436C(&this->actor.projectedPos, NA_SE_EV_CROWD - SFX_FLAG, 2.0f); + } + if (this->judgeTimer < 120) { + this->judgeTimer++; + } else { + ignore = false; + reaction = DNT_SIGNAL_NONE; + delay = 0; + switch (Player_GetMask(globalCtx)) { + case PLAYER_MASK_SKULL: + if (!(gSaveContext.itemGetInf[1] & 0x4000)) { + reaction = DNT_SIGNAL_CELEBRATE; + this->prize = DNT_PRIZE_STICK; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_SARIA_THEME); + break; + } + case PLAYER_MASK_TRUTH: + if (!(gSaveContext.itemGetInf[1] & 0x8000) && (Player_GetMask(globalCtx) != PLAYER_MASK_SKULL)) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->prize = DNT_PRIZE_NUTS; + this->leader->stageSignal = DNT_LEADER_SIGNAL_UP; + reaction = DNT_SIGNAL_LOOK; + if (this->subCamera != SUBCAM_FREE) { + this->subCamera = SUBCAM_FREE; + reaction = DNT_SIGNAL_LOOK; + OnePointCutscene_Init(globalCtx, 2340, -99, &this->leader->actor, MAIN_CAM); + } + break; + } + case PLAYER_MASK_KEATON: + case PLAYER_MASK_SPOOKY: + case PLAYER_MASK_BUNNY: + case PLAYER_MASK_GORON: + case PLAYER_MASK_ZORA: + case PLAYER_MASK_GERUDO: + rand9 = Rand_ZeroFloat(8.99f); + maskIdx = Player_GetMask(globalCtx); + maskIdx--; + if (rand9 == 8) { + ignore = true; + delay = 8; + reaction = DNT_SIGNAL_HIDE; + // "Special!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 特別! ☆☆☆☆☆ \n" VT_RST); + } else { + if (maskIdx >= PLAYER_MASK_MAX - 1) { + // "This is dangerous!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ヤバいよこれ! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ヤバいよこれ! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ ヤバいよこれ! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ ヤバいよこれ! ☆☆☆☆☆ \n" VT_RST); + maskIdx = Rand_ZeroFloat(7.99f); + } + + resultIdx = sResultTable[rand9][maskIdx]; + reaction = sResultValues[resultIdx][0]; + this->action = sResultValues[resultIdx][1]; + switch (this->action) { + case DNT_ACTION_LOW_RUPEES: + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_COURTYARD); + break; + case DNT_ACTION_ATTACK: + if (this->subCamera != SUBCAM_FREE) { + this->subCamera = SUBCAM_FREE; + OnePointCutscene_Init(globalCtx, 2350, -99, &this->scrubs[3]->actor, MAIN_CAM); + } + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_ENEMY | 0x800); + break; + case DNT_ACTION_DANCE: + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_SHOP); + break; + } + osSyncPrintf("\n\n"); + // "Each index 1" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 各インデックス1 ☆☆☆☆☆ %d\n" VT_RST, rand9); + // "Each index 2" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 各インデックス2 ☆☆☆☆☆ %d\n" VT_RST, maskIdx); + // "Each index 3" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 各インデックス3 ☆☆☆☆☆ %d\n" VT_RST, resultIdx); + osSyncPrintf("\n"); + // "What kind of evaluation?" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ どういう評価? ☆☆☆☆☆☆ %d\n" VT_RST, reaction); + // "What kind of action?" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ どういうアクション? ☆☆☆ %d\n" VT_RST, this->action); + osSyncPrintf("\n\n"); + break; + } + } + if (reaction != DNT_SIGNAL_NONE) { + for (i = 0; i < 9; i++) { + if (delay != 0) { + this->scrubs[i]->timer3 = delay * i; + } + this->scrubs[i]->action = this->action; + this->scrubs[i]->stageSignal = reaction; + this->scrubs[i]->ignore = ignore; + if (this->prize != DNT_PRIZE_NONE) { + this->scrubs[i]->timer1 = 300; + this->scrubs[i]->stagePrize = this->prize; + this->scrubs[i]->targetPos = this->leader->actor.world.pos; + if (this->prize == DNT_PRIZE_NUTS) { + this->leader->stageSignal = DNT_LEADER_SIGNAL_UP; + } + if (this->prize == DNT_PRIZE_STICK) { + this->leader->timer = 300; + } + } + } + this->actionFunc = EnDntDemo_Results; + } + } + } +} + +void EnDntDemo_Results(EnDntDemo* this, GlobalContext* globalCtx) { + s32 i; + + if (this->leaderSignal != DNT_SIGNAL_NONE) { + for (i = 0; i < 9; i++) { + this->scrubs[i]->action = this->action; + this->scrubs[i]->stageSignal = this->leaderSignal; + this->scrubs[i]->stagePrize = DNT_PRIZE_NONE; + } + if (this->leader->action == DNT_LEADER_ACTION_UP) { + this->leader->stageSignal = DNT_LEADER_SIGNAL_BURROW; + } else if (this->leader->unburrow) { + this->leader->stageSignal = DNT_LEADER_SIGNAL_RETURN; + } else { + this->leader->action = DNT_LEADER_ACTION_ATTACK; + } + this->leader->timer = 0; + this->leaderSignal = this->action = DNT_SIGNAL_NONE; + this->actionFunc = EnDntDemo_Prize; + } else if (this->prize == DNT_PRIZE_STICK) { + for (i = 0; i < 9; i++) { + s16 offsetAngle = -this->leader->actor.shape.rot.y; + Vec3f leaderPos = this->leader->actor.world.pos; + f32 offsetDist; + + if (!(i & 1)) { + offsetAngle -= 0x59D8; + } + offsetDist = ((i + 1) * 20.0f) + 20.0f; + this->scrubs[i]->timer2 = 10; + this->scrubs[i]->targetPos.x = leaderPos.x + Math_SinS(offsetAngle) * offsetDist; + this->scrubs[i]->targetPos.y = leaderPos.y; + this->scrubs[i]->targetPos.z = leaderPos.z + Math_CosS(offsetAngle) * offsetDist; + } + } +} + +void EnDntDemo_Prize(EnDntDemo* this, GlobalContext* globalCtx) { + s32 i; + + if (this->leaderSignal != DNT_SIGNAL_NONE) { + for (i = 0; i < 9; i++) { + this->scrubs[i]->action = this->action; + this->scrubs[i]->stageSignal = this->leaderSignal; + this->scrubs[i]->stagePrize = DNT_PRIZE_NONE; + } + this->leaderSignal = this->action = DNT_SIGNAL_NONE; + } +} + +void EnDntDemo_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDntDemo* this = (EnDntDemo*)thisx; + + if (this->unkTimer2 != 0) { + this->unkTimer2--; + } + if (this->unkTimer1 != 0) { + this->unkTimer1--; + } + this->actionFunc(this, globalCtx); + if (BREG(0)) { + if (this->debugArrowTimer != 0) { + if (!(this->debugArrowTimer & 1)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 120, 120, 0, 255, 4, globalCtx->state.gfxCtx); + } + } else { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 255, 255, 255, 255, 4, globalCtx->state.gfxCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h new file mode 100644 index 000000000..c2f3b9cdc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h @@ -0,0 +1,67 @@ +#ifndef Z_EN_DNT_DEMO_H +#define Z_EN_DNT_DEMO_H + +#include "ultra64.h" +#include "global.h" + +struct EnDntDemo; + +typedef void (*EnDntDemoActionFunc)(struct EnDntDemo*, GlobalContext*); + +typedef struct EnDntDemo { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnDntDemoActionFunc actionFunc; + /* 0x0150 */ s16 judgeTimer; + /* 0x0152 */ s16 unkTimer1; + /* 0x0154 */ s16 unkTimer2; + /* 0x0156 */ s16 debugArrowTimer; + /* 0x0158 */ s16 leaderSignal; + /* 0x015A */ s16 action; + /* 0x015C */ s16 prize; + /* 0x015E */ s16 subCamera; + /* 0x0160 */ Vec3f scrubPos[9]; + /* 0x01CC */ Vec3f leaderPos; + /* 0x01D8 */ struct EnDntNomal* scrubs[9]; + /* 0x01FC */ struct EnDntJiji* leader; +} EnDntDemo; // size = 0x0200 + +typedef enum { + /* 0 */ DNT_SIGNAL_NONE, + /* 1 */ DNT_SIGNAL_LOOK, + /* 2 */ DNT_SIGNAL_CELEBRATE, + /* 3 */ DNT_SIGNAL_DANCE, + /* 4 */ DNT_SIGNAL_HIDE, + /* 5 */ DNT_SIGNAL_RETURN, + /* 6 */ DNT_SIGNAL_UNUSED +} EnDntSignal; + +typedef enum { + /* 0 */ DNT_LEADER_ACTION_NONE, + /* 1 */ DNT_LEADER_ACTION_UP, + /* 2 */ DNT_LEADER_ACTION_UNUSED, + /* 3 */ DNT_LEADER_ACTION_ATTACK +} EnDntLeaderAction; + +typedef enum { + /* 0 */ DNT_LEADER_SIGNAL_NONE, + /* 1 */ DNT_LEADER_SIGNAL_UP, + /* 2 */ DNT_LEADER_SIGNAL_BURROW, + /* 3 */ DNT_LEADER_SIGNAL_RETURN +} EnDntLeaderSignal; + +typedef enum { + /* 0 */ DNT_PRIZE_NONE, + /* 1 */ DNT_PRIZE_NUTS, + /* 2 */ DNT_PRIZE_STICK +} EnDntPrize; + +typedef enum { + /* 0 */ DNT_ACTION_NONE, + /* 1 */ DNT_ACTION_DANCE, + /* 2 */ DNT_ACTION_ATTACK, + /* 3 */ DNT_ACTION_LOW_RUPEES, + /* 4 */ DNT_ACTION_HIGH_RUPEES, + /* 5 */ DNT_ACTION_PRIZE +} EnDntAction; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c new file mode 100644 index 000000000..7627efeec --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.c @@ -0,0 +1,446 @@ +/* + * File: z_en_dnt_jiji.c + * Overlay: ovl_En_Dnt_Jiji + * Description: Forest Stage scrub leader + */ + +#include "z_en_dnt_jiji.h" +#include "objects/object_dns/object_dns.h" +#include "overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnDntJiji_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDntJiji_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDntJiji_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDntJiji_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDntJiji_SetFlower(EnDntJiji* this, GlobalContext* globalCtx); + +void EnDntJiji_SetupWait(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupUnburrow(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupWalk(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupCower(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupGivePrize(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupHide(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupReturn(EnDntJiji* this, GlobalContext* globalCtx); + +void EnDntJiji_Wait(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Up(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Unburrow(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Walk(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Burrow(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Cower(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_SetupTalk(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Talk(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_GivePrize(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Hide(EnDntJiji* this, GlobalContext* globalCtx); +void EnDntJiji_Return(EnDntJiji* this, GlobalContext* globalCtx); + +const ActorInit En_Dnt_Jiji_InitVars = { + ACTOR_EN_DNT_JIJI, + ACTORCAT_NPC, + FLAGS, + OBJECT_DNS, + sizeof(EnDntJiji), + (ActorFunc)EnDntJiji_Init, + (ActorFunc)EnDntJiji_Destroy, + (ActorFunc)EnDntJiji_Update, + (ActorFunc)EnDntJiji_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 30, 80, 0, { 0, 0, 0 } }, +}; + +void EnDntJiji_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDntJiji* this = (EnDntJiji*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gDntJijiSkel, &gDntJijiBurrowAnim, this->jointTable, this->morphTable, + 13); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->stage = (EnDntDemo*)this->actor.parent; + osSyncPrintf("\n\n"); + // "Deku Scrub mask show elder" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ デグナッツお面品評会長老 ☆☆☆☆☆ %x\n" VT_RST, this->stage); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.colChkInfo.mass = 0xFF; + this->actor.targetMode = 6; + this->actionFunc = EnDntJiji_SetFlower; + this->actor.gravity = -2.0f; +} + +void EnDntJiji_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDntJiji* this = (EnDntJiji*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnDntJiji_SetFlower(EnDntJiji* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1) { + this->flowerPos = this->actor.world.pos; + this->actionFunc = EnDntJiji_SetupWait; + } +} + +void EnDntJiji_SetupWait(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiBurrowAnim); + Animation_Change(&this->skelAnime, &gDntJijiBurrowAnim, 0.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->skelAnime.curFrame = 8.0f; + this->isSolid = this->action = DNT_LEADER_ACTION_NONE; + this->actionFunc = EnDntJiji_Wait; +} + +void EnDntJiji_Wait(EnDntJiji* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + if ((this->timer == 1) && (this->actor.xzDistToPlayer < 150.0f) && !Gameplay_InCsMode(globalCtx) && + !(player->stateFlags1 & 0x800)) { + OnePointCutscene_Init(globalCtx, 2230, -99, &this->actor, MAIN_CAM); + this->timer = 0; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = EnDntJiji_SetupUnburrow; + } +} + +void EnDntJiji_SetupUp(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiUpAnim); + Animation_Change(&this->skelAnime, &gDntJijiUpAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 6.0f, 0, 15, 5, 20, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + this->actionFunc = EnDntJiji_Up; +} + +void EnDntJiji_Up(EnDntJiji* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + if (this->actor.xzDistToPlayer < 150.0f) { + this->actionFunc = EnDntJiji_SetupCower; + } +} + +void EnDntJiji_SetupUnburrow(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiUnburrowAnim); + Animation_Change(&this->skelAnime, &gDntJijiUnburrowAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 6.0f, 0, 15, 5, 20, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + this->actionFunc = EnDntJiji_Unburrow; +} + +void EnDntJiji_Unburrow(EnDntJiji* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (this->endFrame <= frame) { + if (this->action != DNT_LEADER_ACTION_ATTACK) { + this->actionFunc = EnDntJiji_SetupWalk; + } else { + this->actionFunc = EnDntJiji_SetupReturn; + } + } +} + +void EnDntJiji_SetupWalk(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiWalkAnim); + Animation_Change(&this->skelAnime, &gDntJijiWalkAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actor.speedXZ = 1.0f; + this->isSolid = true; + this->unburrow = true; + this->actionFunc = EnDntJiji_Walk; +} + +void EnDntJiji_Walk(EnDntJiji* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, 0x3E8, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + Math_ApproachF(&this->actor.speedXZ, 1.0f, 0.2f, 0.4f); + if (this->sfxTimer == 0) { + this->sfxTimer = 5; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if ((this->actor.bgCheckFlags & 8) && (this->actor.bgCheckFlags & 1)) { + this->actor.velocity.y = 9.0f; + this->actor.speedXZ = 3.0f; + } + if (this->actor.xzDistToPlayer < 100.0f) { + if (CUR_UPG_VALUE(UPG_STICKS) == 1) { + this->getItemId = GI_STICK_UPGRADE_20; + } else { + this->getItemId = GI_STICK_UPGRADE_30; + } + this->actor.textId = 0x104D; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->actor.speedXZ = 0.0f; + this->unused = 5; + this->actionFunc = EnDntJiji_Talk; + } +} + +void EnDntJiji_SetupBurrow(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiBurrowAnim); + Animation_Change(&this->skelAnime, &gDntJijiBurrowAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 6.0f, 0, 15, 5, 20, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DOWN); + this->actionFunc = EnDntJiji_Burrow; +} + +void EnDntJiji_Burrow(EnDntJiji* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); +} + +void EnDntJiji_SetupCower(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiCowerAnim); + Animation_Change(&this->skelAnime, &gDntJijiCowerAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 3.0f, 0, 9, 3, 10, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + + if ((CUR_UPG_VALUE(UPG_NUTS) == 1) || (CUR_UPG_VALUE(UPG_NUTS) == 0)) { + this->getItemId = GI_NUT_UPGRADE_30; + } else { + this->getItemId = GI_NUT_UPGRADE_40; + } + this->actor.flags |= ACTOR_FLAG_0; + this->actor.textId = 0x10DB; + this->unused = 5; + this->actionFunc = EnDntJiji_Cower; +} + +void EnDntJiji_Cower(EnDntJiji* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + if (frame >= this->endFrame) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnDntJiji_SetupTalk; + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } +} + +void EnDntJiji_SetupTalk(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiTalkAnim); + Animation_Change(&this->skelAnime, &gDntJijiTalkAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actionFunc = EnDntJiji_Talk; +} + +void EnDntJiji_Talk(EnDntJiji* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, NULL, 7); + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, this->getItemId, 400.0f, 200.0f); + this->actionFunc = EnDntJiji_SetupGivePrize; + } +} + +void EnDntJiji_SetupGivePrize(EnDntJiji* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = EnDntJiji_GivePrize; + } else { + func_8002F434(&this->actor, globalCtx, this->getItemId, 400.0f, 200.0f); + } +} + +void EnDntJiji_GivePrize(EnDntJiji* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + if ((this->getItemId == GI_NUT_UPGRADE_30) || (this->getItemId == GI_NUT_UPGRADE_40)) { + // "nut" + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + osSyncPrintf("実 \n"); + gSaveContext.itemGetInf[1] |= 0x8000; + } else { + // "stick" + osSyncPrintf("棒 \n"); + osSyncPrintf("棒 \n"); + osSyncPrintf("棒 \n"); + osSyncPrintf("棒 \n"); + osSyncPrintf("棒 \n"); + osSyncPrintf("棒 \n"); + gSaveContext.itemGetInf[1] |= 0x4000; + } + this->actor.textId = 0; + if ((this->stage != NULL) && (this->stage->actor.update != NULL)) { + this->stage->action = DNT_ACTION_NONE; + if (!this->unburrow) { + this->stage->leaderSignal = DNT_SIGNAL_HIDE; + } else { + this->stage->leaderSignal = DNT_SIGNAL_RETURN; + } + } + this->actor.flags &= ~ACTOR_FLAG_0; + if (!this->unburrow) { + this->actionFunc = EnDntJiji_SetupHide; + } else { + this->actionFunc = EnDntJiji_SetupReturn; + } + } +} + +void EnDntJiji_SetupHide(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiHideAnim); + Animation_Change(&this->skelAnime, &gDntJijiHideAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->actionFunc = EnDntJiji_Hide; +} + +void EnDntJiji_Hide(EnDntJiji* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (this->endFrame <= frame) { + this->actionFunc = EnDntJiji_SetupWait; + } +} + +void EnDntJiji_SetupReturn(EnDntJiji* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntJijiWalkAnim); + Animation_Change(&this->skelAnime, &gDntJijiWalkAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actor.speedXZ = 2.0f; + this->isSolid = this->unburrow = true; + this->actionFunc = EnDntJiji_Return; +} + +void EnDntJiji_Return(EnDntJiji* this, GlobalContext* globalCtx) { + f32 dx; + f32 dz; + + SkelAnime_Update(&this->skelAnime); + dx = this->flowerPos.x - this->actor.world.pos.x; + dz = this->flowerPos.z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 1, 0xBB8, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if ((this->actor.bgCheckFlags & 8) && (this->actor.bgCheckFlags & 1)) { + this->actor.velocity.y = 9.0f; + this->actor.speedXZ = 3.0f; + } + if (this->sfxTimer == 0) { + this->sfxTimer = 3; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if ((fabsf(dx) < 5.0f) && (fabsf(dz) < 5.0f)) { + this->actor.world.pos.x = this->flowerPos.x; + this->actor.world.pos.z = this->flowerPos.z; + if (this->attackFlag) { + if ((this->stage->actor.update != NULL) && (this->stage->leaderSignal == DNT_SIGNAL_NONE)) { + this->stage->leaderSignal = DNT_SIGNAL_HIDE; + this->stage->action = DNT_ACTION_ATTACK; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_ENEMY | 0x800); + } + } + this->actor.speedXZ = 0.0f; + this->isSolid = 0; + this->actionFunc = EnDntJiji_SetupBurrow; + } +} + +void EnDntJiji_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDntJiji* this = (EnDntJiji*)thisx; + + Actor_SetScale(&this->actor, 0.015f); + this->unkTimer++; + if (BREG(0)) { + // "time" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 時間 ☆☆☆☆☆ %d\n" VT_RST, this->timer); + } + if ((this->timer > 1) && (this->timer != 0)) { + this->timer--; + } + if (this->sfxTimer != 0) { + this->sfxTimer--; + } + if (this->blinkTimer != 0) { + this->blinkTimer--; + } + switch (this->stageSignal) { + case DNT_LEADER_SIGNAL_UP: + this->isSolid = true; + this->action = DNT_LEADER_ACTION_UP; + this->actionFunc = EnDntJiji_SetupUp; + break; + case DNT_LEADER_SIGNAL_BURROW: + this->isSolid = false; + this->action = DNT_LEADER_ACTION_NONE; + this->actionFunc = EnDntJiji_SetupBurrow; + break; + case DNT_LEADER_SIGNAL_RETURN: + this->actionFunc = EnDntJiji_SetupReturn; + break; + case DNT_LEADER_SIGNAL_NONE: + break; + } + if (this->actor.textId != 0) { + Actor_SetFocus(&this->actor, 30.0f); + } + if (this->stageSignal != DNT_LEADER_SIGNAL_NONE) { + this->stageSignal = DNT_LEADER_SIGNAL_NONE; + } + if (this->blinkTimer == 0) { + this->eyeState++; + if (this->eyeState > 2) { + this->eyeState = 0; + this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 0x1D); + Collider_UpdateCylinder(&this->actor, &this->collider); + if (this->isSolid != 0) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnDntJiji_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* blinkTex[] = { gDntJijiEyeOpenTex, gDntJijiEyeHalfTex, gDntJijiEyeShutTex }; + EnDntJiji* this = (EnDntJiji*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_jiji.c", 1019); + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Push(); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(blinkTex[this->eyeState])); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, this); + Matrix_Pop(); + Matrix_Translate(this->flowerPos.x, this->flowerPos.y, this->flowerPos.z, MTXMODE_NEW); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dnt_jiji.c", 1040), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDntJijiFlowerDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_jiji.c", 1043); +} diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.h b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.h new file mode 100644 index 000000000..1d809c309 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dnt_Jiji/z_en_dnt_jiji.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_DNT_JIJI_H +#define Z_EN_DNT_JIJI_H + +#include "ultra64.h" +#include "global.h" + +struct EnDntJiji; + +typedef void (*EnDntJijiActionFunc)(struct EnDntJiji*, GlobalContext*); + +typedef struct EnDntJiji { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[13]; + /* 0x01DE */ Vec3s morphTable[13]; + /* 0x022C */ EnDntJijiActionFunc actionFunc; + /* 0x0230 */ Vec3f flowerPos; + /* 0x023C */ u8 isSolid; + /* 0x023E */ s16 action; + /* 0x0240 */ s16 timer; + /* 0x0242 */ s16 sfxTimer; + /* 0x0244 */ s16 blinkTimer; + /* 0x0246 */ s16 unkTimer; + /* 0x0248 */ s16 endFrame; + /* 0x024A */ s16 unburrow; + /* 0x024C */ s16 eyeState; + /* 0x024E */ s16 stageSignal; + /* 0x0250 */ s16 unused; // always set to 5 + /* 0x0252 */ s16 attackFlag; // Is never set + /* 0x0254 */ s32 getItemId; + /* 0x0258 */ struct EnDntDemo* stage; + /* 0x025C */ ColliderCylinder collider; +} EnDntJiji; // size = 0x02A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c b/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c new file mode 100644 index 000000000..9ac0d2912 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.c @@ -0,0 +1,888 @@ +/* + * File: z_en_dnt_nomal + * Overlay: ovl_En_Dnt_Nomal + * Description: Lost Woods minigame scrubs + */ + +#include "z_en_dnt_nomal.h" +#include "objects/object_dnk/object_dnk.h" +#include "overlays/actors/ovl_En_Dnt_Demo/z_en_dnt_demo.h" +#include "overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h" +#include "overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "objects/object_hintnuts/object_hintnuts.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnDntNomal_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDntNomal_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDntNomal_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDntNomal_DrawTargetScrub(Actor* thisx, GlobalContext* globalCtx); +void EnDntNomal_DrawStageScrub(Actor* thisx, GlobalContext* globalCtx); + +void EnDntNomal_WaitForObject(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetFlower(EnDntNomal* this, GlobalContext* globalCtx); + +void EnDntNomal_SetupTargetWait(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupTargetUnburrow(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupTargetWalk(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupTargetTalk(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupTargetGivePrize(EnDntNomal* this, GlobalContext* globalCtx); + +void EnDntNomal_TargetWait(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetUnburrow(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetWalk(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetFacePlayer(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetTalk(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetGivePrize(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetReturn(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_TargetBurrow(EnDntNomal* this, GlobalContext* globalCtx); + +void EnDntNomal_SetupStageWait(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupStageCelebrate(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupStageDance(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupStageHide(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_SetupStageAttack(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageSetupReturn(EnDntNomal* this, GlobalContext* globalCtx); + +void EnDntNomal_StageWait(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageUp(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageUnburrow(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageCelebrate(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageDance(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageHide(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageAttackHide(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageAttack(EnDntNomal* this, GlobalContext* globalCtx); +void EnDntNomal_StageReturn(EnDntNomal* this, GlobalContext* globalCtx); + +const ActorInit En_Dnt_Nomal_InitVars = { + ACTOR_EN_DNT_NOMAL, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnDntNomal), + (ActorFunc)EnDntNomal_Init, + (ActorFunc)EnDntNomal_Destroy, + (ActorFunc)EnDntNomal_Update, + NULL, + NULL, +}; + +static ColliderCylinderInit sBodyCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 16, 46, 0, { 0, 0, 0 } }, +}; + +static ColliderQuadInit sTargetQuadInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static Color_RGBA8 sLeafColors[] = { + { 255, 255, 255, 255 }, { 255, 195, 175, 255 }, { 210, 255, 0, 255 }, + { 255, 255, 255, 255 }, { 210, 255, 0, 255 }, { 255, 195, 175, 255 }, + { 255, 255, 255, 255 }, { 255, 195, 175, 255 }, { 210, 255, 0, 255 }, +}; + +void EnDntNomal_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDntNomal* this = (EnDntNomal*)thisx; + + this->type = this->actor.params; + if (this->type < ENDNTNOMAL_TARGET) { + this->type = ENDNTNOMAL_TARGET; + } + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.colChkInfo.mass = 0xFF; + this->objId = -1; + if (this->type == ENDNTNOMAL_TARGET) { + osSyncPrintf("\n\n"); + // "Deku Scrub target" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ デグナッツ的当て ☆☆☆☆☆ \n" VT_RST); + Collider_InitQuad(globalCtx, &this->targetQuad); + Collider_SetQuad(globalCtx, &this->targetQuad, &this->actor, &sTargetQuadInit); + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->objId = OBJECT_HINTNUTS; + } else { + osSyncPrintf("\n\n"); + // "Deku Scrub mask show audience" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ デグナッツお面品評会一般人 ☆☆☆☆☆ \n" VT_RST); + Collider_InitCylinder(globalCtx, &this->bodyCyl); + Collider_SetCylinder(globalCtx, &this->bodyCyl, &this->actor, &sBodyCylinderInit); + this->objId = OBJECT_DNK; + } + if (this->objId >= 0) { + this->objIndex = Object_GetIndex(&globalCtx->objectCtx, this->objId); + if (this->objIndex < 0) { + Actor_Kill(&this->actor); + // "What?" + osSyncPrintf(VT_FGCOL(PURPLE) " なにみの? %d\n" VT_RST "\n", this->objIndex); + // "Bank is funny" + osSyncPrintf(VT_FGCOL(CYAN) " バンクおかしいしぞ!%d\n" VT_RST "\n", this->actor.params); + return; + } + } else { + Actor_Kill(&this->actor); + } + this->actionFunc = EnDntNomal_WaitForObject; +} + +void EnDntNomal_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDntNomal* this = (EnDntNomal*)thisx; + + if (this->type == ENDNTNOMAL_TARGET) { + Collider_DestroyQuad(globalCtx, &this->targetQuad); + } else { + Collider_DestroyCylinder(globalCtx, &this->bodyCyl); + } +} + +void EnDntNomal_WaitForObject(EnDntNomal* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objIndex)) { + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objIndex].segment); + this->actor.objBankIndex = this->objIndex; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + this->actor.gravity = -2.0f; + Actor_SetScale(&this->actor, 0.01f); + if (this->type == ENDNTNOMAL_TARGET) { + SkelAnime_Init(globalCtx, &this->skelAnime, &gHintNutsSkel, &gHintNutsBurrowAnim, this->jointTable, + this->morphTable, 10); + this->actor.draw = EnDntNomal_DrawTargetScrub; + } else { + SkelAnime_Init(globalCtx, &this->skelAnime, &gDntStageSkel, &gDntStageHideAnim, this->jointTable, + this->morphTable, 11); + this->actor.draw = EnDntNomal_DrawStageScrub; + } + this->actionFunc = EnDntNomal_SetFlower; + } +} + +void EnDntNomal_SetFlower(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1) { + this->flowerPos = this->actor.world.pos; + if (this->type == ENDNTNOMAL_TARGET) { + this->actionFunc = EnDntNomal_SetupTargetWait; + } else { + this->actionFunc = EnDntNomal_SetupStageWait; + } + } +} + +void EnDntNomal_SetupTargetWait(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsBurrowAnim); + Animation_Change(&this->skelAnime, &gHintNutsBurrowAnim, 0.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->skelAnime.curFrame = 8.0f; + this->actionFunc = EnDntNomal_TargetWait; +} + +void EnDntNomal_TargetWait(EnDntNomal* this, GlobalContext* globalCtx) { + Vec3f scorePos; + f32 targetX = 1340.0f; + f32 targetY = 50.0f; + f32 targetZ = -30.0f; + f32 dx; + f32 dy; + f32 dz; + Vec3f scoreAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f scoreVel = { 0.0f, 0.0f, 0.0f }; + + this->targetVtx[0].x = this->targetVtx[1].x = this->targetVtx[2].x = this->targetVtx[3].x = targetX; + + this->targetVtx[1].y = this->targetVtx[0].y = targetY - 24.0f; + + this->targetVtx[2].z = this->targetVtx[0].z = targetZ + 24.0f; + + this->targetVtx[3].z = this->targetVtx[1].z = targetZ - 24.0f; + + this->targetVtx[3].y = this->targetVtx[2].y = targetY + 24.0f; + + SkelAnime_Update(&this->skelAnime); + if ((this->targetQuad.base.acFlags & AC_HIT) || BREG(0)) { + this->targetQuad.base.acFlags &= ~AC_HIT; + + dx = fabsf(targetX - this->targetQuad.info.bumper.hitPos.x); + dy = fabsf(targetY - this->targetQuad.info.bumper.hitPos.y); + dz = fabsf(targetZ - this->targetQuad.info.bumper.hitPos.z); + + scoreVel.y = 5.0f; + + if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 8.0f) { + scorePos.x = this->actor.world.pos.x - 20.0f; + scorePos.y = this->actor.world.pos.y + 20.0f; + scorePos.z = this->actor.world.pos.z; + EffectSsExtra_Spawn(globalCtx, &scorePos, &scoreVel, &scoreAccel, 4, 2); + Audio_StopSfxById(NA_SE_SY_TRE_BOX_APPEAR); + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + // "Big hit" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 大当り ☆☆☆☆☆ %d\n" VT_RST, this->hitCounter); + if (!LINK_IS_ADULT && !(gSaveContext.itemGetInf[1] & 0x2000)) { + this->hitCounter++; + if (this->hitCounter >= 3) { + OnePointCutscene_Init(globalCtx, 4140, -99, &this->actor, MAIN_CAM); + func_8002DF54(globalCtx, &this->actor, 1); + this->timer4 = 50; + this->actionFunc = EnDntNomal_SetupTargetUnburrow; + } + } + } else if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 24.0f) { + scorePos.x = this->actor.world.pos.x; + scorePos.y = this->actor.world.pos.y + 20.0f; + scorePos.z = this->actor.world.pos.z; + EffectSsExtra_Spawn(globalCtx, &scorePos, &scoreVel, &scoreAccel, 4, 0); + this->hitCounter = 0; + } + } +} + +void EnDntNomal_SetupTargetUnburrow(EnDntNomal* this, GlobalContext* globalCtx) { + Vec3f spawnPos; + + if (this->timer4 == 0) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsUnburrowAnim); + Animation_Change(&this->skelAnime, &gHintNutsUnburrowAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + spawnPos = this->actor.world.pos; + spawnPos.y = this->actor.world.pos.y + 50.0f; + EffectSsHahen_SpawnBurst(globalCtx, &spawnPos, 4.0f, 0, 10, 3, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + this->actionFunc = EnDntNomal_TargetUnburrow; + } +} + +void EnDntNomal_TargetUnburrow(EnDntNomal* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (frame >= this->endFrame) { + this->actionFunc = EnDntNomal_SetupTargetWalk; + } +} + +void EnDntNomal_SetupTargetWalk(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsRunAnim); + Animation_Change(&this->skelAnime, &gHintNutsRunAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actor.speedXZ = 1.0f; + this->actor.colChkInfo.mass = 0; + this->actionFunc = EnDntNomal_TargetWalk; +} + +void EnDntNomal_TargetWalk(EnDntNomal* this, GlobalContext* globalCtx) { + f32 dx; + f32 dz; + + SkelAnime_Update(&this->skelAnime); + dx = 1340.0f + 3.0f - this->actor.world.pos.x; + dz = 0.0f - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 0x32, 0xBB8, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 6.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if (this->actor.world.pos.z > -30.0f) { + this->actor.speedXZ = 0.0f; + this->actionFunc = EnDntNomal_TargetFacePlayer; + } +} + +void EnDntNomal_TargetFacePlayer(EnDntNomal* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 6.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if (fabsf(this->actor.shape.rot.y - this->actor.yawTowardsPlayer) < 30.0f) { + this->actionFunc = EnDntNomal_SetupTargetTalk; + } +} + +void EnDntNomal_SetupTargetTalk(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsTalkAnim); + Animation_Change(&this->skelAnime, &gHintNutsTalkAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actor.textId = 0x10AF; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->actionFunc = EnDntNomal_TargetTalk; +} + +void EnDntNomal_TargetTalk(EnDntNomal* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + GET_ACTIVE_CAM(globalCtx)->csId = 0; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = EnDntNomal_SetupTargetGivePrize; + } +} + +void EnDntNomal_SetupTargetGivePrize(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsSpitAnim); + Animation_Change(&this->skelAnime, &gHintNutsSpitAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->actionFunc = EnDntNomal_TargetGivePrize; +} + +void EnDntNomal_TargetGivePrize(EnDntNomal* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if ((frame >= 8.0f) && !this->spawnedItem) { + f32 itemX = this->mouthPos.x - 10.0f; + f32 itemY = this->mouthPos.y; + f32 itemZ = this->mouthPos.z; + + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_ITEM, itemX, itemY, itemZ, 0, + 0, 0, EXITEM_BULLET_BAG) == NULL) { + func_8002DF54(globalCtx, NULL, 7); + Actor_Kill(&this->actor); + } + this->spawnedItem = true; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_THROW); + } + if (frame >= this->endFrame) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsRunAnim); + Animation_Change(&this->skelAnime, &gHintNutsRunAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actionFunc = EnDntNomal_TargetReturn; + } +} + +void EnDntNomal_TargetReturn(EnDntNomal* this, GlobalContext* globalCtx) { + f32 dx; + f32 dz; + + SkelAnime_Update(&this->skelAnime); + dx = this->flowerPos.x - this->actor.world.pos.x; + dz = -180.0f - this->actor.world.pos.z; + + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 3, 0x1388, 0); + if (fabsf(this->actor.shape.rot.y - (s16)(Math_FAtan2F(dx, dz) * (0x8000 / M_PI))) < 20.0f) { + this->actor.speedXZ = 1.0f; + } + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 6.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->actor.world.pos.z < -172.0f) { + this->endFrame = (f32)Animation_GetLastFrame(&gHintNutsBurrowAnim); + Animation_Change(&this->skelAnime, &gHintNutsBurrowAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->actor.world.pos.z = -173.0f; + this->actor.speedXZ = 0.0f; + this->actionFunc = EnDntNomal_TargetBurrow; + } +} + +void EnDntNomal_TargetBurrow(EnDntNomal* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (frame >= this->endFrame) { + this->actionFunc = EnDntNomal_SetupTargetWait; + } +} + +void EnDntNomal_SetupStageWait(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->timer3 == 0) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageHideAnim); + Animation_Change(&this->skelAnime, &gDntStageHideAnim, 0.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->skelAnime.curFrame = 8.0f; + this->isSolid = false; + this->actionFunc = EnDntNomal_StageWait; + } +} + +void EnDntNomal_StageWait(EnDntNomal* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); +} + +void EnDntNomal_SetupStageUp(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->timer3 == 0) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageUpAnim); + Animation_Change(&this->skelAnime, &gDntStageUpAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + if (this->action != DNT_ACTION_ATTACK) { + this->rotDirection = -1; + } + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 4.0f, 0, 10, 3, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + this->isSolid = true; + this->actionFunc = EnDntNomal_StageUp; + } +} + +void EnDntNomal_StageUp(EnDntNomal* this, GlobalContext* globalCtx) { + s16 rotTarget; + f32 frame = this->skelAnime.curFrame; + f32 turnMod; + + SkelAnime_Update(&this->skelAnime); + if ((frame >= this->endFrame) && (this->action == DNT_ACTION_ATTACK)) { + this->actionFunc = EnDntNomal_SetupStageAttack; + } else { + if (this->timer4 == 0) { + turnMod = 0.0f; + if (this->stagePrize == DNT_PRIZE_NONE) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + } else { + f32 dx = this->targetPos.x - this->actor.world.pos.x; + f32 dz = this->targetPos.z - this->actor.world.pos.z; + + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 1, 0xBB8, 0); + turnMod = 90.0f; + } + if ((Rand_ZeroFloat(10.0f + turnMod) < 1.0f) && (this->action != DNT_ACTION_ATTACK)) { + this->timer4 = (s16)Rand_ZeroFloat(30.0f) + 30; + } + } else { + if (this->timer2 == 0) { + this->rotDirection++; + if (this->rotDirection > 1) { + this->rotDirection = -1; + } + this->timer2 = (s16)Rand_ZeroFloat(10.0f) + 10; + } + rotTarget = this->actor.yawTowardsPlayer; + if (this->rotDirection != 0) { + rotTarget += this->rotDirection * 0x1388; + } + Math_SmoothStepToS(&this->actor.shape.rot.y, rotTarget, 3, 0x1388, 0); + } + if (this->actor.xzDistToPlayer < 70.0f) { + this->actionFunc = EnDntNomal_SetupStageHide; + } + } +} + +void EnDntNomal_SetupStageUnburrow(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->timer3 == 0) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageUnburrowAnim); + Animation_Change(&this->skelAnime, &gDntStageUnburrowAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->isSolid = false; + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 4.0f, 0, 10, 3, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + this->actionFunc = EnDntNomal_StageUnburrow; + } +} + +void EnDntNomal_StageUnburrow(EnDntNomal* this, GlobalContext* globalCtx) { + f32 frame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (frame >= this->endFrame) { + if (this->action != DNT_ACTION_DANCE) { + this->timer3 = (s16)Rand_ZeroFloat(2.0f) + (s16)(this->type * 0.5f); + this->actionFunc = EnDntNomal_SetupStageCelebrate; + } else { + this->timer2 = 300; + this->actionFunc = EnDntNomal_SetupStageDance; + } + } +} + +void EnDntNomal_SetupStageCelebrate(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageWalkAnim); + Animation_Change(&this->skelAnime, &gDntStageWalkAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actor.speedXZ = 3.0f; + this->isSolid = true; + this->actionFunc = EnDntNomal_StageCelebrate; +} + +void EnDntNomal_StageCelebrate(EnDntNomal* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((this->timer1 == 0) && (this->timer3 == 0)) { + f32 dx = this->targetPos.x - this->actor.world.pos.x; + f32 dz = this->targetPos.z - this->actor.world.pos.z; + + if ((fabsf(dx) < 10.0f) && (fabsf(dz) < 10.0f) && (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) { + this->action = DNT_ACTION_PRIZE; + this->actionFunc = EnDntNomal_SetupStageDance; + this->actor.speedXZ = 0.0f; + return; + } + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 1, 0xBB8, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + } else { + if (this->timer1 == 1) { + this->timer3 = (s16)Rand_ZeroFloat(20.0f) + 20.0f; + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0x14, 0x1388, 0); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + } + if (this->timer5 == 0) { + this->timer5 = 20; + if ((this->type & 1) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DAMAGE); + } + } else if ((this->timer5 & 3) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if ((this->actor.bgCheckFlags & 8) && (this->actor.bgCheckFlags & 1)) { + this->actor.velocity.y = 7.5f; + } +} + +void EnDntNomal_SetupStageDance(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageDanceAnim); + Animation_Change(&this->skelAnime, &gDntStageDanceAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->isSolid = true; + this->timer3 = (s16)Rand_ZeroFloat(20.0f) + 20.0f; + this->rotDirection = -1; + if (Rand_ZeroFloat(1.99f) < 1.0f) { + this->rotDirection = 1; + } + this->actionFunc = EnDntNomal_StageDance; +} + +void EnDntNomal_StageDance(EnDntNomal* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer2 == 0) { + if (this->action == DNT_ACTION_DANCE) { + this->action = DNT_ACTION_HIGH_RUPEES; + this->actionFunc = EnDntNomal_SetupStageHide; + } else { + this->action = DNT_ACTION_NONE; + this->actionFunc = EnDntNomal_StageSetupReturn; + } + } else if (this->timer3 != 0) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + if (this->timer3 == 1) { + this->timer4 = (s16)Rand_ZeroFloat(20.0f) + 20.0f; + this->rotDirection = -this->rotDirection; + } + } else if (this->timer4 != 0) { + this->actor.shape.rot.y += this->rotDirection * 0x800; + if (this->timer4 == 1) { + this->timer3 = (s16)Rand_ZeroFloat(20.0f) + 20.0f; + } + } +} + +void EnDntNomal_SetupStageHide(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->timer3 != 0) { + if ((this->timer3 == 1) && (this->ignore == 1)) { + func_80078884(NA_SE_SY_ERROR); + } + } else { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageHideAnim); + Animation_Change(&this->skelAnime, &gDntStageHideAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->isSolid = false; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DOWN); + this->actionFunc = EnDntNomal_StageHide; + } +} + +void EnDntNomal_StageHide(EnDntNomal* this, GlobalContext* globalCtx) { + EnExRuppy* rupee; + f32 frame = this->skelAnime.curFrame; + s16 rupeeColor; + + SkelAnime_Update(&this->skelAnime); + if (frame >= this->endFrame) { + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 4.0f, 0, 10, 3, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + switch (this->action) { + case DNT_ACTION_NONE: + this->actionFunc = EnDntNomal_SetupStageWait; + break; + case DNT_ACTION_ATTACK: + this->actionFunc = EnDntNomal_StageAttackHide; + break; + case DNT_ACTION_LOW_RUPEES: + case DNT_ACTION_HIGH_RUPEES: + rupee = + (EnExRuppy*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_EX_RUPPY, this->actor.world.pos.x, + this->actor.world.pos.y + 20.0f, this->actor.world.pos.z, 0, 0, 0, 3); + if (rupee != NULL) { + rupeeColor = this->action - DNT_ACTION_LOW_RUPEES; + rupee->colorIdx = rupeeColor; + if (Rand_ZeroFloat(3.99f) < 1.0f) { + rupee->colorIdx = rupeeColor + 1; + } + rupee->actor.velocity.y = 5.0f; + if (rupee->colorIdx == 2) { + rupee->actor.velocity.y = 7.0f; + } + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + this->action = DNT_ACTION_NONE; + this->actionFunc = EnDntNomal_SetupStageWait; + break; + } + } +} + +void EnDntNomal_StageAttackHide(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer > 70.0f) { + this->actionFunc = EnDntNomal_SetupStageUp; + } +} + +void EnDntNomal_SetupStageAttack(EnDntNomal* this, GlobalContext* globalCtx) { + if (this->timer3 == 0) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageSpitAnim); + Animation_Change(&this->skelAnime, &gDntStageSpitAnim, 1.0f, 0.0f, this->endFrame, ANIMMODE_ONCE, -10.0f); + this->actor.colChkInfo.mass = 0xFF; + this->isSolid = true; + this->timer2 = 0; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + this->actionFunc = EnDntNomal_StageAttack; + } +} + +void EnDntNomal_StageAttack(EnDntNomal* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* nut; + f32 frame = this->skelAnime.curFrame; + f32 dz; + f32 dx; + f32 dy; + + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x1388, 0); + dx = player->actor.world.pos.x - this->mouthPos.x; + dy = player->actor.world.pos.y + 30.0f - this->mouthPos.y; + dz = player->actor.world.pos.z - this->mouthPos.z; + Math_SmoothStepToS(&this->actor.shape.rot.x, -(s16)(Math_FAtan2F(dy, sqrtf(SQ(dx) + SQ(dz))) * (0x8000 / M_PI)), 3, + 0x1388, 0); + if ((frame >= this->endFrame) && (this->timer2 == 0)) { + this->timer2 = (s16)Rand_ZeroFloat(10.0f) + 10; + } + if (this->timer2 == 1) { + this->spawnedItem = false; + this->actionFunc = EnDntNomal_SetupStageAttack; + } else if (this->actor.xzDistToPlayer < 50.0f) { + this->action = DNT_ACTION_ATTACK; + this->actionFunc = EnDntNomal_SetupStageHide; + } else if ((frame >= 8.0f) && (!this->spawnedItem)) { + Vec3f baseOffset; + Vec3f spawnOffset; + f32 spawnX; + f32 spawnY; + f32 spawnZ; + + Matrix_RotateY(this->actor.shape.rot.y / (f32)0x8000 * M_PI, MTXMODE_NEW); + Matrix_RotateX(this->actor.shape.rot.x / (f32)0x8000 * M_PI, MTXMODE_APPLY); + baseOffset.x = 0.0f; + baseOffset.y = 0.0f; + baseOffset.z = 5.0f; + Matrix_MultVec3f(&baseOffset, &spawnOffset); + spawnX = this->mouthPos.x + spawnOffset.x; + spawnY = this->mouthPos.y + spawnOffset.y; + spawnZ = this->mouthPos.z + spawnOffset.z; + + nut = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_NUTSBALL, spawnX, spawnY, spawnZ, + this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, 4); + if (nut != NULL) { + nut->velocity.y = spawnOffset.y * 0.5f; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_THROW); + this->spawnedItem = true; + } +} + +void EnDntNomal_StageSetupReturn(EnDntNomal* this, GlobalContext* globalCtx) { + this->endFrame = (f32)Animation_GetLastFrame(&gDntStageWalkAnim); + Animation_Change(&this->skelAnime, &gDntStageWalkAnim, 1.5f, 0.0f, this->endFrame, ANIMMODE_LOOP, -10.0f); + this->actor.speedXZ = 4.0f; + this->isSolid = false; + this->actionFunc = EnDntNomal_StageReturn; +} + +void EnDntNomal_StageReturn(EnDntNomal* this, GlobalContext* globalCtx) { + f32 sp2C; + f32 sp28; + + SkelAnime_Update(&this->skelAnime); + sp2C = this->flowerPos.x - this->actor.world.pos.x; + sp28 = this->flowerPos.z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(sp2C, sp28) * (0x8000 / M_PI), 1, 0xBB8, 0); + if (this->timer5 == 0) { + this->timer5 = 10; + } else if (!(this->timer5 & 1)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if ((fabsf(sp2C) < 7.0f) && (fabsf(sp28) < 7.0f)) { + this->actor.world.pos.x = this->flowerPos.x; + this->actor.world.pos.z = this->flowerPos.z; + this->actor.speedXZ = 0.0f; + this->actionFunc = EnDntNomal_SetupStageHide; + } +} + +void EnDntNomal_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDntNomal* this = (EnDntNomal*)thisx; + + if (this->timer1 != 0) { + this->timer1--; + } + if (this->timer2 != 0) { + this->timer2--; + } + if (this->timer3 != 0) { + this->timer3--; + } + if (this->timer4 != 0) { + this->timer4--; + } + if (this->timer5 != 0) { + this->timer5--; + } + if (this->blinkTimer != 0) { + this->blinkTimer--; + } + this->actor.world.rot.x = this->actor.shape.rot.x; + if (this->actionFunc != EnDntNomal_StageCelebrate) { + this->actor.world.rot.y = this->actor.shape.rot.y; + } + this->unkCounter++; + if (this->type != ENDNTNOMAL_TARGET) { + switch (this->stageSignal) { + case DNT_SIGNAL_LOOK: + if (this->stagePrize == DNT_PRIZE_NONE) { + this->actionFunc = EnDntNomal_SetupStageUp; + } else { + this->actionFunc = EnDntNomal_StageUp; + } + break; + case DNT_SIGNAL_CELEBRATE: + this->action = DNT_ACTION_NONE; + this->actor.colChkInfo.mass = 0; + this->timer3 = (s16)Rand_ZeroFloat(3.0f) + (s16)(this->type * 0.5f); + this->actionFunc = EnDntNomal_SetupStageUnburrow; + break; + case DNT_SIGNAL_DANCE: + this->action = DNT_ACTION_DANCE; + this->actionFunc = EnDntNomal_SetupStageUnburrow; + break; + case DNT_SIGNAL_HIDE: + this->actionFunc = EnDntNomal_SetupStageHide; + break; + case DNT_SIGNAL_RETURN: + this->actionFunc = EnDntNomal_StageSetupReturn; + break; + case DNT_SIGNAL_UNUSED: + this->actionFunc = EnDntNomal_SetupStageDance; + break; + case DNT_SIGNAL_NONE: + break; + } + } + if (this->stageSignal != DNT_SIGNAL_NONE) { + this->stageSignal = DNT_SIGNAL_NONE; + } + if (this->blinkTimer == 0) { + this->eyeState++; + if (this->eyeState >= 3) { + this->eyeState = 0; + this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 0x1D); + if (this->type == ENDNTNOMAL_TARGET) { + Collider_SetQuadVertices(&this->targetQuad, &this->targetVtx[0], &this->targetVtx[1], &this->targetVtx[2], + &this->targetVtx[3]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->targetQuad.base); + } else { + Collider_UpdateCylinder(&this->actor, &this->bodyCyl); + if (this->isSolid) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCyl.base); + } + } +} + +s32 EnDntNomal_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnDntNomal* this = (EnDntNomal*)thisx; + + if ((limbIndex == 1) || (limbIndex == 3) || (limbIndex == 4) || (limbIndex == 5) || (limbIndex == 6)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1733); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, sLeafColors[this->type - ENDNTNOMAL_STAGE].r, + sLeafColors[this->type - ENDNTNOMAL_STAGE].g, sLeafColors[this->type - ENDNTNOMAL_STAGE].b, 255); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1743); + } + return false; +} + +void EnDntNomal_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnDntNomal* this = (EnDntNomal*)thisx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (this->type == ENDNTNOMAL_TARGET) { + if (limbIndex == 5) { + Matrix_MultVec3f(&zeroVec, &this->mouthPos); + } + } else if (limbIndex == 7) { + Matrix_MultVec3f(&zeroVec, &this->mouthPos); + } +} + +void EnDntNomal_DrawStageScrub(Actor* thisx, GlobalContext* globalCtx) { + static void* blinkTex[] = { gDntStageEyeOpenTex, gDntStageEyeHalfTex, gDntStageEyeShutTex }; + EnDntNomal* this = (EnDntNomal*)thisx; + Vec3f dustScale = { 0.25f, 0.25f, 0.25f }; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1790); + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(blinkTex[this->eyeState])); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnDntNomal_OverrideLimbDraw, + EnDntNomal_PostLimbDraw, this); + Matrix_Translate(this->flowerPos.x, this->flowerPos.y, this->flowerPos.z, MTXMODE_NEW); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, sLeafColors[this->type - ENDNTNOMAL_STAGE].r, + sLeafColors[this->type - ENDNTNOMAL_STAGE].g, sLeafColors[this->type - ENDNTNOMAL_STAGE].b, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1814), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDntStageFlowerDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1817); + if (this->actionFunc == EnDntNomal_StageCelebrate) { + func_80033C30(&this->actor.world.pos, &dustScale, 255, globalCtx); + } +} + +void EnDntNomal_DrawTargetScrub(Actor* thisx, GlobalContext* globalCtx) { + EnDntNomal* this = (EnDntNomal*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1833); + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, EnDntNomal_PostLimbDraw, + this); + Matrix_Translate(this->flowerPos.x, this->flowerPos.y, this->flowerPos.z, MTXMODE_NEW); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1848), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gHintNutsFlowerDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dnt_nomal.c", 1851); +} diff --git a/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h b/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h new file mode 100644 index 000000000..d94da9e10 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dnt_Nomal/z_en_dnt_nomal.h @@ -0,0 +1,48 @@ +#ifndef Z_EN_DNT_NOMAL_H +#define Z_EN_DNT_NOMAL_H + +#include "ultra64.h" +#include "global.h" + +struct EnDntNomal; + +typedef void (*EnDntNomalActionFunc)(struct EnDntNomal*, GlobalContext*); + +typedef struct EnDntNomal { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[11]; + /* 0x01D2 */ Vec3s morphTable[11]; + /* 0x0214 */ EnDntNomalActionFunc actionFunc; + /* 0x0218 */ u8 isSolid; + /* 0x0219 */ Vec3f flowerPos; + /* 0x0228 */ Vec3f targetVtx[4]; + /* 0x0258 */ s16 timer1; + /* 0x025A */ s16 timer2; + /* 0x025C */ s16 timer4; + /* 0x025E */ s16 timer5; + /* 0x0260 */ s16 blinkTimer; + /* 0x0262 */ s16 unkCounter; + /* 0x0264 */ s16 timer3; + /* 0x0266 */ s16 objId; + /* 0x0268 */ s16 eyeState; + /* 0x026A */ s16 type; + /* 0x026C */ s16 hitCounter; + /* 0x026E */ s16 endFrame; + /* 0x0270 */ s16 stageSignal; + /* 0x0272 */ s16 rotDirection; + /* 0x0274 */ s16 action; + /* 0x0276 */ u8 ignore; + /* 0x0277 */ u8 spawnedItem; + /* 0x0278 */ u8 stagePrize; + /* 0x0279 */ s8 objIndex; + /* 0x027C */ Vec3f mouthPos; + /* 0x0288 */ Vec3f targetPos; + /* 0x0294 */ ColliderQuad targetQuad; + /* 0x0314 */ ColliderCylinder bodyCyl; +} EnDntNomal; // size = 0x0360 + +#define ENDNTNOMAL_TARGET 0 +#define ENDNTNOMAL_STAGE 1 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c b/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c new file mode 100644 index 000000000..6b95bd2e5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.c @@ -0,0 +1,650 @@ +/* + * File: z_en_dodojr.c + * Overlay: ovl_En_Dodojr + * Description: Baby Dodongo + */ + +#include "z_en_dodojr.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/object_dodojr/object_dodojr.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnDodojr_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDodojr_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDodojr_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDodojr_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809F73AC(EnDodojr* this, GlobalContext* globalCtx); +void func_809F7BE4(EnDodojr* this, GlobalContext* globalCtx); +void func_809F74C4(EnDodojr* this, GlobalContext* globalCtx); +void func_809F758C(EnDodojr* this, GlobalContext* globalCtx); +void func_809F786C(EnDodojr* this, GlobalContext* globalCtx); +void func_809F799C(EnDodojr* this, GlobalContext* globalCtx); +void func_809F78EC(EnDodojr* this, GlobalContext* globalCtx); +void func_809F773C(EnDodojr* this, GlobalContext* globalCtx); +void func_809F77AC(EnDodojr* this, GlobalContext* globalCtx); +void func_809F784C(EnDodojr* this, GlobalContext* globalCtx); +void func_809F7AB8(EnDodojr* this, GlobalContext* globalCtx); +void func_809F7A00(EnDodojr* this, GlobalContext* globalCtx); +void func_809F7B3C(EnDodojr* this, GlobalContext* globalCtx); +void func_809F7C48(EnDodojr* this, GlobalContext* globalCtx); +void func_809F768C(EnDodojr* this, GlobalContext* globalCtx); + +const ActorInit En_Dodojr_InitVars = { + ACTOR_EN_DODOJR, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DODOJR, + sizeof(EnDodojr), + (ActorFunc)EnDodojr_Init, + (ActorFunc)EnDodojr_Destroy, + (ActorFunc)EnDodojr_Update, + (ActorFunc)EnDodojr_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFC5FFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 18, 20, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInit = { 1, 2, 25, 25, 0xFF }; + +void EnDodojr_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDodojr* this = (EnDodojr*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 18.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &object_dodojr_Skel_0020E0, &object_dodojr_Anim_0009D4, + this->jointTable, this->morphTable, 15); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(4), &sColChkInit); + + this->actor.naviEnemyId = 0xE; + this->actor.flags &= ~ACTOR_FLAG_0; + + Actor_SetScale(&this->actor, 0.02f); + + this->actionFunc = func_809F73AC; +} + +void EnDodojr_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDodojr* this = (EnDodojr*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_809F64D0(EnDodojr* this) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + Actor_SetColorFilter(&this->actor, 0x4000, 200, 0, 8); +} + +void func_809F6510(EnDodojr* this, GlobalContext* globalCtx, s32 count) { + Color_RGBA8 prim = { 170, 130, 90, 255 }; + Color_RGBA8 env = { 100, 60, 20, 0 }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + Vec3f pos; + s16 angle = ((Rand_ZeroOne() - 0.5f) * 65536.0f); + s32 i; + + pos.y = this->dustPos.y; + + for (i = count; i >= 0; i--, angle += (s16)(0x10000 / count)) { + accel.x = (Rand_ZeroOne() - 0.5f) * 4.0f; + accel.z = (Rand_ZeroOne() - 0.5f) * 4.0f; + + pos.x = (Math_SinS(angle) * 22.0f) + this->dustPos.x; + pos.z = (Math_CosS(angle) * 22.0f) + this->dustPos.z; + + func_8002836C(globalCtx, &pos, &velocity, &accel, &prim, &env, 120, 40, 10); + } +} + +void func_809F6730(EnDodojr* this, GlobalContext* globalCtx, Vec3f* arg2) { + Color_RGBA8 prim = { 170, 130, 90, 255 }; + Color_RGBA8 env = { 100, 60, 20, 0 }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + Vec3f pos; + s16 angle = ((Rand_ZeroOne() - 0.5f) * 65536.0f); + + pos.y = this->actor.floorHeight; + + accel.x = (Rand_ZeroOne() - 0.5f) * 2; + accel.z = (Rand_ZeroOne() - 0.5f) * 2; + + pos.x = (Math_SinS(angle) * 11.0f) + arg2->x; + pos.z = (Math_CosS(angle) * 11.0f) + arg2->z; + + func_8002836C(globalCtx, &pos, &velocity, &accel, &prim, &env, 100, 60, 8); +} + +s32 func_809F68B0(EnDodojr* this, GlobalContext* globalCtx) { + if (this->actor.velocity.y >= 0.0f) { + return 0; + } + + if (this->unk_1FC == 0) { + return 0; + } + + if (this->actor.bgCheckFlags & 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + this->dustPos = this->actor.world.pos; + func_809F6510(this, globalCtx, 10); + this->actor.velocity.y = 10.0f / (4 - this->unk_1FC); + this->unk_1FC--; + + if (this->unk_1FC == 0) { + this->actor.velocity.y = 0.0f; + return 1; + } + } + + return 0; +} + +void func_809F6994(EnDodojr* this) { + f32 lastFrame = Animation_GetLastFrame(&object_dodojr_Anim_000860); + + Animation_Change(&this->skelAnime, &object_dodojr_Anim_000860, 1.8f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -10.0f); + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 2.6f; + this->actor.gravity = -0.8f; +} + +void func_809F6A20(EnDodojr* this) { + f32 lastFrame = Animation_GetLastFrame(&object_dodojr_Anim_0004A0); + + Animation_Change(&this->skelAnime, &object_dodojr_Anim_0004A0, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, -10.0f); + this->actor.speedXZ = 0.0f; + this->actor.velocity.x = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.gravity = -0.8f; + + if (this->unk_1FC == 0) { + this->unk_1FC = 3; + this->actor.velocity.y = 10.0f; + } +} + +void func_809F6AC4(EnDodojr* this) { + f32 lastFrame = Animation_GetLastFrame(&object_dodojr_Anim_0005F0); + + Animation_Change(&this->skelAnime, &object_dodojr_Anim_0005F0, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP, 0.0f); + this->actor.velocity.y = 0.0f; + this->actor.gravity = -0.8f; +} + +void func_809F6B38(EnDodojr* this) { + f32 lastFrame = Animation_GetLastFrame(&object_dodojr_Anim_000724); + + Animation_Change(&this->skelAnime, &object_dodojr_Anim_000724, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP, -10.0f); + this->actor.gravity = -0.8f; + this->unk_1FC = 3; + this->actor.velocity.y = 10.0f; +} + +void func_809F6BBC(EnDodojr* this) { + this->actor.shape.shadowDraw = NULL; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.home.pos = this->actor.world.pos; + this->actor.speedXZ = 0.0f; + this->actor.gravity = -0.8f; + this->timer3 = 30; + this->dustPos = this->actor.world.pos; +} + +void func_809F6C24(EnDodojr* this) { + Animation_Change(&this->skelAnime, &object_dodojr_Anim_000724, 1.0f, 8.0f, 12.0f, ANIMMODE_ONCE, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_EAT); + this->actor.speedXZ = 0.0f; + this->actor.velocity.x = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.gravity = -0.8f; +} + +s32 func_809F6CA4(EnDodojr* this, GlobalContext* globalCtx) { + Actor* bomb; + Vec3f unkVec = { 99999.0f, 99999.0f, 99999.0f }; + s32 retVar = 0; + f32 xDist; + f32 yDist; + f32 zDist; + + bomb = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + this->bomb = NULL; + + while (bomb != NULL) { + if ((bomb->params != 0) || (bomb->parent != NULL) || (bomb->update == NULL)) { + bomb = bomb->next; + continue; + } + + if (bomb->id != ACTOR_EN_BOM) { + bomb = bomb->next; + continue; + } + + xDist = bomb->world.pos.x - this->actor.world.pos.x; + yDist = bomb->world.pos.y - this->actor.world.pos.y; + zDist = bomb->world.pos.z - this->actor.world.pos.z; + + if ((fabsf(xDist) >= fabsf(unkVec.x)) || (fabsf(yDist) >= fabsf(unkVec.y)) || + (fabsf(zDist) >= fabsf(unkVec.z))) { + bomb = bomb->next; + continue; + } + + this->bomb = bomb; + unkVec = bomb->world.pos; + retVar = 1; + bomb = bomb->next; + } + + return retVar; +} + +s32 func_809F6DD0(EnDodojr* this) { + if (this->bomb == NULL) { + return 0; + } else if (this->bomb->parent != NULL) { + return 0; + } else if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->bomb->world.pos) > 30.0f) { + return 0; + } else { + this->bomb->parent = &this->actor; + return 1; + } +} + +void func_809F6E54(EnDodojr* this, GlobalContext* globalCtx) { + f32 angles[] = { 0.0f, 210.0f, 60.0f, 270.0f, 120.0f, 330.0f, 180.0f, 30.0f, 240.0f, 90.0f, 300.0f, 150.0f }; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + Vec3f pos; + s16 angleIndex; + + if ((this->bomb == NULL) || (this->bomb->update == NULL) || + ((this->bomb != NULL) && (this->bomb->parent != NULL))) { + func_809F6CA4(this, globalCtx); + } + + if (this->bomb != NULL) { + pos = this->bomb->world.pos; + } else { + pos = player->actor.world.pos; + } + + if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &pos) > 80.0f) { + angleIndex = (s16)(this->actor.home.pos.x + this->actor.home.pos.y + this->actor.home.pos.z + + globalCtx->state.frames / 30) % + 12; + angleIndex = ABS(angleIndex); + pos.x += 80.0f * sinf(angles[angleIndex]); + pos.z += 80.0f * cosf(angles[angleIndex]); + } + + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &pos), 10, 1000, 1); + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +s32 func_809F706C(EnDodojr* this) { + if (this->actor.xzDistToPlayer > 40.0f) { + return 0; + } else { + return 1; + } +} + +void func_809F709C(EnDodojr* this) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_DEAD); + this->actor.flags &= ~ACTOR_FLAG_0; + func_809F6A20(this); + this->actionFunc = func_809F7AB8; +} + +s32 func_809F70E8(EnDodojr* this, GlobalContext* globalCtx) { + if ((this->actionFunc == func_809F773C) || (this->actionFunc == func_809F77AC) || + (this->actionFunc == func_809F784C) || (this->actionFunc == func_809F7A00) || + (this->actionFunc == func_809F7AB8) || (this->actionFunc == func_809F7B3C) || + (this->actionFunc == func_809F7BE4)) { + return 0; + } + + if (globalCtx->actorCtx.unk_02 != 0) { + if (this->actionFunc != func_809F73AC) { + if (this->actionFunc == func_809F74C4) { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + } + + func_809F709C(this); + } + return 0; + } + + if (!(this->collider.base.acFlags & 2)) { + return 0; + } else { + this->collider.base.acFlags &= ~2; + + if ((this->actionFunc == func_809F73AC) || (this->actionFunc == func_809F74C4)) { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + } + + if ((this->actor.colChkInfo.damageEffect == 0) && (this->actor.colChkInfo.damage != 0)) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->timer2 = 2; + this->actionFunc = func_809F7C48; + return 1; + } + + if ((this->actor.colChkInfo.damageEffect == 1) && (this->actionFunc != func_809F78EC) && + (this->actionFunc != func_809F786C)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->timer1 = 120; + Actor_SetColorFilter(&this->actor, 0, 200, 0, 120); + func_809F6A20(this); + this->actionFunc = func_809F786C; + } + + return 0; + } +} + +void func_809F72A4(EnDodojr* this, GlobalContext* globalCtx) { + Collider_UpdateCylinder(&this->actor, &this->collider); + + if ((this->actionFunc != func_809F73AC) && (this->actionFunc != func_809F7BE4)) { + if ((this->actionFunc == func_809F74C4) || (this->actionFunc == func_809F758C) || + (this->actionFunc == func_809F799C)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if ((this->actionFunc == func_809F74C4) || (this->actionFunc == func_809F758C) || + (this->actionFunc == func_809F786C) || (this->actionFunc == func_809F78EC) || + (this->actionFunc == func_809F799C)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_809F73AC(EnDodojr* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&object_dodojr_Anim_000860); + Player* player = GET_PLAYER(globalCtx); + f32 dist; + + if (!(this->actor.xzDistToPlayer >= 320.0f)) { + dist = this->actor.world.pos.y - player->actor.world.pos.y; + + if (!(dist >= 40.0f)) { + Animation_Change(&this->skelAnime, &object_dodojr_Anim_000860, 1.8f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, + -10.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_UP); + this->actor.world.pos.y -= 60.0f; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.world.rot.x -= 0x4000; + this->actor.shape.rot.x = this->actor.world.rot.x; + this->dustPos = this->actor.world.pos; + this->dustPos.y = this->actor.floorHeight; + this->actionFunc = func_809F74C4; + } + } +} + +void func_809F74C4(EnDodojr* this, GlobalContext* globalCtx) { + f32 sp2C; + + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 4, 0x3E8, 0x64); + sp2C = this->actor.shape.rot.x; + sp2C /= 16384.0f; + this->actor.world.pos.y = this->actor.home.pos.y + (60.0f * sp2C); + func_809F6510(this, globalCtx, 3); + + if (sp2C == 0.0f) { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + this->actor.world.rot.x = this->actor.shape.rot.x; + this->actor.speedXZ = 2.6f; + this->actionFunc = func_809F758C; + } +} + +void func_809F758C(EnDodojr* this, GlobalContext* globalCtx) { + func_8002D868(&this->actor); + func_809F6730(this, globalCtx, &this->actor.world.pos); + + if (DECR(this->timer4) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_MOVE); + this->timer4 = 5; + } + + if (func_809F6DD0(this) != 0) { + func_809F6C24(this); + this->actionFunc = func_809F768C; + return; + } + + func_809F6E54(this, globalCtx); + + if (func_809F706C(this) != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_CRY); + func_809F6B38(this); + this->actionFunc = func_809F799C; + } + + if (this->actor.bgCheckFlags & 8) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_DOWN); + func_809F6BBC(this); + this->actionFunc = func_809F7A00; + } +} + +void func_809F768C(EnDodojr* this, GlobalContext* globalCtx) { + EnBom* bomb; + + if (((s16)this->skelAnime.curFrame - 8) < 4) { + bomb = (EnBom*)this->bomb; + bomb->timer++; + this->bomb->world.pos = this->headPos; + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_DRINK); + Actor_Kill(this->bomb); + this->timer3 = 24; + this->unk_1FC = 0; + this->actionFunc = func_809F773C; + } +} + +void func_809F773C(EnDodojr* this, GlobalContext* globalCtx) { + if (DECR(this->timer3) == 0) { + func_809F64D0(this); + this->actor.flags &= ~ACTOR_FLAG_0; + func_809F6A20(this); + this->actionFunc = func_809F77AC; + } +} + +void func_809F77AC(EnDodojr* this, GlobalContext* globalCtx) { + this->rootScale = 1.2f; + this->rootScale *= ((f32)this->actor.colorFilterTimer / 8); + func_8002D868(&this->actor); + + if (func_809F68B0(this, globalCtx) != 0) { + this->timer3 = 60; + func_809F6AC4(this); + this->unk_1FC = 7; + this->actionFunc = func_809F784C; + } +} + +void func_809F784C(EnDodojr* this, GlobalContext* globalCtx) { + func_809F7B3C(this, globalCtx); +} + +void func_809F786C(EnDodojr* this, GlobalContext* globalCtx) { + func_8002D868(&this->actor); + + if (func_809F68B0(this, globalCtx) != 0) { + func_809F6AC4(this); + this->actionFunc = func_809F78EC; + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, 0, 4, 1000, 10); + this->actor.world.rot.x = this->actor.shape.rot.x; + this->actor.colorFilterTimer = this->timer1; +} + +void func_809F78EC(EnDodojr* this, GlobalContext* globalCtx) { + if (DECR(this->timer1) != 0) { + if (this->timer1 < 30) { + if ((this->timer1 & 1) != 0) { + this->actor.world.pos.x += 1.5f; + this->actor.world.pos.z += 1.5f; + } else { + this->actor.world.pos.x -= 1.5f; + this->actor.world.pos.z -= 1.5f; + } + + return; + } + } else { + func_809F6994(this); + this->actionFunc = func_809F758C; + } +} + +void func_809F799C(EnDodojr* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_24; + func_8002D868(&this->actor); + + if (func_809F68B0(this, globalCtx) != 0) { + func_809F6994(this); + this->actionFunc = func_809F758C; + } +} + +void func_809F7A00(EnDodojr* this, GlobalContext* globalCtx) { + f32 tmp; + + Math_SmoothStepToS(&this->actor.shape.rot.x, 0x4000, 4, 1000, 100); + + if (DECR(this->timer3) != 0) { + tmp = (30 - this->timer3) / 30.0f; + this->actor.world.pos.y = this->actor.home.pos.y - (60.0f * tmp); + } else { + Actor_Kill(&this->actor); + } + + func_809F6510(this, globalCtx, 3); +} + +void func_809F7AB8(EnDodojr* this, GlobalContext* globalCtx) { + func_8002D868(&this->actor); + Math_SmoothStepToS(&this->actor.shape.rot.y, 0, 4, 1000, 10); + this->actor.world.rot.x = this->actor.shape.rot.x; + + if (func_809F68B0(this, globalCtx) != 0) { + this->timer3 = 60; + func_809F6AC4(this); + this->unk_1FC = 7; + this->actionFunc = func_809F7B3C; + } +} + +void func_809F7B3C(EnDodojr* this, GlobalContext* globalCtx) { + EnBom* bomb; + + if (this->unk_1FC != 0) { + if (this->actor.colorFilterTimer == 0) { + Actor_SetColorFilter(&this->actor, 0x4000, 200, 0, this->unk_1FC); + this->unk_1FC--; + } + } else { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, BOMB_BODY); + + if (bomb != NULL) { + bomb->timer = 0; + } + + this->timer3 = 8; + this->actionFunc = func_809F7BE4; + } +} + +void func_809F7BE4(EnDodojr* this, GlobalContext* globalCtx) { + if (DECR(this->timer3) == 0) { + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0x40); + Actor_Kill(&this->actor); + } +} + +void func_809F7C48(EnDodojr* this, GlobalContext* globalCtx) { + if (DECR(this->timer2) == 0) { + func_809F709C(this); + } +} + +void EnDodojr_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDodojr* this = (EnDodojr*)thisx; + + SkelAnime_Update(&this->skelAnime); + Actor_MoveForward(&this->actor); + func_809F70E8(this, globalCtx); + + if (this->actionFunc != func_809F73AC) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, this->collider.dim.radius, this->collider.dim.height, 0.0f, 5); + } + + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, 10.0f); + func_809F72A4(this, globalCtx); +} + +s32 func_809F7D50(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnDodojr* this = (EnDodojr*)thisx; + Vec3f D_809F7F64 = { 480.0f, 620.0f, 0.0f }; + + if (limbIndex == 1) { + Matrix_Scale((this->rootScale * 0.5f) + 1.0f, this->rootScale + 1.0f, (this->rootScale * 0.5f) + 1.0f, + MTXMODE_APPLY); + } + + if (limbIndex == 4) { + Matrix_MultVec3f(&D_809F7F64, &this->headPos); + } + + return false; +} + +void func_809F7DFC(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { +} + +void EnDodojr_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDodojr* this = (EnDodojr*)thisx; + + if ((this->actionFunc != func_809F73AC) && (this->actionFunc != func_809F7BE4)) { + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, func_809F7D50, func_809F7DFC, + &this->actor); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.h b/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.h new file mode 100644 index 000000000..2ca10bd5f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dodojr/z_en_dodojr.h @@ -0,0 +1,29 @@ +#ifndef Z_EN_DODOJR_H +#define Z_EN_DODOJR_H + +#include "ultra64.h" +#include "global.h" + +struct EnDodojr; + +typedef void (*EnDodojrActionFunc)(struct EnDodojr*, GlobalContext*); + +typedef struct EnDodojr { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnDodojrActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ Actor* bomb; + /* 0x01E4 */ Vec3f headPos; + /* 0x01F0 */ Vec3f dustPos; + /* 0x01FC */ s16 unk_1FC; + /* 0x01FE */ s16 timer1; + /* 0x0200 */ s16 timer2; + /* 0x0202 */ s16 timer3; + /* 0x0204 */ s16 timer4; + /* 0x0208 */ f32 rootScale; // scale used with the root limb + /* 0x020C */ Vec3s jointTable[15]; + /* 0x0266 */ Vec3s morphTable[15]; +} EnDodojr; // size = 0x02C0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c new file mode 100644 index 000000000..88a2be0dd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -0,0 +1,968 @@ +#include "z_en_dodongo.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "overlays/actors/ovl_En_Bombf/z_en_bombf.h" +#include "objects/object_dodongo/object_dodongo.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +typedef enum { + DODONGO_SWEEP_TAIL, + DODONGO_SWALLOW_BOMB, + DODONGO_DEATH, + DODONGO_BREATHE_FIRE, + DODONGO_IDLE, + DODONGO_END_BREATHE_FIRE, + DODONGO_UNUSED, + DODONGO_STUNNED, + DODONGO_WALK +} EnDodongoActionState; + +void EnDodongo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDodongo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDodongo_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDodongo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDodongo_SetupDeath(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_ShiftVecRadial(s16 yaw, f32 radius, Vec3f* vec); +s32 EnDodongo_AteBomb(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_SetupIdle(EnDodongo* this); + +void EnDodongo_Idle(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_EndBreatheFire(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_BreatheFire(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_SwallowBomb(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_Walk(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_Stunned(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_Death(EnDodongo* this, GlobalContext* globalCtx); +void EnDodongo_SweepTail(EnDodongo* this, GlobalContext* globalCtx); + +const ActorInit En_Dodongo_InitVars = { + ACTOR_EN_DODONGO, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DODONGO, + sizeof(EnDodongo), + (ActorFunc)EnDodongo_Init, + (ActorFunc)EnDodongo_Destroy, + (ActorFunc)EnDodongo_Update, + (ActorFunc)EnDodongo_Draw, + NULL, +}; + +static ColliderJntSphElementInit sBodyElementsInit[6] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { 0, 0, 0 }, 17 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 14, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 21, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 28, { { 0, 0, 0 }, 20 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x0D800691, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON | OCELEM_UNK3, + }, + { 6, { { 0, 0, 0 }, 35 }, 100 }, + }, +}; + +static ColliderJntSphInit sBodyJntSphInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 6, + sBodyElementsInit, +}; + +static ColliderTrisElementInit sHardElementsInit[3] = { + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xF24BF96E, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, 14.0f, 2.0f }, { -10.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCBF96E, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, -6.0f, 2.0f }, { 9.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCBF96E, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, -6.0f, 2.0f }, { 9.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, +}; + +static ColliderTrisInit sHardTrisInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 3, + sHardElementsInit, +}; + +static ColliderQuadInit sAttackQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x01, 0x10 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(1, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0xF), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x6), + /* Ice magic */ DMG_ENTRY(3, 0xF), + /* Light magic */ DMG_ENTRY(0, 0x6), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +void EnDodongo_SetupAction(EnDodongo* this, EnDodongoActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnDodongo_SpawnBombSmoke(EnDodongo* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + Vec3f pos = this->headPos; + s16 randAngle = Rand_CenteredFloat(0x4000); + f32 randCos; + f32 randSin; + + randCos = Math_CosS(this->actor.shape.rot.y + randAngle); + randSin = Math_SinS(this->actor.shape.rot.y + randAngle); + if (this->bombSmokePrimColor.r > 30) { + this->bombSmokePrimColor.r -= 16; + this->bombSmokePrimColor.g -= 16; + } + + if (this->bombSmokePrimColor.b < 30) { + this->bombSmokePrimColor.b += 5; + this->bombSmokePrimColor.a += 8; + this->bombSmokeEnvColor.a += 8; + } + if (this->bombSmokeEnvColor.r != 0) { + this->bombSmokeEnvColor.r -= 15; + } + if (this->bombSmokeEnvColor.g != 0) { + this->bombSmokeEnvColor.g--; + } + velocity.x = randSin * 3.5f; + velocity.y = this->bombSmokeEnvColor.r * 0.02f; + velocity.z = randCos * 3.5f; + accel.x = ((Rand_ZeroOne() * 0.1f) + 0.15f) * -randSin; + accel.z = ((Rand_ZeroOne() * 0.1f) + 0.15f) * -randCos; + func_8002836C(globalCtx, &pos, &velocity, &accel, &this->bombSmokePrimColor, &this->bombSmokeEnvColor, 100, 25, 20); + + randAngle = Rand_ZeroOne() * 0x2000; + randCos = Math_CosS(this->actor.shape.rot.y + randAngle); + randSin = Math_SinS(this->actor.shape.rot.y + randAngle); + pos.x -= randCos * 6.0f; + pos.z += randSin * 6.0f; + velocity.x = -randCos * 3.5f; + velocity.y = this->bombSmokeEnvColor.r * 0.02f; + velocity.z = randSin * 3.5f; + accel.x = ((Rand_ZeroOne() * 0.1f) + 0.15f) * randCos; + accel.z = ((Rand_ZeroOne() * 0.1f) + 0.15f) * -randSin; + func_8002836C(globalCtx, &pos, &velocity, &accel, &this->bombSmokePrimColor, &this->bombSmokeEnvColor, 100, 25, 20); + + randAngle = Rand_ZeroOne() * 0x2000; + randCos = Math_CosS(this->actor.shape.rot.y + randAngle); + randSin = Math_SinS(this->actor.shape.rot.y + randAngle); + + pos.x = this->headPos.x + (randCos * 6.0f); + pos.z = this->headPos.z - (randSin * 6.0f); + velocity.x = randCos * 3.5f; + velocity.y = this->bombSmokeEnvColor.r * 0.02f; + velocity.z = -randSin * 3.5f; + accel.x = ((Rand_ZeroOne() * 0.1f) + 0.15f) * -randCos; + accel.z = ((Rand_ZeroOne() * 0.1f) + 0.15f) * randSin; + func_8002836C(globalCtx, &pos, &velocity, &accel, &this->bombSmokePrimColor, &this->bombSmokeEnvColor, 100, 25, 20); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x0D, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2800, ICHAIN_STOP), +}; + +void EnDodongo_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDodongo* this = (EnDodongo*)thisx; + EffectBlureInit1 blureInit; + + this->actor.targetMode = 3; + Actor_ProcessInitChain(&this->actor, sInitChain); + this->bombSmokePrimColor.r = this->bombSmokePrimColor.g = this->bombSmokeEnvColor.r = 255; + this->bombSmokePrimColor.a = this->bombSmokeEnvColor.a = 200; + this->bombSmokeEnvColor.g = 10; + this->bodyScale.x = this->bodyScale.y = this->bodyScale.z = 1.0f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 48.0f); + Actor_SetScale(&this->actor, 0.01875f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gDodongoSkel, &gDodongoWaitAnim, this->jointTable, this->morphTable, + 31); + this->actor.colChkInfo.health = 4; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.colChkInfo.damageTable = &sDamageTable; + Collider_InitQuad(globalCtx, &this->colliderAT); + Collider_InitTris(globalCtx, &this->colliderHard); + Collider_InitJntSph(globalCtx, &this->colliderBody); + Collider_SetQuad(globalCtx, &this->colliderAT, &this->actor, &sAttackQuadInit); + Collider_SetTris(globalCtx, &this->colliderHard, &this->actor, &sHardTrisInit, this->trisElements); + Collider_SetJntSph(globalCtx, &this->colliderBody, &this->actor, &sBodyJntSphInit, this->sphElements); + + blureInit.p1StartColor[0] = blureInit.p1StartColor[1] = blureInit.p1StartColor[2] = blureInit.p1StartColor[3] = + blureInit.p2StartColor[0] = blureInit.p2StartColor[1] = blureInit.p2StartColor[2] = blureInit.p1EndColor[0] = + blureInit.p1EndColor[1] = blureInit.p1EndColor[2] = blureInit.p2EndColor[0] = blureInit.p2EndColor[1] = + blureInit.p2EndColor[2] = 255; + + blureInit.p1EndColor[3] = blureInit.p2EndColor[3] = 0; + blureInit.p2StartColor[3] = 64; + blureInit.elemDuration = 8; + blureInit.unkFlag = false; + blureInit.calcMode = 2; + + Effect_Add(globalCtx, &this->blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 60.0f, 70.0f, 0x1D); + EnDodongo_SetupIdle(this); +} + +void EnDodongo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDodongo* this = (EnDodongo*)thisx; + + Effect_Delete(globalCtx, this->blureIdx); + Collider_DestroyTris(globalCtx, &this->colliderHard); + Collider_DestroyJntSph(globalCtx, &this->colliderBody); + Collider_DestroyQuad(globalCtx, &this->colliderAT); +} + +void EnDodongo_SetupIdle(EnDodongo* this) { + Animation_MorphToLoop(&this->skelAnime, &gDodongoWaitAnim, -4.0f); + this->actor.speedXZ = 0.0f; + this->timer = Rand_S16Offset(30, 50); + this->actionState = DODONGO_IDLE; + EnDodongo_SetupAction(this, EnDodongo_Idle); +} + +void EnDodongo_SetupWalk(EnDodongo* this) { + f32 frames = Animation_GetLastFrame(&gDodongoWalkAnim); + + Animation_Change(&this->skelAnime, &gDodongoWalkAnim, 0.0f, 0.0f, frames, ANIMMODE_LOOP, -4.0f); + this->actor.speedXZ = 1.5f; + this->timer = Rand_S16Offset(50, 70); + this->rightFootStep = true; + this->actionState = DODONGO_WALK; + EnDodongo_SetupAction(this, EnDodongo_Walk); +} + +void EnDodongo_SetupBreatheFire(EnDodongo* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoBreatheFireAnim, -4.0f); + this->actionState = DODONGO_BREATHE_FIRE; + this->actor.speedXZ = 0.0f; + EnDodongo_SetupAction(this, EnDodongo_BreatheFire); +} + +void EnDodongo_SetupEndBreatheFire(EnDodongo* this) { + Animation_PlayOnce(&this->skelAnime, &gDodongoAfterBreatheFireAnim); + this->actionState = DODONGO_END_BREATHE_FIRE; + this->actor.speedXZ = 0.0f; + EnDodongo_SetupAction(this, EnDodongo_EndBreatheFire); +} + +void EnDodongo_SetupSwallowBomb(EnDodongo* this) { + Animation_Change(&this->skelAnime, &gDodongoBreatheFireAnim, -1.0f, 35.0f, 0.0f, ANIMMODE_ONCE, -4.0f); + this->actionState = DODONGO_SWALLOW_BOMB; + this->timer = 25; + this->actor.speedXZ = 0.0f; + EnDodongo_SetupAction(this, EnDodongo_SwallowBomb); +} + +void EnDodongo_SetupStunned(EnDodongo* this) { + Animation_Change(&this->skelAnime, &gDodongoBreatheFireAnim, 0.0f, 25.0f, 0.0f, ANIMMODE_ONCE, -4.0f); + this->actionState = DODONGO_STUNNED; + this->actor.speedXZ = 0.0f; + if (this->damageEffect == 0xF) { + this->iceTimer = 36; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + EnDodongo_SetupAction(this, EnDodongo_Stunned); +} + +void EnDodongo_Idle(EnDodongo* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((DECR(this->timer) == 0) && Animation_OnFrame(&this->skelAnime, 0.0f)) { + EnDodongo_SetupWalk(this); + } +} + +void EnDodongo_EndBreatheFire(EnDodongo* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnDodongo_SetupIdle(this); + this->timer = Rand_S16Offset(10, 20); + } +} + +void EnDodongo_BreatheFire(EnDodongo* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + s16 pad2; + s16 fireFrame; + + if ((s32)this->skelAnime.curFrame == 24) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_CRY); + } + if ((29.0f <= this->skelAnime.curFrame) && (this->skelAnime.curFrame <= 43.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_FIRE - SFX_FLAG); + fireFrame = this->skelAnime.curFrame - 29.0f; + pos = this->actor.world.pos; + pos.y += 35.0f; + EnDodongo_ShiftVecRadial(this->actor.world.rot.y, 30.0f, &pos); + EnDodongo_ShiftVecRadial(this->actor.world.rot.y, 2.5f, &accel); + EffectSsDFire_SpawnFixedScale(globalCtx, &pos, &velocity, &accel, 255 - (fireFrame * 10), fireFrame + 3); + } else if ((2.0f <= this->skelAnime.curFrame) && (this->skelAnime.curFrame <= 20.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_BREATH - SFX_FLAG); + } + if (SkelAnime_Update(&this->skelAnime)) { + EnDodongo_SetupEndBreatheFire(this); + } +} + +void EnDodongo_SwallowBomb(EnDodongo* this, GlobalContext* globalCtx) { + Vec3f smokeVel = { 0.0f, 0.0f, 0.0f }; + Vec3f smokeAccel = { 0.0f, 0.6f, 0.0f }; + Color_RGBA8 white = { 255, 255, 255, 255 }; + Vec3f deathFireVel = { 0.0f, 0.0f, 0.0f }; + Vec3f deathFireAccel = { 0.0f, 1.0f, 0.0f }; + s16 i; + Vec3f pos; + s32 pad; + + if (this->actor.child != NULL) { + this->actor.child->world.pos = this->mouthPos; + ((EnBom*)this->actor.child)->timer++; + } else if (this->actor.parent != NULL) { + this->actor.parent->world.pos = this->mouthPos; + ((EnBombf*)this->actor.parent)->timer++; + //! @bug An explosive can also be a bombchu, not always a bomb, which leads to a serious bug. ->timer (0x1F8) is + //! outside the bounds of the bombchu actor, and the memory it writes to happens to be one of the pointers in + //! the next arena node. When this value is written to, massive memory corruption occurs. + } + + if ((s32)this->skelAnime.curFrame == 28) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_EAT); + if (this->actor.child != NULL) { + Actor_Kill(this->actor.child); + this->actor.child = NULL; + } else if (this->actor.parent != NULL) { + Actor_Kill(this->actor.parent); + this->actor.parent = NULL; + } + } else if ((s32)this->skelAnime.curFrame == 24) { + this->timer--; + if (this->timer != 0) { + this->skelAnime.curFrame++; + if (this->timer == 10) { + for (i = 10; i >= 0; i--) { + deathFireVel.x = Rand_CenteredFloat(10.0f); + deathFireVel.y = Rand_CenteredFloat(10.0f); + deathFireVel.z = Rand_CenteredFloat(10.0f); + deathFireAccel.x = deathFireVel.x * -0.1f; + deathFireAccel.y = deathFireVel.y * -0.1f; + deathFireAccel.z = deathFireVel.z * -0.1f; + pos.x = this->sphElements[0].dim.worldSphere.center.x + deathFireVel.x; + pos.y = this->sphElements[0].dim.worldSphere.center.y + deathFireVel.y; + pos.z = this->sphElements[0].dim.worldSphere.center.z + deathFireVel.z; + func_8002836C(globalCtx, &pos, &deathFireVel, &deathFireAccel, &this->bombSmokePrimColor, + &this->bombSmokeEnvColor, 400, 10, 10); + } + Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + Actor_SetColorFilter(&this->actor, 0x4000, 0x78, 0, 8); + } + } + } + if ((s32)this->skelAnime.curFrame < 28) { + if (((s32)this->skelAnime.curFrame < 26) && (this->timer <= 10)) { + EnDodongo_SpawnBombSmoke(this, globalCtx); + } else { + pos = this->headPos; + func_8002829C(globalCtx, &pos, &smokeVel, &smokeAccel, &white, &white, 50, 5); + pos.x -= (Math_CosS(this->actor.shape.rot.y) * 6.0f); + pos.z += (Math_SinS(this->actor.shape.rot.y) * 6.0f); + func_8002829C(globalCtx, &pos, &smokeVel, &smokeAccel, &white, &white, 50, 5); + pos.x = this->headPos.x + (Math_CosS(this->actor.shape.rot.y) * 6.0f); + pos.z = this->headPos.z - (Math_SinS(this->actor.shape.rot.y) * 6.0f); + func_8002829C(globalCtx, &pos, &smokeVel, &smokeAccel, &white, &white, 50, 5); + } + } + this->bodyScale.y = this->bodyScale.z = (Math_SinS(this->actor.colorFilterTimer * 0x1000) * 0.5f) + 1.0f; + this->bodyScale.x = Math_SinS(this->actor.colorFilterTimer * 0x1000) + 1.0f; + + SkelAnime_Update(&this->skelAnime); + if (this->timer == 0) { + EnDodongo_SetupDeath(this, globalCtx); + } +} + +void EnDodongo_Walk(EnDodongo* this, GlobalContext* globalCtx) { + s32 pad; + f32 playbackSpeed; + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff = (s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y); + + yawDiff = ABS(yawDiff); + + Math_SmoothStepToF(&this->actor.speedXZ, 1.5f, 0.1f, 1.0f, 0.0f); + + playbackSpeed = this->actor.speedXZ * 0.75f; + if (this->actor.speedXZ >= 0.0f) { + if (playbackSpeed > 3.0f / 2) { + playbackSpeed = 3.0f / 2; + } + } else { + if (playbackSpeed < -3.0f / 2) { + playbackSpeed = -3.0f / 2; + } + } + this->skelAnime.playSpeed = playbackSpeed; + + SkelAnime_Update(&this->skelAnime); + if ((s32)this->skelAnime.curFrame < 21) { + if (!this->rightFootStep) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_WALK); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 10.0f, 3, 2.0f, 0xC8, 0xF, 0); + this->rightFootStep = true; + } + } else { + if (this->rightFootStep) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_WALK); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 10.0f, 3, 2.0f, 0xC8, 0xF, 0); + this->rightFootStep = false; + } + } + + if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 400.0f) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0x1F4, 0); + this->actor.flags |= ACTOR_FLAG_0; + if ((this->actor.xzDistToPlayer < 100.0f) && (yawDiff < 0x1388) && (this->actor.yDistToPlayer < 60.0f)) { + EnDodongo_SetupBreatheFire(this); + } + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + if ((Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) > 150.0f) || (this->retreatTimer != 0)) { + s16 yawToHome = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + + Math_SmoothStepToS(&this->actor.world.rot.y, yawToHome, 1, 0x1F4, 0); + } + if (this->retreatTimer != 0) { + this->retreatTimer--; + } + this->timer--; + if (this->timer == 0) { + if (Rand_ZeroOne() > 0.7f) { + this->timer = Rand_S16Offset(50, 70); + this->retreatTimer = Rand_S16Offset(15, 40); + } else { + EnDodongo_SetupIdle(this); + } + } + } + + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnDodongo_SetupSweepTail(EnDodongo* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoDamageAnim, -4.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_DAMAGE); + this->actionState = DODONGO_SWEEP_TAIL; + this->timer = 0; + this->actor.speedXZ = 0.0f; + EnDodongo_SetupAction(this, EnDodongo_SweepTail); +} + +void EnDodongo_SweepTail(EnDodongo* this, GlobalContext* globalCtx) { + s16 yawDiff1 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (SkelAnime_Update(&this->skelAnime)) { + if ((this->timer != 0) || (ABS(yawDiff1) < 0x4000)) { + this->sphElements[2].info.toucherFlags = TOUCH_NONE; + this->sphElements[1].info.toucherFlags = TOUCH_NONE; + this->colliderBody.base.atFlags = AT_NONE; + this->sphElements[2].info.toucher.dmgFlags = 0; + this->sphElements[1].info.toucher.dmgFlags = 0; + this->sphElements[2].info.toucher.damage = 0; + this->sphElements[1].info.toucher.damage = 0; + EnDodongo_SetupBreatheFire(this); + this->timer = Rand_S16Offset(5, 10); + } else { + s16 yawDiff2 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + AnimationHeader* animation; + + this->tailSwipeSpeed = (0xFFFF - ABS(yawDiff2)) / 0xF; + if ((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y) >= 0) { + this->tailSwipeSpeed = -this->tailSwipeSpeed; + animation = &gDodongoSweepTailLeftAnim; + } else { + animation = &gDodongoSweepTailRightAnim; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_TAIL); + Animation_PlayOnceSetSpeed(&this->skelAnime, animation, 2.0f); + this->timer = 18; + this->colliderBody.base.atFlags = this->sphElements[1].info.toucherFlags = + this->sphElements[2].info.toucherFlags = AT_ON | AT_TYPE_ENEMY; // also TOUCH_ON | TOUCH_SFX_WOOD + this->sphElements[1].info.toucher.dmgFlags = this->sphElements[2].info.toucher.dmgFlags = 0xFFCFFFFF; + this->sphElements[1].info.toucher.damage = this->sphElements[2].info.toucher.damage = 8; + } + } else if (this->timer > 1) { + Vec3f tailPos; + + this->timer--; + this->actor.shape.rot.y = this->actor.world.rot.y += this->tailSwipeSpeed; + tailPos.x = this->sphElements[1].dim.worldSphere.center.x; + tailPos.y = this->sphElements[1].dim.worldSphere.center.y; + tailPos.z = this->sphElements[1].dim.worldSphere.center.z; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &tailPos, 5.0f, 2, 2.0f, 100, 15, 0); + tailPos.x = this->sphElements[2].dim.worldSphere.center.x; + tailPos.y = this->sphElements[2].dim.worldSphere.center.y; + tailPos.z = this->sphElements[2].dim.worldSphere.center.z; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &tailPos, 5.0f, 2, 2.0f, 100, 15, 0); + + if (this->colliderBody.base.atFlags & AT_HIT) { + Player* player = GET_PLAYER(globalCtx); + + if (this->colliderBody.base.at == &player->actor) { + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } +} + +void EnDodongo_SetupDeath(EnDodongo* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &gDodongoDieAnim, -8.0f); + this->timer = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_J_DEAD); + this->actionState = DODONGO_DEATH; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + EnDodongo_SetupAction(this, EnDodongo_Death); +} + +void EnDodongo_Death(EnDodongo* this, GlobalContext* globalCtx) { + EnBom* bomb; + + if (this->skelAnime.curFrame < 35.0f) { + if (this->actor.params == EN_DODONGO_SMOKE_DEATH) { + EnDodongo_SpawnBombSmoke(this, globalCtx); + } + } else if (this->actor.colorFilterTimer == 0) { + Actor_SetColorFilter(&this->actor, 0x4000, 0x78, 0, 4); + } + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->timer == 0) { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 6, BOMB_BODY); + if (bomb != NULL) { + bomb->timer = 0; + this->timer = 8; + } + } + } else if ((s32)this->skelAnime.curFrame == 52) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } + if (this->timer != 0) { + this->timer--; + if (this->timer == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x40); + Actor_Kill(&this->actor); + } + } +} + +void EnDodongo_Stunned(EnDodongo* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->actor.colorFilterTimer == 0) { + if (this->actor.colChkInfo.health == 0) { + EnDodongo_SetupDeath(this, globalCtx); + } else { + EnDodongo_SetupIdle(this); + } + } +} + +void EnDodongo_CollisionCheck(EnDodongo* this, GlobalContext* globalCtx) { + if (this->colliderHard.base.acFlags & AC_BOUNCED) { + this->colliderHard.base.acFlags &= ~AC_BOUNCED; + this->colliderBody.base.acFlags &= ~AC_HIT; + } else if ((this->colliderBody.base.acFlags & AC_HIT) && (this->actionState > DODONGO_DEATH)) { + this->colliderBody.base.acFlags &= ~AC_HIT; + Actor_SetDropFlagJntSph(&this->actor, &this->colliderBody, 0); + if (this->actor.colChkInfo.damageEffect != 0xE) { + this->damageEffect = this->actor.colChkInfo.damageEffect; + if ((this->actor.colChkInfo.damageEffect == 1) || (this->actor.colChkInfo.damageEffect == 0xF)) { + if (this->actionState != DODONGO_STUNNED) { + Actor_SetColorFilter(&this->actor, 0, 0x78, 0, 0x50); + Actor_ApplyDamage(&this->actor); + EnDodongo_SetupStunned(this); + } + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 0x78, 0, 8); + if (Actor_ApplyDamage(&this->actor) == 0) { + EnDodongo_SetupDeath(this, globalCtx); + } else { + EnDodongo_SetupSweepTail(this); + } + } + } + } +} + +void EnDodongo_UpdateQuad(EnDodongo* this, GlobalContext* globalCtx) { + Vec3f sp94 = { -1000.0f, -1500.0f, 0.0f }; + Vec3f sp88 = { -1000.0f, -200.0f, 1500.0f }; + Vec3f sp7C = { -1000.0f, -200.0f, -1500.0f }; + Vec3f sp70 = { 0.0f, 0.0f, 0.0f }; + s32 pad4C[9]; // Possibly 3 more Vec3fs? + s32 a = 0; + s32 b = 1; // These indices are needed to match. + s32 c = 2; // Might be a way to quickly test vertex arrangements + s32 d = 3; + f32 xMod = Math_SinF((this->skelAnime.curFrame - 28.0f) * 0.08f) * 5500.0f; + + sp7C.x -= xMod; + sp94.x -= xMod; + sp88.x -= xMod; + + Matrix_MultVec3f(&sp94, &this->colliderAT.dim.quad[b]); + Matrix_MultVec3f(&sp88, &this->colliderAT.dim.quad[a]); + Matrix_MultVec3f(&sp7C, &this->colliderAT.dim.quad[d]); + Matrix_MultVec3f(&sp70, &this->colliderAT.dim.quad[c]); + + Collider_SetQuadVertices(&this->colliderAT, &this->colliderAT.dim.quad[a], &this->colliderAT.dim.quad[b], + &this->colliderAT.dim.quad[c], &this->colliderAT.dim.quad[d]); +} + +void EnDodongo_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnDodongo* this = (EnDodongo*)thisx; + + EnDodongo_CollisionCheck(this, globalCtx); + if (this->actor.colChkInfo.damageEffect != 0xE) { + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 60.0f, 70.0f, 0x1D); + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + if (this->actionState != DODONGO_DEATH) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderHard.base); + } + if (this->actionState > DODONGO_DEATH) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); + } + if ((this->actionState >= DODONGO_IDLE) && EnDodongo_AteBomb(this, globalCtx)) { + EnDodongo_SetupSwallowBomb(this); + } + if (this->actionState == DODONGO_BREATHE_FIRE) { + if ((29.0f < this->skelAnime.curFrame) && (this->skelAnime.curFrame < 43.0f)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderAT.base); + } + } + this->actor.focus.pos.x = this->actor.world.pos.x + Math_SinS(this->actor.shape.rot.y) * -30.0f; + this->actor.focus.pos.y = this->actor.world.pos.y + 20.0f; + this->actor.focus.pos.z = this->actor.world.pos.z + Math_CosS(this->actor.shape.rot.y) * -30.0f; +} + +s32 EnDodongo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnDodongo* this = (EnDodongo*)thisx; + + if ((limbIndex == 15) || (limbIndex == 16)) { + Matrix_Scale(this->bodyScale.x, this->bodyScale.y, this->bodyScale.z, MTXMODE_APPLY); + } + return false; +} + +void EnDodongo_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f legOffsets[3] = { + { 1100.0f, -700.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 2190.0f, 0.0f, 0.0f }, + }; + Vec3f tailTipOffset = { 3000.0f, 0.0f, 0.0f }; + Vec3f baseOffset = { 0.0f, 0.0f, 0.0f }; + s32 i; + Vec3f hardTris0Vtx[3]; + Vec3f hardTris1Vtx[3]; + Vec3f hardTris2Vtx[3]; + Vec3f tailTip; + Vec3f tailBase; + EnDodongo* this = (EnDodongo*)thisx; + Vec3f hardTris0VtxOffset[] = { + { -300.0f, -2500.0f, 0.0f }, + { -300.0f, 1200.0f, -2700.0f }, + { 3000.0f, 1200.0f, 0.0f }, + }; + Vec3f hardTris1VtxOffset[] = { + { -300.0f, -2500.0f, 0.0f }, + { -300.0f, 1200.0f, 2700.0f }, + { 3000.0f, 1200.0f, 0.0f }, + }; + Vec3f hardTris2VtxOffset[] = { + { -600.0f, 1200.0f, -2800.0f }, + { -600.0f, 1200.0f, 2800.0f }, + { 3000.0f, 1200.0f, 0.0f }, + }; + Vec3f mouthOffset = { 1800.0f, 1200.0f, 0.0f }; + Vec3f headOffset = { 1500.0f, 300.0f, 0.0f }; + + Collider_UpdateSpheres(limbIndex, &this->colliderBody); + + switch (limbIndex) { + case 2: + if ((this->actionState == DODONGO_BREATHE_FIRE) && (29.0f < this->skelAnime.curFrame) && + (this->skelAnime.curFrame < 43.0f)) { + EnDodongo_UpdateQuad(this, globalCtx); + } + break; + case 7: + for (i = 0; i < 3; i++) { + Matrix_MultVec3f(&hardTris0VtxOffset[i], &hardTris0Vtx[i]); + Matrix_MultVec3f(&hardTris1VtxOffset[i], &hardTris1Vtx[i]); + Matrix_MultVec3f(&hardTris2VtxOffset[i], &hardTris2Vtx[i]); + } + Collider_SetTrisVertices(&this->colliderHard, 0, &hardTris0Vtx[0], &hardTris0Vtx[1], &hardTris0Vtx[2]); + Collider_SetTrisVertices(&this->colliderHard, 1, &hardTris1Vtx[0], &hardTris1Vtx[1], &hardTris1Vtx[2]); + Collider_SetTrisVertices(&this->colliderHard, 2, &hardTris2Vtx[0], &hardTris2Vtx[1], &hardTris2Vtx[2]); + Matrix_MultVec3f(&mouthOffset, &this->mouthPos); + Matrix_MultVec3f(&headOffset, &this->headPos); + break; + case 15: + if ((this->actionState == DODONGO_SWEEP_TAIL) && (this->timer >= 2)) { + Matrix_MultVec3f(&tailTipOffset, &tailTip); + Matrix_MultVec3f(&baseOffset, &tailBase); + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIdx), &tailTip, &tailBase); + } else if ((this->actionState == DODONGO_SWEEP_TAIL) && (this->timer != 0)) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIdx)); + } + break; + case 21: + Matrix_MultVec3f(&legOffsets[1], &this->leftFootPos); + break; + case 28: + Matrix_MultVec3f(&legOffsets[1], &this->rightFootPos); + break; + } + if (this->iceTimer != 0) { + i = -1; + switch (limbIndex) { + case 7: + baseOffset.x = 1200.0f; + i = 0; + break; + case 13: + i = 1; + break; + case 14: + i = 2; + break; + case 15: + i = 3; + break; + case 16: + i = 4; + break; + case 22: + i = 5; + break; + case 29: + i = 6; + break; + case 21: + i = 7; + break; + case 28: + i = 8; + break; + } + if (i >= 0) { + Matrix_MultVec3f(&baseOffset, &this->icePos[i]); + } + } +} + +void EnDodongo_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnDodongo* this = (EnDodongo*)thisx; + s32 index; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnDodongo_OverrideLimbDraw, + EnDodongo_PostLimbDraw, this); + + if (this->iceTimer != 0) { + this->actor.colorFilterTimer++; + if (1) {} + this->iceTimer--; + if ((this->iceTimer % 4) == 0) { + index = this->iceTimer >> 2; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &this->icePos[index], 150, 150, 150, 250, 235, 245, + 255, 1.8f); + } + } +} + +void EnDodongo_ShiftVecRadial(s16 yaw, f32 radius, Vec3f* vec) { + vec->x += Math_SinS(yaw) * radius; + vec->z += Math_CosS(yaw) * radius; +} + +s32 EnDodongo_AteBomb(EnDodongo* this, GlobalContext* globalCtx) { + Actor* actor = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + f32 dx; + f32 dy; + f32 dz; + + while (actor != NULL) { + if ((actor->params != 0) || (actor->parent != NULL)) { + actor = actor->next; + continue; + } + dx = actor->world.pos.x - this->mouthPos.x; + dy = actor->world.pos.y - this->mouthPos.y; + dz = actor->world.pos.z - this->mouthPos.z; + if ((fabsf(dx) < 20.0f) && (fabsf(dy) < 10.0f) && (fabsf(dz) < 20.0f)) { + if (actor->id == ACTOR_EN_BOM) { + this->actor.child = actor; + } else { + this->actor.parent = actor; + } + actor->parent = &this->actor; + return true; + } + actor = actor->next; + } + return false; +} diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.h b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.h new file mode 100644 index 000000000..31fb24501 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.h @@ -0,0 +1,47 @@ +#ifndef Z_EN_DODONGO_H +#define Z_EN_DODONGO_H + +#include "ultra64.h" +#include "global.h" + +struct EnDodongo; + +typedef void (*EnDodongoActionFunc)(struct EnDodongo*, GlobalContext*); + +typedef struct EnDodongo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[31]; + /* 0x024A */ Vec3s morphTable[31]; + /* 0x0304 */ s32 actionState; + /* 0x0308 */ EnDodongoActionFunc actionFunc; + /* 0x030C */ s16 timer; + /* 0x030E */ s16 retreatTimer; + /* 0x0310 */ s16 tailSwipeSpeed; + /* 0x0312 */ s16 iceTimer; + /* 0x0314 */ char unk_314[2]; + /* 0x0316 */ s16 rightFootStep; + /* 0x0318 */ char unk_318[4]; + /* 0x031C */ Vec3f leftFootPos; + /* 0x0328 */ Vec3f rightFootPos; + /* 0x0334 */ Vec3f mouthPos; + /* 0x0334 */ Vec3f headPos; + /* 0x034C */ Vec3f bodyScale; + /* 0x0358 */ Vec3f icePos[9]; + /* 0x03C4 */ Color_RGBA8 bombSmokePrimColor; + /* 0x03C8 */ Color_RGBA8 bombSmokeEnvColor; + /* 0x03CC */ u8 damageEffect; + /* 0x03D0 */ s32 blureIdx; + /* 0x03D4 */ ColliderQuad colliderAT; + /* 0x0454 */ ColliderTris colliderHard; + /* 0x0474 */ ColliderTrisElement trisElements[3]; + /* 0x0588 */ ColliderJntSph colliderBody; + /* 0x05A8 */ ColliderJntSphElement sphElements[6]; +} EnDodongo; // size = 0x0728 + +typedef enum { + EN_DODONGO_NORMAL = -1, + EN_DODONGO_SMOKE_DEATH +} EnDodongoParam; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c b/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c new file mode 100644 index 000000000..eb23224a4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.c @@ -0,0 +1,488 @@ +/* + * File: z_en_dog.c + * Overlay: ovl_En_Dog + * Description: Dog + */ + +#include "z_en_dog.h" +#include "objects/object_dog/object_dog.h" + +#define FLAGS 0 + +void EnDog_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDog_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDog_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDog_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDog_FollowPath(EnDog* this, GlobalContext* globalCtx); +void EnDog_ChooseMovement(EnDog* this, GlobalContext* globalCtx); +void EnDog_FollowPlayer(EnDog* this, GlobalContext* globalCtx); +void EnDog_RunAway(EnDog* this, GlobalContext* globalCtx); +void EnDog_FaceLink(EnDog* this, GlobalContext* globalCtx); +void EnDog_Wait(EnDog* this, GlobalContext* globalCtx); + +const ActorInit En_Dog_InitVars = { + ACTOR_EN_DOG, + ACTORCAT_NPC, + FLAGS, + OBJECT_DOG, + sizeof(EnDog), + (ActorFunc)EnDog_Init, + (ActorFunc)EnDog_Destroy, + (ActorFunc)EnDog_Update, + (ActorFunc)EnDog_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 16, 20, 0, { 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, 50 }; + +typedef enum { + /* 0 */ ENDOG_ANIM_0, + /* 1 */ ENDOG_ANIM_1, + /* 2 */ ENDOG_ANIM_2, + /* 3 */ ENDOG_ANIM_3, + /* 4 */ ENDOG_ANIM_4, + /* 5 */ ENDOG_ANIM_5, + /* 6 */ ENDOG_ANIM_6, + /* 7 */ ENDOG_ANIM_7 +} EnDogAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gDogWalkAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gDogWalkAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -6.0f }, + { &gDogRunAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -6.0f }, + { &gDogBarkAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -6.0f }, + { &gDogSitAnim, 1.0f, 0.0f, 4.0f, ANIMMODE_ONCE, -6.0f }, + { &gDogSitAnim, 1.0f, 5.0f, 25.0f, ANIMMODE_LOOP_PARTIAL, -6.0f }, + { &gDogBowAnim, 1.0f, 0.0f, 6.0f, ANIMMODE_ONCE, -6.0f }, + { &gDogBow2Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -6.0f }, +}; + +typedef enum { + /* 0x00 */ DOG_WALK, + /* 0x01 */ DOG_RUN, + /* 0x02 */ DOG_BARK, + /* 0x03 */ DOG_SIT, + /* 0x04 */ DOG_SIT_2, + /* 0x05 */ DOG_BOW, + /* 0x06 */ DOG_BOW_2 +} DogBehavior; + +void EnDog_PlayWalkSFX(EnDog* this) { + AnimationHeader* walk = &gDogWalkAnim; + + if (this->skelAnime.animation == walk) { + if ((this->skelAnime.curFrame == 1.0f) || (this->skelAnime.curFrame == 7.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHIBI_WALK); + } + } +} + +void EnDog_PlayRunSFX(EnDog* this) { + AnimationHeader* run = &gDogRunAnim; + + if (this->skelAnime.animation == run) { + if ((this->skelAnime.curFrame == 2.0f) || (this->skelAnime.curFrame == 4.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHIBI_WALK); + } + } +} + +void EnDog_PlayBarkSFX(EnDog* this) { + AnimationHeader* bark = &gDogBarkAnim; + + if (this->skelAnime.animation == bark) { + if ((this->skelAnime.curFrame == 13.0f) || (this->skelAnime.curFrame == 19.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SMALL_DOG_BARK); + } + } +} + +s32 EnDog_PlayAnimAndSFX(EnDog* this) { + s32 animation; + + if (this->behavior != this->nextBehavior) { + if (this->nextBehavior == DOG_SIT_2) { + this->nextBehavior = DOG_SIT; + } + if (this->nextBehavior == DOG_BOW_2) { + this->nextBehavior = DOG_BOW; + } + + this->behavior = this->nextBehavior; + switch (this->behavior) { + case DOG_WALK: + animation = ENDOG_ANIM_1; + break; + case DOG_RUN: + animation = ENDOG_ANIM_2; + break; + case DOG_BARK: + animation = ENDOG_ANIM_3; + break; + case DOG_SIT: + animation = ENDOG_ANIM_4; + break; + case DOG_BOW: + animation = ENDOG_ANIM_6; + break; + } + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animation); + } + + switch (this->behavior) { + case DOG_SIT: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDOG_ANIM_5); + this->behavior = this->nextBehavior = DOG_SIT_2; + } + break; + case DOG_BOW: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDOG_ANIM_7); + this->behavior = this->nextBehavior = DOG_BOW_2; + } + break; + case DOG_WALK: + EnDog_PlayWalkSFX(this); + break; + case DOG_RUN: + EnDog_PlayRunSFX(this); + break; + case DOG_BARK: + EnDog_PlayBarkSFX(this); + break; + } + return 0; +} + +s8 EnDog_CanFollow(EnDog* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + return 2; + } + + if (globalCtx->sceneNum == SCENE_MARKET_DAY) { + return 0; + } + + if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { + this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; + if (gSaveContext.dogParams != 0) { + return 0; + } + gSaveContext.dogParams = (this->actor.params & 0x7FFF); + return 1; + } + + return 0; +} + +s32 EnDog_UpdateWaypoint(EnDog* this, GlobalContext* globalCtx) { + s32 change; + + if (this->path == NULL) { + return 0; + } + + if (this->reverse) { + change = -1; + } else { + change = 1; + } + + this->waypoint += change; + + if (this->reverse) { + if (this->waypoint < 0) { + this->waypoint = this->path->count - 1; + } + } else { + if ((this->path->count - 1) < this->waypoint) { + this->waypoint = 0; + } + } + + return 1; +} + +s32 EnDog_Orient(EnDog* this, GlobalContext* globalCtx) { + s16 targetYaw; + f32 waypointDistSq; + + waypointDistSq = Path_OrientAndGetDistSq(&this->actor, this->path, this->waypoint, &targetYaw); + Math_SmoothStepToS(&this->actor.world.rot.y, targetYaw, 10, 1000, 1); + + if ((waypointDistSq > 0.0f) && (waypointDistSq < 1000.0f)) { + return EnDog_UpdateWaypoint(this, globalCtx); + } else { + return 0; + } +} + +void EnDog_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDog* this = (EnDog*)thisx; + s16 followingDog; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 24.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDogSkel, NULL, this->jointTable, this->morphTable, 13); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDOG_ANIM_0); + + if ((this->actor.params & 0x8000) == 0) { + this->actor.params = (this->actor.params & 0xF0FF) | ((((this->actor.params & 0x0F00) >> 8) + 1) << 8); + } + + followingDog = ((gSaveContext.dogParams & 0x0F00) >> 8); + if (followingDog == ((this->actor.params & 0x0F00) >> 8) && ((this->actor.params & 0x8000) == 0)) { + Actor_Kill(&this->actor); + return; + } + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, 0, &sColChkInfoInit); + Actor_SetScale(&this->actor, 0.0075f); + this->waypoint = 0; + this->actor.gravity = -1.0f; + this->path = Path_GetByIndex(globalCtx, (this->actor.params & 0x00F0) >> 4, 0xF); + + switch (globalCtx->sceneNum) { + case SCENE_MARKET_NIGHT: + if ((!gSaveContext.dogIsLost) && (((this->actor.params & 0x0F00) >> 8) == 1)) { + Actor_Kill(&this->actor); + } + break; + case SCENE_IMPA: // Richard's Home + if (!(this->actor.params & 0x8000)) { + if (!gSaveContext.dogIsLost) { + this->nextBehavior = DOG_SIT; + this->actionFunc = EnDog_Wait; + this->actor.speedXZ = 0.0f; + return; + } else { + Actor_Kill(&this->actor); + return; + } + } + break; + } + + if (this->actor.params & 0x8000) { + this->nextBehavior = DOG_WALK; + this->actionFunc = EnDog_FollowPlayer; + } else { + this->nextBehavior = DOG_SIT; + this->actionFunc = EnDog_ChooseMovement; + } +} + +void EnDog_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDog* this = (EnDog*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnDog_FollowPath(EnDog* this, GlobalContext* globalCtx) { + s32 behaviors[] = { DOG_SIT, DOG_BOW, DOG_BARK }; + s32 unused[] = { 40, 80, 20 }; + f32 speed; + s32 frame; + + if (EnDog_CanFollow(this, globalCtx) == 1) { + this->actionFunc = EnDog_FollowPlayer; + } + + if (DECR(this->behaviorTimer) != 0) { + if (this->nextBehavior == DOG_WALK) { + speed = 1.0f; + } else { + speed = 4.0f; + } + Math_SmoothStepToF(&this->actor.speedXZ, speed, 0.4f, 1.0f, 0.0f); + EnDog_Orient(this, globalCtx); + this->actor.shape.rot = this->actor.world.rot; + + // Used to change between two text boxes for Richard's owner in the Market Day scene + // depending on where he is on his path. En_Hy checks these event flags. + if (this->waypoint < 9) { + // Richard is close to her, text says something about his coat + gSaveContext.eventInf[3] |= 1; + } else { + // Richard is far, text says something about running fast + gSaveContext.eventInf[3] &= ~1; + } + } else { + frame = globalCtx->state.frames % 3; + this->nextBehavior = behaviors[frame]; + // no clue why they're using the behavior id to calculate timer. possibly meant to use the unused array? + this->behaviorTimer = Rand_S16Offset(60, behaviors[frame]); + this->actionFunc = EnDog_ChooseMovement; + } +} + +void EnDog_ChooseMovement(EnDog* this, GlobalContext* globalCtx) { + if (EnDog_CanFollow(this, globalCtx) == 1) { + this->actionFunc = EnDog_FollowPlayer; + } + + if (DECR(this->behaviorTimer) == 0) { + this->behaviorTimer = Rand_S16Offset(200, 100); + if (globalCtx->state.frames % 2) { + this->nextBehavior = DOG_WALK; + } else { + this->nextBehavior = DOG_RUN; + } + + if (this->nextBehavior == DOG_RUN) { + this->behaviorTimer /= 2; + } + this->actionFunc = EnDog_FollowPath; + } + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.4f, 1.0f, 0.0f); +} + +void EnDog_FollowPlayer(EnDog* this, GlobalContext* globalCtx) { + f32 speed; + + if (gSaveContext.dogParams == 0) { + this->nextBehavior = DOG_SIT; + this->actionFunc = EnDog_Wait; + this->actor.speedXZ = 0.0f; + return; + } + + if (this->actor.xzDistToPlayer > 400.0f) { + if (this->nextBehavior != DOG_SIT && this->nextBehavior != DOG_SIT_2) { + this->nextBehavior = DOG_BOW; + } + gSaveContext.dogParams = 0; + speed = 0.0f; + } else if (this->actor.xzDistToPlayer > 100.0f) { + this->nextBehavior = DOG_RUN; + speed = 4.0f; + } else if (this->actor.xzDistToPlayer < 40.0f) { + if (this->nextBehavior != DOG_BOW && this->nextBehavior != DOG_BOW_2) { + this->nextBehavior = DOG_BOW; + } + speed = 0.0f; + } else { + this->nextBehavior = DOG_WALK; + speed = 1.0f; + } + + Math_ApproachF(&this->actor.speedXZ, speed, 0.6f, 1.0f); + + if (!(this->actor.xzDistToPlayer > 400.0f)) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 10, 1000, 1); + this->actor.shape.rot = this->actor.world.rot; + } +} + +void EnDog_RunAway(EnDog* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer < 200.0f) { + Math_ApproachF(&this->actor.speedXZ, 4.0f, 0.6f, 1.0f); + Math_SmoothStepToS(&this->actor.world.rot.y, (this->actor.yawTowardsPlayer ^ 0x8000), 10, 1000, 1); + } else { + this->actionFunc = EnDog_FaceLink; + } + this->actor.shape.rot = this->actor.world.rot; +} + +void EnDog_FaceLink(EnDog* this, GlobalContext* globalCtx) { + s16 rotTowardLink; + s16 prevRotY; + f32 absAngleDiff; + + // if the dog is more than 200 units away from Link, turn to face him then wait + if (200.0f <= this->actor.xzDistToPlayer) { + this->nextBehavior = DOG_WALK; + + Math_ApproachF(&this->actor.speedXZ, 1.0f, 0.6f, 1.0f); + + rotTowardLink = this->actor.yawTowardsPlayer; + prevRotY = this->actor.world.rot.y; + Math_SmoothStepToS(&this->actor.world.rot.y, rotTowardLink, 10, 1000, 1); + + absAngleDiff = this->actor.world.rot.y; + absAngleDiff -= prevRotY; + absAngleDiff = fabsf(absAngleDiff); + if (absAngleDiff < 200.0f) { + this->nextBehavior = DOG_SIT; + this->actionFunc = EnDog_Wait; + this->actor.speedXZ = 0.0f; + } + } else { + this->nextBehavior = DOG_RUN; + this->actionFunc = EnDog_RunAway; + } + this->actor.shape.rot = this->actor.world.rot; +} + +void EnDog_Wait(EnDog* this, GlobalContext* globalCtx) { + this->unusedAngle = (this->actor.yawTowardsPlayer - this->actor.shape.rot.y); + + // If another dog is following Link and he gets within 200 units of waiting dog, run away + if ((gSaveContext.dogParams != 0) && (this->actor.xzDistToPlayer < 200.0f)) { + this->nextBehavior = DOG_RUN; + this->actionFunc = EnDog_RunAway; + } +} + +void EnDog_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDog* this = (EnDog*)thisx; + s32 pad; + + EnDog_PlayAnimAndSFX(this); + SkelAnime_Update(&this->skelAnime); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, this->collider.dim.radius, this->collider.dim.height * 0.5f, 0.0f, + 5); + Actor_MoveForward(&this->actor); + this->actionFunc(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnDog_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + return false; +} + +void EnDog_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { +} + +void EnDog_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDog* this = (EnDog*)thisx; + Color_RGBA8 colors[] = { { 255, 255, 200, 0 }, { 150, 100, 50, 0 } }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_dog.c", 972); + + func_80093D18(globalCtx->state.gfxCtx); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, colors[this->actor.params & 0xF].r, colors[this->actor.params & 0xF].g, + colors[this->actor.params & 0xF].b, colors[this->actor.params & 0xF].a); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDog_OverrideLimbDraw, EnDog_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_dog.c", 994); +} diff --git a/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.h b/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.h new file mode 100644 index 000000000..afaf027aa --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dog/z_en_dog.h @@ -0,0 +1,29 @@ +#ifndef Z_EN_DOG_H +#define Z_EN_DOG_H + +#include "ultra64.h" +#include "global.h" + +struct EnDog; + +typedef void (*EnDogActionFunc)(struct EnDog*, GlobalContext*); + +typedef struct EnDog { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnDogActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ Path* path; + /* 0x01E4 */ u8 reverse; + /* 0x01E6 */ s16 waypoint; + /* 0x01E8 */ s16 unusedAngle; + /* 0x01EA */ s16 pad1; + /* 0x01EC */ s16 behaviorTimer; + /* 0x01EE */ s16 pad2; + /* 0x01F0 */ s16 nextBehavior; + /* 0x01F2 */ s16 behavior; + /* 0x01F4 */ Vec3s jointTable[13]; + /* 0x0242 */ Vec3s morphTable[13]; +} EnDog; // size = 0x0290 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c new file mode 100644 index 000000000..a3fc60b7c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c @@ -0,0 +1,355 @@ +/* + * File: z_en_door.c + * Overlay: ovl_En_Door + * Description: Doors with handles + */ + +#include "z_en_door.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "objects/object_hidan_objects/object_hidan_objects.h" +#include "objects/object_mizu_objects/object_mizu_objects.h" +#include "objects/object_haka_door/object_haka_door.h" + +#define FLAGS ACTOR_FLAG_4 + +#define DOOR_AJAR_SLAM_RANGE 120.0f +#define DOOR_AJAR_OPEN_RANGE (2 * DOOR_AJAR_SLAM_RANGE) + +#define DOOR_CHECK_RANGE 40.0f + +void EnDoor_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDoor_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDoor_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDoor_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDoor_SetupType(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_Idle(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_WaitForCheck(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_Check(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_AjarWait(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_AjarOpen(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_AjarClose(EnDoor* this, GlobalContext* globalCtx); +void EnDoor_Open(EnDoor* this, GlobalContext* globalCtx); + +const ActorInit En_Door_InitVars = { + ACTOR_EN_DOOR, + ACTORCAT_DOOR, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnDoor), + (ActorFunc)EnDoor_Init, + (ActorFunc)EnDoor_Destroy, + (ActorFunc)EnDoor_Update, + (ActorFunc)EnDoor_Draw, + NULL, +}; + +/** + * Controls which object and display lists to use in a given scene + */ +static EnDoorInfo sDoorInfo[] = { + { SCENE_HIDAN, 1, OBJECT_HIDAN_OBJECTS }, + { SCENE_MIZUSIN, 2, OBJECT_MIZU_OBJECTS }, + { SCENE_HAKADAN, 3, OBJECT_HAKA_DOOR }, + { SCENE_HAKADANCH, 3, OBJECT_HAKA_DOOR }, + { SCENE_BMORI1, 0, OBJECT_GAMEPLAY_KEEP }, // Hacky fix, but behavior same as console. + // KEEP objects should remain last and in this order + { -1, 0, OBJECT_GAMEPLAY_KEEP }, + { -1, 4, OBJECT_GAMEPLAY_FIELD_KEEP }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 0, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_STOP), +}; + +static AnimationHeader* D_809FCECC[] = { &gDoor3Anim, &gDoor1Anim, &gDoor4Anim, &gDoor2Anim }; + +static u8 sDoorAnimOpenFrames[] = { 25, 25, 25, 25 }; + +static u8 sDoorAnimCloseFrames[] = { 60, 70, 60, 70 }; + +static Gfx* D_809FCEE4[5][2] = { + { gDoorLeftDL, gDoorRightDL }, + { gFireTempleDoorWithHandleFrontDL, gFireTempleDoorWithHandleBackDL }, + { gWaterTempleDoorLeftDL, gWaterTempleDoorRightDL }, + { object_haka_door_DL_0013B8, object_haka_door_DL_001420 }, + { gFieldDoor1DL, gFieldDoor2DL }, +}; + +void EnDoor_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnDoor* this = (EnDoor*)thisx; + EnDoorInfo* objectInfo; + s32 i; + s32 objBankIndex; + f32 xOffset; + f32 zOffset; + + objectInfo = &sDoorInfo[0]; + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_Init(globalCtx, &this->skelAnime, &gDoorSkel, &gDoor3Anim, this->jointTable, this->morphTable, 5); + for (i = 0; i < ARRAY_COUNT(sDoorInfo) - 2; i++, objectInfo++) { + if (globalCtx->sceneNum == objectInfo->sceneNum) { + break; + } + } + + // Due to Object_GetIndex always returning 0, doors always use the OBJECT_GAMEPLAY_FIELD_KEEP door. + if (i >= ARRAY_COUNT(sDoorInfo) - 2 && Object_GetIndex(&globalCtx->objectCtx, OBJECT_GAMEPLAY_FIELD_KEEP) >= 0) { + objectInfo++; + } + + this->dListIndex = objectInfo->dListIndex; + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, objectInfo->objectId); + if (objBankIndex < 0) { + Actor_Kill(&this->actor); + return; + } + + this->requiredObjBankIndex = objBankIndex; + this->dListIndex = objectInfo->dListIndex; + if (this->actor.objBankIndex == this->requiredObjBankIndex) { + EnDoor_SetupType(this, globalCtx); + } else { + this->actionFunc = EnDoor_SetupType; + } + + // Double doors + if (this->actor.params & 0x40) { + EnDoor* other; + + xOffset = Math_CosS(this->actor.shape.rot.y) * 30.0f; + zOffset = Math_SinS(this->actor.shape.rot.y) * 30.0f; + other = (EnDoor*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_DOOR, + this->actor.world.pos.x + xOffset, this->actor.world.pos.y, + this->actor.world.pos.z - zOffset, 0, this->actor.shape.rot.y + 0x8000, 0, + this->actor.params & ~0x40); + if (other != NULL) { + other->unk_192 = 1; + } + this->actor.world.pos.x -= xOffset; + this->actor.world.pos.z += zOffset; + } + Actor_SetFocus(&this->actor, 70.0f); +} + +void EnDoor_Destroy(Actor* thisx, GlobalContext* globalCtx) { + TransitionActorEntry* transitionEntry; + EnDoor* this = (EnDoor*)thisx; + + transitionEntry = &globalCtx->transiActorCtx.list[(u16)this->actor.params >> 0xA]; + if (transitionEntry->id < 0) { + transitionEntry->id = -transitionEntry->id; + } +} + +void EnDoor_SetupType(EnDoor* this, GlobalContext* globalCtx) { + s32 doorType; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->requiredObjBankIndex)) { + doorType = this->actor.params >> 7 & 7; + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.objBankIndex = this->requiredObjBankIndex; + this->actionFunc = EnDoor_Idle; + if (doorType == DOOR_EVENING) { + doorType = + (gSaveContext.dayTime > 0xC000 && gSaveContext.dayTime < 0xE000) ? DOOR_SCENEEXIT : DOOR_CHECKABLE; + } + this->actor.world.rot.y = 0x0000; + if (doorType == DOOR_LOCKED) { + if (!Flags_GetSwitch(globalCtx, this->actor.params & 0x3F)) { + this->lockTimer = 10; + } + } else if (doorType == DOOR_AJAR) { + if (Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor) > DOOR_AJAR_SLAM_RANGE) { + this->actionFunc = EnDoor_AjarWait; + this->actor.world.rot.y = -0x1800; + } + } else if (doorType == DOOR_CHECKABLE) { + this->actor.textId = (this->actor.params & 0x3F) + 0x0200; + if (this->actor.textId == 0x0229 && !(gSaveContext.eventChkInf[1] & 0x10)) { + // Talon's house door. If Talon has not been woken up at Hyrule Castle + // this door should be openable at any time of day. Note that there is no + // check for time of day as the scene setup for Lon Lon merely initializes + // the door with a different text id between day and night setups + doorType = DOOR_SCENEEXIT; + } else { + this->actionFunc = EnDoor_WaitForCheck; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_27; + } + } + // Replace the door type it was loaded with by the new type + this->actor.params = (this->actor.params & ~0x380) | (doorType << 7); + } +} + +void EnDoor_Idle(EnDoor* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 doorType; + Vec3f playerPosRelToDoor; + s16 phi_v0; + + doorType = this->actor.params >> 7 & 7; + func_8002DBD0(&this->actor, &playerPosRelToDoor, &player->actor.world.pos); + if (this->playerIsOpening != 0) { + this->actionFunc = EnDoor_Open; + Animation_PlayOnceSetSpeed(&this->skelAnime, D_809FCECC[this->animStyle], + (player->stateFlags1 & 0x8000000) ? 0.75f : 1.5f); + if (this->lockTimer != 0) { + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex]--; + Flags_SetSwitch(globalCtx, this->actor.params & 0x3F); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHAIN_KEY_UNLOCK); + } + } else if (!Player_InCsMode(globalCtx)) { + if (fabsf(playerPosRelToDoor.y) < 20.0f && fabsf(playerPosRelToDoor.x) < 20.0f && + fabsf(playerPosRelToDoor.z) < 50.0f) { + phi_v0 = player->actor.shape.rot.y - this->actor.shape.rot.y; + if (playerPosRelToDoor.z > 0.0f) { + phi_v0 = 0x8000 - phi_v0; + } + if (ABS(phi_v0) < 0x3000) { + if (this->lockTimer != 0) { + if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] <= 0) { + Player* player2 = GET_PLAYER(globalCtx); + + player2->naviTextId = -0x203; + return; + } else { + player->doorTimer = 10; + } + } + player->doorType = (doorType == DOOR_AJAR) ? PLAYER_DOORTYPE_AJAR : PLAYER_DOORTYPE_HANDLE; + player->doorDirection = (playerPosRelToDoor.z >= 0.0f) ? 1.0f : -1.0f; + player->doorActor = &this->actor; + } + } else if (doorType == DOOR_AJAR && this->actor.xzDistToPlayer > DOOR_AJAR_OPEN_RANGE) { + this->actionFunc = EnDoor_AjarOpen; + } + } +} + +void EnDoor_WaitForCheck(EnDoor* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnDoor_Check; + } else { + func_8002F2CC(&this->actor, globalCtx, DOOR_CHECK_RANGE); + } +} + +void EnDoor_Check(EnDoor* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnDoor_WaitForCheck; + } +} + +void EnDoor_AjarWait(EnDoor* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer < DOOR_AJAR_SLAM_RANGE) { + this->actionFunc = EnDoor_AjarClose; + } +} + +void EnDoor_AjarOpen(EnDoor* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer < DOOR_AJAR_SLAM_RANGE) { + this->actionFunc = EnDoor_AjarClose; + } else if (Math_ScaledStepToS(&this->actor.world.rot.y, -0x1800, 0x100)) { + this->actionFunc = EnDoor_AjarWait; + } +} + +void EnDoor_AjarClose(EnDoor* this, GlobalContext* globalCtx) { + if (Math_ScaledStepToS(&this->actor.world.rot.y, 0, 0x700)) { + this->actionFunc = EnDoor_Idle; + } +} + +void EnDoor_Open(EnDoor* this, GlobalContext* globalCtx) { + s32 i; + s32 numEffects; + + if (DECR(this->lockTimer) == 0) { + if (SkelAnime_Update(&this->skelAnime)) { + this->actionFunc = EnDoor_Idle; + this->playerIsOpening = 0; + } else if (Animation_OnFrame(&this->skelAnime, sDoorAnimOpenFrames[this->animStyle])) { + Audio_PlayActorSound2(&this->actor, + (globalCtx->sceneNum == SCENE_HAKADAN || globalCtx->sceneNum == SCENE_HAKADANCH || + globalCtx->sceneNum == SCENE_HIDAN) + ? NA_SE_EV_IRON_DOOR_OPEN + : NA_SE_OC_DOOR_OPEN); + if (this->skelAnime.playSpeed < 1.5f) { + numEffects = (s32)(Rand_ZeroOne() * 30.0f) + 50; + for (i = 0; i < numEffects; i++) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 60.0f, 100.0f, 50.0f, 0.15f); + } + } + } else if (Animation_OnFrame(&this->skelAnime, sDoorAnimCloseFrames[this->animStyle])) { + Audio_PlayActorSound2(&this->actor, + (globalCtx->sceneNum == SCENE_HAKADAN || globalCtx->sceneNum == SCENE_HAKADANCH || + globalCtx->sceneNum == SCENE_HIDAN) + ? NA_SE_EV_IRON_DOOR_CLOSE + : NA_SE_EV_DOOR_CLOSE); + } + } +} + +void EnDoor_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDoor* this = (EnDoor*)thisx; + + this->actionFunc(this, globalCtx); +} + +s32 EnDoor_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + s32 pad; + TransitionActorEntry* transitionEntry; + Gfx** temp_a2; + s32 pad2; + s16 phi_v0_2; + s32 phi_v0; + EnDoor* this = (EnDoor*)thisx; + + if (limbIndex == 4) { + temp_a2 = D_809FCEE4[this->dListIndex]; + transitionEntry = &globalCtx->transiActorCtx.list[(u16)this->actor.params >> 0xA]; + rot->z += this->actor.world.rot.y; + if ((globalCtx->roomCtx.prevRoom.num >= 0) || + (transitionEntry->sides[0].room == transitionEntry->sides[1].room)) { + phi_v0_2 = ((this->actor.shape.rot.y + this->skelAnime.jointTable[3].z) + rot->z) - + Math_Vec3f_Yaw(&globalCtx->view.eye, &this->actor.world.pos); + *dList = (ABS(phi_v0_2) < 0x4000) ? temp_a2[0] : temp_a2[1]; + } else { + phi_v0 = this->unk_192; + if (transitionEntry->sides[0].room != this->actor.room) { + phi_v0 ^= 1; + } + *dList = temp_a2[phi_v0]; + } + } + return false; +} + +void EnDoor_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDoor* this = (EnDoor*)thisx; + + if (this->actor.objBankIndex == this->requiredObjBankIndex) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_door.c", 910); + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnDoor_OverrideLimbDraw, + NULL, &this->actor); + if (this->actor.world.rot.y != 0) { + if (1) {} + if (this->actor.world.rot.y > 0) { + gSPDisplayList(POLY_OPA_DISP++, gDoorRightDL); + } else { + gSPDisplayList(POLY_OPA_DISP++, gDoorLeftDL); + } + } + if (this->lockTimer != 0) { + Actor_DrawDoorLock(globalCtx, this->lockTimer, DOORLOCK_NORMAL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_door.c", 941); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Door/z_en_door.h b/soh/src/overlays/actors/ovl_En_Door/z_en_door.h new file mode 100644 index 000000000..94909cb36 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Door/z_en_door.h @@ -0,0 +1,60 @@ +#ifndef Z_EN_DOOR_H +#define Z_EN_DOOR_H + +#include "ultra64.h" +#include "global.h" + +/** + * Actor Parameters + * + * | | | | + * | Transition Index | Type | Double Door | Switch Flag OR Text Id - 0x0200 + * |------------------|-------|-------------|--------------------------------- + * | 0 0 0 0 0 0 | 0 0 0 | 0 | 0 0 0 0 0 0 + * | 6 | 3 | 1 | 6 + * | + * + * Transition Index 1111110000000000 Set by the actor engine when the door is spawned + * Type 0000001110000000 + * Double Door 0000000001000000 + * Switch Flag 0000000000111111 For use with the `DOOR_LOCKED` type + * Text id - 0x0200 0000000000111111 For use with the `DOOR_CHECKABLE` type + * + */ + +typedef struct { + /* 0x00 */ s16 sceneNum; + /* 0x02 */ u8 dListIndex; + /* 0x04 */ s16 objectId; +} EnDoorInfo; + +typedef enum { + /* 0x00 */ DOOR_ROOMLOAD, // loads rooms + /* 0x01 */ DOOR_LOCKED, // small key locked door + /* 0x02 */ DOOR_ROOMLOAD2, // loads rooms + /* 0x03 */ DOOR_SCENEEXIT, // doesn't load rooms, used for doors paired with scene transition polygons + /* 0x04 */ DOOR_AJAR, // open slightly but slams shut if Link gets too close + /* 0x05 */ DOOR_CHECKABLE, // doors that display a textbox when interacting + /* 0x06 */ DOOR_EVENING, // unlocked between 18:00 and 21:00, Dampé's hut + /* 0x07 */ DOOR_ROOMLOAD7 // loads rooms +} EnDoorType; + +struct EnDoor; + +typedef void (*EnDoorActionFunc)(struct EnDoor*, GlobalContext*); + +typedef struct EnDoor { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ u8 animStyle; // Must be at same offset as animStyle in DoorKiller due to the cast in func_80839800 + /* 0x0191 */ u8 playerIsOpening; // Must be at same offset as playerIsOpening in DoorKiller due to the cast in func_80839800 + /* 0x0192 */ u8 unk_192; + /* 0x0193 */ s8 requiredObjBankIndex; + /* 0x0194 */ s8 dListIndex; + /* 0x0196 */ s16 lockTimer; + /* 0x0198 */ Vec3s jointTable[5]; + /* 0x01B6 */ Vec3s morphTable[5]; + /* 0x01D4 */ EnDoorActionFunc actionFunc; +} EnDoor; // size = 0x01D8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c new file mode 100644 index 000000000..14e193f36 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.c @@ -0,0 +1,280 @@ +/* + * File: z_en_ds.c + * Overlay: ovl_En_Ds + * Description: Potion Shop Granny + */ + +#include "z_en_ds.h" +#include "objects/object_ds/object_ds.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnDs_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDs_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDs_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDs_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDs_Wait(EnDs* this, GlobalContext* globalCtx); + +const ActorInit En_Ds_InitVars = { + ACTOR_EN_DS, + ACTORCAT_NPC, + FLAGS, + OBJECT_DS, + sizeof(EnDs), + (ActorFunc)EnDs_Init, + (ActorFunc)EnDs_Destroy, + (ActorFunc)EnDs_Update, + (ActorFunc)EnDs_Draw, + NULL, +}; + +void EnDs_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDs* this = (EnDs*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gPotionShopLadySkel, &gPotionShopLadyAnim, this->jointTable, + this->morphTable, 6); + Animation_PlayOnce(&this->skelAnime, &gPotionShopLadyAnim); + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + + Actor_SetScale(&this->actor, 0.013f); + + this->actionFunc = EnDs_Wait; + this->actor.targetMode = 1; + this->unk_1E8 = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_1E4 = 0.0f; +} + +void EnDs_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnDs_Talk(EnDs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnDs_Wait; + this->actor.flags &= ~ACTOR_FLAG_16; + } + this->unk_1E8 |= 1; +} + +void EnDs_TalkNoEmptyBottle(EnDs* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = EnDs_Wait; + } + this->unk_1E8 |= 1; +} + +void EnDs_TalkAfterGiveOddPotion(EnDs* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnDs_Talk; + } else { + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2CC(&this->actor, globalCtx, 1000.0f); + } +} + +void EnDs_DisplayOddPotionText(EnDs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actor.textId = 0x504F; + this->actionFunc = EnDs_TalkAfterGiveOddPotion; + this->actor.flags &= ~ACTOR_FLAG_8; + gSaveContext.itemGetInf[3] |= 1; + } +} + +void EnDs_GiveOddPotion(EnDs* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnDs_DisplayOddPotionText; + gSaveContext.timer2State = 0; + } else { + func_8002F434(&this->actor, globalCtx, GI_ODD_POTION, 10000.0f, 50.0f); + } +} + +void EnDs_TalkAfterBrewOddPotion(EnDs* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = EnDs_GiveOddPotion; + func_8002F434(&this->actor, globalCtx, GI_ODD_POTION, 10000.0f, 50.0f); + } +} + +void EnDs_BrewOddPotion3(EnDs* this, GlobalContext* globalCtx) { + if (this->brewTimer > 0) { + this->brewTimer -= 1; + } else { + this->actionFunc = EnDs_TalkAfterBrewOddPotion; + Message_ContinueTextbox(globalCtx, 0x504D); + } + + Math_StepToF(&this->unk_1E4, 0, 0.03f); + Environment_AdjustLights(globalCtx, this->unk_1E4 * (2.0f - this->unk_1E4), 0.0f, 0.1f, 1.0f); +} + +void EnDs_BrewOddPotion2(EnDs* this, GlobalContext* globalCtx) { + if (this->brewTimer > 0) { + this->brewTimer -= 1; + } else { + this->actionFunc = EnDs_BrewOddPotion3; + this->brewTimer = 60; + Flags_UnsetSwitch(globalCtx, 0x3F); + } +} + +void EnDs_BrewOddPotion1(EnDs* this, GlobalContext* globalCtx) { + if (this->brewTimer > 0) { + this->brewTimer -= 1; + } else { + this->actionFunc = EnDs_BrewOddPotion2; + this->brewTimer = 20; + } + + Math_StepToF(&this->unk_1E4, 1.0f, 0.01f); + Environment_AdjustLights(globalCtx, this->unk_1E4 * (2.0f - this->unk_1E4), 0.0f, 0.1f, 1.0f); +} + +void EnDs_OfferOddPotion(EnDs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + this->actionFunc = EnDs_BrewOddPotion1; + this->brewTimer = 60; + Flags_SetSwitch(globalCtx, 0x3F); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + player->exchangeItemId = EXCH_ITEM_NONE; + break; + case 1: // no + Message_ContinueTextbox(globalCtx, 0x504C); + this->actionFunc = EnDs_Talk; + } + } +} + +s32 EnDs_CheckRupeesAndBottle() { + if (gSaveContext.rupees < 100) { + return 0; + } else if (Inventory_HasEmptyBottle() == 0) { + return 1; + } else { + return 2; + } +} + +void EnDs_GiveBluePotion(EnDs* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnDs_Talk; + } else { + func_8002F434(&this->actor, globalCtx, GI_POTION_BLUE, 10000.0f, 50.0f); + } +} + +void EnDs_OfferBluePotion(EnDs* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + switch (EnDs_CheckRupeesAndBottle()) { + case 0: // have less than 100 rupees + Message_ContinueTextbox(globalCtx, 0x500E); + break; + case 1: // have 100 rupees but no empty bottle + Message_ContinueTextbox(globalCtx, 0x96); + this->actionFunc = EnDs_TalkNoEmptyBottle; + return; + case 2: // have 100 rupees and empty bottle + Rupees_ChangeBy(-100); + this->actor.flags &= ~ACTOR_FLAG_16; + func_8002F434(&this->actor, globalCtx, GI_POTION_BLUE, 10000.0f, 50.0f); + this->actionFunc = EnDs_GiveBluePotion; + return; + } + break; + case 1: // no + Message_ContinueTextbox(globalCtx, 0x500D); + } + this->actionFunc = EnDs_Talk; + } +} + +void EnDs_Wait(EnDs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (func_8002F368(globalCtx) == EXCH_ITEM_ODD_MUSHROOM) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + player->actor.textId = 0x504A; + this->actionFunc = EnDs_OfferOddPotion; + } else if (gSaveContext.itemGetInf[3] & 1) { + player->actor.textId = 0x500C; + this->actionFunc = EnDs_OfferBluePotion; + } else { + if (INV_CONTENT(ITEM_ODD_MUSHROOM) == ITEM_ODD_MUSHROOM) { + player->actor.textId = 0x5049; + } else { + player->actor.textId = 0x5048; + } + this->actionFunc = EnDs_Talk; + } + } else { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + this->actor.textId = 0x5048; + + if ((ABS(yawDiff) < 0x2151) && (this->actor.xzDistToPlayer < 200.0f)) { + func_8002F298(&this->actor, globalCtx, 100.0f, EXCH_ITEM_ODD_MUSHROOM); + this->unk_1E8 |= 1; + } + } +} + +void EnDs_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDs* this = (EnDs*)thisx; + + if (SkelAnime_Update(&this->skelAnime) != 0) { + this->skelAnime.curFrame = 0.0f; + } + + this->actionFunc(this, globalCtx); + + if (this->unk_1E8 & 1) { + func_80038290(globalCtx, &this->actor, &this->unk_1D8, &this->unk_1DE, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->unk_1D8.x, 0, 6, 0x1838, 100); + Math_SmoothStepToS(&this->unk_1D8.y, 0, 6, 0x1838, 100); + Math_SmoothStepToS(&this->unk_1DE.x, 0, 6, 0x1838, 100); + Math_SmoothStepToS(&this->unk_1DE.y, 0, 6, 0x1838, 100); + } +} + +s32 EnDs_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnDs* this = (EnDs*)thisx; + + if (limbIndex == 5) { + rot->x += this->unk_1D8.y; + rot->z += this->unk_1D8.x; + } + return false; +} + +void EnDs_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f sMultVec = { 1100.0f, 500.0f, 0.0f }; + EnDs* this = (EnDs*)thisx; + + if (limbIndex == 5) { + Matrix_MultVec3f(&sMultVec, &this->actor.focus.pos); + } +} + +void EnDs_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnDs* this = (EnDs*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnDs_OverrideLimbDraw, EnDs_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.h b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.h new file mode 100644 index 000000000..035332f48 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ds/z_en_ds.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_DS_H +#define Z_EN_DS_H + +#include "ultra64.h" +#include "global.h" + +struct EnDs; + +typedef void (*EnDsActionFunc)(struct EnDs*, GlobalContext*); + +typedef struct EnDs { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[6]; + /* 0x01B4 */ Vec3s morphTable[6]; + /* 0x01D8 */ Vec3s unk_1D8; + /* 0x01DE */ Vec3s unk_1DE; + /* 0x01E4 */ f32 unk_1E4; + /* 0x01E8 */ u16 unk_1E8; + /* 0x01EA */ u16 brewTimer; + /* 0x01EC */ EnDsActionFunc actionFunc; +} EnDs; // size = 0x01F0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Du/z_en_du.c b/soh/src/overlays/actors/ovl_En_Du/z_en_du.c new file mode 100644 index 000000000..878f4a3ef --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Du/z_en_du.c @@ -0,0 +1,641 @@ +#include "z_en_du.h" +#include "objects/object_du/object_du.h" +#include "scenes/overworld/spot18/spot18_scene.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_25) + +void EnDu_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDu_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809FE3B4(EnDu* this, GlobalContext* globalCtx); +void func_809FE3C0(EnDu* this, GlobalContext* globalCtx); +void func_809FE638(EnDu* this, GlobalContext* globalCtx); +void func_809FE890(EnDu* this, GlobalContext* globalCtx); +void func_809FE4A4(EnDu* this, GlobalContext* globalCtx); +void func_809FE6CC(EnDu* this, GlobalContext* globalCtx); +void func_809FE740(EnDu* this, GlobalContext* globalCtx); +void func_809FE798(EnDu* this, GlobalContext* globalCtx); +void func_809FEC14(EnDu* this, GlobalContext* globalCtx); +void func_809FEC70(EnDu* this, GlobalContext* globalCtx); +void func_809FECE4(EnDu* this, GlobalContext* globalCtx); +void func_809FEB08(EnDu* this, GlobalContext* globalCtx); + +const ActorInit En_Du_InitVars = { + ACTOR_EN_DU, + ACTORCAT_NPC, + FLAGS, + OBJECT_DU, + sizeof(EnDu), + (ActorFunc)EnDu_Init, + (ActorFunc)EnDu_Destroy, + (ActorFunc)EnDu_Update, + (ActorFunc)EnDu_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { + 0, 0, 0, 0, MASS_IMMOVABLE, +}; + +typedef enum { + /* 0 */ ENDU_ANIM_0, + /* 1 */ ENDU_ANIM_1, + /* 2 */ ENDU_ANIM_2, + /* 3 */ ENDU_ANIM_3, + /* 4 */ ENDU_ANIM_4, + /* 5 */ ENDU_ANIM_5, + /* 6 */ ENDU_ANIM_6, + /* 7 */ ENDU_ANIM_7, + /* 8 */ ENDU_ANIM_8, + /* 9 */ ENDU_ANIM_9, + /* 10 */ ENDU_ANIM_10, + /* 11 */ ENDU_ANIM_11, + /* 12 */ ENDU_ANIM_12, + /* 13 */ ENDU_ANIM_13, + /* 14 */ ENDU_ANIM_14 +} EnDuAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gDaruniaIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gDaruniaIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDaruniaItemGiveAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDaruniaItemGiveIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDaruniaHitLinkAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDaruniaHitBreastAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDaruniaStandUpAfterFallingAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gDaruniaDancingLoop1Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -10.0f }, + { &gDaruniaDancingLoop1Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + { &gDaruniaDancingLoop2Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + { &gDaruniaDancingLoop3Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + { &gDaruniaWrongSongAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + { &gDaruniaWrongSongEndAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gDaruniaDancingLoop4Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + { &gDaruniaDancingEndAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -6.0f }, +}; + +void EnDu_SetupAction(EnDu* this, EnDuActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +u16 func_809FDC38(GlobalContext* globalCtx, Actor* actor) { + u16 reaction = Text_GetFaceReaction(globalCtx, 0x21); + + if (reaction != 0) { + return reaction; + } + if (CUR_UPG_VALUE(UPG_STRENGTH) != 0) { + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x301E; + } else { + return 0x301D; + } + } + if (gSaveContext.infTable[0x11] & 8) { + return 0x301B; + } else { + return 0x301A; + } +} + +s16 func_809FDCDC(GlobalContext* globalCtx, Actor* actor) { + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + break; + case TEXT_STATE_CLOSING: + switch (actor->textId) { + case 0x301A: + gSaveContext.infTable[0x11] |= 8; + break; + case 0x301C: + case 0x301F: + return 2; + case 0x3020: + gSaveContext.eventChkInf[0x2] |= 4; + break; + } + return 0; + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + return 3; + } + break; + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + break; + } + return 1; +} + +s32 func_809FDDB4(EnDu* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT18 && LINK_IS_CHILD) { + return 1; + } else if (globalCtx->sceneNum == SCENE_HIDAN && !(gSaveContext.infTable[0x11] & 0x400) && LINK_IS_ADULT) { + return 1; + } + return 0; +} + +void func_809FDE24(EnDu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3 = 0; + + if (this->unk_1F4.unk_00 == 0) { + phi_a3 = 1; + } + if (this->actionFunc == func_809FE890) { + phi_a3 = 1; + } + this->unk_1F4.unk_18 = player->actor.world.pos; + this->unk_1F4.unk_14 = 10.0f; + func_80034A14(&this->actor, &this->unk_1F4, 3, phi_a3); +} + +void func_809FDE9C(EnDu* this) { + if (this->blinkTimer > 0) { + this->blinkTimer--; + } else { + this->blinkTimer = 0; + } + if (this->blinkTimer < 3) { + this->eyeTexIndex = this->blinkTimer; + } + + switch (this->unk_1EC) { + case 0: + if (this->blinkTimer == 0) { + this->blinkTimer = Rand_S16Offset(30, 30); + } + break; + case 1: + if (this->blinkTimer == 0) { + this->eyeTexIndex = 2; + } + break; + case 2: + if (this->blinkTimer == 0) { + this->eyeTexIndex = 2; + } + break; + case 3: + if (this->blinkTimer == 0) { + this->eyeTexIndex = 0; + } + break; + } + + switch (this->unk_1ED) { + case 1: + this->mouthTexIndex = 1; + break; + case 2: + this->mouthTexIndex = 2; + break; + case 3: + this->mouthTexIndex = 3; + break; + default: + this->mouthTexIndex = 0; + break; + } + + if (this->unk_1EE == 1) { + this->noseTexIndex = 1; + } else { + this->noseTexIndex = 0; + } +} + +void func_809FDFC0(CsCmdActorAction* csAction, Vec3f* dst) { + dst->x = csAction->startPos.x; + dst->y = csAction->startPos.y; + dst->z = csAction->startPos.z; +} + +void func_809FE000(CsCmdActorAction* csAction, Vec3f* dst) { + dst->x = csAction->endPos.x; + dst->y = csAction->endPos.y; + dst->z = csAction->endPos.z; +} + +void func_809FE040(EnDu* this) { + s32 animationIndices[] = { + ENDU_ANIM_8, ENDU_ANIM_8, ENDU_ANIM_8, ENDU_ANIM_8, ENDU_ANIM_9, ENDU_ANIM_10, ENDU_ANIM_10, ENDU_ANIM_13, + }; + + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + this->unk_1E6++; + if (this->unk_1E6 >= 8) { + this->unk_1E6 = 0; + } + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]); + } +} + +void func_809FE104(EnDu* this) { + s32 animationIndices[] = { + ENDU_ANIM_8, + ENDU_ANIM_8, + ENDU_ANIM_11, + ENDU_ANIM_12, + }; + + if (this->unk_1E6 < 4) { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + this->unk_1E6++; + if (this->unk_1E6 < 4) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animationIndices[this->unk_1E6]); + } + } + } +} + +void EnDu_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDu* this = (EnDu*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDaruniaSkel, NULL, 0, 0, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0x16), &sColChkInfoInit); + if (func_809FDDB4(this, globalCtx) == 0) { + Actor_Kill(&this->actor); + return; + } + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_0); + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 1; + this->unk_1F4.unk_00 = 0; + + if (gSaveContext.cutsceneIndex >= 0xFFF0) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDarunia01Cs); + gSaveContext.cutsceneTrigger = 1; + EnDu_SetupAction(this, func_809FE890); + } else if (globalCtx->sceneNum == 4) { + EnDu_SetupAction(this, func_809FE638); + } else if (!LINK_IS_ADULT) { + EnDu_SetupAction(this, func_809FE3C0); + } else { + EnDu_SetupAction(this, func_809FE3B4); + } +} + +void EnDu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnDu* this = (EnDu*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_809FE3B4(EnDu* this, GlobalContext* globalCtx) { +} + +void func_809FE3C0(EnDu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags2 & 0x1000000) { + func_8010BD88(globalCtx, OCARINA_ACTION_CHECK_SARIA); + player->stateFlags2 |= 0x2000000; + player->unk_6A8 = &this->actor; + EnDu_SetupAction(this, func_809FE4A4); + return; + } + if (this->unk_1F4.unk_00 == 2) { + func_8002DF54(globalCtx, &this->actor, 7); + this->unk_1F4.unk_00 = 0; + } + if (this->actor.xzDistToPlayer < 116.0f + this->collider.dim.radius) { + player->stateFlags2 |= 0x800000; + } +} + +void func_809FE4A4(EnDu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + EnDu_SetupAction(this, func_809FE3C0); + } else if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_06) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDaruniaWrongCs); + gSaveContext.cutsceneTrigger = 1; + this->unk_1E8 = 1; + EnDu_SetupAction(this, func_809FE890); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGoronCityDaruniaCorrectCs); + gSaveContext.cutsceneTrigger = 1; + this->unk_1E8 = 0; + EnDu_SetupAction(this, func_809FE890); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else { + player->stateFlags2 |= 0x800000; + } +} + +void func_809FE638(EnDu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (!(player->stateFlags1 & 0x20000000)) { + OnePointCutscene_Init(globalCtx, 3330, -99, &this->actor, MAIN_CAM); + player->actor.shape.rot.y = player->actor.world.rot.y = this->actor.world.rot.y + 0x7FFF; + Audio_PlayFanfare(NA_BGM_APPEAR); + EnDu_SetupAction(this, func_809FE6CC); + this->unk_1E2 = 0x32; + } +} + +void func_809FE6CC(EnDu* this, GlobalContext* globalCtx) { + s16 phi_v1; + + if (this->unk_1E2 == 0) { + phi_v1 = 0; + } else { + this->unk_1E2--; + phi_v1 = this->unk_1E2; + } + if (phi_v1 == 0) { + this->actor.textId = 0x3039; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_1F4.unk_00 = 1; + EnDu_SetupAction(this, func_809FE740); + } +} + +void func_809FE740(EnDu* this, GlobalContext* globalCtx) { + if (this->unk_1F4.unk_00 == 0) { + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + this->unk_1E2 = 0x5A; + EnDu_SetupAction(this, func_809FE798); + } +} + +void func_809FE798(EnDu* this, GlobalContext* globalCtx) { + s32 phi_v0; + + if (this->unk_1E2 == 0) { + phi_v0 = 0; + } else { + this->unk_1E2--; + phi_v0 = this->unk_1E2; + } + if (phi_v0 != 0) { + switch (this->unk_1E2) { + case 0x50: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHAIN_KEY_UNLOCK_B); + break; + case 0x3C: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SLIDE_DOOR_OPEN); + break; + case 0xF: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SLIDE_DOOR_CLOSE); + break; + case 5: + Audio_PlayActorSound2(&this->actor, NA_SE_EV_STONE_BOUND); + break; + } + if (this->unk_1E2 >= 0x3D) { + this->actor.world.pos.x -= 10.0f; + } + } else { + Actor_Kill(&this->actor); + gSaveContext.infTable[0x11] |= 0x400; + } +} + +void func_809FE890(EnDu* this, GlobalContext* globalCtx) { + f32 frame; + Vec3f startPos; + Vec3f endPos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + CsCmdActorAction* csAction; + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + func_8002DF54(globalCtx, &this->actor, 1); + EnDu_SetupAction(this, func_809FEB08); + return; + } + csAction = globalCtx->csCtx.npcActions[2]; + + if (csAction != NULL) { + func_809FDFC0(csAction, &startPos); + func_809FE000(csAction, &endPos); + if (this->unk_1EA == 0) { + func_809FDFC0(csAction, &startPos); + this->actor.world.pos = startPos; + } + if (this->unk_1EA != csAction->action) { + if (csAction->action == 1) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_1); + } + if (csAction->action == 7 || csAction->action == 8) { + this->unk_1E6 = 0; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_7); + } + this->unk_1EA = csAction->action; + if (this->unk_1EA == 7) { + this->blinkTimer = 11; + this->unk_1EC = 2; + this->unk_1ED = 2; + this->unk_1EE = 1; + } + if (this->unk_1EA == 8) { + this->blinkTimer = 11; + this->unk_1EC = 3; + this->unk_1ED = 3; + this->unk_1EE = 0; + } + } + if (this->unk_1EA == 7) { + func_809FE040(this); + } + if (this->unk_1EA == 8) { + func_809FE104(this); + } + this->actor.shape.rot.x = csAction->urot.x; + this->actor.shape.rot.y = csAction->urot.y; + this->actor.shape.rot.z = csAction->urot.z; + this->actor.velocity = velocity; + + if (globalCtx->csCtx.frames < csAction->endFrame) { + frame = csAction->endFrame - csAction->startFrame; + + this->actor.velocity.x = (endPos.x - startPos.x) / frame; + this->actor.velocity.y = (endPos.y - startPos.y) / frame; + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + this->actor.velocity.z = (endPos.z - startPos.z) / frame; + } + } +} + +void func_809FEB08(EnDu* this, GlobalContext* globalCtx) { + this->blinkTimer = 11; + this->unk_1EC = 0; + this->unk_1ED = 0; + this->unk_1EE = 0; + + if (this->unk_1E8 == 1) { + func_8002DF54(globalCtx, &this->actor, 7); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_1); + EnDu_SetupAction(this, func_809FE3C0); + return; + } + if (CUR_UPG_VALUE(UPG_STRENGTH) <= 0) { + this->actor.textId = 0x301C; + EnDu_SetupAction(this, func_809FEC14); + } else { + this->actor.textId = 0x301F; + EnDu_SetupAction(this, func_809FE3C0); + } + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_14); + this->unk_1F4.unk_00 = 1; +} + +void func_809FEC14(EnDu* this, GlobalContext* globalCtx) { + if (this->unk_1F4.unk_00 == 2) { + func_8002DF54(globalCtx, &this->actor, 7); + EnDu_SetupAction(this, func_809FEC70); + func_809FEC70(this, globalCtx); + } +} + +void func_809FEC70(EnDu* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + EnDu_SetupAction(this, func_809FECE4); + } else { + f32 xzRange = this->actor.xzDistToPlayer + 1.0f; + func_8002F434(&this->actor, globalCtx, GI_BRACELET, xzRange, fabsf(this->actor.yDistToPlayer) + 1.0f); + } +} + +void func_809FECE4(EnDu* this, GlobalContext* globalCtx) { + if (this->unk_1F4.unk_00 == 3) { + this->unk_1F4.unk_00 = 0; + EnDu_SetupAction(this, func_809FE3C0); + } +} + +void EnDu_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDu* this = (EnDu*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->skelAnime.animation == &gDaruniaDancingEndAnim && + Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENDU_ANIM_1); + } + + SkelAnime_Update(&this->skelAnime); + func_809FDE9C(this); + func_809FDE24(this, globalCtx); + + if (this->actionFunc == func_809FE890) { + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.world.pos.z += this->actor.velocity.z; + } else { + func_8002D7EC(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + if (this->actionFunc != func_809FE4A4) { + func_800343CC(globalCtx, &this->actor, &this->unk_1F4.unk_00, this->collider.dim.radius + 116.0f, func_809FDC38, + func_809FDCDC); + } + this->actionFunc(this, globalCtx); +} + +s32 EnDu_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnDu* this = (EnDu*)thisx; + Vec3s sp1C; + + if (limbIndex == 16) { + Matrix_Translate(2400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + sp1C = this->unk_1F4.unk_08; + Matrix_RotateX(BINANG_TO_RAD(sp1C.y), MTXMODE_APPLY); + Matrix_RotateZ(BINANG_TO_RAD(sp1C.x), MTXMODE_APPLY); + Matrix_Translate(-2400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == 8) { + sp1C = this->unk_1F4.unk_0E; + Matrix_RotateY(BINANG_TO_RAD(sp1C.y), MTXMODE_APPLY); + Matrix_RotateX(BINANG_TO_RAD(sp1C.x), MTXMODE_APPLY); + } + return 0; +} + +void EnDu_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnDu* this = (EnDu*)thisx; + Vec3f D_809FF40C = { 0.0f, -1000.0f, 0.0f }; + + if (limbIndex == 16) { + Matrix_MultVec3f(&D_809FF40C, &this->actor.focus.pos); + } +} + +void EnDu_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gDaruniaEyeOpenTex, + gDaruniaEyeOpeningTex, + gDaruniaEyeShutTex, + gDaruniaEyeClosingTex, + }; + static void* mouthTextures[] = { + gDaruniaMouthSeriousTex, + gDaruniaMouthGrinningTex, + gDaruniaMouthOpenTex, + gDaruniaMouthHappyTex, + }; + static void* noseTextures[] = { + gDaruniaNoseSeriousTex, + gDaruniaNoseHappyTex, + }; + EnDu* this = (EnDu*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_du.c", 1470); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTextures[this->mouthTexIndex])); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(noseTextures[this->noseTexIndex])); + + func_80034BA0(globalCtx, &this->skelAnime, EnDu_OverrideLimbDraw, EnDu_PostLimbDraw, &this->actor, 255); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_du.c", 1487); +} diff --git a/soh/src/overlays/actors/ovl_En_Du/z_en_du.h b/soh/src/overlays/actors/ovl_En_Du/z_en_du.h new file mode 100644 index 000000000..b5045b5ab --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Du/z_en_du.h @@ -0,0 +1,32 @@ +#ifndef Z_EN_DU_H +#define Z_EN_DU_H + +#include "ultra64.h" +#include "global.h" + +struct EnDu; + +typedef void (*EnDuActionFunc)(struct EnDu*, GlobalContext*); + +typedef struct EnDu { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnDuActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ char unk_1E0[0x2]; + /* 0x01E2 */ s16 unk_1E2; + /* 0x01E4 */ char unk_1E4[0x2]; + /* 0x01E6 */ s16 unk_1E6; + /* 0x01E8 */ s16 unk_1E8; + /* 0x01EA */ s16 unk_1EA; + /* 0x01EC */ u8 unk_1EC; + /* 0x01ED */ u8 unk_1ED; + /* 0x01EE */ u8 unk_1EE; + /* 0x01EF */ u8 eyeTexIndex; + /* 0x01F0 */ u8 mouthTexIndex; + /* 0x01F1 */ u8 noseTexIndex; + /* 0x01F2 */ s16 blinkTimer; + /* 0x01F4 */ struct_80034A14_arg1 unk_1F4; +} EnDu; // size = 0x021C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.c b/soh/src/overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.c new file mode 100644 index 000000000..e803656c4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.c @@ -0,0 +1,128 @@ +/* + * File: z_en_dy_extra.c + * Overlay: ovl_En_Dy_Extra + * Description: Spiral Beams (Great Fairy Fountains) + */ + +#include "z_en_dy_extra.h" +#include "objects/object_dy_obj/object_dy_obj.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnDyExtra_Init(Actor* thisx, GlobalContext* globalCtx); +void EnDyExtra_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnDyExtra_Update(Actor* thisx, GlobalContext* globalCtx); +void EnDyExtra_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnDyExtra_WaitForTrigger(EnDyExtra* this, GlobalContext* globalCtx); +void EnDyExtra_FallAndKill(EnDyExtra* this, GlobalContext* globalCtx); + +const ActorInit En_Dy_Extra_InitVars = { + ACTOR_EN_DY_EXTRA, + ACTORCAT_PROP, + FLAGS, + OBJECT_DY_OBJ, + sizeof(EnDyExtra), + (ActorFunc)EnDyExtra_Init, + (ActorFunc)EnDyExtra_Destroy, + (ActorFunc)EnDyExtra_Update, + (ActorFunc)EnDyExtra_Draw, + NULL, +}; + +void EnDyExtra_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnDyExtra_Init(Actor* thisx, GlobalContext* globalCtx) { + EnDyExtra* this = (EnDyExtra*)thisx; + + osSyncPrintf("\n\n"); + // "Big fairy effect" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 大妖精効果 ☆☆☆☆☆ %d\n" VT_RST, this->actor.params); + this->type = this->actor.params; + this->scale.x = 0.025f; + this->scale.y = 0.039f; + this->scale.z = 0.025f; + this->unk_168 = this->actor.world.pos; + this->actor.gravity = -0.2f; + this->unk_158 = 1.0f; + this->timer = 60; + this->actionFunc = EnDyExtra_WaitForTrigger; +} + +void EnDyExtra_WaitForTrigger(EnDyExtra* this, GlobalContext* globalCtx) { + Math_ApproachF(&this->actor.gravity, 0.0f, 0.1f, 0.005f); + if (this->actor.world.pos.y < -55.0f) { + this->actor.velocity.y = 0.0f; + } + if (this->timer == 0 && this->trigger != 0) { + this->timer = 200; + this->actionFunc = EnDyExtra_FallAndKill; + } +} + +void EnDyExtra_FallAndKill(EnDyExtra* this, GlobalContext* globalCtx) { + Math_ApproachF(&this->actor.gravity, 0.0f, 0.1f, 0.005f); + if (this->timer == 0 || this->unk_158 < 0.02f) { + Actor_Kill(&this->actor); + return; + } + Math_ApproachZeroF(&this->unk_158, 0.03f, 0.05f); + if (this->actor.world.pos.y < -55.0f) { + this->actor.velocity.y = 0.0f; + } +} + +void EnDyExtra_Update(Actor* thisx, GlobalContext* globalCtx) { + EnDyExtra* this = (EnDyExtra*)thisx; + + if (this->timer != 0) { + this->timer--; + } + this->actor.scale.x = this->scale.x; + this->actor.scale.y = this->scale.y; + this->actor.scale.z = this->scale.z; + Audio_PlayActorSound2(&this->actor, NA_SE_PL_SPIRAL_HEAL_BEAM - SFX_FLAG); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); +} + +void EnDyExtra_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Color_RGBA8 primColors[] = { { 255, 255, 170, 255 }, { 255, 255, 170, 255 } }; + static Color_RGBA8 envColors[] = { { 255, 100, 255, 255 }, { 100, 255, 255, 255 } }; + static u8 D_809FFC50[] = { 0x02, 0x01, 0x01, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, + 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x02 }; + EnDyExtra* this = (EnDyExtra*)thisx; + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + Vtx* vertices = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gGreatFairySpiralBeamVtx)); + s32 i; + u8 unk[3]; + + unk[0] = 0.0f; + unk[1] = (s8)(this->unk_158 * 240.0f); + unk[2] = (s8)(this->unk_158 * 255.0f); + + for (i = 0; i < 27; i++) { + if (D_809FFC50[i]) { + vertices[i].v.cn[3] = unk[D_809FFC50[i]]; + } + } + + OPEN_DISPS(gfxCtx, "../z_en_dy_extra.c", 294); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->state.frames * 2, 0, 0x20, 0x40, 1, + globalCtx->state.frames, globalCtx->state.frames * -8, 0x10, 0x10)); + gDPPipeSync(POLY_XLU_DISP++); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_dy_extra.c", 307), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, primColors[this->type].r, primColors[this->type].g, + primColors[this->type].b, 255); + gDPSetEnvColor(POLY_XLU_DISP++, envColors[this->type].r, envColors[this->type].g, envColors[this->type].b, 128); + gSPDisplayList(POLY_XLU_DISP++, gGreatFairySpiralBeamDL); + + CLOSE_DISPS(gfxCtx, "../z_en_dy_extra.c", 325); +} diff --git a/soh/src/overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.h b/soh/src/overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.h new file mode 100644 index 000000000..4cb282ac6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Dy_Extra/z_en_dy_extra.h @@ -0,0 +1,22 @@ +#ifndef Z_EN_DY_EXTRA_H +#define Z_EN_DY_EXTRA_H + +#include "ultra64.h" +#include "global.h" + +struct EnDyExtra; + +typedef void (*EnDyExtraActionFunc)(struct EnDyExtra*, GlobalContext*); + +typedef struct EnDyExtra { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnDyExtraActionFunc actionFunc; + /* 0x0150 */ s16 type; + /* 0x0152 */ s16 trigger; + /* 0x0154 */ s16 timer; + /* 0x0158 */ f32 unk_158; + /* 0x015C */ Vec3f scale; + /* 0x0168 */ Vec3f unk_168; // Set and not used +} EnDyExtra; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Eg/z_en_eg.c b/soh/src/overlays/actors/ovl_En_Eg/z_en_eg.c new file mode 100644 index 000000000..8cc0628a5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Eg/z_en_eg.c @@ -0,0 +1,77 @@ +/* + * File: z_en_eg.c + * Overlay: ovl_En_Eg + * Description: Triggers a void out (used in the tower collapse sequence?) + */ + +#include "z_en_eg.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnEg_Init(Actor* thisx, GlobalContext* globalCtx); +void EnEg_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnEg_Update(Actor* thisx, GlobalContext* globalCtx); +void EnEg_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_809FFDC8(EnEg* this, GlobalContext* globalCtx); + +static s32 voided = false; + +static EnEgActionFunc sActionFuncs[] = { + func_809FFDC8, +}; + +const ActorInit En_Eg_InitVars = { + ACTOR_EN_EG, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_ZL2, + sizeof(EnEg), + (ActorFunc)EnEg_Init, + (ActorFunc)EnEg_Destroy, + (ActorFunc)EnEg_Update, + (ActorFunc)EnEg_Draw, + NULL, +}; + +void EnEg_PlayVoidOutSFX() { + func_800788CC(NA_SE_OC_ABYSS); +} + +void EnEg_Destroy(Actor* thisx, GlobalContext* globalCtx) { + voided = false; +} + +void EnEg_Init(Actor* thisx, GlobalContext* globalCtx) { + EnEg* this = (EnEg*)thisx; + + this->action = 0; +} + +void func_809FFDC8(EnEg* this, GlobalContext* globalCtx) { + if (!voided && (gSaveContext.timer2Value < 1) && Flags_GetSwitch(globalCtx, 0x36) && (kREG(0) == 0)) { + // Void the player out + Gameplay_TriggerRespawn(globalCtx); + gSaveContext.respawnFlag = -2; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); + globalCtx->fadeTransition = 2; + EnEg_PlayVoidOutSFX(); + voided = true; + } +} + +void EnEg_Update(Actor* thisx, GlobalContext* globalCtx) { + EnEg* this = (EnEg*)thisx; + s32 action = this->action; + + if (((action < 0) || (0 < action)) || (sActionFuncs[action] == NULL)) { + // "Main Mode is wrong!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sActionFuncs[action](this, globalCtx); + } +} + +void EnEg_Draw(Actor* thisx, GlobalContext* globalCtx) { +} diff --git a/soh/src/overlays/actors/ovl_En_Eg/z_en_eg.h b/soh/src/overlays/actors/ovl_En_Eg/z_en_eg.h new file mode 100644 index 000000000..86b3898ce --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Eg/z_en_eg.h @@ -0,0 +1,15 @@ +#ifndef Z_EN_EG_H +#define Z_EN_EG_H + +#include "ultra64.h" +#include "global.h" + +struct EnEg; + +typedef void (*EnEgActionFunc)(struct EnEg*, GlobalContext*); + +typedef struct EnEg { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; +} EnEg; // size = 0x0154 +#endif diff --git a/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c b/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c new file mode 100644 index 000000000..564d499b5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.c @@ -0,0 +1,718 @@ +#include "z_en_eiyer.h" +#include "objects/object_ei/object_ei.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnEiyer_Init(Actor* thisx, GlobalContext* globalCtx); +void EnEiyer_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnEiyer_Update(Actor* thisx, GlobalContext* globalCtx); +void EnEiyer_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnEiyer_SetupAppearFromGround(EnEiyer* this); +void EnEiyer_SetupUnderground(EnEiyer* this); +void EnEiyer_SetupInactive(EnEiyer* this); +void EnEiyer_SetupAmbush(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_SetupGlide(EnEiyer* this); +void EnEiyer_SetupStartAttack(EnEiyer* this); +void EnEiyer_SetupDiveAttack(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_SetupLand(EnEiyer* this); +void EnEiyer_SetupHurt(EnEiyer* this); +void EnEiyer_SetupDie(EnEiyer* this); +void EnEiyer_SetupDead(EnEiyer* this); +void EnEiyer_SetupStunned(EnEiyer* this); + +void EnEiyer_AppearFromGround(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_WanderUnderground(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_CircleUnderground(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Inactive(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Ambush(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Glide(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_StartAttack(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_DiveAttack(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Land(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Hurt(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Die(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Dead(EnEiyer* this, GlobalContext* globalCtx); +void EnEiyer_Stunned(EnEiyer* this, GlobalContext* globalCtx); + +const ActorInit En_Eiyer_InitVars = { + ACTOR_EN_EIYER, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_EI, + sizeof(EnEiyer), + (ActorFunc)EnEiyer_Init, + (ActorFunc)EnEiyer_Destroy, + (ActorFunc)EnEiyer_Update, + (ActorFunc)EnEiyer_Draw, + NULL, +}; + +static ColliderCylinderInit sColCylInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x04, 0x08 }, + { 0x00000019, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 27, 17, -10, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 2, 45, 15, 100 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x0), + /* Light magic */ DMG_ENTRY(4, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x19, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 5, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2500, ICHAIN_STOP), +}; + +/** + * params 0: Spawn 3 clones and circle around spawn point + * params 1-3: Clone, spawn another clone for the main Eiyer if params < 3 + * params 10: Normal Eiyer, wander around spawn point + */ +void EnEiyer_Init(Actor* thisx, GlobalContext* globalCtx) { + EnEiyer* this = (EnEiyer*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 600.0f, ActorShadow_DrawCircle, 65.0f); + SkelAnime_Init(globalCtx, &this->skelanime, &gStingerSkel, &gStingerIdleAnim, this->jointTable, this->morphTable, + 19); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sColCylInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + + if (this->actor.params < 3) { + // Each clone spawns another clone + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EIYER, this->actor.home.pos.x, + this->actor.home.pos.y, this->actor.home.pos.z, 0, this->actor.shape.rot.y + 0x4000, 0, + this->actor.params + 1) == NULL) { + Actor_Kill(&this->actor); + return; + } + + if (this->actor.params == 0) { + Actor* child = this->actor.child; + s32 clonesSpawned; + + for (clonesSpawned = 0; clonesSpawned != 3; clonesSpawned++) { + if (child == NULL) { + break; + } + child = child->child; + } + + if (clonesSpawned != 3) { + for (child = &this->actor; child != NULL; child = child->child) { + Actor_Kill(child); + } + return; + } else { + this->actor.child->parent = &this->actor; + this->actor.child->child->parent = &this->actor; + this->actor.child->child->child->parent = &this->actor; + } + } + } + + if (this->actor.params == 0 || this->actor.params == 10) { + EnEiyer_SetupAppearFromGround(this); + } else { + EnEiyer_SetupInactive(this); + } +} + +void EnEiyer_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnEiyer* this = (EnEiyer*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnEiyer_RotateAroundHome(EnEiyer* this) { + this->actor.world.pos.x = Math_SinS(this->actor.world.rot.y) * 80.0f + this->actor.home.pos.x; + this->actor.world.pos.z = Math_CosS(this->actor.world.rot.y) * 80.0f + this->actor.home.pos.z; + this->actor.shape.rot.y = this->actor.world.rot.y + 0x4000; +} + +void EnEiyer_SetupAppearFromGround(EnEiyer* this) { + this->collider.info.bumper.dmgFlags = 0x19; + Animation_PlayLoop(&this->skelanime, &gStingerIdleAnim); + + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.y = this->actor.home.pos.y - 40.0f; + this->actor.world.pos.z = this->actor.home.pos.z; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + + if (this->actor.params != 0xA) { + if (this->actor.params == 0) { + this->actor.world.rot.y = Rand_ZeroOne() * 0x10000; + } else { + this->actor.world.rot.y = this->actor.parent->world.rot.y + this->actor.params * 0x4000; + } + EnEiyer_RotateAroundHome(this); + } else { + this->actor.world.rot.y = this->actor.shape.rot.y = Rand_ZeroOne() * 0x10000; + } + + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_12); + this->actor.shape.shadowScale = 0.0f; + this->actor.shape.yOffset = 0.0f; + this->actionFunc = EnEiyer_AppearFromGround; +} + +void EnEiyer_SetupUnderground(EnEiyer* this) { + if (this->actor.params == 0xA) { + this->actor.speedXZ = -0.5f; + this->actionFunc = EnEiyer_WanderUnderground; + } else { + this->actionFunc = EnEiyer_CircleUnderground; + } + + this->collider.base.acFlags |= AC_ON; + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.flags |= ACTOR_FLAG_0; +} + +void EnEiyer_SetupInactive(EnEiyer* this) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actionFunc = EnEiyer_Inactive; +} + +void EnEiyer_SetupAmbush(EnEiyer* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + Animation_PlayOnce(&this->skelanime, &gStingerBackflipAnim); + this->collider.info.bumper.dmgFlags = ~0x00300000; + this->basePos = this->actor.world.pos; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.flags |= ACTOR_FLAG_12; + this->collider.base.acFlags &= ~AC_ON; + this->actor.shape.shadowScale = 65.0f; + this->actor.shape.yOffset = 600.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_JUMP); + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, NULL, NULL, 1, 700); + this->actionFunc = EnEiyer_Ambush; +} + +void EnEiyer_SetupGlide(EnEiyer* this) { + this->targetYaw = this->actor.shape.rot.y; + this->basePos.y = (cosf(-M_PI / 8) * 5.0f) + this->actor.world.pos.y; + Animation_MorphToLoop(&this->skelanime, &gStingerHitAnim, -5.0f); + this->timer = 60; + this->actionFunc = EnEiyer_Glide; +} + +void EnEiyer_SetupStartAttack(EnEiyer* this) { + this->actionFunc = EnEiyer_StartAttack; +} + +void EnEiyer_SetupDiveAttack(EnEiyer* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->actor.velocity.y = 0.0f; + this->basePos.y = player->actor.world.pos.y + 15.0f; + this->collider.base.atFlags |= AT_ON; + this->collider.base.atFlags &= ~AT_HIT; + this->actionFunc = EnEiyer_DiveAttack; +} + +void EnEiyer_SetupLand(EnEiyer* this) { + Animation_MorphToPlayOnce(&this->skelanime, &gStingerDiveAnim, -3.0f); + this->collider.base.atFlags &= ~AT_ON; + this->actor.flags |= ACTOR_FLAG_4; + + // Update BgCheck info, play sound, and spawn effect on the first frame of the land action + this->timer = -1; + this->actor.gravity = 0.0f; + this->collider.dim.height = sColCylInit.dim.height; + this->actionFunc = EnEiyer_Land; +} + +void EnEiyer_SetupHurt(EnEiyer* this) { + this->basePos.y = this->actor.world.pos.y; + Animation_Change(&this->skelanime, &gStingerHitAnim, 2.0f, 0.0f, 0.0f, 0, -3.0f); + this->timer = 40; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 5.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 200, 0, 40); + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnEiyer_Hurt; +} + +void EnEiyer_SetupDie(EnEiyer* this) { + this->timer = 20; + Actor_SetColorFilter(&this->actor, 0x4000, 200, 0, 40); + + if (this->collider.info.bumper.dmgFlags != 0x19) { + this->actor.speedXZ = 6.0f; + Animation_MorphToLoop(&this->skelanime, &gStingerHitAnim, -3.0f); + } else { + this->actor.speedXZ -= 6.0f; + } + + this->collider.info.bumper.dmgFlags = ~0x00300000; + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnEiyer_Die; +} + +void EnEiyer_SetupDead(EnEiyer* this) { + this->actor.colorFilterParams |= 0x2000; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actionFunc = EnEiyer_Dead; +} + +void EnEiyer_SetupStunned(EnEiyer* this) { + Animation_Change(&this->skelanime, &gStingerPopOutAnim, 2.0f, 0.0f, 0.0f, 0, -8.0f); + this->timer = 80; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -1.0f; + this->collider.dim.height = sColCylInit.dim.height + 8; + Actor_SetColorFilter(&this->actor, 0, 200, 0, 80); + this->collider.base.atFlags &= ~AT_ON; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actionFunc = EnEiyer_Stunned; +} + +void EnEiyer_AppearFromGround(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.75f)) { + EnEiyer_SetupUnderground(this); + } +} + +void EnEiyer_CheckPlayerCollision(EnEiyer* this, GlobalContext* globalCtx) { + if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { + this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; + EnEiyer_SetupAmbush(this, globalCtx); + } +} + +void EnEiyer_CircleUnderground(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + this->actor.world.rot.y += -0x60; + EnEiyer_RotateAroundHome(this); + EnEiyer_CheckPlayerCollision(this, globalCtx); + + // Clones disappear when the main Eiyer leaves the ground + if (this->actor.params != 0 && ((EnEiyer*)this->actor.parent)->actionFunc != EnEiyer_CircleUnderground) { + EnEiyer_SetupInactive(this); + } +} + +void EnEiyer_WanderUnderground(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) > 100.0f) { + this->targetYaw = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos) + 0x8000; + } else if (this->targetYaw == this->actor.world.rot.y && Rand_ZeroOne() > 0.99f) { + this->targetYaw = + this->actor.world.rot.y + (Rand_ZeroOne() < 0.5f ? -1 : 1) * (Rand_ZeroOne() * 0x2000 + 0x2000); + } + + Math_ScaledStepToS(&this->actor.world.rot.y, this->targetYaw, 0xB6); + EnEiyer_CheckPlayerCollision(this, globalCtx); +} + +void EnEiyer_Inactive(EnEiyer* this, GlobalContext* globalCtx) { + EnEiyer* parent; + + if (this->actor.home.pos.y - 50.0f < this->actor.world.pos.y) { + this->actor.world.pos.y -= 0.5f; + } + + parent = (EnEiyer*)this->actor.parent; + if (parent->actionFunc == EnEiyer_Dead) { + Actor_Kill(&this->actor); + } else if (parent->actionFunc == EnEiyer_AppearFromGround) { + EnEiyer_SetupAppearFromGround(this); + } +} + +void EnEiyer_Ambush(EnEiyer* this, GlobalContext* globalCtx) { + s32 animFinished; + f32 curFrame; + f32 xzOffset; + s32 bgId; + + animFinished = SkelAnime_Update(&this->skelanime); + curFrame = this->skelanime.curFrame; + + if (this->skelanime.curFrame < 12.0f) { + this->actor.world.pos.y = ((1.0f - cosf((0.996f * M_PI / 12.0f) * curFrame)) * 40.0f) + this->actor.home.pos.y; + xzOffset = sinf((0.996f * M_PI / 12.0f) * curFrame) * -40.0f; + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y) * xzOffset) + this->basePos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y) * xzOffset) + this->basePos.z; + } else { + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 80.0f, 0.5f); + this->actor.speedXZ = 0.8f; + } + + if (animFinished) { + this->collider.base.acFlags |= AC_ON; + EnEiyer_SetupGlide(this); + } else { + this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId, + &this->actor, &this->actor.world.pos); + } +} + +void EnEiyer_Glide(EnEiyer* this, GlobalContext* globalCtx) { + f32 curFrame; + s32 pad; + s16 yawChange; + + SkelAnime_Update(&this->skelanime); + + if (this->timer != 0) { + this->timer--; + } + + curFrame = this->skelanime.curFrame; + Math_ApproachF(&this->basePos.y, this->actor.floorHeight + 80.0f + 5.0f, 0.3f, this->actor.speedXZ); + this->actor.world.pos.y = this->basePos.y - cosf((curFrame - 5.0f) * (M_PI / 40)) * 5.0f; + + if (curFrame <= 45.0f) { + Math_StepToF(&this->actor.speedXZ, 1.0f, 0.03f); + } else { + Math_StepToF(&this->actor.speedXZ, 1.5f, 0.03f); + } + + if (this->actor.bgCheckFlags & 8) { + this->targetYaw = this->actor.wallYaw; + } + + if (Math_ScaledStepToS(&this->actor.world.rot.y, this->targetYaw, 0xB6)) { + if (this->timer != 0 || Rand_ZeroOne() > 0.05f) { + this->actor.world.rot.y += 0x100; + } else { + yawChange = Rand_S16Offset(0x2000, 0x2000); + this->targetYaw = (Rand_ZeroOne() < 0.5f ? -1 : 1) * yawChange + this->actor.world.rot.y; + } + } + + if (this->timer == 0 && this->actor.yDistToPlayer < 0.0f && this->actor.xzDistToPlayer < 120.0f) { + EnEiyer_SetupStartAttack(this); + } + + func_8002F974(&this->actor, NA_SE_EN_EIER_FLY - SFX_FLAG); +} + +void EnEiyer_StartAttack(EnEiyer* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f focus; + + SkelAnime_Update(&this->skelanime); + + if (this->actor.shape.rot.x > 0 && this->actor.shape.rot.x < 0x8000) { + focus.x = player->actor.world.pos.x; + focus.y = player->actor.world.pos.y + 20.0f; + focus.z = player->actor.world.pos.z; + + if (Math_ScaledStepToS(&this->actor.shape.rot.x, Actor_WorldPitchTowardPoint(&this->actor, &focus), 0x1000)) { + EnEiyer_SetupDiveAttack(this, globalCtx); + } + } else { + this->actor.shape.rot.x -= 0x1000; + } + + this->actor.world.rot.x = -this->actor.shape.rot.x; + Math_StepToF(&this->actor.speedXZ, 5.0f, 0.3f); + Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 2, 0x71C); + func_8002F974(&this->actor, NA_SE_EN_EIER_FLY - SFX_FLAG); +} + +void EnEiyer_DiveAttack(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + this->actor.speedXZ *= 1.1f; + + if (this->actor.bgCheckFlags & 8 || this->actor.bgCheckFlags & 1) { + EnEiyer_SetupLand(this); + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~(AT_ON | AT_HIT); + } + + func_8002F974(&this->actor, NA_SE_EN_EIER_FLY - SFX_FLAG); +} + +void EnEiyer_Land(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ScaledStepToS(&this->actor.world.rot.x, -0x4000, 0x450); + Math_StepToF(&this->actor.speedXZ, 7.0f, 1.0f); + + if (this->timer == -1) { + if (this->actor.bgCheckFlags & 8 || this->actor.bgCheckFlags & 1) { + this->timer = 10; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 30, NA_SE_EN_OCTAROCK_SINK); + + if (this->actor.bgCheckFlags & 1) { + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, NULL, NULL, 1, 700); + } + } + } else { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + this->actor.shape.rot.x = 0; + this->actor.world.rot.x = 0; + EnEiyer_SetupAppearFromGround(this); + } + } +} + +void EnEiyer_Hurt(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->timer != 0) { + this->timer--; + } + + Math_ApproachF(&this->basePos.y, this->actor.floorHeight + 80.0f + 5.0f, 0.5f, this->actor.speedXZ); + this->actor.world.pos.y = this->basePos.y - 5.0f; + + if (this->actor.bgCheckFlags & 8) { + this->targetYaw = this->actor.wallYaw; + } else { + this->targetYaw = this->actor.yawTowardsPlayer + 0x8000; + } + + Math_ScaledStepToS(&this->actor.world.rot.y, this->targetYaw, 0x38E); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x200); + this->actor.shape.rot.z = sinf(this->timer * (M_PI / 5)) * 5120.0f; + + if (this->timer == 0) { + this->actor.shape.rot.x = 0; + this->actor.shape.rot.z = 0; + this->collider.base.acFlags |= AC_ON; + EnEiyer_SetupGlide(this); + } + this->actor.world.rot.x = -this->actor.shape.rot.x; +} + +void EnEiyer_Die(EnEiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->actor.speedXZ > 0.0f) { + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x4000, 0x400); + } else { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x4000, 0x400); + } + + this->actor.shape.rot.z += 0x1000; + + if (this->timer != 0) { + this->timer--; + } + + this->actor.world.rot.x = -this->actor.shape.rot.x; + + if (this->timer == 0 || this->actor.bgCheckFlags & 0x10) { + EnEiyer_SetupDead(this); + } +} + +void EnEiyer_Dead(EnEiyer* this, GlobalContext* globalCtx) { + this->actor.shape.shadowAlpha = CLAMP_MIN((s16)(this->actor.shape.shadowAlpha - 5), 0); + this->actor.world.pos.y -= 2.0f; + + if (this->actor.shape.shadowAlpha == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 80); + Actor_Kill(&this->actor); + } +} + +void EnEiyer_Stunned(EnEiyer* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x200); + SkelAnime_Update(&this->skelanime); + + if (Animation_OnFrame(&this->skelanime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_FLUTTER); + } + + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + + if (this->timer == 0) { + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->collider.dim.height = sColCylInit.dim.height; + EnEiyer_SetupGlide(this); + } +} + +void EnEiyer_UpdateDamage(EnEiyer* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + + if (this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_DEAD); + this->actor.flags &= ~ACTOR_FLAG_0; + } + + // If underground, one hit kill + if (this->collider.info.bumper.dmgFlags == 0x19) { + if (this->actor.colChkInfo.damage == 0) { + EnEiyer_SetupAmbush(this, globalCtx); + } else { + EnEiyer_SetupDie(this); + } + } else if (this->actor.colChkInfo.damageEffect == 1) { + if (this->actionFunc != EnEiyer_Stunned) { + EnEiyer_SetupStunned(this); + } + } else if (this->actor.colChkInfo.health != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_DAMAGE); + EnEiyer_SetupHurt(this); + } else { + this->collider.dim.height = sColCylInit.dim.height; + EnEiyer_SetupDie(this); + } + } + } +} + +void EnEiyer_Update(Actor* thisx, GlobalContext* globalCtx) { + EnEiyer* this = (EnEiyer*)thisx; + s32 pad; + + EnEiyer_UpdateDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + + if (this->actor.world.rot.x == 0 || this->actionFunc == EnEiyer_Stunned) { + Actor_MoveForward(&this->actor); + } else { + func_8002D97C(&this->actor); + } + + if (this->actionFunc == EnEiyer_Glide || this->actionFunc == EnEiyer_DiveAttack || + this->actionFunc == EnEiyer_Stunned || this->actionFunc == EnEiyer_Die || this->actionFunc == EnEiyer_Hurt || + (this->actionFunc == EnEiyer_Land && this->timer == -1)) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 27.0f, 30.0f, 7); + } + + if (this->actor.params == 0xA || + (this->actionFunc != EnEiyer_AppearFromGround && this->actionFunc != EnEiyer_CircleUnderground)) { + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + // only the main Eiyer can ambush the player + if (this->actor.params == 0 || this->actor.params == 0xA) { + Collider_UpdateCylinder(&this->actor, &this->collider); + if (this->collider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->actionFunc != EnEiyer_Ambush) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + if (this->actor.flags & ACTOR_FLAG_0) { + this->actor.focus.pos.x = this->actor.world.pos.x + Math_SinS(this->actor.shape.rot.y) * 12.5f; + this->actor.focus.pos.z = this->actor.world.pos.z + Math_CosS(this->actor.shape.rot.y) * 12.5f; + this->actor.focus.pos.y = this->actor.world.pos.y; + } +} + +s32 EnEiyer_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnEiyer* this = (EnEiyer*)thisx; + + if (limbIndex == 1) { + pos->z += 2500.0f; + } + + if (this->collider.info.bumper.dmgFlags == 0x19 && limbIndex != 9 && limbIndex != 10) { + *dList = NULL; + } + return 0; +} + +void EnEiyer_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnEiyer* this = (EnEiyer*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_eiyer.c", 1494); + if (this->actionFunc != EnEiyer_Dead) { + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, + EnEiyer_OverrideLimbDraw, NULL, this, POLY_OPA_DISP); + } else { + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, D_80116280); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->actor.shape.shadowAlpha); + + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, + EnEiyer_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_eiyer.c", 1541); +} diff --git a/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.h b/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.h new file mode 100644 index 000000000..fbd152996 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Eiyer/z_en_eiyer.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_EIYER_H +#define Z_EN_EIYER_H + +#include "ultra64.h" +#include "global.h" + +struct EnEiyer; + +typedef void (*EnEiyerActionFunc)(struct EnEiyer*, GlobalContext*); + +typedef struct EnEiyer { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelanime; + /* 0x0190 */ EnEiyerActionFunc actionFunc; + /* 0x0194 */ s16 timer; + /* 0x0196 */ s16 targetYaw; + /* 0x0198 */ Vec3s jointTable[19]; + /* 0x020A */ Vec3s morphTable[19]; + /* 0x027C */ Vec3f basePos; + /* 0x0288 */ ColliderCylinder collider; +} EnEiyer; // size = 0x02D4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c new file mode 100644 index 000000000..d7530014d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -0,0 +1,1555 @@ +/* + * File: z_en_elf.c + * Overlay: ovl_En_Elf + * Description: Fairy + */ + +#include "z_en_elf.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) + +#define FAIRY_FLAG_TIMED (1 << 8) +#define FAIRY_FLAG_BIG (1 << 9) + +void EnElf_Init(Actor* thisx, GlobalContext* globalCtx); +void EnElf_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnElf_Update(Actor* thisx, GlobalContext* globalCtx); +void EnElf_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_80A053F0(Actor* thisx, GlobalContext* globalCtx); +void func_80A052F4(Actor* thisx, GlobalContext* globalCtx); +void func_80A05208(Actor* thisx, GlobalContext* globalCtx); +void func_80A05188(Actor* thisx, GlobalContext* globalCtx); +void func_80A05114(Actor* thisx, GlobalContext* globalCtx); +void func_80A05040(Actor* thisx, GlobalContext* globalCtx); + +// Navi +void func_80A03CF8(EnElf* this, GlobalContext* globalCtx); + +// Healing Fairies +void func_80A0329C(EnElf* this, GlobalContext* globalCtx); +void func_80A03610(EnElf* this, GlobalContext* globalCtx); + +// Healing Fairies Revive From Death +void func_80A03990(EnElf* this, GlobalContext* globalCtx); +void func_80A03814(EnElf* this, GlobalContext* globalCtx); + +// Kokiri Fairies +void func_80A0353C(EnElf* this, GlobalContext* globalCtx); + +// Fairy Spawner +void func_80A03604(EnElf* this, GlobalContext* globalCtx); + +// Move(?) functions +void func_80A0214C(EnElf* this, GlobalContext* globalCtx); +void func_80A02AA4(EnElf* this, GlobalContext* globalCtx); +void func_80A02A20(EnElf* this, GlobalContext* globalCtx); +void func_80A02B38(EnElf* this, GlobalContext* globalCtx); +void func_80A020A4(EnElf* this, GlobalContext* globalCtx); +void func_80A01FE0(EnElf* this, GlobalContext* globalCtx); + +// misc +void func_80A04414(EnElf* this, GlobalContext* globalCtx); +void func_80A0461C(EnElf* this, GlobalContext* globalCtx); +void EnElf_SpawnSparkles(EnElf* this, GlobalContext* globalCtx, s32 sparkleLife); +void EnElf_GetCutsceneNextPos(Vec3f* vec, GlobalContext* globalCtx, s32 action); + +const ActorInit En_Elf_InitVars = { + ACTOR_EN_ELF, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnElf), + (ActorFunc)EnElf_Init, + (ActorFunc)EnElf_Destroy, + (ActorFunc)EnElf_Update, + (ActorFunc)EnElf_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 8, ICHAIN_STOP), +}; + +static Color_RGBAf sInnerColors[] = { + { 255.0f, 255.0f, 255.0f, 255.0f }, + { 255.0f, 220.0f, 220.0f, 255.0f }, +}; + +static Color_RGBAf sOuterColors[] = { + { 255.0f, 255.0f, 255.0f, 255.0f }, + { 255.0f, 50.0f, 100.0f, 255.0f }, +}; + +typedef struct { + u8 r, g, b; +} FairyColorFlags; + +static FairyColorFlags sColorFlags[] = { + { 0, 0, 0 }, { 1, 0, 0 }, { 1, 2, 0 }, { 1, 0, 2 }, { 0, 1, 0 }, { 2, 1, 0 }, { 0, 1, 2 }, + { 0, 0, 1 }, { 2, 0, 1 }, { 0, 2, 1 }, { 1, 1, 0 }, { 1, 0, 1 }, { 0, 1, 1 }, +}; + +void EnElf_SetupAction(EnElf* this, EnElfActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void func_80A01C38(EnElf* this, s32 arg1) { + this->unk_2A8 = arg1; + + switch (this->unk_2A8) { + case 0: + this->unk_2AE = 0x400; + this->unk_2B0 = 0x200; + this->func_2C8 = func_80A02A20; + this->unk_2C0 = 100; + this->unk_2B4 = 5.0f; + this->unk_2B8 = 20.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 12: + this->unk_2AE = 0x400; + this->unk_2B0 = 0x200; + this->func_2C8 = func_80A02A20; + this->unk_2C0 = 100; + this->unk_2B4 = 1.0f; + this->unk_2B8 = 5.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 10: + this->unk_2AE = 0x400; + this->unk_2B0 = 0; + this->func_2C8 = func_80A02A20; + this->unk_2B8 = 0.0f; + this->unk_2B4 = 5.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 9: + this->unk_2AE = 0x1000; + this->unk_2B0 = 0x200; + this->func_2C8 = func_80A02A20; + this->unk_2B4 = 3.0f; + this->unk_2B8 = 10.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 7: + this->func_2C8 = func_80A02A20; + this->unk_2AE = 0x1E; + this->unk_2C0 = 1; + this->unk_2B4 = 0.0f; + this->unk_2B8 = 0.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 8: + this->unk_2AE = 0x1000; + this->unk_2B0 = 0x200; + this->func_2C8 = func_80A02A20; + this->unk_2B4 = 0.0f; + this->unk_2B8 = 0.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 1: + this->unk_2AE = 0x1000; + this->unk_2B0 = 0x800; + this->func_2C8 = func_80A02A20; + this->unk_2B4 = 5.0f; + this->unk_2B8 = 7.5f; + this->skelAnime.playSpeed = 2.0f; + break; + case 2: + this->unk_2AE = 0x400; + this->unk_2B0 = 0x1000; + this->func_2C8 = func_80A02AA4; + this->unk_2B4 = 10.0f; + this->unk_2B8 = 20.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 3: + this->unk_2B0 = 0x600; + this->func_2C8 = func_80A02B38; + this->unk_2B8 = 1.0f; + this->unk_2B4 = 1.0f; + this->skelAnime.playSpeed = 1.0f; + break; + case 4: + this->unk_2B0 = 0x800; + this->func_2C8 = func_80A02B38; + this->unk_2B4 = 20.0f; + this->unk_2B8 = 10.0; + this->skelAnime.playSpeed = 2.0f; + break; + case 5: + this->unk_2B0 = 0x200; + this->func_2C8 = func_80A02B38; + this->unk_2B4 = 10.0f; + this->unk_2B8 = 10.0f; + this->skelAnime.playSpeed = 0.5f; + break; + case 6: + this->unk_2AE = 0x1000; + this->unk_2B0 = 0x800; + this->func_2C8 = func_80A02A20; + this->unk_2B4 = 60.0f; + this->unk_2B8 = 20.0f; + this->skelAnime.playSpeed = 2.0f; + break; + case 11: + this->unk_2AE = 0x400; + this->unk_2B0 = 0x2000; + this->func_2C8 = func_80A02A20; + this->unk_2C0 = 42; + this->unk_2B4 = 5.0f; + this->unk_2B8 = 1.0f; + this->skelAnime.playSpeed = 1.0f; + break; + } +} + +s32 func_80A01F90(Vec3f* this, Vec3f* arg1, f32 arg2) { + return SQ(arg2) < (SQ(arg1->x - this->x) + SQ(arg1->z - this->z)); +} + +void func_80A01FE0(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (!func_80A01F90(&this->actor.world.pos, &player->actor.world.pos, 30.0f)) { + this->unk_2B8 = 0.5f; + } else { + this->unk_2B8 = 2.0f; + } + + if (this->unk_2C0 > 0) { + this->unk_2C0--; + } else { + this->unk_2A8 = 1; + this->unk_2AC = 0x80; + this->unk_2B8 = Rand_ZeroFloat(1.0f) + 0.5f; + this->unk_2B0 = Rand_CenteredFloat(32767.0f); + this->func_2C8 = func_80A0214C; + } +} + +void func_80A020A4(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (func_80A01F90(&this->actor.world.pos, &player->actor.world.pos, 50.0f)) { + if (this->unk_2C0 > 0) { + this->unk_2C0--; + } else { + this->unk_2A8 = 1; + this->unk_2AC = 0x80; + this->unk_2B8 = Rand_ZeroFloat(1.0f) + 0.5f; + this->unk_2B0 = Rand_CenteredFloat(32767.0f); + this->func_2C8 = func_80A0214C; + } + } +} + +void func_80A0214C(EnElf* this, GlobalContext* globalCtx) { + f32 xzDistToPlayer; + + if (this->unk_2C0 > 0) { + this->unk_2C0--; + } else { + xzDistToPlayer = this->actor.xzDistToPlayer; + if (xzDistToPlayer < 50.0f) { + if (Rand_ZeroOne() < 0.2f) { + this->unk_2A8 = 2; + this->unk_2AC = 0x400; + this->unk_2B8 = 2.0f; + this->func_2C8 = func_80A020A4; + this->actor.speedXZ = 1.5f; + this->unk_2C0 = (s16)Rand_ZeroFloat(8.0f) + 4; + } else { + this->unk_2C0 = 10; + } + } else { + if (xzDistToPlayer > 150.0f) { + xzDistToPlayer = 150.0f; + } + + xzDistToPlayer = ((xzDistToPlayer - 50.0f) * 0.95f) + 0.05f; + + if (Rand_ZeroOne() < xzDistToPlayer) { + this->unk_2A8 = 3; + this->unk_2AC = 0x200; + this->unk_2B8 = (xzDistToPlayer * 2.0f) + 1.0f; + this->func_2C8 = func_80A01FE0; + this->unk_2C0 = (s16)Rand_ZeroFloat(16.0f) + 0x10; + } else { + this->unk_2C0 = 10; + } + } + } + + if (Rand_ZeroOne() < 0.1f) { + this->unk_2A8 = 1; + this->unk_2AC = 0x80; + this->unk_2B8 = Rand_ZeroFloat(0.5f) + 0.5f; + this->unk_2B0 = Rand_CenteredFloat(32767.0f); + } +} + +void func_80A0232C(EnElf* this, GlobalContext* globalCtx) { + if (func_80A01F90(&this->unk_28C, &this->actor.world.pos, 100.0f)) { + this->unk_2A8 = 0; + this->unk_2AC = 0x200; + this->func_2C8 = func_80A0214C; + this->unk_2B8 = 1.5f; + } else { + this->func_2C8(this, globalCtx); + } +} + +f32 EnElf_GetColorValue(s32 colorFlag) { + switch (colorFlag) { + case 1: + return Rand_ZeroFloat(55.0f) + 200.0f; + case 2: + return Rand_ZeroFloat(255.0f); + default: + return 0.0f; + } +} + +void EnElf_Init(Actor* thisx, GlobalContext* globalCtx) { + EnElf* this = (EnElf*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 colorConfig; + s32 i; + + Actor_ProcessInitChain(thisx, sInitChain); + SkelAnime_Init(globalCtx, &this->skelAnime, &gFairySkel, &gFairyAnim, this->jointTable, this->morphTable, 15); + ActorShape_Init(&thisx->shape, 0.0f, NULL, 15.0f); + thisx->shape.shadowAlpha = 0xFF; + + Lights_PointGlowSetInfo(&this->lightInfoGlow, thisx->world.pos.x, thisx->world.pos.y, thisx->world.pos.z, 255, 255, + 255, 0); + this->lightNodeGlow = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfoGlow); + + Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, thisx->world.pos.x, thisx->world.pos.y, thisx->world.pos.z, 255, + 255, 255, 0); + this->lightNodeNoGlow = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfoNoGlow); + + this->fairyFlags = 0; + this->disappearTimer = 600; + this->unk_2A4 = 0.0f; + colorConfig = 0; + + switch (thisx->params) { + case FAIRY_NAVI: + thisx->room = -1; + EnElf_SetupAction(this, func_80A03CF8); + func_80A01C38(this, 0); + this->fairyFlags |= 4; + thisx->update = func_80A053F0; + this->elfMsg = NULL; + this->unk_2C7 = 0x14; + + if ((gSaveContext.naviTimer >= 25800) || (gSaveContext.naviTimer < 3000)) { + gSaveContext.naviTimer = 0; + } + break; + case FAIRY_REVIVE_BOTTLE: + colorConfig = -1; + EnElf_SetupAction(this, func_80A03610); + this->unk_2B8 = Math_Vec3f_DistXZ(&thisx->world.pos, &player->actor.world.pos); + this->unk_2AC = player->actor.shape.rot.y; + this->unk_2B0 = -0x1000; + this->unk_28C.y = thisx->world.pos.y - player->actor.world.pos.y; + this->unk_2AA = 0; + this->unk_2B4 = 0.0f; + break; + case FAIRY_REVIVE_DEATH: + colorConfig = -1; + EnElf_SetupAction(this, func_80A03990); + this->unk_2B8 = 0.0f; + this->unk_2AC = player->actor.shape.rot.y; + this->unk_2B0 = 0; + this->unk_28C.y = thisx->world.pos.y - player->actor.world.pos.y; + this->unk_2AA = 0; + this->unk_2B4 = 7.0f; + break; + case FAIRY_HEAL_BIG: + this->fairyFlags |= FAIRY_FLAG_BIG; + thisx->shape.shadowDraw = ActorShadow_DrawWhiteCircle; + case FAIRY_HEAL_TIMED: + this->fairyFlags |= FAIRY_FLAG_TIMED; + case FAIRY_HEAL: + colorConfig = -1; + EnElf_SetupAction(this, func_80A0329C); + this->unk_2B4 = Rand_ZeroFloat(10.0f) + 10.0f; + this->unk_2AA = 0; + this->unk_2AE = (s16)(Rand_ZeroFloat(1048.0f)) + 0x200; + this->unk_28C = thisx->world.pos; + this->unk_2BC = Rand_CenteredFloat(32767.0f); + this->func_2C8 = func_80A0214C; + func_80A0232C(this, globalCtx); + this->unk_2C0 = 0; + this->disappearTimer = 240; + break; + case FAIRY_KOKIRI: + colorConfig = Rand_ZeroFloat(11.99f) + 1.0f; + EnElf_SetupAction(this, func_80A0353C); + func_80A01C38(this, 0); + break; + case FAIRY_SPAWNER: + EnElf_SetupAction(this, func_80A03604); + func_80A01C38(this, 8); + + for (i = 0; i < 8; i++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, thisx->world.pos.x, + thisx->world.pos.y - 30.0f, thisx->world.pos.z, 0, 0, 0, FAIRY_HEAL); + } + break; + default: + ASSERT(0, "0", "../z_en_elf.c", 1103); + break; + } + + this->unk_2A0 = 3.0f; + this->innerColor = sInnerColors[0]; + + if (colorConfig > 0) { + this->outerColor.r = EnElf_GetColorValue(sColorFlags[colorConfig].r); + this->outerColor.g = EnElf_GetColorValue(sColorFlags[colorConfig].g); + this->outerColor.b = EnElf_GetColorValue(sColorFlags[colorConfig].b); + this->outerColor.a = 0.0f; + } else { + this->innerColor = sInnerColors[-colorConfig]; + this->outerColor = sOuterColors[-colorConfig]; + } +} + +void func_80A0299C(EnElf* this, s32 arg1) { +} + +void func_80A029A8(EnElf* this, s16 increment) { + if (this->disappearTimer < 600) { + this->disappearTimer += increment; + } +} + +void EnElf_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnElf* this = (EnElf*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNodeGlow); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNodeNoGlow); +} + +void func_80A02A20(EnElf* this, GlobalContext* globalCtx) { + this->unk_28C.x = Math_SinS(this->unk_2AC) * this->unk_2B8; + this->unk_28C.y = Math_SinS(this->unk_2AA) * this->unk_2B4; + this->unk_28C.z = Math_CosS(this->unk_2AC) * this->unk_2B8; + this->unk_2AC += this->unk_2B0; + this->unk_2AA += this->unk_2AE; +} + +void func_80A02AA4(EnElf* this, GlobalContext* globalCtx) { + f32 xzScale; + + xzScale = (Math_CosS(this->unk_2AA) * this->unk_2B4) + this->unk_2B8; + + this->unk_28C.x = Math_SinS(this->unk_2AC) * xzScale; + this->unk_28C.y = 0.0f; + this->unk_28C.z = Math_CosS(this->unk_2AC) * xzScale; + + this->unk_2AC += this->unk_2B0; + this->unk_2AA += this->unk_2AE; +} + +void func_80A02B38(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_2AA = (this->unk_2AC * 2) & 0xFFFF; + this->unk_28C.x = Math_SinS(this->unk_2AC) * this->unk_2B8; + this->unk_28C.y = Math_SinS(this->unk_2AA) * this->unk_2B4; + this->unk_28C.z = -Math_SinS(player->actor.shape.rot.y) * this->unk_28C.x; + this->unk_28C.x = Math_CosS(player->actor.shape.rot.y) * this->unk_28C.x; + this->unk_2AC += this->unk_2B0; +} + +void func_80A02BD8(EnElf* this, Vec3f* targetPos, f32 arg2) { + f32 yVelTarget; + f32 yVelDirection; + + yVelTarget = ((targetPos->y + this->unk_28C.y) - this->actor.world.pos.y) * arg2; + yVelDirection = (yVelTarget >= 0.0f) ? 1.0f : -1.0f; + yVelTarget = fabsf(yVelTarget); + yVelTarget = CLAMP(yVelTarget, 0.0f, 20.0f) * yVelDirection; + Math_StepToF(&this->actor.velocity.y, yVelTarget, 32.0f); +} + +void func_80A02C98(EnElf* this, Vec3f* targetPos, f32 arg2) { + f32 xVelTarget; + f32 zVelTarget; + f32 xVelDirection; + f32 zVelDirection; + + xVelTarget = ((targetPos->x + this->unk_28C.x) - this->actor.world.pos.x) * arg2; + zVelTarget = ((targetPos->z + this->unk_28C.z) - this->actor.world.pos.z) * arg2; + + xVelDirection = (xVelTarget >= 0.0f) ? 1.0f : -1.0f; + zVelDirection = (zVelTarget >= 0.0f) ? 1.0f : -1.0f; + + xVelTarget = fabsf(xVelTarget); + zVelTarget = fabsf(zVelTarget); + + xVelTarget = CLAMP(xVelTarget, 0.0f, 20.0f) * xVelDirection; + zVelTarget = CLAMP(zVelTarget, 0.0f, 20.0f) * zVelDirection; + + func_80A02BD8(this, targetPos, arg2); + Math_StepToF(&this->actor.velocity.x, xVelTarget, 1.5f); + Math_StepToF(&this->actor.velocity.z, zVelTarget, 1.5f); + func_8002D7EC(&this->actor); +} + +void func_80A02E30(EnElf* this, Vec3f* targetPos) { + func_80A02BD8(this, targetPos, 0.2f); + this->actor.velocity.x = (targetPos->x + this->unk_28C.x) - this->actor.world.pos.x; + this->actor.velocity.z = (targetPos->z + this->unk_28C.z) - this->actor.world.pos.z; + func_8002D7EC(&this->actor); + this->actor.world.pos.x = targetPos->x + this->unk_28C.x; + this->actor.world.pos.z = targetPos->z + this->unk_28C.z; +} + +void func_80A02EC0(EnElf* this, Vec3f* targetPos) { + func_80A02BD8(this, targetPos, 0.2f); + this->actor.velocity.x = this->actor.velocity.z = 0.0f; + func_8002D7EC(&this->actor); + this->actor.world.pos.x = targetPos->x + this->unk_28C.x; + this->actor.world.pos.z = targetPos->z + this->unk_28C.z; +} + +void func_80A02F2C(EnElf* this, Vec3f* targetPos) { + f32 yVelTarget; + f32 yVelDirection; + + yVelTarget = (((Math_SinS(this->unk_2AA) * this->unk_2B4) + targetPos->y) - this->actor.world.pos.y) * 0.2f; + yVelDirection = (yVelTarget >= 0.0f) ? 1.0f : -1.0f; + this->unk_2AA += this->unk_2AE; + yVelTarget = fabsf(yVelTarget); + yVelTarget = CLAMP(yVelTarget, 0.0f, 20.0f) * yVelDirection; + Math_StepToF(&this->actor.velocity.y, yVelTarget, 1.5f); +} + +void func_80A03018(EnElf* this, GlobalContext* globalCtx) { + s32 pad[2]; + Player* player = GET_PLAYER(globalCtx); + s16 targetYaw; + Vec3f* unk_28C = &this->unk_28C; + + Math_SmoothStepToF(&this->actor.speedXZ, this->unk_2B8, 0.2f, 0.5f, 0.01f); + + switch (this->unk_2A8) { + case 0: + targetYaw = Math_Atan2S(-(this->actor.world.pos.z - unk_28C->z), -(this->actor.world.pos.x - unk_28C->x)); + break; + + case 3: + targetYaw = Math_Atan2S(-(this->actor.world.pos.z - player->actor.world.pos.z), + -(this->actor.world.pos.x - player->actor.world.pos.x)); + break; + + case 2: + targetYaw = Math_Atan2S(this->actor.world.pos.z - player->actor.world.pos.z, + this->actor.world.pos.x - player->actor.world.pos.x); + break; + + default: + targetYaw = this->unk_2B0; + break; + } + + Math_SmoothStepToS(&this->unk_2BC, targetYaw, 10, this->unk_2AC, 0x20); + this->actor.world.rot.y = this->unk_2BC; + Actor_MoveForward(&this->actor); +} + +void func_80A03148(EnElf* this, Vec3f* arg1, f32 arg2, f32 arg3, f32 arg4) { + f32 xVelTarget; + f32 zVelTarget; + f32 xzVelocity; + f32 clampedXZ; + + xVelTarget = ((arg1->x + this->unk_28C.x) - this->actor.world.pos.x) * arg4; + zVelTarget = ((arg1->z + this->unk_28C.z) - this->actor.world.pos.z) * arg4; + arg4 += 0.3f; + arg3 += 30.0f; + + func_80A02BD8(this, arg1, arg4); + + xzVelocity = sqrtf(SQ(xVelTarget) + SQ(zVelTarget)); + + this->actor.speedXZ = clampedXZ = CLAMP(xzVelocity, arg2, arg3); + + if ((xzVelocity != clampedXZ) && (xzVelocity != 0.0f)) { + xzVelocity = clampedXZ / xzVelocity; + xVelTarget *= xzVelocity; + zVelTarget *= xzVelocity; + } + + Math_StepToF(&this->actor.velocity.x, xVelTarget, 5.0f); + Math_StepToF(&this->actor.velocity.z, zVelTarget, 5.0f); + func_8002D7EC(&this->actor); +} + +void func_80A0329C(EnElf* this, GlobalContext* globalCtx) { + Player* refActor = GET_PLAYER(globalCtx); + s32 pad; + Player* player = GET_PLAYER(globalCtx); + f32 heightDiff; + + SkelAnime_Update(&this->skelAnime); + + if (Rand_ZeroOne() < 0.05f) { + this->unk_2B4 = Rand_ZeroFloat(10.0f) + 10.0f; + this->unk_2AE = (s16)(Rand_ZeroFloat(1024.0f)) + 0x200; + } + + func_80A0232C(this, globalCtx); + this->unk_28C.y = player->bodyPartsPos[0].y; + func_80A02F2C(this, &this->unk_28C); + func_80A03018(this, globalCtx); + + if ((this->unk_2A8 == 2) || (this->unk_2A8 == 3)) { + EnElf_SpawnSparkles(this, globalCtx, 16); + } + + if (Actor_HasParent(&this->actor, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + + if (!Player_InCsMode(globalCtx)) { + heightDiff = this->actor.world.pos.y - refActor->actor.world.pos.y; + + if ((heightDiff > 0.0f) && (heightDiff < 60.0f)) { + if (!func_80A01F90(&this->actor.world.pos, &refActor->actor.world.pos, 10.0f)) { + Health_ChangeBy(globalCtx, 128); + if (this->fairyFlags & FAIRY_FLAG_BIG) { + Magic_Fill(globalCtx); + } + this->unk_2B8 = 50.0f; + this->unk_2AC = refActor->actor.shape.rot.y; + this->unk_2B0 = -0x1000; + this->unk_28C.y = 30.0f; + this->unk_2B4 = 0.0f; + this->unk_2AA = 0; + EnElf_SetupAction(this, func_80A03610); + return; + } + } + + if (this->fairyFlags & FAIRY_FLAG_TIMED) { + if (this->disappearTimer > 0) { + this->disappearTimer--; + } else { + this->disappearTimer--; + + if (this->disappearTimer > -10) { + Actor_SetScale(&this->actor, ((this->disappearTimer + 10) * 0.008f) * 0.1f); + } else { + Actor_Kill(&this->actor); + return; + } + } + } + + if (!(this->fairyFlags & FAIRY_FLAG_BIG)) { + // GI_MAX in this case allows the player to catch the actor in a bottle + func_8002F434(&this->actor, globalCtx, GI_MAX, 80.0f, 60.0f); + } + } +} + +void func_80A0353C(EnElf* this, GlobalContext* globalCtx) { + Vec3f parentPos; + Actor* parent; + + SkelAnime_Update(&this->skelAnime); + func_80A02A20(this, globalCtx); + parent = this->actor.parent; + + if ((parent != NULL) && (parent->update != NULL)) { + parentPos = this->actor.parent->world.pos; + parentPos.y += ((1500.0f * this->actor.scale.y) + 40.0f); + func_80A02C98(this, &parentPos, 0.2f); + } else { + Actor_Kill(&this->actor); + } + + this->unk_2BC = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); +} + +void func_80A03604(EnElf* this, GlobalContext* globalCtx) { +} + +void func_80A03610(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToF(&this->unk_2B8, 30.0f, 0.1f, 4.0f, 1.0f); + + this->unk_28C.x = Math_CosS(this->unk_2AC) * this->unk_2B8; + this->unk_28C.y = this->unk_28C.y + this->unk_2B4; + + switch (this->unk_2AA) { + case 0: + if (this->unk_2B4 < 2.0f) { + this->unk_2B4 += 0.1f; + } else { + this->unk_2AA++; + } + break; + case 1: + if (this->unk_2B4 > -1.0f) { + this->unk_2B4 -= 0.2f; + } + } + + this->unk_28C.z = Math_SinS(this->unk_2AC) * -this->unk_2B8; + this->unk_2AC += this->unk_2B0; + func_80A02C98(this, &player->actor.world.pos, 0.2f); + + if (this->unk_2B4 < 0.0f) { + if ((this->unk_28C.y < 20.0f) && (this->unk_28C.y > 0.0f)) { + Actor_SetScale(&this->actor, (this->unk_28C.y * 0.008f) * 0.05f); + } + } + + if (this->unk_28C.y < -10.0f) { + Actor_Kill(&this->actor); + return; + } + + this->unk_2BC = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + EnElf_SpawnSparkles(this, globalCtx, 32); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FIATY_HEAL - SFX_FLAG); +} + +void func_80A03814(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_28C.y > 200.0f) { + Actor_Kill(&this->actor); + return; + } + + if (this->unk_2AE >= 0x7E) { + this->unk_2B8 += 0.1f; + this->unk_2B4 += 0.5f; + this->unk_28C.y += this->unk_2B4; + } else { + this->unk_2AE++; + + if (this->unk_2B8 < 30.0f) { + this->unk_2B8 += 0.5f; + } + + if (this->unk_28C.y > 0.0f) { + this->unk_28C.y -= 0.7f; + } + } + + this->unk_28C.x = Math_CosS(this->unk_2AC) * this->unk_2B8; + this->unk_28C.z = Math_SinS(this->unk_2AC) * -this->unk_2B8; + this->unk_2AC += this->unk_2B0; + func_80A02E30(this, &player->bodyPartsPos[0]); + this->unk_2BC = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + EnElf_SpawnSparkles(this, globalCtx, 32); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FIATY_HEAL - SFX_FLAG); +} + +void func_80A03990(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + + this->unk_28C.z = 0.0f; + this->unk_28C.x = 0.0f; + this->unk_28C.y += this->unk_2B4; + this->unk_2B4 -= 0.35f; + + if (this->unk_2B4 <= 0.0f) { + EnElf_SetupAction(this, func_80A03814); + this->unk_2B0 = 0x800; + this->unk_2AE = 0; + this->unk_2B4 = 0.0f; + this->unk_2B8 = 1.0f; + } + + func_80A02E30(this, &player->bodyPartsPos[0]); + Actor_SetScale(&this->actor, (1.0f - (SQ(this->unk_2B4) * SQ(1.0f / 9.0f))) * 0.008f); + this->unk_2BC = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + EnElf_SpawnSparkles(this, globalCtx, 32); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FIATY_HEAL - SFX_FLAG); +} + +void func_80A03AB0(EnElf* this, GlobalContext* globalCtx) { + if (this->fairyFlags & 4) { + func_80A04414(this, globalCtx); + } + + SkelAnime_Update(&this->skelAnime); + + if (this->func_2C8 == NULL) { + ASSERT(0, "0", "../z_en_elf.c", 1725); + } + + this->func_2C8(this, globalCtx); +} + +void EnElf_UpdateLights(EnElf* this, GlobalContext* globalCtx) { + s16 glowLightRadius; + Player* player; + + glowLightRadius = 100; + + if (this->unk_2A8 == 8) { + glowLightRadius = 0; + } + + if (this->fairyFlags & 0x20) { + player = GET_PLAYER(globalCtx); + Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, player->actor.world.pos.x, + (s16)(player->actor.world.pos.y) + 60.0f, player->actor.world.pos.z, 255, 255, 255, + 200); + } else { + Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, -1); + } + + Lights_PointGlowSetInfo(&this->lightInfoGlow, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, glowLightRadius); + + this->unk_2BC = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + + Actor_SetScale(&this->actor, this->actor.scale.x); +} + +void func_80A03CF8(EnElf* this, GlobalContext* globalCtx) { + Vec3f nextPos; + Vec3f prevPos; + Player* player = GET_PLAYER(globalCtx); + Actor* arrowPointedActor; + f32 xScale; + f32 distFromLinksHead; + + func_80A0461C(this, globalCtx); + func_80A03AB0(this, globalCtx); + + xScale = 0.0f; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[8] != NULL)) { + EnElf_GetCutsceneNextPos(&nextPos, globalCtx, 8); + + if (globalCtx->csCtx.npcActions[8]->action == 5) { + if (1) {} + EnElf_SpawnSparkles(this, globalCtx, 16); + } + + prevPos = this->actor.world.pos; + + if (this->unk_2A8 == 0xA) { + func_80A02EC0(this, &nextPos); + } else { + func_80A02C98(this, &nextPos, 0.2f); + } + + if ((globalCtx->sceneNum == SCENE_LINK_HOME) && (gSaveContext.sceneSetupIndex == 4)) { + // play dash sound as Navi enters Links house in the intro + if (1) {} + if (globalCtx->csCtx.frames == 55) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); + } + + // play dash sound in intervals as Navi is waking up Link in the intro + if (this->unk_2A8 == 6) { + if (this->fairyFlags & 0x40) { + if (prevPos.y < this->actor.world.pos.y) { + this->fairyFlags &= ~0x40; + } + } else { + if (this->actor.world.pos.y < prevPos.y) { + this->fairyFlags |= 0x40; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); + } + } + } + } + } else { + distFromLinksHead = Math_Vec3f_DistXYZ(&player->bodyPartsPos[8], &this->actor.world.pos); + + switch (this->unk_2A8) { + case 7: + func_80A02C98(this, &player->bodyPartsPos[8], 1.0f - this->unk_2AE * (1.0f / 30.0f)); + xScale = Math_Vec3f_DistXYZ(&player->bodyPartsPos[8], &this->actor.world.pos); + + if (distFromLinksHead < 7.0f) { + this->unk_2C0 = 0; + xScale = 0.0f; + } else if (distFromLinksHead < 25.0f) { + xScale = (xScale - 5.0f) * 0.05f; + xScale = 1.0f - xScale; + xScale = (1.0f - SQ(xScale)) * 0.008f; + } else { + xScale = 0.008f; + } + EnElf_SpawnSparkles(this, globalCtx, 16); + break; + case 8: + func_80A02C98(this, &player->bodyPartsPos[8], 0.2f); + this->actor.world.pos = player->bodyPartsPos[8]; + func_80A029A8(this, 1); + break; + case 11: + nextPos = player->bodyPartsPos[8]; + nextPos.y += 1500.0f * this->actor.scale.y; + func_80A02E30(this, &nextPos); + EnElf_SpawnSparkles(this, globalCtx, 16); + + if (this->unk_2B8 <= 19.0f) { + this->unk_2B8 += 1.0f; + } + + if (this->unk_2B8 >= 21.0f) { + this->unk_2B8 -= 1.0f; + } + + if (this->unk_2C0 < 0x20) { + this->unk_2B0 = (this->unk_2C0 * 0xF0) + 0x200; + func_80A0299C(this, 1); + } + break; + case 12: + nextPos = GET_ACTIVE_CAM(globalCtx)->eye; + nextPos.y += (-2000.0f * this->actor.scale.y); + func_80A03148(this, &nextPos, 0.0f, 20.0f, 0.2f); + break; + default: + func_80A029A8(this, 1); + nextPos = globalCtx->actorCtx.targetCtx.naviRefPos; + nextPos.y += (1500.0f * this->actor.scale.y); + arrowPointedActor = globalCtx->actorCtx.targetCtx.arrowPointedActor; + + if (arrowPointedActor != NULL) { + func_80A03148(this, &nextPos, 0.0f, 20.0f, 0.2f); + + if (this->actor.speedXZ >= 5.0f) { + EnElf_SpawnSparkles(this, globalCtx, 16); + } + } else { + if ((this->timer % 32) == 0) { + this->unk_2A0 = Rand_ZeroFloat(7.0f) + 3.0f; + } + + if (this->fairyFlags & 2) { + if (distFromLinksHead < 30.0f) { + this->fairyFlags ^= 2; + } + + func_80A03148(this, &nextPos, 0.0f, 20.0f, 0.2f); + EnElf_SpawnSparkles(this, globalCtx, 16); + } else { + if (distFromLinksHead > 100.0f) { + this->fairyFlags |= 2; + + if (this->unk_2C7 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); + } + + this->unk_2C0 = 0x64; + } + func_80A03148(this, &nextPos, 0.0f, this->unk_2A0, 0.2f); + } + } + break; + } + } + + if (this->unk_2A8 == 7) { + this->actor.scale.x = xScale; + } else if (this->unk_2A8 == 8) { + this->actor.scale.x = 0.0f; + } else { + Math_SmoothStepToF(&this->actor.scale.x, 0.008f, 0.3f, 0.00080000004f, 0.000080000005f); + } + + EnElf_UpdateLights(this, globalCtx); +} + +void EnElf_ChangeColor(Color_RGBAf* dest, Color_RGBAf* newColor, Color_RGBAf* curColor, f32 rate) { + Color_RGBAf rgbaDiff; + + rgbaDiff.r = (newColor->r - curColor->r); + rgbaDiff.g = (newColor->g - curColor->g); + rgbaDiff.b = (newColor->b - curColor->b); + rgbaDiff.a = (newColor->a - curColor->a); + + dest->r += (rgbaDiff.r * rate); + dest->g += (rgbaDiff.g * rate); + dest->b += (rgbaDiff.b * rate); + dest->a += (rgbaDiff.a * rate); +} + +void func_80A04414(EnElf* this, GlobalContext* globalCtx) { + Actor* arrowPointedActor = globalCtx->actorCtx.targetCtx.arrowPointedActor; + Player* player = GET_PLAYER(globalCtx); + f32 transitionRate; + u16 targetSound; + + if (globalCtx->actorCtx.targetCtx.unk_40 != 0.0f) { + this->unk_2C6 = 0; + this->unk_29C = 1.0f; + + if (this->unk_2C7 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); + } + + } else { + if (this->unk_2C6 == 0) { + if ((arrowPointedActor == NULL) || + (Math_Vec3f_DistXYZ(&this->actor.world.pos, &globalCtx->actorCtx.targetCtx.naviRefPos) < 50.0f)) { + this->unk_2C6 = 1; + } + } else if (this->unk_29C != 0.0f) { + if (Math_StepToF(&this->unk_29C, 0.0f, 0.25f) != 0) { + this->innerColor = globalCtx->actorCtx.targetCtx.naviInner; + this->outerColor = globalCtx->actorCtx.targetCtx.naviOuter; + } else { + transitionRate = 0.25f / this->unk_29C; + EnElf_ChangeColor(&this->innerColor, &globalCtx->actorCtx.targetCtx.naviInner, &this->innerColor, + transitionRate); + EnElf_ChangeColor(&this->outerColor, &globalCtx->actorCtx.targetCtx.naviOuter, &this->outerColor, + transitionRate); + } + } + } + + if (this->fairyFlags & 1) { + if ((arrowPointedActor == NULL) || (player->unk_664 == NULL)) { + this->fairyFlags ^= 1; + } + } else { + if ((arrowPointedActor != NULL) && (player->unk_664 != NULL)) { + if (arrowPointedActor->category == ACTORCAT_NPC) { + targetSound = NA_SE_VO_NAVY_HELLO; + } else { + targetSound = + (arrowPointedActor->category == ACTORCAT_ENEMY) ? NA_SE_VO_NAVY_ENEMY : NA_SE_VO_NAVY_HEAR; + } + + if (this->unk_2C7 == 0) { + Audio_PlayActorSound2(&this->actor, targetSound); + } + + this->fairyFlags |= 1; + } + } +} + +void func_80A0461C(EnElf* this, GlobalContext* globalCtx) { + s32 temp; + Actor* arrowPointedActor; + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[8] != NULL) { + switch (globalCtx->csCtx.npcActions[8]->action) { + case 4: + temp = 9; + break; + case 3: + temp = 6; + break; + case 1: + temp = 10; + break; + default: + temp = 0; + break; + } + } else { + temp = 0; + this->unk_2C0 = 100; + } + + } else { + arrowPointedActor = globalCtx->actorCtx.targetCtx.arrowPointedActor; + + if ((player->stateFlags1 & 0x400) || ((YREG(15) & 0x10) && func_800BC56C(globalCtx, 2))) { + temp = 12; + this->unk_2C0 = 100; + } else if (arrowPointedActor == NULL || arrowPointedActor->category == ACTORCAT_NPC) { + if (arrowPointedActor != NULL) { + this->unk_2C0 = 100; + player->stateFlags2 |= 0x100000; + temp = 0; + } else { + switch (this->unk_2A8) { + case 0: + if (this->unk_2C0 != 0) { + this->unk_2C0--; + temp = 0; + } else { + if (this->unk_2C7 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_NAVY_VANISH); + } + temp = 7; + } + break; + case 7: + if (this->unk_2C0 != 0) { + if (this->unk_2AE > 0) { + this->unk_2AE--; + temp = 7; + } else { + player->stateFlags2 |= 0x100000; + temp = 0; + } + } else { + temp = 8; + func_80A029A8(this, 10); + } + break; + case 8: + temp = 8; + break; + case 11: + temp = this->unk_2A8; + if (this->unk_2C0 > 0) { + this->unk_2C0--; + } else { + temp = 0; + } + break; + default: + temp = 0; + break; + } + } + } else { + temp = 1; + } + + switch (temp) { + case 0: + if (!(player->stateFlags2 & 0x100000)) { + temp = 7; + if (this->unk_2C7 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_NAVY_VANISH); + } + } + break; + case 8: + if (player->stateFlags2 & 0x100000) { + func_80A0299C(this, 0x32); + this->unk_2C0 = 42; + temp = 11; + if (this->unk_2C7 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); + } + } + break; + case 7: + player->stateFlags2 &= ~0x100000; + break; + default: + player->stateFlags2 |= 0x100000; + break; + } + } + + if (temp != this->unk_2A8) { + func_80A01C38(this, temp); + + if (temp == 11) { + this->unk_2B8 = Math_Vec3f_DistXZ(&player->bodyPartsPos[8], &this->actor.world.pos); + this->unk_2AC = Math_Vec3f_Yaw(&this->actor.world.pos, &player->bodyPartsPos[8]); + } + } +} + +void EnElf_SpawnSparkles(EnElf* this, GlobalContext* globalCtx, s32 sparkleLife) { + static Vec3f sparkleVelocity = { 0.0f, -0.05f, 0.0f }; + static Vec3f sparkleAccel = { 0.0f, -0.025f, 0.0f }; + s32 pad; + Vec3f sparklePos; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + + sparklePos.x = Rand_CenteredFloat(6.0f) + this->actor.world.pos.x; + sparklePos.y = (Rand_ZeroOne() * 6.0f) + this->actor.world.pos.y; + sparklePos.z = Rand_CenteredFloat(6.0f) + this->actor.world.pos.z; + + primColor.r = this->innerColor.r; + primColor.g = this->innerColor.g; + primColor.b = this->innerColor.b; + + envColor.r = this->outerColor.r; + envColor.g = this->outerColor.g; + envColor.b = this->outerColor.b; + + EffectSsKiraKira_SpawnDispersed(globalCtx, &sparklePos, &sparkleVelocity, &sparkleAccel, &primColor, &envColor, + 1000, sparkleLife); +} + +void func_80A04D90(EnElf* this, GlobalContext* globalCtx) { + s32 pad; + s32 bgId; + + this->actor.floorHeight = BgCheck_EntityRaycastFloor5(globalCtx, &globalCtx->colCtx, &this->actor.floorPoly, &bgId, + &this->actor, &this->actor.world.pos); + this->actor.shape.shadowAlpha = 0x32; +} + +// move to talk to player +void func_80A04DE4(EnElf* this, GlobalContext* globalCtx) { + Vec3f headCopy; + Player* player = GET_PLAYER(globalCtx); + Vec3f naviRefPos; + + if (this->fairyFlags & 0x10) { + naviRefPos = globalCtx->actorCtx.targetCtx.naviRefPos; + + if ((player->unk_664 == NULL) || (&player->actor == player->unk_664) || (&this->actor == player->unk_664)) { + naviRefPos.x = player->bodyPartsPos[7].x + (Math_SinS(player->actor.shape.rot.y) * 20.0f); + naviRefPos.y = player->bodyPartsPos[7].y + 5.0f; + naviRefPos.z = player->bodyPartsPos[7].z + (Math_CosS(player->actor.shape.rot.y) * 20.0f); + } + + this->actor.focus.pos = naviRefPos; + this->fairyFlags &= ~0x10; + } + + func_80A03AB0(this, globalCtx); + headCopy = this->actor.focus.pos; + + func_80A03148(this, &headCopy, 0, 20.0f, 0.2f); + + if (this->actor.speedXZ >= 5.0f) { + EnElf_SpawnSparkles(this, globalCtx, 16); + } + + Math_SmoothStepToF(&this->actor.scale.x, 0.008f, 0.3f, 0.00080000004f, 0.000080000005f); + EnElf_UpdateLights(this, globalCtx); +} + +// move after talking to player +void func_80A04F94(EnElf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->unk_2BC, 5, 0x1000, 0x400); + this->timer++; + Math_StepToF(&this->unk_2A4, 1.0f, 0.05f); + Environment_AdjustLights(globalCtx, SQ(this->unk_2A4), player->actor.projectedPos.z + 780.0f, 0.2f, 0.5f); +} + +// ask to talk to saria again +void func_80A05040(Actor* thisx, GlobalContext* globalCtx) { + EnElf* this = (EnElf*)thisx; + + func_80A04DE4(this, globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + Message_ContinueTextbox(globalCtx, ElfMessage_GetSariaText(globalCtx)); + this->actor.update = func_80A05114; + break; + case 1: // no + Message_CloseTextbox(globalCtx); + this->actor.update = func_80A053F0; + func_80A01C38(this, 0); + this->fairyFlags &= ~0x20; + break; + } + } + + func_80A04F94(this, globalCtx); +} + +void func_80A05114(Actor* thisx, GlobalContext* globalCtx) { + EnElf* this = (EnElf*)thisx; + + func_80A04DE4(this, globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0xE3); + this->actor.update = func_80A05040; + } + + func_80A04F94(this, globalCtx); +} + +void func_80A05188(Actor* thisx, GlobalContext* globalCtx) { + EnElf* this = (EnElf*)thisx; + + func_80A04DE4(this, globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, ElfMessage_GetSariaText(globalCtx)); + this->actor.update = func_80A05114; + } + + func_80A04F94(this, globalCtx); +} + +// ask to talk to navi +void func_80A05208(Actor* thisx, GlobalContext* globalCtx) { + s32 naviCUpText; + EnElf* this = (EnElf*)thisx; + + func_80A04DE4(this, globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + naviCUpText = ElfMessage_GetCUpText(globalCtx); + + if (naviCUpText != 0) { + Message_ContinueTextbox(globalCtx, naviCUpText); + } else { + Message_ContinueTextbox(globalCtx, 0x15F); + } + + this->actor.update = func_80A052F4; + break; + case 1: // no + Message_CloseTextbox(globalCtx); + this->actor.update = func_80A053F0; + func_80A01C38(this, 0); + this->fairyFlags &= ~0x20; + break; + } + } + + func_80A04F94(this, globalCtx); +} + +// ask to talk to saria +void func_80A052F4(Actor* thisx, GlobalContext* globalCtx) { + EnElf* this = (EnElf*)thisx; + + func_80A04DE4(this, globalCtx); + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) { + if (Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.unk_E3F2 = 0xFF; + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + this->actor.update = func_80A05188; + Message_ContinueTextbox(globalCtx, 0xE2); + break; + case 1: // no + this->actor.update = func_80A05208; + Message_ContinueTextbox(globalCtx, 0xE1); + break; + } + } + } else if (Actor_TextboxIsClosing(thisx, globalCtx)) { + this->actor.update = func_80A053F0; + func_80A01C38(this, 0); + this->fairyFlags &= ~0x20; + } + + func_80A04F94(this, globalCtx); +} + +void func_80A053F0(Actor* thisx, GlobalContext* globalCtx) { + u8 unk2C7; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + EnElf* this = (EnElf*)thisx; + + if (player->naviTextId == 0) { + if (player->unk_664 == NULL) { + if (((gSaveContext.naviTimer >= 600) && (gSaveContext.naviTimer <= 3000)) || (nREG(89) != 0)) { + player->naviTextId = ElfMessage_GetCUpText(globalCtx); + + if (player->naviTextId == 0x15F) { + player->naviTextId = 0; + } + } + } + } else if (player->naviTextId < 0) { + // trigger dialog instantly for negative message IDs + thisx->flags |= ACTOR_FLAG_16; + } + + if (Actor_ProcessTalkRequest(thisx, globalCtx)) { + func_800F4524(&D_801333D4, NA_SE_VO_SK_LAUGH, 0x20); + thisx->focus.pos = thisx->world.pos; + + if (thisx->textId == ElfMessage_GetCUpText(globalCtx)) { + this->fairyFlags |= 0x80; + gSaveContext.naviTimer = 3001; + } + + this->fairyFlags |= 0x10; + this->fairyFlags |= 0x20; + thisx->update = func_80A052F4; + func_80A01C38(this, 3); + + if (this->elfMsg != NULL) { + this->elfMsg->actor.flags |= ACTOR_FLAG_8; + } + + thisx->flags &= ~ACTOR_FLAG_16; + } else { + this->actionFunc(this, globalCtx); + thisx->shape.rot.y = this->unk_2BC; + nREG(80) = gSaveContext.sceneFlags[127].chest; + + if (nREG(81) != 0) { + if (gSaveContext.sceneFlags[127].chest) { + LOG_NUM("z_common_data.memory.information.room_inf[127][ 0 ]", gSaveContext.sceneFlags[127].chest, + "../z_en_elf.c", 2595); + } + } + + if (!Gameplay_InCsMode(globalCtx)) { + if (gSaveContext.naviTimer < 25800) { + gSaveContext.naviTimer++; + } else if (!(this->fairyFlags & 0x80)) { + gSaveContext.naviTimer = 0; + } + } + } + + this->elfMsg = NULL; + this->timer++; + + if (this->unk_2A4 > 0.0f) { + Math_StepToF(&this->unk_2A4, 0.0f, 0.05f); + Environment_AdjustLights(globalCtx, SQ(this->unk_2A4) * this->unk_2A4, player->actor.projectedPos.z + 780.0f, + 0.2f, 0.5f); + } + + // temp probably fake match + unk2C7 = this->unk_2C7; + if (unk2C7 > 0) { + this->unk_2C7--; + } + + if ((this->unk_2C7 == 0) && (globalCtx->csCtx.state != CS_STATE_IDLE)) { + this->unk_2C7 = 1; + } + + func_80A04D90(this, globalCtx); +} + +void EnElf_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnElf* this = (EnElf*)thisx; + + this->actionFunc(this, globalCtx); + this->actor.shape.rot.y = this->unk_2BC; + this->timer++; + + if (this->fairyFlags & FAIRY_FLAG_BIG) { + func_80A04D90(this, globalCtx); + } +} + +s32 EnElf_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 pad; + f32 scale; + Vec3f mtxMult; + EnElf* this = (EnElf*)thisx; + + if (limbIndex == 8) { + scale = ((Math_SinS(this->timer * 4096) * 0.1f) + 1.0f) * 0.012f; + + if (this->fairyFlags & FAIRY_FLAG_BIG) { + scale *= 2.0f; + } + + scale *= (this->actor.scale.x * 124.99999f); + Matrix_MultVec3f(&zeroVec, &mtxMult); + Matrix_Translate(mtxMult.x, mtxMult.y, mtxMult.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + } + + // do not draw wings for big fairies + if (this->fairyFlags & FAIRY_FLAG_BIG) { + if (limbIndex == 4 || limbIndex == 7 || limbIndex == 11 || limbIndex == 14) { + *dList = NULL; + } + } + + return false; +} + +void EnElf_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + f32 alphaScale; + s32 envAlpha; + EnElf* this = (EnElf*)thisx; + s32 pad1; + Gfx* dListHead; + Player* player = GET_PLAYER(globalCtx); + + if ((this->unk_2A8 != 8) && !(this->fairyFlags & 8)) { + if (!(player->stateFlags1 & 0x100000) || (kREG(90) < this->actor.projectedPos.z)) { + dListHead = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Gfx) * 4); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_elf.c", 2730); + + func_80094B58(globalCtx->state.gfxCtx); + + envAlpha = (this->timer * 50) & 0x1FF; + envAlpha = (envAlpha > 255) ? 511 - envAlpha : envAlpha; + + alphaScale = this->disappearTimer < 0 ? (this->disappearTimer * (7.0f / 6000.0f)) + 1.0f : 1.0f; + + gSPSegment(POLY_XLU_DISP++, 0x08, dListHead); + gDPPipeSync(dListHead++); + gDPSetPrimColor(dListHead++, 0, 0x01, (u8)this->innerColor.r, (u8)this->innerColor.g, + (u8)this->innerColor.b, (u8)(this->innerColor.a * alphaScale)); + + if (this->fairyFlags & 4) { + gDPSetRenderMode(dListHead++, G_RM_PASS, G_RM_CLD_SURF2); + } else { + gDPSetRenderMode(dListHead++, G_RM_PASS, G_RM_ZB_CLD_SURF2); + } + + gSPEndDisplayList(dListHead++); + gDPSetEnvColor(POLY_XLU_DISP++, (u8)this->outerColor.r, (u8)this->outerColor.g, (u8)this->outerColor.b, + (u8)(envAlpha * alphaScale)); + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnElf_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_elf.c", 2793); + } + } +} + +void EnElf_GetCutsceneNextPos(Vec3f* vec, GlobalContext* globalCtx, s32 action) { + Vec3f startPos; + Vec3f endPos; + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[action]; + f32 lerp; + + startPos.x = npcAction->startPos.x; + startPos.y = npcAction->startPos.y; + startPos.z = npcAction->startPos.z; + + endPos.x = npcAction->endPos.x; + endPos.y = npcAction->endPos.y; + endPos.z = npcAction->endPos.z; + + lerp = Environment_LerpWeight(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames); + + vec->x = ((endPos.x - startPos.x) * lerp) + startPos.x; + vec->y = ((endPos.y - startPos.y) * lerp) + startPos.y; + vec->z = ((endPos.z - startPos.z) * lerp) + startPos.z; +} diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.h b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.h new file mode 100644 index 000000000..1aaa029c7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.h @@ -0,0 +1,58 @@ +#ifndef Z_EN_ELF_H +#define Z_EN_ELF_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_Elf_Msg/z_elf_msg.h" + +struct EnElf; + +typedef void (*EnElfActionFunc)(struct EnElf*, GlobalContext*); +typedef void (*EnElfUnkFunc)(struct EnElf*, GlobalContext*); + +typedef struct EnElf { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[15]; + /* 0x01EA */ Vec3s morphTable[15]; + /* 0x0244 */ Color_RGBAf innerColor; + /* 0x0254 */ Color_RGBAf outerColor; + /* 0x0264 */ LightInfo lightInfoGlow; + /* 0x0274 */ LightNode* lightNodeGlow; + /* 0x0278 */ LightInfo lightInfoNoGlow; + /* 0x0288 */ LightNode* lightNodeNoGlow; + /* 0x028C */ Vec3f unk_28C; + /* 0x0298 */ ElfMsg* elfMsg; + /* 0x029C */ f32 unk_29C; + /* 0x02A0 */ f32 unk_2A0; + /* 0x02A4 */ f32 unk_2A4; + /* 0x02A8 */ s16 unk_2A8; + /* 0x02AA */ s16 unk_2AA; + /* 0x02AC */ s16 unk_2AC; + /* 0x02AE */ s16 unk_2AE; + /* 0x02B0 */ s16 unk_2B0; + /* 0x02B4 */ f32 unk_2B4; + /* 0x02B8 */ f32 unk_2B8; + /* 0x02BC */ s16 unk_2BC; + /* 0x02BE */ u16 timer; + /* 0x02C0 */ s16 unk_2C0; + /* 0x02C2 */ s16 disappearTimer; + /* 0x02C4 */ u16 fairyFlags; + /* 0x02C6 */ u8 unk_2C6; + /* 0x02C7 */ u8 unk_2C7; + /* 0x02C8 */ EnElfUnkFunc func_2C8; + /* 0x02CC */ EnElfActionFunc actionFunc; +} EnElf; // size = 0x02D0 + +typedef enum { + /* 0x00 */ FAIRY_NAVI, + /* 0x01 */ FAIRY_REVIVE_BOTTLE, + /* 0x02 */ FAIRY_HEAL_TIMED, + /* 0x03 */ FAIRY_KOKIRI, + /* 0x04 */ FAIRY_SPAWNER, + /* 0x05 */ FAIRY_REVIVE_DEATH, + /* 0x06 */ FAIRY_HEAL, + /* 0x07 */ FAIRY_HEAL_BIG +} FairyType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.c b/soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.c new file mode 100644 index 000000000..c86ec3125 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.c @@ -0,0 +1,339 @@ +#include "z_en_encount1.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Tite/z_en_tite.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_27) + +void EnEncount1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnEncount1_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnEncount1_SpawnLeevers(EnEncount1* this, GlobalContext* globalCtx); +void EnEncount1_SpawnTektites(EnEncount1* this, GlobalContext* globalCtx); +void EnEncount1_SpawnStalchildOrWolfos(EnEncount1* this, GlobalContext* globalCtx); + +static s16 sLeeverAngles[] = { 0x0000, 0x2710, 0x7148, 0x8EB8, 0xD8F0 }; +static f32 sLeeverDists[] = { 200.0f, 170.0f, 120.0f, 120.0f, 170.0f }; + +const ActorInit En_Encount1_InitVars = { + ACTOR_EN_ENCOUNT1, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnEncount1), + (ActorFunc)EnEncount1_Init, + NULL, + (ActorFunc)EnEncount1_Update, + NULL, + NULL, +}; + +void EnEncount1_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnEncount1* this = (EnEncount1*)thisx; + f32 spawnRange; + + if (this->actor.params <= 0) { + osSyncPrintf("\n\n"); + // "Input error death!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 入力エラーデッス! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 入力エラーデッス! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 入力エラーデッス! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 入力エラーデッス! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 入力エラーデッス! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf("\n\n"); + Actor_Kill(&this->actor); + return; + } + + this->spawnType = (this->actor.params >> 0xB) & 0x1F; + this->maxCurSpawns = (this->actor.params >> 6) & 0x1F; + this->maxTotalSpawns = this->actor.params & 0x3F; + this->curNumSpawn = this->totalNumSpawn = 0; + spawnRange = 120.0f + (40.0f * this->actor.world.rot.z); + this->spawnRange = spawnRange; + + osSyncPrintf("\n\n"); + // "It's an enemy spawner!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 敵発生ゾーンでた! ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + // "Type" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 種類\t\t ☆☆☆☆☆ %d\n" VT_RST, this->spawnType); + // "Maximum number of simultaneous spawns" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 最大同時発生数 ☆☆☆☆☆ %d\n" VT_RST, this->maxCurSpawns); + // "Maximum number of spawns" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 最大発生数 \t ☆☆☆☆☆ %d\n" VT_RST, this->maxTotalSpawns); + // "Spawn check range" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生チェック範囲 ☆☆☆☆☆ %f\n" VT_RST, this->spawnRange); + osSyncPrintf("\n\n"); + + this->actor.flags &= ~ACTOR_FLAG_0; + switch (this->spawnType) { + case SPAWNER_LEEVER: + this->timer = 30; + this->maxCurSpawns = 5; + if (globalCtx->sceneNum == SCENE_SPOT13) { // Haunted Wasteland + this->reduceLeevers = true; + this->maxCurSpawns = 3; + } + this->updateFunc = EnEncount1_SpawnLeevers; + break; + case SPAWNER_TEKTITE: + this->maxCurSpawns = 2; + this->updateFunc = EnEncount1_SpawnTektites; + break; + case SPAWNER_STALCHILDREN: + case SPAWNER_WOLFOS: + if (globalCtx->sceneNum == SCENE_SPOT00) { // Hyrule Field + this->maxTotalSpawns = 10000; + } + this->updateFunc = EnEncount1_SpawnStalchildOrWolfos; + break; + } +} + +void EnEncount1_SpawnLeevers(EnEncount1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 floorType; + f32 spawnDist; + s32 spawnParams; + s16 spawnLimit; + s16 spawnAngle; + Vec3f spawnPos; + CollisionPoly* floorPoly; + s32 bgId; + f32 floorY; + EnReeba* leever; + + this->outOfRangeTimer = 0; + spawnPos = this->actor.world.pos; + + if ((this->timer == 0) && (globalCtx->csCtx.state == CS_STATE_IDLE) && (this->curNumSpawn <= this->maxCurSpawns) && + (this->curNumSpawn < 5)) { + floorType = func_80041D4C(&globalCtx->colCtx, player->actor.floorPoly, player->actor.floorBgId); + if ((floorType != 4) && (floorType != 7) && (floorType != 12)) { + this->numLeeverSpawns = 0; + } else if (!(this->reduceLeevers && (this->actor.xzDistToPlayer > 1300.0f))) { + spawnLimit = 5; + if (this->reduceLeevers) { + spawnLimit = 3; + } + while ((this->curNumSpawn < this->maxCurSpawns) && (this->curNumSpawn < spawnLimit) && (this->timer == 0)) { + spawnDist = sLeeverDists[this->leeverIndex]; + spawnAngle = sLeeverAngles[this->leeverIndex] + player->actor.shape.rot.y; + spawnParams = LEEVER_SMALL; + + if ((this->killCount >= 10) && (this->bigLeever == NULL)) { + this->killCount = this->numLeeverSpawns = 0; + spawnAngle = sLeeverAngles[0]; + spawnDist = sLeeverDists[2]; + spawnParams = LEEVER_BIG; + } + + spawnPos.x = player->actor.world.pos.x + Math_SinS(spawnAngle) * spawnDist; + spawnPos.y = player->actor.floorHeight + 120.0f; + spawnPos.z = player->actor.world.pos.z + Math_CosS(spawnAngle) * spawnDist; + + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &floorPoly, &bgId, &this->actor, &spawnPos); + if (floorY <= BGCHECK_Y_MIN) { + break; + } + spawnPos.y = floorY; + + leever = (EnReeba*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_REEBA, + spawnPos.x, spawnPos.y, spawnPos.z, 0, 0, 0, spawnParams); + + if (1) {} + if (1) {} + if (leever != NULL) { + this->curNumSpawn++; + leever->unk_280 = this->leeverIndex++; + if (this->leeverIndex >= 5) { + this->leeverIndex = 0; + } + this->numLeeverSpawns++; + if (this->numLeeverSpawns >= 12) { + this->timer = 150; + this->numLeeverSpawns = 0; + } + if (spawnParams != LEEVER_SMALL) { + this->timer = 300; + this->bigLeever = leever; + } + if (!this->reduceLeevers) { + this->maxCurSpawns = (s16)Rand_ZeroFloat(3.99f) + 2; + } else { + this->maxCurSpawns = (s16)Rand_ZeroFloat(2.99f) + 1; + } + } else { + // "Cannot spawn!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + break; + } + } + } + } +} + +void EnEncount1_SpawnTektites(EnEncount1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 bgId; + CollisionPoly* floorPoly; + Vec3f spawnPos; + f32 floorY; + + if (this->timer == 0) { + this->timer = 10; + if ((fabsf(player->actor.world.pos.y - this->actor.world.pos.y) > 100.0f) || + (this->actor.xzDistToPlayer > this->spawnRange)) { + this->outOfRangeTimer++; + } else { + this->outOfRangeTimer = 0; + if ((this->curNumSpawn < this->maxCurSpawns) && (this->totalNumSpawn < this->maxTotalSpawns)) { + spawnPos.x = this->actor.world.pos.x + Rand_CenteredFloat(50.0f); + spawnPos.y = this->actor.world.pos.y + 120.0f; + spawnPos.z = this->actor.world.pos.z + Rand_CenteredFloat(50.0f); + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &floorPoly, &bgId, &this->actor, &spawnPos); + if (floorY <= BGCHECK_Y_MIN) { + return; + } + spawnPos.y = floorY; + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_TITE, spawnPos.x, + spawnPos.y, spawnPos.z, 0, 0, 0, TEKTITE_RED) != NULL) { // Red tektite + this->curNumSpawn++; + this->totalNumSpawn++; + } else { + // "Cannot spawn!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + } + } + } + } +} + +void EnEncount1_SpawnStalchildOrWolfos(EnEncount1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 spawnDist; + s16 spawnAngle; + s16 spawnId; + s16 spawnParams; + s16 kcOver10; + s16 tempmod; + Vec3f spawnPos; + CollisionPoly* floorPoly; + s32 bgId; + f32 floorY; + + if (globalCtx->sceneNum != SCENE_SPOT00) { + if ((fabsf(player->actor.world.pos.y - this->actor.world.pos.y) > 100.0f) || + (this->actor.xzDistToPlayer > this->spawnRange)) { + this->outOfRangeTimer++; + return; + } + } else if (IS_DAY || (Player_GetMask(globalCtx) == PLAYER_MASK_BUNNY)) { + this->killCount = 0; + return; + } + + this->outOfRangeTimer = 0; + spawnPos = this->actor.world.pos; + if ((this->curNumSpawn < this->maxCurSpawns) && (this->totalNumSpawn < this->maxTotalSpawns)) { + while ((this->curNumSpawn < this->maxCurSpawns) && (this->totalNumSpawn < this->maxTotalSpawns)) { + if (globalCtx->sceneNum == SCENE_SPOT00) { + if ((player->unk_89E == 0) || (player->actor.floorBgId != BGCHECK_SCENE) || + !(player->actor.bgCheckFlags & 1) || (player->stateFlags1 & 0x08000000)) { + + this->fieldSpawnTimer = 60; + break; + } + if (this->fieldSpawnTimer == 60) { + this->maxCurSpawns = 2; + } + if (this->fieldSpawnTimer != 0) { + this->fieldSpawnTimer--; + break; + } + + spawnDist = Rand_CenteredFloat(40.0f) + 200.0f; + spawnAngle = player->actor.shape.rot.y; + if (this->curNumSpawn != 0) { + spawnAngle = -spawnAngle; + spawnDist = Rand_CenteredFloat(40.0f) + 100.0f; + } + spawnPos.x = + player->actor.world.pos.x + (Math_SinS(spawnAngle) * spawnDist) + Rand_CenteredFloat(40.0f); + spawnPos.y = player->actor.floorHeight + 120.0f; + spawnPos.z = + player->actor.world.pos.z + (Math_CosS(spawnAngle) * spawnDist) + Rand_CenteredFloat(40.0f); + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &floorPoly, &bgId, &this->actor, &spawnPos); + if (floorY <= BGCHECK_Y_MIN) { + break; + } + if ((player->actor.yDistToWater != BGCHECK_Y_MIN) && + (floorY < (player->actor.world.pos.y - player->actor.yDistToWater))) { + break; + } + spawnPos.y = floorY; + } + if (this->spawnType == SPAWNER_WOLFOS) { + spawnId = ACTOR_EN_WF; + spawnParams = (0xFF << 8) | 0x00; + } else { + spawnId = ACTOR_EN_SKB; + spawnParams = 0; + + kcOver10 = this->killCount / 10; + if (kcOver10 > 0) { + tempmod = this->killCount % 10; + if (tempmod == 0) { + spawnParams = kcOver10 * 5; + } + } + this->killCount++; + } + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, spawnId, spawnPos.x, spawnPos.y, + spawnPos.z, 0, 0, 0, spawnParams) != NULL) { + this->curNumSpawn++; + if (this->curNumSpawn >= this->maxCurSpawns) { + this->fieldSpawnTimer = 100; + } + if (globalCtx->sceneNum != SCENE_SPOT00) { + this->totalNumSpawn++; + } + } else { + // "Cannot spawn!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + break; + } + } + } +} + +void EnEncount1_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnEncount1* this = (EnEncount1*)thisx; + + if (this->timer != 0) { + this->timer--; + } + + this->updateFunc(this, globalCtx); + + if (BREG(0) != 0) { + if (this->outOfRangeTimer != 0) { + if ((this->outOfRangeTimer & 1) == 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 120, 120, 120, 255, 4, globalCtx->state.gfxCtx); + } + } else { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 255, 0, 255, 255, 4, globalCtx->state.gfxCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.h b/soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.h new file mode 100644 index 000000000..2df4b11b4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.h @@ -0,0 +1,40 @@ +#ifndef Z_EN_ENCOUNT1_H +#define Z_EN_ENCOUNT1_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Reeba/z_en_reeba.h" + +#define SPAWNER_PARAMS(type, number, total) ((type << 0xB) | (number << 0x6) | total) + +struct EnEncount1; + +typedef void (*EnEncount1UpdateFunc)(struct EnEncount1*, GlobalContext*); + +typedef struct EnEncount1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnEncount1UpdateFunc updateFunc; + /* 0x0150 */ s16 maxCurSpawns; + /* 0x0152 */ s16 curNumSpawn; + /* 0x0154 */ s16 spawnType; + /* 0x0156 */ s16 maxTotalSpawns; + /* 0x0158 */ s16 totalNumSpawn; + /* 0x015A */ s16 outOfRangeTimer; + /* 0x015C */ s16 fieldSpawnTimer; + /* 0x015E */ s16 killCount; + /* 0x0160 */ s16 numLeeverSpawns; + /* 0x0162 */ s16 leeverIndex; + /* 0x0164 */ s16 timer; + /* 0x0166 */ u8 reduceLeevers; + /* 0x0168 */ f32 spawnRange; + /* 0x016C */ EnReeba* bigLeever; +} EnEncount1; // size = 0x0170 + +typedef enum { + /* 0 */ SPAWNER_LEEVER, + /* 1 */ SPAWNER_TEKTITE, + /* 2 */ SPAWNER_STALCHILDREN, + /* 3 */ SPAWNER_WOLFOS +} EnEncount1type; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.c b/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.c new file mode 100644 index 000000000..4d946508e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.c @@ -0,0 +1,373 @@ +#include "z_en_encount2.h" +#include "overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.h" +#include "vt.h" +#include "objects/object_efc_star_field/object_efc_star_field.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0x0 */ ENCOUNT2_INACTIVE, + /* 0x1 */ ENCOUNT2_ACTIVE_DEATH_MOUNTAIN, + /* 0x2 */ ENCOUNT2_ACTIVE_GANONS_TOWER +} Encount2State; + +void EnEncount2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnEncount2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnEncount2_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnEncount2_Wait(EnEncount2* this, GlobalContext* globalCtx); +void EnEncount2_SpawnRocks(EnEncount2* this, GlobalContext* globalCtx); + +void EnEncount2_ParticleInit(EnEncount2* this, Vec3f* particlePos, f32 scale); +void EnEncount2_ParticleDraw(Actor* thisx, GlobalContext* globalCtx); +void EnEncount2_ParticleUpdate(EnEncount2* this, GlobalContext* globalCtx); + +const ActorInit En_Encount2_InitVars = { + ACTOR_EN_ENCOUNT2, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_EFC_STAR_FIELD, + sizeof(EnEncount2), + (ActorFunc)EnEncount2_Init, + NULL, + (ActorFunc)EnEncount2_Update, + (ActorFunc)EnEncount2_Draw, + NULL, +}; + +void EnEncount2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnEncount2* this = (EnEncount2*)thisx; + + if (globalCtx->sceneNum != SCENE_SPOT16) { + this->isNotDeathMountain = true; + } + + if (!this->isNotDeathMountain) { + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Death Mountain Encount2 set ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ デスマウンテンエンカウント2セットされました ☆☆☆☆☆ %d\n" VT_RST, + this->actor.params); + if (LINK_IS_ADULT && (gSaveContext.eventChkInf[4] & 0x200)) { // flag for having used fire temple blue warp + Actor_Kill(thisx); + } + } else { + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Ganon Tower Escape Encount2 set ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ガノンタワー脱出エンカウント2セットされました ☆☆☆☆☆ %d\n" VT_RST, + this->actor.params); + } + + this->actionFunc = EnEncount2_Wait; +} + +void EnEncount2_Wait(EnEncount2* this, GlobalContext* globalCtx) { + s32 pad; + s16 quakeIndex; + s16 spawnerState; + Player* player = GET_PLAYER(globalCtx); + + spawnerState = ENCOUNT2_INACTIVE; + if (!this->isNotDeathMountain) { + if ((player->actor.world.pos.y > 1500.0f) && (player->actor.world.pos.x > -700.0f) && + (player->actor.world.pos.x < 100.0f) && (player->actor.world.pos.z < -1290.0f) && + (player->actor.world.pos.z > -3600.0f)) { + spawnerState = ENCOUNT2_ACTIVE_DEATH_MOUNTAIN; + } + } else if ((this->actor.xzDistToPlayer < 700.0f) && (Flags_GetSwitch(globalCtx, 0x37))) { + s16 scene = globalCtx->sceneNum; + + if (((scene == SCENE_GANON_DEMO) || (scene == SCENE_GANON_FINAL) || (scene == SCENE_GANON_SONOGO) || + (scene == SCENE_GANONTIKA_SONOGO)) && + (!this->collapseSpawnerInactive)) { + spawnerState = ENCOUNT2_ACTIVE_GANONS_TOWER; + } + } + + switch (spawnerState) { + case ENCOUNT2_INACTIVE: + this->isQuaking = false; + this->envEffectsTimer--; + if (this->envEffectsTimer <= 0) { + this->envEffectsTimer = 0; + } + break; + case ENCOUNT2_ACTIVE_DEATH_MOUNTAIN: + if ((this->deathMountainSpawnerTimer == 1) || (!this->isQuaking)) { + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 1); + Quake_SetSpeed(quakeIndex, 0x7FFF); + Quake_SetQuakeValues(quakeIndex, 50, 0, 0, 0); + Quake_SetCountdown(quakeIndex, 300); + this->isQuaking = true; + } + case ENCOUNT2_ACTIVE_GANONS_TOWER: + this->envEffectsTimer++; + if (this->envEffectsTimer > 60) { + this->envEffectsTimer = 60; + } + if (this->deathMountainSpawnerTimer == 0) { + this->deathMountainSpawnerTimer = 200; + this->numSpawnedRocks = 0; + this->actionFunc = EnEncount2_SpawnRocks; + } + break; + } + return; +} + +void EnEncount2_SpawnRocks(EnEncount2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnFireRock* spawnedRock; + f32 tempVec1X; + f32 tempVec1Y; + f32 tempVec1Z; + f32 magnitude; + f32 tempVec2X; + f32 tempVec2Y; + f32 tempVec2Z; + f32 particleScale; + Vec3f particlePos; + s16 spawnedRockType; + s16 spawnerState; + s16 maxRocks; + + this->envEffectsTimer++; + + if (this->envEffectsTimer > 60) { + this->envEffectsTimer = 60; + } + + spawnerState = ENCOUNT2_INACTIVE; + + if (!this->isNotDeathMountain) { + if (this->deathMountainSpawnerTimer == 0) { + this->deathMountainSpawnerTimer = 100; + this->actionFunc = EnEncount2_Wait; + return; + } + + if ((player->actor.world.pos.y > 1500.0f) && (player->actor.world.pos.x > -700.0f) && + (player->actor.world.pos.x < 100.0f) && (player->actor.world.pos.z < -1290.0f) && + (player->actor.world.pos.z > -3860.0f)) { + maxRocks = 2; + spawnerState = ENCOUNT2_ACTIVE_DEATH_MOUNTAIN; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_VOLCANO - SFX_FLAG); + } else if ((this->actor.xzDistToPlayer < 700.0f) && (Flags_GetSwitch(globalCtx, 0x37) != 0)) { + s16 scene = globalCtx->sceneNum; + + if (((scene == SCENE_GANON_DEMO) || (scene == SCENE_GANON_FINAL) || (scene == SCENE_GANON_SONOGO) || + (scene == SCENE_GANONTIKA_SONOGO)) && + (!this->collapseSpawnerInactive)) { + maxRocks = 1; + spawnerState = ENCOUNT2_ACTIVE_GANONS_TOWER; + } + } + if (spawnerState != ENCOUNT2_INACTIVE) { + // Direction vector for the direction the camera is facing + tempVec1X = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + tempVec1Y = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + tempVec1Z = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + + // Normalised direction vector for the direction the camera is facing + magnitude = sqrtf(SQ(tempVec1X) + SQ(tempVec1Y) + SQ(tempVec1Z)); + tempVec2X = tempVec1X / magnitude; + tempVec2Y = tempVec1Y / magnitude; + tempVec2Z = tempVec1Z / magnitude; + + // Position between 160 and 300 units ahead of camera depending on camera pitch, plus a 400 unit offset in +y + tempVec1X = globalCtx->view.eye.x + (tempVec2X * 300.0f); + tempVec1Y = globalCtx->view.eye.y + (tempVec2Y * 160.0f) + 400.0f; + tempVec1Z = globalCtx->view.eye.z + (tempVec2Z * 300.0f); + + // Position between 160 and 200 units ahead of camera depending on camera pitch, plus a 400 unit offset in +y + // (plus some random variation) + particlePos.x = Rand_CenteredFloat(200.0f) + (globalCtx->view.eye.x + (tempVec2X * 200.0f)); + particlePos.y = Rand_CenteredFloat(50.0f) + tempVec1Y; + particlePos.z = Rand_CenteredFloat(200.0f) + (globalCtx->view.eye.z + (tempVec2Z * 200.0f)); + particleScale = Rand_CenteredFloat(0.005f) + 0.007f; + + if (spawnerState == ENCOUNT2_ACTIVE_DEATH_MOUNTAIN) { + EnEncount2_ParticleInit(this, &particlePos, particleScale); + } else if (this->particleSpawnTimer == 0) { + EnEncount2_ParticleInit(this, &particlePos, particleScale); + this->particleSpawnTimer = 5; + } + + if ((this->numSpawnedRocks < maxRocks) && (this->timerBetweenRockSpawns == 0)) { + if (spawnerState == ENCOUNT2_ACTIVE_DEATH_MOUNTAIN) { + this->timerBetweenRockSpawns = 4; + spawnedRockType = FIRE_ROCK_SPAWNED_FALLING1; + if ((Rand_ZeroFloat(1.99f) < 1.0f) && !LINK_IS_ADULT) { + // rock spawn pos X, Z near player + tempVec2X = Rand_CenteredFloat(10.0f) + player->actor.world.pos.x; + tempVec2Z = Rand_CenteredFloat(10.0f) + player->actor.world.pos.z; + } else { + if (player->linearVelocity != 0.0f) { + // rock spawn pos is between 300 and 600 units from the camera depending on the camera yaw. + // Rocks will generally spawn closer to the camera in the X axis than in the Z axis. + tempVec2X = Rand_CenteredFloat(200.0f) + (globalCtx->view.eye.x + (tempVec2X * 300.0f)); + tempVec2Z = Rand_CenteredFloat(50.0f) + (globalCtx->view.eye.z + (tempVec2Z * 600.0f)); + } else { + // rock spawn pos X, Z near player + tempVec2X = Rand_CenteredFloat(10.0f) + player->actor.world.pos.x; + tempVec2Z = Rand_CenteredFloat(10.0f) + player->actor.world.pos.z; + } + spawnedRockType = FIRE_ROCK_SPAWNED_FALLING2; + } + } else { + this->timerBetweenRockSpawns = 50; + spawnedRockType = FIRE_ROCK_SPAWNED_FALLING2; + // rock spawn pos X,Z at a random position roughly 300 units ahead of camera + tempVec2X = Rand_CenteredFloat(100.0f) + tempVec1X; + tempVec2Z = Rand_CenteredFloat(100.0f) + tempVec1Z; + + if (Rand_ZeroFloat(3.99f) < 1.0f) { + // rock spawn pos X,Z at a random position near player + tempVec2X = Rand_CenteredFloat(70.0f) + player->actor.world.pos.x; + tempVec2Z = Rand_CenteredFloat(70.0f) + player->actor.world.pos.z; + } + } + spawnedRock = + (EnFireRock*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FIRE_ROCK, + tempVec2X, tempVec1Y, tempVec2Z, 0, 0, 0, spawnedRockType); + if (spawnedRock != NULL) { + spawnedRock->spawner = this; + this->numSpawnedRocks++; + return; + } + // "☆☆☆☆☆ Can't occur! ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n" VT_RST); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生できません! ☆☆☆☆☆\n\n" VT_RST); + } + } +} + +void EnEncount2_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnEncount2* this = (EnEncount2*)thisx; + GlobalContext* globalCtx = globalCtx2; + + if (this->deathMountainSpawnerTimer != 0) { + this->deathMountainSpawnerTimer--; + } + + if (this->timerBetweenRockSpawns != 0) { + this->timerBetweenRockSpawns--; + } + + if (this->particleSpawnTimer != 0) { + this->particleSpawnTimer--; + } + + this->actionFunc(this, globalCtx); + + EnEncount2_ParticleUpdate(this, globalCtx); + + if (!this->isNotDeathMountain) { + this->unk17C = this->envEffectsTimer / 60.0f; + this->unk160 = this->unk17C * -50.0f; + globalCtx->envCtx.adjAmbientColor[0] = (s16)this->unk160 * -1.5f; + globalCtx->envCtx.adjAmbientColor[1] = globalCtx->envCtx.adjAmbientColor[2] = this->unk160; + this->unk168 = this->unk17C * -20.0f; + globalCtx->envCtx.adjLight1Color[0] = (s16)this->unk168 * -1.5f; + globalCtx->envCtx.adjLight1Color[1] = globalCtx->envCtx.adjLight1Color[2] = this->unk168; + this->unk170 = this->unk17C * -50.0f; + globalCtx->envCtx.adjFogNear = this->unk170; + globalCtx->envCtx.adjFogColor[0] = (u8)((160.0f - globalCtx->envCtx.lightSettings.fogColor[0]) * this->unk17C); + globalCtx->envCtx.adjFogColor[1] = (u8)((160.0f - globalCtx->envCtx.lightSettings.fogColor[1]) * this->unk17C); + globalCtx->envCtx.adjFogColor[2] = (u8)((150.0f - globalCtx->envCtx.lightSettings.fogColor[2]) * this->unk17C); + } +} + +void EnEncount2_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnEncount2* this = (EnEncount2*)thisx; + + EnEncount2_ParticleDraw(&this->actor, globalCtx); +} + +void EnEncount2_ParticleInit(EnEncount2* this, Vec3f* particlePos, f32 scale) { + EnEncount2Particle* particle = this->particles; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->particles); i++, particle++) { + if (!particle->isAlive) { + particle->pos = *particlePos; + particle->scale = scale; + particle->rot.x = 0.0f; + particle->rot.y = 0.0f; + particle->rot.z = 0.0f; + particle->moveDirection.x = Rand_CenteredFloat(20.0f); + particle->moveDirection.y = -20.0f; + particle->moveDirection.z = Rand_CenteredFloat(20.0f); + particle->isAlive = 1; + break; + } + } +} + +void EnEncount2_ParticleUpdate(EnEncount2* this, GlobalContext* globalCtx) { + s16 i; + EnEncount2Particle* particle = this->particles; + Player* player = GET_PLAYER(globalCtx); + Vec3f targetPos; + + for (i = 0; i < ARRAY_COUNT(this->particles); particle++, i++) { + if (particle->isAlive) { + particle->rot.x += Rand_ZeroOne() * 500.0f; + particle->rot.y += Rand_ZeroOne() * 500.0f; + particle->rot.z += Rand_ZeroOne() * 500.0f; + targetPos.x = particle->pos.x + particle->moveDirection.x; + targetPos.y = particle->pos.y + particle->moveDirection.y; + targetPos.z = particle->pos.z + particle->moveDirection.z; + Math_ApproachF(&particle->pos.x, targetPos.x, 0.3f, 30.0f); + Math_ApproachF(&particle->pos.y, targetPos.y, 0.8f, 250.0f); + Math_ApproachF(&particle->pos.z, targetPos.z, 0.3f, 30.0f); + Math_ApproachF(&particle->moveDirection.y, -20.0f, 0.9f, 1.0f); + + if (globalCtx->sceneNum != SCENE_SPOT16) { + if (particle->pos.y < (player->actor.floorHeight - 50.0f)) { + particle->isAlive = 0; + } + } else if (particle->pos.y < 1500.0f) { + particle->isAlive = 0; + } + } + } +} + +void EnEncount2_ParticleDraw(Actor* thisx, GlobalContext* globalCtx) { + EnEncount2* this = (EnEncount2*)thisx; + EnEncount2Particle* particle = this->particles; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 i; + s32 objBankIndex; + + OPEN_DISPS(gfxCtx, "../z_en_encount2.c", 642); + + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_EFC_STAR_FIELD); + + if (objBankIndex >= 0) { + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[objBankIndex].segment); + + for (i = 0; i < ARRAY_COUNT(this->particles); particle++, i++) { + if (particle->isAlive) { + Matrix_Translate(particle->pos.x, particle->pos.y, particle->pos.z, MTXMODE_NEW); + Matrix_RotateX(particle->rot.x * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_RotateY(particle->rot.y * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_RotateZ(particle->rot.z * (M_PI / 180.0f), MTXMODE_APPLY); + Matrix_Scale(particle->scale, particle->scale, particle->scale, MTXMODE_APPLY); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 155, 55, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 155, 255, 55, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_encount2.c", 669), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_efc_star_field_DL_000DE0); + } + } + } + + CLOSE_DISPS(gfxCtx, "../z_en_encount2.c", 678); +} diff --git a/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h b/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h new file mode 100644 index 000000000..302affd9a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Encount2/z_en_encount2.h @@ -0,0 +1,41 @@ +#ifndef Z_EN_ENCOUNT2_H +#define Z_EN_ENCOUNT2_H + +#include "ultra64.h" +#include "global.h" + +struct EnEncount2; + +typedef void (*EnEncount2ActionFunc)(struct EnEncount2*, GlobalContext*); + +typedef struct { + /* 0x0000 */ Vec3f pos; + /* 0x000C */ f32 scale; + /* 0x0010 */ u8 isAlive; + /* 0x0014 */ Vec3f moveDirection; + /* 0x0020 */ Vec3f rot; +} EnEncount2Particle; // size = 0x2C + +typedef struct EnEncount2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnEncount2ActionFunc actionFunc; + /* 0x0150 */ char unk150[0x4]; + /* 0x0154 */ s16 deathMountainSpawnerTimer; + /* 0x0156 */ s16 timerBetweenRockSpawns; + /* 0x0158 */ s16 numSpawnedRocks; + /* 0x015A */ s16 isNotDeathMountain; + /* 0x015C */ s16 collapseSpawnerInactive; + /* 0x015E */ s16 particleSpawnTimer; + /* 0x0160 */ f32 unk160; + /* 0x0164 */ char unk164[0x4]; + /* 0x0168 */ f32 unk168; + /* 0x016C */ char unk16C[0x4]; + /* 0x0178 */ f32 unk170; + /* 0x0174 */ char unk174[0x4]; + /* 0x0178 */ s16 envEffectsTimer; + /* 0x017C */ f32 unk17C; + /* 0x0180 */ u64 isQuaking; + /* 0x0188 */ EnEncount2Particle particles[50]; +} EnEncount2; // size = 0x0A20 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ex_Item/z_en_ex_item.c b/soh/src/overlays/actors/ovl_En_Ex_Item/z_en_ex_item.c new file mode 100644 index 000000000..91c059bee --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ex_Item/z_en_ex_item.c @@ -0,0 +1,520 @@ +/* + * File: z_en_ex_item.c + * Overlay: ovl_En_Ex_Item + * Description: Minigame prize items + */ + +#include "z_en_ex_item.h" +#include "overlays/actors/ovl_En_Bom_Bowl_Pit/z_en_bom_bowl_pit.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnExItem_Init(Actor* thisx, GlobalContext* globalCtx); +void EnExItem_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnExItem_Update(Actor* thisx, GlobalContext* globalCtx); +void EnExItem_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnExItem_DrawItems(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_DrawHeartPiece(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_DrawRupee(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_DrawKey(EnExItem* this, GlobalContext* globalCtx, s32 index); +void EnExItem_DrawMagic(EnExItem* this, GlobalContext* globalCtx, s16 magicIndex); + +void EnExItem_WaitForObject(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_BowlPrize(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_SetupBowlCounter(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_BowlCounter(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_ExitChest(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_FairyMagic(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_TargetPrizeApproach(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_TargetPrizeGive(EnExItem* this, GlobalContext* globalCtx); +void EnExItem_TargetPrizeFinish(EnExItem* this, GlobalContext* globalCtx); + +const ActorInit En_Ex_Item_InitVars = { + ACTOR_EN_EX_ITEM, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnExItem), + (ActorFunc)EnExItem_Init, + (ActorFunc)EnExItem_Destroy, + (ActorFunc)EnExItem_Update, + (ActorFunc)EnExItem_Draw, + NULL, +}; + +void EnExItem_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnExItem_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnExItem* this = (EnExItem*)thisx; + + this->actor.flags &= ~ACTOR_FLAG_0; + this->type = this->actor.params & 0xFF; + this->unusedParam = (this->actor.params >> 8) & 0xFF; + osSyncPrintf("\n\n"); + // "What will come out?" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ なにがでるかな? ☆☆☆☆☆ %d\n" VT_RST, this->type); + // "What will come out?" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ なにがでるかな? ☆☆☆☆☆ %d\n" VT_RST, this->unusedParam); + this->initPos = this->actor.world.pos; + this->getItemObjId = -1; + switch (this->type) { + case EXITEM_BOMB_BAG_BOWLING: + case EXITEM_BOMB_BAG_COUNTER: + this->getItemObjId = OBJECT_GI_BOMBPOUCH; + break; + case EXITEM_HEART_PIECE_BOWLING: + case EXITEM_HEART_PIECE_COUNTER: + this->getItemObjId = OBJECT_GI_HEARTS; + break; + case EXITEM_BOMBCHUS_BOWLING: + case EXITEM_BOMBCHUS_COUNTER: + this->getItemObjId = OBJECT_GI_BOMB_2; + break; + case EXITEM_BOMBS_BOWLING: + case EXITEM_BOMBS_COUNTER: + this->getItemObjId = OBJECT_GI_BOMB_1; + break; + case EXITEM_PURPLE_RUPEE_BOWLING: + case EXITEM_PURPLE_RUPEE_COUNTER: + case EXITEM_GREEN_RUPEE_CHEST: + case EXITEM_BLUE_RUPEE_CHEST: + case EXITEM_RED_RUPEE_CHEST: + case EXITEM_13: + case EXITEM_14: + this->getItemObjId = OBJECT_GI_RUPY; + break; + case EXITEM_SMALL_KEY_CHEST: + this->scale = 0.05f; + this->actor.velocity.y = 10.0f; + this->timer = 7; + this->actionFunc = EnExItem_ExitChest; + break; + case EXITEM_MAGIC_FIRE: + case EXITEM_MAGIC_WIND: + case EXITEM_MAGIC_DARK: + this->getItemObjId = OBJECT_GI_GODDESS; + break; + case EXITEM_BULLET_BAG: + this->getItemObjId = OBJECT_GI_DEKUPOUCH; + } + + if (this->getItemObjId >= 0) { + this->objectIdx = Object_GetIndex(&globalCtx->objectCtx, this->getItemObjId); + this->actor.draw = NULL; + if (this->objectIdx < 0) { + Actor_Kill(&this->actor); + // "What?" + osSyncPrintf("なにみの? %d\n", this->actor.params); + // "bank is funny" + osSyncPrintf(VT_FGCOL(PURPLE) " バンクおかしいしぞ!%d\n" VT_RST "\n", this->actor.params); + return; + } + this->actionFunc = EnExItem_WaitForObject; + } +} + +void EnExItem_WaitForObject(EnExItem* this, GlobalContext* globalCtx) { + s32 onCounter; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objectIdx)) { + // "End of transfer" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 転送終了 ☆☆☆☆☆ %d\n" VT_RST, this->actor.params, this); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 転送終了 ☆☆☆☆☆ %d\n" VT_RST, this->actor.params, this); + osSyncPrintf(VT_FGCOL(BLUE) "☆☆☆☆☆ 転送終了 ☆☆☆☆☆ %d\n" VT_RST, this->actor.params, this); + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 転送終了 ☆☆☆☆☆ %d\n" VT_RST, this->actor.params, this); + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 転送終了 ☆☆☆☆☆ %d\n\n" VT_RST, this->actor.params, this); + this->actor.objBankIndex = this->objectIdx; + this->actor.draw = EnExItem_Draw; + this->stopRotate = false; + onCounter = false; + switch (this->type) { + case EXITEM_BOMB_BAG_COUNTER: + onCounter = true; + case EXITEM_BOMB_BAG_BOWLING: + this->unk_17C = func_8002EBCC; + this->giDrawId = GID_BOMB_BAG_30; + this->timer = 65; + this->prizeRotateTimer = 35; + this->scale = 0.5f; + if (onCounter == 0) { + this->actionFunc = EnExItem_BowlPrize; + } else { + this->actionFunc = EnExItem_SetupBowlCounter; + this->actor.shape.yOffset = -18.0f; + } + break; + case EXITEM_HEART_PIECE_COUNTER: + onCounter = true; + case EXITEM_HEART_PIECE_BOWLING: + this->unk_17C = func_8002ED80; + this->timer = 65; + this->prizeRotateTimer = 35; + this->scale = 0.5f; + if (!onCounter) { + func_80078884(NA_SE_SY_PIECE_OF_HEART); + this->actionFunc = EnExItem_BowlPrize; + } else { + this->actionFunc = EnExItem_SetupBowlCounter; + this->actor.shape.yOffset = -10.0f; + } + break; + case EXITEM_BOMBCHUS_COUNTER: + onCounter = true; + case EXITEM_BOMBCHUS_BOWLING: + this->unk_17C = func_8002EBCC; + this->giDrawId = GID_BOMBCHU; + this->timer = 65; + this->prizeRotateTimer = 35; + this->scale = 0.5f; + if (!onCounter) { + this->actionFunc = EnExItem_BowlPrize; + } else { + this->actionFunc = EnExItem_SetupBowlCounter; + } + break; + case EXITEM_BOMBS_BOWLING: + case EXITEM_BOMBS_COUNTER: + this->unk_17C = func_8002EBCC; + this->giDrawId = GID_BOMB; + this->timer = 65; + this->prizeRotateTimer = 35; + this->scale = 0.5f; + this->unkFloat = 0.5f; + if (this->type == EXITEM_BOMBS_BOWLING) { + this->actionFunc = EnExItem_BowlPrize; + } else { + this->actionFunc = EnExItem_SetupBowlCounter; + this->actor.shape.yOffset = 10.0f; + } + break; + case EXITEM_PURPLE_RUPEE_BOWLING: + case EXITEM_PURPLE_RUPEE_COUNTER: + this->unk_17C = func_8002EBCC; + this->unk_180 = func_8002ED80; + this->giDrawId = GID_RUPEE_PURPLE; + this->timer = 65; + this->prizeRotateTimer = 35; + this->scale = 0.5f; + this->unkFloat = 0.5f; + if (this->type == EXITEM_PURPLE_RUPEE_BOWLING) { + this->actionFunc = EnExItem_BowlPrize; + } else { + this->actionFunc = EnExItem_SetupBowlCounter; + this->actor.shape.yOffset = 40.0f; + } + break; + case EXITEM_GREEN_RUPEE_CHEST: + case EXITEM_BLUE_RUPEE_CHEST: + case EXITEM_RED_RUPEE_CHEST: + case EXITEM_13: + case EXITEM_14: + this->unk_17C = func_8002EBCC; + this->unk_180 = func_8002ED80; + this->timer = 7; + this->scale = 0.5f; + this->unkFloat = 0.5f; + this->actor.velocity.y = 10.0f; + switch (this->type) { + case EXITEM_GREEN_RUPEE_CHEST: + this->giDrawId = GID_RUPEE_GREEN; + break; + case EXITEM_BLUE_RUPEE_CHEST: + this->giDrawId = GID_RUPEE_BLUE; + break; + case EXITEM_RED_RUPEE_CHEST: + this->giDrawId = GID_RUPEE_RED; + break; + case EXITEM_14: + this->giDrawId = GID_RUPEE_PURPLE; + break; + } + this->actionFunc = EnExItem_ExitChest; + break; + case EXITEM_MAGIC_FIRE: + case EXITEM_MAGIC_WIND: + case EXITEM_MAGIC_DARK: + this->scale = 0.35f; + this->actionFunc = EnExItem_FairyMagic; + break; + case EXITEM_BULLET_BAG: + this->unk_17C = func_8002EBCC; + this->giDrawId = GID_BULLET_BAG; + this->scale = 0.1f; + this->timer = 80; + this->prizeRotateTimer = 35; + this->actionFunc = EnExItem_TargetPrizeApproach; + break; + case EXITEM_SMALL_KEY_CHEST: + break; + } + } +} + +void EnExItem_BowlPrize(EnExItem* this, GlobalContext* globalCtx) { + s32 pad; + f32 tmpf1; + f32 tmpf2; + f32 tmpf3; + f32 tmpf4; + f32 tmpf5; + f32 tmpf6; + f32 tmpf7; + f32 sp3C; + + if (!this->stopRotate) { + this->actor.shape.rot.y += 0x1000; + if ((this->prizeRotateTimer == 0) && ((this->actor.shape.rot.y & 0xFFFF) == 0x9000)) { + this->stopRotate++; + } + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, 0, 5, 0x1000, 0); + } + if (this->timer != 0) { + if (this->prizeRotateTimer != 0) { + sp3C = 250.0f; + if (this->type == EXITEM_BOMBCHUS_BOWLING) { + sp3C = 220.0f; + } + tmpf1 = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + tmpf2 = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + tmpf3 = globalCtx->view.lookAt.z + sp3C - globalCtx->view.eye.z; + tmpf4 = sqrtf(SQ(tmpf1) + SQ(tmpf2) + SQ(tmpf3)); + + tmpf5 = (tmpf1 / tmpf4) * 5.0f; + tmpf6 = (tmpf2 / tmpf4) * 5.0f; + tmpf7 = (tmpf3 / tmpf4) * 5.0f; + + tmpf1 = globalCtx->view.eye.x + tmpf5 - this->actor.world.pos.x; + tmpf2 = globalCtx->view.eye.y + tmpf6 - this->actor.world.pos.y; + tmpf3 = globalCtx->view.eye.z + tmpf7 - this->actor.world.pos.z; + + this->actor.world.pos.x += (tmpf1 / tmpf4) * 5.0f; + this->actor.world.pos.y += (tmpf2 / tmpf4) * 5.0f; + this->actor.world.pos.z += (tmpf3 / tmpf4) * 5.0f; + } + } else { + // "parent" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 母親ー? ☆☆☆☆☆ %x\n" VT_RST, this->actor.parent); + // "Can it move?" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 動いてねー? ☆☆☆☆☆ %x\n" VT_RST, this->actor.parent->update); + if ((this->actor.parent != NULL) && (this->actor.parent->update != NULL)) { + ((EnBomBowlPit*)this->actor.parent)->exItemDone = 1; + // "It can't move!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ さぁきえるぞ! ☆☆☆☆☆ \n" VT_RST); + } + Actor_Kill(&this->actor); + } +} + +void EnExItem_SetupBowlCounter(EnExItem* this, GlobalContext* globalCtx) { + this->actor.world.rot.y = this->actor.shape.rot.y = 0x4268; + this->actionFunc = EnExItem_BowlCounter; +} + +void EnExItem_BowlCounter(EnExItem* this, GlobalContext* globalCtx) { + this->actor.shape.rot.y += 0x800; + if (this->killItem) { + Actor_Kill(&this->actor); + } +} + +void EnExItem_ExitChest(EnExItem* this, GlobalContext* globalCtx) { + this->actor.shape.rot.y += 0x1000; + if (this->timer != 0) { + if (this->timer == 1) { + this->chestKillTimer = 20; + } + } else { + this->actor.velocity.y = 0.0f; + if (this->chestKillTimer == 0) { + Actor_Kill(&this->actor); + } + } + Actor_MoveForward(&this->actor); +} + +void EnExItem_FairyMagic(EnExItem* this, GlobalContext* globalCtx) { + this->actor.shape.rot.y += 0x800; +} + +void EnExItem_TargetPrizeApproach(EnExItem* this, GlobalContext* globalCtx) { + f32 tmpf1; + f32 tmpf2; + f32 tmpf3; + f32 tmpf4; + f32 tmpf5; + f32 tmpf6; + f32 tmpf7; + + Math_ApproachF(&this->scale, 0.8f, 0.1f, 0.02f); + if (!this->stopRotate) { + this->actor.shape.rot.y += 0x1000; + if ((this->prizeRotateTimer == 0) && ((this->actor.shape.rot.y & 0xFFFF) == 0x9000)) { + this->stopRotate++; + } + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, -0x4000, 5, 0x1000, 0); + } + + if (this->timer != 0) { + if (this->prizeRotateTimer != 0) { + tmpf1 = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + tmpf2 = globalCtx->view.lookAt.y - 10.0f - globalCtx->view.eye.y; + tmpf3 = globalCtx->view.lookAt.z + 10.0f - globalCtx->view.eye.z; + tmpf4 = sqrtf(SQ(tmpf1) + SQ(tmpf2) + SQ(tmpf3)); + + tmpf5 = (tmpf1 / tmpf4) * 5.0f; + tmpf6 = (tmpf2 / tmpf4) * 5.0f; + tmpf7 = (tmpf3 / tmpf4) * 5.0f; + + tmpf1 = globalCtx->view.eye.x + tmpf5 - this->actor.world.pos.x; + tmpf2 = globalCtx->view.eye.y - 10.0f + tmpf6 - this->actor.world.pos.y; + tmpf3 = globalCtx->view.eye.z + 10.0f + tmpf7 - this->actor.world.pos.z; + + this->actor.world.pos.x += (tmpf1 / tmpf4) * 5.0f; + this->actor.world.pos.y += (tmpf2 / tmpf4) * 5.0f; + this->actor.world.pos.z += (tmpf3 / tmpf4) * 5.0f; + } + } else { + s32 getItemId; + + this->actor.draw = NULL; + func_8002DF54(globalCtx, NULL, 7); + this->actor.parent = NULL; + if (CUR_UPG_VALUE(UPG_BULLET_BAG) == 1) { + getItemId = GI_BULLET_BAG_40; + } else { + getItemId = GI_BULLET_BAG_50; + } + func_8002F434(&this->actor, globalCtx, getItemId, 2000.0f, 1000.0f); + this->actionFunc = EnExItem_TargetPrizeGive; + } +} + +void EnExItem_TargetPrizeGive(EnExItem* this, GlobalContext* globalCtx) { + s32 getItemId; + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = EnExItem_TargetPrizeFinish; + } else { + getItemId = (CUR_UPG_VALUE(UPG_BULLET_BAG) == 2) ? GI_BULLET_BAG_50 : GI_BULLET_BAG_40; + + func_8002F434(&this->actor, globalCtx, getItemId, 2000.0f, 1000.0f); + } +} + +void EnExItem_TargetPrizeFinish(EnExItem* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + // "Successful completion" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); + gSaveContext.itemGetInf[1] |= 0x2000; + Actor_Kill(&this->actor); + } +} + +void EnExItem_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnExItem* this = (EnExItem*)thisx; + + if (this->timer != 0) { + this->timer--; + } + if (this->chestKillTimer != 0) { + this->chestKillTimer--; + } + if (this->prizeRotateTimer != 0) { + this->prizeRotateTimer--; + } + this->actionFunc(this, globalCtx); +} + +void EnExItem_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnExItem* this = (EnExItem*)thisx; + s32 magicType; + + Actor_SetScale(&this->actor, this->scale); + switch (this->type) { + case EXITEM_BOMB_BAG_BOWLING: + case EXITEM_BOMBCHUS_BOWLING: + case EXITEM_BOMBS_BOWLING: + case EXITEM_BOMB_BAG_COUNTER: + case EXITEM_BOMBCHUS_COUNTER: + case EXITEM_BOMBS_COUNTER: + case EXITEM_BULLET_BAG: + EnExItem_DrawItems(this, globalCtx); + break; + case EXITEM_HEART_PIECE_BOWLING: + case EXITEM_HEART_PIECE_COUNTER: + EnExItem_DrawHeartPiece(this, globalCtx); + break; + case EXITEM_PURPLE_RUPEE_BOWLING: + case EXITEM_PURPLE_RUPEE_COUNTER: + case EXITEM_GREEN_RUPEE_CHEST: + case EXITEM_BLUE_RUPEE_CHEST: + case EXITEM_RED_RUPEE_CHEST: + case EXITEM_13: + case EXITEM_14: + EnExItem_DrawRupee(this, globalCtx); + break; + case EXITEM_SMALL_KEY_CHEST: + EnExItem_DrawKey(this, globalCtx, 0); + break; + case EXITEM_MAGIC_FIRE: + case EXITEM_MAGIC_WIND: + case EXITEM_MAGIC_DARK: + magicType = this->type - EXITEM_MAGIC_FIRE; + EnExItem_DrawMagic(this, globalCtx, magicType); + break; + } +} + +void EnExItem_DrawItems(EnExItem* this, GlobalContext* globalCtx) { + if (this->unk_17C != NULL) { + this->unk_17C(&this->actor, globalCtx, 0); + } + if (this) {} + func_8002ED80(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, this->giDrawId); +} + +void EnExItem_DrawHeartPiece(EnExItem* this, GlobalContext* globalCtx) { + func_8002ED80(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, GID_HEART_PIECE); +} + +void EnExItem_DrawMagic(EnExItem* this, GlobalContext* globalCtx, s16 magicIndex) { + static s16 sgiDrawIds[] = { GID_DINS_FIRE, GID_FARORES_WIND, GID_NAYRUS_LOVE }; + + func_8002ED80(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, sgiDrawIds[magicIndex]); +} + +void EnExItem_DrawKey(EnExItem* this, GlobalContext* globalCtx, s32 index) { + static s32 keySegments[] = { 0x0403F140 }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ex_item.c", 880); + + func_8009460C(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ex_item.c", 887), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(keySegments[index])); + gSPDisplayList(POLY_OPA_DISP++, gItemDropDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ex_item.c", 893); +} + +void EnExItem_DrawRupee(EnExItem* this, GlobalContext* globalCtx) { + if (this->unk_17C != NULL) { + this->unk_17C(&this->actor, globalCtx, 0); + } + if (this->unk_180 != NULL) { + this->unk_180(&this->actor, globalCtx, 0); + } + GetItem_Draw(globalCtx, this->giDrawId); +} diff --git a/soh/src/overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h b/soh/src/overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h new file mode 100644 index 000000000..5dff36859 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ex_Item/z_en_ex_item.h @@ -0,0 +1,59 @@ +#ifndef Z_EN_EX_ITEM_H +#define Z_EN_EX_ITEM_H + +#include "ultra64.h" +#include "global.h" + +struct EnExItem; + +typedef void (*EnExItemActionFunc)(struct EnExItem* this, GlobalContext* globalCtx); +typedef void (*EnExItemLightFunc)(Actor*, GlobalContext*, s32); + +typedef struct EnExItem { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnExItemActionFunc actionFunc; + /* 0x0150 */ s16 getItemObjId; + /* 0x0152 */ s16 type; + /* 0x0152 */ s16 unusedParam; + /* 0x0156 */ s16 giDrawId; + /* 0x0158 */ s16 stopRotate; + /* 0x015A */ s16 timer; + /* 0x015A */ s16 chestKillTimer; + /* 0x015A */ s16 prizeRotateTimer; + /* 0x0160 */ s16 killItem; + /* 0x0164 */ f32 scale; + /* 0x0168 */ f32 unkFloat; // set to the same value as scale, but unused + /* 0x016C */ s8 objectIdx; + /* 0x0170 */ Vec3f initPos; // unused + /* 0x017C */ EnExItemLightFunc unk_17C; + /* 0x0180 */ EnExItemLightFunc unk_180; +} EnExItem; // size = 0x0184 + +typedef enum { + /* 0 */ EXITEM_BOMB_BAG_BOWLING, + /* 1 */ EXITEM_HEART_PIECE_BOWLING, + /* 2 */ EXITEM_BOMBCHUS_BOWLING, + /* 3 */ EXITEM_BOMBS_BOWLING, + /* 4 */ EXITEM_PURPLE_RUPEE_BOWLING, + /* 5 */ EXITEM_BOMB_BAG_COUNTER, + /* 6 */ EXITEM_HEART_PIECE_COUNTER, + /* 7 */ EXITEM_BOMBCHUS_COUNTER, + /* 8 */ EXITEM_BOMBS_COUNTER, + /* 9 */ EXITEM_PURPLE_RUPEE_COUNTER, + /* 10 */ EXITEM_GREEN_RUPEE_CHEST, + /* 11 */ EXITEM_BLUE_RUPEE_CHEST, + /* 12 */ EXITEM_RED_RUPEE_CHEST, + /* 13 */ EXITEM_13, + /* 14 */ EXITEM_14, + /* 15 */ EXITEM_SMALL_KEY_CHEST, + /* 16 */ EXITEM_MAGIC_FIRE, + /* 17 */ EXITEM_MAGIC_WIND, + /* 18 */ EXITEM_MAGIC_DARK, + /* 19 */ EXITEM_BULLET_BAG +} EnExItemType; + +#define EXITEM_COUNTER 5 +#define EXITEM_CHEST 10 +#define EXITEM_MAGIC 16 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.c b/soh/src/overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.c new file mode 100644 index 000000000..2e936bcbe --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.c @@ -0,0 +1,397 @@ +#include "z_en_ex_ruppy.h" +#include "vt.h" +#include "../ovl_En_Diving_Game/z_en_diving_game.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnExRuppy_Init(Actor* thisx, GlobalContext* globalCtx); +void EnExRuppy_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnExRuppy_Update(Actor* thisx, GlobalContext* globalCtx); +void EnExRuppy_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnExRuppy_DropIntoWater(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_WaitToBlowUp(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_WaitAsCollectible(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_GalleryTarget(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_EnterWater(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_Sink(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_WaitInGame(EnExRuppy* this, GlobalContext* globalCtx); +void EnExRuppy_Kill(EnExRuppy* this, GlobalContext* globalCtx); + +static s16 sEnExRuppyCollectibleTypes[] = { + ITEM00_RUPEE_GREEN, ITEM00_RUPEE_BLUE, ITEM00_RUPEE_RED, ITEM00_RUPEE_ORANGE, ITEM00_RUPEE_PURPLE, +}; + +// Unused, as the function sets these directly +static s16 sRupeeValues[] = { + 1, 5, 20, 500, 50, +}; + +const ActorInit En_Ex_Ruppy_InitVars = { + ACTOR_EN_EX_RUPPY, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnExRuppy), + (ActorFunc)EnExRuppy_Init, + (ActorFunc)EnExRuppy_Destroy, + (ActorFunc)EnExRuppy_Update, + (ActorFunc)EnExRuppy_Draw, + NULL, +}; + +void EnExRuppy_Init(Actor* thisx, GlobalContext* globalCtx) { + EnExRuppy* this = (EnExRuppy*)thisx; + EnDivingGame* divingGame; + f32 temp1; + f32 temp2; + s16 temp3; + + this->type = this->actor.params; + // "Index" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ インデックス ☆☆☆☆☆ %x\n" VT_RST, this->type); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 25.0f); + + switch (this->type) { + case 0: + this->unk_160 = 0.01f; + Actor_SetScale(&this->actor, this->unk_160); + this->actor.room = -1; + this->actor.gravity = 0.0f; + + // If you haven't won the diving game before, you will always get 5 rupees + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + this->rupeeValue = 5; + this->colorIdx = 1; + } else { + temp1 = 200.99f; + if (this->actor.parent != NULL) { + divingGame = (EnDivingGame*)this->actor.parent; + if (divingGame->actor.update != NULL) { + temp2 = divingGame->extraWinCount * 10.0f; + temp1 += temp2; + } + } + + temp3 = Rand_ZeroFloat(temp1); + if ((temp3 >= 0) && (temp3 < 40)) { + this->rupeeValue = 1; + this->colorIdx = 0; + } else if ((temp3 >= 40) && (temp3 < 170)) { + this->rupeeValue = 5; + this->colorIdx = 1; + } else if ((temp3 >= 170) && (temp3 < 190)) { + this->rupeeValue = 20; + this->colorIdx = 2; + } else if ((temp3 >= 190) && (temp3 < 200)) { + this->rupeeValue = 50; + this->colorIdx = 4; + } else { + this->unk_160 = 0.02f; + Actor_SetScale(&this->actor, this->unk_160); + this->rupeeValue = 500; + this->colorIdx = 3; + if (this->actor.parent != NULL) { + divingGame = (EnDivingGame*)this->actor.parent; + if (divingGame->actor.update != NULL) { + divingGame->extraWinCount = 0; + } + } + } + } + + this->actor.shape.shadowScale = 7.0f; + this->actor.shape.yOffset = 700.0f; + this->unk_15A = this->actor.world.rot.z; + this->actor.world.rot.z = 0; + this->timer = 30; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnExRuppy_DropIntoWater; + break; + + case 1: + case 2: // Giant pink ruppe that explodes when you touch it + if (this->type == 1) { + Actor_SetScale(&this->actor, 0.1f); + this->colorIdx = 4; + } else { + Actor_SetScale(thisx, 0.02f); + this->colorIdx = (s16)Rand_ZeroFloat(3.99f) + 1; + } + this->actor.gravity = -3.0f; + // "Wow Coin" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ わーなーコイン ☆☆☆☆☆ \n" VT_RST); + this->actor.shape.shadowScale = 6.0f; + this->actor.shape.yOffset = 700.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnExRuppy_WaitToBlowUp; + break; + + case 3: // Spawned by the guard in Hyrule courtyard + Actor_SetScale(&this->actor, 0.02f); + this->colorIdx = 0; + switch ((s16)Rand_ZeroFloat(30.99f)) { + case 0: + this->colorIdx = 2; + break; + case 10: + case 20: + case 30: + this->colorIdx = 1; + break; + } + this->actor.gravity = -3.0f; + // "Normal rupee" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ノーマルルピー ☆☆☆☆☆ \n" VT_RST); + this->actor.shape.shadowScale = 6.0f; + this->actor.shape.yOffset = 700.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnExRuppy_WaitAsCollectible; + break; + + case 4: // Progress markers in the shooting gallery + this->actor.gravity = -3.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + Actor_SetScale(&this->actor, 0.01f); + this->actor.shape.shadowScale = 6.0f; + this->actor.shape.yOffset = -700.0f; + this->actionFunc = EnExRuppy_GalleryTarget; + break; + } +} + +void EnExRuppy_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnExRuppy_SpawnSparkles(EnExRuppy* this, GlobalContext* globalCtx, s16 numSparkles, s32 movementType) { + static Vec3f velocities[] = { { 0.0f, 0.1f, 0.0f }, { 0.0f, 0.0f, 0.0f } }; + static Vec3f accelerations[] = { { 0.0f, 0.01f, 0.0f }, { 0.0f, 0.0f, 0.0f } }; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + s32 i; + s16 scale; + s16 life; + + if (numSparkles < 1) { + numSparkles = 1; + } + + primColor.r = 255; + primColor.g = 255; + primColor.b = 0; + envColor.r = 255; + envColor.g = 255; + envColor.b = 255; + velocity = velocities[movementType]; + accel = accelerations[movementType]; + scale = 3000; + life = 16; + + for (i = 0; i < numSparkles; i++) { + if (movementType == 1) { + accel.x = Rand_CenteredFloat(20.0f); + accel.z = Rand_CenteredFloat(20.0f); + scale = 5000; + life = 20; + } + pos.x = (Rand_ZeroOne() - 0.5f) * 10.0f + this->actor.world.pos.x; + pos.y = (Rand_ZeroOne() - 0.5f) * 10.0f + (this->actor.world.pos.y + this->unk_160 * 600.0f); + pos.z = (Rand_ZeroOne() - 0.5f) * 10.0f + this->actor.world.pos.z; + EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, scale, life); + } +} + +void EnExRuppy_DropIntoWater(EnExRuppy* this, GlobalContext* globalCtx) { + EnDivingGame* divingGame; + + this->actor.shape.rot.y += 0x7A8; + Math_ApproachF(&this->actor.gravity, -2.0f, 0.3f, 1.0f); + EnExRuppy_SpawnSparkles(this, globalCtx, 2, 0); + func_80078884(NA_SE_EV_RAINBOW_SHOWER - SFX_FLAG); + divingGame = (EnDivingGame*)this->actor.parent; + if ((divingGame != NULL) && (divingGame->actor.update != NULL) && + ((divingGame->unk_296 == 0) || (this->actor.bgCheckFlags & 0x20) || (this->timer == 0))) { + this->invisible = true; + this->actor.speedXZ = 0.0f; + this->actor.velocity.x = this->actor.velocity.y = this->actor.velocity.z = 0.0f; + this->actor.gravity = 0.0f; + func_80078914(&this->actor.projectedPos, NA_SE_EV_BOMB_DROP_WATER); + this->actionFunc = EnExRuppy_EnterWater; + } +} + +void EnExRuppy_EnterWater(EnExRuppy* this, GlobalContext* globalCtx) { + EnDivingGame* divingGame = (EnDivingGame*)this->actor.parent; + f32 temp_f2; + + if ((divingGame != NULL) && (divingGame->actor.update != NULL) && (divingGame->unk_2A2 == 2)) { + this->invisible = false; + this->actor.world.pos.x = ((Rand_ZeroOne() - 0.5f) * 300.0f) + -260.0f; + this->actor.world.pos.y = ((Rand_ZeroOne() - 0.5f) * 200.0f) + 370.0f; + temp_f2 = this->unk_15A * -50.0f; + if (!(gSaveContext.eventChkInf[3] & 0x100)) { + temp_f2 += -500.0f; + this->actor.world.pos.z = ((Rand_ZeroOne() - 0.5f) * 80.0f) + temp_f2; + } else { + temp_f2 += -300.0f; + this->actor.world.pos.z = ((Rand_ZeroOne() - 0.5f) * 60.0f) + temp_f2; + } + this->actionFunc = EnExRuppy_Sink; + this->actor.gravity = -1.0f; + } +} + +void EnExRuppy_Sink(EnExRuppy* this, GlobalContext* globalCtx) { + EnDivingGame* divingGame; + Vec3f pos; + s32 pad; + + if ((this->actor.bgCheckFlags & 0x20) && (this->actor.yDistToWater > 15.0f)) { + pos = this->actor.world.pos; + pos.y += this->actor.yDistToWater; + this->actor.velocity.y = -1.0f; + this->actor.gravity = -0.2f; + EffectSsGSplash_Spawn(globalCtx, &pos, 0, 0, 0, 800); + func_80078914(&this->actor.projectedPos, NA_SE_EV_BOMB_DROP_WATER); + this->actionFunc = EnExRuppy_WaitInGame; + } + divingGame = (EnDivingGame*)this->actor.parent; + if ((divingGame != NULL) && (divingGame->actor.update != NULL) && (divingGame->phase == ENDIVINGGAME_PHASE_ENDED)) { + this->timer = 20; + this->actionFunc = EnExRuppy_Kill; + } +} + +void EnExRuppy_WaitInGame(EnExRuppy* this, GlobalContext* globalCtx) { + EnDivingGame* divingGame; + Vec3f D_80A0B388 = { 0.0f, 0.1f, 0.0f }; + Vec3f D_80A0B394 = { 0.0f, 0.0f, 0.0f }; + f32 localConst = 30.0f; + + if (this->timer == 0) { + this->timer = 10; + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 0.0f, 5.0f, 5.0f, Rand_ZeroFloat(0.03f) + 0.07f); + } + if (this->actor.parent != NULL) { + divingGame = (EnDivingGame*)this->actor.parent; + if (divingGame->actor.update != NULL) { + if (divingGame->phase == ENDIVINGGAME_PHASE_ENDED) { + this->timer = 20; + this->actionFunc = EnExRuppy_Kill; + if (1) {} + } else if (this->actor.xyzDistToPlayerSq < SQ(localConst)) { + Rupees_ChangeBy(this->rupeeValue); + func_80078884(NA_SE_SY_GET_RUPY); + divingGame->grabbedRupeesCounter++; + Actor_Kill(&this->actor); + } + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnExRuppy_Kill(EnExRuppy* this, GlobalContext* globalCtx) { + this->invisible += 1; + this->invisible &= 1; // Net effect is this->invisible = !this->invisible; + if (this->timer == 0) { + Actor_Kill(&this->actor); + } +} + +typedef struct { + /* 0x000 */ Actor actor; + /* 0x14C */ char unk_14C[0x11A]; + /* 0x226 */ s16 unk_226; +} EnExRuppyParentActor; // Unclear what actor was intended to spawn this. + +void EnExRuppy_WaitToBlowUp(EnExRuppy* this, GlobalContext* globalCtx) { + EnExRuppyParentActor* parent; + Vec3f accel = { 0.0f, 0.1f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + f32 distToBlowUp = 50.0f; + s16 explosionScale; + s16 explosionScaleStep; + + if (this->type == 2) { + distToBlowUp = 30.0f; + } + if (this->actor.xyzDistToPlayerSq < SQ(distToBlowUp)) { + parent = (EnExRuppyParentActor*)this->actor.parent; + if (parent != NULL) { + if (parent->actor.update != NULL) { + parent->unk_226 = 1; + } + } else { + // "That idiot! error" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ そ、そんなばかな!エラー!!!!! ☆☆☆☆☆ \n" VT_RST); + } + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ バカめ! ☆☆☆☆☆ \n" VT_RST); // "Stupid!" + explosionScale = 100; + explosionScaleStep = 30; + if (this->type == 2) { + explosionScale = 20; + explosionScaleStep = 6; + } + EffectSsBomb2_SpawnLayered(globalCtx, &this->actor.world.pos, &velocity, &accel, explosionScale, + explosionScaleStep); + func_8002F71C(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + Actor_Kill(&this->actor); + } +} + +void EnExRuppy_WaitAsCollectible(EnExRuppy* this, GlobalContext* globalCtx) { + f32 localConst = 30.0f; + + if (this->actor.xyzDistToPlayerSq < SQ(localConst)) { + func_80078884(NA_SE_SY_GET_RUPY); + Item_DropCollectible(globalCtx, &this->actor.world.pos, (sEnExRuppyCollectibleTypes[this->colorIdx] | 0x8000)); + Actor_Kill(&this->actor); + } +} + +void EnExRuppy_GalleryTarget(EnExRuppy* this, GlobalContext* globalCtx) { + if (this->galleryFlag) { + Math_ApproachF(&this->actor.shape.yOffset, 700.0f, 0.5f, 200.0f); + } else { + Math_ApproachF(&this->actor.shape.yOffset, -700.0f, 0.5f, 200.0f); + } +} + +void EnExRuppy_Update(Actor* thisx, GlobalContext* globalCtx) { + EnExRuppy* this = (EnExRuppy*)thisx; + + this->actor.shape.rot.y += 1960; + this->actionFunc(this, globalCtx); + if (this->timer != 0) { + this->timer--; + } + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 50.0f, 0x1C); +} + +void EnExRuppy_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* rupeeTextures[] = { + gRupeeGreenTex, gRupeeBlueTex, gRupeeRedTex, gRupeePinkTex, gRupeeOrangeTex, + }; + s32 pad; + EnExRuppy* this = (EnExRuppy*)thisx; + + if (!this->invisible) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ex_ruppy.c", 774); + + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(thisx, globalCtx, 0); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ex_ruppy.c", 780), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(rupeeTextures[this->colorIdx])); + gSPDisplayList(POLY_OPA_DISP++, gRupeeDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ex_ruppy.c", 784); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h b/soh/src/overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h new file mode 100644 index 000000000..2625aa3b6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_EX_RUPPY_H +#define Z_EN_EX_RUPPY_H + +#include "ultra64.h" +#include "global.h" + +struct EnExRuppy; + +typedef void (*EnExRuppyActionFunc)(struct EnExRuppy*, GlobalContext*); + +typedef struct EnExRuppy { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnExRuppyActionFunc actionFunc; + /* 0x0150 */ s16 colorIdx; + /* 0x0152 */ s16 type; + /* 0x0154 */ s16 invisible; + /* 0x0156 */ s16 timer; + /* 0x0158 */ s16 rupeeValue; + /* 0x015A */ s16 unk_15A; + /* 0x015C */ s16 galleryFlag; + /* 0x0160 */ f32 unk_160; +} EnExRuppy; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c new file mode 100644 index 000000000..3d6a8420a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.c @@ -0,0 +1,943 @@ +/* + * File: z_en_fd.c + * Overlay: ovl_En_Fd + * Description: Flare Dancer (enflamed form) + */ + +#include "z_en_fd.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_fw/object_fw.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_9) + +#define FLG_COREDEAD (0x4000) +#define FLG_COREDONE (0x8000) + +void EnFd_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFd_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFd_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnFd_Run(EnFd* this, GlobalContext* globalCtx); +void EnFd_SpinAndSpawnFire(EnFd* this, GlobalContext* globalCtx); +void EnFd_Reappear(EnFd* this, GlobalContext* globalCtx); +void EnFd_SpinAndGrow(EnFd* this, GlobalContext* globalCtx); +void EnFd_JumpToGround(EnFd* this, GlobalContext* globalCtx); +void EnFd_WaitForCore(EnFd* this, GlobalContext* globalCtx); +void EnFd_UpdateFlames(EnFd* this); +void EnFd_UpdateDots(EnFd* this); +void EnFd_AddEffect(EnFd*, u8, Vec3f*, Vec3f*, Vec3f*, u8, f32, f32); +void EnFd_DrawDots(EnFd* this, GlobalContext* globalCtx); +void EnFd_DrawFlames(EnFd* this, GlobalContext* globalCtx); +void EnFd_Land(EnFd* this, GlobalContext* globalCtx); + +const ActorInit En_Fd_InitVars = { + ACTOR_EN_FD, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_FW, + sizeof(EnFd), + (ActorFunc)EnFd_Init, + (ActorFunc)EnFd_Destroy, + (ActorFunc)EnFd_Update, + (ActorFunc)EnFd_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[12] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040088, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 21, { { 1600, 0, 0 }, 5 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 12, { { 1600, 0, 0 }, 5 }, 400 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 14, { { 800, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 15, { { 1600, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 16, { { 2000, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 17, { { 800, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 18, { { 1600, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 19, { { 2000, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 4, { { 2200, 0, 0 }, 4 }, 400 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 4, { { 5000, 0, 0 }, 4 }, 300 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 8, { { 2200, 0, 0 }, 4 }, 400 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00040008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 8, { { 5000, 0, 0 }, 4 }, 300 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 12, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit2 sColChkInit = { 24, 2, 25, 25, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENFD_ANIM_0, + /* 1 */ ENFD_ANIM_1, + /* 2 */ ENFD_ANIM_2, + /* 3 */ ENFD_ANIM_3, + /* 4 */ ENFD_ANIM_4 +} EnFdAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gFlareDancerCastingFireAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, 0.0f }, + { &gFlareDancerBackflipAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -10.0f }, + { &gFlareDancerGettingUpAnim, 0.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -10.0f }, + { &gFlareDancerChasingAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, -10.0f }, + { &gFlareDancerTwirlAnim, 0.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -10.0f }, +}; + +s32 EnFd_SpawnCore(EnFd* this, GlobalContext* globalCtx) { + if (this->invincibilityTimer != 0) { + return false; + } + + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FW, this->corePos.x, this->corePos.y, + this->corePos.z, 0, this->actor.shape.rot.y, 0, this->runDir) == NULL) { + return false; + } + + this->actor.child->colChkInfo.health = this->actor.colChkInfo.health % 8; + + if (this->actor.child->colChkInfo.health == 0) { + this->actor.child->colChkInfo.health = 8; + } + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { + func_8002DE04(globalCtx, &this->actor, this->actor.child); + } + + this->coreActive = true; + + return true; +} + +void EnFd_SpawnChildFire(EnFd* this, GlobalContext* globalCtx, s16 fireCnt, s16 color) { + s32 i; + + for (i = 0; i < fireCnt; i++) { + s16 angle = (s16)((((i * 360.0f) / fireCnt) * (0x10000 / 360.0f))) + this->actor.yawTowardsPlayer; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FD_FIRE, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, angle, 0, (color << 0xF) | i); + } +} + +void EnFd_SpawnDot(EnFd* this, GlobalContext* globalCtx) { + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + if (this->actionFunc == EnFd_Run || this->actionFunc == EnFd_SpinAndSpawnFire) { + pos.x = this->actor.world.pos.x; + pos.y = this->actor.floorHeight + 4.0f; + pos.z = this->actor.world.pos.z; + accel.x = (Rand_ZeroOne() - 0.5f) * 2.0f; + accel.y = ((Rand_ZeroOne() - 0.5f) * 0.2f) + 0.3f; + accel.z = (Rand_ZeroOne() - 0.5f) * 2.0f; + EnFd_AddEffect(this, FD_EFFECT_FLAME, &pos, &velocity, &accel, 8, 0.6f, 0.2f); + } +} + +/** + * Checks to see if the hammer effect is active, and if it should be applied + */ +s32 EnFd_CheckHammer(EnFd* this, GlobalContext* globalCtx) { + if (this->actionFunc == EnFd_Reappear || this->actionFunc == EnFd_SpinAndGrow || + this->actionFunc == EnFd_JumpToGround || this->actionFunc == EnFd_WaitForCore) { + return false; + } else if (globalCtx->actorCtx.unk_02 != 0 && this->actor.xzDistToPlayer < 300.0f && + this->actor.yDistToPlayer < 60.0f) { + return true; + } else { + return false; + } +} + +s32 EnFd_ColliderCheck(EnFd* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + ColliderInfo* info; + + if (this->collider.base.acFlags & AC_HIT || EnFd_CheckHammer(this, globalCtx)) { + this->collider.base.acFlags &= ~AC_HIT; + if (this->invincibilityTimer != 0) { + return false; + } + info = &this->collider.elements[0].info; + if (info->acHitInfo != NULL && (info->acHitInfo->toucher.dmgFlags & 0x80)) { + return false; + } + + if (!EnFd_SpawnCore(this, globalCtx)) { + return false; + } + this->invincibilityTimer = 30; + this->actor.flags &= ~ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_DAMAGE); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + return true; + } else if (DECR(this->attackTimer) == 0 && this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + if (this->invincibilityTimer != 0) { + return false; + } + + if (this->collider.base.atFlags & AT_BOUNCED) { + return false; + } + this->attackTimer = 30; + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + func_8002F71C(globalCtx, &this->actor, this->actor.speedXZ + 2.0f, this->actor.yawTowardsPlayer, 6.0f); + } + return false; +} + +/** + * Determines if `actor` is within an acceptable range for `this` to be able to "see" `actor` + * `actor` must be within 400 units of `this`, `actor` must be within +/- 40 degrees facing angle + * towards `actor`, and there must not be a collision poly between `this` and `actor` + */ +s32 EnFd_CanSeeActor(EnFd* this, Actor* actor, GlobalContext* globalCtx) { + CollisionPoly* colPoly; + s32 bgId; + Vec3f colPoint; + s16 angle; + s32 pad; + + // Check to see if `actor` is within 400 units of `this` + if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &actor->world.pos) > 400.0f) { + return false; + } + + // Check to see if the angle between this facing angle and `actor` is withing ~40 degrees + angle = (f32)Math_Vec3f_Yaw(&this->actor.world.pos, &actor->world.pos) - this->actor.shape.rot.y; + if (ABS(angle) > 0x1C70) { + return false; + } + + // check to see if the line between `this` and `actor` does not intersect a collision poly + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &actor->world.pos, &colPoint, &colPoly, + true, false, false, true, &bgId)) { + return false; + } + + return true; +} + +Actor* EnFd_FindBomb(EnFd* this, GlobalContext* globalCtx) { + Actor* actor = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + + while (actor != NULL) { + if (actor->params != 0 || actor->parent != NULL) { + actor = actor->next; + continue; + } + + if (actor->id != ACTOR_EN_BOM) { + actor = actor->next; + continue; + } + + if (EnFd_CanSeeActor(this, actor, globalCtx) != 1) { + actor = actor->next; + continue; + } + + return actor; + } + return NULL; +} + +Actor* EnFd_FindPotentialTheat(EnFd* this, GlobalContext* globalCtx) { + Player* player; + Actor* bomb = EnFd_FindBomb(this, globalCtx); + + if (bomb != NULL) { + return bomb; + } + + if (this->attackTimer != 0) { + return NULL; + } + + player = GET_PLAYER(globalCtx); + if (!EnFd_CanSeeActor(this, &player->actor, globalCtx)) { + return NULL; + } + + return &player->actor; +} + +/** + * Creates a delta in `dst` for the position from `this`'s current position to the next + * position in a circle formed by `radius` with center at `this`'s initial position. + */ +Vec3f* EnFd_GetPosAdjAroundCircle(Vec3f* dst, EnFd* this, f32 radius, s16 dir) { + s16 angle; + Vec3f newPos; + + angle = Math_Vec3f_Yaw(&this->actor.home.pos, &this->actor.world.pos) + (dir * 0x1554); // ~30 degrees + newPos.x = (Math_SinS(angle) * radius) + this->actor.home.pos.x; + newPos.z = (Math_CosS(angle) * radius) + this->actor.home.pos.z; + newPos.x -= this->actor.world.pos.x; + newPos.z -= this->actor.world.pos.z; + *dst = newPos; + return dst; +} + +s32 EnFd_ShouldStopRunning(EnFd* this, GlobalContext* globalCtx, f32 radius, s16* runDir) { + CollisionPoly* poly; + s32 bgId; + Vec3f colPoint; + Vec3f pos; + + // Check to see if the next position on the rotation around the circle + // will result in a background collision + EnFd_GetPosAdjAroundCircle(&pos, this, radius, *runDir); + + pos.x += this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z += this->actor.world.pos.z; + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &pos, &colPoint, &poly, true, false, false, + true, &bgId)) { + *runDir = -*runDir; + return true; + } + + if (this->circlesToComplete != 0 || DECR(this->spinTimer) != 0) { + return false; + } + + if (Rand_ZeroOne() > 0.5f) { + *runDir = -*runDir; + } + return true; +} + +void EnFd_Fade(EnFd* this, GlobalContext* globalCtx) { + if (this->invincibilityTimer != 0) { + Math_SmoothStepToF(&this->fadeAlpha, 0.0f, 0.3f, 10.0f, 0.0f); + this->actor.shape.shadowAlpha = this->fadeAlpha; + if (!(this->fadeAlpha >= 0.9f)) { + this->invincibilityTimer = 0; + this->spinTimer = 0; + this->actionFunc = EnFd_WaitForCore; + this->actor.speedXZ = 0.0f; + } + } +} + +void EnFd_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFd* this = (EnFd*)thisx; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFlareDancerSkel, NULL, this->jointTable, this->morphTable, 27); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 32.0f); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colSphs); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0xF), &sColChkInit); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.flags |= ACTOR_FLAG_24; + Actor_SetScale(&this->actor, 0.01f); + this->firstUpdateFlag = true; + this->actor.gravity = -1.0f; + this->runDir = Rand_ZeroOne() < 0.5f ? -1 : 1; + this->actor.naviEnemyId = 0x22; + this->actionFunc = EnFd_Reappear; +} + +void EnFd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFd* this = (EnFd*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnFd_Reappear(EnFd* this, GlobalContext* globalCtx) { + this->actor.world.pos = this->actor.home.pos; + this->actor.params = 0; + this->actor.shape.shadowAlpha = 0xFF; + this->coreActive = false; + this->actor.scale.y = 0.0f; + this->fadeAlpha = 255.0f; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFD_ANIM_0); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_LAUGH); + this->actionFunc = EnFd_SpinAndGrow; +} + +void EnFd_SpinAndGrow(EnFd* this, GlobalContext* globalCtx) { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + this->actor.velocity.y = 6.0f; + this->actor.scale.y = 0.01f; + this->actor.world.rot.y ^= 0x8000; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.speedXZ = 8.0f; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFD_ANIM_1); + this->actionFunc = EnFd_JumpToGround; + } else { + this->actor.scale.y = this->skelAnime.curFrame * (0.01f / this->skelAnime.animLength); + this->actor.shape.rot.y += 0x2000; + this->actor.world.rot.y = this->actor.shape.rot.y; + } +} + +void EnFd_JumpToGround(EnFd* this, GlobalContext* globalCtx) { + if ((this->actor.bgCheckFlags & 1) && !(this->actor.velocity.y > 0.0f)) { + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFD_ANIM_2); + this->actionFunc = EnFd_Land; + } +} + +void EnFd_Land(EnFd* this, GlobalContext* globalCtx) { + Vec3f adjPos; + + Math_SmoothStepToF(&this->skelAnime.playSpeed, 1.0f, 0.1f, 1.0f, 0.0f); + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + this->spinTimer = Rand_S16Offset(60, 90); + this->runRadius = Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->actor.home.pos); + EnFd_GetPosAdjAroundCircle(&adjPos, this, this->runRadius, this->runDir); + this->actor.world.rot.y = Math_FAtan2F(adjPos.x, adjPos.z) * (0x8000 / M_PI); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFD_ANIM_4); + this->actionFunc = EnFd_SpinAndSpawnFire; + } +} + +void EnFd_SpinAndSpawnFire(EnFd* this, GlobalContext* globalCtx) { + f32 deceleration; + f32 tgtSpeed; + f32 rotSpeed; + + if ((this->spinTimer < 31) && (this->invincibilityTimer == 0)) { + func_8002F974(&this->actor, NA_SE_EN_FLAME_FIRE_ATTACK - SFX_FLAG); + } else { + func_8002F974(&this->actor, NA_SE_EN_FLAME_ROLL - SFX_FLAG); + } + + if (DECR(this->spinTimer) != 0) { + this->actor.shape.rot.y += (this->runDir * 0x2000); + if (this->spinTimer == 30 && this->invincibilityTimer == 0) { + if (this->actor.xzDistToPlayer > 160.0f) { + // orange flames + EnFd_SpawnChildFire(this, globalCtx, 8, 0); + } else { + // blue flames + EnFd_SpawnChildFire(this, globalCtx, 8, 1); + } + } + } else { + // slow shape rotation down to meet `this` rotation within ~1.66 degrees + deceleration = this->actor.world.rot.y; + deceleration -= this->actor.shape.rot.y; + rotSpeed = 0.0f; + tgtSpeed = fabsf(deceleration); + deceleration /= tgtSpeed; + Math_ApproachF(&rotSpeed, tgtSpeed, 0.6f, 0x2000); + rotSpeed *= deceleration; + this->actor.shape.rot.y += (s16)rotSpeed; + rotSpeed = fabsf(rotSpeed); + if ((s32)rotSpeed <= 300) { + // ~1.6 degrees + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + if (this->actor.shape.rot.y == this->actor.world.rot.y) { + this->initYawToInitPos = Math_Vec3f_Yaw(&this->actor.home.pos, &this->actor.world.pos); + this->curYawToInitPos = this->runDir < 0 ? 0xFFFF : 0; + this->circlesToComplete = (globalCtx->state.frames & 7) + 2; + this->spinTimer = Rand_S16Offset(30, 120); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFD_ANIM_3); + this->actionFunc = EnFd_Run; + } + } +} + +/** + * Run around in a circle with the center being the initial position, and + * the radius being the distance from the initial position to the nearest + * threat (bomb or player). + */ +void EnFd_Run(EnFd* this, GlobalContext* globalCtx) { + Actor* potentialThreat; + s16 yawToYawTarget; + f32 runRadiusTarget; + Vec3f adjPos; + + if (EnFd_ShouldStopRunning(this, globalCtx, this->runRadius, &this->runDir)) { + if (this->invincibilityTimer == 0) { + this->actor.world.rot.y ^= 0x8000; + this->actor.velocity.y = 6.0f; + this->actor.speedXZ = 0.0f; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFD_ANIM_1); + this->actionFunc = EnFd_JumpToGround; + return; + } + } + + yawToYawTarget = Math_Vec3f_Yaw(&this->actor.home.pos, &this->actor.world.pos) - this->initYawToInitPos; + if (this->runDir > 0) { + if ((u16)this->curYawToInitPos > (u16)(yawToYawTarget)) { + this->circlesToComplete--; + } + } else if ((u16)this->curYawToInitPos < (u16)(yawToYawTarget)) { + this->circlesToComplete--; + } + + if (this->circlesToComplete < 0) { + this->circlesToComplete = 0; + } + this->curYawToInitPos = yawToYawTarget; + + // If there is a bomb out, or if the player exists, set radius to + // the distance to that threat, otherwise default to 200. + potentialThreat = EnFd_FindPotentialTheat(this, globalCtx); + if ((potentialThreat != NULL) && (this->invincibilityTimer == 0)) { + runRadiusTarget = Math_Vec3f_DistXYZ(&this->actor.home.pos, &potentialThreat->world.pos); + } else { + runRadiusTarget = 200.0f; + } + Math_SmoothStepToF(&this->runRadius, runRadiusTarget, 0.3f, 100.0f, 0.0f); + EnFd_GetPosAdjAroundCircle(&adjPos, this, this->runRadius, this->runDir); + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(adjPos.x, adjPos.z) * (0x8000 / M_PI), 4, 0xFA0, 1); + this->actor.world.rot = this->actor.shape.rot; + func_8002F974(&this->actor, NA_SE_EN_FLAME_RUN - SFX_FLAG); + if (this->skelAnime.curFrame == 6.0f || this->skelAnime.curFrame == 13.0f || this->skelAnime.curFrame == 28.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_KICK); + } + Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 0.1f, 1.0f, 0.0f); +} + +/** + * En_Fw will set `this` params when it is done with its action. + * It will set FLG_COREDONE when the core has returned to `this`'s initial + * position, and FLG_COREDEAD when there is no health left + */ +void EnFd_WaitForCore(EnFd* this, GlobalContext* globalCtx) { + if (this->spinTimer != 0) { + this->spinTimer--; + if (this->spinTimer == 0) { + Actor_Kill(&this->actor); + } + } else if (this->actor.params & FLG_COREDONE) { + this->actionFunc = EnFd_Reappear; + } else if (this->actor.params & FLG_COREDEAD) { + this->actor.params = 0; + this->spinTimer = 30; + } +} + +void EnFd_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFd* this = (EnFd*)thisx; + s32 pad; + + if (this->firstUpdateFlag) { + func_800F5ACC(NA_BGM_MINI_BOSS); + this->firstUpdateFlag = false; + } + + if (this->actionFunc != EnFd_Reappear) { + SkelAnime_Update(&this->skelAnime); + EnFd_SpawnDot(this, globalCtx); + } + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { + // has been hookshoted + if (EnFd_SpawnCore(this, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->invincibilityTimer = 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_DAMAGE); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + this->actor.flags &= ~ACTOR_FLAG_13; + } + } else if (this->actionFunc != EnFd_WaitForCore) { + EnFd_ColliderCheck(this, globalCtx); + } + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + EnFd_Fade(this, globalCtx); + this->actionFunc(this, globalCtx); + EnFd_UpdateDots(this); + EnFd_UpdateFlames(this); + if (this->actionFunc != EnFd_Reappear && this->actionFunc != EnFd_SpinAndGrow && + this->actionFunc != EnFd_WaitForCore) { + if (this->attackTimer == 0 && this->invincibilityTimer == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if ((this->actionFunc == EnFd_Run) || (this->actionFunc == EnFd_SpinAndSpawnFire)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +s32 EnFd_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfxP) { + EnFd* this = (EnFd*)thisx; + + if (this->invincibilityTimer != 0) { + switch (limbIndex) { + case 13: + case 21: + *dList = NULL; + break; + } + } + + return false; +} + +void EnFd_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfxP) { + EnFd* this = (EnFd*)thisx; + Vec3f unused0 = { 6800.0f, 0.0f, 0.0f }; + Vec3f unused1 = { 6800.0f, 0.0f, 0.0f }; + Vec3f initialPos = { 0.0f, 0.0f, 0.0f }; + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + s32 i; + + if (limbIndex == 21) { + Matrix_MultVec3f(&initialPos, &this->corePos); + } + + if (limbIndex == 13) { + Matrix_MultVec3f(&initialPos, &this->actor.focus.pos); + } + + if (limbIndex == 3 || limbIndex == 6 || limbIndex == 7 || limbIndex == 10 || limbIndex == 14 || limbIndex == 15 || + limbIndex == 17 || limbIndex == 18 || limbIndex == 20 || limbIndex == 22 || limbIndex == 23 || + limbIndex == 24 || limbIndex == 25 || limbIndex == 26) { + if ((globalCtx->state.frames % 2) != 0) { + for (i = 0; i < 1; i++) { + Matrix_MultVec3f(&initialPos, &pos); + pos.x += (Rand_ZeroOne() - 0.5f) * 20.0f; + pos.y += (Rand_ZeroOne() - 0.5f) * 40.0f; + pos.z += (Rand_ZeroOne() - 0.5f) * 20.0f; + accel.x = (Rand_ZeroOne() - 0.5f) * 0.4f; + accel.y = ((Rand_ZeroOne() - 0.5f) * 0.2f) + 0.6f; + accel.z = (Rand_ZeroOne() - 0.5f) * 0.4f; + EnFd_AddEffect(this, FD_EFFECT_DOT, &pos, &velocity, &accel, 0, 0.006f, 0.0f); + } + } + } + + Collider_UpdateSpheres(limbIndex, &this->collider); +} + +void EnFd_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnFd* this = (EnFd*)thisx; + s32 clampedHealth; + Color_RGBA8 primColors[] = { + { 255, 255, 200, 255 }, + { 200, 200, 200, 255 }, + { 255, 255, 0, 255 }, + }; + Color_RGBA8 envColors[] = { + { 0, 255, 0, 255 }, + { 0, 0, 255, 255 }, + { 255, 0, 0, 255 }, + }; + u32 frames; + s32 pad; + + frames = globalCtx->state.frames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fd.c", 1751); + + Matrix_Push(); + EnFd_DrawDots(this, globalCtx); + EnFd_DrawFlames(this, globalCtx); + Matrix_Pop(); + if (this->actionFunc != EnFd_Reappear && !(this->fadeAlpha < 0.9f)) { + if (1) {} + func_80093D84(globalCtx->state.gfxCtx); + clampedHealth = CLAMP(thisx->colChkInfo.health - 1, 0, 23); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, primColors[clampedHealth / 8].r, primColors[clampedHealth / 8].g, + primColors[clampedHealth / 8].b, (u8)this->fadeAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, envColors[clampedHealth / 8].r, envColors[clampedHealth / 8].g, + envColors[clampedHealth / 8].b, (u8)this->fadeAlpha); + gSPSegment( + POLY_XLU_DISP++, 0x8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, 0xFF - (u8)(frames * 6), 8, 0x40)); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x9, D_80116280); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnFd_OverrideLimbDraw, EnFd_PostLimbDraw, this, + POLY_XLU_DISP); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fd.c", 1822); +} + +void EnFd_AddEffect(EnFd* this, u8 type, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 timer, f32 scale, + f32 scaleStep) { + EnFdEffect* eff = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type != FD_EFFECT_NONE) { + continue; + } + eff->scale = scale; + eff->scaleStep = scaleStep; + eff->initialTimer = eff->timer = timer; + eff->type = type; + eff->pos = *pos; + eff->accel = *accel; + eff->velocity = *velocity; + if (eff->type == FD_EFFECT_DOT) { + eff->color.a = 255; + eff->timer = (s16)(Rand_ZeroOne() * 10.0f); + } + return; + } +} + +void EnFd_UpdateFlames(EnFd* this) { + s16 i; + EnFdEffect* eff = this->effects; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type == FD_EFFECT_FLAME) { + eff->timer--; + if (eff->timer == 0) { + eff->type = FD_EFFECT_NONE; + } + eff->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + eff->accel.z = (Rand_ZeroOne() * 0.4f) - 0.2f; + eff->pos.x += eff->velocity.x; + eff->pos.y += eff->velocity.y; + eff->pos.z += eff->velocity.z; + eff->velocity.x += eff->accel.x; + eff->velocity.y += eff->accel.y; + eff->velocity.z += eff->accel.z; + eff->scale += eff->scaleStep; + } + } +} + +void EnFd_UpdateDots(EnFd* this) { + EnFdEffect* eff = this->effects; + s16 i; + Color_RGBA8 dotColors[] = { + { 255, 128, 0, 0 }, + { 255, 0, 0, 0 }, + { 255, 255, 0, 0 }, + { 255, 0, 0, 0 }, + }; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type == FD_EFFECT_DOT) { + eff->pos.x += eff->velocity.x; + eff->pos.y += eff->velocity.y; + eff->pos.z += eff->velocity.z; + eff->timer++; + eff->velocity.x += eff->accel.x; + eff->velocity.y += eff->accel.y; + eff->velocity.z += eff->accel.z; + + eff->color.r = dotColors[eff->timer % 4].r; + eff->color.g = dotColors[eff->timer % 4].g; + eff->color.b = dotColors[eff->timer % 4].b; + if (eff->color.a > 30) { + eff->color.a -= 30; + } else { + eff->color.a = 0; + eff->type = FD_EFFECT_NONE; + } + } + } +} + +void EnFd_DrawFlames(EnFd* this, GlobalContext* globalCtx) { + static void* dustTextures[] = { + gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex, + }; + s32 firstDone; + s16 i; + s16 idx; + EnFdEffect* eff = this->effects; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fd.c", 1969); + firstDone = false; + if (1) {} + func_80093D84(globalCtx->state.gfxCtx); + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type == FD_EFFECT_FLAME) { + if (!firstDone) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gSPDisplayList(POLY_XLU_DISP++, gFlareDancerDL_7928); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 10, 0, (u8)((this->fadeAlpha / 255.0f) * 255)); + firstDone = true; + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 0, (u8)((this->fadeAlpha / 255.0f) * 255)); + gDPPipeSync(POLY_XLU_DISP++); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fd.c", 2006), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + idx = eff->timer * (8.0f / eff->initialTimer); + gSPSegment(POLY_XLU_DISP++, 0x8, SEGMENTED_TO_VIRTUAL(dustTextures[idx])); + gSPDisplayList(POLY_XLU_DISP++, gFlareDancerSquareParticleDL); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fd.c", 2020); +} + +void EnFd_DrawDots(EnFd* this, GlobalContext* globalCtx) { + s16 i; + s16 firstDone; + EnFdEffect* eff = this->effects; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fd.c", 2034); + + firstDone = false; + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type == FD_EFFECT_DOT) { + if (!firstDone) { + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gFlareDancerDL_79F8); + firstDone = true; + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, eff->color.r, eff->color.g, eff->color.b, + (u8)(eff->color.a * (this->fadeAlpha / 255.0f))); + gDPPipeSync(POLY_XLU_DISP++); + if (1) {} + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fd.c", 2064), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFlareDancerTriangleParticleDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fd.c", 2071); +} diff --git a/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h new file mode 100644 index 000000000..a00b622c9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fd/z_en_fd.h @@ -0,0 +1,53 @@ +#ifndef Z_EN_FD_H +#define Z_EN_FD_H + +#include "ultra64.h" +#include "global.h" + +struct EnFd; + +typedef void (*EnFdActionFunc)(struct EnFd* this, GlobalContext* globalCtx); + +typedef enum { + FD_EFFECT_NONE, + FD_EFFECT_FLAME, + FD_EFFECT_DOT +} FDEffectType; + +typedef struct { + /* 0x0000 */ u8 type; + /* 0x0001 */ u8 timer; + /* 0x0002 */ u8 initialTimer; + /* 0x0004 */ f32 scale; + /* 0x0008 */ f32 scaleStep; + /* 0x000C */ Color_RGBA8 color; + /* 0x0010 */ char unk_10[4]; + /* 0x0014 */ Vec3f pos; + /* 0x0020 */ Vec3f velocity; + /* 0x002C */ Vec3f accel; +} EnFdEffect; // size = 0x38 + +typedef struct EnFd { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnFdActionFunc actionFunc; + /* 0x0194 */ ColliderJntSph collider; + /* 0x01B4 */ ColliderJntSphElement colSphs[12]; + /* 0x04B4 */ u8 coreActive; + /* 0x04B6 */ s16 initYawToInitPos; + /* 0x04B8 */ s16 curYawToInitPos; + /* 0x04BA */ s16 runDir; + /* 0x04BC */ s16 firstUpdateFlag; + /* 0x04BE */ s16 spinTimer; + /* 0x04C0 */ s16 circlesToComplete; + /* 0x04C2 */ s16 invincibilityTimer; + /* 0x04C4 */ s16 attackTimer; + /* 0x04C8 */ f32 runRadius; + /* 0x04CC */ f32 fadeAlpha; + /* 0x04D0 */ Vec3f corePos; + /* 0x04DC */ Vec3s jointTable[27]; + /* 0x057E */ Vec3s morphTable[27]; + /* 0x0620 */ EnFdEffect effects[200]; +} EnFd; // size = 0x31E0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fd_Fire/z_en_fd_fire.c b/soh/src/overlays/actors/ovl_En_Fd_Fire/z_en_fd_fire.c new file mode 100644 index 000000000..442e1ef28 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fd_Fire/z_en_fd_fire.c @@ -0,0 +1,287 @@ +#include "z_en_fd_fire.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnFdFire_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFdFire_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFdFire_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFdFire_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnFdFire_Disappear(EnFdFire* this, GlobalContext* globalCtx); +void func_80A0E70C(EnFdFire* this, GlobalContext* globalCtx); +void EnFdFire_DanceTowardsPlayer(EnFdFire* this, GlobalContext* globalCtx); +void EnFdFire_WaitToDie(EnFdFire* this, GlobalContext* globalCtx); + +const ActorInit En_Fd_Fire_InitVars = { + ACTOR_EN_FD_FIRE, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(EnFdFire), + (ActorFunc)EnFdFire_Init, + (ActorFunc)EnFdFire_Destroy, + (ActorFunc)EnFdFire_Update, + (ActorFunc)EnFdFire_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x08 }, + { 0x0D840008, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 12, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInit = { 1, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +void EnFdFire_UpdatePos(EnFdFire* this, Vec3f* targetPos) { + f32 dist; + f32 xDiff = targetPos->x - this->actor.world.pos.x; + f32 yDiff = targetPos->y - this->actor.world.pos.y; + f32 zDiff = targetPos->z - this->actor.world.pos.z; + + dist = sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)); + if (fabsf(dist) > fabsf(this->actor.speedXZ)) { + this->actor.velocity.x = (xDiff / dist) * this->actor.speedXZ; + this->actor.velocity.z = (zDiff / dist) * this->actor.speedXZ; + } else { + this->actor.velocity.x = 0.0f; + this->actor.velocity.z = 0.0f; + } + + this->actor.velocity.y += this->actor.gravity; + if (!(this->actor.minVelocityY <= this->actor.velocity.y)) { + this->actor.velocity.y = this->actor.minVelocityY; + } +} + +s32 EnFdFire_CheckCollider(EnFdFire* this, GlobalContext* globalCtx) { + if (this->actionFunc == EnFdFire_Disappear) { + return false; + } + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + return true; + } + + if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { + this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; + return true; + } + return false; +} + +void EnFdFire_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFdFire* this = (EnFdFire*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInit); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.gravity = -0.6f; + this->actor.speedXZ = 5.0f; + this->actor.velocity.y = 12.0f; + this->spawnRadius = Math_Vec3f_DistXYZ(&this->actor.world.pos, &player->actor.world.pos); + this->scale = 3.0f; + this->tile2Y = (s16)Rand_ZeroFloat(5.0f) - 25; + this->actionFunc = func_80A0E70C; +} + +void EnFdFire_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFdFire* this = (EnFdFire*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80A0E70C(EnFdFire* this, GlobalContext* globalCtx) { + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f targetPos = this->actor.parent->world.pos; + + targetPos.x += this->spawnRadius * Math_SinS(this->actor.world.rot.y); + targetPos.z += this->spawnRadius * Math_CosS(this->actor.world.rot.y); + EnFdFire_UpdatePos(this, &targetPos); + if (this->actor.bgCheckFlags & 1 && (!(this->actor.velocity.y > 0.0f))) { + this->actor.velocity = velocity; + this->actor.speedXZ = 0.0f; + this->actor.bgCheckFlags &= ~1; + if (this->actor.params & 0x8000) { + this->deathTimer = 200; + this->actionFunc = EnFdFire_DanceTowardsPlayer; + } else { + this->deathTimer = 300; + this->actionFunc = EnFdFire_WaitToDie; + } + } +} + +void EnFdFire_WaitToDie(EnFdFire* this, GlobalContext* globalCtx) { + if (DECR(this->deathTimer) == 0) { + this->actionFunc = EnFdFire_Disappear; + } +} + +void EnFdFire_DanceTowardsPlayer(EnFdFire* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 angles[] = { + 0.0f, 210.0f, 60.0f, 270.0f, 120.0f, 330.0f, 180.0f, 30.0f, 240.0f, 90.0f, 300.0f, 150.0f, + }; + Vec3f pos; + s16 idx; + + idx = ((globalCtx->state.frames / 10) + (this->actor.params & 0x7FFF)) % ARRAY_COUNT(angles); + pos = player->actor.world.pos; + pos.x += 120.0f * sinf(angles[idx]); + pos.z += 120.0f * cosf(angles[idx]); + + if (DECR(this->deathTimer) == 0) { + this->actionFunc = EnFdFire_Disappear; + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &pos), 8, 0xFA0, 1); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.4f, 1.0f, 0.0f); + if (this->actor.speedXZ < 0.1f) { + this->actor.speedXZ = 5.0f; + } + func_8002D868(&this->actor); + } +} + +void EnFdFire_Disappear(EnFdFire* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.6f, 9.0f, 0.0f); + func_8002D868(&this->actor); + Math_SmoothStepToF(&this->scale, 0.0f, 0.3f, 0.1f, 0.0f); + this->actor.shape.shadowScale = 20.0f; + this->actor.shape.shadowScale *= (this->scale / 3.0f); + if (!(this->scale > 0.01f)) { + Actor_Kill(&this->actor); + } +} + +void EnFdFire_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFdFire* this = (EnFdFire*)thisx; + s32 pad; + + if (this->actionFunc != EnFdFire_Disappear) { + if ((this->actor.parent->update == NULL) || EnFdFire_CheckCollider(this, globalCtx)) { + this->actionFunc = EnFdFire_Disappear; + } + } + + func_8002D7EC(&this->actor); + this->actionFunc(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 12.0f, 10.0f, 0.0f, 5); + + if (this->actionFunc != EnFdFire_Disappear) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnFdFire_Draw(Actor* thisx, GlobalContext* globalCtx) { + Color_RGBA8 primColors[] = { + { 255, 255, 0, 255 }, + { 255, 255, 255, 255 }, + }; + Color_RGBA8 envColors[] = { + { 255, 10, 0, 255 }, + { 0, 10, 255, 255 }, + }; + s32 pad; + EnFdFire* this = (EnFdFire*)thisx; + Vec3f scale = { 0.0f, 0.0f, 0.0f }; + Vec3f sp90 = { 0.0f, 0.0f, 0.0f }; + s16 sp8E; + f32 sp88; + f32 sp84; + f32 sp80; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fd_fire.c", 572); + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + sp8E = Math_Vec3f_Yaw(&scale, &this->actor.velocity) - Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)); + sp84 = fabsf(Math_CosS(sp8E)); + sp88 = Math_SinS(sp8E); + sp80 = Math_Vec3f_DistXZ(&scale, &this->actor.velocity) / 1.5f; + if (1) {} + if (1) {} + if (1) {} + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(((sp88 * -10.0f) * sp80) * (M_PI / 180.0f), MTXMODE_APPLY); + scale.x = scale.y = scale.z = this->scale * 0.001f; + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + sp84 = sp80 * ((0.01f * -15.0f) * sp84) + 1.0f; + if (sp84 < 0.1f) { + sp84 = 0.1f; + } + Matrix_Scale(1.0f, sp84, 1.0f / sp84, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fd_fire.c", 623), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + globalCtx->state.frames * this->tile2Y, 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, primColors[((this->actor.params & 0x8000) >> 0xF)].r, + primColors[((this->actor.params & 0x8000) >> 0xF)].g, + primColors[((this->actor.params & 0x8000) >> 0xF)].b, + primColors[((this->actor.params & 0x8000) >> 0xF)].a); + gDPSetEnvColor(POLY_XLU_DISP++, envColors[((this->actor.params & 0x8000) >> 0xF)].r, + envColors[((this->actor.params & 0x8000) >> 0xF)].g, + envColors[((this->actor.params & 0x8000) >> 0xF)].b, + envColors[((this->actor.params & 0x8000) >> 0xF)].a); + gDPPipeSync(POLY_XLU_DISP++); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fd_fire.c", 672); +} diff --git a/soh/src/overlays/actors/ovl_En_Fd_Fire/z_en_fd_fire.h b/soh/src/overlays/actors/ovl_En_Fd_Fire/z_en_fd_fire.h new file mode 100644 index 000000000..7de1c53fe --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fd_Fire/z_en_fd_fire.h @@ -0,0 +1,22 @@ +#ifndef Z_EN_FD_FIRE_H +#define Z_EN_FD_FIRE_H + +#include "ultra64.h" +#include "global.h" + +struct EnFdFire; + +typedef void (*EnFdFireActionFunc)(struct EnFdFire* this, GlobalContext* globalCtx); + +typedef struct EnFdFire { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnFdFireActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; + /* 0x019C */ f32 spawnRadius; + /* 0x01A0 */ f32 scale; + /* 0x01A4 */ char unk_1A4[0x2]; + /* 0x01A6 */ s16 deathTimer; + /* 0x01A8 */ s16 tile2Y; +} EnFdFire; // size = 0x01AC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.c b/soh/src/overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.c new file mode 100644 index 000000000..a94a141b6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.c @@ -0,0 +1,757 @@ +/* + * File: z_en_fhg_fire.c + * Overlay: ovl_En_Fhg_Fire + * Description: Phantom Ganon's Lighting Attack + */ + +#include "z_en_fhg_fire.h" +#include "objects/object_fhg/object_fhg.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h" +#include "overlays/actors/ovl_En_fHG/z_en_fhg.h" +#include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ STRIKE_INIT, + /* 10 */ STRIKE_BURST = 10, + /* 11 */ STRIKE_TRAILS +} StrikeMode; + +typedef enum { + /* 0 */ TRAIL_INIT, + /* 1 */ TRAIL_APPEAR, + /* 2 */ TRAIL_DISSIPATE +} TrailMode; + +typedef enum { + /* 0 */ BALL_FIZZLE, + /* 1 */ BALL_BURST, + /* 2 */ BALL_IMPACT +} BallKillMode; + +void EnFhgFire_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFhgFire_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFhgFire_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFhgFire_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnFhgFire_LightningStrike(EnFhgFire* this, GlobalContext* globalCtx); +void EnFhgFire_LightningTrail(EnFhgFire* this, GlobalContext* globalCtx); +void EnFhgFire_LightningShock(EnFhgFire* this, GlobalContext* globalCtx); +void EnFhgFire_LightningBurst(EnFhgFire* this, GlobalContext* globalCtx); +void EnFhgFire_SpearLight(EnFhgFire* this, GlobalContext* globalCtx); +void EnFhgFire_EnergyBall(EnFhgFire* this, GlobalContext* globalCtx); +void EnFhgFire_PhantomWarp(EnFhgFire* this, GlobalContext* globalCtx); + +const ActorInit En_Fhg_Fire_InitVars = { + 0, + ACTORCAT_BOSS, + FLAGS, + OBJECT_FHG, + sizeof(EnFhgFire), + (ActorFunc)EnFhgFire_Init, + (ActorFunc)EnFhgFire_Destroy, + (ActorFunc)EnFhgFire_Update, + (ActorFunc)EnFhgFire_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK6, + { 0x00100700, 0x03, 0x20 }, + { 0x0D900700, 0x00, 0x00 }, + TOUCH_ON, + BUMP_ON, + OCELEM_ON, + }, + { 20, 30, 10, { 0, 0, 0 } }, +}; + +void EnFhgFire_SetUpdate(EnFhgFire* this, EnFhgFireUpdateFunc updateFunc) { + this->updateFunc = updateFunc; +} + +void EnFhgFire_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnFhgFire* this = (EnFhgFire*)thisx; + Player* player = GET_PLAYER(globalCtx); + + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + if ((this->actor.params == FHGFIRE_LIGHTNING_SHOCK) || (this->actor.params == FHGFIRE_LIGHTNING_BURST) || + (this->actor.params == FHGFIRE_ENERGY_BALL)) { + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + } + this->fwork[FHGFIRE_ALPHA] = 200.0f; + Actor_SetScale(&this->actor, 0.0f); + + if (this->actor.params == FHGFIRE_LIGHTNING_STRIKE) { + EnFhgFire_SetUpdate(this, EnFhgFire_LightningStrike); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER); + } else if (this->actor.params >= FHGFIRE_LIGHTNING_TRAIL) { + EnFhgFire_SetUpdate(this, EnFhgFire_LightningTrail); + this->actor.shape.rot = this->actor.world.rot; + } + if (this->actor.params == FHGFIRE_LIGHTNING_SHOCK) { + this->actor.draw = NULL; + EnFhgFire_SetUpdate(this, EnFhgFire_LightningShock); + this->actor.speedXZ = 30.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_SPARK); + } else if (this->actor.params == FHGFIRE_LIGHTNING_BURST) { + EnFhgFire_SetUpdate(this, EnFhgFire_LightningBurst); + this->fwork[FHGFIRE_ALPHA] = 255.0f; + this->work[FHGFIRE_TIMER] = 32; + this->work[FHGFIRE_FX_TIMER] = 50; + this->lensFlareTimer = 10; + + this->fwork[FHGFIRE_BURST_SCALE] = this->actor.world.rot.x / 100.0f; + this->collider.dim.radius = this->actor.world.rot.x * 0.13f; + this->collider.dim.height = this->actor.world.rot.x * 0.13f; + this->collider.dim.yShift = 0; + } else if (this->actor.params == FHGFIRE_SPEAR_LIGHT) { + osSyncPrintf("yari hikari ct 1\n"); // "light spear" + EnFhgFire_SetUpdate(this, EnFhgFire_SpearLight); + osSyncPrintf("yari hikari ct 2\n"); + this->work[FHGFIRE_TIMER] = this->actor.world.rot.x; + this->work[FHGFIRE_FIRE_MODE] = this->actor.world.rot.y; + } else if ((this->actor.params == FHGFIRE_WARP_EMERGE) || (this->actor.params == FHGFIRE_WARP_RETREAT) || + (this->actor.params == FHGFIRE_WARP_DEATH)) { + Actor_SetScale(&this->actor, 7.0f); + EnFhgFire_SetUpdate(this, EnFhgFire_PhantomWarp); + if (this->actor.params == FHGFIRE_WARP_DEATH) { + this->work[FHGFIRE_TIMER] = 440; + this->actor.scale.z = 1.0f; + } else { + this->work[FHGFIRE_TIMER] = 76; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FANTOM_WARP_S); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FANTOM_WARP_S2); + } + } else if (this->actor.params == FHGFIRE_ENERGY_BALL) { + f32 dxL; + f32 dyL; + f32 dzL; + f32 dxzL; + + this->actor.speedXZ = (this->actor.world.rot.x == 0) ? 8.0f : 3.0f; + EnFhgFire_SetUpdate(this, EnFhgFire_EnergyBall); + + this->work[FHGFIRE_TIMER] = 70; + this->work[FHGFIRE_FX_TIMER] = 2; + + dxL = player->actor.world.pos.x - this->actor.world.pos.x; + dyL = player->actor.world.pos.y + 30.0f - this->actor.world.pos.y; + dzL = player->actor.world.pos.z - this->actor.world.pos.z; + this->actor.world.rot.y = Math_FAtan2F(dxL, dzL) * (0x8000 / M_PI); + dxzL = sqrtf(SQ(dxL) + SQ(dzL)); + this->actor.world.rot.x = Math_FAtan2F(dyL, dxzL) * (0x8000 / M_PI); + this->collider.dim.radius = 40; + this->collider.dim.height = 50; + this->collider.dim.yShift = -25; + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, 255); + } +} + +void EnFhgFire_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnFhgFire* this = (EnFhgFire*)thisx; + + if ((this->actor.params == FHGFIRE_LIGHTNING_SHOCK) || (this->actor.params == FHGFIRE_LIGHTNING_BURST) || + (this->actor.params == FHGFIRE_ENERGY_BALL)) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } + + if (this->actor.params == FHGFIRE_ENERGY_BALL) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + } +} + +void EnFhgFire_LightningStrike(EnFhgFire* this, GlobalContext* globalCtx) { + Camera* camera = Gameplay_GetCamera(globalCtx, 0); + s16 i; + + switch (this->work[FHGFIRE_FIRE_MODE]) { + case STRIKE_INIT: + this->work[FHGFIRE_FIRE_MODE] = STRIKE_BURST; + this->work[FHGFIRE_TIMER] = 7; + break; + case STRIKE_BURST: + this->actor.shape.rot.y = + Camera_GetInputDirYaw(camera) + 0x8000 * (this->work[FHGFIRE_VARIANCE_TIMER] & 0xFF); + Math_ApproachF(&this->fwork[FHGFIRE_SCALE], 1.0f, 1.0f, 0.2f); + + if (this->work[FHGFIRE_TIMER] == 0) { + this->work[FHGFIRE_FIRE_MODE] = STRIKE_TRAILS; + this->actor.shape.rot.z += 0x8000; + this->work[FHGFIRE_TIMER] = 37; + this->actor.world.pos.y -= 200.0f; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 500, 0, 0, + FHGFIRE_LIGHTNING_BURST); + { + Vec3f sp7C; + Vec3f sp70 = { 0.0f, -1.0f, 0.0f }; + + for (i = 0; i < 35; i++) { + sp7C.x = Rand_CenteredFloat(30.f); + sp7C.y = Rand_ZeroFloat(5.0f) + 3.0f; + sp7C.z = Rand_CenteredFloat(30.f); + sp70.y = -0.2f; + EffectSsFhgFlash_SpawnLightBall(globalCtx, &this->actor.world.pos, &sp7C, &sp70, + (s16)(Rand_ZeroOne() * 100.0f) + 240, FHGFLASH_LIGHTBALL_GREEN); + } + } + func_80033E88(&this->actor, globalCtx, 4, 10); + } + + break; + case STRIKE_TRAILS: + this->actor.shape.rot.y = + Camera_GetInputDirYaw(camera) + (this->work[FHGFIRE_VARIANCE_TIMER] & 0xFF) * 0x8000; + + Math_ApproachF(&this->fwork[FHGFIRE_SCALE], 0.0f, 1.0f, 0.2f); + if (this->work[FHGFIRE_TIMER] == 30) { + s16 randY = (Rand_ZeroOne() < 0.5f) ? 0x1000 : 0; + + for (i = 0; i < 8; i++) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, + (i * 0x2000) + randY, 0x4000, FHGFIRE_LIGHTNING_TRAIL + i); + } + + for (i = 0; i < 8; i++) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, + (i * 0x2000) + randY, 0, FHGFIRE_LIGHTNING_SHOCK); + } + } + + if (this->work[FHGFIRE_TIMER] == 0) { + Actor_Kill(&this->actor); + } + } + + Actor_SetScale(&this->actor, this->fwork[FHGFIRE_SCALE]); +} + +void EnFhgFire_LightningTrail(EnFhgFire* this, GlobalContext* globalCtx) { + osSyncPrintf("FF MOVE 1\n"); + this->actor.shape.rot.x += (s16)(Rand_ZeroOne() * 4000.0f) + 0x4000; + + switch (this->work[FHGFIRE_FIRE_MODE]) { + case TRAIL_INIT: + this->work[FHGFIRE_FIRE_MODE] = TRAIL_APPEAR; + this->work[FHGFIRE_TIMER] = (s16)(Rand_ZeroOne() * 7.0f) + 7; + case TRAIL_APPEAR: + Math_ApproachF(&this->fwork[FHGFIRE_SCALE], 1.7f, 1.0f, 0.34f); + + if (this->work[FHGFIRE_TIMER] == 0) { + this->work[FHGFIRE_FIRE_MODE] = TRAIL_DISSIPATE; + this->work[FHGFIRE_TIMER] = 10; + this->actor.world.pos.z += Math_SinS(this->actor.shape.rot.y) * -200.0f * this->fwork[FHGFIRE_SCALE]; + this->actor.world.pos.x += Math_CosS(this->actor.shape.rot.y) * 200.0f * this->fwork[FHGFIRE_SCALE]; + this->actor.shape.rot.y += 0x8000; + } + break; + case TRAIL_DISSIPATE: + Math_ApproachZeroF(&this->fwork[FHGFIRE_SCALE], 1.0f, 0.34f); + if (this->work[FHGFIRE_TIMER] == 0) { + Actor_Kill(&this->actor); + } + break; + } + + Actor_SetScale(&this->actor, this->fwork[FHGFIRE_SCALE]); + if (this->actor.scale.x > 1.0f) { + this->actor.scale.x = 1.0f; + } + + osSyncPrintf("FF MOVE 2\n"); +} + +void EnFhgFire_LightningShock(EnFhgFire* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f pos; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_HIT_THUNDER); + } + + if (Rand_ZeroOne() < 0.5f) { + pos = this->actor.world.pos; + pos.y -= 20.0f; + EffectSsFhgFlash_SpawnShock(globalCtx, &this->actor, &pos, 200, FHGFLASH_SHOCK_NO_ACTOR); + } + + Actor_MoveForward(&this->actor); + Collider_UpdateCylinder(&this->actor, &this->collider); + if (player->invincibilityTimer == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 1); + if (this->actor.bgCheckFlags & 8) { + Actor_Kill(&this->actor); + } +} + +void EnFhgFire_LightningBurst(EnFhgFire* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + globalCtx->envCtx.fillScreen = true; + this->actor.shape.rot.y += 0x1000; + + if (this->work[FHGFIRE_FX_TIMER] == 49) { + globalCtx->envCtx.unk_BF = 1; + globalCtx->envCtx.unk_D6 = 0xFF; + } + if (this->work[FHGFIRE_FX_TIMER] == 31) { + globalCtx->envCtx.unk_BF = 0x00; + globalCtx->envCtx.unk_D6 = 0x14; + } + if (this->work[FHGFIRE_FX_TIMER] >= 48) { + globalCtx->envCtx.screenFillColor[0] = globalCtx->envCtx.screenFillColor[1] = + globalCtx->envCtx.screenFillColor[2] = 255; + + if ((this->work[FHGFIRE_TIMER] % 2) != 0) { + globalCtx->envCtx.screenFillColor[3] = 70; + } else { + globalCtx->envCtx.screenFillColor[3] = 0; + } + } else { + globalCtx->envCtx.screenFillColor[3] = 0; + } + + if (this->work[FHGFIRE_TIMER] <= 20) { + Math_ApproachZeroF(&this->fwork[FHGFIRE_ALPHA], 1.0f, 45.0f); + Math_ApproachZeroF(&this->fwork[FHGFIRE_SCALE], 1.0f, 0.5f); + } else { + Math_ApproachF(&this->fwork[FHGFIRE_SCALE], this->fwork[FHGFIRE_BURST_SCALE], 0.5f, 3.0f); + } + + Actor_SetScale(&this->actor, this->fwork[FHGFIRE_SCALE]); + if (this->fwork[FHGFIRE_BURST_SCALE] > 3.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + if (player->invincibilityTimer == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + if (this->work[FHGFIRE_TIMER] == 0) { + Actor_Kill(&this->actor); + globalCtx->envCtx.fillScreen = false; + } + + if (this->lensFlareTimer != 0) { + this->lensFlareTimer--; + this->lensFlareOn = true; + Math_ApproachF(&this->lensFlareScale, 40.0f, 0.3f, 10.0f); + } else { + Math_ApproachZeroF(&this->lensFlareScale, 1.0f, 5.0f); + if (this->lensFlareScale == 0.0f) { + this->lensFlareOn = false; + } + } + + gCustomLensFlareOn = this->lensFlareOn; + gCustomLensFlarePos = this->actor.world.pos; + gLensFlareScale = this->lensFlareScale; + gLensFlareColorIntensity = 10.0f; + gLensFlareScreenFillAlpha = 0; +} + +void EnFhgFire_SpearLight(EnFhgFire* this, GlobalContext* globalCtx) { + BossGanondrof* bossGnd; + s16 i; + + osSyncPrintf("yari hikari 1\n"); + bossGnd = (BossGanondrof*)this->actor.parent; + if ((this->work[FHGFIRE_VARIANCE_TIMER] % 2) != 0) { + Actor_SetScale(&this->actor, 6.0f); + } else { + Actor_SetScale(&this->actor, 5.25f); + } + + this->actor.world.pos = bossGnd->spearTip; + this->actor.shape.rot.z += (s16)(Rand_ZeroOne() * 0x4E20) + 0x4000; + + osSyncPrintf("yari hikari 2\n"); + if (this->work[FHGFIRE_FIRE_MODE] == FHGFIRE_LIGHT_GREEN) { + Vec3f ballPos; + Vec3f ballVel = { 0.0f, 0.0f, 0.0f }; + Vec3f ballAccel = { 0.0f, 0.0f, 0.0f }; + + osSyncPrintf("FLASH !!\n"); + + for (i = 0; i < 2; i++) { + ballPos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + ballPos.y = Rand_CenteredFloat(20.0f) + this->actor.world.pos.y; + ballPos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + ballAccel.y = -0.08f; + + EffectSsFhgFlash_SpawnLightBall(globalCtx, &ballPos, &ballVel, &ballAccel, + (s16)(Rand_ZeroOne() * 80.0f) + 150, FHGFLASH_LIGHTBALL_GREEN); + } + } + + if (this->work[FHGFIRE_TIMER] == 0) { + Actor_Kill(&this->actor); + } +} + +void EnFhgFire_EnergyBall(EnFhgFire* this, GlobalContext* globalCtx) { + f32 dxL; + f32 dyL; + f32 dzL; + f32 dxzL; + f32 dxPG; + f32 dyPG; + f32 dzPG; + u8 killMode = BALL_FIZZLE; + u8 canBottleReflect1; + Player* player = GET_PLAYER(globalCtx); + + if (this->work[FHGFIRE_KILL_TIMER] != 0) { + this->work[FHGFIRE_KILL_TIMER]--; + if (this->work[FHGFIRE_KILL_TIMER] == 0) { + Actor_Kill(&this->actor); + return; + } + } else { + s32 canBottleReflect2; + BossGanondrof* bossGnd = (BossGanondrof*)this->actor.parent; + + dxPG = bossGnd->targetPos.x - this->actor.world.pos.x; + dyPG = bossGnd->targetPos.y - this->actor.world.pos.y; + dzPG = bossGnd->targetPos.z - this->actor.world.pos.z; + dxL = player->actor.world.pos.x - this->actor.world.pos.x; + dyL = player->actor.world.pos.y + 40.0f - this->actor.world.pos.y; + dzL = player->actor.world.pos.z - this->actor.world.pos.z; + func_8002D908(&this->actor); + func_8002D7EC(&this->actor); + if (this->work[FHGFIRE_VARIANCE_TIMER] & 1) { + Actor_SetScale(&this->actor, 6.0f); + } else { + Actor_SetScale(&this->actor, 5.25f); + } + this->actor.shape.rot.z += (s16)(Rand_ZeroOne() * 0x4E20) + 0x4000; + { + u8 lightBallColor1 = FHGFLASH_LIGHTBALL_GREEN; + s16 i1; + Vec3f spD4; + Vec3f spC8 = { 0.0f, 0.0f, 0.0f }; + Vec3f spBC = { 0.0f, 0.0f, 0.0f }; + + if (this->work[FHGFIRE_FIRE_MODE] >= FHGFIRE_LIGHT_BLUE) { + lightBallColor1 = FHGFLASH_LIGHTBALL_LIGHTBLUE; + } + for (i1 = 0; i1 < 3; i1++) { + spD4.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + spD4.y = Rand_CenteredFloat(20.0f) + this->actor.world.pos.y; + spD4.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + spBC.y = -0.08f; + EffectSsFhgFlash_SpawnLightBall(globalCtx, &spD4, &spC8, &spBC, (s16)(Rand_ZeroOne() * 80.0f) + 150, + lightBallColor1); + } + } + switch (this->work[FHGFIRE_FIRE_MODE]) { + case FHGFIRE_LIGHT_GREEN: + canBottleReflect1 = + ((player->stateFlags1 & 2) && + (ABS((s16)(player->actor.shape.rot.y - (s16)(bossGnd->actor.yawTowardsPlayer + 0x8000))) < + 0x2000) && + (sqrtf(SQ(dxL) + SQ(dyL) + SQ(dzL)) <= 25.0f)) + ? true + : false; + if ((this->collider.base.acFlags & AC_HIT) || canBottleReflect1) { + ColliderInfo* hurtbox = this->collider.info.acHitInfo; + s16 i2; + Vec3f spA8; + Vec3f sp9C = { 0.0f, -0.5f, 0.0f }; + s16 angleModX; + s16 angleModY; + + for (i2 = 0; i2 < 30; i2++) { + spA8.x = Rand_CenteredFloat(20.0f); + spA8.y = Rand_CenteredFloat(20.0f); + spA8.z = Rand_CenteredFloat(20.0f); + EffectSsFhgFlash_SpawnLightBall(globalCtx, &this->actor.world.pos, &spA8, &sp9C, + (s16)(Rand_ZeroOne() * 25.0f) + 50, FHGFLASH_LIGHTBALL_GREEN); + } + canBottleReflect2 = canBottleReflect1; + if (!canBottleReflect2 && (hurtbox->toucher.dmgFlags & 0x00100000)) { + killMode = BALL_IMPACT; + Audio_PlaySoundGeneral(NA_SE_IT_SHIELD_REFLECT_MG, &player->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + func_800AA000(this->actor.xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + } else { + if (bossGnd->flyMode == GND_FLY_NEUTRAL) { + angleModX = Rand_CenteredFloat(0x2000); + angleModY = Rand_CenteredFloat(0x2000); + this->actor.speedXZ = 15.0f; + } else { + angleModX = 0; + angleModY = 0; + this->work[FHGFIRE_RETURN_COUNT]++; + if ((this->work[FHGFIRE_RETURN_COUNT] > 3) && (Rand_ZeroOne() < 0.5f)) { + this->work[FHGFIRE_RETURN_COUNT] = 100; + } + + if (!canBottleReflect2 && (player->swordAnimation >= 24)) { + this->actor.speedXZ = 20.0f; + this->work[FHGFIRE_RETURN_COUNT] = 4; + } else { + this->actor.speedXZ += 1.0f; + } + } + this->actor.world.rot.y = (s16)(Math_FAtan2F(dxPG, dzPG) * (0x8000 / M_PI)) + angleModY; + this->actor.world.rot.x = + (s16)(Math_FAtan2F(dyPG, sqrtf((dxPG * dxPG) + (dzPG * dzPG))) * (0x8000 / M_PI)) + + angleModX; + this->work[FHGFIRE_FIRE_MODE] = FHGFIRE_LIGHT_BLUE; + this->work[FHGFIRE_FX_TIMER] = 2; + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_REFLECT_MG, &player->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + func_800AA000(this->actor.xyzDistToPlayerSq, 0xB4, 0x14, 0x64); + } + } else if (sqrtf(SQ(dxL) + SQ(dyL) + SQ(dzL)) <= 25.0f) { + killMode = BALL_BURST; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_HIT_THUNDER); + if ((bossGnd->flyMode >= GND_FLY_VOLLEY) && (this->work[FHGFIRE_RETURN_COUNT] >= 2)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAUGH); + } + func_8002F698(globalCtx, &this->actor, 3.0f, this->actor.world.rot.y, 0.0f, 3, 0x10); + } + break; + case FHGFIRE_LIGHT_BLUE: + if ((bossGnd->flyMode == GND_FLY_RETURN) && (this->work[FHGFIRE_RETURN_COUNT] < 100)) { + this->actor.world.rot.y = Math_FAtan2F(dxPG, dzPG) * (0x8000 / M_PI); + if ((sqrtf(SQ(dxPG) + SQ(dzPG)) < (150.0f + (this->actor.speedXZ * 8.0f)))) { + this->work[FHGFIRE_FIRE_MODE] = FHGFIRE_LIGHT_REFLECT; + bossGnd->returnSuccess = true; + this->work[FHGFIRE_TIMER] = 8; + } + } else { + if (this->work[FHGFIRE_RETURN_COUNT] >= 100) { + if ((sqrtf(SQ(dxPG) + SQ(dyPG) + SQ(dzPG)) < 100.0f)) { + bossGnd->returnSuccess = true; + } + this->actor.world.rot.y = Math_FAtan2F(dxPG, dzPG) * (0x8000 / M_PI); + this->actor.world.rot.x = Math_FAtan2F(dyPG, sqrtf(SQ(dxPG) + SQ(dzPG))) * (0x8000 / M_PI); + } + if ((fabsf(dxPG) < 30.0f) && (fabsf(dzPG) < 30.0f) && (fabsf(dyPG) < 45.0f)) { + killMode = BALL_IMPACT; + bossGnd->returnCount = this->work[FHGFIRE_RETURN_COUNT] + 1; + Audio_PlaySoundGeneral(NA_SE_EN_FANTOM_HIT_THUNDER, &bossGnd->actor.projectedPos, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + Audio_PlaySoundGeneral(NA_SE_EN_FANTOM_DAMAGE, &bossGnd->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } + break; + case FHGFIRE_LIGHT_REFLECT: + if (this->work[FHGFIRE_TIMER] == 0) { + s16 i3; + Vec3f sp88; + Vec3f sp7C = { 0.0f, -0.5f, 0.0f }; + + for (i3 = 0; i3 < 30; i3++) { + sp88.x = Rand_CenteredFloat(20.0f); + sp88.y = Rand_CenteredFloat(20.0f); + sp88.z = Rand_CenteredFloat(20.0f); + EffectSsFhgFlash_SpawnLightBall(globalCtx, &this->actor.world.pos, &sp88, &sp7C, + (s16)(Rand_ZeroOne() * 40.0f) + 80, FHGFLASH_LIGHTBALL_GREEN); + } + this->actor.world.rot.y = Math_FAtan2F(dxL, dzL) * (0x8000 / M_PI); + dxzL = sqrtf(SQ(dxL) + SQ(dzL)); + this->actor.world.rot.x = Math_FAtan2F(dyL, dxzL) * (0x8000 / M_PI); + this->work[FHGFIRE_FIRE_MODE] = FHGFIRE_LIGHT_GREEN; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_REFLECT_MG); + this->actor.speedXZ += 2.0f; + } + break; + } + + osSyncPrintf("F_FIRE_MODE %d\n", this->work[FHGFIRE_FIRE_MODE]); + osSyncPrintf("fly_mode %d\n", bossGnd->flyMode); + if (this->work[FHGFIRE_FX_TIMER] == 0) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 7); + if ((this->actor.bgCheckFlags & 0x19) || killMode) { + u8 lightBallColor2 = FHGFLASH_LIGHTBALL_GREEN; + s16 i4; + Vec3f sp6C; + Vec3f sp60 = { 0.0f, -1.0f, 0.0f }; + + if (this->work[FHGFIRE_FIRE_MODE] > FHGFIRE_LIGHT_GREEN) { + lightBallColor2 = FHGFLASH_LIGHTBALL_LIGHTBLUE; + } + for (i4 = 0; i4 < 30; i4++) { + sp6C.x = Rand_CenteredFloat(20.0f); + sp6C.y = Rand_CenteredFloat(20.0f); + sp6C.z = Rand_CenteredFloat(20.0f); + sp60.y = -0.1f; + EffectSsFhgFlash_SpawnLightBall(globalCtx, &this->actor.world.pos, &sp6C, &sp60, + (s16)(Rand_ZeroOne() * 50.0f) + 100, lightBallColor2); + } + if (killMode == BALL_BURST) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + this->actor.world.pos.x, player->actor.world.pos.y + 20.0f, + this->actor.world.pos.z, 0xC8, 0, 0, FHGFIRE_LIGHTNING_BURST); + } + bossGnd->flyMode = GND_FLY_NEUTRAL; + this->work[FHGFIRE_KILL_TIMER] = 30; + this->actor.draw = NULL; + if (killMode == BALL_FIZZLE) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND); + } + return; + } else { + Collider_UpdateCylinder(&this->actor, &this->collider); + osSyncPrintf("BEFORE setAC %d\n", this->collider.base.shape); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + osSyncPrintf("AFTER setAC\n"); + } + } + Lights_PointNoGlowSetInfo(&this->lightInfo, (s16)this->actor.world.pos.x, (s16)this->actor.world.pos.y, + (s16)this->actor.world.pos.z, 255, 255, 255, 200); + if (this->actor.speedXZ > 20.0f) { + this->actor.speedXZ = 20.0f; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_FIRE - SFX_FLAG); + // "Why ah ah ah ah" + osSyncPrintf("なぜだああああああああ %d\n", this->work[FHGFIRE_VARIANCE_TIMER]); + } +} + +void EnFhgFire_PhantomWarp(EnFhgFire* this, GlobalContext* globalCtx) { + EnfHG* horse = (EnfHG*)this->actor.parent; + f32 scrollDirection; + + this->fwork[FHGFIRE_WARP_TEX_1_X] += 25.0f * this->fwork[FHGFIRE_WARP_TEX_SPEED]; + this->fwork[FHGFIRE_WARP_TEX_1_Y] -= 40.0f * this->fwork[FHGFIRE_WARP_TEX_SPEED]; + this->fwork[FHGFIRE_WARP_TEX_2_X] += 5.0f * this->fwork[FHGFIRE_WARP_TEX_SPEED]; + this->fwork[FHGFIRE_WARP_TEX_2_Y] -= 30.0f * this->fwork[FHGFIRE_WARP_TEX_SPEED]; + + if (this->actor.params == FHGFIRE_WARP_DEATH) { + if (this->work[FHGFIRE_TIMER] > 70) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FANTOM_WARP_L - SFX_FLAG); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FANTOM_WARP_L2 - SFX_FLAG); + } + + if (this->work[FHGFIRE_TIMER] == 70) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FANTOM_WARP_S); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FANTOM_WARP_S2); + } + } + + if (this->work[FHGFIRE_TIMER] > 50) { + scrollDirection = 1.0f; + if (this->actor.params > FHGFIRE_WARP_EMERGE) { + scrollDirection = -1.0f; + } + Math_ApproachF(&this->fwork[FHGFIRE_WARP_TEX_SPEED], scrollDirection, 1.0f, 0.04f); + Math_ApproachF(&this->fwork[FHGFIRE_WARP_ALPHA], 255.0f, 1.0f, 10.2f); + } else if (this->work[FHGFIRE_TIMER] <= 25) { + Math_ApproachZeroF(&this->fwork[FHGFIRE_WARP_TEX_SPEED], 1.0f, 0.04f); + Math_ApproachZeroF(&this->fwork[FHGFIRE_WARP_ALPHA], 1.0f, 10.2f); + } + + osSyncPrintf("EFC 1\n"); + if ((this->work[FHGFIRE_TIMER] == 0) || ((this->actor.params == FHGFIRE_WARP_EMERGE) && horse->fhgFireKillWarp)) { + Actor_Kill(&this->actor); + } + osSyncPrintf("EFC 2\n"); +} + +void EnFhgFire_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnFhgFire* this = (EnFhgFire*)thisx; + + this->work[FHGFIRE_VARIANCE_TIMER]++; + + if (this->work[FHGFIRE_TIMER] != 0) { + this->work[FHGFIRE_TIMER]--; + } + if (this->work[FHGFIRE_FX_TIMER] != 0) { + this->work[FHGFIRE_FX_TIMER]--; + } + + this->updateFunc(this, globalCtx); +} + +static void* sDustTextures[] = { + gDust1Tex, gDust2Tex, gDust3Tex, gDust4Tex, gDust5Tex, gDust6Tex, gDust7Tex, gDust8Tex, +}; + +void EnFhgFire_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnFhgFire* this = (EnFhgFire*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fhg_fire.c", 1723); + + if (this->actor.params == FHGFIRE_LIGHTNING_BURST) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->fwork[FHGFIRE_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 165, 255, 75, 0); + gDPPipeSync(POLY_XLU_DISP++); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fhg_fire.c", 1745), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gPhantomLightningBlastDL)); + } else if ((this->actor.params == FHGFIRE_SPEAR_LIGHT) || (this->actor.params == FHGFIRE_ENERGY_BALL)) { + osSyncPrintf("yari hikari draw 1\n"); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->fwork[FHGFIRE_ALPHA]); + + if (this->work[FHGFIRE_FIRE_MODE] > FHGFIRE_LIGHT_GREEN) { + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, 0); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 165, 255, 75, 0); + } + gDPPipeSync(POLY_XLU_DISP++); + Matrix_RotateZ((this->actor.shape.rot.z / (f32)0x8000) * 3.1416f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fhg_fire.c", 1801), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gPhantomEnergyBallDL); + } else if ((this->actor.params == FHGFIRE_WARP_EMERGE) || (this->actor.params == FHGFIRE_WARP_RETREAT) || + (this->actor.params == FHGFIRE_WARP_DEATH)) { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (u8)this->fwork[FHGFIRE_WARP_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 90, 50, 95, (s8)(this->fwork[FHGFIRE_WARP_ALPHA] * 0.5f)); + gDPPipeSync(POLY_XLU_DISP++); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fhg_fire.c", 1833), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (s16)this->fwork[FHGFIRE_WARP_TEX_1_X], + (s16)this->fwork[FHGFIRE_WARP_TEX_1_Y], 0x40, 0x40, 1, + (s16)this->fwork[FHGFIRE_WARP_TEX_2_X], (s16)this->fwork[FHGFIRE_WARP_TEX_2_Y], + 0x40, 0x40)); + gSPDisplayList(POLY_XLU_DISP++, gPhantomWarpDL); + } else { + osSyncPrintf("FF DRAW 1\n"); + Matrix_Translate(0.0f, -100.0f, 0.0f, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, (s8)this->fwork[FHGFIRE_ALPHA]); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 30, 0); + gDPPipeSync(POLY_XLU_DISP++); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fhg_fire.c", 1892), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gPhantomLightningDL); + osSyncPrintf("FF DRAW 2\n"); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fhg_fire.c", 1900); +} diff --git a/soh/src/overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.h b/soh/src/overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.h new file mode 100644 index 000000000..8bc564a3f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.h @@ -0,0 +1,69 @@ +#ifndef Z_EN_FHG_FIRE_H +#define Z_EN_FHG_FIRE_H + +#include "ultra64.h" +#include "global.h" + +struct EnFhgFire; + +typedef void (*EnFhgFireUpdateFunc)(struct EnFhgFire*, GlobalContext*); + +typedef enum { + /* 1 */ FHGFIRE_LIGHTNING_STRIKE = 1, + /* 35 */ FHGFIRE_LIGHTNING_SHOCK = 35, + /* 36 */ FHGFIRE_LIGHTNING_BURST, + /* 38 */ FHGFIRE_SPEAR_LIGHT = 38, + /* 39 */ FHGFIRE_WARP_EMERGE, + /* 40 */ FHGFIRE_WARP_RETREAT, + /* 41 */ FHGFIRE_WARP_DEATH, + /* 50 */ FHGFIRE_ENERGY_BALL = 50, + /* 100 */ FHGFIRE_LIGHTNING_TRAIL = 100 +} FhgFireParam; + +typedef enum { + /* 0 */ FHGFIRE_LIGHT_GREEN, + /* 1 */ FHGFIRE_LIGHT_BLUE, + /* 2 */ FHGFIRE_LIGHT_REFLECT +} FhgLightMode; + +typedef enum { + /* 0 */ FHGFIRE_TIMER, + /* 1 */ FHGFIRE_FX_TIMER, + /* 2 */ FHGFIRE_US_2, + /* 3 */ FHGFIRE_VARIANCE_TIMER, + /* 4 */ FHGFIRE_FIRE_MODE, + /* 5 */ FHGFIRE_RETURN_COUNT, + /* 6 */ FHGFIRE_KILL_TIMER, + /* 7 */ FHGFIRE_SHORT_COUNT +} FhgFireS16Var; + +typedef enum { + /* 0 */ FHGFIRE_ALPHA, + /* 1 */ FHGFIRE_UF_1, + /* 2 */ FHGFIRE_UF_2, + /* 3 */ FHGFIRE_SCALE, + /* 4 */ FHGFIRE_UF_4, + /* 5 */ FHGFIRE_WARP_TEX_1_X, + /* 6 */ FHGFIRE_WARP_TEX_1_Y, + /* 7 */ FHGFIRE_WARP_TEX_2_X, + /* 8 */ FHGFIRE_WARP_TEX_2_Y, + /* 9 */ FHGFIRE_WARP_TEX_SPEED, + /* 10 */ FHGFIRE_WARP_ALPHA, + /* 11 */ FHGFIRE_BURST_SCALE, + /* 15 */ FHGFIRE_FLOAT_COUNT = 15 +} FhgFireF32Var; + +typedef struct EnFhgFire { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnFhgFireUpdateFunc updateFunc; + /* 0x0150 */ s16 work[FHGFIRE_SHORT_COUNT]; + /* 0x0160 */ f32 fwork[FHGFIRE_FLOAT_COUNT]; + /* 0x019C */ LightNode* lightNode; + /* 0x01A0 */ LightInfo lightInfo; + /* 0x01B0 */ ColliderCylinder collider; + /* 0x01FC */ u8 lensFlareOn; + /* 0x01FE */ s16 lensFlareTimer; + /* 0x0200 */ f32 lensFlareScale; +} EnFhgFire; // size = 0x0204 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.c b/soh/src/overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.c new file mode 100644 index 000000000..63a8025e2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.c @@ -0,0 +1,399 @@ +#include "z_en_fire_rock.h" +#include "overlays/actors/ovl_En_Encount2/z_en_encount2.h" +#include "vt.h" +#include "objects/object_efc_star_field/object_efc_star_field.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnFireRock_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFireRock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFireRock_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFireRock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void FireRock_WaitSpawnRocksFromCeiling(EnFireRock* this, GlobalContext* globalCtx); +void FireRock_WaitOnFloor(EnFireRock* this, GlobalContext* globalCtx); +void EnFireRock_Fall(EnFireRock* this, GlobalContext* globalCtx); +void EnFireRock_SpawnMoreBrokenPieces(EnFireRock* this, GlobalContext* globalCtx); + +const ActorInit En_Fire_Rock_InitVars = { + ACTOR_EN_FIRE_ROCK, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_EFC_STAR_FIELD, + sizeof(EnFireRock), + (ActorFunc)EnFireRock_Init, + (ActorFunc)EnFireRock_Destroy, + (ActorFunc)EnFireRock_Update, + (ActorFunc)EnFireRock_Draw, + NULL, +}; + +static ColliderCylinderInit D_80A12CA0 = { + { + COLTYPE_HARD, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x09, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 30, 30, -10, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit D_80A12CCC = { + { + COLTYPE_HARD, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 30, 30, -10, { 0, 0, 0 } }, +}; + +void EnFireRock_Init(Actor* thisx, GlobalContext* globalCtx) { + GlobalContext* globalCtx2 = globalCtx; + Player* player = GET_PLAYER(globalCtx); + EnFireRock* this = (EnFireRock*)thisx; + s16 temp; + + this->type = this->actor.params; + if (this->type != FIRE_ROCK_CEILING_SPOT_SPAWNER) { + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 15.0f); + if (this->type != FIRE_ROCK_ON_FLOOR) { + this->angularVelocity.x = (Rand_ZeroFloat(10.0f) + 15.0f); + this->angularVelocity.y = (Rand_ZeroFloat(10.0f) + 15.0f); + this->angularVelocity.z = (Rand_ZeroFloat(10.0f) + 15.0f); + } + } + switch (this->type) { + case FIRE_ROCK_CEILING_SPOT_SPAWNER: + this->actor.draw = NULL; + // "☆☆☆☆☆ ceiling waiting rock ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 天井待ち岩 ☆☆☆☆☆ \n" VT_RST); + this->actionFunc = FireRock_WaitSpawnRocksFromCeiling; + break; + case FIRE_ROCK_ON_FLOOR: + Actor_SetScale(&this->actor, 0.03f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &D_80A12CCC); + // "☆☆☆☆☆ floor rock ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 床岩 ☆☆☆☆☆ \n" VT_RST); + this->collider.dim.radius = 23; + this->collider.dim.height = 37; + this->collider.dim.yShift = -10; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actionFunc = FireRock_WaitOnFloor; + break; + case FIRE_ROCK_SPAWNED_FALLING1: // spawned by encount2 + // sets unused vars? + this->unk17C.x = (f32)(Rand_CenteredFloat(50.0f) + player->actor.world.pos.x); + this->unk17C.z = (f32)(Rand_CenteredFloat(50.0f) + player->actor.world.pos.z); + case FIRE_ROCK_SPAWNED_FALLING2: // spawned by encount2 and by the ceilling spawner + this->scale = (Rand_ZeroFloat(2.0f) / 100.0f) + 0.02f; + Actor_SetScale(&this->actor, this->scale); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &D_80A12CA0); + this->actor.world.rot.y = this->actor.shape.rot.y = Rand_CenteredFloat(65535.0f); + this->actionFunc = EnFireRock_Fall; + this->actor.shape.shadowScale = 15.0f; + break; + case FIRE_ROCK_BROKEN_PIECE1: + this->actor.velocity.y = Rand_ZeroFloat(3.0f) + 4.0f; + this->actor.speedXZ = Rand_ZeroFloat(3.0f) + 3.0f; + this->scale = (Rand_ZeroFloat(1.0f) / 100.0f) + 0.02f; + Actor_SetScale(&this->actor, this->scale); + this->actor.gravity = -1.5f; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &D_80A12CA0); + this->actor.shape.shadowScale = 10.0f; + this->actor.world.rot.y = this->actor.shape.rot.y = Rand_CenteredFloat(65535.0f); + this->actionFunc = EnFireRock_Fall; + break; + case FIRE_ROCK_BROKEN_PIECE2: + this->actor.velocity.y = Rand_ZeroFloat(3.0f) + 4.0f; + this->actor.speedXZ = Rand_ZeroFloat(3.0f) + 2.0f; + this->scale = (Rand_ZeroFloat(1.0f) / 500.0f) + 0.01f; + Actor_SetScale(&this->actor, this->scale); + this->actor.gravity = -1.2f; + this->actor.shape.shadowScale = 5.0f; + this->actor.world.rot.y = this->actor.shape.rot.y = Rand_CenteredFloat(65535.0f); + this->actionFunc = EnFireRock_Fall; + break; + default: + // "☆☆☆☆☆ No such rock! ERR !!!!!! ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ そんな岩はねぇ!ERR!!!!!! ☆☆☆☆☆ \n" VT_RST); + Actor_Kill(&this->actor); + break; + } +} + +void EnFireRock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFireRock* this = (EnFireRock*)thisx; + + if ((this->actor.parent != NULL) && (this->actor.parent == &this->spawner->actor)) { + EnEncount2* spawner = (EnEncount2*)this->actor.parent; + if ((spawner->actor.update != NULL) && (spawner->numSpawnedRocks > 0)) { + spawner->numSpawnedRocks--; + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Number of spawned instances recovery ☆☆☆☆☆%d" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発生数回復 ☆☆☆☆☆%d\n" VT_RST, spawner->numSpawnedRocks); + osSyncPrintf("\n\n"); + } + } + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnFireRock_Fall(EnFireRock* this, GlobalContext* globalCtx) { + Player* player; + Vec3f flamePos; + s32 i; + + player = GET_PLAYER(globalCtx); + if ((this->actor.floorHeight == -10000.0f) || (this->actor.world.pos.y < (player->actor.world.pos.y - 200.0f))) { + Actor_Kill(&this->actor); + return; + } + switch (this->type) { + case FIRE_ROCK_SPAWNED_FALLING1: + if (player->actor.world.pos.y < this->actor.world.pos.y) { + if ((player->actor.world.pos.x > -700.0f) || (player->actor.world.pos.x < 100.0f) || + (player->actor.world.pos.z > -1290.0f) || (player->actor.world.pos.z < -3880.0f)) { + Math_ApproachF(&this->actor.world.pos.x, player->actor.world.pos.x, 1.0f, 10.0f); + Math_ApproachF(&this->actor.world.pos.z, player->actor.world.pos.z, 1.0f, 10.0f); + } + } + case FIRE_ROCK_SPAWNED_FALLING2: + flamePos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + flamePos.y = Rand_CenteredFloat(20.0f) + this->actor.world.pos.y; + flamePos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &flamePos, 100, 0, 0, -1); + break; + case FIRE_ROCK_BROKEN_PIECE1: + if ((globalCtx->gameplayFrames & 3) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_ROCK); + } + break; + } + if ((this->actor.bgCheckFlags & 1) && (this->timer == 0)) { + switch (this->type) { + case FIRE_ROCK_SPAWNED_FALLING1: + case FIRE_ROCK_SPAWNED_FALLING2: + func_80033E88(&this->actor, globalCtx, 5, 2); + case FIRE_ROCK_BROKEN_PIECE1: + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, this->actor.shape.shadowScale, + 1, 8.0f, 500, 10, 0); + for (i = 0; i < 5; i++) { + flamePos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + flamePos.y = this->actor.floorHeight; + flamePos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &flamePos, 300, 0, 0, -1); + } + this->actionFunc = EnFireRock_SpawnMoreBrokenPieces; + break; + default: + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, this->actor.shape.shadowScale, + 3, 8.0f, 200, 10, 0); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_EXPLOSION); + Actor_Kill(&this->actor); + break; + } + } +} + +/** + * After the rock has already hit the ground and started rolling, spawn two more, giving the illusion of breaking into + * two pieces. + */ +void EnFireRock_SpawnMoreBrokenPieces(EnFireRock* this, GlobalContext* globalCtx) { + EnFireRock* spawnedFireRock; + s32 nextRockType; + s32 i; + s32 temp; + + nextRockType = FIRE_ROCK_SPAWNED_FALLING1; + switch (this->type) { + case FIRE_ROCK_SPAWNED_FALLING1: + case FIRE_ROCK_SPAWNED_FALLING2: + nextRockType = FIRE_ROCK_BROKEN_PIECE1; + break; + case FIRE_ROCK_BROKEN_PIECE1: + nextRockType = FIRE_ROCK_BROKEN_PIECE2; + } + + if (nextRockType != FIRE_ROCK_SPAWNED_FALLING1) { + for (i = 0; i < 2; i++) { + spawnedFireRock = (EnFireRock*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_FIRE_ROCK, Rand_CenteredFloat(3.0f) + this->actor.world.pos.x, + Rand_CenteredFloat(3.0f) + (this->actor.world.pos.y + 10.0f), + Rand_CenteredFloat(3.0f) + this->actor.world.pos.z, 0, 0, 0, nextRockType); + if (spawnedFireRock != NULL) { + spawnedFireRock->actor.world.rot.y = this->actor.world.rot.y; + if (i == 0) { + spawnedFireRock->actor.shape.rot.y = this->actor.shape.rot.y; + } + spawnedFireRock->scale = this->scale - 0.01f; + } else { + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ イッパイデッス ☆☆☆☆☆ \n" VT_RST); + } + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_ROCK); + } + Actor_Kill(&this->actor); +} + +void FireRock_WaitSpawnRocksFromCeiling(EnFireRock* this, GlobalContext* globalCtx) { + EnFireRock* spawnedFireRock; + + if (this->actor.xzDistToPlayer < 200.0f) { + if ((this->playerNearby == 0) && (this->timer2 == 0)) { + this->timer2 = 30; + spawnedFireRock = (EnFireRock*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_FIRE_ROCK, Rand_CenteredFloat(3.0f) + this->actor.world.pos.x, + this->actor.world.pos.y + 10.0f, Rand_CenteredFloat(3.0f) + this->actor.world.pos.z, 0, 0, 0, + FIRE_ROCK_SPAWNED_FALLING2); + if (spawnedFireRock != NULL) { + spawnedFireRock->timer = 10; + } else { + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ イッパイデッス ☆☆☆☆☆ \n" VT_RST); + } + } + this->playerNearby = 1; + } else { + this->playerNearby = 0; + } + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 0, 255, 0, 255, 4, globalCtx->state.gfxCtx); + } +} + +void FireRock_WaitOnFloor(EnFireRock* this, GlobalContext* globalCtx) { + Vec3f flamePos; + s16 scale; + + if (this->timer2 == 0) { + flamePos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + flamePos.y = Rand_CenteredFloat(20.0f) + this->actor.world.pos.y; + flamePos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + scale = 130 + (s16)Rand_CenteredFloat(60.0f); + this->timer2 = 3 + (s16)Rand_ZeroFloat(3.0f); + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &flamePos, scale, 0, 0, -1); + } +} + +void EnFireRock_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFireRock* this = (EnFireRock*)thisx; + s16 setCollision; + Player* player = GET_PLAYER(globalCtx); + Actor* playerActor = &GET_PLAYER(globalCtx)->actor; + + if (this->timer2 != 0) { + this->timer2--; + } + if (this->timer != 0) { + this->timer--; + } + this->actionFunc(this, globalCtx); + + if (this->type != FIRE_ROCK_CEILING_SPOT_SPAWNER) { + f32 temp; + + this->rockRotation.x += this->angularVelocity.x; + this->rockRotation.y += this->angularVelocity.y; + this->rockRotation.z += this->angularVelocity.z; + this->relativePos.y = 3.0f; + + temp = 10.0f + (this->scale * 300.0f); + thisx->shape.shadowScale = temp; + if (thisx->shape.shadowScale < 10.0f) { + thisx->shape.shadowScale = 10.0f; + } + if (thisx->shape.shadowScale > 20.0f) { + thisx->shape.shadowScale = 20.0f; + } + + if ((this->type == FIRE_ROCK_SPAWNED_FALLING1) || (this->type == FIRE_ROCK_SPAWNED_FALLING2)) { + thisx->gravity = -0.3f - (this->scale * 7.0f); + } + if (this->type != FIRE_ROCK_ON_FLOOR) { + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 50.0f, 50.0f, 100.0f, 0x1C); + } + + setCollision = false; + if (this->actionFunc != EnFireRock_SpawnMoreBrokenPieces) { + if ((this->type == FIRE_ROCK_SPAWNED_FALLING1) || (this->type == FIRE_ROCK_SPAWNED_FALLING2) || + (this->type == FIRE_ROCK_BROKEN_PIECE1)) { + if (this->collider.base.atFlags & 4) { + this->collider.base.atFlags &= ~4; + Audio_PlayActorSound2(thisx, NA_SE_EV_BRIDGE_OPEN_STOP); + thisx->velocity.y = 0.0f; + thisx->speedXZ = 0.0f; + this->actionFunc = EnFireRock_SpawnMoreBrokenPieces; + // "☆☆☆☆☆ Shield Defense Lv1 ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ シールド防御 Lv1 ☆☆☆☆☆ \n" VT_RST); + return; + } + setCollision = true; + } + } + + if (this->type == FIRE_ROCK_ON_FLOOR) { + if (this->collider.base.atFlags & 2) { + this->collider.base.atFlags &= ~2; + if (this->collider.base.at == playerActor) { + if (!(player->stateFlags1 & 0x04000000)) { + func_8002F758(globalCtx, thisx, 2.0f, -player->actor.world.rot.y, 3.0f, 4); + } + return; + } + } + setCollision = true; + } + if (setCollision) { + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +void EnFireRock_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnFireRock* this = (EnFireRock*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fire_rock.c", 747); + Matrix_Translate(thisx->world.pos.x + this->relativePos.x, thisx->world.pos.y + this->relativePos.y, + thisx->world.pos.z + this->relativePos.z, MTXMODE_NEW); + Matrix_RotateX(DEG_TO_RAD(this->rockRotation.x), MTXMODE_APPLY); + Matrix_RotateY(DEG_TO_RAD(this->rockRotation.y), MTXMODE_APPLY); + Matrix_RotateZ(DEG_TO_RAD(this->rockRotation.z), MTXMODE_APPLY); + Matrix_Scale(thisx->scale.x, thisx->scale.y, thisx->scale.z, MTXMODE_APPLY); + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 155, 55, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 155, 255, 55, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fire_rock.c", 768), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_efc_star_field_DL_000DE0); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fire_rock.c", 773); +} diff --git a/soh/src/overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.h b/soh/src/overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.h new file mode 100644 index 000000000..8dfb334d1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fire_Rock/z_en_fire_rock.h @@ -0,0 +1,39 @@ +#ifndef Z_EN_FIRE_ROCK_H +#define Z_EN_FIRE_ROCK_H + +#include "ultra64.h" +#include "global.h" + +#include "overlays/actors/ovl_En_Encount2/z_en_encount2.h" + +typedef enum { + /* 0x00 */ FIRE_ROCK_SPAWNED_FALLING1, + /* 0x01 */ FIRE_ROCK_BROKEN_PIECE1, + /* 0x02 */ FIRE_ROCK_BROKEN_PIECE2, + /* 0x03 */ FIRE_ROCK_SPAWNED_FALLING2, + /* 0x05 */ FIRE_ROCK_CEILING_SPOT_SPAWNER = 5, + /* 0x06 */ FIRE_ROCK_ON_FLOOR +} EnFireRockType; + +struct EnFireRock; + +typedef void (*EnFireRockActionFunc)(struct EnFireRock*, GlobalContext*); + +typedef struct EnFireRock { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3f angularVelocity; + /* 0x0158 */ Vec3f rockRotation; + /* 0x0164 */ char unk164[0x4]; + /* 0x0168 */ EnFireRockActionFunc actionFunc; + /* 0x016C */ f32 scale; + /* 0x0170 */ Vec3f relativePos; + /* 0x017C */ Vec3f unk17C; // set but unused? + /* 0x0188 */ s16 timer; + /* 0x018A */ s16 timer2; + /* 0x018C */ s16 type; + /* 0x018E */ u8 playerNearby; + /* 0x0190 */ EnEncount2* spawner; + /* 0x0194 */ ColliderCylinder collider; +} EnFireRock; // size = 0x01E0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c new file mode 100644 index 000000000..2761bd38d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c @@ -0,0 +1,836 @@ +/* + * File: z_en_firefly.c + * Overlay: ovl_En_Firefly + * Description: Keese (Normal, Fire, Ice) + */ + +#include "z_en_firefly.h" +#include "objects/object_firefly/object_firefly.h" +#include "overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_12 | ACTOR_FLAG_14) + +void EnFirefly_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFirefly_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFirefly_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFirefly_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnFirefly_DrawInvisible(Actor* thisx, GlobalContext* globalCtx); + +void EnFirefly_FlyIdle(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_Fall(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_Die(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_DiveAttack(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_Rebound(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_FlyAway(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_Stunned(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_FrozenFall(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_Perch(EnFirefly* this, GlobalContext* globalCtx); +void EnFirefly_DisturbDiveAttack(EnFirefly* this, GlobalContext* globalCtx); + +typedef enum { + /* 0 */ KEESE_AURA_NONE, + /* 1 */ KEESE_AURA_FIRE, + /* 2 */ KEESE_AURA_ICE +} KeeseAuraType; + +const ActorInit En_Firefly_InitVars = { + ACTOR_EN_FIREFLY, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_FIREFLY, + sizeof(EnFirefly), + (ActorFunc)EnFirefly_Init, + (ActorFunc)EnFirefly_Destroy, + (ActorFunc)EnFirefly_Update, + (ActorFunc)EnFirefly_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 1, { { 0, 1000, 0 }, 15 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 10, 10, 30 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0xF), + /* Ice arrow */ DMG_ENTRY(4, 0x3), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x2), + /* Ice magic */ DMG_ENTRY(4, 0x3), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 5, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(gravity, -500, ICHAIN_CONTINUE), + ICHAIN_F32(minVelocityY, -4, ICHAIN_CONTINUE), ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 4000, ICHAIN_STOP), +}; + +void EnFirefly_Extinguish(EnFirefly* this) { + this->actor.params += 2; + this->collider.elements[0].info.toucher.effect = 0; // None + this->auraType = KEESE_AURA_NONE; + this->onFire = false; + this->actor.naviEnemyId = 0x12; // Keese +} + +void EnFirefly_Ignite(EnFirefly* this) { + if (this->actor.params == KEESE_ICE_FLY) { + this->actor.params = KEESE_FIRE_FLY; + } else { + this->actor.params -= 2; + } + this->collider.elements[0].info.toucher.effect = 1; // Fire + this->auraType = KEESE_AURA_FIRE; + this->onFire = true; + this->actor.naviEnemyId = 0x11; // Fire Keese +} + +void EnFirefly_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFirefly* this = (EnFirefly*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 25.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gKeeseSkeleton, &gKeeseFlyAnim, this->jointTable, this->morphTable, + 28); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + + if ((this->actor.params & 0x8000) != 0) { + this->actor.flags |= ACTOR_FLAG_7; + if (1) {} + this->actor.draw = EnFirefly_DrawInvisible; + this->actor.params &= 0x7FFF; + } + + if (this->actor.params <= KEESE_FIRE_PERCH) { + this->onFire = true; + } else { + this->onFire = false; + } + + if (this->onFire) { + this->actionFunc = EnFirefly_FlyIdle; + this->timer = Rand_S16Offset(20, 60); + this->actor.shape.rot.x = 0x1554; + this->auraType = KEESE_AURA_FIRE; + this->actor.naviEnemyId = 0x11; // Fire Keese + this->maxAltitude = this->actor.home.pos.y; + } else { + if (this->actor.params == KEESE_NORMAL_PERCH) { + this->actionFunc = EnFirefly_Perch; + } else { + this->actionFunc = EnFirefly_FlyIdle; + } + + if (this->actor.params == KEESE_ICE_FLY) { + this->collider.elements[0].info.toucher.effect = 2; // Ice + this->actor.naviEnemyId = 0x56; // Ice Keese + } else { + this->collider.elements[0].info.toucher.effect = 0; // Nothing + this->actor.naviEnemyId = 0x12; // Keese + } + + this->maxAltitude = this->actor.home.pos.y + 100.0f; + + if (this->actor.params == KEESE_ICE_FLY) { + this->auraType = KEESE_AURA_ICE; + } else { + this->auraType = KEESE_AURA_NONE; + } + } + + this->collider.elements[0].dim.worldSphere.radius = sJntSphInit.elements[0].dim.modelSphere.radius; +} + +void EnFirefly_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFirefly* this = (EnFirefly*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnFirefly_SetupFlyIdle(EnFirefly* this) { + this->timer = Rand_S16Offset(70, 100); + this->actor.speedXZ = (Rand_ZeroOne() * 1.5f) + 1.5f; + Math_ScaledStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), 0x300); + this->targetPitch = ((this->maxAltitude < this->actor.world.pos.y) ? 0xC00 : -0xC00) + 0x1554; + this->skelAnime.playSpeed = 1.0f; + this->actionFunc = EnFirefly_FlyIdle; +} + +void EnFirefly_SetupFall(EnFirefly* this) { + this->timer = 40; + this->actor.velocity.y = 0.0f; + Animation_Change(&this->skelAnime, &gKeeseFlyAnim, 0.5f, 0.0f, 0.0f, ANIMMODE_LOOP_INTERP, -3.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_DEAD); + this->actor.flags |= ACTOR_FLAG_4; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 40); + this->actionFunc = EnFirefly_Fall; +} + +void EnFirefly_SetupDie(EnFirefly* this) { + this->timer = 15; + this->actor.speedXZ = 0.0f; + this->actionFunc = EnFirefly_Die; +} + +void EnFirefly_SetupRebound(EnFirefly* this) { + this->actor.world.rot.x = 0x7000; + this->timer = 18; + this->skelAnime.playSpeed = 1.0f; + this->actor.speedXZ = 2.5f; + this->actionFunc = EnFirefly_Rebound; +} + +void EnFirefly_SetupDiveAttack(EnFirefly* this) { + this->timer = Rand_S16Offset(70, 100); + this->skelAnime.playSpeed = 1.0f; + this->targetPitch = ((this->actor.yDistToPlayer > 0.0f) ? -0xC00 : 0xC00) + 0x1554; + this->actionFunc = EnFirefly_DiveAttack; +} + +void EnFirefly_SetupFlyAway(EnFirefly* this) { + this->timer = 150; + this->skelAnime.playSpeed = 1.0f; + this->targetPitch = 0x954; + this->actionFunc = EnFirefly_FlyAway; +} + +void EnFirefly_SetupStunned(EnFirefly* this) { + this->timer = 80; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 80); + this->auraType = KEESE_AURA_NONE; + this->actor.velocity.y = 0.0f; + this->skelAnime.playSpeed = 3.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actionFunc = EnFirefly_Stunned; +} + +void EnFirefly_SetupFrozenFall(EnFirefly* this, GlobalContext* globalCtx) { + s32 i; + Vec3f iceParticlePos; + + this->actor.flags |= ACTOR_FLAG_4; + this->auraType = KEESE_AURA_NONE; + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0xFF); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_DEAD); + + for (i = 0; i <= 7; i++) { + iceParticlePos.x = (i & 1 ? 7.0f : -7.0f) + this->actor.world.pos.x; + iceParticlePos.y = (i & 2 ? 7.0f : -7.0f) + this->actor.world.pos.y; + iceParticlePos.z = (i & 4 ? 7.0f : -7.0f) + this->actor.world.pos.z; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &iceParticlePos, 150, 150, 150, 250, 235, 245, 255, + (Rand_ZeroOne() * 0.15f) + 0.85f); + } + + this->actionFunc = EnFirefly_FrozenFall; +} + +void EnFirefly_SetupPerch(EnFirefly* this) { + this->timer = 1; + this->actor.speedXZ = 0.0f; + this->actionFunc = EnFirefly_Perch; +} + +void EnFirefly_SetupDisturbDiveAttack(EnFirefly* this) { + this->skelAnime.playSpeed = 3.0f; + this->actor.shape.rot.x = 0x1554; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = 3.0f; + this->timer = 50; + this->actionFunc = EnFirefly_DisturbDiveAttack; +} + +s32 EnFirefly_ReturnToPerch(EnFirefly* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 distFromHome; + + if (this->actor.params != KEESE_NORMAL_PERCH) { + return 0; + } + + if (Actor_WorldDistXZToPoint(&player->actor, &this->actor.home.pos) > 300.0f) { + distFromHome = Actor_WorldDistXYZToPoint(&this->actor, &this->actor.home.pos); + + if (distFromHome < 5.0f) { + EnFirefly_SetupPerch(this); + return 1; + } + + distFromHome *= 0.05f; + + if (distFromHome < 1.0f) { + this->actor.speedXZ *= distFromHome; + } + + Math_ScaledStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), + 0x300); + Math_ScaledStepToS(&this->actor.shape.rot.x, + Actor_WorldPitchTowardPoint(&this->actor, &this->actor.home.pos) + 0x1554, 0x100); + return 1; + } + + return 0; +} + +s32 EnFirefly_SeekTorch(EnFirefly* this, GlobalContext* globalCtx) { + ObjSyokudai* findTorch; + ObjSyokudai* closestTorch; + f32 torchDist; + f32 currentMinDist; + Vec3f flamePos; + + findTorch = (ObjSyokudai*)globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + closestTorch = NULL; + currentMinDist = 35000.0f; + + while (findTorch != NULL) { + if ((findTorch->actor.id == ACTOR_OBJ_SYOKUDAI) && (findTorch->litTimer != 0)) { + torchDist = Actor_WorldDistXYZToActor(&this->actor, &findTorch->actor); + if (torchDist < currentMinDist) { + currentMinDist = torchDist; + closestTorch = findTorch; + } + } + findTorch = (ObjSyokudai*)findTorch->actor.next; + } + + if (closestTorch != NULL) { + flamePos.x = closestTorch->actor.world.pos.x; + flamePos.y = closestTorch->actor.world.pos.y + 52.0f + 15.0f; + flamePos.z = closestTorch->actor.world.pos.z; + if (Actor_WorldDistXYZToPoint(&this->actor, &flamePos) < 15.0f) { + EnFirefly_Ignite(this); + return 1; + } else { + Math_ScaledStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardActor(&this->actor, &closestTorch->actor), + 0x300); + Math_ScaledStepToS(&this->actor.shape.rot.x, Actor_WorldPitchTowardPoint(&this->actor, &flamePos) + 0x1554, + 0x100); + return 1; + } + } + return 0; +} + +void EnFirefly_FlyIdle(EnFirefly* this, GlobalContext* globalCtx) { + s32 skelanimeUpdated; + f32 rand; + + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + skelanimeUpdated = Animation_OnFrame(&this->skelAnime, 0.0f); + this->actor.speedXZ = (Rand_ZeroOne() * 1.5f) + 1.5f; + if (this->onFire || (this->actor.params == KEESE_ICE_FLY) || + ((EnFirefly_ReturnToPerch(this, globalCtx) == 0) && (EnFirefly_SeekTorch(this, globalCtx) == 0))) { + if (skelanimeUpdated) { + rand = Rand_ZeroOne(); + if (rand < 0.5f) { + Math_ScaledStepToS(&this->actor.shape.rot.y, + Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), 0x300); + } else if (rand < 0.8f) { + this->actor.shape.rot.y += Rand_CenteredFloat(1536.0f); + } + // Climb if too close to ground + if (this->actor.world.pos.y < (this->actor.floorHeight + 20.0f)) { + this->targetPitch = 0x954; + // Descend if above maxAltitude + } else if (this->maxAltitude < this->actor.world.pos.y) { + this->targetPitch = 0x2154; + // Otherwise ascend or descend at random, biased towards ascending + } else if (0.35f < Rand_ZeroOne()) { + this->targetPitch = 0x954; + } else { + this->targetPitch = 0x2154; + } + } else { + if (this->actor.bgCheckFlags & 1) { + this->targetPitch = 0x954; + } else if ((this->actor.bgCheckFlags & 0x10) || (this->maxAltitude < this->actor.world.pos.y)) { + this->targetPitch = 0x2154; + } + } + Math_ScaledStepToS(&this->actor.shape.rot.x, this->targetPitch, 0x100); + } + if (this->actor.bgCheckFlags & 8) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.wallYaw, 2, 0xC00, 0x300); + } + if ((this->timer == 0) && (this->actor.xzDistToPlayer < 200.0f) && + (Player_GetMask(globalCtx) != PLAYER_MASK_SKULL)) { + EnFirefly_SetupDiveAttack(this); + } +} + +// Fall to the ground after being hit +void EnFirefly_Fall(EnFirefly* this, GlobalContext* globalCtx) { + if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + this->skelAnime.playSpeed = 0.0f; + } + this->actor.colorFilterTimer = 40; + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + if (this->actor.flags & ACTOR_FLAG_15) { + this->actor.colorFilterTimer = 40; + } else { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x6800, 0x200); + this->actor.shape.rot.y = this->actor.shape.rot.y - 0x300; + if (this->timer != 0) { + this->timer--; + } + if ((this->actor.bgCheckFlags & 1) || (this->timer == 0)) { + EnFirefly_SetupDie(this); + } + } +} + +// Hit the ground or burn up, spawn drops +void EnFirefly_Die(EnFirefly* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + Math_StepToF(&this->actor.scale.x, 0.0f, 0.00034f); + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + if (this->timer == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xE0); + Actor_Kill(&this->actor); + } +} + +void EnFirefly_DiveAttack(EnFirefly* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f preyPos; + + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + Math_StepToF(&this->actor.speedXZ, 4.0f, 0.5f); + if (this->actor.bgCheckFlags & 8) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.wallYaw, 2, 0xC00, 0x300); + Math_ScaledStepToS(&this->actor.shape.rot.x, this->targetPitch, 0x100); + } else if (Actor_IsFacingPlayer(&this->actor, 0x2800)) { + if (Animation_OnFrame(&this->skelAnime, 4.0f)) { + this->skelAnime.playSpeed = 0.0f; + this->skelAnime.curFrame = 4.0f; + } + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xC00, 0x300); + preyPos.x = player->actor.world.pos.x; + preyPos.y = player->actor.world.pos.y + 20.0f; + preyPos.z = player->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.shape.rot.x, Actor_WorldPitchTowardPoint(&this->actor, &preyPos) + 0x1554, 2, + 0x400, 0x100); + } else { + this->skelAnime.playSpeed = 1.5f; + if (this->actor.xzDistToPlayer > 80.0f) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xC00, 0x300); + } + if (this->actor.bgCheckFlags & 1) { + this->targetPitch = 0x954; + } + if ((this->actor.bgCheckFlags & 0x10) || (this->maxAltitude < this->actor.world.pos.y)) { + this->targetPitch = 0x2154; + } else { + this->targetPitch = 0x954; + } + Math_ScaledStepToS(&this->actor.shape.rot.x, this->targetPitch, 0x100); + } + if ((this->timer == 0) || (Player_GetMask(globalCtx) == PLAYER_MASK_SKULL)) { + EnFirefly_SetupFlyAway(this); + } +} + +// Knockback after hitting player +void EnFirefly_Rebound(EnFirefly* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x100); + Math_StepToF(&this->actor.velocity.y, 0.0f, 0.4f); + if (Math_StepToF(&this->actor.speedXZ, 0.0f, 0.15f)) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + EnFirefly_SetupFlyAway(this); + } + } +} + +void EnFirefly_FlyAway(EnFirefly* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + if (((fabsf(this->actor.world.pos.y - this->maxAltitude) < 10.0f) && + (Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) < 20.0f)) || + (this->timer == 0)) { + EnFirefly_SetupFlyIdle(this); + return; + } + Math_StepToF(&this->actor.speedXZ, 3.0f, 0.3f); + if (this->actor.bgCheckFlags & 1) { + this->targetPitch = 0x954; + } else if ((this->actor.bgCheckFlags & 0x10) || (this->maxAltitude < this->actor.world.pos.y)) { + this->targetPitch = 0x2154; + } else { + this->targetPitch = 0x954; + } + if (this->actor.bgCheckFlags & 8) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.wallYaw, 2, 0xC00, 0x300); + } else { + Math_ScaledStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), + 0x300); + } + Math_ScaledStepToS(&this->actor.shape.rot.x, this->targetPitch, 0x100); +} + +void EnFirefly_Stunned(EnFirefly* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x1554, 0x100); + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + if (this->onFire) { + this->auraType = KEESE_AURA_FIRE; + } else if (this->actor.params == KEESE_ICE_FLY) { + this->auraType = KEESE_AURA_ICE; + } + EnFirefly_SetupFlyIdle(this); + } +} + +void EnFirefly_FrozenFall(EnFirefly* this, GlobalContext* globalCtx) { + if ((this->actor.bgCheckFlags & 1) || (this->actor.floorHeight == BGCHECK_Y_MIN)) { + this->actor.colorFilterTimer = 0; + EnFirefly_SetupDie(this); + } else { + this->actor.colorFilterTimer = 255; + } +} + +// When perching, sit on collision and flap at random intervals +void EnFirefly_Perch(EnFirefly* this, GlobalContext* globalCtx) { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x100); + + if (this->timer != 0) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + this->timer--; + } + } else if (Rand_ZeroOne() < 0.02f) { + this->timer = 1; + } + + if (this->actor.xzDistToPlayer < 120.0f) { + EnFirefly_SetupDisturbDiveAttack(this); + } +} + +void EnFirefly_DisturbDiveAttack(EnFirefly* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f preyPos; + + SkelAnime_Update(&this->skelAnime); + + if (this->timer != 0) { + this->timer--; + } + + if (this->timer < 40) { + Math_ScaledStepToS(&this->actor.shape.rot.x, -0xAAC, 0x100); + } else { + preyPos.x = player->actor.world.pos.x; + preyPos.y = player->actor.world.pos.y + 20.0f; + preyPos.z = player->actor.world.pos.z; + Math_ScaledStepToS(&this->actor.shape.rot.x, Actor_WorldPitchTowardPoint(&this->actor, &preyPos) + 0x1554, + 0x100); + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x300); + } + + if (this->timer == 0) { + EnFirefly_SetupFlyIdle(this); + } +} + +void EnFirefly_Combust(EnFirefly* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i <= 2; i++) { + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &this->actor.world.pos, 40, 0, 0, i); + } + + this->auraType = KEESE_AURA_NONE; +} + +void EnFirefly_UpdateDamage(EnFirefly* this, GlobalContext* globalCtx) { + u8 damageEffect; + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.elements[0].info, 1); + + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_0; + } + + damageEffect = this->actor.colChkInfo.damageEffect; + + if (damageEffect == 2) { // Din's Fire + if (this->actor.params == KEESE_ICE_FLY) { + this->actor.colChkInfo.health = 0; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + EnFirefly_Combust(this, globalCtx); + EnFirefly_SetupFall(this); + } else if (!this->onFire) { + EnFirefly_Ignite(this); + if (this->actionFunc == EnFirefly_Perch) { + EnFirefly_SetupFlyIdle(this); + } + } + } else if (damageEffect == 3) { // Ice Arrows or Ice Magic + if (this->actor.params == KEESE_ICE_FLY) { + EnFirefly_SetupFall(this); + } else { + EnFirefly_SetupFrozenFall(this, globalCtx); + } + } else if (damageEffect == 1) { // Deku Nuts + if (this->actionFunc != EnFirefly_Stunned) { + EnFirefly_SetupStunned(this); + } + } else { // Fire Arrows + if ((damageEffect == 0xF) && (this->actor.params == KEESE_ICE_FLY)) { + EnFirefly_Combust(this, globalCtx); + } + EnFirefly_SetupFall(this); + } + } + } +} + +void EnFirefly_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnFirefly* this = (EnFirefly*)thisx; + GlobalContext* globalCtx = globalCtx2; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_ATTACK); + if (this->onFire) { + EnFirefly_Extinguish(this); + } + if (this->actionFunc != EnFirefly_DisturbDiveAttack) { + EnFirefly_SetupRebound(this); + } + } + + EnFirefly_UpdateDamage(this, globalCtx); + + this->actionFunc(this, globalCtx); + + if (!(this->actor.flags & ACTOR_FLAG_15)) { + if ((this->actor.colChkInfo.health == 0) || (this->actionFunc == EnFirefly_Stunned)) { + Actor_MoveForward(&this->actor); + } else { + if (this->actionFunc != EnFirefly_Rebound) { + this->actor.world.rot.x = 0x1554 - this->actor.shape.rot.x; + } + func_8002D97C(&this->actor); + } + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 15.0f, 7); + this->collider.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x; + this->collider.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y + 10.0f; + this->collider.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z; + + if ((this->actionFunc == EnFirefly_DiveAttack) || (this->actionFunc == EnFirefly_DisturbDiveAttack)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->actor.colChkInfo.health != 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + this->actor.world.rot.y = this->actor.shape.rot.y; + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FFLY_FLY); + } + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + this->actor.focus.pos.x = + (10.0f * Math_SinS(this->actor.shape.rot.x) * Math_SinS(this->actor.shape.rot.y)) + this->actor.world.pos.x; + this->actor.focus.pos.y = (10.0f * Math_CosS(this->actor.shape.rot.x)) + this->actor.world.pos.y; + this->actor.focus.pos.z = + (10.0f * Math_SinS(this->actor.shape.rot.x) * Math_CosS(this->actor.shape.rot.y)) + this->actor.world.pos.z; +} + +s32 EnFirefly_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx, Gfx** gfx) { + EnFirefly* this = (EnFirefly*)thisx; + + if ((this->actor.draw == EnFirefly_DrawInvisible) && (globalCtx->actorCtx.unk_03 == 0)) { + *dList = NULL; + } else if (limbIndex == 1) { + pos->y += 2300.0f; + } + return false; +} + +void EnFirefly_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + static Color_RGBA8 fireAuraPrimColor = { 255, 255, 100, 255 }; + static Color_RGBA8 fireAuraEnvColor = { 255, 50, 0, 0 }; + static Color_RGBA8 iceAuraPrimColor = { 100, 200, 255, 255 }; + static Color_RGBA8 iceAuraEnvColor = { 0, 0, 255, 0 }; + static Vec3f effVelocity = { 0.0f, 0.5f, 0.0f }; + static Vec3f effAccel = { 0.0f, 0.5f, 0.0f }; + static Vec3f limbSrc = { 0.0f, 0.0f, 0.0f }; + Vec3f effPos; + Vec3f* limbDest; + Color_RGBA8* effPrimColor; + Color_RGBA8* effEnvColor; + MtxF mtx; + s16 effScaleStep; + s16 effLife; + EnFirefly* this = (EnFirefly*)thisx; + + if (!this->onFire && (limbIndex == 27)) { + gSPDisplayList((*gfx)++, gKeeseEyesDL); + } else { + if ((this->auraType == KEESE_AURA_FIRE) || (this->auraType == KEESE_AURA_ICE)) { + if ((limbIndex == 15) || (limbIndex == 21)) { + if (this->actionFunc != EnFirefly_Die) { + Matrix_Get(&mtx); + effPos.x = (Rand_ZeroOne() * 5.0f) + mtx.xw; + effPos.y = (Rand_ZeroOne() * 5.0f) + mtx.yw; + effPos.z = (Rand_ZeroOne() * 5.0f) + mtx.zw; + effScaleStep = -40; + effLife = 3; + } else { + if (limbIndex == 15) { + effPos.x = (Math_SinS(9100 * this->timer) * this->timer) + this->actor.world.pos.x; + effPos.z = (Math_CosS(9100 * this->timer) * this->timer) + this->actor.world.pos.z; + } else { + effPos.x = this->actor.world.pos.x - (Math_SinS(9100 * this->timer) * this->timer); + effPos.z = this->actor.world.pos.z - (Math_CosS(9100 * this->timer) * this->timer); + } + + effPos.y = this->actor.world.pos.y + ((15 - this->timer) * 1.5f); + effScaleStep = -5; + effLife = 10; + } + + if (this->auraType == KEESE_AURA_FIRE) { + effPrimColor = &fireAuraPrimColor; + effEnvColor = &fireAuraEnvColor; + } else { + effPrimColor = &iceAuraPrimColor; + effEnvColor = &iceAuraEnvColor; + } + + func_8002843C(globalCtx, &effPos, &effVelocity, &effAccel, effPrimColor, effEnvColor, 250, effScaleStep, + effLife); + } + } + } + if ((limbIndex == 15) || (limbIndex == 21) || (limbIndex == 10)) { + if (limbIndex == 15) { + limbDest = &this->bodyPartsPos[0]; + } else if (limbIndex == 21) { + limbDest = &this->bodyPartsPos[1]; + } else { + limbDest = &this->bodyPartsPos[2]; + } + + Matrix_MultVec3f(&limbSrc, limbDest); + limbDest->y -= 5.0f; + } +} + +void EnFirefly_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnFirefly* this = (EnFirefly*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_firefly.c", 1733); + func_80093D18(globalCtx->state.gfxCtx); + + if (this->onFire) { + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + } else { + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + } + + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnFirefly_OverrideLimbDraw, EnFirefly_PostLimbDraw, &this->actor, POLY_OPA_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_firefly.c", 1763); +} + +void EnFirefly_DrawInvisible(Actor* thisx, GlobalContext* globalCtx) { + EnFirefly* this = (EnFirefly*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_firefly.c", 1775); + func_80093D84(globalCtx->state.gfxCtx); + + if (this->onFire) { + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 0); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 255); + } + + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnFirefly_OverrideLimbDraw, EnFirefly_PostLimbDraw, this, POLY_XLU_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_firefly.c", 1805); +} diff --git a/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.h b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.h new file mode 100644 index 000000000..0241d4ebd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Firefly/z_en_firefly.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_FIREFLY_H +#define Z_EN_FIREFLY_H + +#include "ultra64.h" +#include "global.h" + +struct EnFirefly; + +typedef void (*EnFireflyActionFunc)(struct EnFirefly*, GlobalContext*); + +typedef struct EnFirefly { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3f bodyPartsPos[3]; + /* 0x0170 */ SkelAnime skelAnime; + /* 0x01B4 */ EnFireflyActionFunc actionFunc; + /* 0x01B8 */ u8 auraType; + /* 0x01B9 */ u8 onFire; + /* 0x01BA */ s16 timer; + /* 0x01BC */ s16 targetPitch; + /* 0x01BE */ Vec3s jointTable[28]; + /* 0x0266 */ Vec3s morphTable[28]; + /* 0x0310 */ f32 maxAltitude; + /* 0x0314 */ ColliderJntSph collider; + /* 0x0344 */ ColliderJntSphElement colliderItems[1]; +} EnFirefly; // size = 0x0374 + +typedef enum { + /* 0 */ KEESE_FIRE_FLY, + /* 1 */ KEESE_FIRE_PERCH, + /* 2 */ KEESE_NORMAL_FLY, + /* 3 */ KEESE_NORMAL_PERCH, + /* 4 */ KEESE_ICE_FLY +} KeeseType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c b/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c new file mode 100644 index 000000000..01700682c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.c @@ -0,0 +1,768 @@ +/* + * File: z_en_fish.c + * Overlay: ovl_En_Fish + * Description: Fish + */ + +#include "z_en_fish.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "vt.h" + +#define FLAGS 0 + +void EnFish_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFish_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFish_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFish_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnFish_Respawning_SetupSlowDown(EnFish* this); +void EnFish_Respawning_SlowDown(EnFish* this, GlobalContext* globalCtx); +void EnFish_Respawning_SetupFollowChild(EnFish* this); +void EnFish_Respawning_FollowChild(EnFish* this, GlobalContext* globalCtx); +void EnFish_Respawning_SetupFleePlayer(EnFish* this); +void EnFish_Respawning_FleePlayer(EnFish* this, GlobalContext* globalCtx); +void EnFish_Respawning_SetupApproachPlayer(EnFish* this); +void EnFish_Respawning_ApproachPlayer(EnFish* this, GlobalContext* globalCtx); +void EnFish_Dropped_SetupFall(EnFish* this); +void EnFish_Dropped_Fall(EnFish* this, GlobalContext* globalCtx); +void EnFish_Dropped_SetupFlopOnGround(EnFish* this); +void EnFish_Dropped_FlopOnGround(EnFish* this, GlobalContext* globalCtx); +void EnFish_Dropped_SetupSwimAway(EnFish* this); +void EnFish_Dropped_SwimAway(EnFish* this, GlobalContext* globalCtx); +void EnFish_Unique_SetupSwimIdle(EnFish* this); +void EnFish_Unique_SwimIdle(EnFish* this, GlobalContext* globalCtx); + +// Used in the cutscene functions +static Actor* D_80A17010 = NULL; +static f32 D_80A17014 = 0.0f; +static f32 D_80A17018 = 0.0f; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 5 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +const ActorInit En_Fish_InitVars = { + ACTOR_EN_FISH, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnFish), + (ActorFunc)EnFish_Init, + (ActorFunc)EnFish_Destroy, + (ActorFunc)EnFish_Update, + (ActorFunc)EnFish_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 900, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 40, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 700, ICHAIN_STOP), +}; + +f32 EnFish_XZDistanceSquared(Vec3f* v1, Vec3f* v2) { + return SQ(v1->x - v2->x) + SQ(v1->z - v2->z); +} + +void EnFish_SetInWaterAnimation(EnFish* this) { + Animation_Change(&this->skelAnime, &gFishInWaterAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gFishInWaterAnim), + ANIMMODE_LOOP_INTERP, 2.0f); +} + +void EnFish_SetOutOfWaterAnimation(EnFish* this) { + Animation_Change(&this->skelAnime, &gFishOutOfWaterAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gFishOutOfWaterAnim), + ANIMMODE_LOOP_INTERP, 2.0f); +} + +void EnFish_BeginRespawn(EnFish* this) { + this->respawnTimer = 400; + Actor_SetScale(&this->actor, 0.001f); + this->actor.draw = NULL; +} + +void EnFish_SetCutsceneData(EnFish* this) { + Actor* thisx = &this->actor; + + if (D_80A17010 == NULL) { + D_80A17010 = thisx; + Actor_SetScale(thisx, 0.01f); + thisx->draw = EnFish_Draw; + thisx->shape.rot.x = 0; + thisx->shape.rot.y = -0x6410; + thisx->shape.rot.z = 0x4000; + thisx->shape.yOffset = 600.0f; + D_80A17014 = 10.0f; + D_80A17018 = 0.0f; + thisx->flags |= ACTOR_FLAG_4; + EnFish_SetOutOfWaterAnimation(this); + } +} + +void EnFish_ClearCutsceneData(EnFish* this) { + D_80A17010 = NULL; + D_80A17014 = 0.0f; + D_80A17018 = 0.0f; +} + +void EnFish_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFish* this = (EnFish*)thisx; + s16 params = this->actor.params; + + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishSkel, &gFishInWaterAnim, this->jointTable, this->morphTable, + 7); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + this->actor.colChkInfo.mass = 50; + this->slowPhase = Rand_ZeroOne() * (0xFFFF + 0.5f); + this->fastPhase = Rand_ZeroOne() * (0xFFFF + 0.5f); + + if (params == FISH_DROPPED) { + this->actor.flags |= ACTOR_FLAG_4; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 8.0f); + EnFish_Dropped_SetupFall(this); + } else if (params == FISH_SWIMMING_UNIQUE) { + EnFish_Unique_SetupSwimIdle(this); + } else { + EnFish_Respawning_SetupSlowDown(this); + } +} + +void EnFish_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnFish* this = (EnFish*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnFish_SetYOffset(EnFish* this) { + this->actor.shape.yOffset += (Math_SinS(this->slowPhase) * 10.0f + Math_SinS(this->fastPhase) * 5.0f); + this->actor.shape.yOffset = CLAMP(this->actor.shape.yOffset, -200.0f, 200.0f); +} + +s32 EnFish_InBottleRange(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + Vec3f sp1C; + + if (this->actor.xzDistToPlayer < 32.0f) { + sp1C.x = (Math_SinS(this->actor.yawTowardsPlayer + 0x8000) * 16.0f) + player->actor.world.pos.x; + sp1C.y = player->actor.world.pos.y; + sp1C.z = (Math_CosS(this->actor.yawTowardsPlayer + 0x8000) * 16.0f) + player->actor.world.pos.z; + + //! @bug: this check is superfluous: it is automatically satisfied if the coarse check is satisfied. It may have + //! been intended to check the actor is in front of Player, but yawTowardsPlayer does not depend on Player's + //! world rotation. + if (EnFish_XZDistanceSquared(&sp1C, &this->actor.world.pos) <= SQ(20.0f)) { + return true; + } + } + + return false; +} + +s32 EnFish_CheckXZDistanceToPlayer(EnFish* this, GlobalContext* globalCtx) { + return (this->actor.xzDistToPlayer < 60.0f); +} + +// Respawning type functions + +void EnFish_Respawning_SetupSlowDown(EnFish* this) { + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + this->timer = Rand_S16Offset(5, 35); + this->unk_250 = 0; + EnFish_SetInWaterAnimation(this); + this->actionFunc = EnFish_Respawning_SlowDown; +} + +void EnFish_Respawning_SlowDown(EnFish* this, GlobalContext* globalCtx) { + EnFish_SetYOffset(this); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.05f, 0.3f, 0.0f); + this->skelAnime.playSpeed = CLAMP_MAX(this->actor.speedXZ * 1.4f + 0.8f, 2.0f); + SkelAnime_Update(&this->skelAnime); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->timer <= 0) { + EnFish_Respawning_SetupFollowChild(this); + } else if (&this->actor == this->actor.child) { + EnFish_Respawning_SetupApproachPlayer(this); + } else if (EnFish_CheckXZDistanceToPlayer(this, globalCtx)) { + EnFish_Respawning_SetupFleePlayer(this); + } +} + +// The three following actionfunctions also turn the yaw to home if the fish is too far from it. + +void EnFish_Respawning_SetupFollowChild(EnFish* this) { + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + this->timer = Rand_S16Offset(15, 45); + this->unk_250 = 0; + EnFish_SetInWaterAnimation(this); + this->actionFunc = EnFish_Respawning_FollowChild; +} + +void EnFish_Respawning_FollowChild(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + + EnFish_SetYOffset(this); + Math_SmoothStepToF(&this->actor.speedXZ, 1.8f, 0.08f, 0.4f, 0.0f); + + if ((EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(80.0f)) || (this->timer < 4)) { + Math_StepToAngleS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos), + 3000); + } else if ((this->actor.child != NULL) && (&this->actor != this->actor.child)) { + Math_StepToAngleS(&this->actor.world.rot.y, + Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.child->world.pos), 3000); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + this->skelAnime.playSpeed = CLAMP_MAX(this->actor.speedXZ * 1.5f + 0.8f, 4.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + EnFish_Respawning_SetupSlowDown(this); + } else if (&this->actor == this->actor.child) { + EnFish_Respawning_SetupApproachPlayer(this); + } else if (EnFish_CheckXZDistanceToPlayer(this, globalCtx)) { + EnFish_Respawning_SetupFleePlayer(this); + } +} + +void EnFish_Respawning_SetupFleePlayer(EnFish* this) { + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + this->timer = Rand_S16Offset(10, 40); + this->unk_250 = 0; + EnFish_SetInWaterAnimation(this); + this->actionFunc = EnFish_Respawning_FleePlayer; +} + +void EnFish_Respawning_FleePlayer(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + s16 pad2; + s16 frames; + s16 yaw; + s16 playerClose; + + EnFish_SetYOffset(this); + playerClose = EnFish_CheckXZDistanceToPlayer(this, globalCtx); + Math_SmoothStepToF(&this->actor.speedXZ, 4.2f, 0.08f, 1.4f, 0.0f); + + if (EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(160.0f)) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_StepToAngleS(&this->actor.world.rot.y, yaw, 3000); + } else if ((this->actor.child != NULL) && (&this->actor != this->actor.child)) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.child->world.pos); + Math_StepToAngleS(&this->actor.world.rot.y, yaw, 2000); + } else if (playerClose) { + yaw = this->actor.yawTowardsPlayer + 0x8000; + frames = globalCtx->state.frames; + + if (frames & 0x10) { + if (frames & 0x20) { + yaw += 0x2000; + } + } else { + if (frames & 0x20) { + yaw -= 0x2000; + } + } + if (globalCtx) {} + Math_StepToAngleS(&this->actor.world.rot.y, yaw, 2000); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + this->skelAnime.playSpeed = CLAMP_MAX(this->actor.speedXZ * 1.5f + 0.8f, 4.0f); + + SkelAnime_Update(&this->skelAnime); + + if ((this->timer <= 0) || !playerClose) { + EnFish_Respawning_SetupSlowDown(this); + } else if (&this->actor == this->actor.child) { + EnFish_Respawning_SetupApproachPlayer(this); + } +} + +void EnFish_Respawning_SetupApproachPlayer(EnFish* this) { + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + EnFish_SetInWaterAnimation(this); + this->timer = Rand_S16Offset(10, 40); + this->unk_250 = 0; + this->actionFunc = EnFish_Respawning_ApproachPlayer; +} + +void EnFish_Respawning_ApproachPlayer(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 pad2; + Vec3f sp38; + s16 yaw; + s16 temp_a0_2; + + EnFish_SetYOffset(this); + Math_SmoothStepToF(&this->actor.speedXZ, 1.8f, 0.1f, 0.5f, 0.0f); + + if (EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(80.0f)) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_StepToAngleS(&this->actor.world.rot.y, yaw, 3000); + } else { + if ((s16)globalCtx->state.frames & 0x40) { + temp_a0_2 = (this->actor.yawTowardsPlayer + 0x9000); + } else { + temp_a0_2 = (this->actor.yawTowardsPlayer + 0x7000); + } + + sp38.x = player->actor.world.pos.x + (Math_SinS(temp_a0_2) * 20.0f); + sp38.y = player->actor.world.pos.y; + sp38.z = player->actor.world.pos.z + (Math_CosS(temp_a0_2) * 20.0f); + + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &sp38); + Math_StepToAngleS(&this->actor.world.rot.y, yaw, 3000); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + this->skelAnime.playSpeed = CLAMP_MAX((this->actor.speedXZ * 1.5f) + 0.8f, 4.0f); + + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + EnFish_Respawning_SetupSlowDown(this); + } +} + +// Dropped type functions + +void EnFish_Dropped_SetupFall(EnFish* this) { + this->actor.gravity = -1.0f; + this->actor.minVelocityY = -10.0f; + this->actor.shape.yOffset = 0.0f; + EnFish_SetOutOfWaterAnimation(this); + this->unk_250 = 5; + this->actionFunc = EnFish_Dropped_Fall; + this->timer = 300; +} + +void EnFish_Dropped_Fall(EnFish* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 0.1f, 0.0f); + Math_StepToAngleS(&this->actor.world.rot.x, 0x4000, 100); + Math_StepToAngleS(&this->actor.world.rot.z, -0x4000, 100); + this->actor.shape.rot.x = this->actor.world.rot.x; + this->actor.shape.rot.y = this->actor.world.rot.y; + this->actor.shape.rot.z = this->actor.world.rot.z; + SkelAnime_Update(&this->skelAnime); + + if (this->actor.bgCheckFlags & 1) { // On floor + this->timer = 400; + EnFish_Dropped_SetupFlopOnGround(this); + } else if (this->actor.bgCheckFlags & 0x20) { // In water + EnFish_Dropped_SetupSwimAway(this); + } else if ((this->timer <= 0) && (this->actor.params == FISH_DROPPED) && + (this->actor.floorHeight < BGCHECK_Y_MIN + 10.0f)) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "BG missing? Running Actor_delete" + osSyncPrintf("BG 抜け? Actor_delete します(%s %d)\n", "../z_en_sakana.c", 822); + osSyncPrintf(VT_RST); + Actor_Kill(&this->actor); + } +} + +/** + * If the fish is on a floor, this function is looped back to by EnFish_Dropped_FlopOnGround to set a new flopping + * height and whether the sound should play again. + */ +void EnFish_Dropped_SetupFlopOnGround(EnFish* this) { + s32 pad; + f32 randomFloat; + s32 playSound; + + this->actor.gravity = -1.0f; + this->actor.minVelocityY = -10.0f; + randomFloat = Rand_ZeroOne(); + + if (randomFloat < 0.1f) { + this->actor.velocity.y = (Rand_ZeroOne() * 3.0f) + 2.5f; + playSound = true; + } else if (randomFloat < 0.2f) { + this->actor.velocity.y = (Rand_ZeroOne() * 1.2f) + 0.2f; + playSound = true; + } else { + this->actor.velocity.y = 0.0f; + + if (Rand_ZeroOne() < 0.2f) { + playSound = true; + } else { + playSound = false; + } + } + + this->actor.shape.yOffset = 300.0f; + EnFish_SetOutOfWaterAnimation(this); + this->actionFunc = EnFish_Dropped_FlopOnGround; + this->unk_250 = 5; + + if (playSound && (this->actor.draw != NULL)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FISH_LEAP); + } +} + +void EnFish_Dropped_FlopOnGround(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + s16 frames = globalCtx->state.frames; + s16 targetXRot; + + Math_SmoothStepToF(&this->actor.speedXZ, Rand_ZeroOne() * 0.2f, 0.1f, 0.1f, 0.0f); + + targetXRot = (s16)((((frames >> 5) & 2) | ((frames >> 2) & 1)) << 0xB) * 0.3f; + + if (frames & 4) { + targetXRot = -targetXRot; + } + + Math_StepToAngleS(&this->actor.world.rot.x, targetXRot, 4000); + Math_StepToAngleS(&this->actor.world.rot.z, 0x4000, 1000); + this->actor.world.rot.y += + (s16)(((Math_SinS(this->slowPhase) * 2000.0f) + (Math_SinS(this->fastPhase) * 1000.0f)) * Rand_ZeroOne()); + this->actor.shape.rot = this->actor.world.rot; + + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + Actor_Kill(&this->actor); + return; + } + + if (this->timer <= 60) { + // Blink when about to disappear + if (frames & 4) { + this->actor.draw = EnFish_Draw; + } else { + this->actor.draw = NULL; + } + } else if (this->actor.bgCheckFlags & 0x20) { // In water + EnFish_Dropped_SetupSwimAway(this); + } else if (this->actor.bgCheckFlags & 1) { // On floor + EnFish_Dropped_SetupFlopOnGround(this); + } +} + +void EnFish_Dropped_SetupSwimAway(EnFish* this) { + this->actor.home.pos = this->actor.world.pos; + this->actor.flags |= ACTOR_FLAG_4; + this->timer = 200; + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + this->actor.shape.yOffset = 0.0f; + EnFish_SetInWaterAnimation(this); + this->actionFunc = EnFish_Dropped_SwimAway; + this->unk_250 = 5; +} + +void EnFish_Dropped_SwimAway(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + + Math_SmoothStepToF(&this->actor.speedXZ, 2.8f, 0.1f, 0.4f, 0.0f); + + // If touching wall or not in water, turn back and slow down for one frame. + if ((this->actor.bgCheckFlags & 8) || !(this->actor.bgCheckFlags & 0x20)) { + this->actor.home.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + this->actor.speedXZ *= 0.5f; + } + + Math_StepToAngleS(&this->actor.world.rot.x, 0, 1500); + Math_StepToAngleS(&this->actor.world.rot.y, this->actor.home.rot.y, 3000); + Math_StepToAngleS(&this->actor.world.rot.z, 0, 1000); + + this->actor.shape.rot = this->actor.world.rot; + + // Raise if on a floor. + if (this->actor.bgCheckFlags & 1) { + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 4.0f, 2.0f); + } else { + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 10.0f, 2.0f); + } + + // Shrink when close to disappearing. + if (this->timer < 100) { + Actor_SetScale(&this->actor, this->actor.scale.x * 0.982f); + } + + this->skelAnime.playSpeed = CLAMP_MAX((this->actor.speedXZ * 1.5f) + 1.0f, 4.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + Actor_Kill(&this->actor); + } +} + +// Unique type functions + +void EnFish_Unique_SetupSwimIdle(EnFish* this) { + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + this->timer = Rand_S16Offset(5, 35); + this->unk_250 = 0; + EnFish_SetInWaterAnimation(this); + this->actionFunc = EnFish_Unique_SwimIdle; +} + +void EnFish_Unique_SwimIdle(EnFish* this, GlobalContext* globalCtx) { + static f32 speedStopping[] = { 0.0f, 0.04f, 0.09f }; + static f32 speedMoving[] = { 0.5f, 0.1f, 0.15f }; + f32 playSpeed; + u32 frames = globalCtx->gameplayFrames; + f32* speed; + s32 pad2; + f32 extraPlaySpeed; + s32 pad3; + + if (this->actor.xzDistToPlayer < 60.0f) { + if (this->timer < 12) { + speed = speedMoving; + } else { + speed = speedStopping; + } + } else { + if (this->timer < 4) { + speed = speedMoving; + } else { + speed = speedStopping; + } + } + + EnFish_SetYOffset(this); + Math_SmoothStepToF(&this->actor.speedXZ, speed[0], speed[1], speed[2], 0.0f); + + extraPlaySpeed = 0.0f; + + if ((EnFish_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > SQ(15.0f))) { + if (!Math_ScaledStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos), + 200)) { + extraPlaySpeed = 0.5f; + } + } else if ((this->timer < 4) && !Math_ScaledStepToS(&this->actor.world.rot.y, frames * 0x80, 100)) { + extraPlaySpeed = 0.5f; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + playSpeed = (this->actor.speedXZ * 1.2f) + 0.2f + extraPlaySpeed; + this->skelAnime.playSpeed = CLAMP(playSpeed, 1.5f, 0.5); + SkelAnime_Update(&this->skelAnime); + + if (this->timer <= 0) { + this->timer = Rand_S16Offset(5, 80); + } +} + +// Cutscene functions + +void EnFish_Cutscene_FlopOnGround(EnFish* this, GlobalContext* globalCtx) { + f32 sp24 = Math_SinS(this->slowPhase); + f32 sp20 = Math_SinS(this->fastPhase); + + D_80A17014 += D_80A17018; + + if (D_80A17014 <= 1.0f) { + D_80A17014 = 1.0f; + + if (Rand_ZeroOne() < 0.1f) { + D_80A17018 = (Rand_ZeroOne() * 3.0f) + 2.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FISH_LEAP); + } else { + D_80A17018 = 0.0f; + } + } else { + D_80A17018 -= 0.4f; + } + + this->skelAnime.playSpeed = ((sp24 + sp20) * 0.5f) + 2.0f; + SkelAnime_Update(&this->skelAnime); +} + +void EnFish_Cutscene_WiggleFlyingThroughAir(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + f32 sp28 = Math_SinS(this->slowPhase); + f32 sp24 = Math_SinS(this->fastPhase); + + this->actor.shape.rot.x -= 500; + this->actor.shape.rot.z += 100; + Math_StepToF(&D_80A17014, 0.0f, 1.0f); + this->skelAnime.playSpeed = ((sp28 + sp24) * 0.5f) + 2.0f; + SkelAnime_Update(&this->skelAnime); +} + +void EnFish_UpdateCutscene(EnFish* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + CsCmdActorAction* csAction = globalCtx->csCtx.npcActions[1]; + Vec3f startPos; + Vec3f endPos; + f32 progress; + s32 bgId; + + if (csAction == NULL) { + // "Warning : DEMO ended without dousa (action) 3 termination being called" + osSyncPrintf("Warning : dousa 3 消滅 が呼ばれずにデモが終了した(%s %d)(arg_data 0x%04x)\n", "../z_en_sakana.c", + 1169, this->actor.params); + EnFish_ClearCutsceneData(this); + Actor_Kill(&this->actor); + return; + } + + this->slowPhase += 0x111; + this->fastPhase += 0x500; + + switch (csAction->action) { + case 1: + EnFish_Cutscene_FlopOnGround(this, globalCtx); + break; + case 2: + EnFish_Cutscene_WiggleFlyingThroughAir(this, globalCtx); + break; + case 3: + // "DEMO fish termination" + osSyncPrintf("デモ魚消滅\n"); + EnFish_ClearCutsceneData(this); + Actor_Kill(&this->actor); + return; + default: + // "Improper DEMO action" + osSyncPrintf("不正なデモ動作(%s %d)(arg_data 0x%04x)\n", "../z_en_sakana.c", 1200, this->actor.params); + break; + } + + startPos.x = csAction->startPos.x; + startPos.y = csAction->startPos.y; + startPos.z = csAction->startPos.z; + endPos.x = csAction->endPos.x; + endPos.y = csAction->endPos.y; + endPos.z = csAction->endPos.z; + + progress = Environment_LerpWeight(csAction->endFrame, csAction->startFrame, globalCtx->csCtx.frames); + + this->actor.world.pos.x = (endPos.x - startPos.x) * progress + startPos.x; + this->actor.world.pos.y = (endPos.y - startPos.y) * progress + startPos.y + D_80A17014; + this->actor.world.pos.z = (endPos.z - startPos.z) * progress + startPos.z; + + this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId, + &this->actor, &this->actor.world.pos); +} + +// Update functions and Draw + +void EnFish_OrdinaryUpdate(EnFish* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + } + + this->slowPhase += 0x111; + this->fastPhase += 0x500; + + if ((this->actor.child != NULL) && (this->actor.child->update == NULL) && (&this->actor != this->actor.child)) { + this->actor.child = NULL; + } + + if ((this->actionFunc == NULL) || (this->actionFunc(this, globalCtx), (this->actor.update != NULL))) { + Actor_MoveForward(&this->actor); + + if (this->unk_250 != 0) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 17.5f, 4.0f, 0.0f, this->unk_250); + } + + if (this->actor.xzDistToPlayer < 70.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + Actor_SetFocus(&this->actor, this->actor.shape.yOffset * 0.01f); + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + + if (this->actor.params == FISH_DROPPED) { + Actor_Kill(&this->actor); + return; + } + + EnFish_BeginRespawn(this); + } else if (EnFish_InBottleRange(this, globalCtx)) { + // GI_MAX in this case allows the player to catch the actor in a bottle + func_8002F434(&this->actor, globalCtx, GI_MAX, 80.0f, 20.0f); + } + } +} + +void EnFish_RespawningUpdate(EnFish* this, GlobalContext* globalCtx) { + if (this->actor.params == FISH_SWIMMING_UNIQUE) { + Actor_Kill(&this->actor); + return; + } + + if ((this->actor.child != NULL) && (this->actor.child->update == NULL) && (&this->actor != this->actor.child)) { + this->actor.child = NULL; + } + + if ((this->actionFunc == NULL) || (this->actionFunc(this, globalCtx), (this->actor.update != NULL))) { + Actor_MoveForward(&this->actor); + + if (this->respawnTimer == 20) { + this->actor.draw = EnFish_Draw; + } else if (this->respawnTimer == 0) { + Actor_SetScale(&this->actor, 0.01f); + } else if (this->respawnTimer < 20) { + Actor_SetScale(&this->actor, CLAMP_MAX(this->actor.scale.x + 0.001f, 0.01f)); + } + } +} + +void EnFish_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFish* this = (EnFish*)thisx; + + if ((D_80A17010 == NULL) && (this->actor.params == FISH_DROPPED) && (globalCtx->csCtx.state != 0) && + (globalCtx->csCtx.npcActions[1] != NULL)) { + EnFish_SetCutsceneData(this); + } + + if ((D_80A17010 != NULL) && (&this->actor == D_80A17010)) { + EnFish_UpdateCutscene(this, globalCtx); + } else if (this->respawnTimer > 0) { + this->respawnTimer--; + EnFish_RespawningUpdate(this, globalCtx); + } else { + EnFish_OrdinaryUpdate(this, globalCtx); + } +} + +void EnFish_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnFish* this = (EnFish*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, NULL); + Collider_UpdateSpheres(0, &this->collider); +} diff --git a/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.h b/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.h new file mode 100644 index 000000000..911f79a45 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fish/z_en_fish.h @@ -0,0 +1,32 @@ +#ifndef Z_EN_FISH_H +#define Z_EN_FISH_H + +#include "ultra64.h" +#include "global.h" + +struct EnFish; + +typedef void (*EnFishActionFunc)(struct EnFish*, GlobalContext*); + +typedef struct EnFish { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderJntSph collider; + /* 0x016C */ ColliderJntSphElement colliderItems[1]; + /* 0x01AC */ SkelAnime skelAnime; + /* 0x01F0 */ Vec3s jointTable[7]; + /* 0x021A */ Vec3s morphTable[7]; + /* 0x0244 */ EnFishActionFunc actionFunc; + /* 0x0248 */ s16 timer; + /* 0x024A */ s16 respawnTimer; + /* 0x024C */ s16 slowPhase; + /* 0x024E */ s16 fastPhase; + /* 0x0250 */ s32 unk_250; // Set to 0 or 5, arg5 of Actor_UpdateBgCheckInfo +} EnFish; // size = 0x0254 + +typedef enum { + /* -1 */ FISH_SWIMMING_RESPAWNING = -1, // Used in Zora's Domain; code only uses not 0 or 1, runs away from Player + /* 0 */ FISH_DROPPED, + /* 1 */ FISH_SWIMMING_UNIQUE // Used in grottos +} EnFishType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c new file mode 100644 index 000000000..9e9e8f988 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -0,0 +1,1145 @@ +/* + * File: z_en_floormas + * Overlay: En_Floormas + * Description: Floormaster + */ + +#include "z_en_floormas.h" +#include "objects/object_wallmaster/object_wallmaster.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_10) + +#define SPAWN_INVISIBLE 0x8000 +#define SPAWN_SMALL 0x10 + +#define MERGE_MASTER 0x40 +#define MERGE_SLAVE 0x20 + +void EnFloormas_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFloormas_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFloormas_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFloormas_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnFloormas_GrabLink(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Split(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Recover(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_DrawHighlighted(Actor* this, GlobalContext* globalCtx); +void EnFloormas_SmWait(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_SetupBigDecideAction(EnFloormas* this); +void EnFloormas_Freeze(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_TakeDamage(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Merge(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_JumpAtLink(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_SmSlaveJumpAtMaster(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_SmShrink(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_SmDecideAction(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_SmWalk(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Land(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Hover(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Turn(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Run(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_BigStopWalk(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_BigWalk(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Stand(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_BigDecideAction(EnFloormas* this, GlobalContext* globalCtx); +void EnFloormas_Charge(EnFloormas* this, GlobalContext* globalCtx); + +const ActorInit En_Floormas_InitVars = { + ACTOR_EN_FLOORMAS, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_WALLMASTER, + sizeof(EnFloormas), + (ActorFunc)EnFloormas_Init, + (ActorFunc)EnFloormas_Destroy, + (ActorFunc)EnFloormas_Update, + (ActorFunc)EnFloormas_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x04, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 25, 40, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 4, 30, 40, 150 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(4, 0x4), + /* Unk arrow 1 */ DMG_ENTRY(4, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(4, 0x4), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x31, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 5500, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_STOP), +}; + +void EnFloormas_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnFloormas* this = (EnFloormas*)thisx; + GlobalContext* globalCtx = globalCtx2; + s32 invisble; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 50.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gWallmasterSkel, &gWallmasterWaitAnim, this->jointTable, + this->morphTable, 25); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + this->zOffset = -1600; + invisble = this->actor.params & SPAWN_INVISIBLE; + + // s16 cast needed + this->actor.params &= (s16) ~(SPAWN_INVISIBLE); + if (invisble) { + this->actor.flags |= ACTOR_FLAG_7; + this->actor.draw = EnFloormas_DrawHighlighted; + } + + if (this->actor.params == SPAWN_SMALL) { + this->actor.draw = NULL; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnFloormas_SmWait; + } else { + // spawn first small floormaster + this->actor.parent = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_FLOORMAS, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, invisble + SPAWN_SMALL); + if (this->actor.parent == NULL) { + Actor_Kill(&this->actor); + return; + } + // spawn 2nd small floormaster + this->actor.child = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_FLOORMAS, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, invisble + SPAWN_SMALL); + if (this->actor.child == NULL) { + Actor_Kill(this->actor.parent); + Actor_Kill(&this->actor); + return; + } + + // link floormasters together + this->actor.parent->child = &this->actor; + this->actor.parent->parent = this->actor.child; + this->actor.child->parent = &this->actor; + this->actor.child->child = this->actor.parent; + EnFloormas_SetupBigDecideAction(this); + } +} + +void EnFloormas_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFloormas* this = (EnFloormas*)thisx; + ColliderCylinder* col = &this->collider; + Collider_DestroyCylinder(globalCtx, col); +} + +void EnFloormas_MakeInvulnerable(EnFloormas* this) { + this->collider.base.colType = COLTYPE_HARD; + this->collider.base.acFlags |= AC_HARD; + this->actionTarget = 0x28; +} + +void EnFloormas_MakeVulnerable(EnFloormas* this) { + this->collider.base.colType = COLTYPE_HIT0; + this->actionTarget = 0; + this->collider.base.acFlags &= ~AC_HARD; +} + +void EnFloormas_SetupBigDecideAction(EnFloormas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterWaitAnim); + this->actionFunc = EnFloormas_BigDecideAction; + this->actor.speedXZ = 0.0f; +} + +void EnFloormas_SetupStand(EnFloormas* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWallmasterStandUpAnim, -3.0f); + this->actionFunc = EnFloormas_Stand; +} + +void EnFloormas_SetupBigWalk(EnFloormas* this) { + if (this->actionFunc != EnFloormas_Run) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gWallmasterWalkAnim, 1.5f); + } else { + this->skelAnime.playSpeed = 1.5f; + } + + this->actionTimer = Rand_S16Offset(2, 4); + this->actionFunc = EnFloormas_BigWalk; + this->actor.speedXZ = 1.5f; +} + +void EnFloormas_SetupBigStopWalk(EnFloormas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterStopWalkAnim); + this->actionFunc = EnFloormas_BigStopWalk; + this->actor.speedXZ = 0.0f; +} + +void EnFloormas_SetupRun(EnFloormas* this) { + this->actionTimer = 0; + this->actionFunc = EnFloormas_Run; + this->actor.speedXZ = 5.0f; + this->skelAnime.playSpeed = 3.0f; +} + +void EnFloormas_SetupTurn(EnFloormas* this) { + s16 rotDelta = this->actionTarget - this->actor.shape.rot.y; + + this->actor.speedXZ = 0.0f; + if (rotDelta > 0) { + Animation_MorphToPlayOnce(&this->skelAnime, &gFloormasterTurnAnim, -3.0f); + } else { + Animation_Change(&this->skelAnime, &gFloormasterTurnAnim, -1.0f, Animation_GetLastFrame(&gFloormasterTurnAnim), + 0.0f, ANIMMODE_ONCE, -3.0f); + } + + if (this->actor.scale.x > 0.004f) { + this->actionTarget = (rotDelta * (2.0f / 30.0f)); + } else { + this->skelAnime.playSpeed = this->skelAnime.playSpeed + this->skelAnime.playSpeed; + this->actionTarget = rotDelta * (2.0f / 15.0f); + } + this->actionFunc = EnFloormas_Turn; +} + +void EnFloormas_SetupHover(EnFloormas* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gWallmasterHoverAnim, 3.0f, 0, Animation_GetLastFrame(&gWallmasterHoverAnim), + ANIMMODE_ONCE, -3.0f); + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + EnFloormas_MakeInvulnerable(this); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 15.0f, 6, 20.0f, 0x12C, 0x64, 1); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_ATTACK); + this->actionFunc = EnFloormas_Hover; +} + +void EnFloormas_SetupCharge(EnFloormas* this) { + this->actionTimer = 25; + this->actor.gravity = -0.15f; + this->actionFunc = EnFloormas_Charge; + this->actor.speedXZ = 0.5f; +} + +void EnFloormas_SetupLand(EnFloormas* this) { + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 1.0f, 41.0f, 42.0f, ANIMMODE_ONCE, 5.0f); + if ((this->actor.speedXZ < 0.0f) || (this->actionFunc != EnFloormas_Charge)) { + this->actionTimer = 30; + } else { + this->actionTimer = 45; + } + this->actor.gravity = -1.0f; + this->actionFunc = EnFloormas_Land; +} + +void EnFloormas_SetupSplit(EnFloormas* this) { + + Actor_SetScale(&this->actor, 0.004f); + this->actor.flags |= ACTOR_FLAG_4; + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + this->actor.draw = EnFloormas_DrawHighlighted; + } else { + this->actor.draw = EnFloormas_Draw; + } + this->actor.shape.rot.y = this->actor.parent->shape.rot.y + 0x5555; + this->actor.world.pos = this->actor.parent->world.pos; + this->actor.params = 0x10; + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 1.0f, 41.0f, Animation_GetLastFrame(&gWallmasterJumpAnim), + ANIMMODE_ONCE, 0.0f); + this->collider.dim.radius = sCylinderInit.dim.radius * 0.6f; + this->collider.dim.height = sCylinderInit.dim.height * 0.6f; + this->collider.info.bumperFlags &= ~BUMP_HOOKABLE; + this->actor.speedXZ = 4.0f; + this->actor.velocity.y = 7.0f; + // using div creates a signed check. + this->actor.colChkInfo.health = sColChkInfoInit.health >> 1; + this->actionFunc = EnFloormas_Split; +} + +void EnFloormas_SetupSmWalk(EnFloormas* this) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gWallmasterWalkAnim, 4.5f); + this->actionFunc = EnFloormas_SmWalk; + this->actor.speedXZ = 5.0f; +} + +void EnFloormas_SetupSmDecideAction(EnFloormas* this) { + if (this->actionFunc != EnFloormas_SmWalk) { + Animation_PlayLoopSetSpeed(&this->skelAnime, &gWallmasterWalkAnim, 4.5f); + } + this->actionFunc = EnFloormas_SmDecideAction; + this->actor.speedXZ = 5.0f; +} + +void EnFloormas_SetupSmShrink(EnFloormas* this, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 15.0f; + pos.z = this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &pos, &velocity, &accel, 150, -10, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); + this->actionFunc = EnFloormas_SmShrink; +} + +void EnFloormas_SetupSlaveJumpAtMaster(EnFloormas* this) { + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 2.0f, 0.0f, 41.0f, ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnFloormas_SmSlaveJumpAtMaster; + this->actor.speedXZ = 0.0f; +} + +void EnFloormas_SetupJumpAtLink(EnFloormas* this) { + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 2.0f, 0.0f, 41.0f, ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnFloormas_JumpAtLink; + this->actor.speedXZ = 0.0f; +} + +void EnFloormas_SetupGrabLink(EnFloormas* this, Player* player) { + f32 yDelta; + f32 xzDelta; + + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 1.0f, 36.0f, 45.0f, ANIMMODE_ONCE, -3.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + EnFloormas_MakeInvulnerable(this); + if (!LINK_IS_ADULT) { + yDelta = CLAMP(-this->actor.yDistToPlayer, 20.0f, 30.0f); + xzDelta = -10.0f; + } else { + yDelta = CLAMP(-this->actor.yDistToPlayer, 25.0f, 45.0f); + xzDelta = -70.0f; + } + this->actor.world.pos.y = player->actor.world.pos.y + yDelta; + this->actor.world.pos.x = Math_SinS(this->actor.shape.rot.y) * (xzDelta * 0.1f) + player->actor.world.pos.x; + this->actor.world.pos.z = Math_CosS(this->actor.shape.rot.y) * (xzDelta * 0.1f) + player->actor.world.pos.z; + this->actor.shape.rot.x = -0x4CE0; + this->actionFunc = EnFloormas_GrabLink; +} + +void EnFloormas_SetupMerge(EnFloormas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterWaitAnim); + this->actionTimer = 0; + this->smActionTimer += 1500; + EnFloormas_MakeInvulnerable(this); + this->actionFunc = EnFloormas_Merge; +} + +void EnFloormas_SetupSmWait(EnFloormas* this) { + EnFloormas* parent = (EnFloormas*)this->actor.parent; + EnFloormas* child = (EnFloormas*)this->actor.child; + + // if this is the last remaining small floor master, kill all. + if ((parent->actionFunc == EnFloormas_SmWait) && (child->actionFunc == EnFloormas_SmWait)) { + Actor_Kill(&parent->actor); + Actor_Kill(&child->actor); + Actor_Kill(&this->actor); + return; + } + this->actor.draw = NULL; + this->actionFunc = EnFloormas_SmWait; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_4); +} + +void EnFloormas_SetupTakeDamage(EnFloormas* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWallmasterDamageAnim, -3.0f); + if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F824) { + this->actor.world.rot.y = this->collider.base.ac->world.rot.y; + } else { + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, this->collider.base.ac) + 0x8000; + } + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x14); + this->actionFunc = EnFloormas_TakeDamage; + this->actor.speedXZ = 5.0f; + this->actor.velocity.y = 5.5f; +} + +void EnFloormas_SetupRecover(EnFloormas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterRecoverFromDamageAnim); + this->actor.velocity.y = this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actionFunc = EnFloormas_Recover; +} + +void EnFloormas_SetupFreeze(EnFloormas* this) { + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 1.5f, 0, 20.0f, ANIMMODE_ONCE, -3.0f); + this->actor.speedXZ = 0.0f; + if (this->actor.colChkInfo.damageEffect == 4) { + Actor_SetColorFilter(&this->actor, -0x8000, 0xFF, 0, 0x50); + } else { + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + if (this->actor.scale.x > 0.004f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + } + } + this->actionTimer = 80; + this->actionFunc = EnFloormas_Freeze; +} + +void EnFloormas_Die(EnFloormas* this, GlobalContext* globalCtx) { + if (this->actor.scale.x > 0.004f) { + // split + this->actor.shape.rot.y = this->actor.yawTowardsPlayer + 0x8000; + EnFloormas_SetupSplit((EnFloormas*)this->actor.child); + EnFloormas_SetupSplit((EnFloormas*)this->actor.parent); + EnFloormas_SetupSplit(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SPLIT); + } else { + // Die + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x90); + EnFloormas_SetupSmShrink(this, globalCtx); + } +} + +void EnFloormas_BigDecideAction(EnFloormas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + // within 400 units of link and within 90 degrees rotation of him + if (this->actor.xzDistToPlayer < 400.0f && !Actor_IsFacingPlayer(&this->actor, 0x4000)) { + this->actionTarget = this->actor.yawTowardsPlayer; + EnFloormas_SetupTurn(this); + // within 280 units of link and within 45 degrees rotation of him + } else if (this->actor.xzDistToPlayer < 280.0f && Actor_IsFacingPlayer(&this->actor, 0x2000)) { + EnFloormas_SetupHover(this, globalCtx); + } else { + EnFloormas_SetupStand(this); + } + } +} + +void EnFloormas_Stand(EnFloormas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.scale.x > 0.004f) { + EnFloormas_SetupBigWalk(this); + } else if (this->actor.params == MERGE_SLAVE) { + EnFloormas_SetupSmDecideAction(this); + } else { + EnFloormas_SetupSmWalk(this); + } + } +} + +void EnFloormas_BigWalk(EnFloormas* this, GlobalContext* globalCtx) { + s32 animPastFrame; + + SkelAnime_Update(&this->skelAnime); + animPastFrame = Animation_OnFrame(&this->skelAnime, 0.0f); + if (animPastFrame) { + if (this->actionTimer != 0) { + this->actionTimer--; + } + } + if (((animPastFrame || (Animation_OnFrame(&this->skelAnime, 12.0f))) || + (Animation_OnFrame(&this->skelAnime, 24.0f) != 0)) || + (Animation_OnFrame(&this->skelAnime, 36.0f) != 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_WALK); + } + + if ((this->actor.xzDistToPlayer < 320.0f) && (Actor_IsFacingPlayer(&this->actor, 0x4000))) { + EnFloormas_SetupRun(this); + } else if (this->actor.bgCheckFlags & 8) { + // set target rotation to the colliding wall's rotation + this->actionTarget = this->actor.wallYaw; + EnFloormas_SetupTurn(this); + } else if ((this->actor.xzDistToPlayer < 400.0f) && !Actor_IsFacingPlayer(&this->actor, 0x4000)) { + // set target rotation to link. + this->actionTarget = this->actor.yawTowardsPlayer; + EnFloormas_SetupTurn(this); + } else if (this->actionTimer == 0) { + EnFloormas_SetupBigStopWalk(this); + } +} + +void EnFloormas_BigStopWalk(EnFloormas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnFloormas_SetupBigDecideAction(this); + } +} + +void EnFloormas_Run(EnFloormas* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f) || + Animation_OnFrame(&this->skelAnime, 24.0f) || Animation_OnFrame(&this->skelAnime, 36.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_WALK); + } + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x71C); + + if ((this->actor.xzDistToPlayer < 280.0f) && Actor_IsFacingPlayer(&this->actor, 0x2000) && + !(this->actor.bgCheckFlags & 8)) { + EnFloormas_SetupHover(this, globalCtx); + } else if (this->actor.xzDistToPlayer > 400.0f) { + EnFloormas_SetupBigWalk(this); + } +} + +void EnFloormas_Turn(EnFloormas* this, GlobalContext* globalCtx) { + char pad[4]; + f32 sp30; + f32 sp2C; + + if (SkelAnime_Update(&this->skelAnime)) { + EnFloormas_SetupStand(this); + } + + if (((this->skelAnime.playSpeed > 0.0f) && Animation_OnFrame(&this->skelAnime, 21.0f)) || + ((this->skelAnime.playSpeed < 0.0f) && Animation_OnFrame(&this->skelAnime, 6.0f))) { + if (this->actor.scale.x > 0.004f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_WALK); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_WALK); + } + } + // Needed to match + if (!this->skelAnime.curFrame) {} + if (this->skelAnime.curFrame >= 7.0f && this->skelAnime.curFrame < 22.0f) { + sp30 = Math_SinS(this->actor.shape.rot.y + 0x4268); + sp2C = Math_CosS(this->actor.shape.rot.y + 0x4268); + this->actor.shape.rot.y += this->actionTarget; + this->actor.world.pos.x -= + (this->actor.scale.x * 2700.0f) * (Math_SinS(this->actor.shape.rot.y + 0x4268) - sp30); + this->actor.world.pos.z -= + (this->actor.scale.x * 2700.0f) * (Math_CosS(this->actor.shape.rot.y + 0x4268) - sp2C); + } +} + +void EnFloormas_Hover(EnFloormas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnFloormas_SetupCharge(this); + } + this->actor.shape.rot.x += 0x140; + this->actor.world.pos.y += 10.0f; + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 2730); + Math_StepToS(&this->zOffset, 1200, 100); +} + +void EnFloormas_Slide(EnFloormas* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + Vec3f velocity; + + pos.x = this->actor.world.pos.x; + pos.z = this->actor.world.pos.z; + pos.y = this->actor.floorHeight; + + velocity.y = 2.0f; + velocity.x = Math_SinS(this->actor.shape.rot.y + 0x6000) * 7.0f; + velocity.z = Math_CosS(this->actor.shape.rot.y + 0x6000) * 7.0f; + + func_800286CC(globalCtx, &pos, &velocity, &accel, 450, 100); + + velocity.x = Math_SinS(this->actor.shape.rot.y - 0x6000) * 7.0f; + velocity.z = Math_CosS(this->actor.shape.rot.y - 0x6000) * 7.0f; + + func_800286CC(globalCtx, &pos, &velocity, &accel, 450, 100); + + func_8002F974(&this->actor, NA_SE_EN_FLOORMASTER_SLIDING); +} + +void EnFloormas_Charge(EnFloormas* this, GlobalContext* globalCtx) { + f32 distFromGround; + + if (this->actionTimer != 0) { + this->actionTimer--; + } + + Math_StepToF(&this->actor.speedXZ, 15.0f, SQ(this->actor.speedXZ) * (1.0f / 3.0f)); + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x1680, 0x140); + + distFromGround = this->actor.world.pos.y - this->actor.floorHeight; + if (distFromGround < 10.0f) { + this->actor.world.pos.y = this->actor.floorHeight + 10.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + } + + if (distFromGround < 12.0f) { + EnFloormas_Slide(this, globalCtx); + } + + if ((this->actor.bgCheckFlags & 8) || (this->actionTimer == 0)) { + EnFloormas_SetupLand(this); + } +} + +void EnFloormas_Land(EnFloormas* this, GlobalContext* globalCtx) { + s32 isOnGround; + + isOnGround = this->actor.bgCheckFlags & 1; + if (this->actor.bgCheckFlags & 2) { + if (this->actor.params != MERGE_MASTER) { + EnFloormas_MakeVulnerable(this); + } + + if (this->actor.velocity.y < -4.0f) { + if (this->actor.scale.x > 0.004f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_LAND); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_LAND); + } + } + } + if (this->actor.bgCheckFlags & 8) { + this->actor.speedXZ = 0.0f; + } + + if (isOnGround) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 2.0f); + } + + if ((this->actor.speedXZ > 0.0f) && ((this->actor.world.pos.y - this->actor.floorHeight) < 12.0f)) { + EnFloormas_Slide(this, globalCtx); + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actionTimer != 0) { + this->actionTimer--; + } + + if (this->actionTimer == 0 && isOnGround) { + if (this->skelAnime.endFrame < 45.0f) { + this->skelAnime.endFrame = Animation_GetLastFrame(&gWallmasterJumpAnim); + } else if (this->actor.params == MERGE_MASTER) { + EnFloormas_SetupMerge(this); + } else { + EnFloormas_SetupStand(this); + this->smActionTimer = 50; + } + } + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x140); + Math_StepToS(&this->zOffset, -1600, 100); +} + +void EnFloormas_Split(EnFloormas* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1) { + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.flags |= ACTOR_FLAG_0; + this->smActionTimer = 50; + EnFloormas_SetupStand(this); + } + Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); + } + + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_LAND); + } +} + +void EnFloormas_SmWalk(EnFloormas* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + DECR(this->smActionTimer); + + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 18.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_WALK); + } + + if (this->smActionTimer == 0) { + EnFloormas_SetupSmDecideAction(this); + } else if (this->actor.bgCheckFlags & 8) { + this->actionTarget = this->actor.wallYaw; + EnFloormas_SetupTurn(this); + } else if (this->actor.xzDistToPlayer < 120.0f) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + 0x8000, 0x38E); + } +} + +void EnFloormas_SmDecideAction(EnFloormas* this, GlobalContext* globalCtx) { + Actor* primaryFloormas; + s32 isAgainstWall; + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 18.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_WALK); + } + isAgainstWall = this->actor.bgCheckFlags & 8; + if (isAgainstWall) { + this->actionTarget = this->actor.wallYaw; + EnFloormas_SetupTurn(this); + return; + } + + if (this->actor.params == MERGE_SLAVE) { + if (this->actor.parent->params == MERGE_MASTER) { + primaryFloormas = this->actor.parent; + } else if (this->actor.child->params == MERGE_MASTER) { + primaryFloormas = this->actor.child; + } else { + this->actor.params = 0x10; + return; + } + + Math_ScaledStepToS(&this->actor.shape.rot.y, Actor_WorldYawTowardActor(&this->actor, primaryFloormas), 0x38E); + if (Actor_WorldDistXZToActor(&this->actor, primaryFloormas) < 80.0f) { + EnFloormas_SetupSlaveJumpAtMaster(this); + } + } else { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x71C); + if (this->actor.xzDistToPlayer < 80.0f) { + EnFloormas_SetupJumpAtLink(this); + } + } +} + +void EnFloormas_SmShrink(EnFloormas* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0015f)) { + EnFloormas_SetupSmWait(this); + } + this->actor.scale.z = this->actor.scale.x; + this->actor.scale.y = this->actor.scale.x; +} + +void EnFloormas_JumpAtLink(EnFloormas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + if (this->skelAnime.curFrame < 20.0f) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + } else if (Animation_OnFrame(&this->skelAnime, 20.0f)) { + this->actor.speedXZ = 5.0f; + this->actor.velocity.y = 7.0f; + } else if (this->actor.bgCheckFlags & 2) { + this->actionTimer = 0x32; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_LAND); + EnFloormas_SetupLand(this); + } else if ((this->actor.yDistToPlayer < -10.0f) && (this->collider.base.ocFlags1 & OC1_HIT) && + (&player->actor == this->collider.base.oc)) { + globalCtx->grabPlayer(globalCtx, player); + EnFloormas_SetupGrabLink(this, player); + } +} + +void EnFloormas_GrabLink(EnFloormas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnFloormas* parent; + EnFloormas* child; + f32 yDelta; + f32 xzDelta; + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->skelAnime.playSpeed > 0.0f) { + this->skelAnime.playSpeed = -1.0f; + this->skelAnime.endFrame = 36.0f; + this->skelAnime.startFrame = 45.0f; + } else { + this->skelAnime.playSpeed = 1.0f; + this->skelAnime.endFrame = 45.0f; + this->skelAnime.startFrame = 36.0f; + } + } + + if (!LINK_IS_ADULT) { + yDelta = CLAMP(-this->actor.yDistToPlayer, 20.0f, 30.0f); + xzDelta = -10.0f; + } else { + yDelta = CLAMP(-this->actor.yDistToPlayer, 25.0f, 45.0f); + xzDelta = -30.0f; + } + + this->actor.world.pos.y = player->actor.world.pos.y + yDelta; + this->actor.world.pos.x = Math_SinS(this->actor.shape.rot.y) * (xzDelta * 0.1f) + player->actor.world.pos.x; + this->actor.world.pos.z = Math_CosS(this->actor.shape.rot.y) * (xzDelta * 0.1f) + player->actor.world.pos.z; + + // let go + if (!(player->stateFlags2 & 0x80) || (player->invincibilityTimer < 0)) { + parent = (EnFloormas*)this->actor.parent; + child = (EnFloormas*)this->actor.child; + + if (((parent->actionFunc == EnFloormas_GrabLink) || parent->actionFunc == EnFloormas_SmWait) && + (child->actionFunc == EnFloormas_GrabLink || child->actionFunc == EnFloormas_SmWait)) { + + parent->actor.params = MERGE_SLAVE; + child->actor.params = MERGE_SLAVE; + this->actor.params = MERGE_MASTER; + } + + this->actor.shape.rot.x = 0; + this->actor.velocity.y = 6.0f; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.speedXZ = -3.0f; + EnFloormas_SetupLand(this); + } else { + // Damage link every 20 frames + if ((this->actionTarget % 20) == 0) { + if (!LINK_IS_ADULT) { + func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S_KID); + } else { + func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S); + } + globalCtx->damagePlayer(globalCtx, -8); + } + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_STICK); +} + +void EnFloormas_SmSlaveJumpAtMaster(EnFloormas* this, GlobalContext* globalCtx) { + Actor* primFloormas; + + SkelAnime_Update(&this->skelAnime); + if (this->actor.parent->params == MERGE_MASTER) { + primFloormas = this->actor.parent; + } else if (this->actor.child->params == MERGE_MASTER) { + primFloormas = this->actor.child; + } else { + if (this->actor.bgCheckFlags & 2) { + this->actor.params = 0x10; + EnFloormas_SetupLand(this); + } + return; + } + if (Animation_OnFrame(&this->skelAnime, 20.0f)) { + this->actor.speedXZ = 5.0f; + this->actor.velocity.y = 7.0f; + } else if (this->skelAnime.curFrame < 20.0f) { + Math_ApproachS(&this->actor.shape.rot.y, Actor_WorldYawTowardActor(&this->actor, primFloormas), 2, 0xE38); + } else if ((((primFloormas->world.pos.y - this->actor.world.pos.y) < -10.0f) && + (fabsf(this->actor.world.pos.x - primFloormas->world.pos.x) < 10.0f)) && + (fabsf(this->actor.world.pos.z - primFloormas->world.pos.z) < 10.0f)) { + EnFloormas_SetupSmWait(this); + this->collider.base.ocFlags1 |= OC1_ON; + } else if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_LAND); + EnFloormas_SetupLand(this); + } + + if (fabsf(this->actor.world.pos.x - primFloormas->world.pos.x) < 5.0f && + fabsf(this->actor.world.pos.z - primFloormas->world.pos.z) < 5.0f) { + Math_StepToF(&this->actor.speedXZ, 0, 2.0f); + } +} + +void EnFloormas_Merge(EnFloormas* this, GlobalContext* globalCtx) { + EnFloormas* parent; + EnFloormas* child; + s32 mergeCnt; + f32 prevScale; + f32 curScale; + + mergeCnt = 0; + + DECR(this->smActionTimer); + + parent = (EnFloormas*)this->actor.parent; + child = (EnFloormas*)this->actor.child; + + if (this->smActionTimer == 0) { + if (parent->actionFunc != EnFloormas_SmWait) { + EnFloormas_SetupSmShrink(parent, globalCtx); + } + + if (child->actionFunc != EnFloormas_SmWait) { + EnFloormas_SetupSmShrink(child, globalCtx); + } + } else { + if ((parent->actionFunc != EnFloormas_SmWait) && (parent->actionFunc != EnFloormas_SmShrink)) { + mergeCnt++; + } + + if ((child->actionFunc != EnFloormas_SmWait) && (child->actionFunc != EnFloormas_SmShrink)) { + mergeCnt++; + } + } + + prevScale = this->actor.scale.x; + + if (mergeCnt == 1) { + Math_StepToF(&this->actor.scale.x, 0.007f, 0.0005f); + } else if (mergeCnt == 0) { + Math_StepToF(&this->actor.scale.x, 0.01f, 0.0005f); + } + + curScale = this->actor.scale.x; + this->actor.scale.y = this->actor.scale.z = curScale; + + if (((prevScale == 0.007f) || (prevScale == 0.004f)) && (prevScale != this->actor.scale.x)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_EXPAND); + } + + this->collider.dim.radius = (sCylinderInit.dim.radius * 100.0f) * this->actor.scale.x; + this->collider.dim.height = (sCylinderInit.dim.height * 100.0f) * this->actor.scale.x; + + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->actor.scale.x >= 0.01f) { + this->actor.flags &= ~ACTOR_FLAG_4; + EnFloormas_MakeVulnerable(this); + this->actor.params = 0; + this->collider.info.bumperFlags |= BUMP_HOOKABLE; + this->actor.colChkInfo.health = sColChkInfoInit.health; + EnFloormas_SetupStand(this); + } else { + if (this->actionTimer == 0) { + Animation_PlayOnce(&this->skelAnime, &gFloormasterTapFingerAnim); + this->actionTimer = 1; + } else { + Animation_PlayOnce(&this->skelAnime, &gWallmasterWaitAnim); + this->actionTimer = 0; + } + } + } + func_8002F974(&this->actor, NA_SE_EN_FLOORMASTER_RESTORE - SFX_FLAG); +} + +void EnFloormas_SmWait(EnFloormas* this, GlobalContext* globalCtx) { +} + +void EnFloormas_TakeDamage(EnFloormas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->actor.colChkInfo.health == 0) { + EnFloormas_Die(this, globalCtx); + } else { + EnFloormas_SetupRecover(this); + } + } + + if (Animation_OnFrame(&this->skelAnime, 13.0f)) { + if (this->actor.scale.x > 0.004f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + } + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.2f); +} + +void EnFloormas_Recover(EnFloormas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnFloormas_SetupStand(this); + } +} + +void EnFloormas_Freeze(EnFloormas* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (this->actionTimer == 0) { + if (this->actor.colChkInfo.health == 0) { + EnFloormas_Die(this, globalCtx); + return; + } + EnFloormas_SetupStand(this); + } +} + +void EnFloormas_ColliderCheck(EnFloormas* this, GlobalContext* globalCtx) { + s32 pad; + s32 isSmall; + + if ((this->collider.base.acFlags & AC_HIT) != 0) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (this->collider.base.colType != COLTYPE_HARD) { + isSmall = 0; + if (this->actor.scale.x < 0.01f) { + isSmall = 1; + } + if (isSmall && this->collider.info.acHitInfo->toucher.dmgFlags & 0x80) { + this->actor.colChkInfo.damage = 2; + this->actor.colChkInfo.damageEffect = 0; + } + if (Actor_ApplyDamage(&this->actor) == 0) { + if (isSmall) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLOORMASTER_SM_DEAD); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_DEAD); + } + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_0; + } else if (this->actor.colChkInfo.damage != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_DAMAGE); + } + + if ((this->actor.colChkInfo.damageEffect == 4) || (this->actor.colChkInfo.damageEffect == 1)) { + if (this->actionFunc != EnFloormas_Freeze) { + EnFloormas_SetupFreeze(this); + } + } else { + if (this->actor.colChkInfo.damageEffect == 2) { + EffectSsFCircle_Spawn(globalCtx, &this->actor, &this->actor.world.pos, + this->actor.scale.x * 4000.0f, this->actor.scale.x * 4000.0f); + } + EnFloormas_SetupTakeDamage(this); + } + } + } + } +} + +void EnFloormas_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFloormas* this = (EnFloormas*)thisx; + s32 pad; + + if (this->actionFunc != EnFloormas_SmWait) { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + this->actor.speedXZ *= -0.5f; + + if (-5.0f < this->actor.speedXZ) { + this->actor.speedXZ = -5.0f; + } else { + this->actor.speedXZ = this->actor.speedXZ; + } + + this->actor.velocity.y = 5.0f; + + EnFloormas_SetupLand(this); + } + EnFloormas_ColliderCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + + if (this->actionFunc != EnFloormas_TakeDamage) { + this->actor.world.rot.y = this->actor.shape.rot.y; + } + + if (this->actionFunc != EnFloormas_GrabLink) { + Actor_MoveForward(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, this->actor.scale.x * 3000.0f, 0.0f, 0x1D); + Collider_UpdateCylinder(&this->actor, &this->collider); + if (this->actionFunc == EnFloormas_Charge) { + this->actor.flags |= ACTOR_FLAG_24; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->actionFunc != EnFloormas_GrabLink) { + if (this->actionFunc != EnFloormas_Split && this->actionFunc != EnFloormas_TakeDamage && + this->actor.freezeTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if ((this->actionFunc != EnFloormas_SmSlaveJumpAtMaster) || (this->skelAnime.curFrame < 20.0f)) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + Actor_SetFocus(&this->actor, this->actor.scale.x * 2500.0f); + + if (this->collider.base.colType == COLTYPE_HARD) { + if (this->actionTarget != 0) { + this->actionTarget--; + } + + if (this->actionTarget == 0) { + this->actionTarget = 0x28; + } + } + } +} + +s32 EnFloormas_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx, Gfx** gfx) { + EnFloormas* this = (EnFloormas*)thisx; + + if (limbIndex == 1) { + pos->z += this->zOffset; + } + return false; +} + +void EnFloormas_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + if (limbIndex == 2) { + Matrix_Push(); + Matrix_Translate(1600.0f, -700.0f, -1700.0f, MTXMODE_APPLY); + Matrix_RotateY(DEGTORAD(60.0f), MTXMODE_APPLY); + Matrix_RotateZ(DEGTORAD(15.0f), MTXMODE_APPLY); + Matrix_Scale(2.0f, 2.0f, 2.0f, MTXMODE_APPLY); + gSPMatrix((*gfx)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_floormas.c", 2299), G_MTX_LOAD); + gSPDisplayList((*gfx)++, gWallmasterFingerDL); + Matrix_Pop(); + } +} + +static Color_RGBA8 sMergeColor = { 0, 255, 0, 0 }; + +void EnFloormas_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnFloormas* this = (EnFloormas*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_floormas.c", 2318); + + func_80093D18(globalCtx->state.gfxCtx); + if (this->collider.base.colType == COLTYPE_HARD) { + func_80026230(globalCtx, &sMergeColor, this->actionTarget % 0x28, 0x28); + } + + POLY_OPA_DISP = + SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnFloormas_OverrideLimbDraw, EnFloormas_PostLimbDraw, this, POLY_OPA_DISP); + if (this->collider.base.colType == COLTYPE_HARD) { + func_80026608(globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_floormas.c", 2340); +} + +void EnFloormas_DrawHighlighted(Actor* thisx, GlobalContext* globalCtx) { + EnFloormas* this = (EnFloormas*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_floormas.c", 2352); + + func_80093D84(globalCtx->state.gfxCtx); + if (this->collider.base.colType == COLTYPE_HARD) { + func_80026690(globalCtx, &sMergeColor, this->actionTarget % 0x28, 0x28); + } + POLY_XLU_DISP = + SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnFloormas_OverrideLimbDraw, EnFloormas_PostLimbDraw, this, POLY_XLU_DISP); + if (this->collider.base.colType == COLTYPE_HARD) { + func_80026A6C(globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_floormas.c", 2374); +} diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.h b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.h new file mode 100644 index 000000000..52f98fc9f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_FLOORMAS_H +#define Z_EN_FLOORMAS_H + +#include "ultra64.h" +#include "global.h" + +typedef struct EnFloormas EnFloormas; + +typedef void (*EnFloormasActionFunc)(EnFloormas* this, GlobalContext* globalCtx); + +struct EnFloormas{ + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnFloormasActionFunc actionFunc; + /* 0x0194 */ s16 actionTimer; + /* 0x0196 */ s16 actionTarget; + /* 0x0198 */ s16 zOffset; + /* 0x019A */ s16 smActionTimer; + /* 0x019C */ Vec3s jointTable[25]; + /* 0x0232 */ Vec3s morphTable[25]; + /* 0x02C8 */ ColliderCylinder collider; +}; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c b/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c new file mode 100644 index 000000000..38c86eda3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.c @@ -0,0 +1,1100 @@ +#include "z_en_fr.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "vt.h" +#include "objects/object_fr/object_fr.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnFr_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFr_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFr_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFr_UpdateIdle(Actor* thisx, GlobalContext* globalCtx); +void EnFr_UpdateActive(Actor* thisx, GlobalContext* globalCtx); +void EnFr_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnFr_Reset(void); + +// Animation Functions +void EnFr_SetupJumpingOutOfWater(EnFr* this, GlobalContext* globalCtx); +void EnFr_JumpingOutOfWater(EnFr* this, GlobalContext* globalCtx); +void EnFr_OrientOnLogSpot(EnFr* this, GlobalContext* globalCtx); +void EnFr_ChooseJumpFromLogSpot(EnFr* this, GlobalContext* globalCtx); +void EnFr_JumpingUp(EnFr* this, GlobalContext* globalCtx); +void EnFr_JumpingBackIntoWater(EnFr* this, GlobalContext* globalCtx); +void EnFr_DecrementBlinkTimerUpdate(EnFr* this); + +// Activation +void EnFr_Idle(EnFr* this, GlobalContext* globalCtx); +void EnFr_Activate(EnFr* this, GlobalContext* globalCtx); +void EnFr_ActivateCheckFrogSong(EnFr* this, GlobalContext* globalCtx); + +// Listening for Child Songs +void func_80A1BE98(EnFr* this, GlobalContext* globalCtx); +void EnFr_ListeningToOcarinaNotes(EnFr* this, GlobalContext* globalCtx); +void EnFr_ChildSong(EnFr* this, GlobalContext* globalCtx); +void EnFr_ChildSongFirstTime(EnFr* this, GlobalContext* globalCtx); + +// Frog Song for HP Functions +void EnFr_TalkBeforeFrogSong(EnFr* this, GlobalContext* globalCtx); +void EnFr_SetupFrogSong(EnFr* this, GlobalContext* globalCtx); +void EnFr_ContinueFrogSong(EnFr* this, GlobalContext* globalCtx); +void EnFr_OcarinaMistake(EnFr* this, GlobalContext* globalCtx); + +// Reward Functions +void EnFr_SetupReward(EnFr* this, GlobalContext* globalCtx, u8 unkCondition); +void EnFr_PrintTextBox(EnFr* this, GlobalContext* globalCtx); +void EnFr_TalkBeforeReward(EnFr* this, GlobalContext* globalCtx); +void EnFr_SetReward(EnFr* this, GlobalContext* globalCtx); + +// Deactivate +void EnFr_Deactivate(EnFr* this, GlobalContext* globalCtx); +void EnFr_GiveReward(EnFr* this, GlobalContext* globalCtx); +void EnFr_SetIdle(EnFr* this, GlobalContext* globalCtx); + +/* +Frogs params WIP docs + +Represents 6 Actor Instances for frogs: + - 1 Prop actor instance set to where Link plays Ocarina, manages 5 frogs + - 5 NPC actor instances for the frogs themselves + +0: Prop Actor Instance (located where link detects ocarina, interacts with Link) +1: Frog 0 (Yellow) +2: Frog 1 (Blue) +3: Frog 2 (Red) +4: Frog 3 (Purple) +5: Frog 4 (White) + +Note that because of the Prop Actor, actor.params is 1 shifted from frogIndex +Therefore, frogIndex = actor.params - 1 + + +sEnFrPointers.flags = 1 + - Activate frogs, frogs will jump out of the water + +sEnFrPointers.flags = 1 to 11: + - Counter: Frogs will sequentially jump out based on counter: + - 1: Frog 1 (Blue) + - 3: frog 3 (Purple) + - 5: frog 0 (Yellow) + - 7: frog 2 (Red) + - 9: frog 4 (White) + - Will proceed when counter reachers 11 + +sEnFrPointers.flags = 12 + - Deactivate frogs, frogs will jump back into the water +*/ + +typedef struct { + u8 flags; + EnFr* frogs[5]; +} EnFrPointers; + +typedef struct { + f32 xzDist; + f32 yaw; + f32 yDist; +} LogSpotToFromWater; + +static EnFrPointers sEnFrPointers = { + 0x00, + { + NULL, + NULL, + NULL, + NULL, + NULL, + }, +}; + +// Flags for gSaveContext.eventChkInf[13] +static u16 sSongIndex[] = { + 0x0002, 0x0004, 0x0010, 0x0008, 0x0020, 0x0040, 0x0001, 0x0000, +}; + +// Frog to Index for Song Flag (sSongIndex) Mapping +static u8 sFrogToSongIndex[] = { + FROG_SARIA, FROG_SUNS, FROG_SOT, FROG_ZL, FROG_EPONA, +}; + +// Song to Frog Index Mapping +static s32 sSongToFrog[] = { + FROG_PURPLE, FROG_WHITE, FROG_YELLOW, FROG_BLUE, FROG_RED, +}; + +const ActorInit En_Fr_InitVars = { + ACTOR_EN_FR, + ACTORCAT_NPC, + FLAGS, + OBJECT_FR, + sizeof(EnFr), + (ActorFunc)EnFr_Init, + (ActorFunc)EnFr_Destroy, + (ActorFunc)EnFr_Update, + NULL, + (ActorResetFunc)EnFr_Reset, +}; + +static Color_RGBA8 sEnFrColor[] = { + { 200, 170, 0, 255 }, { 0, 170, 200, 255 }, { 210, 120, 100, 255 }, { 120, 130, 230, 255 }, { 190, 190, 190, 255 }, +}; + +// Jumping back into water frog animation +// sLogSpotToFromWater[frog].xzDist is magnitude of xz distance frog travels +// sLogSpotToFromWater[frog].yaw is rot around y-axis of jumping back into water +// sLogSpotToFromWater[frog].yDist is change in y distance frog travels +static LogSpotToFromWater sLogSpotToFromWater[] = { + { 0.0f, 0.0f, 0.0f }, // Prop (Where link pulls ocarina) + { 80.0f, -0.375f * M_PI, -80.0f }, // FROG_YELLOW + { 80.0f, -0.5f * M_PI, -80.0f }, // FROG_BLUE + { 80.0f, -0.25f * M_PI, -80.0f }, // FROG_RED + { 80.0f, 0.875f * M_PI, -80.0f }, // FROG_PURPLE + { 80.0f, 0.5f * M_PI, -80.0f }, // FROG_WHITE +}; + +// Timer values for the frog choir song +static s16 sTimerFrogSong[] = { + 40, 20, 15, 12, 12, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +// Counter to Coordinate Frog jumping out of water one at a time +// Used as part of sEnFrPointers.flags +static u8 sTimerJumpingOutOfWater[] = { + 5, 1, 7, 3, 9, +}; + +// targetScale (default = 150.0) Actor scale target for Math_ApproachF +// Used as a frog grows from hearing a new child song +static f32 sGrowingScale[] = { + 180.0f, + 210.0f, + 240.0f, + 270.0f, +}; + +static u8 sSmallFrogNotes[] = { + 5, // C-Down Ocarina + 2, // A Button Ocarina + 9, // C-Right Ocarina + 11, // C-Left Ocarina + 14, // C Up Ocarina +}; + +static s8 sLargeFrogNotes[] = { + -7, // C-Down Ocarina + -10, // A Button Ocarina + -3, // C-Right Ocarina + -1, // C-Left Ocarina + 2, // C Up Ocarina +}; + +static u8 sJumpOrder[] = { + FROG_BLUE, FROG_YELLOW, FROG_RED, FROG_PURPLE, FROG_WHITE, FROG_BLUE, FROG_YELLOW, FROG_RED, +}; + +static u8 sOcarinaNotes[] = { + OCARINA_NOTE_A, OCARINA_NOTE_C_DOWN, OCARINA_NOTE_C_RIGHT, OCARINA_NOTE_C_LEFT, OCARINA_NOTE_C_UP, +}; + +void EnFr_OrientUnderwater(EnFr* this) { + Vec3f vec1; + Vec3f vec2; + + vec1.x = vec1.y = 0.0f; + vec1.z = this->xzDistToLogSpot = sLogSpotToFromWater[this->actor.params].xzDist; + Matrix_RotateY(sLogSpotToFromWater[this->actor.params].yaw, MTXMODE_NEW); + Matrix_MultVec3f(&vec1, &vec2); + this->actor.world.pos.x = this->posLogSpot.x + vec2.x; + this->actor.world.pos.z = this->posLogSpot.z + vec2.z; + this->actor.world.pos.y = sLogSpotToFromWater[this->actor.params].yDist + this->posLogSpot.y; + this->actor.world.rot.y = this->actor.shape.rot.y = + (s16)(sLogSpotToFromWater[this->actor.params].yaw * ((f32)0x8000 / M_PI)) + 0x8000; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; +} + +void EnFr_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFr* this = (EnFr*)thisx; + + if (this->actor.params == 0) { + this->actor.destroy = NULL; + this->actor.draw = NULL; + this->actor.update = EnFr_UpdateIdle; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_4); + this->actor.flags &= ~0; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + this->actor.textId = 0x40AC; + this->actionFunc = EnFr_Idle; + } else { + if ((this->actor.params >= 6) || (this->actor.params < 0)) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "The argument is wrong!!" + osSyncPrintf("%s[%d] : 引数が間違っている!!(%d)\n", "../z_en_fr.c", 370, this->actor.params); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_fr.c", 372); + } + + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GAMEPLAY_FIELD_KEEP); + if (this->objBankIndex < 0) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_COL(RED, WHITE)); + // "There is no bank!!" + osSyncPrintf("%s[%d] : バンクが無いよ!!\n", "../z_en_fr.c", 380); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_fr.c", 382); + } + } +} + +// Draw only the purple frog when ocarina is not pulled out on the log spot +void EnFr_DrawIdle(EnFr* this) { + this->actor.draw = (this->actor.params - 1) != FROG_PURPLE ? NULL : EnFr_Draw; +} + +void EnFr_DrawActive(EnFr* this) { + this->actor.draw = EnFr_Draw; +} + +void EnFr_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFr* this = (EnFr*)thisx; + s32 pad; + s32 frogIndex; + s32 pad2; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->actor.flags &= ~ACTOR_FLAG_4; + frogIndex = this->actor.params - 1; + sEnFrPointers.frogs[frogIndex] = this; + Actor_ProcessInitChain(&this->actor, sInitChain); + // frog + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_fr_Skel_00B498, &object_fr_Anim_001534, + this->jointTable, this->morphTable, 24); + // butterfly + SkelAnime_Init(globalCtx, &this->skelAnimeButterfly, &gButterflySkel, &gButterflyAnim, + this->jointTableButterfly, this->morphTableButterfly, 8); + // When playing the song for the HP, the frog with the next note and the butterfly turns on its lightsource + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, + this->actor.home.pos.z, 255, 255, 255, -1); + // Check to see if the song for a particular frog has been played. + // If it has, the frog is larger. If not, the frog is smaller + this->scale = gSaveContext.eventChkInf[13] & sSongIndex[sFrogToSongIndex[frogIndex]] ? 270.0f : 150.0f; + // When the frogs are not active (link doesn't have his ocarina out), + // Then shrink the frogs down by a factor of 10,000 + Actor_SetScale(&this->actor, this->scale * 0.0001f); + this->actor.minVelocityY = -9999.0f; + Actor_SetFocus(&this->actor, 10.0f); + this->eyeTexIndex = 1; + this->blinkTimer = (s16)(Rand_ZeroFloat(60.0f) + 20.0f); + this->blinkFunc = EnFr_DecrementBlinkTimerUpdate; + this->isBelowWaterSurfacePrevious = this->isBelowWaterSurfaceCurrent = false; + this->isJumpingUp = false; + this->posLogSpot = this->actor.world.pos; + this->actionFunc = EnFr_SetupJumpingOutOfWater; + this->isDeactivating = false; + this->growingScaleIndex = 0; + this->isActive = false; + this->isJumpingToFrogSong = false; + this->songIndex = FROG_NO_SONG; + this->unusedButterflyActor = NULL; + EnFr_OrientUnderwater(this); + EnFr_DrawIdle(this); + this->actor.update = EnFr_UpdateActive; + this->isButterflyDrawn = false; + this->xyAngleButterfly = 0x1000 * (s16)Rand_ZeroFloat(255.0f); + this->posButterflyLight.x = this->posButterfly.x = this->posLogSpot.x; + this->posButterflyLight.y = this->posButterfly.y = this->posLogSpot.y + 50.0f; + this->posButterflyLight.z = this->posButterfly.z = this->posLogSpot.z; + this->actor.flags &= ~ACTOR_FLAG_0; + } +} + +void EnFr_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFr* this = (EnFr*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); +} + +void EnFr_IsDivingIntoWater(EnFr* this, GlobalContext* globalCtx) { + WaterBox* waterBox; + f32 waterSurface; + + if (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, + &waterSurface, &waterBox)) { + this->isBelowWaterSurfacePrevious = this->isBelowWaterSurfaceCurrent; + this->isBelowWaterSurfaceCurrent = this->actor.world.pos.y <= waterSurface ? true : false; + } +} + +void EnFr_DivingIntoWater(EnFr* this, GlobalContext* globalCtx) { + Vec3f vec; + + // Jumping into or out of water + if (this->isBelowWaterSurfaceCurrent != this->isBelowWaterSurfacePrevious) { + vec.x = this->actor.world.pos.x; + vec.y = this->actor.world.pos.y - 10.0f; + vec.z = this->actor.world.pos.z; + EffectSsGSplash_Spawn(globalCtx, &vec, NULL, NULL, 1, 1); + + if (this->isBelowWaterSurfaceCurrent == false) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_DIVE_INTO_WATER_L); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BOMB_DROP_WATER); + } + } +} + +s32 EnFr_IsBelowLogSpot(EnFr* this, f32* yDistToLogSpot) { + yDistToLogSpot[0] = this->actor.world.pos.y - this->posLogSpot.y; + if ((this->actor.velocity.y < 0.0f) && (yDistToLogSpot[0] <= 0.0f)) { + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = this->posLogSpot.y; + return true; + } else { + return false; + } +} + +s32 EnFr_IsAboveAndWithin30DistXZ(Player* player, EnFr* this) { + f32 xDistToPlayer = player->actor.world.pos.x - this->actor.world.pos.x; + f32 zDistToPlayer = player->actor.world.pos.z - this->actor.world.pos.z; + f32 yDistToPlayer = player->actor.world.pos.y - this->actor.world.pos.y; + + return ((SQ(xDistToPlayer) + SQ(zDistToPlayer)) <= SQ(30.0f)) && (yDistToPlayer >= 0.0f); +} + +void EnFr_DecrementBlinkTimer(EnFr* this) { + if (this->blinkTimer != 0) { + this->blinkTimer--; + } else { + this->blinkFunc = EnFr_DecrementBlinkTimerUpdate; + } +} + +void EnFr_DecrementBlinkTimerUpdate(EnFr* this) { + if (this->blinkTimer != 0) { + this->blinkTimer--; + } else if (this->eyeTexIndex) { + this->eyeTexIndex = 0; + this->blinkTimer = (s16)(Rand_ZeroFloat(60.0f) + 20.0f); + this->blinkFunc = EnFr_DecrementBlinkTimer; + } else { + this->eyeTexIndex = 1; + this->blinkTimer = 1; + } +} + +void EnFr_SetupJumpingOutOfWater(EnFr* this, GlobalContext* globalCtx) { + if (sEnFrPointers.flags == sTimerJumpingOutOfWater[this->actor.params - 1]) { + Animation_Change(&this->skelAnime, &object_fr_Anim_0007BC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_fr_Anim_0007BC), ANIMMODE_ONCE, 0.0f); + EnFr_DrawActive(this); + this->actionFunc = EnFr_JumpingOutOfWater; + } +} + +void EnFr_JumpingOutOfWater(EnFr* this, GlobalContext* globalCtx) { + Vec3f vec1; + Vec3f vec2; + + if (this->skelAnime.curFrame == 6.0f) { + sEnFrPointers.flags++; + this->skelAnime.playSpeed = 0.0f; + } else if (this->skelAnime.curFrame == 3.0f) { + this->actor.gravity = -10.0f; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 47.0f; + } + + vec1.x = vec1.y = 0.0f; + vec1.z = this->xzDistToLogSpot; + Matrix_RotateY(((this->actor.world.rot.y + 0x8000) / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&vec1, &vec2); + this->actor.world.pos.x = this->posLogSpot.x + vec2.x; + this->actor.world.pos.z = this->posLogSpot.z + vec2.z; + if (this->skelAnime.curFrame >= 3.0f) { + Math_ApproachF(&this->xzDistToLogSpot, 0.0f, 1.0f, 10.0f); + } + + if (EnFr_IsBelowLogSpot(this, &vec2.y)) { + this->actor.gravity = 0.0f; + this->actionFunc = EnFr_OrientOnLogSpot; + this->unusedFloat = 0.0f; + } + + if ((this->actor.velocity.y <= 0.0f) && (vec2.y < 40.0f)) { + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnFr_OrientOnLogSpot(EnFr* this, GlobalContext* globalCtx) { + s16 rotYRemaining = Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 10000, 100); + + this->actor.world.rot.y = this->actor.shape.rot.y; + + if ((rotYRemaining == 0) && (this->skelAnime.curFrame == this->skelAnime.endFrame)) { + sEnFrPointers.flags++; + this->actionFunc = EnFr_ChooseJumpFromLogSpot; + Animation_Change(&this->skelAnime, &object_fr_Anim_001534, 1.0f, 0.0f, + Animation_GetLastFrame(&object_fr_Anim_001534), ANIMMODE_LOOP, 0.0f); + } +} + +void EnFr_ChooseJumpFromLogSpot(EnFr* this, GlobalContext* globalCtx) { + if (sEnFrPointers.flags == 12) { + this->actor.world.rot.y = ((f32)0x8000 / M_PI) * sLogSpotToFromWater[this->actor.params].yaw; + Animation_Change(&this->skelAnime, &object_fr_Anim_0007BC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_fr_Anim_0007BC), ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnFr_JumpingBackIntoWater; + } else if (this->isJumpingUp) { + Animation_Change(&this->skelAnime, &object_fr_Anim_0007BC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_fr_Anim_0007BC), ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnFr_JumpingUp; + } +} + +void EnFr_JumpingUp(EnFr* this, GlobalContext* globalCtx) { + f32 yDistToLogSpot; + + if (this->skelAnime.curFrame == 6.0f) { + this->skelAnime.playSpeed = 0.0f; + } else if (this->skelAnime.curFrame == 3.0f) { + this->actor.gravity = -10.0f; + this->actor.velocity.y = 25.0f; + if (this->isJumpingToFrogSong) { + this->isJumpingToFrogSong = false; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_EAT); + } + } + + if (EnFr_IsBelowLogSpot(this, &yDistToLogSpot)) { + this->isJumpingUp = false; + this->actor.gravity = 0.0f; + Animation_Change(&this->skelAnime, &object_fr_Anim_0011C0, 1.0f, 0.0f, + Animation_GetLastFrame(&object_fr_Anim_0011C0), ANIMMODE_LOOP, 0.0f); + this->actionFunc = EnFr_ChooseJumpFromLogSpot; + } else if ((this->actor.velocity.y <= 0.0f) && (yDistToLogSpot < 40.0f)) { + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnFr_JumpingBackIntoWater(EnFr* this, GlobalContext* globalCtx) { + f32 yUnderwater = sLogSpotToFromWater[this->actor.params].yDist + this->posLogSpot.y; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 2, 10000, 100); + if (this->skelAnime.curFrame == 6.0f) { + this->skelAnime.playSpeed = 0.0f; + } else if (this->skelAnime.curFrame == 3.0f) { + this->actor.speedXZ = 6.0f; + this->actor.gravity = -10.0f; + this->actor.velocity.y = 25.0f; + } + + // Final Spot Reached + if ((this->actor.velocity.y < 0.0f) && (this->actor.world.pos.y < yUnderwater)) { + Animation_Change(&this->skelAnime, &object_fr_Anim_001534, 1.0f, 0.0f, + Animation_GetLastFrame(&object_fr_Anim_001534), ANIMMODE_LOOP, 0.0f); + this->actionFunc = EnFr_SetupJumpingOutOfWater; + EnFr_DrawIdle(this); + this->isDeactivating = true; + EnFr_OrientUnderwater(this); + } +} + +void EnFr_SetScaleActive(EnFr* this, GlobalContext* globalCtx) { + switch (this->isGrowing) { + case false: + Math_ApproachF(&this->scale, sGrowingScale[this->growingScaleIndex], 2.0f, 25.0f); + if (this->scale >= sGrowingScale[this->growingScaleIndex]) { + this->scale = sGrowingScale[this->growingScaleIndex]; + if (this->growingScaleIndex < 3) { + this->isGrowing = true; + } else { + this->isJumpingUp = false; + this->isActive = false; + } + } + break; + case true: + Math_ApproachF(&this->scale, 150.0f, 2.0f, 25.0f); + if (this->scale <= 150.0f) { + this->scale = 150.0f; + this->growingScaleIndex++; + if (this->growingScaleIndex >= 4) { + this->growingScaleIndex = 3; + } + this->isGrowing = false; + } + break; + } +} + +void EnFr_ButterflyPath(EnFr* this, GlobalContext* globalCtx) { + s16 rotY = this->actor.shape.rot.y; + f32 sin; + Vec3f vec1; + Vec3f vec2; + + this->xyAngleButterfly += 0x1000; + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, MTXMODE_APPLY); + vec1.x = vec1.y = 0.0f; + vec1.z = 25.0f; + Matrix_MultVec3f(&vec1, &vec2); + sin = Math_SinS(this->xyAngleButterfly * 2) * 5.0f; + this->posButterfly.x = (Math_SinS(rotY) * sin) + vec2.x; + this->posButterfly.y = (2.0f * Math_CosS(this->xyAngleButterfly)) + (this->posLogSpot.y + 50.0f); + this->posButterfly.z = (Math_CosS(rotY) * sin) + vec2.z; + Matrix_Translate(this->posButterfly.x, this->posButterfly.y, this->posButterfly.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, MTXMODE_APPLY); + vec1.x = 0.0f; + vec1.y = -15.0f; + vec1.z = 20.0f; + Matrix_MultVec3f(&vec1, &this->posButterflyLight); +} + +void EnFr_UpdateActive(Actor* thisx, GlobalContext* globalCtx) { + EnFr* this = (EnFr*)thisx; + + this->jumpCounter++; + Actor_SetScale(&this->actor, this->scale * 0.0001f); + + if (this->isActive) { + EnFr_SetScaleActive(this, globalCtx); + } else { + Actor_SetFocus(&this->actor, 10.0f); + this->blinkFunc(this); + this->actionFunc(this, globalCtx); + EnFr_IsDivingIntoWater(this, globalCtx); + EnFr_DivingIntoWater(this, globalCtx); + SkelAnime_Update(&this->skelAnime); + SkelAnime_Update(&this->skelAnimeButterfly); + EnFr_ButterflyPath(this, globalCtx); + Actor_MoveForward(&this->actor); + } +} + +s32 EnFr_SetupJumpingUp(EnFr* this, s32 frogIndex) { + EnFr* frog = sEnFrPointers.frogs[frogIndex]; + u8 semitone; + + if (frog != NULL && frog->isJumpingUp == false) { + semitone = frog->growingScaleIndex == 3 ? sLargeFrogNotes[frogIndex] : sSmallFrogNotes[frogIndex]; + if (this->songIndex == FROG_CHOIR_SONG) { + frog->isJumpingToFrogSong = true; + } + frog->isJumpingUp = true; + Audio_PlaySoundTransposed(&frog->actor.projectedPos, NA_SE_EV_FROG_JUMP, semitone); + return true; + } else { + return false; + } +} + +void EnFr_Idle(EnFr* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags2 & 0x2000000) { + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + } + + OnePointCutscene_Init(globalCtx, 4110, ~0x62, &this->actor, MAIN_CAM); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + player->actor.world.pos.x = this->actor.world.pos.x; // x = 990.0f + player->actor.world.pos.y = this->actor.world.pos.y; // y = 205.0f + player->actor.world.pos.z = this->actor.world.pos.z; // z = -1220.0f + player->currentYaw = player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y; + this->reward = GI_NONE; + this->actionFunc = EnFr_Activate; + } else if (EnFr_IsAboveAndWithin30DistXZ(player, this)) { + player->unk_6A8 = &this->actor; + } +} + +void EnFr_Activate(EnFr* this, GlobalContext* globalCtx) { + if (globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_PLAYING) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + sEnFrPointers.flags = 1; + this->actionFunc = EnFr_ActivateCheckFrogSong; + } else if (globalCtx->msgCtx.msgMode == MSGMODE_PAUSED) { // Goes to Frogs 2 Song + sEnFrPointers.flags = 1; + this->actionFunc = EnFr_ActivateCheckFrogSong; + } +} + +void EnFr_ActivateCheckFrogSong(EnFr* this, GlobalContext* globalCtx) { + if (sEnFrPointers.flags == 11) { + // Check if all 6 child songs have been played for the frogs + if ((gSaveContext.eventChkInf[13] & 0x2) // ZL + && (gSaveContext.eventChkInf[13] & 0x4) // Epona + && (gSaveContext.eventChkInf[13] & 0x10) // Saria + && (gSaveContext.eventChkInf[13] & 0x8) // Suns + && (gSaveContext.eventChkInf[13] & 0x20) // SoT + && (gSaveContext.eventChkInf[13] & 0x40)) { // SoS + this->actionFunc = EnFr_TalkBeforeFrogSong; + this->songIndex = FROG_CHOIR_SONG; + Message_StartTextbox(globalCtx, 0x40AB, &this->actor); + } else { + this->songIndex = FROG_ZL; + this->actionFunc = func_80A1BE98; + } + } +} + +void func_80A1BE98(EnFr* this, GlobalContext* globalCtx) { + EnFr* frog; + s32 frogIndex; + + for (frogIndex = 0; frogIndex < ARRAY_COUNT(sEnFrPointers.frogs); frogIndex++) { + frog = sEnFrPointers.frogs[frogIndex]; + if (frog != NULL && frog->actionFunc == EnFr_ChooseJumpFromLogSpot) { + continue; + } else { + return; + } + } + + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_NOWARP); + this->actionFunc = EnFr_ListeningToOcarinaNotes; +} + +void EnFr_ListeningToOcarinaNotes(EnFr* this, GlobalContext* globalCtx) { + this->songIndex = FROG_NO_SONG; + switch (globalCtx->msgCtx.ocarinaMode) { // Ocarina Song played + case OCARINA_MODE_07: + this->songIndex = FROG_ZL; + break; + case OCARINA_MODE_06: + this->songIndex = FROG_EPONA; + break; + case OCARINA_MODE_05: + this->songIndex = FROG_SARIA; + break; + case OCARINA_MODE_08: + this->songIndex = FROG_SUNS; + break; + case OCARINA_MODE_09: + this->songIndex = FROG_SOT; + break; + case OCARINA_MODE_0A: + this->songIndex = FROG_STORMS; + break; + case OCARINA_MODE_04: + EnFr_OcarinaMistake(this, globalCtx); + break; + case OCARINA_MODE_01: // Ocarina note played, but no song played + switch (globalCtx->msgCtx.lastOcaNoteIdx) { // Jumping frogs in open ocarina based on ocarina note played + case OCARINA_NOTE_A: + EnFr_SetupJumpingUp(this, FROG_BLUE); + break; + case OCARINA_NOTE_C_DOWN: + EnFr_SetupJumpingUp(this, FROG_YELLOW); + break; + case OCARINA_NOTE_C_RIGHT: + EnFr_SetupJumpingUp(this, FROG_RED); + break; + case OCARINA_NOTE_C_LEFT: + EnFr_SetupJumpingUp(this, FROG_PURPLE); + break; + case OCARINA_NOTE_C_UP: + EnFr_SetupJumpingUp(this, FROG_WHITE); + break; + } + } + if (this->songIndex != FROG_NO_SONG) { + this->jumpCounter = 0; + this->actionFunc = EnFr_ChildSong; + } +} + +void EnFr_ChildSong(EnFr* this, GlobalContext* globalCtx) { + EnFr* frog; + u8 songIndex; + + if (this->jumpCounter < 48) { + if (this->jumpCounter % 4 == 0) { + EnFr_SetupJumpingUp(this, sJumpOrder[(this->jumpCounter >> 2) & 7]); + } + } else { + songIndex = this->songIndex; + if (songIndex == FROG_STORMS) { + this->actor.textId = 0x40AA; + EnFr_SetupReward(this, globalCtx, false); + } else if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { + frog = sEnFrPointers.frogs[sSongToFrog[songIndex]]; + func_80078884(NA_SE_SY_CORRECT_CHIME); + if (frog->actionFunc == EnFr_ChooseJumpFromLogSpot) { + frog->isJumpingUp = true; + frog->isActive = true; + Audio_PlayActorSound2(&frog->actor, NA_SE_EV_FROG_GROW_UP); + this->actionFunc = EnFr_ChildSongFirstTime; + } else { + this->jumpCounter = 48; + } + } else { + this->actor.textId = 0x40A9; + EnFr_SetupReward(this, globalCtx, true); + } + } +} + +void EnFr_ChildSongFirstTime(EnFr* this, GlobalContext* globalCtx) { + EnFr* frog = sEnFrPointers.frogs[sSongToFrog[this->songIndex]]; + + if (frog->isActive == false) { + this->actor.textId = 0x40A9; + EnFr_SetupReward(this, globalCtx, true); + } +} + +void EnFr_TalkBeforeFrogSong(EnFr* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->frogSongTimer = 2; + this->actionFunc = EnFr_SetupFrogSong; + } +} + +void EnFr_CheckOcarinaInputFrogSong(u8 ocarinaNote) { + EnFr* frog; + s32 frogIndexButterfly; + s32 frogIndex; + + switch (ocarinaNote) { + case 0: + frogIndexButterfly = FROG_BLUE; + break; + case 1: + frogIndexButterfly = FROG_YELLOW; + break; + case 2: + frogIndexButterfly = FROG_RED; + break; + case 3: + frogIndexButterfly = FROG_PURPLE; + break; + case 4: + frogIndexButterfly = FROG_WHITE; + } + // Turn on or off butterfly above frog + for (frogIndex = 0; frogIndex < ARRAY_COUNT(sEnFrPointers.frogs); frogIndex++) { + frog = sEnFrPointers.frogs[frogIndex]; + frog->isButterflyDrawn = frogIndex == frogIndexButterfly ? true : false; + } +} + +void EnFr_DeactivateButterfly() { + s32 frogIndex; + EnFr* frog; + + for (frogIndex = 0; frogIndex < ARRAY_COUNT(sEnFrPointers.frogs); frogIndex++) { + frog = sEnFrPointers.frogs[frogIndex]; + frog->isButterflyDrawn = false; + } +} + +u8 EnFr_GetNextNoteFrogSong(u8 ocarinaNoteIndex) { + if (!(gSaveContext.eventChkInf[13] & 1)) { + return gFrogsSongPtr[ocarinaNoteIndex]; + } else { + return sOcarinaNotes[(s32)Rand_ZeroFloat(60.0f) % 5]; + } +} + +void EnFr_SetupFrogSong(EnFr* this, GlobalContext* globalCtx) { + if (this->frogSongTimer != 0) { + this->frogSongTimer--; + } else { + this->frogSongTimer = 40; + this->ocarinaNoteIndex = 0; + func_8010BD58(globalCtx, OCARINA_ACTION_FROGS); + this->ocarinaNote = EnFr_GetNextNoteFrogSong(this->ocarinaNoteIndex); + EnFr_CheckOcarinaInputFrogSong(this->ocarinaNote); + this->actionFunc = EnFr_ContinueFrogSong; + } +} + +s32 EnFr_IsFrogSongComplete(EnFr* this, GlobalContext* globalCtx) { + u8 index; + u8 ocarinaNote; + MessageContext* msgCtx = &globalCtx->msgCtx; + u8 ocarinaNoteIndex; + + if (this->ocarinaNote == (*msgCtx).lastOcaNoteIdx) { // required to match, possibly an array? + this->ocarinaNoteIndex++; + ocarinaNoteIndex = this->ocarinaNoteIndex; + if (1) {} + if (ocarinaNoteIndex >= 14) { // Frog Song is completed + this->ocarinaNoteIndex = 13; + return true; + } + // The first four notes have more frames to receive an input + index = ocarinaNoteIndex < 4 ? (s32)ocarinaNoteIndex : 4; + ocarinaNote = EnFr_GetNextNoteFrogSong(ocarinaNoteIndex); + this->ocarinaNote = ocarinaNote; + EnFr_CheckOcarinaInputFrogSong(ocarinaNote); + this->frogSongTimer = sTimerFrogSong[index]; + } + return false; +} + +void EnFr_OcarinaMistake(EnFr* this, GlobalContext* globalCtx) { + Message_CloseTextbox(globalCtx); + this->reward = GI_NONE; + func_80078884(NA_SE_SY_OCARINA_ERROR); + Audio_OcaSetInstrument(0); + sEnFrPointers.flags = 12; + EnFr_DeactivateButterfly(); + this->actionFunc = EnFr_Deactivate; +} + +void EnFr_ContinueFrogSong(EnFr* this, GlobalContext* globalCtx) { + s32 counter; + EnFr* frog; + s32 i; + + if (this->frogSongTimer == 0) { + EnFr_OcarinaMistake(this, globalCtx); + } else { + this->frogSongTimer--; + if (globalCtx->msgCtx.msgMode == MSGMODE_FROGS_PLAYING) { + counter = 0; + for (i = 0; i < ARRAY_COUNT(sEnFrPointers.frogs); i++) { + frog = sEnFrPointers.frogs[i]; + if (frog != NULL && frog->actionFunc == EnFr_ChooseJumpFromLogSpot) { + continue; + } else { + counter++; + } + } + if (counter == 0 && CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B)) { + EnFr_OcarinaMistake(this, globalCtx); + return; + } + } + + if (globalCtx->msgCtx.msgMode == MSGMODE_FROGS_WAITING) { + globalCtx->msgCtx.msgMode = MSGMODE_FROGS_START; + switch (globalCtx->msgCtx.lastOcaNoteIdx) { + case OCARINA_NOTE_A: + EnFr_SetupJumpingUp(this, FROG_BLUE); + break; + case OCARINA_NOTE_C_DOWN: + EnFr_SetupJumpingUp(this, FROG_YELLOW); + break; + case OCARINA_NOTE_C_RIGHT: + EnFr_SetupJumpingUp(this, FROG_RED); + break; + case OCARINA_NOTE_C_LEFT: + EnFr_SetupJumpingUp(this, FROG_PURPLE); + break; + case OCARINA_NOTE_C_UP: + EnFr_SetupJumpingUp(this, FROG_WHITE); + } + if (EnFr_IsFrogSongComplete(this, globalCtx)) { + this->actor.textId = 0x40AC; + EnFr_SetupReward(this, globalCtx, false); + } + } + } +} + +void EnFr_SetupReward(EnFr* this, GlobalContext* globalCtx, u8 unkCondition) { + EnFr_DeactivateButterfly(); + if (unkCondition) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } else { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + + Audio_OcaSetInstrument(0); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actionFunc = EnFr_PrintTextBox; +} + +void EnFr_PrintTextBox(EnFr* this, GlobalContext* globalCtx) { + Message_StartTextbox(globalCtx, this->actor.textId, &this->actor); + this->actionFunc = EnFr_TalkBeforeReward; +} + +void EnFr_TalkBeforeReward(EnFr* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->frogSongTimer = 100; + Message_CloseTextbox(globalCtx); + this->actionFunc = EnFr_SetReward; + } +} + +void EnFr_SetReward(EnFr* this, GlobalContext* globalCtx) { + u16 songIndex; + + sEnFrPointers.flags = 12; + songIndex = this->songIndex; + this->actionFunc = EnFr_Deactivate; + this->reward = GI_NONE; + if ((songIndex >= FROG_ZL) && (songIndex <= FROG_SOT)) { + if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { + gSaveContext.eventChkInf[13] |= sSongIndex[songIndex]; + this->reward = GI_RUPEE_PURPLE; + } else { + this->reward = GI_RUPEE_BLUE; + } + } else if (songIndex == FROG_STORMS) { + if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { + gSaveContext.eventChkInf[13] |= sSongIndex[songIndex]; + this->reward = GI_HEART_PIECE; + } else { + this->reward = GI_RUPEE_BLUE; + } + } else if (songIndex == FROG_CHOIR_SONG) { + if (!(gSaveContext.eventChkInf[13] & sSongIndex[songIndex])) { + gSaveContext.eventChkInf[13] |= sSongIndex[songIndex]; + this->reward = GI_HEART_PIECE; + } else { + this->reward = GI_RUPEE_PURPLE; + } + } +} + +void EnFr_Deactivate(EnFr* this, GlobalContext* globalCtx) { + EnFr* frogLoop1; + EnFr* frogLoop2; + s32 frogIndex; + + // Originally was going to have separate butterfly actor + // Changed to include butterfly as part of frog actor + // This unused code would have frozen the butterfly actor above frog + if (this->unusedButterflyActor != NULL) { + this->unusedButterflyActor->freezeTimer = 10; + } + + for (frogIndex = 0; frogIndex < ARRAY_COUNT(sEnFrPointers.frogs); frogIndex++) { + frogLoop1 = sEnFrPointers.frogs[frogIndex]; + if (frogLoop1 == NULL) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "There are no frogs!?" + osSyncPrintf("%s[%d]カエルがいない!?\n", "../z_en_fr.c", 1604); + osSyncPrintf(VT_RST); + return; + } else if (frogLoop1->isDeactivating != true) { + return; + } + } + + for (frogIndex = 0; frogIndex < ARRAY_COUNT(sEnFrPointers.frogs); frogIndex++) { + frogLoop2 = sEnFrPointers.frogs[frogIndex]; + if (frogLoop2 == NULL) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "There are no frogs!?" + osSyncPrintf("%s[%d]カエルがいない!?\n", "../z_en_fr.c", 1618); + osSyncPrintf(VT_RST); + return; + } + frogLoop2->isDeactivating = false; + } + + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FROG_CRY_0); + if (this->reward == GI_NONE) { + this->actionFunc = EnFr_Idle; + } else { + this->actionFunc = EnFr_GiveReward; + func_8002F434(&this->actor, globalCtx, this->reward, 30.0f, 100.0f); + } +} + +void EnFr_GiveReward(EnFr* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnFr_SetIdle; + } else { + func_8002F434(&this->actor, globalCtx, this->reward, 30.0f, 100.0f); + } +} + +void EnFr_SetIdle(EnFr* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = EnFr_Idle; + } +} + +void EnFr_UpdateIdle(Actor* thisx, GlobalContext* globalCtx) { + EnFr* this = (EnFr*)thisx; + + if (BREG(0)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } + this->jumpCounter++; + this->actionFunc(this, globalCtx); +} + +s32 EnFr_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + if ((limbIndex == 7) || (limbIndex == 8)) { + *dList = NULL; + } + return 0; +} + +void EnFr_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnFr* this = (EnFr*)thisx; + + if ((limbIndex == 7) || (limbIndex == 8)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fr.c", 1735); + Matrix_Push(); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fr.c", 1738), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fr.c", 1741); + } +} + +void EnFr_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + object_fr_Tex_0059A0, + object_fr_Tex_005BA0, + }; + s16 lightRadius; + EnFr* this = (EnFr*)thisx; + s16 frogIndex = this->actor.params - 1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fr.c", 1754); + func_80093D18(globalCtx->state.gfxCtx); + // For the frogs 2 HP, the frog with the next note and the butterfly lights up + lightRadius = this->isButterflyDrawn ? 95 : -1; + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->posButterflyLight.x, this->posButterflyLight.y, + this->posButterflyLight.z, 255, 255, 255, lightRadius); + gDPSetEnvColor(POLY_OPA_DISP++, sEnFrColor[frogIndex].r, sEnFrColor[frogIndex].g, sEnFrColor[frogIndex].b, 255); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnFr_OverrideLimbDraw, EnFr_PostLimbDraw, this); + if (this->isButterflyDrawn) { + Matrix_Translate(this->posButterfly.x, this->posButterfly.y, this->posButterfly.z, MTXMODE_NEW); + Matrix_Scale(0.015f, 0.015f, 0.015f, MTXMODE_APPLY); + Matrix_RotateZYX(this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, MTXMODE_APPLY); + SkelAnime_DrawOpa(globalCtx, this->skelAnimeButterfly.skeleton, this->skelAnimeButterfly.jointTable, NULL, NULL, + NULL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fr.c", 1816); +} + +void EnFr_Reset(void) { + sEnFrPointers.flags = 0; + sEnFrPointers.frogs[0] = NULL; + sEnFrPointers.frogs[1] = NULL; + sEnFrPointers.frogs[2] = NULL; + sEnFrPointers.frogs[3] = NULL; + sEnFrPointers.frogs[4] = NULL; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.h b/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.h new file mode 100644 index 000000000..ba2ef5014 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fr/z_en_fr.h @@ -0,0 +1,72 @@ +#ifndef Z_EN_FR_H +#define Z_EN_FR_H + +#include "ultra64.h" +#include "global.h" + +struct EnFr; + +typedef void (*EnFrActionFunc)(struct EnFr*, GlobalContext*); +typedef void (*EnFrBlinkFunc)(struct EnFr*); + +typedef enum { + /* 00 */ FROG_YELLOW, // Middle + /* 01 */ FROG_BLUE, // Front Left + /* 02 */ FROG_RED, // Front Right + /* 03 */ FROG_PURPLE, // Back Left + /* 04 */ FROG_WHITE // Back Right +} FrogType; + +typedef enum { + /* 00 */ FROG_ZL, + /* 01 */ FROG_EPONA, + /* 02 */ FROG_SARIA, + /* 03 */ FROG_SUNS, + /* 04 */ FROG_SOT, + /* 05 */ FROG_STORMS, + /* 06 */ FROG_CHOIR_SONG, + /* 07 */ FROG_NO_SONG +} FrogSongType; + +typedef struct EnFr { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; // Frog Skeleton + /* 0x0190 */ Vec3s jointTable[24]; + /* 0x0220 */ Vec3s morphTable[24]; + /* 0x02B0 */ SkelAnime skelAnimeButterfly; // Butterfly Skeleton above Frog during Frog Song + /* 0x02F4 */ Vec3s jointTableButterfly[8]; + /* 0x0324 */ Vec3s morphTableButterfly[8]; + /* 0x0354 */ EnFrActionFunc actionFunc; + /* 0x0358 */ LightNode* lightNode; // Frogs light up during the Frog Song, specifically the frog with the next note. + /* 0x035C */ LightInfo lightInfo; + /* 0x036A */ s8 objBankIndex; + /* 0x036C */ s32 jumpCounter; // cyclic clock used to time celebratory frog jumps every 4 frames when child song is played. + /* 0x0370 */ s32 blinkTimer; // blinking timer + /* 0x0374 */ EnFrBlinkFunc blinkFunc; // unique case of using a function pointer for blinking + /* 0x0378 */ u8 eyeTexIndex; // index for blinking graphics + /* 0x0379 */ u8 isJumpingUp; // Conditional for jumping up from the log back onto the log + /* 0x037A */ u8 isBelowWaterSurfaceCurrent; // Used for diving effects + /* 0x037B */ u8 isBelowWaterSurfacePrevious; + /* 0x037C */ u8 isDeactivating; // Related to debugging (osSyncPrintf) + /* 0x037D */ u8 isActive; // Each Frog grows when their specific song is played. + /* 0x037E */ u8 growingScaleIndex; // Target Scale Level 0-3 while growing (3 highest smooth) + /* 0x037F */ u8 isGrowing; // While growing, the frog will switch between its larger size and its original size every frame + /* 0x0380 */ u8 ocarinaNoteIndex; // Related to unk_381 + /* 0x0381 */ u8 ocarinaNote; // Ocarina Note to Play for Frogs 2 + /* 0x0382 */ u8 songIndex; // Song index 1-7 (6 songs + custom HP Song): 5 = sos, 6 = all songs + /* 0x0383 */ u8 isJumpingToFrogSong; // Conditional: + /* 0x0384 */ char unk_384[0x02]; + /* 0x0386 */ s16 frogSongTimer; // timer #2 for frogs 2 HP + /* 0x0388 */ s32 reward; // Prize for completing a song + /* 0x038C */ Vec3f posLogSpot; // Where the frog goes when activated + /* 0x0398 */ f32 xzDistToLogSpot; // z position or axis + /* 0x039C */ f32 unusedFloat; // Unused float, only set to zero. + /* 0x03A0 */ f32 scale; //scale + /* 0x03A4 */ Actor* unusedButterflyActor; // unused pointer to Actor. Likely intended for butterfly + /* 0x03A8 */ u8 isButterflyDrawn; // isButterflyDrawn + /* 0x03AA */ s16 xyAngleButterfly; // Butterfly Travels along random angles in the x-y plane + /* 0x03AC */ Vec3f posButterfly; // Position/Coordinates of the Butterfly + /* 0x03B8 */ Vec3f posButterflyLight; // Used in Lights_PointNoGlowSetInfo() +} EnFr; // size = 0x03C4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c b/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c new file mode 100644 index 000000000..5bf834c0c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.c @@ -0,0 +1,310 @@ +/* + * File: z_en_fu.c + * Overlay: ovl_En_Fu + * Description: Windmill Man + */ + +#include "z_en_fu.h" +#include "objects/object_fu/object_fu.h" +#include "scenes/indoors/hakasitarelay/hakasitarelay_scene.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_25) + +#define FU_RESET_LOOK_ANGLE (1 << 0) +#define FU_WAIT (1 << 1) + +void EnFu_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFu_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnFu_WaitChild(EnFu* this, GlobalContext* globalCtx); +void func_80A1DA04(EnFu* this, GlobalContext* globalCtx); + +void EnFu_WaitAdult(EnFu* this, GlobalContext* globalCtx); +void EnFu_TeachSong(EnFu* this, GlobalContext* globalCtx); +void EnFu_WaitForPlayback(EnFu* this, GlobalContext* globalCtx); +void func_80A1DBA0(EnFu* this, GlobalContext* globalCtx); +void func_80A1DBD4(EnFu* this, GlobalContext* globalCtx); +void func_80A1DB60(EnFu* this, GlobalContext* globalCtx); + +const ActorInit En_Fu_InitVars = { + ACTOR_EN_FU, + ACTORCAT_NPC, + FLAGS, + OBJECT_FU, + sizeof(EnFu), + (ActorFunc)EnFu_Init, + (ActorFunc)EnFu_Destroy, + (ActorFunc)EnFu_Update, + (ActorFunc)EnFu_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +static Vec3f sMtxSrc = { + 700.0f, + 700.0f, + 0.0f, +}; + +typedef enum { + /* 0x00 */ FU_FACE_CALM, + /* 0x01 */ FU_FACE_MAD +} EnFuFace; + +void EnFu_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnFu* this = (EnFu*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelanime, &gWindmillManSkel, &gWindmillManPlayStillAnim, this->jointTable, + this->morphTable, FU_LIMB_MAX); + Animation_PlayLoop(&this->skelanime, &gWindmillManPlayStillAnim); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + if (!LINK_IS_ADULT) { + this->actionFunc = EnFu_WaitChild; + this->facialExpression = FU_FACE_CALM; + } else { + this->actionFunc = EnFu_WaitAdult; + this->facialExpression = FU_FACE_MAD; + this->skelanime.playSpeed = 2.0f; + } + this->behaviorFlags = 0; + this->actor.targetMode = 6; +} + +void EnFu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFu* this = (EnFu*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80A1D94C(EnFu* this, GlobalContext* globalCtx, u16 textID, EnFuActionFunc actionFunc) { + s16 yawDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = actionFunc; + return true; + } + this->actor.textId = textID; + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(yawDiff) < 0x2301) && (this->actor.xzDistToPlayer < 100.0f)) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } else { + this->behaviorFlags |= FU_RESET_LOOK_ANGLE; + } + return false; +} + +void func_80A1DA04(EnFu* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->behaviorFlags &= ~FU_WAIT; + this->actionFunc = EnFu_WaitChild; + + if (this->skelanime.animation == &gWindmillManPlayAndMoveHeadAnim) { + Animation_Change(&this->skelanime, &gWindmillManPlayStillAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gWindmillManPlayStillAnim), ANIMMODE_ONCE, -4.0f); + } + } +} + +void EnFu_WaitChild(EnFu* this, GlobalContext* globalCtx) { + u16 textID = Text_GetFaceReaction(globalCtx, 0xB); + + if (textID == 0) { + textID = (gSaveContext.eventChkInf[6] & 0x80) ? 0x5033 : 0x5032; + } + + // if ACTOR_FLAG_8 is set and textID is 0x5033, change animation + // if func_80A1D94C returns 1, actionFunc is set to func_80A1DA04 + if (func_80A1D94C(this, globalCtx, textID, func_80A1DA04)) { + if (textID == 0x5033) { + Animation_Change(&this->skelanime, &gWindmillManPlayAndMoveHeadAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gWindmillManPlayAndMoveHeadAnim), ANIMMODE_ONCE, -4.0f); + } + } +} + +void func_80A1DB60(EnFu* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + this->actionFunc = EnFu_WaitAdult; + gSaveContext.eventChkInf[5] |= 0x800; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } +} + +void func_80A1DBA0(EnFu* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnFu_WaitAdult; + } +} + +void func_80A1DBD4(EnFu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_04) { + this->actionFunc = EnFu_WaitAdult; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + this->actor.flags &= ~ACTOR_FLAG_16; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + this->actionFunc = func_80A1DB60; + this->actor.flags &= ~ACTOR_FLAG_16; + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gSongOfStormsCs); + gSaveContext.cutsceneTrigger = 1; + Item_Give(globalCtx, ITEM_SONG_STORMS); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + gSaveContext.eventChkInf[6] |= 0x20; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_02) { + player->stateFlags2 &= ~0x1000000; + this->actionFunc = EnFu_WaitAdult; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + player->stateFlags2 |= 0x800000; + } +} + +void EnFu_WaitForPlayback(EnFu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + player->stateFlags2 |= 0x800000; + // if dialog state is 7, player has played back the song + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_SONG_DEMO_DONE) { + func_8010BD58(globalCtx, OCARINA_ACTION_PLAYBACK_STORMS); + this->actionFunc = func_80A1DBD4; + } +} + +void EnFu_TeachSong(EnFu* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + player->stateFlags2 |= 0x800000; + // if dialog state is 2, start song demonstration + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->behaviorFlags &= ~FU_WAIT; + Audio_OcaSetInstrument(4); // seems to be related to setting instrument type + func_8010BD58(globalCtx, OCARINA_ACTION_TEACH_STORMS); + this->actionFunc = EnFu_WaitForPlayback; + } +} + +void EnFu_WaitAdult(EnFu* this, GlobalContext* globalCtx) { + static s16 yawDiff; + Player* player = GET_PLAYER(globalCtx); + + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((gSaveContext.eventChkInf[5] & 0x800)) { + func_80A1D94C(this, globalCtx, 0x508E, func_80A1DBA0); + } else if (player->stateFlags2 & 0x1000000) { + this->actor.textId = 0x5035; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->actionFunc = EnFu_TeachSong; + this->behaviorFlags |= FU_WAIT; + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_80A1DBA0; + } else if (ABS(yawDiff) < 0x2301) { + if (this->actor.xzDistToPlayer < 100.0f) { + this->actor.textId = 0x5034; + func_8002F2CC(&this->actor, globalCtx, 100.0f); + player->stateFlags2 |= 0x800000; + } + } +} + +void EnFu_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnFu* this = (EnFu*)thisx; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if ((!(this->behaviorFlags & FU_WAIT)) && (SkelAnime_Update(&this->skelanime) != 0)) { + Animation_Change(&this->skelanime, this->skelanime.animation, 1.0f, 0.0f, + Animation_GetLastFrame(this->skelanime.animation), ANIMMODE_ONCE, 0.0f); + } + this->actionFunc(this, globalCtx); + if ((this->behaviorFlags & FU_RESET_LOOK_ANGLE)) { + Math_SmoothStepToS(&this->lookAngleOffset.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->lookAngleOffset.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.y, 0, 6, 6200, 100); + this->behaviorFlags &= ~FU_RESET_LOOK_ANGLE; + } else { + func_80038290(globalCtx, &this->actor, &this->lookAngleOffset, &this->unk_2A2, this->actor.focus.pos); + } +} + +s32 EnFu_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnFu* this = (EnFu*)thisx; + s32 pad; + + if (limbIndex == FU_LIMB_UNK) { + return false; + } + switch (limbIndex) { + case FU_LIMB_HEAD: + rot->x += this->lookAngleOffset.y; + rot->z += this->lookAngleOffset.x; + break; + case FU_LIMB_CHEST_MUSIC_BOX: + break; + } + + if (!(this->behaviorFlags & FU_WAIT)) { + return false; + } + + if (limbIndex == FU_LIMB_CHEST_MUSIC_BOX) { + rot->y += (Math_SinS((globalCtx->state.frames * (limbIndex * 50 + 0x814))) * 200.0f); + rot->z += (Math_CosS((globalCtx->state.frames * (limbIndex * 50 + 0x940))) * 200.0f); + } + return false; +} + +void EnFu_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnFu* this = (EnFu*)thisx; + + if (limbIndex == FU_LIMB_HEAD) { + Matrix_MultVec3f(&sMtxSrc, &this->actor.focus.pos); + } +} + +void EnFu_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sEyesSegments[] = { gWindmillManEyeClosedTex, gWindmillManEyeAngryTex }; + static void* sMouthSegments[] = { gWindmillManMouthOpenTex, gWindmillManMouthAngryTex }; + s32 pad; + EnFu* this = (EnFu*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fu.c", 773); + + func_800943C8(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyesSegments[this->facialExpression])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthSegments[this->facialExpression])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, this->skelanime.dListCount, + EnFu_OverrideLimbDraw, EnFu_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fu.c", 791); +} diff --git a/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.h b/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.h new file mode 100644 index 000000000..b9de1a1cf --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fu/z_en_fu.h @@ -0,0 +1,44 @@ +#ifndef Z_EN_FU_H +#define Z_EN_FU_H + +#include "ultra64.h" +#include "global.h" + +struct EnFu; + +typedef void (*EnFuActionFunc)(struct EnFu*, GlobalContext*); + +typedef enum { + /* 0x00 */ FU_LIMB_ROOT, + /* 0x01 */ FU_LIMB_TORSO, + /* 0x02 */ FU_LIMB_LEFT_THIGH, + /* 0x03 */ FU_LIMB_LEFT_LEG, + /* 0x04 */ FU_LIMB_LEFT_FOOT, + /* 0x05 */ FU_LIMB_RIGHT_THIGH, + /* 0x06 */ FU_LIMB_RIGHT_LEG, + /* 0x07 */ FU_LIMB_RIGHT_FOOT, + /* 0x08 */ FU_LIMB_CHEST_MUSIC_BOX, + /* 0x09 */ FU_LIMB_LEFT_ARM_MUSIC_BOX, + /* 0x0A */ FU_LIMB_UNK, + /* 0x0B */ FU_LIMB_RIGHT_SHOULDER, + /* 0x0C */ FU_LIMB_RIGHT_ARM, + /* 0x0D */ FU_LIMB_RIGHT_HAND_AND_CRANK, + /* 0x0E */ FU_LIMB_HEAD, + /* 0x0F */ FU_LIMB_HORN, + /* 0x10 */ FU_LIMB_MAX +} EnFuLimb; + +typedef struct EnFu { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelanime; + /* 0x01DC */ Vec3s jointTable[FU_LIMB_MAX]; + /* 0x023C */ Vec3s morphTable[FU_LIMB_MAX]; + /* 0x029C */ Vec3s lookAngleOffset; // offset applied to neck rotation to look at link when he is close + /* 0x02A2 */ Vec3s unk_2A2; + /* 0x02A8 */ u16 behaviorFlags; + /* 0x02AA */ u16 facialExpression; + /* 0x02AC */ EnFuActionFunc actionFunc; +} EnFu; // size = 0x02B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c new file mode 100644 index 000000000..605f8931b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.c @@ -0,0 +1,491 @@ +/* + * File: z_en_fw.c + * Overlay: ovl_En_Fw + * Description: Flare Dancer Core + */ + +#include "z_en_fw.h" +#include "objects/object_fw/object_fw.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_9) + +void EnFw_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFw_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFw_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnFw_UpdateDust(EnFw* this); +void EnFw_DrawDust(EnFw* this, GlobalContext* globalCtx); +void EnFw_AddDust(EnFw* this, Vec3f* initialPos, Vec3f* initialSpeed, Vec3f* accel, u8 initialTimer, f32 scale, + f32 scaleStep); +void EnFw_Bounce(EnFw* this, GlobalContext* globalCtx); +void EnFw_Run(EnFw* this, GlobalContext* globalCtx); +void EnFw_JumpToParentInitPos(EnFw* this, GlobalContext* globalCtx); +void EnFw_TurnToParentInitPos(EnFw* this, GlobalContext* globalCtx); + +const ActorInit En_Fw_InitVars = { + ACTOR_EN_FW, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_FW, + sizeof(EnFw), + (ActorFunc)EnFw_Init, + (ActorFunc)EnFw_Destroy, + (ActorFunc)EnFw_Update, + (ActorFunc)EnFw_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x04 }, + { 0xFFCFFFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 2, { { 1200, 0, 0 }, 16 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit2 D_80A1FB94 = { 8, 2, 25, 25, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENFW_ANIM_0, + /* 1 */ ENFW_ANIM_1, + /* 2 */ ENFW_ANIM_2 +} EnFwAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gFlareDancerCoreInitRunCycleAnim, 0.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, 0.0f }, + { &gFlareDancerCoreRunCycleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -8.0f }, + { &gFlareDancerCoreEndRunCycleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, -8.0f }, +}; + +s32 EnFw_DoBounce(EnFw* this, s32 totalBounces, f32 yVelocity) { + s16 temp_v1; + + if (!(this->actor.bgCheckFlags & 1) || (this->actor.velocity.y > 0.0f)) { + // not on the ground or moving upwards. + return false; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + this->bounceCnt--; + if (this->bounceCnt <= 0) { + if (this->bounceCnt == 0) { + this->bounceCnt = 0; + this->actor.velocity.y = 0.0f; + return true; + } + this->bounceCnt = totalBounces; + } + this->actor.velocity.y = yVelocity; + this->actor.velocity.y *= ((f32)this->bounceCnt / totalBounces); + return 1; +} + +s32 EnFw_PlayerInRange(EnFw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + CollisionPoly* poly; + s32 bgId; + Vec3f collisionPos; + + if (this->actor.xzDistToPlayer > 300.0f) { + return false; + } + + if (ABS((s16)((f32)this->actor.yawTowardsPlayer - (f32)this->actor.shape.rot.y)) > 0x1C70) { + return false; + } + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &player->actor.world.pos, &collisionPos, + &poly, true, false, false, true, &bgId)) { + return false; + } + + return true; +} + +Vec3f* EnFw_GetPosAdjAroundCircle(Vec3f* dst, EnFw* this, f32 radius, s16 dir) { + s16 angle; + Vec3f posAdj; + + // increase rotation around circle ~30 degrees. + angle = Math_Vec3f_Yaw(&this->actor.parent->home.pos, &this->actor.world.pos) + (dir * 0x1554); + posAdj.x = (Math_SinS(angle) * radius) + this->actor.parent->home.pos.x; + posAdj.z = (Math_CosS(angle) * radius) + this->actor.parent->home.pos.z; + posAdj.x -= this->actor.world.pos.x; + posAdj.z -= this->actor.world.pos.z; + *dst = posAdj; + return dst; +} + +s32 EnFw_CheckCollider(EnFw* this, GlobalContext* globalCtx) { + ColliderInfo* info; + s32 phi_return; + + if (this->collider.base.acFlags & AC_HIT) { + info = &this->collider.elements[0].info; + if (info->acHitInfo->toucher.dmgFlags & 0x80) { + this->lastDmgHook = true; + } else { + this->lastDmgHook = false; + } + this->collider.base.acFlags &= ~AC_HIT; + if (Actor_ApplyDamage(&this->actor) <= 0) { + if (this->actor.parent->colChkInfo.health <= 8) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.parent->colChkInfo.health = 0; + } else { + this->actor.parent->colChkInfo.health -= 8; + } + this->returnToParentTimer = 0; + } + return true; + } else { + return false; + } +} + +s32 EnFw_SpawnDust(EnFw* this, u8 timer, f32 scale, f32 scaleStep, s32 dustCnt, f32 radius, f32 xzAccel, f32 yAccel) { + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + s16 angle; + s32 i; + + pos = this->actor.world.pos; + pos.y = this->actor.floorHeight + 2.0f; + angle = ((Rand_ZeroOne() - 0.5f) * 0x10000); + i = dustCnt; + while (i >= 0) { + accel.x = (Rand_ZeroOne() - 0.5f) * xzAccel; + accel.y = yAccel; + accel.z = (Rand_ZeroOne() - 0.5f) * xzAccel; + pos.x = (Math_SinS(angle) * radius) + this->actor.world.pos.x; + pos.z = (Math_CosS(angle) * radius) + this->actor.world.pos.z; + EnFw_AddDust(this, &pos, &velocity, &accel, timer, scale, scaleStep); + angle += (s16)(0x10000 / dustCnt); + i--; + } + return 0; +} + +void EnFw_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFw* this = (EnFw*)thisx; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFlareDancerCoreSkel, NULL, this->jointTable, this->morphTable, + 11); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFW_ANIM_0); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->sphs); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0x10), &D_80A1FB94); + Actor_SetScale(&this->actor, 0.01f); + this->runDirection = -this->actor.params; + this->actionFunc = EnFw_Bounce; + this->actor.gravity = -1.0f; +} + +void EnFw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFw* this = (EnFw*)thisx; + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnFw_Bounce(EnFw* this, GlobalContext* globalCtx) { + if (EnFw_DoBounce(this, 3, 8.0f) && this->bounceCnt == 0) { + this->returnToParentTimer = Rand_S16Offset(300, 150); + this->actionFunc = EnFw_Run; + } +} + +void EnFw_Run(EnFw* this, GlobalContext* globalCtx) { + f32 tmpAngle; + s16 phi_v0; + f32 facingDir; + EnBom* bomb; + Actor* flareDancer; + + Math_SmoothStepToF(&this->skelAnime.playSpeed, 1.0f, 0.1f, 1.0f, 0.0f); + if (this->skelAnime.animation == &gFlareDancerCoreInitRunCycleAnim) { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) == 0) { + this->runRadius = Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->actor.parent->world.pos); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFW_ANIM_2); + } + return; + } + + if (this->damageTimer == 0 && this->explosionTimer == 0 && EnFw_CheckCollider(this, globalCtx)) { + if (this->actor.parent->colChkInfo.health > 0) { + if (!this->lastDmgHook) { + this->actor.velocity.y = 6.0f; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_MAN_DAMAGE); + this->damageTimer = 20; + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_MAN_DAMAGE); + this->explosionTimer = 6; + } + this->actor.speedXZ = 0.0f; + } + + if (this->explosionTimer != 0) { + this->skelAnime.playSpeed = 0.0f; + Math_SmoothStepToF(&this->actor.scale.x, 0.024999999f, 0.08f, 0.6f, 0.0f); + Actor_SetScale(&this->actor, this->actor.scale.x); + if (this->actor.colorFilterTimer == 0) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->explosionTimer); + this->explosionTimer--; + } + + if (this->explosionTimer == 0) { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->bompPos.x, this->bompPos.y, + this->bompPos.z, 0, 0, 0x600, 0); + if (bomb != NULL) { + bomb->timer = 0; + } + flareDancer = this->actor.parent; + flareDancer->params |= 0x4000; + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0xA0); + Actor_Kill(&this->actor); + return; + } + } else { + if (!(this->actor.bgCheckFlags & 1) || this->actor.velocity.y > 0.0f) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->damageTimer); + return; + } + DECR(this->damageTimer); + if ((200.0f - this->runRadius) < 0.9f) { + if (DECR(this->returnToParentTimer) == 0) { + this->actor.speedXZ = 0.0f; + this->actionFunc = EnFw_TurnToParentInitPos; + return; + } + } + + // Run outwards until the radius of the run circle is 200 + Math_SmoothStepToF(&this->runRadius, 200.0f, 0.3f, 100.0f, 0.0f); + + if (this->turnAround) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 1.0f, 0.0f); + tmpAngle = (s16)(this->actor.world.rot.y ^ 0x8000); + facingDir = this->actor.shape.rot.y; + tmpAngle = Math_SmoothStepToF(&facingDir, tmpAngle, 0.1f, 10000.0f, 0.0f); + this->actor.shape.rot.y = facingDir; + if (tmpAngle > 0x1554) { + return; + } + this->turnAround = false; + } else { + Vec3f sp48; + EnFw_GetPosAdjAroundCircle(&sp48, this, this->runRadius, this->runDirection); + Math_SmoothStepToS(&this->actor.shape.rot.y, (Math_FAtan2F(sp48.x, sp48.z) * (0x8000 / M_PI)), 4, 0xFA0, 1); + } + + this->actor.world.rot = this->actor.shape.rot; + + if (this->slideTimer == 0 && EnFw_PlayerInRange(this, globalCtx)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_MAN_SURP); + this->slideSfxTimer = 8; + this->slideTimer = 8; + } + + if (this->slideTimer != 0) { + if (DECR(this->slideSfxTimer) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_MAN_SLIDE); + this->slideSfxTimer = 4; + } + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 1.0f, 0.0f); + this->skelAnime.playSpeed = 0.0f; + EnFw_SpawnDust(this, 8, 0.16f, 0.2f, 3, 8.0f, 20.0f, ((Rand_ZeroOne() - 0.5f) * 0.2f) + 0.3f); + this->slideTimer--; + if (this->slideTimer == 0) { + this->turnAround = true; + this->runDirection = -this->runDirection; + } + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 6.0f, 0.1f, 1.0f, 0.0f); + phi_v0 = this->skelAnime.curFrame; + if (phi_v0 == 1 || phi_v0 == 4) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FLAME_MAN_RUN); + EnFw_SpawnDust(this, 8, 0.16f, 0.1f, 1, 0.0f, 20.0f, 0.0f); + } + } + } +} + +void EnFw_TurnToParentInitPos(EnFw* this, GlobalContext* globalCtx) { + s16 angleToParentInit; + + angleToParentInit = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.parent->home.pos); + Math_SmoothStepToS(&this->actor.shape.rot.y, angleToParentInit, 4, 0xFA0, 1); + if (ABS(angleToParentInit - this->actor.shape.rot.y) < 0x65) { + // angle to parent init pos is ~0.5 degrees + this->actor.world.rot = this->actor.shape.rot; + this->actor.velocity.y = 14.0f; + this->actor.home.pos = this->actor.world.pos; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENFW_ANIM_1); + this->actionFunc = EnFw_JumpToParentInitPos; + } +} + +void EnFw_JumpToParentInitPos(EnFw* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1 && this->actor.velocity.y <= 0.0f) { + this->actor.parent->params |= 0x8000; + Actor_Kill(&this->actor); + } else { + Math_SmoothStepToF(&this->actor.world.pos.x, this->actor.parent->home.pos.x, 0.6f, 8.0f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, this->actor.parent->home.pos.z, 0.6f, 8.0f, 0.0f); + } +} + +void EnFw_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFw* this = (EnFw*)thisx; + SkelAnime_Update(&this->skelAnime); + if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { + // not attached to hookshot. + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 20.0f, 0.0f, 5); + this->actionFunc(this, globalCtx); + if (this->damageTimer == 0 && this->explosionTimer == 0 && this->actionFunc == EnFw_Run) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +s32 EnFw_OverrideLimbDraw(GlobalContext* globalContext, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + return false; +} + +void EnFw_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnFw* this = (EnFw*)thisx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == 2) { + // body + Matrix_MultVec3f(&zeroVec, &this->bompPos); + } + + if (limbIndex == 3) { + // head + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); + } + + Collider_UpdateSpheres(limbIndex, &this->collider); +} + +void EnFw_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnFw* this = (EnFw*)thisx; + + EnFw_UpdateDust(this); + Matrix_Push(); + EnFw_DrawDust(this, globalCtx); + Matrix_Pop(); + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnFw_OverrideLimbDraw, EnFw_PostLimbDraw, this); +} + +void EnFw_AddDust(EnFw* this, Vec3f* initialPos, Vec3f* initialSpeed, Vec3f* accel, u8 initialTimer, f32 scale, + f32 scaleStep) { + EnFwEffect* eff = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type != 1) { + eff->scale = scale; + eff->scaleStep = scaleStep; + eff->initialTimer = eff->timer = initialTimer; + eff->type = 1; + eff->pos = *initialPos; + eff->accel = *accel; + eff->velocity = *initialSpeed; + return; + } + } +} + +void EnFw_UpdateDust(EnFw* this) { + EnFwEffect* eff = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type != 0) { + if ((--eff->timer) == 0) { + eff->type = 0; + } + eff->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + eff->accel.z = (Rand_ZeroOne() * 0.4f) - 0.2f; + eff->pos.x += eff->velocity.x; + eff->pos.y += eff->velocity.y; + eff->pos.z += eff->velocity.z; + eff->velocity.x += eff->accel.x; + eff->velocity.y += eff->accel.y; + eff->velocity.z += eff->accel.z; + eff->scale += eff->scaleStep; + } + } +} + +void EnFw_DrawDust(EnFw* this, GlobalContext* globalCtx) { + static void* dustTextures[] = { + gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex, + }; + EnFwEffect* eff = this->effects; + s16 firstDone; + s16 alpha; + s16 i; + s16 idx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fw.c", 1191); + + firstDone = false; + func_80093D84(globalCtx->state.gfxCtx); + if (1) {} + + for (i = 0; i < ARRAY_COUNT(this->effects); i++, eff++) { + if (eff->type != 0) { + if (!firstDone) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0U); + gSPDisplayList(POLY_XLU_DISP++, gFlareDancerDL_7928); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 60, 20, 0); + firstDone = true; + } + + alpha = eff->timer * (255.0f / eff->initialTimer); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 130, 90, alpha); + gDPPipeSync(POLY_XLU_DISP++); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(eff->scale, eff->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fw.c", 1229), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + idx = eff->timer * (8.0f / eff->initialTimer); + gSPSegment(POLY_XLU_DISP++, 0x8, SEGMENTED_TO_VIRTUAL(dustTextures[idx])); + gSPDisplayList(POLY_XLU_DISP++, gFlareDancerSquareParticleDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fw.c", 1243); +} diff --git a/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h new file mode 100644 index 000000000..f7d245e5e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fw/z_en_fw.h @@ -0,0 +1,48 @@ +#ifndef Z_EN_FW_H +#define Z_EN_FW_H + +#include "ultra64.h" +#include "global.h" + +struct EnFw; + +typedef void (*EnFwActionFunc)(struct EnFw* this, GlobalContext* globalCtx); + +typedef struct { + /* 0x0000 */ u8 type; + /* 0x0001 */ u8 timer; + /* 0x0002 */ u8 initialTimer; + /* 0x0004 */ f32 scale; + /* 0x0008 */ f32 scaleStep; + /* 0x000C */ Color_RGBA8 color; + /* 0x0010 */ char unk_10[4]; + /* 0x0014 */ Vec3f pos; + /* 0x0020 */ Vec3f velocity; + /* 0x002C */ Vec3f accel; +} EnFwEffect; + +typedef struct EnFw { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnFwActionFunc actionFunc; + /* 0x0194 */ ColliderJntSph collider; + /* 0x01B4 */ ColliderJntSphElement sphs[1]; + /* 0x01F4 */ Vec3f bompPos; + /* 0x0200 */ u8 lastDmgHook; + /* 0x0202 */ s16 runDirection; + /* 0x0204 */ s16 bounceCnt; + /* 0x0206 */ char unk_206[0x2]; + /* 0x0208 */ s16 damageTimer; + /* 0x020A */ s16 explosionTimer; + /* 0x020C */ char unk_20C[0x2]; + /* 0x020E */ s16 slideTimer; + /* 0x0210 */ s16 slideSfxTimer; + /* 0x0212 */ s16 returnToParentTimer; + /* 0x0214 */ s16 turnAround; + /* 0x0218 */ f32 runRadius; + /* 0x021C */ Vec3s jointTable[11]; + /* 0x025E */ Vec3s morphTable[11]; + /* 0x02A0 */ EnFwEffect effects[20]; +} EnFw; // size = 0x0700 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c new file mode 100644 index 000000000..3ae77c12f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.c @@ -0,0 +1,893 @@ +#include "z_en_fz.h" +#include "objects/object_fz/object_fz.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_10) + +void EnFz_Init(Actor* thisx, GlobalContext* globalCtx); +void EnFz_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnFz_Update(Actor* thisx, GlobalContext* globalCtx); +void EnFz_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnFz_UpdateTargetPos(EnFz* this, GlobalContext* globalCtx); + +// Stationary Freezard +void EnFz_SetupBlowSmokeStationary(EnFz* this); +void EnFz_BlowSmokeStationary(EnFz* this, GlobalContext* globalCtx); + +// Moving Freezard that can vanish and reappear +void EnFz_Wait(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupAppear(EnFz* this); +void EnFz_Appear(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupAimForMove(EnFz* this); +void EnFz_AimForMove(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupMoveTowardsPlayer(EnFz* this); +void EnFz_MoveTowardsPlayer(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupAimForFreeze(EnFz* this); +void EnFz_AimForFreeze(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupBlowSmoke(EnFz* this, GlobalContext* globalCtx); +void EnFz_BlowSmoke(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupDisappear(EnFz* this); +void EnFz_Disappear(EnFz* this, GlobalContext* globalCtx); +void EnFz_SetupWait(EnFz* this); + +// Killed with fire source +void EnFz_SetupMelt(EnFz* this); +void EnFz_Melt(EnFz* this, GlobalContext* globalCtx); + +// Death +void EnFz_SetupDespawn(EnFz* this, GlobalContext* globalCtx); +void EnFz_Despawn(EnFz* this, GlobalContext* globalCtx); + +// Ice Smoke Effects +void EnFz_SpawnIceSmokeNoFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale); +void EnFz_SpawnIceSmokeFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale, f32 xyScaleTarget, + s16 primAlpha, u8 isTimerMod8); +void EnFz_UpdateIceSmoke(EnFz* this, GlobalContext* globalCtx); +void EnFz_DrawIceSmoke(EnFz* this, GlobalContext* globalCtx); + +const ActorInit En_Fz_InitVars = { + ACTOR_EN_FZ, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_FZ, + sizeof(EnFz), + (ActorFunc)EnFz_Init, + (ActorFunc)EnFz_Destroy, + (ActorFunc)EnFz_Update, + (ActorFunc)EnFz_Draw, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit1 = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCE0FDB, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 30, 80, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInitType1 sCylinderInit2 = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0x0001F024, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 35, 80, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInitType1 sCylinderInit3 = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x02, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { 20, 30, -15, { 0, 0, 0 } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0xF), + /* Slingshot */ DMG_ENTRY(0, 0xF), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0xF), + /* Normal arrow */ DMG_ENTRY(0, 0xF), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(2, 0xF), + /* Kokiri sword */ DMG_ENTRY(0, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(0, 0xF), + /* Light arrow */ DMG_ENTRY(0, 0xF), + /* Unk arrow 1 */ DMG_ENTRY(0, 0xF), + /* Unk arrow 2 */ DMG_ENTRY(0, 0xF), + /* Unk arrow 3 */ DMG_ENTRY(0, 0xF), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0xF), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(0, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x3B, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +void EnFz_Init(Actor* thisx, GlobalContext* globalCtx) { + EnFz* this = (EnFz*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.colChkInfo.health = 6; + + Collider_InitCylinder(globalCtx, &this->collider1); + Collider_SetCylinderType1(globalCtx, &this->collider1, &this->actor, &sCylinderInit1); + + Collider_InitCylinder(globalCtx, &this->collider2); + Collider_SetCylinderType1(globalCtx, &this->collider2, &this->actor, &sCylinderInit2); + + Collider_InitCylinder(globalCtx, &this->collider3); + Collider_SetCylinderType1(globalCtx, &this->collider3, &this->actor, &sCylinderInit3); + + Actor_SetScale(&this->actor, 0.008f); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.flags &= ~ACTOR_FLAG_0; + this->unusedTimer1 = 0; + this->unusedCounter = 0; + this->updateBgInfo = true; + this->isMoving = false; + this->isFreezing = false; + this->isActive = true; + this->isDespawning = false; + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->posOrigin.y = this->actor.world.pos.y; + this->iceSmokeFreezingSpawnHeight = this->actor.world.pos.y; + this->posOrigin.x = this->actor.world.pos.x; + this->posOrigin.z = this->actor.world.pos.z; + this->unusedFloat = 135.0f; + + if (this->actor.params < 0) { + this->envAlpha = 0; + this->actor.scale.y = 0.0f; + EnFz_SetupWait(this); + } else { + this->envAlpha = 255; + EnFz_SetupBlowSmokeStationary(this); + } + + EnFz_UpdateTargetPos(this, globalCtx); +} + +void EnFz_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnFz* this = (EnFz*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider1); + Collider_DestroyCylinder(globalCtx, &this->collider2); + Collider_DestroyCylinder(globalCtx, &this->collider3); +} + +void EnFz_UpdateTargetPos(EnFz* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f hitPos; + Vec3f vec1; + s32 bgId; + CollisionPoly* hitPoly; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 20.0f; + pos.z = this->actor.world.pos.z; + + Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, MTXMODE_APPLY); + vec1.x = vec1.y = 0.0f; + vec1.z = 220.0f; + Matrix_MultVec3f(&vec1, &this->wallHitPos); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &pos, &this->wallHitPos, &hitPos, &hitPoly, true, false, false, + true, &bgId)) { + Math_Vec3f_Copy(&this->wallHitPos, &hitPos); + } + + pos.x = this->actor.world.pos.x - this->wallHitPos.x; + pos.z = this->actor.world.pos.z - this->wallHitPos.z; + + this->distToTargetSq = SQ(pos.x) + SQ(pos.z); +} + +s32 EnFz_ReachedTarget(EnFz* this, Vec3f* vec) { + if (this->distToTargetSq <= (SQ(this->actor.world.pos.x - vec->x) + SQ(this->actor.world.pos.z - vec->z))) { + return true; + } else { + return false; + } +} + +void EnFz_Damaged(EnFz* this, GlobalContext* globalCtx, Vec3f* vec, s32 numEffects, f32 unkFloat) { + s32 i; + Vec3f pos; + Vec3f vel; + Vec3f accel; + Color_RGBA8 primColor; + Color_RGBA8 envColor; + f32 scale; + s32 life; + + primColor.r = 155; + primColor.g = 255; + primColor.b = 255; + primColor.a = 255; + envColor.r = 200; + envColor.g = 200; + envColor.b = 200; + accel.x = accel.z = 0.0f; + accel.y = -1.0f; + + for (i = 0; i < numEffects; i++) { + scale = Rand_CenteredFloat(0.3f) + 0.6f; + life = (s32)Rand_CenteredFloat(5.0f) + 12; + pos.x = Rand_CenteredFloat(unkFloat) + vec->x; + pos.y = Rand_ZeroFloat(unkFloat) + vec->y; + pos.z = Rand_CenteredFloat(unkFloat) + vec->z; + vel.x = Rand_CenteredFloat(10.0f); + vel.y = Rand_ZeroFloat(10.0f) + 2.0f; + vel.z = Rand_CenteredFloat(10.0f); + EffectSsEnIce_Spawn(globalCtx, &pos, scale, &vel, &accel, &primColor, &envColor, life); + } + + CollisionCheck_SpawnShieldParticles(globalCtx, vec); +} + +void EnFz_SpawnIceSmokeHiddenState(EnFz* this) { +} + +// Fully grown +void EnFz_SpawnIceSmokeGrowingState(EnFz* this) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + if ((this->counter % 16) == 0) { + pos.x = Rand_CenteredFloat(40.0f) + this->actor.world.pos.x; + pos.y = Rand_CenteredFloat(40.0f) + this->actor.world.pos.y + 30.0f; + pos.z = Rand_CenteredFloat(40.0f) + this->actor.world.pos.z; + accel.x = accel.z = 0.0f; + accel.y = 0.1f; + velocity.x = velocity.y = velocity.z = 0.0f; + EnFz_SpawnIceSmokeNoFreeze(this, &pos, &velocity, &accel, Rand_ZeroFloat(7.5f) + 15.0f); + } +} + +// (2) Growing or Shrinking to/from hiding or (3) melting from fire +void EnFz_SpawnIceSmokeActiveState(EnFz* this) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + + if ((this->counter % 4) == 0) { + pos.x = Rand_CenteredFloat(40.0f) + this->actor.world.pos.x; + pos.y = this->iceSmokeFreezingSpawnHeight; + pos.z = Rand_CenteredFloat(40.0f) + this->actor.world.pos.z; + accel.x = accel.z = 0.0f; + accel.y = 0.1f; + velocity.x = velocity.y = velocity.z = 0.0f; + EnFz_SpawnIceSmokeNoFreeze(this, &pos, &velocity, &accel, Rand_ZeroFloat(7.5f) + 15.0f); + } +} + +void EnFz_ApplyDamage(EnFz* this, GlobalContext* globalCtx) { + Vec3f vec; + + if (this->isMoving && + ((this->actor.bgCheckFlags & 8) || + (Actor_TestFloorInDirection(&this->actor, globalCtx, 60.0f, this->actor.world.rot.y) == 0))) { + this->actor.bgCheckFlags &= ~8; + this->isMoving = false; + this->speedXZ = 0.0f; + this->actor.speedXZ = 0.0f; + } + + if (this->isFreezing) { + if ((this->actor.params < 0) && (this->collider1.base.atFlags & 2)) { + this->isMoving = false; + this->collider1.base.acFlags &= ~2; + this->actor.speedXZ = this->speedXZ = 0.0f; + this->timer = 10; + EnFz_SetupDisappear(this); + } else if (this->collider2.base.acFlags & 0x80) { + this->collider2.base.acFlags &= ~0x80; + this->collider1.base.acFlags &= ~2; + } else if (this->collider1.base.acFlags & 2) { + this->collider1.base.acFlags &= ~2; + if (this->actor.colChkInfo.damageEffect != 2) { + if (this->actor.colChkInfo.damageEffect == 0xF) { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8); + if (this->actor.colChkInfo.health) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE); + vec.x = this->actor.world.pos.x; + vec.y = this->actor.world.pos.y; + vec.z = this->actor.world.pos.z; + EnFz_Damaged(this, globalCtx, &vec, 10, 0.0f); + this->unusedCounter++; + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_ICE_BROKEN); + vec.x = this->actor.world.pos.x; + vec.y = this->actor.world.pos.y; + vec.z = this->actor.world.pos.z; + EnFz_Damaged(this, globalCtx, &vec, 30, 10.0f); + EnFz_SetupDespawn(this, globalCtx); + } + } + } else { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 8); + if (this->actor.colChkInfo.health == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DEAD); + EnFz_SetupMelt(this); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FREEZAD_DAMAGE); + } + } + } + } +} + +void EnFz_SetYawTowardsPlayer(EnFz* this) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 10, 2000, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; +} + +void EnFz_SetupDisappear(EnFz* this) { + this->state = 2; + this->isFreezing = false; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnFz_Disappear; +} + +void EnFz_Disappear(EnFz* this, GlobalContext* globalCtx) { + this->envAlpha -= 16; + + if (this->envAlpha > 255) { + this->envAlpha = 0; + } + + if (Math_SmoothStepToF(&this->actor.scale.y, 0.0f, 1.0f, 0.0005f, 0) == 0.0f) { + EnFz_SetupWait(this); + } +} + +void EnFz_SetupWait(EnFz* this) { + this->state = 0; + this->unusedNum2 = 0; + this->unusedNum1 = 0; + this->timer = 100; + this->actionFunc = EnFz_Wait; + this->actor.world.pos.x = this->posOrigin.x; + this->actor.world.pos.y = this->posOrigin.y; + this->actor.world.pos.z = this->posOrigin.z; +} + +void EnFz_Wait(EnFz* this, GlobalContext* globalCtx) { + if ((this->timer == 0) && (this->actor.xzDistToPlayer < 400.0f)) { + EnFz_SetupAppear(this); + } +} + +void EnFz_SetupAppear(EnFz* this) { + this->state = 2; + this->timer = 20; + this->unusedNum2 = 4000; + this->actionFunc = EnFz_Appear; +} + +void EnFz_Appear(EnFz* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + this->envAlpha += 8; + if (this->envAlpha > 255) { + this->envAlpha = 255; + } + + if (Math_SmoothStepToF(&this->actor.scale.y, 0.008f, 1.0f, 0.0005f, 0.0f) == 0.0f) { + EnFz_SetupAimForMove(this); + } + } +} + +void EnFz_SetupAimForMove(EnFz* this) { + this->state = 1; + this->timer = 40; + this->updateBgInfo = true; + this->isFreezing = true; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = EnFz_AimForMove; + this->actor.gravity = -1.0f; +} + +void EnFz_AimForMove(EnFz* this, GlobalContext* globalCtx) { + EnFz_SetYawTowardsPlayer(this); + + if (this->timer == 0) { + EnFz_SetupMoveTowardsPlayer(this); + } +} + +void EnFz_SetupMoveTowardsPlayer(EnFz* this) { + this->state = 1; + this->isMoving = true; + this->timer = 100; + this->actionFunc = EnFz_MoveTowardsPlayer; + this->speedXZ = 4.0f; +} + +void EnFz_MoveTowardsPlayer(EnFz* this, GlobalContext* globalCtx) { + if ((this->timer == 0) || !this->isMoving) { + EnFz_SetupAimForFreeze(this); + } +} + +void EnFz_SetupAimForFreeze(EnFz* this) { + this->state = 1; + this->timer = 40; + this->actionFunc = EnFz_AimForFreeze; + this->speedXZ = 0.0f; + this->actor.speedXZ = 0.0f; +} + +void EnFz_AimForFreeze(EnFz* this, GlobalContext* globalCtx) { + EnFz_SetYawTowardsPlayer(this); + + if (this->timer == 0) { + EnFz_SetupBlowSmoke(this, globalCtx); + } +} + +void EnFz_SetupBlowSmoke(EnFz* this, GlobalContext* globalCtx) { + this->state = 1; + this->timer = 80; + this->actionFunc = EnFz_BlowSmoke; + EnFz_UpdateTargetPos(this, globalCtx); +} + +void EnFz_BlowSmoke(EnFz* this, GlobalContext* globalCtx) { + Vec3f vec1; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + u8 isTimerMod8; + s16 primAlpha; + + if (this->timer == 0) { + EnFz_SetupDisappear(this); + } else if (this->timer >= 11) { + isTimerMod8 = false; + primAlpha = 150; + func_8002F974(&this->actor, NA_SE_EN_FREEZAD_BREATH - SFX_FLAG); + + if ((this->timer - 10) < 16) { // t < 26 + primAlpha = (this->timer * 10) - 100; + } + + accel.x = accel.z = 0.0f; + accel.y = 0.6f; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 20.0f; + pos.z = this->actor.world.pos.z; + + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + + vec1.x = 0.0f; + vec1.y = -2.0f; + vec1.z = 20.0f; // xz velocity + + Matrix_MultVec3f(&vec1, &velocity); + + if ((this->timer % 8) == 0) { + isTimerMod8 = true; + } + + EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, isTimerMod8); + + pos.x += (velocity.x * 0.5f); + pos.y += (velocity.y * 0.5f); + pos.z += (velocity.z * 0.5f); + + EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, false); + } +} + +void EnFz_SetupDespawn(EnFz* this, GlobalContext* globalCtx) { + this->state = 0; + this->updateBgInfo = true; + this->isFreezing = false; + this->isDespawning = true; + this->actor.flags &= ~ACTOR_FLAG_0; + this->isActive = false; + this->timer = 60; + this->speedXZ = 0.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x60); + this->actionFunc = EnFz_Despawn; +} + +void EnFz_Despawn(EnFz* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + Actor_Kill(&this->actor); + } +} + +void EnFz_SetupMelt(EnFz* this) { + this->state = 3; + this->isFreezing = false; + this->isDespawning = true; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnFz_Melt; + this->actor.speedXZ = 0.0f; + this->speedXZ = 0.0f; +} + +void EnFz_Melt(EnFz* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.scale.y, 0.0006f, 0.0002f); + + if (this->actor.scale.y < 0.006f) { + this->actor.scale.x += 0.0004f; + this->actor.scale.z += 0.0004f; + } + + if (this->actor.scale.y < 0.004f) { + this->envAlpha -= 8; + if (this->envAlpha > 255) { + this->envAlpha = 0; + } + } + + if (this->envAlpha == 0) { + EnFz_SetupDespawn(this, globalCtx); + } +} + +void EnFz_SetupBlowSmokeStationary(EnFz* this) { + this->state = 1; + this->timer = 40; + this->updateBgInfo = true; + this->isFreezing = true; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = EnFz_BlowSmokeStationary; + this->actor.gravity = -1.0f; +} + +void EnFz_BlowSmokeStationary(EnFz* this, GlobalContext* globalCtx) { + Vec3f vec1; + Vec3f pos; + Vec3f velocity; + Vec3f accel; + u8 isTimerMod8; + s16 primAlpha; + + if (this->counter & 0xC0) { + EnFz_SetYawTowardsPlayer(this); + EnFz_UpdateTargetPos(this, globalCtx); + } else { + isTimerMod8 = false; + primAlpha = 150; + func_8002F974(&this->actor, NA_SE_EN_FREEZAD_BREATH - SFX_FLAG); + + if ((this->counter & 0x3F) >= 48) { + primAlpha = 630 - ((this->counter & 0x3F) * 10); + } + + accel.x = accel.z = 0.0f; + accel.y = 0.6f; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 20.0f; + pos.z = this->actor.world.pos.z; + + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + + vec1.x = 0.0f; + vec1.y = -2.0f; + vec1.z = 20.0f; + + Matrix_MultVec3f(&vec1, &velocity); + + if ((this->counter % 8) == 0) { + isTimerMod8 = true; + } + + EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, isTimerMod8); + + pos.x += (velocity.x * 0.5f); + pos.y += (velocity.y * 0.5f); + pos.z += (velocity.z * 0.5f); + + EnFz_SpawnIceSmokeFreeze(this, &pos, &velocity, &accel, 2.0f, 25.0f, primAlpha, false); + } +} + +static EnFzSpawnIceSmokeFunc iceSmokeSpawnFuncs[] = { + EnFz_SpawnIceSmokeHiddenState, + EnFz_SpawnIceSmokeGrowingState, + EnFz_SpawnIceSmokeActiveState, + EnFz_SpawnIceSmokeActiveState, +}; + +void EnFz_Update(Actor* thisx, GlobalContext* globalCtx) { + EnFz* this = (EnFz*)thisx; + s32 pad; + + this->counter++; + + if (this->unusedTimer1 != 0) { + this->unusedTimer1--; + } + + if (this->timer != 0) { + this->timer--; + } + + if (this->unusedTimer2 != 0) { + this->unusedTimer2--; + } + + Actor_SetFocus(&this->actor, 50.0f); + EnFz_ApplyDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + if (this->isDespawning == false) { + Collider_UpdateCylinder(&this->actor, &this->collider1); + Collider_UpdateCylinder(&this->actor, &this->collider2); + if (this->isFreezing) { + if (this->actor.colorFilterTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + } + } + + Math_StepToF(&this->actor.speedXZ, this->speedXZ, 0.2f); + Actor_MoveForward(&this->actor); + + if (this->updateBgInfo) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 5); + } + + iceSmokeSpawnFuncs[this->state](this); + EnFz_UpdateIceSmoke(this, globalCtx); +} + +void EnFz_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* displayLists[] = { + gFreezardIntactDL, // Body fully intact (5 or 6 health) + gFreezardTopRightHornChippedDL, // Top right horn chipped off (from Freezards perspective) (3 or 4 health) + gFreezardHeadChippedDL, // Entire head chipped off (1 or 2 health) + }; + EnFz* this = (EnFz*)thisx; + s32 pad; + s32 index; + + index = (6 - this->actor.colChkInfo.health) >> 1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fz.c", 1167); + + if (1) {} + + if (this->actor.colChkInfo.health == 0) { + index = 2; + } + + if (this->isActive) { + func_8002ED80(&this->actor, globalCtx, 0); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, globalCtx->state.frames & 0x7F, 32, 32, 1, 0, + (2 * globalCtx->state.frames) & 0x7F, 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_fz.c", 1183), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetCombineLERP(POLY_XLU_DISP++, TEXEL1, PRIMITIVE, PRIM_LOD_FRAC, TEXEL0, TEXEL1, TEXEL0, PRIMITIVE, TEXEL0, + PRIMITIVE, ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, ENVIRONMENT, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, 155, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, this->envAlpha); + gSPDisplayList(POLY_XLU_DISP++, displayLists[index]); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fz.c", 1200); + EnFz_DrawIceSmoke(this, globalCtx); +} + +void EnFz_SpawnIceSmokeNoFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale) { + EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) { + if (iceSmoke->type == 0) { + iceSmoke->type = 1; + iceSmoke->pos = *pos; + iceSmoke->velocity = *velocity; + iceSmoke->accel = *accel; + iceSmoke->primAlphaState = 0; + iceSmoke->xyScale = xyScale / 1000.0f; + iceSmoke->primAlpha = 0; + iceSmoke->timer = 0; + break; + } + + iceSmoke++; + } +} + +void EnFz_SpawnIceSmokeFreeze(EnFz* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, f32 xyScale, f32 xyScaleTarget, + s16 primAlpha, u8 isTimerMod8) { + EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) { + if (iceSmoke->type == 0) { + iceSmoke->type = 2; + iceSmoke->pos = *pos; + iceSmoke->velocity = *velocity; + iceSmoke->accel = *accel; + iceSmoke->primAlphaState = 0; + iceSmoke->xyScale = xyScale / 1000.0f; + iceSmoke->xyScaleTarget = xyScaleTarget / 1000.0f; + iceSmoke->primAlpha = primAlpha; + iceSmoke->timer = 0; + iceSmoke->isTimerMod8 = isTimerMod8; + break; + } + + iceSmoke++; + } +} + +void EnFz_UpdateIceSmoke(EnFz* this, GlobalContext* globalCtx) { + EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke; + s16 i; + Vec3f pos; + + for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) { + if (iceSmoke->type) { + iceSmoke->pos.x += iceSmoke->velocity.x; + iceSmoke->pos.y += iceSmoke->velocity.y; + iceSmoke->pos.z += iceSmoke->velocity.z; + iceSmoke->timer++; + iceSmoke->velocity.x += iceSmoke->accel.x; + iceSmoke->velocity.y += iceSmoke->accel.y; + iceSmoke->velocity.z += iceSmoke->accel.z; + if (iceSmoke->type == 1) { + if (iceSmoke->primAlphaState == 0) { // Becoming more opaque + iceSmoke->primAlpha += 10; + if (iceSmoke->primAlpha >= 100) { + iceSmoke->primAlphaState++; + } + } else { // Becoming more transparent + iceSmoke->primAlpha -= 3; + if (iceSmoke->primAlpha <= 0) { + iceSmoke->primAlpha = 0; + iceSmoke->type = 0; + } + } + } else if (iceSmoke->type == 2) { // Freezing + Math_ApproachF(&iceSmoke->xyScale, iceSmoke->xyScaleTarget, 0.1f, iceSmoke->xyScaleTarget / 10.0f); + if (iceSmoke->primAlphaState == 0) { // Becoming more opaque + if (iceSmoke->timer >= 7) { + iceSmoke->primAlphaState++; + } + } else { // Becoming more transparent, slows down + iceSmoke->accel.y = 2.0f; + iceSmoke->primAlpha -= 17; + iceSmoke->velocity.x *= 0.75f; + iceSmoke->velocity.z *= 0.75f; + if (iceSmoke->primAlpha <= 0) { + iceSmoke->primAlpha = 0; + iceSmoke->type = 0; + } + } + + if ((this->unusedTimer2 == 0) && (iceSmoke->primAlpha >= 101) && iceSmoke->isTimerMod8) { + this->collider3.dim.pos.x = (s16)iceSmoke->pos.x; + this->collider3.dim.pos.y = (s16)iceSmoke->pos.y; + this->collider3.dim.pos.z = (s16)iceSmoke->pos.z; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider3.base); + } + + pos.x = iceSmoke->pos.x; + pos.y = iceSmoke->pos.y + 10.0f; + pos.z = iceSmoke->pos.z; + + if ((iceSmoke->primAlphaState != 2) && EnFz_ReachedTarget(this, &pos)) { + iceSmoke->primAlphaState = 2; + iceSmoke->velocity.x = 0.0f; + iceSmoke->velocity.z = 0.0f; + } + } + } + iceSmoke++; + } +} + +void EnFz_DrawIceSmoke(EnFz* this, GlobalContext* globalCtx) { + EnFzEffectSsIceSmoke* iceSmoke = this->iceSmoke; + s16 i; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u8 texLoaded = false; + + OPEN_DISPS(gfxCtx, "../z_en_fz.c", 1384); + + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < ARRAY_COUNT(this->iceSmoke); i++) { + if (iceSmoke->type > 0) { + gDPPipeSync(POLY_XLU_DISP++); + + if (!texLoaded) { + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFreezardSteamStartDL)); + texLoaded++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, iceSmoke->primAlpha); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 3 * (iceSmoke->timer + (3 * i)), + 15 * (iceSmoke->timer + (3 * i)), 32, 64, 1, 0, 0, 32, 32)); + Matrix_Translate(iceSmoke->pos.x, iceSmoke->pos.y, iceSmoke->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(iceSmoke->xyScale, iceSmoke->xyScale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_fz.c", 1424), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFreezardSteamDL)); + } + + iceSmoke++; + } + + CLOSE_DISPS(gfxCtx, "../z_en_fz.c", 1430); +} diff --git a/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h new file mode 100644 index 000000000..536d2525f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Fz/z_en_fz.h @@ -0,0 +1,55 @@ +#ifndef Z_EN_FZ_H +#define Z_EN_FZ_H + +#include "ultra64.h" +#include "global.h" + +struct EnFz; + +typedef void (*EnFzActionFunc)(struct EnFz*, GlobalContext*); +typedef void (*EnFzSpawnIceSmokeFunc)(struct EnFz*); + +typedef struct { + /* 0x0000 */ u8 type; // 0,1,2: State of freezard (1 not freezing, 2 freezing) + /* 0x0001 */ u8 timer; // increments primAlphaState after reaching 7 (freezing), used in Gfx_TwoTexScroll + /* 0x0004 */ Vec3f pos; // Random position within 20.0f of actor + /* 0x0010 */ Vec3f velocity; + /* 0x001C */ Vec3f accel; + /* 0x0028 */ char unk_28[0x4]; + /* 0x002C */ s16 primAlpha; // transparency in RGBA colour system + /* 0x002E */ s16 primAlphaState; // 0: increasing (more opaque) 1: decreasing (more transparent) 2: collision + /* 0x0030 */ f32 xyScale; // + /* 0x0034 */ f32 xyScaleTarget; + /* 0x0038 */ u8 isTimerMod8; // conditional, used to run CollisionCheck_SetAT +} EnFzEffectSsIceSmoke; // size = 0x3C + +typedef struct EnFz { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnFzActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider1; + /* 0x019C */ ColliderCylinder collider2; + /* 0x01E8 */ ColliderCylinder collider3; + /* 0x0234 */ Vec3f posOrigin; // Spawn position for moving freezard + /* 0x0240 */ s16 counter; // A perpetual counter + /* 0x0242 */ s16 unusedTimer1; + /* 0x0244 */ s16 timer; // Used to time transition into next action. Can be 10, 20, 40, 60, 80, 100 + /* 0x0246 */ u8 updateBgInfo; // Always true in every instance + /* 0x0247 */ u8 isMoving; // Freezard is moving in xz plane + /* 0x0248 */ u8 isFreezing; // Freezard shooting ice projectiles that can freeze Link + /* 0x0249 */ u8 unusedCounter; // Incremented when Freezard takes damage + /* 0x024C */ f32 iceSmokeFreezingSpawnHeight; // Height for Ice Smoke Spawn, only when freezing + /* 0x0250 */ f32 unusedFloat; // Set to 135.0f + /* 0x0254 */ f32 speedXZ; // Set to 4.0f when moving + /* 0x0258 */ u32 envAlpha; // transparency in RGBA colour system + /* 0x025C */ u16 unusedNum1; // Only set to 0 + /* 0x025E */ u16 unusedNum2; // Set to either 0 when hidden or 4000 when growing + /* 0x0260 */ u8 state; // 0 (hidden) 1 (growning/shrinking) 2 (full size) 3 (melting from fire) + /* 0x0261 */ u8 isActive; // Default true. Set to false after beginning to despawn + /* 0x0262 */ u8 isDespawning; // Default false. Set to false after beginning to despawn or melting + /* 0x0263 */ u8 unusedTimer2; // Timer + /* 0x0264 */ Vec3f wallHitPos; // Position contact was made with a wall + /* 0x0270 */ f32 distToTargetSq; + /* 0x0274 */ EnFzEffectSsIceSmoke iceSmoke[40]; +} EnFz; // size = 0x0BD4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.c b/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.c new file mode 100644 index 000000000..2da9368c5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.c @@ -0,0 +1,571 @@ +/* + * File: z_en_g_switch.c + * Overlay: ovl_En_G_Switch + * Description: Silver rupees, shooting gallery targets, and horseback archery pots + */ + +#include "z_en_g_switch.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_tsubo/object_tsubo.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ MOVE_TARGET, + /* 1 */ MOVE_HOME +} GSwitchMoveState; + +void EnGSwitch_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGSwitch_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGSwitch_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGSwitch_DrawRupee(Actor* thisx, GlobalContext* globalCtx); +void EnGSwitch_DrawPot(Actor* thisx, GlobalContext* globalCtx); + +void EnGSwitch_SilverRupeeTracker(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_SilverRupeeIdle(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_WaitForObject(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_SilverRupeeCollected(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_GalleryRupee(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_ArcheryPot(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_Kill(EnGSwitch* this, GlobalContext* globalCtx); + +void EnGSwitch_SpawnEffects(EnGSwitch* this, Vec3f* pos, s16 scale, s16 colorIdx); +void EnGSwitch_UpdateEffects(EnGSwitch* this, GlobalContext* globalCtx); +void EnGSwitch_DrawEffects(EnGSwitch* this, GlobalContext* globalCtx); + +static s16 sCollectedCount = 0; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 13, 40, 0, { 0, 0, 0 } }, +}; + +// Unused, but probably intended to be this +static s16 sRupeeTypes[] = { + ITEM00_RUPEE_GREEN, ITEM00_RUPEE_BLUE, ITEM00_RUPEE_RED, ITEM00_RUPEE_ORANGE, ITEM00_RUPEE_PURPLE, +}; + +const ActorInit En_G_Switch_InitVars = { + ACTOR_EN_G_SWITCH, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnGSwitch), + (ActorFunc)EnGSwitch_Init, + (ActorFunc)EnGSwitch_Destroy, + (ActorFunc)EnGSwitch_Update, + NULL, + NULL, +}; + +void EnGSwitch_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGSwitch* this = (EnGSwitch*)thisx; + + this->type = (this->actor.params >> 0xC) & 0xF; + this->switchFlag = this->actor.params & 0x3F; + this->numEffects = ARRAY_COUNT(this->effects); + // "index" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ インデックス ☆☆☆☆☆ %x\n" VT_RST, this->type); + // "save" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ セーブ\t ☆☆☆☆☆ %x\n" VT_RST, this->switchFlag); + switch (this->type) { + case ENGSWITCH_SILVER_TRACKER: + osSyncPrintf("\n\n"); + // "parent switch spawn" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 親スイッチ発生 ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + sCollectedCount = 0; + this->silverCount = this->actor.params >> 6; + this->silverCount &= 0x3F; + // "maximum number of checks" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 最大チェック数 ☆☆☆☆☆ %d\n" VT_RST, this->silverCount); + osSyncPrintf("\n\n"); + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + // This is a reference to Hokuto no Ken + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ You are Shock! ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + Actor_Kill(&this->actor); + } else { + this->actionFunc = EnGSwitch_SilverRupeeTracker; + } + break; + case ENGSWITCH_SILVER_RUPEE: + osSyncPrintf("\n\n"); + // "child switch spawn" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 子スイッチ発生 ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + this->colorIdx = 5; + this->numEffects = 20; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.draw = EnGSwitch_DrawRupee; + this->actor.shape.yOffset = 700.0f; + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ You are Shock! ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + Actor_Kill(&this->actor); + } else { + Actor_SetScale(&this->actor, 0.03f); + this->actionFunc = EnGSwitch_SilverRupeeIdle; + } + break; + case ENGSWITCH_ARCHERY_POT: + osSyncPrintf("\n\n"); + // "Horseback archery destructible pot" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ やぶさめぶち抜き壷 ☆☆☆☆☆ \n" VT_RST); + this->actor.gravity = -3.0f; + this->colorIdx = Rand_ZeroFloat(2.99f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.scale.x = 0.25f; + this->actor.scale.y = 0.45f; + this->actor.scale.z = 0.25f; + this->collider.info.bumper.dmgFlags = 0x1F820; + this->objId = OBJECT_TSUBO; + this->objIndex = Object_GetIndex(&globalCtx->objectCtx, this->objId); + if (this->objIndex < 0) { + Actor_Kill(&this->actor); + // "what?" + osSyncPrintf(VT_FGCOL(PURPLE) " なにみの? %d\n" VT_RST "\n", this->objIndex); + // "bank is funny" + osSyncPrintf(VT_FGCOL(CYAN) " バンクおかしいしぞ!%d\n" VT_RST "\n", this->actor.params); + } + this->collider.dim.radius = 24; + this->collider.dim.height = 74; + this->collider.dim.yShift = 0; + this->actionFunc = EnGSwitch_WaitForObject; + break; + case ENGSWITCH_TARGET_RUPEE: + this->actor.shape.yOffset = 700.0f; + Actor_SetScale(&this->actor, 0.05f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.draw = EnGSwitch_DrawRupee; + this->collider.dim.radius = 20; + this->collider.dim.height = 60; + this->collider.dim.yShift = 5; + this->actionFunc = EnGSwitch_GalleryRupee; + break; + } +} + +void EnGSwitch_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGSwitch* this = (EnGSwitch*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnGSwitch_Break(EnGSwitch* this, GlobalContext* globalCtx) { + Vec3f randPos; + Vec3f hitPos; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + s32 i; + + randPos.x = this->actor.world.pos.x + Rand_CenteredFloat(40.0f); + randPos.y = this->actor.world.pos.y + 30.0f + Rand_CenteredFloat(35.0f); + randPos.z = this->actor.world.pos.z + Rand_CenteredFloat(40.0f); + hitPos.x = this->collider.info.bumper.hitPos.x; + hitPos.y = this->collider.info.bumper.hitPos.y; + hitPos.z = this->collider.info.bumper.hitPos.z; + EffectSsHitMark_SpawnCustomScale(globalCtx, EFFECT_HITMARK_WHITE, 700, &hitPos); + if (this->type == ENGSWITCH_ARCHERY_POT) { + velocity.y = 15.0f; + EffectSsExtra_Spawn(globalCtx, &hitPos, &velocity, &accel, 5, 2); + } + if (this->type == ENGSWITCH_TARGET_RUPEE) { + for (i = 0; i < this->numEffects; i++) { + EnGSwitch_SpawnEffects(this, &randPos, 100, this->colorIdx); + } + } +} + +void EnGSwitch_WaitForObject(EnGSwitch* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objIndex)) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objIndex].segment); + this->actor.objBankIndex = this->objIndex; + this->actor.draw = EnGSwitch_DrawPot; + this->actionFunc = EnGSwitch_ArcheryPot; + } +} + +void EnGSwitch_SilverRupeeTracker(EnGSwitch* this, GlobalContext* globalCtx) { + static s8 majorScale[] = { 0, 2, 4, 5, 7 }; + + if (this->noteIndex < sCollectedCount) { + if (sCollectedCount < 5) { + // "sound?" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 音? ☆☆☆☆☆ %d\n" VT_RST, this->noteIndex); + Audio_PlaySoundTransposed(&D_801333D4, NA_SE_EV_FIVE_COUNT_LUPY, majorScale[this->noteIndex]); + this->noteIndex = sCollectedCount; + } + } + if (sCollectedCount >= this->silverCount) { + // "It is now the end of the century." + // This another reference to Hokuto no Ken. + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 時はまさに世紀末〜 ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + // "Last!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ らすとぉ! ☆☆☆☆☆ \n" VT_RST); + if ((globalCtx->sceneNum == SCENE_MEN) && (this->actor.room == 2)) { + Flags_SetTempClear(globalCtx, this->actor.room); + } else { + func_80078884(NA_SE_SY_CORRECT_CHIME); + Flags_SetSwitch(globalCtx, this->switchFlag); + } + func_80078884(NA_SE_SY_GET_RUPY); + Actor_Kill(&this->actor); + } +} + +void EnGSwitch_SilverRupeeIdle(EnGSwitch* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->actor.shape.rot.y += 0x800; + if (this->actor.xyzDistToPlayerSq < 900.0f) { + Rupees_ChangeBy(5); + sCollectedCount++; + func_80078884(NA_SE_SY_GET_RUPY); + this->actor.world.pos = player->actor.world.pos; + this->actor.world.pos.y += 40.0f; + if (LINK_IS_ADULT) { + this->actor.world.pos.y += 20.0f; + } + this->actor.gravity = 0.0f; + this->killTimer = 15; + this->actionFunc = EnGSwitch_SilverRupeeCollected; + } +} + +void EnGSwitch_SilverRupeeCollected(EnGSwitch* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->actor.shape.rot.y += 0x3C0; + if (this->killTimer == 0) { + Actor_Kill(&this->actor); + return; + } + this->actor.world.pos = player->actor.world.pos; + this->actor.world.pos.y = + player->actor.world.pos.y + 40.0f + (this->killTimer * 0.3f) * Math_SinS(this->killTimer * 0x3A98); + if (LINK_IS_ADULT) { + this->actor.world.pos.y += 20.0f; + } +} + +void EnGSwitch_GalleryRupee(EnGSwitch* this, GlobalContext* globalCtx) { + EnSyatekiItm* gallery; + + this->actor.shape.rot.y += 0x3C0; + if (this->delayTimer == 0) { + switch (this->moveMode) { + case GSWITCH_THROW: + Actor_MoveForward(&this->actor); + if ((this->actor.velocity.y < 0.0f) && (this->actor.world.pos.y < (this->actor.home.pos.y - 50.0f))) { + gallery = ((EnSyatekiItm*)this->actor.parent); + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + if (gallery->actor.update != NULL) { + gallery->targetState[this->index] = ENSYATEKIHIT_MISS; + } + Actor_Kill(&this->actor); + } + break; + case GSWITCH_LEFT: + func_8002D7EC(&this->actor); + if ((this->actor.velocity.x < 0.0f) && (this->actor.world.pos.x < this->targetPos.x)) { + gallery = ((EnSyatekiItm*)this->actor.parent); + if (gallery->actor.update != NULL) { + gallery->targetState[this->index] = ENSYATEKIHIT_MISS; + } + Actor_Kill(&this->actor); + } + break; + case GSWITCH_RIGHT: + func_8002D7EC(&this->actor); + if (this->actor.world.pos.x > this->targetPos.x) { + gallery = ((EnSyatekiItm*)this->actor.parent); + if (gallery->actor.update != NULL) { + gallery->targetState[this->index] = ENSYATEKIHIT_MISS; + } + Actor_Kill(&this->actor); + } + break; + default: + switch (this->moveState) { + case MOVE_TARGET: + if ((fabsf(this->actor.world.pos.x - this->targetPos.x) > 5.0f) || + (fabsf(this->actor.world.pos.y - this->targetPos.y) > 5.0f)) { + Math_ApproachF(&this->actor.world.pos.x, this->targetPos.x, 0.3f, 30.0f); + Math_ApproachF(&this->actor.world.pos.y, this->targetPos.y, 0.3f, 30.0f); + } else { + this->moveState = MOVE_HOME; + this->waitTimer = 60; + } + break; + case MOVE_HOME: + if (this->waitTimer == 0) { + if ((fabsf(this->actor.world.pos.x - this->actor.home.pos.x) > 5.0f) || + (fabsf(this->actor.world.pos.y - this->actor.home.pos.y) > 5.0f)) { + Math_ApproachF(&this->actor.world.pos.x, this->actor.home.pos.x, 0.3f, 30.0f); + Math_ApproachF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.3f, 30.0f); + } else { + gallery = ((EnSyatekiItm*)this->actor.parent); + if (gallery->actor.update != NULL) { + gallery->targetState[this->index] = ENSYATEKIHIT_MISS; + } + Actor_Kill(&this->actor); + } + } + break; + } + break; + } + if ((this->collider.base.acFlags & AC_HIT) || BREG(8)) { + gallery = ((EnSyatekiItm*)this->actor.parent); + this->collider.base.acFlags &= ~AC_HIT; + if (gallery->actor.update != NULL) { + gallery->hitCount++; + gallery->targetState[this->index] = ENSYATEKIHIT_HIT; + func_80078884(NA_SE_EV_HIT_SOUND); + func_80078884(NA_SE_SY_GET_RUPY); + // "Yeah !" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ いぇぇーす!HIT!! ☆☆☆☆☆ %d\n" VT_RST, gallery->hitCount); + EnGSwitch_Break(this, globalCtx); + this->killTimer = 50; + this->broken = true; + this->actionFunc = EnGSwitch_Kill; + } + } + } +} + +void EnGSwitch_ArcheryPot(EnGSwitch* this, GlobalContext* globalCtx) { + s32 i; + s16 angle; + Vec3f* thisPos = &this->actor.world.pos; + + this->actor.shape.rot.y += 0x3C0; + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + for (i = 0, angle = 0; i < 30; i++, angle += 0x4E20) { + Vec3f pos; + Vec3f vel; + f32 sn = Math_SinS(angle); + f32 cs = Math_CosS(angle); + f32 rand; + s32 phi_s0; + s32 scale; + s32 pad; + + pos.x = sn * 8.0f; + pos.y = 10.0f + Rand_CenteredFloat(5.0f); + pos.z = cs * 8.0f; + + vel.x = pos.x / 2.0f; + vel.y = 10.0f + Rand_ZeroOne() * 15.0f; + vel.z = pos.z / 2.0f; + + pos.x += thisPos->x; + pos.y += thisPos->y; + pos.z += thisPos->z; + + rand = Rand_ZeroOne(); + if (rand < 0.2f) { + phi_s0 = 0x60; + } else if (rand < 0.6f) { + phi_s0 = 0x40; + } else { + phi_s0 = 0x20; + } + + scale = 30.0f + Rand_ZeroOne() * 130.0f; + + EffectSsKakera_Spawn(globalCtx, &pos, &vel, thisPos, -240, phi_s0, 10, 10, 0, scale, 0, 0x20, 60, + KAKERA_COLOR_NONE, OBJECT_TSUBO, object_tsubo_DL_001960); + } + func_80033480(globalCtx, thisPos, 30.0f, 4, 20, 50, 0); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, thisPos, 40, NA_SE_EV_POT_BROKEN); + EnGSwitch_Break(this, globalCtx); + this->killTimer = 50; + this->broken = true; + this->actionFunc = EnGSwitch_Kill; + } +} + +void EnGSwitch_Kill(EnGSwitch* this, GlobalContext* globalCtx) { + if (this->killTimer == 0) { + Actor_Kill(&this->actor); + } +} + +void EnGSwitch_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGSwitch* this = (EnGSwitch*)thisx; + + this->actionFunc(this, globalCtx); + if (this->killTimer != 0) { + this->killTimer--; + } + if (this->waitTimer != 0) { + this->waitTimer--; + } + if (this->delayTimer != 0) { + this->delayTimer--; + } + if ((this->type != ENGSWITCH_SILVER_TRACKER) && (this->type != ENGSWITCH_SILVER_RUPEE) && + (this->type != ENGSWITCH_TARGET_RUPEE)) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 0x1C); + } + if (this->actor.draw != NULL) { + if (this->type == ENGSWITCH_TARGET_RUPEE) { + EnGSwitch_UpdateEffects(this, globalCtx); + } + if ((this->actionFunc != EnGSwitch_Kill) && (this->actionFunc != EnGSwitch_SilverRupeeIdle)) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + if (BREG(0) && (this->type == ENGSWITCH_SILVER_TRACKER)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } +} + +void EnGSwitch_DrawPot(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGSwitch* this = (EnGSwitch*)thisx; + + if (!this->broken) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 918); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 925), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_tsubo_DL_0017C0); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 928); + } +} + +static void* sRupeeTextures[] = { + gRupeeGreenTex, gRupeeBlueTex, gRupeeRedTex, gRupeePinkTex, gRupeeOrangeTex, gRupeeSilverTex, +}; + +void EnGSwitch_DrawRupee(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGSwitch* this = (EnGSwitch*)thisx; + + if (1) {} + if (!this->broken) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 951); + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 957), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sRupeeTextures[this->colorIdx])); + gSPDisplayList(POLY_OPA_DISP++, gRupeeDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 961); + } + if (this->type == ENGSWITCH_TARGET_RUPEE) { + EnGSwitch_DrawEffects(this, globalCtx); + } +} + +void EnGSwitch_SpawnEffects(EnGSwitch* this, Vec3f* pos, s16 scale, s16 colorIdx) { + EnGSwitchEffect* effect = this->effects; + s16 i; + + for (i = 0; i < this->numEffects; i++, effect++) { + if (!effect->flag) { + Vec3f baseVel; + f32 pitch; + f32 yaw; + + effect->pos = *pos; + effect->scale = scale; + effect->colorIdx = colorIdx; + effect->timer = 30; + effect->rot.x = effect->rot.y = effect->rot.z = 0.0f; + pitch = Rand_CenteredFloat(1000.0f) - 13000.0f; + yaw = Rand_CenteredFloat(65535.0f); + Matrix_RotateY(yaw, MTXMODE_NEW); + Matrix_RotateX(pitch, MTXMODE_APPLY); + baseVel.x = baseVel.y = 0.0f; + baseVel.z = 20.0f; + Matrix_MultVec3f(&baseVel, &effect->velocity); + effect->flag = true; + return; + } + } +} + +void EnGSwitch_UpdateEffects(EnGSwitch* this, GlobalContext* globalCtx) { + Vec3f temp; + s16 i; + EnGSwitchEffect* effect = this->effects; + + for (i = 0; i < this->numEffects; i++, effect++) { + if (effect->flag) { + effect->rot.x += Rand_ZeroOne() * 10.0f + 15.0f; + effect->rot.y += Rand_ZeroOne() * 10.0f + 15.0f; + effect->rot.z += Rand_ZeroOne() * 10.0f + 15.0f; + temp.x = effect->pos.x + effect->velocity.x; + temp.y = effect->pos.y + effect->velocity.y; + temp.z = effect->pos.z + effect->velocity.z; + Math_ApproachF(&effect->pos.x, temp.x, 0.3f, 30.0f); + Math_ApproachF(&effect->pos.y, temp.y, 0.8f, 250.0f); + Math_ApproachF(&effect->pos.z, temp.z, 0.3f, 30.0f); + Math_ApproachF(&effect->velocity.y, -20.0f, 0.9f, 1.0f); + if (effect->timer != 0) { + effect->timer--; + } else if (effect->scale < 10) { + effect->flag = false; + } else { + effect->scale -= 2; + } + } + } +} + +void EnGSwitch_DrawEffects(EnGSwitch* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + EnGSwitchEffect* effect = this->effects; + s16 i; + f32 scale; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_en_g_switch.c", 1073); + func_80093D18(globalCtx->state.gfxCtx); + for (i = 0; i < this->numEffects; i++, effect++) { + if (effect->flag) { + scale = effect->scale / 10000.0f; + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_RotateX(effect->rot.x, MTXMODE_APPLY); + Matrix_RotateY(effect->rot.y, MTXMODE_APPLY); + Matrix_RotateZ(effect->rot.z, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_g_switch.c", 1088), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sRupeeTextures[effect->colorIdx])); + gSPDisplayList(POLY_OPA_DISP++, gRupeeDL); + } + } + CLOSE_DISPS(gfxCtx, "../z_en_g_switch.c", 1095); +} diff --git a/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h b/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h new file mode 100644 index 000000000..dba5b09c0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_G_Switch/z_en_g_switch.h @@ -0,0 +1,60 @@ +#ifndef Z_EN_G_SWITCH_H +#define Z_EN_G_SWITCH_H + +#include "ultra64.h" +#include "global.h" + +struct EnGSwitch; + +typedef void (*EnGSwitchActionFunc)(struct EnGSwitch*, GlobalContext*); + +typedef enum { + /* 0 */ GSWITCH_NONE, + /* 1 */ GSWITCH_APPEAR, + /* 2 */ GSWITCH_THROW, + /* 3 */ GSWITCH_UNUSED, + /* 4 */ GSWITCH_LEFT, + /* 5 */ GSWITCH_RIGHT +} EnGSwitchMoveMode; + +typedef enum { + /* 0 */ ENGSWITCH_SILVER_TRACKER, + /* 1 */ ENGSWITCH_SILVER_RUPEE, + /* 2 */ ENGSWITCH_ARCHERY_POT, + /* 3 */ ENGSWITCH_TARGET_RUPEE +} EnGSwitchType; + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 scale; + /* 0x0E */ s16 timer; + /* 0x10 */ s16 colorIdx; + /* 0x12 */ u8 flag; + /* 0x14 */ Vec3f velocity; + /* 0x20 */ Vec3f rot; +} EnGSwitchEffect; // size = 0x2C + +typedef struct EnGSwitch { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnGSwitchActionFunc actionFunc; + /* 0x0150 */ s16 type; + /* 0x0152 */ s16 silverCount; + /* 0x0154 */ s16 switchFlag; + /* 0x0156 */ s16 killTimer; + /* 0x0158 */ s16 colorIdx; + /* 0x015A */ s16 broken; + /* 0x015C */ s16 numEffects; + /* 0x015E */ s16 objId; + /* 0x0160 */ s16 index; // first or second rupee in two-rupee patterns + /* 0x0162 */ s16 delayTimer; // delay between the two blue rupees appearing + /* 0x0164 */ s16 waitTimer; // time rupee waits before retreating + /* 0x0166 */ s16 moveMode; // Type of movement in the shooting gallery + /* 0x0168 */ s16 moveState; // Appear or retreat (for blue rupees and the stationary green one) + /* 0x016A */ s16 noteIndex; + /* 0x016C */ Vec3f targetPos; + /* 0x0178 */ s8 objIndex; + /* 0x017C */ ColliderCylinder collider; + /* 0x01C8 */ EnGSwitchEffect effects[100]; +} EnGSwitch; // size = 0x12F8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.c b/soh/src/overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.c new file mode 100644 index 000000000..5159b5ebb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.c @@ -0,0 +1,473 @@ +/* + * File: z_en_ganon_mant.c + * Overlay: ovl_En_Ganon_Mant + * Description: Ganondorf's Cape + */ + +#include "z_en_ganon_mant.h" +#include "overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnGanonMant_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGanonMant_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGanonMant_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGanonMant_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Ganon_Mant_InitVars = { + ACTOR_EN_GANON_MANT, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnGanonMant), + (ActorFunc)EnGanonMant_Init, + (ActorFunc)EnGanonMant_Destroy, + (ActorFunc)EnGanonMant_Update, + (ActorFunc)EnGanonMant_Draw, + NULL, +}; + +static s16 sTearSizesMedium[] = { + 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, +}; + +static s16 sTearSizesLarge[] = { + 0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, 0, +}; + +static s16 sTearSizesSmall[] = { + 0, 0, 0, 0, 0, 0, 0, +}; + +typedef struct { + s16* tearAreaSizes; + s16 count; +} TearShape; // size = 0x8 + +/** + * The arrays pointed to by this table describe how many pixels should + * be removed from the cloak texture in a single pass + */ +static TearShape sTearShapes[] = { + { sTearSizesMedium, ARRAY_COUNT(sTearSizesMedium) }, + { sTearSizesMedium, ARRAY_COUNT(sTearSizesMedium) }, + { sTearSizesLarge, ARRAY_COUNT(sTearSizesLarge) }, + { sTearSizesSmall, ARRAY_COUNT(sTearSizesSmall) }, +}; + +// How much each joint is affected by backwards/forwards swaying motion +static f32 sBackSwayCoefficients[GANON_MANT_NUM_JOINTS] = { + 0.0f, 1.0f, 0.5f, 0.25f, 0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, +}; + +static f32 D_80A24DB4[] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, +}; + +// How much each joint is affected by sideways swaying motion, tends to 0 +static f32 sSideSwayCoefficients[GANON_MANT_NUM_JOINTS] = { + 0.0f, 1.0f, 0.9f, 0.8f, 0.7f, 0.6f, 0.5f, 0.4f, 0.3f, 0.2f, 0.1f, 0.0f, +}; + +static f32 D_80A24E00[] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, +}; + +static f32 sDistMultipliers[GANON_MANT_NUM_JOINTS] = { + 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, +}; + +static f32 D_80A24E48[] = { + 1.8f, +}; + +#define MAP_STRAND_TO_VTX(n) \ + (n) + GANON_MANT_NUM_JOINTS * 0, (n) + GANON_MANT_NUM_JOINTS * 1, (n) + GANON_MANT_NUM_JOINTS * 2, \ + (n) + GANON_MANT_NUM_JOINTS * 3, (n) + GANON_MANT_NUM_JOINTS * 4, (n) + GANON_MANT_NUM_JOINTS * 5, \ + (n) + GANON_MANT_NUM_JOINTS * 6, (n) + GANON_MANT_NUM_JOINTS * 7, (n) + GANON_MANT_NUM_JOINTS * 8, \ + (n) + GANON_MANT_NUM_JOINTS * 9, (n) + GANON_MANT_NUM_JOINTS * 10, (n) + GANON_MANT_NUM_JOINTS * 11 + +static u16 sVerticesMap[GANON_MANT_NUM_STRANDS * GANON_MANT_NUM_JOINTS] = { + MAP_STRAND_TO_VTX(11), MAP_STRAND_TO_VTX(10), MAP_STRAND_TO_VTX(9), MAP_STRAND_TO_VTX(8), + MAP_STRAND_TO_VTX(7), MAP_STRAND_TO_VTX(6), MAP_STRAND_TO_VTX(5), MAP_STRAND_TO_VTX(4), + MAP_STRAND_TO_VTX(3), MAP_STRAND_TO_VTX(2), MAP_STRAND_TO_VTX(1), MAP_STRAND_TO_VTX(0), +}; + +#define MANT_TEX_WIDTH 32 +#define MANT_TEX_HEIGHT 64 + +//static u64 sForceAlignment = 0; + +#include "overlays/ovl_En_Ganon_Mant/ovl_En_Ganon_Mant.h" + +void EnGanonMant_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGanonMant* this = (EnGanonMant*)thisx; + + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void EnGanonMant_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +/** + * Randomly zeros portions of the cloak texture + */ +void EnGanonMant_Tear(EnGanonMant* this) { + s32 pad; + s16 i; + s16 areaX; + s16 areaY; + s16 texIdx; + f32 tx = Rand_ZeroFloat(MANT_TEX_WIDTH); + f32 ty = Rand_ZeroFloat(MANT_TEX_HEIGHT); + f32 tearAngle = Rand_ZeroFloat(2 * M_PI); + f32 tearDirX = sinf(tearAngle); + f32 tearDirY = cosf(tearAngle); + TearShape* shape = &sTearShapes[(s16)Rand_ZeroFloat(ARRAY_COUNT(sTearShapes) - 0.01f)]; + s16 count = shape->count; + s16* tearAreaSizes = shape->tearAreaSizes; + + u8* gMantTexProper = ResourceMgr_LoadTexByName(gMantTex); + + for (i = 0; i < count; i++) { + if ((0 <= tx && tx < MANT_TEX_WIDTH) && (0 <= ty && ty < MANT_TEX_HEIGHT)) { + for (areaX = 0; areaX <= tearAreaSizes[i]; areaX++) { + texIdx = 0; + if (1) {} + for (areaY = 0; areaY <= tearAreaSizes[i]; areaY++) { + texIdx = (s16)((s16)tx + ((s16)ty * MANT_TEX_WIDTH)) + ((s16)areaX + ((s16)areaY * MANT_TEX_WIDTH)); + if (texIdx < MANT_TEX_WIDTH * MANT_TEX_HEIGHT) { + ((u16*)gMantTexProper)[texIdx] = 0; + } + } + } + } + tx += tearDirX; + ty += tearDirY; + } + + for (i = 0; i < 4; i++) { + this->strands[(s16)Rand_ZeroFloat(GANON_MANT_NUM_STRANDS - 0.1f)] + .torn[(s16)Rand_ZeroFloat(GANON_MANT_NUM_JOINTS - 0.1f)] = true; + } +} + +/** + * Updates the dynamic strands that control the shape and motion of the cloak + */ +void EnGanonMant_UpdateStrand(GlobalContext* globalCtx, EnGanonMant* this, Vec3f* root, Vec3f* pos, Vec3f* nextPos, + Vec3f* rot, Vec3f* vel, s16 strandNum) { + f32 xDiff; + f32 zDiff; + f32 gravity; + s32 pad[4]; + f32 yaw; + s16 i; + f32 x; + f32 y; + f32 z; + f32 jointLength; + Vec3f delta; + Vec3f posStep; + Vec3f backSwayOffset; + Vec3f sideSwayOffset; + + delta.y = 0; + if (this->actor.params == 0x23) { + // Pushes all the strands away from the actor + delta.x = 0.0f; + delta.z = -30.0f; + Matrix_RotateY(BINANG_TO_RAD(this->actor.shape.rot.y), MTXMODE_NEW); + Matrix_MultVec3f(&delta, &posStep); + for (i = 0; i < GANON_MANT_NUM_JOINTS; i++) { + (pos + i)->x += posStep.x; + (pos + i)->z += posStep.z; + } + // Set length + jointLength = 6.5f; + } else { + jointLength = 9.5f; + } + + for (i = 0; i < GANON_MANT_NUM_JOINTS; i++, pos++, vel++, rot++, nextPos++) { + if (i == 0) { + // Constraint: first position is always root + pos->x = root->x; + pos->y = root->y; + pos->z = root->z; + } else { + // Decelerate + Math_ApproachZeroF(&vel->x, 1.0f, 0.1f); + Math_ApproachZeroF(&vel->y, 1.0f, 0.1f); + Math_ApproachZeroF(&vel->z, 1.0f, 0.1f); + + // Push the cloak away from attached actor, plus oscillations + delta.x = 0; + delta.z = (this->backPush + (sinf((strandNum * (2 * M_PI)) / 2.1f) * this->backSwayMagnitude)) * + sBackSwayCoefficients[i]; + Matrix_RotateY(this->baseYaw, MTXMODE_NEW); + Matrix_MultVec3f(&delta, &backSwayOffset); + + // Push the cloak out to either side, in a swaying manner + delta.x = cosf((strandNum * M_PI) / (GANON_MANT_NUM_STRANDS - 1.0f)) * this->sideSwayMagnitude * + sSideSwayCoefficients[i]; + delta.z = 0; + Matrix_MultVec3f(&delta, &sideSwayOffset); + + // Calculate position difference + gravity = this->gravity; + x = ((pos->x + vel->x) - (pos - 1)->x) + (backSwayOffset.x + sideSwayOffset.x); + y = ((pos->y + vel->y) - (pos - 1)->y) + gravity; + z = ((pos->z + vel->z) - (pos - 1)->z) + (backSwayOffset.z + sideSwayOffset.z); + + // Calculate rotations in the direction of the position difference + yaw = Math_Atan2F(z, x); + x = -Math_Atan2F(sqrtf(SQ(x) + SQ(z)), y); + (rot - 1)->x = x; + + // Calculate real position difference of correct length in the correct direction + delta.x = 0; + delta.z = jointLength; + Matrix_RotateY(yaw, MTXMODE_NEW); + Matrix_RotateX(x, MTXMODE_APPLY); + Matrix_MultVec3f(&delta, &posStep); + + // Save position + x = pos->x; + y = pos->y; + z = pos->z; + + // Calculate next position + pos->x = (pos - 1)->x + posStep.x; + pos->y = (pos - 1)->y + posStep.y; + pos->z = (pos - 1)->z + posStep.z; + + // Pushes the cloak away from the actor if it is too close + xDiff = pos->x - this->actor.world.pos.x; + zDiff = pos->z - this->actor.world.pos.z; + if (sqrtf(SQ(xDiff) + SQ(zDiff)) < (sDistMultipliers[i] * this->minDist)) { + yaw = Math_Atan2F(zDiff, xDiff); + delta.z = this->minDist * sDistMultipliers[i]; + Matrix_RotateY(yaw, MTXMODE_NEW); + Matrix_MultVec3f(&delta, &posStep); + pos->x = this->actor.world.pos.x + posStep.x; + pos->z = this->actor.world.pos.z + posStep.z; + } + + // Enforces minY constraint + if (pos->y < this->minY) { + pos->y = this->minY; + } + + // Calculate next velocity + vel->x = (pos->x - x) * 80.0f / 100.0f; + vel->y = (pos->y - y) * 80.0f / 100.0f; + vel->z = (pos->z - z) * 80.0f / 100.0f; + + if (this->actor.params != 0x23) { + // Clamp elements of vel into [-5.0, 5.0] + if (vel->x > 5.0f) { + vel->x = 5.0f; + } else if (vel->x < -5.0f) { + vel->x = -5.0f; + } + if (vel->y > 5.0f) { + vel->y = 5.0f; + } else if (vel->y < -5.0f) { + vel->y = -5.0f; + } + if (vel->z > 5.0f) { + vel->z = 5.0f; + } else if (vel->z < -5.0f) { + vel->z = -5.0f; + } + } + + // update angle + xDiff = pos->x - nextPos->x; + zDiff = pos->z - nextPos->z; + (rot - 1)->y = Math_Atan2F(zDiff, xDiff); + } + } + rot[11].y = rot[10].y; + rot[11].x = rot[10].x; +} + +/** + * Update the cloak vertices using the current state of the strands + */ +void EnGanonMant_UpdateVertices(EnGanonMant* this) { + s16 i; + Vtx* vtx; + Vtx* vertices; + MantStrand* strand; + s16 j; + s16 k; + Vec3f up; + Vec3f normal; + + if (this->frameTimer % 2 != 0) { + vertices = SEGMENTED_TO_VIRTUAL(gMant1Vtx); + } else { + vertices = SEGMENTED_TO_VIRTUAL(gMant2Vtx); + } + + vertices = ResourceMgr_LoadVtxByName(vertices); + + up.x = 0.0f; + up.y = 30.0f; + up.z = 0.0f; + + strand = &this->strands[0]; + for (i = 0; i < GANON_MANT_NUM_STRANDS; i++, strand++) { + for (j = 0, k = 0; j < GANON_MANT_NUM_JOINTS; j++, k += GANON_MANT_NUM_JOINTS) { + vtx = &vertices[sVerticesMap[i + k]]; + vtx->n.ob[0] = strand->joints[j].x; + vtx->n.ob[1] = strand->joints[j].y; + vtx->n.ob[2] = strand->joints[j].z; + Matrix_RotateY(strand->rotations[j].y, MTXMODE_NEW); + Matrix_RotateX(strand->rotations[j].x, MTXMODE_APPLY); + Matrix_MultVec3f(&up, &normal); + vtx->n.n[0] = normal.x; + vtx->n.n[1] = normal.y; + vtx->n.n[2] = normal.z; + } + } +} + +void EnGanonMant_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGanonMant* this = (EnGanonMant*)thisx; + BossGanon* ganon = (BossGanon*)this->actor.parent; + + this->updateHasRun = true; + this->frameTimer++; + + if (this->attachLeftArmTimer == 0.0f) { + } else { + this->attachLeftArmTimer -= 1.0f; + } + if (this->attachRightArmTimer != 0.0f) { + this->attachRightArmTimer -= 1.0f; + } + if (this->attachShouldersTimer != 0.0f) { + this->attachShouldersTimer -= 1.0f; + } + + this->actor.shape.rot.y = ganon->actor.shape.rot.y; + + if (this->tearTimer != 0) { + this->tearTimer--; + EnGanonMant_Tear(this); + } +} + +void EnGanonMant_DrawCloak(GlobalContext* globalCtx, EnGanonMant* this) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ganon_mant.c", 564); + + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_NEW); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ganon_mant.c", 572), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + // set texture + gSPDisplayList(POLY_OPA_DISP++, gMantMaterialDL); + + // set vertices, vertices are double buffered to prevent + // modification of vertices as they are being drawn + if (this->frameTimer % 2 != 0) { + gSPSegment(POLY_OPA_DISP++, 0x0C, gMant1Vtx); + } else { + gSPSegment(POLY_OPA_DISP++, 0x0C, gMant2Vtx); + } + + // draw cloak + gSPDisplayList(POLY_OPA_DISP++, gMantDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ganon_mant.c", 584); +} + +void EnGanonMant_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGanonMant* this = (EnGanonMant*)thisx; + f32 xDiff; + f32 pitch; + Vec3f strandOffset; + Vec3f strandDivPos; + f32 zDiff; + f32 diffHalfDist; + f32 yDiff; + f32 yaw; + Vec3f* rightPos; + Vec3f* leftPos; + s16 strandIdx; + Vec3f midpoint; + s16 nextStrandIdx; + + if (this->updateHasRun) { + // Only run this if update has run since last draw + + // Choose endpoints + if (this->attachRightArmTimer != 0.0f) { + rightPos = &this->rightForearmPos; + leftPos = &this->leftShoulderPos; + this->gravity = -13.0f; + } else if (this->attachLeftArmTimer != 0.0f) { + rightPos = &this->rightShoulderPos; + leftPos = &this->leftForearmPos; + this->gravity = -13.0f; + } else if (this->attachShouldersTimer != 0.0f) { + rightPos = &this->rightShoulderPos; + leftPos = &this->leftShoulderPos; + this->gravity = -3.0f; + } else { + rightPos = &this->rightForearmPos; + leftPos = &this->leftForearmPos; + } + + xDiff = leftPos->x - rightPos->x; + yDiff = leftPos->y - rightPos->y; + zDiff = leftPos->z - rightPos->z; + + midpoint.x = rightPos->x + xDiff * 0.5f; + midpoint.y = rightPos->y + yDiff * 0.5f; + midpoint.z = rightPos->z + zDiff * 0.5f; + + // Calculte base orientation for chosen endpoints + yaw = Math_Atan2F(zDiff, xDiff); + pitch = -Math_Atan2F(sqrtf(SQ(xDiff) + SQ(zDiff)), yDiff); + diffHalfDist = sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)) * 0.5f; + + Matrix_RotateY(yaw, MTXMODE_NEW); + Matrix_RotateX(pitch, MTXMODE_APPLY); + this->baseYaw = yaw - M_PI / 2.0f; + + for (strandIdx = 0; strandIdx < GANON_MANT_NUM_STRANDS; strandIdx++) { + Matrix_Push(); + + // Calculate root positions for chosen endpoints + strandOffset.x = sinf((strandIdx * M_PI) / (GANON_MANT_NUM_STRANDS - 1)) * diffHalfDist; + strandOffset.y = 0; + strandOffset.z = -cosf((strandIdx * M_PI) / (GANON_MANT_NUM_STRANDS - 1)) * diffHalfDist; + Matrix_MultVec3f(&strandOffset, &strandDivPos); + this->strands[strandIdx].root.x = midpoint.x + strandDivPos.x; + this->strands[strandIdx].root.y = midpoint.y + strandDivPos.y; + this->strands[strandIdx].root.z = midpoint.z + strandDivPos.z; + + nextStrandIdx = strandIdx + 1; + if (nextStrandIdx >= GANON_MANT_NUM_STRANDS) { + nextStrandIdx = strandIdx - 1; + } + + // Update the strand joints + EnGanonMant_UpdateStrand(globalCtx, this, &this->strands[strandIdx].root, this->strands[strandIdx].joints, + this->strands[nextStrandIdx].joints, this->strands[strandIdx].rotations, + this->strands[strandIdx].velocities, strandIdx); + Matrix_Pop(); + } + EnGanonMant_UpdateVertices(this); + this->updateHasRun = false; + } + + EnGanonMant_DrawCloak(globalCtx, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.h b/soh/src/overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.h new file mode 100644 index 000000000..3b416e59e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.h @@ -0,0 +1,42 @@ +#ifndef Z_EN_GANON_MANT_H +#define Z_EN_GANON_MANT_H + +#include "ultra64.h" +#include "global.h" + +struct EnGanonMant; + +#define GANON_MANT_NUM_JOINTS 12 +#define GANON_MANT_NUM_STRANDS 12 + +typedef struct { + /* 0x000 */ Vec3f root; // root position along the collar + /* 0x00C */ Vec3f joints[GANON_MANT_NUM_JOINTS]; // "joints" for deforming the cloak, stemming from root and propagating down the cloak + /* 0x090 */ Vec3f rotations[GANON_MANT_NUM_JOINTS]; // normal vector rotations, x and y only + /* 0x120 */ Vec3f velocities[GANON_MANT_NUM_JOINTS]; // velocities calculated as differences in position from last update + /* 0x1B0 */ u8 torn[GANON_MANT_NUM_JOINTS]; // guess: whether the joint is torn? +} MantStrand; // size = 0x1C8 + +typedef struct EnGanonMant { + /* 0x0000 */ Actor actor; + /* 0x014C */ MantStrand strands[GANON_MANT_NUM_STRANDS]; + /* 0x16AC */ f32 minY; // minimum Y value possible for joints, for emulating collision with a floor + /* 0x16B0 */ f32 backPush; // larger values push the cloak further outwards, negative is away from the actor + /* 0x16B4 */ f32 backSwayMagnitude; // magnitude of backwards/forwards swaying + /* 0x16B8 */ f32 sideSwayMagnitude; // magnitude of sideways swaying + /* 0x16BC */ f32 attachRightArmTimer; // timer for the duration of which the cloak is attached to right forearm and left shoulder + /* 0x16C0 */ f32 attachLeftArmTimer; // timer for the duration of which the cloak is attached to left forearm and right shoulder + /* 0x16C4 */ f32 attachShouldersTimer; // timer for the duration of which the cloak is attached to both shoulders + /* 0x16C8 */ f32 gravity; // strand gravity + /* 0x16CC */ f32 baseYaw; + /* 0x16D0 */ f32 minDist; // closest distance the cloak can get to the attached actor without being pushed away + /* 0x16D4 */ Vec3f rightForearmPos; + /* 0x16E0 */ Vec3f leftForearmPos; + /* 0x16EC */ Vec3f rightShoulderPos; + /* 0x16F8 */ Vec3f leftShoulderPos; + /* 0x1704 */ u8 tearTimer; // tear the cloak for x many frames + /* 0x1705 */ u8 updateHasRun; + /* 0x1706 */ u8 frameTimer; +} EnGanonMant; // size = 0x1708 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ganon_Organ/z_en_ganon_organ.c b/soh/src/overlays/actors/ovl_En_Ganon_Organ/z_en_ganon_organ.c new file mode 100644 index 000000000..fa5ae3ece --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ganon_Organ/z_en_ganon_organ.c @@ -0,0 +1,117 @@ +/* + * File: z_en_ganon_organ.c + * Overlay: ovl_En_Ganon_Organ + * Description: The organ that Ganondorf plays in the cutscene before the fight. Includes carpet and scenery as well. + */ + +#include "z_en_ganon_organ.h" +#include "overlays/actors/ovl_Boss_Ganon/z_boss_ganon.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnGanonOrgan_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGanonOrgan_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGanonOrgan_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGanonOrgan_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Ganon_Organ_InitVars = { + ACTOR_EN_GANON_ORGAN, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GANON, + sizeof(EnGanonOrgan), + (ActorFunc)EnGanonOrgan_Init, + (ActorFunc)EnGanonOrgan_Destroy, + (ActorFunc)EnGanonOrgan_Update, + (ActorFunc)EnGanonOrgan_Draw, + NULL, +}; + +//static u64 sForceAlignment = 0; + +#include "overlays/ovl_En_Ganon_Organ/ovl_En_Ganon_Organ.h" + +void EnGanonOrgan_Init(Actor* thisx, GlobalContext* globalCtx) { + thisx->flags &= ~ACTOR_FLAG_0; +} + +void EnGanonOrgan_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnGanonOrgan_Update(Actor* thisx, GlobalContext* globalCtx) { + BossGanon* dorf; + + osSyncPrintf("ORGAN MOVE 1\n"); + if (thisx->params == 1) { + dorf = (BossGanon*)thisx->parent; + if (dorf->organAlpha == 0) { + Actor_Kill(thisx); + } + } + osSyncPrintf("ORGAN MOVE 2\n"); +} + +Gfx* EnGanonOrgan_EmptyDList(GraphicsContext* gfxCtx) { + Gfx* displayList; + + displayList = Graph_Alloc(gfxCtx, sizeof(Gfx)); + gSPEndDisplayList(displayList); + return displayList; +} + +Gfx* func_80A280BC(GraphicsContext* gfxCtx, BossGanon* dorf) { + Gfx* displayList; + Gfx* displayListHead; + + displayList = Graph_Alloc(gfxCtx, 4 * sizeof(Gfx)); + displayListHead = displayList; + gDPPipeSync(displayListHead++); + if (1) {} + if (1) {} + gDPSetEnvColor(displayListHead++, 25, 20, 0, dorf->organAlpha); + gDPSetRenderMode(displayListHead++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2); + gSPEndDisplayList(displayListHead); + return displayList; +} + +Gfx* func_80A28148(GraphicsContext* gfxCtx, BossGanon* dorf) { + Gfx* displayList; + Gfx* displayListHead; + + displayList = Graph_Alloc(gfxCtx, 4 * sizeof(Gfx)); + displayListHead = displayList; + + gDPPipeSync(displayListHead++); + if (1) {} + if (1) {} + gDPSetEnvColor(displayListHead++, 0, 0, 0, dorf->organAlpha); + gDPSetRenderMode(displayListHead++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2); + gSPEndDisplayList(displayListHead); + return displayList; +} + +void EnGanonOrgan_Draw(Actor* thisx, GlobalContext* globalCtx) { + BossGanon* dorf = (BossGanon*)thisx->parent; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ganon_organ.c", 205); + + osSyncPrintf("ORGAN DRAW 1\n"); + func_80093D18(globalCtx->state.gfxCtx); + if ((thisx->params == 1) && (dorf->organAlpha != 255)) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A280BC(globalCtx->state.gfxCtx, dorf)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A28148(globalCtx->state.gfxCtx, dorf)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, EnGanonOrgan_EmptyDList(globalCtx->state.gfxCtx)); + gSPSegment(POLY_OPA_DISP++, 0x09, EnGanonOrgan_EmptyDList(globalCtx->state.gfxCtx)); + } + Matrix_Translate(0.0f, 0.0f, 0.0f, MTXMODE_NEW); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ganon_organ.c", 221), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, sRoomOrganAndFloorDL); + gSPDisplayList(POLY_OPA_DISP++, sRoomStatuesDL); + + osSyncPrintf("ORGAN DRAW 2\n"); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ganon_organ.c", 230); +} diff --git a/soh/src/overlays/actors/ovl_En_Ganon_Organ/z_en_ganon_organ.h b/soh/src/overlays/actors/ovl_En_Ganon_Organ/z_en_ganon_organ.h new file mode 100644 index 000000000..0c301bddc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ganon_Organ/z_en_ganon_organ.h @@ -0,0 +1,14 @@ +#ifndef Z_EN_GANON_ORGAN_H +#define Z_EN_GANON_ORGAN_H + +#include "ultra64.h" +#include "global.h" + +struct EnGanonOrgan; + +typedef struct EnGanonOrgan { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[0x4]; +} EnGanonOrgan; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c new file mode 100644 index 000000000..d1ef4e9a8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.c @@ -0,0 +1,554 @@ +/* + * File: z_en_gb.c + * Overlay: ovl_En_Gb + * Description: Poe Seller + */ + +#include "z_en_gb.h" +#include "objects/object_ps/object_ps.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnGb_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGb_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGb_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A2F83C(EnGb* this, GlobalContext* globalCtx); +void func_80A2FC70(EnGb* this, GlobalContext* globalCtx); +void func_80A2FA50(EnGb* this, GlobalContext* globalCtx); +void func_80A2F9C0(EnGb* this, GlobalContext* globalCtx); +void func_80A2F94C(EnGb* this, GlobalContext* globalCtx); +void func_80A2FB40(EnGb* this, GlobalContext* globalCtx); +void func_80A2FBB0(EnGb* this, GlobalContext* globalCtx); +void func_80A2FC0C(EnGb* this, GlobalContext* globalCtx); + +void EnGb_DrawCagedSouls(EnGb* this, GlobalContext* globalCtx); +void EnGb_UpdateCagedSouls(EnGb* this, GlobalContext* globalCtx); + +const ActorInit En_Gb_InitVars = { + ACTOR_EN_GB, + ACTORCAT_NPC, + FLAGS, + OBJECT_PS, + sizeof(EnGb), + (ActorFunc)EnGb_Init, + (ActorFunc)EnGb_Destroy, + (ActorFunc)EnGb_Update, + (ActorFunc)EnGb_Draw, + NULL, +}; + +static EnGbCagedSoulInfo sCagedSoulInfo[] = { + { { 255, 255, 170, 255 }, { 255, 200, 0, 255 }, gPoeSellerAngrySoulTex, -15 }, + { { 255, 255, 170, 255 }, { 0, 150, 0, 255 }, gPoeSellerHappySoulTex, -12 }, + { { 255, 170, 255, 255 }, { 100, 0, 150, 255 }, gPoeSellerSadSoulTex, -8 }, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 40, 75, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInitType1 sBottlesCylindersInit[] = { + { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 4, 20, 0, { 0, 0, 0 } }, + }, + { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 4, 20, 0, { 0, 0, 0 } }, + }, + { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 10, 20, 0, { 0, 0, 0 } }, + }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2200, ICHAIN_STOP), +}; + +// relative positions of poe souls +static Vec3f sCagedSoulPositions[] = { + { -8.0f, 112.0f, -8.0f }, + { -3.0f, 112.0f, 29.0f }, + { 31.0f, 112.0f, 29.0f }, + { 31.0f, 112.0f, -8.0f }, +}; + +// relative positions of bottles +static Vec3f sBottlesPositions[] = { + { -48.0f, 0.0f, 34.0f }, + { -55.0f, 0.0f, 49.0f }, + { -48.0f, 0.0f, 60.0f }, +}; + +void func_80A2F180(EnGb* this) { + if (gSaveContext.infTable[0xB] & 0x40) { + this->textId = 0x70F5; + } else { + this->textId = 0x70F4; + } +} + +void EnGb_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGb* this = (EnGb*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + s32 i; + f32 rand; + Vec3f focusOffset; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gPoeSellerCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gPoeSellerSkel, &gPoeSellerIdleAnim, this->jointTable, + this->morphTable, 12); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + + for (i = 0; i < ARRAY_COUNT(sBottlesCylindersInit); i++) { + Collider_InitCylinder(globalCtx, &this->bottlesColliders[i]); + Collider_SetCylinderType1(globalCtx, &this->bottlesColliders[i], &this->dyna.actor, &sBottlesCylindersInit[i]); + } + + this->light = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->dyna.actor.home.pos.x, this->dyna.actor.home.pos.y, + this->dyna.actor.home.pos.z, 255, 255, 255, 200); + + ActorShape_Init(&this->dyna.actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f); + Actor_SetScale(&this->dyna.actor, 0.01f); + this->dyna.actor.colChkInfo.mass = 0xFF; + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.gravity = -1.0f; + this->actionTimer = (s16)Rand_ZeroFloat(100.0f) + 100; + + for (i = 0; i < ARRAY_COUNT(sCagedSoulPositions); i++) { + this->cagedSouls[i].infoIdx = (s32)Rand_ZeroFloat(30.0f) % 3; + this->cagedSouls[i].unk_14.x = this->cagedSouls[i].translation.x = + sCagedSoulPositions[i].x + this->dyna.actor.world.pos.x; + this->cagedSouls[i].unk_14.y = this->cagedSouls[i].translation.y = + sCagedSoulPositions[i].y + this->dyna.actor.world.pos.y; + this->cagedSouls[i].unk_14.z = this->cagedSouls[i].translation.z = + sCagedSoulPositions[i].z + this->dyna.actor.world.pos.z; + this->cagedSouls[i].unk_1 = 1; + this->cagedSouls[i].unk_3 = this->cagedSouls[i].unk_2 = 0; + this->cagedSouls[i].unk_20 = this->cagedSouls[i].unk_24 = 0.0f; + this->cagedSouls[i].unk_6 = Rand_ZeroFloat(40.0f); + this->cagedSouls[i].rotate180 = this->cagedSouls[i].unk_6 & 1; + this->cagedSouls[i].unk_28 = 0.2f; + } + + rand = Rand_ZeroOne(); + this->lightColor.r = (s8)(rand * 30.0f) + 225; + this->lightColor.g = (s8)(rand * 100.0f) + 155; + this->lightColor.b = (s8)(rand * 160.0f) + 95; + this->lightColor.a = 200; + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateZYX(this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, this->dyna.actor.world.rot.z, + MTXMODE_APPLY); + focusOffset.x = focusOffset.y = 0.0f; + focusOffset.z = 44.0f; + Matrix_MultVec3f(&focusOffset, &this->dyna.actor.focus.pos); + this->dyna.actor.focus.pos.y += 62.5f; + func_80A2F180(this); + this->actionFunc = func_80A2F83C; +} + +void EnGb_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGb* this = (EnGb*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->light); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80A2F608(EnGb* this) { + s32 i; + Vec3f sp48; + Vec3f sp3C; + + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateZYX(this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, this->dyna.actor.world.rot.z, + MTXMODE_APPLY); + sp48.x = sp48.y = 0.0f; + sp48.z = 25.0f; + Matrix_MultVec3f(&sp48, &sp3C); + this->collider.dim.pos.x = sp3C.x; + this->collider.dim.pos.y = sp3C.y; + this->collider.dim.pos.z = sp3C.z; + + for (i = 0; i < ARRAY_COUNT(sBottlesPositions); i++) { + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateZYX(this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, this->dyna.actor.world.rot.z, + MTXMODE_APPLY); + Matrix_MultVec3f(&sBottlesPositions[i], &sp3C); + this->bottlesColliders[i].dim.pos.x = sp3C.x; + this->bottlesColliders[i].dim.pos.y = sp3C.y; + this->bottlesColliders[i].dim.pos.z = sp3C.z; + } +} + +s32 func_80A2F760(EnGb* this) { + s32 i; + for (i = 0; i < ARRAY_COUNT(this->cagedSouls); i++) { + if (this->cagedSouls[i].unk_3) { + return 1; + } + } + return 0; +} + +void func_80A2F7C0(EnGb* this) { + Animation_Change(&this->skelAnime, &gPoeSellerSwingStickAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gPoeSellerSwingStickAnim), ANIMMODE_ONCE, 0.0f); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_NALE_MAGIC); + this->actionFunc = func_80A2FC70; +} + +void func_80A2F83C(EnGb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (!func_80A2F760(this)) { + if (this->actionTimer != 0) { + this->actionTimer--; + } else { + func_80A2F7C0(this); + return; + } + } + if (Actor_ProcessTalkRequest(&this->dyna.actor, globalCtx)) { + switch (func_8002F368(globalCtx)) { + case EXCH_ITEM_NONE: + func_80A2F180(this); + this->actionFunc = func_80A2F94C; + break; + case EXCH_ITEM_POE: + player->actor.textId = 0x70F6; + this->actionFunc = func_80A2F9C0; + break; + case EXCH_ITEM_BIG_POE: + player->actor.textId = 0x70F7; + this->actionFunc = func_80A2FA50; + break; + } + return; + } + if (this->dyna.actor.xzDistToPlayer < 100.0f) { + func_8002F298(&this->dyna.actor, globalCtx, 100.0f, EXCH_ITEM_POE); + } +} + +void func_80A2F94C(EnGb* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + if (!(gSaveContext.infTable[0xB] & 0x40)) { + gSaveContext.infTable[0xB] |= 0x40; + } + func_80A2F180(this); + this->actionFunc = func_80A2F83C; + } +} + +void func_80A2F9C0(EnGb* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + if (!(gSaveContext.infTable[0xB] & 0x40)) { + gSaveContext.infTable[0xB] |= 0x40; + } + func_80A2F180(this); + Player_UpdateBottleHeld(globalCtx, GET_PLAYER(globalCtx), ITEM_BOTTLE, PLAYER_AP_BOTTLE); + Rupees_ChangeBy(10); + this->actionFunc = func_80A2F83C; + } +} + +void func_80A2FA50(EnGb* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + if (!(gSaveContext.infTable[0xB] & 0x40)) { + gSaveContext.infTable[0xB] |= 0x40; + } + func_80A2F180(this); + Player_UpdateBottleHeld(globalCtx, GET_PLAYER(globalCtx), ITEM_BOTTLE, PLAYER_AP_BOTTLE); + Rupees_ChangeBy(50); + HIGH_SCORE(HS_POE_POINTS) += 100; + if (HIGH_SCORE(HS_POE_POINTS) != 1000) { + if (HIGH_SCORE(HS_POE_POINTS) > 1100) { + HIGH_SCORE(HS_POE_POINTS) = 1100; + } + this->actionFunc = func_80A2F83C; + } else { + Player* player = GET_PLAYER(globalCtx); + + player->exchangeItemId = EXCH_ITEM_NONE; + this->textId = 0x70F8; + Message_ContinueTextbox(globalCtx, this->textId); + this->actionFunc = func_80A2FB40; + } + } +} + +void func_80A2FB40(EnGb* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + func_8002F434(&this->dyna.actor, globalCtx, GI_BOTTLE, 100.0f, 10.0f); + this->actionFunc = func_80A2FBB0; + } +} + +void func_80A2FBB0(EnGb* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->dyna.actor, globalCtx)) { + this->dyna.actor.parent = NULL; + this->actionFunc = func_80A2FC0C; + } else { + func_8002F434(&this->dyna.actor, globalCtx, GI_BOTTLE, 100.0f, 10.0f); + } +} + +void func_80A2FC0C(EnGb* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + Actor_ProcessTalkRequest(&this->dyna.actor, globalCtx); + func_80A2F180(this); + this->actionFunc = func_80A2F83C; + } +} + +void func_80A2FC70(EnGb* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame == Animation_GetLastFrame(&gPoeSellerSwingStickAnim)) { + Animation_Change(&this->skelAnime, &gPoeSellerIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gPoeSellerIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->actionFunc = func_80A2F83C; + } else if (this->skelAnime.curFrame == 18.0f) { + this->cagedSouls[1].unk_1 = 3; + this->cagedSouls[1].unk_3 = 1; + this->cagedSouls[2].unk_1 = 3; + this->cagedSouls[2].unk_3 = 1; + this->cagedSouls[3].unk_1 = 3; + this->cagedSouls[3].unk_3 = 1; + if (this->actionFunc) {} // these ifs cannot just contain a constant + this->cagedSouls[0].unk_1 = 3; + this->cagedSouls[0].unk_3 = 1; + if (this->actionFunc) {} + this->actionTimer = (s16)Rand_ZeroFloat(600.0f) + 600; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_WOOD_HIT); + } +} + +void EnGb_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnGb* this = (EnGb*)thisx; + GlobalContext* globalCtx = globalCtx2; + s32 i; + f32 rand; + + this->frameTimer++; + SkelAnime_Update(&this->skelAnime); + this->actionFunc(this, globalCtx); + this->dyna.actor.textId = this->textId; + func_80A2F608(this); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + for (i = 0; i < ARRAY_COUNT(this->bottlesColliders); i++) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bottlesColliders[i].base); + } + + rand = Rand_ZeroOne(); + this->lightColor.r = (s8)(rand * 30.0f) + 225; + this->lightColor.g = (s8)(rand * 100.0f) + 155; + this->lightColor.b = (s8)(rand * 160.0f) + 95; + this->lightColor.a = 200; + EnGb_UpdateCagedSouls(this, globalCtx); +} + +void EnGb_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGb* this = (EnGb*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_gb.c", 763); + + func_80093D18(globalCtx->state.gfxCtx); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, 255); + + Lights_PointNoGlowSetInfo(&this->lightInfo, this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, + this->dyna.actor.world.pos.z, this->lightColor.r, this->lightColor.g, this->lightColor.b, + this->lightColor.a); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, &this->dyna.actor); + EnGb_DrawCagedSouls(this, globalCtx); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_gb.c", 796); +} + +void EnGb_UpdateCagedSouls(EnGb* this, GlobalContext* globalCtx) { + f32 temp_f20; + s16 rot; + s32 i; + + for (i = 0; i < 4; i++) { + switch (this->cagedSouls[i].unk_1) { + case 0: + Math_ApproachF(&this->cagedSouls[i].unk_20, 1.0f, 0.02f, this->cagedSouls[i].unk_24); + Math_ApproachF(&this->cagedSouls[i].unk_24, 1.0f, 0.001f, 1.0f); + if ((this->cagedSouls[i].unk_28 - .01f) <= this->cagedSouls[i].unk_20) { + this->cagedSouls[i].unk_20 = this->cagedSouls[i].unk_28; + this->cagedSouls[i].unk_24 = 0.0f; + this->cagedSouls[i].unk_1 = 1; + this->cagedSouls[i].unk_2 = 2; + this->cagedSouls[i].unk_6 = 0; + } + break; + case 1: + if (this->cagedSouls[i].unk_6 != 0) { + this->cagedSouls[i].unk_6--; + } else { + this->cagedSouls[i].unk_3 = 0; + this->cagedSouls[i].unk_24 = 0.0f; + this->cagedSouls[i].unk_1 = this->cagedSouls[i].unk_2; + } + break; + case 2: + Math_ApproachF(&this->cagedSouls[i].unk_20, 0.0f, 0.02f, this->cagedSouls[i].unk_24); + Math_ApproachF(&this->cagedSouls[i].unk_24, 1.0f, 0.001f, 1.0f); + if (this->cagedSouls[i].unk_20 <= 0.01f) { + this->cagedSouls[i].unk_28 = this->cagedSouls[i].unk_28 + 0.2f; + if (this->cagedSouls[i].unk_28 > 1.0f) { + this->cagedSouls[i].unk_28 = 1.0f; + } + this->cagedSouls[i].unk_20 = 0.0f; + this->cagedSouls[i].unk_24 = 0.0f; + this->cagedSouls[i].unk_1 = 1; + this->cagedSouls[i].unk_2 = 0; + this->cagedSouls[i].unk_6 = 0; + } + break; + case 3: + Math_ApproachF(&this->cagedSouls[i].unk_20, 0.0f, 0.5f, 1.0f); + if (this->cagedSouls[i].unk_20 <= 0.01f) { + this->cagedSouls[i].unk_28 = 0.2f; + this->cagedSouls[i].unk_20 = 0.0f; + this->cagedSouls[i].unk_24 = 0.0f; + this->cagedSouls[i].unk_1 = 1; + this->cagedSouls[i].unk_2 = 0; + this->cagedSouls[i].unk_6 = (s16)Rand_ZeroFloat(60.0f) + 60; + } + break; + } + + temp_f20 = this->cagedSouls[i].unk_20 * 60.0f; + if ((i == 0) || (i == 3)) { + this->cagedSouls[i].translation.x = this->cagedSouls[i].unk_14.x; + this->cagedSouls[i].translation.y = this->cagedSouls[i].unk_14.y + temp_f20; + this->cagedSouls[i].translation.z = this->cagedSouls[i].unk_14.z; + } else if (i == 1) { + rot = this->dyna.actor.world.rot.y - 0x4000; + this->cagedSouls[i].translation.x = this->cagedSouls[i].unk_14.x + Math_SinS(rot) * temp_f20; + this->cagedSouls[i].translation.z = this->cagedSouls[i].unk_14.z + Math_CosS(rot) * temp_f20; + this->cagedSouls[i].translation.y = this->cagedSouls[i].unk_14.y; + } else { + rot = this->dyna.actor.world.rot.y + 0x4000; + this->cagedSouls[i].translation.x = this->cagedSouls[i].unk_14.x + Math_SinS(rot) * temp_f20; + this->cagedSouls[i].translation.z = this->cagedSouls[i].unk_14.z + Math_CosS(rot) * temp_f20; + this->cagedSouls[i].translation.y = this->cagedSouls[i].unk_14.y; + } + } +} + +void EnGb_DrawCagedSouls(EnGb* this, GlobalContext* globalCtx) { + s32 pad; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_gb.c", 914); + + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < 4; i++) { + s32 idx = this->cagedSouls[i].infoIdx; + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + (u32)(sCagedSoulInfo[idx].timerMultiplier * this->frameTimer) % 512, 32, 128)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sCagedSoulInfo[idx].texture)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, sCagedSoulInfo[idx].prim.r, sCagedSoulInfo[idx].prim.g, + sCagedSoulInfo[idx].prim.b, sCagedSoulInfo[idx].prim.a); + gDPSetEnvColor(POLY_XLU_DISP++, sCagedSoulInfo[idx].env.r, sCagedSoulInfo[idx].env.g, sCagedSoulInfo[idx].env.b, + sCagedSoulInfo[idx].env.a); + + Matrix_Push(); + Matrix_Translate(this->cagedSouls[i].translation.x, this->cagedSouls[i].translation.y, + this->cagedSouls[i].translation.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + if (this->cagedSouls[i].rotate180) { + Matrix_RotateZYX(0, -0x8000, 0, MTXMODE_APPLY); + } + Matrix_Scale(0.007f, 0.007f, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_gb.c", 955), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gPoeSellerCagedSoulDL); + + Matrix_Pop(); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_gb.c", 962); +} diff --git a/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h new file mode 100644 index 000000000..5de160ff2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Gb/z_en_gb.h @@ -0,0 +1,49 @@ +#ifndef Z_EN_GB_H +#define Z_EN_GB_H + +#include "ultra64.h" +#include "global.h" + +struct EnGb; + +typedef void (*EnGbActionFunc)(struct EnGb*, GlobalContext*); + +typedef struct { + /* 0x00 */ Color_RGBA8 prim; + /* 0x04 */ Color_RGBA8 env; + /* 0x08 */ void* texture; + /* 0x0C */ s16 timerMultiplier; +} EnGbCagedSoulInfo; // size = 0x10 + +typedef struct { + /* 0x00 */ u8 infoIdx; + /* 0x01 */ u8 unk_1; + /* 0x02 */ u8 unk_2; + /* 0x03 */ u8 unk_3; + /* 0x04 */ u8 rotate180; + /* 0x06 */ s16 unk_6; + /* 0x08 */ Vec3f translation; + /* 0x14 */ Vec3f unk_14; + /* 0x20 */ f32 unk_20; + /* 0x24 */ f32 unk_24; + /* 0x28 */ f32 unk_28; +} EnGbCagedSoul; // size = 0x2C + +typedef struct EnGb { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ SkelAnime skelAnime; + /* 0x01A8 */ Vec3s jointTable[12]; + /* 0x01F0 */ Vec3s morphTable[12]; + /* 0x0238 */ EnGbActionFunc actionFunc; + /* 0x023C */ ColliderCylinder collider; + /* 0x0288 */ ColliderCylinder bottlesColliders[3]; + /* 0x036C */ LightNode* light; + /* 0x0370 */ LightInfo lightInfo; + /* 0x037E */ u16 textId; + /* 0x0380 */ s16 frameTimer; + /* 0x0382 */ s16 actionTimer; + /* 0x0384 */ Color_RGBA8 lightColor; + /* 0x0388 */ EnGbCagedSoul cagedSouls[4]; +} EnGb; // size = 0x0438 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c b/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c new file mode 100644 index 000000000..01ea8e3e0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c @@ -0,0 +1,814 @@ +/* + * File: z_en_ge1.c + * Overlay: ovl_En_Ge1 + * Description: White-clothed Gerudo + */ + +#include "z_en_ge1.h" +#include "vt.h" +#include "objects/object_ge1/object_ge1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +#define GE1_STATE_TALKING (1 << 0) +#define GE1_STATE_GIVE_QUIVER (1 << 1) +#define GE1_STATE_IDLE_ANIM (1 << 2) +#define GE1_STATE_STOP_FIDGET (1 << 3) + +typedef enum { + /* 00 */ GE1_HAIR_BOB, + /* 01 */ GE1_HAIR_STRAIGHT, + /* 02 */ GE1_HAIR_SPIKY +} EnGe1Hairstyle; + +void EnGe1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGe1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGe1_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGe1_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnGe1_CheckCarpentersFreed(void); +void EnGe1_WatchForPlayerFrontOnly(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_SetNormalText(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_WatchForAndSensePlayer(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_GetReaction_ValleyFloor(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_CheckForCard_GTGGuard(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_CheckGate_GateOp(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_GetReaction_GateGuard(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_TalkAfterGame_Archery(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_Wait_Archery(EnGe1* this, GlobalContext* globalCtx); +void EnGe1_CueUpAnimation(EnGe1* this); +void EnGe1_StopFidget(EnGe1* this); + +const ActorInit En_Ge1_InitVars = { + ACTOR_EN_GE1, + ACTORCAT_NPC, + FLAGS, + OBJECT_GE1, + sizeof(EnGe1), + (ActorFunc)EnGe1_Init, + (ActorFunc)EnGe1_Destroy, + (ActorFunc)EnGe1_Update, + (ActorFunc)EnGe1_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000702, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 40, 0, { 0, 0, 0 } }, +}; + +static Gfx* sHairstyleDLists[] = { + gGerudoWhiteHairstyleBobDL, + gGerudoWhiteHairstyleStraightFringeDL, + gGerudoWhiteHairstyleSpikyDL, +}; + +static Vec3f D_80A327A8 = { 600.0f, 700.0f, 0.0f }; + +static void* sEyeTextures[] = { + gGerudoWhiteEyeOpenTex, + gGerudoWhiteEyeHalfTex, + gGerudoWhiteEyeClosedTex, +}; + +void EnGe1_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGe1* this = (EnGe1*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGerudoWhiteSkel, &gGerudoWhiteIdleAnim, this->jointTable, + this->morphTable, GE1_LIMB_MAX); + Animation_PlayOnce(&this->skelAnime, &gGerudoWhiteIdleAnim); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->animation = &gGerudoWhiteIdleAnim; + this->animFunc = EnGe1_CueUpAnimation; + this->actor.targetMode = 6; + Actor_SetScale(&this->actor, 0.01f); + + // In Gerudo Valley + this->actor.uncullZoneForward = ((globalCtx->sceneNum == SCENE_SPOT09) ? 1000.0f : 1200.0f); + + switch (this->actor.params & 0xFF) { + + case GE1_TYPE_GATE_GUARD: + this->hairstyle = GE1_HAIR_SPIKY; + this->actionFunc = EnGe1_GetReaction_GateGuard; + break; + + case GE1_TYPE_GATE_OPERATOR: + this->hairstyle = GE1_HAIR_STRAIGHT; + + if (EnGe1_CheckCarpentersFreed()) { + this->actionFunc = EnGe1_CheckGate_GateOp; + } else { + this->actionFunc = EnGe1_WatchForPlayerFrontOnly; + } + break; + + case GE1_TYPE_NORMAL: + this->hairstyle = GE1_HAIR_STRAIGHT; + + if (EnGe1_CheckCarpentersFreed()) { + this->actionFunc = EnGe1_SetNormalText; + } else { + this->actionFunc = EnGe1_WatchForAndSensePlayer; + } + break; + + case GE1_TYPE_VALLEY_FLOOR: + if (LINK_IS_ADULT) { + // "Valley floor Gerudo withdrawal" + osSyncPrintf(VT_FGCOL(CYAN) "谷底 ゲルド 撤退 \n" VT_RST); + Actor_Kill(&this->actor); + return; + } + this->hairstyle = GE1_HAIR_BOB; + this->actionFunc = EnGe1_GetReaction_ValleyFloor; + break; + + case GE1_TYPE_HORSEBACK_ARCHERY: + if (INV_CONTENT(SLOT_BOW) == ITEM_NONE) { + Actor_Kill(&this->actor); + return; + } + this->actor.targetMode = 3; + this->hairstyle = GE1_HAIR_BOB; + // "Horseback archery Gerudo EVENT_INF(0) =" + osSyncPrintf(VT_FGCOL(CYAN) "やぶさめ ゲルド EVENT_INF(0) = %x\n" VT_RST, gSaveContext.eventInf[0]); + + if (gSaveContext.eventInf[0] & 0x100) { + this->actionFunc = EnGe1_TalkAfterGame_Archery; + } else if (EnGe1_CheckCarpentersFreed()) { + this->actionFunc = EnGe1_Wait_Archery; + } else { + this->actionFunc = EnGe1_WatchForPlayerFrontOnly; + } + break; + + case GE1_TYPE_TRAINING_GROUNDS_GUARD: + this->hairstyle = GE1_HAIR_STRAIGHT; + + if (EnGe1_CheckCarpentersFreed()) { + this->actionFunc = EnGe1_CheckForCard_GTGGuard; + } else { + this->actionFunc = EnGe1_WatchForPlayerFrontOnly; + } + break; + } + + this->stateFlags = 0; +} + +void EnGe1_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGe1* this = (EnGe1*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnGe1_SetTalkAction(EnGe1* this, GlobalContext* globalCtx, u16 textId, f32 arg3, EnGe1ActionFunc actionFunc) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = actionFunc; + this->animFunc = EnGe1_StopFidget; + this->stateFlags &= ~GE1_STATE_IDLE_ANIM; + this->animation = &gGerudoWhiteIdleAnim; + Animation_Change(&this->skelAnime, &gGerudoWhiteIdleAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGerudoWhiteIdleAnim), ANIMMODE_ONCE, -8.0f); + return true; + } + + this->actor.textId = textId; + + if (this->actor.xzDistToPlayer < arg3) { + func_8002F2CC(&this->actor, globalCtx, arg3); + } + + return false; +} + +void EnGe1_SetAnimationIdle(EnGe1* this) { + Animation_Change(&this->skelAnime, &gGerudoWhiteIdleAnim, -1.0f, Animation_GetLastFrame(&gGerudoWhiteIdleAnim), + 0.0f, ANIMMODE_ONCE, 8.0f); + this->animation = &gGerudoWhiteIdleAnim; + this->animFunc = EnGe1_CueUpAnimation; +} + +s32 EnGe1_CheckCarpentersFreed(void) { + u16 carpenterFlags = gSaveContext.eventChkInf[9]; + + if (!((carpenterFlags & 1) && (carpenterFlags & 2) && (carpenterFlags & 4) && (carpenterFlags & 8))) { + return 0; + } + return 1; +} + +/** + * Sends player to different places depending on if has hookshot, and if this is the first time captured + */ +void EnGe1_KickPlayer(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + + if (this->cutsceneTimer > 0) { + this->cutsceneTimer--; + } else { + func_8006D074(globalCtx); + + if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) { + globalCtx->nextEntranceIndex = 0x1A5; + } else if (gSaveContext.eventChkInf[12] & 0x80) { // Caught previously + globalCtx->nextEntranceIndex = 0x5F8; + } else { + globalCtx->nextEntranceIndex = 0x3B4; + } + + globalCtx->fadeTransition = 0x26; + globalCtx->sceneLoadFlag = 0x14; + } +} + +void EnGe1_SpotPlayer(EnGe1* this, GlobalContext* globalCtx) { + this->cutsceneTimer = 30; + this->actionFunc = EnGe1_KickPlayer; + func_8002DF54(globalCtx, &this->actor, 0x5F); + func_80078884(NA_SE_SY_FOUND); + Message_StartTextbox(globalCtx, 0x6000, &this->actor); +} + +void EnGe1_WatchForPlayerFrontOnly(EnGe1* this, GlobalContext* globalCtx) { + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(angleDiff) <= 0x4300) && (this->actor.xzDistToPlayer < 100.0f)) { + EnGe1_SpotPlayer(this, globalCtx); + } + + if (this->collider.base.acFlags & AC_HIT) { + EnGe1_SpotPlayer(this, globalCtx); + } + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnGe1_ChooseActionFromTextId(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + switch (this->actor.textId) { + case 0x6001: + this->actionFunc = EnGe1_SetNormalText; + break; + + case 0x601A: + case 0x6019: + this->actionFunc = EnGe1_GetReaction_ValleyFloor; + break; + + case 0x6018: + this->actionFunc = EnGe1_CheckGate_GateOp; + break; + + default: + this->actionFunc = EnGe1_GetReaction_ValleyFloor; + break; + } + } +} + +void EnGe1_SetNormalText(EnGe1* this, GlobalContext* globalCtx) { + EnGe1_SetTalkAction(this, globalCtx, 0x6001, 100.0f, EnGe1_ChooseActionFromTextId); +} + +void EnGe1_WatchForAndSensePlayer(EnGe1* this, GlobalContext* globalCtx) { + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((this->actor.xzDistToPlayer < 50.0f) || ((ABS(angleDiff) <= 0x4300) && (this->actor.xzDistToPlayer < 400.0f))) { + EnGe1_SpotPlayer(this, globalCtx); + } + + if (this->collider.base.acFlags & AC_HIT) { + EnGe1_SpotPlayer(this, globalCtx); + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnGe1_GetReaction_ValleyFloor(EnGe1* this, GlobalContext* globalCtx) { + u16 reactionText = Text_GetFaceReaction(globalCtx, 0x22); + + if (reactionText == 0) { + reactionText = 0x6019; + } + + EnGe1_SetTalkAction(this, globalCtx, reactionText, 100.0f, EnGe1_ChooseActionFromTextId); +} + +// Gerudo Training Ground Guard functions + +void EnGe1_WaitTillOpened_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + if (this->cutsceneTimer > 0) { + this->cutsceneTimer--; + } else { + EnGe1_SetAnimationIdle(this); + this->actionFunc = EnGe1_SetNormalText; + } + + this->stateFlags |= GE1_STATE_STOP_FIDGET; +} + +void EnGe1_Open_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + if (this->stateFlags & GE1_STATE_IDLE_ANIM) { + this->actionFunc = EnGe1_WaitTillOpened_GTGGuard; + Flags_SetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F); + this->cutsceneTimer = 50; + Message_CloseTextbox(globalCtx); + } else if ((this->skelAnime.curFrame == 15.0f) || (this->skelAnime.curFrame == 19.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_HAND_CLAP); + } +} + +void EnGe1_SetupOpen_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = EnGe1_Open_GTGGuard; + Animation_Change(&this->skelAnime, &gGerudoWhiteClapAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGerudoWhiteClapAnim), ANIMMODE_ONCE, -3.0f); + this->animation = &gGerudoWhiteClapAnim; + this->animFunc = EnGe1_StopFidget; + this->stateFlags &= ~GE1_STATE_IDLE_ANIM; + } +} + +void EnGe1_RefuseEntryTooPoor_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actionFunc = EnGe1_CheckForCard_GTGGuard; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_OfferOpen_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (gSaveContext.rupees < 10) { + Message_ContinueTextbox(globalCtx, 0x6016); + this->actionFunc = EnGe1_RefuseEntryTooPoor_GTGGuard; + } else { + Rupees_ChangeBy(-10); + Message_ContinueTextbox(globalCtx, 0x6015); + this->actionFunc = EnGe1_SetupOpen_GTGGuard; + } + break; + case 1: + this->actionFunc = EnGe1_CheckForCard_GTGGuard; + EnGe1_SetAnimationIdle(this); + break; + } + } +} + +void EnGe1_RefuseOpenNoCard_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_CheckForCard_GTGGuard; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_CheckForCard_GTGGuard(EnGe1* this, GlobalContext* globalCtx) { + if (CHECK_QUEST_ITEM(QUEST_GERUDO_CARD)) { + EnGe1_SetTalkAction(this, globalCtx, 0x6014, 100.0f, EnGe1_OfferOpen_GTGGuard); + } else { + //! @bug This outcome is inaccessible in normal gameplay since this function it is unreachable without + //! obtaining the card in the first place. + EnGe1_SetTalkAction(this, globalCtx, 0x6013, 100.0f, EnGe1_RefuseOpenNoCard_GTGGuard); + } +} + +// Gate Operator functions + +void EnGe1_WaitGateOpen_GateOp(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = EnGe1_CheckGate_GateOp; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_WaitUntilGateOpened_GateOp(EnGe1* this, GlobalContext* globalCtx) { + if (this->cutsceneTimer > 0) { + this->cutsceneTimer--; + } else { + EnGe1_SetAnimationIdle(this); + this->actionFunc = EnGe1_CheckGate_GateOp; + } + this->stateFlags |= GE1_STATE_STOP_FIDGET; +} + +void EnGe1_OpenGate_GateOp(EnGe1* this, GlobalContext* globalCtx) { + if (this->stateFlags & GE1_STATE_IDLE_ANIM) { + this->actionFunc = EnGe1_WaitUntilGateOpened_GateOp; + Flags_SetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F); + this->cutsceneTimer = 50; + Message_CloseTextbox(globalCtx); + } else if ((this->skelAnime.curFrame == 15.0f) || (this->skelAnime.curFrame == 19.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_IT_HAND_CLAP); + } +} + +void EnGe1_SetupOpenGate_GateOp(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = EnGe1_OpenGate_GateOp; + Animation_Change(&this->skelAnime, &gGerudoWhiteClapAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGerudoWhiteClapAnim), ANIMMODE_ONCE, -3.0f); + this->animation = &gGerudoWhiteClapAnim; + this->animFunc = EnGe1_StopFidget; + this->stateFlags &= ~GE1_STATE_IDLE_ANIM; + } +} + +void EnGe1_CheckGate_GateOp(EnGe1* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + EnGe1_SetTalkAction(this, globalCtx, 0x6018, 100.0f, EnGe1_WaitGateOpen_GateOp); + } else { + EnGe1_SetTalkAction(this, globalCtx, 0x6017, 100.0f, EnGe1_SetupOpenGate_GateOp); + } +} + +// Gate guard functions + +void EnGe1_Talk_GateGuard(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_GetReaction_GateGuard; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_GetReaction_GateGuard(EnGe1* this, GlobalContext* globalCtx) { + u16 reactionText; + + reactionText = Text_GetFaceReaction(globalCtx, 0x22); + + if (reactionText == 0) { + reactionText = 0x6069; + } + + if (EnGe1_SetTalkAction(this, globalCtx, reactionText, 100.0f, EnGe1_Talk_GateGuard)) { + this->animFunc = EnGe1_CueUpAnimation; + this->animation = &gGerudoWhiteDismissiveAnim; + Animation_Change(&this->skelAnime, &gGerudoWhiteDismissiveAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gGerudoWhiteDismissiveAnim), ANIMMODE_ONCE, -8.0f); + } +} + +// Archery functions + +void EnGe1_SetupWait_Archery(EnGe1* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_Wait_Archery; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_WaitTillItemGiven_Archery(EnGe1* this, GlobalContext* globalCtx) { + s32 getItemId; + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_SetupWait_Archery; + if (this->stateFlags & GE1_STATE_GIVE_QUIVER) { + gSaveContext.itemGetInf[0] |= 0x8000; + } else { + gSaveContext.infTable[25] |= 1; + } + } else { + if (this->stateFlags & GE1_STATE_GIVE_QUIVER) { + switch (CUR_UPG_VALUE(UPG_QUIVER)) { + //! @bug Asschest. See next function for details + case 1: + getItemId = GI_QUIVER_40; + break; + case 2: + getItemId = GI_QUIVER_50; + break; + } + } else { + getItemId = GI_HEART_PIECE; + } + func_8002F434(&this->actor, globalCtx, getItemId, 10000.0f, 50.0f); + } +} + +void EnGe1_BeginGiveItem_Archery(EnGe1* this, GlobalContext* globalCtx) { + s32 getItemId; + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = EnGe1_WaitTillItemGiven_Archery; + } + + if (this->stateFlags & GE1_STATE_GIVE_QUIVER) { + switch (CUR_UPG_VALUE(UPG_QUIVER)) { + //! @bug Asschest: the compiler inserts a default assigning *(sp+0x24) to getItemId, which is junk data left + //! over from the previous function run in EnGe1_Update, namely EnGe1_CueUpAnimation. The top stack variable + //! in that function is &this->skelAnime = thisx + 198, and depending on where this loads in memory, the + //! getItemId changes. + case 1: + getItemId = GI_QUIVER_40; + break; + case 2: + getItemId = GI_QUIVER_50; + break; + } + } else { + getItemId = GI_HEART_PIECE; + } + + func_8002F434(&this->actor, globalCtx, getItemId, 10000.0f, 50.0f); +} + +void EnGe1_TalkWinPrize_Archery(EnGe1* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_BeginGiveItem_Archery; + this->actor.flags &= ~ACTOR_FLAG_16; + } else { + func_8002F2CC(&this->actor, globalCtx, 200.0f); + } +} + +void EnGe1_TalkTooPoor_Archery(EnGe1* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = EnGe1_Wait_Archery; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_WaitDoNothing(EnGe1* this, GlobalContext* globalCtx) { +} + +void EnGe1_BeginGame_Archery(EnGe1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* horse; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (gSaveContext.rupees < 20) { + Message_ContinueTextbox(globalCtx, 0x85); + this->actionFunc = EnGe1_TalkTooPoor_Archery; + } else { + Rupees_ChangeBy(-20); + globalCtx->nextEntranceIndex = 0x129; + gSaveContext.nextCutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 0x26; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.eventInf[0] |= 0x100; + gSaveContext.eventChkInf[6] |= 0x100; + + if (!(player->stateFlags1 & 0x800000)) { + func_8002DF54(globalCtx, &this->actor, 1); + } else { + horse = Actor_FindNearby(globalCtx, &player->actor, ACTOR_EN_HORSE, ACTORCAT_BG, 1200.0f); + player->actor.freezeTimer = 1200; + + if (horse != NULL) { + horse->freezeTimer = 1200; + } + } + + this->actionFunc = EnGe1_WaitDoNothing; + } + break; + + case 1: + this->actionFunc = EnGe1_Wait_Archery; + Message_CloseTextbox(globalCtx); + break; + } + } +} + +void EnGe1_TalkOfferPlay_Archery(EnGe1* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x6041); + this->actionFunc = EnGe1_BeginGame_Archery; + } +} + +void EnGe1_TalkNoPrize_Archery(EnGe1* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_TalkOfferPlay_Archery; + } else { + func_8002F2CC(&this->actor, globalCtx, 300.0f); + } +} + +void EnGe1_TalkAfterGame_Archery(EnGe1* this, GlobalContext* globalCtx) { + gSaveContext.eventInf[0] &= ~0x100; + LOG_NUM("z_common_data.yabusame_total", gSaveContext.minigameScore, "../z_en_ge1.c", 1110); + LOG_NUM("z_common_data.memory.information.room_inf[127][ 0 ]", HIGH_SCORE(HS_HBA), "../z_en_ge1.c", 1111); + this->actor.flags |= ACTOR_FLAG_16; + + if (HIGH_SCORE(HS_HBA) < gSaveContext.minigameScore) { + HIGH_SCORE(HS_HBA) = gSaveContext.minigameScore; + } + + if (gSaveContext.minigameScore < 1000) { + this->actor.textId = 0x6045; + this->actionFunc = EnGe1_TalkNoPrize_Archery; + } else if (!(gSaveContext.infTable[25] & 1)) { + this->actor.textId = 0x6046; + this->actionFunc = EnGe1_TalkWinPrize_Archery; + this->stateFlags &= ~GE1_STATE_GIVE_QUIVER; + } else if (gSaveContext.minigameScore < 1500) { + this->actor.textId = 0x6047; + this->actionFunc = EnGe1_TalkNoPrize_Archery; + } else if (gSaveContext.itemGetInf[0] & 0x8000) { + this->actor.textId = 0x6047; + this->actionFunc = EnGe1_TalkNoPrize_Archery; + } else { + this->actor.textId = 0x6044; + this->actionFunc = EnGe1_TalkWinPrize_Archery; + this->stateFlags |= GE1_STATE_GIVE_QUIVER; + } +} + +void EnGe1_TalkNoHorse_Archery(EnGe1* this, GlobalContext* globalCtx) { + this->stateFlags |= GE1_STATE_TALKING; + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnGe1_Wait_Archery; + EnGe1_SetAnimationIdle(this); + } +} + +void EnGe1_Wait_Archery(EnGe1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + u16 textId; + + if (!(player->stateFlags1 & 0x800000)) { + EnGe1_SetTalkAction(this, globalCtx, 0x603F, 100.0f, EnGe1_TalkNoHorse_Archery); + } else { + if (gSaveContext.eventChkInf[6] & 0x100) { + if (gSaveContext.infTable[25] & 1) { + textId = 0x6042; + } else { + textId = 0x6043; + } + } else { + textId = 0x6040; + } + EnGe1_SetTalkAction(this, globalCtx, textId, 200.0f, EnGe1_TalkOfferPlay_Archery); + } +} + +// General functions + +void EnGe1_TurnToFacePlayer(EnGe1* this, GlobalContext* globalCtx) { + s32 pad; + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(angleDiff) <= 0x4000) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, 4000, 100); + this->actor.world.rot.y = this->actor.shape.rot.y; + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_2A2, this->actor.focus.pos); + } else { + if (angleDiff < 0) { + Math_SmoothStepToS(&this->headRot.y, -0x2000, 6, 6200, 0x100); + } else { + Math_SmoothStepToS(&this->headRot.y, 0x2000, 6, 6200, 0x100); + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 12, 1000, 100); + this->actor.world.rot.y = this->actor.shape.rot.y; + } +} + +void EnGe1_LookAtPlayer(EnGe1* this, GlobalContext* globalCtx) { + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(angleDiff) <= 0x4300) && (this->actor.xzDistToPlayer < 100.0f)) { + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_2A2, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->headRot.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->headRot.y, 0, 6, 6200, 100); + } +} + +void EnGe1_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGe1* this = (EnGe1*)thisx; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 40.0f, 25.0f, 40.0f, 5); + this->animFunc(this); + this->actionFunc(this, globalCtx); + + if (this->stateFlags & GE1_STATE_TALKING) { + EnGe1_TurnToFacePlayer(this, globalCtx); + this->stateFlags &= ~GE1_STATE_TALKING; + } else { + EnGe1_LookAtPlayer(this, globalCtx); + } + this->unk_2A2.x = this->unk_2A2.y = this->unk_2A2.z = 0; + + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + this->eyeIndex = this->blinkTimer; + + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + } +} + +// Animation functions + +void EnGe1_CueUpAnimation(EnGe1* this) { + if (SkelAnime_Update(&this->skelAnime)) { + Animation_PlayOnce(&this->skelAnime, this->animation); + } +} + +void EnGe1_StopFidget(EnGe1* this) { + if (!(this->stateFlags & GE1_STATE_IDLE_ANIM)) { + if (SkelAnime_Update(&this->skelAnime)) { + this->stateFlags |= GE1_STATE_IDLE_ANIM; + } + this->stateFlags |= GE1_STATE_STOP_FIDGET; + } +} + +s32 EnGe1_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + s32 pad; + EnGe1* this = (EnGe1*)thisx; + + if (limbIndex == GE1_LIMB_HEAD) { + rot->x += this->headRot.y; + rot->z += this->headRot.x; + } + + if (this->stateFlags & GE1_STATE_STOP_FIDGET) { + this->stateFlags &= ~GE1_STATE_STOP_FIDGET; + return 0; + } + + // The purpose of the state flag GE1_STATE_STOP_FIDGET is to skip this code, which this actor has in lieu of an idle + // animation. + if ((limbIndex == GE1_LIMB_TORSO) || (limbIndex == GE1_LIMB_L_FOREARM) || (limbIndex == GE1_LIMB_R_FOREARM)) { + rot->y += Math_SinS(globalCtx->state.frames * (limbIndex * 50 + 0x814)) * 200.0f; + rot->z += Math_CosS(globalCtx->state.frames * (limbIndex * 50 + 0x940)) * 200.0f; + } + return 0; +} + +void EnGe1_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnGe1* this = (EnGe1*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ge1.c", 1419); + + if (limbIndex == GE1_LIMB_HEAD) { + gSPDisplayList(POLY_OPA_DISP++, sHairstyleDLists[this->hairstyle]); + Matrix_MultVec3f(&D_80A327A8, &this->actor.focus.pos); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ge1.c", 1427); +} + +void EnGe1_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGe1* this = (EnGe1*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ge1.c", 1442); + + func_800943C8(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnGe1_OverrideLimbDraw, EnGe1_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ge1.c", 1459); +} diff --git a/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.h b/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.h new file mode 100644 index 000000000..84a96be51 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ge1/z_en_ge1.h @@ -0,0 +1,59 @@ +#ifndef Z_EN_GE1_H +#define Z_EN_GE1_H + +#include "ultra64.h" +#include "global.h" + +struct EnGe1; + +typedef void (*EnGe1AnimFunc)(struct EnGe1*); +typedef void (*EnGe1ActionFunc)(struct EnGe1*, GlobalContext*); + +typedef enum { + /* 0x00 */ GE1_TYPE_GATE_GUARD, + /* 0x01 */ GE1_TYPE_GATE_OPERATOR, + /* 0x04 */ GE1_TYPE_NORMAL = 4, + /* 0x05 */ GE1_TYPE_VALLEY_FLOOR, + /* 0x45 */ GE1_TYPE_HORSEBACK_ARCHERY = 0x45, + /* 0x46 */ GE1_TYPE_TRAINING_GROUNDS_GUARD +} EnGe1Type; + +typedef enum { + /* 00 */ GE1_LIMB_NONE, + /* 01 */ GE1_LIMB_WAIST, + /* 02 */ GE1_LIMB_L_THIGH, + /* 03 */ GE1_LIMB_L_LOWER_LEG, + /* 04 */ GE1_LIMB_L_FOOT, + /* 05 */ GE1_LIMB_R_THIGH, + /* 06 */ GE1_LIMB_R_LOWER_LEG, + /* 07 */ GE1_LIMB_R_FOOT, + /* 08 */ GE1_LIMB_TORSO, + /* 09 */ GE1_LIMB_L_UPPER_ARM, + /* 10 */ GE1_LIMB_L_FOREARM, + /* 11 */ GE1_LIMB_L_HAND, + /* 12 */ GE1_LIMB_R_UPPER_ARM, + /* 13 */ GE1_LIMB_R_FOREARM, + /* 14 */ GE1_LIMB_R_HAND, + /* 15 */ GE1_LIMB_HEAD, + /* 16 */ GE1_LIMB_MAX +} EnGe1Limb; + +typedef struct EnGe1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[GE1_LIMB_MAX]; + /* 0x023C */ Vec3s morphTable[GE1_LIMB_MAX]; + /* 0x029C */ Vec3s headRot; + /* 0x02A2 */ Vec3s unk_2A2; + /* 0x02A8 */ s16 eyeIndex; + /* 0x02AA */ s16 blinkTimer; + /* 0x02AC */ u16 stateFlags; + /* 0x02AE */ u8 hairstyle; + /* 0x02AF */ u8 cutsceneTimer; + /* 0x02B0 */ AnimationHeader* animation; + /* 0x02B4 */ EnGe1ActionFunc actionFunc; + /* 0x02B8 */ EnGe1AnimFunc animFunc; +} EnGe1; // size = 0x02BC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c b/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c new file mode 100644 index 000000000..708f4ed27 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.c @@ -0,0 +1,658 @@ +/* + * File: z_en_ge2.c + * Overlay: ovl_En_Ge2 + * Description: Purple-clothed Gerudo + */ + +#include "z_en_ge2.h" +#include "vt.h" +#include "objects/object_gla/object_gla.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +#define GE2_STATE_ANIMCOMPLETE (1 << 1) +#define GE2_STATE_KO (1 << 2) +#define GE2_STATE_CAPTURING (1 << 3) +#define GE2_STATE_TALKED (1 << 4) + +typedef enum { + /* 0 */ GE2_TYPE_PATROLLING, + /* 1 */ GE2_TYPE_STATIONARY, + /* 2 */ GE2_TYPE_GERUDO_CARD_GIVER +} EnGe2Type; + +typedef enum { + /* 0 */ GE2_ACTION_WALK, + /* 1 */ GE2_ACTION_ABOUTTURN, + /* 2 */ GE2_ACTION_TURNPLAYERSPOTTED, + /* 3 */ GE2_ACTION_KNOCKEDOUT, + /* 4 */ GE2_ACTION_CAPTURETURN, + /* 5 */ GE2_ACTION_CAPTURECHARGE, + /* 6 */ GE2_ACTION_CAPTURECLOSE, + /* 7 */ GE2_ACTION_STAND, + /* 8 */ GE2_ACTION_WAITLOOKATPLAYER +} EnGe2Action; + +void EnGe2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGe2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGe2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGe2_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnGe2_CheckCarpentersFreed(void); +void EnGe2_CaptureClose(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_CaptureCharge(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_CaptureTurn(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_KnockedOut(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_TurnPlayerSpotted(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_AboutTurn(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_Walk(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_Stand(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_WaitLookAtPlayer(EnGe2* this, GlobalContext* globalCtx); +void EnGe2_ForceTalk(EnGe2* this, GlobalContext* globalCtx); + +// Update functions +void EnGe2_UpdateFriendly(Actor* thisx, GlobalContext* globalCtx); +void EnGe2_UpdateAfterTalk(Actor* thisx, GlobalContext* globalCtx); +void EnGe2_UpdateStunned(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Ge2_InitVars = { + ACTOR_EN_GE2, + ACTORCAT_NPC, + FLAGS, + OBJECT_GLA, + sizeof(EnGe2), + (ActorFunc)EnGe2_Init, + (ActorFunc)EnGe2_Destroy, + (ActorFunc)EnGe2_Update, + (ActorFunc)EnGe2_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x000007A2, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 60, 0, { 0, 0, 0 } }, +}; + +static EnGe2ActionFunc sActionFuncs[] = { + EnGe2_Walk, EnGe2_AboutTurn, EnGe2_TurnPlayerSpotted, + EnGe2_KnockedOut, EnGe2_CaptureTurn, EnGe2_CaptureCharge, + EnGe2_CaptureClose, EnGe2_Stand, EnGe2_WaitLookAtPlayer, +}; + +static AnimationHeader* sAnimations[] = { + &gGerudoPurpleWalkingAnim, &gGerudoPurpleLookingAboutAnim, &gGerudoPurpleLookingAboutAnim, + &gGerudoPurpleFallingToGroundAnim, &gGerudoPurpleLookingAboutAnim, &gGerudoPurpleChargingAnim, + &gGerudoPurpleLookingAboutAnim, &gGerudoPurpleLookingAboutAnim, &gGerudoPurpleLookingAboutAnim, +}; + +static u8 sAnimModes[] = { + ANIMMODE_LOOP, ANIMMODE_ONCE, ANIMMODE_LOOP, ANIMMODE_ONCE, ANIMMODE_LOOP, + ANIMMODE_LOOP, ANIMMODE_LOOP, ANIMMODE_LOOP, ANIMMODE_ONCE, +}; + +void EnGe2_ChangeAction(EnGe2* this, s32 i) { + this->actionFunc = sActionFuncs[i]; + Animation_Change(&this->skelAnime, sAnimations[i], 1.0f, 0.0f, Animation_GetLastFrame(sAnimations[i]), + sAnimModes[i], -8.0f); + this->stateFlags &= ~GE2_STATE_ANIMCOMPLETE; +} + +void EnGe2_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGe2* this = (EnGe2*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGerudoPurpleSkel, NULL, this->jointTable, this->morphTable, 22); + Animation_PlayLoop(&this->skelAnime, &gGerudoPurpleWalkingAnim); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + + if (globalCtx->sceneNum == SCENE_SPOT09) { + this->actor.uncullZoneForward = 1000.0f; + } else { + this->actor.uncullZoneForward = 1200.0f; + } + + this->yDetectRange = (this->actor.world.rot.z + 1) * 40.0f; + this->actor.world.rot.z = 0; + this->actor.shape.rot.z = 0; + + switch (this->actor.params & 0xFF) { + case GE2_TYPE_PATROLLING: + EnGe2_ChangeAction(this, GE2_ACTION_WALK); + if (EnGe2_CheckCarpentersFreed()) { + this->actor.update = EnGe2_UpdateFriendly; + this->actor.targetMode = 6; + } + break; + case GE2_TYPE_STATIONARY: + EnGe2_ChangeAction(this, GE2_ACTION_STAND); + if (EnGe2_CheckCarpentersFreed()) { + this->actor.update = EnGe2_UpdateFriendly; + this->actor.targetMode = 6; + } + break; + case GE2_TYPE_GERUDO_CARD_GIVER: + EnGe2_ChangeAction(this, GE2_ACTION_WAITLOOKATPLAYER); + this->actor.update = EnGe2_UpdateAfterTalk; + this->actionFunc = EnGe2_ForceTalk; + this->actor.targetMode = 6; + break; + default: + ASSERT(0, "0", "../z_en_ge2.c", 418); + } + + this->stateFlags = 0; + this->unk_304 = 0; // Set and not used + this->walkTimer = 0; + this->playerSpottedParam = 0; + this->actor.minVelocityY = -4.0f; + this->actor.gravity = -1.0f; + this->walkDirection = this->actor.world.rot.y; + this->walkDuration = ((this->actor.params & 0xFF00) >> 8) * 10; +} + +void EnGe2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGe2* this = (EnGe2*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +// Detection/check functions + +s32 Ge2_DetectPlayerInAction(GlobalContext* globalCtx, EnGe2* this) { + f32 visionScale; + + visionScale = (!IS_DAY ? 0.75f : 1.5f); + + if ((250.0f * visionScale) < this->actor.xzDistToPlayer) { + return 0; + } + + if (this->actor.xzDistToPlayer < 50.0f) { + return 2; + } + + if (func_8002DDE4(globalCtx)) { + return 1; + } + return 0; +} + +s32 Ge2_DetectPlayerInUpdate(GlobalContext* globalCtx, EnGe2* this, Vec3f* pos, s16 yRot, f32 yDetectRange) { + Player* player = GET_PLAYER(globalCtx); + Vec3f posResult; + CollisionPoly* outPoly; + f32 visionScale; + + visionScale = (!IS_DAY ? 0.75f : 1.5f); + + if ((250.0f * visionScale) < this->actor.xzDistToPlayer) { + return 0; + } + + if (yDetectRange < ABS(this->actor.yDistToPlayer)) { + return 0; + } + + if (ABS((s16)(this->actor.yawTowardsPlayer - yRot)) > 0x2000) { + return 0; + } + + if (BgCheck_AnyLineTest1(&globalCtx->colCtx, pos, &player->bodyPartsPos[7], &posResult, &outPoly, 0)) { + return 0; + } + return 1; +} + +s32 EnGe2_CheckCarpentersFreed(void) { + if ((u8)(gSaveContext.eventChkInf[9] & 0xF) == 0xF) { + return 1; + } + return 0; +} + +// Actions + +void EnGe2_CaptureClose(EnGe2* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + } else { + func_8006D074(globalCtx); + + if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) { + globalCtx->nextEntranceIndex = 0x1A5; + } else if (gSaveContext.eventChkInf[12] & 0x80) { + globalCtx->nextEntranceIndex = 0x5F8; + } else { + globalCtx->nextEntranceIndex = 0x3B4; + } + + globalCtx->fadeTransition = 0x26; + globalCtx->sceneLoadFlag = 0x14; + } +} + +void EnGe2_CaptureCharge(EnGe2* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 2, 0x400, 0x100); + this->actor.shape.rot.y = this->actor.world.rot.y; + if (this->actor.xzDistToPlayer < 50.0f) { + EnGe2_ChangeAction(this, GE2_ACTION_CAPTURECLOSE); + this->actor.speedXZ = 0.0f; + } + + if (this->timer > 0) { + this->timer--; + } else { + func_8006D074(globalCtx); + + if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) { + globalCtx->nextEntranceIndex = 0x1A5; + } else if (gSaveContext.eventChkInf[12] & 0x80) { + globalCtx->nextEntranceIndex = 0x5F8; + } else { + globalCtx->nextEntranceIndex = 0x3B4; + } + + globalCtx->fadeTransition = 0x26; + globalCtx->sceneLoadFlag = 0x14; + } +} + +void EnGe2_CaptureTurn(EnGe2* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 2, 0x400, 0x100); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->actor.world.rot.y == this->actor.yawTowardsPlayer) { + EnGe2_ChangeAction(this, GE2_ACTION_CAPTURECHARGE); + this->timer = 50; + this->actor.speedXZ = 4.0f; + } +} + +void EnGe2_KnockedOut(EnGe2* this, GlobalContext* globalCtx) { + static Vec3f effectVelocity = { 0.0f, -0.05f, 0.0f }; + static Vec3f effectAccel = { 0.0f, -0.025f, 0.0f }; + static Color_RGBA8 effectPrimColor = { 255, 255, 255, 0 }; + static Color_RGBA8 effectEnvColor = { 255, 150, 0, 0 }; + s32 effectAngle; + Vec3f effectPos; + + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->stateFlags & GE2_STATE_ANIMCOMPLETE) { + effectAngle = (globalCtx->state.frames) * 0x2800; + effectPos.x = this->actor.focus.pos.x + (Math_CosS(effectAngle) * 5.0f); + effectPos.y = this->actor.focus.pos.y + 10.0f; + effectPos.z = this->actor.focus.pos.z + (Math_SinS(effectAngle) * 5.0f); + EffectSsKiraKira_SpawnDispersed(globalCtx, &effectPos, &effectVelocity, &effectAccel, &effectPrimColor, + &effectEnvColor, 1000, 16); + } +} + +void EnGe2_TurnPlayerSpotted(EnGe2* this, GlobalContext* globalCtx) { + s32 playerSpotted; + + this->actor.speedXZ = 0.0f; + + if (this->stateFlags & GE2_STATE_TALKED) { + this->stateFlags &= ~GE2_STATE_TALKED; + } else { + playerSpotted = Ge2_DetectPlayerInAction(globalCtx, this); + + if (playerSpotted != 0) { + this->timer = 100; + this->yawTowardsPlayer = this->actor.yawTowardsPlayer; + + if (this->playerSpottedParam < playerSpotted) { + this->playerSpottedParam = playerSpotted; + } + } else if (this->actor.world.rot.y == this->yawTowardsPlayer) { + this->playerSpottedParam = 0; + EnGe2_ChangeAction(this, GE2_ACTION_ABOUTTURN); + return; + } + } + + switch (this->playerSpottedParam) { + case 1: + Math_SmoothStepToS(&this->actor.world.rot.y, this->yawTowardsPlayer, 2, 0x200, 0x100); + break; + case 2: + Math_SmoothStepToS(&this->actor.world.rot.y, this->yawTowardsPlayer, 2, 0x600, 0x180); + break; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnGe2_AboutTurn(EnGe2* this, GlobalContext* globalCtx) { + s32 playerSpotted; + + this->actor.speedXZ = 0.0f; + playerSpotted = Ge2_DetectPlayerInAction(globalCtx, this); + + if (playerSpotted != 0) { + EnGe2_ChangeAction(this, GE2_ACTION_TURNPLAYERSPOTTED); + this->timer = 100; + this->playerSpottedParam = playerSpotted; + this->yawTowardsPlayer = this->actor.yawTowardsPlayer; + } else if (this->stateFlags & GE2_STATE_ANIMCOMPLETE) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->walkDirection, 2, 0x400, 0x200); + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + if (this->actor.shape.rot.y == this->walkDirection) { + EnGe2_ChangeAction(this, GE2_ACTION_WALK); + } +} + +void EnGe2_Walk(EnGe2* this, GlobalContext* globalCtx) { + u8 playerSpotted; + + playerSpotted = Ge2_DetectPlayerInAction(globalCtx, this); + if (playerSpotted != 0) { + this->actor.speedXZ = 0.0f; + EnGe2_ChangeAction(this, GE2_ACTION_TURNPLAYERSPOTTED); + this->timer = 100; + this->playerSpottedParam = playerSpotted; + this->yawTowardsPlayer = this->actor.yawTowardsPlayer; + } else if (this->walkTimer >= this->walkDuration) { + this->walkTimer = 0; + this->walkDirection += 0x8000; + EnGe2_ChangeAction(this, GE2_ACTION_ABOUTTURN); + this->actor.speedXZ = 0.0f; + } else { + this->walkTimer++; + this->actor.speedXZ = 2.0f; + } +} + +void EnGe2_Stand(EnGe2* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->walkDirection, 2, 0x400, 0x200); +} + +void EnGe2_TurnToFacePlayer(EnGe2* this, GlobalContext* globalCtx) { + s32 pad; + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(angleDiff) <= 0x4000) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, 4000, 100); + this->actor.world.rot.y = this->actor.shape.rot.y; + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_2EE, this->actor.focus.pos); + } else { + if (angleDiff < 0) { + Math_SmoothStepToS(&this->headRot.y, -0x2000, 6, 6200, 0x100); + } else { + Math_SmoothStepToS(&this->headRot.y, 0x2000, 6, 6200, 0x100); + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 12, 1000, 100); + this->actor.world.rot.y = this->actor.shape.rot.y; + } +} + +void EnGe2_LookAtPlayer(EnGe2* this, GlobalContext* globalCtx) { + if ((ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4300) && + (this->actor.xzDistToPlayer < 200.0f)) { + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_2EE, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->headRot.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->headRot.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2EE.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2EE.y, 0, 6, 6200, 100); + } +} + +void EnGe2_SetActionAfterTalk(EnGe2* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + + switch (this->actor.params & 0xFF) { + case GE2_TYPE_PATROLLING: + EnGe2_ChangeAction(this, GE2_ACTION_ABOUTTURN); + break; + case GE2_TYPE_STATIONARY: + EnGe2_ChangeAction(this, GE2_ACTION_STAND); + break; + case GE2_TYPE_GERUDO_CARD_GIVER: + this->actionFunc = EnGe2_WaitLookAtPlayer; + break; + } + this->actor.update = EnGe2_UpdateFriendly; + this->actor.flags &= ~ACTOR_FLAG_16; + } + EnGe2_TurnToFacePlayer(this, globalCtx); +} + +void EnGe2_WaitLookAtPlayer(EnGe2* this, GlobalContext* globalCtx) { + EnGe2_LookAtPlayer(this, globalCtx); +} + +void EnGe2_WaitTillCardGiven(EnGe2* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnGe2_SetActionAfterTalk; + } else { + func_8002F434(&this->actor, globalCtx, GI_GERUDO_CARD, 10000.0f, 50.0f); + } +} + +void EnGe2_GiveCard(EnGe2* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = EnGe2_WaitTillCardGiven; + func_8002F434(&this->actor, globalCtx, GI_GERUDO_CARD, 10000.0f, 50.0f); + } +} + +void EnGe2_ForceTalk(EnGe2* this, GlobalContext* globalCtx) { + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnGe2_GiveCard; + } else { + this->actor.textId = 0x6004; + this->actor.flags |= ACTOR_FLAG_16; + func_8002F1C4(&this->actor, globalCtx, 300.0f, 300.0f, 0); + } + EnGe2_LookAtPlayer(this, globalCtx); +} + +void EnGe2_SetupCapturePlayer(EnGe2* this, GlobalContext* globalCtx) { + this->stateFlags |= GE2_STATE_CAPTURING; + this->actor.speedXZ = 0.0f; + EnGe2_ChangeAction(this, GE2_ACTION_CAPTURETURN); + func_8002DF54(globalCtx, &this->actor, 95); + func_80078884(NA_SE_SY_FOUND); + Message_StartTextbox(globalCtx, 0x6000, &this->actor); +} + +void EnGe2_MaintainColliderAndSetAnimState(EnGe2* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 40.0f, 25.0f, 40.0f, 5); + + if (!(this->stateFlags & GE2_STATE_ANIMCOMPLETE) && SkelAnime_Update(&this->skelAnime)) { + this->stateFlags |= GE2_STATE_ANIMCOMPLETE; + } +} + +void EnGe2_MoveAndBlink(EnGe2* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->actor); + + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + this->eyeIndex = this->blinkTimer; + + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + } +} + +// Update functions + +void EnGe2_UpdateFriendly(Actor* thisx, GlobalContext* globalCtx) { + EnGe2* this = (EnGe2*)thisx; + + EnGe2_MaintainColliderAndSetAnimState(this, globalCtx); + this->actionFunc(this, globalCtx); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if ((this->actor.params & 0xFF) == GE2_TYPE_PATROLLING) { + this->actor.speedXZ = 0.0f; + EnGe2_ChangeAction(this, GE2_ACTION_WAITLOOKATPLAYER); + } + this->actionFunc = EnGe2_SetActionAfterTalk; + this->actor.update = EnGe2_UpdateAfterTalk; + } else { + this->actor.textId = 0x6005; + + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + EnGe2_MoveAndBlink(this, globalCtx); +} + +void EnGe2_UpdateAfterTalk(Actor* thisx, GlobalContext* globalCtx) { + EnGe2* this = (EnGe2*)thisx; + + this->stateFlags |= GE2_STATE_TALKED; + EnGe2_MaintainColliderAndSetAnimState(this, globalCtx); + this->actionFunc(this, globalCtx); + EnGe2_MoveAndBlink(this, globalCtx); +} + +void EnGe2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGe2* this = (EnGe2*)thisx; + s32 paramsType; + + EnGe2_MaintainColliderAndSetAnimState(this, globalCtx); + + if ((this->stateFlags & GE2_STATE_KO) || (this->stateFlags & GE2_STATE_CAPTURING)) { + this->actionFunc(this, globalCtx); + } else if (this->collider.base.acFlags & 2) { + if ((this->collider.info.acHitInfo != NULL) && (this->collider.info.acHitInfo->toucher.dmgFlags & 0x80)) { + Actor_SetColorFilter(&this->actor, 0, 120, 0, 400); + this->actor.update = EnGe2_UpdateStunned; + return; + } + + EnGe2_ChangeAction(this, GE2_ACTION_KNOCKEDOUT); + this->timer = 100; + this->stateFlags |= GE2_STATE_KO; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_SK_CRASH); + } else { + this->actionFunc(this, globalCtx); + + if (Ge2_DetectPlayerInUpdate(globalCtx, this, &this->actor.focus.pos, this->actor.shape.rot.y, + this->yDetectRange)) { + // "Discovered!" + osSyncPrintf(VT_FGCOL(GREEN) "発見!!!!!!!!!!!!\n" VT_RST); + EnGe2_SetupCapturePlayer(this, globalCtx); + } + + if (((this->actor.params & 0xFF) == GE2_TYPE_STATIONARY) && (this->actor.xzDistToPlayer < 100.0f)) { + // "Discovered!" + osSyncPrintf(VT_FGCOL(GREEN) "発見!!!!!!!!!!!!\n" VT_RST); + EnGe2_SetupCapturePlayer(this, globalCtx); + } + } + + if (!(this->stateFlags & GE2_STATE_KO)) { + paramsType = this->actor.params & 0xFF; // Not necessary, but looks a bit nicer + if ((paramsType == GE2_TYPE_PATROLLING) || (paramsType == GE2_TYPE_STATIONARY)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + EnGe2_MoveAndBlink(this, globalCtx); + + if (EnGe2_CheckCarpentersFreed() && !(this->stateFlags & GE2_STATE_KO)) { + this->actor.update = EnGe2_UpdateFriendly; + this->actor.targetMode = 6; + } +} + +void EnGe2_UpdateStunned(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnGe2* this = (EnGe2*)thisx; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 40.0f, 25.0f, 40.0f, 5); + + if ((this->collider.base.acFlags & 2) && + ((this->collider.info.acHitInfo == NULL) || !(this->collider.info.acHitInfo->toucher.dmgFlags & 0x80))) { + this->actor.colorFilterTimer = 0; + EnGe2_ChangeAction(this, GE2_ACTION_KNOCKEDOUT); + this->timer = 100; + this->stateFlags |= GE2_STATE_KO; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_SK_CRASH); + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (EnGe2_CheckCarpentersFreed()) { + this->actor.update = EnGe2_UpdateFriendly; + this->actor.targetMode = 6; + this->actor.colorFilterTimer = 0; + } else if (this->actor.colorFilterTimer == 0) { + this->actor.update = EnGe2_Update; + } +} + +s32 EnGe2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnGe2* this = (EnGe2*)thisx; + + if (limbIndex == 3) { + rot->x += this->headRot.y; + rot->z += this->headRot.x; + } + return 0; +} + +void EnGe2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_80A343B0 = { 600.0f, 700.0f, 0.0f }; + EnGe2* this = (EnGe2*)thisx; + + if (limbIndex == 6) { + Matrix_MultVec3f(&D_80A343B0, &this->actor.focus.pos); + } +} + +void EnGe2_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gGerudoPurpleEyeOpenTex, gGerudoPurpleEyeHalfTex, gGerudoPurpleEyeClosedTex }; + s32 pad; + EnGe2* this = (EnGe2*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ge2.c", 1274); + + func_800943C8(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + func_8002EBCC(&this->actor, globalCtx, 0); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnGe2_OverrideLimbDraw, EnGe2_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ge2.c", 1291); +} diff --git a/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.h b/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.h new file mode 100644 index 000000000..b9d08f0bb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ge2/z_en_ge2.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_GE2_H +#define Z_EN_GE2_H + +#include "ultra64.h" +#include "global.h" + +struct EnGe2; + +typedef void (*EnGe2ActionFunc)(struct EnGe2*, GlobalContext*); + +typedef struct EnGe2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[22]; + /* 0x0260 */ Vec3s morphTable[22]; + /* 0x02E4 */ s16 eyeIndex; + /* 0x02E6 */ s16 blinkTimer; + /* 0x02E8 */ Vec3s headRot; + /* 0x02EE */ Vec3s unk_2EE; + /* 0x02F4 */ u16 stateFlags; + /* 0x02F6 */ s16 walkDirection; + /* 0x02F8 */ s16 yawTowardsPlayer; + /* 0x02FC */ f32 yDetectRange; + /* 0x0300 */ u16 walkDuration; + /* 0x0302 */ u16 walkTimer; + /* 0x0304 */ u8 unk_304; + /* 0x0305 */ u8 timer; + /* 0x0306 */ u8 playerSpottedParam; + /* 0x0308 */ EnGe2ActionFunc actionFunc; +} EnGe2; // size = 0x030C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c new file mode 100644 index 000000000..697800559 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.c @@ -0,0 +1,296 @@ +/* + * File: z_en_ge3.c + * Overlay: ovl_En_Ge3 + * Description: Gerudo giving you membership card + */ + +#include "z_en_ge3.h" +#include "objects/object_geldb/object_geldb.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnGe3_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGe3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGe3_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGe3_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnGe3_WaitLookAtPlayer(EnGe3* this, GlobalContext* globalCtx); +void EnGe3_ForceTalk(EnGe3* this, GlobalContext* globalCtx); +void EnGe3_UpdateWhenNotTalking(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Ge3_InitVars = { + ACTOR_EN_GE3, + ACTORCAT_NPC, + FLAGS, + OBJECT_GELDB, + sizeof(EnGe3), + (ActorFunc)EnGe3_Init, + (ActorFunc)EnGe3_Destroy, + (ActorFunc)EnGe3_Update, + (ActorFunc)EnGe3_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000722, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 50, 0, { 0, 0, 0 } }, +}; + +static EnGe3ActionFunc sActionFuncs[] = { EnGe3_WaitLookAtPlayer }; +static AnimationHeader* sAnimations[] = { &gGerudoRedStandAnim }; // Idle with right hand on hip and left over mouth +static u8 sAnimationModes[] = { ANIMMODE_LOOP }; + +void EnGe3_ChangeAction(EnGe3* this, s32 i) { + this->actionFunc = sActionFuncs[i]; + + Animation_Change(&this->skelAnime, sAnimations[i], 1.0f, 0.0f, (f32)Animation_GetLastFrame(sAnimations[i]), + sAnimationModes[i], -8.0f); + + this->unk_30C &= ~2; +} + +void EnGe3_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnGe3* this = (EnGe3*)thisx; + GlobalContext* globalCtx = globalCtx2; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGerudoRedSkel, NULL, this->jointTable, this->morphTable, + GELDB_LIMB_MAX); + Animation_PlayLoop(&this->skelAnime, &gGerudoRedStandAnim); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + this->actor.world.rot.z = 0; + this->actor.shape.rot.z = 0; + EnGe3_ChangeAction(this, 0); + this->actionFunc = EnGe3_ForceTalk; + this->unk_30C = 0; + this->actor.targetMode = 6; + this->actor.minVelocityY = -4.0f; + this->actor.gravity = -1.0f; +} + +void EnGe3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGe3* this = (EnGe3*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnGe3_TurnToFacePlayer(EnGe3* this, GlobalContext* globalCtx) { + s32 pad; + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(angleDiff) <= 0x4000) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, 4000, 100); + this->actor.world.rot.y = this->actor.shape.rot.y; + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_306, this->actor.focus.pos); + } else { + if (angleDiff < 0) { + Math_SmoothStepToS(&this->headRot.y, -0x2000, 6, 6200, 0x100); + } else { + Math_SmoothStepToS(&this->headRot.y, 0x2000, 6, 6200, 0x100); + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 12, 1000, 100); + this->actor.world.rot.y = this->actor.shape.rot.y; + } +} + +void EnGe3_LookAtPlayer(EnGe3* this, GlobalContext* globalCtx) { + if ((ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x2300) && + (this->actor.xzDistToPlayer < 100.0f)) { + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_306, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->headRot.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->headRot.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_306.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_306.y, 0, 6, 6200, 100); + } +} + +void EnGe3_Wait(EnGe3* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnGe3_WaitLookAtPlayer; + this->actor.update = EnGe3_UpdateWhenNotTalking; + this->actor.flags &= ~ACTOR_FLAG_16; + } + EnGe3_TurnToFacePlayer(this, globalCtx); +} + +void EnGe3_WaitLookAtPlayer(EnGe3* this, GlobalContext* globalCtx) { + EnGe3_LookAtPlayer(this, globalCtx); +} + +void EnGe3_WaitTillCardGiven(EnGe3* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnGe3_Wait; + } else { + func_8002F434(&this->actor, globalCtx, GI_GERUDO_CARD, 10000.0f, 50.0f); + } +} + +void EnGe3_GiveCard(EnGe3* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = EnGe3_WaitTillCardGiven; + func_8002F434(&this->actor, globalCtx, GI_GERUDO_CARD, 10000.0f, 50.0f); + } +} + +void EnGe3_ForceTalk(EnGe3* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnGe3_GiveCard; + } else { + if (!(this->unk_30C & 4)) { + func_8002DF54(globalCtx, &this->actor, 7); + this->unk_30C |= 4; + } + this->actor.textId = 0x6004; + this->actor.flags |= ACTOR_FLAG_16; + func_8002F1C4(&this->actor, globalCtx, 300.0f, 300.0f, 0); + } + EnGe3_LookAtPlayer(this, globalCtx); +} + +void EnGe3_UpdateCollision(EnGe3* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 40.0f, 25.0f, 40.0f, 5); + + if (!(this->unk_30C & 2) && SkelAnime_Update(&this->skelAnime)) { + this->unk_30C |= 2; + } +} + +void EnGe3_MoveAndBlink(EnGe3* this, GlobalContext* globalCtx) { + + Actor_MoveForward(&this->actor); + + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + + this->eyeIndex = this->blinkTimer; + + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + } +} + +void EnGe3_UpdateWhenNotTalking(Actor* thisx, GlobalContext* globalCtx) { + EnGe3* this = (EnGe3*)thisx; + + EnGe3_UpdateCollision(this, globalCtx); + this->actionFunc(this, globalCtx); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnGe3_Wait; + this->actor.update = EnGe3_Update; + } else { + this->actor.textId = 0x6005; + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + + EnGe3_MoveAndBlink(this, globalCtx); +} + +void EnGe3_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGe3* this = (EnGe3*)thisx; + + EnGe3_UpdateCollision(this, globalCtx); + this->actionFunc(this, globalCtx); + EnGe3_MoveAndBlink(this, globalCtx); +} + +s32 EnGe3_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnGe3* this = (EnGe3*)thisx; + + switch (limbIndex) { + // Hide swords and veil from object_geldb + case GELDB_LIMB_VEIL: + case GELDB_LIMB_R_SWORD: + case GELDB_LIMB_L_SWORD: + *dList = NULL; + return false; + // Turn head + case GELDB_LIMB_HEAD: + rot->x += this->headRot.y; + + // This is a hack to fix the color-changing clothes this Gerudo has on N64 versions + default: + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ge3.c", 547); + switch (limbIndex) { + case GELDB_LIMB_NECK: + break; + case GELDB_LIMB_HEAD: + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 80, 60, 10, 255); + break; + case GELDB_LIMB_R_SWORD: + case GELDB_LIMB_L_SWORD: + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 140, 170, 230, 255); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + break; + default: + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 140, 0, 0, 255); + break; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ge3.c", 566); + break; + } + return false; +} + +void EnGe3_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnGe3* this = (EnGe3*)thisx; + Vec3f D_80A351C8 = { 600.0f, 700.0f, 0.0f }; + + if (limbIndex == GELDB_LIMB_HEAD) { + Matrix_MultVec3f(&D_80A351C8, &this->actor.focus.pos); + } +} + +void EnGe3_Draw(Actor* thisx, GlobalContext* globalCtx2) { + static void* eyeTextures[] = { + gGerudoRedEyeOpenTex, + gGerudoRedEyeHalfTex, + gGerudoRedEyeShutTex, + }; + EnGe3* this = (EnGe3*)thisx; + GlobalContext* globalCtx = globalCtx2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ge3.c", 614); + + func_800943C8(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + func_8002EBCC(&this->actor, globalCtx, 0); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnGe3_OverrideLimbDraw, EnGe3_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ge3.c", 631); +} diff --git a/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.h b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.h new file mode 100644 index 000000000..f292c9968 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ge3/z_en_ge3.h @@ -0,0 +1,26 @@ +#ifndef Z_EN_GE3_H +#define Z_EN_GE3_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_GeldB/z_en_geldb.h" + +struct EnGe3; + +typedef void (*EnGe3ActionFunc)(struct EnGe3*, GlobalContext*); + +typedef struct EnGe3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[GELDB_LIMB_MAX]; + /* 0x026C */ Vec3s morphTable[GELDB_LIMB_MAX]; + /* 0x02FC */ s16 eyeIndex; + /* 0x02FE */ s16 blinkTimer; + /* 0x0300 */ Vec3s headRot; + /* 0x0306 */ Vec3s unk_306; + /* 0x030C */ u16 unk_30C; + /* 0x0310 */ EnGe3ActionFunc actionFunc; +} EnGe3; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c new file mode 100644 index 000000000..61298756a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.c @@ -0,0 +1,1643 @@ +/* + * File: z_en_geldb.c + * Overlay: ovl_En_GeldB + * Description: Gerudo fighter + */ + +#include "z_en_geldb.h" +#include "objects/object_geldb/object_geldb.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +typedef enum { + /* 0 */ GELDB_WAIT, + /* 1 */ GELDB_DEFEAT, + /* 2 */ GELDB_DAMAGED, + /* 3 */ GELDB_JUMP, + /* 4 */ GELDB_ROLL_BACK, + /* 5 */ GELDB_READY, + /* 6 */ GELDB_BLOCK, + /* 7 */ GELDB_SLASH, + /* 8 */ GELDB_ADVANCE, + /* 9 */ GELDB_PIVOT, + /* 10 */ GELDB_CIRCLE, + /* 11 */ GELDB_UNUSED, + /* 12 */ GELDB_SPIN_ATTACK, + /* 13 */ GELDB_SIDESTEP, + /* 14 */ GELDB_ROLL_FORWARD, + /* 15 */ GELDB_STUNNED, + /* 16 */ GELDB_SPIN_DODGE +} EnGeldBAction; + +void EnGeldB_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGeldB_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGeldB_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGeldB_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnGeldB_DodgeRanged(GlobalContext* globalCtx, EnGeldB* this); + +void EnGeldB_SetupWait(EnGeldB* this); +void EnGeldB_SetupReady(EnGeldB* this); +void EnGeldB_SetupAdvance(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_SetupPivot(EnGeldB* this); +void EnGeldB_SetupRollForward(EnGeldB* this); +void EnGeldB_SetupCircle(EnGeldB* this); +void EnGeldB_SetupSpinDodge(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_SetupSlash(EnGeldB* this); +void EnGeldB_SetupSpinAttack(EnGeldB* this); +void EnGeldB_SetupRollBack(EnGeldB* this); +void EnGeldB_SetupJump(EnGeldB* this); +void EnGeldB_SetupBlock(EnGeldB* this); +void EnGeldB_SetupSidestep(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_SetupDefeated(EnGeldB* this); + +void EnGeldB_Wait(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Flee(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Ready(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Advance(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_RollForward(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Pivot(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Circle(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_SpinDodge(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Slash(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_SpinAttack(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_RollBack(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Stunned(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Damaged(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Jump(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Block(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Sidestep(EnGeldB* this, GlobalContext* globalCtx); +void EnGeldB_Defeated(EnGeldB* this, GlobalContext* globalCtx); + +const ActorInit En_GeldB_InitVars = { + ACTOR_EN_GELDB, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_GELDB, + sizeof(EnGeldB), + (ActorFunc)EnGeldB_Init, + (ActorFunc)EnGeldB_Destroy, + (ActorFunc)EnGeldB_Update, + (ActorFunc)EnGeldB_Draw, + NULL, +}; + +static ColliderCylinderInit sBodyCylInit = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 50, 0, { 0, 0, 0 } }, +}; + +static ColliderTrisElementInit sBlockTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC1FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -10.0f, 14.0f, 2.0f }, { -10.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC1FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -10.0f, -6.0f, 2.0f }, { 9.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, +}; + +static ColliderTrisInit sBlockTrisInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sBlockTrisElementsInit, +}; + +static ColliderQuadInit sSwordQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +typedef enum { + /* 0x0 */ GELDB_DMG_NORMAL, + /* 0x1 */ GELDB_DMG_STUN, + /* 0x6 */ GELDB_DMG_UNK_6 = 0x6, + /* 0xD */ GELDB_DMG_UNK_D = 0xD, + /* 0xE */ GELDB_DMG_UNK_E, + /* 0xF */ GELDB_DMG_FREEZE +} EnGeldBDamageEffects; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, GELDB_DMG_STUN), + /* Deku stick */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Slingshot */ DMG_ENTRY(1, GELDB_DMG_NORMAL), + /* Explosive */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Boomerang */ DMG_ENTRY(0, GELDB_DMG_STUN), + /* Normal arrow */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Hammer swing */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Hookshot */ DMG_ENTRY(0, GELDB_DMG_STUN), + /* Kokiri sword */ DMG_ENTRY(1, GELDB_DMG_NORMAL), + /* Master sword */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Giant's Knife */ DMG_ENTRY(4, GELDB_DMG_NORMAL), + /* Fire arrow */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Ice arrow */ DMG_ENTRY(2, GELDB_DMG_FREEZE), + /* Light arrow */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Unk arrow 1 */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Unk arrow 2 */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Unk arrow 3 */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Fire magic */ DMG_ENTRY(4, GELDB_DMG_UNK_E), + /* Ice magic */ DMG_ENTRY(0, GELDB_DMG_UNK_6), + /* Light magic */ DMG_ENTRY(3, GELDB_DMG_UNK_D), + /* Shield */ DMG_ENTRY(0, GELDB_DMG_NORMAL), + /* Mirror Ray */ DMG_ENTRY(0, GELDB_DMG_NORMAL), + /* Kokiri spin */ DMG_ENTRY(1, GELDB_DMG_NORMAL), + /* Giant spin */ DMG_ENTRY(4, GELDB_DMG_NORMAL), + /* Master spin */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Kokiri jump */ DMG_ENTRY(2, GELDB_DMG_NORMAL), + /* Giant jump */ DMG_ENTRY(8, GELDB_DMG_NORMAL), + /* Master jump */ DMG_ENTRY(4, GELDB_DMG_NORMAL), + /* Unknown 1 */ DMG_ENTRY(4, GELDB_DMG_NORMAL), + /* Unblockable */ DMG_ENTRY(0, GELDB_DMG_NORMAL), + /* Hammer jump */ DMG_ENTRY(4, GELDB_DMG_NORMAL), + /* Unknown 2 */ DMG_ENTRY(0, GELDB_DMG_NORMAL), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -3000, ICHAIN_STOP), +}; + +//static Vec3f sUnusedOffset = { 1100.0f, -700.0f, 0.0f }; + +void EnGeldB_SetupAction(EnGeldB* this, EnGeldBActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnGeldB_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EffectBlureInit1 blureInit; + EnGeldB* this = (EnGeldB*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + thisx->colChkInfo.damageTable = &sDamageTable; + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawFeet, 0.0f); + this->actor.colChkInfo.mass = MASS_HEAVY; + thisx->colChkInfo.health = 20; + thisx->colChkInfo.cylRadius = 50; + thisx->colChkInfo.cylHeight = 100; + thisx->naviEnemyId = 0x54; + this->keyFlag = thisx->params & 0xFF00; + thisx->params &= 0xFF; + this->blinkState = 0; + this->unkFloat = 10.0f; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGerudoRedSkel, &gGerudoRedNeutralAnim, this->jointTable, + this->morphTable, GELDB_LIMB_MAX); + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, thisx, &sBodyCylInit); + Collider_InitTris(globalCtx, &this->blockCollider); + Collider_SetTris(globalCtx, &this->blockCollider, thisx, &sBlockTrisInit, this->blockElements); + Collider_InitQuad(globalCtx, &this->swordCollider); + Collider_SetQuad(globalCtx, &this->swordCollider, thisx, &sSwordQuadInit); + blureInit.p1StartColor[0] = blureInit.p1StartColor[1] = blureInit.p1StartColor[2] = blureInit.p1StartColor[3] = + blureInit.p2StartColor[0] = blureInit.p2StartColor[1] = blureInit.p2StartColor[2] = blureInit.p1EndColor[0] = + blureInit.p1EndColor[1] = blureInit.p1EndColor[2] = blureInit.p2EndColor[0] = blureInit.p2EndColor[1] = + blureInit.p2EndColor[2] = 255; + blureInit.p2StartColor[3] = 64; + blureInit.p1EndColor[3] = blureInit.p2EndColor[3] = 0; + blureInit.elemDuration = 8; + blureInit.unkFlag = 0; + blureInit.calcMode = 2; + + Effect_Add(globalCtx, &this->blureIndex, EFFECT_BLURE1, 0, 0, &blureInit); + Actor_SetScale(thisx, 0.012499999f); + EnGeldB_SetupWait(this); + if ((this->keyFlag != 0) && Flags_GetCollectible(globalCtx, this->keyFlag >> 8)) { + Actor_Kill(thisx); + } +} + +void EnGeldB_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGeldB* this = (EnGeldB*)thisx; + + func_800F5B58(); + Effect_Delete(globalCtx, this->blureIndex); + Collider_DestroyTris(globalCtx, &this->blockCollider); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyQuad(globalCtx, &this->swordCollider); +} + +s32 EnGeldB_ReactToPlayer(GlobalContext* globalCtx, EnGeldB* this, s16 arg2) { + Player* player = GET_PLAYER(globalCtx); + Actor* thisx = &this->actor; + s16 angleToWall; + s16 angleToLink; + Actor* bomb; + + angleToWall = thisx->wallYaw - thisx->shape.rot.y; + angleToWall = ABS(angleToWall); + angleToLink = thisx->yawTowardsPlayer - thisx->shape.rot.y; + angleToLink = ABS(angleToLink); + + if (func_800354B4(globalCtx, thisx, 100.0f, 0x2710, 0x3E80, thisx->shape.rot.y)) { + if (player->swordAnimation == 0x11) { + EnGeldB_SetupSpinDodge(this, globalCtx); + return true; + } else if (globalCtx->gameplayFrames & 1) { + EnGeldB_SetupBlock(this); + return true; + } + } + if (func_800354B4(globalCtx, thisx, 100.0f, 0x5DC0, 0x2AA8, thisx->shape.rot.y)) { + thisx->shape.rot.y = thisx->world.rot.y = thisx->yawTowardsPlayer; + if ((thisx->bgCheckFlags & 8) && (ABS(angleToWall) < 0x2EE0) && (thisx->xzDistToPlayer < 90.0f)) { + EnGeldB_SetupJump(this); + return true; + } else if (player->swordAnimation == 0x11) { + EnGeldB_SetupSpinDodge(this, globalCtx); + return true; + } else if ((thisx->xzDistToPlayer < 90.0f) && (globalCtx->gameplayFrames & 1)) { + EnGeldB_SetupBlock(this); + return true; + } else { + EnGeldB_SetupRollBack(this); + return true; + } + } else if ((bomb = Actor_FindNearby(globalCtx, thisx, -1, ACTORCAT_EXPLOSIVE, 80.0f)) != NULL) { + thisx->shape.rot.y = thisx->world.rot.y = thisx->yawTowardsPlayer; + if (((thisx->bgCheckFlags & 8) && (angleToWall < 0x2EE0)) || (bomb->id == ACTOR_EN_BOM_CHU)) { + if ((bomb->id == ACTOR_EN_BOM_CHU) && (Actor_WorldDistXYZToActor(thisx, bomb) < 80.0f) && + ((s16)(thisx->shape.rot.y - (bomb->world.rot.y - 0x8000)) < 0x3E80)) { + EnGeldB_SetupJump(this); + return true; + } else { + EnGeldB_SetupSidestep(this, globalCtx); + return true; + } + } else { + EnGeldB_SetupRollBack(this); + return true; + } + } else if (arg2) { + if (angleToLink >= 0x1B58) { + EnGeldB_SetupSidestep(this, globalCtx); + return true; + } else { + s16 angleToFacingLink = player->actor.shape.rot.y - thisx->shape.rot.y; + + if ((thisx->xzDistToPlayer <= 45.0f) && !Actor_OtherIsTargeted(globalCtx, thisx) && + ((globalCtx->gameplayFrames & 7) || (ABS(angleToFacingLink) < 0x38E0))) { + EnGeldB_SetupSlash(this); + return true; + } else { + EnGeldB_SetupCircle(this); + return true; + } + } + } + return false; +} + +void EnGeldB_SetupWait(EnGeldB* this) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gGerudoRedJumpAnim, 0.0f); + this->actor.world.pos.y = this->actor.home.pos.y + 120.0f; + this->timer = 10; + this->invisible = true; + this->action = GELDB_WAIT; + this->actor.bgCheckFlags &= ~3; + this->actor.gravity = -2.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + EnGeldB_SetupAction(this, EnGeldB_Wait); +} + +void EnGeldB_Wait(EnGeldB* this, GlobalContext* globalCtx) { + if ((this->invisible && !Flags_GetSwitch(globalCtx, this->actor.home.rot.z)) || + this->actor.xzDistToPlayer > 300.0f) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.world.pos.y = this->actor.floorHeight + 120.0f; + } else { + this->invisible = false; + this->actor.shape.shadowScale = 90.0f; + func_800F5ACC(NA_BGM_MINI_BOSS); + } + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + this->skelAnime.playSpeed = 1.0f; + this->actor.world.pos.y = this->actor.floorHeight; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.focus.pos = this->actor.world.pos; + this->actor.bgCheckFlags &= ~2; + this->actor.velocity.y = 0.0f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + } + if (SkelAnime_Update(&this->skelAnime)) { + EnGeldB_SetupReady(this); + } +} + +void EnGeldB_SetupFlee(EnGeldB* this) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedJumpAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedJumpAnim, -2.0f, lastFrame, 0.0f, ANIMMODE_ONCE_INTERP, -4.0f); + this->timer = 20; + this->invisible = false; + this->action = GELDB_WAIT; + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnGeldB_SetupAction(this, EnGeldB_Flee); +} + +void EnGeldB_Flee(EnGeldB* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame == 10.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + } + if (this->skelAnime.curFrame == 2.0f) { + this->actor.gravity = 0.0f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + } + if (SkelAnime_Update(&this->skelAnime)) { + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight + 300.0f, 1.0f, 20.5f, 0.0f); + this->timer--; + if (this->timer == 0) { + Actor_Kill(&this->actor); + } + } +} + +void EnGeldB_SetupReady(EnGeldB* this) { + Animation_MorphToLoop(&this->skelAnime, &gGerudoRedNeutralAnim, -4.0f); + this->action = GELDB_READY; + this->timer = Rand_ZeroOne() * 10.0f + 5.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnGeldB_SetupAction(this, EnGeldB_Ready); +} + +void EnGeldB_Ready(EnGeldB* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 angleToLink; + + SkelAnime_Update(&this->skelAnime); + if (this->lookTimer != 0) { + angleToLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y - this->headRot.y; + if (ABS(angleToLink) > 0x2000) { + this->lookTimer--; + return; + } + this->lookTimer = 0; + } + angleToLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (!EnGeldB_DodgeRanged(globalCtx, this)) { + if (this->unkTimer != 0) { + this->unkTimer--; + + if (ABS(angleToLink) >= 0x1FFE) { + return; + } + this->unkTimer = 0; + } else if (EnGeldB_ReactToPlayer(globalCtx, this, 0)) { + return; + } + angleToLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + if ((this->actor.xzDistToPlayer < 100.0f) && (player->swordState != 0) && (ABS(angleToLink) >= 0x1F40)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnGeldB_SetupCircle(this); + } else if (--this->timer == 0) { + if (Actor_IsFacingPlayer(&this->actor, 30 * 0x10000 / 360)) { + if ((210.0f > this->actor.xzDistToPlayer) && (this->actor.xzDistToPlayer > 150.0f) && + (Rand_ZeroOne() < 0.3f)) { + if (Actor_OtherIsTargeted(globalCtx, &this->actor) || (Rand_ZeroOne() > 0.5f) || + (ABS(angleToLink) < 0x38E0)) { + EnGeldB_SetupRollForward(this); + } else { + EnGeldB_SetupSpinAttack(this); + } + } else if (Rand_ZeroOne() > 0.3f) { + EnGeldB_SetupAdvance(this, globalCtx); + } else { + EnGeldB_SetupCircle(this); + } + } else { + EnGeldB_SetupPivot(this); + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } + } + } +} + +void EnGeldB_SetupAdvance(EnGeldB* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedWalkAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedWalkAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f); + this->action = GELDB_ADVANCE; + EnGeldB_SetupAction(this, EnGeldB_Advance); +} + +void EnGeldB_Advance(EnGeldB* this, GlobalContext* globalCtx) { + s32 thisKeyFrame; + s32 prevKeyFrame; + s32 playSpeed; + s16 facingAngletoLink; + Player* player = GET_PLAYER(globalCtx); + + if (!EnGeldB_DodgeRanged(globalCtx, this)) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x2EE, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->actor.xzDistToPlayer <= 40.0f) { + Math_SmoothStepToF(&this->actor.speedXZ, -8.0f, 1.0f, 1.5f, 0.0f); + } else if (this->actor.xzDistToPlayer > 55.0f) { + Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 6.65f, 0.0f); + } + this->skelAnime.playSpeed = this->actor.speedXZ / 8.0f; + facingAngletoLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + facingAngletoLink = ABS(facingAngletoLink); + if ((this->actor.xzDistToPlayer < 150.0f) && (player->swordState != 0) && (facingAngletoLink >= 0x1F40)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (Rand_ZeroOne() > 0.7f) { + EnGeldB_SetupCircle(this); + return; + } + } + thisKeyFrame = (s32)this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + playSpeed = (f32)ABS(this->skelAnime.playSpeed); + if (!Actor_IsFacingPlayer(&this->actor, 0x11C7)) { + if (Rand_ZeroOne() > 0.5f) { + EnGeldB_SetupCircle(this); + } else { + EnGeldB_SetupReady(this); + } + } else if (this->actor.xzDistToPlayer < 90.0f) { + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && + (Rand_ZeroOne() > 0.03f || (this->actor.xzDistToPlayer <= 45.0f && facingAngletoLink < 0x38E0))) { + EnGeldB_SetupSlash(this); + } else if (Actor_OtherIsTargeted(globalCtx, &this->actor) && (Rand_ZeroOne() > 0.5f)) { + EnGeldB_SetupRollBack(this); + } else { + EnGeldB_SetupCircle(this); + } + } + if (!EnGeldB_ReactToPlayer(globalCtx, this, 0)) { + if ((210.0f > this->actor.xzDistToPlayer) && (this->actor.xzDistToPlayer > 150.0f) && + Actor_IsFacingPlayer(&this->actor, 0x71C)) { + if (Actor_IsTargeted(globalCtx, &this->actor)) { + if (Rand_ZeroOne() > 0.5f) { + EnGeldB_SetupRollForward(this); + } else { + EnGeldB_SetupSpinAttack(this); + } + } else { + EnGeldB_SetupCircle(this); + return; + } + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } + if (thisKeyFrame != (s32)this->skelAnime.curFrame) { + s32 temp = playSpeed + thisKeyFrame; + + if (((prevKeyFrame < 0) && (temp > 0)) || ((prevKeyFrame < 4) && (temp > 4))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_LAND); + } + } + } + } +} + +void EnGeldB_SetupRollForward(EnGeldB* this) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedFlipAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedFlipAnim, -1.0f, lastFrame, 0.0f, ANIMMODE_ONCE, -3.0f); + this->timer = 0; + this->invisible = true; + this->action = GELDB_ROLL_FORWARD; + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = 10.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + EnGeldB_SetupAction(this, EnGeldB_RollForward); +} + +void EnGeldB_RollForward(EnGeldB* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 facingAngleToLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if (SkelAnime_Update(&this->skelAnime)) { + this->invisible = false; + this->actor.speedXZ = 0.0f; + if (!Actor_IsFacingPlayer(&this->actor, 0x1554)) { + EnGeldB_SetupReady(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + if (ABS(facingAngleToLink) < 0x38E0) { + this->lookTimer = 20; + } + } else if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && + (Rand_ZeroOne() > 0.5f || (ABS(facingAngleToLink) < 0x3FFC))) { + EnGeldB_SetupSlash(this); + } else { + EnGeldB_SetupAdvance(this, globalCtx); + } + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } +} + +void EnGeldB_SetupPivot(EnGeldB* this) { + Animation_MorphToLoop(&this->skelAnime, &gGerudoRedSidestepAnim, -4.0f); + this->action = GELDB_PIVOT; + EnGeldB_SetupAction(this, EnGeldB_Pivot); +} + +void EnGeldB_Pivot(EnGeldB* this, GlobalContext* globalCtx) { + s16 angleToLink; + s16 turnRate; + f32 playSpeed; + + if (!EnGeldB_DodgeRanged(globalCtx, this) && !EnGeldB_ReactToPlayer(globalCtx, this, 0)) { + angleToLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + turnRate = (angleToLink > 0) ? ((angleToLink * 0.25f) + 2000.0f) : ((angleToLink * 0.25f) - 2000.0f); + this->actor.world.rot.y = this->actor.shape.rot.y += turnRate; + if (angleToLink > 0) { + playSpeed = turnRate * 0.5f; + playSpeed = CLAMP_MAX(playSpeed, 1.0f); + } else { + playSpeed = turnRate * 0.5f; + playSpeed = CLAMP_MIN(playSpeed, -1.0f); + } + this->skelAnime.playSpeed = -playSpeed; + SkelAnime_Update(&this->skelAnime); + if (Actor_IsFacingPlayer(&this->actor, 30 * 0x10000 / 360)) { + if (Rand_ZeroOne() > 0.8f) { + EnGeldB_SetupCircle(this); + } else { + EnGeldB_SetupAdvance(this, globalCtx); + } + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } + } +} + +void EnGeldB_SetupCircle(EnGeldB* this) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedSidestepAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedSidestepAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, 0.0f); + this->actor.speedXZ = Rand_CenteredFloat(12.0f); + this->actor.world.rot.y = this->actor.shape.rot.y; + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.5f; + this->timer = Rand_ZeroOne() * 30.0f + 30.0f; + this->action = GELDB_CIRCLE; + this->approachRate = 0.0f; + EnGeldB_SetupAction(this, EnGeldB_Circle); +} + +void EnGeldB_Circle(EnGeldB* this, GlobalContext* globalCtx) { + s16 angleBehindLink; + s16 phi_v1; + s32 nextKeyFrame; + s32 thisKeyFrame; + s32 pad; + s32 prevKeyFrame; + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + if (!EnGeldB_DodgeRanged(globalCtx, this) && !EnGeldB_ReactToPlayer(globalCtx, this, 0)) { + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3A98; + angleBehindLink = player->actor.shape.rot.y + 0x8000; + if (Math_SinS(angleBehindLink - this->actor.shape.rot.y) >= 0.0f) { + this->actor.speedXZ -= 0.25f; + if (this->actor.speedXZ < -8.0f) { + this->actor.speedXZ = -8.0f; + } + } else if (Math_SinS(angleBehindLink - this->actor.shape.rot.y) < 0.0f) { + this->actor.speedXZ += 0.25f; + if (this->actor.speedXZ > 8.0f) { + this->actor.speedXZ = 8.0f; + } + } + if ((this->actor.bgCheckFlags & 8) || !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, + this->actor.shape.rot.y + 0x3E80)) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + phi_v1 = this->actor.shape.rot.y + 0x3E80; + } else { + phi_v1 = this->actor.shape.rot.y - 0x3E80; + } + phi_v1 = this->actor.wallYaw - phi_v1; + } else { + this->actor.speedXZ *= -0.8f; + phi_v1 = 0; + } + if (ABS(phi_v1) > 0x4000) { + this->actor.speedXZ *= -0.8f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ += 0.5f; + } + } + } + if (this->actor.xzDistToPlayer <= 45.0f) { + Math_SmoothStepToF(&this->approachRate, -4.0f, 1.0f, 1.5f, 0.0f); + } else if (this->actor.xzDistToPlayer > 40.0f) { + Math_SmoothStepToF(&this->approachRate, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->approachRate, 0.0f, 1.0f, 6.65f, 0.0f); + } + if (this->approachRate != 0.0f) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->approachRate; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->approachRate; + } + if (ABS(this->approachRate) < ABS(this->actor.speedXZ)) { + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.5f; + } else { + this->skelAnime.playSpeed = -this->approachRate * 0.5f; + } + this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f); + + thisKeyFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + + prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + nextKeyFrame = (s32)ABS(this->skelAnime.playSpeed) + thisKeyFrame; + if ((thisKeyFrame != (s32)this->skelAnime.curFrame) && + ((prevKeyFrame < 0 && 0 < nextKeyFrame) || (prevKeyFrame < 5 && 5 < nextKeyFrame))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_LAND); + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } + if ((Math_CosS(angleBehindLink - this->actor.shape.rot.y) < -0.85f) && + !Actor_OtherIsTargeted(globalCtx, &this->actor) && (this->actor.xzDistToPlayer <= 45.0f)) { + EnGeldB_SetupSlash(this); + } else if (--this->timer == 0) { + if (Actor_OtherIsTargeted(globalCtx, &this->actor) && (Rand_ZeroOne() > 0.5f)) { + EnGeldB_SetupRollBack(this); + } else { + EnGeldB_SetupReady(this); + } + } + } +} + +void EnGeldB_SetupSpinDodge(EnGeldB* this, GlobalContext* globalCtx) { + s16 sp3E; + Player* player = GET_PLAYER(globalCtx); + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedSidestepAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedSidestepAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, 0.0f); + sp3E = player->actor.shape.rot.y; + if (Math_SinS(sp3E - this->actor.shape.rot.y) > 0.0f) { + this->actor.speedXZ = -10.0f; + } else if (Math_SinS(sp3E - this->actor.shape.rot.y) < 0.0f) { + this->actor.speedXZ = 10.0f; + } else if (Rand_ZeroOne() > 0.5f) { + this->actor.speedXZ = 10.0f; + } else { + this->actor.speedXZ = -10.0f; + } + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.5f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->timer = 6; + this->approachRate = 0.0f; + this->unkFloat = 0.0f; + this->action = GELDB_SPIN_DODGE; + + EnGeldB_SetupAction(this, EnGeldB_SpinDodge); +} + +void EnGeldB_SpinDodge(EnGeldB* this, GlobalContext* globalCtx) { + s16 phi_v1; + s32 thisKeyFrame; + s32 pad; + s32 lastKeyFrame; + s32 nextKeyFrame; + + this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x3A98; + if ((this->actor.bgCheckFlags & 8) || + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y + 0x3E80)) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + phi_v1 = this->actor.shape.rot.y + 0x3E80; + } else { + phi_v1 = this->actor.shape.rot.y - 0x3E80; + } + phi_v1 = this->actor.wallYaw - phi_v1; + } else { + this->actor.speedXZ *= -0.8f; + phi_v1 = 0; + } + if (ABS(phi_v1) > 0x4000) { + EnGeldB_SetupJump(this); + return; + } + } + if (this->actor.xzDistToPlayer <= 45.0f) { + Math_SmoothStepToF(&this->approachRate, -4.0f, 1.0f, 1.5f, 0.0f); + } else if (this->actor.xzDistToPlayer > 40.0f) { + Math_SmoothStepToF(&this->approachRate, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->approachRate, 0.0f, 1.0f, 6.65f, 0.0f); + } + if (this->approachRate != 0.0f) { + this->actor.world.pos.x += Math_SinS(this->actor.yawTowardsPlayer) * this->approachRate; + this->actor.world.pos.z += Math_CosS(this->actor.yawTowardsPlayer) * this->approachRate; + } + if (ABS(this->approachRate) < ABS(this->actor.speedXZ)) { + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.5f; + } else { + this->skelAnime.playSpeed = -this->approachRate * 0.5f; + } + this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f); + thisKeyFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + lastKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + nextKeyFrame = (s32)ABS(this->skelAnime.playSpeed) + thisKeyFrame; + if ((thisKeyFrame != (s32)this->skelAnime.curFrame) && + ((lastKeyFrame < 0 && 0 < nextKeyFrame) || (lastKeyFrame < 5 && 5 < nextKeyFrame))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_LAND); + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } + this->timer--; + if (this->timer == 0) { + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + if (!EnGeldB_DodgeRanged(globalCtx, this)) { + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && (this->actor.xzDistToPlayer <= 70.0f)) { + EnGeldB_SetupSlash(this); + } else { + EnGeldB_SetupRollBack(this); + } + } + } else { + if (this->actor.speedXZ >= 0.0f) { + this->actor.shape.rot.y += 0x4000; + } else { + this->actor.shape.rot.y -= 0x4000; + } + } +} + +void EnGeldB_SetupSlash(EnGeldB* this) { + Animation_PlayOnce(&this->skelAnime, &gGerudoRedSlashAnim); + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + this->action = GELDB_SLASH; + this->spinAttackState = 0; + this->actor.speedXZ = 0.0f; + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_GERUDOFT_BREATH); + EnGeldB_SetupAction(this, EnGeldB_Slash); +} + +void EnGeldB_Slash(EnGeldB* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 angleFacingLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + s16 angleToLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + angleFacingLink = ABS(angleFacingLink); + angleToLink = ABS(angleToLink); + + this->actor.speedXZ = 0.0f; + if ((s32)this->skelAnime.curFrame == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_ATTACK); + this->swordState = 1; + } else if ((s32)this->skelAnime.curFrame == 6) { + this->swordState = -1; + } + if (this->swordCollider.base.atFlags & AT_BOUNCED) { + this->swordState = -1; + this->swordCollider.base.atFlags &= ~(AT_HIT | AT_BOUNCED); + EnGeldB_SetupRollBack(this); + } else if (SkelAnime_Update(&this->skelAnime)) { + if (!Actor_IsFacingPlayer(&this->actor, 0x1554)) { + EnGeldB_SetupReady(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + if (angleToLink > 0x4000) { + this->lookTimer = 20; + } + } else if (Rand_ZeroOne() > 0.7f || (this->actor.xzDistToPlayer >= 120.0f)) { + EnGeldB_SetupReady(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } else { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (Rand_ZeroOne() > 0.7f) { + EnGeldB_SetupSidestep(this, globalCtx); + } else if (angleFacingLink <= 0x2710) { + if (angleToLink > 0x3E80) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnGeldB_SetupCircle(this); + } else { + EnGeldB_ReactToPlayer(globalCtx, this, 1); + } + } else { + EnGeldB_SetupCircle(this); + } + } + } +} + +void EnGeldB_SetupSpinAttack(EnGeldB* this) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedSpinAttackAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedSpinAttackAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE_INTERP, 0.0f); + this->swordCollider.base.atFlags &= ~(AT_HIT | AT_BOUNCED); + this->action = GELDB_SPIN_ATTACK; + this->spinAttackState = 0; + this->actor.speedXZ = 0.0f; + EnGeldB_SetupAction(this, EnGeldB_SpinAttack); +} + +void EnGeldB_SpinAttack(EnGeldB* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 angleFacingLink; + s16 angleToLink; + + if (this->spinAttackState < 2) { + if (this->swordCollider.base.atFlags & AT_BOUNCED) { + this->swordCollider.base.atFlags &= ~(AT_HIT | AT_BOUNCED); + this->spinAttackState = 1; + this->skelAnime.playSpeed = 1.5f; + } else if (this->swordCollider.base.atFlags & AT_HIT) { + this->swordCollider.base.atFlags &= ~AT_HIT; + if (&player->actor == this->swordCollider.base.at) { + func_8002F71C(globalCtx, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); + this->spinAttackState = 2; + func_8002DF54(globalCtx, &this->actor, 0x18); + Message_StartTextbox(globalCtx, 0x6003, &this->actor); + this->timer = 30; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_LAUGH); + return; + } + } + } + if ((s32)this->skelAnime.curFrame < 9) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + } else if ((s32)this->skelAnime.curFrame == 13) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + this->swordState = 1; + this->actor.speedXZ = 10.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_ATTACK); + } else if ((s32)this->skelAnime.curFrame == 21) { + this->actor.speedXZ = 0.0f; + } else if ((s32)this->skelAnime.curFrame == 24) { + this->swordState = -1; + } + if (SkelAnime_Update(&this->skelAnime) && (this->spinAttackState < 2)) { + if (!Actor_IsFacingPlayer(&this->actor, 0x1554)) { + EnGeldB_SetupReady(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + this->lookTimer = 46; + } else if (this->spinAttackState != 0) { + EnGeldB_SetupRollBack(this); + } else if (Rand_ZeroOne() > 0.7f || (this->actor.xzDistToPlayer >= 120.0f)) { + EnGeldB_SetupReady(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } else { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (Rand_ZeroOne() > 0.7f) { + EnGeldB_SetupSidestep(this, globalCtx); + } else { + angleFacingLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + angleFacingLink = ABS(angleFacingLink); + if (angleFacingLink <= 0x2710) { + angleToLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + angleToLink = ABS(angleToLink); + if (angleToLink > 0x3E80) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnGeldB_SetupCircle(this); + } else { + EnGeldB_ReactToPlayer(globalCtx, this, 1); + } + } else { + EnGeldB_SetupCircle(this); + } + } + } + } +} + +void EnGeldB_SetupRollBack(EnGeldB* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gGerudoRedFlipAnim, -3.0f); + this->timer = 0; + this->invisible = true; + this->action = GELDB_ROLL_BACK; + this->actor.speedXZ = -8.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnGeldB_SetupAction(this, EnGeldB_RollBack); +} + +void EnGeldB_RollBack(EnGeldB* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && (this->actor.xzDistToPlayer < 170.0f) && + (this->actor.xzDistToPlayer > 140.0f) && (Rand_ZeroOne() < 0.2f)) { + EnGeldB_SetupSpinAttack(this); + } else if (globalCtx->gameplayFrames & 1) { + EnGeldB_SetupSidestep(this, globalCtx); + } else { + EnGeldB_SetupReady(this); + } + } + if ((globalCtx->state.frames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } +} + +void EnGeldB_SetupStunned(EnGeldB* this) { + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = 0.0f; + } + if ((this->damageEffect != GELDB_DMG_FREEZE) || (this->action == GELDB_SPIN_ATTACK)) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gGerudoRedDamageAnim, 0.0f); + } + if (this->damageEffect == GELDB_DMG_FREEZE) { + this->iceTimer = 36; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->action = GELDB_STUNNED; + EnGeldB_SetupAction(this, EnGeldB_Stunned); +} + +void EnGeldB_Stunned(EnGeldB* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + this->invisible = false; + } + if ((this->actor.colorFilterTimer == 0) && (this->actor.bgCheckFlags & 1)) { + if (this->actor.colChkInfo.health == 0) { + EnGeldB_SetupDefeated(this); + } else { + EnGeldB_ReactToPlayer(globalCtx, this, 1); + } + } +} + +void EnGeldB_SetupDamaged(EnGeldB* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gGerudoRedDamageAnim, -4.0f); + if (this->actor.bgCheckFlags & 1) { + this->invisible = false; + this->actor.speedXZ = -4.0f; + } else { + this->invisible = true; + } + this->lookTimer = 0; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_DAMAGE); + this->action = GELDB_DAMAGED; + EnGeldB_SetupAction(this, EnGeldB_Damaged); +} + +void EnGeldB_Damaged(EnGeldB* this, GlobalContext* globalCtx) { + s16 angleToWall; + + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + this->invisible = false; + } + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x1194, 0); + if (!EnGeldB_DodgeRanged(globalCtx, this) && !EnGeldB_ReactToPlayer(globalCtx, this, 0) && + SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & 1)) { + angleToWall = this->actor.wallYaw - this->actor.shape.rot.y; + if ((this->actor.bgCheckFlags & 8) && (ABS(angleToWall) < 0x2EE0) && (this->actor.xzDistToPlayer < 90.0f)) { + EnGeldB_SetupJump(this); + } else if (!EnGeldB_DodgeRanged(globalCtx, this)) { + if ((this->actor.xzDistToPlayer <= 45.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) && + (globalCtx->gameplayFrames & 7)) { + EnGeldB_SetupSlash(this); + } else { + EnGeldB_SetupRollBack(this); + } + } + } +} + +void EnGeldB_SetupJump(EnGeldB* this) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedFlipAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedFlipAnim, -1.0f, lastFrame, 0.0f, ANIMMODE_ONCE, -3.0f); + this->timer = 0; + this->invisible = false; + this->action = GELDB_JUMP; + this->actor.speedXZ = 6.5f; + this->actor.velocity.y = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnGeldB_SetupAction(this, EnGeldB_Jump); +} + +void EnGeldB_Jump(EnGeldB* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + if (this->actor.velocity.y >= 5.0f) { + func_800355B8(globalCtx, &this->leftFootPos); + func_800355B8(globalCtx, &this->rightFootPos); + } + if (SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & 3)) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.shape.rot.x = 0; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = this->actor.floorHeight; + if (!Actor_OtherIsTargeted(globalCtx, &this->actor)) { + EnGeldB_SetupSlash(this); + } else { + EnGeldB_SetupReady(this); + } + } +} + +void EnGeldB_SetupBlock(EnGeldB* this) { + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedBlockAnim); + + if (this->swordState != 0) { + this->swordState = -1; + } + this->actor.speedXZ = 0.0f; + this->action = GELDB_BLOCK; + this->timer = (s32)Rand_CenteredFloat(10.0f) + 10; + Animation_Change(&this->skelAnime, &gGerudoRedBlockAnim, 0.0f, 0.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + EnGeldB_SetupAction(this, EnGeldB_Block); +} + +void EnGeldB_Block(EnGeldB* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 angleToLink; + s16 angleFacingLink; + + if (this->timer != 0) { + this->timer--; + } else { + this->skelAnime.playSpeed = 1.0f; + } + if (SkelAnime_Update(&this->skelAnime)) { + angleToLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((ABS(angleToLink) <= 0x4000) && (this->actor.xzDistToPlayer < 40.0f) && + (ABS(this->actor.yDistToPlayer) < 50.0f)) { + if (func_800354B4(globalCtx, &this->actor, 100.0f, 0x2710, 0x4000, this->actor.shape.rot.y)) { + if (player->swordAnimation == 0x11) { + EnGeldB_SetupSpinDodge(this, globalCtx); + } else if (globalCtx->gameplayFrames & 1) { + EnGeldB_SetupBlock(this); + } else { + EnGeldB_SetupRollBack(this); + } + } else { + angleFacingLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && + ((globalCtx->gameplayFrames & 1) || (ABS(angleFacingLink) < 0x38E0))) { + EnGeldB_SetupSlash(this); + } else { + EnGeldB_SetupCircle(this); + } + } + } else { + EnGeldB_SetupCircle(this); + } + } else if ((this->timer == 0) && + func_800354B4(globalCtx, &this->actor, 100.0f, 0x2710, 0x4000, this->actor.shape.rot.y)) { + if (player->swordAnimation == 0x11) { + EnGeldB_SetupSpinDodge(this, globalCtx); + } else if (!EnGeldB_DodgeRanged(globalCtx, this)) { + if ((globalCtx->gameplayFrames & 1)) { + if ((this->actor.xzDistToPlayer < 100.0f) && (Rand_ZeroOne() > 0.7f)) { + EnGeldB_SetupJump(this); + } else { + EnGeldB_SetupRollBack(this); + } + } else { + EnGeldB_SetupBlock(this); + } + } + } +} + +void EnGeldB_SetupSidestep(EnGeldB* this, GlobalContext* globalCtx) { + s16 linkAngle; + Player* player; + f32 lastFrame = Animation_GetLastFrame(&gGerudoRedSidestepAnim); + + Animation_Change(&this->skelAnime, &gGerudoRedSidestepAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, 0.0f); + player = GET_PLAYER(globalCtx); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + linkAngle = player->actor.shape.rot.y; + if (Math_SinS(linkAngle - this->actor.shape.rot.y) > 0.0f) { + this->actor.speedXZ = -6.0f; + } else if (Math_SinS(linkAngle - this->actor.shape.rot.y) < 0.0f) { + this->actor.speedXZ = 6.0f; + } else { + this->actor.speedXZ = Rand_CenteredFloat(12.0f); + } + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.5f; + this->approachRate = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + this->timer = Rand_ZeroOne() * 10.0f + 5.0f; + this->action = GELDB_SIDESTEP; + EnGeldB_SetupAction(this, EnGeldB_Sidestep); +} + +void EnGeldB_Sidestep(EnGeldB* this, GlobalContext* globalCtx) { + s16 behindLinkAngle; + s16 phi_v1; + Player* player = GET_PLAYER(globalCtx); + s32 thisKeyFrame; + s32 prevKeyFrame; + f32 playSpeed; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 1); + behindLinkAngle = player->actor.shape.rot.y + 0x8000; + if (Math_SinS(behindLinkAngle - this->actor.shape.rot.y) > 0.0f) { + this->actor.speedXZ += 0.125f; + } else if (Math_SinS(behindLinkAngle - this->actor.shape.rot.y) <= 0.0f) { + this->actor.speedXZ -= 0.125f; + } + + if ((this->actor.bgCheckFlags & 8) || + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y + 0x3E80)) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + phi_v1 = this->actor.shape.rot.y + 0x3E80; + } else { + phi_v1 = this->actor.shape.rot.y - 0x3E80; + } + phi_v1 = this->actor.wallYaw - phi_v1; + } else { + this->actor.speedXZ *= -0.8f; + phi_v1 = 0; + } + if (ABS(phi_v1) > 0x4000) { + this->actor.speedXZ *= -0.8f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ += 0.5f; + } + } + } + if (this->actor.speedXZ >= 0.0f) { + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3E80; + } else { + this->actor.world.rot.y = this->actor.shape.rot.y - 0x3E80; + } + if (this->actor.xzDistToPlayer <= 45.0f) { + Math_SmoothStepToF(&this->approachRate, -4.0f, 1.0f, 1.5f, 0.0f); + } else if (this->actor.xzDistToPlayer > 40.0f) { + Math_SmoothStepToF(&this->approachRate, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->approachRate, 0.0f, 1.0f, 6.65f, 0.0f); + } + if (this->approachRate != 0.0f) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->approachRate; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->approachRate; + } + if (ABS(this->approachRate) < ABS(this->actor.speedXZ)) { + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.5f; + } else { + this->skelAnime.playSpeed = -this->approachRate * 0.5f; + } + this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f); + thisKeyFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + + playSpeed = ((void)0, ABS(this->skelAnime.playSpeed)); // Needed to match for some reason + + if (!EnGeldB_DodgeRanged(globalCtx, this) && !EnGeldB_ReactToPlayer(globalCtx, this, 0)) { + if (--this->timer == 0) { + s16 angleFacingPlayer = player->actor.shape.rot.y - this->actor.shape.rot.y; + + angleFacingPlayer = ABS(angleFacingPlayer); + if (angleFacingPlayer >= 0x3A98) { + EnGeldB_SetupReady(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 1.0f; + } else { + Player* player2 = GET_PLAYER(globalCtx); + s16 angleFacingPlayer2 = player2->actor.shape.rot.y - this->actor.shape.rot.y; + + this->actor.world.rot.y = this->actor.shape.rot.y; + if ((this->actor.xzDistToPlayer <= 45.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) && + (!(globalCtx->gameplayFrames & 3) || (ABS(angleFacingPlayer2) < 0x38E0))) { + EnGeldB_SetupSlash(this); + } else if ((210.0f > this->actor.xzDistToPlayer) && (this->actor.xzDistToPlayer > 150.0f) && + !(globalCtx->gameplayFrames & 1)) { + if (Actor_OtherIsTargeted(globalCtx, &this->actor) || (Rand_ZeroOne() > 0.5f) || + (ABS(angleFacingPlayer2) < 0x38E0)) { + EnGeldB_SetupRollForward(this); + } else { + EnGeldB_SetupSpinAttack(this); + } + } else { + EnGeldB_SetupAdvance(this, globalCtx); + } + } + } + if ((thisKeyFrame != (s32)this->skelAnime.curFrame) && + (((prevKeyFrame < 0) && (((s32)playSpeed + thisKeyFrame) > 0)) || + ((prevKeyFrame < 5) && (((s32)playSpeed + thisKeyFrame) > 5)))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_LAND); + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_BREATH); + } + } +} + +void EnGeldB_SetupDefeated(EnGeldB* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gGerudoRedDefeatAnim, -4.0f); + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + if (this->actor.bgCheckFlags & 1) { + this->invisible = false; + this->actor.speedXZ = -6.0f; + } else { + this->invisible = true; + } + this->action = GELDB_DEFEAT; + this->actor.flags &= ~ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GERUDOFT_DEAD); + EnGeldB_SetupAction(this, EnGeldB_Defeated); +} + +void EnGeldB_Defeated(EnGeldB* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + if (this->actor.bgCheckFlags & 1) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + this->invisible = false; + } + if (SkelAnime_Update(&this->skelAnime)) { + EnGeldB_SetupFlee(this); + } else if ((s32)this->skelAnime.curFrame == 10) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + func_800F5B58(); + } +} + +void EnGeldB_TurnHead(EnGeldB* this, GlobalContext* globalCtx) { + if ((this->action == GELDB_READY) && (this->lookTimer != 0)) { + this->headRot.y = Math_SinS(this->lookTimer * 0x1068) * 8920.0f; + } else if (this->action != GELDB_STUNNED) { + if ((this->action != GELDB_SLASH) && (this->action != GELDB_SPIN_ATTACK)) { + Math_SmoothStepToS(&this->headRot.y, this->actor.yawTowardsPlayer - this->actor.shape.rot.y, 1, 0x1F4, 0); + this->headRot.y = CLAMP(this->headRot.y, -0x256F, 0x256F); + } else { + this->headRot.y = 0; + } + } +} + +void EnGeldB_CollisionCheck(EnGeldB* this, GlobalContext* globalCtx) { + s32 pad; + EnItem00* key; + + if (this->blockCollider.base.acFlags & AC_BOUNCED) { + this->blockCollider.base.acFlags &= ~AC_BOUNCED; + this->bodyCollider.base.acFlags &= ~AC_HIT; + } else if ((this->bodyCollider.base.acFlags & AC_HIT) && (this->action >= GELDB_READY) && + (this->spinAttackState < 2)) { + this->bodyCollider.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != GELDB_DMG_UNK_6) { + this->damageEffect = this->actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->actor, &this->bodyCollider.info, 1); + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_GERUDOFT_BREATH); + if ((this->actor.colChkInfo.damageEffect == GELDB_DMG_STUN) || + (this->actor.colChkInfo.damageEffect == GELDB_DMG_FREEZE)) { + if (this->action != GELDB_STUNNED) { + Actor_SetColorFilter(&this->actor, 0, 0x78, 0, 0x50); + Actor_ApplyDamage(&this->actor); + EnGeldB_SetupStunned(this); + } + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + if (Actor_ApplyDamage(&this->actor) == 0) { + if (this->keyFlag != 0) { + key = Item_DropCollectible(globalCtx, &this->actor.world.pos, this->keyFlag | ITEM00_SMALL_KEY); + if (key != NULL) { + key->actor.world.rot.y = Math_Vec3f_Yaw(&key->actor.world.pos, &this->actor.home.pos); + key->actor.speedXZ = 6.0f; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + EnGeldB_SetupDefeated(this); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + EnGeldB_SetupDamaged(this); + } + } + } + } +} + +void EnGeldB_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGeldB* this = (EnGeldB*)thisx; + + EnGeldB_CollisionCheck(this, globalCtx); + if (this->actor.colChkInfo.damageEffect != GELDB_DMG_UNK_6) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 15.0f, 30.0f, 60.0f, 0x1D); + this->actionFunc(this, globalCtx); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 40.0f; + EnGeldB_TurnHead(this, globalCtx); + } + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + if ((this->action >= GELDB_READY) && (this->spinAttackState < 2) && + ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000))) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + if ((this->action == GELDB_BLOCK) && (this->skelAnime.curFrame == 0.0f)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->blockCollider.base); + } + if (this->swordState > 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->swordCollider.base); + } + if (this->blinkState == 0) { + if ((Rand_ZeroOne() < 0.1f) && ((globalCtx->gameplayFrames % 4) == 0)) { + this->blinkState++; + } + } else { + this->blinkState = (this->blinkState + 1) & 3; + } +} + +s32 EnGeldB_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnGeldB* this = (EnGeldB*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_geldB.c", 2507); + if (limbIndex == GELDB_LIMB_NECK) { + rot->z += this->headRot.x; + rot->x += this->headRot.y; + rot->y += this->headRot.z; + } else if (limbIndex == GELDB_LIMB_HEAD) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 80, 60, 10, 255); + } else if ((limbIndex == GELDB_LIMB_R_SWORD) || (limbIndex == GELDB_LIMB_L_SWORD)) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 140, 170, 230, 255); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + } else { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 140, 0, 0, 255); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_geldB.c", 2529); + return false; +} + +void EnGeldB_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f footOffset = { 300.0f, 0.0f, 0.0f }; + static Vec3f swordTipOffset = { 0.0f, -3000.0f, 0.0f }; + static Vec3f swordHiltOffset = { 400.0f, 0.0f, 0.0f }; + static Vec3f swordQuadOffset1 = { 1600.0f, -4000.0f, 0.0f }; + static Vec3f swordQuadOffset0 = { -3000.0f, -2000.0f, 1300.0f }; + static Vec3f swordQuadOffset3 = { -3000.0f, -2000.0f, -1300.0f }; + static Vec3f swordQuadOffset2 = { 1000.0f, 1000.0f, 0.0f }; + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f swordTip; + Vec3f swordHilt; + EnGeldB* this = (EnGeldB*)thisx; + s32 bodyPart = -1; + + if (limbIndex == GELDB_LIMB_R_SWORD) { + Matrix_MultVec3f(&swordQuadOffset1, &this->swordCollider.dim.quad[1]); + Matrix_MultVec3f(&swordQuadOffset0, &this->swordCollider.dim.quad[0]); + Matrix_MultVec3f(&swordQuadOffset3, &this->swordCollider.dim.quad[3]); + Matrix_MultVec3f(&swordQuadOffset2, &this->swordCollider.dim.quad[2]); + Collider_SetQuadVertices(&this->swordCollider, &this->swordCollider.dim.quad[0], + &this->swordCollider.dim.quad[1], &this->swordCollider.dim.quad[2], + &this->swordCollider.dim.quad[3]); + Matrix_MultVec3f(&swordTipOffset, &swordTip); + Matrix_MultVec3f(&swordHiltOffset, &swordHilt); + + if ((this->swordState < 0) || ((this->action != GELDB_SLASH) && (this->action != GELDB_SPIN_ATTACK))) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIndex)); + this->swordState = 0; + } else if (this->swordState > 0) { + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIndex), &swordTip, &swordHilt); + } + } else { + Actor_SetFeetPos(&this->actor, limbIndex, GELDB_LIMB_L_FOOT, &footOffset, GELDB_LIMB_R_FOOT, &footOffset); + } + + if (limbIndex == GELDB_LIMB_L_FOOT) { + Matrix_MultVec3f(&footOffset, &this->leftFootPos); + } else if (limbIndex == GELDB_LIMB_R_FOOT) { + Matrix_MultVec3f(&footOffset, &this->rightFootPos); + } + + if (this->iceTimer != 0) { + switch (limbIndex) { + case GELDB_LIMB_NECK: + bodyPart = 0; + break; + case GELDB_LIMB_L_SWORD: + bodyPart = 1; + break; + case GELDB_LIMB_R_SWORD: + bodyPart = 2; + break; + case GELDB_LIMB_L_UPPER_ARM: + bodyPart = 3; + break; + case GELDB_LIMB_R_UPPER_ARM: + bodyPart = 4; + break; + case GELDB_LIMB_TORSO: + bodyPart = 5; + break; + case GELDB_LIMB_WAIST: + bodyPart = 6; + break; + case GELDB_LIMB_L_FOOT: + bodyPart = 7; + break; + case GELDB_LIMB_R_FOOT: + bodyPart = 8; + break; + default: + break; + } + + if (bodyPart >= 0) { + Vec3f limbPos; + + Matrix_MultVec3f(&zeroVec, &limbPos); + this->bodyPartsPos[bodyPart].x = limbPos.x; + this->bodyPartsPos[bodyPart].y = limbPos.y; + this->bodyPartsPos[bodyPart].z = limbPos.z; + } + } +} + +void EnGeldB_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f blockTrisOffsets0[3] = { + { -3000.0f, 6000.0f, 1600.0f }, + { -3000.0f, 0.0f, 1600.0f }, + { 3000.0f, 6000.0f, 1600.0f }, + }; + static Vec3f blockTrisOffsets1[3] = { + { -3000.0f, 0.0f, 1600.0f }, + { 3000.0f, 0.0f, 1600.0f }, + { 3000.0f, 6000.0f, 1600.0f }, + }; + static void* eyeTextures[] = { gGerudoRedEyeOpenTex, gGerudoRedEyeHalfTex, gGerudoRedEyeShutTex, + gGerudoRedEyeHalfTex }; + s32 pad; + EnGeldB* this = (EnGeldB*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_geldB.c", 2672); + if (1) {} + + if ((this->spinAttackState >= 2) && SkelAnime_Update(&this->skelAnime)) { + if (this->spinAttackState == 2) { + Animation_Change(&this->skelAnime, &gGerudoRedSpinAttackAnim, 0.5f, 0.0f, 12.0f, ANIMMODE_ONCE_INTERP, + 4.0f); + this->spinAttackState++; + thisx->world.rot.y = thisx->shape.rot.y = thisx->yawTowardsPlayer; + } else { + this->timer--; + if (this->timer == 0) { + if ((INV_CONTENT(ITEM_HOOKSHOT) == ITEM_NONE) || (INV_CONTENT(ITEM_LONGSHOT) == ITEM_NONE)) { + globalCtx->nextEntranceIndex = 0x1A5; + } else if (gSaveContext.eventChkInf[12] & 0x80) { + globalCtx->nextEntranceIndex = 0x5F8; + } else { + globalCtx->nextEntranceIndex = 0x3B4; + } + globalCtx->fadeTransition = 0x26; + globalCtx->sceneLoadFlag = 0x14; + } + } + } + + if ((this->action != GELDB_WAIT) || !this->invisible) { + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->blinkState])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnGeldB_OverrideLimbDraw, EnGeldB_PostLimbDraw, this); + if (this->action == GELDB_BLOCK) { + s32 i; + Vec3f blockTrisVtx0[3]; + Vec3f blockTrisVtx1[3]; + + for (i = 0; i < 3; i++) { + Matrix_MultVec3f(&blockTrisOffsets0[i], &blockTrisVtx0[i]); + Matrix_MultVec3f(&blockTrisOffsets1[i], &blockTrisVtx1[i]); + } + Collider_SetTrisVertices(&this->blockCollider, 0, &blockTrisVtx0[0], &blockTrisVtx0[1], &blockTrisVtx0[2]); + Collider_SetTrisVertices(&this->blockCollider, 1, &blockTrisVtx1[0], &blockTrisVtx1[1], &blockTrisVtx1[2]); + } + + if (this->iceTimer != 0) { + thisx->colorFilterTimer++; + this->iceTimer--; + if ((this->iceTimer % 4) == 0) { + s32 iceIndex = this->iceTimer >> 2; + + EffectSsEnIce_SpawnFlyingVec3s(globalCtx, thisx, &this->bodyPartsPos[iceIndex], 150, 150, 150, 250, 235, + 245, 255, 1.5f); + } + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_geldB.c", 2744); +} + +s32 EnGeldB_DodgeRanged(GlobalContext* globalCtx, EnGeldB* this) { + Actor* actor = Actor_GetProjectileActor(globalCtx, &this->actor, 800.0f); + + if (actor != NULL) { + s16 angleToFacing; + s16 pad18; + f32 dist; + + angleToFacing = Actor_WorldYawTowardActor(&this->actor, actor) - this->actor.shape.rot.y; + this->actor.world.rot.y = (u16)this->actor.shape.rot.y & 0xFFFF; + dist = Actor_WorldDistXYZToPoint(&this->actor, &actor->world.pos); + //! @bug + // Actor_WorldDistXYZToPoint already sqrtfs the distance, so this actually checks for a + // distance of 360000. Also it's a double calculation because no f on sqrt. + if ((ABS(angleToFacing) < 0x2EE0) && (sqrt(dist) < 600.0)) { + if (actor->id == ACTOR_ARMS_HOOK) { + EnGeldB_SetupJump(this); + } else { + EnGeldB_SetupBlock(this); + } + } else { + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + if ((ABS(angleToFacing) < 0x2000) || (ABS(angleToFacing) > 0x5FFF)) { + EnGeldB_SetupSidestep(this, globalCtx); + this->actor.speedXZ *= 3.0f; + } else if (ABS(angleToFacing) < 0x5FFF) { + EnGeldB_SetupRollBack(this); + } + } + return true; + } + return false; +} diff --git a/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.h b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.h new file mode 100644 index 000000000..b4c3c3e84 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_GeldB/z_en_geldb.h @@ -0,0 +1,72 @@ +#ifndef Z_EN_GELDB_H +#define Z_EN_GELDB_H + +#include "ultra64.h" +#include "global.h" + +struct EnGeldB; + +typedef enum { + /* 0x00 */ GELDB_LIMB_NONE, + /* 0x01 */ GELDB_LIMB_ROOT, + /* 0x02 */ GELDB_LIMB_TORSO, + /* 0x03 */ GELDB_LIMB_NECK, + /* 0x04 */ GELDB_LIMB_PONYTAIL, + /* 0x05 */ GELDB_LIMB_VEIL, + /* 0x06 */ GELDB_LIMB_HEAD, + /* 0x07 */ GELDB_LIMB_R_UPPER_ARM, + /* 0x08 */ GELDB_LIMB_R_FOREARM, + /* 0x09 */ GELDB_LIMB_R_WRIST, + /* 0x0A */ GELDB_LIMB_R_HAND, + /* 0x0B */ GELDB_LIMB_R_SWORD, + /* 0x0C */ GELDB_LIMB_L_UPPER_ARM, + /* 0x0D */ GELDB_LIMB_L_FOREARM, + /* 0x0E */ GELDB_LIMB_L_WRIST, + /* 0x0F */ GELDB_LIMB_L_HAND, + /* 0x10 */ GELDB_LIMB_L_SWORD, + /* 0x11 */ GELDB_LIMB_L_THIGH, + /* 0x12 */ GELDB_LIMB_L_SHIN, + /* 0x13 */ GELDB_LIMB_L_FOOT, + /* 0x14 */ GELDB_LIMB_R_THIGH, + /* 0x15 */ GELDB_LIMB_R_SHIN, + /* 0x16 */ GELDB_LIMB_R_FOOT, + /* 0x17 */ GELDB_LIMB_WAIST, + /* 0x18 */ GELDB_LIMB_MAX +} EnGeldBLimb; + +typedef void (*EnGeldBActionFunc)(struct EnGeldB*, GlobalContext*); + +typedef struct EnGeldB { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3s bodyPartsPos[10]; + /* 0x0188 */ SkelAnime skelAnime; + /* 0x01CC */ Vec3s jointTable[GELDB_LIMB_MAX]; + /* 0x025C */ Vec3s morphTable[GELDB_LIMB_MAX]; + /* 0x02EC */ s32 action; + /* 0x02F0 */ char unk_2F0[4]; + /* 0x02F4 */ EnGeldBActionFunc actionFunc; + /* 0x02F8 */ s16 unkTimer; + /* 0x02FA */ s16 lookTimer; + /* 0x02FC */ s16 iceTimer; + /* 0x02FE */ u8 damageEffect; + /* 0x0300 */ s32 timer; + /* 0x0304 */ f32 approachRate; + /* 0x0308 */ char unk_308[4]; + /* 0x030C */ f32 unkFloat; + /* 0x0310 */ s16 swordState; + /* 0x0312 */ s16 spinAttackState; + /* 0x0314 */ s16 keyFlag; + /* 0x0316 */ char unk_316[2]; + /* 0x0318 */ s16 invisible; + /* 0x031A */ u8 blinkState; + /* 0x031C */ s32 blureIndex; + /* 0x0320 */ ColliderCylinder bodyCollider; + /* 0x036C */ ColliderQuad swordCollider; + /* 0x03EC */ ColliderTris blockCollider; + /* 0x040C */ ColliderTrisElement blockElements[2]; + /* 0x04C4 */ Vec3f rightFootPos; + /* 0x04D0 */ Vec3f leftFootPos; + /* 0x04DC */ Vec3s headRot; +} EnGeldB; // size = 0x04E4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c new file mode 100644 index 000000000..e76b258e3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -0,0 +1,1111 @@ +/* + * File: z_en_girla.c + * Overlay: En_GirlA + * Description: Shop Items + */ + +#include "z_en_girla.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnGirlA_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGirlA_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGirlA_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnGirlA_SetItemOutOfStock(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_UpdateStockedItem(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_InitializeItemAction(EnGirlA* this, GlobalContext* globalCtx); +void EnGirlA_Update2(EnGirlA* this, GlobalContext* globalCtx); +void func_80A3C498(Actor* thisx, GlobalContext* globalCtx, s32 flags); +void EnGirlA_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnGirlA_CanBuy_Arrows(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Bombs(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_DekuNuts(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_DekuSticks(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Fish(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_RedPotion(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_GreenPotion(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_BluePotion(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Longsword(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_HylianShield(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_DekuShield(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_GoronTunic(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Health(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_MilkBottle(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_WeirdEgg(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Unk19(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Unk20(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Bombchus(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_DekuSeeds(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_SoldOut(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_BlueFire(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Bugs(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Poe(GlobalContext* globalCtx, EnGirlA* this); +s32 EnGirlA_CanBuy_Fairy(GlobalContext* globalCtx, EnGirlA* this); + +void EnGirlA_ItemGive_DekuNuts(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_Arrows(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_Bombs(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_DekuSticks(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_BottledItem(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_Longsword(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_HylianShield(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_DekuShield(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_GoronTunic(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_Health(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_MilkBottle(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_WeirdEgg(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_Unk19(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_Unk20(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_ItemGive_DekuSeeds(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_BuyEvent_ShieldDiscount(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_BuyEvent_GoronTunic(GlobalContext* globalCtx, EnGirlA* this); +void EnGirlA_BuyEvent_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this); + +const ActorInit En_GirlA_InitVars = { + ACTOR_EN_GIRLA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnGirlA), + (ActorFunc)EnGirlA_Init, + (ActorFunc)EnGirlA_Destroy, + (ActorFunc)EnGirlA_Update, + NULL, + NULL, +}; + +static char* sShopItemDescriptions[] = { + "デクの実×5 ", // "Deku nut x5" + "矢×30 ", // "Arrow x30" + "矢×50 ", // "Arrow x50" + "爆弾×5 ", // "bomb" + "デクの実×10 ", // "Deku nut x10" + "デクの棒 ", // "Deku stick" + "爆弾×10 ", // "Bomb x10" + "さかな ", // "Fish" + "赤クスリ ", // "Red medicine" + "緑クスリ ", // "Green medicine" + "青クスリ ", // "Blue medicine" + "巨人のナイフ ", // "Giant knife" + "ハイリアの盾 ", // "Hyria Shield" + "デクの盾 ", // "Deku Shield" + "ゴロンの服 ", // "Goron's clothes" + "ゾ─ラの服 ", // "Zora's clothes" + "回復のハート ", // "Heart of recovery" + "ロンロン牛乳 ", // "Ron Ron milk" + "鶏の卵 ", // "Chicken egg" + "インゴー牛乳 ", // "Ingo milk" + "インゴー卵 ", // "Ingo egg" + "もだえ石 ", // "Modae stone" + "大人の財布 ", // "Adult wallet" + "ハートの欠片 ", // "Heart fragment" + "ボムチュウ ", // "Bombchu" + "ボムチュウ ", // "Bombchu" + "ボムチュウ ", // "Bombchu" + "ボムチュウ ", // "Bombchu" + "ボムチュウ ", // "Bombchu" + "デクのタネ ", // "Deku seeds" + "キータンのお面", // "Ketan's mask" + "こわそなお面 ", // "Scary face" + "ドクロのお面 ", // "Skull mask" + "ウサギずきん ", // "Rabbit hood" + "まことの仮面 ", // "True mask" + "ゾーラのお面 ", // "Zora's mask" + "ゴロンのお面 ", // "Goron's mask" + "ゲルドのお面 ", // "Gerd's mask" + "SOLDOUT", + "炎 ", // "Flame" + "虫 ", // "Bugs" + "チョウチョ ", // "Butterfly" + "ポウ ", // "Poe" + "妖精の魂 ", // "Fairy soul" + "矢×10 ", // "Arrow" + "爆弾×20 ", // "Bomb x20" + "爆弾×30 ", // "Bomb x30" + "爆弾×5 ", // "Bomb x5" + "赤クスリ ", // "Red medicine" + "赤クスリ " // "Red medicine" +}; + +static s16 sMaskShopItems[8] = { + ITEM_MASK_KEATON, ITEM_MASK_SPOOKY, ITEM_MASK_SKULL, ITEM_MASK_BUNNY, + ITEM_MASK_TRUTH, ITEM_MASK_ZORA, ITEM_MASK_GORON, ITEM_MASK_GERUDO, +}; + +static u16 sMaskShopFreeToBorrowTextIds[5] = { 0x70B6, 0x70B5, 0x70B4, 0x70B7, 0x70BB }; + +typedef struct { + /* 0x00 */ s16 objID; + /* 0x02 */ s16 giDrawId; + /* 0x04 */ void (*hiliteFunc)(Actor*, GlobalContext*, s32); + /* 0x08 */ s16 price; + /* 0x0A */ s16 count; + /* 0x0C */ u16 itemDescTextId; + /* 0x0C */ u16 itemBuyPromptTextId; + /* 0x10 */ s32 getItemId; + /* 0x14 */ s32 (*canBuyFunc)(GlobalContext*, EnGirlA*); + /* 0x18 */ void (*itemGiveFunc)(GlobalContext*, EnGirlA*); + /* 0x1C */ void (*buyEventFunc)(GlobalContext*, EnGirlA*); +} ShopItemEntry; // size = 0x20 + +static ShopItemEntry shopItemEntries[] = { + // SI_DEKU_NUTS_5 + { OBJECT_GI_NUTS, GID_NUTS, func_8002ED80, 15, 5, 0x00B2, 0x007F, GI_NUTS_5_2, EnGirlA_CanBuy_DekuNuts, + EnGirlA_ItemGive_DekuNuts, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_ARROWS_30 */ + { OBJECT_GI_ARROW, GID_ARROWS_MEDIUM, func_8002EBCC, 60, 30, 0x00C1, 0x009B, GI_ARROWS_MEDIUM, + EnGirlA_CanBuy_Arrows, EnGirlA_ItemGive_Arrows, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_ARROWS_50 */ + { OBJECT_GI_ARROW, GID_ARROWS_LARGE, func_8002EBCC, 90, 50, 0x00B0, 0x007D, GI_ARROWS_LARGE, EnGirlA_CanBuy_Arrows, + EnGirlA_ItemGive_Arrows, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BOMBS_5_R25 */ + { OBJECT_GI_BOMB_1, GID_BOMB, func_8002EBCC, 25, 5, 0x00A3, 0x008B, GI_BOMBS_5, EnGirlA_CanBuy_Bombs, + EnGirlA_ItemGive_Bombs, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_DEKU_NUTS_10 */ + { OBJECT_GI_NUTS, GID_NUTS, func_8002ED80, 30, 10, 0x00A2, 0x0087, GI_NUTS_10, EnGirlA_CanBuy_DekuNuts, + EnGirlA_ItemGive_DekuNuts, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_DEKU_STICK */ + { OBJECT_GI_STICK, GID_STICK, NULL, 10, 1, 0x00A1, 0x0088, GI_STICKS_1, EnGirlA_CanBuy_DekuSticks, + EnGirlA_ItemGive_DekuSticks, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BOMBS_10 */ + { OBJECT_GI_BOMB_1, GID_BOMB, func_8002EBCC, 50, 10, 0x00B1, 0x007C, GI_BOMBS_10, EnGirlA_CanBuy_Bombs, + EnGirlA_ItemGive_Bombs, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_FISH */ + { OBJECT_GI_FISH, GID_FISH, func_8002ED80, 200, 1, 0x00B3, 0x007E, GI_FISH, EnGirlA_CanBuy_Fish, NULL, + EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_RED_POTION_R30 */ + { OBJECT_GI_LIQUID, GID_POTION_RED, func_8002EBCC, 30, 1, 0x00A5, 0x008E, GI_POTION_RED, EnGirlA_CanBuy_RedPotion, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_GREEN_POTION */ + { OBJECT_GI_LIQUID, GID_POTION_GREEN, func_8002EBCC, 30, 1, 0x00A6, 0x008F, GI_POTION_GREEN, + EnGirlA_CanBuy_GreenPotion, EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BLUE_POTION */ + { OBJECT_GI_LIQUID, GID_POTION_BLUE, func_8002EBCC, 60, 1, 0x00A7, 0x0090, GI_POTION_BLUE, + EnGirlA_CanBuy_BluePotion, EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_LONGSWORD */ + { OBJECT_GI_LONGSWORD, GID_SWORD_BGS, func_8002EBCC, 1000, 1, 0x00A8, 0x0091, GI_SWORD_KNIFE, + EnGirlA_CanBuy_Longsword, EnGirlA_ItemGive_Longsword, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_HYLIAN_SHIELD */ + { OBJECT_GI_SHIELD_2, GID_SHIELD_HYLIAN, func_8002EBCC, 80, 1, 0x00A9, 0x0092, GI_SHIELD_HYLIAN, + EnGirlA_CanBuy_HylianShield, EnGirlA_ItemGive_HylianShield, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_DEKU_SHIELD */ + { OBJECT_GI_SHIELD_1, GID_SHIELD_DEKU, func_8002EBCC, 40, 1, 0x009F, 0x0089, GI_SHIELD_DEKU, + EnGirlA_CanBuy_DekuShield, EnGirlA_ItemGive_DekuShield, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_GORON_TUNIC */ + { OBJECT_GI_CLOTHES, GID_TUNIC_GORON, NULL, 200, 1, 0x00AA, 0x0093, GI_TUNIC_GORON, EnGirlA_CanBuy_GoronTunic, + EnGirlA_ItemGive_GoronTunic, EnGirlA_BuyEvent_GoronTunic }, + /* SI_ZORA_TUNIC */ + { OBJECT_GI_CLOTHES, GID_TUNIC_ZORA, NULL, 300, 1, 0x00AB, 0x0094, GI_TUNIC_ZORA, EnGirlA_CanBuy_ZoraTunic, + EnGirlA_ItemGive_ZoraTunic, EnGirlA_BuyEvent_ZoraTunic }, + /* SI_HEART */ + { OBJECT_GI_HEART, GID_HEART, NULL, 10, 16, 0x00AC, 0x0095, GI_HEART, EnGirlA_CanBuy_Health, + EnGirlA_ItemGive_Health, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_MILK_BOTTLE */ + { OBJECT_GI_MILK, GID_MILK, func_80A3C498, 100, 1, 0x00AD, 0x0097, GI_MILK_BOTTLE, EnGirlA_CanBuy_MilkBottle, + EnGirlA_ItemGive_MilkBottle, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_WEIRD_EGG */ + { OBJECT_GI_EGG, GID_EGG, func_8002EBCC, 100, 1, 0x00AE, 0x0099, GI_WEIRD_EGG, EnGirlA_CanBuy_WeirdEgg, + EnGirlA_ItemGive_WeirdEgg, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_19 */ + { OBJECT_GI_MILK, GID_MILK, func_80A3C498, 10000, 1, 0x00B4, 0x0085, GI_NONE, EnGirlA_CanBuy_Unk19, + EnGirlA_ItemGive_Unk19, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_20 */ + { OBJECT_GI_EGG, GID_EGG, func_8002EBCC, 10000, 1, 0x00B5, 0x0085, GI_NONE, EnGirlA_CanBuy_Unk20, + EnGirlA_ItemGive_Unk20, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BOMBCHU_10_1 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 100, 10, 0x00BC, 0x008C, GI_BOMBCHUS_10, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_20_1 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 180, 20, 0x0061, 0x002A, GI_BOMBCHUS_20, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_20_2 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 180, 20, 0x0061, 0x002A, GI_BOMBCHUS_20, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_10_2 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 100, 10, 0x00BC, 0x008C, GI_BOMBCHUS_10, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_10_3 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 100, 10, 0x00BC, 0x008C, GI_BOMBCHUS_10, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_20_3 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 180, 20, 0x0061, 0x002A, GI_BOMBCHUS_20, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_20_4 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 180, 20, 0x0061, 0x002A, GI_BOMBCHUS_20, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_BOMBCHU_10_4 */ + { OBJECT_GI_BOMB_2, GID_BOMBCHU, func_8002EBCC, 100, 10, 0x00BC, 0x008C, GI_BOMBCHUS_10, EnGirlA_CanBuy_Bombchus, + NULL, EnGirlA_BuyEvent_ObtainBombchuPack }, + /* SI_DEKU_SEEDS_30 */ + { OBJECT_GI_SEED, GID_SEEDS, func_8002EBCC, 30, 30, 0x00DF, 0x00DE, GI_SEEDS_30, EnGirlA_CanBuy_DekuSeeds, + EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_KEATON_MASK */ + { OBJECT_GI_KI_TAN_MASK, GID_MASK_KEATON, func_8002EBCC, 0, 1, 0x70B2, 0x70BE, GI_MASK_KEATON, + EnGirlA_CanBuy_DekuSeeds, EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_SPOOKY_MASK */ + { OBJECT_GI_REDEAD_MASK, GID_MASK_SPOOKY, func_8002EBCC, 0, 1, 0x70B1, 0x70BD, GI_MASK_SPOOKY, + EnGirlA_CanBuy_DekuSeeds, EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_SKULL_MASK */ + { OBJECT_GI_SKJ_MASK, GID_MASK_SKULL, func_8002EBCC, 0, 1, 0x70B0, 0x70BC, GI_MASK_SKULL, EnGirlA_CanBuy_DekuSeeds, + EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BUNNY_HOOD */ + { OBJECT_GI_RABIT_MASK, GID_MASK_BUNNY, func_8002EBCC, 0, 1, 0x70B3, 0x70BF, GI_MASK_BUNNY, + EnGirlA_CanBuy_DekuSeeds, EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_MASK_OF_TRUTH */ + { OBJECT_GI_TRUTH_MASK, GID_MASK_TRUTH, func_80A3C498, 0, 1, 0x70AF, 0x70C3, GI_MASK_TRUTH, + EnGirlA_CanBuy_DekuSeeds, EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_ZORA_MASK */ + { OBJECT_GI_ZORAMASK, GID_MASK_ZORA, NULL, 0, 1, 0x70B9, 0x70C1, GI_MASK_ZORA, EnGirlA_CanBuy_DekuSeeds, + EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_GORON_MASK */ + { OBJECT_GI_GOLONMASK, GID_MASK_GORON, NULL, 0, 1, 0x70B8, 0x70C0, GI_MASK_GORON, EnGirlA_CanBuy_DekuSeeds, + EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_GERUDO_MASK */ + { OBJECT_GI_GERUDOMASK, GID_MASK_GERUDO, NULL, 0, 1, 0x70BA, 0x70C2, GI_MASK_GERUDO, EnGirlA_CanBuy_DekuSeeds, + EnGirlA_ItemGive_DekuSeeds, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_SOLD_OUT */ + { OBJECT_GI_SOLDOUT, GID_SOLDOUT, func_8002EBCC, 0, 0, 0x00BD, 0x70C2, GI_MASK_GERUDO, EnGirlA_CanBuy_SoldOut, NULL, + NULL }, + /* SI_BLUE_FIRE */ + { OBJECT_GI_FIRE, GID_BLUE_FIRE, func_8002EBCC, 300, 1, 0x00B9, 0x00B8, GI_BLUE_FIRE, EnGirlA_CanBuy_BlueFire, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BUGS */ + { OBJECT_GI_INSECT, GID_BUG, func_80A3C498, 50, 1, 0x00BB, 0x00BA, GI_BUGS, EnGirlA_CanBuy_Bugs, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BIG_POE */ + { OBJECT_GI_GHOST, GID_BIG_POE, func_80A3C498, 50, 1, 0x506F, 0x5070, GI_BIG_POE, EnGirlA_CanBuy_Poe, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_POE */ + { OBJECT_GI_GHOST, GID_POE, func_80A3C498, 30, 1, 0x506D, 0x506E, GI_POE, EnGirlA_CanBuy_Poe, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_FAIRY */ + { OBJECT_GI_SOUL, GID_FAIRY, func_80A3C498, 50, 1, 0x00B7, 0x00B6, GI_FAIRY, EnGirlA_CanBuy_Fairy, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_ARROWS_10 */ + { OBJECT_GI_ARROW, GID_ARROWS_SMALL, func_8002EBCC, 20, 10, 0x00A0, 0x008A, GI_ARROWS_SMALL, EnGirlA_CanBuy_Arrows, + EnGirlA_ItemGive_Arrows, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BOMBS_20 */ + { OBJECT_GI_BOMB_1, GID_BOMB, func_8002EBCC, 80, 20, 0x001C, 0x0006, GI_BOMBS_20, EnGirlA_CanBuy_Bombs, + EnGirlA_ItemGive_Bombs, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BOMBS_30 */ + { OBJECT_GI_BOMB_1, GID_BOMB, func_8002EBCC, 120, 30, 0x001D, 0x001E, GI_BOMBS_30, EnGirlA_CanBuy_Bombs, + EnGirlA_ItemGive_Bombs, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_BOMBS_5_R35 */ + { OBJECT_GI_BOMB_1, GID_BOMB, func_8002EBCC, 35, 5, 0x00CB, 0x00CA, GI_BOMBS_5, EnGirlA_CanBuy_Bombs, + EnGirlA_ItemGive_Bombs, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_RED_POTION_R40 */ + { OBJECT_GI_LIQUID, GID_POTION_RED, func_8002EBCC, 40, 1, 0x0064, 0x0062, GI_POTION_RED, EnGirlA_CanBuy_RedPotion, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount }, + /* SI_RED_POTION_R50 */ + { OBJECT_GI_LIQUID, GID_POTION_RED, func_8002EBCC, 50, 1, 0x0065, 0x0063, GI_POTION_RED, EnGirlA_CanBuy_RedPotion, + EnGirlA_ItemGive_BottledItem, EnGirlA_BuyEvent_ShieldDiscount } +}; + +// Defines the Hylian Shield discount amount +static s16 sShieldDiscounts[] = { 5, 10, 15, 20, 25, 30, 35, 40 }; + +void EnGirlA_SetupAction(EnGirlA* this, EnGirlAActionFunc func) { + this->actionFunc = func; +} + +s32 EnGirlA_TryChangeShopItem(EnGirlA* this) { + switch (this->actor.params) { + case SI_MILK_BOTTLE: + if (gSaveContext.itemGetInf[0] & 0x4) { + this->actor.params = SI_HEART; + return true; + } + break; + case SI_BOMBCHU_10_2: + if (gSaveContext.itemGetInf[0] & 0x40) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_10_3: + if (gSaveContext.itemGetInf[0] & 0x80) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_20_3: + if (gSaveContext.itemGetInf[0] & 0x100) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_20_4: + if (gSaveContext.itemGetInf[0] & 0x200) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_10_4: + if (gSaveContext.itemGetInf[0] & 0x400) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_10_1: + if (gSaveContext.itemGetInf[0] & 0x8) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_20_1: + if (gSaveContext.itemGetInf[0] & 0x10) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + case SI_BOMBCHU_20_2: + if (gSaveContext.itemGetInf[0] & 0x20) { + this->actor.params = SI_SOLD_OUT; + return true; + } + break; + } + return false; +} + +void EnGirlA_InitItem(EnGirlA* this, GlobalContext* globalCtx) { + s16 params = this->actor.params; + + osSyncPrintf("%s(%2d)初期設定\n", sShopItemDescriptions[params], params); + + if ((params >= SI_MAX) && (params < 0)) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("引数がおかしいよ(arg_data=%d)!!\n", this->actor.params); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_girlA.c", 1421); + return; + } + + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, shopItemEntries[params].objID); + + if (this->objBankIndex < 0) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("バンクが無いよ!!(%s)\n", sShopItemDescriptions[params]); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_girlA.c", 1434); + return; + } + + this->actor.params = params; + this->actionFunc2 = EnGirlA_InitializeItemAction; +} + +void EnGirlA_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGirlA* this = (EnGirlA*)thisx; + + EnGirlA_TryChangeShopItem(this); + EnGirlA_InitItem(this, globalCtx); + osSyncPrintf("%s(%2d)初期設定\n", sShopItemDescriptions[this->actor.params], this->actor.params); +} + +void EnGirlA_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGirlA* this = (EnGirlA*)thisx; + + if (this->isInitialized) { + SkelAnime_Free(&this->skelAnime, globalCtx); + } +} + +s32 EnGirlA_CanBuy_Arrows(GlobalContext* globalCtx, EnGirlA* this) { + if (Item_CheckObtainability(ITEM_BOW) == ITEM_NONE) { + return CANBUY_RESULT_CANT_GET_NOW_5; + } + if (AMMO(ITEM_BOW) >= CUR_CAPACITY(UPG_QUIVER)) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Bombs(GlobalContext* globalCtx, EnGirlA* this) { + if (!CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (AMMO(ITEM_BOMB) >= CUR_CAPACITY(UPG_BOMB_BAG)) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_DekuNuts(GlobalContext* globalCtx, EnGirlA* this) { + if ((CUR_CAPACITY(UPG_NUTS) != 0) && (AMMO(ITEM_NUT) >= CUR_CAPACITY(UPG_NUTS))) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_NUT) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_DekuSticks(GlobalContext* globalCtx, EnGirlA* this) { + if ((CUR_CAPACITY(UPG_STICKS) != 0) && (AMMO(ITEM_STICK) >= CUR_CAPACITY(UPG_STICKS))) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_STICK) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Fish(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_FISH) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_RedPotion(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_POTION_RED) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_GreenPotion(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_POTION_GREEN) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_BluePotion(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_POTION_BLUE) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Longsword(GlobalContext* globalCtx, EnGirlA* this) { + if ((gBitFlags[2] & gSaveContext.inventory.equipment) && !(gBitFlags[3] & gSaveContext.inventory.equipment)) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_SWORD_BGS) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_HylianShield(GlobalContext* globalCtx, EnGirlA* this) { + if (gBitFlags[5] & gSaveContext.inventory.equipment) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_SHIELD_HYLIAN) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_DekuShield(GlobalContext* globalCtx, EnGirlA* this) { + if (gBitFlags[4] & gSaveContext.inventory.equipment) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_SHIELD_DEKU) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_GoronTunic(GlobalContext* globalCtx, EnGirlA* this) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gBitFlags[9] & gSaveContext.inventory.equipment) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_TUNIC_GORON) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gBitFlags[10] & gSaveContext.inventory.equipment) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_TUNIC_ZORA) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Health(GlobalContext* globalCtx, EnGirlA* this) { + if (gSaveContext.healthCapacity == gSaveContext.health) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_MilkBottle(GlobalContext* globalCtx, EnGirlA* this) { + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_MILK_BOTTLE) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_WeirdEgg(GlobalContext* globalCtx, EnGirlA* this) { + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_LETTER_ZELDA) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Unk19(GlobalContext* globalCtx, EnGirlA* this) { + return CANBUY_RESULT_NEED_RUPEES; +} + +s32 EnGirlA_CanBuy_Unk20(GlobalContext* globalCtx, EnGirlA* this) { + return CANBUY_RESULT_NEED_RUPEES; +} + +s32 EnGirlA_CanBuy_Bombchus(GlobalContext* globalCtx, EnGirlA* this) { + if (AMMO(ITEM_BOMBCHU) >= 50) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_BOMBCHU) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_DekuSeeds(GlobalContext* globalCtx, EnGirlA* this) { + if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { + return CANBUY_RESULT_CANT_GET_NOW; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_SEEDS) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_SoldOut(GlobalContext* globalCtx, EnGirlA* this) { + return CANBUY_RESULT_CANT_GET_NOW_5; +} + +s32 EnGirlA_CanBuy_BlueFire(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_BLUE_FIRE) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Bugs(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_BUG) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Poe(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_POE) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +s32 EnGirlA_CanBuy_Fairy(GlobalContext* globalCtx, EnGirlA* this) { + if (!Inventory_HasEmptyBottle()) { + return CANBUY_RESULT_NEED_BOTTLE; + } + if (gSaveContext.rupees < this->basePrice) { + return CANBUY_RESULT_NEED_RUPEES; + } + if (Item_CheckObtainability(ITEM_FAIRY) == ITEM_NONE) { + return CANBUY_RESULT_SUCCESS_FANFARE; + } + return CANBUY_RESULT_SUCCESS; +} + +void EnGirlA_ItemGive_Arrows(GlobalContext* globalCtx, EnGirlA* this) { + Inventory_ChangeAmmo(ITEM_BOW, this->itemCount); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_Bombs(GlobalContext* globalCtx, EnGirlA* this) { + switch (this->itemCount) { + case 5: + Item_Give(globalCtx, ITEM_BOMBS_5); + break; + case 10: + Item_Give(globalCtx, ITEM_BOMBS_10); + break; + case 20: + Item_Give(globalCtx, ITEM_BOMBS_20); + break; + case 30: + Item_Give(globalCtx, ITEM_BOMBS_30); + break; + } + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_DekuNuts(GlobalContext* globalCtx, EnGirlA* this) { + switch (this->itemCount) { + case 5: + Item_Give(globalCtx, ITEM_NUTS_5); + break; + case 10: + Item_Give(globalCtx, ITEM_NUTS_10); + break; + } + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_DekuSticks(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_STICK); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_Longsword(GlobalContext* globalCtx, EnGirlA* this) { + func_800849EC(globalCtx); + gSaveContext.swordHealth = 8; + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_HylianShield(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_SHIELD_HYLIAN); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_DekuShield(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_SHIELD_DEKU); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_GoronTunic(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_TUNIC_GORON); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_TUNIC_ZORA); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_Health(GlobalContext* globalCtx, EnGirlA* this) { + Health_ChangeBy(globalCtx, this->itemCount); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_MilkBottle(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_MILK_BOTTLE); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_WeirdEgg(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_WEIRD_EGG); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_Unk19(GlobalContext* globalCtx, EnGirlA* this) { + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_Unk20(GlobalContext* globalCtx, EnGirlA* this) { + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_DekuSeeds(GlobalContext* globalCtx, EnGirlA* this) { + Item_Give(globalCtx, ITEM_SEEDS_30); + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_ItemGive_BottledItem(GlobalContext* globalCtx, EnGirlA* this) { + switch (this->actor.params) { + case SI_FISH: + Item_Give(globalCtx, ITEM_FISH); + break; + case SI_RED_POTION_R30: + Item_Give(globalCtx, ITEM_POTION_RED); + break; + case SI_GREEN_POTION: + Item_Give(globalCtx, ITEM_POTION_GREEN); + break; + case SI_BLUE_POTION: + Item_Give(globalCtx, ITEM_POTION_BLUE); + break; + case SI_BLUE_FIRE: + Item_Give(globalCtx, ITEM_BLUE_FIRE); + break; + case SI_BUGS: + Item_Give(globalCtx, ITEM_BUG); + break; + case SI_BIG_POE: + Item_Give(globalCtx, ITEM_BIG_POE); + break; + case SI_POE: + Item_Give(globalCtx, ITEM_POE); + break; + case SI_FAIRY: + Item_Give(globalCtx, ITEM_FAIRY); + break; + } + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_BuyEvent_ShieldDiscount(GlobalContext* globalCtx, EnGirlA* this) { + if (this->actor.params == SI_HYLIAN_SHIELD) { + if (gSaveContext.infTable[7] & 0x40) { + Rupees_ChangeBy(-(this->basePrice - sShieldDiscounts[(s32)Rand_ZeroFloat(7.9f)])); + return; + } + } + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_BuyEvent_GoronTunic(GlobalContext* globalCtx, EnGirlA* this) { + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_BuyEvent_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this) { + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this) { + switch (this->actor.params) { + case SI_BOMBCHU_10_2: + gSaveContext.itemGetInf[0] |= 0x40; + break; + case SI_BOMBCHU_10_3: + gSaveContext.itemGetInf[0] |= 0x80; + break; + case SI_BOMBCHU_20_3: + gSaveContext.itemGetInf[0] |= 0x100; + break; + case SI_BOMBCHU_20_4: + gSaveContext.itemGetInf[0] |= 0x200; + break; + case SI_BOMBCHU_10_4: + gSaveContext.itemGetInf[0] |= 0x400; + break; + case SI_BOMBCHU_10_1: + gSaveContext.itemGetInf[0] |= 0x8; + break; + case SI_BOMBCHU_20_1: + gSaveContext.itemGetInf[0] |= 0x10; + break; + case SI_BOMBCHU_20_2: + gSaveContext.itemGetInf[0] |= 0x20; + break; + } + Rupees_ChangeBy(-this->basePrice); +} + +void EnGirlA_Noop(EnGirlA* this, GlobalContext* globalCtx) { +} + +void EnGirlA_SetItemDescription(GlobalContext* globalCtx, EnGirlA* this) { + ShopItemEntry* tmp = &shopItemEntries[this->actor.params]; + s32 params = this->actor.params; + s32 maskId; + s32 isMaskFreeToBorrow; + + if ((this->actor.params >= SI_KEATON_MASK) && (this->actor.params <= SI_MASK_OF_TRUTH)) { + maskId = this->actor.params - SI_KEATON_MASK; + isMaskFreeToBorrow = false; + switch (this->actor.params) { + case SI_KEATON_MASK: + if (gSaveContext.itemGetInf[3] & 0x100) { + isMaskFreeToBorrow = true; + } + break; + case SI_SPOOKY_MASK: + if (gSaveContext.itemGetInf[3] & 0x400) { + isMaskFreeToBorrow = true; + } + break; + case SI_SKULL_MASK: + if (gSaveContext.itemGetInf[3] & 0x200) { + isMaskFreeToBorrow = true; + } + break; + case SI_BUNNY_HOOD: + if (gSaveContext.itemGetInf[3] & 0x800) { + isMaskFreeToBorrow = true; + } + break; + case SI_MASK_OF_TRUTH: + if (gSaveContext.itemGetInf[3] & 0x800) { + isMaskFreeToBorrow = true; + } + break; + } + if (isMaskFreeToBorrow) { + this->actor.textId = sMaskShopFreeToBorrowTextIds[maskId]; + } else { + this->actor.textId = tmp->itemDescTextId; + } + } else { + this->actor.textId = tmp->itemDescTextId; + } + this->isInvisible = false; + this->actor.draw = EnGirlA_Draw; +} + +void EnGirlA_SetItemOutOfStock(GlobalContext* globalCtx, EnGirlA* this) { + this->isInvisible = true; + this->actor.draw = NULL; + if ((this->actor.params >= SI_KEATON_MASK) && (this->actor.params <= SI_GERUDO_MASK)) { + this->actor.textId = 0xBD; + } +} + +void EnGirlA_UpdateStockedItem(GlobalContext* globalCtx, EnGirlA* this) { + ShopItemEntry* itemEntry; + + if (EnGirlA_TryChangeShopItem(this)) { + EnGirlA_InitItem(this, globalCtx); + itemEntry = &shopItemEntries[this->actor.params]; + this->actor.textId = itemEntry->itemDescTextId; + } else { + this->isInvisible = false; + this->actor.draw = EnGirlA_Draw; + } +} + +s32 EnGirlA_TrySetMaskItemDescription(EnGirlA* this, GlobalContext* globalCtx) { + s32 params; + + if ((this->actor.params >= SI_KEATON_MASK) && (this->actor.params <= SI_GERUDO_MASK)) { + params = this->actor.params - SI_KEATON_MASK; + if (INV_CONTENT(ITEM_TRADE_CHILD) == sMaskShopItems[params]) { + EnGirlA_SetItemOutOfStock(globalCtx, this); + } else { + EnGirlA_SetItemDescription(globalCtx, this); + } + return true; + } + return false; +} + +void EnGirlA_InitializeItemAction(EnGirlA* this, GlobalContext* globalCtx) { + s16 params = this->actor.params; + ShopItemEntry* itemEntry = &shopItemEntries[params]; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.objBankIndex = this->objBankIndex; + switch (this->actor.params) { + case SI_KEATON_MASK: + if (gSaveContext.itemGetInf[3] & 0x100) { + this->actor.textId = 0x70B6; + } else { + this->actor.textId = itemEntry->itemDescTextId; + } + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + case SI_SPOOKY_MASK: + if (gSaveContext.itemGetInf[3] & 0x400) { + this->actor.textId = 0x70B5; + } else { + this->actor.textId = itemEntry->itemDescTextId; + } + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + case SI_SKULL_MASK: + if (gSaveContext.itemGetInf[3] & 0x200) { + this->actor.textId = 0x70B4; + } else { + this->actor.textId = itemEntry->itemDescTextId; + } + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + case SI_BUNNY_HOOD: + if (gSaveContext.itemGetInf[3] & 0x800) { + this->actor.textId = 0x70B7; + } else { + this->actor.textId = itemEntry->itemDescTextId; + } + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + case SI_MASK_OF_TRUTH: + if (gSaveContext.itemGetInf[3] & 0x800) { + this->actor.textId = 0x70BB; + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + } else { + this->actor.textId = itemEntry->itemDescTextId; + this->itemBuyPromptTextId = 0xEB; + } + break; + case SI_ZORA_MASK: + this->actor.textId = itemEntry->itemDescTextId; + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + case SI_GORON_MASK: + this->actor.textId = itemEntry->itemDescTextId; + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + case SI_GERUDO_MASK: + this->actor.textId = itemEntry->itemDescTextId; + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + default: + this->actor.textId = itemEntry->itemDescTextId; + this->itemBuyPromptTextId = itemEntry->itemBuyPromptTextId; + break; + } + if (!EnGirlA_TrySetMaskItemDescription(this, globalCtx)) { + EnGirlA_SetItemDescription(globalCtx, this); + } + + this->setOutOfStockFunc = EnGirlA_SetItemOutOfStock; + this->updateStockedItemFunc = EnGirlA_UpdateStockedItem; + this->getItemId = itemEntry->getItemId; + this->canBuyFunc = itemEntry->canBuyFunc; + this->itemGiveFunc = itemEntry->itemGiveFunc; + this->buyEventFunc = itemEntry->buyEventFunc; + this->basePrice = itemEntry->price; + this->itemCount = itemEntry->count; + this->hiliteFunc = itemEntry->hiliteFunc; + this->giDrawId = itemEntry->giDrawId; + osSyncPrintf("%s(%2d)\n", sShopItemDescriptions[params], params); + this->actor.flags &= ~ACTOR_FLAG_0; + Actor_SetScale(&this->actor, 0.25f); + this->actor.shape.yOffset = 24.0f; + this->actor.shape.shadowScale = 4.0f; + this->actor.floorHeight = this->actor.home.pos.y; + this->actor.gravity = 0.0f; + EnGirlA_SetupAction(this, EnGirlA_Noop); + this->isInitialized = true; + this->actionFunc2 = EnGirlA_Update2; + this->isSelected = false; + this->yRotation = 0; + this->yRotationInit = this->actor.shape.rot.y; + } +} + +void EnGirlA_Update2(EnGirlA* this, GlobalContext* globalCtx) { + Actor_SetScale(&this->actor, 0.25f); + this->actor.shape.yOffset = 24.0f; + this->actor.shape.shadowScale = 4.0f; + EnGirlA_TrySetMaskItemDescription(this, globalCtx); + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, 5.0f); + this->actor.shape.rot.x = 0.0f; + if (this->actor.params != SI_SOLD_OUT) { + if (this->isSelected) { + this->yRotation += 0x1F4; + } else { + Math_SmoothStepToS(&this->yRotation, 0, 10, 0x7D0, 0); + } + } +} + +void EnGirlA_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGirlA* this = (EnGirlA*)thisx; + + this->actionFunc2(this, globalCtx); +} + +void func_80A3C498(Actor* thisx, GlobalContext* globalCtx, s32 flags) { + func_8002EBCC(thisx, globalCtx, 0); + func_8002ED80(thisx, globalCtx, 0); +} + +void EnGirlA_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGirlA* this = (EnGirlA*)thisx; + + Matrix_RotateY(((this->yRotation * 360.0f) / 65536.0f) * (M_PI / 180.0f), MTXMODE_APPLY); + if (this->hiliteFunc != NULL) { + this->hiliteFunc(thisx, globalCtx, 0); + } + GetItem_Draw(globalCtx, this->giDrawId); +} diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.h b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.h new file mode 100644 index 000000000..1fdc18929 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.h @@ -0,0 +1,101 @@ +#ifndef Z_EN_GIRLA_H +#define Z_EN_GIRLA_H + +#include "ultra64.h" +#include "global.h" + +struct EnGirlA; + +typedef void (*EnGirlAActionFunc)(struct EnGirlA*, GlobalContext*); +typedef void (*EnGirlA2Func)(GlobalContext*, struct EnGirlA*); +typedef void (*EnGirlA3Func)(Actor*, GlobalContext*, s32); +typedef s32 (*EnGirlA4Func)(GlobalContext*, struct EnGirlA*); + +typedef struct EnGirlA { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnGirlAActionFunc actionFunc; + /* 0x0194 */ s8 objBankIndex; + /* 0x0198 */ EnGirlAActionFunc actionFunc2; + /* 0x019C */ s32 isInitialized; + /* 0x01A0 */ s16 itemBuyPromptTextId; + /* 0x01A4 */ s32 getItemId; + /* 0x01A8 */ s16 isInvisible; + /* 0x01AC */ EnGirlA2Func setOutOfStockFunc; + /* 0x01B0 */ EnGirlA2Func updateStockedItemFunc; + /* 0x01B4 */ s16 isSelected; + /* 0x01B6 */ s16 yRotationInit; + /* 0x01B8 */ s16 yRotation; + /* 0x01BC */ EnGirlA4Func canBuyFunc; + /* 0x01C0 */ EnGirlA2Func itemGiveFunc; + /* 0x01C4 */ EnGirlA2Func buyEventFunc; + /* 0x01C8 */ s16 basePrice; + /* 0x01CA */ s16 itemCount; + /* 0x01CC */ s16 giDrawId; + /* 0x01D0 */ EnGirlA3Func hiliteFunc; +} EnGirlA; // size = 0x01D4 + +typedef enum { + /* 0x00 */ SI_DEKU_NUTS_5, + /* 0x01 */ SI_ARROWS_30, + /* 0x02 */ SI_ARROWS_50, + /* 0x03 */ SI_BOMBS_5_R25, + /* 0x04 */ SI_DEKU_NUTS_10, + /* 0x05 */ SI_DEKU_STICK, + /* 0x06 */ SI_BOMBS_10, + /* 0x07 */ SI_FISH, + /* 0x08 */ SI_RED_POTION_R30, + /* 0x09 */ SI_GREEN_POTION, + /* 0x0A */ SI_BLUE_POTION, + /* 0x0B */ SI_LONGSWORD, + /* 0x0C */ SI_HYLIAN_SHIELD, + /* 0x0D */ SI_DEKU_SHIELD, + /* 0x0E */ SI_GORON_TUNIC, + /* 0x0F */ SI_ZORA_TUNIC, + /* 0x10 */ SI_HEART, + /* 0x11 */ SI_MILK_BOTTLE, + /* 0x12 */ SI_WEIRD_EGG, + /* 0x13 */ SI_19, + /* 0x14 */ SI_20, + /* 0x15 */ SI_BOMBCHU_10_1, + /* 0x16 */ SI_BOMBCHU_20_1, + /* 0x17 */ SI_BOMBCHU_20_2, + /* 0x18 */ SI_BOMBCHU_10_2, + /* 0x19 */ SI_BOMBCHU_10_3, + /* 0x1A */ SI_BOMBCHU_20_3, + /* 0x1B */ SI_BOMBCHU_20_4, + /* 0x1C */ SI_BOMBCHU_10_4, + /* 0x1D */ SI_DEKU_SEEDS_30, + /* 0x1E */ SI_KEATON_MASK, + /* 0x1F */ SI_SPOOKY_MASK, + /* 0x20 */ SI_SKULL_MASK, + /* 0x21 */ SI_BUNNY_HOOD, + /* 0x22 */ SI_MASK_OF_TRUTH, + /* 0x23 */ SI_ZORA_MASK, + /* 0x24 */ SI_GORON_MASK, + /* 0x25 */ SI_GERUDO_MASK, + /* 0x26 */ SI_SOLD_OUT, + /* 0x27 */ SI_BLUE_FIRE, + /* 0x28 */ SI_BUGS, + /* 0x29 */ SI_BIG_POE, + /* 0x2A */ SI_POE, + /* 0x2B */ SI_FAIRY, + /* 0x2C */ SI_ARROWS_10, + /* 0x2D */ SI_BOMBS_20, + /* 0x2E */ SI_BOMBS_30, + /* 0x2F */ SI_BOMBS_5_R35, + /* 0x30 */ SI_RED_POTION_R40, + /* 0x31 */ SI_RED_POTION_R50, + /* 0x32 */ SI_MAX +} EnGirlAShopItem; + +typedef enum { + /* 0 */ CANBUY_RESULT_SUCCESS_FANFARE, + /* 1 */ CANBUY_RESULT_SUCCESS, + /* 2 */ CANBUY_RESULT_CANT_GET_NOW, + /* 3 */ CANBUY_RESULT_NEED_BOTTLE, + /* 4 */ CANBUY_RESULT_NEED_RUPEES, + /* 5 */ CANBUY_RESULT_CANT_GET_NOW_5 +} EnGirlACanBuyResult; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c new file mode 100644 index 000000000..e211b8d24 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.c @@ -0,0 +1,334 @@ +/* + * File: z_en_gm.c + * Overlay: ovl_En_Gm + * Description: Medi-Goron + */ + +#include "z_en_gm.h" +#include "objects/object_oF1d_map/object_oF1d_map.h" +#include "objects/object_gm/object_gm.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnGm_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGm_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGm_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A3D838(EnGm* this, GlobalContext* globalCtx); +void func_80A3DFBC(EnGm* this, GlobalContext* globalCtx); +void func_80A3DB04(EnGm* this, GlobalContext* globalCtx); +void func_80A3DC44(EnGm* this, GlobalContext* globalCtx); +void func_80A3DBF4(EnGm* this, GlobalContext* globalCtx); +void func_80A3DD7C(EnGm* this, GlobalContext* globalCtx); +void EnGm_ProcessChoiceIndex(EnGm* this, GlobalContext* globalCtx); +void func_80A3DF00(EnGm* this, GlobalContext* globalCtx); +void func_80A3DF60(EnGm* this, GlobalContext* globalCtx); + +const ActorInit En_Gm_InitVars = { + ACTOR_EN_GM, + ACTORCAT_NPC, + FLAGS, + OBJECT_OF1D_MAP, + sizeof(EnGm), + (ActorFunc)EnGm_Init, + (ActorFunc)EnGm_Destroy, + (ActorFunc)EnGm_Update, + NULL, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 100, 120, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +void EnGm_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGm* this = (EnGm*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + + // "Medi Goron" + osSyncPrintf(VT_FGCOL(GREEN) "%s[%d] : 中ゴロン[%d]" VT_RST "\n", "../z_en_gm.c", 133, this->actor.params); + + this->objGmBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_GM); + + if (this->objGmBankIndex < 0) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "There is no model bank! !! (Medi Goron)" + osSyncPrintf("モデル バンクが無いよ!!(中ゴロン)\n"); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_gm.c", 145); + } + + this->updateFunc = func_80A3D838; +} + +void EnGm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGm* this = (EnGm*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80A3D7C8(void) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + return 0; + } else if (!(gBitFlags[2] & gSaveContext.inventory.equipment)) { + return 1; + } else if (gBitFlags[3] & gSaveContext.inventory.equipment) { + return 2; + } else { + return 3; + } +} + +void func_80A3D838(EnGm* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objGmBankIndex)) { + this->actor.flags &= ~ACTOR_FLAG_4; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGoronSkel, NULL, this->jointTable, this->morphTable, 18); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objGmBankIndex].segment); + Animation_Change(&this->skelAnime, &object_gm_Anim_0002B8, 1.0f, 0.0f, + Animation_GetLastFrame(&object_gm_Anim_0002B8), ANIMMODE_LOOP, 0.0f); + this->actor.draw = EnGm_Draw; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f); + Actor_SetScale(&this->actor, 0.05f); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->eyeTexIndex = 0; + this->blinkTimer = 20; + this->actor.textId = 0x3049; + this->updateFunc = func_80A3DFBC; + this->actionFunc = func_80A3DB04; + this->actor.speedXZ = 0.0f; + this->actor.gravity = -1.0f; + this->actor.velocity.y = 0.0f; + } +} + +void EnGm_UpdateEye(EnGm* this) { + if (this->blinkTimer != 0) { + this->blinkTimer--; + } else { + this->eyeTexIndex++; + + if (this->eyeTexIndex >= 3) { + this->eyeTexIndex = 0; + this->blinkTimer = Rand_ZeroFloat(60.0f) + 20.0f; + } + } +} + +void EnGm_SetTextID(EnGm* this) { + switch (func_80A3D7C8()) { + case 0: + if (gSaveContext.infTable[11] & 1) { + this->actor.textId = 0x304B; + } else { + this->actor.textId = 0x304A; + } + break; + case 1: + if (gSaveContext.infTable[11] & 2) { + this->actor.textId = 0x304F; + } else { + this->actor.textId = 0x304C; + } + break; + case 2: + this->actor.textId = 0x304E; + break; + case 3: + this->actor.textId = 0x304D; + break; + } +} + +void func_80A3DB04(EnGm* this, GlobalContext* globalCtx) { + f32 dx; + f32 dz; + Player* player = GET_PLAYER(globalCtx); + + dx = this->talkPos.x - player->actor.world.pos.x; + dz = this->talkPos.z - player->actor.world.pos.z; + + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + EnGm_SetTextID(this); + this->actionFunc = func_80A3DC44; + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_80A3DBF4; + } else if ((this->collider.base.ocFlags1 & OC1_HIT) || (SQ(dx) + SQ(dz)) < SQ(100.0f)) { + this->collider.base.acFlags &= ~AC_HIT; + func_8002F2CC(&this->actor, globalCtx, 415.0f); + } +} + +void func_80A3DBF4(EnGm* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = func_80A3DB04; + } +} + +void func_80A3DC44(EnGm* this, GlobalContext* globalCtx) { + f32 dx; + f32 dz; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + EnGm_SetTextID(this); + + dx = this->talkPos.x - player->actor.world.pos.x; + dz = this->talkPos.z - player->actor.world.pos.z; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + switch (func_80A3D7C8()) { + case 0: + gSaveContext.infTable[11] |= 1; + case 3: + this->actionFunc = func_80A3DD7C; + return; + case 1: + gSaveContext.infTable[11] |= 2; + case 2: + this->actionFunc = EnGm_ProcessChoiceIndex; + default: + return; + } + + this->actionFunc = EnGm_ProcessChoiceIndex; + } + if ((this->collider.base.ocFlags1 & OC1_HIT) || (SQ(dx) + SQ(dz)) < SQ(100.0f)) { + this->collider.base.acFlags &= ~AC_HIT; + func_8002F2CC(&this->actor, globalCtx, 415.0f); + } +} + +void func_80A3DD7C(EnGm* this, GlobalContext* globalCtx) { + u8 dialogState = Message_GetState(&globalCtx->msgCtx); + + if ((dialogState == TEXT_STATE_DONE || dialogState == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = func_80A3DC44; + if (dialogState == TEXT_STATE_EVENT) { + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + globalCtx->msgCtx.stateTimer = 4; + } + } +} + +void EnGm_ProcessChoiceIndex(EnGm* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + if (gSaveContext.rupees < 200) { + Message_ContinueTextbox(globalCtx, 0xC8); + this->actionFunc = func_80A3DD7C; + } else { + func_8002F434(&this->actor, globalCtx, GI_SWORD_KNIFE, 415.0f, 10.0f); + this->actionFunc = func_80A3DF00; + } + break; + case 1: // no + Message_ContinueTextbox(globalCtx, 0x3050); + this->actionFunc = func_80A3DD7C; + break; + } + } +} + +void func_80A3DF00(EnGm* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_80A3DF60; + } else { + func_8002F434(&this->actor, globalCtx, GI_SWORD_KNIFE, 415.0f, 10.0f); + } +} + +void func_80A3DF60(EnGm* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + Rupees_ChangeBy(-200); + this->actionFunc = func_80A3DC44; + } +} + +void func_80A3DFBC(EnGm* this, GlobalContext* globalCtx) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objGmBankIndex].segment); + this->timer++; + this->actionFunc(this, globalCtx); + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + EnGm_UpdateEye(this); + SkelAnime_Update(&this->skelAnime); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnGm_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGm* this = (EnGm*)thisx; + + this->updateFunc(this, globalCtx); +} + +void func_80A3E090(EnGm* this) { + Vec3f vec1; + Vec3f vec2; + + Matrix_Push(); + Matrix_Translate(0.0f, 0.0f, 2600.0f, MTXMODE_APPLY); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, MTXMODE_APPLY); + vec1.x = vec1.y = vec1.z = 0.0f; + Matrix_MultVec3f(&vec1, &vec2); + this->collider.dim.pos.x = vec2.x; + this->collider.dim.pos.y = vec2.y; + this->collider.dim.pos.z = vec2.z; + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(0.0f, 0.0f, 4300.0f, MTXMODE_APPLY); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, MTXMODE_APPLY); + vec1.x = vec1.y = vec1.z = 0.0f; + Matrix_MultVec3f(&vec1, &this->talkPos); + Matrix_Pop(); + Matrix_Translate(0.0f, 0.0f, 3800.0f, MTXMODE_APPLY); + Matrix_RotateZYX(this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, MTXMODE_APPLY); + vec1.x = vec1.y = vec1.z = 0.0f; + Matrix_MultVec3f(&vec1, &this->actor.focus.pos); + this->actor.focus.pos.y += 100.0f; +} + +void EnGm_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gGoronCsEyeOpenTex, gGoronCsEyeHalfTex, gGoronCsEyeClosedTex }; + EnGm* this = (EnGm*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_gm.c", 613); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gGoronCsMouthNeutralTex)); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_gm.c", 629); + + func_80A3E090(this); +} diff --git a/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.h b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.h new file mode 100644 index 000000000..94e454e4f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Gm/z_en_gm.h @@ -0,0 +1,27 @@ +#ifndef Z_EN_GM_H +#define Z_EN_GM_H + +#include "ultra64.h" +#include "global.h" + +struct EnGm; + +typedef void (*EnGmUpdateFunc)(struct EnGm*, GlobalContext*); +typedef void (*EnGmActionFunc)(struct EnGm*, GlobalContext*); + +typedef struct EnGm { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[18]; + /* 0x01FC */ Vec3s morphTable[18]; + /* 0x0268 */ EnGmUpdateFunc updateFunc; + /* 0x026C */ EnGmActionFunc actionFunc; + /* 0x0270 */ ColliderCylinder collider; + /* 0x02BC */ s8 objGmBankIndex; + /* 0x02BE */ s16 timer; + /* 0x02C0 */ s16 blinkTimer; + /* 0x02C2 */ u8 eyeTexIndex; + /* 0x02C4 */ Vec3f talkPos; +} EnGm; // size = 0x02D0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Go/z_en_go.c b/soh/src/overlays/actors/ovl_En_Go/z_en_go.c new file mode 100644 index 000000000..9508abf9a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Go/z_en_go.c @@ -0,0 +1,1237 @@ +#include "z_en_go.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_oF1d_map/object_oF1d_map.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnGo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGo_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A3FEB4(EnGo* this, GlobalContext* globalCtx); +void EnGo_StopRolling(EnGo* this, GlobalContext* globalCtx); +void func_80A4008C(EnGo* this, GlobalContext* globalCtx); +void EnGo_GoronLinkRolling(EnGo* this, GlobalContext* globalCtx); +void EnGo_FireGenericActionFunc(EnGo* this, GlobalContext* globalCtx); +void EnGo_CurledUp(EnGo* this, GlobalContext* globalCtx); +void EnGo_WakeUp(EnGo* this, GlobalContext* globalCtx); + +void func_80A40494(EnGo* this, GlobalContext* globalCtx); +void func_80A405CC(EnGo* this, GlobalContext* globalCtx); +void EnGo_BiggoronActionFunc(EnGo* this, GlobalContext* globalCtx); +void func_80A408D8(EnGo* this, GlobalContext* globalCtx); + +void func_80A40B1C(EnGo* this, GlobalContext* globalCtx); +void EnGo_GetItem(EnGo* this, GlobalContext* globalCtx); +void func_80A40C78(EnGo* this, GlobalContext* globalCtx); +void EnGo_Eyedrops(EnGo* this, GlobalContext* globalCtx); +void func_80A40DCC(EnGo* this, GlobalContext* globalCtx); + +void EnGo_AddDust(EnGo* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale, f32 scaleStep); +void EnGo_UpdateDust(EnGo* this); +void EnGo_DrawDust(EnGo* this, GlobalContext* globalCtx); + +const ActorInit En_Go_InitVars = { + ACTOR_EN_GO, + ACTORCAT_NPC, + FLAGS, + OBJECT_OF1D_MAP, + sizeof(EnGo), + (ActorFunc)EnGo_Init, + (ActorFunc)EnGo_Destroy, + (ActorFunc)EnGo_Update, + (ActorFunc)EnGo_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { + 0, 0, 0, 0, MASS_IMMOVABLE, +}; + +typedef enum { + /* 0 */ ENGO_ANIM_0, + /* 1 */ ENGO_ANIM_1, + /* 2 */ ENGO_ANIM_2, + /* 3 */ ENGO_ANIM_3 +} EnGoAnimation; + +static AnimationSpeedInfo sAnimationInfo[] = { + { &gGoronAnim_004930, 0.0f, ANIMMODE_LOOP_INTERP, 0.0f }, + { &gGoronAnim_004930, 0.0f, ANIMMODE_LOOP_INTERP, -10.0f }, + { &gGoronAnim_0029A8, 1.0f, ANIMMODE_LOOP_INTERP, -10.0f }, + { &gGoronAnim_010590, 1.0f, ANIMMODE_LOOP_INTERP, -10.0f }, +}; + +void EnGo_SetupAction(EnGo* this, EnGoActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +u16 EnGo_GetTextID(GlobalContext* globalCtx, Actor* thisx) { + Player* player = GET_PLAYER(globalCtx); + + switch (thisx->params & 0xF0) { + case 0x90: + if (gSaveContext.bgsFlag) { + return 0x305E; + } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) { + if (Environment_GetBgsDayCount() >= 3) { + return 0x305E; + } else { + return 0x305D; + } + } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_EYEDROPS) { + player->exchangeItemId = EXCH_ITEM_EYEDROPS; + return 0x3059; + } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_PRESCRIPTION) { + return 0x3058; + } else { + player->exchangeItemId = EXCH_ITEM_SWORD_BROKEN; + return 0x3053; + } + case 0x00: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) { + if (gSaveContext.infTable[16] & 0x8000) { + return 0x3042; + } else { + return 0x3041; + } + } else if (CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1) || (gSaveContext.infTable[16] & 0x2000)) { + if (gSaveContext.infTable[16] & 0x4000) { + return 0x3038; + } else { + return 0x3037; + } + } else { + if (gSaveContext.infTable[16] & 0x200) { + if (gSaveContext.infTable[16] & 0x400) { + return 0x3033; + } else { + return 0x3032; + } + } else { + return 0x3030; + } + } + case 0x10: + if (Flags_GetSwitch(globalCtx, thisx->params >> 8)) { + return 0x3052; + } else { + return 0x3051; + } + case 0x20: + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else if (gSaveContext.eventChkInf[2] & 0x8) { + return 0x3021; + } else if (gSaveContext.infTable[14] & 0x1) { + return 0x302A; + } else { + return 0x3008; + } + case 0x30: + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else if (gSaveContext.eventChkInf[2] & 0x8) { + return 0x3026; + } else { + return 0x3009; + } + case 0x40: + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else if (gSaveContext.eventChkInf[2] & 0x8) { + return 0x3026; + } else { + return 0x300A; + } + case 0x50: + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else if (gSaveContext.infTable[15] & 1) { + return 0x3015; + } else { + return 0x3014; + } + case 0x60: + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else if (gSaveContext.infTable[15] & 0x10) { + return 0x3017; + } else { + return 0x3016; + } + case 0x70: + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else if (gSaveContext.infTable[15] & 0x100) { + return 0x3019; + } else { + return 0x3018; + } + default: + return 0x0000; + } +} + +s16 EnGo_SetFlagsGetStates(GlobalContext* globalCtx, Actor* thisx) { + s16 unkState = 1; + f32 xzRange; + f32 yRange = fabsf(thisx->yDistToPlayer) + 1.0f; + + xzRange = thisx->xzDistToPlayer + 1.0f; + switch (Message_GetState(&globalCtx->msgCtx)) { + if (globalCtx) {} + case TEXT_STATE_CLOSING: + switch (thisx->textId) { + case 0x3008: + gSaveContext.infTable[14] |= 1; + unkState = 0; + break; + case 0x300B: + gSaveContext.infTable[14] |= 0x800; + unkState = 0; + break; + case 0x3014: + gSaveContext.infTable[15] |= 1; + unkState = 0; + break; + case 0x3016: + gSaveContext.infTable[15] |= 0x10; + unkState = 0; + break; + case 0x3018: + gSaveContext.infTable[15] |= 0x100; + unkState = 0; + break; + case 0x3036: + func_8002F434(thisx, globalCtx, GI_TUNIC_GORON, xzRange, yRange); + gSaveContext.infTable[16] |= 0x2000; // EnGo exclusive flag + unkState = 2; + break; + case 0x3037: + gSaveContext.infTable[16] |= 0x4000; + unkState = 0; + break; + case 0x3041: + gSaveContext.infTable[16] |= 0x8000; + unkState = 0; + break; + case 0x3059: + unkState = 2; + break; + case 0x3052: + case 0x3054: + case 0x3055: + case 0x305A: + unkState = 2; + break; + case 0x305E: + unkState = 2; + break; + default: + unkState = 0; + break; + } + break; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + switch (thisx->textId) { + case 0x300A: + if (globalCtx->msgCtx.choiceIndex == 0) { + if (CUR_UPG_VALUE(UPG_STRENGTH) != 0 || (gSaveContext.infTable[14] & 0x800)) { + thisx->textId = 0x300B; + } else { + thisx->textId = 0x300C; + } + } else { + thisx->textId = 0x300D; + } + Message_ContinueTextbox(globalCtx, thisx->textId); + unkState = 1; + break; + case 0x3034: + if (globalCtx->msgCtx.choiceIndex == 0) { + if (gSaveContext.infTable[16] & 0x800) { + thisx->textId = 0x3033; + } else { + thisx->textId = 0x3035; + } + } else if (gSaveContext.infTable[16] & 0x800) { + thisx->textId = 0x3036; + } else { + thisx->textId = 0x3033; + } + Message_ContinueTextbox(globalCtx, thisx->textId); + unkState = 1; + break; + case 0x3054: + case 0x3055: + if (globalCtx->msgCtx.choiceIndex == 0) { + unkState = 2; + } else { + thisx->textId = 0x3056; + Message_ContinueTextbox(globalCtx, thisx->textId); + unkState = 1; + } + gSaveContext.infTable[11] |= 0x10; + break; + } + } + break; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + switch (thisx->textId) { + case 0x3035: + gSaveContext.infTable[16] |= 0x800; + case 0x3032: + case 0x3033: + thisx->textId = 0x3034; + Message_ContinueTextbox(globalCtx, thisx->textId); + unkState = 1; + break; + default: + unkState = 2; + break; + } + } + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + unkState = 3; + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_9: + break; + } + return unkState; +} + +s32 func_80A3ED24(GlobalContext* globalCtx, EnGo* this, struct_80034A14_arg1* arg2, f32 arg3, + u16 (*getTextId)(GlobalContext*, Actor*), s16 (*unkFunc2)(GlobalContext*, Actor*)) { + if (arg2->unk_00) { + arg2->unk_00 = unkFunc2(globalCtx, &this->actor); + return false; + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + arg2->unk_00 = 1; + return true; + } else if (!func_8002F2CC(&this->actor, globalCtx, arg3)) { + return false; + } else { + this->actor.textId = getTextId(globalCtx, &this->actor); + return false; + } +} + +void EnGo_ChangeAnim(EnGo* this, s32 index) { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, + sAnimationInfo[index].playSpeed * ((this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f), 0.0f, + Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, + sAnimationInfo[index].morphFrames); +} + +s32 EnGo_IsActorSpawned(EnGo* this, GlobalContext* globalCtx) { + if (((this->actor.params) & 0xF0) == 0x90) { + return true; + } else if (globalCtx->sceneNum == SCENE_HIDAN && !Flags_GetSwitch(globalCtx, (this->actor.params) >> 8) && + LINK_IS_ADULT && (this->actor.params & 0xF0) == 0x10) { + return true; + } else if (globalCtx->sceneNum == SCENE_SPOT18 && LINK_IS_ADULT && (this->actor.params & 0xF0) == 0x00) { + return true; + } else if (globalCtx->sceneNum == SCENE_SPOT16 && LINK_IS_CHILD && + ((this->actor.params & 0xF0) == 0x20 || (this->actor.params & 0xF0) == 0x30 || + (this->actor.params & 0xF0) == 0x40)) { + return true; + } else if (globalCtx->sceneNum == SCENE_SPOT18 && LINK_IS_CHILD && + ((this->actor.params & 0xF0) == 0x50 || (this->actor.params & 0xF0) == 0x60 || + (this->actor.params & 0xF0) == 0x70)) { + return true; + } else { + return false; + } +} + +f32 EnGo_GetGoronSize(EnGo* this) { + switch (this->actor.params & 0xF0) { + case 0x00: + return 10.0f; + case 0x20: + case 0x30: + case 0x50: + case 0x60: + case 0x70: + return 20.0f; + case 0x40: + return 60.0f; + default: + return 20.0f; + } +} + +void func_80A3F060(EnGo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 unkVal; + + if (this->actionFunc != EnGo_BiggoronActionFunc && this->actionFunc != EnGo_FireGenericActionFunc && + this->actionFunc != func_80A40B1C) { + unkVal = 1; + } + + this->unk_1E0.unk_18 = player->actor.world.pos; + this->unk_1E0.unk_14 = EnGo_GetGoronSize(this); + func_80034A14(&this->actor, &this->unk_1E0, 4, unkVal); +} + +void func_80A3F0E4(EnGo* this) { + if (DECR(this->unk_214) == 0) { + this->unk_216++; + if (this->unk_216 >= 3) { + this->unk_214 = Rand_S16Offset(30, 30); + this->unk_216 = 0; + } + } +} + +s32 EnGo_IsCameraModified(EnGo* this, GlobalContext* globalCtx) { + f32 xyzDist; + s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + Camera* camera = globalCtx->cameraPtrs[MAIN_CAM]; + + if (fabsf(yawDiff) > 10920.0f) { + return 0; + } + + xyzDist = (this->actor.scale.x / 0.01f) * 10000.0f; + if ((this->actor.params & 0xF0) == 0x90) { + Camera_ChangeSetting(camera, CAM_SET_DIRECTED_YAW); + xyzDist *= 4.8f; + } + + if (fabsf(this->actor.xyzDistToPlayerSq) > xyzDist) { + if (camera->setting == CAM_SET_DIRECTED_YAW) { + Camera_ChangeSetting(camera, CAM_SET_NORMAL0); + } + return 0; + } else { + return 1; + } +} + +void EnGo_ReverseAnimation(EnGo* this) { + f32 startFrame = this->skelAnime.startFrame; + + this->skelAnime.startFrame = this->skelAnime.endFrame; + this->skelAnime.endFrame = startFrame; +} + +void EnGo_UpdateShadow(EnGo* this) { + s16 shadowAlpha; + f32 currentFrame = this->skelAnime.curFrame; + s16 shadowAlphaTarget = (this->skelAnime.animation == &gGoronAnim_004930 && currentFrame > 32.0f) || + this->skelAnime.animation != &gGoronAnim_004930 + ? 255 + : 0; + + shadowAlpha = this->actor.shape.shadowAlpha; + Math_SmoothStepToS(&shadowAlpha, shadowAlphaTarget, 10, 60, 1); + this->actor.shape.shadowAlpha = shadowAlpha; +} + +s32 EnGo_FollowPath(EnGo* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* pointPos; + f32 xDist; + f32 zDist; + + if ((this->actor.params & 0xF) == 15) { + return false; + } + + path = &globalCtx->setupPathList[this->actor.params & 0xF]; + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos += this->unk_218; + xDist = pointPos->x - this->actor.world.pos.x; + zDist = pointPos->z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.world.rot.y, (s16)(Math_FAtan2F(xDist, zDist) * ((f32)0x8000 / M_PI)), 10, 1000, 1); + + if ((SQ(xDist) + SQ(zDist)) < 600.0f) { + this->unk_218++; + if (this->unk_218 >= path->count) { + this->unk_218 = 0; + } + + if ((this->actor.params & 0xF0) != 0x00) { + return true; + } else if (Flags_GetSwitch(globalCtx, this->actor.params >> 8)) { + return true; + } else if (this->unk_218 >= this->actor.shape.rot.z) { + this->unk_218 = 0; + } + + return true; + } + + return false; +} + +s32 EnGo_SetMovedPos(EnGo* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* pointPos; + + if ((this->actor.params & 0xF) == 0xF) { + return false; + } else { + path = &globalCtx->setupPathList[this->actor.params & 0xF]; + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos += (path->count - 1); + this->actor.world.pos.x = pointPos->x; + this->actor.world.pos.y = pointPos->y; + this->actor.world.pos.z = pointPos->z; + this->actor.home.pos = this->actor.world.pos; + return true; + } +} + +s32 EnGo_SpawnDust(EnGo* this, u8 initialTimer, f32 scale, f32 scaleStep, s32 numDustEffects, f32 radius, f32 xzAccel) { + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + s16 angle; + s32 i; + + pos = this->actor.world.pos; // Overwrites pos + pos.y = this->actor.floorHeight; + angle = (Rand_ZeroOne() - 0.5f) * 0x10000; + i = numDustEffects; + while (i >= 0) { + accel.x = (Rand_ZeroOne() - 0.5f) * xzAccel; + accel.z = (Rand_ZeroOne() - 0.5f) * xzAccel; + pos.x = (Math_SinS(angle) * radius) + this->actor.world.pos.x; + pos.z = (Math_CosS(angle) * radius) + this->actor.world.pos.z; + EnGo_AddDust(this, &pos, &velocity, &accel, initialTimer, scale, scaleStep); + angle += (s16)(0x10000 / numDustEffects); + i--; + } + return 0; +} + +s32 EnGo_IsRollingOnGround(EnGo* this, s16 unkArg1, f32 unkArg2) { + if ((this->actor.bgCheckFlags & 1) == 0 || this->actor.velocity.y > 0.0f) { + return false; + } else if (this->unk_1E0.unk_00 != 0) { + return true; + } else if (DECR(this->unk_21C)) { + if ((this->unk_21C & 1)) { + this->actor.world.pos.y += 1.5f; + } else { + this->actor.world.pos.y -= 1.5f; + } + return true; + } else { + this->unk_21A--; + if (this->unk_21A <= 0) { + if (this->unk_21A == 0) { + this->unk_21C = Rand_S16Offset(60, 30); + this->unk_21A = 0; + this->actor.velocity.y = 0.0f; + return true; + } + this->unk_21A = unkArg1; + } + this->actor.velocity.y = ((f32)this->unk_21A / (f32)unkArg1) * unkArg2; + return true; + } +} + +void func_80A3F908(EnGo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 float1; + s32 isUnkCondition; + + if (this->actionFunc == EnGo_BiggoronActionFunc || this->actionFunc == EnGo_GoronLinkRolling || + this->actionFunc == EnGo_FireGenericActionFunc || this->actionFunc == EnGo_Eyedrops || + this->actionFunc == func_80A40DCC || this->actionFunc == EnGo_GetItem || this->actionFunc == func_80A40C78 || + this->actionFunc == func_80A40B1C) { + + float1 = (this->collider.dim.radius + 30.0f); + float1 *= (this->actor.scale.x / 0.01f); + if ((this->actor.params & 0xF0) == 0x90) { + float1 *= 4.8f; + } + + if ((this->actor.params & 0xF0) == 0x90) { + isUnkCondition = + func_80A3ED24(globalCtx, this, &this->unk_1E0, float1, EnGo_GetTextID, EnGo_SetFlagsGetStates); + } else { + isUnkCondition = func_800343CC(globalCtx, &this->actor, &this->unk_1E0.unk_00, float1, EnGo_GetTextID, + EnGo_SetFlagsGetStates); + } + + if (((this->actor.params & 0xF0) == 0x90) && (isUnkCondition == true)) { + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_SWORD_BROKEN) { + if (func_8002F368(globalCtx) == EXCH_ITEM_SWORD_BROKEN) { + if (gSaveContext.infTable[11] & 0x10) { + this->actor.textId = 0x3055; + } else { + this->actor.textId = 0x3054; + } + } else { + this->actor.textId = 0x3053; + } + player->actor.textId = this->actor.textId; + } + + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) { + if (func_8002F368(globalCtx) == EXCH_ITEM_EYEDROPS) { + this->actor.textId = 0x3059; + } else { + this->actor.textId = 0x3058; + } + player->actor.textId = this->actor.textId; + } + } + } +} + +void EnGo_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGo* this = (EnGo*)thisx; + s32 pad; + Vec3f D_80A41B9C = { 0.0f, 0.0f, 0.0f }; // unused + Vec3f D_80A41BA8 = { 0.0f, 0.0f, 0.0f }; // unused + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGoronSkel, NULL, 0, 0, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0x16), &sColChkInfoInit); + + if (!EnGo_IsActorSpawned(this, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + + if ((this->actor.params & 0xF0) && ((this->actor.params & 0xF0) != 0x90)) { + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.flags &= ~ACTOR_FLAG_5; + } + + EnGo_ChangeAnim(this, ENGO_ANIM_0); + this->actor.targetMode = 6; + this->unk_1E0.unk_00 = 0; + this->actor.gravity = -1.0f; + + switch (this->actor.params & 0xF0) { + case 0x00: + Actor_SetScale(&this->actor, 0.008f); + if (CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1)) { + EnGo_SetMovedPos(this, globalCtx); + EnGo_SetupAction(this, EnGo_CurledUp); + } else { + this->actor.shape.yOffset = 1400.0f; + this->actor.speedXZ = 3.0f; + EnGo_SetupAction(this, EnGo_GoronLinkRolling); + } + break; + case 0x10: + this->skelAnime.curFrame = Animation_GetLastFrame(&gGoronAnim_004930); + Actor_SetScale(&this->actor, 0.01f); + EnGo_SetupAction(this, EnGo_FireGenericActionFunc); + break; + case 0x40: + if (gSaveContext.infTable[14] & 0x800) { + EnGo_SetMovedPos(this, globalCtx); + } + Actor_SetScale(&this->actor, 0.015f); + EnGo_SetupAction(this, EnGo_CurledUp); + break; + case 0x30: + this->actor.shape.yOffset = 1400.0f; + Actor_SetScale(&this->actor, 0.01f); + EnGo_SetupAction(this, func_80A3FEB4); + break; + case 0x90: + this->actor.targetMode = 5; + Actor_SetScale(&this->actor, 0.16f); + EnGo_SetupAction(this, EnGo_CurledUp); + break; + case 0x20: + case 0x50: + case 0x60: + case 0x70: + Actor_SetScale(&this->actor, 0.01f); + EnGo_SetupAction(this, EnGo_CurledUp); + break; + default: + Actor_Kill(&this->actor); + } +} + +void EnGo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGo* this = (EnGo*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80A3FEB4(EnGo* this, GlobalContext* globalCtx) { + if (!(this->actor.xyzDistToPlayerSq > SQ(1200.0f))) { + EnGo_SetupAction(this, EnGo_StopRolling); + } +} + +void EnGo_StopRolling(EnGo* this, GlobalContext* globalCtx) { + EnBom* bomb; + + if (DECR(this->unk_20E) == 0) { + if (this->collider.base.ocFlags2 & 1) { + this->collider.base.ocFlags2 &= ~1; + globalCtx->damagePlayer(globalCtx, -4); + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); + this->unk_20E = 0x10; + } + } + + this->actor.speedXZ = 3.0f; + if ((EnGo_FollowPath(this, globalCtx) == true) && (this->unk_218 == 0)) { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (bomb != NULL) { + bomb->timer = 0; + } + + this->actor.speedXZ = 0.0f; + EnGo_SetupAction(this, func_80A4008C); + } + + this->actor.shape.rot = this->actor.world.rot; + + if (EnGo_IsRollingOnGround(this, 3, 6.0f)) { + EnGo_SpawnDust(this, 12, 0.16f, 0.1f, 1, 10.0f, 20.0f); + } +} + +void func_80A4008C(EnGo* this, GlobalContext* globalCtx) { + if (EnGo_IsRollingOnGround(this, 3, 6.0f)) { + if (this->unk_21A == 0) { + this->actor.shape.yOffset = 0.0f; + EnGo_SetupAction(this, EnGo_CurledUp); + } else { + EnGo_SpawnDust(this, 12, 0.16f, 0.1f, 1, 10.0f, 20.0f); + } + } +} + +void EnGo_GoronLinkRolling(EnGo* this, GlobalContext* globalCtx) { + if ((EnGo_FollowPath(this, globalCtx) == true) && Flags_GetSwitch(globalCtx, this->actor.params >> 8) && + (this->unk_218 == 0)) { + this->actor.speedXZ = 0.0f; + EnGo_SetupAction(this, func_80A4008C); + gSaveContext.infTable[16] |= 0x200; + } + + this->actor.shape.rot = this->actor.world.rot; + + if (EnGo_IsRollingOnGround(this, 3, 6.0f)) { + EnGo_SpawnDust(this, 12, 0.18f, 0.2f, 2, 13.0f, 20.0f); + } +} + +void EnGo_FireGenericActionFunc(EnGo* this, GlobalContext* globalCtx) { +} + +void EnGo_CurledUp(EnGo* this, GlobalContext* globalCtx) { + if ((DECR(this->unk_210) == 0) && EnGo_IsCameraModified(this, globalCtx)) { + Audio_PlaySoundGeneral(NA_SE_EN_GOLON_WAKE_UP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + this->skelAnime.playSpeed = 0.1f; + this->skelAnime.playSpeed *= (this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f; + + EnGo_SetupAction(this, EnGo_WakeUp); + if ((this->actor.params & 0xF0) == 0x90) { + OnePointCutscene_Init(globalCtx, 4200, -99, &this->actor, MAIN_CAM); + } + } +} + +void EnGo_WakeUp(EnGo* this, GlobalContext* globalCtx) { + f32 frame; + + if (this->skelAnime.playSpeed != 0.0f) { + Math_SmoothStepToF(&this->skelAnime.playSpeed, ((this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f) * 0.5f, 0.1f, + 1000.0f, 0.1f); + frame = this->skelAnime.curFrame; + frame += this->skelAnime.playSpeed; + + if (frame <= 12.0f) { + return; + } else { + this->skelAnime.curFrame = 12.0f; + this->skelAnime.playSpeed = 0.0f; + if ((this->actor.params & 0xF0) != 0x90) { + this->unk_212 = 30; + return; + } + } + } + + if (DECR(this->unk_212) == 0) { + Audio_PlaySoundGeneral(NA_SE_EN_GOLON_SIT_DOWN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + EnGo_SetupAction(this, func_80A405CC); + } else if (!EnGo_IsCameraModified(this, globalCtx)) { + EnGo_ReverseAnimation(this); + this->skelAnime.playSpeed = 0.0f; + EnGo_SetupAction(this, func_80A40494); + } +} + +void func_80A40494(EnGo* this, GlobalContext* globalCtx) { + f32 frame; + + Math_SmoothStepToF(&this->skelAnime.playSpeed, ((this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f) * -0.5f, 0.1f, + 1000.0f, 0.1f); + frame = this->skelAnime.curFrame; + frame += this->skelAnime.playSpeed; + + if (!(frame >= 0.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_DODO_M_GND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + EnGo_SpawnDust(this, 10, 0.4f, 0.1f, 16, 26.0f, 2.0f); + EnGo_ReverseAnimation(this); + this->skelAnime.playSpeed = 0.0f; + this->skelAnime.curFrame = 0.0f; + this->unk_210 = Rand_S16Offset(30, 30); + EnGo_SetupAction(this, EnGo_CurledUp); + } +} + +void func_80A405CC(EnGo* this, GlobalContext* globalCtx) { + f32 lastFrame; + f32 frame; + + lastFrame = Animation_GetLastFrame(&gGoronAnim_004930); + Math_SmoothStepToF(&this->skelAnime.playSpeed, (this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f, 0.1f, 1000.0f, + 0.1f); + + frame = this->skelAnime.curFrame; + frame += this->skelAnime.playSpeed; + + if (!(frame < lastFrame)) { + this->skelAnime.curFrame = lastFrame; + this->skelAnime.playSpeed = 0.0f; + this->unk_212 = Rand_S16Offset(30, 30); + if (((this->actor.params & 0xF0) == 0x40) && ((gSaveContext.infTable[14] & 0x800) == 0)) { + EnGo_SetupAction(this, func_80A40B1C); + } else { + EnGo_SetupAction(this, EnGo_BiggoronActionFunc); + } + } +} + +void EnGo_BiggoronActionFunc(EnGo* this, GlobalContext* globalCtx) { + if (((this->actor.params & 0xF0) == 0x90) && (this->unk_1E0.unk_00 == 2)) { + if (gSaveContext.bgsFlag) { + this->unk_1E0.unk_00 = 0; + } else { + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) { + EnGo_ChangeAnim(this, ENGO_ANIM_2); + this->unk_21E = 100; + this->unk_1E0.unk_00 = 0; + EnGo_SetupAction(this, EnGo_Eyedrops); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + gSaveContext.timer2State = 0; + OnePointCutscene_Init(globalCtx, 4190, -99, &this->actor, MAIN_CAM); + } else { + this->unk_1E0.unk_00 = 0; + EnGo_SetupAction(this, EnGo_GetItem); + Message_CloseTextbox(globalCtx); + EnGo_GetItem(this, globalCtx); + } + } + } else if (((this->actor.params & 0xF0) == 0) && (this->unk_1E0.unk_00 == 2)) { + EnGo_SetupAction(this, EnGo_GetItem); + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } else { + if ((DECR(this->unk_212) == 0) && !EnGo_IsCameraModified(this, globalCtx)) { + EnGo_ReverseAnimation(this); + this->skelAnime.playSpeed = -0.1f; + this->skelAnime.playSpeed *= (this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f; + EnGo_SetupAction(this, func_80A408D8); + } + } +} + +void func_80A408D8(EnGo* this, GlobalContext* globalCtx) { + f32 frame; + + if (this->skelAnime.playSpeed != 0.0f) { + Math_SmoothStepToF(&this->skelAnime.playSpeed, ((this->actor.params & 0xF0) == 0x90 ? 0.5f : 1.0f) * -1.0f, + 0.1f, 1000.0f, 0.1f); + frame = this->skelAnime.curFrame; + frame += this->skelAnime.playSpeed; + if (frame >= 12.0f) { + return; + } else { + this->skelAnime.curFrame = 12.0f; + this->skelAnime.playSpeed = 0.0f; + if ((this->actor.params & 0xF0) != 0x90) { + this->unk_212 = 30; + return; + } + } + } + + if (DECR(this->unk_212) == 0) { + EnGo_SetupAction(this, func_80A40494); + } else if (EnGo_IsCameraModified(this, globalCtx)) { + EnGo_ReverseAnimation(this); + Audio_PlaySoundGeneral(NA_SE_EN_GOLON_SIT_DOWN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->skelAnime.playSpeed = 0.0f; + EnGo_SetupAction(this, func_80A405CC); + } +} + +void func_80A40A54(EnGo* this, GlobalContext* globalCtx) { + f32 float1 = ((f32)0x8000 / Animation_GetLastFrame(&gGoronAnim_010590)); + f32 float2 = this->skelAnime.curFrame * float1; + + this->actor.speedXZ = Math_SinS((s16)float2); + if (EnGo_FollowPath(this, globalCtx) && this->unk_218 == 0) { + EnGo_ChangeAnim(this, ENGO_ANIM_1); + this->skelAnime.curFrame = Animation_GetLastFrame(&gGoronAnim_004930); + this->actor.speedXZ = 0.0f; + EnGo_SetupAction(this, EnGo_BiggoronActionFunc); + } +} + +void func_80A40B1C(EnGo* this, GlobalContext* globalCtx) { + if (gSaveContext.infTable[14] & 0x800) { + EnGo_ChangeAnim(this, ENGO_ANIM_3); + EnGo_SetupAction(this, func_80A40A54); + } else { + EnGo_BiggoronActionFunc(this, globalCtx); + } +} + +void EnGo_GetItem(EnGo* this, GlobalContext* globalCtx) { + f32 xzDist; + f32 yDist; + s32 getItemId; + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->unk_1E0.unk_00 = 2; + this->actor.parent = NULL; + EnGo_SetupAction(this, func_80A40C78); + } else { + this->unk_20C = 0; + if ((this->actor.params & 0xF0) == 0x90) { + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) { + getItemId = GI_SWORD_BGS; + this->unk_20C = 1; + } + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) { + getItemId = GI_CLAIM_CHECK; + } + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_SWORD_BROKEN) { + getItemId = GI_PRESCRIPTION; + } + } + + if ((this->actor.params & 0xF0) == 0) { + getItemId = GI_TUNIC_GORON; + } + + yDist = fabsf(this->actor.yDistToPlayer) + 1.0f; + xzDist = this->actor.xzDistToPlayer + 1.0f; + func_8002F434(&this->actor, globalCtx, getItemId, xzDist, yDist); + } +} + +void func_80A40C78(EnGo* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 3) { + EnGo_SetupAction(this, EnGo_BiggoronActionFunc); + if ((this->actor.params & 0xF0) != 0x90) { + this->unk_1E0.unk_00 = 0; + } else if (this->unk_20C) { + this->unk_1E0.unk_00 = 0; + gSaveContext.bgsFlag = true; + } else if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_PRESCRIPTION) { + this->actor.textId = 0x3058; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E0.unk_00 = 1; + } else if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) { + this->actor.textId = 0x305C; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E0.unk_00 = 1; + Environment_ClearBgsDayCount(); + } + } +} + +void EnGo_Eyedrops(EnGo* this, GlobalContext* globalCtx) { + if (DECR(this->unk_21E) == 0) { + this->actor.textId = 0x305A; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E0.unk_00 = 1; + EnGo_SetupAction(this, func_80A40DCC); + } +} + +void func_80A40DCC(EnGo* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 2) { + EnGo_ChangeAnim(this, ENGO_ANIM_1); + this->skelAnime.curFrame = Animation_GetLastFrame(&gGoronAnim_004930); + Message_CloseTextbox(globalCtx); + EnGo_SetupAction(this, EnGo_GetItem); + EnGo_GetItem(this, globalCtx); + } +} + +void EnGo_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnGo* this = (EnGo*)thisx; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + + if (this->actionFunc == EnGo_BiggoronActionFunc || this->actionFunc == EnGo_FireGenericActionFunc || + this->actionFunc == func_80A40B1C) { + func_80034F54(globalCtx, this->jointTable, this->morphTable, 18); + } + + EnGo_UpdateShadow(this); + + if (this->unk_1E0.unk_00 == 0) { + Actor_MoveForward(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + func_80A3F0E4(this); + func_80A3F908(this, globalCtx); + this->actionFunc(this, globalCtx); + func_80A3F060(this, globalCtx); +} + +void EnGo_DrawCurledUp(EnGo* this, GlobalContext* globalCtx) { + Vec3f D_80A41BB4 = { 0.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2320); + + Matrix_Push(); + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_go.c", 2326), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gGoronDL_00BD80); + + Matrix_MultVec3f(&D_80A41BB4, &this->actor.focus.pos); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2341); +} + +void EnGo_DrawRolling(EnGo* this, GlobalContext* globalCtx) { + Vec3f D_80A41BC0 = { 0.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2355); + + Matrix_Push(); + func_80093D18(globalCtx->state.gfxCtx); + Matrix_RotateZYX((s16)(globalCtx->state.frames * ((s16)this->actor.speedXZ * 1400)), 0, this->actor.shape.rot.z, + MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_go.c", 2368), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGoronDL_00C140); + Matrix_MultVec3f(&D_80A41BC0, &this->actor.focus.pos); + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2383); +} + +s32 EnGo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limb, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnGo* this = (EnGo*)thisx; + Vec3s vec1; + f32 float1; + + if (limb == 17) { + Matrix_Translate(2800.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec1 = this->unk_1E0.unk_08; + float1 = (vec1.y / (f32)0x8000) * M_PI; + Matrix_RotateX(float1, MTXMODE_APPLY); + float1 = (vec1.x / (f32)0x8000) * M_PI; + Matrix_RotateZ(float1, MTXMODE_APPLY); + Matrix_Translate(-2800.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + + if (limb == 10) { + vec1 = this->unk_1E0.unk_0E; + float1 = (vec1.y / (f32)0x8000) * M_PI; + Matrix_RotateY(float1, MTXMODE_APPLY); + float1 = (vec1.x / (f32)0x8000) * M_PI; + Matrix_RotateX(float1, MTXMODE_APPLY); + } + + if ((limb == 10) || (limb == 11) || (limb == 14)) { + float1 = Math_SinS(this->jointTable[limb]); + rot->y += float1 * 200.0f; + float1 = Math_CosS(this->morphTable[limb]); + rot->z += float1 * 200.0f; + } + + return 0; +} + +void EnGo_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnGo* this = (EnGo*)thisx; + Vec3f D_80A41BCC = { 600.0f, 0.0f, 0.0f }; + + if (limbIndex == 17) { + Matrix_MultVec3f(&D_80A41BCC, &this->actor.focus.pos); + } +} + +void EnGo_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGo* this = (EnGo*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2479); + + EnGo_UpdateDust(this); + Matrix_Push(); + EnGo_DrawDust(this, globalCtx); + Matrix_Pop(); + + if (this->actionFunc == EnGo_CurledUp) { + EnGo_DrawCurledUp(this, globalCtx); + return; // needed for match? + } else if (this->actionFunc == EnGo_GoronLinkRolling || this->actionFunc == func_80A3FEB4 || + this->actionFunc == EnGo_StopRolling || this->actionFunc == func_80A3FEB4) { + EnGo_DrawRolling(this, globalCtx); + return; // needed for match? + } else { + func_800943C8(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gGoronCsEyeOpenTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gGoronCsMouthNeutralTex)); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnGo_OverrideLimbDraw, EnGo_PostLimbDraw, &this->actor); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2525); + EnGo_DrawDust(this, globalCtx); + } +} + +void EnGo_AddDust(EnGo* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale, f32 scaleStep) { + EnGoEffect* dustEffect = this->dustEffects; + s16 i; + s16 timer; + + for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { + if (dustEffect->type != 1) { + dustEffect->scale = scale; + dustEffect->scaleStep = scaleStep; + if (1) {} + timer = initialTimer; + dustEffect->timer = timer; + dustEffect->type = 1; + dustEffect->initialTimer = initialTimer; + dustEffect->pos = *pos; + dustEffect->accel = *accel; + dustEffect->velocity = *velocity; + return; + } + } +} + +void EnGo_UpdateDust(EnGo* this) { + EnGoEffect* dustEffect = this->dustEffects; + f32 randomNumber; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { + if (dustEffect->type) { + dustEffect->timer--; + if (dustEffect->timer == 0) { + dustEffect->type = 0; + } + + dustEffect->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + randomNumber = Rand_ZeroOne() * 0.4f; + dustEffect->accel.z = randomNumber - 0.2f; + dustEffect->pos.x += dustEffect->velocity.x; + dustEffect->pos.y += dustEffect->velocity.y; + dustEffect->pos.z += dustEffect->velocity.z; + dustEffect->velocity.x += dustEffect->accel.x; + dustEffect->velocity.y += dustEffect->accel.y; + dustEffect->velocity.z += randomNumber - 0.2f; + dustEffect->scale += dustEffect->scaleStep; + } + } +} + +void EnGo_DrawDust(EnGo* this, GlobalContext* globalCtx) { + static void* dustTex[] = { gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex }; + EnGoEffect* dustEffect = this->dustEffects; + s16 alpha; + s16 firstDone; + s16 index; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2626); + firstDone = false; + func_80093D84(globalCtx->state.gfxCtx); + if (1) {} + for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { + if (dustEffect->type) { + if (!firstDone) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gSPDisplayList(POLY_XLU_DISP++, gGoronDL_00FD40); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 60, 20, 0); + firstDone = true; + } + + alpha = dustEffect->timer * (255.0f / dustEffect->initialTimer); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 130, 90, alpha); + gDPPipeSync(POLY_XLU_DISP++); + Matrix_Translate(dustEffect->pos.x, dustEffect->pos.y, dustEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(dustEffect->scale, dustEffect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_go.c", 2664), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + index = dustEffect->timer * (8.0f / dustEffect->initialTimer); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTex[index])); + gSPDisplayList(POLY_XLU_DISP++, gGoronDL_00FD50); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go.c", 2678); +} diff --git a/soh/src/overlays/actors/ovl_En_Go/z_en_go.h b/soh/src/overlays/actors/ovl_En_Go/z_en_go.h new file mode 100644 index 000000000..2aef4b168 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Go/z_en_go.h @@ -0,0 +1,61 @@ +#ifndef Z_EN_GO_H +#define Z_EN_GO_H + +#include "ultra64.h" +#include "global.h" + +struct EnGo; + +typedef void (*EnGoActionFunc)(struct EnGo*, GlobalContext*); +typedef u16 (*callback1_80A3ED24)(GlobalContext*, struct EnGo*); +typedef s16 (*callback2_80A3ED24)(GlobalContext*, struct EnGo*); + +// WIP type docs +// /* 0x00 */ GORON1_CITY_LINK, +// /* 0x10 */ GORON1_FIRE_GENERIC, +// /* 0x20 */ GORON1_DMT_DC_ENTRANCE, +// /* 0x30 */ GORON1_DMT_ROLLING_SMALL, +// /* 0x40 */ GORON1_DMT_BOMB_FLOWER, +// /* 0x50 */ GORON1_CITY_ENTRANCE, +// /* 0x60 */ GORON1_CITY_ISLAND, +// /* 0x70 */ GORON1_CITY_LOST_WOODS, +// /* 0x80 */ // Not Used +// /* 0x90 */ GORON1_DMT_BIGGORON, + + +typedef struct { + /* 0x0000 */ u8 type; + /* 0x0001 */ u8 timer; + /* 0x0002 */ u8 initialTimer; + /* 0x0004 */ f32 scale; + /* 0x0008 */ f32 scaleStep; + /* 0x000C */ Color_RGBA8 color; + /* 0x0010 */ char unk_10[4]; + /* 0x0014 */ Vec3f pos; + /* 0x0020 */ Vec3f velocity; + /* 0x002C */ Vec3f accel; +} EnGoEffect; // size = 0x38 + +typedef struct EnGo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnGoActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ char unk_208[0x4]; + /* 0x020C */ s16 unk_20C; + /* 0x020E */ s16 unk_20E; + /* 0x0210 */ s16 unk_210; + /* 0x0212 */ s16 unk_212; + /* 0x0214 */ s16 unk_214; + /* 0x0216 */ s16 unk_216; + /* 0x0218 */ s16 unk_218; + /* 0x021A */ s16 unk_21A; + /* 0x021C */ s16 unk_21C; + /* 0x021E */ s16 unk_21E; + /* 0x0220 */ s16 jointTable[18]; + /* 0x0244 */ s16 morphTable[18]; + /* 0x0268 */ EnGoEffect dustEffects[20]; +} EnGo; // size = 0x06C8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c new file mode 100644 index 000000000..b9603e246 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c @@ -0,0 +1,2067 @@ +#include "z_en_go2.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_oF1d_map/object_oF1d_map.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +/* +FLAGS + +gSaveContext.eventChkInf[2] & 0x8 - DC entrance boulder blown up as child + +InfTable + +gSaveContext.infTable[11] & 0x10 +gSaveContext.infTable[14] & 0x1 - Talked to DMT Goron at DC entrance (Before DC is opened as child) +gSaveContext.infTable[14] & 0x8 - Talked to GC Goron in bottom level stairwell +gSaveContext.infTable[14] & 0x40 - Talked to GC Goron at LW entrance (Before LW shortcut is opened) +gSaveContext.infTable[14] & 0x800 - Talked to DMT Goron at Bomb Flower with goron bracelet +gSaveContext.infTable[15] & 0x1 - Talked to Goron at GC Entrance (Before goron ruby is obtained) +gSaveContext.infTable[15] & 0x10 - Talked to Goron at GC Island (Before goron ruby is obtained) +gSaveContext.infTable[15] & 0x100 - (not on cloud modding) Talked to GC Goron outside Darunias door (after opening door, +before getting goron bracelet) gSaveContext.infTable[16] & 0x200 - Obtained Fire Tunic from Goron Link +gSaveContext.infTable[16] & 0x400 - (not on cloud modding) +gSaveContext.infTable[16] & 0x800 - Spoke to Goron Link About Volvagia +gSaveContext.infTable[16] & 0x1000 - Stopped Goron Link's Rolling +gSaveContext.infTable[16] & 0x2000 - EnGo Exclusive +gSaveContext.infTable[16] & 0x4000 - Spoke to Goron Link +gSaveContext.infTable[16] & 0x8000 - (not on cloud modding) + +gSaveContext.infTable[17] & 0x4000 - Bomb bag upgrade obtained from rolling Goron + +EnGo +pathIndex: this->actor.params & 0xF +Goron: this->actor.params & 0xF0 + +EnGo2 +(this->actor.params & 0x3E0) >> 5 +(this->actor.params & 0xFC00) >> 0xA - Gorons in Fire Temple +this->actor.params & 0x1F + +Gorons only move when this->unk_194.unk_00 == 0 +*/ + +void EnGo2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGo2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGo2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGo2_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnGo2_StopRolling(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_CurledUp(EnGo2* this, GlobalContext* globalCtx); + +void func_80A46B40(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_GoronDmtBombFlowerAnimation(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_GoronRollingBigContinueRolling(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_ContinueRolling(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_SlowRolling(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_GroundRolling(EnGo2* this, GlobalContext* globalCtx); + +void EnGo2_ReverseRolling(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_SetupGetItem(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_SetGetItem(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_BiggoronEyedrops(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_GoronLinkStopRolling(EnGo2* this, GlobalContext* globalCtx); +void EnGo2_GoronFireGenericAction(EnGo2* this, GlobalContext* globalCtx); + +static void* sDustTex[] = { gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex }; + +static Vec3f sPos = { 0.0f, 0.0f, 0.0f }; +static Vec3f sVelocity = { 0.0f, 0.0f, 0.0f }; +static Vec3f sAccel = { 0.0f, 0.3f, 0.0f }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000008, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 40, 65, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { + 0, 0, 0, 0, MASS_IMMOVABLE, +}; + +const ActorInit En_Go2_InitVars = { + ACTOR_EN_GO2, + ACTORCAT_NPC, + FLAGS, + OBJECT_OF1D_MAP, + sizeof(EnGo2), + (ActorFunc)EnGo2_Init, + (ActorFunc)EnGo2_Destroy, + (ActorFunc)EnGo2_Update, + (ActorFunc)EnGo2_Draw, + NULL, +}; + +static EnGo2DataStruct1 D_80A4816C[14] = { + { 0, 0, 0, 68, 148 }, { 0, 0, 0, 24, 52 }, { 0, 320, 380, 400, 120 }, { 0, 0, 0, 30, 68 }, { 0, 0, 0, 46, 90 }, + { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, + { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, { 0, 0, 0, 30, 68 }, +}; + +static EnGo2DataStruct2 D_80A481F8[14] = { + { 30.0f, 0.026f, 6, 60.0f }, { 24.0f, 0.008f, 6, 30.0f }, { 28.0f, 0.16f, 5, 380.0f }, { 28.0f, 0.01f, 7, 40.0f }, + { 30.0f, 0.015f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, + { 28.0f, 0.01f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, + { 28.0f, 0.01f, 6, 30.0f }, { 28.0f, 0.01f, 6, 30.0f }, +}; + +static f32 D_80A482D8[14][2] = { + { 80.0f, 80.0f }, { -10.0f, -10.0f }, { 800.0f, 800.0f }, { 0.0f, 0.0f }, { 20.0f, 40.0f }, + { 20.0f, 20.0f }, { 20.0f, 20.0f }, { 20.0f, 20.0f }, { 20.0f, 20.0f }, { 20.0f, 20.0f }, + { 20.0f, 20.0f }, { 20.0f, 20.0f }, { 20.0f, 20.0f }, { 20.0f, 20.0f }, +}; + +typedef enum { + /* 0 */ ENGO2_ANIM_0, + /* 1 */ ENGO2_ANIM_1, + /* 2 */ ENGO2_ANIM_2, + /* 3 */ ENGO2_ANIM_3, + /* 4 */ ENGO2_ANIM_4, + /* 5 */ ENGO2_ANIM_5, + /* 6 */ ENGO2_ANIM_6, + /* 7 */ ENGO2_ANIM_7, + /* 8 */ ENGO2_ANIM_8, + /* 9 */ ENGO2_ANIM_9, + /* 10 */ ENGO2_ANIM_10, + /* 11 */ ENGO2_ANIM_11, + /* 12 */ ENGO2_ANIM_12 +} EnGo2Animation; + +static AnimationInfo sAnimationInfo[] = { + { &gGoronAnim_004930, 0.0f, 0.0f, -1.0f, 0x00, 0.0f }, { &gGoronAnim_004930, 0.0f, 0.0f, -1.0f, 0x00, -8.0f }, + { &gGoronAnim_0029A8, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, { &gGoronAnim_010590, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + { &gGoronAnim_003768, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, { &gGoronAnim_0038E4, 1.0f, 0.0f, -1.0f, 0x02, -8.0f }, + { &gGoronAnim_002D80, 1.0f, 0.0f, -1.0f, 0x02, -8.0f }, { &gGoronAnim_00161C, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + { &gGoronAnim_001A00, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, { &gGoronAnim_0021D0, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + { &gGoronAnim_004930, 0.0f, 0.0f, -1.0f, 0x01, -8.0f }, { &gGoronAnim_000750, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + { &gGoronAnim_000D5C, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, +}; + +static EnGo2DustEffectData sDustEffectData[2][4] = { + { + { 12, 0.2f, 0.2f, 1, 18.0f, 0.0f }, + { 12, 0.1f, 0.2f, 12, 26.0f, 0.0f }, + { 12, 0.1f, 0.3f, 4, 10.0f, 0.0f }, + { 12, 0.2f, 0.2f, 1, 18.0f, 0.0f }, + }, + { + { 12, 0.5f, 0.4f, 3, 42.0f, 0.0f }, + { 12, 0.5f, 0.4f, 3, 42.0f, 0.0f }, + { 12, 0.5f, 0.4f, 3, 42.0f, 0.0f }, + { 12, 0.5f, 0.4f, 3, 42.0f, 0.0f }, + }, +}; + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; + +void EnGo2_AddDust(EnGo2* this, Vec3f* pos, Vec3f* velocity, Vec3f* accel, u8 initialTimer, f32 scale, f32 scaleStep) { + EnGoEffect* dustEffect = this->dustEffects; + s16 i; + s16 timer; + + for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { + if (dustEffect->type != 1) { + dustEffect->scale = scale; + dustEffect->scaleStep = scaleStep; + if (1) {} + timer = initialTimer; + dustEffect->timer = timer; + dustEffect->type = 1; + dustEffect->initialTimer = initialTimer; + dustEffect->pos = *pos; + dustEffect->accel = *accel; + dustEffect->velocity = *velocity; + return; + } + } +} + +void EnGo2_UpdateDust(EnGo2* this) { + EnGoEffect* dustEffect = this->dustEffects; + f32 randomNumber; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { + if (dustEffect->type) { + dustEffect->timer--; + if (dustEffect->timer == 0) { + dustEffect->type = 0; + } + dustEffect->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + randomNumber = Rand_ZeroOne() * 0.4f; + dustEffect->accel.z = randomNumber - 0.2f; + dustEffect->pos.x += dustEffect->velocity.x; + dustEffect->pos.y += dustEffect->velocity.y; + dustEffect->pos.z += dustEffect->velocity.z; + dustEffect->velocity.x += dustEffect->accel.x; + dustEffect->velocity.y += dustEffect->accel.y; + dustEffect->velocity.z += randomNumber - 0.2f; + dustEffect->scale += dustEffect->scaleStep; + } + } +} + +void EnGo2_DrawDust(EnGo2* this, GlobalContext* globalCtx) { + EnGoEffect* dustEffect = this->dustEffects; + s16 alpha; + s16 firstDone; + s16 index; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go2_eff.c", 111); + + firstDone = false; + func_80093D84(globalCtx->state.gfxCtx); + if (1) {} + + for (i = 0; i < ARRAY_COUNT(this->dustEffects); i++, dustEffect++) { + if (dustEffect->type) { + if (!firstDone) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gSPDisplayList(POLY_XLU_DISP++, gGoronDL_00FD40); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 60, 20, 0); + firstDone = true; + } + + alpha = dustEffect->timer * (255.0f / dustEffect->initialTimer); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 130, 90, alpha); + gDPPipeSync(POLY_XLU_DISP++); + Matrix_Translate(dustEffect->pos.x, dustEffect->pos.y, dustEffect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(dustEffect->scale, dustEffect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_go2_eff.c", 137), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + index = dustEffect->timer * (8.0f / dustEffect->initialTimer); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sDustTex[index])); + gSPDisplayList(POLY_XLU_DISP++, gGoronDL_00FD50); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go2_eff.c", 151); +} + +s32 EnGo2_SpawnDust(EnGo2* this, u8 initialTimer, f32 scale, f32 scaleStep, s32 numDustEffects, f32 radius, + f32 yAccel) { + Vec3f pos = sPos; + Vec3f velocity = sVelocity; + Vec3f accel = sAccel; + s32 i; + s16 angle; + + pos = this->actor.world.pos; // overwrites sPos data + pos.y = this->actor.floorHeight; + angle = (Rand_ZeroOne() - 0.5f) * 0x10000; + i = numDustEffects; + while (i >= 0) { + accel.y += Rand_ZeroOne() * yAccel; + pos.x = (Math_SinS(angle) * radius) + this->actor.world.pos.x; + pos.z = (Math_CosS(angle) * radius) + this->actor.world.pos.z; + EnGo2_AddDust(this, &pos, &velocity, &accel, initialTimer, scale, scaleStep); + angle += (s16)(0x10000 / numDustEffects); + i--; + } + return 0; +} + +void EnGo2_GetItem(EnGo2* this, GlobalContext* globalCtx, s32 getItemId) { + this->getItemId = getItemId; + func_8002F434(&this->actor, globalCtx, getItemId, this->actor.xzDistToPlayer + 1.0f, + fabsf(this->actor.yDistToPlayer) + 1.0f); +} + +s32 EnGo2_GetDialogState(EnGo2* this, GlobalContext* globalCtx) { + s16 dialogState = Message_GetState(&globalCtx->msgCtx); + + if ((this->dialogState == TEXT_STATE_AWAITING_NEXT) || (this->dialogState == TEXT_STATE_EVENT) || + (this->dialogState == TEXT_STATE_CLOSING) || (this->dialogState == TEXT_STATE_DONE_HAS_NEXT)) { + if (dialogState != this->dialogState) { + this->unk_20C++; + } + } + + this->dialogState = dialogState; + return dialogState; +} + +u16 EnGo2_GoronFireGenericGetTextId(EnGo2* this) { + switch ((this->actor.params & 0xFC00) >> 0xA) { + case 3: + return 0x3069; + case 5: + return 0x306A; + case 4: + return 0x306B; + case 2: + return 0x306C; + case 10: + return 0x306D; + case 8: + return 0x306E; + case 11: + return 0x306F; + case 1: + return 0x3070; + default: + return 0x3052; + } +} + +u16 EnGo2_GetTextIdGoronCityRollingBig(GlobalContext* globalCtx, EnGo2* this) { + if (gSaveContext.infTable[17] & 0x4000) { + return 0x3013; + } else if (CUR_CAPACITY(UPG_BOMB_BAG) >= 20 && this->waypoint > 7 && this->waypoint < 12) { + return 0x3012; + } else { + return 0x3011; + } +} + +s16 EnGo2_GetStateGoronCityRollingBig(GlobalContext* globalCtx, EnGo2* this) { + s32 bombBagUpgrade; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CLOSING: + return 2; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + if (this->actor.textId == 0x3012) { + this->actionFunc = EnGo2_SetupGetItem; + bombBagUpgrade = CUR_CAPACITY(UPG_BOMB_BAG) == 30 ? GI_BOMB_BAG_40 : GI_BOMB_BAG_30; + EnGo2_GetItem(this, globalCtx, bombBagUpgrade); + Message_CloseTextbox(globalCtx); + gSaveContext.infTable[17] |= 0x4000; + return 2; + } else { + return 2; + } + } + default: + return 1; + } +} + +u16 EnGo2_GetTextIdGoronDmtBombFlower(GlobalContext* globalCtx, EnGo2* this) { + return CHECK_QUEST_ITEM(QUEST_GORON_RUBY) ? 0x3027 : 0x300A; +} + +// DMT Goron by Bomb Flower Choice +s16 EnGo2_GetStateGoronDmtBombFlower(GlobalContext* globalCtx, EnGo2* this) { + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CLOSING: + if ((this->actor.textId == 0x300B) && (gSaveContext.infTable[14] & 0x800) == 0) { + gSaveContext.infTable[14] |= 0x800; + return 2; + } else { + return 0; + } + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + // Ask question to DMT Goron by bomb flower + if (this->actor.textId == 0x300A) { + if (globalCtx->msgCtx.choiceIndex == 0) { + this->actor.textId = CUR_UPG_VALUE(UPG_STRENGTH) != 0 ? 0x300B : 0x300C; + } else { + this->actor.textId = 0x300D; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + return 1; + } + default: + return 1; + } +} + +u16 EnGo2_GetTextIdGoronDmtRollingSmall(GlobalContext* globalCtx, EnGo2* this) { + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else { + return (gSaveContext.eventChkInf[2] & 0x8) ? 0x3026 : 0x3009; + } +} + +s16 EnGo2_GetStateGoronDmtRollingSmall(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextIdGoronDmtDcEntrance(GlobalContext* globalCtx, EnGo2* this) { + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && LINK_IS_ADULT) { + return 0x3043; + } else if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else { + return gSaveContext.eventChkInf[2] & 0x8 ? 0x3021 : gSaveContext.infTable[14] & 0x1 ? 0x302A : 0x3008; + } +} + +s16 EnGo2_GetStateGoronDmtDcEntrance(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->actor.textId == 0x3008) { + gSaveContext.infTable[14] |= 0x1; + } + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextIdGoronCityEntrance(GlobalContext* globalCtx, EnGo2* this) { + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && LINK_IS_ADULT) { + return 0x3043; + } else if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else { + return gSaveContext.infTable[15] & 0x1 ? 0x3015 : 0x3014; + } +} + +s16 EnGo2_GetStateGoronCityEntrance(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->actor.textId == 0x3014) { + gSaveContext.infTable[15] |= 0x1; + } + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextIdGoronCityIsland(GlobalContext* globalCtx, EnGo2* this) { + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && LINK_IS_ADULT) { + return 0x3043; + } else if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3067; + } else { + return gSaveContext.infTable[15] & 0x10 ? 0x3017 : 0x3016; + } +} + +s16 EnGo2_GetStateGoronCityIsland(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->actor.textId == 0x3016) { + gSaveContext.infTable[15] |= 0x10; + } + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextIdGoronCityLowestFloor(GlobalContext* globalCtx, EnGo2* this) { + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && LINK_IS_ADULT) { + return 0x3043; + } else if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + return 0x3027; + } else { + return CUR_UPG_VALUE(UPG_STRENGTH) != 0 ? 0x302C + : !Flags_GetSwitch(globalCtx, 0x1B) ? 0x3017 + : gSaveContext.infTable[15] & 0x100 ? 0x3019 + : 0x3018; + } +} + +s16 EnGo2_GetStateGoronCityLowestFloor(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->actor.textId == 0x3018) { + gSaveContext.infTable[15] |= 0x100; + } + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextIdGoronCityLink(GlobalContext* globalCtx, EnGo2* this) { + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) { + return gSaveContext.infTable[16] & 0x8000 ? 0x3042 : 0x3041; + } else if (CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1)) { + return gSaveContext.infTable[16] & 0x4000 ? 0x3038 : 0x3037; + } else if (gSaveContext.infTable[16] & 0x1000) { + this->unk_20C = 0; + this->dialogState = TEXT_STATE_NONE; + return gSaveContext.infTable[16] & 0x400 ? 0x3033 : 0x3032; + } else { + return 0x3030; + } +} + +s16 EnGo2_GetStateGoronCityLink(GlobalContext* globalCtx, EnGo2* this) { + switch (EnGo2_GetDialogState(this, globalCtx)) { + case TEXT_STATE_CLOSING: + switch (this->actor.textId) { + case 0x3036: + EnGo2_GetItem(this, globalCtx, GI_TUNIC_GORON); + this->actionFunc = EnGo2_SetupGetItem; + return 2; + case 0x3037: + gSaveContext.infTable[16] |= 0x4000; + default: + return 0; + } + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + if (this->actor.textId == 0x3034) { + if (globalCtx->msgCtx.choiceIndex == 0) { + this->actor.textId = gSaveContext.infTable[16] & 0x800 ? 0x3033 : 0x3035; + if (this->actor.textId == 0x3035) { + Audio_StopSfxById(NA_SE_EN_GOLON_CRY); + } + } else { + this->actor.textId = gSaveContext.infTable[16] & 0x800 ? 0x3036 : 0x3033; + if (this->actor.textId == 0x3036) { + Audio_StopSfxById(NA_SE_EN_GOLON_CRY); + } + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_20C = 0; + } + } else { + break; + } + return 1; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + switch (this->actor.textId) { + case 0x3035: + gSaveContext.infTable[16] |= 0x800; + case 0x3032: + case 0x3033: + this->actor.textId = 0x3034; + Message_ContinueTextbox(globalCtx, this->actor.textId); + return 1; + default: + return 2; + } + } + } + return 1; +} + +u16 EnGo2_GetTextIdGoronDmtBiggoron(GlobalContext* globalCtx, EnGo2* this) { + Player* player = GET_PLAYER(globalCtx); + + if (gSaveContext.bgsFlag) { + player->exchangeItemId = EXCH_ITEM_CLAIM_CHECK; + return 0x305E; + } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) { + player->exchangeItemId = EXCH_ITEM_CLAIM_CHECK; + return 0x305E; + } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_PRESCRIPTION) { + player->exchangeItemId = EXCH_ITEM_EYEDROPS; + return 0x3058; + } else { + player->exchangeItemId = EXCH_ITEM_SWORD_BROKEN; + return 0x3053; + } +} + +s16 EnGo2_GetStateGoronDmtBiggoron(GlobalContext* globalCtx, EnGo2* this) { + s32 unusedPad; + u8 dialogState = this->dialogState; + + switch (EnGo2_GetDialogState(this, globalCtx)) { + case TEXT_STATE_DONE: + if (this->actor.textId == 0x305E) { + if (!gSaveContext.bgsFlag) { + EnGo2_GetItem(this, globalCtx, GI_SWORD_BGS); + this->actionFunc = EnGo2_SetupGetItem; + return 2; + } else { + return 0; + } + } else { + return 0; + } + case TEXT_STATE_DONE_FADING: + switch (this->actor.textId) { + case 0x305E: + if (func_8002F368(globalCtx) != EXCH_ITEM_CLAIM_CHECK) { + break; + } + case 0x3059: + if (dialogState == TEXT_STATE_NONE) { + func_800F4524(&D_801333D4, NA_SE_EN_GOLON_WAKE_UP, 60); + } + case 0x3054: + if (dialogState == TEXT_STATE_NONE) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + return 1; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + if ((this->actor.textId == 0x3054) || (this->actor.textId == 0x3055)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + EnGo2_GetItem(this, globalCtx, GI_PRESCRIPTION); + this->actionFunc = EnGo2_SetupGetItem; + return 2; + } + this->actor.textId = 0x3056; + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + return 1; + } + break; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + if (this->actor.textId == 0x3059) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actionFunc = EnGo2_BiggoronEyedrops; + } + return 2; + } + } + return 1; +} + +u16 EnGo2_GetTextIdGoronFireGeneric(GlobalContext* globalCtx, EnGo2* this) { + if (Flags_GetSwitch(globalCtx, (this->actor.params & 0xFC00) >> 0xA)) { + return 0x3071; + } else { + return 0x3051; + } +} + +s16 EnGo2_GetStateGoronFireGeneric(GlobalContext* globalCtx, EnGo2* this) { + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CLOSING: + return 0; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + if (this->actor.textId == 0x3071) { + this->actor.textId = EnGo2_GoronFireGenericGetTextId(this); + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + return 1; + } + default: + return 1; + } +} + +u16 EnGo2_GetTextIdGoronCityStairwell(GlobalContext* globalCtx, EnGo2* this) { + return !LINK_IS_ADULT ? gSaveContext.infTable[14] & 0x8 ? 0x3022 : 0x300E : 0x3043; +} + +s16 EnGo2_GetStateGoronCityStairwell(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->actor.textId == 0x300E) { + gSaveContext.infTable[14] |= 0x8; + } + return 0; + } else { + return 1; + } +} + +// Goron in child market bazaar after obtaining Goron Ruby +u16 EnGo2_GetTextIdGoronMarketBazaar(GlobalContext* globalCtx, EnGo2* this) { + return 0x7122; +} + +s16 EnGo2_GetStateGoronMarketBazaar(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextIdGoronCityLostWoods(GlobalContext* globalCtx, EnGo2* this) { + if (!LINK_IS_ADULT) { + if (Flags_GetSwitch(globalCtx, 0x1C)) { + return 0x302F; + } else { + return gSaveContext.infTable[14] & 0x40 ? 0x3025 : 0x3024; + } + } else { + return 0x3043; + } +} + +s16 EnGo2_GetStateGoronCityLostWoods(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->actor.textId == 0x3024) { + gSaveContext.infTable[14] |= 0x40; + } + return 0; + } else { + return 1; + } +} + +// Goron at base of DMT summit +u16 EnGo2_GetTextIdGoronDmtFairyHint(GlobalContext* globalCtx, EnGo2* this) { + if (!LINK_IS_ADULT) { + return CHECK_QUEST_ITEM(QUEST_GORON_RUBY) ? 0x3065 : 0x3064; + } else { + return 0x3043; + } +} + +s16 EnGo2_GetStateGoronDmtFairyHint(GlobalContext* globalCtx, EnGo2* this) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + return 0; + } else { + return 1; + } +} + +u16 EnGo2_GetTextId(GlobalContext* globalCtx, Actor* thisx) { + EnGo2* this = (EnGo2*)thisx; + u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x20); + + if (faceReaction) { + return faceReaction; + } else { + switch (this->actor.params & 0x1F) { + case GORON_CITY_ROLLING_BIG: + return EnGo2_GetTextIdGoronCityRollingBig(globalCtx, this); + case GORON_CITY_LINK: + return EnGo2_GetTextIdGoronCityLink(globalCtx, this); + case GORON_DMT_BIGGORON: + return EnGo2_GetTextIdGoronDmtBiggoron(globalCtx, this); + case GORON_FIRE_GENERIC: + return EnGo2_GetTextIdGoronFireGeneric(globalCtx, this); + case GORON_DMT_BOMB_FLOWER: + return EnGo2_GetTextIdGoronDmtBombFlower(globalCtx, this); + case GORON_DMT_ROLLING_SMALL: + return EnGo2_GetTextIdGoronDmtRollingSmall(globalCtx, this); + case GORON_DMT_DC_ENTRANCE: + return EnGo2_GetTextIdGoronDmtDcEntrance(globalCtx, this); + case GORON_CITY_ENTRANCE: + return EnGo2_GetTextIdGoronCityEntrance(globalCtx, this); + case GORON_CITY_ISLAND: + return EnGo2_GetTextIdGoronCityIsland(globalCtx, this); + case GORON_CITY_LOWEST_FLOOR: + return EnGo2_GetTextIdGoronCityLowestFloor(globalCtx, this); + case GORON_CITY_STAIRWELL: + return EnGo2_GetTextIdGoronCityStairwell(globalCtx, this); + case GORON_CITY_LOST_WOODS: + return EnGo2_GetTextIdGoronCityLostWoods(globalCtx, this); + case GORON_DMT_FAIRY_HINT: + return EnGo2_GetTextIdGoronDmtFairyHint(globalCtx, this); + case GORON_MARKET_BAZAAR: + return EnGo2_GetTextIdGoronMarketBazaar(globalCtx, this); + } + } +} + +s16 EnGo2_GetState(GlobalContext* globalCtx, Actor* thisx) { + EnGo2* this = (EnGo2*)thisx; + switch (this->actor.params & 0x1F) { + case GORON_CITY_ROLLING_BIG: + return EnGo2_GetStateGoronCityRollingBig(globalCtx, this); + case GORON_CITY_LINK: + return EnGo2_GetStateGoronCityLink(globalCtx, this); + case GORON_DMT_BIGGORON: + return EnGo2_GetStateGoronDmtBiggoron(globalCtx, this); + case GORON_FIRE_GENERIC: + return EnGo2_GetStateGoronFireGeneric(globalCtx, this); + case GORON_DMT_BOMB_FLOWER: + return EnGo2_GetStateGoronDmtBombFlower(globalCtx, this); + case GORON_DMT_ROLLING_SMALL: + return EnGo2_GetStateGoronDmtRollingSmall(globalCtx, this); + case GORON_DMT_DC_ENTRANCE: + return EnGo2_GetStateGoronDmtDcEntrance(globalCtx, this); + case GORON_CITY_ENTRANCE: + return EnGo2_GetStateGoronCityEntrance(globalCtx, this); + case GORON_CITY_ISLAND: + return EnGo2_GetStateGoronCityIsland(globalCtx, this); + case GORON_CITY_LOWEST_FLOOR: + return EnGo2_GetStateGoronCityLowestFloor(globalCtx, this); + case GORON_CITY_STAIRWELL: + return EnGo2_GetStateGoronCityStairwell(globalCtx, this); + case GORON_CITY_LOST_WOODS: + return EnGo2_GetStateGoronCityLostWoods(globalCtx, this); + case GORON_DMT_FAIRY_HINT: + return EnGo2_GetStateGoronDmtFairyHint(globalCtx, this); + case GORON_MARKET_BAZAAR: + return EnGo2_GetStateGoronMarketBazaar(globalCtx, this); + } +} + +s32 func_80A44790(EnGo2* this, GlobalContext* globalCtx) { + if ((this->actor.params & 0x1F) != GORON_DMT_BIGGORON && (this->actor.params & 0x1F) != GORON_CITY_ROLLING_BIG) { + return func_800343CC(globalCtx, &this->actor, &this->unk_194.unk_00, this->unk_218, EnGo2_GetTextId, + EnGo2_GetState); + } else if (((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) && ((this->collider.base.ocFlags2 & 1) == 0)) { + return false; + } else { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_194.unk_00 = 1; + return true; + } else if (this->unk_194.unk_00 != 0) { + this->unk_194.unk_00 = EnGo2_GetState(globalCtx, &this->actor); + return false; + } else if (func_8002F2CC(&this->actor, globalCtx, this->unk_218)) { + this->actor.textId = EnGo2_GetTextId(globalCtx, &this->actor); + } + return false; + } +} + +void EnGo2_SetColliderDim(EnGo2* this) { + u8 index = this->actor.params & 0x1F; + + this->collider.dim.radius = D_80A4816C[index].radius; + this->collider.dim.height = D_80A4816C[index].height; +} + +void EnGo2_SetShape(EnGo2* this) { + u8 index = this->actor.params & 0x1F; + + this->actor.shape.shadowScale = D_80A481F8[index].shape_unk_10; + Actor_SetScale(&this->actor, D_80A481F8[index].scale); + this->actor.targetMode = D_80A481F8[index].actor_unk_1F; + this->unk_218 = D_80A481F8[index].unk_218; + this->unk_218 += this->collider.dim.radius; +} + +void EnGo2_CheckCollision(EnGo2* this, GlobalContext* globalCtx) { + Vec3s pos; + f32 xzDist; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z; + xzDist = D_80A4816C[this->actor.params & 0x1F].xzDist; + pos.x += (s16)(xzDist * Math_SinS(this->actor.shape.rot.y)); + pos.z += (s16)(xzDist * Math_CosS(this->actor.shape.rot.y)); + pos.y += D_80A4816C[this->actor.params & 0x1F].yDist; + this->collider.dim.pos = pos; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnGo2_SwapInitialFrameAnimFrameCount(EnGo2* this) { + f32 initialFrame; + + initialFrame = this->skelAnime.startFrame; + this->skelAnime.startFrame = this->skelAnime.endFrame; + this->skelAnime.endFrame = initialFrame; +} + +s32 func_80A44AB0(EnGo2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 arg2; + + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + return false; + } else { + if ((this->actionFunc != EnGo2_SlowRolling) && (this->actionFunc != EnGo2_ReverseRolling) && + (this->actionFunc != EnGo2_ContinueRolling)) { + return false; + } else { + if (this->collider.base.acFlags & 2) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->actor.flags &= ~ACTOR_FLAG_24; + this->collider.base.acFlags &= ~0x2; + EnGo2_StopRolling(this, globalCtx); + return true; + } + if (player->invincibilityTimer <= 0) { + this->collider.base.ocFlags1 |= 8; + } else { + return false; + } + if (this->collider.base.ocFlags2 & 1) { + this->collider.base.ocFlags2 &= ~1; + + arg2 = this->actionFunc == EnGo2_ContinueRolling ? 1.5f : this->actor.speedXZ * 1.5f; + + globalCtx->damagePlayer(globalCtx, -4); + func_8002F71C(globalCtx, &this->actor, arg2, this->actor.yawTowardsPlayer, 6.0f); + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + this->collider.base.ocFlags1 &= ~0x8; + } + } + } + return false; +} + +s32 EnGo2_UpdateWaypoint(EnGo2* this, GlobalContext* globalCtx) { + s32 change; + + if (this->path == NULL) { + return 0; + } + + change = (u8)(this->path->count - 1); + if (this->reverse) { + this->waypoint--; + if (this->waypoint < 0) { + this->waypoint = change - 1; + } + } else { + this->waypoint++; + if (this->waypoint >= change) { + this->waypoint = 0; + } + } + + return 1; +} + +s32 EnGo2_Orient(EnGo2* this, GlobalContext* globalCtx) { + s16 targetYaw; + f32 waypointDistSq = Path_OrientAndGetDistSq(&this->actor, this->path, this->waypoint, &targetYaw); + + Math_SmoothStepToS(&this->actor.world.rot.y, targetYaw, 6, 4000, 1); + if (waypointDistSq > 0.0f && waypointDistSq < SQ(30.0f)) { + return EnGo2_UpdateWaypoint(this, globalCtx); + } else { + return 0; + } +} + +s32 func_80A44D84(EnGo2* this) { + s16 targetYaw; + + Path_OrientAndGetDistSq(&this->actor, this->path, this->waypoint, &targetYaw); + this->actor.world.rot.y = targetYaw; + return 1; +} + +s32 EnGo2_IsWakingUp(EnGo2* this) { + s16 yawDiff; + f32 xyzDist = (this->actor.params & 0x1F) == GORON_DMT_BIGGORON ? 800.0f : 200.0f; + f32 yDist = (this->actor.params & 0x1F) == GORON_DMT_BIGGORON ? 400.0f : 60.0f; + s16 yawDiffAbs; + + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + if ((this->collider.base.ocFlags2 & 1) == 0) { + this->actor.flags &= ~ACTOR_FLAG_0; + return false; + } else { + this->actor.flags |= ACTOR_FLAG_0; + return true; + } + } + + xyzDist = SQ(xyzDist); + yawDiff = (f32)this->actor.yawTowardsPlayer - (f32)this->actor.shape.rot.y; + yawDiffAbs = ABS(yawDiff); + if (this->actor.xyzDistToPlayerSq <= xyzDist && fabsf(this->actor.yDistToPlayer) < yDist && yawDiffAbs < 0x2AA8) { + return true; + } else { + return false; + } +} + +s32 EnGo2_IsRollingOnGround(EnGo2* this, s16 arg1, f32 arg2, s16 arg3) { + if ((this->actor.bgCheckFlags & 1) == 0 || this->actor.velocity.y > 0.0f) { + return false; + } + + if (DECR(this->unk_590)) { + if (!arg3) { + return true; + } else { + this->actor.world.pos.y = + (this->unk_590 & 1) ? this->actor.world.pos.y + 1.5f : this->actor.world.pos.y - 1.5f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BIGBALL_ROLL - SFX_FLAG); + return true; + } + } + + if (this->unk_59C >= 2) { + Audio_PlayActorSound2(&this->actor, (this->actor.params & 0x1F) == GORON_CITY_ROLLING_BIG + ? NA_SE_EN_GOLON_LAND_BIG + : NA_SE_EN_DODO_M_GND); + } + + this->unk_59C--; + if (this->unk_59C <= 0) { + if (this->unk_59C == 0) { + this->unk_590 = Rand_S16Offset(60, 30); + this->unk_59C = 0; + this->actor.velocity.y = 0.0f; + return true; + } else { + this->unk_59C = arg1; + } + } + + this->actor.velocity.y = ((f32)this->unk_59C / (f32)arg1) * arg2; + return true; +} + +void EnGo2_BiggoronSetTextId(EnGo2* this, GlobalContext* globalCtx, Player* player) { + u16 textId; + + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + if (gSaveContext.bgsFlag) { + if (func_8002F368(globalCtx) == EXCH_ITEM_CLAIM_CHECK) { + this->actor.textId = 0x3003; + } else { + this->actor.textId = 0x305E; + } + player->actor.textId = this->actor.textId; + + } else if (!gSaveContext.bgsFlag && (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK)) { + if (func_8002F368(globalCtx) == EXCH_ITEM_CLAIM_CHECK) { + if (Environment_GetBgsDayCount() >= 3) { + textId = 0x305E; + } else { + textId = 0x305D; + } + this->actor.textId = textId; + } else { + if (Environment_GetBgsDayCount() >= 3) { + textId = 0x3002; + } else { + textId = 0x305D; + } + this->actor.textId = textId; + } + player->actor.textId = this->actor.textId; + + } else if ((INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_PRESCRIPTION) && + (INV_CONTENT(ITEM_TRADE_ADULT) <= ITEM_CLAIM_CHECK)) { + if (func_8002F368(globalCtx) == EXCH_ITEM_EYEDROPS) { + this->actor.textId = 0x3059; + } else { + this->actor.textId = 0x3058; + } + if (this->actor.textId == 0x3059) { + gSaveContext.timer2State = 0; + } + player->actor.textId = this->actor.textId; + + } else if (INV_CONTENT(ITEM_TRADE_ADULT) <= ITEM_SWORD_BROKEN) { + if (func_8002F368(globalCtx) == EXCH_ITEM_SWORD_BROKEN) { + if (gSaveContext.infTable[11] & 0x10) { + textId = 0x3055; + } else { + textId = 0x3054; + } + this->actor.textId = textId; + } else { + this->actor.textId = 0x3053; + } + player->actor.textId = this->actor.textId; + } else { + this->actor.textId = 0x3053; + player->actor.textId = this->actor.textId; + } + } +} + +void func_80A45288(EnGo2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 linkAge; + + if (this->actionFunc != EnGo2_GoronFireGenericAction) { + this->unk_194.unk_18 = player->actor.world.pos; + linkAge = gSaveContext.linkAge; + this->unk_194.unk_14 = D_80A482D8[this->actor.params & 0x1F][linkAge]; + func_80034A14(&this->actor, &this->unk_194, 4, this->unk_26E); + } + if ((this->actionFunc != EnGo2_SetGetItem) && (this->isAwake == true)) { + if (func_80A44790(this, globalCtx)) { + EnGo2_BiggoronSetTextId(this, globalCtx, player); + } + } +} + +void func_80A45360(EnGo2* this, f32* alpha) { + f32 alphaTarget = + (this->skelAnime.animation == &gGoronAnim_004930) && (this->skelAnime.curFrame <= 32.0f) ? 0.0f : 255.0f; + + Math_ApproachF(alpha, alphaTarget, 0.4f, 100.0f); + this->actor.shape.shadowAlpha = (u8)(u32)*alpha; +} + +void EnGo2_RollForward(EnGo2* this) { + f32 speedXZ = this->actor.speedXZ; + + if (this->unk_194.unk_00 != 0) { + this->actor.speedXZ = 0.0f; + } + + if (this->actionFunc != EnGo2_ContinueRolling) { + Actor_MoveForward(&this->actor); + } + + this->actor.speedXZ = speedXZ; +} + +void func_80A454CC(EnGo2* this) { + switch (this->actor.params & 0x1F) { + case GORON_CITY_ROLLING_BIG: + case GORON_DMT_DC_ENTRANCE: + case GORON_CITY_ENTRANCE: + case GORON_CITY_STAIRWELL: + case GORON_DMT_FAIRY_HINT: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_9); + break; + case GORON_DMT_BIGGORON: + if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_SWORD_BROKEN && INV_CONTENT(ITEM_TRADE_ADULT) <= ITEM_EYEDROPS) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_4); + break; + } + default: + this->skelAnime.playSpeed = 0.0f; + break; + } +} + +f32 EnGo2_GetTargetXZSpeed(EnGo2* this) { + f32 yDist = (this->actor.params & 0x1F) == GORON_DMT_BIGGORON ? 400.0f : 60.0f; + s32 index = this->actor.params & 0x1F; + + if (index == GORON_CITY_LINK && (fabsf(this->actor.yDistToPlayer) < yDist) && + (this->actor.xzDistToPlayer < 400.0f)) { + return 9.0f; + } else { + return index == GORON_CITY_ROLLING_BIG ? 3.6000001f : 6.0f; + } +} + +s32 EnGo2_IsCameraModified(EnGo2* this, GlobalContext* globalCtx) { + Camera* camera = globalCtx->cameraPtrs[MAIN_CAM]; + + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + if (EnGo2_IsWakingUp(this)) { + Camera_ChangeSetting(camera, CAM_SET_DIRECTED_YAW); + func_8005AD1C(camera, 4); + } else if (!EnGo2_IsWakingUp(this) && (camera->setting == CAM_SET_DIRECTED_YAW)) { + Camera_ChangeSetting(camera, CAM_SET_DUNGEON1); + func_8005ACFC(camera, 4); + } + } + + if ((this->actor.params & 0x1F) == GORON_FIRE_GENERIC || (this->actor.params & 0x1F) == GORON_CITY_ROLLING_BIG || + (this->actor.params & 0x1F) == GORON_CITY_STAIRWELL || (this->actor.params & 0x1F) == GORON_DMT_BIGGORON || + (this->actor.params & 0x1F) == GORON_MARKET_BAZAAR) { + return true; + } else if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1)) { + return true; + } else { + return false; + } +} + +void EnGo2_DefaultWakingUp(EnGo2* this) { + if (EnGo2_IsWakingUp(this)) { + this->unk_26E = 2; + } else { + this->unk_26E = 1; + } + + if (this->unk_194.unk_00 != 0) { + this->unk_26E = 4; + } + + this->isAwake = true; +} + +void EnGo2_WakingUp(EnGo2* this) { + f32 xyzDist = (this->actor.params & 0x1F) == GORON_DMT_BIGGORON ? 800.0f : 200.0f; + s32 isTrue = true; + + xyzDist = SQ(xyzDist); + this->unk_26E = 1; + if ((this->actor.xyzDistToPlayerSq <= xyzDist) || (this->unk_194.unk_00 != 0)) { + this->unk_26E = 4; + } + + this->isAwake = isTrue; +} + +void EnGo2_BiggoronWakingUp(EnGo2* this) { + if (EnGo2_IsWakingUp(this) || this->unk_194.unk_00 != 0) { + this->unk_26E = 2; + this->isAwake = true; + } else { + this->unk_26E = 1; + this->isAwake = false; + } +} + +void EnGo2_SelectGoronWakingUp(EnGo2* this) { + switch (this->actor.params & 0x1F) { + case GORON_DMT_BOMB_FLOWER: + this->isAwake = true; + this->unk_26E = EnGo2_IsWakingUp(this) ? 2 : 1; + break; + case GORON_FIRE_GENERIC: + EnGo2_WakingUp(this); + break; + case GORON_DMT_BIGGORON: + EnGo2_BiggoronWakingUp(this); + break; + case GORON_CITY_LINK: + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1)) { + EnGo2_WakingUp(this); + break; + } + default: + EnGo2_DefaultWakingUp(this); + } +} + +void EnGo2_EyeMouthTexState(EnGo2* this) { + switch (this->eyeMouthTexState) { + case 1: + this->blinkTimer = 0; + this->eyeTexIndex = 0; + this->mouthTexIndex = 0; + break; + case 2: + this->blinkTimer = 0; + this->eyeTexIndex = 1; + this->mouthTexIndex = 0; + break; + // case 3 only when biggoron is given eyedrops. Biggoron smiles. (only use of second mouth texture) + case 3: + this->blinkTimer = 0; + this->eyeTexIndex = 0; + this->mouthTexIndex = 1; + break; + default: + if (DECR(this->blinkTimer) == 0) { + this->eyeTexIndex++; + if (this->eyeTexIndex >= 4) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeTexIndex = 1; + } + } + } +} + +void EnGo2_SitDownAnimation(EnGo2* this) { + if ((this->skelAnime.playSpeed != 0.0f) && (this->skelAnime.animation == &gGoronAnim_004930)) { + if (this->skelAnime.playSpeed > 0.0f && this->skelAnime.curFrame == 14.0f) { + if ((this->actor.params & 0x1F) != GORON_DMT_BIGGORON) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOLON_SIT_DOWN); + } else { + func_800F4524(&D_801333D4, NA_SE_EN_GOLON_SIT_DOWN, 60); + } + } + if (this->skelAnime.playSpeed < 0.0f) { + if (this->skelAnime.curFrame == 1.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + if (this->skelAnime.curFrame == 40.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOLON_SIT_DOWN); + } + } + } +} + +void EnGo2_GetDustData(EnGo2* this, s32 index2) { + s32 index1 = (this->actor.params & 0x1F) == GORON_CITY_ROLLING_BIG ? 1 : 0; + EnGo2DustEffectData* dustEffectData = &sDustEffectData[index1][index2]; + + EnGo2_SpawnDust(this, dustEffectData->initialTimer, dustEffectData->scale, dustEffectData->scaleStep, + dustEffectData->numDustEffects, dustEffectData->radius, dustEffectData->yAccel); +} + +void EnGo2_RollingAnimation(EnGo2* this, GlobalContext* globalCtx) { + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + this->actor.flags &= ~ACTOR_FLAG_0; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_10); + this->skelAnime.playSpeed = -0.5f; + } else { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_1); + this->skelAnime.playSpeed = -1.0f; + } + EnGo2_SwapInitialFrameAnimFrameCount(this); + this->unk_26E = 1; + this->unk_211 = false; + this->isAwake = false; + this->actionFunc = EnGo2_CurledUp; +} + +void EnGo2_WakeUp(EnGo2* this, GlobalContext* globalCtx) { + if (this->skelAnime.playSpeed == 0.0f) { + if ((this->actor.params & 0x1F) != GORON_DMT_BIGGORON) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOLON_WAKE_UP); + } else { + func_800F4524(&D_801333D4, NA_SE_EN_GOLON_WAKE_UP, 60); + } + } + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + OnePointCutscene_Init(globalCtx, 4200, -99, &this->actor, MAIN_CAM); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_10); + this->skelAnime.playSpeed = 0.5f; + } else { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_1); + this->skelAnime.playSpeed = 1.0f; + } + this->actionFunc = func_80A46B40; +} + +void EnGo2_GetItemAnimation(EnGo2* this, GlobalContext* globalCtx) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_1); + this->unk_211 = true; + this->actionFunc = func_80A46B40; + this->skelAnime.playSpeed = 0.0f; + this->actor.speedXZ = 0.0f; + this->skelAnime.curFrame = this->skelAnime.endFrame; +} + +void EnGo2_SetupRolling(EnGo2* this, GlobalContext* globalCtx) { + if ((this->actor.params & 0x1F) == GORON_CITY_ROLLING_BIG || (this->actor.params & 0x1F) == GORON_CITY_LINK) { + this->collider.info.bumperFlags = 1; + this->actor.speedXZ = gSaveContext.infTable[17] & 0x4000 ? 6.0f : 3.6000001f; + } else { + this->actor.speedXZ = 6.0f; + } + this->actor.flags |= ACTOR_FLAG_24; + this->animTimer = 10; + this->actor.shape.yOffset = 1800.0f; + this->actor.speedXZ += this->actor.speedXZ; // Speeding up + this->actionFunc = EnGo2_ContinueRolling; +} + +void EnGo2_StopRolling(EnGo2* this, GlobalContext* globalCtx) { + EnBom* bomb; + + if (((this->actor.params & 0x1F) != GORON_CITY_ROLLING_BIG) && ((this->actor.params & 0x1F) != GORON_CITY_LINK)) { + if ((this->actor.params & 0x1F) == GORON_DMT_ROLLING_SMALL) { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (bomb != NULL) { + bomb->timer = 0; + } + } + } else { + this->collider.info.bumperFlags = 0; + } + + this->actor.shape.rot = this->actor.world.rot; + this->unk_59C = 0; + this->unk_590 = 0; + this->actionFunc = EnGo2_GroundRolling; + this->actor.shape.yOffset = 0.0f; + this->actor.speedXZ = 0.0f; +} + +s32 EnGo2_IsFreeingGoronInFire(EnGo2* this, GlobalContext* globalCtx) { + if ((this->actor.params & 0x1F) != GORON_FIRE_GENERIC) { + return false; + } + + // shaking curled up + this->actor.world.pos.x += (globalCtx->state.frames & 1) ? 1.0f : -1.0f; + if (Flags_GetSwitch(globalCtx, (this->actor.params & 0xFC00) >> 0xA)) { + return true; + } + return false; +} + +s32 EnGo2_IsGoronDmtBombFlower(EnGo2* this) { + if ((this->actor.params & 0x1F) != GORON_DMT_BOMB_FLOWER || this->unk_194.unk_00 != 2) { + return false; + } + + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_3); + this->unk_194.unk_00 = 0; + this->isAwake = false; + this->unk_26E = 1; + this->actionFunc = EnGo2_GoronDmtBombFlowerAnimation; + return true; +} + +s32 EnGo2_IsGoronRollingBig(EnGo2* this, GlobalContext* globalCtx) { + if ((this->actor.params & 0x1F) != GORON_CITY_ROLLING_BIG || (this->unk_194.unk_00 != 2)) { + return false; + } + this->unk_194.unk_00 = 0; + EnGo2_RollingAnimation(this, globalCtx); + this->actionFunc = EnGo2_GoronRollingBigContinueRolling; + return true; +} + +s32 EnGo2_IsGoronFireGeneric(EnGo2* this) { + if ((this->actor.params & 0x1F) != GORON_FIRE_GENERIC || this->unk_194.unk_00 == 0) { + return false; + } + this->actionFunc = EnGo2_GoronFireGenericAction; + return true; +} + +s32 EnGo2_IsGoronLinkReversing(EnGo2* this) { + if ((this->actor.params & 0x1F) != GORON_CITY_LINK || (this->waypoint >= this->unk_216) || + !EnGo2_IsWakingUp(this)) { + return false; + } + return true; +} + +s32 EnGo2_IsRolling(EnGo2* this) { + if (this->unk_194.unk_00 == 0 || this->actor.speedXZ < 1.0f) { + return false; + } + if (EnGo2_IsRollingOnGround(this, 2, 20.0 / 3.0f, 0)) { + if ((this->unk_590 >= 9) && (this->unk_59C == 0)) { + this->unk_590 = 8; + } + EnGo2_GetDustData(this, 0); + } + return true; +} + +void EnGo2_GoronLinkAnimation(EnGo2* this, GlobalContext* globalCtx) { + s32 animation = ARRAY_COUNT(sAnimationInfo); + + if ((this->actor.params & 0x1F) == GORON_CITY_LINK) { + if ((this->actor.textId == 0x3035 && this->unk_20C == 0) || + (this->actor.textId == 0x3036 && this->unk_20C == 0)) { + if (this->skelAnime.animation != &gGoronAnim_000D5C) { + animation = ENGO2_ANIM_12; + this->eyeMouthTexState = 0; + } + } + + if ((this->actor.textId == 0x3032 && this->unk_20C == 12) || (this->actor.textId == 0x3033) || + (this->actor.textId == 0x3035 && this->unk_20C == 6)) { + if (this->skelAnime.animation != &gGoronAnim_000750) { + animation = ENGO2_ANIM_11; + this->eyeMouthTexState = 1; + } + } + + if (this->skelAnime.animation == &gGoronAnim_000750) { + if (this->skelAnime.curFrame == 20.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOLON_CRY); + } + } + + if (animation != ARRAY_COUNT(sAnimationInfo)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animation); + } + } +} + +void EnGo2_GoronFireCamera(EnGo2* this, GlobalContext* globalCtx) { + s16 yaw; + + this->camId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->camId, CAM_STAT_ACTIVE); + Path_CopyLastPoint(this->path, &this->at); + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->at) + 0xE38; + this->eye.x = Math_SinS(yaw) * 100.0f + this->actor.world.pos.x; + this->eye.z = Math_CosS(yaw) * 100.0f + this->actor.world.pos.z; + this->eye.y = this->actor.world.pos.y + 20.0f; + this->at.x = this->actor.world.pos.x; + this->at.y = this->actor.world.pos.y + 40.0f; + this->at.z = this->actor.world.pos.z; + Gameplay_CameraSetAtEye(globalCtx, this->camId, &this->at, &this->eye); +} + +void EnGo2_GoronFireClearCamera(EnGo2* this, GlobalContext* globalCtx) { + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, this->camId); +} + +void EnGo2_BiggoronAnimation(EnGo2* this) { + if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_SWORD_BROKEN && INV_CONTENT(ITEM_TRADE_ADULT) <= ITEM_EYEDROPS && + (this->actor.params & 0x1F) == GORON_DMT_BIGGORON && this->unk_194.unk_00 == 0) { + if (DECR(this->animTimer) == 0) { + this->animTimer = Rand_S16Offset(30, 30); + func_800F4524(&D_801333D4, NA_SE_EN_GOLON_EYE_BIG, 60); + } + } +} + +void EnGo2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGo2* this = (EnGo2*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 28.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGoronSkel, NULL, this->jointTable, this->morphTable, 18); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + + // Not GORON_CITY_ROLLING_BIG, GORON_CITY_LINK, GORON_DMT_BIGGORON + switch (this->actor.params & 0x1F) { + case GORON_FIRE_GENERIC: + case GORON_DMT_BOMB_FLOWER: + case GORON_DMT_ROLLING_SMALL: + case GORON_DMT_DC_ENTRANCE: + case GORON_CITY_ENTRANCE: + case GORON_CITY_ISLAND: + case GORON_CITY_LOWEST_FLOOR: + case GORON_CITY_STAIRWELL: + case GORON_CITY_LOST_WOODS: + case GORON_DMT_FAIRY_HINT: + case GORON_MARKET_BAZAAR: + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.flags &= ~ACTOR_FLAG_5; + } + + EnGo2_SetColliderDim(this); + EnGo2_SetShape(this); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_0); + this->actor.gravity = -1.0f; + this->alpha = this->actor.shape.shadowAlpha = 0; + this->reverse = 0; + this->isAwake = false; + this->unk_211 = false; + this->goronState = 0; + this->waypoint = 0; + this->unk_216 = this->actor.shape.rot.z; + this->unk_26E = 1; + this->path = Path_GetByIndex(globalCtx, (this->actor.params & 0x3E0) >> 5, 0x1F); + switch (this->actor.params & 0x1F) { + case GORON_CITY_ENTRANCE: + case GORON_CITY_ISLAND: + case GORON_CITY_LOWEST_FLOOR: + case GORON_CITY_STAIRWELL: + case GORON_CITY_LOST_WOODS: + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && LINK_IS_ADULT) { + Actor_Kill(&this->actor); + } + this->actionFunc = EnGo2_CurledUp; + break; + case GORON_MARKET_BAZAAR: + if ((LINK_IS_ADULT) || !CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + Actor_Kill(&this->actor); + } + EnGo2_GetItemAnimation(this, globalCtx); + break; + case GORON_CITY_LINK: + if ((gSaveContext.infTable[16] & 0x200)) { + Path_CopyLastPoint(this->path, &this->actor.world.pos); + this->actor.home.pos = this->actor.world.pos; + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && CHECK_OWNED_EQUIP(EQUIP_TUNIC, 1)) { + EnGo2_GetItemAnimation(this, globalCtx); + } else { + this->actionFunc = EnGo2_CurledUp; + } + } else { + gSaveContext.infTable[16] &= ~0x1000; + this->collider.dim.height = (D_80A4816C[this->actor.params & 0x1F].height * 0.6f); + EnGo2_SetupRolling(this, globalCtx); + this->isAwake = true; + } + break; + case GORON_CITY_ROLLING_BIG: + case GORON_DMT_ROLLING_SMALL: + this->collider.dim.height = (D_80A4816C[this->actor.params & 0x1F].height * 0.6f); + EnGo2_SetupRolling(this, globalCtx); + break; + case GORON_FIRE_GENERIC: + if (Flags_GetSwitch(globalCtx, (this->actor.params & 0xFC00) >> 0xA)) { + Actor_Kill(&this->actor); + } else { + this->isAwake = true; + this->actionFunc = EnGo2_CurledUp; + } + break; + case GORON_DMT_BIGGORON: + this->actor.shape.shadowDraw = NULL; + this->actor.flags &= ~ACTOR_FLAG_0; + if ((INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_SWORD_BROKEN) && + (INV_CONTENT(ITEM_TRADE_ADULT) <= ITEM_EYEDROPS)) { + this->eyeMouthTexState = 1; + } + this->collider.base.acFlags = 0; + this->collider.base.ocFlags1 = 0xD; // OC_PLAYER | OC_NO_PUSH | OC_ON + this->actionFunc = EnGo2_CurledUp; + break; + case GORON_DMT_BOMB_FLOWER: + if (gSaveContext.infTable[14] & 0x800) { + Path_CopyLastPoint(this->path, &this->actor.world.pos); + this->actor.home.pos = this->actor.world.pos; + } + case GORON_DMT_DC_ENTRANCE: + case GORON_DMT_FAIRY_HINT: + default: + this->actionFunc = EnGo2_CurledUp; + } +} + +void EnGo2_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnGo2_CurledUp(EnGo2* this, GlobalContext* globalCtx) { + u8 index = this->actor.params & 0x1F; + s16 height; + s32 quake; + + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + quake = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quake, -0x3CB0); + Quake_SetQuakeValues(quake, 8, 0, 0, 0); + Quake_SetCountdown(quake, 16); + } else { + EnGo2_GetDustData(this, 1); + } + this->skelAnime.playSpeed = 0.0f; + } + + if ((s32)this->skelAnime.curFrame == 0) { + this->collider.dim.height = (D_80A4816C[index].height * 0.6f); + } else { + height = D_80A4816C[index].height; + this->collider.dim.height = + ((D_80A4816C[index].height * 0.4f * (this->skelAnime.curFrame / this->skelAnime.startFrame)) + + (height * 0.6f)); + } + if (EnGo2_IsFreeingGoronInFire(this, globalCtx)) { + this->isAwake = false; + EnGo2_WakeUp(this, globalCtx); + } + if (((this->actor.params & 0x1F) != GORON_FIRE_GENERIC) && EnGo2_IsWakingUp(this)) { + EnGo2_WakeUp(this, globalCtx); + } +} + +void func_80A46B40(EnGo2* this, GlobalContext* globalCtx) { + u8 index = (this->actor.params & 0x1F); + f32 height; + + if (this->unk_211 == true) { + EnGo2_BiggoronAnimation(this); + EnGo2_GoronLinkAnimation(this, globalCtx); + EnGo2_SelectGoronWakingUp(this); + + if (!EnGo2_IsGoronRollingBig(this, globalCtx) && !EnGo2_IsGoronFireGeneric(this)) { + if (EnGo2_IsGoronDmtBombFlower(this)) { + return; + } + } else { + return; + } + } else { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { + this->actor.flags |= ACTOR_FLAG_0; + } + func_80A454CC(this); + this->unk_211 = true; + this->collider.dim.height = D_80A4816C[index].height; + } else { + height = D_80A4816C[index].height; + this->collider.dim.height = + (s16)((height * 0.4f * (this->skelAnime.curFrame / this->skelAnime.endFrame)) + (height * 0.6f)); + } + } + if ((!EnGo2_IsCameraModified(this, globalCtx)) && (!EnGo2_IsWakingUp(this))) { + EnGo2_RollingAnimation(this, globalCtx); + } +} + +void EnGo2_GoronDmtBombFlowerAnimation(EnGo2* this, GlobalContext* globalCtx) { + f32 float1 = this->skelAnime.endFrame; + f32 float2 = this->skelAnime.curFrame * ((f32)0x8000 / float1); + + this->actor.speedXZ = Math_SinS(float2); + if ((EnGo2_Orient(this, globalCtx)) && (this->waypoint == 0)) { + EnGo2_GetItemAnimation(this, globalCtx); + } +} + +void EnGo2_GoronRollingBigContinueRolling(EnGo2* this, GlobalContext* globalCtx) { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + EnGo2_GetDustData(this, 1); + this->skelAnime.playSpeed = 0.0f; + EnGo2_SetupRolling(this, globalCtx); + } +} + +void EnGo2_ContinueRolling(EnGo2* this, GlobalContext* globalCtx) { + f32 float1 = 1000.0f; + + if (((this->actor.params & 0x1F) != GORON_DMT_ROLLING_SMALL || !(this->actor.xyzDistToPlayerSq > SQ(float1))) && + DECR(this->animTimer) == 0) { + this->actionFunc = EnGo2_SlowRolling; + this->actor.speedXZ *= 0.5f; // slowdown + } + EnGo2_GetDustData(this, 2); +} + +void EnGo2_SlowRolling(EnGo2* this, GlobalContext* globalCtx) { + s32 orientation; + s32 index; + + if (!EnGo2_IsRolling(this)) { + if (EnGo2_IsRollingOnGround(this, 4, 8.0f, 1) == true) { + if (EnGo2_IsGoronLinkReversing(this)) { + this->actionFunc = EnGo2_ReverseRolling; + return; + } + EnGo2_GetDustData(this, 3); + } + orientation = EnGo2_Orient(this, globalCtx); + index = this->actor.params & 0x1F; + if (index != GORON_CITY_LINK) { + if ((index == GORON_DMT_ROLLING_SMALL) && (orientation == 1) && (this->waypoint == 0)) { + EnGo2_StopRolling(this, globalCtx); + return; + } + } else if ((orientation == 2) && (this->waypoint == 1)) { + EnGo2_StopRolling(this, globalCtx); + return; + } + Math_ApproachF(&this->actor.speedXZ, EnGo2_GetTargetXZSpeed(this), 0.4f, 0.6f); + this->actor.shape.rot = this->actor.world.rot; + } +} + +void EnGo2_GroundRolling(EnGo2* this, GlobalContext* globalCtx) { + if (EnGo2_IsRollingOnGround(this, 4, 8.0f, 0)) { + EnGo2_GetDustData(this, 0); + if (this->unk_59C == 0) { + switch (this->actor.params & 0x1F) { + case GORON_CITY_LINK: + this->goronState = 0; + this->actionFunc = EnGo2_GoronLinkStopRolling; + break; + case GORON_CITY_ROLLING_BIG: + EnGo2_WakeUp(this, globalCtx); + break; + default: + this->actionFunc = EnGo2_CurledUp; + } + } + } +} + +void EnGo2_ReverseRolling(EnGo2* this, GlobalContext* globalCtx) { + if (!EnGo2_IsRolling(this)) { + Math_ApproachF(&this->actor.speedXZ, 0.0f, 0.6f, 0.8f); + if (this->actor.speedXZ >= 1.0f) { + EnGo2_GetDustData(this, 3); + } + if ((s32)this->actor.speedXZ == 0) { + this->actor.world.rot.y ^= 0x8000; + this->actor.shape.rot.y = this->actor.world.rot.y; + this->reverse ^= 1; + EnGo2_UpdateWaypoint(this, globalCtx); + EnGo2_SetupRolling(this, globalCtx); + } + } +} + +void EnGo2_SetupGetItem(EnGo2* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnGo2_SetGetItem; + } else { + func_8002F434(&this->actor, globalCtx, this->getItemId, this->actor.xzDistToPlayer + 1.0f, + fabsf(this->actor.yDistToPlayer) + 1.0f); + } +} + +void EnGo2_SetGetItem(EnGo2* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->unk_194.unk_00 = 0; + switch (this->getItemId) { + case GI_CLAIM_CHECK: + Environment_ClearBgsDayCount(); + EnGo2_GetItemAnimation(this, globalCtx); + return; + case GI_TUNIC_GORON: + gSaveContext.infTable[16] |= 0x200; + EnGo2_GetItemAnimation(this, globalCtx); + return; + case GI_SWORD_BGS: + gSaveContext.bgsFlag = true; + break; + case GI_BOMB_BAG_30: + case GI_BOMB_BAG_40: + EnGo2_RollingAnimation(this, globalCtx); + this->actionFunc = EnGo2_GoronRollingBigContinueRolling; + return; + } + this->actionFunc = func_80A46B40; + } +} + +void EnGo2_BiggoronEyedrops(EnGo2* this, GlobalContext* globalCtx) { + switch (this->goronState) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_5); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.shape.rot.y += 0x5B0; + this->unk_26E = 1; + this->animTimer = this->skelAnime.endFrame + 60.0f + 60.0f; // eyeDrops animation timer + this->eyeMouthTexState = 2; + this->unk_20C = 0; + this->goronState++; + func_800F483C(0x28, 5); + OnePointCutscene_Init(globalCtx, 4190, -99, &this->actor, MAIN_CAM); + break; + case 1: + if (DECR(this->animTimer)) { + if (this->animTimer == 60 || this->animTimer == 120) { + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + func_800F4524(&D_801333D4, NA_SE_EV_GORON_WATER_DROP, 60); + } + } else { + func_800F4524(&D_801333D4, NA_SE_EN_GOLON_GOOD_BIG, 60); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_6); + Message_ContinueTextbox(globalCtx, 0x305A); + this->eyeMouthTexState = 3; + this->goronState++; + func_800F483C(0x7F, 5); + } + break; + case 2: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + this->eyeMouthTexState = 0; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_1); + this->actor.flags |= ACTOR_FLAG_0; + this->unk_26E = 2; + this->skelAnime.playSpeed = 0.0f; + this->skelAnime.curFrame = this->skelAnime.endFrame; + EnGo2_GetItem(this, globalCtx, GI_CLAIM_CHECK); + this->actionFunc = EnGo2_SetupGetItem; + this->goronState = 0; + } + break; + } +} + +void EnGo2_GoronLinkStopRolling(EnGo2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + switch (this->goronState) { + case 0: + if (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE) { + return; + } else { + Message_StartTextbox(globalCtx, 0x3031, NULL); + player->actor.freezeTimer = 10; + this->goronState++; + } + case 1: + break; + default: + return; + } + + if (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_CLOSING) { + player->actor.freezeTimer = 10; + } else { + gSaveContext.infTable[16] |= 0x1000; + this->unk_26E = 1; + this->unk_211 = false; + this->isAwake = false; + this->actionFunc = EnGo2_CurledUp; + } +} + +void EnGo2_GoronFireGenericAction(EnGo2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3s D_80A4854C = { 0x00, 0x00, 0x00 }; + + switch (this->goronState) { + case 0: // Wake up + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + EnGo2_GoronFireCamera(this, globalCtx); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENGO2_ANIM_2); + this->waypoint = 1; + this->skelAnime.playSpeed = 2.0f; + func_80A44D84(this); + this->actor.shape.rot = this->actor.world.rot; + this->animTimer = 60; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 2.0f; + this->unk_194.unk_08 = D_80A4854C; + this->unk_194.unk_0E = D_80A4854C; + this->goronState++; + this->goronState++; + player->actor.world.rot.y = this->actor.world.rot.y; + player->actor.shape.rot.y = this->actor.world.rot.y; + player->actor.world.pos.x = + (f32)((Math_SinS(this->actor.world.rot.y) * -30.0f) + this->actor.world.pos.x); + player->actor.world.pos.z = + (f32)((Math_CosS(this->actor.world.rot.y) * -30.0f) + this->actor.world.pos.z); + func_8002DF54(globalCtx, &this->actor, 8); + Audio_PlayFanfare(NA_BGM_APPEAR); + } + break; + case 2: // Walking away + if (DECR(this->animTimer)) { + if (!(this->animTimer % 8)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + Actor_MoveForward(&this->actor); + } else { + this->animTimer = 0; + this->actor.speedXZ = 0.0f; + if ((((this->actor.params & 0xFC00) >> 0xA) != 1) && (((this->actor.params & 0xFC00) >> 0xA) != 2) && + (((this->actor.params & 0xFC00) >> 0xA) != 4) && (((this->actor.params & 0xFC00) >> 0xA) != 5) && + (((this->actor.params & 0xFC00) >> 0xA) != 9) && (((this->actor.params & 0xFC00) >> 0xA) != 11)) { + this->goronState++; + } + this->goronState++; + } + break; + case 3: // Walking away + this->animTimer++; + if (!(this->animTimer % 8) && (this->animTimer < 10)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + if (this->animTimer == 10) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_IRON_DOOR_OPEN); + } + if (this->animTimer > 44) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_IRON_DOOR_CLOSE); + } else { + break; + } + case 4: // Finalize walking away + Message_CloseTextbox(globalCtx); + EnGo2_GoronFireClearCamera(this, globalCtx); + func_8002DF54(globalCtx, &this->actor, 7); + Actor_Kill(&this->actor); + break; + case 1: + break; + } +} + +void EnGo2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGo2* this = (EnGo2*)thisx; + + func_80A45360(this, &this->alpha); + EnGo2_SitDownAnimation(this); + SkelAnime_Update(&this->skelAnime); + EnGo2_RollForward(this); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, (f32)this->collider.dim.height * 0.5f, + (f32)this->collider.dim.radius * 0.6f, 0.0f, 5); + if (this->unk_194.unk_00 == 0) { + func_80A44AB0(this, globalCtx); + } + this->actionFunc(this, globalCtx); + if (this->unk_211 == true) { + func_80034F54(globalCtx, this->unk_226, this->unk_24A, 18); + } + func_80A45288(this, globalCtx); + EnGo2_EyeMouthTexState(this); + EnGo2_CheckCollision(this, globalCtx); +} + +s32 EnGo2_DrawCurledUp(EnGo2* this, GlobalContext* globalCtx) { + Vec3f D_80A48554 = { 0.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go2.c", 2881); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_go2.c", 2884), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGoronDL_00BD80); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go2.c", 2889); + Matrix_MultVec3f(&D_80A48554, &this->actor.focus.pos); + + return 1; +} + +s32 EnGo2_DrawRolling(EnGo2* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f D_80A48560 = { 0.0f, 0.0f, 0.0f }; + f32 speedXZ; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go2.c", 2914); + func_80093D18(globalCtx->state.gfxCtx); + speedXZ = this->actionFunc == EnGo2_ReverseRolling ? 0.0f : this->actor.speedXZ; + Matrix_RotateZYX((globalCtx->state.frames * ((s16)speedXZ * 1400)), 0, this->actor.shape.rot.z, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_go2.c", 2926), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGoronDL_00C140); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go2.c", 2930); + Matrix_MultVec3f(&D_80A48560, &this->actor.focus.pos); + return 1; +} + +s32 EnGo2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limb, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnGo2* this = (EnGo2*)thisx; + Vec3s vec1; + f32 float1; + + if (limb == 17) { + Matrix_Translate(2800.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec1 = this->unk_194.unk_08; + float1 = (vec1.y / (f32)0x8000) * M_PI; + Matrix_RotateX(float1, MTXMODE_APPLY); + float1 = (vec1.x / (f32)0x8000) * M_PI; + Matrix_RotateZ(float1, MTXMODE_APPLY); + Matrix_Translate(-2800.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limb == 10) { + vec1 = this->unk_194.unk_0E; + float1 = (vec1.y / (f32)0x8000) * M_PI; + Matrix_RotateY(float1, MTXMODE_APPLY); + float1 = (vec1.x / (f32)0x8000) * M_PI; + Matrix_RotateX(float1, MTXMODE_APPLY); + } + if ((limb == 10) || (limb == 11) || (limb == 14)) { + float1 = Math_SinS(this->unk_226[limb]); + rot->y += float1 * 200.0f; + float1 = Math_CosS(this->unk_24A[limb]); + rot->z += float1 * 200.0f; + } + return 0; +} + +void EnGo2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnGo2* this = (EnGo2*)thisx; + Vec3f D_80A4856C = { 600.0f, 0.0f, 0.0f }; + + if (limbIndex == 17) { + Matrix_MultVec3f(&D_80A4856C, &this->actor.focus.pos); + } +} + +void EnGo2_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGo2* this = (EnGo2*)thisx; + void* eyeTextures[] = { gGoronCsEyeClosed2Tex, gGoronCsEyeOpenTex, gGoronCsEyeHalfTex, gGoronCsEyeClosedTex }; + void* mouthTextures[] = { gGoronCsMouthNeutralTex, gGoronCsMouthSmileTex }; + + EnGo2_UpdateDust(this); + Matrix_Push(); + EnGo2_DrawDust(this, globalCtx); + Matrix_Pop(); + + if ((this->actionFunc == EnGo2_CurledUp) && (this->skelAnime.playSpeed == 0.0f) && + (this->skelAnime.curFrame == 0.0f)) { + if (1) {} + EnGo2_DrawCurledUp(this, globalCtx); + } else if (this->actionFunc == EnGo2_SlowRolling || this->actionFunc == EnGo2_ReverseRolling || + this->actionFunc == EnGo2_ContinueRolling) { + EnGo2_DrawRolling(this, globalCtx); + } else { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_go2.c", 3063); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTextures[this->mouthTexIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnGo2_OverrideLimbDraw, EnGo2_PostLimbDraw, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_go2.c", 3081); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.h b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.h new file mode 100644 index 000000000..d31eb7678 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.h @@ -0,0 +1,109 @@ +#ifndef Z_EN_GO2_H +#define Z_EN_GO2_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Go/z_en_go.h" + +struct EnGo2; + +typedef void (*EnGo2ActionFunc)(struct EnGo2*, GlobalContext*); + +typedef enum { + /* 0x00 */ GORON_CITY_ROLLING_BIG, + /* 0x01 */ GORON_CITY_LINK, + /* 0x02 */ GORON_DMT_BIGGORON, + /* 0x03 */ GORON_FIRE_GENERIC, + /* 0x04 */ GORON_DMT_BOMB_FLOWER, + /* 0x05 */ GORON_DMT_ROLLING_SMALL, + /* 0x06 */ GORON_DMT_DC_ENTRANCE, + /* 0x07 */ GORON_CITY_ENTRANCE, + /* 0x08 */ GORON_CITY_ISLAND, + /* 0x09 */ GORON_CITY_LOWEST_FLOOR, + /* 0x0A */ GORON_CITY_STAIRWELL, + /* 0x0B */ GORON_CITY_LOST_WOODS, + /* 0x0C */ GORON_DMT_FAIRY_HINT, + /* 0x0D */ GORON_MARKET_BAZAAR +} GoronType; + +// WIP fire temple type docs +// /* 0x00 */ UNUSED +// /* 0x01 */ GORON_FIRE_LAVA_ROOM_OPEN +// /* 0x02 */ GORON_FIRE_LAVA_ROOM_BOMB +// /* 0x03 */ GORON_FIRE_MAZE_LOWER +// /* 0x04 */ GORON_FIRE_MAZE_SHORTCUT +// /* 0x05 */ GORON_FIRE_MAZE_SIDE_ROOM +// /* 0x06 */ GORON_FIRE_BOSS_KEY +// /* 0x07 */ GORON_FIRE_BOSS_KEY +// /* 0x08 */ GORON_FIRE_NEAR_BOSS +// /* 0x09 */ GORON_FIRE_BOSS_KEY +// /* 0x0A */ GORON_FIRE_MAZE_UPPER, +// /* 0x0B */ GORON_FIRE_HIGHEST + + +typedef struct { + s16 unused; + s16 yDist; + s16 xzDist; + s16 radius; + s16 height; +} EnGo2DataStruct1; // size = 0xA + +typedef struct { + f32 shape_unk_10; + f32 scale; + s8 actor_unk_1F; + f32 unk_218; +} EnGo2DataStruct2; // size = 0x10 + +typedef struct { + u8 initialTimer; + f32 scale; + f32 scaleStep; + s32 numDustEffects; + f32 radius; + f32 yAccel; +} EnGo2DustEffectData; // size = 0x18 + +typedef struct EnGo2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnGo2ActionFunc actionFunc; + /* 0x0194 */ struct_80034A14_arg1 unk_194; + /* 0x01BC */ ColliderCylinder collider; + /* 0x0208 */ Path* path; + /* 0x020C */ u8 unk_20C; // counter for GORON_CITY_LINK animation + /* 0x020D */ u8 dialogState; + /* 0x020E */ u8 reverse; + /* 0x020F */ u8 isAwake; // Conditional + /* 0x0210 */ s8 waypoint; + /* 0x0211 */ u8 unk_211; // Conditional + // goron link: 0 - rolling, 1 - frozen + // biggoron: 0 - give eyedrops, 1 - applying eyedrops, 2 - getting claimcheck + // generic fire: 0 - + /* 0x0212 */ u8 goronState; + /* 0x0213 */ u8 eyeMouthTexState; // 0, 1, 2, 3 + /* 0x0214 */ u8 eyeTexIndex; + /* 0x0215 */ u8 mouthTexIndex; + /* 0x0216 */ u8 unk_216; // Set to z rotation, checked by waypoint + /* 0x0218 */ f32 unk_218; + /* 0x021C */ char unk_21C[0x04]; + /* 0x0220 */ f32 alpha; // Set to 0, used by func_80A45360, smoothed to this->actor.shape.unk_14 from either 0 or 255.0f + /* 0x0224 */ s16 blinkTimer; + /* 0x0226 */ s16 unk_226[18]; // Remains unknown + /* 0x024A */ s16 unk_24A[18]; // Remains unknown + /* 0x026E */ u16 unk_26E; // Remains unknown = 1, 2, or 4: used in func_80034A14 + /* 0x0270 */ EnGoEffect dustEffects[10]; + /* 0x04A0 */ Vec3f eye; + /* 0x04AC */ Vec3f at; + /* 0x04B8 */ Vec3s jointTable[18]; + /* 0x0524 */ Vec3s morphTable[18]; + /* 0x0590 */ s16 unk_590; // timer + /* 0x0592 */ s16 animTimer; // animTimer. Plays NA_SE_EN_MORIBLIN_WALK, NA_SE_EV_IRON_DOOR_OPEN, NA_SE_EV_IRON_DOOR_CLOSE + /* 0x0594 */ s32 getItemId; + /* 0x0598 */ char unk_598[0x02]; + /* 0x059A */ s16 camId; + /* 0x059C */ s16 unk_59C; +} EnGo2; // size = 0x05A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c new file mode 100644 index 000000000..85049b7f1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.c @@ -0,0 +1,910 @@ +#include "z_en_goma.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "objects/object_gol/object_gol.h" +#include "overlays/actors/ovl_Boss_Goma/z_boss_goma.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnGoma_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGoma_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGoma_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGoma_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnGoma_Reset(void); + +void EnGoma_Flee(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_EggFallToGround(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Egg(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Hatch(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Hurt(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Die(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Dead(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_PrepareJump(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Land(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Jump(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Stand(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_ChasePlayer(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Stunned(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_LookAtPlayer(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_UpdateHit(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_Debris(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_SpawnHatchDebris(EnGoma* this, GlobalContext* globalCtx2); +void EnGoma_BossLimb(EnGoma* this, GlobalContext* globalCtx); + +void EnGoma_SetupFlee(EnGoma* this); +void EnGoma_SetupHatch(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_SetupHurt(EnGoma* this, GlobalContext* globalCtx); +void EnGoma_SetupDie(EnGoma* this); +void EnGoma_SetupDead(EnGoma* this); +void EnGoma_SetupStand(EnGoma* this); +void EnGoma_SetupChasePlayer(EnGoma* this); +void EnGoma_SetupPrepareJump(EnGoma* this); +void EnGoma_SetupLand(EnGoma* this); +void EnGoma_SetupJump(EnGoma* this); +void EnGoma_SetupStunned(EnGoma* this, GlobalContext* globalCtx); + +const ActorInit En_Goma_InitVars = { + ACTOR_BOSS_GOMA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_GOL, + sizeof(EnGoma), + (ActorFunc)EnGoma_Init, + (ActorFunc)EnGoma_Destroy, + (ActorFunc)EnGoma_Update, + (ActorFunc)EnGoma_Draw, + (ActorResetFunc)EnGoma_Reset, +}; + +static ColliderCylinderInit D_80A4B7A0 = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 15, 30, 10, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit D_80A4B7CC = { + { + COLTYPE_HIT3, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFDFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 15, 30, 10, { 0, 0, 0 } }, +}; + +static u8 sSpawnNum = 0; +static Vec3f sDeadEffectVel = { 0.0f, 0.0f, 0.0f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 3, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x03, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 20, ICHAIN_STOP), +}; + +void EnGoma_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGoma* this = (EnGoma*)thisx; + s16 params; + + this->eggTimer = Rand_ZeroOne() * 200.0f; + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.01f); + params = this->actor.params; + + if (params >= 100) { // piece of boss goma + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_BOSS); + this->actionFunc = EnGoma_BossLimb; + this->gomaType = ENGOMA_BOSSLIMB; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + this->actionTimer = this->actor.params + 150; + this->actor.flags &= ~ACTOR_FLAG_0; + } else if (params >= 10) { // Debris when hatching + this->actor.gravity = -1.3f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionTimer = 50; + this->gomaType = ENGOMA_HATCH_DEBRIS; + this->eggScale = 1.0f; + this->actor.velocity.y = Rand_ZeroOne() * 5.0f + 5.0f; + this->actionFunc = EnGoma_Debris; + this->actor.speedXZ = Rand_ZeroOne() * 2.3f + 1.5f; + this->actionTimer = 30; + this->actor.scale.x = Rand_ZeroOne() * 0.005f + 0.01f; + this->actor.scale.y = Rand_ZeroOne() * 0.005f + 0.01f; + this->actor.scale.z = Rand_ZeroOne() * 0.005f + 0.01f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + } else { // Egg + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 40.0f); + SkelAnime_Init(globalCtx, &this->skelanime, &gObjectGolSkel, &gObjectGolStandAnim, this->jointTable, + this->morphTable, GOMA_LIMB_MAX); + Animation_PlayLoop(&this->skelanime, &gObjectGolStandAnim); + this->actor.colChkInfo.health = 2; + + if (this->actor.params < 3) { // Spawned by boss + this->actionFunc = EnGoma_EggFallToGround; + this->invincibilityTimer = 10; + this->actor.speedXZ = 1.5f; + } else if (this->actor.params == 8 || this->actor.params == 6) { + this->actionFunc = EnGoma_Egg; + this->spawnNum = sSpawnNum++; + } else if (this->actor.params == 9 || this->actor.params == 7) { + this->actionFunc = EnGoma_Egg; + } + + if (this->actor.params >= 8) { // on ceiling + this->eggYOffset = -1500.0f; + } else { + this->eggYOffset = 1500.0f; + } + + this->gomaType = ENGOMA_EGG; + this->eggScale = 1.0f; + this->eggSquishAngle = Rand_ZeroOne() * 1000.0f; + this->actionTimer = 50; + Collider_InitCylinder(globalCtx, &this->colCyl1); + Collider_SetCylinder(globalCtx, &this->colCyl1, &this->actor, &D_80A4B7A0); + Collider_InitCylinder(globalCtx, &this->colCyl2); + Collider_SetCylinder(globalCtx, &this->colCyl2, &this->actor, &D_80A4B7CC); + } +} + +void EnGoma_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGoma* this = (EnGoma*)thisx; + + if (this->actor.params < 10) { + Collider_DestroyCylinder(globalCtx, &this->colCyl1); + Collider_DestroyCylinder(globalCtx, &this->colCyl2); + } +} + +void EnGoma_SetupFlee(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolRunningAnim, 2.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolRunningAnim), ANIMMODE_LOOP, -2.0f); + this->actionFunc = EnGoma_Flee; + this->actionTimer = 20; + + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_DAM2); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_DAM2); + } +} + +void EnGoma_Flee(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ApproachF(&this->actor.speedXZ, 20.0f / 3.0f, 0.5f, 2.0f); + Math_ApproachS(&this->actor.world.rot.y, + Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) + 0x8000, 3, 2000); + Math_ApproachS(&this->actor.shape.rot.y, this->actor.world.rot.y, 2, 3000); + + if (this->actionTimer == 0) { + EnGoma_SetupStand(this); + } +} + +void EnGoma_EggFallToGround(EnGoma* this, GlobalContext* globalCtx) { + this->actor.gravity = -1.3f; + this->eggSquishAccel += 0.03f; + this->eggSquishAngle += 1.0f + this->eggSquishAccel; + Math_ApproachZeroF(&this->eggSquishAmount, 1.0f, 0.005f); + Math_ApproachF(&this->eggYOffset, 1500.0f, 1.0f, 150.0f); + + switch (this->hatchState) { + case 0: + if (this->actor.bgCheckFlags & 1) { // floor + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_EGG1); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_EGG1); + } + + if (this->actor.params > 5) { + EnGoma_SetupHatch(this, globalCtx); + } else { + this->hatchState = 1; + this->actionTimer = 3; + Math_ApproachF(&this->eggScale, 1.5f, 0.5f, 1.0f); + } + } + break; + + case 1: + if (this->actionTimer == 0) { + this->hatchState = 2; + this->actionTimer = 3; + Math_ApproachF(&this->eggScale, 0.75f, 0.5f, 1.0f); + this->actor.velocity.y = 5.0f; + this->actor.speedXZ = 2.0f; + } else { + Math_ApproachF(&this->eggScale, 1.5f, 0.5f, 1.0f); + } + break; + + case 2: + if (this->actionTimer == 0) { + this->hatchState = 3; + this->actionTimer = 80; + } else { + Math_ApproachF(&this->eggScale, 0.75f, 0.5f, 1.0f); + } + break; + + case 3: + Math_ApproachF(&this->eggScale, 1.0f, 0.1f, 0.1f); + if (this->actionTimer == 0) { + EnGoma_SetupHatch(this, globalCtx); + } + break; + } + + if (this->actor.bgCheckFlags & 1) { + Math_ApproachZeroF(&this->actor.speedXZ, 0.2f, 0.05f); + } + this->eggPitch += (this->actor.speedXZ * 0.1f); + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnGoma_Egg(EnGoma* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 i; + + this->eggSquishAngle += 1.0f; + Math_ApproachF(&this->eggSquishAmount, 0.1f, 1.0f, 0.005f); + if (fabsf(this->actor.world.pos.x - player->actor.world.pos.x) < 100.0f && + fabsf(this->actor.world.pos.z - player->actor.world.pos.z) < 100.0f) { + if (++this->playerDetectionTimer > 9) { + this->actionFunc = EnGoma_EggFallToGround; + } + } else { + this->playerDetectionTimer = 0; + } + + if (!(this->eggTimer & 0xF) && Rand_ZeroOne() < 0.5f) { + for (i = 0; i < 2; i++) { + Vec3f vel = { 0.0f, 0.0f, 0.0f }; + Vec3f acc = { 0.0f, -0.5f, 0.0f }; + Vec3f pos; + + pos.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x; + pos.y = Rand_ZeroFloat(30.0f) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z; + EffectSsHahen_Spawn(globalCtx, &pos, &vel, &acc, 0, (s16)(Rand_ZeroOne() * 5.0f) + 10, HAHEN_OBJECT_DEFAULT, + 10, NULL); + } + } +} + +void EnGoma_SetupHatch(EnGoma* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelanime, &gObjectGolJumpHeadbuttAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolJumpHeadbuttAnim), ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnGoma_Hatch; + Actor_SetScale(&this->actor, 0.005f); + this->gomaType = ENGOMA_NORMAL; + this->actionTimer = 5; + this->actor.shape.rot.y = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnGoma_SpawnHatchDebris(this, globalCtx); + this->eggScale = 1.0f; + this->actor.speedXZ = 0.0f; +} + +void EnGoma_Hatch(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + if (this->actionTimer == 0) { + EnGoma_SetupStand(this); + } +} + +void EnGoma_SetupHurt(EnGoma* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelanime, &gObjectGolDamagedAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolDamagedAnim), ANIMMODE_ONCE, -2.0f); + this->actionFunc = EnGoma_Hurt; + + if ((s8)this->actor.colChkInfo.health <= 0) { + this->actionTimer = 5; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + this->actionTimer = 10; + } + + this->actor.speedXZ = 20.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_DAM1); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_DAM1); + } +} + +void EnGoma_Hurt(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->actor.bgCheckFlags & 1) { + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 2.0f); + } + + if (this->actionTimer == 0) { + if ((s8)this->actor.colChkInfo.health <= 0) { + EnGoma_SetupDie(this); + } else { + EnGoma_SetupFlee(this); + } + } +} + +void EnGoma_SetupDie(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolDeathAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gObjectGolDeathAnim), + ANIMMODE_ONCE, -2.0f); + this->actionFunc = EnGoma_Die; + this->actionTimer = 30; + + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_DEAD); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_DEAD); + } + + this->invincibilityTimer = 100; + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void EnGoma_Die(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->actor.bgCheckFlags & 1) { + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 2.0f); + } + + if (this->actionTimer == 17) { + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_LAND); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_LAND); + } + } + + if (this->actionTimer == 0) { + EnGoma_SetupDead(this); + } +} + +void EnGoma_SetupDead(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolDeadTwitchingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolDeadTwitchingAnim), ANIMMODE_LOOP, -2.0f); + this->actionFunc = EnGoma_Dead; + this->actionTimer = 3; +} + +void EnGoma_Dead(EnGoma* this, GlobalContext* globalCtx) { + Vec3f accel; + Vec3f pos; + + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 2.0f); + + if (this->actionTimer == 2) { + pos.x = this->actor.world.pos.x; + pos.y = (this->actor.world.pos.y + 5.0f) - 10.0f; + pos.z = this->actor.world.pos.z; + accel = sDeadEffectVel; + accel.y = 0.03f; + EffectSsKFire_Spawn(globalCtx, &pos, &sDeadEffectVel, &accel, 40, 0); + } + + if (this->actionTimer == 0 && Math_SmoothStepToF(&this->actor.scale.y, 0.0f, 0.5f, 0.00225f, 0.00001f) <= 0.001f) { + if (this->actor.params < 6) { + BossGoma* parent = (BossGoma*)this->actor.parent; + + parent->childrenGohmaState[this->actor.params] = -1; + } + Audio_PlaySoundGeneral(NA_SE_EN_EXTINCT, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Actor_Kill(&this->actor); + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0x30); + } + this->visualState = 2; +} + +void EnGoma_SetupStand(EnGoma* this) { + f32 lastFrame; + + lastFrame = Animation_GetLastFrame(&gObjectGolStandAnim); + this->actionTimer = Rand_S16Offset(10, 30); + Animation_Change(&this->skelanime, &gObjectGolStandAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP, -5.0f); + this->actionFunc = EnGoma_Stand; + this->gomaType = ENGOMA_NORMAL; +} + +void EnGoma_SetupChasePlayer(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolRunningAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolRunningAnim), ANIMMODE_LOOP, -5.0f); + this->actionFunc = EnGoma_ChasePlayer; + this->actionTimer = Rand_S16Offset(70, 110); +} + +void EnGoma_SetupPrepareJump(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolPrepareJumpAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolPrepareJumpAnim), ANIMMODE_ONCE, -5.0f); + this->actionFunc = EnGoma_PrepareJump; + this->actionTimer = 30; +} + +void EnGoma_PrepareJump(EnGoma* this, GlobalContext* globalCtx) { + s16 targetAngle; + + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + + targetAngle = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + Math_ApproachS(&this->actor.world.rot.y, targetAngle, 2, 4000); + Math_ApproachS(&this->actor.shape.rot.y, targetAngle, 2, 3000); + + if (this->actionTimer == 0) { + EnGoma_SetupJump(this); + } + this->visualState = 0; +} + +void EnGoma_SetupLand(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolLandFromJumpAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolLandFromJumpAnim), ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnGoma_Land; + this->actionTimer = 10; +} + +void EnGoma_Land(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->actor.bgCheckFlags & 1) { + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 2.0f); + } + if (this->actionTimer == 0) { + EnGoma_SetupStand(this); + } +} + +void EnGoma_SetupJump(EnGoma* this) { + Animation_Change(&this->skelanime, &gObjectGolJumpHeadbuttAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gObjectGolJumpHeadbuttAnim), ANIMMODE_ONCE, 0.0f); + this->actionFunc = EnGoma_Jump; + this->actor.velocity.y = 8.0f; + + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_CRY); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_CRY); + } +} + +void EnGoma_Jump(EnGoma* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_24; + SkelAnime_Update(&this->skelanime); + Math_ApproachF(&this->actor.speedXZ, 10.0f, 0.5f, 5.0f); + + if (this->actor.velocity.y <= 0.0f && (this->actor.bgCheckFlags & 1)) { + EnGoma_SetupLand(this); + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_LAND2); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_LAND2); + } + } + this->visualState = 0; +} + +void EnGoma_Stand(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + Math_ApproachS(&this->actor.shape.rot.y, Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor), 2, + 3000); + + if (this->actionTimer == 0) { + EnGoma_SetupChasePlayer(this); + } +} + +void EnGoma_ChasePlayer(EnGoma* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (Animation_OnFrame(&this->skelanime, 1.0f) || Animation_OnFrame(&this->skelanime, 5.0f)) { + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_WALK); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_WALK); + } + } + + Math_ApproachF(&this->actor.speedXZ, 10.0f / 3.0f, 0.5f, 2.0f); + Math_ApproachS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 3, 2000); + Math_ApproachS(&this->actor.shape.rot.y, this->actor.world.rot.y, 2, 3000); + + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 0.0f; + } + if (this->actor.xzDistToPlayer <= 150.0f) { + EnGoma_SetupPrepareJump(this); + } +} + +void EnGoma_SetupStunned(EnGoma* this, GlobalContext* globalCtx) { + this->actionFunc = EnGoma_Stunned; + this->stunTimer = 100; + Animation_MorphToLoop(&this->skelanime, &gObjectGolStandAnim, -5.0f); + this->actionTimer = (s16)Rand_ZeroFloat(15.0f) + 3; + + if (this->actor.params < 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_BJR_FREEZE); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + } +} + +void EnGoma_Stunned(EnGoma* this, GlobalContext* globalCtx) { + Actor_SetColorFilter(&this->actor, 0, 180, 0, 2); + this->visualState = 2; + + if (this->actionTimer != 0) { + SkelAnime_Update(&this->skelanime); + } + + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 0.0f; + Math_ApproachZeroF(&this->actor.speedXZ, 0.5f, 2.0f); + } + + if (this->stunTimer == 0) { + EnGoma_SetupStand(this); + } else if (--this->stunTimer < 30) { + if (this->stunTimer & 1) { + this->actor.world.pos.x += 1.5f; + this->actor.world.pos.z += 1.5f; + } else { + this->actor.world.pos.x -= 1.5f; + this->actor.world.pos.z -= 1.5f; + } + } +} + +void EnGoma_LookAtPlayer(EnGoma* this, GlobalContext* globalCtx) { + s16 eyePitch; + s16 eyeYaw; + + eyeYaw = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.shape.rot.y; + eyePitch = Actor_WorldPitchTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.shape.rot.x; + + if (eyeYaw > 6000) { + eyeYaw = 6000; + } + if (eyeYaw < -6000) { + eyeYaw = -6000; + } + Math_ApproachS(&this->eyeYaw, eyeYaw, 3, 2000); + Math_ApproachS(&this->eyePitch, eyePitch, 3, 2000); +} + +void EnGoma_UpdateHit(EnGoma* this, GlobalContext* globalCtx) { + static Vec3f sShieldKnockbackVel = { 0.0f, 0.0f, 20.0f }; + Player* player = GET_PLAYER(globalCtx); + + if (this->hurtTimer != 0) { + this->hurtTimer--; + } else { + ColliderInfo* acHitInfo; + u8 swordDamage; + + if ((this->colCyl1.base.atFlags & 2) && this->actionFunc == EnGoma_Jump) { + EnGoma_SetupLand(this); + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + } + + if ((this->colCyl2.base.acFlags & AC_HIT) && (s8)this->actor.colChkInfo.health > 0) { + acHitInfo = this->colCyl2.info.acHitInfo; + this->colCyl2.base.acFlags &= ~AC_HIT; + + if (this->gomaType == ENGOMA_NORMAL) { + u32 dmgFlags = acHitInfo->toucher.dmgFlags; + + if (dmgFlags & 0x100000) { + if (this->actionFunc == EnGoma_Jump) { + EnGoma_SetupLand(this); + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = -5.0f; + } else { + Matrix_RotateY(player->actor.shape.rot.y / (f32)0x8000 * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&sShieldKnockbackVel, &this->shieldKnockbackVel); + this->invincibilityTimer = 5; + } + } else if (dmgFlags & 1) { // stun + if (this->actionFunc != EnGoma_Stunned) { + EnGoma_SetupStunned(this, globalCtx); + this->hurtTimer = 8; + } + } else { + swordDamage = CollisionCheck_GetSwordDamage(dmgFlags); + + if (swordDamage) { + EffectSsSibuki_SpawnBurst(globalCtx, &this->actor.focus.pos); + } else { + swordDamage = 1; + } + + this->actor.colChkInfo.health -= swordDamage; + EnGoma_SetupHurt(this, globalCtx); + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5); + this->hurtTimer = 13; + } + } else { + // die if still an egg + if (this->actor.params <= 5) { //! BossGoma only has 3 children + BossGoma* parent = (BossGoma*)this->actor.parent; + + parent->childrenGohmaState[this->actor.params] = -1; + } + + EnGoma_SpawnHatchDebris(this, globalCtx); + Actor_Kill(&this->actor); + } + } + } +} + +void EnGoma_UpdateEyeEnvColor(EnGoma* this) { + static f32 sTargetEyeEnvColors[][3] = { + { 255.0f, 0.0f, 50.0f }, + { 17.0f, 255.0f, 50.0f }, + { 0.0f, 170.0f, 50.0f }, + }; + + Math_ApproachF(&this->eyeEnvColor[0], sTargetEyeEnvColors[0][this->visualState], 0.5f, 20.0f); + Math_ApproachF(&this->eyeEnvColor[1], sTargetEyeEnvColors[1][this->visualState], 0.5f, 20.0f); + Math_ApproachF(&this->eyeEnvColor[2], sTargetEyeEnvColors[2][this->visualState], 0.5f, 20.0f); +} + +void EnGoma_SetFloorRot(EnGoma* this) { + f32 nx; + f32 ny; + f32 nz; + + if (this->actor.floorPoly != NULL) { + nx = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.x); + ny = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); + nz = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.z); + Math_ApproachS(&this->slopePitch, -Math_FAtan2F(-nz * ny, 1.0f) * (0x8000 / M_PI), 1, 1000); + Math_ApproachS(&this->slopeRoll, Math_FAtan2F(-nx * ny, 1.0f) * (0x8000 / M_PI), 1, 1000); + } +} + +void EnGoma_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGoma* this = (EnGoma*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (this->invincibilityTimer != 0) { + this->invincibilityTimer--; + } + + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + this->actor.world.pos.x = this->actor.world.pos.x + this->shieldKnockbackVel.x; + this->actor.world.pos.z = this->actor.world.pos.z + this->shieldKnockbackVel.z; + Math_ApproachZeroF(&this->shieldKnockbackVel.x, 1.0f, 3.0f); + Math_ApproachZeroF(&this->shieldKnockbackVel.z, 1.0f, 3.0f); + + if (this->actor.params < 10) { + this->eggTimer++; + Math_SmoothStepToF(&this->actor.scale.x, 0.01f, 0.5f, 0.00075f, 0.000001f); + Math_SmoothStepToF(&this->actor.scale.y, 0.01f, 0.5f, 0.00075f, 0.000001f); + Math_SmoothStepToF(&this->actor.scale.z, 0.01f, 0.5f, 0.00075f, 0.000001f); + EnGoma_UpdateHit(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 5); + EnGoma_SetFloorRot(this); + Actor_SetFocus(&this->actor, 20.0f); + EnGoma_LookAtPlayer(this, globalCtx); + EnGoma_UpdateEyeEnvColor(this); + this->visualState = 1; + if (player->swordState != 0) { + this->colCyl2.dim.radius = 35; + this->colCyl2.dim.height = 35; + this->colCyl2.dim.yShift = 0; + } else { + this->colCyl2.dim.radius = 15; + this->colCyl2.dim.height = 30; + this->colCyl2.dim.yShift = 10; + } + if (this->invincibilityTimer == 0) { + Collider_UpdateCylinder(&this->actor, &this->colCyl1); + Collider_UpdateCylinder(&this->actor, &this->colCyl2); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colCyl1.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCyl2.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colCyl1.base); + } + } +} + +s32 EnGoma_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnGoma* this = (EnGoma*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_goma.c", 1976); + gDPSetEnvColor(POLY_OPA_DISP++, (s16)this->eyeEnvColor[0], (s16)this->eyeEnvColor[1], (s16)this->eyeEnvColor[2], + 255); + + if (limbIndex == GOMA_LIMB_EYE_IRIS_ROOT1) { + rot->x += this->eyePitch; + rot->y += this->eyeYaw; + } else if (limbIndex == GOMA_LIMB_BODY && this->hurtTimer != 0) { + gDPSetEnvColor(POLY_OPA_DISP++, (s16)(Rand_ZeroOne() * 255.0f), (s16)(Rand_ZeroOne() * 255.0f), + (s16)(Rand_ZeroOne() * 255.0f), 255); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_goma.c", 2011); + return 0; +} + +Gfx* EnGoma_NoBackfaceCullingDlist(GraphicsContext* gfxCtx) { + Gfx* dListHead; + Gfx* dList; + + dListHead = dList = Graph_Alloc(gfxCtx, sizeof(Gfx) * 4); + gDPPipeSync(dListHead++); + gDPSetRenderMode(dListHead++, G_RM_PASS, G_RM_AA_ZB_TEX_EDGE2); + gSPClearGeometryMode(dListHead++, G_CULL_BACK); + gSPEndDisplayList(dListHead++); + return dList; +} + +void EnGoma_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGoma* this = (EnGoma*)thisx; + s32 y; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_goma.c", 2040); + func_80093D18(globalCtx->state.gfxCtx); + + switch (this->gomaType) { + case ENGOMA_NORMAL: + this->actor.naviEnemyId = 0x03; + Matrix_Translate(this->actor.world.pos.x, + this->actor.world.pos.y + ((this->actor.shape.yOffset * this->actor.scale.y) + + globalCtx->mainCamera.skyboxOffset.y), + this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateX(this->slopePitch / (f32)0x8000 * M_PI, MTXMODE_APPLY); + Matrix_RotateZ(this->slopeRoll / (f32)0x8000 * M_PI, MTXMODE_APPLY); + Matrix_RotateY(this->actor.shape.rot.y / (f32)0x8000 * M_PI, MTXMODE_APPLY); + Matrix_RotateX(this->actor.shape.rot.x / (f32)0x8000 * M_PI, MTXMODE_APPLY); + Matrix_RotateZ(this->actor.shape.rot.z / (f32)0x8000 * M_PI, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + SkelAnime_DrawOpa(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, EnGoma_OverrideLimbDraw, + NULL, this); + break; + + case ENGOMA_EGG: + this->actor.naviEnemyId = 0x02; + y = (s16)(sinf((this->eggTimer * 5.0f * 3.1415f) / 180.0f) * 31.9f); + y = (s16)(y + 31); + gSPSegment(POLY_OPA_DISP++, 0x08, func_80094E78(globalCtx->state.gfxCtx, 0, y)); + Matrix_Push(); + Matrix_Scale(this->eggScale, 1.0f / this->eggScale, this->eggScale, MTXMODE_APPLY); + Matrix_RotateY(this->eggSquishAngle * 0.15f, MTXMODE_APPLY); + Matrix_RotateZ(this->eggSquishAngle * 0.1f, MTXMODE_APPLY); + Matrix_Scale(0.95f - this->eggSquishAmount, this->eggSquishAmount + 1.05f, 0.95f - this->eggSquishAmount, + MTXMODE_APPLY); + Matrix_RotateZ(-(this->eggSquishAngle * 0.1f), MTXMODE_APPLY); + Matrix_RotateY(-(this->eggSquishAngle * 0.15f), MTXMODE_APPLY); + Matrix_Translate(0.0f, this->eggYOffset, 0.0f, MTXMODE_APPLY); + Matrix_RotateX(this->eggPitch, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_goma.c", 2101), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gObjectGolEggDL); + Matrix_Pop(); + break; + + case ENGOMA_HATCH_DEBRIS: + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_goma.c", 2107), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBrownFragmentDL); + break; + + case ENGOMA_BOSSLIMB: + if (this->bossLimbDl != NULL) { + gSPSegment(POLY_OPA_DISP++, 0x08, EnGoma_NoBackfaceCullingDlist(globalCtx->state.gfxCtx)); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_goma.c", 2114), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, this->bossLimbDl); + } + break; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_goma.c", 2119); +} + +void EnGoma_Debris(EnGoma* this, GlobalContext* globalCtx) { + this->actor.shape.rot.y += 2500; + this->actor.shape.rot.x += 3500; + if (this->actionTimer == 0) { + Actor_Kill(&this->actor); + } +} + +void EnGoma_SpawnHatchDebris(EnGoma* this, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + s16 i; + + if (this->actor.params < 6) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_GOMA_BJR_EGG2); + } else { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_GOMA_EGG2); + } + + for (i = 0; i < 15; i++) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_GOMA, + Rand_CenteredFloat(10.0f) + this->actor.world.pos.x, + Rand_CenteredFloat(10.0f) + this->actor.world.pos.y + 15.0f, + Rand_CenteredFloat(10.0f) + this->actor.world.pos.z, 0, Rand_CenteredFloat(0x10000 - 0.01f), + 0, i + 10); + } +} + +void EnGoma_BossLimb(EnGoma* this, GlobalContext* globalCtx) { + Vec3f vel = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 1.0f, 0.0f }; + Color_RGBA8 primColor = { 255, 255, 255, 255 }; + Color_RGBA8 envColor = { 0, 100, 255, 255 }; + Vec3f pos; + + this->actor.world.pos.y -= 5.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4); + this->actor.world.pos.y += 5.0f; + + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 0.0f; + } else if (this->actionTimer < 250) { + this->actor.shape.rot.y += 2000; + } + + if (this->actionTimer == 250) { + this->actor.gravity = -1.0f; + } + + if (this->actionTimer < 121) { + if (Math_SmoothStepToF(&this->actor.scale.y, 0.0f, 1.0f, 0.00075f, 0) <= 0.001f) { + Actor_Kill(&this->actor); + } + this->actor.scale.x = this->actor.scale.z = this->actor.scale.y; + } + + if (this->actionTimer % 8 == 0 && this->actionTimer != 0) { + pos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + pos.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + func_8002836C(globalCtx, &pos, &vel, &accel, &primColor, &envColor, 500, 10, 10); + } +} + +void EnGoma_Reset(void) { + sSpawnNum = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.h b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.h new file mode 100644 index 000000000..493cf6996 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Goma/z_en_goma.h @@ -0,0 +1,80 @@ +#ifndef Z_EN_GOMA_H +#define Z_EN_GOMA_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0 */ ENGOMA_NORMAL, + /* 1 */ ENGOMA_EGG, + /* 2 */ ENGOMA_HATCH_DEBRIS, + /* 3 */ ENGOMA_BOSSLIMB +} GomaType; + +struct EnGoma; + +typedef void (*EnGomaActionFunc)(struct EnGoma*, GlobalContext*); + +typedef enum { + /* 0 */ GOMA_LIMB_NONE, + /* 1 */ GOMA_LIMB_ROOT1, + /* 2 */ GOMA_LIMB_ROOT2, + /* 3 */ GOMA_LIMB_BODY, + /* 4 */ GOMA_LIMB_ANTENNA_ROOT1, + /* 5 */ GOMA_LIMB_ANTENNA_ROOT2, + /* 6 */ GOMA_LIMB_ANTENNA, + /* 7 */ GOMA_LIMB_EYE_IRIS_ROOT1, + /* 8 */ GOMA_LIMB_EYE_IRIS_ROOT2, + /* 9 */ GOMA_LIMB_EYE_IRIS, + /* 10 */ GOMA_LIMB_L_LEG_ROOT1, + /* 11 */ GOMA_LIMB_L_LEG_ROOT2, + /* 12 */ GOMA_LIMB_L_SHIN_ROOT, + /* 13 */ GOMA_LIMB_L_FOOT_ROOT, + /* 14 */ GOMA_LIMB_L_FOOT, + /* 15 */ GOMA_LIMB_L_SHIN, + /* 16 */ GOMA_LIMB_L_THIGH, + /* 17 */ GOMA_LIMB_R_LEG_ROOT1, + /* 18 */ GOMA_LIMB_R_LEG_ROOT2, + /* 19 */ GOMA_LIMB_R_SHIN_ROOT, + /* 20 */ GOMA_LIMB_R_FOOT_ROOT, + /* 21 */ GOMA_LIMB_R_FOOT, + /* 22 */ GOMA_LIMB_R_SHIN, + /* 23 */ GOMA_LIMB_R_THIGH, + /* 24 */ GOMA_LIMB_MAX +} EnGomaLimb; + +typedef struct EnGoma { + /* 0x000 */ Actor actor; + /* 0x14C */ SkelAnime skelanime; + /* 0x190 */ Vec3s jointTable[24]; + /* 0x220 */ Vec3s morphTable[24]; + /* 0x2B0 */ EnGomaActionFunc actionFunc; + /* 0x2B4 */ s16 slopePitch; + /* 0x2B6 */ s16 slopeRoll; + /* 0x2B8 */ s16 gomaType; + /* 0x2BA */ s16 eyePitch; + /* 0x2BC */ s16 eyeYaw; + /* 0x2BE */ s16 hatchState; + /* 0x2C0 */ s16 eggTimer; + /* 0x2C2 */ s16 hurtTimer; + /* 0x2C4 */ s16 visualState; + /* 0x2C6 */ s16 playerDetectionTimer; + /* 0x2C8 */ s16 spawnNum; // some debug spawn ID + /* 0x2CA */ s16 invincibilityTimer; + /* 0x2CC */ s16 actionTimer; + /* 0x2D0 */ f32 eggScale; + /* 0x2D4 */ f32 eggPitch; + /* 0x2D8 */ f32 eggSquishAngle; + /* 0x2DC */ f32 eggSquishAccel; + /* 0x2E0 */ f32 eyeEnvColor[3]; + /* 0x2EC */ f32 eggSquishAmount; + /* 0x2F0 */ f32 eggYOffset; + /* 0x2F4 */ s32 unk_2F4; + /* 0x2F8 */ s16 stunTimer; + /* 0x2FC */ Vec3f shieldKnockbackVel; + /* 0x308 */ Gfx* bossLimbDl; // set by z_boss_goma + /* 0x30C */ ColliderCylinder colCyl1; + /* 0x358 */ ColliderCylinder colCyl2; +} EnGoma; // size = 0x03A4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.c b/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.c new file mode 100644 index 000000000..08db6d91f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.c @@ -0,0 +1,758 @@ +/* + * File: z_en_goroiwa.c + * Overlay: ovl_En_Goroiwa + * Description: Rolling boulders + */ + +#include "z_en_goroiwa.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_goroiwa/object_goroiwa.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef s32 (*EnGoroiwaUnkFunc1)(EnGoroiwa* this, GlobalContext* globalCtx); +typedef void (*EnGoroiwaUnkFunc2)(EnGoroiwa* this); + +#define ENGOROIWA_ENABLE_AT (1 << 0) +#define ENGOROIWA_ENABLE_OC (1 << 1) +#define ENGOROIWA_PLAYER_IN_THE_WAY (1 << 2) +#define ENGOROIWA_RETAIN_ROT_SPEED (1 << 3) +#define ENGOROIWA_IN_WATER (1 << 4) + +#define ENGOROIWA_LOOPMODE_ONEWAY 0 +/* same as ENGOROIWA_LOOPMODE_ONEWAY but display rock fragments as if the boulder broke at the end of the path*/ +#define ENGOROIWA_LOOPMODE_ONEWAY_BREAK 1 +#define ENGOROIWA_LOOPMODE_ROUNDTRIP 3 + +void EnGoroiwa_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGoroiwa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGoroiwa_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGoroiwa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnGoroiwa_SetupRoll(EnGoroiwa* this); +void EnGoroiwa_Roll(EnGoroiwa* this, GlobalContext* globalCtx); +void EnGoroiwa_SetupMoveAndFallToGround(EnGoroiwa* this); +void EnGoroiwa_MoveAndFallToGround(EnGoroiwa* this, GlobalContext* globalCtx); +void EnGoroiwa_SetupWait(EnGoroiwa* this); +void EnGoroiwa_Wait(EnGoroiwa* this, GlobalContext* globalCtx); +void EnGoroiwa_SetupMoveUp(EnGoroiwa* this); +void EnGoroiwa_MoveUp(EnGoroiwa* this, GlobalContext* globalCtx); +void EnGoroiwa_SetupMoveDown(EnGoroiwa* this); +void EnGoroiwa_MoveDown(EnGoroiwa* this, GlobalContext* globalCtx); + +const ActorInit En_Goroiwa_InitVars = { + ACTOR_EN_GOROIWA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GOROIWA, + sizeof(EnGoroiwa), + (ActorFunc)EnGoroiwa_Init, + (ActorFunc)EnGoroiwa_Destroy, + (ActorFunc)EnGoroiwa_Update, + (ActorFunc)EnGoroiwa_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x20000000, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 58 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_HEAVY }; + +static f32 sUnused[] = { 10.0f, 9.2f }; + +void EnGoroiwa_UpdateCollider(EnGoroiwa* this) { + static f32 yOffsets[] = { 0.0f, 59.5f }; + Sphere16* worldSphere = &this->collider.elements[0].dim.worldSphere; + + worldSphere->center.x = this->actor.world.pos.x; + worldSphere->center.y = this->actor.world.pos.y + yOffsets[(this->actor.params >> 10) & 1]; + worldSphere->center.z = this->actor.world.pos.z; +} + +void EnGoroiwa_InitCollider(EnGoroiwa* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + EnGoroiwa_UpdateCollider(this); + this->collider.elements[0].dim.worldSphere.radius = 58; +} + +void EnGoroiwa_UpdateFlags(EnGoroiwa* this, u8 setFlags) { + this->stateFlags &= ~(ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC); + this->stateFlags |= setFlags; +} + +s32 EnGoroiwa_Vec3fNormalize(Vec3f* ret, Vec3f* a) { + f32 magnitude = Math3D_Vec3fMagnitude(a); + f32 scale; + + if (magnitude < 0.001f) { + return false; + } + + scale = 1.0f / magnitude; + + ret->x = a->x * scale; + ret->y = a->y * scale; + ret->z = a->z * scale; + + return true; +} + +void EnGoroiwa_SetSpeed(EnGoroiwa* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT04) { + this->isInKokiri = true; + R_EN_GOROIWA_SPEED = 920; + } else { + this->isInKokiri = false; + R_EN_GOROIWA_SPEED = 1000; + } +} + +void EnGoroiwa_FaceNextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + Vec3s* nextPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint; + Vec3f nextPosF; + + nextPosF.x = nextPos->x; + nextPosF.y = nextPos->y; + nextPosF.z = nextPos->z; + + this->actor.world.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &nextPosF); +} + +void EnGoroiwa_GetPrevWaypointDiff(EnGoroiwa* this, GlobalContext* globalCtx, Vec3f* dest) { + s16 loopMode = (this->actor.params >> 8) & 3; + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + s16 prevWaypoint = this->currentWaypoint - this->pathDirection; + Vec3s* prevPointPos; + Vec3s* currentPointPos; + + if (prevWaypoint < 0) { + if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) { + prevWaypoint = this->endWaypoint; + } else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) { + prevWaypoint = 1; + } + } else if (prevWaypoint > this->endWaypoint) { + if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) { + prevWaypoint = 0; + } else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) { + prevWaypoint = this->endWaypoint - 1; + } + } + + currentPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->currentWaypoint; + prevPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + prevWaypoint; + dest->x = currentPointPos->x - prevPointPos->x; + dest->y = currentPointPos->x - prevPointPos->y; + dest->z = currentPointPos->x - prevPointPos->z; +} + +void EnGoroiw_CheckEndOfPath(EnGoroiwa* this) { + s16 loopMode = (this->actor.params >> 8) & 3; + + if (this->nextWaypoint < 0) { + if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) { + this->currentWaypoint = this->endWaypoint; + this->nextWaypoint = this->endWaypoint - 1; + this->pathDirection = -1; + } else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) { + this->currentWaypoint = 0; + this->nextWaypoint = 1; + this->pathDirection = 1; + } + } else if (this->nextWaypoint > this->endWaypoint) { + if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) { + this->currentWaypoint = 0; + this->nextWaypoint = 1; + this->pathDirection = 1; + } else if (loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) { + this->currentWaypoint = this->endWaypoint; + this->nextWaypoint = this->endWaypoint - 1; + this->pathDirection = -1; + } + } +} + +void EnGoroiwa_SetNextWaypoint(EnGoroiwa* this) { + this->currentWaypoint = this->nextWaypoint; + this->nextWaypoint += this->pathDirection; + EnGoroiw_CheckEndOfPath(this); +} + +void EnGoroiwa_ReverseDirection(EnGoroiwa* this) { + this->pathDirection *= -1; + this->currentWaypoint = this->nextWaypoint; + this->nextWaypoint += this->pathDirection; +} + +void EnGoroiwa_InitPath(EnGoroiwa* this, GlobalContext* globalCtx) { + this->endWaypoint = globalCtx->setupPathList [this->actor.params & 0xFF].count - 1; + this->currentWaypoint = 0; + this->nextWaypoint = 1; + this->pathDirection = 1; +} + +void EnGoroiwa_TeleportToWaypoint(EnGoroiwa* this, GlobalContext* globalCtx, s32 waypoint) { + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + Vec3s* pointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + waypoint; + + this->actor.world.pos.x = pointPos->x; + this->actor.world.pos.y = pointPos->y; + this->actor.world.pos.z = pointPos->z; +} + +void EnGoroiwa_InitRotation(EnGoroiwa* this) { + this->prevUnitRollAxis.x = 1.0f; + this->rollRotSpeed = 1.0f; +} + +s32 EnGoroiwa_GetAscendDirection(EnGoroiwa* this, GlobalContext* globalCtx) { + s32 pad; + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint; + Vec3s* currentPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->currentWaypoint; + + if (nextPointPos->x == currentPointPos->x && nextPointPos->z == currentPointPos->z) { + if (nextPointPos->y == currentPointPos->y) { + // "Error: Invalid path data (points overlap)" + osSyncPrintf("Error : レールデータ不正(点が重なっている)"); + osSyncPrintf("(%s %d)(arg_data 0x%04x)\n", "../z_en_gr.c", 559, this->actor.params); + } + + if (nextPointPos->y > currentPointPos->y) { + return 1; + } else { + return -1; + } + } + + return 0; +} + +void EnGoroiwa_SpawnDust(GlobalContext* globalCtx, Vec3f* pos) { + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.3f, 0.0f }; + Vec3f randPos; + s32 i; + s16 angle = 0; + + for (i = 0; i < 8; i++) { + angle += 0x4E20; + randPos.x = pos->x + (47.0f * (Rand_ZeroOne() * 0.5f + 0.5f)) * Math_SinS(angle); + randPos.y = pos->y + (Rand_ZeroOne() - 0.5f) * 40.0f; + randPos.z = pos->z + ((47.0f * (Rand_ZeroOne() * 0.5f + 0.5f))) * Math_CosS(angle); + func_800286CC(globalCtx, &randPos, &velocity, &accel, (s16)(Rand_ZeroOne() * 30.0f) + 100, 80); + func_800286CC(globalCtx, &randPos, &velocity, &accel, (s16)(Rand_ZeroOne() * 20.0f) + 80, 80); + } +} + +void EnGoroiwa_SpawnWaterEffects(GlobalContext* globalCtx, Vec3f* contactPos) { + Vec3f splashPos; + s32 i; + s16 angle = 0; + + for (i = 0; i < 11; i++) { + angle += 0x1746; + splashPos.x = contactPos->x + (Math_SinS(angle) * 55.0f); + splashPos.y = contactPos->y; + splashPos.z = contactPos->z + (Math_CosS(angle) * 55.0f); + EffectSsGSplash_Spawn(globalCtx, &splashPos, 0, 0, 0, 350); + } + + EffectSsGRipple_Spawn(globalCtx, contactPos, 300, 700, 0); + EffectSsGRipple_Spawn(globalCtx, contactPos, 500, 900, 4); + EffectSsGRipple_Spawn(globalCtx, contactPos, 500, 1300, 8); +} + +s32 EnGoroiwa_MoveAndFall(EnGoroiwa* this, GlobalContext* globalCtx) { + Path* path; + s32 result; + s32 pad; + Vec3s* nextPointPos; + + Math_StepToF(&this->actor.speedXZ, R_EN_GOROIWA_SPEED * 0.01f, 0.3f); + func_8002D868(&this->actor); + path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint; + result = true; + result &= Math_StepToF(&this->actor.world.pos.x, nextPointPos->x, fabsf(this->actor.velocity.x)); + result &= Math_StepToF(&this->actor.world.pos.z, nextPointPos->z, fabsf(this->actor.velocity.z)); + this->actor.world.pos.y += this->actor.velocity.y; + return result; +} + +s32 EnGoroiwa_Move(EnGoroiwa* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + s32 pad; + Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint; + Vec3s* currentPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->currentWaypoint; + s32 nextPointReached; + Vec3f posDiff; + Vec3f nextPointPosF; + + nextPointPosF.x = nextPointPos->x; + nextPointPosF.y = nextPointPos->y; + nextPointPosF.z = nextPointPos->z; + Math_StepToF(&this->actor.speedXZ, R_EN_GOROIWA_SPEED * 0.01f, 0.3f); + if (Math3D_Vec3fDistSq(&nextPointPosF, &this->actor.world.pos) < SQ(5.0f)) { + Math_Vec3f_Diff(&nextPointPosF, &this->actor.world.pos, &posDiff); + } else { + posDiff.x = nextPointPosF.x - currentPointPos->x; + posDiff.y = nextPointPosF.y - currentPointPos->y; + posDiff.z = nextPointPosF.z - currentPointPos->z; + } + EnGoroiwa_Vec3fNormalize(&this->actor.velocity, &posDiff); + this->actor.velocity.x *= this->actor.speedXZ; + this->actor.velocity.y *= this->actor.speedXZ; + this->actor.velocity.z *= this->actor.speedXZ; + nextPointReached = true; + nextPointReached &= Math_StepToF(&this->actor.world.pos.x, nextPointPosF.x, fabsf(this->actor.velocity.x)); + nextPointReached &= Math_StepToF(&this->actor.world.pos.y, nextPointPosF.y, fabsf(this->actor.velocity.y)); + nextPointReached &= Math_StepToF(&this->actor.world.pos.z, nextPointPosF.z, fabsf(this->actor.velocity.z)); + return nextPointReached; +} + +s32 EnGoroiwa_MoveUpToNextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) { + s32 pad; + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint; + + Math_StepToF(&this->actor.velocity.y, (R_EN_GOROIWA_SPEED * 0.01f) * 0.5f, 0.18f); + this->actor.world.pos.x = nextPointPos->x; + this->actor.world.pos.z = nextPointPos->z; + return Math_StepToF(&this->actor.world.pos.y, nextPointPos->y, fabsf(this->actor.velocity.y)); +} + +s32 EnGoroiwa_MoveDownToNextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) { + s32 pad; + Path* path = &globalCtx->setupPathList[this->actor.params & 0xFF]; + Vec3s* nextPointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->nextWaypoint; + f32 nextPointY; + f32 thisY; + f32 yDistToFloor; + s32 quakeIdx; + CollisionPoly* floorPoly; + Vec3f raycastFrom; + f32 floorY; + s32 pad2; + s32 floorBgId; + Vec3f dustPos; + WaterBox* waterBox; + f32 ySurface; + Vec3f waterHitPos; + + nextPointY = nextPointPos->y; + Math_StepToF(&this->actor.velocity.y, -14.0f, 1.0f); + this->actor.world.pos.x = nextPointPos->x; + this->actor.world.pos.z = nextPointPos->z; + thisY = this->actor.world.pos.y; + if (1) {} + this->actor.world.pos.y += this->actor.velocity.y; + if (this->actor.velocity.y < 0.0f && this->actor.world.pos.y <= nextPointY) { + if (this->bounceCount == 0) { + if (this->actor.xzDistToPlayer < 600.0f) { + quakeIdx = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIdx, -0x3CB0); + Quake_SetQuakeValues(quakeIdx, 3, 0, 0, 0); + Quake_SetCountdown(quakeIdx, 7); + } + this->rollRotSpeed = 0.0f; + if (!(this->stateFlags & ENGOROIWA_IN_WATER)) { + raycastFrom.x = this->actor.world.pos.x; + raycastFrom.y = this->actor.world.pos.y + 50.0f; + raycastFrom.z = this->actor.world.pos.z; + floorY = BgCheck_EntityRaycastFloor5(globalCtx, &globalCtx->colCtx, &floorPoly, &floorBgId, + &this->actor, &raycastFrom); + yDistToFloor = floorY - (this->actor.world.pos.y - 59.5f); + if (fabsf(yDistToFloor) < 15.0f) { + dustPos.x = this->actor.world.pos.x; + dustPos.y = floorY + 10.0f; + dustPos.z = this->actor.world.pos.z; + EnGoroiwa_SpawnDust(globalCtx, &dustPos); + } + } + } + if (this->bounceCount >= 1) { + return true; + } + this->bounceCount++; + this->actor.velocity.y *= -0.3f; + this->actor.world.pos.y = nextPointY - ((this->actor.world.pos.y - nextPointY) * 0.3f); + } + if (this->bounceCount == 0 && + WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, + &ySurface, &waterBox) && + this->actor.world.pos.y <= ySurface) { + this->stateFlags |= ENGOROIWA_IN_WATER; + if (ySurface < thisY) { + waterHitPos.x = this->actor.world.pos.x; + waterHitPos.y = ySurface; + waterHitPos.z = this->actor.world.pos.z; + EnGoroiwa_SpawnWaterEffects(globalCtx, &waterHitPos); + this->actor.velocity.y *= 0.2f; + } + if (this->actor.velocity.y < -8.0f) { + this->actor.velocity.y = -8.0f; + } + } + return false; +} + +void EnGoroiwa_UpdateRotation(EnGoroiwa* this, GlobalContext* globalCtx) { + static Vec3f unitY = { 0.0f, 1.0f, 0.0f }; + s32 pad; + Vec3f* rollAxisPtr; + f32 rollAngleDiff; + Vec3f rollAxis; + Vec3f unitRollAxis; + MtxF mtx; + Vec3f unusedDiff; + + if (this->stateFlags & ENGOROIWA_RETAIN_ROT_SPEED) { + rollAngleDiff = this->prevRollAngleDiff; + } else { + this->prevRollAngleDiff = Math3D_Vec3f_DistXYZ(&this->actor.world.pos, &this->actor.prevPos) * (1.0f / 59.5f); + rollAngleDiff = this->prevRollAngleDiff; + } + rollAngleDiff *= this->rollRotSpeed; + rollAxisPtr = &rollAxis; + if (this->stateFlags & ENGOROIWA_RETAIN_ROT_SPEED) { + /* + * EnGoroiwa_GetPrevWaypointDiff has no side effects and its result goes unused, + * its result was probably meant to be used instead of the actor's velocity in the + * Math3D_Vec3f_Cross call. + */ + EnGoroiwa_GetPrevWaypointDiff(this, globalCtx, &unusedDiff); + Math3D_Vec3f_Cross(&unitY, &this->actor.velocity, rollAxisPtr); + } else { + Math3D_Vec3f_Cross(&unitY, &this->actor.velocity, rollAxisPtr); + } + + if (EnGoroiwa_Vec3fNormalize(&unitRollAxis, rollAxisPtr)) { + this->prevUnitRollAxis = unitRollAxis; + } else { + unitRollAxis = this->prevUnitRollAxis; + } + + Matrix_RotateAxis(rollAngleDiff, &unitRollAxis, MTXMODE_NEW); + Matrix_RotateY(this->actor.shape.rot.y * (2.0f * M_PI / 0x10000), MTXMODE_APPLY); + Matrix_RotateX(this->actor.shape.rot.x * (2.0f * M_PI / 0x10000), MTXMODE_APPLY); + Matrix_RotateZ(this->actor.shape.rot.z * (2.0f * M_PI / 0x10000), MTXMODE_APPLY); + Matrix_Get(&mtx); + Matrix_MtxFToYXZRotS(&mtx, &this->actor.shape.rot, 0); +} + +void EnGoroiwa_NextWaypoint(EnGoroiwa* this, GlobalContext* globalCtx) { + s16 loopMode = (this->actor.params >> 8) & 3; + + EnGoroiwa_SetNextWaypoint(this); + + if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY || loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK) { + if (this->currentWaypoint == 0 || this->currentWaypoint == this->endWaypoint) { + EnGoroiwa_TeleportToWaypoint(this, globalCtx, this->currentWaypoint); + } + } + + EnGoroiwa_FaceNextWaypoint(this, globalCtx); +} + +void EnGoroiwa_SpawnFragments(EnGoroiwa* this, GlobalContext* globalCtx) { + static f32 yOffsets[] = { 0.0f, 59.5f }; + s16 angle1; + s16 angle2; + s32 pad; + Vec3f* thisPos = &this->actor.world.pos; + Vec3f effectPos; + Vec3f fragmentVelocity; + f32 cos1; + f32 sin1; + f32 sin2; + s16 yOffsetIdx = (this->actor.params >> 10) & 1; + s32 i; + + for (i = 0, angle1 = 0; i < 16; i++, angle1 += 0x4E20) { + sin1 = Math_SinS(angle1); + cos1 = Math_CosS(angle1); + angle2 = Rand_ZeroOne() * 0xFFFF; + effectPos.x = Rand_ZeroOne() * 50.0f * sin1 * Math_SinS(angle2); + sin2 = Math_SinS(angle2); + effectPos.y = (Rand_ZeroOne() - 0.5f) * 100.0f * sin2 + yOffsets[yOffsetIdx]; + effectPos.z = Rand_ZeroOne() * 50.0f * cos1 * Math_SinS(angle2); + fragmentVelocity.x = effectPos.x * 0.2f; + fragmentVelocity.y = Rand_ZeroOne() * 15.0f + 2.0f; + fragmentVelocity.z = effectPos.z * 0.2f; + Math_Vec3f_Sum(&effectPos, thisPos, &effectPos); + EffectSsKakera_Spawn(globalCtx, &effectPos, &fragmentVelocity, &effectPos, -340, 33, 28, 2, 0, + Rand_ZeroOne() * 7.0f + 1.0f, 1, 0, 70, KAKERA_COLOR_NONE, 1, gBoulderFragmentsDL); + } + + effectPos.x = thisPos->x; + effectPos.y = thisPos->y + yOffsets[yOffsetIdx]; + effectPos.z = thisPos->z; + func_80033480(globalCtx, &effectPos, 80.0f, 5, 70, 110, 1); + func_80033480(globalCtx, &effectPos, 90.0f, 5, 110, 160, 1); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32_DIV1000(gravity, -860, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(minVelocityY, -15000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneForward, 1500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 150, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP), +}; + +void EnGoroiwa_Init(Actor* thisx, GlobalContext* globalCtx) { + static f32 yOffsets[] = { 0.0f, 595.0f }; + EnGoroiwa* this = (EnGoroiwa*)thisx; + s32 pathIdx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + EnGoroiwa_InitCollider(this, globalCtx); + pathIdx = this->actor.params & 0xFF; + if (pathIdx == 0xFF) { + // "Error: Invalid arg_data" + osSyncPrintf("Error : arg_data が不正(%s %d)(arg_data 0x%04x)\n", "../z_en_gr.c", 1033, + this->actor.params); + Actor_Kill(&this->actor); + return; + } + if (globalCtx->setupPathList[pathIdx].count < 2) { + // "Error: Invalid Path Data" + osSyncPrintf("Error : レールデータ が不正(%s %d)\n", "../z_en_gr.c", 1043); + Actor_Kill(&this->actor); + return; + } + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + ActorShape_Init(&this->actor.shape, yOffsets[(this->actor.params >> 10) & 1], ActorShadow_DrawCircle, 9.4f); + this->actor.shape.shadowAlpha = 200; + EnGoroiwa_SetSpeed(this, globalCtx); + EnGoroiwa_InitPath(this, globalCtx); + EnGoroiwa_TeleportToWaypoint(this, globalCtx, 0); + EnGoroiwa_InitRotation(this); + EnGoroiwa_FaceNextWaypoint(this, globalCtx); + EnGoroiwa_SetupRoll(this); + // "(Goroiwa)" + osSyncPrintf("(ごろ岩)(arg 0x%04x)(rail %d)(end %d)(bgc %d)(hit %d)\n", this->actor.params, + this->actor.params & 0xFF, (this->actor.params >> 8) & 3, (this->actor.params >> 10) & 1, + this->actor.home.rot.z & 1); +} + +void EnGoroiwa_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnGoroiwa* this = (EnGoroiwa*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnGoroiwa_SetupRoll(EnGoroiwa* this) { + this->actionFunc = EnGoroiwa_Roll; + EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC); + this->rollRotSpeed = 1.0f; +} + +void EnGoroiwa_Roll(EnGoroiwa* this, GlobalContext* globalCtx) { + static EnGoroiwaUnkFunc1 moveFuncs[] = { EnGoroiwa_Move, EnGoroiwa_MoveAndFall }; + static EnGoroiwaUnkFunc2 onHitSetupFuncs[] = { EnGoroiwa_SetupWait, EnGoroiwa_SetupMoveAndFallToGround }; + + s32 ascendDirection; + s16 yawDiff; + s16 loopMode; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + this->stateFlags &= ~ENGOROIWA_PLAYER_IN_THE_WAY; + yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y; + if (yawDiff > -0x4000 && yawDiff < 0x4000) { + this->stateFlags |= ENGOROIWA_PLAYER_IN_THE_WAY; + if (((this->actor.params >> 10) & 1) || (this->actor.home.rot.z & 1) != 1) { + EnGoroiwa_ReverseDirection(this); + EnGoroiwa_FaceNextWaypoint(this, globalCtx); + } + } + func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 0); + osSyncPrintf(VT_FGCOL(CYAN)); + osSyncPrintf("Player ぶっ飛ばし\n"); // "Player knocked down" + osSyncPrintf(VT_RST); + onHitSetupFuncs[(this->actor.params >> 10) & 1](this); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + if ((this->actor.home.rot.z & 1) == 1) { + this->collisionDisabledTimer = 50; + } + } else if (moveFuncs[(this->actor.params >> 10) & 1](this, globalCtx)) { + loopMode = (this->actor.params >> 8) & 3; + if (loopMode == ENGOROIWA_LOOPMODE_ONEWAY_BREAK && + (this->nextWaypoint == 0 || this->nextWaypoint == this->endWaypoint)) { + EnGoroiwa_SpawnFragments(this, globalCtx); + } + EnGoroiwa_NextWaypoint(this, globalCtx); + if ((loopMode == ENGOROIWA_LOOPMODE_ROUNDTRIP) && + (this->currentWaypoint == 0 || this->currentWaypoint == this->endWaypoint)) { + EnGoroiwa_SetupWait(this); + } else if (!((this->actor.params >> 10) & 1) && this->currentWaypoint != 0 && + this->currentWaypoint != this->endWaypoint) { + ascendDirection = EnGoroiwa_GetAscendDirection(this, globalCtx); + if (ascendDirection > 0) { + EnGoroiwa_SetupMoveUp(this); + } else if (ascendDirection < 0) { + EnGoroiwa_SetupMoveDown(this); + } else { + EnGoroiwa_SetupRoll(this); + } + } else { + EnGoroiwa_SetupRoll(this); + } + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BIGBALL_ROLL - SFX_FLAG); +} + +void EnGoroiwa_SetupMoveAndFallToGround(EnGoroiwa* this) { + this->actionFunc = EnGoroiwa_MoveAndFallToGround; + EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_OC); + this->actor.gravity = -0.86f; + this->actor.minVelocityY = -15.0f; + this->actor.speedXZ *= 0.15f; + this->actor.velocity.y = 5.0f; + this->rollRotSpeed = 1.0f; +} + +void EnGoroiwa_MoveAndFallToGround(EnGoroiwa* this, GlobalContext* globalCtx) { + EnGoroiwa_MoveAndFall(this, globalCtx); + if ((this->actor.bgCheckFlags & 1) && this->actor.velocity.y < 0.0f) { + if ((this->stateFlags & ENGOROIWA_PLAYER_IN_THE_WAY) && (this->actor.home.rot.z & 1) == 1) { + EnGoroiwa_ReverseDirection(this); + EnGoroiwa_FaceNextWaypoint(this, globalCtx); + } + EnGoroiwa_SetupWait(this); + } +} + +void EnGoroiwa_SetupWait(EnGoroiwa* this) { + static s16 waitDurations[] = { 20, 6 }; + + this->actionFunc = EnGoroiwa_Wait; + this->actor.speedXZ = 0.0f; + EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_OC); + this->waitTimer = waitDurations[this->actor.home.rot.z & 1]; + this->rollRotSpeed = 0.0f; +} + +void EnGoroiwa_Wait(EnGoroiwa* this, GlobalContext* globalCtx) { + if (this->waitTimer > 0) { + this->waitTimer--; + } else { + this->collider.base.atFlags &= ~AT_HIT; + EnGoroiwa_SetupRoll(this); + } +} + +void EnGoroiwa_SetupMoveUp(EnGoroiwa* this) { + this->actionFunc = EnGoroiwa_MoveUp; + EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC); + this->rollRotSpeed = 0.0f; + this->actor.velocity.y = fabsf(this->actor.speedXZ) * 0.1f; +} + +void EnGoroiwa_MoveUp(EnGoroiwa* this, GlobalContext* globalCtx) { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 4); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + if ((this->actor.home.rot.z & 1) == 1) { + this->collisionDisabledTimer = 50; + } + } else if (EnGoroiwa_MoveUpToNextWaypoint(this, globalCtx)) { + EnGoroiwa_NextWaypoint(this, globalCtx); + EnGoroiwa_SetupRoll(this); + this->actor.speedXZ = 0.0f; + } +} + +void EnGoroiwa_SetupMoveDown(EnGoroiwa* this) { + this->actionFunc = EnGoroiwa_MoveDown; + EnGoroiwa_UpdateFlags(this, ENGOROIWA_ENABLE_AT | ENGOROIWA_ENABLE_OC); + this->rollRotSpeed = 0.3f; + this->bounceCount = 0; + this->actor.velocity.y = fabsf(this->actor.speedXZ) * -0.3f; + this->stateFlags |= ENGOROIWA_RETAIN_ROT_SPEED; + this->stateFlags &= ~ENGOROIWA_IN_WATER; +} + +void EnGoroiwa_MoveDown(EnGoroiwa* this, GlobalContext* globalCtx) { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + func_8002F6D4(globalCtx, &this->actor, 2.0f, this->actor.yawTowardsPlayer, 0.0f, 4); + func_8002F7DC(&GET_PLAYER(globalCtx)->actor, NA_SE_PL_BODY_HIT); + if ((this->actor.home.rot.z & 1) == 1) { + this->collisionDisabledTimer = 50; + } + } else if (EnGoroiwa_MoveDownToNextWaypoint(this, globalCtx)) { + EnGoroiwa_NextWaypoint(this, globalCtx); + EnGoroiwa_SetupRoll(this); + this->stateFlags &= ~ENGOROIWA_RETAIN_ROT_SPEED; + this->actor.speedXZ = 0.0f; + } +} + +void EnGoroiwa_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGoroiwa* this = (EnGoroiwa*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s32 sp30; + + if (!(player->stateFlags1 & 0x300000C0)) { + if (this->collisionDisabledTimer > 0) { + this->collisionDisabledTimer--; + } + this->actionFunc(this, globalCtx); + switch ((this->actor.params >> 10) & 1) { + case 1: + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C); + break; + case 0: + this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &sp30, + &this->actor, &this->actor.world.pos); + break; + } + EnGoroiwa_UpdateRotation(this, globalCtx); + if (this->actor.xzDistToPlayer < 300.0f) { + EnGoroiwa_UpdateCollider(this); + if ((this->stateFlags & ENGOROIWA_ENABLE_AT) && this->collisionDisabledTimer <= 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if ((this->stateFlags & ENGOROIWA_ENABLE_OC) && this->collisionDisabledTimer <= 0) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + } +} + +void EnGoroiwa_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gRollingRockDL); +} diff --git a/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.h b/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.h new file mode 100644 index 000000000..0f36f7a42 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Goroiwa/z_en_goroiwa.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_GOROIWA_H +#define Z_EN_GOROIWA_H + +#include "ultra64.h" +#include "global.h" + +struct EnGoroiwa; + +typedef void (*EnGoroiwaActionFunc)(struct EnGoroiwa*, GlobalContext*); + +typedef struct EnGoroiwa { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnGoroiwaActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItems[1]; + /* 0x01B0 */ Vec3f prevUnitRollAxis; + /* 0x01BC */ f32 prevRollAngleDiff; + /* 0x01C0 */ f32 rollRotSpeed; + /* 0x01C4 */ s16 waitTimer; + /* 0x01C6 */ s16 bounceCount; + /* 0x01C8 */ s16 collisionDisabledTimer; + /* 0x01CA */ s16 endWaypoint; + /* 0x01CC */ s16 currentWaypoint; + /* 0x01CE */ s16 nextWaypoint; + /* 0x01D0 */ s16 pathDirection; + /* 0x01D2 */ u8 isInKokiri; + /* 0x01D3 */ u8 stateFlags; +} EnGoroiwa; // size = 0x01D4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Gs/z_en_gs.c b/soh/src/overlays/actors/ovl_En_Gs/z_en_gs.c new file mode 100644 index 000000000..36486722c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Gs/z_en_gs.c @@ -0,0 +1,616 @@ +/* + * File: z_en_gs.c + * Overlay: ovl_En_Gs + * Description: Gossip Stone + */ + +#include "z_en_gs.h" +#include "objects/object_gs/object_gs.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_25) + +void EnGs_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGs_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGs_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGs_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A4F734(EnGs* this, GlobalContext* globalCtx); +void func_80A4F700(EnGs* this, GlobalContext* globalCtx); + +void func_80A4F77C(EnGs* this); + +const ActorInit En_Gs_InitVars = { + ACTOR_EN_GS, + ACTORCAT_PROP, + FLAGS, + OBJECT_GS, + sizeof(EnGs), + (ActorFunc)EnGs_Init, + (ActorFunc)EnGs_Destroy, + (ActorFunc)EnGs_Update, + (ActorFunc)EnGs_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 21, 48, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0xE), + /* Explosive */ DMG_ENTRY(0, 0xC), + /* Boomerang */ DMG_ENTRY(0, 0xE), + /* Normal arrow */ DMG_ENTRY(0, 0xE), + /* Hammer swing */ DMG_ENTRY(0, 0xD), + /* Hookshot */ DMG_ENTRY(0, 0xE), + /* Kokiri sword */ DMG_ENTRY(0, 0xF), + /* Master sword */ DMG_ENTRY(0, 0xF), + /* Giant's Knife */ DMG_ENTRY(0, 0xF), + /* Fire arrow */ DMG_ENTRY(0, 0xB), + /* Ice arrow */ DMG_ENTRY(0, 0xB), + /* Light arrow */ DMG_ENTRY(0, 0xB), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0xB), + /* Ice magic */ DMG_ENTRY(0, 0xB), + /* Light magic */ DMG_ENTRY(0, 0xB), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void EnGs_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGs* this = (EnGs*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + CollisionCheck_SetInfo2(&thisx->colChkInfo, &sDamageTable, &sColChkInfoInit); + + thisx->targetMode = 6; + this->unk_1D8 = thisx->world.pos; + this->actionFunc = func_80A4F734; + this->unk_1B4[0].x = 1.0f; + this->unk_1B4[0].y = 1.0f; + this->unk_1B4[0].z = 1.0f; + this->unk_1B4[1].x = 1.0f; + this->unk_1B4[1].y = 1.0f; + this->unk_1B4[1].z = 1.0f; +} + +void EnGs_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +s32 func_80A4E3EC(EnGs* this, GlobalContext* globalCtx) { + s32 ret = 2; + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + switch (this->actor.textId) { + case 0x2054: + this->actor.textId = (this->actor.params & 0xFF) + 0x400; + ret = 1; + break; + default: + ret = 0; + break; + } + } + break; + } + return ret; +} + +void func_80A4E470(EnGs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + bREG(15) = 0; + if (this->actor.xzDistToPlayer <= 100.0f) { + bREG(15) = 1; + if (this->unk_19D == 0) { + player->stateFlags2 |= 0x800000; + if (player->stateFlags2 & 0x1000000) { + func_8010BD58(globalCtx, OCARINA_ACTION_FREE_PLAY); + this->unk_19D |= 1; + } + + } else if (this->unk_19D & 1) { + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + if ((globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_SARIAS) || + (globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_EPONAS) || + (globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_LULLABY) || + (globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_SUNS) || + (globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_TIME)) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y + 40.0f, this->actor.world.pos.z, 0, 0, 0, FAIRY_HEAL_TIMED); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BUTTERFRY_TO_FAIRY); + } else if (globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_STORMS) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y + 40.0f, this->actor.world.pos.z, 0, 0, 0, FAIRY_HEAL_BIG); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BUTTERFRY_TO_FAIRY); + } + this->unk_19D = 0; + Flags_SetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F); + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + player->stateFlags2 |= 0x800000; + } + } + } +} + +void func_80A4E648(EnGs* this, GlobalContext* globalCtx) { + s16 sp26; + s16 sp24; + + if (this->unk_19C == 1) { + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_19C = 2; + } else if (this->unk_19C == 2) { + this->unk_19C = func_80A4E3EC(this, globalCtx); + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_19C = 2; + } else { + Actor_GetScreenPos(globalCtx, &this->actor, &sp26, &sp24); + if ((sp26 >= 0) && (sp26 <= SCREEN_WIDTH) && (sp24 >= 0) && (sp24 <= SCREEN_HEIGHT) && (this->unk_19C != 3)) { + if (func_8002F2CC(&this->actor, globalCtx, 40.0f) == 1) { + if (Player_GetMask(globalCtx) == PLAYER_MASK_TRUTH) { + this->actor.textId = 0x2054; + } else { + this->actor.textId = 0x2053; + } + } + } + } +} + +f32 func_80A4E754(EnGs* this, GlobalContext* globalCtx, f32* arg2, f32* arg3, u16* arg4, f32 arg5, f32 arg6, f32 arg7, + s32 arg8, s32 arg9) { + f32 sp2C = Math_SmoothStepToF(arg2, *arg3, arg5, arg6, arg7); + + if (arg9 == 0) { + sp2C = Math_SmoothStepToF(arg2, *arg3, arg5, arg6, arg7); + this->unk_1B4[0].x = 1.0f + (sinf((((*arg4 % arg8) * (1.0f / arg8)) * 360.0f) * (M_PI / 180.0f)) * *arg2); + this->unk_1B4[0].y = 1.0f - (sinf((((*arg4 % arg8) * (1.0f / arg8)) * 360.0f) * (M_PI / 180.0f)) * *arg2); + *arg4 += 1; + } + return sp2C; +} + +void func_80A4E910(EnGs* this, GlobalContext* globalCtx) { + if (this->unk_19F == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_ATTACK); + this->unk_200 = 0; + this->unk_19F = 1; + this->unk_1E8 = 0.5f; + this->unk_1EC = 0.0f; + } else if ((this->unk_19F == 1) && (func_80A4E754(this, globalCtx, &this->unk_1E8, &this->unk_1EC, &this->unk_200, + 0.8f, 0.007f, 0.001f, 7, 0) == 0.0f)) { + if (!Gameplay_InCsMode(globalCtx)) { + Message_StartTextbox(globalCtx, 0x71B1, NULL); + } + this->unk_19C = 0; + this->actionFunc = func_80A4F734; + } +} + +void func_80A4EA08(EnGs* this, GlobalContext* globalCtx) { + if (this->unk_19F == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_ATTACK); + this->unk_1E8 = 0.3f; + this->unk_1EC = 0.0f; + this->unk_200 = 0; + this->unk_19F = 1; + } else if (this->unk_19F == 1) { + this->unk_1A0[0].z = (((this->unk_200 % 8) / 8.0f) * 360.0f) * (0x10000 / 360.0f); + this->unk_1A0[1].z = -this->unk_1A0[0].z; + if (func_80A4E754(this, globalCtx, &this->unk_1E8, &this->unk_1EC, &this->unk_200, 0.8f, 0.005f, 0.001f, 7, + 0) == 0.0f) { + this->unk_19C = 0; + this->actionFunc = func_80A4F734; + } + } +} + +void func_80A4EB3C(EnGs* this, GlobalContext* globalCtx) { + f32 ret; + + if (this->unk_19F == 0) { + this->unk_1E8 = this->unk_1B4[0].y - 1.0f; + this->unk_1EC = -0.8f; + this->unk_19F++; + } else if (this->unk_19F == 1) { + ret = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 1.0f, 0.4f, 0.001f); + this->unk_1B4[0].y = this->unk_1E8 + 1.0f; + if (ret == 0.0f) { + this->unk_200 = 0; + this->unk_19F++; + } + } else if (this->unk_19F == 2) { + this->unk_200++; + if (this->unk_200 >= 100) { + this->unk_19F++; + this->unk_1E8 = this->unk_1B4[0].y - 1.0f; + this->unk_1EC = 0.0f; + } + } else if (this->unk_19F == 3) { + ret = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 1.0f, 0.5f, 0.001f); + this->unk_1B4[0].y = this->unk_1E8 + 1.0f; + if (ret == 0.0f) { + this->unk_1E8 = 0.5f; + this->unk_1EC = 0.0f; + this->unk_200 = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_ATTACK); + this->unk_19F++; + } + } else if (this->unk_19F == 4) { + if (func_80A4E754(this, globalCtx, &this->unk_1E8, &this->unk_1EC, &this->unk_200, 1.0f, 0.03f, 0.001f, 5, 0) == + 0.0f) { + this->unk_19C = 0; + this->actionFunc = func_80A4F734; + } + } +} + +void func_80A4ED34(EnGs* this, GlobalContext* globalCtx) { + static Color_RGBA8 flashRed = { 255, 50, 50, 0 }; + static Color_RGBA8 flashBlue = { 50, 50, 255, 0 }; + static Color_RGBA8 baseWhite = { 255, 255, 255, 0 }; + static Vec3f dustAccel = { 0.0f, -0.3f, 0.0f }; + static Color_RGBA8 dustPrim = { 200, 200, 200, 128 }; + static Color_RGBA8 dustEnv = { 100, 100, 100, 0 }; + static Vec3f bomb2Velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f bomb2Accel = { 0.0f, 0.0f, 0.0f }; + u8 i; + Vec3f dustPos; + Vec3f dustVelocity; + Vec3f bomb2Pos; + + if (this->unk_19F == 0) { + this->unk_200 = 40; + this->unk_19F++; + } + + if (this->unk_19F == 1) { + if ((u32)this->unk_200-- <= 0) { + this->unk_200 = 80; + this->unk_19E |= 4; + this->unk_19F++; + } + } + + if (this->unk_19F == 2) { + this->unk_200--; + Color_RGBA8_Copy(&this->flashColor, &baseWhite); + if ((this->unk_200 < 80) && ((this->unk_200 % 20) < 8)) { + if (this->unk_200 < 20) { + Color_RGBA8_Copy(&this->flashColor, &flashRed); + if ((this->unk_200 % 20) == 7) { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_E, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } else { + Color_RGBA8_Copy(&this->flashColor, &flashBlue); + if ((this->unk_200 % 20) == 7) { + Audio_PlaySoundGeneral(NA_SE_SY_WARNING_COUNT_N, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } + if (this->unk_200 <= 0) { + this->unk_200 = 0; + this->unk_19E &= ~4; + this->unk_19F++; + } + } + + if (this->unk_19F == 3) { + for (i = 0; i < 3; i++) { + dustVelocity.x = Rand_CenteredFloat(15.0f); + dustVelocity.y = Rand_ZeroFloat(-1.0f); + dustVelocity.z = Rand_CenteredFloat(15.0f); + dustPos.x = this->actor.world.pos.x + (dustVelocity.x + dustVelocity.x); + dustPos.y = this->actor.world.pos.y + 7.0f; + dustPos.z = this->actor.world.pos.z + (dustVelocity.z + dustVelocity.z); + func_8002836C(globalCtx, &dustPos, &dustVelocity, &dustAccel, &dustPrim, &dustEnv, + (s16)Rand_ZeroFloat(50.0f) + 200, 40, 15); + } + + func_8002F974(&this->actor, NA_SE_EV_FIRE_PILLAR - SFX_FLAG); + if (this->unk_200++ >= 40) { + this->unk_19E |= 0x10; + this->actor.flags |= ACTOR_FLAG_4; + this->actor.uncullZoneForward = 12000.0f; + + this->actor.gravity = 0.3f; + this->unk_19F++; + } + } + + if (this->unk_19F == 4) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 3); + if (this->actor.bgCheckFlags & 0x18) { + bomb2Pos.x = this->actor.world.pos.x; + bomb2Pos.y = this->actor.world.pos.y; + bomb2Pos.z = this->actor.world.pos.z; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + EffectSsBomb2_SpawnLayered(globalCtx, &bomb2Pos, &bomb2Velocity, &bomb2Accel, 100, 20); + this->unk_200 = 10; + this->unk_19E |= 8; + this->actionFunc = func_80A4F700; + } else { + func_8002F974(&this->actor, NA_SE_EV_STONE_LAUNCH - SFX_FLAG); + } + + Actor_MoveForward(&this->actor); + if (this->actor.yDistToPlayer < -12000.0f) { + Actor_Kill(&this->actor); + } + } +} + +void func_80A4F13C(EnGs* this, GlobalContext* globalCtx) { + f32 tmpf1; + f32 tmpf2; + f32 tmpf3; + s32 tmp; + s16 tmp2 = this->unk_1A0[0].y; + + if (this->unk_19F == 0) { + this->unk_1E8 = 0.0f; + this->unk_1EC = 90.0f; + this->unk_1F0 = 0.1f; + this->unk_1F4 = 2.0f; + this->unk_1F8 = 0; + this->unk_19F = 1; + } + if (this->unk_19F == 1) { + Math_SmoothStepToF(&this->unk_1F0, this->unk_1F4, 1.0f, 0.1f, 0.001f); + tmpf1 = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 1.0f, this->unk_1F0, 0.001f); + this->unk_1A0[0].y += (s32)(this->unk_1E8 * (0x10000 / 360.0f)); + if (tmpf1 == 0.0f) { + this->unk_200 = 0; + this->unk_19F = 2; + } + } + if (this->unk_19F == 2) { + this->unk_1A0[0].y += (s32)(this->unk_1E8 * (0x10000 / 360.0f)); + if (this->unk_200++ > 40) { + this->unk_1E8 = this->unk_1B4[0].y - 1.0f; + this->unk_1EC = 1.5f; + this->unk_1F0 = this->unk_1B4[1].y - 1.0f; + this->unk_1F4 = -0.3f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_STONE_GROW_UP); + this->unk_19F = 3; + } + } + if (this->unk_19F == 3) { + this->unk_1A0[0].y += 0x4000; + tmpf1 = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 0.8f, 0.2f, 0.001f); + Math_SmoothStepToF(&this->unk_1F0, this->unk_1F4, 0.8f, 0.2f, 0.001f); + this->unk_1B4[0].x = this->unk_1F0 + 1.0f; + this->unk_1B4[0].y = this->unk_1E8 + 1.0f; + if (tmpf1 == 0.0f) { + this->unk_1E8 = 2.0f * M_PI / 9.0000002; + this->unk_1EC = M_PI / 9.0000002; + this->unk_19F = 4; + } + } + if (this->unk_19F == 4) { + tmpf1 = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 0.8f, 16384.0f, 3640.0f); + this->unk_1A0[0].y += (s16)this->unk_1E8; + if (tmpf1 == 0.0f) { + + tmp = this->unk_1A0[0].y; + if (tmp > 0) { + tmp += 0xFFFF0000; + } + this->unk_1E8 = tmp; + + this->unk_1EC = 0.0f; + this->unk_19F = 5; + } + } + if (this->unk_19F == 5) { + tmp = this->unk_1A0[0].y; + if (tmp > 0) { + tmp += 0xFFFF0001; + } + this->unk_1E8 = tmp; + tmpf1 = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 0.8f, 3640.0f, 0.001f); + this->unk_1A0[0].y = this->unk_1E8; + if (tmpf1 == 0.0f) { + this->unk_1E8 = this->unk_1B4[0].y - 1.0f; + this->unk_1EC = 0; + this->unk_1A0[0].y = 0; + this->unk_200 = 0; + this->unk_1F0 = this->unk_1B4[0].x - 1.0f; + this->unk_1F4 = 0; + this->unk_1F8 = 0.5f; + this->unk_1FC = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_ATTACK); + this->unk_19F = 6; + } + } + if (this->unk_19F == 6) { + tmpf1 = Math_SmoothStepToF(&this->unk_1E8, this->unk_1EC, 0.8f, 0.1f, 0.001f); + tmpf2 = Math_SmoothStepToF(&this->unk_1F0, this->unk_1F4, 0.8f, 0.1f, 0.001f); + tmpf3 = Math_SmoothStepToF(&this->unk_1F8, this->unk_1FC, 0.8f, 0.02f, 0.001f); + this->unk_1B4[0].x = this->unk_1F0 + 1.0f; + this->unk_1B4[0].y = this->unk_1E8 + 1.0f; + this->unk_1B4[0].x += sinf((((this->unk_200 % 10) * 0.1f) * 360.0f) * (M_PI / 180.0f)) * this->unk_1F8; + this->unk_1B4[0].y += sinf((((this->unk_200 % 10) * 0.1f) * 360.0f) * (M_PI / 180.0f)) * this->unk_1F8; + this->unk_200++; + if ((tmpf1 == 0.0f) && (tmpf2 == 0.0f) && (tmpf3 == 0.0f)) { + this->unk_19C = 0; + this->actionFunc = func_80A4F734; + } + } + if ((u16)this->unk_1A0[0].y < (u16)tmp2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_STONE_ROLLING); + } +} + +void func_80A4F700(EnGs* this, GlobalContext* globalCtx) { + if (this->unk_200-- <= 0) { + Actor_Kill(&this->actor); + } +} + +void func_80A4F734(EnGs* this, GlobalContext* globalCtx) { + if (!Flags_GetSwitch(globalCtx, (this->actor.params >> 8) & 0x3F)) { + func_80A4E470(this, globalCtx); + } +} + +void func_80A4F77C(EnGs* this) { + static Vec3s D_80A4FE34 = { 0, 0, 0 }; + static Vec3f D_80A4FE3C = { 1.0f, 1.0f, 1.0f }; + s32 i; + + for (i = 0; i < 3; i++) { + this->unk_1A0[i] = D_80A4FE34; + } + + for (i = 0; i < 2; i++) { + this->unk_1B4[i] = D_80A4FE3C; + } + + this->unk_19C = 3; +} + +void EnGs_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnGs* this = (EnGs*)thisx; + + Actor_SetFocus(&this->actor, 23.0f); + if (!(this->unk_19E & 0x10)) { + if (this->collider.base.acFlags & AC_HIT) { + this->unk_19F = 0; + this->collider.base.acFlags &= ~AC_HIT; + + switch (this->actor.colChkInfo.damageEffect) { + case 15: + this->unk_19E |= 1; + func_80A4F77C(this); + this->actionFunc = func_80A4E910; + break; + case 14: + this->unk_19E |= 1; + func_80A4F77C(this); + this->actionFunc = func_80A4EA08; + break; + case 13: + this->unk_19E |= 1; + func_80A4F77C(this); + this->actionFunc = func_80A4EB3C; + break; + case 12: + this->unk_19E |= 2; + func_80A4F77C(this); + this->actionFunc = func_80A4ED34; + break; + case 11: + this->unk_19E |= 1; + func_80A4F77C(this); + this->actionFunc = func_80A4F13C; + break; + } + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + this->actionFunc(this, globalCtx); + func_80A4E648(this, globalCtx); +} + +void EnGs_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnGs* this = (EnGs*)thisx; + s32 tmp; + u32 frames; + + if (!(this->unk_19E & 8)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_gs.c", 1046); + + frames = globalCtx->gameplayFrames; + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Push(); + if (this->unk_19E & 1) { + Matrix_RotateY(this->unk_1A0[0].y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(this->unk_1A0[0].x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(this->unk_1A0[0].z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(this->unk_1B4[0].x, this->unk_1B4[0].y, this->unk_1B4[0].z, MTXMODE_APPLY); + Matrix_RotateY(this->unk_1A0[1].y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(this->unk_1A0[1].x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(this->unk_1A0[1].z * (M_PI / 0x8000), MTXMODE_APPLY); + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_gs.c", 1064), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGossipStoneMaterialDL); + + if (this->unk_19E & 4) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->flashColor.r, this->flashColor.g, this->flashColor.b, + this->flashColor.a); + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + } + + gSPDisplayList(POLY_OPA_DISP++, gGossipStoneDL); + gSPDisplayList(POLY_OPA_DISP++, gGossipStoneSquishedDL); + + Matrix_Pop(); + if (this->unk_19E & 2) { + func_80093D84(globalCtx->state.gfxCtx); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(0.05f, -0.05f, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_gs.c", 1087), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, -frames * 0x14, 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 128, 128, 255, 255, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_gs.c", 1101); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Gs/z_en_gs.h b/soh/src/overlays/actors/ovl_En_Gs/z_en_gs.h new file mode 100644 index 000000000..cc95e3ee0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Gs/z_en_gs.h @@ -0,0 +1,34 @@ +#ifndef Z_EN_GS_H +#define Z_EN_GS_H + +#include "ultra64.h" +#include "global.h" + +struct EnGs; + +typedef void (*EnGsActionFunc)(struct EnGs*, GlobalContext*); + +typedef struct EnGs { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ EnGsActionFunc actionFunc; + /* 0x019C */ u8 unk_19C; + /* 0x019D */ u8 unk_19D; + /* 0x019E */ u8 unk_19E; + /* 0x019F */ u8 unk_19F; + /* 0x01A0 */ Vec3s unk_1A0[3]; + /* 0x01B4 */ Vec3f unk_1B4[2]; + /* 0x01CC */ char unk_1CC[0xC]; + /* 0x01D8 */ Vec3f unk_1D8; + /* 0x01E4 */ Color_RGBA8 flashColor; + /* 0x01E8 */ f32 unk_1E8; + /* 0x01EC */ f32 unk_1EC; + /* 0x01F0 */ f32 unk_1F0; + /* 0x01F4 */ f32 unk_1F4; + /* 0x01F8 */ f32 unk_1F8; + /* 0x01FC */ f32 unk_1FC; + /* 0x0200 */ u16 unk_200; + /* 0x0202 */ char unk_202[0x6]; +} EnGs; // size = 0x0208 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c b/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c new file mode 100644 index 000000000..cc85b8c53 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.c @@ -0,0 +1,235 @@ +/* + * File: z_en_guest.c + * Overlay: ovl_En_Guest + * Description: Happy Mask Shop Customer + */ + +#include "z_en_guest.h" +#include "objects/object_os_anime/object_os_anime.h" +#include "objects/object_boj/object_boj.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnGuest_Init(Actor* thisx, GlobalContext* globalCtx); +void EnGuest_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnGuest_Update(Actor* thisx, GlobalContext* globalCtx); +void EnGuest_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A50518(EnGuest* this, GlobalContext* globalCtx); +void func_80A5057C(EnGuest* this, GlobalContext* globalCtx); +void func_80A505CC(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Guest_InitVars = { + ACTOR_EN_GUEST, + ACTORCAT_NPC, + FLAGS, + OBJECT_BOJ, + sizeof(EnGuest), + (ActorFunc)EnGuest_Init, + (ActorFunc)EnGuest_Destroy, + (ActorFunc)EnGuest_Update, + NULL, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0x00000000, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, 0x00, 0x00, 0x01 }, + { 10, 60, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP), +}; + +void EnGuest_Init(Actor* thisx, GlobalContext* globalCtx) { + EnGuest* this = (EnGuest*)thisx; + + if (gSaveContext.infTable[7] & 0x40) { + Actor_Kill(&this->actor); + } else { + this->osAnimeBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_OS_ANIME); + if (this->osAnimeBankIndex < 0) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "No such bank!!" + osSyncPrintf("%s[%d] : バンクが無いよ!!\n", "../z_en_guest.c", 129); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_guest.c", 132); + } + } +} + +void EnGuest_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnGuest* this = (EnGuest*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnGuest_Update(Actor* thisx, GlobalContext* globalCtx) { + EnGuest* this = (EnGuest*)thisx; + s32 pad; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->osAnimeBankIndex)) { + this->actor.flags &= ~ACTOR_FLAG_4; + Actor_ProcessInitChain(&this->actor, sInitChain); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_boj_Skel_0000F0, NULL, this->jointTable, this->morphTable, 16); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->osAnimeBankIndex].segment); + Animation_Change(&this->skelAnime, &gObjOsAnim_42AC, 1.0f, 0.0f, Animation_GetLastFrame(&gObjOsAnim_42AC), + ANIMMODE_LOOP, 0.0f); + + this->actor.draw = EnGuest_Draw; + this->actor.update = func_80A505CC; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + Actor_SetFocus(&this->actor, 60.0f); + + this->unk_30E = 0; + this->unk_30D = 0; + this->unk_2CA = 0; + this->actor.textId = 0x700D; + this->actionFunc = func_80A50518; + } +} + +void func_80A5046C(EnGuest* this) { + if (this->unk_30D == 0) { + if (this->unk_2CA != 0) { + this->unk_2CA--; + } else { + this->unk_30D = 1; + } + } else { + if (this->unk_2CA != 0) { + this->unk_2CA--; + } else { + this->unk_30E += 1; + if (this->unk_30E >= 3) { + this->unk_30E = 0; + this->unk_30D = 0; + this->unk_2CA = (s32)Rand_ZeroFloat(60.0f) + 20; + } else { + this->unk_2CA = 1; + } + } + } +} + +void func_80A50518(EnGuest* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_80A5057C; + } else if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } +} + +void func_80A5057C(EnGuest* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = func_80A50518; + } +} + +void func_80A505CC(Actor* thisx, GlobalContext* globalCtx) { + EnGuest* this = (EnGuest*)thisx; + s32 pad; + Player* player; + + player = GET_PLAYER(globalCtx); + this->unk_2C8++; + + func_80A5046C(this); + this->actionFunc(this, globalCtx); + + this->unk_2A0.unk_18 = player->actor.world.pos; + if (LINK_IS_ADULT) { + this->unk_2A0.unk_14 = 10.0f; + } else { + this->unk_2A0.unk_14 = 20.0f; + } + func_80034A14(&this->actor, &this->unk_2A0, 6, 2); + + func_80034F54(globalCtx, this->unk_2CC, this->unk_2EC, 16); + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->osAnimeBankIndex].segment); + + SkelAnime_Update(&this->skelAnime); + Actor_SetFocus(&this->actor, 60.0f); + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +Gfx* func_80A50708(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b, u8 a) { + Gfx* dlist; + + dlist = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + gDPSetEnvColor(dlist, r, g, b, a); + gSPEndDisplayList(dlist + 1); + + return dlist; +} + +s32 EnGuest_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnGuest* this = (EnGuest*)thisx; + Vec3s sp3C; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_guest.c", 352); + + if (limbIndex == 15) { + *dList = object_boj_DL_0059B0; + Matrix_Translate(1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + sp3C = this->unk_2A0.unk_08; + Matrix_RotateX((sp3C.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((sp3C.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + + if (limbIndex == 8) { + sp3C = this->unk_2A0.unk_0E; + Matrix_RotateX((-sp3C.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((sp3C.x / 32768.0f) * M_PI, MTXMODE_APPLY); + } + + if (limbIndex == 8 || limbIndex == 9 || limbIndex == 12) { + rot->y += Math_SinS(this->unk_2CC[limbIndex]) * 200.0f; + rot->z += Math_CosS(this->unk_2EC[limbIndex]) * 200.0f; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_guest.c", 388); + + return false; +} + +void EnGuest_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* D_80A50BA4[] = { + object_boj_Tex_0005FC, + object_boj_Tex_0006FC, + object_boj_Tex_0007FC, + }; + EnGuest* this = (EnGuest*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_guest.c", 404); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A50708(globalCtx->state.gfxCtx, 255, 255, 255, 255)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A50708(globalCtx->state.gfxCtx, 160, 60, 220, 255)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(D_80A50BA4[this->unk_30E])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnGuest_OverrideLimbDraw, NULL, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_guest.c", 421); +} diff --git a/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.h b/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.h new file mode 100644 index 000000000..a86caa613 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Guest/z_en_guest.h @@ -0,0 +1,28 @@ +#ifndef Z_EN_GUEST_H +#define Z_EN_GUEST_H + +#include "ultra64.h" +#include "global.h" + +struct EnGuest; + +typedef void (*EnGuestActionFunc)(struct EnGuest* this, GlobalContext* globalCtx); + +typedef struct EnGuest { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s morphTable[16]; + /* 0x0250 */ EnGuestActionFunc actionFunc; + /* 0x0254 */ ColliderCylinder collider; + /* 0x02A0 */ struct_80034A14_arg1 unk_2A0; + /* 0x02C8 */ s16 unk_2C8; + /* 0x02CA */ s16 unk_2CA; + /* 0x02CC */ s16 unk_2CC[16]; + /* 0x02EC */ s16 unk_2EC[16]; + /* 0x030C */ s8 osAnimeBankIndex; + /* 0x030D */ u8 unk_30D; + /* 0x030E */ u8 unk_30E; +} EnGuest; // size = 0x0310 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Hata/z_en_hata.c b/soh/src/overlays/actors/ovl_En_Hata/z_en_hata.c new file mode 100644 index 000000000..00042e7d3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hata/z_en_hata.c @@ -0,0 +1,146 @@ +/* + * File: z_en_hata.c + * Overlay: ovl_En_Hata + * Description: Wooden post with red cloth + */ + +#include "z_en_hata.h" +#include "objects/object_hata/object_hata.h" + +#define FLAGS 0 + +void EnHata_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHata_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHata_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHata_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Hata_InitVars = { + ACTOR_EN_HATA, + ACTORCAT_PROP, + FLAGS, + OBJECT_HATA, + sizeof(EnHata), + (ActorFunc)EnHata_Init, + (ActorFunc)EnHata_Destroy, + (ActorFunc)EnHata_Update, + (ActorFunc)EnHata_Draw, + NULL, +}; + +// Unused Collider and CollisionCheck data +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000080, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 16, 246, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +void EnHata_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHata* this = (EnHata*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + f32 frameCount = Animation_GetLastFrame(&gFlagpoleFlapAnim); + + Actor_SetScale(&this->dyna.actor, 1.0f / 75.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gFlagpoleSkel, &gFlagpoleFlapAnim, NULL, NULL, 0); + Animation_Change(&this->skelAnime, &gFlagpoleFlapAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&gFlagpoleCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->dyna.actor.uncullZoneScale = 500.0f; + this->dyna.actor.uncullZoneDownward = 550.0f; + this->dyna.actor.uncullZoneForward = 2200.0f; + this->invScale = 6; + this->maxStep = 1000; + this->minStep = 1; + this->unk_278 = Rand_ZeroOne() * 0xFFFF; +} + +void EnHata_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHata* this = (EnHata*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void EnHata_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnHata* this = (EnHata*)thisx; + s32 pitch; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f windVec; + f32 sin; + + SkelAnime_Update(&this->skelAnime); + // Rotate to hang down by default + this->limbs[FLAGPOLE_LIMB_FLAG_1_BASE].y = this->limbs[FLAGPOLE_LIMB_FLAG_2_BASE].y = -0x4000; + windVec.x = globalCtx->envCtx.windDirection.x; + windVec.y = globalCtx->envCtx.windDirection.y; + windVec.z = globalCtx->envCtx.windDirection.z; + + if (globalCtx->envCtx.windSpeed > 255.0f) { + globalCtx->envCtx.windSpeed = 255.0f; + } + + if (globalCtx->envCtx.windSpeed < 0.0f) { + globalCtx->envCtx.windSpeed = 0.0f; + } + + if (Rand_ZeroOne() > 0.5f) { + this->unk_278 += 6000; + } else { + this->unk_278 += 3000; + } + + // Mimic varying wind gusts + sin = Math_SinS(this->unk_278) * 80.0f; + pitch = -Math_Vec3f_Pitch(&zeroVec, &windVec); + pitch = ((s32)((15000 - pitch) * (1.0f - (globalCtx->envCtx.windSpeed / (255.0f - sin))))) + pitch; + Math_SmoothStepToS(&this->limbs[FLAGPOLE_LIMB_FLAG_1_HOIST_END_BASE].y, pitch, this->invScale, this->maxStep, + this->minStep); + this->limbs[FLAGPOLE_LIMB_FLAG_2_HOIST_END_BASE].y = this->limbs[FLAGPOLE_LIMB_FLAG_1_HOIST_END_BASE].y; + this->limbs[FLAGPOLE_LIMB_FLAG_1_HOIST_END_BASE].z = -Math_Vec3f_Yaw(&zeroVec, &windVec); + this->limbs[FLAGPOLE_LIMB_FLAG_2_HOIST_END_BASE].z = this->limbs[FLAGPOLE_LIMB_FLAG_1_HOIST_END_BASE].z; + this->skelAnime.playSpeed = (Rand_ZeroFloat(1.25f) + 2.75f) * (globalCtx->envCtx.windSpeed / 255.0f); +} + +s32 EnHata_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnHata* this = (EnHata*)thisx; + Vec3s* limbs; + + if (limbIndex == FLAGPOLE_LIMB_FLAG_2_BASE || limbIndex == FLAGPOLE_LIMB_FLAG_1_BASE || + limbIndex == FLAGPOLE_LIMB_FLAG_2_HOIST_END_BASE || limbIndex == FLAGPOLE_LIMB_FLAG_1_HOIST_END_BASE) { + limbs = this->limbs; + rot->x += limbs[limbIndex].x; + rot->y += limbs[limbIndex].y; + rot->z += limbs[limbIndex].z; + } + return false; +} + +void EnHata_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { +} + +void EnHata_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHata* this = (EnHata*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + Matrix_Scale(1.0f, 1.1f, 1.0f, MTXMODE_APPLY); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnHata_OverrideLimbDraw, + EnHata_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Hata/z_en_hata.h b/soh/src/overlays/actors/ovl_En_Hata/z_en_hata.h new file mode 100644 index 000000000..f208b1e01 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hata/z_en_hata.h @@ -0,0 +1,44 @@ +#ifndef Z_EN_HATA_H +#define Z_EN_HATA_H + +#include "ultra64.h" +#include "global.h" + + +typedef enum { + /* 0x00 */ FLAGPOLE_LIMB_NONE, + /* 0x01 */ FLAGPOLE_LIMB_POLE_BASE, + /* 0x02 */ FLAGPOLE_LIMB_POLE, + /* 0x03 */ FLAGPOLE_LIMB_FLAG_1_BASE, + /* 0x04 */ FLAGPOLE_LIMB_FLAG_1_HOIST_END_BASE, + /* 0x05 */ FLAGPOLE_LIMB_FLAG_1_HOIST_MID_BASE, + /* 0x06 */ FLAGPOLE_LIMB_FLAG_1_FLY_MID_BASE, + /* 0x07 */ FLAGPOLE_LIMB_FLAG_1_FLY_END_BASE, + /* 0x08 */ FLAGPOLE_LIMB_FLAG_1_FLY_END, + /* 0x09 */ FLAGPOLE_LIMB_FLAG_1_FLY_MID, + /* 0x0A */ FLAGPOLE_LIMB_FLAG_1_HOIST_MID, + /* 0x0B */ FLAGPOLE_LIMB_FLAG_1_HOIST_END, + /* 0x0C */ FLAGPOLE_LIMB_FLAG_2_BASE, + /* 0x0D */ FLAGPOLE_LIMB_FLAG_2_HOIST_END_BASE, + /* 0x0E */ FLAGPOLE_LIMB_FLAG_2_HOIST_MID_BASE, + /* 0x0F */ FLAGPOLE_LIMB_FLAG_2_FLY_MID_BASE, + /* 0x10 */ FLAGPOLE_LIMB_FLAG_2_FLY_END_BASE, + /* 0x11 */ FLAGPOLE_LIMB_FLAG_2_FLY_END, + /* 0x12 */ FLAGPOLE_LIMB_FLAG_2_FLY_MID, + /* 0x13 */ FLAGPOLE_LIMB_FLAG_2_HOIST_MID, + /* 0x14 */ FLAGPOLE_LIMB_FLAG_2_HOIST_END, + /* 0x15 */ FLAGPOLE_LIMB_MAX +} EnHataLimb; + +typedef struct { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ SkelAnime skelAnime; + /* 0x01A8 */ ColliderCylinder collider; // Unused, but indicated by the form of the unused data + /* 0x01F4 */ Vec3s limbs[FLAGPOLE_LIMB_MAX]; + /* 0x0272 */ s16 invScale; + /* 0x0274 */ s16 maxStep; + /* 0x0276 */ s16 minStep; + /* 0x0278 */ s16 unk_278; +} EnHata; // size = 0x027C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c new file mode 100644 index 000000000..e0bad7df3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c @@ -0,0 +1,501 @@ +/* + * File: z_en_heishi1.c + * Overlay: ovl_En_Heishi1 + * Description: Courtyard Guards + */ + +#include "z_en_heishi1.h" +#include "objects/object_sd/object_sd.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnHeishi1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi1_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi1_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHeishi1_SetupWait(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_SetupWalk(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_SetupMoveToLink(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_SetupTurnTowardLink(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_SetupKick(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_SetupWaitNight(EnHeishi1* this, GlobalContext* globalCtx); + +void EnHeishi1_Wait(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_Walk(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_MoveToLink(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_TurnTowardLink(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_Kick(EnHeishi1* this, GlobalContext* globalCtx); +void EnHeishi1_WaitNight(EnHeishi1* this, GlobalContext* globalCtx); + +static s32 sPlayerIsCaught = false; + +const ActorInit En_Heishi1_InitVars = { + 0, + ACTORCAT_NPC, + FLAGS, + OBJECT_SD, + sizeof(EnHeishi1), + (ActorFunc)EnHeishi1_Init, + (ActorFunc)EnHeishi1_Destroy, + (ActorFunc)EnHeishi1_Update, + (ActorFunc)EnHeishi1_Draw, + NULL, +}; + +static f32 sAnimParamsInit[][8] = { + { 1.0f, -10.0f, 3.0f, 0.5f, 1000.0f, 200.0f, 0.3f, 1000.0f }, + { 3.0f, -3.0f, 6.0f, 0.8f, 2000.0f, 400.0f, 0.5f, 2000.0f }, + { 1.0f, -10.0f, 3.0f, 0.5f, 1000.0f, 200.0f, 0.3f, 1000.0f }, + { 3.0f, -3.0f, 6.0f, 0.8f, 2000.0f, 400.0f, 0.5f, 2000.0f }, +}; + +static s16 sBaseHeadTimers[] = { 20, 10, 20, 10, 13, 0 }; + +static Vec3f sRupeePositions[] = { + { 0.0f, 0.0f, 90.0f }, { -55.0f, 0.0f, 90.0f }, { -55.0f, 0.0f, 30.0f }, { -55.0f, 0.0f, -30.0f }, + { 0.0f, 0.0f, -30.0f }, { 55.0f, 0.0f, -30.0f }, { 55.0f, 0.0f, 30.0f }, { 55.0f, 0.0f, 90.0f }, +}; + +static s32 sCamDataIdxs[] = { + 7, 7, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 4, 4, 5, 6, +}; + +static s16 sWaypoints[] = { 0, 4, 1, 5, 2, 6, 3, 7 }; + +void EnHeishi1_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHeishi1* this = (EnHeishi1*)thisx; + Vec3f rupeePos; + s32 i; + + Actor_SetScale(&this->actor, 0.01f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gEnHeishiSkel, &gEnHeishiIdleAnim, this->jointTable, this->morphTable, + 17); + + this->type = (this->actor.params >> 8) & 0xFF; + this->path = this->actor.params & 0xFF; + + for (i = 0; i < ARRAY_COUNT(sAnimParamsInit[0]); i++) { + this->animParams[i] = sAnimParamsInit[this->type][i]; + } + + // "type" + osSyncPrintf(VT_FGCOL(GREEN) " 種類☆☆☆☆☆☆☆☆☆☆☆☆☆ %d\n" VT_RST, this->type); + // "path data" + osSyncPrintf(VT_FGCOL(YELLOW) " れえるでぇたぁ☆☆☆☆☆☆☆☆ %d\n" VT_RST, this->path); + osSyncPrintf(VT_FGCOL(PURPLE) " anime_frame_speed ☆☆☆☆☆☆ %f\n" VT_RST, this->animSpeed); + // "interpolation frame" + osSyncPrintf(VT_FGCOL(PURPLE) " 補間フレーム☆☆☆☆☆☆☆☆☆ %f\n" VT_RST, this->transitionRate); + // "targeted movement speed value between points" + osSyncPrintf(VT_FGCOL(PURPLE) " point間の移動スピード目標値 ☆ %f\n" VT_RST, this->moveSpeedTarget); + // "maximum movement speed value between points" + osSyncPrintf(VT_FGCOL(PURPLE) " point間の移動スピード最大 ☆☆ %f\n" VT_RST, this->moveSpeedMax); + // "(body) targeted turning angle speed value" + osSyncPrintf(VT_FGCOL(PURPLE) " (体)反転アングルスピード目標値 %f\n" VT_RST, this->bodyTurnSpeedTarget); + // "(body) maximum turning angle speed" + osSyncPrintf(VT_FGCOL(PURPLE) " (体)反転アングルスピード最大☆ %f\n" VT_RST, this->bodyTurnSpeedMax); + // "(head) targeted turning angle speed value" + osSyncPrintf(VT_FGCOL(PURPLE) " (頭)反転アングルスピード加算値 %f\n" VT_RST, this->headTurnSpeedScale); + // "(head) maximum turning angle speed" + osSyncPrintf(VT_FGCOL(PURPLE) " (頭)反転アングルスピード最大☆ %f\n" VT_RST, this->headTurnSpeedMax); + osSyncPrintf(VT_FGCOL(GREEN) " 今時間 %d\n" VT_RST, ((void)0, gSaveContext.dayTime)); // "current time" + osSyncPrintf(VT_FGCOL(YELLOW) " チェック時間 %d\n" VT_RST, 0xBAAA); // "check time" + osSyncPrintf("\n\n"); + + if (this->path == 3) { + for (i = 0; i < ARRAY_COUNT(sRupeePositions); i++) { + rupeePos = sRupeePositions[i]; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_RUPPY, rupeePos.x, rupeePos.y, + rupeePos.z, 0, 0, 0, 3); + } + } + + if (this->type != 5) { + if (((gSaveContext.dayTime < 0xB888) || IS_DAY) && !(gSaveContext.eventChkInf[8] & 1)) { + this->actionFunc = EnHeishi1_SetupWalk; + } else { + Actor_Kill(&this->actor); + } + } else { + if ((gSaveContext.dayTime >= 0xB889) || !IS_DAY || (gSaveContext.eventChkInf[8] & 1)) { + this->actionFunc = EnHeishi1_SetupWaitNight; + } else { + Actor_Kill(&this->actor); + } + } +} + +void EnHeishi1_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnHeishi1_SetupWalk(EnHeishi1* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiWalkAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiWalkAnim, this->animSpeed, 0.0f, (s16)frameCount, ANIMMODE_LOOP, + this->transitionRate); + this->bodyTurnSpeed = 0.0f; + this->moveSpeed = 0.0f; + this->headDirection = Rand_ZeroFloat(1.99f); + this->actionFunc = EnHeishi1_Walk; +} + +void EnHeishi1_Walk(EnHeishi1* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* pointPos; + f32 pathDiffX; + f32 pathDiffZ; + s16 randOffset; + + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 1.0f) || Animation_OnFrame(&this->skelAnime, 17.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KNIGHT_WALK); + } + + if (!sPlayerIsCaught) { + path = &globalCtx->setupPathList[this->path]; + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos += this->waypoint; + + Math_ApproachF(&this->actor.world.pos.x, pointPos->x, 1.0f, this->moveSpeed); + Math_ApproachF(&this->actor.world.pos.z, pointPos->z, 1.0f, this->moveSpeed); + + Math_ApproachF(&this->moveSpeed, this->moveSpeedTarget, 1.0f, this->moveSpeedMax); + + pathDiffX = pointPos->x - this->actor.world.pos.x; + pathDiffZ = pointPos->z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.shape.rot.y, (Math_FAtan2F(pathDiffX, pathDiffZ) * (0x8000 / M_PI)), 3, + this->bodyTurnSpeed, 0); + + Math_ApproachF(&this->bodyTurnSpeed, this->bodyTurnSpeedTarget, 1.0f, this->bodyTurnSpeedMax); + + if (this->headTimer == 0) { + this->headDirection++; + this->headAngleTarget = 0x2000; + // if headDirection is odd, face 45 degrees left + if ((this->headDirection & 1) != 0) { + this->headAngleTarget *= -1; + } + randOffset = Rand_ZeroFloat(30.0f); + this->headTimer = sBaseHeadTimers[this->type] + randOffset; + } + + Math_ApproachF(&this->headAngle, this->headAngleTarget, this->headTurnSpeedScale, this->headTurnSpeedMax); + + if ((this->path == BREG(1)) && (BREG(0) != 0)) { + osSyncPrintf(VT_FGCOL(RED) " 種類 %d\n" VT_RST, this->path); + osSyncPrintf(VT_FGCOL(RED) " ぱす %d\n" VT_RST, this->waypoint); + osSyncPrintf(VT_FGCOL(RED) " 反転 %d\n" VT_RST, this->bodyTurnSpeed); + osSyncPrintf(VT_FGCOL(RED) " 時間 %d\n" VT_RST, this->waypointTimer); + osSyncPrintf(VT_FGCOL(RED) " 点座 %d\n" VT_RST, path->count); + osSyncPrintf("\n\n"); + } + + // when 20 units away from a middle waypoint, decide whether or not to skip it + if ((fabsf(pathDiffX) < 20.0f) && (fabsf(pathDiffZ) < 20.0f)) { + if (this->waypointTimer == 0) { + if (this->type >= 2) { + if ((this->waypoint >= 4) && (Rand_ZeroFloat(1.99f) > 1.0f)) { + if (this->waypoint == 7) { + this->waypoint = 0; + } + if (this->waypoint >= 4) { + this->waypoint -= 3; + } + this->waypointTimer = 5; + return; + } + } + this->actionFunc = EnHeishi1_SetupWait; + } + } + } +} + +void EnHeishi1_SetupMoveToLink(EnHeishi1* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiWalkAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiWalkAnim, 3.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -3.0f); + this->bodyTurnSpeed = 0.0f; + this->moveSpeed = 0.0f; + Message_StartTextbox(globalCtx, 0x702D, &this->actor); + Interface_SetDoAction(globalCtx, DO_ACTION_STOP); + this->actionFunc = EnHeishi1_MoveToLink; +} + +void EnHeishi1_MoveToLink(EnHeishi1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.world.pos.x, player->actor.world.pos.x, 1.0f, this->moveSpeed); + Math_ApproachF(&this->actor.world.pos.z, player->actor.world.pos.z, 1.0f, this->moveSpeed); + Math_ApproachF(&this->moveSpeed, 6.0f, 1.0f, 0.4f); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, this->bodyTurnSpeed, 0); + Math_ApproachF(&this->bodyTurnSpeed, 3000.0f, 1.0f, 300.0f); + Math_ApproachZeroF(&this->headAngle, 0.5f, 2000.0f); + + if (this->actor.xzDistToPlayer < 70.0f) { + this->actionFunc = EnHeishi1_SetupTurnTowardLink; + } +} + +void EnHeishi1_SetupWait(EnHeishi1* this, GlobalContext* globalCtx) { + s16 rand; + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, this->animSpeed, 0.0f, (s16)frameCount, ANIMMODE_LOOP, + this->transitionRate); + this->headBehaviorDecided = false; + this->headDirection = Rand_ZeroFloat(1.99f); + rand = Rand_ZeroFloat(50.0f); + this->waitTimer = rand + 50; + this->actionFunc = EnHeishi1_Wait; +} + +void EnHeishi1_Wait(EnHeishi1* this, GlobalContext* globalCtx) { + s16 randOffset; + s32 i; + + SkelAnime_Update(&this->skelAnime); + if (!sPlayerIsCaught) { + switch (this->headBehaviorDecided) { + case false: + this->headDirection++; + // if headDirection is odd, face 52 degrees left + this->headAngleTarget = (this->headDirection & 1) ? 0x2500 : -0x2500; + randOffset = Rand_ZeroFloat(30.0f); + this->headTimer = sBaseHeadTimers[this->type] + randOffset; + this->headBehaviorDecided = true; + break; + case true: + if (this->headTimer == 0) { + if (this->waitTimer == 0) { + if ((this->type == 0) || (this->type == 1)) { + this->waypoint++; + if (this->waypoint >= 4) { + this->waypoint = 0; + } + } else { + // waypoints are defined with corners as 0-3 and middle points as 4-7 + // to choose the next waypoint, the order "04152637" is hardcoded in an array + for (i = 0; i < ARRAY_COUNT(sWaypoints); i++) { + if (this->waypoint == sWaypoints[i]) { + i++; + if (i >= ARRAY_COUNT(sWaypoints)) { + i = 0; + } + this->waypoint = sWaypoints[i]; + break; + } + } + this->waypointTimer = 5; + } + this->actionFunc = EnHeishi1_SetupWalk; + } else { + this->headBehaviorDecided = false; + } + } + break; + } + Math_ApproachF(&this->headAngle, this->headAngleTarget, this->headTurnSpeedScale, + this->headTurnSpeedMax + this->headTurnSpeedMax); + + if ((this->path == BREG(1)) && (BREG(0) != 0)) { + osSyncPrintf(VT_FGCOL(GREEN) " 種類 %d\n" VT_RST, this->path); + osSyncPrintf(VT_FGCOL(GREEN) " ぱす %d\n" VT_RST, this->waypoint); + osSyncPrintf(VT_FGCOL(GREEN) " 反転 %d\n" VT_RST, this->bodyTurnSpeed); + osSyncPrintf(VT_FGCOL(GREEN) " 時間 %d\n" VT_RST, this->waypointTimer); + osSyncPrintf("\n\n"); + } + } +} + +void EnHeishi1_SetupTurnTowardLink(EnHeishi1* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->kickTimer = 30; + this->actionFunc = EnHeishi1_TurnTowardLink; +} + +void EnHeishi1_TurnTowardLink(EnHeishi1* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->type != 5) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, this->bodyTurnSpeed, 0); + Math_ApproachF(&this->bodyTurnSpeed, 3000.0f, 1.0f, 300.0f); + Math_ApproachZeroF(&this->headAngle, 0.5f, 2000.0f); + } + + if (this->kickTimer == 0) { + this->actionFunc = EnHeishi1_SetupKick; + } +} + +void EnHeishi1_SetupKick(EnHeishi1* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = EnHeishi1_Kick; +} + +void EnHeishi1_Kick(EnHeishi1* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (!this->loadStarted) { + // if dialog state is 5 and textbox has been advanced, kick player out + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + if (!this->loadStarted) { + gSaveContext.eventChkInf[4] |= 0x4000; + globalCtx->nextEntranceIndex = 0x4FA; + globalCtx->sceneLoadFlag = 0x14; + this->loadStarted = true; + sPlayerIsCaught = false; + globalCtx->fadeTransition = 0x2E; + gSaveContext.nextTransition = 0x2E; + } + } + } +} + +void EnHeishi1_SetupWaitNight(EnHeishi1* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = EnHeishi1_WaitNight; +} + +void EnHeishi1_WaitNight(EnHeishi1* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->actor.xzDistToPlayer < 100.0f) { + Message_StartTextbox(globalCtx, 0x702D, &this->actor); + func_80078884(NA_SE_SY_FOUND); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発見! ☆☆☆☆☆ \n" VT_RST); // "Discovered!" + func_8002DF54(globalCtx, &this->actor, 1); + this->actionFunc = EnHeishi1_SetupKick; + } +} + +void EnHeishi1_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi1* this = (EnHeishi1*)thisx; + s16 path; + u8 i; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 pad2; + Camera* activeCam; + + this->activeTimer++; + + for (i = 0; i < ARRAY_COUNT(this->timers); i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + if (this->waypointTimer != 0) { + this->waypointTimer--; + } + + activeCam = GET_ACTIVE_CAM(globalCtx); + + if (player->actor.freezeTimer == 0) { + + this->actionFunc(this, globalCtx); + + this->actor.uncullZoneForward = 550.0f; + this->actor.uncullZoneScale = 350.0f; + this->actor.uncullZoneDownward = 700.0f; + + if (this->type != 5) { + path = this->path * 2; + if ((sCamDataIdxs[path] == activeCam->camDataIdx) || (sCamDataIdxs[path + 1] == activeCam->camDataIdx)) { + if (!sPlayerIsCaught) { + if ((this->actionFunc == EnHeishi1_Walk) || (this->actionFunc == EnHeishi1_Wait)) { + Vec3f searchBallVel; + Vec3f searchBallAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f searchBallMult = { 0.0f, 0.0f, 20.0f }; + Vec3f searchBallPos; + + searchBallPos.x = this->actor.world.pos.x; + searchBallPos.y = this->actor.world.pos.y + 60.0f; + searchBallPos.z = this->actor.world.pos.z; + + Matrix_Push(); + Matrix_RotateY(((this->actor.shape.rot.y + this->headAngle) / 32768.0f) * M_PI, MTXMODE_NEW); + searchBallMult.z = 30.0f; + Matrix_MultVec3f(&searchBallMult, &searchBallVel); + Matrix_Pop(); + + EffectSsSolderSrchBall_Spawn(globalCtx, &searchBallPos, &searchBallVel, &searchBallAccel, 2, + &this->linkDetected); + + if (this->actor.xzDistToPlayer < 60.0f) { + this->linkDetected = true; + } else if (this->actor.xzDistToPlayer < 70.0f) { + // this case probably exists to detect link making a jump sound + // from slightly further away than the previous 60 unit check + if (player->actor.velocity.y > -4.0f) { + this->linkDetected = true; + } + } + + if (this->linkDetected) { + //! @bug This appears to be a check to make sure that link is standing on the ground + // before getting caught. However this is an issue for two reasons: + // 1: When doing a backflip or falling from the upper path, links y velocity will reach + // less than -4.0 before even touching the ground. + // 2: There is one frame when landing from a sidehop where you can sidehop again without + // letting y velocity reach -4.0 or less. This enables the player to do frame perfect + // sidehops onto the next screen and prevent getting caught. + if (!(player->actor.velocity.y > -3.9f)) { + this->linkDetected = false; + // this 60 unit height check is so the player doesnt get caught when on the upper path + if (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 60.0f) { + func_80078884(NA_SE_SY_FOUND); + // "Discovered!" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発見! ☆☆☆☆☆ \n" VT_RST); + func_8002DF54(globalCtx, &this->actor, 1); + sPlayerIsCaught = true; + this->actionFunc = EnHeishi1_SetupMoveToLink; + } + } + } + } + } + } + } + } +} + +s32 EnHeishi1_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnHeishi1* this = (EnHeishi1*)thisx; + + // turn the guards head to match the direction he is looking + if (limbIndex == 16) { + rot->x += (s16)this->headAngle; + } + + return false; +} + +void EnHeishi1_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHeishi1* this = (EnHeishi1*)thisx; + Vec3f matrixScale = { 0.3f, 0.3f, 0.3f }; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnHeishi1_OverrideLimbDraw, NULL, + this); + func_80033C30(&this->actor.world.pos, &matrixScale, 0xFF, globalCtx); + + if ((this->path == BREG(1)) && (BREG(0) != 0)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y + 100.0f, this->actor.world.pos.z, + 17000, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, 1.0f, 255, 0, 0, + 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.h b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.h new file mode 100644 index 000000000..af1e4c9d0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.h @@ -0,0 +1,55 @@ +#ifndef Z_EN_HEISHI1_H +#define Z_EN_HEISHI1_H + +#include "ultra64.h" +#include "global.h" + +struct EnHeishi1; + +typedef void (*EnHeishi1ActionFunc)(struct EnHeishi1*, GlobalContext*); + +typedef struct EnHeishi1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ EnHeishi1ActionFunc actionFunc; + /* 0x0260 */ s16 activeTimer; + /* 0x0262 */ s16 headDirection; + /* 0x0264 */ s16 headBehaviorDecided; + /* 0x0266 */ s16 linkDetected; + /* 0x0268 */ s16 loadStarted; + /* 0x026A */ s16 type; + /* 0x026C */ s16 path; + /* 0x026E */ s16 unused1; + /* 0x0270 */ s16 waypointTimer; + /* 0x0272 */ s16 unused2; + /* 0x0274 */ f32 moveSpeed; + /* 0x0278 */ f32 bodyTurnSpeed; + /* 0x027C */ f32 headAngle; + /* 0x0280 */ f32 headAngleTarget; + union { + struct { + /* 0x0284 */ f32 animSpeed; + /* 0x0288 */ f32 transitionRate; + /* 0x028C */ f32 moveSpeedTarget; + /* 0x0290 */ f32 moveSpeedMax; + /* 0x0294 */ f32 bodyTurnSpeedTarget; + /* 0x0298 */ f32 bodyTurnSpeedMax; + /* 0x029C */ f32 headTurnSpeedScale; + /* 0x02A0 */ f32 headTurnSpeedMax; + }; + f32 animParams[8]; + }; + union { + struct { + /* 0x02A4 */ s16 headTimer; + /* 0x02A6 */ s16 waitTimer; + /* 0x02A8 */ s16 kickTimer; + }; + s16 timers[3]; + }; + /* 0x02AA */ s16 waypoint; +} EnHeishi1; // size = 0x02AC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c new file mode 100644 index 000000000..86fbab0f6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -0,0 +1,865 @@ +/* + * File: z_en_heishi2.c + * Overlay: ovl_En_Heishi2 + * Description: Hyrulian Guards + */ + +#include "vt.h" +#include "z_en_heishi2.h" +#include "objects/object_sd/object_sd.h" +#include "objects/object_link_child/object_link_child.h" +#include "overlays/actors/ovl_Bg_Gate_Shutter/z_bg_gate_shutter.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "overlays/actors/ovl_Bg_Spot15_Saku/z_bg_spot15_saku.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnHeishi2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi2_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHeishi2_DrawKingGuard(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi2_DoNothing1(EnHeishi2* this, GlobalContext* globalCtx); +void EnHeishi_DoNothing2(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A531E4(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53278(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A5344C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A54954(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53538(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A535BC(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A5399C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53638(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A5372C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A5475C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53AD4(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53C0C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53C90(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53D0C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A544AC(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53908(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53F30(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A54038(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A5427C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A549E8(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53850(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A54320(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A543A0(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A5455C(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A546DC(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A541FC(EnHeishi2* this, GlobalContext* globalCtx); +void func_80A53DF8(EnHeishi2* this, GlobalContext* globalCtx); + +const ActorInit En_Heishi2_InitVars = { + ACTOR_EN_HEISHI2, + ACTORCAT_NPC, + FLAGS, + OBJECT_SD, + sizeof(EnHeishi2), + (ActorFunc)EnHeishi2_Init, + (ActorFunc)EnHeishi2_Destroy, + (ActorFunc)EnHeishi2_Update, + (ActorFunc)EnHeishi2_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 33, 40, 0, { 0, 0, 0 } }, +}; + +void EnHeishi2_Init(Actor* thisx, GlobalContext* globalCtx) { + ColliderCylinder* collider; + EnHeishi2* this = (EnHeishi2*)thisx; + + Actor_SetScale(&this->actor, 0.01f); + this->type = this->actor.params & 0xFF; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + + if ((this->type == 6) || (this->type == 9)) { + this->actor.draw = EnHeishi2_DrawKingGuard; + this->actor.flags &= ~ACTOR_FLAG_0; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, 6); + if (this->type == 6) { + this->actionFunc = EnHeishi2_DoNothing1; + + } else { + osSyncPrintf("\n\n"); + // "No, I'm completely disappointed" (message for when shooting guard window in courtyard) + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ いやー ついうっかり ☆☆☆☆☆ \n" VT_RST); + + Actor_SetScale(&this->actor, 0.02f); + + this->unk_274 = this->actor.world.pos; + this->actor.world.rot.y = 0x7918; + this->actor.world.pos.x += 90.0f; + this->actor.world.pos.y -= 60.0f; + this->actor.world.pos.z += 90.0f; + this->actor.shape.rot.y = this->actor.world.rot.y; + Collider_DestroyCylinder(globalCtx, &this->collider); + func_8002DF54(globalCtx, 0, 8); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_4; + this->actionFunc = func_80A544AC; + } + } else { + this->unk_2E0 = 60.0f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gEnHeishiSkel, &gEnHeishiIdleAnim, this->jointTable, + this->morphTable, 17); + collider = &this->collider; + Collider_InitCylinder(globalCtx, collider); + Collider_SetCylinder(globalCtx, collider, &this->actor, &sCylinderInit); + this->collider.dim.yShift = 0; + this->collider.dim.radius = 15; + this->collider.dim.height = 70; + this->actor.targetMode = 6; + + switch (this->type) { + + case 2: + this->actionFunc = func_80A531E4; + this->actor.gravity = -1.0f; + break; + case 5: + this->actionFunc = func_80A53908; + this->actor.gravity = -1.0f; + break; + case 6: + osSyncPrintf("\n\n"); + // "Peep hole soldier!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 覗き穴奥兵士ふぃ〜 ☆☆☆☆☆ \n" VT_RST); + Collider_DestroyCylinder(globalCtx, collider); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actionFunc = EnHeishi_DoNothing2; + break; + } + + this->unk_2F0 = (this->actor.params >> 8) & 0xFF; + osSyncPrintf("\n\n"); + // "Soldier Set 2 Completed!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 兵士2セット完了! ☆☆☆☆☆ %d\n" VT_RST, this->actor.params); + // "Identification Completed!" + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ 識別完了! ☆☆☆☆☆ %d\n" VT_RST, this->type); + // "Message completed!" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ メッセージ完了! ☆☆☆☆☆ %x\n\n" VT_RST, (this->actor.params >> 8) & 0xF); + } +} + +void EnHeishi2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi2* this = (EnHeishi2*)thisx; + + if ((this->collider.dim.radius != 0) || (this->collider.dim.height != 0)) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnHeishi2_DoNothing1(EnHeishi2* this, GlobalContext* globalCtx) { +} + +void EnHeishi_DoNothing2(EnHeishi2* this, GlobalContext* globalCtx) { +} + +void func_80A531E4(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A53278; +} + +void func_80A53278(EnHeishi2* this, GlobalContext* globalCtx) { + this->unk_30B = 0; + this->unk_30E = 0; + if (Text_GetFaceReaction(globalCtx, 5) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 5); + this->unk_30B = 1; + this->unk_300 = TEXT_STATE_DONE; + this->actionFunc = func_80A5475C; + } else if ((gSaveContext.eventChkInf[0] & 0x200) && (gSaveContext.eventChkInf[2] & 0x20) && + (gSaveContext.eventChkInf[3] & 0x80)) { + // "Get all spiritual stones!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 全部の精霊石GET! ☆☆☆☆☆ \n" VT_RST); + this->unk_300 = TEXT_STATE_DONE; + this->actor.textId = 0x7006; + this->actionFunc = func_80A5475C; + } else if (!IS_DAY) { + // "Sleep early for children!" + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ 子供ははやくネロ! ☆☆☆☆☆ \n" VT_RST); + this->unk_300 = TEXT_STATE_DONE; + this->actor.textId = 0x7002; + this->actionFunc = func_80A5475C; + + } else if (this->unk_30C != 0) { + // "Anything passes" + osSyncPrintf(VT_FGCOL(BLUE) " ☆☆☆☆☆ なんでも通るよ ☆☆☆☆☆ \n" VT_RST); + this->unk_300 = TEXT_STATE_DONE; + this->actor.textId = 0x7099; + this->actionFunc = func_80A5475C; + } else if (gSaveContext.eventChkInf[1] & 4) { + if (this->unk_30E == 0) { + // "Start under the first sleeve!" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ 1回目袖の下開始! ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x7071; + this->unk_30E = 1; + } else { + // "Start under the second sleeve!" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ 2回目袖の下開始! ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x7072; + } + this->unk_300 = TEXT_STATE_CHOICE; + this->actionFunc = func_80A5475C; + + } else { + // "That's okay" + osSyncPrintf(VT_FGCOL(CYAN) " ☆☆☆☆☆ それはとおらんよぉ ☆☆☆☆☆ \n" VT_RST); + this->unk_300 = TEXT_STATE_DONE; + this->actor.textId = 0x7029; + this->actionFunc = func_80A5475C; + } +} + +void func_80A5344C(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + this->unk_300 = TEXT_STATE_EVENT; + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (gSaveContext.rupees >= 10) { + Rupees_ChangeBy(-10); + this->actor.textId = 0x7098; + this->actionFunc = func_80A53538; + break; + } else { + this->actor.textId = 0x7097; + this->actionFunc = func_80A54954; + break; + } + case 1: + this->actor.textId = 0x7096; + this->actionFunc = func_80A54954; + break; + + default: + break; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + } +} + +void func_80A53538(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_300 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + func_8002DF54(globalCtx, NULL, 8); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actionFunc = func_80A535BC; + } +} + +void func_80A535BC(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiSlamSpearAnim); + + this->unk_2EC = frameCount; + Animation_Change(&this->skelAnime, &gEnHeishiSlamSpearAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, -10.0f); + this->actionFunc = func_80A53638; +} + +void func_80A53638(EnHeishi2* this, GlobalContext* globalCtx) { + s32 pad; + f32 frameCount = this->skelAnime.curFrame; + + BgSpot15Saku* actor = (BgSpot15Saku*)globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + + SkelAnime_Update(&this->skelAnime); + if ((frameCount >= 12.0f) && (!this->audioFlag)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SPEAR_HIT); + this->audioFlag = 1; + } + if (this->unk_2EC <= frameCount) { + while (actor != NULL) { + if (actor->dyna.actor.id != ACTOR_BG_SPOT15_SAKU) { + actor = (BgSpot15Saku*)(actor->dyna.actor.next); + } else { + this->gate = &actor->dyna.actor; + actor->unk_168 = 1; + break; + } + } + // "I've come!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆ きたきたきたぁ! ☆☆☆ %x\n" VT_RST, actor->dyna.actor.next); + this->actionFunc = func_80A5372C; + } +} + +void func_80A5372C(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->unk_2F2[0] = 200; + this->cameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->cameraId, CAM_STAT_ACTIVE); + this->unk_280.x = 947.0f; + this->unk_280.y = 1195.0f; + this->unk_280.z = 2682.0f; + + this->unk_28C.x = 1164.0f; + this->unk_28C.y = 1145.0f; + this->unk_28C.z = 3014.0f; + + Gameplay_CameraSetAtEye(globalCtx, this->cameraId, &this->unk_280, &this->unk_28C); + this->actionFunc = func_80A53850; +} + +void func_80A53850(EnHeishi2* this, GlobalContext* globalCtx) { + BgSpot15Saku* gate; + + SkelAnime_Update(&this->skelAnime); + Gameplay_CameraSetAtEye(globalCtx, this->cameraId, &this->unk_280, &this->unk_28C); + gate = (BgSpot15Saku*)this->gate; + if ((this->unk_2F2[0] == 0) || (gate->unk_168 == 0)) { + Gameplay_ClearCamera(globalCtx, this->cameraId); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + Message_CloseTextbox(globalCtx); + this->unk_30C = 1; + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = func_80A531E4; + } +} + +void func_80A53908(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A5399C; +} + +void func_80A5399C(EnHeishi2* this, GlobalContext* globalCtx) { + s16 var; + + this->unk_30B = 0; + var = 0; + if (gSaveContext.infTable[7] & 0x40) { + if (!(gSaveContext.infTable[7] & 0x80)) { + if (Player_GetMask(globalCtx) == PLAYER_MASK_KEATON) { + if (this->unk_309 == 0) { + this->actor.textId = 0x200A; + } else { + this->actor.textId = 0x200B; + } + this->unk_300 = TEXT_STATE_CHOICE; + this->unk_30B = 1; + var = 1; + } else { + this->actor.textId = 0x2016; + this->unk_300 = TEXT_STATE_DONE; + var = 1; + } + } else { + this->actor.textId = 0x2020; + this->unk_300 = TEXT_STATE_EVENT; + this->unk_30E = 0; + } + if (Text_GetFaceReaction(globalCtx, 5) != 0) { + if (var == 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 5); + this->unk_30B = 1; + this->unk_300 = TEXT_STATE_DONE; + this->unk_30E = 0; + } + } + this->actionFunc = func_80A5475C; + } else { + // "I don't know" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ とおしゃしねぇちゅーの ☆☆☆☆☆ \n" VT_RST); + this->actionFunc = func_80A53AD4; + } +} + +void func_80A53AD4(EnHeishi2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 exchangeItemId; + s16 yawDiffTemp; + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + if (Text_GetFaceReaction(globalCtx, 5) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 5); + } else { + this->actor.textId = 0x200E; + } + this->unk_300 = TEXT_STATE_DONE; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + exchangeItemId = func_8002F368(globalCtx); + if (exchangeItemId == EXCH_ITEM_LETTER_ZELDA) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + player->actor.textId = 0x2010; + this->unk_300 = TEXT_STATE_EVENT; + this->actionFunc = func_80A53C0C; + } else if (exchangeItemId != EXCH_ITEM_NONE) { + player->actor.textId = 0x200F; + } + } else { + yawDiffTemp = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + yawDiff = ABS(yawDiffTemp); + if (!(120.0f < this->actor.xzDistToPlayer) && (yawDiff < 0x4300)) { + func_8002F298(&this->actor, globalCtx, 100.0f, EXCH_ITEM_LETTER_ZELDA); + } + } +} + +void func_80A53C0C(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((this->unk_300 == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + func_8002DF54(globalCtx, 0, 8); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actionFunc = func_80A53C90; + } +} + +void func_80A53C90(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiSlamSpearAnim); + + this->unk_2EC = frameCount; + Animation_Change(&this->skelAnime, &gEnHeishiSlamSpearAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, -10.0f); + this->actionFunc = func_80A53D0C; +} + +void func_80A53D0C(EnHeishi2* this, GlobalContext* globalCtx) { + s32 pad; + f32 frameCount; + BgGateShutter* gate; + + frameCount = this->skelAnime.curFrame; + gate = (BgGateShutter*)globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + SkelAnime_Update(&this->skelAnime); + if (12.0f <= frameCount) { + if (this->audioFlag == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SPEAR_HIT); + this->audioFlag = 1; + } + } + if (this->unk_2EC <= frameCount) { + while (gate != NULL) { + if (gate->dyna.actor.id != ACTOR_BG_GATE_SHUTTER) { + gate = (BgGateShutter*)gate->dyna.actor.next; + } else { + this->gate = &gate->dyna.actor; + gate->openingState = 1; + break; + } + } + // "I've come!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆ きたきたきたぁ! ☆☆☆ %x\n" VT_RST, gate->dyna.actor.next); + this->actionFunc = func_80A53DF8; + } +} + +void func_80A53DF8(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->unk_2F2[0] = 200; + this->cameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->cameraId, CAM_STAT_ACTIVE); + this->unk_2BC.x = -71.0f; + this->unk_280.x = -71.0f; + this->unk_2BC.y = 571.0f; + this->unk_280.y = 571.0f; + this->unk_2BC.z = -1487.0f; + this->unk_280.z = -1487.0f; + this->unk_298.x = 181.0f; + this->unk_28C.x = 181.0f; + this->unk_298.y = 417.0f; + this->unk_28C.y = 417.0f; + this->unk_298.z = -1079.0f; + this->unk_28C.z = -1079.0f; + Gameplay_CameraSetAtEye(globalCtx, this->cameraId, &this->unk_280, &this->unk_28C); + this->actionFunc = func_80A53F30; +} + +void func_80A53F30(EnHeishi2* this, GlobalContext* globalCtx) { + BgGateShutter* gate; + + SkelAnime_Update(&this->skelAnime); + Gameplay_CameraSetAtEye(globalCtx, this->cameraId, &this->unk_280, &this->unk_28C); + gate = (BgGateShutter*)this->gate; + if ((this->unk_2F2[0] == 0) || (gate->openingState == 0)) { + Gameplay_ClearCamera(globalCtx, this->cameraId); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + if ((this->unk_30A != 2)) { + if (this->unk_30A == 0) { + this->actor.textId = 0x2015; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->actionFunc = func_80A54038; + } else { + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = func_80A53908; + } + } else { + this->unk_30E = 0; + this->actor.textId = 0x2021; + Rupees_ChangeBy(15); + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->actionFunc = func_80A5427C; + } + } +} + +void func_80A54038(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[7] |= 0x40; + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, 0, 7); + this->actionFunc = func_80A53908; + } +} + +void func_80A540C0(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + this->actor.textId = 0x2020; + Message_ContinueTextbox(globalCtx, this->actor.textId); + Player_UnsetMask(globalCtx); + gSaveContext.infTable[7] |= 0x80; + gSaveContext.itemGetInf[3] |= 0x100; + Item_Give(globalCtx, ITEM_SOLD_OUT); + if (this->unk_30A != 0) { + this->unk_30A = 2; + this->unk_30E = 1; + this->actionFunc = func_80A5427C; + } else { + this->unk_30E = 0; + this->actionFunc = func_80A541FC; + } + break; + case 1: + this->unk_30E = 1; + this->actor.textId = 0x200C; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_300 = TEXT_STATE_EVENT; + if (this->unk_30A == 0) { + this->actionFunc = func_80A5427C; + } else { + this->actionFunc = func_80A54954; + } + } + } +} + +void func_80A541FC(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actor.textId = 0x2021; + Rupees_ChangeBy(15); + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->actionFunc = func_80A5427C; + } +} + +void func_80A5427C(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + if (this->unk_30E == 0) { + this->unk_30E = 0; + this->unk_30A = this->unk_30E; + Message_CloseTextbox(globalCtx); + this->actionFunc = func_80A53908; + } else { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actionFunc = func_80A54320; + } + } +} + +void func_80A54320(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiSlamSpearAnim); + + this->unk_2EC = frameCount; + Animation_Change(&this->skelAnime, &gEnHeishiSlamSpearAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, -10.0f); + this->audioFlag = 0; + this->actionFunc = func_80A543A0; +} + +void func_80A543A0(EnHeishi2* this, GlobalContext* globalCtx) { + s32 pad; + f32 frameCount = this->skelAnime.curFrame; + BgGateShutter* gate = (BgGateShutter*)(globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head); + SkelAnime_Update(&this->skelAnime); + + if ((frameCount >= 12.0f) && (!this->audioFlag)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_SPEAR_HIT); + this->audioFlag = 1; + } + + if (this->unk_2EC <= frameCount) { + while (gate != NULL) { + if (ACTOR_BG_GATE_SHUTTER != gate->dyna.actor.id) { + gate = (BgGateShutter*)(gate->dyna.actor.next); + } else { + this->gate = &gate->dyna.actor; + if (this->unk_30A != 2) { + gate->openingState = -1; + break; + } else { + gate->openingState = 2; + break; + } + } + } + if (this->unk_30A == 0) { + this->unk_30A = 1; + } + this->actionFunc = func_80A53DF8; + } +} + +void func_80A544AC(EnHeishi2* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.z, -6100, 5, this->unk_2E4, 0); + Math_ApproachF(&this->unk_2E4, 3000.0f, 1.0f, 500.0f); + this->actor.world.rot.z = this->actor.shape.rot.z; + if (this->actor.shape.rot.z < -6000) { + Message_StartTextbox(globalCtx, 0x708F, NULL); + this->actor.flags |= ACTOR_FLAG_16; + this->actionFunc = func_80A5455C; + this->unk_2E4 = 0.0f; + } +} + +void func_80A5455C(EnHeishi2* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f pos; + f32 rotY; + EnBom* bomb; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + func_8002DF54(globalCtx, NULL, 7); + Message_CloseTextbox(globalCtx); + + pos.x = Rand_CenteredFloat(20.0f) + this->unk_274.x; + pos.y = Rand_CenteredFloat(20.0f) + (this->unk_274.y - 40.0f); + pos.z = Rand_CenteredFloat(20.0f) + (this->unk_274.z - 20.0f); + rotY = Rand_CenteredFloat(7000.0f) + this->actor.yawTowardsPlayer; + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, pos.x, pos.y, pos.z, 0, rotY, 0, 0); + if (bomb != NULL) { + bomb->actor.speedXZ = Rand_CenteredFloat(5.0f) + 10.0f; + bomb->actor.velocity.y = Rand_CenteredFloat(5.0f) + 10.0f; + } + + // "This is down!" + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ これでダウンだ! ☆☆☆☆☆ \n" VT_RST); + this->actionFunc = func_80A546DC; + } +} + +void func_80A546DC(EnHeishi2* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.z, 200, 5, this->unk_2E4, 0); + Math_ApproachF(&this->unk_2E4, 3000.0f, 1.0f, 500.0f); + this->actor.world.rot.z = this->actor.shape.rot.z; + if (this->actor.shape.rot.z > 0) { + Actor_Kill(&this->actor); + } +} + +void func_80A5475C(EnHeishi2* this, GlobalContext* globalCtx) { + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + + if (Text_GetFaceReaction(globalCtx, 5) != 0) { + if (this->unk_30B == 0) { + if (this->type == 2) { + this->actionFunc = func_80A53278; + return; + } + if (this->type == 5) { + this->actionFunc = func_80A5399C; + return; + } + } + } else if (this->unk_30B != 0) { + if (this->type == 2) { + this->actionFunc = func_80A53278; + return; + } + if (this->type == 5) { + this->actionFunc = func_80A5399C; + return; + } + } + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->type == 2) { + if (this->unk_30E == 1) { + this->actionFunc = func_80A5344C; + return; + } else { + this->actionFunc = func_80A53278; + return; + } + } else if (this->type == 5) { + if (this->unk_300 == TEXT_STATE_DONE) { + this->actionFunc = func_80A5399C; + } + + if (this->unk_300 == TEXT_STATE_EVENT) { + this->actionFunc = func_80A54954; + } + + if (this->unk_300 == TEXT_STATE_CHOICE) { + this->unk_309 = 1; + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + this->actionFunc = func_80A540C0; + } + return; + } + } + + if (((this->type != 2) && (this->type != 5)) || + ((yawDiff = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)), + !(this->actor.xzDistToPlayer > 120.0f)) && + (yawDiff < 0x4300))) { + func_8002F2F4(&this->actor, globalCtx); + } +} + +void func_80A54954(EnHeishi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A549E8; +} + +void func_80A549E8(EnHeishi2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_300 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + if (this->type == 2) { + this->actionFunc = func_80A531E4; + } + if (this->type == 5) { + this->actionFunc = func_80A53908; + } + } +} + +void EnHeishi2_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHeishi2* this = (EnHeishi2*)thisx; + s32 i; + + Actor_SetFocus(&this->actor, this->unk_2E0); + if ((this->type == 2) || (this->type == 5)) { + this->actor.focus.pos.y = 70.0f; + Actor_SetFocus(&this->actor, 70.0f); + func_80038290(globalCtx, &this->actor, &this->unk_260, &this->unk_26C, this->actor.focus.pos); + } + + this->unk_2FC++; + + for (i = 0; i != 5; i++) { + if (this->unk_2F2[i] != 0) { + this->unk_2F2[i]--; + } + } + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + switch (this->type) { + case 6: + break; + case 9: + break; + default: + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 30.0f, 0x1D); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + break; + } +} + +s32 EnHeishi2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnHeishi2* this = (EnHeishi2*)thisx; + + switch (this->type) { + case 1: + break; + case 7: + break; + default: + if (limbIndex == 9) { + rot->x = rot->x + this->unk_26C.y; + } + if (limbIndex == 16) { + rot->x = rot->x + this->unk_260.y; + rot->z = rot->z + this->unk_260.z; + } + } + + return false; +} + +void EnHeishi2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnHeishi2* this = (EnHeishi2*)thisx; + + if (limbIndex == 16) { + Matrix_Get(&this->mtxf_330); + } +} + +void EnHeishi2_DrawKingGuard(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_heishi2.c", 1772); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_heishi2.c", 1774), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gHeishiKingGuardDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_heishi2.c", 1777); +} + +void EnHeishi2_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi2* this = (EnHeishi2*)thisx; + Mtx* mtx; + s32 linkObjBankIndex; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_heishi2.c", 1792); + + func_80093D18(globalCtx->state.gfxCtx); + + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnHeishi2_OverrideLimbDraw, + EnHeishi2_PostLimbDraw, this); + if ((this->type == 5) && (gSaveContext.infTable[7] & 0x80)) { + linkObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_LINK_CHILD); + if (linkObjBankIndex >= 0) { + Matrix_Put(&this->mtxf_330); + Matrix_Translate(-570.0f, 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateZ(DEGTORAD(70.0), MTXMODE_APPLY); + mtx = Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_heishi2.c", 1820) - 7; + + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[linkObjBankIndex].segment); + gSPSegment(POLY_OPA_DISP++, 0x0D, mtx); + gSPDisplayList(POLY_OPA_DISP++, gLinkChildKeatonMaskDL); + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_heishi2.c", 1834); +} diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h new file mode 100644 index 000000000..86a7e0539 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.h @@ -0,0 +1,53 @@ +#ifndef Z_EN_HEISHI2_H +#define Z_EN_HEISHI2_H + +#include "ultra64.h" +#include "global.h" + + +struct EnHeishi2; + +typedef void (*EnHeishi2ActionFunc)(struct EnHeishi2*, GlobalContext*); + +typedef struct EnHeishi2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ EnHeishi2ActionFunc actionFunc; + /* 0x0260 */ Vec3s unk_260; + /* 0x0266 */ char unk_266[0x06]; + /* 0x026C */ Vec3s unk_26C; // padding inbetween these + /* 0x0274 */ Vec3f unk_274; + /* 0x0280 */ Vec3f unk_280; // camera related + /* 0x028C */ Vec3f unk_28C; // camera related + /* 0x0298 */ Vec3f unk_298; // camera related + /* 0x02A4 */ char unk_2A4[0x18]; + /* 0x02BC */ Vec3f unk_2BC; // camera related + /* 0x02C8 */ char unk_2C8[0x18]; + /* 0x02E0 */ f32 unk_2E0; + /* 0x02E4 */ f32 unk_2E4; + /* 0x02E8 */ char unk_2E8[0x4]; + /* 0x02EC */ f32 unk_2EC; + /* 0x02F0 */ u16 unk_2F0; + /* 0x02F2 */ s16 unk_2F2[0x5]; // starts counting down when guard starts to open gate. + /* 0x02FC */ s16 unk_2FC; + /* 0x02FE */ s16 type; // copy of actor params at init + /* 0x0300 */ s16 unk_300; // mask related + /* 0x0302 */ char unk_302[0x6]; + /* 0x0308 */ u8 audioFlag; + /* 0x0309 */ u8 unk_309; + /* 0x030A */ u8 unk_30A; + /* 0x030B */ u8 unk_30B; + /* 0x030C */ u8 unk_30C; + /* 0x030D */ char unk_30D; + /* 0x030E */ s16 unk_30E; + /* 0x0310 */ s16 cameraId; + /* 0x0314 */ Actor* gate; // first BgGateShutter that can be found in the actor list + /* 0x0318 */ char unk_318[0x18]; + /* 0x0330 */ MtxF mtxf_330; + /* 0x0370 */ char unk_370[0x28]; + /* 0x0398 */ ColliderCylinder collider; +} EnHeishi2; // size = 0x03E4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c b/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c new file mode 100644 index 000000000..8e31d5670 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.c @@ -0,0 +1,254 @@ +/* + * File: z_en_heishi3.c + * Overlay: ovl_En_Heishi3 + * Description: Hyrule Castle Guard + */ + +#include "z_en_heishi3.h" +#include "objects/object_sd/object_sd.h" +#include "vt.h" + +#define FLAGS 0 + +void EnHeishi3_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi3_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi3_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHeishi3_SetupGuardType(EnHeishi3* this, GlobalContext* globalCtx); +void EnHeishi3_StandSentinelInGrounds(EnHeishi3* this, GlobalContext* globalCtx); +void EnHeishi3_StandSentinelInCastle(EnHeishi3* this, GlobalContext* globalCtx); +void EnHeishi3_CatchStart(EnHeishi3* this, GlobalContext* globalCtx); +void EnHeishi3_ResetAnimationToIdle(EnHeishi3* this, GlobalContext* globalCtx); +void func_80A55D00(EnHeishi3* this, GlobalContext* globalCtx); +void func_80A55BD4(EnHeishi3* this, GlobalContext* globalCtx); + +static s16 sPlayerCaught = 0; + +const ActorInit En_Heishi3_InitVars = { + ACTOR_EN_HEISHI3, + ACTORCAT_NPC, + FLAGS, + OBJECT_SD, + sizeof(EnHeishi3), + (ActorFunc)EnHeishi3_Init, + (ActorFunc)EnHeishi3_Destroy, + (ActorFunc)EnHeishi3_Update, + (ActorFunc)EnHeishi3_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 15, 70, 0, { 0, 0, 0 } }, +}; + +void EnHeishi3_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi3* this = (EnHeishi3*)thisx; + + sPlayerCaught = 0; + if (this->actor.params <= 0) { + this->unk_278 = 0; + } else { + this->unk_278 = 1; + if (this->actor.world.pos.x < -290.0f) { + this->unk_278 = 2; + } + } + Actor_SetScale(&this->actor, 0.01f); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gEnHeishiSkel, &gEnHeishiIdleAnim, this->jointTable, this->morphTable, + 17); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.targetMode = 6; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + // "Castle Gate Soldier - Power Up" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 城門兵パワーアップ ☆☆☆☆☆ \n" VT_RST); + + this->actor.gravity = -3.0f; + this->actor.focus.pos = this->actor.world.pos; + this->actionFunc = EnHeishi3_SetupGuardType; +} + +void EnHeishi3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi3* this = (EnHeishi3*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnHeishi3_SetupGuardType(EnHeishi3* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + if (this->unk_278 == 0) { + this->actionFunc = EnHeishi3_StandSentinelInGrounds; + } else { + this->actionFunc = EnHeishi3_StandSentinelInCastle; + } +} + +/** + * Handles the guards standing on Hyrule Castle Grounds. + **/ +void EnHeishi3_StandSentinelInGrounds(EnHeishi3* this, GlobalContext* globalCtx) { + Player* player; + s16 yawDiff; + s16 yawDiffNew; + f32 sightRange; + + player = GET_PLAYER(globalCtx); + SkelAnime_Update(&this->skelAnime); + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + yawDiffNew = ABS(yawDiff); + if (yawDiffNew < 0x4300) { + if (IS_DAY) { + sightRange = 250.0f; + } else { + sightRange = 200.0f; + } + } else { + if (IS_DAY) { + sightRange = 150.0f; + } else { + sightRange = 100.0f; + } + } + if ((this->actor.xzDistToPlayer < sightRange) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 100.0f) && (sPlayerCaught == 0)) { + sPlayerCaught = 1; + Message_StartTextbox(globalCtx, 0x702D, &this->actor); + func_80078884(NA_SE_SY_FOUND); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発見! ☆☆☆☆☆ \n" VT_RST); // "Discovered!" + func_8002DF54(globalCtx, &this->actor, 1); + this->actionFunc = EnHeishi3_CatchStart; + } +} + +/** + * Handles the guards standing in front of Hyrule Castle. + **/ +void EnHeishi3_StandSentinelInCastle(EnHeishi3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + if ((player->actor.world.pos.x < -190.0f) && (player->actor.world.pos.x > -380.0f) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 100.0f) && + (player->actor.world.pos.z < 1020.0f) && (player->actor.world.pos.z > 700.0f) && (sPlayerCaught == 0)) { + if (this->unk_278 == 1) { + if ((player->actor.world.pos.x < -290.0f)) { + return; + } + } else { + if (player->actor.world.pos.x > -290.0f) { + return; + } + } + sPlayerCaught = 1; + Message_StartTextbox(globalCtx, 0x702D, &this->actor); + func_80078884(NA_SE_SY_FOUND); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 発見! ☆☆☆☆☆ \n" VT_RST); // "Discovered!" + func_8002DF54(globalCtx, &this->actor, 1); + this->actionFunc = EnHeishi3_CatchStart; + } +} + +void EnHeishi3_CatchStart(EnHeishi3* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiWalkAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiWalkAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->caughtTimer = 20; + this->actionFunc = func_80A55BD4; + this->actor.speedXZ = 2.5f; +} + +void func_80A55BD4(EnHeishi3* this, GlobalContext* globalCtx) { + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 1.0f) || Animation_OnFrame(&this->skelAnime, 17.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KNIGHT_WALK); + } + if (this->caughtTimer == 0) { + this->actionFunc = EnHeishi3_ResetAnimationToIdle; + this->actor.speedXZ = 0.0f; + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 5, 3000, 0); + } +} + +void EnHeishi3_ResetAnimationToIdle(EnHeishi3* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A55D00; +} + +// This function initiates the respawn after the player gets caught. +void func_80A55D00(EnHeishi3* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx) && + (this->respawnFlag == 0)) { + gSaveContext.eventChkInf[4] |= 0x4000; + globalCtx->nextEntranceIndex = 0x47E; // Hyrule Castle from Guard Capture (outside) + globalCtx->sceneLoadFlag = 0x14; + this->respawnFlag = 1; + globalCtx->fadeTransition = 0x2E; + gSaveContext.nextTransition = 0x2E; + } +} + +void EnHeishi3_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi3* this = (EnHeishi3*)thisx; + s32 pad; + + Actor_SetFocus(&this->actor, 60.0f); + this->unk_274 += 1; + if (this->caughtTimer != 0) { + this->caughtTimer -= 1; + } + this->actionFunc(this, globalCtx); + this->actor.shape.rot = this->actor.world.rot; + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 50.0f, 0x1C); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnHeishi3_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnHeishi3* this = (EnHeishi3*)thisx; + + if (limbIndex == 9) { + rot->x += this->unk_26E; + } + + if (limbIndex == 16) { + rot->x += this->unk_262; + rot->z += this->unk_264; + } + + return false; +} + +void EnHeishi3_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi3* this = (EnHeishi3*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnHeishi3_OverrideLimbDraw, NULL, + this); +} diff --git a/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.h b/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.h new file mode 100644 index 000000000..824e721ca --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi3/z_en_heishi3.h @@ -0,0 +1,31 @@ +#ifndef Z_EN_HEISHI3_H +#define Z_EN_HEISHI3_H + +#include "ultra64.h" +#include "global.h" + +struct EnHeishi3; + +typedef void (*EnHeishi3ActionFunc)(struct EnHeishi3*, GlobalContext*); + +typedef struct EnHeishi3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ EnHeishi3ActionFunc actionFunc; + /* 0x0260 */ char unk_260[0x2]; + /* 0x0262 */ s16 unk_262; + /* 0x0264 */ s16 unk_264; + /* 0x0266 */ char unk_266[0x8]; + /* 0x026E */ s16 unk_26E; // All these s16 are probably part of a Vec3s + /* 0x0270 */ char unk_270[0x2]; + /* 0x0272 */ s16 caughtTimer; + /* 0x0274 */ s16 unk_274; + /* 0x0276 */ u8 respawnFlag; // set to 1 when reloading area after being thrown out + /* 0x0278 */ s16 unk_278; + /* 0x027A */ char unk_27A[0x2]; + /* 0x027C */ ColliderCylinder collider; +} EnHeishi3; // size = 0x02C8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c new file mode 100644 index 000000000..f13c5c3ef --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c @@ -0,0 +1,387 @@ +#include "z_en_heishi4.h" +#include "objects/object_sd/object_sd.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnHeishi4_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi4_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi4_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHeishi4_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A56544(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A5673C(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56328(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A563BC(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56B40(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56614(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56874(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56900(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56994(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56A50(EnHeishi4* this, GlobalContext* globalCtx); +void func_80A56ACC(EnHeishi4* this, GlobalContext* globalCtx); + +const ActorInit En_Heishi4_InitVars = { + ACTOR_EN_HEISHI4, + ACTORCAT_NPC, + FLAGS, + OBJECT_SD, + sizeof(EnHeishi4), + (ActorFunc)EnHeishi4_Init, + (ActorFunc)EnHeishi4_Destroy, + (ActorFunc)EnHeishi4_Update, + (ActorFunc)EnHeishi4_Draw, + NULL, +}; + +static u32 sFaceReactionSets[] = { 6, 7 }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 33, 40, 0, { 0, 0, 0 } }, +}; + +void EnHeishi4_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi4* this = (EnHeishi4*)thisx; + + Actor_SetScale(thisx, 0.01f); + this->type = thisx->params & 0xFF; + thisx->colChkInfo.mass = MASS_IMMOVABLE; + this->pos = thisx->world.pos; + thisx->targetMode = 6; + if (this->type == HEISHI4_AT_MARKET_DYING) { + this->height = 30.0f; + ActorShape_Init(&thisx->shape, 0.0f, NULL, 30.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gEnHeishiSkel, &gEnHeishiDyingGuardAnim_00C444, this->jointTable, + this->morphTable, 17); + } else { + this->height = 60.0f; + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gEnHeishiSkel, &gEnHeishiIdleAnim, this->jointTable, + this->morphTable, 17); + } + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + this->collider.dim.yShift = 0; + this->collider.dim.radius = 15; + this->collider.dim.height = 70; + switch (this->type) { + case HEISHI4_AT_KAKRIKO_ENTRANCE: + case HEISHI4_AT_IMPAS_HOUSE: + this->actionFunc = func_80A56328; + break; + case HEISHI4_AT_MARKET_DYING: + this->collider.dim.radius = 28; + this->collider.dim.height = 5; + this->actionFunc = func_80A5673C; + break; + case HEISHI4_AT_MARKET_NIGHT: + this->actionFunc = func_80A56544; + break; + } + this->unk_27C = ((thisx->params >> 8) & 0xFF); + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 兵士2セット完了! ☆☆☆☆☆ %d\n" VT_RST, thisx->params); + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ 識別完了!\t ☆☆☆☆☆ %d\n" VT_RST, this->type); + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ メッセージ完了! ☆☆☆☆☆ %x\n\n" VT_RST, (thisx->params >> 8) & 0xF); + osSyncPrintf("\n\n"); +} + +void EnHeishi4_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi4* this = (EnHeishi4*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80A56328(EnHeishi4* this, GlobalContext* globalCtx) { + f32 frames = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A563BC; +} + +void func_80A563BC(EnHeishi4* this, GlobalContext* globalCtx) { + s16 reactionOffset; + + this->unk_2B4 = 0; + reactionOffset = this->type - 4; + if (reactionOffset < 0) { + reactionOffset = 0; + } + if (reactionOffset >= 3) { + reactionOffset = 1; + } + if (Text_GetFaceReaction(globalCtx, sFaceReactionSets[reactionOffset]) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, sFaceReactionSets[reactionOffset]); + this->unk_2B4 = 1; + this->actionFunc = func_80A56B40; + } else { + if (gSaveContext.eventChkInf[8] & 1) { + this->actor.textId = 0x5065; + this->actionFunc = func_80A56B40; + return; + } + if (gSaveContext.eventChkInf[4] & 0x20) { + this->actor.textId = 0x5068; + this->actionFunc = func_80A56B40; + return; + } + if (this->type == HEISHI4_AT_IMPAS_HOUSE) { + if (this->unk_284 == 0) { + this->actor.textId = 0x5079; + } else { + this->actor.textId = 0x507A; + } + } else if (IS_DAY) { + if (this->unk_284 == 0) { + this->actor.textId = 0x5063; + } else { + this->actor.textId = 0x5064; + } + } else if (this->unk_284 == 0) { + this->actor.textId = 0x5066; + } else { + this->actor.textId = 0x5067; + } + + this->actionFunc = func_80A56B40; + } +} + +void func_80A56544(EnHeishi4* this, GlobalContext* globalCtx) { + f32 frames = Animation_GetLastFrame(&gEnHeishiIdleAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiIdleAnim, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, -10.0f); + if (LINK_AGE_IN_YEARS != YEARS_CHILD) { + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ ぎゃぁ!オトナだー ☆☆☆☆☆ \n" VT_RST); + Actor_Kill(&this->actor); + } else { + this->actionFunc = func_80A56614; + } +} + +void func_80A56614(EnHeishi4* this, GlobalContext* globalCtx) { + s16 reactionOffset; + + reactionOffset = this->type - 4; + this->unk_2B4 = 0; + if (reactionOffset < 0) { + reactionOffset = 0; + } + if (reactionOffset >= 3) { + reactionOffset = 1; + } + if (Text_GetFaceReaction(globalCtx, sFaceReactionSets[reactionOffset]) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, sFaceReactionSets[reactionOffset]); + this->unk_2B4 = 1; + this->actionFunc = func_80A56B40; + return; + } + if (globalCtx->sceneNum == SCENE_MIHARIGOYA) { + if (IS_DAY) { + this->actor.textId = 0x7004; + } else { + this->actor.textId = 0x709A; + } + } else if (globalCtx->sceneNum != SCENE_MARKET_NIGHT) { + if (IS_DAY) { + this->actor.textId = 0x7002; + } else { + this->actor.textId = 0x7003; + } + } else { + this->actor.textId = 0x7003; + } + + this->actionFunc = func_80A56B40; +} + +void func_80A5673C(EnHeishi4* this, GlobalContext* globalCtx) { + if (gSaveContext.eventChkInf[4] & 0x20) { + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ マスターソード祝入手! ☆☆☆☆☆ \n" VT_RST); + Actor_Kill(&this->actor); + return; + } + this->unk_284 = 0; + if (gSaveContext.eventChkInf[8] & 1) { + if (!(gSaveContext.infTable[6] & 0x1000)) { + f32 frames = Animation_GetLastFrame(&gEnHeishiDyingGuardAnim_00C444); + Animation_Change(&this->skelAnime, &gEnHeishiDyingGuardAnim_00C444, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, + -10.0f); + this->actor.textId = 0x7007; + this->unk_282 = TEXT_STATE_EVENT; + this->unk_284 = 1; + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ デモ開始! ☆☆☆☆☆ \n" VT_RST); + } else { + this->actor.textId = 0x7008; + this->unk_282 = TEXT_STATE_DONE; + osSyncPrintf(VT_FGCOL(BLUE) " ☆☆☆☆☆ 返事なし ☆☆☆☆☆ \n" VT_RST); + } + this->actionFunc = func_80A56874; + } else { + Actor_Kill(&this->actor); + } +} + +void func_80A56874(EnHeishi4* this, GlobalContext* globalCtx) { + if (this->unk_284 != 0) { + SkelAnime_Update(&this->skelAnime); + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->unk_284 == 0) { + this->actionFunc = func_80A5673C; + + } else { + this->actionFunc = func_80A56900; + } + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } +} + +void func_80A56900(EnHeishi4* this, GlobalContext* globalCtx) { + f32 frames = Animation_GetLastFrame(&gEnHeishiDyingGuardTalkAnim); + + Animation_Change(&this->skelAnime, &gEnHeishiDyingGuardTalkAnim, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A56994; +} + +void func_80A56994(EnHeishi4* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + func_80038290(globalCtx, &this->actor, &this->unk_260, &this->unk_266, this->actor.focus.pos); + if ((this->unk_282 == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + gSaveContext.infTable[6] |= 0x1000; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_80A56A50; + } +} + +void func_80A56A50(EnHeishi4* this, GlobalContext* globalCtx) { + f32 frames = Animation_GetLastFrame(&gEnHeishiDyingGuardDieAnim); + + this->unk_288 = frames; + Animation_Change(&this->skelAnime, &gEnHeishiDyingGuardDieAnim, 1.0f, 0.0f, frames, ANIMMODE_ONCE, -10.0f); + this->actionFunc = func_80A56ACC; +} + +void func_80A56ACC(EnHeishi4* this, GlobalContext* globalCtx) { + f32 currentFrame = this->skelAnime.curFrame; + + SkelAnime_Update(&this->skelAnime); + if (this->unk_288 <= currentFrame) { + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = func_80A5673C; + } +} + +void func_80A56B40(EnHeishi4* this, GlobalContext* globalCtx) { + s16 reactionOffset; + + SkelAnime_Update(&this->skelAnime); + reactionOffset = (this->type - 4); + if (reactionOffset < 0) { + reactionOffset = 0; + } + if (reactionOffset >= 3) { + reactionOffset = 1; + } + if (Text_GetFaceReaction(globalCtx, sFaceReactionSets[reactionOffset]) != 0) { + if (this->unk_2B4 == 0) { + if ((this->type == HEISHI4_AT_KAKRIKO_ENTRANCE) || (this->type == HEISHI4_AT_IMPAS_HOUSE)) { + this->actionFunc = func_80A563BC; + return; + } + if (this->type == HEISHI4_AT_MARKET_NIGHT) { + this->actionFunc = func_80A56614; + return; + } + } + } else { + if (this->unk_2B4 != 0) { + if ((this->type == HEISHI4_AT_KAKRIKO_ENTRANCE) || (this->type == HEISHI4_AT_IMPAS_HOUSE)) { + this->actionFunc = func_80A563BC; + return; + } + if (this->type == HEISHI4_AT_MARKET_NIGHT) { + this->actionFunc = func_80A56614; + return; + } + } + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if ((this->type == HEISHI4_AT_KAKRIKO_ENTRANCE) || (this->type == HEISHI4_AT_IMPAS_HOUSE)) { + this->unk_284 = 1; + this->actionFunc = func_80A563BC; + return; + } + if (this->type == HEISHI4_AT_MARKET_NIGHT) { + this->actionFunc = func_80A56614; + return; + } + } + func_8002F2F4(&this->actor, globalCtx); +} + +void EnHeishi4_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi4* this = (EnHeishi4*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + thisx->world.pos.x = this->pos.x; + thisx->world.pos.y = this->pos.y; + thisx->world.pos.z = this->pos.z; + Actor_SetFocus(thisx, this->height); + if (this->type != HEISHI4_AT_MARKET_DYING) { + this->unk_28C.unk_18 = player->actor.world.pos; + if (!LINK_IS_ADULT) { + this->unk_28C.unk_18.y = (player->actor.world.pos.y - 10.0f); + } + func_80034A14(thisx, &this->unk_28C, 2, 4); + this->unk_260 = this->unk_28C.unk_08; + this->unk_266 = this->unk_28C.unk_0E; + } + this->unk_27E += 1; + this->actionFunc(this, globalCtx); + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 10.0f, 10.0f, 30.0f, 0x1D); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnHeishi_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnHeishi4* this = (EnHeishi4*)thisx; + + if (limbIndex == 9) { + rot->x += this->unk_266.y; + } + if (limbIndex == 16) { + rot->x += this->unk_260.y; + rot->z += this->unk_260.z; + } + return false; +} + +void EnHeishi4_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHeishi4* this = (EnHeishi4*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnHeishi_OverrideLimbDraw, NULL, + this); +} diff --git a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.h b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.h new file mode 100644 index 000000000..7af01e3fb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.h @@ -0,0 +1,40 @@ +#ifndef Z_EN_HEISHI4_H +#define Z_EN_HEISHI4_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ HEISHI4_AT_KAKRIKO_ENTRANCE, + /* 0x04 */ HEISHI4_AT_IMPAS_HOUSE = 4, + /* 0x07 */ HEISHI4_AT_MARKET_DYING = 7, + /* 0x08 */ HEISHI4_AT_MARKET_NIGHT +} Heishi4Type; + +struct EnHeishi4; + +typedef void (*EnHeishi4ActionFunc)(struct EnHeishi4*, GlobalContext*); + +typedef struct EnHeishi4 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ EnHeishi4ActionFunc actionFunc; + /* 0x0260 */ Vec3s unk_260; + /* 0x0266 */ Vec3s unk_266; + /* 0x026C */ Vec3f pos; + /* 0x0278 */ f32 height; + /* 0x027C */ s16 unk_27C; + /* 0x027E */ s16 unk_27E; + /* 0x0280 */ s16 type; + /* 0x0282 */ s16 unk_282; + /* 0x0284 */ s16 unk_284; + /* 0x0288 */ f32 unk_288; + /* 0x028C */ struct_80034A14_arg1 unk_28C; + /* 0x02B4 */ u8 unk_2B4; + /* 0x02B6 */ char unk_2B6[7]; + /* 0x02BC */ ColliderCylinder collider; +} EnHeishi4; // size = 0x0308 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c b/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c new file mode 100644 index 000000000..2d887de43 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.c @@ -0,0 +1,544 @@ +/* + * File: z_en_hintnuts.c + * Overlay: ovl_En_Hintnuts + * Description: Hint Deku Scrubs (Deku Tree) + */ + +#include "z_en_hintnuts.h" +#include "objects/object_hintnuts/object_hintnuts.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnHintnuts_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHintnuts_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHintnuts_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHintnuts_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHintnuts_SetupWait(EnHintnuts* this); +void EnHintnuts_Wait(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_LookAround(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_Stand(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_ThrowNut(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_Burrow(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_BeginRun(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_BeginFreeze(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_Run(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_Talk(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_Leave(EnHintnuts* this, GlobalContext* globalCtx); +void EnHintnuts_Freeze(EnHintnuts* this, GlobalContext* globalCtx); + +const ActorInit En_Hintnuts_InitVars = { + ACTOR_EN_HINTNUTS, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_HINTNUTS, + sizeof(EnHintnuts), + (ActorFunc)EnHintnuts_Init, + (ActorFunc)EnHintnuts_Destroy, + (ActorFunc)EnHintnuts_Update, + (ActorFunc)EnHintnuts_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 18, 32, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 18, 32, MASS_HEAVY }; + +static s16 sPuzzleCounter = 0; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(gravity, -1, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x0A, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2600, ICHAIN_STOP), +}; + +void EnHintnuts_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHintnuts* this = (EnHintnuts*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + if (this->actor.params == 0xA) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + } else { + ActorShape_Init(&this->actor.shape, 0x0, ActorShadow_DrawCircle, 35.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gHintNutsSkel, &gHintNutsStandAnim, this->jointTable, + this->morphTable, 10); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + Actor_SetTextWithPrefix(globalCtx, &this->actor, (this->actor.params >> 8) & 0xFF); + this->textIdCopy = this->actor.textId; + this->actor.params &= 0xFF; + sPuzzleCounter = 0; + if (this->actor.textId == 0x109B) { + if (Flags_GetClear(globalCtx, 0x9) != 0) { + Actor_Kill(&this->actor); + return; + } + } + EnHintnuts_SetupWait(this); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_HINTNUTS, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.world.rot.y, 0, 0xA); + } +} + +void EnHintnuts_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHintnuts* this = (EnHintnuts*)thisx; + + if (this->actor.params != 0xA) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnHintnuts_HitByScrubProjectile1(EnHintnuts* this, GlobalContext* globalCtx) { + if (this->actor.textId != 0 && this->actor.category == ACTORCAT_ENEMY && + ((this->actor.params == 0) || (sPuzzleCounter == 2))) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_BG); + } +} + +void EnHintnuts_SetupWait(EnHintnuts* this) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gHintNutsUpAnim, 0.0f); + this->animFlagAndTimer = Rand_S16Offset(100, 50); + this->collider.dim.height = 5; + this->actor.world.pos = this->actor.home.pos; + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnHintnuts_Wait; +} + +void EnHintnuts_SetupLookAround(EnHintnuts* this) { + Animation_PlayLoop(&this->skelAnime, &gHintNutsLookAroundAnim); + this->animFlagAndTimer = 2; + this->actionFunc = EnHintnuts_LookAround; +} + +void EnHintnuts_SetupThrowScrubProjectile(EnHintnuts* this) { + Animation_PlayOnce(&this->skelAnime, &gHintNutsSpitAnim); + this->actionFunc = EnHintnuts_ThrowNut; +} + +void EnHintnuts_SetupStand(EnHintnuts* this) { + Animation_MorphToLoop(&this->skelAnime, &gHintNutsStandAnim, -3.0f); + if (this->actionFunc == EnHintnuts_ThrowNut) { + this->animFlagAndTimer = 2 | 0x1000; // sets timer and flag + } else { + this->animFlagAndTimer = 1; + } + this->actionFunc = EnHintnuts_Stand; +} + +void EnHintnuts_SetupBurrow(EnHintnuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gHintNutsBurrowAnim, -5.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DOWN); + this->actionFunc = EnHintnuts_Burrow; +} + +void EnHintnuts_HitByScrubProjectile2(EnHintnuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gHintNutsUnburrowAnim, -3.0f); + this->collider.dim.height = 37; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DAMAGE); + this->collider.base.acFlags &= ~AC_ON; + + if (this->actor.params > 0 && this->actor.params < 4 && this->actor.category == ACTORCAT_ENEMY) { + if (sPuzzleCounter == -4) { + sPuzzleCounter = 0; + } + if (this->actor.params == sPuzzleCounter + 1) { + sPuzzleCounter++; + } else { + if (sPuzzleCounter > 0) { + sPuzzleCounter = -sPuzzleCounter; + } + sPuzzleCounter--; + } + this->actor.flags |= ACTOR_FLAG_4; + this->actionFunc = EnHintnuts_BeginFreeze; + } else { + this->actionFunc = EnHintnuts_BeginRun; + } +} + +void EnHintnuts_SetupRun(EnHintnuts* this) { + Animation_PlayLoop(&this->skelAnime, &gHintNutsRunAnim); + this->animFlagAndTimer = 5; + this->actionFunc = EnHintnuts_Run; +} + +void EnHintnuts_SetupTalk(EnHintnuts* this) { + Animation_MorphToLoop(&this->skelAnime, &gHintNutsTalkAnim, -5.0f); + this->actionFunc = EnHintnuts_Talk; + this->actor.speedXZ = 0.0f; +} + +void EnHintnuts_SetupLeave(EnHintnuts* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &gHintNutsRunAnim, -5.0f); + this->actor.speedXZ = 3.0f; + this->animFlagAndTimer = 100; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->collider.base.ocFlags1 &= ~OC1_ON; + this->actor.flags |= ACTOR_FLAG_4; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DAMAGE); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ITEM00, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0x0, 0x0, 0x0, 0x3); // recovery heart + this->actionFunc = EnHintnuts_Leave; +} + +void EnHintnuts_SetupFreeze(EnHintnuts* this) { + Animation_PlayLoop(&this->skelAnime, &gHintNutsFreezeAnim); + this->actor.flags &= ~ACTOR_FLAG_0; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 100); + this->actor.colorFilterTimer = 1; + this->animFlagAndTimer = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_FAINT); + if (sPuzzleCounter == -3) { + func_80078884(NA_SE_SY_ERROR); + sPuzzleCounter = -4; + } + this->actionFunc = EnHintnuts_Freeze; +} + +void EnHintnuts_Wait(EnHintnuts* this, GlobalContext* globalCtx) { + s32 hasSlowPlaybackSpeed = false; + + if (this->skelAnime.playSpeed < 0.5f) { + hasSlowPlaybackSpeed = true; + } + if (hasSlowPlaybackSpeed && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + this->collider.base.acFlags |= AC_ON; + } else if (Animation_OnFrame(&this->skelAnime, 8.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + } + + this->collider.dim.height = 5.0f + ((CLAMP(this->skelAnime.curFrame, 9.0f, 12.0f) - 9.0f) * 9.0f); + if (!hasSlowPlaybackSpeed && (this->actor.xzDistToPlayer < 120.0f)) { + EnHintnuts_SetupBurrow(this); + } else if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.xzDistToPlayer < 120.0f) { + EnHintnuts_SetupBurrow(this); + } else if ((this->animFlagAndTimer == 0) && (this->actor.xzDistToPlayer > 320.0f)) { + EnHintnuts_SetupLookAround(this); + } else { + EnHintnuts_SetupStand(this); + } + } + if (hasSlowPlaybackSpeed && 160.0f < this->actor.xzDistToPlayer && fabsf(this->actor.yDistToPlayer) < 120.0f && + ((this->animFlagAndTimer == 0) || (this->actor.xzDistToPlayer < 480.0f))) { + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnHintnuts_LookAround(EnHintnuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->animFlagAndTimer != 0) { + this->animFlagAndTimer--; + } + if ((this->actor.xzDistToPlayer < 120.0f) || (this->animFlagAndTimer == 0)) { + EnHintnuts_SetupBurrow(this); + } +} + +void EnHintnuts_Stand(EnHintnuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->animFlagAndTimer != 0) { + this->animFlagAndTimer--; + } + if (!(this->animFlagAndTimer & 0x1000)) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + } + if (this->actor.xzDistToPlayer < 120.0f || this->animFlagAndTimer == 0x1000) { + EnHintnuts_SetupBurrow(this); + } else if (this->animFlagAndTimer == 0) { + EnHintnuts_SetupThrowScrubProjectile(this); + } +} + +void EnHintnuts_ThrowNut(EnHintnuts* this, GlobalContext* globalCtx) { + Vec3f nutPos; + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + if (this->actor.xzDistToPlayer < 120.0f) { + EnHintnuts_SetupBurrow(this); + } else if (SkelAnime_Update(&this->skelAnime)) { + EnHintnuts_SetupStand(this); + } else if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + nutPos.x = this->actor.world.pos.x + (Math_SinS(this->actor.shape.rot.y) * 23.0f); + nutPos.y = this->actor.world.pos.y + 12.0f; + nutPos.z = this->actor.world.pos.z + (Math_CosS(this->actor.shape.rot.y) * 23.0f); + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_NUTSBALL, nutPos.x, nutPos.y, nutPos.z, + this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, 1) != NULL) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_THROW); + } + } +} + +void EnHintnuts_Burrow(EnHintnuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnHintnuts_SetupWait(this); + } else { + this->collider.dim.height = 5.0f + ((3.0f - CLAMP(this->skelAnime.curFrame, 1.0f, 3.0f)) * 12.0f); + } + if (Animation_OnFrame(&this->skelAnime, 4.0f)) { + this->collider.base.acFlags &= ~AC_ON; + } + + Math_ApproachF(&this->actor.world.pos.x, this->actor.home.pos.x, 0.5f, 3.0f); + Math_ApproachF(&this->actor.world.pos.z, this->actor.home.pos.z, 0.5f, 3.0f); +} + +void EnHintnuts_BeginRun(EnHintnuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_196 = this->actor.yawTowardsPlayer + 0x8000; + EnHintnuts_SetupRun(this); + } + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); +} + +void EnHintnuts_BeginFreeze(EnHintnuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnHintnuts_SetupFreeze(this); + } +} + +void EnHintnuts_CheckProximity(EnHintnuts* this, GlobalContext* globalCtx) { + if (this->actor.category != ACTORCAT_ENEMY) { + if ((this->collider.base.ocFlags1 & OC1_HIT) || this->actor.isTargeted) { + this->actor.flags |= ACTOR_FLAG_16; + } else { + this->actor.flags &= ~ACTOR_FLAG_16; + } + if (this->actor.xzDistToPlayer < 130.0f) { + this->actor.textId = this->textIdCopy; + func_8002F2F4(&this->actor, globalCtx); + } + } +} + +void EnHintnuts_Run(EnHintnuts* this, GlobalContext* globalCtx) { + s32 temp_ret; + s16 diffRotInit; + s16 diffRot; + f32 phi_f0; + + SkelAnime_Update(&this->skelAnime); + temp_ret = Animation_OnFrame(&this->skelAnime, 0.0f); + if (temp_ret != 0 && this->animFlagAndTimer != 0) { + this->animFlagAndTimer--; + } + if ((temp_ret != 0) || (Animation_OnFrame(&this->skelAnime, 6.0f))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + + Math_StepToF(&this->actor.speedXZ, 7.5f, 1.0f); + if (Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_196, 1, 0xE38, 0xB6) == 0) { + if (this->actor.bgCheckFlags & 0x20) { + this->unk_196 = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + } else if (this->actor.bgCheckFlags & 8) { + this->unk_196 = this->actor.wallYaw; + } else if (this->animFlagAndTimer == 0) { + diffRotInit = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + diffRot = diffRotInit - this->actor.yawTowardsPlayer; + if (ABS(diffRot) >= 0x2001) { + this->unk_196 = diffRotInit; + } else { + phi_f0 = (0.0f <= (f32)diffRot) ? 1.0f : -1.0f; + this->unk_196 = (s16)((phi_f0 * -8192.0f) + (f32)this->actor.yawTowardsPlayer); + } + } else { + this->unk_196 = (s16)(this->actor.yawTowardsPlayer + 0x8000); + } + } + + this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + EnHintnuts_SetupTalk(this); + } else if (this->animFlagAndTimer == 0 && Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) < 20.0f && + fabsf(this->actor.world.pos.y - this->actor.home.pos.y) < 2.0f) { + this->actor.speedXZ = 0.0f; + if (this->actor.category == ACTORCAT_BG) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_16); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + } + EnHintnuts_SetupBurrow(this); + } else { + EnHintnuts_CheckProximity(this, globalCtx); + } +} + +void EnHintnuts_Talk(EnHintnuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x3, 0x400, 0x100); + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) { + EnHintnuts_SetupLeave(this, globalCtx); + } +} + +void EnHintnuts_Leave(EnHintnuts* this, GlobalContext* globalCtx) { + s16 temp_a1; + + SkelAnime_Update(&this->skelAnime); + if (this->animFlagAndTimer != 0) { + this->animFlagAndTimer--; + } + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 6.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_WALK); + } + if (this->actor.bgCheckFlags & 8) { + temp_a1 = this->actor.wallYaw; + } else { + temp_a1 = this->actor.yawTowardsPlayer - Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - 0x8000; + if (ABS(temp_a1) >= 0x4001) { + temp_a1 = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000; + } else { + temp_a1 = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - (temp_a1 >> 1) + 0x8000; + } + } + Math_ScaledStepToS(&this->actor.shape.rot.y, temp_a1, 0x800); + this->actor.world.rot.y = this->actor.shape.rot.y; + if ((this->animFlagAndTimer == 0) || (this->actor.projectedPos.z < 0.0f)) { + Message_CloseTextbox(globalCtx); + if (this->actor.params == 3) { + Flags_SetClear(globalCtx, this->actor.room); + sPuzzleCounter = 3; + } + if (this->actor.child != NULL) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, this->actor.child, ACTORCAT_PROP); + } + Actor_Kill(&this->actor); + } +} + +void EnHintnuts_Freeze(EnHintnuts* this, GlobalContext* globalCtx) { + this->actor.colorFilterTimer = 1; + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_FAINT); + } + if (this->animFlagAndTimer == 0) { + if (sPuzzleCounter == 3) { + if (this->actor.child != NULL) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, this->actor.child, ACTORCAT_PROP); + } + this->animFlagAndTimer = 1; + } else if (sPuzzleCounter == -4) { + this->animFlagAndTimer = 2; + } + } else if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y - 35.0f, 7.0f) != 0) { + if (this->animFlagAndTimer == 1) { + Actor_Kill(&this->actor); + } else { + this->actor.flags |= ACTOR_FLAG_0; + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.colChkInfo.health = sColChkInfoInit.health; + this->actor.colorFilterTimer = 0; + EnHintnuts_SetupWait(this); + } + } +} + +void EnHintnuts_ColliderCheck(EnHintnuts* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if (this->collider.base.ac->id != ACTOR_EN_NUTSBALL) { + EnHintnuts_SetupBurrow(this); + } else { + EnHintnuts_HitByScrubProjectile1(this, globalCtx); + EnHintnuts_HitByScrubProjectile2(this); + } + } else if (globalCtx->actorCtx.unk_02 != 0) { + EnHintnuts_HitByScrubProjectile1(this, globalCtx); + EnHintnuts_HitByScrubProjectile2(this); + } +} + +void EnHintnuts_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHintnuts* this = (EnHintnuts*)thisx; + s32 pad; + + if (this->actor.params != 0xA) { + EnHintnuts_ColliderCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + if (this->actionFunc != EnHintnuts_Freeze && this->actionFunc != EnHintnuts_BeginFreeze) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, this->collider.dim.radius, + this->collider.dim.height, 0x1D); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actionFunc == EnHintnuts_Wait) { + Actor_SetFocus(&this->actor, this->skelAnime.curFrame); + } else if (this->actionFunc == EnHintnuts_Burrow) { + Actor_SetFocus(&this->actor, + 20.0f - ((this->skelAnime.curFrame * 20.0f) / Animation_GetLastFrame(&gHintNutsBurrowAnim))); + } else { + Actor_SetFocus(&this->actor, 20.0f); + } + } +} + +s32 EnHintnuts_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + Vec3f vec; + f32 curFrame; + EnHintnuts* this = (EnHintnuts*)thisx; + + if (limbIndex == 5 && this->actionFunc == EnHintnuts_ThrowNut) { + curFrame = this->skelAnime.curFrame; + if (curFrame <= 6.0f) { + vec.y = 1.0f - (curFrame * 0.0833f); + vec.z = 1.0f + (curFrame * 0.1167f); + vec.x = 1.0f + (curFrame * 0.1167f); + } else if (curFrame <= 7.0f) { + curFrame -= 6.0f; + vec.y = 0.5f + curFrame; + vec.z = 1.7f - (curFrame * 0.7f); + vec.x = 1.7f - (curFrame * 0.7f); + } else if (curFrame <= 10.0f) { + vec.y = 1.5f - ((curFrame - 7.0f) * 0.1667f); + vec.z = 1.0f; + vec.x = 1.0f; + } else { + return false; + } + Matrix_Scale(vec.x, vec.y, vec.z, MTXMODE_APPLY); + } + return false; +} + +void EnHintnuts_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHintnuts* this = (EnHintnuts*)thisx; + + if (this->actor.params == 0xA) { + Gfx_DrawDListOpa(globalCtx, gHintNutsFlowerDL); + } else { + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnHintnuts_OverrideLimbDraw, + NULL, this); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.h b/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.h new file mode 100644 index 000000000..f474591b3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hintnuts/z_en_hintnuts.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_HINTNUTS_H +#define Z_EN_HINTNUTS_H + +#include "ultra64.h" +#include "global.h" + +struct EnHintnuts; + +typedef void (*EnHintnutsActionFunc)(struct EnHintnuts*, GlobalContext*); + +typedef struct EnHintnuts { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnHintnutsActionFunc actionFunc; + /* 0x0194 */ s16 animFlagAndTimer; // 0x1000 bit denotes that projectile has been thrown + /* 0x0196 */ s16 unk_196; + /* 0x0198 */ u16 textIdCopy; + /* 0x019A */ Vec3s jointTable[10]; + /* 0x01D6 */ Vec3s morphTable[10]; + /* 0x0214 */ ColliderCylinder collider; +} EnHintnuts; // size = 0x0260 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c b/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c new file mode 100644 index 000000000..07223a5df --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c @@ -0,0 +1,359 @@ +#include "z_en_holl.h" + +#define FLAGS ACTOR_FLAG_4 + +// Horizontal Plane parameters + +#define PLANE_Y_MIN -50.0f +#define PLANE_Y_MAX 200.0f + +#define PLANE_HALFWIDTH 100.0f +#define PLANE_HALFWIDTH_2 200.0f + +void EnHoll_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHoll_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHoll_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHoll_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHoll_NextAction(EnHoll* this, GlobalContext* globalCtx); +void func_80A58DD4(EnHoll* this, GlobalContext* globalCtx); +void func_80A59014(EnHoll* this, GlobalContext* globalCtx); +void func_80A591C0(EnHoll* this, GlobalContext* globalCtx); +void func_80A593A4(EnHoll* this, GlobalContext* globalCtx); +void func_80A59520(EnHoll* this, GlobalContext* globalCtx); +void func_80A59618(EnHoll* this, GlobalContext* globalCtx); + +const ActorInit En_Holl_InitVars = { + ACTOR_EN_HOLL, + ACTORCAT_DOOR, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnHoll), + (ActorFunc)EnHoll_Init, + (ActorFunc)EnHoll_Destroy, + (ActorFunc)EnHoll_Update, + (ActorFunc)EnHoll_Draw, + NULL, +}; + +static EnHollActionFunc sActionFuncs[] = { + func_80A58DD4, func_80A591C0, func_80A59520, func_80A59618, func_80A59014, func_80A593A4, func_80A59014, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), +}; + +/** + * These are all absolute distances in the relative z direction. That is, moving + * towards or away from the "face" of the loading plane regardless of orientation. + * Moving within these distances of the load plane have the following effects: + * [0] : Load the room on this side of the loading plane if not already loaded + * [1] : Load the room on the other side of the loading plane + * [2] : Fade Region (opaque -> transparent if approaching, transparent -> opaque if receding) + * [3] : Transparent Region + * + * When traversing a loading plane of this kind, it attempts to: + * Load Current Room (fails as it is already loaded) + * Load Next Room + * Load Previous Room + * Load Next Room + * + * @bug The striped nature of loading planes can cause some actors to unload due to + * conflicting Object Lists between the two rooms + * + * @bug If you can get around to the other side of the loading plane without triggering it, + * you can load the room on the other side multiple times + */ +static f32 sHorizTriggerDists[2][4] = { + { 200.0f, 150.0f, 100.0f, 50.0f }, + { 100.0f, 75.0f, 50.0f, 25.0f }, +}; + +void EnHoll_SetupAction(EnHoll* this, EnHollActionFunc func) { + this->actionFunc = func; +} + +s32 EnHoll_IsKokiriSetup8() { + return gSaveContext.entranceIndex == 0x00EE && gSaveContext.sceneSetupIndex == 8; +} + +void EnHoll_ChooseAction(EnHoll* this) { + s32 action; + + action = (this->actor.params >> 6) & 7; + EnHoll_SetupAction(this, sActionFuncs[action]); + if (action != 0) { + this->actor.draw = NULL; + } else { + this->planeAlpha = 255; + } +} + +void EnHoll_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHoll* this = (EnHoll*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + EnHoll_ChooseAction(this); + this->unk_14F = 0; +} + +void EnHoll_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 transitionActorIdx = (u16)thisx->params >> 0xA; + TransitionActorEntry* transitionEntry = &globalCtx->transiActorCtx.list[transitionActorIdx]; + + transitionEntry->id = -transitionEntry->id; +} + +void EnHoll_SwapRooms(GlobalContext* globalCtx) { + Room tempRoom; + RoomContext* roomCtx = &globalCtx->roomCtx; + + tempRoom = roomCtx->curRoom; + roomCtx->curRoom = roomCtx->prevRoom; + roomCtx->prevRoom = tempRoom; + globalCtx->roomCtx.unk_30 ^= 1; +} + +// Horizontal Planes +void func_80A58DD4(EnHoll* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 phi_t0 = ((globalCtx->sceneNum == SCENE_JYASINZOU) ? 1 : 0) & 0xFFFFFFFF; + Vec3f vec; + f32 absZ; + s32 transitionActorIdx; + + func_8002DBD0(&this->actor, &vec, &player->actor.world.pos); + this->side = (vec.z < 0.0f) ? 0 : 1; + absZ = fabsf(vec.z); + if (vec.y > PLANE_Y_MIN && vec.y < PLANE_Y_MAX && fabsf(vec.x) < PLANE_HALFWIDTH && + absZ < sHorizTriggerDists[phi_t0][0]) { + transitionActorIdx = (u16)this->actor.params >> 0xA; + if (absZ > sHorizTriggerDists[phi_t0][1]) { + if (globalCtx->roomCtx.prevRoom.num >= 0 && globalCtx->roomCtx.status == 0) { + this->actor.room = globalCtx->transiActorCtx.list[transitionActorIdx].sides[this->side].room; + EnHoll_SwapRooms(globalCtx); + func_80097534(globalCtx, &globalCtx->roomCtx); + } + } else { + this->actor.room = globalCtx->transiActorCtx.list[transitionActorIdx].sides[this->side ^ 1].room; + if (globalCtx->roomCtx.prevRoom.num < 0) { + func_8009728C(globalCtx, &globalCtx->roomCtx, this->actor.room); + } else { + this->planeAlpha = (255.0f / (sHorizTriggerDists[phi_t0][2] - sHorizTriggerDists[phi_t0][3])) * + (absZ - sHorizTriggerDists[phi_t0][3]); + this->planeAlpha = CLAMP(this->planeAlpha, 0, 255); + if (globalCtx->roomCtx.curRoom.num != this->actor.room) { + EnHoll_SwapRooms(globalCtx); + } + } + } + } +} + +// Horizontal Planes +void func_80A59014(EnHoll* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 useViewEye = gDbgCamEnabled || globalCtx->csCtx.state != CS_STATE_IDLE; + Vec3f vec; + s32 temp; + f32 planeHalfWidth; + f32 absZ; + + func_8002DBD0(&this->actor, &vec, (useViewEye) ? &globalCtx->view.eye : &player->actor.world.pos); + planeHalfWidth = (((this->actor.params >> 6) & 7) == 6) ? PLANE_HALFWIDTH : PLANE_HALFWIDTH_2; + + temp = EnHoll_IsKokiriSetup8(); + if (temp || (PLANE_Y_MIN < vec.y && vec.y < PLANE_Y_MAX && fabsf(vec.x) < planeHalfWidth && + (absZ = fabsf(vec.z), 100.0f > absZ && absZ > 50.0f))) { + s32 transitionActorIdx = (u16)this->actor.params >> 0xA; + s32 side = (vec.z < 0.0f) ? 0 : 1; + TransitionActorEntry* transitionEntry = &globalCtx->transiActorCtx.list[transitionActorIdx]; + s32 room = transitionEntry->sides[side].room; + + this->actor.room = room; + if (temp) {} + if (this->actor.room != globalCtx->roomCtx.curRoom.num) { + if (room) {} + if (func_8009728C(globalCtx, &globalCtx->roomCtx, this->actor.room)) { + EnHoll_SetupAction(this, EnHoll_NextAction); + } + } + } +} + +// Vertical Planes +void func_80A591C0(EnHoll* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 absY = fabsf(this->actor.yDistToPlayer); + s32 transitionActorIdx; + + if (this->actor.xzDistToPlayer < 500.0f && absY < 700.0f) { + transitionActorIdx = (u16)this->actor.params >> 0xA; + if (absY < 95.0f) { + globalCtx->unk_11E18 = 0xFF; + } else if (absY > 605.0f) { + globalCtx->unk_11E18 = 0; + } else { + globalCtx->unk_11E18 = (s16)(605.0f - absY) * 0.5f; + } + if (absY < 95.0f) { + this->actor.room = globalCtx->transiActorCtx.list[transitionActorIdx].sides[1].room; + Math_SmoothStepToF(&player->actor.world.pos.x, this->actor.world.pos.x, 1.0f, 50.0f, 10.0f); + Math_SmoothStepToF(&player->actor.world.pos.z, this->actor.world.pos.z, 1.0f, 50.0f, 10.0f); + if (this->actor.room != globalCtx->roomCtx.curRoom.num && + func_8009728C(globalCtx, &globalCtx->roomCtx, this->actor.room) != 0) { + EnHoll_SetupAction(this, EnHoll_NextAction); + this->unk_14F = 1; + player->actor.speedXZ = 0.0f; + } + } + } else if (this->unk_14F != 0) { + globalCtx->unk_11E18 = 0; + this->unk_14F = 0; + } +} + +// Vertical Planes +void func_80A593A4(EnHoll* this, GlobalContext* globalCtx) { + f32 absY; + s32 side; + s32 transitionActorIdx; + + if ((this->actor.xzDistToPlayer < 120.0f) && (absY = fabsf(this->actor.yDistToPlayer), absY < 200.0f)) { + if (absY < 50.0f) { + globalCtx->unk_11E18 = 0xFF; + } else { + globalCtx->unk_11E18 = (200.0f - absY) * 1.7f; + } + if (absY > 50.0f) { + transitionActorIdx = (u16)this->actor.params >> 0xA; + side = (0.0f < this->actor.yDistToPlayer) ? 0 : 1; + this->actor.room = globalCtx->transiActorCtx.list[transitionActorIdx].sides[side].room; + if (this->actor.room != globalCtx->roomCtx.curRoom.num && + func_8009728C(globalCtx, &globalCtx->roomCtx, this->actor.room) != 0) { + EnHoll_SetupAction(this, EnHoll_NextAction); + this->unk_14F = 1; + } + } + } else if (this->unk_14F != 0) { + this->unk_14F = 0; + globalCtx->unk_11E18 = 0; + } +} + +// Vertical Planes +void func_80A59520(EnHoll* this, GlobalContext* globalCtx) { + f32 absY; + s8 side; + s32 transitionActorIdx; + + if (this->actor.xzDistToPlayer < 120.0f) { + absY = fabsf(this->actor.yDistToPlayer); + if (absY < 200.0f && absY > 50.0f) { + transitionActorIdx = (u16)this->actor.params >> 0xA; + side = (0.0f < this->actor.yDistToPlayer) ? 0 : 1; + this->actor.room = globalCtx->transiActorCtx.list[transitionActorIdx].sides[side].room; + if (this->actor.room != globalCtx->roomCtx.curRoom.num && + func_8009728C(globalCtx, &globalCtx->roomCtx, this->actor.room) != 0) { + EnHoll_SetupAction(this, EnHoll_NextAction); + } + } + } +} + +// Horizontal Planes +void func_80A59618(EnHoll* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f vec; + f32 absZ; + s32 side; + s32 transitionActorIdx; + + if (!Flags_GetSwitch(globalCtx, this->actor.params & 0x3F)) { + if (this->unk_14F != 0) { + globalCtx->unk_11E18 = 0; + this->unk_14F = 0; + } + } else { + func_8002DBD0(&this->actor, &vec, &player->actor.world.pos); + absZ = fabsf(vec.z); + if (PLANE_Y_MIN < vec.y && vec.y < PLANE_Y_MAX && fabsf(vec.x) < PLANE_HALFWIDTH_2 && absZ < 100.0f) { + this->unk_14F = 1; + transitionActorIdx = (u16)this->actor.params >> 0xA; + globalCtx->unk_11E18 = 0xFF - (s32)((absZ - 50.0f) * 5.9f); + if (globalCtx->unk_11E18 >= 0x100) { + globalCtx->unk_11E18 = 0xFF; + } else if (globalCtx->unk_11E18 < 0) { + globalCtx->unk_11E18 = 0; + } + if (absZ < 50.0f) { + side = (vec.z < 0.0f) ? 0 : 1; + this->actor.room = globalCtx->transiActorCtx.list[transitionActorIdx].sides[side].room; + if (this->actor.room != globalCtx->roomCtx.curRoom.num && + func_8009728C(globalCtx, &globalCtx->roomCtx, this->actor.room) != 0) { + EnHoll_SetupAction(this, EnHoll_NextAction); + } + } + } else if (this->unk_14F != 0) { + globalCtx->unk_11E18 = 0; + this->unk_14F = 0; + } + } +} + +void EnHoll_NextAction(EnHoll* this, GlobalContext* globalCtx) { + if (!EnHoll_IsKokiriSetup8() && globalCtx->roomCtx.status == 0) { + func_80097534(globalCtx, &globalCtx->roomCtx); + if (globalCtx->unk_11E18 == 0) { + this->unk_14F = 0; + } + EnHoll_ChooseAction(this); + } +} + +void EnHoll_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHoll* this = (EnHoll*)thisx; + + this->actionFunc(this, globalCtx); +} + +#include "overlays/ovl_En_Holl/ovl_En_Holl.h" + +void EnHoll_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHoll* this = (EnHoll*)thisx; + Gfx* gfxP; + u32 setupDlIdx; + + // Only draw the plane if not invisible + if (this->planeAlpha != 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_holl.c", 805); + + if (this->planeAlpha == 255) { + gfxP = POLY_OPA_DISP; + setupDlIdx = 37; + } else { + gfxP = POLY_XLU_DISP; + setupDlIdx = 0; + } + gfxP = Gfx_CallSetupDL(gfxP, setupDlIdx); + if (this->side == 0) { + Matrix_RotateY(M_PI, MTXMODE_APPLY); + } + + gSPMatrix(gfxP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_holl.c", 824), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(gfxP++, 0, 0, 0, 0, 0, (u8)this->planeAlpha); + gSPDisplayList(gfxP++, sPlaneDL); + + if (this->planeAlpha == 255) { + POLY_OPA_DISP = gfxP; + } else { + POLY_XLU_DISP = gfxP; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_holl.c", 831); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.h b/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.h new file mode 100644 index 000000000..b163d0fec --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.h @@ -0,0 +1,19 @@ +#ifndef Z_EN_HOLL_H +#define Z_EN_HOLL_H + +#include "ultra64.h" +#include "global.h" + +struct EnHoll; + +typedef void (*EnHollActionFunc)(struct EnHoll*, GlobalContext*); + +typedef struct EnHoll { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 planeAlpha; + /* 0x014E */ u8 side; + /* 0x014F */ u8 unk_14F; + /* 0x0150 */ EnHollActionFunc actionFunc; +} EnHoll; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Honotrap/z_en_honotrap.c b/soh/src/overlays/actors/ovl_En_Honotrap/z_en_honotrap.c new file mode 100644 index 000000000..9d452425f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Honotrap/z_en_honotrap.c @@ -0,0 +1,542 @@ +/** + * File: z_en_honotrap.c + * Overlay: ovl_En_Honotrap + * Description: Fake eye switches and Dampe flames + */ + +#include "z_en_honotrap.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +#define HONOTRAP_AT_ACTIVE (1 << 0) +#define HONOTRAP_AC_ACTIVE (1 << 1) +#define HONOTRAP_OC_ACTIVE (1 << 2) + +typedef enum { + /* 0 */ HONOTRAP_EYE_OPEN, + /* 1 */ HONOTRAP_EYE_HALF, + /* 2 */ HONOTRAP_EYE_CLOSE, + /* 3 */ HONOTRAP_EYE_SHUT +} EnHonotrapEyeState; + +void EnHonotrap_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHonotrap_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHonotrap_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHonotrap_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHonotrap_SetupEyeIdle(EnHonotrap* this); +void EnHonotrap_EyeIdle(EnHonotrap* this, GlobalContext* globalCtx); +void EnHonotrap_SetupEyeOpen(EnHonotrap* this); +void EnHonotrap_EyeOpen(EnHonotrap* this, GlobalContext* globalCtx); +void EnHonotrap_SetupEyeAttack(EnHonotrap* this); +void EnHonotrap_EyeAttack(EnHonotrap* this, GlobalContext* globalCtx); +void EnHonotrap_SetupEyeClose(EnHonotrap* this); +void EnHonotrap_EyeClose(EnHonotrap* this, GlobalContext* globalCtx); + +void EnHonotrap_SetupFlame(EnHonotrap* this); +void EnHonotrap_Flame(EnHonotrap* this, GlobalContext* globalCtx); +void EnHonotrap_SetupFlameDrop(EnHonotrap* this); +void EnHonotrap_FlameDrop(EnHonotrap* this, GlobalContext* globalCtx); + +void EnHonotrap_SetupFlameMove(EnHonotrap* this); +void EnHonotrap_FlameMove(EnHonotrap* this, GlobalContext* globalCtx); +void EnHonotrap_SetupFlameChase(EnHonotrap* this); +void EnHonotrap_FlameChase(EnHonotrap* this, GlobalContext* globalCtx); +void EnHonotrap_SetupFlameVanish(EnHonotrap* this); +void EnHonotrap_FlameVanish(EnHonotrap* this, GlobalContext* globalCtx); + +const ActorInit En_Honotrap_InitVars = { + ACTOR_EN_HONOTRAP, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(EnHonotrap), + (ActorFunc)EnHonotrap_Init, + (ActorFunc)EnHonotrap_Destroy, + (ActorFunc)EnHonotrap_Update, + (ActorFunc)EnHonotrap_Draw, + NULL, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK4, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 23.0f, 8.5f }, { -23.0f, 0.0f, 8.5f }, { 0.0f, -23.0f, 8.5f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 23.0f, 8.5f }, { 0.0f, -23.0f, 8.5f }, { 23.0f, 0.0f, 8.5f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00100000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 10, 25, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 9, 23, 1 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void EnHonotrap_FlameCollisionCheck(EnHonotrap* this, GlobalContext* globalCtx) { + s32 pad[3]; + + Collider_UpdateCylinder(&this->actor, &this->collider.cyl); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.cyl.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.cyl.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.cyl.base); + this->colChkFlags |= HONOTRAP_AT_ACTIVE; + this->colChkFlags |= HONOTRAP_AC_ACTIVE; + this->colChkFlags |= HONOTRAP_OC_ACTIVE; +} + +void EnHonotrap_GetNormal(Vec3f* normal, Vec3f* vec) { + f32 mag = Math3D_Vec3fMagnitude(vec); + + if (mag < 0.001f) { + osSyncPrintf("Warning : vector size zero (%s %d)\n", "../z_en_honotrap.c", 328, normal); + + normal->x = normal->y = 0.0f; + normal->z = 1.0f; + } else { + normal->x = vec->x * (1.0f / mag); + normal->y = vec->y * (1.0f / mag); + normal->z = vec->z * (1.0f / mag); + } +} + +void EnHonotrap_InitEye(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHonotrap* this = (EnHonotrap*)thisx; + s32 i; + s32 j; + Vec3f* vtx; + Vec3f triangle[3]; + f32 cos; + f32 sin; + + Actor_SetScale(thisx, 0.1f); + sin = Math_SinS(thisx->home.rot.y); + cos = Math_CosS(thisx->home.rot.y); + Collider_InitTris(globalCtx, &this->collider.tris); + Collider_SetTris(globalCtx, &this->collider.tris, thisx, &sTrisInit, this->collider.elements); + + for (i = 0; i < 2; i++) { + for (j = 0, vtx = triangle; j < 3; j++, vtx++) { + Vec3f* baseVtx = &sTrisInit.elements[i].dim.vtx[j]; + + vtx->x = baseVtx->z * sin + baseVtx->x * cos; + vtx->y = baseVtx->y; + vtx->z = baseVtx->z * cos - baseVtx->x * sin; + Math_Vec3f_Sum(vtx, &thisx->world.pos, vtx); + } + Collider_SetTrisVertices(&this->collider.tris, i, &triangle[0], &triangle[1], &triangle[2]); + } + EnHonotrap_SetupEyeIdle(this); + Actor_SetFocus(thisx, 0.0f); +} + +void EnHonotrap_InitFlame(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHonotrap* this = (EnHonotrap*)thisx; + + Actor_SetScale(&this->actor, 0.0001f); + Collider_InitCylinder(globalCtx, &this->collider.cyl); + Collider_SetCylinder(globalCtx, &this->collider.cyl, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider.cyl); + this->actor.minVelocityY = -1.0f; + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->actor.shape.shadowAlpha = 128; + this->targetPos = GET_PLAYER(globalCtx)->actor.world.pos; + this->targetPos.y += 10.0f; + this->flameScroll = Rand_ZeroOne() * 511.0f; + EnHonotrap_SetupFlame(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FLAME_IGNITION); + if (this->actor.params == HONOTRAP_FLAME_DROP) { + this->actor.room = -1; + this->collider.cyl.dim.radius = 12; + this->collider.cyl.dim.height = 30; + this->actor.shape.yOffset = -1000.0f; + } +} + +void EnHonotrap_Init(Actor* thisx, GlobalContext* globalCtx) { + Actor_ProcessInitChain(thisx, sInitChain); + if (thisx->params == HONOTRAP_EYE) { + EnHonotrap_InitEye(thisx, globalCtx); + } else { + EnHonotrap_InitFlame(thisx, globalCtx); + } +} + +void EnHonotrap_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHonotrap* this = (EnHonotrap*)thisx; + + if (this->actor.params == HONOTRAP_EYE) { + Collider_DestroyTris(globalCtx, &this->collider.tris); + } else { + Collider_DestroyCylinder(globalCtx, &this->collider.cyl); + } +} + +void EnHonotrap_SetupEyeIdle(EnHonotrap* this) { + this->actionFunc = EnHonotrap_EyeIdle; + this->eyeState = HONOTRAP_EYE_SHUT; +} + +void EnHonotrap_EyeIdle(EnHonotrap* this, GlobalContext* globalCtx) { + if (this->actor.child != NULL) { + this->timer = 200; + } else if ((this->timer <= 0) && (this->actor.xzDistToPlayer < 750.0f) && (0.0f > this->actor.yDistToPlayer) && + (this->actor.yDistToPlayer > -700.0f) && + (-0x4000 < (this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) && + ((this->actor.yawTowardsPlayer - this->actor.shape.rot.y) < 0x4000)) { + EnHonotrap_SetupEyeOpen(this); + } +} + +void EnHonotrap_SetupEyeOpen(EnHonotrap* this) { + this->actionFunc = EnHonotrap_EyeOpen; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x28); + this->timer = 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_RED_EYE); +} + +void EnHonotrap_EyeOpen(EnHonotrap* this, GlobalContext* globalCtx) { + f32 cos; + f32 sin; + + this->eyeState--; + if (this->eyeState <= HONOTRAP_EYE_OPEN) { + EnHonotrap_SetupEyeAttack(this); + sin = Math_SinS(this->actor.shape.rot.y); + cos = Math_CosS(this->actor.shape.rot.y); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_HONOTRAP, + (sin * 12.0f) + this->actor.home.pos.x, this->actor.home.pos.y - 10.0f, + (cos * 12.0f) + this->actor.home.pos.z, this->actor.home.rot.x, this->actor.home.rot.y, + this->actor.home.rot.z, HONOTRAP_FLAME_MOVE); + } +} + +void EnHonotrap_SetupEyeAttack(EnHonotrap* this) { + this->actionFunc = EnHonotrap_EyeAttack; + this->eyeState = HONOTRAP_EYE_OPEN; +} + +void EnHonotrap_EyeAttack(EnHonotrap* this, GlobalContext* globalCtx) { + if (this->timer <= 0) { + EnHonotrap_SetupEyeClose(this); + } +} + +void EnHonotrap_SetupEyeClose(EnHonotrap* this) { + this->actionFunc = EnHonotrap_EyeClose; +} + +void EnHonotrap_EyeClose(EnHonotrap* this, GlobalContext* globalCtx) { + this->eyeState++; + if (this->eyeState >= HONOTRAP_EYE_SHUT) { + EnHonotrap_SetupEyeIdle(this); + this->timer = 200; + } +} + +void EnHonotrap_SetupFlame(EnHonotrap* this) { + this->actionFunc = EnHonotrap_Flame; +} + +void EnHonotrap_Flame(EnHonotrap* this, GlobalContext* globalCtx) { + s32 pad; + s32 ready = + Math_StepToF(&this->actor.scale.x, (this->actor.params == HONOTRAP_FLAME_MOVE) ? 0.004f : 0.0048f, 0.0006f); + + this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; + if (ready) { + if (this->actor.params == HONOTRAP_FLAME_MOVE) { + EnHonotrap_SetupFlameMove(this); + } else { + EnHonotrap_SetupFlameDrop(this); + } + } +} + +void EnHonotrap_SetupFlameDrop(EnHonotrap* this) { + this->timer = 40; + this->actor.velocity.y = 1.0f; + this->actor.velocity.x = 2.0f * Math_SinS(this->actor.world.rot.y); + this->actor.velocity.z = 2.0f * Math_CosS(this->actor.world.rot.y); + this->actionFunc = EnHonotrap_FlameDrop; +} + +void EnHonotrap_FlameDrop(EnHonotrap* this, GlobalContext* globalCtx) { + if ((this->collider.cyl.base.atFlags & AT_HIT) || (this->timer <= 0)) { + if ((this->collider.cyl.base.atFlags & AT_HIT) && !(this->collider.cyl.base.atFlags & AT_BOUNCED)) { + func_8002F71C(globalCtx, &this->actor, 5.0f, this->actor.yawTowardsPlayer, 0.0f); + } + this->actor.velocity.x = this->actor.velocity.y = this->actor.velocity.z = 0.0f; + EnHonotrap_SetupFlameVanish(this); + } else { + if (this->actor.velocity.y > 0.0f) { + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.z += this->actor.velocity.z; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.0f, 12.0f, 0.0f, 5); + } + if (!Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight + 1.0f, this->actor.velocity.y)) { + this->actor.velocity.y += 1.0f; + } else { + this->actor.velocity.y = 0.0f; + } + EnHonotrap_FlameCollisionCheck(this, globalCtx); + } +} + +void EnHonotrap_SetupFlameMove(EnHonotrap* this) { + f32 distFrac; + + this->actionFunc = EnHonotrap_FlameMove; + + distFrac = 1.0f / (Actor_WorldDistXYZToPoint(&this->actor, &this->targetPos) + 1.0f); + this->actor.velocity.x = (this->targetPos.x - this->actor.world.pos.x) * distFrac; + this->actor.velocity.y = (this->targetPos.y - this->actor.world.pos.y) * distFrac; + this->actor.velocity.z = (this->targetPos.z - this->actor.world.pos.z) * distFrac; + this->speedMod = 0.0f; + + this->timer = 160; +} + +void EnHonotrap_FlameMove(EnHonotrap* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f speed; + s32 ready; + + Math_StepToF(&this->speedMod, 13.0f, 0.5f); + speed.x = fabsf(this->speedMod * this->actor.velocity.x); + speed.y = fabsf(this->speedMod * this->actor.velocity.y); + speed.z = fabsf(this->speedMod * this->actor.velocity.z); + ready = true; + ready &= Math_StepToF(&this->actor.world.pos.x, this->targetPos.x, speed.x); + ready &= Math_StepToF(&this->actor.world.pos.y, this->targetPos.y, speed.y); + ready &= Math_StepToF(&this->actor.world.pos.z, this->targetPos.z, speed.z); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.0f, 10.0f, 0.0f, 0x1D); + + if (this->collider.tris.base.atFlags & AT_BOUNCED) { + Player* player = GET_PLAYER(globalCtx); + Vec3f shieldNorm; + Vec3f tempVel; + Vec3f shieldVec; + + shieldVec.x = -player->shieldMf.xz; + shieldVec.y = -player->shieldMf.yz; + shieldVec.z = -player->shieldMf.zz; + EnHonotrap_GetNormal(&shieldNorm, &shieldVec); + + tempVel = this->actor.velocity; + Math3D_Vec3fReflect(&tempVel, &shieldNorm, &this->actor.velocity); + this->actor.speedXZ = this->speedMod * 0.5f; + this->actor.world.rot.y = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + EnHonotrap_SetupFlameVanish(this); + } else if (this->collider.tris.base.atFlags & AT_HIT) { + this->actor.velocity.y = this->actor.speedXZ = 0.0f; + EnHonotrap_SetupFlameVanish(this); + } else if (this->timer <= 0) { + EnHonotrap_SetupFlameVanish(this); + } else { + EnHonotrap_FlameCollisionCheck(this, globalCtx); + if (ready) { + EnHonotrap_SetupFlameChase(this); + } + } +} + +void EnHonotrap_SetupFlameChase(EnHonotrap* this) { + this->actionFunc = EnHonotrap_FlameChase; + + this->actor.velocity.x = this->actor.velocity.y = this->actor.velocity.z = this->actor.speedXZ = 0.0f; + this->actor.world.rot.x = this->actor.world.rot.y = this->actor.world.rot.z = 0; + + this->timer = 100; +} + +void EnHonotrap_FlameChase(EnHonotrap* this, GlobalContext* globalCtx) { + s32 pad; + + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0x300); + Math_StepToF(&this->actor.speedXZ, 3.0f, 0.1f); + this->actor.gravity = (-this->actor.yDistToPlayer < 10.0f) ? 0.08f : -0.08f; + func_8002D868(&this->actor); + if (this->actor.velocity.y > 1.0f) { + this->actor.velocity.y = 1.0f; + } + this->actor.velocity.y *= 0.95f; + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.0f, 10.0f, 0.0f, 0x1D); + if (this->collider.cyl.base.atFlags & AT_BOUNCED) { + Player* player = GET_PLAYER(globalCtx); + Vec3s shieldRot; + + Matrix_MtxFToYXZRotS(&player->shieldMf, &shieldRot, false); + this->actor.world.rot.y = ((shieldRot.y * 2) - this->actor.world.rot.y) + 0x8000; + EnHonotrap_SetupFlameVanish(this); + } else if (this->collider.cyl.base.atFlags & AT_HIT) { + this->actor.speedXZ *= 0.1f; + this->actor.velocity.y *= 0.1f; + EnHonotrap_SetupFlameVanish(this); + } else if ((this->actor.bgCheckFlags & 8) || (this->timer <= 0)) { + EnHonotrap_SetupFlameVanish(this); + } else { + EnHonotrap_FlameCollisionCheck(this, globalCtx); + } +} + +void EnHonotrap_SetupFlameVanish(EnHonotrap* this) { + this->actionFunc = EnHonotrap_FlameVanish; +} + +void EnHonotrap_FlameVanish(EnHonotrap* this, GlobalContext* globalCtx) { + s32 pad; + s32 ready = Math_StepToF(&this->actor.scale.x, 0.0001f, 0.00015f); + + this->actor.scale.z = this->actor.scale.y = this->actor.scale.x; + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.0f, 10.0f, 0.0f, 0x1D); + if (ready) { + Actor_Kill(&this->actor); + } +} + +void EnHonotrap_Update(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.1f, 0.0f }; + s32 pad; + EnHonotrap* this = (EnHonotrap*)thisx; + + if (this->timer > 0) { + this->timer--; + } + if (this->actor.params == HONOTRAP_EYE) { + if ((this->actor.child != NULL) && (this->actor.child->update == NULL)) { + this->actor.child = NULL; + } + } else { + this->colChkFlags = 0; + this->bobPhase += 0x640; + this->actor.shape.yOffset = (Math_SinS(this->bobPhase) * 1000.0f) + 600.0f; + Actor_SetFocus(&this->actor, 5.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BURN_OUT - SFX_FLAG); + } + this->actionFunc(this, globalCtx); + if (this->actor.params == HONOTRAP_EYE) { + if (this->collider.tris.base.acFlags & AC_HIT) { + EffectSsBomb2_SpawnLayered(globalCtx, &this->actor.world.pos, &velocity, &accel, 15, 8); + Actor_Kill(&this->actor); + } else if (this->eyeState < HONOTRAP_EYE_SHUT) { + this->collider.tris.base.acFlags &= ~AC_HIT; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.tris.base); + } + } +} + +void EnHonotrap_DrawEye(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gEyeSwitchSilverOpenTex, + gEyeSwitchSilverHalfTex, + gEyeSwitchSilverClosedTex, + gEyeSwitchSilverClosedTex, + }; + EnHonotrap* this = (EnHonotrap*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_honotrap.c", 982); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeState])); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_honotrap.c", 987), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gEyeSwitch2DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_honotrap.c", 991); +} + +void EnHonotrap_DrawFlame(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHonotrap* this = (EnHonotrap*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_honotrap.c", 1000); + + func_80093D84(globalCtx->state.gfxCtx); + this->flameScroll -= 20; + this->flameScroll &= 0x1FF; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, this->flameScroll, 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 200, 0, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y + 0x8000) * + (M_PI / 0x8000), + MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_honotrap.c", 1024), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_honotrap.c", 1028); +} + +void EnHonotrap_Draw(Actor* thisx, GlobalContext* globalCtx) { + switch (thisx->params) { + case HONOTRAP_EYE: + EnHonotrap_DrawEye(thisx, globalCtx); + break; + case HONOTRAP_FLAME_MOVE: + case HONOTRAP_FLAME_DROP: + EnHonotrap_DrawFlame(thisx, globalCtx); + break; + } +} diff --git a/soh/src/overlays/actors/ovl_En_Honotrap/z_en_honotrap.h b/soh/src/overlays/actors/ovl_En_Honotrap/z_en_honotrap.h new file mode 100644 index 000000000..73e3cdf07 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Honotrap/z_en_honotrap.h @@ -0,0 +1,38 @@ +#ifndef Z_EN_HONOTRAP_H +#define Z_EN_HONOTRAP_H + +#include "ultra64.h" +#include "global.h" + +struct EnHonotrap; + +typedef void (*EnHonotrapActionFunc)(struct EnHonotrap*, GlobalContext*); + +typedef union { + struct { + ColliderTris tris; + ColliderTrisElement elements[2]; + }; + ColliderCylinder cyl; +} EnHonotrapCollider; // size = 0xD8 + +typedef struct EnHonotrap { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnHonotrapActionFunc actionFunc; + /* 0x0150 */ EnHonotrapCollider collider; + /* 0x0228 */ s16 timer; + /* 0x022A */ s16 eyeState; + /* 0x022C */ Vec3f targetPos; + /* 0x0238 */ f32 speedMod; + /* 0x023C */ s16 bobPhase; + /* 0x023E */ s16 flameScroll; + /* 0x0240 */ u8 colChkFlags; +} EnHonotrap; // size = 0x0244 + +typedef enum { + HONOTRAP_EYE, + HONOTRAP_FLAME_MOVE, + HONOTRAP_FLAME_DROP +} EnHonotrapType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c new file mode 100644 index 000000000..51da01932 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.c @@ -0,0 +1,3838 @@ +/** + * File: z_en_horse.c + * Overlay: ovl_En_Horse + * Description: Rideable horses + */ + +#include "z_en_horse.h" +#include "overlays/actors/ovl_En_In/z_en_in.h" +#include "objects/object_horse/object_horse.h" +#include "objects/object_hni/object_hni.h" +#include "scenes/overworld/spot09/spot09_scene.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef void (*EnHorseCsFunc)(EnHorse*, GlobalContext*, CsCmdActorAction*); +typedef void (*EnHorseActionFunc)(EnHorse*, GlobalContext*); + +void EnHorse_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHorse_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHorse_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHorse_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHorse_InitCutscene(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_InitHorsebackArchery(EnHorse* this); +void EnHorse_InitFleePlayer(EnHorse* this); +void EnHorse_ResetIdleAnimation(EnHorse* this); +void EnHorse_StartIdleRidable(EnHorse* this); +void EnHorse_InitInactive(EnHorse* this); +void EnHorse_InitIngoHorse(EnHorse* this); + +void EnHorse_Frozen(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_Inactive(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_Idle(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_FollowPlayer(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_UpdateIngoRace(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedIdle(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedIdleWhinneying(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedTurn(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedWalk(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedTrot(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedGallop(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_MountedRearing(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_Stopping(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_Reverse(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_LowJump(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_HighJump(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_BridgeJump(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_CutsceneUpdate(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_UpdateHorsebackArchery(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_FleePlayer(EnHorse* this, GlobalContext* globalCtx); + +static AnimationHeader* sEponaAnimHeaders[] = { + &gEponaIdleAnim, &gEponaWhinnyAnim, &gEponaRefuseAnim, &gEponaRearingAnim, &gEponaWalkingAnim, + &gEponaTrottingAnim, &gEponaGallopingAnim, &gEponaJumpingAnim, &gEponaJumpingHighAnim, +}; + +static AnimationHeader* sHniAnimHeaders[] = { + &gHorseIngoIdleAnim, &gHorseIngoWhinnyAnim, &gHorseIngoRefuseAnim, + &gHorseIngoRearingAnim, &gHorseIngoWalkingAnim, &gHorseIngoTrottingAnim, + &gHorseIngoGallopingAnim, &gHorseIngoJumpingAnim, &gHorseIngoJumpingHighAnim, +}; + +static AnimationHeader** sAnimationHeaders[] = { sEponaAnimHeaders, sHniAnimHeaders }; + +static f32 sPlaybackSpeeds[] = { 2.0f / 3.0f, 2.0f / 3.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 2.0f / 3.0f, 2.0f / 3.0f }; + +static SkeletonHeader* sSkeletonHeaders[] = { &gEponaSkel, &gHorseIngoSkel }; + +const ActorInit En_Horse_InitVars = { + ACTOR_EN_HORSE, + ACTORCAT_BG, + FLAGS, + OBJECT_HORSE, + sizeof(EnHorse), + (ActorFunc)EnHorse_Init, + (ActorFunc)EnHorse_Destroy, + (ActorFunc)EnHorse_Update, + (ActorFunc)EnHorse_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit1 = { + { + COLTYPE_NONE, + AT_TYPE_PLAYER, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000400, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInit2 = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphItemsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO | BUMP_NO_DAMAGE | BUMP_NO_SWORD_SFX | BUMP_NO_HITMARK, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphItemsInit, +}; + +static CollisionCheckInfoInit D_80A65F38 = { 10, 35, 100, MASS_HEAVY }; + +typedef struct { + s16 scene; + Vec3s pos; + s16 angle; +} EnHorseSpawnpoint; + +static EnHorseSpawnpoint sHorseSpawns[] = { + // Hyrule Field + { SCENE_SPOT00, 16, 0, 1341, 0 }, + { SCENE_SPOT00, -1297, 0, 1459, 0 }, + { SCENE_SPOT00, -5416, -300, 1296, 0 }, + { SCENE_SPOT00, -4667, -300, 3620, 0 }, + { SCENE_SPOT00, -3837, 81, 5537, 0 }, + { SCENE_SPOT00, -5093, -226, 6661, 0 }, + { SCENE_SPOT00, -6588, -79, 5053, 0 }, + { SCENE_SPOT00, -7072, -500, 7538, 0 }, + { SCENE_SPOT00, -6139, -500, 8910, 0 }, + { SCENE_SPOT00, -8497, -300, 7802, 0 }, + { SCENE_SPOT00, -5481, -499, 12127, 0 }, + { SCENE_SPOT00, -4808, -700, 13583, 0 }, + { SCENE_SPOT00, -3416, -490, 12092, 0 }, + { SCENE_SPOT00, -2915, 100, 8339, 0 }, + { SCENE_SPOT00, -2277, -500, 13247, 0 }, + { SCENE_SPOT00, -1026, -500, 12101, 0 }, + { SCENE_SPOT00, 1427, -500, 13341, 0 }, + { SCENE_SPOT00, -200, -486, 10205, 0 }, + { SCENE_SPOT00, -1469, 100, 7496, 0 }, + { SCENE_SPOT00, -995, 168, 5652, 0 }, + { SCENE_SPOT00, 1938, 89, 6232, 0 }, + { SCENE_SPOT00, 1387, -105, 9206, 0 }, + { SCENE_SPOT00, 1571, -223, 7701, 0 }, + { SCENE_SPOT00, 3893, -121, 7068, 0 }, + { SCENE_SPOT00, 3179, 373, 5221, 0 }, + { SCENE_SPOT00, 4678, -20, 3869, 0 }, + { SCENE_SPOT00, 3460, 246, 4207, 0 }, + { SCENE_SPOT00, 3686, 128, 2366, 0 }, + { SCENE_SPOT00, 1791, 18, 152, 0 }, + { SCENE_SPOT00, 3667, -16, 1399, 0 }, + { SCENE_SPOT00, 1827, -15, 983, 0 }, + { SCENE_SPOT00, 1574, 399, 4318, 0 }, + { SCENE_SPOT00, 716, 95, 3391, 0 }, + { SCENE_SPOT00, -1189, -41, 4739, 0 }, + { SCENE_SPOT00, -1976, -171, 4172, 0 }, + { SCENE_SPOT00, 1314, 391, 5665, 0 }, + { SCENE_SPOT00, 112, 0, 1959, 0 }, + { SCENE_SPOT00, -3011, -111, 9397, 0 }, + { SCENE_SPOT00, -5674, -270, 8585, 0 }, + { SCENE_SPOT00, -8861, -300, 7836, 0 }, + { SCENE_SPOT00, -6056, -500, 7810, 0 }, + { SCENE_SPOT00, -7306, -500, 5994, 0 }, + { SCENE_SPOT00, -7305, -500, 7605, 0 }, + { SCENE_SPOT00, -7439, -300, 7600, 0 }, + { SCENE_SPOT00, -7464, -300, 6268, 0 }, + { SCENE_SPOT00, -8080, -300, 7553, 0 }, + { SCENE_SPOT00, -8091, -300, 7349, 0 }, + { SCENE_SPOT00, -8785, -300, 7383, 0 }, + { SCENE_SPOT00, -8745, -300, 7508, 0 }, + { SCENE_SPOT00, -8777, -300, 7788, 0 }, + { SCENE_SPOT00, -8048, -299, 7738, 0 }, + { SCENE_SPOT00, -7341, -184, 7730, 0 }, + { SCENE_SPOT00, -6410, -288, 7824, 0 }, + { SCENE_SPOT00, -6326, -290, 8205, 0 }, + { SCENE_SPOT00, -6546, -292, 8400, 0 }, + { SCENE_SPOT00, -7533, -180, 8459, 0 }, + { SCENE_SPOT00, -8024, -295, 7903, 0 }, + { SCENE_SPOT00, -8078, -308, 7994, 0 }, + { SCENE_SPOT00, -9425, -287, 7696, 0 }, + { SCENE_SPOT00, -9322, -292, 7577, 0 }, + { SCENE_SPOT00, -9602, -199, 7160, 0 }, + { SCENE_SPOT00, -9307, -300, 7758, 0 }, + { SCENE_SPOT00, -9230, -300, 7642, 0 }, + { SCENE_SPOT00, -7556, -499, 8695, 0 }, + { SCENE_SPOT00, -6438, -500, 8606, 0 }, + { SCENE_SPOT00, -6078, -500, 8258, 0 }, + { SCENE_SPOT00, -6233, -500, 7613, 0 }, + { SCENE_SPOT00, -5035, -205, 7814, 0 }, + { SCENE_SPOT00, -5971, -500, 8501, 0 }, + { SCENE_SPOT00, -5724, -498, 10123, 0 }, + { SCENE_SPOT00, -5094, -392, 11106, 0 }, + { SCENE_SPOT00, -5105, -393, 11312, 0 }, + { SCENE_SPOT00, -4477, -314, 11132, 0 }, + { SCENE_SPOT00, -3867, -380, 11419, 0 }, + { SCENE_SPOT00, -2952, -500, 11944, 0 }, + { SCENE_SPOT00, -2871, -488, 11743, 0 }, + { SCENE_SPOT00, -3829, -372, 11327, 0 }, + { SCENE_SPOT00, -4439, -293, 10989, 0 }, + { SCENE_SPOT00, -5014, -381, 11086, 0 }, + { SCENE_SPOT00, -5113, -188, 10968, 0 }, + { SCENE_SPOT00, -5269, -188, 11156, 0 }, + { SCENE_SPOT00, -5596, -178, 9972, 0 }, + { SCENE_SPOT00, -5801, -288, 8518, 0 }, + { SCENE_SPOT00, -4910, -178, 7931, 0 }, + { SCENE_SPOT00, -3586, 73, 8140, 0 }, + { SCENE_SPOT00, -4487, -106, 9362, 0 }, + { SCENE_SPOT00, -4339, -112, 6412, 0 }, + { SCENE_SPOT00, -3417, 105, 8194, 0 }, + { SCENE_SPOT00, -4505, -20, 6608, 0 }, + { SCENE_SPOT00, -5038, -199, 6603, 0 }, + { SCENE_SPOT00, -4481, 1, 6448, 0 }, + { SCENE_SPOT00, -5032, -168, 6418, 0 }, + { SCENE_SPOT00, -5256, -700, 14329, 0 }, + { SCENE_SPOT00, -5749, -820, 15380, 0 }, + { SCENE_SPOT00, -3122, -700, 13608, 0 }, + { SCENE_SPOT00, -3758, -525, 13228, 0 }, + { SCENE_SPOT00, -3670, -500, 13123, 0 }, + { SCENE_SPOT00, -2924, -500, 13526, 0 }, + { SCENE_SPOT00, 1389, -115, 9370, 0 }, + { SCENE_SPOT00, 548, -116, 8889, 0 }, + { SCENE_SPOT00, -106, -107, 8530, 0 }, + { SCENE_SPOT00, -1608, 85, 7646, 0 }, + { SCENE_SPOT00, -5300, -700, 13772, 0 }, + { SCENE_SPOT00, -5114, -700, 13400, 0 }, + { SCENE_SPOT00, -4561, -700, 13700, 0 }, + { SCENE_SPOT00, -4762, -700, 14084, 0 }, + { SCENE_SPOT00, -2954, 100, 8216, 0 }, + { SCENE_SPOT00, 1460, -104, 9246, 0 }, + { SCENE_SPOT00, 629, -105, 8791, 0 }, + { SCENE_SPOT00, -10, -90, 8419, 0 }, + { SCENE_SPOT00, -1462, 100, 7504, 0 }, + { SCENE_SPOT00, -3018, -500, 12493, 0 }, + { SCENE_SPOT00, -2994, -311, 10331, 0 }, + { SCENE_SPOT00, -4006, -700, 14152, 0 }, + { SCENE_SPOT00, -4341, -500, 12743, 0 }, + { SCENE_SPOT00, -5879, -497, 6799, 0 }, + { SCENE_SPOT00, 22, -473, 10103, 0 }, + { SCENE_SPOT00, -1389, -192, 8874, 0 }, + { SCENE_SPOT00, -4, 92, 6866, 0 }, + { SCENE_SPOT00, 483, 104, 6637, 0 }, + { SCENE_SPOT00, 1580, 183, 5987, 0 }, + { SCENE_SPOT00, 1548, 308, 5077, 0 }, + { SCENE_SPOT00, 1511, 399, 4267, 0 }, + { SCENE_SPOT00, 1358, 385, 4271, 0 }, + { SCENE_SPOT00, 1379, 395, 5063, 0 }, + { SCENE_SPOT00, 1360, 394, 5870, 0 }, + { SCENE_SPOT00, 813, 283, 6194, 0 }, + { SCENE_SPOT00, -57, 101, 6743, 0 }, + { SCENE_SPOT00, 91, 325, 5143, 0 }, + { SCENE_SPOT00, 1425, -214, 7659, 0 }, + { SCENE_SPOT00, 3487, -19, 880, 0 }, + { SCENE_SPOT00, 2933, 152, 2094, 0 }, + { SCENE_SPOT00, 2888, -145, 6862, 0 }, + { SCENE_SPOT00, 1511, 67, 6471, 0 }, + { SCENE_SPOT00, 4051, -47, 1722, 0 }, + { SCENE_SPOT00, -7335, -500, 8627, 0 }, + { SCENE_SPOT00, -7728, -462, 8498, 0 }, + { SCENE_SPOT00, -7791, -446, 8832, 0 }, + { SCENE_SPOT00, -2915, -435, 11334, 0 }, + { SCENE_SPOT00, 165, -278, 3352, 0 }, + + // Lake Hylia + { SCENE_SPOT06, -2109, -882, 1724, 0 }, + { SCENE_SPOT06, -328, -1238, 2705, 0 }, + { SCENE_SPOT06, -3092, -1033, 3527, 0 }, + + // Gerudo Valley + { SCENE_SPOT09, 2687, -269, 753, 0 }, + { SCENE_SPOT09, 2066, -132, 317, 0 }, + { SCENE_SPOT09, 523, -8, 635, 0 }, + { SCENE_SPOT09, 558, 36, -323, 0 }, + { SCENE_SPOT09, 615, 51, -839, 0 }, + { SCENE_SPOT09, -614, 32, 29, 0 }, + { SCENE_SPOT09, -639, -3, 553, 0 }, + { SCENE_SPOT09, -540, 10, -889, 0 }, + { SCENE_SPOT09, -1666, 58, 378, 0 }, + { SCENE_SPOT09, -3044, 210, -648, 0 }, + + // Gerudo's Fortress + { SCENE_SPOT12, -678, 21, -623, 0 }, + { SCENE_SPOT12, 149, 333, -2499, 0 }, + { SCENE_SPOT12, 499, 581, -547, 0 }, + { SCENE_SPOT12, 3187, 1413, -3775, 0 }, + { SCENE_SPOT12, 3198, 1413, 307, 0 }, + { SCENE_SPOT12, 3380, 1413, -1200, 0 }, + { SCENE_SPOT12, -966, 1, -56, 0 }, + { SCENE_SPOT12, -966, 24, -761, 0 }, + { SCENE_SPOT12, -694, 174, -2820, 0 }, + + // Lon Lon Ranch + { SCENE_SPOT20, 1039, 0, 2051, 0 }, + { SCENE_SPOT20, -1443, 0, 1429, 0 }, + { SCENE_SPOT20, 856, 0, -918, 0 }, // Hardcoded to always load in lon lon + { SCENE_SPOT20, 882, 0, -2256, 0 }, + { SCENE_SPOT20, -1003, 0, -755, 0 }, // Hardcoded to always load in lon lon + { SCENE_SPOT20, -2254, 0, -629, 0 }, + { SCENE_SPOT20, 907, 0, -2336, 0 }, +}; + +typedef struct { + s16 zMin; + s16 zMax; + + s16 xMin; + s16 xMax; + s16 xOffset; + + s16 angle; + s16 angleRange; + + Vec3s pos; +} BridgeJumpPoint; + +static BridgeJumpPoint sBridgeJumps[] = { + { -195, -40, 225, 120, 360, -0x4000, 0x7D0, -270, -52, -117 }, + { -195, -40, -240, -120, -360, 0x4000, 0x7D0, 270, -52, -117 }, +}; + +typedef struct { + s16 x; + s16 y; + s16 z; + s16 speed; + s16 angle; +} RaceWaypoint; + +typedef struct { + s32 numWaypoints; + RaceWaypoint* waypoints; +} RaceInfo; + +static RaceWaypoint sIngoRaceWaypoints[] = { + { 1056, 1, -1540, 11, 0x2A8D }, { 1593, 1, -985, 10, 0xFC27 }, { 1645, 1, -221, 11, 0xE891 }, + { 985, 1, 403, 10, 0xBB9C }, { -1023, 1, 354, 11, 0xA37D }, { -1679, 1, -213, 10, 0x889C }, + { -1552, 1, -1008, 11, 0x638D }, { -947, -1, -1604, 10, 0x4002 }, +}; + +static RaceInfo sIngoRace = { 8, sIngoRaceWaypoints }; +static s32 sAnimSoundFrames[] = { 0, 16 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 600, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 300, ICHAIN_STOP), +}; + +static u8 sResetNoInput[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0 }; + +static s32 sIdleAnimIds[] = { 1, 3, 0, 3, 1, 0 }; + +static s16 sIngoAnimations[] = { 7, 6, 2, 2, 1, 1, 0, 0, 0, 0 }; + +void EnHorse_CsMoveInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsJumpInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsRearingInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_WarpMoveInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsWarpRearingInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); + +static EnHorseCsFunc sCutsceneInitFuncs[] = { + NULL, + EnHorse_CsMoveInit, + EnHorse_CsJumpInit, + EnHorse_CsRearingInit, + EnHorse_WarpMoveInit, + EnHorse_CsWarpRearingInit, +}; + +void EnHorse_CsMoveToPoint(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsJump(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsRearing(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsWarpMoveToPoint(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); +void EnHorse_CsWarpRearing(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action); + +static EnHorseCsFunc sCutsceneActionFuncs[] = { + NULL, EnHorse_CsMoveToPoint, EnHorse_CsJump, EnHorse_CsRearing, EnHorse_CsWarpMoveToPoint, EnHorse_CsWarpRearing, +}; + +typedef struct { + s32 csAction; + s32 csFuncIdx; +} CsActionEntry; + +static CsActionEntry sCsActionTable[] = { + { 36, 1 }, { 37, 2 }, { 38, 3 }, { 64, 4 }, { 65, 5 }, +}; + +static RaceWaypoint sHbaWaypoints[] = { + { 3600, 1413, -5055, 11, 0x8001 }, { 3360, 1413, -5220, 5, 0xC000 }, { 3100, 1413, -4900, 5, 0x0000 }, + { 3600, 1413, -4100, 11, 0x0000 }, { 3600, 1413, 360, 11, 0x0000 }, +}; + +static RaceInfo sHbaInfo = { 5, sHbaWaypoints }; + +static EnHorseActionFunc sActionFuncs[] = { + EnHorse_Frozen, + EnHorse_Inactive, + EnHorse_Idle, + EnHorse_FollowPlayer, + EnHorse_UpdateIngoRace, + EnHorse_MountedIdle, + EnHorse_MountedIdleWhinneying, + EnHorse_MountedTurn, + EnHorse_MountedWalk, + EnHorse_MountedTrot, + EnHorse_MountedGallop, + EnHorse_MountedRearing, + EnHorse_Stopping, + EnHorse_Reverse, + EnHorse_LowJump, + EnHorse_HighJump, + EnHorse_BridgeJump, + EnHorse_CutsceneUpdate, + EnHorse_UpdateHorsebackArchery, + EnHorse_FleePlayer, +}; + +s32 EnHorse_BgCheckBridgeJumpPoint(EnHorse* this, GlobalContext* globalCtx) { + f32 xMin; + f32 xMax; + s32 i; + + if (globalCtx->sceneNum != SCENE_SPOT09) { + return false; + } + if (this->actor.speedXZ < 12.8f) { + return false; + } + if ((gSaveContext.eventChkInf[9] & 0xF) == 0xF) { + return false; + } + + for (i = 0; i < 2; i++) { + xMin = sBridgeJumps[i].xMin; + xMax = (xMin + sBridgeJumps[i].xMax) + sBridgeJumps[i].xOffset; + if (xMax < xMin) { + f32 temp = xMin; + + xMin = xMax; + xMax = temp; + } + if (sBridgeJumps[i].zMin < this->actor.world.pos.z && this->actor.world.pos.z < sBridgeJumps[i].zMax) { + if (xMin < this->actor.world.pos.x && this->actor.world.pos.x < xMax) { + if (sBridgeJumps[i].angle - sBridgeJumps[i].angleRange < this->actor.world.rot.y && + this->actor.world.rot.y < sBridgeJumps[i].angle + sBridgeJumps[i].angleRange) { + return true; + } + } + } + } + return false; +} + +void EnHorse_StartBridgeJump(EnHorse* this, GlobalContext* globalCtx); + +s32 EnHorse_CheckBridgeJumps(EnHorse* this, GlobalContext* globalCtx) { + f32 xMin; + f32 xMax; + s32 i; + + if (this->actor.speedXZ < 12.8f) { + return false; + } + + for (i = 0; i != 2; i++) { + xMin = sBridgeJumps[i].xMin; + xMax = sBridgeJumps[i].xMax + xMin; + + if (xMax < xMin) { + f32 temp = xMin; + + xMin = xMax; + xMax = temp; + } + + if (sBridgeJumps[i].zMin < this->actor.world.pos.z && this->actor.world.pos.z < sBridgeJumps[i].zMax) { + if (xMin < this->actor.world.pos.x && this->actor.world.pos.x < xMax) { + if (sBridgeJumps[i].angle - sBridgeJumps[i].angleRange < this->actor.world.rot.y && + this->actor.world.rot.y < sBridgeJumps[i].angle + sBridgeJumps[i].angleRange) { + this->bridgeJumpIdx = i; + EnHorse_StartBridgeJump(this, globalCtx); + return true; + } + } + } + } + + return false; +} + +void EnHorse_RaceWaypointPos(RaceWaypoint* waypoints, s32 idx, Vec3f* pos) { + pos->x = waypoints[idx].x; + pos->y = waypoints[idx].y; + pos->z = waypoints[idx].z; +} + +void EnHorse_RotateToPoint(EnHorse* this, GlobalContext* globalCtx, Vec3f* pos, s16 turnAmount) { + func_8006DD9C(&this->actor, pos, turnAmount); +} + +void EnHorse_UpdateIngoRaceInfo(EnHorse* this, GlobalContext* globalCtx, RaceInfo* raceInfo) { + Vec3f curWaypointPos; + Vec3f prevWaypointPos; + f32 playerDist; + f32 sp50; + s16 relPlayerYaw; + f32 px; + f32 pz; + f32 d; + f32 dist; + s32 prevWaypoint; + + EnHorse_RaceWaypointPos(raceInfo->waypoints, this->curRaceWaypoint, &curWaypointPos); + Math3D_RotateXZPlane(&curWaypointPos, raceInfo->waypoints[this->curRaceWaypoint].angle, &px, &pz, &d); + if (((this->actor.world.pos.x * px) + (pz * this->actor.world.pos.z) + d) > 0.0f) { + this->curRaceWaypoint++; + if (this->curRaceWaypoint >= raceInfo->numWaypoints) { + this->curRaceWaypoint = 0; + } + } + + EnHorse_RaceWaypointPos(raceInfo->waypoints, this->curRaceWaypoint, &curWaypointPos); + + prevWaypoint = this->curRaceWaypoint - 1; + if (prevWaypoint < 0) { + prevWaypoint = raceInfo->numWaypoints - 1; + } + EnHorse_RaceWaypointPos(raceInfo->waypoints, prevWaypoint, &prevWaypointPos); + Math3D_PointDistToLine2D(this->actor.world.pos.x, this->actor.world.pos.z, prevWaypointPos.x, prevWaypointPos.z, + curWaypointPos.x, curWaypointPos.z, &dist); + EnHorse_RotateToPoint(this, globalCtx, &curWaypointPos, 400); + + if (dist < 90000.0f) { + playerDist = this->actor.xzDistToPlayer; + if (playerDist < 130.0f || this->jntSph.elements[0].info.ocElemFlags & 2) { + if (Math_SinS(this->actor.yawTowardsPlayer - this->actor.world.rot.y) > 0.0f) { + this->actor.world.rot.y = this->actor.world.rot.y - 280; + } else { + this->actor.world.rot.y = this->actor.world.rot.y + 280; + } + } else if (playerDist < 300.0f) { + if (Math_SinS(this->actor.yawTowardsPlayer - this->actor.world.rot.y) > 0.0f) { + this->actor.world.rot.y = this->actor.world.rot.y + 280; + } else { + this->actor.world.rot.y = this->actor.world.rot.y - 280; + } + } + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + sp50 = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + relPlayerYaw = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y; + if (sp50 <= 200.0f || (fabsf(Math_SinS(relPlayerYaw)) < 0.8f && Math_CosS(relPlayerYaw) > 0.0f)) { + if (this->actor.speedXZ < this->ingoHorseMaxSpeed) { + this->actor.speedXZ += 0.47f; + } else { + this->actor.speedXZ -= 0.47f; + } + this->ingoRaceFlags |= 1; + return; + } + + if (this->actor.speedXZ < raceInfo->waypoints[this->curRaceWaypoint].speed) { + this->actor.speedXZ = this->actor.speedXZ + 0.4f; + } else { + this->actor.speedXZ = this->actor.speedXZ - 0.4f; + } + this->ingoRaceFlags &= ~0x1; +} + +void EnHorse_PlayWalkingSound(EnHorse* this) { + if (sAnimSoundFrames[this->soundTimer] < this->curFrame) { + if (this->soundTimer == 0 && (sAnimSoundFrames[1] < this->curFrame)) { + return; + } + + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_WALK, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + if (++this->soundTimer > 1) { + this->soundTimer = 0; + } + } +} + +void EnHorse_PlayTrottingSound(EnHorse* this) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void EnHorse_PlayGallopingSound(EnHorse* this) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +f32 EnHorse_SlopeSpeedMultiplier(EnHorse* this, GlobalContext* globalCtx) { + f32 multiplier = 1.0f; + + if (Math_CosS(this->actor.shape.rot.x) < 0.939262f && Math_SinS(this->actor.shape.rot.x) < 0.0f) { + multiplier = 0.7f; + } + return multiplier; +} + +void func_80A5BB90(GlobalContext* globalCtx, Vec3f* vec, Vec3f* arg2, f32* arg3) { + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, vec, arg2, arg3); +} + +s32 func_80A5BBBC(GlobalContext* globalCtx, EnHorse* this, Vec3f* pos) { + Vec3f sp24; + f32 sp20; + f32 eyeDist; + + func_80A5BB90(globalCtx, pos, &sp24, &sp20); + if (fabsf(sp20) < 0.008f) { + return false; + } + eyeDist = Math3D_Vec3f_DistXYZ(pos, &globalCtx->view.eye); + return func_800314D4(globalCtx, &this->actor, &sp24, sp20) || eyeDist < 100.0f; +} + +void EnHorse_IdleAnimSounds(EnHorse* this, GlobalContext* globalCtx) { + if (this->animationIdx == ENHORSE_ANIM_IDLE && + ((this->curFrame > 35.0f && this->type == HORSE_EPONA) || + (this->curFrame > 28.0f && this->type == HORSE_HNI)) && + !(this->stateFlags & ENHORSE_SANDDUST_SOUND)) { + this->stateFlags |= ENHORSE_SANDDUST_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_SANDDUST, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (this->animationIdx == ENHORSE_ANIM_REARING && this->curFrame > 25.0f && + !(this->stateFlags & ENHORSE_LAND2_SOUND)) { + this->stateFlags |= ENHORSE_LAND2_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +s32 EnHorse_Spawn(EnHorse* this, GlobalContext* globalCtx) { + f32 minDist = 1e38f; + s32 spawn = false; + f32 dist; + s32 i; + Player* player; + Vec3f spawnPos; + + for (i = 0; i < 169; i++) { + if (sHorseSpawns[i].scene == globalCtx->sceneNum) { + player = GET_PLAYER(globalCtx); + if (globalCtx->sceneNum != SCENE_SPOT20 || + //! Same flag checked twice + (Flags_GetEventChkInf(0x18) && ((gSaveContext.eventInf[0] & 0xF) != 6 || Flags_GetEventChkInf(0x18))) || + // always load two spawns inside lon lon + ((sHorseSpawns[i].pos.x == 856 && sHorseSpawns[i].pos.y == 0 && sHorseSpawns[i].pos.z == -918) || + (sHorseSpawns[i].pos.x == -1003 && sHorseSpawns[i].pos.y == 0 && sHorseSpawns[i].pos.z == -755))) { + + spawnPos.x = sHorseSpawns[i].pos.x; + spawnPos.y = sHorseSpawns[i].pos.y; + spawnPos.z = sHorseSpawns[i].pos.z; + dist = Math3D_Vec3f_DistXYZ(&player->actor.world.pos, &spawnPos); + + if (globalCtx->sceneNum) {} + if (!(minDist < dist) && !func_80A5BBBC(globalCtx, this, &spawnPos)) { + minDist = dist; + this->actor.world.pos.x = sHorseSpawns[i].pos.x; + this->actor.world.pos.y = sHorseSpawns[i].pos.y; + this->actor.world.pos.z = sHorseSpawns[i].pos.z; + this->actor.prevPos = this->actor.world.pos; + this->actor.world.rot.y = sHorseSpawns[i].angle; + this->actor.shape.rot.y = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + spawn = true; + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->actor.world.pos, + &this->actor.projectedPos, &this->actor.projectedW); + } + } + } + } + + return spawn; +} + +void EnHorse_ResetCutscene(EnHorse* this, GlobalContext* globalCtx) { + this->cutsceneAction = -1; + this->cutsceneFlags = 0; +} + +void EnHorse_ResetRace(EnHorse* this, GlobalContext* globalCtx) { + this->inRace = false; +} + +s32 EnHorse_PlayerCanMove(EnHorse* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags1 & 1) || func_8002DD78(GET_PLAYER(globalCtx)) == 1 || (player->stateFlags1 & 0x100000) || + ((this->stateFlags & ENHORSE_FLAG_19) && !this->inRace) || this->action == ENHORSE_ACT_HBA || + player->actor.flags & ACTOR_FLAG_8 || globalCtx->csCtx.state != 0) { + return false; + } + return true; +} + +void EnHorse_ResetHorsebackArchery(EnHorse* this, GlobalContext* globalCtx) { + this->unk_39C = 0; + this->hbaStarted = 0; + this->hbaFlags = 0; +} + +void EnHorse_ClearDustFlags(u16* dustFlags) { + *dustFlags = 0; +} + +void EnHorse_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnHorse* this = (EnHorse*)thisx; + GlobalContext* globalCtx = globalCtx2; + + AREG(6) = 0; + Actor_ProcessInitChain(&this->actor, sInitChain); + EnHorse_ClearDustFlags(&this->dustFlags); + DREG(53) = 0; + this->riderPos = this->actor.world.pos; + this->noInputTimer = 0; + this->noInputTimerMax = 0; + this->riderPos.y = this->riderPos.y + 70.0f; + + if (DREG(4) == 0) { + DREG(4) = 70; + } + + if (this->actor.params & 0x8000) { + this->actor.params &= ~0x8000; + this->type = HORSE_HNI; + + if ((this->bankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_HNI)) < 0) { + Actor_Kill(&this->actor); + return; + } + + do { + } while (!Object_IsLoaded(&globalCtx->objectCtx, this->bankIndex)); + + this->actor.objBankIndex = this->bankIndex; + Actor_SetObjectDependency(globalCtx, &this->actor); + this->boostSpeed = 12; + } else { + this->type = HORSE_EPONA; + this->boostSpeed = 14; + } + + // params was -1 + if (this->actor.params == 0x7FFF) { + this->actor.params = 1; + } + + if (globalCtx->sceneNum == SCENE_SOUKO) { + this->stateFlags = ENHORSE_UNRIDEABLE; + } else if (globalCtx->sceneNum == SCENE_SPOT12 && this->type == HORSE_HNI) { + this->stateFlags = ENHORSE_FLAG_18 | ENHORSE_UNRIDEABLE; + } else { + if (this->actor.params == 3) { + this->stateFlags = ENHORSE_FLAG_19 | ENHORSE_CANT_JUMP | ENHORSE_UNRIDEABLE; + } else if (this->actor.params == 6) { + this->stateFlags = ENHORSE_FLAG_19 | ENHORSE_CANT_JUMP; + if (Flags_GetEventChkInf(0x18) || DREG(1) != 0) { + this->stateFlags &= ~ENHORSE_CANT_JUMP; + this->stateFlags |= ENHORSE_FLAG_26; + } else if (gSaveContext.eventInf[0] & 0x40 && this->type == HORSE_HNI) { + this->stateFlags |= ENHORSE_FLAG_21 | ENHORSE_FLAG_20; + } + } else if (this->actor.params == 1) { + this->stateFlags = ENHORSE_FLAG_7; + } else { + this->stateFlags = 0; + } + } + + if (globalCtx->sceneNum == SCENE_SPOT20 && (gSaveContext.eventInf[0] & 0xF) == 6 && + Flags_GetEventChkInf(0x18) == 0 && !DREG(1)) { + this->stateFlags |= ENHORSE_FLAG_25; + } + + Actor_SetScale(&this->actor, 0.01f); + this->actor.gravity = -3.5f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawHorse, 20.0f); + this->action = ENHORSE_ACT_IDLE; + this->actor.speedXZ = 0.0f; + Collider_InitCylinder(globalCtx, &this->cyl1); + Collider_SetCylinder(globalCtx, &this->cyl1, &this->actor, &sCylinderInit1); + Collider_InitCylinder(globalCtx, &this->cyl2); + Collider_SetCylinder(globalCtx, &this->cyl2, &this->actor, &sCylinderInit2); + Collider_InitJntSph(globalCtx, &this->jntSph); + Collider_SetJntSph(globalCtx, &this->jntSph, &this->actor, &sJntSphInit, &this->jntSphList); + CollisionCheck_SetInfo(&this->actor.colChkInfo, DamageTable_Get(0xB), &D_80A65F38); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + this->playerControlled = false; + + if ((globalCtx->sceneNum == SCENE_SPOT20) && (gSaveContext.sceneSetupIndex < 4)) { + if (this->type == HORSE_HNI) { + if (this->actor.world.rot.z == 0 || !IS_DAY) { + Actor_Kill(&this->actor); + return; + } + if (Flags_GetEventChkInf(0x18)) { + Actor_Kill(&this->actor); + return; + } + if (this->actor.world.rot.z != 5) { + Actor_Kill(&this->actor); + return; + } + } else if (!Flags_GetEventChkInf(0x18) && !DREG(1) && !IS_DAY) { + Actor_Kill(&this->actor); + return; + } + } else if (globalCtx->sceneNum == SCENE_MALON_STABLE) { + if (IS_DAY || Flags_GetEventChkInf(0x18) || DREG(1) != 0 || !LINK_IS_ADULT) { + Actor_Kill(&this->actor); + return; + } + this->stateFlags |= ENHORSE_UNRIDEABLE; + } + + Skin_Init(globalCtx, &this->skin, sSkeletonHeaders[this->type], sAnimationHeaders[this->type][ENHORSE_ANIM_IDLE]); + this->animationIdx = ENHORSE_ANIM_IDLE; + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx]); + this->numBoosts = 6; + this->blinkTimer = this->postDrawFunc = this->boostRegenTime = 0; + EnHorse_ResetCutscene(this, globalCtx); + EnHorse_ResetRace(this, globalCtx); + EnHorse_ResetHorsebackArchery(this, globalCtx); + + if (this->actor.params == 2) { + EnHorse_InitInactive(this); + } else if (this->actor.params == 3) { + EnHorse_InitIngoHorse(this); + this->rider = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_IN, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->actor.shape.rot.x, this->actor.shape.rot.y, 1, 1); + if (this->rider == NULL) { + __assert("this->race.rider != NULL", "../z_en_horse.c", 3077); + } + if (!(gSaveContext.eventInf[0] & 0x40)) { + this->ingoHorseMaxSpeed = 12.07f; + } else { + this->ingoHorseMaxSpeed = 12.625f; + } + } else if (this->actor.params == 7) { + EnHorse_InitCutscene(this, globalCtx); + } else if (this->actor.params == 8) { + EnHorse_InitHorsebackArchery(this); + Interface_InitHorsebackArchery(globalCtx); + } else if (globalCtx->sceneNum == SCENE_SPOT20 && !Flags_GetEventChkInf(0x18) && !DREG(1)) { + EnHorse_InitFleePlayer(this); + } else { + if (globalCtx->sceneNum == SCENE_SOUKO) { + EnHorse_ResetIdleAnimation(this); + } else if (globalCtx->sceneNum == SCENE_SPOT12 && this->type == HORSE_HNI) { + EnHorse_ResetIdleAnimation(this); + } else { + EnHorse_StartIdleRidable(this); + } + } + this->actor.home.rot.z = this->actor.world.rot.z = this->actor.shape.rot.z = 0; +} + +void EnHorse_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHorse* this = (EnHorse*)thisx; + + if (this->stateFlags & ENHORSE_DRAW) { + Audio_StopSfxByPos(&this->unk_21C); + } + Skin_Free(globalCtx, &this->skin); + Collider_DestroyCylinder(globalCtx, &this->cyl1); + Collider_DestroyCylinder(globalCtx, &this->cyl2); + Collider_DestroyJntSph(globalCtx, &this->jntSph); +} + +void EnHorse_RotateToPlayer(EnHorse* this, GlobalContext* globalCtx) { + EnHorse_RotateToPoint(this, globalCtx, &GET_PLAYER(globalCtx)->actor.world.pos, 400); + if (this->stateFlags & ENHORSE_OBSTACLE) { + this->actor.world.rot.y += 800.0f; + } + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnHorse_Freeze(EnHorse* this) { + if (this->action != ENHORSE_ACT_CS_UPDATE && this->action != ENHORSE_ACT_HBA) { + if (sResetNoInput[this->actor.params] != 0 && this->actor.params != 4) { + this->noInputTimer = 0; + this->noInputTimerMax = 0; + } + this->prevAction = this->action; + this->action = ENHORSE_ACT_FROZEN; + this->cyl1.base.ocFlags1 &= ~OC1_ON; + this->cyl2.base.ocFlags1 &= ~OC1_ON; + this->jntSph.base.ocFlags1 &= ~OC1_ON; + this->animationIdx = ENHORSE_ANIM_IDLE; + } +} + +void EnHorse_ChangeIdleAnimation(EnHorse* this, s32 arg1, f32 arg2); +void EnHorse_StartMountedIdleResetAnim(EnHorse* this); +void EnHorse_StartMountedIdle(EnHorse* this); +void EnHorse_StartGalloping(EnHorse* this); + +void EnHorse_Frozen(EnHorse* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + this->noInputTimer--; + if (this->noInputTimer < 0) { + this->cyl1.base.ocFlags1 |= OC1_ON; + this->cyl2.base.ocFlags1 |= OC1_ON; + this->jntSph.base.ocFlags1 |= OC1_ON; + if (this->playerControlled == true) { + this->stateFlags &= ~ENHORSE_FLAG_7; + if (this->actor.params == 4) { + EnHorse_StartMountedIdleResetAnim(this); + } else if (this->actor.params == 9) { + this->actor.params = 5; + if (globalCtx->csCtx.state != 0) { + EnHorse_StartMountedIdle(this); + } else { + this->actor.speedXZ = 8.0f; + EnHorse_StartGalloping(this); + } + } else if (this->prevAction == 2) { + EnHorse_StartMountedIdle(this); + } else { + EnHorse_StartMountedIdleResetAnim(this); + } + if (this->actor.params != 0) { + this->actor.params = 0; + return; + } + } else { + if (this->prevAction == 5) { + EnHorse_ChangeIdleAnimation(this, 0, 0); + return; + } + if (this->prevAction == 6) { + EnHorse_ChangeIdleAnimation(this, 0, 0); + return; + } + EnHorse_ChangeIdleAnimation(this, 0, 0); + } + } +} + +void EnHorse_StickDirection(Vec2f* curStick, f32* stickMag, s16* angle); + +void EnHorse_UpdateSpeed(EnHorse* this, GlobalContext* globalCtx, f32 brakeDecel, f32 brakeAngle, f32 minStickMag, + f32 decel, f32 baseSpeed, s16 turnSpeed) { + s16* stickAnglePtr; // probably fake + f32 stickMag; + s16 stickAngle; + f32 temp_f12; + f32 traction; + s16 turn; + + if (!EnHorse_PlayerCanMove(this, globalCtx)) { + if (this->actor.speedXZ > 8) { + this->actor.speedXZ -= decel; + } else if (this->actor.speedXZ < 0) { + this->actor.speedXZ = 0; + } + + return; + } + + stickAnglePtr = &stickAngle; + + baseSpeed *= EnHorse_SlopeSpeedMultiplier(this, globalCtx); + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (Math_CosS(stickAngle) <= brakeAngle) { + this->actor.speedXZ -= brakeDecel; + this->actor.speedXZ = this->actor.speedXZ < 0.0f ? 0.0f : this->actor.speedXZ; + return; + } + + if (stickMag < minStickMag) { + this->stateFlags &= ~ENHORSE_BOOST; + this->stateFlags &= ~ENHORSE_BOOST_DECEL; + this->actor.speedXZ -= decel; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ = 0.0f; + } + + return; + } + + if (this->stateFlags & ENHORSE_BOOST) { + if ((16 - this->boostTimer) > 0) { + this->actor.speedXZ = + (EnHorse_SlopeSpeedMultiplier(this, globalCtx) * this->boostSpeed - this->actor.speedXZ) / + (16 - this->boostTimer) + + this->actor.speedXZ; + } else { + this->actor.speedXZ = EnHorse_SlopeSpeedMultiplier(this, globalCtx) * this->boostSpeed; + } + + if ((EnHorse_SlopeSpeedMultiplier(this, globalCtx) * this->boostSpeed) <= this->actor.speedXZ) { + this->stateFlags &= ~ENHORSE_BOOST; + this->stateFlags |= ENHORSE_BOOST_DECEL; + } + + } else if (this->stateFlags & ENHORSE_BOOST_DECEL) { + if (baseSpeed < this->actor.speedXZ) { + temp_f12 = this->actor.speedXZ; + this->actor.speedXZ = temp_f12 - 0.06f; + } else if (this->actor.speedXZ < baseSpeed) { + this->actor.speedXZ = baseSpeed; + this->stateFlags &= ~ENHORSE_BOOST_DECEL; + } + } else { + this->actor.speedXZ += + (this->actor.speedXZ <= baseSpeed * (1.0f / 54.0f) * stickMag ? 1.0f : -1.0f) * 50.0f * 0.01f; + if (baseSpeed < this->actor.speedXZ) { + this->actor.speedXZ = this->actor.speedXZ - decel; + if (this->actor.speedXZ < baseSpeed) { + this->actor.speedXZ = baseSpeed; + } + } + } + + temp_f12 = *stickAnglePtr * (1 / 32236.f); + traction = 2.2f - (this->actor.speedXZ * (1.0f / this->boostSpeed)); + turn = *stickAnglePtr * temp_f12 * temp_f12 * traction; + turn = CLAMP(turn, -turnSpeed * traction, turnSpeed * traction); + this->actor.world.rot.y += turn; + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnHorse_StartMountedIdleResetAnim(EnHorse* this) { + this->skin.skelAnime.curFrame = 0.0f; + EnHorse_StartMountedIdle(this); + this->stateFlags &= ~ENHORSE_SANDDUST_SOUND; +} + +void EnHorse_StartMountedIdle(EnHorse* this) { + f32 curFrame; + + this->action = ENHORSE_ACT_MOUNTED_IDLE; + this->animationIdx = ENHORSE_ANIM_IDLE; + if ((this->curFrame > 35.0f && this->type == HORSE_EPONA) || (this->curFrame > 28.0f && this->type == HORSE_HNI)) { + if (!(this->stateFlags & ENHORSE_SANDDUST_SOUND)) { + this->stateFlags |= ENHORSE_SANDDUST_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_SANDDUST, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + curFrame = this->skin.skelAnime.curFrame; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, curFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_StartReversingInterruptable(EnHorse* this); +void EnHorse_StartTurning(EnHorse* this); +void EnHorse_StartWalkingFromIdle(EnHorse* this); +void EnHorse_MountedIdleAnim(EnHorse* this); +void EnHorse_StartReversing(EnHorse* this); +void EnHorse_StartWalkingInterruptable(EnHorse* this); +void EnHorse_MountedIdleWhinney(EnHorse* this); +void EnHorse_StartWalking(EnHorse* this); + +void EnHorse_MountedIdle(EnHorse* this, GlobalContext* globalCtx) { + f32 mag; + s16 angle = 0; + + this->actor.speedXZ = 0; + EnHorse_StickDirection(&this->curStick, &mag, &angle); + if (mag > 10.0f && EnHorse_PlayerCanMove(this, globalCtx) == true) { + if (Math_CosS(angle) <= -0.5f) { + EnHorse_StartReversingInterruptable(this); + } else if (Math_CosS(angle) <= 0.7071) { // cos(45 degrees) + EnHorse_StartTurning(this); + } else { + EnHorse_StartWalkingFromIdle(this); + } + } + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorse_MountedIdleAnim(this); + } +} + +void EnHorse_MountedIdleAnim(EnHorse* this) { + this->skin.skelAnime.curFrame = 0.0f; + EnHorse_MountedIdleWhinney(this); +} + +void EnHorse_MountedIdleWhinney(EnHorse* this) { + f32 curFrame; + + this->action = ENHORSE_ACT_MOUNTED_IDLE_WHINNEYING; + this->animationIdx = ENHORSE_ANIM_WHINNEY; + curFrame = this->skin.skelAnime.curFrame; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, curFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void EnHorse_MountedIdleWhinneying(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 stickAngle = 0; + + this->actor.speedXZ = 0; + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (stickMag > 10.0f && EnHorse_PlayerCanMove(this, globalCtx) == true) { + if (Math_CosS(stickAngle) <= -0.5f) { + EnHorse_StartReversingInterruptable(this); + } else if (Math_CosS(stickAngle) <= 0.7071) { // cos(45 degrees) + EnHorse_StartTurning(this); + } else { + EnHorse_StartWalkingFromIdle(this); + } + } + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorse_StartMountedIdleResetAnim(this); + } +} + +void EnHorse_StartTurning(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_TURN; + this->soundTimer = 0; + this->animationIdx = ENHORSE_ANIM_WALK; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_MountedTurn(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 clampedYaw; + s16 stickAngle; + + this->actor.speedXZ = 0; + EnHorse_PlayWalkingSound(this); + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (stickMag > 10.0f) { + if (!EnHorse_PlayerCanMove(this, globalCtx)) { + EnHorse_StartMountedIdleResetAnim(this); + } else if (Math_CosS(stickAngle) <= -0.5f) { + EnHorse_StartReversingInterruptable(this); + } else if (Math_CosS(stickAngle) <= 0.7071) { // cos(45 degrees) + clampedYaw = CLAMP(stickAngle, -800.0f, 800.0f); + this->actor.world.rot.y = this->actor.world.rot.y + clampedYaw; + this->actor.shape.rot.y = this->actor.world.rot.y; + } else { + EnHorse_StartWalkingInterruptable(this); + } + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + if (Math_CosS(stickAngle) <= 0.7071) { // cos(45 degrees) + EnHorse_StartTurning(this); + } else { + EnHorse_StartMountedIdleResetAnim(this); + } + } +} + +void EnHorse_StartWalkingFromIdle(EnHorse* this) { + EnHorse_StartWalkingInterruptable(this); + + if (!(this->stateFlags & ENHORSE_FLAG_8) && !(this->stateFlags & ENHORSE_FLAG_9)) { + this->stateFlags |= ENHORSE_FLAG_9; + this->waitTimer = 8; + return; + } + this->waitTimer = 0; +} + +void EnHorse_StartWalkingInterruptable(EnHorse* this) { + this->noInputTimer = 0; + this->noInputTimerMax = 0; + EnHorse_StartWalking(this); +} + +void EnHorse_StartWalking(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_WALK; + this->soundTimer = 0; + this->animationIdx = ENHORSE_ANIM_WALK; + this->waitTimer = 0; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_MountedWalkingReset(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_WALK; + this->soundTimer = 0; + this->animationIdx = ENHORSE_ANIM_WALK; + this->waitTimer = 0; + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx]); +} + +void EnHorse_StartTrotting(EnHorse* this); + +void EnHorse_MountedWalk(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 stickAngle; + + EnHorse_PlayWalkingSound(this); + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (this->noInputTimerMax == 0.0f || + (this->noInputTimer > 0.0f && this->noInputTimer < this->noInputTimerMax - 20.0f)) { + EnHorse_UpdateSpeed(this, globalCtx, 0.3f, -0.5f, 10.0f, 0.06f, 3.0f, 400); + } else { + this->actor.speedXZ = 3.0f; + } + + if (this->actor.speedXZ == 0.0f) { + this->stateFlags &= ~ENHORSE_FLAG_9; + EnHorse_StartMountedIdleResetAnim(this); + this->noInputTimer = 0; + this->noInputTimerMax = 0; + } else if (this->actor.speedXZ > 3.0f) { + this->stateFlags &= ~ENHORSE_FLAG_9; + EnHorse_StartTrotting(this); + this->noInputTimer = 0; + this->noInputTimerMax = 0; + } + + if (this->noInputTimer > 0.0f) { + this->noInputTimer--; + if (this->noInputTimer <= 0.0f) { + this->noInputTimerMax = 0; + } + } + + if (this->waitTimer <= 0) { + this->stateFlags &= ~ENHORSE_FLAG_9; + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.75f; + if (SkelAnime_Update(&this->skin.skelAnime) || this->actor.speedXZ == 0.0f) { + if (this->noInputTimer <= 0.0f) { + if (this->actor.speedXZ > 3.0f) { + EnHorse_StartTrotting(this); + this->noInputTimer = 0; + this->noInputTimerMax = 0; + } else if ((stickMag < 10.0f) || (Math_CosS(stickAngle) <= -0.5f)) { + EnHorse_StartMountedIdleResetAnim(this); + this->noInputTimer = 0; + this->noInputTimerMax = 0; + } else { + EnHorse_MountedWalkingReset(this); + } + } + } + } else { + this->actor.speedXZ = 0.0f; + this->waitTimer--; + } +} + +void EnHorse_StartTrotting(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_TROT; + this->animationIdx = ENHORSE_ANIM_TROT; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_MountedTrotReset(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_TROT; + this->animationIdx = ENHORSE_ANIM_TROT; + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx]); +} + +void EnHorse_StartGallopingInterruptable(EnHorse* this); + +void EnHorse_MountedTrot(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 stickAngle; + + EnHorse_UpdateSpeed(this, globalCtx, 0.3f, -0.5f, 10.0f, 0.06f, 6.0f, 400); + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (this->actor.speedXZ < 3.0f) { + EnHorse_StartWalkingInterruptable(this); + } + + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.375f; + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorse_PlayTrottingSound(this); + func_800AA000(0.0f, 60, 8, 255); + if (this->actor.speedXZ >= 6.0f) { + EnHorse_StartGallopingInterruptable(this); + } else if (this->actor.speedXZ < 3.0f) { + EnHorse_StartWalkingInterruptable(this); + } else { + EnHorse_MountedTrotReset(this); + } + } +} + +void EnHorse_StartGallopingInterruptable(EnHorse* this) { + this->noInputTimerMax = 0; + this->noInputTimer = 0; + EnHorse_StartGalloping(this); +} + +void EnHorse_StartGalloping(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_GALLOP; + this->animationIdx = ENHORSE_ANIM_GALLOP; + this->unk_234 = 0; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_MountedGallopReset(EnHorse* this) { + this->noInputTimerMax = 0; + this->noInputTimer = 0; + this->action = ENHORSE_ACT_MOUNTED_GALLOP; + this->animationIdx = ENHORSE_ANIM_GALLOP; + this->unk_234 = 0; + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx]); +} + +void EnHorse_JumpLanding(EnHorse* this, GlobalContext* globalCtx) { + Vec3s* jointTable; + f32 y; + + this->action = ENHORSE_ACT_MOUNTED_GALLOP; + this->animationIdx = ENHORSE_ANIM_GALLOP; + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx]); + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->riderPos.y += y * 0.01f; + this->postDrawFunc = NULL; +} + +void EnHorse_StartBraking(EnHorse* this, GlobalContext* globalCtx); + +void EnHorse_MountedGallop(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 stickAngle; + + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + + if (this->noInputTimer <= 0.0f) { + EnHorse_UpdateSpeed(this, globalCtx, 0.3f, -0.5f, 10.0f, 0.06f, 8.0f, 0x190); + } else if (this->noInputTimer > 0.0f) { + this->noInputTimer -= 1; + this->actor.speedXZ = 8.0f; + } + if (this->actor.speedXZ < 6.0f) { + EnHorse_StartTrotting(this); + } + + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.3f; + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorse_PlayGallopingSound(this); + func_800AA000(0, 120, 8, 255); + if (EnHorse_PlayerCanMove(this, globalCtx) == true) { + if (stickMag >= 10.0f && Math_CosS(stickAngle) <= -0.5f) { + EnHorse_StartBraking(this, globalCtx); + } else if (this->actor.speedXZ < 6.0f) { + EnHorse_StartTrotting(this); + } else { + EnHorse_MountedGallopReset(this); + } + return; + } + EnHorse_MountedGallopReset(this); + } +} + +void EnHorse_StartRearing(EnHorse* this) { + this->action = ENHORSE_ACT_MOUNTED_REARING; + this->animationIdx = ENHORSE_ANIM_REARING; + this->stateFlags &= ~ENHORSE_LAND2_SOUND; + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + func_800AA000(0.0f, 180, 20, 100); + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_MountedRearing(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 stickAngle; + + this->actor.speedXZ = 0; + if (this->curFrame > 25.0f) { + if (!(this->stateFlags & ENHORSE_LAND2_SOUND)) { + this->stateFlags |= ENHORSE_LAND2_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_800AA000(0, 180, 20, 100); + } + } + + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (SkelAnime_Update(&this->skin.skelAnime)) { + if (EnHorse_PlayerCanMove(this, globalCtx) == true) { + if (this->stateFlags & ENHORSE_FORCE_REVERSING) { + this->noInputTimer = 100; + this->noInputTimerMax = 100; + this->stateFlags &= ~ENHORSE_FORCE_REVERSING; + EnHorse_StartReversing(this); + } else if (this->stateFlags & ENHORSE_FORCE_WALKING) { + this->noInputTimer = 100; + this->noInputTimerMax = 100; + this->stateFlags &= ~ENHORSE_FORCE_WALKING; + EnHorse_StartWalking(this); + } else if (Math_CosS(stickAngle) <= -0.5f) { + EnHorse_StartReversingInterruptable(this); + } else { + EnHorse_StartMountedIdleResetAnim(this); + } + return; + } + EnHorse_StartMountedIdleResetAnim(this); + } +} + +void EnHorse_StartBraking(EnHorse* this, GlobalContext* globalCtx) { + this->action = ENHORSE_ACT_STOPPING; + this->animationIdx = ENHORSE_ANIM_STOPPING; + + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_SLIP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.5f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); + + this->stateFlags |= ENHORSE_STOPPING_NEIGH_SOUND; + this->stateFlags &= ~ENHORSE_BOOST; +} + +void EnHorse_Stopping(EnHorse* this, GlobalContext* globalCtx) { + if (this->actor.speedXZ > 0.0f) { + this->actor.speedXZ = this->actor.speedXZ - 0.6f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ = 0.0f; + } + } + + if (this->stateFlags & ENHORSE_STOPPING_NEIGH_SOUND && this->skin.skelAnime.curFrame > 29.0f) { + this->actor.speedXZ = 0.0f; + if (Rand_ZeroOne() > 0.5) { + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + func_800AA000(0.0f, 180, 20, 100); + this->stateFlags &= ~ENHORSE_STOPPING_NEIGH_SOUND; + } else { + EnHorse_StartMountedIdleResetAnim(this); + } + } + + if (this->skin.skelAnime.curFrame > 29.0f) { + this->actor.speedXZ = 0.0f; + } else if (this->actor.speedXZ > 3.0f && this->stateFlags & ENHORSE_FORCE_REVERSING) { + this->actor.speedXZ = 3.0f; + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + if (this->stateFlags & ENHORSE_FORCE_REVERSING) { + this->noInputTimer = 100; + this->noInputTimerMax = 100; + EnHorse_StartReversing(this); + this->stateFlags &= ~ENHORSE_FORCE_REVERSING; + } else { + EnHorse_StartMountedIdleResetAnim(this); + } + } +} + +void EnHorse_StartReversingInterruptable(EnHorse* this) { + this->noInputTimerMax = 0; + this->noInputTimer = 0; + EnHorse_StartReversing(this); +} + +void EnHorse_StartReversing(EnHorse* this) { + this->action = ENHORSE_ACT_REVERSE; + this->animationIdx = ENHORSE_ANIM_WALK; + this->soundTimer = 0; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_LOOP, -3.0f); +} + +void EnHorse_Reverse(EnHorse* this, GlobalContext* globalCtx) { + f32 stickMag; + s16 stickAngle; + s16 turnAmount; + Player* player = GET_PLAYER(globalCtx); + + EnHorse_PlayWalkingSound(this); + EnHorse_StickDirection(&this->curStick, &stickMag, &stickAngle); + if (EnHorse_PlayerCanMove(this, globalCtx) == true) { + if (this->noInputTimerMax == 0.0f || + (this->noInputTimer > 0.0f && this->noInputTimer < this->noInputTimerMax - 20.0f)) { + if (stickMag < 10.0f && this->noInputTimer <= 0.0f) { + EnHorse_StartMountedIdleResetAnim(this); + this->actor.speedXZ = 0.0f; + return; + } + if (stickMag < 10.0f) { + stickAngle = -0x7FFF; + } else if (Math_CosS(stickAngle) > -0.5f) { + this->noInputTimerMax = 0; + EnHorse_StartMountedIdleResetAnim(this); + this->actor.speedXZ = 0.0f; + return; + } + } else if (stickMag < 10.0f) { + stickAngle = -0x7FFF; + } + } else if (player->actor.flags & ACTOR_FLAG_8) { + EnHorse_StartMountedIdleResetAnim(this); + this->actor.speedXZ = 0.0f; + return; + } else { + stickAngle = -0x7FFF; + } + + this->actor.speedXZ = -2.0f; + turnAmount = 0x7FFF - stickAngle; + turnAmount = CLAMP(turnAmount, -1200.0f, 1200.0f); + this->actor.world.rot.y += turnAmount; + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->noInputTimer > 0.0f) { + this->noInputTimer--; + if (this->noInputTimer <= 0.0f) { + this->noInputTimerMax = 0; + } + } + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.5f * 1.5f; + if (SkelAnime_Update(&this->skin.skelAnime) && (f32)this->noInputTimer <= 0.0f && + EnHorse_PlayerCanMove(this, globalCtx) == true) { + if (stickMag > 10.0f && Math_CosS(stickAngle) <= -0.5f) { + this->noInputTimerMax = 0; + EnHorse_StartReversingInterruptable(this); + } else if (stickMag < 10.0f) { + this->noInputTimerMax = 0; + EnHorse_StartMountedIdleResetAnim(this); + } else { + EnHorse_StartReversing(this); + } + } +} + +void EnHorse_StartLowJump(EnHorse* this, GlobalContext* globalCtx); + +void EnHorse_LowJumpInit(EnHorse* this, GlobalContext* globalCtx) { + this->skin.skelAnime.curFrame = 0.0f; + EnHorse_StartLowJump(this, globalCtx); +} + +void EnHorse_StartLowJump(EnHorse* this, GlobalContext* globalCtx) { + f32 curFrame; + Vec3s* jointTable; + f32 y; + + this->action = ENHORSE_ACT_LOW_JUMP; + this->animationIdx = ENHORSE_ANIM_LOW_JUMP; + curFrame = this->skin.skelAnime.curFrame; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.5f, curFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); + + this->postDrawFunc = NULL; + this->jumpStartY = this->actor.world.pos.y; + + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0; + + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->riderPos.y -= y * 0.01f; + + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_JUMP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_800AA000(0.0f, 170, 10, 10); +} + +void EnHorse_Stub1(EnHorse* this) { +} + +void EnHorse_LowJump(EnHorse* this, GlobalContext* globalCtx) { + Vec3f pad; + Vec3s* jointTable; + f32 curFrame; + f32 y; + + curFrame = this->skin.skelAnime.curFrame; + this->stateFlags |= ENHORSE_JUMPING; + this->actor.speedXZ = 12.0f; + if (curFrame > 17.0f) { + this->actor.gravity = -3.5f; + if (this->actor.velocity.y == 0) { + this->actor.velocity.y = -6.0f; + } + if (this->actor.world.pos.y < this->actor.floorHeight + 90.0f) { + this->skin.skelAnime.playSpeed = 1.5f; + } else { + this->skin.skelAnime.playSpeed = 0; + } + } else { + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->actor.world.pos.y = this->jumpStartY + y * 0.01f; + } + + if (SkelAnime_Update(&this->skin.skelAnime) || + (curFrame > 17.0f && this->actor.world.pos.y < this->actor.floorHeight - this->actor.velocity.y + 80.0f)) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_800AA000(0.0f, 255, 10, 80); + this->stateFlags &= ~ENHORSE_JUMPING; + this->actor.gravity = -3.5f; + this->actor.world.pos.y = this->actor.floorHeight; + func_80028A54(globalCtx, 25.0f, &this->actor.world.pos); + EnHorse_JumpLanding(this, globalCtx); + } +} + +void EnHorse_StartHighJump(EnHorse* this, GlobalContext* globalCtx); + +void EnHorse_HighJumpInit(EnHorse* this, GlobalContext* globalCtx) { + this->skin.skelAnime.curFrame = 0.0f; + EnHorse_StartHighJump(this, globalCtx); +} + +void EnHorse_StartHighJump(EnHorse* this, GlobalContext* globalCtx) { + f32 curFrame; + Vec3s* jointTable; + f32 y; + + this->action = ENHORSE_ACT_HIGH_JUMP; + this->animationIdx = ENHORSE_ANIM_HIGH_JUMP; + curFrame = this->skin.skelAnime.curFrame; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.5f, curFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); + + this->jumpStartY = this->actor.world.pos.y; + this->postDrawFunc = NULL; + + this->actor.gravity = 0; + this->actor.velocity.y = 0.0f; + + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->riderPos.y -= y * 0.01f; + + this->stateFlags |= ENHORSE_CALC_RIDER_POS; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_JUMP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_800AA000(0.0f, 170, 10, 10); +} + +void EnHorse_Stub2(EnHorse* this) { +} + +void EnHorse_HighJump(EnHorse* this, GlobalContext* globalCtx) { + Vec3f pad; + Vec3s* jointTable; + f32 curFrame; + f32 y; + + curFrame = this->skin.skelAnime.curFrame; + this->stateFlags |= ENHORSE_JUMPING; + this->actor.speedXZ = 13.0f; + if (curFrame > 23.0f) { + this->actor.gravity = -3.5f; + if (this->actor.velocity.y == 0) { + this->actor.velocity.y = -10.5f; + } + + if (this->actor.world.pos.y < this->actor.floorHeight + 90.0f) { + this->skin.skelAnime.playSpeed = 1.5f; + } else { + this->skin.skelAnime.playSpeed = 0; + } + } else { + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->actor.world.pos.y = this->jumpStartY + y * 0.01f; + } + + if (SkelAnime_Update(&this->skin.skelAnime) || + (curFrame > 23.0f && this->actor.world.pos.y < this->actor.floorHeight - this->actor.velocity.y + 80.0f)) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_800AA000(0.0f, 255, 10, 80); + this->stateFlags &= ~ENHORSE_JUMPING; + this->actor.gravity = -3.5f; + this->actor.world.pos.y = this->actor.floorHeight; + func_80028A54(globalCtx, 25.0f, &this->actor.world.pos); + EnHorse_JumpLanding(this, globalCtx); + } +} + +void EnHorse_InitInactive(EnHorse* this) { + this->cyl1.base.ocFlags1 &= ~OC1_ON; + this->cyl2.base.ocFlags1 &= ~OC1_ON; + this->jntSph.base.ocFlags1 &= ~OC1_ON; + this->action = ENHORSE_ACT_INACTIVE; + this->animationIdx = ENHORSE_ANIM_WALK; + this->stateFlags |= ENHORSE_INACTIVE; + this->followTimer = 0; +} + +void EnHorse_SetFollowAnimation(EnHorse* this, GlobalContext* globalCtx); + +void EnHorse_Inactive(EnHorse* this, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + + if (DREG(53) != 0 && this->type == HORSE_EPONA) { + DREG(53) = 0; + if (EnHorse_Spawn(this, globalCtx) != 0) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->stateFlags &= ~ENHORSE_INACTIVE; + gSaveContext.horseData.scene = globalCtx->sceneNum; + + // Focus the camera on Epona + Camera_SetParam(globalCtx->cameraPtrs[0], 8, this); + Camera_ChangeSetting(globalCtx->cameraPtrs[0], 0x38); + Camera_SetCameraData(globalCtx->cameraPtrs[0], 4, NULL, NULL, 0x51, 0, 0); + } + } + if (!(this->stateFlags & ENHORSE_INACTIVE)) { + this->followTimer = 0; + EnHorse_SetFollowAnimation(this, globalCtx); + this->actor.params = 0; + this->cyl1.base.ocFlags1 |= OC1_ON; + this->cyl2.base.ocFlags1 |= OC1_ON; + this->jntSph.base.ocFlags1 |= OC1_ON; + } +} + +void EnHorse_PlayIdleAnimation(EnHorse* this, s32 anim, f32 morphFrames, f32 startFrame) { + this->action = ENHORSE_ACT_IDLE; + this->actor.speedXZ = 0.0f; + if (anim != ENHORSE_ANIM_IDLE && anim != ENHORSE_ANIM_WHINNEY && anim != ENHORSE_ANIM_REARING) { + anim = ENHORSE_ANIM_IDLE; + } + if (anim != this->animationIdx) { + this->animationIdx = anim; + if (this->animationIdx == ENHORSE_ANIM_IDLE) { + this->stateFlags &= ~ENHORSE_SANDDUST_SOUND; + } else if (this->animationIdx == ENHORSE_ANIM_WHINNEY) { + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else if (this->animationIdx == ENHORSE_ANIM_REARING) { + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + this->stateFlags &= ~ENHORSE_LAND2_SOUND; + } + + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, startFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + morphFrames); + } +} + +void EnHorse_ChangeIdleAnimation(EnHorse* this, s32 anim, f32 morphFrames) { + EnHorse_PlayIdleAnimation(this, anim, morphFrames, this->curFrame); +} + +void EnHorse_ResetIdleAnimation(EnHorse* this) { + this->animationIdx = ENHORSE_ANIM_WALK; // this forces anim 0 to play from the beginning + EnHorse_PlayIdleAnimation(this, this->animationIdx, 0, 0); +} + +void EnHorse_StartIdleRidable(EnHorse* this) { + EnHorse_ResetIdleAnimation(this); + this->stateFlags &= ~ENHORSE_UNRIDEABLE; +} + +void EnHorse_StartMovingAnimation(EnHorse* this, s32 arg1, f32 arg2, f32 arg3); + +void EnHorse_Idle(EnHorse* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + EnHorse_IdleAnimSounds(this, globalCtx); + + if (DREG(53) && this->type == HORSE_EPONA) { + DREG(53) = 0; + if (!func_80A5BBBC(globalCtx, this, &this->actor.world.pos)) { + if (EnHorse_Spawn(this, globalCtx)) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->followTimer = 0; + EnHorse_SetFollowAnimation(this, globalCtx); + Camera_SetParam(globalCtx->cameraPtrs[0], 8, this); + Camera_ChangeSetting(globalCtx->cameraPtrs[0], 0x38); + Camera_SetCameraData(globalCtx->cameraPtrs[0], 4, NULL, NULL, 0x51, 0, 0); + } + } else { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->followTimer = 0; + EnHorse_StartMovingAnimation(this, 6, -3.0f, 0.0f); + } + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + s32 idleAnimIdx = 0; + + if (this->animationIdx != ENHORSE_ANIM_IDLE) { + if (this->animationIdx == ENHORSE_ANIM_WHINNEY) { + idleAnimIdx = 1; + } else if (this->animationIdx == ENHORSE_ANIM_REARING) { + idleAnimIdx = 2; + } + } + + // Play one of the two other idle animations + EnHorse_PlayIdleAnimation(this, sIdleAnimIds[(Rand_ZeroOne() > 0.5f ? 0 : 1) + idleAnimIdx * 2], 0.0f, 0.0f); + } +} + +void EnHorse_StartMovingAnimation(EnHorse* this, s32 animId, f32 morphFrames, f32 startFrame) { + this->action = ENHORSE_ACT_FOLLOW_PLAYER; + this->stateFlags &= ~ENHORSE_TURNING_TO_PLAYER; + if (animId != ENHORSE_ANIM_TROT && animId != ENHORSE_ANIM_GALLOP && animId != ENHORSE_ANIM_WALK) { + animId = ENHORSE_ANIM_WALK; + } + if (this->animationIdx != animId) { + this->animationIdx = animId; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, startFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + morphFrames); + } else { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, startFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + 0.0f); + } +} + +void EnHorse_SetFollowAnimation(EnHorse* this, GlobalContext* globalCtx) { + s32 animId = ENHORSE_ANIM_WALK; + f32 distToPlayer; + + distToPlayer = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + if (distToPlayer > 400.0f) { + animId = ENHORSE_ANIM_GALLOP; + } else if (!(distToPlayer <= 300.0f)) { + if (distToPlayer <= 400.0f) { + animId = ENHORSE_ANIM_TROT; + } + } + + if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + if (distToPlayer > 400.0f) { + animId = ENHORSE_ANIM_GALLOP; + } else { + animId = ENHORSE_ANIM_TROT; + } + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + if (distToPlayer > 400.0f) { + animId = ENHORSE_ANIM_GALLOP; + } else if (distToPlayer < 300.0f) { + animId = ENHORSE_ANIM_WALK; + } else { + animId = ENHORSE_ANIM_TROT; + } + } else if (this->animationIdx == ENHORSE_ANIM_WALK) { + if (distToPlayer > 300.0f) { + animId = ENHORSE_ANIM_TROT; + } else { + animId = ENHORSE_ANIM_WALK; + } + } + EnHorse_StartMovingAnimation(this, animId, -3.0f, 0.0f); +} + +void EnHorse_FollowPlayer(EnHorse* this, GlobalContext* globalCtx) { + f32 distToPlayer; + f32 angleDiff; + + DREG(53) = 0; + distToPlayer = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + + // First rotate if the player is behind + if ((this->playerDir == PLAYER_DIR_BACK_R || this->playerDir == PLAYER_DIR_BACK_L) && + (distToPlayer > 300.0f && !(this->stateFlags & ENHORSE_TURNING_TO_PLAYER))) { + this->animationIdx = ENHORSE_ANIM_REARING; + this->stateFlags |= ENHORSE_TURNING_TO_PLAYER; + this->angleToPlayer = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + angleDiff = (f32)this->angleToPlayer - (f32)this->actor.world.rot.y; + if (angleDiff > 32767.f) { + angleDiff -= 32767.0f; + } else if (angleDiff < -32767) { + angleDiff += 32767; + } + + this->followPlayerTurnSpeed = + angleDiff / Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]); + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx]); + this->skin.skelAnime.playSpeed = 1.0f; + this->stateFlags &= ~ENHORSE_LAND2_SOUND; + this->unk_21C = this->unk_228; + } else if (this->stateFlags & ENHORSE_TURNING_TO_PLAYER) { + this->actor.world.rot.y = this->actor.world.rot.y + this->followPlayerTurnSpeed; + this->actor.shape.rot.y = this->actor.world.rot.y; + if (this->curFrame > 25.0f) { + if (!(this->stateFlags & ENHORSE_LAND2_SOUND)) { + this->stateFlags |= ENHORSE_LAND2_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } else { + EnHorse_RotateToPlayer(this, globalCtx); + } + + if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + this->actor.speedXZ = 8; + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.3f; + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + this->actor.speedXZ = 6; + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.375f; + } else if (this->animationIdx == ENHORSE_ANIM_WALK) { + this->actor.speedXZ = 3; + EnHorse_PlayWalkingSound(this); + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.75f; + } else { + this->actor.speedXZ = 0; + this->skin.skelAnime.playSpeed = 1.0f; + } + + if (!(this->stateFlags & ENHORSE_TURNING_TO_PLAYER) && ++this->followTimer > 300) { + EnHorse_StartIdleRidable(this); + this->unk_21C = this->unk_228; + + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + EnHorse_PlayGallopingSound(this); + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + EnHorse_PlayTrottingSound(this); + } + this->stateFlags &= ~ENHORSE_TURNING_TO_PLAYER; + if (distToPlayer < 100.0f) { + EnHorse_StartIdleRidable(this); + } else { + EnHorse_SetFollowAnimation(this, globalCtx); + } + } +} + +void EnHorse_UpdateIngoHorseAnim(EnHorse* this); + +void EnHorse_InitIngoHorse(EnHorse* this) { + this->curRaceWaypoint = 0; + this->soundTimer = 0; + this->actor.speedXZ = 0.0f; + EnHorse_UpdateIngoHorseAnim(this); + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_IT_INGO_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void EnHorse_SetIngoAnimation(s32 idx, f32 curFrame, s32 arg2, s16* animIdxOut, f32* curFrameOut) { + *animIdxOut = sIngoAnimations[idx]; + *curFrameOut = curFrame; + if (idx == 3 || idx == 7 || idx == 8 || idx == 4) { + *curFrameOut = 0.0f; + } + if (arg2 == 1) { + if (idx == 5) { + *animIdxOut = 4; + *curFrameOut = curFrame; + } else if (idx == 6) { + *animIdxOut = 3; + *curFrameOut = curFrame; + } + } +} + +void EnHorse_UpdateIngoHorseAnim(EnHorse* this) { + s32 animChanged = 0; + f32 animSpeed; + + this->action = ENHORSE_ACT_INGO_RACE; + this->stateFlags &= ~ENHORSE_SANDDUST_SOUND; + if (this->actor.speedXZ == 0.0f) { + if (this->animationIdx != ENHORSE_ANIM_IDLE) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_IDLE; + } else if (this->actor.speedXZ <= 3.0f) { + if (this->animationIdx != ENHORSE_ANIM_WALK) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_WALK; + } else if (this->actor.speedXZ <= 6.0f) { + if (this->animationIdx != ENHORSE_ANIM_TROT) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_TROT; + } else { + if (this->animationIdx != ENHORSE_ANIM_GALLOP) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_GALLOP; + } + + if (this->animationIdx == ENHORSE_ANIM_WALK) { + animSpeed = this->actor.speedXZ * 0.5f; + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + animSpeed = this->actor.speedXZ * 0.25f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + animSpeed = this->actor.speedXZ * 0.2f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + animSpeed = 1.0f; + } + + if (animChanged == true) { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + sPlaybackSpeeds[this->animationIdx] * animSpeed * 1.5f, 0, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3); + } else { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + sPlaybackSpeeds[this->animationIdx] * animSpeed * 1.5f, 0, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, 0); + } +} + +void EnHorse_UpdateIngoRace(EnHorse* this, GlobalContext* globalCtx) { + f32 playSpeed; + + if (this->animationIdx == ENHORSE_ANIM_IDLE || this->animationIdx == ENHORSE_ANIM_WHINNEY) { + EnHorse_IdleAnimSounds(this, globalCtx); + } else if (this->animationIdx == ENHORSE_ANIM_WALK) { + EnHorse_PlayWalkingSound(this); + } + + EnHorse_UpdateIngoRaceInfo(this, globalCtx, &sIngoRace); + if (!this->inRace) { + this->actor.speedXZ = 0.0f; + this->rider->speedXZ = 0.0f; + if (this->animationIdx != ENHORSE_ANIM_IDLE) { + EnHorse_UpdateIngoHorseAnim(this); + } + } + + if (this->animationIdx == ENHORSE_ANIM_WALK) { + playSpeed = this->actor.speedXZ * 0.5f; + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + playSpeed = this->actor.speedXZ * 0.25f; + } else if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + playSpeed = this->actor.speedXZ * 0.2f; + } else { + playSpeed = 1.0f; + } + this->skin.skelAnime.playSpeed = playSpeed; + if (SkelAnime_Update(&this->skin.skelAnime) || + (this->animationIdx == ENHORSE_ANIM_IDLE && this->actor.speedXZ != 0.0f)) { + EnHorse_UpdateIngoHorseAnim(this); + } + + if (this->stateFlags & ENHORSE_INGO_WON) { + ((EnIn*)this->rider)->animationIdx = 7; + ((EnIn*)this->rider)->unk_1E0 = 0; + return; + } + + EnHorse_SetIngoAnimation(this->animationIdx, this->skin.skelAnime.curFrame, this->ingoRaceFlags & 1, + &((EnIn*)this->rider)->animationIdx, &((EnIn*)this->rider)->unk_1E0); +} + +void EnHorse_CsMoveInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + this->animationIdx = ENHORSE_ANIM_GALLOP; + this->cutsceneAction = 1; + Animation_PlayOnceSetSpeed(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + this->actor.speedXZ * 0.3f); +} + +void EnHorse_CsPlayHighJumpAnim(EnHorse* this, GlobalContext* globalCtx); + +void EnHorse_CsMoveToPoint(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + Vec3f endPos; + f32 speed = 8.0f; + + endPos.x = action->endPos.x; + endPos.y = action->endPos.y; + endPos.z = action->endPos.z; + if (Math3D_Vec3f_DistXYZ(&endPos, &this->actor.world.pos) > speed) { + EnHorse_RotateToPoint(this, globalCtx, &endPos, 400); + this->actor.speedXZ = speed; + this->skin.skelAnime.playSpeed = speed * 0.3f; + } else { + this->actor.world.pos = endPos; + this->actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorse_PlayGallopingSound(this); + func_800AA000(0.0f, 120, 8, 255); + Animation_PlayOnceSetSpeed(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + this->actor.speedXZ * 0.3f); + } +} + +void EnHorse_CsSetAnimHighJump(EnHorse* this, GlobalContext* globalCtx) { + this->skin.skelAnime.curFrame = 0.0f; + EnHorse_CsPlayHighJumpAnim(this, globalCtx); +} + +void EnHorse_CsPlayHighJumpAnim(EnHorse* this, GlobalContext* globalCtx) { + f32 curFrame; + f32 y; + Vec3s* jointTable; + + this->animationIdx = ENHORSE_ANIM_HIGH_JUMP; + curFrame = this->skin.skelAnime.curFrame; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.5f, curFrame, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); + this->postDrawFunc = NULL; + this->jumpStartY = this->actor.world.pos.y; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0; + + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->riderPos.y -= y * 0.01f; + + this->stateFlags |= ENHORSE_CALC_RIDER_POS; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_JUMP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_800AA000(0.0f, 170, 10, 10); +} + +void EnHorse_CsJumpInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + EnHorse_CsSetAnimHighJump(this, globalCtx); + this->cutsceneAction = 2; + this->cutsceneFlags &= ~1; +} + +void EnHorse_CsJump(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + f32 temp_f2; + + if (this->cutsceneFlags & 1) { + EnHorse_CsMoveToPoint(this, globalCtx, action); + return; + } + temp_f2 = this->skin.skelAnime.curFrame; + this->stateFlags |= ENHORSE_JUMPING; + this->actor.speedXZ = 13.0f; + if (temp_f2 > 19.0f) { + this->actor.gravity = -3.5f; + if (this->actor.velocity.y == 0.0f) { + this->actor.velocity.y = -10.5f; + } + if (this->actor.world.pos.y < (this->actor.floorHeight + 90.0f)) { + this->skin.skelAnime.playSpeed = 1.5f; + } else { + this->skin.skelAnime.playSpeed = 0.0f; + } + } else { + Vec3s* jointTable; + f32 y; + + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->actor.world.pos.y = this->jumpStartY + y * 0.01f; + } + if (SkelAnime_Update(&this->skin.skelAnime) || + (temp_f2 > 19.0f && this->actor.world.pos.y < (this->actor.floorHeight - this->actor.velocity.y) + 80.0f)) { + Vec3s* jointTable; + f32 y; + + this->cutsceneFlags |= 1; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_800AA000(0.0f, 255, 10, 80); + this->stateFlags &= ~ENHORSE_JUMPING; + this->actor.gravity = -3.5f; + this->actor.velocity.y = 0; + this->actor.world.pos.y = this->actor.floorHeight; + func_80028A54(globalCtx, 25.0f, &this->actor.world.pos); + this->animationIdx = ENHORSE_ANIM_GALLOP; + Animation_PlayOnceSetSpeed(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + sPlaybackSpeeds[6]); + jointTable = this->skin.skelAnime.jointTable; + y = jointTable->y; + this->riderPos.y += y * 0.01f; + this->postDrawFunc = NULL; + } +} + +void EnHorse_CsRearingInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + this->animationIdx = ENHORSE_ANIM_REARING; + this->cutsceneAction = 3; + this->cutsceneFlags &= ~4; + this->stateFlags &= ~ENHORSE_LAND2_SOUND; + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_CsRearing(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + this->actor.speedXZ = 0.0f; + if (this->curFrame > 25.0f) { + if (!(this->stateFlags & ENHORSE_LAND2_SOUND)) { + this->stateFlags |= ENHORSE_LAND2_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + if (SkelAnime_Update(&this->skin.skelAnime)) { + this->animationIdx = ENHORSE_ANIM_IDLE; + if (!(this->cutsceneFlags & 4)) { + this->cutsceneFlags |= 4; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + -3.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), 0, 0.0f); + } + } +} + +void EnHorse_WarpMoveInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + this->actor.world.pos.x = action->startPos.x; + this->actor.world.pos.y = action->startPos.y; + this->actor.world.pos.z = action->startPos.z; + this->actor.prevPos = this->actor.world.pos; + this->actor.world.rot.y = action->urot.y; + this->actor.shape.rot = this->actor.world.rot; + this->animationIdx = ENHORSE_ANIM_GALLOP; + this->cutsceneAction = 4; + Animation_PlayOnceSetSpeed(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + this->actor.speedXZ * 0.3f); +} + +void EnHorse_CsWarpMoveToPoint(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + Vec3f endPos; + f32 speed = 8.0f; + + endPos.x = action->endPos.x; + endPos.y = action->endPos.y; + endPos.z = action->endPos.z; + if (Math3D_Vec3f_DistXYZ(&endPos, &this->actor.world.pos) > speed) { + EnHorse_RotateToPoint(this, globalCtx, &endPos, 400); + this->actor.speedXZ = speed; + this->skin.skelAnime.playSpeed = speed * 0.3f; + } else { + this->actor.world.pos = endPos; + this->actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorse_PlayGallopingSound(this); + func_800AA000(0.0f, 120, 8, 255); + Animation_PlayOnceSetSpeed(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + this->actor.speedXZ * 0.3f); + } +} + +void EnHorse_CsWarpRearingInit(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + this->actor.world.pos.x = action->startPos.x; + this->actor.world.pos.y = action->startPos.y; + this->actor.world.pos.z = action->startPos.z; + this->actor.prevPos = this->actor.world.pos; + this->actor.world.rot.y = action->urot.y; + this->actor.shape.rot = this->actor.world.rot; + this->animationIdx = ENHORSE_ANIM_REARING; + this->cutsceneAction = 5; + this->cutsceneFlags &= ~4; + this->stateFlags &= ~ENHORSE_LAND2_SOUND; + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); +} + +void EnHorse_CsWarpRearing(EnHorse* this, GlobalContext* globalCtx, CsCmdActorAction* action) { + this->actor.speedXZ = 0.0f; + if (this->curFrame > 25.0f) { + if (!(this->stateFlags & ENHORSE_LAND2_SOUND)) { + this->stateFlags |= ENHORSE_LAND2_SOUND; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + if (SkelAnime_Update(&this->skin.skelAnime)) { + this->animationIdx = ENHORSE_ANIM_IDLE; + if (!(this->cutsceneFlags & 4)) { + this->cutsceneFlags |= 4; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + -3.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), 0, 0.0f); + } + } +} + +void EnHorse_InitCutscene(EnHorse* this, GlobalContext* globalCtx) { + this->playerControlled = false; + this->action = ENHORSE_ACT_CS_UPDATE; + this->cutsceneAction = 0; + this->actor.speedXZ = 0.0f; +} + +s32 EnHorse_GetCutsceneFunctionIndex(s32 csAction) { + s32 numActions = ARRAY_COUNT(sCsActionTable); // prevents unrolling + s32 i; + + for (i = 0; i < numActions; i++) { + if (csAction == sCsActionTable[i].csAction) { + return sCsActionTable[i].csFuncIdx; + } + if (csAction < sCsActionTable[i].csAction) { + return 0; + } + } + return 0; +} + +void EnHorse_CutsceneUpdate(EnHorse* this, GlobalContext* globalCtx) { + s32 csFunctionIdx; + CsCmdActorAction* linkCsAction = globalCtx->csCtx.linkAction; + + if (globalCtx->csCtx.state == 3) { + this->playerControlled = 1; + this->actor.params = 10; + this->action = ENHORSE_ACT_IDLE; + EnHorse_Freeze(this); + return; + } + if (linkCsAction != 0 && linkCsAction != 0xABABABAB) { + csFunctionIdx = EnHorse_GetCutsceneFunctionIndex(linkCsAction->action); + if (csFunctionIdx != 0) { + if (this->cutsceneAction != csFunctionIdx) { + if (this->cutsceneAction == 0) { + this->actor.world.pos.x = linkCsAction->startPos.x; + this->actor.world.pos.y = linkCsAction->startPos.y; + this->actor.world.pos.z = linkCsAction->startPos.z; + this->actor.world.rot.y = linkCsAction->urot.y; + this->actor.shape.rot = this->actor.world.rot; + this->actor.prevPos = this->actor.world.pos; + } + this->cutsceneAction = csFunctionIdx; + sCutsceneInitFuncs[this->cutsceneAction](this, globalCtx, linkCsAction); + } + sCutsceneActionFuncs[this->cutsceneAction](this, globalCtx, linkCsAction); + } + } +} + +s32 EnHorse_UpdateHbaRaceInfo(EnHorse* this, GlobalContext* globalCtx, RaceInfo* raceInfo) { + Vec3f pos; + f32 px; + f32 pz; + f32 d; + + EnHorse_RaceWaypointPos(raceInfo->waypoints, this->curRaceWaypoint, &pos); + Math3D_RotateXZPlane(&pos, raceInfo->waypoints[this->curRaceWaypoint].angle, &px, &pz, &d); + + if (this->curRaceWaypoint >= raceInfo->numWaypoints - 1 && + Math3D_Vec3f_DistXYZ(&pos, &this->actor.world.pos) < DREG(8)) { + this->hbaFlags |= 2; + } + + if (((this->actor.world.pos.x * px) + (pz * this->actor.world.pos.z) + d) > 0.0f) { + this->curRaceWaypoint++; + if (this->curRaceWaypoint >= raceInfo->numWaypoints) { + this->hbaFlags |= 1; + return 1; + } + } + + if (!(this->hbaFlags & 1)) { + EnHorse_RotateToPoint(this, globalCtx, &pos, 800); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + if (this->actor.speedXZ < raceInfo->waypoints[this->curRaceWaypoint].speed && !(this->hbaFlags & 1)) { + this->actor.speedXZ += 0.4f; + } else { + this->actor.speedXZ -= 0.4f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ = 0.0f; + } + } + return 0; +} + +void EnHorse_UpdateHbaAnim(EnHorse* this); + +void EnHorse_InitHorsebackArchery(EnHorse* this) { + this->hbaStarted = 0; + this->soundTimer = 0; + this->curRaceWaypoint = 0; + this->hbaTimer = 0; + this->actor.speedXZ = 0.0f; + EnHorse_UpdateHbaAnim(this); +} + +void EnHorse_UpdateHbaAnim(EnHorse* this) { + s32 animChanged = 0; + f32 animSpeed; + + this->action = ENHORSE_ACT_HBA; + if (this->actor.speedXZ == 0.0f) { + if (this->animationIdx != ENHORSE_ANIM_IDLE) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_IDLE; + } else if (this->actor.speedXZ <= 3.0f) { + if (this->animationIdx != ENHORSE_ANIM_WALK) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_WALK; + } else if (this->actor.speedXZ <= 6.0f) { + if (this->animationIdx != ENHORSE_ANIM_TROT) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_TROT; + } else { + if (this->animationIdx != ENHORSE_ANIM_GALLOP) { + animChanged = true; + } + this->animationIdx = ENHORSE_ANIM_GALLOP; + } + + if (this->animationIdx == ENHORSE_ANIM_WALK) { + animSpeed = this->actor.speedXZ * 0.5f; + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + animSpeed = this->actor.speedXZ * 0.25f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_800AA000(0.0f, 60, 8, 255); + } else if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + animSpeed = this->actor.speedXZ * 0.2f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_800AA000(0.0f, 120, 8, 255); + } else { + animSpeed = 1.0f; + } + + if (animChanged == true) { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + sPlaybackSpeeds[this->animationIdx] * animSpeed * 1.5f, 0, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + -3.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], + sPlaybackSpeeds[this->animationIdx] * animSpeed * 1.5f, 0, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, 0); + } +} + +void EnHorse_UpdateHorsebackArchery(EnHorse* this, GlobalContext* globalCtx) { + f32 playSpeed; + s32 sp20; + + if (this->animationIdx == ENHORSE_ANIM_WALK) { + EnHorse_PlayWalkingSound(this); + } + if (globalCtx->interfaceCtx.hbaAmmo == 0) { + this->hbaTimer++; + } + + sp20 = func_800F5A58(NA_BGM_HORSE_GOAL); + EnHorse_UpdateHbaRaceInfo(this, globalCtx, &sHbaInfo); + if (this->hbaFlags & 1 || this->hbaTimer >= 46) { + if (sp20 != 1 && gSaveContext.minigameState != 3) { + gSaveContext.cutsceneIndex = 0; + globalCtx->nextEntranceIndex = 0x3B0; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 0x20; + } + } + + if (globalCtx->interfaceCtx.hbaAmmo != 0) { + if (!(this->hbaFlags & 2)) { + if (gSaveContext.infTable[25] & 1) { + if ((s32)gSaveContext.minigameScore >= 1500) { + this->hbaFlags |= 4; + } + } else { + if ((s32)gSaveContext.minigameScore >= 1000) { + this->hbaFlags |= 4; + } + } + } + } + + if ((globalCtx->interfaceCtx.hbaAmmo == 0) || (this->hbaFlags & 2)) { + if (this->hbaFlags & 4) { + this->hbaFlags &= ~4; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HORSE_GOAL); + } + } + + if (!this->hbaStarted) { + this->actor.speedXZ = 0.0f; + if (this->animationIdx != ENHORSE_ANIM_IDLE) { + EnHorse_UpdateHbaAnim(this); + } + } + + if (this->animationIdx == ENHORSE_ANIM_WALK) { + playSpeed = this->actor.speedXZ * 0.5f; + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + playSpeed = this->actor.speedXZ * 0.25f; + } else if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + playSpeed = this->actor.speedXZ * 0.2f; + } else { + playSpeed = 1.0f; + } + + this->skin.skelAnime.playSpeed = playSpeed; + if (SkelAnime_Update(&this->skin.skelAnime) || + (this->animationIdx == ENHORSE_ANIM_IDLE && this->actor.speedXZ != 0.0f)) { + EnHorse_UpdateHbaAnim(this); + } +} + +void EnHorse_InitFleePlayer(EnHorse* this) { + this->action = ENHORSE_ACT_FLEE_PLAYER; + this->stateFlags |= ENHORSE_UNRIDEABLE; + this->actor.speedXZ = 0.0f; +} + +void EnHorse_FleePlayer(EnHorse* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 distToHome; + f32 playerDistToHome; + f32 distToPlayer; + s32 nextAnim = this->animationIdx; + s32 animFinished; + s16 yaw; + + if (DREG(53) || this->type == HORSE_HNI) { + EnHorse_StartIdleRidable(this); + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + distToHome = Math3D_Vec3f_DistXYZ(&this->actor.home.pos, &this->actor.world.pos); + playerDistToHome = Math3D_Vec3f_DistXYZ(&player->actor.world.pos, &this->actor.home.pos); + distToPlayer = Math3D_Vec3f_DistXYZ(&player->actor.world.pos, &this->actor.world.pos); + + // Run home + if (playerDistToHome > 300.0f) { + if (distToHome > 150.0f) { + this->actor.speedXZ += 0.4f; + if (this->actor.speedXZ > 8.0f) { + this->actor.speedXZ = 8.0f; + } + } else { + this->actor.speedXZ -= 0.47f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ = 0.0f; + } + } + } else { + // Run away from Link + if (distToPlayer < 300.0f) { + this->actor.speedXZ += 0.4f; + if (this->actor.speedXZ > 8.0f) { + this->actor.speedXZ = 8.0f; + } + } else { + this->actor.speedXZ -= 0.47f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ = 0.0f; + } + } + } + + if (this->actor.speedXZ >= 6.0f) { // hoof it + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.3f; + nextAnim = ENHORSE_ANIM_GALLOP; + } else if (this->actor.speedXZ >= 3.0f) { // trot + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.375f; + nextAnim = ENHORSE_ANIM_TROT; + } else if (this->actor.speedXZ > 0.1f) { // walk + this->skin.skelAnime.playSpeed = this->actor.speedXZ * 0.75f; + nextAnim = ENHORSE_ANIM_WALK; + EnHorse_PlayWalkingSound(this); + } else { // idle + nextAnim = Rand_ZeroOne() > 0.5f ? 1 : 0; + EnHorse_IdleAnimSounds(this, globalCtx); + this->skin.skelAnime.playSpeed = 1.0f; + } + + // Turn away from Link, or towards home + if (nextAnim == ENHORSE_ANIM_GALLOP || nextAnim == ENHORSE_ANIM_TROT || nextAnim == ENHORSE_ANIM_WALK) { + if (playerDistToHome < 300.0f) { + yaw = player->actor.shape.rot.y; + yaw += (Actor_WorldYawTowardActor(&this->actor, &player->actor) > 0 ? 1 : -1) * 0x3FFF; + } else { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos) - this->actor.world.rot.y; + } + + if (yaw > 400) { + this->actor.world.rot.y += 400; + } else if (yaw < -400) { + this->actor.world.rot.y -= 400; + } else { + this->actor.world.rot.y += yaw; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + animFinished = SkelAnime_Update(&this->skin.skelAnime); + + if (this->animationIdx == ENHORSE_ANIM_IDLE || this->animationIdx == ENHORSE_ANIM_WHINNEY) { + if (nextAnim == ENHORSE_ANIM_GALLOP || nextAnim == ENHORSE_ANIM_TROT || nextAnim == ENHORSE_ANIM_WALK) { + this->animationIdx = nextAnim; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + -3.0f); + if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + EnHorse_PlayGallopingSound(this); + } else if (this->animationIdx == ENHORSE_ANIM_TROT) { + EnHorse_PlayTrottingSound(this); + } + return; + } + } + + if (animFinished) { + if (nextAnim == ENHORSE_ANIM_GALLOP) { + EnHorse_PlayGallopingSound(this); + } else if (nextAnim == ENHORSE_ANIM_TROT) { + EnHorse_PlayTrottingSound(this); + } + + if (this->animationIdx == ENHORSE_ANIM_IDLE || this->animationIdx == ENHORSE_ANIM_WHINNEY) { + if (nextAnim != this->animationIdx) { + this->animationIdx = nextAnim; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), + ANIMMODE_ONCE, -3.0f); + return; + } else { + if (Rand_ZeroOne() > 0.5f) { + this->animationIdx = ENHORSE_ANIM_IDLE; + this->stateFlags &= ~ENHORSE_SANDDUST_SOUND; + } else { + this->animationIdx = ENHORSE_ANIM_WHINNEY; + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_21C, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), + ANIMMODE_ONCE, -3.0f); + return; + } + } + + if (nextAnim != this->animationIdx) { + this->animationIdx = nextAnim; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + -3.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + 0.0f); + } + return; + } + + if (this->animationIdx == ENHORSE_ANIM_WALK) { + if (nextAnim == ENHORSE_ANIM_IDLE || nextAnim == ENHORSE_ANIM_WHINNEY) { + this->animationIdx = nextAnim; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, + -3.0f); + } + } +} + +void EnHorse_BridgeJumpInit(EnHorse* this, GlobalContext* globalCtx) { + f32 y; + + func_80028A54(globalCtx, 25.0f, &this->actor.world.pos); + this->action = ENHORSE_ACT_BRIDGE_JUMP; + this->stateFlags |= ENHORSE_JUMPING; + this->animationIdx = ENHORSE_ANIM_HIGH_JUMP; + y = this->skin.skelAnime.jointTable->y; + y = y * 0.01f; + this->bridgeJumpStart = this->actor.world.pos; + this->bridgeJumpStart.y += y; + this->bridgeJumpYVel = + (((sBridgeJumps[this->bridgeJumpIdx].pos.y + 48.7f) - this->bridgeJumpStart.y) - -360.0f) / 30.0f; + this->riderPos.y -= y; + this->stateFlags |= ENHORSE_CALC_RIDER_POS; + this->bridgeJumpRelAngle = this->actor.world.rot.y - sBridgeJumps[this->bridgeJumpIdx].angle; + this->bridgeJumpTimer = 0; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 0; + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->type][this->animationIdx], 1.5f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->type][this->animationIdx]), ANIMMODE_ONCE, -3.0f); + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_JUMP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_800AA000(0.0f, 170, 10, 10); + this->postDrawFunc = NULL; +} + +void EnHorse_StartBridgeJump(EnHorse* this, GlobalContext* globalCtx) { + this->postDrawFunc = EnHorse_BridgeJumpInit; + if (this->bridgeJumpIdx == 0) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGerudoValleyBridgeJumpFieldFortressCs); + gSaveContext.cutsceneTrigger = 1; + } else { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gGerudoValleyBridgeJumpFortressToFieldCs); + gSaveContext.cutsceneTrigger = 1; + } +} + +void EnHorse_BridgeJumpMove(EnHorse* this, GlobalContext* globalCtx) { + f32 interp; + f32 timeSq; + + interp = this->bridgeJumpTimer / 30.0f; + timeSq = (this->bridgeJumpTimer * this->bridgeJumpTimer); + + this->actor.world.pos.x = + ((sBridgeJumps[this->bridgeJumpIdx].pos.x - this->bridgeJumpStart.x) * interp) + this->bridgeJumpStart.x; + this->actor.world.pos.z = + ((sBridgeJumps[this->bridgeJumpIdx].pos.z - this->bridgeJumpStart.z) * interp) + this->bridgeJumpStart.z; + + this->actor.world.pos.y = + (this->bridgeJumpStart.y + (this->bridgeJumpYVel * this->bridgeJumpTimer) + (-0.4f * timeSq)); + + this->actor.world.rot.y = this->actor.shape.rot.y = + (sBridgeJumps[this->bridgeJumpIdx].angle + ((1.0f - interp) * this->bridgeJumpRelAngle)); + this->skin.skelAnime.curFrame = 23.0f * interp; + SkelAnime_Update(&this->skin.skelAnime); + if (this->bridgeJumpTimer < 30) { + this->stateFlags |= ENHORSE_FLAG_24; + } +} + +void EnHorse_CheckBridgeJumpLanding(EnHorse* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 8.0f; + this->skin.skelAnime.playSpeed = 1.5f; + if (SkelAnime_Update(&this->skin.skelAnime)) { + this->stateFlags &= ~ENHORSE_JUMPING; + this->actor.gravity = -3.5f; + this->actor.world.pos.y = sBridgeJumps[this->bridgeJumpIdx].pos.y; + func_80028A54(globalCtx, 25.0f, &this->actor.world.pos); + EnHorse_JumpLanding(this, globalCtx); + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_800AA000(0.0f, 255, 10, 80); + } +} + +void EnHorse_BridgeJump(EnHorse* this, GlobalContext* globalCtx) { + this->bridgeJumpTimer++; + if (this->bridgeJumpTimer < 30) { + EnHorse_BridgeJumpMove(this, globalCtx); + return; + } + EnHorse_CheckBridgeJumpLanding(this, globalCtx); +} + +void EnHorse_Vec3fOffset(Vec3f* src, s16 yaw, f32 dist, f32 height, Vec3f* dst) { + dst->x = src->x + Math_SinS(yaw) * dist; + dst->y = src->y + height; + dst->z = src->z + Math_CosS(yaw) * dist; +} + +s32 EnHorse_CalcFloorHeight(EnHorse* this, GlobalContext* globalCtx, Vec3f* pos, CollisionPoly** floorPoly, + f32* floorHeight) { + s32 bgId; + f32 waterY; + WaterBox* waterBox; + + *floorPoly = NULL; + *floorHeight = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, floorPoly, &bgId, pos); + + if (*floorHeight == BGCHECK_Y_MIN) { + return 1; // No floor + } + + if (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, pos->x, pos->z, &waterY, &waterBox) == 1 && + *floorHeight < waterY) { + return 2; // Water + } + + if ((*floorPoly)->normal.y * COLPOLY_NORMAL_FRAC < 0.81915206f || // cos(35 degrees) + SurfaceType_IsHorseBlocked(&globalCtx->colCtx, *floorPoly, bgId) || + func_80041D4C(&globalCtx->colCtx, *floorPoly, bgId) == 7) { + return 3; // Horse blocked surface + } + return 0; +} + +/** + * obstacleType: + * 1: Water in front + * 2: Water behind? + * 3: ? + * 4: Obstructed in front + * 5: Obstructed behind + */ +void EnHorse_ObstructMovement(EnHorse* this, GlobalContext* globalCtx, s32 obstacleType, s32 galloping) { + if (this->action == ENHORSE_ACT_CS_UPDATE || EnHorse_BgCheckBridgeJumpPoint(this, globalCtx)) { + return; + } + + this->actor.world.pos = this->lastPos; + this->actor.shape.rot.y = this->lastYaw; + this->actor.world.rot.y = this->lastYaw; + this->stateFlags |= ENHORSE_OBSTACLE; + + if (!this->playerControlled) { + if (this->animationIdx != ENHORSE_ANIM_REARING) { + return; + } + } else if (this->action != ENHORSE_ACT_MOUNTED_REARING) { + if (this->stateFlags & ENHORSE_JUMPING) { + this->stateFlags &= ~ENHORSE_JUMPING; + this->actor.gravity = -3.5f; + this->actor.world.pos.y = this->actor.floorHeight; + } + if (obstacleType == 1 || obstacleType == 4) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + } else if (obstacleType == 2 || obstacleType == 5) { + this->stateFlags |= ENHORSE_FORCE_WALKING; + } + if (galloping == true) { + EnHorse_StartRearing(this); + } + } +} + +void EnHorse_CheckFloors(EnHorse* this, GlobalContext* globalCtx) { + s32 status; + CollisionPoly* frontFloor; + CollisionPoly* backFloor; + s16 floorSlope; + Vec3f frontPos; + Vec3f backPos; + Vec3f pos; + f32 nx; + f32 ny; + f32 nz; + s32 galloping = this->actor.speedXZ > 8; + f32 dist; + f32 waterHeight; + WaterBox* waterBox; + s32 pad; + + if (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, + &waterHeight, &waterBox) == 1 && + this->actor.floorHeight < waterHeight) { + EnHorse_ObstructMovement(this, globalCtx, 1, galloping); + return; + } + + EnHorse_Vec3fOffset(&this->actor.world.pos, this->actor.shape.rot.y, 30.0f, 60.0f, &frontPos); + status = EnHorse_CalcFloorHeight(this, globalCtx, &frontPos, &frontFloor, &this->yFront); + if (status == 1) { + this->actor.shape.rot.x = 0; + EnHorse_ObstructMovement(this, globalCtx, 4, galloping); + return; + } + if (status == 2) { + EnHorse_ObstructMovement(this, globalCtx, 4, galloping); + return; + } + if (status == 3) { + EnHorse_ObstructMovement(this, globalCtx, 4, galloping); + return; + } + + EnHorse_Vec3fOffset(&this->actor.world.pos, this->actor.shape.rot.y, -30.0f, 60.0f, &backPos); + status = EnHorse_CalcFloorHeight(this, globalCtx, &backPos, &backFloor, &this->yBack); + if (status == 1) { + this->actor.shape.rot.x = 0; + EnHorse_ObstructMovement(this, globalCtx, 5, galloping); + return; + } + if (status == 2) { + EnHorse_ObstructMovement(this, globalCtx, 5, galloping); + return; + } + if (status == 3) { + EnHorse_ObstructMovement(this, globalCtx, 5, galloping); + return; + } + + floorSlope = Math_FAtan2F(this->yBack - this->yFront, 60.0f) * (0x8000 / M_PI); + if (this->actor.floorPoly != 0) { + nx = this->actor.floorPoly->normal.x * COLPOLY_NORMAL_FRAC; + ny = this->actor.floorPoly->normal.y * COLPOLY_NORMAL_FRAC; + nz = this->actor.floorPoly->normal.z * COLPOLY_NORMAL_FRAC; + pos = frontPos; + pos.y = this->yFront; + dist = Math3D_DistPlaneToPos(nx, ny, nz, this->actor.floorPoly->dist, &pos); + if ((frontFloor != this->actor.floorPoly) && (this->actor.speedXZ >= 0.0f)) { + if ((!(this->stateFlags & ENHORSE_JUMPING) && dist < -40.0f) || + (this->stateFlags & ENHORSE_JUMPING && dist < -200.0f)) { + EnHorse_ObstructMovement(this, globalCtx, 4, galloping); + return; + } + } + + pos = backPos; + pos.y = this->yBack; + dist = Math3D_DistPlaneToPos(nx, ny, nz, this->actor.floorPoly->dist, &pos); + if (((backFloor != this->actor.floorPoly) && (this->actor.speedXZ <= 0.0f) && + !(this->stateFlags & ENHORSE_JUMPING) && (dist < -40.0f)) || + (this->stateFlags & ENHORSE_JUMPING && dist < -200.0f)) { + EnHorse_ObstructMovement(this, globalCtx, 5, galloping); + return; + } + + if (ny < 0.81915206f || // cos(35 degrees) + SurfaceType_IsHorseBlocked(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId) || + func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId) == 7) { + if ((this->actor.speedXZ >= 0.0f)) { + EnHorse_ObstructMovement(this, globalCtx, 4, galloping); + } else { + EnHorse_ObstructMovement(this, globalCtx, 5, galloping); + } + return; + } + + if (this->stateFlags & ENHORSE_JUMPING) { + this->actor.shape.rot.x = 0; + return; + } + + if (this->actor.floorHeight + 4.0f < this->actor.world.pos.y) { + this->actor.shape.rot.x = 0; + return; + } + + if (fabsf(floorSlope) > 8191.0f) { + return; + } + + this->actor.shape.rot.x = floorSlope; + this->actor.shape.yOffset = + (this->yFront + (((this->yBack - this->yFront) * 20.0f) / 45.0f)) - this->actor.floorHeight; + } +} + +s32 EnHorse_GetMountSide(EnHorse* this, GlobalContext* globalCtx); + +void EnHorse_MountDismount(EnHorse* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 mountSide; + Player* player = GET_PLAYER(globalCtx); + + mountSide = EnHorse_GetMountSide(this, globalCtx); + if (mountSide != 0 && !(this->stateFlags & ENHORSE_UNRIDEABLE) && player->rideActor == NULL) { + Actor_SetRideActor(globalCtx, &this->actor, mountSide); + } + + if (this->playerControlled == false && Actor_IsMounted(globalCtx, &this->actor) == true) { + this->noInputTimer = 55; + this->noInputTimerMax = 55; + this->playerControlled = 1; + EnHorse_Freeze(this); + } else if (this->playerControlled == true && Actor_NotMounted(globalCtx, &this->actor) == true) { + this->noInputTimer = 35; + this->noInputTimerMax = 35; + this->stateFlags &= ~ENHORSE_UNRIDEABLE; + this->playerControlled = 0; + EnHorse_Freeze(this); + } +} + +void EnHorse_StickDirection(Vec2f* curStick, f32* stickMag, s16* angle) { + f32 dist; + f32 y; + f32 x; + + x = curStick->x; + y = curStick->y; + dist = sqrtf(SQ(x) + SQ(y)); + + *stickMag = dist; + if (dist > 60.0f) { + *stickMag = 60.0f; + } else { + *stickMag = *stickMag; + } + + *angle = Math_FAtan2F(-curStick->x, curStick->y) * (32768.0f / M_PI); +} + +void EnHorse_UpdateStick(EnHorse* this, GlobalContext* globalCtx) { + this->lastStick = this->curStick; + this->curStick.x = globalCtx->state.input[0].rel.stick_x; + this->curStick.y = globalCtx->state.input[0].rel.stick_y; +} + +void EnHorse_ResolveCollision(EnHorse* this, GlobalContext* globalCtx, CollisionPoly* colPoly) { + f32 dist; + f32 nx; + f32 ny; + f32 nz; + f32 offset; + + nx = COLPOLY_GET_NORMAL(colPoly->normal.x); + ny = COLPOLY_GET_NORMAL(colPoly->normal.y); + nz = COLPOLY_GET_NORMAL(colPoly->normal.z); + if (!(Math_CosS(this->actor.world.rot.y - + (s16)(Math_FAtan2F(colPoly->normal.x, colPoly->normal.z) * (0x8000 / M_PI)) - 0x7FFF) < + 0.7071f)) { // cos(45 degrees) + dist = Math3D_DistPlaneToPos(nx, ny, nz, colPoly->dist, &this->actor.world.pos); + offset = (1.0f / sqrtf(SQ(nx) + SQ(nz))); + offset = (30.0f - dist) * offset; + this->actor.world.pos.x += offset * nx; + this->actor.world.pos.z += offset * nz; + } +} + +void EnHorse_BgCheckSlowMoving(EnHorse* this, GlobalContext* globalCtx) { + f32 yOffset; + Vec3f start; + Vec3f end; + Vec3f intersect; + CollisionPoly* colPoly; + s32 bgId; + + if (globalCtx->sceneNum == SCENE_SPOT20) { + yOffset = 19.0f; + } else { + yOffset = 40.0f; + } + Math_Vec3f_Copy(&start, &this->actor.world.pos); + start.y = start.y + yOffset; + + Math_Vec3f_Copy(&end, &start); + end.x += 30.0f * Math_SinS(this->actor.world.rot.y); + end.y += 30.0f * Math_SinS(-this->actor.shape.rot.x); + end.z += 30.0f * Math_CosS(this->actor.world.rot.y); + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &start, &end, &intersect, &colPoly, 1, 0, 0, 1, &bgId) != 0) { + EnHorse_ResolveCollision(this, globalCtx, colPoly); + } +} + +void EnHorse_HighJumpInit(EnHorse* this, GlobalContext* globalCtx); +void EnHorse_Stub2(EnHorse* this); +void EnHorse_Stub1(EnHorse* this); + +void EnHorse_UpdateBgCheckInfo(EnHorse* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + Vec3f startPos; + Vec3f endPos; + Vec3f obstaclePos; + f32 pad3; + f32 intersectDist; + CollisionPoly* wall = NULL; + CollisionPoly* obstacleFloor = NULL; + s32 bgId; + f32 obstacleHeight; + f32 behindObstacleHeight; + f32 ny; + s32 movingFast; + s32 pad5; + DynaPolyActor* dynaPoly; + Vec3f intersect; + Vec3f obstacleTop; + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, globalCtx->sceneNum == SCENE_SPOT20 ? 19.0f : 40.0f, 35.0f, 100.0f, + 29); + + if (EnHorse_BgCheckBridgeJumpPoint(this, globalCtx)) { + return; + } + + // void 0 trick required to match, but is surely not real. revisit at a later time + if (this->actor.bgCheckFlags & 8 && Math_CosS(this->actor.wallYaw - ((void)0, this->actor.world).rot.y) < -0.3f) { + if (this->actor.speedXZ > 4.0f) { + this->actor.speedXZ -= 1.0f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_SANDDUST, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + + if (this->stateFlags & ENHORSE_JUMPING || !this->playerControlled) { + return; + } + + if (this->actor.speedXZ < 0.0f) { + return; + } + + // Braking or rearing from obstacle + if (this->action == ENHORSE_ACT_STOPPING || this->action == ENHORSE_ACT_MOUNTED_REARING) { + return; + } + + if (this->actor.speedXZ > 8.0f) { + if (this->actor.speedXZ < 12.8f) { + intersectDist = 160.0f; + movingFast = 0; + } else { + intersectDist = 230.0f; + movingFast = 1; + } + } else { + EnHorse_BgCheckSlowMoving(this, globalCtx); + return; + } + + startPos = this->actor.world.pos; + startPos.y += 19.0f; + endPos = startPos; + endPos.x += (intersectDist * Math_SinS(this->actor.world.rot.y)); + endPos.y += (intersectDist * Math_SinS(-this->actor.shape.rot.x)); + endPos.z += (intersectDist * Math_CosS(this->actor.world.rot.y)); + intersect = endPos; + wall = NULL; + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &startPos, &endPos, &intersect, &wall, 1, 0, 0, 1, &bgId) == 1) { + intersectDist = sqrt(Math3D_Vec3fDistSq(&startPos, &intersect)); + this->stateFlags |= ENHORSE_OBSTACLE; + } + + if (wall != NULL) { + if (intersectDist < 30.0f) { + EnHorse_ResolveCollision(this, globalCtx, wall); + } + if ((Math_CosS(this->actor.world.rot.y - (s16)(Math_FAtan2F(wall->normal.x, wall->normal.z) * (0x8000 / M_PI)) - + 0x7FFF) < 0.5f) || + SurfaceType_IsHorseBlocked(&globalCtx->colCtx, wall, bgId) != 0) { + return; + } + + // too close to jump + if ((movingFast == false && intersectDist < 80.0f) || (movingFast == true && intersectDist < 150.0f)) { + if (movingFast == false) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + } else if (movingFast == true) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + EnHorse_StartBraking(this, globalCtx); + } + return; + } + + dynaPoly = DynaPoly_GetActor(&globalCtx->colCtx, bgId); + if ((this->stateFlags & ENHORSE_FLAG_26) && ((dynaPoly && dynaPoly->actor.id != 0x108) || dynaPoly == 0)) { + if (movingFast == false) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + } else if (movingFast == true) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + EnHorse_StartBraking(this, globalCtx); + } + return; + } + } + + // Get obstacle's height + intersectDist += 5.0f; + obstaclePos = startPos; + obstaclePos.x += intersectDist * Math_SinS(this->actor.world.rot.y); + obstaclePos.y = this->actor.world.pos.y + 120.0f; + obstaclePos.z += intersectDist * Math_CosS(this->actor.world.rot.y); + obstacleTop = obstaclePos; + obstacleTop.y = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &obstacleFloor, &bgId, &obstaclePos); + if (obstacleTop.y == BGCHECK_Y_MIN) { + return; + } + obstacleHeight = obstacleTop.y - this->actor.world.pos.y; + if (this->actor.floorPoly == NULL || obstacleFloor == NULL) { + return; + } + + if (Math3D_DistPlaneToPos(this->actor.floorPoly->normal.x * COLPOLY_NORMAL_FRAC, + this->actor.floorPoly->normal.y * COLPOLY_NORMAL_FRAC, + this->actor.floorPoly->normal.z * COLPOLY_NORMAL_FRAC, this->actor.floorPoly->dist, + &obstacleTop) < -40.0f && + Math3D_DistPlaneToPos( + obstacleFloor->normal.x * COLPOLY_NORMAL_FRAC, obstacleFloor->normal.y * COLPOLY_NORMAL_FRAC, + obstacleFloor->normal.z * COLPOLY_NORMAL_FRAC, obstacleFloor->dist, &this->actor.world.pos) > 40.0f) { + if (movingFast == true && this->action != ENHORSE_ACT_STOPPING) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + EnHorse_StartBraking(this, globalCtx); + } + this->stateFlags |= ENHORSE_OBSTACLE; + return; + } + + ny = obstacleFloor->normal.y * COLPOLY_NORMAL_FRAC; + if (ny < 0.81915206f || // cos(35 degrees) + (SurfaceType_IsHorseBlocked(&globalCtx->colCtx, obstacleFloor, bgId) != 0) || + (func_80041D4C(&globalCtx->colCtx, obstacleFloor, bgId) == 7)) { + if (movingFast == true && this->action != ENHORSE_ACT_STOPPING) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + EnHorse_StartBraking(this, globalCtx); + } + return; + } + + if (wall == NULL || obstacleTop.y < intersect.y || (this->stateFlags & ENHORSE_CANT_JUMP)) { + return; + } + + obstaclePos = startPos; + obstaclePos.y = this->actor.world.pos.y + 120.0f; + if (movingFast == false) { + obstaclePos.x += (276.0f * Math_SinS(this->actor.world.rot.y)); + obstaclePos.z += (276.0f * Math_CosS(this->actor.world.rot.y)); + } else { + obstaclePos.x += (390.0f * Math_SinS(this->actor.world.rot.y)); + obstaclePos.z += (390.0f * Math_CosS(this->actor.world.rot.y)); + } + + obstacleTop = obstaclePos; + obstacleTop.y = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &obstacleFloor, &bgId, &obstaclePos); + if (obstacleTop.y == BGCHECK_Y_MIN) { + return; + } + + behindObstacleHeight = obstacleTop.y - this->actor.world.pos.y; + + if (obstacleFloor == NULL) { + return; + } + + ny = obstacleFloor->normal.y * COLPOLY_NORMAL_FRAC; + if (ny < 0.81915206f || // cos(35 degrees) + SurfaceType_IsHorseBlocked(&globalCtx->colCtx, obstacleFloor, bgId) || + func_80041D4C(&globalCtx->colCtx, obstacleFloor, bgId) == 7) { + if (movingFast == true && this->action != ENHORSE_ACT_STOPPING) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + EnHorse_StartBraking(this, globalCtx); + } + } else if (behindObstacleHeight < -DREG(4)) { // -70 + if (movingFast == true && this->action != ENHORSE_ACT_STOPPING) { + this->stateFlags |= ENHORSE_FORCE_REVERSING; + EnHorse_StartBraking(this, globalCtx); + } + } else if (movingFast == false && obstacleHeight > 19.0f && obstacleHeight <= 40.0f) { + EnHorse_Stub1(this); + this->postDrawFunc = EnHorse_LowJumpInit; + } else if ((movingFast == true && this->actor.speedXZ < 13.8f && obstacleHeight > 19.0f && + obstacleHeight <= 72.0f) || + (this->actor.speedXZ > 13.8f && obstacleHeight <= 112.0f)) { + + EnHorse_Stub2(this); + this->postDrawFunc = EnHorse_HighJumpInit; + } +} + +void EnHorse_CheckBoost(EnHorse* thisx, GlobalContext* globalCtx2) { + EnHorse* this = (EnHorse*)thisx; + GlobalContext* globalCtx = globalCtx2; + s32 pad; + + if (this->action == ENHORSE_ACT_MOUNTED_WALK || this->action == ENHORSE_ACT_MOUNTED_TROT || + this->action == ENHORSE_ACT_MOUNTED_GALLOP) { + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_A) && (globalCtx->interfaceCtx.unk_1EE == 8)) { + if (!(this->stateFlags & ENHORSE_BOOST) && !(this->stateFlags & ENHORSE_FLAG_8) && + !(this->stateFlags & ENHORSE_FLAG_9)) { + if (this->numBoosts > 0) { + func_800AA000(0.0f, 180, 20, 100); + this->stateFlags |= ENHORSE_BOOST; + this->stateFlags |= ENHORSE_FIRST_BOOST_REGEN; + this->stateFlags |= ENHORSE_FLAG_8; + this->numBoosts--; + this->boostTimer = 0; + if (this->numBoosts == 0) { + this->boostRegenTime = 140; + return; + } + if (this->type == HORSE_EPONA) { + if (this->stateFlags & ENHORSE_FIRST_BOOST_REGEN) { + this->boostRegenTime = 60; + this->stateFlags &= ~ENHORSE_FIRST_BOOST_REGEN; + } else { + this->boostRegenTime = 8; // Never happens + } + } else { + this->boostRegenTime = 70; + } + return; + } + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + if (Rand_ZeroOne() < 0.1f) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } + } + } +} + +void EnHorse_RegenBoost(EnHorse* this, GlobalContext* globalCtx) { + if (this->numBoosts < 6 && this->numBoosts > 0) { + this->boostRegenTime--; + this->boostTimer++; + + if (this->boostRegenTime <= 0) { + this->numBoosts = this->numBoosts + 1; + + if (!EN_HORSE_CHECK_4(this)) { + Audio_PlaySoundGeneral(NA_SE_SY_CARROT_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + if (this->numBoosts < 6) { + this->boostRegenTime = 11; + } + } + } else if (this->numBoosts == 0) { + this->boostRegenTime--; + this->boostTimer++; + + if (this->boostRegenTime <= 0) { + this->boostRegenTime = 0; + this->numBoosts = 6; + + if (!EN_HORSE_CHECK_4(this)) { + Audio_PlaySoundGeneral(NA_SE_SY_CARROT_RECOVER, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + } + + if (this->boostTimer == 8 && Rand_ZeroOne() < 0.25f) { + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + globalCtx->interfaceCtx.numHorseBoosts = this->numBoosts; +} + +void EnHorse_UpdatePlayerDir(EnHorse* this, GlobalContext* globalCtx) { + EnHorse* pad; + s16 angle; + f32 s; + f32 c; + + angle = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y; + s = Math_SinS(angle); + c = Math_CosS(angle); + if (s > 0.8660254f) { // sin(60 degrees) + this->playerDir = PLAYER_DIR_SIDE_L; + } else if (s < -0.8660254f) { // -sin(60 degrees) + this->playerDir = PLAYER_DIR_SIDE_R; + } else { + if (c > 0.0f) { + if (s > 0) { + this->playerDir = PLAYER_DIR_FRONT_L; + } else { + this->playerDir = PLAYER_DIR_FRONT_R; + } + } else { + if (s > 0) { + this->playerDir = PLAYER_DIR_BACK_L; + } else { + this->playerDir = PLAYER_DIR_BACK_R; + } + } + } +} + +void EnHorse_TiltBody(EnHorse* this, GlobalContext* globalCtx) { + f32 speed; + f32 rollDiff; + s32 targetRoll; + s16 turnVel; + + speed = this->actor.speedXZ / this->boostSpeed; + turnVel = this->actor.shape.rot.y - this->lastYaw; + targetRoll = -((s16)((1820.0f * speed) * (turnVel / 480.00003f))); + rollDiff = targetRoll - this->actor.world.rot.z; + + if (fabsf(targetRoll) < 100.0f) { + this->actor.world.rot.z = 0; + } else if (fabsf(rollDiff) < 100.0f) { + this->actor.world.rot.z = targetRoll; + } else if (rollDiff > 0.0f) { + this->actor.world.rot.z += 100; + } else { + this->actor.world.rot.z -= 100; + } + + this->actor.shape.rot.z = this->actor.world.rot.z; +} + +s32 EnHorse_UpdateConveyors(EnHorse* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 conveyorDir; + + if ((this->actor.floorPoly == NULL) && (this != (EnHorse*)player->rideActor)) { + return 0; + } + conveyorDir = SurfaceType_GetConveyorDirection(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + conveyorDir = (conveyorDir << 10) - this->actor.world.rot.y; + if (conveyorDir > 800.0f) { + this->actor.world.rot.y += 800.0f; + } else if (conveyorDir < -800.0f) { + this->actor.world.rot.y -= 800.0f; + } else { + this->actor.world.rot.y += conveyorDir; + } + this->actor.shape.rot.y = this->actor.world.rot.y; + + return 1; +} + +s32 EnHorse_RandInt(f32 range) { + return Rand_ZeroOne() * range; +} + +void EnHorse_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnHorse* this = (EnHorse*)thisx; + GlobalContext* globalCtx = globalCtx2; + Vec3f dustAcc = { 0.0f, 0.0f, 0.0f }; + Vec3f dustVel = { 0.0f, 1.0f, 0.0f }; + Player* player = GET_PLAYER(globalCtx); + + this->lastYaw = thisx->shape.rot.y; + EnHorse_UpdateStick(this, globalCtx); + EnHorse_UpdatePlayerDir(this, globalCtx); + + if (!(this->stateFlags & ENHORSE_INACTIVE)) { + EnHorse_MountDismount(this, globalCtx); + } + + if (this->stateFlags & ENHORSE_FLAG_19) { + if (this->stateFlags & ENHORSE_FLAG_20 && this->inRace == true) { + this->stateFlags &= ~ENHORSE_FLAG_20; + EnHorse_StartRearing(this); + } else if (!(this->stateFlags & ENHORSE_FLAG_20) && this->stateFlags & ENHORSE_FLAG_21 && + this->action != ENHORSE_ACT_MOUNTED_REARING && this->inRace == true) { + this->stateFlags &= ~ENHORSE_FLAG_21; + EnHorse_StartRearing(this); + } + } + + sActionFuncs[this->action](this, globalCtx); + this->stateFlags &= ~ENHORSE_OBSTACLE; + this->curFrame = this->skin.skelAnime.curFrame; + this->lastPos = thisx->world.pos; + if (!(this->stateFlags & ENHORSE_INACTIVE)) { + if (this->action == ENHORSE_ACT_MOUNTED_GALLOP || this->action == ENHORSE_ACT_MOUNTED_TROT || + this->action == ENHORSE_ACT_MOUNTED_WALK) { + EnHorse_CheckBoost(this, globalCtx); + } + if (this->playerControlled == true) { + EnHorse_RegenBoost(this, globalCtx); + } + Actor_MoveForward(thisx); + if (this->action == ENHORSE_ACT_INGO_RACE) { + if (this->rider != NULL) { + this->rider->world.pos.x = thisx->world.pos.x; + this->rider->world.pos.y = thisx->world.pos.y + 10.0f; + this->rider->world.pos.z = thisx->world.pos.z; + this->rider->shape.rot.x = thisx->shape.rot.x; + this->rider->shape.rot.y = thisx->shape.rot.y; + } + } + if (this->jntSph.elements[0].info.ocElemFlags & 2) { + if (thisx->speedXZ > 6.0f) { + thisx->speedXZ -= 1.0f; + } + } + if (this->jntSph.base.acFlags & 2) { + this->unk_21C = this->unk_228; + if (this->stateFlags & ENHORSE_DRAW) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_21C, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + if (this->action != ENHORSE_ACT_INGO_RACE) { + EnHorse_TiltBody(this, globalCtx); + } + Collider_UpdateCylinder(thisx, &this->cyl1); + Collider_UpdateCylinder(thisx, &this->cyl2); + + // Required to match + this->cyl1.dim.pos.x = this->cyl1.dim.pos.x + (s16)(Math_SinS(thisx->shape.rot.y) * 11.0f); + this->cyl1.dim.pos.z = this->cyl1.dim.pos.z + (s16)(Math_CosS(thisx->shape.rot.y) * 11.0f); + this->cyl2.dim.pos.x = this->cyl2.dim.pos.x + (s16)(Math_SinS(thisx->shape.rot.y) * -18.0f); + this->cyl2.dim.pos.z = this->cyl2.dim.pos.z + (s16)(Math_CosS(thisx->shape.rot.y) * -18.0f); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->cyl1.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cyl1.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cyl2.base); + if ((player->stateFlags1 & 1) && player->rideActor != NULL) { + if (globalCtx->sceneNum != SCENE_SPOT20 || + (globalCtx->sceneNum == SCENE_SPOT20 && (thisx->world.pos.z < -2400.0f))) { + EnHorse_UpdateConveyors(this, globalCtx); + } + } + if (!(this->stateFlags & ENHORSE_FLAG_24)) { + EnHorse_UpdateBgCheckInfo(this, globalCtx); + EnHorse_CheckFloors(this, globalCtx); + if (thisx->world.pos.y < this->yFront && thisx->world.pos.y < this->yBack) { + if (this->yBack < this->yFront) { + thisx->world.pos.y = this->yBack; + } else { + thisx->world.pos.y = this->yFront; + } + } + + } else { + this->stateFlags &= ~ENHORSE_FLAG_24; + } + + if (globalCtx->sceneNum == SCENE_SPOT09 && (gSaveContext.eventChkInf[9] & 0xF) != 0xF) { + EnHorse_CheckBridgeJumps(this, globalCtx); + } + + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 70.0f; + if ((Rand_ZeroOne() < 0.025f) && this->blinkTimer == 0) { + this->blinkTimer++; + } else if (this->blinkTimer > 0) { + this->blinkTimer++; + if (this->blinkTimer >= 4) { + this->blinkTimer = 0; + } + } + + if (thisx->speedXZ == 0.0f && !(this->stateFlags & ENHORSE_FLAG_19)) { + thisx->colChkInfo.mass = 0xFF; + } else { + thisx->colChkInfo.mass = 0xFE; + } + + if (thisx->speedXZ >= 5.0f) { + this->cyl1.base.atFlags |= 1; + } else { + this->cyl1.base.atFlags &= ~1; + } + + if (gSaveContext.entranceIndex != 343 || gSaveContext.sceneSetupIndex != 9) { + if (this->dustFlags & 1) { + this->dustFlags &= ~1; + func_800287AC(globalCtx, &this->frontRightHoof, &dustVel, &dustAcc, EnHorse_RandInt(100) + 200, + EnHorse_RandInt(10) + 30, EnHorse_RandInt(20) + 30); + } else if (this->dustFlags & 2) { + this->dustFlags &= ~2; + func_800287AC(globalCtx, &this->frontLeftHoof, &dustVel, &dustAcc, EnHorse_RandInt(100) + 200, + EnHorse_RandInt(10) + 30, EnHorse_RandInt(20) + 30); + } else if (this->dustFlags & 4) { + this->dustFlags &= ~4; + func_800287AC(globalCtx, &this->backRightHoof, &dustVel, &dustAcc, EnHorse_RandInt(100) + 200, + EnHorse_RandInt(10) + 30, EnHorse_RandInt(20) + 30); + } else if (this->dustFlags & 8) { + this->dustFlags &= ~8; + func_800287AC(globalCtx, &this->backLeftHoof, &dustVel, &dustAcc, EnHorse_RandInt(100) + 200, + EnHorse_RandInt(10) + 30, EnHorse_RandInt(20) + 30); + } + } + this->stateFlags &= ~ENHORSE_DRAW; + } +} + +s32 EnHorse_PlayerDirToMountSide(EnHorse* this, GlobalContext* globalCtx, Player* player) { + if (this->playerDir == PLAYER_DIR_SIDE_L) { + return -1; + } + if (this->playerDir == PLAYER_DIR_SIDE_R) { + return 1; + } + return 0; +} + +s32 EnHorse_MountSideCheck(EnHorse* this, GlobalContext* globalCtx, Player* player) { + s32 mountSide; + + if (Actor_WorldDistXZToActor(&this->actor, &player->actor) > 75.0f) { + return 0; + } else if (fabsf(this->actor.world.pos.y - player->actor.world.pos.y) > 30.0f) { + return 0; + } else if (Math_CosS(Actor_WorldYawTowardActor(&player->actor, &this->actor) - player->actor.world.rot.y) < + 0.17364818f) { // cos(80 degrees) + return 0; + } else { + mountSide = EnHorse_PlayerDirToMountSide(this, globalCtx, player); + if (mountSide == -1) { + return -1; + } + if (mountSide == 1) { + return 1; + } + } + return 0; +} + +s32 EnHorse_GetMountSide(EnHorse* this, GlobalContext* globalCtx) { + if (this->action != ENHORSE_ACT_IDLE) { + return 0; + } + if ((this->animationIdx != ENHORSE_ANIM_IDLE) && (this->animationIdx != ENHORSE_ANIM_WHINNEY)) { + return 0; + } + return EnHorse_MountSideCheck(this, globalCtx, GET_PLAYER(globalCtx)); +} + +void EnHorse_RandomOffset(Vec3f* src, f32 dist, Vec3f* dst) { + dst->x = (Rand_ZeroOne() * (dist * 2.0f) + src->x) - dist; + dst->y = (Rand_ZeroOne() * (dist * 2.0f) + src->y) - dist; + dst->z = (Rand_ZeroOne() * (dist * 2.0f) + src->z) - dist; +} + +void EnHorse_PostDraw(Actor* thisx, GlobalContext* globalCtx, Skin* skin) { + EnHorse* this = (EnHorse*)thisx; + s32 pad; + Vec3f sp94 = { 0.0f, 0.0f, 0.0f }; + Vec3f hoofOffset = { 5.0f, -4.0f, 5.0f }; + Vec3f riderOffset = { 600.0f, -1670.0f, 0.0f }; + Vec3f sp70; + Vec3f sp64 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp58 = { 0.0f, -1.0f, 0.0f }; + + f32 frame = this->skin.skelAnime.curFrame; + Vec3f center; + Vec3f newCenter; + s32 i; + Vec3f sp2C; + f32 sp28; + + if (!(this->stateFlags & ENHORSE_CALC_RIDER_POS)) { + Skin_GetLimbPos(skin, 30, &riderOffset, &this->riderPos); + this->riderPos.x = this->riderPos.x - this->actor.world.pos.x; + this->riderPos.y = this->riderPos.y - this->actor.world.pos.y; + this->riderPos.z = this->riderPos.z - this->actor.world.pos.z; + } else { + this->stateFlags &= ~ENHORSE_CALC_RIDER_POS; + } + + Skin_GetLimbPos(skin, 13, &sp94, &sp2C); + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &sp2C, &this->unk_228, &sp28); + if ((this->animationIdx == ENHORSE_ANIM_IDLE && this->action != ENHORSE_ACT_FROZEN) && + ((frame > 40.0f && frame < 45.0f && this->type == HORSE_EPONA) || + (frame > 28.0f && frame < 33.0f && this->type == HORSE_HNI))) { + if (Rand_ZeroOne() < 0.6f) { + this->dustFlags |= 1; + Skin_GetLimbPos(skin, 28, &hoofOffset, &this->frontRightHoof); + this->frontRightHoof.y = this->frontRightHoof.y - 5.0f; + } + } else { + if (this->action == ENHORSE_ACT_STOPPING) { + if ((frame > 10.0f && frame < 13.0f) || (frame > 25.0f && frame < 33.0f)) { + if (Rand_ZeroOne() < 0.7f) { + this->dustFlags |= 2; + Skin_GetLimbPos(skin, 20, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->frontLeftHoof); + } + if (Rand_ZeroOne() < 0.7f) { + this->dustFlags |= 1; + Skin_GetLimbPos(skin, 28, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->frontRightHoof); + } + } + + if ((frame > 6.0f && frame < 10.0f) || (frame > 23.0f && frame < 29.0f)) { + if (Rand_ZeroOne() < 0.7f) { + this->dustFlags |= 8; + Skin_GetLimbPos(skin, 37, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backLeftHoof); + } + } + + if ((frame > 7.0f && frame < 14.0f) || (frame > 26.0f && frame < 30.0f)) { + if (Rand_ZeroOne() < 0.7f) { + this->dustFlags |= 4; + Skin_GetLimbPos(skin, 45, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backRightHoof); + } + } + } else if (this->animationIdx == ENHORSE_ANIM_GALLOP) { + if ((frame > 14.0f) && (frame < 16.0f)) { + this->dustFlags |= 1; + Skin_GetLimbPos(skin, 28, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 5.0f, &this->frontRightHoof); + } else if (frame > 8.0f && frame < 10.0f) { + this->dustFlags |= 2; + Skin_GetLimbPos(skin, 20, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->frontLeftHoof); + } else if (frame > 1.0f && frame < 3.0f) { + this->dustFlags |= 4; + Skin_GetLimbPos(skin, 45, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backRightHoof); + } else if ((frame > 26.0f) && (frame < 28.0f)) { + this->dustFlags |= 8; + Skin_GetLimbPos(skin, 37, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backLeftHoof); + } + } else if (this->action == ENHORSE_ACT_LOW_JUMP && frame > 6.0f && + Rand_ZeroOne() < 1.0f - (frame - 6.0f) * (1.0f / 17.0f)) { + if (Rand_ZeroOne() < 0.5f) { + this->dustFlags |= 8; + Skin_GetLimbPos(skin, 37, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backLeftHoof); + } + if (Rand_ZeroOne() < 0.5f) { + this->dustFlags |= 4; + Skin_GetLimbPos(skin, 45, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backRightHoof); + } + } else if (this->action == ENHORSE_ACT_HIGH_JUMP && frame > 5.0f && + Rand_ZeroOne() < 1.0f - (frame - 5.0f) * (1.0f / 25.0f)) { + if (Rand_ZeroOne() < 0.5f) { + this->dustFlags |= 8; + Skin_GetLimbPos(skin, 37, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backLeftHoof); + } + if (Rand_ZeroOne() < 0.5f) { + this->dustFlags |= 4; + Skin_GetLimbPos(skin, 45, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backRightHoof); + } + } else if (this->action == ENHORSE_ACT_BRIDGE_JUMP && Rand_ZeroOne() < 0.5f) { + if (Rand_ZeroOne() < 0.5f) { + this->dustFlags |= 8; + Skin_GetLimbPos(skin, 37, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backLeftHoof); + } else { + this->dustFlags |= 4; + Skin_GetLimbPos(skin, 45, &hoofOffset, &sp70); + EnHorse_RandomOffset(&sp70, 10.0f, &this->backRightHoof); + } + } + } + + for (i = 0; i < this->jntSph.count; i++) { + center.x = this->jntSph.elements[i].dim.modelSphere.center.x; + center.y = this->jntSph.elements[i].dim.modelSphere.center.y; + center.z = this->jntSph.elements[i].dim.modelSphere.center.z; + + Skin_GetLimbPos(skin, this->jntSph.elements[i].dim.limb, ¢er, &newCenter); + this->jntSph.elements[i].dim.worldSphere.center.x = newCenter.x; + this->jntSph.elements[i].dim.worldSphere.center.y = newCenter.y; + this->jntSph.elements[i].dim.worldSphere.center.z = newCenter.z; + this->jntSph.elements[i].dim.worldSphere.radius = + this->jntSph.elements[i].dim.modelSphere.radius * this->jntSph.elements[i].dim.scale; + } + + //! @bug Setting colliders in a draw function allows for duplicate entries to be added to their respective lists + //! under certain conditions, like when pausing and unpausing the game. + //! Actors will draw for a couple of frames between the pauses, but some important logic updates will not occur. + //! In the case of OC, this can cause unwanted effects such as a very large amount of displacement being applied to + //! a colliding actor. + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->jntSph.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->jntSph.base); +} + +// unused +static s32 D_80A667DC[] = { 0, 3, 7, 14 }; + +s32 EnHorse_OverrideLimbDraw(Actor* thisx, GlobalContext* globalCtx, s32 limbIndex, Skin* arg3) { + static void* eyeTextures[] = { + gEponaEyeOpenTex, + gEponaEyeHalfTex, + gEponaEyeClosedTex, + }; + static u8 eyeBlinkIndexes[] = { 0, 1, 2, 1 }; + EnHorse* this = (EnHorse*)thisx; + s32 drawOriginalLimb = true; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_horse.c", 8582); + if (limbIndex == 13 && this->type == HORSE_EPONA) { + u8 index = eyeBlinkIndexes[this->blinkTimer]; + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[index])); + } else if (this->type == HORSE_HNI && this->stateFlags & ENHORSE_FLAG_18 && limbIndex == 30) { + Skin_DrawLimb(globalCtx->state.gfxCtx, &this->skin, limbIndex, gHorseIngoGerudoSaddleDL, 0); + drawOriginalLimb = false; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_horse.c", 8601); + return drawOriginalLimb; +} + +void EnHorse_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHorse* this = (EnHorse*)thisx; + + if (!(this->stateFlags & ENHORSE_INACTIVE)) { + func_80093D18(globalCtx->state.gfxCtx); + this->stateFlags |= ENHORSE_DRAW; + if (this->stateFlags & ENHORSE_JUMPING) { + func_800A6360(thisx, globalCtx, &this->skin, EnHorse_PostDraw, EnHorse_OverrideLimbDraw, false); + } else { + func_800A6360(thisx, globalCtx, &this->skin, EnHorse_PostDraw, EnHorse_OverrideLimbDraw, true); + } + if (this->postDrawFunc != NULL) { + this->postDrawFunc(this, globalCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.h b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.h new file mode 100644 index 000000000..08c1cf226 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse/z_en_horse.h @@ -0,0 +1,191 @@ +#ifndef Z_EN_HORSE_H +#define Z_EN_HORSE_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0 */ ENHORSE_ACT_FROZEN, + /* 1 */ ENHORSE_ACT_INACTIVE, + /* 2 */ ENHORSE_ACT_IDLE, + /* 3 */ ENHORSE_ACT_FOLLOW_PLAYER, + /* 4 */ ENHORSE_ACT_INGO_RACE, + /* 5 */ ENHORSE_ACT_MOUNTED_IDLE, + /* 6 */ ENHORSE_ACT_MOUNTED_IDLE_WHINNEYING, + /* 7 */ ENHORSE_ACT_MOUNTED_TURN, + /* 8 */ ENHORSE_ACT_MOUNTED_WALK, + /* 9 */ ENHORSE_ACT_MOUNTED_TROT, + /* 10 */ ENHORSE_ACT_MOUNTED_GALLOP, + /* 11 */ ENHORSE_ACT_MOUNTED_REARING, + /* 12 */ ENHORSE_ACT_STOPPING, + /* 13 */ ENHORSE_ACT_REVERSE, + /* 14 */ ENHORSE_ACT_LOW_JUMP, + /* 15 */ ENHORSE_ACT_HIGH_JUMP, + /* 16 */ ENHORSE_ACT_BRIDGE_JUMP, + /* 17 */ ENHORSE_ACT_CS_UPDATE, + /* 18 */ ENHORSE_ACT_HBA, + /* 19 */ ENHORSE_ACT_FLEE_PLAYER +} EnHorseAction; + + +#define ENHORSE_BOOST (1 << 0) /* 0x1 */ +#define ENHORSE_BOOST_DECEL (1 << 1) /* 0x2 */ +#define ENHORSE_JUMPING (1 << 2) /* 0x4 */ +#define ENHORSE_CALC_RIDER_POS (1 << 3) /* 0x8 */ +#define ENHORSE_FORCE_REVERSING (1 << 4) /* 0x10 */ +#define ENHORSE_FORCE_WALKING (1 << 5) /* 0x20 */ +#define ENHORSE_FLAG_6 (1 << 6) /* 0x40 */ +#define ENHORSE_FLAG_7 (1 << 7) /* 0x80 */ +#define ENHORSE_FLAG_8 (1 << 8) /* 0x100 */ +#define ENHORSE_FLAG_9 (1 << 9) /* 0x200 */ +#define ENHORSE_STOPPING_NEIGH_SOUND (1 << 10) /* 0x400 */ +#define ENHORSE_LAND2_SOUND (1 << 11) /* 0x800 */ +#define ENHORSE_SANDDUST_SOUND (1 << 12) /* 0x1000 */ +#define ENHORSE_INACTIVE (1 << 13) /* 0x2000 */ +#define ENHORSE_OBSTACLE (1 << 14) /* 0x4000 */ +#define ENHORSE_TURNING_TO_PLAYER (1 << 15) /* 0x8000 */ +#define ENHORSE_UNRIDEABLE (1 << 16) /* 0x1 0000 */ +#define ENHORSE_CANT_JUMP (1 << 17) /* 0x2 0000 */ +#define ENHORSE_FLAG_18 (1 << 18) /* 0x4 0000 */ +#define ENHORSE_FLAG_19 (1 << 19) /* 0x8 0000 */ +#define ENHORSE_FLAG_20 (1 << 20) /* 0x10 0000 */ +#define ENHORSE_FLAG_21 (1 << 21) /* 0x20 0000 */ +#define ENHORSE_FIRST_BOOST_REGEN (1 << 22) /* 0x40 0000 */ +#define ENHORSE_INGO_WON (1 << 23) /* 0x80 0000 */ +#define ENHORSE_FLAG_24 (1 << 24) /* 0x100 0000 */ +#define ENHORSE_FLAG_25 (1 << 25) /* 0x200 0000 */ +#define ENHORSE_FLAG_26 (1 << 26) /* 0x400 0000 */ +#define ENHORSE_DRAW (1 << 27) /* 0x800 0000 */ +#define ENHORSE_FLAG_28 (1 << 28) /* 0x1000 0000 */ +#define ENHORSE_FLAG_29 (1 << 29) /* 0x2000 0000 */ +#define ENHORSE_FLAG_30 (1 << 30) /* 0x4000 0000 */ +#define ENHORSE_FLAG_31 (1 << 31) /* 0x8000 0000 */ + +struct EnHorse; + +typedef enum { + /* 0 */ PLAYER_DIR_FRONT_R, + /* 1 */ PLAYER_DIR_FRONT_L, + /* 2 */ PLAYER_DIR_BACK_R, + /* 3 */ PLAYER_DIR_BACK_L, + /* 4 */ PLAYER_DIR_SIDE_R, + /* 5 */ PLAYER_DIR_SIDE_L +} EnHorsePlayerDir; + +typedef enum { + /* 0 */ ENHORSE_ANIM_IDLE, + /* 1 */ ENHORSE_ANIM_WHINNEY, + /* 2 */ ENHORSE_ANIM_STOPPING, + /* 3 */ ENHORSE_ANIM_REARING, + /* 4 */ ENHORSE_ANIM_WALK, + /* 5 */ ENHORSE_ANIM_TROT, + /* 6 */ ENHORSE_ANIM_GALLOP, + /* 7 */ ENHORSE_ANIM_LOW_JUMP, + /* 8 */ ENHORSE_ANIM_HIGH_JUMP +} EnHorseAnimationIndex; + +typedef enum { + /* 0 */ HORSE_EPONA, + /* 1 */ HORSE_HNI +} HorseType; + +typedef void (*EnHorsePostdrawFunc)(struct EnHorse*, GlobalContext*); + +typedef struct EnHorse { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnHorseAction action; + /* 0x0150 */ s32 noInputTimer; + /* 0x0154 */ s32 noInputTimerMax; + /* 0x0158 */ s32 type; + /* 0x015C */ s8 bankIndex; + /* 0x0160 */ Skin skin; + /* 0x01F0 */ u32 stateFlags; + /* 0x01F4 */ Vec3f lastPos; + /* 0x0200 */ s16 lastYaw; + /* 0x0204 */ s32 curRaceWaypoint; + /* 0x0208 */ s32 boostSpeed; + /* 0x020C */ s32 playerControlled; + /* 0x0210 */ s32 animationIdx; + /* 0x0214 */ f32 curFrame; + /* 0x0218 */ s32 soundTimer; + /* 0x021C */ Vec3f unk_21C; + /* 0x0228 */ Vec3f unk_228; + /* 0x0234 */ s32 unk_234; + /* 0x0238 */ u8 numBoosts; + /* 0x023C */ s32 boostRegenTime; + /* 0x0240 */ s32 boostTimer; + /* 0x0244 */ EnHorsePostdrawFunc postDrawFunc; + /* 0x0248 */ f32 yFront; // The y coordinate of the floor under the front feet + /* 0x024C */ f32 yBack; // The y coordinate of the floor under the back feet + /* 0x0250 */ s16 followTimer; + /* 0x0252 */ s16 unk_252; + /* 0x0254 */ EnHorseAction prevAction; + /* 0x0258 */ Vec3f riderPos; + /* 0x0264 */ Vec2f curStick; + /* 0x026C */ Vec2f lastStick; + /* 0x0274 */ f32 jumpStartY; + /* 0x0278 */ ColliderCylinder cyl1; + /* 0x02C4 */ ColliderCylinder cyl2; + /* 0x0310 */ ColliderJntSph jntSph; + /* 0x0330 */ ColliderJntSphElement jntSphList; + /* 0x0370 */ u32 playerDir; + /* 0x0374 */ s16 unk_374; + /* 0x0376 */ s16 angleToPlayer; + /* 0x0378 */ s16 followPlayerTurnSpeed; + /* 0x037A */ u8 blinkTimer; + /* 0x037C */ s16 waitTimer; + /* 0x037E */ s16 unk_37E; + /* 0x0380 */ s32 cutsceneAction; + /* 0x0384 */ u16 cutsceneFlags; + // struct { + /* 0x0388 */ s32 inRace; + /* 0x038C */ Actor* rider; + /* 0x0390 */ u32 unk_390; + /* 0x0394 */ u16 ingoRaceFlags; + /* 0x0398 */ f32 ingoHorseMaxSpeed; + // } race; //? + /* 0x039C */ s32 unk_39C; // probably hbaAction + /* 0x03A0 */ s32 hbaStarted; + /* 0x03A4 */ s32 hbaFlags; + /* 0x03A8 */ s32 hbaTimer; + /* 0x03AC */ u8 bridgeJumpIdx; + /* 0x03B0 */ Vec3f bridgeJumpStart; + /* 0x03BC */ s32 bridgeJumpTimer; + /* 0x03C0 */ f32 bridgeJumpYVel; + /* 0x03C4 */ s16 bridgeJumpRelAngle; + /* 0x03C6 */ s16 unk_3C6; // pad + // sub struct? + /* 0x03C8 */ u16 dustFlags; + /* 0x03CC */ Vec3f frontRightHoof; + /* 0x03D8 */ Vec3f frontLeftHoof; + /* 0x03E4 */ Vec3f backRightHoof; + /* 0x03F0 */ Vec3f backLeftHoof; +} EnHorse; // size = 0x03FC + +#define EN_HORSE_CHECK_1(horseActor) \ + (((horseActor)->stateFlags & ENHORSE_FLAG_6) \ + ? true \ + : false) + +#define EN_HORSE_CHECK_2(horseActor) \ + (((horseActor)->stateFlags & ENHORSE_FLAG_8) \ + ? true \ + : false) + +#define EN_HORSE_CHECK_3(horseActor) \ + (((horseActor)->stateFlags & ENHORSE_FLAG_9) \ + ? true \ + : false) + +#define EN_HORSE_CHECK_4(horseActor) \ + (((((horseActor)->action == ENHORSE_ACT_MOUNTED_IDLE) || ((horseActor)->action == ENHORSE_ACT_FROZEN) || ((horseActor)->action == ENHORSE_ACT_MOUNTED_IDLE_WHINNEYING)) && \ + !((horseActor)->stateFlags & ENHORSE_FLAG_19) && !((horseActor)->stateFlags & ENHORSE_FLAG_25)) \ + ? true \ + : false) + +#define EN_HORSE_CHECK_JUMPING(horseActor) \ + (((horseActor)->stateFlags & ENHORSE_JUMPING) \ + ? true \ + : false) + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.c b/soh/src/overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.c new file mode 100644 index 000000000..1705b2a9c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.c @@ -0,0 +1,471 @@ +/* + * File: z_en_horse_game_check.c + * Overlay: ovl_En_Horse_Game_Check + * Description: Horseback Minigames + */ + +#include "z_en_horse_game_check.h" +#include "overlays/actors/ovl_En_Horse/z_en_horse.h" + +#define FLAGS ACTOR_FLAG_4 + +#define AT_FINISH_LINE(actor) \ + (Math3D_PointInSquare2D(sFinishLine[0], sFinishLine[1], sFinishLine[2], sFinishLine[3], (actor)->world.pos.x, \ + (actor)->world.pos.z)) +#define AT_RANCH_EXIT(actor) \ + (Math3D_PointInSquare2D(sRanchExit[0], sRanchExit[1], sRanchExit[2], sRanchExit[3], (actor)->world.pos.x, \ + (actor)->world.pos.z)) + +#define INGORACE_PLAYER_MOVE (1 << 0) +#define INGORACE_SET_TIMER (1 << 1) +#define INGORACE_INGO_MOVE (1 << 2) + +typedef enum { + /* 0 */ INGORACE_NO_RESULT, + /* 1 */ INGORACE_PLAYER_WIN, + /* 2 */ INGORACE_INGO_WIN, + /* 3 */ INGORACE_TIME_UP +} HorseGameIngoRaceResult; + +#define MALONRACE_PLAYER_MOVE (1 << 0) +#define MALONRACE_SET_TIMER (1 << 1) +#define MALONRACE_SECOND_LAP (1 << 2) +#define MALONRACE_BROKE_RULE (1 << 3) +#define MALONRACE_START_SFX (1 << 4) +#define MALONRACE_PLAYER_START (1 << 5) +#define MALONRACE_PLAYER_ON_MARK (1 << 6) + +typedef enum { + /* 0 */ MALONRACE_NO_RESULT, + /* 1 */ MALONRACE_SUCCESS, + /* 2 */ MALONRACE_TIME_UP, + /* 3 */ MALONRACE_UNUSED, + /* 4 */ MALONRACE_FAILURE +} HorseGameMalonRaceResult; + +void EnHorseGameCheck_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHorseGameCheck_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHorseGameCheck_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHorseGameCheck_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Horse_Game_Check_InitVars = { + ACTOR_EN_HORSE_GAME_CHECK, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnHorseGameCheck), + (ActorFunc)EnHorseGameCheck_Init, + (ActorFunc)EnHorseGameCheck_Destroy, + (ActorFunc)EnHorseGameCheck_Update, + (ActorFunc)EnHorseGameCheck_Draw, + NULL, +}; + +static Vec3f sIngoRaceCheckpoints[] = { + { 1700.0f, 1.0f, -540.0f }, + { 117.0f, 1.0f, 488.0f }, + { -1700.0f, 1.0f, -540.0f }, +}; + +static f32 sFinishLine[4] = { -200.0f, 80.0f, -2300.0f, -1470.0f }; + +static f32 sRanchExit[4] = { 800.0f, 1000.0f, -2900.0f, -2700.0f }; + +static Vec3f sUnusedZeroVec = { 0.0f, 0.0f, 0.0f }; + +static Vec3f sFencePos[] = { + { 820.0f, -44.0f, -1655.0f }, { 1497.0f, -21.0f, -1198.0f }, { 1655.0f, -44.0f, -396.0f }, + { 1291.0f, -44.0f, 205.0f }, { 379.0f, -21.0f, 455.0f }, { -95.0f, -21.0f, 455.0f }, + { -939.0f, 1.0f, 455.0f }, { -1644.0f, -21.0f, -1035.0f }, +}; + +s32 EnHorseGameCheck_InitIngoRace(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheckIngoRace* this = (EnHorseGameCheckIngoRace*)base; + s32 i; + + this->base.type = HORSEGAME_INGO_RACE; + this->startFlags = 0; + for (i = 0; i < 3; i++) { + this->playerCheck[i] = 0; + } + this->ingoHorse = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HORSE, -250.0f, 1.0f, -1650.0f, 0, 0x4000, 0, 0x8003); + + if (this->ingoHorse == NULL) { + LogUtils_HungupThread("../z_en_horse_game_check.c", 385); + } + this->startTimer = 0; + this->finishTimer = 0; + this->result = INGORACE_NO_RESULT; + this->playerFinish = 0; + this->ingoFinish = 0; + + return true; +} + +s32 EnHorseGameCheck_DestroyIngoRace(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + return true; +} + +void EnHorseGameCheck_FinishIngoRace(EnHorseGameCheckIngoRace* this, GlobalContext* globalCtx) { + gSaveContext.cutsceneIndex = 0; + if (this->result == INGORACE_PLAYER_WIN) { + globalCtx->nextEntranceIndex = 0x4CE; + if (gSaveContext.eventInf[0] & 0x40) { + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0xF) | 6; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + globalCtx->fadeTransition = 3; + Environment_ForcePlaySequence(NA_BGM_INGO); + } else { + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0xF) | 4; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + Environment_ForcePlaySequence(NA_BGM_INGO); + globalCtx->fadeTransition = 0x2E; + } + } else { + globalCtx->nextEntranceIndex = 0x558; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0xF) | 3; + globalCtx->fadeTransition = 0x20; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + } + DREG(25) = 0; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.timer1State = 0; +} + +s32 EnHorseGameCheck_UpdateIngoRace(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheckIngoRace* this = (EnHorseGameCheckIngoRace*)base; + Player* player = GET_PLAYER(globalCtx); + s32 i; + EnHorse* ingoHorse; + EnHorse* horse; + + if ((this->startTimer > 50) && !(this->startFlags & INGORACE_SET_TIMER)) { + this->startFlags |= INGORACE_SET_TIMER; + func_80088B34(0); + } else if ((this->startTimer > 80) && (player->rideActor != NULL) && !(this->startFlags & INGORACE_PLAYER_MOVE)) { + this->startFlags |= INGORACE_PLAYER_MOVE; + horse = (EnHorse*)player->rideActor; + horse->inRace = 1; + } else if ((this->startTimer > 81) && !(this->startFlags & INGORACE_INGO_MOVE)) { + ingoHorse = (EnHorse*)this->ingoHorse; + + ingoHorse->inRace = 1; + this->startFlags |= INGORACE_INGO_MOVE; + Audio_PlaySoundGeneral(NA_SE_SY_START_SHOT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + this->startTimer++; + + for (i = 0; i < 3; i++) { + if ((player->rideActor != NULL) && + (Math3D_Vec3f_DistXYZ(&sIngoRaceCheckpoints[i], &player->rideActor->world.pos) < 400.0f)) { + if ((i > 0) && (this->playerCheck[i - 1] == 1)) { + this->playerCheck[i] = 1; + } else if (i == 0) { + this->playerCheck[i] = 1; + } + } + if (Math3D_Vec3f_DistXYZ(&sIngoRaceCheckpoints[i], &this->ingoHorse->world.pos) < 400.0f) { + if ((i > 0) && (this->ingoCheck[i - 1] == 1)) { + this->ingoCheck[i] = 1; + } else if (i == 0) { + this->ingoCheck[i] = 1; + } + } + } + + if (this->result == INGORACE_NO_RESULT) { + Player* player2 = player; + + if ((player2->rideActor != NULL) && (this->playerCheck[2] == 1) && AT_FINISH_LINE(player2->rideActor)) { + this->playerFinish++; + if (this->playerFinish > 0) { + this->result = INGORACE_PLAYER_WIN; + this->finishTimer = 55; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HORSE_GOAL); + Audio_PlaySoundGeneral(NA_SE_SY_START_SHOT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + for (i = 0; i < 3; i++) { + this->playerCheck[i] = 0; + } + } + if ((this->ingoHorse != NULL) && (this->ingoCheck[2] == 1) && AT_FINISH_LINE(this->ingoHorse)) { + this->ingoFinish++; + if (this->ingoFinish > 0) { + ingoHorse = (EnHorse*)this->ingoHorse; + + this->result = INGORACE_INGO_WIN; + this->finishTimer = 70; + ingoHorse->stateFlags |= ENHORSE_INGO_WON; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HORSE_GOAL); + Audio_PlaySoundGeneral(NA_SE_SY_START_SHOT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + for (i = 0; i < 3; i++) { + this->ingoCheck[i] = 0; + } + } + if (((player2->rideActor != NULL) && AT_RANCH_EXIT(player2->rideActor)) || AT_RANCH_EXIT(&player2->actor)) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HORSE_GOAL); + this->result = INGORACE_INGO_WIN; + this->finishTimer = 20; + } + if ((gSaveContext.timer1Value >= 180) && (this->startFlags & 2)) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HORSE_GOAL); + this->result = INGORACE_TIME_UP; + this->finishTimer = 20; + } + } else { + if (this->finishTimer > 0) { + this->finishTimer--; + } else { + EnHorseGameCheck_FinishIngoRace(this, globalCtx); + } + } + return true; +} + +s32 EnHorseGameCheck_InitGerudoArchery(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheckGerudoArchery* this = (EnHorseGameCheckGerudoArchery*)base; + + this->base.type = HORSEGAME_GERUDO_ARCHERY; + this->unk_150 = 0; + this->startTimer = 0; + return true; +} + +s32 EnHorseGameCheck_DestroyGerudoArchery(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + return true; +} + +s32 EnHorseGameCheck_UpdateGerudoArchery(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheckGerudoArchery* this = (EnHorseGameCheckGerudoArchery*)base; + Player* player = GET_PLAYER(globalCtx); + EnHorse* horse = (EnHorse*)player->rideActor; + + if (horse == NULL) { + return true; + } + + if (this->startTimer > 90) { + if (globalCtx) {} + horse->hbaStarted = 1; + } + this->startTimer++; + return true; +} + +s32 EnHorseGameCheck_InitType3(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheck3* this = (EnHorseGameCheck3*)base; + + this->base.type = HORSEGAME_TYPE3; + this->unk_150 = 0; + return true; +} + +s32 EnHorseGameCheck_DestroyType3(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + return true; +} + +s32 EnHorseGameCheck_UpdateType3(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + return true; +} + +s32 EnHorseGameCheck_InitMalonRace(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheckMalonRace* this = (EnHorseGameCheckMalonRace*)base; + s32 i; + + this->base.type = HORSEGAME_MALON_RACE; + this->raceFlags = 0; + this->finishTimer = 0; + this->result = MALONRACE_NO_RESULT; + for (i = 0; i < 16; i++) { + this->fenceCheck[i] = 0; + } + this->lapCount = 0; + return true; +} + +s32 EnHorseGameCheck_DestroyMalonRace(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + return true; +} + +void EnHorseGameCheck_FinishMalonRace(EnHorseGameCheckMalonRace* this, GlobalContext* globalCtx) { + if ((this->result == MALONRACE_SUCCESS) || (this->result == MALONRACE_TIME_UP)) { + gSaveContext.cutsceneIndex = 0; + globalCtx->nextEntranceIndex = 0x4CE; + globalCtx->fadeTransition = 0x2E; + globalCtx->sceneLoadFlag = 0x14; + } else if (this->result == MALONRACE_FAILURE) { + gSaveContext.timer1Value = 240; + gSaveContext.timer1State = 0xF; + gSaveContext.cutsceneIndex = 0; + globalCtx->nextEntranceIndex = 0x4CE; + globalCtx->fadeTransition = 0x2E; + globalCtx->sceneLoadFlag = 0x14; + } else { + // "not supported" + osSyncPrintf("En_HGC_Spot20_Ta_end():対応せず\n"); + gSaveContext.cutsceneIndex = 0; + globalCtx->nextEntranceIndex = 0x157; + globalCtx->fadeTransition = 0x2E; + globalCtx->sceneLoadFlag = 0x14; + } +} + +s32 EnHorseGameCheck_UpdateMalonRace(EnHorseGameCheckBase* base, GlobalContext* globalCtx) { + EnHorseGameCheckMalonRace* this = (EnHorseGameCheckMalonRace*)base; + s32 i; + Player* player = GET_PLAYER(globalCtx); + EnHorse* horse; + + if (!(this->raceFlags & MALONRACE_PLAYER_ON_MARK) && AT_FINISH_LINE(player->rideActor)) { + this->raceFlags |= MALONRACE_PLAYER_ON_MARK; + } else if ((this->raceFlags & MALONRACE_PLAYER_ON_MARK) && !(this->raceFlags & MALONRACE_PLAYER_START) && + !AT_FINISH_LINE(player->rideActor)) { + this->raceFlags |= MALONRACE_PLAYER_START; + } + if ((this->startTimer > 50) && !(this->raceFlags & MALONRACE_SET_TIMER)) { + this->raceFlags |= MALONRACE_SET_TIMER; + func_80088B34(0); + } else if ((this->startTimer > 80) && (player->rideActor != NULL) && !(this->raceFlags & MALONRACE_PLAYER_MOVE)) { + this->raceFlags |= MALONRACE_PLAYER_MOVE; + horse = (EnHorse*)player->rideActor; + + horse->inRace = 1; + } else if ((this->startTimer > 81) && !(this->raceFlags & MALONRACE_START_SFX)) { + this->raceFlags |= MALONRACE_START_SFX; + Audio_PlaySoundGeneral(NA_SE_SY_START_SHOT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + this->startTimer++; + if (this->result == MALONRACE_NO_RESULT) { + Player* player2 = player; + f32 dist; + + for (i = 0; i < 16; i++) { + if ((this->lapCount == 0) && (i >= 8)) { + break; + } + dist = Math_Vec3f_DistXZ(&sFencePos[i % 8], &player2->rideActor->world.pos); + if ((player->rideActor != NULL) && (dist < 250.0f)) { + horse = (EnHorse*)player2->rideActor; + + if (horse->stateFlags & ENHORSE_JUMPING) { + if ((i > 0) && (this->fenceCheck[i - 1] == 1)) { + this->fenceCheck[i] = 1; + } else if (i == 0) { + this->fenceCheck[i] = 1; + } + + if ((this->fenceCheck[i - 1] == 0) && !(this->raceFlags & MALONRACE_BROKE_RULE)) { + this->raceFlags |= MALONRACE_BROKE_RULE; + Message_StartTextbox(globalCtx, 0x208C, NULL); + this->result = 4; + this->finishTimer = 30; + } + } + } + } + if ((player2->rideActor != NULL) && (this->raceFlags & MALONRACE_PLAYER_START) && + AT_FINISH_LINE(player2->rideActor)) { + if ((this->lapCount == 1) && (this->fenceCheck[15] == 0) && (player2->rideActor->prevPos.x < -200.0f)) { + this->raceFlags |= MALONRACE_BROKE_RULE; + Message_StartTextbox(globalCtx, 0x208C, NULL); + this->result = MALONRACE_FAILURE; + this->finishTimer = 30; + } else if (this->fenceCheck[15] == 1) { + this->lapCount = 2; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HORSE_GOAL); + Audio_PlaySoundGeneral(NA_SE_SY_START_SHOT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->result = MALONRACE_SUCCESS; + this->finishTimer = 70; + gSaveContext.timer1State = 0xF; + } else if ((this->fenceCheck[7] == 1) && !(this->raceFlags & MALONRACE_SECOND_LAP)) { + this->lapCount = 1; + this->raceFlags |= MALONRACE_SECOND_LAP; + Message_StartTextbox(globalCtx, 0x208D, NULL); + } else if (this->fenceCheck[7] == 0) { + this->raceFlags |= MALONRACE_BROKE_RULE; + Message_StartTextbox(globalCtx, 0x208C, NULL); + this->result = MALONRACE_FAILURE; + this->finishTimer = 30; + } else if (player2->rideActor->prevPos.x > 80.0f) { + this->raceFlags |= MALONRACE_BROKE_RULE; + Message_StartTextbox(globalCtx, 0x208C, NULL); + this->result = MALONRACE_FAILURE; + this->finishTimer = 30; + } + } + if ((gSaveContext.timer1Value >= 180) && (this->raceFlags & MALONRACE_SET_TIMER)) { + gSaveContext.timer1Value = 240; + this->result = MALONRACE_TIME_UP; + this->finishTimer = 30; + gSaveContext.timer1State = 0; + } + } else { + if (this->finishTimer > 0) { + this->finishTimer--; + } else { + EnHorseGameCheck_FinishMalonRace(this, globalCtx); + } + } + return true; +} + +static EnHorseGameCheckFunc sInitFuncs[] = { + NULL, + EnHorseGameCheck_InitIngoRace, + EnHorseGameCheck_InitGerudoArchery, + EnHorseGameCheck_InitType3, + EnHorseGameCheck_InitMalonRace, +}; + +static EnHorseGameCheckFunc sDestroyFuncs[] = { + NULL, + EnHorseGameCheck_DestroyIngoRace, + EnHorseGameCheck_DestroyGerudoArchery, + EnHorseGameCheck_DestroyType3, + EnHorseGameCheck_DestroyMalonRace, +}; + +static EnHorseGameCheckFunc sUpdateFuncs[] = { + NULL, + EnHorseGameCheck_UpdateIngoRace, + EnHorseGameCheck_UpdateGerudoArchery, + EnHorseGameCheck_UpdateType3, + EnHorseGameCheck_UpdateMalonRace, +}; + +void EnHorseGameCheck_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHorseGameCheckBase* this = (EnHorseGameCheckBase*)thisx; + + if ((globalCtx->sceneNum == SCENE_SPOT20) && (Flags_GetEventChkInf(0x18) || DREG(1))) { + this->actor.params = HORSEGAME_MALON_RACE; + } + if (sInitFuncs[this->actor.params] != NULL) { + sInitFuncs[this->actor.params](this, globalCtx); + } +} + +void EnHorseGameCheck_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHorseGameCheckBase* this = (EnHorseGameCheckBase*)thisx; + + if (sDestroyFuncs[this->actor.params] != NULL) { + sDestroyFuncs[this->actor.params](this, globalCtx); + } +} + +void EnHorseGameCheck_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnHorseGameCheckBase* this = (EnHorseGameCheckBase*)thisx; + + if (sUpdateFuncs[this->type] != NULL) { + sUpdateFuncs[this->type](this, globalCtx); + } +} + +void EnHorseGameCheck_Draw(Actor* thisx, GlobalContext* globalCtx) { +} diff --git a/soh/src/overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.h b/soh/src/overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.h new file mode 100644 index 000000000..0828d2705 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Game_Check/z_en_horse_game_check.h @@ -0,0 +1,65 @@ +#ifndef Z_EN_HORSE_GAME_CHECK_H +#define Z_EN_HORSE_GAME_CHECK_H + +#include "ultra64.h" +#include "global.h" + +struct EnHorseGameCheckBase; + +typedef s32 (*EnHorseGameCheckFunc)(struct EnHorseGameCheckBase* this, GlobalContext* globalCtx); + +typedef struct EnHorseGameCheckBase { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 type; +} EnHorseGameCheckBase; // size = 0x0150 + +typedef struct EnHorseGameCheckIngoRace { + /* 0x0000 */ EnHorseGameCheckBase base; + /* 0x0150 */ s32 startFlags; + /* 0x0154 */ s32 playerCheck[3]; + /* 0x0160 */ s32 ingoCheck[3]; + /* 0x016C */ Actor* ingoHorse; + /* 0x0170 */ s32 startTimer; + /* 0x0174 */ s32 result; + /* 0x0178 */ s32 finishTimer; + /* 0x017C */ s32 playerFinish; + /* 0x0180 */ s32 ingoFinish; +} EnHorseGameCheckIngoRace; // size = 0x0184 + +typedef struct EnHorseGameCheckGerudoArchery { + /* 0x0000 */ EnHorseGameCheckBase base; + /* 0x0150 */ s32 unk_150; + /* 0x0154 */ u32 startTimer; +} EnHorseGameCheckGerudoArchery; // size = 0x0158 + +typedef struct EnHorseGameCheck3 { + /* 0x0000 */ EnHorseGameCheckBase base; + /* 0x0150 */ s32 unk_150; +} EnHorseGameCheck3; // size = 0x0154 + +typedef struct EnHorseGameCheckMalonRace { + /* 0x0000 */ EnHorseGameCheckBase base; + /* 0x0150 */ s32 startTimer; + /* 0x0154 */ s32 raceFlags; + /* 0x0158 */ s32 fenceCheck[16]; + /* 0x0198 */ s32 finishTimer; + /* 0x019C */ s32 result; + /* 0x01A0 */ s32 lapCount; +} EnHorseGameCheckMalonRace; // size = 0x01A4 + +typedef union EnHorseGameCheck { + EnHorseGameCheckIngoRace ingo; + EnHorseGameCheckGerudoArchery gerudo; + EnHorseGameCheck3 type3; + EnHorseGameCheckMalonRace malon; +} EnHorseGameCheck; // size = 0x01A4 + +typedef enum { + /* 0 */ HORSEGAME_NONE, + /* 1 */ HORSEGAME_INGO_RACE, + /* 2 */ HORSEGAME_GERUDO_ARCHERY, + /* 3 */ HORSEGAME_TYPE3, + /* 4 */ HORSEGAME_MALON_RACE +} EnHorseGameCheckType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Horse_Ganon/z_en_horse_ganon.c b/soh/src/overlays/actors/ovl_En_Horse_Ganon/z_en_horse_ganon.c new file mode 100644 index 000000000..5432e8a36 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Ganon/z_en_horse_ganon.c @@ -0,0 +1,331 @@ +/* + * File: z_en_horse_ganon.c + * Overlay: ovl_En_Horse_Ganon + * Description: Ganondorf's Horse + */ + +#include "z_en_horse_ganon.h" +#include "objects/object_horse_ganon/object_horse_ganon.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef struct { + /* 0x0 */ Vec3s unk_0; + /* 0x6 */ u8 unk_6; +} unk_D_80A69248; // size = 0x8 + +void EnHorseGanon_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHorseGanon_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHorseGanon_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHorseGanon_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A68AC4(EnHorseGanon* this); +void func_80A68AF0(EnHorseGanon* this, GlobalContext* globalCtx); +void func_80A68DB0(EnHorseGanon* this, GlobalContext* globalCtx); + +const ActorInit En_Horse_Ganon_InitVars = { + ACTOR_EN_HORSE_GANON, + ACTORCAT_BG, + FLAGS, + OBJECT_HORSE_GANON, + sizeof(EnHorseGanon), + (ActorFunc)EnHorseGanon_Init, + (ActorFunc)EnHorseGanon_Destroy, + (ActorFunc)EnHorseGanon_Update, + (ActorFunc)EnHorseGanon_Draw, + NULL, +}; + +static AnimationHeader* sAnimations[] = { + &gHorseGanonIdleAnim, &gHorseGanonWhinnyAnim, &gHorseGanonWalkingAnim, + &gHorseGanonTrottingAnim, &gHorseGanonGallopingAnim, &gHorseGanonRearingAnim, +}; + +static f32 splaySpeeds[] = { 2.0f / 3.0f, 2.0f / 3.0f, 1.0f, 1.0f, 1.0f, 2.0f / 3.0f }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 40, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 10, 35, 100, MASS_HEAVY }; + +static unk_D_80A69248 D_80A69248[] = { + { 0x09B8, 0x0126, 0x0E2C, 0x07 }, { 0x0C11, 0x017A, 0x1269, 0x07 }, { 0x064E, 0xFEFB, 0x1DAC, 0x07 }, + { 0x02F2, 0xFF45, 0x244F, 0x07 }, { 0xF96E, 0xFE0C, 0x3122, 0x07 }, { 0xF328, 0xFE0C, 0x32D5, 0x07 }, + { 0xEBEA, 0xFE5F, 0x2D6E, 0x07 }, { 0xE95E, 0xFE27, 0x2565, 0x07 }, { 0xE593, 0xFE0C, 0x20AC, 0x07 }, + { 0xE625, 0xFE77, 0x1B07, 0x07 }, { 0xEBB7, 0x007C, 0x1539, 0x07 }, { 0xF466, 0x0002, 0x11B9, 0x07 }, + { 0xF47B, 0xFFDD, 0x11AF, 0x07 }, { 0xF88D, 0xFFD1, 0x0BA2, 0x07 }, +}; + +static s32 D_80A692B8[] = { 0, 16 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_STOP), +}; + +static EnHorseGanonActionFunc sActionFuncs[] = { func_80A68AF0, func_80A68DB0 }; + +void func_80A68660(unk_D_80A69248* data, s32 index, Vec3f* vec) { + vec->x = data[index].unk_0.x; + vec->y = data[index].unk_0.y; + vec->z = data[index].unk_0.z; +} + +void func_80A686A8(EnHorseGanon* this, GlobalContext* globalCtx) { + Vec3f* tempPos; + Vec3f vec; + s16 y; + + func_80A68660(D_80A69248, this->unk_1EC, &vec); + if (Math3D_Vec3f_DistXYZ(&vec, &this->actor.world.pos) <= 400.0f) { + this->unk_1EC += 1; + if (this->unk_1EC >= 14) { + this->unk_1EC = 0; + func_80A68660(D_80A69248, 0, &vec); + } + } + + tempPos = &this->actor.world.pos; + y = Math_Vec3f_Yaw(tempPos, &vec) - this->actor.world.rot.y; + if (y >= 301) { + this->actor.world.rot.y += 300; + } else if (y < -300) { + this->actor.world.rot.y -= 300; + } else { + this->actor.world.rot.y += y; + } + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor) <= 300.0f) { + if (this->actor.speedXZ < 12.0f) { + this->actor.speedXZ += 1.0f; + } else { + this->actor.speedXZ -= 1.0f; + } + } else if (this->actor.speedXZ < D_80A69248[this->unk_1EC].unk_6) { + this->actor.speedXZ += 0.5f; + } else { + this->actor.speedXZ -= 0.5f; + } +} + +void func_80A68870(EnHorseGanon* this) { + if ((this->skin.skelAnime.curFrame > D_80A692B8[this->soundCount]) && + (this->soundCount != 0 || !(this->skin.skelAnime.curFrame > D_80A692B8[1]))) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_WALK, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + this->soundCount++; + if (this->soundCount >= 2) { + this->soundCount = 0; + } + } +} + +void EnHorseGanon_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHorseGanon* this = (EnHorseGanon*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.0115f); + + this->actor.gravity = -3.5f; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawHorse, 20.0f); + this->actor.speedXZ = 0.0f; + this->actor.focus.pos = this->actor.world.pos; + this->action = 0; + this->actor.focus.pos.y += 70.0f; + Skin_Init(globalCtx, &this->skin, &gHorseGanonSkel, &gHorseGanonIdleAnim); + this->currentAnimation = 0; + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[0]); + + Collider_InitCylinder(globalCtx, &this->colliderBody); + Collider_SetCylinder(globalCtx, &this->colliderBody, &this->actor, &sCylinderInit); + Collider_InitJntSph(globalCtx, &this->colliderHead); + Collider_SetJntSph(globalCtx, &this->colliderHead, &this->actor, &sJntSphInit, this->headElements); + + CollisionCheck_SetInfo(&this->actor.colChkInfo, 0, &sColChkInfoInit); + func_80A68AC4(this); +} + +void EnHorseGanon_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHorseGanon* this = (EnHorseGanon*)thisx; + + Skin_Free(globalCtx, &this->skin); + Collider_DestroyCylinder(globalCtx, &this->colliderBody); + Collider_DestroyJntSph(globalCtx, &this->colliderHead); +} + +void func_80A68AC4(EnHorseGanon* this) { + this->action = 0; + Animation_PlayLoop(&this->skin.skelAnime, sAnimations[4]); +} + +void func_80A68AF0(EnHorseGanon* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + SkelAnime_Update(&this->skin.skelAnime); +} + +void func_80A68B20(EnHorseGanon* this) { + s32 animationChanged; + f32 sp30; + + animationChanged = 0; + this->action = 1; + if (this->actor.speedXZ <= 3.0f) { + if (this->currentAnimation != 2) { + animationChanged = 1; + } + this->currentAnimation = 2; + } else if (this->actor.speedXZ <= 6.0f) { + if (this->currentAnimation != 3) { + animationChanged = 1; + } + this->currentAnimation = 3; + } else { + if (this->currentAnimation != 4) { + animationChanged = 1; + } + this->currentAnimation = 4; + } + + if (this->currentAnimation == 2) { + sp30 = this->actor.speedXZ / 3.0f; + } else if (this->currentAnimation == 3) { + sp30 = this->actor.speedXZ / 5.0f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (this->currentAnimation == 4) { + sp30 = this->actor.speedXZ / 7.0f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + sp30 = 1.0f; + } + + if (animationChanged == 1) { + Animation_Change(&this->skin.skelAnime, sAnimations[this->currentAnimation], + splaySpeeds[this->currentAnimation] * sp30 * 1.5f, 0.0f, + Animation_GetLastFrame(sAnimations[this->currentAnimation]), ANIMMODE_ONCE, -3.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimations[this->currentAnimation], + splaySpeeds[this->currentAnimation] * sp30 * 1.5f, 0.0f, + Animation_GetLastFrame(sAnimations[this->currentAnimation]), ANIMMODE_ONCE, 0.0f); + } +} + +void func_80A68DB0(EnHorseGanon* this, GlobalContext* globalCtx) { + if (this->currentAnimation == 2) { + func_80A68870(this); + } + + func_80A686A8(this, globalCtx); + + if (SkelAnime_Update(&this->skin.skelAnime) != 0) { + func_80A68B20(this); + } +} + +void func_80A68E14(EnHorseGanon* this, GlobalContext* globalCtx) { + s32 pad; + CollisionPoly* col; + f32 temp_ret; + Vec3f v; + s32 temp1; + + v.x = Math_SinS(this->actor.shape.rot.y) * 30.0f + this->actor.world.pos.x; + v.y = this->actor.world.pos.y + 60.0f; + v.z = Math_CosS(this->actor.shape.rot.y) * 30.0f + this->actor.world.pos.z; + + temp_ret = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &col, &temp1, &v); + + this->unk_1F4 = temp_ret; + this->actor.shape.rot.x = (0x8000 / M_PI) * Math_FAtan2F(this->actor.world.pos.y - temp_ret, 30.0f); +} + +void EnHorseGanon_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHorseGanon* this = (EnHorseGanon*)thisx; + s32 pad; + + sActionFuncs[this->action](this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 55.0f, 100.0f, 29); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + Collider_UpdateCylinder(&this->actor, &this->colliderBody); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderBody.base); +} + +void EnHorseGanon_PostDraw(Actor* thisx, GlobalContext* globalCtx, Skin* skin) { + Vec3f sp4C; + Vec3f sp40; + EnHorseGanon* this = (EnHorseGanon*)thisx; + s32 index; + + for (index = 0; index < this->colliderHead.count; index++) { + sp4C.x = this->colliderHead.elements[index].dim.modelSphere.center.x; + sp4C.y = this->colliderHead.elements[index].dim.modelSphere.center.y; + sp4C.z = this->colliderHead.elements[index].dim.modelSphere.center.z; + + Skin_GetLimbPos(skin, this->colliderHead.elements[index].dim.limb, &sp4C, &sp40); + + this->colliderHead.elements[index].dim.worldSphere.center.x = sp40.x; + this->colliderHead.elements[index].dim.worldSphere.center.y = sp40.y; + this->colliderHead.elements[index].dim.worldSphere.center.z = sp40.z; + + this->colliderHead.elements[index].dim.worldSphere.radius = + this->colliderHead.elements[index].dim.modelSphere.radius * this->colliderHead.elements[index].dim.scale; + } + + //! @bug see relevant comment in `EnHorse_SkinCallback1` + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderHead.base); +} + +void EnHorseGanon_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHorseGanon* this = (EnHorseGanon*)thisx; + + func_80A68E14(this, globalCtx); + func_80093D18(globalCtx->state.gfxCtx); + func_800A6330(&this->actor, globalCtx, &this->skin, EnHorseGanon_PostDraw, true); +} diff --git a/soh/src/overlays/actors/ovl_En_Horse_Ganon/z_en_horse_ganon.h b/soh/src/overlays/actors/ovl_En_Horse_Ganon/z_en_horse_ganon.h new file mode 100644 index 000000000..a7fd8378e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Ganon/z_en_horse_ganon.h @@ -0,0 +1,27 @@ +#ifndef Z_EN_HORSE_GANON_H +#define Z_EN_HORSE_GANON_H + +#include "ultra64.h" +#include "global.h" + +struct EnHorseGanon; + +typedef void (*EnHorseGanonActionFunc)(struct EnHorseGanon*, GlobalContext*); + +typedef struct EnHorseGanon { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; + /* 0x0150 */ s32 currentAnimation; + /* 0x0154 */ Skin skin; + /* 0x01E4 */ u8 unk_1E4[0x04]; + /* 0x01E8 */ s32 soundCount; + /* 0x01EC */ s32 unk_1EC; + /* 0x01F0 */ u8 unk_1F0[0x04]; + /* 0x01F4 */ f32 unk_1F4; + /* 0x01F8 */ u8 unk_1F8[0x04]; + /* 0x01FC */ ColliderCylinder colliderBody; + /* 0x0248 */ ColliderJntSph colliderHead; + /* 0x0268 */ ColliderJntSphElement headElements[1]; +} EnHorseGanon; // size = 0x02A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Horse_Link_Child/z_en_horse_link_child.c b/soh/src/overlays/actors/ovl_En_Horse_Link_Child/z_en_horse_link_child.c new file mode 100644 index 000000000..6ea9a8b1d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Link_Child/z_en_horse_link_child.c @@ -0,0 +1,626 @@ +/* + * File: z_en_horse_link_child.c + * Overlay: ovl_En_Horse_Link_Child + * Description: Young Epona + */ + +#include "z_en_horse_link_child.h" +#include "objects/object_horse_link_child/object_horse_link_child.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnHorseLinkChild_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHorseLinkChild_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHorseLinkChild_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHorseLinkChild_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A69B7C(EnHorseLinkChild* this); +void func_80A69EC0(EnHorseLinkChild* this); +void func_80A6A4DC(EnHorseLinkChild* this); +void func_80A6A724(EnHorseLinkChild* this); + +const ActorInit En_Horse_Link_Child_InitVars = { + ACTOR_EN_HORSE_LINK_CHILD, + ACTORCAT_BG, + FLAGS, + OBJECT_HORSE_LINK_CHILD, + sizeof(EnHorseLinkChild), + (ActorFunc)EnHorseLinkChild_Init, + (ActorFunc)EnHorseLinkChild_Destroy, + (ActorFunc)EnHorseLinkChild_Update, + (ActorFunc)EnHorseLinkChild_Draw, + NULL, +}; + +static AnimationHeader* sAnimations[] = { + &gChildEponaIdleAnim, &gChildEponaWhinnyAnim, &gChildEponaWalkingAnim, + &gChildEponaTrottingAnim, &gChildEponaGallopingAnim, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 10 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementInit, +}; + +static CollisionCheckInfoInit sColCheckInfoInit = { 10, 35, 100, MASS_HEAVY }; + +void func_80A693D0(EnHorseLinkChild* this) { + static s32 D_80A6AF5C[] = { 1, 19 }; + + if ((this->skin.skelAnime.curFrame > D_80A6AF5C[this->unk_1F0]) && + !((this->unk_1F0 == 0) && (this->skin.skelAnime.curFrame > D_80A6AF5C[1]))) { + Audio_PlaySoundGeneral(NA_SE_EV_KID_HORSE_WALK, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_1F0++; + if (this->unk_1F0 >= ARRAY_COUNT(D_80A6AF5C)) { + this->unk_1F0 = 0; + } + } +} + +void func_80A6948C(EnHorseLinkChild* this) { + if (this->animationIdx == 2) { + func_80A693D0(this); + } else if (this->skin.skelAnime.curFrame == 0.0f) { + if ((this->animationIdx == 3) || (this->animationIdx == 4)) { + Audio_PlaySoundGeneral(NA_SE_EV_KID_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (this->animationIdx == 1) { + if (Rand_ZeroOne() > 0.5f) { + Audio_PlaySoundGeneral(NA_SE_EV_KID_HORSE_GROAN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_EV_KID_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } +} + +static f32 D_80A6AF64[] = { 1.0f, 1.0f, 1.5f, 1.5f, 1.5f }; + +f32 func_80A695A4(EnHorseLinkChild* this) { + f32 result; + + if (this->animationIdx == 2) { + result = D_80A6AF64[this->animationIdx] * this->actor.speedXZ * (1.0f / 2.0f); + } else if (this->animationIdx == 3) { + result = D_80A6AF64[this->animationIdx] * this->actor.speedXZ * (1.0f / 3.0f); + } else if (this->animationIdx == 4) { + result = D_80A6AF64[this->animationIdx] * this->actor.speedXZ * (1.0f / 5.0f); + } else { + result = D_80A6AF64[this->animationIdx]; + } + + return result; +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_STOP), +}; + +void EnHorseLinkChild_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHorseLinkChild* this = (EnHorseLinkChild*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.005f); + this->actor.gravity = -3.5f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawHorse, 20.0f); + this->actor.speedXZ = 0.0f; + this->action = 1; + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + Skin_Init(globalCtx, &this->skin, &gChildEponaSkel, &gChildEponaGallopingAnim); + this->animationIdx = 0; + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[0]); + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinderType1(globalCtx, &this->bodyCollider, &this->actor, &sCylinderInit); + Collider_InitJntSph(globalCtx, &this->headCollider); + Collider_SetJntSph(globalCtx, &this->headCollider, &this->actor, &sJntSphInit, this->headElements); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColCheckInfoInit); + this->unk_1F0 = 0; + this->eyeTexIndex = 0; + + if (gSaveContext.sceneSetupIndex > 3) { + func_80A69EC0(this); + } else if (globalCtx->sceneNum == SCENE_SPOT20) { + if (!Flags_GetEventChkInf(0x14)) { + Actor_Kill(&this->actor); + return; + } + this->unk_2A0 = gSaveContext.eventChkInf[1] & 0x40; + func_80A69EC0(this); + } else { + func_80A69EC0(this); + } + + this->actor.home.rot.z = this->actor.world.rot.z = this->actor.shape.rot.z = 0; +} + +void EnHorseLinkChild_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHorseLinkChild* this = (EnHorseLinkChild*)thisx; + + Skin_Free(globalCtx, &this->skin); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyJntSph(globalCtx, &this->headCollider); +} + +void func_80A6988C(EnHorseLinkChild* this) { + this->action = 0; + this->animationIdx++; + if (this->animationIdx >= ARRAY_COUNT(sAnimations)) { + this->animationIdx = 0; + } + + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); + this->skin.skelAnime.playSpeed = func_80A695A4(this); +} + +void func_80A698F4(EnHorseLinkChild* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + if (SkelAnime_Update(&this->skin.skelAnime)) { + func_80A6988C(this); + } +} + +void func_80A6993C(EnHorseLinkChild* this, s32 newAnimationIdx) { + this->action = 2; + this->actor.speedXZ = 0.0f; + + if (!((newAnimationIdx == 0) || (newAnimationIdx == 1))) { + newAnimationIdx = 0; + } + + if (this->animationIdx != newAnimationIdx) { + this->animationIdx = newAnimationIdx; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); + } +} + +void func_80A699FC(EnHorseLinkChild* this, GlobalContext* globalCtx) { + f32 distFromLink; + s32 newAnimationIdx; + + distFromLink = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + + if (SkelAnime_Update(&this->skin.skelAnime)) { + if ((distFromLink < 1000.0f) && (distFromLink > 70.0f)) { + func_80A69B7C(this); + } else { + newAnimationIdx = this->animationIdx == 1 ? 0 : 1; + if (this->animationIdx != newAnimationIdx) { + this->animationIdx = newAnimationIdx; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0); + } + } + } +} + +void func_80A69B7C(EnHorseLinkChild* this) { + this->action = 1; + this->animationIdx = 0; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); +} + +void func_80A69C18(EnHorseLinkChild* this, GlobalContext* globalCtx) { + s16 yawDiff; + f32 distFromLink; + s32 newAnimationIdx; + + if ((this->animationIdx == 4) || (this->animationIdx == 3) || (this->animationIdx == 2)) { + yawDiff = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y; + + if (yawDiff > 0x12C) { + this->actor.world.rot.y += 0x12C; + } else if (yawDiff < -0x12C) { + this->actor.world.rot.y -= 0x12C; + } else { + this->actor.world.rot.y += yawDiff; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + distFromLink = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + + if (distFromLink > 1000.0f) { + func_80A6993C(this, 0); + return; + } else if ((distFromLink < 1000.0f) && (distFromLink >= 300.0f)) { + newAnimationIdx = 4; + this->actor.speedXZ = 6.0f; + } else if ((distFromLink < 300.0f) && (distFromLink >= 150.0f)) { + newAnimationIdx = 3; + this->actor.speedXZ = 4.0f; + } else if ((distFromLink < 150.0f) && (distFromLink >= 70.0f)) { + newAnimationIdx = 2; + this->actor.speedXZ = 2.0f; + this->unk_1F0 = 0; + } else { + func_80A6993C(this, 1); + return; + } + + if (this->animationIdx != newAnimationIdx) { + this->animationIdx = newAnimationIdx; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); + } + } +} + +void func_80A69EC0(EnHorseLinkChild* this) { + this->action = 3; + this->animationIdx = 0; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); +} + +void func_80A69F5C(EnHorseLinkChild* this, GlobalContext* globalCtx) { + Player* player; + s16 yawDiff; + s32 yawSign; + s32 yawOffset; + + if ((this->animationIdx == 4) || (this->animationIdx == 3) || (this->animationIdx == 2)) { + player = GET_PLAYER(globalCtx); + + if (Math3D_Vec3f_DistXYZ(&player->actor.world.pos, &this->actor.home.pos) < 250.0f) { + yawDiff = player->actor.shape.rot.y; + yawSign = Actor_WorldYawTowardActor(&this->actor, &player->actor) > 0 ? 1 : -1; + yawOffset = yawSign << 0xE; + yawDiff += yawOffset; + } else { + yawDiff = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos) - this->actor.world.rot.y; + } + + if (yawDiff > 0x12C) { + this->actor.world.rot.y += 0x12C; + } else if (yawDiff < -0x12C) { + this->actor.world.rot.y -= 0x12C; + } else { + this->actor.world.rot.y += yawDiff; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + } +} + +void func_80A6A068(EnHorseLinkChild* this, GlobalContext* globalCtx) { + Player* player; + f32 distFromLink; + s32 animationEnded; + s32 newAnimationIdx; + f32 distFromHome; + f32 distLinkFromHome; + + func_80A69F5C(this, globalCtx); + player = GET_PLAYER(globalCtx); + distFromLink = Actor_WorldDistXZToActor(&this->actor, &player->actor); + + if (gSaveContext.entranceIndex == 0x2AE) { + Audio_PlaySoundGeneral(NA_SE_EV_KID_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_80A6A724(this); + return; + } + + if (((gSaveContext.eventChkInf[1] & 0x40) && (DREG(53) != 0)) || + ((globalCtx->sceneNum == SCENE_SPOT20) && (gSaveContext.cutsceneIndex == 0xFFF1))) { + func_80A6A4DC(this); + } else { + this->unk_2A0 = gSaveContext.eventChkInf[1] & 0x40; + } + + newAnimationIdx = this->animationIdx; + animationEnded = SkelAnime_Update(&this->skin.skelAnime); + if (animationEnded || (this->animationIdx == 1) || (this->animationIdx == 0)) { + if (gSaveContext.eventChkInf[1] & 0x20) { + distFromHome = Math3D_Vec3f_DistXYZ(&this->actor.world.pos, &this->actor.home.pos); + distLinkFromHome = Math3D_Vec3f_DistXYZ(&player->actor.world.pos, &this->actor.home.pos); + if (distLinkFromHome > 250.0f) { + if (distFromHome >= 300.0f) { + newAnimationIdx = 4; + this->actor.speedXZ = 6.0f; + } else if ((distFromHome < 300.0f) && (distFromHome >= 150.0f)) { + newAnimationIdx = 3; + this->actor.speedXZ = 4.0f; + } else if ((distFromHome < 150.0f) && (distFromHome >= 70.0f)) { + newAnimationIdx = 2; + this->actor.speedXZ = 2.0f; + this->unk_1F0 = 0; + } else { + this->actor.speedXZ = 0.0f; + if (this->animationIdx == 0) { + newAnimationIdx = animationEnded == true ? 1 : 0; + } else { + newAnimationIdx = animationEnded == true ? 0 : 1; + } + } + } else { + if (distFromLink < 200.0f) { + newAnimationIdx = 4; + this->actor.speedXZ = 6.0f; + } else if (distFromLink < 300.0f) { + newAnimationIdx = 3; + this->actor.speedXZ = 4.0f; + } else if (distFromLink < 400.0f) { + newAnimationIdx = 2; + this->actor.speedXZ = 2.0f; + this->unk_1F0 = 0; + } else { + this->actor.speedXZ = 0.0f; + if (this->animationIdx == 0) { + newAnimationIdx = animationEnded == true ? 1 : 0; + } else { + newAnimationIdx = animationEnded == true ? 0 : 1; + } + } + } + } else { + this->actor.speedXZ = 0.0f; + if (this->animationIdx == 0) { + newAnimationIdx = animationEnded == true ? 1 : 0; + } else { + newAnimationIdx = animationEnded == true ? 0 : 1; + } + } + } + + if ((this->animationIdx != newAnimationIdx) || (animationEnded == true)) { + this->animationIdx = newAnimationIdx; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), + this->skin.skelAnime.curFrame, Animation_GetLastFrame(sAnimations[this->animationIdx]), 2, + 0.0f); + } +} + +void func_80A6A4DC(EnHorseLinkChild* this) { + this->action = 5; + this->animationIdx = Rand_ZeroOne() > 0.5f ? 0 : 1; + DREG(53) = 0; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); +} + +void func_80A6A5A4(EnHorseLinkChild* this, GlobalContext* globalCtx) { + s16 yawDiff; + + if (DREG(53) != 0) { + DREG(53) = 0; + Audio_PlaySoundGeneral(NA_SE_EV_KID_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_80A6A724(this); + } else { + this->actor.speedXZ = 0.0f; + yawDiff = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y; + // 0.7071 = cos(pi/4) + if ((Math_CosS(yawDiff) < 0.7071f) && (this->animationIdx == 2)) { + func_8006DD9C(&this->actor, &GET_PLAYER(globalCtx)->actor.world.pos, 300); + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + if (Math_CosS(yawDiff) < 0.0f) { + this->animationIdx = 2; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], D_80A6AF64[this->animationIdx], + 0.0f, Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); + } else { + func_80A6A4DC(this); + } + } + } +} + +void func_80A6A724(EnHorseLinkChild* this) { + this->timer = 0; + this->action = 4; + this->animationIdx = 2; + this->unk_1E8 = false; + this->actor.speedXZ = 2.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); +} + +void func_80A6A7D0(EnHorseLinkChild* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 dist; + s32 newAnimationIdx; + + this->timer++; + if (this->timer > 300) { + this->unk_1E8 = true; + } + + if ((this->animationIdx == 4) || (this->animationIdx == 3) || (this->animationIdx == 2)) { + if (!this->unk_1E8) { + func_8006DD9C(&this->actor, &player->actor.world.pos, 300); + } else { + func_8006DD9C(&this->actor, &this->actor.home.pos, 300); + } + } + + if (SkelAnime_Update(&this->skin.skelAnime)) { + if (!this->unk_1E8) { + dist = Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor); + } else { + dist = Math3D_Vec3f_DistXYZ(&this->actor.world.pos, &this->actor.home.pos); + } + + if (!this->unk_1E8) { + if (dist >= 300.0f) { + newAnimationIdx = 4; + this->actor.speedXZ = 6.0f; + } else if (dist >= 150.0f) { + newAnimationIdx = 3; + this->actor.speedXZ = 4.0f; + } else { + newAnimationIdx = 2; + this->actor.speedXZ = 2.0f; + this->unk_1F0 = 0; + } + } else { + if (dist >= 300.0f) { + newAnimationIdx = 4; + this->actor.speedXZ = 6.0f; + } else if (dist >= 150.0f) { + newAnimationIdx = 3; + this->actor.speedXZ = 4.0f; + } else if (dist >= 70.0f) { + newAnimationIdx = 2; + this->actor.speedXZ = 2.0f; + this->unk_1F0 = 0; + } else { + func_80A6A4DC(this); + return; + } + } + + if (this->animationIdx != newAnimationIdx) { + this->animationIdx = newAnimationIdx; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -5.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A695A4(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); + } + } +} + +static EnHorseLinkChildActionFunc sActionFuncs[] = { + func_80A698F4, func_80A69C18, func_80A699FC, func_80A6A068, func_80A6A7D0, func_80A6A5A4, +}; + +static void* sEyeTextures[] = { gChildEponaEyeOpenTex, gChildEponaEyeHalfTex, gChildEponaEyeCloseTex }; +static u8 sEyeIndexOrder[] = { 0, 1, 2, 1 }; + +void EnHorseLinkChild_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHorseLinkChild* this = (EnHorseLinkChild*)thisx; + s32 pad; + + sActionFuncs[this->action](this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 55.0f, 100.0f, 0x1D); + + if ((globalCtx->sceneNum == SCENE_SPOT20) && (this->actor.world.pos.z < -2400.0f)) { + this->actor.world.pos.z = -2400.0f; + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + + if ((Rand_ZeroOne() < 0.025f) && (this->eyeTexIndex == 0)) { + this->eyeTexIndex++; + } else if (this->eyeTexIndex > 0) { + this->eyeTexIndex++; + if (this->eyeTexIndex >= ARRAY_COUNT(sEyeIndexOrder)) { + this->eyeTexIndex = 0; + } + } + + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + func_80A6948C(this); +} + +void EnHorseLinkChild_PostDraw(Actor* thisx, GlobalContext* globalCtx, Skin* skin) { + Vec3f center; + Vec3f newCenter; + EnHorseLinkChild* this = (EnHorseLinkChild*)thisx; + s32 i; + + for (i = 0; i < this->headCollider.count; i++) { + center.x = this->headCollider.elements[i].dim.modelSphere.center.x; + center.y = this->headCollider.elements[i].dim.modelSphere.center.y; + center.z = this->headCollider.elements[i].dim.modelSphere.center.z; + Skin_GetLimbPos(skin, this->headCollider.elements[i].dim.limb, ¢er, &newCenter); + this->headCollider.elements[i].dim.worldSphere.center.x = newCenter.x; + this->headCollider.elements[i].dim.worldSphere.center.y = newCenter.y; + this->headCollider.elements[i].dim.worldSphere.center.z = newCenter.z; + this->headCollider.elements[i].dim.worldSphere.radius = + this->headCollider.elements[i].dim.modelSphere.radius * this->headCollider.elements[i].dim.scale; + } + + //! @bug see relevant comment in `EnHorse_SkinCallback1` + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->headCollider.base); +} + +s32 EnHorseLinkChild_OverrideLimbDraw(Actor* thisx, GlobalContext* globalCtx, s32 arg2, Skin* skin) { + EnHorseLinkChild* this = (EnHorseLinkChild*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_horse_link_child.c", 1467); + + if (arg2 == 0xD) { + u8 index = sEyeIndexOrder[this->eyeTexIndex]; + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[index])); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_horse_link_child.c", 1479); + + return 1; +} + +void EnHorseLinkChild_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHorseLinkChild* this = (EnHorseLinkChild*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + func_800A6360(&this->actor, globalCtx, &this->skin, EnHorseLinkChild_PostDraw, EnHorseLinkChild_OverrideLimbDraw, + true); +} diff --git a/soh/src/overlays/actors/ovl_En_Horse_Link_Child/z_en_horse_link_child.h b/soh/src/overlays/actors/ovl_En_Horse_Link_Child/z_en_horse_link_child.h new file mode 100644 index 000000000..6e813173d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Link_Child/z_en_horse_link_child.h @@ -0,0 +1,26 @@ +#ifndef Z_EN_HORSE_LINK_CHILD_H +#define Z_EN_HORSE_LINK_CHILD_H + +#include "ultra64.h" +#include "global.h" + +struct EnHorseLinkChild; + +typedef void (*EnHorseLinkChildActionFunc)(struct EnHorseLinkChild*, GlobalContext*); + +typedef struct EnHorseLinkChild { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; + /* 0x0150 */ s32 animationIdx; + /* 0x0154 */ Skin skin; + /* 0x01E4 */ s32 timer; + /* 0x01E8 */ s32 unk_1E8; + /* 0x01EC */ u8 eyeTexIndex; + /* 0x01F0 */ s32 unk_1F0; + /* 0x01F4 */ ColliderCylinder bodyCollider; + /* 0x0240 */ ColliderJntSph headCollider; + /* 0x0260 */ ColliderJntSphElement headElements[1]; + /* 0x02A0 */ s32 unk_2A0; +} EnHorseLinkChild; // size = 0x02A4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Horse_Normal/z_en_horse_normal.c b/soh/src/overlays/actors/ovl_En_Horse_Normal/z_en_horse_normal.c new file mode 100644 index 000000000..6af4cb8a2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Normal/z_en_horse_normal.c @@ -0,0 +1,716 @@ +/* + * File: z_en_horse_normal.c + * Overlay: ovl_En_Horse_Normal + * Description: Non-rideable horses (Lon Lon Ranch and Stable) + */ + +#include "z_en_horse_normal.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_horse_normal/object_horse_normal.h" + +#define FLAGS 0 + +typedef struct { + Vec3s pos; + u8 unk_06; // this may be a s16 if the always-0 following byte is actually not padding +} EnHorseNormalUnkStruct1; + +typedef struct { + s32 len; + EnHorseNormalUnkStruct1* items; +} EnHorseNormalUnkStruct2; + +typedef enum { + /* 0x00 */ HORSE_CYCLE_ANIMATIONS, + /* 0x01 */ HORSE_WANDER, + /* 0x02 */ HORSE_WAIT, + /* 0x03 */ HORSE_WAIT_CLONE, + /* 0x04 */ HORSE_FOLLOW_PATH +} EnHorseNormalAction; + +void EnHorseNormal_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHorseNormal_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHorseNormal_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHorseNormal_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A6B91C(EnHorseNormal* this, GlobalContext* globalCtx); +void func_80A6BC48(EnHorseNormal* this); +void func_80A6BCEC(EnHorseNormal* this); +void func_80A6C4CC(EnHorseNormal* this); +void func_80A6C6B0(EnHorseNormal* this); + +const ActorInit En_Horse_Normal_InitVars = { + ACTOR_EN_HORSE_NORMAL, + ACTORCAT_BG, + FLAGS, + OBJECT_HORSE_NORMAL, + sizeof(EnHorseNormal), + (ActorFunc)EnHorseNormal_Init, + (ActorFunc)EnHorseNormal_Destroy, + (ActorFunc)EnHorseNormal_Update, + (ActorFunc)EnHorseNormal_Draw, + NULL, +}; + +static AnimationHeader* sAnimations[] = { + &gHorseNormalIdleAnim, &gHorseNormalWhinnyAnim, &gHorseNormalRefuseAnim, + &gHorseNormalRearingAnim, &gHorseNormalWalkingAnim, &gHorseNormalTrottingAnim, + &gHorseNormalGallopingAnim, &gHorseNormalJumpingAnim, &gHorseNormalJumpingHighAnim, +}; + +static ColliderCylinderInit sCylinderInit1 = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 40, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInit2 = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 60, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 11, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInit), + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 10, 35, 100, MASS_HEAVY }; + +// Unused +static EnHorseNormalUnkStruct1 D_80A6D428[] = { + { { 1058, 1, 384 }, 7 }, { { 1653, 39, -381 }, 6 }, { { 1606, 1, -1048 }, 6 }, { { 1053, 1, -1620 }, 6 }, + { { -1012, 1, -1633 }, 7 }, { { -1655, 1, -918 }, 6 }, { { -1586, 1, -134 }, 6 }, { { -961, 1, 403 }, 7 }, +}; + +// Unused +static EnHorseNormalUnkStruct2 D_80A6D468 = { ARRAY_COUNT(D_80A6D428), D_80A6D428 }; + +// Unused +static EnHorseNormalUnkStruct1 D_80A6D470[] = { + { { 88, 0, 2078 }, 10 }, { { 2482, 376, 4631 }, 7 }, { { 2228, -28, 6605 }, 12 }, + { { 654, -100, 8864 }, 7 }, { { -297, -500, 10667 }, 12 }, { { -5303, -420, 10640 }, 10 }, + { { -6686, -500, 7760 }, 10 }, { { -5260, 100, 5411 }, 7 }, { { -3573, -269, 3893 }, 10 }, +}; + +// Unused +static EnHorseNormalUnkStruct2 D_80A6D4B8 = { ARRAY_COUNT(D_80A6D470), D_80A6D470 }; + +void func_80A6B250(EnHorseNormal* this) { + static s32 D_80A6D4C0[] = { 0, 16 }; + + if (D_80A6D4C0[this->unk_200] < this->skin.skelAnime.curFrame && + ((this->unk_200 != 0) || !(D_80A6D4C0[1] < this->skin.skelAnime.curFrame))) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_WALK, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_200++; + if (this->unk_200 >= ARRAY_COUNT(D_80A6D4C0)) { + this->unk_200 = 0; + } + } +} + +f32 func_80A6B30C(EnHorseNormal* this) { + static f32 D_80A6D4C8[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.5f, 1.5f, 1.5f, 1.5f, 1.0f }; + f32 result; + + if (this->animationIdx == 4) { + result = D_80A6D4C8[this->animationIdx] * this->actor.speedXZ * (1 / 2.0f); + } else if (this->animationIdx == 5) { + result = D_80A6D4C8[this->animationIdx] * this->actor.speedXZ * (1 / 3.0f); + } else if (this->animationIdx == 6) { + result = D_80A6D4C8[this->animationIdx] * this->actor.speedXZ * (1 / 5.0f); + } else { + result = D_80A6D4C8[this->animationIdx]; + } + + return result; +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 300, ICHAIN_STOP), +}; + +void EnHorseNormal_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHorseNormal* this = (EnHorseNormal*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.01f); + this->actor.gravity = -3.5f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawHorse, 20.0f); + this->actor.speedXZ = 0.0f; + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + this->action = HORSE_CYCLE_ANIMATIONS; + this->animationIdx = 0; + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, &this->actor, &sCylinderInit1); + Collider_InitJntSph(globalCtx, &this->headCollider); + Collider_SetJntSph(globalCtx, &this->headCollider, &this->actor, &sJntSphInit, this->headElements); + Collider_InitCylinder(globalCtx, &this->cloneCollider); + Collider_SetCylinder(globalCtx, &this->cloneCollider, &this->actor, &sCylinderInit2); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + if (globalCtx->sceneNum == SCENE_SPOT20) { + if (this->actor.world.rot.z == 0 || !IS_DAY) { + Actor_Kill(&this->actor); + return; + } + if (!LINK_IS_ADULT) { + if (Flags_GetEventChkInf(0x14)) { + if (this->actor.world.rot.z != 3) { + Actor_Kill(&this->actor); + return; + } + } else if (this->actor.world.rot.z != 1) { + Actor_Kill(&this->actor); + return; + } + } else if (Flags_GetEventChkInf(0x18) || (DREG(1) != 0)) { + if (this->actor.world.rot.z != 7) { + Actor_Kill(&this->actor); + return; + } + } else if (this->actor.world.rot.z != 5) { + Actor_Kill(&this->actor); + return; + } + this->actor.home.rot.z = this->actor.world.rot.z = this->actor.shape.rot.z = 0; + Skin_Init(globalCtx, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim); + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); + if ((this->actor.world.pos.x == -730.0f && this->actor.world.pos.y == 0.0f && + this->actor.world.pos.z == -1100.0f) || + (this->actor.world.pos.x == 880.0f && this->actor.world.pos.y == 0.0f && + this->actor.world.pos.z == -1170.0f)) { + func_80A6C6B0(this); + return; + } + } else if (globalCtx->sceneNum == SCENE_MALON_STABLE) { + if (IS_DAY) { + Actor_Kill(&this->actor); + return; + } else { + Skin_Init(globalCtx, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim); + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); + func_80A6C6B0(this); + return; + } + } else if (globalCtx->sceneNum == SCENE_SPOT12) { + if (this->actor.world.pos.x == 3707.0f && this->actor.world.pos.y == 1413.0f && + this->actor.world.pos.z == -665.0f) { + Skin_Init(globalCtx, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim); + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); + func_80A6C4CC(this); + return; + } + Skin_Init(globalCtx, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim); + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); + } else { + Skin_Init(globalCtx, &this->skin, &gHorseNormalSkel, &gHorseNormalIdleAnim); + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); + } + if ((this->actor.params & 0xF0) == 0x10 && (this->actor.params & 0xF) != 0xF) { + func_80A6B91C(this, globalCtx); + } else { + func_80A6BC48(this); + } +} + +void EnHorseNormal_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHorseNormal* this = (EnHorseNormal*)thisx; + + Skin_Free(globalCtx, &this->skin); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyCylinder(globalCtx, &this->cloneCollider); + Collider_DestroyJntSph(globalCtx, &this->headCollider); +} + +void func_80A6B91C(EnHorseNormal* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_4; + this->action = HORSE_FOLLOW_PATH; + this->animationIdx = 6; + this->waypoint = 0; + this->actor.speedXZ = 7.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); +} + +void EnHorseNormal_FollowPath(EnHorseNormal* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->actor.params & 0xF]; + Vec3s* pointPos = SEGMENTED_TO_VIRTUAL(path->points); + f32 dx; + f32 dz; + s32 pad; + + pointPos += this->waypoint; + dx = pointPos->x - this->actor.world.pos.x; + dz = pointPos->z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.world.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 0xA, 0x7D0, 1); + this->actor.shape.rot.y = this->actor.world.rot.y; + if (SQ(dx) + SQ(dz) < 600.0f) { + this->waypoint++; + if (this->waypoint >= path->count) { + this->waypoint = 0; + } + } + this->skin.skelAnime.playSpeed = func_80A6B30C(this); + if (SkelAnime_Update(&this->skin.skelAnime)) { + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); + func_80A6BCEC(this); + } +} + +void EnHorseNormal_NextAnimation(EnHorseNormal* this) { + this->action = HORSE_CYCLE_ANIMATIONS; + this->animationIdx++; + + if (this->animationIdx >= ARRAY_COUNT(sAnimations)) { + this->animationIdx = 0; + } + + Animation_PlayOnce(&this->skin.skelAnime, sAnimations[this->animationIdx]); +} + +void EnHorseNormal_CycleAnimations(EnHorseNormal* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + + if (SkelAnime_Update(&this->skin.skelAnime)) { + EnHorseNormal_NextAnimation(this); + } +} + +void func_80A6BC48(EnHorseNormal* this) { + this->action = HORSE_WANDER; + this->animationIdx = 0; + this->unk_21C = 0; + this->unk_21E = 0; + this->actor.speedXZ = 0.0f; + this->unk_218 = 0.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); +} + +void func_80A6BCEC(EnHorseNormal* this) { + if (this->animationIdx == 5) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (this->animationIdx == 6) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void func_80A6BD7C(EnHorseNormal* this) { + f32 frame = this->skin.skelAnime.curFrame; + + if (this->animationIdx == 0 && frame > 28.0f && !(this->unk_1E4 & 1)) { + this->unk_1E4 |= 1; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_SANDDUST, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (this->animationIdx == 3 && frame > 25.0f && !(this->unk_1E4 & 2)) { + this->unk_1E4 |= 2; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void EnHorseNormal_Wander(EnHorseNormal* this, GlobalContext* globalCtx) { + static s32 D_80A6D4F4[] = { 0, 1, 4, 5, 6, 2, 3 }; + static s32 D_80A6D510[] = { 0, 0, 2, 2, 1, 1, 1, 3, 3 }; + s32 phi_t0 = this->animationIdx; + s32 pad; + + switch (D_80A6D510[this->animationIdx]) { + case 0: + func_80A6BD7C(this); + this->actor.speedXZ = 0.0f; + this->unk_218 = 0.0f; + break; + case 1: + if (Rand_ZeroOne() < 0.1f) { + this->unk_218 = 2.0f * Rand_ZeroOne() - 1.0f; + } + this->actor.speedXZ += this->unk_218; + if (this->actor.speedXZ <= 0.0f) { + this->actor.speedXZ = 0.0f; + this->unk_218 = 0.0f; + phi_t0 = 0; + } else if (this->actor.speedXZ < 3.0f) { + func_80A6B250(this); + phi_t0 = 4; + } else if (this->actor.speedXZ < 6.0f) { + phi_t0 = 5; + } else if (this->actor.speedXZ < 8.0f) { + phi_t0 = 6; + } else { + this->actor.speedXZ = 8.0f; + phi_t0 = 6; + } + if (Rand_ZeroOne() < 0.1f || (this->unk_21E == 0 && ((this->actor.bgCheckFlags & 8) || + (this->bodyCollider.base.ocFlags1 & OC1_HIT) || + (this->headCollider.base.ocFlags1 & OC1_HIT)))) { + this->unk_21E += (Rand_ZeroOne() * 30.0f) - 15.0f; + if (this->unk_21E > 50) { + this->unk_21E = 50; + } else if (this->unk_21E < -50) { + this->unk_21E = -50; + } + } + this->unk_21C += this->unk_21E; + if (this->unk_21C < -300) { + this->unk_21C = -300; + } else if (this->unk_21C > 300) { + this->unk_21C = 300; + } else if (Rand_ZeroOne() < 0.25f && fabsf(this->unk_21C) < 100.0f) { + this->unk_21C = 0; + this->unk_21E = 0; + } + this->actor.world.rot.y += this->unk_21C; + this->actor.shape.rot.y = this->actor.world.rot.y; + break; + case 2: + case 3: + break; + } + + if (phi_t0 != this->animationIdx || SkelAnime_Update(&this->skin.skelAnime)) { + if (phi_t0 != this->animationIdx) { + this->animationIdx = phi_t0; + this->unk_1E4 &= ~1; + this->unk_1E4 &= ~2; + if (phi_t0 == 1) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (phi_t0 == 3) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + func_80A6BCEC(this); + } + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -3.0f); + } else { + switch (D_80A6D510[this->animationIdx]) { + case 0: + if (Rand_ZeroOne() < 0.25f) { + this->unk_218 = 1.0f; + phi_t0 = 4; + } else { + phi_t0 = D_80A6D4F4[(s32)(Rand_ZeroOne() * 2)]; + this->actor.speedXZ = 0.0f; + this->unk_218 = 0.0f; + } + break; + case 1: + case 2: + case 3: + break; + } + + this->unk_1E4 &= ~1; + this->unk_1E4 &= ~2; + if (phi_t0 == 1) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (phi_t0 == 3) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + func_80A6BCEC(this); + } + if (phi_t0 != this->animationIdx) { + this->animationIdx = phi_t0; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, -3.0f); + } else { + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); + } + } + } +} + +void func_80A6C4CC(EnHorseNormal* this) { + this->action = HORSE_WAIT; + this->animationIdx = 0; + this->unk_21C = 0; + this->unk_21E = 0; + this->actor.speedXZ = 0.0f; + this->unk_218 = 0.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); +} + +void EnHorseNormal_Wait(EnHorseNormal* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skin.skelAnime)) { + f32 rand = Rand_ZeroOne(); + + if (rand < 0.4f) { + this->animationIdx = 0; + } else if (rand < 0.8f) { + this->animationIdx = 1; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + this->animationIdx = 3; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); + } +} + +void func_80A6C6B0(EnHorseNormal* this) { + this->action = HORSE_WAIT_CLONE; + this->animationIdx = 0; + this->unk_21C = 0; + this->unk_21E = 0; + this->actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + this->actor.speedXZ = 0.0f; + this->unk_218 = 0.0f; + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); +} + +void EnHorseNormal_WaitClone(EnHorseNormal* this, GlobalContext* globalCtx) { + func_80A6BD7C(this); + + if (SkelAnime_Update(&this->skin.skelAnime)) { + f32 rand = Rand_ZeroOne(); + + if (rand < 0.4f) { + this->animationIdx = 0; + } else if (rand < 0.8f) { + this->animationIdx = 1; + this->unk_1E4 |= 0x20; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + this->animationIdx = 3; + this->unk_1E4 |= 0x20; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_204, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + Animation_Change(&this->skin.skelAnime, sAnimations[this->animationIdx], func_80A6B30C(this), 0.0f, + Animation_GetLastFrame(sAnimations[this->animationIdx]), ANIMMODE_ONCE, 0.0f); + + this->unk_1E4 &= ~1; + this->unk_1E4 &= ~2; + this->unk_1E4 &= ~8; + this->unk_1E4 &= ~0x10; + } +} + +void func_80A6C8E0(EnHorseNormal* this, GlobalContext* globalCtx) { + s32 pad; + CollisionPoly* sp38; + s32 pad2; + Vec3f sp28; + s32 sp24; + + sp28.x = (Math_SinS(this->actor.shape.rot.y) * 30.0f) + this->actor.world.pos.x; + sp28.y = this->actor.world.pos.y + 60.0f; + sp28.z = (Math_CosS(this->actor.shape.rot.y) * 30.0f) + this->actor.world.pos.z; + this->unk_220 = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &sp38, &sp24, &sp28); + this->actor.shape.rot.x = Math_FAtan2F(this->actor.world.pos.y - this->unk_220, 30.0f) * (0x8000 / M_PI); +} + +static EnHorseNormalActionFunc sActionFuncs[] = { + EnHorseNormal_CycleAnimations, EnHorseNormal_Wander, EnHorseNormal_Wait, + EnHorseNormal_WaitClone, EnHorseNormal_FollowPath, +}; + +void EnHorseNormal_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHorseNormal* this = (EnHorseNormal*)thisx; + s32 pad; + + sActionFuncs[this->action](this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 35.0f, 100.0f, 0x1D); + if (globalCtx->sceneNum == SCENE_SPOT20 && this->actor.world.pos.z < -2400.0f) { + this->actor.world.pos.z = -2400.0f; + } + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + this->unk_204 = this->actor.projectedPos; + this->unk_204.y += 120.0f; + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + if (this->actor.speedXZ == 0.0f) { + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + } else { + this->actor.colChkInfo.mass = MASS_HEAVY; + } +} + +void EnHorseNormal_PostDraw(Actor* thisx, GlobalContext* globalCtx, Skin* skin) { + Vec3f sp4C; + Vec3f sp40; + EnHorseNormal* this = (EnHorseNormal*)thisx; + s32 i; + + for (i = 0; i < this->headCollider.count; i++) { + sp4C.x = this->headCollider.elements[i].dim.modelSphere.center.x; + sp4C.y = this->headCollider.elements[i].dim.modelSphere.center.y; + sp4C.z = this->headCollider.elements[i].dim.modelSphere.center.z; + Skin_GetLimbPos(skin, this->headCollider.elements[i].dim.limb, &sp4C, &sp40); + this->headCollider.elements[i].dim.worldSphere.center.x = sp40.x; + this->headCollider.elements[i].dim.worldSphere.center.y = sp40.y; + this->headCollider.elements[i].dim.worldSphere.center.z = sp40.z; + this->headCollider.elements[i].dim.worldSphere.radius = + this->headCollider.elements[i].dim.modelSphere.radius * this->headCollider.elements[i].dim.scale; + } + + //! @bug see relevant comment in `EnHorse_SkinCallback1` + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->headCollider.base); +} + +void func_80A6CC88(GlobalContext* globalCtx, EnHorseNormal* this, Vec3f* arg2) { + f32 curFrame = this->skin.skelAnime.curFrame; + f32 wDest; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, arg2, &this->unk_1E8, &wDest); + this->unk_1F4 = this->unk_1E8; + this->unk_1F4.y += 120.0f; + + if (this->animationIdx == 0 && curFrame > 28.0f && !(this->unk_1E4 & 8)) { + this->unk_1E4 |= 8; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_SANDDUST, &this->unk_1E8, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (this->animationIdx == 3 && curFrame > 25.0f && !(this->unk_1E4 & 0x10)) { + this->unk_1E4 |= 0x10; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_LAND2, &this->unk_1E8, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (this->animationIdx == 3 && this->unk_1E4 & 0x20) { + this->unk_1E4 &= ~0x20; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->unk_1F4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (this->animationIdx == 1 && this->unk_1E4 & 0x20) { + this->unk_1E4 &= ~0x20; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->unk_1F4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void EnHorseNormal_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHorseNormal* this = (EnHorseNormal*)thisx; + Mtx* mtx2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_horse_normal.c", 2224); + + if (globalCtx->sceneNum != SCENE_SPOT20 || globalCtx->sceneNum != SCENE_MALON_STABLE) { + func_80A6C8E0(this, globalCtx); + } + func_80093D18(globalCtx->state.gfxCtx); + func_800A6330(&this->actor, globalCtx, &this->skin, EnHorseNormal_PostDraw, true); + + if (this->action == HORSE_WAIT_CLONE) { + MtxF skinMtx; + Mtx* mtx1; + Vec3f clonePos = { 0.0f, 0.0f, 0.0f }; + s16 cloneRotY; + f32 distFromGround = this->actor.world.pos.y - this->actor.floorHeight; + f32 temp_f0_4; + + if (globalCtx->sceneNum == SCENE_MALON_STABLE) { + if (this->actor.world.pos.x == 355.0f && this->actor.world.pos.y == 0.0f && + this->actor.world.pos.z == -245.0f) { + clonePos.x = 235.0f; + clonePos.y = 0.0f; + clonePos.z = 100.0f; + cloneRotY = 0x7FFF; + } else if (this->actor.world.pos.x == 238.0f && this->actor.world.pos.y == 0.0f && + this->actor.world.pos.z == -245.0f) { + clonePos.x = 478.0f; + clonePos.y = 0.0f; + clonePos.z = 100.0f; + cloneRotY = 0x7FFF; + } + } else if (globalCtx->sceneNum == SCENE_SPOT20) { + if (this->actor.world.pos.x == -730.0f && this->actor.world.pos.y == 0.0f && + this->actor.world.pos.z == -1100.0f) { + clonePos.x = 780.0f; + clonePos.y = 0.0f; + clonePos.z = -80.0f; + cloneRotY = 0; + } else if (this->actor.world.pos.x == 880.0f && this->actor.world.pos.y == 0.0f && + this->actor.world.pos.z == -1170.0f) { + clonePos.x = -1000.0f; + clonePos.y = 0.0f; + clonePos.z = -70.0f; + cloneRotY = 0; + } + } + func_80A6CC88(globalCtx, this, &clonePos); + SkinMatrix_SetTranslateRotateYXZScale(&skinMtx, this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, + this->actor.shape.rot.x, cloneRotY, this->actor.shape.rot.z, clonePos.x, + (this->actor.shape.yOffset * this->actor.scale.y) + clonePos.y, + clonePos.z); + mtx1 = SkinMatrix_MtxFToNewMtx(globalCtx->state.gfxCtx, &skinMtx); + if (mtx1 == NULL) { + return; + } + gSPMatrix(POLY_OPA_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(POLY_OPA_DISP++, mtx1, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_800A63CC(&this->actor, globalCtx, &this->skin, NULL, NULL, true, 0, + SKIN_DRAW_FLAG_CUSTOM_TRANSFORMS | SKIN_DRAW_FLAG_CUSTOM_MATRIX); + this->cloneCollider.dim.pos.x = clonePos.x; + this->cloneCollider.dim.pos.y = clonePos.y; + this->cloneCollider.dim.pos.z = clonePos.z; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cloneCollider.base); + func_80094044(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 255); + Matrix_Translate(clonePos.x, clonePos.y, clonePos.z, MTXMODE_NEW); + temp_f0_4 = (1.0f - (distFromGround * 0.01f)) * this->actor.shape.shadowScale; + Matrix_Scale(this->actor.scale.x * temp_f0_4, 1.0f, this->actor.scale.z * temp_f0_4, MTXMODE_APPLY); + Matrix_RotateY(cloneRotY * (2.0f * M_PI / 0x10000), MTXMODE_APPLY); + mtx2 = Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_horse_normal.c", 2329); + if (mtx2 != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx2, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gHorseShadowDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_horse_normal.c", 2339); +} diff --git a/soh/src/overlays/actors/ovl_En_Horse_Normal/z_en_horse_normal.h b/soh/src/overlays/actors/ovl_En_Horse_Normal/z_en_horse_normal.h new file mode 100644 index 000000000..9c5ffb74c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Normal/z_en_horse_normal.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_HORSE_NORMAL_H +#define Z_EN_HORSE_NORMAL_H + +#include "ultra64.h" +#include "global.h" + +struct EnHorseNormal; + +typedef void (*EnHorseNormalActionFunc)(struct EnHorseNormal*, GlobalContext*); + +typedef struct EnHorseNormal { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; + /* 0x0150 */ s32 animationIdx; + /* 0x0154 */ Skin skin; + /* 0x01E4 */ u16 unk_1E4; + /* 0x01E8 */ Vec3f unk_1E8; + /* 0x01F4 */ Vec3f unk_1F4; + /* 0x0200 */ s32 unk_200; + /* 0x0204 */ Vec3f unk_204; + /* 0x0210 */ char unk_210[0x08]; + /* 0x0218 */ f32 unk_218; + /* 0x021C */ s16 unk_21C; + /* 0x021E */ s16 unk_21E; + /* 0x0220 */ f32 unk_220; + /* 0x0224 */ char unk_224[0x04]; + /* 0x0228 */ ColliderCylinder bodyCollider; + /* 0x0274 */ ColliderJntSph headCollider; + /* 0x0294 */ ColliderJntSphElement headElements[1]; + /* 0x02D4 */ ColliderCylinder cloneCollider; + /* 0x0320 */ char unk_320[0x04]; + /* 0x0324 */ s32 waypoint; +} EnHorseNormal; // size = 0x0328 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Horse_Zelda/z_en_horse_zelda.c b/soh/src/overlays/actors/ovl_En_Horse_Zelda/z_en_horse_zelda.c new file mode 100644 index 000000000..0aee97741 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Zelda/z_en_horse_zelda.c @@ -0,0 +1,275 @@ +/* + * File: z_en_horse_zelda.c + * Overlay: ovl_En_Horse_Zelda + * Description: Zelda's Horse + */ + +#include "z_en_horse_zelda.h" +#include "objects/object_horse_zelda/object_horse_zelda.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnHorseZelda_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHorseZelda_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHorseZelda_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHorseZelda_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A6DCCC(EnHorseZelda* this, GlobalContext* globalCtx); +void func_80A6DDFC(EnHorseZelda* this, GlobalContext* globalCtx); +void func_80A6DC7C(EnHorseZelda* this); + +const ActorInit En_Horse_Zelda_InitVars = { + ACTOR_EN_HORSE_ZELDA, + ACTORCAT_BG, + FLAGS, + OBJECT_HORSE_ZELDA, + sizeof(EnHorseZelda), + (ActorFunc)EnHorseZelda_Init, + (ActorFunc)EnHorseZelda_Destroy, + (ActorFunc)EnHorseZelda_Update, + (ActorFunc)EnHorseZelda_Draw, + NULL, +}; + +static AnimationHeader* sAnimationHeaders[] = { &gHorseZeldaGallopingAnim }; + +static f32 splaySpeeds[] = { 2.0f / 3.0f }; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 40, 100, 0, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 13, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1 | OC2_UNK1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 10, 35, 100, MASS_HEAVY }; + +typedef struct { + /* 0x0 */ Vec3s unk_0; + /* 0x6 */ u8 unk_6; +} unknownStruct; // size = 0x8 + +static unknownStruct D_80A6E240[] = { + { -1682, -500, 12578, 0x07 }, { -3288, -500, 13013, 0x07 }, { -5142, -417, 11630, 0x07 }, + { -5794, -473, 9573, 0x07 }, { -6765, -500, 8364, 0x07 }, { -6619, -393, 6919, 0x07 }, + { -5193, 124, 5433, 0x07 }, { -2970, 2, 4537, 0x07 }, { -2949, -35, 4527, 0x07 }, + { -1907, -47, 2978, 0x07 }, { 2488, 294, 3628, 0x07 }, { 3089, 378, 4713, 0x07 }, + { 1614, -261, 7596, 0x07 }, { 754, -187, 9295, 0x07 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_STOP), +}; + +static EnHorseZeldaActionFunc sActionFuncs[] = { + func_80A6DCCC, + func_80A6DDFC, +}; + +void func_80A6D8D0(unknownStruct* data, s32 index, Vec3f* vec) { + vec->x = data[index].unk_0.x; + vec->y = data[index].unk_0.y; + vec->z = data[index].unk_0.z; +} + +void func_80A6D918(EnHorseZelda* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f sp28; + s16 yawDiff; + + func_80A6D8D0(D_80A6E240, this->unk_1EC, &sp28); + if (Math3D_Vec3f_DistXYZ(&sp28, &this->actor.world.pos) <= 400.0f) { + this->unk_1EC++; + if (this->unk_1EC >= 14) { + this->unk_1EC = 0; + func_80A6D8D0(D_80A6E240, 0, &sp28); + } + } + yawDiff = Math_Vec3f_Yaw(&this->actor.world.pos, &sp28) - this->actor.world.rot.y; + if (yawDiff >= 0x12D) { + this->actor.world.rot.y += 0x12C; + } else if (yawDiff < -0x12C) { + this->actor.world.rot.y -= 0x12C; + } else { + this->actor.world.rot.y += yawDiff; + } + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (Actor_WorldDistXZToActor(&this->actor, &GET_PLAYER(globalCtx)->actor) <= 300.0f) { + if (this->actor.speedXZ < 12.0f) { + this->actor.speedXZ += 1.0f; + } else { + this->actor.speedXZ -= 1.0f; + } + } else if (this->actor.speedXZ < D_80A6E240[this->unk_1EC].unk_6) { + this->actor.speedXZ += 0.5f; + } else { + this->actor.speedXZ -= 0.5f; + } +} + +void EnHorseZelda_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHorseZelda* this = (EnHorseZelda*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 0.0115f); + this->actor.gravity = -3.5f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawHorse, 20.0f); + this->actor.speedXZ = 0.0f; + this->actor.focus.pos = this->actor.world.pos; + this->action = 0; + this->actor.focus.pos.y += 70.0f; + Skin_Init(globalCtx, &this->skin, &gHorseZeldaSkel, &gHorseZeldaGallopingAnim); + this->animationIndex = 0; + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[0]); + Collider_InitCylinder(globalCtx, &this->colliderCylinder); + Collider_SetCylinderType1(globalCtx, &this->colliderCylinder, &this->actor, &sCylinderInit); + Collider_InitJntSph(globalCtx, &this->colliderSphere); + Collider_SetJntSph(globalCtx, &this->colliderSphere, &this->actor, &sJntSphInit, &this->colliderSphereItem); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + this->animationIndex = 0; + func_80A6DC7C(this); +} + +void EnHorseZelda_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHorseZelda* this = (EnHorseZelda*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->colliderCylinder); + Collider_DestroyJntSph(globalCtx, &this->colliderSphere); + Skin_Free(globalCtx, &this->skin); +} + +void func_80A6DC7C(EnHorseZelda* this) { + this->action = 0; + this->animationIndex++; + if (this->animationIndex > 0) { + this->animationIndex = 0; + } + Animation_PlayOnce(&this->skin.skelAnime, sAnimationHeaders[this->animationIndex]); +} + +void func_80A6DCCC(EnHorseZelda* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + if (SkelAnime_Update(&this->skin.skelAnime)) { + func_80A6DC7C(this); + } +} + +void func_80A6DD14(EnHorseZelda* this) { + f32 sp34; + + this->action = 1; + this->animationIndex = 0; + sp34 = this->actor.speedXZ / 6.0f; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_RUN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Animation_Change(&this->skin.skelAnime, sAnimationHeaders[this->animationIndex], + splaySpeeds[this->animationIndex] * sp34 * 1.5f, 0.0f, + Animation_GetLastFrame(sAnimationHeaders[this->animationIndex]), ANIMMODE_ONCE, 0.0f); +} + +void func_80A6DDFC(EnHorseZelda* this, GlobalContext* globalCtx) { + func_80A6D918(this, globalCtx); + if (SkelAnime_Update(&this->skin.skelAnime)) { + func_80A6DD14(this); + } +} + +void func_80A6DE38(EnHorseZelda* this, GlobalContext* globalCtx) { + s32 pad; + CollisionPoly* poly; + s32 pad2; + Vec3f pos; + s32 bgId; + + pos.x = (Math_SinS(this->actor.shape.rot.y) * 30.0f) + this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 60.0f; + pos.z = (Math_CosS(this->actor.shape.rot.y) * 30.0f) + this->actor.world.pos.z; + this->unk_1F4 = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &poly, &bgId, &pos); + this->actor.shape.rot.x = Math_FAtan2F(this->actor.world.pos.y - this->unk_1F4, 30.0f) * (0x8000 / M_PI); +} + +void EnHorseZelda_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHorseZelda* this = (EnHorseZelda*)thisx; + s32 pad; + + sActionFuncs[this->action](this, globalCtx); + this->actor.speedXZ = 0.0f; + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 55.0f, 100.0f, 0x1D); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + Collider_UpdateCylinder(&this->actor, &this->colliderCylinder); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinder.base); +} + +void EnHorseZelda_PostDraw(Actor* thisx, GlobalContext* globalCtx, Skin* skin) { + Vec3f sp4C; + Vec3f sp40; + EnHorseZelda* this = (EnHorseZelda*)thisx; + s32 i; + + for (i = 0; i < this->colliderSphere.count; i++) { + sp4C.x = this->colliderSphere.elements[i].dim.modelSphere.center.x; + sp4C.y = this->colliderSphere.elements[i].dim.modelSphere.center.y; + sp4C.z = this->colliderSphere.elements[i].dim.modelSphere.center.z; + + Skin_GetLimbPos(skin, this->colliderSphere.elements[i].dim.limb, &sp4C, &sp40); + + this->colliderSphere.elements[i].dim.worldSphere.center.x = sp40.x; + this->colliderSphere.elements[i].dim.worldSphere.center.y = sp40.y; + this->colliderSphere.elements[i].dim.worldSphere.center.z = sp40.z; + + this->colliderSphere.elements[i].dim.worldSphere.radius = + this->colliderSphere.elements[i].dim.modelSphere.radius * this->colliderSphere.elements[i].dim.scale; + } + + //! @bug see relevant comment in `EnHorse_SkinCallback1` + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderSphere.base); +} + +void EnHorseZelda_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHorseZelda* this = (EnHorseZelda*)thisx; + + func_80A6DE38(this, globalCtx); + func_80093D18(globalCtx->state.gfxCtx); + func_800A6330(&this->actor, globalCtx, &this->skin, EnHorseZelda_PostDraw, true); +} diff --git a/soh/src/overlays/actors/ovl_En_Horse_Zelda/z_en_horse_zelda.h b/soh/src/overlays/actors/ovl_En_Horse_Zelda/z_en_horse_zelda.h new file mode 100644 index 000000000..531a1a6c3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Horse_Zelda/z_en_horse_zelda.h @@ -0,0 +1,26 @@ +#ifndef Z_EN_HORSE_ZELDA_H +#define Z_EN_HORSE_ZELDA_H + +#include "ultra64.h" +#include "global.h" + +struct EnHorseZelda; + +typedef void (*EnHorseZeldaActionFunc)(struct EnHorseZelda*, GlobalContext*); + +typedef struct EnHorseZelda { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 action; + /* 0x0150 */ s32 animationIndex; + /* 0x0154 */ Skin skin; + /* 0x01E4 */ char unk_1E4[0x8]; + /* 0x01EC */ s32 unk_1EC; + /* 0x01F0 */ char unk_1F0[0x4]; + /* 0x01F4 */ f32 unk_1F4; + /* 0x01F8 */ char unk_1F8[0x4]; + /* 0x01FC */ ColliderCylinder colliderCylinder; + /* 0x0248 */ ColliderJntSph colliderSphere; + /* 0x0268 */ ColliderJntSphElement colliderSphereItem; +} EnHorseZelda; // size = 0x02A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c new file mode 100644 index 000000000..efdbbdef8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.c @@ -0,0 +1,300 @@ +/* + * File: z_en_hs.c + * Overlay: ovl_En_Hs + * Description: Carpenter's Son + */ + +#include "z_en_hs.h" +#include "vt.h" +#include "objects/object_hs/object_hs.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnHs_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHs_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHs_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHs_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A6E9AC(EnHs* this, GlobalContext* globalCtx); +void func_80A6E6B0(EnHs* this, GlobalContext* globalCtx); + +const ActorInit En_Hs_InitVars = { + ACTOR_EN_HS, + ACTORCAT_NPC, + FLAGS, + OBJECT_HS, + sizeof(EnHs), + (ActorFunc)EnHs_Init, + (ActorFunc)EnHs_Destroy, + (ActorFunc)EnHs_Update, + (ActorFunc)EnHs_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 40, 40, 0, { 0, 0, 0 } }, +}; + +void func_80A6E3A0(EnHs* this, EnHsActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnHs_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHs* this = (EnHs*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_hs_Skel_006260, &object_hs_Anim_0005C0, this->jointTable, + this->morphTable, 16); + Animation_PlayLoop(&this->skelAnime, &object_hs_Anim_0005C0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + + if (!LINK_IS_ADULT) { + this->actor.params = 0; + } else { + this->actor.params = 1; + } + + if (this->actor.params == 1) { + // "chicken shop (adult era)" + osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコの店(大人の時) \n" VT_RST); + func_80A6E3A0(this, func_80A6E9AC); + if (gSaveContext.itemGetInf[3] & 1) { + // "chicken shop closed" + osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコ屋閉店 \n" VT_RST); + Actor_Kill(&this->actor); + } + } else { + // "chicken shop (child era)" + osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコの店(子人の時) \n" VT_RST); + func_80A6E3A0(this, func_80A6E9AC); + } + + this->unk_2A8 = 0; + this->actor.targetMode = 6; +} + +void EnHs_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHs* this = (EnHs*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80A6E53C(EnHs* this, GlobalContext* globalCtx, u16 textId, EnHsActionFunc actionFunc) { + s16 yawDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + func_80A6E3A0(this, actionFunc); + return 1; + } + + this->actor.textId = textId; + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((ABS(yawDiff) <= 0x2150) && (this->actor.xzDistToPlayer < 100.0f)) { + this->unk_2A8 |= 1; + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + + return 0; +} + +void func_80A6E5EC(EnHs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80A6E3A0(this, func_80A6E6B0); + } + + this->unk_2A8 |= 1; +} + +void func_80A6E630(EnHs* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + func_80088AA0(180); + func_80A6E3A0(this, func_80A6E6B0); + gSaveContext.eventInf[1] &= ~1; + } + + this->unk_2A8 |= 1; +} + +void func_80A6E6B0(EnHs* this, GlobalContext* globalCtx) { + func_80A6E53C(this, globalCtx, 0x10B6, func_80A6E5EC); +} + +void func_80A6E6D8(EnHs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80A6E3A0(this, func_80A6E9AC); + } +} + +void func_80A6E70C(EnHs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80A6E3A0(this, func_80A6E9AC); + } +} + +void func_80A6E740(EnHs* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + func_80A6E3A0(this, func_80A6E630); + } else { + func_8002F434(&this->actor, globalCtx, GI_ODD_MUSHROOM, 10000.0f, 50.0f); + } + + this->unk_2A8 |= 1; +} + +void func_80A6E7BC(EnHs* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + func_80A6E3A0(this, func_80A6E740); + func_8002F434(&this->actor, globalCtx, GI_ODD_MUSHROOM, 10000.0f, 50.0f); + break; + case 1: + Message_ContinueTextbox(globalCtx, 0x10B4); + func_80A6E3A0(this, func_80A6E70C); + break; + } + + Animation_Change(&this->skelAnime, &object_hs_Anim_0005C0, 1.0f, 0.0f, + Animation_GetLastFrame(&object_hs_Anim_0005C0), ANIMMODE_LOOP, 8.0f); + } + + this->unk_2A8 |= 1; +} + +void func_80A6E8CC(EnHs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x10B3); + func_80A6E3A0(this, func_80A6E7BC); + Animation_Change(&this->skelAnime, &object_hs_Anim_000528, 1.0f, 0.0f, + Animation_GetLastFrame(&object_hs_Anim_000528), ANIMMODE_LOOP, 8.0f); + } + + if (this->unk_2AA > 0) { + this->unk_2AA--; + if (this->unk_2AA == 0) { + func_8002F7DC(&player->actor, NA_SE_EV_CHICKEN_CRY_M); + } + } + + this->unk_2A8 |= 1; +} + +void func_80A6E9AC(EnHs* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (func_8002F368(globalCtx) == 7) { + player->actor.textId = 0x10B2; + func_80A6E3A0(this, func_80A6E8CC); + Animation_Change(&this->skelAnime, &object_hs_Anim_000304, 1.0f, 0.0f, + Animation_GetLastFrame(&object_hs_Anim_000304), ANIMMODE_LOOP, 8.0f); + this->unk_2AA = 40; + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } else { + player->actor.textId = 0x10B1; + func_80A6E3A0(this, func_80A6E6D8); + } + } else { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + this->actor.textId = 0x10B1; + if ((ABS(yawDiff) <= 0x2150) && (this->actor.xzDistToPlayer < 100.0f)) { + func_8002F298(&this->actor, globalCtx, 100.0f, 7); + } + } +} + +void EnHs_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHs* this = (EnHs*)thisx; + s32 pad; + + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if (SkelAnime_Update(&this->skelAnime)) { + this->skelAnime.curFrame = 0.0f; + } + + this->actionFunc(this, globalCtx); + + if (this->unk_2A8 & 1) { + func_80038290(globalCtx, &this->actor, &this->unk_29C, &this->unk_2A2, this->actor.focus.pos); + this->unk_2A8 &= ~1; + } else { + Math_SmoothStepToS(&this->unk_29C.x, 12800, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_29C.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.y, 0, 6, 6200, 100); + } +} + +s32 EnHs_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnHs* this = (EnHs*)thisx; + + switch (limbIndex) { + case 9: + rot->x += this->unk_29C.y; + rot->z += this->unk_29C.x; + break; + case 10: + *dList = NULL; + return false; + case 11: + *dList = NULL; + return false; + case 12: + if (this->actor.params == 1) { + *dList = NULL; + return false; + } + break; + case 13: + if (this->actor.params == 1) { + *dList = NULL; + return false; + } + break; + } + return false; +} + +void EnHs_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_80A6EDFC = { 300.0f, 1000.0f, 0.0f }; + EnHs* this = (EnHs*)thisx; + + if (limbIndex == 9) { + Matrix_MultVec3f(&D_80A6EDFC, &this->actor.focus.pos); + } +} + +void EnHs_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHs* this = (EnHs*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnHs_OverrideLimbDraw, EnHs_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.h b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.h new file mode 100644 index 000000000..4a6576ddc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hs/z_en_hs.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_HS_H +#define Z_EN_HS_H + +#include "ultra64.h" +#include "global.h" + +struct EnHs; + +typedef void (*EnHsActionFunc)(struct EnHs*, GlobalContext*); + +typedef struct EnHs { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[16]; + /* 0x023C */ Vec3s morphTable[16]; + /* 0x029C */ Vec3s unk_29C; + /* 0x02A2 */ Vec3s unk_2A2; + /* 0x02A8 */ u16 unk_2A8; + /* 0x02AA */ s16 unk_2AA; + /* 0x02AC */ EnHsActionFunc actionFunc; +} EnHs; // size = 0x02B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c b/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c new file mode 100644 index 000000000..d70164af5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.c @@ -0,0 +1,169 @@ +/* + * File: z_en_hs2.c + * Overlay: ovl_En_Hs2 + * Description: Carpenter's Son (Child Link version) + */ + +#include "z_en_hs2.h" +#include "vt.h" +#include "objects/object_hs/object_hs.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnHs2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHs2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHs2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHs2_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_80A6F1A4(EnHs2* this, GlobalContext* globalCtx); + +const ActorInit En_Hs2_InitVars = { + ACTOR_EN_HS2, + ACTORCAT_NPC, + FLAGS, + OBJECT_HS, + sizeof(EnHs2), + (ActorFunc)EnHs2_Init, + (ActorFunc)EnHs2_Destroy, + (ActorFunc)EnHs2_Update, + (ActorFunc)EnHs2_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 40, 40, 0, { 0, 0, 0 } }, +}; + +void EnHs2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHs2* this = (EnHs2*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_hs_Skel_006260, &object_hs_Anim_0005C0, this->jointTable, + this->morphTable, 16); + Animation_PlayLoop(&this->skelAnime, &object_hs_Anim_0005C0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + osSyncPrintf(VT_FGCOL(CYAN) " ヒヨコの店(子人の時) \n" VT_RST); + this->actionFunc = func_80A6F1A4; + this->unk_2A8 = 0; + this->actor.targetMode = 6; +} + +void EnHs2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHs2* this = (EnHs2*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80A6F0B4(EnHs2* this, GlobalContext* globalCtx, u16 textId, EnHs2ActionFunc actionFunc) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = actionFunc; + return 1; + } + + this->actor.textId = textId; + if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x2151 && + this->actor.xzDistToPlayer < 100.0f) { + this->unk_2A8 |= 0x1; + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + return 0; +} + +void func_80A6F164(EnHs2* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = func_80A6F1A4; + } + this->unk_2A8 |= 0x1; +} + +void func_80A6F1A4(EnHs2* this, GlobalContext* globalCtx) { + u16 textId; + + textId = Text_GetFaceReaction(globalCtx, 9); + if (textId == 0) { + textId = 0x5069; + } + + func_80A6F0B4(this, globalCtx, textId, func_80A6F164); +} + +void EnHs2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHs2* this = (EnHs2*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if (SkelAnime_Update(&this->skelAnime) != 0) { + this->skelAnime.curFrame = 0.0f; + } + this->actionFunc(this, globalCtx); + if (this->unk_2A8 & 0x1) { + func_80038290(globalCtx, &this->actor, &this->unk_29C, &this->unk_2A2, this->actor.focus.pos); + this->unk_2A8 &= ~1; + } else { + Math_SmoothStepToS(&this->unk_29C.x, 12800, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_29C.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2A2.y, 0, 6, 6200, 100); + } +} + +s32 EnHs2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnHs2* this = (EnHs2*)thisx; + + switch (limbIndex) { + case 12: + case 13: + *dList = NULL; + return false; + case 9: + rot->x += this->unk_29C.y; + rot->z += this->unk_29C.x; + break; + case 10: + *dList = NULL; + return false; + case 11: + *dList = NULL; + return false; + } + return false; +} + +void EnHs2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_80A6F4CC = { 300.0f, 1000.0f, 0.0f }; + EnHs2* this = (EnHs2*)thisx; + + if (limbIndex == 9) { + Matrix_MultVec3f(&D_80A6F4CC, &this->actor.focus.pos); + } +} + +void EnHs2_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHs2* this = (EnHs2*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnHs2_OverrideLimbDraw, EnHs2_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.h b/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.h new file mode 100644 index 000000000..e006b91f7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hs2/z_en_hs2.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_HS2_H +#define Z_EN_HS2_H + +#include "ultra64.h" +#include "global.h" + +struct EnHs2; + +typedef void (*EnHs2ActionFunc)(struct EnHs2*, GlobalContext*); + +typedef struct EnHs2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[16]; + /* 0x023C */ Vec3s morphTable[16]; + /* 0x029C */ Vec3s unk_29C; + /* 0x02A2 */ Vec3s unk_2A2; + /* 0x02A8 */ u16 unk_2A8; + /* 0x02AC */ EnHs2ActionFunc actionFunc; +} EnHs2; // size = 0x02B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c new file mode 100644 index 000000000..bc47e7b6a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c @@ -0,0 +1,1242 @@ +/* + * File: z_en_hy.c + * Overlay: ovl_En_Hy + * Description: Hylian NPCs + */ + +#include "z_en_hy.h" +#include "objects/object_aob/object_aob.h" +#include "objects/object_ahg/object_ahg.h" +#include "objects/object_bob/object_bob.h" +#include "objects/object_boj/object_boj.h" +#include "objects/object_bba/object_bba.h" +#include "objects/object_bji/object_bji.h" +#include "objects/object_cne/object_cne.h" +#include "objects/object_cob/object_cob.h" +#include "objects/object_os_anime/object_os_anime.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnHy_Init(Actor* thisx, GlobalContext* globalCtx); +void EnHy_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnHy_Update(Actor* thisx, GlobalContext* globalCtx); +void EnHy_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnHy_InitImpl(EnHy* this, GlobalContext* globalCtx); +void func_80A7134C(EnHy* this, GlobalContext* globalCtx); +void func_80A71530(EnHy* this, GlobalContext* globalCtx); +void func_80A711B4(EnHy* this, GlobalContext* globalCtx); +void func_80A712C0(EnHy* this, GlobalContext* globalCtx); +void func_80A710F8(EnHy* this, GlobalContext* globalCtx); +void func_80A7127C(EnHy* this, GlobalContext* globalCtx); +void EnHy_DoNothing(EnHy* this, GlobalContext* globalCtx); +void func_80A714C4(EnHy* this, GlobalContext* globalCtx); + +const ActorInit En_Hy_InitVars = { + ACTOR_EN_HY, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnHy), + (ActorFunc)EnHy_Init, + (ActorFunc)EnHy_Destroy, + (ActorFunc)EnHy_Update, + (ActorFunc)EnHy_Draw, + NULL, +}; + +static ColliderCylinderInit sColCylInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +// NULL-terminated arrays of eye textures +static void* sEyeTexturesAOB[] = { gDogLadyEyeOpenTex, gDogLadyEyeHalfTex, gDogLadyEyeClosedTex, NULL }; +static void* sEyeTexturesAHG7[] = { object_ahg_Tex_0005FC, object_ahg_Tex_0006FC, object_ahg_Tex_0007FC, NULL }; +static void* sEyeTexturesBBA[] = { object_bba_Tex_0004C8, NULL }; +static void* sEyeTexturesBJI13[] = { object_bji_Tex_0005FC, object_bji_Tex_0009FC, object_bji_Tex_000DFC, NULL }; +static void* sEyeTexturesBOJ2[] = { object_boj_Tex_0005FC, object_boj_Tex_0006FC, object_boj_Tex_0007FC, NULL }; +static void* sEyeTexturesBOB[] = { object_bob_Tex_0007C8, object_bob_Tex_000FC8, object_bob_Tex_0017C8, NULL }; + +typedef struct { + /* 0x00 */ s16 objectId; + /* 0x04 */ Gfx* headDList; + /* 0x08 */ void** eyeTextures; +} EnHyHeadInfo; // size = 0xC + +typedef enum { + /* 0 */ ENHY_HEAD_AOB, + /* 1 */ ENHY_HEAD_BOB, + /* 2 */ ENHY_HEAD_BOJ_2, + /* 3 */ ENHY_HEAD_BOJ_3, + /* 4 */ ENHY_HEAD_BOJ_4, + /* 5 */ ENHY_HEAD_BOJ_5, + /* 6 */ ENHY_HEAD_BOJ_6, + /* 7 */ ENHY_HEAD_AHG_7, + /* 8 */ ENHY_HEAD_AHG_8, + /* 9 */ ENHY_HEAD_AHG_9, + /* 10 */ ENHY_HEAD_BBA, + /* 11 */ ENHY_HEAD_CNE_11, + /* 12 */ ENHY_HEAD_CNE_12, + /* 13 */ ENHY_HEAD_BJI_13, + /* 14 */ ENHY_HEAD_BJI_14, + /* 15 */ ENHY_HEAD_COB +} EnHyHeadIndex; + +static EnHyHeadInfo sHeadInfo[] = { + /* ENHY_HEAD_AOB */ { OBJECT_AOB, gDogLadyHeadDL, sEyeTexturesAOB }, + /* ENHY_HEAD_BOB */ { OBJECT_BOB, object_bob_DL_003B78, sEyeTexturesBOB }, + /* ENHY_HEAD_BOJ_2 */ { OBJECT_BOJ, object_boj_DL_0026F0, sEyeTexturesBOJ2 }, + /* ENHY_HEAD_BOJ_3 */ { OBJECT_BOJ, object_boj_DL_0052E0, NULL }, + /* ENHY_HEAD_BOJ_4 */ { OBJECT_BOJ, object_boj_DL_005528, NULL }, + /* ENHY_HEAD_BOJ_5 */ { OBJECT_BOJ, object_boj_DL_005738, NULL }, + /* ENHY_HEAD_BOJ_6 */ { OBJECT_BOJ, object_boj_DL_0059B0, NULL }, + /* ENHY_HEAD_AHG_7 */ { OBJECT_AHG, object_ahg_DL_0030F0, sEyeTexturesAHG7 }, + /* ENHY_HEAD_AHG_8 */ { OBJECT_AHG, object_ahg_DL_005508, NULL }, + /* ENHY_HEAD_AHG_9 */ { OBJECT_AHG, object_ahg_DL_005728, NULL }, + /* ENHY_HEAD_BBA */ { OBJECT_BBA, object_bba_DL_002948, sEyeTexturesBBA }, + /* ENHY_HEAD_CNE_11 */ { OBJECT_CNE, object_cne_DL_001300, NULL }, + /* ENHY_HEAD_CNE_12 */ { OBJECT_CNE, object_cne_DL_002860, NULL }, + /* ENHY_HEAD_BJI_13 */ { OBJECT_BJI, object_bji_DL_002560, sEyeTexturesBJI13 }, + /* ENHY_HEAD_BJI_14 */ { OBJECT_BJI, object_bji_DL_003F68, NULL }, + /* ENHY_HEAD_COB */ { OBJECT_COB, object_cob_DL_001300, NULL }, +}; + +typedef struct { + /* 0x00 */ s16 objectId; + /* 0x04 */ FlexSkeletonHeader* skeleton; +} EnHySkeletonInfo; // size = 0x8 + +typedef enum { + /* 0 */ ENHY_SKEL_AOB, + /* 1 */ ENHY_SKEL_BOB, + /* 2 */ ENHY_SKEL_BOJ, + /* 3 */ ENHY_SKEL_AHG, + /* 4 */ ENHY_SKEL_BBA, + /* 5 */ ENHY_SKEL_CNE, + /* 6 */ ENHY_SKEL_BJI, + /* 7 */ ENHY_SKEL_COB +} EnHySkeletonIndex; + +static EnHySkeletonInfo sSkeletonInfo[] = { + /* ENHY_SKEL_AOB */ { OBJECT_AOB, &gDogLadySkel }, + /* ENHY_SKEL_BOB */ { OBJECT_BOB, &object_bob_Skel_0000F0 }, + /* ENHY_SKEL_BOJ */ { OBJECT_BOJ, &object_boj_Skel_0000F0 }, + /* ENHY_SKEL_AHG */ { OBJECT_AHG, &object_ahg_Skel_0000F0 }, + /* ENHY_SKEL_BBA */ { OBJECT_BBA, &object_bba_Skel_0000F0 }, + /* ENHY_SKEL_CNE */ { OBJECT_CNE, &object_cne_Skel_0000F0 }, + /* ENHY_SKEL_BJI */ { OBJECT_BJI, &object_bji_Skel_0000F0 }, + /* ENHY_SKEL_COB */ { OBJECT_COB, &object_cob_Skel_0021F8 }, +}; + +typedef enum { + /* 0 */ ENHY_ANIM_0, + /* 1 */ ENHY_ANIM_1, + /* 2 */ ENHY_ANIM_2, + /* 3 */ ENHY_ANIM_3, + /* 4 */ ENHY_ANIM_4, + /* 5 */ ENHY_ANIM_5, + /* 6 */ ENHY_ANIM_6, + /* 7 */ ENHY_ANIM_7, + /* 8 */ ENHY_ANIM_8, + /* 9 */ ENHY_ANIM_9, + /* 10 */ ENHY_ANIM_10, + /* 11 */ ENHY_ANIM_11, + /* 12 */ ENHY_ANIM_12, + /* 13 */ ENHY_ANIM_13, + /* 14 */ ENHY_ANIM_14, + /* 15 */ ENHY_ANIM_15, + /* 16 */ ENHY_ANIM_16, + /* 17 */ ENHY_ANIM_17, + /* 18 */ ENHY_ANIM_18, + /* 19 */ ENHY_ANIM_19, + /* 20 */ ENHY_ANIM_20, + /* 21 */ ENHY_ANIM_21, + /* 22 */ ENHY_ANIM_22, + /* 23 */ ENHY_ANIM_23, + /* 24 */ ENHY_ANIM_24, + /* 25 */ ENHY_ANIM_25, + /* 26 */ ENHY_ANIM_26 +} EnHyAnimationIndex; + +static AnimationInfo sAnimationInfo[] = { + /* ENHY_ANIM_0 */ { &gObjOsAnim_092C, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_1 */ { &gObjOsAnim_0228, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_2 */ { &gObjOsAnim_4CF4, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_3 */ { &gObjOsAnim_16EC, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_4 */ { &gObjOsAnim_265C, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_5 */ { &gObjOsAnim_42AC, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_6 */ { &gObjOsAnim_28DC, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_7 */ { &gObjOsAnim_2160, 1.0f, 0.0f, -1.0f, 0x00, -10.0f }, + /* ENHY_ANIM_8 */ { &gObjOsAnim_265C, 1.0f, 0.0f, -1.0f, 0x00, -10.0f }, + /* ENHY_ANIM_9 */ { &gObjOsAnim_4E90, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_10 */ { &gObjOsAnim_1E7C, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_11 */ { &gObjOsAnim_0170, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_12 */ { &gObjOsAnim_00B4, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_13 */ { &gObjOsAnim_3D84, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_14 */ { &gObjOsAnim_41F8, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_15 */ { &gObjOsAnim_300C, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_16 */ { &gObjOsAnim_31B0, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_17 */ { &gObjOsAnim_31B0, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + /* ENHY_ANIM_18 */ { &gObjOsAnim_2D0C, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_19 */ { &gObjOsAnim_2DC0, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_20 */ { &gObjOsAnim_4408, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_21 */ { &gObjOsAnim_1F18, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_22 */ { &gObjOsAnim_4F28, 1.0f, 0.0f, -1.0f, 0x00, 0.0f }, + /* ENHY_ANIM_23 */ { &gObjOsAnim_33B4, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + /* ENHY_ANIM_24 */ { &gObjOsAnim_12E8, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + /* ENHY_ANIM_25 */ { &gObjOsAnim_0FE4, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, + /* ENHY_ANIM_26 */ { &gObjOsAnim_0BFC, 1.0f, 0.0f, -1.0f, 0x00, -8.0f }, +}; + +typedef struct { + /* 0x00 */ u8 headInfoIndex; // EnHyHeadIndex + /* 0x01 */ u8 skelInfoIndex2; // EnHySkeletonIndex, see EnHy#objBankIndexSkel2 + /* 0x02 */ Color_RGBA8 envColorSeg8; + /* 0x06 */ u8 skelInfoIndex1; // EnHySkeletonIndex, see EnHy#objBankIndexSkel1 + /* 0x07 */ Color_RGBA8 envColorSeg9; + /* 0x0B */ u8 animInfoIndex; // EnHyAnimationIndex +} EnHyModelInfo; // size = 0xC + +static EnHyModelInfo sModelInfo[] = { + /* ENHY_TYPE_AOB */ + { ENHY_HEAD_AOB, ENHY_SKEL_AOB, { 255, 255, 255, 255 }, ENHY_SKEL_AOB, { 255, 255, 255, 255 }, ENHY_ANIM_0 }, + /* ENHY_TYPE_COB */ + { ENHY_HEAD_COB, ENHY_SKEL_COB, { 255, 255, 255, 255 }, ENHY_SKEL_COB, { 255, 255, 255, 255 }, ENHY_ANIM_22 }, + /* ENHY_TYPE_AHG_2 */ + { ENHY_HEAD_AHG_7, ENHY_SKEL_AHG, { 255, 255, 255, 255 }, ENHY_SKEL_AHG, { 255, 255, 255, 255 }, ENHY_ANIM_1 }, + /* ENHY_TYPE_BOJ_3 */ + { ENHY_HEAD_BOJ_3, ENHY_SKEL_BOJ, { 255, 255, 255, 0 }, ENHY_SKEL_BOJ, { 55, 55, 255, 0 }, ENHY_ANIM_15 }, + /* ENHY_TYPE_AHG_4 */ + { ENHY_HEAD_AHG_8, ENHY_SKEL_AHG, { 0, 0, 0, 0 }, ENHY_SKEL_AHG, { 255, 0, 0, 0 }, ENHY_ANIM_11 }, + /* ENHY_TYPE_BOJ_5 */ + { ENHY_HEAD_BOJ_4, ENHY_SKEL_BOJ, { 50, 80, 0, 0 }, ENHY_SKEL_BOJ, { 50, 80, 0, 0 }, ENHY_ANIM_16 }, + /* ENHY_TYPE_BBA */ + { ENHY_HEAD_BBA, ENHY_SKEL_BBA, { 255, 255, 255, 255 }, ENHY_SKEL_BBA, { 255, 255, 255, 255 }, ENHY_ANIM_10 }, + /* ENHY_TYPE_BJI_7 */ + { ENHY_HEAD_BJI_13, ENHY_SKEL_BJI, { 0, 50, 160, 0 }, ENHY_SKEL_BJI, { 255, 255, 255, 0 }, ENHY_ANIM_4 }, + /* ENHY_TYPE_CNE_8 */ + { ENHY_HEAD_CNE_11, ENHY_SKEL_CNE, { 160, 180, 255, 0 }, ENHY_SKEL_CNE, { 160, 180, 255, 0 }, ENHY_ANIM_9 }, + /* ENHY_TYPE_BOJ_9 */ + { ENHY_HEAD_BOJ_2, ENHY_SKEL_BOJ, { 220, 0, 80, 0 }, ENHY_SKEL_BOJ, { 255, 255, 255, 0 }, ENHY_ANIM_13 }, + /* ENHY_TYPE_BOJ_10 */ + { ENHY_HEAD_BOJ_2, ENHY_SKEL_BOJ, { 0, 130, 220, 0 }, ENHY_SKEL_BOJ, { 255, 255, 255, 0 }, ENHY_ANIM_14 }, + /* ENHY_TYPE_CNE_11 */ + { ENHY_HEAD_CNE_12, ENHY_SKEL_CNE, { 70, 160, 230, 0 }, ENHY_SKEL_CNE, { 255, 255, 100, 0 }, ENHY_ANIM_20 }, + /* ENHY_TYPE_BOJ_12 */ + { ENHY_HEAD_BOJ_5, ENHY_SKEL_BOJ, { 150, 60, 90, 0 }, ENHY_SKEL_BOJ, { 255, 240, 150, 0 }, ENHY_ANIM_18 }, + /* ENHY_TYPE_AHG_13 */ + { ENHY_HEAD_AHG_9, ENHY_SKEL_AHG, { 200, 180, 255, 0 }, ENHY_SKEL_AHG, { 200, 180, 255, 0 }, ENHY_ANIM_12 }, + /* ENHY_TYPE_BOJ_14 */ + { ENHY_HEAD_BOJ_6, ENHY_SKEL_BOJ, { 140, 255, 110, 0 }, ENHY_SKEL_BOJ, { 255, 255, 255, 0 }, ENHY_ANIM_19 }, + /* ENHY_TYPE_BJI_15 */ + { ENHY_HEAD_BJI_14, ENHY_SKEL_BJI, { 130, 70, 20, 0 }, ENHY_SKEL_BJI, { 130, 180, 255, 0 }, ENHY_ANIM_21 }, + /* ENHY_TYPE_BOJ_16 */ + { ENHY_HEAD_BOJ_2, ENHY_SKEL_BOJ, { 255, 255, 255, 255 }, ENHY_SKEL_BOJ, { 255, 255, 255, 255 }, ENHY_ANIM_5 }, + /* ENHY_TYPE_AHG_17 */ + { ENHY_HEAD_AHG_8, ENHY_SKEL_AHG, { 90, 100, 20, 255 }, ENHY_SKEL_AHG, { 100, 140, 50, 255 }, ENHY_ANIM_11 }, + /* ENHY_TYPE_BOB_18 */ + { ENHY_HEAD_BOB, ENHY_SKEL_BOB, { 255, 255, 255, 255 }, ENHY_SKEL_BOB, { 255, 255, 255, 255 }, ENHY_ANIM_6 }, + /* ENHY_TYPE_BJI_19 */ + { ENHY_HEAD_BJI_14, ENHY_SKEL_BJI, { 160, 0, 100, 0 }, ENHY_SKEL_BJI, { 70, 130, 210, 0 }, ENHY_ANIM_21 }, + /* ENHY_TYPE_AHG_20 */ + { ENHY_HEAD_AHG_9, ENHY_SKEL_AHG, { 160, 230, 0, 0 }, ENHY_SKEL_AHG, { 0, 150, 110, 0 }, ENHY_ANIM_12 }, +}; + +typedef struct { + /* 0x00 */ Vec3s offset; + /* 0x06 */ s16 radius; + /* 0x08 */ s16 height; +} EnHyColliderInfo; // size 0xA + +static EnHyColliderInfo sColliderInfo[] = { + /* ENHY_TYPE_AOB */ { { 0, 0, 4 }, 24, 70 }, + /* ENHY_TYPE_COB */ { { 0, 0, 8 }, 28, 62 }, + /* ENHY_TYPE_AHG_2 */ { { 0, 0, 4 }, 20, 60 }, + /* ENHY_TYPE_BOJ_3 */ { { 0, 0, 2 }, 20, 60 }, + /* ENHY_TYPE_AHG_4 */ { { 0, 0, -2 }, 20, 60 }, + /* ENHY_TYPE_BOJ_5 */ { { 0, 0, 8 }, 24, 40 }, + /* ENHY_TYPE_BBA */ { { 0, 0, 10 }, 26, 40 }, + /* ENHY_TYPE_BJI_7 */ { { 0, 0, 12 }, 26, 58 }, + /* ENHY_TYPE_CNE_8 */ { { 0, 0, 2 }, 18, 68 }, + /* ENHY_TYPE_BOJ_9 */ { { 0, 0, 4 }, 20, 60 }, + /* ENHY_TYPE_BOJ_10 */ { { 0, 0, 4 }, 20, 60 }, + /* ENHY_TYPE_CNE_11 */ { { 0, 0, 6 }, 20, 64 }, + /* ENHY_TYPE_BOJ_12 */ { { 0, 0, 0 }, 18, 60 }, + /* ENHY_TYPE_AHG_13 */ { { 0, 0, 0 }, 16, 60 }, + /* ENHY_TYPE_BOJ_14 */ { { 0, 0, 0 }, 16, 64 }, + /* ENHY_TYPE_BJI_15 */ { { 0, 0, 8 }, 20, 58 }, + /* ENHY_TYPE_BOJ_16 */ { { 4, 0, 0 }, 18, 62 }, + /* ENHY_TYPE_AHG_17 */ { { 4, 0, 0 }, 18, 62 }, + /* ENHY_TYPE_BOB_18 */ { { 0, 0, 8 }, 28, 62 }, + /* ENHY_TYPE_BJI_19 */ { { 0, 0, 0 }, 16, 60 }, + /* ENHY_TYPE_AHG_20 */ { { 0, 0, 8 }, 20, 58 }, +}; + +typedef struct { + /* 0x00 */ u8 unkPresetIndex; + /* 0x04 */ f32 unkValueChild; + /* 0x08 */ f32 unkValueAdult; +} EnHyInit1Info; // size = 0xC + +static EnHyInit1Info sInit1Info[] = { + /* ENHY_TYPE_AOB */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_COB */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_AHG_2 */ { 0x07, 40.0f, 20.0f }, + /* ENHY_TYPE_BOJ_3 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_AHG_4 */ { 0x07, 40.0f, 20.0f }, + /* ENHY_TYPE_BOJ_5 */ { 0x08, 0.0f, -20.0f }, + /* ENHY_TYPE_BBA */ { 0x09, 20.0f, 0.0f }, + /* ENHY_TYPE_BJI_7 */ { 0x09, 20.0f, 0.0f }, + /* ENHY_TYPE_CNE_8 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BOJ_9 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BOJ_10 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_CNE_11 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BOJ_12 */ { 0x00, 0.0f, 0.0f }, + /* ENHY_TYPE_AHG_13 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BOJ_14 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BJI_15 */ { 0x0A, 20.0f, 0.0f }, + /* ENHY_TYPE_BOJ_16 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_AHG_17 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BOB_18 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_BJI_19 */ { 0x06, 20.0f, 10.0f }, + /* ENHY_TYPE_AHG_20 */ { 0x0A, 20.0f, 0.0f }, +}; + +typedef struct { + /* 0x00 */ f32 shadowScale; + /* 0x04 */ Vec3f modelOffset; + /* 0x10 */ f32 scale; + /* 0x14 */ s8 targetMode; + /* 0x18 */ f32 unkRange; +} EnHyInit2Info; // size = 0x1C + +static EnHyInit2Info sInit2Info[] = { + /* ENHY_TYPE_AOB */ { 36.0f, { 0.0f, 0.0f, 600.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_COB */ { 40.0f, { -100.0f, 0.0f, 400.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_AHG_2 */ { 22.0f, { 0.0f, 0.0f, -200.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_3 */ { 20.0f, { -100.0f, 0.0f, 0.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_AHG_4 */ { 22.0f, { 0.0f, 0.0f, 0.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_5 */ { 21.0f, { 0.0f, 0.0f, 0.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BBA */ { 25.0f, { -100.0f, 0.0f, 600.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BJI_7 */ { 28.0f, { -100.0f, 0.0f, 800.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_CNE_8 */ { 17.0f, { 0.0f, 0.0f, 700.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_9 */ { 18.0f, { 0.0f, 0.0f, 100.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_10 */ { 18.0f, { 0.0f, 0.0f, -200.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_CNE_11 */ { 17.0f, { 0.0f, 0.0f, 700.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_12 */ { 21.0f, { 0.0f, 0.0f, -300.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_AHG_13 */ { 20.0f, { 0.0f, 0.0f, -200.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_14 */ { 18.0f, { -200.0f, 0.0f, -200.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BJI_15 */ { 27.0f, { -100.0f, 0.0f, 800.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOJ_16 */ { 19.0f, { 400.0f, 0.0f, 0.0f }, 0.01f, 0x04, 30.0f }, + /* ENHY_TYPE_AHG_17 */ { 19.0f, { 400.0f, 0.0f, 0.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BOB_18 */ { 40.0f, { -100.0f, 0.0f, 400.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_BJI_19 */ { 17.0f, { 0.0f, 0.0f, 700.0f }, 0.01f, 0x06, 30.0f }, + /* ENHY_TYPE_AHG_20 */ { 20.0f, { 0.0f, 0.0f, -200.0f }, 0.01f, 0x06, 30.0f }, +}; + +s32 EnHy_FindSkelAndHeadObjects(EnHy* this, GlobalContext* globalCtx) { + u8 headInfoIndex = sModelInfo[this->actor.params & 0x7F].headInfoIndex; + u8 skelInfoIndex2 = sModelInfo[this->actor.params & 0x7F].skelInfoIndex2; + u8 skelInfoIndex1 = sModelInfo[this->actor.params & 0x7F].skelInfoIndex1; + + this->objBankIndexSkel1 = Object_GetIndex(&globalCtx->objectCtx, sSkeletonInfo[skelInfoIndex1].objectId); + if (this->objBankIndexSkel1 < 0) { + return false; + } + + this->objBankIndexSkel2 = Object_GetIndex(&globalCtx->objectCtx, sSkeletonInfo[skelInfoIndex2].objectId); + if (this->objBankIndexSkel2 < 0) { + return false; + } + + this->objBankIndexHead = Object_GetIndex(&globalCtx->objectCtx, sHeadInfo[headInfoIndex].objectId); + if (this->objBankIndexHead < 0) { + return false; + } + + return true; +} + +s32 EnHy_AreSkelAndHeadObjectsLoaded(EnHy* this, GlobalContext* globalCtx) { + if (!Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndexSkel1)) { + return false; + } + + if (!Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndexSkel2)) { + return false; + } + + if (!Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndexHead)) { + return false; + } + + return true; +} + +s32 EnHy_FindOsAnimeObject(EnHy* this, GlobalContext* globalCtx) { + this->objBankIndexOsAnime = Object_GetIndex(&globalCtx->objectCtx, OBJECT_OS_ANIME); + + if (this->objBankIndexOsAnime < 0) { + return false; + } + + return true; +} + +s32 EnHy_IsOsAnimeObjectLoaded(EnHy* this, GlobalContext* globalCtx) { + if (!Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndexOsAnime)) { + return false; + } + + return true; +} + +void func_80A6F7CC(EnHy* this, GlobalContext* globalCtx, s32 getItemId) { + this->unkGetItemId = getItemId; + func_8002F434(&this->actor, globalCtx, getItemId, this->actor.xzDistToPlayer + 1.0f, + fabsf(this->actor.yDistToPlayer) + 1.0f); +} + +u16 func_80A6F810(GlobalContext* globalCtx, Actor* thisx) { + Player* player = GET_PLAYER(globalCtx); + EnHy* this = (EnHy*)thisx; + u16 textId = Text_GetFaceReaction(globalCtx, (this->actor.params & 0x7F) + 37); + + if (textId != 0) { + if ((this->actor.params & 0x7F) == ENHY_TYPE_BOJ_5) { + player->exchangeItemId = EXCH_ITEM_BLUE_FIRE; + } + return textId; + } + + switch (this->actor.params & 0x7F) { + case ENHY_TYPE_AOB: + if (globalCtx->sceneNum == SCENE_KAKARIKO) { + return (this->unk_330 & 0x800) ? 0x508D : ((gSaveContext.infTable[12] & 0x800) ? 0x508C : 0x508B); + } else if (globalCtx->sceneNum == SCENE_MARKET_DAY) { + return (gSaveContext.eventInf[3] & 1) ? 0x709B : 0x709C; + } else if (gSaveContext.dogIsLost) { + s16 followingDog = (gSaveContext.dogParams & 0xF00) >> 8; + + if (followingDog != 0) { + this->unk_215 = false; + return (followingDog == 1) ? 0x709F : 0x709E; + } else { + return 0x709D; + } + } else { + return 0x70A0; + } + case ENHY_TYPE_COB: + if (gSaveContext.eventChkInf[8] & 1) { + return (gSaveContext.infTable[12] & 2) ? 0x7017 : 0x7045; + } else { + return (gSaveContext.infTable[12] & 1) ? 0x7017 : 0x7016; + } + case ENHY_TYPE_AHG_2: + if (globalCtx->sceneNum == SCENE_KAKARIKO) { + return 0x5086; + } else if (globalCtx->sceneNum == SCENE_SPOT01) { + return 0x5085; + } else if (gSaveContext.eventChkInf[8] & 1) { + return (gSaveContext.infTable[12] & 8) ? 0x701A : 0x7047; + } else if (gSaveContext.eventChkInf[1] & 0x10) { + return 0x701A; + } else if (gSaveContext.eventChkInf[1] & 1) { + return 0x701B; + } else if (gSaveContext.infTable[12] & 4) { + return 0x701C; + } else { + return 0x701A; + } + case ENHY_TYPE_BOJ_3: + return (gSaveContext.eventChkInf[8] & 1) ? ((gSaveContext.infTable[12] & 0x10) ? 0x7001 : 0x70EB) : 0x7001; + case ENHY_TYPE_AHG_4: + return (gSaveContext.eventChkInf[8] & 1) ? 0x704B : ((gSaveContext.infTable[12] & 0x20) ? 0x7024 : 0x7023); + case ENHY_TYPE_BOJ_5: + player->exchangeItemId = EXCH_ITEM_BLUE_FIRE; + return 0x700C; + case ENHY_TYPE_BBA: + return (gSaveContext.eventChkInf[8] & 1) ? 0x704A : ((gSaveContext.infTable[12] & 0x40) ? 0x7022 : 0x7021); + case ENHY_TYPE_BJI_7: + if (globalCtx->sceneNum == SCENE_KAKARIKO) { + return 0x5088; + } else if (globalCtx->sceneNum == SCENE_SPOT01) { + return 0x5087; + } else { + return (gSaveContext.eventChkInf[8] & 1) ? 0x704D + : ((gSaveContext.infTable[12] & 0x80) ? 0x7028 : 0x7027); + } + case ENHY_TYPE_CNE_8: + if (gSaveContext.eventChkInf[8] & 1) { + return (gSaveContext.infTable[12] & 0x200) ? 0x701E : 0x7048; + } else { + return (gSaveContext.infTable[12] & 0x100) ? 0x701E : 0x701D; + } + case ENHY_TYPE_BOJ_9: + if (globalCtx->sceneNum == SCENE_KAKARIKO) { + return (gSaveContext.eventChkInf[10] & 0x400) ? 0x5082 : 0x5081; + } else if (globalCtx->sceneNum == SCENE_SPOT01) { + return CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) ? 0x5080 : 0x507F; + } else { + return (gSaveContext.eventChkInf[8] & 1) ? 0x7049 + : ((gSaveContext.infTable[12] & 0x400) ? 0x7020 : 0x701F); + } + case ENHY_TYPE_BOJ_10: + if (globalCtx->sceneNum == SCENE_LABO) { + return (gSaveContext.eventChkInf[10] & 0x400) ? 0x507E : 0x507D; + } else if (globalCtx->sceneNum == SCENE_SPOT01) { + return CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW) ? 0x507C : 0x507B; + } else { + return (gSaveContext.eventChkInf[8] & 1) ? 0x7046 + : ((gSaveContext.infTable[12] & 0x2000) ? 0x7019 : 0x7018); + } + case ENHY_TYPE_CNE_11: + return (gSaveContext.infTable[8] & 0x800) ? ((gSaveContext.infTable[12] & 0x1000) ? 0x7014 : 0x70A4) + : 0x7014; + case ENHY_TYPE_BOJ_12: + if (globalCtx->sceneNum == SCENE_SPOT01) { + return !IS_DAY ? 0x5084 : 0x5083; + } else { + return (gSaveContext.eventChkInf[8] & 1) ? 0x7044 : 0x7015; + } + case ENHY_TYPE_AHG_13: + return 0x7055; + case ENHY_TYPE_BOJ_14: + return 0x7089; + case ENHY_TYPE_BJI_15: + return 0x708A; + case ENHY_TYPE_BOJ_16: + return 0x700E; + case ENHY_TYPE_AHG_17: + if (!LINK_IS_ADULT) { + if (IS_DAY) { + return (gSaveContext.infTable[22] & 1) ? 0x5058 : 0x5057; + } else { + return (gSaveContext.infTable[22] & 2) ? 0x505A : 0x5059; + } + } else if (IS_DAY) { + return (gSaveContext.infTable[22] & 4) ? 0x505C : 0x505B; + } else { + return 0x5058; + } + case ENHY_TYPE_BOB_18: + if (!LINK_IS_ADULT) { + return (gSaveContext.eventChkInf[8] & 1) ? 0x505F : ((gSaveContext.infTable[22] & 8) ? 0x505E : 0x505D); + } else { + return (this->unk_330 & 0x800) ? 0x5062 : ((gSaveContext.infTable[22] & 0x10) ? 0x5061 : 0x5060); + } + case ENHY_TYPE_BJI_19: + return 0x7120; + case ENHY_TYPE_AHG_20: + return 0x7121; + default: + return 0; + } +} + +s16 func_80A70058(GlobalContext* globalCtx, Actor* thisx) { + EnHy* this = (EnHy*)thisx; + s16 beggarItems[] = { ITEM_BLUE_FIRE, ITEM_FISH, ITEM_BUG, ITEM_FAIRY }; + s16 beggarRewards[] = { 150, 100, 50, 25 }; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_CHOICE: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + return 1; + case TEXT_STATE_DONE_FADING: + switch (this->actor.textId) { + case 0x709E: + case 0x709F: + if (!this->unk_215) { + Audio_PlaySoundGeneral(this->actor.textId == 0x709F ? NA_SE_SY_CORRECT_CHIME : NA_SE_SY_ERROR, + &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->unk_215 = true; + } + break; + case 0x70F0: + case 0x70F1: + case 0x70F2: + case 0x70F3: + if (this->skelAnime.animation != &gObjOsAnim_33B4) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_23); + Audio_PlayFanfare(NA_BGM_ITEM_GET | 0x900); + } + break; + } + return 1; + case TEXT_STATE_CLOSING: + switch (this->actor.textId) { + case 0x70F0: + case 0x70F1: + case 0x70F2: + case 0x70F3: + Rupees_ChangeBy(beggarRewards[this->actor.textId - 0x70F0]); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_17); + Player_UpdateBottleHeld(globalCtx, GET_PLAYER(globalCtx), ITEM_BOTTLE, PLAYER_AP_BOTTLE); + break; + case 0x7016: + gSaveContext.infTable[12] |= 1; + break; + case 0x7045: + gSaveContext.infTable[12] |= 2; + break; + case 0x701B: + gSaveContext.infTable[12] |= 4; + break; + case 0x7047: + gSaveContext.infTable[12] |= 8; + break; + case 0x70EB: + gSaveContext.infTable[12] |= 0x10; + break; + case 0x7023: + gSaveContext.infTable[12] |= 0x20; + break; + case 0x7021: + gSaveContext.infTable[12] |= 0x40; + break; + case 0x7027: + gSaveContext.infTable[12] |= 0x80; + break; + case 0x701D: + gSaveContext.infTable[12] |= 0x100; + break; + case 0x7048: + gSaveContext.infTable[12] |= 0x200; + break; + case 0x701F: + gSaveContext.infTable[12] |= 0x400; + break; + case 0x7018: + gSaveContext.infTable[12] |= 0x2000; + break; + case 0x70A4: + gSaveContext.infTable[12] |= 0x1000; + break; + case 0x5057: + gSaveContext.infTable[22] |= 1; + break; + case 0x5059: + gSaveContext.infTable[22] |= 2; + break; + case 0x505B: + gSaveContext.infTable[22] |= 4; + break; + case 0x505D: + gSaveContext.infTable[22] |= 8; + break; + case 0x5060: + gSaveContext.infTable[22] |= 0x10; + break; + case 0x508B: + gSaveContext.infTable[12] |= 0x800; + break; + case 0x709E: + gSaveContext.dogParams = 0; + break; + case 0x709F: + func_80A6F7CC(this, globalCtx, (gSaveContext.infTable[25] & 2) ? GI_RUPEE_BLUE : GI_HEART_PIECE); + this->actionFunc = func_80A714C4; + break; + } + return 0; + case TEXT_STATE_EVENT: + if (!Message_ShouldAdvance(globalCtx)) { + return 1; + } else { + return 2; + } + } + + return 1; +} + +void EnHy_UpdateEyes(EnHy* this) { + if (DECR(this->nextEyeIndexTimer) == 0) { + u8 headInfoIndex = sModelInfo[this->actor.params & 0x7F].headInfoIndex; + + this->curEyeIndex++; + if ((sHeadInfo[headInfoIndex].eyeTextures != NULL) && + (sHeadInfo[headInfoIndex].eyeTextures[this->curEyeIndex] == NULL)) { + this->nextEyeIndexTimer = Rand_S16Offset(30, 30); + this->curEyeIndex = 0; + } + } +} + +void EnHy_InitCollider(EnHy* this) { + u8 type = this->actor.params & 0x7F; + + this->collider.dim.radius = sColliderInfo[type].radius; + this->collider.dim.height = sColliderInfo[type].height; +} + +void EnHy_InitSetProperties(EnHy* this) { + u8 type = this->actor.params & 0x7F; + + this->actor.shape.shadowScale = sInit2Info[type].shadowScale; + Actor_SetScale(&this->actor, sInit2Info[type].scale); + this->actor.targetMode = sInit2Info[type].targetMode; + this->modelOffset = sInit2Info[type].modelOffset; + this->unkRange = sInit2Info[type].unkRange; + this->unkRange += this->collider.dim.radius; +} + +void EnHy_UpdateCollider(EnHy* this, GlobalContext* globalCtx) { + Vec3s pos; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z; + pos.x += sColliderInfo[this->actor.params & 0x7F].offset.x; + pos.y += sColliderInfo[this->actor.params & 0x7F].offset.y; + pos.z += sColliderInfo[this->actor.params & 0x7F].offset.z; + this->collider.dim.pos = pos; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void func_80A70834(EnHy* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((this->actor.params & 0x7F) == ENHY_TYPE_BOJ_5) { + if (!Inventory_HasSpecificBottle(ITEM_BLUE_FIRE) && !Inventory_HasSpecificBottle(ITEM_BUG) && + !Inventory_HasSpecificBottle(ITEM_FISH)) { + switch (func_8002F368(globalCtx)) { + case EXCH_ITEM_POE: + case EXCH_ITEM_BIG_POE: + case EXCH_ITEM_LETTER_RUTO: + this->actor.textId = 0x70EF; + break; + default: + if (Player_GetMask(globalCtx) == PLAYER_MASK_NONE) { + this->actor.textId = 0x70ED; + } + break; + } + } else { + switch (func_8002F368(globalCtx)) { + case EXCH_ITEM_BLUE_FIRE: + this->actor.textId = 0x70F0; + break; + case EXCH_ITEM_FISH: + this->actor.textId = 0x70F1; + break; + case EXCH_ITEM_BUG: + this->actor.textId = 0x70F2; + break; + default: + if (Player_GetMask(globalCtx) == PLAYER_MASK_NONE) { + this->actor.textId = 0x700C; + } + break; + } + } + + player->actor.textId = this->actor.textId; + } +} + +void func_80A70978(EnHy* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3; + + switch (this->actor.params & 0x7F) { + case ENHY_TYPE_BOJ_3: + case ENHY_TYPE_BJI_7: + case ENHY_TYPE_BOJ_9: + case ENHY_TYPE_BOJ_10: + phi_a3 = (this->unk_1E8.unk_00 == 0) ? 1 : 2; + break; + case ENHY_TYPE_BOJ_12: + phi_a3 = 1; + break; + case ENHY_TYPE_AHG_2: + case ENHY_TYPE_AHG_17: + phi_a3 = 4; + break; + case ENHY_TYPE_AOB: + case ENHY_TYPE_BOB_18: + phi_a3 = (this->unk_1E8.unk_00 == 0) ? 2 : 4; + break; + default: + phi_a3 = 2; + break; + } + + this->unk_1E8.unk_18 = player->actor.world.pos; + + if (LINK_IS_ADULT) { + this->unk_1E8.unk_14 = sInit1Info[this->actor.params & 0x7F].unkValueAdult; + } else { + this->unk_1E8.unk_14 = sInit1Info[this->actor.params & 0x7F].unkValueChild; + } + + func_80034A14(&this->actor, &this->unk_1E8, sInit1Info[this->actor.params & 0x7F].unkPresetIndex, phi_a3); + + if (func_800343CC(globalCtx, &this->actor, &this->unk_1E8.unk_00, this->unkRange, func_80A6F810, func_80A70058)) { + func_80A70834(this, globalCtx); + } +} + +s32 EnHy_ShouldSpawn(EnHy* this, GlobalContext* globalCtx) { + switch (globalCtx->sceneNum) { + case SCENE_SPOT01: + if (!((this->actor.params & 0x7F) == ENHY_TYPE_BOJ_9 || (this->actor.params & 0x7F) == ENHY_TYPE_BOJ_10 || + (this->actor.params & 0x7F) == ENHY_TYPE_BOJ_12 || (this->actor.params & 0x7F) == ENHY_TYPE_AHG_2 || + (this->actor.params & 0x7F) == ENHY_TYPE_BJI_7)) { + return true; + } else if (!LINK_IS_ADULT) { + return true; + } else if ((this->actor.params & 0x7F) != ENHY_TYPE_BOJ_12 && IS_NIGHT) { + return false; + } else { + return true; + } + case SCENE_LABO: + if ((this->actor.params & 0x7F) != ENHY_TYPE_BOJ_10) { + return true; + } else if (LINK_IS_CHILD) { + return false; + } else if ((this->actor.params & 0x7F) == ENHY_TYPE_BOJ_10 && IS_DAY) { + return false; + } else { + return true; + } + case SCENE_IMPA: + if ((this->actor.params & 0x7F) != ENHY_TYPE_AOB) { + return true; + } else if (IS_DAY) { + return false; + } else { + return true; + } + case SCENE_KAKARIKO: + if ((this->actor.params & 0x7F) == ENHY_TYPE_AOB) { + return !LINK_IS_ADULT ? false : true; + } else if (!((this->actor.params & 0x7F) == ENHY_TYPE_BOJ_9 || + (this->actor.params & 0x7F) == ENHY_TYPE_AHG_2 || + (this->actor.params & 0x7F) == ENHY_TYPE_BJI_7)) { + return true; + } else if (IS_DAY) { + return false; + } else if (LINK_IS_CHILD) { + return false; + } else { + return true; + } + case SCENE_MARKET_ALLEY: + case SCENE_MARKET_ALLEY_N: + if ((this->actor.params & 0x7F) != ENHY_TYPE_BOJ_14) { + return true; + } else if (IS_NIGHT) { + return false; + } else if ((gSaveContext.eventChkInf[8] & 1) && !(gSaveContext.eventChkInf[4] & 0x20)) { + return false; + } else { + return true; + } + default: + switch (this->actor.params & 0x7F) { + case ENHY_TYPE_BJI_19: + case ENHY_TYPE_AHG_20: + if (LINK_IS_ADULT) { + return false; + } + } + return true; + } +} + +void EnHy_Init(Actor* thisx, GlobalContext* globalCtx) { + EnHy* this = (EnHy*)thisx; + + if ((this->actor.params & 0x7F) >= ENHY_TYPE_MAX || !EnHy_FindOsAnimeObject(this, globalCtx) || + !EnHy_FindSkelAndHeadObjects(this, globalCtx)) { + Actor_Kill(&this->actor); + } + + if (!EnHy_ShouldSpawn(this, globalCtx)) { + Actor_Kill(&this->actor); + } + + this->actionFunc = EnHy_InitImpl; +} + +void EnHy_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnHy* this = (EnHy*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnHy_InitImpl(EnHy* this, GlobalContext* globalCtx) { + if (EnHy_IsOsAnimeObjectLoaded(this, globalCtx) && EnHy_AreSkelAndHeadObjectsLoaded(this, globalCtx)) { + this->actor.objBankIndex = this->objBankIndexSkel1; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, + sSkeletonInfo[sModelInfo[this->actor.params & 0x7F].skelInfoIndex1].skeleton, NULL, + this->jointTable, this->morphTable, 16); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndexOsAnime].segment); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sColCylInit); + EnHy_InitCollider(this); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, sModelInfo[this->actor.params & 0x7F].animInfoIndex); + + if ((globalCtx->sceneNum == SCENE_MARKET_ALLEY) || (globalCtx->sceneNum == SCENE_MARKET_DAY)) { + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.uncullZoneScale = 0.0f; + } + + if (globalCtx->sceneNum == SCENE_KAKARIKO) { + this->unk_330 = gSaveContext.eventChkInf[6]; + } + + EnHy_InitSetProperties(this); + this->path = Path_GetByIndex(globalCtx, (this->actor.params & 0x780) >> 7, 15); + + switch (this->actor.params & 0x7F) { + case ENHY_TYPE_BOJ_3: + if (this->path != NULL) { + this->actor.speedXZ = 3.0f; + } + this->actionFunc = func_80A711B4; + break; + case ENHY_TYPE_BJI_7: + this->pathReverse = false; + this->actionFunc = func_80A712C0; + break; + case ENHY_TYPE_AOB: + if (globalCtx->sceneNum == SCENE_MARKET_DAY) { + this->actionFunc = func_80A710F8; + break; + } + // fall-through + case ENHY_TYPE_COB: + case ENHY_TYPE_AHG_2: + case ENHY_TYPE_AHG_4: + case ENHY_TYPE_BBA: + case ENHY_TYPE_CNE_8: + case ENHY_TYPE_AHG_13: + case ENHY_TYPE_BOJ_14: + case ENHY_TYPE_BJI_15: + case ENHY_TYPE_BOJ_16: + case ENHY_TYPE_AHG_17: + case ENHY_TYPE_BOB_18: + case ENHY_TYPE_BJI_19: + case ENHY_TYPE_AHG_20: + this->actionFunc = func_80A7127C; + break; + case ENHY_TYPE_BOJ_5: + case ENHY_TYPE_BOJ_9: + case ENHY_TYPE_BOJ_10: + case ENHY_TYPE_CNE_11: + case ENHY_TYPE_BOJ_12: + this->actionFunc = EnHy_DoNothing; + break; + default: + Actor_Kill(&this->actor); + break; + } + } +} + +void func_80A710F8(EnHy* this, GlobalContext* globalCtx) { + if (this->unk_1E8.unk_00 != 0) { + if (this->skelAnime.animation != &gObjOsAnim_0BFC) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_26); + } + } else if (gSaveContext.eventInf[3] & 1) { + if (this->skelAnime.animation != &gObjOsAnim_0FE4) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_25); + } + } else if (this->skelAnime.animation != &gObjOsAnim_12E8) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_24); + } +} + +void func_80A711B4(EnHy* this, GlobalContext* globalCtx) { + s16 yaw; + f32 distSq; + + distSq = Path_OrientAndGetDistSq(&this->actor, this->path, this->waypoint, &yaw); + Math_SmoothStepToS(&this->actor.world.rot.y, yaw, 10, 1000, 1); + this->actor.shape.rot = this->actor.world.rot; + + if ((distSq > 0.0f) && (distSq < 1000.0f)) { + this->waypoint++; + if (this->waypoint > (this->path->count - 1)) { + this->waypoint = 0; + } + } +} + +void func_80A7127C(EnHy* this, GlobalContext* globalCtx) { + func_80034F54(globalCtx, this->unk_21C, this->unk_23C, 16); +} + +void EnHy_DoNothing(EnHy* this, GlobalContext* globalCtx) { +} + +void func_80A712C0(EnHy* this, GlobalContext* globalCtx) { + if ((this->actor.xzDistToPlayer <= 100.0f) && (this->path != NULL)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_7); + this->actor.speedXZ = 0.4f; + this->actionFunc = func_80A7134C; + } + + func_80034F54(globalCtx, this->unk_21C, this->unk_23C, 16); +} + +void func_80A7134C(EnHy* this, GlobalContext* globalCtx) { + s16 yaw; + f32 distSq; + + if ((this->skelAnime.animation == &gObjOsAnim_2160) && (this->unk_1E8.unk_00 != 0)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_8); + } + + if ((this->skelAnime.animation == &gObjOsAnim_265C) && (this->unk_1E8.unk_00 == 0)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_7); + } + + this->actor.speedXZ = 0.4f; + distSq = Path_OrientAndGetDistSq(&this->actor, this->path, this->waypoint, &yaw); + Math_SmoothStepToS(&this->actor.world.rot.y, yaw, 10, 1000, 1); + this->actor.shape.rot = this->actor.world.rot; + + if (!(distSq <= 0.0f) && !(distSq >= 1000.0f)) { + if (!this->pathReverse) { + this->waypoint++; + if (this->waypoint > (this->path->count - 1)) { + this->pathReverse = true; + this->waypoint = this->path->count - 2; + } + } else { + this->waypoint--; + if (this->waypoint < 0) { + this->pathReverse = false; + this->waypoint = 1; + } + } + } +} + +void func_80A714C4(EnHy* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = func_80A71530; + } else { + func_8002F434(&this->actor, globalCtx, this->unkGetItemId, this->actor.xzDistToPlayer + 1.0f, + fabsf(this->actor.yDistToPlayer) + 1.0f); + } +} + +void func_80A71530(EnHy* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + switch (this->unkGetItemId) { + case GI_HEART_PIECE: + gSaveContext.dogParams = 0; + gSaveContext.dogIsLost = false; + gSaveContext.infTable[25] |= 2; + break; + case GI_RUPEE_BLUE: + Rupees_ChangeBy(5); + gSaveContext.dogParams = 0; + gSaveContext.dogIsLost = false; + break; + } + + this->actionFunc = func_80A7127C; + } +} + +void EnHy_Update(Actor* thisx, GlobalContext* globalCtx) { + EnHy* this = (EnHy*)thisx; + + if (this->actionFunc != EnHy_InitImpl) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndexOsAnime].segment); + SkelAnime_Update(&this->skelAnime); + EnHy_UpdateEyes(this); + + if (this->unk_1E8.unk_00 == 0) { + Actor_MoveForward(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + } + + this->actionFunc(this, globalCtx); + func_80A70978(this, globalCtx); + EnHy_UpdateCollider(this, globalCtx); +} + +s32 EnHy_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnHy* this = (EnHy*)thisx; + s32 pad; + Vec3s sp48; + u8 i; + UNK_PTR ptr; + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_hy.c", 2170); + + if (limbIndex == 15) { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->objBankIndexHead].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndexHead].segment); + i = sModelInfo[this->actor.params & 0x7F].headInfoIndex; + *dList = sHeadInfo[i].headDList; + + if (sHeadInfo[i].eyeTextures != NULL) { + ptr = sHeadInfo[i].eyeTextures[this->curEyeIndex]; + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(ptr)); + } + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndexSkel1].segment); + } + + if (limbIndex == 15) { + Matrix_Translate(1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + sp48 = this->unk_1E8.unk_08; + Matrix_RotateX((sp48.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((sp48.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + + if (limbIndex == 8) { + sp48 = this->unk_1E8.unk_0E; + Matrix_RotateX((-sp48.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((sp48.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + } + + if ((limbIndex == 8) || (limbIndex == 9) || (limbIndex == 12)) { + rot->y += Math_SinS(this->unk_21C[limbIndex]) * 200.0f; + rot->z += Math_CosS(this->unk_23C[limbIndex]) * 200.0f; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_hy.c", 2228); + + return false; +} + +void EnHy_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnHy* this = (EnHy*)thisx; + s32 pad; + Vec3f sp3C = { 400.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_hy.c", 2255); + + if (limbIndex == 7) { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->objBankIndexSkel2].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndexSkel2].segment); + } + + if ((this->actor.params & 0x7F) == ENHY_TYPE_BOJ_3 && limbIndex == 8) { + gSPDisplayList(POLY_OPA_DISP++, object_boj_DL_005BC8); + } + + if (limbIndex == 15) { + Matrix_MultVec3f(&sp3C, &this->actor.focus.pos); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_hy.c", 2281); +} + +Gfx* EnHy_SetEnvColor(GraphicsContext* globalCtx, u8 envR, u8 envG, u8 envB, u8 envA) { + Gfx* dList; + + dList = Graph_Alloc(globalCtx, 2 * sizeof(Gfx)); + gDPSetEnvColor(dList, envR, envG, envB, envA); + gSPEndDisplayList(dList + 1); + + return dList; +} + +void EnHy_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnHy* this = (EnHy*)thisx; + Color_RGBA8 envColorSeg8; + Color_RGBA8 envColorSeg9; + Color_RGBA8 envColorSeg10; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_hy.c", 2318); + + if (this->actionFunc != EnHy_InitImpl) { + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Translate(this->modelOffset.x, this->modelOffset.y, this->modelOffset.z, MTXMODE_APPLY); + envColorSeg8 = sModelInfo[this->actor.params & 0x7F].envColorSeg8; + envColorSeg9 = sModelInfo[this->actor.params & 0x7F].envColorSeg9; + + switch (this->actor.params & 0x7F) { + // ENHY_TYPE_AOB + // ENHY_TYPE_COB + case ENHY_TYPE_AHG_2: + case ENHY_TYPE_BOJ_3: + case ENHY_TYPE_AHG_4: + case ENHY_TYPE_BOJ_5: + // ENHY_TYPE_BBA + case ENHY_TYPE_BJI_7: + case ENHY_TYPE_CNE_8: + case ENHY_TYPE_BOJ_9: + case ENHY_TYPE_BOJ_10: + case ENHY_TYPE_CNE_11: + case ENHY_TYPE_BOJ_12: + case ENHY_TYPE_AHG_13: + case ENHY_TYPE_BOJ_14: + case ENHY_TYPE_BJI_15: + case ENHY_TYPE_BOJ_16: + case ENHY_TYPE_AHG_17: + // ENHY_TYPE_BOB_18 + case ENHY_TYPE_BJI_19: + case ENHY_TYPE_AHG_20: + gSPSegment(POLY_OPA_DISP++, 0x08, + EnHy_SetEnvColor(globalCtx->state.gfxCtx, envColorSeg8.r, envColorSeg8.g, envColorSeg8.b, + envColorSeg8.a)); + gSPSegment(POLY_OPA_DISP++, 0x09, + EnHy_SetEnvColor(globalCtx->state.gfxCtx, envColorSeg9.r, envColorSeg9.g, envColorSeg9.b, + envColorSeg9.a)); + + if ((this->actor.params & 0x7F) == ENHY_TYPE_CNE_8 || (this->actor.params & 0x7F) == ENHY_TYPE_CNE_11) { + if ((this->actor.params & 0x7F) == ENHY_TYPE_CNE_8) { + envColorSeg10 = envColorSeg8; + } + if ((this->actor.params & 0x7F) == ENHY_TYPE_CNE_11) { + envColorSeg10.r = envColorSeg10.g = envColorSeg10.b = 255; + envColorSeg10.a = 0; + } + gSPSegment(POLY_OPA_DISP++, 0x0A, + EnHy_SetEnvColor(globalCtx->state.gfxCtx, envColorSeg10.r, envColorSeg10.g, + envColorSeg10.b, envColorSeg10.a)); + } + break; + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnHy_OverrideLimbDraw, EnHy_PostLimbDraw, &this->actor); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_hy.c", 2388); +} diff --git a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.h b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.h new file mode 100644 index 000000000..f7e66544d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.h @@ -0,0 +1,64 @@ +#ifndef Z_EN_HY_H +#define Z_EN_HY_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0 */ ENHY_TYPE_AOB, + /* 1 */ ENHY_TYPE_COB, + /* 2 */ ENHY_TYPE_AHG_2, + /* 3 */ ENHY_TYPE_BOJ_3, + /* 4 */ ENHY_TYPE_AHG_4, + /* 5 */ ENHY_TYPE_BOJ_5, + /* 6 */ ENHY_TYPE_BBA, + /* 7 */ ENHY_TYPE_BJI_7, + /* 8 */ ENHY_TYPE_CNE_8, + /* 9 */ ENHY_TYPE_BOJ_9, + /* 10 */ ENHY_TYPE_BOJ_10, + /* 11 */ ENHY_TYPE_CNE_11, + /* 12 */ ENHY_TYPE_BOJ_12, + /* 13 */ ENHY_TYPE_AHG_13, + /* 14 */ ENHY_TYPE_BOJ_14, + /* 15 */ ENHY_TYPE_BJI_15, + /* 16 */ ENHY_TYPE_BOJ_16, + /* 17 */ ENHY_TYPE_AHG_17, + /* 18 */ ENHY_TYPE_BOB_18, + /* 19 */ ENHY_TYPE_BJI_19, + /* 20 */ ENHY_TYPE_AHG_20, + /* 21 */ ENHY_TYPE_MAX +} EnHyType; + +struct EnHy; + +typedef void (*EnHyActionFunc)(struct EnHy*, GlobalContext*); + +typedef struct EnHy { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnHyActionFunc actionFunc; + /* 0x0194 */ char unk_194; // unused + /* 0x0195 */ u8 pathReverse; + /* 0x0196 */ s8 objBankIndexHead; + /* 0x0197 */ s8 objBankIndexSkel2; // 7 < limb < 15 (upper part?) (always same as objBankIndexSkel1) + /* 0x0198 */ s8 objBankIndexSkel1; // sets the object used when drawing the skeleton for limb <= 7 (lower part?) + /* 0x0199 */ s8 objBankIndexOsAnime; + /* 0x019C */ ColliderCylinder collider; + /* 0x01E8 */ struct_80034A14_arg1 unk_1E8; + /* 0x0210 */ Path* path; + /* 0x0214 */ s8 waypoint; + /* 0x0215 */ s8 unk_215; + /* 0x0216 */ char unk_216[2]; // unused + /* 0x0218 */ s16 curEyeIndex; + /* 0x021A */ s16 nextEyeIndexTimer; + /* 0x021C */ s16 unk_21C[16]; // bodyWiggleY ? + /* 0x023C */ s16 unk_23C[16]; // bodyWiggleZ ? + /* 0x025C */ f32 unkRange; + /* 0x0260 */ s32 unkGetItemId; + /* 0x0264 */ Vec3f modelOffset; + /* 0x0270 */ Vec3s jointTable[16]; + /* 0x02D0 */ Vec3s morphTable[16]; + /* 0x0330 */ u16 unk_330; +} EnHy; // size = 0x0334 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.c b/soh/src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.c new file mode 100644 index 000000000..2bd5a53d1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.c @@ -0,0 +1,398 @@ +/* + * File: z_en_ice_hono.c + * Overlay: ovl_En_Ice_Hono + * Description: The various types of Blue Fire + */ + +#include "z_en_ice_hono.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS 0 + +void EnIceHono_Init(Actor* thisx, GlobalContext* globalCtx); +void EnIceHono_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnIceHono_Update(Actor* thisx, GlobalContext* globalCtx); +void EnIceHono_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnIceHono_CapturableFlame(EnIceHono* this, GlobalContext* globalCtx); +void EnIceHono_DropFlame(EnIceHono* this, GlobalContext* globalCtx); +void EnIceHono_SpreadFlames(EnIceHono* this, GlobalContext* globalCtx); +void EnIceHono_SmallFlameMove(EnIceHono* this, GlobalContext* globalCtx); + +void EnIceHono_SetupActionCapturableFlame(EnIceHono* this); +void EnIceHono_SetupActionDroppedFlame(EnIceHono* this); +void EnIceHono_SetupActionSpreadFlames(EnIceHono* this); +void EnIceHono_SetupActionSmallFlame(EnIceHono* this); + +const ActorInit En_Ice_Hono_InitVars = { + ACTOR_EN_ICE_HONO, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnIceHono), + (ActorFunc)EnIceHono_Init, + (ActorFunc)EnIceHono_Destroy, + (ActorFunc)EnIceHono_Update, + (ActorFunc)EnIceHono_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInitCapturableFlame = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 25, 80, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInitDroppedFlame = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_OTHER, + AC_NONE, + OC1_ON | OC1_TYPE_2, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 12, 60, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChainCapturableFlame[] = { + ICHAIN_U8(targetMode, 0, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 60, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static InitChainEntry sInitChainDroppedFlame[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static InitChainEntry sInitChainSmallFlame[] = { + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +f32 EnIceHono_XZDistanceSquared(Vec3f* v1, Vec3f* v2) { + return SQ(v1->x - v2->x) + SQ(v1->z - v2->z); +} + +void EnIceHono_InitCapturableFlame(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChainCapturableFlame); + Actor_SetScale(&this->actor, 0.0074f); + this->actor.flags |= ACTOR_FLAG_0; + Actor_SetFocus(&this->actor, 10.0f); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitCapturableFlame); + Collider_UpdateCylinder(&this->actor, &this->collider); + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + EnIceHono_SetupActionCapturableFlame(this); +} + +void EnIceHono_InitDroppedFlame(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChainDroppedFlame); + this->actor.scale.x = this->actor.scale.z = this->actor.scale.y = 0.00002f; + this->actor.gravity = -0.3f; + this->actor.minVelocityY = -4.0f; + this->actor.shape.yOffset = 0.0f; + this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = this->actor.world.rot.x = + this->actor.world.rot.y = this->actor.world.rot.z = 0; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInitDroppedFlame); + Collider_UpdateCylinder(&this->actor, &this->collider); + + this->collider.dim.radius = this->actor.scale.x * 4000.4f; + this->collider.dim.height = this->actor.scale.y * 8000.2f; + this->actor.colChkInfo.mass = 253; + EnIceHono_SetupActionDroppedFlame(this); +} + +void EnIceHono_InitSmallFlame(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChainSmallFlame); + this->actor.scale.x = this->actor.scale.z = this->actor.scale.y = 0.0008f; + this->actor.gravity = -0.3f; + this->actor.minVelocityY = -4.0f; + this->actor.shape.yOffset = 0.0f; + + EnIceHono_SetupActionSmallFlame(this); +} + +void EnIceHono_Init(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + s16 params = this->actor.params; + + switch (this->actor.params) { + case -1: + EnIceHono_InitCapturableFlame(&this->actor, globalCtx); + break; + case 0: + EnIceHono_InitDroppedFlame(&this->actor, globalCtx); + break; + case 1: + case 2: + EnIceHono_InitSmallFlame(&this->actor, globalCtx); + break; + } + + if ((this->actor.params == -1) || (this->actor.params == 0)) { + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, (s16)this->actor.world.pos.y + 10, + this->actor.world.pos.z, 155, 210, 255, 0); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + this->unk_154 = Rand_ZeroOne() * (0x1FFFF / 2.0f); + this->unk_156 = Rand_ZeroOne() * (0x1FFFF / 2.0f); + osSyncPrintf("(ice 炎)(arg_data 0x%04x)\n", this->actor.params); // "(ice flame)" + } +} + +void EnIceHono_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + + if ((this->actor.params == -1) || (this->actor.params == 0)) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +u32 EnIceHono_InBottleRange(EnIceHono* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->actor.xzDistToPlayer < 60.0f) { + Vec3f tempPos; + tempPos.x = Math_SinS(this->actor.yawTowardsPlayer + 0x8000) * 40.0f + player->actor.world.pos.x; + tempPos.y = player->actor.world.pos.y; + tempPos.z = Math_CosS(this->actor.yawTowardsPlayer + 0x8000) * 40.0f + player->actor.world.pos.z; + + //! @bug: this check is superfluous: it is automatically satisfied if the coarse check is satisfied. It may have + //! been intended to check the actor is in front of Player, but yawTowardsPlayer does not depend on Player's + //! world rotation. + if (EnIceHono_XZDistanceSquared(&tempPos, &this->actor.world.pos) <= SQ(40.0f)) { + return true; + } + } + return false; +} + +void EnIceHono_SetupActionCapturableFlame(EnIceHono* this) { + this->actionFunc = EnIceHono_CapturableFlame; + this->alpha = 255; + this->actor.shape.yOffset = -1000.0f; +} + +void EnIceHono_CapturableFlame(EnIceHono* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + } else if (EnIceHono_InBottleRange(this, globalCtx)) { + // GI_MAX in this case allows the player to catch the actor in a bottle + func_8002F434(&this->actor, globalCtx, GI_MAX, 60.0f, 100.0f); + } + + if (this->actor.xzDistToPlayer < 200.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + func_8002F8F0(&this->actor, NA_SE_EV_FIRE_PILLAR_S - SFX_FLAG); +} + +void EnIceHono_SetupActionDroppedFlame(EnIceHono* this) { + this->actionFunc = EnIceHono_DropFlame; + this->timer = 200; + this->alpha = 255; +} + +void EnIceHono_DropFlame(EnIceHono* this, GlobalContext* globalCtx) { + u32 bgFlag = this->actor.bgCheckFlags & 1; + + Math_StepToF(&this->actor.scale.x, 0.0017f, 0.00008f); + this->actor.scale.z = this->actor.scale.x; + Math_StepToF(&this->actor.scale.y, 0.0017f, 0.00008f); + + if (bgFlag != 0) { + s32 i; + for (i = 0; i < 8; i++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ICE_HONO, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, + ((s32)(Rand_ZeroOne() * 1000.0f) + i * 0x2000) - 0x1F4, 0, 1); + } + EnIceHono_SetupActionSpreadFlames(this); + } + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, this->actor.scale.x * 3500.0f, 0.0f, 5); + + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.dim.radius = this->actor.scale.x * 4000.0f; + this->collider.dim.height = this->actor.scale.y * 8000.0f; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->timer <= 0) { + Actor_Kill(&this->actor); + } +} + +void EnIceHono_SetupActionSpreadFlames(EnIceHono* this) { + this->actionFunc = EnIceHono_SpreadFlames; + this->timer = 60; + this->alpha = 255; +} + +void EnIceHono_SpreadFlames(EnIceHono* this, GlobalContext* globalCtx) { + if (this->timer > 20) { + Math_StepToF(&this->actor.scale.x, 0.011f, 0.00014f); + Math_StepToF(&this->actor.scale.y, 0.006f, 0.00012f); + } else { + Math_StepToF(&this->actor.scale.x, 0.0001f, 0.00015f); + Math_StepToF(&this->actor.scale.y, 0.0001f, 0.00015f); + } + this->actor.scale.z = this->actor.scale.x; + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, this->actor.scale.x * 3500.0f, 0.0f, 4); + if (this->timer < 25) { + this->alpha -= 10; + this->alpha = CLAMP(this->alpha, 0, 255); + } + + if ((this->alpha > 100) && (this->timer < 40)) { + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.dim.radius = this->actor.scale.x * 6000.0f; + this->collider.dim.height = this->actor.scale.y * 8000.0f; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->timer == 46) { + s32 i; + for (i = 0; i < 10; i++) { + s32 rot = i * 0x1999; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ICE_HONO, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, + ((s32)(Rand_ZeroOne() * 1000.0f) + rot) - 0x1F4, 0, 2); + } + } + + if (this->timer <= 0) { + Actor_Kill(&this->actor); + } +} + +void EnIceHono_SetupActionSmallFlame(EnIceHono* this) { + this->actionFunc = EnIceHono_SmallFlameMove; + this->timer = 44; + this->alpha = 255; + if (this->actor.params == 1) { + this->smallFlameTargetYScale = (Rand_ZeroOne() * 0.005f) + 0.004f; + this->actor.speedXZ = (Rand_ZeroOne() * 1.6f) + 0.5f; + } else { + this->smallFlameTargetYScale = (Rand_ZeroOne() * 0.005f) + 0.003f; + this->actor.speedXZ = (Rand_ZeroOne() * 2.0f) + 0.5f; + } +} + +void EnIceHono_SmallFlameMove(EnIceHono* this, GlobalContext* globalCtx) { + if (this->timer > 20) { + Math_StepToF(&this->actor.scale.x, 0.006f, 0.00016f); + Math_StepToF(&this->actor.scale.y, this->smallFlameTargetYScale * 0.667f, 0.00014f); + } else { + Math_StepToF(&this->actor.scale.x, 0.0001f, 0.00015f); + Math_StepToF(&this->actor.scale.y, 0.0001f, 0.00015f); + } + this->actor.scale.z = this->actor.scale.x; + Math_StepToF(&this->actor.speedXZ, 0, 0.06f); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 0.0f, 5); + + if (this->timer < 25) { + this->alpha -= 10; + this->alpha = CLAMP(this->alpha, 0, 255); + } + if (this->timer <= 0) { + Actor_Kill(&this->actor); + } +} + +void EnIceHono_Update(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + s32 pad1; + f32 intensity; + s32 pad2; + f32 sin154; + f32 sin156; + + if (this->timer > 0) { + this->timer--; + } + if (this->actor.params == 0) { + func_8002F8F0(&this->actor, NA_SE_IT_FLAME - SFX_FLAG); + } + if ((this->actor.params == -1) || (this->actor.params == 0)) { + this->unk_154 += 0x1111; + this->unk_156 += 0x4000; + sin156 = Math_SinS(this->unk_156); + sin154 = Math_SinS(this->unk_154); + intensity = (Rand_ZeroOne() * 0.05f) + ((sin154 * 0.125f) + (sin156 * 0.1f)) + 0.425f; + if ((intensity > 0.7f) || (intensity < 0.2f)) { + osSyncPrintf("ありえない値(ratio = %f)\n", intensity); // "impossible value(ratio = %f)" + } + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, (s16)this->actor.world.pos.y + 10, + this->actor.world.pos.z, (s32)(155.0f * intensity), (s32)(210.0f * intensity), + (s32)(255.0f * intensity), 1400); + } + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void EnIceHono_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnIceHono* this = (EnIceHono*)thisx; + u32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ice_hono.c", 695); + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, (globalCtx->state.frames * -20) % 512, + 32, 128)); + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 170, 255, 255, this->alpha); + + gDPSetEnvColor(POLY_XLU_DISP++, 0, 150, 255, 0); + + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y + 0x8000) * + (M_PI / 0x8000), + MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ice_hono.c", 718), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ice_hono.c", 722); +} diff --git a/soh/src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.h b/soh/src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.h new file mode 100644 index 000000000..8c6b8df3c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ice_Hono/z_en_ice_hono.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_ICE_HONO_H +#define Z_EN_ICE_HONO_H + +#include "ultra64.h" +#include "global.h" + +struct EnIceHono; + +typedef void (*EnIceHonoActionFunc)(struct EnIceHono*, GlobalContext*); + +typedef struct EnIceHono { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnIceHonoActionFunc actionFunc; + /* 0x0150 */ s16 alpha; + /* 0x0152 */ s16 timer; + /* 0x0154 */ s16 unk_154; + /* 0x0156 */ s16 unk_156; + /* 0x0158 */ f32 smallFlameTargetYScale; + /* 0x015C */ ColliderCylinder collider; + /* 0x01A8 */ LightNode* lightNode; + /* 0x01AC */ LightInfo lightInfo; +} EnIceHono; // size = 0x01BC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c new file mode 100644 index 000000000..120bfae41 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -0,0 +1,1461 @@ +/* + * File: z_en_ik.c + * Overlay: ovl_En_Ik + * Description: Iron Knuckle + */ + +#include "z_en_ik.h" +#include "scenes/dungeons/jyasinboss/jyasinboss_scene.h" +#include "objects/object_ik/object_ik.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef void (*EnIkDrawFunc)(struct EnIk*, GlobalContext*); + +void EnIk_Init(Actor* thisx, GlobalContext* globalCtx); +void EnIk_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnIk_Update(Actor* thisx, GlobalContext* globalCtx); +void EnIk_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A74714(EnIk* this); +void func_80A747C0(EnIk* this, GlobalContext* globalCtx); +void func_80A7492C(EnIk* this, GlobalContext* globalCtx); +void func_80A74AAC(EnIk* this); +void func_80A74BA4(EnIk* this, GlobalContext* globalCtx); +void func_80A74E2C(EnIk* this); +void func_80A74EBC(EnIk* this, GlobalContext* globalCtx); +void func_80A7506C(EnIk* this); +void func_80A7510C(EnIk* this, GlobalContext* globalCtx); +void func_80A751C8(EnIk* this); +void func_80A75260(EnIk* this, GlobalContext* globalCtx); +void func_80A753D0(EnIk* this); +void func_80A7545C(EnIk* this, GlobalContext* globalCtx); +void func_80A754A0(EnIk* this); +void func_80A75530(EnIk* this, GlobalContext* globalCtx); +void func_80A755F0(EnIk* this); +void func_80A7567C(EnIk* this, GlobalContext* globalCtx); +void func_80A758B0(EnIk* this, GlobalContext* globalCtx); +void func_80A75A38(EnIk* this, GlobalContext* globalCtx); +void func_80A75FA0(Actor* thisx, GlobalContext* globalCtx); +void func_80A76798(Actor* thisx, GlobalContext* globalCtx); +void func_80A7748C(EnIk* this, GlobalContext* globalCtx); +void func_80A774BC(EnIk* this, GlobalContext* globalCtx); +void func_80A774F8(EnIk* this, GlobalContext* globalCtx); +void func_80A77844(EnIk* this, GlobalContext* globalCtx); +void func_80A779DC(EnIk* this, GlobalContext* globalCtx); +void func_80A77AEC(EnIk* this, GlobalContext* globalCtx); +void func_80A77B0C(EnIk* this, GlobalContext* globalCtx); +void func_80A77B3C(EnIk* this, GlobalContext* globalCtx); +void func_80A77ED0(EnIk* this, GlobalContext* globalCtx); +void func_80A77EDC(EnIk* this, GlobalContext* globalCtx); +void func_80A78160(EnIk* this, GlobalContext* globalCtx); +void func_80A781CC(Actor* thisx, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 25, 80, 0, { 0, 0, 0 } }, +}; + +static ColliderTrisElementInit sTrisElementsInit[2] = { + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC3FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, 14.0f, 2.0f }, { -10.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC3FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, -6.0f, 2.0f }, { 9.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, +}; + +static ColliderTrisInit sTrisInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sTrisElementsInit, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x20000000, 0x00, 0x40 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0xD), + /* Deku stick */ DMG_ENTRY(2, 0xF), + /* Slingshot */ DMG_ENTRY(1, 0xE), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0xD), + /* Normal arrow */ DMG_ENTRY(2, 0xE), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0xD), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(2, 0xE), + /* Ice arrow */ DMG_ENTRY(2, 0xE), + /* Light arrow */ DMG_ENTRY(2, 0xE), + /* Unk arrow 1 */ DMG_ENTRY(2, 0xE), + /* Unk arrow 2 */ DMG_ENTRY(2, 0xE), + /* Unk arrow 3 */ DMG_ENTRY(15, 0xE), + /* Fire magic */ DMG_ENTRY(0, 0x6), + /* Ice magic */ DMG_ENTRY(0, 0x6), + /* Light magic */ DMG_ENTRY(0, 0x6), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0xF), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(2, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(10, 0xF), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xF), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +void EnIk_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + + if (Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_IK, ACTORCAT_ENEMY, 8000.0f) == NULL) { + func_800F5B58(); + } + + Collider_DestroyTris(globalCtx, &this->shieldCollider); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyQuad(globalCtx, &this->axeCollider); +} + +void EnIk_SetupAction(EnIk* this, EnIkActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void func_80A74398(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + s32 pad; + EffectBlureInit1 blureInit; + + thisx->update = func_80A75FA0; + thisx->draw = func_80A76798; + thisx->flags |= ACTOR_FLAG_10; + + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, thisx, &sCylinderInit); + Collider_InitTris(globalCtx, &this->shieldCollider); + Collider_SetTris(globalCtx, &this->shieldCollider, thisx, &sTrisInit, this->shieldColliderItems); + Collider_InitQuad(globalCtx, &this->axeCollider); + Collider_SetQuad(globalCtx, &this->axeCollider, thisx, &sQuadInit); + + thisx->colChkInfo.damageTable = &sDamageTable; + thisx->colChkInfo.mass = MASS_HEAVY; + this->unk_2FC = 0; + thisx->colChkInfo.health = 30; + thisx->gravity = -1.0f; + this->switchFlags = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + + if (thisx->params == 0) { + thisx->colChkInfo.health += 20; + thisx->naviEnemyId = 0x34; + } else { + Actor_SetScale(thisx, 0.012f); + thisx->naviEnemyId = 0x35; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_ENEMY); + } + + blureInit.p1StartColor[0] = blureInit.p1StartColor[1] = blureInit.p2StartColor[0] = blureInit.p2StartColor[1] = + blureInit.p2StartColor[2] = blureInit.p1EndColor[0] = blureInit.p1EndColor[1] = blureInit.p2EndColor[0] = + blureInit.p2EndColor[1] = blureInit.p2EndColor[2] = 255; + + blureInit.p2StartColor[3] = 64; + blureInit.p1StartColor[3] = 200; + blureInit.p1StartColor[2] = blureInit.p1EndColor[2] = 150; + blureInit.p1EndColor[3] = blureInit.p2EndColor[3] = 0; + + blureInit.elemDuration = 8; + blureInit.unkFlag = 0; + blureInit.calcMode = 2; + + Effect_Add(globalCtx, &this->blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); + func_80A74714(this); + + if (this->switchFlags != 0xFF) { + if (Flags_GetSwitch(globalCtx, this->switchFlags)) { + Actor_Kill(thisx); + } + } else if (thisx->params != 0 && Flags_GetClear(globalCtx, globalCtx->roomCtx.curRoom.num)) { + Actor_Kill(thisx); + } +} + +s32 func_80A745E4(EnIk* this, GlobalContext* globalCtx) { + if (((this->unk_2FB != 0) || (this->actor.params == 0)) && + (func_800354B4(globalCtx, &this->actor, 100.0f, 0x2710, 0x4000, this->actor.shape.rot.y) != 0) && + (globalCtx->gameplayFrames & 1)) { + func_80A755F0(this); + return true; + } else { + return false; + } +} + +Actor* func_80A74674(GlobalContext* globalCtx, Actor* actor) { + Actor* prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + + while (prop != NULL) { + if ((prop == actor) || (prop->id != ACTOR_BG_JYA_IRONOBJ)) { + prop = prop->next; + continue; + } else if (Actor_ActorAIsFacingAndNearActorB(actor, prop, 80.0f, 0x2710)) { + return prop; + } + + prop = prop->next; + } + + return NULL; +} + +void func_80A74714(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_00CD70); + f32 frame; + + if (this->actor.params >= 2) { + frame = frames - 1.0f; + } else { + frame = 0.0f; + } + + Animation_Change(&this->skelAnime, &object_ik_Anim_00CD70, 0.0f, frame, frames, ANIMMODE_ONCE, 0.0f); + this->unk_2F8 = 3; + this->actor.speedXZ = 0.0f; + EnIk_SetupAction(this, func_80A747C0); +} + +void func_80A747C0(EnIk* this, GlobalContext* globalCtx) { + Vec3f sp24; + + if (this->bodyCollider.base.acFlags & AC_HIT) { + sp24 = this->actor.world.pos; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_ARMOR_HIT); + sp24.y += 30.0f; + func_8003424C(globalCtx, &sp24); + this->skelAnime.playSpeed = 1.0f; + func_800F5ACC(NA_BGM_MINI_BOSS); + } + if (this->skelAnime.curFrame == 5.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_WAKEUP); + } + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2; + func_80A74AAC(this); + } +} + +void func_80A7489C(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_00DD50); + + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2; + this->unk_2F8 = 4; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_ik_Anim_00DD50, 0.0f, 0.0f, frames, ANIMMODE_LOOP, 4.0f); + EnIk_SetupAction(this, func_80A7492C); +} + +void func_80A7492C(EnIk* this, GlobalContext* globalCtx) { + s32 phi_a0 = (this->unk_2FB == 0) ? 0xAAA : 0x3FFC; + s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(yawDiff) <= phi_a0) && (this->actor.xzDistToPlayer < 100.0f) && + (ABS(this->actor.yDistToPlayer) < 150.0f)) { + if ((globalCtx->gameplayFrames & 1)) { + func_80A74E2C(this); + } else { + func_80A751C8(this); + } + } else if ((ABS(yawDiff) <= 0x4000) && (ABS(this->actor.yDistToPlayer) < 150.0f)) { + func_80A74AAC(this); + } else { + func_80A74AAC(this); + } + func_80A745E4(this, globalCtx); + SkelAnime_Update(&this->skelAnime); +} + +void func_80A74AAC(EnIk* this) { + this->unk_2F8 = 5; + if (this->unk_2FB == 0) { + Animation_Change(&this->skelAnime, &object_ik_Anim_00ED24, 1.0f, 0.0f, + Animation_GetLastFrame(&object_ik_Anim_00ED24), ANIMMODE_LOOP, -4.0f); + this->actor.speedXZ = 0.9f; + } else { + Animation_Change(&this->skelAnime, &object_ik_Anim_006734, 1.0f, 0.0f, + Animation_GetLastFrame(&object_ik_Anim_006734), ANIMMODE_LOOP, -4.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_DASH); + this->actor.speedXZ = 2.5f; + } + this->actor.world.rot.y = this->actor.shape.rot.y; + EnIk_SetupAction(this, func_80A74BA4); +} + +void func_80A74BA4(EnIk* this, GlobalContext* globalCtx) { + s16 temp_t0; + s16 temp_a1; + s16 yawDiff; + s16 sp30; + s16 sp2E; + s16 phi_a3; + + if (this->unk_2FB == 0) { + temp_t0 = 0xAAA; + phi_a3 = 0x320; + sp30 = 0; + sp2E = 0x10; + } else { + temp_t0 = 0x3FFC; + phi_a3 = 0x4B0; + sp30 = 2; + sp2E = 9; + } + temp_a1 = this->actor.wallYaw - this->actor.shape.rot.y; + if ((this->actor.bgCheckFlags & 8) && (ABS(temp_a1) >= 0x4000)) { + temp_a1 = (this->actor.yawTowardsPlayer > 0) ? this->actor.wallYaw - 0x4000 : this->actor.wallYaw + 0x4000; + Math_SmoothStepToS(&this->actor.world.rot.y, temp_a1, 1, phi_a3, 0); + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, phi_a3, 0); + } + this->actor.shape.rot.y = this->actor.world.rot.y; + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((ABS(yawDiff) <= temp_t0) && (this->actor.xzDistToPlayer < 100.0f)) { + if (ABS(this->actor.yDistToPlayer) < 150.0f) { + if ((globalCtx->gameplayFrames & 1)) { + func_80A74E2C(this); + } else { + func_80A751C8(this); + } + } + } + if (func_80A74674(globalCtx, &this->actor) != NULL) { + func_80A751C8(this); + this->unk_2FC = 1; + } else { + temp_t0 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (ABS(temp_t0) > 0x4000) { + this->unk_300--; + if (this->unk_300 == 0) { + func_80A754A0(this); + } + } else { + this->unk_300 = 0x28; + } + } + func_80A745E4(this, globalCtx); + SkelAnime_Update(&this->skelAnime); + if ((sp30 == (s16)this->skelAnime.curFrame) || (sp2E == (s16)this->skelAnime.curFrame)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_WALK); + } +} + +void func_80A74E2C(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_001C28); + + this->unk_2FF = 1; + this->unk_2F8 = 6; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_ik_Anim_001C28, 1.5f, 0.0f, frames, ANIMMODE_ONCE, -4.0f); + EnIk_SetupAction(this, func_80A74EBC); +} + +void func_80A74EBC(EnIk* this, GlobalContext* globalCtx) { + Vec3f sp2C; + + if (this->skelAnime.curFrame == 15.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_SWING_AXE); + } else if (this->skelAnime.curFrame == 21.0f) { + sp2C.x = this->actor.world.pos.x + Math_SinS(this->actor.shape.rot.y + 0x6A4) * 70.0f; + sp2C.z = this->actor.world.pos.z + Math_CosS(this->actor.shape.rot.y + 0x6A4) * 70.0f; + sp2C.y = this->actor.world.pos.y; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_HIT_GND); + Camera_AddQuake(&globalCtx->mainCamera, 2, 0x19, 5); + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + CollisionCheck_SpawnShieldParticles(globalCtx, &sp2C); + } + + if ((this->skelAnime.curFrame > 17.0f) && (this->skelAnime.curFrame < 23.0f)) { + this->unk_2FE = 1; + } else { + if ((this->unk_2FB != 0) && (this->skelAnime.curFrame < 10.0f)) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0x5DC, 0); + this->actor.shape.rot.y = this->actor.world.rot.y; + } + this->unk_2FE = 0; + } + + if (SkelAnime_Update(&this->skelAnime)) { + func_80A7506C(this); + } +} + +void func_80A7506C(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_0029FC); + + this->unk_2FE = 0; + this->unk_2F9 = (s8)frames; + this->unk_2F8 = 7; + this->unk_2FF = this->unk_2FE; + Animation_Change(&this->skelAnime, &object_ik_Anim_0029FC, 1.0f, 0.0f, frames, ANIMMODE_LOOP, -4.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_PULLOUT); + EnIk_SetupAction(this, func_80A7510C); +} + +void func_80A7510C(EnIk* this, GlobalContext* globalCtx) { + f32 frames; + + if (SkelAnime_Update(&this->skelAnime) || (--this->unk_2F9 == 0)) { + if (this->unk_2F8 == 8) { + func_80A7489C(this); + } else { + frames = Animation_GetLastFrame(&object_ik_Anim_002538); + this->unk_2F8 = 8; + Animation_Change(&this->skelAnime, &object_ik_Anim_002538, 1.5f, 0.0f, frames, ANIMMODE_ONCE_INTERP, -4.0f); + } + } +} + +void func_80A751C8(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_0033C4); + + this->unk_2FF = 2; + this->unk_300 = 0; + this->unk_2F8 = 6; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_ik_Anim_0033C4, 0.0f, 0.0f, frames, ANIMMODE_ONCE_INTERP, -6.0f); + this->unk_2FC = 0; + EnIk_SetupAction(this, func_80A75260); +} + +void func_80A75260(EnIk* this, GlobalContext* globalCtx) { + f32 temp_f0; + + this->unk_300 += 0x1C2; + temp_f0 = Math_SinS(this->unk_300); + this->skelAnime.playSpeed = ABS(temp_f0); + + if (this->skelAnime.curFrame > 11.0f) { + this->unk_2FF = 3; + } + if (((this->skelAnime.curFrame > 1.0f) && (this->skelAnime.curFrame < 9.0f)) || + ((this->skelAnime.curFrame > 13.0f) && (this->skelAnime.curFrame < 18.0f))) { + if ((this->unk_2FC == 0) && (this->unk_2FB != 0) && (this->skelAnime.curFrame < 10.0f)) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0x5DC, 0); + this->actor.shape.rot.y = this->actor.world.rot.y; + } + if (this->unk_2FE < 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_SWING_AXE); + } + this->unk_2FE = 1; + } else { + this->unk_2FE = 0; + } + if (SkelAnime_Update(&this->skelAnime)) { + func_80A753D0(this); + } +} + +void func_80A753D0(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_003DBC); + + this->unk_2FF = this->unk_2FE = 0; + this->unk_2F8 = 8; + Animation_Change(&this->skelAnime, &object_ik_Anim_003DBC, 1.5f, 0.0f, frames, ANIMMODE_ONCE_INTERP, -4.0f); + EnIk_SetupAction(this, func_80A7545C); +} + +void func_80A7545C(EnIk* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + func_80A7489C(this); + func_80A745E4(this, globalCtx); + } +} + +void func_80A754A0(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_0033C4); + + this->unk_2F8 = 1; + this->unk_2FF = 3; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_ik_Anim_0033C4, 0.5f, 13.0f, frames, ANIMMODE_ONCE_INTERP, -4.0f); + EnIk_SetupAction(this, func_80A75530); +} + +void func_80A75530(EnIk* this, GlobalContext* globalCtx) { + Math_StepUntilS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0x7D0); + this->actor.shape.rot.y = this->actor.world.rot.y; + if ((this->skelAnime.curFrame > 13.0f) && (this->skelAnime.curFrame < 18.0f)) { + if (this->unk_2FE < 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_SWING_AXE); + } + this->unk_2FE = 1; + } else { + this->unk_2FE = 0; + } + if (SkelAnime_Update(&this->skelAnime)) { + func_80A753D0(this); + func_80A745E4(this, globalCtx); + } +} + +void func_80A755F0(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_00485C); + + this->unk_2FE = 0; + this->unk_2F8 = 9; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_ik_Anim_00485C, 1.0f, 0.0f, frames, ANIMMODE_ONCE_INTERP, -4.0f); + EnIk_SetupAction(this, func_80A7567C); +} + +void func_80A7567C(EnIk* this, GlobalContext* globalCtx) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->shieldCollider.base); + if (SkelAnime_Update(&this->skelAnime)) { + if ((ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4000) && + (this->actor.xzDistToPlayer < 100.0f) && (ABS(this->actor.yDistToPlayer) < 150.0f)) { + if ((globalCtx->gameplayFrames & 1)) { + func_80A74E2C(this); + } else { + func_80A751C8(this); + } + } else { + func_80A7489C(this); + } + } +} + +void func_80A75790(EnIk* this) { + s16 yaw; + s16 yawDiff; + + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->bodyCollider.base.ac->world.pos); + this->unk_2F8 = 0; + yawDiff = yaw - this->actor.shape.rot.y; + if (ABS(yawDiff) <= 0x4000) { + Animation_Change(&this->skelAnime, &object_ik_Anim_006194, 1.0f, 0.0f, + Animation_GetLastFrame(&object_ik_Anim_006194), ANIMMODE_ONCE, -4.0f); + this->actor.speedXZ = -6.0f; + } else { + Animation_Change(&this->skelAnime, &object_ik_Anim_0045BC, 1.0f, 0.0f, + Animation_GetLastFrame(&object_ik_Anim_0045BC), ANIMMODE_ONCE, -4.0f); + this->actor.speedXZ = 6.0f; + } + this->unk_2FE = 0; + EnIk_SetupAction(this, func_80A758B0); +} + +void func_80A758B0(EnIk* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, this->actor.params + 4)) { + this->bodyBreak.val = BODYBREAK_STATUS_FINISHED; + } + if (SkelAnime_Update(&this->skelAnime)) { + if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4000) { + func_80A7489C(this); + func_80A745E4(this, globalCtx); + } else { + func_80A754A0(this); + } + } +} + +void func_80A7598C(EnIk* this) { + f32 frames = Animation_GetLastFrame(&object_ik_Anim_005944); + + this->unk_2FE = 0; + this->unk_2F8 = 2; + this->actor.speedXZ = 0.0f; + Animation_Change(&this->skelAnime, &object_ik_Anim_005944, 1.0f, 0.0f, frames, ANIMMODE_ONCE, -4.0f); + this->unk_2F9 = 0x18; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_DEAD); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_CUTBODY); + EnIk_SetupAction(this, func_80A75A38); +} + +void func_80A75A38(EnIk* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if ((this->actor.colChkInfo.health == 0) && (this->unk_2F9 != 0)) { + s32 i; + Vec3f pos; + Vec3f sp7C = { 0.0f, 0.5f, 0.0f }; + + this->unk_2F9--; + + for (i = 0xC - (this->unk_2F9 >> 1); i >= 0; i--) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(120.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(120.0f); + pos.y = this->actor.world.pos.y + 20.0f + Rand_CenteredFloat(50.0f); + EffectSsDeadDb_Spawn(globalCtx, &pos, &sp7C, &sp7C, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1, 9, true); + } + if (this->unk_2F9 == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xB0); + if (this->switchFlags != 0xFF) { + Flags_SetSwitch(globalCtx, this->switchFlags); + } + Actor_Kill(&this->actor); + } + } + } else if (this->skelAnime.curFrame == 23.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_WALK); + } +} + +void func_80A75C38(EnIk* this, GlobalContext* globalCtx) { + f32 temp_f0; + u8 pad; + u8 pad2; + u8 prevHealth; + s32 temp_v0_3; + Vec3f sp38; + + if ((this->unk_2F8 == 3) || (this->unk_2F8 == 2)) { + return; + } + if (this->shieldCollider.base.acFlags & AC_BOUNCED) { + temp_f0 = Animation_GetLastFrame(&object_ik_Anim_00485C) - 2.0f; + if (this->skelAnime.curFrame < temp_f0) { + this->skelAnime.curFrame = temp_f0; + } + this->shieldCollider.base.acFlags &= ~AC_BOUNCED; + this->bodyCollider.base.acFlags &= ~AC_HIT; + return; + } + if (!(this->bodyCollider.base.acFlags & AC_HIT)) { + return; + } + sp38 = this->actor.world.pos; + sp38.y += 50.0f; + Actor_SetDropFlag(&this->actor, &this->bodyCollider.info, 1); + temp_v0_3 = this->actor.colChkInfo.damageEffect; + this->unk_2FD = temp_v0_3 & 0xFF; + this->bodyCollider.base.acFlags &= ~AC_HIT; + + if (1) {} + + if ((this->unk_2FD == 0) || (this->unk_2FD == 0xD) || ((this->unk_2FB == 0) && (this->unk_2FD == 0xE))) { + if (this->unk_2FD != 0) { + CollisionCheck_SpawnShieldParticlesMetal(globalCtx, &sp38); + } + return; + } + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); + prevHealth = this->actor.colChkInfo.health; + Actor_ApplyDamage(&this->actor); + if (this->actor.params != 0) { + if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + this->unk_2FB = 1; + BodyBreak_Alloc(&this->bodyBreak, 3, globalCtx); + } + } else if (this->actor.colChkInfo.health <= 10) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_BOSS); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EN_LAST_DAMAGE); + if (this->switchFlags != 0xFF) { + Flags_SetSwitch(globalCtx, this->switchFlags); + } + return; + } else if (prevHealth == 50) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + } + + if (this->actor.colChkInfo.health == 0) { + func_80A7598C(this); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + return; + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0x7D0, 0); + if ((this->actor.params == 0) && (Rand_ZeroOne() < 0.5f)) { + if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) > 0x4000) { + func_80A754A0(this); + } + } + if ((this->actor.params != 0) && (this->unk_2FB != 0)) { + if ((prevHealth > 10) && (this->actor.colChkInfo.health <= 10)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_ARMOR_OFF_DEMO); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_DAMAGE); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_CUTBODY); + } + func_80A75790(this); + return; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_ARMOR_HIT); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_IRONNACK_DAMAGE); + CollisionCheck_SpawnShieldParticles(globalCtx, &sp38); +} + +void func_80A75FA0(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + u8 prevInvincibilityTimer; + + this->unk_2FA = this->unk_2FB; + func_80A75C38(this, globalCtx); + if ((this->actor.params == 0) && (this->actor.colChkInfo.health <= 10)) { + func_80A781CC(&this->actor, globalCtx); + return; + } + this->actionFunc(this, globalCtx); + if (this->axeCollider.base.atFlags & AT_HIT) { + this->axeCollider.base.atFlags &= ~AT_HIT; + if (&player->actor == this->axeCollider.base.at) { + prevInvincibilityTimer = player->invincibilityTimer; + if (player->invincibilityTimer <= 0) { + if (player->invincibilityTimer < -39) { + player->invincibilityTimer = 0; + } else { + player->invincibilityTimer = 0; + globalCtx->damagePlayer(globalCtx, -64); + this->unk_2FE = 0; + } + } + func_8002F71C(globalCtx, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); + player->invincibilityTimer = prevInvincibilityTimer; + } + } + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 0x1D); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 45.0f; + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + if ((this->actor.colChkInfo.health > 0) && (this->actor.colorFilterTimer == 0) && (this->unk_2F8 >= 2)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + if (this->unk_2FE > 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->axeCollider.base); + } + if (this->unk_2F8 == 9) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->shieldCollider.base); + } +} + +Gfx* func_80A761B0(GraphicsContext* gfxCtx, u8 primR, u8 primG, u8 primB, u8 envR, u8 envG, u8 envB) { + Gfx* displayList; + Gfx* displayListHead; + + displayList = Graph_Alloc(gfxCtx, 4 * sizeof(Gfx)); + displayListHead = displayList; + + gDPPipeSync(displayListHead++); + gDPSetPrimColor(displayListHead++, 0, 0, primR, primG, primB, 255); + gDPSetEnvColor(displayListHead++, envR, envG, envB, 255); + gSPEndDisplayList(displayListHead++); + + return displayList; +} + +s32 EnIk_OverrideLimbDraw3(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnIk* this = (EnIk*)thisx; + + if (limbIndex == 12) { + if (this->actor.params != 0) { + *dList = object_ik_DL_018E78; + } + } else if (limbIndex == 13) { + if (this->actor.params != 0) { + *dList = object_ik_DL_019100; + } + } else if ((limbIndex == 26) || (limbIndex == 27)) { + if ((this->unk_2FA & 1)) { + *dList = NULL; + } + } else if ((limbIndex == 28) || (limbIndex == 29)) { + if (!(this->unk_2FA & 1)) { + *dList = NULL; + } + } + return false; +} + +// unused +static Vec3f D_80A78470 = { 300.0f, 0.0f, 0.0f }; + +static Vec3f D_80A7847C[] = { + { 800.0f, -200.0f, -5200.0f }, + { 0.0f, 0.0f, 0.0f }, + { -200.0f, -2200.0f, -200.0f }, + { -6000.0f, 2000.0f, -3000.0f }, +}; + +static Vec3f D_80A784AC[] = { + { -3000.0, -700.0, -5000.0 }, + { -3000.0, -700.0, 2000.0 }, + { 4000.0, -700.0, 2000.0 }, +}; + +static Vec3f D_80A784D0[] = { + { 4000.0, -700.0, 2000.0 }, + { 4000.0, -700.0, -5000.0 }, + { -3000.0, -700.0, -5000.0 }, +}; + +void EnIk_PostLimbDraw3(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + Vec3f spF4; + Vec3f spE8; + EnIk* this = (EnIk*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1201); + + if (this->unk_2FB & 1) { + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 26, 27, 28, dList, BODYBREAK_OBJECT_DEFAULT); + } + if (limbIndex == 12) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1217), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->actor.params != 0) { + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_019E08); + } else { + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016D88); + } + } else if (limbIndex == 17) { + s32 i; + Vec3f sp9C[3]; + Vec3f sp78[3]; + + Matrix_MultVec3f(&D_80A7847C[0], &this->axeCollider.dim.quad[1]); + Matrix_MultVec3f(&D_80A7847C[1], &this->axeCollider.dim.quad[0]); + Matrix_MultVec3f(&D_80A7847C[2], &this->axeCollider.dim.quad[3]); + Matrix_MultVec3f(&D_80A7847C[3], &this->axeCollider.dim.quad[2]); + Collider_SetQuadVertices(&this->axeCollider, &this->axeCollider.dim.quad[0], &this->axeCollider.dim.quad[1], + &this->axeCollider.dim.quad[2], &this->axeCollider.dim.quad[3]); + Matrix_MultVec3f(&D_80A7847C[0], &spF4); + Matrix_MultVec3f(&D_80A7847C[1], &spE8); + if (this->unk_2FE > 0) { + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIdx), &spF4, &spE8); + } else if (this->unk_2FE == 0) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIdx)); + this->unk_2FE = -1; + } + if (this->unk_2F8 == 9) { + for (i = 0; i < ARRAY_COUNT(sp78); i++) { + Matrix_MultVec3f(&D_80A784AC[i], &sp9C[i]); + Matrix_MultVec3f(&D_80A784D0[i], &sp78[i]); + } + + Collider_SetTrisVertices(&this->shieldCollider, 0, &sp9C[0], &sp9C[1], &sp9C[2]); + Collider_SetTrisVertices(&this->shieldCollider, 1, &sp78[0], &sp78[1], &sp78[2]); + } + } + + switch (limbIndex) { + case 22: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1270), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016F88); + break; + case 24: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1275), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016EE8); + break; + case 26: + if (!(this->unk_2FA & 1)) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1281), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016BE0); + } + break; + case 27: + if (!(this->unk_2FA & 1)) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1288), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016CD8); + } + break; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1294); +} + +void func_80A76798(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1309); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + if (this->actor.params == 0) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A761B0(globalCtx->state.gfxCtx, 245, 225, 155, 30, 30, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A761B0(globalCtx->state.gfxCtx, 255, 40, 0, 40, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80A761B0(globalCtx->state.gfxCtx, 255, 255, 255, 20, 40, 30)); + } else if (this->actor.params == 1) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A761B0(globalCtx->state.gfxCtx, 245, 255, 205, 30, 35, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A761B0(globalCtx->state.gfxCtx, 185, 135, 25, 20, 20, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80A761B0(globalCtx->state.gfxCtx, 255, 255, 255, 30, 40, 20)); + } else if (this->actor.params == 2) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A761B0(globalCtx->state.gfxCtx, 55, 65, 55, 0, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A761B0(globalCtx->state.gfxCtx, 205, 165, 75, 25, 20, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80A761B0(globalCtx->state.gfxCtx, 205, 165, 75, 25, 20, 0)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A761B0(globalCtx->state.gfxCtx, 255, 255, 255, 180, 180, 180)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A761B0(globalCtx->state.gfxCtx, 225, 205, 115, 25, 20, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80A761B0(globalCtx->state.gfxCtx, 225, 205, 115, 25, 20, 0)); + } + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnIk_OverrideLimbDraw3, EnIk_PostLimbDraw3, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ik_inFight.c", 1351); +} + +void EnIk_StartMusic(void) { + func_800F5ACC(NA_BGM_MINI_BOSS); +} + +void func_80A76C14(EnIk* this) { + if (Animation_OnFrame(&this->skelAnime, 1.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_WAKEUP, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (Animation_OnFrame(&this->skelAnime, 33.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_WALK, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (Animation_OnFrame(&this->skelAnime, 68.0f) || Animation_OnFrame(&this->skelAnime, 80.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_ARMOR_DEMO, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (Animation_OnFrame(&this->skelAnime, 107.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_FINGER_DEMO, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (Animation_OnFrame(&this->skelAnime, 156.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_ARMOR_DEMO, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else if (Animation_OnFrame(&this->skelAnime, 188.0f)) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_WAVE_DEMO, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void func_80A76DDC(EnIk* this, GlobalContext* globalCtx, Vec3f* pos) { + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_TRANSFORM, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); +} + +void func_80A76E2C(EnIk* this, GlobalContext* globalCtx, Vec3f* pos) { + static Vec3f D_80A78514[] = { + { 1000.0, -1000.0, 1000.0 }, { 0.0, -1000.0, 0.0 }, { -1000.0, -5000.0, -4000.0 }, + { 1000.0, -5000.0, -3000.0 }, { -1000.0, 1000.0, -6000.0 }, { -1000.0, 3000.0, -5000.0 }, + { -800.0, 1000.0, -3000.0 }, { 0.0, -4000.0, -2000.0 }, { -1000.0, -2000.0, -6000.0 }, + { 1000.0, -3000.0, 0.0 }, { 2000.0, -2000.0, -4000.0 }, { -1000.0, 0.0, -6000.0 }, + { 1000.0, -2000.0, -2000.0 }, { 0.0, -2000.0, 2100.0 }, { 0.0, 0.0, 0.0 }, + { 1000.0, -1000.0, -6000.0 }, { 2000.0, 0.0, -3000.0 }, { -1000.0, -1000.0, -4000.0 }, + { 900.0, -800.0, 2700.0 }, { 720.0f, 900.0f, 2500.0f }, + }; + + if (this->unk_4D4 == 0) { + s32 pad; + Vec3f effectVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f effectAccel = { 0.0f, 0.3f, 0.0f }; + s32 i; + + for (i = ARRAY_COUNT(D_80A78514) - 1; i >= 0; i--) { + Color_RGBA8 primColor = { 200, 200, 200, 255 }; + Color_RGBA8 envColor = { 150, 150, 150, 0 }; + s32 temp_v0; + Vec3f effectPos; + + Matrix_MultVec3f(&D_80A78514[i], &effectPos); + temp_v0 = (Rand_ZeroOne() * 20.0f) - 10.0f; + primColor.r += temp_v0; + primColor.g += temp_v0; + primColor.b += temp_v0; + envColor.r += temp_v0; + envColor.g += temp_v0; + envColor.b += temp_v0; + func_8002829C(globalCtx, &effectPos, &effectVelocity, &effectAccel, &primColor, &envColor, + (Rand_ZeroOne() * 60.0f) + 300.0f, 0); + } + + this->unk_4D4 = 1; + func_80A76DDC(this, globalCtx, pos); + } +} + +void func_80A77034(EnIk* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +s32 func_80A7707C(EnIk* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* EnIk_GetNpcAction(GlobalContext* globalCtx, s32 actionIdx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[actionIdx]; + } else { + return NULL; + } +} + +void func_80A770C0(EnIk* this, GlobalContext* globalCtx, s32 actionIdx) { + CsCmdActorAction* npcAction = EnIk_GetNpcAction(globalCtx, actionIdx); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +f32 EnIk_curFrame(Actor* thisx) { + EnIk* this = (EnIk*)thisx; + + return this->skelAnime.curFrame; +} + +void func_80A77148(EnIk* this) { + this->action = 0; + this->drawMode = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80A77158(EnIk* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &object_ik_Anim_00C114, 1.0f, 0.0f, + Animation_GetLastFrame(&object_ik_Anim_00C114), ANIMMODE_ONCE, 0.0f); + func_80A770C0(this, globalCtx, 4); + this->action = 1; + this->drawMode = 1; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80A771E4(EnIk* this) { + Animation_Change(&this->skelAnime, &object_ik_Anim_00C114, 1.0f, 0.0f, + Animation_GetLastFrame(&object_ik_Anim_00C114), ANIMMODE_ONCE, 0.0f); + this->action = 2; + this->drawMode = 1; + this->unk_4D4 = 0; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80A77264(EnIk* this, GlobalContext* globalCtx, s32 arg2) { + if ((arg2 != 0) && (EnIk_GetNpcAction(globalCtx, 4) != NULL)) { + func_80A78160(this, globalCtx); + } +} + +void func_80A772A4(EnIk* this) { + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_STAGGER_DEMO, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); +} + +void func_80A772EC(EnIk* this, GlobalContext* globalCtx) { + static Vec3f D_80A78FA0; + s32 pad[2]; + f32 wDest; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->actor.world.pos, &D_80A78FA0, &wDest); + Audio_PlaySoundGeneral(NA_SE_EN_IRONNACK_DEAD, &D_80A78FA0, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void func_80A7735C(EnIk* this, GlobalContext* globalCtx) { + s32 pad[3]; + f32 frames = Animation_GetLastFrame(&object_ik_Anim_0203D8); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ik_Skel_0205C0, NULL, this->jointTable, this->morphTable, + 30); + Animation_Change(&this->skelAnime, &object_ik_Anim_0203D8, 1.0f, 0.0f, frames, ANIMMODE_ONCE, 0.0f); + this->action = 3; + this->drawMode = 2; + func_80A770C0(this, globalCtx, 4); + func_80A772EC(this, globalCtx); + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80A77434(EnIk* this, GlobalContext* globalCtx) { + this->action = 4; + this->drawMode = 2; + func_80A772A4(this); + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80A77474(EnIk* this, GlobalContext* globalCtx) { + this->action = 5; + this->drawMode = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80A7748C(EnIk* this, GlobalContext* globalCtx) { + func_80A77034(this, globalCtx); + func_80A779DC(this, globalCtx); +} + +void func_80A774BC(EnIk* this, GlobalContext* globalCtx) { + func_80A7707C(this); + func_80A77034(this, globalCtx); + func_80A779DC(this, globalCtx); +} + +void func_80A774F8(EnIk* this, GlobalContext* globalCtx) { + if (EnIk_GetNpcAction(globalCtx, 4) == NULL) { + Actor_Kill(&this->actor); + } +} + +s32 EnIk_OverrideLimbDraw2(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnIk* this = (EnIk*)thisx; + + if ((limbIndex == 13) || (limbIndex == 26) || (limbIndex == 27)) { + if (EnIk_curFrame(&this->actor) >= 30.0f) { + *dList = NULL; + } + } + + return 0; +} + +void EnIk_PostLimbDraw2(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_en_ik_inAwake.c", 207); + + switch (limbIndex) { + case 13: { + EnIk* this = (EnIk*)thisx; + + if (EnIk_curFrame(&this->actor) < 30.0f) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inAwake.c", 267), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016D88); + } + } break; + case 22: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inAwake.c", 274), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016F88); + break; + case 24: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inAwake.c", 280), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016EE8); + break; + case 26: { + EnIk* this = (EnIk*)thisx; + + if (EnIk_curFrame(&this->actor) < 30.0f) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inAwake.c", 288), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016BE0); + } + } break; + case 27: { + EnIk* this = (EnIk*)thisx; + + if (EnIk_curFrame(&this->actor) < 30.0f) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inAwake.c", 297), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016CD8); + } + } break; + } + CLOSE_DISPS(gfxCtx, "../z_en_ik_inAwake.c", 304); +} + +void func_80A77844(EnIk* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + SkelAnime* skelAnime = &this->skelAnime; + s32 pad[2]; + + OPEN_DISPS(gfxCtx, "../z_en_ik_inAwake.c", 322); + + func_8002EBCC(&this->actor, globalCtx, 0); + func_80093D18(gfxCtx); + func_80093D84(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A761B0(gfxCtx, 245, 225, 155, 30, 30, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A761B0(gfxCtx, 255, 40, 0, 40, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80A761B0(gfxCtx, 255, 255, 255, 20, 40, 30)); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnIk_OverrideLimbDraw2, EnIk_PostLimbDraw2, this); + + CLOSE_DISPS(gfxCtx, "../z_en_ik_inAwake.c", 345); +} + +void func_80A779DC(EnIk* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnIk_GetNpcAction(globalCtx, 4); + u32 action; + u32 currentNpcAction; + + if (npcAction != NULL) { + action = npcAction->action; + currentNpcAction = this->npcAction; + if (action != currentNpcAction) { + switch (action) { + case 1: + func_80A77148(this); + break; + case 2: + func_80A77158(this, globalCtx); + break; + case 3: + func_80A771E4(this); + break; + case 4: + func_80A78160(this, globalCtx); + break; + case 5: + func_80A7735C(this, globalCtx); + break; + case 6: + func_80A77434(this, globalCtx); + break; + case 7: + func_80A77474(this, globalCtx); + break; + default: + osSyncPrintf("En_Ik_inConfrontion_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + + this->npcAction = action; + } + } +} + +void func_80A77AEC(EnIk* this, GlobalContext* globalCtx) { + func_80A779DC(this, globalCtx); +} + +void func_80A77B0C(EnIk* this, GlobalContext* globalCtx) { + func_80A77034(this, globalCtx); + func_80A779DC(this, globalCtx); +} + +void func_80A77B3C(EnIk* this, GlobalContext* globalCtx) { + s32 sp24; + + sp24 = func_80A7707C(this); + func_80A76C14(this); + func_80A77034(this, globalCtx); + func_80A779DC(this, globalCtx); + func_80A77264(this, globalCtx, sp24); +} + +static EnIkActionFunc sActionFuncs[] = { + func_80A77AEC, func_80A77B0C, func_80A77B3C, func_80A7748C, func_80A774BC, func_80A774F8, +}; + +void EnIk_Update(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + + if (this->action < 0 || this->action >= ARRAY_COUNT(sActionFuncs) || sActionFuncs[this->action] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sActionFuncs[this->action](this, globalCtx); +} + +s32 EnIk_OverrideLimbDraw1(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnIk* this = (EnIk*)thisx; + f32 curFrame; + + switch (limbIndex) { + case 17: + curFrame = this->skelAnime.curFrame; + if (curFrame < 120.0f) { + *dList = NULL; + } else { + func_80A76E2C(this, globalCtx, pos); + } + break; + case 29: + case 30: + *dList = NULL; + break; + } + + return 0; +} + +void EnIk_PostLimbDraw1(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_en_ik_inConfrontion.c", 571); + + switch (limbIndex) { + case 12: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inConfrontion.c", 575), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016D88); + break; + case 22: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inConfrontion.c", 581), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016F88); + break; + case 24: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inConfrontion.c", 587), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016EE8); + break; + case 26: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inConfrontion.c", 593), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016BE0); + break; + case 27: + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_ik_inConfrontion.c", 599), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_ik_DL_016CD8); + break; + } + + CLOSE_DISPS(gfxCtx, "../z_en_ik_inConfrontion.c", 604); +} + +void func_80A77ED0(EnIk* this, GlobalContext* globalCtx) { +} + +void func_80A77EDC(EnIk* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + SkelAnime* skelAnime = &this->skelAnime; + s32 pad[2]; + + OPEN_DISPS(gfxCtx, "../z_en_ik_inConfrontion.c", 630); + + func_8002EBCC(&this->actor, globalCtx, 0); + func_80093D18(gfxCtx); + func_80093D84(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, func_80A761B0(gfxCtx, 245, 225, 155, 30, 30, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80A761B0(gfxCtx, 255, 40, 0, 40, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80A761B0(gfxCtx, 255, 255, 255, 20, 40, 30)); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnIk_OverrideLimbDraw1, EnIk_PostLimbDraw1, this); + + CLOSE_DISPS(gfxCtx, "../z_en_ik_inConfrontion.c", 653); +} + +static EnIkDrawFunc sDrawFuncs[] = { func_80A77ED0, func_80A77EDC, func_80A77844 }; + +void EnIk_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + + if (this->drawMode < 0 || this->drawMode >= ARRAY_COUNT(sDrawFuncs) || sDrawFuncs[this->drawMode] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sDrawFuncs[this->drawMode](this, globalCtx); +} + +void func_80A780D0(EnIk* this, GlobalContext* globalCtx) { + if (this->actor.params == 0) { + if (!(gSaveContext.eventChkInf[3] & 0x800)) { + this->actor.update = EnIk_Update; + this->actor.draw = EnIk_Draw; + Actor_SetScale(&this->actor, 0.01f); + } else { + func_80A78160(this, globalCtx); + EnIk_StartMusic(); + } + } + osSyncPrintf("En_Ik_inConfrontion_Init : %d !!!!!!!!!!!!!!!!\n", this->actor.params); +} + +void func_80A78160(EnIk* this, GlobalContext* globalCtx) { + this->actor.update = func_80A75FA0; + this->actor.draw = func_80A76798; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2; + gSaveContext.eventChkInf[3] |= 0x800; + Actor_SetScale(&this->actor, 0.012f); + func_80A7489C(this); +} + +void func_80A781CC(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + + if (!Gameplay_InCsMode(globalCtx)) { + this->actor.update = EnIk_Update; + this->actor.draw = EnIk_Draw; + Cutscene_SetSegment(globalCtx, gSpiritBossNabooruKnuckleDefeatCs); + gSaveContext.cutsceneTrigger = 1; + Actor_SetScale(&this->actor, 0.01f); + gSaveContext.eventChkInf[3] |= 0x1000; + func_80A7735C(this, globalCtx); + } +} + +void EnIk_Init(Actor* thisx, GlobalContext* globalCtx) { + EnIk* this = (EnIk*)thisx; + s32 flag = this->actor.params & 0xFF00; + + if (((this->actor.params & 0xFF) == 0 && (gSaveContext.eventChkInf[3] & 0x1000)) || + (flag != 0 && Flags_GetSwitch(globalCtx, flag >> 8))) { + Actor_Kill(&this->actor); + } else { + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ik_Skel_01E178, &object_ik_Anim_00C114, + this->jointTable, this->morphTable, 30); + func_80A74398(&this->actor, globalCtx); + func_80A780D0(this, globalCtx); + } +} + +const ActorInit En_Ik_InitVars = { + ACTOR_EN_IK, + ACTORCAT_BOSS, + FLAGS, + OBJECT_IK, + sizeof(EnIk), + (ActorFunc)EnIk_Init, + (ActorFunc)EnIk_Destroy, + (ActorFunc)EnIk_Update, + (ActorFunc)EnIk_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.h b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.h new file mode 100644 index 000000000..6c45cd43b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.h @@ -0,0 +1,40 @@ +#ifndef Z_EN_IK_H +#define Z_EN_IK_H + +#include "ultra64.h" +#include "global.h" + +struct EnIk; + +typedef void (*EnIkActionFunc)(struct EnIk*, GlobalContext*); + +typedef struct EnIk { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[30]; + /* 0x0244 */ Vec3s morphTable[30]; + /* 0x02F8 */ u8 unk_2F8; + /* 0x02F9 */ u8 unk_2F9; + /* 0x02FA */ u8 unk_2FA; + /* 0x02FB */ u8 unk_2FB; + /* 0x02FC */ u8 unk_2FC; + /* 0x02FD */ u8 unk_2FD; + /* 0x02FE */ s8 unk_2FE; + /* 0x02FF */ s8 unk_2FF; + /* 0x0300 */ s16 unk_300; + /* 0x0302 */ s16 switchFlags; + /* 0x0304 */ EnIkActionFunc actionFunc; + /* 0x0308 */ BodyBreak bodyBreak; + /* 0x0320 */ ColliderCylinder bodyCollider; + /* 0x036C */ ColliderQuad axeCollider; + /* 0x03EC */ ColliderTris shieldCollider; + /* 0x040C */ ColliderTrisElement shieldColliderItems[2]; + /* 0x04C4 */ s32 blureIdx; + /* 0x04C8 */ s32 action; + /* 0x04CC */ s32 drawMode; + /* 0x04D0 */ u32 npcAction; + /* 0x04D4 */ s32 unk_4D4; + /* 0x04D8 */ char unk_4D8[0x04]; +} EnIk; // size = 0x04DC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_In/z_en_in.c b/soh/src/overlays/actors/ovl_En_In/z_en_in.c new file mode 100644 index 000000000..41c773cdc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_In/z_en_in.c @@ -0,0 +1,1010 @@ +#include "z_en_in.h" +#include "overlays/actors/ovl_En_Horse/z_en_horse.h" +#include "objects/object_in/object_in.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnIn_Init(Actor* thisx, GlobalContext* globalCtx); +void EnIn_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnIn_Update(Actor* thisx, GlobalContext* globalCtx); +void EnIn_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnIn_Reset(void); + +void func_80A79FB0(EnIn* this, GlobalContext* globalCtx); +void func_80A7A304(EnIn* this, GlobalContext* globalCtx); +void func_80A7A4C8(EnIn* this, GlobalContext* globalCtx); +void func_80A7A568(EnIn* this, GlobalContext* globalCtx); +void func_80A7A848(EnIn* this, GlobalContext* globalCtx); +void func_80A7ABD4(EnIn* this, GlobalContext* globalCtx); +void func_80A7AEF0(EnIn* this, GlobalContext* globalCtx); +void func_80A7B018(EnIn* this, GlobalContext* globalCtx); +void func_80A7B024(EnIn* this, GlobalContext* globalCtx); +void func_80A7AE84(EnIn* this, GlobalContext* globalCtx); +void func_80A7A770(EnIn* this, GlobalContext* globalCtx); +void func_80A7A940(EnIn* this, GlobalContext* globalCtx); +void func_80A7AA40(EnIn* this, GlobalContext* globalCtx); +void func_80A7A4BC(EnIn* this, GlobalContext* globalCtx); + +const ActorInit En_In_InitVars = { + ACTOR_EN_IN, + ACTORCAT_NPC, + FLAGS, + OBJECT_IN, + sizeof(EnIn), + (ActorFunc)EnIn_Init, + (ActorFunc)EnIn_Destroy, + (ActorFunc)EnIn_Update, + (ActorFunc)EnIn_Draw, + (ActorResetFunc)EnIn_Reset, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { + 0, 0, 0, 0, MASS_IMMOVABLE, +}; + +typedef enum { + /* 0 */ ENIN_ANIM_0, + /* 1 */ ENIN_ANIM_1, + /* 2 */ ENIN_ANIM_2, + /* 3 */ ENIN_ANIM_3, + /* 4 */ ENIN_ANIM_4, + /* 5 */ ENIN_ANIM_5, + /* 6 */ ENIN_ANIM_6, + /* 7 */ ENIN_ANIM_7, + /* 8 */ ENIN_ANIM_8, + /* 9 */ ENIN_ANIM_9 +} EnInAnimation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &object_in_Anim_001CC0, 1.0f, ANIMMODE_LOOP, 0.0f }, { &object_in_Anim_001CC0, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &object_in_Anim_013C6C, 1.0f, ANIMMODE_LOOP, 0.0f }, { &object_in_Anim_013C6C, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &object_in_Anim_000CB0, 1.0f, ANIMMODE_LOOP, 0.0f }, { &object_in_Anim_0003B4, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &object_in_Anim_001BE0, 1.0f, ANIMMODE_LOOP, 0.0f }, { &object_in_Anim_013D60, 1.0f, ANIMMODE_LOOP, 0.0f }, + { &object_in_Anim_01431C, 1.0f, ANIMMODE_LOOP, 0.0f }, { &object_in_Anim_014CA8, 1.0f, ANIMMODE_LOOP, 0.0f }, +}; + +static AnimationHeader* D_80A7B918[] = { + &object_in_Anim_0151C8, &object_in_Anim_015DF0, &object_in_Anim_016B3C, &object_in_Anim_015814, + &object_in_Anim_01646C, &object_in_Anim_0175D0, &object_in_Anim_017B58, &object_in_Anim_018C38, +}; + +static Gfx* sAdultEraDLs[] = { + NULL, + NULL, + gIngoAdultEraLeftThighDL, + gIngoAdultEraLeftLegDL, + gIngoAdultEraLeftFootDL, + gIngoAdultEraRightThighDL, + gIngoAdultEraRightLegDL, + gIngoAdultEraRightFootDL, + gIngoAdultEraTorsoDL, + gIngoAdultEraChestDL, + gIngoAdultEraLeftShoulderDL, + gIngoAdultEraLeftArmDL, + gIngoAdultEraLeftHandDL, + gIngoAdultEraRightShoulderDL, + gIngoAdultEraRightArmDL, + gIngoAdultEraRightHandDL, + gIngoAdultEraHeadDL, + gIngoAdultEraLeftEyebrowDL, + gIngoAdultEraRightEyebrowDL, + gIngoAdultEraMustacheDL, +}; + +u16 func_80A78FB0(GlobalContext* globalCtx) { + if (gSaveContext.eventChkInf[1] & 0x10) { + if (gSaveContext.infTable[9] & 0x80) { + return 0x2046; + } else { + return 0x2045; + } + } + if (gSaveContext.infTable[9] & 0x10) { + return 0x2040; + } else { + return 0x203F; + } +} + +u16 func_80A79010(GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + u16 temp_v0 = Text_GetFaceReaction(globalCtx, 25); + + if (temp_v0 != 0) { + return temp_v0; + } + if (gSaveContext.eventChkInf[1] & 0x100) { + if (IS_DAY) { + return 0x205F; + } else { + return 0x2057; + } + } + if (IS_NIGHT) { + return 0x204E; + } + switch (gSaveContext.eventInf[0] & 0xF) { + case 1: + if (!(player->stateFlags1 & 0x800000)) { + return 0x2036; + } else if (gSaveContext.eventChkInf[1] & 0x800) { + if (gSaveContext.infTable[10] & 4) { + return 0x2036; + } else { + return 0x2038; + } + } else { + return 0x2037; + } + case 3: + if ((gSaveContext.eventInf[0] & 0x40) || (gSaveContext.eventInf[0] & 0x20)) { + return 0x203E; + } else { + return 0x203D; + } + case 4: + return 0x203A; + case 5: + case 6: + return 0x203C; + case 7: + return 0x205B; + case 2: + default: + if (gSaveContext.infTable[0x9] & 0x400) { + return 0x2031; + } else { + return 0x2030; + } + } +} + +u16 func_80A79168(GlobalContext* globalCtx, Actor* thisx) { + u16 temp_v0 = Text_GetFaceReaction(globalCtx, 25); + + if (temp_v0 != 0) { + return temp_v0; + } + if (!LINK_IS_ADULT) { + return func_80A78FB0(globalCtx); + } else { + return func_80A79010(globalCtx); + } +} + +s16 func_80A791CC(GlobalContext* globalCtx, Actor* thisx) { + s32 ret = 0; + + switch (thisx->textId) { + case 0x2045: + gSaveContext.infTable[9] |= 0x80; + break; + case 0x203E: + ret = 2; + break; + case 0x203F: + gSaveContext.eventChkInf[1] |= 2; + gSaveContext.infTable[9] |= 0x10; + break; + } + return ret; +} + +s16 func_80A7924C(GlobalContext* globalCtx, Actor* thisx) { + EnIn* this = (EnIn*)thisx; + s32 sp18 = 1; + + switch (this->actor.textId) { + case 0x2030: + case 0x2031: + if (globalCtx->msgCtx.choiceIndex == 1) { + this->actor.textId = 0x2032; + } else if (gSaveContext.rupees < 10) { + this->actor.textId = 0x2033; + } else { + this->actor.textId = 0x2034; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + gSaveContext.infTable[9] |= 0x400; + break; + case 0x2034: + if (globalCtx->msgCtx.choiceIndex == 1) { + Rupees_ChangeBy(-10); + this->actor.textId = 0x205C; + } else { + this->actor.textId = 0x2035; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + break; + case 0x2036: + case 0x2037: + if (globalCtx->msgCtx.choiceIndex == 1) { + sp18 = 2; + } else { + this->actor.textId = 0x201F; + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + break; + case 0x2038: + if (globalCtx->msgCtx.choiceIndex == 0 && gSaveContext.rupees >= 50) { + sp18 = 2; + } else { + this->actor.textId = 0x2039; + Message_ContinueTextbox(globalCtx, this->actor.textId); + gSaveContext.infTable[10] |= 4; + } + break; + case 0x205B: + if (globalCtx->msgCtx.choiceIndex == 0 && gSaveContext.rupees >= 50) { + sp18 = 2; + } else { + Message_ContinueTextbox(globalCtx, this->actor.textId = 0x2039); + gSaveContext.eventInf[0] &= ~0xF; + gSaveContext.eventInf[0] &= ~0x20; + gSaveContext.eventInf[0] &= ~0x40; + this->actionFunc = func_80A7A4C8; + } + break; + } + if (!gSaveContext.rupees) {} + + return sp18; +} + +s16 func_80A7949C(GlobalContext* globalCtx, Actor* thisx) { + s32 phi_v1 = 1; + + if (thisx->textId == 0x2035) { + Rupees_ChangeBy(-10); + thisx->textId = 0x205C; + Message_ContinueTextbox(globalCtx, thisx->textId); + } else { + phi_v1 = 2; + } + return phi_v1; +} + +s16 func_80A79500(GlobalContext* globalCtx, Actor* thisx) { + s16 sp1E = 1; + + osSyncPrintf("message_check->(%d[%x])\n", Message_GetState(&globalCtx->msgCtx), thisx->textId); + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + break; + case TEXT_STATE_CLOSING: + sp1E = func_80A791CC(globalCtx, thisx); + break; + case TEXT_STATE_DONE_FADING: + break; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + sp1E = func_80A7924C(globalCtx, thisx); + } + break; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + sp1E = func_80A7949C(globalCtx, thisx); + } + break; + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + break; + } + return sp1E; +} + +void func_80A795C8(EnIn* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3; + + if (this->skelAnime.animation == &object_in_Anim_0003B4 || this->skelAnime.animation == &object_in_Anim_001BE0 || + this->skelAnime.animation == &object_in_Anim_013D60) { + phi_a3 = 1; + } else { + phi_a3 = 0; + } + if (this->actionFunc == func_80A7A568) { + phi_a3 = 4; + } + if (this->actionFunc == func_80A7B024) { + this->unk_308.unk_18 = globalCtx->view.eye; + this->unk_308.unk_14 = 60.0f; + } else { + this->unk_308.unk_18 = player->actor.world.pos; + this->unk_308.unk_14 = 16.0f; + } + func_80034A14(&this->actor, &this->unk_308, 1, phi_a3); +} + +void func_80A79690(SkelAnime* skelAnime, EnIn* this, GlobalContext* globalCtx) { + if (skelAnime->baseTransl.y < skelAnime->jointTable[0].y) { + skelAnime->moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, skelAnime, 1.0f); + } +} + +void EnIn_ChangeAnim(EnIn* this, s32 index) { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, + sAnimationInfo[index].morphFrames); +} + +s32 func_80A7975C(EnIn* this, GlobalContext* globalCtx) { + if (this->actor.params != 1 || this->actor.shape.rot.z != 1 || !LINK_IS_ADULT) { + return 0; + } + this->animationIdx = 1; + this->collider.base.ocFlags1 &= ~OC1_ON; + Animation_Change(&this->skelAnime, D_80A7B918[this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(D_80A7B918[this->animationIdx]), 2, 0.0f); + this->actionFunc = func_80A7A304; + return 1; +} + +s32 func_80A79830(EnIn* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT20 && LINK_IS_CHILD && IS_DAY && this->actor.shape.rot.z == 1 && + !(gSaveContext.eventChkInf[1] & 0x10)) { + return 1; + } + if (globalCtx->sceneNum == SCENE_MALON_STABLE && LINK_IS_CHILD && IS_DAY && this->actor.shape.rot.z == 3 && + (gSaveContext.eventChkInf[1] & 0x10)) { + return 1; + } + if (globalCtx->sceneNum == SCENE_MALON_STABLE && LINK_IS_CHILD && IS_NIGHT) { + if ((this->actor.shape.rot.z == 2) && !(gSaveContext.eventChkInf[1] & 0x10)) { + return 1; + } + if ((this->actor.shape.rot.z == 4) && (gSaveContext.eventChkInf[1] & 0x10)) { + return 1; + } + } + if (globalCtx->sceneNum == SCENE_SPOT20 && LINK_IS_ADULT && IS_DAY) { + if ((this->actor.shape.rot.z == 5) && !(gSaveContext.eventChkInf[1] & 0x100)) { + return 2; + } + if ((this->actor.shape.rot.z == 7) && (gSaveContext.eventChkInf[1] & 0x100)) { + return 4; + } + } + if (globalCtx->sceneNum == SCENE_SOUKO && LINK_IS_ADULT && IS_NIGHT) { + if (this->actor.shape.rot.z == 6 && !(gSaveContext.eventChkInf[1] & 0x100)) { + return 3; + } + if (this->actor.shape.rot.z == 8 && (gSaveContext.eventChkInf[1] & 0x100)) { + return 3; + } + } + return 0; +} + +void EnIn_UpdateEyes(EnIn* this) { + if (this->eyeIndex != 3) { + if (DECR(this->blinkTimer) == 0) { + this->eyeIndex++; + if (this->eyeIndex >= 3) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeIndex = 0; + } + } + } +} + +void func_80A79AB4(EnIn* this, GlobalContext* globalCtx) { + s32 i; + u32 f = 0; + + if (this->skelAnime.animation != &object_in_Anim_014CA8) { + f = globalCtx->gameplayFrames; + } + for (i = 0; i < ARRAY_COUNT(this->unk_330); i++) { + this->unk_330[i].y = (2068 + 50 * i) * f; + this->unk_330[i].z = (2368 + 50 * i) * f; + } +} + +void func_80A79BAC(EnIn* this, GlobalContext* globalCtx, s32 index, u32 arg3) { + s16 entrances[] = { 0x0558, 0x04CA, 0x0157 }; + + globalCtx->nextEntranceIndex = entrances[index]; + if (index == 2) { + gSaveContext.nextCutsceneIndex = 0xFFF0; + } + globalCtx->fadeTransition = arg3; + globalCtx->sceneLoadFlag = 0x14; + func_8002DF54(globalCtx, &this->actor, 8); + Interface_ChangeAlpha(1); + if (index == 0) { + AREG(6) = 0; + } + gSaveContext.timer1State = 0; +} + +void func_80A79C78(EnIn* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f sp48; + Vec3f sp3C; + Vec3s zeroVec = { 0, 0, 0 }; + + this->camId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->camId, CAM_STAT_ACTIVE); + sp48.x = this->actor.world.pos.x; + sp48.y = this->actor.world.pos.y + 60.0f; + sp48.z = this->actor.world.pos.z; + sp3C.x = sp48.x; + sp3C.y = sp48.y - 22.0f; + sp3C.z = sp48.z + 40.0f; + Gameplay_CameraSetAtEye(globalCtx, this->camId, &sp48, &sp3C); + this->actor.shape.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, &sp3C); + this->unk_308.unk_08 = zeroVec; + this->unk_308.unk_0E = zeroVec; + Message_StartTextbox(globalCtx, 0x2025, NULL); + this->unk_308.unk_00 = 1; + player->actor.world.pos = this->actor.world.pos; + player->actor.world.pos.x += 100.0f * Math_SinS(this->actor.shape.rot.y); + player->actor.world.pos.z += 100.0f * Math_CosS(this->actor.shape.rot.y); + if (player->rideActor != NULL) { + player->rideActor->world.pos = player->actor.world.pos; + player->rideActor->freezeTimer = 10; + } + player->actor.freezeTimer = 10; + this->actor.flags &= ~ACTOR_FLAG_0; + ShrinkWindow_SetVal(0x20); + Interface_ChangeAlpha(2); +} + +static s32 D_80A7B998 = 0; + +void EnIn_Init(Actor* thisx, GlobalContext* globalCtx) { + EnIn* this = (EnIn*)thisx; + RespawnData* respawn = &gSaveContext.respawn[RESPAWN_MODE_DOWN]; + Vec3f respawnPos; + + this->ingoObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_IN); + if (this->ingoObjBankIndex < 0 && this->actor.params > 0) { + this->actionFunc = NULL; + Actor_Kill(&this->actor); + return; + } + respawnPos = respawn->pos; + // hardcoded coords for lon lon entrance + if (D_80A7B998 == 0 && respawnPos.x == 1107.0f && respawnPos.y == 0.0f && respawnPos.z == -3740.0f) { + gSaveContext.eventInf[0] = 0; + D_80A7B998 = 1; + } + this->actionFunc = func_80A79FB0; +} + +void EnIn_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnIn* this = (EnIn*)thisx; + + if (this->actionFunc != NULL && this->actionFunc != func_80A79FB0) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void func_80A79FB0(EnIn* this, GlobalContext* globalCtx) { + s32 sp3C = 0; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->ingoObjBankIndex) || this->actor.params <= 0) { + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gIngoSkel, NULL, this->jointTable, this->morphTable, 20); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + if (func_80A7975C(this, globalCtx)) { + gSaveContext.eventInf[0] &= ~0x8000; + return; + } + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->unk_308.unk_00 = 0; + this->actionFunc = func_80A7A4BC; + + switch (func_80A79830(this, globalCtx)) { + case 1: + EnIn_ChangeAnim(this, ENIN_ANIM_9); + this->actionFunc = func_80A7A4BC; + break; + case 3: + EnIn_ChangeAnim(this, ENIN_ANIM_7); + this->actionFunc = func_80A7A4BC; + if (!(gSaveContext.eventChkInf[1] & 0x100)) { + this->actor.params = 5; + } + break; + case 4: + EnIn_ChangeAnim(this, ENIN_ANIM_8); + this->eyeIndex = 3; + this->actionFunc = func_80A7A4BC; + break; + case 0: + Actor_Kill(&this->actor); + break; + default: + switch (gSaveContext.eventInf[0] & 0xF) { + case 0: + case 2: + case 3: + case 4: + case 7: + if (this->actor.params == 2) { + sp3C = 1; + } + break; + case 1: + if (this->actor.params == 3) { + sp3C = 1; + } + break; + case 5: + case 6: + if (this->actor.params == 4) { + sp3C = 1; + } + break; + } + if (sp3C != 1) { + Actor_Kill(&this->actor); + return; + } + switch (gSaveContext.eventInf[0] & 0xF) { + case 0: + case 2: + EnIn_ChangeAnim(this, ENIN_ANIM_2); + this->actionFunc = func_80A7A4C8; + gSaveContext.eventInf[0] = 0; + break; + case 1: + this->actor.targetMode = 3; + EnIn_ChangeAnim(this, ENIN_ANIM_2); + this->actionFunc = func_80A7A568; + func_80088B34(0x3C); + break; + case 3: + EnIn_ChangeAnim(this, ENIN_ANIM_4); + this->actionFunc = func_80A7A770; + break; + case 4: + EnIn_ChangeAnim(this, ENIN_ANIM_6); + this->unk_1EC = 8; + this->actionFunc = func_80A7A940; + break; + case 5: + case 6: + this->actor.targetMode = 3; + EnIn_ChangeAnim(this, ENIN_ANIM_6); + this->unk_1EC = 8; + this->actionFunc = func_80A7AA40; + break; + case 7: + EnIn_ChangeAnim(this, ENIN_ANIM_2); + this->actionFunc = func_80A7A848; + break; + } + } + } +} + +void func_80A7A304(EnIn* this, GlobalContext* globalCtx) { + if (this->skelAnime.animation == &object_in_Anim_015814 || this->skelAnime.animation == &object_in_Anim_01646C) { + if (this->skelAnime.curFrame == 8.0f) { + Audio_PlaySoundRandom(&this->actor.projectedPos, NA_SE_VO_IN_LASH_0, + NA_SE_VO_IN_LASH_1 - NA_SE_VO_IN_LASH_0 + 1); + } + } + if (this->skelAnime.animation == &object_in_Anim_018C38 && this->skelAnime.curFrame == 20.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_IN_CRY_0); + } + if (SkelAnime_Update(&this->skelAnime) != 0) { + this->animationIdx %= 8; + this->unk_1E8 = this->animationIdx; + if (this->animationIdx == 3 || this->animationIdx == 4) { + Audio_PlaySoundGeneral(NA_SE_IT_LASH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (Rand_ZeroOne() < 0.3f) { + Audio_PlaySoundGeneral(NA_SE_IT_INGO_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + } + Animation_Change(&this->skelAnime, D_80A7B918[this->animationIdx], 1.0f, 0.0f, + Animation_GetLastFrame(D_80A7B918[this->animationIdx]), 2, -10.0f); + } +} + +void func_80A7A4BC(EnIn* this, GlobalContext* globalCtx) { +} + +void func_80A7A4C8(EnIn* this, GlobalContext* globalCtx) { + if (this->unk_308.unk_00 == 2) { + func_80A79BAC(this, globalCtx, 1, 0x20); + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x000F) | 0x0001; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + gSaveContext.infTable[10] &= ~4; + Environment_ForcePlaySequence(NA_BGM_HORSE); + globalCtx->msgCtx.stateTimer = 0; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + } +} + +void func_80A7A568(EnIn* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 phi_a2; + s32 phi_a3; + + if (!(gSaveContext.eventChkInf[1] & 0x800) && (player->stateFlags1 & 0x800000)) { + gSaveContext.infTable[10] |= 0x800; + } + if (gSaveContext.timer1State == 10) { + Audio_PlaySoundGeneral(NA_SE_SY_FOUND, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + func_80A79C78(this, globalCtx); + this->actionFunc = func_80A7B024; + gSaveContext.timer1State = 0; + } else if (this->unk_308.unk_00 == 2) { + if (globalCtx->msgCtx.choiceIndex == 0) { + if (gSaveContext.rupees < 50) { + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + return; + } + gSaveContext.eventInf[0] = + (gSaveContext.eventInf[0] & ~0x10) | (((EnHorse*)GET_PLAYER(globalCtx)->rideActor)->type << 4); + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0xF) | 2; + phi_a2 = 2; + phi_a3 = 2; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FOUND, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (!(gSaveContext.eventChkInf[1] & 0x800)) { + if (gSaveContext.infTable[10] & 0x800) { + gSaveContext.eventChkInf[1] |= 0x800; + gSaveContext.infTable[10] |= 0x800; + } + } + gSaveContext.eventInf[0] &= ~0xF; + phi_a2 = 0; + phi_a3 = 0x20; + } + func_80A79BAC(this, globalCtx, phi_a2, phi_a3); + globalCtx->msgCtx.stateTimer = 0; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + } +} + +void func_80A7A770(EnIn* this, GlobalContext* globalCtx) { + if (this->unk_308.unk_00 == 0) { + this->actor.flags |= ACTOR_FLAG_16; + } else if (this->unk_308.unk_00 == 2) { + Rupees_ChangeBy(-50); + this->actor.flags &= ~ACTOR_FLAG_16; + EnIn_ChangeAnim(this, ENIN_ANIM_3); + this->actionFunc = func_80A7A848; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x0F) | 7; + this->unk_308.unk_00 = 0; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & 0xFFFF) | 0x20; + if (!(gSaveContext.eventInf[0] & 0x40)) { + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } + } +} + +void func_80A7A848(EnIn* this, GlobalContext* globalCtx) { + if (this->unk_308.unk_00 == 2) { + if ((globalCtx->msgCtx.choiceIndex == 0 && gSaveContext.rupees < 50) || globalCtx->msgCtx.choiceIndex == 1) { + gSaveContext.eventInf[0] &= ~0xF; + this->actionFunc = func_80A7A4C8; + } else { + func_80A79BAC(this, globalCtx, 2, 0x26); + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0xF) | 2; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + globalCtx->msgCtx.stateTimer = 0; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } + this->unk_308.unk_00 = 0; + gSaveContext.eventInf[0] &= ~0x20; + gSaveContext.eventInf[0] &= ~0x40; + } +} + +void func_80A7A940(EnIn* this, GlobalContext* globalCtx) { + if (this->unk_308.unk_00 == 0) { + this->actor.flags |= ACTOR_FLAG_16; + return; + } + if (this->unk_1EC != 0) { + this->unk_1EC--; + if (this->unk_1EC == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_IN_LOST); + } + } + if (this->unk_308.unk_00 == 2) { + this->actor.flags &= ~ACTOR_FLAG_16; + func_80A79BAC(this, globalCtx, 2, 0x26); + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x000F) | 0x0002; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + globalCtx->msgCtx.stateTimer = 0; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & 0xFFFF) | 0x40; + } +} + +void func_80A7AA40(EnIn* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f sp30; + Vec3f sp24; + + this->camId = Gameplay_CreateSubCamera(globalCtx); + this->activeCamId = globalCtx->activeCamera; + Gameplay_ChangeCameraStatus(globalCtx, this->activeCamId, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->camId, CAM_STAT_ACTIVE); + + this->unk_2F0 = 0.0f; + this->unk_2F4 = 50.0f; + this->unk_2F8 = 0.0f; + this->unk_2FC = 0.0f; + this->unk_300 = 50.0f; + this->unk_304 = 50.0f; + + sp30 = this->actor.world.pos; + sp24 = this->actor.world.pos; + + sp30.x += this->unk_2F0; + sp30.y += this->unk_2F4; + sp30.z += this->unk_2F8; + + sp24.x += this->unk_2FC; + sp24.y += this->unk_300; + sp24.z += this->unk_304; + + Gameplay_CameraSetAtEye(globalCtx, this->camId, &sp30, &sp24); + this->actor.textId = 0x203B; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_308.unk_00 = 1; + this->unk_1FC = 0; + globalCtx->csCtx.frames = 0; + ShrinkWindow_SetVal(0x20); + Interface_ChangeAlpha(2); + this->actionFunc = func_80A7ABD4; +} + +void func_80A7ABD4(EnIn* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f sp48; + Vec3f sp3C; + + if (player->rideActor != NULL) { + player->rideActor->freezeTimer = 10; + } + player->actor.freezeTimer = 10; + if (this->actor.textId == 0x203B) { + if (this->unk_1EC != 0) { + this->unk_1EC--; + if (this->unk_1EC == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_IN_LOST); + } + } + } + if (this->unk_308.unk_00 != 0) { + if (this->unk_308.unk_00 == 2) { + if (this->actor.textId == 0x203B) { + this->actor.textId = 0x203C; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_308.unk_00 = 1; + EnIn_ChangeAnim(this, ENIN_ANIM_3); + } else { + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + } + } + } else { + if (globalCtx->csCtx.frames++ >= 50) { + this->actionFunc = func_80A7AE84; + return; + } + if (globalCtx->csCtx.frames == 44) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_RONRON_DOOR_CLOSE); + } + Math_SmoothStepToF(&this->unk_2F0, 0.0f, 0.06f, 10000.0f, 0.0f); + Math_SmoothStepToF(&this->unk_2F4, 50.0f, 0.06f, 10000.0f, 0.0f); + Math_SmoothStepToF(&this->unk_2F8, 0.0f, 0.06f, 10000.0f, 0.0f); + Math_SmoothStepToF(&this->unk_2FC, 0.0f, 0.06f, 10000.0f, 0.0f); + Math_SmoothStepToF(&this->unk_300, 150.0f, 0.06f, 10000.0f, 0.0f); + Math_SmoothStepToF(&this->unk_304, 300.0f, 0.06f, 10000.0f, 0.0f); + + sp48 = this->actor.world.pos; + sp3C = this->actor.world.pos; + + sp48.x += this->unk_2F0; + sp48.y += this->unk_2F4; + sp48.z += this->unk_2F8; + sp3C.x += this->unk_2FC; + sp3C.y += this->unk_300; + sp3C.z += this->unk_304; + Gameplay_CameraSetAtEye(globalCtx, this->camId, &sp48, &sp3C); + } +} + +void func_80A7AE84(EnIn* this, GlobalContext* globalCtx) { + Gameplay_ChangeCameraStatus(globalCtx, this->activeCamId, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, this->camId); + func_8002DF54(globalCtx, &this->actor, 7); + Interface_ChangeAlpha(0x32); + this->actionFunc = func_80A7AEF0; +} + +void func_80A7AEF0(EnIn* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 yaw; + Vec3f pos = this->actor.world.pos; + + pos.x += 90.0f * Math_SinS(this->actor.shape.rot.y); + pos.z += 90.0f * Math_CosS(this->actor.shape.rot.y); + yaw = Math_Vec3f_Yaw(&pos, &player->actor.world.pos); + if (ABS(yaw) > 0x4000) { + globalCtx->nextEntranceIndex = 0x0476; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 5; + this->actionFunc = func_80A7B018; + } else if (this->unk_308.unk_00 == 2) { + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + } +} + +void func_80A7B018(EnIn* this, GlobalContext* globalCtx) { +} + +void func_80A7B024(EnIn* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->rideActor != NULL) { + player->rideActor->freezeTimer = 10; + } + player->actor.freezeTimer = 10; + if (this->unk_308.unk_00 == 2) { + if (1) {} + if (!(gSaveContext.eventChkInf[1] & 0x800) && (gSaveContext.infTable[10] & 0x800)) { + gSaveContext.eventChkInf[1] |= 0x800; + gSaveContext.infTable[10] |= 0x800; + } + func_80A79BAC(this, globalCtx, 0, 0x26); + gSaveContext.eventInf[0] = gSaveContext.eventInf[0] & ~0xF; + gSaveContext.eventInf[0] = (gSaveContext.eventInf[0] & ~0x8000) | 0x8000; + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + this->unk_308.unk_00 = 0; + } +} + +void EnIn_Update(Actor* thisx, GlobalContext* globalCtx) { + ColliderCylinder* collider; + EnIn* this = (EnIn*)thisx; + + if (this->actionFunc == func_80A79FB0) { + this->actionFunc(this, globalCtx); + return; + } + collider = &this->collider; + Collider_UpdateCylinder(&this->actor, collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &collider->base); + if (this->actionFunc != func_80A7A304) { + SkelAnime_Update(&this->skelAnime); + if (this->skelAnime.animation == &object_in_Anim_001BE0 && ((gSaveContext.eventInf[0] & 0xF) != 6)) { + func_80A79690(&this->skelAnime, this, globalCtx); + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + } + EnIn_UpdateEyes(this); + this->actionFunc(this, globalCtx); + if (this->actionFunc != func_80A7A304) { + func_80A79AB4(this, globalCtx); + if (gSaveContext.timer2Value < 6 && gSaveContext.timer2State != 0 && this->unk_308.unk_00 == 0) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) {} + } else { + func_800343CC(globalCtx, &this->actor, &this->unk_308.unk_00, + ((this->actor.targetMode == 6) ? 80.0f : 320.0f) + this->collider.dim.radius, func_80A79168, + func_80A79500); + if (this->unk_308.unk_00 != 0) { + this->unk_1FA = this->unk_1F8; + this->unk_1F8 = Message_GetState(&globalCtx->msgCtx); + } + } + func_80A795C8(this, globalCtx); + } +} + +s32 EnIn_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnIn* this = (EnIn*)thisx; + Vec3s sp2C; + + if (this->actor.params > 0 && limbIndex != INGO_HEAD_LIMB) { + if (sAdultEraDLs[limbIndex] != NULL) { + *dList = sAdultEraDLs[limbIndex]; + } + } + if (limbIndex == INGO_HEAD_LIMB) { + Matrix_Translate(1500.0f, 0.0f, 0.0f, MTXMODE_APPLY); + sp2C = this->unk_308.unk_08; + Matrix_RotateZ(BINANG_TO_RAD(sp2C.x), MTXMODE_APPLY); + Matrix_RotateX(BINANG_TO_RAD(sp2C.y), MTXMODE_APPLY); + Matrix_Translate(-1500.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == INGO_CHEST_LIMB) { + sp2C = this->unk_308.unk_0E; + Matrix_RotateX(BINANG_TO_RAD(sp2C.x), MTXMODE_APPLY); + Matrix_RotateY(BINANG_TO_RAD(sp2C.y), MTXMODE_APPLY); + } + if (limbIndex == INGO_CHEST_LIMB || limbIndex == INGO_LEFT_SHOULDER_LIMB || limbIndex == INGO_RIGHT_SHOULDER_LIMB) { + rot->y += Math_SinS(this->unk_330[limbIndex].y) * 200.0f; + rot->z += Math_CosS(this->unk_330[limbIndex].z) * 200.0f; + } + return 0; +} + +void EnIn_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnIn* this = (EnIn*)thisx; + Vec3f D_80A7B9A8 = { 1600.0, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_in.c", 2335); + + if (limbIndex == INGO_HEAD_LIMB) { + Matrix_MultVec3f(&D_80A7B9A8, &this->actor.focus.pos); + this->actor.focus.rot = this->actor.world.rot; + } + if (limbIndex == INGO_LEFT_HAND_LIMB && this->skelAnime.animation == &object_in_Anim_014CA8) { + gSPDisplayList(POLY_OPA_DISP++, gIngoChildEraBasketDL); + } + if (limbIndex == INGO_RIGHT_HAND_LIMB && this->skelAnime.animation == &object_in_Anim_014CA8) { + gSPDisplayList(POLY_OPA_DISP++, gIngoChildEraPitchForkDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_in.c", 2365); +} + +void EnIn_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gIngoEyeOpenTex, gIngoEyeHalfTex, gIngoEyeClosedTex, gIngoEyeClosed2Tex }; + + EnIn* this = (EnIn*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_in.c", 2384); + if (this->actionFunc != func_80A79FB0) { + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gIngoHeadGradient2Tex)); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnIn_OverrideLimbDraw, EnIn_PostLimbDraw, &this->actor); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_in.c", 2416); +} + +void EnIn_Reset(void) { + D_80A7B998 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_In/z_en_in.h b/soh/src/overlays/actors/ovl_En_In/z_en_in.h new file mode 100644 index 000000000..5777c21f7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_In/z_en_in.h @@ -0,0 +1,65 @@ +#ifndef Z_EN_IN_H +#define Z_EN_IN_H + +#include "ultra64.h" +#include "global.h" + +struct EnIn; + +typedef void (*EnInActionFunc)(struct EnIn*, GlobalContext*); + +typedef enum { + /* 0x00 */ INGO_LIMB_NONE, + /* 0x01 */ INGO_LIMB_ROOT, + /* 0x02 */ INGO_LEFT_THIGH_LIMB, + /* 0x03 */ INGO_LEFT_LEG_LIMB, + /* 0x04 */ INGO_LEFT_FOOT_LIMB, + /* 0x05 */ INGO_RIGHT_THIGH_LIMB, + /* 0x06 */ INGO_RIGHT_LEG_LIMB, + /* 0x07 */ INGO_RIGHT_FOOT_LIMB, + /* 0x08 */ INGO_TORSO_LIMB, + /* 0x09 */ INGO_CHEST_LIMB, + /* 0x0A */ INGO_LEFT_SHOULDER_LIMB, + /* 0x0B */ INGO_LEFT_ARM_LIMB, + /* 0x0C */ INGO_LEFT_HAND_LIMB, + /* 0x0D */ INGO_RIGHT_SHOULDER_LIMB, + /* 0x0E */ INGO_RIGHT_ARM_LIMB, + /* 0x0F */ INGO_RIGHT_HAND_LIMB, + /* 0x10 */ INGO_HEAD_LIMB, + /* 0x11 */ INGO_LEFT_EYEBROW_LIMB, + /* 0x12 */ INGO_RIGHTEYEBROW_LIMB, + /* 0x13 */ INGO_MUSTACHE_LIMB, + /* 0x14 */ INGO_LIMB_MAX +} IngoLimb; + +typedef struct EnIn { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnInActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ f32 unk_1E0; + /* 0x01E4 */ s8 ingoObjBankIndex; + /* 0x01E6 */ s16 animationIdx; + /* 0x01E8 */ s16 unk_1E8; + /* 0x01EA */ s16 blinkTimer; + /* 0x01EC */ s16 unk_1EC; + /* 0x01EE */ s16 eyeIndex; + /* 0x01F0 */ s16 camId; + /* 0x01F2 */ s16 activeCamId; + /* 0x01F4 */ char unk_1F4[0x4]; + /* 0x01F8 */ s16 unk_1F8; + /* 0x01FA */ s16 unk_1FA; + /* 0x01FC */ s16 unk_1FC; + /* 0x01FE */ Vec3s jointTable[INGO_LIMB_MAX]; + /* 0x0276 */ Vec3s morphTable[INGO_LIMB_MAX]; + /* 0x02F0 */ f32 unk_2F0; + /* 0x02F4 */ f32 unk_2F4; + /* 0x02F8 */ f32 unk_2F8; + /* 0x02FC */ f32 unk_2FC; + /* 0x0300 */ f32 unk_300; + /* 0x0304 */ f32 unk_304; + /* 0x0308 */ struct_80034A14_arg1 unk_308; + /* 0x0330 */ Vec3s unk_330[INGO_LIMB_MAX]; +} EnIn; // size = 0x03A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c new file mode 100644 index 000000000..649c1b747 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.c @@ -0,0 +1,802 @@ +/* + * File: z_en_insect.c + * Overlay: ovl_En_Insect + * Description: Bugs + */ + +#include "z_en_insect.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS 0 + +void EnInsect_Init(Actor* thisx, GlobalContext* globalCtx); +void EnInsect_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnInsect_Update(Actor* thisx, GlobalContext* globalCtx); +void EnInsect_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnInsect_Reset(void); + +void func_80A7C3A0(EnInsect* this); +void func_80A7C3F4(EnInsect* this, GlobalContext* globalCtx); +void func_80A7C598(EnInsect* this); +void func_80A7C5EC(EnInsect* this, GlobalContext* globalCtx); +void func_80A7C818(EnInsect* this); +void func_80A7C86C(EnInsect* this, GlobalContext* globalCtx); +void func_80A7CAD0(EnInsect* this, GlobalContext* globalCtx); +void func_80A7CBC8(EnInsect* this); +void func_80A7CC3C(EnInsect* this, GlobalContext* globalCtx); +void func_80A7CE60(EnInsect* this); +void func_80A7CEC0(EnInsect* this, GlobalContext* globalCtx); +void func_80A7D1F4(EnInsect* this); +void func_80A7D26C(EnInsect* this, GlobalContext* globalCtx); +void func_80A7D39C(EnInsect* this); +void func_80A7D460(EnInsect* this, GlobalContext* globalCtx); + +static f32 D_80A7DEB0 = 0.0f; +static s16 D_80A7DEB4 = 0; +static s16 D_80A7DEB8 = 0; + +const ActorInit En_Insect_InitVars = { + ACTOR_EN_INSECT, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnInsect), + (ActorFunc)EnInsect_Init, + (ActorFunc)EnInsect_Destroy, + (ActorFunc)EnInsect_Update, + (ActorFunc)EnInsect_Draw, + EnInsect_Reset, +}; + +static ColliderJntSphElementInit sColliderItemInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 5 }, 100 }, + }, +}; + +static ColliderJntSphInit sColliderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER | OC1_TYPE_1, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sColliderItemInit, +}; + +static u16 D_80A7DF10[] = { 0, 5, 7, 7 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 700, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 20, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 600, ICHAIN_STOP), +}; + +void func_80A7BE20(EnInsect* this) { + this->unk_314 = D_80A7DF10[this->actor.params & 3]; +} + +f32 EnInsect_XZDistanceSquared(Vec3f* v1, Vec3f* v2) { + return SQ(v1->x - v2->x) + SQ(v1->z - v2->z); +} + +s32 EnInsect_InBottleRange(EnInsect* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + Vec3f pos; + + if (this->actor.xzDistToPlayer < 32.0f) { + pos.x = Math_SinS(this->actor.yawTowardsPlayer + 0x8000) * 16.0f + player->actor.world.pos.x; + pos.y = player->actor.world.pos.y; + pos.z = Math_CosS(this->actor.yawTowardsPlayer + 0x8000) * 16.0f + player->actor.world.pos.z; + + //! @bug: this check is superfluous: it is automatically satisfied if the coarse check is satisfied. It may have + //! been intended to check the actor is in front of Player, but yawTowardsPlayer does not depend on Player's + //! world rotation. + if (EnInsect_XZDistanceSquared(&pos, &this->actor.world.pos) <= SQ(20.0f)) { + return true; + } + } + + return false; +} + +void func_80A7BF58(EnInsect* this) { + Animation_Change(&this->skelAnime, &gBugCrawlAnim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP_INTERP, 0.0f); +} + +/** + * Find the nearest soft dirt patch within 6400 units in the xz plane and the current room + * + * @return 1 if one was found, 0 otherwise + */ +s32 EnInsect_FoundNearbySoil(EnInsect* this, GlobalContext* globalCtx) { + Actor* currentActor; + f32 currentDistance; + f32 bestDistance; + s32 ret; + + ret = 0; + currentActor = globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + bestDistance = 6400.0f; + this->soilActor = NULL; + + while (currentActor != NULL) { + if (currentActor->id == ACTOR_OBJ_MAKEKINSUTA) { + currentDistance = Math3D_Dist2DSq(this->actor.world.pos.x, this->actor.world.pos.z, + currentActor->world.pos.x, currentActor->world.pos.z); + + if (currentDistance < bestDistance && currentActor->room == this->actor.room) { + ret = 1; + bestDistance = currentDistance; + this->soilActor = (ObjMakekinsuta*)currentActor; + } + } + currentActor = currentActor->next; + } + return ret; +} + +void func_80A7C058(EnInsect* this) { + if (this->unk_31E > 0) { + this->unk_31E--; + return; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_WALK); + + this->unk_31E = 3.0f / CLAMP_MIN(this->skelAnime.playSpeed, 0.1f); + if (this->unk_31E < 2) { + this->unk_31E = 2; + } +} + +void EnInsect_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnInsect* this = (EnInsect*)thisx; + GlobalContext* globalCtx = globalCtx2; + f32 rand; + s16 temp_s2; + s32 count; + + Actor_ProcessInitChain(&this->actor, sInitChain); + func_80A7BE20(this); + + temp_s2 = this->actor.params & 3; + + SkelAnime_Init(globalCtx, &this->skelAnime, &gBugSkel, &gBugCrawlAnim, this->jointTable, this->morphTable, 24); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderInit, &this->colliderItem); + + this->actor.colChkInfo.mass = 30; + + if (this->unk_314 & 1) { + this->actor.gravity = -0.2f; + this->actor.minVelocityY = -2.0f; + } + + if (this->unk_314 & 4) { + this->unk_31C = Rand_S16Offset(200, 40); + this->actor.flags |= ACTOR_FLAG_4; + } + + if (temp_s2 == 2 || temp_s2 == 3) { + if (EnInsect_FoundNearbySoil(this, globalCtx)) { + this->unk_314 |= 0x10; + D_80A7DEB0 = 0.0f; + } + + if (temp_s2 == 2) { + this->actor.world.rot.z = 0; + this->actor.shape.rot.z = this->actor.world.rot.z; + + for (count = 0; count < 2; count++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_INSECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, this->actor.shape.rot.x, + this->actor.shape.rot.y, this->actor.shape.rot.z, 3); + } + } + + func_80A7D39C(this); + + D_80A7DEB8++; + } else { + rand = Rand_ZeroOne(); + + if (rand < 0.3f) { + func_80A7C3A0(this); + } else if (rand < 0.4f) { + func_80A7C598(this); + } else { + func_80A7C818(this); + } + } +} + +void EnInsect_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s16 temp_v0; + EnInsect* this = (EnInsect*)thisx; + + temp_v0 = this->actor.params & 3; + Collider_DestroyJntSph(globalCtx, &this->collider); + if ((temp_v0 == 2 || temp_v0 == 3) && D_80A7DEB8 > 0) { + D_80A7DEB8--; + } +} + +void func_80A7C3A0(EnInsect* this) { + this->unk_31A = Rand_S16Offset(5, 35); + func_80A7BF58(this); + this->actionFunc = func_80A7C3F4; + this->unk_314 |= 0x100; +} + +void func_80A7C3F4(EnInsect* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 sp2E; + f32 playSpeed; + + sp2E = this->actor.params & 3; + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 0.5f, 0.0f); + + playSpeed = (Rand_ZeroOne() * 0.8f) + (this->actor.speedXZ * 1.2f); + this->skelAnime.playSpeed = CLAMP(playSpeed, 0.0f, 1.9f); + + SkelAnime_Update(&this->skelAnime); + this->actor.shape.rot.y = this->actor.world.rot.y; + if (this->unk_31A <= 0) { + func_80A7C598(this); + } + + if (((this->unk_314 & 4) && this->unk_31C <= 0) || + ((sp2E == 2 || sp2E == 3) && (this->unk_314 & 1) && (this->actor.bgCheckFlags & 1) && D_80A7DEB8 >= 4)) { + func_80A7CBC8(this); + } else if ((this->unk_314 & 1) && (this->actor.bgCheckFlags & 0x40)) { + func_80A7CE60(this); + } else if (this->actor.xzDistToPlayer < 40.0f) { + func_80A7C818(this); + } +} + +void func_80A7C598(EnInsect* this) { + this->unk_31A = Rand_S16Offset(10, 45); + func_80A7BF58(this); + this->actionFunc = func_80A7C5EC; + this->unk_314 |= 0x100; +} + +void func_80A7C5EC(EnInsect* this, GlobalContext* globalCtx) { + s32 pad1; + s32 pad2; + s16 yaw; + s16 sp34 = this->actor.params & 3; + + Math_SmoothStepToF(&this->actor.speedXZ, 1.5f, 0.1f, 0.5f, 0.0f); + + if (EnInsect_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > 1600.0f || (this->unk_31A < 4)) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_ScaledStepToS(&this->actor.world.rot.y, yaw, 2000); + } else if (this->actor.child != NULL && &this->actor != this->actor.child) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.child->world.pos); + Math_ScaledStepToS(&this->actor.world.rot.y, yaw, 2000); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + this->skelAnime.playSpeed = CLAMP(this->actor.speedXZ * 1.4f, 0.7f, 1.9f); + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_31A <= 0) { + func_80A7C3A0(this); + } + + if (((this->unk_314 & 4) && this->unk_31C <= 0) || + ((sp34 == 2 || sp34 == 3) && (this->unk_314 & 1) && (this->actor.bgCheckFlags & 1) && D_80A7DEB8 >= 4)) { + func_80A7CBC8(this); + } else if ((this->unk_314 & 1) && (this->actor.bgCheckFlags & 0x40)) { + func_80A7CE60(this); + } else if (this->actor.xzDistToPlayer < 40.0f) { + func_80A7C818(this); + } +} + +void func_80A7C818(EnInsect* this) { + this->unk_31A = Rand_S16Offset(10, 40); + func_80A7BF58(this); + this->actionFunc = func_80A7C86C; + this->unk_314 |= 0x100; +} + +void func_80A7C86C(EnInsect* this, GlobalContext* globalCtx) { + s32 pad1; + s32 pad2; + s16 pad3; + s16 frames; + s16 yaw; + s16 sp38 = this->actor.xzDistToPlayer < 40.0f; + + Math_SmoothStepToF(&this->actor.speedXZ, 1.8f, 0.1f, 0.5f, 0.0f); + + if (EnInsect_XZDistanceSquared(&this->actor.world.pos, &this->actor.home.pos) > 25600.0f || this->unk_31A < 4) { + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_ScaledStepToS(&this->actor.world.rot.y, yaw, 2000); + } else if (sp38 != 0) { + frames = globalCtx->state.frames; + yaw = this->actor.yawTowardsPlayer + 0x8000; + + if (frames & 0x10) { + if (frames & 0x20) { + yaw += 0x2000; + } + } else { + if (frames & 0x20) { + yaw -= 0x2000; + } + } + if (globalCtx) {} // Must be 'globalCtx' + Math_ScaledStepToS(&this->actor.world.rot.y, yaw, 2000); + } + this->actor.shape.rot.y = this->actor.world.rot.y; + this->skelAnime.playSpeed = CLAMP(this->actor.speedXZ * 1.6f, 0.8f, 1.9f); + SkelAnime_Update(&this->skelAnime); + + if (this->unk_31A <= 0 || !sp38) { + func_80A7C3A0(this); + } else if ((this->unk_314 & 1) && (this->actor.bgCheckFlags & 0x40)) { + func_80A7CE60(this); + } +} + +void func_80A7CA64(EnInsect* this) { + this->unk_31A = 200; + + Actor_SetScale(&this->actor, 0.001f); + + this->actor.draw = NULL; + this->actor.speedXZ = 0.0f; + + func_80A7BF58(this); + + this->skelAnime.playSpeed = 0.3f; + this->actionFunc = func_80A7CAD0; + this->unk_314 &= ~0x100; +} + +void func_80A7CAD0(EnInsect* this, GlobalContext* globalCtx) { + if (this->unk_31A == 20 && !(this->unk_314 & 4)) { + this->actor.draw = EnInsect_Draw; + } else if (this->unk_31A == 0) { + if (this->unk_314 & 4) { + Actor_Kill(&this->actor); + } else { + Actor_SetScale(&this->actor, 0.01f); + func_80A7C3A0(this); + } + } else if (this->unk_31A < 20) { + Actor_SetScale(&this->actor, CLAMP_MAX(this->actor.scale.x + 0.001f, 0.01f)); + SkelAnime_Update(&this->skelAnime); + } +} + +void func_80A7CBC8(EnInsect* this) { + this->unk_31A = 60; + func_80A7BF58(this); + this->skelAnime.playSpeed = 1.9f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_SINK); + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + this->actionFunc = func_80A7CC3C; + this->unk_314 &= ~0x100; + this->unk_314 |= 0x8; +} + +void func_80A7CC3C(EnInsect* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Vec3f unused = { 0.0f, 0.0f, 0.0f }; + s32 pad[2]; + Vec3f velocity; + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 0.5f, 0.0f); + Math_StepToS(&this->actor.shape.rot.x, 10922, 352); + + Actor_SetScale(&this->actor, CLAMP_MIN(this->actor.scale.x - 0.0002f, 0.001f)); + + this->actor.shape.yOffset -= 0.8f; + this->actor.world.pos.x = Rand_ZeroOne() + this->actor.home.pos.x - 0.5f; + this->actor.world.pos.z = Rand_ZeroOne() + this->actor.home.pos.z - 0.5f; + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_31A > 20 && Rand_ZeroOne() < 0.1f) { + velocity.x = Math_SinS(this->actor.shape.rot.y) * -0.6f; + velocity.y = Math_SinS(this->actor.shape.rot.x) * 0.6f; + velocity.z = Math_CosS(this->actor.shape.rot.y) * -0.6f; + func_800286CC(globalCtx, &this->actor.world.pos, &velocity, &accel, Rand_ZeroOne() * 5.0f + 8.0f, + Rand_ZeroOne() * 5.0f + 8.0f); + } + + if (this->unk_31A <= 0) { + if ((this->unk_314 & 0x10) && this->soilActor != NULL && + Math3D_Vec3fDistSq(&this->soilActor->actor.world.pos, &this->actor.world.pos) < 64.0f) { + this->soilActor->unk_152 = 1; + } + Actor_Kill(&this->actor); + } +} + +void func_80A7CE60(EnInsect* this) { + this->unk_31A = Rand_S16Offset(120, 50); + func_80A7BF58(this); + this->unk_316 = this->unk_318 = 0; + this->actionFunc = func_80A7CEC0; + this->unk_314 &= ~0x100; +} + +void func_80A7CEC0(EnInsect* this, GlobalContext* globalCtx) { + f32 temp_f0; + s16 temp_v1; + s16 pad; + s16 sp4E; + Vec3f sp40; + s32 phi_v0; + s32 phi_v0_2; + + sp4E = this->actor.params & 3; + + if (this->unk_31A >= 81) { + Math_StepToF(&this->actor.speedXZ, 0.6f, 0.08f); + } else { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.02f); + } + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y += this->actor.yDistToWater; + this->skelAnime.playSpeed = CLAMP(this->unk_31A * 0.018f, 0.1f, 1.9f); + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_31A >= 81) { + this->unk_316 += Rand_S16Offset(-50, 100); + this->unk_318 += Rand_S16Offset(-300, 600); + } + + temp_v1 = this->skelAnime.playSpeed * 200.0f; + + if (this->unk_316 < -temp_v1) { + this->unk_316 = -temp_v1; + } else { + if (temp_v1 < this->unk_316) { + phi_v0 = temp_v1; + } else { + phi_v0 = this->unk_316; + } + this->unk_316 = phi_v0; + } + this->actor.world.rot.y += this->unk_316; + + temp_v1 = this->skelAnime.playSpeed * 1000.0f; + if (this->unk_318 < -temp_v1) { + this->unk_318 = -temp_v1; + } else { + if (temp_v1 < this->unk_318) { + phi_v0_2 = temp_v1; + } else { + phi_v0_2 = this->unk_318; + } + this->unk_318 = phi_v0_2; + } + this->actor.shape.rot.y += this->unk_318; + + Math_ScaledStepToS(&this->actor.world.rot.x, 0, 3000); + this->actor.shape.rot.x = this->actor.world.rot.x; + + if (Rand_ZeroOne() < 0.03f) { + sp40.x = this->actor.world.pos.x; + sp40.y = this->actor.world.pos.y + this->actor.yDistToWater; + sp40.z = this->actor.world.pos.z; + EffectSsGRipple_Spawn(globalCtx, &sp40, 20, 100, 4); + EffectSsGRipple_Spawn(globalCtx, &sp40, 40, 200, 8); + } + + if (this->unk_31A <= 0 || ((this->unk_314 & 4) && this->unk_31C <= 0) || + ((sp4E == 2 || sp4E == 3) && (this->unk_314 & 1) && D_80A7DEB8 >= 4)) { + func_80A7D1F4(this); + } else if (!(this->actor.bgCheckFlags & 0x40)) { + if (this->unk_314 & 0x10) { + func_80A7D39C(this); + } else { + func_80A7C3A0(this); + } + } +} + +void func_80A7D1F4(EnInsect* this) { + this->unk_31A = 100; + func_80A7BF58(this); + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.minVelocityY = -0.8f; + this->actor.gravity = -0.04f; + this->unk_314 &= ~0x3; + this->actionFunc = func_80A7D26C; + this->unk_314 &= ~0x100; + this->unk_314 |= 8; +} + +void func_80A7D26C(EnInsect* this, GlobalContext* globalCtx) { + this->actor.shape.rot.x -= 500; + this->actor.shape.rot.y += 200; + Actor_SetScale(&this->actor, CLAMP_MIN(this->actor.scale.x - 0.00005f, 0.001f)); + + if (this->actor.yDistToWater > 5.0f && this->actor.yDistToWater < 30.0f && Rand_ZeroOne() < 0.3f) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, -5.0f, 5.0f, 5.0f, (Rand_ZeroOne() * 0.04f) + 0.02f); + } + + if (this->unk_31A <= 0) { + Actor_Kill(&this->actor); + } +} + +void func_80A7D39C(EnInsect* this) { + func_80A7BF58(this); + this->unk_31A = 100; + this->unk_324 = 1.5f; + this->unk_328 = Rand_ZeroOne() * (0xFFFF + 0.5f); + this->unk_316 = (Rand_ZeroOne() - 0.5f) * 1500.0f; + this->actor.world.rot.y = Rand_ZeroOne() * (0xFFFF + 0.5f); + Actor_SetScale(&this->actor, 0.003f); + this->actionFunc = func_80A7D460; + this->unk_314 |= 0x100; +} + +void func_80A7D460(EnInsect* this, GlobalContext* globalCtx) { + s32 temp_a0; + s32 sp50; + f32 phi_f0; + EnInsect* thisTemp = this; + s32 temp_a1; + f32 sp40; + f32 phi_f2; + s16 sp3A; + s16 sp38; + f32 sp34; + + sp50 = 0; + sp3A = this->actor.params & 3; + + if (this->soilActor != NULL) { + sp40 = Math3D_Vec3fDistSq(&this->actor.world.pos, &this->soilActor->actor.world.pos); + } else { + if (this->unk_314 & 0x10) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "warning: target Actor is NULL" + osSyncPrintf("warning:目標 Actor が NULL (%s %d)\n", "../z_en_mushi.c", 1046); + osSyncPrintf(VT_RST); + } + sp40 = 40.0f; + } + + D_80A7DEB0 += 0.99999994f / 300.0f; + if (D_80A7DEB0 > 1.0f) { + D_80A7DEB0 = 1.0f; + } + + if (D_80A7DEB0 > 0.999f) { + phi_f2 = 0.0f; + } else { + if (sp40 > 900.0f) { + phi_f2 = ((1.1f - D_80A7DEB0) * 100.0f) + 20.0f; + } else { + phi_f2 = (1.0f - D_80A7DEB0) * 10.0f; + } + } + + if (this->soilActor != NULL && Rand_ZeroOne() < 0.07f) { + this->actor.home.pos.x = (Rand_ZeroOne() - 0.5f) * phi_f2 + thisTemp->soilActor->actor.world.pos.x; + this->actor.home.pos.y = thisTemp->soilActor->actor.world.pos.y; + this->actor.home.pos.z = (Rand_ZeroOne() - 0.5f) * phi_f2 + thisTemp->soilActor->actor.world.pos.z; + } + + if (D_80A7DEB0 > 0.999f) { + this->unk_328 = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + this->unk_324 = Rand_ZeroOne() * 0.6f + 0.6f; + } else if (Rand_ZeroOne() < 0.07f) { + if (this->unk_324 > 1.0f) { + this->unk_324 = 0.1f; + } else { + this->unk_324 = Rand_ZeroOne() * 0.8f + 1.0f; + } + + sp34 = 1.3f - D_80A7DEB0; + if (sp34 < 0.0f) { + sp34 = 0.0f; + } else { + if (sp34 > 1.0f) { + phi_f0 = 1.0f; + } else { + phi_f0 = sp34; + } + sp34 = phi_f0; + } + + sp38 = (Rand_ZeroOne() - 0.5f) * 65535.0f * sp34; + this->unk_328 = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos) + sp38; + } + + Actor_SetScale(&this->actor, CLAMP_MAX(thisTemp->actor.scale.x + 0.0008f, 0.01f)); + + if (this->actor.bgCheckFlags & 1) { + Math_SmoothStepToF(&this->actor.speedXZ, this->unk_324, 0.1f, 0.5f, 0.0f); + Math_ScaledStepToS(&this->actor.world.rot.y, this->unk_328, 2000); + sp50 = Math_ScaledStepToS(&this->actor.world.rot.x, 0, 2000); + this->actor.shape.rot.y = this->actor.world.rot.y; + this->actor.shape.rot.x = this->actor.world.rot.x; + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 0.5f, 0.0f); + this->actor.speedXZ += (Rand_ZeroOne() - 0.5f) * 0.14f; + this->actor.velocity.y += Rand_ZeroOne() * 0.12f; + this->actor.world.rot.y += this->unk_316; + this->actor.shape.rot.y = this->actor.world.rot.y; + this->actor.shape.rot.x -= 2000; + } + + phi_f2 = Rand_ZeroOne() * 0.5f + this->actor.speedXZ * 1.3f; + if (phi_f2 < 0.0f) { + this->skelAnime.playSpeed = 0.0f; + } else { + if (phi_f2 > 1.9f) { + phi_f0 = 1.9f; + } else { + phi_f0 = phi_f2; + } + this->skelAnime.playSpeed = phi_f0; + } + + SkelAnime_Update(&this->skelAnime); + if (!(this->unk_314 & 0x40) && (this->unk_314 & 1) && (this->actor.bgCheckFlags & 1)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MUSI_LAND); + this->unk_314 |= 0x40; + } + + if (sp3A == 2 && (this->unk_314 & 0x10) && !(this->unk_314 & 0x80)) { + if (this->unk_32A >= 15) { + if (this->soilActor != NULL) { + if (!(GET_GS_FLAGS(((this->soilActor->actor.params >> 8) & 0x1F) - 1) & + (this->soilActor->actor.params & 0xFF))) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } + this->unk_314 |= 0x80; + } else { + this->unk_32A++; + } + } + + if ((this->unk_314 & 1) && (this->actor.bgCheckFlags & 0x40)) { + func_80A7CE60(this); + } else if (this->unk_314 & 0x10) { + if (sp40 < 9.0f) { + func_80A7CBC8(this); + } else if (this->unk_31A <= 0 || this->unk_31C <= 0 || + ((this->unk_314 & 1) && (this->actor.bgCheckFlags & 1) && D_80A7DEB8 >= 4 && + (sp3A == 2 || sp3A == 3))) { + func_80A7CBC8(this); + } else { + if (sp40 < 900.0f) { + this->unk_31C++; + this->unk_314 |= 0x20; + } else { + this->unk_31A = 100; + } + } + } else if (sp50 != 0) { + func_80A7C3A0(this); + } else if ((sp3A == 2 || sp3A == 3) && (this->unk_314 & 1) && this->unk_31C <= 0 && this->unk_31A <= 0 && + this->actor.floorHeight < BGCHECK_Y_MIN + 10.0f) { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "BG missing? To do Actor_delete" + osSyncPrintf("BG 抜け? Actor_delete します(%s %d)\n", "../z_en_mushi.c", 1197); + osSyncPrintf(VT_RST); + Actor_Kill(&this->actor); + } +} + +void EnInsect_Update(Actor* thisx, GlobalContext* globalCtx) { + EnInsect* this = (EnInsect*)thisx; + s32 phi_v0; + + if (this->actor.child != NULL) { + if (this->actor.child->update == NULL) { + if (&this->actor != this->actor.child) { + this->actor.child = NULL; + } + } + } + + if (this->unk_31A > 0) { + this->unk_31A--; + } + + if (this->unk_31C > 0) { + this->unk_31C--; + } + + this->actionFunc(this, globalCtx); + + if (this->actor.update != NULL) { + Actor_MoveForward(&this->actor); + if (this->unk_314 & 0x100) { + if (this->unk_314 & 1) { + if (this->actor.bgCheckFlags & 1) { + func_80A7C058(this); + } + } else { + func_80A7C058(this); + } + } + + phi_v0 = 0; + + if (this->unk_314 & 1) { + phi_v0 = 4; + } + + if (this->unk_314 & 2) { + phi_v0 |= 1; + } + + if (phi_v0 != 0) { + phi_v0 |= 0x40; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 8.0f, 5.0f, 0.0f, phi_v0); + } + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + phi_v0 = this->actor.params & 3; + + if (phi_v0 == 2 || phi_v0 == 3) { + Actor_Kill(&this->actor); + } else { + func_80A7CA64(this); + } + } else if (this->actor.xzDistToPlayer < 50.0f && this->actionFunc != func_80A7CAD0) { + if (!(this->unk_314 & 0x20) && this->unk_31C < 180) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (!(this->unk_314 & 8) && D_80A7DEB4 < 4 && EnInsect_InBottleRange(this, globalCtx) && + // GI_MAX in this case allows the player to catch the actor in a bottle + func_8002F434(&this->actor, globalCtx, GI_MAX, 60.0f, 30.0f)) { + D_80A7DEB4++; + } + } + + Actor_SetFocus(&this->actor, 0.0f); + } +} + +void EnInsect_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnInsect* this = (EnInsect*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, NULL); + Collider_UpdateSpheres(0, &this->collider); + D_80A7DEB4 = 0; +} + +void EnInsect_Reset(void) { + D_80A7DEB0 = 0.0f; + D_80A7DEB4 = 0; + D_80A7DEB8 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.h b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.h new file mode 100644 index 000000000..3755fead1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Insect/z_en_insect.h @@ -0,0 +1,32 @@ +#ifndef Z_EN_INSECT_H +#define Z_EN_INSECT_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h" + +struct EnInsect; + +typedef void (*EnInsectActionFunc)(struct EnInsect*, GlobalContext*); + +typedef struct EnInsect { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderJntSph collider; + /* 0x016C */ ColliderJntSphElement colliderItem; + /* 0x01AC */ SkelAnime skelAnime; + /* 0x01F0 */ Vec3s jointTable[24]; + /* 0x0280 */ Vec3s morphTable[24]; + /* 0x0310 */ EnInsectActionFunc actionFunc; + /* 0x0314 */ u16 unk_314; + /* 0x0316 */ s16 unk_316; + /* 0x0318 */ s16 unk_318; + /* 0x031A */ s16 unk_31A; + /* 0x031C */ s16 unk_31C; + /* 0x031E */ s16 unk_31E; + /* 0x0320 */ ObjMakekinsuta* soilActor; + /* 0x0324 */ f32 unk_324; + /* 0x0328 */ s16 unk_328; + /* 0x032A */ u8 unk_32A; +} EnInsect; // size = 0x032C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c b/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c new file mode 100644 index 000000000..4ce8fae20 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c @@ -0,0 +1,507 @@ +/* + * File: z_en_ishi.c + * Overlay: ovl_En_Ishi + * Description: Small and large gray rocks + */ + +#include "z_en_ishi.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#include "vt.h" + +#define FLAGS ACTOR_FLAG_23 + +void EnIshi_Init(Actor* thisx, GlobalContext* globalCtx); +void EnIshi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnIshi_Update(Actor* thisx, GlobalContext* globalCtx); +void EnIshi_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnIshi_Reset(void); + +void EnIshi_SetupWait(EnIshi* this); +void EnIshi_Wait(EnIshi* this, GlobalContext* globalCtx); +void EnIshi_SetupLiftedUp(EnIshi* this); +void EnIshi_LiftedUp(EnIshi* this, GlobalContext* globalCtx); +void EnIshi_SetupFly(EnIshi* this); +void EnIshi_Fly(EnIshi* this, GlobalContext* globalCtx); +void EnIshi_SpawnFragmentsSmall(EnIshi* this, GlobalContext* globalCtx); +void EnIshi_SpawnFragmentsLarge(EnIshi* this, GlobalContext* globalCtx); +void EnIshi_SpawnDustSmall(EnIshi* this, GlobalContext* globalCtx); +void EnIshi_SpawnDustLarge(EnIshi* this, GlobalContext* globalCtx); + +static s16 sRotSpeedX = 0; +static s16 sRotSpeedY = 0; + +const ActorInit En_Ishi_InitVars = { + ACTOR_EN_ISHI, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(EnIshi), + (ActorFunc)EnIshi_Init, + (ActorFunc)EnIshi_Destroy, + (ActorFunc)EnIshi_Update, + (ActorFunc)EnIshi_Draw, + (ActorResetFunc)EnIshi_Reset, +}; + +static f32 sRockScales[] = { 0.1f, 0.4f }; +static f32 D_80A7FA20[] = { 58.0f, 80.0f }; +static f32 D_80A7FA28[] = { 0.0f, 0.005f }; + +// the sizes of these arrays are very large and take up way more space than it needs to. +// coincidentally the sizes are the same as the ID for NA_SE_EV_ROCK_BROKEN, which may explain a mistake that could +// have been made here +static u16 sBreakSounds[] = { NA_SE_EV_ROCK_BROKEN, NA_SE_EV_WALL_BROKEN }; + +static u8 sBreakSoundDurations[] = { 20, 40 }; + +static EnIshiEffectSpawnFunc sFragmentSpawnFuncs[] = { EnIshi_SpawnFragmentsSmall, EnIshi_SpawnFragmentsLarge }; + +static EnIshiEffectSpawnFunc sDustSpawnFuncs[] = { EnIshi_SpawnDustSmall, EnIshi_SpawnDustLarge }; + +static ColliderCylinderInit sCylinderInits[] = { + { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC1FFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 10, 18, -2, { 0, 0, 0 } }, + }, + { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC1FFF6, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 55, 70, 0, { 0, 0, 0 } }, + }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE }; + +void EnIshi_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + EnIshi* this = (EnIshi*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInits[this->actor.params & 1]); + Collider_UpdateCylinder(&this->actor, &this->collider); +} + +s32 EnIshi_SnapToFloor(EnIshi* this, GlobalContext* globalCtx, f32 arg2) { + CollisionPoly* poly; + Vec3f pos; + s32 bgId; + f32 floorY; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 30.0f; + pos.z = this->actor.world.pos.z; + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &poly, &bgId, &this->actor, &pos); + if (floorY > BGCHECK_Y_MIN) { + this->actor.world.pos.y = floorY + arg2; + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + return true; + } else { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "Failure attaching to ground" + osSyncPrintf("地面に付着失敗(%s %d)\n", "../z_en_ishi.c", 388); + osSyncPrintf(VT_RST); + return false; + } +} + +void EnIshi_SpawnFragmentsSmall(EnIshi* this, GlobalContext* globalCtx) { + static s16 scales[] = { 16, 13, 11, 9, 7, 5 }; + s32 pad; + Vec3f velocity; + Vec3f pos; + s16 phi_v0; + s32 i; + + for (i = 0; i < ARRAY_COUNT(scales); i++) { + pos.x = this->actor.world.pos.x + (Rand_ZeroOne() - 0.5f) * 8.0f; + pos.y = this->actor.world.pos.y + (Rand_ZeroOne() * 5.0f) + 5.0f; + pos.z = this->actor.world.pos.z + (Rand_ZeroOne() - 0.5f) * 8.0f; + Math_Vec3f_Copy(&velocity, &this->actor.velocity); + if (this->actor.bgCheckFlags & 1) { + velocity.x *= 0.8f; + velocity.y *= -0.8f; + velocity.z *= 0.8f; + } else if (this->actor.bgCheckFlags & 8) { + velocity.x *= -0.8f; + velocity.y *= 0.8f; + velocity.z *= -0.8f; + } + velocity.x += (Rand_ZeroOne() - 0.5f) * 11.0f; + velocity.y += Rand_ZeroOne() * 6.0f; + velocity.z += (Rand_ZeroOne() - 0.5f) * 11.0f; + if (Rand_ZeroOne() < 0.5f) { + phi_v0 = 65; + } else { + phi_v0 = 33; + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -420, phi_v0, 30, 5, 0, scales[i], 3, 10, 40, + KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_FIELD_KEEP, gFieldKakeraDL); + } +} + +void EnIshi_SpawnFragmentsLarge(EnIshi* this, GlobalContext* globalCtx) { + static s16 scales[] = { 145, 135, 120, 100, 70, 50, 45, 40, 35 }; + Actor* thisx = &this->actor; + Vec3f velocity; + Vec3f pos; + s16 angle = 0x1000; + s32 i; + f32 rand; + s16 phi_v0; + s16 phi_v1; + + for (i = 0; i < ARRAY_COUNT(scales); i++) { + angle += 0x4E20; + rand = Rand_ZeroOne() * 10.0f; + pos.x = this->actor.world.pos.x + (Math_SinS(angle) * rand); + pos.y = this->actor.world.pos.y + (Rand_ZeroOne() * 40.0f) + 5.0f; + pos.z = this->actor.world.pos.z + (Math_CosS(angle) * rand); + Math_Vec3f_Copy(&velocity, &thisx->velocity); + if (thisx->bgCheckFlags & 1) { + velocity.x *= 0.9f; + velocity.y *= -0.8f; + velocity.z *= 0.9f; + } else if (thisx->bgCheckFlags & 8) { + velocity.x *= -0.9f; + velocity.y *= 0.8f; + velocity.z *= -0.9f; + } + rand = Rand_ZeroOne() * 10.0f; + velocity.x += rand * Math_SinS(angle); + velocity.y += (Rand_ZeroOne() * 4.0f) + ((Rand_ZeroOne() * i) * 0.7f); + velocity.z += rand * Math_CosS(angle); + if (i == 0) { + phi_v0 = 41; + phi_v1 = -450; + } else if (i < 4) { + phi_v0 = 37; + phi_v1 = -380; + } else { + phi_v0 = 69; + phi_v1 = -320; + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &this->actor.world.pos, phi_v1, phi_v0, 30, 5, 0, scales[i], 5, + 2, 70, KAKERA_COLOR_WHITE, OBJECT_GAMEPLAY_FIELD_KEEP, gSilverRockFragmentsDL); + } +} + +void EnIshi_SpawnDustSmall(EnIshi* this, GlobalContext* globalCtx) { + Vec3f pos; + + Math_Vec3f_Copy(&pos, &this->actor.world.pos); + if (this->actor.bgCheckFlags & 1) { + pos.x += 2.0f * this->actor.velocity.x; + pos.y -= 2.0f * this->actor.velocity.y; + pos.z += 2.0f * this->actor.velocity.z; + } else if (this->actor.bgCheckFlags & 8) { + pos.x -= 2.0f * this->actor.velocity.x; + pos.y += 2.0f * this->actor.velocity.y; + pos.z -= 2.0f * this->actor.velocity.z; + } + func_80033480(globalCtx, &pos, 60.0f, 3, 0x50, 0x3C, 1); +} + +void EnIshi_SpawnDustLarge(EnIshi* this, GlobalContext* globalCtx) { + Vec3f pos; + + Math_Vec3f_Copy(&pos, &this->actor.world.pos); + if (this->actor.bgCheckFlags & 1) { + pos.x += 2.0f * this->actor.velocity.x; + pos.y -= 2.0f * this->actor.velocity.y; + pos.z += 2.0f * this->actor.velocity.z; + } else if (this->actor.bgCheckFlags & 8) { + pos.x -= 2.0f * this->actor.velocity.x; + pos.y += 2.0f * this->actor.velocity.y; + pos.z -= 2.0f * this->actor.velocity.z; + } + func_80033480(globalCtx, &pos, 140.0f, 0xA, 0xB4, 0x5A, 1); +} + +void EnIshi_DropCollectible(EnIshi* this, GlobalContext* globalCtx) { + s16 dropParams; + + if ((this->actor.params & 1) == ROCK_SMALL) { + dropParams = (this->actor.params >> 8) & 0xF; + + if (dropParams >= 0xD) { + dropParams = 0; + } + + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, dropParams << 4); + } +} + +void EnIshi_Fall(EnIshi* this) { + this->actor.velocity.y += this->actor.gravity; + + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } +} + +void func_80A7ED94(Vec3f* arg0, f32 arg1) { + arg1 += ((Rand_ZeroOne() * 0.2f) - 0.1f) * arg1; + arg0->x -= arg0->x * arg1; + arg0->y -= arg0->y * arg1; + arg0->z -= arg0->z * arg1; +} + +void EnIshi_SpawnBugs(EnIshi* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 3; i++) { + Actor* bug = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_INSECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, Rand_ZeroOne() * 0xFFFF, 0, 1); + + if (bug == NULL) { + break; + } + } +} + +static InitChainEntry sInitChains[][5] = { + { + ICHAIN_F32_DIV1000(gravity, -1200, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(minVelocityY, -20000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 150, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), + }, + { + ICHAIN_F32_DIV1000(gravity, -2500, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(minVelocityY, -20000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 250, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 500, ICHAIN_STOP), + }, +}; + +void EnIshi_Init(Actor* thisx, GlobalContext* globalCtx) { + EnIshi* this = (EnIshi*)thisx; + s16 type = this->actor.params & 1; + + Actor_ProcessInitChain(&this->actor, sInitChains[type]); + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + this->actor.uncullZoneForward += 1000.0f; + } + if (this->actor.shape.rot.y == 0) { + this->actor.shape.rot.y = this->actor.world.rot.y = Rand_ZeroFloat(0x10000); + } + Actor_SetScale(&this->actor, sRockScales[type]); + EnIshi_InitCollider(&this->actor, globalCtx); + if ((type == ROCK_LARGE) && + Flags_GetSwitch(globalCtx, ((this->actor.params >> 0xA) & 0x3C) | ((this->actor.params >> 6) & 3))) { + Actor_Kill(&this->actor); + return; + } + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + this->actor.shape.yOffset = D_80A7FA20[type]; + if (!((this->actor.params >> 5) & 1) && !EnIshi_SnapToFloor(this, globalCtx, 0.0f)) { + Actor_Kill(&this->actor); + return; + } + EnIshi_SetupWait(this); +} + +void EnIshi_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnIshi* this = (EnIshi*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnIshi_SetupWait(EnIshi* this) { + this->actionFunc = EnIshi_Wait; +} + +void EnIshi_Wait(EnIshi* this, GlobalContext* globalCtx) { + static u16 liftSounds[] = { NA_SE_PL_PULL_UP_ROCK, NA_SE_PL_PULL_UP_BIGROCK }; + s32 pad; + s16 type = this->actor.params & 1; + + if (Actor_HasParent(&this->actor, globalCtx)) { + EnIshi_SetupLiftedUp(this); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, liftSounds[type]); + if ((this->actor.params >> 4) & 1) { + EnIshi_SpawnBugs(this, globalCtx); + } + } else if ((this->collider.base.acFlags & AC_HIT) && (type == ROCK_SMALL) && + this->collider.info.acHitInfo->toucher.dmgFlags & 0x40000048) { + EnIshi_DropCollectible(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, sBreakSoundDurations[type], + sBreakSounds[type]); + sFragmentSpawnFuncs[type](this, globalCtx); + sDustSpawnFuncs[type](this, globalCtx); + Actor_Kill(&this->actor); + } else if (this->actor.xzDistToPlayer < 600.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.base.acFlags &= ~AC_HIT; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actor.xzDistToPlayer < 400.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actor.xzDistToPlayer < 90.0f) { + // GI_NONE in these cases allows the player to lift the actor + if (type == ROCK_LARGE) { + func_8002F434(&this->actor, globalCtx, GI_NONE, 80.0f, 20.0f); + } else { + func_8002F434(&this->actor, globalCtx, GI_NONE, 50.0f, 10.0f); + } + } + } + } +} + +void EnIshi_SetupLiftedUp(EnIshi* this) { + this->actionFunc = EnIshi_LiftedUp; + this->actor.room = -1; + this->actor.flags |= ACTOR_FLAG_4; +} + +void EnIshi_LiftedUp(EnIshi* this, GlobalContext* globalCtx) { + if (Actor_HasNoParent(&this->actor, globalCtx)) { + this->actor.room = globalCtx->roomCtx.curRoom.num; + if ((this->actor.params & 1) == ROCK_LARGE) { + Flags_SetSwitch(globalCtx, ((this->actor.params >> 0xA) & 0x3C) | ((this->actor.params >> 6) & 3)); + } + EnIshi_SetupFly(this); + EnIshi_Fall(this); + func_80A7ED94(&this->actor.velocity, D_80A7FA28[this->actor.params & 1]); + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.5f, 35.0f, 0.0f, 0xC5); + } +} + +void EnIshi_SetupFly(EnIshi* this) { + this->actor.velocity.x = Math_SinS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.velocity.z = Math_CosS(this->actor.world.rot.y) * this->actor.speedXZ; + if ((this->actor.params & 1) == ROCK_SMALL) { + sRotSpeedX = (Rand_ZeroOne() - 0.5f) * 16000.0f; + sRotSpeedY = (Rand_ZeroOne() - 0.5f) * 2400.0f; + } else { + sRotSpeedX = (Rand_ZeroOne() - 0.5f) * 8000.0f; + sRotSpeedY = (Rand_ZeroOne() - 0.5f) * 1600.0f; + } + this->actor.colChkInfo.mass = 240; + this->actionFunc = EnIshi_Fly; +} + +void EnIshi_Fly(EnIshi* this, GlobalContext* globalCtx) { + s32 pad; + s16 type = this->actor.params & 1; + s32 pad2; + s32 quakeIdx; + Vec3f contactPos; + + if (this->actor.bgCheckFlags & 9) { + EnIshi_DropCollectible(this, globalCtx); + sFragmentSpawnFuncs[type](this, globalCtx); + if (!(this->actor.bgCheckFlags & 0x20)) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, sBreakSoundDurations[type], + sBreakSounds[type]); + sDustSpawnFuncs[type](this, globalCtx); + } + if (type == ROCK_LARGE) { + quakeIdx = Quake_Add(GET_ACTIVE_CAM(globalCtx), 3); + Quake_SetSpeed(quakeIdx, -0x3CB0); + Quake_SetQuakeValues(quakeIdx, 3, 0, 0, 0); + Quake_SetCountdown(quakeIdx, 7); + func_800AA000(this->actor.xyzDistToPlayerSq, 0xFF, 0x14, 0x96); + } + Actor_Kill(&this->actor); + return; + } + if (this->actor.bgCheckFlags & 0x40) { + contactPos.x = this->actor.world.pos.x; + contactPos.y = this->actor.world.pos.y + this->actor.yDistToWater; + contactPos.z = this->actor.world.pos.z; + EffectSsGSplash_Spawn(globalCtx, &contactPos, 0, 0, 0, 350); + if (type == ROCK_SMALL) { + EffectSsGRipple_Spawn(globalCtx, &contactPos, 150, 650, 0); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 400, 800, 4); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 500, 1100, 8); + } else { + EffectSsGRipple_Spawn(globalCtx, &contactPos, 300, 700, 0); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 500, 900, 4); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 500, 1300, 8); + } + this->actor.minVelocityY = -6.0f; + sRotSpeedX >>= 2; + sRotSpeedY >>= 2; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_DIVE_INTO_WATER_L); + this->actor.bgCheckFlags &= ~0x40; + } + Math_StepToF(&this->actor.shape.yOffset, 0.0f, 2.0f); + EnIshi_Fall(this); + func_80A7ED94(&this->actor.velocity, D_80A7FA28[type]); + func_8002D7EC(&this->actor); + this->actor.shape.rot.x += sRotSpeedX; + this->actor.shape.rot.y += sRotSpeedY; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.5f, 35.0f, 0.0f, 0xC5); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnIshi_Update(Actor* thisx, GlobalContext* globalCtx) { + EnIshi* this = (EnIshi*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EnIshi_DrawSmall(EnIshi* this, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gFieldKakeraDL); +} + +void EnIshi_DrawLarge(EnIshi* this, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ishi.c", 1050); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ishi.c", 1055), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + gSPDisplayList(POLY_OPA_DISP++, gSilverRockDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ishi.c", 1062); +} + +static EnIshiDrawFunc sDrawFuncs[] = { EnIshi_DrawSmall, EnIshi_DrawLarge }; + +void EnIshi_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnIshi* this = (EnIshi*)thisx; + + sDrawFuncs[this->actor.params & 1](this, globalCtx); +} + +void EnIshi_Reset(void) { + sRotSpeedX = 0; + sRotSpeedY = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.h b/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.h new file mode 100644 index 000000000..e1566db3c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_ISHI_H +#define Z_EN_ISHI_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ ROCK_SMALL, + /* 0x01 */ ROCK_LARGE +} EnIshiType; + +struct EnIshi; + +typedef void (*EnIshiActionFunc)(struct EnIshi*, GlobalContext*); +typedef void (*EnIshiEffectSpawnFunc)(struct EnIshi*, GlobalContext*); +typedef void (*EnIshiDrawFunc)(struct EnIshi*, GlobalContext*); + +typedef struct EnIshi { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnIshiActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; +} EnIshi; // size = 0x019C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_It/z_en_it.c b/soh/src/overlays/actors/ovl_En_It/z_en_it.c new file mode 100644 index 000000000..977ef8564 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_It/z_en_it.c @@ -0,0 +1,71 @@ +/* + * File: z_en_it.c + * Overlay: ovl_En_It + * Description: Dampe's Minigame digging spot hitboxes + */ + +#include "z_en_it.h" + +#define FLAGS 0 + +void EnIt_Init(Actor* thisx, GlobalContext* globalCtx); +void EnIt_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnIt_Update(Actor* thisx, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_NO_PUSH, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 40, 10, 0, { 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +const ActorInit En_It_InitVars = { + ACTOR_EN_IT, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnIt), + (ActorFunc)EnIt_Init, + (ActorFunc)EnIt_Destroy, + (ActorFunc)EnIt_Update, + (ActorFunc)NULL, + NULL, +}; + +void EnIt_Init(Actor* thisx, GlobalContext* globalCtx) { + EnIt* this = (EnIt*)thisx; + + this->actor.params = 0x0D05; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, 0, &sColChkInfoInit); +} + +void EnIt_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnIt* this = (EnIt*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnIt_Update(Actor* thisx, GlobalContext* globalCtx) { + EnIt* this = (EnIt*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} diff --git a/soh/src/overlays/actors/ovl_En_It/z_en_it.h b/soh/src/overlays/actors/ovl_En_It/z_en_it.h new file mode 100644 index 000000000..fb150fcb0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_It/z_en_it.h @@ -0,0 +1,15 @@ +#ifndef Z_EN_IT_H +#define Z_EN_IT_H + +#include "ultra64.h" +#include "global.h" + +struct EnIt; + +typedef struct EnIt { + /* 0x0000 */ Actor actor; + /* 0x014C */ u32 unk_14C; + /* 0x0150 */ ColliderCylinder collider; +} EnIt; // size = 0x019C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c new file mode 100644 index 000000000..362f344fa --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.c @@ -0,0 +1,323 @@ +/* + * File: z_en_jj.c + * Overlay: ovl_En_Jj + * Description: Lord Jabu-Jabu + */ + +#include "z_en_jj.h" +#include "objects/object_jj/object_jj.h" +#include "overlays/actors/ovl_Eff_Dust/z_eff_dust.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ JABUJABU_EYE_OPEN, + /* 1 */ JABUJABU_EYE_HALF, + /* 2 */ JABUJABU_EYE_CLOSED, + /* 3 */ JABUJABU_EYE_MAX +} EnJjEyeState; + +void EnJj_Init(Actor* thisx, GlobalContext* globalCtx); +void EnJj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnJj_Update(Actor* thisx, GlobalContext* globalCtx); +void EnJj_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnJj_UpdateStaticCollision(Actor* thisx, GlobalContext* globalCtx); +void EnJj_WaitToOpenMouth(EnJj* this, GlobalContext* globalCtx); +void EnJj_WaitForFish(EnJj* this, GlobalContext* globalCtx); +void EnJj_BeginCutscene(EnJj* this, GlobalContext* globalCtx); +void EnJj_RemoveDust(EnJj* this, GlobalContext* globalCtx); + +const ActorInit En_Jj_InitVars = { + ACTOR_EN_JJ, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_JJ, + sizeof(EnJj), + (ActorFunc)EnJj_Init, + (ActorFunc)EnJj_Destroy, + (ActorFunc)EnJj_Update, + (ActorFunc)EnJj_Draw, + NULL, +}; + +static s32 sUnused = 0; + +#include "z_en_jj_cutscene_data.c" EARLY + +static s32 sUnused2[] = { 0, 0 }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000004, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 170, 150, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 87, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 3300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1100, ICHAIN_STOP), +}; + +void EnJj_SetupAction(EnJj* this, EnJjActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnJj_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnJj* this = (EnJj*)thisx; + CollisionHeader* colHeader = NULL; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); + + switch (this->dyna.actor.params) { + case JABUJABU_MAIN: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gJabuJabuSkel, &gJabuJabuAnim, this->jointTable, + this->morphTable, 22); + Animation_PlayLoop(&this->skelAnime, &gJabuJabuAnim); + this->unk_30A = 0; + this->eyeIndex = 0; + this->blinkTimer = 0; + this->extraBlinkCounter = 0; + this->extraBlinkTotal = 0; + + if (gSaveContext.eventChkInf[3] & 0x400) { // Fish given + EnJj_SetupAction(this, EnJj_WaitToOpenMouth); + } else { + EnJj_SetupAction(this, EnJj_WaitForFish); + } + + this->bodyCollisionActor = (DynaPolyActor*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_JJ, this->dyna.actor.world.pos.x - 10.0f, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, this->dyna.actor.world.rot.y, 0, + JABUJABU_COLLISION); + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&gJabuJabuHeadCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; + break; + + case JABUJABU_COLLISION: + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&gJabuJabuBodyCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + func_8003ECA8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dyna.actor.update = EnJj_UpdateStaticCollision; + this->dyna.actor.draw = NULL; + Actor_SetScale(&this->dyna.actor, 0.087f); + break; + + case JABUJABU_UNUSED_COLLISION: + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&gJabuJabuUnusedCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->dyna.actor.update = EnJj_UpdateStaticCollision; + this->dyna.actor.draw = NULL; + Actor_SetScale(&this->dyna.actor, 0.087f); + break; + } +} + +void EnJj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnJj* this = (EnJj*)thisx; + + switch (this->dyna.actor.params) { + case JABUJABU_MAIN: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + Collider_DestroyCylinder(globalCtx, &this->collider); + break; + + case JABUJABU_COLLISION: + case JABUJABU_UNUSED_COLLISION: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + break; + } +} + +/** + * Blink routine. Blinks at the end of each randomised blinkTimer cycle. If extraBlinkCounter is not zero, blink that + * many more times before resuming random blinkTimer cycles. extraBlinkTotal can be set to a positive number to blink + * that many extra times at the end of every blinkTimer cycle, but the actor always sets it to zero, so only one + * multiblink happens when extraBlinkCounter is nonzero. + */ +void EnJj_Blink(EnJj* this) { + if (this->blinkTimer > 0) { + this->blinkTimer--; + } else { + this->eyeIndex++; + if (this->eyeIndex >= JABUJABU_EYE_MAX) { + this->eyeIndex = JABUJABU_EYE_OPEN; + if (this->extraBlinkCounter > 0) { + this->extraBlinkCounter--; + } else { + this->blinkTimer = Rand_S16Offset(20, 20); + this->extraBlinkCounter = this->extraBlinkTotal; + } + } + } +} + +void EnJj_OpenMouth(EnJj* this, GlobalContext* globalCtx) { + DynaPolyActor* bodyCollisionActor = this->bodyCollisionActor; + + if (this->mouthOpenAngle >= -5200) { + this->mouthOpenAngle -= 102; + + if (this->mouthOpenAngle < -2600) { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, bodyCollisionActor->bgId); + } + } +} + +void EnJj_WaitToOpenMouth(EnJj* this, GlobalContext* globalCtx) { + if (this->dyna.actor.xzDistToPlayer < 300.0f) { + EnJj_SetupAction(this, EnJj_OpenMouth); + } +} + +void EnJj_WaitForFish(EnJj* this, GlobalContext* globalCtx) { + static Vec3f feedingSpot = { -1589.0f, 53.0f, -43.0f }; + Player* player = GET_PLAYER(globalCtx); + + if ((Math_Vec3f_DistXZ(&feedingSpot, &player->actor.world.pos) < 300.0f) && + globalCtx->isPlayerDroppingFish(globalCtx)) { + this->cutsceneCountdownTimer = 100; + EnJj_SetupAction(this, EnJj_BeginCutscene); + } + + this->collider.dim.pos.x = -1245; + this->collider.dim.pos.y = 20; + this->collider.dim.pos.z = -48; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnJj_BeginCutscene(EnJj* this, GlobalContext* globalCtx) { + DynaPolyActor* bodyCollisionActor = this->bodyCollisionActor; + + if (this->cutsceneCountdownTimer > 0) { + this->cutsceneCountdownTimer--; + } else { + EnJj_SetupAction(this, EnJj_RemoveDust); + globalCtx->csCtx.segment = &D_80A88164; + gSaveContext.cutsceneTrigger = 1; + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, bodyCollisionActor->bgId); + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + gSaveContext.eventChkInf[3] |= 0x400; + func_80078884(NA_SE_SY_CORRECT_CHIME); + } +} + +void EnJj_CutsceneUpdate(EnJj* this, GlobalContext* globalCtx) { + switch (globalCtx->csCtx.npcActions[2]->action) { + case 1: + if (this->unk_30A & 2) { + this->eyeIndex = 0; + this->blinkTimer = Rand_S16Offset(20, 20); + this->extraBlinkCounter = 0; + this->extraBlinkTotal = 0; + this->unk_30A ^= 2; + } + break; + + case 2: + this->unk_30A |= 1; + + if (!(this->unk_30A & 8)) { + this->dust = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EFF_DUST, + -1100.0f, 105.0f, -27.0f, 0, 0, 0, EFF_DUST_TYPE_0); + this->unk_30A |= 8; + } + break; + + case 3: + if (!(this->unk_30A & 2)) { + this->eyeIndex = 0; + this->blinkTimer = 0; + this->extraBlinkCounter = 1; + this->extraBlinkTotal = 0; + this->unk_30A |= 2; + } + break; + } + + if (this->unk_30A & 1) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_JABJAB_BREATHE - SFX_FLAG); + + if (this->mouthOpenAngle >= -5200) { + this->mouthOpenAngle -= 102; + } + } +} + +void EnJj_RemoveDust(EnJj* this, GlobalContext* globalCtx) { + Actor* dust; + + if (!(this->unk_30A & 4)) { + this->unk_30A |= 4; + dust = this->dust; + + if (dust != NULL) { + Actor_Kill(dust); + this->dyna.actor.child = NULL; + } + } +} + +void EnJj_UpdateStaticCollision(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnJj_Update(Actor* thisx, GlobalContext* globalCtx) { + EnJj* this = (EnJj*)thisx; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[2] != NULL)) { + EnJj_CutsceneUpdate(this, globalCtx); + } else { + this->actionFunc(this, globalCtx); + + if (this->skelAnime.curFrame == 41.0f) { + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_JABJAB_GROAN); + } + } + + EnJj_Blink(this); + SkelAnime_Update(&this->skelAnime); + Actor_SetScale(&this->dyna.actor, 0.087f); + + // Head + this->skelAnime.jointTable[10].z = this->mouthOpenAngle; +} + +void EnJj_Draw(Actor* thisx, GlobalContext* globalCtx2) { + static void* eyeTextures[] = { gJabuJabuEyeOpenTex, gJabuJabuEyeHalfTex, gJabuJabuEyeClosedTex }; + GlobalContext* globalCtx = globalCtx2; + EnJj* this = (EnJj*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_jj.c", 879); + + func_800943C8(globalCtx->state.gfxCtx); + Matrix_Translate(0.0f, (cosf(this->skelAnime.curFrame * (M_PI / 41.0f)) * 10.0f) - 10.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(10.0f, 10.0f, 10.0f, MTXMODE_APPLY); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_jj.c", 898); +} diff --git a/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.h b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.h new file mode 100644 index 000000000..3733cc976 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj.h @@ -0,0 +1,36 @@ +#ifndef Z_EN_JJ_H +#define Z_EN_JJ_H + +#include "ultra64.h" +#include "global.h" + +struct EnJj; + +typedef void (*EnJjActionFunc)(struct EnJj*, GlobalContext*); + +typedef struct EnJj { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ SkelAnime skelAnime; + /* 0x01A8 */ Vec3s jointTable[22]; + /* 0x022C */ Vec3s morphTable[22]; + /* 0x02B0 */ ColliderCylinder collider; + /* 0x02FC */ EnJjActionFunc actionFunc; + /* 0x0300 */ DynaPolyActor* bodyCollisionActor; + /* 0x0304 */ Actor* dust; + /* 0x0308 */ s16 mouthOpenAngle; + /* 0x030A */ u16 unk_30A; + /* 0x030C */ s16 cutsceneCountdownTimer; + /* 0x030E */ u8 eyeIndex; + /* 0x030F */ u8 blinkTimer; + /* 0x0310 */ u8 extraBlinkCounter; + /* 0x0311 */ u8 extraBlinkTotal; +} EnJj; // size = 0x0314 + +typedef enum { + /* -1 */ JABUJABU_MAIN = -1, // Head, drawn body, handles updating + /* 0 */ JABUJABU_COLLISION, // Static collision for body + /* 1 */ JABUJABU_UNUSED_COLLISION // Shaped like a screen +} EnJjType; + + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Jj/z_en_jj_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj_cutscene_data.c new file mode 100644 index 000000000..9399075ab --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Jj/z_en_jj_cutscene_data.c @@ -0,0 +1,158 @@ +#include "z_en_jj.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_80A88164[] = { + CS_BEGIN_CUTSCENE(26, 1629), + CS_PLAYER_ACTION_LIST(4), + CS_PLAYER_ACTION(0x0005, 0, 240, 0x0000, 0x4000, 0x0000, -1732, 52, -44, -1732, 52, -44, 1.1393037e-29f, 0.0f, 1.4e-45f), + CS_PLAYER_ACTION(0x0003, 240, 255, 0x0000, 0x4000, 0x0000, -1732, 52, -44, -1732, 52, -44, 1.1393037e-29f, 0.0f, 1.4e-45f), + CS_PLAYER_ACTION(0x0006, 255, 285, 0x0000, 0x4000, 0x0000, -1732, 52, -44, -1732, 52, -44, 1.1393037e-29f, 0.0f, 1.4e-45f), + CS_PLAYER_ACTION(0x0020, 285, 300, 0x0000, 0xC000, 0x0000, -1732, 52, -44, -1537, 109, -44, 1.1393037e-29f, 0.0f, 1.4e-45f), + CS_NPC_ACTION_LIST(68, 4), + CS_NPC_ACTION(0x0001, 0, 234, 0x0000, 0x4000, 0x0000, -1665, 52, -44, -1665, 52, -44, 1.1393037e-29f, 0.0f, 1.4e-45f), + CS_NPC_ACTION(0x0002, 234, 241, 0x41F8, 0x0000, 0x0000, -1665, 52, -44, -1603, 130, -47, 8.857142f, 11.142858f, -8.857142f), + CS_NPC_ACTION(0x0002, 241, 280, 0x4031, 0x0000, 0x0000, -1603, 130, -47, -549, 130, -52, 27.02564f, 0.0f, -27.02564f), + CS_NPC_ACTION(0x0003, 280, 300, 0x0000, 0x0000, 0x0000, -549, 130, -52, -549, 130, -52, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(67, 5), + CS_NPC_ACTION(0x0001, 0, 93, 0x0000, 0x0000, 0x0000, 0, 51, 124, 0, 51, 124, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 93, 121, 0x0000, 0x0000, 0x0000, 0, 51, 124, 0, 51, 124, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0001, 121, 146, 0x0000, 0x0000, 0x0000, 0, 51, 124, 0, 51, 124, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 146, 241, 0x0000, 0x0000, 0x0000, 0, 51, 124, 0, 51, 124, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0001, 241, 441, 0x0000, 0x0000, 0x0000, 0, 51, 124, 0, 51, 124, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(69, 3), + CS_NPC_ACTION(0x0001, 0, 90, 0x0000, 0x0000, 0x0000, 0, -33, 9, 0, -33, 9, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 90, 330, 0x0000, 0x0000, 0x0000, 0, -33, 9, 0, -62, 22, 0.0f, -0.12083333f, 0.0f), + CS_NPC_ACTION(0x0003, 330, 380, 0x0000, 0x0000, 0x0000, 0, -62, 22, 0, -62, 22, 0.0f, 0.0f, 0.0f), + CS_MISC_LIST(1), + CS_MISC(0x000C, 1095, 1161, 0x0000, 0x00000000, 0xFFFFFFD2, 0x00000000, 0xFFFFFFD0, 0xFFFFFFD2, 0x00000000, 0xFFFFFFD0, 0x00000000, 0x00000000, 0x00000000), + CS_SCENE_TRANS_FX(0x0009, 0, 10), + CS_PLAYER_ACTION_LIST(1), + CS_PLAYER_ACTION(0x0035, 300, 1629, 0x0000, 0x0000, 0x0000, -1630, 52, -52, -1630, 52, -52, 0.0f, 0.0f, 0.0f), + CS_CAM_EYE_LIST(0, 1091), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x015C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x016D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x017E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x0223), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, -1641, 95, -41, 0x7065), + CS_CAM_EYE_LIST(60, 1151), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1532, 251, 222, 0x015C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1532, 251, 222, 0x016D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1532, 251, 222, 0x017E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1532, 251, 222, 0x0223), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, -1532, 251, 222, 0x7065), + CS_CAM_EYE_LIST(90, 351), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1698, 382, 455, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1698, 382, 455, 0xAC34), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1698, 382, 455, 0x4428), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1694, 380, 451, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 32.999897f, -1694, 380, 451, 0xAC10), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 32.999897f, -1694, 380, 451, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 32.999897f, -1694, 380, 451, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 32.999897f, -1694, 380, 451, 0x0164), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 32.999897f, -1694, 380, 451, 0xAD78), + CS_CAM_EYE_LIST(220, 392), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0xAC34), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x4428), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0xAC10), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1641, 95, -41, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, -1641, 95, -41, 0x0000), + CS_CAM_EYE_LIST(240, 1331), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1810, 65, -15, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1810, 65, -15, 0xAC34), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1810, 65, -15, 0x4428), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1810, 65, -15, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.599945f, -1810, 65, -15, 0xAC10), + CS_CAM_EYE_LIST(280, 1371), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1531, 95, -7, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1531, 95, -7, 0xAC34), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1531, 95, -7, 0x4428), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, -1531, 95, -7, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.599945f, -1531, 95, -7, 0xAC10), + CS_CAM_EYE_LIST(310, 1421), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1717, 83, -59, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1717, 83, -59, 0xAC34), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1649, 177, -59, 0x4428), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1533, 224, -59, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -1243, 180, -59, 0xAC10), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -953, 71, -55, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -953, 71, -55, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, -953, 71, -55, 0x0164), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, -953, 71, -55, 0xAD78), + CS_CAM_EYE_LIST(355, 1466), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1830, 103, 18, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1830, 103, 18, 0xAC34), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1830, 103, 18, 0x4428), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1830, 103, 18, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1830, 103, 18, 0xAC10), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1830, 103, 18, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.600002f, -1830, 103, 18, 0x0000), + CS_CAM_AT_LIST(0, 1120), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1724, -5, -45, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1724, -5, -45, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.399944f, -1724, -5, -45, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1724, -5, -45, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, -1724, -5, -45, 0xAC10), + CS_CAM_AT_LIST(60, 1180), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1440, 241, 134, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1440, 241, 134, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.399944f, -1440, 241, 134, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1440, 241, 134, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, -1440, 241, 134, 0xAC10), + CS_CAM_AT_LIST(90, 380), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1610, 348, 373, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1610, 348, 373, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 45.399944f, -1610, 348, 373, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 35.399906f, -1614, 338, 367, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 32.999897f, -1614, 338, 367, 0xAC10), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 32.999897f, -1614, 338, 367, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 32.999897f, -1614, 338, 367, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 32.999897f, -1614, 338, 367, 0x0164), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 32.999897f, -1614, 338, 367, 0xAD78), + CS_CAM_AT_LIST(220, 421), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1724, -5, -45, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 70, 45.399944f, -1724, -5, -45, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 5, 45.399944f, -1724, -5, -45, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 6, 45.799946f, -1593, 150, -146, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1531, 152, -75, 0xAC10), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -1531, 152, -75, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, -1531, 152, -75, 0x0000), + CS_CAM_AT_LIST(240, 1360), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -1712, 74, -37, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -1712, 74, -37, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.599945f, -1712, 74, -37, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -1712, 74, -37, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.599945f, -1712, 74, -37, 0xAC10), + CS_CAM_AT_LIST(280, 1400), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -1619, 99, -50, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -1619, 99, -50, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.599945f, -1619, 99, -50, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, -1619, 99, -50, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.599945f, -1619, 99, -50, 0xAC10), + CS_CAM_AT_LIST(310, 1450), + CS_CAM_AT(CS_CMD_CONTINUE, 0x0B, 30, 90.9996f, -1610, 141, -59, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x09, 10, 90.79961f, -1599, 114, -57, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0xFC, 10, 90.39961f, -1528, 192, -54, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 90.59961f, -1427, 164, -54, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0xCB, 10, 90.39961f, -1138, 119, -37, 0xAC10), + CS_CAM_AT(CS_CMD_CONTINUE, 0x20, 10, 90.39961f, -832, 50, -51, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.399944f, -836, 35, -51, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, -836, 35, -51, 0x0164), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, -836, 35, -51, 0xAD78), + CS_CAM_AT_LIST(355, 1495), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1706, 111, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1706, 111, -6, 0xAC34), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.600002f, -1706, 111, -6, 0x4428), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.600002f, -1721, 82, -42, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 60.600002f, -1721, 82, -42, 0xAC10), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1721, 82, -42, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.600002f, -1721, 82, -42, 0x0000), + CS_SCENE_TRANS_FX(0x000B, 335, 342), + CS_TERMINATOR(JABU_JABU_INTRO, 345, 395), + CS_NPC_ACTION_LIST(62, 1), + CS_NPC_ACTION(0x0001, 305, 494, 0x0000, 0x0000, 0x0000, -1399, 452, -53, -1399, 452, -53, 0.0f, 0.0f, 0.0f), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_En_Js/z_en_js.c b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c new file mode 100644 index 000000000..e2f093769 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Js/z_en_js.c @@ -0,0 +1,232 @@ +/* + * File: z_en_js.c + * Overlay: ovl_En_Js + * Description: Magic Carpet Salesman + */ + +#include "z_en_js.h" +#include "objects/object_js/object_js.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnJs_Init(Actor* thisx, GlobalContext* globalCtx); +void EnJs_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnJs_Update(Actor* thisx, GlobalContext* globalCtx); +void EnJs_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A89304(EnJs* this, GlobalContext* globalCtx); + +const ActorInit En_Js_InitVars = { + ACTOR_EN_JS, + ACTORCAT_NPC, + FLAGS, + OBJECT_JS, + sizeof(EnJs), + (ActorFunc)EnJs_Init, + (ActorFunc)EnJs_Destroy, + (ActorFunc)EnJs_Update, + (ActorFunc)EnJs_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +void En_Js_SetupAction(EnJs* this, EnJsActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnJs_Init(Actor* thisx, GlobalContext* globalCtx) { + EnJs* this = (EnJs*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCarpetMerchantSkel, &gCarpetMerchantSlappingKneeAnim, + this->jointTable, this->morphTable, 13); + Animation_PlayOnce(&this->skelAnime, &gCarpetMerchantSlappingKneeAnim); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + En_Js_SetupAction(this, func_80A89304); + this->unk_284 = 0; + this->actor.gravity = -1.0f; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_JSJUTAN, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); +} + +void EnJs_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnJs* this = (EnJs*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +u8 func_80A88F64(EnJs* this, GlobalContext* globalCtx, u16 textId) { + s16 yawDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + return 1; + } else { + this->actor.textId = textId; + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(yawDiff) <= 0x1800 && this->actor.xzDistToPlayer < 100.0f) { + this->unk_284 |= 1; + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + return 0; + } +} + +void func_80A89008(EnJs* this) { + En_Js_SetupAction(this, func_80A89304); + Animation_Change(&this->skelAnime, &gCarpetMerchantSlappingKneeAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gCarpetMerchantSlappingKneeAnim), ANIMMODE_ONCE, -4.0f); +} + +void func_80A89078(EnJs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80A89008(this); + this->actor.flags &= ~ACTOR_FLAG_16; + } +} + +void func_80A890C0(EnJs* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + En_Js_SetupAction(this, func_80A89078); + } else { + func_8002F2CC(&this->actor, globalCtx, 1000.0f); + } +} + +void func_80A8910C(EnJs* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actor.textId = 0x6078; + En_Js_SetupAction(this, func_80A890C0); + this->actor.flags |= ACTOR_FLAG_16; + } +} + +void func_80A89160(EnJs* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + En_Js_SetupAction(this, func_80A8910C); + } else { + func_8002F434(&this->actor, globalCtx, GI_BOMBCHUS_10, 10000.0f, 50.0f); + } +} + +void func_80A891C4(EnJs* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + if (gSaveContext.rupees < 200) { + Message_ContinueTextbox(globalCtx, 0x6075); + func_80A89008(this); + } else { + Rupees_ChangeBy(-200); + En_Js_SetupAction(this, func_80A89160); + } + break; + case 1: // no + Message_ContinueTextbox(globalCtx, 0x6074); + func_80A89008(this); + } + } +} + +void func_80A89294(EnJs* this) { + En_Js_SetupAction(this, func_80A891C4); + Animation_Change(&this->skelAnime, &gCarpetMerchantIdleAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gCarpetMerchantIdleAnim), ANIMMODE_ONCE, -4.0f); +} + +void func_80A89304(EnJs* this, GlobalContext* globalCtx) { + if (func_80A88F64(this, globalCtx, 0x6077)) { + func_80A89294(this); + } +} + +void EnJs_Update(Actor* thisx, GlobalContext* globalCtx) { + EnJs* this = (EnJs*)thisx; + s32 pad; + s32 pad2; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + if (this->actor.bgCheckFlags & 1) { + if (SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId) == 1) { + Math_ApproachF(&this->actor.shape.yOffset, sREG(80) + -2000.0f, 1.0f, (sREG(81) / 10.0f) + 50.0f); + } + } else { + Math_ApproachZeroF(&this->actor.shape.yOffset, 1.0f, (sREG(81) / 10.0f) + 50.0f); + } + if (SkelAnime_Update(&this->skelAnime)) { + this->skelAnime.curFrame = 0.0f; + } + this->actionFunc(this, globalCtx); + if (this->unk_284 & 1) { + func_80038290(globalCtx, &this->actor, &this->unk_278, &this->unk_27E, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->unk_278.x, 0, 6, 0x1838, 0x64); + Math_SmoothStepToS(&this->unk_278.y, 0, 6, 0x1838, 0x64); + Math_SmoothStepToS(&this->unk_27E.x, 0, 6, 0x1838, 0x64); + Math_SmoothStepToS(&this->unk_27E.y, 0, 6, 0x1838, 0x64); + } + this->unk_284 &= ~0x1; + + if (DECR(this->unk_288) == 0) { + this->unk_288 = Rand_S16Offset(0x3C, 0x3C); + } + + this->unk_286 = this->unk_288; + + if (this->unk_286 >= 3) { + this->unk_286 = 0; + } +} + +s32 EnJs_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnJs* this = (EnJs*)thisx; + + if (limbIndex == 12) { + rot->y -= this->unk_278.y; + } + return false; +} + +void EnJs_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_80A896DC = { 0.0f, 0.0f, 0.0f }; + EnJs* this = (EnJs*)thisx; + + if (limbIndex == 12) { + Matrix_MultVec3f(&D_80A896DC, &this->actor.focus.pos); + } +} +void EnJs_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnJs* this = (EnJs*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnJs_OverrideLimbDraw, EnJs_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Js/z_en_js.h b/soh/src/overlays/actors/ovl_En_Js/z_en_js.h new file mode 100644 index 000000000..7653b15d3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Js/z_en_js.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_JS_H +#define Z_EN_JS_H + +#include "ultra64.h" +#include "global.h" + +struct EnJs; + +typedef void (*EnJsActionFunc)(struct EnJs*, GlobalContext*); + +typedef struct EnJs { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[13]; + /* 0x022A */ Vec3s morphTable[13]; + /* 0x0278 */ Vec3s unk_278; + /* 0x027E */ Vec3s unk_27E; + /* 0x0284 */ u16 unk_284; + /* 0x0286 */ s16 unk_286; + /* 0x0288 */ s16 unk_288; + /* 0x028C */ EnJsActionFunc actionFunc; +} EnJs; // size = 0x0290 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Jsjutan/z_en_jsjutan.c b/soh/src/overlays/actors/ovl_En_Jsjutan/z_en_jsjutan.c new file mode 100644 index 000000000..8b3d008fe --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Jsjutan/z_en_jsjutan.c @@ -0,0 +1,455 @@ +/* + * File: z_en_jsjutan.c + * Overlay: ovl_En_Jsjutan + * Description: Magic carpet man's carpet + */ + +#include "z_en_jsjutan.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnJsjutan_Init(Actor* thisx, GlobalContext* globalCtx); +void EnJsjutan_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnJsjutan_Update(Actor* thisx, GlobalContext* globalCtx); +void EnJsjutan_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Jsjutan_InitVars = { + ACTOR_EN_JSJUTAN, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnJsjutan), + (ActorFunc)EnJsjutan_Init, + (ActorFunc)EnJsjutan_Destroy, + (ActorFunc)EnJsjutan_Update, + (ActorFunc)EnJsjutan_Draw, + NULL, +}; + +// Shadow texture. 32x64 I8. +static u8 sShadowTex[0x800]; + +static Vec3s D_80A8EE10[0x90]; + +static s32 sUnused[2] = { 0, 0 }; + +#include "overlays/ovl_En_Jsjutan/ovl_En_Jsjutan.h" + +void EnJsjutan_Init(Actor* thisx, GlobalContext* globalCtx) { + EnJsjutan* this = (EnJsjutan*)thisx; + s32 pad; + CollisionHeader* header = NULL; + + this->dyna.actor.flags &= ~ACTOR_FLAG_0; + DynaPolyActor_Init(&this->dyna, DPM_UNK); + CollisionHeader_GetVirtual(&sCol, &header); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, header); + Actor_SetScale(thisx, 0.02f); + this->unk_164 = true; + this->shadowAlpha = 100.0f; +} + +void EnJsjutan_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnJsjutan* this = (EnJsjutan*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80A89860(EnJsjutan* this, GlobalContext* globalCtx) { + s16 i; + Vtx* oddVtx; + Vtx* evenVtx; + Vec3f actorPos = this->dyna.actor.world.pos; + + oddVtx = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(gShadowOddVtx)); + evenVtx = ResourceMgr_LoadVtxByName(SEGMENTED_TO_VIRTUAL(sShadowEvenVtx)); + + for (i = 0; i < ARRAY_COUNT(D_80A8EE10); i++, oddVtx++, evenVtx++) { + D_80A8EE10[i].x = oddVtx->v.ob[0]; + D_80A8EE10[i].z = oddVtx->v.ob[2]; + if (this->dyna.actor.params == ENJSJUTAN_TYPE_01) { + oddVtx->v.ob[1] = evenVtx->v.ob[1] = 0x585; + } else { + this->dyna.actor.world.pos.x = oddVtx->v.ob[0] * 0.02f + actorPos.x; + this->dyna.actor.world.pos.z = oddVtx->v.ob[2] * 0.02f + actorPos.z; + Actor_UpdateBgCheckInfo(globalCtx, &this->dyna.actor, 10.0f, 10.0f, 10.0f, 4); + oddVtx->v.ob[1] = evenVtx->v.ob[1] = this->dyna.actor.floorHeight; + this->dyna.actor.world.pos = actorPos; + } + } +} + +void func_80A89A6C(EnJsjutan* this, GlobalContext* globalCtx) { + u8 isPlayerOnTop = false; // sp127 + s16 i; + s16 j; + Vtx* carpetVtx; + Vtx* shadowVtx; + Vtx* phi_s0_2; + Vec3f sp108; + Vec3f spFC; + Actor* actorProfessor; + Actor* actorBeanGuy; + f32 dxVtx; + f32 dyVtx; + f32 dzVtx; + f32 distVtx; + // 0 if no actor in that index of diffToTracked + u8 spE0[3]; + // Tracks distance to other actors. + // Index 0 is always the Magic Carpet Man. 1 and 2 could be bombs, or EnMk and EnMs if in credits. + f32 spD4[3]; // diffToTracked X + f32 spC8[3]; // diffToTracked Y + f32 spBC[3]; // diffToTracked Z + // Tracks distance to Link + f32 spB8; // diffToPlayer X + f32 spB4; // diffToPlayer Y + f32 spB0; // diffToPlayer Z + f32 weight; + f32 spA8; // wave amplitude (?) + f32 offset; + f32 maxOffset; + f32 maxAmp; + f32 waveform; + Player* player = GET_PLAYER(globalCtx); + Actor* parent = this->dyna.actor.parent; + Actor* actorExplosive = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + u8 isInCreditsScene = false; // sp8B + + if (globalCtx->gameplayFrames % 2 != 0) { + carpetVtx = SEGMENTED_TO_VIRTUAL(sCarpetOddVtx); + shadowVtx = SEGMENTED_TO_VIRTUAL(gShadowOddVtx); + } else { + carpetVtx = SEGMENTED_TO_VIRTUAL(sCarpetEvenVtx); + shadowVtx = SEGMENTED_TO_VIRTUAL(sShadowEvenVtx); + } + + carpetVtx = ResourceMgr_LoadVtxByName(carpetVtx); + shadowVtx = ResourceMgr_LoadVtxByName(shadowVtx); + + // Distance of player to carpet. + spB8 = (player->actor.world.pos.x - this->dyna.actor.world.pos.x) * 50.0f; + spB4 = (player->actor.world.pos.y - this->unk_168) * 50.0f; + spB0 = (player->actor.world.pos.z - this->dyna.actor.world.pos.z) * 50.0f; + phi_s0_2 = carpetVtx; + + if ((fabsf(spB8) < 5500.0f) && (fabsf(spB4) < 3000.0f) && (fabsf(spB0) < 5500.0f)) { + isPlayerOnTop = true; + } + + // Distance of Magic Carpet Salesman to carpet. + spD4[0] = (parent->world.pos.x - this->dyna.actor.world.pos.x) * 50.0f; + spC8[0] = ((parent->world.pos.y - 8.0f) - this->unk_168) * 50.0f; + spBC[0] = (parent->world.pos.z - this->dyna.actor.world.pos.z) * 50.0f; + spE0[0] = 1; + + for (i = 1; i < 3; i++) { + spE0[i] = 0; + } + + i = 1; + + // Credits scene. The magic carpet man is friends with the bean guy and the lakeside professor. + if ((gSaveContext.entranceIndex == 0x157) && (gSaveContext.sceneSetupIndex == 8)) { + isInCreditsScene = true; + + actorProfessor = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + while (actorProfessor != NULL) { + if (actorProfessor->id == ACTOR_EN_MK) { + break; + } + actorProfessor = actorProfessor->next; + } + + actorBeanGuy = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + while (actorBeanGuy != NULL) { + if (actorBeanGuy->id == ACTOR_EN_MS) { + break; + } + actorBeanGuy = actorBeanGuy->next; + } + + spD4[1] = 50.0f * (actorProfessor->world.pos.x - this->dyna.actor.world.pos.x); + spC8[1] = 50.0f * (actorProfessor->world.pos.y - this->unk_168); + spBC[1] = 50.0f * (actorProfessor->world.pos.z - this->dyna.actor.world.pos.z); + spE0[1] = 1; + + spD4[2] = 50.0f * (actorBeanGuy->world.pos.x - this->dyna.actor.world.pos.x); + spC8[2] = 50.0f * (actorBeanGuy->world.pos.y - this->unk_168); + spBC[2] = 50.0f * (actorBeanGuy->world.pos.z - this->dyna.actor.world.pos.z); + spE0[2] = 1; + } else { + // Player can place bombs in carpet and it will react to it. + while (actorExplosive != NULL) { + if (i < 3) { + spD4[i] = (actorExplosive->world.pos.x - this->dyna.actor.world.pos.x) * 50.0f; + spC8[i] = (actorExplosive->world.pos.y - this->unk_168) * 50.0f; + spBC[i] = (actorExplosive->world.pos.z - this->dyna.actor.world.pos.z) * 50.0f; + + if ((fabsf(spD4[i]) < 5500.0f) && (fabsf(spC8[i]) < 3000.0f) && (fabsf(spBC[i]) < 5500.0f)) { + if (actorExplosive->params == BOMB_EXPLOSION) { + spE0[i] = 35; // Code never checks this, so it goes unused. Maybe it was planned to damage the + // carpet with explosions (?) + } else { + spE0[i] = 1; + } + } + i++; + } + actorExplosive = actorExplosive->next; + } + } + + // Fancy math to make a woobly and reactive carpet. + for (i = 0; i < ARRAY_COUNT(D_80A8EE10); i++, carpetVtx++, shadowVtx++) { + if (isPlayerOnTop) { + // Linear distance from j-th wave to player, in XZ plane. + dxVtx = carpetVtx->n.ob[0] - spB8; + dzVtx = carpetVtx->n.ob[2] - spB0; + distVtx = sqrtf(SQ(dxVtx) + SQ(dzVtx)); + + // Distance percentage. 0.0f to 1.0f. 2500.0f is the max distance to an actor that this wave will consider. + weight = (2500.0f - distVtx) / 2500.0f; + if (weight < 0.0f) { + weight = 0.0f; + } + offset = (spB4 * weight) + ((this->unk_170 - (this->unk_170 * weight)) - 200.0f); + + distVtx -= 1500.0f; + if (distVtx < 0.0f) { + distVtx = 0.0f; + } + + spA8 = 100.0f * distVtx * 0.01f; + spA8 = CLAMP_MAX(spA8, 100.0f); + + } else { + offset = this->unk_170 - 200.f; + spA8 = 100.0f; + } + + for (j = 0; j < 3; j++) { + if (spE0[j] != 0) { + dxVtx = carpetVtx->n.ob[0] - spD4[j]; + dzVtx = carpetVtx->n.ob[2] - spBC[j]; + // Linear distance from j-th wave to whatever actor is there, in XZ plane. + distVtx = sqrtf(SQ(dxVtx) + SQ(dzVtx)); + + if ((j == 0) || isInCreditsScene) { + weight = (3000.0f - distVtx) / 3000.0f; + } else { + weight = (2000.0f - distVtx) / 2000.0f; + } + if (weight < 0.0f) { + weight = 0.0f; + } + + // should be the following, but doesn't match that way. + // maxoffset = (spC8[i] * weight) + ((this->unk_170 - (this->unk_170 * weight)) - 200.0f); + maxOffset = (spC8[j] * weight); + maxOffset += ((this->unk_170 - (this->unk_170 * weight)) - 200.0f); + + distVtx -= 1500.0f; + if (distVtx < 0.0f) { + distVtx = 0.0f; + } + + maxAmp = 100.0f * distVtx * 0.01f; + maxAmp = CLAMP_MAX(maxAmp, 100.0f); + + offset = CLAMP_MAX(offset, maxOffset); + + spA8 = CLAMP_MAX(spA8, maxAmp); + } + } + + /** + * See https://en.wikipedia.org/wiki/Sine_wave#General_form + * k: 10000 + * x: j + * w: 4000 + * t: gameplayFrames + * A: spA8 + * D: phi_f28 + */ + waveform = spA8 * Math_SinS(globalCtx->gameplayFrames * 4000 + i * 10000); + + if (this->unk_174) { + s16 phi_v1_4 = offset + waveform; + s16 temp_a0_3 = (shadowVtx->n.ob[1] - this->unk_168) * 50.0f; + + if (phi_v1_4 < temp_a0_3) { + phi_v1_4 = temp_a0_3; + } + + carpetVtx->n.ob[1] = phi_v1_4; + } else { + carpetVtx->n.ob[1] = offset + waveform; + + carpetVtx->n.ob[0] = D_80A8EE10[i].x + (s16)(waveform * 0.5f); + carpetVtx->n.ob[2] = D_80A8EE10[i].z + (s16)(waveform * 0.5f); + + shadowVtx->n.ob[0] = D_80A8EE10[i].x + (s16)waveform; + shadowVtx->n.ob[2] = D_80A8EE10[i].z + (s16)waveform; + } + } + + if (!this->unk_174) { + u16 dayTime; + + this->dyna.actor.velocity.y = 0.0f; + this->dyna.actor.world.pos.y = this->unk_168; + + dayTime = gSaveContext.dayTime; + if (dayTime >= 0x8000) { + dayTime = 0xFFFF - dayTime; + } + + this->shadowAlpha = (dayTime * 0.00275f) + 10.0f; // (1.0f / 364.0f) ? + this->unk_170 = 1000.0f; + } else { + Math_ApproachF(&this->dyna.actor.world.pos.y, this->unk_168 - 1000.0f, 1.0f, this->dyna.actor.velocity.y); + Math_ApproachF(&this->dyna.actor.velocity.y, 5.0f, 1.0f, 0.5f); + Math_ApproachF(&this->shadowAlpha, 0.0f, 1.0f, 3.0f); + Math_ApproachF(&this->unk_170, -5000.0f, 1.0f, 100.0f); + } + + carpetVtx = phi_s0_2; + + sp108.x = 0.0f; + sp108.y = 0.0f; + sp108.z = 120.0f; + + // Fancy math to smooth each part of the wave considering its neighborhood. + for (i = 0; i < ARRAY_COUNT(sCarpetOddVtx); i++, carpetVtx++) { + f32 rotX; + f32 rotZ; + s32 pad; + + // Carpet size is 12x12. + if ((i % 12) == 11) { // Last column. + j = i - 1; + dzVtx = carpetVtx->n.ob[2] - phi_s0_2[j].n.ob[2]; + } else { + j = i + 1; + dzVtx = phi_s0_2[j].n.ob[2] - carpetVtx->n.ob[2]; + } + + dyVtx = phi_s0_2[j].n.ob[1] - carpetVtx->n.ob[1]; + + rotX = Math_Atan2F(dzVtx, dyVtx); + + if (i >= 132) { // Last row. + j = i - 12; + dxVtx = carpetVtx->n.ob[0] - phi_s0_2[j].n.ob[0]; + } else { + j = i + 12; + dxVtx = phi_s0_2[j].n.ob[0] - carpetVtx->n.ob[0]; + } + + rotZ = Math_Atan2F(dxVtx, dyVtx); + + Matrix_RotateX(rotX, MTXMODE_NEW); + Matrix_RotateZ(rotZ, MTXMODE_APPLY); + Matrix_MultVec3f(&sp108, &spFC); + + carpetVtx->n.n[0] = spFC.x; + carpetVtx->n.n[1] = spFC.y; + carpetVtx->n.n[2] = spFC.z; + } +} + +void EnJsjutan_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + + thisx->shape.rot.x = Math_SinS(globalCtx->gameplayFrames * 3000) * 300.0f; + thisx->shape.rot.z = Math_CosS(globalCtx->gameplayFrames * 3500) * 300.0f; +} + +extern uintptr_t jsjutanShadowTex; + +void EnJsjutan_Draw(Actor* thisx, GlobalContext* globalCtx2) { + EnJsjutan* this = (EnJsjutan*)thisx; + GlobalContext* globalCtx = globalCtx2; + s16 i; + Actor* parent = thisx->parent; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_jsjutan.c", 701); + + if (thisx->params == ENJSJUTAN_TYPE_01) { + thisx->world.pos.x = parent->world.pos.x; + thisx->world.pos.y = parent->world.pos.y; + thisx->world.pos.z = parent->world.pos.z; + this->unk_168 = thisx->world.pos.y; + if (!this->unk_175) { + this->unk_175 = true; + func_80A89860(this, globalCtx); + } + } else if (!this->unk_175) { + this->unk_175 = true; + thisx->world.pos.x = Math_SinS(parent->shape.rot.y) * 60.0f + parent->world.pos.x; + thisx->world.pos.y = (parent->world.pos.y + 5.0f) - 10.0f; + thisx->world.pos.z = Math_CosS(parent->shape.rot.y) * 60.0f + parent->world.pos.z; + this->unk_168 = thisx->world.pos.y; + func_80A89860(this, globalCtx); + } + + func_80A89A6C(this, globalCtx); + jsjutanShadowTex = sShadowTex; + + if (this->unk_164) { + this->unk_164 = false; + u8* carpTex = ResourceMgr_LoadTexByName(sCarpetTex); + u8* shadTex = sShadowTex; + for (i = 0; i < ARRAY_COUNT(shadTex); i++) { + if (((u16*)carpTex)[i] != 0) { // Hack to bypass ZAPD exporting textures as u64. + shadTex[i] = 0xFF; + } else { + shadTex[i] = 0; + } + } + } + func_80093D18(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, (s16)this->shadowAlpha); + + Matrix_Translate(thisx->world.pos.x, 3.0f, thisx->world.pos.z, MTXMODE_NEW); + Matrix_Scale(thisx->scale.x, 1.0f, thisx->scale.z, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_jsjutan.c", 782), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + // Draws the carpet's shadow texture. + gSPDisplayList(POLY_OPA_DISP++, sShadowMaterialDL); + gDPPipeSync(POLY_OPA_DISP++); + + // Draws the carpet's shadow vertices. Swaps them between frames to get a smoother result. + if (globalCtx->gameplayFrames % 2 != 0) { + gSPSegment(POLY_OPA_DISP++, 0x0C, gShadowOddVtx); + } else { + gSPSegment(POLY_OPA_DISP++, 0x0C, sShadowEvenVtx); + } + gSPDisplayList(POLY_OPA_DISP++, sModelDL); + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Translate(thisx->world.pos.x, this->unk_168 + 3.0f, thisx->world.pos.z, MTXMODE_NEW); + Matrix_Scale(thisx->scale.x, thisx->scale.y, thisx->scale.z, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_jsjutan.c", 805), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + // Draws the carpet's texture. + gSPDisplayList(POLY_OPA_DISP++, sCarpetMaterialDL); + + gDPPipeSync(POLY_OPA_DISP++); + + // Draws the carpet vertices. + if (globalCtx->gameplayFrames % 2 != 0) { + gSPSegment(POLY_OPA_DISP++, 0x0C, sCarpetOddVtx); + } else { + gSPSegment(POLY_OPA_DISP++, 0x0C, sCarpetEvenVtx); + } + gSPDisplayList(POLY_OPA_DISP++, sModelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_jsjutan.c", 823); +} diff --git a/soh/src/overlays/actors/ovl_En_Jsjutan/z_en_jsjutan.h b/soh/src/overlays/actors/ovl_En_Jsjutan/z_en_jsjutan.h new file mode 100644 index 000000000..f9d85b6d0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Jsjutan/z_en_jsjutan.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_JSJUTAN_H +#define Z_EN_JSJUTAN_H + +#include "ultra64.h" +#include "global.h" + +struct EnJsjutan; + +typedef struct EnJsjutan { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ u8 unk_164; // flag to set carpet shadow textures. + /* 0x0168 */ f32 unk_168; // actorYPos + /* 0x016C */ f32 shadowAlpha; + /* 0x0170 */ f32 unk_170; + /* 0x0174 */ u8 unk_174; // flag? + /* 0x0175 */ u8 unk_175; // setup flag? +} EnJsjutan; // size = 0x0178 + +typedef enum { + /* 0 */ ENJSJUTAN_TYPE_00, + /* 1 */ ENJSJUTAN_TYPE_01 +} EnJsjutanType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c b/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c new file mode 100644 index 000000000..ae63485e2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.c @@ -0,0 +1,350 @@ +/* + * File: z_en_kakasi.c + * Overlay: ovl_En_Kakasi + * Description: Pierre the Scarecrow + */ + +#include "z_en_kakasi.h" +#include "vt.h" +#include "objects/object_ka/object_ka.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_25) + +void EnKakasi_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A8F660(EnKakasi* this, GlobalContext* globalCtx); +void func_80A8F75C(EnKakasi* this, GlobalContext* globalCtx); +void func_80A8F8D0(EnKakasi* this, GlobalContext* globalCtx); +void func_80A8F9C8(EnKakasi* this, GlobalContext* globalCtx); +void func_80A8FBB8(EnKakasi* this, GlobalContext* globalCtx); +void func_80A8FAA4(EnKakasi* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +const ActorInit En_Kakasi_InitVars = { + ACTOR_EN_KAKASI, + ACTORCAT_NPC, + FLAGS, + OBJECT_KA, + sizeof(EnKakasi), + (ActorFunc)EnKakasi_Init, + (ActorFunc)EnKakasi_Destroy, + (ActorFunc)EnKakasi_Update, + (ActorFunc)EnKakasi_Draw, + NULL, +}; + +void EnKakasi_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi* this = (EnKakasi*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + SkelAnime_Free(&this->skelanime, globalCtx); // OTR - Fixed this memory leak + //! @bug SkelAnime_Free is not called +} + +void EnKakasi_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi* this = (EnKakasi*)thisx; + + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ Let’s DANCE! ☆☆☆☆☆ %f\n" VT_RST, this->actor.world.pos.y); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.targetMode = 6; + SkelAnime_InitFlex(globalCtx, &this->skelanime, &object_ka_Skel_0065B0, &object_ka_Anim_000214, NULL, NULL, 0); + + this->rot = this->actor.world.rot; + this->actor.flags |= ACTOR_FLAG_10; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + + Actor_SetScale(&this->actor, 0.01f); + this->actionFunc = func_80A8F660; +} + +void func_80A8F28C(EnKakasi* this) { + this->unk_1A4 = 0; + this->skelanime.playSpeed = 0.0f; + this->unk_1A8 = this->unk_1AC = 0; + + Math_ApproachZeroF(&this->skelanime.curFrame, 0.5f, 1.0f); + Math_SmoothStepToS(&this->actor.shape.rot.x, this->rot.x, 5, 0x2710, 0); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->rot.y, 5, 0x2710, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->rot.z, 5, 0x2710, 0); +} + +void func_80A8F320(EnKakasi* this, GlobalContext* globalCtx, s16 arg) { + s16 ocarinaNote = globalCtx->msgCtx.lastOcaNoteIdx; + s16 currentFrame; + + if (arg != 0) { + if (this->unk_19C[3] == 0) { + this->unk_19C[3] = (s16)Rand_ZeroFloat(10.99f) + 30; + this->unk_1A6 = (s16)Rand_ZeroFloat(4.99f); + } + + this->unk_19A = (s16)Rand_ZeroFloat(2.99f) + 5; + ocarinaNote = this->unk_1A6; + } + switch (ocarinaNote) { + case OCARINA_NOTE_A: + this->unk_19A++; + if (this->unk_1A4 == 0) { + this->unk_1A4 = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KAKASHI_ROLL); + } + break; + case OCARINA_NOTE_C_DOWN: + this->unk_19A++; + this->unk_1B8 = 1.0f; + break; + case OCARINA_NOTE_C_RIGHT: + this->unk_19A++; + if (this->unk_1AC == 0) { + this->unk_1AC = 0x1388; + } + break; + case OCARINA_NOTE_C_LEFT: + this->unk_19A++; + if (this->unk_1A8 == 0) { + this->unk_1A8 = 0x1388; + } + break; + case OCARINA_NOTE_C_UP: + this->unk_19A++; + this->unk_1B8 = 2.0f; + break; + } + + if (this->unk_19A > 8) { + this->unk_19A = 8; + } + + if (this->unk_19A != 0) { + this->actor.gravity = -1.0f; + if (this->unk_19A == 8 && (this->actor.bgCheckFlags & 1)) { + this->actor.velocity.y = 3.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_KAKASHI_JUMP); + } + Math_ApproachF(&this->skelanime.playSpeed, this->unk_1B8, 0.1f, 0.2f); + Math_SmoothStepToS(&this->actor.shape.rot.x, this->unk_1A8, 5, 0x3E8, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->unk_1AC, 5, 0x3E8, 0); + + if (this->unk_1A8 != 0 && fabsf(this->actor.shape.rot.x - this->unk_1A8) < 50.0f) { + this->unk_1A8 *= -1.0f; + } + if (this->unk_1AC != 0 && fabsf(this->actor.shape.rot.z - this->unk_1AC) < 50.0f) { + this->unk_1AC *= -1.0f; + } + + if (this->unk_1A4 != 0) { + this->actor.shape.rot.y += 0x1000; + if (this->actor.shape.rot.y == 0) { + this->unk_1A4 = 0; + } + } + currentFrame = this->skelanime.curFrame; + if (currentFrame == 11 || currentFrame == 17) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KAKASHI_SWING); + } + SkelAnime_Update(&this->skelanime); + } +} + +void func_80A8F660(EnKakasi* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&object_ka_Anim_000214); + + Animation_Change(&this->skelanime, &object_ka_Anim_000214, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + + this->actor.textId = 0x4076; + this->unk_196 = TEXT_STATE_DONE; + if (!LINK_IS_ADULT) { + this->unk_194 = false; + if (gSaveContext.scarecrowCustomSongSet) { + this->actor.textId = 0x407A; + this->unk_196 = TEXT_STATE_EVENT; + } + } else { + this->unk_194 = true; + if (gSaveContext.scarecrowCustomSongSet) { + this->actor.textId = 0x4079; + this->unk_196 = TEXT_STATE_EVENT; + } + } + this->actionFunc = func_80A8F75C; +} + +void func_80A8F75C(EnKakasi* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + func_80A8F28C(this); + SkelAnime_Update(&this->skelanime); + this->camId = SUBCAM_NONE; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->unk_196 == TEXT_STATE_EVENT) { + this->actionFunc = func_80A8F9C8; + } else { + this->actionFunc = func_80A8F660; + } + } else { + s16 yawTowardsPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (!(this->actor.xzDistToPlayer > 120.0f)) { + s16 absyawTowardsPlayer = ABS(yawTowardsPlayer); + + if (absyawTowardsPlayer < 0x4300) { + if (!this->unk_194) { + if (player->stateFlags2 & 0x1000000) { + this->camId = OnePointCutscene_Init(globalCtx, 2260, -99, &this->actor, MAIN_CAM); + + func_8010BD58(globalCtx, OCARINA_ACTION_SCARECROW_LONG_RECORDING); + this->unk_19A = 0; + this->unk_1B8 = 0.0; + player->stateFlags2 |= 0x800000; + this->actionFunc = func_80A8F8D0; + return; + } + if (this->actor.xzDistToPlayer < 80.0f) { + player->stateFlags2 |= 0x800000; + } + } + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + } +} + +void func_80A8F8D0(EnKakasi* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04 && globalCtx->msgCtx.msgMode == MSGMODE_NONE) { + // "end?" + osSyncPrintf(VT_FGCOL(BLUE) "☆☆☆☆☆ 終り? ☆☆☆☆☆ \n" VT_RST); + + if (this->unk_19A != 0) { + Message_CloseTextbox(globalCtx); + this->actor.textId = 0x4077; + this->unk_196 = TEXT_STATE_EVENT; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->actionFunc = func_80A8F9C8; + } else { + OnePointCutscene_EndCutscene(globalCtx, this->camId); + this->camId = SUBCAM_NONE; + this->actionFunc = func_80A8F660; + } + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + func_80A8F320(this, globalCtx, 0); + player->stateFlags2 |= 0x800000; + } +} + +void func_80A8F9C8(EnKakasi* this, GlobalContext* globalCtx) { + func_80A8F28C(this); + SkelAnime_Update(&this->skelanime); + func_8002DF54(globalCtx, NULL, 8); + + if (this->unk_196 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + + if (this->camId != SUBCAM_NONE) { + func_8005B1A4(globalCtx->cameraPtrs[this->camId]); + } + this->camId = OnePointCutscene_Init(globalCtx, 2270, -99, &this->actor, MAIN_CAM); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + func_8002DF54(globalCtx, NULL, 8); + func_8010BD58(globalCtx, OCARINA_ACTION_SCARECROW_LONG_PLAYBACK); + this->actionFunc = func_80A8FAA4; + } +} + +void func_80A8FAA4(EnKakasi* this, GlobalContext* globalCtx) { + if (globalCtx->msgCtx.ocarinaMode != OCARINA_MODE_0F) { + func_80A8F320(this, globalCtx, 1); + return; + } + + osSyncPrintf("game_play->message.msg_mode=%d\n", globalCtx->msgCtx.msgMode); + + if (globalCtx->msgCtx.msgMode == MSGMODE_NONE) { + if (this->unk_194) { + this->actor.textId = 0x4077; + this->unk_196 = TEXT_STATE_EVENT; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + } else { + this->actor.textId = 0x4078; + this->unk_196 = TEXT_STATE_EVENT; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + } + this->actionFunc = func_80A8FBB8; + OnePointCutscene_EndCutscene(globalCtx, this->camId); + this->camId = SUBCAM_NONE; + this->camId = OnePointCutscene_Init(globalCtx, 2260, -99, &this->actor, MAIN_CAM); + func_8005B1A4(globalCtx->cameraPtrs[this->camId]); + } +} + +void func_80A8FBB8(EnKakasi* this, GlobalContext* globalCtx) { + func_80A8F28C(this); + SkelAnime_Update(&this->skelanime); + + if (this->unk_196 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + func_8005B1A4(globalCtx->cameraPtrs[this->camId]); + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = func_80A8F660; + } +} + +void EnKakasi_Update(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi* this = (EnKakasi*)thisx; + s32 pad; + s32 i; + + this->unk_198++; + this->actor.world.rot = this->actor.shape.rot; + for (i = 0; i < ARRAY_COUNT(this->unk_19C); i++) { + if (this->unk_19C[i] != 0) { + this->unk_19C[i]--; + } + } + + this->height = 60.0f; + Actor_SetFocus(&this->actor, this->height); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 28); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnKakasi_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi* this = (EnKakasi*)thisx; + + if (BREG(3) != 0) { + osSyncPrintf("\n\n"); + // "flag!" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ フラグ! ☆☆☆☆☆ %d\n" VT_RST, gSaveContext.scarecrowCustomSongSet); + } + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, this->skelanime.dListCount, + NULL, NULL, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.h b/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.h new file mode 100644 index 000000000..aeb13ebcb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.h @@ -0,0 +1,32 @@ +#ifndef Z_EN_KAKASI_H +#define Z_EN_KAKASI_H + +#include "ultra64.h" +#include "global.h" + +struct EnKakasi; + +typedef void (*EnKakasiFunc)(struct EnKakasi*, GlobalContext*); + +typedef struct EnKakasi { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnKakasiFunc actionFunc; + /* 0x0150 */ SkelAnime skelanime; + /* 0x0194 */ u8 unk_194; + /* 0x0196 */ s16 unk_196; + /* 0x0198 */ s16 unk_198; + /* 0x019A */ s16 unk_19A; + /* 0x019C */ s16 unk_19C[4]; + /* 0x01A4 */ s16 unk_1A4; + /* 0x01A6 */ s16 unk_1A6; + /* 0x01A8 */ s16 unk_1A8; + /* 0x01AA */ char unk_1AA[0x2]; + /* 0x01AC */ s16 unk_1AC; + /* 0x01AE */ Vec3s rot; + /* 0x01B4 */ f32 height; + /* 0x01B8 */ f32 unk_1B8; + /* 0x01BC */ ColliderCylinder collider; + /* 0x0208 */ s16 camId; +} EnKakasi; // size = 0x020C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c new file mode 100644 index 000000000..3469d4d6b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.c @@ -0,0 +1,248 @@ +/* + * File: z_en_kakasi2.c + * Overlay: ovl_En_Kakasi2 + * Description: Pierre the Scarecrow Spawn + */ + +#include "z_en_kakasi2.h" +#include "vt.h" +#include "objects/object_ka/object_ka.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25 | ACTOR_FLAG_27) + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +void EnKakasi2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi2_Update(Actor* thisx, GlobalContext* globalCtx); +void func_80A90948(Actor* thisx, GlobalContext* globalCtx); + +void func_80A9062C(EnKakasi2* this, GlobalContext* globalCtx); +void func_80A90264(EnKakasi2* this, GlobalContext* globalCtx); +void func_80A904D8(EnKakasi2* this, GlobalContext* globalCtx); +void func_80A90578(EnKakasi2* this, GlobalContext* globalCtx); +void func_80A906C4(EnKakasi2* this, GlobalContext* globalCtx); + +const ActorInit En_Kakasi2_InitVars = { + ACTOR_EN_KAKASI2, + ACTORCAT_PROP, + FLAGS, + OBJECT_KA, + sizeof(EnKakasi2), + (ActorFunc)EnKakasi2_Init, + (ActorFunc)EnKakasi2_Destroy, + (ActorFunc)EnKakasi2_Update, + NULL, + NULL, +}; + +void EnKakasi2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi2* this = (EnKakasi2*)thisx; + s32 pad; + f32 spawnRangeY; + f32 spawnRangeXZ; + + osSyncPrintf("\n\n"); + // "Visit Umeda" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 梅田参号見参! ☆☆☆☆☆ \n" VT_RST); + + this->switchFlag = this->actor.params & 0x3F; + spawnRangeY = (this->actor.params >> 6) & 0xFF; + spawnRangeXZ = this->actor.world.rot.z; + if (this->switchFlag == 0x3F) { + this->switchFlag = -1; + } + this->actor.targetMode = 4; + this->maxSpawnDistance.x = (spawnRangeY * 40.0f) + 40.0f; + this->maxSpawnDistance.y = (spawnRangeXZ * 40.0f) + 40.0f; + + // "Former? (Argument 0)" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 元?(引数0) ☆☆☆☆ %f\n" VT_RST, spawnRangeY); + // "Former? (Z angle)" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 元?(Zアングル) ☆☆ %f\n" VT_RST, spawnRangeXZ); + // "Correction coordinates X" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 補正座標X ☆☆☆☆☆ %f\n" VT_RST, this->maxSpawnDistance.x); + // "Correction coordinates Y" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 補正座標Y ☆☆☆☆☆ %f\n" VT_RST, this->maxSpawnDistance.y); + // "Correction coordinates Z" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 補正座標Z ☆☆☆☆☆ %f\n" VT_RST, this->maxSpawnDistance.z); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ SAVE ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + osSyncPrintf("\n\n"); + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->height = 60.0f; + Actor_SetScale(&this->actor, 0.01f); + this->actor.flags |= ACTOR_FLAG_10; + this->unk_198 = this->actor.shape.rot.y; + + if (this->switchFlag >= 0 && Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actor.draw = func_80A90948; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ka_Skel_0065B0, &object_ka_Anim_000214, NULL, NULL, 0); + this->actionFunc = func_80A9062C; + } else { + this->actionFunc = func_80A90264; + this->actor.shape.yOffset = -8000.0f; + } +} + +void EnKakasi2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi2* this = (EnKakasi2*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + SkelAnime_Free(&this->skelAnime, globalCtx); // OTR - Fixed this memory leak + //! @bug SkelAnime_Free is not called +} + +void func_80A90264(EnKakasi2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_194++; + + if ((BREG(1) != 0) && (this->actor.xzDistToPlayer < this->maxSpawnDistance.x) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < this->maxSpawnDistance.y)) { + + this->actor.draw = func_80A90948; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ka_Skel_0065B0, &object_ka_Anim_000214, NULL, NULL, 0); + OnePointCutscene_Attention(globalCtx, &this->actor); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_27; + + func_80078884(NA_SE_SY_CORRECT_CHIME); + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ SAVE 終了 ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + this->actionFunc = func_80A904D8; + } else if ((this->actor.xzDistToPlayer < this->maxSpawnDistance.x) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < this->maxSpawnDistance.y) && + (gSaveContext.eventChkInf[9] & 0x1000)) { + + this->unk_194 = 0; + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_0B) { + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ SAVE 終了 ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + this->actor.draw = func_80A90948; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ka_Skel_0065B0, &object_ka_Anim_000214, NULL, NULL, + 0); + OnePointCutscene_Attention(globalCtx, &this->actor); + func_80078884(NA_SE_SY_CORRECT_CHIME); + + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_27; + this->actionFunc = func_80A904D8; + } + } +} + +void func_80A904D8(EnKakasi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&object_ka_Anim_000214); + + Animation_Change(&this->skelAnime, &object_ka_Anim_000214, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_COME_UP_DEKU_JR); + this->actionFunc = func_80A90578; +} + +void func_80A90578(EnKakasi2* this, GlobalContext* globalCtx) { + s16 currentFrame; + + SkelAnime_Update(&this->skelAnime); + + currentFrame = this->skelAnime.curFrame; + if (currentFrame == 11 || currentFrame == 17) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KAKASHI_SWING); + } + + this->actor.shape.rot.y += 0x800; + Math_ApproachZeroF(&this->actor.shape.yOffset, 0.5f, 500.0f); + + if (this->actor.shape.yOffset > -100.0f) { + this->actionFunc = func_80A9062C; + this->actor.shape.yOffset = 0.0f; + } +} + +void func_80A9062C(EnKakasi2* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&object_ka_Anim_000214); + + Animation_Change(&this->skelAnime, &object_ka_Anim_000214, 0.0f, 0.0f, (s16)frameCount, ANIMMODE_ONCE, -10.0f); + this->actionFunc = func_80A906C4; +} + +void func_80A906C4(EnKakasi2* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame != 0) { + Math_ApproachZeroF(&this->skelAnime.curFrame, 0.5f, 1.0f); + } + Math_SmoothStepToS(&this->actor.shape.rot.y, this->unk_198, 5, 0xBB8, 0); + SkelAnime_Update(&this->skelAnime); +} + +void EnKakasi2_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnKakasi2* this = (EnKakasi2*)thisx; + GlobalContext* globalCtx = globalCtx2; + + this->actor.world.rot = this->actor.shape.rot; + Actor_SetFocus(&this->actor, this->height); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + + if (this->actor.shape.yOffset == 0.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (BREG(0) != 0) { + if (BREG(5) != 0) { + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ this->actor.player_distance ☆☆☆☆☆ %f\n" VT_RST, + this->actor.xzDistToPlayer); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ this->hosei.x ☆☆☆☆☆ %f\n" VT_RST, this->maxSpawnDistance.x); + osSyncPrintf("\n\n"); + } + if (this->actor.draw == NULL) { + if (this->unk_194 != 0) { + if ((this->unk_194 % 2) == 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, + 1.0f, 1.0f, 1.0f, 70, 70, 70, 255, 4, globalCtx->state.gfxCtx); + } + } else { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 0, 255, 255, 255, 4, globalCtx->state.gfxCtx); + } + } + } +} + +void func_80A90948(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi2* this = (EnKakasi2*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.h b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.h new file mode 100644 index 000000000..a917e7e2e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kakasi2/z_en_kakasi2.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_KAKASI2_H +#define Z_EN_KAKASI2_H + +#include "ultra64.h" +#include "global.h" + +struct EnKakasi2; + +typedef void (*EnKakasi2ActionFunc)(struct EnKakasi2*, GlobalContext*); + +typedef struct EnKakasi2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnKakasi2ActionFunc actionFunc; + /* 0x0150 */ SkelAnime skelAnime; + /* 0x0194 */ s16 unk_194; + /* 0x0196 */ s16 switchFlag; + /* 0x0198 */ s16 unk_198; + /* 0x019C */ Vec3f maxSpawnDistance; + /* 0x01A8 */ f32 height; + /* 0x01AC */ ColliderCylinder collider; +} EnKakasi2; // size = 0x01F8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c new file mode 100644 index 000000000..6dada005c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.c @@ -0,0 +1,442 @@ +/* + * File: z_en_kakasi3.c + * Overlay: ovl_En_Kakasi3 + * Description: Bonooru the Scarecrow + */ + +#include "z_en_kakasi3.h" +#include "vt.h" +#include "objects/object_ka/object_ka.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_25) + +void EnKakasi3_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi3_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKakasi3_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A911F0(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A91284(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A91348(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A915B8(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A91620(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A91760(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A917FC(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A9187C(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A918E4(EnKakasi3* this, GlobalContext* globalCtx); +void func_80A91A90(EnKakasi3* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +const ActorInit En_Kakasi3_InitVars = { + ACTOR_EN_KAKASI3, + ACTORCAT_NPC, + FLAGS, + OBJECT_KA, + sizeof(EnKakasi3), + (ActorFunc)EnKakasi3_Init, + (ActorFunc)EnKakasi3_Destroy, + (ActorFunc)EnKakasi3_Update, + (ActorFunc)EnKakasi3_Draw, + NULL, +}; + +void EnKakasi3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi3* this = (EnKakasi3*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + SkelAnime_Free(&this->skelAnime, globalCtx); //OTR - Fixed this memory leak + //! @bug SkelAnime_Free is not called +} + +void EnKakasi3_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi3* this = (EnKakasi3*)thisx; + + osSyncPrintf("\n\n"); + // "Obonur" -- Related to the name of the scarecrow (Bonooru) + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ おーボヌール ☆☆☆☆☆ \n" VT_RST); + this->actor.targetMode = 6; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ka_Skel_0065B0, &object_ka_Anim_000214, NULL, NULL, 0); + this->actor.flags |= ACTOR_FLAG_10; + this->rot = this->actor.world.rot; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + this->actionFunc = func_80A911F0; +} + +void func_80A90E28(EnKakasi3* this) { + this->unk_1A4 = 0; + this->skelAnime.playSpeed = 0.0f; + this->unk_1AA = this->unk_1AE = 0x0; + + Math_ApproachZeroF(&this->skelAnime.curFrame, 0.5f, 1.0f); + Math_SmoothStepToS(&this->actor.shape.rot.x, this->rot.x, 5, 0x2710, 0); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->rot.y, 5, 0x2710, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->rot.z, 5, 0x2710, 0); +} + +void func_80A90EBC(EnKakasi3* this, GlobalContext* globalCtx, s32 arg) { + s16 currentFrame; + s16 ocarinaNote = globalCtx->msgCtx.lastOcaNoteIdx; + + if (arg != 0) { + if (this->unk_19C[3] == 0) { + this->unk_19C[3] = (s16)Rand_ZeroFloat(10.99f) + 30; + this->unk_1A6 = (s16)Rand_ZeroFloat(4.99f); + } + + this->unk_19A = (s16)Rand_ZeroFloat(2.99f) + 5; + ocarinaNote = this->unk_1A6; + } + switch (ocarinaNote) { + case OCARINA_NOTE_A: + this->unk_19A++; + if (this->unk_1A4 == 0) { + this->unk_1A4 = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KAKASHI_ROLL); + } + break; + case OCARINA_NOTE_C_DOWN: + this->unk_19A++; + this->unk_1B8 = 1.0f; + break; + case OCARINA_NOTE_C_RIGHT: + this->unk_19A++; + if (this->unk_1AE == 0x0) { + this->unk_1AE = 0x1388; + } + break; + case OCARINA_NOTE_C_LEFT: + this->unk_19A++; + if (this->unk_1AA == 0x0) { + this->unk_1AA = 0x1388; + } + break; + case OCARINA_NOTE_C_UP: + this->unk_19A++; + this->unk_1B8 = 2.0f; + break; + } + + if (this->unk_19A > 8) { + this->unk_19A = 8; + } + + if (this->unk_19A != 0) { + this->actor.gravity = -1.0f; + if (this->unk_19A == 8 && (this->actor.bgCheckFlags & 1)) { + this->actor.velocity.y = 3.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_KAKASHI_JUMP); + } + Math_ApproachF(&this->skelAnime.playSpeed, this->unk_1B8, 0.1f, 0.2f); + Math_SmoothStepToS(&this->actor.shape.rot.x, this->unk_1AA, 0x5, 0x3E8, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->unk_1AE, 0x5, 0x3E8, 0); + + if (this->unk_1AA != 0x0 && fabsf(this->actor.shape.rot.x - this->unk_1AA) < 50.0f) { + this->unk_1AA *= -1.0f; + } + if (this->unk_1AE != 0x0 && fabsf(this->actor.shape.rot.z - this->unk_1AE) < 50.0f) { + this->unk_1AE *= -1.0f; + } + + if (this->unk_1A4 != 0) { + this->actor.shape.rot.y += 0x1000; + if (this->actor.shape.rot.y == 0) { + this->unk_1A4 = 0; + } + } + currentFrame = this->skelAnime.curFrame; + if (currentFrame == 11 || currentFrame == 17) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_KAKASHI_SWING); + } + SkelAnime_Update(&this->skelAnime); + } +} + +void func_80A911F0(EnKakasi3* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&object_ka_Anim_000214); + + Animation_Change(&this->skelAnime, &object_ka_Anim_000214, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + this->actionFunc = func_80A91284; +} + +void func_80A91284(EnKakasi3* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + this->actor.textId = 0x40A1; + this->dialogState = TEXT_STATE_DONE; + this->unk_19A = 0; + + if (!LINK_IS_ADULT) { + this->unk_194 = false; + if (gSaveContext.scarecrowSpawnSongSet) { + this->actor.textId = 0x40A0; + this->dialogState = TEXT_STATE_EVENT; + this->unk_1A8 = 1; + } + } else { + this->unk_194 = true; + if (gSaveContext.scarecrowSpawnSongSet) { + if (this->unk_195) { + this->actor.textId = 0x40A2; + } else { + this->actor.textId = 0x40A3; + } + } + } + this->actionFunc = func_80A91348; +} + +void func_80A91348(EnKakasi3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + func_80A90E28(this); + SkelAnime_Update(&this->skelAnime); + this->camId = SUBCAM_NONE; + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (!this->unk_194) { + if (this->unk_1A8 == 0) { + this->actionFunc = func_80A91284; + } else { + this->actionFunc = func_80A91760; + } + } else { + this->actionFunc = func_80A91284; + } + } else { + s16 angleTowardsLink = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (!(this->actor.xzDistToPlayer > 120.0f)) { + s16 absAngleTowardsLink = ABS(angleTowardsLink); + + if (absAngleTowardsLink < 0x4300) { + if (!this->unk_194) { + + if (player->stateFlags2 & 0x1000000) { + this->camId = OnePointCutscene_Init(globalCtx, 2260, -99, &this->actor, MAIN_CAM); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->dialogState = TEXT_STATE_EVENT; + this->unk_1B8 = 0.0f; + Message_StartTextbox(globalCtx, 0x40A4, NULL); + player->stateFlags2 |= 0x800000; + this->actionFunc = func_80A915B8; + return; + } + if (this->actor.xzDistToPlayer < 80.0f) { + player->stateFlags2 |= 0x800000; + } + } else if (gSaveContext.scarecrowSpawnSongSet && !this->unk_195) { + + if (player->stateFlags2 & 0x1000000) { + this->camId = OnePointCutscene_Init(globalCtx, 2260, -99, &this->actor, MAIN_CAM); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->dialogState = TEXT_STATE_EVENT; + this->unk_1B8 = 0.0f; + Message_StartTextbox(globalCtx, 0x40A8, NULL); + player->stateFlags2 |= 0x800000; + this->actionFunc = func_80A9187C; + return; + } + if (this->actor.xzDistToPlayer < 80.0f) { + player->stateFlags2 |= 0x800000; + } + } + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + } +} + +void func_80A915B8(EnKakasi3* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + func_8010BD58(globalCtx, OCARINA_ACTION_SCARECROW_RECORDING); + this->actionFunc = func_80A91620; + } +} + +void func_80A91620(EnKakasi3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04 || + (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_05 && globalCtx->msgCtx.ocarinaMode < OCARINA_MODE_0B)) && + (globalCtx->msgCtx.msgMode == MSGMODE_NONE)) { + + OnePointCutscene_EndCutscene(globalCtx, this->camId); + if (globalCtx->cameraPtrs[this->camId] == NULL) { + this->camId = SUBCAM_NONE; + } + if (this->camId != SUBCAM_NONE) { + func_8005B1A4(globalCtx->cameraPtrs[this->camId]); + } + this->actionFunc = func_80A911F0; + return; + } + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03 && globalCtx->msgCtx.msgMode == MSGMODE_NONE) { + this->dialogState = TEXT_STATE_EVENT; + Message_StartTextbox(globalCtx, 0x40A5, NULL); + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_80A91A90; + return; + } + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + func_80A90EBC(this, globalCtx, 0); + player->stateFlags2 |= 0x800000; + } +} + +void func_80A91760(EnKakasi3* this, GlobalContext* globalCtx) { + + func_80A90E28(this); + SkelAnime_Update(&this->skelAnime); + if (this->dialogState == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + func_8010BD58(globalCtx, OCARINA_ACTION_SCARECROW_PLAYBACK); + this->actionFunc = func_80A917FC; + this->camId = OnePointCutscene_Init(globalCtx, 2280, -99, &this->actor, MAIN_CAM); + } +} + +void func_80A917FC(EnKakasi3* this, GlobalContext* globalCtx) { + + if (globalCtx->msgCtx.ocarinaMode != OCARINA_MODE_0F) { + func_80A90EBC(this, globalCtx, 1); + } else { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + Message_CloseTextbox(globalCtx); + OnePointCutscene_EndCutscene(globalCtx, this->camId); + this->actionFunc = func_80A911F0; + } +} + +void func_80A9187C(EnKakasi3* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_SCARECROW); + this->actionFunc = func_80A918E4; + } +} + +void func_80A918E4(EnKakasi3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (BREG(3) != 0) { + // "No way!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ まさか! ☆☆☆☆☆ %d\n" VT_RST, globalCtx->msgCtx.ocarinaMode); + } + if ((globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04 || + (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_05 && globalCtx->msgCtx.ocarinaMode < OCARINA_MODE_0B)) && + globalCtx->msgCtx.msgMode == MSGMODE_NONE) { + + Message_StartTextbox(globalCtx, 0x40A6, NULL); + this->dialogState = TEXT_STATE_EVENT; + OnePointCutscene_EndCutscene(globalCtx, this->camId); + this->camId = SUBCAM_NONE; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_80A91A90; + return; + } + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03 && globalCtx->msgCtx.msgMode == MSGMODE_NONE) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + if (BREG(3) != 0) { + osSyncPrintf("\n\n"); + // "With this, other guys are OK! That's it!" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ これで、他の奴もOK!だ! ☆☆☆☆☆ %d\n" VT_RST, + globalCtx->msgCtx.ocarinaMode); + } + this->unk_195 = true; + Message_StartTextbox(globalCtx, 0x40A7, NULL); + this->dialogState = TEXT_STATE_EVENT; + func_8002DF54(globalCtx, NULL, 8); + this->actionFunc = func_80A91A90; + return; + } + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + func_80A90EBC(this, globalCtx, 0); + player->stateFlags2 |= 0x800000; + } +} + +void func_80A91A90(EnKakasi3* this, GlobalContext* globalCtx) { + func_80A90E28(this); + SkelAnime_Update(&this->skelAnime); + func_8002DF54(globalCtx, NULL, 8); + + if (this->dialogState == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + if (this->unk_195) { + if (!(gSaveContext.eventChkInf[9] & 0x1000)) { + gSaveContext.eventChkInf[9] |= 0x1000; + } + } + if (globalCtx->cameraPtrs[this->camId] == NULL) { + this->camId = SUBCAM_NONE; + } + if (this->camId != SUBCAM_NONE) { + func_8005B1A4(globalCtx->cameraPtrs[this->camId]); + } + Message_CloseTextbox(globalCtx); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + func_8002DF54(globalCtx, NULL, 7); + this->actionFunc = func_80A911F0; + } +} + +void EnKakasi3_Update(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi3* this = (EnKakasi3*)thisx; + s32 pad; + s32 i; + + if (BREG(2) != 0) { + osSyncPrintf("\n\n"); + // "flag!" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ フラグ! ☆☆☆☆☆ %d\n" VT_RST, gSaveContext.scarecrowSpawnSongSet); + } + + this->unk_198++; + this->actor.world.rot = this->actor.shape.rot; + for (i = 0; i < ARRAY_COUNT(this->unk_19C); i++) { + if (this->unk_19C[i] != 0) { + this->unk_19C[i]--; + } + } + + Actor_SetFocus(&this->actor, 60.0f); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 28); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnKakasi3_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnKakasi3* this = (EnKakasi3*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.h b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.h new file mode 100644 index 000000000..a5c68445a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kakasi3/z_en_kakasi3.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_KAKASI3_H +#define Z_EN_KAKASI3_H + +#include "ultra64.h" +#include "global.h" + +struct EnKakasi3; + +typedef void (*EnKakasi3ActionFunc)(struct EnKakasi3*, GlobalContext*); + +typedef struct EnKakasi3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnKakasi3ActionFunc actionFunc; + /* 0x0150 */ SkelAnime skelAnime; + /* 0x0194 */ u8 unk_194; + /* 0x0195 */ u8 unk_195; + /* 0x0196 */ s16 dialogState; + /* 0x0198 */ s16 unk_198; + /* 0x019A */ s16 unk_19A; + /* 0x019C */ s16 unk_19C[4]; + /* 0x01A4 */ s16 unk_1A4; + /* 0x01A6 */ s16 unk_1A6; + /* 0x01A8 */ s16 unk_1A8; + /* 0x01AA */ s16 unk_1AA; + /* 0x01AC */ char unk_1AC[2]; + /* 0x01AE */ s16 unk_1AE; + /* 0x01B0 */ Vec3s rot; + /* 0x01B8 */ f32 unk_1B8; + /* 0x01BC */ ColliderCylinder collider; + /* 0x0208 */ s16 camId; +} EnKakasi3; // size = 0x020C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban.c b/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban.c new file mode 100644 index 000000000..4c3ab4afb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban.c @@ -0,0 +1,896 @@ +/* + * File: z_en_kanban.c + * Overlay: ovl_En_Kanban + * Description: Cuttable square sign + */ + +#include "z_en_kanban.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_kanban/object_kanban.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +#define PART_UPPER_LEFT (1 << 0) +#define PART_LEFT_UPPER (1 << 1) +#define PART_LEFT_LOWER (1 << 2) +#define PART_RIGHT_UPPER (1 << 3) +#define PART_RIGHT_LOWER (1 << 4) +#define PART_LOWER_LEFT (1 << 5) +#define PART_UPPER_RIGHT (1 << 6) +#define PART_LOWER_RIGHT (1 << 7) +#define PART_POST_UPPER (1 << 8) +#define PART_POST_LOWER (1 << 9) +#define PART_POST_STAND (1 << 10) +#define LEFT_HALF (PART_UPPER_LEFT | PART_LEFT_UPPER | PART_LEFT_LOWER | PART_LOWER_LEFT) +#define RIGHT_HALF (PART_UPPER_RIGHT | PART_RIGHT_UPPER | PART_RIGHT_LOWER | PART_LOWER_RIGHT) +#define UPPER_HALF (PART_POST_UPPER | PART_UPPER_RIGHT | PART_RIGHT_UPPER | PART_UPPER_LEFT | PART_LEFT_UPPER) +#define UPPERLEFT_HALF (PART_POST_UPPER | PART_UPPER_RIGHT | PART_LEFT_LOWER | PART_UPPER_LEFT | PART_LEFT_UPPER) +#define UPPERRIGHT_HALF (PART_POST_UPPER | PART_UPPER_RIGHT | PART_RIGHT_UPPER | PART_UPPER_LEFT | PART_RIGHT_LOWER) +#define ALL_PARTS (LEFT_HALF | RIGHT_HALF | PART_POST_UPPER | PART_POST_LOWER) + +typedef enum { + ENKANBAN_SIGN, + ENKANBAN_AIR, + ENKANBAN_UNUSED, + ENKANBAN_GROUND, + ENKANBAN_WATER, + ENKANBAN_REPAIR +} EnKanbanActionState; + +typedef enum { + PIECE_WHOLE_SIGN, + PIECE_UPPER_HALF, + PIECE_LOWER_HALF, + PIECE_RIGHT_HALF, + PIECE_LEFT_HALF, + PIECE_2ND_QUAD, + PIECE_1ST_QUAD, + PIECE_3RD_QUAD, + PIECE_4TH_QUAD, + PIECE_UPPER_LEFT, + PIECE_LEFT_UPPER, + PIECE_LEFT_LOWER, + PIECE_LOWER_LEFT, + PIECE_UPPER_RIGHT, + PIECE_RIGHT_UPPER, + PIECE_RIGHT_LOWER, + PIECE_LOWER_RIGHT, + PIECE_POST_UPPER, + PIECE_POST_LOWER, + PIECE_OTHER = 100 +} EnKanbanPiece; + +typedef enum { + CUT_POST, + CUT_VERT_L, + CUT_HORIZ, + CUT_DIAG_L, // lower left to upper right + CUT_DIAG_R, // upper left to lower right + CUT_VERT_R +} EnKanbanCutType; + +void EnKanban_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKanban_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKanban_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKanban_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Kanban_InitVars = { + ACTOR_EN_KANBAN, + ACTORCAT_PROP, + FLAGS, + OBJECT_KANBAN, + sizeof(EnKanban), + (ActorFunc)EnKanban_Init, + (ActorFunc)EnKanban_Destroy, + (ActorFunc)EnKanban_Update, + (ActorFunc)EnKanban_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, 50, 5, { 0, 0, 0 } }, +}; + +static u16 sPartFlags[] = { + PART_UPPER_LEFT, PART_LEFT_UPPER, PART_LEFT_LOWER, PART_RIGHT_UPPER, PART_RIGHT_LOWER, PART_LOWER_LEFT, + PART_UPPER_RIGHT, PART_LOWER_RIGHT, PART_POST_UPPER, PART_POST_LOWER, PART_POST_STAND, +}; + +static Vec3f sPieceOffsets[] = { + /* WHOLE_SIGN */ { 0.0f, 44.0f, 0.0f }, + /* UPPER_HALF */ { 0.0f, 50.0f, 0.0f }, + /* LOWER_HALF */ { 0.0f, 38.0f, 0.0f }, + /* RIGHT_HALF */ { 10.0f, 44.0f, 0.0f }, + /* LEFT_HALF */ { -10.0f, 44.0f, 0.0f }, + /* 2ND_QUAD */ { -10.0f, 50.0f, 0.0f }, + /* 1ST_QUAD */ { 10.0f, 50.0f, 0.0f }, + /* 3RD_QUAD */ { -10.0f, 38.0f, 0.0f }, + /* 4TH_QUAD */ { 10.0f, 38.0f, 0.0f }, + /* UPPER_LEFT */ { -7.5f, 51.0f, 0.0f }, + /* LEFT_UPPER */ { -12.5f, 48.0f, 0.0f }, + /* LEFT_LOWER */ { -12.5f, 40.0f, 0.0f }, + /* LOWER_LEFT */ { -7.5f, 37.0f, 0.0f }, + /* UPPER_RIGHT */ { 7.5f, 51.0f, 0.0f }, + /* RIGHT_UPPER */ { 12.5f, 48.0f, 0.0f }, + /* RIGHT_LOWER */ { 12.5f, 40.0f, 0.0f }, + /* LOWER_RIGHT */ { 7.5f, 37.0f, 0.0f }, + /* POST_UPPER */ { 0.0f, 50.0f, 0.0f }, + /* POST_LOWER */ { 0.0f, 38.0f, 0.0f }, +}; + +static Vec3f sPieceSizes[] = { + /* WHOLE_SIGN */ { 1500.0f, 1000.0f, 0.0f }, + /* UPPER_HALF */ { 1500.0f, 500.0f, 0.0f }, + /* LOWER_HALF */ { 1500.0f, 500.0f, 0.0f }, + /* RIGHT_HALF */ { 700.0f, 1000.0f, 0.0f }, + /* LEFT_HALF */ { 700.0f, 1000.0f, 0.0f }, + /* 2ND_QUAD */ { 700.0f, 500.0f, 0.0f }, + /* 1ST_QUAD */ { 700.0f, 500.0f, 0.0f }, + /* 3RD_QUAD */ { 700.0f, 500.0f, 0.0f }, + /* 4TH_QUAD */ { 700.0f, 500.0f, 0.0f }, + /* UPPER_LEFT */ { 700.0f, 500.0f, 0.0f }, + /* LEFT_UPPER */ { 700.0f, 500.0f, 0.0f }, + /* LEFT_LOWER */ { 700.0f, 500.0f, 0.0f }, + /* LOWER_LEFT */ { 700.0f, 500.0f, 0.0f }, + /* UPPER_RIGHT */ { 700.0f, 500.0f, 0.0f }, + /* RIGHT_UPPER */ { 700.0f, 500.0f, 0.0f }, + /* RIGHT_LOWER */ { 700.0f, 500.0f, 0.0f }, + /* LOWER_RIGHT */ { 700.0f, 500.0f, 0.0f }, + /* POST_UPPER */ { 200.0f, 500.0f, 0.0f }, + /* POST_LOWER */ { 200.0f, 500.0f, 0.0f }, +}; + +static u8 sCutTypes[] = { + /* 1H_OVER */ CUT_VERT_L, /* 2H_OVER */ CUT_VERT_L, + /* 1H_COMBO */ CUT_DIAG_R, /* 2H_COMBO */ CUT_DIAG_R, + /* 1H_LEFT */ CUT_HORIZ, /* 2H_LEFT */ CUT_HORIZ, + /* 1H_COMBO */ CUT_HORIZ, /* 2H_COMBO */ CUT_HORIZ, + /* 1H_RIGHT */ CUT_HORIZ, /* 2H_RIGHT */ CUT_HORIZ, + /* 1H_COMBO */ CUT_HORIZ, /* 2H_COMBO */ CUT_HORIZ, + /* 1H_STAB */ CUT_POST, /* 2H_STAB */ CUT_POST, + /* 1H_COMBO */ CUT_POST, /* 2H_COMBO */ CUT_POST, + /* FLIP_START */ CUT_VERT_L, /* JUMP_START */ CUT_VERT_L, + /* FLIP_END */ CUT_VERT_L, /* JUMP_END */ CUT_VERT_L, + /* BACK_LEFT */ CUT_HORIZ, /* BACK_RIGHT */ CUT_HORIZ, + /* OVER_HAMMER */ CUT_POST, /* SIDE_HAMMER */ CUT_POST, + /* 1H_SPIN_ATK */ CUT_POST, /* 2H_SPIN_ATK */ CUT_POST, + /* 1H_BIG_SPIN */ CUT_POST, /* 2H_BIG_SPIN */ CUT_POST, +}; + +static u16 sCutFlags[] = { + /* CUT_POST */ ALL_PARTS, /* CUT_VERT_L */ LEFT_HALF, + /* CUT_HORIZ */ UPPER_HALF, /* CUT_DIAG_L */ UPPERLEFT_HALF, + /* CUT_DIAG_R */ UPPERRIGHT_HALF, /* CUT_VERT_R */ RIGHT_HALF, +}; + +void EnKanban_SetFloorRot(EnKanban* this) { + if (this->actor.floorPoly != NULL) { + f32 nx = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.x); + f32 ny = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); + f32 nz = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.z); + + this->floorRot.x = -Math_FAtan2F(-nz * ny, 1.0f); + this->floorRot.z = Math_FAtan2F(-nx * ny, 1.0f); + } +} + +void EnKanban_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKanban* this = (EnKanban*)thisx; + + Actor_SetScale(&this->actor, 0.01f); + if (this->actor.params != ENKANBAN_PIECE) { + this->actor.targetMode = 0; + this->actor.flags |= ACTOR_FLAG_0; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + osSyncPrintf("KANBAN ARG %x\n", this->actor.params); + if (this->actor.params == ENKANBAN_FISHING) { + if (LINK_IS_CHILD) { + this->actor.textId = 0x409D; + } else { + this->actor.textId = 0x4090; + } + } else { + this->actor.textId = this->actor.params | 0x300; + } + this->bounceX = 1; + this->partFlags = 0xFFFF; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 50.0f, 4); + EnKanban_SetFloorRot(this); + if (LINK_IS_CHILD) { + this->actor.world.pos.y -= 15.0f; + } + } +} + +void EnKanban_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnKanban* this = (EnKanban*)thisx; + + if (this->actionState == ENKANBAN_SIGN) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnKanban_Message(EnKanban* this, GlobalContext* globalCtx) { + if (!this->msgFlag) { + if (this->msgTimer == 0) { + if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x2800) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->msgFlag = true; + } else { + func_8002F2CC(&this->actor, globalCtx, 68.0f); + } + } + } else { + this->msgTimer--; + } + } else { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->msgFlag = false; + this->msgTimer = 20; + } + } +} + +void EnKanban_Update(Actor* thisx, GlobalContext* globalCtx2) { + u8 bounced = false; + GlobalContext* globalCtx = globalCtx2; + EnKanban* this = (EnKanban*)thisx; + EnKanban* signpost; + EnKanban* piece; + Player* player = GET_PLAYER(globalCtx); + Vec3f offset; + + this->frameCount++; + switch (this->actionState) { + case ENKANBAN_SIGN: + if (this->invincibilityTimer != 0) { + this->invincibilityTimer--; + } + if (this->zTargetTimer != 0) { + this->zTargetTimer--; + } + if (this->zTargetTimer == 1) { + this->actor.flags &= ~ACTOR_FLAG_0; + } + if (this->partFlags == 0xFFFF) { + EnKanban_Message(this, globalCtx); + } + if ((this->invincibilityTimer == 0) && (this->collider.base.acFlags & AC_HIT)) { + this->collider.base.acFlags &= ~AC_HIT; + this->invincibilityTimer = 6; + piece = (EnKanban*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_KANBAN, + this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->actor.shape.rot.x, + this->actor.shape.rot.y, this->actor.shape.rot.z, ENKANBAN_PIECE); + if (piece != NULL) { + ColliderInfo* hitItem = this->collider.info.acHitInfo; + s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + u8 i; + + if (hitItem->toucher.dmgFlags & 0x700) { + this->cutType = sCutTypes[player->swordAnimation]; + } else { + this->cutType = CUT_POST; + } + if (ABS(yawDiff) > 0x4000) { + if (this->cutType == CUT_DIAG_R) { + this->cutType = CUT_DIAG_L; + } else if (this->cutType == CUT_VERT_L) { + this->cutType = CUT_VERT_R; + } + } + piece->partFlags = sCutFlags[this->cutType] & this->partFlags; + if (piece->partFlags == 0) { + Actor_Kill(&piece->actor); + return; + } + piece->partCount = 0; + for (i = 0; i < ARRAY_COUNT(sPartFlags); i++) { + if (sPartFlags[i] & piece->partFlags) { + piece->partCount++; + } + } + this->partFlags &= ~sCutFlags[this->cutType]; + if (!(this->partFlags & ALL_PARTS)) { + this->zTargetTimer = 10; + } + if ((piece->partFlags & PART_UPPER_LEFT) && (piece->partFlags & PART_LOWER_RIGHT)) { + piece->pieceType = PIECE_WHOLE_SIGN; + } else if ((piece->partFlags & PART_LEFT_UPPER) && (piece->partFlags & PART_RIGHT_UPPER)) { + piece->pieceType = PIECE_UPPER_HALF; + } else if ((piece->partFlags & PART_LEFT_LOWER) && (piece->partFlags & PART_RIGHT_LOWER)) { + piece->pieceType = PIECE_LOWER_HALF; + } else if ((piece->partFlags & PART_UPPER_RIGHT) && (piece->partFlags & PART_LOWER_RIGHT)) { + piece->pieceType = PIECE_RIGHT_HALF; + } else if ((piece->partFlags & PART_UPPER_LEFT) && (piece->partFlags & PART_LOWER_LEFT)) { + piece->pieceType = PIECE_LEFT_HALF; + } else if ((piece->partFlags & PART_UPPER_LEFT) && (piece->partFlags & PART_LEFT_UPPER)) { + piece->pieceType = PIECE_2ND_QUAD; + } else if ((piece->partFlags & PART_UPPER_RIGHT) && (piece->partFlags & PART_RIGHT_UPPER)) { + piece->pieceType = PIECE_1ST_QUAD; + } else if ((piece->partFlags & PART_LEFT_LOWER) && (piece->partFlags & PART_LOWER_LEFT)) { + piece->pieceType = PIECE_3RD_QUAD; + } else if ((piece->partFlags & PART_RIGHT_LOWER) && (piece->partFlags & PART_LOWER_RIGHT)) { + piece->pieceType = PIECE_4TH_QUAD; + } else if (piece->partFlags & PART_UPPER_LEFT) { + piece->pieceType = PIECE_UPPER_LEFT; + } else if (piece->partFlags & PART_LEFT_UPPER) { + piece->pieceType = PIECE_LEFT_UPPER; + } else if (piece->partFlags & PART_LEFT_LOWER) { + piece->pieceType = PIECE_LEFT_LOWER; + } else if (piece->partFlags & PART_LOWER_LEFT) { + piece->pieceType = PIECE_LOWER_LEFT; + } else if (piece->partFlags & PART_UPPER_RIGHT) { + piece->pieceType = PIECE_UPPER_RIGHT; + } else if (piece->partFlags & PART_RIGHT_UPPER) { + piece->pieceType = PIECE_RIGHT_UPPER; + } else if (piece->partFlags & PART_RIGHT_LOWER) { + piece->pieceType = PIECE_RIGHT_LOWER; + } else if (piece->partFlags & PART_LOWER_RIGHT) { + piece->pieceType = PIECE_LOWER_RIGHT; + } else if (piece->partFlags & PART_POST_UPPER) { + piece->pieceType = PIECE_POST_UPPER; + } else if (piece->partFlags & PART_POST_LOWER) { + piece->pieceType = PIECE_POST_LOWER; + } else { + piece->pieceType = PIECE_OTHER; + } + if (piece->pieceType == 100) { + piece->pieceType = PIECE_WHOLE_SIGN; + } + + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&sPieceOffsets[piece->pieceType], &offset); + piece->actor.world.pos.x += offset.x; + piece->actor.world.pos.y += offset.y; + piece->actor.world.pos.z += offset.z; + piece->offset.x = -sPieceOffsets[piece->pieceType].x / this->actor.scale.x; + piece->offset.y = -sPieceOffsets[piece->pieceType].y / this->actor.scale.x; + piece->offset.z = -sPieceOffsets[piece->pieceType].z / this->actor.scale.x; + piece->pieceWidth = sPieceSizes[piece->pieceType].x; + piece->pieceHeight = sPieceSizes[piece->pieceType].y; + piece->actor.gravity = -1.0f; + piece->actionState = ENKANBAN_AIR; + piece->actor.world.rot.y = (s16)Rand_CenteredFloat(0x3000) + this->actor.yawTowardsPlayer + 0x8000; + piece->actor.velocity.y = Rand_ZeroFloat(2.0f) + 3.0f; + piece->actor.speedXZ = Rand_ZeroFloat(2.0f) + 3.0f; + if (piece->partCount >= 4) { + piece->bounceX = (s16)Rand_ZeroFloat(10.0f) + 6; + piece->bounceZ = (s16)Rand_ZeroFloat(10.0f) + 6; + } else { + piece->bounceX = (s16)Rand_ZeroFloat(7.0f) + 3; + piece->bounceZ = (s16)Rand_ZeroFloat(7.0f) + 3; + } + piece->spinVel.y = Rand_CenteredFloat(0x1800); + if (Rand_ZeroOne() < 0.5f) { + piece->direction = 1; + } else { + piece->direction = -1; + } + piece->airTimer = 100; + piece->actor.flags &= ~ACTOR_FLAG_0; + piece->actor.flags |= ACTOR_FLAG_25; + this->cutMarkTimer = 5; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_STRIKE); + } + } + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 44.0f; + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actor.xzDistToPlayer > 500.0f) { + this->actor.flags |= ACTOR_FLAG_0; + this->partFlags = 0xFFFF; + } + if (this->cutMarkTimer != 0) { + if (this->cutMarkTimer >= 5) { + this->cutMarkAlpha += 255; + if (this->cutMarkAlpha > 255) { + this->cutMarkAlpha = 255; + } + } else { + this->cutMarkAlpha -= 65; + if (this->cutMarkAlpha < 0) { + this->cutMarkAlpha = 0; + } + } + this->cutMarkTimer--; + } + break; + case ENKANBAN_AIR: + case ENKANBAN_UNUSED: { + u16 tempBgFlags; + f32 tempX; + f32 tempY; + f32 tempZ; + f32 tempYDistToWater; + u8 onGround; + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 30.0f, 50.0f, 5); + + tempX = this->actor.world.pos.x; + tempY = this->actor.world.pos.y; + tempZ = this->actor.world.pos.z; + tempBgFlags = this->actor.bgCheckFlags; + tempYDistToWater = this->actor.yDistToWater; + + this->actor.world.pos.z += ((this->actor.world.pos.y - this->actor.floorHeight) * -50.0f) / 100.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 50.0f, 4); + EnKanban_SetFloorRot(this); + + this->actor.world.pos.x = tempX; + this->actor.world.pos.y = tempY; + this->actor.world.pos.z = tempZ; + this->actor.bgCheckFlags = tempBgFlags; + this->actor.yDistToWater = tempYDistToWater; + + osSyncPrintf(VT_RST); + onGround = (this->actor.bgCheckFlags & 1); + if (this->spinXFlag) { + this->spinRot.x += this->spinVel.x; + this->spinVel.x -= 0x800; + if ((this->spinRot.x <= 0) && onGround) { + this->spinRot.x = 0; + this->spinVel.x = 0; + } + } else { + this->spinRot.x -= this->spinVel.x; + this->spinVel.x -= 0x800; + if ((this->spinRot.x >= 0) && onGround) { + this->spinRot.x = 0; + this->spinVel.x = 0; + } + } + if (this->spinVel.x < -0xC00) { + this->spinVel.x = -0xC00; + } + if (this->spinZFlag) { + this->spinRot.z += this->spinVel.z; + this->spinVel.z -= 0x800; + if ((this->spinRot.z <= 0) && onGround) { + this->spinRot.z = 0; + this->spinVel.z = 0; + } + } else { + this->spinRot.z -= this->spinVel.z; + this->spinVel.z -= 0x800; + if ((this->spinRot.z >= 0) && onGround) { + this->spinRot.z = 0; + this->spinVel.z = 0; + } + } + if (this->spinVel.z < -0xC00) { + this->spinVel.z = -0xC00; + } + if (this->actor.bgCheckFlags & 8) { + this->actor.speedXZ *= -0.5f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WOODPLATE_BOUND); + } + if (this->actor.bgCheckFlags & 0x40) { + this->actionState = ENKANBAN_WATER; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BOMB_DROP_WATER); + this->bounceX = this->bounceZ = 0; + this->actor.world.pos.y += this->actor.yDistToWater; + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, NULL, NULL, 0, (this->partCount * 20) + 300); + EffectSsGRipple_Spawn(globalCtx, &this->actor.world.pos, 150, 650, 0); + EffectSsGRipple_Spawn(globalCtx, &this->actor.world.pos, 300, 800, 5); + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + osSyncPrintf(" WAT Y = %f\n", this->actor.yDistToWater); + osSyncPrintf(" POS Y = %f\n", this->actor.world.pos.y); + osSyncPrintf(" GROUND Y = %f\n", this->actor.floorHeight); + break; + } + + if (onGround) { + if (this->bounceCount <= 0) { + this->bounceCount++; + this->actor.velocity.y *= -0.3f; + this->actor.world.rot.y += (s16)Rand_CenteredFloat(16384.0f); + } else { + this->actor.velocity.y = 0.0f; + } + this->actor.speedXZ *= 0.7f; + if ((this->spinRot.x == 0) && (this->bounceX != 0)) { + this->spinVel.x = this->bounceX * 0x200; + if (this->bounceX != 0) { + this->bounceX -= 5; + if (this->bounceX <= 0) { + this->bounceX = 0; + } + } + if (Rand_ZeroOne() < 0.5f) { + this->spinXFlag = true; + } else { + this->spinXFlag = false; + } + bounced = true; + } + if ((this->spinRot.z == 0) && (this->bounceZ != 0)) { + this->spinVel.z = this->bounceZ * 0x200; + if (this->bounceZ != 0) { + this->bounceZ -= 5; + if (this->bounceZ <= 0) { + this->bounceZ = 0; + } + } + if (Rand_ZeroOne() < 0.5f) { + this->spinZFlag = true; + } else { + this->spinZFlag = false; + } + bounced = true; + } + Math_ApproachS(&this->actor.shape.rot.x, this->direction * 0x4000, 1, 0x2000); + } else { + this->actor.shape.rot.y += this->spinVel.y; + this->actor.shape.rot.x += this->direction * 0x7D0; + } + if (bounced) { + s16 dustCount; + s16 j; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel; + Vec3f pos; + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_WOODPLATE_BOUND); + accel.x = 0.0f; + accel.y = 0.1f; + accel.z = 0.0f; + pos.y = this->actor.floorHeight + 3.0f; + dustCount = this->partCount * 0.5f; + for (j = 0; j < dustCount + 3; j++) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat((this->partCount * 0.5f) + 20.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat((this->partCount * 0.5f) + 20.0f); + func_800286CC(globalCtx, &pos, &velocity, &accel, 100, 5); + } + } + if (DECR(this->airTimer) == 0) { + this->actionState = ENKANBAN_GROUND; + } + } + case ENKANBAN_GROUND: + case ENKANBAN_WATER: + signpost = (EnKanban*)this->actor.parent; + + if (signpost->partFlags == 0xFFFF) { + Actor_Kill(&this->actor); + } + Math_ApproachF(&this->actor.shape.yOffset, 100.0f, 1.0f, 5.0f); + if (this->actionState == ENKANBAN_WATER) { + s32 rippleDelay; + s32 rippleScale; + + if ((player->actor.speedXZ > 0.0f) && (player->actor.world.pos.y < this->actor.world.pos.y) && + (this->actor.xyzDistToPlayerSq < 2500.0f)) { + Math_ApproachF(&this->actor.speedXZ, player->actor.speedXZ, 1.0f, 0.2f); + if (this->actor.speedXZ > 1.0f) { + this->actor.speedXZ = 1.0f; + } + if (Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x8000, 1, 0x1000, + 0) > 0) { + this->spinVel.y = this->actor.speedXZ * 1000.0f; + } else { + this->spinVel.y = this->actor.speedXZ * -1000.0f; + } + } + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = 0.0f; + } + Actor_MoveForward(&this->actor); + if (this->actor.speedXZ != 0.0f) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 50.0f, 5); + if (this->actor.bgCheckFlags & 8) { + this->actor.speedXZ *= -0.5f; + if (this->spinVel.y > 0) { + this->spinVel.y = -0x7D0; + } else { + this->spinVel.y = 0x7D0; + } + } + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 0.15f); + } + this->actor.shape.rot.y += this->spinVel.y; + Math_ApproachS(&this->spinVel.y, 0, 1, 0x3A); + Math_ApproachS(&this->actor.shape.rot.x, this->direction * 0x4000, 2, 0x1000); + Math_ApproachS(&this->spinRot.x, Math_SinS(2500 * this->frameCount) * 500.0f, 2, 0x1000); + Math_ApproachS(&this->spinRot.z, Math_CosS(3000 * this->frameCount) * 500.0f, 2, 0x1000); + Math_ApproachZeroF(&this->floorRot.x, 0.5f, 0.2f); + Math_ApproachZeroF(&this->floorRot.z, 0.5f, 0.2f); + if (fabsf(this->actor.speedXZ) > 1.0f) { + rippleDelay = 0; + } else if (fabsf(this->actor.speedXZ) > 0.5f) { + rippleDelay = 3; + } else { + rippleDelay = 7; + } + if (!(this->frameCount & rippleDelay)) { + if (this->partCount < 3) { + rippleScale = 0; + } else if (this->partCount < 6) { + rippleScale = 100; + } else { + rippleScale = 200; + } + EffectSsGRipple_Spawn(globalCtx, &this->actor.world.pos, rippleScale, rippleScale + 500, 0); + } + } else if ((globalCtx->actorCtx.unk_02 != 0) && (this->actor.xyzDistToPlayerSq < SQ(100.0f))) { + f32 hammerStrength = (100.0f - sqrtf(this->actor.xyzDistToPlayerSq)) * 0.05f; + + this->actionState = ENKANBAN_AIR; + this->actor.gravity = -1.0f; + this->actor.world.rot.y = Rand_CenteredFloat(0x10000); + if (this->partCount >= 4) { + this->bounceX = (s16)Rand_ZeroFloat(10.0f) + 6; + this->bounceZ = (s16)Rand_ZeroFloat(10.0f) + 6; + this->actor.velocity.y = 2.0f + hammerStrength; + this->actor.speedXZ = Rand_ZeroFloat(1.0f); + } else { + this->bounceX = (s16)Rand_ZeroFloat(7.0f) + 3; + this->bounceZ = (s16)Rand_ZeroFloat(7.0f) + 3; + this->actor.velocity.y = 3.0f + hammerStrength; + this->actor.speedXZ = Rand_ZeroFloat(1.5f); + } + this->spinVel.y = Rand_CenteredFloat(0x1800); + if (Rand_ZeroOne() < 0.5f) { + this->direction = 1; + } else { + this->direction = -1; + } + this->airTimer = 70; + } + if (this->bounceX == 0) { + Actor* bomb = globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + f32 dx; + f32 dy; + f32 dz; + + while (bomb != NULL) { + if (bomb->params != 1) { + bomb = bomb->next; + continue; + } + dx = this->actor.world.pos.x - bomb->world.pos.x; + dy = this->actor.world.pos.y - bomb->world.pos.y; + dz = this->actor.world.pos.z - bomb->world.pos.z; + if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 100.0f) { + f32 bombStrength = (100.0f - sqrtf(SQ(dx) + SQ(dy) + SQ(dz))) * 0.05f; + + this->actionState = ENKANBAN_AIR; + this->actor.gravity = -1.0f; + this->actor.world.rot.y = Math_FAtan2F(dx, dz) * (0x8000 / M_PI); + if (this->partCount >= 4) { + this->bounceX = (s16)Rand_ZeroFloat(10.0f) + 6; + this->bounceZ = (s16)Rand_ZeroFloat(10.0f) + 6; + this->actor.velocity.y = 2.5f + bombStrength; + this->actor.speedXZ = 3.0f + bombStrength; + } else { + this->bounceX = (s16)Rand_ZeroFloat(7.0f) + 3; + this->bounceZ = (s16)Rand_ZeroFloat(7.0f) + 3; + this->actor.velocity.y = 5.0f + bombStrength; + this->actor.speedXZ = 4.0f + bombStrength; + } + this->spinVel.y = Rand_CenteredFloat(0x1800); + if (Rand_ZeroOne() < 0.5f) { + this->direction = 1; + } else { + this->direction = -1; + } + this->airTimer = 70; + } + bomb = bomb->next; + } + } + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("OCARINA_MODE %d\n", globalCtx->msgCtx.ocarinaMode); + osSyncPrintf(VT_RST); + switch (this->ocarinaFlag) { + case 0: + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + this->ocarinaFlag = 1; + } + break; + case 1: + if ((globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) && + (globalCtx->msgCtx.unk_E3F2 == OCARINA_SONG_LULLABY)) { + this->actionState = ENKANBAN_REPAIR; + this->bounceX = 1; + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + break; + } + break; + case ENKANBAN_REPAIR: { + f32 distX; + f32 distY; + f32 distZ; + s16 pDiff; + s16 yDiff; + s16 rDiff; + signpost = (EnKanban*)this->actor.parent; + + if (signpost->partFlags == 0xFFFF) { + Actor_Kill(&this->actor); + } + + Matrix_RotateY((signpost->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&sPieceOffsets[this->pieceType], &offset); + distX = + Math_SmoothStepToF(&this->actor.world.pos.x, signpost->actor.world.pos.x + offset.x, 1.0f, 3.0f, 0.0f); + distY = + Math_SmoothStepToF(&this->actor.world.pos.y, signpost->actor.world.pos.y + offset.y, 1.0f, 3.0f, 0.0f); + distZ = + Math_SmoothStepToF(&this->actor.world.pos.z, signpost->actor.world.pos.z + offset.z, 1.0f, 3.0f, 0.0f); + pDiff = Math_SmoothStepToS(&this->actor.shape.rot.x, signpost->actor.shape.rot.x, 1, 0x200, 0); + yDiff = Math_SmoothStepToS(&this->actor.shape.rot.y, signpost->actor.shape.rot.y, 1, 0x200, 0); + rDiff = Math_SmoothStepToS(&this->actor.shape.rot.z, signpost->actor.shape.rot.z, 1, 0x200, 0); + Math_ApproachS(&this->spinRot.x, 0, 1, 0x200); + Math_ApproachS(&this->spinRot.z, 0, 1, 0x200); + Math_ApproachZeroF(&this->floorRot.x, 1.0f, 0.05f); + Math_ApproachZeroF(&this->floorRot.z, 1.0f, 0.05f); + Math_ApproachZeroF(&this->actor.shape.yOffset, 1.0f, 2.0f); + if (((distX + distY + distZ) == 0.0f) && + ((pDiff + yDiff + rDiff + this->spinRot.x + this->spinRot.z) == 0) && (this->floorRot.x == 0.0f) && + (this->floorRot.z == 0.0f)) { + signpost->partFlags |= this->partFlags; + signpost->actor.flags |= ACTOR_FLAG_0; + Actor_Kill(&this->actor); + } + } break; + } +} + +static Gfx* sDisplayLists[] = { + object_kanban_DL_000CB0, object_kanban_DL_000DB8, object_kanban_DL_000E78, object_kanban_DL_000F38, + object_kanban_DL_000FF8, object_kanban_DL_0010B8, object_kanban_DL_0011C0, object_kanban_DL_0012C8, + object_kanban_DL_0013D0, object_kanban_DL_001488, object_kanban_DL_001540, +}; + +#include "z_en_kanban_gfx.c" + +static f32 sCutAngles[] = { + /* CUT_POST */ 0.50f * M_PI, + /* CUT_VERT_L */ 0.00f * M_PI, + /* CUT_HORIZ */ 0.50f * M_PI, + /* CUT_DIAG_L */ 0.66f * M_PI, + /* CUT_DIAG_R */ 0.34f * M_PI, + /* CUT_VERT_R */ 0.00f * M_PI, +}; + +static s32 sUnused[] = { 0, 0, 0 }; // Unused zero vector? + +#include "overlays/ovl_En_Kanban/ovl_En_Kanban.h" + +void EnKanban_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnKanban* this = (EnKanban*)thisx; + f32 zShift; + f32 zShift2; + s16 i; + u8* shadowTex = Graph_Alloc(globalCtx->state.gfxCtx, 0x400); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_kanban.c", 1659); + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, object_kanban_DL_000C30); + if (this->actionState != ENKANBAN_SIGN) { + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + Matrix_RotateX(this->floorRot.x, MTXMODE_APPLY); + Matrix_RotateZ(this->floorRot.z, MTXMODE_APPLY); + Matrix_Translate(0.0f, this->actor.shape.yOffset, 0.0f, MTXMODE_APPLY); + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((this->actor.shape.rot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + zShift = fabsf(Math_SinS(this->spinRot.x) * this->pieceHeight); + zShift2 = fabsf(Math_SinS(this->spinRot.z) * this->pieceWidth); + zShift = MAX(zShift2, zShift); + zShift *= -(f32)this->direction; + Matrix_Translate(0.0f, 0.0f, zShift, MTXMODE_APPLY); + Matrix_RotateX((this->spinRot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateY((this->spinRot.z / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_Translate(this->offset.x, this->offset.y, this->offset.z - 100.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_kanban.c", 1715), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + for (i = 0; i < ARRAY_COUNT(sPartFlags); i++) { + if (sPartFlags[i] & this->partFlags) { + gSPDisplayList(POLY_OPA_DISP++, sDisplayLists[i]); + } + } + } else { + Matrix_Translate(0.0f, 0.0f, -100.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_kanban.c", 1725), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + if (this->partFlags == 0xFFFF) { + gSPDisplayList(POLY_OPA_DISP++, gSignRectangularDL); + } else { + for (i = 0; i < ARRAY_COUNT(sPartFlags); i++) { + if (sPartFlags[i] & this->partFlags) { + gSPDisplayList(POLY_OPA_DISP++, sDisplayLists[i]); + } + } + } + if (this->cutMarkAlpha != 0) { + f32 cutOffset = (this->cutType == CUT_POST) ? -1200.0f : 0.0f; + + Matrix_Translate(0.0f, 4400.0f + cutOffset, 200.0f, MTXMODE_APPLY); + Matrix_RotateZ(sCutAngles[this->cutType], MTXMODE_APPLY); + Matrix_Scale(0.0f, 10.0f, 2.0f, MTXMODE_APPLY); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x00, 255, 255, 255, this->cutMarkAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 150, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_kanban.c", 1773), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_kanban_DL_001630); + } + } + if ((this->actor.projectedPos.z <= 400.0f) && (this->actor.projectedPos.z > 0.0f) && + (this->actor.floorHeight > -3000.0f)) { + if ((this->bounceX != 0) || (this->bounceZ != 0)) { + u16 dayTime = gSaveContext.dayTime; + f32 shadowAlpha; + + if (dayTime >= 0x8000) { + dayTime = 0xFFFF - dayTime; + } + shadowAlpha = (dayTime * 0.00275f) + 10.0f; + if (this->actor.projectedPos.z > 300.0f) { + shadowAlpha *= ((400.0f - this->actor.projectedPos.z) * 0.01f); + } + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x00, 0, 0, 0, (s8)shadowAlpha); + + if ((this->actionState == ENKANBAN_SIGN) && LINK_IS_CHILD) { + zShift = 0.0f; + } else { + zShift = ((this->actor.world.pos.y - this->actor.floorHeight) * -50.0f) / 100.0f; + } + + Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z + zShift, + MTXMODE_NEW); + Matrix_RotateX(this->floorRot.x, MTXMODE_APPLY); + Matrix_RotateZ(this->floorRot.z, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, 0.0f, this->actor.scale.z, MTXMODE_APPLY); + if (this->actionState == ENKANBAN_SIGN) { + Matrix_RotateX(-M_PI / 5, MTXMODE_APPLY); + } + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((this->actor.shape.rot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((this->spinRot.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateY((this->spinRot.z / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_Translate(this->offset.x, this->offset.y, this->offset.z, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_kanban.c", 1833), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + for (i = 0; i < 0x400; i++) { + if (sShadowTexFlags[i] & this->partFlags) { + shadowTex[i] = 0xFF; + } else { + shadowTex[i] = 0; + } + } + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(shadowTex)); + gSPDisplayList(POLY_XLU_DISP++, sShadowDL); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_kanban.c", 1857); +} diff --git a/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban.h b/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban.h new file mode 100644 index 000000000..15e878175 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban.h @@ -0,0 +1,44 @@ +#ifndef Z_EN_KANBAN_H +#define Z_EN_KANBAN_H + +#include "ultra64.h" +#include "global.h" + +struct EnKanban; + +typedef struct EnKanban { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[4]; + /* 0x0150 */ u8 frameCount; + /* 0x0152 */ s16 airTimer; + /* 0x0154 */ u8 actionState; + /* 0x0156 */ u16 partFlags; + /* 0x0158 */ u8 partCount; + /* 0x015A */ s16 invincibilityTimer; + /* 0x015C */ Vec3f offset; + /* 0x0168 */ Vec3s spinRot; + /* 0x016E */ Vec3s spinVel; + /* 0x0174 */ s8 spinXFlag; + /* 0x0175 */ s8 spinZFlag; + /* 0x0176 */ s16 bounceX; + /* 0x0178 */ s16 bounceZ; + /* 0x017A */ u8 bounceCount; + /* 0x017C */ f32 pieceWidth; + /* 0x0180 */ f32 pieceHeight; + /* 0x0184 */ s16 direction; + /* 0x0188 */ Vec3f floorRot; + /* 0x0194 */ u8 cutType; + /* 0x0195 */ u8 pieceType; + /* 0x0196 */ s16 cutMarkTimer; + /* 0x0198 */ s16 cutMarkAlpha; + /* 0x019A */ s16 zTargetTimer; + /* 0x019C */ u8 msgFlag; + /* 0x019D */ u8 msgTimer; + /* 0x019E */ u8 ocarinaFlag; + /* 0x01A0 */ ColliderCylinder collider; +} EnKanban; // size = 0x01EC + +#define ENKANBAN_PIECE ((s16)0xFFDD) +#define ENKANBAN_FISHING 0x300 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban_gfx.c b/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban_gfx.c new file mode 100644 index 000000000..c38e9df94 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kanban/z_en_kanban_gfx.c @@ -0,0 +1,68 @@ +#include "z_en_kanban.h" + +static u16 sShadowTexFlags[] = { + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x100, + 0x100, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x100, 0x100, + 0x100, 0x100, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x100, 0x100, + 0x100, 0x100, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x100, 0x100, + 0x100, 0x100, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x101, 0x101, + 0x140, 0x140, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, + 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x101, 0x101, + 0x140, 0x140, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x008, 0x008, 0x008, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x101, 0x101, + 0x140, 0x140, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x040, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x101, 0x101, + 0x140, 0x140, 0x040, 0x040, 0x040, 0x040, 0x040, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x001, 0x001, 0x101, 0x101, + 0x140, 0x140, 0x040, 0x040, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002, 0x102, 0x301, + 0x340, 0x108, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, 0x008, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x204, 0x220, + 0x280, 0x210, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x020, 0x020, 0x220, 0x220, + 0x280, 0x280, 0x080, 0x080, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x020, 0x020, 0x020, 0x020, 0x020, 0x220, 0x220, + 0x280, 0x280, 0x080, 0x080, 0x080, 0x080, 0x080, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x004, 0x004, 0x004, 0x004, 0x004, 0x004, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x220, 0x220, + 0x280, 0x280, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x010, 0x010, 0x010, 0x010, 0x010, 0x010, + 0x004, 0x004, 0x004, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x220, 0x220, + 0x280, 0x280, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x010, 0x010, 0x010, + 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x020, 0x220, 0x620, + 0x680, 0x280, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, 0x080, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x400, 0x400, + 0x400, 0x400, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, +}; diff --git a/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c new file mode 100644 index 000000000..ca55672cc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c @@ -0,0 +1,542 @@ +/** + * File: z_en_karebaba.c + * Overlay: ovl_En_Karebaba + * Description: Withered Deku Baba + */ + +#include "z_en_karebaba.h" +#include "objects/object_dekubaba/object_dekubaba.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnKarebaba_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKarebaba_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKarebaba_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKarebaba_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnKarebaba_SetupGrow(EnKarebaba* this); +void EnKarebaba_SetupIdle(EnKarebaba* this); +void EnKarebaba_Grow(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Idle(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Awaken(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Spin(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Dying(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_DeadItemDrop(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Retract(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Dead(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Regrow(EnKarebaba* this, GlobalContext* globalCtx); +void EnKarebaba_Upright(EnKarebaba* this, GlobalContext* globalCtx); + +const ActorInit En_Karebaba_InitVars = { + ACTOR_EN_KAREBABA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_DEKUBABA, + sizeof(EnKarebaba), + (ActorFunc)EnKarebaba_Init, + (ActorFunc)EnKarebaba_Destroy, + (ActorFunc)EnKarebaba_Update, + (ActorFunc)EnKarebaba_Draw, + NULL, +}; + +static ColliderCylinderInit sBodyColliderInit = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 7, 25, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sHeadColliderInit = { + { + COLTYPE_HARD, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_NONE, + OCELEM_ON, + }, + { 4, 25, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColCheckInfoInit = { 1, 15, 80, MASS_HEAVY }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2500, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 1, ICHAIN_CONTINUE), + ICHAIN_S8(naviEnemyId, 0x09, ICHAIN_STOP), +}; + +void EnKarebaba_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKarebaba* this = (EnKarebaba*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 22.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gDekuBabaSkel, &gDekuBabaFastChompAnim, this->jointTable, + this->morphTable, 8); + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, &this->actor, &sBodyColliderInit); + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + Collider_InitCylinder(globalCtx, &this->headCollider); + Collider_SetCylinder(globalCtx, &this->headCollider, &this->actor, &sHeadColliderInit); + Collider_UpdateCylinder(&this->actor, &this->headCollider); + CollisionCheck_SetInfo(&this->actor.colChkInfo, DamageTable_Get(1), &sColCheckInfoInit); + + this->boundFloor = NULL; + + if (this->actor.params == 0) { + EnKarebaba_SetupGrow(this); + } else { + EnKarebaba_SetupIdle(this); + } +} + +void EnKarebaba_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnKarebaba* this = (EnKarebaba*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyCylinder(globalCtx, &this->headCollider); +} + +void EnKarebaba_ResetCollider(EnKarebaba* this) { + this->bodyCollider.dim.radius = 7; + this->bodyCollider.dim.height = 25; + this->bodyCollider.base.colType = COLTYPE_HARD; + this->bodyCollider.base.acFlags |= AC_HARD; + this->bodyCollider.info.bumper.dmgFlags = ~0x00300000; + this->headCollider.dim.height = 25; +} + +void EnKarebaba_SetupGrow(EnKarebaba* this) { + Actor_SetScale(&this->actor, 0.0f); + this->actor.shape.rot.x = -0x4000; + this->actionFunc = EnKarebaba_Grow; + this->actor.world.pos.y = this->actor.home.pos.y + 14.0f; +} + +void EnKarebaba_SetupIdle(EnKarebaba* this) { + Actor_SetScale(&this->actor, 0.005f); + this->actor.shape.rot.x = -0x4000; + this->actionFunc = EnKarebaba_Idle; + this->actor.world.pos.y = this->actor.home.pos.y + 14.0f; +} + +void EnKarebaba_SetupAwaken(EnKarebaba* this) { + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, 4.0f, 0.0f, + Animation_GetLastFrame(&gDekuBabaFastChompAnim), ANIMMODE_LOOP, -3.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DUMMY482); + this->actionFunc = EnKarebaba_Awaken; +} + +void EnKarebaba_SetupUpright(EnKarebaba* this) { + if (this->actionFunc != EnKarebaba_Spin) { + Actor_SetScale(&this->actor, 0.01f); + this->bodyCollider.base.colType = COLTYPE_HIT6; + this->bodyCollider.base.acFlags &= ~AC_HARD; + this->bodyCollider.info.bumper.dmgFlags = !LINK_IS_ADULT ? 0x07C00710 : 0x0FC00710; + this->bodyCollider.dim.radius = 15; + this->bodyCollider.dim.height = 80; + this->headCollider.dim.height = 80; + } + + this->actor.params = 40; + this->actionFunc = EnKarebaba_Upright; +} + +void EnKarebaba_SetupSpin(EnKarebaba* this) { + this->actor.params = 40; + this->actionFunc = EnKarebaba_Spin; +} + +void EnKarebaba_SetupDying(EnKarebaba* this) { + this->actor.params = 0; + this->actor.gravity = -0.8f; + this->actor.velocity.y = 4.0f; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x8000; + this->actor.speedXZ = 3.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_DEAD); + this->actor.flags |= ACTOR_FLAG_4 | ACTOR_FLAG_5; + this->actionFunc = EnKarebaba_Dying; +} + +void EnKarebaba_SetupDeadItemDrop(EnKarebaba* this, GlobalContext* globalCtx) { + Actor_SetScale(&this->actor, 0.03f); + this->actor.shape.rot.x -= 0x4000; + this->actor.shape.yOffset = 1000.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.shape.shadowScale = 3.0f; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_MISC); + this->actor.params = 200; + this->actor.flags &= ~ACTOR_FLAG_5; + this->actionFunc = EnKarebaba_DeadItemDrop; +} + +void EnKarebaba_SetupRetract(EnKarebaba* this) { + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, -3.0f, Animation_GetLastFrame(&gDekuBabaFastChompAnim), + 0.0f, ANIMMODE_ONCE, -3.0f); + EnKarebaba_ResetCollider(this); + this->actionFunc = EnKarebaba_Retract; +} + +void EnKarebaba_SetupDead(EnKarebaba* this) { + Animation_Change(&this->skelAnime, &gDekuBabaFastChompAnim, 0.0f, 0.0f, 0.0f, ANIMMODE_ONCE, 0.0f); + EnKarebaba_ResetCollider(this); + this->actor.shape.rot.x = -0x4000; + this->actor.params = 200; + this->actor.parent = NULL; + this->actor.shape.shadowScale = 0.0f; + Math_Vec3f_Copy(&this->actor.world.pos, &this->actor.home.pos); + this->actionFunc = EnKarebaba_Dead; +} + +void EnKarebaba_SetupRegrow(EnKarebaba* this) { + this->actor.shape.yOffset = 0.0f; + this->actor.shape.shadowScale = 22.0f; + this->headCollider.dim.radius = sHeadColliderInit.dim.radius; + Actor_SetScale(&this->actor, 0.0f); + this->actionFunc = EnKarebaba_Regrow; +} + +void EnKarebaba_Grow(EnKarebaba* this, GlobalContext* globalCtx) { + f32 scale; + + this->actor.params++; + scale = this->actor.params * 0.05f; + Actor_SetScale(&this->actor, 0.005f * scale); + this->actor.world.pos.y = this->actor.home.pos.y + (14.0f * scale); + if (this->actor.params == 20) { + EnKarebaba_SetupIdle(this); + } +} + +void EnKarebaba_Idle(EnKarebaba* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer < 200.0f && fabsf(this->actor.yDistToPlayer) < 30.0f) { + EnKarebaba_SetupAwaken(this); + } +} + +void EnKarebaba_Awaken(EnKarebaba* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.scale.x, 0.01f, 0.0005f); + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 60.0f, 5.0f)) { + EnKarebaba_SetupUpright(this); + } + this->actor.shape.rot.y += 0x1999; + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.home.pos, 3.0f, 0, 12, 5, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); +} + +void EnKarebaba_Upright(EnKarebaba* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + + if (this->actor.params != 0) { + this->actor.params--; + } + + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_MOUTH); + } + + if (this->bodyCollider.base.acFlags & AC_HIT) { + EnKarebaba_SetupDying(this); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) > 240.0f) { + EnKarebaba_SetupRetract(this); + } else if (this->actor.params == 0) { + EnKarebaba_SetupSpin(this); + } +} + +void EnKarebaba_Spin(EnKarebaba* this, GlobalContext* globalCtx) { + s32 value; + f32 cos60; + + if (this->actor.params != 0) { + this->actor.params--; + } + + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f)) { + if (1) {} // Here for matching purposes only. + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_JR_MOUTH); + } + + value = 20 - this->actor.params; + value = 20 - ABS(value); + + if (value > 10) { + value = 10; + } + + this->headCollider.dim.radius = sHeadColliderInit.dim.radius + (value * 2); + this->actor.shape.rot.x = 0xC000 - (value * 0x100); + this->actor.shape.rot.y += value * 0x2C0; + this->actor.world.pos.y = (Math_SinS(this->actor.shape.rot.x) * -60.0f) + this->actor.home.pos.y; + + cos60 = Math_CosS(this->actor.shape.rot.x) * 60.0f; + + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y) * cos60) + this->actor.home.pos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y) * cos60) + this->actor.home.pos.z; + + if (this->bodyCollider.base.acFlags & AC_HIT) { + EnKarebaba_SetupDying(this); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else if (this->actor.params == 0) { + EnKarebaba_SetupUpright(this); + } +} + +void EnKarebaba_Dying(EnKarebaba* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 i; + Vec3f position; + Vec3f rotation; + + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.1f); + + if (this->actor.params == 0) { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x4800, 0x71C); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 3.0f, 0, 12, 5, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); + + if (this->actor.scale.x > 0.005f && ((this->actor.bgCheckFlags & 2) || (this->actor.bgCheckFlags & 8))) { + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 3.0f, 0, 12, 5, 15, HAHEN_OBJECT_DEFAULT, 10, + NULL); + } + + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + this->actor.params = 1; + } + } else if (this->actor.params == 1) { + Math_Vec3f_Copy(&position, &this->actor.world.pos); + rotation.z = Math_SinS(this->actor.shape.rot.x) * 20.0f; + rotation.x = -20.0f * Math_CosS(this->actor.shape.rot.x) * Math_SinS(this->actor.shape.rot.y); + rotation.y = -20.0f * Math_CosS(this->actor.shape.rot.x) * Math_CosS(this->actor.shape.rot.y); + + for (i = 0; i < 4; i++) { + func_800286CC(globalCtx, &position, &zeroVec, &zeroVec, 500, 50); + position.x += rotation.x; + position.y += rotation.z; + position.z += rotation.y; + } + + func_800286CC(globalCtx, &this->actor.home.pos, &zeroVec, &zeroVec, 500, 100); + EnKarebaba_SetupDeadItemDrop(this, globalCtx); + } +} + +void EnKarebaba_DeadItemDrop(EnKarebaba* this, GlobalContext* globalCtx) { + if (this->actor.params != 0) { + this->actor.params--; + } + + if (Actor_HasParent(&this->actor, globalCtx) || this->actor.params == 0) { + EnKarebaba_SetupDead(this); + } else { + func_8002F554(&this->actor, globalCtx, GI_STICKS_1); + } +} + +void EnKarebaba_Retract(EnKarebaba* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.scale.x, 0.005f, 0.0005f); + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 14.0f, 5.0f)) { + EnKarebaba_SetupIdle(this); + } + + this->actor.shape.rot.y += 0x1999; + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.home.pos, 3.0f, 0, 12, 5, 1, HAHEN_OBJECT_DEFAULT, 10, NULL); +} + +void EnKarebaba_Dead(EnKarebaba* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->actor.params != 0) { + this->actor.params--; + } + if (this->actor.params == 0) { + EnKarebaba_SetupRegrow(this); + } +} + +void EnKarebaba_Regrow(EnKarebaba* this, GlobalContext* globalCtx) { + f32 scaleFactor; + + this->actor.params++; + scaleFactor = this->actor.params * 0.05f; + Actor_SetScale(&this->actor, 0.005f * scaleFactor); + this->actor.world.pos.y = this->actor.home.pos.y + (14.0f * scaleFactor); + + if (this->actor.params == 20) { + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + EnKarebaba_SetupIdle(this); + } +} + +void EnKarebaba_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnKarebaba* this = (EnKarebaba*)thisx; + f32 height; + + this->actionFunc(this, globalCtx); + + if (this->actionFunc != EnKarebaba_Dead) { + if (this->actionFunc == EnKarebaba_Dying) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 15.0f, 10.0f, 5); + } else { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if (this->boundFloor == NULL) { + this->boundFloor = this->actor.floorPoly; + } + } + if (this->actionFunc != EnKarebaba_Dying && this->actionFunc != EnKarebaba_DeadItemDrop) { + if (this->actionFunc != EnKarebaba_Regrow && this->actionFunc != EnKarebaba_Grow) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->headCollider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->headCollider.base); + Actor_SetFocus(&this->actor, (this->actor.scale.x * 10.0f) / 0.01f); + height = this->actor.home.pos.y + 40.0f; + this->actor.focus.pos.x = this->actor.home.pos.x; + this->actor.focus.pos.y = CLAMP_MAX(this->actor.focus.pos.y, height); + this->actor.focus.pos.z = this->actor.home.pos.z; + } + } +} + +void EnKarebaba_DrawBaseShadow(EnKarebaba* this, GlobalContext* globalCtx) { + MtxF mf; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1013); + + func_80094044(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 255); + func_80038A28(this->boundFloor, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, &mf); + Matrix_Mult(&mf, MTXMODE_NEW); + Matrix_Scale(0.15f, 1.0f, 0.15f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1029), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCircleShadowDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1034); +} + +void EnKarebaba_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Color_RGBA8 black = { 0, 0, 0, 0 }; + static Gfx* stemDLists[] = { gDekuBabaStemTopDL, gDekuBabaStemMiddleDL, gDekuBabaStemBaseDL }; + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + EnKarebaba* this = (EnKarebaba*)thisx; + s32 i; + s32 stemSections; + f32 scale; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1056); + + func_80093D18(globalCtx->state.gfxCtx); + + if (this->actionFunc == EnKarebaba_DeadItemDrop) { + if (this->actor.params > 40 || (this->actor.params & 1)) { + Matrix_Translate(0.0f, 0.0f, 200.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1066), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaStickDropDL); + } + } else if (this->actionFunc != EnKarebaba_Dead) { + func_80026230(globalCtx, &black, 1, 2); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, NULL, NULL); + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + + if ((this->actionFunc == EnKarebaba_Regrow) || (this->actionFunc == EnKarebaba_Grow)) { + scale = this->actor.params * 0.0005f; + } else { + scale = 0.01f; + } + + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_RotateZYX(this->actor.shape.rot.x, this->actor.shape.rot.y, 0, MTXMODE_APPLY); + + if (this->actionFunc == EnKarebaba_Dying) { + stemSections = 2; + } else { + stemSections = 3; + } + + for (i = 0; i < stemSections; i++) { + Matrix_Translate(0.0f, 0.0f, -2000.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1116), + G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, stemDLists[i]); + + if (i == 0 && this->actionFunc == EnKarebaba_Dying) { + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); + } + } + + func_80026608(globalCtx); + } + + func_80026230(globalCtx, &black, 1, 2); + Matrix_Translate(this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, MTXMODE_NEW); + + if (this->actionFunc != EnKarebaba_Grow) { + scale = 0.01f; + } + + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_RotateY(this->actor.home.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1144), + G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaBaseLeavesDL); + + if (this->actionFunc == EnKarebaba_Dying) { + Matrix_RotateZYX(-0x4000, (s16)(this->actor.shape.rot.y - this->actor.home.rot.y), 0, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1155), + G_MTX_LOAD | G_MTX_NOPUSH | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDekuBabaStemBaseDL); + } + + func_80026608(globalCtx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_karebaba.c", 1163); + + if (this->boundFloor != NULL) { + EnKarebaba_DrawBaseShadow(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.h b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.h new file mode 100644 index 000000000..b0f5c8347 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.h @@ -0,0 +1,22 @@ +#ifndef Z_EN_KAREBABA_H +#define Z_EN_KAREBABA_H + +#include "ultra64.h" +#include "global.h" + +struct EnKarebaba; + +typedef void (*EnKarebabaActionFunc)(struct EnKarebaba*, GlobalContext*); + +typedef struct EnKarebaba { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnKarebabaActionFunc actionFunc; + /* 0x0194 */ Vec3s jointTable[8]; + /* 0x01C4 */ Vec3s morphTable[8]; + /* 0x01F4 */ CollisionPoly* boundFloor; + /* 0x01F8 */ ColliderCylinder headCollider; + /* 0x0244 */ ColliderCylinder bodyCollider; +} EnKarebaba; // size = 0x0290 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c b/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c new file mode 100644 index 000000000..533c23637 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.c @@ -0,0 +1,1362 @@ +/* + * File: z_en_ko.c + * Overlay: ovl_En_Ko + * Description: Kokiri children, and Fado + */ + +#include "z_en_ko.h" +#include "objects/object_fa/object_fa.h" +#include "objects/object_os_anime/object_os_anime.h" +#include "objects/object_km1/object_km1.h" +#include "objects/object_kw1/object_kw1.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +#define ENKO_TYPE (this->actor.params & 0xFF) +#define ENKO_PATH ((this->actor.params & 0xFF00) >> 8) + +void EnKo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKo_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A99048(EnKo* this, GlobalContext* globalCtx); +void func_80A995CC(EnKo* this, GlobalContext* globalCtx); +void func_80A99384(EnKo* this, GlobalContext* globalCtx); +void func_80A99438(EnKo* this, GlobalContext* globalCtx); +void func_80A99504(EnKo* this, GlobalContext* globalCtx); +void func_80A99560(EnKo* this, GlobalContext* globalCtx); + +s32 func_80A98ECC(EnKo* this, GlobalContext* globalCtx); + +const ActorInit En_Ko_InitVars = { + ACTOR_EN_KO, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnKo), + (ActorFunc)EnKo_Init, + (ActorFunc)EnKo_Destroy, + (ActorFunc)EnKo_Update, + (ActorFunc)EnKo_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static void* sFaEyes[] = { gFaEyeOpenTex, gFaEyeHalfTex, gFaEyeClosedTex, NULL }; +static void* sKw1Eyes[] = { gKw1EyeOpenTex , gKw1EyeHalfTex, + gKw1EyeClosedTex, NULL }; + +typedef struct { + /* 0x0 */ s16 objectId; + /* 0x4 */ Gfx* dList; + /* 0x8 */ void** eyeTextures; +} EnKoHead; // size = 0xC + +static EnKoHead sHead[] = { + { OBJECT_KM1, gKm1DL , NULL }, + { OBJECT_KW1, object_kw1_DL_002C10, sKw1Eyes }, + { OBJECT_FA, gFaDL, sFaEyes }, +}; + +typedef struct { + /* 0x0 */ s16 objectId; + /* 0x4 */ FlexSkeletonHeader* flexSkeletonHeader; +} EnKoSkeleton; // size = 0x8 + +static EnKoSkeleton sSkeleton[2] = { + { OBJECT_KM1, gKm1Skel, /* 0x060000F0 */ }, + { OBJECT_KW1, gKw1Skel, /* 0x060000F0 */ }, +}; + +typedef enum { + /* 0 */ ENKO_ANIM_0, + /* 1 */ ENKO_ANIM_1, + /* 2 */ ENKO_ANIM_2, + /* 3 */ ENKO_ANIM_3, + /* 4 */ ENKO_ANIM_4, + /* 5 */ ENKO_ANIM_5, + /* 6 */ ENKO_ANIM_6, + /* 7 */ ENKO_ANIM_7, + /* 8 */ ENKO_ANIM_8, + /* 9 */ ENKO_ANIM_9, + /* 10 */ ENKO_ANIM_10, + /* 11 */ ENKO_ANIM_11, + /* 12 */ ENKO_ANIM_12, + /* 13 */ ENKO_ANIM_13, + /* 14 */ ENKO_ANIM_14, + /* 15 */ ENKO_ANIM_15, + /* 16 */ ENKO_ANIM_16, + /* 17 */ ENKO_ANIM_17, + /* 18 */ ENKO_ANIM_18, + /* 19 */ ENKO_ANIM_19, + /* 20 */ ENKO_ANIM_20, + /* 21 */ ENKO_ANIM_21, + /* 22 */ ENKO_ANIM_22, + /* 23 */ ENKO_ANIM_23, + /* 24 */ ENKO_ANIM_24, + /* 25 */ ENKO_ANIM_25, + /* 26 */ ENKO_ANIM_26, + /* 27 */ ENKO_ANIM_27, + /* 28 */ ENKO_ANIM_28, + /* 29 */ ENKO_ANIM_29, + /* 30 */ ENKO_ANIM_30, + /* 31 */ ENKO_ANIM_31, + /* 32 */ ENKO_ANIM_32, + /* 33 */ ENKO_ANIM_33 +} EnKoAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gObjOsAnim_8F6C, 1.0f, 2.0f, 14.0f, ANIMMODE_LOOP_PARTIAL, 0.0f }, + { &gObjOsAnim_8F6C, 0.0f, 1.0f, 1.0f, ANIMMODE_LOOP_PARTIAL, 0.0f }, + { &gObjOsAnim_9B64, 0.0f, 0.0f, 0.0f, ANIMMODE_ONCE, 0.0f }, + { &gObjOsAnim_9B64, 0.0f, 1.0f, 1.0f, ANIMMODE_ONCE, 0.0f }, + { &gObjOsAnim_9B64, 0.0f, 2.0f, 2.0f, ANIMMODE_ONCE, 0.0f }, + { &gObjOsAnim_62DC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_62DC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gObjOsAnim_5808, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gObjOsAnim_7830, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_8178, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_65E0, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_879C, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7FFC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_80B4, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_91AC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_6F9C, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7064, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7120, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7F38, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7D94, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_6EE0, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_98EC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_90EC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_982C, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_9274, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_99A4, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_9028, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7E64, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_7454, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gObjOsAnim_8F6C, 0.0f, 1.0f, 1.0f, ANIMMODE_LOOP_PARTIAL, -8.0f }, + { &gObjOsAnim_7D94, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gObjOsAnim_879C, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gObjOsAnim_6A60, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gObjOsAnim_7830, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, +}; + +static u8 sOsAnimeLookup[13][5] = { + /* ENKO_TYPE_CHILD_0 */ { ENKO_ANIM_8, ENKO_ANIM_9, ENKO_ANIM_9, ENKO_ANIM_14, ENKO_ANIM_11 }, + /* ENKO_TYPE_CHILD_1 */ { ENKO_ANIM_2, ENKO_ANIM_12, ENKO_ANIM_2, ENKO_ANIM_13, ENKO_ANIM_13 }, + /* ENKO_TYPE_CHILD_2 */ { ENKO_ANIM_11, ENKO_ANIM_11, ENKO_ANIM_11, ENKO_ANIM_15, ENKO_ANIM_9 }, + /* ENKO_TYPE_CHILD_3 */ { ENKO_ANIM_0, ENKO_ANIM_16, ENKO_ANIM_16, ENKO_ANIM_17, ENKO_ANIM_18 }, + /* ENKO_TYPE_CHILD_4 */ { ENKO_ANIM_19, ENKO_ANIM_19, ENKO_ANIM_20, ENKO_ANIM_10, ENKO_ANIM_9 }, + /* ENKO_TYPE_CHILD_5 */ { ENKO_ANIM_3, ENKO_ANIM_3, ENKO_ANIM_3, ENKO_ANIM_3, ENKO_ANIM_3 }, + /* ENKO_TYPE_CHILD_6 */ { ENKO_ANIM_4, ENKO_ANIM_22, ENKO_ANIM_22, ENKO_ANIM_4, ENKO_ANIM_23 }, + /* ENKO_TYPE_CHILD_7 */ { ENKO_ANIM_24, ENKO_ANIM_16, ENKO_ANIM_16, ENKO_ANIM_25, ENKO_ANIM_16 }, + /* ENKO_TYPE_CHILD_8 */ { ENKO_ANIM_26, ENKO_ANIM_15, ENKO_ANIM_15, ENKO_ANIM_26, ENKO_ANIM_15 }, + /* ENKO_TYPE_CHILD_9 */ { ENKO_ANIM_3, ENKO_ANIM_3, ENKO_ANIM_3, ENKO_ANIM_27, ENKO_ANIM_27 }, + /* ENKO_TYPE_CHILD_10 */ { ENKO_ANIM_2, ENKO_ANIM_2, ENKO_ANIM_2, ENKO_ANIM_2, ENKO_ANIM_22 }, + /* ENKO_TYPE_CHILD_11 */ { ENKO_ANIM_14, ENKO_ANIM_14, ENKO_ANIM_14, ENKO_ANIM_14, ENKO_ANIM_14 }, + /* ENKO_TYPE_CHILD_FADO */ { ENKO_ANIM_5, ENKO_ANIM_5, ENKO_ANIM_5, ENKO_ANIM_5, ENKO_ANIM_5 }, +}; + +typedef struct { + /* 0x0 */ u8 headId; + /* 0x1 */ u8 bodyId; + /* 0x4 */ Color_RGBA8 tunicColor; + /* 0x8 */ u8 legsId; + /* 0xC */ Color_RGBA8 bootsColor; +} EnKoModelInfo; // size = 0x10 + +typedef enum { + /* 0 */ KO_BOY, + /* 1 */ KO_GIRL, + /* 2 */ KO_FADO +} KokiriGender; + +static EnKoModelInfo sModelInfo[] = { + /* ENKO_TYPE_CHILD_0 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_1 */ { KO_GIRL, KO_GIRL, { 70, 190, 60, 255 }, KO_GIRL, { 100, 30, 0, 255 } }, + /* ENKO_TYPE_CHILD_2 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_3 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_4 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_5 */ { KO_GIRL, KO_GIRL, { 70, 190, 60, 255 }, KO_GIRL, { 100, 30, 0, 255 } }, + /* ENKO_TYPE_CHILD_6 */ { KO_GIRL, KO_GIRL, { 70, 190, 60, 255 }, KO_GIRL, { 100, 30, 0, 255 } }, + /* ENKO_TYPE_CHILD_7 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_8 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_9 */ { KO_GIRL, KO_GIRL, { 70, 190, 60, 255 }, KO_GIRL, { 100, 30, 0, 255 } }, + /* ENKO_TYPE_CHILD_10 */ { KO_GIRL, KO_GIRL, { 70, 190, 60, 255 }, KO_GIRL, { 100, 30, 0, 255 } }, + /* ENKO_TYPE_CHILD_11 */ { KO_BOY, KO_BOY, { 0, 130, 70, 255 }, KO_BOY, { 110, 170, 20, 255 } }, + /* ENKO_TYPE_CHILD_FADO */ { KO_FADO, KO_GIRL, { 70, 190, 60, 255 }, KO_GIRL, { 100, 30, 0, 255 } }, +}; + +typedef struct { + /* 0x0 */ s8 targetMode; + /* 0x4 */ f32 lookDist; // extended by collider radius + /* 0x8 */ f32 appearDist; +} EnKoInteractInfo; // size = 0xC + +static EnKoInteractInfo sInteractInfo[] = { + /* ENKO_TYPE_CHILD_0 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_1 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_2 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_3 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_4 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_5 */ { 1, 30.0f, 240.0f }, + /* ENKO_TYPE_CHILD_6 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_7 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_8 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_9 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_10 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_11 */ { 6, 30.0f, 180.0f }, + /* ENKO_TYPE_CHILD_FADO */ { 6, 30.0f, 180.0f }, +}; + +s32 EnKo_AreObjectsAvailable(EnKo* this, GlobalContext* globalCtx) { + u8 headId = sModelInfo[ENKO_TYPE].headId; + u8 bodyId = sModelInfo[ENKO_TYPE].bodyId; + u8 legsId = sModelInfo[ENKO_TYPE].legsId; + + this->legsObjectBankIdx = Object_GetIndex(&globalCtx->objectCtx, sSkeleton[legsId].objectId); + if (this->legsObjectBankIdx < 0) { + return false; + } + + this->bodyObjectBankIdx = Object_GetIndex(&globalCtx->objectCtx, sSkeleton[bodyId].objectId); + if (this->bodyObjectBankIdx < 0) { + return false; + } + + this->headObjectBankIdx = Object_GetIndex(&globalCtx->objectCtx, sHead[headId].objectId); + if (this->headObjectBankIdx < 0) { + return false; + } + return true; +} + +s32 EnKo_AreObjectsLoaded(EnKo* this, GlobalContext* globalCtx) { + if (!Object_IsLoaded(&globalCtx->objectCtx, this->legsObjectBankIdx)) { + return false; + } + if (!Object_IsLoaded(&globalCtx->objectCtx, this->bodyObjectBankIdx)) { + return false; + } + if (!Object_IsLoaded(&globalCtx->objectCtx, this->headObjectBankIdx)) { + return false; + } + return true; +} + +s32 EnKo_IsOsAnimeAvailable(EnKo* this, GlobalContext* globalCtx) { + this->osAnimeBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_OS_ANIME); + if (this->osAnimeBankIndex < 0) { + return false; + } + return true; +} + +s32 EnKo_IsOsAnimeLoaded(EnKo* this, GlobalContext* globalCtx) { + if (!Object_IsLoaded(&globalCtx->objectCtx, this->osAnimeBankIndex)) { + return false; + } + return true; +} + +u16 func_80A96FD0(GlobalContext* globalCtx, Actor* thisx) { + EnKo* this = (EnKo*)thisx; + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_FADO: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x10DA; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x10D9; + } + return (gSaveContext.infTable[11] & 0x80) ? 0x10D8 : 0x10D7; + case ENKO_TYPE_CHILD_0: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x1025; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1042; + } + return 0x1004; + case ENKO_TYPE_CHILD_1: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x1023; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1043; + } + if (gSaveContext.infTable[1] & 0x4000) { + return 0x1006; + } + return 0x1005; + case ENKO_TYPE_CHILD_2: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x1022; + } + return 0x1007; + case ENKO_TYPE_CHILD_3: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x1021; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1044; + } + if (gSaveContext.infTable[2] & 4) { + return 0x1009; + } + return 0x1008; + case ENKO_TYPE_CHILD_4: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x1097; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1042; + } + if (gSaveContext.infTable[2] & 0x10) { + return 0x100B; + } + return 0x100A; + case ENKO_TYPE_CHILD_5: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x10B0; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1043; + } + if (gSaveContext.infTable[2] & 0x40) { + return 0x100D; + } + return 0x100C; + case ENKO_TYPE_CHILD_6: + if (gSaveContext.eventChkInf[4] & 1) { + return 0x10B5; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1043; + } + if (gSaveContext.infTable[2] & 0x100) { + return 0x1019; + } + return 0x100E; + case ENKO_TYPE_CHILD_7: + return 0x1035; + case ENKO_TYPE_CHILD_8: + return 0x1038; + case ENKO_TYPE_CHILD_9: + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x104B; + } + return 0x103C; + case ENKO_TYPE_CHILD_10: + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x104C; + } + return 0x103D; + case ENKO_TYPE_CHILD_11: + return 0x103E; + } + return 0; +} + +u16 func_80A97338(GlobalContext* globalCtx, Actor* thisx) { + Player* player = GET_PLAYER(globalCtx); + EnKo* this = (EnKo*)thisx; + + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_FADO: + player->exchangeItemId = EXCH_ITEM_ODD_POTION; + return 0x10B9; + case ENKO_TYPE_CHILD_0: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1072; + } + if (gSaveContext.infTable[4] & 2) { + return 0x1056; + } + return 0x1055; + case ENKO_TYPE_CHILD_1: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1073; + } + return 0x105A; + case ENKO_TYPE_CHILD_2: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1074; + } + if (gSaveContext.infTable[4] & 0x80) { + return 0x105E; + } + return 0x105D; + case ENKO_TYPE_CHILD_3: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1075; + } + return 0x105B; + case ENKO_TYPE_CHILD_4: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1076; + } + return 0x105F; + case ENKO_TYPE_CHILD_5: + return 0x1057; + case ENKO_TYPE_CHILD_6: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1077; + } + if (gSaveContext.infTable[5] & 2) { + return 0x1059; + } + return 0x1058; + case ENKO_TYPE_CHILD_7: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x1079; + } + return 0x104E; + case ENKO_TYPE_CHILD_8: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x107A; + } + if (gSaveContext.infTable[5] & 0x200) { + return 0x1050; + } + return 0x104F; + case ENKO_TYPE_CHILD_9: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x107B; + } + return 0x1051; + case ENKO_TYPE_CHILD_10: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x107C; + } + return 0x1052; + case ENKO_TYPE_CHILD_11: + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + return 0x107C; + } + if (gSaveContext.infTable[6] & 2) { + return 0x1054; + } + return 0x1053; + default: + return 0; + } +} + +u16 func_80A97610(GlobalContext* globalCtx, Actor* thisx) { + u16 faceReaction; + EnKo* this = (EnKo*)thisx; + + if (ENKO_TYPE == ENKO_TYPE_CHILD_0 || ENKO_TYPE == ENKO_TYPE_CHILD_2 || ENKO_TYPE == ENKO_TYPE_CHILD_3 || + ENKO_TYPE == ENKO_TYPE_CHILD_4 || ENKO_TYPE == ENKO_TYPE_CHILD_7 || ENKO_TYPE == ENKO_TYPE_CHILD_8 || + ENKO_TYPE == ENKO_TYPE_CHILD_11) { + faceReaction = Text_GetFaceReaction(globalCtx, 0x13); + } + if (ENKO_TYPE == ENKO_TYPE_CHILD_1 || ENKO_TYPE == ENKO_TYPE_CHILD_5 || ENKO_TYPE == ENKO_TYPE_CHILD_6 || + ENKO_TYPE == ENKO_TYPE_CHILD_9 || ENKO_TYPE == ENKO_TYPE_CHILD_10) { + faceReaction = Text_GetFaceReaction(globalCtx, 0x14); + } + if (ENKO_TYPE == ENKO_TYPE_CHILD_FADO) { + faceReaction = Text_GetFaceReaction(globalCtx, 0x12); + } + if (faceReaction != 0) { + return faceReaction; + } + if (LINK_IS_ADULT) { + return func_80A97338(globalCtx, thisx); + } + return func_80A96FD0(globalCtx, thisx); +} + +s16 func_80A97738(GlobalContext* globalCtx, Actor* thisx) { + EnKo* this = (EnKo*)thisx; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CLOSING: + switch (this->actor.textId) { + case 0x1005: + gSaveContext.infTable[1] |= 0x4000; + break; + case 0x1008: + gSaveContext.infTable[2] |= 0x4; + break; + case 0x100A: + gSaveContext.infTable[2] |= 0x10; + break; + case 0x100C: + gSaveContext.infTable[2] |= 0x40; + break; + case 0x100E: + gSaveContext.infTable[2] |= 0x100; + break; + case 0x104F: + gSaveContext.infTable[5] |= 0x200; + break; + case 0x1053: + gSaveContext.infTable[6] |= 2; + break; + case 0x1055: + gSaveContext.infTable[4] |= 2; + break; + case 0x1058: + gSaveContext.infTable[5] |= 2; + break; + case 0x105D: + gSaveContext.infTable[4] |= 0x80; + break; + case 0x10D7: + gSaveContext.infTable[11] |= 0x80; + break; + case 0x10BA: + return 1; + } + return 0; + case TEXT_STATE_DONE_FADING: + switch (this->actor.textId) { + case 0x10B7: + case 0x10B8: + if (this->unk_210 == 0) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_210 = 1; + } + } + return 1; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + switch (this->actor.textId) { + case 0x1035: + this->actor.textId = (globalCtx->msgCtx.choiceIndex == 0) ? 0x1036 : 0x1037; + Message_ContinueTextbox(globalCtx, this->actor.textId); + break; + case 0x1038: + this->actor.textId = (globalCtx->msgCtx.choiceIndex != 0) + ? (globalCtx->msgCtx.choiceIndex == 1) ? 0x103A : 0x103B + : 0x1039; + Message_ContinueTextbox(globalCtx, this->actor.textId); + break; + case 0x103E: + this->actor.textId = (globalCtx->msgCtx.choiceIndex == 0) ? 0x103F : 0x1040; + Message_ContinueTextbox(globalCtx, this->actor.textId); + break; + case 0x10B7: + gSaveContext.infTable[11] |= 0x1000; + + case 0x10B8: + this->actor.textId = (globalCtx->msgCtx.choiceIndex == 0) ? 0x10BA : 0x10B9; + return (globalCtx->msgCtx.choiceIndex == 0) ? 2 : 1; + } + return 1; + } + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + return 3; + } + } + return 1; +} + +s32 EnKo_GetForestQuestState(EnKo* this) { + s32 result; + + if (!LINK_IS_ADULT) { + // Obtained Zelda's Letter + if (gSaveContext.eventChkInf[4] & 1) { + return ENKO_FQS_CHILD_SARIA; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return ENKO_FQS_CHILD_STONE; + } + return ENKO_FQS_CHILD_START; + } + + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + result = ENKO_FQS_ADULT_SAVED; + } else { + result = ENKO_FQS_ADULT_ENEMY; + } + return result; +} + +f32 func_80A97BC0(EnKo* this) { + f32 D_80A9A62C[13][5] = { + /* ENKO_TYPE_CHILD_0 */ { 0.0f, 0.0f, 0.0f, -30.0f, -20.0f }, + /* ENKO_TYPE_CHILD_1 */ { 0.0f, 0.0f, 0.0f, -20.0f, -10.0f }, + /* ENKO_TYPE_CHILD_2 */ { 0.0f, 0.0f, 0.0f, -30.0f, -20.0f }, + /* ENKO_TYPE_CHILD_3 */ { -10.0f, 10.0f, 10.0f, -10.0f, -30.0f }, + /* ENKO_TYPE_CHILD_4 */ { 0.0f, 0.0f, 0.0f, -10.0f, -20.0f }, + /* ENKO_TYPE_CHILD_5 */ { 0.0f, 0.0f, 0.0f, -20.0f, -20.0f }, + /* ENKO_TYPE_CHILD_6 */ { 0.0f, 0.0f, 0.0f, -10.0f, -20.0f }, + /* ENKO_TYPE_CHILD_7 */ { 10.0f, 10.0f, 10.0f, -60.0f, -20.0f }, + /* ENKO_TYPE_CHILD_8 */ { -10.0f, -10.0f, -20.0f, -30.0f, -30.0f }, + /* ENKO_TYPE_CHILD_9 */ { -10.0f, -10.0f, -10.0f, -40.0f, -40.0f }, + /* ENKO_TYPE_CHILD_10 */ { 0.0f, 0.0f, 0.0f, -10.0f, -20.0f }, + /* ENKO_TYPE_CHILD_11 */ { -10.0f, -10.0f, -20.0f, -30.0f, -30.0f }, + /* ENKO_TYPE_CHILD_FADO */ { 0.0f, 0.0f, 0.0f, -20.0f, -20.0f }, + }; + + if (LINK_IS_ADULT && ENKO_TYPE == ENKO_TYPE_CHILD_FADO) { + return -20.0f; + } + return D_80A9A62C[ENKO_TYPE][EnKo_GetForestQuestState(this)]; +} + +u8 func_80A97C7C(EnKo* this) { + u8 D_80A9A730[13][5] = { + /* ENKO_TYPE_CHILD_0 */ { 1, 1, 1, 0, 1 }, + /* ENKO_TYPE_CHILD_1 */ { 1, 1, 1, 1, 1 }, + /* ENKO_TYPE_CHILD_2 */ { 1, 1, 1, 0, 1 }, + /* ENKO_TYPE_CHILD_3 */ { 1, 1, 1, 0, 1 }, + /* ENKO_TYPE_CHILD_4 */ { 1, 1, 1, 0, 1 }, + /* ENKO_TYPE_CHILD_5 */ { 0, 0, 0, 0, 0 }, + /* ENKO_TYPE_CHILD_6 */ { 1, 1, 1, 1, 1 }, + /* ENKO_TYPE_CHILD_7 */ { 1, 1, 1, 0, 1 }, + /* ENKO_TYPE_CHILD_8 */ { 0, 0, 0, 0, 0 }, + /* ENKO_TYPE_CHILD_9 */ { 0, 0, 0, 0, 0 }, + /* ENKO_TYPE_CHILD_10 */ { 1, 1, 1, 1, 1 }, + /* ENKO_TYPE_CHILD_11 */ { 0, 0, 0, 0, 0 }, + /* ENKO_TYPE_CHILD_FADO */ { 1, 1, 1, 1, 1 }, + }; + + return D_80A9A730[ENKO_TYPE][EnKo_GetForestQuestState(this)]; +} + +s32 EnKo_IsWithinTalkAngle(EnKo* this) { + s16 yawDiff; + s16 yawDiffAbs; + s32 result; + + yawDiff = this->actor.yawTowardsPlayer - (f32)this->actor.shape.rot.y; + yawDiffAbs = ABS(yawDiff); + + if (yawDiffAbs < 0x3FFC) { + result = true; + } else { + result = false; + } + return result; +} + +s32 func_80A97D68(EnKo* this, GlobalContext* globalCtx) { + s16 arg3; + + if (this->unk_1E8.unk_00 != 0) { + if ((this->skelAnime.animation == &gObjOsAnim_6A60) == false) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_32); + } + arg3 = 2; + } else { + if ((this->skelAnime.animation == &gObjOsAnim_7830) == false) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_33); + } + arg3 = 1; + } + func_80034A14(&this->actor, &this->unk_1E8, 2, arg3); + return EnKo_IsWithinTalkAngle(this); +} + +s32 func_80A97E18(EnKo* this, GlobalContext* globalCtx) { + s16 arg3; + + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + if (EnKo_IsWithinTalkAngle(this) == true) { + arg3 = 2; + } else { + arg3 = 1; + } + if (this->unk_1E8.unk_00 != 0) { + arg3 = 4; + } else if (this->lookDist < this->actor.xzDistToPlayer) { + arg3 = 1; + } + func_80034A14(&this->actor, &this->unk_1E8, 2, arg3); + return 1; +} + +s32 func_80A97EB0(EnKo* this, GlobalContext* globalCtx) { + s16 arg3; + s32 result; + + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + result = EnKo_IsWithinTalkAngle(this); + arg3 = (result == true) ? 2 : 1; + func_80034A14(&this->actor, &this->unk_1E8, 2, arg3); + return result; +} + +s32 func_80A97F20(EnKo* this, GlobalContext* globalCtx) { + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + func_80034A14(&this->actor, &this->unk_1E8, 2, 4); + return 1; +} + +s32 func_80A97F70(EnKo* this, GlobalContext* globalCtx) { + s16 arg3; + + if (this->unk_1E8.unk_00 != 0) { + if ((this->skelAnime.animation == &gObjOsAnim_8F6C) == false) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_29); + } + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + arg3 = 2; + } else { + if ((this->skelAnime.animation == &gObjOsAnim_7D94) == false) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_30); + } + arg3 = 1; + } + func_80034A14(&this->actor, &this->unk_1E8, 5, arg3); + return EnKo_IsWithinTalkAngle(this); +} + +s32 func_80A98034(EnKo* this, GlobalContext* globalCtx) { + s16 arg3; + s32 result; + + if (this->unk_1E8.unk_00 != 0) { + if ((this->skelAnime.animation == &gObjOsAnim_8F6C) == false) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_29); + } + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + result = EnKo_IsWithinTalkAngle(this); + arg3 = (result == true) ? 2 : 1; + } else { + if ((this->skelAnime.animation == &gObjOsAnim_879C) == false) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_31); + } + arg3 = 1; + result = EnKo_IsWithinTalkAngle(this); + } + func_80034A14(&this->actor, &this->unk_1E8, 5, arg3); + return result; +} + +// Same as func_80A97F20 +s32 func_80A98124(EnKo* this, GlobalContext* globalCtx) { + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + func_80034A14(&this->actor, &this->unk_1E8, 2, 4); + return 1; +} + +s32 func_80A98174(EnKo* this, GlobalContext* globalCtx) { + if (this->unk_1E8.unk_00 != 0) { + if (Animation_OnFrame(&this->skelAnime, 18.0f)) { + this->skelAnime.playSpeed = 0.0f; + } + } else if (this->skelAnime.playSpeed != 1.0f) { + this->skelAnime.playSpeed = 1.0f; + } + if (this->skelAnime.playSpeed == 0.0f) { + func_80034F54(globalCtx, this->unk_2E4, this->unk_304, 16); + } + func_80034A14(&this->actor, &this->unk_1E8, 2, (this->skelAnime.playSpeed == 0.0f) ? 2 : 1); + return EnKo_IsWithinTalkAngle(this); +} + +s32 EnKo_ChildStart(EnKo* this, GlobalContext* globalCtx) { + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_0: + return func_80A97D68(this, globalCtx); + case ENKO_TYPE_CHILD_1: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_2: + return func_80A98034(this, globalCtx); + case ENKO_TYPE_CHILD_3: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_4: + return func_80A97F70(this, globalCtx); + case ENKO_TYPE_CHILD_5: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_6: + return func_80A97F20(this, globalCtx); + case ENKO_TYPE_CHILD_7: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_8: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_9: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_10: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_11: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_FADO: + return func_80A97E18(this, globalCtx); + } +} + +s32 EnKo_ChildStone(EnKo* this, GlobalContext* globalCtx) { + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_0: + return func_80A98124(this, globalCtx); + case ENKO_TYPE_CHILD_1: + return func_80A98124(this, globalCtx); + case ENKO_TYPE_CHILD_2: + return func_80A98034(this, globalCtx); + case ENKO_TYPE_CHILD_3: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_4: + return func_80A97F70(this, globalCtx); + case ENKO_TYPE_CHILD_5: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_6: + return func_80A97F20(this, globalCtx); + case ENKO_TYPE_CHILD_7: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_8: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_9: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_10: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_11: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_FADO: + return func_80A97E18(this, globalCtx); + } +} + +s32 EnKo_ChildSaria(EnKo* this, GlobalContext* globalCtx) { + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_0: + return func_80A98124(this, globalCtx); + case ENKO_TYPE_CHILD_1: + return func_80A98124(this, globalCtx); + case ENKO_TYPE_CHILD_2: + return func_80A98034(this, globalCtx); + case ENKO_TYPE_CHILD_3: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_4: + return func_80A98174(this, globalCtx); + case ENKO_TYPE_CHILD_5: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_6: + return func_80A97F20(this, globalCtx); + case ENKO_TYPE_CHILD_7: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_8: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_9: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_10: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_11: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_FADO: + return func_80A97E18(this, globalCtx); + } +} + +s32 EnKo_AdultEnemy(EnKo* this, GlobalContext* globalCtx) { + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_0: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_1: + return func_80A98124(this, globalCtx); + case ENKO_TYPE_CHILD_2: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_3: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_4: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_5: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_6: + return func_80A97F20(this, globalCtx); + case ENKO_TYPE_CHILD_7: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_8: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_9: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_10: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_11: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_FADO: + return func_80A97E18(this, globalCtx); + } +} + +s32 EnKo_AdultSaved(EnKo* this, GlobalContext* globalCtx) { + switch (ENKO_TYPE) { + case ENKO_TYPE_CHILD_0: + return func_80A98034(this, globalCtx); + case ENKO_TYPE_CHILD_1: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_2: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_3: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_4: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_5: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_6: + return func_80A97F20(this, globalCtx); + case ENKO_TYPE_CHILD_7: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_8: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_9: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_10: + return func_80A97E18(this, globalCtx); + case ENKO_TYPE_CHILD_11: + return func_80A97EB0(this, globalCtx); + case ENKO_TYPE_CHILD_FADO: + return func_80A97E18(this, globalCtx); + } +} +void func_80A9877C(EnKo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((globalCtx->csCtx.state != 0) || (gDbgCamEnabled != 0)) { + this->unk_1E8.unk_18 = globalCtx->view.eye; + this->unk_1E8.unk_14 = 40.0f; + if (ENKO_TYPE != ENKO_TYPE_CHILD_0) { + func_80034A14(&this->actor, &this->unk_1E8, 2, 2); + } + } else { + this->unk_1E8.unk_18 = player->actor.world.pos; + this->unk_1E8.unk_14 = func_80A97BC0(this); + if ((func_80A98ECC(this, globalCtx) == 0) && (this->unk_1E8.unk_00 == 0)) { + return; + } + } + if (func_800343CC(globalCtx, &this->actor, &this->unk_1E8.unk_00, this->lookDist, func_80A97610, func_80A97738) && + ENKO_TYPE == ENKO_TYPE_CHILD_FADO && globalCtx->sceneNum == SCENE_SPOT10) { + this->actor.textId = INV_CONTENT(ITEM_TRADE_ADULT) > ITEM_ODD_POTION ? 0x10B9 : 0x10DF; + + if (func_8002F368(globalCtx) == ENKO_TYPE_CHILD_9) { + this->actor.textId = (gSaveContext.infTable[11] & 0x1000) ? 0x10B8 : 0x10B7; + this->unk_210 = 0; + } + player->actor.textId = this->actor.textId; + } +} + +// Checks if the Kokiri should spawn based on quest progress +s32 EnKo_CanSpawn(EnKo* this, GlobalContext* globalCtx) { + switch (globalCtx->sceneNum) { + case SCENE_SPOT04: + if (ENKO_TYPE >= ENKO_TYPE_CHILD_7 && ENKO_TYPE != ENKO_TYPE_CHILD_FADO) { + return false; + } + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && LINK_IS_ADULT) { + return false; + } + return true; + case SCENE_KOKIRI_HOME: + if (ENKO_TYPE != ENKO_TYPE_CHILD_7 && ENKO_TYPE != ENKO_TYPE_CHILD_8 && ENKO_TYPE != ENKO_TYPE_CHILD_11) { + return false; + } else { + return true; + } + case SCENE_KOKIRI_HOME3: + if (LINK_IS_ADULT && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + if (ENKO_TYPE != ENKO_TYPE_CHILD_1 && ENKO_TYPE != ENKO_TYPE_CHILD_9) { + return false; + } else { + return true; + } + } + if (ENKO_TYPE != ENKO_TYPE_CHILD_9) { + return false; + } else { + return true; + } + case SCENE_KOKIRI_HOME4: + if (LINK_IS_ADULT && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + if (ENKO_TYPE != ENKO_TYPE_CHILD_0 && ENKO_TYPE != ENKO_TYPE_CHILD_4) { + return false; + } else { + return true; + } + } else { + return false; + } + case SCENE_KOKIRI_HOME5: + if (LINK_IS_ADULT && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + if (ENKO_TYPE != ENKO_TYPE_CHILD_6) { + return false; + } else { + return true; + } + } else { + return false; + } + + case SCENE_KOKIRI_SHOP: + if (LINK_IS_ADULT && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + if (ENKO_TYPE != ENKO_TYPE_CHILD_5 && ENKO_TYPE != ENKO_TYPE_CHILD_10) { + return false; + } else { + return true; + } + } else if (ENKO_TYPE != ENKO_TYPE_CHILD_10) { + return false; + } else { + return true; + } + + case SCENE_SPOT10: + return (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_ODD_POTION) ? true : false; + default: + return false; + } +} + +void EnKo_Blink(EnKo* this) { + void** eyeTextures; + s32 headId; + + if (DECR(this->blinkTimer) == 0) { + headId = sModelInfo[ENKO_TYPE].headId; + this->eyeTextureIndex++; + eyeTextures = sHead[headId].eyeTextures; + if (eyeTextures != NULL && eyeTextures[this->eyeTextureIndex] == NULL) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeTextureIndex = 0; + } + } +} + +void func_80A98CD8(EnKo* this) { + s32 type = ENKO_TYPE; + EnKoInteractInfo* info = &sInteractInfo[type]; + + this->actor.targetMode = info->targetMode; + this->lookDist = info->lookDist; + this->lookDist += this->collider.dim.radius; + this->appearDist = info->appearDist; +} + +// Used to fetch actor animation? +s32 EnKo_GetForestQuestState2(EnKo* this) { + if (LINK_IS_ADULT) { + return CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) ? ENKO_FQS_ADULT_SAVED : ENKO_FQS_ADULT_ENEMY; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return (gSaveContext.eventChkInf[4] & 1) ? ENKO_FQS_CHILD_SARIA : ENKO_FQS_CHILD_STONE; + } + return ENKO_FQS_CHILD_START; +} + +void func_80A98DB4(EnKo* this, GlobalContext* globalCtx) { + f32 dist; + + if (globalCtx->sceneNum != SCENE_SPOT10 && globalCtx->sceneNum != SCENE_SPOT04) { + this->modelAlpha = 255.0f; + return; + } + if (globalCtx->csCtx.state != 0 || gDbgCamEnabled != 0) { + dist = Math_Vec3f_DistXYZ(&this->actor.world.pos, &globalCtx->view.eye) * 0.25f; + } else { + dist = this->actor.xzDistToPlayer; + } + + Math_SmoothStepToF(&this->modelAlpha, (this->appearDist < dist) ? 0.0f : 255.0f, 0.3f, 40.0f, 1.0f); + if (this->modelAlpha < 10.0f) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + this->actor.flags |= ACTOR_FLAG_0; + } +} + +s32 func_80A98ECC(EnKo* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT10 && ENKO_TYPE == ENKO_TYPE_CHILD_FADO) { + return func_80A97E18(this, globalCtx); + } + switch (EnKo_GetForestQuestState(this)) { + case ENKO_FQS_CHILD_START: + return EnKo_ChildStart(this, globalCtx); + case ENKO_FQS_CHILD_STONE: + return EnKo_ChildStone(this, globalCtx); + case ENKO_FQS_CHILD_SARIA: + return EnKo_ChildSaria(this, globalCtx); + case ENKO_FQS_ADULT_ENEMY: + return EnKo_AdultEnemy(this, globalCtx); + case ENKO_FQS_ADULT_SAVED: + return EnKo_AdultSaved(this, globalCtx); + } +} + +void EnKo_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKo* this = (EnKo*)thisx; + + if (ENKO_TYPE >= ENKO_TYPE_CHILD_MAX || !EnKo_IsOsAnimeAvailable(this, globalCtx) || + !EnKo_AreObjectsAvailable(this, globalCtx)) { + Actor_Kill(thisx); + } + if (!EnKo_CanSpawn(this, globalCtx)) { + Actor_Kill(thisx); + } + this->actionFunc = func_80A99048; +} + +void EnKo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnKo* this = (EnKo*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80A99048(EnKo* this, GlobalContext* globalCtx) { + if (EnKo_IsOsAnimeLoaded(this, globalCtx) && EnKo_AreObjectsLoaded(this, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.objBankIndex = this->legsObjectBankIdx; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, sSkeleton[sModelInfo[ENKO_TYPE].legsId].flexSkeletonHeader, + NULL, this->jointTable, this->morphTable, 16); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->osAnimeBankIndex].segment); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + if (ENKO_TYPE == ENKO_TYPE_CHILD_7) { + // "Angle Z" + osSyncPrintf(VT_BGCOL(BLUE) " アングルZ->(%d)\n" VT_RST, this->actor.shape.rot.z); + if (LINK_IS_ADULT && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + if (this->actor.shape.rot.z != 1) { + Actor_Kill(&this->actor); + return; + } + } else if (this->actor.shape.rot.z != 0) { + Actor_Kill(&this->actor); + return; + } + } + if (ENKO_TYPE == ENKO_TYPE_CHILD_5) { + this->collider.base.ocFlags1 |= 0x40; + } + this->forestQuestState = EnKo_GetForestQuestState2(this); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, sOsAnimeLookup[ENKO_TYPE][this->forestQuestState]); + Actor_SetScale(&this->actor, 0.01f); + func_80A98CD8(this); + this->modelAlpha = 0.0f; + this->path = Path_GetByIndex(globalCtx, ENKO_PATH, 0xFF); + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 3); + if (ENKO_TYPE == ENKO_TYPE_CHILD_3) { + if (!CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + this->collider.dim.height += 200; + this->actionFunc = func_80A995CC; + return; + } + Path_CopyLastPoint(this->path, &this->actor.world.pos); + } + this->actionFunc = func_80A99384; + } +} + +void func_80A99384(EnKo* this, GlobalContext* globalCtx) { + if (ENKO_TYPE == ENKO_TYPE_CHILD_FADO && this->unk_1E8.unk_00 != 0 && this->actor.textId == 0x10B9) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_7); + this->actionFunc = func_80A99438; + } else if (ENKO_TYPE == ENKO_TYPE_CHILD_FADO && this->unk_1E8.unk_00 == 2) { + this->actionFunc = func_80A99504; + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } +} + +void func_80A99438(EnKo* this, GlobalContext* globalCtx) { + if (ENKO_TYPE == ENKO_TYPE_CHILD_FADO && this->unk_1E8.unk_00 == 2) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_6); + this->actionFunc = func_80A99504; + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } else if (this->unk_1E8.unk_00 == 0 || this->actor.textId != 0x10B9) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENKO_ANIM_6); + this->actionFunc = func_80A99384; + } +} + +void func_80A99504(EnKo* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_80A99560; + } else { + func_8002F434(&this->actor, globalCtx, GI_SAW, 120.0f, 10.0f); + } +} + +void func_80A99560(EnKo* this, GlobalContext* globalCtx) { + if (this->unk_1E8.unk_00 == 3) { + this->actor.textId = 0x10B9; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E8.unk_00 = 1; + gSaveContext.itemGetInf[3] |= 2; + this->actionFunc = func_80A99384; + } +} + +void func_80A995CC(EnKo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 temp_f2; + f32 phi_f0; + s16 homeYawToPlayer = Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos); + + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.x += 80.0f * Math_SinS(homeYawToPlayer); + this->actor.world.pos.z = this->actor.home.pos.z; + this->actor.world.pos.z += 80.0f * Math_CosS(homeYawToPlayer); + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (this->unk_1E8.unk_00 == 0 || !this->actor.isTargeted) { + temp_f2 = fabsf((f32)this->actor.yawTowardsPlayer - homeYawToPlayer) * 0.001f * 3.0f; + if (temp_f2 < 1.0f) { + this->skelAnime.playSpeed = 1.0f; + } else { + phi_f0 = CLAMP_MAX(temp_f2, 3.0f); + this->skelAnime.playSpeed = phi_f0; + } + } else { + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnKo_Update(Actor* thisx, GlobalContext* globalCtx) { + ColliderCylinder* collider; + EnKo* this = (EnKo*)thisx; + s32 pad; + + if (this->actionFunc != func_80A99048) { + if ((s32)this->modelAlpha != 0) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->osAnimeBankIndex].segment); + SkelAnime_Update(&this->skelAnime); + func_80A98DB4(this, globalCtx); + EnKo_Blink(this); + } else { + func_80A98DB4(this, globalCtx); + } + } + if (this->unk_1E8.unk_00 == 0) { + Actor_MoveForward(&this->actor); + } + if (func_80A97C7C(this)) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actor.gravity = -1.0f; + } else { + this->actor.gravity = 0.0f; + } + + this->actionFunc(this, globalCtx); + + func_80A9877C(this, globalCtx); + collider = &this->collider; + Collider_UpdateCylinder(&this->actor, collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &collider->base); +} + +s32 EnKo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnKo* this = (EnKo*)thisx; + void* eyeTexture; + Vec3s sp40; + u8 headId; + s32 pad; + + if (limbIndex == 15) { + gSPSegment((*gfx)++, 0x06, globalCtx->objectCtx.status[this->headObjectBankIdx].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->headObjectBankIdx].segment); + + headId = sModelInfo[ENKO_TYPE].headId; + *dList = sHead[headId].dList; + if (sHead[headId].eyeTextures != NULL) { + eyeTexture = sHead[headId].eyeTextures[this->eyeTextureIndex]; + gSPSegment((*gfx)++, 0x0A, SEGMENTED_TO_VIRTUAL(eyeTexture)); + } + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->legsObjectBankIdx].segment); + } + if (limbIndex == 8) { + sp40 = this->unk_1E8.unk_0E; + Matrix_RotateX(BINANG_TO_RAD(-sp40.y), MTXMODE_APPLY); + Matrix_RotateZ(BINANG_TO_RAD(sp40.x), MTXMODE_APPLY); + } + if (limbIndex == 15) { + Matrix_Translate(1200.0f, 0.0f, 0.0f, MTXMODE_APPLY); + sp40 = this->unk_1E8.unk_08; + Matrix_RotateX(BINANG_TO_RAD(sp40.y), MTXMODE_APPLY); + Matrix_RotateZ(BINANG_TO_RAD(sp40.x), MTXMODE_APPLY); + Matrix_Translate(-1200.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == 8 || limbIndex == 9 || limbIndex == 12) { + rot->y += Math_SinS(this->unk_2E4[limbIndex]) * 200.0f; + rot->z += Math_CosS(this->unk_304[limbIndex]) * 200.0f; + } + return false; +} + +void EnKo_PostLimbDraw(GlobalContext* globalCtx2, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + GlobalContext* globalCtx = globalCtx2; + EnKo* this = (EnKo*)thisx; + Vec3f D_80A9A774 = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == 7) { + gSPSegment((*gfx)++, 0x06, globalCtx->objectCtx.status[this->bodyObjectBankIdx].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->bodyObjectBankIdx].segment); + } + if (limbIndex == 15) { + Matrix_MultVec3f(&D_80A9A774, &this->actor.focus.pos); + } +} + +Gfx* EnKo_SetEnvColor(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b, u8 a) { + Gfx* dList = Graph_Alloc(gfxCtx, sizeof(Gfx) * 2); + + gDPSetEnvColor(dList, r, g, b, a); + gSPEndDisplayList(dList + 1); + return dList; +} + +void EnKo_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnKo* this = (EnKo*)thisx; + Color_RGBA8 tunicColor = sModelInfo[ENKO_TYPE].tunicColor; + Color_RGBA8 bootsColor = sModelInfo[ENKO_TYPE].bootsColor; + + this->actor.shape.shadowAlpha = this->modelAlpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ko.c", 2095); + if ((s16)this->modelAlpha == 255) { + gSPSegment(POLY_OPA_DISP++, 0x08, + EnKo_SetEnvColor(globalCtx->state.gfxCtx, tunicColor.r, tunicColor.g, tunicColor.b, 255)); + gSPSegment(POLY_OPA_DISP++, 0x09, + EnKo_SetEnvColor(globalCtx->state.gfxCtx, bootsColor.r, bootsColor.g, bootsColor.b, 255)); + func_80034BA0(globalCtx, &this->skelAnime, EnKo_OverrideLimbDraw, EnKo_PostLimbDraw, &this->actor, + this->modelAlpha); + } else if ((s16)this->modelAlpha != 0) { + tunicColor.a = this->modelAlpha; + bootsColor.a = this->modelAlpha; + gSPSegment(POLY_XLU_DISP++, 0x08, + EnKo_SetEnvColor(globalCtx->state.gfxCtx, tunicColor.r, tunicColor.g, tunicColor.b, tunicColor.a)); + gSPSegment(POLY_XLU_DISP++, 0x09, + EnKo_SetEnvColor(globalCtx->state.gfxCtx, bootsColor.r, bootsColor.g, bootsColor.b, bootsColor.a)); + func_80034CC4(globalCtx, &this->skelAnime, EnKo_OverrideLimbDraw, EnKo_PostLimbDraw, &this->actor, + this->modelAlpha); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ko.c", 2136); +} diff --git a/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.h b/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.h new file mode 100644 index 000000000..b7a60dd05 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ko/z_en_ko.h @@ -0,0 +1,60 @@ +#ifndef Z_EN_KO_H +#define Z_EN_KO_H + +#include "ultra64.h" +#include "global.h" + +struct EnKo; + +typedef void (*EnKoActionFunc)(struct EnKo*, GlobalContext*); + +typedef struct EnKo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnKoActionFunc actionFunc; + /* 0x0194 */ s8 headObjectBankIdx; + /* 0x0195 */ s8 bodyObjectBankIdx; + /* 0x0196 */ s8 legsObjectBankIdx; + /* 0x0197 */ s8 osAnimeBankIndex; + /* 0x0198 */ ColliderCylinder collider; + /* 0x01E4 */ Path* path; + /* 0x01E8 */ struct_80034A14_arg1 unk_1E8; + /* 0x0210 */ u8 unk_210; // block trade quest sfx + /* 0x0212 */ s16 forestQuestState; + /* 0x0214 */ s16 blinkTimer; + /* 0x0216 */ s16 eyeTextureIndex; + /* 0x0218 */ f32 appearDist; + /* 0x021C */ f32 lookDist; // distance to start looking at player + /* 0x0220 */ f32 modelAlpha; + /* 0x0224 */ Vec3s jointTable[16]; + /* 0x0284 */ Vec3s morphTable[16]; + /* 0x02E4 */ s16 unk_2E4[16]; + /* 0x0304 */ s16 unk_304[16]; +} EnKo; // size = 0x0324 + +typedef enum { + ENKO_TYPE_CHILD_0, + ENKO_TYPE_CHILD_1, + ENKO_TYPE_CHILD_2, + ENKO_TYPE_CHILD_3, + ENKO_TYPE_CHILD_4, + ENKO_TYPE_CHILD_5, // Shop Awning + ENKO_TYPE_CHILD_6, + ENKO_TYPE_CHILD_7, + ENKO_TYPE_CHILD_8, + ENKO_TYPE_CHILD_9, + ENKO_TYPE_CHILD_10, + ENKO_TYPE_CHILD_11, + ENKO_TYPE_CHILD_FADO, + ENKO_TYPE_CHILD_MAX +} KokiriChildren; + +typedef enum { + ENKO_FQS_CHILD_START, + ENKO_FQS_CHILD_STONE, + ENKO_FQS_CHILD_SARIA, + ENKO_FQS_ADULT_ENEMY, + ENKO_FQS_ADULT_SAVED +} KokiriForestQuestState; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c new file mode 100644 index 000000000..1b89b7b8d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c @@ -0,0 +1,504 @@ +/* + * File: z_en_kusa.c + * Overlay: ovl_en_kusa + * Description: Bush + */ + +#include "z_en_kusa.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" +#include "objects/object_kusa/object_kusa.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_23) + +void EnKusa_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKusa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKusa_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKusa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnKusa_SetupLiftedUp(EnKusa* this); +void EnKusa_SetupWaitObject(EnKusa* this); +void EnKusa_SetupMain(EnKusa* this); +void EnKusa_SetupFall(EnKusa* this); +void EnKusa_SetupCut(EnKusa* this); +void EnKusa_SetupUprootedWaitRegrow(EnKusa* this); +void EnKusa_SetupRegrow(EnKusa* this); + +void EnKusa_Fall(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_WaitObject(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_Main(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_LiftedUp(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_CutWaitRegrow(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_DoNothing(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_UprootedWaitRegrow(EnKusa* this, GlobalContext* globalCtx); +void EnKusa_Regrow(EnKusa* this, GlobalContext* globalCtx); + +static s16 rotSpeedXtarget = 0; +static s16 rotSpeedX = 0; +static s16 rotSpeedYtarget = 0; +static s16 rotSpeedY = 0; + +const ActorInit En_Kusa_InitVars = { + ACTOR_EN_KUSA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnKusa), + (ActorFunc)EnKusa_Init, + (ActorFunc)EnKusa_Destroy, + (ActorFunc)EnKusa_Update, + NULL, + NULL, +}; + +static s16 sObjectIds[] = { OBJECT_GAMEPLAY_FIELD_KEEP, OBJECT_KUSA, OBJECT_KUSA }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER | OC1_TYPE_2, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC00758, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 12, 44, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 30, MASS_IMMOVABLE }; + +static Vec3f sUnitDirections[] = { + { 0.0f, 0.7071f, 0.7071f }, + { 0.7071f, 0.7071f, 0.0f }, + { 0.0f, 0.7071f, -0.7071f }, + { -0.7071f, 0.7071f, 0.0f }, +}; + +static s16 sFragmentScales[] = { 108, 102, 96, 84, 66, 55, 42, 38 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 400, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(gravity, -3200, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(minVelocityY, -17000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 120, ICHAIN_STOP), +}; + +void EnKusa_SetupAction(EnKusa* this, EnKusaActionFunc actionFunc) { + this->timer = 0; + this->actionFunc = actionFunc; +} + +s32 EnKusa_SnapToFloor(EnKusa* this, GlobalContext* globalCtx, f32 yOffset) { + s32 pad; + CollisionPoly* poly; + Vec3f pos; + s32 bgId; + f32 floorY; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 30.0f; + pos.z = this->actor.world.pos.z; + + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &poly, &bgId, &this->actor, &pos); + + if (floorY > BGCHECK_Y_MIN) { + this->actor.world.pos.y = floorY + yOffset; + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + return true; + } else { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "Failure attaching to ground" + osSyncPrintf("地面に付着失敗(%s %d)\n", "../z_en_kusa.c", 323); + osSyncPrintf(VT_RST); + return false; + } +} + +void EnKusa_DropCollectible(EnKusa* this, GlobalContext* globalCtx) { + s16 dropParams; + + switch (this->actor.params & 3) { + case ENKUSA_TYPE_0: + case ENKUSA_TYPE_2: + dropParams = (this->actor.params >> 8) & 0xF; + + if (dropParams >= 0xD) { + dropParams = 0; + } + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, dropParams << 4); + break; + case ENKUSA_TYPE_1: + if (Rand_ZeroOne() < 0.5f) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_SEEDS); + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_HEART); + } + break; + } +} + +void EnKusa_UpdateVelY(EnKusa* this) { + this->actor.velocity.y += this->actor.gravity; + + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } +} + +void EnKusa_RandScaleVecToZero(Vec3f* vec, f32 scale) { + scale += ((Rand_ZeroOne() * 0.2f) - 0.1f) * scale; + vec->x -= vec->x * scale; + vec->y -= vec->y * scale; + vec->z -= vec->z * scale; +} + +void EnKusa_SetScaleSmall(EnKusa* this) { + this->actor.scale.y = 0.16000001f; + this->actor.scale.x = 0.120000005f; + this->actor.scale.z = 0.120000005f; +} + +void EnKusa_SpawnFragments(EnKusa* this, GlobalContext* globalCtx) { + Vec3f velocity; + Vec3f pos; + s32 i; + s32 scaleIndex; + Vec3f* dir; + s32 pad; + + for (i = 0; i < ARRAY_COUNT(sUnitDirections); i++) { + dir = &sUnitDirections[i]; + + pos.x = this->actor.world.pos.x + (dir->x * this->actor.scale.x * 20.0f); + pos.y = this->actor.world.pos.y + (dir->y * this->actor.scale.y * 20.0f) + 10.0f; + pos.z = this->actor.world.pos.z + (dir->z * this->actor.scale.z * 20.0f); + + velocity.x = (Rand_ZeroOne() - 0.5f) * 8.0f; + velocity.y = Rand_ZeroOne() * 10.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 8.0f; + + scaleIndex = (s32)(Rand_ZeroOne() * 111.1f) & 7; + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -100, 64, 40, 3, 0, sFragmentScales[scaleIndex], 0, 0, + 80, KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_KEEP, gCuttableShrubStalkDL); + + pos.x = this->actor.world.pos.x + (dir->x * this->actor.scale.x * 40.0f); + pos.y = this->actor.world.pos.y + (dir->y * this->actor.scale.y * 40.0f) + 10.0f; + pos.z = this->actor.world.pos.z + (dir->z * this->actor.scale.z * 40.0f); + + velocity.x = (Rand_ZeroOne() - 0.5f) * 6.0f; + velocity.y = Rand_ZeroOne() * 10.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 6.0f; + + scaleIndex = (s32)(Rand_ZeroOne() * 111.1f) % 7; + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -100, 64, 40, 3, 0, sFragmentScales[scaleIndex], 0, 0, + 80, KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_KEEP, gCuttableShrubTipDL); + } +} + +void EnKusa_SpawnBugs(EnKusa* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 3; i++) { + Actor* bug = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_INSECT, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, Rand_ZeroOne() * 0xFFFF, 0, 1); + + if (bug == NULL) { + break; + } + } +} + +void EnKusa_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + EnKusa* this = (EnKusa*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); +} + +void EnKusa_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKusa* this = (EnKusa*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + this->actor.uncullZoneForward += 1000.0f; + } + + EnKusa_InitCollider(thisx, globalCtx); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + + if (this->actor.shape.rot.y == 0) { + s16 rand = Rand_ZeroFloat(0x10000); + + this->actor.world.rot.y = rand; + this->actor.home.rot.y = rand; + this->actor.shape.rot.y = rand; + } + + if (!EnKusa_SnapToFloor(this, globalCtx, 0.0f)) { + Actor_Kill(&this->actor); + return; + } + + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectIds[thisx->params & 3]); + + if (this->objBankIndex < 0) { + // "Bank danger!" + osSyncPrintf("Error : バンク危険! (arg_data 0x%04x)(%s %d)\n", thisx->params, "../z_en_kusa.c", 561); + Actor_Kill(&this->actor); + return; + } + + EnKusa_SetupWaitObject(this); +} + +void EnKusa_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnKusa* this = (EnKusa*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnKusa_SetupWaitObject(EnKusa* this) { + EnKusa_SetupAction(this, EnKusa_WaitObject); +} + +void EnKusa_WaitObject(EnKusa* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + if (this->actor.flags & ACTOR_FLAG_ENKUSA_CUT) { + EnKusa_SetupCut(this); + } else { + EnKusa_SetupMain(this); + } + + this->actor.draw = EnKusa_Draw; + this->actor.objBankIndex = this->objBankIndex; + this->actor.flags &= ~ACTOR_FLAG_4; + } +} + +void EnKusa_SetupMain(EnKusa* this) { + EnKusa_SetupAction(this, EnKusa_Main); + this->actor.flags &= ~ACTOR_FLAG_4; +} + +void EnKusa_Main(EnKusa* this, GlobalContext* globalCtx) { + s32 pad; + + if (Actor_HasParent(&this->actor, globalCtx)) { + EnKusa_SetupLiftedUp(this); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_PL_PULL_UP_PLANT); + } else if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + EnKusa_SpawnFragments(this, globalCtx); + EnKusa_DropCollectible(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_PLANT_BROKEN); + + if ((this->actor.params >> 4) & 1) { + EnKusa_SpawnBugs(this, globalCtx); + } + + if ((this->actor.params & 3) == ENKUSA_TYPE_0) { + Actor_Kill(&this->actor); + return; + } + + EnKusa_SetupCut(this); + this->actor.flags |= ACTOR_FLAG_ENKUSA_CUT; + } else { + if (!(this->collider.base.ocFlags1 & OC1_TYPE_PLAYER) && (this->actor.xzDistToPlayer > 12.0f)) { + this->collider.base.ocFlags1 |= OC1_TYPE_PLAYER; + } + + if (this->actor.xzDistToPlayer < 600.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->actor.xzDistToPlayer < 400.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F580(&this->actor, globalCtx); + } + } + } + } +} + +void EnKusa_SetupLiftedUp(EnKusa* this) { + EnKusa_SetupAction(this, EnKusa_LiftedUp); + this->actor.room = -1; + this->actor.flags |= ACTOR_FLAG_4; +} + +void EnKusa_LiftedUp(EnKusa* this, GlobalContext* globalCtx) { + if (Actor_HasNoParent(&this->actor, globalCtx)) { + this->actor.room = globalCtx->roomCtx.curRoom.num; + EnKusa_SetupFall(this); + this->actor.velocity.x = this->actor.speedXZ * Math_SinS(this->actor.world.rot.y); + this->actor.velocity.z = this->actor.speedXZ * Math_CosS(this->actor.world.rot.y); + this->actor.colChkInfo.mass = 240; + this->actor.gravity = -0.1f; + EnKusa_UpdateVelY(this); + EnKusa_RandScaleVecToZero(&this->actor.velocity, 0.005f); + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.5f, 35.0f, 0.0f, 0xC5); + this->actor.gravity = -3.2f; + } +} + +void EnKusa_SetupFall(EnKusa* this) { + EnKusa_SetupAction(this, EnKusa_Fall); + rotSpeedXtarget = -0xBB8; + rotSpeedYtarget = (Rand_ZeroOne() - 0.5f) * 1600.0f; + rotSpeedX = 0; + rotSpeedY = 0; +} + +void EnKusa_Fall(EnKusa* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f contactPos; + + if (this->actor.bgCheckFlags & 0xB) { + if (!(this->actor.bgCheckFlags & 0x20)) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_PLANT_BROKEN); + } + EnKusa_SpawnFragments(this, globalCtx); + EnKusa_DropCollectible(this, globalCtx); + switch (this->actor.params & 3) { + case ENKUSA_TYPE_0: + case ENKUSA_TYPE_2: + Actor_Kill(&this->actor); + break; + + case ENKUSA_TYPE_1: + EnKusa_SetupUprootedWaitRegrow(this); + break; + } + return; + } + + if (this->actor.bgCheckFlags & 0x40) { + contactPos.x = this->actor.world.pos.x; + contactPos.y = this->actor.world.pos.y + this->actor.yDistToWater; + contactPos.z = this->actor.world.pos.z; + EffectSsGSplash_Spawn(globalCtx, &contactPos, NULL, NULL, 0, 400); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 150, 650, 0); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 400, 800, 4); + EffectSsGRipple_Spawn(globalCtx, &contactPos, 500, 1100, 8); + this->actor.minVelocityY = -3.0f; + rotSpeedX >>= 1; + rotSpeedXtarget >>= 1; + rotSpeedY >>= 1; + rotSpeedYtarget >>= 1; + this->actor.bgCheckFlags &= ~0x40; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_DIVE_INTO_WATER_L); + } + + EnKusa_UpdateVelY(this); + Math_StepToS(&rotSpeedX, rotSpeedXtarget, 0x1F4); + Math_StepToS(&rotSpeedY, rotSpeedYtarget, 0xAA); + this->actor.shape.rot.x += rotSpeedX; + this->actor.shape.rot.y += rotSpeedY; + EnKusa_RandScaleVecToZero(&this->actor.velocity, 0.05f); + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 7.5f, 35.0f, 0.0f, 0xC5); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnKusa_SetupCut(EnKusa* this) { + switch (this->actor.params & 3) { + case ENKUSA_TYPE_2: + EnKusa_SetupAction(this, EnKusa_DoNothing); + break; + case ENKUSA_TYPE_1: + EnKusa_SetupAction(this, EnKusa_CutWaitRegrow); + break; + } +} + +void EnKusa_CutWaitRegrow(EnKusa* this, GlobalContext* globalCtx) { + if (this->timer >= 120) { + EnKusa_SetupRegrow(this); + } +} + +void EnKusa_DoNothing(EnKusa* this, GlobalContext* globalCtx) { +} + +void EnKusa_SetupUprootedWaitRegrow(EnKusa* this) { + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.y = this->actor.home.pos.y - 9.0f; + this->actor.world.pos.z = this->actor.home.pos.z; + EnKusa_SetScaleSmall(this); + this->actor.shape.rot = this->actor.home.rot; + EnKusa_SetupAction(this, EnKusa_UprootedWaitRegrow); +} + +void EnKusa_UprootedWaitRegrow(EnKusa* this, GlobalContext* globalCtx) { + if (this->timer > 120) { + if (Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.6f)) { + if (this->timer >= 170) { + EnKusa_SetupRegrow(this); + } + } + } +} + +void EnKusa_SetupRegrow(EnKusa* this) { + EnKusa_SetupAction(this, EnKusa_Regrow); + EnKusa_SetScaleSmall(this); + this->actor.shape.rot = this->actor.home.rot; + this->actor.flags &= ~ACTOR_FLAG_ENKUSA_CUT; +} + +void EnKusa_Regrow(EnKusa* this, GlobalContext* globalCtx) { + s32 isFullyGrown = true; + + isFullyGrown &= Math_StepToF(&this->actor.scale.y, 0.4f, 0.014f); + isFullyGrown &= Math_StepToF(&this->actor.scale.x, 0.4f, 0.011f); + this->actor.scale.z = this->actor.scale.x; + + if (isFullyGrown) { + Actor_SetScale(&this->actor, 0.4f); + EnKusa_SetupMain(this); + this->collider.base.ocFlags1 &= ~OC1_TYPE_PLAYER; + } +} + +void EnKusa_Update(Actor* thisx, GlobalContext* globalCtx) { + EnKusa* this = (EnKusa*)thisx; + + this->timer++; + + this->actionFunc(this, globalCtx); + + if (this->actor.flags & ACTOR_FLAG_ENKUSA_CUT) { + this->actor.shape.yOffset = -6.25f; + } else { + this->actor.shape.yOffset = 0.0f; + } +} + +void EnKusa_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* dLists[] = { gFieldBushDL, object_kusa_DL_000140, object_kusa_DL_000140 }; + EnKusa* this = (EnKusa*)thisx; + + if (this->actor.flags & ACTOR_FLAG_ENKUSA_CUT) { + Gfx_DrawDListOpa(globalCtx, object_kusa_DL_0002E0); + } else { + Gfx_DrawDListOpa(globalCtx, dLists[thisx->params & 3]); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.h b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.h new file mode 100644 index 000000000..a5ef48687 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_KUSA_H +#define Z_EN_KUSA_H + +#include "ultra64.h" +#include "global.h" + +struct EnKusa; + +typedef void (*EnKusaActionFunc)(struct EnKusa*, GlobalContext*); + +typedef enum { + /* 0 */ ENKUSA_TYPE_0, + /* 1 */ ENKUSA_TYPE_1, + /* 2 */ ENKUSA_TYPE_2 +} EnKusaType; + +typedef struct EnKusa { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnKusaActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; + /* 0x019C */ s16 timer; + /* 0x019E */ s8 objBankIndex; +} EnKusa; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c b/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c new file mode 100644 index 000000000..1161ba5e3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.c @@ -0,0 +1,507 @@ +/* + * File: z_en_kz.c + * Overlay: ovl_En_Kz + * Description: King Zora + */ + +#include "z_en_kz.h" +#include "objects/object_kz/object_kz.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnKz_Init(Actor* thisx, GlobalContext* globalCtx); +void EnKz_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnKz_Update(Actor* thisx, GlobalContext* globalCtx); +void EnKz_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnKz_PreMweepWait(EnKz* this, GlobalContext* globalCtx); +void EnKz_SetupMweep(EnKz* this, GlobalContext* globalCtx); +void EnKz_Mweep(EnKz* this, GlobalContext* globalCtx); +void EnKz_StopMweep(EnKz* this, GlobalContext* globalCtx); +void EnKz_Wait(EnKz* this, GlobalContext* globalCtx); +void EnKz_SetupGetItem(EnKz* this, GlobalContext* globalCtx); +void EnKz_StartTimer(EnKz* this, GlobalContext* globalCtx); + +const ActorInit En_Kz_InitVars = { + ACTOR_EN_KZ, + ACTORCAT_NPC, + FLAGS, + OBJECT_KZ, + sizeof(EnKz), + (ActorFunc)EnKz_Init, + (ActorFunc)EnKz_Destroy, + (ActorFunc)EnKz_Update, + (ActorFunc)EnKz_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 80, 120, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENKZ_ANIM_0, + /* 1 */ ENKZ_ANIM_1, + /* 2 */ ENKZ_ANIM_2 +} EnKzAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gKzIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gKzIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gKzMweepAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, +}; + +u16 EnKz_GetTextNoMaskChild(GlobalContext* globalCtx, EnKz* this) { + Player* player = GET_PLAYER(globalCtx); + + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402B; + } else if (gSaveContext.eventChkInf[3] & 8) { + return 0x401C; + } else { + player->exchangeItemId = EXCH_ITEM_LETTER_RUTO; + return 0x401A; + } +} + +u16 EnKz_GetTextNoMaskAdult(GlobalContext* globalCtx, EnKz* this) { + Player* player = GET_PLAYER(globalCtx); + + if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_FROG) { + if (!(gSaveContext.infTable[19] & 0x200)) { + if (CHECK_OWNED_EQUIP(EQUIP_TUNIC, 2)) { + return 0x401F; + } else { + return 0x4012; + } + } else { + return CHECK_QUEST_ITEM(QUEST_SONG_SERENADE) ? 0x4045 : 0x401A; + } + } else { + player->exchangeItemId = EXCH_ITEM_PRESCRIPTION; + return 0x4012; + } +} + +u16 EnKz_GetText(GlobalContext* globalCtx, Actor* thisx) { + EnKz* this = (EnKz*)thisx; + u16 reactionText = Text_GetFaceReaction(globalCtx, 0x1E); + + if (reactionText != 0) { + return reactionText; + } + + if (LINK_IS_ADULT) { + return EnKz_GetTextNoMaskAdult(globalCtx, this); + } else { + return EnKz_GetTextNoMaskChild(globalCtx, this); + } +} + +s16 func_80A9C6C0(GlobalContext* globalCtx, Actor* thisx) { + EnKz* this = (EnKz*)thisx; + s16 ret = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_DONE: + ret = 0; + switch (this->actor.textId) { + case 0x4012: + gSaveContext.infTable[19] |= 0x200; + ret = 2; + break; + case 0x401B: + ret = !Message_ShouldAdvance(globalCtx) ? 1 : 2; + break; + case 0x401F: + gSaveContext.infTable[19] |= 0x200; + break; + } + break; + case TEXT_STATE_DONE_FADING: + if (this->actor.textId != 0x4014) { + if (this->actor.textId == 0x401B && !this->sfxPlayed) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->sfxPlayed = true; + } + } else if (!this->sfxPlayed) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->sfxPlayed = true; + } + break; + case TEXT_STATE_CHOICE: + if (!Message_ShouldAdvance(globalCtx)) { + break; + } + if (this->actor.textId == 0x4014) { + if (globalCtx->msgCtx.choiceIndex == 0) { + EnKz_SetupGetItem(this, globalCtx); + ret = 2; + } else { + this->actor.textId = 0x4016; + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + } + break; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + ret = 2; + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_CLOSING: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + break; + } + return ret; +} + +void EnKz_UpdateEyes(EnKz* this) { + if (DECR(this->blinkTimer) == 0) { + this->eyeIdx += 1; + if (this->eyeIdx >= 3) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeIdx = 0; + } + } +} + +s32 func_80A9C95C(GlobalContext* globalCtx, EnKz* this, s16* arg2, f32 unkf, callback1_800343CC callback1, + callback2_800343CC callback2) { + Player* player = GET_PLAYER(globalCtx); + s16 sp32; + s16 sp30; + f32 xzDistToPlayer; + f32 yaw; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + *arg2 = 1; + return 1; + } + + if (*arg2 != 0) { + *arg2 = callback2(globalCtx, &this->actor); + return 0; + } + + yaw = Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos); + yaw -= this->actor.shape.rot.y; + if ((fabsf(yaw) > 1638.0f) || (this->actor.xzDistToPlayer < 265.0f)) { + this->actor.flags &= ~ACTOR_FLAG_0; + return 0; + } + + this->actor.flags |= ACTOR_FLAG_0; + + Actor_GetScreenPos(globalCtx, &this->actor, &sp32, &sp30); + if (!((sp32 >= -30) && (sp32 < 361) && (sp30 >= -10) && (sp30 < 241))) { + return 0; + } + + xzDistToPlayer = this->actor.xzDistToPlayer; + this->actor.xzDistToPlayer = Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos); + if (func_8002F2CC(&this->actor, globalCtx, unkf) == 0) { + this->actor.xzDistToPlayer = xzDistToPlayer; + return 0; + } + this->actor.xzDistToPlayer = xzDistToPlayer; + this->actor.textId = callback1(globalCtx, &this->actor); + + return 0; +} + +void func_80A9CB18(EnKz* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (func_80A9C95C(globalCtx, this, &this->unk_1E0.unk_00, 340.0f, EnKz_GetText, func_80A9C6C0)) { + if ((this->actor.textId == 0x401A) && !(gSaveContext.eventChkInf[3] & 8)) { + if (func_8002F368(globalCtx) == EXCH_ITEM_LETTER_RUTO) { + this->actor.textId = 0x401B; + this->sfxPlayed = false; + } else { + this->actor.textId = 0x401A; + } + player->actor.textId = this->actor.textId; + return; + } + + if (LINK_IS_ADULT) { + if ((INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_PRESCRIPTION) && + (func_8002F368(globalCtx) == EXCH_ITEM_PRESCRIPTION)) { + this->actor.textId = 0x4014; + this->sfxPlayed = false; + player->actor.textId = this->actor.textId; + this->isTrading = true; + return; + } + + this->isTrading = false; + if (gSaveContext.infTable[19] & 0x200) { + this->actor.textId = CHECK_QUEST_ITEM(QUEST_SONG_SERENADE) ? 0x4045 : 0x401A; + player->actor.textId = this->actor.textId; + } else { + this->actor.textId = CHECK_OWNED_EQUIP(EQUIP_TUNIC, 2) ? 0x401F : 0x4012; + player->actor.textId = this->actor.textId; + } + } + } +} + +s32 EnKz_FollowPath(EnKz* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* pointPos; + f32 pathDiffX; + f32 pathDiffZ; + + if ((this->actor.params & 0xFF00) == 0xFF00) { + return 0; + } + + path = &globalCtx->setupPathList[(this->actor.params & 0xFF00) >> 8]; + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos += this->waypoint; + + pathDiffX = pointPos->x - this->actor.world.pos.x; + pathDiffZ = pointPos->z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.world.rot.y, (Math_FAtan2F(pathDiffX, pathDiffZ) * (0x8000 / M_PI)), 0xA, 0x3E8, 1); + + if ((SQ(pathDiffX) + SQ(pathDiffZ)) < 10.0f) { + this->waypoint++; + if (this->waypoint >= path->count) { + this->waypoint = 0; + } + return 1; + } + return 0; +} + +s32 EnKz_SetMovedPos(EnKz* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* lastPointPos; + + if ((this->actor.params & 0xFF00) == 0xFF00) { + return 0; + } + + path = &globalCtx->setupPathList[(this->actor.params & 0xFF00) >> 8]; + lastPointPos = SEGMENTED_TO_VIRTUAL(path->points); + lastPointPos += path->count - 1; + + this->actor.world.pos.x = lastPointPos->x; + this->actor.world.pos.y = lastPointPos->y; + this->actor.world.pos.z = lastPointPos->z; + + return 1; +} + +void EnKz_Init(Actor* thisx, GlobalContext* globalCtx) { + EnKz* this = (EnKz*)thisx; + s32 pad; + + SkelAnime_InitFlex(globalCtx, &this->skelanime, &gKzSkel, NULL, this->jointTable, this->morphTable, 12); + ActorShape_Init(&this->actor.shape, 0.0, NULL, 0.0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + Actor_SetScale(&this->actor, 0.01); + this->actor.targetMode = 3; + this->unk_1E0.unk_00 = 0; + Animation_ChangeByInfo(&this->skelanime, sAnimationInfo, ENKZ_ANIM_0); + + if (gSaveContext.eventChkInf[3] & 8) { + EnKz_SetMovedPos(this, globalCtx); + } + + if (LINK_IS_ADULT) { + if (!(gSaveContext.infTable[19] & 0x100)) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BG_ICE_SHELTER, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, + 0x04FF); + } + this->actionFunc = EnKz_Wait; + } else { + this->actionFunc = EnKz_PreMweepWait; + } +} + +void EnKz_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnKz* this = (EnKz*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnKz_PreMweepWait(EnKz* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 2) { + Animation_ChangeByInfo(&this->skelanime, sAnimationInfo, ENKZ_ANIM_2); + this->unk_1E0.unk_00 = 0; + this->actionFunc = EnKz_SetupMweep; + } else { + func_80034F54(globalCtx, this->unk_2A6, this->unk_2BE, 12); + } +} + +void EnKz_SetupMweep(EnKz* this, GlobalContext* globalCtx) { + Vec3f unused = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + Vec3f initPos; + + this->cutsceneCamera = Gameplay_CreateSubCamera(globalCtx); + this->gameplayCamera = globalCtx->activeCamera; + Gameplay_ChangeCameraStatus(globalCtx, this->gameplayCamera, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->cutsceneCamera, CAM_STAT_ACTIVE); + pos = this->actor.world.pos; + initPos = this->actor.home.pos; + pos.y += 60.0f; + initPos.y += -100.0f; + initPos.z += 260.0f; + Gameplay_CameraSetAtEye(globalCtx, this->cutsceneCamera, &pos, &initPos); + func_8002DF54(globalCtx, &this->actor, 8); + this->actor.speedXZ = 0.1f; + this->actionFunc = EnKz_Mweep; +} + +void EnKz_Mweep(EnKz* this, GlobalContext* globalCtx) { + Vec3f unused = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + Vec3f initPos; + + pos = this->actor.world.pos; + initPos = this->actor.home.pos; + pos.y += 60.0f; + initPos.y += -100.0f; + initPos.z += 260.0f; + Gameplay_CameraSetAtEye(globalCtx, this->cutsceneCamera, &pos, &initPos); + if ((EnKz_FollowPath(this, globalCtx) == 1) && (this->waypoint == 0)) { + Animation_ChangeByInfo(&this->skelanime, sAnimationInfo, ENKZ_ANIM_1); + Inventory_ReplaceItem(globalCtx, ITEM_LETTER_RUTO, ITEM_BOTTLE); + EnKz_SetMovedPos(this, globalCtx); + gSaveContext.eventChkInf[3] |= 8; + this->actor.speedXZ = 0.0; + this->actionFunc = EnKz_StopMweep; + } + if (this->skelanime.curFrame == 13.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_KZ_MOVE); + } +} + +void EnKz_StopMweep(EnKz* this, GlobalContext* globalCtx) { + Gameplay_ChangeCameraStatus(globalCtx, this->gameplayCamera, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, this->cutsceneCamera); + func_8002DF54(globalCtx, &this->actor, 7); + this->actionFunc = EnKz_Wait; +} + +void EnKz_Wait(EnKz* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 2) { + this->actionFunc = EnKz_SetupGetItem; + EnKz_SetupGetItem(this, globalCtx); + } else { + func_80034F54(globalCtx, this->unk_2A6, this->unk_2BE, 12); + } +} + +void EnKz_SetupGetItem(EnKz* this, GlobalContext* globalCtx) { + s32 getItemId; + f32 xzRange; + f32 yRange; + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->unk_1E0.unk_00 = 1; + this->actionFunc = EnKz_StartTimer; + } else { + getItemId = this->isTrading == true ? GI_FROG : GI_TUNIC_ZORA; + yRange = fabsf(this->actor.yDistToPlayer) + 1.0f; + xzRange = this->actor.xzDistToPlayer + 1.0f; + func_8002F434(&this->actor, globalCtx, getItemId, xzRange, yRange); + } +} + +void EnKz_StartTimer(EnKz* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_FROG) { + func_80088AA0(180); // start timer2 with 3 minutes + gSaveContext.eventInf[1] &= ~1; + } + this->unk_1E0.unk_00 = 0; + this->actionFunc = EnKz_Wait; + } +} + +void EnKz_Update(Actor* thisx, GlobalContext* globalCtx) { + EnKz* this = (EnKz*)thisx; + s32 pad; + + if (LINK_IS_ADULT && !(gSaveContext.infTable[19] & 0x100)) { + gSaveContext.infTable[19] |= 0x100; + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelanime); + EnKz_UpdateEyes(this); + Actor_MoveForward(&this->actor); + if (this->actionFunc != EnKz_StartTimer) { + func_80A9CB18(this, globalCtx); + } + this->actionFunc(this, globalCtx); +} + +s32 EnKz_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnKz* this = (EnKz*)thisx; + + if (limbIndex == 8 || limbIndex == 9 || limbIndex == 10) { + rot->y += Math_SinS(this->unk_2A6[limbIndex]) * 200.0f; + rot->z += Math_CosS(this->unk_2BE[limbIndex]) * 200.0f; + } + if (limbIndex) {} + return false; +} + +void EnKz_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnKz* this = (EnKz*)thisx; + Vec3f mult = { 2600.0f, 0.0f, 0.0f }; + + if (limbIndex == 11) { + Matrix_MultVec3f(&mult, &this->actor.focus.pos); + } +} + +void EnKz_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sEyeSegments[] = { + gKzEyeOpenTex, + gKzEyeHalfTex, + gKzEyeClosedTex, + }; + EnKz* this = (EnKz*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_kz.c", 1259); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeSegments[this->eyeIdx])); + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, this->skelanime.dListCount, + EnKz_OverrideLimbDraw, EnKz_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_kz.c", 1281); +} diff --git a/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.h b/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.h new file mode 100644 index 000000000..67491acee --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Kz/z_en_kz.h @@ -0,0 +1,31 @@ +#ifndef Z_EN_KZ_H +#define Z_EN_KZ_H + +#include "ultra64.h" +#include "global.h" + +struct EnKz; + +typedef void (*EnKzActionFunc)(struct EnKz*, GlobalContext*); + +typedef struct EnKz { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelanime; + /* 0x0190 */ EnKzActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ u8 sfxPlayed; + /* 0x0209 */ u8 isTrading; + /* 0x020A */ s16 waypoint; + /* 0x020C */ s16 blinkTimer; + /* 0x020E */ char unk_20E[2]; + /* 0x0210 */ s16 eyeIdx; + /* 0x0212 */ s16 cutsceneCamera; + /* 0x0214 */ s16 gameplayCamera; + /* 0x0216 */ Vec3s jointTable[12]; + /* 0x025E */ Vec3s morphTable[12]; + /* 0x02A6 */ s16 unk_2A6[12]; + /* 0x02BE */ s16 unk_2BE[12]; +} EnKz; // size = 0x02D8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Light/z_en_light.c b/soh/src/overlays/actors/ovl_En_Light/z_en_light.c new file mode 100644 index 000000000..ee3699dcd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Light/z_en_light.c @@ -0,0 +1,200 @@ +/* + * File: z_en_light.c + * Overlay: ovl_En_Light + * Description: Flame + */ + +#include "z_en_light.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" + +#define FLAGS 0 + +void EnLight_Init(Actor* thisx, GlobalContext* globalCtx); +void EnLight_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnLight_Update(Actor* thisx, GlobalContext* globalCtx); +void EnLight_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnLight_UpdateSwitch(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Light_InitVars = { + ACTOR_EN_LIGHT, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnLight), + (ActorFunc)EnLight_Init, + (ActorFunc)EnLight_Destroy, + (ActorFunc)EnLight_Update, + (ActorFunc)EnLight_Draw, + NULL, +}; + +typedef struct { + /* 0x00 */ Color_RGBA8 primColor; + /* 0x04 */ Color_RGB8 envColor; + /* 0x07 */ u8 scale; +} FlameParams; + +static FlameParams D_80A9E840[] = { + { { 255, 200, 0, 255 }, { 255, 0, 0 }, 75 }, { { 255, 200, 0, 255 }, { 255, 0, 0 }, 75 }, + { { 0, 170, 255, 255 }, { 0, 0, 255 }, 75 }, { { 170, 255, 0, 255 }, { 0, 150, 0 }, 75 }, + { { 255, 200, 0, 255 }, { 255, 0, 0 }, 40 }, { { 255, 200, 0, 255 }, { 255, 0, 0 }, 75 }, + { { 170, 255, 0, 255 }, { 0, 150, 0 }, 75 }, { { 0, 170, 255, 255 }, { 0, 0, 255 }, 75 }, + { { 255, 0, 170, 255 }, { 200, 0, 0 }, 75 }, { { 255, 255, 170, 255 }, { 255, 50, 0 }, 75 }, + { { 255, 255, 170, 255 }, { 255, 255, 0 }, 75 }, { { 255, 255, 170, 255 }, { 100, 255, 0 }, 75 }, + { { 255, 170, 255, 255 }, { 255, 0, 100 }, 75 }, { { 255, 170, 255, 255 }, { 100, 0, 255 }, 75 }, + { { 170, 255, 255, 255 }, { 0, 0, 255 }, 75 }, { { 170, 255, 255, 255 }, { 0, 150, 255 }, 75 }, +}; + +void EnLight_Init(Actor* thisx, GlobalContext* globalCtx) { + EnLight* this = (EnLight*)thisx; + s16 yOffset; + + if (gSaveContext.gameMode == 3) { + // special case for the credits + yOffset = (this->actor.params < 0) ? 1 : 40; + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, yOffset + (s16)this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 180, -1); + } else { + yOffset = (this->actor.params < 0) ? 1 : 40; + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, yOffset + (s16)this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 180, -1); + } + + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Actor_SetScale(&this->actor, D_80A9E840[this->actor.params & 0xF].scale * 0.0001f); + this->timer = (s32)(Rand_ZeroOne() * 255.0f); + + if ((this->actor.params & 0x400) != 0) { + this->actor.update = EnLight_UpdateSwitch; + } +} + +void EnLight_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnLight* this = (EnLight*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); +} + +void EnLight_UpdatePosRot(EnLight* this, GlobalContext* globalCtx) { + // update yaw for billboard effect + this->actor.shape.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000; + + if (this->actor.parent != NULL) { + Math_Vec3f_Copy(&this->actor.world.pos, &(this->actor.parent)->world.pos); + this->actor.world.pos.y += 17.0f; + } + + this->timer++; +} + +void EnLight_Update(Actor* thisx, GlobalContext* globalCtx) { + f32 intensity; + FlameParams* flameParams; + s16 radius; + EnLight* this = (EnLight*)thisx; + + flameParams = &D_80A9E840[this->actor.params & 0xF]; + intensity = (Rand_ZeroOne() * 0.5f) + 0.5f; + radius = (this->actor.params < 0) ? 100 : 300; + Lights_PointSetColorAndRadius(&this->lightInfo, (flameParams->primColor.r * intensity), + (flameParams->primColor.g * intensity), (flameParams->primColor.b * intensity), + radius); + EnLight_UpdatePosRot(this, globalCtx); + + if (this->actor.params >= 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_TORCH - SFX_FLAG); + } +} + +void EnLight_UpdateSwitch(Actor* thisx, GlobalContext* globalCtx) { + f32 intensity; + FlameParams* flameParams; + EnLight* this = (EnLight*)thisx; + f32 scale; + + flameParams = &D_80A9E840[this->actor.params & 0xF]; + scale = this->actor.scale.x / ((f32)flameParams->scale * 0.0001); + + if ((this->actor.params & 0x800) != 0) { + if (Flags_GetSwitch(globalCtx, (this->actor.params & 0x3F0) >> 4)) { + Math_StepToF(&scale, 1.0f, 0.05f); + } else { + if (scale < 0.1f) { + Actor_SetScale(&this->actor, 0.0f); + return; + } + Math_StepToF(&scale, 0.0f, 0.05f); + } + } else { + if (Flags_GetSwitch(globalCtx, (this->actor.params & 0x3F0) >> 4)) { + if (scale < 0.1f) { + Actor_SetScale(&this->actor, 0.0f); + return; + } + Math_StepToF(&scale, 0.0f, 0.05f); + } else { + Math_StepToF(&scale, 1.0f, 0.05f); + } + } + + Actor_SetScale(&this->actor, ((f32)flameParams->scale * 0.0001) * scale); + intensity = (Rand_ZeroOne() * 0.5f) + 0.5f; + Lights_PointSetColorAndRadius(&this->lightInfo, (flameParams->primColor.r * intensity), + (flameParams->primColor.g * intensity), (flameParams->primColor.b * intensity), + 300.0f * scale); + EnLight_UpdatePosRot(this, globalCtx); + + if (this->actor.params >= 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_TORCH - SFX_FLAG); + } +} + +void EnLight_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnLight* this = (EnLight*)thisx; + s32 pad; + FlameParams* flameParams; + Gfx* dList; + + if (1) {} + + flameParams = &D_80A9E840[this->actor.params & 0xF]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_light.c", 441); + + func_80093D84(globalCtx->state.gfxCtx); + + if (this->actor.params >= 0) { + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, (this->timer * -20) & 511, 32, 128)); + + dList = gEffFire1DL; + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, flameParams->primColor.r, flameParams->primColor.g, + flameParams->primColor.b, flameParams->primColor.a); + gDPSetEnvColor(POLY_XLU_DISP++, flameParams->envColor.r, flameParams->envColor.g, flameParams->envColor.b, 0); + } else { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 16, 32, 1, ((this->timer * 2) & 63), + (this->timer * -6) & 127 * 1, 16, 32)); + + dList = gUnusedCandleDL; + gDPSetPrimColor(POLY_XLU_DISP++, 0xC0, 0xC0, 255, 200, 0, 0); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + } + + Matrix_RotateY((s16)((Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y) + 0x8000) * + (M_PI / 32768.0f), + MTXMODE_APPLY); + + if (this->actor.params & 1) { + Matrix_RotateY(M_PI, MTXMODE_APPLY); + } + + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_light.c", 488), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, dList); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_light.c", 491); +} diff --git a/soh/src/overlays/actors/ovl_En_Light/z_en_light.h b/soh/src/overlays/actors/ovl_En_Light/z_en_light.h new file mode 100644 index 000000000..1cbf786b0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Light/z_en_light.h @@ -0,0 +1,16 @@ +#ifndef Z_EN_LIGHT_H +#define Z_EN_LIGHT_H + +#include "ultra64.h" +#include "global.h" + +struct EnLight; + +typedef struct EnLight { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 timer; + /* 0x0150 */ LightNode* lightNode; + /* 0x0154 */ LightInfo lightInfo; +} EnLight; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Lightbox/z_en_lightbox.c b/soh/src/overlays/actors/ovl_En_Lightbox/z_en_lightbox.c new file mode 100644 index 000000000..d54e87599 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Lightbox/z_en_lightbox.c @@ -0,0 +1,113 @@ +/* + * File: z_en_lightbox.c + * Overlay: ovl_En_Lightbox + * Description: + */ + +#include "z_en_lightbox.h" +#include "objects/object_lightbox/object_lightbox.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnLightbox_Init(Actor* thisx, GlobalContext* globalCtx); +void EnLightbox_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnLightbox_Update(Actor* thisx, GlobalContext* globalCtx); +void EnLightbox_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Lightbox_InitVars = { + ACTOR_EN_LIGHTBOX, + ACTORCAT_PROP, + FLAGS, + OBJECT_LIGHTBOX, + sizeof(EnLightbox), + (ActorFunc)EnLightbox_Init, + (ActorFunc)EnLightbox_Destroy, + (ActorFunc)EnLightbox_Update, + (ActorFunc)EnLightbox_Draw, + NULL, +}; + +void EnLightbox_Init(Actor* thisx, GlobalContext* globalCtx) { + CollisionHeader* colHeader = NULL; + EnLightbox* this = (EnLightbox*)thisx; + s32 pad[4]; + + switch (thisx->params) { + case 0: + Actor_SetScale(thisx, 0.025f); + break; + case 1: + Actor_SetScale(thisx, 0.05f); + break; + case 2: + Actor_SetScale(thisx, 0.075f); + break; + case 3: + Actor_SetScale(thisx, 0.1f); + default: + break; + } + + thisx->focus.pos = thisx->world.pos; + thisx->colChkInfo.cylRadius = 30; + thisx->colChkInfo.cylHeight = 50; + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 6.0f); + this->dyna.unk_160 = 0; + this->dyna.unk_15C = 0; + thisx->targetMode = 0; + thisx->gravity = -2.0f; + CollisionHeader_GetVirtual(&object_lightbox_Col_001F10, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); +} + +void EnLightbox_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnLightbox* this = (EnLightbox*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void EnLightbox_Update(Actor* thisx, GlobalContext* globalCtx) { + EnLightbox* this = (EnLightbox*)thisx; + + if (this->dyna.unk_162 != 0) { + if (Actor_HasNoParent(thisx, globalCtx)) { + this->dyna.unk_162 = 0; + } + } else { + if (Actor_HasParent(thisx, globalCtx)) { + this->dyna.unk_162++; + } else { + if (thisx->speedXZ) { + if (thisx->bgCheckFlags & 8) { + thisx->world.rot.y = (thisx->world.rot.y + thisx->wallYaw) - thisx->world.rot.y; + Audio_PlaySoundGeneral(NA_SE_EV_BOMB_BOUND, &thisx->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + thisx->speedXZ *= 0.7f; + thisx->bgCheckFlags &= ~0x8; + } + } + + if ((thisx->bgCheckFlags & 1) == 0) { + Math_StepToF(&thisx->speedXZ, 0, IREG(57) / 100.0f); + } else { + Math_StepToF(&thisx->speedXZ, 0, IREG(58) / 100.0f); + if ((thisx->bgCheckFlags & 2) && (thisx->velocity.y < IREG(59) / 100.0f)) { + Audio_PlaySoundGeneral(NA_SE_EV_BOMB_BOUND, &thisx->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + thisx->velocity.y *= IREG(60) / 100.0f; + thisx->bgCheckFlags &= ~0x1; + } else { + func_8002F580(thisx, globalCtx); + } + } + } + } + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, thisx->colChkInfo.cylHeight, thisx->colChkInfo.cylRadius, + thisx->colChkInfo.cylRadius, 0x1D); + thisx->focus.pos = thisx->world.pos; +} + +void EnLightbox_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, object_lightbox_DL_000B70); +} diff --git a/soh/src/overlays/actors/ovl_En_Lightbox/z_en_lightbox.h b/soh/src/overlays/actors/ovl_En_Lightbox/z_en_lightbox.h new file mode 100644 index 000000000..e24afd44e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Lightbox/z_en_lightbox.h @@ -0,0 +1,13 @@ +#ifndef Z_EN_LIGHTBOX_H +#define Z_EN_LIGHTBOX_H + +#include "ultra64.h" +#include "global.h" + +struct EnLightbox; + +typedef struct EnLightbox { + /* 0x0000 */ DynaPolyActor dyna; +} EnLightbox; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_M_Fire1/z_en_m_fire1.c b/soh/src/overlays/actors/ovl_En_M_Fire1/z_en_m_fire1.c new file mode 100644 index 000000000..fe4240d33 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_M_Fire1/z_en_m_fire1.c @@ -0,0 +1,76 @@ +/* + * File: z_en_m_fire1.c + * Overlay: ovl_En_M_Fire1 + * Description: Deku Nut Hitbox + */ + +#include "z_en_m_fire1.h" + +#define FLAGS 0 + +void EnMFire1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMFire1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMFire1_Update(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_M_Fire1_InitVars = { + ACTOR_EN_M_FIRE1, + ACTORCAT_MISC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnMFire1), + (ActorFunc)EnMFire1_Init, + (ActorFunc)EnMFire1_Destroy, + (ActorFunc)EnMFire1_Update, + NULL, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000001, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 200, 200, 0, { 0 } }, +}; + +void EnMFire1_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMFire1* this = (EnMFire1*)thisx; + s32 pad; + + if (this->actor.params < 0) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ITEMACTION); + } + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); +} + +void EnMFire1_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMFire1* this = (EnMFire1*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnMFire1_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMFire1* this = (EnMFire1*)thisx; + s32 pad; + + if (Math_StepToF(&this->timer, 1.0f, 0.2f)) { + Actor_Kill(&this->actor); + } else { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} diff --git a/soh/src/overlays/actors/ovl_En_M_Fire1/z_en_m_fire1.h b/soh/src/overlays/actors/ovl_En_M_Fire1/z_en_m_fire1.h new file mode 100644 index 000000000..10d3e3fef --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_M_Fire1/z_en_m_fire1.h @@ -0,0 +1,15 @@ +#ifndef Z_EN_M_FIRE1_H +#define Z_EN_M_FIRE1_H + +#include "ultra64.h" +#include "global.h" + +struct EnMFire1; + +typedef struct EnMFire1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ f32 timer; +} EnMFire1; // size = 0x019C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c b/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c new file mode 100644 index 000000000..3fb412dea --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c @@ -0,0 +1,397 @@ +#include "z_en_m_thunder.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS 0 + +void EnMThunder_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMThunder_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMThunder_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMThunder_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80A9F314(GlobalContext* globalCtx, f32 arg1); +void func_80A9F408(EnMThunder* this, GlobalContext* globalCtx); +void func_80A9F9B4(EnMThunder* this, GlobalContext* globalCtx); + +const ActorInit En_M_Thunder_InitVars = { + ACTOR_EN_M_THUNDER, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnMThunder), + (ActorFunc)EnMThunder_Init, + (ActorFunc)EnMThunder_Destroy, + (ActorFunc)EnMThunder_Update, + (ActorFunc)EnMThunder_Draw, + NULL, +}; + +static ColliderCylinderInit D_80AA0420 = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000001, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 200, 200, 0, { 0, 0, 0 } }, +}; + +static u32 D_80AA044C[] = { 0x01000000, 0x00400000, 0x00800000 }; +static u32 D_80AA0458[] = { 0x08000000, 0x02000000, 0x04000000 }; + +static u16 sSfxIds[] = { + NA_SE_IT_ROLLING_CUT_LV2, + NA_SE_IT_ROLLING_CUT_LV1, + NA_SE_IT_ROLLING_CUT_LV2, + NA_SE_IT_ROLLING_CUT_LV1, +}; + +// Setup action +void func_80A9EFE0(EnMThunder* this, EnMThunderActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnMThunder_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnMThunder* this = (EnMThunder*)thisx; + Player* player = GET_PLAYER(globalCtx); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &D_80AA0420); + this->unk_1C7 = (this->actor.params & 0xFF) - 1; + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 255, 255, 255, 0); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + this->collider.dim.radius = 0; + this->collider.dim.height = 40; + this->collider.dim.yShift = -20; + this->unk_1C4 = 8; + this->unk_1B4 = 0.0f; + this->actor.world.pos = player->bodyPartsPos[0]; + this->unk_1AC = 0.0f; + this->unk_1BC = 0.0f; + this->actor.shape.rot.y = player->actor.shape.rot.y + 0x8000; + this->actor.room = -1; + Actor_SetScale(&this->actor, 0.1f); + this->unk_1CA = 0; + + if (player->stateFlags2 & 0x20000) { + if (!gSaveContext.magicAcquired || gSaveContext.unk_13F0 || + (((this->actor.params & 0xFF00) >> 8) && + !(func_80087708(globalCtx, (this->actor.params & 0xFF00) >> 8, 0)))) { + Audio_PlaySoundGeneral(NA_SE_IT_ROLLING_CUT, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_SWING_HARD, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Actor_Kill(&this->actor); + return; + } + + player->stateFlags2 &= ~0x20000; + this->unk_1CA = 1; + this->collider.info.toucher.dmgFlags = D_80AA044C[this->unk_1C7]; + this->unk_1C6 = 1; + this->unk_1C9 = ((this->unk_1C7 == 1) ? 2 : 4); + func_80A9EFE0(this, func_80A9F9B4); + this->unk_1C4 = 8; + Audio_PlaySoundGeneral(NA_SE_IT_ROLLING_CUT_LV1, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_1AC = 1.0f; + } else { + func_80A9EFE0(this, func_80A9F408); + } + this->actor.child = NULL; +} + +void EnMThunder_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMThunder* this = (EnMThunder*)thisx; + + if (this->unk_1CA != 0) { + func_800876C8(globalCtx); + } + + Collider_DestroyCylinder(globalCtx, &this->collider); + func_80A9F314(globalCtx, 0.0f); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); +} + +void func_80A9F314(GlobalContext* globalCtx, f32 arg1) { + Environment_AdjustLights(globalCtx, arg1, 850.0f, 0.2f, 0.0f); +} + +void func_80A9F350(EnMThunder* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags2 & 0x20000) { + if (player->swordAnimation >= 0x18) { + Audio_PlaySoundGeneral(NA_SE_IT_ROLLING_CUT, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_SWING_HARD, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + Actor_Kill(&this->actor); + return; + } + + if (!(player->stateFlags1 & 0x1000)) { + Actor_Kill(&this->actor); + } +} + +void func_80A9F408(EnMThunder* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* child = this->actor.child; + + this->unk_1B8 = player->unk_858; + this->actor.world.pos = player->bodyPartsPos[0]; + this->actor.shape.rot.y = player->actor.shape.rot.y + 0x8000; + + if (this->unk_1CA == 0) { + if (player->unk_858 >= 0.1f) { + if ((gSaveContext.unk_13F0) || (((this->actor.params & 0xFF00) >> 8) && + !(func_80087708(globalCtx, (this->actor.params & 0xFF00) >> 8, 4)))) { + func_80A9F350(this, globalCtx); + func_80A9EFE0(this, func_80A9F350); + this->unk_1C8 = 0; + this->unk_1BC = 0.0; + this->unk_1AC = 0.0f; + return; + } + + this->unk_1CA = 1; + } + } + + if (player->unk_858 >= 0.1f) { + func_800AA000(0.0f, (s32)(player->unk_858 * 150.0f) & 0xFF, 2, (s32)(player->unk_858 * 150.0f) & 0xFF); + } + + if (player->stateFlags2 & 0x20000) { + if ((child != NULL) && (child->update != NULL)) { + child->parent = NULL; + } + + if (player->unk_858 <= 0.15f) { + if ((player->unk_858 >= 0.1f) && (player->swordAnimation >= 0x18)) { + Audio_PlaySoundGeneral(NA_SE_IT_ROLLING_CUT, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_SWING_HARD, &player->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } + Actor_Kill(&this->actor); + return; + } else { + player->stateFlags2 &= ~0x20000; + if ((this->actor.params & 0xFF00) >> 8) { + gSaveContext.unk_13F0 = 1; + } + if (player->unk_858 < 0.85f) { + this->collider.info.toucher.dmgFlags = D_80AA044C[this->unk_1C7]; + this->unk_1C6 = 1; + this->unk_1C9 = ((this->unk_1C7 == 1) ? 2 : 4); + } else { + this->collider.info.toucher.dmgFlags = D_80AA0458[this->unk_1C7]; + this->unk_1C6 = 0; + this->unk_1C9 = ((this->unk_1C7 == 1) ? 4 : 8); + } + + func_80A9EFE0(this, func_80A9F9B4); + this->unk_1C4 = 8; + Audio_PlaySoundGeneral(sSfxIds[this->unk_1C6], &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_1AC = 1.0f; + return; + } + } + + if (!(player->stateFlags1 & 0x1000)) { + if (this->actor.child != NULL) { + this->actor.child->parent = NULL; + } + Actor_Kill(&this->actor); + return; + } + + if (player->unk_858 > 0.15f) { + this->unk_1C8 = 255; + if (this->actor.child == NULL) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EFF_DUST, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, + this->unk_1C7 + 2); + } + this->unk_1BC += ((((player->unk_858 - 0.15f) * 1.5f) - this->unk_1BC) * 0.5f); + + } else if (player->unk_858 > .1f) { + this->unk_1C8 = (s32)((player->unk_858 - .1f) * 255.0f * 20.0f); + this->unk_1AC = (player->unk_858 - .1f) * 10.0f; + } else { + this->unk_1C8 = 0; + } + + if (player->unk_858 > 0.85f) { + func_800F4254(&player->actor.projectedPos, 2); + } else if (player->unk_858 > 0.15f) { + func_800F4254(&player->actor.projectedPos, 1); + } else if (player->unk_858 > 0.1f) { + func_800F4254(&player->actor.projectedPos, 0); + } + + if (Gameplay_InCsMode(globalCtx)) { + Actor_Kill(&this->actor); + } +} + +void func_80A9F938(EnMThunder* this, GlobalContext* globalCtx) { + if (this->unk_1C4 < 2) { + if (this->unk_1C8 < 40) { + this->unk_1C8 = 0; + } else { + this->unk_1C8 -= 40; + } + } + + this->unk_1B4 += 2.0f * this->unk_1B0; + + if (this->unk_1BC < this->unk_1AC) { + this->unk_1BC += ((this->unk_1AC - this->unk_1BC) * 0.1f); + } else { + this->unk_1BC = this->unk_1AC; + } +} + +void func_80A9F9B4(EnMThunder* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Math_StepToF(&this->unk_1AC, 0.0f, 1 / 16.0f)) { + Actor_Kill(&this->actor); + } else { + Math_SmoothStepToF(&this->actor.scale.x, (s32)this->unk_1C9, 0.6f, 0.8f, 0.0f); + Actor_SetScale(&this->actor, this->actor.scale.x); + this->collider.dim.radius = (this->actor.scale.x * 25.0f); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->unk_1C4 > 0) { + this->actor.world.pos.x = player->bodyPartsPos[0].x; + this->actor.world.pos.z = player->bodyPartsPos[0].z; + this->unk_1C4--; + } + + if (this->unk_1AC > 0.6f) { + this->unk_1B0 = 1.0f; + } else { + this->unk_1B0 = this->unk_1AC * (5.0f / 3.0f); + } + + func_80A9F938(this, globalCtx); + + if (Gameplay_InCsMode(globalCtx)) { + Actor_Kill(&this->actor); + } +} + +void EnMThunder_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMThunder* this = (EnMThunder*)thisx; + f32 blueRadius; + s32 redGreen; + + this->actionFunc(this, globalCtx); + func_80A9F314(globalCtx, this->unk_1BC); + blueRadius = this->unk_1AC; + redGreen = (u32)(blueRadius * 255.0f) & 0xFF; + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, redGreen, redGreen, (u32)(blueRadius * 100.0f), + (s32)(blueRadius * 800.0f)); +} + +void EnMThunder_Draw(Actor* thisx, GlobalContext* globalCtx2) { + static f32 D_80AA046C[] = { 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.25f, 0.2f, 0.15f }; + GlobalContext* globalCtx = globalCtx2; + EnMThunder* this = (EnMThunder*)thisx; + Player* player = GET_PLAYER(globalCtx); + f32 phi_f14; + s32 phi_t1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_m_thunder.c", 844); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Scale(0.02f, 0.02f, 0.02f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_m_thunder.c", 853), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + switch (this->unk_1C6) { + case 0: + case 1: + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0xFF - ((u8)(s32)(this->unk_1B4 * 30) & 0xFF), 0, + 0x40, 0x20, 1, 0xFF - ((u8)(s32)(this->unk_1B4 * 20) & 0xFF), 0, 8, 8)); + break; + } + + switch (this->unk_1C6) { + case 0: + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 170, (u8)(this->unk_1B0 * 255)); + gSPDisplayList(POLY_XLU_DISP++, gSpinAttack3DL); + gSPDisplayList(POLY_XLU_DISP++, gSpinAttack4DL); + break; + case 1: + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 170, 255, 255, (u8)(this->unk_1B0 * 255)); + gSPDisplayList(POLY_XLU_DISP++, gSpinAttack1DL); + gSPDisplayList(POLY_XLU_DISP++, gSpinAttack2DL); + break; + } + + Matrix_Mult(&player->mf_9E0, MTXMODE_NEW); + + switch (this->unk_1C7) { + case 1: + Matrix_Translate(0.0f, 220.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(-0.7f, -0.6f, -0.4f, MTXMODE_APPLY); + Matrix_RotateX(16384.0f, MTXMODE_APPLY); + break; + case 0: + Matrix_Translate(0.0f, 300.0f, -100.0f, MTXMODE_APPLY); + Matrix_Scale(-1.2f, -1.0f, -0.7f, MTXMODE_APPLY); + Matrix_RotateX(16384.0f, MTXMODE_APPLY); + break; + case 2: + Matrix_Translate(200.0f, 350.0f, 0.0f, MTXMODE_APPLY); + Matrix_Scale(-1.8f, -1.4f, -0.7f, MTXMODE_APPLY); + Matrix_RotateX(16384.0f, MTXMODE_APPLY); + break; + } + + if (this->unk_1B8 >= 0.85f) { + phi_f14 = (D_80AA046C[(globalCtx->gameplayFrames & 7)] * 6.0f) + 1.0f; + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 170, this->unk_1C8); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 100, 0, 128); + phi_t1 = 0x28; + } else { + phi_f14 = (D_80AA046C[globalCtx->gameplayFrames & 7] * 2.0f) + 1.0f; + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 170, 255, 255, this->unk_1C8); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 128); + phi_t1 = 0x14; + } + Matrix_Scale(1.0f, phi_f14, phi_f14, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_m_thunder.c", 960), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (globalCtx->gameplayFrames * 5) & 0xFF, 0, 0x20, 0x20, 1, + (globalCtx->gameplayFrames * 20) & 0xFF, (globalCtx->gameplayFrames * phi_t1) & 0xFF, 8, + 8)); + + gSPDisplayList(POLY_XLU_DISP++, gSpinAttackChargingDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_m_thunder.c", 1031); +} diff --git a/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.h b/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.h new file mode 100644 index 000000000..92f1bdbf4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_M_THUNDER_H +#define Z_EN_M_THUNDER_H + +#include "ultra64.h" +#include "global.h" + +struct EnMThunder; + +typedef void (*EnMThunderActionFunc)(struct EnMThunder*, GlobalContext*); + +typedef struct EnMThunder { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ LightNode* lightNode; + /* 0x019C */ LightInfo lightInfo; + /* 0x01AC */ f32 unk_1AC; + /* 0x01B0 */ f32 unk_1B0; + /* 0x01B0 */ f32 unk_1B4; + /* 0x01B0 */ f32 unk_1B8; + /* 0x01BC */ f32 unk_1BC; + /* 0x01C0 */ EnMThunderActionFunc actionFunc; + /* 0x01C4 */ u16 unk_1C4; + /* 0x01C6 */ u8 unk_1C6; + /* 0x01C7 */ u8 unk_1C7; + /* 0x01C8 */ u8 unk_1C8; + /* 0x01C9 */ u8 unk_1C9; + /* 0x01CA */ u8 unk_1CA; +} EnMThunder; // size = 0x01CC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c new file mode 100644 index 000000000..282a899dd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.c @@ -0,0 +1,468 @@ +/* + * File: z_en_ma1.c + * Overlay: En_Ma1 + * Description: Child Malon + */ + +#include "z_en_ma1.h" +#include "objects/object_ma1/object_ma1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) + +void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMa1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMa1_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMa1_Draw(Actor* thisx, GlobalContext* globalCtx); + +u16 EnMa1_GetText(GlobalContext* globalCtx, Actor* this); +s16 func_80AA0778(GlobalContext* globalCtx, Actor* this); + +void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx); +void func_80AA0EA0(EnMa1* this, GlobalContext* globalCtx); +void func_80AA0EFC(EnMa1* this, GlobalContext* globalCtx); +void func_80AA0F44(EnMa1* this, GlobalContext* globalCtx); +void func_80AA106C(EnMa1* this, GlobalContext* globalCtx); +void func_80AA10EC(EnMa1* this, GlobalContext* globalCtx); +void func_80AA1150(EnMa1* this, GlobalContext* globalCtx); +void EnMa1_DoNothing(EnMa1* this, GlobalContext* globalCtx); + +const ActorInit En_Ma1_InitVars = { + ACTOR_EN_MA1, + ACTORCAT_NPC, + FLAGS, + OBJECT_MA1, + sizeof(EnMa1), + (ActorFunc)EnMa1_Init, + (ActorFunc)EnMa1_Destroy, + (ActorFunc)EnMa1_Update, + (ActorFunc)EnMa1_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENMA1_ANIM_0, + /* 1 */ ENMA1_ANIM_1, + /* 2 */ ENMA1_ANIM_2, + /* 3 */ ENMA1_ANIM_3 +} EnMa1Animation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &gMalonChildIdleAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, + { &gMalonChildIdleAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gMalonChildSingAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, + { &gMalonChildSingAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, +}; + +static Vec3f D_80AA16B8 = { 800.0f, 0.0f, 0.0f }; + +static void* sMouthTextures[] = { + gMalonChildNeutralMouthTex, + gMalonChildSmilingMouthTex, + gMalonChildTalkingMouthTex, +}; + +static void* sEyeTextures[] = { + gMalonChildEyeOpenTex, + gMalonChildEyeHalfTex, + gMalonChildEyeClosedTex, +}; + +u16 EnMa1_GetText(GlobalContext* globalCtx, Actor* thisx) { + u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x17); + + if (faceReaction != 0) { + return faceReaction; + } + if (CHECK_QUEST_ITEM(QUEST_SONG_EPONA)) { + return 0x204A; + } + if (gSaveContext.eventChkInf[1] & 0x40) { + return 0x2049; + } + if (gSaveContext.eventChkInf[1] & 0x20) { + if ((gSaveContext.infTable[8] & 0x20)) { + return 0x2049; + } else { + return 0x2048; + } + } + if (gSaveContext.eventChkInf[1] & 0x10) { + return 0x2047; + } + if (gSaveContext.eventChkInf[1] & 4) { + return 0x2044; + } + if (gSaveContext.infTable[8] & 0x10) { + if (gSaveContext.infTable[8] & 0x800) { + return 0x2043; + } else { + return 0x2042; + } + } + return 0x2041; +} + +s16 func_80AA0778(GlobalContext* globalCtx, Actor* thisx) { + s16 ret = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CLOSING: + switch (thisx->textId) { + case 0x2041: + gSaveContext.infTable[8] |= 0x10; + gSaveContext.eventChkInf[1] |= 1; + ret = 0; + break; + case 0x2043: + ret = 1; + break; + case 0x2047: + gSaveContext.eventChkInf[1] |= 0x20; + ret = 0; + break; + case 0x2048: + gSaveContext.infTable[8] |= 0x20; + ret = 0; + break; + case 0x2049: + gSaveContext.eventChkInf[1] |= 0x40; + ret = 0; + break; + case 0x2061: + ret = 2; + break; + default: + ret = 0; + break; + } + break; + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + ret = 2; + } + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + ret = 3; + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + ret = 1; + break; + } + return ret; +} + +s32 func_80AA08C4(EnMa1* this, GlobalContext* globalCtx) { + if ((this->actor.shape.rot.z == 3) && (gSaveContext.sceneSetupIndex == 5)) { + return 1; + } + if (!LINK_IS_CHILD) { + return 0; + } + if (((globalCtx->sceneNum == SCENE_MARKET_NIGHT) || (globalCtx->sceneNum == SCENE_MARKET_DAY)) && + !(gSaveContext.eventChkInf[1] & 0x10) && !(gSaveContext.infTable[8] & 0x800)) { + return 1; + } + if ((globalCtx->sceneNum == SCENE_SPOT15) && !(gSaveContext.eventChkInf[1] & 0x10)) { + if (gSaveContext.infTable[8] & 0x800) { + return 1; + } else { + gSaveContext.infTable[8] |= 0x800; + return 0; + } + } + if ((globalCtx->sceneNum == SCENE_SOUKO) && IS_NIGHT && (gSaveContext.eventChkInf[1] & 0x10)) { + return 1; + } + if (globalCtx->sceneNum != SCENE_SPOT20) { + return 0; + } + if ((this->actor.shape.rot.z == 3) && IS_DAY && (gSaveContext.eventChkInf[1] & 0x10)) { + return 1; + } + return 0; +} + +void EnMa1_UpdateEyes(EnMa1* this) { + if (DECR(this->blinkTimer) == 0) { + this->eyeIndex += 1; + if (this->eyeIndex >= 3) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeIndex = 0; + } + } +} + +void EnMa1_ChangeAnim(EnMa1* this, s32 index) { + f32 frameCount = Animation_GetLastFrame(sAnimationInfo[index].animation); + + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, frameCount, + sAnimationInfo[index].mode, sAnimationInfo[index].morphFrames); +} + +void func_80AA0AF4(EnMa1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3; + + if ((this->unk_1E8.unk_00 == 0) && (this->skelAnime.animation == &gMalonChildSingAnim)) { + phi_a3 = 1; + } else { + phi_a3 = 0; + } + + this->unk_1E8.unk_18 = player->actor.world.pos; + this->unk_1E8.unk_18.y -= -10.0f; + + func_80034A14(&this->actor, &this->unk_1E8, 0, phi_a3); +} + +void func_80AA0B74(EnMa1* this) { + if (this->skelAnime.animation == &gMalonChildSingAnim) { + if (this->unk_1E8.unk_00 == 0) { + if (this->unk_1E0 != 0) { + this->unk_1E0 = 0; + func_800F6584(0); + } + } else { + if (this->unk_1E0 == 0) { + this->unk_1E0 = 1; + func_800F6584(1); + } + } + } +} + +void EnMa1_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMa1* this = (EnMa1*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gMalonChildSkel, NULL, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(22), &sColChkInfoInit); + + if (!func_80AA08C4(this, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->unk_1E8.unk_00 = 0; + + if (!(gSaveContext.eventChkInf[1] & 0x10) || CHECK_QUEST_ITEM(QUEST_SONG_EPONA)) { + this->actionFunc = func_80AA0D88; + EnMa1_ChangeAnim(this, ENMA1_ANIM_2); + } else { + this->actionFunc = func_80AA0F44; + EnMa1_ChangeAnim(this, ENMA1_ANIM_2); + } +} + +void EnMa1_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMa1* this = (EnMa1*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AA0D88(EnMa1* this, GlobalContext* globalCtx) { + if (this->unk_1E8.unk_00 != 0) { + if (this->skelAnime.animation != &gMalonChildIdleAnim) { + EnMa1_ChangeAnim(this, ENMA1_ANIM_1); + } + } else { + if (this->skelAnime.animation != &gMalonChildSingAnim) { + EnMa1_ChangeAnim(this, ENMA1_ANIM_3); + } + } + + if ((globalCtx->sceneNum == SCENE_SPOT15) && (gSaveContext.eventChkInf[1] & 0x10)) { + Actor_Kill(&this->actor); + } else if (!(gSaveContext.eventChkInf[1] & 0x10) || CHECK_QUEST_ITEM(QUEST_SONG_EPONA)) { + if (this->unk_1E8.unk_00 == 2) { + this->actionFunc = func_80AA0EA0; + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } + } +} + +void func_80AA0EA0(EnMa1* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_80AA0EFC; + } else { + func_8002F434(&this->actor, globalCtx, GI_WEIRD_EGG, 120.0f, 10.0f); + } +} + +void func_80AA0EFC(EnMa1* this, GlobalContext* globalCtx) { + if (this->unk_1E8.unk_00 == 3) { + this->unk_1E8.unk_00 = 0; + this->actionFunc = func_80AA0D88; + gSaveContext.eventChkInf[1] |= 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } +} + +void func_80AA0F44(EnMa1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_1E8.unk_00 != 0) { + if (this->skelAnime.animation != &gMalonChildIdleAnim) { + EnMa1_ChangeAnim(this, ENMA1_ANIM_1); + } + } else { + if (this->skelAnime.animation != &gMalonChildSingAnim) { + EnMa1_ChangeAnim(this, ENMA1_ANIM_3); + } + } + + if (gSaveContext.eventChkInf[1] & 0x40) { + if (player->stateFlags2 & 0x1000000) { + player->stateFlags2 |= 0x2000000; + player->unk_6A8 = &this->actor; + this->actor.textId = 0x2061; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_1E8.unk_00 = 1; + this->actor.flags |= ACTOR_FLAG_16; + this->actionFunc = func_80AA106C; + } else if (this->actor.xzDistToPlayer < 30.0f + (f32)this->collider.dim.radius) { + player->stateFlags2 |= 0x800000; + } + } +} + +void func_80AA106C(EnMa1* this, GlobalContext* globalCtx) { + GET_PLAYER(globalCtx)->stateFlags2 |= 0x800000; + if (this->unk_1E8.unk_00 == 2) { + Audio_OcaSetInstrument(2); + func_8010BD58(globalCtx, OCARINA_ACTION_TEACH_EPONA); + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = func_80AA10EC; + } +} + +void func_80AA10EC(EnMa1* this, GlobalContext* globalCtx) { + GET_PLAYER(globalCtx)->stateFlags2 |= 0x800000; + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_SONG_DEMO_DONE) { + func_8010BD58(globalCtx, OCARINA_ACTION_PLAYBACK_EPONA); + this->actionFunc = func_80AA1150; + } +} + +void func_80AA1150(EnMa1* this, GlobalContext* globalCtx) { + GET_PLAYER(globalCtx)->stateFlags2 |= 0x800000; + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + globalCtx->nextEntranceIndex = 0x157; + gSaveContext.nextCutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 42; + globalCtx->sceneLoadFlag = 0x14; + this->actionFunc = EnMa1_DoNothing; + } +} + +void EnMa1_DoNothing(EnMa1* this, GlobalContext* globalCtx) { +} + +void EnMa1_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMa1* this = (EnMa1*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + EnMa1_UpdateEyes(this); + this->actionFunc(this, globalCtx); + if (this->actionFunc != EnMa1_DoNothing) { + func_800343CC(globalCtx, &this->actor, &this->unk_1E8.unk_00, (f32)this->collider.dim.radius + 30.0f, + EnMa1_GetText, func_80AA0778); + } + func_80AA0B74(this); + func_80AA0AF4(this, globalCtx); +} + +s32 EnMa1_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMa1* this = (EnMa1*)thisx; + Vec3s vec; + + if ((limbIndex == 2) || (limbIndex == 5)) { + *dList = NULL; + } + if (limbIndex == 15) { + Matrix_Translate(1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec = this->unk_1E8.unk_08; + Matrix_RotateX((vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == 8) { + vec = this->unk_1E8.unk_0E; + Matrix_RotateX((-vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((-vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + } + return false; +} + +void EnMa1_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnMa1* this = (EnMa1*)thisx; + Vec3f vec = D_80AA16B8; + + if (limbIndex == 15) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } +} + +void EnMa1_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnMa1* this = (EnMa1*)thisx; + Camera* camera; + f32 distFromCamera; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ma1.c", 1226); + + camera = GET_ACTIVE_CAM(globalCtx); + distFromCamera = Math_Vec3f_DistXZ(&this->actor.world.pos, &camera->eye); + func_800F6268(distFromCamera, NA_BGM_LONLON); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[this->mouthIndex])); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMa1_OverrideLimbDraw, EnMa1_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ma1.c", 1261); +} diff --git a/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.h b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.h new file mode 100644 index 000000000..ee68c0b88 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ma1/z_en_ma1.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_MA1_H +#define Z_EN_MA1_H + +#include "ultra64.h" +#include "global.h" + +struct EnMa1; + +typedef void (*EnMa1ActionFunc)(struct EnMa1*, GlobalContext*); + +typedef struct EnMa1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMa1ActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s16 unk_1E0; + /* 0x01E2 */ s16 blinkTimer; + /* 0x01E4 */ s16 eyeIndex; + /* 0x01E6 */ s16 mouthIndex; + /* 0x01E8 */ struct_80034A14_arg1 unk_1E8; +} EnMa1; // size = 0x0210 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ma2/z_en_ma2.c b/soh/src/overlays/actors/ovl_En_Ma2/z_en_ma2.c new file mode 100644 index 000000000..5da5e5766 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ma2/z_en_ma2.c @@ -0,0 +1,394 @@ +#include "z_en_ma2.h" +#include "objects/object_ma2/object_ma2.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) + +void EnMa2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMa2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMa2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMa2_Draw(Actor* thisx, GlobalContext* globalCtx); + +u16 func_80AA19A0(GlobalContext* globalCtx, Actor* this); +s16 func_80AA1A38(GlobalContext* globalCtx, Actor* this); + +void func_80AA1AE4(EnMa2* this, GlobalContext* globalCtx); +s32 func_80AA1C68(EnMa2* this); +void EnMa2_UpdateEyes(EnMa2* this); +void func_80AA1DB4(EnMa2* this, GlobalContext* globalCtx); +void func_80AA2018(EnMa2* this, GlobalContext* globalCtx); +void func_80AA204C(EnMa2* this, GlobalContext* globalCtx); +void func_80AA20E4(EnMa2* this, GlobalContext* globalCtx); +void func_80AA21C8(EnMa2* this, GlobalContext* globalCtx); + +const ActorInit En_Ma2_InitVars = { + ACTOR_EN_MA2, + ACTORCAT_NPC, + FLAGS, + OBJECT_MA2, + sizeof(EnMa2), + (ActorFunc)EnMa2_Init, + (ActorFunc)EnMa2_Destroy, + (ActorFunc)EnMa2_Update, + (ActorFunc)EnMa2_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENMA2_ANIM_0, + /* 1 */ ENMA2_ANIM_1, + /* 2 */ ENMA2_ANIM_2, + /* 3 */ ENMA2_ANIM_3, + /* 4 */ ENMA2_ANIM_4 +} EnMa2Animation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &gMalonAdultIdleAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, { &gMalonAdultIdleAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gMalonAdultStandStillAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, { &gMalonAdultSingAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, + { &gMalonAdultSingAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, +}; + +u16 func_80AA19A0(GlobalContext* globalCtx, Actor* thisx) { + u16 faceReaction = Text_GetFaceReaction(globalCtx, 23); + + if (faceReaction != 0) { + return faceReaction; + } + if (gSaveContext.eventChkInf[1] & 0x100) { + return 0x2056; + } + if (IS_NIGHT) { + if (gSaveContext.infTable[8] & 0x1000) { + return 0x2052; + } else if (gSaveContext.infTable[8] & 0x4000) { + return 0x2051; + } else { + return 0x2050; + } + } + return 0x204C; +} + +s16 func_80AA1A38(GlobalContext* globalCtx, Actor* thisx) { + s16 ret = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_CLOSING: + switch (thisx->textId) { + case 0x2051: + gSaveContext.infTable[8] |= 0x1000; + ret = 2; + break; + case 0x2053: + gSaveContext.infTable[8] |= 0x2000; + ret = 0; + break; + default: + ret = 0; + break; + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_9: + break; + } + return ret; +} + +void func_80AA1AE4(EnMa2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3; + + if ((this->unk_1E0.unk_00 == 0) && (this->skelAnime.animation == &gMalonAdultSingAnim)) { + phi_a3 = 1; + } else { + phi_a3 = 0; + } + + this->unk_1E0.unk_18 = player->actor.world.pos; + this->unk_1E0.unk_14 = 0.0f; + + func_80034A14(&this->actor, &this->unk_1E0, 0, phi_a3); +} + +u16 func_80AA1B58(EnMa2* this, GlobalContext* globalCtx) { + if (LINK_IS_CHILD) { + return 0; + } + if (!(gSaveContext.eventChkInf[1] & 0x100) && (globalCtx->sceneNum == SCENE_MALON_STABLE) && IS_DAY && + (this->actor.shape.rot.z == 5)) { + return 1; + } + if (!(gSaveContext.eventChkInf[1] & 0x100) && (globalCtx->sceneNum == SCENE_SPOT20) && IS_NIGHT && + (this->actor.shape.rot.z == 6)) { + return 2; + } + if (!(gSaveContext.eventChkInf[1] & 0x100) || (globalCtx->sceneNum != SCENE_SPOT20)) { + return 0; + } + if ((this->actor.shape.rot.z == 7) && IS_DAY) { + return 3; + } + if ((this->actor.shape.rot.z == 8) && IS_NIGHT) { + return 3; + } + return 0; +} + +s32 func_80AA1C68(EnMa2* this) { + if (this->skelAnime.animation != &gMalonAdultSingAnim) { + return 0; + } + if (this->unk_1E0.unk_00 != 0) { + return 0; + } + this->blinkTimer = 0; + if (this->eyeIndex != 2) { + return 0; + } + this->mouthIndex = 2; + return 1; +} + +void EnMa2_UpdateEyes(EnMa2* this) { + if ((!func_80AA1C68(this)) && (DECR(this->blinkTimer) == 0)) { + this->eyeIndex += 1; + if (this->eyeIndex >= 3) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeIndex = 0; + } + } +} + +void EnMa2_ChangeAnim(EnMa2* this, s32 index) { + f32 frameCount = Animation_GetLastFrame(sAnimationInfo[index].animation); + + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, frameCount, + sAnimationInfo[index].mode, sAnimationInfo[index].morphFrames); +} + +void func_80AA1DB4(EnMa2* this, GlobalContext* globalCtx) { + if (this->skelAnime.animation == &gMalonAdultSingAnim) { + if (this->unk_1E0.unk_00 == 0) { + if (this->unk_20A != 0) { + func_800F6584(0); + this->unk_20A = 0; + } + } else { + if (this->unk_20A == 0) { + func_800F6584(1); + this->unk_20A = 1; + } + } + } +} + +void EnMa2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMa2* this = (EnMa2*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gMalonAdultSkel, NULL, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(22), &sColChkInfoInit); + + switch (func_80AA1B58(this, globalCtx)) { + case 1: + EnMa2_ChangeAnim(this, ENMA2_ANIM_2); + this->actionFunc = func_80AA2018; + break; + case 2: + EnMa2_ChangeAnim(this, ENMA2_ANIM_3); + this->actionFunc = func_80AA204C; + break; + case 3: + if (gSaveContext.infTable[8] & 0x2000) { + EnMa2_ChangeAnim(this, ENMA2_ANIM_0); + } else { + EnMa2_ChangeAnim(this, ENMA2_ANIM_3); + } + this->actionFunc = func_80AA2018; + break; + case 0: + Actor_Kill(&this->actor); + return; + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->unk_1E0.unk_00 = 0; +} + +void EnMa2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMa2* this = (EnMa2*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AA2018(EnMa2* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 2) { + this->actor.flags &= ~ACTOR_FLAG_16; + this->unk_1E0.unk_00 = 0; + } +} + +void func_80AA204C(EnMa2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags2 & 0x1000000) { + player->unk_6A8 = &this->actor; + player->stateFlags2 |= 0x2000000; + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_EPONA); + this->actionFunc = func_80AA20E4; + } else if (this->actor.xzDistToPlayer < 30.0f + (f32)this->collider.dim.radius) { + player->stateFlags2 |= 0x800000; + } +} + +void func_80AA20E4(EnMa2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_04) { + this->actionFunc = func_80AA204C; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->unk_208 = 0x1E; + gSaveContext.infTable[8] |= 0x4000; + this->actionFunc = func_80AA21C8; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else { + player->stateFlags2 |= 0x800000; + } +} + +void func_80AA21C8(EnMa2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (DECR(this->unk_208)) { + player->stateFlags2 |= 0x800000; + } else { + if (this->unk_1E0.unk_00 == 0) { + this->actor.flags |= ACTOR_FLAG_16; + Message_CloseTextbox(globalCtx); + } else { + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = func_80AA2018; + } + } +} + +void EnMa2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMa2* this = (EnMa2*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + EnMa2_UpdateEyes(this); + this->actionFunc(this, globalCtx); + func_80AA1DB4(this, globalCtx); + func_80AA1AE4(this, globalCtx); + if (this->actionFunc != func_80AA20E4) { + func_800343CC(globalCtx, &this->actor, &this->unk_1E0.unk_00, (f32)this->collider.dim.radius + 30.0f, + func_80AA19A0, func_80AA1A38); + } +} + +s32 EnMa2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMa2* this = (EnMa2*)thisx; + Vec3s vec; + + if ((limbIndex == MALON_ADULT_LEFT_THIGH_LIMB) || (limbIndex == MALON_ADULT_RIGHT_THIGH_LIMB)) { + *dList = NULL; + } + if (limbIndex == MALON_ADULT_HEAD_LIMB) { + Matrix_Translate(1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec = this->unk_1E0.unk_08; + Matrix_RotateX((vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == MALON_ADULT_CHEST_AND_NECK_LIMB) { + vec = this->unk_1E0.unk_0E; + Matrix_RotateY((-vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((-vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + } + if ((limbIndex == MALON_ADULT_CHEST_AND_NECK_LIMB) || (limbIndex == MALON_ADULT_LEFT_SHOULDER_LIMB) || + (limbIndex == MALON_ADULT_RIGHT_SHOULDER_LIMB)) { + rot->y += Math_SinS(this->unk_212[limbIndex].y) * 200.0f; + rot->z += Math_CosS(this->unk_212[limbIndex].z) * 200.0f; + } + return false; +} + +void EnMa2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnMa2* this = (EnMa2*)thisx; + Vec3f vec = { 900.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ma2.c", 904); + + if (limbIndex == MALON_ADULT_HEAD_LIMB) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } + if ((limbIndex == MALON_ADULT_LEFT_HAND_LIMB) && (this->skelAnime.animation == &gMalonAdultStandStillAnim)) { + gSPDisplayList(POLY_OPA_DISP++, gMalonAdultBasketDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ma2.c", 927); +} + +void EnMa2_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sMouthTextures[] = { gMalonAdultMouthNeutralTex, gMalonAdultMouthSadTex, gMalonAdultMouthHappyTex }; + static void* sEyeTextures[] = { gMalonAdultEyeOpenTex, gMalonAdultEyeHalfTex, gMalonAdultEyeClosedTex }; + + EnMa2* this = (EnMa2*)thisx; + Camera* camera; + f32 someFloat; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ma2.c", 955); + + camera = GET_ACTIVE_CAM(globalCtx); + someFloat = Math_Vec3f_DistXZ(&this->actor.world.pos, &camera->eye); + func_800F6268(someFloat, NA_BGM_LONLON); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[this->mouthIndex])); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMa2_OverrideLimbDraw, EnMa2_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ma2.c", 990); +} diff --git a/soh/src/overlays/actors/ovl_En_Ma2/z_en_ma2.h b/soh/src/overlays/actors/ovl_En_Ma2/z_en_ma2.h new file mode 100644 index 000000000..6d95e2f90 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ma2/z_en_ma2.h @@ -0,0 +1,48 @@ +#ifndef Z_EN_MA2_H +#define Z_EN_MA2_H + +#include "ultra64.h" +#include "global.h" + +struct EnMa2; + +typedef void (*EnMa2ActionFunc)(struct EnMa2*, GlobalContext*); + +typedef enum { + /* 0x00 */ MALON_ADULT_LIMB_NONE, + /* 0x01 */ MALON_ADULT_ROOT_LIMB, + /* 0x02 */ MALON_ADULT_LOWER_CONTROL_LIMB, + /* 0x03 */ MALON_ADULT_LEFT_THIGH_LIMB, + /* 0x04 */ MALON_ADULT_LEFT_LEG_LIMB, + /* 0x05 */ MALON_ADULT_LEFT_FOOT_LIMB, + /* 0x06 */ MALON_ADULT_RIGHT_THIGH_LIMB, + /* 0x07 */ MALON_ADULT_RIGHT_LEG_LIMB, + /* 0x08 */ MALON_ADULT_RIGHT_FOOT_LIMB, + /* 0x09 */ MALON_ADULT_TORSO_LIMB, + /* 0x0A */ MALON_ADULT_DRESS_LIMB, + /* 0x0B */ MALON_ADULT_CHEST_AND_NECK_LIMB, + /* 0x0C */ MALON_ADULT_LEFT_SHOULDER_LIMB, + /* 0x0D */ MALON_ADULT_LEFT_ARM_LIMB, + /* 0x0E */ MALON_ADULT_LEFT_HAND_LIMB, + /* 0x0F */ MALON_ADULT_RIGHT_SHOULDER_LIMB, + /* 0x10 */ MALON_ADULT_RIGHT_ARM_LIMB, + /* 0x11 */ MALON_ADULT_RIGHT_HAND_LIMB, + /* 0x12 */ MALON_ADULT_HEAD_LIMB, + /* 0x13 */ MALON_ADULT_LIMB_MAX +} AdultMalonLimb; + +typedef struct EnMa2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMa2ActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ s16 unk_208; + /* 0x020A */ s16 unk_20A; + /* 0x020C */ s16 blinkTimer; + /* 0x020E */ s16 eyeIndex; + /* 0x0210 */ s16 mouthIndex; + /* 0x0212 */ Vec3s unk_212[MALON_ADULT_LIMB_MAX]; +} EnMa2; // size = 0x0284 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ma3/z_en_ma3.c b/soh/src/overlays/actors/ovl_En_Ma3/z_en_ma3.c new file mode 100644 index 000000000..b314bcf0b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ma3/z_en_ma3.c @@ -0,0 +1,374 @@ +/* + * File: z_en_ma3.c + * Overlay: En_Ma3 + * Description: Adult Malon (Ranch) + */ + +#include "z_en_ma3.h" +#include "objects/object_ma2/object_ma2.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnMa3_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMa3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMa3_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMa3_Draw(Actor* thisx, GlobalContext* globalCtx); + +u16 func_80AA2AA0(GlobalContext* globalCtx, Actor* this); +s16 func_80AA2BD4(GlobalContext* globalCtx, Actor* this); + +void func_80AA2E54(EnMa3* this, GlobalContext* globalCtx); +s32 func_80AA2EC8(EnMa3* this, GlobalContext* globalCtx); +s32 func_80AA2F28(EnMa3* this); +void EnMa3_UpdateEyes(EnMa3* this); +void func_80AA3200(EnMa3* this, GlobalContext* globalCtx); + +const ActorInit En_Ma3_InitVars = { + ACTOR_EN_MA3, + ACTORCAT_NPC, + FLAGS, + OBJECT_MA2, + sizeof(EnMa3), + (ActorFunc)EnMa3_Init, + (ActorFunc)EnMa3_Destroy, + (ActorFunc)EnMa3_Update, + (ActorFunc)EnMa3_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENMA3_ANIM_0, + /* 1 */ ENMA3_ANIM_1, + /* 2 */ ENMA3_ANIM_2, + /* 3 */ ENMA3_ANIM_3, + /* 4 */ ENMA3_ANIM_4 +} EnMa3Animation; + +static AnimationFrameCountInfo sAnimationInfo[] = { + { &gMalonAdultIdleAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, { &gMalonAdultIdleAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gMalonAdultStandStillAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, { &gMalonAdultSingAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, + { &gMalonAdultSingAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, +}; + +u16 func_80AA2AA0(GlobalContext* globalCtx, Actor* thisx) { + Player* player = GET_PLAYER(globalCtx); + s16* timer1ValuePtr; // weirdness with this necessary to match + + if (!(gSaveContext.infTable[11] & 0x100)) { + return 0x2000; + } + timer1ValuePtr = &gSaveContext.timer1Value; + if (gSaveContext.eventInf[0] & 0x400) { + gSaveContext.timer1Value = gSaveContext.timer1Value; + thisx->flags |= ACTOR_FLAG_16; + if (gSaveContext.timer1Value >= 0xD3) { + return 0x208E; + } + if ((HIGH_SCORE(HS_HORSE_RACE) == 0) || (HIGH_SCORE(HS_HORSE_RACE) >= 0xB4)) { + HIGH_SCORE(HS_HORSE_RACE) = 0xB4; + gSaveContext.timer1Value = *timer1ValuePtr; + } + if (!(gSaveContext.eventChkInf[1] & 0x4000) && (gSaveContext.timer1Value < 0x32)) { + return 0x208F; + } else if (gSaveContext.timer1Value < HIGH_SCORE(HS_HORSE_RACE)) { + return 0x2012; + } else { + return 0x2004; + } + } + if ((!(player->stateFlags1 & 0x800000)) && + (Actor_FindNearby(globalCtx, thisx, ACTOR_EN_HORSE, 1, 1200.0f) == NULL)) { + return 0x2001; + } + if (!(gSaveContext.infTable[11] & 0x200)) { + return 0x2002; + } else { + return 0x2003; + } +} + +s16 func_80AA2BD4(GlobalContext* globalCtx, Actor* thisx) { + s16 ret = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + globalCtx->nextEntranceIndex = 0x157; + gSaveContext.nextCutsceneIndex = 0xFFF0; + globalCtx->fadeTransition = 0x26; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.eventInf[0] |= 0x400; + gSaveContext.timer1State = 0xF; + } + break; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[11] |= 0x200; + if (globalCtx->msgCtx.choiceIndex == 0) { + if (gSaveContext.eventChkInf[1] & 0x4000) { + Message_ContinueTextbox(globalCtx, 0x2091); + } else if (HIGH_SCORE(HS_HORSE_RACE) == 0) { + Message_ContinueTextbox(globalCtx, 0x2092); + } else { + Message_ContinueTextbox(globalCtx, 0x2090); + } + } + } + break; + case TEXT_STATE_CLOSING: + switch (thisx->textId) { + case 0x2000: + gSaveContext.infTable[11] |= 0x100; + ret = 0; + break; + case 0x208F: + gSaveContext.eventChkInf[1] |= 0x4000; + case 0x2004: + case 0x2012: + if (HIGH_SCORE(HS_HORSE_RACE) > gSaveContext.timer1Value) { + HIGH_SCORE(HS_HORSE_RACE) = gSaveContext.timer1Value; + } + case 0x208E: + gSaveContext.eventInf[0] &= ~0x400; + thisx->flags &= ~ACTOR_FLAG_16; + ret = 0; + gSaveContext.timer1State = 0xA; + break; + case 0x2002: + gSaveContext.infTable[11] |= 0x200; + case 0x2003: + if (!(gSaveContext.eventInf[0] & 0x400)) { + ret = 0; + } + break; + default: + ret = 0; + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + break; + } + return ret; +} + +void func_80AA2E54(EnMa3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3; + + if ((this->unk_1E0.unk_00 == 0) && (this->skelAnime.animation == &gMalonAdultSingAnim)) { + phi_a3 = 1; + } else { + phi_a3 = 0; + } + + this->unk_1E0.unk_18 = player->actor.world.pos; + this->unk_1E0.unk_14 = 0.0f; + func_80034A14(&this->actor, &this->unk_1E0, 0, phi_a3); +} + +s32 func_80AA2EC8(EnMa3* this, GlobalContext* globalCtx) { + if (LINK_IS_CHILD) { + return 2; + } + if (!(gSaveContext.eventChkInf[1] & 0x100)) { + return 2; + } + if (gSaveContext.eventInf[0] & 0x400) { + return 1; + } + return 0; +} + +s32 func_80AA2F28(EnMa3* this) { + if (this->skelAnime.animation != &gMalonAdultSingAnim) { + return 0; + } + if (this->unk_1E0.unk_00 != 0) { + return 0; + } + this->blinkTimer = 0; + if (this->eyeIndex != 2) { + return 0; + } + this->mouthIndex = 2; + return 1; +} + +void EnMa3_UpdateEyes(EnMa3* this) { + if ((!func_80AA2F28(this)) && (DECR(this->blinkTimer) == 0)) { + this->eyeIndex += 1; + if (this->eyeIndex >= 3) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeIndex = 0; + } + } +} + +void EnMa3_ChangeAnim(EnMa3* this, s32 index) { + f32 frameCount = Animation_GetLastFrame(sAnimationInfo[index].animation); + + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, frameCount, + sAnimationInfo[index].mode, sAnimationInfo[index].morphFrames); +} + +void EnMa3_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMa3* this = (EnMa3*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gMalonAdultSkel, NULL, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(22), &sColChkInfoInit); + + switch (func_80AA2EC8(this, globalCtx)) { + case 0: + EnMa3_ChangeAnim(this, ENMA3_ANIM_0); + this->actionFunc = func_80AA3200; + break; + case 1: + EnMa3_ChangeAnim(this, ENMA3_ANIM_0); + this->actionFunc = func_80AA3200; + break; + case 2: + Actor_Kill(&this->actor); + return; + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + Actor_SetScale(&this->actor, 0.01f); + this->unk_1E0.unk_00 = (u16)0; +} + +void EnMa3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMa3* this = (EnMa3*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AA3200(EnMa3* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 2) { + this->actor.flags &= ~ACTOR_FLAG_16; + this->unk_1E0.unk_00 = 0; + } +} + +void EnMa3_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMa3* this = (EnMa3*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + EnMa3_UpdateEyes(this); + this->actionFunc(this, globalCtx); + func_80AA2E54(this, globalCtx); + func_800343CC(globalCtx, &this->actor, &this->unk_1E0.unk_00, (f32)this->collider.dim.radius + 150.0f, + func_80AA2AA0, func_80AA2BD4); + if (this->unk_1E0.unk_00 == 0) { + if (this->unk_20A != 0) { + func_800F6584(0); + this->unk_20A = 0; + } + } else if (this->unk_20A == 0) { + func_800F6584(1); + this->unk_20A = 1; + } +} + +s32 EnMa3_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMa3* this = (EnMa3*)thisx; + Vec3s vec; + + if ((limbIndex == MALON_ADULT_LIMB_LEFT_THIGH) || (limbIndex == MALON_ADULT_LIMB_RIGHT_THIGH)) { + *dList = NULL; + } + if (limbIndex == MALON_ADULT_LIMB_HEAD) { + Matrix_Translate(1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec = this->unk_1E0.unk_08; + Matrix_RotateX((vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1400.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == MALON_ADULT_LIMB_CHEST_AND_NECK) { + vec = this->unk_1E0.unk_0E; + Matrix_RotateY((-vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((-vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + } + if ((limbIndex == MALON_ADULT_LIMB_CHEST_AND_NECK) || (limbIndex == MALON_ADULT_LIMB_LEFT_SHOULDER) || + (limbIndex == MALON_ADULT_LIMB_RIGHT_SHOULDER)) { + rot->y += Math_SinS(this->unk_212[limbIndex].y) * 200.0f; + rot->z += Math_CosS(this->unk_212[limbIndex].z) * 200.0f; + } + return false; +} + +void EnMa3_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnMa3* this = (EnMa3*)thisx; + Vec3f vec = { 900.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ma3.c", 927); + + if (limbIndex == MALON_ADULT_LIMB_HEAD) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } + + if ((limbIndex == MALON_ADULT_LIMB_LEFT_HAND) && (this->skelAnime.animation == &gMalonAdultStandStillAnim)) { + gSPDisplayList(POLY_OPA_DISP++, gMalonAdultBasketDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ma3.c", 950); +} + +void EnMa3_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sMouthTextures[] = { gMalonAdultMouthNeutralTex, gMalonAdultMouthSadTex, gMalonAdultMouthHappyTex }; + static void* sEyeTextures[] = { gMalonAdultEyeOpenTex, gMalonAdultEyeHalfTex, gMalonAdultEyeClosedTex }; + EnMa3* this = (EnMa3*)thisx; + Camera* camera; + f32 someFloat; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ma3.c", 978); + + camera = GET_ACTIVE_CAM(globalCtx); + someFloat = Math_Vec3f_DistXZ(&this->actor.world.pos, &camera->eye); + func_800F6268(someFloat, NA_BGM_LONLON); + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(sMouthTextures[this->mouthIndex])); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIndex])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMa3_OverrideLimbDraw, EnMa3_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ma3.c", 1013); +} diff --git a/soh/src/overlays/actors/ovl_En_Ma3/z_en_ma3.h b/soh/src/overlays/actors/ovl_En_Ma3/z_en_ma3.h new file mode 100644 index 000000000..9d4d248b0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ma3/z_en_ma3.h @@ -0,0 +1,48 @@ +#ifndef Z_EN_MA3_H +#define Z_EN_MA3_H + +#include "ultra64.h" +#include "global.h" + +struct EnMa3; + +typedef void (*EnMa3ActionFunc)(struct EnMa3*, GlobalContext*); + +typedef enum { + /* 0x00 */ MALON_ADULT_LIMB_NONE, + /* 0x01 */ MALON_ADULT_LIMB_ROOT, + /* 0x02 */ MALON_ADULT_LIMB_LOWER_CONTROL, + /* 0x03 */ MALON_ADULT_LIMB_LEFT_THIGH, + /* 0x04 */ MALON_ADULT_LIMB_LEFT_LEG, + /* 0x05 */ MALON_ADULT_LIMB_LEFT_FOOT, + /* 0x06 */ MALON_ADULT_LIMB_RIGHT_THIGH, + /* 0x07 */ MALON_ADULT_LIMB_RIGHT_LEG, + /* 0x08 */ MALON_ADULT_LIMB_RIGHT_FOOT, + /* 0x09 */ MALON_ADULT_LIMB_TORSO, + /* 0x0A */ MALON_ADULT_LIMB_DRESS, + /* 0x0B */ MALON_ADULT_LIMB_CHEST_AND_NECK, + /* 0x0C */ MALON_ADULT_LIMB_LEFT_SHOULDER, + /* 0x0D */ MALON_ADULT_LIMB_LEFT_ARM, + /* 0x0E */ MALON_ADULT_LIMB_LEFT_HAND, + /* 0x0F */ MALON_ADULT_LIMB_RIGHT_SHOULDER, + /* 0x10 */ MALON_ADULT_LIMB_RIGHT_ARM, + /* 0x11 */ MALON_ADULT_LIMB_RIGHT_HAND, + /* 0x12 */ MALON_ADULT_LIMB_HEAD, + /* 0x13 */ MALON_ADULT_LIMB_MAX +} AdultMalonLimb; + +typedef struct EnMa3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMa3ActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ s16 unk_208; + /* 0x020A */ s16 unk_20A; + /* 0x020C */ s16 blinkTimer; + /* 0x020E */ s16 eyeIndex; + /* 0x0210 */ s16 mouthIndex; + /* 0x0212 */ Vec3s unk_212[MALON_ADULT_LIMB_MAX]; +} EnMa3; // size = 0x0284 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c new file mode 100644 index 000000000..a7392e14c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.c @@ -0,0 +1,1003 @@ +/* + * File: z_en_mag.c + * Overlay: ovl_En_Mag + * Description: Title Screen Manager & Logo + */ + +#include "z_en_mag.h" +#include "objects/object_mag/object_mag.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnMag_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMag_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMag_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMag_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Mag_InitVars = { + ACTOR_EN_MAG, + ACTORCAT_PROP, + FLAGS, + OBJECT_MAG, + sizeof(EnMag), + (ActorFunc)EnMag_Init, + (ActorFunc)EnMag_Destroy, + (ActorFunc)EnMag_Update, + (ActorFunc)EnMag_Draw, + NULL, +}; + +static s16 sDelayTimer = 0; +#ifdef MASTER_QUEST +void EnMag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMag* this = (EnMag*)thisx; + + YREG(1) = 63; + YREG(3) = 80; + YREG(4) = 255; + YREG(5) = 30; + YREG(6) = 30; + YREG(7) = 119; + YREG(8) = 7; + YREG(9) = 5; + YREG(10) = 3; + + VREG(4) = 1; + VREG(5) = 6; + VREG(6) = 2; + + this->copyrightAlphaStep = 6; + this->fadeOutAlphaStep = 10; + + VREG(19) = 99; + VREG(21) = 9; + VREG(23) = 10; + VREG(24) = 8; + + this->effectScroll = 0; + this->unk_E30C = 0; + + this->effectPrimColor[0] = 0.0f; + this->effectPrimColor[1] = 100.0f; + this->effectPrimColor[2] = 170.0f; + this->effectEnvColor[0] = 0.0f; + this->effectEnvColor[1] = 100.0f; + this->effectEnvColor[2] = 0.0f; + + this->effectFadeInTimer = 40; + + this->effectFadeInState = this->effectPrimLodFrac = this->globalState = this->effectAlpha = this->mainAlpha = + this->subAlpha = this->copyrightAlpha = 0.0f; + + if (gSaveContext.unk_13E7 != 0) { + this->mainAlpha = 210; + this->subAlpha = 255; + this->copyrightAlpha = 255; + + this->effectPrimLodFrac = 128.0f; + this->effectAlpha = 255.0f; + + this->effectPrimColor[0] = 170; + this->effectPrimColor[1] = 255.0f; + this->effectPrimColor[2] = 255.0f; + this->effectEnvColor[0] = 200.0f; + this->effectEnvColor[1] = 255.0f; + this->effectEnvColor[2] = 0; + + gSaveContext.unk_13E7 = 0; + this->globalState = MAG_STATE_DISPLAY; + sDelayTimer = 20; + gSaveContext.fadeDuration = 1; + gSaveContext.unk_1419 = 255; + } + + Font_LoadOrderedFont(&this->font); + + this->unk_E316 = 0; + this->unk_E318 = 0; + this->unk_E31C = 0; + this->unk_E320 = 0; +} +#else +void EnMag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMag* this = (EnMag*)thisx; + + YREG(1) = 63; + YREG(3) = 80; + YREG(4) = 255; + YREG(5) = 30; + YREG(6) = 30; + YREG(7) = 119; + YREG(8) = 7; + YREG(9) = 5; + YREG(10) = 3; + + VREG(4) = 1; + VREG(5) = 6; + VREG(6) = 2; + + this->copyrightAlphaStep = 6; + this->fadeOutAlphaStep = 10; + + VREG(19) = 99; + VREG(21) = 9; + VREG(23) = 10; + VREG(24) = 8; + + this->effectScroll = 0; + this->unk_E30C = 0; + + this->effectPrimColor[0] = 0.0f; + this->effectPrimColor[1] = 100.0f; + this->effectPrimColor[2] = 170.0f; + this->effectEnvColor[0] = 0.0f; + this->effectEnvColor[1] = 100.0f; + this->effectEnvColor[2] = 0.0f; + + this->effectFadeInTimer = 40; + + this->effectFadeInState = this->effectPrimLodFrac = this->globalState = this->effectAlpha = this->mainAlpha = + this->subAlpha = this->copyrightAlpha = 0.0f; + + if (gSaveContext.unk_13E7 != 0) { + this->mainAlpha = 210; + this->subAlpha = 255; + this->copyrightAlpha = 255; + + this->effectPrimLodFrac = 128.0f; + this->effectAlpha = 255.0f; + + this->effectPrimColor[0] = 255.0f; + this->effectPrimColor[1] = 255.0f; + this->effectPrimColor[2] = 170; + this->effectEnvColor[0] = 255.0f; + this->effectEnvColor[1] = 100; + + gSaveContext.unk_13E7 = 0; + this->globalState = MAG_STATE_DISPLAY; + sDelayTimer = 20; + gSaveContext.fadeDuration = 1; + gSaveContext.unk_1419 = 255; + } + + Font_LoadOrderedFont(&this->font); + + this->unk_E316 = 0; + this->unk_E318 = 0; + this->unk_E31C = 0; + this->unk_E320 = 0; +} +#endif + +void EnMag_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +#ifdef MASTER_QUEST +void EnMag_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad[2]; + EnMag* this = (EnMag*)thisx; + + if (gSaveContext.fileNum != 0xFEDC) { + if (this->globalState < MAG_STATE_DISPLAY) { + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B)) { + + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + this->mainAlpha = 210; + this->subAlpha = 255; + this->copyrightAlpha = 255; + + this->effectPrimLodFrac = 128.0f; + this->effectAlpha = 255.0f; + + this->effectPrimColor[0] = 170; + this->effectPrimColor[1] = 255.0f; + this->effectPrimColor[2] = 255.0f; + this->effectEnvColor[0] = 200.0f; + this->effectEnvColor[1] = 255.0f; + this->effectEnvColor[2] = 0; + + this->globalState = MAG_STATE_DISPLAY; + sDelayTimer = 20; + gSaveContext.fadeDuration = 1; + gSaveContext.unk_1419 = 255; + } + } else if (this->globalState >= MAG_STATE_DISPLAY) { + if (sDelayTimer == 0) { + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B)) { + + if (globalCtx->sceneLoadFlag != 20) { + Audio_SetCutsceneFlag(0); + + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + gSaveContext.gameMode = 2; + globalCtx->sceneLoadFlag = 20; + globalCtx->fadeTransition = 2; + } + + this->copyrightAlphaStep = 15; + this->fadeOutAlphaStep = 25; + this->globalState = MAG_STATE_FADE_OUT; + } + } else { + sDelayTimer--; + } + } + } + + if (this->globalState == MAG_STATE_FADE_IN) { + if (this->effectFadeInState == 0) { + this->effectAlpha += 6.375f; + this->effectPrimLodFrac += 0.8f; + + this->effectPrimColor[0] += 6.375f; + this->effectPrimColor[1] += 3.875f; + this->effectPrimColor[2] += 2.125f; + this->effectEnvColor[0] += 6.375f; + this->effectEnvColor[1] += 3.875f; + + this->effectFadeInTimer--; + + if (this->effectFadeInTimer == 0) { + this->effectPrimLodFrac = 32.0f; + this->effectAlpha = 255.0f; + + this->effectPrimColor[0] = 255.0f; + this->effectPrimColor[1] = 255.0f; + this->effectPrimColor[2] = 255.0f; + this->effectEnvColor[0] = 255.0f; + this->effectEnvColor[1] = 255.0f; + + this->effectFadeInTimer = 40; + this->effectFadeInState = 1; + } + } else if (this->effectFadeInState == 1) { + this->effectPrimColor[0] += -2.125f; + this->effectEnvColor[0] += -1.375f; + + this->effectPrimLodFrac += 2.4f; + + this->effectFadeInTimer--; + + if (this->effectFadeInTimer == 0) { + this->effectPrimLodFrac = 128.0f; + + this->effectPrimColor[0] = 170.0f; + this->effectEnvColor[0] = 200.0f; + + this->effectFadeInTimer = 32; + this->effectFadeInState = 2; + } + } + + if (this->effectAlpha > 160) { + this->mainAlpha += VREG(5); + if (this->mainAlpha >= 210.0f) { + this->mainAlpha = 210.0f; + } + + if (this->mainAlpha >= 210) { + this->subAlpha += VREG(6); + if (this->subAlpha >= 255.0f) { + this->subAlpha = 255.0f; + } + + if (this->subAlpha >= 200) { + this->copyrightAlpha += this->copyrightAlphaStep; + if (this->copyrightAlpha >= 255.0f) { + this->copyrightAlpha = 255.0f; + this->globalState = MAG_STATE_DISPLAY; + sDelayTimer = 20; + } + } + } + } + } else if (this->globalState == MAG_STATE_FADE_OUT) { + this->effectAlpha -= this->fadeOutAlphaStep; + if (this->effectAlpha < 0.0f) { + this->effectAlpha = 0.0f; + } + + this->mainAlpha -= this->fadeOutAlphaStep; + if (this->mainAlpha < 0.0f) { + this->mainAlpha = 0.0f; + } + + this->subAlpha -= this->fadeOutAlphaStep; + if (this->subAlpha < 0.0f) { + this->subAlpha = 0.0f; + } + + this->copyrightAlpha -= this->copyrightAlphaStep; + if (this->copyrightAlpha < 0.0f) { + this->copyrightAlpha = 0.0f; + this->globalState = MAG_STATE_POST_DISPLAY; + } + } + + if (this->globalState == MAG_STATE_INITIAL) { + if (Flags_GetEnv(globalCtx, 3)) { + this->effectFadeInTimer = 40; + this->globalState = MAG_STATE_FADE_IN; + } + } else if (this->globalState == MAG_STATE_DISPLAY) { + if (Flags_GetEnv(globalCtx, 4)) { + this->globalState = MAG_STATE_FADE_OUT; + } + } +} +#else +void EnMag_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad[2]; + EnMag* this = (EnMag*)thisx; + + if (gSaveContext.fileNum != 0xFEDC) { + if (this->globalState < MAG_STATE_DISPLAY) { + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B)) { + + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + this->mainAlpha = 210; + this->subAlpha = 255; + this->copyrightAlpha = 255; + + this->effectPrimLodFrac = 128.0f; + this->effectAlpha = 255.0f; + + this->effectPrimColor[0] = 255.0f; + this->effectPrimColor[1] = 255.0f; + this->effectPrimColor[2] = 170; + this->effectEnvColor[0] = 255.0f; + this->effectEnvColor[1] = 100; + + this->globalState = MAG_STATE_DISPLAY; + sDelayTimer = 20; + gSaveContext.fadeDuration = 1; + gSaveContext.unk_1419 = 255; + } + } else if (this->globalState >= MAG_STATE_DISPLAY) { + if (sDelayTimer == 0) { + if (CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_START) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_A) || + CHECK_BTN_ALL(globalCtx->state.input[0].press.button, BTN_B)) { + + if (globalCtx->sceneLoadFlag != 20) { + Audio_SetCutsceneFlag(0); + + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + gSaveContext.gameMode = 2; + globalCtx->sceneLoadFlag = 20; + globalCtx->fadeTransition = 2; + } + + this->copyrightAlphaStep = 15; + this->fadeOutAlphaStep = 25; + this->globalState = MAG_STATE_FADE_OUT; + } + } else { + sDelayTimer--; + } + } + } + + if (this->globalState == MAG_STATE_FADE_IN) { + if (this->effectFadeInState == 0) { + this->effectAlpha += 6.375f; + this->effectPrimLodFrac += 0.8f; + + this->effectPrimColor[0] += 6.375f; + this->effectPrimColor[1] += 3.875f; + this->effectPrimColor[2] += 2.125f; + this->effectEnvColor[0] += 6.375f; + this->effectEnvColor[1] += 3.875f; + + this->effectFadeInTimer--; + + if (this->effectFadeInTimer == 0) { + this->effectPrimLodFrac = 32.0f; + this->effectAlpha = 255.0f; + + this->effectPrimColor[0] = 255.0f; + this->effectPrimColor[1] = 255.0f; + this->effectPrimColor[2] = 255.0f; + this->effectEnvColor[0] = 255.0f; + this->effectEnvColor[1] = 255.0f; + + this->effectFadeInTimer = 40; + this->effectFadeInState = 1; + } + } else if (this->effectFadeInState == 1) { + this->effectPrimColor[2] += -2.125f; + this->effectEnvColor[1] += -3.875f; + + this->effectPrimLodFrac += 2.4f; + + this->effectFadeInTimer--; + + if (this->effectFadeInTimer == 0) { + this->effectPrimLodFrac = 128.0f; + + this->effectPrimColor[2] = 170.0f; + this->effectEnvColor[1] = 100.0f; + + this->effectFadeInTimer = 32; + this->effectFadeInState = 2; + } + } + + if (this->effectAlpha > 160) { + this->mainAlpha += VREG(5); + if (this->mainAlpha >= 210.0f) { + this->mainAlpha = 210.0f; + } + + if (this->mainAlpha >= 210) { + this->subAlpha += VREG(6); + if (this->subAlpha >= 255.0f) { + this->subAlpha = 255.0f; + } + + if (this->subAlpha >= 200) { + this->copyrightAlpha += this->copyrightAlphaStep; + if (this->copyrightAlpha >= 255.0f) { + this->copyrightAlpha = 255.0f; + this->globalState = MAG_STATE_DISPLAY; + sDelayTimer = 20; + } + } + } + } + } else if (this->globalState == MAG_STATE_FADE_OUT) { + this->effectAlpha -= this->fadeOutAlphaStep; + if (this->effectAlpha < 0.0f) { + this->effectAlpha = 0.0f; + } + + this->mainAlpha -= this->fadeOutAlphaStep; + if (this->mainAlpha < 0.0f) { + this->mainAlpha = 0.0f; + } + + this->subAlpha -= this->fadeOutAlphaStep; + if (this->subAlpha < 0.0f) { + this->subAlpha = 0.0f; + } + + this->copyrightAlpha -= this->copyrightAlphaStep; + if (this->copyrightAlpha < 0.0f) { + this->copyrightAlpha = 0.0f; + this->globalState = MAG_STATE_POST_DISPLAY; + } + } + + if (this->globalState == MAG_STATE_INITIAL) { + if (Flags_GetEnv(globalCtx, 3)) { + this->effectFadeInTimer = 40; + this->globalState = MAG_STATE_FADE_IN; + } + } else if (this->globalState == MAG_STATE_DISPLAY) { + if (Flags_GetEnv(globalCtx, 4)) { + this->globalState = MAG_STATE_FADE_OUT; + } + } +} +#endif + +void EnMag_DrawTextureI8(Gfx** gfxp, void* texture, s16 texWidth, s16 texHeight, s16 rectLeft, s16 rectTop, + s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy) { + Gfx* gfx = *gfxp; + + gDPLoadTextureBlock(gfx++, texture, G_IM_FMT_I, G_IM_SIZ_8b, texWidth, texHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, (rectTop + rectHeight) << 2, + G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + *gfxp = gfx; +} + +void EnMag_DrawEffectTextures(Gfx** gfxp, void* maskTex, void* effectTex, s16 maskWidth, s16 maskHeight, + s16 effectWidth, s16 effectHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, + s16 rectHeight, u16 dsdx, u16 dtdy, u16 shifts, u16 shiftt, u16 flag, EnMag* this) { + Gfx* gfx = *gfxp; + + gDPLoadMultiBlock_4b(gfx++, maskTex, 0x0000, 0, G_IM_FMT_I, maskWidth, maskHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + if (!flag) { + gDPLoadMultiBlock(gfx++, effectTex, 0x0100, 1, G_IM_FMT_I, G_IM_SIZ_8b, effectWidth, effectHeight, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, 5, shifts, shiftt); + + gDPSetTileSize(gfx++, 1, 0, this->effectScroll & 0x7F, 0x7C, (this->effectScroll & 0x7F) + 0x7C); + } + + gSPTextureRectangle(gfx++, rectLeft << 2, rectTop << 2, (rectLeft + rectWidth) << 2, (rectTop + rectHeight) << 2, + G_TX_RENDERTILE, 0, 0, dsdx, dtdy); + + *gfxp = gfx; +} + +void EnMag_DrawImageRGBA32(Gfx** gfxp, s16 centerX, s16 centerY, u8* source, u32 width, u32 height) { + Gfx* gfx = *gfxp; + u8* curTexture; + s32 textureCount; + u32 rectLeft; + u32 rectTop; + u32 textureHeight; + s32 remainingSize; + s32 textureSize; + s32 pad; + s32 i; + + source = ResourceMgr_LoadTexByName(source); + + func_80094D28(&gfx); + + curTexture = source; + rectLeft = centerX - (width / 2); + rectTop = centerY - (height / 2); + textureHeight = 4096 / (width << 2); + remainingSize = (width * height) << 2; + textureSize = (width * textureHeight) << 2; + textureCount = remainingSize / textureSize; + if ((remainingSize % textureSize) != 0) { + textureCount += 1; + } + + gDPSetTileCustom(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_32b, width, textureHeight, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + remainingSize -= textureSize; + + for (i = 0; i < textureCount; i++) { + gDPSetTextureImage(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_32b, width, curTexture); + + gDPLoadSync(gfx++); + gDPLoadTile(gfx++, G_TX_LOADTILE, 0, 0, (width - 1) << 2, (textureHeight - 1) << 2); + + gSPTextureRectangle(gfx++, rectLeft << 2, rectTop << 2, (rectLeft + (s32)width) << 2, + (rectTop + textureHeight) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + curTexture += textureSize; + rectTop += textureHeight; + + if ((remainingSize - textureSize) < 0) { + if (remainingSize > 0) { + textureHeight = remainingSize / (s32)(width << 2); + remainingSize -= textureSize; + + gDPSetTileCustom(gfx++, G_IM_FMT_RGBA, G_IM_SIZ_32b, width, textureHeight, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } + } else { + remainingSize -= textureSize; + } + } + + *gfxp = gfx; +} + +void EnMag_DrawCharTexture(Gfx** gfxp, u8* texture, s32 rectLeft, s32 rectTop) { + Gfx* gfx = *gfxp; + + YREG(0) = 1024.0f / (YREG(1) / 100.0f); + YREG(2) = 16.0f * (YREG(1) / 100.0f); + + gDPLoadTextureBlock_4b(gfx++, texture, G_IM_FMT_I, 16, 16, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, rectLeft << 2, rectTop << 2, (rectLeft + YREG(2)) << 2, (rectTop + YREG(2)) << 2, + G_TX_RENDERTILE, 0, 0, YREG(0), YREG(0)); + + *gfxp = gfx; +} +#ifdef MASTER_QUEST +void EnMag_DrawInner(Actor* thisx, GlobalContext* globalCtx, Gfx** gfxp) { + static s16 textAlpha = 0; + static s16 textFadeDirection = 0; + static s16 textFadeTimer = 0; + static u8 noControllerFontIndexes[] = { + 0x17, 0x18, 0x0C, 0x18, 0x17, 0x1D, 0x1B, 0x18, 0x15, 0x15, 0x0E, 0x1B, + }; + static u8 pressStartFontIndexes[] = { + 0x19, 0x1B, 0x0E, 0x1C, 0x1C, 0x1C, 0x1D, 0x0A, 0x1B, 0x1D, + }; + static void* effectMaskTextures[] = { + gTitleEffectMask00Tex, gTitleEffectMask01Tex, gTitleEffectMask02Tex, + gTitleEffectMask10Tex, gTitleEffectMask11Tex, gTitleEffectMask12Tex, + gTitleEffectMask20Tex, gTitleEffectMask21Tex, gTitleEffectMask22Tex, + }; + EnMag* this = (EnMag*)thisx; + Font* font = &this->font; + s32 pad; + Gfx* gfx = *gfxp; + u16 i, j, k; + u16 rectLeft; + u16 rectTop; + + gSPSegment(gfx++, 0x06, globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + + func_8009457C(&gfx); + + this->effectScroll -= 2; + + gDPSetCycleType(gfx++, G_CYC_2CYCLE); + gDPSetAlphaCompare(gfx++, G_AC_THRESHOLD); + gDPSetRenderMode(gfx++, G_RM_PASS, G_RM_CLD_SURF2); + gDPSetCombineLERP(gfx++, TEXEL1, PRIMITIVE, PRIM_LOD_FRAC, TEXEL0, TEXEL1, 1, PRIM_LOD_FRAC, TEXEL0, PRIMITIVE, + ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0); + + gDPSetPrimColor(gfx++, 0, (s16)this->effectPrimLodFrac, (s16)this->effectPrimColor[0], + (s16)this->effectPrimColor[1], (s16)this->effectPrimColor[2], (s16)this->effectAlpha); + gDPSetEnvColor(gfx++, (s16)this->effectEnvColor[0], (s16)this->effectEnvColor[1], (s16)this->effectEnvColor[2], + 255); + + if ((s16)this->effectPrimLodFrac != 0) { + for (k = 0, i = 0, rectTop = 0; i < 3; i++, rectTop += 64) { + for (j = 0, rectLeft = 56; j < 3; j++, k++, rectLeft += 64) { + EnMag_DrawEffectTextures(&gfx, effectMaskTextures[k], gTitleFlameEffectTex, 64, 64, 32, 32, rectLeft, + rectTop, 64, 64, 1024, 1024, 1, 1, k, this); + } + } + } + + gDPSetPrimColor(gfx++, 0, 0, 255, 255, 255, (s16)this->mainAlpha); + + if ((s16)this->mainAlpha != 0) { + EnMag_DrawImageRGBA32(&gfx, 152, 100, (u8*)gTitleZeldaShieldLogoMQTex, 160, 160); + } + + func_8009457C(&gfx); + + gDPPipeSync(gfx++); + gDPSetAlphaCompare(gfx++, G_AC_NONE); + gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + if ((s16)this->mainAlpha < 100) { + gDPSetRenderMode(gfx++, G_RM_CLD_SURF, G_RM_CLD_SURF2); + } else { + gDPSetRenderMode(gfx++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + } + + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, (s16)this->mainAlpha); + gDPSetEnvColor(gfx++, 0, 0, 100, 255); + + if ((s16)this->mainAlpha != 0) { + EnMag_DrawTextureI8(&gfx, gTitleTheLegendOfTextTex, 72, 8, 146, 73, 72, 8, 1024, 1024); + EnMag_DrawTextureI8(&gfx, gTitleOcarinaOfTimeTMTextTex, 96, 8, 144, 127, 96, 8, 1024, 1024); + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 100, 150, 255, (s16)this->mainAlpha); + gDPSetEnvColor(gfx++, 20, 80, 160, 255); + + EnMag_DrawTextureI8(&gfx, gTitleTheLegendOfTextTex, 72, 8, 145, 72, 72, 8, 1024, 1024); + EnMag_DrawTextureI8(&gfx, gTitleOcarinaOfTimeTMTextTex, 96, 8, 143, 126, 96, 8, 1024, 1024); + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 255, 255, 255, (s16)this->subAlpha); + EnMag_DrawImageRGBA32(&gfx, 174, 145, (u8*)gTitleMasterQuestSubtitleTex, 128, 32); + } + + func_8009457C(&gfx); + + gDPSetAlphaCompare(gfx++, G_AC_NONE); + gDPSetCombineMode(gfx++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + gDPSetPrimColor(gfx++, 0, 0, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha, + (s16)this->copyrightAlpha); + + if ((s16)this->copyrightAlpha != 0) { + gDPLoadTextureBlock(gfx++, gTitleCopyright19982003Tex, G_IM_FMT_IA, G_IM_SIZ_8b, 160, 16, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, 312, 792, 952, 856, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + if (gSaveContext.fileNum == 0xFEDC) { + // Draw "NO CONTROLLER" Text + textAlpha = textFadeTimer * 10; + if (textAlpha >= 255) { + textAlpha = 255; + } + + // Text Shadow + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0); + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, textAlpha); + + rectLeft = VREG(19) + 1; + for (i = 0; i < ARRAY_COUNT(noControllerFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + noControllerFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 172); + rectLeft += VREG(21); + if (i == 1) { + rectLeft += VREG(23); + } + } + + // Actual Text + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 100, 255, 255, textAlpha); + + rectLeft = VREG(19); + for (i = 0; i < ARRAY_COUNT(noControllerFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + noControllerFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 171); + rectLeft += VREG(21); + if (i == 1) { + rectLeft += VREG(23); + } + } + } else if (this->copyrightAlpha >= 200.0f) { + // Draw "PRESS START" Text + textAlpha = textFadeTimer * 10; + if (textAlpha >= 255) { + textAlpha = 255; + } + + // Text Shadow + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0); + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, textAlpha); + + rectLeft = YREG(7) + 1; + for (i = 0; i < ARRAY_COUNT(pressStartFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + pressStartFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 172); + rectLeft += YREG(8); + if (i == 4) { + rectLeft += YREG(9); + } + } + + // Actual Text + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, YREG(4), YREG(5), YREG(6), textAlpha); + + rectLeft = YREG(7); + for (i = 0; i < ARRAY_COUNT(pressStartFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + pressStartFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 171); + rectLeft += YREG(8); + if (i == 4) { + rectLeft += YREG(9); + } + } + } + + if (textFadeDirection != 0) { + if (--textFadeTimer == 0) { + textFadeDirection = 0; + } + } else { + if (++textFadeTimer >= 26) { + textFadeDirection = 1; + } + } + + *gfxp = gfx; +} +#else +void EnMag_DrawInner(Actor* thisx, GlobalContext* globalCtx, Gfx** gfxp) { + static s16 textAlpha = 0; + static s16 textFadeDirection = 0; + static s16 textFadeTimer = 0; + static u8 noControllerFontIndexes[] = { + 0x17, 0x18, 0x0C, 0x18, 0x17, 0x1D, 0x1B, 0x18, 0x15, 0x15, 0x0E, 0x1B, + }; + static u8 pressStartFontIndexes[] = { + 0x19, 0x1B, 0x0E, 0x1C, 0x1C, 0x1C, 0x1D, 0x0A, 0x1B, 0x1D, + }; + static void* effectMaskTextures[] = { + gTitleEffectMask00Tex, gTitleEffectMask01Tex, gTitleEffectMask02Tex, + gTitleEffectMask10Tex, gTitleEffectMask11Tex, gTitleEffectMask12Tex, + gTitleEffectMask20Tex, gTitleEffectMask21Tex, gTitleEffectMask22Tex, + }; + EnMag* this = (EnMag*)thisx; + Font* font = &this->font; + s32 pad; + Gfx* gfx = *gfxp; + u16 i, j, k; + u16 rectLeft; + u16 rectTop; + + gSPSegment(gfx++, 0x06, globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + + func_8009457C(&gfx); + + this->effectScroll -= 2; + + gDPSetCycleType(gfx++, G_CYC_2CYCLE); + gDPSetAlphaCompare(gfx++, G_AC_THRESHOLD); + gDPSetRenderMode(gfx++, G_RM_PASS, G_RM_CLD_SURF2); + gDPSetCombineLERP(gfx++, TEXEL1, PRIMITIVE, PRIM_LOD_FRAC, TEXEL0, TEXEL1, 1, PRIM_LOD_FRAC, TEXEL0, PRIMITIVE, + ENVIRONMENT, COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0); + + gDPSetPrimColor(gfx++, 0, (s16)this->effectPrimLodFrac, (s16)this->effectPrimColor[0], + (s16)this->effectPrimColor[1], (s16)this->effectPrimColor[2], (s16)this->effectAlpha); + gDPSetEnvColor(gfx++, (s16)this->effectEnvColor[0], (s16)this->effectEnvColor[1], (s16)this->effectEnvColor[2], + 255); + + if ((s16)this->effectPrimLodFrac != 0) { + for (k = 0, i = 0, rectTop = 0; i < 3; i++, rectTop += 64) { + for (j = 0, rectLeft = 64; j < 3; j++, k++, rectLeft += 64) { + EnMag_DrawEffectTextures(&gfx, effectMaskTextures[k], gTitleFlameEffectTex, 64, 64, 32, 32, rectLeft, + rectTop, 64, 64, 1024, 1024, 1, 1, k, this); + } + } + } + + gDPSetPrimColor(gfx++, 0, 0, 255, 255, 255, (s16)this->mainAlpha); + + if ((s16)this->mainAlpha != 0) { + EnMag_DrawImageRGBA32(&gfx, 160, 100, (u8*)gTitleZeldaShieldLogoMQTex, 160, 160); + } + + func_8009457C(&gfx); + + gDPPipeSync(gfx++); + gDPSetAlphaCompare(gfx++, G_AC_NONE); + gDPSetCombineLERP(gfx++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + if ((s16)this->mainAlpha < 100) { + gDPSetRenderMode(gfx++, G_RM_CLD_SURF, G_RM_CLD_SURF2); + } else { + gDPSetRenderMode(gfx++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + } + + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, (s16)this->mainAlpha); + gDPSetEnvColor(gfx++, 100, 0, 100, 255); + + if ((s16)this->mainAlpha != 0) { + EnMag_DrawTextureI8(&gfx, gTitleTheLegendOfTextTex, 72, 8, 154, 73, 72, 8, 1024, 1024); + EnMag_DrawTextureI8(&gfx, gTitleOcarinaOfTimeTMTextTex, 96, 8, 152, 127, 96, 8, 1024, 1024); + + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 200, 200, 150, (s16)this->mainAlpha); + gDPSetEnvColor(gfx++, 100, 100, 50, 255); + + EnMag_DrawTextureI8(&gfx, gTitleTheLegendOfTextTex, 72, 8, 153, 72, 72, 8, 1024, 1024); + EnMag_DrawTextureI8(&gfx, gTitleOcarinaOfTimeTMTextTex, 96, 8, 151, 126, 96, 8, 1024, 1024); + + if (1) {} + } + + func_8009457C(&gfx); + + gDPSetAlphaCompare(gfx++, G_AC_NONE); + gDPSetCombineMode(gfx++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + gDPSetPrimColor(gfx++, 0, 0, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha, (s16)this->copyrightAlpha, + (s16)this->copyrightAlpha); + + if ((s16)this->copyrightAlpha != 0) { + gDPLoadTextureBlock(gfx++, gTitleCopyright19982003Tex, G_IM_FMT_IA, G_IM_SIZ_8b, 160, 16, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPTextureRectangle(gfx++, 312, 792, 952, 856, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + if (gSaveContext.fileNum == 0xFEDC) { + // Draw "NO CONTROLLER" Text + textAlpha = textFadeTimer * 10; + if (textAlpha >= 255) { + textAlpha = 255; + } + + // Text Shadow + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0); + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, textAlpha); + + rectLeft = VREG(19) + 1; + for (i = 0; i < ARRAY_COUNT(noControllerFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + noControllerFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 172); + rectLeft += VREG(21); + if (i == 1) { + rectLeft += VREG(23); + } + } + + // Actual Text + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, 100, 255, 255, textAlpha); + + rectLeft = VREG(19); + for (i = 0; i < ARRAY_COUNT(noControllerFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + noControllerFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 171); + rectLeft += VREG(21); + if (i == 1) { + rectLeft += VREG(23); + } + } + } else if (this->copyrightAlpha >= 200.0f) { + // Draw "PRESS START" Text + textAlpha = textFadeTimer * 10; + if (textAlpha >= 255) { + textAlpha = 255; + } + + // Text Shadow + gDPPipeSync(gfx++); + gDPSetCombineLERP(gfx++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, + 0); + gDPSetPrimColor(gfx++, 0, 0, 0, 0, 0, textAlpha); + + rectLeft = YREG(7) + 1; + for (i = 0; i < ARRAY_COUNT(pressStartFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + pressStartFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 172); + rectLeft += YREG(8); + if (i == 4) { + rectLeft += YREG(9); + } + } + + // Actual Text + gDPPipeSync(gfx++); + gDPSetPrimColor(gfx++, 0, 0, YREG(4), YREG(5), YREG(6), textAlpha); + + rectLeft = YREG(7); + for (i = 0; i < ARRAY_COUNT(pressStartFontIndexes); i++) { + EnMag_DrawCharTexture(&gfx, font->fontBuf + pressStartFontIndexes[i] * FONT_CHAR_TEX_SIZE, rectLeft, + YREG(10) + 171); + rectLeft += YREG(8); + if (i == 4) { + rectLeft += YREG(9); + } + } + } + + if (textFadeDirection != 0) { + if (--textFadeTimer == 0) { + textFadeDirection = 0; + } + } else { + if (++textFadeTimer >= 26) { + textFadeDirection = 1; + } + } + + *gfxp = gfx; +} +#endif + +void EnMag_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + Gfx* gfx; + Gfx* gfxRef; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_mag.c", 1151); + + gfxRef = POLY_OPA_DISP; + gfx = Graph_GfxPlusOne(gfxRef); + gSPDisplayList(OVERLAY_DISP++, gfx); + + EnMag_DrawInner(thisx, globalCtx, &gfx); + + gSPEndDisplayList(gfx++); + Graph_BranchDlist(gfxRef, gfx); + POLY_OPA_DISP = gfx; + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_mag.c", 1161); +} diff --git a/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.h b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.h new file mode 100644 index 000000000..a9f1b9d14 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mag/z_en_mag.h @@ -0,0 +1,43 @@ +#ifndef Z_EN_MAG_H +#define Z_EN_MAG_H + +#include "ultra64.h" +#include "global.h" + +struct EnMag; + +typedef struct EnMag { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[0x0004]; + /* 0x0150 */ Font font; + /* 0xE2D8 */ s16 effectFadeInState; + /* 0xE2DA */ s16 effectFadeInTimer; + /* 0xE2DC */ s16 globalState; + /* 0xE2E0 */ f32 effectPrimLodFrac; + /* 0xE2E4 */ f32 effectPrimColor[3]; + /* 0xE2F0 */ f32 effectEnvColor[3]; + /* 0xE2FC */ f32 effectAlpha; + /* 0xE300 */ f32 mainAlpha; + /* 0xE304 */ f32 subAlpha; + /* 0xE308 */ f32 copyrightAlpha; + /* 0xE30C */ s16 unk_E30C; + /* 0xE30E */ s16 effectScroll; + /* 0xE310 */ char unk_E310[0x0002]; + /* 0xE312 */ s16 copyrightAlphaStep; + /* 0xE314 */ s16 fadeOutAlphaStep; + /* 0xE316 */ s16 unk_E316; + /* 0xE318 */ s16 unk_E318; + /* 0xE31C */ s32 unk_E31C; + /* 0xE320 */ s32 unk_E320; + /* 0xE324 */ char unk_E324[0x0004]; +} EnMag; // size = 0xE328 + +typedef enum { + /* 0x00 */ MAG_STATE_INITIAL, + /* 0x01 */ MAG_STATE_FADE_IN, + /* 0x02 */ MAG_STATE_DISPLAY, + /* 0x03 */ MAG_STATE_FADE_OUT, + /* 0x04 */ MAG_STATE_POST_DISPLAY +} EnMagGlobalState; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c new file mode 100644 index 000000000..7778ae6ba --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -0,0 +1,1559 @@ +/* + * File: z_en_mb.c + * Overlay: ovl_En_Mb + * Description: Moblins + */ + +#include "z_en_mb.h" +#include "objects/object_mb/object_mb.h" + +/* + * This actor can have three behaviors: + * - "Spear Guard" (variable -1): uses a spear, walks around home point, charges player if too close + * - "Club" (variable 0): uses a club, stands still and smashes its club on the ground when the player approaches + * - "Spear Patrol" (variable 0xPP00 PP=pathId): uses a spear, patrols following a path, charges + */ + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +typedef enum { + /* -1 */ ENMB_TYPE_SPEAR_GUARD = -1, + /* 0 */ ENMB_TYPE_CLUB, + /* 1 */ ENMB_TYPE_SPEAR_PATROL +} EnMbType; + +#define ENMB_ATTACK_NONE 0 +#define ENMB_ATTACK_SPEAR 1 +#define ENMB_ATTACK_CLUB_RIGHT 1 +#define ENMB_ATTACK_CLUB_MIDDLE 2 +#define ENMB_ATTACK_CLUB_LEFT 3 + +/* Spear and Club moblins use a different skeleton but the limbs are organized the same */ +typedef enum { + /* 1 */ ENMB_LIMB_ROOT = 1, + /* 3 */ ENMB_LIMB_WAIST = 3, + /* 6 */ ENMB_LIMB_CHEST = 6, + /* 7 */ ENMB_LIMB_HEAD, + /* 9 */ ENMB_LIMB_LSHOULDER = 9, + /* 11 */ ENMB_LIMB_LFOREARM = 11, + /* 12 */ ENMB_LIMB_LHAND, + /* 14 */ ENMB_LIMB_RSHOULDER = 14, + /* 16 */ ENMB_LIMB_RFOREARM = 16, + /* 17 */ ENMB_LIMB_RHAND, + /* 20 */ ENMB_LIMB_LTHIGH = 20, + /* 21 */ ENMB_LIMB_LSHIN, + /* 22 */ ENMB_LIMB_LFOOT, + /* 25 */ ENMB_LIMB_RTHIGH = 25, + /* 26 */ ENMB_LIMB_RSHIN, + /* 27 */ ENMB_LIMB_RFOOT +} EnMbLimb; + +void EnMb_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMb_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMb_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Mb_InitVars = { + ACTOR_EN_MB, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_MB, + sizeof(EnMb), + (ActorFunc)EnMb_Init, + (ActorFunc)EnMb_Destroy, + (ActorFunc)EnMb_Update, + (ActorFunc)EnMb_Draw, + NULL, +}; + +void EnMb_SetupSpearPatrolTurnTowardsWaypoint(EnMb* this, GlobalContext* globalCtx); +void EnMb_SetupClubWaitPlayerNear(EnMb* this); +void EnMb_SpearGuardLookAround(EnMb* this, GlobalContext* globalCtx); +void EnMb_SetupSpearGuardLookAround(EnMb* this); +void EnMb_SetupSpearDamaged(EnMb* this); +void EnMb_SpearGuardWalk(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearGuardPrepareAndCharge(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearPatrolPrepareAndCharge(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearEndChargeQuick(EnMb* this, GlobalContext* globalCtx); +void EnMb_Stunned(EnMb* this, GlobalContext* globalCtx); +void EnMb_ClubDead(EnMb* this, GlobalContext* globalCtx); +void EnMb_ClubDamagedWhileKneeling(EnMb* this, GlobalContext* globalCtx); +void EnMb_ClubWaitPlayerNear(EnMb* this, GlobalContext* globalCtx); +void EnMb_ClubAttack(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearDead(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearDamaged(EnMb* this, GlobalContext* globalCtx); +void EnMb_SetupSpearDead(EnMb* this); +void EnMb_SpearPatrolTurnTowardsWaypoint(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearPatrolWalkTowardsWaypoint(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearPatrolEndCharge(EnMb* this, GlobalContext* globalCtx); +void EnMb_SpearPatrolImmediateCharge(EnMb* this, GlobalContext* globalCtx); +void EnMb_ClubWaitAfterAttack(EnMb* this, GlobalContext* globalCtx); +void EnMb_ClubDamaged(EnMb* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sHitboxInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderTrisElementInit sFrontShieldingTrisInit[2] = { + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, 14.0f, 2.0f }, { -10.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, + { + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO, + OCELEM_NONE, + }, + { { { -10.0f, -6.0f, 2.0f }, { 9.0f, -6.0f, 2.0f }, { 9.0f, 14.0f, 2.0f } } }, + }, +}; + +static ColliderTrisInit sFrontShieldingInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + sFrontShieldingTrisInit, +}; + +static ColliderQuadInit sAttackColliderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +typedef enum { + /* 0x0 */ ENMB_DMGEFF_IGNORE, + /* 0x1 */ ENMB_DMGEFF_STUN, + /* 0x5 */ ENMB_DMGEFF_FREEZE = 0x5, + /* 0x6 */ ENMB_DMGEFF_STUN_ICE, + /* 0xF */ ENMB_DMGEFF_DEFAULT = 0xF +} EnMbDamageEffect; + +static DamageTable sSpearMoblinDamageTable = { + /* Deku nut */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Deku stick */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Slingshot */ DMG_ENTRY(1, ENMB_DMGEFF_DEFAULT), + /* Explosive */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Boomerang */ DMG_ENTRY(0, ENMB_DMGEFF_STUN), + /* Normal arrow */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Hammer swing */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Hookshot */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Kokiri sword */ DMG_ENTRY(1, ENMB_DMGEFF_DEFAULT), + /* Master sword */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Giant's Knife */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Fire arrow */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Ice arrow */ DMG_ENTRY(4, ENMB_DMGEFF_STUN_ICE), + /* Light arrow */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Unk arrow 1 */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Unk arrow 2 */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Unk arrow 3 */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Fire magic */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Ice magic */ DMG_ENTRY(3, ENMB_DMGEFF_STUN_ICE), + /* Light magic */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Shield */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Mirror Ray */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Kokiri spin */ DMG_ENTRY(1, ENMB_DMGEFF_DEFAULT), + /* Giant spin */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Master spin */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Kokiri jump */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Giant jump */ DMG_ENTRY(8, ENMB_DMGEFF_DEFAULT), + /* Master jump */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Unknown 1 */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Unblockable */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Hammer jump */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Unknown 2 */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), +}; + +static DamageTable sClubMoblinDamageTable = { + /* Deku nut */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Deku stick */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Slingshot */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Explosive */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Boomerang */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Normal arrow */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Hammer swing */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Hookshot */ DMG_ENTRY(0, ENMB_DMGEFF_STUN), + /* Kokiri sword */ DMG_ENTRY(1, ENMB_DMGEFF_DEFAULT), + /* Master sword */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Giant's Knife */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Fire arrow */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Ice arrow */ DMG_ENTRY(4, ENMB_DMGEFF_STUN_ICE), + /* Light arrow */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Unk arrow 1 */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Unk arrow 2 */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Unk arrow 3 */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Fire magic */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Ice magic */ DMG_ENTRY(3, ENMB_DMGEFF_STUN_ICE), + /* Light magic */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Shield */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Mirror Ray */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Kokiri spin */ DMG_ENTRY(1, ENMB_DMGEFF_DEFAULT), + /* Giant spin */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Master spin */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Kokiri jump */ DMG_ENTRY(2, ENMB_DMGEFF_DEFAULT), + /* Giant jump */ DMG_ENTRY(8, ENMB_DMGEFF_DEFAULT), + /* Master jump */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Unknown 1 */ DMG_ENTRY(0, ENMB_DMGEFF_FREEZE), + /* Unblockable */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), + /* Hammer jump */ DMG_ENTRY(4, ENMB_DMGEFF_DEFAULT), + /* Unknown 2 */ DMG_ENTRY(0, ENMB_DMGEFF_IGNORE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x4A, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 5300, ICHAIN_STOP), +}; + +void EnMb_SetupAction(EnMb* this, EnMbActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnMb_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMb* this = (EnMb*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s16 relYawFromPlayer; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 46.0f); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.colChkInfo.damageTable = &sSpearMoblinDamageTable; + Collider_InitCylinder(globalCtx, &this->hitbox); + Collider_SetCylinder(globalCtx, &this->hitbox, &this->actor, &sHitboxInit); + Collider_InitTris(globalCtx, &this->frontShielding); + Collider_SetTris(globalCtx, &this->frontShielding, &this->actor, &sFrontShieldingInit, this->frontShieldingTris); + Collider_InitQuad(globalCtx, &this->attackCollider); + Collider_SetQuad(globalCtx, &this->attackCollider, &this->actor, &sAttackColliderInit); + + switch (this->actor.params) { + case ENMB_TYPE_SPEAR_GUARD: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gEnMbSpearSkel, &gEnMbSpearStandStillAnim, + this->jointTable, this->morphTable, 28); + this->actor.colChkInfo.health = 2; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->maxHomeDist = 1000.0f; + this->playerDetectionRange = 1750.0f; + EnMb_SetupSpearGuardLookAround(this); + break; + case ENMB_TYPE_CLUB: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gEnMbClubSkel, &gEnMbClubStandStillClubDownAnim, + this->jointTable, this->morphTable, 28); + + this->actor.colChkInfo.health = 6; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.colChkInfo.damageTable = &sClubMoblinDamageTable; + Actor_SetScale(&this->actor, 0.02f); + this->hitbox.dim.height = 170; + this->hitbox.dim.radius = 45; + this->actor.uncullZoneForward = 4000.0f; + this->actor.uncullZoneScale = 800.0f; + this->actor.uncullZoneDownward = 1800.0f; + this->playerDetectionRange = 710.0f; + this->attackCollider.info.toucher.dmgFlags = 0x20000000; + + relYawFromPlayer = + this->actor.world.rot.y - Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos); + if (ABS(relYawFromPlayer) > 0x4000) { + this->actor.world.rot.y = thisx->world.rot.y + 0x8000; + this->actor.shape.rot.y = thisx->world.rot.y; + this->actor.world.pos.z = thisx->world.pos.z + 600.0f; + } + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawFeet, 90.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.naviEnemyId += 1; + EnMb_SetupClubWaitPlayerNear(this); + break; + default: /* Spear Patrol */ + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gEnMbSpearSkel, &gEnMbSpearStandStillAnim, + this->jointTable, this->morphTable, 28); + + Actor_SetScale(&this->actor, 0.014f); + this->path = (thisx->params & 0xFF00) >> 8; + this->actor.params = ENMB_TYPE_SPEAR_PATROL; + this->waypoint = 0; + this->actor.colChkInfo.health = 1; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->maxHomeDist = 350.0f; + this->playerDetectionRange = 1750.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + EnMb_SetupSpearPatrolTurnTowardsWaypoint(this, globalCtx); + break; + } +} + +void EnMb_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMb* this = (EnMb*)thisx; + + Collider_DestroyTris(globalCtx, &this->frontShielding); + Collider_DestroyCylinder(globalCtx, &this->hitbox); + Collider_DestroyQuad(globalCtx, &this->attackCollider); +} + +void EnMb_FaceWaypoint(EnMb* this, GlobalContext* globalCtx) { + s16 yawToWaypoint = Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos); + + this->actor.shape.rot.y = yawToWaypoint; + this->actor.world.rot.y = yawToWaypoint; +} + +void EnMb_NextWaypoint(EnMb* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* waypointPos; + + path = &globalCtx->setupPathList[this->path]; + + if (this->waypoint == 0) { + this->direction = 1; + } else if (this->waypoint == (s8)(path->count - 1)) { + this->direction = -1; + } + + this->waypoint += this->direction; + waypointPos = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + this->waypoint; + this->waypointPos.x = waypointPos->x; + this->waypointPos.y = waypointPos->y; + this->waypointPos.z = waypointPos->z; +} + +/** + * Checks if the player is in a 800*74 units XZ area centered on this actor, + * the area being directed along its line of sight snapped to a cardinal angle. + * Note: the longest corridor in Sacred Forest Meadows is 800 units long, + * and they all are 100 units wide. + */ +s32 EnMb_IsPlayerInCorridor(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 xFromPlayer; + f32 zFromPlayer; + f32 cos; + f32 sin; + f32 xFromPlayerAbs; + f32 zFromPlayerAbs; + s16 alignedYaw = 0; + + if ((this->actor.world.rot.y < -0x62A2) || (this->actor.world.rot.y > 0x62A2)) { + alignedYaw = -0x8000; + } else if (this->actor.world.rot.y < -0x20E0) { + alignedYaw = -0x4000; + } else if (this->actor.world.rot.y > 0x20E0) { + alignedYaw = 0x4000; + } + + cos = Math_CosS(alignedYaw); + sin = Math_SinS(alignedYaw); + cos = ABS(cos); + sin = ABS(sin); + xFromPlayer = this->actor.world.pos.x - player->actor.world.pos.x; + zFromPlayer = this->actor.world.pos.z - player->actor.world.pos.z; + xFromPlayerAbs = ABS(xFromPlayer); + if (xFromPlayerAbs < (cos * 37.0f + sin * 400.0f)) { + zFromPlayerAbs = ABS(zFromPlayer); + if (zFromPlayerAbs < (sin * 37.0f + cos * 400.0f)) { + return true; + } + } + return false; +} + +void EnMb_FindWaypointTowardsPlayer(EnMb* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->path]; + s16 yawToWaypoint; + Vec3f waypointPosF; + Vec3s* waypointPosS; + s16 yawPlayerToWaypoint; + s32 i; + s32 waypoint; + + for (waypoint = 0, i = path->count - 1; i >= 0; i--, waypoint++) { + waypointPosS = (Vec3s*)SEGMENTED_TO_VIRTUAL(path->points) + waypoint; + waypointPosF.x = waypointPosS->x; + waypointPosF.y = waypointPosS->y; + waypointPosF.z = waypointPosS->z; + yawToWaypoint = Math_Vec3f_Yaw(&this->actor.world.pos, &waypointPosF); + yawPlayerToWaypoint = yawToWaypoint - this->actor.yawTowardsPlayer; + if (ABS(yawPlayerToWaypoint) <= 0x1770) { + this->actor.world.rot.y = yawToWaypoint; + if (waypoint == this->waypoint) { + this->direction = -this->direction; + } + this->waypointPos = waypointPosF; + this->waypoint = waypoint; + break; + } + } +} + +void EnMb_SetupSpearGuardLookAround(EnMb* this) { + Animation_MorphToLoop(&this->skelAnime, &gEnMbSpearLookLeftAndRightAnim, -4.0f); + this->actor.speedXZ = 0.0f; + this->timer1 = Rand_S16Offset(30, 50); + this->state = ENMB_STATE_IDLE; + EnMb_SetupAction(this, EnMb_SpearGuardLookAround); +} + +void EnMb_SetupClubWaitPlayerNear(EnMb* this) { + Animation_PlayLoop(&this->skelAnime, &gEnMbClubStandStillClubDownAnim); + this->actor.speedXZ = 0.0f; + this->timer1 = Rand_S16Offset(30, 50); + this->state = ENMB_STATE_IDLE; + EnMb_SetupAction(this, EnMb_ClubWaitPlayerNear); +} + +void EnMb_SetupSpearPatrolTurnTowardsWaypoint(EnMb* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &gEnMbSpearLookLeftAndRightAnim, -4.0f); + this->actor.speedXZ = 0.0f; + this->timer1 = Rand_S16Offset(40, 80); + this->state = ENMB_STATE_IDLE; + EnMb_NextWaypoint(this, globalCtx); + EnMb_SetupAction(this, EnMb_SpearPatrolTurnTowardsWaypoint); +} + +void EnMb_SetupSpearGuardWalk(EnMb* this) { + Animation_Change(&this->skelAnime, &gEnMbSpearWalkAnim, 0.0f, 0.0f, Animation_GetLastFrame(&gEnMbSpearWalkAnim), + ANIMMODE_LOOP, -4.0f); + this->actor.speedXZ = 0.59999996f; + this->timer1 = Rand_S16Offset(50, 70); + this->unk_332 = 1; + this->state = ENMB_STATE_WALK; + EnMb_SetupAction(this, EnMb_SpearGuardWalk); +} + +void EnMb_SetupSpearPatrolWalkTowardsWaypoint(EnMb* this) { + f32 frameCount = Animation_GetLastFrame(&gEnMbSpearWalkAnim); + + this->actor.speedXZ = 0.59999996f; + this->timer1 = Rand_S16Offset(50, 70); + this->unk_332 = 1; + this->state = ENMB_STATE_WALK; + Animation_Change(&this->skelAnime, &gEnMbSpearWalkAnim, 0.0f, 0.0f, frameCount, ANIMMODE_LOOP_INTERP, -4.0f); + EnMb_SetupAction(this, EnMb_SpearPatrolWalkTowardsWaypoint); +} + +void EnMb_SetupSpearPrepareAndCharge(EnMb* this) { + f32 frameCount = Animation_GetLastFrame(&gEnMbSpearPrepareChargeAnim); + + Animation_MorphToPlayOnce(&this->skelAnime, &gEnMbSpearPrepareChargeAnim, -4.0f); + this->state = ENMB_STATE_ATTACK; + this->actor.speedXZ = 0.0f; + this->timer3 = (s16)frameCount + 6; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_SPEAR_AT); + if (this->actor.params == ENMB_TYPE_SPEAR_GUARD) { + EnMb_SetupAction(this, EnMb_SpearGuardPrepareAndCharge); + } else { + EnMb_SetupAction(this, EnMb_SpearPatrolPrepareAndCharge); + } +} + +void EnMb_SetupSpearPatrolImmediateCharge(EnMb* this) { + Animation_PlayLoop(&this->skelAnime, &gEnMbSpearChargeAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_ATTACK); + this->attack = ENMB_ATTACK_SPEAR; + this->state = ENMB_STATE_ATTACK; + this->timer3 = 3; + this->actor.speedXZ = 10.0f; + EnMb_SetupAction(this, EnMb_SpearPatrolImmediateCharge); +} + +void EnMb_SetupClubAttack(EnMb* this) { + f32 frames = Animation_GetLastFrame(&gEnMbClubLiftClubAnim); + s16 relYawFromPlayer; + + this->state = ENMB_STATE_ATTACK; + Animation_Change(&this->skelAnime, &gEnMbClubLiftClubAnim, 3.0f, 0.0f, frames, ANIMMODE_ONCE_INTERP, 0.0f); + this->timer3 = 1; + relYawFromPlayer = this->actor.world.rot.y - this->actor.yawTowardsPlayer; + + if (ABS(relYawFromPlayer) <= 0x258) { + this->attack = ENMB_ATTACK_CLUB_MIDDLE; + } else if (relYawFromPlayer >= 0) { + this->attack = ENMB_ATTACK_CLUB_RIGHT; + } else { + this->attack = ENMB_ATTACK_CLUB_LEFT; + } + + EnMb_SetupAction(this, EnMb_ClubAttack); +} + +void EnMb_SetupSpearEndChargeQuick(EnMb* this) { + Animation_PlayOnce(&this->skelAnime, &gEnMbSpearSlowDownAnim); + this->state = ENMB_STATE_ATTACK_END; + this->timer1 = 0; + this->timer3 = 5; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_SLIDE); + EnMb_SetupAction(this, EnMb_SpearEndChargeQuick); +} + +void EnMb_SetupSpearPatrolEndCharge(EnMb* this) { + Animation_PlayOnce(&this->skelAnime, &gEnMbSpearSlowDownAnim); + this->state = ENMB_STATE_ATTACK_END; + this->actor.bgCheckFlags &= ~1; + this->timer1 = 0; + this->timer3 = 50; + this->actor.speedXZ = -8.0f; + this->actor.velocity.y = 6.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_SLIDE); + EnMb_SetupAction(this, EnMb_SpearPatrolEndCharge); +} + +void EnMb_SetupClubWaitAfterAttack(EnMb* this) { + f32 frameCount = Animation_GetLastFrame(&gEnMbClubStandStillClubDownAnim); + + this->state = ENMB_STATE_ATTACK_END; + Animation_Change(&this->skelAnime, &gEnMbClubStandStillClubDownAnim, 5.0f, 0.0f, frameCount, ANIMMODE_ONCE_INTERP, + 0.0f); + EnMb_SetupAction(this, EnMb_ClubWaitAfterAttack); +} + +void EnMb_SetupClubDamaged(EnMb* this) { + Animation_PlayOnce(&this->skelAnime, &gEnMbClubDamagedKneelAnim); + this->state = ENMB_STATE_CLUB_KNEELING; + this->timer1 = 0; + this->timer3 = 20; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DEAD); + EnMb_SetupAction(this, EnMb_ClubDamaged); +} + +void EnMb_SetupClubDamagedWhileKneeling(EnMb* this) { + f32 frames = Animation_GetLastFrame(&gEnMbClubBeatenKneelingAnim); + + this->state = ENMB_STATE_CLUB_KNEELING_DAMAGED; + this->timer1 = 0; + this->timer3 = 6; + Animation_Change(&this->skelAnime, &gEnMbClubBeatenKneelingAnim, 1.0f, 4.0f, frames, ANIMMODE_ONCE_INTERP, 0.0f); + EnMb_SetupAction(this, EnMb_ClubDamagedWhileKneeling); +} + +void EnMb_SetupClubDead(EnMb* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gEnMbClubFallOnItsBackAnim, -4.0f); + this->state = ENMB_STATE_CLUB_DEAD; + this->actor.flags &= ~ACTOR_FLAG_0; + this->hitbox.dim.height = 80; + this->hitbox.dim.radius = 95; + this->timer1 = 30; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DEAD); + EnMb_SetupAction(this, EnMb_ClubDead); +} + +void EnMb_SetupStunned(EnMb* this) { + this->state = ENMB_STATE_STUNNED; + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 0x78, 0, 0x50); + if (this->damageEffect == ENMB_DMGEFF_STUN_ICE) { + this->iceEffectTimer = 40; + } else { + if (this->actor.params != ENMB_TYPE_CLUB) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gEnMbSpearDamagedFromFrontAnim, 0.0f); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + } + EnMb_SetupAction(this, EnMb_Stunned); +} + +void EnMb_Stunned(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((player->stateFlags2 & 0x80) && player->actor.parent == &this->actor) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.world.rot.y, 4.0f); + this->attack = ENMB_ATTACK_NONE; + } + + if (this->actor.colorFilterTimer == 0) { + if (this->actor.params == ENMB_TYPE_CLUB) { + if (this->actor.colChkInfo.health == 0) { + EnMb_SetupClubDead(this); + } else if (this->state == ENMB_STATE_CLUB_KNEELING) { + /* dead code: the setup for this action sets state to something else */ + EnMb_SetupClubDamagedWhileKneeling(this); + } else { + EnMb_SetupClubWaitPlayerNear(this); + } + } else { + if (this->actor.colChkInfo.health == 0) { + EnMb_SetupSpearDead(this); + } else { + EnMb_SetupSpearDamaged(this); + } + } + } +} + +void EnMb_SpearGuardLookAround(EnMb* this, GlobalContext* globalCtx) { + s16 timer1; + + SkelAnime_Update(&this->skelAnime); + if (this->timer1 == 0) { + timer1 = 0; + } else { + this->timer1--; + timer1 = this->timer1; + } + if (timer1 == 0 && Animation_OnFrame(&this->skelAnime, 0.0f)) { + EnMb_SetupSpearGuardWalk(this); + } +} + +void EnMb_SpearPatrolTurnTowardsWaypoint(EnMb* this, GlobalContext* globalCtx) { + s16 relYawFromPlayer; + + SkelAnime_Update(&this->skelAnime); + + if (this->timer1 == 0) { + this->yawToWaypoint = Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos); + if (Math_SmoothStepToS(&this->actor.shape.rot.y, this->yawToWaypoint, 1, 0x3E8, 0) == 0) { + this->actor.world.rot.y = this->actor.shape.rot.y; + EnMb_SetupSpearPatrolWalkTowardsWaypoint(this); + } + } else { + this->timer1--; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 1, 0x3E8, 0); + } + + if (ABS(this->actor.yDistToPlayer) <= 20.0f && EnMb_IsPlayerInCorridor(this, globalCtx)) { + relYawFromPlayer = this->actor.shape.rot.y - this->actor.yawTowardsPlayer; + if (ABS(relYawFromPlayer) <= 0x4000 || (func_8002DDE4(globalCtx) && this->actor.xzDistToPlayer < 160.0f)) { + EnMb_FindWaypointTowardsPlayer(this, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_VOICE); + EnMb_SetupSpearPrepareAndCharge(this); + } + } +} + +/** + * Slow down and resume walking. + */ +void EnMb_SpearEndChargeQuick(EnMb* this, GlobalContext* globalCtx) { + s32 pad; + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.5f, 1.0f, 0.0f); + if (this->actor.speedXZ > 1.0f) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 5.0f, 3, 4.0f, 100, 15, false); + } + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer1 == 0) { + this->timer3--; + if (this->timer3 == 0) { + /* Play the charge animation in reverse: let go of the spear and stand normally */ + Animation_Change(&this->skelAnime, &gEnMbSpearPrepareChargeAnim, -1.0f, + Animation_GetLastFrame(&gEnMbSpearPrepareChargeAnim), 0.0f, ANIMMODE_ONCE, 0.0f); + this->timer1 = 1; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_SPEAR_NORM); + } + } else { + if (this->actor.params <= ENMB_TYPE_SPEAR_GUARD) { + EnMb_SetupSpearGuardWalk(this); + this->timer1 = this->timer2 = this->timer3 = 80; + } else { + EnMb_SetupSpearPatrolTurnTowardsWaypoint(this, globalCtx); + } + } + } +} + +void EnMb_ClubWaitAfterAttack(EnMb* this, GlobalContext* globalCtx) { + this->attack = ENMB_ATTACK_NONE; + if (SkelAnime_Update(&this->skelAnime)) { + EnMb_SetupClubWaitPlayerNear(this); + } +} + +/** + * Slow down, charge again if the player is near, or resume walking. + */ +void EnMb_SpearPatrolEndCharge(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 lastFrame; + s16 relYawFromPlayer; + s16 yawPlayerToWaypoint; + + if ((player->stateFlags2 & 0x80) && player->actor.parent == &this->actor) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.world.rot.y, 4.0f); + } + + if (this->actor.bgCheckFlags & 1) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.5f, 0.0f); + + if (this->actor.speedXZ > 1.0f) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 5.0f, 3, 4.0f, 100, 15, false); + } + + if (this->timer1 != 0) { + this->timer3--; + if (this->timer3 == 0) { + relYawFromPlayer = this->actor.shape.rot.y - this->actor.yawTowardsPlayer; + + if (ABS(this->actor.yDistToPlayer) <= 20.0f && EnMb_IsPlayerInCorridor(this, globalCtx) && + ABS(relYawFromPlayer) <= 0x4000 && this->actor.xzDistToPlayer <= 200.0f) { + EnMb_SetupSpearPrepareAndCharge(this); + } else { + lastFrame = Animation_GetLastFrame(&gEnMbSpearPrepareChargeAnim); + /* Play the charge animation in reverse: let go of the spear and stand normally */ + Animation_Change(&this->skelAnime, &gEnMbSpearPrepareChargeAnim, -1.0f, lastFrame, 0.0f, + ANIMMODE_ONCE, 0.0f); + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_SPEAR_NORM); + } + } else { + if (this->actor.xzDistToPlayer <= 160.0f) { + this->actor.speedXZ = -5.0f; + } else { + this->actor.speedXZ = 0.0f; + } + } + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer1 == 0) { + lastFrame = Animation_GetLastFrame(&gEnMbSpearChargeAnim); + Animation_Change(&this->skelAnime, &gEnMbSpearChargeAnim, 0.5f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, + 0.0f); + this->timer1 = 1; + } else { + yawPlayerToWaypoint = + Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos) - this->actor.yawTowardsPlayer; + + if (ABS(yawPlayerToWaypoint) <= 0x4000) { + EnMb_SetupSpearPatrolTurnTowardsWaypoint(this, globalCtx); + } else { + EnMb_SetupSpearPatrolWalkTowardsWaypoint(this); + } + } + } + } +} + +/** + * Prepare charge (animation), then charge until the player isn't in front. + */ +void EnMb_SpearGuardPrepareAndCharge(EnMb* this, GlobalContext* globalCtx) { + s32 prevFrame; + s16 relYawTowardsPlayerAbs = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (relYawTowardsPlayerAbs < 0) { + relYawTowardsPlayerAbs = -relYawTowardsPlayerAbs; + } + + prevFrame = this->skelAnime.curFrame; + + if (SkelAnime_Update(&this->skelAnime)) { + Animation_PlayLoop(&this->skelAnime, &gEnMbSpearChargeAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_ATTACK); + } + + if (this->timer3 != 0) { + this->timer3--; + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 0); + } else { + this->actor.speedXZ = 10.0f; + this->attack = ENMB_ATTACK_SPEAR; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 5.0f, 3, 4.0f, 100, 15, false); + if (prevFrame != (s32)this->skelAnime.curFrame && + ((s32)this->skelAnime.curFrame == 2 || (s32)this->skelAnime.curFrame == 6)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DASH); + } + } + + if (relYawTowardsPlayerAbs > 0x1388) { + this->attack = ENMB_ATTACK_NONE; + EnMb_SetupSpearEndChargeQuick(this); + } +} + +void EnMb_ClubAttack(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + Vec3f effSpawnPos; + Vec3f effWhiteShockwaveDynamics = { 0.0f, 0.0f, 0.0f }; + f32 flamesParams[] = { 18.0f, 18.0f, 0.0f }; + s16 flamesUnused[] = { 20, 40, 0 }; + s16 relYawTarget[] = { -0x9C4, 0, 0xDAC }; + + Math_SmoothStepToS(&this->actor.shape.rot.y, relYawTarget[this->attack - 1] + this->actor.world.rot.y, 1, 0x2EE, 0); + + if (this->attackCollider.base.atFlags & AT_HIT) { + this->attackCollider.base.atFlags &= ~AT_HIT; + if (this->attackCollider.base.at == &player->actor) { + u8 prevPlayerInvincibilityTimer = player->invincibilityTimer; + + if (player->invincibilityTimer < 0) { + if (player->invincibilityTimer <= -40) { + player->invincibilityTimer = 0; + } else { + player->invincibilityTimer = 0; + globalCtx->damagePlayer(globalCtx, -8); + } + } + + func_8002F71C(globalCtx, &this->actor, (650.0f - this->actor.xzDistToPlayer) * 0.04f + 4.0f, + this->actor.world.rot.y, 8.0f); + + player->invincibilityTimer = prevPlayerInvincibilityTimer; + } + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer3 != 0) { + this->timer3--; + if (this->timer3 == 0) { + f32 lastAnimFrame = Animation_GetLastFrame(&gEnMbClubStrikeDownAnim); + Animation_Change(&this->skelAnime, &gEnMbClubStrikeDownAnim, 1.5f, 0.0f, lastAnimFrame, + ANIMMODE_ONCE_INTERP, 0.0f); + } + } else { + effSpawnPos = this->effSpawnPos; + effSpawnPos.y = this->actor.floorHeight; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MONBLIN_HAM_LAND); + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + EffectSsBlast_SpawnWhiteShockwave(globalCtx, &effSpawnPos, &effWhiteShockwaveDynamics, + &effWhiteShockwaveDynamics); + func_80033480(globalCtx, &effSpawnPos, 2.0f, 3, 0x12C, 0xB4, 1); + Camera_AddQuake(&globalCtx->mainCamera, 2, 0x19, 5); + func_800358DC(&this->actor, &effSpawnPos, &this->actor.world.rot, flamesParams, 20, flamesUnused, globalCtx, + -1, 0); + EnMb_SetupClubWaitAfterAttack(this); + } + } else { + if (this->timer3 != 0 && this->skelAnime.curFrame == 6.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MONBLIN_HAM_UP); + + } else if (this->timer3 == 0 && this->skelAnime.curFrame == 3.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MONBLIN_HAM_DOWN); + } + } +} + +/** + * Prepare charge (animation), then charge to the end of the floor collision. + */ +void EnMb_SpearPatrolPrepareAndCharge(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 prevFrame; + s32 hasHitPlayer = false; + s32 endCharge = !Actor_TestFloorInDirection(&this->actor, globalCtx, 110.0f, this->actor.world.rot.y); + + prevFrame = (s32)this->skelAnime.curFrame; + if (SkelAnime_Update(&this->skelAnime)) { + Animation_PlayLoop(&this->skelAnime, &gEnMbSpearChargeAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_ATTACK); + } + + if (this->timer3 != 0) { + this->timer3--; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 1, 0x1F40, 0); + endCharge = false; + } else { + this->actor.speedXZ = 10.0f; + this->attack = ENMB_ATTACK_SPEAR; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 5.0f, 3, 4.0f, 100, 15, false); + if (prevFrame != (s32)this->skelAnime.curFrame && + ((s32)this->skelAnime.curFrame == 2 || (s32)this->skelAnime.curFrame == 6)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DASH); + } + } + + if (this->attackCollider.base.atFlags & AT_HIT) { + if (this->attackCollider.base.at == &player->actor) { + if (!endCharge && !(player->stateFlags2 & 0x80)) { + if (player->invincibilityTimer < 0) { + if (player->invincibilityTimer < -39) { + player->invincibilityTimer = 0; + } else { + player->invincibilityTimer = 0; + globalCtx->damagePlayer(globalCtx, -8); + } + } + if (!(this->attackCollider.base.atFlags & AT_BOUNCED)) { + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + if (globalCtx->grabPlayer(globalCtx, player)) { + player->actor.parent = &this->actor; + } + } + hasHitPlayer = true; + } else { + this->attackCollider.base.atFlags &= ~AT_HIT; + } + } + + if ((player->stateFlags2 & 0x80) && player->actor.parent == &this->actor) { + player->actor.world.pos.x = this->actor.world.pos.x + Math_CosS(this->actor.shape.rot.y) * 10.0f + + Math_SinS(this->actor.shape.rot.y) * 89.0f; + hasHitPlayer = true; + player->actor.world.pos.z = this->actor.world.pos.z + Math_SinS(this->actor.shape.rot.y) * 10.0f + + Math_CosS(this->actor.shape.rot.y) * 89.0f; + player->unk_850 = 0; + player->actor.speedXZ = 0.0f; + player->actor.velocity.y = 0.0f; + } + + if (endCharge) { + if (hasHitPlayer || (player->stateFlags2 & 0x80)) { + this->attackCollider.base.atFlags &= ~AT_HIT; + if (player->stateFlags2 & 0x80) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.world.rot.y, 4.0f); + } + } + this->attack = ENMB_ATTACK_NONE; + this->actor.speedXZ = -10.0f; + EnMb_SetupSpearPatrolEndCharge(this); + } +} + +/** + * Charge and follow the path, until hitting the player or, after some time, reaching home. + */ +void EnMb_SpearPatrolImmediateCharge(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 prevFrame; + s32 hasHitPlayer = false; + s32 endCharge = !Actor_TestFloorInDirection(&this->actor, globalCtx, 110.0f, this->actor.world.rot.y); + + prevFrame = (s32)this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 5.0f, 3, 4.0f, 100, 15, false); + if (prevFrame != (s32)this->skelAnime.curFrame && + ((s32)this->skelAnime.curFrame == 2 || (s32)this->skelAnime.curFrame == 6)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DASH); + } + + if (this->attackCollider.base.atFlags & AT_HIT) { + if (this->attackCollider.base.at == &player->actor) { + if (!endCharge && !(player->stateFlags2 & 0x80)) { + if (player->invincibilityTimer < 0) { + if (player->invincibilityTimer <= -40) { + player->invincibilityTimer = 0; + } else { + player->invincibilityTimer = 0; + globalCtx->damagePlayer(globalCtx, -8); + } + } + if (!(this->attackCollider.base.atFlags & AT_BOUNCED)) { + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + if (globalCtx->grabPlayer(globalCtx, player)) { + player->actor.parent = &this->actor; + } + } + hasHitPlayer = true; + } else { + this->attackCollider.base.atFlags &= ~AT_HIT; + } + } + + if ((player->stateFlags2 & 0x80) && player->actor.parent == &this->actor) { + player->actor.world.pos.x = this->actor.world.pos.x + Math_CosS(this->actor.shape.rot.y) * 10.0f + + Math_SinS(this->actor.shape.rot.y) * 89.0f; + hasHitPlayer = true; + player->actor.world.pos.z = this->actor.world.pos.z + Math_SinS(this->actor.shape.rot.y) * 10.0f + + Math_CosS(this->actor.shape.rot.y) * 89.0f; + player->unk_850 = 0; + player->actor.speedXZ = 0.0f; + player->actor.velocity.y = 0.0f; + } + + if (endCharge) { + if (hasHitPlayer || (player->stateFlags2 & 0x80)) { + this->attackCollider.base.atFlags &= ~AT_HIT; + if (player->stateFlags2 & 0x80) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.world.rot.y, 4.0f); + } + this->attack = ENMB_ATTACK_NONE; + this->actor.speedXZ = -10.0f; + EnMb_SetupSpearPatrolEndCharge(this); + this->timer3 = 1; + } else { + this->timer3--; + EnMb_NextWaypoint(this, globalCtx); + } + } + + EnMb_FaceWaypoint(this, globalCtx); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->timer3 == 0 && Math_Vec3f_DistXZ(&this->actor.home.pos, &this->actor.world.pos) < 80.0f) { + this->attack = ENMB_ATTACK_NONE; + EnMb_SetupSpearEndChargeQuick(this); + } +} + +void EnMb_ClubDamaged(EnMb* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer3 != 0) { + Animation_PlayOnce(&this->skelAnime, &gEnMbClubStandUpAnim); + this->timer3 = 0; + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + Camera_AddQuake(&globalCtx->mainCamera, 2, 25, 5); + } else { + EnMb_SetupClubWaitPlayerNear(this); + } + } +} + +void EnMb_ClubDamagedWhileKneeling(EnMb* this, GlobalContext* globalCtx) { + s32 pad; + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer3 != 0) { + this->timer3--; + if (this->timer3 == 0) { + if (this->timer1 == 0) { + Animation_Change(&this->skelAnime, &gEnMbClubStandUpAnim, 3.0f, 0.0f, + Animation_GetLastFrame(&gEnMbClubStandUpAnim), ANIMMODE_ONCE_INTERP, 0.0f); + this->timer1 = 1; + this->timer3 = 6; + } else { + Animation_Change(&this->skelAnime, &gEnMbClubStandUpAnim, 3.0f, 0.0f, + Animation_GetLastFrame(&gEnMbClubStandUpAnim), ANIMMODE_ONCE_INTERP, 0.0f); + } + } + } else { + EnMb_SetupClubWaitPlayerNear(this); + } + } +} + +void EnMb_ClubDead(EnMb* this, GlobalContext* globalCtx) { + Vec3f effPos; + Vec3f effPosBase; + + effPos = this->actor.world.pos; + effPos.x += Math_SinS(this->actor.shape.rot.y) * -70.0f; + effPos.z += Math_CosS(this->actor.shape.rot.y) * -70.0f; + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + effPosBase = effPos; + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer1 > 0) { + Vec3f effZeroVec = { 0.0f, 0.0f, 0.0f }; + s32 i; + + this->timer1--; + for (i = 4; i >= 0; i--) { + effPos.x = Rand_CenteredFloat(240.0f) + effPosBase.x; + effPos.y = Rand_CenteredFloat(15.0f) + (effPosBase.y + 20.0f); + effPos.z = Rand_CenteredFloat(240.0f) + effPosBase.z; + EffectSsDeadDb_Spawn(globalCtx, &effPos, &effZeroVec, &effZeroVec, 230, 7, 255, 255, 255, 255, 0, 255, + 0, 1, 9, true); + } + } else { + Item_DropCollectibleRandom(globalCtx, &this->actor, &effPos, 0xC0); + Actor_Kill(&this->actor); + } + } else if ((s32)this->skelAnime.curFrame == 15 || (s32)this->skelAnime.curFrame == 22) { + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &effPos, 50.0f, 10, 3.0f, 400, 60, false); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + Camera_AddQuake(&globalCtx->mainCamera, 2, 25, 5); + } +} + +/** + * Walk around the home point, face and charge the player if close. + */ +void EnMb_SpearGuardWalk(EnMb* this, GlobalContext* globalCtx) { + s32 prevFrame; + s32 beforeCurFrame; + s32 pad1; + s32 pad2; + Player* player = GET_PLAYER(globalCtx); + s16 relYawTowardsPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + s16 yawTowardsHome; + f32 playSpeedAbs; + + relYawTowardsPlayer = ABS(relYawTowardsPlayer); + Math_SmoothStepToF(&this->actor.speedXZ, 0.59999996f, 0.1f, 1.0f, 0.0f); + this->skelAnime.playSpeed = this->actor.speedXZ; + prevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + + playSpeedAbs = ABS(this->skelAnime.playSpeed); + beforeCurFrame = this->skelAnime.curFrame - playSpeedAbs; + playSpeedAbs = ABS(this->skelAnime.playSpeed); + if (this->timer3 == 0 && + Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < this->playerDetectionRange) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 0x2EE, 0); + this->actor.flags |= ACTOR_FLAG_0; + if (this->actor.xzDistToPlayer < 500.0f && relYawTowardsPlayer < 0x1388) { + EnMb_SetupSpearPrepareAndCharge(this); + } + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + if (Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) > this->maxHomeDist || this->timer2 != 0) { + yawTowardsHome = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_SmoothStepToS(&this->actor.world.rot.y, yawTowardsHome, 1, 0x2EE, 0); + } + if (this->timer2 != 0) { + this->timer2--; + } + if (this->timer3 != 0) { + this->timer3--; + } + if (this->timer2 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_VOICE); + } + this->timer1--; + if (this->timer1 == 0) { + if (Rand_ZeroOne() > 0.7f) { + this->timer1 = Rand_S16Offset(50, 70); + this->timer2 = Rand_S16Offset(15, 40); + } else { + EnMb_SetupSpearGuardLookAround(this); + } + } + } + + if (prevFrame != (s32)this->skelAnime.curFrame) { + if ((beforeCurFrame <= 1 && prevFrame + (s32)playSpeedAbs >= 1) || + (beforeCurFrame <= 20 && prevFrame + (s32)playSpeedAbs >= 20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + } + + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnMb_SpearPatrolWalkTowardsWaypoint(EnMb* this, GlobalContext* globalCtx) { + s32 prevFrame; + s32 beforeCurFrame; + s16 relYawTowardsPlayer; + f32 yDistToPlayerAbs; + f32 playSpeedABS; + + if (Math_Vec3f_DistXZ(&this->waypointPos, &this->actor.world.pos) <= 8.0f || + (Rand_ZeroOne() < 0.1f && Math_Vec3f_DistXZ(&this->actor.home.pos, &this->actor.world.pos) <= 4.0f)) { + EnMb_SetupSpearPatrolTurnTowardsWaypoint(this, globalCtx); + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 0.59999996f, 0.1f, 1.0f, 0.0f); + this->skelAnime.playSpeed = 2.0f * this->actor.speedXZ; + } + + this->yawToWaypoint = Math_Vec3f_Yaw(&this->actor.world.pos, &this->waypointPos); + Math_SmoothStepToS(&this->actor.world.rot.y, this->yawToWaypoint, 1, 0x5DC, 0); + + yDistToPlayerAbs = (this->actor.yDistToPlayer >= 0.0f) ? this->actor.yDistToPlayer : -this->actor.yDistToPlayer; + if (yDistToPlayerAbs <= 20.0f && EnMb_IsPlayerInCorridor(this, globalCtx)) { + relYawTowardsPlayer = (this->actor.shape.rot.y - this->actor.yawTowardsPlayer); + if (ABS(relYawTowardsPlayer) <= 0x4000 || (func_8002DDE4(globalCtx) && this->actor.xzDistToPlayer < 160.0f)) { + EnMb_FindWaypointTowardsPlayer(this, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_VOICE); + EnMb_SetupSpearPrepareAndCharge(this); + return; + } + } + + if (this->timer2 != 0) { + this->timer2--; + } + if (this->timer3 != 0) { + this->timer3--; + } + if (this->timer2 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_VOICE); + this->timer2 = Rand_S16Offset(30, 70); + } + + prevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + playSpeedABS = ABS(this->skelAnime.playSpeed); + beforeCurFrame = this->skelAnime.curFrame - playSpeedABS; + playSpeedABS = (this->skelAnime.playSpeed >= 0.0f) ? this->skelAnime.playSpeed : -this->skelAnime.playSpeed; + if (prevFrame != (s32)this->skelAnime.curFrame) { + if ((beforeCurFrame <= 1 && (s32)playSpeedABS + prevFrame >= 1) || + (beforeCurFrame <= 20 && (s32)playSpeedABS + prevFrame >= 20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + } + + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnMb_ClubWaitPlayerNear(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 relYawFromPlayer = this->actor.world.rot.y - this->actor.yawTowardsPlayer; + + SkelAnime_Update(&this->skelAnime); + if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < this->playerDetectionRange && + !(player->stateFlags1 & 0x4000000) && ABS(relYawFromPlayer) < 0x3E80) { + EnMb_SetupClubAttack(this); + } +} + +void EnMb_SetupSpearDamaged(EnMb* this) { + s16 relYawTowardsPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(relYawTowardsPlayer) <= 0x4000) { + Animation_MorphToPlayOnce(&this->skelAnime, &gEnMbSpearDamagedFromFrontAnim, -4.0f); + this->actor.speedXZ = -8.0f; + } else { + Animation_MorphToPlayOnce(&this->skelAnime, &gEnMbSpearDamagedFromBehindAnim, -4.0f); + this->actor.speedXZ = 8.0f; + } + + this->timer1 = 30; + this->state = ENMB_STATE_SPEAR_SPEARPATH_DAMAGED; + this->actor.shape.rot.y = this->actor.world.rot.y; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DEAD); + EnMb_SetupAction(this, EnMb_SpearDamaged); +} + +void EnMb_SpearDamaged(EnMb* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.params <= ENMB_TYPE_SPEAR_GUARD) { + EnMb_SetupSpearGuardLookAround(this); + } else { + EnMb_SetupSpearPatrolImmediateCharge(this); + } + } +} + +void EnMb_SetupSpearDead(EnMb* this) { + s16 relYawTowardsPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(relYawTowardsPlayer) <= 0x4000) { + Animation_MorphToPlayOnce(&this->skelAnime, &gEnMbSpearFallOnItsBackAnim, -4.0f); + this->actor.speedXZ = -8.0f; + } else { + /* The gEnMbSpearFallFaceDownAnim animation was probably meant to be used here */ + Animation_MorphToPlayOnce(&this->skelAnime, &gEnMbSpearFallOnItsBackAnim, -4.0f); + this->actor.speedXZ = 8.0f; + } + + this->actor.world.rot.y = this->actor.shape.rot.y; + this->timer1 = 30; + this->state = ENMB_STATE_SPEAR_SPEARPATH_DAMAGED; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_DEAD); + this->actor.flags &= ~ACTOR_FLAG_0; + EnMb_SetupAction(this, EnMb_SpearDead); +} + +void EnMb_SpearDead(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + + if ((player->stateFlags2 & 0x80) && player->actor.parent == &this->actor) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.world.rot.y, 4.0f); + this->attack = ENMB_ATTACK_NONE; + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer1 > 0) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 i; + Vec3f effPos; + + this->actor.shape.shadowScale = 0.0f; + this->timer1--; + for (i = 4; i >= 0; i--) { + effPos.x = Rand_CenteredFloat(110.0f) + this->actor.world.pos.x; + effPos.y = Rand_CenteredFloat(15.0f) + (this->actor.world.pos.y + 20.0f); + effPos.z = Rand_CenteredFloat(110.0f) + this->actor.world.pos.z; + + EffectSsDeadDb_Spawn(globalCtx, &effPos, &zeroVec, &zeroVec, 100, 7, 255, 255, 255, 255, 0, 255, 0, 1, + 9, true); + } + } else { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xE0); + Actor_Kill(&this->actor); + } + } +} + +void EnMb_SpearUpdateAttackCollider(Actor* thisx, GlobalContext* globalCtx) { + Vec3f quadModel0 = { 1000.0f, 1500.0f, 0.0f }; + Vec3f quadModel1 = { -1000.0f, 1500.0f, 0.0f }; + Vec3f quadModel2 = { 1000.0f, 1500.0f, 4500.0f }; + Vec3f quadModel3 = { -1000.0f, 1500.0f, 4500.0f }; + EnMb* this = (EnMb*)thisx; + + if (this->actor.params >= ENMB_TYPE_SPEAR_PATROL) { + quadModel0.x += 2000.0f; + quadModel0.z = -4000.0f; + quadModel1.z = -4000.0f; + quadModel2.x += 2000.0f; + quadModel1.x -= 2000.0f; + quadModel3.x -= 2000.0f; + quadModel2.z += 4000.0f; + quadModel3.z += 4000.0f; + } + Matrix_MultVec3f(&quadModel0, &this->attackCollider.dim.quad[1]); + Matrix_MultVec3f(&quadModel1, &this->attackCollider.dim.quad[0]); + Matrix_MultVec3f(&quadModel2, &this->attackCollider.dim.quad[3]); + Matrix_MultVec3f(&quadModel3, &this->attackCollider.dim.quad[2]); + Collider_SetQuadVertices(&this->attackCollider, &this->attackCollider.dim.quad[0], + &this->attackCollider.dim.quad[1], &this->attackCollider.dim.quad[2], + &this->attackCollider.dim.quad[3]); +} + +void EnMb_ClubUpdateAttackCollider(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f quadModel[] = { { 1000.0f, 0.0f, 0.0f }, + { 1000.0f, 0.0f, 0.0f }, + { 1000.0f, -8000.0f, -1500.0f }, + { 1000.0f, -9000.0f, 2000.0f } }; + EnMb* this = (EnMb*)thisx; + + Matrix_MultVec3f(&quadModel[0], &this->attackCollider.dim.quad[1]); + Matrix_MultVec3f(&quadModel[1], &this->attackCollider.dim.quad[0]); + Matrix_MultVec3f(&quadModel[2], &this->attackCollider.dim.quad[3]); + Matrix_MultVec3f(&quadModel[3], &this->attackCollider.dim.quad[2]); + Collider_SetQuadVertices(&this->attackCollider, &this->attackCollider.dim.quad[0], + &this->attackCollider.dim.quad[1], &this->attackCollider.dim.quad[2], + &this->attackCollider.dim.quad[3]); +} + +void EnMb_CheckColliding(EnMb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->frontShielding.base.acFlags & AC_HIT) { + this->frontShielding.base.acFlags &= ~(AC_HIT | AC_BOUNCED); + this->hitbox.base.acFlags &= ~AC_HIT; + } else if ((this->hitbox.base.acFlags & AC_HIT) && this->state >= ENMB_STATE_STUNNED) { + this->hitbox.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != ENMB_DMGEFF_IGNORE && + this->actor.colChkInfo.damageEffect != ENMB_DMGEFF_FREEZE) { + if ((player->stateFlags2 & 0x80) && player->actor.parent == &this->actor) { + player->stateFlags2 &= ~0x80; + player->actor.parent = NULL; + player->unk_850 = 200; + func_8002F71C(globalCtx, &this->actor, 6.0f, this->actor.world.rot.y, 6.0f); + } + this->damageEffect = this->actor.colChkInfo.damageEffect; + this->attack = ENMB_ATTACK_NONE; + Actor_SetDropFlag(&this->actor, &this->hitbox.info, false); + if (this->actor.colChkInfo.damageEffect == ENMB_DMGEFF_STUN || + this->actor.colChkInfo.damageEffect == ENMB_DMGEFF_STUN_ICE) { + if (this->state != ENMB_STATE_STUNNED) { + Actor_ApplyDamage(&this->actor); + EnMb_SetupStunned(this); + } + } else { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFA, 0, 0xC); + if (this->actor.params == ENMB_TYPE_CLUB) { + if (this->actor.colChkInfo.health == 0) { + EnMb_SetupClubDead(this); + } else if (this->state != ENMB_STATE_CLUB_KNEELING) { + EnMb_SetupClubDamaged(this); + } + } else { + if (this->actor.colChkInfo.health == 0) { + EnMb_SetupSpearDead(this); + } else { + EnMb_SetupSpearDamaged(this); + } + } + } + } + } +} + +void EnMb_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMb* this = (EnMb*)thisx; + s32 pad; + + EnMb_CheckColliding(this, globalCtx); + if (thisx->colChkInfo.damageEffect != ENMB_DMGEFF_FREEZE) { + this->actionFunc(this, globalCtx); + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 40.0f, 40.0f, 70.0f, 0x1D); + Actor_SetFocus(thisx, thisx->scale.x * 4500.0f); + Collider_UpdateCylinder(thisx, &this->hitbox); + if (thisx->colChkInfo.health <= 0) { + this->hitbox.dim.pos.x += Math_SinS(thisx->shape.rot.y) * (-4400.0f * thisx->scale.y); + this->hitbox.dim.pos.z += Math_CosS(thisx->shape.rot.y) * (-4400.0f * thisx->scale.y); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->hitbox.base); + if (this->state >= ENMB_STATE_STUNNED && + (thisx->params == ENMB_TYPE_CLUB || this->state != ENMB_STATE_ATTACK)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->hitbox.base); + } + if (this->state >= ENMB_STATE_IDLE) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->frontShielding.base); + } + if (this->attack > ENMB_ATTACK_NONE) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->attackCollider.base); + } + } +} + +void EnMb_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f unused = { 1100.0f, -700.0f, 0.0f }; + static Vec3f feetPos = { 0.0f, 0.0f, 0.0f }; + static Vec3f effSpawnPosModel = { 0.0f, -8000.0f, 0.0f }; + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 bodyPart = -1; + EnMb* this = (EnMb*)thisx; + Vec3f bodyPartPos; + + if (this->actor.params == ENMB_TYPE_CLUB) { + if (limbIndex == ENMB_LIMB_LHAND) { + Matrix_MultVec3f(&effSpawnPosModel, &this->effSpawnPos); + if (this->attack > ENMB_ATTACK_NONE) { + EnMb_ClubUpdateAttackCollider(&this->actor, globalCtx); + } + } + Actor_SetFeetPos(&this->actor, limbIndex, ENMB_LIMB_LFOOT, &feetPos, ENMB_LIMB_RFOOT, &feetPos); + } + + if (this->iceEffectTimer != 0) { + switch (limbIndex) { + case ENMB_LIMB_HEAD: + bodyPart = 0; + break; + case ENMB_LIMB_LHAND: + bodyPart = 1; + break; + case ENMB_LIMB_RHAND: + bodyPart = 2; + break; + case ENMB_LIMB_LSHOULDER: + bodyPart = 3; + break; + case ENMB_LIMB_RSHOULDER: + bodyPart = 4; + break; + case ENMB_LIMB_CHEST: + bodyPart = 5; + break; + case ENMB_LIMB_LTHIGH: + bodyPart = 6; + break; + case ENMB_LIMB_RTHIGH: + bodyPart = 7; + break; + case ENMB_LIMB_LFOOT: + bodyPart = 8; + break; + case ENMB_LIMB_RFOOT: + bodyPart = 9; + break; + } + if (bodyPart >= 0) { + Matrix_MultVec3f(&zeroVec, &bodyPartPos); + this->bodyPartsPos[bodyPart].x = bodyPartPos.x; + this->bodyPartsPos[bodyPart].y = bodyPartPos.y; + this->bodyPartsPos[bodyPart].z = bodyPartPos.z; + } + } +} + +void EnMb_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f frontShieldingTriModel0[] = { + { 4000.0f, 7000.0f, 3500.0f }, + { 4000.0f, 0.0f, 3500.0f }, + { -4000.0f, 7000.0f, 3500.0f }, + }; + static Vec3f frontShieldingTriModel1[] = { + { -4000.0f, 7000.0f, 3500.0f }, + { -4000.0f, 0.0f, 3500.0f }, + { 4000.0f, 0.0f, 3500.0f }, + }; + s32 i; + f32 scale; + Vec3f frontShieldingTri0[3]; + Vec3f frontShieldingTri1[3]; + s32 bodyPartIdx; + EnMb* this = (EnMb*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, EnMb_PostLimbDraw, thisx); + + if (thisx->params != ENMB_TYPE_CLUB) { + if (this->attack > ENMB_ATTACK_NONE) { + EnMb_SpearUpdateAttackCollider(thisx, globalCtx); + } + for (i = 0; i < 3; i++) { + Matrix_MultVec3f(&frontShieldingTriModel0[i], &frontShieldingTri0[i]); + Matrix_MultVec3f(&frontShieldingTriModel1[i], &frontShieldingTri1[i]); + } + Collider_SetTrisVertices(&this->frontShielding, 0, &frontShieldingTri0[0], &frontShieldingTri0[1], + &frontShieldingTri0[2]); + Collider_SetTrisVertices(&this->frontShielding, 1, &frontShieldingTri1[0], &frontShieldingTri1[1], + &frontShieldingTri1[2]); + } + + if (this->iceEffectTimer != 0) { + thisx->colorFilterTimer++; + if (this->iceEffectTimer >= 0) { + this->iceEffectTimer--; + } + if ((this->iceEffectTimer % 4) == 0) { + scale = 2.5f; + if (thisx->params == ENMB_TYPE_CLUB) { + scale = 4.0f; + } + bodyPartIdx = this->iceEffectTimer >> 2; + EffectSsEnIce_SpawnFlyingVec3s(globalCtx, thisx, &this->bodyPartsPos[bodyPartIdx], 150, 150, 150, 250, 235, + 245, 255, scale); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.h b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.h new file mode 100644 index 000000000..5ddb628fc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.h @@ -0,0 +1,53 @@ +#ifndef Z_EN_MB_H +#define Z_EN_MB_H + +#include "ultra64.h" +#include "global.h" + +struct EnMb; + +typedef void (*EnMbActionFunc)(struct EnMb*, GlobalContext*); + +typedef enum { + /* 0 */ ENMB_STATE_SPEAR_SPEARPATH_DAMAGED, + /* 1 */ ENMB_STATE_CLUB_DEAD, + /* 2 */ ENMB_STATE_CLUB_KNEELING_DAMAGED, + /* 3 */ ENMB_STATE_CLUB_KNEELING, + /* 5 */ ENMB_STATE_STUNNED = 5, + /* 6 */ ENMB_STATE_IDLE, + /* 9 */ ENMB_STATE_WALK = 9, + /* 10 */ ENMB_STATE_ATTACK, + /* 11 */ ENMB_STATE_ATTACK_END +} EnMbState; + +typedef struct EnMb { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3s bodyPartsPos[10]; + /* 0x0188 */ u8 damageEffect; + /* 0x018C */ SkelAnime skelAnime; + /* 0x01D0 */ Vec3s jointTable[28]; + /* 0x0278 */ Vec3s morphTable[28]; + /* 0x0320 */ EnMbState state; + /* 0x0324 */ EnMbActionFunc actionFunc; + /* 0x0328 */ s16 iceEffectTimer; + /* 0x032A */ s16 timer1; + /* 0x032C */ s16 timer2; + /* 0x032E */ s16 timer3; + /* 0x0330 */ s16 yawToWaypoint; + /* 0x0332 */ s16 unk_332; + /* 0x0334 */ s16 attack; + /* 0x0338 */ Vec3f effSpawnPos; + /* 0x0344 */ Vec3f waypointPos; + /* 0x0350 */ char unk_34A[0xC]; + /* 0x035C */ s8 waypoint; + /* 0x035D */ s8 path; + /* 0x035E */ s8 direction; + /* 0x0360 */ f32 maxHomeDist; + /* 0x0364 */ f32 playerDetectionRange; + /* 0x0368 */ ColliderCylinder hitbox; + /* 0x03B4 */ ColliderQuad attackCollider; // for attacking the player + /* 0x0434 */ ColliderTris frontShielding; // Moblins don't have shields, but this acts as one + /* 0x0454 */ ColliderTrisElement frontShieldingTris[2]; +} EnMb; // size = 0x050C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Md/z_en_md.c b/soh/src/overlays/actors/ovl_En_Md/z_en_md.c new file mode 100644 index 000000000..38fdd8bfa --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Md/z_en_md.c @@ -0,0 +1,865 @@ +/* + * File: z_en_md.c + * Overlay: ovl_En_Md + * Description: Mido + */ + +#include "z_en_md.h" +#include "objects/object_md/object_md.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnMd_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMd_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMd_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AAB874(EnMd* this, GlobalContext* globalCtx); +void func_80AAB8F8(EnMd* this, GlobalContext* globalCtx); +void func_80AAB948(EnMd* this, GlobalContext* globalCtx); +void func_80AABC10(EnMd* this, GlobalContext* globalCtx); +void func_80AABD0C(EnMd* this, GlobalContext* globalCtx); + +const ActorInit En_Md_InitVars = { + ACTOR_EN_MD, + ACTORCAT_NPC, + FLAGS, + OBJECT_MD, + sizeof(EnMd), + (ActorFunc)EnMd_Init, + (ActorFunc)EnMd_Destroy, + (ActorFunc)EnMd_Update, + (ActorFunc)EnMd_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 36, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENMD_ANIM_0, + /* 1 */ ENMD_ANIM_1, + /* 2 */ ENMD_ANIM_2, + /* 3 */ ENMD_ANIM_3, + /* 4 */ ENMD_ANIM_4, + /* 5 */ ENMD_ANIM_5, + /* 6 */ ENMD_ANIM_6, + /* 7 */ ENMD_ANIM_7, + /* 8 */ ENMD_ANIM_8, + /* 9 */ ENMD_ANIM_9, + /* 10 */ ENMD_ANIM_10, + /* 11 */ ENMD_ANIM_11, + /* 12 */ ENMD_ANIM_12, + /* 13 */ ENMD_ANIM_13, +} EnMdAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gMidoHandsOnHipsIdleAnim, 0.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gMidoHandsOnHipsIdleAnim, 0.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -10.0f }, + { &gMidoRaiseHand1Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gMidoHaltAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gMidoPutHandDownAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gMidoAnnoyedPointedHeadIdle1Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gMidoAnnoyedPointedHeadIdle2Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gMidoAnim_92B0, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gMidoWalkingAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gMidoHandsOnHipsTransitionAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gMidoHandsOnHipsIdleAnim, 0.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gMidoSlamAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gMidoRaiseHand2Anim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gMidoAngryHeadTurnAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, +}; + +void func_80AAA250(EnMd* this) { + f32 startFrame; + + startFrame = this->skelAnime.startFrame; + this->skelAnime.startFrame = this->skelAnime.endFrame; + this->skelAnime.curFrame = this->skelAnime.endFrame; + this->skelAnime.endFrame = startFrame; + this->skelAnime.playSpeed = -1.0f; +} + +void func_80AAA274(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_2); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_3); + this->unk_20A++; + } + } +} + +void func_80AAA308(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_4); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_5); + this->unk_20A++; + } + } +} + +void func_80AAA39C(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_2); + func_80AAA250(this); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_7); + this->unk_20A++; + } else { + break; + } + case 2: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_8); + this->unk_20A++; + } + } +} + +void func_80AAA474(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_7); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_10); + this->unk_20A++; + } + } +} + +void func_80AAA508(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_2); + func_80AAA250(this); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_10); + this->unk_20A++; + } + } +} + +void func_80AAA5A4(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_9); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_6); + this->unk_20A++; + } + } +} + +void func_80AAA638(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_9); + func_80AAA250(this); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_10); + this->unk_20A++; + } + } +} + +void func_80AAA6D4(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_11); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_6); + this->unk_20A++; + } + } +} + +void func_80AAA768(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_12); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_3); + this->unk_20A++; + } + } +} + +void func_80AAA7FC(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_13); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_6); + this->unk_20A++; + } + } +} + +void func_80AAA890(EnMd* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_7); + func_80AAA250(this); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_10); + this->unk_20A++; + } + } +} + +void func_80AAA92C(EnMd* this, u8 arg1) { + this->unk_20B = arg1; + this->unk_20A = 0; +} + +void func_80AAA93C(EnMd* this) { + switch (this->unk_20B) { + case 1: + func_80AAA274(this); + break; + case 2: + func_80AAA308(this); + break; + case 3: + func_80AAA39C(this); + break; + case 4: + func_80AAA474(this); + break; + case 5: + func_80AAA508(this); + break; + case 6: + func_80AAA5A4(this); + break; + case 7: + func_80AAA638(this); + break; + case 8: + func_80AAA6D4(this); + break; + case 9: + func_80AAA768(this); + break; + case 10: + func_80AAA7FC(this); + break; + case 11: + func_80AAA890(this); + } +} + +void func_80AAAA24(EnMd* this) { + if (this->unk_1E0.unk_00 != 0) { + switch (this->actor.textId) { + case 0x102F: + if ((this->unk_208 == 0) && (this->unk_20B != 1)) { + func_80AAA92C(this, 1); + } + if ((this->unk_208 == 2) && (this->unk_20B != 2)) { + func_80AAA92C(this, 2); + } + if ((this->unk_208 == 5) && (this->unk_20B != 8)) { + func_80AAA92C(this, 8); + } + if ((this->unk_208 == 11) && (this->unk_20B != 9)) { + func_80AAA92C(this, 9); + } + break; + case 0x1033: + if ((this->unk_208 == 0) && (this->unk_20B != 1)) { + func_80AAA92C(this, 1); + } + if ((this->unk_208 == 1) && (this->unk_20B != 2)) { + func_80AAA92C(this, 2); + } + if ((this->unk_208 == 5) && (this->unk_20B != 10)) { + func_80AAA92C(this, 10); + } + if ((this->unk_208 == 7) && (this->unk_20B != 9)) { + func_80AAA92C(this, 9); + } + break; + case 0x1030: + case 0x1034: + case 0x1045: + if ((this->unk_208 == 0) && (this->unk_20B != 1)) { + func_80AAA92C(this, 1); + } + break; + case 0x1046: + if ((this->unk_208 == 0) && (this->unk_20B != 6)) { + func_80AAA92C(this, 6); + } + break; + } + } else if (this->skelAnime.animation != &gMidoHandsOnHipsIdleAnim) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_10); + func_80AAA92C(this, 0); + } + + func_80AAA93C(this); +} + +s16 func_80AAAC78(EnMd* this, GlobalContext* globalCtx) { + s16 dialogState = Message_GetState(&globalCtx->msgCtx); + + if ((this->unk_209 == TEXT_STATE_AWAITING_NEXT) || (this->unk_209 == TEXT_STATE_EVENT) || + (this->unk_209 == TEXT_STATE_CLOSING) || (this->unk_209 == TEXT_STATE_DONE_HAS_NEXT)) { + if (this->unk_209 != dialogState) { + this->unk_208++; + } + } + + this->unk_209 = dialogState; + return dialogState; +} + +u16 EnMd_GetTextKokiriForest(GlobalContext* globalCtx, EnMd* this) { + u16 reactionText = Text_GetFaceReaction(globalCtx, 0x11); + + if (reactionText != 0) { + return reactionText; + } + + this->unk_208 = 0; + this->unk_209 = TEXT_STATE_NONE; + + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + return 0x1045; + } + + if (gSaveContext.eventChkInf[0] & 0x10) { + return 0x1034; + } + + if ((CUR_EQUIP_VALUE(EQUIP_SHIELD) == 1) && (CUR_EQUIP_VALUE(EQUIP_SWORD) == 1)) { + return 0x1033; + } + + if (gSaveContext.infTable[0] & 0x1000) { + return 0x1030; + } + + return 0x102F; +} + +u16 EnMd_GetTextKokiriHome(GlobalContext* globalCtx, EnMd* this) { + this->unk_208 = 0; + this->unk_209 = TEXT_STATE_NONE; + + if (gSaveContext.eventChkInf[4] & 1) { + return 0x1028; + } + + return 0x1046; +} + +u16 EnMd_GetTextLostWoods(GlobalContext* globalCtx, EnMd* this) { + this->unk_208 = 0; + this->unk_209 = TEXT_STATE_NONE; + + if (gSaveContext.eventChkInf[4] & 0x100) { + if (gSaveContext.infTable[1] & 0x200) { + return 0x1071; + } + return 0x1070; + } + + if (gSaveContext.eventChkInf[0] & 0x400) { + return 0x1068; + } + + if (gSaveContext.infTable[1] & 0x20) { + return 0x1061; + } + + return 0x1060; +} + +u16 EnMd_GetText(GlobalContext* globalCtx, Actor* thisx) { + EnMd* this = (EnMd*)thisx; + + switch (globalCtx->sceneNum) { + case SCENE_SPOT04: + return EnMd_GetTextKokiriForest(globalCtx, this); + case SCENE_KOKIRI_HOME4: + return EnMd_GetTextKokiriHome(globalCtx, this); + case SCENE_SPOT10: + return EnMd_GetTextLostWoods(globalCtx, this); + default: + return 0; + } +} + +s16 func_80AAAF04(GlobalContext* globalCtx, Actor* thisx) { + EnMd* this = (EnMd*)thisx; + switch (func_80AAAC78(this, globalCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + return 1; + case TEXT_STATE_CLOSING: + switch (this->actor.textId) { + case 0x1028: + gSaveContext.eventChkInf[0] |= 0x8000; + break; + case 0x102F: + gSaveContext.eventChkInf[0] |= 4; + gSaveContext.infTable[0] |= 0x1000; + break; + case 0x1060: + gSaveContext.infTable[1] |= 0x20; + break; + case 0x1070: + gSaveContext.infTable[1] |= 0x200; + break; + case 0x1033: + case 0x1067: + return 2; + } + return 0; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + return 2; + } + default: + return 1; + } +} + +u8 EnMd_ShouldSpawn(EnMd* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT04) { + if (!(gSaveContext.eventChkInf[1] & 0x1000) && !(gSaveContext.eventChkInf[4] & 1)) { + return 1; + } + } + + if (globalCtx->sceneNum == SCENE_KOKIRI_HOME4) { + if (((gSaveContext.eventChkInf[1] & 0x1000) != 0) || ((gSaveContext.eventChkInf[4] & 1) != 0)) { + if (!LINK_IS_ADULT) { + return 1; + } + } + } + + if (globalCtx->sceneNum == SCENE_SPOT10) { + return 1; + } + + return 0; +} + +void EnMd_UpdateEyes(EnMd* this) { + if (DECR(this->blinkTimer) == 0) { + this->eyeIdx++; + if (this->eyeIdx > 2) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeIdx = 0; + } + } +} + +void func_80AAB158(EnMd* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 absYawDiff; + s16 temp; + s16 temp2; + s16 yawDiff; + + if (this->actor.xzDistToPlayer < 170.0f) { + yawDiff = (f32)this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + absYawDiff = ABS(yawDiff); + + temp = (absYawDiff <= func_800347E8(2)) ? 2 : 1; + temp2 = 1; + } else { + temp = 1; + temp2 = 0; + } + + if (this->unk_1E0.unk_00 != 0) { + temp = 4; + } + + if (this->actionFunc == func_80AABD0C) { + temp = 1; + temp2 = 0; + } + if (this->actionFunc == func_80AAB8F8) { + temp = 4; + temp2 = 1; + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) || gDbgCamEnabled) { + this->unk_1E0.unk_18 = globalCtx->view.eye; + this->unk_1E0.unk_14 = 40.0f; + temp = 2; + } else { + this->unk_1E0.unk_18 = player->actor.world.pos; + this->unk_1E0.unk_14 = (gSaveContext.linkAge > 0) ? 0.0f : -18.0f; + } + + func_80034A14(&this->actor, &this->unk_1E0, 2, temp); + if (this->actionFunc != func_80AABC10) { + if (temp2) { + func_800343CC(globalCtx, &this->actor, &this->unk_1E0.unk_00, this->collider.dim.radius + 30.0f, + EnMd_GetText, func_80AAAF04); + } + } +} + +u8 EnMd_FollowPath(EnMd* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* pointPos; + f32 pathDiffX; + f32 pathDiffZ; + + if ((this->actor.params & 0xFF00) == 0xFF00) { + return 0; + } + + path = &globalCtx->setupPathList[(this->actor.params & 0xFF00) >> 8]; + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos += this->waypoint; + + pathDiffX = pointPos->x - this->actor.world.pos.x; + pathDiffZ = pointPos->z - this->actor.world.pos.z; + Math_SmoothStepToS(&this->actor.world.rot.y, Math_FAtan2F(pathDiffX, pathDiffZ) * (65536.0f / (2 * M_PI)), 4, 4000, + 1); + + if ((SQ(pathDiffX) + SQ(pathDiffZ)) < 100.0f) { + this->waypoint++; + if (this->waypoint >= path->count) { + this->waypoint = 0; + } + return 1; + } + return 0; +} + +u8 EnMd_SetMovedPos(EnMd* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* lastPointPos; + + if ((this->actor.params & 0xFF00) == 0xFF00) { + return 0; + } + + path = &globalCtx->setupPathList[(this->actor.params & 0xFF00) >> 8]; + lastPointPos = SEGMENTED_TO_VIRTUAL(path->points); + lastPointPos += path->count - 1; + + this->actor.world.pos.x = lastPointPos->x; + this->actor.world.pos.y = lastPointPos->y; + this->actor.world.pos.z = lastPointPos->z; + + return 1; +} + +void func_80AAB5A4(EnMd* this, GlobalContext* globalCtx) { + f32 temp; + + if (globalCtx->sceneNum != SCENE_KOKIRI_HOME4) { + temp = (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !(gSaveContext.eventChkInf[1] & 0x1000) && + (globalCtx->sceneNum == SCENE_SPOT04)) + ? 100.0f + : 400.0f; + this->alpha = func_80034DD4(&this->actor, globalCtx, this->alpha, temp); + this->actor.shape.shadowAlpha = this->alpha; + } else { + this->alpha = 255; + this->actor.shape.shadowAlpha = this->alpha; + } +} + +void EnMd_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMd* this = (EnMd*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 24.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gMidoSkel, NULL, this->jointTable, this->morphTable, 17); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + if (!EnMd_ShouldSpawn(this, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENMD_ANIM_0); + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->alpha = 255; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, FAIRY_KOKIRI); + + if (((globalCtx->sceneNum == SCENE_SPOT04) && !(gSaveContext.eventChkInf[0] & 0x10)) || + ((globalCtx->sceneNum == SCENE_SPOT04) && (gSaveContext.eventChkInf[0] & 0x10) && + CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) || + ((globalCtx->sceneNum == SCENE_SPOT10) && !(gSaveContext.eventChkInf[0] & 0x400))) { + this->actor.home.pos = this->actor.world.pos; + this->actionFunc = func_80AAB948; + return; + } + + if (globalCtx->sceneNum != SCENE_KOKIRI_HOME4) { + EnMd_SetMovedPos(this, globalCtx); + } + + this->actionFunc = func_80AAB874; +} + +void EnMd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMd* this = (EnMd*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AAB874(EnMd* this, GlobalContext* globalCtx) { + if (this->skelAnime.animation == &gMidoHandsOnHipsIdleAnim) { + func_80034F54(globalCtx, this->unk_214, this->unk_236, 17); + } else if ((this->unk_1E0.unk_00 == 0) && (this->unk_20B != 7)) { + func_80AAA92C(this, 7); + } + + func_80AAAA24(this); +} + +void func_80AAB8F8(EnMd* this, GlobalContext* globalCtx) { + if (this->skelAnime.animation == &gMidoHandsOnHipsIdleAnim) { + func_80034F54(globalCtx, this->unk_214, this->unk_236, 17); + } + func_80AAA93C(this); +} + +void func_80AAB948(EnMd* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 temp; + Actor* actorToBlock = &GET_PLAYER(globalCtx)->actor; + s16 yaw; + + func_80AAAA24(this); + + if (this->unk_1E0.unk_00 == 0) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + + yaw = Math_Vec3f_Yaw(&this->actor.home.pos, &actorToBlock->world.pos); + + this->actor.world.pos.x = this->actor.home.pos.x; + this->actor.world.pos.x += 60.0f * Math_SinS(yaw); + + this->actor.world.pos.z = this->actor.home.pos.z; + this->actor.world.pos.z += 60.0f * Math_CosS(yaw); + + temp = fabsf((f32)this->actor.yawTowardsPlayer - yaw) * 0.001f * 3.0f; + this->skelAnime.playSpeed = CLAMP(temp, 1.0f, 3.0f); + } + + if (this->unk_1E0.unk_00 == 2) { + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !(gSaveContext.eventChkInf[1] & 0x1000) && + (globalCtx->sceneNum == SCENE_SPOT04)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + } + + if (globalCtx->sceneNum == SCENE_SPOT04) { + gSaveContext.eventChkInf[0] |= 0x10; + } + if (globalCtx->sceneNum == SCENE_SPOT10) { + gSaveContext.eventChkInf[0] |= 0x400; + } + + func_80AAA92C(this, 3); + func_80AAA93C(this); + this->waypoint = 1; + this->unk_1E0.unk_00 = 0; + this->actionFunc = func_80AABD0C; + this->actor.speedXZ = 1.5f; + return; + } + + if (this->skelAnime.animation == &gMidoHandsOnHipsIdleAnim) { + func_80034F54(globalCtx, this->unk_214, this->unk_236, 17); + } + + if ((this->unk_1E0.unk_00 == 0) && (globalCtx->sceneNum == SCENE_SPOT10)) { + if (player->stateFlags2 & 0x1000000) { + player->stateFlags2 |= 0x2000000; + player->unk_6A8 = &this->actor; + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_SARIA); + this->actionFunc = func_80AABC10; + return; + } + + if (this->actor.xzDistToPlayer < (30.0f + this->collider.dim.radius)) { + player->stateFlags2 |= 0x800000; + } + } +} + +void func_80AABC10(EnMd* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_04) { + this->actionFunc = func_80AAB948; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->actor.textId = 0x1067; + func_8002F2CC(&this->actor, globalCtx, this->collider.dim.radius + 30.0f); + + this->actionFunc = func_80AAB948; + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } else { + player->stateFlags2 |= 0x800000; + } +} + +void func_80AABD0C(EnMd* this, GlobalContext* globalCtx) { + func_80034F54(globalCtx, this->unk_214, this->unk_236, 17); + func_80AAA93C(this); + + if (!(EnMd_FollowPath(this, globalCtx)) || (this->waypoint != 0)) { + this->actor.shape.rot = this->actor.world.rot; + return; + } + + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && !(gSaveContext.eventChkInf[1] & 0x1000) && + (globalCtx->sceneNum == SCENE_SPOT04)) { + Message_CloseTextbox(globalCtx); + gSaveContext.eventChkInf[1] |= 0x1000; + Actor_Kill(&this->actor); + return; + } + + func_80AAA92C(this, 11); + + this->skelAnime.playSpeed = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.home.pos = this->actor.world.pos; + this->actionFunc = func_80AAB8F8; +} + +void EnMd_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMd* this = (EnMd*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + EnMd_UpdateEyes(this); + func_80AAB5A4(this, globalCtx); + Actor_MoveForward(&this->actor); + func_80AAB158(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actionFunc(this, globalCtx); +} + +s32 EnMd_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnMd* this = (EnMd*)thisx; + Vec3s vec; + + if (limbIndex == 16) { + Matrix_Translate(1200.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec = this->unk_1E0.unk_08; + Matrix_RotateX((vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1200.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == 9) { + vec = this->unk_1E0.unk_0E; + Matrix_RotateX((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateY((vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + } + + if (((limbIndex == 9) || (limbIndex == 10)) || (limbIndex == 13)) { + rot->y += Math_SinS(this->unk_214[limbIndex]) * 200.0f; + rot->z += Math_CosS(this->unk_236[limbIndex]) * 200.0f; + } + + return false; +} + +void EnMd_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnMd* this = (EnMd*)thisx; + Vec3f vec = { 400.0f, 0.0f, 0.0f }; + + if (limbIndex == 16) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } +} + +void EnMd_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sEyeTextures[] = { + gMidoEyeOpenTex, + gMidoEyeHalfTex, + gMidoEyeClosedTex, + }; + EnMd* this = (EnMd*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_md.c", 1280); + + if (this->alpha == 255) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIdx])); + func_80034BA0(globalCtx, &this->skelAnime, EnMd_OverrideLimbDraw, EnMd_PostLimbDraw, &this->actor, this->alpha); + } else if (this->alpha != 0) { + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIdx])); + func_80034CC4(globalCtx, &this->skelAnime, EnMd_OverrideLimbDraw, EnMd_PostLimbDraw, &this->actor, this->alpha); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_md.c", 1317); +} diff --git a/soh/src/overlays/actors/ovl_En_Md/z_en_md.h b/soh/src/overlays/actors/ovl_En_Md/z_en_md.h new file mode 100644 index 000000000..064cc61ef --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Md/z_en_md.h @@ -0,0 +1,31 @@ +#ifndef Z_EN_MD_H +#define Z_EN_MD_H + +#include "ultra64.h" +#include "global.h" + +struct EnMd; + +typedef void (*EnMdActionFunc)(struct EnMd*, GlobalContext*); + +typedef struct EnMd { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMdActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ u8 unk_208; + /* 0x0209 */ u8 unk_209; + /* 0x020A */ u8 unk_20A; + /* 0x020B */ u8 unk_20B; + /* 0x020C */ s16 blinkTimer; + /* 0x020E */ s16 eyeIdx; + /* 0x0210 */ s16 alpha; + /* 0x0212 */ s16 waypoint; + /* 0x0214 */ s16 unk_214[17]; + /* 0x0236 */ s16 unk_236[17]; + /* 0x0258 */ Vec3s jointTable[17]; + /* 0x02BE */ Vec3s morphTable[17]; +} EnMd; // size = 0x0324 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c new file mode 100644 index 000000000..e0798da82 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.c @@ -0,0 +1,372 @@ +/* + * File: z_en_mk.c + * Overlay: ovl_En_Mk + * Description: Lakeside Professor + */ + +#include "z_en_mk.h" +#include "objects/object_mk/object_mk.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnMk_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMk_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMk_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMk_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnMk_Wait(EnMk* this, GlobalContext* globalCtx); + +const ActorInit En_Mk_InitVars = { + ACTOR_EN_MK, + ACTORCAT_NPC, + FLAGS, + OBJECT_MK, + sizeof(EnMk), + (ActorFunc)EnMk_Init, + (ActorFunc)EnMk_Destroy, + (ActorFunc)EnMk_Update, + (ActorFunc)EnMk_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +void EnMk_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMk* this = (EnMk*)thisx; + s32 swimFlag; + + this->actor.minVelocityY = -4.0f; + this->actor.gravity = -1.0f; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_mk_Skel_005DF0, &object_mk_Anim_000D88, this->jointTable, + this->morphTable, 13); + Animation_PlayLoop(&this->skelAnime, &object_mk_Anim_000D88); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = 0xFF; + Actor_SetScale(&this->actor, 0.01f); + + this->actionFunc = EnMk_Wait; + this->flags = 0; + this->swimFlag = 0; + this->actor.targetMode = 6; + + if (gSaveContext.itemGetInf[1] & 1) { + this->flags |= 4; + } +} + +void EnMk_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMk* this = (EnMk*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AACA40(EnMk* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = EnMk_Wait; + } + + this->flags |= 1; +} + +void func_80AACA94(EnMk* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx) != 0) { + this->actor.parent = NULL; + this->actionFunc = func_80AACA40; + func_80088AA0(240); + gSaveContext.eventInf[1] &= ~1; + } else { + func_8002F434(&this->actor, globalCtx, GI_EYEDROPS, 10000.0f, 50.0f); + } +} + +void func_80AACB14(EnMk* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = func_80AACA94; + func_8002F434(&this->actor, globalCtx, GI_EYEDROPS, 10000.0f, 50.0f); + } +} + +void func_80AACB6C(EnMk* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_80AACB14; + } + + this->flags |= 1; +} + +void func_80AACBAC(EnMk* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + this->actor.shape.rot.y -= 0x800; + } else { + this->actionFunc = func_80AACB6C; + Message_ContinueTextbox(globalCtx, 0x4030); + } +} + +void func_80AACC04(EnMk* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + } else { + this->timer = 16; + this->actionFunc = func_80AACBAC; + Animation_Change(&this->skelAnime, &object_mk_Anim_000D88, 1.0f, 0.0f, + Animation_GetLastFrame(&object_mk_Anim_000D88), ANIMMODE_LOOP, -4.0f); + this->flags &= ~2; + } +} + +void func_80AACCA0(EnMk* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + this->actor.shape.rot.y += 0x800; + } else { + this->timer = 120; + this->actionFunc = func_80AACC04; + Animation_Change(&this->skelAnime, &object_mk_Anim_000724, 1.0f, 0.0f, + Animation_GetLastFrame(&object_mk_Anim_000724), ANIMMODE_LOOP, -4.0f); + this->flags &= ~2; + } +} + +void func_80AACD48(EnMk* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = func_80AACCA0; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + player->exchangeItemId = EXCH_ITEM_NONE; + this->timer = 16; + Animation_Change(&this->skelAnime, &object_mk_Anim_000D88, 1.0f, 0.0f, + Animation_GetLastFrame(&object_mk_Anim_000D88), ANIMMODE_LOOP, -4.0f); + this->flags &= ~2; + } + + this->flags |= 1; +} + +void func_80AACE2C(EnMk* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x4001); + Animation_Change(&this->skelAnime, &object_mk_Anim_000AC0, 1.0f, 0.0f, + Animation_GetLastFrame(&object_mk_Anim_000AC0), ANIMMODE_ONCE, -4.0f); + this->flags &= ~2; + this->actionFunc = func_80AACD48; + } + + this->flags |= 1; +} + +void func_80AACEE8(EnMk* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x4000); + Animation_Change(&this->skelAnime, &object_mk_Anim_000AC0, 1.0f, 0.0f, + Animation_GetLastFrame(&object_mk_Anim_000AC0), ANIMMODE_LOOP, -4.0f); + this->flags &= ~2; + this->actionFunc = func_80AACE2C; + } + + this->flags |= 1; +} + +void func_80AACFA0(EnMk* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_80AACA40; + gSaveContext.itemGetInf[1] |= 1; + } else { + func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, 10000.0f, 50.0f); + } +} + +void func_80AAD014(EnMk* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = func_80AACFA0; + func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, 10000.0f, 50.0f); + } + + this->flags |= 1; +} + +void EnMk_Wait(EnMk* this, GlobalContext* globalCtx) { + s16 angle; + s32 swimFlag; + Player* player = GET_PLAYER(globalCtx); + s32 playerExchangeItem; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + playerExchangeItem = func_8002F368(globalCtx); + + if (this->actor.textId != 0x4018) { + player->actor.textId = this->actor.textId; + this->actionFunc = func_80AACA40; + } else { + if (INV_CONTENT(ITEM_ODD_MUSHROOM) == ITEM_EYEDROPS) { + player->actor.textId = 0x4032; + this->actionFunc = func_80AACA40; + } else { + switch (playerExchangeItem) { + case EXCH_ITEM_NONE: + if (this->swimFlag >= 8) { + if (gSaveContext.itemGetInf[1] & 1) { + player->actor.textId = 0x4075; + this->actionFunc = func_80AACA40; + } else { + player->actor.textId = 0x4074; + this->actionFunc = func_80AAD014; + this->swimFlag = 0; + } + } else { + if (this->swimFlag == 0) { + player->actor.textId = 0x4018; + this->actionFunc = func_80AACA40; + } else { + player->actor.textId = 0x406C + this->swimFlag; + this->actionFunc = func_80AACA40; + } + } + break; + case EXCH_ITEM_FROG: + player->actor.textId = 0x4019; + this->actionFunc = func_80AACEE8; + Animation_Change(&this->skelAnime, &object_mk_Anim_000368, 1.0f, 0.0f, + Animation_GetLastFrame(&object_mk_Anim_000368), ANIMMODE_ONCE, -4.0f); + this->flags &= ~2; + gSaveContext.timer2State = 0; + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + break; + default: + player->actor.textId = 0x4018; + this->actionFunc = func_80AACA40; + break; + } + } + } + } else { + this->actor.textId = Text_GetFaceReaction(globalCtx, 0x1A); + + if (this->actor.textId == 0) { + this->actor.textId = 0x4018; + } + + angle = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(angle) < 0x2151) && (this->actor.xzDistToPlayer < 100.0f)) { + func_8002F298(&this->actor, globalCtx, 100.0f, EXCH_ITEM_FROG); + this->flags |= 1; + } + } +} + +void EnMk_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMk* this = (EnMk*)thisx; + s32 pad; + Vec3s vec; + Player* player; + s16 swimFlag; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + if ((!(this->flags & 2)) && (SkelAnime_Update(&this->skelAnime))) { + this->flags |= 2; + } + + this->actionFunc(this, globalCtx); + + if (this->flags & 1) { + func_80038290(globalCtx, &this->actor, &this->headRotation, &vec, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->headRotation.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->headRotation.y, 0, 6, 6200, 100); + } + + player = GET_PLAYER(globalCtx); + + if (this->flags & 8) { + if (!(player->stateFlags2 & 0x400)) { + this->flags &= ~8; + } + } else { + if (player->currentBoots == PLAYER_BOOTS_IRON) { + this->flags |= 8; + } else if (player->stateFlags2 & 0x400) { + swimFlag = player->actor.yDistToWater; + + if (swimFlag > 0) { + if (swimFlag >= 320) { + if (swimFlag >= 355) { + swimFlag = 8; + } else { + swimFlag = 7; + } + } else if (swimFlag < 80) { + swimFlag = 1; + } else { + swimFlag *= 0.025f; + } + + if (this->swimFlag < swimFlag) { + this->swimFlag = swimFlag; + + if ((!(this->flags & 4)) && (this->swimFlag >= 8)) { + this->flags |= 4; + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + } + } + } + } +} + +s32 EnMk_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMk* this = (EnMk*)thisx; + + if (limbIndex == 11) { + rot->y -= this->headRotation.y; + rot->z += this->headRotation.x; + } + + return false; +} + +void EnMk_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_80AAD64C = { 1000.0f, -100.0f, 0.0f }; + EnMk* this = (EnMk*)thisx; + + if (limbIndex == 11) { + Matrix_MultVec3f(&D_80AAD64C, &this->actor.focus.pos); + } +} + +void EnMk_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnMk* this = (EnMk*)thisx; + + func_800943C8(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMk_OverrideLimbDraw, EnMk_PostLimbDraw, &this->actor); +} diff --git a/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.h b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.h new file mode 100644 index 000000000..6c8ba12ca --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mk/z_en_mk.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_MK_H +#define Z_EN_MK_H + +#include "ultra64.h" +#include "global.h" + +struct EnMk; + +typedef void (*EnMkActionFunc)(struct EnMk*, GlobalContext*); + +typedef struct EnMk { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[13]; + /* 0x022A */ Vec3s morphTable[13]; + /* 0x0278 */ Vec3s headRotation; // Used to rotate the man's head towards Link + /* 0x027E */ u16 flags; + /* 0x0280 */ s16 swimFlag; + /* 0x0282 */ u16 timer; + /* 0x0284 */ EnMkActionFunc actionFunc; +} EnMk; // size = 0x0288 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c b/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c new file mode 100644 index 000000000..4c61ae18e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.c @@ -0,0 +1,600 @@ +/* + * File: z_en_mm.c + * Overlay: ovl_En_Mm + * Description: Running Man (child) + */ + +#include "z_en_mm.h" +#include "objects/object_mm/object_mm.h" +#include "objects/object_link_child/object_link_child.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +typedef enum { + /* 0 */ RM_ANIM_RUN, + /* 1 */ RM_ANIM_SIT, + /* 2 */ RM_ANIM_SIT_WAIT, + /* 3 */ RM_ANIM_STAND, + /* 4 */ RM_ANIM_SPRINT, + /* 5 */ RM_ANIM_EXCITED, // plays when talking to him with bunny hood on + /* 6 */ RM_ANIM_HAPPY // plays when you sell him the bunny hood +} RunningManAnimIndex; + +typedef enum { + /* 0 */ RM_MOUTH_CLOSED, + /* 1 */ RM_MOUTH_OPEN +} RunningManMouthTex; + +void EnMm_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMm_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMm_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AAE598(EnMm* this, GlobalContext* globalCtx); +void func_80AAE294(EnMm* this, GlobalContext* globalCtx); +void func_80AAE50C(EnMm* this, GlobalContext* globalCtx); +void func_80AAE224(EnMm* this, GlobalContext* globalCtx); +s32 func_80AADA70(void); + +s32 EnMm_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx); +void EnMm_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void*); + +const ActorInit En_Mm_InitVars = { + ACTOR_EN_MM, + ACTORCAT_NPC, + FLAGS, + OBJECT_MM, + sizeof(EnMm), + (ActorFunc)EnMm_Init, + (ActorFunc)EnMm_Destroy, + (ActorFunc)EnMm_Update, + (ActorFunc)EnMm_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 63, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit[] = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static AnimationSpeedInfo sAnimationInfo[] = { + { &gRunningManRunAnim, 1.0f, ANIMMODE_LOOP, -7.0f }, { &gRunningManSitStandAnim, -1.0f, ANIMMODE_ONCE, -7.0f }, + { &gRunningManSitWaitAnim, 1.0f, ANIMMODE_LOOP, -7.0f }, { &gRunningManSitStandAnim, 1.0f, ANIMMODE_ONCE, -7.0f }, + { &gRunningManSprintAnim, 1.0f, ANIMMODE_LOOP, -7.0f }, { &gRunningManExcitedAnim, 1.0f, ANIMMODE_LOOP, -12.0f }, + { &gRunningManHappyAnim, 1.0f, ANIMMODE_LOOP, -12.0f }, +}; + +typedef struct { + /* 0x00 */ s32 unk_00; + /* 0x04 */ s32 unk_04; + /* 0x08 */ s32 unk_08; + /* 0x0C */ s32 unk_0C; +} EnMmPathInfo; + +static EnMmPathInfo sPathInfo[] = { + { 0, 1, 0, 0 }, + { 1, 1, 0, 1 }, + { 1, 3, 2, 1 }, + { -1, 0, 2, 0 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_STOP), +}; + +void EnMm_ChangeAnim(EnMm* this, s32 index, s32* currentIndex) { + f32 morphFrames; + + if ((*currentIndex < 0) || (index == *currentIndex)) { + morphFrames = 0.0f; + } else { + morphFrames = sAnimationInfo[index].morphFrames; + } + + if (sAnimationInfo[index].playSpeed >= 0.0f) { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, sAnimationInfo[index].playSpeed, 0.0f, + Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, + morphFrames); + } else { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, sAnimationInfo[index].playSpeed, + Animation_GetLastFrame(sAnimationInfo[index].animation), 0.0f, sAnimationInfo[index].mode, + morphFrames); + } + + *currentIndex = index; +} + +void EnMm_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnMm* this = (EnMm*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 21.0f); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gRunningManSkel, NULL, this->jointTable, this->morphTable, 16); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, sColChkInfoInit); + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + Animation_Change(&this->skelAnime, sAnimationInfo[RM_ANIM_RUN].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[RM_ANIM_RUN].animation), sAnimationInfo[RM_ANIM_RUN].mode, + sAnimationInfo[RM_ANIM_RUN].morphFrames); + + this->path = this->actor.params & 0xFF; + this->unk_1F0 = 2; + this->unk_1E8 = 0; + this->actor.targetMode = 2; + this->actor.gravity = -1.0f; + this->speedXZ = 3.0f; + this->unk_204 = this->actor.objBankIndex; + + if (func_80AADA70() == 1) { + this->mouthTexIndex = RM_MOUTH_OPEN; + EnMm_ChangeAnim(this, RM_ANIM_RUN, &this->curAnimIndex); + this->actionFunc = func_80AAE598; + } else { + this->mouthTexIndex = RM_MOUTH_CLOSED; + EnMm_ChangeAnim(this, RM_ANIM_SIT_WAIT, &this->curAnimIndex); + this->actionFunc = func_80AAE294; + } +} + +void EnMm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnMm* this = (EnMm*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80AADA70(void) { + s32 isDay = false; + + if ((gSaveContext.dayTime > 0x3555) && (gSaveContext.dayTime <= 0xD556)) { + isDay = true; + } + + return isDay; +} + +s32 func_80AADAA0(EnMm* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 sp1C = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_CLOSING: + case TEXT_STATE_DONE_FADING: + break; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + player->actor.textId = 0x202D; + this->unk_254 &= ~1; + EnMm_ChangeAnim(this, RM_ANIM_HAPPY, &this->curAnimIndex); + } else { + player->actor.textId = 0x202C; + gSaveContext.infTable[23] |= 0x1000; + } + sp1C = 2; + } + break; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx)) { + Player_UnsetMask(globalCtx); + Item_Give(globalCtx, ITEM_SOLD_OUT); + gSaveContext.itemGetInf[3] |= 0x800; + Rupees_ChangeBy(500); + player->actor.textId = 0x202E; + sp1C = 2; + } + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + if ((player->actor.textId == 0x202E) || (player->actor.textId == 0x202C)) { + this->unk_254 |= 1; + EnMm_ChangeAnim(this, RM_ANIM_SIT_WAIT, &this->curAnimIndex); + } + sp1C = 0; + } + break; + } + + return sp1C; +} + +s32 EnMm_GetTextId(EnMm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 textId; + + textId = Text_GetFaceReaction(globalCtx, 0x1C); + + if (gSaveContext.itemGetInf[3] & 0x800) { + if (textId == 0) { + textId = 0x204D; + } + } else if (player->currentMask == PLAYER_MASK_BUNNY) { + textId = (gSaveContext.infTable[23] & 0x1000) ? 0x202B : 0x202A; + } else if (textId == 0) { + textId = 0x2029; + } + + return textId; +} + +void func_80AADCD0(EnMm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 yawDiff; + s16 sp26; + s16 sp24; + + if (this->unk_1E0 == 2) { + Message_ContinueTextbox(globalCtx, player->actor.textId); + this->unk_1E0 = 1; + } else if (this->unk_1E0 == 1) { + this->unk_1E0 = func_80AADAA0(this, globalCtx); + } else { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_1E0 = 1; + + if (this->curAnimIndex != 5) { + if ((this->actor.textId == 0x202A) || (this->actor.textId == 0x202B)) { + EnMm_ChangeAnim(this, RM_ANIM_EXCITED, &this->curAnimIndex); + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } + } else { + Actor_GetScreenPos(globalCtx, &this->actor, &sp26, &sp24); + yawDiff = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)); + + if ((sp26 >= 0) && (sp26 <= 0x140) && (sp24 >= 0) && (sp24 <= 0xF0) && (yawDiff <= 17152.0f) && + (this->unk_1E0 != 3) && func_8002F2CC(&this->actor, globalCtx, 100.0f)) { + this->actor.textId = EnMm_GetTextId(this, globalCtx); + } + } + } +} + +s32 EnMm_GetPointCount(Path* pathList, s32 pathNum) { + return (pathList + pathNum)->count; +} + +s32 func_80AADE60(Path* pathList, Vec3f* pos, s32 pathNum, s32 waypoint) { + Vec3s* pointPos; + pointPos = &((Vec3s*)SEGMENTED_TO_VIRTUAL((pathList + pathNum)->points))[waypoint]; + + pos->x = pointPos->x; + pos->y = pointPos->y; + pos->z = pointPos->z; + + return 0; +} + +s32 func_80AADEF0(EnMm* this, GlobalContext* globalCtx) { + f32 xDiff; + f32 zDiff; + Vec3f waypointPos; + s32 phi_a2; + s32 phi_v1; + + func_80AADE60(globalCtx->setupPathList, &waypointPos, this->path, this->waypoint); + + xDiff = waypointPos.x - this->actor.world.pos.x; + zDiff = waypointPos.z - this->actor.world.pos.z; + + this->yawToWaypoint = (s32)(Math_FAtan2F(xDiff, zDiff) * (0x8000 / M_PI)); + this->distToWaypoint = sqrtf(SQ(xDiff) + SQ(zDiff)); + + while ((this->distToWaypoint <= 10.44f) && (this->unk_1E8 != 0)) { + this->waypoint += sPathInfo[this->unk_1E8].unk_00; + + phi_a2 = sPathInfo[this->unk_1E8].unk_08; + + switch (phi_a2) { + case 0: + phi_a2 = 0; + break; + case 1: + phi_a2 = EnMm_GetPointCount(globalCtx->setupPathList, this->path) - 1; + break; + case 2: + phi_a2 = this->unk_1F0; + break; + } + + phi_v1 = sPathInfo[this->unk_1E8].unk_0C; + + switch (phi_v1) { + case 0: + phi_v1 = 0; + break; + case 1: + phi_v1 = EnMm_GetPointCount(globalCtx->setupPathList, this->path) - 1; + break; + case 2: + phi_v1 = this->unk_1F0; + break; + } + + if ((sPathInfo[this->unk_1E8].unk_00 >= 0 && (this->waypoint < phi_a2 || phi_v1 < this->waypoint)) || + (sPathInfo[this->unk_1E8].unk_00 < 0 && (phi_a2 < this->waypoint || this->waypoint < phi_v1))) { + this->unk_1E8 = sPathInfo[this->unk_1E8].unk_04; + this->waypoint = sPathInfo[this->unk_1E8].unk_08; + } + + func_80AADE60(globalCtx->setupPathList, &waypointPos, this->path, this->waypoint); + + xDiff = waypointPos.x - this->actor.world.pos.x; + zDiff = waypointPos.z - this->actor.world.pos.z; + + this->yawToWaypoint = (s32)(Math_FAtan2F(xDiff, zDiff) * (0x8000 / M_PI)); + this->distToWaypoint = sqrtf(SQ(xDiff) + SQ(zDiff)); + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->yawToWaypoint, 1, 2500, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + Math_SmoothStepToF(&this->actor.speedXZ, this->speedXZ, 0.6f, this->distToWaypoint, 0.0f); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + return 0; +} + +void func_80AAE224(EnMm* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->actionFunc = func_80AAE598; + this->unk_1E8 = 0; + this->mouthTexIndex = RM_MOUTH_CLOSED; + this->unk_254 |= 1; + this->unk_1E0 = 0; + this->actor.speedXZ = 0.0f; + EnMm_ChangeAnim(this, RM_ANIM_SIT_WAIT, &this->curAnimIndex); + } +} + +void func_80AAE294(EnMm* this, GlobalContext* globalCtx) { + f32 floorYNorm; + Vec3f dustPos; + + if (!Player_InCsMode(globalCtx)) { + SkelAnime_Update(&this->skelAnime); + + if (this->curAnimIndex == 0) { + if (((s32)this->skelAnime.curFrame == 1) || ((s32)this->skelAnime.curFrame == 6)) { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_GROUND); + } + } + + if (this->curAnimIndex == 4) { + if (((this->skelAnime.curFrame - this->skelAnime.playSpeed < 9.0f) && (this->skelAnime.curFrame >= 9.0f)) || + ((this->skelAnime.curFrame - this->skelAnime.playSpeed < 19.0f) && + (this->skelAnime.curFrame >= 19.0f))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + } + + if (gSaveContext.itemGetInf[3] & 0x800) { + this->speedXZ = 10.0f; + this->skelAnime.playSpeed = 2.0f; + } else { + this->speedXZ = 3.0f; + this->skelAnime.playSpeed = 1.0f; + } + + func_80AADEF0(this, globalCtx); + + if (func_80AADA70() == 0) { + if (this->actor.floorPoly != NULL) { + floorYNorm = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); + + if ((floorYNorm > 0.9848f) || (floorYNorm < -0.9848f)) { + if (this->sitTimer > 30) { + EnMm_ChangeAnim(this, RM_ANIM_SIT, &this->curAnimIndex); + this->actionFunc = func_80AAE224; + } else { + this->sitTimer++; + } + } else { + this->sitTimer = 0; + } + } + } + + if (gSaveContext.itemGetInf[3] & 0x800) { + dustPos.x = this->actor.world.pos.x; + dustPos.y = this->actor.world.pos.y; + dustPos.z = this->actor.world.pos.z; + + if (gSaveContext.gameMode != 3) { + func_80033480(globalCtx, &dustPos, 50.0f, 2, 350, 20, 0); + } + + if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { + func_8002F71C(globalCtx, &this->actor, 3.0f, this->actor.yawTowardsPlayer, 4.0f); + } + } + } +} + +void func_80AAE50C(EnMm* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->sitTimer = 0; + this->actionFunc = func_80AAE294; + + if (gSaveContext.itemGetInf[3] & 0x800) { + EnMm_ChangeAnim(this, RM_ANIM_SPRINT, &this->curAnimIndex); + this->mouthTexIndex = RM_MOUTH_CLOSED; + } else { + EnMm_ChangeAnim(this, RM_ANIM_RUN, &this->curAnimIndex); + this->mouthTexIndex = RM_MOUTH_OPEN; + } + + this->unk_1E8 = 1; + } +} + +void func_80AAE598(EnMm* this, GlobalContext* globalCtx) { + func_80038290(globalCtx, &this->actor, &this->unk_248, &this->unk_24E, this->actor.focus.pos); + SkelAnime_Update(&this->skelAnime); + + if ((func_80AADA70() != 0) && (this->unk_1E0 == 0)) { + this->unk_1E0 = 3; + this->actionFunc = func_80AAE50C; + this->unk_254 &= ~1; + EnMm_ChangeAnim(this, RM_ANIM_STAND, &this->curAnimIndex); + } +} + +void EnMm_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnMm* this = (EnMm*)thisx; + + this->actionFunc(this, globalCtx); + func_80AADCD0(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnMm_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* mouthTextures[] = { gRunningManMouthOpenTex, gRunningManMouthClosedTex }; + s32 pad; + EnMm* this = (EnMm*)thisx; + + if (0) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_mm.c", 1065); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(mouthTextures[this->mouthTexIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMm_OverrideLimbDraw, EnMm_PostLimbDraw, this); + + if (gSaveContext.itemGetInf[3] & 0x800) { + s32 linkChildObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_LINK_CHILD); + + if (linkChildObjBankIndex >= 0) { + Mtx* mtx; + Vec3s sp50; + Mtx* mtx2; + + mtx = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx) * 2); + + Matrix_Put(&this->unk_208); + mtx2 = Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_mm.c", 1111); + + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[linkChildObjBankIndex].segment); + gSPSegment(POLY_OPA_DISP++, 0x0B, mtx); + gSPSegment(POLY_OPA_DISP++, 0x0D, mtx2 - 7); + + sp50.x = 994; + sp50.y = 3518; + sp50.z = -13450; + + Matrix_SetTranslateRotateYXZ(97.0f, -1203.0f, -240.0f, &sp50); + Matrix_ToMtx(mtx++, "../z_en_mm.c", 1124); + + sp50.x = -994; + sp50.y = -3518; + sp50.z = -13450; + + Matrix_SetTranslateRotateYXZ(97.0f, -1203.0f, 240.0f, &sp50); + Matrix_ToMtx(mtx, "../z_en_mm.c", 1131); + + gSPDisplayList(POLY_OPA_DISP++, gLinkChildBunnyHoodDL); + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->actor.objBankIndex].segment); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_mm.c", 1141); +} + +s32 EnMm_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMm* this = (EnMm*)thisx; + + if (this->unk_254 & 1) { + switch (limbIndex) { + case 8: + rot->x += this->unk_24E.y; + rot->y -= this->unk_24E.x; + break; + case 15: + rot->x += this->unk_248.y; + rot->z += (this->unk_248.x + 0xFA0); + break; + default: + break; + } + } + + return 0; +} + +void EnMm_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f headOffset = { 200.0f, 800.0f, 0.0f }; + EnMm* this = (EnMm*)thisx; + + if (limbIndex == 15) { + Matrix_MultVec3f(&headOffset, &this->actor.focus.pos); + Matrix_Translate(260.0f, 20.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateY(0.0f, MTXMODE_APPLY); + Matrix_RotateX(0.0f, MTXMODE_APPLY); + Matrix_RotateZ(4.0f * M_PI / 5.0f, MTXMODE_APPLY); + Matrix_Translate(-260.0f, 58.0f, 10.0f, MTXMODE_APPLY); + Matrix_Get(&this->unk_208); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.h b/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.h new file mode 100644 index 000000000..5dec76bb0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mm/z_en_mm.h @@ -0,0 +1,36 @@ +#ifndef Z_EN_MM_H +#define Z_EN_MM_H + +#include "ultra64.h" +#include "global.h" + +struct EnMm; + +typedef void (*EnMmActionFunc)(struct EnMm*, GlobalContext*); + +typedef struct EnMm { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMmActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s32 unk_1E0; + /* 0x01E4 */ s32 mouthTexIndex; + /* 0x01E8 */ s32 unk_1E8; + /* 0x01EC */ s32 path; + /* 0x01F0 */ s32 unk_1F0; + /* 0x01F4 */ s32 waypoint; + /* 0x01F8 */ f32 yawToWaypoint; + /* 0x01FC */ f32 distToWaypoint; + /* 0x0200 */ f32 speedXZ; + /* 0x0204 */ s32 unk_204; + /* 0x0208 */ MtxF unk_208; + /* 0x0248 */ Vec3s unk_248; + /* 0x024E */ Vec3s unk_24E; + /* 0x0254 */ s32 unk_254; + /* 0x0258 */ s32 curAnimIndex; + /* 0x025C */ s32 sitTimer; + /* 0x0260 */ Vec3s jointTable[16]; + /* 0x02C0 */ Vec3s morphTable[16]; +} EnMm; // size = 0x0320 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c b/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c new file mode 100644 index 000000000..9ac0d06a2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.c @@ -0,0 +1,346 @@ +/* + * File: z_en_mm.c + * Overlay: ovl_En_Mm + * Description: Running Man (adult) + */ + +#include "z_en_mm2.h" +#include "vt.h" +#include "objects/object_mm/object_mm.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +typedef enum { + /* 0 */ RM2_ANIM_RUN, + /* 1 */ RM2_ANIM_SIT, + /* 2 */ RM2_ANIM_SIT_WAIT, + /* 3 */ RM2_ANIM_STAND, + /* 4 */ RM2_ANIM_SPRINT, + /* 5 */ RM2_ANIM_EXCITED, // plays when talking to him with bunny hood on + /* 6 */ RM2_ANIM_HAPPY // plays when you sell him the bunny hood +} RunningManAnimIndex; + +typedef enum { + /* 0 */ RM2_MOUTH_CLOSED, + /* 1 */ RM2_MOUTH_OPEN +} RunningManMouthTex; + +void EnMm2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMm2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMm2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMm2_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_80AAF3C0(EnMm2* this, GlobalContext* globalCtx); +void func_80AAF57C(EnMm2* this, GlobalContext* globalCtx); +void func_80AAF668(EnMm2* this, GlobalContext* globalCtx); +s32 EnMm2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx); +void EnMm2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx); + +const ActorInit En_Mm2_InitVars = { + ACTOR_EN_MM2, + ACTORCAT_NPC, + FLAGS, + OBJECT_MM, + sizeof(EnMm2), + (ActorFunc)EnMm2_Init, + (ActorFunc)EnMm2_Destroy, + (ActorFunc)EnMm2_Update, + (ActorFunc)EnMm2_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000004, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 18, 63, 0, { 0, 0, 0 } }, +}; + +static AnimationSpeedInfo sAnimationInfo[] = { + { &gRunningManRunAnim, 1.0f, ANIMMODE_LOOP, -7.0f }, { &gRunningManSitStandAnim, -1.0f, ANIMMODE_ONCE, -7.0f }, + { &gRunningManSitWaitAnim, 1.0f, ANIMMODE_LOOP, -7.0f }, { &gRunningManSitStandAnim, 1.0f, ANIMMODE_ONCE, -7.0f }, + { &gRunningManSprintAnim, 1.0f, ANIMMODE_LOOP, -7.0f }, { &gRunningManExcitedAnim, 1.0f, ANIMMODE_LOOP, -12.0f }, + { &gRunningManHappyAnim, 1.0f, ANIMMODE_LOOP, -12.0f }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_STOP), +}; + +void EnMm2_ChangeAnim(EnMm2* this, s32 index, s32* currentIndex) { + f32 phi_f0; + + if ((*currentIndex < 0) || (index == *currentIndex)) { + phi_f0 = 0.0f; + } else { + phi_f0 = sAnimationInfo[index].morphFrames; + } + + if (sAnimationInfo[index].playSpeed >= 0.0f) { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, sAnimationInfo[index].playSpeed, 0.0f, + (f32)Animation_GetLastFrame(sAnimationInfo[index].animation), sAnimationInfo[index].mode, + phi_f0); + } else { + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, sAnimationInfo[index].playSpeed, + (f32)Animation_GetLastFrame(sAnimationInfo[index].animation), 0.0f, sAnimationInfo[index].mode, + phi_f0); + } + *currentIndex = index; +} + +void func_80AAEF70(EnMm2* this, GlobalContext* globalCtx) { + if ((gSaveContext.eventChkInf[9] & 0xF) != 0xF) { + this->actor.textId = 0x6086; + } else if (gSaveContext.infTable[23] & 0x8000) { + if (gSaveContext.eventInf[1] & 1) { + this->actor.textId = 0x6082; + } else if (gSaveContext.timer2State != 0) { + this->actor.textId = 0x6076; + } else if (HIGH_SCORE(HS_MARATHON) == 158) { + this->actor.textId = 0x607E; + } else { + this->actor.textId = 0x6081; + } + } else if (gSaveContext.timer2State) { + this->actor.textId = 0x6076; + } else { + this->actor.textId = 0x607D; + gSaveContext.eventInf[1] &= ~1; + HIGH_SCORE(HS_MARATHON) = 158; + } +} + +void EnMm2_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnMm2* this = (EnMm2*)thisx; + GlobalContext* globalCtx = globalCtx2; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 21.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gRunningManSkel, NULL, this->jointTable, this->morphTable, 16); + Animation_Change(&this->skelAnime, sAnimationInfo[RM2_ANIM_SIT_WAIT].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo[RM2_ANIM_SIT_WAIT].animation), + sAnimationInfo[RM2_ANIM_SIT_WAIT].mode, sAnimationInfo[RM2_ANIM_SIT_WAIT].morphFrames); + this->previousAnimation = RM2_ANIM_SIT_WAIT; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->mouthTexIndex = RM2_MOUTH_CLOSED; + this->actor.targetMode = 6; + this->unk_1F4 |= 1; + this->actor.gravity = -1.0f; + if (this->actor.params == 1) { + this->actionFunc = func_80AAF668; + } else { + func_80AAEF70(this, globalCtx); + this->actionFunc = func_80AAF57C; + } + if (!LINK_IS_ADULT) { + Actor_Kill(&this->actor); + } + if (this->actor.params == 1) { + if (!(gSaveContext.infTable[23] & 0x8000) || !(gSaveContext.eventInf[1] & 1)) { + osSyncPrintf(VT_FGCOL(CYAN) " マラソン 開始されていない \n" VT_RST "\n"); + Actor_Kill(&this->actor); + } + } +} + +void EnMm2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMm2* this = (EnMm2*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80AAF224(EnMm2* this, GlobalContext* globalCtx, EnMm2ActionFunc actionFunc) { + s16 yawDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = actionFunc; + return 1; + } + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((ABS(yawDiff) <= 0x4300) && (this->actor.xzDistToPlayer < 100.0f)) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + return 0; +} + +void func_80AAF2BC(EnMm2* this, GlobalContext* globalCtx) { + if (this->unk_1F6 > 60) { + Actor_Kill(&this->actor); + } + SkelAnime_Update(&this->skelAnime); + this->unk_1F6++; + Math_SmoothStepToF(&this->actor.speedXZ, 10.0f, 0.6f, 2.0f, 0.0f); +} + +void func_80AAF330(EnMm2* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->actionFunc = func_80AAF2BC; + EnMm2_ChangeAnim(this, RM2_ANIM_RUN, &this->previousAnimation); + this->mouthTexIndex = RM2_MOUTH_OPEN; + if (!(this->unk_1F4 & 2)) { + Message_CloseTextbox(globalCtx); + } + gSaveContext.timer2State = 0; + gSaveContext.eventInf[1] &= ~1; + } +} + +void func_80AAF3C0(EnMm2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + switch (this->actor.textId) { + case 0x607D: + case 0x607E: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + Message_ContinueTextbox(globalCtx, 0x607F); + this->actor.textId = 0x607F; + gSaveContext.eventInf[1] |= 1; + break; + case 1: + Message_ContinueTextbox(globalCtx, 0x6080); + this->actor.textId = 0x6080; + break; + }; + if (this->unk_1F4 & 4) { + if (1) {} + this->unk_1F4 &= ~4; + HIGH_SCORE(HS_MARATHON) += 1; + } + } + return; + case 0x6081: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->unk_1F4 |= 4; + HIGH_SCORE(HS_MARATHON) -= 1; + Message_ContinueTextbox(globalCtx, 0x607E); + this->actor.textId = 0x607E; + } + return; + } + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + if (this->actor.textId == 0x607F) { + func_80088AA0(0); + this->actionFunc = func_80AAF57C; + } else { + this->actionFunc = func_80AAF57C; + } + this->actionFunc = func_80AAF57C; + func_80AAEF70(this, globalCtx); + } +} + +void func_80AAF57C(EnMm2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + func_80AAEF70(this, globalCtx); + if ((func_80AAF224(this, globalCtx, func_80AAF3C0)) && (this->actor.textId == 0x607D)) { + gSaveContext.infTable[23] |= 0x8000; + } +} + +void func_80AAF5EC(EnMm2* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->unk_1F4 &= ~1; + EnMm2_ChangeAnim(this, RM2_ANIM_STAND, &this->previousAnimation); + this->actionFunc = func_80AAF330; + } +} + +void func_80AAF668(EnMm2* this, GlobalContext* globalCtx) { + this->actor.world.rot.y = -0x3E80; + this->actor.shape.rot.y = this->actor.world.rot.y; + SkelAnime_Update(&this->skelAnime); + if (((void)0, gSaveContext.timer2Value) < HIGH_SCORE(HS_MARATHON)) { + this->actor.textId = 0x6085; + } else { + this->actor.textId = 0x6084; + } + if (func_80AAF224(this, globalCtx, func_80AAF5EC)) { + this->unk_1F6 = 0; + if (((void)0, gSaveContext.timer2Value) < HIGH_SCORE(HS_MARATHON)) { + HIGH_SCORE(HS_MARATHON) = gSaveContext.timer2Value; + } + } else { + LOG_HEX("((z_common_data.event_inf[1]) & (0x0001))", gSaveContext.eventInf[1] & 1, "../z_en_mm2.c", 541); + if (!(gSaveContext.eventInf[1] & 1)) { + this->unk_1F4 |= 2; + this->unk_1F4 &= ~1; + EnMm2_ChangeAnim(this, RM2_ANIM_STAND, &this->previousAnimation); + this->actionFunc = func_80AAF330; + } + } +} + +void EnMm2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMm2* this = (EnMm2*)thisx; + s32 pad; + + if (this->unk_1F4 & 1) { + func_80038290(globalCtx, &this->actor, &this->unk_1E8, &this->unk_1EE, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->unk_1E8.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_1E8.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_1EE.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_1EE.y, 0, 6, 6200, 100); + } + this->actionFunc(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); +} + +void EnMm2_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* mouthTextures[] = { gRunningManMouthOpenTex, gRunningManMouthClosedTex }; + EnMm2* this = (EnMm2*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_mm2.c", 634); + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(mouthTextures[this->mouthTexIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMm2_OverrideLimbDraw, EnMm2_PostLimbDraw, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_mm2.c", 654); +} + +s32 EnMm2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMm2* this = (EnMm2*)thisx; + + switch (limbIndex) { + case 8: + rot->x += this->unk_1EE.y; + rot->y -= this->unk_1EE.x; + break; + case 15: + rot->x += this->unk_1E8.y; + rot->z += this->unk_1E8.x + 0xFA0; + break; + } + + return 0; +} + +void EnMm2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f headOffset = { 200.0f, 800.0f, 0.0f }; + EnMm2* this = (EnMm2*)thisx; + + if (limbIndex == 15) { + Matrix_MultVec3f(&headOffset, &this->actor.focus.pos); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.h b/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.h new file mode 100644 index 000000000..9fb3df3e8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mm2/z_en_mm2.h @@ -0,0 +1,27 @@ +#ifndef Z_EN_MM2_H +#define Z_EN_MM2_H + +#include "ultra64.h" +#include "global.h" + +struct EnMm2; + +typedef void (*EnMm2ActionFunc)(struct EnMm2*, GlobalContext*); + +typedef struct EnMm2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMm2ActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s32 mouthTexIndex; + /* 0x01E4 */ char unk_1E4[0x4]; + /* 0x01E8 */ Vec3s unk_1E8; + /* 0x01EE */ Vec3s unk_1EE; + /* 0x01F4 */ u16 unk_1F4; + /* 0x01F6 */ s16 unk_1F6; + /* 0x01F8 */ s32 previousAnimation; + /* 0x01FC */ Vec3s jointTable[16]; + /* 0x025C */ Vec3s morphTable[16]; +} EnMm2; // size = 0x02BC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c b/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c new file mode 100644 index 000000000..04919761e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.c @@ -0,0 +1,187 @@ +/* + * File: z_en_ms.c + * Overlay: ovl_En_Ms + * Description: Magic Bean Salesman + */ + +#include "z_en_ms.h" +#include "objects/object_ms/object_ms.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnMs_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMs_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnMs_SetOfferText(EnMs* this, GlobalContext* globalCtx); +void EnMs_Wait(EnMs* this, GlobalContext* globalCtx); +void EnMs_Talk(EnMs* this, GlobalContext* globalCtx); +void EnMs_Sell(EnMs* this, GlobalContext* globalCtx); +void EnMs_TalkAfterPurchase(EnMs* this, GlobalContext* globalCtx); + +const ActorInit En_Ms_InitVars = { + ACTOR_EN_MS, + ACTORCAT_NPC, + FLAGS, + OBJECT_MS, + sizeof(EnMs), + (ActorFunc)EnMs_Init, + (ActorFunc)EnMs_Destroy, + (ActorFunc)EnMs_Update, + (ActorFunc)EnMs_Draw, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0x00000000, 0x00, 0x00 }, { 0xFFCFFFFF, 0x00, 0x00 }, 0x00, 0x01, 0x01 }, + { 22, 37, 0, { 0 } }, +}; + +static s16 sPrices[] = { + 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, +}; + +static u16 sOfferTextIDs[] = { + 0x405E, 0x405F, 0x4060, 0x4061, 0x4062, 0x4063, 0x4064, 0x4065, 0x4066, 0x4067, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP), +}; + +void EnMs_SetOfferText(EnMs* this, GlobalContext* globalCtx) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 0x1B); + if (this->actor.textId == 0) { + if (BEANS_BOUGHT >= 10) { + this->actor.textId = 0x406B; + } else { + this->actor.textId = sOfferTextIDs[BEANS_BOUGHT]; + } + } +} + +void EnMs_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMs* this = (EnMs*)thisx; + s32 pad; + + if (LINK_AGE_IN_YEARS != YEARS_CHILD) { + Actor_Kill(&this->actor); + return; + } + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBeanSalesmanSkel, &gBeanSalesmanEatingAnim, this->jointTable, + this->morphTable, 9); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f); + Actor_SetScale(&this->actor, 0.015f); + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -1.0f; + + EnMs_SetOfferText(this, globalCtx); + + this->actionFunc = EnMs_Wait; +} + +void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMs* this = (EnMs*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnMs_Wait(EnMs* this, GlobalContext* globalCtx) { + s16 yawDiff; + + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + EnMs_SetOfferText(this, globalCtx); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { // if talk is initiated + this->actionFunc = EnMs_Talk; + } else if ((this->actor.xzDistToPlayer < 90.0f) && (ABS(yawDiff) < 0x2000)) { // talk range + func_8002F2CC(&this->actor, globalCtx, 90.0f); + } +} + +void EnMs_Talk(EnMs* this, GlobalContext* globalCtx) { + u8 dialogState; + + dialogState = Message_GetState(&globalCtx->msgCtx); + if (dialogState != TEXT_STATE_CHOICE) { + if ((dialogState == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { // advanced final textbox + this->actionFunc = EnMs_Wait; + } + } else if (Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + if (gSaveContext.rupees < sPrices[BEANS_BOUGHT]) { + Message_ContinueTextbox(globalCtx, 0x4069); // not enough rupees text + return; + } + func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f); + this->actionFunc = EnMs_Sell; + return; + case 1: // no + Message_ContinueTextbox(globalCtx, 0x4068); + default: + return; + } + } +} + +void EnMs_Sell(EnMs* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + Rupees_ChangeBy(-sPrices[BEANS_BOUGHT]); + this->actor.parent = NULL; + this->actionFunc = EnMs_TalkAfterPurchase; + } else { + func_8002F434(&this->actor, globalCtx, GI_BEAN, 90.0f, 10.0f); + } +} + +void EnMs_TalkAfterPurchase(EnMs* this, GlobalContext* globalCtx) { + // if dialog state is 6 and player responded to textbox + if ((Message_GetState(&globalCtx->msgCtx)) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x406C); + this->actionFunc = EnMs_Talk; + } +} + +void EnMs_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMs* this = (EnMs*)thisx; + s32 pad; + + this->activeTimer += 1; + Actor_SetFocus(&this->actor, 20.0f); + this->actor.targetArrowOffset = 500.0f; + Actor_SetScale(&this->actor, 0.015f); + SkelAnime_Update(&this->skelAnime); + this->actionFunc(this, globalCtx); + + if (gSaveContext.entranceIndex == 0x157 && gSaveContext.sceneSetupIndex == 8) { // ride carpet if in credits + Actor_MoveForward(&this->actor); + osSyncPrintf("OOOHHHHHH %f\n", this->actor.velocity.y); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnMs* this = (EnMs*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.h b/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.h new file mode 100644 index 000000000..a3d3dc7d5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ms/z_en_ms.h @@ -0,0 +1,21 @@ +#ifndef Z_EN_MS_H +#define Z_EN_MS_H + +#include "ultra64.h" +#include "global.h" + +struct EnMs; + +typedef void (*EnMsActionFunc)(struct EnMs*, GlobalContext*); + +typedef struct EnMs { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[9]; + /* 0x01C6 */ Vec3s morphTable[9]; + /* 0x01FC */ EnMsActionFunc actionFunc; + /* 0x0200 */ ColliderCylinder collider; + /* 0x024C */ s16 activeTimer; +} EnMs; // size = 0x0250 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Mu/z_en_mu.c b/soh/src/overlays/actors/ovl_En_Mu/z_en_mu.c new file mode 100644 index 000000000..0259d20fb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mu/z_en_mu.c @@ -0,0 +1,222 @@ +/* + * File: z_en_mu.c + * Overlay: ovl_En_Mu + * Description: Haggling townspeople + */ + +#include "z_en_mu.h" +#include "objects/object_mu/object_mu.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnMu_Init(Actor* thisx, GlobalContext* globalCtx); +void EnMu_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnMu_Update(Actor* thisx, GlobalContext* globalCtx); +void EnMu_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnMu_Pose(EnMu* this, GlobalContext* globalCtx); +s16 EnMu_CheckDialogState(GlobalContext* globalCtx, Actor* thisx); + +static ColliderCylinderInit D_80AB0BD0 = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 100, 70, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 D_80AB0BFC = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +const ActorInit En_Mu_InitVars = { + ACTOR_EN_MU, + ACTORCAT_NPC, + FLAGS, + OBJECT_MU, + sizeof(EnMu), + (ActorFunc)EnMu_Init, + (ActorFunc)EnMu_Destroy, + (ActorFunc)EnMu_Update, + (ActorFunc)EnMu_Draw, + NULL, +}; + +void EnMu_SetupAction(EnMu* this, EnMuActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnMu_Interact(EnMu* this, GlobalContext* globalCtx) { + u8 textIdOffset[] = { 0x42, 0x43, 0x3F, 0x41, 0x3E }; + u8 bitmask[] = { 0x01, 0x02, 0x04, 0x08, 0x10 }; + u8 textFlags; + s32 randomIndex; + s32 i; + + textFlags = gSaveContext.eventInf[2] & 0x1F; + gSaveContext.eventInf[2] &= ~0x1F; + randomIndex = (globalCtx->state.frames + (s32)(Rand_ZeroOne() * 5.0f)) % 5; + + for (i = 0; i < 5; i++) { + + if (!(textFlags & bitmask[randomIndex])) { + break; + } + + randomIndex++; + if (randomIndex >= 5) { + randomIndex = 0; + } + } + + if (i == 5) { + if (this->defFaceReaction == (textIdOffset[randomIndex] | 0x7000)) { + randomIndex++; + if (randomIndex >= 5) { + randomIndex = 0; + } + } + textFlags = 0; + } + + textFlags |= bitmask[randomIndex]; + this->defFaceReaction = textIdOffset[randomIndex] | 0x7000; + textFlags &= 0xFF; + gSaveContext.eventInf[2] |= textFlags; +} + +u16 EnMu_GetFaceReaction(GlobalContext* globalCtx, Actor* thisx) { + EnMu* this = (EnMu*)thisx; + u16 faceReaction = Text_GetFaceReaction(globalCtx, this->actor.params + 0x3A); + + if (faceReaction != 0) { + return faceReaction; + } + return this->defFaceReaction; +} + +s16 EnMu_CheckDialogState(GlobalContext* globalCtx, Actor* thisx) { + EnMu* this = (EnMu*)thisx; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + return 1; + case TEXT_STATE_CLOSING: + EnMu_Interact(this, globalCtx); + return 0; + default: + return 1; + } +} + +void EnMu_Init(Actor* thisx, GlobalContext* globalCtx) { + EnMu* this = (EnMu*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 160.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_mu_Skel_004F70, &object_mu_Anim_0003F4, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &D_80AB0BD0); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &D_80AB0BFC); + this->actor.targetMode = 6; + Actor_SetScale(&this->actor, 0.01f); + EnMu_Interact(this, globalCtx); + EnMu_SetupAction(this, EnMu_Pose); +} + +void EnMu_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnMu* this = (EnMu*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void EnMu_Pose(EnMu* this, GlobalContext* globalCtx) { + func_80034F54(globalCtx, this->unk_20A, this->unk_22A, 16); +} + +void EnMu_Update(Actor* thisx, GlobalContext* globalCtx) { + EnMu* this = (EnMu*)thisx; + s32 pad; + f32 talkDist; + Vec3s pos; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z; + + this->collider.dim.pos = pos; + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actionFunc(this, globalCtx); + talkDist = this->collider.dim.radius + 30.0f; + func_800343CC(globalCtx, &this->actor, &this->npcInfo.unk_00, talkDist, EnMu_GetFaceReaction, + EnMu_CheckDialogState); + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 60.0f; +} + +s32 EnMu_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnMu* this = (EnMu*)thisx; + + if ((limbIndex == 5) || (limbIndex == 6) || (limbIndex == 7) || (limbIndex == 11) || (limbIndex == 12) || + (limbIndex == 13) || (limbIndex == 14)) { + rot->y += Math_SinS(this->unk_20A[limbIndex]) * 200.0f; + rot->z += Math_CosS(this->unk_22A[limbIndex]) * 200.0f; + } + return false; +} + +void EnMu_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { +} + +Gfx* EnMu_DisplayListSetColor(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b, u8 a) { + Gfx* dlist; + + dlist = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + gDPSetEnvColor(dlist, r, g, b, a); + gSPEndDisplayList(dlist + 1); + return dlist; +} + +void EnMu_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnMu* this = (EnMu*)thisx; + Color_RGBA8 colors[2][5] = { + { { 100, 130, 235, 0 }, { 160, 250, 60, 0 }, { 90, 60, 20, 0 }, { 30, 240, 200, 0 }, { 140, 70, 20, 0 } }, + { { 140, 70, 20, 0 }, { 30, 240, 200, 0 }, { 90, 60, 20, 0 }, { 160, 250, 60, 0 }, { 100, 130, 235, 0 } } + }; + u8 segmentId[] = { 0x08, 0x09, 0x0A, 0x0B, 0x0C }; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_mu.c", 514); + Matrix_Translate(-1200.0f, 0.0f, -1400.0f, MTXMODE_APPLY); + for (i = 0; i < 5; i++) { + gSPSegment(POLY_OPA_DISP++, segmentId[i], + EnMu_DisplayListSetColor(globalCtx->state.gfxCtx, colors[this->actor.params][i].r, + colors[this->actor.params][i].g, colors[this->actor.params][i].b, + colors[this->actor.params][i].a)); + } + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnMu_OverrideLimbDraw, EnMu_PostLimbDraw, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_mu.c", 534); +} diff --git a/soh/src/overlays/actors/ovl_En_Mu/z_en_mu.h b/soh/src/overlays/actors/ovl_En_Mu/z_en_mu.h new file mode 100644 index 000000000..089045ebf --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Mu/z_en_mu.h @@ -0,0 +1,22 @@ +#ifndef Z_EN_MU_H +#define Z_EN_MU_H + +#include "ultra64.h" +#include "global.h" + +struct EnMu; + +typedef void (*EnMuActionFunc)(struct EnMu*, struct GlobalContext*); + +typedef struct EnMu { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnMuActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 npcInfo; + /* 0x0208 */ u16 defFaceReaction; + /* 0x020A */ s16 unk_20A[16]; + /* 0x022A */ s16 unk_22A[17]; +} EnMu; // size = 0x024C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c new file mode 100644 index 000000000..bea6994a5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.c @@ -0,0 +1,1543 @@ +/* + * File: z_en_nb.c + * Overlay: ovl_En_Nb + * Description: Nabooru + */ + +#include "z_en_nb.h" +#include "vt.h" +#include "objects/object_nb/object_nb.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0x00 */ NB_CHAMBER_INIT, + /* 0x01 */ NB_CHAMBER_UNDERGROUND, + /* 0x02 */ NB_CHAMBER_APPEAR, + /* 0x03 */ NB_CHAMBER_IDLE, + /* 0x04 */ NB_CHAMBER_RAISE_ARM, + /* 0x05 */ NB_CHAMBER_RAISE_ARM_TRANSITION, + /* 0x06 */ NB_GIVE_MEDALLION, + /* 0x07 */ NB_ACTION_7, + /* 0x08 */ NB_SEAL_HIDE, + /* 0x09 */ NB_ACTION_9, + /* 0x0A */ NB_KIDNAPPED, + /* 0x0B */ NB_KIDNAPPED_LOOK_AROUND, + /* 0x0C */ NB_PORTAL_FALLTHROUGH, + /* 0x0D */ NB_IN_CONFRONTATION, + /* 0x0E */ NB_ACTION_14, + /* 0x0F */ NB_KNEEL, + /* 0x10 */ NB_LOOK_RIGHT, + /* 0x11 */ NB_LOOK_LEFT, + /* 0x12 */ NB_RUN, + /* 0x13 */ NB_CONFRONTATION_DESTROYED, + /* 0x14 */ NB_CREDITS_INIT, + /* 0x15 */ NB_CREDITS_FADEIN, + /* 0x16 */ NB_CREDITS_SIT, + /* 0x17 */ NB_CREDITS_HEAD_TURN, + /* 0x18 */ NB_CROUCH_CRAWLSPACE, + /* 0x19 */ NB_NOTICE_PLAYER, + /* 0x1A */ NB_IDLE_CRAWLSPACE, + /* 0x1B */ NB_IN_DIALOG, + /* 0x1C */ NB_IN_PATH, + /* 0x1D */ NB_IDLE_AFTER_TALK, + /* 0x1E */ NB_ACTION_30 +} EnNbAction; + +typedef enum { + /* 0x00 */ NB_DRAW_NOTHING, + /* 0x01 */ NB_DRAW_DEFAULT, + /* 0x02 */ NB_DRAW_HIDE, + /* 0x03 */ NB_DRAW_KNEEL, + /* 0x04 */ NB_DRAW_LOOK_DIRECTION +} EnNbDrawMode; + +void EnNb_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNb_Update(Actor* thisx, GlobalContext* globalCtx); +void EnNb_Draw(Actor* thisx, GlobalContext* globalCtx); + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 25, 80, 0, { 0, 0, 0 } }, +}; + +static void* sEyeTextures[] = { + gNabooruEyeOpenTex, + gNabooruEyeHalfTex, + gNabooruEyeClosedTex, +}; + +static s32 D_80AB4318 = 0; + +#include "z_en_nb_cutscene_data.c" EARLY + +s32 EnNb_GetPath(EnNb* this) { + s32 path = this->actor.params >> 8; + + return path & 0xFF; +} + +s32 EnNb_GetType(EnNb* this) { + s32 type = this->actor.params; + + return type & 0xFF; +} + +void EnNb_UpdatePath(EnNb* this, GlobalContext* globalCtx) { + Vec3s* pointPos; + Path* pathList; + s32 pad; + s32 path; + + pathList = globalCtx->setupPathList; + + if (pathList != NULL) { + path = EnNb_GetPath(this); + pathList += path; + pointPos = SEGMENTED_TO_VIRTUAL(pathList->points); + this->initialPos.x = pointPos[0].x; + this->initialPos.y = pointPos[0].y; + this->initialPos.z = pointPos[0].z; + this->finalPos.x = pointPos[1].x; + this->finalPos.y = pointPos[1].y; + this->finalPos.z = pointPos[1].z; + this->pathYaw = (Math_FAtan2F(this->finalPos.x - this->initialPos.x, this->finalPos.z - this->initialPos.z) * + (0x8000 / M_PI)); + // "En_Nb_Get_path_info Rail Data Get! = %d!!!!!!!!!!!!!!" + osSyncPrintf("En_Nb_Get_path_info レールデータをゲットだぜ = %d!!!!!!!!!!!!!!\n", path); + } else { + // "En_Nb_Get_path_info Rail Data Doesn't Exist!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf("En_Nb_Get_path_info レールデータが無い!!!!!!!!!!!!!!!!!!!!\n"); + } +} + +void EnNb_SetupCollider(Actor* thisx, GlobalContext* globalCtx) { + EnNb* this = (EnNb*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, thisx, &sCylinderInit); +} + +void EnNb_UpdateCollider(EnNb* this, GlobalContext* globalCtx) { + s32 pad[4]; + ColliderCylinder* collider = &this->collider; + + Collider_UpdateCylinder(&this->actor, collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &collider->base); +} + +void EnNb_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnNb* this = (EnNb*)thisx; + + D_80AB4318 = 0; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AB0FBC(EnNb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_300.unk_18 = player->actor.world.pos; + this->unk_300.unk_14 = kREG(16) + 9.0f; + func_80034A14(&this->actor, &this->unk_300, kREG(17) + 0xC, 2); +} + +void func_80AB1040(EnNb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_300.unk_18 = player->actor.world.pos; + this->unk_300.unk_14 = kREG(16) + 9.0f; + func_80034A14(&this->actor, &this->unk_300, kREG(17) + 0xC, 4); +} + +void func_80AB10C4(EnNb* this) { + s32 pad2[2]; + Vec3s* tempPtr; + Vec3s* tempPtr2; + + tempPtr = &this->unk_300.unk_08; + Math_SmoothStepToS(&tempPtr->x, 0, 20, 6200, 100); + Math_SmoothStepToS(&tempPtr->y, 0, 20, 6200, 100); + tempPtr2 = &this->unk_300.unk_0E; + Math_SmoothStepToS(&tempPtr2->x, 0, 20, 6200, 100); + Math_SmoothStepToS(&tempPtr2->y, 0, 20, 6200, 100); +} + +void EnNb_UpdateEyes(EnNb* this) { + s32 pad[3]; + s16* blinkTimer = &this->blinkTimer; + s16* eyeIdx = &this->eyeIdx; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + + *eyeIdx = *blinkTimer; + if (*eyeIdx >= ARRAY_COUNT(sEyeTextures)) { + *eyeIdx = 0; + } +} + +void func_80AB11EC(EnNb* this) { + this->action = NB_ACTION_7; + this->drawMode = NB_DRAW_NOTHING; + this->alpha = 0; + this->flag = 0; + this->actor.shape.shadowAlpha = 0; + this->alphaTimer = 0.0f; +} + +void func_80AB1210(EnNb* this, GlobalContext* globalCtx) { + s32 one; // required to match + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + if (D_80AB4318) { + if (this->actor.params == NB_TYPE_DEMO02) { + func_80AB11EC(this); + } + + D_80AB4318 = 0; + } + } else { + one = 1; + if (!D_80AB4318) { + D_80AB4318 = one; + } + } +} + +void func_80AB1284(EnNb* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 4); +} + +s32 EnNb_UpdateSkelAnime(EnNb* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* EnNb_GetNpcCsAction(GlobalContext* globalCtx, s32 npcActionIdx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[npcActionIdx]; + } + return NULL; +} + +void EnNb_SetupCsPosRot(EnNb* this, GlobalContext* globalCtx, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = EnNb_GetNpcCsAction(globalCtx, npcActionIdx); + s16 newRotY; + Actor* thisx = &this->actor; + + if (csCmdNPCAction != NULL) { + thisx->world.pos.x = csCmdNPCAction->startPos.x; + thisx->world.pos.y = csCmdNPCAction->startPos.y; + thisx->world.pos.z = csCmdNPCAction->startPos.z; + thisx->world.rot.y = thisx->shape.rot.y = csCmdNPCAction->rot.y; + } +} + +s32 func_80AB1390(EnNb* this, GlobalContext* globalCtx, u16 arg2, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && + (csCmdNPCAction = globalCtx->csCtx.npcActions[npcActionIdx], csCmdNPCAction != NULL) && + (csCmdNPCAction->action == arg2)) { + return true; + } + return false; +} + +s32 func_80AB13D8(EnNb* this, GlobalContext* globalCtx, u16 arg2, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction; + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && + (csCmdNPCAction = globalCtx->csCtx.npcActions[npcActionIdx], csCmdNPCAction != NULL) && + (csCmdNPCAction->action != arg2)) { + return true; + } + return false; +} + +void EnNb_SetInitialCsPosRot(EnNb* this, GlobalContext* globalCtx, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = EnNb_GetNpcCsAction(globalCtx, npcActionIdx); + Actor* thisx = &this->actor; + + if (csCmdNPCAction != NULL) { + thisx->world.pos.x = csCmdNPCAction->startPos.x; + thisx->world.pos.y = csCmdNPCAction->startPos.y; + thisx->world.pos.z = csCmdNPCAction->startPos.z; + thisx->world.rot.y = thisx->shape.rot.y = csCmdNPCAction->rot.y; + } +} + +void EnNb_SetCurrentAnim(EnNb* this, AnimationHeader* animation, u8 mode, f32 transitionRate, s32 arg4) { + f32 frameCount = Animation_GetLastFrame(animation); + f32 playbackSpeed; + f32 unk0; + f32 fc; + + if (!arg4) { + unk0 = 0.0f; + fc = frameCount; + playbackSpeed = 1.0f; + } else { + unk0 = frameCount; + fc = 0.0f; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animation, playbackSpeed, unk0, fc, mode, transitionRate); +} + +void EnNb_SetChamberAnim(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetCurrentAnim(this, &gNabooruStandingHandsOnHipsChamberOfSagesAnim, 0, 0, 0); + this->actor.shape.yOffset = -10000.0f; +} + +void EnNb_SpawnBlueWarp(EnNb* this, GlobalContext* globalCtx) { + f32 posX = this->actor.world.pos.x; + f32 posY = this->actor.world.pos.y; + f32 posZ = this->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, + WARP_SAGES); +} + +void EnNb_GiveMedallion(EnNb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 posX = player->actor.world.pos.x; + f32 posY = player->actor.world.pos.y + 50.0f; + f32 posZ = player->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, + 0xC); + Item_Give(globalCtx, ITEM_MEDALLION_SPIRIT); +} + +void EnNb_ComeUpImpl(EnNb* this, GlobalContext* globalCtx) { + this->actor.shape.yOffset += 250.0f / 3.0f; +} + +void EnNb_SetupChamberCsImpl(EnNb* this, GlobalContext* globalCtx) { + s32 pad[2]; + Player* player; + + if ((gSaveContext.chamberCutsceneNum == 3) && (gSaveContext.sceneSetupIndex < 4)) { + player = GET_PLAYER(globalCtx); + this->action = NB_CHAMBER_UNDERGROUND; + globalCtx->csCtx.segment = &D_80AB431C; + gSaveContext.cutsceneTrigger = 2; + Item_Give(globalCtx, ITEM_MEDALLION_SPIRIT); + player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + } +} + +void EnNb_SetupChamberWarpImpl(EnNb* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + CsCmdActorAction* csCmdNPCAction; + + if (csCtx->state != CS_STATE_IDLE) { + csCmdNPCAction = csCtx->npcActions[1]; + if (csCmdNPCAction != NULL && csCmdNPCAction->action == 2) { + this->action = NB_CHAMBER_APPEAR; + this->drawMode = NB_DRAW_DEFAULT; + EnNb_SpawnBlueWarp(this, globalCtx); + } + } +} + +void EnNb_SetupDefaultChamberIdle(EnNb* this) { + if (this->actor.shape.yOffset >= 0.0f) { + this->action = NB_CHAMBER_IDLE; + this->actor.shape.yOffset = 0.0f; + } +} + +void EnNb_SetupArmRaise(EnNb* this, GlobalContext* globalCtx) { + AnimationHeader* animation = &gNabooruRaisingArmsGivingMedallionAnim; + CsCmdActorAction* csCmdNPCAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdNPCAction = globalCtx->csCtx.npcActions[1]; + if (csCmdNPCAction != NULL && csCmdNPCAction->action == 3) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, + 0.0f); + this->action = NB_CHAMBER_RAISE_ARM; + } + } +} + +void EnNb_SetupRaisedArmTransition(EnNb* this, s32 animFinished) { + AnimationHeader* animation = &gNabooruArmsRaisedGivingMedallionAnim; + + if (animFinished) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + 0.0f); + this->action = NB_CHAMBER_RAISE_ARM_TRANSITION; + } +} + +void EnNb_SetupMedallion(EnNb* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdNPCAction = globalCtx->csCtx.npcActions[6]; + if (csCmdNPCAction != NULL && csCmdNPCAction->action == 2) { + this->action = NB_GIVE_MEDALLION; + EnNb_GiveMedallion(this, globalCtx); + } + } +} + +// Action func is never explicitly set to this, but it runs when the memory gets zero cleared +void EnNb_SetupChamberCs(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetupChamberCsImpl(this, globalCtx); +} + +void EnNb_SetupChamberWarp(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetupChamberWarpImpl(this, globalCtx); +} + +void EnNb_ComeUp(EnNb* this, GlobalContext* globalCtx) { + EnNb_ComeUpImpl(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetupDefaultChamberIdle(this); +} + +void func_80AB193C(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetupArmRaise(this, globalCtx); +} + +void EnNb_RaiseArm(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + func_80AB1284(this, globalCtx); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetupRaisedArmTransition(this, animFinished); +} + +void func_80AB19BC(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetupMedallion(this, globalCtx); +} + +void func_80AB19FC(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); +} + +void EnNb_SetupLightArrowOrSealingCs(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetCurrentAnim(this, &gNabooruPuttingHandsTogetherCastingMagicAnim, 2, 0.0f, 0); + this->action = NB_ACTION_7; + this->actor.shape.shadowAlpha = 0; +} + +void EnNb_PlaySealingSound(void) { + func_800788CC(NA_SE_SY_WHITE_OUT_T); +} + +void EnNb_InitializeDemo6K(EnNb* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + kREG(21) + 22.0f + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 7); +} + +void EnNb_SetupHide(EnNb* this, GlobalContext* globalCtx) { + if (func_80AB1390(this, globalCtx, 4, 1)) { + this->action = NB_SEAL_HIDE; + this->drawMode = NB_DRAW_HIDE; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + this->alphaTimer = 0.0f; + EnNb_PlaySealingSound(); + } +} + +void EnNb_CheckToFade(EnNb* this, GlobalContext* globalCtx) { + f32* alphaTimer = &this->alphaTimer; + s32 alpha; + + if (func_80AB1390(this, globalCtx, 4, 1)) { + *alphaTimer += 1.0f; + if (*alphaTimer >= kREG(5) + 10.0f) { + this->action = NB_ACTION_9; + this->drawMode = NB_DRAW_DEFAULT; + *alphaTimer = kREG(5) + 10.0f; + this->alpha = 255; + this->actor.shape.shadowAlpha = 0xFF; + return; + } + } else { + *alphaTimer -= 1.0f; + if (*alphaTimer <= 0.0f) { + this->action = NB_ACTION_7; + this->drawMode = NB_DRAW_NOTHING; + *alphaTimer = 0.0f; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + return; + } + } + + alpha = (*alphaTimer / (kREG(5) + 10.0f)) * 255.0f; + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; +} + +void EnNb_SetupLightOrb(EnNb* this, GlobalContext* globalCtx) { + if (func_80AB13D8(this, globalCtx, 4, 1)) { + this->action = NB_SEAL_HIDE; + this->drawMode = NB_DRAW_HIDE; + this->alphaTimer = kREG(5) + 10.0f; + this->alpha = 255; + + if (this->flag == 0) { + EnNb_InitializeDemo6K(this, globalCtx); + this->flag = 1; + } + + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void EnNb_Hide(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetupHide(this, globalCtx); + func_80AB1210(this, globalCtx); +} + +void EnNb_Fade(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_CheckToFade(this, globalCtx); + func_80AB1210(this, globalCtx); +} + +void EnNb_CreateLightOrb(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetupLightOrb(this, globalCtx); + func_80AB1210(this, globalCtx); +} + +void EnNb_DrawTransparency(EnNb* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeSegIdx = this->eyeIdx; + void* eyeTex = sEyeTextures[eyeSegIdx]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_nb_inKenjyanomaDemo02.c", 263); + + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, &D_80116280[0]); + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + NULL, NULL, NULL, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_nb_inKenjyanomaDemo02.c", 290); +} + +void EnNb_InitKidnap(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetCurrentAnim(this, &gNabooruTrappedInVortexPushingGroundAnim, 0, 0.0f, 0); + this->action = NB_KIDNAPPED; + this->actor.shape.shadowAlpha = 0; + gSaveContext.eventChkInf[9] |= 0x20; +} + +void EnNb_PlayCrySFX(EnNb* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 3) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_NB_CRY_0); + } +} + +void EnNb_PlayAgonySFX(EnNb* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 420) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_NB_AGONY); + } +} + +void EnNb_SetPosInPortal(EnNb* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction = EnNb_GetNpcCsAction(globalCtx, 1); + Vec3f* pos = &this->actor.world.pos; + f32 f0; + s32 pad; + Vec3f startPos; + Vec3f endPos; + + if (csCmdNPCAction != NULL) { + f0 = Environment_LerpWeightAccelDecel(csCmdNPCAction->endFrame, csCmdNPCAction->startFrame, + globalCtx->csCtx.frames, 4, 4); + startPos.x = csCmdNPCAction->startPos.x; + startPos.y = csCmdNPCAction->startPos.y; + startPos.z = csCmdNPCAction->startPos.z; + endPos.x = csCmdNPCAction->endPos.x; + endPos.y = csCmdNPCAction->endPos.y; + endPos.z = csCmdNPCAction->endPos.z; + pos->x = ((endPos.x - startPos.x) * f0) + startPos.x; + pos->y = ((endPos.y - startPos.y) * f0) + startPos.y; + pos->z = ((endPos.z - startPos.z) * f0) + startPos.z; + } +} + +void EnNb_SetupCaptureCutsceneState(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetupCsPosRot(this, globalCtx, 1); + this->action = NB_KIDNAPPED; + this->drawMode = NB_DRAW_NOTHING; + this->actor.shape.shadowAlpha = 0; +} + +void EnNb_SetRaisedArmCaptureAnim(EnNb* this, s32 animFinished) { + AnimationHeader* animation = &gNabooruSuckedByVortexAnim; + + if (animFinished) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + 0.0f); + } +} + +void EnNb_SetupLookAroundInKidnap(EnNb* this) { + AnimationHeader* animation = &gNabooruTrappedInVortexPushingGroundAnim; + + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, -8.0f); + this->action = NB_KIDNAPPED_LOOK_AROUND; + this->drawMode = NB_DRAW_DEFAULT; +} + +void EnNb_SetupKidnap(EnNb* this) { + AnimationHeader* animation = &gNabooruTrappedInVortexRaisingArmAnim; + + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, -8.0f); + this->action = NB_PORTAL_FALLTHROUGH; + this->drawMode = NB_DRAW_DEFAULT; +} + +void EnNb_CheckKidnapCsMode(EnNb* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction = EnNb_GetNpcCsAction(globalCtx, 1); + s32 action; + s32 previousCsAction; + + if (csCmdNPCAction != NULL) { + action = csCmdNPCAction->action; + previousCsAction = this->previousCsAction; + if (action != previousCsAction) { + switch (action) { + case 1: + EnNb_SetupCaptureCutsceneState(this, globalCtx); + break; + case 7: + EnNb_SetupLookAroundInKidnap(this); + break; + case 8: + EnNb_SetupKidnap(this); + break; + case 9: + Actor_Kill(&this->actor); + break; + default: + // "Operation Doesn't Exist!!!!!!!!" + osSyncPrintf("En_Nb_Kidnap_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->previousCsAction = action; + } + } +} + +void func_80AB23A8(EnNb* this, GlobalContext* globalCtx) { + EnNb_PlayCrySFX(this, globalCtx); + EnNb_CheckKidnapCsMode(this, globalCtx); +} + +void EnNb_MovingInPortal(EnNb* this, GlobalContext* globalCtx) { + EnNb_PlayCrySFX(this, globalCtx); + EnNb_PlayAgonySFX(this, globalCtx); + EnNb_UpdateEyes(this); + EnNb_UpdateSkelAnime(this); + EnNb_CheckKidnapCsMode(this, globalCtx); +} + +void EnNb_SuckedInByPortal(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + EnNb_UpdateEyes(this); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_SetRaisedArmCaptureAnim(this, animFinished); + EnNb_SetPosInPortal(this, globalCtx); + EnNb_CheckKidnapCsMode(this, globalCtx); +} + +void EnNb_SetupConfrontation(EnNb* this, GlobalContext* globalCtx) { + AnimationHeader* animation = &gNabooruCollapseFromStandingToKneelingTransitionAnim; + + EnNb_SetCurrentAnim(this, animation, 0, 0.0f, 0); + this->action = NB_IN_CONFRONTATION; + this->actor.shape.shadowAlpha = 0; +} + +void EnNb_PlayKnuckleDefeatSFX(EnNb* this, GlobalContext* globalCtx) { + s32 pad[2]; + + if (globalCtx->csCtx.frames == 548) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_NB_CRY_0); + func_80078914(&this->actor.projectedPos, NA_SE_EN_FANTOM_HIT_THUNDER); + } +} + +void EnNb_PlayKneelingOnGroundSFX(EnNb* this) { + s32 pad[2]; + + if ((this->skelAnime.mode == 2) && + (Animation_OnFrame(&this->skelAnime, 18.0f) || Animation_OnFrame(&this->skelAnime, 25.0f))) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_HUMAN_BOUND); + } +} + +void EnNb_PlayLookRightSFX(EnNb* this) { + s32 pad[2]; + + if ((this->skelAnime.mode == 2) && Animation_OnFrame(&this->skelAnime, 9.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_CONCRETE); + } +} + +void EnNb_PlayLookLeftSFX(EnNb* this) { + s32 pad[2]; + + if (Animation_OnFrame(&this->skelAnime, 9.0f) || Animation_OnFrame(&this->skelAnime, 13.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_CONCRETE); + } +} + +void EnNb_InitDemo6KInConfrontation(EnNb* this, GlobalContext* globalCtx) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + kREG(21) + 22.0f + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0xB); +} + +void func_80AB2688(EnNb* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 1; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_80AB26C8(EnNb* this) { + this->action = NB_IN_CONFRONTATION; + this->drawMode = NB_DRAW_NOTHING; + this->actor.shape.shadowAlpha = 0; +} + +void func_80AB26DC(EnNb* this, GlobalContext* globalCtx) { + s32 pad; + AnimationHeader* animation = &gNabooruCollapseFromStandingToKneelingTransitionAnim; + f32 lastFrame = Animation_GetLastFrame(animation); + + EnNb_SetupCsPosRot(this, globalCtx, 1); + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + this->action = NB_ACTION_14; + this->drawMode = NB_DRAW_KNEEL; + this->actor.shape.shadowAlpha = 0xFF; +} + +void EnNb_SetupKneel(EnNb* this) { + AnimationHeader* animation = &gNabooruCollapseFromStandingToKneelingTransitionAnim; + f32 lastFrame = Animation_GetLastFrame(animation); + + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + this->action = NB_KNEEL; + this->drawMode = NB_DRAW_KNEEL; + this->actor.shape.shadowAlpha = 0xFF; +} + +void EnNb_CheckIfKneeling(EnNb* this, s32 animFinished) { + AnimationHeader* animation = &gNabooruOnAllFoursAnim; + + if (animFinished) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + 0.0f); + this->drawMode = NB_DRAW_KNEEL; + } +} + +void EnNb_SetupLookRight(EnNb* this) { + AnimationHeader* animation = &gNabooruOnAllFoursToOnOneKneeLookingRightTransitionAnim; + f32 lastFrame = Animation_GetLastFrame(animation); + + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, -8.0f); + this->action = NB_LOOK_RIGHT; + this->drawMode = NB_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 0xFF; +} + +void EnNb_CheckIfLookingRight(EnNb* this, s32 animFinished) { + AnimationHeader* animation = &gNabooruOnOneKneeLookingRightAnim; + + if (animFinished) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + 0.0f); + this->drawMode = NB_DRAW_LOOK_DIRECTION; + } +} + +void EnNb_SetupLookLeft(EnNb* this) { + AnimationHeader* animation = &gNabooruOnOneKneeTurningHeadRightToLeftTransitionAnim; + f32 lastFrame = Animation_GetLastFrame(animation); + + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, -8.0f); + this->action = NB_LOOK_LEFT; + this->drawMode = NB_DRAW_LOOK_DIRECTION; + this->actor.shape.shadowAlpha = 0xFF; +} + +void EnNb_CheckIfLookLeft(EnNb* this, s32 animFinished) { + AnimationHeader* animation = &gNabooruOnOneKneeLookingLeftAnim; + + if (animFinished) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_LOOP, + 0.0f); + } +} + +void EnNb_SetupDemo6KInConfrontation(EnNb* this, GlobalContext* globalCtx, s32 animFinished) { + if (!this->flag && animFinished) { + EnNb_InitDemo6KInConfrontation(this, globalCtx); + this->flag = 1; + } +} + +void EnNb_SetupRun(EnNb* this) { + AnimationHeader* animation = &gNabooruKneeingToRunningToHitAnim; + f32 lastFrame = Animation_GetLastFrame(animation); + + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, -8.0f); + this->action = NB_RUN; + this->drawMode = NB_DRAW_LOOK_DIRECTION; + this->actor.shape.shadowAlpha = 0xFF; +} + +void EnNb_SetupConfrontationDestroy(EnNb* this) { + this->action = NB_CONFRONTATION_DESTROYED; + this->drawMode = NB_DRAW_NOTHING; + this->actor.shape.shadowAlpha = 0; +} + +void EnNb_CheckConfrontationCsMode(EnNb* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction; + s32 csAction; + s32 previousCsAction; + + csCmdNPCAction = EnNb_GetNpcCsAction(globalCtx, 1); + if (csCmdNPCAction != NULL) { + csAction = csCmdNPCAction->action; + previousCsAction = this->previousCsAction; + + if (csAction != previousCsAction) { + switch (csAction) { + case 1: + func_80AB26C8(this); + break; + case 10: + func_80AB26DC(this, globalCtx); + break; + case 11: + EnNb_SetupKneel(this); + break; + case 12: + EnNb_SetupLookRight(this); + break; + case 13: + EnNb_SetupLookLeft(this); + break; + case 14: + EnNb_SetupRun(this); + break; + case 9: + EnNb_SetupConfrontationDestroy(this); + break; + default: + // "En_Nb_Confrontion_Check_DemoMode: Operation doesn't exist!!!!!!!!" + osSyncPrintf("En_Nb_Confrontion_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->previousCsAction = csAction; + } + } +} + +void EnNb_CheckConfrontationCsModeWrapper(EnNb* this, GlobalContext* globalCtx) { + EnNb_CheckConfrontationCsMode(this, globalCtx); +} + +void func_80AB2C18(EnNb* this, GlobalContext* globalCtx) { + EnNb_UpdateEyes(this); + func_80AB2688(this, globalCtx); + func_80AB1284(this, globalCtx); + EnNb_CheckConfrontationCsMode(this, globalCtx); +} + +void EnNb_Kneel(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + EnNb_UpdateEyes(this); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_CheckIfKneeling(this, animFinished); + EnNb_PlayKneelingOnGroundSFX(this); + func_80AB2688(this, globalCtx); + func_80AB1284(this, globalCtx); + EnNb_CheckConfrontationCsMode(this, globalCtx); +} + +void EnNb_LookRight(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + EnNb_UpdateEyes(this); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_CheckIfLookingRight(this, animFinished); + EnNb_PlayLookRightSFX(this); + func_80AB2688(this, globalCtx); + func_80AB1284(this, globalCtx); + EnNb_CheckConfrontationCsMode(this, globalCtx); +} + +void EnNb_LookLeft(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + EnNb_UpdateEyes(this); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_CheckIfLookLeft(this, animFinished); + func_80AB2688(this, globalCtx); + func_80AB1284(this, globalCtx); + EnNb_CheckConfrontationCsMode(this, globalCtx); +} + +void EnNb_Run(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + EnNb_PlayKnuckleDefeatSFX(this, globalCtx); + EnNb_UpdateEyes(this); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_PlayLookLeftSFX(this); + func_80AB2688(this, globalCtx); + func_80AB1284(this, globalCtx); + EnNb_SetupDemo6KInConfrontation(this, globalCtx, animFinished); + EnNb_CheckConfrontationCsMode(this, globalCtx); +} + +void EnNb_ConfrontationDestroy(EnNb* this, GlobalContext* globalCtx) { + this->timer++; + + if (this->timer > 60.0f) { + Actor_Kill(&this->actor); + } +} + +void func_80AB2E70(EnNb* this, GlobalContext* globalCtx) { + s32 pad; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_nb_inConfrontion.c", 572); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gNabooruEyeWideTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gNabooruEyeWideTex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, NULL, NULL, + &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_nb_inConfrontion.c", 593); +} + +s32 func_80AB2FC0(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnNb* this = (EnNb*)thisx; + + if (limbIndex == NB_LIMB_HEAD) { + *dList = gNabooruHeadMouthOpenDL; + } + + return 0; +} + +void func_80AB2FE4(EnNb* this, GlobalContext* globalCtx) { + s32 pad; + s16 eyeIdx = this->eyeIdx; + SkelAnime* skelAnime = &this->skelAnime; + void* eyeTexture = sEyeTextures[eyeIdx]; + s32 pad1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_nb_inConfrontion.c", 623); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, func_80AB2FC0, + NULL, &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_nb_inConfrontion.c", 644); +} + +void EnNb_SetupCreditsSpawn(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetCurrentAnim(this, &gNabooruSittingCrossLeggedAnim, 0, 0.0f, 0); + this->action = NB_CREDITS_INIT; + this->drawMode = NB_DRAW_NOTHING; + this->actor.shape.shadowAlpha = 0; +} + +void EnNb_SetAlphaInCredits(EnNb* this) { + f32* alphaTimer = &this->alphaTimer; + s32 alpha; + + this->alphaTimer++; + + if ((kREG(17) + 10.0f) <= this->alphaTimer) { + this->alpha = 255; + this->actor.shape.shadowAlpha = 255; + } else { + alpha = (*alphaTimer / (kREG(17) + 10.0f)) * 255.0f; + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; + } +} + +void EnNb_SetupCreditsFadeIn(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetInitialCsPosRot(this, globalCtx, 1); + this->action = NB_CREDITS_FADEIN; + this->drawMode = NB_DRAW_HIDE; +} + +void EnNb_SetupCreditsSit(EnNb* this) { + if (this->alphaTimer >= kREG(17) + 10.0f) { + this->action = NB_CREDITS_SIT; + this->drawMode = NB_DRAW_DEFAULT; + } +} + +void EnNb_SetupCreditsHeadTurn(EnNb* this) { + EnNb_SetCurrentAnim(this, &gNabooruSittingCrossLeggedTurningToLookUpRightTransitionAnim, 2, -8.0f, 0); + this->action = NB_CREDITS_HEAD_TURN; +} + +void EnNb_CheckIfLookingUp(EnNb* this, s32 animFinished) { + if (animFinished) { + EnNb_SetCurrentAnim(this, &gNabooruSittingCrossLeggedLookingUpRightAnim, 0, 0.0f, 0); + } +} + +void EnNb_CheckCreditsCsModeImpl(EnNb* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction = EnNb_GetNpcCsAction(globalCtx, 1); + s32 action; + s32 previousCsAction; + + if (csCmdNPCAction != NULL) { + action = csCmdNPCAction->action; + previousCsAction = this->previousCsAction; + if (action != previousCsAction) { + switch (action) { + case 15: + EnNb_SetupCreditsFadeIn(this, globalCtx); + break; + case 16: + EnNb_SetupCreditsHeadTurn(this); + break; + default: + // "En_Nb_inEnding_Check_DemoMode: Operation doesn't exist!!!!!!!!" + osSyncPrintf("En_Nb_inEnding_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->previousCsAction = action; + } + } +} + +void EnNb_CheckCreditsCsMode(EnNb* this, GlobalContext* globalCtx) { + EnNb_CheckCreditsCsModeImpl(this, globalCtx); +} + +void EnNb_CreditsFade(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetAlphaInCredits(this); + EnNb_SetupCreditsSit(this); +} + +void func_80AB3428(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_CheckCreditsCsModeImpl(this, globalCtx); +} + +void EnNb_LookUp(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + func_80AB1284(this, globalCtx); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_CheckIfLookingUp(this, animFinished); +} + +void EnNb_CrawlspaceSpawnCheck(EnNb* this, GlobalContext* globalCtx) { + if (!(gSaveContext.eventChkInf[9] & 0x20) && LINK_IS_CHILD) { + EnNb_UpdatePath(this, globalCtx); + + // looking into crawlspace + if (!(gSaveContext.eventChkInf[9] & 0x10)) { + EnNb_SetCurrentAnim(this, &gNabooruKneeingAtCrawlspaceAnim, 0, 0.0f, 0); + this->action = NB_CROUCH_CRAWLSPACE; + this->drawMode = NB_DRAW_DEFAULT; + } else { + EnNb_SetCurrentAnim(this, &gNabooruStandingHandsOnHipsAnim, 0, 0.0f, 0); + this->headTurnFlag = 1; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.world.pos = this->finalPos; + this->action = NB_IDLE_AFTER_TALK; + this->drawMode = NB_DRAW_DEFAULT; + } + } else { + Actor_Kill(&this->actor); + } +} + +void func_80AB359C(EnNb* this) { + PosRot* world = &this->actor.world; + Vec3f* initialPos = &this->initialPos; + Vec3f* finalPos = &this->finalPos; + f32 f0; + u16 temp_t1; + s16 temp_2; + + this->movementTimer++; + temp_2 = kREG(17); + temp_t1 = temp_2; + temp_t1 += 25; + + if (temp_t1 >= this->movementTimer) { + f0 = Environment_LerpWeightAccelDecel(temp_t1, 0, this->movementTimer, 3, 3); + world->pos.x = initialPos->x + (f0 * (finalPos->x - initialPos->x)); + world->pos.y = initialPos->y + (f0 * (finalPos->y - initialPos->y)); + world->pos.z = initialPos->z + (f0 * (finalPos->z - initialPos->z)); + } +} + +void EnNb_SetNoticeSFX(EnNb* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_NB_NOTICE); +} + +s32 EnNb_GetNoticedStatus(EnNb* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 playerX = player->actor.world.pos.x; + f32 playerZ = player->actor.world.pos.z; + f32 thisX = this->actor.world.pos.x; + f32 thisZ = this->actor.world.pos.z; + + if (SQ(playerX - thisX) + SQ(playerZ - thisZ) < SQ(80.0f)) { + return true; + } else { + return false; + } +} + +void func_80AB36DC(EnNb* this, GlobalContext* globalCtx) { + u16 moveTime = this->movementTimer; + + if ((((u16)((u16)(kREG(17) + 25) - 4))) > moveTime) { + s16 invScale = 4 - moveTime; + + if (invScale > 0) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->pathYaw, invScale, 6200, 100); + } + } else { + s16 invScale = (u16)(kREG(17) + 25) - moveTime; + + if (invScale > 0) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, invScale, 6200, 100); + } + } +} + +void EnNb_CheckNoticed(EnNb* this, GlobalContext* globalCtx) { + if (EnNb_GetNoticedStatus(this, globalCtx)) { + EnNb_SetCurrentAnim(this, &gNabooruStandingToWalkingTransitionAnim, 2, -8.0f, 0); + this->action = NB_NOTICE_PLAYER; + EnNb_SetNoticeSFX(this); + } +} + +void EnNb_SetupIdleCrawlspace(EnNb* this, s32 animFinished) { + if (animFinished) { + EnNb_SetCurrentAnim(this, &gNabooruStandingHandsOnHipsAnim, 0, -8.0f, 0); + this->headTurnFlag = 1; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->action = NB_IDLE_CRAWLSPACE; + } +} + +void func_80AB3838(EnNb* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->action = NB_IN_DIALOG; + } else { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + + if (!(gSaveContext.infTable[22] & 0x1000)) { + this->actor.textId = 0x601D; + } else { + this->actor.textId = 0x6024; + } + + func_8002F2F4(&this->actor, globalCtx); + } +} + +void EnNb_SetupPathMovement(EnNb* this, GlobalContext* globalCtx) { + EnNb_SetCurrentAnim(this, &gNabooruStandingToWalkingTransitionAnim, 2, -8.0f, 0); + gSaveContext.eventChkInf[9] |= 0x10; + this->action = NB_IN_PATH; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); +} + +void EnNb_SetTextIdAsChild(EnNb* this, GlobalContext* globalCtx) { + s32 pad; + u8 choiceIndex; + s32 pad1; + u16 textId; + + textId = this->actor.textId; + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (textId == 0x6025) { + EnNb_SetupPathMovement(this, globalCtx); + } else { + if (textId == 0x6027) { + gSaveContext.infTable[22] |= 0x1000; + } + this->action = NB_IDLE_CRAWLSPACE; + } + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + } else if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + choiceIndex = globalCtx->msgCtx.choiceIndex; + + if (textId == 0x601D) { + switch (choiceIndex) { + case 0: + this->actor.textId = 0x601E; + break; + case 1: + this->actor.textId = 0x601F; + break; + default: + this->actor.textId = 0x6020; + } + } else if (textId == 0x6020) { + switch (choiceIndex) { + case 0: + this->actor.textId = 0x6021; + break; + default: + this->actor.textId = 0x6022; + break; + } + } else { + switch (choiceIndex) { + case 0: + this->actor.textId = 0x6025; + break; + default: + this->actor.textId = 0x6027; + break; + } + } + + Message_ContinueTextbox(globalCtx, this->actor.textId); + } +} + +void func_80AB3A7C(EnNb* this, GlobalContext* globalCtx, s32 animFinished) { + u16 movementTimer = this->movementTimer; + + if ((u16)(kREG(17) + 25) > movementTimer) { + if (animFinished) { + EnNb_SetCurrentAnim(this, &gNabooruWalkingAnim, 0, 0.0f, 0); + } + } else { + EnNb_SetCurrentAnim(this, &gNabooruStandingHandsOnHipsAnim, 0, -8.0f, 0); + this->action = NB_IDLE_AFTER_TALK; + } +} + +void func_80AB3B04(EnNb* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->action = NB_ACTION_30; + } else { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.textId = Text_GetFaceReaction(globalCtx, 0x23); + + if ((this->actor.textId) == 0) { + this->actor.textId = 0x6026; + } + + func_8002F2F4(&this->actor, globalCtx); + } +} + +void func_80AB3B7C(EnNb* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->action = NB_IDLE_AFTER_TALK; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + } +} + +void EnNb_WaitForNotice(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_CheckNoticed(this, globalCtx); +} + +void EnNb_StandUpAfterNotice(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetupIdleCrawlspace(this, animFinished); +} + +void EnNb_BlockCrawlspace(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + func_80AB0FBC(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + func_80AB3838(this, globalCtx); +} + +void EnNb_InitCrawlspaceDialogue(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + func_80AB0FBC(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + EnNb_SetTextIdAsChild(this, globalCtx); +} + +void EnNb_FollowPath(EnNb* this, GlobalContext* globalCtx) { + s32 animFinished; + + func_80AB359C(this); + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + func_80AB36DC(this, globalCtx); + func_80AB10C4(this); + animFinished = EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + func_80AB3A7C(this, globalCtx, animFinished); +} + +void func_80AB3DB0(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + func_80AB0FBC(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + func_80AB3B04(this, globalCtx); +} + +void func_80AB3E10(EnNb* this, GlobalContext* globalCtx) { + func_80AB1284(this, globalCtx); + EnNb_UpdateCollider(this, globalCtx); + func_80AB1040(this, globalCtx); + EnNb_UpdateSkelAnime(this); + EnNb_UpdateEyes(this); + func_80AB3B7C(this, globalCtx); +} + +static EnNbActionFunc sActionFuncs[] = { + EnNb_SetupChamberCs, + EnNb_SetupChamberWarp, + EnNb_ComeUp, + func_80AB193C, + EnNb_RaiseArm, + func_80AB19BC, + func_80AB19FC, + EnNb_Hide, + EnNb_Fade, + EnNb_CreateLightOrb, + func_80AB23A8, + EnNb_MovingInPortal, + EnNb_SuckedInByPortal, + EnNb_CheckConfrontationCsModeWrapper, + func_80AB2C18, + EnNb_Kneel, + EnNb_LookRight, + EnNb_LookLeft, + EnNb_Run, + EnNb_ConfrontationDestroy, + EnNb_CheckCreditsCsMode, + EnNb_CreditsFade, + func_80AB3428, + EnNb_LookUp, + EnNb_WaitForNotice, + EnNb_StandUpAfterNotice, + EnNb_BlockCrawlspace, + EnNb_InitCrawlspaceDialogue, + EnNb_FollowPath, + func_80AB3DB0, + func_80AB3E10, +}; + +void EnNb_Update(Actor* thisx, GlobalContext* globalCtx) { + EnNb* this = (EnNb*)thisx; + + if (this->action < 0 || this->action > 30 || sActionFuncs[this->action] == NULL) { + // "Main mode is wrong!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sActionFuncs[this->action](this, globalCtx); +} + +void EnNb_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNb* this = (EnNb*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + EnNb_SetupCollider(thisx, globalCtx); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gNabooruSkel, NULL, this->jointTable, this->morphTable, + NB_LIMB_MAX); + + switch (EnNb_GetType(this)) { + case NB_TYPE_DEMO02: + EnNb_SetupLightArrowOrSealingCs(this, globalCtx); + break; + case NB_TYPE_KIDNAPPED: + EnNb_InitKidnap(this, globalCtx); + break; + case NB_TYPE_KNUCKLE: + EnNb_SetupConfrontation(this, globalCtx); + break; + case NB_TYPE_CREDITS: + EnNb_SetupCreditsSpawn(this, globalCtx); + break; + case NB_TYPE_CRAWLSPACE: + EnNb_CrawlspaceSpawnCheck(this, globalCtx); + break; + default: // giving medallion + EnNb_SetChamberAnim(this, globalCtx); + break; + } +} + +s32 EnNb_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnNb* this = (EnNb*)thisx; + struct_80034A14_arg1* unk_300 = &this->unk_300; + s32 ret = false; + + if (this->headTurnFlag != 0) { + if (limbIndex == NB_LIMB_TORSO) { + rot->x += unk_300->unk_0E.y; + rot->y -= unk_300->unk_0E.x; + ret = false; + } else if (limbIndex == NB_LIMB_HEAD) { + rot->x += unk_300->unk_08.y; + rot->z += unk_300->unk_08.x; + ret = false; + } + } + + return ret; +} + +void EnNb_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnNb* this = (EnNb*)thisx; + + if (limbIndex == NB_LIMB_HEAD) { + Vec3f vec1 = { 0.0f, 10.0f, 0.0f }; + Vec3f vec2; + + Matrix_MultVec3f(&vec1, &vec2); + this->actor.focus.pos.x = vec2.x; + this->actor.focus.pos.y = vec2.y; + this->actor.focus.pos.z = vec2.z; + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + } +} + +void EnNb_DrawNothing(EnNb* this, GlobalContext* globalCtx) { +} + +void EnNb_DrawDefault(EnNb* this, GlobalContext* globalCtx) { + s32 pad; + s16 eyeIdx = this->eyeIdx; + SkelAnime* skelAnime = &this->skelAnime; + void* eyeTexture = sEyeTextures[eyeIdx]; + s32 pad1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_nb.c", 992); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnNb_OverrideLimbDraw, EnNb_PostLimbDraw, &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_nb.c", 1013); +} + +static EnNbDrawFunc sDrawFuncs[] = { + EnNb_DrawNothing, EnNb_DrawDefault, EnNb_DrawTransparency, func_80AB2E70, func_80AB2FE4, +}; + +void EnNb_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnNb* this = (EnNb*)thisx; + + if (this->drawMode < 0 || this->drawMode >= 5 || sDrawFuncs[this->drawMode] == NULL) { + // "Draw mode is wrong!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sDrawFuncs[this->drawMode](this, globalCtx); +} + +const ActorInit En_Nb_InitVars = { + ACTOR_EN_NB, + ACTORCAT_NPC, + FLAGS, + OBJECT_NB, + sizeof(EnNb), + (ActorFunc)EnNb_Init, + (ActorFunc)EnNb_Destroy, + (ActorFunc)EnNb_Update, + (ActorFunc)EnNb_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.h b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.h new file mode 100644 index 000000000..f0e66ac4e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb.h @@ -0,0 +1,66 @@ +#ifndef Z_EN_NB_H +#define Z_EN_NB_H + +#include "ultra64.h" +#include "global.h" + +struct EnNb; + +typedef enum { + /* 0x00 */ NB_LIMB_NONE, + /* 0x01 */ NB_LIMB_ROOT, + /* 0x02 */ NB_LIMB_L_THIGH, + /* 0x03 */ NB_LIMB_L_SHIN, + /* 0x04 */ NB_LIMB_L_FOOT, + /* 0x05 */ NB_LIMB_R_THIGH, + /* 0x06 */ NB_LIMB_R_SHIN, + /* 0x07 */ NB_LIMB_R_FOOT, + /* 0x08 */ NB_LIMB_TORSO, + /* 0x09 */ NB_LIMB_L_UPPER_ARM, + /* 0x0A */ NB_LIMB_L_FOREARM, + /* 0x0B */ NB_LIMB_L_HAND, + /* 0x0C */ NB_LIMB_R_UPPER_ARM, + /* 0x0D */ NB_LIMB_R_FOREARM, + /* 0x0E */ NB_LIMB_R_HAND, + /* 0x0F */ NB_LIMB_HEAD, + /* 0x10 */ NB_LIMB_BLANK, + /* 0x11 */ NB_LIMB_PONYTAIL, + /* 0x12 */ NB_LIMB_WAIST, + /* 0x13 */ NB_LIMB_MAX +} EnNbLimb; + +typedef void (*EnNbActionFunc)(struct EnNb*, GlobalContext*); +typedef void (*EnNbDrawFunc)(struct EnNb*, GlobalContext*); + +typedef struct EnNb { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[NB_LIMB_MAX]; + /* 0x0202 */ Vec3s morphTable[NB_LIMB_MAX]; + /* 0x0274 */ s16 eyeIdx; + /* 0x0276 */ s16 blinkTimer; + /* 0x0278 */ s32 action; + /* 0x027C */ s32 drawMode; + /* 0x0280 */ f32 alphaTimer; + /* 0x0284 */ u32 alpha; + /* 0x0288 */ s32 flag; + /* 0x028C */ s32 previousCsAction; + /* 0x0290 */ f32 timer; + /* 0x0294 */ ColliderCylinder collider; + /* 0x02E0 */ s32 headTurnFlag; + /* 0x02E4 */ Vec3f initialPos; + /* 0x02F0 */ Vec3f finalPos; + /* 0x02FC */ s16 pathYaw; + /* 0x02FE */ u16 movementTimer; + /* 0x0300 */ struct_80034A14_arg1 unk_300; +} EnNb; // size = 0x0328 + +typedef enum { + /* 0x02 */ NB_TYPE_DEMO02 = 2, + /* 0x03 */ NB_TYPE_KIDNAPPED, + /* 0x04 */ NB_TYPE_KNUCKLE, + /* 0x05 */ NB_TYPE_CREDITS, + /* 0x06 */ NB_TYPE_CRAWLSPACE +} EnNbType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Nb/z_en_nb_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb_cutscene_data.c new file mode 100644 index 000000000..a9acb6b8a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nb/z_en_nb_cutscene_data.c @@ -0,0 +1,170 @@ +#include "z_en_nb.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_80AB431C[] = { + CS_BEGIN_CUTSCENE(27, 3011), + CS_UNK_DATA_LIST(0x00000020, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(31, 5), + CS_NPC_ACTION(0x0001, 0, 501, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 501, 502, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 502, 573, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 573, 621, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 82, 0, 0.0f, -2.7916667f, 0.0f), + CS_NPC_ACTION(0x0003, 621, 3011, 0x0000, 0x0000, 0x0000, 0, 82, 0, 0, 82, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x000D, 0, 240, 0x0000, 0x1555, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0005, 240, 461, 0x0000, 0x1555, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0013, 461, 1616, 0x0000, 0x9555, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_NPC_ACTION_LIST(49, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, -16, -121, 0, -16, -121, 0.0f, 0.0f, 0.0f), + CS_LIGHTING_LIST(1), + CS_LIGHTING(0x0004, 0, 3000, 0x0000, 0x00000000, 0xFFFFFF9E, 0x00000000, 0x0000002F, 0xFFFFFF9E, 0x00000000, 0x0000002F), + CS_NPC_ACTION_LIST(39, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, 0, -2, 0, 0, -2, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(40, 3), + CS_NPC_ACTION(0x0001, 0, 145, 0x0000, 0x0000, 0x0000, -97, 6, -167, -97, 6, -167, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 145, 431, 0x0000, 0x0000, 0x0000, -97, 6, -167, -97, 6, -167, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 431, 1399, 0x0000, 0x0000, 0x0000, -97, 6, -167, -97, 6, -167, 0.0f, 0.0f, 0.0f), + CS_SCENE_TRANS_FX(0x0005, 500, 530), + CS_SCENE_TRANS_FX(0x0001, 489, 499), + CS_SCENE_TRANS_FX(0x0001, 765, 795), + CS_TEXT_LIST(10), + CS_TEXT_NONE(0, 345), + CS_TEXT_DISPLAY_TEXTBOX(0x6035, 345, 353, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(353, 374), + CS_TEXT_DISPLAY_TEXTBOX(0x6039, 374, 399, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(399, 400), + CS_TEXT_DISPLAY_TEXTBOX(0x603A, 400, 424, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(424, 760), + CS_TEXT_DISPLAY_TEXTBOX(0x003F, 760, 765, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(765, 825), + CS_TEXT_DISPLAY_TEXTBOX(0x6036, 825, 835, 0x0000, 0x0000, 0x0000), + CS_NPC_ACTION_LIST(62, 1), + CS_NPC_ACTION(0x0004, 0, 3000, 0x0000, 0x0000, 0x0000, 50, 80, 56, 50, 80, 56, 0.0f, 0.0f, 0.0f), + CS_TERMINATOR(DESERT_COLOSSUS_SPIRIT_BLUE_WARP, 865, 907), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0044, 575, 576, 0x0000, 0x00000000, 0xFFFFFFEF, 0x00000000, 0x00000034, 0xFFFFFFEF, 0x00000000, 0x00000034), + CS_FADE_BGM_LIST(1), + CS_FADE_BGM(0x0004, 479, 529, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC7, 0x0000005C, 0x00000000, 0xFFFFFFC7, 0x0000005C), + CS_CAM_EYE_LIST(0, 366), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -269, 89, -454, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -269, 89, -454, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -269, 89, -454, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -223, 75, -377, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -92, 31, -157, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, 13, 15, 19, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, 49, 28, 77, 0x013F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, 74, 20, 122, 0x014E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 50.599964f, 74, 20, 122, 0x015F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 74, 20, 122, 0x0161), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 74, 20, 122, 0x300A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 74, 20, 122, 0x656F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, 74, 20, 122, 0x676F), + CS_CAM_EYE_LIST(263, 2484), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 62.004566f, 134, 19, 198, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 62.004566f, 134, 19, 198, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 62.004566f, 134, 43, 198, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 62.004566f, 133, 60, 197, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 62.004566f, 133, 60, 197, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 62.004566f, 133, 60, 197, 0x013D), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 62.004566f, 133, 60, 197, 0x013F), + CS_CAM_EYE_LIST(363, 1595), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 116, 21, 45, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 116, 21, 45, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 116, 21, 45, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 117, 24, 69, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 105, 46, 117, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 105, 46, 117, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.399925f, 105, 46, 117, 0x013F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 40.399925f, 105, 46, 117, 0x002E), + CS_CAM_EYE_LIST(433, 1594), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 139, 33, -108, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 139, 33, -108, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 140, 81, -109, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.599945f, 140, 252, -109, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 140, 252, -109, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 140, 252, -109, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 140, 252, -109, 0x013F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.600002f, 140, 252, -109, 0x002E), + CS_CAM_EYE_LIST(498, 840), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 13, 854, 2, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 853, 5, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -3, 853, 5, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 853, -6, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -2, 852, -17, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, -17, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 16, 852, -6, 0x013F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, 5, 0x002E), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -3, 851, 5, 0x0063), + CS_CAM_EYE_REL_TO_PLAYER_LIST(574, 1755), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x00C6), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x00C8), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 68, -26, 0x00D7), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x00E8), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x00EA), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x013D), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x013F), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 68.599945f, 0, 103, -26, 0x002E), + CS_CAM_AT_LIST(0, 395), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -141, 51, -239, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -141, 51, -239, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -141, 52, -239, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -96, 32, -164, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, 34, 19, 56, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, 138, 2, 230, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, 165, 22, 286, 0x013F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 35, 55.799984f, 181, 116, 315, 0x014E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.599964f, 175, 134, 305, 0x015F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 172, 142, 300, 0x0161), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 171, 142, 300, 0x300A), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 171, 141, 299, 0x656F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, 171, 141, 299, 0x676F), + CS_CAM_AT_LIST(263, 2513), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 70, 62.004566f, -67, 36, 17, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 70, 62.004566f, -67, 32, 17, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 62.004566f, -66, 34, 18, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 62.004566f, -60, 24, 23, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 62.004566f, -60, 22, 23, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 62.004566f, -60, 19, 23, 0x013D), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 62.004566f, -60, 18, 23, 0x013F), + CS_CAM_AT_LIST(363, 1624), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.399925f, 34, 58, 269, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.399925f, 34, 58, 269, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 40.399925f, 34, 58, 269, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 40.399925f, 42, 74, 293, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 41, 40.399925f, 22, 93, 336, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 40.399925f, 22, 93, 336, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.399925f, 22, 93, 336, 0x013F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 40.399925f, 22, 93, 336, 0x002E), + CS_CAM_AT_LIST(433, 1623), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, 4, 14, 108, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.599945f, 4, 14, 108, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 50.999966f, 4, 76, 108, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 70.599915f, 111, 501, -62, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.599915f, 111, 500, -62, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 70.599915f, 111, 500, -62, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.39992f, 111, 500, -62, 0x013F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 70.39992f, 111, 500, -62, 0x002E), + CS_CAM_AT_LIST(498, 889), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.0f, 3, 6, -6, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.0f, 3, 6, -6, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.999966f, 3, 6, -6, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 20.59985f, 3, 6, -6, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 51, 10.799838f, 3, 6, -6, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x013F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.199839f, 3, 6, -6, 0x002E), + CS_CAM_AT(CS_CMD_STOP, 0x00, 50, 10.999838f, 3, 6, -6, 0x0063), + CS_CAM_AT_REL_TO_PLAYER_LIST(574, 1784), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 100, 5, 0x00C6), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 101, 6, 0x00C8), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 1, 99, 41, 0x00D7), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x00E8), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x00EA), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 68.599945f, 0, 42, 16, 0x013D), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x013F), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 68.599945f, 0, 42, 16, 0x002E), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c new file mode 100644 index 000000000..c6e23b8b9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.c @@ -0,0 +1,1235 @@ +/* + * File: z_en_niw.c + * Overlay: ovl_En_Niw + * Description: Cucco + */ + +#include "z_en_niw.h" +#include "objects/object_niw/object_niw.h" +#include "overlays/actors/ovl_En_Attack_Niw/z_en_attack_niw.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_23) + +void EnNiw_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNiw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNiw_Update(Actor* thisx, GlobalContext* globalCtx); +void EnNiw_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnNiw_Reset(void); + +void EnNiw_ResetAction(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6324(EnNiw* this, GlobalContext* globalCtx); +void func_80AB63A8(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6450(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6570(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6A38(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6BF8(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6D08(EnNiw* this, GlobalContext* globalCtx); +void func_80AB6EB4(EnNiw* this, GlobalContext* globalCtx); +void func_80AB70F8(EnNiw* this, GlobalContext* globalCtx); +void func_80AB714C(EnNiw* this, GlobalContext* globalCtx); +void func_80AB7204(EnNiw* this, GlobalContext* globalCtx); +void func_80AB7290(EnNiw* this, GlobalContext* globalCtx); +void func_80AB7328(EnNiw* this, GlobalContext* globalCtx); +void EnNiw_FeatherSpawn(EnNiw* this, Vec3f* pos, Vec3f* vel, Vec3f* accel, f32 scale); +void EnNiw_FeatherUpdate(EnNiw* this, GlobalContext* globalCtx); +void EnNiw_FeatherDraw(EnNiw* this, GlobalContext* globalCtx); + +static s16 D_80AB85E0 = 0; + +const ActorInit En_Niw_InitVars = { + ACTOR_EN_NIW, + ACTORCAT_PROP, + FLAGS, + OBJECT_NIW, + sizeof(EnNiw), + (ActorFunc)EnNiw_Init, + (ActorFunc)EnNiw_Destroy, + (ActorFunc)EnNiw_Update, + (ActorFunc)EnNiw_Draw, + (ActorResetFunc)EnNiw_Reset, +}; + +static f32 D_80AB8604[] = { + 5000.0f, + -5000.0f, +}; + +static f32 D_80AB860C[] = { + 5000.0f, + 3000.0f, + 4000.0f, +}; + +static Vec3f sKakarikoPosList[] = { + { -1697.0f, 80.0f, 870.0f }, { 57.0f, 320.0f, -673.0f }, { 796.0f, 80.0f, 1639.0f }, { 1417.0f, 465.0f, 169.0f }, + { -60.0f, 0.0f, -46.0f }, { -247.0f, 80.0f, 854.0f }, { 1079.0f, 80.0f, -47.0f }, +}; + +static s16 sKakarikoFlagList[] = { + 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, +}; + +static u8 sLowerRiverSpawned = false; + +static u8 sUpperRiverSpawned = false; + +static ColliderCylinderInit sCylinderInit1 = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 15, 25, 4, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInit2 = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 15, 25, 4, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -2000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void EnNiw_Init(Actor* thisx, GlobalContext* globalCtx) { + EnNiw* this = (EnNiw*)thisx; + s32 pad; + s32 i; + + if (this->actor.params < 0) { + this->actor.params = 0; + } + + // Cucco at at the very beginning of Zora's River + if (this->actor.params == 0xB) { + if (sLowerRiverSpawned) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ もういてる原 Ver.1 ☆☆☆☆☆ \n" VT_RST); + return; + } + sLowerRiverSpawned = true; + this->actor.room = -1; + } + + // Cucco at tall platform in the first room of Zora's River + if (this->actor.params == 0xC) { + if (sUpperRiverSpawned) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ もういてる原 Ver.2 ☆☆☆☆☆ \n" VT_RST); + return; + } + sUpperRiverSpawned = true; + this->actor.room = -1; + } + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.flags |= ACTOR_FLAG_0; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 25.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCuccoSkel, &gCuccoAnim, this->jointTable, this->morphTable, 16); + + if (globalCtx->sceneNum == SCENE_SPOT01) { + for (i = 0; i < ARRAY_COUNT(sKakarikoPosList); i++) { + if (fabsf(this->actor.world.pos.x - sKakarikoPosList[i].x) < 40.0f && + fabsf(this->actor.world.pos.z - sKakarikoPosList[i].z) < 40.0f) { + this->unk_2AA = i; + osSyncPrintf(VT_FGCOL(YELLOW) " 通常鶏index %d\n" VT_RST, this->unk_2AA); + if (gSaveContext.infTable[25] & sKakarikoFlagList[i]) { + this->actor.world.pos.x = 300.0f; + this->actor.world.pos.y = 100.0f; + this->actor.world.pos.z = 1530.0f; + this->actor.params = 0; + } + } + } + } + + Math_Vec3f_Copy(&this->unk_2AC, &this->actor.world.pos); + Math_Vec3f_Copy(&this->unk_2B8, &this->actor.world.pos); + this->unk_304 = 10.0f; + Actor_SetScale(&this->actor, 0.01f); + this->unk_2A4 = (s16)Rand_ZeroFloat(3.99f) + 5; + + if (this->unk_2A4 < 0) { + this->unk_2A4 = 1; + } + + switch (this->actor.params) { + case 2: + if (IS_DAY) { + Actor_Kill(&this->actor); + } + break; + case 1: + if (gSaveContext.eventChkInf[1] & 0x10) { + Actor_Kill(&this->actor); + } + break; + case 3: + if (!(gSaveContext.eventChkInf[1] & 0x10)) { + Actor_Kill(&this->actor); + } + break; + case 5: + if (gSaveContext.eventChkInf[1] & 0x100) { + Actor_Kill(&this->actor); + } + break; + case 7: + if (!(gSaveContext.eventChkInf[1] & 0x100)) { + Actor_Kill(&this->actor); + } + break; + case 0xD: + this->actor.gravity = 0.0f; + case 0xE: + this->actor.colChkInfo.mass = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + break; + case 4: + this->actor.gravity = 0.0f; + break; + } + + Collider_InitCylinder(globalCtx, &this->collider); + + switch (this->actor.params) { + case 0xA: + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + case 0xD: + case 0xE: + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit2); + if (globalCtx->sceneNum == SCENE_LINK_HOME && !(gSaveContext.eventChkInf[1] & 0x4000)) { + Actor_Kill(&this->actor); + } + break; + default: + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit1); + break; + } + + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ どんな奴? ☆☆☆☆☆ %d\n" VT_RST, this->actor.params); + osSyncPrintf("\n\n"); + this->actionFunc = EnNiw_ResetAction; +} + +void EnNiw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnNiw* this = (EnNiw*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AB5BF8(EnNiw* this, GlobalContext* globalCtx, s16 arg2) { + f32 factor = 1.0f; + + if (this->actor.params == 0xD) { + factor = 2.0f; + } + if (this->timer1 == 0) { + if (arg2 == 0) { + this->unk_26C[0] = 0.0f; + } else { + this->unk_26C[0] = -10000.0f * factor; + } + this->unk_298++; + this->timer1 = 3; + if (!(this->unk_298 & 1)) { + this->unk_26C[0] = 0.0f; + + if (arg2 == 0) { + this->timer1 = Rand_ZeroFloat(30.0f); + } + } + } + if (this->timer2 == 0) { + this->unk_29C++; + this->unk_29C &= 1; + + switch (arg2) { + case 0: + this->unk_26C[1] = this->unk_26C[2] = 0.0f; + break; + case 1: + this->timer2 = 3; + this->unk_26C[1] = this->unk_26C[2] = 7000.0f * factor; + + if (this->unk_29C == 0) { + this->unk_26C[1] = this->unk_26C[2] = 0.0f; + } + break; + case 2: + this->timer2 = 2; + this->unk_26C[1] = this->unk_26C[2] = -10000.0f; + this->unk_26C[7] = this->unk_26C[5] = 25000.0f; + this->unk_26C[8] = this->unk_26C[6] = 6000.0f; + + if (this->unk_29C == 0) { + this->unk_26C[5] = this->unk_26C[7] = 8000.0f; + } + break; + case 3: + this->timer2 = 2; + this->unk_26C[7] = this->unk_26C[5] = 10000.0f; + + if (this->unk_29C == 0) { + this->unk_26C[7] = this->unk_26C[5] = 3000.0f; + } + break; + case 4: + this->timer1 = 5; + break; + case 5: + this->timer2 = 5; + this->unk_26C[7] = this->unk_26C[5] = 14000.0f; + if (this->unk_29C == 0) { + this->unk_26C[7] = this->unk_26C[5] = 10000.0f; + } + break; + } + } + if (this->unk_2E0 != this->unk_26C[9]) { + Math_ApproachF(&this->unk_2E0, this->unk_26C[9], 0.5f, 4000.0f); + } + if (this->unk_2DC != this->unk_26C[0]) { + Math_ApproachF(&this->unk_2DC, this->unk_26C[0], 0.5f, 4000.0f); + } + if (this->unk_2C4 != this->unk_26C[2]) { + Math_ApproachF(&this->unk_2C4, this->unk_26C[2], 0.8f, 7000.0f); + } + if (this->unk_2C8 != this->unk_26C[7]) { + Math_ApproachF(&this->unk_2C8, this->unk_26C[7], 0.8f, 7000.0f); + } + if (this->unk_2CC != this->unk_26C[8]) { + Math_ApproachF(&this->unk_2CC, this->unk_26C[8], 0.8f, 7000.0f); + } + if (this->unk_2D0 != this->unk_26C[1]) { + Math_ApproachF(&this->unk_2D0, this->unk_26C[1], 0.8f, 7000.0f); + } + if (this->unk_2D4 != this->unk_26C[5]) { + Math_ApproachF(&this->unk_2D4, this->unk_26C[5], 0.8f, 7000.0f); + } + if (this->unk_2D8 != this->unk_26C[6]) { + Math_ApproachF(&this->unk_2D8, this->unk_26C[6], 0.8f, 7000.0f); + } +} + +void EnNiw_SpawnAttackCucco(EnNiw* this, GlobalContext* globalCtx) { + f32 viewX; + f32 viewY; + f32 viewZ; + Vec3f attackCuccoPos; + Actor* attackCucco; + + if ((this->timer5 == 0) && (this->unk_296 < 7)) { + viewX = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + viewY = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + viewZ = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + attackCuccoPos.x = ((Rand_ZeroOne() - 0.5f) * viewX) + globalCtx->view.eye.x; + attackCuccoPos.y = Rand_CenteredFloat(0.3f) + ((globalCtx->view.eye.y + 50.0f) + (viewY * 0.5f)); + attackCuccoPos.z = ((Rand_ZeroOne() - 0.5f) * viewZ) + globalCtx->view.eye.z; + attackCucco = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ATTACK_NIW, + attackCuccoPos.x, attackCuccoPos.y, attackCuccoPos.z, 0, 0, 0, 0); + + if (attackCucco != NULL) { + this->unk_296++; + this->timer5 = 10; + } else { + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 発生できず ☆☆☆☆☆ \n" VT_RST); + } + } +} + +void func_80AB6100(EnNiw* this, GlobalContext* globalCtx, s32 arg2) { + f32 factor; + f32 targetRotY; + + if (this->timer4 == 0) { + this->timer4 = 3; + + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 3.5f; + } + } + if (this->timer3 == 0) { + this->unk_2A0++; + this->unk_2A0 &= 1; + this->timer3 = 5; + } + if (this->unk_2A0 == 0) { + factor = D_80AB860C[arg2]; + } else { + factor = -D_80AB860C[arg2]; + } + if (arg2 == 1) { + if (this->timer6 == 0 || this->actor.bgCheckFlags & 8) { + this->timer6 = 150; + if (this->timer8 == 0) { + this->timer8 = 70; + this->unk_2E4 = this->actor.yawTowardsPlayer; + } + } + } + targetRotY = this->unk_2E4 + factor; + Math_SmoothStepToS(&this->actor.world.rot.y, targetRotY, 3, this->unk_2FC, 0); + Math_ApproachF(&this->unk_2FC, 3000.0f, 1.0f, 500.0f); + func_80AB5BF8(this, globalCtx, 5); +} + +void EnNiw_ResetAction(EnNiw* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gCuccoAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gCuccoAnim), ANIMMODE_LOOP, + -10.0f); + + switch (this->actor.params) { + case 4: + this->actionFunc = func_80AB6450; + break; + case 0xD: + this->actionFunc = func_80AB6324; + break; + default: + this->actionFunc = func_80AB6570; + break; + } +} + +void func_80AB6324(EnNiw* this, GlobalContext* globalCtx) { + if (this->unk_308 != 0) { + this->actor.velocity.y = Rand_ZeroFloat(2.0f) + 4.0f; + this->actor.speedXZ = Rand_ZeroFloat(2.0f) + 3.0f; + this->actionFunc = func_80AB63A8; + } + func_80AB5BF8(this, globalCtx, 1); +} + +void func_80AB63A8(EnNiw* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1 && this->actor.velocity.y < 0.0f) { + this->unk_2AC.x = this->unk_2B8.x = this->actor.world.pos.x; + this->unk_2AC.y = this->unk_2B8.y = this->actor.world.pos.y; + this->unk_2AC.z = this->unk_2B8.z = this->actor.world.pos.z; + this->timer5 = this->timer4 = this->unk_29E = 0; + + this->unk_26C[7] = this->unk_26C[5] = this->unk_26C[6] = this->unk_26C[8] = this->actor.speedXZ = + this->unk_2FC = this->unk_300 = 0.0f; + + this->actionFunc = func_80AB6570; + } else { + func_80AB5BF8(this, globalCtx, 2); + } +} + +void func_80AB6450(EnNiw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->actor.xzDistToPlayer < 30.0f && fabsf(this->actor.world.pos.y - player->actor.world.pos.y) < 5.0f) { + this->timer6 = 100; + this->actor.gravity = -2.0f; + this->actionFunc = func_80AB7290; + } else if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.gravity = -2.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + this->sfxTimer1 = 30; + this->path = 0; + this->timer4 = 30; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + this->actionFunc = func_80AB6BF8; + } else { + // GI_NONE in this case allows the player to lift the actor + func_8002F434(&this->actor, globalCtx, GI_NONE, 25.0f, 10.0f); + func_80AB5BF8(this, globalCtx, 1); + } +} + +void func_80AB6570(EnNiw* this, GlobalContext* globalCtx) { + s32 pad[2]; + f32 posY = Rand_CenteredFloat(100.0f); + f32 posZ = Rand_CenteredFloat(100.0f); + s16 tmp; + + if (this->actor.params != 0xA) { + if (Actor_HasParent(&this->actor, globalCtx)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + this->sfxTimer1 = 30; + this->path = 0; + this->timer4 = 30; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + this->actionFunc = func_80AB6BF8; + return; + } + func_8002F580(&this->actor, globalCtx); + } else { + if (this->path != 0) { + this->unk_2A6 = 1; + if (this->sfxTimer3 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + this->sfxTimer3 = 100; + } + this->unk_2A0 = Rand_ZeroFloat(1.99f); + this->actor.speedXZ = 4.0f; + this->unk_300 = 0.0f; + this->unk_2FC = 0.0f; + this->actionFunc = func_80AB6A38; + return; + } + } + + tmp = 0; + + if (this->timer5 != 0) { + if (Rand_ZeroFloat(3.99f) < 1.0f) { + this->unk_2E6++; + this->unk_2E6 &= 1; + } + Math_ApproachF(&this->unk_26C[9], D_80AB8604[this->unk_2E6], 0.5f, 4000.0f); + } + + if (this->timer5 == 0 && this->timer4 == 0) { + this->unk_29E++; + + if (this->unk_29E >= 8) { + this->timer5 = Rand_ZeroFloat(30.0f); + this->unk_29E = Rand_ZeroFloat(3.99f); + if (this->actor.params != 0xA && this->actor.params != 8) { + if (posY < 0.0f) { + posY -= 100.0f; + } else { + posY += 100.0f; + } + if (posZ < 0.0f) { + posZ -= 100.0f; + } else { + posZ += 100.0f; + } + } else { + posY = Rand_CenteredFloat(30.0f); + posZ = Rand_CenteredFloat(30.0f); + if (posY < 0.0f) { + posY -= 20.0f; + } else { + posY += 20.0f; + } + if (posZ < 0.0f) { + if (1) {} // Required to match + if (1) {} + if (1) {} + if (1) {} + posZ -= 20.0f; + } else { + posZ += 20.0f; + } + } + this->unk_2B8.x = this->unk_2AC.x + posY; + this->unk_2B8.z = this->unk_2AC.z + posZ; + } else { + this->timer4 = 4; + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 3.5f; + } + } + } + + if (this->timer4 != 0) { + Math_ApproachZeroF(&this->unk_26C[9], 0.5f, 4000.0f); + tmp = 1; + Math_ApproachF(&this->actor.world.pos.x, this->unk_2B8.x, 1.0f, this->unk_2FC); + Math_ApproachF(&this->actor.world.pos.z, this->unk_2B8.z, 1.0f, this->unk_2FC); + Math_ApproachF(&this->unk_2FC, 3.0f, 1.0f, 0.3f); + posY = this->unk_2B8.x - this->actor.world.pos.x; + posZ = this->unk_2B8.z - this->actor.world.pos.z; + + if (fabsf(posY) < 10.0f) { + posY = 0.0; + } + if (fabsf(posZ) < 10.0f) { + posZ = 0.0; + } + if (posY == 0.0f && posZ == 0.0f) { + this->timer4 = 0; + this->unk_29E = 7; + } + + Math_SmoothStepToS(&this->actor.world.rot.y, Math_FAtan2F(posY, posZ) * (0x8000 / M_PI), 3, this->unk_300, 0); + Math_ApproachF(&this->unk_300, 10000.0f, 1.0f, 1000.0f); + } + + func_80AB5BF8(this, globalCtx, tmp); +} + +void func_80AB6A38(EnNiw* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* pointPos; + f32 pathDiffX; + f32 pathDiffZ; + s16 pathIndex = this->path - 1; + + if (this->path == 0) { + this->unk_2AC.x = this->unk_2B8.x = this->actor.world.pos.x; + this->unk_2AC.y = this->unk_2B8.y = this->actor.world.pos.y; + this->unk_2AC.z = this->unk_2B8.z = this->actor.world.pos.z; + this->timer5 = this->timer4 = this->unk_29E = 0; + this->unk_26C[7] = this->unk_26C[5] = this->unk_26C[6] = this->unk_26C[8] = this->actor.speedXZ = + this->unk_2FC = this->unk_300 = 0.0f; + this->actionFunc = EnNiw_ResetAction; + } else { + path = &globalCtx->setupPathList[pathIndex]; + pointPos = SEGMENTED_TO_VIRTUAL(path->points); + pointPos += this->waypoint; + pathDiffX = pointPos->x - this->actor.world.pos.x; + pathDiffZ = pointPos->z - this->actor.world.pos.z; + this->unk_2E4 = Math_FAtan2F(pathDiffX, pathDiffZ) * (0x8000 / M_PI); + func_80AB6100(this, globalCtx, 2); + + if (fabsf(pathDiffX) < 30.0f && fabsf(pathDiffZ) < 30.0f) { + this->waypoint++; + if (this->waypoint >= this->unk_2EC) { + this->waypoint = 0; + } + } + + func_80AB5BF8(this, globalCtx, 2); + } +} + +void func_80AB6BF8(EnNiw* this, GlobalContext* globalCtx) { + if (this->timer4 == 0) { + this->unk_2A6 = 2; + this->timer4 = 10; + } + + this->actor.shape.rot.x = Rand_CenteredFloat(5000.0f); + this->actor.shape.rot.y = Rand_CenteredFloat(5000.0f); + this->actor.shape.rot.z = Rand_CenteredFloat(5000.0f); + + if (Actor_HasNoParent(&this->actor, globalCtx)) { + if (this->actor.params == 0xD) { + this->sfxTimer1 = 0; + this->unk_2A6 = 1; + this->actionFunc = func_80AB6EB4; + this->actor.velocity.y = 4.0f; + return; + } + this->actor.shape.rot.z = 0; + this->actor.shape.rot.y = this->actor.shape.rot.z; + this->actor.shape.rot.x = this->actor.shape.rot.z; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = func_80AB6D08; + } + func_80AB5BF8(this, globalCtx, 2); +} + +void func_80AB6D08(EnNiw* this, GlobalContext* globalCtx) { + if (this->path == 0) { + if (!(this->actor.bgCheckFlags & 1)) { + return; + } + if (this->actor.params == 0xE) { + this->unk_2AC.x = this->unk_2B8.x = this->actor.world.pos.x; + this->unk_2AC.y = this->unk_2B8.y = this->actor.world.pos.y; + this->unk_2AC.z = this->unk_2B8.z = this->actor.world.pos.z; + this->timer5 = this->timer4 = this->unk_29E = 0; + + this->unk_26C[7] = this->unk_26C[5] = this->unk_26C[6] = this->unk_26C[8] = this->actor.speedXZ = + this->unk_2FC = this->unk_300 = 0.0f; + + this->actionFunc = EnNiw_ResetAction; + return; + } + + this->path = 1; + this->timer5 = 80; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 4.0f; + } else { + if (this->actor.bgCheckFlags & 1) { + this->sfxTimer1 = 0; + this->actor.velocity.y = 4.0f; + this->unk_2A6 = 1; + } + if (this->timer5 == 0) { + this->timer6 = 100; + this->timer4 = 0; + this->path = 0; + this->actionFunc = func_80AB7290; + return; + } + } + + if (Actor_HasParent(&this->actor, globalCtx)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + this->sfxTimer1 = 30; + this->path = 0; + this->timer4 = 30; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + this->actionFunc = func_80AB6BF8; + } else { + if (this->timer5 >= 6) { + func_8002F580(&this->actor, globalCtx); + } + func_80AB5BF8(this, globalCtx, 2); + } +} + +void func_80AB6EB4(EnNiw* this, GlobalContext* globalCtx) { + if (this->actor.world.pos.y > 400.0f) { + Actor_Kill(&this->actor); + } + + func_80AB5BF8(this, globalCtx, 2); +} + +void func_80AB6F04(EnNiw* this, GlobalContext* globalCtx) { + Vec3f pos; + + if (this->unk_2A8 != 0) { + EnNiw_SpawnAttackCucco(this, globalCtx); + } + + this->actor.speedXZ = 2.0f; + + if (this->actor.bgCheckFlags & 0x20) { + this->actor.gravity = 0.0f; + + if (this->actor.yDistToWater > 15.0f) { + this->actor.world.pos.y += 2.0f; + } + if (this->timer4 == 0) { + this->timer4 = 30; + Math_Vec3f_Copy(&pos, &this->actor.world.pos); + pos.y += this->actor.yDistToWater; + EffectSsGRipple_Spawn(globalCtx, &pos, 100, 500, 30); + } + if (this->actor.bgCheckFlags & 8) { + this->actor.velocity.y = 10.0f; + this->actor.speedXZ = 1.0f; + } + } else { + this->actor.gravity = -2.0f; + + if (this->actor.bgCheckFlags & 8) { + this->actor.velocity.y = 10.0f; + this->actor.speedXZ = 1.0f; + this->actor.gravity = 0.0f; + } else { + this->actor.speedXZ = 4.0f; + } + if (this->actor.bgCheckFlags & 1) { + this->actor.gravity = -2.0f; + this->timer6 = 100; + this->timer4 = 0; + this->actor.velocity.y = 0.0f; + if (this->unk_2A8 == 0) { + this->actionFunc = func_80AB7290; + } else { + this->actionFunc = func_80AB7204; + } + } + } + + func_80AB5BF8(this, globalCtx, 2); +} + +void func_80AB70A0(EnNiw* this, GlobalContext* globalCtx) { + OnePointCutscene_Init(globalCtx, 2290, -99, &this->actor, MAIN_CAM); + this->timer5 = 100; + this->unk_2A2 = 1; + this->actionFunc = func_80AB70F8; +} + +void func_80AB70F8(EnNiw* this, GlobalContext* globalCtx) { + this->sfxTimer1 = 100; + + if (this->timer5 == 0) { + this->timer5 = 60; + this->timer1 = 10; + this->unk_2A2 = 4; + this->actionFunc = func_80AB714C; + } + + func_80AB5BF8(this, globalCtx, this->unk_2A2); +} + +void func_80AB714C(EnNiw* this, GlobalContext* globalCtx) { + this->sfxTimer1 = 100; + + if (this->timer5 == 40) { + this->unk_26C[0] = 10000.0f; + this->unk_26C[7] = 14000.0f; + this->unk_26C[5] = 14000.0f; + this->unk_26C[6] = 0.0f; + this->unk_26C[8] = 0.0f; + this->unk_26C[1] = 0.0f; + this->unk_26C[2] = 0.0f; + this->timer1 = 10; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + } + if (this->timer5 == 0) { + this->timer7 = 10; + this->unk_2E4 = this->actor.yawTowardsPlayer; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = func_80AB7204; + } + + func_80AB5BF8(this, globalCtx, this->unk_2A2); +} + +void func_80AB7204(EnNiw* this, GlobalContext* globalCtx) { + EnNiw_SpawnAttackCucco(this, globalCtx); + + if (this->timer7 < 2) { + if (this->timer7 == 1) { + this->actor.speedXZ = 3.0f; + this->unk_2A0 = Rand_ZeroFloat(1.99f); + this->timer1 = this->timer2 = this->timer3 = this->timer4 = 0; + } else { + func_80AB6100(this, globalCtx, 1); + } + } +} + +void func_80AB7290(EnNiw* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gCuccoAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gCuccoAnim), ANIMMODE_LOOP, + -10.0f); + this->unk_2A0 = Rand_ZeroFloat(1.99f); + this->actor.speedXZ = 4.0f; + this->actionFunc = func_80AB7328; +} + +void func_80AB7328(EnNiw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer6 == 0) { + this->unk_2AC.x = this->unk_2B8.x = this->actor.world.pos.x; + this->unk_2AC.y = this->unk_2B8.y = this->actor.world.pos.y; + this->unk_2AC.z = this->unk_2B8.z = this->actor.world.pos.z; + this->timer5 = this->timer4 = this->unk_29E = 0; + this->unk_26C[7] = this->unk_26C[5] = this->unk_26C[6] = this->unk_26C[8] = this->actor.speedXZ = + this->unk_2FC = this->unk_300 = 0.0f; + if (this->actor.params == 4) { + this->actor.params = 0; + } + this->actionFunc = EnNiw_ResetAction; + } else { + this->unk_2E4 = Math_FAtan2F(this->actor.world.pos.x - player->actor.world.pos.x, + this->actor.world.pos.z - player->actor.world.pos.z) * + (0x8000 / M_PI); + func_80AB6100(this, globalCtx, 0); + func_80AB5BF8(this, globalCtx, 2); + } +} + +void func_80AB7420(EnNiw* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1) { + this->unk_2A4 = (s16)Rand_ZeroFloat(3.99f) + 5; + this->actionFunc = EnNiw_ResetAction; + } +} + +void func_80AB747C(EnNiw* this, GlobalContext* globalCtx) { + if (this->unk_2A8 == 0 && this->actor.params != 0xA && this->actionFunc != func_80AB6450 && + this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + this->sfxTimer1 = 30; + if (this->unk_2A4 > 0 && D_80AB85E0 == 0) { + this->unk_2A4--; + } + this->unk_2A6 = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + this->timer6 = 100; + this->path = 0; + this->actionFunc = func_80AB7290; + } +} + +void EnNiw_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad1; + EnNiw* this = (EnNiw*)thisx; + Player* player = GET_PLAYER(globalCtx); + s16 i; + s16 featherCount; + Vec3f zeroVec1 = { 0.0f, 0.0f, 0.0f }; + Vec3f zeroVec2 = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + Vec3f vel; + Vec3f accel; + s32 pad2; + f32 scale; + Vec3f cam; + f32 dist; + f32 camResult; + s32 pad3[10]; + + if (1) {} // Required to match + if (1) {} + if (1) {} + + this->unk_294++; + + if (this->actionFunc != func_80AB6570) { + this->unk_26C[9] = 0.0f; + } + if (this->unk_2A6) { + featherCount = 20; + + if (this->unk_2A6 == 2) { + featherCount = 4; + } + + for (i = 0; i < featherCount; i++) { + pos.x = Rand_CenteredFloat(10.0f) + thisx->world.pos.x; + pos.y = Rand_CenteredFloat(10.0f) + (thisx->world.pos.y + this->unk_304); + pos.z = Rand_CenteredFloat(10.0f) + thisx->world.pos.z; + scale = Rand_ZeroFloat(6.0f) + 6.0f; + + if (this->unk_2A6 == 2 && this->unk_304 != 0) { + pos.y += 10; + } + if (this->unk_304 == 0) { + scale = Rand_ZeroFloat(2.0f) + 2; + } + + vel.x = Rand_CenteredFloat(3.0f); + vel.y = (Rand_ZeroFloat(2.0f) * 0.5f) + 2.0f; + vel.z = Rand_CenteredFloat(3.0f); + accel.x = 0.0f; + accel.y = -0.15f; + accel.z = 0.0f; + EnNiw_FeatherSpawn(this, &pos, &vel, &accel, scale); + } + + this->unk_2A6 = 0; + } + + EnNiw_FeatherUpdate(this, globalCtx); + if (this->timer1 != 0) { + this->timer1--; + } + if (this->timer2 != 0) { + this->timer2--; + } + if (this->timer3 != 0) { + this->timer3--; + } + if (this->timer4 != 0) { + this->timer4--; + } + if (this->timer5 != 0) { + this->timer5--; + } + if (this->timer7 != 0) { + this->timer7--; + } + if (this->timer6 != 0) { + this->timer6--; + } + if (this->sfxTimer1 != 0) { + this->sfxTimer1--; + } + if (this->sfxTimer2 != 0) { + this->sfxTimer2--; + } + if (this->sfxTimer3 != 0) { + this->sfxTimer3--; + } + if (this->timer8 != 0) { + this->timer8--; + } + if (this->timer9 != 0) { + this->timer9--; + } + thisx->shape.rot = thisx->world.rot; + thisx->shape.shadowScale = 15.0f; + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, this->unk_304); + Actor_MoveForward(&this->actor); + + if (this->actionFunc != func_80AB6EB4 && this->actionFunc != func_80AB6450 && globalCtx->sceneNum != SCENE_SPOT03) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 31); + } + if (globalCtx->sceneNum == SCENE_SPOT03) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 29); + } + if (thisx->floorHeight <= BGCHECK_Y_MIN || thisx->floorHeight >= 32000.0f) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 上下? ☆☆☆☆☆ %f\n" VT_RST, thisx->floorHeight); + cam.x = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + cam.y = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + cam.z = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + camResult = cam.y / sqrtf(SQ(cam.x) + SQ(cam.y) + SQ(cam.z)); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 範囲外X! ☆☆☆☆☆ %f\n" VT_RST, thisx->world.pos.x); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 範囲外Y! ☆☆☆☆☆ %f\n" VT_RST, thisx->world.pos.y); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 範囲外Z! ☆☆☆☆☆ %f\n" VT_RST, thisx->world.pos.z); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ セットX! ☆☆☆☆☆ %f\n" VT_RST, thisx->home.pos.x); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ セットY! ☆☆☆☆☆ %f\n" VT_RST, thisx->home.pos.y); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ セットZ! ☆☆☆☆☆ %f\n" VT_RST, thisx->home.pos.z); + thisx->world.pos.x = thisx->home.pos.x; + thisx->world.pos.z = thisx->home.pos.z; + thisx->world.pos.y = ((thisx->home.pos.y + globalCtx->view.eye.y) + (camResult * 160.0f)); + + if (thisx->world.pos.y < thisx->home.pos.y) { + thisx->world.pos.y = thisx->home.pos.y + 300.0f; + } + + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 修整後X! ☆☆☆☆☆ %f\n" VT_RST, thisx->world.pos.x); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 修整後Y! ☆☆☆☆☆ %f\n" VT_RST, thisx->world.pos.y); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 修整後Z! ☆☆☆☆☆ %f\n" VT_RST, thisx->world.pos.z); + osSyncPrintf("\n\n"); + thisx->speedXZ = 0.0f; + thisx->gravity = -2.0f; + Math_Vec3f_Copy(&this->unk_2AC, &thisx->home.pos); + Math_Vec3f_Copy(&this->unk_2B8, &thisx->home.pos); + this->unk_300 = 0.0f; + this->unk_2FC = 0.0f; + this->unk_2F0.z = 0.0f; + this->unk_2F0.y = 0.0f; + this->unk_2F0.x = 0.0f; + this->unk_2D8 = 0.0f; + this->unk_2D4 = 0.0f; + this->unk_2D0 = 0.0f; + this->unk_2CC = 0.0f; + this->unk_2C8 = 0.0f; + this->unk_2C4 = 0.0f; + this->unk_2DC = 0.0f; + this->unk_2E0 = 0.0f; + this->unk_2A8 = this->unk_294 = this->unk_298 = this->unk_2A6 = this->unk_29E = this->unk_2A0 = this->unk_2A2 = + 0; + + for (i = 0; i < ARRAY_COUNT(this->unk_26C); i++) { + this->unk_26C[i] = 0; + } + + this->unk_2A8 = 0; + this->actionFunc = func_80AB7420; + return; + } + + if (thisx->bgCheckFlags & 0x20 && thisx->yDistToWater > 15.0f && this->actionFunc != func_80AB6F04 && + thisx->params != 0xD && thisx->params != 0xE && thisx->params != 0xA) { + thisx->velocity.y = 0.0f; + thisx->gravity = 0.0f; + Math_Vec3f_Copy(&pos, &thisx->world.pos); + pos.y += thisx->yDistToWater; + this->timer4 = 30; + EffectSsGSplash_Spawn(globalCtx, &pos, 0, 0, 0, 400); + this->timer5 = 0; + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ぶくぶく ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf("\n\n"); + this->actionFunc = func_80AB6F04; + return; + } + + if (D_80AB85E0 == 0 && this->unk_2A4 <= 0 && thisx->params != 0xD && thisx->params != 0xE && thisx->params != 0xA) { + this->timer6 = 100; + + if (thisx->xzDistToPlayer > 10.0f) { + D_80AB85E0 = 1; + this->timer5 = this->timer4 = this->unk_29E = 0; + thisx->speedXZ = 0.0f; + this->unk_2FC = 0.0f; + this->unk_300 = 0.0f; + this->unk_26C[7] = 0.0f; + this->unk_26C[5] = 0.0f; + this->unk_26C[6] = 0.0f; + this->unk_26C[8] = 0.0f; + this->sfxTimer1 = 10000; + this->unk_2A8 = 1; + this->unk_2AC.x = this->unk_2B8.x = thisx->world.pos.x; + this->unk_2AC.y = this->unk_2B8.y = thisx->world.pos.y; + this->unk_2AC.z = this->unk_2B8.z = thisx->world.pos.z; + this->actionFunc = func_80AB70A0; + return; + } + } + + dist = 20.0f; + + if (this->unk_2A8 != 0 && thisx->xyzDistToPlayerSq < SQ(dist) && player->invincibilityTimer == 0) { + func_8002F6D4(globalCtx, &this->actor, 2.0f, thisx->world.rot.y, 0.0f, 0x10); + } + + func_80AB747C(this, globalCtx); + + if (this->sfxTimer2 == 0 && this->actionFunc == func_80AB6BF8) { + this->sfxTimer2 = 7; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DEKU_WAKEUP); + } + if (this->sfxTimer1 == 0) { + if (this->actionFunc != func_80AB6570) { + this->sfxTimer1 = 30; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_A); + } else { + this->sfxTimer1 = 300; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_N); + } + } + if (this->unk_2A8 == 0) { + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (thisx->params != 0xA && thisx->params != 0xD && thisx->params != 0xE && thisx->params != 4) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->actionFunc != func_80AB6BF8 && this->actionFunc != func_80AB6D08 && + this->actionFunc != func_80AB6324 && this->actionFunc != func_80AB63A8 && + this->actionFunc != func_80AB6450) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +s32 EnNiw_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnNiw* this = (EnNiw*)thisx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == 13) { + rot->y += (s16)this->unk_2DC; + } + if (limbIndex == 15) { + rot->y += (s16)this->unk_2E0; + } + if (limbIndex == 11) { + rot->x += (s16)this->unk_2D8; + rot->y += (s16)this->unk_2D4; + rot->z += (s16)this->unk_2D0; + } + if (limbIndex == 7) { + rot->x += (s16)this->unk_2CC; + rot->y += (s16)this->unk_2C8; + rot->z += (s16)this->unk_2C4; + } + + return false; +} + +void EnNiw_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnNiw* this = (EnNiw*)thisx; + Vec3f scale = { 0.15f, 0.15f, 0.15f }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnNiw_OverrideLimbDraw, NULL, this); + + if (this->actionFunc == func_80AB6450) { + func_80033C30(&this->actor.world.pos, &scale, 255, globalCtx); + } + + EnNiw_FeatherDraw(this, globalCtx); +} + +void EnNiw_FeatherSpawn(EnNiw* this, Vec3f* pos, Vec3f* vel, Vec3f* accel, f32 scale) { + s16 i; + EnNiwFeather* feather = this->feathers; + + for (i = 0; i < ARRAY_COUNT(this->feathers); i++, feather++) { + if (feather->type == 0) { + feather->type = 1; + feather->pos = *pos; + feather->vel = *vel; + feather->accel = *accel; + feather->timer = 0; + feather->scale = scale / 1000.0f; + feather->life = (s16)Rand_ZeroFloat(20.0f) + 40; + feather->unk_2A = Rand_ZeroFloat(1000.0f); + break; + } + } +} + +void EnNiw_FeatherUpdate(EnNiw* this, GlobalContext* globalCtx) { + s16 i; + EnNiwFeather* feather = this->feathers; + + for (i = 0; i < ARRAY_COUNT(this->feathers); i++, feather++) { + if (feather->type != 0) { + feather->timer++; + feather->pos.x += feather->vel.x; + feather->pos.y += feather->vel.y; + feather->pos.z += feather->vel.z; + feather->vel.x += feather->accel.x; + feather->vel.y += feather->accel.y; + feather->vel.z += feather->accel.z; + if (feather->type == 1) { + feather->unk_2A++; + Math_ApproachF(&feather->vel.x, 0.0f, 1.0f, 0.05f); + Math_ApproachF(&feather->vel.z, 0.0f, 1.0f, 0.05f); + if (feather->vel.y < -0.5f) { + feather->vel.y = -0.5f; + } + + feather->unk_30 = Math_SinS(feather->unk_2A * 0xBB8) * M_PI * 0.2f; + + if (feather->life < feather->timer) { + feather->type = 0; + } + } + } + } +} + +void EnNiw_FeatherDraw(EnNiw* this, GlobalContext* globalCtx) { + u8 flag = 0; + s16 i; + s32 pad; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + EnNiwFeather* feather = &this->feathers[0]; + + OPEN_DISPS(gfxCtx, "../z_en_niw.c", 1897); + + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < ARRAY_COUNT(this->feathers); i++, feather++) { + if (feather->type == 1) { + if (!flag) { + gSPDisplayList(POLY_XLU_DISP++, gCuccoParticleAppearDL); + flag++; + } + Matrix_Translate(feather->pos.x, feather->pos.y, feather->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(feather->scale, feather->scale, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ(feather->unk_30, MTXMODE_APPLY); + Matrix_Translate(0.0f, -1000.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_niw.c", 1913), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCuccoParticleAliveDL); + } + } + + CLOSE_DISPS(gfxCtx, "../z_en_niw.c", 1919); +} + +void EnNiw_Reset(void) { + D_80AB85E0 = 0; + sLowerRiverSpawned = false; + sUpperRiverSpawned = false; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h new file mode 100644 index 000000000..d02732496 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Niw/z_en_niw.h @@ -0,0 +1,79 @@ +#ifndef Z_EN_NIW_H +#define Z_EN_NIW_H + +#include "ultra64.h" +#include "global.h" + +struct EnNiw; + +typedef void (*EnNiwActionFunc)(struct EnNiw*, GlobalContext*); + +typedef struct { + /* 0x0000 */ u8 type; + /* 0x0004 */ Vec3f pos; + /* 0x0010 */ Vec3f vel; + /* 0x001C */ Vec3f accel; + /* 0x0028 */ s16 life; + /* 0x002A */ s16 unk_2A; // Variance timer? + /* 0x002C */ f32 scale; + /* 0x0030 */ f32 unk_30; + /* 0x0034 */ u8 timer; +} EnNiwFeather; // size = 0x0038 + +typedef struct EnNiw { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s morphTable[16]; + /* 0x0250 */ EnNiwActionFunc actionFunc; + /* 0x0254 */ s16 timer1; + /* 0x0256 */ s16 timer2; + /* 0x0258 */ s16 timer3; + /* 0x025A */ s16 timer4; + /* 0x025C */ s16 timer5; + /* 0x025E */ s16 timer6; + /* 0x0260 */ s16 sfxTimer1; + /* 0x0262 */ s16 sfxTimer2; + /* 0x0264 */ s16 sfxTimer3; + /* 0x0266 */ s16 timer7; + /* 0x0268 */ s16 timer8; + /* 0x026A */ s16 timer9; + /* 0x026C */ f32 unk_26C[10]; + /* 0x0294 */ s16 unk_294; + /* 0x0296 */ s16 unk_296; + /* 0x0298 */ s16 unk_298; + /* 0x029A */ s16 unk_29A; + /* 0x029C */ s16 unk_29C; + /* 0x029E */ s16 unk_29E; + /* 0x02A0 */ s16 unk_2A0; + /* 0x02A2 */ s16 unk_2A2; + /* 0x02A4 */ s16 unk_2A4; + /* 0x02A6 */ s16 unk_2A6; + /* 0x02A8 */ s16 unk_2A8; + /* 0x02AA */ s16 unk_2AA; + /* 0x02AC */ Vec3f unk_2AC; + /* 0x02B8 */ Vec3f unk_2B8; + /* 0x02C4 */ f32 unk_2C4; + /* 0x02C8 */ f32 unk_2C8; + /* 0x02CC */ f32 unk_2CC; + /* 0x02D0 */ f32 unk_2D0; + /* 0x02D4 */ f32 unk_2D4; + /* 0x02D8 */ f32 unk_2D8; + /* 0x02DC */ f32 unk_2DC; + /* 0x02E0 */ f32 unk_2E0; + /* 0x02E4 */ s16 unk_2E4; + /* 0x02E6 */ s16 unk_2E6; + /* 0x02E8 */ s16 path; + /* 0x02EA */ s16 waypoint; + /* 0x02EC */ s16 unk_2EC; + /* 0x02EE */ s16 unk_2EE; + /* 0x02F0 */ Vec3f unk_2F0; + /* 0x02FC */ f32 unk_2FC; + /* 0x0300 */ f32 unk_300; + /* 0x0304 */ f32 unk_304; + /* 0x0308 */ u8 unk_308; + /* 0x030C */ ColliderCylinder collider; + /* 0x0358 */ EnNiwFeather feathers[20]; +} EnNiw; // size = 0x07B8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c b/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c new file mode 100644 index 000000000..fae45335b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.c @@ -0,0 +1,263 @@ +/* + * File: z_en_niw_girl.c + * Overlay: ovl_En_Niw_Girl + * Description: Girl that chases a cuckoo around in Hyrule Market and Kakariko Village + */ + +#include "z_en_niw_girl.h" +#include "objects/object_gr/object_gr.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnNiwGirl_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNiwGirl_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNiwGirl_Update(Actor* thisx, GlobalContext* globalCtx); +void EnNiwGirl_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnNiwGirl_Talk(EnNiwGirl* this, GlobalContext* globalCtx); +void func_80AB94D0(EnNiwGirl* this, GlobalContext* globalCtx); +void func_80AB9210(EnNiwGirl* this, GlobalContext* globalCtx); + +const ActorInit En_Niw_Girl_InitVars = { + ACTOR_EN_NIW_GIRL, + ACTORCAT_NPC, + FLAGS, + OBJECT_GR, + sizeof(EnNiwGirl), + (ActorFunc)EnNiwGirl_Init, + (ActorFunc)EnNiwGirl_Destroy, + (ActorFunc)EnNiwGirl_Update, + (ActorFunc)EnNiwGirl_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 10, 30, 0, { 0, 0, 0 } }, +}; + +void EnNiwGirl_Init(Actor* thisx, GlobalContext* globalCtx) { + EnNiwGirl* this = (EnNiwGirl*)thisx; + s32 pad; + Vec3f vec1; + Vec3f vec2; + s32 pad2; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gNiwGirlSkel, &gNiwGirlRunAnim, this->jointTable, this->morphTable, + 17); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.targetMode = 6; + if (this->actor.params < 0) { + this->actor.params = 0; + } + this->path = ((this->actor.params >> 8) & 0xFF); + this->actor.gravity = -3.0f; + Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + vec2.x = vec2.y = vec2.z = 0.0f; + vec1.x = vec1.y = 0.0f; + vec1.z = 50.0; + Matrix_MultVec3f(&vec1, &vec2); + this->chasedEnNiw = (EnNiw*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_NIW, this->actor.world.pos.x + vec2.x, + this->actor.world.pos.y + vec2.y, this->actor.world.pos.z + vec2.z, 0, this->actor.world.rot.y, 0, 0xA); + if (this->chasedEnNiw != NULL) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ シツレイしちゃうわね!プンプン ☆☆☆☆☆ %d\n" VT_RST, this->actor.params); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ きゃははははは、まてー ☆☆☆☆☆ %d\n" VT_RST, this->path); + osSyncPrintf("\n\n"); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actionFunc = EnNiwGirl_Talk; + } else { + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ なぜか、セットできむぅあせん ☆☆☆☆☆ %d\n" VT_RST, this->actor.params); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ んんがくく ☆☆☆☆☆ %d\n" VT_RST, this->path); + osSyncPrintf("\n\n"); + Actor_Kill(&this->actor); + } +} + +void EnNiwGirl_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnNiwGirl_Jump(EnNiwGirl* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&gNiwGirlRunAnim); + Animation_Change(&this->skelAnime, &gNiwGirlRunAnim, 1.0f, 0.0f, frameCount, 0, -10.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = func_80AB9210; +} + +void func_80AB9210(EnNiwGirl* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->path]; + f32 xDistBetween; + f32 zDistBetween; + + SkelAnime_Update(&this->skelAnime); + Math_ApproachF(&this->actor.speedXZ, 3.0f, 0.2f, 0.4f); + + // Find the X and Z distance between the girl and the cuckoo she is chasing + xDistBetween = this->chasedEnNiw->actor.world.pos.x - this->actor.world.pos.x; + zDistBetween = this->chasedEnNiw->actor.world.pos.z - this->actor.world.pos.z; + if (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE) { + this->chasedEnNiw->path = 0; + } + if (sqrtf(SQ(xDistBetween) + SQ(zDistBetween)) < 70.0f) { + this->chasedEnNiw->path = (this->path + 1); + this->chasedEnNiw->unk_2EC = path->count; + } else if (sqrtf(SQ(xDistBetween) + SQ(zDistBetween)) > 150.0f) { + this->chasedEnNiw->path = 0; + } + + // Change her angle so that she is always facing the cuckoo + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(xDistBetween, zDistBetween) * (0x8000 / M_PI), 3, + this->unk_27C, 0); + Math_ApproachF(&this->unk_27C, 5000.0f, 30.0f, 150.0f); + this->actor.world.rot.y = this->actor.shape.rot.y; + + // Only allow Link to talk to her when she is playing the jumping animation + if ((this->jumpTimer == 0) || (Player_GetMask(globalCtx) != PLAYER_MASK_NONE)) { + this->jumpTimer = 60; + this->actionFunc = EnNiwGirl_Talk; + } +} + +void EnNiwGirl_Talk(EnNiwGirl* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gNiwGirlJumpAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gNiwGirlJumpAnim), 0, + -10.0f); + this->actor.flags |= ACTOR_FLAG_0; + this->actor.textId = 0x7000; + if ((gSaveContext.eventChkInf[8] & 1) && (this->unk_27A == 0)) { + this->actor.textId = 0x70EA; + } + switch (Player_GetMask(globalCtx)) { + case PLAYER_MASK_KEATON: + this->actor.textId = 0x7118; + break; + case PLAYER_MASK_SPOOKY: + this->actor.textId = 0x7119; + break; + case PLAYER_MASK_BUNNY: + case PLAYER_MASK_ZORA: + case PLAYER_MASK_GERUDO: + this->actor.textId = 0x711A; + break; + case PLAYER_MASK_SKULL: + case PLAYER_MASK_GORON: + case PLAYER_MASK_TRUTH: + this->actor.textId = 0x711B; + break; + } + this->unk_270 = 6; + this->actionFunc = func_80AB94D0; +} + +void func_80AB94D0(EnNiwGirl* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE) { + this->chasedEnNiw->path = 0; + } + Math_ApproachZeroF(&this->actor.speedXZ, 0.8f, 0.2f); + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->actor.textId == 0x70EA) { + this->unk_27A = 1; + } + } else { + if ((this->jumpTimer == 0) && Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + this->jumpTimer = Rand_ZeroFloat(100.0f) + 250.0f; + this->actionFunc = EnNiwGirl_Jump; + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } +} + +void EnNiwGirl_Update(Actor* thisx, GlobalContext* globalCtx) { + EnNiwGirl* this = (EnNiwGirl*)thisx; + EnNiwGirlActionFunc tempActionFunc; + Player* player = GET_PLAYER(globalCtx); + + Actor_SetScale(&this->actor, 0.013f); + this->unkUpTimer++; + tempActionFunc = func_80AB94D0; + if (this->blinkTimer == 0) { + this->eyeIndex++; + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + this->unk_280 = 30.0f; + Actor_SetFocus(&this->actor, 30.0f); + if (tempActionFunc == this->actionFunc) { + this->unk_2D4.unk_18 = player->actor.world.pos; + if (!LINK_IS_ADULT) { + this->unk_2D4.unk_18.y = player->actor.world.pos.y - 10.0f; + } + func_80034A14(&this->actor, &this->unk_2D4, 2, 4); + this->unk_260 = this->unk_2D4.unk_08; + this->unk_266 = this->unk_2D4.unk_0E; + } else { + Math_SmoothStepToS(&this->unk_266.y, 0, 5, 3000, 0); + Math_SmoothStepToS(&this->unk_260.y, 0, 5, 3000, 0); + Math_SmoothStepToS(&this->unk_260.z, 0, 5, 3000, 0); + } + if (this->blinkTimer != 0) { + this->blinkTimer--; + } + if (this->jumpTimer != 0) { + this->jumpTimer--; + } + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 100.0f, 100.0f, 200.0f, 0x1C); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnNiwGirlOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnNiwGirl* this = (EnNiwGirl*)thisx; + + if (limbIndex == 3) { + rot->x += this->unk_266.y; + } + if (limbIndex == 4) { + rot->x += this->unk_260.y; + rot->z += this->unk_260.z; + } + return false; +} + +static Vec3f sConstVec3f = { 0.2f, 0.2f, 0.2f }; + +void EnNiwGirl_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gNiwGirlEyeOpenTex, gNiwGirlEyeHalfTex, gNiwGirlEyeClosedTex }; + EnNiwGirl* this = (EnNiwGirl*)thisx; + s32 pad; + Vec3f sp4C = sConstVec3f; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_niw_girl.c", 573); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnNiwGirlOverrideLimbDraw, NULL, this); + func_80033C30(&this->actor.world.pos, &sp4C, 255, globalCtx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_niw_girl.c", 592); +} diff --git a/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.h b/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.h new file mode 100644 index 000000000..d931973c3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Niw_Girl/z_en_niw_girl.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_NIW_GIRL_H +#define Z_EN_NIW_GIRL_H + +#include "ultra64.h" +#include "global.h" +#include "../ovl_En_Niw/z_en_niw.h" + +struct EnNiwGirl; + +typedef void (*EnNiwGirlActionFunc)(struct EnNiwGirl*, GlobalContext*); + +typedef struct EnNiwGirl { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ EnNiwGirlActionFunc actionFunc; + /* 0x0260 */ Vec3s unk_260; + /* 0x0266 */ Vec3s unk_266; + /* 0x026C */ s16 jumpTimer; // Controls how many frames she jumps for and how long until she jumps again + /* 0x026E */ s16 unkUpTimer; + /* 0x0270 */ s16 unk_270; + /* 0x0272 */ s16 eyeIndex; + /* 0x0274 */ s16 blinkTimer; + /* 0x0276 */ s16 path; + /* 0x0278 */ s16 unk_278; + /* 0x027A */ s16 unk_27A; + /* 0x027C */ f32 unk_27C; + /* 0x0280 */ f32 unk_280; + /* 0x0284 */ EnNiw* chasedEnNiw; + /* 0x0288 */ ColliderCylinder collider; + /* 0x02D4 */ struct_80034A14_arg1 unk_2D4; +} EnNiwGirl; // size = 0x02FC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c new file mode 100644 index 000000000..272e7c693 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.c @@ -0,0 +1,585 @@ +#include "z_en_niw_lady.h" +#include "objects/object_ane/object_ane.h" +#include "objects/object_os_anime/object_os_anime.h" +#include "overlays/actors/ovl_En_Niw/z_en_niw.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnNiwLady_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNiwLady_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNiwLady_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80AB9F24(EnNiwLady* this, GlobalContext* globalCtx); +void EnNiwLady_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_80ABA21C(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABAD38(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABA778(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABA878(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABA9B8(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABAB08(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABAC00(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABAA9C(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABAC84(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABA244(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABA654(EnNiwLady* this, GlobalContext* globalCtx); +void func_80ABAD7C(EnNiwLady* this, GlobalContext* globalCtx); + +const ActorInit En_Niw_Lady_InitVars = { + ACTOR_EN_NIW_LADY, + ACTORCAT_NPC, + FLAGS, + OBJECT_ANE, + sizeof(EnNiwLady), + (ActorFunc)EnNiwLady_Init, + (ActorFunc)EnNiwLady_Destroy, + (ActorFunc)EnNiwLady_Update, + NULL, + NULL, +}; + +static s16 sMissingCuccoTextIds[] = { + 0x5036, 0x5070, 0x5072, 0x5037, 0x5038, 0x5039, 0x503A, 0x503B, 0x503D, 0x503C, +}; + +static s16 D_80ABB3B4[] = { + 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 10, 10, 0, { 0, 0, 0 } }, +}; + +void EnNiwLady_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNiwLady* this = (EnNiwLady*)thisx; + + this->objectAneIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_ANE); + this->objectOsAnimeIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_OS_ANIME); + if ((this->objectOsAnimeIndex < 0) || (this->objectAneIndex < 0)) { + Actor_Kill(thisx); + return; + } + this->unk_278 = 0; + if (globalCtx->sceneNum == SCENE_LABO) { + this->unk_278 = 1; + } + if ((this->unk_278 != 0) && IS_DAY) { + Actor_Kill(thisx); + return; + } + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ねぇちゃんうっふん ☆☆☆☆☆ %d\n" VT_RST, this->unk_278); + osSyncPrintf("\n\n"); + this->actionFunc = func_80AB9F24; + thisx->uncullZoneForward = 600.0f; +} + +void EnNiwLady_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnNiwLady* this = (EnNiwLady*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnNiwLady_ChoseAnimation(EnNiwLady* this, GlobalContext* globalCtx, s32 arg2) { + f32 frames; + + if (Text_GetFaceReaction(globalCtx, 8) != 0) { + arg2 = 8; + } + if (arg2 != this->unk_270) { + this->unk_275 = 0; + this->unk_276 = 1; + this->unk_270 = arg2; + switch (arg2) { + case 10: + this->unk_275 = 1; + case 9: + frames = Animation_GetLastFrame(&gObjOsAnim_07D0); + Animation_Change(&this->skelAnime, &gObjOsAnim_07D0, 1.0f, 0.0f, frames, ANIMMODE_LOOP, -10.0f); + break; + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 8: + case 21: + case 22: + case 24: + case 29: + frames = Animation_GetLastFrame(&gObjOsAnim_9F94); + Animation_Change(&this->skelAnime, &gObjOsAnim_9F94, 1.0f, 0.0f, frames, ANIMMODE_LOOP, -10.0f); + break; + case 7: + case 20: + case 23: + case 25: + case 26: + case 27: + case 28: + frames = Animation_GetLastFrame(&gObjOsAnim_0718); + Animation_Change(&this->skelAnime, &gObjOsAnim_0718, 1.0f, 0.0f, frames, ANIMMODE_LOOP, -10.0f); + break; + case 100: + frames = Animation_GetLastFrame(&gObjOsAnim_A630); + Animation_Change(&this->skelAnime, &gObjOsAnim_A630, 1.0f, 0.0f, frames, ANIMMODE_LOOP, -10.0f); + this->unk_276 = 0; + break; + } + return; + } +} + +void func_80AB9F24(EnNiwLady* this, GlobalContext* globalCtx) { + f32 frames; + s32 pad; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objectAneIndex) && + Object_IsLoaded(&globalCtx->objectCtx, this->objectOsAnimeIndex)) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objectAneIndex].segment); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCuccoLadySkel, NULL, this->jointTable, this->morphTable, 16); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objectOsAnimeIndex].segment); + this->unk_27E = 1; + this->actor.gravity = -3.0f; + Actor_SetScale(&this->actor, 0.01f); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->unk_272 = 0; + this->actor.targetMode = 6; + this->actor.draw = EnNiwLady_Draw; + switch (this->unk_278) { + case 0: + if (!(gSaveContext.itemGetInf[0] & 0x1000) && !LINK_IS_ADULT) { + frames = Animation_GetLastFrame(&gObjOsAnim_A630); + Animation_Change(&this->skelAnime, &gObjOsAnim_A630, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, 0.0f); + } else { + frames = Animation_GetLastFrame(&gObjOsAnim_07D0); + Animation_Change(&this->skelAnime, &gObjOsAnim_07D0, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, 0.0f); + } + if (LINK_IS_ADULT) { + this->actionFunc = func_80ABA778; + } else { + this->actionFunc = func_80ABA21C; + } + return; + case 1: + frames = Animation_GetLastFrame(&gObjOsAnim_07D0); + Animation_Change(&this->skelAnime, &gObjOsAnim_07D0, 1.0f, 0.0f, (s16)frames, ANIMMODE_LOOP, 0.0f); + this->actionFunc = func_80ABAD38; + return; + } + } +} + +void func_80ABA21C(EnNiwLady* this, GlobalContext* globalCtx) { + this->actor.textId = sMissingCuccoTextIds[0]; + this->unk_262 = TEXT_STATE_DONE; + this->actionFunc = func_80ABA244; +} + +void func_80ABA244(EnNiwLady* this, GlobalContext* globalCtx) { + EnNiw* currentCucco; + s32 phi_s1; + + this->cuccosInPen = 0; + currentCucco = (EnNiw*)globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + while (currentCucco != NULL) { + if (currentCucco->actor.id == ACTOR_EN_NIW) { + if ((fabsf(currentCucco->actor.world.pos.x - 330.0f) < 90.0f) && + (fabsf(currentCucco->actor.world.pos.z - 1610.0f) < 190.0f)) { + if (this->unk_26C == 0) { + gSaveContext.infTable[25] |= D_80ABB3B4[currentCucco->unk_2AA]; + if (BREG(1) != 0) { + // "GET inside the chicken fence!" + osSyncPrintf(VT_FGCOL(GREEN) "☆ 鶏柵内GET!☆ %x\n" VT_RST, + D_80ABB3B4[currentCucco->unk_2AA]); + } + } + this->cuccosInPen++; + } else if (this->unk_26C == 0) { + gSaveContext.infTable[25] &= ~D_80ABB3B4[currentCucco->unk_2AA]; + } + } + currentCucco = (EnNiw*)currentCucco->actor.next; + } + if (BREG(7) != 0) { + this->cuccosInPen = BREG(7) - 1; + } + phi_s1 = this->cuccosInPen; + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE)) { + this->unk_26E = 101; + } + if (this->cuccosInPen >= 7) { + phi_s1 = 8; + if ((this->unk_26C < 2) && (this->unk_26C == 0)) { + phi_s1 = 7; + } + } + if ((this->unk_26C != 0) && (phi_s1 < 7)) { + phi_s1 = 9; + } + this->actor.textId = sMissingCuccoTextIds[phi_s1]; + if (Text_GetFaceReaction(globalCtx, 8) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 8); + this->unk_262 = TEXT_STATE_DONE; + } + if ((this->unk_26C != 0) && (phi_s1 != 9)) { + phi_s1 = 10; + this->unk_26E = 11; + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ねぇちゃん選択\t ☆☆☆☆ %d\n" VT_RST, phi_s1); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ねぇちゃんハート ☆☆☆☆ %d\n" VT_RST, this->unk_26C); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ねぇちゃん保存 ☆☆☆☆ %d\n" VT_RST, this->unk_26A); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ねぇちゃん今\t ☆☆☆☆ %d\n" VT_RST, this->cuccosInPen); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ this->actor.talk_message ☆☆ %x\n" VT_RST, this->actor.textId); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ this->message_end_code ☆☆ %d\n" VT_RST, this->unk_262); + osSyncPrintf("\n\n"); + if (Text_GetFaceReaction(globalCtx, 8) == 0) { + if (this->actor.textId == 0x503C) { + func_80078884(NA_SE_SY_ERROR); + this->unk_26C = 2; + this->unk_262 = TEXT_STATE_EVENT; + this->actionFunc = func_80ABA654; + return; + } + this->unk_26E = phi_s1 + 1; + if (phi_s1 == 7) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + this->unk_26C = 1; + this->unk_262 = TEXT_STATE_EVENT; + this->unk_26A = this->cuccosInPen; + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 柵内BIT変更前 ☆☆ %x\n" VT_RST, gSaveContext.infTable[25]); + gSaveContext.infTable[25] &= 0x1FF; + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 柵内BIT変更後 ☆☆ %x\n" VT_RST, gSaveContext.infTable[25]); + osSyncPrintf("\n\n"); + this->actionFunc = func_80ABA654; + return; + } + if (this->unk_26A != this->cuccosInPen) { + if (this->cuccosInPen < this->unk_26A) { + func_80078884(NA_SE_SY_ERROR); + } else if (phi_s1 + 1 < 9) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } + if (this->unk_26A < this->cuccosInPen) { + this->unk_26A = this->cuccosInPen; + return; + } + } + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } +} + +void func_80ABA654(EnNiwLady* this, GlobalContext* globalCtx) { + if (this->unk_262 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ハート ☆☆☆☆☆ %d\n" VT_RST, this->unk_26C); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 爆弾 ☆☆☆☆☆ %d\n" VT_RST, this->unk_272); + osSyncPrintf("\n\n"); + this->unk_26E = 0xB; + if (!(gSaveContext.itemGetInf[0] & 0x1000)) { + this->actor.parent = NULL; + this->getItemId = GI_BOTTLE; + func_8002F434(&this->actor, globalCtx, GI_BOTTLE, 100.0f, 50.0f); + this->actionFunc = func_80ABAC00; + return; + } + if (this->unk_26C == 1) { + this->getItemId = GI_RUPEE_PURPLE; + func_8002F434(&this->actor, globalCtx, GI_RUPEE_PURPLE, 100.0f, 50.0f); + this->actionFunc = func_80ABAC00; + } + this->actionFunc = func_80ABA244; + } +} + +static s16 sTradeItemTextIds[] = { 0x503E, 0x503F, 0x5047, 0x5040, 0x5042, 0x5043, + 0x5044, 0x00CF, 0x5045, 0x5042, 0x5027 }; + +void func_80ABA778(EnNiwLady* this, GlobalContext* globalCtx) { + // "☆☆☆☆☆ Adult message check ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ アダルトメッセージチェック ☆☆☆☆☆ \n" VT_RST); + this->unk_262 = TEXT_STATE_DONE; + this->unk_273 = 0; + if (!(gSaveContext.itemGetInf[2] & 0x1000)) { + if (this->unk_274 != 0) { + this->unk_27A = 1; + } else { + this->unk_27A = 0; + } + this->unk_273 = 1; + this->unk_262 = TEXT_STATE_CHOICE; + } else { + this->unk_27A = 2; + if (!(gSaveContext.itemGetInf[2] & 0x4000)) { + this->unk_27A = 3; + if (gSaveContext.eventChkInf[6] & 0x400) { + this->unk_27A = 9; + if (this->unk_277 != 0) { + this->unk_27A = 10; + } + } else { + this->unk_27A = 4; + } + } + } + this->actor.textId = sTradeItemTextIds[this->unk_27A]; + this->actionFunc = func_80ABA878; +} + +void func_80ABA878(EnNiwLady* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s8 playerExchangeItemId; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE)) { + this->unk_26E = 11; + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + playerExchangeItemId = func_8002F368(globalCtx); + if ((playerExchangeItemId == 6) && (gSaveContext.eventChkInf[6] & 0x400)) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + player->actor.textId = sTradeItemTextIds[5]; + this->unk_26E = this->unk_27A + 21; + this->unk_262 = TEXT_STATE_CHOICE; + this->actionFunc = func_80ABAB08; + } else if (playerExchangeItemId != 0) { + player->actor.textId = sTradeItemTextIds[7]; + this->unk_26E = this->unk_27A + 21; + } else { + this->unk_274 = 1; + this->unk_26E = this->unk_27A + 21; + this->actionFunc = !this->unk_273 ? func_80ABA778 : func_80ABA9B8; + } + } else { + func_8002F298(&this->actor, globalCtx, 50.0f, 6); + } +} + +void func_80ABA9B8(EnNiwLady* this, GlobalContext* globalCtx) { + if ((this->unk_262 == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + Message_CloseTextbox(globalCtx); + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, GI_POCKET_EGG, 200.0f, 100.0f); + this->actionFunc = func_80ABAC00; + break; + case 1: + this->actor.textId = sTradeItemTextIds[3]; + this->unk_26E = this->unk_27A + 21; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_262 = TEXT_STATE_EVENT; + this->actionFunc = func_80ABAA9C; + break; + } + } +} + +void func_80ABAA9C(EnNiwLady* this, GlobalContext* globalCtx) { + this->unk_26E = 11; + if ((this->unk_262 == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = func_80ABA778; + } +} + +void func_80ABAB08(EnNiwLady* this, GlobalContext* globalCtx) { + if ((this->unk_262 == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + Message_CloseTextbox(globalCtx); + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, GI_COJIRO, 200.0f, 100.0f); + this->actionFunc = func_80ABAC00; + break; + case 1: + Message_CloseTextbox(globalCtx); + this->unk_277 = 1; + this->actor.textId = sTradeItemTextIds[8]; + this->unk_26E = this->unk_27A + 21; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_262 = TEXT_STATE_EVENT; + this->actionFunc = func_80ABAA9C; + break; + } + } +} + +void func_80ABAC00(EnNiwLady* this, GlobalContext* globalCtx) { + s32 getItemId; + + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = func_80ABAC84; + } else { + getItemId = this->getItemId; + if (LINK_IS_ADULT) { + getItemId = !(gSaveContext.itemGetInf[2] & 0x1000) ? GI_POCKET_EGG : GI_COJIRO; + } + func_8002F434(&this->actor, globalCtx, getItemId, 200.0f, 100.0f); + } +} + +void func_80ABAC84(EnNiwLady* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_DONE) || !Message_ShouldAdvance(globalCtx)) { + return; + } + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); + if (LINK_IS_ADULT) { + if (!(gSaveContext.itemGetInf[2] & 0x1000)) { + gSaveContext.itemGetInf[2] |= 0x1000; + } else { + gSaveContext.itemGetInf[2] |= 0x4000; + } + this->actionFunc = func_80ABA778; + } else { + gSaveContext.itemGetInf[0] |= 0x1000; + this->unk_262 = TEXT_STATE_DONE; + this->actionFunc = func_80ABA244; + } +} + +void func_80ABAD38(EnNiwLady* this, GlobalContext* globalCtx) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 通常メッセージチェック ☆☆☆☆☆ \n" VT_RST); + this->unk_262 = TEXT_STATE_DONE; + this->actionFunc = func_80ABAD7C; +} + +void func_80ABAD7C(EnNiwLady* this, GlobalContext* globalCtx) { + this->actor.textId = 0x503D; + if (Text_GetFaceReaction(globalCtx, 8) != 0) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 8); + } + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE)) { + this->unk_26E = 8; + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_274 = 1; + this->unk_26E = this->unk_27A + 9; + this->actionFunc = func_80ABAD38; + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } +} + +void EnNiwLady_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNiwLady* this = (EnNiwLady*)thisx; + Player* player = GET_PLAYER(globalCtx); + + Actor_SetFocus(thisx, 60.0f); + this->unk_288.unk_18 = player->actor.world.pos; + if (!LINK_IS_ADULT) { + this->unk_288.unk_18.y = player->actor.world.pos.y - 10.0f; + } + func_80034A14(thisx, &this->unk_288, 2, 4); + this->unk_254 = this->unk_288.unk_08; + this->unk_25A = this->unk_288.unk_0E; + if (this->unk_276 == 0) { + Math_SmoothStepToS(&this->unk_254.y, 0, 5, 3000, 0); + } + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objectOsAnimeIndex].segment); + if (this->objectOsAnimeIndex >= 0) { + if (this->unk_27E != 0) { + if (this->unk_26E != 0) { + this->unk_26E--; + EnNiwLady_ChoseAnimation(this, globalCtx, this->unk_26E); + this->unk_26E = 0; + } + SkelAnime_Update(&this->skelAnime); + } + this->objectAneIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_ANE); + if (this->objectAneIndex >= 0) { + this->actionFunc(this, globalCtx); + if (this->unusedTimer2 != 0) { + this->unusedTimer2--; + } + if (this->unusedRandomTimer != 0) { + this->unusedRandomTimer--; + } + this->unusedTimer++; + if (this->unusedRandomTimer == 0) { + this->faceState++; + if (this->faceState >= 3) { + this->faceState = 0; + this->unusedRandomTimer = ((s16)Rand_ZeroFloat(60.0f) + 0x14); + } + } + Actor_UpdateBgCheckInfo(globalCtx, thisx, 20.0f, 20.0f, 60.0f, 0x1D); + Collider_UpdateCylinder(thisx, &this->collider); + if (1) {} + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +Gfx* func_80ABB0A0(GraphicsContext* gfxCtx) { + Gfx* dList; + + dList = Graph_Alloc(gfxCtx, sizeof(Gfx)); + gSPEndDisplayList(dList); + return dList; +} + +s32 EnNiwLady_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnNiwLady* this = (EnNiwLady*)thisx; + s32 pad; + + if (limbIndex == 15) { + rot->x += this->unk_254.y; + rot->z += this->unk_254.x; + } + if (limbIndex == 8) { + rot->x += this->unk_25A.y; + } + if (this->unk_275 != 0) { + if ((limbIndex == 8) || (limbIndex == 10) || (limbIndex == 13)) { + rot->y += (Math_SinS((globalCtx->state.frames * ((limbIndex * 0x32) + 0x814))) * 200.0f); + rot->z += (Math_CosS((globalCtx->state.frames * ((limbIndex * 0x32) + 0x940))) * 200.0f); + } + } + return false; +} + +void EnNiwLady_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sEyeTextures[] = { gCuccoLadyEyeOpenTex, gCuccoLadyEyeHalfTex, gCuccoLadyEyeClosedTex }; + EnNiwLady* this = (EnNiwLady*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_niw_lady.c", 1347); + if (this->unk_27E != 0) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->faceState])); + gSPSegment(POLY_OPA_DISP++, 0x0C, func_80ABB0A0(globalCtx->state.gfxCtx)); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnNiwLady_OverrideLimbDraw, NULL, this); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_niw_lady.c", 1370); +} diff --git a/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.h b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.h new file mode 100644 index 000000000..57ba86f2d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Niw_Lady/z_en_niw_lady.h @@ -0,0 +1,45 @@ +#ifndef Z_EN_NIW_LADY_H +#define Z_EN_NIW_LADY_H + +#include "ultra64.h" +#include "global.h" + +struct EnNiwLady; + +typedef void (*EnNiwLadyActionFunc)(struct EnNiwLady*, GlobalContext*); + +typedef struct EnNiwLady { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s morphTable[16]; + /* 0x0250 */ EnNiwLadyActionFunc actionFunc; + /* 0x0254 */ Vec3s unk_254; + /* 0x025A */ Vec3s unk_25A; + /* 0x0260 */ s16 unusedTimer; + /* 0x0262 */ s16 unk_262; // "message_end_code" + /* 0x0264 */ s16 unusedTimer2; + /* 0x0266 */ s16 unusedRandomTimer; + /* 0x0268 */ s16 cuccosInPen; + /* 0x026A */ s16 unk_26A; + /* 0x026C */ s16 unk_26C; + /* 0x026E */ s16 unk_26E; + /* 0x0270 */ s16 unk_270; + /* 0x0272 */ u8 unk_272; + /* 0x0273 */ u8 unk_273; + /* 0x0274 */ u8 unk_274; + /* 0x0275 */ u8 unk_275; + /* 0x0276 */ u8 unk_276; + /* 0x0277 */ u8 unk_277; + /* 0x0278 */ s16 unk_278; + /* 0x027A */ s16 unk_27A; + /* 0x027C */ s16 faceState; + /* 0x027E */ s16 unk_27E; + /* 0x0280 */ s8 objectAneIndex; + /* 0x0281 */ s8 objectOsAnimeIndex; + /* 0x0284 */ s32 getItemId; + /* 0x0288 */ struct_80034A14_arg1 unk_288; + /* 0x02B0 */ ColliderCylinder collider; +} EnNiwLady; // size = 0x02FC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c b/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c new file mode 100644 index 000000000..8d6a4c41c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c @@ -0,0 +1,178 @@ +/* + * File: z_en_nutsball.c + * Overlay: ovl_En_Nutsball + * Description: Projectile fired by deku scrubs + */ + +#include "z_en_nutsball.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" +#include "objects/object_dekunuts/object_dekunuts.h" +#include "objects/object_hintnuts/object_hintnuts.h" +#include "objects/object_shopnuts/object_shopnuts.h" +#include "objects/object_dns/object_dns.h" +#include "objects/object_dnk/object_dnk.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnNutsball_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNutsball_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNutsball_Update(Actor* thisx, GlobalContext* globalCtx); +void EnNutsball_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80ABBB34(EnNutsball* this, GlobalContext* globalCtx); +void func_80ABBBA8(EnNutsball* this, GlobalContext* globalCtx); + +const ActorInit En_Nutsball_InitVars = { + ACTOR_EN_NUTSBALL, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnNutsball), + (ActorFunc)EnNutsball_Init, + (ActorFunc)EnNutsball_Destroy, + (ActorFunc)EnNutsball_Update, + (ActorFunc)NULL, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_WOOD, + BUMP_ON, + OCELEM_ON, + }, + { 13, 13, 0, { 0 } }, +}; + +static s16 sObjectIDs[] = { + OBJECT_DEKUNUTS, OBJECT_HINTNUTS, OBJECT_SHOPNUTS, OBJECT_DNS, OBJECT_DNK, +}; + +static Gfx* sDLists[] = { + gDekuNutsDekuNutDL, gHintNutsNutDL, gBusinessScrubDekuNutDL, gDntJijiNutDL, gDntStageNutDL, +}; + +void EnNutsball_Init(Actor* thisx, GlobalContext* globalCtx) { + EnNutsball* this = (EnNutsball*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 400.0f, ActorShadow_DrawCircle, 13.0f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->objBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectIDs[this->actor.params]); + + if (this->objBankIndex < 0) { + Actor_Kill(&this->actor); + } else { + this->actionFunc = func_80ABBB34; + } +} + +void EnNutsball_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnNutsball* this = (EnNutsball*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80ABBB34(EnNutsball* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->actor.objBankIndex = this->objBankIndex; + this->actor.draw = EnNutsball_Draw; + this->actor.shape.rot.y = 0; + this->timer = 30; + this->actionFunc = func_80ABBBA8; + this->actor.speedXZ = 10.0f; + } +} + +void func_80ABBBA8(EnNutsball* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3s sp4C; + Vec3f sp40; + + this->timer--; + + if (this->timer == 0) { + this->actor.gravity = -1.0f; + } + + this->actor.home.rot.z += 0x2AA8; + + if ((this->actor.bgCheckFlags & 8) || (this->actor.bgCheckFlags & 1) || (this->collider.base.atFlags & AT_HIT) || + (this->collider.base.acFlags & AC_HIT) || (this->collider.base.ocFlags1 & OC1_HIT)) { + // Checking if the player is using a shield that reflects projectiles + // And if so, reflects the projectile on impact + if ((player->currentShield == PLAYER_SHIELD_DEKU) || + ((player->currentShield == PLAYER_SHIELD_HYLIAN) && LINK_IS_ADULT)) { + if ((this->collider.base.atFlags & AT_HIT) && (this->collider.base.atFlags & AT_TYPE_ENEMY) && + (this->collider.base.atFlags & AT_BOUNCED)) { + this->collider.base.atFlags &= ~AT_TYPE_ENEMY & ~AT_BOUNCED & ~AT_HIT; + this->collider.base.atFlags |= AT_TYPE_PLAYER; + + this->collider.info.toucher.dmgFlags = 2; + Matrix_MtxFToYXZRotS(&player->shieldMf, &sp4C, 0); + this->actor.world.rot.y = sp4C.y + 0x8000; + this->timer = 30; + return; + } + } + + sp40.x = this->actor.world.pos.x; + sp40.y = this->actor.world.pos.y + 4; + sp40.z = this->actor.world.pos.z; + + EffectSsHahen_SpawnBurst(globalCtx, &sp40, 6.0f, 0, 7, 3, 15, HAHEN_OBJECT_DEFAULT, 10, NULL); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EN_OCTAROCK_ROCK); + Actor_Kill(&this->actor); + } else { + if (this->timer == -300) { + Actor_Kill(&this->actor); + } + } +} + +void EnNutsball_Update(Actor* thisx, GlobalContext* globalCtx) { + EnNutsball* this = (EnNutsball*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + if (!(player->stateFlags1 & 0x300000C0) || (this->actionFunc == func_80ABBB34)) { + this->actionFunc(this, globalCtx); + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10, sCylinderInit.dim.radius, sCylinderInit.dim.height, 5); + Collider_UpdateCylinder(&this->actor, &this->collider); + + this->actor.flags |= ACTOR_FLAG_24; + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnNutsball_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_nutsball.c", 327); + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(thisx->home.rot.z * 9.58738e-05f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_nutsball.c", 333), + G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, sDLists[thisx->params]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_nutsball.c", 337); +} diff --git a/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.h b/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.h new file mode 100644 index 000000000..5c4c09928 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.h @@ -0,0 +1,19 @@ +#ifndef Z_EN_NUTSBALL_H +#define Z_EN_NUTSBALL_H + +#include "ultra64.h" +#include "global.h" + +struct EnNutsball; + +typedef void (*EnNutsballActionFunc)(struct EnNutsball*, GlobalContext*); + +typedef struct EnNutsball { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnNutsballActionFunc actionFunc; + /* 0x0150 */ s8 objBankIndex; + /* 0x0152 */ s16 timer; + /* 0x0154 */ ColliderCylinder collider; +} EnNutsball; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.c b/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.c new file mode 100644 index 000000000..3d5f42cb2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.c @@ -0,0 +1,256 @@ +/* + * File: z_en_nwc.c + * Overlay: ovl_En_Nwc + * Description: Cluster of cucco chicks. Unfinished. + */ + +#include "z_en_nwc.h" +#include "objects/object_nwc/object_nwc.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnNwc_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNwc_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNwc_Update(Actor* thisx, GlobalContext* globalCtx); +void EnNwc_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnNwc_SetUpdate(EnNwc* this, EnNwcUpdateFunc updateFunc); +void EnNwc_ChickNoop(EnNwcChick* chick, EnNwc* this, GlobalContext* globalCtx); +void EnNwc_ChickBgCheck(EnNwcChick* chick, GlobalContext* globalCtx); +void EnNwc_ChickFall(EnNwcChick* chick, EnNwc* this, GlobalContext* globalCtx); +void EnNwc_UpdateChicks(EnNwc* this, GlobalContext* globalCtx); +void EnNwc_DrawChicks(EnNwc* this, GlobalContext* globalCtx); +void EnNwc_Idle(EnNwc* this, GlobalContext* globalCtx); + +#define CHICK_BG_FLOOR (1 << 0) +#define CHICK_BG_WALL (1 << 1) + +typedef enum { + /* 0 */ CHICK_NONE, + /* 1 */ CHICK_NORMAL +} ChickTypes; + +const ActorInit En_Nwc_InitVars = { + ACTOR_EN_NWC, + ACTORCAT_PROP, + FLAGS, + OBJECT_NWC, + sizeof(EnNwc), + (ActorFunc)EnNwc_Init, + (ActorFunc)EnNwc_Destroy, + (ActorFunc)EnNwc_Update, + (ActorFunc)EnNwc_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementInit = { + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 10 }, 100 }, +}; + +static ColliderJntSphInitType1 sJntSphInit = { + { + COLTYPE_HIT3, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_JNTSPH, + }, + 16, + NULL, +}; + +void EnNwc_SetUpdate(EnNwc* this, EnNwcUpdateFunc updateFunc) { + this->updateFunc = updateFunc; +} + +void EnNwc_ChickNoop(EnNwcChick* chick, EnNwc* this, GlobalContext* globalCtx) { +} + +void EnNwc_ChickBgCheck(EnNwcChick* chick, GlobalContext* globalCtx) { + CollisionPoly* outPoly; + s32 bgId; + Vec3f outPos; + f32 dy; + + chick->bgFlags &= ~CHICK_BG_WALL & ~CHICK_BG_FLOOR; + outPos.x = chick->pos.x; + outPos.y = chick->pos.y; + outPos.z = chick->pos.z; + if (BgCheck_EntitySphVsWall1(&globalCtx->colCtx, &outPos, &chick->pos, &chick->lastPos, 10.0f, &chick->floorPoly, + 20.0f)) { + chick->bgFlags |= CHICK_BG_WALL; + } + //! @bug The use of outPos here is totally wrong. Even if it didn't get overwritten + // by the wall check, it should add an offset to the y-value so the raycast + // doesn't go through the floor and cause the chicks to ignore all floors. + chick->floorY = BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &outPoly, &bgId, &outPos); + dy = chick->floorY - chick->pos.y; + if ((0.0f <= dy) && (dy < 40.0f)) { + chick->pos.y = chick->floorY; + chick->bgFlags |= CHICK_BG_FLOOR; + } +} + +void EnNwc_ChickFall(EnNwcChick* chick, EnNwc* this, GlobalContext* globalCtx) { + chick->velY -= 0.1f; + if (chick->velY < -10.0f) { + chick->velY = -10.0f; + } + chick->pos.y += chick->velY; + EnNwc_ChickBgCheck(chick, globalCtx); + if (chick) {} // Needed for matching. Possibly from remnant of unfinished code? +} + +void EnNwc_UpdateChicks(EnNwc* this, GlobalContext* globalCtx) { + static EnNwcChickFunc chickActionFuncs[] = { EnNwc_ChickNoop, EnNwc_ChickFall }; + EnNwcChick* chick = this->chicks; + ColliderJntSphElement* element = this->collider.elements; + Vec3f prevChickPos; + s32 i; + f32 test; + + prevChickPos.y = 99999.9f; + for (i = 0; i < this->count; i++, prevChickPos = chick->pos, chick++, element++) { + Math_Vec3f_Copy(&chick->lastPos, &chick->pos); + + chickActionFuncs[chick->type](chick, this, globalCtx); + + element->dim.worldSphere.center.x = chick->pos.x; + element->dim.worldSphere.center.y = chick->pos.y; + element->dim.worldSphere.center.z = chick->pos.z; + + test = chick->pos.y - prevChickPos.y; + if (fabsf(test) < 10.0f) { + f32 dx = chick->pos.x - prevChickPos.x; + f32 dz = chick->pos.z - prevChickPos.z; + + test = SQ(dx) + SQ(dz); + if (test < SQ(10.0f)) { + if (test != 0.0f) { + chick->pos.x += dx / sqrtf(test); + chick->pos.z += dz / sqrtf(test); + } else { + chick->pos.x += 1.0f; + chick->pos.z += 1.0f; + } + } + } + } +} + +void EnNwc_DrawChicks(EnNwc* this, GlobalContext* globalCtx) { + s32 i; + Gfx* dList1; + Gfx* dList2; + Gfx* dList3; + MtxF floorMat; + EnNwcChick* chick; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_nwc.c", 316); + func_80093C80(globalCtx); + + dList1 = POLY_XLU_DISP; + dList2 = dList1 + 3 * this->count + 1; + dList3 = dList2 + 2 * this->count + 1; + + gSPDisplayList(dList1++, gCuccoChickSetupBodyDL); + gSPDisplayList(dList2++, gCuccoChickSetupEyeDL); + gSPDisplayList(dList3++, gCuccoChickSetupBeakDL); + + chick = this->chicks; + for (i = 0; i < this->count; i++, chick++) { + if (chick->type != CHICK_NONE) { + Mtx* mtx; + + Matrix_SetTranslateRotateYXZ(chick->pos.x, chick->pos.y + chick->height, chick->pos.z, &chick->rot); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + mtx = Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_nwc.c", 346); + gDPSetEnvColor(dList1++, 0, 100, 255, 255); + gSPMatrix(dList1++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(dList1++, gCuccoChickBodyDL); + gSPMatrix(dList2++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(dList2++, gCuccoChickEyesDL); + gSPMatrix(dList3++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(dList3++, gCuccoChickBeakDL); + } + } + + chick = this->chicks; + POLY_XLU_DISP = dList3; + func_80094044(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, gCuccoChickSetupShadowDL); + + for (i = 0; i < this->count; i++, chick++) { + if ((chick->type != CHICK_NONE) && (chick->floorPoly != NULL)) { + func_80038A28(chick->floorPoly, chick->pos.x, chick->floorY, chick->pos.z, &floorMat); + Matrix_Put(&floorMat); + Matrix_RotateY(chick->rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_nwc.c", 388), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCuccoChickShadowDL); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_nwc.c", 395); +} + +void EnNwc_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNwc* this = (EnNwc*)thisx; + ColliderJntSphElementInit elementInits[16]; + ColliderJntSphElementInit* element; + EnNwcChick* chick; + s32 i; + + element = sJntSphInit.elements = elementInits; + for (i = 0; i < 16; i++, element++) { + *element = sJntSphElementInit; + } + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSphAllocType1(globalCtx, &this->collider, &this->actor, &sJntSphInit); + this->count = 16; + chick = this->chicks; + for (i = 0; i < this->count; i++, chick++) { + chick->type = CHICK_NORMAL; + chick->pos.x = thisx->world.pos.x + ((Rand_ZeroOne() * 100.0f) - 50.0f); + chick->pos.y = thisx->world.pos.y + 20.0f; + chick->pos.z = thisx->world.pos.z + ((Rand_ZeroOne() * 100.0f) - 50.0f); + chick->height = 5; + } + EnNwc_SetUpdate(this, EnNwc_Idle); +} + +void EnNwc_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNwc* this = (EnNwc*)thisx; + + Collider_FreeJntSph(globalCtx, &this->collider); +} + +void EnNwc_Idle(EnNwc* this, GlobalContext* globalCtx) { + EnNwc_UpdateChicks(this, globalCtx); +} + +void EnNwc_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNwc* this = (EnNwc*)thisx; + + this->updateFunc(this, globalCtx); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnNwc_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNwc* this = (EnNwc*)thisx; + + EnNwc_DrawChicks(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h b/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h new file mode 100644 index 000000000..0597ce76c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Nwc/z_en_nwc.h @@ -0,0 +1,36 @@ +#ifndef Z_EN_NWC_H +#define Z_EN_NWC_H + +#include "ultra64.h" +#include "global.h" + +struct EnNwc; +struct EnNwcChick; + +typedef void (*EnNwcUpdateFunc)(struct EnNwc*, GlobalContext*); +typedef void (*EnNwcChickFunc)(struct EnNwcChick*, struct EnNwc*, GlobalContext*); + +typedef struct EnNwcChick { + /* 0x00 */ s8 type; + /* 0x01 */ u8 bgFlags; + /* 0x04 */ f32 floorY; + /* 0x08 */ Vec3f pos; + /* 0x14 */ char unk_14[8]; + /* 0x1C */ Vec3f lastPos; + /* 0x28 */ char unk_28[4]; + /* 0x2C */ f32 velY; + /* 0x30 */ Vec3s rot; + /* 0x36 */ u16 height; + /* 0x38 */ CollisionPoly* floorPoly; + /* 0x44 */ char unk_3C[0x20]; +} EnNwcChick; // size = 0x5C + +typedef struct EnNwc { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderJntSph collider; + /* 0x016C */ u8 count; + /* 0x0170 */ EnNwcChick chicks[16]; + /* 0x0730 */ EnNwcUpdateFunc updateFunc; +} EnNwc; // size = 0x0734 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.c b/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.c new file mode 100644 index 000000000..150bb728d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.c @@ -0,0 +1,605 @@ +#include "z_en_ny.h" +#include "objects/object_ny/object_ny.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnNy_Init(Actor* thisx, GlobalContext* globalCtx); +void EnNy_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnNy_Update(Actor* thisx, GlobalContext* globalCtx); +void EnNy_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnNy_UpdateUnused(Actor* thisx, GlobalContext* globalCtx); +void EnNy_Move(EnNy* this, GlobalContext* globalCtx); +void EnNy_Die(EnNy* this, GlobalContext* globalCtx); +void func_80ABCD40(EnNy* this); +void func_80ABCDBC(EnNy* this); +void EnNy_TurnToStone(EnNy* this, GlobalContext* globalCtx); +void func_80ABD11C(EnNy* this, GlobalContext* globalCtx); +void func_80ABCE50(EnNy* this, GlobalContext* globalCtx); +void func_80ABCE90(EnNy* this, GlobalContext* globalCtx); +void func_80ABCEEC(EnNy* this, GlobalContext* globalCtx); +void EnNy_UpdateDeath(Actor* thisx, GlobalContext* GlobalContext); +void EnNy_SetupDie(EnNy* this, GlobalContext* globalCtx); +void EnNy_DrawDeathEffect(Actor* thisx, GlobalContext* GlobalContext); +void func_80ABD3B8(EnNy* this, f32, f32); + +const ActorInit En_Ny_InitVars = { + ACTOR_EN_NY, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_NY, + sizeof(EnNy), + (ActorFunc)EnNy_Init, + (ActorFunc)EnNy_Destroy, + (ActorFunc)EnNy_Update, + (ActorFunc)EnNy_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x04, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 15 }, 100 }, + }, +}; + +static ColliderJntSphInit sColliderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0xF), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(2, 0x1), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0xF), + /* Light arrow */ DMG_ENTRY(2, 0xF), + /* Unk arrow 1 */ DMG_ENTRY(4, 0xE), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x28, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +void EnNy_Init(Actor* thisx, GlobalContext* globalCtx) { + EnNy* this = (EnNy*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.colChkInfo.health = 2; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderInit, this->elements); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f); + this->unk_1CA = 0; + this->unk_1D0 = 0; + Actor_SetScale(&this->actor, 0.01f); + this->actor.speedXZ = 0.0f; + this->actor.shape.rot.y = 0; + this->actor.gravity = -0.4f; + this->hitPlayer = 0; + this->unk_1CE = 2; + this->actor.velocity.y = 0.0f; + this->unk_1D4 = 0xFF; + this->unk_1D8 = 0; + this->unk_1E8 = 0.0f; + this->unk_1E0 = 0.25f; + if (this->actor.params == 0) { + // "New initials" + osSyncPrintf("ニュウ イニシャル[ %d ] !!\n", this->actor.params); + this->actor.colChkInfo.mass = 0; + this->unk_1D4 = 0; + this->unk_1D8 = 0xFF; + this->unk_1E0 = 1.0f; + func_80ABCDBC(this); + } else { + // This mode is unused in the final game + // "Dummy new initials" + osSyncPrintf("ダミーニュウ イニシャル[ %d ] !!\n", this->actor.params); + osSyncPrintf("En_Ny_actor_move2[ %x ] !!\n", EnNy_UpdateUnused); + this->actor.colChkInfo.mass = 0xFF; + this->collider.base.colType = COLTYPE_METAL; + this->actor.update = EnNy_UpdateUnused; + } +} + +void EnNy_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnNy* this = (EnNy*)thisx; + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void func_80ABCD40(EnNy* this) { + f32 temp; + + temp = (this->actor.yDistToWater > 0.0f) ? 0.7f : 1.0f; + this->unk_1E8 = 2.8f * temp; +} + +void func_80ABCD84(EnNy* this) { + this->actionFunc = func_80ABCE50; +} + +void func_80ABCD94(EnNy* this) { + this->stoneTimer = 0x14; + this->actionFunc = func_80ABCE90; +} + +void func_80ABCDAC(EnNy* this) { + this->actionFunc = func_80ABCEEC; +} + +void func_80ABCDBC(EnNy* this) { + this->unk_1F4 = 0.0f; + func_80ABCD40(this); + this->stoneTimer = 180; + this->actionFunc = EnNy_Move; +} + +void EnNy_SetupTurnToStone(EnNy* this) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NYU_HIT_STOP); + this->actionFunc = EnNy_TurnToStone; + this->unk_1E8 = 0.0f; +} + +void func_80ABCE38(EnNy* this) { + this->stoneTimer = 0x3C; + this->actionFunc = func_80ABD11C; +} + +void func_80ABCE50(EnNy* this, GlobalContext* globalCtx) { + if (this->actor.xyzDistToPlayerSq <= 25600.0f) { + func_80ABCD94(this); + } +} + +void func_80ABCE90(EnNy* this, GlobalContext* globalCtx) { + s32 phi_v1; + s32 phi_v0; + + phi_v1 = this->unk_1D4 - 0x40; + phi_v0 = this->unk_1D8 + 0x40; + if (phi_v0 >= 0xFF) { + phi_v1 = 0; + phi_v0 = 0xFF; + func_80ABCDAC(this); + } + this->unk_1D4 = phi_v1; + this->unk_1D8 = phi_v0; +} + +void func_80ABCEEC(EnNy* this, GlobalContext* globalCtx) { + f32 phi_f0; + + phi_f0 = this->unk_1E0; + phi_f0 += 2.0f; + if (phi_f0 >= 1.0f) { + phi_f0 = 1.0f; + func_80ABCDBC(this); + } + this->unk_1E0 = phi_f0; +} + +void EnNy_Move(EnNy* this, GlobalContext* globalCtx) { + f32 yawDiff; + s32 stoneTimer; + + if (!(this->unk_1F0 < this->actor.yDistToWater)) { + func_8002F974(&this->actor, NA_SE_EN_NYU_MOVE - SFX_FLAG); + } + func_80ABCD40(this); + stoneTimer = this->stoneTimer; + this->stoneTimer--; + if ((stoneTimer <= 0) || (this->hitPlayer != false)) { + EnNy_SetupTurnToStone(this); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, this->unk_1F4, 0); + Math_ApproachF(&this->unk_1F4, 2000.0f, 1.0f, 100.0f); + this->actor.world.rot.y = this->actor.shape.rot.y; + yawDiff = Math_FAtan2F(this->actor.yDistToPlayer, this->actor.xzDistToPlayer); + this->actor.speedXZ = fabsf(cosf(yawDiff) * this->unk_1E8); + if (this->unk_1F0 < this->actor.yDistToWater) { + this->unk_1EC = sinf(yawDiff) * this->unk_1E8; + } + } +} + +void EnNy_TurnToStone(EnNy* this, GlobalContext* globalCtx) { + f32 phi_f0; + + phi_f0 = this->unk_1E0; + phi_f0 -= 2.0f; + if (phi_f0 <= 0.25f) { + phi_f0 = 0.25f; + if (this->actor.bgCheckFlags & 2) { + if (!(this->unk_1F0 < this->actor.yDistToWater)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + this->actor.bgCheckFlags &= ~2; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + func_80ABCE38(this); + } + } + this->unk_1E0 = phi_f0; +} + +void func_80ABD11C(EnNy* this, GlobalContext* globalCtx) { + s32 phi_v0; + s32 phi_v1; + + phi_v0 = this->unk_1D4; + phi_v0 += 0x40; + phi_v1 = this->unk_1D8; + phi_v1 -= 0x40; + if (phi_v0 >= 0xFF) { + phi_v0 = 0xFF; + phi_v1 = 0; + if (this->stoneTimer != 0) { + this->stoneTimer--; + } else { + func_80ABCD84(this); + } + } + this->unk_1D4 = phi_v0; + this->unk_1D8 = phi_v1; +} + +s32 EnNy_CollisionCheck(EnNy* this, GlobalContext* globalCtx) { + u8 sp3F; + Vec3f effectPos; + + sp3F = 0; + this->hitPlayer = 0; + if (this->collider.base.atFlags & 4) { + this->collider.base.atFlags &= ~4; + this->hitPlayer = 1; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = -4.0f; + return 0; + } + if (this->collider.base.atFlags & 2) { + this->collider.base.atFlags &= ~2; + this->hitPlayer = 1; + return 0; + } else { + if (this->collider.base.acFlags & 2) { + this->collider.base.acFlags &= ~2; + effectPos.x = this->collider.elements[0].info.bumper.hitPos.x; + effectPos.y = this->collider.elements[0].info.bumper.hitPos.y; + effectPos.z = this->collider.elements[0].info.bumper.hitPos.z; + if ((this->unk_1E0 == 0.25f) && (this->unk_1D4 == 0xFF)) { + switch (this->actor.colChkInfo.damageEffect) { + case 0xE: + sp3F = 1; + case 0xF: + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + break; + case 1: + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + break; + case 2: + this->unk_1CA = 4; + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + break; + } + } + this->stoneTimer = 0; + if (this->actor.colChkInfo.health == 0) { + this->actor.shape.shadowAlpha = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_1D0 = sp3F; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + return 1; + } + EffectSsHitMark_SpawnFixedScale(globalCtx, 0, &effectPos); + return 0; + } + } + return 0; +} + +void func_80ABD3B8(EnNy* this, f32 arg1, f32 arg2) { + if (this->unk_1E8 == 0.0f) { + this->actor.gravity = -0.4f; + } else if (!(arg1 < this->actor.yDistToWater)) { + this->actor.gravity = -0.4f; + } else if (arg2 < this->actor.yDistToWater) { + this->actor.gravity = 0.0; + if (this->unk_1EC < this->actor.velocity.y) { + this->actor.velocity.y -= 0.4f; + if (this->actor.velocity.y < this->unk_1EC) { + this->actor.velocity.y = this->unk_1EC; + } + } else if (this->actor.velocity.y < this->unk_1EC) { + this->actor.velocity.y += 0.4f; + if (this->unk_1EC < this->actor.velocity.y) { + this->actor.velocity.y = this->unk_1EC; + } + } + } +} + +void EnNy_Update(Actor* thisx, GlobalContext* globalCtx) { + EnNy* this = (EnNy*)thisx; + f32 temp_f20; + f32 temp_f22; + s32 i; + + this->timer++; + temp_f20 = this->unk_1E0 - 0.25f; + if (this->unk_1CA != 0) { + this->unk_1CA--; + } + Actor_SetFocus(&this->actor, 0.0f); + Actor_SetScale(&this->actor, 0.01f); + this->collider.elements[0].dim.scale = 1.33f * temp_f20 + 1.0f; + temp_f22 = (24.0f * temp_f20) + 12.0f; + this->actor.shape.rot.x += (s16)(this->unk_1E8 * 1000.0f); + func_80ABD3B8(this, temp_f22 + 10.0f, temp_f22 - 10.0f); + Actor_MoveForward(&this->actor); + Math_StepToF(&this->unk_1E4, this->unk_1E8, 0.1f); + this->actionFunc(this, globalCtx); + this->actor.prevPos.y -= temp_f22; + this->actor.world.pos.y -= temp_f22; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 7); + this->unk_1F0 = temp_f22; + this->actor.world.pos.y += temp_f22; + if (EnNy_CollisionCheck(this, globalCtx) != 0) { + for (i = 0; i < 8; i++) { + this->unk_1F8[i].x = (Rand_CenteredFloat(20.0f) + this->actor.world.pos.x); + this->unk_1F8[i].y = (Rand_CenteredFloat(20.0f) + this->actor.world.pos.y); + this->unk_1F8[i].z = (Rand_CenteredFloat(20.0f) + this->actor.world.pos.z); + } + this->timer = 0; + this->actor.update = EnNy_UpdateDeath; + this->actor.draw = EnNy_DrawDeathEffect; + this->actionFunc = EnNy_SetupDie; + return; + } + if (this->unk_1E0 > 0.25f) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnNy_SetupDie(EnNy* this, GlobalContext* globalCtx) { + s32 effectScale; + s32 i; + Vec3f effectPos; + Vec3f effectVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f effectAccel = { 0.0f, 0.1f, 0.0f }; + + if (this->timer >= 2) { + if (this->actor.yDistToWater > 0.0f) { + for (i = 0; i < 10; i++) { + effectPos.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x; + effectPos.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y; + effectPos.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z; + effectScale = Rand_S16Offset(0x50, 0x64); + EffectSsDtBubble_SpawnColorProfile(globalCtx, &effectPos, &effectVelocity, &effectAccel, effectScale, + 25, 0, 1); + } + for (i = 0; i < 0x14; i++) { + effectPos.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x; + effectPos.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y; + effectPos.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z; + EffectSsBubble_Spawn(globalCtx, &effectPos, 10.0f, 10.0f, 30.0f, 0.25f); + } + } + for (i = 0; i < 8; i++) { + this->unk_1F8[i + 8].x = Rand_CenteredFloat(10.0f); + this->unk_1F8[i + 8].z = Rand_CenteredFloat(10.0f); + this->unk_1F8[i + 8].y = Rand_ZeroFloat(4.0f) + 4.0f; + } + this->timer = 0; + if (this->unk_1D0 == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xA0); + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, 8); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NYU_DEAD); + this->actionFunc = EnNy_Die; + } +} + +void EnNy_Die(EnNy* this, GlobalContext* globalCtx) { + s32 i; + + if (this->actor.yDistToWater > 0.0f) { + for (i = 0; i < 8; i += 1) { + this->unk_1F8[i].x += this->unk_1F8[i + 8].x; + this->unk_1F8[i].y += this->unk_1F8[i + 8].y; + this->unk_1F8[i].z += this->unk_1F8[i + 8].z; + Math_StepToF(&this->unk_1F8[i + 8].x, 0.0f, 0.1f); + Math_StepToF(&this->unk_1F8[i + 8].y, -1.0f, 0.4f); + Math_StepToF(&this->unk_1F8[i + 8].z, 0.0f, 0.1f); + } + if (this->timer >= 0x1F) { + Actor_Kill(&this->actor); + return; + } + } else { + for (i = 0; i < 8; i += 1) { + this->unk_1F8[i].x += this->unk_1F8[i + 8].x; + this->unk_1F8[i].y += this->unk_1F8[i + 8].y; + this->unk_1F8[i].z += this->unk_1F8[i + 8].z; + Math_StepToF(&this->unk_1F8[i + 8].x, 0.0f, 0.15f); + Math_StepToF(&this->unk_1F8[i + 8].y, -1.0f, 0.6f); + Math_StepToF(&this->unk_1F8[i + 8].z, 0.0f, 0.15f); + } + if (this->timer >= 0x10) { + Actor_Kill(&this->actor); + return; + } + } +} + +void EnNy_UpdateDeath(Actor* thisx, GlobalContext* globalCtx) { + EnNy* this = (EnNy*)thisx; + + this->timer++; + if (this->unk_1CA != 0) { + this->unk_1CA--; + } + this->actionFunc(this, globalCtx); +} + +void EnNy_UpdateUnused(Actor* thisx, GlobalContext* globalCtx2) { + EnNy* this = (EnNy*)thisx; + GlobalContext* globalCtx = globalCtx2; + f32 sp3C; + f32 temp_f0; + + sp3C = this->unk_1E0 - 0.25f; + this->timer++; + Actor_SetFocus(&this->actor, 0.0f); + Actor_SetScale(&this->actor, 0.01f); + temp_f0 = (24.0f * sp3C) + 12.0f; + this->actor.prevPos.y -= temp_f0; + this->actor.world.pos.y -= temp_f0; + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 7); + this->unk_1F0 = temp_f0; + this->actor.world.pos.y += temp_f0; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Math_StepToF(&this->unk_1E4, this->unk_1E8, 0.1f); +} +static Vec3f sFireOffsets[] = { + { 5.0f, 0.0f, 0.0f }, + { -5.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 5.0f }, + { 0.0f, 0.0f, -5.0f }, +}; + +void EnNy_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnNy* this = (EnNy*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ny.c", 837); + Collider_UpdateSpheres(0, &this->collider); + func_8002ED80(&this->actor, globalCtx, 1); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ny.c", 845), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->unk_1D8); + gSPDisplayList(POLY_XLU_DISP++, gEnNyMetalBodyDL); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->unk_1D4); + gSPDisplayList(POLY_XLU_DISP++, gEnNyRockBodyDL); + if (this->unk_1E0 > 0.25f) { + Matrix_Scale(this->unk_1E0, this->unk_1E0, this->unk_1E0, MTXMODE_APPLY); + func_8002EBCC(&this->actor, globalCtx, 1); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ny.c", 868), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gEnNySpikeDL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ny.c", 872); + if (this->unk_1CA != 0) { + Vec3f tempVec; + Vec3f* fireOffset; + s16 temp; + + temp = this->unk_1CA - 1; + this->actor.colorFilterTimer++; + if (temp == 0) { + fireOffset = &sFireOffsets[temp & 3]; + tempVec.x = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.x + fireOffset->x); + tempVec.y = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.y + fireOffset->y); + tempVec.z = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.z + fireOffset->z); + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &tempVec, 100, 0, 0, -1); + } + } +} + +void EnNy_DrawDeathEffect(Actor* thisx, GlobalContext* globalCtx) { + EnNy* this = (EnNy*)thisx; + Vec3f* temp; + f32 scale; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ny.c", 900); + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0x00, 0x00, 0x00, 0xFF); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2); + gDPPipeSync(POLY_OPA_DISP++); + for (i = 0; i < 8; i++) { + if (this->timer < (i + 22)) { + temp = &this->unk_1F8[i]; + Matrix_Translate(temp->x, temp->y, temp->z, MTXMODE_NEW); + scale = this->actor.scale.x * 0.4f * (1.0f + (i * 0.04f)); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_ny.c", 912), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gEnNyRockBodyDL); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ny.c", 919); + if (this->unk_1CA != 0) { + Vec3f tempVec; + Vec3f* fireOffset; + s16 fireOffsetIndex; + + fireOffsetIndex = this->unk_1CA - 1; + this->actor.colorFilterTimer++; + if ((fireOffsetIndex & 1) == 0) { + fireOffset = &sFireOffsets[fireOffsetIndex & 3]; + tempVec.x = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.x + fireOffset->x); + tempVec.y = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.y + fireOffset->y); + tempVec.z = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.z + fireOffset->z); + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &tempVec, 100, 0, 0, -1); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h b/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h new file mode 100644 index 000000000..fd93efcdb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ny/z_en_ny.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_NY_H +#define Z_EN_NY_H + +#include "ultra64.h" +#include "global.h" + +struct EnNy; + +typedef void (*EnNyActionFunc)(struct EnNy*, GlobalContext*); + +typedef struct EnNy { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnNyActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement elements[1]; + /* 0x01B0 */ char unk_1B0[0x18]; + /* 0x01C8 */ s16 timer; + /* 0x01CA */ s16 unk_1CA; + /* 0x01CC */ s16 hitPlayer; + /* 0x01CE */ u16 unk_1CE; + /* 0x01D0 */ u8 unk_1D0; + /* 0x01D1 */ s8 unk_1D1; + /* 0x01D4 */ s32 unk_1D4; + /* 0x01D8 */ s32 unk_1D8; + /* 0x01DC */ s32 stoneTimer; // Delay for when to attempt to change forms + /* 0x01E0 */ f32 unk_1E0; + /* 0x01E4 */ f32 unk_1E4; // This, unk_1E8, and unk_1EC have to do with movement speed + /* 0x01E8 */ f32 unk_1E8; + /* 0x01EC */ f32 unk_1EC; + /* 0x01F0 */ f32 unk_1F0; + /* 0x01F4 */ f32 unk_1F4; + /* 0x01F8 */ Vec3f unk_1F8[16]; +} EnNy; // size = 0x02B8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_OE2/z_en_oe2.c b/soh/src/overlays/actors/ovl_En_OE2/z_en_oe2.c new file mode 100644 index 000000000..50bfef8b5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_OE2/z_en_oe2.c @@ -0,0 +1,51 @@ +/* + * File: z_en_oe2.c + * Overlay: ovl_En_Oe2 + * Description: Blue Navi Target Spot + */ + +#include "z_en_oe2.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnOE2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnOE2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnOE2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnOE2_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnOE2_DoNothing(EnOE2* this, GlobalContext* globalCtx); + +const ActorInit En_OE2_InitVars = { + ACTOR_EN_OE2, + ACTORCAT_NPC, + FLAGS, + OBJECT_OE2, + sizeof(EnOE2), + (ActorFunc)EnOE2_Init, + (ActorFunc)EnOE2_Destroy, + (ActorFunc)EnOE2_Update, + (ActorFunc)EnOE2_Draw, + NULL, +}; + +void EnOE2_SetupAction(EnOE2* this, EnOE2ActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnOE2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnOE2* this = (EnOE2*)thisx; + + EnOE2_SetupAction(this, EnOE2_DoNothing); +} + +void EnOE2_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnOE2_DoNothing(EnOE2* this, GlobalContext* globalCtx) { +} + +void EnOE2_Update(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnOE2_Draw(Actor* thisx, GlobalContext* globalCtx) { +} diff --git a/soh/src/overlays/actors/ovl_En_OE2/z_en_oe2.h b/soh/src/overlays/actors/ovl_En_OE2/z_en_oe2.h new file mode 100644 index 000000000..b12b8a0a6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_OE2/z_en_oe2.h @@ -0,0 +1,17 @@ +#ifndef Z_EN_OE2_H +#define Z_EN_OE2_H + +#include "ultra64.h" +#include "global.h" + +struct EnOE2; + +typedef void (*EnOE2ActionFunc)(struct EnOE2*, GlobalContext*); + +typedef struct EnOE2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[0x44]; + /* 0x0190 */ EnOE2ActionFunc actionFunc; +} EnOE2; // size = 0x0194 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c b/soh/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c new file mode 100644 index 000000000..a3be05726 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.c @@ -0,0 +1,122 @@ +/* + * File: z_en_okarina_effect.c + * Overlay: ovl_En_Okarina_Effect + * Description: Manages the storm created when playing Song of Storms + */ + +#include "z_en_okarina_effect.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnOkarinaEffect_Init(Actor* thisx, GlobalContext* globalCtx); +void EnOkarinaEffect_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnOkarinaEffect_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnOkarinaEffect_TriggerStorm(EnOkarinaEffect* this, GlobalContext* globalCtx); +void EnOkarinaEffect_ManageStorm(EnOkarinaEffect* this, GlobalContext* globalCtx); + +const ActorInit En_Okarina_Effect_InitVars = { + ACTOR_EN_OKARINA_EFFECT, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnOkarinaEffect), + (ActorFunc)EnOkarinaEffect_Init, + (ActorFunc)EnOkarinaEffect_Destroy, + (ActorFunc)EnOkarinaEffect_Update, + NULL, + NULL, +}; + +void EnOkarinaEffect_SetupAction(EnOkarinaEffect* this, EnOkarinaEffectActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnOkarinaEffect_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnOkarinaEffect* this = (EnOkarinaEffect*)thisx; + + globalCtx->envCtx.unk_F2[0] = 0; + if ((gWeatherMode != 4) && (gWeatherMode != 5) && (globalCtx->envCtx.gloomySkyMode == 1)) { + globalCtx->envCtx.gloomySkyMode = 2; // end gloomy sky + Environment_StopStormNatureAmbience(globalCtx); + } + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_LAST; +} + +void EnOkarinaEffect_Init(Actor* thisx, GlobalContext* globalCtx) { + EnOkarinaEffect* this = (EnOkarinaEffect*)thisx; + + osSyncPrintf("\n\n"); + // "Ocarina Storm Effect" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ オカリナあらし効果ビカビカビカ〜 ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf("\n\n"); + if (globalCtx->envCtx.unk_EE[1] != 0) { + Actor_Kill(&this->actor); + } + EnOkarinaEffect_SetupAction(this, EnOkarinaEffect_TriggerStorm); +} + +void EnOkarinaEffect_TriggerStorm(EnOkarinaEffect* this, GlobalContext* globalCtx) { + this->timer = 400; // 20 seconds + globalCtx->envCtx.unk_F2[0] = 20; // rain intensity target + globalCtx->envCtx.gloomySkyMode = 1; // start gloomy sky + if ((gWeatherMode != 0) || globalCtx->envCtx.unk_17 != 0) { + globalCtx->envCtx.unk_DE = 1; + } + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_ON; + Environment_PlayStormNatureAmbience(globalCtx); + EnOkarinaEffect_SetupAction(this, EnOkarinaEffect_ManageStorm); +} + +void EnOkarinaEffect_ManageStorm(EnOkarinaEffect* this, GlobalContext* globalCtx) { + Flags_UnsetEnv(globalCtx, 5); // clear storms env flag + if (((globalCtx->pauseCtx.state == 0) && (globalCtx->gameOverCtx.state == GAMEOVER_INACTIVE) && + (globalCtx->msgCtx.msgLength == 0) && (!FrameAdvance_IsEnabled(globalCtx)) && + ((globalCtx->transitionMode == 0) || (gSaveContext.gameMode != 0))) || + (this->timer >= 250)) { + if (globalCtx->envCtx.indoors || globalCtx->envCtx.unk_1F != 1) { + this->timer--; + } + osSyncPrintf("\nthis->timer=[%d]", this->timer); + if (this->timer == 308) { + osSyncPrintf("\n\n\n豆よ のびろ 指定\n\n\n"); // "Let's grow some beans" + Flags_SetEnv(globalCtx, 5); // set storms env flag + } + } + + if (D_8011FB38 != 0) { + this->timer = 0; + } + + if (this->timer == 0) { + globalCtx->envCtx.unk_F2[0] = 0; + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + Environment_StopStormNatureAmbience(globalCtx); + } else if (func_800FA0B4(SEQ_PLAYER_BGM_MAIN) == NA_BGM_NATURE_AMBIENCE) { + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_LIGHTNING, CHANNEL_IO_PORT_1, 0); + Audio_SetNatureAmbienceChannelIO(NATURE_CHANNEL_RAIN, CHANNEL_IO_PORT_1, 0); + } + osSyncPrintf("\n\n\nE_wether_flg=[%d]", gWeatherMode); + osSyncPrintf("\nrain_evt_trg=[%d]\n\n", globalCtx->envCtx.gloomySkyMode); + if (gWeatherMode == 0 && (globalCtx->envCtx.gloomySkyMode == 1)) { + globalCtx->envCtx.gloomySkyMode = 2; // end gloomy sky + } else { + globalCtx->envCtx.gloomySkyMode = 0; + globalCtx->envCtx.unk_DE = 0; + } + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_LAST; + Actor_Kill(&this->actor); + } +} + +void EnOkarinaEffect_Update(Actor* thisx, GlobalContext* globalCtx) { + EnOkarinaEffect* this = (EnOkarinaEffect*)thisx; + + this->actionFunc(this, globalCtx); + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 0xFF, 0, 0xFF, 0xFF, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h b/soh/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h new file mode 100644 index 000000000..832021711 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okarina_Effect/z_en_okarina_effect.h @@ -0,0 +1,17 @@ +#ifndef Z_EN_OKARINA_EFFECT_H +#define Z_EN_OKARINA_EFFECT_H + +#include "ultra64.h" +#include "global.h" + +struct EnOkarinaEffect; + +typedef void (*EnOkarinaEffectActionFunc)(struct EnOkarinaEffect*, GlobalContext*); + +typedef struct EnOkarinaEffect { + /* 0x0000 */ Actor actor; + /* 0x014C */ u16 timer; + /* 0x0150 */ EnOkarinaEffectActionFunc actionFunc; +} EnOkarinaEffect; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag.c b/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag.c new file mode 100644 index 000000000..42185d102 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag.c @@ -0,0 +1,334 @@ +/* + * File: z_en_okarina_tag.c + * Overlay: ovl_En_Okarina_Tag + * Description: Music Staff (Ocarina) spot + */ + +#include "z_en_okarina_tag.h" +#include "scenes/misc/hakaana_ouke/hakaana_ouke_scene.h" +#include "scenes/overworld/spot02/spot02_scene.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnOkarinaTag_Init(Actor* thisx, GlobalContext* globalCtx); +void EnOkarinaTag_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnOkarinaTag_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80ABEF2C(EnOkarinaTag* this, GlobalContext* globalCtx); +void func_80ABF708(EnOkarinaTag* this, GlobalContext* globalCtx); +void func_80ABF28C(EnOkarinaTag* this, GlobalContext* globalCtx); +void func_80ABF0CC(EnOkarinaTag* this, GlobalContext* globalCtx); +void func_80ABF4C8(EnOkarinaTag* this, GlobalContext* globalCtx); +void func_80ABF7CC(EnOkarinaTag* this, GlobalContext* globalCtx); + +const ActorInit En_Okarina_Tag_InitVars = { + ACTOR_EN_OKARINA_TAG, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnOkarinaTag), + (ActorFunc)EnOkarinaTag_Init, + (ActorFunc)EnOkarinaTag_Destroy, + (ActorFunc)EnOkarinaTag_Update, + NULL, + NULL, +}; + +extern CutsceneData D_80ABF9D0[]; +extern CutsceneData D_80ABFB40[]; + +void EnOkarinaTag_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnOkarinaTag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnOkarinaTag* this = (EnOkarinaTag*)thisx; + + osSyncPrintf("\n\n"); + // "Ocarina tag outbreak" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ オカリナタグ発生 ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + this->actor.flags &= ~ACTOR_FLAG_0; + this->type = (this->actor.params >> 0xA) & 0x3F; + this->ocarinaSong = (this->actor.params >> 6) & 0xF; + this->switchFlag = this->actor.params & 0x3F; + if (this->switchFlag == 0x3F) { + this->switchFlag = -1; + } + if (this->ocarinaSong == 0xF) { + this->ocarinaSong = 0; + this->unk_158 = 1; + } + this->actor.targetMode = 1; + if (this->actor.world.rot.z > 0) { + this->interactRange = this->actor.world.rot.z * 40.0f; + } + + // "Save information" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ セーブ情報\t ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + // "Type index" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 種類インデックス ☆☆☆☆☆ %d\n" VT_RST, this->type); + // "Correct answer information" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 正解情報\t ☆☆☆☆☆ %d\n" VT_RST, this->ocarinaSong); + // "Range information" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 範囲情報\t ☆☆☆☆☆ %d\n" VT_RST, this->actor.world.rot.z); + // "Processing range information" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 処理範囲情報\t ☆☆☆☆☆ %f\n" VT_RST, this->interactRange); + // "Hit?" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 当り?\t\t ☆☆☆☆☆ %d\n" VT_RST, this->unk_158); + osSyncPrintf("\n\n"); + + if ((this->switchFlag >= 0) && (Flags_GetSwitch(globalCtx, this->switchFlag))) { + Actor_Kill(&this->actor); + } else { + switch (this->type) { + case 7: + this->actionFunc = func_80ABEF2C; + break; + case 2: + if (LINK_IS_ADULT) { + Actor_Kill(&this->actor); + break; + } + case 1: + case 4: + case 6: + this->actionFunc = func_80ABF28C; + break; + case 5: + this->actor.textId = 0x5021; + this->actionFunc = func_80ABF708; + break; + default: + Actor_Kill(&this->actor); + break; + } + } +} + +void func_80ABEF2C(EnOkarinaTag* this, GlobalContext* globalCtx) { + Player* player; + u16 ocarinaSong; + + player = GET_PLAYER(globalCtx); + this->unk_15A++; + if ((this->switchFlag >= 0) && (Flags_GetSwitch(globalCtx, this->switchFlag))) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + if ((this->ocarinaSong != 6) || (gSaveContext.scarecrowSpawnSongSet)) { + if (player->stateFlags2 & 0x1000000) { + // "North! ! ! ! !" + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 北!!!!! ☆☆☆☆☆ %f\n" VT_RST, this->actor.xzDistToPlayer); + } + if ((this->actor.xzDistToPlayer < (90.0f + this->interactRange)) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 80.0f)) { + if (player->stateFlags2 & 0x2000000) { + ocarinaSong = this->ocarinaSong; + if (ocarinaSong == 6) { + ocarinaSong = 0xA; + } + player->stateFlags2 |= 0x800000; + func_8010BD58(globalCtx, ocarinaSong + OCARINA_ACTION_CHECK_SARIA); + this->actionFunc = func_80ABF0CC; + } else if ((this->actor.xzDistToPlayer < (50.0f + this->interactRange) && + ((fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 40.0f)))) { + this->unk_15A = 0; + player->unk_6A8 = &this->actor; + } + } + } + } +} + +void func_80ABF0CC(EnOkarinaTag* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + this->actionFunc = func_80ABEF2C; + } else { + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + if (globalCtx->sceneNum == SCENE_MIZUSIN) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + } + if ((globalCtx->sceneNum != SCENE_DAIYOUSEI_IZUMI) && (globalCtx->sceneNum != SCENE_YOUSEI_IZUMI_YOKO)) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + } + func_80078884(NA_SE_SY_CORRECT_CHIME); + this->actionFunc = func_80ABEF2C; + return; + } + if (this->unk_158 != 0) { + if ((globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_05) || + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_06) || + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_07) || + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_08) || + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_09) || + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_0A) || + (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_0D)) { + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + func_80078884(NA_SE_SY_CORRECT_CHIME); + this->actionFunc = func_80ABEF2C; + return; + } + } + if ((globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_05) && (globalCtx->msgCtx.ocarinaMode < OCARINA_MODE_0E)) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + this->actionFunc = func_80ABEF2C; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + player->stateFlags2 |= 0x800000; + } + } +} + +void func_80ABF28C(EnOkarinaTag* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_15A++; + if ((this->ocarinaSong != 6) || (gSaveContext.scarecrowSpawnSongSet)) { + if ((this->switchFlag >= 0) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else if (((this->type != 4) || !(gSaveContext.eventChkInf[4] & 0x800)) && + ((this->type != 6) || !(gSaveContext.eventChkInf[1] & 0x2000)) && + (this->actor.xzDistToPlayer < (90.0f + this->interactRange)) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 80.0f)) { + if (player->stateFlags2 & 0x1000000) { + switch (this->type) { + case 1: + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_LULLABY); + break; + case 2: + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_STORMS); + break; + case 4: + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_TIME); + break; + case 6: + func_8010BD58(globalCtx, OCARINA_ACTION_CHECK_LULLABY); + break; + default: + // "Ocarina Invisible-kun demo start check error source" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ オカリナ透明君デモ開始チェックエラー原 ☆☆☆☆☆ %d\n" VT_RST, + this->type); + Actor_Kill(&this->actor); + break; + } + player->stateFlags2 |= 0x800000; + this->actionFunc = func_80ABF4C8; + } else if ((this->actor.xzDistToPlayer < (50.0f + this->interactRange)) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 40.0f)) { + this->unk_15A = 0; + player->stateFlags2 |= 0x800000; + } + } + } +} + +void func_80ABF4C8(EnOkarinaTag* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + this->actionFunc = func_80ABF28C; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + switch (this->type) { + case 1: + Flags_SetSwitch(globalCtx, this->switchFlag); + gSaveContext.eventChkInf[3] |= 0x200; + break; + case 2: + globalCtx->csCtx.segment = D_80ABF9D0; + gSaveContext.cutsceneTrigger = 1; + func_800F574C(1.18921f, 0x5A); + break; + case 4: + globalCtx->csCtx.segment = D_80ABFB40; + gSaveContext.cutsceneTrigger = 1; + break; + case 6: + globalCtx->csCtx.segment = LINK_IS_ADULT ? SEGMENTED_TO_VIRTUAL(&spot02_scene_Cs_003C80) + : SEGMENTED_TO_VIRTUAL(&spot02_scene_Cs_005020); + gSaveContext.cutsceneTrigger = 1; + gSaveContext.eventChkInf[1] |= 0x2000; + func_80078884(NA_SE_SY_CORRECT_CHIME); + break; + default: + break; + } + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + this->actionFunc = func_80ABF28C; + } else { + if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_05) { + if (globalCtx->msgCtx.ocarinaMode < OCARINA_MODE_0E) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + this->actionFunc = func_80ABF28C; + return; + } + } + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + player->stateFlags2 |= 0x800000; + } + } +} + +void func_80ABF708(EnOkarinaTag* this, GlobalContext* globalCtx) { + s16 yawDiff; + s16 yawDiffNew; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_80ABF7CC; + } else { + yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y; + this->unk_15A++; + if (!(this->actor.xzDistToPlayer > 120.0f)) { + if (CHECK_QUEST_ITEM(QUEST_SONG_SUN)) { + this->actor.textId = 0x5021; + } + yawDiffNew = ABS(yawDiff); + if (yawDiffNew < 0x4300) { + this->unk_15A = 0; + func_8002F2CC(&this->actor, globalCtx, 70.0f); + } + } + } +} + +void func_80ABF7CC(EnOkarinaTag* this, GlobalContext* globalCtx) { + // "Open sesame sesame!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 開けゴマゴマゴマ! ☆☆☆☆☆ %d\n" VT_RST, Message_GetState(&globalCtx->msgCtx)); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + if (!CHECK_QUEST_ITEM(QUEST_SONG_SUN)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gSunSongGraveSunSongTeachCs); + gSaveContext.cutsceneTrigger = 1; + } + this->actionFunc = func_80ABF708; + } +} + +void EnOkarinaTag_Update(Actor* thisx, GlobalContext* globalCtx) { + EnOkarinaTag* this = (EnOkarinaTag*)thisx; + + this->actionFunc(this, globalCtx); + if (BREG(0) != 0) { + if (this->unk_15A != 0) { + if (!(this->unk_15A & 1)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 120, 120, 120, 255, 4, globalCtx->state.gfxCtx); + } + } else { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag.h b/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag.h new file mode 100644 index 000000000..5946f7023 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag.h @@ -0,0 +1,23 @@ +#ifndef Z_EN_OKARINA_TAG_H +#define Z_EN_OKARINA_TAG_H + +#include "ultra64.h" +#include "global.h" + +struct EnOkarinaTag; + +typedef void (*EnOkarinaTagActionFunc)(struct EnOkarinaTag*, GlobalContext*); + +typedef struct EnOkarinaTag { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnOkarinaTagActionFunc actionFunc; + /* 0x0150 */ s16 type; + /* 0x0152 */ s16 ocarinaSong; + /* 0x0154 */ s16 switchFlag; + /* 0x0156 */ char unk_156[0x2]; + /* 0x0158 */ s16 unk_158; + /* 0x015A */ s16 unk_15A; + /* 0x015C */ f32 interactRange; +} EnOkarinaTag; // size = 0x0160 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag_cutscene_data.c new file mode 100644 index 000000000..f25593c4b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okarina_Tag/z_en_okarina_tag_cutscene_data.c @@ -0,0 +1,137 @@ +#include "z_en_okarina_tag.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData D_80ABF9D0[] = { + CS_BEGIN_CUTSCENE(4, 360), + CS_TERMINATOR(KAKARIKO_VILLAGE_DRAIN_WELL, 200, 201), + CS_CAM_EYE_LIST(0, 331), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3100, 201, -100, 0x3235), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3100, 201, -100, 0x3336), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3100, 201, -100, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3178, 201, 113, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3178, 201, 113, 0x3235), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3178, 201, 113, 0x3238), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 3178, 201, 113, 0x3639), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, 3178, 201, 113, 0x2C20), + CS_CAM_AT_LIST(0, 360), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 3054, 137, -64, 0x3235), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 3054, 137, -64, 0x3336), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 90, 60.0f, 3054, 137, -64, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 90, 60.0f, 3118, 142, 96, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 3118, 142, 96, 0x3235), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 3118, 142, 96, 0x3238), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 3118, 142, 96, 0x3639), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, 3118, 142, 96, 0x2C20), + CS_MISC_LIST(1), + CS_MISC(0x0013, 30, 31, 0x0000, 0x00000000, 0xFFFFFFFE, 0x00000000, 0x00000002, 0xFFFFFFFE, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_END(), +}; + +CutsceneData D_80ABFB40[] = { + CS_BEGIN_CUTSCENE(18, 3000), + CS_UNK_DATA_LIST(0x00000021, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFAA, 0xFFFFFFAE, 0x00000000, 0xFFFFFFAA, 0xFFFFFFAE, 0x00000000, 0x00000000, 0x00000000), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x0011, 0, 80, 0x0000, 0x8000, 0x0000, 0, -40, 1400, 0, -40, 1400, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0012, 80, 201, 0x0000, 0x8000, 0x0000, 0, -40, 1400, 0, -40, 1400, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0005, 201, 662, 0x0000, 0x8000, 0x0000, 0, -40, 1400, 0, -40, 1400, 0.0f, 0.0f, 1.401298464324817e-45f), + CS_MISC_LIST(1), + CS_MISC(0x000D, 510, 611, 0x0000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(48, 1), + CS_NPC_ACTION(0x0003, 160, 289, 0x0000, 0x0000, 0x0000, 0, 20, 1400, 0, 60, 1400, 0.0f, 0.31007752f, 0.0f), + CS_NPC_ACTION_LIST(48, 3), + CS_NPC_ACTION(0x0004, 340, 420, 0x0000, 0x0000, 0x0000, 0, 120, 1335, 0, 40, 1335, 0.0f, -1.0f, 0.0f), + CS_NPC_ACTION(0x0004, 420, 465, 0x0000, 0x0000, 0x0000, 0, 40, 1335, 0, 16, 1335, 0.0f, -0.53333336f, 0.0f), + CS_NPC_ACTION(0x0002, 465, 519, 0x0000, 0x0000, 0x0000, 0, 16, 1335, 0, 16, 1335, 0.0f, 0.0f, 0.0f), + CS_MISC_LIST(1), + CS_MISC(0x0003, 620, 621, 0x0000, 0x00000000, 0x00000001, 0x00000000, 0xFFFFFFFF, 0x00000001, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000), + CS_SCENE_TRANS_FX(0x0001, 338, 341), + CS_SCENE_TRANS_FX(0x0005, 344, 349), + CS_MISC_LIST(1), + CS_MISC(0x000C, 790, 791, 0x0000, 0x00000000, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_SCENE_TRANS_FX(0x0001, 157, 160), + CS_SCENE_TRANS_FX(0x0005, 159, 166), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x005A, 560, 561, 0x0000, 0x00000000, 0x00000003, 0x00000000, 0x00000004, 0x00000003, 0x00000000, 0x00000004), + CS_CAM_EYE_LIST(0, 451), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 2, 11, 1397, 0xA220), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 2, 11, 1397, 0xB820), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 2, 11, 1397, 0xD0A1), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 27, 1445, 0xBAEE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 56, 27, 1385, 0xA5AF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -19, 27, 1341, 0xAE0A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xA8A5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xA5ED), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xA220), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xA5A4), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xC9A5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xA5A2), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xA5BB), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -44, -4, 1429, 0xAE0A), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -44, -4, 1429, 0xB3A5), + CS_CAM_EYE_LIST(340, 1461), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -6, 15, 1560, 0xA220), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -6, 15, 1560, 0xB820), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -6, 15, 1560, 0xD0A1), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -6, 15, 1560, 0xBAEE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -6, 15, 1560, 0xA5AF), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -6, 15, 1560, 0xAE0A), + CS_CAM_EYE_LIST(490, 941), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 264, 1379, 0xA220), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 264, 1379, 0xB820), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 264, 1379, 0xD0A1), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 264, 1379, 0xBAEE), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 0, 153, 1379, 0xA5AF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 55, 32, 1398, 0xAE0A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0xA8A5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0xA5ED), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0xA220), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0xA5A4), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0xC9A5), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 111, -38, 1511, 0xA5A2), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, 111, -38, 1511, 0xA5BB), + CS_CAM_AT_LIST(0, 480), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 2, 36, 1335, 0xA220), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 2, 36, 1335, 0xB820), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 2, 36, 1335, 0xD0A1), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 0, -8, 1391, 0xBAEE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 1, -5, 1398, 0xA5AF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -2, -3, 1394, 0xAE0A), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 5, 0, 1399, 0xA8A5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 1, 14, 1399, 0xA5ED), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -9, 33, 1402, 0xA220), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -14, 41, 1409, 0xA5A4), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -14, 41, 1409, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -14, 41, 1409, 0xC9A5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -14, 41, 1409, 0xA5A2), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -14, 41, 1409, 0xA5BB), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -14, 41, 1409, 0xAE0A), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, -14, 41, 1409, 0xB3A5), + CS_CAM_AT_LIST(340, 1490), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -3, 48, 1414, 0xA220), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -3, 48, 1414, 0xB820), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 60.0f, -3, 48, 1414, 0xD0A1), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -3, 48, 1414, 0xBAEE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -3, 47, 1414, 0xA5AF), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, -3, 47, 1414, 0xAE0A), + CS_CAM_AT_LIST(490, 970), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 0, 264, 1274, 0xA220), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 60, 60.0f, 0, 264, 1274, 0xB820), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 60, 60.0f, 0, 264, 1274, 0xD0A1), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 0, 264, 1274, 0xBAEE), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 0, 170, 1275, 0xA5AF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 10, 51, 1306, 0xAE0A), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0xA8A5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0xA5ED), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0xA220), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0xA5A4), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0xC9A5), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, 68, -2, 1424, 0xA5A2), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, 68, -2, 1424, 0xA5BB), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c b/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c new file mode 100644 index 000000000..25543895e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c @@ -0,0 +1,724 @@ +#include "z_en_okuta.h" +#include "objects/object_okuta/object_okuta.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnOkuta_Init(Actor* thisx, GlobalContext* globalCtx); +void EnOkuta_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnOkuta_Update(Actor* thisx, GlobalContext* globalCtx); +void EnOkuta_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnOkuta_SetupWaitToAppear(EnOkuta* this); +void EnOkuta_WaitToAppear(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_Appear(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_Hide(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_WaitToShoot(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_Shoot(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_WaitToDie(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_Die(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_Freeze(EnOkuta* this, GlobalContext* globalCtx); +void EnOkuta_ProjectileFly(EnOkuta* this, GlobalContext* globalCtx); + +const ActorInit En_Okuta_InitVars = { + ACTOR_EN_OKUTA, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_OKUTA, + sizeof(EnOkuta), + (ActorFunc)EnOkuta_Init, + (ActorFunc)EnOkuta_Destroy, + (ActorFunc)EnOkuta_Update, + (ActorFunc)EnOkuta_Draw, + NULL, +}; + +static ColliderCylinderInit sProjectileColliderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 13, 20, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sOctorockColliderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 40, -30, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 15, 60, 100 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0x3), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x42, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 6500, ICHAIN_STOP), +}; + +void EnOkuta_Init(Actor* thisx, GlobalContext* globalCtx) { + EnOkuta* this = (EnOkuta*)thisx; + s32 pad; + WaterBox* outWaterBox; + f32 ySurface; + s32 sp30; + + Actor_ProcessInitChain(thisx, sInitChain); + this->numShots = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + if (thisx->params == 0) { + SkelAnime_Init(globalCtx, &this->skelAnime, &gOctorokSkel, &gOctorokAppearAnim, this->jointTable, + this->morphTable, 38); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sOctorockColliderInit); + CollisionCheck_SetInfo(&thisx->colChkInfo, &sDamageTable, &sColChkInfoInit); + if ((this->numShots == 0xFF) || (this->numShots == 0)) { + this->numShots = 1; + } + thisx->floorHeight = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &thisx->floorPoly, &sp30, thisx, &thisx->world.pos); + //! @bug calls WaterBox_GetSurfaceImpl directly + if (!WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, thisx->world.pos.x, thisx->world.pos.z, &ySurface, + &outWaterBox) || + (ySurface <= thisx->floorHeight)) { + Actor_Kill(thisx); + } else { + thisx->home.pos.y = ySurface; + } + EnOkuta_SetupWaitToAppear(this); + } else { + ActorShape_Init(&thisx->shape, 1100.0f, ActorShadow_DrawCircle, 18.0f); + thisx->flags &= ~ACTOR_FLAG_0; + thisx->flags |= ACTOR_FLAG_4; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sProjectileColliderInit); + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_PROP); + this->timer = 30; + thisx->shape.rot.y = 0; + this->actionFunc = EnOkuta_ProjectileFly; + thisx->speedXZ = 10.0f; + } +} + +void EnOkuta_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnOkuta* this = (EnOkuta*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnOkuta_SpawnBubbles(EnOkuta* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 10; i++) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, -10.0f, 10.0f, 30.0f, 0.25f); + } +} + +void EnOkuta_SpawnDust(Vec3f* pos, Vec3f* velocity, s16 scaleStep, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 150, 150, 150, 255 }; + + func_8002829C(globalCtx, pos, velocity, &accel, &primColor, &envColor, 0x190, scaleStep); +} + +void EnOkuta_SpawnSplash(EnOkuta* this, GlobalContext* globalCtx) { + EffectSsGSplash_Spawn(globalCtx, &this->actor.home.pos, NULL, NULL, 0, 1300); +} + +void EnOkuta_SpawnRipple(EnOkuta* this, GlobalContext* globalCtx) { + Vec3f pos; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.home.pos.y; + pos.z = this->actor.world.pos.z; + if ((globalCtx->gameplayFrames % 7) == 0 && + ((this->actionFunc != EnOkuta_Shoot) || ((this->actor.world.pos.y - this->actor.home.pos.y) < 50.0f))) { + EffectSsGRipple_Spawn(globalCtx, &pos, 250, 650, 0); + } +} + +void EnOkuta_SetupWaitToAppear(EnOkuta* this) { + this->actor.draw = NULL; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnOkuta_WaitToAppear; + this->actor.world.pos.y = this->actor.home.pos.y; +} + +void EnOkuta_SetupAppear(EnOkuta* this, GlobalContext* globalCtx) { + this->actor.draw = EnOkuta_Draw; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.flags |= ACTOR_FLAG_0; + Animation_PlayOnce(&this->skelAnime, &gOctorokAppearAnim); + EnOkuta_SpawnBubbles(this, globalCtx); + this->actionFunc = EnOkuta_Appear; +} + +void EnOkuta_SetupHide(EnOkuta* this) { + Animation_PlayOnce(&this->skelAnime, &gOctorokHideAnim); + this->actionFunc = EnOkuta_Hide; +} + +void EnOkuta_SetupWaitToShoot(EnOkuta* this) { + Animation_PlayLoop(&this->skelAnime, &gOctorokFloatAnim); + this->timer = (this->actionFunc == EnOkuta_Shoot) ? 2 : 0; + this->actionFunc = EnOkuta_WaitToShoot; +} + +void EnOkuta_SetupShoot(EnOkuta* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gOctorokShootAnim); + if (this->actionFunc != EnOkuta_Shoot) { + this->timer = this->numShots; + } + this->jumpHeight = this->actor.yDistToPlayer + 20.0f; + this->jumpHeight = CLAMP_MIN(this->jumpHeight, 10.0f); + if (this->jumpHeight > 50.0f) { + EnOkuta_SpawnSplash(this, globalCtx); + } + if (this->jumpHeight > 50.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_JUMP); + } + this->actionFunc = EnOkuta_Shoot; +} + +void EnOkuta_SetupWaitToDie(EnOkuta* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gOctorokHitAnim, -5.0f); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xB); + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetScale(&this->actor, 0.01f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_DEAD1); + this->actionFunc = EnOkuta_WaitToDie; +} + +void EnOkuta_SetupDie(EnOkuta* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gOctorokDieAnim, -3.0f); + this->timer = 0; + this->actionFunc = EnOkuta_Die; +} + +void EnOkuta_SetupFreeze(EnOkuta* this) { + this->timer = 80; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + this->actionFunc = EnOkuta_Freeze; +} + +void EnOkuta_SpawnProjectile(EnOkuta* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + f32 sin = Math_SinS(this->actor.shape.rot.y); + f32 cos = Math_CosS(this->actor.shape.rot.y); + + pos.x = this->actor.world.pos.x + (25.0f * sin); + pos.y = this->actor.world.pos.y - 6.0f; + pos.z = this->actor.world.pos.z + (25.0f * cos); + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_OKUTA, pos.x, pos.y, pos.z, this->actor.shape.rot.x, + this->actor.shape.rot.y, this->actor.shape.rot.z, 0x10) != NULL) { + pos.x = this->actor.world.pos.x + (40.0f * sin); + pos.z = this->actor.world.pos.z + (40.0f * cos); + pos.y = this->actor.world.pos.y; + velocity.x = 1.5f * sin; + velocity.y = 0.0f; + velocity.z = 1.5f * cos; + EnOkuta_SpawnDust(&pos, &velocity, 20, globalCtx); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_THROW); +} + +void EnOkuta_WaitToAppear(EnOkuta* this, GlobalContext* globalCtx) { + this->actor.world.pos.y = this->actor.home.pos.y; + if ((this->actor.xzDistToPlayer < 480.0f) && (this->actor.xzDistToPlayer > 200.0f)) { + EnOkuta_SetupAppear(this, globalCtx); + } +} + +void EnOkuta_Appear(EnOkuta* this, GlobalContext* globalCtx) { + s32 pad; + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.xzDistToPlayer < 160.0f) { + EnOkuta_SetupHide(this); + } else { + EnOkuta_SetupWaitToShoot(this); + } + } else if (this->skelAnime.curFrame <= 4.0f) { + Actor_SetScale(&this->actor, this->skelAnime.curFrame * 0.25f * 0.01f); + } else if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + Actor_SetScale(&this->actor, 0.01f); + } + if (Animation_OnFrame(&this->skelAnime, 2.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_JUMP); + } + if (Animation_OnFrame(&this->skelAnime, 12.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_LAND); + } + if (Animation_OnFrame(&this->skelAnime, 3.0f) || Animation_OnFrame(&this->skelAnime, 15.0f)) { + EnOkuta_SpawnSplash(this, globalCtx); + } +} + +void EnOkuta_Hide(EnOkuta* this, GlobalContext* globalCtx) { + s32 pad; + + Math_ApproachF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f, 30.0f); + if (SkelAnime_Update(&this->skelAnime)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_BUBLE); + EnOkuta_SpawnBubbles(this, globalCtx); + EnOkuta_SetupWaitToAppear(this); + } else if (this->skelAnime.curFrame >= 4.0f) { + Actor_SetScale(&this->actor, (6.0f - this->skelAnime.curFrame) * 0.5f * 0.01f); + } + if (Animation_OnFrame(&this->skelAnime, 2.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_SINK); + } + if (Animation_OnFrame(&this->skelAnime, 4.0f)) { + EnOkuta_SpawnSplash(this, globalCtx); + } +} + +void EnOkuta_WaitToShoot(EnOkuta* this, GlobalContext* globalCtx) { + s16 temp_v0_2; + s32 phi_v1; + + this->actor.world.pos.y = this->actor.home.pos.y; + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + if (this->timer != 0) { + this->timer--; + } + } + if (Animation_OnFrame(&this->skelAnime, 0.5f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_FLOAT); + } + if (this->actor.xzDistToPlayer < 160.0f || this->actor.xzDistToPlayer > 560.0f) { + EnOkuta_SetupHide(this); + } else { + temp_v0_2 = Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x71C, 0x38E); + phi_v1 = ABS(temp_v0_2); + if ((phi_v1 < 0x38E) && (this->timer == 0) && (this->actor.yDistToPlayer < 200.0f)) { + EnOkuta_SetupShoot(this, globalCtx); + } + } +} + +void EnOkuta_Shoot(EnOkuta* this, GlobalContext* globalCtx) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 3, 0x71C); + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + EnOkuta_SetupWaitToShoot(this); + } else { + EnOkuta_SetupShoot(this, globalCtx); + } + } else { + f32 curFrame = this->skelAnime.curFrame; + + if (curFrame < 13.0f) { + this->actor.world.pos.y = (sinf((0.08333f * M_PI) * curFrame) * this->jumpHeight) + this->actor.home.pos.y; + } + if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + EnOkuta_SpawnProjectile(this, globalCtx); + } + if ((this->jumpHeight > 50.0f) && Animation_OnFrame(&this->skelAnime, 13.0f)) { + EnOkuta_SpawnSplash(this, globalCtx); + } + if ((this->jumpHeight > 50.0f) && Animation_OnFrame(&this->skelAnime, 13.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_LAND); + } + } + if (this->actor.xzDistToPlayer < 160.0f) { + EnOkuta_SetupHide(this); + } +} + +void EnOkuta_WaitToDie(EnOkuta* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnOkuta_SetupDie(this); + } + Math_ApproachF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f, 5.0f); +} + +void EnOkuta_Die(EnOkuta* this, GlobalContext* globalCtx) { + static Vec3f accel = { 0.0f, -0.5f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 150, 150, 150, 0 }; + Vec3f velocity; + Vec3f pos; + s32 i; + + if (SkelAnime_Update(&this->skelAnime)) { + this->timer++; + } + Math_ApproachF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f, 5.0f); + if (this->timer == 5) { + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 40.0f; + pos.z = this->actor.world.pos.z; + velocity.x = 0.0f; + velocity.y = -0.5f; + velocity.z = 0.0f; + EnOkuta_SpawnDust(&pos, &velocity, -0x14, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_DEAD2); + } + if (Animation_OnFrame(&this->skelAnime, 15.0f)) { + EnOkuta_SpawnSplash(this, globalCtx); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_LAND); + } + if (this->timer < 3) { + Actor_SetScale(&this->actor, ((this->timer * 0.25f) + 1.0f) * 0.01f); + } else if (this->timer < 6) { + Actor_SetScale(&this->actor, (1.5f - ((this->timer - 2) * 0.2333f)) * 0.01f); + } else if (this->timer < 11) { + Actor_SetScale(&this->actor, (((this->timer - 5) * 0.04f) + 0.8f) * 0.01f); + } else { + if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0005f)) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 30, NA_SE_EN_OCTAROCK_BUBLE); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x70); + for (i = 0; i < 20; i++) { + velocity.x = (Rand_ZeroOne() - 0.5f) * 7.0f; + velocity.y = Rand_ZeroOne() * 7.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 7.0f; + EffectSsDtBubble_SpawnCustomColor(globalCtx, &this->actor.world.pos, &velocity, &accel, &primColor, + &envColor, Rand_S16Offset(100, 50), 25, 0); + } + Actor_Kill(&this->actor); + } + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + } +} + +void EnOkuta_Freeze(EnOkuta* this, GlobalContext* globalCtx) { + Vec3f pos; + s16 temp_v1; + + if (this->timer != 0) { + this->timer--; + } + if (this->timer == 0) { + EnOkuta_SetupDie(this); + } + if ((this->timer >= 64) && (this->timer & 1)) { + temp_v1 = (this->timer - 64) >> 1; + pos.y = (this->actor.world.pos.y - 32.0f) + (8.0f * (8 - temp_v1)); + pos.x = this->actor.world.pos.x + ((temp_v1 & 2) ? 10.0f : -10.0f); + pos.z = this->actor.world.pos.z + ((temp_v1 & 1) ? 10.0f : -10.0f); + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &pos, 150, 150, 150, 250, 235, 245, 255, + (Rand_ZeroOne() * 0.2f) + 1.9f); + } + Math_ApproachF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f, 5.0f); +} + +void EnOkuta_ProjectileFly(EnOkuta* this, GlobalContext* globalCtx) { + Vec3f pos; + Player* player = GET_PLAYER(globalCtx); + Vec3s sp40; + + this->timer--; + if (this->timer == 0) { + this->actor.gravity = -1.0f; + } + this->actor.home.rot.z += 0x1554; + if (this->actor.bgCheckFlags & 0x20) { + this->actor.gravity = -1.0f; + this->actor.speedXZ -= 0.1f; + this->actor.speedXZ = CLAMP_MIN(this->actor.speedXZ, 1.0f); + } + if ((this->actor.bgCheckFlags & 8) || (this->actor.bgCheckFlags & 1) || (this->collider.base.atFlags & AT_HIT) || + this->collider.base.acFlags & AC_HIT || this->collider.base.ocFlags1 & OC1_HIT || + this->actor.floorHeight == BGCHECK_Y_MIN) { + if ((player->currentShield == PLAYER_SHIELD_DEKU || + (player->currentShield == PLAYER_SHIELD_HYLIAN && LINK_IS_ADULT)) && + this->collider.base.atFlags & AT_HIT && this->collider.base.atFlags & AT_TYPE_ENEMY && + this->collider.base.atFlags & AT_BOUNCED) { + this->collider.base.atFlags &= ~(AT_HIT | AT_BOUNCED | AT_TYPE_ENEMY); + this->collider.base.atFlags |= AT_TYPE_PLAYER; + this->collider.info.toucher.dmgFlags = 2; + Matrix_MtxFToYXZRotS(&player->shieldMf, &sp40, 0); + this->actor.world.rot.y = sp40.y + 0x8000; + this->timer = 30; + } else { + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 11.0f; + pos.z = this->actor.world.pos.z; + EffectSsHahen_SpawnBurst(globalCtx, &pos, 6.0f, 0, 1, 2, 15, 7, 10, gOctorokProjectileDL); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EN_OCTAROCK_ROCK); + Actor_Kill(&this->actor); + } + } else if (this->timer == -300) { + Actor_Kill(&this->actor); + } +} + +void EnOkuta_UpdateHeadScale(EnOkuta* this) { + f32 curFrame = this->skelAnime.curFrame; + + if (this->actionFunc == EnOkuta_Appear) { + if (curFrame < 8.0f) { + this->headScale.x = this->headScale.y = this->headScale.z = 1.0f; + } else if (curFrame < 10.0f) { + this->headScale.x = this->headScale.z = 1.0f; + this->headScale.y = ((curFrame - 7.0f) * 0.4f) + 1.0f; + } else if (curFrame < 14.0f) { + this->headScale.x = this->headScale.z = ((curFrame - 9.0f) * 0.075f) + 1.0f; + this->headScale.y = 1.8f - ((curFrame - 9.0f) * 0.25f); + } else { + this->headScale.x = this->headScale.z = 1.3f - ((curFrame - 13.0f) * 0.05f); + this->headScale.y = ((curFrame - 13.0f) * 0.0333f) + 0.8f; + } + } else if (this->actionFunc == EnOkuta_Hide) { + if (curFrame < 3.0f) { + this->headScale.y = 1.0f; + } else if (curFrame < 4.0f) { + this->headScale.y = (curFrame - 2.0f) + 1.0f; + } else { + this->headScale.y = 2.0f - ((curFrame - 3.0f) * 0.333f); + } + this->headScale.x = this->headScale.z = 1.0f; + } else if (this->actionFunc == EnOkuta_Shoot) { + if (curFrame < 5.0f) { + this->headScale.x = this->headScale.y = this->headScale.z = (curFrame * 0.125f) + 1.0f; + } else if (curFrame < 7.0f) { + this->headScale.x = this->headScale.y = this->headScale.z = 1.5f - ((curFrame - 4.0f) * 0.35f); + } else if (curFrame < 17.0f) { + this->headScale.x = this->headScale.z = ((curFrame - 6.0f) * 0.05f) + 0.8f; + this->headScale.y = 0.8f; + } else { + this->headScale.x = this->headScale.z = 1.3f - ((curFrame - 16.0f) * 0.1f); + this->headScale.y = ((curFrame - 16.0f) * 0.0666f) + 0.8f; + } + } else if (this->actionFunc == EnOkuta_WaitToShoot) { + this->headScale.x = this->headScale.z = 1.0f; + this->headScale.y = (sinf((M_PI / 16) * curFrame) * 0.2f) + 1.0f; + } else { + this->headScale.x = this->headScale.y = this->headScale.z = 1.0f; + } +} + +void EnOkuta_ColliderCheck(EnOkuta* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.colChkInfo.health = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->actor.colChkInfo.damageEffect == 3) { + EnOkuta_SetupFreeze(this); + } else { + EnOkuta_SetupWaitToDie(this); + } + } + } +} + +void EnOkuta_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnOkuta* this = (EnOkuta*)thisx; + GlobalContext* globalCtx = globalCtx2; + Player* player = GET_PLAYER(globalCtx); + WaterBox* outWaterBox; + f32 ySurface; + Vec3f sp38; + s32 sp34; + + if (!(player->stateFlags1 & 0x300000C0)) { + if (this->actor.params == 0) { + EnOkuta_ColliderCheck(this, globalCtx); + if (!WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, + this->actor.world.pos.z, &ySurface, &outWaterBox) || + (ySurface < this->actor.floorHeight)) { + if (this->actor.colChkInfo.health != 0) { + Actor_Kill(&this->actor); + return; + } + } else { + this->actor.home.pos.y = ySurface; + } + } + this->actionFunc(this, globalCtx); + if (this->actor.params == 0) { + EnOkuta_UpdateHeadScale(this); + this->collider.dim.height = + (((sOctorockColliderInit.dim.height * this->headScale.y) - this->collider.dim.yShift) * + this->actor.scale.y * 100.0f); + } else { + sp34 = false; + Actor_MoveForward(&this->actor); + Math_Vec3f_Copy(&sp38, &this->actor.world.pos); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 15.0f, 30.0f, 5); + if ((this->actor.bgCheckFlags & 8) && + SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, this->actor.wallPoly, this->actor.wallBgId)) { + sp34 = true; + this->actor.bgCheckFlags &= ~8; + } + if ((this->actor.bgCheckFlags & 1) && + SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId)) { + sp34 = true; + this->actor.bgCheckFlags &= ~1; + } + if (sp34 && !(this->actor.bgCheckFlags & 9)) { + Math_Vec3f_Copy(&this->actor.world.pos, &sp38); + } + } + Collider_UpdateCylinder(&this->actor, &this->collider); + if ((this->actionFunc == EnOkuta_Appear) || (this->actionFunc == EnOkuta_Hide)) { + this->collider.dim.pos.y = this->actor.world.pos.y + (this->skelAnime.jointTable->y * this->actor.scale.y); + this->collider.dim.radius = sOctorockColliderInit.dim.radius * this->actor.scale.x * 100.0f; + } + if (this->actor.params == 0x10) { + this->actor.flags |= ACTOR_FLAG_24; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->actionFunc != EnOkuta_WaitToAppear) { + if ((this->actionFunc != EnOkuta_Die) && (this->actionFunc != EnOkuta_WaitToDie) && + (this->actionFunc != EnOkuta_Freeze)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Actor_SetFocus(&this->actor, 15.0f); + if ((this->actor.params == 0) && (this->actor.draw != NULL)) { + EnOkuta_SpawnRipple(this, globalCtx); + } + } +} + +s32 EnOkuta_GetSnoutScale(EnOkuta* this, f32 curFrame, Vec3f* scale) { + if (this->actionFunc == EnOkuta_WaitToShoot) { + scale->x = scale->z = 1.0f; + scale->y = (sinf((M_PI / 16) * curFrame) * 0.4f) + 1.0f; + } else if (this->actionFunc == EnOkuta_Shoot) { + if (curFrame < 5.0f) { + scale->x = 1.0f; + scale->y = scale->z = (curFrame * 0.25f) + 1.0f; + } else if (curFrame < 7.0f) { + scale->x = (curFrame - 4.0f) * 0.5f + 1.0f; + scale->y = scale->z = 2.0f - (curFrame - 4.0f) * 0.5f; + } else { + scale->x = 2.0f - ((curFrame - 6.0f) * 0.0769f); + scale->y = scale->z = 1.0f; + } + } else if (this->actionFunc == EnOkuta_Die) { + if (curFrame >= 35.0f || curFrame < 25.0f) { + return false; + } + if (curFrame < 27.0f) { + scale->x = 1.0f; + scale->y = scale->z = ((curFrame - 24.0f) * 0.5f) + 1.0f; + } else if (curFrame < 30.0f) { + scale->x = (curFrame - 26.0f) * 0.333f + 1.0f; + scale->y = scale->z = 2.0f - (curFrame - 26.0f) * 0.333f; + } else { + scale->x = 2.0f - ((curFrame - 29.0f) * 0.2f); + scale->y = scale->z = 1.0f; + } + } else { + return false; + } + + return true; +} + +s32 EnOkuta_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnOkuta* this = (EnOkuta*)thisx; + f32 curFrame = this->skelAnime.curFrame; + Vec3f scale; + s32 doScale = false; + + if (this->actionFunc == EnOkuta_Die) { + curFrame += this->timer; + } + if (limbIndex == 5) { + if ((this->headScale.x != 1.0f) || (this->headScale.y != 1.0f) || (this->headScale.z != 1.0f)) { + scale = this->headScale; + doScale = true; + } + } else if (limbIndex == 8) { + doScale = EnOkuta_GetSnoutScale(this, curFrame, &scale); + } + if (doScale) { + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + } + return false; +} + +void EnOkuta_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnOkuta* this = (EnOkuta*)thisx; + s32 pad; + + func_80093D18(globalCtx->state.gfxCtx); + + if (this->actor.params == 0) { + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnOkuta_OverrideLimbDraw, + NULL, this); + } else { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_okuta.c", 1653); + + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(this->actor.home.rot.z * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_okuta.c", 1657), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gOctorokProjectileDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_okuta.c", 1662); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.h b/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.h new file mode 100644 index 000000000..88beeaa1a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_OKUTA_H +#define Z_EN_OKUTA_H + +#include "ultra64.h" +#include "global.h" + +struct EnOkuta; + +typedef void (*EnOkutaActionFunc)(struct EnOkuta*, GlobalContext*); + +typedef struct EnOkuta { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnOkutaActionFunc actionFunc; + /* 0x0194 */ s16 timer; + /* 0x0196 */ s16 numShots; + /* 0x0198 */ Vec3s jointTable[38]; + /* 0x027C */ Vec3s morphTable[38]; + /* 0x0360 */ f32 jumpHeight; + /* 0x0364 */ Vec3f headScale; + /* 0x0370 */ ColliderCylinder collider; +} EnOkuta; // size = 0x03BC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c new file mode 100644 index 000000000..04975d910 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c @@ -0,0 +1,2514 @@ +#include "z_en_ossan.h" +#include "vt.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_ossan/object_ossan.h" +#include "objects/object_oF1d_map/object_oF1d_map.h" +#include "objects/object_os/object_os.h" +#include "objects/object_zo/object_zo.h" +#include "objects/object_rs/object_rs.h" +#include "objects/object_ds2/object_ds2.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/object_masterkokiri/object_masterkokiri.h" +#include "objects/object_km1/object_km1.h" +#include "objects/object_mastergolon/object_mastergolon.h" +#include "objects/object_masterzoora/object_masterzoora.h" +#include "objects/object_masterkokirihead/object_masterkokirihead.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnOssan_Init(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_Update(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawKokiriShopkeeper(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawPotionShopkeeper(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawBombchuShopkeeper(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawBazaarShopkeeper(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawZoraShopkeeper(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawGoronShopkeeper(Actor* thisx, GlobalContext* globalCtx); +void EnOssan_DrawHappyMaskShopkeeper(Actor* thisx, GlobalContext* globalCtx); + +void EnOssan_InitActionFunc(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_MainActionFunc(EnOssan* this, GlobalContext* globalCtx); + +void EnOssan_TalkDefaultShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkKokiriShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkKakarikoPotionShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkBombchuShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkMarketPotionShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkBazaarShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkZoraShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkGoronShopkeeper(GlobalContext* globalCtx); +void EnOssan_TalkHappyMaskShopkeeper(GlobalContext* globalCtx); + +s16 ShopItemDisp_Default(s16 v); +s16 ShopItemDisp_SpookyMask(s16 v); +s16 ShopItemDisp_SkullMask(s16 v); +s16 ShopItemDisp_BunnyHood(s16 v); +s16 ShopItemDisp_ZoraMask(s16 v); +s16 ShopItemDisp_GoronMask(s16 v); +s16 ShopItemDisp_GerudoMask(s16 v); + +void EnOssan_InitKokiriShopkeeper(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_InitPotionShopkeeper(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_InitBombchuShopkeeper(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_InitBazaarShopkeeper(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_InitZoraShopkeeper(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_InitGoronShopkeeper(EnOssan* this, GlobalContext* globalCtx); +void EnOssan_InitHappyMaskShopkeeper(EnOssan* this, GlobalContext* globalCtx); + +void EnOssan_State_Idle(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_StartConversation(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_FacingShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_TalkingToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_LookToLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_LookToRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_BrowseLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_BrowseRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_LookFromShelfToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_ItemSelected(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_SelectMilkBottle(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_SelectWeirdEgg(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_SelectUnimplementedItem(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_SelectBombs(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_CantGetItem(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_GiveItemWithFanfare(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_ItemPurchased(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_ContinueShoppingPrompt(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_GiveLonLonMilk(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_DisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_WaitForDisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_21(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_22(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_QuickBuyDialog(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_SelectMaskItem(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_LendMaskOfTruth(EnOssan* this, GlobalContext* globalCtx, Player* player); +void EnOssan_State_GiveDiscountDialog(EnOssan* this, GlobalContext* globalCtx, Player* player); + +void EnOssan_Obj3ToSeg6(EnOssan* this, GlobalContext* globalCtx); + +void EnOssan_StartShopping(GlobalContext* globalCtx, EnOssan* this); + +void EnOssan_WaitForBlink(EnOssan* this); +void EnOssan_Blink(EnOssan* this); + +u16 EnOssan_SetupHelloDialog(EnOssan* this); + +s32 EnOssan_TakeItemOffShelf(EnOssan* this); +s32 EnOssan_ReturnItemToShelf(EnOssan* this); +void EnOssan_ResetItemPosition(EnOssan* this); +void EnOssan_SetStateGiveDiscountDialog(GlobalContext* globalCtx, EnOssan* this); + +#define CURSOR_INVALID 0xFF + +const ActorInit En_Ossan_InitVars = { + ACTOR_EN_OSSAN, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnOssan), + (ActorFunc)EnOssan_Init, + (ActorFunc)EnOssan_Destroy, + (ActorFunc)EnOssan_Update, + NULL, + NULL, +}; + +// Unused collider +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 30, 80, 0, { 0, 0, 0 } }, +}; + +// Rupees to pay back to Happy Mask Shop +static s16 sMaskPaymentPrice[] = { 10, 30, 20, 50 }; + +// item yaw offsets +static s16 sItemShelfRot[] = { 0xEAAC, 0xEAAC, 0xEAAC, 0xEAAC, 0x1554, 0x1554, 0x1554, 0x1554 }; + +// unused values? +static s16 D_80AC8904[] = { 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025 }; + +static char* sShopkeeperPrintName[] = { + "コキリの店 ", // "Kokiri Shop" + "薬屋 ", // "Potion Shop" + "夜の店 ", // "Night Shop" + "路地裏の店 ", // "Back Alley Shop" + "盾の店 ", // "Shield Shop" + "大人の店 ", // "Adult Shop" + "タロンの店 ", // "Talon Shop" + "ゾーラの店 ", // "Zora Shop" + "ゴロン夜の店", // "Goron Night Shop" + "インゴーの店", // "Ingo Store" + "お面屋 ", // "Mask Shop" +}; + +typedef struct { + /* 0x00 */ s16 objId; + /* 0x02 */ s16 unk_02; + /* 0x04 */ s16 unk_04; +} ShopkeeperObjInfo; + +static s16 sShopkeeperObjectIds[][3] = { + { OBJECT_KM1, OBJECT_MASTERKOKIRIHEAD, OBJECT_MASTERKOKIRI }, + { OBJECT_DS2, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_RS, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_DS2, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_ZO, OBJECT_ID_MAX, OBJECT_MASTERZOORA }, + { OBJECT_OF1D_MAP, OBJECT_ID_MAX, OBJECT_MASTERGOLON }, + { OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX }, + { OBJECT_OS, OBJECT_ID_MAX, OBJECT_ID_MAX }, +}; + +static EnOssanTalkOwnerFunc sShopkeeperTalkOwner[] = { + EnOssan_TalkKokiriShopkeeper, EnOssan_TalkKakarikoPotionShopkeeper, EnOssan_TalkBombchuShopkeeper, + EnOssan_TalkMarketPotionShopkeeper, EnOssan_TalkBazaarShopkeeper, EnOssan_TalkDefaultShopkeeper, + EnOssan_TalkDefaultShopkeeper, EnOssan_TalkZoraShopkeeper, EnOssan_TalkGoronShopkeeper, + EnOssan_TalkDefaultShopkeeper, EnOssan_TalkHappyMaskShopkeeper, +}; + +static f32 sShopkeeperScale[] = { + 0.01f, 0.011f, 0.0105f, 0.011f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, +}; + +typedef struct { + /* 0x00 */ s16 shopItemIndex; + /* 0x02 */ s16 xOffset; + /* 0x04 */ s16 yOffset; + /* 0x06 */ s16 zOffset; +} ShopItem; // size 0x08 + +ShopItem sShopkeeperStores[][8] = { + { { SI_DEKU_SHIELD, 50, 52, -20 }, + { SI_DEKU_NUTS_5, 50, 76, -20 }, + { SI_DEKU_NUTS_10, 80, 52, -3 }, + { SI_DEKU_STICK, 80, 76, -3 }, + { SI_DEKU_SEEDS_30, -50, 52, -20 }, + { SI_ARROWS_10, -50, 76, -20 }, + { SI_ARROWS_30, -80, 52, -3 }, + { SI_HEART, -80, 76, -3 } }, + + { { SI_GREEN_POTION, 50, 52, -20 }, + { SI_BLUE_FIRE, 50, 76, -20 }, + { SI_RED_POTION_R30, 80, 52, -3 }, + { SI_FAIRY, 80, 76, -3 }, + { SI_DEKU_NUTS_5, -50, 52, -20 }, + { SI_BUGS, -50, 76, -20 }, + { SI_POE, -80, 52, -3 }, + { SI_FISH, -80, 76, -3 } }, + + { { SI_BOMBCHU_10_2, 50, 52, -20 }, + { SI_BOMBCHU_10_4, 50, 76, -20 }, + { SI_BOMBCHU_10_3, 80, 52, -3 }, + { SI_BOMBCHU_10_1, 80, 76, -3 }, + { SI_BOMBCHU_20_3, -50, 52, -20 }, + { SI_BOMBCHU_20_1, -50, 76, -20 }, + { SI_BOMBCHU_20_4, -80, 52, -3 }, + { SI_BOMBCHU_20_2, -80, 76, -3 } }, + + { { SI_GREEN_POTION, 50, 52, -20 }, + { SI_BLUE_FIRE, 50, 76, -20 }, + { SI_RED_POTION_R30, 80, 52, -3 }, + { SI_FAIRY, 80, 76, -3 }, + { SI_DEKU_NUTS_5, -50, 52, -20 }, + { SI_BUGS, -50, 76, -20 }, + { SI_POE, -80, 52, -3 }, + { SI_FISH, -80, 76, -3 } }, + + { { SI_HYLIAN_SHIELD, 50, 52, -20 }, + { SI_BOMBS_5_R35, 50, 76, -20 }, + { SI_DEKU_NUTS_5, 80, 52, -3 }, + { SI_HEART, 80, 76, -3 }, + { SI_ARROWS_10, -50, 52, -20 }, + { SI_ARROWS_50, -50, 76, -20 }, + { SI_DEKU_STICK, -80, 52, -3 }, + { SI_ARROWS_30, -80, 76, -3 } }, + + { { SI_HYLIAN_SHIELD, 50, 52, -20 }, + { SI_BOMBS_5_R25, 50, 76, -20 }, + { SI_DEKU_NUTS_5, 80, 52, -3 }, + { SI_HEART, 80, 76, -3 }, + { SI_ARROWS_10, -50, 52, -20 }, + { SI_ARROWS_50, -50, 76, -20 }, + { SI_DEKU_STICK, -80, 52, -3 }, + { SI_ARROWS_30, -80, 76, -3 } }, + + { { SI_MILK_BOTTLE, 50, 52, -20 }, + { SI_DEKU_NUTS_5, 50, 76, -20 }, + { SI_DEKU_NUTS_10, 80, 52, -3 }, + { SI_HEART, 80, 76, -3 }, + { SI_WEIRD_EGG, -50, 52, -20 }, + { SI_DEKU_STICK, -50, 76, -20 }, + { SI_HEART, -80, 52, -3 }, + { SI_HEART, -80, 76, -3 } }, + + { { SI_ZORA_TUNIC, 50, 52, -20 }, + { SI_ARROWS_10, 50, 76, -20 }, + { SI_HEART, 80, 52, -3 }, + { SI_ARROWS_30, 80, 76, -3 }, + { SI_DEKU_NUTS_5, -50, 52, -20 }, + { SI_ARROWS_50, -50, 76, -20 }, + { SI_FISH, -80, 52, -3 }, + { SI_RED_POTION_R50, -80, 76, -3 } }, + + { { SI_BOMBS_5_R25, 50, 52, -20 }, + { SI_BOMBS_10, 50, 76, -20 }, + { SI_BOMBS_20, 80, 52, -3 }, + { SI_BOMBS_30, 80, 76, -3 }, + { SI_GORON_TUNIC, -50, 52, -20 }, + { SI_HEART, -50, 76, -20 }, + { SI_RED_POTION_R40, -80, 52, -3 }, + { SI_HEART, -80, 76, -3 } }, + + { { SI_19, 50, 52, -20 }, + { SI_19, 50, 76, -20 }, + { SI_19, 80, 52, -3 }, + { SI_19, 80, 76, -3 }, + { SI_20, -50, 52, -20 }, + { SI_20, -50, 76, -20 }, + { SI_20, -80, 52, -3 }, + { SI_20, -80, 76, -3 } }, + + { { SI_GERUDO_MASK, 50, 52, -20 }, + { SI_ZORA_MASK, 50, 76, -20 }, + { SI_MASK_OF_TRUTH, 80, 52, -3 }, + { SI_GORON_MASK, 80, 76, -3 }, + { SI_SKULL_MASK, -50, 52, -20 }, + { SI_KEATON_MASK, -50, 76, -20 }, + { SI_BUNNY_HOOD, -80, 52, -3 }, + { SI_SPOOKY_MASK, -80, 76, -3 } }, +}; +static EnOssanGetGirlAParamsFunc sShopItemReplaceFunc[] = { + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_SpookyMask, + ShopItemDisp_SkullMask, ShopItemDisp_BunnyHood, ShopItemDisp_Default, ShopItemDisp_ZoraMask, + ShopItemDisp_GoronMask, ShopItemDisp_GerudoMask, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, + ShopItemDisp_Default, ShopItemDisp_Default, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP), +}; + +// When selecting an item to buy, this is the position the item moves to +static Vec3f sSelectedItemPosition[] = { { 17.0f, 58.0f, 30.0f }, { -17.0f, 58.0f, 30.0f } }; + +static EnOssanInitFunc sInitFuncs[] = { + EnOssan_InitKokiriShopkeeper, EnOssan_InitPotionShopkeeper, EnOssan_InitBombchuShopkeeper, + EnOssan_InitPotionShopkeeper, EnOssan_InitBazaarShopkeeper, EnOssan_InitBazaarShopkeeper, + EnOssan_InitBazaarShopkeeper, EnOssan_InitZoraShopkeeper, EnOssan_InitGoronShopkeeper, + EnOssan_InitBazaarShopkeeper, EnOssan_InitHappyMaskShopkeeper, +}; + +static Vec3f sShopkeeperPositionOffsets[] = { + { 0.0f, 0.0f, 33.0f }, { 0.0f, 0.0f, 31.0f }, { 0.0f, 0.0f, 31.0f }, { 0.0f, 0.0f, 31.0f }, + { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 36.0f }, + { 0.0f, 0.0f, 15.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 26.0f }, +}; + +static EnOssanStateFunc sStateFunc[] = { + EnOssan_State_Idle, + EnOssan_State_StartConversation, + EnOssan_State_FacingShopkeeper, + EnOssan_State_TalkingToShopkeeper, + EnOssan_State_LookToLeftShelf, + EnOssan_State_LookToRightShelf, + EnOssan_State_BrowseLeftShelf, + EnOssan_State_BrowseRightShelf, + EnOssan_State_LookFromShelfToShopkeeper, + EnOssan_State_ItemSelected, + EnOssan_State_SelectMilkBottle, + EnOssan_State_SelectWeirdEgg, + EnOssan_State_SelectUnimplementedItem, + EnOssan_State_SelectBombs, + EnOssan_State_CantGetItem, + EnOssan_State_GiveItemWithFanfare, + EnOssan_State_ItemPurchased, + EnOssan_State_ContinueShoppingPrompt, + EnOssan_State_GiveLonLonMilk, + EnOssan_State_DisplayOnlyBombDialog, + EnOssan_State_WaitForDisplayOnlyBombDialog, + EnOssan_State_21, + EnOssan_State_22, + EnOssan_State_QuickBuyDialog, + EnOssan_State_SelectMaskItem, + EnOssan_State_LendMaskOfTruth, + EnOssan_State_GiveDiscountDialog, +}; + +void EnOssan_SetupAction(EnOssan* this, EnOssanActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +s16 ShopItemDisp_Default(s16 v) { + return v; +} + +s16 ShopItemDisp_SpookyMask(s16 v) { + // Sold Skull Mask + if (gSaveContext.itemGetInf[3] & 0x200) { + return v; + } + return -1; +} + +s16 ShopItemDisp_SkullMask(s16 v) { + // Sold Keaton Mask + if (gSaveContext.itemGetInf[3] & 0x100) { + return v; + } + return -1; +} + +s16 ShopItemDisp_BunnyHood(s16 v) { + // Sold Spooky Mask + if (gSaveContext.itemGetInf[3] & 0x400) { + return v; + } + return -1; +} + +s16 ShopItemDisp_ZoraMask(s16 v) { + // Obtained Mask of Truth + if (gSaveContext.itemGetInf[3] & 0x8000) { + return v; + } + return -1; +} + +s16 ShopItemDisp_GoronMask(s16 v) { + // Obtained Mask of Truth + if (gSaveContext.itemGetInf[3] & 0x8000) { + return v; + } + return -1; +} + +s16 ShopItemDisp_GerudoMask(s16 v) { + // Obtained Mask of Truth + if (gSaveContext.itemGetInf[3] & 0x8000) { + return v; + } + return -1; +} + +void EnOssan_SpawnItemsOnShelves(EnOssan* this, GlobalContext* globalCtx, ShopItem* shopItems) { + EnTana* shelves; + s16 itemParams; + s32 i; + + for (i = 0; i < 8; i++, shopItems++) { + if (shopItems->shopItemIndex < 0) { + this->shelfSlots[i] = NULL; + } else { + itemParams = sShopItemReplaceFunc[shopItems->shopItemIndex](shopItems->shopItemIndex); + + if (itemParams < 0) { + this->shelfSlots[i] = NULL; + } else { + shelves = this->shelves; + this->shelfSlots[i] = (EnGirlA*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_GIRLA, shelves->actor.world.pos.x + shopItems->xOffset, + shelves->actor.world.pos.y + shopItems->yOffset, shelves->actor.world.pos.z + shopItems->zOffset, + shelves->actor.shape.rot.x, shelves->actor.shape.rot.y + sItemShelfRot[i], + shelves->actor.shape.rot.z, itemParams); + } + } + } +} + +void EnOssan_UpdateShopOfferings(EnOssan* this, GlobalContext* globalCtx) { + s32 i; + ShopItem* storeItems; + ShopItem* shopItem; + + if (this->actor.params == OSSAN_TYPE_MASK) { + storeItems = sShopkeeperStores[this->actor.params]; + if (1) {} + for (i = 0; i < 8; i++) { + shopItem = &storeItems[i]; + if (shopItem->shopItemIndex >= 0 && this->shelfSlots[i] == NULL) { + s16 params = sShopItemReplaceFunc[shopItem->shopItemIndex](shopItem->shopItemIndex); + + if (params >= 0) { + this->shelfSlots[i] = (EnGirlA*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_GIRLA, + this->shelves->actor.world.pos.x + shopItem->xOffset, + this->shelves->actor.world.pos.y + shopItem->yOffset, + this->shelves->actor.world.pos.z + shopItem->zOffset, this->shelves->actor.shape.rot.x, + this->shelves->actor.shape.rot.y + sItemShelfRot[i], this->shelves->actor.shape.rot.z, params); + } + } + } + } +} + +void EnOssan_TalkDefaultShopkeeper(GlobalContext* globalCtx) { + Message_ContinueTextbox(globalCtx, 0x9E); +} + +void EnOssan_TalkKakarikoPotionShopkeeper(GlobalContext* globalCtx) { + if (globalCtx->curSpawn == 0) { + Message_ContinueTextbox(globalCtx, 0x5046); + } else { + Message_ContinueTextbox(globalCtx, 0x504E); + } +} + +void EnOssan_TalkMarketPotionShopkeeper(GlobalContext* globalCtx) { + Message_ContinueTextbox(globalCtx, 0x504E); +} + +void EnOssan_TalkKokiriShopkeeper(GlobalContext* globalCtx) { + Message_ContinueTextbox(globalCtx, 0x10BA); +} + +void EnOssan_TalkBazaarShopkeeper(GlobalContext* globalCtx) { + if (globalCtx->curSpawn == 0) { + Message_ContinueTextbox(globalCtx, 0x9D); + } else { + Message_ContinueTextbox(globalCtx, 0x9C); + } +} + +void EnOssan_TalkBombchuShopkeeper(GlobalContext* globalCtx) { + Message_ContinueTextbox(globalCtx, 0x7076); +} + +void EnOssan_TalkZoraShopkeeper(GlobalContext* globalCtx) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + Message_ContinueTextbox(globalCtx, 0x403A); + } else { + Message_ContinueTextbox(globalCtx, 0x403B); + } +} + +// Goron City, Goron +void EnOssan_TalkGoronShopkeeper(GlobalContext* globalCtx) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + if (gSaveContext.eventChkInf[2] & 0x20) { + Message_ContinueTextbox(globalCtx, 0x3028); + } else if (CUR_UPG_VALUE(UPG_STRENGTH) != 0) { + Message_ContinueTextbox(globalCtx, 0x302D); + } else { + Message_ContinueTextbox(globalCtx, 0x300F); + } + } else if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) { + Message_ContinueTextbox(globalCtx, 0x3057); + } else { + Message_ContinueTextbox(globalCtx, 0x305B); + } +} + +// Happy Mask Shop +void EnOssan_TalkHappyMaskShopkeeper(GlobalContext* globalCtx) { + if ((gSaveContext.itemGetInf[3] & 0x100) // Sold Keaton Mask + && (gSaveContext.itemGetInf[3] & 0x200) // Sold Skull Mask + && (gSaveContext.itemGetInf[3] & 0x400) // Sold Spooky Mask + && (gSaveContext.itemGetInf[3] & 0x800)) { // Sold Bunny Hood + Message_ContinueTextbox(globalCtx, 0x70AE); + } else { + switch (globalCtx->msgCtx.choiceIndex) { + case 1: + Message_ContinueTextbox(globalCtx, 0x70A4); + break; + case 0: + Message_ContinueTextbox(globalCtx, 0x70A3); + break; + } + } +} + +void EnOssan_UpdateCameraDirection(EnOssan* this, GlobalContext* globalCtx, f32 cameraFaceAngle) { + this->cameraFaceAngle = cameraFaceAngle; + Camera_SetCameraData(GET_ACTIVE_CAM(globalCtx), 0xC, NULL, NULL, cameraFaceAngle, 0, 0); +} + +s32 EnOssan_TryGetObjBankIndexes(EnOssan* this, GlobalContext* globalCtx, s16* objectIds) { + if (objectIds[1] != OBJECT_ID_MAX) { + this->objBankIndex2 = Object_GetIndex(&globalCtx->objectCtx, objectIds[1]); + if (this->objBankIndex2 < 0) { + return false; + } + } else { + this->objBankIndex2 = -1; + } + if (objectIds[2] != OBJECT_ID_MAX) { + this->objBankIndex3 = Object_GetIndex(&globalCtx->objectCtx, objectIds[2]); + if (this->objBankIndex3 < 0) { + return false; + } + } else { + this->objBankIndex3 = -1; + } + return true; +} + +void EnOssan_Init(Actor* thisx, GlobalContext* globalCtx) { + EnOssan* this = (EnOssan*)thisx; + s32 pad; + s16* objectIds; + + if (this->actor.params == OSSAN_TYPE_TALON && (LINK_AGE_IN_YEARS != YEARS_CHILD)) { + this->actor.params = OSSAN_TYPE_INGO; + } + + //! @bug This check will always evaluate to false, it should be || not && + if (this->actor.params > OSSAN_TYPE_MASK && this->actor.params < OSSAN_TYPE_KOKIRI) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("引数がおかしいよ(arg_data=%d)!!\n", this->actor.params); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_oB1.c", 1246); + return; + } + + // If you've given Zelda's Letter to the Kakariko Guard + if (this->actor.params == OSSAN_TYPE_MASK && !(gSaveContext.infTable[7] & 0x40)) { + Actor_Kill(&this->actor); + return; + } + + if (this->actor.params == OSSAN_TYPE_KAKARIKO_POTION && (LINK_AGE_IN_YEARS == YEARS_CHILD)) { + Actor_Kill(&this->actor); + return; + } + + // Completed Dodongo's Cavern + if (this->actor.params == OSSAN_TYPE_BOMBCHUS && !(gSaveContext.eventChkInf[2] & 0x20)) { + Actor_Kill(&this->actor); + return; + } + + objectIds = sShopkeeperObjectIds[this->actor.params]; + this->objBankIndex1 = Object_GetIndex(&globalCtx->objectCtx, objectIds[0]); + + if (this->objBankIndex1 < 0) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("バンクが無いよ!!(%s)\n", sShopkeeperPrintName[this->actor.params]); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_oB1.c", 1284); + return; + } + + if (EnOssan_TryGetObjBankIndexes(this, globalCtx, objectIds) == 0) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("予備バンクが無いよ!!(%s)\n", sShopkeeperPrintName[this->actor.params]); + osSyncPrintf(VT_RST); + ASSERT(0, "0", "../z_en_oB1.c", 1295); + return; + } + + Actor_ProcessInitChain(&this->actor, sInitChain); + EnOssan_SetupAction(this, EnOssan_InitActionFunc); +} + +void EnOssan_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnOssan* this = (EnOssan*)thisx; + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnOssan_UpdateCursorPos(GlobalContext* globalCtx, EnOssan* this) { + s16 x; + s16 y; + + Actor_GetScreenPos(globalCtx, &this->shelfSlots[this->cursorIndex]->actor, &x, &y); + this->cursorX = x; + this->cursorY = y; +} + +void EnOssan_EndInteraction(GlobalContext* globalCtx, EnOssan* this) { + Player* player = GET_PLAYER(globalCtx); + + // "End of conversation!" + osSyncPrintf(VT_FGCOL(YELLOW) "%s[%d]:★★★ 会話終了!! ★★★" VT_RST "\n", "../z_en_oB1.c", 1337); + YREG(31) = 0; + Actor_ProcessTalkRequest(&this->actor, globalCtx); + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + globalCtx->msgCtx.stateTimer = 4; + player->stateFlags2 &= ~0x20000000; + func_800BC490(globalCtx, 1); + Interface_ChangeAlpha(50); + this->drawCursor = 0; + this->stickLeftPrompt.isEnabled = false; + this->stickRightPrompt.isEnabled = false; + EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f); + this->actor.textId = EnOssan_SetupHelloDialog(this); + this->stateFlag = OSSAN_STATE_IDLE; +} + +s32 EnOssan_TestEndInteraction(EnOssan* this, GlobalContext* globalCtx, Input* input) { + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + EnOssan_EndInteraction(globalCtx, this); + return true; + } else { + return false; + } +} + +s32 EnOssan_TestCancelOption(EnOssan* this, GlobalContext* globalCtx, Input* input) { + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + return true; + } else { + return false; + } +} + +void EnOssan_SetStateStartShopping(GlobalContext* globalCtx, EnOssan* this, u8 skipHelloState) { + YREG(31) = 1; + this->headRot = this->headTargetRot = 0; + Interface_SetDoAction(globalCtx, DO_ACTION_NEXT); + EnOssan_UpdateCameraDirection(this, globalCtx, 0); + + if (!skipHelloState) { + this->stateFlag = OSSAN_STATE_START_CONVERSATION; + } else { + EnOssan_StartShopping(globalCtx, this); + } +} + +void EnOssan_StartShopping(GlobalContext* globalCtx, EnOssan* this) { + this->stateFlag = OSSAN_STATE_FACING_SHOPKEEPER; + + if (this->actor.params == OSSAN_TYPE_MASK) { + // if all masks have been sold, give the option to ask about the mask of truth + if ((gSaveContext.itemGetInf[3] & 0x100) && (gSaveContext.itemGetInf[3] & 0x200) && + (gSaveContext.itemGetInf[3] & 0x400) && (gSaveContext.itemGetInf[3] & 0x800)) { + Message_ContinueTextbox(globalCtx, 0x70AD); + } else { + Message_ContinueTextbox(globalCtx, 0x70A2); + } + } else { + Message_ContinueTextbox(globalCtx, 0x83); + } + + Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE); + this->stickRightPrompt.isEnabled = true; + this->stickLeftPrompt.isEnabled = true; + EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f); +} + +void EnOssan_ChooseTalkToOwner(GlobalContext* globalCtx, EnOssan* this) { + this->stateFlag = OSSAN_STATE_TALKING_TO_SHOPKEEPER; + sShopkeeperTalkOwner[this->actor.params](globalCtx); + Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE); + this->stickLeftPrompt.isEnabled = false; + this->stickRightPrompt.isEnabled = false; +} + +void EnOssan_SetLookToShopkeeperFromShelf(GlobalContext* globalCtx, EnOssan* this) { + func_80078884(NA_SE_SY_CURSOR); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_LOOK_SHOPKEEPER; +} + +void EnOssan_State_Idle(EnOssan* this, GlobalContext* globalCtx, Player* player) { + this->headTargetRot = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + // "Start conversation!!" + osSyncPrintf(VT_FGCOL(YELLOW) "★★★ 会話開始!! ★★★" VT_RST "\n"); + player->stateFlags2 |= 0x20000000; + func_800BC590(globalCtx); + EnOssan_SetStateStartShopping(globalCtx, this, false); + } else if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100); + } +} + +void EnOssan_UpdateJoystickInputState(GlobalContext* globalCtx, EnOssan* this) { + Input* input = &globalCtx->state.input[0]; + s8 stickX = input->rel.stick_x; + s8 stickY = input->rel.stick_y; + + this->moveHorizontal = this->moveVertical = false; + + if (this->stickAccumX == 0) { + if (stickX > 30 || stickX < -30) { + this->stickAccumX = stickX; + this->moveHorizontal = true; + } + } else if (stickX <= 30 && stickX >= -30) { + this->stickAccumX = 0; + } else if (this->stickAccumX * stickX < 0) { // Stick has swapped directions + this->stickAccumX = stickX; + this->moveHorizontal = true; + } else { + this->stickAccumX += stickX; + + if (this->stickAccumX > 2000) { + this->stickAccumX = 2000; + } else if (this->stickAccumX < -2000) { + this->stickAccumX = -2000; + } + } + + if (this->stickAccumY == 0) { + if (stickY > 30 || stickY < -30) { + this->stickAccumY = stickY; + this->moveVertical = true; + } + } else if (stickY <= 30 && stickY >= -30) { + this->stickAccumY = 0; + } else if (this->stickAccumY * stickY < 0) { // Stick has swapped directions + this->stickAccumY = stickY; + this->moveVertical = true; + } else { + this->stickAccumY += stickY; + + if (this->stickAccumY > 2000) { + this->stickAccumY = 2000; + } else if (this->stickAccumY < -2000) { + this->stickAccumY = -2000; + } + } +} + +u8 EnOssan_SetCursorIndexFromNeutral(EnOssan* this, u8 shelfOffset) { + u8 i; + + // if cursor is on the top shelf + if (this->cursorIndex & 1) { + // scan top shelf for non-null item + for (i = shelfOffset + 1; i < shelfOffset + 4; i += 2) { + if (this->shelfSlots[i] != NULL) { + return i; + } + } + // scan bottom shelf for non-null item + for (i = shelfOffset; i < shelfOffset + 4; i += 2) { + if (this->shelfSlots[i] != NULL) { + return i; + } + } + } else { + // scan bottom shelf for non-null item + for (i = shelfOffset; i < shelfOffset + 4; i += 2) { + if (this->shelfSlots[i] != NULL) { + return i; + } + } + // scan top shelf for non-null item + for (i = shelfOffset + 1; i < shelfOffset + 4; i += 2) { + if (this->shelfSlots[i] != NULL) { + return i; + } + } + } + return CURSOR_INVALID; +} + +u8 EnOssan_CursorRight(EnOssan* this, u8 cursorIndex, u8 shelfSlotMin) { + u8 c = shelfSlotMin + 4; + + while (cursorIndex >= shelfSlotMin && cursorIndex < c) { + cursorIndex -= 2; + if (cursorIndex >= shelfSlotMin && cursorIndex < c) { + if (this->shelfSlots[cursorIndex] != NULL) { + return cursorIndex; + } + } + } + return CURSOR_INVALID; +} + +u8 EnOssan_CursorLeft(EnOssan* this, u8 cursorIndex, u8 shelfSlotMax) { + + while (cursorIndex < shelfSlotMax) { + cursorIndex += 2; + if ((cursorIndex < shelfSlotMax) && this->shelfSlots[cursorIndex] != NULL) { + return cursorIndex; + } + } + return CURSOR_INVALID; +} + +// pay salesman back +void EnOssan_TryPaybackMask(EnOssan* this, GlobalContext* globalCtx) { + s16 price = sMaskPaymentPrice[this->happyMaskShopState]; + + if (gSaveContext.rupees < price) { + Message_ContinueTextbox(globalCtx, 0x70A8); + this->happyMaskShopkeeperEyeIdx = 1; + this->happyMaskShopState = OSSAN_HAPPY_STATE_ANGRY; + } else { + Rupees_ChangeBy(-price); + + if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD) { + gSaveContext.eventChkInf[8] |= 0x8000; + Message_ContinueTextbox(globalCtx, 0x70A9); + this->happyMaskShopState = OSSAN_HAPPY_STATE_ALL_MASKS_SOLD; + return; + } + + if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK) { + gSaveContext.eventChkInf[8] |= 0x1000; + } else if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK) { + gSaveContext.eventChkInf[8] |= 0x4000; + } else if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK) { + gSaveContext.eventChkInf[8] |= 0x2000; + } + + Message_ContinueTextbox(globalCtx, 0x70A7); + this->happyMaskShopState = OSSAN_HAPPY_STATE_NONE; + } + this->stateFlag = OSSAN_STATE_START_CONVERSATION; +} + +void EnOssan_State_StartConversation(EnOssan* this, GlobalContext* globalCtx, Player* player) { + u8 dialogState = Message_GetState(&globalCtx->msgCtx); + + if (this->actor.params == OSSAN_TYPE_MASK && dialogState == TEXT_STATE_CHOICE) { + if (!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0]) && + Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + EnOssan_StartShopping(globalCtx, this); + break; + case 1: + EnOssan_EndInteraction(globalCtx, this); + break; + } + } + } else if (dialogState == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + func_80078884(NA_SE_SY_MESSAGE_PASS); + + switch (this->happyMaskShopState) { + case OSSAN_HAPPY_STATE_ALL_MASKS_SOLD: + Message_ContinueTextbox(globalCtx, 0x70AA); + this->stateFlag = OSSAN_STATE_LEND_MASK_OF_TRUTH; + return; + case OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK: + EnOssan_EndInteraction(globalCtx, this); + return; + case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK: + case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK: + case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK: + case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD: + EnOssan_TryPaybackMask(this, globalCtx); + return; + case OSSAN_HAPPY_STATE_ANGRY: + globalCtx->nextEntranceIndex = 0x1D1; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 0x2E; + return; + } + + if (!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0])) { + // "Shop around by moving the stick left and right" + osSyncPrintf("「スティック左右で品物みてくれ!」\n"); + EnOssan_StartShopping(globalCtx, this); + } + } + + if (1) {} +} + +s32 EnOssan_FacingShopkeeperDialogResult(EnOssan* this, GlobalContext* globalCtx) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + EnOssan_ChooseTalkToOwner(globalCtx, this); + return true; + case 1: + EnOssan_EndInteraction(globalCtx, this); + return true; + default: + return false; + } +} + +void EnOssan_State_FacingShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player) { + u8 nextIndex; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && + !EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0])) { + if (Message_ShouldAdvance(globalCtx) && EnOssan_FacingShopkeeperDialogResult(this, globalCtx)) { + func_80078884(NA_SE_SY_DECIDE); + return; + } + // Stick Left + if (this->stickAccumX < 0) { + nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 4); + if (nextIndex != CURSOR_INVALID) { + this->cursorIndex = nextIndex; + this->stateFlag = OSSAN_STATE_LOOK_SHELF_LEFT; + Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE); + this->stickLeftPrompt.isEnabled = false; + func_80078884(NA_SE_SY_CURSOR); + } + } else if (this->stickAccumX > 0) { + nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 0); + if (nextIndex != CURSOR_INVALID) { + this->cursorIndex = nextIndex; + this->stateFlag = OSSAN_STATE_LOOK_SHELF_RIGHT; + Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE); + this->stickRightPrompt.isEnabled = false; + func_80078884(NA_SE_SY_CURSOR); + } + } + } +} + +void EnOssan_State_TalkingToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnOssan_StartShopping(globalCtx, this); + } +} + +void EnOssan_State_LookToLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) { + Math_ApproachF(&this->cameraFaceAngle, 30.0f, 0.5f, 10.0f); + + if (this->cameraFaceAngle > 29.5f) { + EnOssan_UpdateCameraDirection(this, globalCtx, 30.0f); + } + + EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle); + + if (this->cameraFaceAngle >= 30.0f) { + EnOssan_UpdateCameraDirection(this, globalCtx, 30.0f); + EnOssan_UpdateCursorPos(globalCtx, this); + this->stateFlag = OSSAN_STATE_BROWSE_LEFT_SHELF; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + } else { + this->stickAccumX = 0; + } +} + +void EnOssan_State_LookToRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) { + Math_ApproachF(&this->cameraFaceAngle, -30.0f, 0.5f, 10.0f); + + if (this->cameraFaceAngle < -29.5f) { + EnOssan_UpdateCameraDirection(this, globalCtx, -30.0f); + } + + EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle); + + if (this->cameraFaceAngle <= -30.0f) { + EnOssan_UpdateCameraDirection(this, globalCtx, -30.0f); + EnOssan_UpdateCursorPos(globalCtx, this); + this->stateFlag = OSSAN_STATE_BROWSE_RIGHT_SHELF; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + } else { + this->stickAccumX = 0; + } +} + +void EnOssan_CursorUpDown(EnOssan* this) { + u8 curTemp = this->cursorIndex; + u8 curScanTemp; + + if (this->stickAccumY < 0) { + curTemp &= 0xFE; + if (this->shelfSlots[curTemp] != NULL) { + this->cursorIndex = curTemp; + return; + } + // cursorIndex on right shelf + if (curTemp < 4) { + curScanTemp = curTemp + 2; + if (curScanTemp >= 4) { + curScanTemp = 0; + } + while (curScanTemp != curTemp) { + if (this->shelfSlots[curScanTemp] != NULL) { + this->cursorIndex = curScanTemp; + return; + } + curScanTemp += 2; + if (curScanTemp >= 4) { + curScanTemp = 0; + } + } + } else { + // cursorIndex on left shelf + curScanTemp = curTemp + 2; + if (curScanTemp >= 8) { + curScanTemp = 4; + } + while (curScanTemp != curTemp) { + if (this->shelfSlots[curScanTemp] != NULL) { + this->cursorIndex = curScanTemp; + return; + } + curScanTemp += 2; + if (curScanTemp >= 8) { + curScanTemp = 4; + } + } + } + } else if (this->stickAccumY > 0) { + curTemp |= 1; + if (this->shelfSlots[curTemp] != NULL) { + this->cursorIndex = curTemp; + return; + } + // cursorIndex on right shelf + if (curTemp < 4) { + curScanTemp = curTemp + 2; + if (curScanTemp >= 4) { + curScanTemp = 1; + } + while (curScanTemp != curTemp) { + if (this->shelfSlots[curScanTemp] != NULL) { + this->cursorIndex = curScanTemp; + return; + } + curScanTemp += 2; + if (curScanTemp >= 4) { + curScanTemp = 1; + } + } + } else { + // cursorIndex on left shelf + curScanTemp = curTemp + 2; + if (curScanTemp >= 8) { + curScanTemp = 5; + } + while (curScanTemp != curTemp) { + if (this->shelfSlots[curScanTemp] != NULL) { + this->cursorIndex = curScanTemp; + return; + } + curScanTemp += 2; + if (curScanTemp >= 8) { + curScanTemp = 5; + } + } + } + } +} + +s32 EnOssan_HasPlayerSelectedItem(GlobalContext* globalCtx, EnOssan* this, Input* input) { + EnGirlA* selectedItem = this->shelfSlots[this->cursorIndex]; + + if (EnOssan_TestEndInteraction(this, globalCtx, input)) { + return true; + } + if (Message_ShouldAdvance(globalCtx)) { + if (selectedItem->actor.params != SI_SOLD_OUT && selectedItem->isInvisible == 0) { + this->tempStateFlag = this->stateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->itemBuyPromptTextId); + this->stickLeftPrompt.isEnabled = false; + this->stickRightPrompt.isEnabled = false; + switch (selectedItem->actor.params) { + case SI_KEATON_MASK: + case SI_SPOOKY_MASK: + case SI_SKULL_MASK: + case SI_BUNNY_HOOD: + case SI_MASK_OF_TRUTH: + case SI_ZORA_MASK: + case SI_GORON_MASK: + case SI_GERUDO_MASK: + func_80078884(NA_SE_SY_DECIDE); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_SELECT_ITEM_MASK; + return true; + case SI_MILK_BOTTLE: + func_80078884(NA_SE_SY_DECIDE); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_SELECT_ITEM_MILK_BOTTLE; + return true; + case SI_WEIRD_EGG: + func_80078884(NA_SE_SY_DECIDE); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_SELECT_ITEM_WEIRD_EGG; + return true; + case SI_19: + case SI_20: + func_80078884(NA_SE_SY_ERROR); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_SELECT_ITEM_UNIMPLEMENTED; + return true; + case SI_BOMBS_5_R25: + case SI_BOMBS_10: + case SI_BOMBS_20: + case SI_BOMBS_30: + case SI_BOMBS_5_R35: + func_80078884(NA_SE_SY_DECIDE); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_SELECT_ITEM_BOMBS; + return true; + default: + func_80078884(NA_SE_SY_DECIDE); + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_SELECT_ITEM; + return true; + } + } + func_80078884(NA_SE_SY_ERROR); + return true; + } + return false; +} + +void EnOssan_State_BrowseLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) { + s32 a; + s32 b; + u8 prevIndex = this->cursorIndex; + s32 c; + s32 d; + + if (!EnOssan_ReturnItemToShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2152); + this->delayTimer = 3; + return; + } + if (this->delayTimer != 0) { + this->delayTimer--; + return; + } + this->drawCursor = 0xFF; + this->stickRightPrompt.isEnabled = true; + EnOssan_UpdateCursorPos(globalCtx, this); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && + !EnOssan_HasPlayerSelectedItem(globalCtx, this, &globalCtx->state.input[0])) { + if (this->moveHorizontal) { + if (this->stickAccumX > 0) { + a = EnOssan_CursorRight(this, this->cursorIndex, 4); + if (a != CURSOR_INVALID) { + this->cursorIndex = a; + } else { + EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this); + return; + } + } else if (this->stickAccumX < 0) { + b = EnOssan_CursorLeft(this, this->cursorIndex, 8); + if (b != CURSOR_INVALID) { + this->cursorIndex = b; + } + } + } else { + if (this->stickAccumX > 0 && this->stickAccumX > 500) { + c = EnOssan_CursorRight(this, this->cursorIndex, 4); + if (c != CURSOR_INVALID) { + this->cursorIndex = c; + } else { + EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this); + return; + } + } else if (this->stickAccumX < 0 && this->stickAccumX < -500) { + d = EnOssan_CursorLeft(this, this->cursorIndex, 8); + if (d != CURSOR_INVALID) { + this->cursorIndex = d; + } + } + } + EnOssan_CursorUpDown(this); + if (this->cursorIndex != prevIndex) { + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + func_80078884(NA_SE_SY_CURSOR); + } + } +} + +void EnOssan_State_BrowseRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) { + s32 pad[2]; + u8 prevIndex; + u8 nextIndex; + + prevIndex = this->cursorIndex; + if (!EnOssan_ReturnItemToShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2244); + this->delayTimer = 3; + return; + } + if (this->delayTimer != 0) { + this->delayTimer--; + return; + } + this->drawCursor = 0xFF; + this->stickLeftPrompt.isEnabled = true; + EnOssan_UpdateCursorPos(globalCtx, this); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && + !EnOssan_HasPlayerSelectedItem(globalCtx, this, &globalCtx->state.input[0])) { + if (this->moveHorizontal) { + if (this->stickAccumX < 0) { + nextIndex = EnOssan_CursorRight(this, this->cursorIndex, 0); + if (nextIndex != CURSOR_INVALID) { + this->cursorIndex = nextIndex; + } else { + EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this); + return; + } + } else if (this->stickAccumX > 0) { + nextIndex = EnOssan_CursorLeft(this, this->cursorIndex, 4); + if (nextIndex != CURSOR_INVALID) { + this->cursorIndex = nextIndex; + } + } + } else { + if (this->stickAccumX < 0 && this->stickAccumX < -500) { + nextIndex = EnOssan_CursorRight(this, this->cursorIndex, 0); + if (nextIndex != CURSOR_INVALID) { + this->cursorIndex = nextIndex; + } else { + EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this); + return; + } + } else if (this->stickAccumX > 0 && this->stickAccumX > 500) { + nextIndex = EnOssan_CursorLeft(this, this->cursorIndex, 4); + if (nextIndex != CURSOR_INVALID) { + this->cursorIndex = nextIndex; + } + } + } + EnOssan_CursorUpDown(this); + if (this->cursorIndex != prevIndex) { + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + func_80078884(NA_SE_SY_CURSOR); + } + } +} + +void EnOssan_State_LookFromShelfToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player) { + Math_ApproachF(&this->cameraFaceAngle, 0.0f, 0.5f, 10.0f); + if ((this->cameraFaceAngle < 0.5f) && (this->cameraFaceAngle > -0.5f)) { + EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f); + } + EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle); + if (this->cameraFaceAngle == 0.0f) { + EnOssan_StartShopping(globalCtx, this); + } +} + +void EnOssan_State_DisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (!EnOssan_ReturnItemToShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2355); + return; + } + Math_ApproachF(&this->cameraFaceAngle, 0.0f, 0.5f, 10.0f); + if (this->cameraFaceAngle < 0.5f && this->cameraFaceAngle > -0.5f) { + EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f); + } + EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle); + if (this->cameraFaceAngle == 0.0f) { + Message_ContinueTextbox(globalCtx, 0x3010); + this->stateFlag = OSSAN_STATE_WAIT_FOR_DISPLAY_ONLY_BOMB_DIALOG; + } +} + +void EnOssan_GiveItemWithFanfare(GlobalContext* globalCtx, EnOssan* this) { + Player* player = GET_PLAYER(globalCtx); + + osSyncPrintf("\n" VT_FGCOL(YELLOW) "初めて手にいれた!!" VT_RST "\n\n"); + func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f); + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + globalCtx->msgCtx.stateTimer = 4; + player->stateFlags2 &= ~0x20000000; + func_800BC490(globalCtx, 1); + Interface_ChangeAlpha(50); + this->drawCursor = 0; + EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f); + this->stateFlag = OSSAN_STATE_GIVE_ITEM_FANFARE; + osSyncPrintf(VT_FGCOL(YELLOW) "持ち上げ開始!!" VT_RST "\n\n"); +} + +void EnOssan_SetStateCantGetItem(GlobalContext* globalCtx, EnOssan* this, u16 textId) { + Message_ContinueTextbox(globalCtx, textId); + this->stateFlag = OSSAN_STATE_CANT_GET_ITEM; +} + +void EnOssan_SetStateQuickBuyDialog(GlobalContext* globalCtx, EnOssan* this, u16 textId) { + Message_ContinueTextbox(globalCtx, textId); + this->stateFlag = OSSAN_STATE_QUICK_BUY; +} + +void EnOssan_HandleCanBuyItem(GlobalContext* globalCtx, EnOssan* this) { + EnGirlA* selectedItem = this->shelfSlots[this->cursorIndex]; + + switch (selectedItem->canBuyFunc(globalCtx, selectedItem)) { + case CANBUY_RESULT_SUCCESS_FANFARE: + if (selectedItem->actor.params == SI_HYLIAN_SHIELD && gSaveContext.infTable[7] & 0x40) { + EnOssan_SetStateGiveDiscountDialog(globalCtx, this); + } else { + EnOssan_GiveItemWithFanfare(globalCtx, this); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + selectedItem->setOutOfStockFunc(globalCtx, selectedItem); + } + break; + case CANBUY_RESULT_SUCCESS: + selectedItem->itemGiveFunc(globalCtx, selectedItem); + EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x84); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + selectedItem->setOutOfStockFunc(globalCtx, selectedItem); + break; + case CANBUY_RESULT_CANT_GET_NOW: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x86); + break; + case CANBUY_RESULT_NEED_BOTTLE: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x96); + break; + case CANBUY_RESULT_NEED_RUPEES: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x85); + break; + case CANBUY_RESULT_CANT_GET_NOW_5: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x86); + break; + } +} + +void EnOssan_HandleCanBuyLonLonMilk(GlobalContext* globalCtx, EnOssan* this) { + EnGirlA* item = this->shelfSlots[this->cursorIndex]; + + switch (item->canBuyFunc(globalCtx, item)) { + case CANBUY_RESULT_SUCCESS_FANFARE: + Message_ContinueTextbox(globalCtx, 0x9C); + this->stateFlag = OSSAN_STATE_GIVE_LON_LON_MILK; + this->drawCursor = 0; + break; + case CANBUY_RESULT_SUCCESS: + item->itemGiveFunc(globalCtx, item); + EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x98); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + item->setOutOfStockFunc(globalCtx, item); + break; + case CANBUY_RESULT_NEED_BOTTLE: + EnOssan_SetStateCantGetItem(globalCtx, this, 0x96); + break; + case CANBUY_RESULT_NEED_RUPEES: + EnOssan_SetStateCantGetItem(globalCtx, this, 0x85); + break; + } +} + +void EnOssan_HandleCanBuyWeirdEgg(GlobalContext* globalCtx, EnOssan* this) { + EnGirlA* item = this->shelfSlots[this->cursorIndex]; + + switch (item->canBuyFunc(globalCtx, item)) { + case CANBUY_RESULT_SUCCESS_FANFARE: + EnOssan_GiveItemWithFanfare(globalCtx, this); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + item->setOutOfStockFunc(globalCtx, item); + break; + case CANBUY_RESULT_SUCCESS: + item->itemGiveFunc(globalCtx, item); + EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x9A); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + item->setOutOfStockFunc(globalCtx, item); + break; + case CANBUY_RESULT_CANT_GET_NOW: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x9D); + break; + case CANBUY_RESULT_NEED_RUPEES: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x85); + break; + } +} + +void EnOssan_HandleCanBuyBombs(GlobalContext* globalCtx, EnOssan* this) { + EnGirlA* item = this->shelfSlots[this->cursorIndex]; + + switch (item->canBuyFunc(globalCtx, item)) { + case CANBUY_RESULT_SUCCESS_FANFARE: + case CANBUY_RESULT_SUCCESS: + item->itemGiveFunc(globalCtx, item); + EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x84); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + item->setOutOfStockFunc(globalCtx, item); + break; + case CANBUY_RESULT_CANT_GET_NOW: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x86); + break; + case CANBUY_RESULT_NEED_RUPEES: + func_80078884(NA_SE_SY_ERROR); + EnOssan_SetStateCantGetItem(globalCtx, this, 0x85); + break; + } +} + +void EnOssan_BuyGoronCityBombs(GlobalContext* globalCtx, EnOssan* this) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + if (!(gSaveContext.eventChkInf[2] & 0x20)) { + if (gSaveContext.infTable[15] & 0x1000) { + EnOssan_SetStateCantGetItem(globalCtx, this, 0x302E); + } else { + this->stickLeftPrompt.isEnabled = false; + this->stickRightPrompt.isEnabled = false; + this->drawCursor = 0; + this->stateFlag = OSSAN_STATE_DISPLAY_ONLY_BOMB_DIALOG; + } + } else { + EnOssan_HandleCanBuyBombs(globalCtx, this); + } + } else { + EnOssan_HandleCanBuyBombs(globalCtx, this); + } +} + +void EnOssan_State_ItemSelected(EnOssan* this, GlobalContext* globalCtx2, Player* player) { + GlobalContext* globalCtx = globalCtx2; // Necessary for OKs + + if (!EnOssan_TakeItemOffShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2654); + return; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && + !EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + EnOssan_HandleCanBuyItem(globalCtx, this); + break; + case 1: + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + break; + } + } +} + +void EnOssan_State_SelectMilkBottle(EnOssan* this, GlobalContext* globalCtx2, Player* player) { + GlobalContext* globalCtx = globalCtx2; // Need for OK + + if (!EnOssan_TakeItemOffShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2693); + return; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && + !EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + EnOssan_HandleCanBuyLonLonMilk(globalCtx, this); + break; + case 1: + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + break; + } + } +} + +void EnOssan_State_SelectWeirdEgg(EnOssan* this, GlobalContext* globalCtx2, Player* player) { + GlobalContext* globalCtx = globalCtx2; // Needed for OK + + if (!EnOssan_TakeItemOffShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2732); + return; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && + !EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + EnOssan_HandleCanBuyWeirdEgg(globalCtx, this); + break; + case 1: + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + break; + } + } +} + +void EnOssan_State_SelectUnimplementedItem(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (!EnOssan_TakeItemOffShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2771); + return; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + } +} + +void EnOssan_State_SelectBombs(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (!EnOssan_TakeItemOffShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2798); + return; + } + osSyncPrintf("店主の依頼 ( %d )\n", gSaveContext.infTable[15] & 0x1000); + if (this->actor.params != OSSAN_TYPE_GORON) { + EnOssan_State_ItemSelected(this, globalCtx, player); + return; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && + !EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + EnOssan_BuyGoronCityBombs(globalCtx, this); + break; + case 1: + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + break; + } + } +} + +void EnOssan_State_SelectMaskItem(EnOssan* this, GlobalContext* globalCtx, Player* player) { + u8 talkState = Message_GetState(&globalCtx->msgCtx); + EnGirlA* item = this->shelfSlots[this->cursorIndex]; + + if (!EnOssan_TakeItemOffShelf(this)) { + osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", "../z_en_oB1.c", 2845); + return; + } + if (talkState == TEXT_STATE_EVENT) { + if (Message_ShouldAdvance(globalCtx)) { + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + } + } else if (talkState == TEXT_STATE_CHOICE && + !EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && + Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + switch (item->actor.params) { + case SI_KEATON_MASK: + gSaveContext.itemGetInf[2] |= 0x08; + break; + case SI_SPOOKY_MASK: + gSaveContext.itemGetInf[2] |= 0x20; + break; + case SI_SKULL_MASK: + gSaveContext.itemGetInf[2] |= 0x10; + break; + case SI_BUNNY_HOOD: + gSaveContext.itemGetInf[2] |= 0x40; + break; + case SI_MASK_OF_TRUTH: + case SI_ZORA_MASK: + case SI_GORON_MASK: + case SI_GERUDO_MASK: + break; + } + EnOssan_GiveItemWithFanfare(globalCtx, this); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + item->setOutOfStockFunc(globalCtx, item); + break; + case 1: + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + break; + } + } +} + +void EnOssan_State_CantGetItem(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + } +} + +void EnOssan_State_QuickBuyDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) { + EnGirlA* item; + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + this->shopItemSelectedTween = 0.0f; + EnOssan_ResetItemPosition(this); + item = this->shelfSlots[this->cursorIndex]; + item->updateStockedItemFunc(globalCtx, item); + this->stateFlag = this->tempStateFlag; + Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId); + } +} + +void EnOssan_State_GiveItemWithFanfare(EnOssan* this, GlobalContext* globalCtx, Player* player) { + // The player sets itself as the parent actor to signal that it has obtained the give item request + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->stateFlag = OSSAN_STATE_ITEM_PURCHASED; + return; + } + func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f); +} + +void EnOssan_State_ItemPurchased(EnOssan* this, GlobalContext* globalCtx, Player* player) { + EnGirlA* item; + EnGirlA* itemTemp; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + if (this->actor.params == OSSAN_TYPE_MASK) { + itemTemp = this->shelfSlots[this->cursorIndex]; + EnOssan_ResetItemPosition(this); + item = this->shelfSlots[this->cursorIndex]; + item->updateStockedItemFunc(globalCtx, item); + if (itemTemp->actor.params == SI_MASK_OF_TRUTH && !(gSaveContext.itemGetInf[3] & 0x8000)) { + gSaveContext.itemGetInf[3] |= 0x8000; + Message_ContinueTextbox(globalCtx, 0x70AB); + this->happyMaskShopState = OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK; + EnOssan_UpdateShopOfferings(this, globalCtx); + this->stateFlag = OSSAN_STATE_START_CONVERSATION; + return; + } else { + EnOssan_EndInteraction(globalCtx, this); + return; + } + } + item = this->shelfSlots[this->cursorIndex]; + item->buyEventFunc(globalCtx, item); + this->stateFlag = OSSAN_STATE_CONTINUE_SHOPPING_PROMPT; + Message_ContinueTextbox(globalCtx, 0x6B); + } +} + +void EnOssan_State_ContinueShoppingPrompt(EnOssan* this, GlobalContext* globalCtx, Player* player) { + EnGirlA* selectedItem; + u8 talkState = Message_GetState(&globalCtx->msgCtx); + + if (talkState == TEXT_STATE_CHOICE) { + if (Message_ShouldAdvance(globalCtx)) { + EnOssan_ResetItemPosition(this); + selectedItem = this->shelfSlots[this->cursorIndex]; + selectedItem->updateStockedItemFunc(globalCtx, selectedItem); + if (!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0])) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + osSyncPrintf(VT_FGCOL(YELLOW) "★★★ 続けるよ!! ★★★" VT_RST "\n"); + player->actor.shape.rot.y += 0x8000; + player->stateFlags2 |= 0x20000000; + func_800BC490(globalCtx, 2); + Message_StartTextbox(globalCtx, this->actor.textId, &this->actor); + EnOssan_SetStateStartShopping(globalCtx, this, true); + func_8002F298(&this->actor, globalCtx, 100.0f, -1); + break; + case 1: + default: + osSyncPrintf(VT_FGCOL(YELLOW) "★★★ やめるよ!! ★★★" VT_RST "\n"); + EnOssan_EndInteraction(globalCtx, this); + break; + } + } + } + } else if (talkState == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + EnOssan_ResetItemPosition(this); + selectedItem = this->shelfSlots[this->cursorIndex]; + selectedItem->updateStockedItemFunc(globalCtx, selectedItem); + player->actor.shape.rot.y += 0x8000; + player->stateFlags2 |= 0x20000000; + func_800BC490(globalCtx, 2); + Message_StartTextbox(globalCtx, this->actor.textId, &this->actor); + EnOssan_SetStateStartShopping(globalCtx, this, true); + func_8002F298(&this->actor, globalCtx, 100.0f, -1); + } +} + +void EnOssan_State_WaitForDisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[15] |= 0x1000; + EnOssan_StartShopping(globalCtx, this); + } +} + +// Unreachable +void EnOssan_State_21(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE_HAS_NEXT && Message_ShouldAdvance(globalCtx)) { + this->stateFlag = OSSAN_STATE_22; + Message_ContinueTextbox(globalCtx, 0x3012); + gSaveContext.infTable[15] |= 0x1000; + } +} + +// Unreachable +void EnOssan_State_22(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + EnOssan_StartShopping(globalCtx, this); + } +} + +void EnOssan_State_GiveLonLonMilk(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + EnOssan_GiveItemWithFanfare(globalCtx, this); + } +} + +// For giving Mask of Truth when you first sell all masks +void EnOssan_State_LendMaskOfTruth(EnOssan* this, GlobalContext* globalCtx, Player* player) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + gSaveContext.itemGetInf[2] |= 0x400; + this->cursorIndex = 2; + EnOssan_GiveItemWithFanfare(globalCtx, this); + } +} + +// Hylian Shield discount dialog +void EnOssan_SetStateGiveDiscountDialog(GlobalContext* globalCtx, EnOssan* this) { + Message_ContinueTextbox(globalCtx, 0x71B2); + this->stateFlag = OSSAN_STATE_DISCOUNT_DIALOG; +} + +void EnOssan_State_GiveDiscountDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) { + EnGirlA* selectedItem; + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + selectedItem = this->shelfSlots[this->cursorIndex]; + EnOssan_GiveItemWithFanfare(globalCtx, this); + this->drawCursor = 0; + this->shopItemSelectedTween = 0.0f; + selectedItem->setOutOfStockFunc(globalCtx, selectedItem); + } +} + +void EnOssan_PositionSelectedItem(EnOssan* this) { + EnGirlA* item; + u8 i; + u8 i2; + ShopItem* shopItem; + f32 tx; + f32 ty; + f32 tz; + + i = this->cursorIndex; + shopItem = &sShopkeeperStores[this->actor.params][i]; + item = this->shelfSlots[i]; + + i2 = i >> 2; + tx = (sSelectedItemPosition[i2].x - shopItem->xOffset) * this->shopItemSelectedTween + shopItem->xOffset; + ty = (sSelectedItemPosition[i2].y - shopItem->yOffset) * this->shopItemSelectedTween + shopItem->yOffset; + tz = (sSelectedItemPosition[i2].z - shopItem->zOffset) * this->shopItemSelectedTween + shopItem->zOffset; + + item->actor.world.pos.x = this->shelves->actor.world.pos.x + tx; + item->actor.world.pos.y = this->shelves->actor.world.pos.y + ty; + item->actor.world.pos.z = this->shelves->actor.world.pos.z + tz; +} + +void EnOssan_ResetItemPosition(EnOssan* this) { + this->shopItemSelectedTween = 0.0f; + EnOssan_PositionSelectedItem(this); +} + +// returns true if animation has completed +s32 EnOssan_TakeItemOffShelf(EnOssan* this) { + Math_ApproachF(&this->shopItemSelectedTween, 1.0f, 1.0f, 0.15f); + if (this->shopItemSelectedTween >= 0.85f) { + this->shopItemSelectedTween = 1.0f; + } + EnOssan_PositionSelectedItem(this); + if (this->shopItemSelectedTween == 1.0f) { + return true; + } else { + return false; + } +} + +// returns true if animation has completed +s32 EnOssan_ReturnItemToShelf(EnOssan* this) { + Math_ApproachF(&this->shopItemSelectedTween, 0.0f, 1.0f, 0.15f); + if (this->shopItemSelectedTween <= 0.15f) { + this->shopItemSelectedTween = 0.0f; + } + EnOssan_PositionSelectedItem(this); + if (this->shopItemSelectedTween == 0.0f) { + return true; + } else { + return false; + } +} + +void EnOssan_UpdateItemSelectedProperty(EnOssan* this) { + EnGirlA** temp_a1 = this->shelfSlots; + s32 i; + + for (i = 0; i < 8; i++) { + if (temp_a1[0] != NULL) { + if (this->stateFlag != OSSAN_STATE_SELECT_ITEM && this->stateFlag != OSSAN_STATE_SELECT_ITEM_MILK_BOTTLE && + this->stateFlag != OSSAN_STATE_SELECT_ITEM_WEIRD_EGG && + this->stateFlag != OSSAN_STATE_SELECT_ITEM_UNIMPLEMENTED && + this->stateFlag != OSSAN_STATE_SELECT_ITEM_BOMBS && this->stateFlag != OSSAN_STATE_SELECT_ITEM_MASK && + this->stateFlag != OSSAN_STATE_CANT_GET_ITEM && this->drawCursor == 0) { + temp_a1[0]->isSelected = false; + } else { + if (this->cursorIndex == i) { + temp_a1[0]->isSelected = true; + } else { + temp_a1[0]->isSelected = false; + } + } + } + temp_a1++; + } +} + +void EnOssan_UpdateCursorAnim(EnOssan* this) { + f32 t; + + t = this->cursorAnimTween; + if (this->cursorAnimState == 0) { + t += 0.05f; + if (t >= 1.0f) { + t = 1.0f; + this->cursorAnimState = 1; + } + } else { + t -= 0.05f; + if (t <= 0.0f) { + t = 0.0f; + this->cursorAnimState = 0; + } + } + this->cursorColorR = ColChanMix(0, 0.0f, t); + this->cursorColorG = ColChanMix(255, 80.0f, t); + this->cursorColorB = ColChanMix(80, 0.0f, t); + this->cursorColorA = ColChanMix(255, 0.0f, t); + this->cursorAnimTween = t; +} + +void EnOssan_UpdateStickDirectionPromptAnim(EnOssan* this) { + f32 arrowAnimTween; + f32 new_var3; // likely fake temp + s32 new_var2 = 255; // likely fake temp + f32 stickAnimTween; + + arrowAnimTween = this->arrowAnimTween; + stickAnimTween = this->stickAnimTween; + if (this->arrowAnimState == 0) { + arrowAnimTween += 0.05f; + if (arrowAnimTween > 1.0f) { + arrowAnimTween = 1.0f; + this->arrowAnimState = 1; + } + + } else { + arrowAnimTween -= 0.05f; + if (arrowAnimTween < 0.0f) { + arrowAnimTween = 0.0f; + this->arrowAnimState = 0; + } + } + + this->arrowAnimTween = arrowAnimTween; + if (this->stickAnimState == 0) { + stickAnimTween += 0.1f; + if (stickAnimTween > 1.0f) { + stickAnimTween = 1.0f; + this->stickAnimState = 1; + } + + } else { + stickAnimTween = 0.0f; + this->stickAnimState = 0; + } + + this->stickAnimTween = stickAnimTween; + this->stickLeftPrompt.arrowColorR = (u8)(255 - ((s32)(155.0f * arrowAnimTween))); + this->stickLeftPrompt.arrowColorG = (u8)(new_var2 - (s32)(155.0f * arrowAnimTween)); + new_var3 = (155.0f * arrowAnimTween); + this->stickLeftPrompt.arrowColorB = (u8)(0 - ((s32)((-100.0f) * arrowAnimTween))); + this->stickLeftPrompt.arrowColorA = (u8)(200 - ((s32)(50.0f * arrowAnimTween))); + this->stickRightPrompt.arrowColorR = (u8)(new_var2 - (s32)new_var3); + this->stickRightPrompt.arrowColorG = (u8)(255 - (s32)new_var3); + this->stickRightPrompt.arrowColorB = (u8)(0 - ((s32)((-100.0f) * arrowAnimTween))); + this->stickRightPrompt.arrowColorA = (u8)(200 - ((s32)(50.0f * arrowAnimTween))); + this->stickRightPrompt.arrowTexX = 290.0f; + this->stickLeftPrompt.arrowTexX = 33.0f; + this->stickRightPrompt.stickTexX = 274.0f; + this->stickLeftPrompt.stickTexX = 49.0f; + this->stickRightPrompt.stickTexX += (8.0f * stickAnimTween); + this->stickLeftPrompt.stickTexX -= (8.0f * stickAnimTween); + this->stickLeftPrompt.arrowTexY = this->stickRightPrompt.arrowTexY = 91.0f; + this->stickLeftPrompt.stickTexY = this->stickRightPrompt.stickTexY = 95.0f; +} + +void EnOssan_WaitForBlink(EnOssan* this) { + s16 decr = this->blinkTimer - 1; + + if (decr != 0) { + this->blinkTimer = decr; + } else { + this->blinkFunc = EnOssan_Blink; + } +} + +void EnOssan_Blink(EnOssan* this) { + s16 decr; + s16 eyeTextureIdxTemp; + + decr = this->blinkTimer - 1; + if (decr != 0) { + this->blinkTimer = decr; + return; + } + eyeTextureIdxTemp = this->eyeTextureIdx + 1; + if (eyeTextureIdxTemp > 2) { + this->eyeTextureIdx = 0; + this->blinkTimer = (s32)(Rand_ZeroOne() * 60.0f) + 20; + this->blinkFunc = EnOssan_WaitForBlink; + } else { + this->eyeTextureIdx = eyeTextureIdxTemp; + this->blinkTimer = 1; + } +} + +s32 EnOssan_AreShopkeeperObjectsLoaded(EnOssan* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex1)) { + if (this->objBankIndex2 >= 0 && !Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex2)) { + return false; + } + if (this->objBankIndex3 >= 0 && !Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex3)) { + return false; + } + return true; + } + return false; +} + +void EnOssan_InitBazaarShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gObjectOssanSkel, &gObjectOssanAnim_000338, NULL, NULL, 0); + this->actor.draw = EnOssan_DrawBazaarShopkeeper; + this->obj3ToSeg6Func = NULL; +} + +void EnOssan_InitKokiriShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gKm1Skel, NULL, NULL, NULL, 0); + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objBankIndex3].segment); + Animation_Change(&this->skelAnime, &object_masterkokiri_Anim_0004A8, 1.0f, 0.0f, + Animation_GetLastFrame(&object_masterkokiri_Anim_0004A8), 0, 0.0f); + this->actor.draw = EnOssan_DrawKokiriShopkeeper; + this->obj3ToSeg6Func = EnOssan_Obj3ToSeg6; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, FAIRY_KOKIRI); +} + +void EnOssan_InitGoronShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGoronSkel, NULL, NULL, NULL, 0); + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objBankIndex3].segment); + Animation_Change(&this->skelAnime, &gGoronShopkeeperAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGoronShopkeeperAnim), + 0, 0.0f); + this->actor.draw = EnOssan_DrawGoronShopkeeper; + this->obj3ToSeg6Func = EnOssan_Obj3ToSeg6; +} + +void EnOssan_InitZoraShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZoraSkel, NULL, NULL, NULL, 0); + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objBankIndex3].segment); + Animation_Change(&this->skelAnime, &gZoraShopkeeperAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gZoraShopkeeperAnim), + 0, 0.0f); + this->actor.draw = EnOssan_DrawZoraShopkeeper; + this->obj3ToSeg6Func = EnOssan_Obj3ToSeg6; +} + +void EnOssan_InitPotionShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ds2_Skel_004258, &object_ds2_Anim_0002E4, 0, 0, 0); + this->actor.draw = EnOssan_DrawPotionShopkeeper; + this->obj3ToSeg6Func = NULL; +} + +void EnOssan_InitHappyMaskShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_os_Skel_004658, &object_os_Anim_0002E4, NULL, NULL, 0); + this->actor.draw = EnOssan_DrawHappyMaskShopkeeper; + this->obj3ToSeg6Func = NULL; +} + +void EnOssan_InitBombchuShopkeeper(EnOssan* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_rs_Skel_004868, &object_rs_Anim_00065C, 0, 0, 0); + this->actor.draw = EnOssan_DrawBombchuShopkeeper; + this->obj3ToSeg6Func = NULL; +} + +u16 EnOssan_SetupHelloDialog(EnOssan* this) { + this->happyMaskShopState = OSSAN_HAPPY_STATE_NONE; + // mask shop messages + if (this->actor.params == OSSAN_TYPE_MASK) { + if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_SOLD_OUT) { + if (gSaveContext.itemGetInf[3] & 0x800) { + if (!(gSaveContext.eventChkInf[8] & 0x8000)) { + // Pay back Bunny Hood + this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD; + return 0x70C6; + } else { + return 0x70AC; + } + } + if (gSaveContext.itemGetInf[3] & 0x400) { + if (!(gSaveContext.eventChkInf[8] & 0x4000)) { + // Pay back Spooky Mask + this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK; + return 0x70C5; + } else { + return 0x70AC; + } + } + if (gSaveContext.itemGetInf[3] & 0x200) { + if (!(gSaveContext.eventChkInf[8] & 0x2000)) { + // Pay back Skull Mask + this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK; + return 0x70C4; + } else { + return 0x70AC; + } + } + if (gSaveContext.itemGetInf[3] & 0x100) { + if (!(gSaveContext.eventChkInf[8] & 0x1000)) { + // Pay back Keaton Mask + this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK; + return 0x70A5; + } else { + return 0x70AC; + } + } + } else { + if (gSaveContext.itemGetInf[3] & 0x800) { + return 0x70AC; + } else if (!(gSaveContext.itemGetInf[3] & 0x400) && !(gSaveContext.itemGetInf[2] & 0x10) && + !(gSaveContext.itemGetInf[3] & 0x100)) { + // Haven't borrowed the Keaton Mask + if (!(gSaveContext.itemGetInf[2] & 0x8)) { + return 0x70A1; + } else { + // Haven't sold the Keaton Mask + this->happyMaskShopState = OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK; + return 0x70A6; + } + } else { + return 0x70C7; + } + } + } + + return 0x9E; +} + +void EnOssan_InitActionFunc(EnOssan* this, GlobalContext* globalCtx) { + ShopItem* items; + + if (EnOssan_AreShopkeeperObjectsLoaded(this, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_4; + this->actor.objBankIndex = this->objBankIndex1; + Actor_SetObjectDependency(globalCtx, &this->actor); + + this->shelves = (EnTana*)Actor_Find(&globalCtx->actorCtx, ACTOR_EN_TANA, ACTORCAT_PROP); + + if (this->shelves == NULL) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "Warning!! There are no shelves!!" + osSyncPrintf("★★★ 警告!! 棚がないよ!! ★★★\n"); + osSyncPrintf(VT_RST); + return; + } + + // "Shopkeeper (params) init" + osSyncPrintf(VT_FGCOL(YELLOW) "◇◇◇ 店のおやじ( %d ) 初期設定 ◇◇◇" VT_RST "\n", this->actor.params); + + this->actor.world.pos.x += sShopkeeperPositionOffsets[this->actor.params].x; + this->actor.world.pos.y += sShopkeeperPositionOffsets[this->actor.params].y; + this->actor.world.pos.z += sShopkeeperPositionOffsets[this->actor.params].z; + + items = sShopkeeperStores[this->actor.params]; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f); + sInitFuncs[this->actor.params](this, globalCtx); + this->actor.textId = EnOssan_SetupHelloDialog(this); + this->cursorY = this->cursorX = 100.0f; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.colChkInfo.cylRadius = 50; + this->stateFlag = OSSAN_STATE_IDLE; + this->stickAccumX = this->stickAccumY = 0; + + this->cursorIndex = 0; + this->cursorZ = 1.5f; + this->cursorColorR = 0; + this->cursorColorG = 255; + this->cursorColorB = 80; + this->cursorColorA = 255; + this->cursorAnimTween = 0; + + this->cursorAnimState = 0; + this->drawCursor = 0; + this->happyMaskShopkeeperEyeIdx = 0; + + this->stickLeftPrompt.stickColorR = 200; + this->stickLeftPrompt.stickColorG = 200; + this->stickLeftPrompt.stickColorB = 200; + this->stickLeftPrompt.stickColorA = 180; + this->stickLeftPrompt.stickTexX = 49; + this->stickLeftPrompt.stickTexY = 95; + this->stickLeftPrompt.arrowColorR = 255; + this->stickLeftPrompt.arrowColorG = 255; + this->stickLeftPrompt.arrowColorB = 0; + this->stickLeftPrompt.arrowColorA = 200; + this->stickLeftPrompt.arrowTexX = 33; + this->stickLeftPrompt.arrowTexY = 91; + this->stickLeftPrompt.z = 1; + this->stickLeftPrompt.isEnabled = false; + + this->stickRightPrompt.stickColorR = 200; + this->stickRightPrompt.stickColorG = 200; + this->stickRightPrompt.stickColorB = 200; + this->stickRightPrompt.stickColorA = 180; + this->stickRightPrompt.stickTexX = 274; + this->stickRightPrompt.stickTexY = 95; + this->stickRightPrompt.arrowColorR = 255; + this->stickRightPrompt.arrowColorG = 255; + this->stickRightPrompt.arrowColorB = 0; + this->stickRightPrompt.arrowColorA = 200; + this->stickRightPrompt.arrowTexX = 290; + this->stickRightPrompt.arrowTexY = 91; + this->stickRightPrompt.z = 1; + this->stickRightPrompt.isEnabled = false; + + this->arrowAnimState = 0; + this->stickAnimState = 0; + this->arrowAnimTween = 0; + this->stickAnimTween = 0; + this->shopItemSelectedTween = 0; + Actor_SetScale(&this->actor, sShopkeeperScale[this->actor.params]); + EnOssan_SpawnItemsOnShelves(this, globalCtx, items); + this->headRot = this->headTargetRot = 0; + this->blinkTimer = 20; + this->eyeTextureIdx = 0; + this->blinkFunc = EnOssan_WaitForBlink; + this->actor.flags &= ~ACTOR_FLAG_0; + EnOssan_SetupAction(this, EnOssan_MainActionFunc); + } +} + +void EnOssan_Obj3ToSeg6(EnOssan* this, GlobalContext* globalCtx) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndex3].segment); +} + +void EnOssan_MainActionFunc(EnOssan* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->blinkFunc(this); + EnOssan_UpdateJoystickInputState(globalCtx, this); + EnOssan_UpdateItemSelectedProperty(this); + EnOssan_UpdateStickDirectionPromptAnim(this); + EnOssan_UpdateCursorAnim(this); + Math_StepToS(&this->headRot, this->headTargetRot, 0x190); + + if (player != NULL) { + sStateFunc[this->stateFlag](this, globalCtx, player); + } + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 26.0f, 10.0f, 0.0f, 5); + Actor_SetFocus(&this->actor, 90.0f); + Actor_SetScale(&this->actor, sShopkeeperScale[this->actor.params]); + + // use animation object if needed + if (this->obj3ToSeg6Func != NULL) { + this->obj3ToSeg6Func(this, globalCtx); + } + + SkelAnime_Update(&this->skelAnime); +} + +void EnOssan_Update(Actor* thisx, GlobalContext* globalCtx) { + EnOssan* this = (EnOssan*)thisx; + + this->timer++; + this->actionFunc(this, globalCtx); +} + +s32 EnOssan_OverrideLimbDrawDefaultShopkeeper(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, + Vec3s* rot, void* thisx) { + EnOssan* this = (EnOssan*)thisx; + + if (limbIndex == 8) { + rot->x += this->headRot; + } + return 0; +} + +void EnOssan_DrawCursor(GlobalContext* globalCtx, EnOssan* this, f32 x, f32 y, f32 z, u8 drawCursor) { + s32 ulx, uly, lrx, lry; + f32 w; + s32 dsdx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4192); + if (drawCursor != 0) { + func_80094520(globalCtx->state.gfxCtx); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, this->cursorColorR, this->cursorColorG, this->cursorColorB, + this->cursorColorA); + gDPLoadTextureBlock_4b(OVERLAY_DISP++, gSelectionCursorTex, G_IM_FMT_IA, 16, 16, 0, G_TX_MIRROR | G_TX_WRAP, + G_TX_MIRROR | G_TX_WRAP, 4, 4, G_TX_NOLOD, G_TX_NOLOD); + w = 16.0f * z; + ulx = (x - w) * 4.0f; + uly = (y - w) * 4.0f; + lrx = (x + w) * 4.0f; + lry = (y + w) * 4.0f; + dsdx = (1.0f / z) * 1024.0f; + gSPTextureRectangle(OVERLAY_DISP++, ulx, uly, lrx, lry, G_TX_RENDERTILE, 0, 0, dsdx, dsdx); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4215); +} + +void EnOssan_DrawTextRec(GlobalContext* globalCtx, s32 r, s32 g, s32 b, s32 a, f32 x, f32 y, f32 z, s32 s, s32 t, + f32 dx, f32 dy) { + f32 unk; + s32 ulx, uly, lrx, lry; + f32 w, h; + s32 dsdx, dtdy; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4228); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, r, g, b, a); + + w = 8.0f * z; + h = 12.0f * z; + unk = (1.0f / z) * 1024; + dsdx = unk * dx; + dtdy = dy * unk; + + ulx = (x - w) * 4.0f; + uly = (y - h) * 4.0f; + lrx = (x + w) * 4.0f; + lry = (y + h) * 4.0f; + gSPTextureRectangle(OVERLAY_DISP++, ulx, uly, lrx, lry, G_TX_RENDERTILE, s, t, dsdx, dtdy); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4242); +} + +void EnOssan_DrawStickDirectionPrompts(GlobalContext* globalCtx, EnOssan* this) { + s32 drawStickLeftPrompt = this->stickLeftPrompt.isEnabled; + s32 drawStickRightPrompt = this->stickRightPrompt.isEnabled; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4252); + if (drawStickLeftPrompt || drawStickRightPrompt) { + func_80094520(globalCtx->state.gfxCtx); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPLoadTextureBlock(OVERLAY_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + if (drawStickLeftPrompt) { + EnOssan_DrawTextRec(globalCtx, this->stickLeftPrompt.arrowColorR, this->stickLeftPrompt.arrowColorG, + this->stickLeftPrompt.arrowColorB, this->stickLeftPrompt.arrowColorA, + this->stickLeftPrompt.arrowTexX, this->stickLeftPrompt.arrowTexY, + this->stickLeftPrompt.z, 0, 0, -1.0f, 1.0f); + } + if (drawStickRightPrompt) { + EnOssan_DrawTextRec(globalCtx, this->stickRightPrompt.arrowColorR, this->stickRightPrompt.arrowColorG, + this->stickRightPrompt.arrowColorB, this->stickRightPrompt.arrowColorA, + this->stickRightPrompt.arrowTexX, this->stickRightPrompt.arrowTexY, + this->stickRightPrompt.z, 0, 0, 1.0f, 1.0f); + } + gDPLoadTextureBlock(OVERLAY_DISP++, gControlStickTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + if (drawStickLeftPrompt) { + EnOssan_DrawTextRec(globalCtx, this->stickLeftPrompt.stickColorR, this->stickLeftPrompt.stickColorG, + this->stickLeftPrompt.stickColorB, this->stickLeftPrompt.stickColorA, + this->stickLeftPrompt.stickTexX, this->stickLeftPrompt.stickTexY, + this->stickLeftPrompt.z, 0, 0, -1.0f, 1.0f); + } + if (drawStickRightPrompt) { + EnOssan_DrawTextRec(globalCtx, this->stickRightPrompt.stickColorR, this->stickRightPrompt.stickColorG, + this->stickRightPrompt.stickColorB, this->stickRightPrompt.stickColorA, + this->stickRightPrompt.stickTexX, this->stickRightPrompt.stickTexY, + this->stickRightPrompt.z, 0, 0, 1.0f, 1.0f); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4300); +} + +void EnOssan_DrawBazaarShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + static void* sBazaarShopkeeperEyeTextures[] = { gOssanEyeOpenTex, gOssanEyeHalfTex, gOssanEyeClosedTex }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4320); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sBazaarShopkeeperEyeTextures[this->eyeTextureIdx])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnOssan_OverrideLimbDrawDefaultShopkeeper, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4340); +} + +s32 EnOssan_OverrideLimbDrawKokiriShopkeeper(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, + Vec3s* rot, void* thisx) { + static void* sKokiriShopkeeperEyeTextures[] = { + gKokiriShopkeeperEyeDefaultTex, + gKokiriShopkeeperEyeHalfTex, + gKokiriShopkeeperEyeOpenTex, + }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4354); + + if (limbIndex == 15) { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->objBankIndex2].segment); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndex2].segment); + *dList = gKokiriShopkeeperHeadDL; + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(sKokiriShopkeeperEyeTextures[this->eyeTextureIdx])); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4374); + + return 0; +} + +Gfx* EnOssan_EndDList(GraphicsContext* gfxCtx) { + Gfx* disp = Graph_Alloc(gfxCtx, sizeof(Gfx)); + + gSPEndDisplayList(disp); + return disp; +} + +Gfx* EnOssan_SetEnvColor(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b, u8 a) { + Gfx* disp = Graph_Alloc(gfxCtx, sizeof(Gfx) * 2); + + gDPSetEnvColor(disp, r, g, b, a); + gSPEndDisplayList(disp + 1); + return disp; +} + +void EnOssan_DrawKokiriShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4409); + + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x08, EnOssan_SetEnvColor(globalCtx->state.gfxCtx, 0, 130, 70, 255)); + gSPSegment(POLY_OPA_DISP++, 0x09, EnOssan_SetEnvColor(globalCtx->state.gfxCtx, 110, 170, 20, 255)); + gSPSegment(POLY_OPA_DISP++, 0x0C, EnOssan_EndDList(globalCtx->state.gfxCtx)); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnOssan_OverrideLimbDrawKokiriShopkeeper, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4434); +} + +void EnOssan_DrawGoronShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + static void* sGoronShopkeeperEyeTextures[] = { gGoronCsEyeOpenTex, gGoronCsEyeHalfTex, gGoronCsEyeClosedTex }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4455); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sGoronShopkeeperEyeTextures[this->eyeTextureIdx])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gGoronCsMouthNeutralTex)); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4476); +} + +s32 EnOssan_OverrideLimbDrawZoraShopkeeper(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnOssan* this = (EnOssan*)thisx; + + if (limbIndex == 15) { + rot->x += this->headRot; + } + return 0; +} + +void EnOssan_DrawZoraShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + static void* sZoraShopkeeperEyeTextures[] = { gZoraEyeOpenTex, gZoraEyeHalfTex, gZoraEyeClosedTex }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4506); + + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, EnOssan_EndDList(globalCtx->state.gfxCtx)); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sZoraShopkeeperEyeTextures[this->eyeTextureIdx])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnOssan_OverrideLimbDrawZoraShopkeeper, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4531); +} + +void EnOssan_DrawPotionShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + static void* sPotionShopkeeperEyeTextures[] = { gPotionShopkeeperEyeOpenTex, gPotionShopkeeperEyeHalfTex, + gPotionShopkeeperEyeClosedTex }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4544); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sPotionShopkeeperEyeTextures[this->eyeTextureIdx])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4564); +} + +void EnOssan_DrawHappyMaskShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + static void* sHappyMaskShopkeeperEyeTextures[] = { gOsEyeClosedTex, gOsEyeOpenTex }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4578); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, + SEGMENTED_TO_VIRTUAL(sHappyMaskShopkeeperEyeTextures[this->happyMaskShopkeeperEyeIdx])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4598); +} + +void EnOssan_DrawBombchuShopkeeper(Actor* thisx, GlobalContext* globalCtx) { + static void* sBombchuShopkeeperEyeTextures[] = { gBombchuShopkeeperEyeOpenTex, gBombchuShopkeeperEyeHalfTex, + gBombchuShopkeeperEyeClosedTex }; + EnOssan* this = (EnOssan*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4611); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sBombchuShopkeeperEyeTextures[this->eyeTextureIdx])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, NULL, this); + EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor); + EnOssan_DrawStickDirectionPrompts(globalCtx, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_oB1.c", 4631); +} diff --git a/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.h b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.h new file mode 100644 index 000000000..1dddce504 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.h @@ -0,0 +1,141 @@ +#ifndef Z_EN_OSSAN_H +#define Z_EN_OSSAN_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Tana/z_en_tana.h" +#include "overlays/actors/ovl_En_GirlA/z_en_girla.h" + +struct EnOssan; + +typedef void (*EnOssanActionFunc)(struct EnOssan*, GlobalContext*); +typedef void (*EnOssanTalkOwnerFunc)(GlobalContext*); +typedef void (*EnOssanInitFunc)(struct EnOssan*, GlobalContext*); +typedef s16 (*EnOssanGetGirlAParamsFunc)(s16); +typedef void (*EnOssanStateFunc)(struct EnOssan*, GlobalContext*, Player*); + +typedef struct { + /* 0x00 */ u32 stickColorR; + /* 0x04 */ u32 stickColorG; + /* 0x08 */ u32 stickColorB; + /* 0x0C */ u32 stickColorA; + /* 0x10 */ f32 stickTexX; + /* 0x14 */ f32 stickTexY; + /* 0x18 */ u32 arrowColorR; + /* 0x1C */ u32 arrowColorG; + /* 0x20 */ u32 arrowColorB; + /* 0x24 */ u32 arrowColorA; + /* 0x28 */ f32 arrowTexX; + /* 0x2C */ f32 arrowTexY; + /* 0x30 */ f32 z; + /* 0x34 */ s32 isEnabled; +} StickDirectionPrompt; // size = 0x38 + +#define ColChanMix(c1, c2, m) (c1 - (s32)(c2 * m)) & 0xFF + +typedef struct EnOssan { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnOssanActionFunc actionFunc; + /* 0x0194 */ void (*obj3ToSeg6Func)(struct EnOssan*, GlobalContext*); + /* 0x0198 */ ColliderCylinder collider; // unused + /* 0x01E4 */ s16 timer; + /* 0x01E6 */ s16 delayTimer; + /* 0x01E8 */ s8 objBankIndex1; + /* 0x01E9 */ s8 objBankIndex2; + /* 0x01EA */ s8 objBankIndex3; + /* 0x01EB */ u8 happyMaskShopState; + /* 0x01EC */ u8 happyMaskShopkeeperEyeIdx; + /* 0x01EE */ s16 headRot; + /* 0x01F0 */ s16 headTargetRot; + /* 0x01F2 */ s16 eyeTextureIdx; + /* 0x01F4 */ s16 blinkTimer; + /* 0x01F8 */ void (*blinkFunc)(struct EnOssan*); + /* 0x01FC */ s16 stateFlag; + /* 0x01FE */ s16 tempStateFlag; + /* 0x0200 */ EnGirlA* shelfSlots[8]; + // Shelves are indexed as such: + /* 7 5 3 1 */ + /* 6 4 2 0 */ + /* 0x0220 */ EnTana* shelves; + /* 0x0224 */ s32 stickAccumX; + /* 0x0228 */ s32 stickAccumY; + /* 0x022C */ u8 moveHorizontal; + /* 0x022D */ u8 moveVertical; + /* 0x0230 */ f32 cursorX; + /* 0x0234 */ f32 cursorY; + /* 0x0238 */ f32 cursorZ; + /* 0x023C */ u32 cursorColorR; + /* 0x0240 */ u32 cursorColorG; + /* 0x0244 */ u32 cursorColorB; + /* 0x0248 */ u32 cursorColorA; + /* 0x024C */ f32 cursorAnimTween; + /* 0x0250 */ u8 cursorAnimState; + /* 0x0251 */ u8 drawCursor; + /* 0x0252 */ u8 cursorIndex; + /* 0x0254 */ StickDirectionPrompt stickLeftPrompt; + /* 0x028C */ StickDirectionPrompt stickRightPrompt; + /* 0x02C4 */ f32 arrowAnimTween; + /* 0x02C4 */ f32 stickAnimTween; + /* 0x02CC */ u8 arrowAnimState; + /* 0x02CD */ u8 stickAnimState; + /* 0x02D0 */ f32 shopItemSelectedTween; + /* 0x02D4 */ f32 cameraFaceAngle; // stored in degrees +} EnOssan; // size = 0x02D8 + +typedef enum { + /* 00 */ OSSAN_TYPE_KOKIRI, + /* 01 */ OSSAN_TYPE_KAKARIKO_POTION, + /* 02 */ OSSAN_TYPE_BOMBCHUS, + /* 03 */ OSSAN_TYPE_MARKET_POTION, + /* 04 */ OSSAN_TYPE_BAZAAR, + /* 05 */ OSSAN_TYPE_ADULT, + /* 06 */ OSSAN_TYPE_TALON, + /* 07 */ OSSAN_TYPE_ZORA, + /* 08 */ OSSAN_TYPE_GORON, + /* 09 */ OSSAN_TYPE_INGO, + /* 10 */ OSSAN_TYPE_MASK +} OssanType; + +typedef enum { + /* 00 */ OSSAN_STATE_IDLE, + /* 01 */ OSSAN_STATE_START_CONVERSATION, + /* 02 */ OSSAN_STATE_FACING_SHOPKEEPER, + /* 03 */ OSSAN_STATE_TALKING_TO_SHOPKEEPER, + /* 04 */ OSSAN_STATE_LOOK_SHELF_LEFT, + /* 05 */ OSSAN_STATE_LOOK_SHELF_RIGHT, + /* 06 */ OSSAN_STATE_BROWSE_LEFT_SHELF, + /* 07 */ OSSAN_STATE_BROWSE_RIGHT_SHELF, + /* 08 */ OSSAN_STATE_LOOK_SHOPKEEPER, // From looking at shelf + /* 09 */ OSSAN_STATE_SELECT_ITEM, // Select most items + /* 10 */ OSSAN_STATE_SELECT_ITEM_MILK_BOTTLE, + /* 11 */ OSSAN_STATE_SELECT_ITEM_WEIRD_EGG, + /* 12 */ OSSAN_STATE_SELECT_ITEM_UNIMPLEMENTED, // Handles two unfinished shop items + /* 13 */ OSSAN_STATE_SELECT_ITEM_BOMBS, + /* 14 */ OSSAN_STATE_CANT_GET_ITEM, + /* 15 */ OSSAN_STATE_GIVE_ITEM_FANFARE, // Give Item, hold it up with fanfare + /* 16 */ OSSAN_STATE_ITEM_PURCHASED, + /* 17 */ OSSAN_STATE_CONTINUE_SHOPPING_PROMPT, + /* 18 */ OSSAN_STATE_GIVE_LON_LON_MILK, + /* 19 */ OSSAN_STATE_DISPLAY_ONLY_BOMB_DIALOG, // Turn to shopkeeper, talk about fake bombs + /* 20 */ OSSAN_STATE_WAIT_FOR_DISPLAY_ONLY_BOMB_DIALOG, // Can't Get Goron City Bombs + /* 21 */ OSSAN_STATE_21, // Unused + /* 22 */ OSSAN_STATE_22, // Follows OSSAN_STATE_21 + /* 23 */ OSSAN_STATE_QUICK_BUY, + /* 24 */ OSSAN_STATE_SELECT_ITEM_MASK, + /* 25 */ OSSAN_STATE_LEND_MASK_OF_TRUTH, // First time all masks are sold + /* 26 */ OSSAN_STATE_DISCOUNT_DIALOG // Hylian Shield Discount +} EnOssanState; + +typedef enum { + OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK, + OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK, + OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK, + OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD, + OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK, + OSSAN_HAPPY_STATE_ANGRY, // Give me my money man! + OSSAN_HAPPY_STATE_ALL_MASKS_SOLD, // All masks have been sold + OSSAN_HAPPY_STATE_NONE = 8 // No Action / Payment received! +} EnOssanHappyMaskState; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c b/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c new file mode 100644 index 000000000..054f00b4d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.c @@ -0,0 +1,1421 @@ +/* + * File: z_en_owl.c + * Overlay: ovl_En_Owl + * Description: Owl + */ + +#include "z_en_owl.h" +#include "objects/object_owl/object_owl.h" +#include "scenes/overworld/spot06/spot06_scene.h" +#include "scenes/overworld/spot16/spot16_scene.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnOwl_Init(Actor* thisx, GlobalContext* globalCtx); +void EnOwl_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnOwl_Update(Actor* thisx, GlobalContext* globalCtx); +void EnOwl_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnOwl_ChangeMode(EnOwl* this, EnOwlActionFunc, OwlFunc, SkelAnime*, AnimationHeader*, f32); +void EnOwl_WaitDefault(EnOwl* this, GlobalContext* globalCtx); +void func_80ACC540(EnOwl* this); +void EnOwl_WaitOutsideKokiri(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitHyruleCastle(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitKakariko(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitGerudo(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitLakeHylia(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitZoraRiver(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitHyliaShortcut(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitDeathMountainShortcut(EnOwl* this, GlobalContext* globalCtx); +void func_80ACB3E0(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitLWPreSaria(EnOwl* this, GlobalContext* globalCtx); +void EnOwl_WaitLWPostSaria(EnOwl* this, GlobalContext* globalCtx); +void func_80ACD4D4(EnOwl* this, GlobalContext* globalCtx); +void func_80ACD130(EnOwl* this, GlobalContext* globalCtx, s32 arg2); +void func_80ACBAB8(EnOwl* this, GlobalContext* globalCtx); +void func_80ACD2CC(EnOwl* this, GlobalContext* globalCtx); +void func_80ACAA54(EnOwl* this, GlobalContext* globalCtx); +void func_80ACAC6C(EnOwl* this, GlobalContext* globalCtx); +void func_80ACADF0(EnOwl* this, GlobalContext* globalCtx); +void func_80ACAF74(EnOwl* this, GlobalContext* globalCtx); +void func_80ACC30C(EnOwl* this, GlobalContext* globalCtx); +void func_80ACB4FC(EnOwl* this, GlobalContext* globalCtx); +void func_80ACB680(EnOwl* this, GlobalContext* globalCtx); +void func_80ACC460(EnOwl* this); +void func_80ACBEA0(EnOwl*, GlobalContext*); + +typedef enum { + /* 0x00 */ OWL_DEFAULT, + /* 0x01 */ OWL_OUTSIDE_KOKIRI, + /* 0x02 */ OWL_HYRULE_CASTLE, + /* 0x03 */ OWL_KAKARIKO, + /* 0x04 */ OWL_HYLIA_GERUDO, + /* 0x05 */ OWL_LAKE_HYLIA, + /* 0x06 */ OWL_ZORA_RIVER, + /* 0x07 */ OWL_HYLIA_SHORTCUT, + /* 0x08 */ OWL_DEATH_MOUNTAIN, + /* 0x09 */ OWL_DEATH_MOUNTAIN2, + /* 0x0A */ OWL_DESSERT_COLOSSUS, + /* 0x0B */ OWL_LOST_WOODS_PRESARIA, + /* 0x0C */ OWL_LOST_WOODS_POSTSARIA +} EnOwlType; + +typedef enum { + /* 0x00 */ OWL_REPEAT, + /* 0x01 */ OWL_OK +} EnOwlMessageChoice; + +const ActorInit En_Owl_InitVars = { + ACTOR_EN_OWL, + ACTORCAT_NPC, + FLAGS, + OBJECT_OWL, + sizeof(EnOwl), + (ActorFunc)EnOwl_Init, + (ActorFunc)EnOwl_Destroy, + (ActorFunc)EnOwl_Update, + (ActorFunc)EnOwl_Draw, + NULL, +}; + +static ColliderCylinderInit sOwlCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 25, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2400, ICHAIN_STOP), +}; + +void EnOwl_Init(Actor* thisx, GlobalContext* globalCtx) { + EnOwl* this = (EnOwl*)thisx; + ColliderCylinder* collider; + s32 owlType; + s32 switchFlag; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gOwlFlyingSkel, &gOwlFlyAnim, this->jointTable, this->morphTable, + 21); + SkelAnime_InitFlex(globalCtx, &this->skelAnime2, &gOwlPerchingSkel, &gOwlPerchAnim, this->jointTable2, + this->morphTable2, 16); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sOwlCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.minVelocityY = -10.0f; + this->actor.targetArrowOffset = 500.0f; + EnOwl_ChangeMode(this, EnOwl_WaitDefault, func_80ACC540, &this->skelAnime2, &gOwlPerchAnim, 0.0f); + this->actionFlags = this->unk_406 = this->unk_409 = 0; + this->unk_405 = 4; + this->unk_404 = this->unk_407 = 0; + this->unk_408 = 4; + owlType = (this->actor.params & 0xFC0) >> 6; + switchFlag = (this->actor.params & 0x3F); + if (this->actor.params == 0xFFF) { + owlType = OWL_OUTSIDE_KOKIRI; + switchFlag = 0x20; + } + // "conversation owl %4x no = %d, sv = %d" + osSyncPrintf(VT_FGCOL(CYAN) " 会話フクロウ %4x no = %d, sv = %d\n" VT_RST, this->actor.params, owlType, switchFlag); + + if ((owlType != OWL_DEFAULT) && (switchFlag < 0x20) && Flags_GetSwitch(globalCtx, switchFlag)) { + osSyncPrintf("savebitでフクロウ退避\n"); // "Save owl with savebit" + Actor_Kill(&this->actor); + return; + } + + this->unk_3EE = 0; + this->unk_400 = this->actor.world.rot.y; + + switch (owlType) { + case OWL_DEFAULT: + this->actionFunc = EnOwl_WaitDefault; + this->actor.uncullZoneForward = 4000.0f; + this->unk_40A = 0; + break; + case OWL_OUTSIDE_KOKIRI: + this->actionFunc = EnOwl_WaitOutsideKokiri; + break; + case OWL_HYRULE_CASTLE: + this->actionFlags |= 2; + this->unk_3EE = 0x20; + this->actionFunc = EnOwl_WaitHyruleCastle; + break; + case OWL_KAKARIKO: + if (gSaveContext.eventChkInf[4] & 1) { + // has zelda's letter + osSyncPrintf("フクロウ退避\n"); // "Owl evacuation" + Actor_Kill(&this->actor); + return; + } + + this->actionFunc = EnOwl_WaitKakariko; + break; + case OWL_HYLIA_GERUDO: + if (gSaveContext.eventChkInf[4] & 8) { + // has ocarina of time + osSyncPrintf("フクロウ退避\n"); // "Owl evacuation" + Actor_Kill(&this->actor); + return; + } + this->actionFunc = EnOwl_WaitGerudo; + break; + case OWL_LAKE_HYLIA: + this->actionFunc = EnOwl_WaitLakeHylia; + break; + case OWL_ZORA_RIVER: + if ((gSaveContext.eventChkInf[3] & 0x200) || !(gSaveContext.eventChkInf[4] & 1)) { + // opened zora's domain or has zelda's letter + osSyncPrintf("フクロウ退避\n"); // "Owl evacuation" + Actor_Kill(&this->actor); + return; + } + + this->actionFunc = EnOwl_WaitZoraRiver; + break; + case OWL_HYLIA_SHORTCUT: + this->actionFunc = EnOwl_WaitHyliaShortcut; + Flags_UnsetSwitch(globalCtx, 0x23); + return; + case OWL_DEATH_MOUNTAIN: + this->actionFunc = EnOwl_WaitDeathMountainShortcut; + break; + case OWL_DEATH_MOUNTAIN2: + this->actionFunc = EnOwl_WaitDeathMountainShortcut; + break; + case OWL_DESSERT_COLOSSUS: + this->actionFunc = func_80ACB3E0; + break; + case OWL_LOST_WOODS_PRESARIA: + if (!CHECK_QUEST_ITEM(QUEST_SONG_LULLABY)) { + osSyncPrintf("フクロウ退避\n"); // "Owl evacuation" + Actor_Kill(&this->actor); + return; + } + this->actionFunc = EnOwl_WaitLWPreSaria; + break; + case OWL_LOST_WOODS_POSTSARIA: + if (!CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) { + osSyncPrintf("フクロウ退避\n"); // "Owl evacuation" + Actor_Kill(&this->actor); + return; + } + this->actionFunc = EnOwl_WaitLWPostSaria; + break; + default: + // Outside kokiri forest + osSyncPrintf(VT_FGCOL(CYAN)); + osSyncPrintf("no = %d \n", owlType); + // "Unfinished owl unfinished owl unfinished owl" + osSyncPrintf("未完成のフクロウ未完成のフクロウ未完成のフクロウ\n"); + osSyncPrintf(VT_RST); + this->actionFlags |= 2; + this->unk_3EE = 0x20; + this->actionFunc = EnOwl_WaitOutsideKokiri; + break; + } +} + +void EnOwl_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnOwl* this = (EnOwl*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +/** + * Rotates this to the player instance + */ +void EnOwl_LookAtLink(EnOwl* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->actor.shape.rot.y = this->actor.world.rot.y = + Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos); +} + +/** + * Checks if link is within `targetDist` units, initalize the camera for the owl. + * returns 0 if the link is not within `targetDistance`, returns 1 once link is within + * the distance, and the camera has been initalized. + */ +s32 EnOwl_CheckInitTalk(EnOwl* this, GlobalContext* globalCtx, u16 textId, f32 targetDist, u16 flags) { + s32 timer; + f32 distCheck; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->actor.params == 0xFFF) { + this->actionFlags |= 0x40; + timer = -100; + } else { + if (Rand_ZeroOne() < 0.5f) { + timer = (flags & 1) ? -97 : -99; + this->actionFlags |= 0x40; + } else { + timer = (flags & 1) ? -96 : -98; + this->actionFlags &= ~0x40; + } + } + this->cameraIdx = OnePointCutscene_Init(globalCtx, 8700, timer, &this->actor, MAIN_CAM); + return true; + } else { + this->actor.textId = textId; + distCheck = (flags & 2) ? 200.0f : 1000.0f; + if (this->actor.xzDistToPlayer < targetDist) { + this->actor.flags |= ACTOR_FLAG_16; + func_8002F1C4(&this->actor, globalCtx, targetDist, distCheck, 0); + } + return false; + } +} + +s32 func_80ACA558(EnOwl* this, GlobalContext* globalCtx, u16 textId) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + return true; + } else { + this->actor.textId = textId; + if (this->actor.xzDistToPlayer < 120.0f) { + func_8002F1C4(&this->actor, globalCtx, 350.0f, 1000.0f, 0); + } + return false; + } +} + +void func_80ACA5C8(EnOwl* this) { + EnOwl_ChangeMode(this, func_80ACBEA0, func_80ACC540, &this->skelAnime, &gOwlUnfoldWingsAnim, 0.0f); + this->eyeTexIndex = 0; + this->blinkTimer = Rand_S16Offset(60, 60); +} + +void func_80ACA62C(EnOwl* this, GlobalContext* globalCtx) { + s32 switchFlag = this->actor.params & 0x3F; + + if (switchFlag < 0x20) { + Flags_SetSwitch(globalCtx, switchFlag); + osSyncPrintf(VT_FGCOL(CYAN) " Actor_Environment_sw = %d\n" VT_RST, Flags_GetSwitch(globalCtx, switchFlag)); + } + func_80ACA5C8(this); +} + +void func_80ACA690(EnOwl* this, GlobalContext* globalCtx) { + if ((this->unk_3EE & 0x3F) == 0) { + func_80ACA62C(this, globalCtx); + } +} + +void func_80ACA6C0(EnOwl* this) { + if (Rand_CenteredFloat(1.0f) < 0.0f) { + this->actionFlags |= 0x20; + } else { + this->actionFlags &= ~0x20; + } +} + +void func_80ACA71C(EnOwl* this) { + func_80ACA6C0(this); + this->unk_3F2 = 0; + this->actionFlags |= 0x10; + this->unk_408 = 4; + this->unk_404 = 0; + this->unk_406 = 0; + this->unk_405 = 4; + this->unk_407 = this->unk_3F2; +} + +void func_80ACA76C(EnOwl* this, GlobalContext* globalCtx) { + func_8002DF54(globalCtx, &this->actor, 8); + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF); + func_80ACA62C(this, globalCtx); + this->actor.flags &= ~ACTOR_FLAG_16; + } +} + +void func_80ACA7E0(EnOwl* this, GlobalContext* globalCtx) { + func_8002DF54(globalCtx, &this->actor, 8); + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF); + if ((this->unk_3EE & 0x3F) == 0) { + func_80ACA62C(this, globalCtx); + } else { + this->actionFlags &= ~2; + func_80ACA71C(this); + this->actionFunc = func_80ACA690; + } + this->actor.flags &= ~ACTOR_FLAG_16; + } +} + +void EnOwl_ConfirmKokiriMessage(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x2065); + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x2067); + this->actionFunc = func_80ACA76C; + break; + } + } +} + +void EnOwl_WaitOutsideKokiri(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x2064, 360.0f, 0)) { + // Sets BGM + Audio_PlayFanfare(NA_BGM_OWL); + + this->actionFunc = EnOwl_ConfirmKokiriMessage; + // spoke to owl by lost woods + gSaveContext.eventChkInf[6] |= 0x8000; + } +} + +void func_80ACA998(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x2069); + this->actionFunc = func_80ACAA54; + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x206B); + this->actionFunc = func_80ACA7E0; + break; + } + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void func_80ACAA54(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x206A); + this->actionFunc = func_80ACA998; + this->actionFlags |= 2; + func_80ACA71C(this); + } +} + +void func_80ACAAC0(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x2069); + this->actionFunc = func_80ACAA54; + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void EnOwl_WaitHyruleCastle(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x2068, 540.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACAAC0; + } +} + +void func_80ACAB88(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + // obtained zelda's letter + if (gSaveContext.eventChkInf[4] & 1) { + Message_ContinueTextbox(globalCtx, 0x206D); + } else { + Message_ContinueTextbox(globalCtx, 0x206C); + } + this->actionFunc = func_80ACAC6C; + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x206E); + this->actionFunc = func_80ACA7E0; + break; + } + + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void func_80ACAC6C(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x206A); + this->actionFunc = func_80ACAB88; + this->actionFlags |= 2; + func_80ACA71C(this); + } +} + +void EnOwl_WaitKakariko(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x206C, 480.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACAC6C; + } +} + +void func_80ACAD34(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x206F); + this->actionFunc = func_80ACADF0; + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x2070); + this->actionFunc = func_80ACA7E0; + break; + } + + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void func_80ACADF0(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x206A); + this->actionFunc = func_80ACAD34; + this->actionFlags |= 2; + func_80ACA71C(this); + } +} + +void EnOwl_WaitGerudo(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x206F, 360.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACADF0; + } +} + +void func_80ACAEB8(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x2071); + this->actionFunc = func_80ACAF74; + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x2072); + this->actionFunc = func_80ACA7E0; + break; + } + + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void func_80ACAF74(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x206A); + this->actionFunc = func_80ACAEB8; + this->actionFlags |= 2; + func_80ACA71C(this); + } +} + +void EnOwl_WaitLakeHylia(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x2071, 360.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACAF74; + } +} + +void func_80ACB03C(EnOwl* this, GlobalContext* globalCtx) { + func_8002DF54(globalCtx, &this->actor, 8); + + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF); + func_80ACA62C(this, globalCtx); + this->actor.flags &= ~ACTOR_FLAG_16; + } +} + +void EnOwl_WaitZoraRiver(EnOwl* this, GlobalContext* globalCtx) { + u16 textId; + + EnOwl_LookAtLink(this, globalCtx); + + if (CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) { + if (CHECK_QUEST_ITEM(QUEST_SONG_LULLABY)) { + textId = 0x4031; + } else { + textId = 0x4017; + } + } else { + textId = 0x4002; + } + + if (EnOwl_CheckInitTalk(this, globalCtx, textId, 360.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB03C; + } +} + +void func_80ACB148(EnOwl* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF); + func_80ACA5C8(this); + this->actionFunc = func_80ACC30C; + Flags_SetSwitch(globalCtx, 0x23); + } +} + +void EnOwl_WaitHyliaShortcut(EnOwl* this, GlobalContext* globalCtx) { + u16 textId = (gSaveContext.infTable[25] & 0x20) ? 0x4004 : 0x4003; + + // Spoke to Owl in Lake Hylia + EnOwl_LookAtLink(this, globalCtx); + if (func_80ACA558(this, globalCtx, textId)) { + gSaveContext.infTable[25] |= 0x20; + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB148; + } +} + +void func_80ACB22C(EnOwl* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF); + func_80ACA5C8(this); + this->actionFunc = func_80ACC30C; + } +} + +void func_80ACB274(EnOwl* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_FANFARE << 24 | 0xFF); + this->actionFunc = EnOwl_WaitDeathMountainShortcut; + } +} + +void EnOwl_WaitDeathMountainShortcut(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (!gSaveContext.magicAcquired) { + if (func_80ACA558(this, globalCtx, 0x3062)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB274; + return; + } + } else { + if (func_80ACA558(this, globalCtx, 0x3063)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB22C; + } + } +} + +void func_80ACB344(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x607A); + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x607C); + this->actionFunc = func_80ACA7E0; + break; + } + } +} + +void func_80ACB3E0(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x6079, 360.0f, 2)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB344; + } +} + +void func_80ACB440(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x10C1); + this->actionFunc = func_80ACB4FC; + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x10C3); + this->actionFunc = func_80ACA7E0; + } + + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void func_80ACB4FC(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x10C2); + this->actionFunc = func_80ACB440; + this->actionFlags |= 2; + func_80ACA71C(this); + } +} + +void EnOwl_WaitLWPreSaria(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x10C0, 190.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB4FC; + } +} + +void func_80ACB5C4(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case OWL_REPEAT: + Message_ContinueTextbox(globalCtx, 0x10C5); + this->actionFunc = func_80ACB680; + break; + case OWL_OK: + Message_ContinueTextbox(globalCtx, 0x10C7); + this->actionFunc = func_80ACA7E0; + break; + } + + this->actionFlags &= ~2; + func_80ACA71C(this); + } +} + +void func_80ACB680(EnOwl* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x10C6); + this->actionFunc = func_80ACB5C4; + this->actionFlags |= 2; + func_80ACA71C(this); + } +} + +void EnOwl_WaitLWPostSaria(EnOwl* this, GlobalContext* globalCtx) { + EnOwl_LookAtLink(this, globalCtx); + + if (EnOwl_CheckInitTalk(this, globalCtx, 0x10C4, 360.0f, 0)) { + Audio_PlayFanfare(NA_BGM_OWL); + this->actionFunc = func_80ACB680; + } +} + +void func_80ACB748(EnOwl* this, GlobalContext* globalCtx) { + static Vec3f D_80ACD62C = { 0.0f, 0.0f, 0.0f }; + f32 dist; + f32 weight; + s32 owlType = (this->actor.params & 0xFC0) >> 6; + + dist = Math3D_Vec3f_DistXYZ(&this->eye, &globalCtx->view.eye) / 45.0f; + this->eye.x = globalCtx->view.eye.x; + this->eye.y = globalCtx->view.eye.y; + this->eye.z = globalCtx->view.eye.z; + + weight = dist; + if (weight > 1.0f) { + weight = 1.0f; + } + + switch (owlType) { + case 7: + func_800F436C(&D_80ACD62C, NA_SE_EV_FLYING_AIR - SFX_FLAG, weight * 2.0f); + if ((globalCtx->csCtx.frames > 324) || + ((globalCtx->csCtx.frames >= 142 && (globalCtx->csCtx.frames <= 266)))) { + func_800F4414(&D_80ACD62C, NA_SE_EN_OWL_FLUTTER, weight * 2.0f); + } + if (globalCtx->csCtx.frames == 85) { + func_800F436C(&D_80ACD62C, NA_SE_EV_PASS_AIR, weight * 2.0f); + } + break; + case 8: + case 9: + func_800F436C(&D_80ACD62C, NA_SE_EV_FLYING_AIR - SFX_FLAG, weight * 2.0f); + if ((globalCtx->csCtx.frames >= 420) || + ((0xC1 < globalCtx->csCtx.frames && (globalCtx->csCtx.frames <= 280)))) { + func_800F4414(&D_80ACD62C, NA_SE_EN_OWL_FLUTTER, weight * 2.0f); + } + if (globalCtx->csCtx.frames == 217) { + func_800F436C(&D_80ACD62C, NA_SE_EV_PASS_AIR, weight * 2.0f); + } + break; + } +} + +void func_80ACB904(EnOwl* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE && (globalCtx->csCtx.npcActions[7] != NULL)) { + if (this->unk_40A != globalCtx->csCtx.npcActions[7]->action) { + func_80ACD130(this, globalCtx, 7); + func_80ACBAB8(this, globalCtx); + } + func_80ACD2CC(this, globalCtx); + } + + if (this->actionFlags & 0x80) { + func_80ACB748(this, globalCtx); + } +} + +void func_80ACB994(EnOwl* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE && (globalCtx->csCtx.npcActions[7] != NULL)) { + if (this->unk_40A != globalCtx->csCtx.npcActions[7]->action) { + func_80ACD130(this, globalCtx, 7); + func_80ACBAB8(this, globalCtx); + } + func_80ACD4D4(this, globalCtx); + } + + if (this->actionFlags & 0x80) { + func_80ACB748(this, globalCtx); + } +} + +void EnOwl_WaitDefault(EnOwl* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE && (globalCtx->csCtx.npcActions[7] != NULL)) { + if (this->unk_40A != globalCtx->csCtx.npcActions[7]->action) { + this->actionFlags |= 4; + func_80ACD130(this, globalCtx, 7); + func_80ACBAB8(this, globalCtx); + } else { + this->actor.world.rot.z = globalCtx->csCtx.npcActions[7]->urot.y; + } + } + + if (this->actionFlags & 0x80) { + func_80ACB748(this, globalCtx); + } +} + +void func_80ACBAB8(EnOwl* this, GlobalContext* globalCtx) { + switch (globalCtx->csCtx.npcActions[7]->action - 1) { + case 0: + EnOwl_ChangeMode(this, func_80ACB904, func_80ACC540, &this->skelAnime, &gOwlFlyAnim, 0.0f); + break; + case 1: + this->actor.draw = EnOwl_Draw; + EnOwl_ChangeMode(this, EnOwl_WaitDefault, func_80ACC540, &this->skelAnime, &gOwlPerchAnim, 0.0f); + break; + case 2: + this->actor.draw = EnOwl_Draw; + EnOwl_ChangeMode(this, func_80ACB994, func_80ACC540, &this->skelAnime, &gOwlFlyAnim, 0.0f); + break; + case 3: + this->actor.draw = NULL; + this->actionFunc = EnOwl_WaitDefault; + break; + case 4: + Actor_Kill(&this->actor); + break; + } + + this->unk_40A = globalCtx->csCtx.npcActions[7]->action; +} + +void func_80ACBC0C(EnOwl* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_5; + + if (this->actor.xzDistToPlayer > 6000.0f && !(this->actionFlags & 0x80)) { + Actor_Kill(&this->actor); + } + + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_400, 2, 0x80, 0x40); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->actor.speedXZ < 16.0f) { + this->actor.speedXZ += 0.5f; + } + + if ((this->unk_3F8 + 1000.0f) < this->actor.world.pos.y) { + if (this->actor.velocity.y > 0.0f) { + this->actor.velocity.y -= 0.4f; + } + } else if (this->actor.velocity.y < 4.0f) { + this->actor.velocity.y += 0.2f; + } + + this->actionFlags |= 8; +} + +void func_80ACBD4C(EnOwl* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame > 10.0f) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_400, 2, 0x400, 0x40); + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + if (this->skelAnime.curFrame > 45.0f) { + this->actor.velocity.y = 2.0f; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 8.0f; + } else if (this->skelAnime.curFrame > 17.0f) { + this->actor.velocity.y = 6.0f; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 4.0f; + } + + if (this->actionFlags & 1) { + EnOwl_ChangeMode(this, func_80ACBC0C, func_80ACC460, &this->skelAnime, &gOwlFlyAnim, 0.0f); + this->unk_3FE = 6; + if (this->actionFlags & 0x40) { + this->unk_400 += 0x2000; + } else { + this->unk_400 -= 0x2000; + } + } + + this->actionFlags |= 8; +} + +void func_80ACBEA0(EnOwl* this, GlobalContext* GlobalContext) { + if (this->actionFlags & 1) { + this->unk_3FE = 3; + EnOwl_ChangeMode(this, func_80ACBD4C, func_80ACC540, &this->skelAnime, &gOwlTakeoffAnim, 0.0f); + this->unk_3F8 = this->actor.world.pos.y; + this->actor.velocity.y = 2.0f; + if (this->actionFlags & 0x40) { + this->unk_400 = this->actor.world.rot.y + 0x4000; + } else { + this->unk_400 = this->actor.world.rot.y - 0x4000; + } + } + + this->actionFlags |= 8; +} + +void func_80ACBF50(EnOwl* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_400, 2, 0x384, 0x258); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->actionFlags & 1) { + EnOwl_ChangeMode(this, func_80ACBC0C, func_80ACC460, &this->skelAnime, &gOwlFlyAnim, 0.0f); + this->unk_3FE = 6; + this->actor.velocity.y = 2.0f; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 4.0f; + } + this->actionFlags |= 8; +} + +void func_80ACC00C(EnOwl* this, GlobalContext* globalCtx) { + s32 owlType; + s32 temp_v0; + s32 temp_v0_2; + + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_400, 2, 0x384, 0x258); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->actor.xzDistToPlayer < 50.0f) { + if (!Gameplay_InCsMode(globalCtx)) { + owlType = (this->actor.params & 0xFC0) >> 6; + osSyncPrintf(VT_FGCOL(CYAN)); + osSyncPrintf("%dのフクロウ\n", owlType); // "%d owl" + osSyncPrintf(VT_RST); + switch (owlType) { + case 7: + osSyncPrintf(VT_FGCOL(CYAN)); + osSyncPrintf("SPOT 06 の デモがはしった\n"); // "Demo of SPOT 06 has been completed" + osSyncPrintf(VT_RST); + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gLakeHyliaOwlCs); + this->actor.draw = NULL; + break; + case 8: + case 9: + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gDMTOwlCs); + this->actor.draw = NULL; + break; + default: + ASSERT(0, "0", "../z_en_owl.c", 1693); + break; + } + + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + gSaveContext.cutsceneTrigger = 1; + func_800F44EC(0x14, 0xA); + this->actionFunc = EnOwl_WaitDefault; + this->unk_40A = 0; + this->actionFlags |= 0x80; + gTimeIncrement = 0; + } + } + + if (this->skelAnime.curFrame >= 37.0f) { + if (this->unk_3FE > 0) { + this->skelAnime.curFrame = 21.0f; + this->unk_3FE--; + } else { + this->actionFunc = func_80ACBF50; + } + } + + this->actionFlags |= 8; +} + +void func_80ACC23C(EnOwl* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame < 20.0f) { + this->actor.speedXZ = 1.5f; + } else { + this->actor.speedXZ = 0.0f; + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_400, 2, 0x384, 0x258); + this->actor.shape.rot.y = this->actor.world.rot.y; + } + + if (this->skelAnime.curFrame >= 37.0f) { + this->skelAnime.curFrame = 21.0f; + this->actionFunc = func_80ACC00C; + this->unk_3FE = 5; + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + this->actor.speedXZ = 0.0f; + } + + this->actionFlags |= 8; +} + +void func_80ACC30C(EnOwl* this, GlobalContext* globalCtx) { + if (this->actionFlags & 1) { + this->unk_3FE = 3; + EnOwl_ChangeMode(this, func_80ACC23C, func_80ACC540, &this->skelAnime, &gOwlTakeoffAnim, 0.0f); + this->unk_3F8 = this->actor.world.pos.y; + this->actor.velocity.y = 0.2f; + } + + this->actionFlags |= 8; +} + +void func_80ACC390(EnOwl* this) { + SkelAnime_Update(this->curSkelAnime); + + if (this->unk_3FE > 0) { + this->unk_3FE--; + this->actor.shape.rot.z = Math_SinS(this->unk_3FE * 0x333) * 1000.0f; + } else { + this->unk_410 = func_80ACC460; + this->unk_3FE = 6; + Animation_Change(this->curSkelAnime, &gOwlFlyAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gOwlFlyAnim), 2, 5.0f); + } +} + +void func_80ACC460(EnOwl* this) { + if (SkelAnime_Update(this->curSkelAnime)) { + if (this->unk_3FE > 0) { + this->unk_3FE--; + Animation_Change(this->curSkelAnime, this->curSkelAnime->animation, 1.0f, 0.0f, + Animation_GetLastFrame(this->curSkelAnime->animation), ANIMMODE_ONCE, 0.0f); + } else { + this->unk_3FE = 0xA0; + this->unk_410 = func_80ACC390; + Animation_Change(this->curSkelAnime, &gOwlGlideAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gOwlGlideAnim), 0, + 5.0f); + } + } +} + +void func_80ACC540(EnOwl* this) { + if (SkelAnime_Update(this->curSkelAnime)) { + Animation_Change(this->curSkelAnime, this->curSkelAnime->animation, 1.0f, 0.0f, + Animation_GetLastFrame(this->curSkelAnime->animation), ANIMMODE_ONCE, 0.0f); + this->actionFlags |= 1; + } else { + this->actionFlags &= ~1; + } +} + +s32 func_80ACC5CC(EnOwl* this) { + s32 phi_v1 = (this->actionFlags & 2) ? 0x20 : 0; + + if (phi_v1 == (this->unk_3EE & 0x3F)) { + return true; + } else { + if (this->actionFlags & 0x20) { + this->unk_3EE += 4; + } else { + this->unk_3EE -= 4; + } + + return false; + } +} + +s32 func_80ACC624(EnOwl* this, GlobalContext* globalCtx) { + s32 switchFlag = (this->actor.params & 0xFC0) >> 6; + + if (globalCtx->sceneNum != SCENE_SPOT11) { + return true; + } else if (switchFlag == 0xA) { + return true; + } else if (globalCtx->csCtx.frames >= 300 && globalCtx->csCtx.frames <= 430) { + return true; + } else if (globalCtx->csCtx.frames >= 1080 && globalCtx->csCtx.frames <= 1170) { + return true; + } else { + return false; + } +} + +void EnOwl_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnOwl* this = (EnOwl*)thisx; + s16 phi_a1; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 10.0f, 5); + this->unk_410(this); + this->actionFlags &= ~8; + this->actionFunc(this, globalCtx); + if (this->actor.update == NULL) { + // "Owl disappears" + osSyncPrintf("フクロウ消滅!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + return; + } + + if (!(this->actionFlags & 0x80) && func_80ACC624(this, globalCtx)) { + if ((this->skelAnime.animation == &gOwlTakeoffAnim && + (this->skelAnime.curFrame == 2.0f || this->skelAnime.curFrame == 9.0f || + this->skelAnime.curFrame == 23.0f || this->skelAnime.curFrame == 40.0f || + this->skelAnime.curFrame == 58.0f)) || + (this->skelAnime.animation == &gOwlFlyAnim && this->skelAnime.curFrame == 4.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OWL_FLUTTER); + } + } + + if (this->actor.draw != NULL) { + Actor_MoveForward(&this->actor); + } + + if (this->actionFlags & 2) { + this->eyeTexIndex = 2; + } else { + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + + this->eyeTexIndex = this->blinkTimer; + + if (this->eyeTexIndex >= 3) { + this->eyeTexIndex = 0; + } + } + + if (!(this->actionFlags & 8)) { + phi_a1 = 0; + if (this->actionFlags & 0x10) { + switch (this->unk_404) { + case 0: + this->unk_404 = 1; + this->unk_405 = 6; + break; + case 1: + this->unk_405--; + + if (this->unk_405 != 0) { + phi_a1 = Math_CosS(this->unk_405 * 8192) * 4096.0f; + } else { + if (this->actionFlags & 2) { + this->unk_3EE = 0; + } else { + this->unk_3EE = 0x20; + } + + if (this->actionFlags & 0x20) { + this->unk_3EE -= 4; + } else { + this->unk_3EE += 4; + } + this->unk_404++; + } + + if (this->actionFlags & 0x20) { + phi_a1 = -phi_a1; + } + break; + case 2: + if (func_80ACC5CC(this)) { + this->actionFlags &= ~0x10; + this->unk_406 = (s32)Rand_ZeroFloat(20.0f) + 0x3C; + this->unk_404 = 0; + func_80ACA6C0(this); + } + break; + default: + break; + } + } else { + if (this->unk_406 > 0) { + this->unk_406--; + } else { + if (this->unk_404 == 0) { + if (Rand_ZeroOne() < 0.3f) { + this->unk_404 = 4; + this->unk_405 = 0xC; + } else { + this->unk_404 = 1; + this->unk_405 = 4; + } + } + + this->unk_405--; + + switch (this->unk_404) { + case 1: + phi_a1 = Math_SinS((-this->unk_405 * 4096) + 0x4000) * 5000.0f; + if (this->unk_405 <= 0) { + this->unk_405 = (s32)(Rand_ZeroFloat(15.0f) + 5.0f); + this->unk_404 = 2; + } + break; + case 2: + phi_a1 = 0x1388; + if (this->unk_405 <= 0) { + this->unk_404 = 3; + this->unk_405 = 4; + } + break; + case 3: + phi_a1 = Math_SinS(this->unk_405 * 4096) * 5000.0f; + if (this->unk_405 <= 0) { + this->unk_406 = (s32)Rand_ZeroFloat(20.0f) + 0x3C; + this->unk_404 = 0; + func_80ACA6C0(this); + } + break; + case 4: + phi_a1 = Math_SinS(this->unk_405 * 8192) * 5000.0f; + if (this->unk_405 <= 0) { + this->unk_406 = (s32)Rand_ZeroFloat(20.0f) + 0x3C; + this->unk_404 = 0; + func_80ACA6C0(this); + } + break; + default: + break; + } + + if (this->actionFlags & 0x20) { + phi_a1 = -phi_a1; + } + } + + if (this->unk_409 > 0) { + this->unk_409--; + } else { + this->unk_408--; + switch (this->unk_407) { + case 0: + this->unk_3F2 = (-this->unk_408 * 0x5DC) + 0x1770; + if (this->unk_408 <= 0) { + this->unk_407 = 1; + this->unk_408 = (s32)(Rand_ZeroFloat(15.0f) + 5.0f); + } + break; + case 1: + this->unk_3F2 = 0x1770; + if (this->unk_408 <= 0) { + this->unk_407 = 2; + this->unk_408 = 4; + } + break; + case 2: + this->unk_3F2 = this->unk_408 * 0x5DC; + if (this->unk_408 <= 0) { + this->unk_407 = 0; + this->unk_408 = 4; + this->unk_409 = (s32)Rand_ZeroFloat(40.0f) + 0xA0; + } + break; + default: + break; + } + } + } + if (phi_a1) {} + this->unk_3F0 = (u16)((this->unk_3EE << 2) << 8) + phi_a1; + this->unk_3EC = ABS(this->unk_3F0) >> 3; + } else { + this->unk_3F2 = 0; + if (this->actionFlags & 2) { + this->unk_3F0 = -0x8000; + } else { + this->unk_3F0 = 0; + } + + this->unk_3EC = ABS(this->unk_3F0) >> 3; + } +} + +s32 EnOwl_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** gfx, Vec3f* pos, Vec3s* rot, void* thisx) { + EnOwl* this = (EnOwl*)thisx; + + switch (limbIndex) { + case 3: + rot->x += this->unk_3F0; + rot->z += this->unk_3EC; + rot->z -= this->unk_3F2; + break; + case 2: + rot->z += this->unk_3F2; + break; + case 4: + if (!(this->actionFlags & 8)) { + rot->y -= (s16)(this->unk_3EC * 1.5f); + } + break; + case 5: + if (!(this->actionFlags & 8)) { + rot->y += (s16)(this->unk_3EC * 1.5f); + } + break; + default: + break; + } + return false; +} + +void EnOwl_PostLimbUpdate(GlobalContext* globalCtx, s32 limbIndex, Gfx** gfx, Vec3s* rot, void* thisx) { + EnOwl* this = (EnOwl*)thisx; + Vec3f vec; + + vec.z = 0.0f; + if (this->actionFlags & 2) { + vec.x = 700.0f; + vec.y = 400.0f; + } else { + vec.y = 0.0f; + vec.x = 1400.0f; + } + if (limbIndex == 3) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } +} + +void EnOwl_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { gObjOwlEyeOpenTex, gObjOwlEyeHalfTex, gObjOwlEyeClosedTex }; + EnOwl* this = (EnOwl*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_owl.c", 2247); + + func_800943C8(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 8, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexIndex])); + SkelAnime_DrawFlexOpa(globalCtx, this->curSkelAnime->skeleton, this->curSkelAnime->jointTable, + this->curSkelAnime->dListCount, EnOwl_OverrideLimbDraw, EnOwl_PostLimbUpdate, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_owl.c", 2264); +} + +void EnOwl_ChangeMode(EnOwl* this, EnOwlActionFunc actionFunc, OwlFunc arg2, SkelAnime* skelAnime, + AnimationHeader* animation, f32 morphFrames) { + this->curSkelAnime = skelAnime; + Animation_Change(this->curSkelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, + morphFrames); + this->actionFunc = actionFunc; + this->unk_410 = arg2; +} + +void func_80ACD130(EnOwl* this, GlobalContext* globalCtx, s32 idx) { + Vec3f startPos; + + startPos.x = globalCtx->csCtx.npcActions[idx]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[idx]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[idx]->startPos.z; + this->actor.world.pos = startPos; + this->actor.world.rot.y = this->actor.shape.rot.y = globalCtx->csCtx.npcActions[idx]->rot.y; + this->actor.shape.rot.z = globalCtx->csCtx.npcActions[idx]->urot.z; +} + +f32 func_80ACD1C4(GlobalContext* globalCtx, s32 idx) { + f32 ret = Environment_LerpWeight(globalCtx->csCtx.npcActions[idx]->endFrame, + globalCtx->csCtx.npcActions[idx]->startFrame, globalCtx->csCtx.frames); + + ret = CLAMP_MAX(ret, 1.0f); + return ret; +} + +void func_80ACD220(EnOwl* this, Vec3f* arg1, f32 arg2) { + Vec3f rpy; + + rpy.x = (arg1->x - this->actor.world.pos.x) * arg2; + rpy.y = (arg1->y - this->actor.world.pos.y) * arg2; + rpy.z = (arg1->z - this->actor.world.pos.z) * arg2; + + Math_StepToF(&this->actor.velocity.y, rpy.y, 1.0f); + this->actor.speedXZ = sqrtf(SQ(rpy.x) + SQ(rpy.z)); + this->actor.world.rot.y = Math_Vec3f_Yaw(&this->actor.world.pos, arg1); + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void func_80ACD2CC(EnOwl* this, GlobalContext* globalCtx) { + Vec3f pos; + s32 angle; + f32 t = func_80ACD1C4(globalCtx, 7); + + pos.x = globalCtx->csCtx.npcActions[7]->startPos.x; + pos.y = globalCtx->csCtx.npcActions[7]->startPos.y; + pos.z = globalCtx->csCtx.npcActions[7]->startPos.z; + angle = (s16)globalCtx->csCtx.npcActions[7]->rot.y - this->actor.world.rot.z; + if (angle < 0) { + angle += 0x10000; + } + angle = (s16)((t * angle) + this->actor.world.rot.z); + angle = (u16)angle; + if (this->actionFlags & 4) { + f32 phi_f2 = globalCtx->csCtx.npcActions[7]->urot.x; + + phi_f2 *= 10.0f * (360.0f / 0x10000); + if (phi_f2 < 0.0f) { + phi_f2 += 360.0f; + } + pos.x -= Math_SinS(angle) * phi_f2; + pos.z += Math_CosS(angle) * phi_f2; + this->unk_3F8 = phi_f2; + this->actor.world.pos = pos; + this->actor.draw = EnOwl_Draw; + this->actionFlags &= ~4; + this->actor.speedXZ = 0.0f; + } else { + pos.x -= Math_SinS(angle) * this->unk_3F8; + pos.z += Math_CosS(angle) * this->unk_3F8; + func_80ACD220(this, &pos, 1.0f); + } +} + +void func_80ACD4D4(EnOwl* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f endPosf; + f32 temp_ret = func_80ACD1C4(globalCtx, 7); + + pos.x = globalCtx->csCtx.npcActions[7]->startPos.x; + pos.y = globalCtx->csCtx.npcActions[7]->startPos.y; + pos.z = globalCtx->csCtx.npcActions[7]->startPos.z; + endPosf.x = globalCtx->csCtx.npcActions[7]->endPos.x; + endPosf.y = globalCtx->csCtx.npcActions[7]->endPos.y; + endPosf.z = globalCtx->csCtx.npcActions[7]->endPos.z; + pos.x = (endPosf.x - pos.x) * temp_ret + pos.x; + pos.y = (endPosf.y - pos.y) * temp_ret + pos.y; + pos.z = (endPosf.z - pos.z) * temp_ret + pos.z; + func_80ACD220(this, &pos, 1.0f); +} diff --git a/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.h b/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.h new file mode 100644 index 000000000..d83e52819 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Owl/z_en_owl.h @@ -0,0 +1,46 @@ +#ifndef Z_EN_OWL_H +#define Z_EN_OWL_H + +#include "ultra64.h" +#include "global.h" + +struct EnOwl; + +typedef void (*EnOwlActionFunc)(struct EnOwl*, GlobalContext*); +typedef void (*OwlFunc)(struct EnOwl*); + +typedef struct EnOwl { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[21]; + /* 0x025A */ Vec3s morphTable[21]; + /* 0x02D8 */ SkelAnime skelAnime2; + /* 0x031C */ Vec3s jointTable2[16]; + /* 0x037C */ Vec3s morphTable2[16]; + /* 0x03DC */ SkelAnime* curSkelAnime; + /* 0x03E0 */ Vec3f eye; + /* 0x03EC */ s16 unk_3EC; + /* 0x03EE */ s16 unk_3EE; + /* 0x03F0 */ s16 unk_3F0; + /* 0x03F2 */ s16 unk_3F2; + /* 0x03F4 */ s16 eyeTexIndex; + /* 0x03F6 */ s16 blinkTimer; + /* 0x03F8 */ f32 unk_3F8; + /* 0x03FC */ u16 actionFlags; + /* 0x03FE */ u16 unk_3FE; + /* 0x0400 */ s16 unk_400; + /* 0x0402 */ s16 cameraIdx; + /* 0x0404 */ u8 unk_404; + /* 0x0405 */ u8 unk_405; + /* 0x0406 */ u8 unk_406; + /* 0x0407 */ u8 unk_407; + /* 0x0408 */ u8 unk_408; + /* 0x0409 */ u8 unk_409; + /* 0x040A */ u8 unk_40A; + /* 0x040B */ u8 unk_40B; + /* 0x040C */ EnOwlActionFunc actionFunc; + /* 0x0410 */ OwlFunc unk_410; +} EnOwl; // size = 0x0414 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Part/z_en_part.c b/soh/src/overlays/actors/ovl_En_Part/z_en_part.c new file mode 100644 index 000000000..ec4079887 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Part/z_en_part.c @@ -0,0 +1,317 @@ +/* + * File: z_en_part.c + * Overlay: ovl_En_Part + * Description: Effect spawner for enemies' death + */ + +#include "z_en_part.h" +#include "objects/object_tite/object_tite.h" +#include "objects/object_ik/object_ik.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnPart_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPart_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPart_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPart_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Part_InitVars = { + ACTOR_EN_PART, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnPart), + (ActorFunc)EnPart_Init, + (ActorFunc)EnPart_Destroy, + (ActorFunc)EnPart_Update, + (ActorFunc)EnPart_Draw, + NULL, +}; + +void EnPart_Init(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnPart_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_80ACDDE8(EnPart* this, GlobalContext* globalCtx) { + f32 sign = 1.0f; + + this->action = 1; + this->actor.world.rot.y = Rand_ZeroOne() * 20000.0f; + + switch (this->actor.params) { + case 0: + this->actor.velocity.y = 0.0f; + this->actor.gravity = -0.3f - Rand_ZeroOne() * 0.5f; + this->rotZSpeed = 0.3f; + this->timer = 25; + this->actor.speedXZ = (Rand_ZeroOne() - 0.5f) * 2.0f; + break; + case 13: + this->timer = 400; + case 12: + this->actor.speedXZ = Rand_CenteredFloat(6.0f); + this->actor.home.pos = this->actor.world.pos; + this->timer += 60; + this->actor.velocity.y = Rand_ZeroOne() * 5.0f + 4.0f; + this->actor.gravity = -0.6f - Rand_ZeroOne() * 0.5f; + this->rotZSpeed = 0.15f; + break; + case 14: + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &this->actor.world.pos, 40, 0x8001, 0, -1); + case 1: + case 4: + case 9: + case 10: + this->timer += (s16)(Rand_ZeroOne() * 17.0f) + 5; + case 2: + this->actor.velocity.y = Rand_ZeroOne() * 5.0f + 4.0f; + this->actor.gravity = -0.6f - Rand_ZeroOne() * 0.5f; + this->rotZSpeed = 0.15f; + break; + case 11: + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &this->actor.world.pos, 40, 0x8001, 0, -1); + case 3: + this->actor.speedXZ = (Rand_ZeroOne() - 0.5f) * 3.0f; + this->timer = (s16)(Rand_ZeroOne() * 17.0f) + 10; + this->actor.velocity.y = Rand_ZeroOne() * 3.0f + 8.0f; + this->actor.gravity = -0.6f - Rand_ZeroOne() * 0.3f; + this->rotZSpeed = 0.15f; + break; + case 5: + case 6: + case 7: + case 8: + this->actor.world.rot.y = this->actor.parent->shape.rot.y; + if (this->displayList == object_ik_DL_015380) { + sign = -1.0f; + } + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 6.0f * sign; + this->actor.gravity = -1.2f; + this->rotZSpeed = 0.15f * sign; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + this->timer = 18; + break; + } +} + +void func_80ACE13C(EnPart* this, GlobalContext* globalCtx) { + s32 i; + Vec3f pos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if ((this->actor.params == 12) || (this->actor.params == 13)) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 15.0f, 0.0f, 0x1D); + + if ((this->actor.bgCheckFlags & 1) || (this->actor.world.pos.y <= this->actor.floorHeight)) { + this->action = 4; + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + } + + if ((this->actor.params == 13) && (this->actor.parent != NULL) && (this->actor.parent->update == NULL)) { + this->actor.parent = NULL; + } + } else if (this->timer <= 0) { + switch (this->actor.params) { + case 1: + case 9: + case 10: + case 14: + EffectSsDeadDb_Spawn(globalCtx, &this->actor.world.pos, &zeroVec, &zeroVec, + (s16)(this->actor.scale.y * 100.0f) * 40, 7, 255, 255, 255, 255, 0, 255, 0, 1, 9, + true); + break; + case 3: + case 11: + EffectSsDeadDb_Spawn(globalCtx, &this->actor.world.pos, &zeroVec, &zeroVec, + (s16)(this->actor.scale.y * 100.0f) * 40, 7, 255, 255, 255, 255, 0, 0, 255, 1, 9, + true); + break; + case 4: + for (i = 7; i >= 0; i--) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(60.0f); + pos.y = this->actor.world.pos.y + this->actor.shape.yOffset * this->actor.scale.y + + Rand_CenteredFloat(50.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(60.0f); + velocity.y = Rand_ZeroOne() + 1.0f; + EffectSsDtBubble_SpawnColorProfile(globalCtx, &pos, &velocity, &accel, Rand_S16Offset(80, 100), 25, + 0, true); + } + break; + case 5: + case 6: + case 7: + case 8: + for (i = 4; i >= 0; i--) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(25.0f); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(40.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(25.0f); + EffectSsDeadDb_Spawn(globalCtx, &pos, &zeroVec, &zeroVec, 40, 7, 255, 255, 255, 255, 0, 0, 255, 1, + 9, true); + } + break; + } + + Actor_Kill(&this->actor); + return; + } + + this->timer--; + this->rotZ += this->rotZSpeed; +} + +void func_80ACE5B8(EnPart* this, GlobalContext* globalCtx) { + this->action = 3; +} + +void func_80ACE5C8(EnPart* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->timer--; + if (this->timer == 0) { + Actor_Kill(&this->actor); + } else { + Vec3f velocity = { 0.0f, 8.0f, 0.0f }; + Vec3f accel = { 0.0f, -1.5f, 0.0f }; + + if (sqrt(this->actor.xyzDistToPlayerSq) <= 40.0f) { + u8 prevInvincibilityTimer = player->invincibilityTimer; + + if (player->invincibilityTimer <= 0) { + if (player->invincibilityTimer <= -40) { + player->invincibilityTimer = 0; + } else { + player->invincibilityTimer = 0; + globalCtx->damagePlayer(globalCtx, -8); + } + } + func_8002F71C(globalCtx, this->actor.parent, (650.0f - this->actor.parent->xzDistToPlayer) * 0.04f + 4.0f, + this->actor.parent->world.rot.y, 8.0f); + player->invincibilityTimer = prevInvincibilityTimer; + this->timer = 1; + } + + func_80033480(globalCtx, &this->actor.world.pos, 0.0f, 1, 300, 150, 1); + velocity.x = Rand_CenteredFloat(16.0f); + EffectSsHahen_Spawn(globalCtx, &this->actor.world.pos, &velocity, &accel, 20, + (s32)((Rand_ZeroOne() * 5.0f + 12.0f) * 2), -1, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MONBLIN_GNDWAVE - SFX_FLAG); + } +} + +void func_80ACE7E8(EnPart* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if ((this->actor.parent == NULL) || (this->actor.parent->update == NULL)) { + EffectSsDeadDb_Spawn(globalCtx, &this->actor.world.pos, &zeroVec, &zeroVec, + (s16)(this->actor.scale.y * 100.0f) * 40, 7, 255, 255, 255, 255, 0, 255, 0, 1, 9, true); + Actor_Kill(&this->actor); + return; + } + + if (this->timer == 0) { + f32 diffsSum = Math_SmoothStepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 1.0f, 5.0f, 0.0f); + + diffsSum += Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 1.0f, 5.0f, 0.0f); + diffsSum += Math_SmoothStepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 1.0f, 5.0f, 0.0f); + diffsSum += Math_SmoothStepToF(&this->rotZ, 0.0f, 1.0f, 0.25f, 0.0f); + if (diffsSum == 0.0f) { + this->actor.parent->home.rot.x--; + this->timer--; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_DAMAGE); + } + } else if (this->timer > 0) { + this->timer--; + } + + if (this->actor.parent->colChkInfo.health != 0) { + Actor_Kill(&this->actor); + } +} + +void EnPart_Update(Actor* thisx, GlobalContext* globalCtx) { + static EnPartActionFunc sActionFuncs[] = { + func_80ACDDE8, func_80ACE13C, func_80ACE5B8, func_80ACE5C8, func_80ACE7E8, + }; + + EnPart* this = (EnPart*)thisx; + + Actor_MoveForward(&this->actor); + + if ((this->actor.params > 4 && this->actor.params < 9) || this->actor.params < 0) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 15.0f, 0.0f, 5); + if (this->actor.params >= 0) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + if (thisx->bgCheckFlags & 1) { + thisx->bgCheckFlags &= ~1; + thisx->velocity.y = 6.0f; + } + } + } + + sActionFuncs[this->action](this, globalCtx); +} + +Gfx* func_80ACEAC0(GraphicsContext* gfxCtx, u8 primR, u8 primG, u8 primB, u8 envR, u8 envG, u8 envB) { + Gfx* dList; + Gfx* dListHead; + + dList = Graph_Alloc(gfxCtx, 4 * sizeof(Gfx)); + dListHead = dList; + + gDPPipeSync(dListHead++); + gDPSetPrimColor(dListHead++, 0, 0, primR, primG, primB, 255); + gDPSetEnvColor(dListHead++, envR, envG, envB, 255); + gSPEndDisplayList(dListHead++); + + return dList; +} + +void EnPart_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnPart* this = (EnPart*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_part.c", 647); + + if (thisx->params > 0) { + Matrix_RotateZ(this->rotZ, MTXMODE_APPLY); + } + + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(thisx, globalCtx, 0); + + if (thisx->params == 5) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80ACEAC0(globalCtx->state.gfxCtx, 245, 255, 205, 30, 35, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80ACEAC0(globalCtx->state.gfxCtx, 185, 135, 25, 20, 20, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80ACEAC0(globalCtx->state.gfxCtx, 255, 255, 255, 30, 40, 20)); + } else if (thisx->params == 6) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80ACEAC0(globalCtx->state.gfxCtx, 55, 65, 55, 0, 0, 0)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80ACEAC0(globalCtx->state.gfxCtx, 205, 165, 75, 25, 20, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80ACEAC0(globalCtx->state.gfxCtx, 205, 165, 75, 25, 20, 0)); + } else if (thisx->params == 7) { + gSPSegment(POLY_OPA_DISP++, 0x08, func_80ACEAC0(globalCtx->state.gfxCtx, 255, 255, 255, 180, 180, 180)); + gSPSegment(POLY_OPA_DISP++, 0x09, func_80ACEAC0(globalCtx->state.gfxCtx, 225, 205, 115, 25, 20, 0)); + gSPSegment(POLY_OPA_DISP++, 0x0A, func_80ACEAC0(globalCtx->state.gfxCtx, 225, 205, 115, 25, 20, 0)); + } else if ((thisx->params == 9) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) { + gSPSegment(POLY_OPA_DISP++, 0x08, ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(object_tite_Tex_001300))); + gSPSegment(POLY_OPA_DISP++, 0x09, ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(object_tite_Tex_001700))); + gSPSegment(POLY_OPA_DISP++, 0x0A, ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(object_tite_Tex_001900))); + } else if ((thisx->params == 10) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) { + gSPSegment(POLY_OPA_DISP++, 0x08, ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(object_tite_Tex_001B00))); + gSPSegment(POLY_OPA_DISP++, 0x09, ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(object_tite_Tex_001F00))); + gSPSegment(POLY_OPA_DISP++, 0x0A, ResourceMgr_LoadTexByName(SEGMENTED_TO_VIRTUAL(object_tite_Tex_002100))); + } + + if (this->displayList != NULL) { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_part.c", 696), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, this->displayList); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_part.c", 700); +} diff --git a/soh/src/overlays/actors/ovl_En_Part/z_en_part.h b/soh/src/overlays/actors/ovl_En_Part/z_en_part.h new file mode 100644 index 000000000..8beb516a3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Part/z_en_part.h @@ -0,0 +1,20 @@ +#ifndef Z_EN_PART_H +#define Z_EN_PART_H + +#include "ultra64.h" +#include "global.h" + +struct EnPart; + +typedef void (*EnPartActionFunc)(struct EnPart*, GlobalContext*); + +typedef struct EnPart { + /* 0x000 */ Actor actor; + /* 0x14C */ u8 action; + /* 0x14E */ s16 timer; + /* 0x150 */ Gfx* displayList; + /* 0x154 */ f32 rotZ; + /* 0x158 */ f32 rotZSpeed; +} EnPart; // size = 0x015C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c new file mode 100644 index 000000000..b4d8cb23a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -0,0 +1,1084 @@ +#include "z_en_peehat.h" +#include "objects/object_peehat/object_peehat.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_24) + +#define GROUND_HOVER_HEIGHT 75.0f +#define MAX_LARVA 3 + +void EnPeehat_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPeehat_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPeehat_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPeehat_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnPeehat_Ground_SetStateGround(EnPeehat* this); +void EnPeehat_Flying_SetStateGround(EnPeehat* this); +void EnPeehat_Larva_SetStateSeekPlayer(EnPeehat* this); +void EnPeehat_Ground_StateGround(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Ground_SetStateRise(EnPeehat* this); +void EnPeehat_Flying_StateGrounded(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Flying_SetStateRise(EnPeehat* this); +void EnPeehat_Flying_StateFly(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Flying_SetStateLanding(EnPeehat* this); +void EnPeehat_Ground_StateRise(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Ground_SetStateHover(EnPeehat* this); +void EnPeehat_Flying_StateRise(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Ground_StateSeekPlayer(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Ground_SetStateReturnHome(EnPeehat* this); +void EnPeehat_Ground_SetStateLanding(EnPeehat* this); +void EnPeehat_Larva_StateSeekPlayer(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_SetStateAttackRecoil(EnPeehat* this); +void EnPeehat_Ground_StateLanding(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Flying_StateLanding(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Ground_StateHover(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Ground_StateReturnHome(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_StateAttackRecoil(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_StateBoomerangStunned(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_Adult_StateDie(EnPeehat* this, GlobalContext* globalCtx); +void EnPeehat_SetStateExplode(EnPeehat* this); +void EnPeehat_StateExplode(EnPeehat* this, GlobalContext* globalCtx); + +const ActorInit En_Peehat_InitVars = { + ACTOR_EN_PEEHAT, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_PEEHAT, + sizeof(EnPeehat), + (ActorFunc)EnPeehat_Init, + (ActorFunc)EnPeehat_Destroy, + (ActorFunc)EnPeehat_Update, + (ActorFunc)EnPeehat_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_WOOD, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 50, 160, -70, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElemInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElemInit, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +typedef enum { + /* 00 */ PEAHAT_DMG_EFF_ATTACK = 0, + /* 06 */ PEAHAT_DMG_EFF_LIGHT_ICE_ARROW = 6, + /* 12 */ PEAHAT_DMG_EFF_FIRE = 12, + /* 13 */ PEAHAT_DMG_EFF_HOOKSHOT = 13, + /* 14 */ PEAHAT_DMG_EFF_BOOMERANG = 14, + /* 15 */ PEAHAT_DMG_EFF_NUT = 15 +} DamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, PEAHAT_DMG_EFF_NUT), + /* Deku stick */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Slingshot */ DMG_ENTRY(1, PEAHAT_DMG_EFF_ATTACK), + /* Explosive */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Boomerang */ DMG_ENTRY(0, PEAHAT_DMG_EFF_BOOMERANG), + /* Normal arrow */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Hammer swing */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Hookshot */ DMG_ENTRY(2, PEAHAT_DMG_EFF_HOOKSHOT), + /* Kokiri sword */ DMG_ENTRY(1, PEAHAT_DMG_EFF_ATTACK), + /* Master sword */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Giant's Knife */ DMG_ENTRY(4, PEAHAT_DMG_EFF_ATTACK), + /* Fire arrow */ DMG_ENTRY(4, PEAHAT_DMG_EFF_FIRE), + /* Ice arrow */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Light arrow */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Unk arrow 1 */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Unk arrow 2 */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Unk arrow 3 */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Fire magic */ DMG_ENTRY(3, PEAHAT_DMG_EFF_FIRE), + /* Ice magic */ DMG_ENTRY(0, PEAHAT_DMG_EFF_LIGHT_ICE_ARROW), + /* Light magic */ DMG_ENTRY(0, PEAHAT_DMG_EFF_LIGHT_ICE_ARROW), + /* Shield */ DMG_ENTRY(0, PEAHAT_DMG_EFF_ATTACK), + /* Mirror Ray */ DMG_ENTRY(0, PEAHAT_DMG_EFF_ATTACK), + /* Kokiri spin */ DMG_ENTRY(1, PEAHAT_DMG_EFF_ATTACK), + /* Giant spin */ DMG_ENTRY(4, PEAHAT_DMG_EFF_ATTACK), + /* Master spin */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Kokiri jump */ DMG_ENTRY(2, PEAHAT_DMG_EFF_ATTACK), + /* Giant jump */ DMG_ENTRY(8, PEAHAT_DMG_EFF_ATTACK), + /* Master jump */ DMG_ENTRY(4, PEAHAT_DMG_EFF_ATTACK), + /* Unknown 1 */ DMG_ENTRY(0, PEAHAT_DMG_EFF_ATTACK), + /* Unblockable */ DMG_ENTRY(0, PEAHAT_DMG_EFF_ATTACK), + /* Hammer jump */ DMG_ENTRY(4, PEAHAT_DMG_EFF_ATTACK), + /* Unknown 2 */ DMG_ENTRY(0, PEAHAT_DMG_EFF_ATTACK), +}; + +typedef enum { + /* 00 */ PEAHAT_STATE_DYING, + /* 01 */ PEAHAT_STATE_EXPLODE, + /* 03 */ PEAHAT_STATE_3 = 3, + /* 04 */ PEAHAT_STATE_4, + /* 05 */ PEAHAT_STATE_FLY, + /* 07 */ PEAHAT_STATE_ATTACK_RECOIL = 7, + /* 08 */ PEAHAT_STATE_8, + /* 09 */ PEAHAT_STATE_9, + /* 10 */ PEAHAT_STATE_LANDING, + /* 12 */ PEAHAT_STATE_RETURN_HOME = 12, + /* 13 */ PEAHAT_STATE_STUNNED, + /* 14 */ PEAHAT_STATE_SEEK_PLAYER, + /* 15 */ PEAHAT_STATE_15 +} PeahatState; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 700, ICHAIN_STOP), +}; + +void EnPeehat_SetupAction(EnPeehat* this, EnPeehatActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnPeehat_Init(Actor* thisx, GlobalContext* globalCtx) { + EnPeehat* this = (EnPeehat*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, 36.0f * 0.001f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gPeehatSkel, &gPeehatRisingAnim, this->jointTable, this->morphTable, + 24); + ActorShape_Init(&this->actor.shape, 100.0f, ActorShadow_DrawCircle, 27.0f); + this->actor.focus.pos = this->actor.world.pos; + this->unk2D4 = 0; + this->actor.world.rot.y = 0; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.colChkInfo.health = 6; + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.floorHeight = this->actor.world.pos.y; + Collider_InitCylinder(globalCtx, &this->colCylinder); + Collider_SetCylinder(globalCtx, &this->colCylinder, &this->actor, &sCylinderInit); + Collider_InitQuad(globalCtx, &this->colQuad); + Collider_SetQuad(globalCtx, &this->colQuad, &this->actor, &sQuadInit); + Collider_InitJntSph(globalCtx, &this->colJntSph); + Collider_SetJntSph(globalCtx, &this->colJntSph, &this->actor, &sJntSphInit, this->colJntSphItemList); + + this->actor.naviEnemyId = 0x48; + this->xzDistToRise = 740.0f; + this->xzDistMax = 1200.0f; + this->actor.uncullZoneForward = 4000.0f; + this->actor.uncullZoneScale = 800.0f; + this->actor.uncullZoneDownward = 1800.0f; + switch (this->actor.params) { + case PEAHAT_TYPE_GROUNDED: + EnPeehat_Ground_SetStateGround(this); + break; + case PEAHAT_TYPE_FLYING: + this->actor.uncullZoneForward = 4200.0f; + this->xzDistToRise = 2800.0f; + this->xzDistMax = 1400.0f; + EnPeehat_Flying_SetStateGround(this); + this->actor.flags &= ~ACTOR_FLAG_0; + break; + case PEAHAT_TYPE_LARVA: + this->actor.scale.x = this->actor.scale.z = 0.006f; + this->actor.scale.y = 0.003f; + this->colCylinder.dim.radius = 25; + this->colCylinder.dim.height = 15; + this->colCylinder.dim.yShift = -5; + this->colCylinder.info.bumper.dmgFlags = 0x1F824; + this->colQuad.base.atFlags = AT_ON | AT_TYPE_ENEMY; + this->colQuad.base.acFlags = AC_ON | AC_TYPE_PLAYER; + this->actor.naviEnemyId = 0x49; // Larva + EnPeehat_Larva_SetStateSeekPlayer(this); + break; + } +} + +void EnPeehat_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPeehat* this = (EnPeehat*)thisx; + EnPeehat* parent; + + Collider_DestroyCylinder(globalCtx, &this->colCylinder); + Collider_DestroyJntSph(globalCtx, &this->colJntSph); + + // If PEAHAT_TYPE_LARVA, decrement total larva spawned + if (this->actor.params > 0) { + parent = (EnPeehat*)this->actor.parent; + if (parent != NULL && parent->actor.update != NULL) { + parent->unk2FA--; + } + } +} + +void EnPeehat_SpawnDust(GlobalContext* globalCtx, EnPeehat* this, Vec3f* pos, f32 arg3, s32 arg4, f32 arg5, f32 arg6) { + Vec3f dustPos; + Vec3f dustVel = { 0.0f, 8.0f, 0.0f }; + Vec3f dustAccel = { 0.0f, -1.5f, 0.0f }; + f32 rot; // radians + s32 pScale; + + rot = (Rand_ZeroOne() - 0.5f) * 6.28f; + dustPos.y = this->actor.floorHeight; + dustPos.x = Math_SinF(rot) * arg3 + pos->x; + dustPos.z = Math_CosF(rot) * arg3 + pos->z; + dustAccel.x = (Rand_ZeroOne() - 0.5f) * arg5; + dustAccel.z = (Rand_ZeroOne() - 0.5f) * arg5; + dustVel.y += (Rand_ZeroOne() - 0.5f) * 4.0f; + pScale = (Rand_ZeroOne() * 5 + 12) * arg6; + EffectSsHahen_Spawn(globalCtx, &dustPos, &dustVel, &dustAccel, arg4, pScale, HAHEN_OBJECT_DEFAULT, 10, NULL); +} + +/** + * Handles being hit when on the ground + */ +void EnPeehat_HitWhenGrounded(EnPeehat* this, GlobalContext* globalCtx) { + this->colCylinder.base.acFlags &= ~AC_HIT; + if ((globalCtx->gameplayFrames & 0xF) == 0) { + Vec3f itemDropPos = this->actor.world.pos; + + itemDropPos.y += 70.0f; + Item_DropCollectibleRandom(globalCtx, &this->actor, &itemDropPos, 0x40); + Item_DropCollectibleRandom(globalCtx, &this->actor, &itemDropPos, 0x40); + Item_DropCollectibleRandom(globalCtx, &this->actor, &itemDropPos, 0x40); + this->unk2D4 = 240; + } else { + s32 i; + + this->colCylinder.base.acFlags &= ~AC_HIT; + for (i = MAX_LARVA - this->unk2FA; i > 0; i--) { + Actor* larva = + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_PEEHAT, + Rand_CenteredFloat(25.0f) + this->actor.world.pos.x, + Rand_CenteredFloat(25.0f) + (this->actor.world.pos.y + 50.0f), + Rand_CenteredFloat(25.0f) + this->actor.world.pos.z, 0, 0, 0, PEAHAT_TYPE_LARVA); + + if (larva != NULL) { + larva->velocity.y = 6.0f; + larva->shape.rot.y = larva->world.rot.y = Rand_CenteredFloat(0xFFFF); + this->unk2FA++; + } + } + this->unk2D4 = 8; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_DAMAGE); +} + +void EnPeehat_Ground_SetStateGround(EnPeehat* this) { + Animation_Change(&this->skelAnime, &gPeehatRisingAnim, 0.0f, 3.0f, Animation_GetLastFrame(&gPeehatRisingAnim), + ANIMMODE_ONCE, 0.0f); + this->seekPlayerTimer = 600; + this->unk2D4 = 0; + this->unk2FA = 0; + this->state = PEAHAT_STATE_3; + this->colCylinder.base.acFlags &= ~AC_HIT; + EnPeehat_SetupAction(this, EnPeehat_Ground_StateGround); +} + +void EnPeehat_Ground_StateGround(EnPeehat* this, GlobalContext* globalCtx) { + if (IS_DAY) { + this->actor.flags |= ACTOR_FLAG_0; + if (this->riseDelayTimer == 0) { + if (this->actor.xzDistToPlayer < this->xzDistToRise) { + EnPeehat_Ground_SetStateRise(this); + } + } else { + Math_SmoothStepToF(&this->actor.shape.yOffset, -1000.0f, 1.0f, 10.0f, 0.0f); + this->riseDelayTimer--; + } + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + Math_SmoothStepToF(&this->actor.shape.yOffset, -1000.0f, 1.0f, 50.0f, 0.0f); + if (this->unk2D4 != 0) { + this->unk2D4--; + if (this->unk2D4 & 4) { + Math_SmoothStepToF(&this->scaleShift, 0.205f, 1.0f, 0.235f, 0.0f); + } else { + Math_SmoothStepToF(&this->scaleShift, 0.0f, 1.0f, 0.005f, 0.0f); + } + } else if (this->colCylinder.base.acFlags & AC_HIT) { + EnPeehat_HitWhenGrounded(this, globalCtx); + } + } +} + +void EnPeehat_Flying_SetStateGround(EnPeehat* this) { + Animation_Change(&this->skelAnime, &gPeehatRisingAnim, 0.0f, 3.0f, Animation_GetLastFrame(&gPeehatRisingAnim), + ANIMMODE_ONCE, 0.0f); + this->seekPlayerTimer = 400; + this->unk2D4 = 0; + this->unk2FA = 0; //! @bug: overwrites number of child larva spawned, allowing for more than MAX_LARVA spawns + this->state = PEAHAT_STATE_4; + EnPeehat_SetupAction(this, EnPeehat_Flying_StateGrounded); +} + +void EnPeehat_Flying_StateGrounded(EnPeehat* this, GlobalContext* globalCtx) { + if (IS_DAY) { + if (this->actor.xzDistToPlayer < this->xzDistToRise) { + EnPeehat_Flying_SetStateRise(this); + } + } else { + Math_SmoothStepToF(&this->actor.shape.yOffset, -1000.0f, 1.0f, 50.0f, 0.0f); + if (this->unk2D4 != 0) { + this->unk2D4--; + if (this->unk2D4 & 4) { + Math_SmoothStepToF(&this->scaleShift, 0.205f, 1.0f, 0.235f, 0.0f); + } else { + Math_SmoothStepToF(&this->scaleShift, 0.0f, 1.0f, 0.005f, 0.0f); + } + } else if (this->colCylinder.base.acFlags & AC_HIT) { + EnPeehat_HitWhenGrounded(this, globalCtx); + } + } +} + +void EnPeehat_Flying_SetStateFly(EnPeehat* this) { + Animation_PlayLoop(&this->skelAnime, &gPeehatFlyingAnim); + this->state = PEAHAT_STATE_FLY; + EnPeehat_SetupAction(this, EnPeehat_Flying_StateFly); +} + +void EnPeehat_Flying_StateFly(EnPeehat* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_FLY - SFX_FLAG); + SkelAnime_Update(&this->skelAnime); + if (!IS_DAY || this->xzDistToRise < this->actor.xzDistToPlayer) { + EnPeehat_Flying_SetStateLanding(this); + } else if (this->actor.xzDistToPlayer < this->xzDistMax) { + if (this->unk2FA < MAX_LARVA && (globalCtx->gameplayFrames & 7) == 0) { + Actor* larva = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_PEEHAT, + Rand_CenteredFloat(25.0f) + this->actor.world.pos.x, + Rand_CenteredFloat(5.0f) + this->actor.world.pos.y, + Rand_CenteredFloat(25.0f) + this->actor.world.pos.z, 0, 0, 0, 1); + if (larva != NULL) { + larva->shape.rot.y = larva->world.rot.y = Rand_CenteredFloat(0xFFFF); + this->unk2FA++; + } + } + } + this->bladeRot += this->bladeRotVel; +} + +void EnPeehat_Ground_SetStateRise(EnPeehat* this) { + f32 lastFrame = Animation_GetLastFrame(&gPeehatRisingAnim); + + if (this->state != PEAHAT_STATE_STUNNED) { + Animation_Change(&this->skelAnime, &gPeehatRisingAnim, 0.0f, 3.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + } + this->state = PEAHAT_STATE_8; + this->animTimer = lastFrame; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_UP); + EnPeehat_SetupAction(this, EnPeehat_Ground_StateRise); +} + +void EnPeehat_Ground_StateRise(EnPeehat* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 1.0f, 50.0f, 0.0f); + if (Math_SmoothStepToS(&this->bladeRotVel, 4000, 1, 800, 0) == 0) { + if (this->animTimer != 0) { + this->animTimer--; + if (this->skelAnime.playSpeed == 0.0f) { + if (this->animTimer == 0) { + this->animTimer = 40; + this->skelAnime.playSpeed = 1.0f; + } + } + } + if (SkelAnime_Update(&this->skelAnime) || this->animTimer == 0) { + EnPeehat_Ground_SetStateHover(this); + } else { + this->actor.world.pos.y += 6.5f; + } + if (this->actor.world.pos.y - this->actor.floorHeight < 80.0f) { + Vec3f pos = this->actor.world.pos; + pos.y = this->actor.floorHeight; + func_80033480(globalCtx, &pos, 90.0f, 1, 0x96, 100, 1); + } + } + EnPeehat_SpawnDust(globalCtx, this, &this->actor.world.pos, 75.0f, 2, 1.05f, 2.0f); + Math_SmoothStepToF(&this->scaleShift, 0.075f, 1.0f, 0.005f, 0.0f); + this->bladeRot += this->bladeRotVel; +} + +void EnPeehat_Flying_SetStateRise(EnPeehat* this) { + f32 lastFrame; + + lastFrame = Animation_GetLastFrame(&gPeehatRisingAnim); + if (this->state != PEAHAT_STATE_STUNNED) { + Animation_Change(&this->skelAnime, &gPeehatRisingAnim, 0.0f, 3.0f, lastFrame, ANIMMODE_ONCE, 0.0f); + } + this->state = PEAHAT_STATE_9; + this->animTimer = lastFrame; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_UP); + EnPeehat_SetupAction(this, EnPeehat_Flying_StateRise); +} + +void EnPeehat_Flying_StateRise(EnPeehat* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 1.0f, 50.0f, 0.0f); + if (Math_SmoothStepToS(&this->bladeRotVel, 4000, 1, 800, 0) == 0) { + if (this->animTimer != 0) { + this->animTimer--; + if (this->skelAnime.playSpeed == 0.0f) { + if (this->animTimer == 0) { + this->animTimer = 40; + this->skelAnime.playSpeed = 1.0f; + } + } + } + if (SkelAnime_Update(&this->skelAnime) || this->animTimer == 0) { + //! @bug: overwrites number of child larva spawned, allowing for more than MAX_LARVA spawns + this->unk2FA = 0; + EnPeehat_Flying_SetStateFly(this); + } else { + this->actor.world.pos.y += 18.0f; + } + if (this->actor.world.pos.y - this->actor.floorHeight < 80.0f) { + Vec3f pos = this->actor.world.pos; + pos.y = this->actor.floorHeight; + func_80033480(globalCtx, &pos, 90.0f, 1, 0x96, 100, 1); + } + } + EnPeehat_SpawnDust(globalCtx, this, &this->actor.world.pos, 75.0f, 2, 1.05f, 2.0f); + Math_SmoothStepToF(&this->scaleShift, 0.075f, 1.0f, 0.005f, 0.0f); + this->bladeRot += this->bladeRotVel; +} + +void EnPeehat_Ground_SetStateSeekPlayer(EnPeehat* this) { + Animation_PlayLoop(&this->skelAnime, &gPeehatFlyingAnim); + this->state = PEAHAT_STATE_SEEK_PLAYER; + this->unk2E0 = 0.0f; + EnPeehat_SetupAction(this, EnPeehat_Ground_StateSeekPlayer); +} + +void EnPeehat_Ground_StateSeekPlayer(EnPeehat* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.speedXZ, 3.0f, 1.0f, 0.25f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight + 80.0f, 1.0f, 3.0f, 0.0f); + if (this->seekPlayerTimer <= 0) { + EnPeehat_Ground_SetStateLanding(this); + this->riseDelayTimer = 40; + } else { + this->seekPlayerTimer--; + } + if (IS_DAY && (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < this->xzDistMax)) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 1000, 0); + if (this->unk2FA != 0) { + this->actor.shape.rot.y += 0x1C2; + } else { + this->actor.shape.rot.y -= 0x1C2; + } + } else { + EnPeehat_Ground_SetStateReturnHome(this); + } + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->bladeRotVel, 4000, 1, 500, 0); + this->bladeRot += this->bladeRotVel; + Math_SmoothStepToF(&this->scaleShift, 0.075f, 1.0f, 0.005f, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_FLY - SFX_FLAG); +} + +void EnPeehat_Larva_SetStateSeekPlayer(EnPeehat* this) { + Animation_PlayLoop(&this->skelAnime, &gPeehatFlyingAnim); + this->state = PEAHAT_STATE_SEEK_PLAYER; + this->unk2D4 = 0; + EnPeehat_SetupAction(this, EnPeehat_Larva_StateSeekPlayer); +} + +void EnPeehat_Larva_StateSeekPlayer(EnPeehat* this, GlobalContext* globalCtx) { + f32 speedXZ = 5.3f; + + if (this->actor.xzDistToPlayer <= 5.3f) { + speedXZ = this->actor.xzDistToPlayer + 0.0005f; + } + if (this->actor.parent != NULL && this->actor.parent->update == NULL) { + this->actor.parent = NULL; + } + this->actor.speedXZ = speedXZ; + if (this->actor.world.pos.y - this->actor.floorHeight >= 70.0f) { + Math_SmoothStepToF(&this->actor.velocity.y, -1.3f, 1.0f, 0.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->actor.velocity.y, -0.135f, 1.0f, 0.05f, 0.0f); + } + if (this->unk2D4 == 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 830, 0); + } else { + this->unk2D4--; + } + this->actor.shape.rot.y += 0x15E; + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->bladeRotVel, 4000, 1, 500, 0); + this->bladeRot += this->bladeRotVel; + Math_SmoothStepToF(&this->scaleShift, 0.075f, 1.0f, 0.005f, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_SM_FLY - SFX_FLAG); + if (this->colQuad.base.atFlags & AT_BOUNCED) { + this->actor.colChkInfo.health = 0; + this->colQuad.base.acFlags = this->colQuad.base.acFlags & ~AC_BOUNCED; + EnPeehat_SetStateAttackRecoil(this); + } else if ((this->colQuad.base.atFlags & AT_HIT) || (this->colCylinder.base.acFlags & AC_HIT) || + (this->actor.bgCheckFlags & 1)) { + Player* player = GET_PLAYER(globalCtx); + this->colQuad.base.atFlags &= ~AT_HIT; + if (!(this->colCylinder.base.acFlags & AC_HIT) && &player->actor == this->colQuad.base.at) { + if (Rand_ZeroOne() > 0.5f) { + this->actor.world.rot.y += 0x2000; + } else { + this->actor.world.rot.y -= 0x2000; + } + this->unk2D4 = 40; + } else if (this->colCylinder.base.acFlags & AC_HIT || this->actor.bgCheckFlags & 1) { + Vec3f zeroVec = { 0, 0, 0 }; + s32 i; + for (i = 4; i >= 0; i--) { + Vec3f pos; + pos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + pos.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &pos, &zeroVec, &zeroVec, 40, 7, 255, 255, 255, 255, 255, 0, 0, 1, 9, + 1); + } + } + if (&player->actor != this->colQuad.base.at || this->colCylinder.base.acFlags & AC_HIT) { + if (!(this->actor.bgCheckFlags & 1)) { + EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_PIHAT_SM_DEAD, 1, 1, + 40); + } + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x20); + Actor_Kill(&this->actor); + } + } +} + +void EnPeehat_Ground_SetStateLanding(EnPeehat* this) { + this->state = PEAHAT_STATE_LANDING; + Animation_PlayOnce(&this->skelAnime, &gPeehatLandingAnim); + EnPeehat_SetupAction(this, EnPeehat_Ground_StateLanding); +} + +void EnPeehat_Ground_StateLanding(EnPeehat* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.shape.yOffset, -1000.0f, 1.0f, 50.0f, 0.0f); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 50, 0); + if (SkelAnime_Update(&this->skelAnime)) { + EnPeehat_Ground_SetStateGround(this); + this->actor.world.pos.y = this->actor.floorHeight; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_LAND); + } else if (this->actor.floorHeight < this->actor.world.pos.y) { + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight, 0.3f, 3.5f, 0.25f); + if (this->actor.world.pos.y - this->actor.floorHeight < 60.0f) { + Vec3f pos = this->actor.world.pos; + pos.y = this->actor.floorHeight; + func_80033480(globalCtx, &pos, 80.0f, 1, 150, 100, 1); + EnPeehat_SpawnDust(globalCtx, this, &pos, 75.0f, 2, 1.05f, 2.0f); + } + } + Math_SmoothStepToS(&this->bladeRotVel, 0, 1, 100, 0); + this->bladeRot += this->bladeRotVel; +} + +void EnPeehat_Flying_SetStateLanding(EnPeehat* this) { + Animation_PlayOnce(&this->skelAnime, &gPeehatLandingAnim); + this->state = PEAHAT_STATE_LANDING; + EnPeehat_SetupAction(this, EnPeehat_Flying_StateLanding); +} + +void EnPeehat_Flying_StateLanding(EnPeehat* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.shape.yOffset, -1000.0f, 1.0f, 50.0f, 0.0f); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 50, 0); + if (SkelAnime_Update(&this->skelAnime)) { + EnPeehat_Flying_SetStateGround(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_LAND); + this->actor.world.pos.y = this->actor.floorHeight; + } else if (this->actor.floorHeight < this->actor.world.pos.y) { + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight, 0.3f, 13.5f, 0.25f); + if (this->actor.world.pos.y - this->actor.floorHeight < 60.0f) { + Vec3f pos = this->actor.world.pos; + pos.y = this->actor.floorHeight; + func_80033480(globalCtx, &pos, 80.0f, 1, 150, 100, 1); + EnPeehat_SpawnDust(globalCtx, this, &pos, 75.0f, 2, 1.05f, 2.0f); + } + } + Math_SmoothStepToS(&this->bladeRotVel, 0, 1, 100, 0); + this->bladeRot += this->bladeRotVel; +} + +void EnPeehat_Ground_SetStateHover(EnPeehat* this) { + Animation_PlayLoop(&this->skelAnime, &gPeehatFlyingAnim); + this->actor.speedXZ = Rand_ZeroOne() * 0.5f + 2.5f; + this->unk2D4 = Rand_ZeroOne() * 10 + 10; + this->state = PEAHAT_STATE_15; + EnPeehat_SetupAction(this, EnPeehat_Ground_StateHover); +} + +void EnPeehat_Ground_StateHover(EnPeehat* this, GlobalContext* globalCtx) { + f32 cos; + Player* player = GET_PLAYER(globalCtx); + + // hover but don't gain altitude + if (this->actor.world.pos.y - this->actor.floorHeight > 75.0f) { + this->actor.world.pos.y -= 1.0f; + } + this->actor.world.pos.y += Math_CosF(this->unk2E0) * 1.4f; + cos = Math_CosF(this->unk2E0) * 0.18f; + this->unk2E0 += ((0.0f <= cos) ? cos : -cos) + 0.07f; + this->unk2D4--; + if (this->unk2D4 <= 0) { + this->actor.speedXZ = Rand_ZeroOne() * 0.5f + 2.5f; + this->unk2D4 = Rand_ZeroOne() * 10.0f + 10.0f; + this->unk2F4 = (Rand_ZeroOne() - 0.5f) * 1000.0f; + } + SkelAnime_Update(&this->skelAnime); + this->actor.world.rot.y += this->unk2F4; + if (this->seekPlayerTimer <= 0) { + EnPeehat_Ground_SetStateLanding(this); + this->riseDelayTimer = 40; + } else { + this->seekPlayerTimer--; + } + this->actor.shape.rot.y += 0x15E; + // if daytime, and the player is close to the initial spawn position + if (IS_DAY && Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < this->xzDistMax) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnPeehat_Ground_SetStateSeekPlayer(this); + this->unk2FA = globalCtx->gameplayFrames & 1; + } else { + EnPeehat_Ground_SetStateReturnHome(this); + } + Math_SmoothStepToS(&this->bladeRotVel, 4000, 1, 500, 0); + this->bladeRot += this->bladeRotVel; + Math_SmoothStepToF(&this->scaleShift, 0.075f, 1.0f, 0.005f, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_FLY - SFX_FLAG); +} + +void EnPeehat_Ground_SetStateReturnHome(EnPeehat* this) { + this->state = PEAHAT_STATE_RETURN_HOME; + this->actor.speedXZ = 2.5f; + EnPeehat_SetupAction(this, EnPeehat_Ground_StateReturnHome); +} + +void EnPeehat_Ground_StateReturnHome(EnPeehat* this, GlobalContext* globalCtx) { + f32 cos; + s16 yRot; + Player* player; + + player = GET_PLAYER(globalCtx); + if (this->actor.world.pos.y - this->actor.floorHeight > 75.0f) { + this->actor.world.pos.y -= 1.0f; + } else { + this->actor.world.pos.y += 1.0f; + } + this->actor.world.pos.y += Math_CosF(this->unk2E0) * 1.4f; + cos = Math_CosF(this->unk2E0) * 0.18f; + this->unk2E0 += ((0.0f <= cos) ? cos : -cos) + 0.07f; + yRot = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + Math_SmoothStepToS(&this->actor.world.rot.y, yRot, 1, 600, 0); + Math_SmoothStepToS(&this->actor.shape.rot.x, 4500, 1, 600, 0); + this->actor.shape.rot.y += 0x15E; + this->bladeRot += this->bladeRotVel; + if (Math_Vec3f_DistXZ(&this->actor.world.pos, &this->actor.home.pos) < 2.0f) { + EnPeehat_Ground_SetStateLanding(this); + this->riseDelayTimer = 60; + } + if (IS_DAY && Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < this->xzDistMax) { + this->seekPlayerTimer = 400; + EnPeehat_Ground_SetStateSeekPlayer(this); + this->unk2FA = (globalCtx->gameplayFrames & 1); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_FLY - SFX_FLAG); +} + +void EnPeehat_SetStateAttackRecoil(EnPeehat* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPeehatRecoilAnim, -4.0f); + this->state = PEAHAT_STATE_ATTACK_RECOIL; + this->actor.speedXZ = -9.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnPeehat_SetupAction(this, EnPeehat_StateAttackRecoil); +} + +void EnPeehat_StateAttackRecoil(EnPeehat* this, GlobalContext* globalCtx) { + this->bladeRot += this->bladeRotVel; + SkelAnime_Update(&this->skelAnime); + this->actor.speedXZ += 0.5f; + if (this->actor.speedXZ == 0.0f) { + // Is PEAHAT_TYPE_LARVA + if (this->actor.params > 0) { + Vec3f zeroVec = { 0, 0, 0 }; + s32 i; + for (i = 4; i >= 0; i--) { + Vec3f pos; + pos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + pos.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &pos, &zeroVec, &zeroVec, 40, 7, 255, 255, 255, 255, 255, 0, 0, 1, 9, + 1); + } + Actor_Kill(&this->actor); + } else { + EnPeehat_Ground_SetStateSeekPlayer(this); + // Is PEAHAT_TYPE_GROUNDED + if (this->actor.params < 0) { + this->unk2FA = (this->unk2FA != 0) ? 0 : 1; + } + } + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_FLY - SFX_FLAG); +} + +void EnPeehat_SetStateBoomerangStunned(EnPeehat* this) { + this->state = PEAHAT_STATE_STUNNED; + if (this->actor.floorHeight < this->actor.world.pos.y) { + this->actor.speedXZ = -9.0f; + } + this->bladeRotVel = 0; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Actor_SetColorFilter(&this->actor, 0, 200, 0, 80); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + EnPeehat_SetupAction(this, EnPeehat_StateBoomerangStunned); +} + +void EnPeehat_StateBoomerangStunned(EnPeehat* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight, 1.0f, 8.0f, 0.0f); + if (this->actor.colorFilterTimer == 0) { + EnPeehat_Ground_SetStateRise(this); + } +} + +void EnPeehat_Adult_SetStateDie(EnPeehat* this) { + this->bladeRotVel = 0; + this->isStateDieFirstUpdate = 1; + this->actor.speedXZ = 0.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 8); + this->state = PEAHAT_STATE_DYING; + this->scaleShift = 0.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnPeehat_SetupAction(this, EnPeehat_Adult_StateDie); +} + +void EnPeehat_Adult_StateDie(EnPeehat* this, GlobalContext* globalCtx) { + if (this->isStateDieFirstUpdate) { + this->unk2D4--; + if (this->unk2D4 <= 0 || this->actor.colChkInfo.health == 0) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPeehatRecoilAnim, -4.0f); + this->bladeRotVel = 4000; + this->unk2D4 = 14; + this->actor.speedXZ = 0; + this->actor.velocity.y = 6; + this->isStateDieFirstUpdate = 0; + this->actor.shape.rot.z = this->actor.shape.rot.x = 0; + } else if (this->actor.colorFilterTimer & 4) { + Math_SmoothStepToF(&this->scaleShift, 0.205f, 1.0f, 0.235f, 0); + } else { + Math_SmoothStepToF(&this->scaleShift, 0, 1.0f, 0.005f, 0); + } + } else { + SkelAnime_Update(&this->skelAnime); + this->bladeRot += this->bladeRotVel; + Math_SmoothStepToS(&this->bladeRotVel, 4000, 1, 250, 0); + if (this->actor.colChkInfo.health == 0) { + this->actor.scale.x -= 0.0015f; + Actor_SetScale(&this->actor, this->actor.scale.x); + } + if (Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight + 88.5f, 1.0f, 3.0f, 0.0f) == 0.0f && + this->actor.world.pos.y - this->actor.floorHeight < 59.0f) { + Vec3f pos = this->actor.world.pos; + pos.y = this->actor.floorHeight; + func_80033480(globalCtx, &pos, 80.0f, 1, 150, 100, 1); + EnPeehat_SpawnDust(globalCtx, this, &pos, 75.0f, 2, 1.05f, 2.0f); + } + if (this->actor.speedXZ < 0) { + this->actor.speedXZ += 0.25f; + } + this->unk2D4--; + if (this->unk2D4 <= 0) { + if (this->actor.colChkInfo.health == 0) { + EnPeehat_SetStateExplode(this); + // if PEAHAT_TYPE_GROUNDED + } else if (this->actor.params < 0) { + EnPeehat_Ground_SetStateHover(this); + this->riseDelayTimer = 60; + } else { + EnPeehat_Flying_SetStateFly(this); + } + } + } +} + +void EnPeehat_SetStateExplode(EnPeehat* this) { + Animation_PlayLoop(&this->skelAnime, &gPeehatFlyingAnim); + this->state = PEAHAT_STATE_EXPLODE; + this->animTimer = 5; + this->unk2E0 = 0.0f; + EnPeehat_SetupAction(this, EnPeehat_StateExplode); +} + +void EnPeehat_StateExplode(EnPeehat* this, GlobalContext* globalCtx) { + EnBom* bomb; + s32 pad[2]; + + if (this->animTimer == 5) { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0x602, 0); + if (bomb != NULL) { + bomb->timer = 0; + } + } + this->animTimer--; + if (this->animTimer == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x40); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x40); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x40); + Actor_Kill(&this->actor); + } +} + +void EnPeehat_Adult_CollisionCheck(EnPeehat* this, GlobalContext* globalCtx) { + if ((this->colCylinder.base.acFlags & AC_BOUNCED) || (this->colQuad.base.acFlags & AC_BOUNCED)) { + this->colQuad.base.acFlags &= ~AC_BOUNCED; + this->colCylinder.base.acFlags &= ~AC_BOUNCED; + this->colJntSph.base.acFlags &= ~AC_HIT; + } else if (this->colJntSph.base.acFlags & AC_HIT) { + this->colJntSph.base.acFlags &= ~AC_HIT; + Actor_SetDropFlagJntSph(&this->actor, &this->colJntSph, 1); + if (this->actor.colChkInfo.damageEffect == PEAHAT_DMG_EFF_NUT || + this->actor.colChkInfo.damageEffect == PEAHAT_DMG_EFF_LIGHT_ICE_ARROW) { + return; + } + if (this->actor.colChkInfo.damageEffect == PEAHAT_DMG_EFF_HOOKSHOT) { + this->actor.colChkInfo.health = 0; + } else if (this->actor.colChkInfo.damageEffect == PEAHAT_DMG_EFF_BOOMERANG) { + if (this->state != PEAHAT_STATE_STUNNED) { + EnPeehat_SetStateBoomerangStunned(this); + } + return; + } else { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 8); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PIHAT_DAMAGE); + } + + if (this->actor.colChkInfo.damageEffect == PEAHAT_DMG_EFF_FIRE) { + Vec3f pos; + s32 i; + for (i = 4; i >= 0; i--) { + pos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + pos.y = Rand_ZeroOne() * 25.0f + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &pos, 70, 0, 0, -1); + } + Actor_SetColorFilter(&this->actor, 0x4000, 200, 0, 100); + } + if (this->actor.colChkInfo.health == 0) { + EnPeehat_Adult_SetStateDie(this); + } + } +} + +void EnPeehat_Update(Actor* thisx, GlobalContext* globalCtx) { + EnPeehat* this = (EnPeehat*)thisx; + s32 i; + Player* player = GET_PLAYER(globalCtx); + + // If Adult Peahat + if (thisx->params <= 0) { + EnPeehat_Adult_CollisionCheck(this, globalCtx); + } + if (thisx->colChkInfo.damageEffect != PEAHAT_DMG_EFF_LIGHT_ICE_ARROW) { + if (thisx->speedXZ != 0.0f || thisx->velocity.y != 0.0f) { + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 25.0f, 30.0f, 30.0f, 5); + } + + this->actionFunc(this, globalCtx); + if ((globalCtx->gameplayFrames & 0x7F) == 0) { + this->jiggleRotInc = (Rand_ZeroOne() * 0.25f) + 0.5f; + } + this->jiggleRot += this->jiggleRotInc; + } + // if PEAHAT_TYPE_GROUNDED + if (thisx->params < 0) { + // Set the Z-Target point on the Peahat's weak point + thisx->focus.pos.x = this->colJntSph.elements[0].dim.worldSphere.center.x; + thisx->focus.pos.y = this->colJntSph.elements[0].dim.worldSphere.center.y; + thisx->focus.pos.z = this->colJntSph.elements[0].dim.worldSphere.center.z; + if (this->state == PEAHAT_STATE_SEEK_PLAYER) { + Math_SmoothStepToS(&thisx->shape.rot.x, 6000, 1, 300, 0); + } else { + Math_SmoothStepToS(&thisx->shape.rot.x, 0, 1, 300, 0); + } + } else { + thisx->focus.pos = thisx->world.pos; + } + Collider_UpdateCylinder(thisx, &this->colCylinder); + if (thisx->colChkInfo.health > 0) { + // If Adult Peahat + if (thisx->params <= 0) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colJntSph.base); + if (thisx->colorFilterTimer == 0 || !(thisx->colorFilterParams & 0x4000)) { + if (this->state != PEAHAT_STATE_EXPLODE) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colJntSph.base); + } + } + } + if (thisx->params != PEAHAT_TYPE_FLYING && this->colQuad.base.atFlags & AT_HIT) { + this->colQuad.base.atFlags &= ~AT_HIT; + if (&player->actor == this->colQuad.base.at) { + EnPeehat_SetStateAttackRecoil(this); + } + } + } + if (this->state == PEAHAT_STATE_15 || this->state == PEAHAT_STATE_SEEK_PLAYER || this->state == PEAHAT_STATE_FLY || + this->state == PEAHAT_STATE_RETURN_HOME || this->state == PEAHAT_STATE_EXPLODE) { + if (thisx->params != PEAHAT_TYPE_FLYING) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colQuad.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colQuad.base); + } + // if PEAHAT_TYPE_GROUNDED + if (thisx->params < 0 && (thisx->flags & ACTOR_FLAG_6)) { + for (i = 1; i >= 0; i--) { + Vec3f posResult; + CollisionPoly* poly = NULL; + s32 bgId; + Vec3f* posB = &this->bladeTip[i]; + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &thisx->world.pos, posB, &posResult, &poly, true, true, + false, true, &bgId) == true) { + func_80033480(globalCtx, &posResult, 0.0f, 1, 300, 150, 1); + EnPeehat_SpawnDust(globalCtx, this, &posResult, 0.0f, 3, 1.05f, 1.5f); + } + } + } else if (thisx->params != PEAHAT_TYPE_FLYING) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder.base); + } + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder.base); + } + Math_SmoothStepToF(&this->scaleShift, 0.0f, 1.0f, 0.001f, 0.0f); +} + +s32 EnPeehat_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnPeehat* this = (EnPeehat*)thisx; + + if (limbIndex == 4) { + rot->x = -this->bladeRot; + } + if (limbIndex == 3 || (limbIndex == 23 && (this->state == PEAHAT_STATE_DYING || this->state == PEAHAT_STATE_3 || + this->state == PEAHAT_STATE_4))) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_peehat.c", 1946); + Matrix_Push(); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(this->jiggleRot * 0.115f, MTXMODE_APPLY); + Matrix_RotateY(this->jiggleRot * 0.13f, MTXMODE_APPLY); + Matrix_RotateZ(this->jiggleRot * 0.1f, MTXMODE_APPLY); + Matrix_Scale(1.0f - this->scaleShift, this->scaleShift + 1.0f, 1.0f - this->scaleShift, MTXMODE_APPLY); + Matrix_RotateZ(-(this->jiggleRot * 0.1f), MTXMODE_APPLY); + Matrix_RotateY(-(this->jiggleRot * 0.13f), MTXMODE_APPLY); + Matrix_RotateX(-(this->jiggleRot * 0.115f), MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_peehat.c", 1959), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_peehat.c", 1963); + return true; + } + return false; +} + +void EnPeehat_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f peahatBladeTip[] = { { 0.0f, 0.0f, 5500.0f }, { 0.0f, 0.0f, -5500.0f } }; + + EnPeehat* this = (EnPeehat*)thisx; + f32 damageYRot; + + if (limbIndex == 4) { + Matrix_MultVec3f(&peahatBladeTip[0], &this->bladeTip[0]); + Matrix_MultVec3f(&peahatBladeTip[1], &this->bladeTip[1]); + return; + } + // is Adult Peahat + if (limbIndex == 3 && this->actor.params <= 0) { + damageYRot = 0.0f; + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_peehat.c", 1981); + Matrix_Push(); + Matrix_Translate(-1000.0f, 0.0f, 0.0f, MTXMODE_APPLY); + Collider_UpdateSpheres(0, &this->colJntSph); + Matrix_Translate(500.0f, 0.0f, 0.0f, MTXMODE_APPLY); + if (this->actor.colorFilterTimer != 0 && (this->actor.colorFilterParams & 0x4000)) { + damageYRot = Math_SinS(this->actor.colorFilterTimer * 0x4E20) * 0.35f; + } + Matrix_RotateY(3.2f + damageYRot, MTXMODE_APPLY); + Matrix_Scale(0.3f, 0.2f, 0.2f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_peehat.c", 1990), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, *dList); + Matrix_Pop(); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_peehat.c", 1994); + } +} + +void EnPeehat_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f D_80AD285C[] = { + { 0.0f, 0.0f, -4500.0f }, { -4500.0f, 0.0f, 0.0f }, { 4500.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 4500.0f } + }; + EnPeehat* this = (EnPeehat*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnPeehat_OverrideLimbDraw, + EnPeehat_PostLimbDraw, this); + if (this->actor.speedXZ != 0.0f || this->actor.velocity.y != 0.0f) { + Matrix_MultVec3f(&D_80AD285C[0], &this->colQuad.dim.quad[1]); + Matrix_MultVec3f(&D_80AD285C[1], &this->colQuad.dim.quad[0]); + Matrix_MultVec3f(&D_80AD285C[2], &this->colQuad.dim.quad[3]); + Matrix_MultVec3f(&D_80AD285C[3], &this->colQuad.dim.quad[2]); + Collider_SetQuadVertices(&this->colQuad, &this->colQuad.dim.quad[0], &this->colQuad.dim.quad[1], + &this->colQuad.dim.quad[2], &this->colQuad.dim.quad[3]); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.h b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.h new file mode 100644 index 000000000..726b47ad4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.h @@ -0,0 +1,47 @@ +#ifndef Z_EN_PEEHAT_H +#define Z_EN_PEEHAT_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* -1 */ PEAHAT_TYPE_GROUNDED = -1, + /* 0 */ PEAHAT_TYPE_FLYING = 0, + /* 1 */ PEAHAT_TYPE_LARVA = 1 +} PeahatType; + +struct EnPeehat; + +typedef void (*EnPeehatActionFunc)(struct EnPeehat*, GlobalContext*); + +typedef struct EnPeehat { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[24]; + /* 0x0220 */ Vec3s morphTable[24]; + /* 0x02B0 */ s32 state; + /* 0x02B4 */ s32 isStateDieFirstUpdate; + /* 0x02B8 */ EnPeehatActionFunc actionFunc; + /* 0x02BC */ Vec3f bladeTip[2]; // used to simulate the peahat's blades "digging up" earth + /* 0x02D4 */ s32 unk2D4; + /* 0x02D8 */ f32 xzDistMax; // max xz dist to follow Link (PEAHAT_TYPE_GROUNDED) or spawn larva (PEAHAT_TYPE_FLYING) + /* 0x02DC */ f32 xzDistToRise; // xz dist needed to rise from the ground + /* 0x02E0 */ f32 unk2E0; // rot? + /* 0x02E4 */ f32 jiggleRot; + /* 0x02E8 */ f32 jiggleRotInc; + /* 0x02EC */ f32 scaleShift; // 0.0f for no distortion. used for "jiggle" effect when stabbed on ground + /* 0x02F0 */ s16 bladeRotVel; // spinning blades rotational velocity + /* 0x02F2 */ s16 bladeRot; // spinning blades rotation component + /* 0x02F4 */ s16 unk2F4; + /* 0x02F6 */ s16 riseDelayTimer; // countdown timer until peahat is allowed to rise up from the ground + /* 0x02F8 */ s16 seekPlayerTimer; // number of frames the peahat should seek the player before landing to rest + /* 0x02FA */ s16 unk2FA; // larva count (PEAHAT_TYPE_FLYING, PEAHAT_TYPE_GROUNDED), + // shape rotation direction (PEAHAT_TYPE_GROUNDED) + /* 0x02FC */ s16 animTimer; + /* 0x0300 */ ColliderCylinder colCylinder; + /* 0x034C */ ColliderJntSph colJntSph; + /* 0x036C */ ColliderJntSphElement colJntSphItemList[1]; + /* 0x03AC */ ColliderQuad colQuad; +} EnPeehat; // size = 0x042C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c b/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c new file mode 100644 index 000000000..551321011 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.c @@ -0,0 +1,271 @@ +/* + * File: z_en_po_desert.c + * Overlay: ovl_En_Po_Desert + * Description: Guide Poe (Haunted Wasteland) + */ + +#include "z_en_po_desert.h" +#include "objects/object_po_field/object_po_field.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_7 | ACTOR_FLAG_12) + +void EnPoDesert_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPoDesert_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPoDesert_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPoDesert_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnPoDesert_SetNextPathPoint(EnPoDesert* this, GlobalContext* globalCtx); +void EnPoDesert_WaitForPlayer(EnPoDesert* this, GlobalContext* globalCtx); +void EnPoDesert_MoveToNextPoint(EnPoDesert* this, GlobalContext* globalCtx); +void EnPoDesert_Disappear(EnPoDesert* this, GlobalContext* globalCtx); + +const ActorInit En_Po_Desert_InitVars = { + ACTOR_EN_PO_DESERT, + ACTORCAT_BG, + FLAGS, + OBJECT_PO_FIELD, + sizeof(EnPoDesert), + (ActorFunc)EnPoDesert_Init, + (ActorFunc)EnPoDesert_Destroy, + (ActorFunc)EnPoDesert_Update, + (ActorFunc)EnPoDesert_Draw, + NULL, +}; + +static ColliderCylinderInit sColliderInit = { + { + COLTYPE_HIT3, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 25, 50, 20, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x5C, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 3200, ICHAIN_STOP), +}; + +void EnPoDesert_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnPoDesert* this = (EnPoDesert*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_Init(globalCtx, &this->skelAnime, &gPoeFieldSkel, &gPoeFieldFloatAnim, this->jointTable, this->morphTable, + 10); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sColliderInit); + this->lightColor.r = 255; + this->lightColor.g = 255; + this->lightColor.b = 210; + this->lightColor.a = 255; + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, + 255, 255, 255, 200); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 37.0f); + this->currentPathPoint = 1; + this->actor.params = (this->actor.params >> 8) & 0xFF; + this->targetY = this->actor.world.pos.y; + EnPoDesert_SetNextPathPoint(this, globalCtx); +} + +void EnPoDesert_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPoDesert* this = (EnPoDesert*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnPoDesert_SetNextPathPoint(EnPoDesert* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[this->actor.params]; + Vec3s* pathPoint; + + Animation_MorphToLoop(&this->skelAnime, &gPoeFieldDisappearAnim, -6.0f); + pathPoint = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[this->currentPathPoint]; + this->actor.home.pos.x = pathPoint->x; + this->actor.home.pos.y = pathPoint->y; + this->actor.home.pos.z = pathPoint->z; + this->initDistToNextPoint = Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos); + this->initDistToNextPoint = CLAMP_MIN(this->initDistToNextPoint, 1.0f); + this->currentPathPoint++; + this->yDiff = this->actor.home.pos.y - this->actor.world.pos.y; + this->actor.speedXZ = 0.0f; + if (path->count == this->currentPathPoint) { + this->currentPathPoint = 0; + } + this->actionFunc = EnPoDesert_WaitForPlayer; +} + +void EnPoDesert_SetupMoveToNextPoint(EnPoDesert* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeFieldFloatAnim, -5.0f); + this->actionFunc = EnPoDesert_MoveToNextPoint; +} + +void EnPoDesert_SetupDisappear(EnPoDesert* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPoeFieldDisappearAnim, -6.0f); + this->actionTimer = 16; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DISAPPEAR); + this->actionFunc = EnPoDesert_Disappear; +} + +void EnPoDesert_UpdateSpeedModifier(EnPoDesert* this) { + if (this->speedModifier == 0) { + this->speedModifier = 32; + } + if (this->speedModifier != 0) { + this->speedModifier--; + } + this->actor.world.pos.y = Math_SinS(this->speedModifier * 0x800) * 13.0f + this->targetY; +} + +void EnPoDesert_WaitForPlayer(EnPoDesert* this, GlobalContext* globalCtx) { + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); + if (this->actor.xzDistToPlayer < 200.0f && (this->currentPathPoint != 2 || globalCtx->actorCtx.unk_03)) { + if (this->currentPathPoint == 2) { + if (Gameplay_InCsMode(globalCtx)) { + this->actor.shape.rot.y += 0x800; + return; + } + Message_StartTextbox(globalCtx, 0x600B, NULL); + } + EnPoDesert_SetupMoveToNextPoint(this); + } else { + this->actor.shape.rot.y += 0x800; + } +} + +void EnPoDesert_MoveToNextPoint(EnPoDesert* this, GlobalContext* globalCtx) { + f32 temp_f20; + + if (this->actionTimer != 0) { + this->actionTimer--; + } + temp_f20 = sinf(this->actionTimer * (M_PI / 20.0f)) * 5.0f; + this->actor.world.pos.x += temp_f20 * Math_CosS(this->actor.shape.rot.y); + this->actor.world.pos.z += temp_f20 * Math_SinS(this->actor.shape.rot.y); + if (this->actionTimer == 0) { + this->actionTimer = 40; + } + temp_f20 = Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos); + this->actor.world.rot.y = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + Math_ApproachS(&this->actor.shape.rot.y, this->actor.world.rot.y + 0x8000, 5, 0x400); + this->actor.speedXZ = sinf(this->speedModifier * (M_PI / 32.0f)) * 2.5f + 5.5f; + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); + this->targetY = this->actor.home.pos.y - ((temp_f20 * this->yDiff) / this->initDistToNextPoint); + if (temp_f20 < 40.0f) { + if (this->currentPathPoint != 0) { + EnPoDesert_SetNextPathPoint(this, globalCtx); + } else { + EnPoDesert_SetupDisappear(this); + } + } +} + +void EnPoDesert_Disappear(EnPoDesert* this, GlobalContext* globalCtx) { + if (this->actionTimer != 0) { + this->actionTimer--; + } + this->actor.shape.rot.y += 0x2000; + this->lightColor.a = this->actionTimer * 15.9375f; + this->actor.shape.shadowAlpha = this->lightColor.a; + if (this->actionTimer == 0) { + Actor_Kill(&this->actor); + } +} + +void EnPoDesert_Update(Actor* thisx, GlobalContext* globalCtx) { + EnPoDesert* this = (EnPoDesert*)thisx; + s32 pad; + + SkelAnime_Update(&this->skelAnime); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + EnPoDesert_UpdateSpeedModifier(this); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 27.0f, 60.0f, 4); + Actor_SetFocus(&this->actor, 42.0f); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (globalCtx->actorCtx.unk_03) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_7; + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + } else { + this->actor.shape.shadowDraw = NULL; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_7); + } +} + +s32 EnPoDesert_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx, Gfx** gfxP) { + EnPoDesert* this = (EnPoDesert*)thisx; + f32 mtxScale; + + if (this->actionFunc == EnPoDesert_Disappear && limbIndex == 7) { + mtxScale = this->actionTimer / 16.0f; + Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); + } + if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + *dList = NULL; + } + return false; +} + +void EnPoDesert_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, + Gfx** gfxP) { + static Vec3f baseLightPos = { 0.0f, 1400.0f, 0.0f }; + + EnPoDesert* this = (EnPoDesert*)thisx; + f32 rand; + Color_RGBA8 color; + Vec3f lightPos; + + if (limbIndex == 7) { + Matrix_MultVec3f(&baseLightPos, &lightPos); + rand = Rand_ZeroOne(); + color.r = (s16)(rand * 30.0f) + 225; + color.g = (s16)(rand * 100.0f) + 155; + color.b = (s16)(rand * 160.0f) + 95; + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + gDPPipeSync((*gfxP)++); + gDPSetEnvColor((*gfxP)++, color.r, color.g, color.b, 255); + gSPMatrix((*gfxP)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_desert.c", 523), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList((*gfxP)++, gPoeFieldLanternDL); + gSPDisplayList((*gfxP)++, gPoeFieldLanternTopDL); + gDPPipeSync((*gfxP)++); + gDPSetEnvColor((*gfxP)++, this->lightColor.r, this->lightColor.g, this->lightColor.b, this->lightColor.a); + } + Lights_PointNoGlowSetInfo(&this->lightInfo, lightPos.x, lightPos.y, lightPos.z, color.r, color.g, color.b, 200); + } +} + +void EnPoDesert_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnPoDesert* this = (EnPoDesert*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_desert.c", 559); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x0A, Gfx_EnvColor(globalCtx->state.gfxCtx, 255, 85, 0, 255)); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_EnvColor(globalCtx->state.gfxCtx, this->lightColor.r, this->lightColor.g, this->lightColor.b, + this->lightColor.a)); + if (this->actionFunc == EnPoDesert_Disappear) { + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + } else { + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280 + 2); + } + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoDesert_OverrideLimbDraw, EnPoDesert_PostLimbDraw, &this->actor, POLY_XLU_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_desert.c", 597); +} diff --git a/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.h b/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.h new file mode 100644 index 000000000..a0c84fa21 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Desert/z_en_po_desert.h @@ -0,0 +1,29 @@ +#ifndef Z_EN_PO_DESERT_H +#define Z_EN_PO_DESERT_H + +#include "ultra64.h" +#include "global.h" + +struct EnPoDesert; + +typedef void (*EnPoDesertActionFunc)(struct EnPoDesert*, GlobalContext*); + +typedef struct EnPoDesert { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnPoDesertActionFunc actionFunc; + /* 0x0194 */ s16 actionTimer; + /* 0x0196 */ s16 speedModifier; + /* 0x0198 */ s32 currentPathPoint; + /* 0x019C */ f32 initDistToNextPoint; + /* 0x01A0 */ f32 yDiff; + /* 0x01A4 */ f32 targetY; + /* 0x01A8 */ Vec3s jointTable[10]; + /* 0x01E4 */ Vec3s morphTable[10]; + /* 0x0220 */ Color_RGBA8 lightColor; + /* 0x0224 */ LightNode* lightNode; + /* 0x0228 */ LightInfo lightInfo; + /* 0x0238 */ ColliderCylinder collider; +} EnPoDesert; // size = 0x0284 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c new file mode 100644 index 000000000..93f48417e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.c @@ -0,0 +1,1016 @@ +/* + * File: z_en_po_field.c + * Overlay: ovl_En_Po_Field + * Description: Field Poe + */ + +#include "z_en_po_field.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_po_field/object_po_field.h" + +#include + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_12) + +void EnPoField_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPoField_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPoField_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPoField_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnPoField_Reset(void); + +void EnPoField_UpdateDead(Actor* thisx, GlobalContext* globalCtx); +void EnPoField_DrawSoul(Actor* thisx, GlobalContext* globalCtx); + +void EnPoField_SetupWaitForSpawn(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_WaitForSpawn(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_Appear(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_CirclePlayer(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_Damage(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_Flee(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_Death(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_Disappear(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_SoulIdle(EnPoField* this, GlobalContext* globalCtx); +void func_80AD587C(EnPoField* this, GlobalContext* globalCtx); +void func_80AD58D4(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_SoulDisappear(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_SoulInteract(EnPoField* this, GlobalContext* globalCtx); +void EnPoField_SpawnFlame(EnPoField* this); + +const ActorInit En_Po_Field_InitVars = { + ACTOR_EN_PO_FIELD, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_PO_FIELD, + sizeof(EnPoField), + (ActorFunc)EnPoField_Init, + (ActorFunc)EnPoField_Destroy, + (ActorFunc)EnPoField_Update, + (ActorFunc)EnPoField_Draw, + (ActorResetFunc)EnPoField_Reset, +}; + +static ColliderCylinderInit D_80AD7080 = { + { + COLTYPE_HIT3, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 25, 50, 20, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit D_80AD70AC = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x01, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 10, 30, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit D_80AD70D8 = { 4, 25, 50, 40 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static s32 sNumSpawned = 0; + +static Vec3f sFieldMiddle = { -1000.0f, 0.0f, 6500.0f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 3200, ICHAIN_STOP), +}; + +static Vec3f D_80AD7114 = { 0.0f, 3.0f, 0.0f }; + +static Vec3f D_80AD7120 = { 0.0f, 0.0f, 0.0f }; + +static EnPoFieldInfo sPoFieldInfo[2] = { + { { 255, 170, 255 }, { 100, 0, 150 }, { 255, 85, 0 }, 248, gPoeFieldSoulTex }, + { { 255, 255, 170 }, { 255, 200, 0 }, { 160, 0, 255 }, 241, gBigPoeSoulTex }, +}; + +static Vec3f D_80AD714C = { 0.0f, 1400.0f, 0.0f }; + +static Vec3s sSpawnPositions[10]; +static u8 sSpawnSwitchFlags[10]; +static MtxF sLimb7Mtx; + +void EnPoField_Init(Actor* thisx, GlobalContext* globalCtx) { + EnPoField* this = (EnPoField*)thisx; + s32 pad; + + if (sNumSpawned != 10) { + sSpawnPositions[sNumSpawned].x = this->actor.world.pos.x; + sSpawnPositions[sNumSpawned].y = this->actor.world.pos.y; + sSpawnPositions[sNumSpawned].z = this->actor.world.pos.z; + sSpawnSwitchFlags[sNumSpawned] = this->actor.params & 0xFF; + sNumSpawned++; + } + if (sNumSpawned >= 2) { + this->actor.params = 0xFF; + Actor_Kill(&this->actor); + return; + } + Actor_ProcessInitChain(&this->actor, sInitChain); + SkelAnime_Init(globalCtx, &this->skelAnime, &gPoeFieldSkel, &gPoeFieldFloatAnim, this->jointTable, this->morphTable, + 10); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &D_80AD7080); + Collider_InitCylinder(globalCtx, &this->flameCollider); + Collider_SetCylinder(globalCtx, &this->flameCollider, &this->actor, &D_80AD70AC); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &D_80AD70D8); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, + 255, 255, 255, 0); + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + EnPoField_SetupWaitForSpawn(this, globalCtx); +} + +void EnPoField_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPoField* this = (EnPoField*)thisx; + + if (this->actor.params != 0xFF) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + Collider_DestroyCylinder(globalCtx, &this->flameCollider); + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnPoField_SetupWaitForSpawn(EnPoField* this, GlobalContext* globalCtx) { + this->actor.update = EnPoField_Update; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + this->actor.shape.rot.x = 0; + Lights_PointSetColorAndRadius(&this->lightInfo, 0, 0, 0, 0); + this->actionTimer = 200; + Actor_SetScale(&this->actor, 0.0f); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_16); + this->collider.base.acFlags &= ~AC_ON; + this->collider.base.ocFlags1 = OC1_ON | OC1_TYPE_ALL; + this->actor.colChkInfo.health = D_80AD70D8.health; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actionFunc = EnPoField_WaitForSpawn; +} + +void EnPoField_SetupAppear(EnPoField* this) { + Animation_PlayOnce(&this->skelAnime, &gPoeFieldAppearAnim); + this->actor.draw = EnPoField_Draw; + this->lightColor.r = 255; + this->lightColor.g = 255; + this->lightColor.b = 210; + this->lightColor.a = 0; + this->actor.shape.shadowAlpha = 0; + this->actor.shape.yOffset = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + this->actor.home.pos.y = this->actor.world.pos.y; + if (this->actor.params == EN_PO_FIELD_BIG) { + this->actor.speedXZ = 12.0f; + this->collider.dim.radius = 35; + this->collider.dim.height = 100; + this->collider.dim.yShift = 10; + this->actor.shape.shadowScale = 45.0f; + this->scaleModifier = 0.014f; + this->actor.naviEnemyId = 0x5A; + } else { + this->actor.speedXZ = 0.0f; + this->collider.dim.radius = D_80AD7080.dim.radius; + this->collider.dim.height = D_80AD7080.dim.height; + this->collider.dim.yShift = D_80AD7080.dim.yShift; + this->actor.shape.shadowScale = 37.0f; + this->scaleModifier = 0.01f; + this->actor.naviEnemyId = 0x5C; + } + this->actionFunc = EnPoField_Appear; +} + +void EnPoField_SetupCirclePlayer(EnPoField* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Animation_PlayLoop(&this->skelAnime, &gPoeFieldFloatAnim); + this->collider.base.acFlags |= AC_ON; + this->scaleModifier = this->actor.xzDistToPlayer; + Math_Vec3f_Copy(&this->actor.home.pos, &player->actor.world.pos); + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (this->actionFunc != EnPoField_Damage) { + this->actor.flags |= ACTOR_FLAG_0; + this->actionTimer = 600; + this->unk_194 = 32; + } + this->actionFunc = EnPoField_CirclePlayer; +} + +void EnPoField_SetupFlee(EnPoField* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeFieldFleeAnim, -5.0f); + this->collider.base.acFlags |= AC_ON; + this->actionFunc = EnPoField_Flee; + this->actor.speedXZ = 12.0f; + if (this->actionFunc != EnPoField_Damage) { + this->actor.flags |= ACTOR_FLAG_0; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x8000; + this->actionTimer = 2000; + this->unk_194 = 32; + } +} + +void EnPoField_SetupDamage(EnPoField* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPoeFieldDamagedAnim, -6.0f); + if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F824) { + this->actor.world.rot.y = this->collider.base.ac->world.rot.y; + } else { + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, this->collider.base.ac) + 0x8000; + } + this->collider.base.acFlags &= ~(AC_HIT | AC_ON); + this->actor.speedXZ = 5.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 16); + this->actionFunc = EnPoField_Damage; +} + +void EnPoField_SetupDeath(EnPoField* this) { + this->actionTimer = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.naviEnemyId = 0xFF; + if (this->flameTimer >= 20) { + this->flameTimer = 19; + } + this->actionFunc = EnPoField_Death; +} + +void EnPoField_SetupDisappear(EnPoField* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeFieldDisappearAnim, -6.0f); + this->actionTimer = 16; + this->collider.base.acFlags &= ~(AC_HIT | AC_ON); + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DISAPPEAR); + this->actionFunc = EnPoField_Disappear; +} + +void EnPoField_SetupSoulIdle(EnPoField* this, GlobalContext* globalCtx) { + this->actor.update = EnPoField_UpdateDead; + this->actor.draw = EnPoField_DrawSoul; + this->actor.shape.shadowDraw = NULL; + Actor_SetScale(&this->actor, 0.01f); + this->actor.gravity = -1.0f; + this->actor.shape.yOffset = 1500.0f; + this->actor.shape.rot.x = -0x8000; + this->actionTimer = 60; + this->actor.world.pos.y -= 15.0f; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_MISC); + this->actionFunc = EnPoField_SoulIdle; +} + +void func_80AD42B0(EnPoField* this) { + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0); + this->actor.shape.rot.y = 0; + this->lightColor.a = 0; + this->actor.shape.rot.x = 0; + this->actor.shape.yOffset = 0.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.home.pos.y = this->actor.world.pos.y; + this->actor.scale.x = 0.0f; + this->actor.scale.y = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_METAL_BOX_BOUND); + if (this->actor.params == EN_PO_FIELD_BIG) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + this->actionFunc = func_80AD587C; +} + +void func_80AD4384(EnPoField* this) { + this->actor.home.pos.y = this->actor.world.pos.y; + Actor_SetFocus(&this->actor, -10.0f); + this->collider.dim.radius = 13; + this->collider.dim.height = 30; + this->collider.dim.yShift = 0; + this->collider.dim.pos.x = this->actor.world.pos.x; + this->collider.dim.pos.y = this->actor.world.pos.y - 20.0f; + this->collider.dim.pos.z = this->actor.world.pos.z; + this->collider.base.ocFlags1 = OC1_ON | OC1_TYPE_PLAYER; + this->actor.textId = 0x5005; + this->actionTimer = 400; + this->unk_194 = 32; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = func_80AD58D4; +} + +void EnPoField_SetupSoulDisappear(EnPoField* this) { + this->actionFunc = EnPoField_SoulDisappear; +} + +void EnPoField_SetupInteractWithSoul(EnPoField* this) { + this->actionFunc = EnPoField_SoulInteract; + this->actor.home.pos.y = this->actor.world.pos.y - 15.0f; +} + +void EnPoField_CorrectYPos(EnPoField* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_194 == 0) { + this->unk_194 = 32; + } + if (this->unk_194 != 0) { + this->unk_194 -= 1; + } + if (this->actor.floorHeight == BGCHECK_Y_MIN) { + EnPoField_SetupDisappear(this); + return; + } + Math_ApproachF( + &this->actor.home.pos.y, + ((player->actor.world.pos.y > this->actor.floorHeight) ? player->actor.world.pos.y : this->actor.floorHeight) + + 13.0f, + 0.2f, 5.0f); + this->actor.world.pos.y = Math_SinS(this->unk_194 * 0x800) * 13.0f + this->actor.home.pos.y; +} + +f32 EnPoField_SetFleeSpeed(EnPoField* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 speed = ((player->stateFlags1 & 0x800000) && player->rideActor != NULL) ? player->rideActor->speedXZ : 12.0f; + + if (this->actor.xzDistToPlayer < 300.0f) { + this->actor.speedXZ = speed * 1.5f + 2.0f; + } else if (this->actor.xzDistToPlayer < 400.0f) { + this->actor.speedXZ = speed * 1.25f + 2.0f; + } else if (this->actor.xzDistToPlayer < 500.0f) { + this->actor.speedXZ = speed + 2.0f; + } else { + this->actor.speedXZ = 12.0f; + } + this->actor.speedXZ = CLAMP_MIN(this->actor.speedXZ, 12.0f); +} + +void EnPoField_WaitForSpawn(EnPoField* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 spawnDist; + s32 i; + s32 bgId; + + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (this->actionTimer == 0) { + for (i = 0; i < sNumSpawned; i++) { + if (fabsf(sSpawnPositions[i].x - player->actor.world.pos.x) < 150.0f && + fabsf(sSpawnPositions[i].z - player->actor.world.pos.z) < 150.0f) { + if (Flags_GetSwitch(globalCtx, sSpawnSwitchFlags[i])) { + if (player->stateFlags1 & 0x800000) { // Player riding Epona + return; + } else { + this->actor.params = EN_PO_FIELD_SMALL; + spawnDist = 300.0f; + } + } else if (player->stateFlags1 & 0x800000 || Rand_ZeroOne() < 0.4f) { + this->actor.params = EN_PO_FIELD_BIG; + this->spawnFlagIndex = i; + spawnDist = 480.0f; + } else { + this->actor.params = EN_PO_FIELD_SMALL; + spawnDist = 300.0f; + } + this->actor.world.pos.x = Math_SinS(player->actor.shape.rot.y) * spawnDist + player->actor.world.pos.x; + this->actor.world.pos.z = Math_CosS(player->actor.shape.rot.y) * spawnDist + player->actor.world.pos.z; + this->actor.world.pos.y = player->actor.world.pos.y + 1000.0f; + this->actor.world.pos.y = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId, + &this->actor, &this->actor.world.pos); + if (this->actor.world.pos.y != BGCHECK_Y_MIN) { + this->actor.shape.rot.y = Actor_WorldYawTowardActor(&this->actor, &player->actor); + EnPoField_SetupAppear(this); + } else { + return; + } + } + } + } +} + +void EnPoField_Appear(EnPoField* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->lightColor.a = 255; + Actor_SetScale(&this->actor, this->scaleModifier); + if (this->actor.params == EN_PO_FIELD_BIG) { + EnPoField_SetupFlee(this); + } else { + EnPoField_SetupCirclePlayer(this, globalCtx); + } + } else if (this->skelAnime.curFrame > 10.0f) { + this->lightColor.a = ((this->skelAnime.curFrame - 10.0f) * 0.05f) * 255.0f; + } else { + this->actor.scale.x += this->scaleModifier * 0.1f; + this->actor.scale.y = this->actor.scale.x; + this->actor.scale.z = this->actor.scale.x; + } + this->actor.shape.shadowAlpha = this->lightColor.a; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + if (this->actor.params == EN_PO_FIELD_BIG) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; + EnPoField_SetFleeSpeed(this, globalCtx); + } +} + +void EnPoField_CirclePlayer(EnPoField* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 temp_v1 = 16 - this->unk_194; + + SkelAnime_Update(&this->skelAnime); + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (ABS(temp_v1) < 16) { + this->actor.world.rot.y += 512.0f * fabsf(Math_SinS(this->unk_194 * 0x800)); + } + Math_ApproachF(&this->scaleModifier, 180.0f, 0.5f, 10.0f); + Math_ApproachF(&this->actor.home.pos.x, player->actor.world.pos.x, 0.2f, 6.0f); + Math_ApproachF(&this->actor.home.pos.z, player->actor.world.pos.z, 0.2f, 6.0f); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 1, 0x800, 0x200); + if (this->actor.home.pos.x - player->actor.world.pos.x > 100.0f) { + this->actor.home.pos.x = player->actor.world.pos.x + 100.0f; + } else if (this->actor.home.pos.x - player->actor.world.pos.x < -100.0f) { + this->actor.home.pos.x = player->actor.world.pos.x + -100.0f; + } + if (this->actor.home.pos.z - player->actor.world.pos.z > 100.0f) { + this->actor.home.pos.z = player->actor.world.pos.z + 100.0f; + } else if (this->actor.home.pos.z - player->actor.world.pos.z < -100.0f) { + this->actor.home.pos.z = player->actor.world.pos.z + -100.0f; + } + this->actor.world.pos.x = this->actor.home.pos.x - (Math_SinS(this->actor.world.rot.y) * this->scaleModifier); + this->actor.world.pos.z = this->actor.home.pos.z - (Math_CosS(this->actor.world.rot.y) * this->scaleModifier); + if (this->actionTimer == 0) { + EnPoField_SetupDisappear(this); + } else { + EnPoField_SpawnFlame(this); + } + EnPoField_CorrectYPos(this, globalCtx); + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); +} + +void EnPoField_Flee(EnPoField* this, GlobalContext* globalCtx) { + f32 temp_f6; + s16 phi_t0; + + SkelAnime_Update(&this->skelAnime); + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (Actor_WorldDistXZToPoint(&this->actor, &sFieldMiddle) > 3000.0f) { + phi_t0 = (s16)(this->actor.yawTowardsPlayer - Actor_WorldYawTowardPoint(&this->actor, &sFieldMiddle) - 0x8000) * + 0.2f; + } else { + phi_t0 = 0; + } + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer - phi_t0, 6, 0x400); + EnPoField_SetFleeSpeed(this, globalCtx); + this->actor.world.rot.y = this->actor.shape.rot.y + 0x8000; + temp_f6 = Math_SinS(this->actionTimer * 0x800) * 3.0f; + this->actor.world.pos.x -= temp_f6 * Math_CosS(this->actor.shape.rot.y); + this->actor.world.pos.z += temp_f6 * Math_SinS(this->actor.shape.rot.y); + if (this->actionTimer == 0 || this->actor.xzDistToPlayer > 1500.0f) { + EnPoField_SetupDisappear(this); + } else { + EnPoField_CorrectYPos(this, globalCtx); + } + func_8002F974(&this->actor, NA_SE_EN_PO_AWAY - SFX_FLAG); +} + +void EnPoField_Damage(EnPoField* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.colChkInfo.health == 0) { + EnPoField_SetupDeath(this); + } else if (this->actor.params == EN_PO_FIELD_BIG) { + EnPoField_SetupFlee(this); + } else { + EnPoField_SetupCirclePlayer(this, globalCtx); + } + } +} + +void EnPoField_Death(EnPoField* this, GlobalContext* globalCtx) { + Vec3f sp6C; + f32 sp68; + s32 pad; + s32 pad1; + f32 temp_f0; + + this->actionTimer++; + if (this->actionTimer < 8) { + if (this->actionTimer < 5) { + sp6C.y = Math_SinS(this->actionTimer * 0x1000 - 0x4000) * 23.0f + (this->actor.world.pos.y + 40.0f); + sp68 = Math_CosS(this->actionTimer * 0x1000 - 0x4000) * 23.0f; + sp6C.x = + Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * sp68 + this->actor.world.pos.x; + sp6C.z = + Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * sp68 + this->actor.world.pos.z; + } else { + sp6C.y = this->actor.world.pos.y + 40.0f + 15.0f * (this->actionTimer - 5); + sp6C.x = + Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * 23.0f + this->actor.world.pos.x; + sp6C.z = + Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * 23.0f + this->actor.world.pos.z; + } + EffectSsDeadDb_Spawn(globalCtx, &sp6C, &D_80AD7114, &D_80AD7120, this->actionTimer * 10 + 80, 0, 255, 255, 255, + 255, 0, 0, 255, 1, 9, 1); + sp6C.x = (this->actor.world.pos.x + this->actor.world.pos.x) - sp6C.x; + sp6C.z = (this->actor.world.pos.z + this->actor.world.pos.z) - sp6C.z; + EffectSsDeadDb_Spawn(globalCtx, &sp6C, &D_80AD7114, &D_80AD7120, this->actionTimer * 10 + 80, 0, 255, 255, 255, + 255, 0, 0, 255, 1, 9, 1); + sp6C.x = this->actor.world.pos.x; + sp6C.z = this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &sp6C, &D_80AD7114, &D_80AD7120, this->actionTimer * 10 + 80, 0, 255, 255, 255, + 255, 0, 0, 255, 1, 9, 1); + if (this->actionTimer == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EXTINCT); + } + } else if (this->actionTimer == 28) { + EnPoField_SetupSoulIdle(this, globalCtx); + } else if (this->actionTimer >= 19) { + temp_f0 = (28 - this->actionTimer) * 0.001f; + this->actor.world.pos.y += 5.0f; + this->actor.scale.z = temp_f0; + this->actor.scale.y = temp_f0; + this->actor.scale.x = temp_f0; + } + if (this->actionTimer == 18) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DEAD2); + } +} + +void EnPoField_Disappear(EnPoField* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->actionTimer != 0) { + this->actionTimer--; + } + this->actor.shape.rot.y += 0x1000; + this->lightColor.a = this->actionTimer * 15.9375f; + this->actor.shape.shadowAlpha = this->lightColor.a; + if (this->actionTimer == 0) { + EnPoField_SetupWaitForSpawn(this, globalCtx); + } +} + +void EnPoField_SoulIdle(EnPoField* this, GlobalContext* globalCtx) { + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (this->actor.bgCheckFlags & 1) { + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 6.0f, 0, 1, 1, 15, OBJECT_PO_FIELD, 10, + gPoeFieldLanternDL); + func_80AD42B0(this); + } else if (this->actionTimer == 0) { + EnPoField_SetupWaitForSpawn(this, globalCtx); + } + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 10.0f, 4); +} + +void EnPoField_SoulUpdateProperties(EnPoField* this, s32 arg1) { + EnPoFieldInfo* info = &sPoFieldInfo[this->actor.params]; + f32 multiplier; + + this->lightColor.a = CLAMP(this->lightColor.a + arg1, 0, 255); + if (arg1 < 0) { + multiplier = this->lightColor.a * (1.0f / 255); + this->actor.scale.x = this->actor.scale.z = 0.0056000003f * multiplier + 0.0014000001f; + this->actor.scale.y = 0.007f - 0.007f * multiplier + 0.007f; + } else { + multiplier = 1.0f; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = this->lightColor.a * (0.007f / 255); + this->actor.world.pos.y = this->actor.home.pos.y + ((1.0f / 17.0f) * this->lightColor.a); + } + this->lightColor.r = info->lightColor.r * multiplier; + this->lightColor.g = info->lightColor.g * multiplier; + this->lightColor.b = info->lightColor.b * multiplier; + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, info->lightColor.r, info->lightColor.g, info->lightColor.b, + this->lightColor.a * (200.0f / 255)); +} + +void func_80AD587C(EnPoField* this, GlobalContext* globalCtx) { + this->actor.home.pos.y += 2.0f; + EnPoField_SoulUpdateProperties(this, 20); + if (this->lightColor.a == 255) { + func_80AD4384(this); + } +} + +void func_80AD58D4(EnPoField* this, GlobalContext* globalCtx) { + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + EnPoField_SetupInteractWithSoul(this); + return; + } + if (this->actionTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + this->actor.flags &= ~ACTOR_FLAG_16; + EnPoField_SetupSoulDisappear(this); + return; + } + if (this->collider.base.ocFlags1 & OC1_HIT) { + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2F4(&this->actor, globalCtx); + } else { + this->actor.flags &= ~ACTOR_FLAG_16; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + this->actor.world.pos.y = Math_SinS(this->unk_194 * 0x800) * 5.0f + this->actor.home.pos.y; + if (this->unk_194 != 0) { + this->unk_194 -= 1; + } + if (this->unk_194 == 0) { + this->unk_194 = 32; + } + this->collider.dim.pos.y = this->actor.world.pos.y - 20.0f; + Actor_SetFocus(&this->actor, -10.0f); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->lightInfo.params.point.color[0], + this->lightInfo.params.point.color[1], this->lightInfo.params.point.color[2], + this->lightColor.a * (200.0f / 255)); +} + +void EnPoField_SoulDisappear(EnPoField* this, GlobalContext* globalCtx) { + EnPoField_SoulUpdateProperties(this, -13); + if (this->lightColor.a == 0) { + EnPoField_SetupWaitForSpawn(this, globalCtx); + } +} + +void EnPoField_SoulInteract(EnPoField* this, GlobalContext* globalCtx) { + if (this->actor.textId != 0x5005) { + EnPoField_SoulUpdateProperties(this, -13); + } else { + func_8002F974(&this->actor, NA_SE_EN_PO_BIG_CRY - SFX_FLAG); + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) { + if (Message_ShouldAdvance(globalCtx)) { + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_PO_BIG_CRY - SFX_FLAG); + if (globalCtx->msgCtx.choiceIndex == 0) { + if (Inventory_HasEmptyBottle()) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_BIG_GET); + if (this->actor.params == 0) { + Item_Give(globalCtx, ITEM_POE); + this->actor.textId = 0x5008; + } else { + this->actor.textId = 0x508F; + Item_Give(globalCtx, ITEM_BIG_POE); + Flags_SetSwitch(globalCtx, sSpawnSwitchFlags[this->spawnFlagIndex]); + } + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + this->actor.textId = 0x5006; + } + } else { + this->actor.textId = 0x5007; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + return; + } + } else if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + EnPoField_SetupSoulDisappear(this); + } +} + +void EnPoField_TestForDamage(EnPoField* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DEAD); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DAMAGE); + } + EnPoField_SetupDamage(this); + } + } +} + +void EnPoField_SpawnFlame(EnPoField* this) { + if (this->flameTimer == 0) { + this->flamePosition.x = this->lightInfo.params.point.x; + this->flamePosition.y = this->lightInfo.params.point.y; + this->flamePosition.z = this->lightInfo.params.point.z; + this->flameTimer = 70; + this->flameRotation = this->actor.shape.rot.y; + } +} + +void EnPoField_UpdateFlame(EnPoField* this, GlobalContext* globalCtx) { + if (this->flameTimer != 0) { + if (this->flameTimer != 0) { + this->flameTimer--; + } + if (this->flameCollider.base.atFlags & AT_HIT) { + this->flameCollider.base.atFlags &= ~AT_HIT; + this->flameTimer = 19; + } + if (this->flameTimer < 20) { + Math_StepToF(&this->flameScale, 0.0f, 0.00015f); + return; + } + if (Math_StepToF(&this->flameScale, 0.003f, 0.0006f) != 0) { + this->flamePosition.x += 2.5f * Math_SinS(this->flameRotation); + this->flamePosition.z += 2.5f * Math_CosS(this->flameRotation); + } + this->flameCollider.dim.pos.x = this->flamePosition.x; + this->flameCollider.dim.pos.y = this->flamePosition.y; + this->flameCollider.dim.pos.z = this->flamePosition.z; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->flameCollider.base); + } +} + +void EnPoField_DrawFlame(EnPoField* this, GlobalContext* globalCtx) { + f32 sp4C; + s32 pad; + + if (this->flameTimer != 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_field.c", 1669); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + (globalCtx->gameplayFrames * -20) % 512, 32, 128)); + sp4C = this->flameScale * 85000.0f; + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 0, sp4C); + Matrix_Translate(this->flamePosition.x, this->flamePosition.y, this->flamePosition.z, MTXMODE_NEW); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * (M_PI / 0x8000), MTXMODE_APPLY); + if (this->flameTimer >= 20) { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + Matrix_Scale(this->flameScale, this->flameScale, this->flameScale, MTXMODE_APPLY); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, sp4C, 0, 0, 0); + Matrix_Scale((this->flameScale * 0.7f) + 0.00090000004f, (0.003f - this->flameScale) + 0.003f, 0.003f, + MTXMODE_APPLY); + } + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_field.c", 1709), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_field.c", 1712); + } +} + +void func_80AD619C(EnPoField* this) { + s16 temp_var; + + if (this->actionFunc == EnPoField_Flee) { + this->lightColor.r = CLAMP_MAX((s16)(this->lightColor.r + 5), 80); + this->lightColor.g = CLAMP_MAX((s16)(this->lightColor.g + 5), 255); + temp_var = this->lightColor.b + 5; + this->lightColor.b = CLAMP_MAX(temp_var, 225); + } else if (this->actionFunc == EnPoField_Damage) { + if (this->actor.colorFilterTimer & 2) { + this->lightColor.r = 0; + this->lightColor.g = 0; + this->lightColor.b = 0; + } else { + this->lightColor.r = 80; + this->lightColor.g = 255; + this->lightColor.b = 225; + } + } else { + this->lightColor.r = CLAMP_MAX((s16)(this->lightColor.r + 5), 255); + this->lightColor.g = CLAMP_MAX((s16)(this->lightColor.g + 5), 255); + if (this->lightColor.b > 210) { + temp_var = this->lightColor.b - 5; + this->lightColor.b = CLAMP_MIN(temp_var, 210); + } else { + temp_var = this->lightColor.b + 5; + this->lightColor.b = CLAMP_MAX(temp_var, 210); + } + } +} + +void func_80AD6330(EnPoField* this) { + f32 rand; + + if (this->actionFunc == EnPoField_Appear && this->skelAnime.curFrame < 12.0f) { + this->soulColor.r = this->soulColor.g = this->soulColor.b = (s16)(this->skelAnime.curFrame * 16.66f) + 55; + this->soulColor.a = this->skelAnime.curFrame * (100.0f / 6.0f); + } else { + rand = Rand_ZeroOne(); + this->soulColor.r = (s16)(rand * 30.0f) + 225; + this->soulColor.g = (s16)(rand * 100.0f) + 155; + this->soulColor.b = (s16)(rand * 160.0f) + 95; + this->soulColor.a = 200; + } +} + +void EnPoField_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnPoField* this = (EnPoField*)thisx; + + EnPoField_TestForDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + EnPoField_UpdateFlame(this, globalCtx); + if (this->actionFunc == EnPoField_Flee || this->actionFunc == EnPoField_Damage || + this->actionFunc == EnPoField_Appear) { + Actor_MoveForward(&this->actor); + } + if (this->actionFunc != EnPoField_WaitForSpawn) { + Actor_SetFocus(&this->actor, 42.0f); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 27.0f, 60.0f, 4); + func_80AD619C(this); + func_80AD6330(this); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +s32 EnPoField_OverrideLimbDraw2(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx, Gfx** gfxP) { + EnPoField* this = (EnPoField*)thisx; + + if (this->lightColor.a == 0 || limbIndex == 7 || (this->actionFunc == EnPoField_Death && this->actionTimer >= 2)) { + *dList = NULL; + } else if (this->actor.params == EN_PO_FIELD_BIG) { + if (limbIndex == 1) { + *dList = gBigPoeFaceDL; + } else if (limbIndex == 8) { + *dList = gBigPoeCloakDL; + } else if (limbIndex == 9) { + *dList = gBigPoeBodyDL; + } + } + if (this->actionFunc == EnPoField_Disappear && limbIndex == 7) { + Matrix_Scale(this->actionTimer / 16.0f, this->actionTimer / 16.0f, this->actionTimer / 16.0f, MTXMODE_APPLY); + } + return false; +} + +void EnPoField_PostLimDraw2(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfxP) { + EnPoField* this = (EnPoField*)thisx; + + if (this->actionFunc == EnPoField_Death && this->actionTimer >= 2 && limbIndex == 8) { + gSPMatrix((*gfxP)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_field.c", 1916), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList((*gfxP)++, gPoeFieldBurnDL); + } + if (limbIndex == 7) { + Vec3f vec; + Matrix_MultVec3f(&D_80AD714C, &vec); + if (this->actionFunc == EnPoField_Death && this->actionTimer >= 19 && this->actor.scale.x != 0.0f) { + f32 mtxScale = 0.01f / this->actor.scale.x; + Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); + } + Matrix_Get(&sLimb7Mtx); + if (this->actionFunc == EnPoField_Death && this->actionTimer == 27) { + this->actor.world.pos.x = sLimb7Mtx.xw; + this->actor.world.pos.y = sLimb7Mtx.yw; + this->actor.world.pos.z = sLimb7Mtx.zw; + } + Lights_PointGlowSetInfo(&this->lightInfo, vec.x, vec.y, vec.z, this->soulColor.r, this->soulColor.g, + this->soulColor.b, this->soulColor.a * (200.0f / 255)); + } +} + +void EnPoField_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnPoField* this = (EnPoField*)thisx; + EnPoFieldInfo* info = &sPoFieldInfo[this->actor.params]; + + if (this->actionFunc != EnPoField_WaitForSpawn) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_field.c", 1976); + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_EnvColor(globalCtx->state.gfxCtx, info->envColor.r, info->envColor.g, info->envColor.b, 255)); + if (this->lightColor.a == 255 || this->lightColor.a == 0) { + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_EnvColor(globalCtx->state.gfxCtx, this->lightColor.r, this->lightColor.g, this->lightColor.b, + this->lightColor.a)); + gSPSegment(POLY_OPA_DISP++, 0x0C, D_80116280 + 2); + POLY_OPA_DISP = + SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoField_OverrideLimbDraw2, EnPoField_PostLimDraw2, &this->actor, POLY_OPA_DISP); + } else { + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_EnvColor(globalCtx->state.gfxCtx, this->lightColor.r, this->lightColor.g, this->lightColor.b, + this->lightColor.a)); + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + POLY_XLU_DISP = + SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoField_OverrideLimbDraw2, EnPoField_PostLimDraw2, &this->actor, POLY_XLU_DISP); + } + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, this->soulColor.r, this->soulColor.g, this->soulColor.b, 255); + Matrix_Put(&sLimb7Mtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_field.c", 2033), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gPoeFieldLanternDL); + gSPDisplayList(POLY_OPA_DISP++, gPoeFieldLanternTopDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_field.c", 2039); + } + EnPoField_DrawFlame(this, globalCtx); +} + +void EnPoField_UpdateDead(Actor* thisx, GlobalContext* globalCtx) { + EnPoField* this = (EnPoField*)thisx; + + this->actionFunc(this, globalCtx); + if (this->actionFunc == EnPoField_SoulIdle) { + func_80AD6330(this); + } + EnPoField_UpdateFlame(this, globalCtx); +} + +void EnPoField_DrawSoul(Actor* thisx, GlobalContext* globalCtx) { + EnPoField* this = (EnPoField*)thisx; + s32 pad; + EnPoFieldInfo* info = &sPoFieldInfo[this->actor.params]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_field.c", 2077); + if (this->actionFunc == EnPoField_SoulIdle) { + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_EnvColor(globalCtx->state.gfxCtx, info->envColor.r, info->envColor.g, info->envColor.b, 255)); + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->soulColor.r, this->soulColor.g, this->soulColor.b, 200); + gDPSetEnvColor(POLY_OPA_DISP++, this->soulColor.r, this->soulColor.g, this->soulColor.b, 255); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_field.c", 2104), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gPoeFieldLanternDL); + gSPDisplayList(POLY_OPA_DISP++, gPoeFieldLanternTopDL); + } else { + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + (globalCtx->gameplayFrames * info->unk_9) & 0x1FF, 0x20, 0x80)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(info->soulTexture)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, info->primColor.r, info->primColor.g, info->primColor.b, + this->lightColor.a); + gDPSetEnvColor(POLY_XLU_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, 255); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * 9.58738e-05f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_field.c", 2143), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gPoeFieldSoulDL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_field.c", 2149); + EnPoField_DrawFlame(this, globalCtx); +} + +void EnPoField_Reset(void) { + sNumSpawned = 0; + + memset(sSpawnPositions, 0, sizeof(sSpawnPositions)); + memset(sSpawnSwitchFlags, 0, sizeof(sSpawnSwitchFlags)); +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h new file mode 100644 index 000000000..384b1bbdc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Field/z_en_po_field.h @@ -0,0 +1,46 @@ +#ifndef Z_EN_PO_FIELD_H +#define Z_EN_PO_FIELD_H + +#include "ultra64.h" +#include "global.h" + +struct EnPoField; + +typedef void (*EnPoFieldActionFunc)(struct EnPoField*, GlobalContext*); + +typedef enum { + EN_PO_FIELD_SMALL, + EN_PO_FIELD_BIG +} EnPoFieldSize; + +typedef struct { + /* 0x0000 */ Color_RGB8 primColor; + /* 0x0003 */ Color_RGB8 lightColor; + /* 0x0006 */ Color_RGB8 envColor; + /* 0x0009 */ s8 unk_9; + /* 0x000C */ void* soulTexture; +} EnPoFieldInfo; // size = 0x10 + +typedef struct EnPoField { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnPoFieldActionFunc actionFunc; + /* 0x0194 */ u8 unk_194; + /* 0x0195 */ u8 spawnFlagIndex; + /* 0x0196 */ s16 actionTimer; + /* 0x0198 */ s16 flameRotation; + /* 0x019A */ s16 flameTimer; + /* 0x019C */ Vec3s jointTable[10]; + /* 0x01D8 */ Vec3s morphTable[10]; + /* 0x0214 */ Color_RGBA8 lightColor; + /* 0x0214 */ Color_RGBA8 soulColor; + /* 0x021C */ f32 scaleModifier; + /* 0x0220 */ f32 flameScale; + /* 0x0224 */ Vec3f flamePosition; + /* 0x0230 */ LightNode* lightNode; + /* 0x0234 */ LightInfo lightInfo; + /* 0x0244 */ ColliderCylinder collider; + /* 0x0290 */ ColliderCylinder flameCollider; +} EnPoField; // size = 0x02DC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c new file mode 100644 index 000000000..a2e3e31bd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.c @@ -0,0 +1,415 @@ +/* + * File: z_en_po_relay.c + * Overlay: ovl_En_Po_Relay + * Description: Dampé's Ghost + */ + +#include "z_en_po_relay.h" +#include "overlays/actors/ovl_En_Honotrap/z_en_honotrap.h" +#include "objects/object_tk/object_tk.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_12 | ACTOR_FLAG_16) + +void EnPoRelay_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPoRelay_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPoRelay_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPoRelay_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnPoRelay_Idle(EnPoRelay* this, GlobalContext* globalCtx); +void EnPoRelay_Race(EnPoRelay* this, GlobalContext* globalCtx); +void EnPoRelay_EndRace(EnPoRelay* this, GlobalContext* globalCtx); +void EnPoRelay_Talk(EnPoRelay* this, GlobalContext* globalCtx); +void EnPoRelay_Talk2(EnPoRelay* this, GlobalContext* globalCtx); +void EnPoRelay_DisappearAndReward(EnPoRelay* this, GlobalContext* globalCtx); +void EnPoRelay_SetupIdle(EnPoRelay* this); + +static Vec3s D_80AD8C30[] = { + { 0xFFC4, 0xFDEE, 0xF47A }, { 0x0186, 0xFE0C, 0xF47A }, { 0x0186, 0xFE0C, 0xF0F6 }, { 0x00D2, 0xFDEE, 0xF0F6 }, + { 0x00D2, 0xFD9E, 0xEEDA }, { 0x023A, 0xFDC6, 0xEEDA }, { 0x023A, 0xFDC6, 0xED18 }, { 0x00D2, 0xFDC6, 0xED18 }, + { 0x00D2, 0xFDC6, 0xEBCE }, { 0x00D2, 0xFDC6, 0xEAA2 }, { 0x023A, 0xFDC6, 0xEAA2 }, { 0x023A, 0xFDC6, 0xEBB0 }, + { 0x04EC, 0xFD9E, 0xEBB0 }, { 0x0672, 0xFD62, 0xED18 }, { 0x0672, 0xFD30, 0xEE80 }, { 0x07DA, 0xFD26, 0xEE80 }, + { 0x07DA, 0xFD26, 0xEF70 }, { 0x07DA, 0xFD26, 0xF204 }, { 0x0672, 0xFD44, 0xF204 }, { 0x0672, 0xFD6C, 0xF3C6 }, + { 0x088E, 0xFD6C, 0xF3C6 }, { 0x088E, 0xFDB2, 0xF5E2 }, { 0x099C, 0xFDD0, 0xF5E2 }, { 0x0B54, 0xFE66, 0xF772 }, + { 0x0B4E, 0xFE66, 0xF87E }, { 0x0B4A, 0xFE66, 0xF97A }, { 0x0B4A, 0xFE98, 0xF9FC }, { 0x0BAE, 0xFE98, 0xF9FC }, +}; + +const ActorInit En_Po_Relay_InitVars = { + ACTOR_EN_PO_RELAY, + ACTORCAT_NPC, + FLAGS, + OBJECT_TK, + sizeof(EnPoRelay), + (ActorFunc)EnPoRelay_Init, + (ActorFunc)EnPoRelay_Destroy, + (ActorFunc)EnPoRelay_Update, + (ActorFunc)EnPoRelay_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 30, 52, 0, { 0, 0, 0 } }, +}; + +static s32 D_80AD8D24 = 0; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x4F, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 1500, ICHAIN_STOP), +}; + +static Vec3f D_80AD8D30 = { 0.0f, 1.5f, 0.0f }; + +static Vec3f D_80AD8D3C = { 0.0f, 0.0f, 0.0f }; + +static Vec3f D_80AD8D48 = { 0.0f, 1200.0f, 0.0f }; + +static void* sEyesTextures[] = { + gDampeEyeOpenTex, + gDampeEyeHalfTex, + gDampeEyeClosedTex, +}; + +void EnPoRelay_Init(Actor* thisx, GlobalContext* globalCtx) { + EnPoRelay* this = (EnPoRelay*)thisx; + s32 temp; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 42.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDampeSkel, &gDampeFloatAnim, this->jointTable, this->morphTable, + 18); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, + 255, 255, 255, 200); + this->lightColor.a = 255; + temp = 1; + if (D_80AD8D24 != 0) { + Actor_Kill(&this->actor); + } else { + D_80AD8D24 = temp; + Actor_SetTextWithPrefix(globalCtx, &this->actor, 65); + this->textId = this->actor.textId; + EnPoRelay_SetupIdle(this); + } + this->actor.params &= 0x3F; +} + +void EnPoRelay_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPoRelay* this = (EnPoRelay*)thisx; + + D_80AD8D24 = 0; + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnPoRelay_SetupIdle(EnPoRelay* this) { + this->unk_195 = 32; + this->pathIndex = 0; + this->actor.room = -1; + this->actor.shape.rot.y = 0; + this->actor.world.rot.y = -0x8000; + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actionFunc = EnPoRelay_Idle; +} + +void EnPoRelay_Vec3sToVec3f(Vec3f* dest, Vec3s* src) { + dest->x = src->x; + dest->y = src->y; + dest->z = src->z; +} + +void EnPoRelay_SetupRace(EnPoRelay* this) { + Vec3f vec; + + EnPoRelay_Vec3sToVec3f(&vec, &D_80AD8C30[this->pathIndex]); + this->actionTimer = ((s16)(this->actor.shape.rot.y - this->actor.world.rot.y - 0x8000) >> 0xB) % 32U; + func_80088B34(0); + this->hookshotSlotFull = INV_CONTENT(ITEM_HOOKSHOT) != ITEM_NONE; + this->unk_19A = Actor_WorldYawTowardPoint(&this->actor, &vec); + this->actor.flags |= ACTOR_FLAG_27; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + this->actionFunc = EnPoRelay_Race; +} + +void EnPoRelay_SetupEndRace(EnPoRelay* this) { + this->actor.world.rot.y = this->actor.home.rot.y + 0xC000; + this->actor.flags &= ~ACTOR_FLAG_27; + this->actor.speedXZ = 0.0f; + this->actionFunc = EnPoRelay_EndRace; +} + +void EnPoRelay_CorrectY(EnPoRelay* this) { + Math_StepToF(&this->actor.home.pos.y, D_80AD8C30[(this->pathIndex >= 28) ? 27 : this->pathIndex].y + 45.0f, 2.0f); + this->actor.world.pos.y = Math_SinS(this->unk_195 * 0x800) * 8.0f + this->actor.home.pos.y; +} + +void EnPoRelay_Idle(EnPoRelay* this, GlobalContext* globalCtx) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x100); + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actor.flags &= ~ACTOR_FLAG_16; + this->actionFunc = EnPoRelay_Talk; + } else if (this->actor.xzDistToPlayer < 250.0f) { + this->actor.flags |= ACTOR_FLAG_16; + this->actor.textId = this->textId; + func_8002F2CC(&this->actor, globalCtx, 250.0f); + } + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); +} + +void EnPoRelay_Talk(EnPoRelay* this, GlobalContext* globalCtx) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x100); + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + Actor_SetTextWithPrefix(globalCtx, &this->actor, 0x2F); + this->textId = this->actor.textId; + EnPoRelay_SetupRace(this); + } + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); +} + +void EnPoRelay_Race(EnPoRelay* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f vec; + f32 speed; + f32 multiplier; + + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (this->actionTimer == 0 && Rand_ZeroOne() < 0.03f) { + this->actionTimer = 32; + if (this->pathIndex < 23) { + speed = Rand_ZeroOne() * 3.0f; + if (speed < 1.0f) { + multiplier = 1.0f; + } else if (speed < 2.0f) { + multiplier = -1.0f; + } else { + multiplier = 0.0f; + } + speed = 30.0f * multiplier; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HONOTRAP, + Math_CosS(this->unk_19A) * speed + this->actor.world.pos.x, this->actor.world.pos.y, + Math_SinS(this->unk_19A) * speed + this->actor.world.pos.z, 0, + (this->unk_19A + 0x8000) - (0x2000 * multiplier), 0, HONOTRAP_FLAME_DROP); + } + } + Math_SmoothStepToS(&this->actor.world.rot.y, this->unk_19A, 2, 0x1000, 0x100); + this->actor.shape.rot.y = this->actor.world.rot.y + (this->actionTimer * 0x800) + 0x8000; + if (this->pathIndex < 23) { + // If the player travels along a different path to Dampé that converges later + if ((Math3D_PointInSquare2D(660.0f, 840.0f, -4480.0f, -3760.0f, player->actor.world.pos.x, + player->actor.world.pos.z) != 0) || + (Math3D_PointInSquare2D(1560.0f, 1740.0f, -4030.0f, -3670.0f, player->actor.world.pos.x, + player->actor.world.pos.z) != 0) || + (Math3D_PointInSquare2D(1580.0f, 2090.0f, -3030.0f, -2500.0f, player->actor.world.pos.x, + player->actor.world.pos.z) != 0)) { + speed = (this->hookshotSlotFull) ? player->actor.speedXZ * 1.4f : player->actor.speedXZ * 1.2f; + } else if (this->actor.xzDistToPlayer < 150.0f) { + speed = (this->hookshotSlotFull) ? player->actor.speedXZ * 1.2f : player->actor.speedXZ; + } else if (this->actor.xzDistToPlayer < 300.0f) { + speed = (this->hookshotSlotFull) ? player->actor.speedXZ : player->actor.speedXZ * 0.8f; + } else if (this->hookshotSlotFull) { + speed = 4.5f; + } else { + speed = 3.5f; + } + multiplier = 250.0f - this->actor.xzDistToPlayer; + multiplier = CLAMP_MIN(multiplier, 0.0f); + speed += multiplier * 0.02f + 1.0f; + Math_ApproachF(&this->actor.speedXZ, speed, 0.5f, 1.5f); + } else { + Math_ApproachF(&this->actor.speedXZ, 3.5f, 0.5f, 1.5f); + } + EnPoRelay_Vec3sToVec3f(&vec, &D_80AD8C30[this->pathIndex]); + if (Actor_WorldDistXZToPoint(&this->actor, &vec) < 40.0f) { + this->pathIndex++; + EnPoRelay_Vec3sToVec3f(&vec, &D_80AD8C30[this->pathIndex]); + if (this->pathIndex == 28) { + EnPoRelay_SetupEndRace(this); + } else if (this->pathIndex == 9) { + Flags_SetSwitch(globalCtx, 0x35); + } else if (this->pathIndex == 17) { + Flags_SetSwitch(globalCtx, 0x36); + } else if (this->pathIndex == 25) { + Flags_SetSwitch(globalCtx, 0x37); + } + } + this->unk_19A = Actor_WorldYawTowardPoint(&this->actor, &vec); + func_8002F974(&this->actor, NA_SE_EN_PO_AWAY - SFX_FLAG); +} + +void EnPoRelay_EndRace(EnPoRelay* this, GlobalContext* globalCtx) { + Math_ScaledStepToS(&this->actor.shape.rot.y, -0x4000, 0x800); + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnPoRelay_Talk2; + } else if (globalCtx->roomCtx.curRoom.num == 5) { + Actor_Kill(&this->actor); + gSaveContext.timer1State = 0; + } else if (Actor_IsFacingAndNearPlayer(&this->actor, 150.0f, 0x3000)) { + this->actor.textId = this->textId; + func_8002F2CC(&this->actor, globalCtx, 250.0f); + } + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); +} + +void EnPoRelay_Talk2(EnPoRelay* this, GlobalContext* globalCtx) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0x100); + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) { + if (Message_ShouldAdvance(globalCtx)) { + if (this->hookshotSlotFull != 0) { + Actor_SetTextWithPrefix(globalCtx, &this->actor, 0x2E); + } else { + Actor_SetTextWithPrefix(globalCtx, &this->actor, 0x2D); + } + this->textId = this->actor.textId; + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + } else if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + gSaveContext.timer1State = 0; + this->actionTimer = 0; + this->actionFunc = EnPoRelay_DisappearAndReward; + } + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); +} + +void EnPoRelay_DisappearAndReward(EnPoRelay* this, GlobalContext* globalCtx) { + Vec3f vec; + f32 multiplier; + s32 pad; + Vec3f sp60; + s32 pad1; + + this->actionTimer++; + if (this->actionTimer < 8) { + if (this->actionTimer < 5) { + vec.y = Math_SinS((this->actionTimer * 0x1000) - 0x4000) * 23.0f + (this->actor.world.pos.y + 40.0f); + multiplier = Math_CosS((this->actionTimer * 0x1000) - 0x4000) * 23.0f; + vec.x = (Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * multiplier) + + this->actor.world.pos.x; + vec.z = (Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * multiplier) + + this->actor.world.pos.z; + } else { + vec.y = this->actor.world.pos.y + 40.0f + 15.0f * (this->actionTimer - 5); + vec.x = + (Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * 23.0f) + this->actor.world.pos.x; + vec.z = + (Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * 23.0f) + this->actor.world.pos.z; + } + EffectSsDeadDb_Spawn(globalCtx, &vec, &D_80AD8D30, &D_80AD8D3C, this->actionTimer * 10 + 80, 0, 255, 255, 255, + 255, 0, 0, 255, 1, 9, true); + vec.x = (this->actor.world.pos.x + this->actor.world.pos.x) - vec.x; + vec.z = (this->actor.world.pos.z + this->actor.world.pos.z) - vec.z; + EffectSsDeadDb_Spawn(globalCtx, &vec, &D_80AD8D30, &D_80AD8D3C, this->actionTimer * 10 + 80, 0, 255, 255, 255, + 255, 0, 0, 255, 1, 9, true); + vec.x = this->actor.world.pos.x; + vec.z = this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &vec, &D_80AD8D30, &D_80AD8D3C, this->actionTimer * 10 + 80, 0, 255, 255, 255, + 255, 0, 0, 255, 1, 9, true); + if (this->actionTimer == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EXTINCT); + } + } + if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.001f) != 0) { + if (this->hookshotSlotFull != 0) { + sp60.x = this->actor.world.pos.x; + sp60.y = this->actor.floorHeight; + sp60.z = this->actor.world.pos.z; + if (gSaveContext.timer1Value < HIGH_SCORE(HS_DAMPE_RACE)) { + HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timer1Value; + } + if (Flags_GetCollectible(globalCtx, this->actor.params) == 0 && gSaveContext.timer1Value <= 60) { + Item_DropCollectible2(globalCtx, &sp60, (this->actor.params << 8) + (0x4000 | ITEM00_HEART_PIECE)); + } else { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ITEM00, sp60.x, sp60.y, sp60.z, 0, 0, 0, 2); + } + } else { + Flags_SetTempClear(globalCtx, 4); + HIGH_SCORE(HS_DAMPE_RACE) = gSaveContext.timer1Value; + } + Actor_Kill(&this->actor); + } + this->actor.scale.y = this->actor.scale.x; + this->actor.scale.z = this->actor.scale.x; + this->actor.world.pos.y += 10.0f; +} + +void EnPoRelay_Update(Actor* thisx, GlobalContext* globalCtx) { + EnPoRelay* this = (EnPoRelay*)thisx; + s32 pad; + + SkelAnime_Update(&this->skelAnime); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + EnPoRelay_CorrectY(this); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 27.0f, 60.0f, 4); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_SetFocus(&this->actor, 50.0f); + if (this->unk_195 != 0) { + this->unk_195 -= 1; + } + if (this->unk_195 == 0) { + this->unk_195 = 32; + } + this->eyeTextureIdx++; + if (this->eyeTextureIdx == 3) { + this->eyeTextureIdx = 0; + } +} + +void EnPoRelay_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnPoRelay* this = (EnPoRelay*)thisx; + + if (limbIndex == 14) { + f32 rand; + Vec3f vec; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 885); + rand = Rand_ZeroOne(); + this->lightColor.r = (s16)(rand * 30.0f) + 225; + this->lightColor.g = (s16)(rand * 100.0f) + 155; + this->lightColor.b = (s16)(rand * 160.0f) + 95; + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, 128); + gSPDisplayList(POLY_OPA_DISP++, gDampeLanternDL); + if (1) {} + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 901); + Matrix_MultVec3f(&D_80AD8D48, &vec); + Lights_PointNoGlowSetInfo(&this->lightInfo, vec.x, vec.y, vec.z, this->lightColor.r, this->lightColor.g, + this->lightColor.b, 200); + } else if (limbIndex == 8) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 916); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 918), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gDampeHaloDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 922); + } +} + +void EnPoRelay_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnPoRelay* this = (EnPoRelay*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 940); + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyesTextures[this->eyeTextureIdx])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, EnPoRelay_PostLimbDraw, &this->actor); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_relay.c", 954); +} diff --git a/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.h b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.h new file mode 100644 index 000000000..5f26aba8b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Relay/z_en_po_relay.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_PO_RELAY_H +#define Z_EN_PO_RELAY_H + +#include "ultra64.h" +#include "global.h" + +struct EnPoRelay; + +typedef void (*EnPoRelayActionFunc)(struct EnPoRelay*, GlobalContext*); + +typedef struct EnPoRelay { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnPoRelayActionFunc actionFunc; + /* 0x0194 */ u8 hookshotSlotFull; + /* 0x0195 */ u8 unk_195; + /* 0x0196 */ s16 actionTimer; + /* 0x0198 */ s16 pathIndex; + /* 0x019A */ s16 unk_19A; + /* 0x019C */ u16 textId; + /* 0x019E */ u16 eyeTextureIdx; + /* 0x01A0 */ Vec3s jointTable[18]; + /* 0x020C */ Vec3s morphTable[18]; + /* 0x0278 */ Color_RGBA8 lightColor; + /* 0x027C */ LightNode* lightNode; + /* 0x0280 */ LightInfo lightInfo; + /* 0x0290 */ ColliderCylinder collider; +} EnPoRelay; // size = 0x02DC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c new file mode 100644 index 000000000..ec5528ef0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.c @@ -0,0 +1,1424 @@ +/* + * File: z_en_po_sisters.c + * Overlay: ovl_En_Po_Sisters + * Description: Forest Temple Four Poe Sisters + */ + +#include "z_en_po_sisters.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_po_sisters/object_po_sisters.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_9 | ACTOR_FLAG_12 | ACTOR_FLAG_14) + +void EnPoSisters_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPoSisters_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPoSisters_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPoSisters_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnPoSisters_Reset(void); + +void func_80ADA094(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADA4A8(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADA530(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADA6A0(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADA7F0(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADA8C0(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADA9E8(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADAAA4(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADAC70(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADAD54(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADAE6C(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADAFC0(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB17C(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB2B8(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB338(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB9F0(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB4B0(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB51C(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADB770(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBB6C(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBBF4(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBC88(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBD38(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBD8C(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBEE8(EnPoSisters* this, GlobalContext* globalCtx); +void func_80ADBF58(EnPoSisters* this, GlobalContext* globalCtx); + +void func_80AD9AA8(EnPoSisters* this, GlobalContext* globalCtx); +void func_80AD9C24(EnPoSisters* this, GlobalContext* globalCtx); + +void func_80AD9D44(EnPoSisters* this); + +static Color_RGBA8 D_80ADD6F0[4] = { + { 255, 170, 255, 255 }, + { 255, 200, 0, 255 }, + { 0, 170, 255, 255 }, + { 170, 255, 0, 255 }, +}; + +static Color_RGBA8 D_80ADD700[4] = { + { 100, 0, 255, 255 }, + { 255, 0, 0, 255 }, + { 0, 0, 255, 255 }, + { 0, 150, 0, 255 }, +}; + +const ActorInit En_Po_Sisters_InitVars = { + ACTOR_EN_PO_SISTERS, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_PO_SISTERS, + sizeof(EnPoSisters), + (ActorFunc)EnPoSisters_Init, + (ActorFunc)EnPoSisters_Destroy, + (ActorFunc)EnPoSisters_Update, + (ActorFunc)EnPoSisters_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x4FC7FFEA, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 18, 60, 15, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 10, 25, 60, 40 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0xF), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0xE), + /* Master sword */ DMG_ENTRY(2, 0xE), + /* Giant's Knife */ DMG_ENTRY(4, 0xE), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0xE), + /* Giant spin */ DMG_ENTRY(4, 0xE), + /* Master spin */ DMG_ENTRY(2, 0xE), + /* Kokiri jump */ DMG_ENTRY(2, 0xE), + /* Giant jump */ DMG_ENTRY(8, 0xE), + /* Master jump */ DMG_ENTRY(4, 0xE), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static s32 D_80ADD784 = 0; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 7, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 6000, ICHAIN_STOP), +}; + +static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f }; + +static s16 D_80ADD79C[4] = { 0xB000, 0xD000, 0x5000, 0x3000 }; + +static Vec3s D_80ADD7A4[4] = { + { -22, 337, -1704 }, + { -431, 879, -3410 }, + { 549, 879, -3410 }, + { 1717, 515, -1340 }, +}; + +static Vec3f D_80ADD7BC = { 120.0f, 250.0f, -1420.0f }; + +static Gfx* D_80ADD7C8[4] = { + gPoeSistersMegBodyDL, + gPoeSistersJoelleBodyDL, + gPoeSistersBethBodyDL, + gPoeSistersAmyBodyDL, +}; + +static Gfx* D_80ADD7D8[4] = { + gPoeSistersMegFaceDL, + gPoeSistersJoelleFaceDL, + gPoeSistersBethFaceDL, + gPoSistersAmyFaceDL, +}; + +static Color_RGBA8 D_80ADD7E8[4] = { + { 80, 0, 100, 0 }, + { 80, 15, 0, 0 }, + { 0, 70, 50, 0 }, + { 70, 70, 0, 0 }, +}; + +static Vec3f D_80ADD7F8 = { 1000.0f, -1700.0f, 0.0f }; + +void EnPoSisters_Init(Actor* thisx, GlobalContext* globalCtx) { + EnPoSisters* this = (EnPoSisters*)thisx; + s32 pad; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 50.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gPoeSistersSkel, &gPoeSistersSwayAnim, this->jointTable, + this->morphTable, 12); + this->unk_22E.r = 255; + this->unk_22E.g = 255; + this->unk_22E.b = 210; + this->unk_22E.a = 255; + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, 0, + 0, 0, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + this->unk_194 = (thisx->params >> 8) & 3; + this->actor.naviEnemyId = this->unk_194 + 0x50; + if (1) {} + this->unk_195 = (thisx->params >> 0xA) & 3; + this->unk_196 = 32; + this->unk_197 = 20; + this->unk_198 = 1; + this->unk_199 = 32; + this->unk_294 = 110.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->actor.params & 0x1000) { + func_80ADA094(this, globalCtx); + } else if (this->unk_194 == 0) { + if (this->unk_195 == 0) { + this->collider.base.ocFlags1 = OC1_ON | OC1_TYPE_PLAYER; + func_80AD9AA8(this, globalCtx); + } else { + this->actor.flags &= ~(ACTOR_FLAG_9 | ACTOR_FLAG_14); + this->collider.info.elemType = ELEMTYPE_UNK4; + this->collider.info.bumper.dmgFlags |= 1; + this->collider.base.ocFlags1 = OC1_NONE; + func_80AD9C24(this, NULL); + } + } else { + func_80AD9D44(this); + } + this->actor.params &= 0x3F; +} + +void EnPoSisters_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPoSisters* this = (EnPoSisters*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + if (this->unk_194 == 0 && this->unk_195 == 0) { + func_800F5B58(); + } + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AD9240(EnPoSisters* this, s32 arg1, Vec3f* arg2) { + f32 temp_f20 = SQ(arg1) * 0.1f; + Vec3f* vec; + s32 i; + + for (i = 0; i < this->unk_198; i++) { + vec = &this->unk_234[i]; + vec->x = arg2->x + Math_SinS((s16)(this->actor.shape.rot.y + (this->unk_19A * 0x800) + i * 0x2000)) * temp_f20; + vec->z = arg2->z + Math_CosS((s16)(this->actor.shape.rot.y + (this->unk_19A * 0x800) + i * 0x2000)) * temp_f20; + vec->y = arg2->y + arg1; + } +} + +void func_80AD9368(EnPoSisters* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersSwayAnim, -3.0f); + this->unk_19A = Rand_S16Offset(2, 3); + this->actionFunc = func_80ADA4A8; + this->actor.speedXZ = 0.0f; +} + +void func_80AD93C4(EnPoSisters* this) { + if (this->actionFunc != func_80ADA6A0) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersFloatAnim, -3.0f); + } + this->unk_19A = Rand_S16Offset(0xF, 3); + this->unk_199 |= 7; + this->actionFunc = func_80ADA530; +} + +void func_80AD943C(EnPoSisters* this) { + this->actionFunc = func_80ADA6A0; +} + +void func_80AD944C(EnPoSisters* this) { + if (this->unk_22E.a != 0) { + this->collider.base.colType = COLTYPE_METAL; + this->collider.base.acFlags |= AC_HARD; + } + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersAttackAnim, -5.0f); + this->actor.speedXZ = 0.0f; + this->unk_19A = Animation_GetLastFrame(&gPoeSistersAttackAnim) * 3 + 3; + this->unk_199 &= ~2; + this->actionFunc = func_80ADA7F0; +} + +void func_80AD94E0(EnPoSisters* this) { + this->actor.speedXZ = 5.0f; + if (this->unk_194 == 0) { + this->collider.base.colType = COLTYPE_METAL; + this->collider.base.acFlags |= AC_HARD; + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersAttackAnim, -5.0f); + } + this->unk_19A = 5; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->unk_199 |= 8; + this->actionFunc = func_80ADA8C0; +} + +void func_80AD9568(EnPoSisters* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersFloatAnim, -3.0f); + this->actor.world.rot.y = this->actor.yawTowardsPlayer + 0x8000; + if (this->unk_194 != 0) { + this->collider.base.colType = COLTYPE_HIT3; + this->collider.base.acFlags &= ~AC_HARD; + } + this->actionFunc = func_80ADA9E8; +} + +void func_80AD95D8(EnPoSisters* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPoeSistersDamagedAnim, -3.0f); + if (this->collider.base.ac != NULL) { + this->actor.world.rot.y = (this->collider.info.acHitInfo->toucher.dmgFlags & 0x1F824) + ? this->collider.base.ac->world.rot.y + : Actor_WorldYawTowardActor(&this->actor, this->collider.base.ac) + 0x8000; + } + if (this->unk_194 != 0) { + this->actor.speedXZ = 10.0f; + } + this->unk_199 &= ~0xB; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x10); + this->actionFunc = func_80ADAAA4; +} + +void func_80AD96A4(EnPoSisters* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersFleeAnim, -3.0f); + this->actor.world.rot.y = this->actor.shape.rot.y + 0x8000; + this->unk_19A = 5; + this->unk_199 |= 0xB; + this->actor.speedXZ = 5.0f; + this->actionFunc = func_80ADAC70; +} + +void func_80AD9718(EnPoSisters* this) { + Animation_Change(&this->skelAnime, &gPoeSistersAppearDisappearAnim, 1.5f, 0.0f, + Animation_GetLastFrame(&gPoeSistersAppearDisappearAnim), ANIMMODE_ONCE, -3.0f); + this->actor.speedXZ = 0.0f; + this->unk_19C = 100; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->unk_199 &= ~5; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DISAPPEAR); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH2); + this->actionFunc = func_80ADAD54; +} + +void func_80AD97C8(EnPoSisters* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 sp20; + + if (this->unk_195 == 0 || this->actionFunc != func_80ADAAA4) { + if ((player->swordState == 0 || player->swordAnimation >= 24) && + player->actor.world.pos.y - player->actor.floorHeight < 1.0f) { + Math_StepToF(&this->unk_294, 110.0f, 3.0f); + } else { + Math_StepToF(&this->unk_294, 170.0f, 10.0f); + } + sp20 = this->unk_294; + } else if (this->unk_195 != 0) { + sp20 = this->actor.parent->xzDistToPlayer; + } + this->actor.world.pos.x = (Math_SinS(this->actor.shape.rot.y + 0x8000) * sp20) + player->actor.world.pos.x; + this->actor.world.pos.z = (Math_CosS(this->actor.shape.rot.y + 0x8000) * sp20) + player->actor.world.pos.z; +} + +void func_80AD98F4(EnPoSisters* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gPoeSistersAppearDisappearAnim, 1.5f, 0.0f, + Animation_GetLastFrame(&gPoeSistersAppearDisappearAnim), ANIMMODE_ONCE, -3.0f); + if (this->unk_194 == 0) { + this->unk_294 = 110.0f; + func_80AD97C8(this, globalCtx); + this->unk_22E.a = 0; + this->actor.draw = EnPoSisters_Draw; + } else { + this->actor.world.rot.y = this->actor.shape.rot.y; + } + this->unk_19A = 15; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + this->unk_199 &= ~1; + this->actionFunc = func_80ADAE6C; +} + +void func_80AD99D4(EnPoSisters* this, GlobalContext* globalCtx) { + this->unk_19A = 0; + this->actor.speedXZ = 0.0f; + this->actor.world.pos.y += 42.0f; + this->actor.shape.yOffset = -6000.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_199 = 0; + this->actionFunc = func_80ADAFC0; + OnePointCutscene_Init(globalCtx, 3190, 999, &this->actor, MAIN_CAM); +} + +void func_80AD9A54(EnPoSisters* this, GlobalContext* globalCtx) { + this->unk_19A = 0; + this->actor.world.pos.y = this->unk_234[0].y; + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x80); + this->actionFunc = func_80ADB17C; +} + +// Meg spawning fakes +void func_80AD9AA8(EnPoSisters* this, GlobalContext* globalCtx) { + Actor* actor1 = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0x400); + Actor* actor2 = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0x800); + Actor* actor3 = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_PO_SISTERS, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0xC00); + s32 pad; + s32 pad1; + + if (actor1 == NULL || actor2 == NULL || actor3 == NULL) { + if (actor1 != NULL) { + Actor_Kill(actor1); + } + if (actor2 != NULL) { + Actor_Kill(actor2); + } + if (actor3 != NULL) { + Actor_Kill(actor3); + } + Actor_Kill(&this->actor); + } else { + actor3->parent = &this->actor; + actor2->parent = &this->actor; + actor1->parent = &this->actor; + Animation_PlayLoop(&this->skelAnime, &gPoeSistersMegCryAnim); + this->unk_198 = 0; + this->unk_199 = 160; + this->actionFunc = func_80ADB2B8; + } +} + +void func_80AD9C24(EnPoSisters* this, GlobalContext* globalCtx) { + Vec3f vec; + + this->actor.draw = NULL; + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_19C = 100; + this->unk_199 = 32; + this->collider.base.colType = COLTYPE_HIT3; + this->collider.base.acFlags &= ~AC_HARD; + if (globalCtx != NULL) { + vec.x = this->actor.world.pos.x; + vec.y = this->actor.world.pos.y + 45.0f; + vec.z = this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &vec, &sZeroVector, &sZeroVector, 150, 0, 255, 255, 255, 155, 150, 150, 150, 1, + 9, 0); + } + Lights_PointSetColorAndRadius(&this->lightInfo, 0, 0, 0, 0); + this->actionFunc = func_80ADB338; +} + +void func_80AD9D44(EnPoSisters* this) { + if (this->unk_194 == 3) { + Animation_PlayOnce(&this->skelAnime, &gPoeSistersAppearDisappearAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + } else { + Animation_Change(&this->skelAnime, &gPoeSistersAppearDisappearAnim, 0.5f, 0.0f, + Animation_GetLastFrame(&gPoeSistersAppearDisappearAnim), ANIMMODE_ONCE_INTERP, 0.0f); + } + this->unk_22E.a = 0; + this->unk_199 = 32; + this->actionFunc = func_80ADB9F0; +} + +void func_80AD9DF0(EnPoSisters* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPoeSistersAppearDisappearAnim, -5.0f); + this->unk_198 = 1; + this->unk_199 &= ~0x80; + this->actionFunc = func_80ADB4B0; + OnePointCutscene_Init(globalCtx, 3180, 156, &this->actor, MAIN_CAM); +} + +void func_80AD9E60(EnPoSisters* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersFloatAnim, -3.0f); + this->unk_19A = Animation_GetLastFrame(&gPoeSistersFloatAnim) * 7 + 7; + if (this->actor.parent != NULL) { + this->actor.world.pos = this->actor.parent->world.pos; + this->actor.shape.rot.y = this->actor.parent->shape.rot.y; + } else { + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->unk_19A++; + } + if (this->unk_195 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH2); + } + this->actionFunc = func_80ADB51C; +} + +void func_80AD9F1C(EnPoSisters* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersFloatAnim, -3.0f); + this->unk_22E.a = 255; + this->unk_19A = 300; + this->unk_19C = 3; + this->unk_199 |= 9; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = func_80ADB770; +} + +void func_80AD9F90(EnPoSisters* this) { + if (this->unk_194 == 1) { + this->actor.home.pos.x = -632.0f; + this->actor.home.pos.z = -3440.0f; + } else { + this->actor.home.pos.x = 752.0f; + this->actor.home.pos.z = -3440.0f; + } + Animation_PlayLoop(&this->skelAnime, &gPoeSistersFloatAnim); + this->unk_199 |= 0xA; + this->actionFunc = func_80ADBB6C; + this->actor.speedXZ = 5.0f; +} + +void func_80ADA028(EnPoSisters* this) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersSwayAnim, -3.0f); + this->unk_22E.a = 255; + this->unk_199 |= 0x15; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = func_80ADBBF4; + this->actor.speedXZ = 0.0f; +} + +void func_80ADA094(EnPoSisters* this, GlobalContext* globalCtx) { + D_80ADD784 = 0; + this->unk_22E.a = 0; + this->unk_199 = 128; + this->unk_19A = 50; + this->unk_234[0] = this->actor.home.pos; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + this->actionFunc = func_80ADBC88; +} + +void func_80ADA10C(EnPoSisters* this) { + s32 i; + + this->unk_198 = ARRAY_COUNT(this->unk_234); + for (i = 0; i < ARRAY_COUNT(this->unk_234); i++) { + this->unk_234[i] = this->unk_234[0]; + } + this->actionFunc = func_80ADBD38; +} + +void func_80ADA1B8(EnPoSisters* this) { + Animation_Change(&this->skelAnime, &gPoeSistersAppearDisappearAnim, 0.833f, 0.0f, + Animation_GetLastFrame(&gPoeSistersAppearDisappearAnim), ANIMMODE_ONCE_INTERP, 0.0f); + if (this->unk_194 == 0 || this->unk_194 == 1) { + this->unk_19A = 40; + } else { + this->unk_19A = 76; + } + this->unk_198 = 0; + D_80ADD784 = 0; + this->actionFunc = func_80ADBD8C; +} + +void func_80ADA25C(EnPoSisters* this) { + Animation_PlayLoop(&this->skelAnime, &gPoeSistersSwayAnim); + this->unk_198 = 8; + this->unk_19A = 32; + func_80AD9240(this, this->unk_19A, &this->actor.home.pos); + this->actionFunc = func_80ADBEE8; +} + +void func_80ADA2BC(EnPoSisters* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &gPoeSistersFloatAnim, -3.0f); + this->unk_198 = 0; + this->unk_199 = 40; + this->unk_19A = 90; + this->unk_196 = 32; + this->actor.world.rot.y = D_80ADD79C[this->unk_194]; + this->actor.home.pos.y = this->actor.world.pos.y; + if (this->unk_194 == 0) { + Flags_SetSwitch(globalCtx, 0x1B); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FLAME_IGNITION); + this->actionFunc = func_80ADBF58; +} + +void func_80ADA35C(EnPoSisters* this, GlobalContext* globalCtx) { + f32 targetY; + Player* player = GET_PLAYER(globalCtx); + + if (this->actionFunc == func_80ADBF58) { + targetY = this->actor.home.pos.y; + } else if (this->unk_194 == 0 || this->unk_194 == 3) { + targetY = player->actor.world.pos.y + 5.0f; + } else { + targetY = 832.0f; + } + Math_ApproachF(&this->actor.world.pos.y, targetY, 0.5f, 3.0f); + if (!this->unk_196) { + this->unk_196 = 32; + } + if (this->unk_196 != 0) { + this->unk_196--; + } + this->actor.world.pos.y += (2.0f + 0.5f * Rand_ZeroOne()) * Math_SinS(this->unk_196 * 0x800); + if (this->unk_22E.a == 255 && this->actionFunc != func_80ADA8C0 && this->actionFunc != func_80ADA7F0) { + if (this->actionFunc == func_80ADAC70) { + func_8002F974(&this->actor, NA_SE_EN_PO_AWAY - SFX_FLAG); + } else { + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); + } + } +} + +void func_80ADA4A8(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->unk_19A != 0) { + this->unk_19A--; + } + if (this->unk_19A == 0 || this->actor.xzDistToPlayer < 200.0f) { + func_80AD93C4(this); + } +} + +void func_80ADA530(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.speedXZ, 1.0f, 0.2f); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->unk_19A != 0) { + this->unk_19A--; + } + if (this->actor.xzDistToPlayer < 200.0f && fabsf(this->actor.yDistToPlayer + 5.0f) < 30.0f) { + func_80AD943C(this); + } else if (this->unk_19A == 0 && Math_StepToF(&this->actor.speedXZ, 0.0f, 0.2f) != 0) { + func_80AD9368(this); + } + if (this->actor.bgCheckFlags & 8) { + Math_ScaledStepToS(&this->actor.world.rot.y, Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), + 0x71C); + } else if (Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) > 300.0f) { + Math_ScaledStepToS(&this->actor.world.rot.y, Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), + 0x71C); + } +} + +void func_80ADA6A0(EnPoSisters* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 temp_v0; + + SkelAnime_Update(&this->skelAnime); + temp_v0 = this->actor.yawTowardsPlayer - player->actor.shape.rot.y; + Math_StepToF(&this->actor.speedXZ, 2.0f, 0.2f); + if (temp_v0 > 0x3000) { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x3000, 0x71C); + } else if (temp_v0 < -0x3000) { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer - 0x3000, 0x71C); + } else { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0x71C); + } + if (this->actor.xzDistToPlayer < 160.0f && fabsf(this->actor.yDistToPlayer + 5.0f) < 30.0f) { + func_80AD944C(this); + } else if (this->actor.xzDistToPlayer > 240.0f) { + func_80AD93C4(this); + } +} + +void func_80ADA7F0(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_19A != 0) { + this->unk_19A--; + } + this->actor.shape.rot.y += 384.0f * ((this->skelAnime.endFrame + 1.0f) * 3.0f - this->unk_19A); + if (this->unk_19A == 18 || this->unk_19A == 7) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_ROLL); + } + if (this->unk_19A == 0) { + func_80AD94E0(this); + } +} + +void func_80ADA8C0(EnPoSisters* this, GlobalContext* globalCtx) { + s32 pad; + + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->unk_19A != 0) { + this->unk_19A--; + } + this->actor.shape.rot.y += (384.0f * this->skelAnime.endFrame) * 3.0f; + if (this->unk_19A == 0 && ABS((s16)(this->actor.shape.rot.y - this->actor.world.rot.y)) < 0x1000) { + if (this->unk_194 != 0) { + this->collider.base.colType = COLTYPE_HIT3; + this->collider.base.acFlags &= ~AC_HARD; + func_80AD93C4(this); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH2); + func_80AD9C24(this, globalCtx); + } + } + if (Animation_OnFrame(&this->skelAnime, 1.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_ROLL); + } +} + +void func_80ADA9E8(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + this->actor.shape.rot.y -= (this->actor.speedXZ * 10.0f) * 128.0f; + if (Math_StepToF(&this->actor.speedXZ, 0.0f, 0.1f) != 0) { + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->unk_194 != 0) { + func_80AD93C4(this); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH2); + func_80AD9C24(this, globalCtx); + } + } +} + +void func_80ADAAA4(EnPoSisters* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) && !(this->actor.flags & ACTOR_FLAG_15)) { + if (this->actor.colChkInfo.health != 0) { + if (this->unk_194 != 0) { + func_80AD96A4(this); + } else if (this->unk_195 != 0) { + func_80AD9C24(this, NULL); + } else { + func_80AD9C24(this, globalCtx); + } + } else { + func_80AD99D4(this, globalCtx); + } + } + if (this->unk_195 != 0) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.parent->shape.rot.y, + (this->unk_195 == 2) ? 0x800 : 0x400); + this->unk_22E.a = ((this->skelAnime.endFrame - this->skelAnime.curFrame) * 255.0f) / this->skelAnime.endFrame; + this->actor.world.pos.y = this->actor.parent->world.pos.y; + func_80AD97C8(this, globalCtx); + } else if (this->unk_194 != 0) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + } +} + +void func_80ADAC70(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x8000, 1820); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->unk_19A != 0) { + this->unk_19A--; + } + if (this->actor.bgCheckFlags & 8) { + this->actor.world.rot.y = this->actor.shape.rot.y; + this->unk_199 |= 2; + func_80AD9718(this); + } else if (this->unk_19A == 0 && 240.0f < this->actor.xzDistToPlayer) { + this->actor.world.rot.y = this->actor.shape.rot.y; + func_80AD93C4(this); + } +} + +void func_80ADAD54(EnPoSisters* this, GlobalContext* globalCtx) { + s32 endFrame; + + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_22E.a = 0; + this->collider.info.bumper.dmgFlags = 0x00060001; + func_80AD93C4(this); + } else { + endFrame = this->skelAnime.endFrame; + this->unk_22E.a = ((endFrame - this->skelAnime.curFrame) * 255.0f) / endFrame; + } +} + +void func_80ADAE6C(EnPoSisters* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_22E.a = 255; + if (this->unk_194 != 0) { + this->unk_199 |= 1; + this->collider.info.bumper.dmgFlags = 0x4FC7FFEA; + if (this->unk_19A != 0) { + this->unk_19A--; + } + if (this->unk_19A == 0) { + this->unk_197 = 20; + func_80AD93C4(this); + } + } else { + func_80AD9F1C(this); + } + } else { + this->unk_22E.a = (this->skelAnime.curFrame * 255.0f) / this->skelAnime.endFrame; + if (this->unk_194 == 0) { + func_80AD97C8(this, globalCtx); + } + } +} + +void func_80ADAFC0(EnPoSisters* this, GlobalContext* globalCtx) { + s32 i; + + this->unk_19A++; + this->unk_198 = CLAMP_MAX(this->unk_198 + 1, 8); + for (i = this->unk_198 - 1; i > 0; i--) { + this->unk_234[i] = this->unk_234[i - 1]; + } + this->unk_234[0].x = + (Math_SinS((this->actor.shape.rot.y + this->unk_19A * 0x3000) - 0x4000) * (3000.0f * this->actor.scale.x)) + + this->actor.world.pos.x; + this->unk_234[0].z = + (Math_CosS((this->actor.shape.rot.y + this->unk_19A * 0x3000) - 0x4000) * (3000.0f * this->actor.scale.x)) + + this->actor.world.pos.z; + if (this->unk_19A < 8) { + this->unk_234[0].y = this->unk_234[1].y - 9.0f; + } else { + this->unk_234[0].y = this->unk_234[1].y + 2.0f; + if (this->unk_19A >= 16) { + if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.001f) != 0) { + func_80AD9A54(this, globalCtx); + } + this->actor.scale.z = this->actor.scale.x; + this->actor.scale.y = this->actor.scale.x; + } + } + if (this->unk_19A == 16) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DEAD2); + } +} + +void func_80ADB17C(EnPoSisters* this, GlobalContext* globalCtx) { + this->unk_19A++; + if (this->unk_19A == 64) { + Flags_SetSwitch(globalCtx, this->actor.params); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 30, NA_SE_EV_FLAME_IGNITION); + if (this->unk_194 == 0) { + Flags_UnsetSwitch(globalCtx, 0x1B); + } + globalCtx->envCtx.unk_BF = 0xFF; + func_80078884(NA_SE_SY_CORRECT_CHIME); + Actor_Kill(&this->actor); + } else if (this->unk_19A < 32) { + func_80AD9240(this, this->unk_19A, &this->actor.world.pos); + } else { + func_80AD9240(this, 64 - this->unk_19A, &this->actor.world.pos); + } + if (this->unk_19A == 32) { + this->actor.world.pos.x = D_80ADD7A4[this->unk_194].x; + this->actor.world.pos.y = D_80ADD7A4[this->unk_194].y; + this->actor.world.pos.z = D_80ADD7A4[this->unk_194].z; + } +} + +void func_80ADB2B8(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->actor.xzDistToPlayer < 130.0f) { + func_80AD9DF0(this, globalCtx); + } + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_CRY); + } + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; +} + +void func_80ADB338(EnPoSisters* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + EnPoSisters* realMeg = (EnPoSisters*)this->actor.parent; + + if (this->unk_195 == 0) { + if (Actor_WorldDistXZToPoint(&player->actor, &this->actor.home.pos) < 600.0f) { + if (this->unk_19C != 0) { + this->unk_19C--; + } + } else { + this->unk_19C = 100; + } + if (this->unk_19C == 0) { + this->actor.shape.rot.y = (s32)(4.0f * Rand_ZeroOne()) * 0x4000 + this->actor.yawTowardsPlayer; + this->actor.world.pos.y = player->actor.world.pos.y + 5.0f; + func_80AD98F4(this, globalCtx); + } + } else { + if (realMeg->actionFunc == func_80ADB51C) { + this->actor.draw = EnPoSisters_Draw; + func_80AD9E60(this); + } else if (realMeg->actionFunc == func_80ADAE6C) { + this->actor.shape.rot.y = this->actor.parent->shape.rot.y + this->unk_195 * 0x4000; + this->actor.world.pos.y = player->actor.world.pos.y + 5.0f; + func_80AD98F4(this, globalCtx); + } else if (realMeg->actionFunc == func_80ADAFC0) { + Actor_Kill(&this->actor); + } + } +} + +void func_80ADB4B0(EnPoSisters* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + func_80AD9E60(this); + } + func_80AD97C8(this, globalCtx); + this->actor.world.pos.y += 1.0f; + Actor_SetFocus(&this->actor, 40.0f); +} + +void func_80ADB51C(EnPoSisters* this, GlobalContext* globalCtx) { + f32 temp_f2; + s16 phi_v0; + s16 phi_a2; + u8 temp; + + SkelAnime_Update(&this->skelAnime); + temp_f2 = this->skelAnime.endFrame * 0.5f; + this->unk_22E.a = (fabsf(temp_f2 - this->skelAnime.curFrame) * 255.0f) / temp_f2; + if (this->unk_19A != 0) { + this->unk_19A -= 1; + } + if (this->unk_19A == 0) { + this->actor.world.rot.y = this->actor.shape.rot.y += 0x4000 * (s32)(Rand_ZeroOne() * 4.0f); + if (this->unk_195 == 0) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + func_80AD9F1C(this); + } else { + this->actor.world.pos.y += 0.1f; + temp = this->unk_195; + if (temp != 0) { + if (this->unk_19A > 90) { + phi_v0 = 1; + phi_a2 = 64; + } else if (this->unk_19A > 70) { + phi_v0 = 0; + phi_a2 = 64; + } else if (this->unk_19A > 55) { + phi_v0 = 1; + phi_a2 = 96; + } else if (this->unk_19A > 40) { + phi_v0 = 0; + phi_a2 = 96; + } else { + phi_v0 = 1; + phi_a2 = 256; + } + if (this->unk_195 == 2) { + phi_a2 *= 2; + } + Math_ScaledStepToS(&this->actor.shape.rot.y, + this->actor.parent->shape.rot.y + (this->unk_195 * 0x4000) * phi_v0, phi_a2); + } else if (this->unk_19A == 70 || this->unk_19A == 40) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH2); + } + } + func_80AD97C8(this, globalCtx); + Actor_SetFocus(&this->actor, 40.0f); +} + +void func_80ADB770(EnPoSisters* this, GlobalContext* globalCtx) { + s32 temp_v0; + s32 phi_a0; + + if (this->unk_19A != 0) { + this->unk_19A--; + } + if (this->unk_19C > 0) { + if (this->unk_19A >= 16) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_195 == 0) { + if (ABS((s16)(16 - this->unk_196)) < 14) { + this->actor.shape.rot.y += + (0x580 - (this->unk_19C * 0x180)) * fabsf(Math_SinS(this->unk_196 * 0x800)); + } + if (this->unk_19A >= 284 || this->unk_19A < 31) { + this->unk_199 |= 0x40; + } else { + this->unk_199 &= ~0x40; + } + } else { + this->actor.shape.rot.y = (s16)(this->actor.parent->shape.rot.y + (this->unk_195 * 0x4000)); + } + } + } + if (this->unk_195 == 0) { + if (this->unk_19A >= 284 || (this->unk_19A < 31 && this->unk_19A >= 16)) { + this->unk_199 |= 0x40; + } else { + this->unk_199 &= ~0x40; + } + } + if (Actor_WorldDistXZToPoint(&GET_PLAYER(globalCtx)->actor, &this->actor.home.pos) > 600.0f) { + this->unk_199 &= ~0x40; + func_80AD9C24(this, globalCtx); + } else if (this->unk_19A == 0) { + if (this->unk_195 == 0) { + func_80AD94E0(this); + } else { + func_80AD9C24(this, globalCtx); + } + } else if (this->unk_195 != 0) { + EnPoSisters* realMeg = (EnPoSisters*)this->actor.parent; + + if (realMeg->actionFunc == func_80ADAAA4) { + func_80AD95D8(this); + } + } else if (this->unk_19C == 0) { + this->unk_19C = -15; + } else if (this->unk_19C < 0) { + this->unk_19C++; + if (this->unk_19C == 0) { + func_80AD94E0(this); + } + } + func_80AD97C8(this, globalCtx); +} + +void func_80ADB9F0(EnPoSisters* this, GlobalContext* globalCtx) { + f32 div; + + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_22E.a = 255; + if (this->unk_194 == 3) { + this->actor.flags |= ACTOR_FLAG_0; + this->actor.home.pos.x = 1992.0f; + this->actor.home.pos.z = -1440.0f; + this->unk_199 |= 0x18; + func_80AD93C4(this); + } else { + func_80AD9F90(this); + } + } else { + div = this->skelAnime.curFrame / this->skelAnime.endFrame; + this->unk_22E.a = 255.0f * div; + } + if (this->unk_194 != 3 && Animation_OnFrame(&this->skelAnime, 1.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + } + Actor_SetFocus(&this->actor, 40.0f); +} + +void func_80ADBB6C(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) < 10.0f) { + func_80ADA028(this); + } else { + Math_ScaledStepToS(&this->actor.world.rot.y, Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos), + 1820); + } +} + +void func_80ADBBF4(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1820); + if (this->actor.xzDistToPlayer < 240.0f && fabsf(this->actor.yDistToPlayer + 5.0f) < 30.0f) { + func_80AD93C4(this); + } +} + +void func_80ADBC88(EnPoSisters* this, GlobalContext* globalCtx) { + if (D_80ADD784 != 0 || !Player_InCsMode(globalCtx)) { + if (this->unk_19A != 0) { + this->unk_19A--; + } + if (this->unk_19A == 30) { + if (this->unk_194 == 0) { + OnePointCutscene_Init(globalCtx, 3140, 999, NULL, MAIN_CAM); + } + D_80ADD784 = 1; + } + if (this->unk_19A == 0) { + func_80ADA10C(this); + } + } + func_8002F974(&this->actor, NA_SE_EV_TORCH - SFX_FLAG); +} + +void func_80ADBD38(EnPoSisters* this, GlobalContext* globalCtx) { + this->unk_19A++; + func_80AD9240(this, this->unk_19A, &this->actor.home.pos); + if (this->unk_19A == 32) { + func_80ADA1B8(this); + } +} + +void func_80ADBD8C(EnPoSisters* this, GlobalContext* globalCtx) { + this->unk_19A--; + if (this->unk_19A == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + this->unk_199 &= ~0x80; + } + if (this->unk_19A <= 0) { + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_22E.a = 255; + D_80ADD784 |= (1 << this->unk_194); + } else { + this->unk_22E.a = (this->skelAnime.curFrame * 255.0f) / this->skelAnime.endFrame; + } + } + if (D_80ADD784 == 15) { + func_80ADA25C(this); + } +} + +void func_80ADBEE8(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->unk_19A != 0) { + this->unk_19A--; + } + func_80AD9240(this, this->unk_19A, &this->actor.home.pos); + if (this->unk_19A == 0) { + func_80ADA2BC(this, globalCtx); + } +} + +void func_80ADBF58(EnPoSisters* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + this->unk_19A--; + Math_ScaledStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 0x500); + if (this->unk_19A == 0 && this->unk_194 == 0) { + globalCtx->envCtx.unk_BF = 4; + } + if (this->unk_19A < 0) { + Math_StepToF(&this->actor.speedXZ, 5.0f, 0.2f); + } + if (this->unk_19A == -70 && this->unk_194 == 1) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &D_80ADD7BC, 40, NA_SE_EN_PO_LAUGH); + } + if (this->unk_19A < -120) { + Actor_Kill(&this->actor); + } +} + +void func_80ADC034(EnPoSisters* this, GlobalContext* globalCtx) { + if (this->actor.isTargeted && this->unk_22E.a == 255) { + if (this->unk_197 != 0) { + this->unk_197--; + } + } else { + this->unk_197 = 20; + } + if (this->unk_22E.a == 0) { + if (this->unk_19C != 0) { + this->unk_19C--; + } + } + if (this->actionFunc != func_80ADA7F0 && this->actionFunc != func_80ADA8C0 && this->actionFunc != func_80ADAAA4) { + if (this->unk_197 == 0) { + func_80AD9718(this); + } else if (this->unk_19C == 0 && this->unk_22E.a == 0) { + func_80AD98F4(this, globalCtx); + } + } +} + +void func_80ADC10C(EnPoSisters* this, GlobalContext* globalCtx) { + Vec3f sp24; + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if (this->unk_195 != 0) { + ((EnPoSisters*)this->actor.parent)->unk_19C--; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH2); + func_80AD9C24(this, globalCtx); + if (Rand_ZeroOne() < 0.2f) { + sp24.x = this->actor.world.pos.x; + sp24.y = this->actor.world.pos.y; + sp24.z = this->actor.world.pos.z; + Item_DropCollectible(globalCtx, &sp24, ITEM00_ARROWS_SMALL); + } + } else if (this->collider.base.colType == 9 || + (this->actor.colChkInfo.damageEffect == 0 && this->actor.colChkInfo.damage == 0)) { + if (this->unk_194 == 0) { + this->actor.freezeTimer = 0; + } + } else if (this->actor.colChkInfo.damageEffect == 0xF) { + this->actor.world.rot.y = this->actor.shape.rot.y; + this->unk_199 |= 2; + func_80AD98F4(this, globalCtx); + } else if (this->unk_194 == 0 && this->actor.colChkInfo.damageEffect == 0xE && + this->actionFunc == func_80ADB770) { + if (this->unk_19C == 0) { + this->unk_19C = -45; + } + } else { + if (Actor_ApplyDamage(&this->actor) != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DAMAGE); + } else { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_SISTER_DEAD); + } + func_80AD95D8(this); + } + } +} + +void EnPoSisters_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnPoSisters* this = (EnPoSisters*)thisx; + s16 temp; + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + func_80AD9568(this); + } + func_80ADC10C(this, globalCtx); + if (this->unk_199 & 4) { + func_80ADC034(this, globalCtx); + } + this->actionFunc(this, globalCtx); + if (this->unk_199 & 0x1F) { + if (this->unk_199 & 8) { + func_80ADA35C(this, globalCtx); + } + Actor_MoveForward(&this->actor); + + if (this->unk_199 & 0x10) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 0.0f, 5); + } else { + Vec3f vec; + s32 sp34; + + vec.x = this->actor.world.pos.x; + vec.y = this->actor.world.pos.y + 10.0f; + vec.z = this->actor.world.pos.z; + this->actor.floorHeight = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &sp34, &this->actor, &vec); + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + if (this->actionFunc == func_80ADA8C0 || this->actionFunc == func_80ADA7F0) { + this->unk_198++; + this->unk_198 = CLAMP_MAX(this->unk_198, 8); + } else if (this->actionFunc != func_80ADAFC0) { + temp = this->unk_198 - 1; + this->unk_198 = CLAMP_MIN(temp, 1); + } + if (this->actionFunc == func_80ADA8C0) { + this->actor.flags |= ACTOR_FLAG_24; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->unk_199 & 1) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + if (this->actionFunc != func_80ADB338) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Actor_SetFocus(&this->actor, 40.0f); + if (this->actionFunc == func_80ADAC70) { + this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + } else if (this->unk_199 & 2) { + this->actor.shape.rot.y = this->actor.world.rot.y; + } + } +} + +void func_80ADC55C(EnPoSisters* this) { + s16 temp_var; + + if (this->skelAnime.animation == &gPoeSistersAttackAnim) { + this->unk_22E.r = CLAMP_MAX((s16)(this->unk_22E.r + 5), 255); + this->unk_22E.g = CLAMP_MIN((s16)(this->unk_22E.g - 5), 50); + temp_var = this->unk_22E.b - 5; + this->unk_22E.b = CLAMP_MIN(temp_var, 0); + } else if (this->skelAnime.animation == &gPoeSistersFleeAnim) { + this->unk_22E.r = CLAMP_MAX((s16)(this->unk_22E.r + 5), 80); + this->unk_22E.g = CLAMP_MAX((s16)(this->unk_22E.g + 5), 255); + temp_var = this->unk_22E.b + 5; + this->unk_22E.b = CLAMP_MAX(temp_var, 225); + } else if (this->skelAnime.animation == &gPoeSistersDamagedAnim) { + if (this->actor.colorFilterTimer & 2) { + this->unk_22E.r = 0; + this->unk_22E.g = 0; + this->unk_22E.b = 0; + } else { + this->unk_22E.r = 80; + this->unk_22E.g = 255; + this->unk_22E.b = 225; + } + } else { + this->unk_22E.r = CLAMP_MAX((s16)(this->unk_22E.r + 5), 255); + this->unk_22E.g = CLAMP_MAX((s16)(this->unk_22E.g + 5), 255); + if (this->unk_22E.b > 210) { + temp_var = this->unk_22E.b - 5; + this->unk_22E.b = CLAMP_MIN(temp_var, 210); + } else { + temp_var = this->unk_22E.b + 5; + this->unk_22E.b = CLAMP_MAX(temp_var, 210); + } + } +} + +s32 EnPoSisters_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx, Gfx** gfxP) { + EnPoSisters* this = (EnPoSisters*)thisx; + Color_RGBA8* color; + + if (limbIndex == 1 && (this->unk_199 & 0x40)) { + if (this->unk_19A >= 284) { + rot->x += (this->unk_19A * 0x1000) + 0xFFEE4000; + } else { + rot->x += (this->unk_19A * 0x1000) + 0xFFFF1000; + } + } + if (this->unk_22E.a == 0 || limbIndex == 8 || (this->actionFunc == func_80ADAFC0 && this->unk_19A >= 8)) { + *dList = NULL; + } else if (limbIndex == 9) { + *dList = D_80ADD7C8[this->unk_194]; + } else if (limbIndex == 10) { + *dList = D_80ADD7D8[this->unk_194]; + gDPPipeSync((*gfxP)++); + gDPSetEnvColor((*gfxP)++, this->unk_22E.r, this->unk_22E.g, this->unk_22E.b, this->unk_22E.a); + } else if (limbIndex == 11) { + color = &D_80ADD7E8[this->unk_194]; + gDPPipeSync((*gfxP)++); + gDPSetEnvColor((*gfxP)++, color->r, color->g, color->b, this->unk_22E.a); + } + return false; +} + +void EnPoSisters_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, + Gfx** gfxP) { + EnPoSisters* this = (EnPoSisters*)thisx; + s32 i; + s32 pad; + + if (this->actionFunc == func_80ADAFC0 && this->unk_19A >= 8 && limbIndex == 9) { + gSPMatrix((*gfxP)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_sisters.c", 2876), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList((*gfxP)++, gPoSistersBurnDL); + } + if (limbIndex == 8 && this->actionFunc != func_80ADB2B8) { + if (this->unk_199 & 0x20) { + for (i = this->unk_198 - 1; i > 0; i--) { + this->unk_234[i] = this->unk_234[i - 1]; + } + Matrix_MultVec3f(&D_80ADD7F8, &this->unk_234[0]); + } else if (this->actionFunc == func_80ADBD8C) { + Matrix_MultVec3f(&D_80ADD7F8, &this->actor.home.pos); + } + if (this->unk_198 > 0) { + Color_RGBA8* color = &D_80ADD6F0[this->unk_194]; + f32 temp_f2 = Rand_ZeroOne() * 0.3f + 0.7f; + + if (this->actionFunc == func_80ADB17C || this->actionFunc == func_80ADBD38 || + this->actionFunc == func_80ADBEE8) { + Lights_PointNoGlowSetInfo(&this->lightInfo, this->unk_234[0].x, this->unk_234[0].y + 15.0f, + this->unk_234[0].z, color->r * temp_f2, color->g * temp_f2, + color->b * temp_f2, 200); + } else { + Lights_PointGlowSetInfo(&this->lightInfo, this->unk_234[0].x, this->unk_234[0].y + 15.0f, + this->unk_234[0].z, color->r * temp_f2, color->g * temp_f2, color->b * temp_f2, + 200); + } + } else { + Lights_PointSetColorAndRadius(&this->lightInfo, 0, 0, 0, 0); + } + if (!(this->unk_199 & 0x80)) { + Matrix_Get(&this->unk_2F8); + } + } +} + +void EnPoSisters_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnPoSisters* this = (EnPoSisters*)thisx; + u8 phi_s5 = 0; + f32 phi_f20; + s32 i; + u8 spE7 = 0; + Color_RGBA8* temp_s1 = &D_80ADD700[this->unk_194]; + Color_RGBA8* temp_s7 = &D_80ADD6F0[this->unk_194]; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_po_sisters.c", 2989); + func_80ADC55C(this); + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + if (this->unk_22E.a == 255 || this->unk_22E.a == 0) { + gDPSetEnvColor(POLY_OPA_DISP++, this->unk_22E.r, this->unk_22E.g, this->unk_22E.b, this->unk_22E.a); + gSPSegment(POLY_OPA_DISP++, 0x09, D_80116280 + 2); + POLY_OPA_DISP = + SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoSisters_OverrideLimbDraw, EnPoSisters_PostLimbDraw, &this->actor, POLY_OPA_DISP); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->unk_22E.a); + gSPSegment(POLY_XLU_DISP++, 0x09, D_80116280); + POLY_XLU_DISP = + SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoSisters_OverrideLimbDraw, EnPoSisters_PostLimbDraw, &this->actor, POLY_XLU_DISP); + } + if (!(this->unk_199 & 0x80)) { + Matrix_Put(&this->unk_2F8); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_sisters.c", 3034), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gPoSistersTorchDL); + } + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + (globalCtx->gameplayFrames * -20) % 512, 0x20, 0x80)); + gDPSetEnvColor(POLY_XLU_DISP++, temp_s1->r, temp_s1->g, temp_s1->b, temp_s1->a); + if (this->actionFunc == func_80ADB17C) { + if (this->unk_19A < 32) { + phi_s5 = ((32 - this->unk_19A) * 255) / 32; + phi_f20 = 0.0056000003f; + } else { + phi_s5 = (this->unk_19A * 255 - 8160) / 32; + phi_f20 = 0.0027f; + } + } else if (this->actionFunc == func_80ADBD38) { + phi_s5 = ((32 - this->unk_19A) * 255) / 32; + phi_f20 = 0.0027f; + } else if (this->actionFunc == func_80ADBEE8) { + phi_s5 = ((32 - this->unk_19A) * 255) / 32; + phi_f20 = 0.0035f; + } else if (this->actionFunc == func_80ADBC88) { + //! @bug uninitialised spE7 + phi_s5 = spE7; + phi_f20 = 0.0027f; + } else { + phi_s5 = spE7; + phi_f20 = this->actor.scale.x * 0.5f; + } + for (i = 0; i < this->unk_198; i++) { + if (this->actionFunc != func_80ADB17C && this->actionFunc != func_80ADBD38 && + this->actionFunc != func_80ADBEE8) { + phi_s5 = -i * 31 + 248; + } + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, temp_s7->r, temp_s7->g, temp_s7->b, phi_s5); + Matrix_Translate(this->unk_234[i].x, this->unk_234[i].y, this->unk_234[i].z, MTXMODE_NEW); + Matrix_RotateZYX(0, (s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000), 0, MTXMODE_APPLY); + if (this->actionFunc == func_80ADAFC0) { + phi_f20 = (this->unk_19A - i) * 0.025f + 0.5f; + phi_f20 = CLAMP(phi_f20, 0.5f, 0.8f) * 0.007f; + } + Matrix_Scale(phi_f20, phi_f20, phi_f20, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_po_sisters.c", 3132), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_po_sisters.c", 3139); +} + +void EnPoSisters_Reset(void) { + D_80ADD784 = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h new file mode 100644 index 000000000..2b3b7446a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Po_Sisters/z_en_po_sisters.h @@ -0,0 +1,34 @@ +#ifndef Z_EN_PO_SISTERS_H +#define Z_EN_PO_SISTERS_H + +#include "ultra64.h" +#include "global.h" + +struct EnPoSisters; + +typedef void (*EnPoSistersActionFunc)(struct EnPoSisters*, GlobalContext*); + +typedef struct EnPoSisters { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnPoSistersActionFunc actionFunc; + /* 0x0194 */ u8 unk_194; + /* 0x0195 */ u8 unk_195; + /* 0x0196 */ u8 unk_196; + /* 0x0197 */ u8 unk_197; + /* 0x0198 */ u8 unk_198; + /* 0x0199 */ u8 unk_199; + /* 0x019A */ s16 unk_19A; + /* 0x019A */ s16 unk_19C; + /* 0x019E */ Vec3s jointTable[12]; + /* 0x01E6 */ Vec3s morphTable[12]; + /* 0x022E */ Color_RGBA8 unk_22E; + /* 0x0234 */ Vec3f unk_234[8]; + /* 0x0294 */ f32 unk_294; + /* 0x0298 */ LightNode* lightNode; + /* 0x029C */ LightInfo lightInfo; + /* 0x02AC */ ColliderCylinder collider; + /* 0x02F8 */ MtxF unk_2F8; +} EnPoSisters; // size = 0x0338 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c new file mode 100644 index 000000000..a0bc13128 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.c @@ -0,0 +1,1202 @@ +/* + * File: z_en_poh.c + * Overlay: ovl_En_Poh + * Description: Graveyard Poe + */ + +#include "z_en_poh.h" +#include "objects/object_poh/object_poh.h" +#include "objects/object_po_composer/object_po_composer.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_12) + +void EnPoh_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPoh_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPoh_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnPoh_UpdateLiving(Actor* thisx, GlobalContext* globalCtx); +void EnPoh_UpdateDead(Actor* thisx, GlobalContext* globalCtx); +void EnPoh_DrawRegular(Actor* thisx, GlobalContext* globalCtx); +void EnPoh_DrawComposer(Actor* thisx, GlobalContext* globalCtx); +void EnPoh_DrawSoul(Actor* thisx, GlobalContext* globalCtx); + +void func_80ADEAC4(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_Idle(EnPoh* this, GlobalContext* globalCtx); +void func_80ADEC9C(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_Attack(EnPoh* this, GlobalContext* globalCtx); +void func_80ADEECC(EnPoh* this, GlobalContext* globalCtx); +void func_80ADF894(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_ComposerAppear(EnPoh* this, GlobalContext* globalCtx); +void func_80ADEF38(EnPoh* this, GlobalContext* globalCtx); +void func_80ADF15C(EnPoh* this, GlobalContext* globalCtx); +void func_80ADF574(EnPoh* this, GlobalContext* globalCtx); +void func_80ADF5E0(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_Disappear(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_Appear(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_Death(EnPoh* this, GlobalContext* globalCtx); +void func_80ADFE28(EnPoh* this, GlobalContext* globalCtx); +void func_80ADFE80(EnPoh* this, GlobalContext* globalCtx); +void func_80AE009C(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_TalkRegular(EnPoh* this, GlobalContext* globalCtx); +void EnPoh_TalkComposer(EnPoh* this, GlobalContext* globalCtx); + +static s16 D_80AE1A50 = 0; + +const ActorInit En_Poh_InitVars = { + ACTOR_EN_POH, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnPoh), + (ActorFunc)EnPoh_Init, + (ActorFunc)EnPoh_Destroy, + (ActorFunc)EnPoh_Update, + NULL, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT3, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 40, 20, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit D_80AE1AA0[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 18, { { 0, 1400, 0 }, 10 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT3, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + D_80AE1AA0, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 4, 25, 50, 40 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(1, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static EnPohInfo sPoeInfo[2] = { + { + { 255, 170, 255 }, + { 100, 0, 150 }, + 18, + 5, + 248, + &gPoeDisappearAnim, + &gPoeFloatAnim, + &gPoeDamagedAnim, + &gPoeFleeAnim, + gPoeLanternDL, + gPoeBurnDL, + gPoeSoulDL, + }, + { + { 255, 255, 170 }, + { 0, 150, 0 }, + 9, + 1, + 244, + &gPoeComposerDisappearAnim, + &gPoeComposerFloatAnim, + &gPoeComposerDamagedAnim, + &gPoeComposerFleeAnim, + gPoeComposerLanternDL, + gPoeComposerBurnDL, + gPoeComposerSoulDL, + }, +}; + +static Color_RGBA8 D_80AE1B4C = { 75, 20, 25, 255 }; +static Color_RGBA8 D_80AE1B50 = { 80, 110, 90, 255 }; +static Color_RGBA8 D_80AE1B54 = { 90, 85, 50, 255 }; +static Color_RGBA8 D_80AE1B58 = { 100, 90, 100, 255 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 3200, ICHAIN_STOP), +}; + +static Vec3f D_80AE1B60 = { 0.0f, 3.0f, 0.0f }; +static Vec3f D_80AE1B6C = { 0.0f, 0.0f, 0.0f }; + +void EnPoh_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnItem00* collectible; + EnPoh* this = (EnPoh*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + Collider_InitJntSph(globalCtx, &this->colliderSph); + Collider_SetJntSph(globalCtx, &this->colliderSph, &this->actor, &sJntSphInit, &this->colliderSphItem); + this->colliderSph.elements[0].dim.worldSphere.radius = 0; + this->colliderSph.elements[0].dim.worldSphere.center.x = this->actor.world.pos.x; + this->colliderSph.elements[0].dim.worldSphere.center.y = this->actor.world.pos.y; + this->colliderSph.elements[0].dim.worldSphere.center.z = this->actor.world.pos.z; + Collider_InitCylinder(globalCtx, &this->colliderCyl); + Collider_SetCylinder(globalCtx, &this->colliderCyl, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + this->unk_194 = 0; + this->unk_195 = 32; + this->visibilityTimer = Rand_S16Offset(700, 300); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, + 255, 255, 255, 0); + if (this->actor.params >= 4) { + this->actor.params = EN_POH_NORMAL; + } + if (this->actor.params == EN_POH_RUPEE) { + D_80AE1A50++; + if (D_80AE1A50 >= 3) { + Actor_Kill(&this->actor); + } else { + collectible = Item_DropCollectible(globalCtx, &this->actor.world.pos, 0x4000 | ITEM00_RUPEE_BLUE); + if (collectible != NULL) { + collectible->actor.speedXZ = 0.0f; + } + } + } else if (this->actor.params == EN_POH_FLAT) { + if (Flags_GetSwitch(globalCtx, 0x28) || Flags_GetSwitch(globalCtx, 0x9)) { + Actor_Kill(&this->actor); + } else { + Flags_SetSwitch(globalCtx, 0x28); + } + } else if (this->actor.params == EN_POH_SHARP) { + if (Flags_GetSwitch(globalCtx, 0x29) || Flags_GetSwitch(globalCtx, 0x9)) { + Actor_Kill(&this->actor); + } else { + Flags_SetSwitch(globalCtx, 0x29); + } + } + if (this->actor.params < EN_POH_SHARP) { + this->objectIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_POH); + this->infoIdx = EN_POH_INFO_NORMAL; + this->actor.naviEnemyId = 0x44; + } else { + this->objectIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_PO_COMPOSER); + this->infoIdx = EN_POH_INFO_COMPOSER; + this->actor.naviEnemyId = 0x43; + } + this->info = &sPoeInfo[this->infoIdx]; + if (this->objectIdx < 0) { + Actor_Kill(&this->actor); + } +} + +void EnPoh_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + Collider_DestroyJntSph(globalCtx, &this->colliderSph); + Collider_DestroyCylinder(globalCtx, &this->colliderCyl); + if (this->actor.params == EN_POH_RUPEE) { + D_80AE1A50--; + } +} + +void func_80ADE114(EnPoh* this) { + Animation_PlayLoop(&this->skelAnime, this->info->idleAnim); + this->unk_198 = Rand_S16Offset(2, 3); + this->actionFunc = func_80ADEAC4; + this->actor.speedXZ = 0.0f; +} + +void EnPoh_SetupIdle(EnPoh* this) { + Animation_PlayLoop(&this->skelAnime, this->info->idleAnim2); + this->unk_198 = Rand_S16Offset(15, 3); + this->actionFunc = EnPoh_Idle; +} + +void func_80ADE1BC(EnPoh* this) { + Animation_PlayLoop(&this->skelAnime, this->info->idleAnim2); + this->actionFunc = func_80ADEC9C; + this->unk_198 = 0; + this->actor.speedXZ = 2.0f; +} + +void EnPoh_SetupAttack(EnPoh* this) { + if (this->infoIdx == EN_POH_INFO_NORMAL) { + Animation_MorphToLoop(&this->skelAnime, &gPoeAttackAnim, -6.0f); + } else { + Animation_PlayLoop(&this->skelAnime, &gPoeComposerAttackAnim); + } + this->unk_198 = 12; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + this->actionFunc = EnPoh_Attack; +} + +void func_80ADE28C(EnPoh* this) { + if (this->infoIdx == EN_POH_INFO_NORMAL) { + Animation_MorphToPlayOnce(&this->skelAnime, &gPoeDamagedAnim, -6.0f); + } else { + Animation_PlayOnce(&this->skelAnime, &gPoeComposerDamagedAnim); + } + if (this->colliderCyl.info.acHitInfo->toucher.dmgFlags & 0x0001F824) { + this->actor.world.rot.y = this->colliderCyl.base.ac->world.rot.y; + } else { + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, this->colliderCyl.base.ac) + 0x8000; + } + this->colliderCyl.base.acFlags &= ~AC_ON; + this->actor.speedXZ = 5.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x10); + this->actionFunc = func_80ADEECC; +} + +void func_80ADE368(EnPoh* this) { + Animation_MorphToLoop(&this->skelAnime, this->info->fleeAnim, -5.0f); + this->actor.speedXZ = 5.0f; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x8000; + this->colliderCyl.base.acFlags |= AC_ON; + this->unk_198 = 200; + this->actionFunc = func_80ADF894; +} + +void EnPoh_SetupInitialAction(EnPoh* this) { + this->lightColor.a = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->infoIdx == EN_POH_INFO_NORMAL) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gPoeAppearAnim, 0.0f); + this->actionFunc = func_80ADEF38; + } else { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gPoeComposerAppearAnim, 1.0f); + this->actor.world.pos.y = this->actor.home.pos.y + 20.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + this->actionFunc = EnPoh_ComposerAppear; + } +} + +void func_80ADE48C(EnPoh* this) { + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->unk_198 = 0; + this->actor.naviEnemyId = 0xFF; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = func_80ADF15C; +} + +void func_80ADE4C8(EnPoh* this) { + Animation_PlayOnce(&this->skelAnime, this->info->idleAnim2); + this->actionFunc = func_80ADF574; + this->actor.speedXZ = -5.0f; +} + +void func_80ADE514(EnPoh* this) { + Animation_PlayLoop(&this->skelAnime, this->info->idleAnim); + this->unk_19C = this->actor.world.rot.y + 0x8000; + this->actionFunc = func_80ADF5E0; + this->actor.speedXZ = 0.0f; +} + +void EnPoh_SetupDisappear(EnPoh* this) { + this->unk_194 = 32; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DISAPPEAR); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + this->actionFunc = EnPoh_Disappear; +} + +void EnPoh_SetupAppear(EnPoh* this) { + this->unk_194 = 0; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + this->actionFunc = EnPoh_Appear; +} + +void EnPoh_SetupDeath(EnPoh* this, GlobalContext* globalCtx) { + this->actor.update = EnPoh_UpdateDead; + this->actor.draw = EnPoh_DrawSoul; + this->actor.shape.shadowDraw = NULL; + Actor_SetScale(&this->actor, 0.01f); + this->actor.flags |= ACTOR_FLAG_4; + this->actor.gravity = -1.0f; + this->actor.shape.yOffset = 1500.0f; + this->actor.world.pos.y -= 15.0f; + if (this->infoIdx != EN_POH_INFO_COMPOSER) { + this->actor.shape.rot.x = -0x8000; + } + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, 8); + this->unk_198 = 60; + this->actionFunc = EnPoh_Death; +} + +void func_80ADE6D4(EnPoh* this) { + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0); + this->visibilityTimer = 0; + this->actor.shape.rot.y = 0; + this->lightColor.r = 0; + this->lightColor.a = 0; + this->actor.shape.yOffset = 0.0f; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + if (this->actor.params >= EN_POH_SHARP) { + this->lightColor.g = 200; + this->lightColor.b = 0; + } else { + this->lightColor.g = 0; + this->lightColor.b = 200; + } + this->actor.scale.x = 0.0f; + this->actor.scale.y = 0.0f; + this->actor.shape.rot.x = 0; + this->actor.home.pos.y = this->actor.world.pos.y; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_METAL_BOX_BOUND); + this->actionFunc = func_80ADFE28; +} + +void EnPoh_Talk(EnPoh* this, GlobalContext* globalCtx) { + this->actor.home.pos.y = this->actor.world.pos.y; + Actor_SetFocus(&this->actor, -10.0f); + this->colliderCyl.dim.radius = 13; + this->colliderCyl.dim.height = 30; + this->colliderCyl.dim.yShift = 0; + this->colliderCyl.dim.pos.x = this->actor.world.pos.x; + this->colliderCyl.dim.pos.y = this->actor.world.pos.y - 20.0f; + this->colliderCyl.dim.pos.z = this->actor.world.pos.z; + this->colliderCyl.base.ocFlags1 = OC1_ON | OC1_TYPE_PLAYER; + if (this->actor.params == EN_POH_FLAT || this->actor.params == EN_POH_SHARP) { + if (CHECK_QUEST_ITEM(QUEST_SONG_SUN)) { + this->actor.textId = 0x5000; + } else if (!Flags_GetSwitch(globalCtx, 0xA) && !Flags_GetSwitch(globalCtx, 0xB)) { + this->actor.textId = 0x500F; + } else if ((this->actor.params == EN_POH_FLAT && Flags_GetSwitch(globalCtx, 0xA)) || + (this->actor.params == EN_POH_SHARP && Flags_GetSwitch(globalCtx, 0xB))) { + this->actor.textId = 0x5013; + } else { + this->actor.textId = 0x5012; + } + } else { + this->actor.textId = 0x5005; + } + this->unk_198 = 200; + this->unk_195 = 32; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = func_80ADFE80; +} + +void func_80ADE950(EnPoh* this, s32 arg1) { + if (arg1) { + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_PO_BIG_CRY - SFX_FLAG); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + } + this->actionFunc = func_80AE009C; +} + +void func_80ADE998(EnPoh* this) { + this->actionFunc = EnPoh_TalkRegular; + this->actor.home.pos.y = this->actor.world.pos.y - 15.0f; +} + +void func_80ADE9BC(EnPoh* this) { + this->actionFunc = EnPoh_TalkComposer; +} + +void EnPoh_MoveTowardsPlayerHeight(EnPoh* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_StepToF(&this->actor.world.pos.y, player->actor.world.pos.y, 1.0f); + this->actor.world.pos.y += 2.5f * Math_SinS(this->unk_195 * 0x800); + if (this->unk_195 != 0) { + this->unk_195 -= 1; + } + if (this->unk_195 == 0) { + this->unk_195 = 32; + } +} + +void func_80ADEA5C(EnPoh* this) { + if (Actor_WorldDistXZToPoint(&this->actor, &this->actor.home.pos) > 400.0f) { + this->unk_19C = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + } + Math_ScaledStepToS(&this->actor.world.rot.y, this->unk_19C, 0x71C); +} + +void func_80ADEAC4(EnPoh* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->unk_198 != 0) { + this->unk_198--; + } + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + if (this->actor.xzDistToPlayer < 200.0f) { + func_80ADE1BC(this); + } else if (this->unk_198 == 0) { + EnPoh_SetupIdle(this); + } + if (this->lightColor.a == 255) { + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); + } +} + +void EnPoh_Idle(EnPoh* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_StepToF(&this->actor.speedXZ, 1.0f, 0.2f); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && this->unk_198 != 0) { + this->unk_198--; + } + func_80ADEA5C(this); + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + if (this->actor.xzDistToPlayer < 200.0f && this->unk_198 < 19) { + func_80ADE1BC(this); + } else if (this->unk_198 == 0) { + if (Rand_ZeroOne() < 0.1f) { + func_80ADE514(this); + } else { + func_80ADE114(this); + } + } + if (this->lightColor.a == 255) { + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); + } +} + +void func_80ADEC9C(EnPoh* this, GlobalContext* globalCtx) { + Player* player; + s16 facingDiff; + + player = GET_PLAYER(globalCtx); + SkelAnime_Update(&this->skelAnime); + if (this->unk_198 != 0) { + this->unk_198--; + } + facingDiff = this->actor.yawTowardsPlayer - player->actor.shape.rot.y; + if (facingDiff >= 0x3001) { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x3000, 0x71C); + } else if (facingDiff < -0x3000) { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer - 0x3000, 0x71C); + } else { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0x71C); + } + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + if (this->actor.xzDistToPlayer > 280.0f) { + EnPoh_SetupIdle(this); + } else if (this->unk_198 == 0 && this->actor.xzDistToPlayer < 140.0f && + !Player_IsFacingActor(&this->actor, 0x2AAA, globalCtx)) { + EnPoh_SetupAttack(this); + } + if (this->lightColor.a == 255) { + func_8002F974(&this->actor, NA_SE_EN_PO_FLY - SFX_FLAG); + } +} + +void EnPoh_Attack(EnPoh* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_KANTERA); + if (this->unk_198 != 0) { + this->unk_198--; + } + } + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + if (this->unk_198 >= 10) { + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 0xE38); + } else if (this->unk_198 == 9) { + this->actor.speedXZ = 5.0f; + this->skelAnime.playSpeed = 2.0f; + } else if (this->unk_198 == 0) { + EnPoh_SetupIdle(this); + this->unk_198 = 23; + } +} + +void func_80ADEECC(EnPoh* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.colChkInfo.health != 0) { + func_80ADE368(this); + } else { + func_80ADE48C(this); + } + } +} + +void func_80ADEF38(EnPoh* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->lightColor.a = 255; + this->visibilityTimer = Rand_S16Offset(700, 300); + this->actor.flags |= ACTOR_FLAG_0; + EnPoh_SetupIdle(this); + } else if (this->skelAnime.curFrame > 10.0f) { + this->lightColor.a = ((this->skelAnime.curFrame - 10.0f) * 0.05f) * 255.0f; + } + if (this->skelAnime.playSpeed < 0.5f && this->actor.xzDistToPlayer < 280.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_APPEAR); + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnPoh_ComposerAppear(EnPoh* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->lightColor.a = 255; + this->visibilityTimer = Rand_S16Offset(700, 300); + this->actor.flags |= ACTOR_FLAG_0; + EnPoh_SetupIdle(this); + } else { + this->lightColor.a = CLAMP_MAX((s32)(this->skelAnime.curFrame * 25.5f), 255); + } +} + +void func_80ADF15C(EnPoh* this, GlobalContext* globalCtx) { + Vec3f vec; + f32 multiplier; + f32 newScale; + s32 pad; + s32 pad1; + + this->unk_198++; + if (this->unk_198 < 8) { + if (this->unk_198 < 5) { + vec.y = Math_SinS((this->unk_198 * 0x1000) - 0x4000) * 23.0f + (this->actor.world.pos.y + 40.0f); + multiplier = Math_CosS((this->unk_198 * 0x1000) - 0x4000) * 23.0f; + vec.x = Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * multiplier + + this->actor.world.pos.x; + vec.z = Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * multiplier + + this->actor.world.pos.z; + } else { + vec.y = (this->actor.world.pos.y + 40.0f) + (15.0f * (this->unk_198 - 5)); + vec.x = + Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * 23.0f + this->actor.world.pos.x; + vec.z = + Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x4800) * 23.0f + this->actor.world.pos.z; + } + EffectSsDeadDb_Spawn(globalCtx, &vec, &D_80AE1B60, &D_80AE1B6C, this->unk_198 * 10 + 80, 0, 255, 255, 255, 255, + 0, 0, 255, 1, 9, 1); + vec.x = (this->actor.world.pos.x + this->actor.world.pos.x) - vec.x; + vec.z = (this->actor.world.pos.z + this->actor.world.pos.z) - vec.z; + EffectSsDeadDb_Spawn(globalCtx, &vec, &D_80AE1B60, &D_80AE1B6C, this->unk_198 * 10 + 80, 0, 255, 255, 255, 255, + 0, 0, 255, 1, 9, 1); + vec.x = this->actor.world.pos.x; + vec.z = this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &vec, &D_80AE1B60, &D_80AE1B6C, this->unk_198 * 10 + 80, 0, 255, 255, 255, 255, + 0, 0, 255, 1, 9, 1); + if (this->unk_198 == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EXTINCT); + } + } else if (this->unk_198 == 28) { + EnPoh_SetupDeath(this, globalCtx); + } else if (this->unk_198 >= 19) { + newScale = (28 - this->unk_198) * 0.001f; + this->actor.world.pos.y += 5.0f; + this->actor.scale.z = newScale; + this->actor.scale.y = newScale; + this->actor.scale.x = newScale; + } + if (this->unk_198 == 18) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DEAD2); + } +} + +void func_80ADF574(EnPoh* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.world.rot.y = this->actor.shape.rot.y; + EnPoh_SetupIdle(this); + this->unk_198 = 23; + } else { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.5f); + this->actor.shape.rot.y += 0x1000; + } +} + +void func_80ADF5E0(EnPoh* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Math_ScaledStepToS(&this->actor.world.rot.y, this->unk_19C, 1820) != 0) { + EnPoh_SetupIdle(this); + } + if (this->actor.xzDistToPlayer < 200.0f) { + func_80ADE1BC(this); + } + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); +} + +void EnPoh_Disappear(EnPoh* this, GlobalContext* globalCtx) { + if (this->unk_194 != 0) { + this->unk_194--; + } + this->actor.world.rot.y += 0x1000; + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + this->lightColor.a = this->unk_194 * 7.96875f; + if (this->unk_194 == 0) { + this->visibilityTimer = Rand_S16Offset(100, 50); + EnPoh_SetupIdle(this); + } +} + +void EnPoh_Appear(EnPoh* this, GlobalContext* globalCtx) { + this->unk_194++; + this->actor.world.rot.y -= 0x1000; + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + this->lightColor.a = this->unk_194 * 7.96875f; + if (this->unk_194 == 32) { + this->visibilityTimer = Rand_S16Offset(700, 300); + this->unk_194 = 0; + EnPoh_SetupIdle(this); + } +} + +void func_80ADF894(EnPoh* this, GlobalContext* globalCtx) { + f32 multiplier; + + SkelAnime_Update(&this->skelAnime); + multiplier = Math_SinS(this->unk_195 * 0x800) * 3.0f; + this->actor.world.pos.x -= multiplier * Math_CosS(this->actor.shape.rot.y); + this->actor.world.pos.z += multiplier * Math_SinS(this->actor.shape.rot.y); + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x8000, 0x71C); + EnPoh_MoveTowardsPlayerHeight(this, globalCtx); + if (this->unk_198 == 0 || this->actor.xzDistToPlayer > 250.0f) { + this->actor.world.rot.y = this->actor.shape.rot.y; + EnPoh_SetupIdle(this); + } + func_8002F974(&this->actor, NA_SE_EN_PO_AWAY - SFX_FLAG); +} + +void EnPoh_Death(EnPoh* this, GlobalContext* globalCtx) { + s32 objId; + + if (this->unk_198 != 0) { + this->unk_198--; + } + if (this->actor.bgCheckFlags & 1) { + objId = (this->infoIdx == EN_POH_INFO_COMPOSER) ? OBJECT_PO_COMPOSER : OBJECT_POH; + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 6.0f, 0, 1, 1, 15, objId, 10, + this->info->lanternDisplayList); + func_80ADE6D4(this); + } else if (this->unk_198 == 0) { + Actor_Kill(&this->actor); + return; + } + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 10.0f, 4); +} + +void func_80ADFA90(EnPoh* this, s32 arg1) { + f32 multiplier; + + this->lightColor.a = CLAMP(this->lightColor.a + arg1, 0, 255); + if (arg1 < 0) { + multiplier = this->lightColor.a * (1.0f / 255); + this->actor.scale.x = this->actor.scale.z = 0.0056000002f * multiplier + 0.0014000001f; + this->actor.scale.y = (0.007f - 0.007f * multiplier) + 0.007f; + } else { + multiplier = 1.0f; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = this->lightColor.a * (0.007f / 0xFF); + this->actor.world.pos.y = this->actor.home.pos.y + (1.0f / 17.0f) * this->lightColor.a; + } + this->lightColor.r = this->info->lightColor.r * multiplier; + this->lightColor.g = this->info->lightColor.g * multiplier; + this->lightColor.b = this->info->lightColor.b * multiplier; + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->info->lightColor.r, this->info->lightColor.g, + this->info->lightColor.b, this->lightColor.a * (200.0f / 255)); +} + +void func_80ADFE28(EnPoh* this, GlobalContext* globalCtx) { + this->actor.home.pos.y += 2.0f; + func_80ADFA90(this, 20); + if (this->lightColor.a == 255) { + EnPoh_Talk(this, globalCtx); + } +} + +void func_80ADFE80(EnPoh* this, GlobalContext* globalCtx) { + if (this->unk_198 != 0) { + this->unk_198--; + } + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (this->actor.params >= EN_POH_SHARP) { + func_80ADE9BC(this); + } else { + func_80ADE998(this); + } + return; + } + if (this->unk_198 == 0) { + func_80ADE950(this, 1); + this->actor.flags &= ~ACTOR_FLAG_16; + return; + } + if (this->colliderCyl.base.ocFlags1 & OC1_HIT) { + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2F4(&this->actor, globalCtx); + } else { + this->actor.flags &= ~ACTOR_FLAG_16; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderCyl.base); + } + this->actor.world.pos.y = Math_SinS(this->unk_195 * 0x800) * 5.0f + this->actor.home.pos.y; + if (this->unk_195 != 0) { + this->unk_195 -= 1; + } + if (this->unk_195 == 0) { + this->unk_195 = 32; + } + this->colliderCyl.dim.pos.y = this->actor.world.pos.y - 20.0f; + Actor_SetFocus(&this->actor, -10.0f); + Lights_PointNoGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->info->lightColor.r, this->info->lightColor.g, + this->info->lightColor.b, this->lightColor.a * (200.0f / 255)); +} + +void func_80AE009C(EnPoh* this, GlobalContext* globalCtx) { + func_80ADFA90(this, -13); + if (this->lightColor.a == 0) { + Actor_Kill(&this->actor); + } +} + +void EnPoh_TalkRegular(EnPoh* this, GlobalContext* globalCtx) { + if (this->actor.textId != 0x5005) { + func_80ADFA90(this, -13); + } else { + func_8002F974(&this->actor, NA_SE_EN_PO_BIG_CRY - SFX_FLAG); + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) { + if (Message_ShouldAdvance(globalCtx)) { + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_PO_BIG_CRY - SFX_FLAG); + if (globalCtx->msgCtx.choiceIndex == 0) { + if (Inventory_HasEmptyBottle()) { + this->actor.textId = 0x5008; + Item_Give(globalCtx, ITEM_POE); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_BIG_GET); + } else { + this->actor.textId = 0x5006; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + } + } else { + this->actor.textId = 0x5007; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_LAUGH); + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + } + } else if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80ADE950(this, 0); + } +} + +void EnPoh_TalkComposer(EnPoh* this, GlobalContext* globalCtx) { + func_8002F974(&this->actor, NA_SE_EN_PO_BIG_CRY - SFX_FLAG); + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) { + if (Message_ShouldAdvance(globalCtx)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + if (!Flags_GetSwitch(globalCtx, 0xB) && !Flags_GetSwitch(globalCtx, 0xA)) { + this->actor.textId = 0x5010; + } else { + this->actor.textId = 0x5014; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + } else { + if (this->actor.params == EN_POH_SHARP) { + Flags_SetSwitch(globalCtx, 0xB); + } else { + Flags_SetSwitch(globalCtx, 0xA); + } + func_80ADE950(this, 1); + } + } + } else if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + if (this->actor.textId == 0x5000) { + Flags_SetSwitch(globalCtx, 9); + } + func_80ADE950(this, 1); + } +} + +void func_80AE032C(EnPoh* this, GlobalContext* globalCtx) { + if (this->colliderCyl.base.acFlags & AC_HIT) { + this->colliderCyl.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != 0 || this->actor.colChkInfo.damage != 0) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DEAD); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DAMAGE); + } + func_80ADE28C(this); + } + } +} + +void EnPoh_UpdateVisibility(EnPoh* this) { + if (this->actionFunc != EnPoh_Appear && this->actionFunc != EnPoh_Disappear && this->actionFunc != func_80ADEF38 && + this->actionFunc != EnPoh_ComposerAppear) { + if (this->visibilityTimer != 0) { + this->visibilityTimer--; + } + if (this->lightColor.a == 255) { + if (this->actor.isTargeted) { + this->unk_194++; + this->unk_194 = CLAMP_MAX(this->unk_194, 20); + } else { + this->unk_194 = 0; + } + if ((this->unk_194 == 20 || this->visibilityTimer == 0) && + (this->actionFunc == func_80ADEAC4 || this->actionFunc == EnPoh_Idle || + this->actionFunc == func_80ADEC9C || this->actionFunc == func_80ADF894 || + this->actionFunc == func_80ADF5E0)) { + EnPoh_SetupDisappear(this); + } + } else if (this->lightColor.a == 0 && this->visibilityTimer == 0 && + (this->actionFunc == func_80ADEAC4 || this->actionFunc == EnPoh_Idle || + this->actionFunc == func_80ADEC9C || this->actionFunc == func_80ADF5E0)) { + EnPoh_SetupAppear(this); + } + } +} + +void EnPoh_Update(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + + if (Object_IsLoaded(&globalCtx->objectCtx, this->objectIdx)) { + this->actor.objBankIndex = this->objectIdx; + this->actor.update = EnPoh_UpdateLiving; + Actor_SetObjectDependency(globalCtx, &this->actor); + if (this->infoIdx == EN_POH_INFO_NORMAL) { + SkelAnime_Init(globalCtx, &this->skelAnime, &gPoeSkel, &gPoeFloatAnim, this->jointTable, this->morphTable, + 21); + this->actor.draw = EnPoh_DrawRegular; + } else { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gPoeComposerSkel, &gPoeComposerFloatAnim, this->jointTable, + this->morphTable, 12); + this->actor.draw = EnPoh_DrawComposer; + this->colliderSph.elements[0].dim.limb = 9; + this->colliderSph.elements[0].dim.modelSphere.center.y *= -1; + this->actor.shape.rot.y = this->actor.world.rot.y = -0x4000; + this->colliderCyl.dim.radius = 20; + this->colliderCyl.dim.height = 55; + this->colliderCyl.dim.yShift = 15; + } + this->actor.flags &= ~ACTOR_FLAG_4; + EnPoh_SetupInitialAction(this); + } +} + +void func_80AE067C(EnPoh* this) { + s16 temp_var; + + if (this->actionFunc == EnPoh_Attack) { + this->lightColor.r = CLAMP_MAX((s16)(this->lightColor.r + 5), 255); + this->lightColor.g = CLAMP_MIN((s16)(this->lightColor.g - 5), 50); + temp_var = this->lightColor.b - 5; + this->lightColor.b = CLAMP_MIN(temp_var, 0); + } else if (this->actionFunc == func_80ADF894) { + this->lightColor.r = CLAMP_MAX((s16)(this->lightColor.r + 5), 80); + this->lightColor.g = CLAMP_MAX((s16)(this->lightColor.g + 5), 255); + temp_var = this->lightColor.b + 5; + this->lightColor.b = CLAMP_MAX(temp_var, 225); + } else if (this->actionFunc == func_80ADEECC) { + if (this->actor.colorFilterTimer & 2) { + this->lightColor.r = 0; + this->lightColor.g = 0; + this->lightColor.b = 0; + } else { + this->lightColor.r = 80; + this->lightColor.g = 255; + this->lightColor.b = 225; + } + } else { + this->lightColor.r = CLAMP_MAX((s16)(this->lightColor.r + 5), 255); + this->lightColor.g = CLAMP_MAX((s16)(this->lightColor.g + 5), 255); + if (this->lightColor.b >= 211) { + temp_var = this->lightColor.b - 5; + this->lightColor.b = CLAMP_MIN(temp_var, 210); + } else { + temp_var = this->lightColor.b + 5; + this->lightColor.b = CLAMP_MAX(temp_var, 210); + } + } +} + +void func_80AE089C(EnPoh* this) { + f32 rand; + + if ((this->actionFunc == func_80ADEF38 || this->actionFunc == EnPoh_ComposerAppear) && + this->skelAnime.curFrame < 12.0f) { + this->envColor.r = this->envColor.g = this->envColor.b = (s16)(this->skelAnime.curFrame * 16.66f) + 55; + this->envColor.a = this->skelAnime.curFrame * (100.0f / 6.0f); + } else { + rand = Rand_ZeroOne(); + this->envColor.r = (s16)(rand * 30.0f) + 225; + this->envColor.g = (s16)(rand * 100.0f) + 155; + this->envColor.b = (s16)(rand * 160.0f) + 95; + this->envColor.a = 200; + } +} + +void EnPoh_UpdateLiving(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + s32 pad; + Vec3f vec; + s32 sp38; + + if (this->colliderSph.base.atFlags & AT_HIT) { + this->colliderSph.base.atFlags &= ~AT_HIT; + func_80ADE4C8(this); + } + func_80AE032C(this, globalCtx); + EnPoh_UpdateVisibility(this); + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + if (this->actionFunc == EnPoh_Attack && this->unk_198 < 10) { + this->actor.flags |= ACTOR_FLAG_24; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + } + Collider_UpdateCylinder(&this->actor, &this->colliderCyl); + if ((this->colliderCyl.base.acFlags & AC_ON) && this->lightColor.a == 255) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCyl.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderCyl.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + Actor_SetFocus(&this->actor, 42.0f); + if (this->actionFunc != func_80ADEECC && this->actionFunc != func_80ADF574) { + if (this->actionFunc == func_80ADF894) { + this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + } else { + this->actor.shape.rot.y = this->actor.world.rot.y; + } + } + vec.x = this->actor.world.pos.x; + vec.y = this->actor.world.pos.y + 20.0f; + vec.z = this->actor.world.pos.z; + this->actor.floorHeight = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &sp38, &this->actor, &vec); + func_80AE089C(this); + this->actor.shape.shadowAlpha = this->lightColor.a; +} + +s32 EnPoh_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfxP) { + EnPoh* this = (EnPoh*)thisx; + + if ((this->lightColor.a == 0 || limbIndex == this->info->unk_6) || + (this->actionFunc == func_80ADF15C && this->unk_198 >= 2)) { + *dList = NULL; + } else if (this->actor.params == EN_POH_FLAT && limbIndex == 0xA) { + // Replace Sharp's head with Flat's + *dList = gPoeComposerFlatHeadDL; + } + if (limbIndex == 0x13 && this->infoIdx == EN_POH_INFO_NORMAL) { + gDPPipeSync((*gfxP)++); + gDPSetEnvColor((*gfxP)++, this->lightColor.r, this->lightColor.g, this->lightColor.b, this->lightColor.a); + } + return false; +} + +void EnPoh_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfxP) { + EnPoh* this = (EnPoh*)thisx; + + Collider_UpdateSpheres(limbIndex, &this->colliderSph); + if (this->actionFunc == func_80ADF15C && this->unk_198 >= 2 && limbIndex == this->info->unk_7) { + gSPMatrix((*gfxP)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_poh.c", 2460), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList((*gfxP)++, this->info->burnDisplayList); + } + if (limbIndex == this->info->unk_6) { + if (this->actionFunc == func_80ADF15C && this->unk_198 >= 19 && 0.0f != this->actor.scale.x) { + f32 mtxScale = 0.01f / this->actor.scale.x; + Matrix_Scale(mtxScale, mtxScale, mtxScale, MTXMODE_APPLY); + } + Matrix_Get(&this->unk_368); + if (this->actionFunc == func_80ADF15C && this->unk_198 == 27) { + this->actor.world.pos.x = this->unk_368.xw; + this->actor.world.pos.y = this->unk_368.yw; + this->actor.world.pos.z = this->unk_368.zw; + } + Lights_PointGlowSetInfo(&this->lightInfo, this->colliderSph.elements[0].dim.worldSphere.center.x, + this->colliderSph.elements[0].dim.worldSphere.center.y, + this->colliderSph.elements[0].dim.worldSphere.center.z, this->envColor.r, + this->envColor.g, this->envColor.b, this->envColor.a * (200.0f / 255)); + } +} + +void EnPoh_DrawRegular(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_poh.c", 2629); + func_80AE067C(this); + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + if (this->lightColor.a == 255 || this->lightColor.a == 0) { + gDPSetEnvColor(POLY_OPA_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, this->lightColor.a); + gSPSegment(POLY_OPA_DISP++, 0x08, D_80116280 + 2); + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoh_OverrideLimbDraw, EnPoh_PostLimbDraw, &this->actor, POLY_OPA_DISP); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->lightColor.a); + gSPSegment(POLY_XLU_DISP++, 0x08, D_80116280); + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnPoh_OverrideLimbDraw, EnPoh_PostLimbDraw, &this->actor, POLY_XLU_DISP); + } + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, this->envColor.r, this->envColor.g, this->envColor.b, 255); + Matrix_Put(&this->unk_368); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_poh.c", 2676), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, this->info->lanternDisplayList); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_poh.c", 2681); +} + +void EnPoh_DrawComposer(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + Color_RGBA8* sp90; + Color_RGBA8* phi_t0; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_poh.c", 2694); + func_80AE067C(this); + if (this->actor.params == EN_POH_SHARP) { + sp90 = &D_80AE1B4C; + phi_t0 = &D_80AE1B54; + } else { + sp90 = &D_80AE1B50; + phi_t0 = &D_80AE1B58; + } + if (this->lightColor.a == 255 || this->lightColor.a == 0) { + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_EnvColor(globalCtx->state.gfxCtx, this->lightColor.r, this->lightColor.g, this->lightColor.b, + this->lightColor.a)); + gSPSegment(POLY_OPA_DISP++, 0x0A, + Gfx_EnvColor(globalCtx->state.gfxCtx, sp90->r, sp90->g, sp90->b, this->lightColor.a)); + gSPSegment(POLY_OPA_DISP++, 0x0B, + Gfx_EnvColor(globalCtx->state.gfxCtx, phi_t0->r, phi_t0->g, phi_t0->b, this->lightColor.a)); + gSPSegment(POLY_OPA_DISP++, 0x0C, D_80116280 + 2); + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnPoh_OverrideLimbDraw, EnPoh_PostLimbDraw, + &this->actor, POLY_OPA_DISP); + } else { + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_EnvColor(globalCtx->state.gfxCtx, this->lightColor.r, this->lightColor.g, this->lightColor.b, + this->lightColor.a)); + gSPSegment(POLY_XLU_DISP++, 0x0A, + Gfx_EnvColor(globalCtx->state.gfxCtx, sp90->r, sp90->g, sp90->b, this->lightColor.a)); + gSPSegment(POLY_XLU_DISP++, 0x0B, + Gfx_EnvColor(globalCtx->state.gfxCtx, phi_t0->r, phi_t0->g, phi_t0->b, this->lightColor.a)); + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnPoh_OverrideLimbDraw, EnPoh_PostLimbDraw, + &this->actor, POLY_XLU_DISP); + } + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, this->envColor.r, this->envColor.g, this->envColor.b, 255); + Matrix_Put(&this->unk_368); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_poh.c", 2787), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, this->info->lanternDisplayList); + gSPDisplayList(POLY_OPA_DISP++, gPoeComposerLanternBottomDL); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, sp90->r, sp90->g, sp90->b, 255); + gSPDisplayList(POLY_OPA_DISP++, gPoeComposerLanternTopDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_poh.c", 2802); +} + +void EnPoh_UpdateDead(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + + this->actionFunc(this, globalCtx); + if (this->actionFunc != EnPoh_Death) { + this->visibilityTimer++; + } + func_80AE089C(this); +} + +void EnPoh_DrawSoul(Actor* thisx, GlobalContext* globalCtx) { + EnPoh* this = (EnPoh*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_poh.c", 2833); + + if (this->actionFunc == EnPoh_Death) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, this->envColor.r, this->envColor.g, this->envColor.b, 255); + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->envColor.r, this->envColor.g, this->envColor.b, 200); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_poh.c", 2854), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, this->info->lanternDisplayList); + if (this->infoIdx == EN_POH_INFO_COMPOSER) { + Color_RGBA8* envColor = (this->actor.params == EN_POH_SHARP) ? &D_80AE1B4C : &D_80AE1B50; + s32 pad; + + gSPDisplayList(POLY_OPA_DISP++, gPoeComposerLanternBottomDL); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, envColor->r, envColor->g, envColor->b, 255); + gSPDisplayList(POLY_OPA_DISP++, gPoeComposerLanternTopDL); + } + } else { + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + (this->visibilityTimer * this->info->unk_8) % 512U, 0x20, 0x80)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, this->info->primColor.r, this->info->primColor.g, + this->info->primColor.b, this->lightColor.a); + gDPSetEnvColor(POLY_XLU_DISP++, this->lightColor.r, this->lightColor.g, this->lightColor.b, 255); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * 9.58738e-05f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_poh.c", 2910), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, this->info->soulDisplayList); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_poh.c", 2916); +} diff --git a/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.h b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.h new file mode 100644 index 000000000..724ade9e1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Poh/z_en_poh.h @@ -0,0 +1,62 @@ +#ifndef Z_EN_POH_H +#define Z_EN_POH_H + +#include "ultra64.h" +#include "global.h" + +struct EnPoh; + +typedef void (*EnPohActionFunc)(struct EnPoh*, GlobalContext*); + +typedef enum { + EN_POH_NORMAL, + EN_POH_RUPEE, + EN_POH_SHARP, + EN_POH_FLAT +} EnPohType; + +typedef enum { + EN_POH_INFO_NORMAL, + EN_POH_INFO_COMPOSER +} EnPohInfoType; + +typedef struct { + /* 0x0000 */ Color_RGB8 primColor; + /* 0x0003 */ Color_RGB8 lightColor; + /* 0x0006 */ u8 unk_6; // limb index + /* 0x0006 */ u8 unk_7; // limb index + /* 0x0008 */ s8 unk_8; // rate of some kind + /* 0x000C */ AnimationHeader* idleAnim; + /* 0x0010 */ AnimationHeader* idleAnim2; + /* 0x0014 */ AnimationHeader* damageAnim; + /* 0x0018 */ AnimationHeader* fleeAnim; + /* 0x001C */ Gfx* lanternDisplayList; + /* 0x0020 */ Gfx* burnDisplayList; + /* 0x0024 */ Gfx* soulDisplayList; +} EnPohInfo; // size = 0x28 + +typedef struct EnPoh { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnPohActionFunc actionFunc; + /* 0x0194 */ u8 unk_194; + /* 0x0195 */ u8 unk_195; + /* 0x0196 */ s8 objectIdx; + /* 0x0197 */ u8 infoIdx; + /* 0x0198 */ s16 unk_198; + /* 0x019A */ s16 visibilityTimer; + /* 0x019C */ s16 unk_19C; + /* 0x019E */ Vec3s jointTable[21]; + /* 0x021C */ Vec3s morphTable[21]; + /* 0x029A */ Color_RGBA8 lightColor; + /* 0x029E */ Color_RGBA8 envColor; + /* 0x02A4 */ EnPohInfo* info; + /* 0x02A8 */ LightNode* lightNode; + /* 0x02AC */ LightInfo lightInfo; + /* 0x02BC */ ColliderCylinder colliderCyl; + /* 0x0308 */ ColliderJntSph colliderSph; + /* 0x0328 */ ColliderJntSphElement colliderSphItem; + /* 0x0368 */ MtxF unk_368; +} EnPoh; // size = 0x03A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Pu_box/z_en_pu_box.c b/soh/src/overlays/actors/ovl_En_Pu_box/z_en_pu_box.c new file mode 100644 index 000000000..0240b8f30 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Pu_box/z_en_pu_box.c @@ -0,0 +1,90 @@ +/* + * File: z_en_pu_box.c + * Overlay: ovl_En_Pu_Box + * Description: An unused stone cube + */ + +#include "z_en_pu_box.h" +#include "objects/object_pu_box/object_pu_box.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnPubox_Init(Actor* thisx, GlobalContext* globalCtx); +void EnPubox_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnPubox_Update(Actor* thisx, GlobalContext* globalCtx); +void EnPubox_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Pu_box_InitVars = { + ACTOR_EN_PU_BOX, + ACTORCAT_BG, + FLAGS, + OBJECT_PU_BOX, + sizeof(EnPubox), + (ActorFunc)EnPubox_Init, + (ActorFunc)EnPubox_Destroy, + (ActorFunc)EnPubox_Update, + (ActorFunc)EnPubox_Draw, + NULL, +}; + +void EnPubox_Init(Actor* thisx, GlobalContext* globalCtx) { + CollisionHeader* colHeader = NULL; + EnPubox* this = (EnPubox*)thisx; + + switch (thisx->params) { + case 0: + Actor_SetScale(thisx, 0.0025f); + break; + case 1: + Actor_SetScale(thisx, 0.005f); + break; + case 2: + Actor_SetScale(thisx, 0.0075f); + break; + case 3: + Actor_SetScale(thisx, 0.01f); + default: + break; + } + this->unk_164 = 1; + thisx->colChkInfo.cylRadius = 20; + thisx->colChkInfo.cylHeight = 50; + thisx->uncullZoneDownward = 1200.0f; + thisx->uncullZoneScale = 720.0f; + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 6.0f); + this->dyna.unk_160 = 0; + this->dyna.unk_15C = DPM_UNK; + thisx->targetMode = 1; + thisx->gravity = -2.0f; + CollisionHeader_GetVirtual(&gBlockMediumCol, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); +} + +void EnPubox_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnPubox* this = (EnPubox*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void EnPubox_Update(Actor* thisx, GlobalContext* globalCtx) { + EnPubox* this = (EnPubox*)thisx; + + thisx->speedXZ += this->dyna.unk_150; + thisx->world.rot.y = this->dyna.unk_158; + thisx->speedXZ = (thisx->speedXZ < -2.5f) ? -2.5f : ((thisx->speedXZ > 2.5f) ? 2.5f : thisx->speedXZ); + Math_SmoothStepToF(&thisx->speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + if (thisx->speedXZ != 0.0f) { + Audio_PlaySoundGeneral(NA_SE_EV_ROCK_SLIDE - SFX_FLAG, &thisx->projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + this->dyna.unk_154 = 0.0f; + this->dyna.unk_150 = 0.0f; + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, thisx->colChkInfo.cylHeight, thisx->colChkInfo.cylRadius, + thisx->colChkInfo.cylRadius, 0x1D); + thisx->focus.pos = thisx->world.pos; +} + +void EnPubox_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gBlockMediumDL); +} diff --git a/soh/src/overlays/actors/ovl_En_Pu_box/z_en_pu_box.h b/soh/src/overlays/actors/ovl_En_Pu_box/z_en_pu_box.h new file mode 100644 index 000000000..0aa0aaaf4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Pu_box/z_en_pu_box.h @@ -0,0 +1,14 @@ +#ifndef Z_EN_PU_BOX_H +#define Z_EN_PU_BOX_H + +#include "ultra64.h" +#include "global.h" + +struct EnPubox; + +typedef struct EnPubox { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ u32 unk_164; +} EnPubox; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c new file mode 100644 index 000000000..7003d4cde --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -0,0 +1,927 @@ +#include "z_en_rd.h" +#include "objects/object_rd/object_rd.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_10) + +void EnRd_Init(Actor* thisx, GlobalContext* globalCtx); +void EnRd_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnRd_Update(Actor* thisx, GlobalContext* globalCtx); +void EnRd_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AE269C(EnRd* this); +void func_80AE2744(EnRd* this, GlobalContext* globalCtx); +void func_80AE2970(EnRd* this); +void func_80AE2A10(EnRd* this, GlobalContext* globalCtx); +void func_80AE2C1C(EnRd* this, GlobalContext* globalCtx); +void func_80AE2F50(EnRd* this, GlobalContext* globalCtx); +void func_80AE2FD0(EnRd* this, GlobalContext* globalCtx); +void func_80AE31DC(EnRd* this); +void func_80AE3260(EnRd* this, GlobalContext* globalCtx); +void func_80AE33F0(EnRd* this); +void func_80AE392C(EnRd* this); +void func_80AE39D4(EnRd* this); +void func_80AE3454(EnRd* this, GlobalContext* globalCtx); +void func_80AE37BC(EnRd* this); +void func_80AE3834(EnRd* this, GlobalContext* globalCtx); +void func_80AE3978(EnRd* this, GlobalContext* globalCtx); +void func_80AE3A54(EnRd* this, GlobalContext* globalCtx); +void func_80AE3B18(EnRd* this, GlobalContext* globalCtx); +void func_80AE3C98(EnRd* this, GlobalContext* globalCtx); +void func_80AE3ECC(EnRd* this, GlobalContext* globalCtx); + +const ActorInit En_Rd_InitVars = { + ACTOR_EN_RD, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_RD, + sizeof(EnRd), + (ActorFunc)EnRd_Init, + (ActorFunc)EnRd_Destroy, + (ActorFunc)EnRd_Update, + (ActorFunc)EnRd_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0xF), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0xE), + /* Ice magic */ DMG_ENTRY(0, 0x6), + /* Light magic */ DMG_ENTRY(3, 0xD), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0xF), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(2, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xF), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -3500, ICHAIN_STOP), +}; + +static Vec3f D_80AE4918 = { 0.0f, 0.0f, 0.0f }; + +// I'm guessing these are primitive and environment colors that go unused +static Color_RGBA8 D_80AE4924 = { 200, 200, 255, 255 }; +static Color_RGBA8 D_80AE4928 = { 0, 0, 255, 0 }; + +static Vec3f D_80AE492C = { 0.0f, 0.0f, 0.0f }; +static Color_RGBA8 D_80AE4938 = { 200, 200, 255, 255 }; +static Color_RGBA8 D_80AE493C = { 0, 0, 255, 0 }; + +static Vec3f D_80AE4940 = { 300.0f, 0.0f, 0.0f }; +static Vec3f D_80AE494C = { 300.0f, 0.0f, 0.0f }; +static Vec3f D_80AE4958 = { 0.25f, 0.25f, 0.25f }; + +void EnRd_SetupAction(EnRd* this, EnRdActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnRd_Init(Actor* thisx, GlobalContext* globalCtx) { + EnRd* this = (EnRd*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + thisx->targetMode = 0; + thisx->colChkInfo.damageTable = &sDamageTable; + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + this->unk_310 = this->unk_30E = 0; + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 50.0f; + thisx->colChkInfo.mass = MASS_HEAVY; + thisx->colChkInfo.health = 8; + this->unk_314 = this->unk_31D = 0xFF; + this->unk_312 = (thisx->params & 0xFF00) >> 8; + + if (thisx->params & 0x80) { + thisx->params |= 0xFF00; + } else { + thisx->params &= 0xFF; + } + + if (thisx->params >= -1) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_rd_Skel_00E778, &object_rd_Anim_0087D0, + this->jointTable, this->morphTable, 26); + thisx->naviEnemyId = 0x2A; + } else { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_rd_Skel_003DD8, &object_rd_Anim_0087D0, + this->jointTable, this->morphTable, 26); + thisx->naviEnemyId = 0x2D; + } + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + + if (thisx->params >= -2) { + func_80AE269C(this); + } else { + func_80AE2970(this); + } + + SkelAnime_Update(&this->skelAnime); + + if (thisx->params == 3) { + thisx->flags |= ACTOR_FLAG_7; + } +} + +void EnRd_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnRd* this = (EnRd*)thisx; + + if (gSaveContext.sunsSongState != SUNSSONG_INACTIVE) { + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + } + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AE2630(GlobalContext* globalCtx, Actor* thisx, s32 arg2) { + Actor* enemyIt = globalCtx->actorCtx.actorLists[ACTORCAT_ENEMY].head; + + while (enemyIt != NULL) { + if ((enemyIt->id != ACTOR_EN_RD) || (enemyIt == thisx) || (enemyIt->params < 0)) { + enemyIt = enemyIt->next; + continue; + } + + if (arg2 != 0) { + enemyIt->parent = thisx; + } else if (enemyIt->parent == thisx) { + enemyIt->parent = NULL; + } + enemyIt = enemyIt->next; + } +} + +void func_80AE269C(EnRd* this) { + if (this->actor.params != 2) { + Animation_MorphToLoop(&this->skelAnime, &object_rd_Anim_0087D0, -6.0f); + } else { + Animation_PlayLoop(&this->skelAnime, &object_rd_Anim_005D98); + } + + this->unk_31B = 0; + this->unk_30C = (Rand_ZeroOne() * 10.0f) + 5.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnRd_SetupAction(this, func_80AE2744); +} + +void func_80AE2744(EnRd* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x64, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x64, 0); + + if ((this->actor.params == 2) && (0.0f == this->skelAnime.curFrame)) { + if (Rand_ZeroOne() >= 0.5f) { + Animation_PlayLoop(&this->skelAnime, &object_rd_Anim_005D98); + } else { + Animation_PlayLoop(&this->skelAnime, &object_rd_Anim_0057AC); + } + } else { + this->unk_30C--; + if (this->unk_30C == 0) { + this->unk_30C = (Rand_ZeroOne() * 10.0f) + 10.0f; + this->skelAnime.curFrame = 0.0f; + } + } + + if (this->actor.parent != NULL) { + if (this->unk_305 == 0) { + if (this->actor.params != 2) { + func_80AE31DC(this); + } else { + func_80AE392C(this); + } + } + } else { + if (this->unk_305 != 0) { + if (this->actor.params != 2) { + func_80AE37BC(this); + } else { + func_80AE392C(this); + } + } + + this->unk_305 = 0; + if ((this->actor.xzDistToPlayer <= 150.0f) && func_8002DDE4(globalCtx)) { + if ((this->actor.params != 2) && (this->unk_305 == 0)) { + func_80AE37BC(this); + } else { + func_80AE392C(this); + } + } + } + + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_CRY); + } +} + +void func_80AE2970(EnRd* this) { + Animation_Change(&this->skelAnime, &object_rd_Anim_0087D0, 0, 0, Animation_GetLastFrame(&object_rd_Anim_0087D0), + ANIMMODE_LOOP, -6.0f); + this->unk_31B = 11; + this->unk_30C = 6; + this->actor.shape.rot.x = -0x4000; + this->actor.gravity = 0.0f; + this->actor.shape.yOffset = 0.0f; + this->actor.speedXZ = 0.0f; + EnRd_SetupAction(this, func_80AE2A10); +} + +// Rising out of coffin +void func_80AE2A10(EnRd* this, GlobalContext* globalCtx) { + if (this->actor.shape.rot.x != -0x4000) { + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 0x7D0, 0); + if (Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.3f, 2.0f, 0.3f) == 0.0f) { + this->actor.gravity = -3.5f; + func_80AE269C(this); + } + } else { + if (this->actor.world.pos.y == this->actor.home.pos.y) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_CRY); + } + if (Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 50.0f, 0.3f, 2.0f, 0.3f) == 0.0f) { + if (this->unk_30C != 0) { + this->unk_30C--; + Math_SmoothStepToF(&this->actor.speedXZ, 6.0f, 0.3f, 1.0f, 0.3f); + } else if (Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.3f, 1.0f, 0.3f) == 0.0f) { + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 0x7D0, 0); + } + } + } +} + +void func_80AE2B90(EnRd* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &object_rd_Anim_00EFDC, 1.0f, 4.0f, + Animation_GetLastFrame(&object_rd_Anim_00EFDC), ANIMMODE_LOOP_INTERP, -4.0f); + this->actor.speedXZ = 0.4f; + this->unk_31B = 4; + EnRd_SetupAction(this, func_80AE2C1C); +} + +void func_80AE2C1C(EnRd* this, GlobalContext* globalCtx) { + Vec3f sp44 = D_80AE4918; + Color_RGBA8 sp40 = D_80AE4924; + Color_RGBA8 sp3C = D_80AE4928; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 sp32 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y - this->unk_30E - this->unk_310; + + this->skelAnime.playSpeed = this->actor.speedXZ; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA, 0); + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x64, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x64, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + SkelAnime_Update(&this->skelAnime); + + if (Actor_WorldDistXYZToPoint(&player->actor, &this->actor.home.pos) >= 150.0f) { + func_80AE2F50(this, globalCtx); + } + + if ((ABS(sp32) < 0x1554) && (Actor_WorldDistXYZToActor(&this->actor, &player->actor) <= 150.0f)) { + if (!(player->stateFlags1 & 0x2C6080) && !(player->stateFlags2 & 0x80)) { + if (this->unk_306 == 0) { + if (!(this->unk_312 & 0x80)) { + player->actor.freezeTimer = 40; + func_8008EEAC(globalCtx, &this->actor); + GET_PLAYER(globalCtx)->unk_684 = &this->actor; + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + } + this->unk_306 = 0x3C; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_AIM); + } + } else { + func_80AE2F50(this, globalCtx); + } + } + + if (this->unk_307 != 0) { + this->unk_307--; + } + + if (!this->unk_307 && (Actor_WorldDistXYZToActor(&this->actor, &player->actor) <= 45.0f) && + Actor_IsFacingPlayer(&this->actor, 0x38E3)) { + player->actor.freezeTimer = 0; + if (globalCtx->grabPlayer(globalCtx, player)) { + this->actor.flags &= ~ACTOR_FLAG_0; + func_80AE33F0(this); + } + } else if (this->actor.params > 0) { + if (this->actor.parent != NULL) { + func_80AE31DC(this); + } else { + this->unk_305 = 0; + } + } + + if ((this->skelAnime.curFrame == 10.0f) || (this->skelAnime.curFrame == 22.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); + } else if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_CRY); + } +} + +void func_80AE2F50(EnRd* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &object_rd_Anim_00EFDC, 0.5f, 0, Animation_GetLastFrame(&object_rd_Anim_00EFDC), + ANIMMODE_LOOP_INTERP, -4.0f); + this->unk_31B = 2; + EnRd_SetupAction(this, func_80AE2FD0); +} + +void func_80AE2FD0(EnRd* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 targetY = Actor_WorldYawTowardPoint(&this->actor, &this->actor.home.pos); + + if (Actor_WorldDistXYZToPoint(&this->actor, &this->actor.home.pos) >= 5.0f) { + Math_SmoothStepToS(&this->actor.shape.rot.y, targetY, 1, 0x1C2, 0); + } else { + this->actor.speedXZ = 0.0f; + if (Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.home.rot.y, 1, 0x1C2, 0) == 0) { + if (this->actor.params != 2) { + func_80AE269C(this); + } else { + func_80AE39D4(this); + } + } + } + + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x64, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x64, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + SkelAnime_Update(&this->skelAnime); + + if (!(player->stateFlags1 & 0x2C6080) && !(player->stateFlags2 & 0x80) && + (Actor_WorldDistXYZToPoint(&player->actor, &this->actor.home.pos) < 150.0f)) { + this->actor.targetMode = 0; + func_80AE2B90(this, globalCtx); + } else if (this->actor.params > 0) { + if (this->actor.parent != NULL) { + func_80AE31DC(this); + } else { + this->unk_305 = 0; + } + } + + if (this->skelAnime.curFrame == 10.0f || this->skelAnime.curFrame == 22.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); + } else if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_CRY); + } +} + +void func_80AE31DC(EnRd* this) { + Animation_Change(&this->skelAnime, &object_rd_Anim_00EFDC, 0.5f, 0, Animation_GetLastFrame(&object_rd_Anim_00EFDC), + ANIMMODE_LOOP_INTERP, -4.0f); + this->unk_31B = 3; + this->unk_305 = 1; + EnRd_SetupAction(this, func_80AE3260); +} + +void func_80AE3260(EnRd* this, GlobalContext* globalCtx) { + if (this->actor.parent != NULL) { + s32 pad; + s16 targetY; + Vec3f thisPos = this->actor.parent->world.pos; + + targetY = Actor_WorldYawTowardPoint(&this->actor, &thisPos); + + Math_SmoothStepToS(&this->actor.shape.rot.y, targetY, 1, 0xFA, 0); + + if (Actor_WorldDistXYZToPoint(&this->actor, &thisPos) >= 45.0f) { + this->actor.speedXZ = 0.4f; + } else { + this->actor.speedXZ = 0.0f; + + if (this->actor.params != 2) { + func_80AE269C(this); + } else { + func_80AE39D4(this); + } + } + + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x64, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x64, 0); + } else { + func_80AE2B90(this, globalCtx); + } + + this->actor.world.rot.y = this->actor.shape.rot.y; + SkelAnime_Update(&this->skelAnime); + + if (this->skelAnime.curFrame == 10.0f || this->skelAnime.curFrame == 22.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); + } else if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_CRY); + } +} + +void func_80AE33F0(EnRd* this) { + Animation_PlayOnce(&this->skelAnime, &object_rd_Anim_004ADC); + this->unk_30C = this->unk_304 = 0; + this->unk_319 = 200; + this->unk_31B = 8; + this->actor.speedXZ = 0.0f; + EnRd_SetupAction(this, func_80AE3454); +} + +void func_80AE3454(EnRd* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_304++; + } + + switch (this->unk_304) { + case 1: + Animation_PlayLoop(&this->skelAnime, &object_rd_Anim_004268); + this->unk_304++; + globalCtx->damagePlayer(globalCtx, -8); + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 1, 0xC); + this->unk_319 = 20; + case 0: + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x5DC, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x5DC, 0); + case 2: + if (!(player->stateFlags2 & 0x80)) { + Animation_Change(&this->skelAnime, &object_rd_Anim_0046F8, 0.5f, 0.0f, + Animation_GetLastFrame(&object_rd_Anim_0046F8), ANIMMODE_ONCE_INTERP, 0.0f); + this->unk_304++; + this->unk_31B = 4; + return; + } + + if (!LINK_IS_ADULT) { + Math_SmoothStepToF(&this->actor.shape.yOffset, -1500.0f, 1.0f, 150.0f, 0.0f); + } + + Math_SmoothStepToF(&this->actor.world.pos.x, + (Math_SinS(player->actor.shape.rot.y) * -25.0f) + player->actor.world.pos.x, 1.0f, 10.0f, + 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, player->actor.world.pos.y, 1.0f, 10.0f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, + (Math_CosS(player->actor.shape.rot.y) * -25.0f) + player->actor.world.pos.z, 1.0f, 10.0f, + 0.0f); + Math_SmoothStepToS(&this->actor.shape.rot.y, player->actor.shape.rot.y, 1, 0x1770, 0); + + if (this->skelAnime.curFrame == 0.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_ATTACK); + } + this->unk_319--; + + if (this->unk_319 == 0) { + globalCtx->damagePlayer(globalCtx, -8); + func_800AA000(this->actor.xzDistToPlayer, 0xF0, 1, 0xC); + this->unk_319 = 20; + func_8002F7DC(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); + } + break; + case 3: + if (!LINK_IS_ADULT) { + Math_SmoothStepToF(&this->actor.shape.yOffset, 0, 1.0f, 400.0f, 0.0f); + } + break; + case 4: + if (!LINK_IS_ADULT) { + Math_SmoothStepToF(&this->actor.shape.yOffset, 0, 1.0f, 400.0f, 0.0f); + } + this->actor.targetMode = 0; + this->actor.flags |= ACTOR_FLAG_0; + this->unk_306 = 0xA; + this->unk_307 = 0xF; + func_80AE2B90(this, globalCtx); + break; + } +} + +void func_80AE37BC(EnRd* this) { + Animation_Change(&this->skelAnime, &object_rd_Anim_004F94, 0.0f, 0.0f, + Animation_GetLastFrame(&object_rd_Anim_004F94), ANIMMODE_ONCE, 0.0f); + this->unk_31B = 7; + EnRd_SetupAction(this, func_80AE3834); +} + +void func_80AE3834(EnRd* this, GlobalContext* globalCtx) { + Vec3f sp34 = D_80AE492C; + Color_RGBA8 sp30 = D_80AE4938; + Color_RGBA8 sp2C = D_80AE493C; + Player* player = GET_PLAYER(globalCtx); + s16 temp_v0 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y - this->unk_30E - this->unk_310; + + if (ABS(temp_v0) < 0x2008) { + if (!(this->unk_312 & 0x80)) { + player->actor.freezeTimer = 60; + func_800AA000(this->actor.xzDistToPlayer, 0xFF, 0x14, 0x96); + func_8008EEAC(globalCtx, &this->actor); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_AIM); + func_80AE2B90(this, globalCtx); + } +} + +void func_80AE392C(EnRd* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_rd_Anim_008040, -4.0f); + this->unk_31B = 5; + EnRd_SetupAction(this, func_80AE3978); +} + +void func_80AE3978(EnRd* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.parent != NULL) { + func_80AE31DC(this); + } else { + func_80AE37BC(this); + } + } +} + +void func_80AE39D4(EnRd* this) { + Animation_Change(&this->skelAnime, &object_rd_Anim_008040, -1.0f, Animation_GetLastFrame(&object_rd_Anim_008040), + 0.0f, ANIMMODE_ONCE, -4.0f); + this->unk_31B = 6; + EnRd_SetupAction(this, func_80AE3A54); +} + +void func_80AE3A54(EnRd* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + func_80AE269C(this); + } +} + +void func_80AE3A8C(EnRd* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_rd_Anim_0074F0, -6.0f); + + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = -2.0f; + } + + this->actor.flags |= ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_DAMAGE); + this->unk_31B = 9; + EnRd_SetupAction(this, func_80AE3B18); +} + +void func_80AE3B18(EnRd* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.15f; + } + + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x12C, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x12C, 0); + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (this->actor.parent != NULL) { + func_80AE31DC(this); + } else if (Actor_WorldDistXYZToPoint(&player->actor, &this->actor.home.pos) >= 150.0f) { + func_80AE2F50(this, globalCtx); + } else { + func_80AE2B90(this, globalCtx); + } + + this->unk_31D = 0xFF; + } +} + +void func_80AE3C20(EnRd* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &object_rd_Anim_006E88, -1.0f); + this->unk_31B = 10; + this->unk_30C = 300; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_REDEAD_DEAD); + EnRd_SetupAction(this, func_80AE3C98); +} + +void func_80AE3C98(EnRd* this, GlobalContext* globalCtx) { + if (this->actor.category != ACTORCAT_PROP) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + } + + Math_SmoothStepToS(&this->unk_30E, 0, 1, 0x7D0, 0); + Math_SmoothStepToS(&this->unk_310, 0, 1, 0x7D0, 0); + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->unk_30C == 0) { + if (!Flags_GetSwitch(globalCtx, this->unk_312 & 0x7F)) { + Flags_SetSwitch(globalCtx, this->unk_312 & 0x7F); + } + if (this->unk_314 != 0) { + if (this->unk_314 == 0xB4) { + func_80AE2630(globalCtx, &this->actor, 0); + } + this->actor.scale.y -= 0.000075f; + this->unk_314 -= 5; + } else { + Actor_Kill(&this->actor); + } + } else { + this->unk_30C--; + } + } else if (((s32)this->skelAnime.curFrame == 33) || ((s32)this->skelAnime.curFrame == 40)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } +} + +void func_80AE3DE4(EnRd* this) { + this->unk_31B = 1; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + if (gSaveContext.sunsSongState != SUNSSONG_INACTIVE) { + this->unk_318 = 1; + this->unk_316 = 0x258; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIGHT_ARROW_HIT); + Actor_SetColorFilter(&this->actor, -0x8000, -0x7F38, 0, 0xFF); + } else if (this->unk_31C == 1) { + Actor_SetColorFilter(&this->actor, 0, 0xC8, 0, 0x50); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIGHT_ARROW_HIT); + Actor_SetColorFilter(&this->actor, -0x8000, 0xC8, 0, 0x50); + } + EnRd_SetupAction(this, func_80AE3ECC); +} + +void func_80AE3ECC(EnRd* this, GlobalContext* globalCtx) { + if ((this->unk_318 != 0) && (this->unk_316 != 0)) { + this->unk_316--; + if (this->unk_316 >= 0xFF) { + Actor_SetColorFilter(&this->actor, -0x8000, 0xC8, 0, 0xFF); + } + if (this->unk_316 == 0) { + this->unk_318 = 0; + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + } + } + + if (this->actor.colorFilterTimer == 0) { + if (this->actor.colChkInfo.health == 0) { + func_80AE2630(globalCtx, &this->actor, 1); + func_80AE3C20(this); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x90); + } else { + func_80AE3A8C(this); + } + } +} + +void func_80AE3F9C(EnRd* this, GlobalContext* globalCtx) { + s16 temp1; + s16 temp2; + s16 temp3; + + temp1 = this->actor.yawTowardsPlayer - (s16)(this->actor.shape.rot.y + this->unk_310); + temp2 = CLAMP(temp1, -500, 500); + + temp1 -= this->unk_30E; + temp3 = CLAMP(temp1, -500, 500); + + if ((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y) >= 0) { + this->unk_310 += ABS(temp2); + this->unk_30E += ABS(temp3); + } else { + this->unk_310 -= ABS(temp2); + this->unk_30E -= ABS(temp3); + } + + this->unk_310 = CLAMP(this->unk_310, -18783, 18783); + this->unk_30E = CLAMP(this->unk_30E, -9583, 9583); +} + +void func_80AE4114(EnRd* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + if ((gSaveContext.sunsSongState != SUNSSONG_INACTIVE) && (this->actor.shape.rot.x == 0) && (this->unk_318 == 0) && + (this->unk_31B != 9) && (this->unk_31B != 10) && (this->unk_31B != 1)) { + func_80AE3DE4(this); + return; + } + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + this->unk_31C = this->actor.colChkInfo.damageEffect; + + if (this->unk_31B != 11) { + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if (player->unk_844 != 0) { + this->unk_31D = player->unk_845; + } + + if ((this->unk_31C != 0) && (this->unk_31C != 6)) { + if (((this->unk_31C == 1) || (this->unk_31C == 13)) && (this->unk_31B != 1)) { + Actor_ApplyDamage(&this->actor); + func_80AE3DE4(this); + return; + } + + this->unk_318 = 0; + this->unk_316 = 0; + + if (this->unk_31C == 0xE) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x50); + this->unk_31A = 0x28; + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + } + + Actor_ApplyDamage(&this->actor); + if (this->actor.colChkInfo.health == 0) { + func_80AE2630(globalCtx, &this->actor, 1); + func_80AE3C20(this); + Item_DropCollectibleRandom(globalCtx, 0, &this->actor.world.pos, 0x90); + } else { + func_80AE3A8C(this); + } + } + } + } +} + +void EnRd_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnRd* this = (EnRd*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 pad2; + + func_80AE4114(this, globalCtx); + + if (gSaveContext.sunsSongState != SUNSSONG_INACTIVE && this->unk_318 == 0) { + gSaveContext.sunsSongState = SUNSSONG_INACTIVE; + } + + if (this->unk_31C != 6 && ((this->unk_31B != 11) || (this->unk_31C != 14))) { + if (this->unk_306 != 0) { + this->unk_306--; + } + + this->actionFunc(this, globalCtx); + if (this->unk_31B != 8 && this->actor.speedXZ != 0.0f) { + Actor_MoveForward(&this->actor); + } + + if ((this->actor.shape.rot.x == 0) && (this->unk_31B != 8) && (this->actor.speedXZ != 0.0f)) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 20.0f, 35.0f, 0x1D); + } + + if (this->unk_31B == 7) { + func_80AE3F9C(this, globalCtx); + } + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 50.0f; + + if ((this->actor.colChkInfo.health > 0) && (this->unk_31B != 8)) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if ((this->unk_31B != 9) || ((player->unk_844 != 0) && (player->unk_845 != this->unk_31D))) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +s32 EnRd_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnRd* this = (EnRd*)thisx; + + if (limbIndex == 23) { + rot->y += this->unk_30E; + } else if (limbIndex == 12) { + rot->y += this->unk_310; + } + return false; +} + +void EnRd_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + Vec3f sp2C = D_80AE4940; + EnRd* this = (EnRd*)thisx; + s32 idx = -1; + Vec3f destPos; + + if ((this->unk_31A != 0) || ((this->actor.colorFilterTimer != 0) && (this->actor.colorFilterParams & 0x4000))) { + switch (limbIndex - 1) { + case 23: + idx = 0; + break; + case 0: + idx = 1; + break; + case 21: + idx = 2; + break; + case 17: + idx = 3; + break; + case 13: + idx = 4; + break; + case 24: + idx = 5; + break; + case 8: + idx = 6; + break; + case 3: + idx = 7; + break; + case 10: + idx = 8; + break; + case 5: + idx = 9; + break; + } + + if (idx >= 0) { + Matrix_MultVec3f(&sp2C, &destPos); + this->firePos[idx].x = destPos.x; + this->firePos[idx].y = destPos.y; + this->firePos[idx].z = destPos.z; + } + } +} + +void EnRd_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnRd* this = (EnRd*)thisx; + Vec3f thisPos = thisx->world.pos; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_rd.c", 1679); + + if (this->unk_314 == 0xFF) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->unk_314); + gSPSegment(POLY_OPA_DISP++, 8, &D_80116280[2]); + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnRd_OverrideLimbDraw, EnRd_PostLimbDraw, this, + POLY_OPA_DISP); + func_80033C30(&thisPos, &D_80AE4958, 255, globalCtx); + if (this->unk_31A != 0) { + thisx->colorFilterTimer++; + this->unk_31A--; + if (this->unk_31A % 4 == 0) { + EffectSsEnFire_SpawnVec3s(globalCtx, thisx, &this->firePos[this->unk_31A >> 2], 0x4B, 0, 0, + (this->unk_31A >> 2)); + } + } + } else { + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->unk_314); + gSPSegment(POLY_XLU_DISP++, 8, &D_80116280[0]); + POLY_XLU_DISP = + SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnRd_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + + func_80033C30(&thisPos, &D_80AE4958, this->unk_314, globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_rd.c", 1735); +} diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.h b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.h new file mode 100644 index 000000000..f679f08ab --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.h @@ -0,0 +1,37 @@ +#ifndef Z_EN_RD_H +#define Z_EN_RD_H + +#include "ultra64.h" +#include "global.h" + +struct EnRd; + +typedef void (*EnRdActionFunc)(struct EnRd*, GlobalContext*); + +typedef struct EnRd { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3s firePos[10]; + /* 0x0188 */ SkelAnime skelAnime; + /* 0x01CC */ Vec3s jointTable[26]; + /* 0x0268 */ Vec3s morphTable[26]; + /* 0x0304 */ u8 unk_304; + /* 0x0305 */ u8 unk_305; + /* 0x0306 */ u8 unk_306; + /* 0x0307 */ u8 unk_307; + /* 0x0308 */ EnRdActionFunc actionFunc; + /* 0x030C */ s16 unk_30C; + /* 0x030E */ s16 unk_30E; + /* 0x0310 */ s16 unk_310; + /* 0x0312 */ s16 unk_312; + /* 0x0314 */ s16 unk_314; + /* 0x0316 */ s16 unk_316; + /* 0x0318 */ u8 unk_318; + /* 0x0319 */ u8 unk_319; + /* 0x031A */ u8 unk_31A; + /* 0x031B */ u8 unk_31B; + /* 0x031C */ u8 unk_31C; + /* 0x031D */ u8 unk_31D; + /* 0x0320 */ ColliderCylinder collider; +} EnRd; // size = 0x036C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c b/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c new file mode 100644 index 000000000..2c5149646 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.c @@ -0,0 +1,678 @@ + +/* + * File: z_en_reeba.c + * Overlay: ovl_En_Reeba + * Description: Leever + */ + +#include "z_en_reeba.h" +#include "overlays/actors/ovl_En_Encount1/z_en_encount1.h" +#include "vt.h" +#include "objects/object_reeba/object_reeba.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_27) + +void EnReeba_Init(Actor* thisx, GlobalContext* globalCtx); +void EnReeba_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnReeba_Update(Actor* thisx, GlobalContext* globalCtx); +void EnReeba_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AE4F40(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5054(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5270(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5688(EnReeba* this, GlobalContext* globalCtx); +void func_80AE56E0(EnReeba* this, GlobalContext* globalCtx); +void func_80AE538C(EnReeba* this, GlobalContext* globalCtx); +void func_80AE53AC(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5E48(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5854(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5C38(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5938(EnReeba* this, GlobalContext* globalCtx); +void func_80AE5A9C(EnReeba* this, GlobalContext* globalCtx); + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(2, 0xE), + /* Slingshot */ DMG_ENTRY(1, 0xE), + /* Explosive */ DMG_ENTRY(2, 0xE), + /* Boomerang */ DMG_ENTRY(1, 0xC), + /* Normal arrow */ DMG_ENTRY(2, 0xE), + /* Hammer swing */ DMG_ENTRY(2, 0xE), + /* Hookshot */ DMG_ENTRY(2, 0xD), + /* Kokiri sword */ DMG_ENTRY(1, 0xE), + /* Master sword */ DMG_ENTRY(4, 0xE), + /* Giant's Knife */ DMG_ENTRY(6, 0xE), + /* Fire arrow */ DMG_ENTRY(2, 0xE), + /* Ice arrow */ DMG_ENTRY(4, 0x3), + /* Light arrow */ DMG_ENTRY(2, 0xE), + /* Unk arrow 1 */ DMG_ENTRY(2, 0xE), + /* Unk arrow 2 */ DMG_ENTRY(2, 0xE), + /* Unk arrow 3 */ DMG_ENTRY(2, 0xE), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(4, 0x3), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(2, 0xE), + /* Giant spin */ DMG_ENTRY(8, 0xE), + /* Master spin */ DMG_ENTRY(4, 0xE), + /* Kokiri jump */ DMG_ENTRY(2, 0xE), + /* Giant jump */ DMG_ENTRY(8, 0xE), + /* Master jump */ DMG_ENTRY(4, 0xE), + /* Unknown 1 */ DMG_ENTRY(0, 0x1), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +const ActorInit En_Reeba_InitVars = { + ACTOR_EN_REEBA, + ACTORCAT_MISC, + FLAGS, + OBJECT_REEBA, + sizeof(EnReeba), + (ActorFunc)EnReeba_Init, + (ActorFunc)EnReeba_Destroy, + (ActorFunc)EnReeba_Update, + (ActorFunc)EnReeba_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT5, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x08, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 40, 0, { 0, 0, 0 } }, +}; + +void EnReeba_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnReeba* this = (EnReeba*)thisx; + s32 surfaceType; + + this->actor.naviEnemyId = 0x47; + this->actor.targetMode = 3; + this->actor.gravity = -3.5f; + this->actor.focus.pos = this->actor.world.pos; + SkelAnime_Init(globalCtx, &this->skelanime, &object_reeba_Skel_001EE8, &object_reeba_Anim_0001E4, this->jointTable, + this->morphTable, 18); + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.colChkInfo.health = 4; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->isBig = this->actor.params; + this->scale = 0.04f; + + if (this->isBig) { + this->collider.dim.radius = 35; + this->collider.dim.height = 45; + this->scale *= 1.5f; + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ リーバぼす登場 ☆☆☆☆☆ %f\n" VT_RST, this->scale); + this->actor.colChkInfo.health = 20; + this->collider.info.toucher.effect = 4; + this->collider.info.toucher.damage = 16; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + } + + this->actor.shape.yOffset = this->unk_284 = this->scale * -27500.0f; + ActorShape_Init(&this->actor.shape, this->actor.shape.yOffset, ActorShadow_DrawCircle, 0.0f); + this->actor.colChkInfo.damageTable = &sDamageTable; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 35.0f, 60.0f, 60.0f, 0x1D); + + surfaceType = func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + + if ((surfaceType != 4) && (surfaceType != 7)) { + Actor_Kill(&this->actor); + return; + } + + this->actionfunc = func_80AE4F40; +} + +void EnReeba_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnReeba* this = (EnReeba*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + + if (this->actor.parent != NULL) { + EnEncount1* spawner = (EnEncount1*)this->actor.parent; + + if (spawner->actor.update != NULL) { + if (spawner->curNumSpawn > 0) { + spawner->curNumSpawn--; + } + if (this->isBig) { + spawner->bigLeever = NULL; + spawner->timer = 600; + } + } + } +} + +void func_80AE4F40(EnReeba* this, GlobalContext* globalCtx) { + f32 frames = Animation_GetLastFrame(&object_reeba_Anim_0001E4); + Player* player = GET_PLAYER(globalCtx); + s16 playerSpeed; + + Animation_Change(&this->skelanime, &object_reeba_Anim_0001E4, 2.0f, 0.0f, frames, ANIMMODE_LOOP, -10.0f); + + playerSpeed = fabsf(player->linearVelocity); + this->unk_278 = 20 - playerSpeed * 2; + if (this->unk_278 < 0) { + this->unk_278 = 2; + } + if (this->unk_278 > 20) { + this->unk_278 = 20; + } + + this->actor.flags &= ~ACTOR_FLAG_27; + this->actor.world.pos.y = this->actor.floorHeight; + + if (this->isBig) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_BIG_APPEAR); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_APPEAR); + } + + this->actionfunc = func_80AE5054; +} + +void func_80AE5054(EnReeba* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 playerLinearVel; + + SkelAnime_Update(&this->skelanime); + + if ((globalCtx->gameplayFrames % 4) == 0) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, this->actor.shape.shadowScale, 1, + 8.0f, 500, 10, 1); + } + + if (this->unk_278 == 0) { + Math_ApproachF(&this->actor.shape.shadowScale, 12.0f, 1.0f, 1.0f); + if (this->actor.shape.yOffset < 0.0f) { + Math_ApproachZeroF(&this->actor.shape.yOffset, 1.0f, this->unk_288); + Math_ApproachF(&this->unk_288, 300.0f, 1.0f, 5.0f); + } else { + this->unk_288 = 0.0f; + this->actor.shape.yOffset = 0.0f; + playerLinearVel = player->linearVelocity; + + switch (this->unk_280) { + case 0: + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + break; + case 1: + this->actor.world.rot.y = this->actor.yawTowardsPlayer + (800.0f * playerLinearVel); + break; + case 2: + case 3: + this->actor.world.rot.y = + this->actor.yawTowardsPlayer + + (player->actor.shape.rot.y - this->actor.yawTowardsPlayer) * (playerLinearVel * 0.15f); + break; + case 4: + this->actor.world.rot.y = this->actor.yawTowardsPlayer - (800.0f * playerLinearVel); + break; + } + + if (this->isBig) { + this->actionfunc = func_80AE538C; + } else { + this->unk_272 = 130; + this->actor.speedXZ = Rand_ZeroFloat(4.0f) + 6.0f; + this->actionfunc = func_80AE5270; + } + } + } +} + +void func_80AE5270(EnReeba* this, GlobalContext* globalCtx) { + s32 surfaceType; + + SkelAnime_Update(&this->skelanime); + + if (this->actor.shape.shadowScale < 12.0f) { + Math_ApproachF(&this->actor.shape.shadowScale, 12.0f, 3.0f, 1.0f); + } + + surfaceType = func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + + if ((surfaceType != 4) && (surfaceType != 7)) { + this->actor.speedXZ = 0.0f; + this->actionfunc = func_80AE5688; + } else if ((this->unk_272 == 0) || (this->actor.xzDistToPlayer < 30.0f) || (this->actor.xzDistToPlayer > 400.0f) || + (this->actor.bgCheckFlags & 8)) { + this->actionfunc = func_80AE5688; + } else if (this->unk_274 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_MOVE); + this->unk_274 = 10; + } +} + +void func_80AE538C(EnReeba* this, GlobalContext* globalCtx) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2; + this->actionfunc = func_80AE53AC; +} + +void func_80AE53AC(EnReeba* this, GlobalContext* globalCtx) { + f32 speed; + s16 yawDiff; + s16 yaw; + s32 surfaceType; + + SkelAnime_Update(&this->skelanime); + + if (this->actor.shape.shadowScale < 12.0f) { + Math_ApproachF(&this->actor.shape.shadowScale, 12.0f, 3.0f, 1.0f); + } + + surfaceType = func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + + if (((surfaceType != 4) && (surfaceType != 7)) || (this->actor.xzDistToPlayer > 400.0f) || + (this->actor.bgCheckFlags & 8)) { + this->actionfunc = func_80AE5688; + } else { + if ((this->actor.xzDistToPlayer < 70.0f) && (this->unk_270 == 0)) { + this->unk_270 = 30; + } + + speed = (this->actor.xzDistToPlayer - 20.0f) / ((Rand_ZeroOne() * 50.0f) + 150.0f); + this->actor.speedXZ += speed * 1.8f; + if (this->actor.speedXZ >= 3.0f) { + this->actor.speedXZ = 3.0f; + } + if (this->actor.speedXZ < -3.0f) { + this->actor.speedXZ = -3.0f; + } + + yawDiff = (this->unk_270 == 0) ? this->actor.yawTowardsPlayer : -this->actor.yawTowardsPlayer; + yawDiff -= this->actor.world.rot.y; + yaw = (yawDiff > 0) ? ((yawDiff / 31.0f) + 10.0f) : ((yawDiff / 31.0f) - 10.0f); + this->actor.world.rot.y += yaw * 2.0f; + + if (this->unk_274 == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_MOVE); + this->unk_274 = 20; + } + } +} + +void func_80AE561C(EnReeba* this, GlobalContext* globalCtx) { + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 0.3f); + + if (this->unk_272 == 0) { + if (this->isBig) { + this->actionfunc = func_80AE538C; + } else { + this->actionfunc = func_80AE5688; + } + } +} + +void func_80AE5688(EnReeba* this, GlobalContext* globalCtx) { + this->unk_27E = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_AKINDONUTS_HIDE); + this->actor.flags |= ACTOR_FLAG_27; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actionfunc = func_80AE56E0; +} + +void func_80AE56E0(EnReeba* this, GlobalContext* globalCtx) { + Math_ApproachZeroF(&this->actor.shape.shadowScale, 1.0f, 0.3f); + Math_ApproachZeroF(&this->actor.speedXZ, 0.1f, 0.3f); + SkelAnime_Update(&this->skelanime); + + if ((this->unk_284 + 10.0f) <= this->actor.shape.yOffset) { + if ((globalCtx->gameplayFrames % 4) == 0) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, this->actor.shape.shadowScale, 1, + 8.0f, 500, 10, 1); + } + + Math_ApproachF(&this->actor.shape.yOffset, this->unk_284, 1.0f, this->unk_288); + Math_ApproachF(&this->unk_288, 300.0f, 1.0f, 5.0f); + } else { + Actor_Kill(&this->actor); + } +} + +void func_80AE57F0(EnReeba* this, GlobalContext* globalCtx) { + this->unk_276 = 14; + this->actor.speedXZ = -8.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + this->actionfunc = func_80AE5854; +} + +void func_80AE5854(EnReeba* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelanime); + + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 1.0f; + } + + if (this->unk_276 == 0) { + if (this->isBig) { + this->unk_270 = 30; + this->actionfunc = func_80AE538C; + } else { + this->actionfunc = func_80AE5688; + } + } +} + +void func_80AE58EC(EnReeba* this, GlobalContext* globalCtx) { + this->unk_278 = 14; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = -8.0f; + this->actor.flags |= ACTOR_FLAG_27; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actionfunc = func_80AE5938; +} + +void func_80AE5938(EnReeba* this, GlobalContext* globalCtx) { + Vec3f pos; + f32 scale; + + if (this->unk_278 != 0) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 1.0f; + } + } else { + this->actor.speedXZ = 0.0f; + + if ((this->unk_27E == 4) || (this->actor.colChkInfo.health != 0)) { + if (this->unk_27E == 2) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(20.0f); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(20.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(20.0f); + scale = 3.0f; + + if (this->isBig) { + scale = 6.0f; + } + + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &pos, 150, 150, 150, 250, 235, 245, 255, scale); + } + + this->unk_278 = 66; + this->actionfunc = func_80AE5E48; + } else { + this->unk_278 = 30; + this->actionfunc = func_80AE5A9C; + } + } +} + +void func_80AE5A9C(EnReeba* this, GlobalContext* globalCtx) { + Vec3f pos; + f32 scale; + + if (this->unk_278 != 0) { + if ((this->unk_27E == 2) && ((this->unk_278 & 0xF) == 0)) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(20.0f); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(20.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(20.0f); + + scale = 3.0f; + if (this->isBig) { + scale = 6.0f; + } + + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &pos, 150, 150, 150, 250, 235, 245, 255, scale); + } + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_DEAD); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actionfunc = func_80AE5C38; + } +} + +void func_80AE5BC4(EnReeba* this, GlobalContext* globalCtx) { + this->actor.speedXZ = -8.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + this->unk_278 = 14; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionfunc = func_80AE5C38; +} + +void func_80AE5C38(EnReeba* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f accel = { 0.0f, 0.0f, 0.0f }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + + if (this->unk_278 != 0) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 1.0f; + } + } else { + this->actor.speedXZ = 0.0f; + Math_ApproachZeroF(&this->scale, 0.1f, 0.01f); + + if (this->scale < 0.01f) { + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z; + + velocity.y = 4.0f; + + EffectSsDeadDb_Spawn(globalCtx, &pos, &velocity, &accel, 120, 0, 255, 255, 255, 255, 255, 0, 0, 1, 9, true); + + if (!this->isBig) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &pos, 0xE0); + } else { + Item_DropCollectibleRandom(globalCtx, &this->actor, &pos, 0xC0); + } + + if (this->actor.parent != NULL) { + EnEncount1* spawner = (EnEncount1*)this->actor.parent; + + if ((spawner->actor.update != NULL) && !this->isBig) { + if (spawner->killCount < 10) { + spawner->killCount++; + } + // "How many are dead?" + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 何匹DEAD? ☆☆☆☆☆%d\n" VT_RST, spawner->killCount); + osSyncPrintf("\n\n"); + } + + Actor_Kill(&this->actor); + } + } + } +} + +void func_80AE5E48(EnReeba* this, GlobalContext* globalCtx) { + if (this->unk_278 < 37) { + this->actor.shape.rot.x = Rand_CenteredFloat(3000.0f); + this->actor.shape.rot.z = Rand_CenteredFloat(3000.0f); + + if (this->unk_278 == 0) { + if (this->isBig) { + this->actionfunc = func_80AE538C; + } else { + this->actionfunc = func_80AE5688; + } + } + } +} + +void func_80AE5EDC(EnReeba* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + + if ((this->actionfunc != func_80AE5C38) && (this->actionfunc != func_80AE5854)) { + this->actor.shape.rot.x = this->actor.shape.rot.z = 0; + this->unk_27E = 0; + + switch (this->actor.colChkInfo.damageEffect) { + case 11: // none + case 12: // boomerang + if ((this->actor.colChkInfo.health > 1) && (this->unk_27E != 4)) { + this->unk_27E = 4; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + this->actionfunc = func_80AE58EC; + break; + } + case 13: // hookshot/longshot + if ((this->actor.colChkInfo.health > 2) && (this->unk_27E != 4)) { + this->unk_27E = 4; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actionfunc = func_80AE58EC; + break; + } + case 14: + this->unk_27C = 6; + Actor_ApplyDamage(&this->actor); + if (this->actor.colChkInfo.health == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_DEAD); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actionfunc = func_80AE5BC4; + } else { + if (this->actionfunc == func_80AE5E48) { + this->actor.shape.rot.x = this->actor.shape.rot.z = 0; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_DAMAGE); + this->actionfunc = func_80AE57F0; + } + break; + case 3: // ice arrows/ice magic + Actor_ApplyDamage(&this->actor); + this->unk_27C = 2; + this->unk_27E = 2; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 80); + this->actionfunc = func_80AE58EC; + break; + case 1: // unknown + if (this->unk_27E != 4) { + this->unk_27E = 4; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 80); + this->actionfunc = func_80AE58EC; + } + break; + } + } + } +} + +void EnReeba_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnReeba* this = (EnReeba*)thisx; + Player* player = GET_PLAYER(globalCtx); + + func_80AE5EDC(this, globalCtx); + this->actionfunc(this, globalCtx); + Actor_SetScale(&this->actor, this->scale); + + if (this->unk_270 != 0) { + this->unk_270--; + } + + if (this->unk_272 != 0) { + this->unk_272--; + } + + if (this->unk_278 != 0) { + this->unk_278--; + } + + if (this->unk_274 != 0) { + this->unk_274--; + } + + if (this->unk_276 != 0) { + this->unk_276--; + } + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 35.0f, 60.0f, 60.0f, 0x1D); + + if (this->collider.base.atFlags & AT_BOUNCED) { + this->collider.base.atFlags &= ~AT_BOUNCED; + + if ((this->actionfunc == func_80AE5270) || (this->actionfunc == func_80AE53AC)) { + this->actor.speedXZ = 8.0f; + this->actor.world.rot.y *= -1.0f; + this->unk_272 = 14; + this->actionfunc = func_80AE561C; + return; + } + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + if ((this->collider.base.at == &player->actor) && !this->isBig && (this->actionfunc != func_80AE56E0)) { + this->actionfunc = func_80AE5688; + } + } + + this->actor.focus.pos = this->actor.world.pos; + + if (!this->isBig) { + this->actor.focus.pos.y += 15.0f; + } else { + this->actor.focus.pos.y += 30.0f; + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + + if ((this->actor.shape.yOffset >= -700.0f) && (this->actor.colChkInfo.health > 0) && + (this->actionfunc != func_80AE56E0)) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (!(this->actor.shape.yOffset < 0.0f)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if ((this->actionfunc == func_80AE5270) || (this->actionfunc == func_80AE53AC)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + } +} + +void EnReeba_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnReeba* this = (EnReeba*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_reeba.c", 1062); + + func_80093D18(globalCtx->state.gfxCtx); + + if (this->isBig) { + gDPSetPrimColor(POLY_OPA_DISP++, 0x0, 0x01, 155, 55, 255, 255); + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0x0, 0x01, 255, 255, 255, 255); + } + + SkelAnime_DrawOpa(globalCtx, this->skelanime.skeleton, this->skelanime.jointTable, NULL, NULL, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_reeba.c", 1088); + + if (BREG(0)) { + Vec3f debugPos; + + debugPos.x = (Math_SinS(this->actor.world.rot.y) * 30.0f) + this->actor.world.pos.x; + debugPos.y = this->actor.world.pos.y + 20.0f; + debugPos.z = (Math_CosS(this->actor.world.rot.y) * 30.0f) + this->actor.world.pos.z; + DebugDisplay_AddObject(debugPos.x, debugPos.y, debugPos.z, this->actor.world.rot.x, this->actor.world.rot.y, + this->actor.world.rot.z, 1.0f, 1.0f, 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.h b/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.h new file mode 100644 index 000000000..397ff92f6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Reeba/z_en_reeba.h @@ -0,0 +1,38 @@ +#ifndef Z_EN_REEBA_H +#define Z_EN_REEBA_H + +#include "ultra64.h" +#include "global.h" + +struct EnReeba; + +typedef void (*EnReebaActionFunc)(struct EnReeba*, GlobalContext*); + +typedef struct EnReeba { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelanime; + /* 0x0190 */ Vec3s jointTable[18]; + /* 0x01FC */ Vec3s morphTable[18]; + /* 0x0268 */ char unk_268[0x4]; + /* 0x026C */ EnReebaActionFunc actionfunc; + /* 0x0270 */ s16 unk_270; + /* 0x0272 */ s16 unk_272; + /* 0x0274 */ s16 unk_274; + /* 0x0276 */ s16 unk_276; + /* 0x0278 */ s16 unk_278; + /* 0x027A */ s16 isBig; + /* 0x027C */ s16 unk_27C; + /* 0x027E */ s16 unk_27E; + /* 0x0280 */ s16 unk_280; + /* 0x0284 */ f32 unk_284; + /* 0x0288 */ f32 unk_288; + /* 0x028C */ f32 scale; + /* 0x0290 */ ColliderCylinder collider; +} EnReeba; // size = 0x02DC + +typedef enum { + /* 0 */ LEEVER_SMALL, + /* 1 */ LEEVER_BIG +} LeeverParam; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_River_Sound/z_en_river_sound.c b/soh/src/overlays/actors/ovl_En_River_Sound/z_en_river_sound.c new file mode 100644 index 000000000..10fdd7c62 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_River_Sound/z_en_river_sound.c @@ -0,0 +1,253 @@ +/* + * File: z_en_river_sound.c + * Overlay: ovl_En_River_Sound + * Description: Ambient Sound Effects + */ + +#include "z_en_river_sound.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnRiverSound_Init(Actor* thisx, GlobalContext* globalCtx); +void EnRiverSound_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnRiverSound_Update(Actor* thisx, GlobalContext* globalCtx); +void EnRiverSound_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_River_Sound_InitVars = { + ACTOR_EN_RIVER_SOUND, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnRiverSound), + (ActorFunc)EnRiverSound_Init, + (ActorFunc)EnRiverSound_Destroy, + (ActorFunc)EnRiverSound_Update, + (ActorFunc)EnRiverSound_Draw, + NULL, +}; + +void EnRiverSound_Init(Actor* thisx, GlobalContext* globalCtx) { + EnRiverSound* this = (EnRiverSound*)thisx; + + this->playSound = 0; + this->pathIndex = (this->actor.params >> 8) & 0xFF; + this->actor.params = this->actor.params & 0xFF; + + if (this->actor.params >= RS_MAX) { // used for ganon and ganon_boss scenes + func_800F4870(this->actor.params - RS_MAX); + Actor_Kill(&this->actor); + } else if (this->actor.params == RS_UNK_F7) { + Audio_PlayNatureAmbienceSequence(NATURE_ID_KOKIRI_REGION); + Actor_Kill(&this->actor); + } else if (this->actor.params == RS_SARIAS_SONG) { + if (!CHECK_QUEST_ITEM(QUEST_SONG_LULLABY) || CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) { + Actor_Kill(&this->actor); + } + } +} + +void EnRiverSound_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnRiverSound* this = (EnRiverSound*)thisx; + + if (this->actor.params == RS_SARIAS_SONG) { + Audio_ClearSariaBgmAtPos(&this->actor.projectedPos); + } else if (this->actor.params == RS_UNK_13) { + Audio_ClearSariaBgm2(); + } +} + +s32 func_80AE6A54(Vec3f* arg0, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3) { + Vec3f vec[3]; + f32 temp; + + vec[0].x = arg0->x - arg2->x; + vec[0].y = arg0->y - arg2->y; + vec[0].z = arg0->z - arg2->z; + + vec[1].x = arg1->x - arg2->x; + vec[1].y = arg1->y - arg2->y; + vec[1].z = arg1->z - arg2->z; + + vec[2].x = vec[1].x - vec[0].x; + vec[2].y = vec[1].y - vec[0].y; + vec[2].z = vec[1].z - vec[0].z; + + temp = DOTXYZ(vec[2], vec[0]); + + if ((DOTXYZ(vec[2], vec[1]) * temp) < 0.0f) { + temp = -temp / (SQ(vec[2].x) + SQ(vec[2].y) + SQ(vec[2].z)); + + arg3->x = (vec[2].x * temp) + arg0->x; + arg3->y = (vec[2].y * temp) + arg0->y; + arg3->z = (vec[2].z * temp) + arg0->z; + + return true; + } + + return false; +} + +/** + * Writes the position along the river path to `soundPos` based on the `hearPos`, which is usually the position of the + * player. + * Returns true if the distance between the `hearPos` and `soundPos` is less than 10000, false if not. + */ +s32 EnRiverSound_GetSoundPos(Vec3s* points, s32 numPoints, Vec3f* hearPos, Vec3f* soundPos) { + s32 i; + s32 pointIdx; + s32 sp78[2] = { 0, 0 }; + Vec3f pointLoc; + Vec3f sp60; + Vec3f sp54; + Vec3f vec; + f32 pointDist = 10000.0f; + Vec3s* point; + + for (i = 0; i < numPoints; i++) { + f32 dist; + + vec.x = points[i].x; + vec.y = points[i].y; + vec.z = points[i].z; + dist = Math_Vec3f_DistXYZ(hearPos, &vec); + + if (dist < pointDist) { + pointDist = dist; + pointIdx = i; + } + } + + if (pointDist >= 10000.0f) { + return false; + } + + point = &points[pointIdx]; + pointLoc.x = point->x; + pointLoc.y = point->y; + pointLoc.z = point->z; + + if (pointIdx != 0) { + vec.x = point[-1].x; + vec.y = point[-1].y; + vec.z = point[-1].z; + sp78[0] = func_80AE6A54(&vec, &pointLoc, hearPos, &sp54); + } + + if (pointIdx + 1 != numPoints) { + vec.x = point[1].x; + vec.y = point[1].y; + vec.z = point[1].z; + sp78[1] = func_80AE6A54(&pointLoc, &vec, hearPos, &sp60); + } + + if (sp78[0] && sp78[1]) { + if (!func_80AE6A54(&sp54, &sp60, hearPos, soundPos)) { + soundPos->x = (sp54.x + sp60.x) * 0.5f; + soundPos->y = (sp54.y + sp60.y) * 0.5f; + soundPos->z = (sp54.z + sp60.z) * 0.5f; + } + } else if (sp78[0]) { + soundPos->x = sp54.x; + soundPos->y = sp54.y; + soundPos->z = sp54.z; + } else if (sp78[1]) { + soundPos->x = sp60.x; + soundPos->y = sp60.y; + soundPos->z = sp60.z; + } else { + soundPos->x = pointLoc.x; + soundPos->y = pointLoc.y; + soundPos->z = pointLoc.z; + } + + return true; +} + +void EnRiverSound_Update(Actor* thisx, GlobalContext* globalCtx) { + Path* path; + Vec3f* pos; + Player* player = GET_PLAYER(globalCtx); + EnRiverSound* this = (EnRiverSound*)thisx; + s32 sp34; + + if ((thisx->params == RS_UNK_0) || (thisx->params == RS_UNK_4) || (thisx->params == RS_UNK_5)) { + path = &globalCtx->setupPathList[this->pathIndex]; + pos = &thisx->world.pos; + + if (EnRiverSound_GetSoundPos(SEGMENTED_TO_VIRTUAL(path->points), path->count, &player->actor.world.pos, pos)) { + if (BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &thisx->floorPoly, &sp34, thisx, pos) != + BGCHECK_Y_MIN) { + // Get the sound volume pitch based on the speed of the river current under the actor + this->soundPitchIndex = SurfaceType_GetConveyorSpeed(&globalCtx->colCtx, thisx->floorPoly, sp34); + } else { + this->soundPitchIndex = 0; + } + + if (this->soundPitchIndex == 0) { + if (thisx->params == RS_UNK_4) { + this->soundPitchIndex = 0; + } else if (thisx->params == RS_UNK_0) { + this->soundPitchIndex = 1; + } else { + this->soundPitchIndex = 2; + } + } else { + this->soundPitchIndex--; + this->soundPitchIndex = CLAMP_MAX(this->soundPitchIndex, 2); + } + } + } else if ((thisx->params == RS_UNK_13) || (thisx->params == RS_UNK_19)) { + func_8002DBD0(&player->actor, &thisx->home.pos, &thisx->world.pos); + } else if (globalCtx->sceneNum == SCENE_DDAN_BOSS && Flags_GetClear(globalCtx, thisx->room)) { + Actor_Kill(thisx); + } +} + +void EnRiverSound_Draw(Actor* thisx, GlobalContext* globalCtx) { + static s16 soundEffects[] = { + 0, + NA_SE_EV_WATER_WALL - SFX_FLAG, + NA_SE_EV_MAGMA_LEVEL - SFX_FLAG, + NA_SE_EV_WATER_WALL_BIG - SFX_FLAG, + 0, + 0, + NA_SE_EV_MAGMA_LEVEL_M - SFX_FLAG, + NA_SE_EV_MAGMA_LEVEL_L - SFX_FLAG, + NA_SE_EV_WATERDROP - SFX_FLAG, + NA_SE_EV_FOUNTAIN - SFX_FLAG, + NA_SE_EV_CROWD - SFX_FLAG, + 0, + NA_SE_EV_SARIA_MELODY - SFX_FLAG, + 0, + NA_SE_EV_SAND_STORM - SFX_FLAG, + NA_SE_EV_WATER_BUBBLE - SFX_FLAG, + NA_SE_EV_KENJA_ENVIROMENT_0 - SFX_FLAG, + NA_SE_EV_KENJA_ENVIROMENT_1 - SFX_FLAG, + NA_SE_EV_EARTHQUAKE - SFX_FLAG, + 0, + NA_SE_EV_TORCH - SFX_FLAG, + NA_SE_EV_COW_CRY_LV - SFX_FLAG, + }; + static f32 soundPitch[] = { 0.7f, 1.0f, 1.4f }; + EnRiverSound* this = (EnRiverSound*)thisx; + + if (!(this->playSound)) { + this->playSound = true; + } else if ((this->actor.params == RS_UNK_0) || (this->actor.params == RS_UNK_4) || + (this->actor.params == RS_UNK_5)) { + Audio_PlaySoundRiver(&this->actor.projectedPos, soundPitch[this->soundPitchIndex]); + } else if (this->actor.params == RS_UNK_11) { + func_800F4A54(90); + } else if (this->actor.params == RS_SARIAS_SONG) { + func_800F4E30(&this->actor.projectedPos, this->actor.xzDistToPlayer); + } else if (this->actor.params == RS_UNK_13) { + Audio_PlaySariaBgm(&this->actor.home.pos, NA_BGM_SARIA_THEME, 1000); + } else if (this->actor.params == RS_UNK_19) { + Audio_PlaySariaBgm(&this->actor.home.pos, NA_BGM_GREAT_FAIRY, 800); + } else if ((this->actor.params == RS_SANDSTORM) || (this->actor.params == RS_CHAMBER_OF_SAGES_1) || + (this->actor.params == RS_CHAMBER_OF_SAGES_2) || (this->actor.params == RS_RUMBLING)) { + func_800788CC(soundEffects[this->actor.params]); + } else { + Audio_PlayActorSound2(&this->actor, soundEffects[this->actor.params]); + } +} diff --git a/soh/src/overlays/actors/ovl_En_River_Sound/z_en_river_sound.h b/soh/src/overlays/actors/ovl_En_River_Sound/z_en_river_sound.h new file mode 100644 index 000000000..48cdec740 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_River_Sound/z_en_river_sound.h @@ -0,0 +1,43 @@ +#ifndef Z_EN_RIVER_SOUND_H +#define Z_EN_RIVER_SOUND_H + +#include "ultra64.h" +#include "global.h" + +struct EnRiverSound; + +typedef struct EnRiverSound { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 playSound; + /* 0x014D */ u8 soundPitchIndex; + /* 0x014E */ s16 pathIndex; +} EnRiverSound; // size = 0x0150 + +typedef enum { + /* 0x00 */ RS_UNK_0, + /* 0x01 */ RS_SMALL_WATERFALL, + /* 0x02 */ RS_LAVA_BUBBLES_1, + /* 0x03 */ RS_LARGE_WATERFALL, + /* 0x04 */ RS_UNK_4, + /* 0x05 */ RS_UNK_5, + /* 0x06 */ RS_LAVA_BUBBLES_2, + /* 0x07 */ RS_LAVA_BUBBLES_3, + /* 0x08 */ RS_DRIPPING_WATER, + /* 0x09 */ RS_FOUNTAIN_WATER, + /* 0x0A */ RS_MARKET_CROWD, + /* 0x0B */ RS_UNK_11, + /* 0x0C */ RS_SARIAS_SONG, + /* 0x0D */ RS_UNK_13, + /* 0x0E */ RS_SANDSTORM, + /* 0x0F */ RS_LAKESIDE_LAB_TANK, + /* 0x10 */ RS_CHAMBER_OF_SAGES_1, + /* 0x11 */ RS_CHAMBER_OF_SAGES_2, + /* 0x12 */ RS_RUMBLING, + /* 0x13 */ RS_UNK_19, + /* 0x14 */ RS_TORCH_CRACKLING, + /* 0x15 */ RS_COW_MOOING, + /* 0xF7 */ RS_UNK_F7 = 0xF7, + /* 0xF8 */ RS_MAX +} RiverSoundType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Rl/z_en_rl.c b/soh/src/overlays/actors/ovl_En_Rl/z_en_rl.c new file mode 100644 index 000000000..96129766c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Rl/z_en_rl.c @@ -0,0 +1,394 @@ +/* + * File: z_en_rl.c + * Overlay: En_Rl + * Description: Rauru + */ + +#include "z_en_rl.h" +#include "vt.h" +#include "objects/object_rl/object_rl.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnRl_Init(Actor* thisx, GlobalContext* globalCtx); +void EnRl_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnRl_Update(Actor* thisx, GlobalContext* globalCtx); +void EnRl_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AE7798(EnRl* this, GlobalContext* globalCtx); +void func_80AE77B8(EnRl* this, GlobalContext* globalCtx); +void func_80AE77F8(EnRl* this, GlobalContext* globalCtx); +void func_80AE7838(EnRl* this, GlobalContext* globalCtx); +void func_80AE7C64(EnRl* this, GlobalContext* globalCtx); +void func_80AE7C94(EnRl* this, GlobalContext* globalCtx); +void func_80AE7CE8(EnRl* this, GlobalContext* globalCtx); +void func_80AE7D40(EnRl* this, GlobalContext* globalCtx); +void func_80AE7FD0(EnRl* this, GlobalContext* globalCtx); +void func_80AE7FDC(EnRl* this, GlobalContext* globalCtx); +void func_80AE7D94(EnRl* this, GlobalContext* globalCtx); + +static void* D_80AE81A0[] = { object_rl_Tex_003620, object_rl_Tex_003960, object_rl_Tex_003B60 }; +static s32 D_80AE81AC = 0; + +void EnRl_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnRl* this = (EnRl*)thisx; + D_80AE81AC = 0; + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void func_80AE72D0(EnRl* this) { + s32 pad[3]; + s16* timer = &this->timer; + s16* eyeTextureIndex = &this->eyeTextureIndex; + + if (DECR(*timer) == 0) { + *timer = Rand_S16Offset(60, 60); + } + + *eyeTextureIndex = *timer; + if (*eyeTextureIndex > 2) { + *eyeTextureIndex = 0; + } +} + +void func_80AE7358(EnRl* this) { + Animation_Change(&this->skelAnime, &object_rl_Anim_000A3C, 1.0f, 0.0f, + Animation_GetLastFrame(&object_rl_Anim_000A3C), ANIMMODE_LOOP, 0.0f); + this->action = 4; + this->drawConfig = 0; + this->alpha = 0; + this->lightBallSpawned = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_19C = 0.0f; +} + +void func_80AE73D8(EnRl* this, GlobalContext* globalCtx) { + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + if (D_80AE81AC) { + if (this->actor.params == 2) { + func_80AE7358(this); + } + D_80AE81AC = 0; + } + } else if (!D_80AE81AC) { + D_80AE81AC = 1; + } +} + +void func_80AE744C(EnRl* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +s32 func_80AE7494(EnRl* this) { + return SkelAnime_Update(&this->skelAnime); +} + +s32 func_80AE74B4(EnRl* this, GlobalContext* globalCtx, u16 arg2, s32 arg3) { + CsCmdActorAction* csCmdActorAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdActorAction = globalCtx->csCtx.npcActions[arg3]; + if (csCmdActorAction != NULL && csCmdActorAction->action == arg2) { + return 1; + } + } + return 0; +} + +s32 func_80AE74FC(EnRl* this, GlobalContext* globalCtx, u16 arg2, s32 arg3) { + CsCmdActorAction* csCmdActorAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdActorAction = globalCtx->csCtx.npcActions[arg3]; + if (csCmdActorAction != NULL && csCmdActorAction->action != arg2) { + return 1; + } + } + return 0; +} + +void func_80AE7544(EnRl* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_rl_Skel_007B38, &object_rl_Anim_000A3C, NULL, NULL, 0); +} + +void func_80AE7590(EnRl* this, GlobalContext* globalCtx) { + s32 pad; + Player* player; + Vec3f pos; + s16 sceneNum = globalCtx->sceneNum; + + if (gSaveContext.sceneSetupIndex == 4 && sceneNum == SCENE_KENJYANOMA && globalCtx->csCtx.state != CS_STATE_IDLE && + globalCtx->csCtx.npcActions[6] != NULL && globalCtx->csCtx.npcActions[6]->action == 2 && + !this->lightMedallionGiven) { + player = GET_PLAYER(globalCtx); + pos.x = player->actor.world.pos.x; + pos.y = player->actor.world.pos.y + 80.0f; + pos.z = player->actor.world.pos.z; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, pos.x, pos.y, pos.z, 0, 0, 0, 0xE); + Item_Give(globalCtx, ITEM_MEDALLION_LIGHT); + this->lightMedallionGiven = 1; + } +} + +void func_80AE7668(EnRl* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->drawConfig = 1; + this->action = 1; + player->actor.world.rot.y = player->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; +} + +void func_80AE7698(EnRl* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdActorAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdActorAction = globalCtx->csCtx.npcActions[0]; + if (csCmdActorAction != NULL && csCmdActorAction->action == 3) { + Animation_Change(&this->skelAnime, &object_rl_Anim_00040C, 1.0f, 0.0f, + Animation_GetLastFrame(&object_rl_Anim_00040C), ANIMMODE_ONCE, 0.0f); + this->action = 2; + } + } +} + +void func_80AE772C(EnRl* this, s32 arg1) { + if (arg1) { + Animation_Change(&this->skelAnime, &object_rl_Anim_000830, 1.0f, 0.0f, + Animation_GetLastFrame(&object_rl_Anim_000830), ANIMMODE_LOOP, 0.0f); + this->action = 3; + } +} + +void func_80AE7798(EnRl* this, GlobalContext* globalCtx) { + func_80AE7668(this, globalCtx); +} + +void func_80AE77B8(EnRl* this, GlobalContext* globalCtx) { + func_80AE744C(this, globalCtx); + func_80AE7494(this); + func_80AE72D0(this); + func_80AE7698(this, globalCtx); +} + +void func_80AE77F8(EnRl* this, GlobalContext* globalCtx) { + s32 temp; + + func_80AE744C(this, globalCtx); + temp = func_80AE7494(this); + func_80AE72D0(this); + func_80AE772C(this, temp); +} + +void func_80AE7838(EnRl* this, GlobalContext* globalCtx) { + func_80AE744C(this, globalCtx); + func_80AE7494(this); + func_80AE72D0(this); + func_80AE7590(this, globalCtx); +} + +void func_80AE7878(EnRl* this, GlobalContext* globalCtx) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_rl_Skel_007B38, &object_rl_Anim_000A3C, NULL, NULL, 0); + this->action = 4; + this->actor.shape.shadowAlpha = 0; +} + +void func_80AE78D4(EnRl* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + kREG(18) + 22.0f + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 5); +} + +void func_80AE7954(EnRl* this, GlobalContext* globalCtx) { + if (func_80AE74B4(this, globalCtx, 4, 0)) { + this->action = 5; + this->drawConfig = 2; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_19C = 0.0f; + } +} + +void func_80AE79A4(EnRl* this, GlobalContext* globalCtx) { + f32* unk_19C = &this->unk_19C; + s32 alpha = 255; + + if (func_80AE74B4(this, globalCtx, 4, 0)) { + *unk_19C += 1.0f; + if (*unk_19C >= kREG(5) + 10.0f) { + this->action = 7; + this->drawConfig = 1; + *unk_19C = kREG(5) + 10.0f; + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; + return; + } + } else { + *unk_19C -= 1.0f; + if (*unk_19C <= 0.0f) { + this->action = 4; + this->drawConfig = 0; + *unk_19C = 0.0f; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + return; + } + } + alpha = (*unk_19C / (kREG(5) + 10.0f)) * 255.0f; + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; +} + +void func_80AE7AF8(EnRl* this, GlobalContext* globalCtx) { + if (func_80AE74B4(this, globalCtx, 3, 0)) { + Animation_Change(&this->skelAnime, &object_rl_Anim_00040C, 1.0f, 0.0f, + Animation_GetLastFrame(&object_rl_Anim_00040C), ANIMMODE_ONCE, -8.0f); + this->action = 6; + } else if (func_80AE74FC(this, globalCtx, 4, 0)) { + this->action = 5; + this->drawConfig = 2; + this->unk_19C = kREG(5) + 10.0f; + this->alpha = 255; + if (!this->lightBallSpawned) { + func_80AE78D4(this, globalCtx); + this->lightBallSpawned = 1; + } + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80AE7BF8(EnRl* this, s32 arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &object_rl_Anim_000830, 1.0f, 0.0f, + Animation_GetLastFrame(&object_rl_Anim_000830), ANIMMODE_LOOP, 0.0f); + this->action = 7; + } +} + +void func_80AE7C64(EnRl* this, GlobalContext* globalCtx) { + func_80AE7954(this, globalCtx); + func_80AE73D8(this, globalCtx); +} + +void func_80AE7C94(EnRl* this, GlobalContext* globalCtx) { + func_80AE744C(this, globalCtx); + func_80AE7494(this); + func_80AE72D0(this); + func_80AE79A4(this, globalCtx); + func_80AE73D8(this, globalCtx); +} + +void func_80AE7CE8(EnRl* this, GlobalContext* globalCtx) { + s32 temp; + + func_80AE744C(this, globalCtx); + temp = func_80AE7494(this); + func_80AE72D0(this); + func_80AE7BF8(this, temp); + func_80AE73D8(this, globalCtx); +} + +void func_80AE7D40(EnRl* this, GlobalContext* globalCtx) { + func_80AE744C(this, globalCtx); + func_80AE7494(this); + func_80AE72D0(this); + func_80AE7AF8(this, globalCtx); + func_80AE73D8(this, globalCtx); +} + +void func_80AE7D94(EnRl* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 temp = this->eyeTextureIndex; + void* tex = D_80AE81A0[temp]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_rl_inKenjyanomaDemo02.c", 304); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(tex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(tex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + NULL, NULL, NULL, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_rl_inKenjyanomaDemo02.c", 331); +} + +static EnRlActionFunc sActionFuncs[] = { + func_80AE7798, func_80AE77B8, func_80AE77F8, func_80AE7838, + func_80AE7C64, func_80AE7C94, func_80AE7CE8, func_80AE7D40, +}; + +void EnRl_Update(Actor* thisx, GlobalContext* globalCtx) { + EnRl* this = (EnRl*)thisx; + + if ((this->action < 0) || (this->action > 7) || (sActionFuncs[this->action] == NULL)) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void EnRl_Init(Actor* thisx, GlobalContext* globalCtx) { + EnRl* this = (EnRl*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 50.0f); + if (this->actor.params == 2) { + func_80AE7878(this, globalCtx); + } else { + func_80AE7544(this, globalCtx); + } +} +void func_80AE7FD0(EnRl* this, GlobalContext* globalCtx) { +} + +void func_80AE7FDC(EnRl* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 temp = this->eyeTextureIndex; + void* tex = D_80AE81A0[temp]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_rl.c", 416); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(tex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(tex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, NULL, NULL, + &this->actor); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_rl.c", 437); +} + +static EnRlDrawFunc sDrawFuncs[] = { + func_80AE7FD0, + func_80AE7FDC, + func_80AE7D94, +}; + +void EnRl_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnRl* this = (EnRl*)thisx; + + if (this->drawConfig < 0 || this->drawConfig >= 3 || sDrawFuncs[this->drawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} + +const ActorInit En_Rl_InitVars = { + ACTOR_EN_RL, + ACTORCAT_NPC, + FLAGS, + OBJECT_RL, + sizeof(EnRl), + (ActorFunc)EnRl_Init, + (ActorFunc)EnRl_Destroy, + (ActorFunc)EnRl_Update, + (ActorFunc)EnRl_Draw, + NULL, +}; diff --git a/soh/src/overlays/actors/ovl_En_Rl/z_en_rl.h b/soh/src/overlays/actors/ovl_En_Rl/z_en_rl.h new file mode 100644 index 000000000..84a698dcd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Rl/z_en_rl.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_RL_H +#define Z_EN_RL_H + +#include "ultra64.h" +#include "global.h" + +struct EnRl; + +typedef void (*EnRlActionFunc)(struct EnRl*, GlobalContext*); +typedef void (*EnRlDrawFunc)(struct EnRl*, GlobalContext*); + +typedef struct EnRl { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ s16 eyeTextureIndex; + /* 0x0192 */ s16 timer; + /* 0x0194 */ s32 action; + /* 0x0198 */ s32 drawConfig; + /* 0x019C */ f32 unk_19C; + /* 0x01A0 */ s32 alpha; + /* 0x01A4 */ u32 lightBallSpawned; + /* 0x01A8 */ s32 lightMedallionGiven; +} EnRl; // size = 0x01AC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c new file mode 100644 index 000000000..db1e70009 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.c @@ -0,0 +1,905 @@ +/* + * File: z_en_rr.c + * Overlay: ovl_En_Rr + * Description: Like Like + */ + +#include "z_en_rr.h" +#include "objects/object_rr/object_rr.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_10) + +#define RR_MESSAGE_SHIELD (1 << 0) +#define RR_MESSAGE_TUNIC (1 << 1) +#define RR_MOUTH 4 +#define RR_BASE 0 + +typedef enum { + /* 0 */ REACH_NONE, + /* 1 */ REACH_EXTEND, + /* 2 */ REACH_STOP, + /* 3 */ REACH_OPEN, + /* 4 */ REACH_GAPE, + /* 5 */ REACH_CLOSE +} EnRrReachState; + +typedef enum { + /* 0x0 */ RR_DMG_NONE, + /* 0x1 */ RR_DMG_STUN, + /* 0x2 */ RR_DMG_FIRE, + /* 0x3 */ RR_DMG_ICE, + /* 0x4 */ RR_DMG_LIGHT_MAGIC, + /* 0xB */ RR_DMG_LIGHT_ARROW = 11, + /* 0xC */ RR_DMG_SHDW_ARROW, + /* 0xD */ RR_DMG_WIND_ARROW, + /* 0xE */ RR_DMG_SPRT_ARROW, + /* 0xF */ RR_DMG_NORMAL +} EnRrDamageEffect; + +typedef enum { + /* 0 */ RR_DROP_RANDOM_RUPEE, + /* 1 */ RR_DROP_MAGIC, + /* 2 */ RR_DROP_ARROW, + /* 3 */ RR_DROP_FLEXIBLE, + /* 4 */ RR_DROP_RUPEE_PURPLE, + /* 5 */ RR_DROP_RUPEE_RED +} EnRrDropType; + +void EnRr_Init(Actor* thisx, GlobalContext* globalCtx); +void EnRr_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnRr_Update(Actor* thisx, GlobalContext* globalCtx); +void EnRr_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnRr_InitBodySegments(EnRr* this, GlobalContext* globalCtx); + +void EnRr_SetupDamage(EnRr* this); +void EnRr_SetupDeath(EnRr* this); + +void EnRr_Approach(EnRr* this, GlobalContext* globalCtx); +void EnRr_Reach(EnRr* this, GlobalContext* globalCtx); +void EnRr_GrabPlayer(EnRr* this, GlobalContext* globalCtx); +void EnRr_Damage(EnRr* this, GlobalContext* globalCtx); +void EnRr_Death(EnRr* this, GlobalContext* globalCtx); +void EnRr_Retreat(EnRr* this, GlobalContext* globalCtx); +void EnRr_Stunned(EnRr* this, GlobalContext* globalCtx); + +const ActorInit En_Rr_InitVars = { + ACTOR_EN_RR, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_RR, + sizeof(EnRr), + (ActorFunc)EnRr_Init, + (ActorFunc)EnRr_Destroy, + (ActorFunc)EnRr_Update, + (ActorFunc)EnRr_Draw, + NULL, +}; + +static char* sDropNames[] = { + // "type 7", "small magic jar", "arrow", "fairy", "20 rupees", "50 rupees" + "タイプ7 ", "魔法の壷小", "矢 ", "妖精 ", "20ルピー ", "50ルピー ", +}; + +static ColliderCylinderInitType1 sCylinderInit1 = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 30, 55, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInitType1 sCylinderInit2 = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_NO_PUSH | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, 20, -10, { 0, 0, 0 } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, RR_DMG_NONE), + /* Deku stick */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Slingshot */ DMG_ENTRY(1, RR_DMG_NORMAL), + /* Explosive */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Boomerang */ DMG_ENTRY(0, RR_DMG_STUN), + /* Normal arrow */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Hammer swing */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Hookshot */ DMG_ENTRY(0, RR_DMG_STUN), + /* Kokiri sword */ DMG_ENTRY(1, RR_DMG_NORMAL), + /* Master sword */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Giant's Knife */ DMG_ENTRY(4, RR_DMG_NORMAL), + /* Fire arrow */ DMG_ENTRY(4, RR_DMG_FIRE), + /* Ice arrow */ DMG_ENTRY(4, RR_DMG_ICE), + /* Light arrow */ DMG_ENTRY(15, RR_DMG_LIGHT_ARROW), + /* Unk arrow 1 */ DMG_ENTRY(4, RR_DMG_WIND_ARROW), + /* Unk arrow 2 */ DMG_ENTRY(15, RR_DMG_SHDW_ARROW), + /* Unk arrow 3 */ DMG_ENTRY(15, RR_DMG_SPRT_ARROW), + /* Fire magic */ DMG_ENTRY(4, RR_DMG_FIRE), + /* Ice magic */ DMG_ENTRY(3, RR_DMG_ICE), + /* Light magic */ DMG_ENTRY(10, RR_DMG_LIGHT_MAGIC), + /* Shield */ DMG_ENTRY(0, RR_DMG_NONE), + /* Mirror Ray */ DMG_ENTRY(0, RR_DMG_NONE), + /* Kokiri spin */ DMG_ENTRY(1, RR_DMG_NORMAL), + /* Giant spin */ DMG_ENTRY(4, RR_DMG_NORMAL), + /* Master spin */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Kokiri jump */ DMG_ENTRY(2, RR_DMG_NORMAL), + /* Giant jump */ DMG_ENTRY(8, RR_DMG_NORMAL), + /* Master jump */ DMG_ENTRY(4, RR_DMG_NORMAL), + /* Unknown 1 */ DMG_ENTRY(10, RR_DMG_SPRT_ARROW), + /* Unblockable */ DMG_ENTRY(0, RR_DMG_NONE), + /* Hammer jump */ DMG_ENTRY(0, RR_DMG_NONE), + /* Unknown 2 */ DMG_ENTRY(0, RR_DMG_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x37, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +void EnRr_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnRr* this = (EnRr*)thisx; + s32 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.colChkInfo.health = 4; + Collider_InitCylinder(globalCtx, &this->collider1); + Collider_SetCylinderType1(globalCtx, &this->collider1, &this->actor, &sCylinderInit1); + Collider_InitCylinder(globalCtx, &this->collider2); + Collider_SetCylinderType1(globalCtx, &this->collider2, &this->actor, &sCylinderInit2); + Actor_SetFocus(&this->actor, 30.0f); + this->actor.scale.y = 0.013f; + this->actor.scale.x = this->actor.scale.z = 0.014f; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.velocity.y = this->actor.speedXZ = 0.0f; + this->actor.gravity = -0.4f; + this->actionTimer = 0; + this->eatenShield = 0; + this->eatenTunic = 0; + this->retreat = false; + this->grabTimer = 0; + this->invincibilityTimer = 0; + this->effectTimer = 0; + this->hasPlayer = false; + this->stopScroll = false; + this->ocTimer = 0; + this->reachState = this->isDead = false; + this->actionFunc = EnRr_Approach; + for (i = 0; i < 5; i++) { + this->bodySegs[i].height = this->bodySegs[i].heightTarget = this->bodySegs[i].scaleMod.x = + this->bodySegs[i].scaleMod.y = this->bodySegs[i].scaleMod.z = 0.0f; + } + EnRr_InitBodySegments(this, globalCtx); +} + +void EnRr_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnRr* this = (EnRr*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider1); + Collider_DestroyCylinder(globalCtx, &this->collider2); +} + +void EnRr_SetSpeed(EnRr* this, f32 speed) { + this->actor.speedXZ = speed; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_WALK); +} + +void EnRr_SetupReach(EnRr* this) { + static f32 segmentHeights[] = { 0.0f, 500.0f, 750.0f, 1000.0f, 1000.0f }; + s32 i; + + this->reachState = 1; + this->actionTimer = 20; + this->segPhaseVelTarget = 2500.0f; + this->segMoveRate = 0.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].heightTarget = segmentHeights[i]; + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.z = 0.8f; + this->bodySegs[i].rotTarget.x = 6000.0f; + this->bodySegs[i].rotTarget.z = 0.0f; + } + this->actionFunc = EnRr_Reach; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_UNARI); +} + +void EnRr_SetupNeutral(EnRr* this) { + s32 i; + + this->reachState = 0; + this->segMoveRate = 0.0f; + this->segPhaseVelTarget = 2500.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].heightTarget = 0.0f; + this->bodySegs[i].rotTarget.x = this->bodySegs[i].rotTarget.z = 0.0f; + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.z = 1.0f; + } + if (this->retreat) { + this->actionTimer = 100; + this->actionFunc = EnRr_Retreat; + } else { + this->actionTimer = 60; + this->actionFunc = EnRr_Approach; + } +} + +void EnRr_SetupGrabPlayer(EnRr* this, Player* player) { + s32 i; + + this->grabTimer = 100; + this->actor.flags &= ~ACTOR_FLAG_0; + this->ocTimer = 8; + this->hasPlayer = true; + this->reachState = 0; + this->segMoveRate = this->swallowOffset = this->actor.speedXZ = 0.0f; + this->pulseSizeTarget = 0.15f; + this->segPhaseVelTarget = 5000.0f; + this->wobbleSizeTarget = 512.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].heightTarget = 0.0f; + this->bodySegs[i].rotTarget.x = this->bodySegs[i].rotTarget.z = 0.0f; + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.z = 1.0f; + } + this->actionFunc = EnRr_GrabPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_DRINK); +} + +u8 EnRr_GetMessage(u8 shield, u8 tunic) { + u8 messageIndex = 0; + + if ((shield == 1 /* Deku shield */) || (shield == 2 /* Hylian shield */)) { + messageIndex = RR_MESSAGE_SHIELD; + } + if ((tunic == 2 /* Goron tunic */) || (tunic == 3 /* Zora tunic */)) { + messageIndex |= RR_MESSAGE_TUNIC; + } + + return messageIndex; +} + +void EnRr_SetupReleasePlayer(EnRr* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + u8 shield; + u8 tunic; + + this->actor.flags |= ACTOR_FLAG_0; + this->hasPlayer = false; + this->ocTimer = 110; + this->segMoveRate = 0.0f; + this->segPhaseVelTarget = 2500.0f; + this->wobbleSizeTarget = 2048.0f; + tunic = 0; + shield = 0; + if (CUR_EQUIP_VALUE(EQUIP_SHIELD) != 3 /* Mirror shield */) { + shield = Inventory_DeleteEquipment(globalCtx, EQUIP_SHIELD); + if (shield != 0) { + this->eatenShield = shield; + this->retreat = true; + } + } + if (CUR_EQUIP_VALUE(EQUIP_TUNIC) != 1 /* Kokiri tunic */) { + tunic = Inventory_DeleteEquipment(globalCtx, EQUIP_TUNIC); + if (tunic != 0) { + this->eatenTunic = tunic; + this->retreat = true; + } + } + player->actor.parent = NULL; + switch (EnRr_GetMessage(shield, tunic)) { + case RR_MESSAGE_SHIELD: + Message_StartTextbox(globalCtx, 0x305F, NULL); + break; + case RR_MESSAGE_TUNIC: + Message_StartTextbox(globalCtx, 0x3060, NULL); + break; + case RR_MESSAGE_TUNIC | RR_MESSAGE_SHIELD: + Message_StartTextbox(globalCtx, 0x3061, NULL); + break; + } + osSyncPrintf(VT_FGCOL(YELLOW) "%s[%d] : Rr_Catch_Cancel" VT_RST "\n", "../z_en_rr.c", 650); + func_8002F6D4(globalCtx, &this->actor, 4.0f, this->actor.shape.rot.y, 12.0f, 8); + if (this->actor.colorFilterTimer == 0) { + this->actionFunc = EnRr_Approach; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_THROW); + } else if (this->actor.colChkInfo.health != 0) { + EnRr_SetupDamage(this); + } else { + EnRr_SetupDeath(this); + } +} + +void EnRr_SetupDamage(EnRr* this) { + s32 i; + + this->reachState = 0; + this->actionTimer = 20; + this->segMoveRate = 0.0f; + this->segPhaseVelTarget = 2500.0f; + this->pulseSizeTarget = 0.0f; + this->wobbleSizeTarget = 0.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].heightTarget = 0.0f; + this->bodySegs[i].rotTarget.x = this->bodySegs[i].rotTarget.z = 0.0f; + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.z = 1.0f; + } + this->actionFunc = EnRr_Damage; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_DAMAGE); +} + +void EnRr_SetupApproach(EnRr* this) { + s32 i; + + this->segMoveRate = 0.0f; + this->pulseSizeTarget = 0.15f; + this->segPhaseVelTarget = 2500.0f; + this->wobbleSizeTarget = 2048.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].heightTarget = 0.0f; + this->bodySegs[i].rotTarget.x = this->bodySegs[i].rotTarget.z = 0.0f; + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.z = 1.0f; + } + this->actionFunc = EnRr_Approach; +} + +void EnRr_SetupDeath(EnRr* this) { + s32 i; + + this->isDead = true; + this->frameCount = 0; + this->shrinkRate = 0.0f; + this->segMoveRate = 0.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].heightTarget = 0.0f; + this->bodySegs[i].rotTarget.x = this->bodySegs[i].rotTarget.z = 0.0f; + } + this->actionFunc = EnRr_Death; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_DEAD); + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void EnRr_SetupStunned(EnRr* this) { + s32 i; + + this->stopScroll = true; + this->segMovePhase = 0; + this->segPhaseVel = 0.0f; + this->segPhaseVelTarget = 2500.0f; + this->segPulsePhaseDiff = 0.0f; + this->segWobblePhaseDiffX = 0.0f; + this->segWobbleXTarget = 3.0f; + this->segWobblePhaseDiffZ = 0.0f; + this->segWobbleZTarget = 1.0f; + this->pulseSize = 0.0f; + this->pulseSizeTarget = 0.15f; + this->wobbleSize = 0.0f; + this->wobbleSizeTarget = 2048.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].scaleMod.y = 0.0f; + this->bodySegs[i].rotTarget.x = 0.0f; + this->bodySegs[i].rotTarget.y = 0.0f; + this->bodySegs[i].rotTarget.z = 0.0f; + this->bodySegs[i].scale.x = this->bodySegs[i].scale.y = this->bodySegs[i].scale.z = + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.y = this->bodySegs[i].scaleTarget.z = 1.0f; + } + this->actionFunc = EnRr_Stunned; +} + +void EnRr_CollisionCheck(EnRr* this, GlobalContext* globalCtx) { + Vec3f hitPos; + Player* player = GET_PLAYER(globalCtx); + + if (this->collider2.base.acFlags & AC_HIT) { + this->collider2.base.acFlags &= ~AC_HIT; + // "Kakin" (not sure what this means) + osSyncPrintf(VT_FGCOL(GREEN) "カキン(%d)!!" VT_RST "\n", this->frameCount); + hitPos.x = this->collider2.info.bumper.hitPos.x; + hitPos.y = this->collider2.info.bumper.hitPos.y; + hitPos.z = this->collider2.info.bumper.hitPos.z; + CollisionCheck_SpawnShieldParticlesMetal2(globalCtx, &hitPos); + } else { + if (this->collider1.base.acFlags & AC_HIT) { + u8 dropType = RR_DROP_RANDOM_RUPEE; + + this->collider1.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != 0) { + hitPos.x = this->collider1.info.bumper.hitPos.x; + hitPos.y = this->collider1.info.bumper.hitPos.y; + hitPos.z = this->collider1.info.bumper.hitPos.z; + CollisionCheck_BlueBlood(globalCtx, NULL, &hitPos); + } + switch (this->actor.colChkInfo.damageEffect) { + case RR_DMG_LIGHT_ARROW: + dropType++; // purple rupee + case RR_DMG_SHDW_ARROW: + dropType++; // flexible + case RR_DMG_WIND_ARROW: + dropType++; // arrow + case RR_DMG_SPRT_ARROW: + dropType++; // magic jar + case RR_DMG_NORMAL: + // "ouch" + osSyncPrintf(VT_FGCOL(RED) "いてっ( %d : LIFE %d : DAMAGE %d : %x )!!" VT_RST "\n", + this->frameCount, this->actor.colChkInfo.health, this->actor.colChkInfo.damage, + this->actor.colChkInfo.damageEffect); + this->stopScroll = false; + Actor_ApplyDamage(&this->actor); + this->invincibilityTimer = 40; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, this->invincibilityTimer); + if (this->hasPlayer) { + EnRr_SetupReleasePlayer(this, globalCtx); + } else if (this->actor.colChkInfo.health != 0) { + EnRr_SetupDamage(this); + } else { + this->dropType = dropType; + EnRr_SetupDeath(this); + } + return; + case RR_DMG_FIRE: // Fire Arrow and Din's Fire + Actor_ApplyDamage(&this->actor); + if (this->actor.colChkInfo.health == 0) { + this->dropType = RR_DROP_RANDOM_RUPEE; + } + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + this->effectTimer = 20; + EnRr_SetupStunned(this); + return; + case RR_DMG_ICE: // Ice Arrow and unused ice magic + Actor_ApplyDamage(&this->actor); + if (this->actor.colChkInfo.health == 0) { + this->dropType = RR_DROP_RANDOM_RUPEE; + } + if (this->actor.colorFilterTimer == 0) { + this->effectTimer = 20; + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0x2000, 0x50); + } + EnRr_SetupStunned(this); + return; + case RR_DMG_LIGHT_MAGIC: // Unused light magic + Actor_ApplyDamage(&this->actor); + if (this->actor.colChkInfo.health == 0) { + this->dropType = RR_DROP_RUPEE_RED; + } + Actor_SetColorFilter(&this->actor, -0x8000, 0xFF, 0x2000, 0x50); + EnRr_SetupStunned(this); + return; + case RR_DMG_STUN: // Boomerang and Hookshot + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0x2000, 0x50); + EnRr_SetupStunned(this); + return; + } + } + if ((this->ocTimer == 0) && (this->actor.colorFilterTimer == 0) && (player->invincibilityTimer == 0) && + !(player->stateFlags2 & 0x80) && + ((this->collider1.base.ocFlags1 & OC1_HIT) || (this->collider2.base.ocFlags1 & OC1_HIT))) { + this->collider1.base.ocFlags1 &= ~OC1_HIT; + this->collider2.base.ocFlags1 &= ~OC1_HIT; + // "catch" + osSyncPrintf(VT_FGCOL(GREEN) "キャッチ(%d)!!" VT_RST "\n", this->frameCount); + if (globalCtx->grabPlayer(globalCtx, player)) { + player->actor.parent = &this->actor; + this->stopScroll = false; + EnRr_SetupGrabPlayer(this, player); + } + } + } +} + +void EnRr_InitBodySegments(EnRr* this, GlobalContext* globalCtx) { + s32 i; + + this->segMovePhase = 0; + this->segPhaseVel = 0.0f; + this->segPhaseVelTarget = 2500.0f; + this->segPulsePhaseDiff = 0.0f; + this->segWobblePhaseDiffX = 0.0f; + this->segWobbleXTarget = 3.0f; + this->segWobblePhaseDiffZ = 0.0f; + this->segWobbleZTarget = 1.0f; + this->pulseSize = 0.0f; + this->pulseSizeTarget = 0.15f; + this->wobbleSize = 0.0f; + this->wobbleSizeTarget = 2048.0f; + for (i = 0; i < 5; i++) { + this->bodySegs[i].scaleMod.y = 0.0f; + this->bodySegs[i].rotTarget.x = 0.0f; + this->bodySegs[i].rotTarget.y = 0.0f; + this->bodySegs[i].rotTarget.z = 0.0f; + this->bodySegs[i].scale.x = this->bodySegs[i].scale.y = this->bodySegs[i].scale.z = + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.y = this->bodySegs[i].scaleTarget.z = 1.0f; + } + for (i = 0; i < 5; i++) { + this->bodySegs[i].scaleMod.x = this->bodySegs[i].scaleMod.z = + Math_CosS(i * (u32)(s16)this->segPulsePhaseDiff * 0x1000) * this->pulseSize; + } + for (i = 1; i < 5; i++) { + this->bodySegs[i].rotTarget.x = Math_CosS(i * (u32)(s16)this->segWobblePhaseDiffX * 0x1000) * this->wobbleSize; + this->bodySegs[i].rotTarget.z = Math_SinS(i * (u32)(s16)this->segWobblePhaseDiffZ * 0x1000) * this->wobbleSize; + } +} + +void EnRr_UpdateBodySegments(EnRr* this, GlobalContext* globalCtx) { + s32 i; + s16 phase = this->segMovePhase; + + if (!this->isDead) { + for (i = 0; i < 5; i++) { + this->bodySegs[i].scaleMod.x = this->bodySegs[i].scaleMod.z = + Math_CosS(phase + i * (s16)this->segPulsePhaseDiff * 0x1000) * this->pulseSize; + } + phase = this->segMovePhase; + if (!this->isDead && (this->reachState == 0)) { + for (i = 1; i < 5; i++) { + this->bodySegs[i].rotTarget.x = + Math_CosS(phase + i * (s16)this->segWobblePhaseDiffX * 0x1000) * this->wobbleSize; + this->bodySegs[i].rotTarget.z = + Math_SinS(phase + i * (s16)this->segWobblePhaseDiffZ * 0x1000) * this->wobbleSize; + } + } + } + if (!this->stopScroll) { + this->segMovePhase += (s16)this->segPhaseVel; + } +} + +void EnRr_Approach(EnRr* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, 0x1F4, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if ((this->actionTimer == 0) && (this->actor.xzDistToPlayer < 160.0f)) { + EnRr_SetupReach(this); + } else if ((this->actor.xzDistToPlayer < 400.0f) && (this->actor.speedXZ == 0.0f)) { + EnRr_SetSpeed(this, 2.0f); + } +} + +void EnRr_Reach(EnRr* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, 0x1F4, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + switch (this->reachState) { + case REACH_EXTEND: + if (this->actionTimer == 0) { + this->reachState = REACH_STOP; + } + break; + case REACH_STOP: + if (this->actionTimer == 0) { + this->actionTimer = 5; + this->bodySegs[RR_MOUTH].scaleTarget.x = this->bodySegs[RR_MOUTH].scaleTarget.z = 1.5f; + this->reachState = REACH_OPEN; + } + break; + case REACH_OPEN: + if (this->actionTimer == 0) { + this->actionTimer = 2; + this->bodySegs[RR_MOUTH].heightTarget = 2000.0f; + this->reachState = REACH_GAPE; + } + break; + case REACH_GAPE: + if (this->actionTimer == 0) { + this->actionTimer = 20; + this->bodySegs[RR_MOUTH].scaleTarget.x = this->bodySegs[RR_MOUTH].scaleTarget.z = 0.8f; + this->reachState = REACH_CLOSE; + } + break; + case REACH_CLOSE: + if (this->actionTimer == 0) { + EnRr_SetupNeutral(this); + } + break; + } +} + +void EnRr_GrabPlayer(EnRr* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + func_800AA000(this->actor.xyzDistToPlayerSq, 120, 2, 120); + if ((this->frameCount % 8) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_LIKE_EAT); + } + this->ocTimer = 8; + if ((this->grabTimer == 0) || !(player->stateFlags2 & 0x80)) { + EnRr_SetupReleasePlayer(this, globalCtx); + } else { + Math_ApproachF(&player->actor.world.pos.x, this->mouthPos.x, 1.0f, 30.0f); + Math_ApproachF(&player->actor.world.pos.y, this->mouthPos.y + this->swallowOffset, 1.0f, 30.0f); + Math_ApproachF(&player->actor.world.pos.z, this->mouthPos.z, 1.0f, 30.0f); + Math_ApproachF(&this->swallowOffset, -55.0f, 1.0f, 5.0f); + } +} + +void EnRr_Damage(EnRr* this, GlobalContext* globalCtx) { + s32 i; + + if (this->actor.colorFilterTimer == 0) { + EnRr_SetupApproach(this); + } else if ((this->actor.colorFilterTimer & 8) != 0) { + for (i = 1; i < 5; i++) { + this->bodySegs[i].rotTarget.z = 5000.0f; + } + } else { + for (i = 1; i < 5; i++) { + this->bodySegs[i].rotTarget.z = -5000.0f; + } + } +} + +void EnRr_Death(EnRr* this, GlobalContext* globalCtx) { + s32 pad; + s32 i; + + if (this->frameCount < 40) { + for (i = 0; i < 5; i++) { + Math_ApproachF(&this->bodySegs[i].heightTarget, i + 59 - (this->frameCount * 25.0f), 1.0f, 50.0f); + this->bodySegs[i].scaleTarget.x = this->bodySegs[i].scaleTarget.z = + (SQ(4 - i) * (f32)this->frameCount * 0.003f) + 1.0f; + } + } else if (this->frameCount >= 95) { + Vec3f dropPos; + + dropPos.x = this->actor.world.pos.x; + dropPos.y = this->actor.world.pos.y; + dropPos.z = this->actor.world.pos.z; + switch (this->eatenShield) { + case 1: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_SHIELD_DEKU); + break; + case 2: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_SHIELD_HYLIAN); + break; + } + switch (this->eatenTunic) { + case 2: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_TUNIC_GORON); + break; + case 3: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_TUNIC_ZORA); + break; + } + // "dropped" + osSyncPrintf(VT_FGCOL(GREEN) "「%s」が出た!!" VT_RST "\n", sDropNames[this->dropType]); + switch (this->dropType) { + case RR_DROP_MAGIC: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_MAGIC_SMALL); + break; + case RR_DROP_ARROW: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_ARROWS_SINGLE); + break; + case RR_DROP_FLEXIBLE: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_FLEXIBLE); + break; + case RR_DROP_RUPEE_PURPLE: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_RUPEE_PURPLE); + break; + case RR_DROP_RUPEE_RED: + Item_DropCollectible(globalCtx, &dropPos, ITEM00_RUPEE_RED); + break; + case RR_DROP_RANDOM_RUPEE: + default: + Item_DropCollectibleRandom(globalCtx, &this->actor, &dropPos, 12 << 4); + break; + } + Actor_Kill(&this->actor); + } else if (this->frameCount == 88) { + Vec3f pos; + Vec3f vel; + Vec3f accel; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 20.0f; + pos.z = this->actor.world.pos.z; + vel.x = 0.0f; + vel.y = 0.0f; + vel.z = 0.0f; + accel.x = 0.0f; + accel.y = 0.0f; + accel.z = 0.0f; + + EffectSsDeadDb_Spawn(globalCtx, &pos, &vel, &accel, 100, 0, 255, 255, 255, 255, 255, 0, 0, 1, 9, true); + } else { + Math_ApproachF(&this->actor.scale.x, 0.0f, 1.0f, this->shrinkRate); + Math_ApproachF(&this->shrinkRate, 0.001f, 1.0f, 0.00001f); + this->actor.scale.z = this->actor.scale.x; + } +} + +void EnRr_Retreat(EnRr* this, GlobalContext* globalCtx) { + if (this->actionTimer == 0) { + this->retreat = false; + this->actionFunc = EnRr_Approach; + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + 0x8000, 0xA, 0x3E8, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->actor.speedXZ == 0.0f) { + EnRr_SetSpeed(this, 2.0f); + } + } +} + +void EnRr_Stunned(EnRr* this, GlobalContext* globalCtx) { + if (this->actor.colorFilterTimer == 0) { + this->stopScroll = false; + if (this->hasPlayer) { + EnRr_SetupReleasePlayer(this, globalCtx); + } else if (this->actor.colChkInfo.health != 0) { + this->actionFunc = EnRr_Approach; + } else { + EnRr_SetupDeath(this); + } + } +} + +void EnRr_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnRr* this = (EnRr*)thisx; + s32 i; + + this->frameCount++; + if (!this->stopScroll) { + this->scrollTimer++; + } + if (this->actionTimer != 0) { + this->actionTimer--; + } + if (this->grabTimer != 0) { + this->grabTimer--; + } + if (this->ocTimer != 0) { + this->ocTimer--; + } + if (this->invincibilityTimer != 0) { + this->invincibilityTimer--; + } + if (this->effectTimer != 0) { + this->effectTimer--; + } + + Actor_SetFocus(&this->actor, 30.0f); + EnRr_UpdateBodySegments(this, globalCtx); + if (!this->isDead && ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000))) { + EnRr_CollisionCheck(this, globalCtx); + } + + this->actionFunc(this, globalCtx); + if (this->hasPlayer == 0x3F80) { // checks if 1.0f has been stored to hasPlayer's address + ASSERT(0, "0", "../z_en_rr.c", 1355); + } + + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.1f); + Actor_MoveForward(&this->actor); + Collider_UpdateCylinder(&this->actor, &this->collider1); + this->collider2.dim.pos.x = this->mouthPos.x; + this->collider2.dim.pos.y = this->mouthPos.y; + this->collider2.dim.pos.z = this->mouthPos.z; + if (!this->isDead && (this->invincibilityTimer == 0)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + if (this->ocTimer == 0) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider1.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + } else { + this->collider2.base.ocFlags1 &= ~OC1_HIT; + this->collider2.base.acFlags &= ~AC_HIT; + this->collider1.base.ocFlags1 &= ~OC1_HIT; + this->collider1.base.acFlags &= ~AC_HIT; + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 30.0f, 20.0f, 7); + if (!this->stopScroll) { + Math_ApproachF(&this->segPhaseVel, this->segPhaseVelTarget, 1.0f, 50.0f); + Math_ApproachF(&this->segPulsePhaseDiff, 4.0f, 1.0f, 5.0f); + Math_ApproachF(&this->segWobblePhaseDiffX, this->segWobbleXTarget, 1.0f, 0.04f); + Math_ApproachF(&this->segWobblePhaseDiffZ, this->segWobbleZTarget, 1.0f, 0.01f); + Math_ApproachF(&this->pulseSize, this->pulseSizeTarget, 1.0f, 0.0015f); + Math_ApproachF(&this->wobbleSize, this->wobbleSizeTarget, 1.0f, 20.0f); + for (i = 0; i < 5; i++) { + Math_SmoothStepToS(&this->bodySegs[i].rot.x, this->bodySegs[i].rotTarget.x, 5, this->segMoveRate * 1000.0f, + 0); + Math_SmoothStepToS(&this->bodySegs[i].rot.z, this->bodySegs[i].rotTarget.z, 5, this->segMoveRate * 1000.0f, + 0); + Math_ApproachF(&this->bodySegs[i].scale.x, this->bodySegs[i].scaleTarget.x, 1.0f, this->segMoveRate * 0.2f); + this->bodySegs[i].scale.z = this->bodySegs[i].scale.x; + Math_ApproachF(&this->bodySegs[i].height, this->bodySegs[i].heightTarget, 1.0f, this->segMoveRate * 300.0f); + } + Math_ApproachF(&this->segMoveRate, 1.0f, 1.0f, 0.2f); + } +} + +static Vec3f sEffectOffsets[] = { + { 25.0f, 0.0f, 0.0f }, + { -25.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 25.0f }, + { 0.0f, 0.0f, -25.0f }, +}; + +void EnRr_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + Vec3f zeroVec; + EnRr* this = (EnRr*)thisx; + s32 i; + Mtx* segMtx = Graph_Alloc(globalCtx->state.gfxCtx, 4 * sizeof(Mtx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_rr.c", 1478); + if (1) {} + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x0C, segMtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (this->scrollTimer * 0) & 0x7F, + (this->scrollTimer * 0) & 0x3F, 32, 16, 1, (this->scrollTimer * 0) & 0x3F, + (this->scrollTimer * -6) & 0x7F, 32, 16)); + Matrix_Push(); + + Matrix_Scale((1.0f + this->bodySegs[RR_BASE].scaleMod.x) * this->bodySegs[RR_BASE].scale.x, + (1.0f + this->bodySegs[RR_BASE].scaleMod.y) * this->bodySegs[RR_BASE].scale.y, + (1.0f + this->bodySegs[RR_BASE].scaleMod.z) * this->bodySegs[RR_BASE].scale.z, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_rr.c", 1501), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + Matrix_Pop(); + zeroVec.x = 0.0f; + zeroVec.y = 0.0f; + zeroVec.z = 0.0f; + for (i = 1; i < 5; i++) { + Matrix_Translate(0.0f, this->bodySegs[i].height + 1000.0f, 0.0f, MTXMODE_APPLY); + + Matrix_RotateZYX(this->bodySegs[i].rot.x, this->bodySegs[i].rot.y, this->bodySegs[i].rot.z, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Scale((1.0f + this->bodySegs[i].scaleMod.x) * this->bodySegs[i].scale.x, + (1.0f + this->bodySegs[i].scaleMod.y) * this->bodySegs[i].scale.y, + (1.0f + this->bodySegs[i].scaleMod.z) * this->bodySegs[i].scale.z, MTXMODE_APPLY); + Matrix_ToMtx(segMtx, "../z_en_rr.c", 1527); + Matrix_Pop(); + segMtx++; + Matrix_MultVec3f(&zeroVec, &this->effectPos[i]); + } + this->effectPos[0] = this->actor.world.pos; + Matrix_MultVec3f(&zeroVec, &this->mouthPos); + gSPDisplayList(POLY_XLU_DISP++, gLikeLikeDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_rr.c", 1551); + if (this->effectTimer != 0) { + Vec3f effectPos; + s16 effectTimer = this->effectTimer - 1; + + this->actor.colorFilterTimer++; + if ((effectTimer & 1) == 0) { + s32 segIndex = 4 - (effectTimer >> 2); + s32 offIndex = (effectTimer >> 1) & 3; + + effectPos.x = this->effectPos[segIndex].x + sEffectOffsets[offIndex].x + Rand_CenteredFloat(10.0f); + effectPos.y = this->effectPos[segIndex].y + sEffectOffsets[offIndex].y + Rand_CenteredFloat(10.0f); + effectPos.z = this->effectPos[segIndex].z + sEffectOffsets[offIndex].z + Rand_CenteredFloat(10.0f); + if (this->actor.colorFilterParams & 0x4000) { + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &effectPos, 100, 0, 0, -1); + } else { + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &effectPos, 150, 150, 150, 250, 235, 245, 255, + 3.0f); + } + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.h b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.h new file mode 100644 index 000000000..2991723a4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Rr/z_en_rr.h @@ -0,0 +1,62 @@ +#ifndef Z_EN_RR_H +#define Z_EN_RR_H + +#include "ultra64.h" +#include "global.h" + +struct EnRr; + +typedef void (*EnRrActionFunc)(struct EnRr*, GlobalContext*); + +typedef struct { + /* 0x00 */ f32 height; + /* 0x04 */ f32 heightTarget; + /* 0x08 */ Vec3f scale; + /* 0x14 */ Vec3f scaleTarget; + /* 0x20 */ Vec3f scaleMod; + /* 0x2C */ Vec3f rotTarget; + /* 0x38 */ Vec3s rot; +} EnRrBodySegment; // size = 0x40 + +typedef struct EnRr { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnRrActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider1; + /* 0x019C */ ColliderCylinder collider2; + /* 0x01E8 */ s16 frameCount; + /* 0x01EA */ s16 actionTimer; + /* 0x01EC */ s16 scrollTimer; + /* 0x01EE */ s16 grabTimer; + /* 0x01F0 */ s16 invincibilityTimer; + /* 0x01F2 */ s16 effectTimer; + /* 0x01F4 */ s16 ocTimer; + /* 0x01F6 */ s16 segMovePhase; // phase angle for wobble and pulsing motion + /* 0x01F8 */ f32 segPhaseVel; // rate at which motion phase changes + /* 0x01FC */ f32 segPhaseVelTarget; + /* 0x0200 */ f32 segPulsePhaseDiff; // Phase diff between segment pulses. Affects how wave-y the pulse is. + /* 0x0204 */ f32 segWobblePhaseDiffX; // Phase diff between segment X rot. Affects how circular the wobble is. + /* 0x0208 */ f32 segWobbleXTarget; + /* 0x020C */ f32 segWobblePhaseDiffZ; // Phase diff between segment Z rot. Affects how circular the wobble is. + /* 0x0210 */ f32 segWobbleZTarget; + /* 0x0214 */ f32 pulseSize; // Amplitude of the scale pulsations + /* 0x0218 */ f32 pulseSizeTarget; + /* 0x021C */ f32 wobbleSize; // Amplitude of the wobbling motion + /* 0x0220 */ f32 wobbleSizeTarget; + /* 0x0224 */ EnRrBodySegment bodySegs[5]; + /* 0x0364 */ f32 segMoveRate; + /* 0x0368 */ f32 shrinkRate; + /* 0x036C */ f32 swallowOffset; + /* 0x0370 */ u8 reachState; + /* 0x0371 */ u8 isDead; + /* 0x0372 */ u8 eatenShield; + /* 0x0373 */ u8 eatenTunic; + /* 0x0374 */ u8 dropType; + /* 0x0375 */ u8 retreat; + /* 0x0376 */ u8 stopScroll; + /* 0x0378 */ s16 hasPlayer; + /* 0x037C */ Vec3f mouthPos; + /* 0x0388 */ Vec3f effectPos[5]; + /* 0x03C4 */ char unk_3C4[0x2000]; //! @bug This is a huge amount of wasted memory. +} EnRr; // size = 0x23C4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c new file mode 100644 index 000000000..733d98d9b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.c @@ -0,0 +1,2369 @@ +/* + * File: z_en_ru1.c + * Overlay: En_Ru1 + * Description: Ruto (child) + */ + +#include "z_en_ru1.h" +#include "objects/object_ru1/object_ru1.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4 | ACTOR_FLAG_26) + +void EnRu1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnRu1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnRu1_Update(Actor* thisx, GlobalContext* globalCtx); +void EnRu1_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AEC0B4(EnRu1* this, GlobalContext* globalCtx); +void func_80AEC100(EnRu1* this, GlobalContext* globalCtx); +void func_80AEC130(EnRu1* this, GlobalContext* globalCtx); +void func_80AEC17C(EnRu1* this, GlobalContext* globalCtx); +void func_80AEC1D4(EnRu1* this, GlobalContext* globalCtx); +void func_80AEC244(EnRu1* this, GlobalContext* globalCtx); +void func_80AEC2C0(EnRu1* this, GlobalContext* globalCtx); +void func_80AECA94(EnRu1* this, GlobalContext* globalCtx); +void func_80AECAB4(EnRu1* this, GlobalContext* globalCtx); +void func_80AECAD4(EnRu1* this, GlobalContext* globalCtx); +void func_80AECB18(EnRu1* this, GlobalContext* globalCtx); +void func_80AECB60(EnRu1* this, GlobalContext* globalCtx); +void func_80AECBB8(EnRu1* this, GlobalContext* globalCtx); +void func_80AECC1C(EnRu1* this, GlobalContext* globalCtx); +void func_80AECC84(EnRu1* this, GlobalContext* globalCtx); +void func_80AED304(EnRu1* this, GlobalContext* globalCtx); +void func_80AED324(EnRu1* this, GlobalContext* globalCtx); +void func_80AED344(EnRu1* this, GlobalContext* globalCtx); +void func_80AED374(EnRu1* this, GlobalContext* globalCtx); +void func_80AED3A4(EnRu1* this, GlobalContext* globalCtx); +void func_80AED3E0(EnRu1* this, GlobalContext* globalCtx); +void func_80AED414(EnRu1* this, GlobalContext* globalCtx); +void func_80AEF29C(EnRu1* this, GlobalContext* globalCtx); +void func_80AEF2AC(EnRu1* this, GlobalContext* globalCtx); +void func_80AEF2D0(EnRu1* this, GlobalContext* globalCtx); +void func_80AEF354(EnRu1* this, GlobalContext* globalCtx); +void func_80AEF3A8(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEBD4(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEC5C(EnRu1* this, GlobalContext* globalCtx); +void func_80AEECF0(EnRu1* this, GlobalContext* globalCtx); +void func_80AEED58(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEDCC(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEE34(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEE9C(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEF08(EnRu1* this, GlobalContext* globalCtx); +void func_80AEEF5C(EnRu1* this, GlobalContext* globalCtx); +void func_80AEF9D8(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFA2C(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFAAC(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFB04(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFB68(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFCE8(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFBC8(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFC24(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFECC(EnRu1* this, GlobalContext* globalCtx); +void func_80AEFF40(EnRu1* this, GlobalContext* globalCtx); + +void func_80AF0278(EnRu1* this, GlobalContext* globalCtx, s32 limbIndex, Vec3s* rot); + +void EnRu1_DrawNothing(EnRu1* this, GlobalContext* globalCtx); +void EnRu1_DrawOpa(EnRu1* this, GlobalContext* globalCtx); +void EnRu1_DrawXlu(EnRu1* this, GlobalContext* globalCtx); + +static ColliderCylinderInitType1 sCylinderInit1 = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0x00000000, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, 0x00, 0x00, 0x01 }, + { 25, 80, 0, { 0 } }, +}; + +static ColliderCylinderInitType1 sCylinderInit2 = { + { + COLTYPE_HIT0, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0x00000101, 0x00, 0x00 }, { 0x00000000, 0x00, 0x00 }, 0x01, 0x00, 0x01 }, + { 20, 30, 0, { 0 } }, +}; + +static void* sEyeTextures[] = { + gRutoChildEyeOpenTex, gRutoChildEyeHalfTex, gRutoChildEyeClosedTex, + gRutoChildEyeRollLeftTex, gRutoChildEyeHalf2Tex, gRutoChildEyeHalfWithBlushTex, +}; + +static void* sMouthTextures[] = { + gRutoChildMouthClosedTex, + gRutoChildMouthFrownTex, + gRutoChildMouthOpenTex, +}; + +static s32 sUnused = 0; + +#include "z_en_ru1_cutscene_data.c" EARLY + +static u32 D_80AF1938 = 0; + +static EnRu1ActionFunc sActionFuncs[] = { + func_80AEC0B4, func_80AEC100, func_80AEC130, func_80AEC17C, func_80AEC1D4, func_80AEC244, func_80AEC2C0, + func_80AECA94, func_80AECAB4, func_80AECAD4, func_80AECB18, func_80AECB60, func_80AECBB8, func_80AECC1C, + func_80AECC84, func_80AED304, func_80AED324, func_80AED344, func_80AED374, func_80AED3A4, func_80AED3E0, + func_80AED414, func_80AEF29C, func_80AEF2AC, func_80AEF2D0, func_80AEF354, func_80AEF3A8, func_80AEEBD4, + func_80AEEC5C, func_80AEECF0, func_80AEED58, func_80AEEDCC, func_80AEEE34, func_80AEEE9C, func_80AEEF08, + func_80AEEF5C, func_80AEF9D8, func_80AEFA2C, func_80AEFAAC, func_80AEFB04, func_80AEFB68, func_80AEFCE8, + func_80AEFBC8, func_80AEFC24, func_80AEFECC, func_80AEFF40, +}; + +static EnRu1PreLimbDrawFunc sPreLimbDrawFuncs[] = { + func_80AF0278, +}; + +static Vec3f sMultVec = { 0.0f, 10.0f, 0.0f }; + +static EnRu1DrawFunc sDrawFuncs[] = { + EnRu1_DrawNothing, + EnRu1_DrawOpa, + EnRu1_DrawXlu, +}; + +const ActorInit En_Ru1_InitVars = { + ACTOR_EN_RU1, + ACTORCAT_NPC, + FLAGS, + OBJECT_RU1, + sizeof(EnRu1), + (ActorFunc)EnRu1_Init, + (ActorFunc)EnRu1_Destroy, + (ActorFunc)EnRu1_Update, + (ActorFunc)EnRu1_Draw, + NULL, +}; + +void func_80AEAC10(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[5]; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void func_80AEAC54(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[5]; + + Collider_UpdateCylinder(&this->actor, &this->collider2); + if (this->unk_34C != 0) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); + } else if (this->actor.xzDistToPlayer > 32.0f) { + this->unk_34C = 1; + } +} + +void func_80AEACDC(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[5]; + + Collider_UpdateCylinder(&this->actor, &this->collider2); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider2.base); +} + +void func_80AEAD20(Actor* thisx, GlobalContext* globalCtx) { + EnRu1* this = (EnRu1*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit1); + + Collider_InitCylinder(globalCtx, &this->collider2); + Collider_SetCylinderType1(globalCtx, &this->collider2, &this->actor, &sCylinderInit2); +} + +void EnRu1_DestroyColliders(EnRu1* this, GlobalContext* globalCtx) { + Collider_DestroyCylinder(globalCtx, &this->collider); + Collider_DestroyCylinder(globalCtx, &this->collider2); +} + +void func_80AEADD8(EnRu1* this) { + this->unk_34C = 0; +} + +u8 func_80AEADE0(EnRu1* this) { + u8 params = this->actor.params >> 8; + + return params; +} + +u8 func_80AEADF0(EnRu1* this) { + s16 params = this->actor.params; + + return params; +} + +void EnRu1_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnRu1* this = (EnRu1*)thisx; + + D_80AF1938 = 0; + EnRu1_DestroyColliders(this, globalCtx); +} + +void EnRu1_UpdateEyes(EnRu1* this) { + s32 pad[3]; + s16* blinkTimer = &this->blinkTimer; + s16* eyeIndex = &this->eyeIndex; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + + *eyeIndex = *blinkTimer; + if (*eyeIndex >= 3) { + *eyeIndex = 0; + } +} + +void EnRu1_SetEyeIndex(EnRu1* this, s16 eyeIndex) { + this->eyeIndex = eyeIndex; +} + +void EnRu1_SetMouthIndex(EnRu1* this, s16 mouthIndex) { + this->mouthIndex = mouthIndex; +} + +void func_80AEAECC(EnRu1* this, GlobalContext* globalCtx) { + f32* velocityY = &this->actor.velocity.y; + f32 velocityYHeld = *velocityY; + + *velocityY = -4.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 19.0f, 25.0f, 30.0f, 7); + *velocityY = velocityYHeld; +} + +s32 EnRu1_IsCsStateIdle(GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + return true; + } + return false; +} + +CsCmdActorAction* func_80AEAF58(GlobalContext* globalCtx, s32 npcActionIdx) { + s32 pad[2]; + CsCmdActorAction* ret = NULL; + + if (!EnRu1_IsCsStateIdle(globalCtx)) { + ret = globalCtx->csCtx.npcActions[npcActionIdx]; + } + return ret; +} + +s32 func_80AEAFA0(GlobalContext* globalCtx, u16 action, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = func_80AEAF58(globalCtx, npcActionIdx); + + if ((csCmdNPCAction != NULL) && (csCmdNPCAction->action == action)) { + return true; + } + return false; +} + +s32 func_80AEAFE0(GlobalContext* globalCtx, u16 action, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = func_80AEAF58(globalCtx, npcActionIdx); + + if ((csCmdNPCAction != NULL) && (csCmdNPCAction->action != action)) { + return true; + } + return false; +} + +s32 func_80AEB020(EnRu1* this, GlobalContext* globalCtx) { + Actor* actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + EnRu1* someEnRu1; + + while (actorIt != NULL) { + if (actorIt->id == ACTOR_EN_RU1) { + someEnRu1 = (EnRu1*)actorIt; + if (someEnRu1 != this) { + if ((someEnRu1->action == 31) || (someEnRu1->action == 32) || (someEnRu1->action == 24)) { + return true; + } + } + } + actorIt = actorIt->next; + } + return false; +} + +BgBdanObjects* EnRu1_FindSwitch(GlobalContext* globalCtx) { + Actor* actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_BG].head; + + while (actorIt != NULL) { + if (actorIt->id == ACTOR_BG_BDAN_OBJECTS && actorIt->params == 0) { + return (BgBdanObjects*)actorIt; + } + actorIt = actorIt->next; + } + // "There is no stand" + osSyncPrintf(VT_FGCOL(RED) "お立ち台が無い!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return NULL; +} + +void func_80AEB0EC(EnRu1* this, s32 cameraSetting) { + if (this->unk_28C != NULL) { + this->unk_28C->cameraSetting = cameraSetting; + } +} + +s32 func_80AEB104(EnRu1* this) { + if (this->unk_28C != NULL) { + return this->unk_28C->cameraSetting; + } else { + return 0; + } +} + +Actor* func_80AEB124(GlobalContext* globalCtx) { + Actor* actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_BOSS].head; + + while (actorIt != NULL) { + if ((actorIt->id == ACTOR_DEMO_EFFECT) && ((actorIt->params & 0xFF) == 0x15)) { + return actorIt; + } + actorIt = actorIt->next; + } + return NULL; +} + +s32 func_80AEB174(GlobalContext* globalCtx) { + return (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx); +} + +s32 func_80AEB1B4(GlobalContext* globalCtx) { + return Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING; +} + +void func_80AEB1D8(EnRu1* this) { + this->action = 36; + this->drawConfig = 0; + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; + func_80AEB0EC(this, 0); +} + +void func_80AEB220(EnRu1* this, GlobalContext* globalCtx) { + if ((EnRu1_IsCsStateIdle(globalCtx)) && (this->actor.params == 0xA)) { + func_80AEB1D8(this); + } +} + +void func_80AEB264(EnRu1* this, AnimationHeader* animation, u8 arg2, f32 transitionRate, s32 arg4) { + s32 pad[2]; + AnimationHeader* animHeader = SEGMENTED_TO_VIRTUAL(animation); + f32 frameCount = Animation_GetLastFrame(animHeader); + f32 playbackSpeed; + f32 unk0; + f32 fc; + + if (arg4 == 0) { + unk0 = 0.0f; + fc = frameCount; + playbackSpeed = 1.0f; + } else { + unk0 = frameCount; + fc = 0.0f; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animHeader, playbackSpeed, unk0, fc, arg2, transitionRate); +} + +s32 EnRu1_UpdateSkelAnime(EnRu1* this) { + // why? + if (this->action != 32) { + return SkelAnime_Update(&this->skelAnime); + } else { + return SkelAnime_Update(&this->skelAnime); + } +} + +void func_80AEB364(EnRu1* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 1; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_80AEB3A4(EnRu1* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 1; + func_80AEB364(this, globalCtx); +} + +void func_80AEB3CC(EnRu1* this) { + this->skelAnime.moveFlags &= ~0x1; +} + +void func_80AEB3DC(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB264(this, &gRutoChildWaitHandsBehindBackAnim, 0, 0, 0); + this->action = 0; + this->drawConfig = 1; + EnRu1_SetEyeIndex(this, 4); + EnRu1_SetMouthIndex(this, 0); +} + +CsCmdActorAction* func_80AEB438(GlobalContext* globalCtx) { + return func_80AEAF58(globalCtx, 3); +} + +s32 func_80AEB458(GlobalContext* globalCtx, u16 action) { + return func_80AEAFA0(globalCtx, action, 3); +} + +s32 func_80AEB480(GlobalContext* globalCtx, u16 action) { + return func_80AEAFE0(globalCtx, action, 3); +} + +void EnRu1_SpawnRipple(EnRu1* this, GlobalContext* globalCtx, s16 radiusMax, s16 life) { + Vec3f pos; + Actor* thisx = &this->actor; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + this->actor.yDistToWater; + pos.z = this->actor.world.pos.z; + EffectSsGRipple_Spawn(globalCtx, &pos, 100, radiusMax, life); +} + +void func_80AEB50C(EnRu1* this, GlobalContext* globalCtx) { + this->unk_270 += 1.0f; + if (this->unk_270 >= kREG(3) + 10.0f) { + EnRu1_SpawnRipple(this, globalCtx, kREG(1) + 500, 0); + this->unk_270 = 0.0f; + } +} + +void func_80AEB59C(EnRu1* this, GlobalContext* globalCtx) { + EnRu1_SpawnRipple(this, globalCtx, kREG(2) + 500, 0); + EnRu1_SpawnRipple(this, globalCtx, kREG(2) + 500, kREG(3) + 10.0f); + EnRu1_SpawnRipple(this, globalCtx, kREG(2) + 500, (kREG(3) + 10.0f) * 2.0f); +} + +void EnRu1_SpawnSplash(EnRu1* this, GlobalContext* globalCtx) { + Vec3f pos; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + this->actor.yDistToWater; + pos.z = this->actor.world.pos.z; + + EffectSsGSplash_Spawn(globalCtx, &pos, 0, 0, 1, 0); +} + +void func_80AEB6E0(EnRu1* this, GlobalContext* globalCtx) { + SkelAnime* skelAnime = &this->skelAnime; + + if (skelAnime->baseTransl.y < skelAnime->jointTable[0].y) { + skelAnime->moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, skelAnime, 1.0f); + } +} + +void func_80AEB738(EnRu1* this, GlobalContext* globalCtx) { + SkelAnime* skelAnime = &this->skelAnime; + + skelAnime->baseTransl = skelAnime->jointTable[0]; + skelAnime->prevTransl = skelAnime->jointTable[0]; + if (skelAnime->baseTransl.y < skelAnime->jointTable[0].y) { + skelAnime->moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, skelAnime, 1.0f); + } +} + +void func_80AEB7D0(EnRu1* this) { + this->skelAnime.moveFlags &= ~0x3; +} + +f32 func_80AEB7E0(CsCmdActorAction* csCmdNPCAction, GlobalContext* globalCtx) { + s32 csCtxFrames = globalCtx->csCtx.frames; + + if ((csCtxFrames < csCmdNPCAction->endFrame) && (csCmdNPCAction->endFrame - csCmdNPCAction->startFrame > 0)) { + return (Math_CosS(((csCtxFrames - csCmdNPCAction->startFrame) / + (f32)(csCmdNPCAction->endFrame - csCmdNPCAction->startFrame)) * + 32768.0f) * + -0.5f) + + 0.5f; + } + return 1.0f; +} + +f32 func_80AEB87C(f32 arg0, s32 arg1, s32 arg2) { + return (((f32)arg2 - arg1) * arg0) + arg1; +} + +void func_80AEB89C(EnRu1* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = func_80AEB438(globalCtx); + s16 npcActionRotY; + + if (npcAction != NULL) { + npcActionRotY = npcAction->rot.y; + this->actor.shape.rot.y = npcActionRotY; + this->actor.world.rot.y = npcActionRotY; + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + } +} + +void func_80AEB914(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB89C(this, globalCtx); +} + +void func_80AEB934(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB89C(this, globalCtx); +} + +void func_80AEB954(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB6E0(this, globalCtx); +} + +void func_80AEB974(EnRu1* this, GlobalContext* globalCtx) { + Vec3f* thisPos; + f32 sp30; + CsCmdActorAction* csCmdNPCAction = func_80AEB438(globalCtx); + s32 pad; + + if (csCmdNPCAction != NULL) { + sp30 = func_80AEB7E0(csCmdNPCAction, globalCtx); + thisPos = &this->actor.world.pos; + thisPos->x = func_80AEB87C(sp30, csCmdNPCAction->startPos.x, csCmdNPCAction->endPos.x); + thisPos->y = func_80AEB87C(sp30, csCmdNPCAction->startPos.y, csCmdNPCAction->endPos.y); + thisPos->z = func_80AEB87C(sp30, csCmdNPCAction->startPos.z, csCmdNPCAction->endPos.z); + } +} + +void func_80AEBA0C(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB6E0(this, globalCtx); +} + +void func_80AEBA2C(EnRu1* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f* unk_364 = &this->unk_364; + Vec3f* thisPos; + f32 temp_ret_2; + CsCmdActorAction* csCmdNPCAction = func_80AEB438(globalCtx); + s32 pad2; + + if (csCmdNPCAction != NULL) { + temp_ret_2 = func_80AEB7E0(csCmdNPCAction, globalCtx); + thisPos = &this->actor.world.pos; + thisPos->x = func_80AEB87C(temp_ret_2, unk_364->x, csCmdNPCAction->endPos.x); + thisPos->y = func_80AEB87C(temp_ret_2, unk_364->y, csCmdNPCAction->endPos.y); + thisPos->z = func_80AEB87C(temp_ret_2, unk_364->z, csCmdNPCAction->endPos.z); + } +} + +void func_80AEBAFC(EnRu1* this) { + if (this->unk_298 == 0) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_DIVE_INTO_WATER); + this->unk_298 = 1; + } +} + +void func_80AEBB3C(EnRu1* this) { + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_FACE_UP); + } +} + +void func_80AEBB78(EnRu1* this) { + SkelAnime* skelAnime = &this->skelAnime; + + if (Animation_OnFrame(skelAnime, 4.0f) || Animation_OnFrame(skelAnime, 13.0f) || + Animation_OnFrame(skelAnime, 22.0f) || Animation_OnFrame(skelAnime, 31.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_SWIM); + } +} + +void func_80AEBBF4(EnRu1* this) { + if (Animation_OnFrame(&this->skelAnime, 8.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_SUBMERGE); + } +} + +void func_80AEBC30(GlobalContext* globalCtx) { + Player* player; + + if (globalCtx->csCtx.frames == 0xCD) { + player = GET_PLAYER(globalCtx); + Audio_PlaySoundGeneral(NA_SE_EV_DIVE_INTO_WATER, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void func_80AEBC84(EnRu1* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 0x82) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_LAUGH_0); + } +} + +void func_80AEBCB8(EnRu1* this, UNK_TYPE arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gRutoChildSwimOnBackAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildSwimOnBackAnim), ANIMMODE_LOOP, -8.0f); + } +} + +void func_80AEBD1C(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEB480(globalCtx, 2)) { + this->action = 1; + this->drawConfig = 0; + func_80AEB914(this, globalCtx); + func_80AEAECC(this, globalCtx); + EnRu1_SpawnSplash(this, globalCtx); + func_80AEB59C(this, globalCtx); + } +} + +void func_80AEBD94(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + f32 frameCount; + + if (func_80AEB480(globalCtx, 3)) { + frameCount = Animation_GetLastFrame(&gRutoChildAnim_009060); + func_80AEB934(this, globalCtx); + func_80AEB738(this, globalCtx); + Animation_Change(&this->skelAnime, &gRutoChildAnim_009060, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, 0.0f); + this->action = 2; + this->drawConfig = 1; + } +} + +void func_80AEBE3C(EnRu1* this, GlobalContext* globalCtx, s32 arg2) { + s32 pad[2]; + f32 frameCount; + + if (arg2 != 0) { + frameCount = Animation_GetLastFrame(&gRutoChildTreadWaterAnim); + func_80AEB7D0(this); + Animation_Change(&this->skelAnime, &gRutoChildTreadWaterAnim, 1.0f, 0, frameCount, ANIMMODE_LOOP, -8.0f); + this->action = 3; + } else { + func_80AEB954(this, globalCtx); + } +} + +void func_80AEBEC8(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + f32 frameCount; + + if (func_80AEB458(globalCtx, 6)) { + frameCount = Animation_GetLastFrame(&gRutoChildTransitionToSwimOnBackAnim); + func_80AEB738(this, globalCtx); + Animation_Change(&this->skelAnime, &gRutoChildTransitionToSwimOnBackAnim, 1.0f, 0, frameCount, ANIMMODE_ONCE, + -8.0f); + this->action = 4; + } +} + +void func_80AEBF60(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEB480(globalCtx, 6)) { + func_80AEB7D0(this); + this->action = 5; + this->unk_364 = this->actor.world.pos; + } else { + func_80AEBA0C(this, globalCtx); + } +} + +void func_80AEBFD8(EnRu1* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction = func_80AEB438(globalCtx); + f32 frameCount; + u16 csCtxFrames; + u16 endFrame; + + if (csCmdNPCAction != NULL) { + csCtxFrames = globalCtx->csCtx.frames; + endFrame = csCmdNPCAction->endFrame; + if (csCtxFrames >= endFrame - 2) { + frameCount = Animation_GetLastFrame(&gRutoChildTransitionFromSwimOnBackAnim); + Animation_Change(&this->skelAnime, &gRutoChildTransitionFromSwimOnBackAnim, 1.0, 0, frameCount, + ANIMMODE_ONCE, -8.0f); + this->action = 6; + } + } +} + +void func_80AEC070(EnRu1* this, GlobalContext* globalCtx, UNK_TYPE arg2) { + if ((func_80AEB458(globalCtx, 8)) && (arg2 != 0)) { + Actor_Kill(&this->actor); + } +} + +void func_80AEC0B4(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB89C(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + func_80AEBC84(this, globalCtx); + func_80AEBC30(globalCtx); + func_80AEBD1C(this, globalCtx); +} + +void func_80AEC100(EnRu1* this, GlobalContext* globalCtx) { + func_80AEBAFC(this); + func_80AEBD94(this, globalCtx); +} + +void func_80AEC130(EnRu1* this, GlobalContext* globalCtx) { + s32 something = EnRu1_UpdateSkelAnime(this); + + func_80AEAECC(this, globalCtx); + func_80AEBB3C(this); + func_80AEBE3C(this, globalCtx, something); +} + +void func_80AEC17C(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB974(this, globalCtx); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + func_80AEB50C(this, globalCtx); + func_80AEBEC8(this, globalCtx); +} + +void func_80AEC1D4(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + something = EnRu1_UpdateSkelAnime(this); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateEyes(this); + func_80AEB50C(this, globalCtx); + func_80AEBCB8(this, something); + func_80AEBBF4(this); + func_80AEBF60(this, globalCtx); +} + +void func_80AEC244(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + something = EnRu1_UpdateSkelAnime(this); + func_80AEBA2C(this, globalCtx); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateEyes(this); + func_80AEB50C(this, globalCtx); + func_80AEBCB8(this, something); + func_80AEBB78(this); + func_80AEBFD8(this, globalCtx); +} + +void func_80AEC2C0(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + something = EnRu1_UpdateSkelAnime(this); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateEyes(this); + func_80AEB50C(this, globalCtx); + func_80AEC070(this, globalCtx, something); +} + +void func_80AEC320(EnRu1* this, GlobalContext* globalCtx) { + s8 actorRoom; + + if (!(gSaveContext.infTable[20] & 2)) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + this->action = 7; + EnRu1_SetMouthIndex(this, 1); + } else if ((gSaveContext.infTable[20] & 0x80) && !(gSaveContext.infTable[20] & 1) && + !(gSaveContext.infTable[20] & 0x20)) { + if (!func_80AEB020(this, globalCtx)) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + actorRoom = this->actor.room; + this->action = 22; + this->actor.room = -1; + this->drawConfig = 0; + this->roomNum1 = actorRoom; + this->roomNum3 = actorRoom; + this->roomNum2 = actorRoom; + } else { + Actor_Kill(&this->actor); + } + } else { + Actor_Kill(&this->actor); + } +} + +void func_80AEC40C(EnRu1* this) { + f32 unk_26C = this->unk_26C; + + if (unk_26C < 8.0f) { + this->actor.speedXZ = (((kREG(3) * 0.01f) + 2.7f) / 8.0f) * unk_26C; + } else { + this->actor.speedXZ = (kREG(3) * 0.01f) + 2.7f; + } + this->actor.velocity.y = -1.0f; + Actor_MoveForward(&this->actor); +} + +void func_80AEC4CC(EnRu1* this) { + this->actor.velocity.y = -1.0f; + Actor_MoveForward(&this->actor); +} + +void func_80AEC4F4(EnRu1* this) { + f32* speedXZ = &this->actor.speedXZ; + f32* unk_26C = &this->unk_26C; + + if (this->unk_26C < 8.0f) { + *unk_26C += 1.0f; + *speedXZ *= (8.0f - *unk_26C) / 8.0f; + this->actor.velocity.y = -*unk_26C * (((kREG(4) * 0.01f) + 13.0f) / 8.0f); + } else { + *speedXZ = 0.0f; + this->actor.velocity.y = -((kREG(4) * 0.01f) + 13.0f); + } + Actor_MoveForward(&this->actor); +} + +s32 func_80AEC5FC(EnRu1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 thisPosZ = this->actor.world.pos.z; + f32 playerPosZ = player->actor.world.pos.z; + + if ((playerPosZ - thisPosZ <= 265.0f) && (player->actor.world.pos.y >= this->actor.world.pos.y)) { + return true; + } + return false; +} + +void func_80AEC650(EnRu1* this) { + s32 pad[2]; + + if (this->unk_280 == 0) { + if (Animation_OnFrame(&this->skelAnime, 2.0f) || Animation_OnFrame(&this->skelAnime, 7.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_DIRT); + } + } +} + +void func_80AEC6B0(EnRu1* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_FALL_DOWN_DIRT); + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_FALL); +} + +void func_80AEC6E4(EnRu1* this, GlobalContext* globalCtx) { + if ((func_80AEAFA0(globalCtx, 4, 3)) && (this->unk_280 == 0)) { + Animation_Change(&this->skelAnime, &gRutoChildBringArmsUpAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildBringArmsUpAnim), ANIMMODE_ONCE, -8.0f); + this->unk_280 = 1; + func_80AEC6B0(this); + } +} + +void func_80AEC780(EnRu1* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + if ((func_80AEC5FC(this, globalCtx)) && (!Gameplay_InCsMode(globalCtx)) && (!(player->stateFlags1 & 0x206000)) && + (player->actor.bgCheckFlags & 1)) { + + globalCtx->csCtx.segment = &D_80AF0880; + gSaveContext.cutsceneTrigger = 1; + player->linearVelocity = 0.0f; + this->action = 8; + } +} + +void func_80AEC81C(EnRu1* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction; + s16 newRotY; + + if (func_80AEAFE0(globalCtx, 1, 3)) { + csCmdNPCAction = globalCtx->csCtx.npcActions[3]; + this->actor.world.pos.x = csCmdNPCAction->startPos.x; + this->actor.world.pos.y = csCmdNPCAction->startPos.y; + this->actor.world.pos.z = csCmdNPCAction->startPos.z; + newRotY = csCmdNPCAction->rot.y; + this->actor.shape.rot.y = newRotY; + this->actor.world.rot.y = newRotY; + this->action = 9; + this->drawConfig = 1; + } +} + +void func_80AEC8B8(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEAFA0(globalCtx, 3, 3)) { + Animation_Change(&this->skelAnime, &gRutoChildTurnAroundAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildTurnAroundAnim), ANIMMODE_ONCE, -8.0f); + this->action = 10; + } +} + +void func_80AEC93C(EnRu1* this, UNK_TYPE arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gRutoChildWalkAnim, 1.0f, 0, Animation_GetLastFrame(&gRutoChildWalkAnim), + ANIMMODE_LOOP, -8.0f); + this->actor.world.rot.y += 0x8000; + this->action = 0xB; + this->unk_26C = 0.0f; + } +} + +void func_80AEC9C4(EnRu1* this) { + this->unk_26C += 1.0f; + if (this->unk_26C >= 8.0f) { + this->action = 12; + this->unk_26C = 0.0f; + this->actor.velocity.y = -1.0f; + } +} + +void func_80AECA18(EnRu1* this) { + if (!(this->actor.bgCheckFlags & 1)) { + this->action = 13; + this->unk_26C = 0.0f; + this->actor.velocity.y = 0.0f; + } +} + +void func_80AECA44(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEAFA0(globalCtx, 5, 3)) { + gSaveContext.infTable[20] |= 2; + this->action = 14; + } +} + +void func_80AECA94(EnRu1* this, GlobalContext* globalCtx) { + func_80AEC780(this, globalCtx); +} + +void func_80AECAB4(EnRu1* this, GlobalContext* globalCtx) { + func_80AEC81C(this, globalCtx); +} + +void func_80AECAD4(EnRu1* this, GlobalContext* globalCtx) { + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEC8B8(this, globalCtx); +} + +void func_80AECB18(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + something = EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEC93C(this, something); +} + +void func_80AECB60(EnRu1* this, GlobalContext* globalCtx) { + func_80AEC40C(this); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEC650(this); + func_80AEC9C4(this); +} + +void func_80AECBB8(EnRu1* this, GlobalContext* globalCtx) { + func_80AEC4CC(this); + func_80AEC6E4(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEC650(this); + func_80AECA18(this); +} + +void func_80AECC1C(EnRu1* this, GlobalContext* globalCtx) { + func_80AEC4F4(this); + func_80AEC6E4(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEC650(this); + func_80AECA44(this, globalCtx); +} + +void func_80AECC84(EnRu1* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + Actor_Kill(&this->actor); + } +} + +void func_80AECCB0(EnRu1* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f* pos; + s16 yawTowardsPlayer; + f32 spawnX; + f32 spawnY; + f32 spawnZ; + s32 pad2[2]; + + yawTowardsPlayer = this->actor.yawTowardsPlayer; + pos = &this->actor.world.pos; + spawnX = ((kREG(1) + 12.0f) * Math_SinS(yawTowardsPlayer)) + pos->x; + spawnY = pos->y; + spawnZ = ((kREG(1) + 12.0f) * Math_CosS(yawTowardsPlayer)) + pos->z; + this->blueWarp = (DoorWarp1*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, + spawnX, spawnY, spawnZ, 0, yawTowardsPlayer, 0, WARP_BLUE_RUTO); +} + +void func_80AECDA0(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB264(this, &gRutoChildWaitHandsOnHipsAnim, 0, 0, 0); + this->action = 15; + this->actor.shape.yOffset = -10000.0f; + EnRu1_SetEyeIndex(this, 5); + EnRu1_SetMouthIndex(this, 2); +} + +void func_80AECE04(EnRu1* this, GlobalContext* globalCtx) { + this->actor.shape.yOffset += (250.0f / 3.0f); +} + +void func_80AECE20(EnRu1* this, GlobalContext* globalCtx) { + s32 pad2; + Player* player = GET_PLAYER(globalCtx); + Vec3f* playerPos = &player->actor.world.pos; + s16 shapeRotY = player->actor.shape.rot.y; + s32 pad; + f32 unk_27C = this->unk_27C; + Vec3f* pos = &this->actor.world.pos; + + pos->x = (Math_SinS(shapeRotY) * unk_27C) + playerPos->x; + pos->y = playerPos->y; + pos->z = (Math_CosS(shapeRotY) * unk_27C) + playerPos->z; +} + +void func_80AECEB4(EnRu1* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + Vec3f* player_unk_450 = &player->unk_450; + Vec3f* pos = &this->actor.world.pos; + s16 shapeRotY = this->actor.shape.rot.y; + + player_unk_450->x = ((kREG(2) + 30.0f) * Math_SinS(shapeRotY)) + pos->x; + player_unk_450->z = ((kREG(2) + 30.0f) * Math_CosS(shapeRotY)) + pos->z; +} + +s32 func_80AECF6C(EnRu1* this, GlobalContext* globalCtx) { + s16* shapeRotY; + Player* player = GET_PLAYER(globalCtx); + Player* otherPlayer; + s16 temp_f16; + f32 temp1; + f32 temp2; + s32 pad2[5]; + + this->unk_26C += 1.0f; + if ((player->actor.speedXZ == 0.0f) && (this->unk_26C >= 3.0f)) { + otherPlayer = GET_PLAYER(globalCtx); + player->actor.world.pos.x = otherPlayer->unk_450.x; + player->actor.world.pos.y = otherPlayer->unk_450.y; + player->actor.world.pos.z = otherPlayer->unk_450.z; + shapeRotY = &player->actor.shape.rot.y; + temp1 = this->actor.world.pos.x - player->actor.world.pos.x; + temp2 = this->actor.world.pos.z - player->actor.world.pos.z; + temp_f16 = Math_FAtan2F(temp1, temp2) * (0x8000 / M_PI); + if (*shapeRotY != temp_f16) { + Math_SmoothStepToS(shapeRotY, temp_f16, 0x14, 0x1838, 0x64); + player->actor.world.rot.y = *shapeRotY; + } else { + return true; + } + } + return false; +} + +s32 func_80AED084(EnRu1* this, s32 state) { + if (this->blueWarp != NULL && this->blueWarp->rutoWarpState == state) { + return true; + } + return false; +} + +void func_80AED0B0(EnRu1* this, s32 state) { + if (this->blueWarp != NULL) { + this->blueWarp->rutoWarpState = state; + } +} + +void func_80AED0C8(EnRu1* this, GlobalContext* globalCtx) { + this->action = 16; +} + +void func_80AED0D8(EnRu1* this, GlobalContext* globalCtx) { + this->action = 17; + this->drawConfig = 1; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + func_80AECCB0(this, globalCtx); +} + +void func_80AED110(EnRu1* this) { + if (this->actor.shape.yOffset >= 0.0f) { + this->action = 18; + this->actor.shape.yOffset = 0.0f; + func_80AED0B0(this, WARP_BLUE_RUTO_STATE_READY); + } +} + +void func_80AED154(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AED084(this, WARP_BLUE_RUTO_STATE_ENTERED)) { + this->action = 0x13; + this->unk_26C = 0.0f; + func_80AECEB4(this, globalCtx); + } +} + +void func_80AED19C(EnRu1* this, s32 cond) { + if (cond) { + Animation_Change(&this->skelAnime, &gRutoChildTransitionHandsOnHipToCrossArmsAndLegsAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildTransitionHandsOnHipToCrossArmsAndLegsAnim), ANIMMODE_ONCE, + -8.0f); + this->action = 20; + func_80AED0B0(this, WARP_BLUE_RUTO_STATE_3); + } +} + +void func_80AED218(EnRu1* this, UNK_TYPE arg1) { + if (func_80AED084(this, WARP_BLUE_RUTO_STATE_TALKING)) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gRutoChildWaitSittingAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildWaitSittingAnim), ANIMMODE_LOOP, -8.0f); + } + } else if (func_80AED084(this, WARP_BLUE_RUTO_STATE_WARPING)) { + Animation_Change(&this->skelAnime, &gRutoChildWaitInBlueWarpAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildWaitInBlueWarpAnim), ANIMMODE_ONCE, -8.0f); + this->action = 21; + this->unk_27C = this->actor.xzDistToPlayer; + } +} + +void func_80AED304(EnRu1* this, GlobalContext* globalCtx) { + func_80AED0C8(this, globalCtx); +} + +void func_80AED324(EnRu1* this, GlobalContext* globalCtx) { + func_80AED0D8(this, globalCtx); +} + +void func_80AED344(EnRu1* this, GlobalContext* globalCtx) { + func_80AECE04(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + func_80AED110(this); +} + +void func_80AED374(EnRu1* this, GlobalContext* globalCtx) { + EnRu1_UpdateSkelAnime(this); + func_80AED154(this, globalCtx); +} + +void func_80AED3A4(EnRu1* this, GlobalContext* globalCtx) { + EnRu1_UpdateSkelAnime(this); + func_80AED19C(this, func_80AECF6C(this, globalCtx)); +} + +void func_80AED3E0(EnRu1* this, GlobalContext* globalCtx) { + func_80AEAECC(this, globalCtx); + func_80AED218(this, EnRu1_UpdateSkelAnime(this)); +} + +void func_80AED414(EnRu1* this, GlobalContext* globalCtx) { + func_80AECE20(this, globalCtx); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); +} + +void func_80AED44C(EnRu1* this, GlobalContext* globalCtx) { + s8 actorRoom; + + if ((gSaveContext.infTable[20] & 2) && !(gSaveContext.infTable[20] & 0x20) && !(gSaveContext.infTable[20] & 1) && + !(gSaveContext.infTable[20] & 0x80)) { + if (!func_80AEB020(this, globalCtx)) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + actorRoom = this->actor.room; + this->action = 22; + this->actor.room = -1; + this->roomNum1 = actorRoom; + this->roomNum3 = actorRoom; + this->roomNum2 = actorRoom; + } else { + Actor_Kill(&this->actor); + } + } else { + Actor_Kill(&this->actor); + } +} + +void func_80AED4FC(EnRu1* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_LAND_DIRT); +} + +void func_80AED520(EnRu1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Audio_PlaySoundGeneral(NA_SE_PL_PULL_UP_RUTO, &player->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_LIFT); +} + +void func_80AED57C(EnRu1* this) { + if (this->actor.speedXZ != 0.0f) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_THROW); + } +} + +void func_80AED5B8(EnRu1* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_CRASH); +} + +void func_80AED5DC(EnRu1* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_UNBALLANCE); +} + +void func_80AED600(EnRu1* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_DISCOVER); +} + +s32 func_80AED624(EnRu1* this, GlobalContext* globalCtx) { + s8 curRoomNum = globalCtx->roomCtx.curRoom.num; + + if (this->roomNum2 != curRoomNum) { + Actor_Kill(&this->actor); + return false; + } else if (((this->roomNum1 != curRoomNum) || (this->roomNum2 != curRoomNum)) && + (this->actor.yDistToWater > kREG(16) + 50.0f) && (this->action != 33)) { + this->action = 33; + this->drawConfig = 2; + this->alpha = 0xFF; + this->unk_2A4 = 0.0f; + } + return true; +} + +void func_80AED6DC(EnRu1* this, GlobalContext* globalCtx) { + s8 curRoomNum = globalCtx->roomCtx.curRoom.num; + + this->roomNum2 = curRoomNum; + this->unk_288 = 0.0f; +} + +void func_80AED6F8(GlobalContext* globalCtx) { + s8 curRoomNum; + + if ((!(gSaveContext.infTable[20] & 0x80))) { + curRoomNum = globalCtx->roomCtx.curRoom.num; + if (curRoomNum == 2) { + gSaveContext.infTable[20] |= 0x80; + } + } +} + +void func_80AED738(EnRu1* this, GlobalContext* globalCtx) { + u32 temp_v0; + + if (func_80AED624(this, globalCtx)) { + this->unk_2A4 += 1.0f; + if (this->unk_2A4 < 20.0f) { + temp_v0 = ((20.0f - this->unk_2A4) * 255.0f) / 20.0f; + this->alpha = temp_v0; + this->actor.shape.shadowAlpha = temp_v0; + } else { + Actor_Kill(&this->actor); + } + } +} + +void func_80AED83C(EnRu1* this) { + s32 pad[2]; + Vec3s* tempPtr; + Vec3s* tempPtr2; + + tempPtr = &this->unk_374.unk_08; + Math_SmoothStepToS(&tempPtr->x, 0, 0x14, 0x1838, 0x64); + Math_SmoothStepToS(&tempPtr->y, 0, 0x14, 0x1838, 0x64); + tempPtr2 = &this->unk_374.unk_0E; + Math_SmoothStepToS(&tempPtr2->x, 0, 0x14, 0x1838, 0x64); + Math_SmoothStepToS(&tempPtr2->y, 0, 0x14, 0x1838, 0x64); +} + +void func_80AED8DC(EnRu1* this) { + s32 temp_hi; + s16* unk_2AC = &this->unk_2AC; + s16* someY = &this->unk_374.unk_08.y; + s16* unk_29E = &this->unk_29E; + s32 pad[2]; + + if (DECR(*unk_2AC) == 0) { + *unk_2AC = Rand_S16Offset(0xA, 0x19); + temp_hi = *unk_2AC % 5; + if (temp_hi == 0) { + this->unk_2B0 = 1; + } else if (temp_hi == 1) { + this->unk_2B0 = 2; + } else { + this->unk_2B0 = 0; + } + *unk_29E = 0; + } + + if (this->unk_2B0 == 0) { + Math_SmoothStepToS(unk_29E, 0 - *someY, 1, 0x190, 0x190); + Math_SmoothStepToS(someY, 0, 3, ABS(*unk_29E), 0x64); + } else if (this->unk_2B0 == 1) { + Math_SmoothStepToS(unk_29E, -0x2AAA - *someY, 1, 0x190, 0x190); + Math_SmoothStepToS(someY, -0x2AAA, 3, ABS(*unk_29E), 0x64); + } else { + Math_SmoothStepToS(unk_29E, 0x2AAA - *someY, 1, 0x190, 0x190); + Math_SmoothStepToS(someY, 0x2AAA, 3, ABS(*unk_29E), 0x64); + } +} + +void func_80AEDAE0(EnRu1* this, GlobalContext* globalCtx) { + DynaPolyActor* dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.floorBgId); + + if (dynaPolyActor == NULL || dynaPolyActor->actor.id == ACTOR_EN_BOX) { + this->actor.bgCheckFlags &= ~0x19; + } +} + +void func_80AEDB30(EnRu1* this, GlobalContext* globalCtx) { + DynaPolyActor* dynaPolyActor; + f32* velocityY; + f32* speedXZ; + f32* gravity; + s16 wallYaw; + s16 rotY; + s32 temp_a1_2; + s32 temp_a0; + s32 phi_v1; + + if (this->actor.bgCheckFlags & 1) { + velocityY = &this->actor.velocity.y; + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.floorBgId); + if (*velocityY <= 0.0f) { + speedXZ = &this->actor.speedXZ; + if (dynaPolyActor != NULL) { + if (dynaPolyActor->actor.id != ACTOR_EN_BOX) { + *speedXZ = 0.0f; + } + } else { + if (*speedXZ >= (kREG(27) * 0.01f) + 3.0f) { + *speedXZ *= (kREG(19) * 0.01f) + 0.8f; + } else { + *speedXZ = 0.0f; + } + } + gravity = &this->actor.gravity; + if (dynaPolyActor != NULL) { + if (dynaPolyActor->actor.id != ACTOR_EN_BOX) { + *velocityY = 0.0f; + this->actor.minVelocityY = 0.0f; + *gravity = 0.0f; + } else { + *velocityY *= -1.0f; + } + } else { + *velocityY *= -((kREG(20) * 0.01f) + 0.6f); + if (*velocityY <= -*gravity * ((kREG(20) * 0.01f) + 0.6f)) { + *velocityY = 0.0f; + this->actor.minVelocityY = 0.0f; + *gravity = 0.0f; + } + } + func_80AED4FC(this); + } + } + if (this->actor.bgCheckFlags & 0x10) { + speedXZ = &this->actor.speedXZ; + velocityY = &this->actor.velocity.y; + if (*speedXZ >= (kREG(27) * 0.01f) + 3.0f) { + *speedXZ *= (kREG(19) * 0.01f) + 0.8f; + } else { + *speedXZ = 0.0f; + } + if (*velocityY >= 0.0f) { + *velocityY *= -((kREG(20) * 0.01f) + 0.6f); + func_80AED4FC(this); + } + } + if (this->actor.bgCheckFlags & 8) { + speedXZ = &this->actor.speedXZ; + if (*speedXZ != 0.0f) { + rotY = this->actor.world.rot.y; + wallYaw = this->actor.wallYaw; + temp_a0 = (wallYaw * 2) - rotY; + temp_a1_2 = temp_a0 + 0x8000; + if ((s16)((temp_a0 - wallYaw) + 0x8000) >= 0) { + phi_v1 = (s16)(temp_a1_2 - wallYaw); + } else { + phi_v1 = -(s16)(temp_a1_2 - wallYaw); + } + if (phi_v1 < 0x4001) { + if (*speedXZ >= (kREG(27) * 0.01f) + 3.0f) { + *speedXZ *= (kREG(21) * 0.01f) + 0.6f; + } else { + *speedXZ = 0.0f; + } + this->actor.world.rot.y = temp_a1_2; + func_80AED4FC(this); + func_80AED5B8(this); + } + } + } +} + +void func_80AEDEF4(EnRu1* this, GlobalContext* globalCtx) { + f32* speedXZ = &this->actor.speedXZ; + DynaPolyActor* dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.floorBgId); + + if (dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_EN_BOX) { + if (*speedXZ != 0.0f) { + *speedXZ *= 1.1f; + } else { + *speedXZ = 1.0f; + } + } + if (*speedXZ >= (kREG(27) * 0.01f) + 3.0f) { + *speedXZ *= (kREG(22) * 0.01f) + 0.98f; + } else { + *speedXZ = 0.0f; + } +} + +void func_80AEDFF4(EnRu1* this, GlobalContext* globalCtx) { + func_80AEDB30(this, globalCtx); + func_80AEDEF4(this, globalCtx); + Actor_MoveForward(&this->actor); +} + +void func_80AEE02C(EnRu1* this) { + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + this->actor.minVelocityY = 0.0f; +} + +void func_80AEE050(EnRu1* this) { + s32 pad; + f32 sp28; + f32 sp24; + f32 temp_f10; + EnRu1* thisx = this; // necessary to match + + if (this->unk_350 == 0) { + if ((this->actor.minVelocityY == 0.0f) && (this->actor.speedXZ == 0.0f)) { + this->unk_350 = 1; + func_80AEE02C(this); + this->unk_35C = 0; + this->unk_358 = (this->actor.yDistToWater - 10.0f) * 0.5f; + this->unk_354 = this->actor.world.pos.y + thisx->unk_358; // thisx only used here + } else { + this->actor.gravity = 0.0f; + this->actor.minVelocityY *= 0.2f; + this->actor.velocity.y *= 0.2f; + if (this->actor.minVelocityY >= -0.1f) { + this->actor.minVelocityY = 0.0f; + this->actor.velocity.y = 0.0f; + } + this->actor.speedXZ *= 0.5f; + if (this->actor.speedXZ <= 0.1f) { + this->actor.speedXZ = 0.0f; + } + this->actor.velocity.x = Math_SinS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.velocity.z = Math_CosS(this->actor.world.rot.y) * this->actor.speedXZ; + func_8002D7EC(&this->actor); + } + } else { + if (this->unk_350 == 1) { + if (this->unk_358 <= 1.0f) { + func_80AEE02C(this); + this->unk_350 = 2; + this->unk_360 = 0.0f; + } else { + sp28 = this->unk_358; + sp24 = this->unk_354; + temp_f10 = Math_CosS(this->unk_35C) * -sp28; + this->actor.world.pos.y = temp_f10 + sp24; + this->unk_35C += 0x3E8; + this->unk_358 *= 0.95f; + } + } else { + this->unk_360 += 1.0f; + if (this->unk_360 > 0.0f) { + this->unk_350 = 3; + } + } + } +} + +s32 func_80AEE264(EnRu1* this, GlobalContext* globalCtx) { + if (!Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + if ((gSaveContext.infTable[20] & 8)) { + this->actor.textId = 0x404E; + func_8002F2F4(&this->actor, globalCtx); + } else if (gSaveContext.infTable[20] & 4) { + this->actor.textId = 0x404D; + func_8002F2F4(&this->actor, globalCtx); + } else { + this->actor.textId = 0x404C; + func_8002F2F4(&this->actor, globalCtx); + } + return false; + } + return true; +} + +void func_80AEE2F8(EnRu1* this, GlobalContext* globalCtx) { + DynaPolyActor* dynaPolyActor; + s32 floorBgId; + + if ((this->actor.bgCheckFlags & 1) && (this->actor.floorBgId != BGCHECK_SCENE)) { + floorBgId = this->actor.floorBgId; + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, floorBgId); + if ((dynaPolyActor != NULL) && (dynaPolyActor->actor.id == ACTOR_BG_BDAN_SWITCH)) { + if (((dynaPolyActor->actor.params >> 8) & 0x3F) == 0x38) { + gSaveContext.infTable[20] |= 1; + return; + } + } + } + gSaveContext.infTable[20] &= ~0x1; +} + +s32 func_80AEE394(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + CollisionContext* colCtx; + DynaPolyActor* dynaPolyActor; + s32 floorBgId; + + if ((this->actor.bgCheckFlags & 1) && this->actor.floorBgId != BGCHECK_SCENE) { + colCtx = &globalCtx->colCtx; + floorBgId = this->actor.floorBgId; // necessary match, can't move this out of this block unfortunately + dynaPolyActor = DynaPoly_GetActor(colCtx, floorBgId); + if (dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_BG_BDAN_OBJECTS && + dynaPolyActor->actor.params == 0 && !Player_InCsMode(globalCtx) && globalCtx->msgCtx.msgLength == 0) { + func_80AEE02C(this); + globalCtx->csCtx.segment = &D_80AF10A4; + gSaveContext.cutsceneTrigger = 1; + this->action = 36; + this->drawConfig = 0; + this->unk_28C = (BgBdanObjects*)dynaPolyActor; + this->actor.shape.shadowAlpha = 0; + return true; + } + } + return false; +} + +void func_80AEE488(EnRu1* this, GlobalContext* globalCtx) { + s8 curRoomNum; + + if (Actor_HasParent(&this->actor, globalCtx)) { + curRoomNum = globalCtx->roomCtx.curRoom.num; + this->roomNum3 = curRoomNum; + this->action = 31; + func_80AED520(this, globalCtx); + } else if ((!func_80AEE394(this, globalCtx)) && (!(this->actor.bgCheckFlags & 1))) { + this->actor.minVelocityY = -((kREG(24) * 0.01f) + 6.8f); + this->actor.gravity = -((kREG(23) * 0.01f) + 1.3f); + this->action = 28; + } +} + +void func_80AEE568(EnRu1* this, GlobalContext* globalCtx) { + if (!func_80AEE394(this, globalCtx)) { + if ((this->actor.bgCheckFlags & 1) && (this->actor.speedXZ == 0.0f) && (this->actor.minVelocityY == 0.0f)) { + func_80AEE02C(this); + func_8002F580(&this->actor, globalCtx); + this->action = 27; + func_80AEADD8(this); + } else if (this->actor.yDistToWater > 0.0f) { + this->action = 29; + this->unk_350 = 0; + } + } +} + +void func_80AEE628(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + s8 curRoomNum = globalCtx->roomCtx.curRoom.num; + + if (EnRu1_IsCsStateIdle(globalCtx)) { + Animation_Change(&this->skelAnime, &gRutoChildSittingAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildSittingAnim), ANIMMODE_LOOP, -8.0f); + gSaveContext.infTable[20] |= 0x10; + this->action = 31; + } + this->roomNum3 = curRoomNum; +} + +s32 func_80AEE6D0(EnRu1* this, GlobalContext* globalCtx) { + s32 pad; + s8 curRoomNum = globalCtx->roomCtx.curRoom.num; + + if (!(gSaveContext.infTable[20] & 0x10) && (func_80AEB124(globalCtx) != 0)) { + if (!Player_InCsMode(globalCtx)) { + Animation_Change(&this->skelAnime, &gRutoChildSeesSapphireAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildSquirmAnim), ANIMMODE_LOOP, -8.0f); + func_80AED600(this); + this->action = 34; + this->unk_26C = 0.0f; + globalCtx->csCtx.segment = &D_80AF1728; + gSaveContext.cutsceneTrigger = 1; + } + this->roomNum3 = curRoomNum; + return true; + } + this->roomNum3 = curRoomNum; + return false; +} + +void func_80AEE7C4(EnRu1* this, GlobalContext* globalCtx) { + f32 frameCount; + s32 pad[13]; + Player* player; + f32* unk_370 = &this->unk_370; + + if (Actor_HasNoParent(&this->actor, globalCtx)) { + frameCount = Animation_GetLastFrame(&gRutoChildSittingAnim); + Animation_Change(&this->skelAnime, &gRutoChildSittingAnim, 1.0f, 0, frameCount, ANIMMODE_LOOP, -8.0f); + func_80AED6DC(this, globalCtx); + this->actor.speedXZ *= (kREG(25) * 0.01f) + 1.0f; + this->actor.velocity.y *= (kREG(26) * 0.01f) + 1.0f; + this->actor.minVelocityY = -((kREG(24) * 0.01f) + 6.8f); + this->actor.gravity = -((kREG(23) * 0.01f) + 1.3f); + func_80AED57C(this); + this->action = 28; + *unk_370 = 0.0f; + return; + } + + if (func_80AEE6D0(this, globalCtx)) { + *unk_370 = 0.0f; + return; + } + + player = GET_PLAYER(globalCtx); + if (player->stateFlags2 & 0x10000000) { + this->unk_370 += 1.0f; + if (this->action != 32) { + if (*unk_370 > 30.0f) { + if (Rand_S16Offset(0, 3) == 0) { + frameCount = Animation_GetLastFrame(&gRutoChildSquirmAnim); + Animation_Change(&this->skelAnime, &gRutoChildSquirmAnim, 1.0f, 0, frameCount, ANIMMODE_LOOP, + -8.0f); + func_80AED5DC(this); + this->action = 32; + } + *unk_370 = 0.0f; + } + } else { + if (*unk_370 > 50.0f) { + frameCount = Animation_GetLastFrame(&gRutoChildSittingAnim); + Animation_Change(&this->skelAnime, &gRutoChildSittingAnim, 1.0f, 0, frameCount, ANIMMODE_LOOP, -8.0f); + this->action = 31; + *unk_370 = 0.0f; + } + } + } else { + frameCount = Animation_GetLastFrame(&gRutoChildSittingAnim); + Animation_Change(&this->skelAnime, &gRutoChildSittingAnim, 1.0f, 0, frameCount, ANIMMODE_LOOP, -8.0f); + *unk_370 = 0.0f; + } +} + +s32 func_80AEEAC8(EnRu1* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 1) { + func_80AEE02C(this); + func_8002F580(&this->actor, globalCtx); + this->action = 27; + func_80AEADD8(this); + return true; + } + return false; +} + +void func_80AEEB24(EnRu1* this, GlobalContext* globalCtx) { + if ((func_80AEEAC8(this, globalCtx) == 0) && (this->unk_350 == 3)) { + this->action = 30; + func_80AEE02C(this); + this->actor.gravity = -0.1f; + this->actor.minVelocityY = -((kREG(18) * 0.1f) + 0.7f); + } +} + +void func_80AEEBB4(EnRu1* this, GlobalContext* globalCtx) { + func_8002F580(&this->actor, globalCtx); +} + +void func_80AEEBD4(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAC54(this, globalCtx); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEEBB4(this, globalCtx); + func_80AEE488(this, globalCtx); + func_80AED624(this, globalCtx); + func_80AEDAE0(this, globalCtx); +} + +void func_80AEEC5C(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEACDC(this, globalCtx); + func_80AEAECC(this, globalCtx); + func_80AEE2F8(this, globalCtx); + func_80AEDFF4(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEE568(this, globalCtx); + func_80AED624(this, globalCtx); + func_80AEDAE0(this, globalCtx); +} + +void func_80AEECF0(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + func_80AEE050(this); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEEB24(this, globalCtx); + func_80AED624(this, globalCtx); +} + +void func_80AEED58(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + Actor_MoveForward(&this->actor); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEEAC8(this, globalCtx); + func_80AED624(this, globalCtx); + func_80AEDAE0(this, globalCtx); +} + +void func_80AEEDCC(EnRu1* this, GlobalContext* globalCtx) { + func_80AED8DC(this); + EnRu1_UpdateSkelAnime(this); + func_80AEAECC(this, globalCtx); + func_80AEE2F8(this, globalCtx); + EnRu1_UpdateEyes(this); + func_80AED6F8(globalCtx); + func_80AEE7C4(this, globalCtx); +} + +void func_80AEEE34(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + EnRu1_UpdateSkelAnime(this); + func_80AEAECC(this, globalCtx); + func_80AEE2F8(this, globalCtx); + EnRu1_UpdateEyes(this); + func_80AED6F8(globalCtx); + func_80AEE7C4(this, globalCtx); +} + +void func_80AEEE9C(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + func_80AEDFF4(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AED738(this, globalCtx); + func_80AED624(this, globalCtx); +} + +void func_80AEEF08(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + EnRu1_UpdateSkelAnime(this); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateEyes(this); + func_80AEE628(this, globalCtx); +} + +void func_80AEEF5C(EnRu1* this, GlobalContext* globalCtx) { +} + +void func_80AEEF68(EnRu1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 something; + + this->unk_374.unk_18 = player->actor.world.pos; + this->unk_374.unk_14 = kREG(16) - 3.0f; + something = kREG(17) + 0xC; + func_80034A14(&this->actor, &this->unk_374, something, 2); +} + +void func_80AEEFEC(EnRu1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 something; + + this->unk_374.unk_18 = player->actor.world.pos; + this->unk_374.unk_14 = kREG(16) - 3.0f; + something = kREG(17) + 0xC; + func_80034A14(&this->actor, &this->unk_374, something, 4); + this->actor.world.rot.y = this->actor.shape.rot.y; +} + +void func_80AEF080(EnRu1* this) { + if (Animation_OnFrame(&this->skelAnime, 11.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_LAND_DIRT); + } +} + +s32 func_80AEF0BC(EnRu1* this, GlobalContext* globalCtx) { + s32 frameCount; + + if (gSaveContext.infTable[20] & 4) { + frameCount = Animation_GetLastFrame(&gRutoChildSitAnim); + Animation_Change(&this->skelAnime, &gRutoChildSitAnim, 1.0f, 0, frameCount, ANIMMODE_ONCE, -8.0f); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->action = 26; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + return true; + } + return false; +} + +void func_80AEF170(EnRu1* this, GlobalContext* globalCtx, s32 cond) { + if (cond) { + this->action = 25; + } +} + +void func_80AEF188(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEB174(globalCtx) && !func_80AEF0BC(this, globalCtx)) { + Message_CloseTextbox(globalCtx); + gSaveContext.infTable[20] |= 4; + this->action = 24; + } +} + +void func_80AEF1F0(EnRu1* this, GlobalContext* globalCtx, UNK_TYPE arg2) { + if (arg2 != 0) { + Animation_Change(&this->skelAnime, &gRutoChildSittingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gRutoChildSittingAnim), ANIMMODE_LOOP, 0.0f); + Message_CloseTextbox(globalCtx); + gSaveContext.infTable[20] |= 8; + func_80AED6DC(this, globalCtx); + func_8002F580(&this->actor, globalCtx); + this->action = 27; + func_80AEADD8(this); + } +} + +void func_80AEF29C(EnRu1* this, GlobalContext* globalCtx) { + this->action = 23; +} + +void func_80AEF2AC(EnRu1* this, GlobalContext* globalCtx) { + this->action = 24; + this->drawConfig = 1; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; +} + +void func_80AEF2D0(EnRu1* this, GlobalContext* globalCtx) { + s32 cond; + + func_80AEEF68(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAC10(this, globalCtx); + func_80AEAECC(this, globalCtx); + cond = func_80AEE264(this, globalCtx); + func_80AED624(this, globalCtx); + func_80AEF170(this, globalCtx, cond); +} + +void func_80AEF354(EnRu1* this, GlobalContext* globalCtx) { + func_80AEEFEC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEF188(this, globalCtx); +} + +void func_80AEF3A8(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + func_80AED83C(this); + something = EnRu1_UpdateSkelAnime(this); + func_80AEF080(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEF1F0(this, globalCtx, something); +} + +void func_80AEF40C(EnRu1* this) { + SkelAnime* skelAnime = &this->skelAnime; + + if (Animation_OnFrame(skelAnime, 2.0f) || Animation_OnFrame(skelAnime, 7.0f) || + Animation_OnFrame(skelAnime, 12.0f) || Animation_OnFrame(skelAnime, 18.0f) || + Animation_OnFrame(skelAnime, 25.0f) || Animation_OnFrame(skelAnime, 33.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_DIRT); + } +} + +void func_80AEF4A8(EnRu1* this, GlobalContext* globalCtx) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.projectedPos, 20, NA_SE_VO_RT_FALL); +} + +void func_80AEF4E0(EnRu1* this) { + if (Animation_OnFrame(&this->skelAnime, 5.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_LAUGH_0); + } +} + +void func_80AEF51C(EnRu1* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_RT_THROW); +} + +void func_80AEF540(EnRu1* this) { + if (func_80AEB104(this) == 2) { + EnRu1_SetEyeIndex(this, 3); + EnRu1_SetMouthIndex(this, 2); + if (this->skelAnime.mode != 2) { + func_80AEB264(this, &gRutoChildShutterAnim, 2, -8.0f, 0); + func_80AEF51C(this); + } + } +} + +void func_80AEF5B8(EnRu1* this) { + f32 curFrame; + + if (D_80AF1938 == 0) { + curFrame = this->skelAnime.curFrame; + if (curFrame >= 60.0f) { + EnRu1_SetEyeIndex(this, 3); + EnRu1_SetMouthIndex(this, 0); + func_80AED57C(this); + D_80AF1938 = 1; + } + } +} + +void func_80AEF624(EnRu1* this, GlobalContext* globalCtx) { + f32 frameCount; + CsCmdActorAction* csCmdNPCAction; + CsCmdActorAction* csCmdNPCAction2; + s16 newRotTmp; + + if (func_80AEAFE0(globalCtx, 1, 3)) { + frameCount = Animation_GetLastFrame(&gRutoChildWalkToAndHoldUpSapphireAnim); + // this weird part with the redundant variable is necessary to match for some reason + csCmdNPCAction2 = globalCtx->csCtx.npcActions[3]; + csCmdNPCAction = csCmdNPCAction2; + this->actor.world.pos.x = csCmdNPCAction->startPos.x; + this->actor.world.pos.y = csCmdNPCAction->startPos.y; + this->actor.world.pos.z = csCmdNPCAction->startPos.z; + newRotTmp = csCmdNPCAction->rot.x; + this->actor.shape.rot.x = newRotTmp; + this->actor.world.rot.x = newRotTmp; + newRotTmp = csCmdNPCAction->rot.y; + this->actor.shape.rot.y = newRotTmp; + this->actor.world.rot.y = newRotTmp; + newRotTmp = csCmdNPCAction->rot.z; + this->actor.shape.rot.z = newRotTmp; + this->actor.world.rot.z = newRotTmp; + Animation_Change(&this->skelAnime, &gRutoChildWalkToAndHoldUpSapphireAnim, 1.0f, 0.0f, frameCount, + ANIMMODE_ONCE, 0.0f); + func_80AEB3A4(this, globalCtx); + this->action = 37; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80AEF728(EnRu1* this, UNK_TYPE arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gRutoChildHoldArmsUpAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gRutoChildHoldArmsUpAnim), ANIMMODE_LOOP, 0.0f); + func_80AEB3CC(this); + this->action = 38; + } +} + +void func_80AEF79C(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEAFE0(globalCtx, 2, 3)) { + Animation_Change(&this->skelAnime, &gRutoChildBringHandsDownAnim, 1.0f, 0, + Animation_GetLastFrame(&gRutoChildBringHandsDownAnim), ANIMMODE_ONCE, -8.0f); + this->action = 39; + } +} + +void func_80AEF820(EnRu1* this, UNK_TYPE arg1) { + if (arg1 != 0) { + Animation_Change(&this->skelAnime, &gRutoChildWait2Anim, 1.0f, 0, Animation_GetLastFrame(&gRutoChildWait2Anim), + ANIMMODE_LOOP, -8.0f); + this->action = 40; + } +} + +void func_80AEF890(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + s8 curRoomNum; + + if ((gSaveContext.sceneSetupIndex < 4) && (EnRu1_IsCsStateIdle(globalCtx))) { + curRoomNum = globalCtx->roomCtx.curRoom.num; + gSaveContext.infTable[20] |= 0x20; + Flags_SetSwitch(globalCtx, func_80AEADE0(this)); + func_80AEB0EC(this, 1); + this->action = 42; + this->actor.room = curRoomNum; + } +} + +void func_80AEF930(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEB104(this) == 3) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.textId = 0x4048; + Message_ContinueTextbox(globalCtx, this->actor.textId); + func_80AEF4A8(this, globalCtx); + this->action = 43; + this->drawConfig = 0; + } +} + +void func_80AEF99C(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEB1B4(globalCtx) != 0) { + func_80AEB0EC(this, 4); + Actor_Kill(&this->actor); + } +} + +void func_80AEF9D8(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEF624(this, globalCtx); + func_80AEB220(this, globalCtx); +} + +void func_80AEFA2C(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + func_80AED83C(this); + func_80AEB364(this, globalCtx); + func_80AEAECC(this, globalCtx); + something = EnRu1_UpdateSkelAnime(this); + func_80AEF4E0(this); + func_80AEF5B8(this); + func_80AEF40C(this); + func_80AEF728(this, something); + func_80AEB220(this, globalCtx); +} + +void func_80AEFAAC(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + func_80AEF79C(this, globalCtx); + func_80AEB220(this, globalCtx); +} + +void func_80AEFB04(EnRu1* this, GlobalContext* globalCtx) { + s32 something; + + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + something = EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEF820(this, something); + func_80AEB220(this, globalCtx); +} + +void func_80AEFB68(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEF890(this, globalCtx); + func_80AEB220(this, globalCtx); +} + +void func_80AEFBC8(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEAECC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEF540(this); + func_80AEF930(this, globalCtx); +} + +void func_80AEFC24(EnRu1* this, GlobalContext* globalCtx) { + func_80AED83C(this); + func_80AEF99C(this, globalCtx); +} + +void func_80AEFC54(EnRu1* this, GlobalContext* globalCtx) { + if ((gSaveContext.infTable[20] & 0x20) && !(gSaveContext.infTable[20] & 0x40)) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + this->action = 41; + this->unk_28C = EnRu1_FindSwitch(globalCtx); + func_80AEB0EC(this, 1); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + } else { + Actor_Kill(&this->actor); + } +} + +void func_80AEFCE8(EnRu1* this, GlobalContext* globalCtx) { + this->unk_28C = EnRu1_FindSwitch(globalCtx); + if (this->unk_28C != NULL) { + this->action = 42; + this->drawConfig = 1; + func_80AEB0EC(this, 1); + } +} + +void func_80AEFD38(EnRu1* this, GlobalContext* globalCtx) { + if ((gSaveContext.eventChkInf[3] & 0x80) && LINK_IS_CHILD) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + this->actor.flags &= ~ACTOR_FLAG_4; + this->action = 44; + this->drawConfig = 1; + } else { + Actor_Kill(&this->actor); + } +} + +s32 func_80AEFDC0(EnRu1* this, GlobalContext* globalCtx) { + if (!Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.textId = Text_GetFaceReaction(globalCtx, 0x1F); + if (this->actor.textId == 0) { + this->actor.textId = 0x402C; + } + func_8002F2F4(&this->actor, globalCtx); + return false; + } + return true; +} + +s32 func_80AEFE38(EnRu1* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + return true; + } + return false; +} + +void func_80AEFE84(EnRu1* this, GlobalContext* globalCtx, s32 cond) { + if (cond) { + this->action = 45; + } +} + +void func_80AEFE9C(EnRu1* this, GlobalContext* globalCtx) { + if (func_80AEFE38(this, globalCtx)) { + this->action = 44; + } +} + +void func_80AEFECC(EnRu1* this, GlobalContext* globalCtx) { + func_80AEEF68(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAC10(this, globalCtx); + func_80AEAECC(this, globalCtx); + func_80AEFE84(this, globalCtx, func_80AEFDC0(this, globalCtx)); +} + +void func_80AEFF40(EnRu1* this, GlobalContext* globalCtx) { + func_80AEEFEC(this, globalCtx); + EnRu1_UpdateSkelAnime(this); + EnRu1_UpdateEyes(this); + func_80AEAECC(this, globalCtx); + func_80AEFE9C(this, globalCtx); +} + +void func_80AEFF94(EnRu1* this, GlobalContext* globalCtx) { + s8 actorRoom; + + if ((gSaveContext.infTable[20] & 2) && (gSaveContext.infTable[20] & 1) && !(gSaveContext.infTable[20] & 0x20) && + (!(func_80AEB020(this, globalCtx)))) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + actorRoom = this->actor.room; + this->action = 22; + this->actor.room = -1; + this->drawConfig = 0; + this->roomNum1 = actorRoom; + this->roomNum3 = actorRoom; + this->roomNum2 = actorRoom; + // "Ruto switch set" + osSyncPrintf("スイッチルトセット!!!!!!!!!!!!!!!!!!!!!!\n"); + } else { + // "Ruto switch not set" + osSyncPrintf("スイッチルトセットしない!!!!!!!!!!!!!!!!!!!!!!\n"); + Actor_Kill(&this->actor); + } +} + +void func_80AF0050(EnRu1* this, GlobalContext* globalCtx) { + func_80AEB264(this, &gRutoChildWait2Anim, 0, 0, 0); + this->action = 36; + this->roomNum1 = this->actor.room; + this->unk_28C = EnRu1_FindSwitch(globalCtx); + this->actor.room = -1; +} + +void EnRu1_Update(Actor* thisx, GlobalContext* globalCtx) { + EnRu1* this = (EnRu1*)thisx; + + if (this->action < 0 || this->action >= ARRAY_COUNT(sActionFuncs) || sActionFuncs[this->action] == NULL) { + // "Main mode is improper!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + + sActionFuncs[this->action](this, globalCtx); +} + +void EnRu1_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnRu1* this = (EnRu1*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gRutoChildSkel, NULL, this->jointTable, this->morphTable, 17); + func_80AEAD20(&this->actor, globalCtx); + switch (func_80AEADF0(this)) { + case 0: + func_80AECDA0(this, globalCtx); + break; + case 1: + func_80AEB3DC(this, globalCtx); + break; + case 2: + func_80AEC320(this, globalCtx); + break; + case 3: + func_80AED44C(this, globalCtx); + break; + case 4: + func_80AEFC54(this, globalCtx); + break; + case 5: + func_80AEFD38(this, globalCtx); + break; + case 6: + func_80AEFF94(this, globalCtx); + break; + case 10: + func_80AF0050(this, globalCtx); + break; + default: + Actor_Kill(&this->actor); + // "Relevant arge_data = %d unacceptable" + osSyncPrintf("該当 arge_data = %d 無し\n", func_80AEADF0(this)); + break; + } +} + +void func_80AF0278(EnRu1* this, GlobalContext* globalCtx, s32 limbIndex, Vec3s* rot) { + Vec3s* vec1 = &this->unk_374.unk_0E; + Vec3s* vec2 = &this->unk_374.unk_08; + + switch (limbIndex) { + case RUTO_CHILD_LEFT_UPPER_ARM: + rot->x += vec1->y; + rot->y -= vec1->x; + break; + case RUTO_CHILD_TORSO: + rot->x += vec2->y; + rot->z += vec2->x; + break; + } +} + +s32 EnRu1_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnRu1* this = (EnRu1*)thisx; + + if ((this->unk_290 < 0) || (this->unk_290 > 0) || (*sPreLimbDrawFuncs[this->unk_290] == NULL)) { + // "Neck rotation mode is improper!" + osSyncPrintf(VT_FGCOL(RED) "首回しモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sPreLimbDrawFuncs[this->unk_290](this, globalCtx, limbIndex, rot); + } + return false; +} + +void EnRu1_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnRu1* this = (EnRu1*)thisx; + Vec3f vec1; + Vec3f vec2; + + if (limbIndex == RUTO_CHILD_TORSO) { + vec1 = sMultVec; + Matrix_MultVec3f(&vec1, &vec2); + this->actor.focus.pos.x = vec2.x; + this->actor.focus.pos.y = vec2.y; + this->actor.focus.pos.z = vec2.z; + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + } +} + +void EnRu1_DrawNothing(EnRu1* this, GlobalContext* globalCtx) { +} + +void EnRu1_DrawOpa(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeIndex = this->eyeIndex; + void* eyeTex = sEyeTextures[eyeIndex]; + s16 mouthIndex = this->mouthIndex; + SkelAnime* skelAnime = &this->skelAnime; + void* mouthTex = sMouthTextures[mouthIndex]; + s32 pad1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ru1.c", 1282); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnRu1_OverrideLimbDraw, EnRu1_PostLimbDraw, this, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ru1.c", 1309); +} + +void EnRu1_DrawXlu(EnRu1* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeIndex = this->eyeIndex; + void* eyeTex = sEyeTextures[eyeIndex]; + s16 mouthIndex = this->mouthIndex; + SkelAnime* skelAnime = &this->skelAnime; + void* mouthTex = sMouthTextures[mouthIndex]; + s32 pad1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ru1.c", 1324); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, &D_80116280[0]); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnRu1_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ru1.c", 1353); +} + +void EnRu1_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnRu1* this = (EnRu1*)thisx; + + if (this->drawConfig < 0 || this->drawConfig >= ARRAY_COUNT(sDrawFuncs) || sDrawFuncs[this->drawConfig] == 0) { + // "Draw mode is improper!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.h b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.h new file mode 100644 index 000000000..7d6ed6269 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1.h @@ -0,0 +1,79 @@ +#ifndef Z_EN_RU1_H +#define Z_EN_RU1_H + +#include "ultra64.h" +#include "global.h" + +#include "overlays/actors/ovl_Bg_Bdan_Objects/z_bg_bdan_objects.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + +struct EnRu1; + +typedef void (*EnRu1ActionFunc)(struct EnRu1*, GlobalContext*); +typedef void (*EnRu1DrawFunc)(struct EnRu1*, GlobalContext*); +typedef void (*EnRu1PreLimbDrawFunc)(struct EnRu1*, GlobalContext*, s32, Vec3s*); + +typedef struct EnRu1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ s16 eyeIndex; + /* 0x025E */ s16 blinkTimer; + /* 0x0260 */ s16 mouthIndex; + /* 0x0264 */ s32 action; + /* 0x0268 */ s32 drawConfig; + /* 0x026C */ f32 unk_26C; + /* 0x0270 */ f32 unk_270; + /* 0x0274 */ char unk_274[0x4]; + /* 0x0278 */ DoorWarp1* blueWarp; + /* 0x027C */ f32 unk_27C; + /* 0x0280 */ s32 unk_280; + /* 0x0284 */ s8 roomNum1; + /* 0x0285 */ s8 roomNum2; + /* 0x0286 */ s8 roomNum3; + /* 0x0288 */ f32 unk_288; + /* 0x028C */ BgBdanObjects* unk_28C; + /* 0x0290 */ s32 unk_290; + /* 0x0294 */ char unk_294[0x4]; + /* 0x0298 */ s32 unk_298; + /* 0x029C */ char unk_29C[0x2]; + /* 0x029E */ s16 unk_29E; + /* 0x02A0 */ char unk_2A0[0x4]; + /* 0x02A4 */ f32 unk_2A4; + /* 0x02A8 */ s32 alpha; + /* 0x02AC */ s16 unk_2AC; + /* 0x02B0 */ s32 unk_2B0; + /* 0x02B4 */ ColliderCylinder collider; + /* 0x0300 */ ColliderCylinder collider2; + /* 0x034C */ s32 unk_34C; + /* 0x0350 */ s32 unk_350; + /* 0x0354 */ f32 unk_354; + /* 0x0358 */ f32 unk_358; + /* 0x035C */ s16 unk_35C; + /* 0x0360 */ f32 unk_360; + /* 0x0364 */ Vec3f unk_364; + /* 0x0370 */ f32 unk_370; + /* 0x0374 */ struct_80034A14_arg1 unk_374; +} EnRu1; // size = 0x039C + +typedef enum { + /* 0 */ RUTO_CHILD_ROOT, + /* 1 */ RUTO_CHILD_LEFT_THIGH, + /* 2 */ RUTO_CHILD_LEFT_SHIN, + /* 3 */ RUTO_CHILD_LEFT_FOOT, + /* 4 */ RUTO_CHILD_RIGHT_THIGH, + /* 5 */ RUTO_CHILD_RIGHT_SHIN, + /* 6 */ RUTO_CHILD_RIGHT_FOOT, + /* 7 */ RUTO_CHILD_CHEST, + /* 8 */ RUTO_CHILD_LEFT_UPPER_ARM, + /* 9 */ RUTO_CHILD_LEFT_FIN, + /* 10 */ RUTO_CHILD_LEFT_HAND, + /* 11 */ RUTO_CHILD_RIGHT_UPPER_ARM, + /* 12 */ RUTO_CHILD_RIGHT_FIN, + /* 13 */ RUTO_CHILD_RIGHT_HAND, + /* 14 */ RUTO_CHILD_HEAD, + /* 15 */ RUTO_CHILD_TORSO +} RutoLimb; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1_cutscene_data.c new file mode 100644 index 000000000..2ca6c77bb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ru1/z_en_ru1_cutscene_data.c @@ -0,0 +1,239 @@ +#include "z_en_ru1.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_80AF0880[] = { + CS_BEGIN_CUTSCENE(15, 1306), + CS_PLAYER_ACTION_LIST(10), + CS_PLAYER_ACTION(0x0027, 0, 50, 0x0000, 0x6C16, 0x0000, 12, -340, -2810, 12, -340, -2810, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0003, 50, 70, 0x0000, 0x6C16, 0x0000, 12, -340, -2810, 12, -340, -2810, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0005, 70, 109, 0x0000, 0x6C16, 0x0000, 12, -340, -2810, 12, -340, -2810, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0002, 109, 135, 0x0000, 0x6C16, 0x0000, 12, -340, -2810, 100, -340, -2991, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0005, 135, 199, 0x0000, 0x6C16, 0x0000, 100, -340, -2991, 100, -340, -2991, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0003, 199, 219, 0x0000, 0x6C16, 0x0000, 100, -340, -2991, 100, -340, -2991, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0005, 219, 259, 0x0000, 0x6C16, 0x0000, 100, -340, -2991, 100, -340, -2991, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0003, 259, 276, 0x0000, 0x6C16, 0x0000, 100, -340, -2991, 100, -340, -2991, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0002, 276, 297, 0x0000, 0x6C16, 0x0000, 100, -340, -2991, 182, -340, -3132, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0005, 297, 843, 0x0000, 0x6C16, 0x0000, 182, -340, -3132, 182, -340, -3132, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_NPC_ACTION_LIST(63, 4), + CS_NPC_ACTION(0x0002, 0, 178, 0x0000, 0xEC16, 0x0000, 127, -340, -3041, 127, -340, -3041, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_NPC_ACTION(0x0003, 178, 245, 0x0000, 0x6C16, 0x0000, 127, -340, -3041, 127, -340, -3041, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_NPC_ACTION(0x0004, 245, 260, 0x0000, 0x6C16, 0x0000, 127, -340, -3041, 127, -340, -3041, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_NPC_ACTION(0x0005, 260, 396, 0x0000, 0x6C16, 0x0000, 127, -340, -3041, 127, -340, -3041, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_MISC_LIST(1), + CS_MISC(0x000C, 315, 345, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC0, 0x00000032, 0x00000000, 0xFFFFFFC0, 0x00000032, 0x00000000, 0x00000000, 0x00000000), + CS_TEXT_LIST(4), + CS_TEXT_NONE(0, 157), + CS_TEXT_DISPLAY_TEXTBOX(0x404B, 157, 175, 0x0000, 0xFFFF, 0xFFFF), + CS_TEXT_NONE(175, 252), + CS_TEXT_DISPLAY_TEXTBOX(0x401E, 252, 300, 0x0000, 0xFFFF, 0xFFFF), + CS_CAM_EYE_LIST(0, 247), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 55, -290, -2749, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 55, -290, -2749, 0x44B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 55, -290, -2749, 0x8080), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 55, -290, -2749, 0xE243), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.799889f, 55, -290, -2749, 0x005E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.799889f, 55, -290, -2749, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.799889f, 55, -290, -2749, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.799889f, 55, -290, -2749, 0x5B80), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 30.799889f, 55, -290, -2749, 0x49B4), + CS_CAM_EYE_LIST(73, 208), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 99, -307, -2985, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 99, -307, -2985, 0x44B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 99, -307, -2985, 0x8080), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 99, -307, -2985, 0xE243), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 99, -307, -2985, 0x005E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.399944f, 99, -307, -2985, 0xFFFF), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.399944f, 99, -307, -2985, 0x0000), + CS_CAM_EYE_LIST(116, 1207), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.799946f, 155, -276, -2911, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.799946f, 155, -276, -2911, 0x44B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.799946f, 155, -276, -2911, 0x8080), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.799946f, 155, -276, -2911, 0xE243), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.799946f, 155, -276, -2911, 0x005E), + CS_CAM_EYE_LIST(186, 1277), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, 77, -315, -2992, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, 77, -315, -2992, 0x44B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, 77, -315, -2992, 0x8080), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.39995f, 77, -315, -2992, 0xE243), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.39995f, 77, -315, -2992, 0x005E), + CS_CAM_EYE_LIST(256, 425), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 28.199883f, 350, -237, -3314, 0x20BA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 28.199883f, 350, -237, -3314, 0x44B8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 28.199883f, 350, -237, -3314, 0x8080), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 28.199883f, 350, -237, -3314, 0xE243), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 46.799953f, 350, -237, -3314, 0x005E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 46.799953f, 350, -237, -3314, 0xFFFF), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 46.799953f, 350, -237, -3314, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 46.799953f, 350, -237, -3314, 0x5B80), + CS_CAM_AT_LIST(0, 276), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 45.399944f, 40, -256, -2823, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 45.399944f, 40, -256, -2823, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 45.399944f, 22, -313, -2823, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 45.399944f, 23, -313, -2823, 0xE243), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 6, 30.799889f, 73, -298, -2828, 0x005E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.799889f, 73, -298, -2828, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.799889f, 73, -298, -2828, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.799889f, 73, -298, -2828, 0x5B80), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 30.799889f, 73, -298, -2828, 0x49B4), + CS_CAM_AT_LIST(73, 237), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.2f, 136, -314, -3060, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 7, 60.600002f, 136, -314, -3060, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 7, 45.399944f, 136, -314, -3060, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 136, -314, -3060, 0xE243), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 136, -314, -3060, 0x005E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.399944f, 135, -313, -3060, 0xFFFF), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.399944f, 135, -313, -3059, 0x0000), + CS_CAM_AT_LIST(116, 1236), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.799946f, 132, -305, -2969, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.799946f, 132, -304, -2969, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.799946f, 133, -304, -2969, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.799946f, 133, -304, -2969, 0xE243), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.799946f, 133, -304, -2969, 0x005E), + CS_CAM_AT_LIST(186, 1306), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.39995f, 138, -301, -3032, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.39995f, 138, -301, -3032, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.39995f, 138, -301, -3032, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.39995f, 138, -301, -3032, 0xE243), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.39995f, 138, -301, -3032, 0x005E), + CS_CAM_AT_LIST(256, 454), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 28.199883f, 307, -254, -3258, 0x20BA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 28.199883f, 307, -254, -3258, 0x44B8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 9, 28.199883f, 307, -254, -3258, 0x8080), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 9, 46.799953f, 304, -273, -3273, 0xE243), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 46.799953f, 304, -272, -3274, 0x005E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 46.799953f, 304, -272, -3274, 0xFFFF), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 46.799953f, 304, -272, -3274, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 46.799953f, 305, -272, -3274, 0x5B80), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0052, 74, 75, 0x0000, 0x00000000, 0xFFFFFFF1, 0x00000000, 0x0000004E, 0xFFFFFFF1, 0x00000000, 0x0000004E), + CS_END(), +}; + +static u32 D_80AF10A0 = 0; + +static CutsceneData D_80AF10A4[] = { + CS_BEGIN_CUTSCENE(14, 1299), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x0005, 0, 272, 0x0000, 0xC000, 0x0000, -1085, -1025, -3347, -1085, -1025, -3347, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0003, 272, 292, 0x0000, 0xC000, 0x0000, -1085, -1025, -3347, -1085, -1025, -3347, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_PLAYER_ACTION(0x0005, 292, 777, 0x0000, 0xC000, 0x0000, -1085, -1025, -3347, -1085, -1025, -3347, 1.1393037E-29f, 0.0f, 1.4E-45f), + CS_MISC_LIST(1), + CS_MISC(0x000C, 330, 627, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC0, 0x00000032, 0x00000000, 0xFFFFFFC0, 0x00000032, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(66, 3), + CS_NPC_ACTION(0x0001, 0, 40, 0x0000, 0x4000, 0x0000, -1352, -969, -3341, -1352, -969, -3341, 0.0f, 0.0f, 1.4E-45f), + CS_NPC_ACTION(0x0002, 40, 213, 0x0000, 0x4000, 0x0000, -1352, -969, -3341, -1360, -969, -3343, 0.0f, 0.0f, 1.4E-45f), + CS_NPC_ACTION(0x0003, 213, 1000, 0x0000, 0x4000, 0x0000, -1360, -969, -3343, -1360, -969, -3343, 0.0f, 0.0f, 1.4E-45f), + CS_NPC_ACTION_LIST(48, 1), + CS_NPC_ACTION(0x0002, 0, 90, 0x0000, 0x0000, 0x0000, -1360, -963, -3343, -1360, -963, -3343, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(48, 2), + CS_NPC_ACTION(0x0002, 90, 211, 0x0000, 0x0000, 0x0000, -1352, -922, -3341, -1352, -922, -3341, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0006, 211, 311, 0x0000, 0x0000, 0x0000, -1352, -922, -3341, -1352, -922, -3341, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(62, 3), + CS_NPC_ACTION(0x0004, 0, 210, 0x0000, 0x0000, 0x0000, -1065, -972, -3305, -1065, -978, -3305, 0.0f, -0.028571429f, 0.0f), + CS_NPC_ACTION(0x0004, 210, 220, 0x8000, 0x0000, 0x0000, -1065, -978, -3305, -1065, -973, -3344, 0.0f, 0.5f, 0.0f), + CS_NPC_ACTION(0x0004, 220, 410, 0x0000, 0x0000, 0x0000, -1065, -973, -3344, -1065, -976, -3344, 0.0f, -0.015789473f, 0.0f), + CS_TEXT_LIST(6), + CS_TEXT_NONE(0, 162), + CS_TEXT_DISPLAY_TEXTBOX(0x4050, 162, 211, 0x0000, 0xFFFF, 0xFFFF), + CS_TEXT_NONE(211, 232), + CS_TEXT_DISPLAY_TEXTBOX(0x4051, 232, 241, 0x0000, 0xFFFF, 0xFFFF), + CS_TEXT_NONE(241, 247), + CS_TEXT_DISPLAY_TEXTBOX(0x4052, 247, 299, 0x0000, 0xFFFF, 0xFFFF), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0023, 112, 113, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC7, 0x000000B1, 0x00000000, 0xFFFFFFC7, 0x000000B1), + CS_CAM_EYE_LIST(0, 1176), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 41.9066f, -1390, -948, -3339, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.706596f, -1390, -948, -3339, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.706596f, -1390, -948, -3339, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.706596f, -1418, -938, -3337, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.10661f, -1418, -938, -3337, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.10661f, -1418, -938, -3337, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.10661f, -1418, -938, -3337, 0x013F), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.10661f, -1418, -938, -3337, 0x006D), + CS_CAM_EYE_LIST(91, 1270), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 44.906612f, -1319, -934, -3343, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 44.70661f, -1319, -936, -3344, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 44.70661f, -1319, -936, -3344, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 44.70661f, -1319, -936, -3344, 0x00E8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 44.70661f, -1326, -904, -3342, 0x00EA), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.906673f, -1326, -904, -3342, 0x013D), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.906673f, -1326, -904, -3342, 0x013F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.906673f, -1326, -904, -3342, 0x014E), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.906673f, -1326, -904, -3342, 0x015F), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.906673f, -1326, -904, -3342, 0x0161), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.906673f, -1326, -1024, -3342, 0x652E), + CS_CAM_EYE_LIST(211, 332), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.306555f, -1471, -819, -3149, 0x00C6), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.306555f, -1471, -819, -3149, 0x00C8), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.306555f, -1471, -819, -3149, 0x00D7), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 30.306555f, -1471, -819, -3149, 0x00E8), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 30.306555f, -1471, -819, -3149, 0x00EA), + CS_CAM_AT_LIST(0, 1205), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.706596f, -1295, -1003, -3352, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 40.706596f, -1296, -1003, -3352, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 40.706596f, -1296, -1003, -3352, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 45.10661f, -1314, -969, -3346, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.10661f, -1313, -970, -3346, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.10661f, -1313, -969, -3346, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.10661f, -1313, -970, -3346, 0x013F), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.10661f, -1313, -970, -3346, 0x006D), + CS_CAM_AT_LIST(91, 1299), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 44.70661f, -1405, -988, -3343, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 7, 44.70661f, -1406, -989, -3344, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 7, 44.70661f, -1406, -989, -3344, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 7, 44.70661f, -1406, -989, -3344, 0x00E8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 7, 60.906673f, -1393, -978, -3342, 0x00EA), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.906673f, -1393, -977, -3342, 0x013D), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.906673f, -1393, -977, -3342, 0x013F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 60.906673f, -1393, -977, -3342, 0x014E), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.906673f, -1393, -977, -3342, 0x015F), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.906673f, -1393, -977, -3342, 0x0161), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.906673f, -1401, -1094, -3347, 0x652E), + CS_CAM_AT_LIST(211, 361), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.306555f, -1426, -857, -3190, 0x00C6), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.306555f, -1426, -857, -3190, 0x00C8), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.306555f, -1426, -857, -3190, 0x00D7), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 30.306555f, -1426, -857, -3190, 0x00E8), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 30.306555f, -1426, -857, -3190, 0x00EA), + CS_END(), +}; + +static u32 D_80AF1724 = 0; + +static CutsceneData D_80AF1728[] = { + CS_BEGIN_CUTSCENE(7, 1160), + CS_MISC_LIST(1), + CS_MISC(0x000C, 75, 627, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC0, 0x00000032, 0x00000000, 0xFFFFFFC0, 0x00000032, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(48, 1), + CS_NPC_ACTION(0x0002, 0, 90, 0x0000, 0x0000, 0x0000, -1360, -963, -3343, -1360, -963, -3343, 0.0f, 0.0f, 0.0f), + CS_CAM_EYE_LIST(0, 1091), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.599983f, -1381, -958, -3331, 0x8BC0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.599983f, -1381, -958, -3331, 0x2200), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.599983f, -1381, -958, -3331, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 40.599983f, -1381, -958, -3331, 0xD0E8), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 40.599983f, -1381, -958, -3331, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER_LIST(40, 1131), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 50.800022f, 19, 40, 53, 0x8BC0), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 50.800022f, 19, 40, 53, 0x2200), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 50.800022f, 19, 40, 53, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 50.800022f, 19, 40, 53, 0xD0E8), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 50.800022f, 19, 40, 53, 0x0000), + CS_CAM_AT_LIST(0, 1120), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.599983f, -1224, -979, -3366, 0x8BC0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.599983f, -1224, -979, -3366, 0x2200), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 40.599983f, -1224, -979, -3366, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 40.599983f, -1224, -979, -3366, 0xD0E8), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 40.599983f, -1224, -979, -3366, 0x0000), + CS_CAM_AT_REL_TO_PLAYER_LIST(40, 1160), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 50.800022f, -35, 56, -93, 0x8BC0), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 50.800022f, -35, 56, -93, 0x2200), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 50.800022f, -35, 56, -93, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 50.800022f, -35, 56, -93, 0xD0E8), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 50.800022f, -35, 56, -93, 0x0000), + CS_TEXT_LIST(2), + CS_TEXT_NONE(0, 35), + CS_TEXT_DISPLAY_TEXTBOX(0x404F, 35, 70, 0x0000, 0xFFFF, 0xFFFF), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c new file mode 100644 index 000000000..54cc44531 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c @@ -0,0 +1,828 @@ +/* + * File: z_en_ru2.c + * Overlay: En_Ru2 + * Description: Adult Ruto + */ + +#include "z_en_ru2.h" +#include "objects/object_ru2/object_ru2.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnRu2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnRu2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnRu2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnRu2_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AF2CB4(EnRu2* this, GlobalContext* globalCtx); +void func_80AF2CD4(EnRu2* this, GlobalContext* globalCtx); +void func_80AF2CF4(EnRu2* this, GlobalContext* globalCtx); +void func_80AF2D2C(EnRu2* this, GlobalContext* globalCtx); +void func_80AF2D6C(EnRu2* this, GlobalContext* globalCtx); +void func_80AF2DAC(EnRu2* this, GlobalContext* globalCtx); +void func_80AF2DEC(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3144(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3174(EnRu2* this, GlobalContext* globalCtx); +void func_80AF31C8(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3604(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3624(EnRu2* this, GlobalContext* globalCtx); +void func_80AF366C(EnRu2* this, GlobalContext* globalCtx); +void func_80AF36AC(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3BC8(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3C04(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3C64(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3CB8(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3D0C(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3D60(EnRu2* this, GlobalContext* globalCtx); + +void func_80AF3F14(EnRu2* this, GlobalContext* globalCtx); +void func_80AF3F20(EnRu2* this, GlobalContext* globalCtx); +void func_80AF321C(EnRu2* this, GlobalContext* globalCtx); + +void func_80AF2AB4(EnRu2* this, GlobalContext* globalCtx); + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0x00000000, 0x00, 0x00 }, { 0x00000080, 0x00, 0x00 }, 0x00, 0x01, 0x00 }, + { 30, 100, 0, { 0 } }, +}; + +static void* sEyeTextures[] = { + gAdultRutoEyeOpenTex, + gAdultRutoEyeHalfTex, + gAdultRutoEyeClosedTex, +}; + +static UNK_TYPE D_80AF4118 = 0; + +#include "z_en_ru2_cutscene_data.c" EARLY + +static EnRu2ActionFunc sActionFuncs[] = { + func_80AF2CB4, func_80AF2CD4, func_80AF2CF4, func_80AF2D2C, func_80AF2D6C, func_80AF2DAC, func_80AF2DEC, + func_80AF3144, func_80AF3174, func_80AF31C8, func_80AF3604, func_80AF3624, func_80AF366C, func_80AF36AC, + func_80AF3BC8, func_80AF3C04, func_80AF3C64, func_80AF3CB8, func_80AF3D0C, func_80AF3D60, +}; + +static EnRu2DrawFunc sDrawFuncs[] = { + func_80AF3F14, + func_80AF3F20, + func_80AF321C, +}; + +const ActorInit En_Ru2_InitVars = { + ACTOR_EN_RU2, + ACTORCAT_NPC, + FLAGS, + OBJECT_RU2, + sizeof(EnRu2), + (ActorFunc)EnRu2_Init, + (ActorFunc)EnRu2_Destroy, + (ActorFunc)EnRu2_Update, + (ActorFunc)EnRu2_Draw, + NULL, +}; + +void func_80AF2550(Actor* thisx, GlobalContext* globalCtx) { + EnRu2* this = (EnRu2*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); +} + +void func_80AF259C(EnRu2* this, GlobalContext* globalCtx) { + s32 pad[5]; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnRu2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnRu2* this = (EnRu2*)thisx; + D_80AF4118 = 0; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AF2608(EnRu2* this) { + s32 pad[3]; + s16* unk_2A6 = &this->unk_2A6; + s16* unk_2A4 = &this->unk_2A4; + + if (!DECR(*unk_2A6)) { + *unk_2A6 = Rand_S16Offset(0x3C, 0x3C); + } + + *unk_2A4 = *unk_2A6; + if (*unk_2A4 >= 3) { + *unk_2A4 = 0; + } +} + +s32 func_80AF2690(EnRu2* this) { + s32 params_shift = this->actor.params >> 8; + + return params_shift & 0xFF; +} + +s32 func_80AF26A0(EnRu2* this) { + s16 params = this->actor.params; + + return params & 0xFF; +} + +void func_80AF26AC(EnRu2* this) { + this->action = 7; + this->drawConfig = 0; + this->alpha = 0; + this->unk_2B8 = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_2B0 = 0.0f; +} + +void func_80AF26D0(EnRu2* this, GlobalContext* globalCtx) { + s32 one; // Needed to match + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + if (D_80AF4118 != 0) { + if (this->actor.params == 2) { + func_80AF26AC(this); + } + D_80AF4118 = 0; + return; + } + } else { + one = 1; + if (D_80AF4118 == 0) { + D_80AF4118 = one; + } + } +} + +void func_80AF2744(EnRu2* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 4); +} + +s32 EnRu2_UpdateSkelAnime(EnRu2* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* func_80AF27AC(GlobalContext* globalCtx, s32 npcActionIdx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[npcActionIdx]; + } + return NULL; +} + +s32 func_80AF27D0(EnRu2* this, GlobalContext* globalCtx, u16 arg2, s32 npcActionIdx) { + CsCmdActorAction* csCmdActorAction = func_80AF27AC(globalCtx, npcActionIdx); + + if ((csCmdActorAction != NULL) && (csCmdActorAction->action == arg2)) { + return true; + } + return false; +} + +s32 func_80AF281C(EnRu2* this, GlobalContext* globalCtx, u16 arg2, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = func_80AF27AC(globalCtx, npcActionIdx); + + if ((csCmdNPCAction != NULL) && (csCmdNPCAction->action != arg2)) { + return true; + } + return false; +} + +void func_80AF2868(EnRu2* this, GlobalContext* globalCtx, u32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = func_80AF27AC(globalCtx, npcActionIdx); + s16 newRotY; + Actor* thisx = &this->actor; + + if (csCmdNPCAction != NULL) { + thisx->world.pos.x = csCmdNPCAction->startPos.x; + thisx->world.pos.y = csCmdNPCAction->startPos.y; + thisx->world.pos.z = csCmdNPCAction->startPos.z; + newRotY = csCmdNPCAction->rot.y; + thisx->shape.rot.y = newRotY; + thisx->world.rot.y = newRotY; + } +} + +void func_80AF28E8(EnRu2* this, AnimationHeader* animation, u8 arg2, f32 transitionRate, s32 arg4) { + f32 frameCount = Animation_GetLastFrame(animation); + f32 playbackSpeed; + f32 unk0; + f32 fc; + + if (arg4 == 0) { + unk0 = 0.0f; + fc = frameCount; + playbackSpeed = 1.0f; + } else { + unk0 = frameCount; + fc = 0.0f; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animation, playbackSpeed, unk0, fc, arg2, transitionRate); +} + +void func_80AF2978(EnRu2* this, GlobalContext* globalCtx) { + this->actor.shape.yOffset += 250.0f / 3.0f; +} + +void func_80AF2994(EnRu2* this, GlobalContext* globalCtx) { + func_80AF28E8(this, &gAdultRutoIdleAnim, 0, 0.0f, 0); + this->actor.shape.yOffset = -10000.0f; +} + +void func_80AF29DC(EnRu2* this, GlobalContext* globalCtx) { + Actor* thisx = &this->actor; + f32 posX = thisx->world.pos.x; + f32 posY = thisx->world.pos.y; + f32 posZ = thisx->world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, + WARP_SAGES); +} + +void func_80AF2A38(EnRu2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 posX = player->actor.world.pos.x; + f32 posY = player->actor.world.pos.y + 50.0f; + f32 posZ = player->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, 10); + Item_Give(globalCtx, ITEM_MEDALLION_WATER); +} + +void func_80AF2AB4(EnRu2* this, GlobalContext* globalCtx) { + s32 pad[2]; + Player* player; + s16 temp; + + if ((gSaveContext.chamberCutsceneNum == 2) && (gSaveContext.sceneSetupIndex < 4)) { + player = GET_PLAYER(globalCtx); + this->action = 1; + globalCtx->csCtx.segment = &D_80AF411C; + gSaveContext.cutsceneTrigger = 2; + Item_Give(globalCtx, ITEM_MEDALLION_WATER); + temp = this->actor.world.rot.y + 0x8000; + player->actor.shape.rot.y = temp; + player->actor.world.rot.y = temp; + } +} + +void func_80AF2B44(EnRu2* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + CsCmdActorAction* csCmdNPCAction; + + if (csCtx->state != CS_STATE_IDLE) { + csCmdNPCAction = csCtx->npcActions[3]; + if ((csCmdNPCAction != NULL) && (csCmdNPCAction->action == 2)) { + this->action = 2; + this->drawConfig = 1; + func_80AF29DC(this, globalCtx); + } + } +} + +void func_80AF2B94(EnRu2* this) { + if (this->actor.shape.yOffset >= 0.0f) { + this->action = 3; + this->actor.shape.yOffset = 0.0f; + } +} + +void func_80AF2BC0(EnRu2* this, GlobalContext* globalCtx) { + AnimationHeader* animation = &gAdultRutoRaisingArmsUpAnim; + CsCmdActorAction* csCmdNPCAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdNPCAction = globalCtx->csCtx.npcActions[3]; + if ((csCmdNPCAction != NULL) && (csCmdNPCAction->action == 3)) { + Animation_Change(&this->skelAnime, animation, 1.0f, 0.0f, Animation_GetLastFrame(animation), ANIMMODE_ONCE, + 0.0f); + this->action = 4; + } + } +} + +void func_80AF2C54(EnRu2* this, s32 arg1) { + if (arg1 != 0) { + this->action = 5; + } +} + +void func_80AF2C68(EnRu2* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + csCmdNPCAction = globalCtx->csCtx.npcActions[6]; + if ((csCmdNPCAction != NULL) && (csCmdNPCAction->action == 2)) { + this->action = 6; + func_80AF2A38(this, globalCtx); + } + } +} + +void func_80AF2CB4(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2AB4(this, globalCtx); +} + +void func_80AF2CD4(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2B44(this, globalCtx); +} + +void func_80AF2CF4(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2978(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF2B94(this); +} + +void func_80AF2D2C(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF2BC0(this, globalCtx); +} + +void func_80AF2D6C(EnRu2* this, GlobalContext* globalCtx) { + s32 something; + + func_80AF2744(this, globalCtx); + something = EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF2C54(this, something); +} + +void func_80AF2DAC(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF2C68(this, globalCtx); +} + +void func_80AF2DEC(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); +} + +void func_80AF2E1C(EnRu2* this, GlobalContext* globalCtx) { + func_80AF28E8(this, &gAdultRutoCrossingArmsAnim, 2, 0.0f, 0); + this->action = 7; + this->actor.shape.shadowAlpha = 0; +} + +void func_80AF2E64() { + func_800788CC(NA_SE_SY_WHITE_OUT_T); +} + +void func_80AF2E84(EnRu2* this, GlobalContext* globalCtx) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, this->actor.world.pos.x, + kREG(19) + 24.0f + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 8); +} + +void func_80AF2F04(EnRu2* this, GlobalContext* globalCtx) { + if (func_80AF27D0(this, globalCtx, 4, 3)) { + this->action = 8; + this->drawConfig = 2; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + this->unk_2B0 = 0.0f; + func_80AF2E64(); + } +} + +void func_80AF2F58(EnRu2* this, GlobalContext* globalCtx) { + f32* unk_2B0 = &this->unk_2B0; + s32 alpha; + + if (func_80AF27D0(this, globalCtx, 4, 3)) { + *unk_2B0 += 1.0f; + if (*unk_2B0 >= kREG(5) + 10.0f) { + this->action = 9; + this->drawConfig = 1; + *unk_2B0 = kREG(5) + 10.0f; + this->alpha = 255; + this->actor.shape.shadowAlpha = 0xFF; + return; + } + } else { + *unk_2B0 -= 1.0f; + if (*unk_2B0 <= 0.0f) { + this->action = 7; + this->drawConfig = 0; + *unk_2B0 = 0.0f; + this->alpha = 0; + this->actor.shape.shadowAlpha = 0; + return; + } + } + alpha = (*unk_2B0 / (kREG(5) + 10.0f)) * 255.0f; + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; +} + +void func_80AF30AC(EnRu2* this, GlobalContext* globalCtx) { + if (func_80AF281C(this, globalCtx, 4, 3)) { + this->action = 8; + this->drawConfig = 2; + this->unk_2B0 = kREG(5) + 10.0f; + this->alpha = 255; + if (this->unk_2B8 == 0) { + func_80AF2E84(this, globalCtx); + this->unk_2B8 = 1; + } + this->actor.shape.shadowAlpha = 0xFF; + } +} + +void func_80AF3144(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2F04(this, globalCtx); + func_80AF26D0(this, globalCtx); +} + +void func_80AF3174(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF2F58(this, globalCtx); + func_80AF26D0(this, globalCtx); +} + +void func_80AF31C8(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF30AC(this, globalCtx); + func_80AF26D0(this, globalCtx); +} + +void func_80AF321C(EnRu2* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 temp = this->unk_2A4; + void* tex = sEyeTextures[temp]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ru2_inKenjyanomaDemo02.c", 264); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(tex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(tex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, &D_80116280[0]); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + NULL, NULL, NULL, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ru2_inKenjyanomaDemo02.c", 291); +} + +void func_80AF3394(EnRu2* this, GlobalContext* globalCtx) { + func_80AF28E8(this, &gAdultRutoIdleHandsOnHipsAnim, 0, 0.0f, 0); + this->action = 10; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80AF33E0(EnRu2* this) { + f32* unk_2B0 = &this->unk_2B0; + f32 temp_f0; + s32 temp_f18; + + *unk_2B0 += 1.0f; + + temp_f0 = kREG(17) + 10.0f; + if (temp_f0 <= *unk_2B0) { + this->alpha = 255; + this->actor.shape.shadowAlpha = 0xFF; + } else { + temp_f18 = (*unk_2B0 / temp_f0) * 255.0f; + this->alpha = temp_f18; + this->actor.shape.shadowAlpha = temp_f18; + } +} + +void func_80AF346C(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2868(this, globalCtx, 3); + this->action = 11; + this->drawConfig = 2; +} + +void func_80AF34A4(EnRu2* this) { + if (this->unk_2B0 >= kREG(17) + 10.0f) { + this->action = 12; + this->drawConfig = 1; + } +} + +void func_80AF34F0(EnRu2* this) { + func_80AF28E8(this, &gAdultRutoHeadTurnDownLeftAnim, 2, 0.0f, 0); + this->action = 13; +} + +void func_80AF3530(EnRu2* this, s32 arg1) { + if (arg1 != 0) { + func_80AF28E8(this, &gAdultRutoLookingDownLeftAnim, 0, 0.0f, 0); + } +} + +void func_80AF3564(EnRu2* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction = func_80AF27AC(globalCtx, 3); + s32 action; + s32 unk_2BC; + + if (csCmdNPCAction != NULL) { + action = csCmdNPCAction->action; + unk_2BC = this->unk_2BC; + if (action != unk_2BC) { + switch (action) { + case 7: + func_80AF346C(this, globalCtx); + break; + case 8: + func_80AF34F0(this); + break; + default: + // "There is no such action!" + osSyncPrintf("En_Ru2_inEnding_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->unk_2BC = action; + } + } +} + +void func_80AF3604(EnRu2* this, GlobalContext* globalCtx) { + func_80AF3564(this, globalCtx); +} + +void func_80AF3624(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF33E0(this); + func_80AF34A4(this); +} + +void func_80AF366C(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF3564(this, globalCtx); +} + +void func_80AF36AC(EnRu2* this, GlobalContext* globalCtx) { + s32 something; + + func_80AF2744(this, globalCtx); + something = EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + func_80AF3530(this, something); +} + +void func_80AF36EC(EnRu2* this, GlobalContext* globalCtx) { + Flags_SetSwitch(globalCtx, func_80AF2690(this)); +} + +s32 func_80AF3718(EnRu2* this, GlobalContext* globalCtx) { + return Flags_GetSwitch(globalCtx, func_80AF2690(this)); +} + +void func_80AF3744(EnRu2* this, GlobalContext* globalCtx) { + if (func_80AF3718(this, globalCtx)) { + Actor_Kill(&this->actor); + } else { + func_80AF28E8(this, &gAdultRutoIdleAnim, 0, 0.0f, 0); + this->action = 14; + this->drawConfig = 1; + } +} + +void func_80AF37AC(void) { + Audio_PlayFanfare(NA_BGM_APPEAR); +} + +void func_80AF37CC(EnRu2* this) { + f32 funcFloat; + + this->unk_2C0++; + funcFloat = Environment_LerpWeightAccelDecel((kREG(2) + 0x96) & 0xFFFF, 0, this->unk_2C0, 8, 0); + this->actor.world.pos.y = this->actor.home.pos.y + (300.0f * funcFloat); +} + +s32 func_80AF383C(EnRu2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 thisPosX = this->actor.world.pos.x; + f32 playerPosX = player->actor.world.pos.x; + + if (playerPosX - thisPosX >= -202.0f) { + return 1; + } + return 0; +} + +void func_80AF3878(EnRu2* this, GlobalContext* globalCtx) { + if (func_80AF383C(this, globalCtx) && !Gameplay_InCsMode(globalCtx)) { + this->action = 16; + OnePointCutscene_Init(globalCtx, 3130, -99, &this->actor, MAIN_CAM); + } +} + +void func_80AF38D0(EnRu2* this, GlobalContext* globalCtx) { + this->action = 16; + OnePointCutscene_Init(globalCtx, 3130, -99, &this->actor, MAIN_CAM); +} + +void func_80AF390C(EnRu2* this, GlobalContext* globalCtx) { + f32* unk_2C4 = &this->unk_2C4; + + *unk_2C4 += 1.0f; + if (*unk_2C4 == kREG(6) + 40.0f) { + func_80AF37AC(); + } else if (*unk_2C4 > kREG(4) + 50.0f) { + this->actor.textId = 0x403E; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->action = 17; + } +} + +void func_80AF39DC(EnRu2* this, GlobalContext* globalCtx) { + s32 pad; + MessageContext* msgCtx; + s32 pad2; + u8 dialogState; + Player* player; + s32 pad3; + + msgCtx = &globalCtx->msgCtx; + dialogState = Message_GetState(msgCtx); + + if (dialogState == TEXT_STATE_DONE_FADING) { + if (this->unk_2C3 != TEXT_STATE_DONE_FADING) { + // "I'm Komatsu!" (cinema scene dev) + osSyncPrintf("おれが小松だ! \n"); + this->unk_2C2++; + if (this->unk_2C2 % 6 == 3) { + player = GET_PLAYER(globalCtx); + // "uorya-!" (screeming sound) + osSyncPrintf("うおりゃー! \n"); + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + player->actor.world.pos.x = 820.0f; + player->actor.world.pos.y = 0.0f; + player->actor.world.pos.z = 180.0f; + } + } + } + + this->unk_2C3 = dialogState; + if (Message_GetState(msgCtx) == TEXT_STATE_CLOSING) { + this->action = 18; + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + } +} + +void func_80AF3ADC(EnRu2* this, GlobalContext* globalCtx) { + this->unk_2C4 += 1.0f; + if (this->unk_2C4 > kREG(5) + 100.0f) { + func_80AF28E8(this, &gAdultRutoSwimmingUpAnim, 0, -12.0f, 0); + this->action = 19; + func_80AF36EC(this, globalCtx); + } +} + +void func_80AF3B74(EnRu2* this, GlobalContext* globalCtx) { + if (this->unk_2C0 > ((((u16)(kREG(3) + 0x28)) + ((u16)(kREG(2) + 0x96))) & 0xFFFF)) { + Actor_Kill(&this->actor); + } +} + +void func_80AF3BC8(EnRu2* this, GlobalContext* globalCtx) { + func_80AF3878(this, globalCtx); + Actor_SetFocus(&this->actor, 50.0f); + func_80AF259C(this, globalCtx); +} + +void func_80AF3C04(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + func_80AF259C(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + Actor_SetFocus(&this->actor, 50.0f); + func_80AF38D0(this, globalCtx); +} + +void func_80AF3C64(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + Actor_SetFocus(&this->actor, 50.0f); + func_80AF390C(this, globalCtx); +} + +void func_80AF3CB8(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + Actor_SetFocus(&this->actor, 50.0f); + func_80AF39DC(this, globalCtx); +} + +void func_80AF3D0C(EnRu2* this, GlobalContext* globalCtx) { + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + Actor_SetFocus(&this->actor, 50.0f); + func_80AF3ADC(this, globalCtx); +} + +void func_80AF3D60(EnRu2* this, GlobalContext* globalCtx) { + func_80AF37CC(this); + func_80AF2744(this, globalCtx); + EnRu2_UpdateSkelAnime(this); + func_80AF2608(this); + Actor_SetFocus(&this->actor, 50.0f); + func_80AF3B74(this, globalCtx); +} + +void EnRu2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnRu2* this = (EnRu2*)thisx; + + if ((this->action < 0) || (this->action >= ARRAY_COUNT(sActionFuncs)) || (sActionFuncs[this->action] == NULL)) { + // "Main Mode is improper!" + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void EnRu2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnRu2* this = (EnRu2*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + func_80AF2550(thisx, globalCtx); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gAdultRutoSkel, NULL, this->jointTable, this->morphTable, 23); + + switch (func_80AF26A0(this)) { + case 2: + func_80AF2E1C(this, globalCtx); + break; + case 3: + func_80AF3394(this, globalCtx); + break; + case 4: + func_80AF3744(this, globalCtx); + break; + default: + func_80AF2994(this, globalCtx); + break; + } + + this->unk_2C2 = 0; + this->unk_2C3 = TEXT_STATE_DONE_FADING; +} + +void func_80AF3F14(EnRu2* this, GlobalContext* globalCtx) { +} + +void func_80AF3F20(EnRu2* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 temp = this->unk_2A4; + void* tex = sEyeTextures[temp]; + SkelAnime* skelAnime = &this->skelAnime; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ru2.c", 642); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(tex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(tex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, NULL, NULL, + this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ru2.c", 663); +} + +void EnRu2_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnRu2* this = (EnRu2*)thisx; + + if ((this->drawConfig < 0) || (this->drawConfig >= ARRAY_COUNT(sDrawFuncs)) || + (sDrawFuncs[this->drawConfig] == 0)) { + // "Draw Mode is improper!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.h b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.h new file mode 100644 index 000000000..bb3865ce6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.h @@ -0,0 +1,32 @@ +#ifndef Z_EN_RU2_H +#define Z_EN_RU2_H + +#include "ultra64.h" +#include "global.h" + +struct EnRu2; + +typedef void (*EnRu2ActionFunc)(struct EnRu2*, GlobalContext*); +typedef void (*EnRu2DrawFunc)(struct EnRu2*, GlobalContext*); + +typedef struct EnRu2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[23]; + /* 0x021A */ Vec3s morphTable[23]; + /* 0x02A4 */ s16 unk_2A4; + /* 0x02A6 */ s16 unk_2A6; + /* 0x02A8 */ s32 action; + /* 0x02AC */ s32 drawConfig; + /* 0x02B0 */ f32 unk_2B0; + /* 0x02B4 */ u32 alpha; + /* 0x02B8 */ s32 unk_2B8; + /* 0x02BC */ s32 unk_2BC; + /* 0x02C0 */ u16 unk_2C0; + /* 0x02C2 */ u8 unk_2C2; + /* 0x02C3 */ u8 unk_2C3; + /* 0x02C4 */ f32 unk_2C4; + /* 0x02C8 */ ColliderCylinder collider; +} EnRu2; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2_cutscene_data.c new file mode 100644 index 000000000..e265d10a9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2_cutscene_data.c @@ -0,0 +1,224 @@ +#include "z_en_ru2.h" +#include "z64cutscene_commands.h" + +// clang-format off +static CutsceneData D_80AF411C[] = { + CS_BEGIN_CUTSCENE(35, 3338), + CS_UNK_DATA_LIST(0x00000020, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0xFFFFFFFC, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(31, 5), + CS_NPC_ACTION(0x0001, 0, 829, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 829, 830, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 830, 898, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 216, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 898, 948, 0x0000, 0x0000, 0x0000, 0, 216, 0, 0, 82, 0, 0.0f, -2.68f, 0.0f), + CS_NPC_ACTION(0x0003, 948, 3338, 0x0000, 0x0000, 0x0000, 0, 82, 0, 0, 82, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION_LIST(5), + CS_PLAYER_ACTION(0x000D, 0, 240, 0x0000, 0x0000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0005, 240, 520, 0x0000, 0x4000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0003, 520, 550, 0x0000, 0x4000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0005, 550, 801, 0x0000, 0x4000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_PLAYER_ACTION(0x0013, 801, 1956, 0x0000, 0xC000, 0x0000, 0, 6, 0, 0, 6, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(49, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, -16, -121, 0, -16, -121, 0.0f, 0.0f, 0.0f), + CS_LIGHTING_LIST(5), + CS_LIGHTING(0x0005, 0, 384, 0x0000, 0x00000000, 0xFFFFFFA1, 0x00000000, 0x00000058, 0xFFFFFFA1, 0x00000000, 0x00000058), + CS_LIGHTING(0x0006, 384, 454, 0x0000, 0x00000000, 0xFFFFFFA1, 0x00000000, 0x00000058, 0xFFFFFFA1, 0x00000000, 0x00000058), + CS_LIGHTING(0x0005, 454, 554, 0x0000, 0x00000000, 0xFFFFFFA1, 0x00000000, 0x00000058, 0xFFFFFFA1, 0x00000000, 0x00000058), + CS_LIGHTING(0x0006, 554, 624, 0x0000, 0x00000000, 0xFFFFFFA1, 0x00000000, 0x00000058, 0xFFFFFFA1, 0x00000000, 0x00000058), + CS_LIGHTING(0x0005, 624, 3001, 0x0000, 0x00000000, 0xFFFFFFA1, 0x00000000, 0x00000058, 0xFFFFFFA1, 0x00000000, 0x00000058), + CS_NPC_ACTION_LIST(39, 1), + CS_NPC_ACTION(0x0001, 0, 3000, 0x0000, 0x0000, 0x0000, 0, 0, -2, 0, 0, -2, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION_LIST(42, 3), + CS_NPC_ACTION(0x0001, 0, 146, 0x0000, 0x0000, 0x0000, 195, 6, 0, 195, 6, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0002, 146, 754, 0x0000, 0x0000, 0x0000, 195, 6, 0, 195, 6, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0003, 754, 2628, 0x0000, 0x0000, 0x0000, 195, 6, 0, 195, 6, 0, 0.0f, 0.0f, 0.0f), + CS_SCENE_TRANS_FX(0x0001, 810, 823), + CS_SCENE_TRANS_FX(0x0005, 825, 855), + CS_SCENE_TRANS_FX(0x0001, 1090, 1121), + CS_CAM_EYE_LIST(0, 301), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 175.39832f, -617, 30, 71, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 175.39832f, -617, 30, 71, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 175.39832f, -617, 30, 71, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 175.39832f, -617, 30, 71, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 175.39832f, -617, 30, 71, 0x1F98), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 175.39832f, -617, 30, 71, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 145.79877f, -456, 107, 56, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -76, 54, 71, 0x0164), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 116, 54, 96, 0x2100), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 236, 11, 64, 0x0049), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 236, 11, 64, 0x204C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, 236, 11, 64, 0xE990), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.600002f, 236, 11, 64, 0x0000), + CS_CAM_EYE_LIST(263, 1484), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 161, 53, 26, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 161, 53, 26, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 161, 53, 26, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 130, 9, 96, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 130, 9, 96, 0x1F98), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 130, 9, 96, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 42.22703f, 130, 9, 96, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 42.22703f, 130, 9, 96, 0x0164), + CS_CAM_EYE_LIST(383, 1474), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 70.22702f, 207, 45, 34, 0x1F98), + CS_CAM_EYE_LIST(453, 1544), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.027042f, -58, 90, 70, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.027042f, -58, 90, 70, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.027042f, -58, 90, 70, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.027042f, -58, 90, 70, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.027042f, -58, 90, 70, 0x1F98), + CS_CAM_EYE_LIST(553, 1644), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.22702f, 207, 45, 34, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 70.22702f, 207, 45, 34, 0x1F98), + CS_CAM_EYE_LIST(623, 1714), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 161, 59, 0, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 161, 59, 0, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 161, 59, 0, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 161, 59, 0, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.4271f, 161, 59, 0, 0x1F98), + CS_CAM_EYE_LIST(693, 1784), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 151, 82, 35, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 151, 82, 35, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 151, 82, 35, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 151, 82, 35, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.4271f, 151, 82, 35, 0x1F98), + CS_CAM_EYE_LIST(763, 944), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 230, 7, 24, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 230, 7, 24, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 230, 7, 24, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.4271f, 207, 62, 15, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.42702f, 230, 240, 24, 0x1F98), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.42702f, 230, 240, 24, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 70.42702f, 230, 240, 24, 0x0000), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 70.42702f, 230, 240, 24, 0x0164), + CS_CAM_EYE_LIST(823, 1165), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 13, 854, 2, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 853, 5, 0x1FBC), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -3, 853, 5, 0xD5E0), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -9, 853, -6, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -2, 852, -17, 0x1F98), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, -17, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 16, 852, -6, 0x0000), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, 9, 852, 5, 0x0164), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -3, 851, 5, 0x2100), + CS_CAM_EYE_REL_TO_PLAYER_LIST(899, 2080), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 33, -27, 0x1FBC), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 68, -26, 0xD5E0), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x1F98), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 0, 68.599945f, 0, 103, -26, 0x0000), + CS_CAM_EYE_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 0, 68.599945f, 0, 103, -26, 0x0164), + CS_CAM_AT_LIST(0, 330), + CS_CAM_AT(CS_CMD_CONTINUE, 0x1E, 20, 175.39832f, -649, -75, 101, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0xE2, 20, 175.39832f, -652, -75, 98, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x1E, 20, 175.39832f, -658, -75, 87, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0xE2, 20, 175.39832f, -678, 127, 71, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 175.39832f, -727, 0, 71, 0x1F98), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 145.79877f, -513, 0, 43, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -349, 89, 47, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, 6, 61, 1, 0x0164), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, 33, 66, 28, 0x2100), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, 158, 42, 1, 0x0049), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, 158, 42, 1, 0x204C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, 158, 42, 1, 0xE990), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.600002f, 158, 42, 1, 0x0000), + CS_CAM_AT_LIST(263, 1513), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 42.22703f, 322, 62, -122, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 42.22703f, 322, 62, -122, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 42.22703f, 322, 62, -122, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 42.22703f, 232, 63, -83, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 42.22703f, 232, 63, -83, 0x1F98), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 42.22703f, 232, 63, -83, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 42.22703f, 232, 63, -83, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 42.22703f, 232, 63, -83, 0x0164), + CS_CAM_AT_LIST(383, 1503), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.22702f, 51, 62, -105, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.22702f, 51, 62, -105, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 70.22702f, 51, 62, -105, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.22702f, 51, 62, -105, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 70.22702f, 51, 62, -105, 0x1F98), + CS_CAM_AT_LIST(453, 1573), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.027042f, 102, 22, -31, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.027042f, 102, 22, -31, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 45.027042f, 102, 22, -31, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.027042f, 102, 22, -31, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.027042f, 102, 22, -31, 0x1F98), + CS_CAM_AT_LIST(553, 1673), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.22702f, 51, 62, -105, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.22702f, 51, 62, -105, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 70.22702f, 51, 62, -105, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.22702f, 51, 62, -105, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 70.22702f, 51, 62, -105, 0x1F98), + CS_CAM_AT_LIST(623, 1743), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 347, 22, 0, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 347, 22, 0, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 60.4271f, 347, 22, 0, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 347, 22, 0, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.4271f, 347, 22, 0, 0x1F98), + CS_CAM_AT_LIST(693, 1813), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 258, -30, -54, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 258, -30, -54, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 1000, 60.4271f, 258, -30, -54, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 258, -30, -54, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.4271f, 258, -30, -54, 0x1F98), + CS_CAM_AT_LIST(763, 973), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.4271f, 96, 105, -29, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.4271f, 96, 105, -29, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.4271f, 96, 105, -29, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 15, 60.0271f, 110, 196, -26, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 70.42702f, 185, 399, 6, 0x1F98), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.42702f, 185, 399, 6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 70.42702f, 185, 399, 6, 0x0000), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 70.42702f, 185, 399, 6, 0x0164), + CS_CAM_AT_LIST(823, 1214), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 60.0f, 3, 6, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 40, 60.0f, 3, 6, -6, 0x1FBC), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 50.999966f, 3, 6, -6, 0xD5E0), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 20.59985f, 3, 6, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 51, 10.799838f, 3, 6, -6, 0x1F98), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.399838f, 3, 6, -6, 0x0000), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 50, 10.199839f, 3, 6, -6, 0x0164), + CS_CAM_AT(CS_CMD_STOP, 0x00, 50, 10.999838f, 3, 6, -6, 0x2100), + CS_CAM_AT_REL_TO_PLAYER_LIST(899, 2109), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 100, 5, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 101, 6, 0x1FBC), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 1, 99, 41, 0xD5E0), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x1F98), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 1000, 68.599945f, 0, 42, 16, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_CONTINUE, 0x00, 30, 68.599945f, 0, 42, 16, 0x0000), + CS_CAM_AT_REL_TO_PLAYER(CS_CMD_STOP, 0x00, 30, 68.599945f, 0, 42, 16, 0x0164), + CS_NPC_ACTION_LIST(62, 1), + CS_NPC_ACTION(0x0004, 0, 3000, 0xC10F, 0x0000, 0x0000, 77, 80, -2, 0, 80, 0, -0.025666667f, 0.0f, 0.025666667f), + CS_TEXT_LIST(14), + CS_TEXT_NONE(0, 394), + CS_TEXT_DISPLAY_TEXTBOX(0x4041, 394, 444, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(444, 464), + CS_TEXT_DISPLAY_TEXTBOX(0x4046, 464, 543, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(543, 564), + CS_TEXT_DISPLAY_TEXTBOX(0x4049, 564, 613, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(613, 634), + CS_TEXT_DISPLAY_TEXTBOX(0x4047, 634, 684, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(684, 704), + CS_TEXT_DISPLAY_TEXTBOX(0x404A, 704, 750, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(750, 1085), + CS_TEXT_DISPLAY_TEXTBOX(0x003D, 1085, 1090, 0x0000, 0x0000, 0x0000), + CS_TEXT_NONE(1090, 1150), + CS_TEXT_DISPLAY_TEXTBOX(0x4042, 1150, 1160, 0x0000, 0x0000, 0x0000), + CS_TERMINATOR(LAKE_HYLIA_WATER_RISES, 1190, 1213), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x0044, 900, 901, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC7, 0x00000034, 0x00000000, 0xFFFFFFC7, 0x00000034), + CS_FADE_BGM_LIST(1), + CS_FADE_BGM(0x0004, 800, 850, 0x0000, 0x00000000, 0x00000000, 0xFFFFFFC6, 0x00000030, 0x00000000, 0xFFFFFFC6, 0x00000030), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c b/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c new file mode 100644 index 000000000..f91bc16ae --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.c @@ -0,0 +1,816 @@ +#include "z_en_sa.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "objects/object_sa/object_sa.h" +#include "scenes/overworld/spot04/spot04_scene.h" +#include "scenes/overworld/spot05/spot05_scene.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnSa_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSa_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AF6448(EnSa* this, GlobalContext* globalCtx); +void func_80AF67D0(EnSa* this, GlobalContext* globalCtx); +void func_80AF683C(EnSa* this, GlobalContext* globalCtx); +void func_80AF68E4(EnSa* this, GlobalContext* globalCtx); +void func_80AF6B20(EnSa* this, GlobalContext* globalCtx); + +typedef enum { + /* 0 */ SARIA_EYE_OPEN, + /* 1 */ SARIA_EYE_HALF, + /* 2 */ SARIA_EYE_CLOSED, + /* 3 */ SARIA_EYE_SUPRISED, + /* 4 */ SARIA_EYE_SAD +} SariaEyeState; + +typedef enum { + /* 0 */ SARIA_MOUTH_CLOSED2, + /* 1 */ SARIA_MOUTH_SUPRISED, + /* 2 */ SARIA_MOUTH_CLOSED, + /* 3 */ SARIA_MOUTH_SMILING_OPEN, + /* 4 */ SARIA_MOUTH_FROWNING +} SariaMouthState; + +const ActorInit En_Sa_InitVars = { + ACTOR_EN_SA, + ACTORCAT_NPC, + FLAGS, + OBJECT_SA, + sizeof(EnSa), + (ActorFunc)EnSa_Init, + (ActorFunc)EnSa_Destroy, + (ActorFunc)EnSa_Update, + (ActorFunc)EnSa_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 46, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { + 0, 0, 0, 0, MASS_IMMOVABLE, +}; + +typedef enum { + /* 0 */ ENSA_ANIM1_0, + /* 1 */ ENSA_ANIM1_1, + /* 2 */ ENSA_ANIM1_2, + /* 3 */ ENSA_ANIM1_3, + /* 4 */ ENSA_ANIM1_4, + /* 5 */ ENSA_ANIM1_5, + /* 6 */ ENSA_ANIM1_6, + /* 7 */ ENSA_ANIM1_7, + /* 8 */ ENSA_ANIM1_8, + /* 9 */ ENSA_ANIM1_9, + /* 10 */ ENSA_ANIM1_10, + /* 11 */ ENSA_ANIM1_11 +} EnSaAnimation1; + +static AnimationFrameCountInfo sAnimationInfo1[] = { + { &gSariaWaitArmsToSideAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, + { &gSariaLookUpArmExtendedAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gSariaWaveAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gSariaRunAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gSariaWaitArmsToSideAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gSariaLookOverShoulderAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gSariaPlayingOcarinaAnim, 1.0f, ANIMMODE_LOOP, -10.0f }, + { &gSariaStopPlayingOcarinaAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gSariaOcarinaToMouthAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gSariaLinkLearnedSariasSongAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gSariaReturnToOcarinaAnim, 1.0f, ANIMMODE_ONCE, -10.0f }, + { &gSariaPlayingOcarinaAnim, 1.0f, ANIMMODE_LOOP, 0.0f }, +}; + +typedef enum { + /* 0 */ ENSA_ANIM2_0, + /* 1 */ ENSA_ANIM2_1, + /* 2 */ ENSA_ANIM2_2, + /* 3 */ ENSA_ANIM2_3, + /* 4 */ ENSA_ANIM2_4, + /* 5 */ ENSA_ANIM2_5, + /* 6 */ ENSA_ANIM2_6, + /* 7 */ ENSA_ANIM2_7, + /* 8 */ ENSA_ANIM2_8, + /* 9 */ ENSA_ANIM2_9 +} EnSaAnimation2; + +static AnimationInfo sAnimationInfo2[] = { + { &gSariaTransitionHandsSideToChestToSideAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gSariaTransitionHandsSideToBackAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -4.0f }, + { &gSariaRightArmExtendedWaitAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gSariaHandsOutAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gSariaStandHandsOnHipsAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gSariaExtendRightArmAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gSariaTransitionHandsSideToHipsAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gSariaHandsBehindBackWaitAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + { &gSariaHandsOnFaceAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + { &gSariaWaitArmsToSideAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, +}; + +s16 func_80AF5560(EnSa* this, GlobalContext* globalCtx) { + s16 textState = Message_GetState(&globalCtx->msgCtx); + + if (this->unk_209 == TEXT_STATE_AWAITING_NEXT || this->unk_209 == TEXT_STATE_EVENT || + this->unk_209 == TEXT_STATE_CLOSING || this->unk_209 == TEXT_STATE_DONE_HAS_NEXT) { + if (textState != this->unk_209) { + this->unk_208++; + } + } + this->unk_209 = textState; + return textState; +} + +u16 func_80AF55E0(GlobalContext* globalCtx, Actor* thisx) { + EnSa* this = (EnSa*)thisx; + u16 reaction = Text_GetFaceReaction(globalCtx, 0x10); + + if (reaction != 0) { + return reaction; + } + if (CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) { + return 0x10AD; + } + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + this->unk_208 = 0; + this->unk_209 = TEXT_STATE_NONE; + if (gSaveContext.infTable[0] & 0x20) { + return 0x1048; + } else { + return 0x1047; + } + } + if (gSaveContext.eventChkInf[0] & 4) { + this->unk_208 = 0; + this->unk_209 = TEXT_STATE_NONE; + if (gSaveContext.infTable[0] & 8) { + return 0x1032; + } else { + return 0x1031; + } + } + if (gSaveContext.infTable[0] & 1) { + this->unk_208 = 0; + this->unk_209 = TEXT_STATE_NONE; + if (gSaveContext.infTable[0] & 2) { + return 0x1003; + } else { + return 0x1002; + } + } + return 0x1001; +} + +s16 func_80AF56F4(GlobalContext* globalCtx, Actor* thisx) { + s16 ret = 1; + EnSa* this = (EnSa*)thisx; + + switch (func_80AF5560(this, globalCtx)) { + case TEXT_STATE_CLOSING: + switch (this->actor.textId) { + case 0x1002: + gSaveContext.infTable[0] |= 2; + ret = 0; + break; + case 0x1031: + gSaveContext.eventChkInf[0] |= 8; + gSaveContext.infTable[0] |= 8; + ret = 0; + break; + case 0x1047: + gSaveContext.infTable[0] |= 0x20; + ret = 0; + break; + default: + ret = 0; + break; + } + break; + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + break; + } + return ret; +} + +void func_80AF57D8(EnSa* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum != SCENE_SPOT05 || + ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) < 0x1555 || this->unk_1E0.unk_00 != 0) { + func_800343CC(globalCtx, &this->actor, &this->unk_1E0.unk_00, this->collider.dim.radius + 30.0f, func_80AF55E0, + func_80AF56F4); + } +} + +f32 func_80AF5894(EnSa* this) { + f32 endFrame = this->skelAnime.endFrame; + f32 startFrame = this->skelAnime.startFrame; + + this->skelAnime.startFrame = endFrame; + this->skelAnime.curFrame = endFrame; + this->skelAnime.endFrame = startFrame; + this->skelAnime.playSpeed = -1.0f; + return startFrame; +} + +void func_80AF58B8(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_3); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_2); + this->unk_20A++; + } + break; + } +} + +void func_80AF594C(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_8); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_9); + this->unk_20A++; + } + break; + } +} + +void func_80AF59E0(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_1); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_7); + this->unk_20A++; + } + break; + } +} + +void func_80AF5A74(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_1); + func_80AF5894(this); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_9); + this->unk_20A++; + } + break; + } +} + +void func_80AF5B10(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_6); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_4); + this->unk_20A++; + } + break; + } +} + +void func_80AF5BA4(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_6); + func_80AF5894(this); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_9); + this->unk_20A++; + } + break; + } +} + +void func_80AF5C40(EnSa* this) { + switch (this->unk_20A) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_5); + this->unk_20A++; + case 1: + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo2, ENSA_ANIM2_0); + this->unk_20A++; + } + break; + } +} + +void func_80AF5CD4(EnSa* this, u8 arg1) { + this->unk_20B = arg1; + this->unk_20A = 0; +} + +void func_80AF5CE4(EnSa* this) { + switch (this->unk_20B) { + case 1: + func_80AF58B8(this); + break; + case 2: + func_80AF594C(this); + break; + case 3: + func_80AF59E0(this); + break; + case 4: + func_80AF5A74(this); + break; + case 5: + func_80AF5B10(this); + break; + case 6: + func_80AF5BA4(this); + break; + case 7: + func_80AF5C40(this); + break; + } +} + +void EnSa_ChangeAnim(EnSa* this, s32 index) { + Animation_Change(&this->skelAnime, sAnimationInfo1[index].animation, 1.0f, 0.0f, + Animation_GetLastFrame(sAnimationInfo1[index].animation), sAnimationInfo1[index].mode, + sAnimationInfo1[index].morphFrames); +} + +s32 func_80AF5DFC(EnSa* this, GlobalContext* globalCtx) { + if (gSaveContext.cutsceneIndex >= 0xFFF0 && gSaveContext.cutsceneIndex != 0xFFFD) { + if (globalCtx->sceneNum == SCENE_SPOT04) { + return 4; + } + if (globalCtx->sceneNum == SCENE_SPOT05) { + return 5; + } + } + if (globalCtx->sceneNum == SCENE_KOKIRI_HOME5 && !LINK_IS_ADULT && + INV_CONTENT(ITEM_OCARINA_FAIRY) == ITEM_OCARINA_FAIRY && !(gSaveContext.eventChkInf[4] & 1)) { + return 1; + } + if (globalCtx->sceneNum == SCENE_SPOT05 && (gSaveContext.eventChkInf[4] & 1)) { + return CHECK_QUEST_ITEM(QUEST_SONG_SARIA) ? 2 : 5; + } + if (globalCtx->sceneNum == SCENE_SPOT04 && !CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + if (gSaveContext.infTable[0] & 1) { + return 1; + } + return 4; + } + return 0; +} + +void func_80AF5F34(EnSa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a3 = 0; + + if (globalCtx->sceneNum == SCENE_SPOT04) { + phi_a3 = (this->actionFunc == func_80AF68E4) ? 1 : 4; + } + if (globalCtx->sceneNum == SCENE_SPOT05) { + phi_a3 = (this->skelAnime.animation == &gSariaPlayingOcarinaAnim) ? 1 : 3; + } + if (globalCtx->sceneNum == SCENE_SPOT05 && this->actionFunc == func_80AF6448 && + this->skelAnime.animation == &gSariaStopPlayingOcarinaAnim) { + phi_a3 = 1; + } + if (globalCtx->sceneNum == SCENE_SPOT05 && this->actionFunc == func_80AF68E4 && + this->skelAnime.animation == &gSariaOcarinaToMouthAnim) { + phi_a3 = 1; + } + this->unk_1E0.unk_18 = player->actor.world.pos; + this->unk_1E0.unk_14 = 4.0f; + func_80034A14(&this->actor, &this->unk_1E0, 2, phi_a3); +} + +s32 func_80AF603C(EnSa* this) { + if (this->skelAnime.animation != &gSariaPlayingOcarinaAnim && + this->skelAnime.animation != &gSariaOcarinaToMouthAnim) { + return 0; + } + if (this->unk_1E0.unk_00 != 0) { + return 0; + } + this->unk_20E = 0; + if (this->rightEyeIndex != SARIA_EYE_CLOSED) { + return 0; + } + return 1; +} + +void func_80AF609C(EnSa* this) { + s16 phi_v1; + + if (func_80AF603C(this) == 0) { + if (this->unk_20E == 0) { + phi_v1 = 0; + } else { + this->unk_20E--; + phi_v1 = this->unk_20E; + } + if (phi_v1 == 0) { + this->rightEyeIndex++; + if (this->rightEyeIndex < SARIA_EYE_SUPRISED) { + this->leftEyeIndex = this->rightEyeIndex; + } else { + this->unk_20E = Rand_S16Offset(30, 30); + this->leftEyeIndex = SARIA_EYE_OPEN; + this->rightEyeIndex = this->leftEyeIndex; + } + } + } +} + +void func_80AF6130(CsCmdActorAction* csAction, Vec3f* dst) { + dst->x = csAction->startPos.x; + dst->y = csAction->startPos.y; + dst->z = csAction->startPos.z; +} + +void func_80AF6170(CsCmdActorAction* csAction, Vec3f* dst) { + dst->x = csAction->endPos.x; + dst->y = csAction->endPos.y; + dst->z = csAction->endPos.z; +} + +void EnSa_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSa* this = (EnSa*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 12.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSariaSkel, NULL, this->jointTable, this->morphTable, 17); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + + switch (func_80AF5DFC(this, globalCtx)) { + case 2: + EnSa_ChangeAnim(this, ENSA_ANIM1_11); + this->actionFunc = func_80AF6448; + break; + case 5: + EnSa_ChangeAnim(this, ENSA_ANIM1_11); + this->actionFunc = func_80AF683C; + break; + case 1: + this->actor.gravity = -1.0f; + EnSa_ChangeAnim(this, ENSA_ANIM1_0); + this->actionFunc = func_80AF6448; + break; + case 4: + this->unk_210 = 0; + this->actor.gravity = -1.0f; + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gSpot04Cs_10E20); + gSaveContext.cutsceneTrigger = 1; + EnSa_ChangeAnim(this, ENSA_ANIM1_4); + this->actionFunc = func_80AF68E4; + break; + case 3: + this->unk_210 = 0; + this->actor.gravity = -1.0f; + EnSa_ChangeAnim(this, ENSA_ANIM1_0); + this->actionFunc = func_80AF68E4; + break; + case 0: + Actor_Kill(&this->actor); + return; + } + + Actor_SetScale(&this->actor, 0.01f); + + this->actor.targetMode = 6; + this->unk_1E0.unk_00 = 0; + this->alpha = 255; + this->unk_21A = this->actor.shape.rot; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, FAIRY_KOKIRI); +} + +void EnSa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSa* this = (EnSa*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80AF6448(EnSa* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT04) { + if (this->unk_1E0.unk_00 != 0) { + switch (this->actor.textId) { + case 0x1002: + if (this->unk_208 == 0 && this->unk_20B != 1) { + func_80AF5CD4(this, 1); + this->mouthIndex = 1; + } + if (this->unk_208 == 2 && this->unk_20B != 2) { + func_80AF5CD4(this, 2); + this->mouthIndex = 1; + } + if (this->unk_208 == 5) { + this->mouthIndex = 0; + } + break; + case 0x1003: + if (this->unk_208 == 0 && this->unk_20B != 4) { + func_80AF5CD4(this, 4); + } + break; + case 0x1031: + if (this->unk_208 == 0 && this->unk_20B != 4 && + this->skelAnime.animation == &gSariaHandsBehindBackWaitAnim) { + func_80AF5CD4(this, 4); + this->mouthIndex = 3; + } + if (this->unk_208 == 2 && this->unk_20B != 5) { + func_80AF5CD4(this, 5); + this->mouthIndex = 2; + } + if (this->unk_208 == 4 && this->unk_20B != 6) { + func_80AF5CD4(this, 6); + this->mouthIndex = 0; + } + break; + case 0x1032: + if (this->unk_208 == 0 && this->unk_20B != 4 && + this->skelAnime.animation == &gSariaHandsBehindBackWaitAnim) { + func_80AF5CD4(this, 4); + } + break; + case 0x1047: + if (this->unk_208 == 1 && this->unk_20B != 7) { + func_80AF5CD4(this, 7); + } + break; + case 0x1048: + if (this->unk_208 == 0 && this->unk_20B != 7) { + func_80AF5CD4(this, 7); + } + break; + } + } else if (!CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && + ((gSaveContext.infTable[0] & 2) || (gSaveContext.infTable[0] & 8))) { + if (this->unk_20B != 3) { + func_80AF5CD4(this, 3); + } + } else { + func_80AF5CD4(this, 0); + } + func_80AF5CE4(this); + } + if (this->skelAnime.animation == &gSariaStopPlayingOcarinaAnim) { + this->skelAnime.playSpeed = -1.0f; + if ((s32)this->skelAnime.curFrame == 0) { + EnSa_ChangeAnim(this, ENSA_ANIM1_6); + } + } + if (this->unk_1E0.unk_00 != 0 && globalCtx->sceneNum == SCENE_SPOT05) { + Animation_Change(&this->skelAnime, &gSariaStopPlayingOcarinaAnim, 1.0f, 0.0f, 10.0f, ANIMMODE_ONCE, -10.0f); + this->actionFunc = func_80AF67D0; + } +} + +void func_80AF67D0(EnSa* this, GlobalContext* globalCtx) { + if (this->unk_1E0.unk_00 == 0) { + Animation_Change(&this->skelAnime, &gSariaStopPlayingOcarinaAnim, 0.0f, 10.0f, 0.0f, ANIMMODE_ONCE, -10.0f); + this->actionFunc = func_80AF6448; + } +} + +void func_80AF683C(EnSa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (!(player->actor.world.pos.z >= -2220.0f) && !Gameplay_InCsMode(globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(spot05_scene_Cs_005730); + gSaveContext.cutsceneTrigger = 1; + this->actionFunc = func_80AF68E4; + } +} + +void func_80AF68E4(EnSa* this, GlobalContext* globalCtx) { + s16 phi_v0; + Vec3f startPos; + Vec3f endPos; + Vec3f D_80AF7448 = { 0.0f, 0.0f, 0.0f }; + CsCmdActorAction* csAction; + f32 temp_f0; + f32 gravity; + + if ((gSaveContext.cutsceneTrigger != 1) && (globalCtx->csCtx.state == CS_STATE_IDLE)) { + this->actionFunc = func_80AF6B20; + return; + } + csAction = globalCtx->csCtx.npcActions[1]; + if (csAction != NULL) { + func_80AF6130(csAction, &startPos); + func_80AF6170(csAction, &endPos); + + if (this->unk_210 == 0) { + this->actor.world.pos = startPos; + } + if (this->unk_210 != csAction->action) { + switch (csAction->action) { + case 2: + this->mouthIndex = 1; + break; + case 9: + this->mouthIndex = 1; + break; + default: + this->mouthIndex = 0; + break; + } + EnSa_ChangeAnim(this, csAction->action); + this->unk_210 = csAction->action; + } + if (phi_v0) {} + if (csAction->action == 3) { + if (this->unk_20C == 0) { + phi_v0 = 0; + } else { + this->unk_20C--; + phi_v0 = this->unk_20C; + } + if (phi_v0 == 0) { + Audio_PlaySoundGeneral(NA_SE_PL_WALK_GROUND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_20C = 8; + } + } + this->actor.shape.rot.x = csAction->urot.x; + this->actor.shape.rot.y = csAction->urot.y; + this->actor.shape.rot.z = csAction->urot.z; + this->actor.velocity = D_80AF7448; + + if (globalCtx->csCtx.frames < csAction->endFrame) { + temp_f0 = csAction->endFrame - csAction->startFrame; + this->actor.velocity.x = (endPos.x - startPos.x) / temp_f0; + this->actor.velocity.y = (endPos.y - startPos.y) / temp_f0; + gravity = this->actor.gravity; + if (globalCtx->sceneNum == SCENE_SPOT05) { + gravity = 0.0f; + } + this->actor.velocity.y += gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + this->actor.velocity.z = (endPos.z - startPos.z) / temp_f0; + } + } +} + +void func_80AF6B20(EnSa* this, GlobalContext* globalCtx) { + if (globalCtx->sceneNum == SCENE_SPOT05) { + Item_Give(globalCtx, ITEM_SONG_SARIA); + EnSa_ChangeAnim(this, ENSA_ANIM1_6); + } + + if (globalCtx->sceneNum == SCENE_SPOT04) { + EnSa_ChangeAnim(this, ENSA_ANIM1_4); + this->actor.world.pos = this->actor.home.pos; + this->actor.world.rot = this->unk_21A; + this->mouthIndex = 0; + gSaveContext.infTable[0] |= 1; + } + + this->actionFunc = func_80AF6448; +} + +void EnSa_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSa* this = (EnSa*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + + if (this->skelAnime.animation == &gSariaOcarinaToMouthAnim && + this->skelAnime.curFrame >= Animation_GetLastFrame(&gSariaOcarinaToMouthAnim)) { + EnSa_ChangeAnim(this, ENSA_ANIM1_6); + } + + if (this->actionFunc != func_80AF68E4) { + this->alpha = func_80034DD4(&this->actor, globalCtx, this->alpha, 400.0f); + } else { + this->alpha = 255; + } + + this->actor.shape.shadowAlpha = this->alpha; + + if (this->actionFunc == func_80AF68E4) { + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.world.pos.z += this->actor.velocity.z; + } else { + func_8002D7EC(&this->actor); + } + + if (globalCtx->sceneNum != SCENE_SPOT05) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + } + + func_80AF609C(this); + this->actionFunc(this, globalCtx); + func_80AF57D8(this, globalCtx); + func_80AF5F34(this, globalCtx); +} + +s32 EnSa_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnSa* this = (EnSa*)thisx; + s32 pad; + Vec3s sp18; + + if (limbIndex == 16) { + Matrix_Translate(900.0f, 0.0f, 0.0f, MTXMODE_APPLY); + sp18 = this->unk_1E0.unk_08; + Matrix_RotateX(BINANG_TO_RAD(sp18.y), MTXMODE_APPLY); + Matrix_RotateZ(BINANG_TO_RAD(sp18.x), MTXMODE_APPLY); + Matrix_Translate(-900.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + + if (limbIndex == 9) { + sp18 = this->unk_1E0.unk_0E; + Matrix_RotateY(BINANG_TO_RAD(sp18.y), MTXMODE_APPLY); + Matrix_RotateX(BINANG_TO_RAD(sp18.x), MTXMODE_APPLY); + } + + if (globalCtx->sceneNum == SCENE_SPOT05 && limbIndex == 15) { + *dList = gSariaRightHandAndOcarinaDL; + } + + return 0; +} + +void EnSa_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnSa* this = (EnSa*)thisx; + Vec3f D_80AF7454 = { 400.0, 0.0f, 0.0f }; + + if (limbIndex == 16) { + Matrix_MultVec3f(&D_80AF7454, &this->actor.focus.pos); + } +} + +void EnSa_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* mouthTextures[] = { + gSariaMouthClosed2Tex, gSariaMouthSmilingOpenTex, gSariaMouthFrowningTex, + gSariaMouthSuprisedTex, gSariaMouthClosedTex, + }; + static void* eyeTextures[] = { + gSariaEyeOpenTex, gSariaEyeHalfTex, gSariaEyeClosedTex, gSariaEyeSuprisedTex, gSariaEyeSadTex, + }; + EnSa* this = (EnSa*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_sa.c", 1444); + + if (this->alpha == 255) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->rightEyeIndex])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTextures[this->leftEyeIndex])); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(mouthTextures[this->mouthIndex])); + func_80034BA0(globalCtx, &this->skelAnime, EnSa_OverrideLimbDraw, EnSa_PostLimbDraw, &this->actor, this->alpha); + } else if (this->alpha != 0) { + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->rightEyeIndex])); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTextures[this->leftEyeIndex])); + gSPSegment(POLY_XLU_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(mouthTextures[this->mouthIndex])); + func_80034CC4(globalCtx, &this->skelAnime, EnSa_OverrideLimbDraw, EnSa_PostLimbDraw, &this->actor, this->alpha); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_sa.c", 1497); +} diff --git a/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.h b/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.h new file mode 100644 index 000000000..872b50f82 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sa/z_en_sa.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_SA_H +#define Z_EN_SA_H + +#include "ultra64.h" +#include "global.h" + +struct EnSa; + +typedef void (*EnSaActionFunc)(struct EnSa*, GlobalContext*); + +typedef struct EnSa { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnSaActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ u8 unk_208; + /* 0x0209 */ u8 unk_209; + /* 0x020A */ u8 unk_20A; + /* 0x020B */ u8 unk_20B; + /* 0x020C */ s16 unk_20C; + /* 0x020E */ s16 unk_20E; + /* 0x0210 */ s16 unk_210; + /* 0x0212 */ s16 rightEyeIndex; + /* 0x0214 */ s16 leftEyeIndex; + /* 0x0216 */ s16 mouthIndex; + /* 0x0218 */ s16 alpha; + /* 0x021A */ Vec3s unk_21A; + /* 0x0220 */ Vec3s jointTable[17]; + /* 0x0286 */ Vec3s morphTable[17]; +} EnSa; // size = 0x02EC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Sb/z_en_sb.c b/soh/src/overlays/actors/ovl_En_Sb/z_en_sb.c new file mode 100644 index 000000000..c50d4a9b1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sb/z_en_sb.c @@ -0,0 +1,504 @@ +/* + * File: z_en_sb.c + * Overlay: ovl_En_Sb + * Description: Shellblade + */ + +#include "z_en_sb.h" +#include "vt.h" +#include "objects/object_sb/object_sb.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnSb_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSb_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSb_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnSb_SetupWaitClosed(EnSb* this); + +void EnSb_WaitClosed(EnSb* this, GlobalContext* globalCtx); +void EnSb_Open(EnSb* this, GlobalContext* globalCtx); +void EnSb_WaitOpen(EnSb* this, GlobalContext* globalCtx); +void EnSb_TurnAround(EnSb* this, GlobalContext* globalCtx); +void EnSb_Lunge(EnSb* this, GlobalContext* globalCtx); +void EnSb_Bounce(EnSb* this, GlobalContext* globalCtx); +void EnSb_Cooldown(EnSb* this, GlobalContext* globalCtx); + +const ActorInit En_Sb_InitVars = { + ACTOR_EN_SB, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_SB, + sizeof(EnSb), + (ActorFunc)EnSb_Init, + (ActorFunc)EnSb_Destroy, + (ActorFunc)EnSb_Update, + (ActorFunc)EnSb_Draw, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + COLSHAPE_CYLINDER, + }, + { 0x00, { 0xFFCFFFFF, 0x04, 0x08 }, { 0xFFCFFFFF, 0x00, 0x00 }, 0x01, 0x01, 0x01 }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +static DamageTable sDamageTable[] = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(2, 0xF), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(2, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xD), + /* Master sword */ DMG_ENTRY(2, 0xD), + /* Giant's Knife */ DMG_ENTRY(4, 0xD), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0xF), + /* Light arrow */ DMG_ENTRY(2, 0xF), + /* Unk arrow 1 */ DMG_ENTRY(4, 0xE), + /* Unk arrow 2 */ DMG_ENTRY(2, 0xF), + /* Unk arrow 3 */ DMG_ENTRY(2, 0xF), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0xD), + /* Giant spin */ DMG_ENTRY(4, 0xD), + /* Master spin */ DMG_ENTRY(2, 0xD), + /* Kokiri jump */ DMG_ENTRY(2, 0xD), + /* Giant jump */ DMG_ENTRY(8, 0xD), + /* Master jump */ DMG_ENTRY(4, 0xD), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x27, ICHAIN_CONTINUE), + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +static Vec3f sFlamePosOffsets[] = { + { 5.0f, 0.0f, 0.0f }, + { -5.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 5.0f }, + { 0.0f, 0.0f, -5.0f }, +}; + +typedef enum { + /* 0x00 */ SHELLBLADE_OPEN, + /* 0x01 */ SHELLBLADE_WAIT_CLOSED, + /* 0x02 */ SHELLBLADE_WAIT_OPEN, + /* 0x03 */ SHELLBLADE_LUNGE, + /* 0x04 */ SHELLBLADE_BOUNCE +} ShellbladeBehavior; + +void EnSb_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSb* this = (EnSb*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = sDamageTable; + this->actor.colChkInfo.health = 2; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_sb_Skel_002BF0, &object_sb_Anim_000194, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->isDead = false; + this->actor.colChkInfo.mass = 0; + Actor_SetScale(&this->actor, 0.006f); + this->actor.shape.rot.y = 0; + this->actor.speedXZ = 0.0f; + this->actor.gravity = -0.35f; + this->fire = 0; + this->hitByWindArrow = false; + this->actor.velocity.y = -1.0f; + EnSb_SetupWaitClosed(this); +} + +void EnSb_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSb* this = (EnSb*)thisx; + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnSb_SpawnBubbles(GlobalContext* globalCtx, EnSb* this) { + s32 i; + + if (this->actor.yDistToWater > 0) { + for (i = 0; i < 10; i++) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 10.0f, 10.0f, 30.0f, 0.25f); + } + } +} + +void EnSb_SetupWaitClosed(EnSb* this) { + Animation_Change(&this->skelAnime, &object_sb_Anim_00004C, 1.0f, 0, Animation_GetLastFrame(&object_sb_Anim_00004C), + ANIMMODE_ONCE, 0.0f); + this->behavior = SHELLBLADE_WAIT_CLOSED; + this->actionFunc = EnSb_WaitClosed; +} + +void EnSb_SetupOpen(EnSb* this) { + Animation_Change(&this->skelAnime, &object_sb_Anim_000194, 1.0f, 0, Animation_GetLastFrame(&object_sb_Anim_000194), + ANIMMODE_ONCE, 0.0f); + this->behavior = SHELLBLADE_OPEN; + this->actionFunc = EnSb_Open; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHELL_MOUTH); +} + +void EnSb_SetupWaitOpen(EnSb* this) { + Animation_Change(&this->skelAnime, &object_sb_Anim_002C8C, 1.0f, 0, Animation_GetLastFrame(&object_sb_Anim_002C8C), + ANIMMODE_LOOP, 0.0f); + this->behavior = SHELLBLADE_WAIT_OPEN; + this->actionFunc = EnSb_WaitOpen; +} + +void EnSb_SetupLunge(EnSb* this) { + f32 frameCount = Animation_GetLastFrame(&object_sb_Anim_000124); + f32 playbackSpeed = this->actor.yDistToWater > 0.0f ? 1.0f : 0.0f; + + Animation_Change(&this->skelAnime, &object_sb_Anim_000124, playbackSpeed, 0.0f, frameCount, ANIMMODE_ONCE, 0); + this->behavior = SHELLBLADE_LUNGE; + this->actionFunc = EnSb_Lunge; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHELL_MOUTH); +} + +void EnSb_SetupBounce(EnSb* this) { + Animation_Change(&this->skelAnime, &object_sb_Anim_0000B4, 1.0f, 0, Animation_GetLastFrame(&object_sb_Anim_0000B4), + ANIMMODE_ONCE, 0.0f); + this->behavior = SHELLBLADE_BOUNCE; + this->actionFunc = EnSb_Bounce; +} + +void EnSb_SetupCooldown(EnSb* this, s32 changeSpeed) { + f32 frameCount = Animation_GetLastFrame(&object_sb_Anim_00004C); + + if (this->behavior != SHELLBLADE_WAIT_CLOSED) { + Animation_Change(&this->skelAnime, &object_sb_Anim_00004C, 1.0f, 0, frameCount, ANIMMODE_ONCE, 0.0f); + } + this->behavior = SHELLBLADE_WAIT_CLOSED; + if (changeSpeed) { + if (this->actor.yDistToWater > 0.0f) { + this->actor.speedXZ = -5.0f; + if (this->actor.velocity.y < 0.0f) { + this->actor.velocity.y = 2.1f; + } + } else { + this->actor.speedXZ = -6.0f; + if (this->actor.velocity.y < 0.0f) { + this->actor.velocity.y = 1.4f; + } + } + } + this->timer = 60; + this->actionFunc = EnSb_Cooldown; +} + +void EnSb_WaitClosed(EnSb* this, GlobalContext* globalCtx) { + // always face toward link + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, 0x7D0, 0x0); + + if ((this->actor.xzDistToPlayer <= 160.0f) && (this->actor.xzDistToPlayer > 40.0f)) { + EnSb_SetupOpen(this); + } +} + +void EnSb_Open(EnSb* this, GlobalContext* globalCtx) { + f32 currentFrame = this->skelAnime.curFrame; + + if (Animation_GetLastFrame(&object_sb_Anim_000194) <= currentFrame) { + this->timer = 15; + EnSb_SetupWaitOpen(this); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, 0x7D0, 0x0); + if ((this->actor.xzDistToPlayer > 160.0f) || (this->actor.xzDistToPlayer <= 40.0f)) { + EnSb_SetupWaitClosed(this); + } + } +} + +void EnSb_WaitOpen(EnSb* this, GlobalContext* globalCtx) { + s16 timer = this->timer; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, 0x7D0, 0x0); + + if ((this->actor.xzDistToPlayer > 160.0f) || (this->actor.xzDistToPlayer <= 40.0f)) { + EnSb_SetupWaitClosed(this); + } + + if (timer != 0) { + this->timer = timer - 1; + } else { + this->timer = 0; + this->attackYaw = this->actor.yawTowardsPlayer; + this->actionFunc = EnSb_TurnAround; + } +} + +void EnSb_TurnAround(EnSb* this, GlobalContext* globalCtx) { + s16 invertedYaw; + + invertedYaw = this->attackYaw + 0x8000; + Math_SmoothStepToS(&this->actor.shape.rot.y, invertedYaw, 0x1, 0x1F40, 0xA); + + if (this->actor.shape.rot.y == invertedYaw) { + this->actor.world.rot.y = this->attackYaw; + if (this->actor.yDistToWater > 0.0f) { + this->actor.velocity.y = 3.0f; + this->actor.speedXZ = 5.0f; + this->actor.gravity = -0.35f; + } else { + this->actor.velocity.y = 2.0f; + this->actor.speedXZ = 6.0f; + this->actor.gravity = -2.0f; + } + EnSb_SpawnBubbles(globalCtx, this); + this->bouncesLeft = 3; + EnSb_SetupLunge(this); + // "Attack!!" + osSyncPrintf("アタァ〜ック!!\n"); + } +} + +void EnSb_Lunge(EnSb* this, GlobalContext* globalCtx) { + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.2f); + if ((this->actor.velocity.y <= -0.1f) || ((this->actor.bgCheckFlags & 2))) { + if (!(this->actor.yDistToWater > 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + this->actor.bgCheckFlags = this->actor.bgCheckFlags & ~2; + EnSb_SetupBounce(this); + } +} + +void EnSb_Bounce(EnSb* this, GlobalContext* globalCtx) { + s32 pad; + f32 currentFrame; + f32 frameCount; + + currentFrame = this->skelAnime.curFrame; + frameCount = Animation_GetLastFrame(&object_sb_Anim_0000B4); + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.2f); + + if (currentFrame == frameCount) { + if (this->bouncesLeft != 0) { + this->bouncesLeft--; + this->timer = 1; + if (this->actor.yDistToWater > 0.0f) { + this->actor.velocity.y = 3.0f; + this->actor.speedXZ = 5.0f; + this->actor.gravity = -0.35f; + } else { + this->actor.velocity.y = 2.0f; + this->actor.speedXZ = 6.0f; + this->actor.gravity = -2.0f; + } + EnSb_SpawnBubbles(globalCtx, this); + EnSb_SetupLunge(this); + } else if (this->actor.bgCheckFlags & 1) { + this->actor.bgCheckFlags &= ~2; + this->actor.speedXZ = 0.0f; + this->timer = 1; + EnSb_SetupWaitClosed(this); + osSyncPrintf(VT_FGCOL(RED) "攻撃終了!!" VT_RST "\n"); // "Attack Complete!" + } + } +} + +void EnSb_Cooldown(EnSb* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + if (this->actor.bgCheckFlags & 1) { + this->actor.bgCheckFlags &= ~1; + this->actor.speedXZ = 0.0f; + } + } else { + if (this->actor.bgCheckFlags & 1) { + this->actor.bgCheckFlags &= ~1; + this->actionFunc = EnSb_WaitClosed; + this->actor.speedXZ = 0.0f; + } + } +} + +s32 EnSb_IsVulnerable(EnSb* this) { + switch (this->behavior) { + case SHELLBLADE_OPEN: + if ((this->skelAnime.curFrame >= 2.0f) && (this->skelAnime.curFrame <= 5.0f)) { + return true; + } + break; + case SHELLBLADE_WAIT_CLOSED: + if ((this->skelAnime.curFrame >= 0.0f) && (this->skelAnime.curFrame <= 1.0f)) { + return true; + } + break; + case SHELLBLADE_WAIT_OPEN: + if ((this->skelAnime.curFrame >= 0.0f) && (this->skelAnime.curFrame <= 19.0f)) { + return true; + } + break; + case SHELLBLADE_LUNGE: + if (this->skelAnime.curFrame == 0.0f) { + return true; + } + break; + case SHELLBLADE_BOUNCE: + if ((this->skelAnime.curFrame >= 3.0f) && (this->skelAnime.curFrame <= 5.0f)) { + return true; + } + break; + } + return false; +} + +s32 EnSb_UpdateDamage(EnSb* this, GlobalContext* globalCtx) { + Vec3f hitPoint; + f32 hitY; + s16 yawDiff; + s32 tookDamage; + u8 hitByWindArrow; + + // hit box collided, switch to cool down + if ((this->collider.base.atFlags & AT_HIT)) { + EnSb_SetupCooldown(this, 1); + return 1; + } + + // hurt box collided, take damage if appropriate + if ((this->collider.base.acFlags & AC_HIT)) { + hitByWindArrow = false; + tookDamage = false; + this->collider.base.acFlags &= ~AC_HIT; + + switch (this->actor.colChkInfo.damageEffect) { + case 14: // wind arrow + hitByWindArrow = true; + case 15: // explosions, arrow, hammer, ice arrow, light arrow, spirit arrow, shadow arrow + if (EnSb_IsVulnerable(this)) { + hitY = this->collider.info.bumper.hitPos.y - this->actor.world.pos.y; + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((hitY < 30.0f) && (hitY > 10.0f) && (yawDiff >= -0x1FFF) && (yawDiff < 0x2000)) { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + tookDamage = true; + } + } + break; + case 2: // fire arrow, dins fire + this->fire = 4; + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + tookDamage = true; + break; + case 1: // hookshot/longshot + case 13: // all sword damage + if (EnSb_IsVulnerable(this)) { + hitY = this->collider.info.bumper.hitPos.y - this->actor.world.pos.y; + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((hitY < 30.0f) && (hitY > 10.0f) && (yawDiff >= -0x1FFF) && (yawDiff < 0x2000)) { + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0x50); + tookDamage = true; + EnSb_SetupCooldown(this, 0); + } + } + break; + default: + break; + } + if (this->actor.colChkInfo.health == 0) { + this->hitByWindArrow = hitByWindArrow; + BodyBreak_Alloc(&this->bodyBreak, 8, globalCtx); + this->isDead = true; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_SHELL_DEAD); + return 1; + } + + // if player attack didn't do damage, play recoil sound and spawn sparks + if (!tookDamage) { + hitPoint.x = this->collider.info.bumper.hitPos.x; + hitPoint.y = this->collider.info.bumper.hitPos.y; + hitPoint.z = this->collider.info.bumper.hitPos.z; + CollisionCheck_SpawnShieldParticlesMetal2(globalCtx, &hitPoint); + } + } + + return 0; +} + +void EnSb_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSb* this = (EnSb*)thisx; + s32 pad; + + if (this->isDead) { + if (this->actor.yDistToWater > 0.0f) { + this->actor.params = 4; + } else { + this->actor.params = 1; + } + if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, this->actor.params)) { + if (!this->hitByWindArrow) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x80); + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, 8); + } + Actor_Kill(&this->actor); + } + } else { + Actor_SetFocus(&this->actor, 20.0f); + Actor_SetScale(&this->actor, 0.006f); + Actor_MoveForward(&this->actor); + this->actionFunc(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 5); + EnSb_UpdateDamage(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + } +} + +void EnSb_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnSb* this = (EnSb*)thisx; + + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 0, 6, 8, dList, BODYBREAK_OBJECT_DEFAULT); +} + +void EnSb_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSb* this = (EnSb*)thisx; + Vec3f flamePos; + Vec3f* offset; + s16 fireDecr; + + func_8002EBCC(&this->actor, globalCtx, 1); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + NULL, EnSb_PostLimbDraw, this); + if (this->fire != 0) { + this->actor.colorFilterTimer++; + fireDecr = this->fire - 1; + // this is intended to draw flames after being burned, but the condition is never met to run this code + // fire gets set to 4 when burned, decrements to 3 and fails the "& 1" check and never stores the decrement + if ((fireDecr & 1) == 0) { + offset = &sFlamePosOffsets[(fireDecr & 3)]; + flamePos.x = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.x + offset->x); + flamePos.y = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.y + offset->y); + flamePos.z = Rand_CenteredFloat(5.0f) + (this->actor.world.pos.z + offset->z); + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &flamePos, 100, 0, 0, -1); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Sb/z_en_sb.h b/soh/src/overlays/actors/ovl_En_Sb/z_en_sb.h new file mode 100644 index 000000000..587474395 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sb/z_en_sb.h @@ -0,0 +1,26 @@ +#ifndef Z_EN_SB_H +#define Z_EN_SB_H + +#include "ultra64.h" +#include "global.h" + +struct EnSb; + +typedef void (*EnSbActionFunc)(struct EnSb*, GlobalContext*); + +typedef struct EnSb { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnSbActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ BodyBreak bodyBreak; + /* 0x01F8 */ s16 fire; + /* 0x01FA */ s16 behavior; + /* 0x01FC */ s16 isDead; + /* 0x01FE */ s16 timer; + /* 0x0200 */ s16 attackYaw; + /* 0x0202 */ s16 bouncesLeft; // amount of bounces left in the attack before going back to wait + /* 0x0204 */ u8 hitByWindArrow; +} EnSb; // size = 0x0208 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Scene_Change/z_en_scene_change.c b/soh/src/overlays/actors/ovl_En_Scene_Change/z_en_scene_change.c new file mode 100644 index 000000000..e09c0a8b2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Scene_Change/z_en_scene_change.c @@ -0,0 +1,69 @@ +/* + * File: z_en_scene_change.c + * Overlay: ovl_En_Scene_Change + * Description: Unknown (Broken Actor) + */ + +#include "z_en_scene_change.h" + +#define FLAGS 0 + +void EnSceneChange_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSceneChange_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSceneChange_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSceneChange_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnSceneChange_DoNothing(EnSceneChange* this, GlobalContext* globalCtx); + +const ActorInit En_Scene_Change_InitVars = { + ACTOR_EN_SCENE_CHANGE, + ACTORCAT_PROP, + FLAGS, + OBJECT_JJ, + sizeof(EnSceneChange), + (ActorFunc)EnSceneChange_Init, + (ActorFunc)EnSceneChange_Destroy, + (ActorFunc)EnSceneChange_Update, + (ActorFunc)EnSceneChange_Draw, + NULL, +}; + +void EnSceneChange_SetupAction(EnSceneChange* this, EnSceneChangeActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnSceneChange_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSceneChange* this = (EnSceneChange*)thisx; + + EnSceneChange_SetupAction(this, EnSceneChange_DoNothing); +} + +void EnSceneChange_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnSceneChange_DoNothing(EnSceneChange* this, GlobalContext* globalCtx) { +} + +void EnSceneChange_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSceneChange* this = (EnSceneChange*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EnSceneChange_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad[2]; + Gfx* displayList; + s32 pad2[2]; + Gfx* displayListHead; + + displayList = Graph_Alloc(globalCtx->state.gfxCtx, 0x3C0); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_scene_change.c", 290); + + displayListHead = displayList; + gSPSegment(POLY_OPA_DISP++, 0x0C, displayListHead); + + func_80093D18(globalCtx->state.gfxCtx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_scene_change.c", 386); +} diff --git a/soh/src/overlays/actors/ovl_En_Scene_Change/z_en_scene_change.h b/soh/src/overlays/actors/ovl_En_Scene_Change/z_en_scene_change.h new file mode 100644 index 000000000..d0b53e008 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Scene_Change/z_en_scene_change.h @@ -0,0 +1,16 @@ +#ifndef Z_ITEM_SCENE_CHANGE_H +#define Z_ITEM_SCENE_CHANGE_H + +#include "ultra64.h" +#include "global.h" + +struct EnSceneChange; + +typedef void (*EnSceneChangeActionFunc)(struct EnSceneChange*, GlobalContext*); + +typedef struct EnSceneChange { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnSceneChangeActionFunc actionFunc; +} EnSceneChange; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Sda/z_en_sda.c b/soh/src/overlays/actors/ovl_En_Sda/z_en_sda.c new file mode 100644 index 000000000..033903e91 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sda/z_en_sda.c @@ -0,0 +1,372 @@ +/** + * File: z_en_sda.c + * Overlay: ovl_En_Sda + * Description: Dynamic shadow for Link + */ + +#include "z_en_sda.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnSda_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSda_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSda_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSda_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AF95C4(EnSda* this, u8* shadowTexture, Player* player, GlobalContext* globalCtx); +void func_80AF9C70(u8* shadowTexture, Player* player, GlobalContext* globalCtx); +void func_80AF8F60(Player* player, u8* shadowTexture, f32 arg2); + +const ActorInit En_Sda_InitVars = { + ACTOR_EN_SDA, + ACTORCAT_BOSS, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnSda), + (ActorFunc)EnSda_Init, + (ActorFunc)EnSda_Destroy, + (ActorFunc)EnSda_Update, + (ActorFunc)EnSda_Draw, + NULL, +}; + +static Vec3f D_80AFA0D0 = { 0.0f, 0.0f, 0.0f }; + +static s16 D_80AFA0DC[] = { + 1, 2, 3, 3, 2, 1, +}; + +static s16 D_80AFA0E8[] = { + 2, 3, 4, 4, 4, 3, 2, 0, +}; + +static s16 D_80AFA0F8[] = { + 2, 3, 4, 4, 4, 4, 3, 2, +}; + +static s16 D_80AFA108[] = { + 2, 4, 5, 5, 6, 6, 6, 6, 5, 5, 4, 2, +}; + +static s16 D_80AFA120[] = { + 2, 4, 5, 6, 7, 8, 8, 8, 8, 7, 6, 5, 4, 2, +}; + +static s16 D_80AFA13C[] = { + 1, -1, 1, 1, 3, 4, 1, 6, 7, 2, 9, 10, 2, 12, 13, 0, +}; + +static u8 D_80AFA15C[] = { + 2, 2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 3, +}; + +static s8 D_80AFA16C[] = { + 2, 9, 10, 11, 12, 13, 14, 0, 15, -1, 3, 4, 5, 6, 7, 8, -1, 1, 0, 0, +}; + +static Vec3f D_80AFA180[] = { + { -1.0f, 2.0f, -0.2f }, { 0.0f, 2.0f, -0.5f }, { 1.0f, 2.0f, -0.2f }, { -2.0f, 1.0f, -0.5f }, + { -1.0f, 1.0f, -0.2f }, { 0.0f, 1.0f, -0.2f }, { 1.0f, 1.0f, -0.2f }, { 2.0f, 1.0f, -0.5f }, + { -2.0f, 0.0f, -0.5f }, { -1.0f, 0.0f, -0.2f }, { 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, -0.2f }, + { 2.0f, 0.0f, -0.5f }, { -2.0f, -1.0f, -0.5f }, { -1.0f, -1.0f, -0.2f }, { 0.0f, -1.0f, -0.1f }, + { 1.0f, -1.0f, -0.2f }, { 2.0f, -1.0f, -0.5f }, { -1.0f, -2.0f, -0.2f }, { 0.0f, -2.0f, -0.2f }, + { 1.0f, -2.0f, -0.2f }, { 0.0f, -3.0f, -0.5f }, +}; + +// Unused, identical to D_80AFA180 +static Vec3f D_80AFA288[] = { + { -1.0f, 2.0f, -0.2f }, { 0.0f, 2.0f, -0.5f }, { 1.0f, 2.0f, -0.2f }, { -2.0f, 1.0f, -0.5f }, + { -1.0f, 1.0f, -0.2f }, { 0.0f, 1.0f, -0.2f }, { 1.0f, 1.0f, -0.2f }, { 2.0f, 1.0f, -0.5f }, + { -2.0f, 0.0f, -0.5f }, { -1.0f, 0.0f, -0.2f }, { 0.0f, 0.0f, 0.0f }, { 1.0f, 0.0f, -0.2f }, + { 2.0f, 0.0f, -0.5f }, { -2.0f, -1.0f, -0.5f }, { -1.0f, -1.0f, -0.2f }, { 0.0f, -1.0f, -0.1f }, + { 1.0f, -1.0f, -0.2f }, { 2.0f, -1.0f, -0.5f }, { -1.0f, -2.0f, -0.2f }, { 0.0f, -2.0f, -0.2f }, + { 1.0f, -2.0f, -0.2f }, { 0.0f, -3.0f, -0.5f }, +}; + +static u32 D_80AFA390[] = { 0, 0 }; + +#include "overlays/ovl_En_Sda/ovl_En_Sda.h" + +static Vec3f D_80AFA660[16]; + +void EnSda_Init(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnSda_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnSda_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSda* this = (EnSda*)thisx; + Player* player; + + osSyncPrintf("SDA MOVE\n"); + + if (this->actor.params == 1) { + player = (Player*)this->actor.parent; + } else { + player = GET_PLAYER(globalCtx); + } + + this->actor.world.pos = player->actor.world.pos; + + osSyncPrintf("SDA MOVE END\n"); +} + +void EnSda_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSda* this = (EnSda*)thisx; + Player* player; + u8* shadowTexture = Graph_Alloc(globalCtx->state.gfxCtx, 0x1000); + + osSyncPrintf("SDA DRAW \n"); + + if (this->actor.params == 1) { + player = (Player*)this->actor.parent; + } else { + player = GET_PLAYER(globalCtx); + } + + player->actor.shape.shadowAlpha = 0; + func_80AF95C4(this, shadowTexture, player, globalCtx); + + if (KREG(0) < 5) { + func_80AF9C70(shadowTexture, player, globalCtx); + } + + osSyncPrintf("SDA DRAW END\n"); +} + +void func_80AF8F60(Player* player, u8* shadowTexture, f32 arg2) { + s16 temp_t0; + s16 temp_t1; + s16 temp_v1; + s16 temp_v0; + s16 phi_a0; + s16 phi_a3; + s16 i; + s16 j; + Vec3f lerp; + Vec3f sp88; + Vec3f sp7C; + + for (i = 0; i < 16; i++) { + if ((arg2 == 0.0f) || ((j = D_80AFA13C[i]) >= 0)) { + if (arg2 > 0.0f) { + lerp.x = D_80AFA660[i].x + (D_80AFA660[j].x - D_80AFA660[i].x) * arg2; + lerp.y = D_80AFA660[i].y + (D_80AFA660[j].y - D_80AFA660[i].y) * arg2; + lerp.z = D_80AFA660[i].z + (D_80AFA660[j].z - D_80AFA660[i].z) * arg2; + + sp88.x = lerp.x - player->actor.world.pos.x; + sp88.y = lerp.y - player->actor.world.pos.y + BREG(48) + 76.0f + 30.0f - 105.0f + 15.0f; + sp88.z = lerp.z - player->actor.world.pos.z; + } else { + sp88.x = D_80AFA660[i].x - player->actor.world.pos.x; + sp88.y = D_80AFA660[i].y - player->actor.world.pos.y + BREG(48) + 76.0f + 30.0f - 105.0f + 15.0f; + sp88.z = D_80AFA660[i].z - player->actor.world.pos.z; + } + Matrix_MultVec3f(&sp88, &sp7C); + sp7C.x *= (1.0f + (BREG(49) / 100.0f)); + sp7C.y *= (1.0f + (BREG(49) / 100.0f)); + temp_t0 = sp7C.x + 32.0f; + temp_t1 = (s16)sp7C.y << 6; + + if (D_80AFA15C[i] == 2) { + for (j = 0, phi_a3 = -0x180; j < 12; j++, phi_a3 += 0x40) { + for (phi_a0 = -D_80AFA108[j]; phi_a0 < D_80AFA108[j]; phi_a0++) { + temp_v1 = temp_t0 + phi_a0; + if ((temp_v1 >= 0) && (temp_v1 < 0x40)) { + temp_v0 = temp_t1 + phi_a3; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v1 + temp_v0] = 255; + } + } + } + } + } else if (D_80AFA15C[i] == 1) { + for (j = 0, phi_a3 = -0x100; j < 8; j++, phi_a3 += 0x40) { + for (phi_a0 = -D_80AFA0F8[j]; phi_a0 < D_80AFA0F8[j]; phi_a0++) { + temp_v1 = temp_t0 + phi_a0; + if ((temp_v1 >= 0) && (temp_v1 < 0x40)) { + temp_v0 = temp_t1 + phi_a3; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v1 + temp_v0] = 255; + } + } + } + } + } else if (D_80AFA15C[i] == 0) { + for (j = 0, phi_a3 = -0xC0; j < 7; j++, phi_a3 += 0x40) { + for (phi_a0 = -D_80AFA0E8[j]; phi_a0 < D_80AFA0E8[j] - 1; phi_a0++) { + temp_v1 = temp_t0 + phi_a0; + if ((temp_v1 >= 0) && (temp_v1 < 0x40)) { + temp_v0 = temp_t1 + phi_a3; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v1 + temp_v0] = 255; + } + } + } + } + } else if (D_80AFA15C[i] == 4) { + for (j = 0, phi_a3 = -0x1C0; j < 14; j++, phi_a3 += 0x40) { + for (phi_a0 = -D_80AFA120[j]; phi_a0 < D_80AFA120[j]; phi_a0++) { + temp_v1 = temp_t0 + phi_a0; + if ((temp_v1 >= 0) && (temp_v1 < 0x40)) { + temp_v0 = temp_t1 + phi_a3; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v1 + temp_v0] = 255; + } + } + } + } + } else { + for (j = 0, phi_a3 = -0x80; j < 6; j++, phi_a3 += 0x40) { + for (phi_a0 = -D_80AFA0DC[j]; phi_a0 < D_80AFA0DC[j] - 1; phi_a0++) { + temp_v1 = temp_t0 + phi_a0; + if ((temp_v1 >= 0) && (temp_v1 < 0x40)) { + temp_v0 = temp_t1 + phi_a3; + if ((temp_v0 >= 0) && (temp_v0 < 0x1000)) { + shadowTexture[temp_v1 + temp_v0] = 255; + } + } + } + } + } + } + } +} + +void func_80AF95C4(EnSda* this, u8* shadowTexture, Player* player, GlobalContext* globalCtx) { + s16 temp_t0; + s16 temp_t1; + s16 temp_v0; + s16 temp_v1; + s16 phi_a0; + s16 phi_a3; + s16 i; + s16 j; + Vec3f sp194; + Vec3f sp188; + s32* shadowTextureTemp32; + u8* shadowTextureTemp; + Vec3s sp178; + Vec3f sp16C; + Vec3f sp64[22]; + + osSyncPrintf("SDA CONT \n"); + if (BREG(57) != 0) { + for (shadowTextureTemp = shadowTexture, i = 0; i < 0x1000; i++, shadowTextureTemp++) { + if ((i >= 0 && i < 0x40) || (i >= 0xFC0 && i < 0x1000) || ((i & 0x3F) == 0) || ((i & 0x3F) == 0x3F)) { + *shadowTextureTemp = 255; + } else { + *shadowTextureTemp = 0; + } + } + } else { + for (shadowTextureTemp32 = (s32*)shadowTexture, i = 0; i < 0x400; i++, shadowTextureTemp32++) { + *shadowTextureTemp32 = 0; + } + } + Matrix_RotateX((BREG(50) + 70) / 100.0f, MTXMODE_NEW); + for (i = 0; i < 18; i++) { + if (D_80AFA16C[i] >= 0) { + D_80AFA660[D_80AFA16C[i]] = player->bodyPartsPos[i]; + } + } + osSyncPrintf("SDA CONT 2\n"); + D_80AFA660[0].y += 3.0f; + D_80AFA660[15].x = D_80AFA660[0].x + ((D_80AFA660[15].x - D_80AFA660[0].x) * 1.2f); + D_80AFA660[15].y = D_80AFA660[0].y + ((D_80AFA660[15].y - D_80AFA660[0].y) * -1.2f); + D_80AFA660[15].z = D_80AFA660[0].z + ((D_80AFA660[15].z - D_80AFA660[0].z) * 1.2f); + for (i = 0; i < 6; i++) { + func_80AF8F60(player, shadowTexture, i / 5.0f); + } + osSyncPrintf("SDA CONT 3\n"); + if (this->actor.params != 1) { + Matrix_MtxFToYXZRotS(&player->shieldMf, &sp178, false); + sp178.y += (KREG(87) << 0xF) + 0x8000; + sp178.x *= (KREG(88) - 1); + Matrix_Mult(&player->shieldMf, MTXMODE_NEW); + Matrix_MultVec3f(&D_80AFA0D0, &sp16C); + Matrix_RotateY((sp178.y / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_RotateX((sp178.x / 32768.0f) * M_PI, MTXMODE_APPLY); + for (i = 0; i < 22; i++) { + Matrix_MultVec3f(&D_80AFA180[i], &sp188); + if (1) {} + sp64[i].x = (((KREG(82) / 100.0f) + 4.0f) * sp188.x) + sp16C.x; + sp64[i].y = (((KREG(82) / 100.0f) + 4.0f) * sp188.y) + sp16C.y; + sp64[i].z = (((KREG(82) / 100.0f) + 4.0f) * sp188.z) + sp16C.z; + } + Matrix_RotateX((BREG(50) + 70) / 100.0f, MTXMODE_NEW); + for (i = 0; i < 22; i++) { + sp194.x = sp64[i].x - player->actor.world.pos.x; + sp194.y = sp64[i].y - player->actor.world.pos.y + KREG(80) + 16.0f; + sp194.z = sp64[i].z - player->actor.world.pos.z; + Matrix_MultVec3f(&sp194, &sp188); + sp188.x *= (1.0f + (KREG(90) / 100.0f)); + sp188.y *= (1.0f + (KREG(90) / 100.0f)); + temp_t0 = sp188.x + 32.0f; + temp_t1 = (s16)sp188.y << 6; + + do { + for (j = 0, phi_a3 = -0xC0; j < 7; j++, phi_a3 += 0x40) { + for (phi_a0 = -D_80AFA0E8[j]; phi_a0 < D_80AFA0E8[j] - 1; phi_a0++) { + temp_v0 = temp_t0 + phi_a0; + if ((temp_v0 >= 0) && (temp_v0 < 0x40)) { + temp_v1 = temp_t1 + phi_a3; + if ((temp_v1 >= 0) && (temp_v1 < 0x1000)) { + shadowTexture[temp_v0 + temp_v1] = 255; + } + } + } + } + j++; + } while (j < 6); + } + } + if (BREG(61) == 1) { + for (shadowTextureTemp = shadowTexture, i = 0; i < 0x1000; i++, shadowTextureTemp++) { + if (*shadowTextureTemp != 0) { + *shadowTextureTemp = -((i >> 6) * (BREG(60) + 4)) + (255 - BREG(61)); + } + } + } + osSyncPrintf("SDA CONT 4\n"); +} + +void func_80AF9C70(u8* shadowTexture, Player* player, GlobalContext* globalCtx) { + s32 pad; + f32 tempx; + f32 tempz; + s16 phi_s1; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_en_sda.c", 826); + + osSyncPrintf("SDA D 1\n"); + func_80094044(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x00, 0x00, 0, 0, 0, (BREG(52) + 50)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 0); + Matrix_Translate(player->actor.world.pos.x, player->actor.floorHeight, player->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateY(BREG(51) / 100.0f, MTXMODE_APPLY); + Matrix_Scale(1.0f, 1.0f, (BREG(63) / 10.0f) + 1.0f, MTXMODE_APPLY); + tempx = (BREG(62) / 10.0f) + 2.0f; + tempz = ((player->actor.world.pos.y - player->actor.floorHeight + BREG(54)) * (BREG(55) - 5) / 10.0f) + BREG(58) - + 20.0f; + Matrix_Translate(tempx, 0.0f, tempz, MTXMODE_APPLY); + Matrix_Scale(((BREG(56) - 250) / 1000.0f) + 0.6f, 1.0f, ((BREG(59) - 250) / 1000.0f) + 0.6f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_sda.c", 860), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_80AFA3D8); + gDPLoadTextureBlock(POLY_XLU_DISP++, shadowTexture, G_IM_FMT_I, G_IM_SIZ_8b, 0x40, 0x40, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 6, 6, G_TX_NOLOD, G_TX_NOLOD); + gSPDisplayList(POLY_XLU_DISP++, D_80AFA3F8); + + for (phi_s1 = 0; phi_s1 < KREG(78); phi_s1++) { + Matrix_Scale((KREG(79) / 100.0f) + 1.0f, 1.0f, (KREG(79) / 100.0f) + 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_sda.c", 877), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_80AFA3F8); + } + osSyncPrintf("SDA D 2\n"); + CLOSE_DISPS(gfxCtx, "../z_en_sda.c", 882); +} diff --git a/soh/src/overlays/actors/ovl_En_Sda/z_en_sda.h b/soh/src/overlays/actors/ovl_En_Sda/z_en_sda.h new file mode 100644 index 000000000..4dd2abd3a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sda/z_en_sda.h @@ -0,0 +1,13 @@ +#ifndef Z_EN_SDA_H +#define Z_EN_SDA_H + +#include "ultra64.h" +#include "global.h" + +struct EnSda; + +typedef struct EnSda { + /* 0x0000 */ Actor actor; +} EnSda; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c b/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c new file mode 100644 index 000000000..bcac2641f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.c @@ -0,0 +1,308 @@ +#include "z_en_shopnuts.h" +#include "objects/object_shopnuts/object_shopnuts.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnShopnuts_Init(Actor* thisx, GlobalContext* globalCtx); +void EnShopnuts_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnShopnuts_Update(Actor* thisx, GlobalContext* globalCtx); +void EnShopnuts_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnShopnuts_SetupWait(EnShopnuts* this); +void EnShopnuts_Wait(EnShopnuts* this, GlobalContext* globalCtx); +void EnShopnuts_LookAround(EnShopnuts* this, GlobalContext* globalCtx); +void EnShopnuts_Stand(EnShopnuts* this, GlobalContext* globalCtx); +void EnShopnuts_ThrowNut(EnShopnuts* this, GlobalContext* globalCtx); +void EnShopnuts_Burrow(EnShopnuts* this, GlobalContext* globalCtx); +void EnShopnuts_SpawnSalesman(EnShopnuts* this, GlobalContext* globalCtx); + +const ActorInit En_Shopnuts_InitVars = { + ACTOR_EN_SHOPNUTS, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_SHOPNUTS, + sizeof(EnShopnuts), + (ActorFunc)EnShopnuts_Init, + (ActorFunc)EnShopnuts_Destroy, + (ActorFunc)EnShopnuts_Update, + (ActorFunc)EnShopnuts_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 40, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 1, 20, 40, 0xFE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x4E, ICHAIN_CONTINUE), + ICHAIN_F32(gravity, -1, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2600, ICHAIN_STOP), +}; + +void EnShopnuts_Init(Actor* thisx, GlobalContext* globalCtx) { + EnShopnuts* this = (EnShopnuts*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gBusinessScrubSkel, &gBusinessScrubAnim_4574, this->jointTable, + this->morphTable, 18); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (((this->actor.params == 0x0002) && (gSaveContext.itemGetInf[0] & 0x800)) || + ((this->actor.params == 0x0009) && (gSaveContext.infTable[25] & 4)) || + ((this->actor.params == 0x000A) && (gSaveContext.infTable[25] & 8))) { + Actor_Kill(&this->actor); + } else { + EnShopnuts_SetupWait(this); + } +} + +void EnShopnuts_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnShopnuts* this = (EnShopnuts*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnShopnuts_SetupWait(EnShopnuts* this) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gBusinessScrubAnim_139C, 0.0f); + this->animFlagAndTimer = Rand_S16Offset(100, 50); + this->collider.dim.height = 5; + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnShopnuts_Wait; +} + +void EnShopnuts_SetupLookAround(EnShopnuts* this) { + Animation_PlayLoop(&this->skelAnime, &gBusinessScrubLookAroundAnim); + this->animFlagAndTimer = 2; + this->actionFunc = EnShopnuts_LookAround; +} + +void EnShopnuts_SetupThrowNut(EnShopnuts* this) { + Animation_PlayOnce(&this->skelAnime, &gBusinessScrubAnim_1EC); + this->actionFunc = EnShopnuts_ThrowNut; +} + +void EnShopnuts_SetupStand(EnShopnuts* this) { + Animation_MorphToLoop(&this->skelAnime, &gBusinessScrubAnim_4574, -3.0f); + if (this->actionFunc == EnShopnuts_ThrowNut) { + this->animFlagAndTimer = 2 | 0x1000; // sets timer and flag + } else { + this->animFlagAndTimer = 1; + } + this->actionFunc = EnShopnuts_Stand; +} + +void EnShopnuts_SetupBurrow(EnShopnuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBusinessScrubAnim_39C, -5.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DOWN); + this->actionFunc = EnShopnuts_Burrow; +} + +void EnShopnuts_SetupSpawnSalesman(EnShopnuts* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBusinessScrubRotateAnim, -3.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_DAMAGE); + this->collider.base.acFlags &= ~AC_ON; + this->actionFunc = EnShopnuts_SpawnSalesman; +} + +void EnShopnuts_Wait(EnShopnuts* this, GlobalContext* globalCtx) { + s32 hasSlowPlaybackSpeed = false; + + if (this->skelAnime.playSpeed < 0.5f) { + hasSlowPlaybackSpeed = true; + } + if (hasSlowPlaybackSpeed && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (Animation_OnFrame(&this->skelAnime, 9.0f)) { + this->collider.base.acFlags |= AC_ON; + } else if (Animation_OnFrame(&this->skelAnime, 8.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_UP); + } + + this->collider.dim.height = ((CLAMP(this->skelAnime.curFrame, 9.0f, 13.0f) - 9.0f) * 9.0f) + 5.0f; + if (!hasSlowPlaybackSpeed && (this->actor.xzDistToPlayer < 120.0f)) { + EnShopnuts_SetupBurrow(this); + } else if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.xzDistToPlayer < 120.0f) { + EnShopnuts_SetupBurrow(this); + } else if ((this->animFlagAndTimer == 0) && (this->actor.xzDistToPlayer > 320.0f)) { + EnShopnuts_SetupLookAround(this); + } else { + EnShopnuts_SetupStand(this); + } + } + if (hasSlowPlaybackSpeed && + ((this->actor.xzDistToPlayer > 160.0f) && (fabsf(this->actor.yDistToPlayer) < 120.0f)) && + ((this->animFlagAndTimer == 0) || (this->actor.xzDistToPlayer < 480.0f))) { + this->skelAnime.playSpeed = 1.0f; + } +} + +void EnShopnuts_LookAround(EnShopnuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if ((this->actor.xzDistToPlayer < 120.0f) || (this->animFlagAndTimer == 0)) { + EnShopnuts_SetupBurrow(this); + } +} + +void EnShopnuts_Stand(EnShopnuts* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Animation_OnFrame(&this->skelAnime, 0.0f) && (this->animFlagAndTimer != 0)) { + this->animFlagAndTimer--; + } + if (!(this->animFlagAndTimer & 0x1000)) { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + } + if ((this->actor.xzDistToPlayer < 120.0f) || (this->animFlagAndTimer == 0x1000)) { + EnShopnuts_SetupBurrow(this); + } else if (this->animFlagAndTimer == 0) { + EnShopnuts_SetupThrowNut(this); + } +} + +void EnShopnuts_ThrowNut(EnShopnuts* this, GlobalContext* globalCtx) { + Vec3f spawnPos; + + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + if (this->actor.xzDistToPlayer < 120.0f) { + EnShopnuts_SetupBurrow(this); + } else if (SkelAnime_Update(&this->skelAnime)) { + EnShopnuts_SetupStand(this); + } else if (Animation_OnFrame(&this->skelAnime, 6.0f)) { + spawnPos.x = this->actor.world.pos.x + (Math_SinS(this->actor.shape.rot.y) * 23.0f); + spawnPos.y = this->actor.world.pos.y + 12.0f; + spawnPos.z = this->actor.world.pos.z + (Math_CosS(this->actor.shape.rot.y) * 23.0f); + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_NUTSBALL, spawnPos.x, spawnPos.y, spawnPos.z, + this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, 2) != NULL) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_NUTS_THROW); + } + } +} + +void EnShopnuts_Burrow(EnShopnuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnShopnuts_SetupWait(this); + } else { + this->collider.dim.height = ((4.0f - CLAMP_MAX(this->skelAnime.curFrame, 4.0f)) * 10.0f) + 5.0f; + } + if (Animation_OnFrame(&this->skelAnime, 4.0f)) { + this->collider.base.acFlags &= ~AC_ON; + } +} + +void EnShopnuts_SpawnSalesman(EnShopnuts* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_DNS, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, + this->actor.params); + Actor_Kill(&this->actor); + } else { + Math_ApproachS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0xE38); + } +} + +void EnShopnuts_ColliderCheck(EnShopnuts* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + EnShopnuts_SetupSpawnSalesman(this); + } else if (globalCtx->actorCtx.unk_02 != 0) { + EnShopnuts_SetupSpawnSalesman(this); + } +} + +void EnShopnuts_Update(Actor* thisx, GlobalContext* globalCtx) { + EnShopnuts* this = (EnShopnuts*)thisx; + + EnShopnuts_ColliderCheck(this, globalCtx); + this->actionFunc(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, this->collider.dim.radius, this->collider.dim.height, 4); + if (this->collider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actionFunc == EnShopnuts_Wait) { + Actor_SetFocus(&this->actor, this->skelAnime.curFrame); + } else if (this->actionFunc == EnShopnuts_Burrow) { + Actor_SetFocus(&this->actor, + 20.0f - ((this->skelAnime.curFrame * 20.0f) / Animation_GetLastFrame(&gBusinessScrubAnim_39C))); + } else { + Actor_SetFocus(&this->actor, 20.0f); + } +} + +s32 EnShopnuts_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnShopnuts* this = (EnShopnuts*)thisx; + + if ((limbIndex == 9) && (this->actionFunc == EnShopnuts_ThrowNut)) { + *dList = NULL; + } + return 0; +} + +void EnShopnuts_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnShopnuts* this = (EnShopnuts*)thisx; + + f32 curFrame; + f32 x; + f32 y; + f32 z; + + if ((limbIndex == 9) && (this->actionFunc == EnShopnuts_ThrowNut)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_shopnuts.c", 682); + curFrame = this->skelAnime.curFrame; + if (curFrame <= 6.0f) { + y = 1.0f - (curFrame * 0.0833f); + x = z = (curFrame * 0.1167f) + 1.0f; + } else if (curFrame <= 7.0f) { + curFrame -= 6.0f; + y = 0.5f + curFrame; + x = z = 1.7f - (curFrame * 0.7f); + } else if (curFrame <= 10.0f) { + y = 1.5f - ((curFrame - 7.0f) * 0.1667f); + x = z = 1.0f; + } else { + x = y = z = 1.0f; + } + + Matrix_Scale(x, y, z, MTXMODE_APPLY); + if (1) {} + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_shopnuts.c", 714), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBusinessScrubNoseDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_shopnuts.c", 717); + } +} + +void EnShopnuts_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnShopnuts* this = (EnShopnuts*)thisx; + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnShopnuts_OverrideLimbDraw, EnShopnuts_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.h b/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.h new file mode 100644 index 000000000..0b314e0cf --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Shopnuts/z_en_shopnuts.h @@ -0,0 +1,21 @@ +#ifndef Z_EN_SHOPNUTS_H +#define Z_EN_SHOPNUTS_H + +#include "ultra64.h" +#include "global.h" + +struct EnShopnuts; + +typedef void (*EnShopnutsActionFunc)(struct EnShopnuts*, GlobalContext*); + +typedef struct EnShopnuts { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnShopnutsActionFunc actionFunc; + /* 0x0194 */ s16 animFlagAndTimer; // 0x1000 bit denotes that projectile has been thrown + /* 0x0196 */ Vec3s jointTable[18]; + /* 0x0202 */ Vec3s morphTable[18]; + /* 0x0270 */ ColliderCylinder collider; +} EnShopnuts; // size = 0x02BC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.c b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c new file mode 100644 index 000000000..541c6f815 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.c @@ -0,0 +1,154 @@ +/* + * File: z_en_si.c + * Overlay: En_Si + * Description: Gold Skulltula token + */ + +#include "z_en_si.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_9) + +void EnSi_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSi_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSi_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 func_80AFB748(EnSi* this, GlobalContext* globalCtx); +void func_80AFB768(EnSi* this, GlobalContext* globalCtx); +void func_80AFB89C(EnSi* this, GlobalContext* globalCtx); +void func_80AFB950(EnSi* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_NO_PUSH | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000090, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 20, 18, 2, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 D_80AFBADC = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +const ActorInit En_Si_InitVars = { + ACTOR_EN_SI, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_ST, + sizeof(EnSi), + (ActorFunc)EnSi_Init, + (ActorFunc)EnSi_Destroy, + (ActorFunc)EnSi_Update, + (ActorFunc)EnSi_Draw, + NULL, +}; + +void EnSi_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSi* this = (EnSi*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &D_80AFBADC); + Actor_SetScale(&this->actor, 0.025f); + this->unk_19C = 0; + this->actionFunc = func_80AFB768; + this->actor.shape.yOffset = 42.0f; +} + +void EnSi_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSi* this = (EnSi*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80AFB748(EnSi* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + } + return 0; +} + +void func_80AFB768(EnSi* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { + this->actionFunc = func_80AFB89C; + } else { + Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f); + Actor_SetScale(&this->actor, this->actor.scale.x); + this->actor.shape.rot.y += 0x400; + + if (!Player_InCsMode(globalCtx)) { + func_80AFB748(this, globalCtx); + + if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { + this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; + Item_Give(globalCtx, ITEM_SKULL_TOKEN); + player->actor.freezeTimer = 10; + Message_StartTextbox(globalCtx, 0xB4, NULL); + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + this->actionFunc = func_80AFB950; + } else { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + } +} + +void func_80AFB89C(EnSi* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.scale.x, 0.25f, 0.4f, 1.0f, 0.0f); + Actor_SetScale(&this->actor, this->actor.scale.x); + this->actor.shape.rot.y += 0x400; + + if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) { + Item_Give(globalCtx, ITEM_SKULL_TOKEN); + player->actor.freezeTimer = 10; + Message_StartTextbox(globalCtx, 0xB4, NULL); + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + this->actionFunc = func_80AFB950; + } +} + +void func_80AFB950(EnSi* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_CLOSING) { + player->actor.freezeTimer = 10; + } else { + SET_GS_FLAGS((this->actor.params & 0x1F00) >> 8, this->actor.params & 0xFF); + Actor_Kill(&this->actor); + } +} + +void EnSi_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSi* this = (EnSi*)thisx; + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, 16.0f); +} + +void EnSi_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSi* this = (EnSi*)thisx; + + if (this->actionFunc != func_80AFB950) { + func_8002ED80(&this->actor, globalCtx, 0); + func_8002EBCC(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, GID_SKULL_TOKEN_2); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Si/z_en_si.h b/soh/src/overlays/actors/ovl_En_Si/z_en_si.h new file mode 100644 index 000000000..b6dec1237 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Si/z_en_si.h @@ -0,0 +1,18 @@ +#ifndef Z_EN_SI_H +#define Z_EN_SI_H + +#include "ultra64.h" +#include "global.h" + +struct EnSi; + +typedef void (*EnSiActionFunc)(struct EnSi*, GlobalContext*); + +typedef struct EnSi { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnSiActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; + /* 0x019C */ u8 unk_19C; +} EnSi; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Siofuki/z_en_siofuki.c b/soh/src/overlays/actors/ovl_En_Siofuki/z_en_siofuki.c new file mode 100644 index 000000000..060f40c3c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Siofuki/z_en_siofuki.c @@ -0,0 +1,312 @@ +/* + * File: z_en_siofuki.c + * Overlay: ovl_En_Siofuki + * Description: Water Spout + */ + +#include "z_en_siofuki.h" +#include "objects/object_siofuki/object_siofuki.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnSiofuki_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSiofuki_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSiofuki_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSiofuki_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AFC34C(EnSiofuki* this, GlobalContext* globalCtx); +void func_80AFC544(EnSiofuki* this, GlobalContext* globalCtx); +void func_80AFC478(EnSiofuki* this, GlobalContext* globalCtx); + +const ActorInit En_Siofuki_InitVars = { + ACTOR_EN_SIOFUKI, + ACTORCAT_BG, + FLAGS, + OBJECT_SIOFUKI, + sizeof(EnSiofuki), + (ActorFunc)EnSiofuki_Init, + (ActorFunc)EnSiofuki_Destroy, + (ActorFunc)EnSiofuki_Update, + (ActorFunc)EnSiofuki_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_STOP), +}; + +void EnSiofuki_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSiofuki* this = (EnSiofuki*)thisx; + s32 type; + CollisionHeader* colHeader = NULL; + s32 pad; + + if ((thisx->room == 10) && Flags_GetSwitch(globalCtx, 0x1E)) { + Actor_Kill(thisx); + return; + } + + Actor_ProcessInitChain(thisx, sInitChain); + DynaPolyActor_Init(&this->dyna, DPM_PLAYER); + CollisionHeader_GetVirtual(&object_siofuki_Col_000D78, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + this->sfxFlags |= 1; + + type = ((u16)thisx->params >> 0xC) & 0xF; + if (!((type == 0) || (type == 1))) { + Actor_Kill(thisx); + return; + } + + this->initPosY = thisx->world.pos.y; + this->unk_174 = 35.0f; + this->unk_170 = -6058.0f + this->unk_174; + + if (thisx->shape.rot.x != 0) { + this->maxHeight = thisx->shape.rot.x * 40.0f; + this->currentHeight = this->maxHeight; + } + this->activeTime = 0; + if (thisx->shape.rot.y != 0) { + this->activeTime = thisx->shape.rot.y; + } + if (thisx->shape.rot.z != 0) { + thisx->scale.x = thisx->shape.rot.z * (1.0f / 1.73f) * 0.1f; + thisx->scale.z = thisx->shape.rot.z * 0.5f * 0.1f; + } + + thisx->world.rot.x = 0; + thisx->world.rot.y = 0; + thisx->world.rot.z = 0; + thisx->shape.rot.x = 0; + thisx->shape.rot.y = 0; + thisx->shape.rot.z = 0; + + type = ((u16)thisx->params >> 0xC) & 0xF; + if (type == EN_SIOFUKI_RAISING) { + this->currentHeight = 10.0f; + this->targetHeight = 10.0f; + this->actionFunc = func_80AFC34C; + } else if (type == EN_SIOFUKI_LOWERING) { + if (Flags_GetTreasure(globalCtx, (u16)thisx->params & 0x3F)) { + this->currentHeight = -45.0f; + this->targetHeight = -45.0f; + this->actionFunc = func_80AFC544; + } else { + this->targetHeight = this->currentHeight; + this->actionFunc = func_80AFC478; + } + } +} + +void EnSiofuki_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSiofuki* this = (EnSiofuki*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80AFBDC8(EnSiofuki* this, GlobalContext* globalCtx) { + this->oscillation = sinf((globalCtx->gameplayFrames & 0x1F) / 32.0f * M_PI * 2.0f) * 4.0f; + this->unk_170 = this->unk_174 * 10.0f + -6058.0f - this->oscillation * 10.0f; + this->unk_174 = 35.0f; + this->dyna.actor.world.pos.y = this->initPosY + this->currentHeight + this->oscillation; +} + +void func_80AFBE8C(EnSiofuki* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 dX; + f32 dY; + f32 dZ; + s16 angle; + s16 dAngle; + f32 dist2d; + f32 speedScale; + + dX = player->actor.world.pos.x - this->dyna.actor.world.pos.x; + dY = player->actor.world.pos.y - this->dyna.actor.world.pos.y; + dZ = player->actor.world.pos.z - this->dyna.actor.world.pos.z; + + if ((dX > (this->dyna.actor.scale.x * -346.0f)) && (dX < (this->dyna.actor.scale.x * 346.0f)) && + (dZ > (this->dyna.actor.scale.z * -400.0f)) && (dZ < (this->dyna.actor.scale.z * 400.0f)) && (dY < 0.0f)) { + if (func_8004356C(&this->dyna)) { + if (this->splashTimer <= 0) { + EffectSsGSplash_Spawn(globalCtx, &player->actor.world.pos, NULL, NULL, 1, 1); + this->splashTimer = 10; + } else { + this->splashTimer--; + } + + this->applySpeed = false; + this->appliedSpeed = 0.0f; + this->targetAppliedSpeed = 0.0f; + } else { + dist2d = sqrtf(SQ(dX) + SQ(dZ)); + this->applySpeed = true; + this->splashTimer = 0; + angle = Math_FAtan2F(dX, dZ) * (0x8000 / M_PI); + dAngle = (player->actor.world.rot.y ^ 0x8000) - angle; + player->actor.gravity = 0.0f; + player->actor.velocity.y = 0.0f; + Math_SmoothStepToF(&player->actor.world.pos.y, this->dyna.actor.world.pos.y, 0.5f, 4.0f, 1.0f); + + if ((dAngle < 0x4000) && (dAngle > -0x4000)) { + this->appliedYaw = player->actor.world.rot.y ^ 0x8000; + speedScale = dist2d / (this->dyna.actor.scale.x * 40.0f * 10.0f); + speedScale = CLAMP_MIN(speedScale, 0.0f); + speedScale = CLAMP_MAX(speedScale, 1.0f); + player->linearVelocity *= speedScale; + Math_ApproachF(&this->targetAppliedSpeed, 3.0f, 1.0f, 1.0f); + Math_ApproachF(&this->appliedSpeed, this->targetAppliedSpeed, 1.0f, 0.3f * speedScale); + } else { + this->appliedYaw = player->actor.world.rot.y; + player->linearVelocity /= 2.0f; + Math_ApproachF(&this->targetAppliedSpeed, 3.0f, 1.0f, 1.0f); + Math_ApproachF(&this->appliedSpeed, this->targetAppliedSpeed, 1.0f, 0.1f); + } + + player->windDirection = this->appliedYaw; + player->windSpeed = this->appliedSpeed; + } + } else { + if (this->applySpeed) { + player->linearVelocity = this->appliedSpeed + player->linearVelocity; + player->currentYaw = this->appliedYaw; + } + + this->targetAppliedSpeed = 0.0f; + this->appliedSpeed = 0.0f; + this->applySpeed = false; + } +} + +void func_80AFC1D0(EnSiofuki* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->currentHeight, this->targetHeight, 0.8f, 3.0f, 0.01f); +} + +void func_80AFC218(EnSiofuki* this, GlobalContext* globalCtx) { + func_80AFBDC8(this, globalCtx); + func_80AFBE8C(this, globalCtx); + func_80AFC1D0(this, globalCtx); + + this->timer--; + if (this->timer < 0) { + Flags_UnsetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 6) & 0x3F); + switch (((u16)this->dyna.actor.params >> 0xC) & 0xF) { + case EN_SIOFUKI_RAISING: + this->targetHeight = 10.0f; + this->actionFunc = func_80AFC34C; + break; + case EN_SIOFUKI_LOWERING: + this->targetHeight = this->maxHeight; + this->actionFunc = func_80AFC478; + break; + } + } else { + func_8002F994(&this->dyna.actor, this->timer); + } + + if (((((u16)this->dyna.actor.params >> 0xC) & 0xF) == EN_SIOFUKI_LOWERING) && + Flags_GetTreasure(globalCtx, (u16)this->dyna.actor.params & 0x3F)) { + this->currentHeight = -45.0f; + this->targetHeight = -45.0f; + Flags_UnsetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 6) & 0x3F); + this->actionFunc = func_80AFC544; + } +} + +void func_80AFC34C(EnSiofuki* this, GlobalContext* globalCtx) { + func_80AFBDC8(this, globalCtx); + func_80AFBE8C(this, globalCtx); + func_80AFC1D0(this, globalCtx); + + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 6) & 0x3F)) { + this->targetHeight = 400.0f; + this->timer = 300; + this->actionFunc = func_80AFC218; + } +} + +void func_80AFC3C8(EnSiofuki* this, GlobalContext* globalCtx) { + func_80AFBDC8(this, globalCtx); + func_80AFBE8C(this, globalCtx); + func_80AFC1D0(this, globalCtx); + + this->timer--; + if (this->timer < 0) { + this->timer = this->activeTime * 20; + this->targetHeight = -45.0f; + this->actionFunc = func_80AFC218; + } + + if (Flags_GetTreasure(globalCtx, (u16)this->dyna.actor.params & 0x3F)) { + this->currentHeight = -45.0f; + this->targetHeight = -45.0f; + this->actionFunc = func_80AFC544; + } +} + +void func_80AFC478(EnSiofuki* this, GlobalContext* globalCtx) { + func_80AFBDC8(this, globalCtx); + func_80AFBE8C(this, globalCtx); + func_80AFC1D0(this, globalCtx); + + if (((u16)this->dyna.actor.params >> 0xC & 0xF) == EN_SIOFUKI_LOWERING) { + if (Flags_GetSwitch(globalCtx, ((u16)this->dyna.actor.params >> 6) & 0x3F)) { + this->timer = 20; + this->actionFunc = func_80AFC3C8; + OnePointCutscene_Init(globalCtx, 5010, 40, &this->dyna.actor, MAIN_CAM); + } + + if (Flags_GetTreasure(globalCtx, (u16)this->dyna.actor.params & 0x3F)) { + this->currentHeight = -45.0f; + this->targetHeight = -45.0f; + this->actionFunc = func_80AFC544; + } + } +} + +void func_80AFC544(EnSiofuki* this, GlobalContext* globalCtx) { + func_80AFBDC8(this, globalCtx); + func_80AFC1D0(this, globalCtx); +} + +void EnSiofuki_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSiofuki* this = (EnSiofuki*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EnSiofuki_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSiofuki* this = (EnSiofuki*)thisx; + u32 x; + u32 y; + u32 gameplayFrames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_siofuki.c", 654); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Translate(0.0f, this->unk_170, 0.0f, MTXMODE_APPLY); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_siofuki.c", 662), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + x = gameplayFrames * 15; + y = gameplayFrames * -15; + gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, x, y, 64, 64, 1, x, y, 64, 64)); + gSPDisplayList(POLY_XLU_DISP++, object_siofuki_DL_000B70); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_siofuki.c", 674); + + if (this->sfxFlags & 1) { + f32 heightRatio; + switch (((u16)thisx->params >> 0xC) & 0xF) { + case EN_SIOFUKI_RAISING: + heightRatio = (this->currentHeight - 10.0f) / (400.0f - 10.0f); + func_800F436C(&thisx->projectedPos, NA_SE_EV_FOUNTAIN - SFX_FLAG, 1.0f + heightRatio); + break; + case EN_SIOFUKI_LOWERING: + if (this->currentHeight > -35.0f) { + heightRatio = (this->currentHeight - -35.0f) / (this->maxHeight - -35.0f); + func_800F436C(&thisx->projectedPos, NA_SE_EV_FOUNTAIN - SFX_FLAG, 1.0f + heightRatio); + } + break; + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Siofuki/z_en_siofuki.h b/soh/src/overlays/actors/ovl_En_Siofuki/z_en_siofuki.h new file mode 100644 index 000000000..32bad16b7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Siofuki/z_en_siofuki.h @@ -0,0 +1,36 @@ +#ifndef Z_EN_SIOFUKI_H +#define Z_EN_SIOFUKI_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ EN_SIOFUKI_RAISING, + /* 0x01 */ EN_SIOFUKI_LOWERING +} EnSiofukiType; + +struct EnSiofuki; + +typedef void (*EnSiofukiActionFunc)(struct EnSiofuki*, GlobalContext*); + +typedef struct EnSiofuki { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ EnSiofukiActionFunc actionFunc; + /* 0x0168 */ s32 timer; + /* 0x016C */ f32 initPosY; + /* 0x0170 */ f32 unk_170; + /* 0x0174 */ f32 unk_174; + /* 0x0178 */ f32 oscillation; + /* 0x017C */ f32 targetHeight; + /* 0x0180 */ f32 currentHeight; + /* 0x0184 */ s32 splashTimer; + /* 0x0188 */ s32 applySpeed; + /* 0x018C */ f32 appliedSpeed; + /* 0x0190 */ f32 targetAppliedSpeed; + /* 0x0194 */ s16 appliedYaw; + /* 0x0196 */ s16 activeTime; + /* 0x0198 */ f32 maxHeight; + /* 0x019C */ u8 sfxFlags; +} EnSiofuki; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c new file mode 100644 index 000000000..efecd1601 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -0,0 +1,556 @@ +#include "z_en_skb.h" +#include "overlays/actors/ovl_En_Encount1/z_en_encount1.h" +#include "objects/object_skb/object_skb.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnSkb_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSkb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSkb_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSkb_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80AFCD60(EnSkb* this); +void func_80AFCDF8(EnSkb* this); +void func_80AFCE5C(EnSkb* this, GlobalContext* globalCtx); +void func_80AFCF48(EnSkb* this); +void func_80AFCFF0(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD0A4(EnSkb* this); +void EnSkb_Advance(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD33C(EnSkb* this); +void EnSkb_SetupAttack(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD47C(EnSkb* this); +void func_80AFD508(EnSkb* this, GlobalContext* globalCtx); +void EnSkb_SetupStunned(EnSkb* this); +void func_80AFD59C(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD6CC(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD7B4(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD880(EnSkb* this, GlobalContext* globalCtx); +void func_80AFD968(EnSkb* this, GlobalContext* globalCtx); + +static ColliderJntSphElementInit sJntSphElementsInit[2] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { 15, { { 0, 0, 0 }, 10 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 1, { { 0, 0, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 2, + sJntSphElementsInit, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0xF), + /* Slingshot */ DMG_ENTRY(1, 0xF), + /* Explosive */ DMG_ENTRY(2, 0xF), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0xF), + /* Hammer swing */ DMG_ENTRY(2, 0xF), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0xE), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(4, 0x7), + /* Ice arrow */ DMG_ENTRY(2, 0xF), + /* Light arrow */ DMG_ENTRY(2, 0xF), + /* Unk arrow 1 */ DMG_ENTRY(2, 0xF), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x7), + /* Ice magic */ DMG_ENTRY(0, 0x6), + /* Light magic */ DMG_ENTRY(3, 0xD), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0xD), + /* Giant spin */ DMG_ENTRY(4, 0xF), + /* Master spin */ DMG_ENTRY(2, 0xF), + /* Kokiri jump */ DMG_ENTRY(2, 0xF), + /* Giant jump */ DMG_ENTRY(8, 0xF), + /* Master jump */ DMG_ENTRY(4, 0xF), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0xF), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +const ActorInit En_Skb_InitVars = { + ACTOR_EN_SKB, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_SKB, + sizeof(EnSkb), + (ActorFunc)EnSkb_Init, + (ActorFunc)EnSkb_Destroy, + (ActorFunc)EnSkb_Update, + (ActorFunc)EnSkb_Draw, + NULL, +}; + +void EnSkb_SetupAction(EnSkb* this, EnSkbActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnSkb_SpawnDebris(GlobalContext* globalCtx, EnSkb* this, Vec3f* spawnPos) { + Vec3f pos; + Vec3f vel = { 0.0f, 8.0f, 0.0f }; + Vec3f accel = { 0.0f, -1.5f, 0.0f }; + f32 spreadAngle; + f32 scale; + + spreadAngle = (Rand_ZeroOne() - 0.5f) * 6.28f; + pos.y = this->actor.floorHeight; + pos.x = (Math_SinF(spreadAngle) * 15.0f) + spawnPos->x; + pos.z = (Math_CosF(spreadAngle) * 15.0f) + spawnPos->z; + accel.x = Rand_CenteredFloat(1.0f); + accel.z = Rand_CenteredFloat(1.0f); + vel.y += (Rand_ZeroOne() - 0.5f) * 4.0f; + scale = (Rand_ZeroOne() * 5.0f) + 12.0f; + EffectSsHahen_Spawn(globalCtx, &pos, &vel, &accel, 2, scale * 0.8f, -1, 10, 0); + func_80033480(globalCtx, &pos, 10.0f, 1, 150, 0, 1); +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -2000, ICHAIN_STOP), +}; + +void EnSkb_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSkb* this = (EnSkb*)thisx; + s16 paramOffsetBody; + s16 paramOffsetArm; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.colChkInfo.damageTable = &sDamageTable; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + this->actor.focus.pos = this->actor.world.pos; + this->actor.colChkInfo.mass = 0xFE; + this->actor.colChkInfo.health = 2; + this->actor.shape.yOffset = -8000.0f; + SkelAnime_Init(globalCtx, &this->skelAnime, &gStalchildSkel, &gStalchildUncurlingAnim, this->jointTable, + this->morphTable, 20); + this->actor.naviEnemyId = 0x55; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItem); + Actor_SetScale(&this->actor, ((this->actor.params * 0.1f) + 1.0f) * 0.01f); + + paramOffsetBody = this->actor.params + 0xA; + this->collider.elements[0].dim.worldSphere.radius = paramOffsetBody; + this->collider.elements[0].dim.modelSphere.radius = paramOffsetBody; + if (1) {}; + paramOffsetArm = (this->actor.params * 2) + 0x14; + this->collider.elements[1].dim.worldSphere.radius = paramOffsetArm; + this->collider.elements[1].dim.modelSphere.radius = paramOffsetArm; + this->actor.home.pos = this->actor.world.pos; + this->actor.floorHeight = this->actor.world.pos.y; + func_80AFCDF8(this); +} + +void EnSkb_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSkb* this = (EnSkb*)thisx; + + if (this->actor.parent != NULL) { + EnEncount1* spawner = (EnEncount1*)this->actor.parent; + + if (spawner->actor.update != NULL) { + if (spawner->curNumSpawn > 0) { + spawner->curNumSpawn--; + } + } + } + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void func_80AFCD60(EnSkb* this) { + if (IS_DAY) { + func_80AFCF48(this); + } else if (Actor_IsFacingPlayer(&this->actor, 0x11C7) && + (this->actor.xzDistToPlayer < (60.0f + (this->actor.params * 6.0f)))) { + func_80AFD33C(this); + } else { + func_80AFD0A4(this); + } +} + +void func_80AFCDF8(EnSkb* this) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gStalchildUncurlingAnim, 1.0f); + this->unk_280 = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIVA_APPEAR); + EnSkb_SetupAction(this, func_80AFCE5C); +} + +void func_80AFCE5C(EnSkb* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame < 4.0f) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + } else { + this->actor.flags |= ACTOR_FLAG_0; + } + Math_SmoothStepToF(&this->actor.shape.yOffset, 0.0f, 1.0f, 800.0f, 0.0f); + Math_SmoothStepToF(&this->actor.shape.shadowScale, 25.0f, 1.0f, 2.5f, 0.0f); + if ((globalCtx->gameplayFrames & 1) != 0) { + EnSkb_SpawnDebris(globalCtx, this, &this->actor.world.pos); + } + if ((SkelAnime_Update(&this->skelAnime) != 0) && (0.0f == this->actor.shape.yOffset)) { + func_80AFCD60(this); + } +} + +void func_80AFCF48(EnSkb* this) { + Animation_Change(&this->skelAnime, &gStalchildUncurlingAnim, -1.0f, + Animation_GetLastFrame(&gStalchildUncurlingAnim), 0.0f, ANIMMODE_ONCE, -4.0f); + this->unk_280 = 0; + this->unk_281 = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_AKINDONUTS_HIDE); + EnSkb_SetupAction(this, func_80AFCFF0); +} + +void func_80AFCFF0(EnSkb* this, GlobalContext* globalCtx) { + if ((Math_SmoothStepToF(&this->actor.shape.yOffset, -8000.0f, 1.0f, 500.0f, 0.0f) != 0.0f) && + (globalCtx->gameplayFrames & 1)) { + EnSkb_SpawnDebris(globalCtx, this, &this->actor.world.pos); + } + Math_SmoothStepToF(&this->actor.shape.shadowScale, 0.0f, 1.0f, 2.5f, 0.0f); + if (SkelAnime_Update(&this->skelAnime) != 0) { + Actor_Kill(&this->actor); + } +} + +void func_80AFD0A4(EnSkb* this) { + Animation_Change(&this->skelAnime, &gStalchildWalkingAnim, 0.96000004f, 0.0f, + Animation_GetLastFrame(&gStalchildWalkingAnim), ANIMMODE_LOOP, -4.0f); + this->unk_280 = 4; + this->unk_288 = 0; + this->actor.speedXZ = this->actor.scale.y * 160.0f; + EnSkb_SetupAction(this, EnSkb_Advance); +} + +void EnSkb_Advance(EnSkb* this, GlobalContext* globalCtx) { + s32 thisKeyFrame; + s32 prevKeyFrame; + f32 playSpeed; + Player* player = GET_PLAYER(globalCtx); + + if ((this->unk_283 != 0) && ((globalCtx->gameplayFrames & 0xF) == 0)) { + this->unk_288 = Rand_CenteredFloat(50000.0f); + } + Math_SmoothStepToS(&this->actor.shape.rot.y, (this->actor.yawTowardsPlayer + this->unk_288), 1, 0x2EE, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + thisKeyFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + if (this->skelAnime.playSpeed >= 0.0f) { + playSpeed = this->skelAnime.playSpeed; + } else { + playSpeed = -this->skelAnime.playSpeed; + } + prevKeyFrame = (this->skelAnime.curFrame - playSpeed); + if (this->skelAnime.playSpeed >= 0.0f) { + playSpeed = this->skelAnime.playSpeed; + } else { + playSpeed = -this->skelAnime.playSpeed; + } + if (thisKeyFrame != (s32)this->skelAnime.curFrame) { + if (((prevKeyFrame < 9) && (((s32)playSpeed + thisKeyFrame) >= 8)) || + !((prevKeyFrame >= 16) || (((s32)playSpeed + thisKeyFrame) < 15))) { + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_WALK); + } + } + if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) > 800.0f || IS_DAY) { + func_80AFCF48(this); + } else if (Actor_IsFacingPlayer(&this->actor, 0x11C7) && + (this->actor.xzDistToPlayer < (60.0f + (this->actor.params * 6.0f)))) { + func_80AFD33C(this); + } +} + +void func_80AFD33C(EnSkb* this) { + Animation_Change(&this->skelAnime, &gStalchildAttackingAnim, 0.6f, 0.0f, + Animation_GetLastFrame(&gStalchildAttackingAnim), ANIMMODE_ONCE_INTERP, 4.0f); + this->collider.base.atFlags &= ~4; + this->unk_280 = 3; + this->actor.speedXZ = 0.0f; + EnSkb_SetupAction(this, EnSkb_SetupAttack); +} + +void EnSkb_SetupAttack(EnSkb* this, GlobalContext* globalCtx) { + s32 frameData; + + frameData = this->skelAnime.curFrame; + if (frameData == 3) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_ATTACK); + this->unk_281 = 1; + } else if (frameData == 6) { + this->unk_281 = 0; + } + if (this->collider.base.atFlags & 4) { + this->collider.base.atFlags &= ~6; + func_80AFD47C(this); + } else if (SkelAnime_Update(&this->skelAnime) != 0) { + func_80AFCD60(this); + } +} + +void func_80AFD47C(EnSkb* this) { + Animation_Change(&this->skelAnime, &gStalchildAttackingAnim, -0.4f, this->skelAnime.curFrame - 1.0f, 0.0f, + ANIMMODE_ONCE_INTERP, 0.0f); + this->collider.base.atFlags &= ~4; + this->unk_280 = 5; + this->unk_281 = 0; + EnSkb_SetupAction(this, func_80AFD508); +} + +void func_80AFD508(EnSkb* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + func_80AFCD60(this); + } +} + +void EnSkb_SetupStunned(EnSkb* this) { + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = 0.0f; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->unk_281 = 0; + this->unk_280 = 6; + EnSkb_SetupAction(this, func_80AFD59C); +} + +void func_80AFD59C(EnSkb* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + } + if ((this->actor.colorFilterTimer == 0) && (this->actor.bgCheckFlags & 1)) { + if (this->actor.colChkInfo.health == 0) { + func_80AFD7B4(this, globalCtx); + } else { + func_80AFCD60(this); + } + } +} + +void func_80AFD644(EnSkb* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gStalchildDamagedAnim, -4.0f); + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = -4.0f; + } + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALKID_DAMAGE); + this->unk_280 = 2; + EnSkb_SetupAction(this, func_80AFD6CC); +} + +void func_80AFD6CC(EnSkb* this, GlobalContext* globalCtx) { + // this cast is likely not real, but allows for a match + u8* new_var; + + new_var = &this->unk_283; + if ((this->unk_283 != 1) || BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, 1)) { + if ((*new_var) != 0) { + this->unk_283 = (*new_var) | 2; + } + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0; + } + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x1194, 0); + if (SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & 1)) { + func_80AFCD60(this); + } + } +} + +void func_80AFD7B4(EnSkb* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &gStalchildDyingAnim, -4.0f); + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = -6.0f; + } + this->unk_280 = 1; + this->actor.flags &= ~ACTOR_FLAG_0; + BodyBreak_Alloc(&this->bodyBreak, 18, globalCtx); + this->unk_283 |= 4; + EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_STALKID_DEAD, 1, 1, 0x28); + EnSkb_SetupAction(this, func_80AFD880); +} + +void func_80AFD880(EnSkb* this, GlobalContext* globalCtx) { + if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, 1)) { + if (this->actor.scale.x == 0.01f) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x10); + } else if (this->actor.scale.x <= 0.015f) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_RUPEE_BLUE); + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_RUPEE_RED); + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_RUPEE_RED); + Item_DropCollectible(globalCtx, &this->actor.world.pos, ITEM00_RUPEE_RED); + } + + this->unk_283 |= 8; + Actor_Kill(&this->actor); + } +} + +void func_80AFD968(EnSkb* this, GlobalContext* globalCtx) { + s16 pad; + s32 i; + Vec3f flamePos; + s16 scale; + s16 phi_v1; + Player* player; + + if ((this->unk_280 != 1) && (this->actor.bgCheckFlags & 0x60) && (this->actor.yDistToWater >= 40.0f)) { + this->actor.colChkInfo.health = 0; + this->unk_281 = 0; + func_80AFD7B4(this, globalCtx); + } else if (this->unk_280 >= 3) { + if ((this->collider.base.acFlags & 2) != 0) { + this->collider.base.acFlags &= ~2; + if (this->actor.colChkInfo.damageEffect != 6) { + this->unk_282 = this->actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->actor, &this->collider.elements[1].info, 1); + this->unk_281 = 0; + if (this->actor.colChkInfo.damageEffect == 1) { + if (this->unk_280 != 6) { + Actor_SetColorFilter(&this->actor, 0, 0x78, 0, 0x50); + Actor_ApplyDamage(&this->actor); + EnSkb_SetupStunned(this); + } + } else { + phi_v1 = 8; + if (this->actor.colChkInfo.damageEffect == 7) { + scale = this->actor.scale.y * 7500.0f; + for (i = 4; i >= 0; i--) { + flamePos = this->actor.world.pos; + flamePos.x += Rand_CenteredFloat(20.0f); + flamePos.z += Rand_CenteredFloat(20.0f); + flamePos.y += (Rand_ZeroOne() * 25.0f); + EffectSsEnFire_SpawnVec3f(globalCtx, &this->actor, &flamePos, scale, 0, 0, -1); + } + phi_v1 = 25; + } + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, phi_v1); + if (!Actor_ApplyDamage(&this->actor)) { + func_80AFD7B4(this, globalCtx); + return; + } + player = GET_PLAYER(globalCtx); + if (this->unk_283 == 0) { + if ((this->actor.colChkInfo.damageEffect == 0xD) || + ((this->actor.colChkInfo.damageEffect == 0xE) && + ((player->swordAnimation >= 4 && player->swordAnimation <= 11) || + (player->swordAnimation == 20 || player->swordAnimation == 21)))) { + BodyBreak_Alloc(&this->bodyBreak, 2, globalCtx); + this->unk_283 = 1; + } + } + func_80AFD644(this); + } + } + } + } +} + +void EnSkb_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSkb* this = (EnSkb*)thisx; + s32 pad; + + func_80AFD968(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 15.0f, 30.0f, 60.0f, 0x1D); + this->actionFunc(this, globalCtx); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += (3000.0f * this->actor.scale.y); + if (this->unk_281 != 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->unk_280 >= 3) { + if ((this->actor.colorFilterTimer == 0) || ((this->actor.colorFilterParams & 0x4000) == 0)) { + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnSkb_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnSkb* this = (EnSkb*)thisx; + s16 color; + s16 pad[2]; + + if (limbIndex == 11) { + if ((this->unk_283 & 2) == 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_skb.c", 972); + color = ABS((s16)(Math_SinS((globalCtx->gameplayFrames * 0x1770)) * 95.0f)) + 160; + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, color, color, color, 255); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_skb.c", 978); + } else { + *dList = NULL; + } + } else if ((limbIndex == 12) && ((this->unk_283 & 2) != 0)) { + *dList = NULL; + } + return 0; +} + +void EnSkb_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnSkb* this = (EnSkb*)thisx; + + Collider_UpdateSpheres(limbIndex, &this->collider); + + if ((this->unk_283 ^ 1) == 0) { + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 11, 12, 18, dList, BODYBREAK_OBJECT_DEFAULT); + } else if ((this->unk_283 ^ (this->unk_283 | 4)) == 0) { + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 0, 18, 18, dList, BODYBREAK_OBJECT_DEFAULT); + } +} + +void EnSkb_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSkb* this = (EnSkb*)thisx; + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnSkb_OverrideLimbDraw, + EnSkb_PostLimbDraw, &this->actor); +} diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.h b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.h new file mode 100644 index 000000000..142b589c9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.h @@ -0,0 +1,27 @@ +#ifndef Z_EN_SKB_H +#define Z_EN_SKB_H + +#include "ultra64.h" +#include "global.h" + +struct EnSkb; + +typedef void (*EnSkbActionFunc)(struct EnSkb*, GlobalContext*); + +typedef struct EnSkb { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[20]; + /* 0x0208 */ Vec3s morphTable[20]; + /* 0x0280 */ u8 unk_280; + /* 0x0281 */ u8 unk_281; + /* 0x0282 */ u8 unk_282; + /* 0x0283 */ u8 unk_283; + /* 0x0284 */ EnSkbActionFunc actionFunc; + /* 0x0288 */ s16 unk_288; + /* 0x028C */ BodyBreak bodyBreak; + /* 0x02A4 */ ColliderJntSph collider; + /* 0x02C4 */ ColliderJntSphElement colliderItem[2]; +} EnSkb; // size = 0x0344 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c new file mode 100644 index 000000000..f2775c2ca --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.c @@ -0,0 +1,1663 @@ +#include "z_en_skj.h" +#include "overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h" +#include "objects/object_skj/object_skj.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void EnSkj_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSkj_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSkj_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSkj_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnSkj_SariasSongShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx); +void EnSkj_OcarinaMinigameShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx); + +void func_80AFF2A0(EnSkj* this); +void func_80AFF334(EnSkj* this); + +void EnSkj_CalculateCenter(EnSkj* this); +void EnSkj_OcarinaGameSetupWaitForPlayer(EnSkj* this); +void EnSkj_SetupResetFight(EnSkj* this); +void EnSkj_SetupLeaveOcarinaGame(EnSkj* this); +void EnSkj_SetupPlayOcarinaGame(EnSkj* this); +void EnSkj_Backflip(EnSkj* this); +void EnSkj_SetupNeedleRecover(EnSkj* this); +void EnSkj_SetupSpawnDeathEffect(EnSkj* this); +void EnSkj_SetupStand(EnSkj* this); +void EnSkj_SetupWaitForSong(EnSkj* this); +void EnSkj_SetupTalk(EnSkj* this); +void EnSkj_SetupMaskTrade(EnSkj* this); +void EnSkj_SetupWrongSong(EnSkj* this); +void EnSkj_SetupAfterSong(EnSkj* this); +void func_80AFFE24(EnSkj* this); +void EnSkj_SetupPostSariasSong(EnSkj* this); +void EnSkj_JumpFromStump(EnSkj* this); +void EnSkj_SetupWaitForLandAnimFinish(EnSkj* this); +void EnSkj_SetupWalkToPlayer(EnSkj* this); +void EnSkj_SetupWaitForMaskTextClear(EnSkj* this); +void EnSkj_SetupWaitForTextClear(EnSkj* this); +void EnSkj_SetupDie(EnSkj* this); +void func_80AFF1F0(EnSkj* this); +void EnSkj_OfferNextRound(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SetupAskForMask(EnSkj* this, GlobalContext* globalCtx); +f32 EnSkj_GetItemXzRange(EnSkj* this); +s32 EnSkj_CollisionCheck(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SetupTakeMask(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_TurnPlayer(EnSkj* this, Player* player); + +void EnSkj_SetupWaitForOcarina(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_StartOcarinaMinigame(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForOcarina(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForPlayback(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_FailedMiniGame(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WonOcarinaMiniGame(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitToGiveReward(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_GiveOcarinaGameReward(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_FinishOcarinaGameRound(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForNextRound(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForOfferResponse(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SetupWaitForOcarina(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_CleanupOcarinaGame(EnSkj* this, GlobalContext* globalCtx); + +void EnSkj_Fade(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitToShootNeedle(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SariasSongKidIdle(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForDeathAnim(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_PickNextFightAction(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForLandAnim(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_ResetFight(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_Fight(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_NeedleRecover(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SpawnDeathEffect(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitInRange(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForSong(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_AfterSong(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SariaSongTalk(EnSkj* this, GlobalContext* globalCtx); +void func_80AFFE44(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_ChangeModeAfterSong(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_StartMaskTrade(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForLanding(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForLandAnimFinish(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WalkToPlayer(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_AskForMask(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_TakeMask(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WaitForMaskTextClear(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_WrongSong(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_SariasSongWaitForTextClear(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_OcarinaGameWaitForPlayer(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_OcarinaGameIdle(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_PlayOcarinaGame(EnSkj* this, GlobalContext* globalCtx); +void EnSkj_LeaveOcarinaGame(EnSkj* this, GlobalContext* globalCtx); + +void EnSkj_SpawnBlood(GlobalContext* globalCtx, Vec3f* pos); + +void EnSkj_SetupWaitInRange(EnSkj* this); + +#define songFailTimer multiuseTimer +#define battleExitTimer multiuseTimer + +typedef enum { + /* 0 */ SKJ_ANIM_BACKFLIP, + /* 1 */ SKJ_ANIM_SHOOT_NEEDLE, + /* 2 */ SKJ_ANIM_PLAY_FLUTE, + /* 3 */ SKJ_ANIM_DIE, + /* 4 */ SKJ_ANIM_HIT, + /* 5 */ SKJ_ANIM_LAND, + /* 6 */ SKJ_ANIM_LOOK_LEFT_RIGHT, + /* 7 */ SKJ_ANIM_FIGHTING_STANCE, + /* 8 */ SKJ_ANIM_WALK_TO_PLAYER, + /* 9 */ SKJ_ANIM_WAIT +} SkullKidAnim; + +typedef enum { + /* 0 */ SKULL_KID_LEFT, + /* 1 */ SKULL_KID_RIGHT +} SkullKidStumpSide; + +typedef enum { + /* 0 */ SKULL_KID_OCARINA_WAIT, + /* 1 */ SKULL_KID_OCARINA_PLAY_NOTES, + /* 2 */ SKULL_KID_OCARINA_LEAVE_GAME +} SkullKidOcarinaGameState; + +typedef enum { + /* 00 */ SKJ_ACTION_FADE, + /* 01 */ SKJ_ACTION_WAIT_TO_SHOOT_NEEDLE, + /* 02 */ SKJ_ACTION_SARIA_SONG_IDLE, + /* 03 */ SKJ_ACTION_WAIT_FOR_DEATH_ANIM, + /* 04 */ SKJ_ACTION_PICK_NEXT_FIHGT_ACTION, + /* 05 */ SKJ_ACTION_WAIT_FOR_LAND_ANIM, + /* 06 */ SKJ_ACTION_RESET_FIGHT, + /* 07 */ SKJ_ACTION_FIGHT, + /* 08 */ SKJ_ACTION_NEEDLE_RECOVER, + /* 09 */ SKJ_ACTION_SPAWN_DEATH_EFFECT, + /* 10 */ SKJ_ACTION_SARIA_SONG_WAIT_IN_RANGE, + /* 11 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_SONG, + /* 12 */ SKJ_ACTION_SARIA_SONG_AFTER_SONG, + /* 13 */ SKJ_ACTION_SARIA_TALK, + /* 14 */ SKJ_ACTION_UNK14, + /* 15 */ SKJ_ACTION_SARIA_SONG_CHANGE_MODE, + /* 16 */ SKJ_ACTION_SARIA_SONG_START_TRADE, + /* 17 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING, + /* 18 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING_ANIM, + /* 19 */ SKJ_ACTION_SARIA_SONG_WALK_TO_PLAYER, + /* 20 */ SKJ_ACTION_SARIA_SONG_ASK_FOR_MASK, + /* 21 */ SKJ_ACTION_SARIA_SONG_TAKE_MASK, + /* 22 */ SKJ_ACTION_SARIA_SONG_WAIT_MASK_TEXT, + /* 23 */ SKJ_ACTION_SARIA_SONG_WRONG_SONG, + /* 24 */ SKJ_ACTION_SARIA_SONG_WAIT_FOR_TEXT, + /* 25 */ SKJ_ACTION_OCARINA_GAME_WAIT_FOR_PLAYER, + /* 26 */ SKJ_ACTION_OCARINA_GAME_IDLE, + /* 27 */ SKJ_ACTION_OCARINA_GAME_PLAY, + /* 28 */ SKJ_ACTION_OCARINA_GAME_LEAVE +} SkullKidAction; + +typedef struct { + u8 unk0; + EnSkj* skullkid; +} EnSkjUnkStruct; + +static EnSkjUnkStruct sSmallStumpSkullKid = { 0, NULL }; +static EnSkjUnkStruct sOcarinaMinigameSkullKids[] = { { 0, NULL }, { 0, NULL } }; + +const ActorInit En_Skj_InitVars = { + ACTOR_EN_SKJ, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_SKJ, + sizeof(EnSkj), + (ActorFunc)EnSkj_Init, + (ActorFunc)EnSkj_Destroy, + (ActorFunc)EnSkj_Update, + (ActorFunc)EnSkj_Draw, + NULL, +}; + +static ColliderCylinderInitType1 D_80B01678 = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x0, 0x08 }, + { 0xFFCFFFFF, 0x0, 0x0 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 8, 48, 0, { 0, 0, 0 } }, +}; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0xF), + /* Master sword */ DMG_ENTRY(2, 0xF), + /* Giant's Knife */ DMG_ENTRY(4, 0xF), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static s32 sOcarinaGameRewards[] = { + GI_RUPEE_GREEN, + GI_RUPEE_BLUE, + GI_HEART_PIECE, + GI_RUPEE_RED, +}; + +static AnimationMinimalInfo sAnimationInfo[] = { + { &gSkullKidBackflipAnim, ANIMMODE_ONCE, 0.0f }, + { &gSkullKidShootNeedleAnim, ANIMMODE_ONCE, 0.0f }, + { &gSkullKidPlayFluteAnim, ANIMMODE_LOOP, 0.0f }, + { &gSkullKidDieAnim, ANIMMODE_ONCE, 0.0f }, + { &gSkullKidHitAnim, ANIMMODE_ONCE, 0.0f }, + { &gSkullKidLandAnim, ANIMMODE_ONCE, 0.0f }, + { &gSkullKidLookLeftAndRightAnim, ANIMMODE_LOOP, 0.0f }, + { &gSkullKidFightingStanceAnim, ANIMMODE_LOOP, 0.0f }, + { &gSkullKidWalkToPlayerAnim, ANIMMODE_LOOP, 0.0f }, + { &gSkullKidWaitAnim, ANIMMODE_LOOP, 0.0f }, +}; + +static EnSkjActionFunc sActionFuncs[] = { + EnSkj_Fade, + EnSkj_WaitToShootNeedle, + EnSkj_SariasSongKidIdle, + EnSkj_WaitForDeathAnim, + EnSkj_PickNextFightAction, + EnSkj_WaitForLandAnim, + EnSkj_ResetFight, + EnSkj_Fight, + EnSkj_NeedleRecover, + EnSkj_SpawnDeathEffect, + EnSkj_WaitInRange, + EnSkj_WaitForSong, + EnSkj_AfterSong, + EnSkj_SariaSongTalk, + func_80AFFE44, + EnSkj_ChangeModeAfterSong, + EnSkj_StartMaskTrade, + EnSkj_WaitForLanding, + EnSkj_WaitForLandAnimFinish, + EnSkj_WalkToPlayer, + EnSkj_AskForMask, + EnSkj_TakeMask, + EnSkj_WaitForMaskTextClear, + EnSkj_WrongSong, + EnSkj_SariasSongWaitForTextClear, + EnSkj_OcarinaGameWaitForPlayer, + EnSkj_OcarinaGameIdle, + EnSkj_PlayOcarinaGame, + EnSkj_LeaveOcarinaGame, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +static s32 D_80B01EA0; // gets set if ACTOR_FLAG_8 is set + +void EnSkj_ChangeAnim(EnSkj* this, u8 index) { + f32 endFrame = Animation_GetLastFrame(sAnimationInfo[index].animation); + + this->animIndex = index; + Animation_Change(&this->skelAnime, sAnimationInfo[index].animation, 1.0f, 0.0f, endFrame, + sAnimationInfo[index].mode, sAnimationInfo[index].morphFrames); +} + +void EnSkj_SetupAction(EnSkj* this, u8 action) { + this->action = action; + this->actionFunc = sActionFuncs[action]; + + switch (action) { + case SKJ_ACTION_FADE: + case SKJ_ACTION_WAIT_FOR_DEATH_ANIM: + case SKJ_ACTION_PICK_NEXT_FIHGT_ACTION: + case SKJ_ACTION_SPAWN_DEATH_EFFECT: + case SKJ_ACTION_SARIA_SONG_START_TRADE: + case SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING: + case SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING_ANIM: + case SKJ_ACTION_SARIA_SONG_WALK_TO_PLAYER: + case SKJ_ACTION_SARIA_SONG_ASK_FOR_MASK: + case SKJ_ACTION_SARIA_SONG_TAKE_MASK: + case SKJ_ACTION_SARIA_SONG_WAIT_MASK_TEXT: + case SKJ_ACTION_SARIA_SONG_WRONG_SONG: + case SKJ_ACTION_SARIA_SONG_WAIT_FOR_TEXT: + case SKJ_ACTION_OCARINA_GAME_WAIT_FOR_PLAYER: + case SKJ_ACTION_OCARINA_GAME_IDLE: + case SKJ_ACTION_OCARINA_GAME_PLAY: + case SKJ_ACTION_OCARINA_GAME_LEAVE: + this->unk_2D3 = 0; + break; + default: + this->unk_2D3 = 1; + break; + } +} + +void EnSkj_CalculateCenter(EnSkj* this) { + Vec3f mult; + + mult.x = 0.0f; + mult.y = 0.0f; + mult.z = 120.0f; + + Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&mult, &this->center); + + this->center.x += this->actor.world.pos.x; + this->center.z += this->actor.world.pos.z; +} + +void EnSkj_SetNaviId(EnSkj* this) { + switch (this->actor.params) { + case 0: + if (gSaveContext.itemGetInf[3] & 0x200) { + this->actor.naviEnemyId = 0x41; // Skull kid with skull mask + } else if (gSaveContext.itemGetInf[1] & 0x40) { + this->actor.naviEnemyId = 0x40; // Skull kid after Saria's song but no mask + } else { + this->actor.naviEnemyId = 0x3F; // No Sarias song no skull mask + } + break; + + case 1: + case 2: + this->actor.naviEnemyId = 0x3F; + break; + + default: + this->actor.naviEnemyId = 0x36; // Skull kid as adult + break; + } +} + +void EnSkj_Init(Actor* thisx, GlobalContext* globalCtx2) { + s16 type = (thisx->params >> 0xA) & 0x3F; + EnSkj* this = (EnSkj*)thisx; + GlobalContext* globalCtx = globalCtx2; + s32 pad; + Player* player; + + Actor_ProcessInitChain(thisx, sInitChain); + switch (type) { + case 5: // Invisible on the small stump (sarias song)) + sSmallStumpSkullKid.unk0 = 1; + sSmallStumpSkullKid.skullkid = (EnSkj*)thisx; + this->actor.destroy = NULL; + this->actor.draw = NULL; + this->actor.update = EnSkj_SariasSongShortStumpUpdate; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actor.flags |= 0; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_PROP); + break; + + case 6: // Invisible on the short stump (ocarina game) + sSmallStumpSkullKid.unk0 = 1; + sSmallStumpSkullKid.skullkid = (EnSkj*)thisx; + this->actor.destroy = NULL; + this->actor.draw = NULL; + this->actor.update = EnSkj_OcarinaMinigameShortStumpUpdate; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actor.flags |= 0; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_PROP); + this->actor.focus.pos.x = 1230.0f; + this->actor.focus.pos.y = -90.0f; + this->actor.focus.pos.z = 450.0f; + this->actionFunc = EnSkj_SetupWaitForOcarina; + break; + + default: + this->actor.params = type; + if (((this->actor.params != 0) && (this->actor.params != 1)) && (this->actor.params != 2)) { + if (INV_CONTENT(ITEM_TRADE_ADULT) < ITEM_SAW) { + Actor_Kill(&this->actor); + return; + } + } + + EnSkj_SetNaviId(this); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSkullKidSkel, &gSkullKidPlayFluteAnim, this->jointTable, + this->morphTable, 19); + if ((type >= 0) && (type < 3)) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_NPC); + } + + if ((type < 0) || (type >= 7)) { + this->actor.flags &= ~ACTOR_FLAG_25; + } + + if ((type > 0) && (type < 3)) { + this->actor.targetMode = 7; + this->posCopy = this->actor.world.pos; + sOcarinaMinigameSkullKids[type - 1].unk0 = 1; + sOcarinaMinigameSkullKids[type - 1].skullkid = this; + this->minigameState = 0; + this->alpha = 0; + EnSkj_OcarinaGameSetupWaitForPlayer(this); + } else { + this->alpha = 255; + EnSkj_SetupResetFight(this); + } + + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.colChkInfo.health = 10; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &D_80B01678); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 40.0f); + Actor_SetScale(thisx, 0.01f); + this->actor.textId = this->textId = 0; + this->multiuseTimer = 0; + this->backflipFlag = 0; + this->needlesToShoot = 3; + this->hitsUntilDodge = 3; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -1.0f; + EnSkj_CalculateCenter(this); + + player = GET_PLAYER(globalCtx); + osSyncPrintf("Player_X : %f\n", player->actor.world.pos.x); + osSyncPrintf("Player_Z : %f\n", player->actor.world.pos.z); + osSyncPrintf("World_X : %f\n", this->actor.world.pos.x); + osSyncPrintf("World_Z : %f\n", this->actor.world.pos.z); + osSyncPrintf("Center_X : %f\n", this->center.x); + osSyncPrintf("Center_Z : %f\n\n", this->center.z); + + break; + } +} + +void EnSkj_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSkj* this = (EnSkj*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnSkj_RangeCheck(Player* player, EnSkj* this) { + f32 xDiff = player->actor.world.pos.x - this->actor.world.pos.x; + f32 zDiff = player->actor.world.pos.z - this->actor.world.pos.z; + f32 yDiff = player->actor.world.pos.y - this->actor.world.pos.y; + + return (SQ(xDiff) + SQ(zDiff) <= 676.0f) && (yDiff >= 0.0f); +} + +f32 EnSkj_GetItemXzRange(EnSkj* this) { + EnSkj* temp_v0; + f32 zDiff; + f32 xDiff; + + temp_v0 = sSmallStumpSkullKid.skullkid; + xDiff = temp_v0->actor.world.pos.x - this->actor.world.pos.x; + zDiff = temp_v0->actor.world.pos.z - this->actor.world.pos.z; + return sqrtf(SQ(xDiff) + SQ(zDiff)) + 26.0f; +} + +f32 EnSkj_GetItemYRange(EnSkj* this) { + return fabsf(sSmallStumpSkullKid.skullkid->actor.world.pos.y - this->actor.world.pos.y) + 10.0f; +} + +s32 EnSkj_ShootNeedle(EnSkj* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f pos; + Vec3f pos2; + EnSkjneedle* needle; + + pos.x = 1.5f; + pos.y = 0.0f; + pos.z = 40.0f; + + Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&pos, &pos2); + + pos2.x += this->actor.world.pos.x; + pos2.z += this->actor.world.pos.z; + pos2.y = this->actor.world.pos.y + 27.0f; + + needle = (EnSkjneedle*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_SKJNEEDLE, pos2.x, pos2.y, pos2.z, + this->actor.shape.rot.x, this->actor.shape.rot.y, this->actor.shape.rot.z, 0); + if (needle != NULL) { + needle->killTimer = 100; + needle->actor.speedXZ = 24.0f; + return 1; + } + return 0; +} + +void EnSkj_SpawnBlood(GlobalContext* globalCtx, Vec3f* pos) { + EffectSparkInit effect; + s32 sp20; + + effect.position.x = pos->x; + effect.position.y = pos->y; + effect.position.z = pos->z; + effect.uDiv = 5; + effect.vDiv = 5; + + effect.colorStart[0].r = 0; + effect.colorStart[0].g = 0; + effect.colorStart[0].b = 128; + effect.colorStart[0].a = 255; + + effect.colorStart[1].r = 0; + effect.colorStart[1].g = 0; + effect.colorStart[1].b = 128; + effect.colorStart[1].a = 255; + + effect.colorStart[2].r = 0; + effect.colorStart[2].g = 0; + effect.colorStart[2].b = 128; + effect.colorStart[2].a = 255; + + effect.colorStart[3].r = 0; + effect.colorStart[3].g = 0; + effect.colorStart[3].b = 128; + effect.colorStart[3].a = 255; + + effect.colorEnd[0].r = 0; + effect.colorEnd[0].g = 0; + effect.colorEnd[0].b = 32; + effect.colorEnd[0].a = 0; + + effect.colorEnd[1].r = 0; + effect.colorEnd[1].g = 0; + effect.colorEnd[1].b = 32; + effect.colorEnd[1].a = 0; + + effect.colorEnd[2].r = 0; + effect.colorEnd[2].g = 0; + effect.colorEnd[2].b = 64; + effect.colorEnd[2].a = 0; + + effect.colorEnd[3].r = 0; + effect.colorEnd[3].g = 0; + effect.colorEnd[3].b = 64; + effect.colorEnd[3].a = 0; + + effect.speed = 8.0f; + effect.gravity = -1.0f; + + effect.timer = 0; + effect.duration = 8; + + Effect_Add(globalCtx, &sp20, EFFECT_SPARK, 0, 1, &effect); +} + +s32 EnSkj_CollisionCheck(EnSkj* this, GlobalContext* globalCtx) { + s16 yawDiff; + Vec3f effectPos; + + if (!((this->unk_2D3 == 0) || (D_80B01EA0 != 0) || !(this->collider.base.acFlags & AC_HIT))) { + this->collider.base.acFlags &= ~AC_HIT; + if (this->actor.colChkInfo.damageEffect != 0) { + if (this->actor.colChkInfo.damageEffect == 0xF) { + effectPos.x = this->collider.info.bumper.hitPos.x; + effectPos.y = this->collider.info.bumper.hitPos.y; + effectPos.z = this->collider.info.bumper.hitPos.z; + + EnSkj_SpawnBlood(globalCtx, &effectPos); + EffectSsHitMark_SpawnFixedScale(globalCtx, 1, &effectPos); + + yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y; + if ((this->action == 2) || (this->action == 6)) { + if ((yawDiff > 0x6000) || (yawDiff < -0x6000)) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + EnSkj_SetupDie(this); + return 1; + } + } + + Actor_ApplyDamage(&this->actor); + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + + if (this->actor.colChkInfo.health != 0) { + if (this->hitsUntilDodge != 0) { + this->hitsUntilDodge--; + } + if (this->dodgeResetTimer == 0) { + this->dodgeResetTimer = 60; + } + func_80AFF1F0(this); + return 1; + } + EnSkj_SetupDie(this); + return 1; + } + } else { + this->backflipFlag = 1; + EnSkj_Backflip(this); + return 1; + } + } + return 0; +} + +s32 func_80AFEDF8(EnSkj* this, GlobalContext* globalCtx) { + s16 yawDiff; + + if (this->actor.xzDistToPlayer < this->unk_2EC) { + this = this; + if (func_8002DDE4(globalCtx) != 0) { + return 1; + } + } + + yawDiff = this->actor.yawTowardsPlayer - this->actor.world.rot.y; + + if ((yawDiff < this->unk_2C8) && (-this->unk_2C8 < yawDiff)) { + return 1; + } + + return 0; +} + +void EnSkj_Backflip(EnSkj* this) { + this->actor.velocity.y = 8.0f; + this->actor.speedXZ = -8.0f; + + EnSkj_ChangeAnim(this, SKJ_ANIM_BACKFLIP); + EnSkj_SetupAction(this, SKJ_ACTION_FADE); +} + +void EnSkj_Fade(EnSkj* this, GlobalContext* globalCtx) { + u32 alpha = this->alpha; + + if (this->unk_2D6 == 2) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + this->unk_2D6 = 0; + } + + alpha -= 20; + + if (this->backflipFlag != 0) { + if (alpha > 255) { + alpha = 0; + } + + this->alpha = alpha; + this->actor.shape.shadowAlpha = alpha; + } + + if (this->actor.velocity.y <= 0.0f) { + if (this->actor.bgCheckFlags & 2) { + this->actor.bgCheckFlags &= ~2; + func_80AFF2A0(this); + } + } +} + +void EnSkj_SetupWaitToShootNeedle(EnSkj* this) { + this->needlesToShoot = 3; + this->needleShootTimer = 0; + EnSkj_ChangeAnim(this, SKJ_ANIM_SHOOT_NEEDLE); + EnSkj_SetupAction(this, SKJ_ACTION_WAIT_TO_SHOOT_NEEDLE); +} + +void EnSkj_WaitToShootNeedle(EnSkj* this, GlobalContext* globalCtx) { + u8 val; + s16 lastFrame = Animation_GetLastFrame(&gSkullKidShootNeedleAnim); + + if ((this->skelAnime.curFrame == lastFrame) && (this->needleShootTimer == 0)) { + val = this->needlesToShoot; + if (this->needlesToShoot != 0) { + EnSkj_ShootNeedle(this, globalCtx); + this->needleShootTimer = 4; + val--; + this->needlesToShoot = val; + + } else { + EnSkj_SetupNeedleRecover(this); + } + } +} + +void EnSkj_SetupResetFight(EnSkj* this) { + this->unk_2C8 = 0xAAA; + this->unk_2EC = 200.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_PLAY_FLUTE); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_IDLE); +} + +void EnSkj_SariasSongKidIdle(EnSkj* this, GlobalContext* globalCtx) { + if (this->actor.params == 0) { + if (!(gSaveContext.itemGetInf[1] & 0x40) && (this->actor.xzDistToPlayer < 200.0f)) { + this->backflipFlag = 1; + EnSkj_Backflip(this); + } else if (sSmallStumpSkullKid.unk0 != 0) { + Player* player = GET_PLAYER(globalCtx); + if (EnSkj_RangeCheck(player, sSmallStumpSkullKid.skullkid)) { + EnSkj_SetupWaitInRange(this); + player->stateFlags2 |= 0x800000; + player->unk_6A8 = &sSmallStumpSkullKid.skullkid->actor; + } + } + } else { + if (func_80AFEDF8(this, globalCtx) != 0) { + func_80AFF334(this); + } + } +} + +void EnSkj_SetupDie(EnSkj* this) { + EnSkj_ChangeAnim(this, SKJ_ANIM_DIE); + EnSkj_SetupAction(this, SKJ_ACTION_WAIT_FOR_DEATH_ANIM); +} + +void EnSkj_WaitForDeathAnim(EnSkj* this, GlobalContext* globalCtx) { + s16 lastFrame = Animation_GetLastFrame(&gSkullKidDieAnim); + + if (this->skelAnime.curFrame == lastFrame) { + EnSkj_SetupSpawnDeathEffect(this); + } +} + +void func_80AFF1F0(EnSkj* this) { + EnSkj_ChangeAnim(this, SKJ_ANIM_HIT); + EnSkj_SetupAction(this, SKJ_ACTION_PICK_NEXT_FIHGT_ACTION); +} + +void EnSkj_PickNextFightAction(EnSkj* this, GlobalContext* globalCtx) { + s16 lastFrame = Animation_GetLastFrame(&gSkullKidHitAnim); + + if (this->skelAnime.curFrame == lastFrame) { + if (this->hitsUntilDodge == 0) { + this->hitsUntilDodge = 3; + EnSkj_Backflip(this); + } else { + EnSkj_SetupStand(this); + } + } +} + +void func_80AFF2A0(EnSkj* this) { + EnSkj_CalculateCenter(this); + this->actor.speedXZ = 0.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_LAND); + EnSkj_SetupAction(this, SKJ_ACTION_WAIT_FOR_LAND_ANIM); +} + +void EnSkj_WaitForLandAnim(EnSkj* this, GlobalContext* globalCtx) { + s16 lastFrame = Animation_GetLastFrame(&gSkullKidLandAnim); + + if (this->skelAnime.curFrame == lastFrame) { + EnSkj_SetupStand(this); + } +} + +void func_80AFF334(EnSkj* this) { + this->unk_2C8 = 0x2000; + this->battleExitTimer = 400; + this->unk_2EC = 600.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_LOOK_LEFT_RIGHT); + EnSkj_SetupAction(this, SKJ_ACTION_RESET_FIGHT); +} + +void EnSkj_ResetFight(EnSkj* this, GlobalContext* globalCtx) { + if (this->battleExitTimer == 0) { + EnSkj_SetupResetFight(this); + } else if (func_80AFEDF8(this, globalCtx) != 0) { + this->battleExitTimer = 600; + EnSkj_SetupStand(this); + } +} + +void EnSkj_SetupStand(EnSkj* this) { + this->needleShootTimer = 60; + this->unk_2C8 = 0x2000; + this->unk_2F0 = 0.0f; + this->unk_2EC = 600.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_FIGHTING_STANCE); + EnSkj_SetupAction(this, SKJ_ACTION_FIGHT); +} + +void EnSkj_Fight(EnSkj* this, GlobalContext* globalCtx) { + Vec3f pos1; + Vec3f pos2; + s32 pad[3]; + f32 prevPosX; + f32 prevPosZ; + f32 phi_f14; + s16 yawDistToPlayer; + + if (this->needleShootTimer == 0) { + EnSkj_SetupWaitToShootNeedle(this); + } else if (this->battleExitTimer != 0) { + yawDistToPlayer = + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 5, this->unk_2F0, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + Math_ApproachF(&this->unk_2F0, 2000.0f, 1.0f, 200.0f); + + pos1.x = 0.0f; + pos1.y = 0.0f; + pos1.z = -120.0f; + + Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&pos1, &pos2); + prevPosX = this->actor.world.pos.x; + prevPosZ = this->actor.world.pos.z; + if (1) {} + this->actor.world.pos.x = this->center.x + pos2.x; + this->actor.world.pos.z = this->center.z + pos2.z; + + phi_f14 = sqrtf(SQ(this->actor.world.pos.x - prevPosX) + SQ(this->actor.world.pos.z - prevPosZ)); + phi_f14 = CLAMP_MAX(phi_f14, 10.0f); + phi_f14 /= 10.0f; + + this->skelAnime.playSpeed = (yawDistToPlayer < 0) ? -(1.0f + phi_f14) : (1.0f + phi_f14); + + } else if (func_80AFEDF8(this, globalCtx) != 0) { + this->backflipFlag = 1; + EnSkj_Backflip(this); + } else { + EnSkj_SetupResetFight(this); + } +} + +void EnSkj_SetupNeedleRecover(EnSkj* this) { + Animation_Reverse(&this->skelAnime); + EnSkj_SetupAction(this, SKJ_ACTION_NEEDLE_RECOVER); +} + +void EnSkj_NeedleRecover(EnSkj* this, GlobalContext* globalCtx) { + if (this->skelAnime.curFrame == 0.0f) { + EnSkj_SetupStand(this); + } +} + +void EnSkj_SetupSpawnDeathEffect(EnSkj* this) { + this->backflipFlag = 1; + EnSkj_SetupAction(this, SKJ_ACTION_SPAWN_DEATH_EFFECT); +} + +void EnSkj_SpawnDeathEffect(EnSkj* this, GlobalContext* globalCtx) { + Vec3f effectPos; + Vec3f effectVel; + Vec3f effectAccel; + u32 phi_v0; + + phi_v0 = this->alpha - 4; + + if (phi_v0 > 255) { + phi_v0 = 0; + } + this->alpha = phi_v0; + this->actor.shape.shadowAlpha = phi_v0; + + effectPos.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x; + effectPos.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y; + effectPos.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z; + + effectAccel.z = 0.0f; + effectAccel.y = 0.0f; + effectAccel.x = 0.0f; + + effectVel.z = 0.0f; + effectVel.y = 0.0f; + effectVel.x = 0.0f; + + EffectSsDeadDb_Spawn(globalCtx, &effectPos, &effectVel, &effectAccel, 100, 10, 255, 255, 255, 255, 0, 0, 255, 1, 9, + 1); +} + +void EnSkj_SetupWaitInRange(EnSkj* this) { + this->textId = 0x10BC; + + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_IN_RANGE); +} + +void EnSkj_WaitInRange(EnSkj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + // When link pulls out the Ocarina center him on the stump + // Link was probably supposed to be pointed towards skull kid as well + if (player->stateFlags2 & 0x1000000) { + player->stateFlags2 |= 0x2000000; + player->unk_6A8 = &sSmallStumpSkullKid.skullkid->actor; + player->actor.world.pos.x = sSmallStumpSkullKid.skullkid->actor.world.pos.x; + player->actor.world.pos.y = sSmallStumpSkullKid.skullkid->actor.world.pos.y; + player->actor.world.pos.z = sSmallStumpSkullKid.skullkid->actor.world.pos.z; + EnSkj_TurnPlayer(sSmallStumpSkullKid.skullkid, player); + func_8010BD88(globalCtx, OCARINA_ACTION_CHECK_SARIA); + EnSkj_SetupWaitForSong(this); + } else if (D_80B01EA0 != 0) { + player->actor.world.pos.x = sSmallStumpSkullKid.skullkid->actor.world.pos.x; + player->actor.world.pos.y = sSmallStumpSkullKid.skullkid->actor.world.pos.y; + player->actor.world.pos.z = sSmallStumpSkullKid.skullkid->actor.world.pos.z; + if ((Player_GetMask(globalCtx) == PLAYER_MASK_SKULL) && !(gSaveContext.itemGetInf[3] & 0x200)) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + EnSkj_SetupMaskTrade(this); + } else { + EnSkj_SetupTalk(this); + } + } else if (!EnSkj_RangeCheck(player, sSmallStumpSkullKid.skullkid)) { + EnSkj_SetupResetFight(this); + } else { + player->stateFlags2 |= 0x800000; + if (gSaveContext.itemGetInf[1] & 0x40) { + if (gSaveContext.itemGetInf[3] & 0x200) { + this->textId = Text_GetFaceReaction(globalCtx, 0x15); + if (this->textId == 0) { + this->textId = 0x1020; + } + } else if (Player_GetMask(globalCtx) == PLAYER_MASK_NONE) { + this->textId = 0x10BC; + } else if (Player_GetMask(globalCtx) == PLAYER_MASK_SKULL) { + this->textId = 0x101B; + } else { + this->textId = Text_GetFaceReaction(globalCtx, 0x15); + } + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + } + } +} + +void EnSkj_SetupWaitForSong(EnSkj* this) { + this->unk_2D6 = 0; + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_SONG); +} + +void EnSkj_WaitForSong(EnSkj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + // Played a song thats not Saria's song + if (!(gSaveContext.itemGetInf[1] & 0x40) && ((globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_FAIL) || + (globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_FAIL_NO_TEXT))) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + Message_CloseTextbox(globalCtx); + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + EnSkj_SetupWrongSong(this); + } else { + if ((globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) && (this->unk_2D6 == 0)) { + this->unk_2D6 = 1; + EnSkj_ChangeAnim(this, SKJ_ANIM_PLAY_FLUTE); + } else if ((this->unk_2D6 != 0) && (globalCtx->msgCtx.msgMode == MSGMODE_SONG_DEMONSTRATION_DONE)) { + this->unk_2D6 = 0; + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + } + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_00; + this->unk_2D6 = 0; + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_IN_RANGE); + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { + if (!(gSaveContext.itemGetInf[1] & 0x40)) { + // Saria's song has been played for the first titme + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + func_80078884(NA_SE_SY_CORRECT_CHIME); + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + this->textId = 0x10BB; + EnSkj_SetupAfterSong(this); + } else { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_05; + } + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_02) { + player->stateFlags2 &= ~0x1000000; + Actor_Kill(&this->actor); + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_01) { + player->stateFlags2 |= 0x800000; + } else { + if (globalCtx->msgCtx.ocarinaMode >= OCARINA_MODE_05) { + gSaveContext.sunsSongState = 0; + if (gSaveContext.itemGetInf[1] & 0x40) { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + this->textId = 0x10BD; + EnSkj_SetupAfterSong(this); + } else { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + EnSkj_SetupWrongSong(this); + } + } + } + } +} + +void EnSkj_SetupAfterSong(EnSkj* this) { + this->unk_2D6 = 0; + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_AFTER_SONG); +} + +void EnSkj_AfterSong(EnSkj* this, GlobalContext* globalCtx) { + if (D_80B01EA0 != 0) { + EnSkj_SetupTalk(this); + } else { + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + } +} + +void EnSkj_SetupTalk(EnSkj* this) { + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_TALK); +} + +void EnSkj_SariaSongTalk(EnSkj* this, GlobalContext* globalCtx) { + s32 pad; + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + if (gSaveContext.itemGetInf[1] & 0x40) { + EnSkj_SetupWaitInRange(this); + } else { + func_80AFFE24(this); + func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, EnSkj_GetItemXzRange(this), + EnSkj_GetItemYRange(this)); + } + } +} + +void func_80AFFE24(EnSkj* this) { + EnSkj_SetupAction(this, SKJ_ACTION_UNK14); +} + +void func_80AFFE44(EnSkj* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + EnSkj_SetupPostSariasSong(this); + } else { + func_8002F434(&this->actor, globalCtx, GI_HEART_PIECE, EnSkj_GetItemXzRange(this), EnSkj_GetItemYRange(this)); + } +} + +void EnSkj_SetupPostSariasSong(EnSkj* this) { + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_CHANGE_MODE); +} + +void EnSkj_ChangeModeAfterSong(EnSkj* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + gSaveContext.itemGetInf[1] |= 0x40; + EnSkj_SetNaviId(this); + EnSkj_SetupWaitInRange(this); + } +} + +void EnSkj_SetupMaskTrade(EnSkj* this) { + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_START_TRADE); +} + +void EnSkj_StartMaskTrade(EnSkj* this, GlobalContext* globalCtx) { + u8 sp1F = Message_GetState(&globalCtx->msgCtx); + + func_8002DF54(globalCtx, &this->actor, 1); + if ((sp1F == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + EnSkj_JumpFromStump(this); + } +} + +void EnSkj_JumpFromStump(EnSkj* this) { + this->actor.velocity.y = 8.0f; + this->actor.speedXZ = 2.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_BACKFLIP); + Animation_Reverse(&this->skelAnime); + this->skelAnime.curFrame = this->skelAnime.startFrame; + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING); +} + +void EnSkj_WaitForLanding(EnSkj* this, GlobalContext* globalCtx) { + if (this->actor.velocity.y <= 0.0f) { + if (this->actor.bgCheckFlags & 2) { + this->actor.bgCheckFlags &= ~2; + this->actor.speedXZ = 0.0f; + EnSkj_SetupWaitForLandAnimFinish(this); + } + } +} + +void EnSkj_SetupWaitForLandAnimFinish(EnSkj* this) { + EnSkj_ChangeAnim(this, SKJ_ANIM_LAND); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_LANDING_ANIM); +} + +void EnSkj_WaitForLandAnimFinish(EnSkj* this, GlobalContext* globalCtx) { + s16 lastFrame = Animation_GetLastFrame(&gSkullKidLandAnim); + + if (this->skelAnime.curFrame == lastFrame) { + EnSkj_SetupWalkToPlayer(this); + } +} + +void EnSkj_SetupWalkToPlayer(EnSkj* this) { + this->unk_2F0 = 0.0f; + this->actor.speedXZ = 2.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_WALK_TO_PLAYER); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WALK_TO_PLAYER); +} + +void EnSkj_WalkToPlayer(EnSkj* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xA, this->unk_2F0, 0); + Math_ApproachF(&this->unk_2F0, 2000.0f, 1.0f, 100.0f); + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->actor.xzDistToPlayer < 120.0f) { + this->actor.speedXZ = 0.0f; + EnSkj_SetupAskForMask(this, globalCtx); + } +} + +void EnSkj_SetupAskForMask(EnSkj* this, GlobalContext* globalCtx) { + Message_StartTextbox(globalCtx, 0x101C, &this->actor); + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_ASK_FOR_MASK); +} + +void EnSkj_AskForMask(EnSkj* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // Yes + EnSkj_SetupTakeMask(this, globalCtx); + break; + case 1: // No + Message_ContinueTextbox(globalCtx, 0x101D); + EnSkj_SetupWaitForMaskTextClear(this); + break; + } + } +} + +void EnSkj_SetupTakeMask(EnSkj* this, GlobalContext* globalCtx) { + Message_ContinueTextbox(globalCtx, 0x101E); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_TAKE_MASK); +} + +void EnSkj_TakeMask(EnSkj* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + Rupees_ChangeBy(10); + gSaveContext.itemGetInf[3] |= 0x200; + EnSkj_SetNaviId(this); + Player_UnsetMask(globalCtx); + Item_Give(globalCtx, ITEM_SOLD_OUT); + Message_ContinueTextbox(globalCtx, 0x101F); + EnSkj_SetupWaitForMaskTextClear(this); + } +} + +void EnSkj_SetupWaitForMaskTextClear(EnSkj* this) { + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_MASK_TEXT); +} + +void EnSkj_WaitForMaskTextClear(EnSkj* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + func_8002DF54(globalCtx, &this->actor, 7); + this->backflipFlag = 1; + EnSkj_Backflip(this); + } +} + +void EnSkj_SetupWrongSong(EnSkj* this) { + this->textId = 0x1041; + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WRONG_SONG); +} + +void EnSkj_WrongSong(EnSkj* this, GlobalContext* globalCtx) { + if (D_80B01EA0 != 0) { + EnSkj_SetupWaitForTextClear(this); + } else { + func_8002F2CC(&this->actor, globalCtx, EnSkj_GetItemXzRange(this)); + } +} + +void EnSkj_SetupWaitForTextClear(EnSkj* this) { + EnSkj_SetupAction(this, SKJ_ACTION_SARIA_SONG_WAIT_FOR_TEXT); +} + +void EnSkj_SariasSongWaitForTextClear(EnSkj* this, GlobalContext* globalCtx) { + u8 state = Message_GetState(&globalCtx->msgCtx); + Player* player = GET_PLAYER(globalCtx); + + if (state == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + EnSkj_SetupWaitInRange(this); + player->stateFlags2 |= 0x800000; + player->unk_6A8 = (Actor*)sSmallStumpSkullKid.skullkid; + } +} + +void EnSkj_OcarinaGameSetupWaitForPlayer(EnSkj* this) { + this->actor.flags &= ~ACTOR_FLAG_0; + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_WAIT_FOR_PLAYER); +} + +void EnSkj_OcarinaGameWaitForPlayer(EnSkj* this, GlobalContext* globalCtx) { + if (this->playerInRange) { + this->actor.flags |= ACTOR_FLAG_0; + EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_IDLE); + } +} + +s32 EnSkj_IsLeavingGame(EnSkj* this) { + s32 paramDecr = this->actor.params - 1; + + if (sOcarinaMinigameSkullKids[paramDecr].unk0 == 2) { + EnSkj_SetupLeaveOcarinaGame(this); + return true; + } + return false; +} + +void EnSkj_SetupIdle(EnSkj* this) { + EnSkj_ChangeAnim(this, SKJ_ANIM_WAIT); + EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_IDLE); +} + +void EnSkj_Appear(EnSkj* this) { + if (this->alpha != 255) { + this->alpha += 20; + + if (this->alpha > 255) { + this->alpha = 255; + } + } +} + +void EnSkj_OcarinaGameIdle(EnSkj* this, GlobalContext* globalCtx) { + EnSkj_Appear(this); + + if ((EnSkj_IsLeavingGame(this) == false) && (this->minigameState != 0)) { + EnSkj_SetupPlayOcarinaGame(this); + } +} + +void EnSkj_SetupPlayOcarinaGame(EnSkj* this) { + EnSkj_ChangeAnim(this, SKJ_ANIM_PLAY_FLUTE); + EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_PLAY); +} + +void EnSkj_PlayOcarinaGame(EnSkj* this, GlobalContext* globalCtx) { + EnSkj_Appear(this); + + if (!EnSkj_IsLeavingGame(this) && (this->minigameState == 0)) { + EnSkj_SetupIdle(this); + } +} + +void EnSkj_SetupLeaveOcarinaGame(EnSkj* this) { + this->actor.velocity.y = 8.0f; + this->actor.speedXZ = -8.0f; + EnSkj_ChangeAnim(this, SKJ_ANIM_BACKFLIP); + EnSkj_SetupAction(this, SKJ_ACTION_OCARINA_GAME_LEAVE); +} + +void EnSkj_LeaveOcarinaGame(EnSkj* this, GlobalContext* globalCtx) { + s32 paramsDecr = this->actor.params - 1; + + sOcarinaMinigameSkullKids[paramsDecr].unk0 = 0; + sOcarinaMinigameSkullKids[paramsDecr].skullkid = NULL; + this->backflipFlag = 1; + EnSkj_Backflip(this); +} + +void EnSkj_Update(Actor* thisx, GlobalContext* globalCtx) { + Vec3f dropPos; + s32 pad; + EnSkj* this = (EnSkj*)thisx; + + D_80B01EA0 = Actor_ProcessTalkRequest(&this->actor, globalCtx); + + this->timer++; + + if (this->multiuseTimer != 0) { + this->multiuseTimer--; + } + + if (this->needleShootTimer != 0) { + this->needleShootTimer--; + } + + if (this->dodgeResetTimer != 0) { + this->dodgeResetTimer--; + } + + if (this->dodgeResetTimer == 0) { + this->hitsUntilDodge = 3; + } + + if ((this->backflipFlag != 0) && (this->alpha == 0)) { + if (this->action == 9) { + dropPos.x = this->actor.world.pos.x; + dropPos.y = this->actor.world.pos.y; + dropPos.z = this->actor.world.pos.z; + + Item_DropCollectible(globalCtx, &dropPos, ITEM00_RUPEE_ORANGE); + } + Actor_Kill(&this->actor); + return; + } + + Actor_SetFocus(&this->actor, 30.0f); + Actor_SetScale(&this->actor, 0.01f); + this->actionFunc(this, globalCtx); + this->actor.textId = this->textId; + EnSkj_CollisionCheck(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->collider); + + if ((this->unk_2D3 != 0) && (D_80B01EA0 == 0)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->actor.colorFilterTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 7); +} + +void EnSkj_SariasSongShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx) { + EnSkj* this = (EnSkj*)thisx; + + D_80B01EA0 = Actor_ProcessTalkRequest(&this->actor, globalCtx); + + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } +} + +void EnSkj_TurnPlayer(EnSkj* this, Player* player) { + Math_SmoothStepToS(&player->actor.shape.rot.y, this->actor.world.rot.y, 5, 2000, 0); + player->actor.world.rot.y = player->actor.shape.rot.y; + player->currentYaw = player->actor.shape.rot.y; +} + +void EnSkj_SetupWaitForOcarina(EnSkj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (EnSkj_RangeCheck(player, this)) { + sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->playerInRange = true; + sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid->playerInRange = true; + + if (player->stateFlags2 & 0x1000000) { + player->stateFlags2 |= 0x2000000; + func_800F5BF0(NATURE_ID_KOKIRI_REGION); + EnSkj_TurnPlayer(this, player); + player->unk_6A8 = &this->actor; + Message_StartTextbox(globalCtx, 0x10BE, &this->actor); + this->actionFunc = EnSkj_StartOcarinaMinigame; + } else { + this->actionFunc = EnSkj_WaitForOcarina; + } + } +} + +void EnSkj_WaitForOcarina(EnSkj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (player->stateFlags2 & 0x1000000) { + player->stateFlags2 |= 0x2000000; + func_800F5BF0(NATURE_ID_KOKIRI_REGION); + EnSkj_TurnPlayer(this, player); + player->unk_6A8 = &this->actor; + Message_StartTextbox(globalCtx, 0x10BE, &this->actor); + this->actionFunc = EnSkj_StartOcarinaMinigame; + } else if (EnSkj_RangeCheck(player, this)) { + player->stateFlags2 |= 0x800000; + } +} + +void EnSkj_StartOcarinaMinigame(EnSkj* this, GlobalContext* globalCtx) { + u8 dialogState = Message_GetState(&globalCtx->msgCtx); + Player* player = GET_PLAYER(globalCtx); + + EnSkj_TurnPlayer(this, player); + + if (dialogState == TEXT_STATE_CLOSING) { + func_8010BD58(globalCtx, OCARINA_ACTION_MEMORY_GAME); + if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_PLAY_NOTES; + } + this->songFailTimer = 160; + this->actionFunc = EnSkj_WaitForPlayback; + } +} + +void EnSkj_WaitForPlayback(EnSkj* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + EnSkj_TurnPlayer(this, player); + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_03) { // failed the game + Message_CloseTextbox(globalCtx); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, 26.0f); + this->textId = 0x102D; + this->actionFunc = EnSkj_FailedMiniGame; + } else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_0F) { // completed the game + func_80078884(NA_SE_SY_CORRECT_CHIME); + Message_CloseTextbox(globalCtx); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, 26.0f); + this->textId = 0x10BF; + this->actionFunc = EnSkj_WonOcarinaMiniGame; + } else { // playing the game + switch (globalCtx->msgCtx.msgMode) { + case MSGMODE_MEMORY_GAME_LEFT_SKULLKID_WAIT: + if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = SKULL_KID_OCARINA_WAIT; + } + if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) { + if (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid->minigameState = + SKULL_KID_OCARINA_PLAY_NOTES; + } + Message_UpdateOcarinaGame(globalCtx); + } + break; + case MSGMODE_MEMORY_GAME_RIGHT_SKULLKID_WAIT: + if (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid->minigameState = SKULL_KID_OCARINA_WAIT; + } + if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) { + Message_UpdateOcarinaGame(globalCtx); + this->songFailTimer = 160; + } + break; + case MSGMODE_MEMORY_GAME_PLAYER_PLAYING: + if (this->songFailTimer != 0) { + this->songFailTimer--; + } else { // took too long, game failed + func_80078884(NA_SE_SY_OCARINA_ERROR); + Message_CloseTextbox(globalCtx); + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + player->unk_6A8 = &this->actor; + func_8002F2CC(&this->actor, globalCtx, 26.0f); + this->textId = 0x102D; + this->actionFunc = EnSkj_FailedMiniGame; + } + break; + case MSGMODE_MEMORY_GAME_START_NEXT_ROUND: + if (!Audio_IsSfxPlaying(NA_SE_SY_METRONOME)) { + if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid->minigameState = + SKULL_KID_OCARINA_PLAY_NOTES; + } + this->songFailTimer = 160; + Audio_OcaSetInstrument(6); // related instrument sound (flute?) + Audio_OcaSetSongPlayback(OCARINA_SONG_MEMORY_GAME + 1, 1); + globalCtx->msgCtx.msgMode = MSGMODE_MEMORY_GAME_LEFT_SKULLKID_PLAYING; + globalCtx->msgCtx.stateTimer = 2; + } + break; + } + } +} + +void EnSkj_FailedMiniGame(EnSkj* this, GlobalContext* globalCtx) { + if (D_80B01EA0) { + this->actionFunc = EnSkj_WaitForNextRound; + } else { + func_8002F2CC(&this->actor, globalCtx, 26.0f); + } +} + +void EnSkj_WaitForNextRound(EnSkj* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + EnSkj_OfferNextRound(this, globalCtx); + } +} + +void EnSkj_OfferNextRound(EnSkj* this, GlobalContext* globalCtx) { + Message_ContinueTextbox(globalCtx, 0x102E); + this->actionFunc = EnSkj_WaitForOfferResponse; +} + +void EnSkj_WaitForOfferResponse(EnSkj* this, GlobalContext* globalCtx) { + Player* player; + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // yes + player = GET_PLAYER(globalCtx); + player->stateFlags3 |= 0x20; // makes player take ocarina out right away after closing box + this->actionFunc = EnSkj_SetupWaitForOcarina; + break; + case 1: // no + this->actionFunc = EnSkj_CleanupOcarinaGame; + break; + } + } +} + +void EnSkj_WonOcarinaMiniGame(EnSkj* this, GlobalContext* globalCtx) { + if (D_80B01EA0) { + this->actionFunc = EnSkj_WaitToGiveReward; + } else { + func_8002F2CC(&this->actor, globalCtx, 26.0f); + } +} + +void EnSkj_WaitToGiveReward(EnSkj* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + func_8002F434(&this->actor, globalCtx, sOcarinaGameRewards[gSaveContext.ocarinaGameRoundNum], 26.0f, 26.0f); + this->actionFunc = EnSkj_GiveOcarinaGameReward; + } +} + +void EnSkj_GiveOcarinaGameReward(EnSkj* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = EnSkj_FinishOcarinaGameRound; + } else { + func_8002F434(&this->actor, globalCtx, sOcarinaGameRewards[gSaveContext.ocarinaGameRoundNum], 26.0f, 26.0f); + } +} + +void EnSkj_FinishOcarinaGameRound(EnSkj* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + s32 ocarinaGameRoundNum = gSaveContext.ocarinaGameRoundNum; + + if (gSaveContext.ocarinaGameRoundNum < 3) { + gSaveContext.ocarinaGameRoundNum++; + } + + if (ocarinaGameRoundNum == 2) { + gSaveContext.itemGetInf[1] |= 0x80; + this->actionFunc = EnSkj_CleanupOcarinaGame; + } else { + EnSkj_OfferNextRound(this, globalCtx); + } + } +} + +void EnSkj_CleanupOcarinaGame(EnSkj* this, GlobalContext* globalCtx) { + if (sOcarinaMinigameSkullKids[SKULL_KID_LEFT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_LEFT].unk0 = 2; + } + + if (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].skullkid != NULL) { + sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].unk0 = 2; + } + + if ((sOcarinaMinigameSkullKids[SKULL_KID_LEFT].unk0 == 2) && + (sOcarinaMinigameSkullKids[SKULL_KID_RIGHT].unk0 == 2)) { + func_800F5C2C(); + Actor_Kill(&this->actor); + } +} + +void EnSkj_OcarinaMinigameShortStumpUpdate(Actor* thisx, GlobalContext* globalCtx) { + EnSkj* this = (EnSkj*)thisx; + + D_80B01EA0 = Actor_ProcessTalkRequest(&this->actor, globalCtx); + this->timer++; + + this->actor.focus.pos.x = 1230.0f; + this->actor.focus.pos.y = -90.0f; + this->actor.focus.pos.z = 450.0f; + + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } + + this->actionFunc(this, globalCtx); + + this->actor.textId = this->textId; + this->actor.xzDistToPlayer = 50.0; +} + +s32 EnSkj_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + return 0; +} + +void EnSkj_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2417); + + if ((limbIndex == 11) && (gSaveContext.itemGetInf[3] & 0x200)) { + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Push(); + Matrix_RotateZYX(-0x4000, 0, 0, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_skj.c", 2430), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gSKJskullMaskDL); + Matrix_Pop(); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2437); +} + +Gfx* EnSkj_TranslucentDL(GraphicsContext* gfxCtx, u32 alpha) { + Gfx* dList; + Gfx* dListHead; + + //! @bug This only allocates space for 1 command but uses 3 + dList = dListHead = Graph_Alloc(gfxCtx, sizeof(Gfx)); + gDPSetRenderMode(dListHead++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2); + gDPSetEnvColor(dListHead++, 0, 0, 0, alpha); + gSPEndDisplayList(dListHead++); + + return dList; +} + +Gfx* EnSkj_OpaqueDL(GraphicsContext* gfxCtx, u32 alpha) { + Gfx* dList; + Gfx* dListHead; + + //! @bug This only allocates space for 1 command but uses 2 + dList = dListHead = Graph_Alloc(gfxCtx, sizeof(Gfx)); + gDPSetEnvColor(dListHead++, 0, 0, 0, alpha); + gSPEndDisplayList(dListHead++); + + return dList; +} + +void EnSkj_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSkj* this = (EnSkj*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2475); + + func_80093D18(globalCtx->state.gfxCtx); + + if (this->alpha < 255) { + gSPSegment(POLY_OPA_DISP++, 0x0C, EnSkj_TranslucentDL(globalCtx->state.gfxCtx, this->alpha)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x0C, EnSkj_OpaqueDL(globalCtx->state.gfxCtx, this->alpha)); + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnSkj_OverrideLimbDraw, EnSkj_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_skj.c", 2495); +} diff --git a/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.h b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.h new file mode 100644 index 000000000..09b806de6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Skj/z_en_skj.h @@ -0,0 +1,40 @@ +#ifndef Z_EN_SKJ_H +#define Z_EN_SKJ_H + +#include "ultra64.h" +#include "global.h" + +struct EnSkj; + +typedef void (*EnSkjActionFunc)(struct EnSkj*, GlobalContext*); + +typedef struct EnSkj { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[19]; + /* 0x0202 */ Vec3s morphTable[19]; + /* 0x0274 */ EnSkjActionFunc actionFunc; + /* 0x0278 */ ColliderCylinder collider; + /* 0x02C4 */ u16 textId; + /* 0x02C6 */ s16 timer; + /* 0x02C8 */ s16 unk_2C8; + /* 0x02CA */ s16 multiuseTimer; + /* 0x02CC */ s16 needleShootTimer; + /* 0x02CE */ s16 dodgeResetTimer; + /* 0x02D0 */ u8 animIndex; + /* 0x02D1 */ u8 action; + /* 0x02D2 */ u8 backflipFlag; + /* 0x02D3 */ u8 unk_2D3; // Is set to zero when walking to trade for skull mask and set to 1 when dying + /* 0x02D4 */ u8 needlesToShoot; + /* 0x02D5 */ u8 hitsUntilDodge; // Upon reaching zero will always backflip to avoid stun lock + /* 0x02D6 */ u8 unk_2D6; + /* 0x02D7 */ u8 playerInRange; + /* 0x02D8 */ u8 minigameState; // Upon reaching zero stops and does another action + /* 0x02DC */ u32 alpha; + /* 0x02E0 */ Vec3f center; + /* 0x02EC */ f32 unk_2EC; + /* 0x02F0 */ f32 unk_2F0; + /* 0x02F4 */ Vec3f posCopy; +} EnSkj; // size = 0x0300 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.c b/soh/src/overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.c new file mode 100644 index 000000000..767e088a1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.c @@ -0,0 +1,112 @@ +/* + * File: z_en_skjneedle.c + * Overlay: ovl_En_Skjneedle + * Description: Skullkid Needle Attack + */ + +#include "z_en_skjneedle.h" +#include "objects/object_skj/object_skj.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_9) + +void EnSkjneedle_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSkjneedle_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSkjneedle_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSkjneedle_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnSkjNeedle_CollisionCheck(EnSkjneedle* this); + +const ActorInit En_Skjneedle_InitVars = { + ACTOR_EN_SKJNEEDLE, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_SKJ, + sizeof(EnSkjneedle), + (ActorFunc)EnSkjneedle_Init, + (ActorFunc)EnSkjneedle_Destroy, + (ActorFunc)EnSkjneedle_Update, + (ActorFunc)EnSkjneedle_Draw, + NULL, +}; + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_HIT1, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 10, 4, -2, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 30, ICHAIN_STOP), +}; + +void EnSkjneedle_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSkjneedle* this = (EnSkjneedle*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); + ActorShape_Init(&this->actor.shape, 0, ActorShadow_DrawCircle, 20.0f); + thisx->flags &= ~ACTOR_FLAG_0; + Actor_SetScale(&this->actor, 0.01f); +} + +void EnSkjneedle_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSkjneedle* this = (EnSkjneedle*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnSkjNeedle_CollisionCheck(EnSkjneedle* this) { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + return 1; + } + return 0; +} + +void EnSkjneedle_Update(Actor* thisx, GlobalContext* globalCtx2) { + EnSkjneedle* this = (EnSkjneedle*)thisx; + GlobalContext* globalCtx = globalCtx2; + + this->unusedTimer1++; + if (this->killTimer != 0) { + this->killTimer--; + } + if (EnSkjNeedle_CollisionCheck(this) || this->killTimer == 0) { + Actor_Kill(&this->actor); + } else { + Actor_SetScale(&this->actor, 0.01f); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 20.0f, 7); + } +} + +void EnSkjneedle_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_skj_needle.c", 200); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_skj_needle.c", 205), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gSKJNeedleDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_skj_needle.c", 210); +} diff --git a/soh/src/overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h b/soh/src/overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h new file mode 100644 index 000000000..1c4f44494 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Skjneedle/z_en_skjneedle.h @@ -0,0 +1,18 @@ +#ifndef Z_EN_SKJNEEDLE_H +#define Z_EN_SKJNEEDLE_H + +#include "ultra64.h" +#include "global.h" + +struct EnSkjneedle; + +typedef struct EnSkjneedle { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[0x48]; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s16 unusedTimer1; + /* 0x01E2 */ s16 killTimer; + /* 0x01E4 */ char unk_1E4[4]; +} EnSkjneedle; // size = 0x01E8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c new file mode 100644 index 000000000..d88bff535 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c @@ -0,0 +1,886 @@ +#include "z_en_ssh.h" +#include "objects/object_ssh/object_ssh.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +#define SSH_STATE_STUNNED (1 << 0) +#define SSH_STATE_GROUND_START (1 << 2) +#define SSH_STATE_ATTACKED (1 << 3) +#define SSH_STATE_SPIN (1 << 4) + +typedef enum { + SSH_ANIM_UNK0, // Unused animation. Possibly being knocked back? + SSH_ANIM_UP, + SSH_ANIM_WAIT, + SSH_ANIM_LAND, + SSH_ANIM_DROP, + SSH_ANIM_UNK5, // Slower version of ANIM_DROP + SSH_ANIM_UNK6 // Faster repeating version of ANIM_UNK0 +} EnSshAnimation; + +void EnSsh_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSsh_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSsh_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSsh_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnSsh_Idle(EnSsh* this, GlobalContext* globalCtx); +void EnSsh_Drop(EnSsh* this, GlobalContext* globalCtx); +void EnSsh_Return(EnSsh* this, GlobalContext* globalCtx); +void EnSsh_Start(EnSsh* this, GlobalContext* globalCtx); + +#include "overlays/ovl_En_Ssh/ovl_En_Ssh.h" + +const ActorInit En_Ssh_InitVars = { + ACTOR_EN_SSH, + ACTORCAT_NPC, + FLAGS, + OBJECT_SSH, + sizeof(EnSsh), + (ActorFunc)EnSsh_Init, + (ActorFunc)EnSsh_Destroy, + (ActorFunc)EnSsh_Update, + (ActorFunc)EnSsh_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit1 = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 32, 50, -24, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 1, 0, 0, 0, MASS_IMMOVABLE }; + +static ColliderCylinderInit sCylinderInit2 = { + { + COLTYPE_HIT6, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 60, -30, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, -240, 0 }, 28 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphElementsInit), + sJntSphElementsInit, +}; + +void EnSsh_SetupAction(EnSsh* this, EnSshActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnSsh_SpawnShockwave(EnSsh* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f pos; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.floorHeight; + pos.z = this->actor.world.pos.z; + EffectSsBlast_SpawnWhiteCustomScale(globalCtx, &pos, &zeroVec, &zeroVec, 100, 220, 8); +} + +s32 EnSsh_CreateBlureEffect(GlobalContext* globalCtx) { + EffectBlureInit1 blureInit; + u8 p1StartColor[] = { 255, 255, 255, 75 }; + u8 p2StartColor[] = { 255, 255, 255, 75 }; + u8 p1EndColor[] = { 255, 255, 255, 0 }; + u8 p2EndColor[] = { 255, 255, 255, 0 }; + s32 i; + s32 blureIdx; + + for (i = 0; i < 4; i++) { + blureInit.p1StartColor[i] = p1StartColor[i]; + blureInit.p2StartColor[i] = p2StartColor[i]; + blureInit.p1EndColor[i] = p1EndColor[i]; + blureInit.p2EndColor[i] = p2EndColor[i]; + } + + blureInit.elemDuration = 6; + blureInit.unkFlag = 0; + blureInit.calcMode = 3; + + Effect_Add(globalCtx, &blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); + return blureIdx; +} + +s32 EnSsh_CheckCeilingPos(EnSsh* this, GlobalContext* globalCtx) { + CollisionPoly* poly; + s32 bgId; + Vec3f posB; + + posB.x = this->actor.world.pos.x; + posB.y = this->actor.world.pos.y + 1000.0f; + posB.z = this->actor.world.pos.z; + if (!BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &posB, &this->ceilingPos, &poly, false, + false, true, true, &bgId)) { + return false; + } else { + return true; + } +} + +void EnSsh_AddBlureVertex(EnSsh* this) { + Vec3f p1base = { 834.0f, 834.0f, 0.0f }; + Vec3f p2base = { 834.0f, -584.0f, 0.0f }; + Vec3f p1; + Vec3f p2; + + p1base.x *= this->colliderScale; + p1base.y *= this->colliderScale; + p1base.z *= this->colliderScale; + p2base.x *= this->colliderScale; + p2base.y *= this->colliderScale; + p2base.z *= this->colliderScale; + Matrix_Push(); + Matrix_MultVec3f(&p1base, &p1); + Matrix_MultVec3f(&p2base, &p2); + Matrix_Pop(); + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIdx), &p1, &p2); +} + +void EnSsh_AddBlureSpace(EnSsh* this) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIdx)); +} + +void EnSsh_InitColliders(EnSsh* this, GlobalContext* globalCtx) { + ColliderCylinderInit* cylinders[6] = { + &sCylinderInit1, &sCylinderInit1, &sCylinderInit1, &sCylinderInit2, &sCylinderInit2, &sCylinderInit2, + }; + s32 i; + s32 pad; + + for (i = 0; i < ARRAY_COUNT(cylinders); i++) { + Collider_InitCylinder(globalCtx, &this->colCylinder[i]); + Collider_SetCylinder(globalCtx, &this->colCylinder[i], &this->actor, cylinders[i]); + } + + this->colCylinder[0].info.bumper.dmgFlags = 0x0003F8E9; + this->colCylinder[1].info.bumper.dmgFlags = 0xFFC00716; + this->colCylinder[2].base.colType = COLTYPE_METAL; + this->colCylinder[2].info.bumperFlags = BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO; + this->colCylinder[2].info.elemType = ELEMTYPE_UNK2; + this->colCylinder[2].info.bumper.dmgFlags = 0xFFCC0716; + + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(2), &sColChkInfoInit); + + Collider_InitJntSph(globalCtx, &this->colSph); + Collider_SetJntSph(globalCtx, &this->colSph, &this->actor, &sJntSphInit, this->colSphElements); +} + +f32 EnSsh_SetAnimation(EnSsh* this, s32 animIndex) { + AnimationHeader* animation[] = { + &object_ssh_Anim_005BE8, &object_ssh_Anim_000304, &object_ssh_Anim_000304, &object_ssh_Anim_0055F8, + &object_ssh_Anim_000304, &object_ssh_Anim_000304, &object_ssh_Anim_005BE8, + }; + f32 playbackSpeed[] = { 1.0f, 4.0f, 1.0f, 1.0f, 8.0f, 6.0f, 2.0f }; + u8 mode[] = { 3, 3, 1, 3, 1, 1, 1 }; + f32 frameCount = Animation_GetLastFrame(animation[animIndex]); + s32 pad; + + Animation_Change(&this->skelAnime, animation[animIndex], playbackSpeed[animIndex], 0.0f, frameCount, + mode[animIndex], -6.0f); + + return frameCount; +} + +void EnSsh_SetWaitAnimation(EnSsh* this) { + EnSsh_SetAnimation(this, SSH_ANIM_WAIT); +} + +void EnSsh_SetReturnAnimation(EnSsh* this) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_UP); + EnSsh_SetAnimation(this, SSH_ANIM_UP); +} + +void EnSsh_SetLandAnimation(EnSsh* this) { + this->actor.world.pos.y = this->floorHeightOffset + this->actor.floorHeight; + this->animTimer = EnSsh_SetAnimation(this, SSH_ANIM_LAND); +} + +void EnSsh_SetDropAnimation(EnSsh* this) { + if (this->unkTimer == 0) { + this->animTimer = EnSsh_SetAnimation(this, SSH_ANIM_DROP); + } + this->actor.velocity.y = -10.0f; +} + +void EnSsh_SetStunned(EnSsh* this) { + if (this->stunTimer == 0) { + this->stateFlags |= SSH_STATE_ATTACKED; + this->stunTimer = 120; + this->actor.colorFilterTimer = 0; + } +} + +void EnSsh_SetColliderScale(EnSsh* this, f32 scale, f32 radiusMod) { + f32 radius; + f32 height; + f32 yShift; + s32 i; + + radius = this->colSph.elements[0].dim.modelSphere.radius; + radius *= scale; + this->colSph.elements[0].dim.modelSphere.radius = radius; + + for (i = 0; i < 6; i++) { + yShift = this->colCylinder[i].dim.yShift; + radius = this->colCylinder[i].dim.radius; + height = this->colCylinder[i].dim.height; + yShift *= scale; + radius *= scale * radiusMod; + height *= scale; + + this->colCylinder[i].dim.yShift = yShift; + this->colCylinder[i].dim.radius = radius; + this->colCylinder[i].dim.height = height; + } + Actor_SetScale(&this->actor, 0.04f * scale); + this->floorHeightOffset = 40.0f * scale; + this->colliderScale = scale * 1.5f; +} + +s32 EnSsh_Damaged(EnSsh* this) { + if ((this->stunTimer == 120) && (this->stateFlags & SSH_STATE_STUNNED)) { + Actor_SetColorFilter(&this->actor, 0, 0xC8, 0, this->stunTimer); + } + if (DECR(this->stunTimer) != 0) { + Math_SmoothStepToS(&this->maxTurnRate, 0x2710, 0xA, 0x3E8, 1); + return false; + } else { + this->stunTimer = 0; + this->stateFlags &= ~SSH_STATE_STUNNED; + this->spinTimer = 0; + if (this->swayTimer == 0) { + this->spinTimer = 30; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_ROLL); + Audio_PlayActorSound2(&this->actor, NA_SE_VO_ST_ATTACK); + return true; + } +} + +void EnSsh_Turn(EnSsh* this, GlobalContext* globalCtx) { + if (this->hitTimer != 0) { + this->hitTimer--; + } + if (DECR(this->spinTimer) != 0) { + this->actor.world.rot.y += 10000.0f * (this->spinTimer / 30.0f); + } else if ((this->swayTimer == 0) && (this->stunTimer == 0)) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 4, 0x2710, 1); + } + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnSsh_Stunned(EnSsh* this, GlobalContext* globalCtx) { + if ((this->swayTimer == 0) && (this->stunTimer == 0)) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer ^ 0x8000, 4, this->maxTurnRate, 1); + } + this->actor.shape.rot.y = this->actor.world.rot.y; + if (this->stunTimer < 30) { + if (this->stunTimer & 1) { + this->actor.shape.rot.y += 0x7D0; + } else { + this->actor.shape.rot.y -= 0x7D0; + } + } +} + +void EnSsh_UpdateYaw(EnSsh* this, GlobalContext* globalCtx) { + if (this->stunTimer != 0) { + EnSsh_Stunned(this, globalCtx); + } else { + EnSsh_Turn(this, globalCtx); + } +} + +void EnSsh_Bob(EnSsh* this, GlobalContext* globalCtx) { + f32 bobVel = 0.5f; + + if ((globalCtx->state.frames & 8) != 0) { + bobVel *= -1.0f; + } + Math_SmoothStepToF(&this->actor.velocity.y, bobVel, 0.4f, 1000.0f, 0.0f); +} + +s32 EnSsh_IsCloseToLink(EnSsh* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 yDist; + + if (this->stateFlags & SSH_STATE_GROUND_START) { + return true; + } + if (this->unkTimer != 0) { + return true; + } + if (this->swayTimer != 0) { + return true; + } + if (this->animTimer != 0) { + return true; + } + + if (this->actor.xzDistToPlayer > 160.0f) { + return false; + } + + yDist = this->actor.world.pos.y - player->actor.world.pos.y; + if (yDist < 0.0f || yDist > 400.0f) { + return false; + } + + if (player->actor.world.pos.y < this->actor.floorHeight) { + return false; + } + return true; +} + +s32 EnSsh_IsCloseToHome(EnSsh* this) { + f32 vel = this->actor.velocity.y; + f32 nextY = this->actor.world.pos.y + 2.0f * this->actor.velocity.y; + + if (nextY >= this->actor.home.pos.y) { + return 1; + } + return 0; +} + +s32 EnSsh_IsCloseToGround(EnSsh* this) { + f32 vel = this->actor.velocity.y; + f32 nextY = this->actor.world.pos.y + 2.0f * this->actor.velocity.y; + + if ((nextY - this->actor.floorHeight) <= this->floorHeightOffset) { + return 1; + } + return 0; +} + +void EnSsh_Sway(EnSsh* this) { + Vec3f swayVecBase; + Vec3f swayVec; + f32 temp; + s16 swayAngle; + + if (this->swayTimer != 0) { + this->swayAngle += 0x640; + this->swayTimer--; + if (this->swayTimer == 0) { + this->swayAngle = 0; + } + temp = this->swayTimer * (1.0f / 6); + swayAngle = temp * (0x10000 / 360.0f) * Math_SinS(this->swayAngle); + temp = this->actor.world.pos.y - this->ceilingPos.y; + swayVecBase.x = Math_SinS(swayAngle) * temp; + swayVecBase.y = Math_CosS(swayAngle) * temp; + swayVecBase.z = 0.0f; + Matrix_Push(); + Matrix_Translate(this->ceilingPos.x, this->ceilingPos.y, this->ceilingPos.z, MTXMODE_NEW); + Matrix_RotateY(this->actor.world.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_MultVec3f(&swayVecBase, &swayVec); + Matrix_Pop(); + this->actor.shape.rot.z = -(swayAngle * 2); + this->actor.world.pos.x = swayVec.x; + this->actor.world.pos.z = swayVec.z; + } +} + +void EnSsh_CheckBodyStickHit(EnSsh* this, GlobalContext* globalCtx) { + ColliderInfo* info = &this->colCylinder[0].info; + Player* player = GET_PLAYER(globalCtx); + + if (player->unk_860 != 0) { + info->bumper.dmgFlags |= 2; + this->colCylinder[1].info.bumper.dmgFlags &= ~2; + this->colCylinder[2].info.bumper.dmgFlags &= ~2; + } else { + info->bumper.dmgFlags &= ~2; + this->colCylinder[1].info.bumper.dmgFlags |= 2; + this->colCylinder[2].info.bumper.dmgFlags |= 2; + } +} + +s32 EnSsh_CheckHitPlayer(EnSsh* this, GlobalContext* globalCtx) { + s32 i; + s32 hit = false; + + if ((this->hitCount == 0) && (this->spinTimer == 0)) { + return false; + } + for (i = 0; i < 3; i++) { + if (this->colCylinder[i + 3].base.ocFlags2 & OC2_HIT_PLAYER) { + this->colCylinder[i + 3].base.ocFlags2 &= ~OC2_HIT_PLAYER; + hit = true; + } + } + if (!hit) { + return false; + } + this->hitTimer = 30; + if (this->swayTimer == 0) { + this->spinTimer = this->hitTimer; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_ROLL); + Audio_PlayActorSound2(&this->actor, NA_SE_VO_ST_ATTACK); + globalCtx->damagePlayer(globalCtx, -8); + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); + this->hitCount--; + return true; +} + +s32 EnSsh_CheckHitFront(EnSsh* this) { + u32 acFlags; + + if (this->colCylinder[2].base.acFlags) {} // Needed for matching + acFlags = this->colCylinder[2].base.acFlags; + + if (!!(acFlags & AC_HIT) == 0) { + return 0; + } else { + this->colCylinder[2].base.acFlags &= ~AC_HIT; + this->invincibilityTimer = 8; + if ((this->swayTimer == 0) && (this->hitTimer == 0) && (this->stunTimer == 0)) { + this->swayTimer = 60; + } + return 1; + } +} + +s32 EnSsh_CheckHitBack(EnSsh* this, GlobalContext* globalCtx) { + ColliderCylinder* cyl = &this->colCylinder[0]; + s32 hit = false; + + if (cyl->base.acFlags & AC_HIT) { + cyl->base.acFlags &= ~AC_HIT; + hit = true; + } + cyl = &this->colCylinder[1]; + if (cyl->base.acFlags & AC_HIT) { + cyl->base.acFlags &= ~AC_HIT; + hit = true; + } + if (!hit) { + return false; + } + this->invincibilityTimer = 8; + if (this->hitCount <= 0) { + this->hitCount++; + } + if (this->stunTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Audio_PlayActorSound2(&this->actor, NA_SE_VO_ST_DAMAGE); + } + EnSsh_SetStunned(this); + this->stateFlags |= SSH_STATE_STUNNED; + return false; +} + +s32 EnSsh_CollisionCheck(EnSsh* this, GlobalContext* globalCtx) { + if (this->stunTimer == 0) { + EnSsh_CheckHitPlayer(this, globalCtx); + } + if (EnSsh_CheckHitFront(this)) { + return false; + } else if (globalCtx->actorCtx.unk_02 != 0) { + this->invincibilityTimer = 8; + if (this->stunTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Audio_PlayActorSound2(&this->actor, NA_SE_VO_ST_DAMAGE); + } + EnSsh_SetStunned(this); + this->stateFlags |= SSH_STATE_STUNNED; + return false; + } else { + return EnSsh_CheckHitBack(this, globalCtx); + // Always returns false + } +} + +void EnSsh_SetBodyCylinderAC(EnSsh* this, GlobalContext* globalCtx) { + Collider_UpdateCylinder(&this->actor, &this->colCylinder[0]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[0].base); +} + +void EnSsh_SetLegsCylinderAC(EnSsh* this, GlobalContext* globalCtx) { + s16 angleTowardsLink = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)); + + if (angleTowardsLink < 90 * (0x10000 / 360)) { + Collider_UpdateCylinder(&this->actor, &this->colCylinder[2]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[2].base); + } else { + Collider_UpdateCylinder(&this->actor, &this->colCylinder[1]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[1].base); + } +} + +s32 EnSsh_SetCylinderOC(EnSsh* this, GlobalContext* globalCtx) { + Vec3f cyloffsets[] = { + { 40.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { -40.0f, 0.0f, 0.0f }, + }; + Vec3f cylPos; + s32 i; + + for (i = 0; i < 3; i++) { + cylPos = this->actor.world.pos; + cyloffsets[i].x *= this->colliderScale; + cyloffsets[i].y *= this->colliderScale; + cyloffsets[i].z *= this->colliderScale; + Matrix_Push(); + Matrix_Translate(cylPos.x, cylPos.y, cylPos.z, MTXMODE_NEW); + Matrix_RotateY((this->initialYaw / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_MultVec3f(&cyloffsets[i], &cylPos); + Matrix_Pop(); + this->colCylinder[i + 3].dim.pos.x = cylPos.x; + this->colCylinder[i + 3].dim.pos.y = cylPos.y; + this->colCylinder[i + 3].dim.pos.z = cylPos.z; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[i + 3].base); + } + return 1; +} + +void EnSsh_SetColliders(EnSsh* this, GlobalContext* globalCtx) { + if (this->actor.colChkInfo.health == 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colSph.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colSph.base); + } else { + if (this->hitTimer == 0) { + EnSsh_SetCylinderOC(this, globalCtx); + } + if (DECR(this->invincibilityTimer) == 0) { + EnSsh_SetBodyCylinderAC(this, globalCtx); + EnSsh_SetLegsCylinderAC(this, globalCtx); + } + } +} + +void EnSsh_Init(Actor* thisx, GlobalContext* globalCtx) { + f32 frameCount; + s32 pad; + EnSsh* this = (EnSsh*)thisx; + + frameCount = Animation_GetLastFrame(&object_ssh_Anim_000304); + if (this->actor.params == ENSSH_FATHER) { + if (gSaveContext.inventory.gsTokens >= 100) { + Actor_Kill(&this->actor); + return; + } + } else if (gSaveContext.inventory.gsTokens >= (this->actor.params * 10)) { + Actor_Kill(&this->actor); + return; + } + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &object_ssh_Skel_0052E0, NULL, this->jointTable, this->morphTable, 30); + Animation_Change(&this->skelAnime, &object_ssh_Anim_000304, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP_INTERP, 0.0f); + this->blureIdx = EnSsh_CreateBlureEffect(globalCtx); + EnSsh_InitColliders(this, globalCtx); + this->stateFlags = 0; + this->hitCount = 0; + EnSsh_CheckCeilingPos(this, globalCtx); + if (this->actor.params != ENSSH_FATHER) { + EnSsh_SetColliderScale(this, 0.5f, 1.0f); + } else { + EnSsh_SetColliderScale(this, 0.75f, 1.0f); + } + this->actor.gravity = 0.0f; + this->initialYaw = this->actor.world.rot.y; + EnSsh_SetupAction(this, EnSsh_Start); +} + +void EnSsh_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSsh* this = (EnSsh*)thisx; + s32 i; + + Effect_Delete(globalCtx, this->blureIdx); + for (i = 0; i < 6; i++) { + Collider_DestroyCylinder(globalCtx, &this->colCylinder[i]); + } + Collider_DestroyJntSph(globalCtx, &this->colSph); +} + +void EnSsh_Wait(EnSsh* this, GlobalContext* globalCtx) { + if (EnSsh_IsCloseToLink(this, globalCtx)) { + EnSsh_SetDropAnimation(this); + EnSsh_SetupAction(this, EnSsh_Drop); + } else { + EnSsh_Bob(this, globalCtx); + } +} + +void EnSsh_Talk(EnSsh* this, GlobalContext* globalCtx) { + EnSsh_Bob(this, globalCtx); + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->actionFunc = EnSsh_Idle; + } +} + +void EnSsh_Idle(EnSsh* this, GlobalContext* globalCtx) { + if (1) {} + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnSsh_Talk; + if (this->actor.params == ENSSH_FATHER) { + gSaveContext.eventChkInf[9] |= 0x40; + } + if ((this->actor.textId == 0x26) || (this->actor.textId == 0x27)) { + gSaveContext.infTable[25] |= 0x40; + } + if ((this->actor.textId == 0x24) || (this->actor.textId == 0x25)) { + gSaveContext.infTable[25] |= 0x80; + } + } else { + if ((this->unkTimer != 0) && (DECR(this->unkTimer) == 0)) { + EnSsh_SetAnimation(this, SSH_ANIM_WAIT); + } + if ((this->animTimer != 0) && (DECR(this->animTimer) == 0)) { + EnSsh_SetAnimation(this, SSH_ANIM_WAIT); + } + if (!EnSsh_IsCloseToLink(this, globalCtx)) { + EnSsh_SetReturnAnimation(this); + EnSsh_SetupAction(this, EnSsh_Return); + } else { + if (DECR(this->sfxTimer) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_LAUGH); + this->sfxTimer = 64; + } + EnSsh_Bob(this, globalCtx); + if ((this->unkTimer == 0) && (this->animTimer == 0)) { + this->actor.textId = Text_GetFaceReaction(globalCtx, 0xD); + if (this->actor.textId == 0) { + if (this->actor.params == ENSSH_FATHER) { + if (gSaveContext.inventory.gsTokens >= 50) { + this->actor.textId = 0x29; + } else if (gSaveContext.inventory.gsTokens >= 10) { + if (gSaveContext.infTable[25] & 0x80) { + this->actor.textId = 0x24; + } else { + this->actor.textId = 0x25; + } + } else { + if (gSaveContext.infTable[25] & 0x40) { + this->actor.textId = 0x27; + } else { + this->actor.textId = 0x26; + } + } + } else { + this->actor.textId = 0x22; + } + } + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + } +} + +void EnSsh_Land(EnSsh* this, GlobalContext* globalCtx) { + if ((this->unkTimer != 0) && (DECR(this->unkTimer) == 0)) { + EnSsh_SetAnimation(this, SSH_ANIM_WAIT); + } + if ((this->animTimer != 0) && (DECR(this->animTimer) == 0)) { + EnSsh_SetAnimation(this, SSH_ANIM_WAIT); + } + if ((this->actor.floorHeight + this->floorHeightOffset) <= this->actor.world.pos.y) { + EnSsh_SetupAction(this, EnSsh_Idle); + } else { + Math_SmoothStepToF(&this->actor.velocity.y, 2.0f, 0.6f, 1000.0f, 0.0f); + } +} + +void EnSsh_Drop(EnSsh* this, GlobalContext* globalCtx) { + if ((this->unkTimer != 0) && (DECR(this->unkTimer) == 0)) { + EnSsh_SetAnimation(this, SSH_ANIM_DROP); + } + if (!EnSsh_IsCloseToLink(this, globalCtx)) { + EnSsh_SetReturnAnimation(this); + EnSsh_SetupAction(this, EnSsh_Return); + } else if (EnSsh_IsCloseToGround(this)) { + EnSsh_SpawnShockwave(this, globalCtx); + EnSsh_SetLandAnimation(this); + EnSsh_SetupAction(this, EnSsh_Land); + } else if (DECR(this->sfxTimer) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DOWN); + this->sfxTimer = 3; + } +} + +void EnSsh_Return(EnSsh* this, GlobalContext* globalCtx) { + f32 frameRatio = this->skelAnime.curFrame / (this->skelAnime.animLength - 1.0f); + + if (frameRatio == 1.0f) { + EnSsh_SetReturnAnimation(this); + } + if (EnSsh_IsCloseToLink(this, globalCtx)) { + EnSsh_SetDropAnimation(this); + EnSsh_SetupAction(this, EnSsh_Drop); + } else if (EnSsh_IsCloseToHome(this)) { + EnSsh_SetWaitAnimation(this); + EnSsh_SetupAction(this, EnSsh_Wait); + } else { + this->actor.velocity.y = 4.0f * frameRatio; + } +} + +void EnSsh_UpdateColliderScale(EnSsh* this) { + if (this->stateFlags & SSH_STATE_SPIN) { + if (this->spinTimer == 0) { + this->stateFlags &= ~SSH_STATE_SPIN; + if (this->actor.params != ENSSH_FATHER) { + EnSsh_SetColliderScale(this, 0.5f, 1.0f); + } else { + EnSsh_SetColliderScale(this, 0.75f, 1.0f); + } + } + } else { + if (this->spinTimer != 0) { + this->stateFlags |= SSH_STATE_SPIN; + if (this->actor.params != ENSSH_FATHER) { + EnSsh_SetColliderScale(this, 0.5f, 2.0f); + } else { + EnSsh_SetColliderScale(this, 0.75f, 2.0f); + } + } + } +} + +void EnSsh_Start(EnSsh* this, GlobalContext* globalCtx) { + if (!EnSsh_IsCloseToGround(this)) { + EnSsh_SetupAction(this, EnSsh_Wait); + EnSsh_Wait(this, globalCtx); + } else { + EnSsh_SetLandAnimation(this); + this->stateFlags |= 4; + EnSsh_SetupAction(this, EnSsh_Land); + EnSsh_Land(this, globalCtx); + } +} + +void EnSsh_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSsh* this = (EnSsh*)thisx; + + EnSsh_UpdateColliderScale(this); + if (EnSsh_CollisionCheck(this, globalCtx)) { + return; // EnSsh_CollisionCheck always returns false, so this never happens + } + if (this->stunTimer != 0) { + EnSsh_Damaged(this); + } else { + SkelAnime_Update(&this->skelAnime); + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actionFunc(this, globalCtx); + } + EnSsh_UpdateYaw(this, globalCtx); + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + this->blinkState = this->blinkTimer; + if (this->blinkState >= 3) { + this->blinkState = 0; + } + EnSsh_SetColliders(this, globalCtx); + Actor_SetFocus(&this->actor, 0.0f); +} + +s32 EnSsh_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnSsh* this = (EnSsh*)thisx; + + switch (limbIndex) { + case 1: + if ((this->spinTimer != 0) && (this->swayTimer == 0)) { + if (this->spinTimer >= 2) { + EnSsh_AddBlureVertex(this); + } else { + EnSsh_AddBlureSpace(this); + } + } + break; + case 4: + if (this->actor.params == ENSSH_FATHER) { + *dList = object_ssh_DL_0046C0; + } + break; + case 5: + if (this->actor.params == ENSSH_FATHER) { + *dList = object_ssh_DL_004080; + } + break; + case 8: + if (this->actor.params == ENSSH_FATHER) { + *dList = object_ssh_DL_004DE8; + } + break; + } + return false; +} + +void EnSsh_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnSsh* this = (EnSsh*)thisx; + + Collider_UpdateSpheres(limbIndex, &this->colSph); +} + +void EnSsh_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* blinkTex[] = { + object_ssh_Tex_0007E0, + object_ssh_Tex_000C60, + object_ssh_Tex_001060, + }; + s32 pad; + EnSsh* this = (EnSsh*)thisx; + + EnSsh_CheckBodyStickHit(this, globalCtx); + EnSsh_Sway(this); + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ssh.c", 2333); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(blinkTex[this->blinkState])); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ssh.c", 2336); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnSsh_OverrideLimbDraw, + EnSsh_PostLimbDraw, &this->actor); +} diff --git a/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.h b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.h new file mode 100644 index 000000000..1cb04e59c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.h @@ -0,0 +1,44 @@ +#ifndef Z_EN_SSH_H +#define Z_EN_SSH_H + +#include "ultra64.h" +#include "global.h" + +struct EnSsh; + +typedef void (*EnSshActionFunc)(struct EnSsh*, GlobalContext*); + +typedef struct EnSsh { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[30]; + /* 0x0244 */ Vec3s morphTable[30]; + /* 0x02F8 */ EnSshActionFunc actionFunc; + /* 0x02FC */ ColliderCylinder colCylinder[6]; + /* 0x04C4 */ ColliderJntSph colSph; + /* 0x04E4 */ ColliderJntSphElement colSphElements[1]; + /* 0x0524 */ s16 initialYaw; + /* 0x0526 */ s16 maxTurnRate; + /* 0x0528 */ s16 unkTimer; + /* 0x052A */ s16 spinTimer; + /* 0x052C */ s16 hitTimer; + /* 0x052E */ s16 invincibilityTimer; + /* 0x0530 */ s16 sfxTimer; + /* 0x0532 */ s16 stunTimer; + /* 0x0534 */ s16 animTimer; + /* 0x0536 */ s16 swayTimer; + /* 0x0538 */ s32 blureIdx; + /* 0x053C */ f32 colliderScale; + /* 0x0540 */ f32 floorHeightOffset; + /* 0x0544 */ Vec3f ceilingPos; + /* 0x0558 */ char unk_558[0x78]; + /* 0x05C8 */ s16 swayAngle; + /* 0x05CA */ u16 stateFlags; + /* 0x05CC */ u8 hitCount; + /* 0x05CE */ s16 blinkState; + /* 0x05D0 */ s16 blinkTimer; +} EnSsh; // size = 0x05D4 + +#define ENSSH_FATHER 0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c new file mode 100644 index 000000000..57009428c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -0,0 +1,1089 @@ +/* + * File: z_en_st.c + * Overlay: ovl_En_St + * Description: Skulltula (normal, big, invisible) + */ + +#include "z_en_st.h" +#include "objects/object_st/object_st.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnSt_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSt_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSt_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSt_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnSt_ReturnToCeiling(EnSt* this, GlobalContext* globalCtx); +void EnSt_MoveToGround(EnSt* this, GlobalContext* globalCtx); +void EnSt_StartOnCeilingOrGround(EnSt* this, GlobalContext* globalCtx); +void EnSt_WaitOnGround(EnSt* this, GlobalContext* globalCtx); +void EnSt_Die(EnSt* this, GlobalContext* globalCtx); +void EnSt_BounceAround(EnSt* this, GlobalContext* globalCtx); +void EnSt_FinishBouncing(EnSt* this, GlobalContext* globalCtx); + +#include "overlays/ovl_En_St/ovl_En_St.h" + +const ActorInit En_St_InitVars = { + ACTOR_EN_ST, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_ST, + sizeof(EnSt), + (ActorFunc)EnSt_Init, + (ActorFunc)EnSt_Destroy, + (ActorFunc)EnSt_Update, + (ActorFunc)EnSt_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT6, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 32, 50, -24, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInit = { 2, 0, 0, 0, MASS_IMMOVABLE }; + +static ColliderCylinderInit sCylinderInit2 = { + { + COLTYPE_HIT6, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 60, -30, { 0, 0, 0 } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_ON, + }, + { 1, { { 0, -240, 0 }, 28 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +typedef enum { + /* 0 */ ENST_ANIM_0, + /* 1 */ ENST_ANIM_1, + /* 2 */ ENST_ANIM_2, + /* 3 */ ENST_ANIM_3, + /* 4 */ ENST_ANIM_4, + /* 5 */ ENST_ANIM_5, + /* 6 */ ENST_ANIM_6, + /* 7 */ ENST_ANIM_7 +} EnStAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &object_st_Anim_000304, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, 0.0f }, + { &object_st_Anim_005B98, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -8.0f }, + { &object_st_Anim_000304, 4.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -8.0f }, + { &object_st_Anim_000304, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, -8.0f }, + { &object_st_Anim_0055A8, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE_INTERP, -8.0f }, + { &object_st_Anim_000304, 8.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, -8.0f }, + { &object_st_Anim_000304, 6.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, -8.0f }, + { &object_st_Anim_005B98, 2.0f, 0.0f, -1.0f, ANIMMODE_LOOP_INTERP, -8.0f }, +}; + +void EnSt_SetupAction(EnSt* this, EnStActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +/** + * Spawns `dustCnt` dust particles in a random pattern around the Skulltula + */ +void EnSt_SpawnDust(EnSt* this, GlobalContext* globalCtx, s32 dustCnt) { + Color_RGBA8 primColor = { 170, 130, 90, 255 }; + Color_RGBA8 envColor = { 100, 60, 20, 0 }; + Vec3f dustVel = { 0.0f, 0.0f, 0.0f }; + Vec3f dustAccel = { 0.0f, 0.3f, 0.0f }; + Vec3f dustPos; + s16 yAngle; + s32 i; + + yAngle = (Rand_ZeroOne() - 0.5f) * 65536.0f; + dustPos.y = this->actor.floorHeight; + for (i = dustCnt; i >= 0; i--, yAngle += (s16)(0x10000 / dustCnt)) { + dustAccel.x = (Rand_ZeroOne() - 0.5f) * 4.0f; + dustAccel.z = (Rand_ZeroOne() - 0.5f) * 4.0f; + dustPos.x = this->actor.world.pos.x + (Math_SinS(yAngle) * 22.0f); + dustPos.z = this->actor.world.pos.z + (Math_CosS(yAngle) * 22.0f); + func_8002836C(globalCtx, &dustPos, &dustVel, &dustAccel, &primColor, &envColor, 120, 40, 10); + } +} + +void EnSt_SpawnBlastEffect(EnSt* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f blastPos; + + blastPos.x = this->actor.world.pos.x; + blastPos.y = this->actor.floorHeight; + blastPos.z = this->actor.world.pos.z; + + EffectSsBlast_SpawnWhiteCustomScale(globalCtx, &blastPos, &zeroVec, &zeroVec, 100, 220, 8); +} + +void EnSt_SpawnDeadEffect(EnSt* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f firePos; + + firePos.x = this->actor.world.pos.x + ((Rand_ZeroOne() - 0.5f) * 60.0f); + firePos.y = (this->actor.world.pos.y + 10.0f) + ((Rand_ZeroOne() - 0.5f) * 45.0f); + firePos.z = this->actor.world.pos.z + ((Rand_ZeroOne() - 0.5f) * 60.0f); + EffectSsDeadDb_Spawn(globalCtx, &firePos, &zeroVec, &zeroVec, 100, 0, 255, 255, 255, 255, 255, 0, 0, 1, 9, true); +} + +s32 EnSt_CreateBlureEffect(GlobalContext* globalCtx) { + EffectBlureInit1 blureInit; + u8 p1StartColor[] = { 255, 255, 255, 75 }; + u8 p2StartColor[] = { 255, 255, 255, 75 }; + u8 p1EndColor[] = { 255, 255, 255, 0 }; + u8 p2EndColor[] = { 255, 255, 255, 0 }; + s32 i; + s32 blureIdx; + + for (i = 0; i < 4; i++) { + blureInit.p1StartColor[i] = p1StartColor[i]; + blureInit.p2StartColor[i] = p2StartColor[i]; + blureInit.p1EndColor[i] = p1EndColor[i]; + blureInit.p2EndColor[i] = p2EndColor[i]; + } + + blureInit.elemDuration = 6; + blureInit.unkFlag = 0; + blureInit.calcMode = 3; + + Effect_Add(globalCtx, &blureIdx, EFFECT_BLURE1, 0, 0, &blureInit); + return blureIdx; +} + +/** + * Checks for the position of the ceiling above the Skulltula. + * If no ceiling is found it is set to 1000 units above the Skulltula + */ +s32 EnSt_CheckCeilingPos(EnSt* this, GlobalContext* globalCtx) { + CollisionPoly* poly; + s32 bgId; + Vec3f checkPos; + + checkPos.x = this->actor.world.pos.x; + checkPos.y = this->actor.world.pos.y + 1000.0f; + checkPos.z = this->actor.world.pos.z; + if (!BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &checkPos, &this->ceilingPos, &poly, false, + false, true, true, &bgId)) { + return false; + } + this->unusedPos = this->actor.world.pos; + this->unusedPos.y -= 100.0f; + return true; +} + +void EnSt_AddBlurVertex(EnSt* this) { + Vec3f v1 = { 834.0f, 834.0f, 0.0f }; + Vec3f v2 = { 834.0f, -584.0f, 0.0f }; + Vec3f v1Pos; + Vec3f v2Pos; + + v1.x *= this->colliderScale; + v1.y *= this->colliderScale; + v1.z *= this->colliderScale; + + v2.x *= this->colliderScale; + v2.y *= this->colliderScale; + v2.z *= this->colliderScale; + + Matrix_Push(); + Matrix_MultVec3f(&v1, &v1Pos); + Matrix_MultVec3f(&v2, &v2Pos); + Matrix_Pop(); + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIdx), &v1Pos, &v2Pos); +} + +void EnSt_AddBlurSpace(EnSt* this) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIdx)); +} + +void EnSt_SetWaitingAnimation(EnSt* this) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_3); +} + +void EnSt_SetReturnToCeilingAnimation(EnSt* this) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_UP); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_2); +} + +void EnSt_SetLandAnimation(EnSt* this) { + this->actor.world.pos.y = this->actor.floorHeight + this->floorHeightOffset; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_4); + this->sfxTimer = 0; + this->animFrames = this->skelAnime.animLength; +} + +void EnSt_SetDropAnimAndVel(EnSt* this) { + if (this->takeDamageSpinTimer == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_4); + this->animFrames = this->skelAnime.animLength; + } + this->sfxTimer = 0; + this->actor.velocity.y = -10.0f; +} + +/** + * Initalizes the Skulltula's 6 cylinders, and sphere collider. + */ +void EnSt_InitColliders(EnSt* this, GlobalContext* globalCtx) { + ColliderCylinderInit* cylinders[6] = { + &sCylinderInit, &sCylinderInit, &sCylinderInit, &sCylinderInit2, &sCylinderInit2, &sCylinderInit2, + }; + + s32 i; + s32 pad; + + for (i = 0; i < ARRAY_COUNT(cylinders); i++) { + Collider_InitCylinder(globalCtx, &this->colCylinder[i]); + Collider_SetCylinder(globalCtx, &this->colCylinder[i], &this->actor, cylinders[i]); + } + + this->colCylinder[0].info.bumper.dmgFlags = 0x0003F8F9; + this->colCylinder[1].info.bumper.dmgFlags = 0xFFC00706; + this->colCylinder[2].base.colType = COLTYPE_METAL; + this->colCylinder[2].info.bumperFlags = BUMP_ON | BUMP_HOOKABLE | BUMP_NO_AT_INFO; + this->colCylinder[2].info.elemType = ELEMTYPE_UNK2; + this->colCylinder[2].info.bumper.dmgFlags = 0xFFCC0706; + + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(2), &sColChkInit); + + Collider_InitJntSph(globalCtx, &this->colSph); + Collider_SetJntSph(globalCtx, &this->colSph, &this->actor, &sJntSphInit, this->colSphItems); +} + +void EnSt_CheckBodyStickHit(EnSt* this, GlobalContext* globalCtx) { + ColliderInfo* body = &this->colCylinder[0].info; + Player* player = GET_PLAYER(globalCtx); + + if (player->unk_860 != 0) { + body->bumper.dmgFlags |= 2; + this->colCylinder[1].info.bumper.dmgFlags &= ~2; + this->colCylinder[2].info.bumper.dmgFlags &= ~2; + } else { + body->bumper.dmgFlags &= ~2; + this->colCylinder[1].info.bumper.dmgFlags |= 2; + this->colCylinder[2].info.bumper.dmgFlags |= 2; + } +} + +void EnSt_SetBodyCylinderAC(EnSt* this, GlobalContext* globalCtx) { + Collider_UpdateCylinder(&this->actor, &this->colCylinder[0]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[0].base); +} + +void EnSt_SetLegsCylinderAC(EnSt* this, GlobalContext* globalCtx) { + s16 angleTowardsLink = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)); + + if (angleTowardsLink < 0x3FFC) { + Collider_UpdateCylinder(&this->actor, &this->colCylinder[2]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[2].base); + } else { + Collider_UpdateCylinder(&this->actor, &this->colCylinder[1]); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[1].base); + } +} + +s32 EnSt_SetCylinderOC(EnSt* this, GlobalContext* globalCtx) { + Vec3f cyloffsets[] = { + { 40.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { -40.0f, 0.0f, 0.0f }, + }; + Vec3f cylPos; + s32 i; + + for (i = 0; i < 3; i++) { + cylPos = this->actor.world.pos; + cyloffsets[i].x *= this->colliderScale; + cyloffsets[i].y *= this->colliderScale; + cyloffsets[i].z *= this->colliderScale; + Matrix_Push(); + Matrix_Translate(cylPos.x, cylPos.y, cylPos.z, MTXMODE_NEW); + Matrix_RotateY((this->initalYaw / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_MultVec3f(&cyloffsets[i], &cylPos); + Matrix_Pop(); + this->colCylinder[i + 3].dim.pos.x = cylPos.x; + this->colCylinder[i + 3].dim.pos.y = cylPos.y; + this->colCylinder[i + 3].dim.pos.z = cylPos.z; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colCylinder[i + 3].base); + } + + return true; +} + +void EnSt_UpdateCylinders(EnSt* this, GlobalContext* globalCtx) { + if ((this->actor.colChkInfo.health != 0) || (this->actionFunc == EnSt_FinishBouncing)) { + if (DECR(this->gaveDamageSpinTimer) == 0) { + EnSt_SetCylinderOC(this, globalCtx); + } + + DECR(this->invulnerableTimer); + DECR(this->takeDamageSpinTimer); + + if (this->invulnerableTimer == 0 && this->takeDamageSpinTimer == 0) { + EnSt_SetBodyCylinderAC(this, globalCtx); + EnSt_SetLegsCylinderAC(this, globalCtx); + } + } +} + +s32 EnSt_CheckHitLink(EnSt* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 hit; + s32 i; + + for (i = 0, hit = 0; i < 3; i++) { + if (((this->colCylinder[i + 3].base.ocFlags2 & OC2_HIT_PLAYER) != 0) == 0) { + continue; + } + this->colCylinder[i + 3].base.ocFlags2 &= ~OC2_HIT_PLAYER; + hit = true; + } + + if (!hit) { + return false; + } + + if (this->swayTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_ROLL); + } + + this->gaveDamageSpinTimer = 30; + globalCtx->damagePlayer(globalCtx, -8); + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + func_8002F71C(globalCtx, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); + return true; +} + +s32 EnSt_CheckHitFrontside(EnSt* this) { + u8 acFlags = this->colCylinder[2].base.acFlags; + + if (!!(acFlags & AC_HIT) == 0) { + // not hit + return false; + } else { + this->colCylinder[2].base.acFlags &= ~AC_HIT; + this->invulnerableTimer = 8; + this->playSwayFlag = 0; + this->swayTimer = 60; + return true; + } +} + +s32 EnSt_CheckHitBackside(EnSt* this, GlobalContext* globalCtx) { + ColliderCylinder* cyl = &this->colCylinder[0]; + s32 flags = 0; // ac hit flags from colliders 0 and 1 + s32 hit = false; + + if (cyl->base.acFlags & AC_HIT) { + cyl->base.acFlags &= ~AC_HIT; + hit = true; + flags |= cyl->info.acHitInfo->toucher.dmgFlags; + } + + cyl = &this->colCylinder[1]; + if (cyl->base.acFlags & AC_HIT) { + cyl->base.acFlags &= ~AC_HIT; + hit = true; + flags |= cyl->info.acHitInfo->toucher.dmgFlags; + } + + if (!hit) { + return false; + } + + this->invulnerableTimer = 8; + if (this->actor.colChkInfo.damageEffect == 1) { + if (this->stunTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->stunTimer = 120; + Actor_SetColorFilter(&this->actor, 0, 0xC8, 0, this->stunTimer); + } + return false; + } + + this->swayTimer = this->stunTimer = 0; + this->gaveDamageSpinTimer = 1; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_3); + this->takeDamageSpinTimer = this->skelAnime.animLength; + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->takeDamageSpinTimer); + if (Actor_ApplyDamage(&this->actor)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DAMAGE); + return false; + } + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_0; + this->groundBounces = 3; + this->deathTimer = 20; + this->actor.gravity = -1.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_DEAD); + + if (flags & 0x1F820) { + // arrow, fire arrow, ice arrow, light arrow, + // and three unknows, unused arrows? + EnSt_SetupAction(this, EnSt_Die); + this->finishDeathTimer = 8; + } else { + EnSt_SetupAction(this, EnSt_BounceAround); + } + + return true; +} + +/** + * Checks if the Skulltula's colliders have been hit, returns true if the hit has dealt damage to the Skulltula + */ +s32 EnSt_CheckColliders(EnSt* this, GlobalContext* globalCtx) { + if (EnSt_CheckHitFrontside(this)) { + // player has hit the front shield area of the Skulltula + return false; + } + + if (globalCtx->actorCtx.unk_02 != 0) { + return true; + } + + if (EnSt_CheckHitBackside(this, globalCtx)) { + // player has hit the backside of the Skulltula + return true; + } + + if (this->stunTimer == 0 && this->takeDamageSpinTimer == 0) { + // check if the Skulltula has hit link. + EnSt_CheckHitLink(this, globalCtx); + } + return false; +} + +void EnSt_SetColliderScale(EnSt* this) { + f32 scaleAmount = 1.0f; + f32 radius; + f32 height; + f32 yShift; + s32 i; + + if (this->actor.params == 1) { + scaleAmount = 1.4f; + } + + radius = this->colSph.elements[0].dim.modelSphere.radius; + radius *= scaleAmount; + this->colSph.elements[0].dim.modelSphere.radius = radius; + + for (i = 0; i < 6; i++) { + yShift = this->colCylinder[i].dim.yShift; + radius = this->colCylinder[i].dim.radius; + height = this->colCylinder[i].dim.height; + yShift *= scaleAmount; + radius *= scaleAmount; + height *= scaleAmount; + + this->colCylinder[i].dim.yShift = yShift; + this->colCylinder[i].dim.radius = radius; + this->colCylinder[i].dim.height = height; + } + Actor_SetScale(&this->actor, 0.04f * scaleAmount); + this->colliderScale = scaleAmount; + this->floorHeightOffset = 32.0f * scaleAmount; +} + +s32 EnSt_SetTeethColor(EnSt* this, s16 redTarget, s16 greenTarget, s16 blueTarget, s16 minMaxStep) { + s16 red = this->teethR; + s16 green = this->teethG; + s16 blue = this->teethB; + + minMaxStep = 255 / (s16)(0.6f * minMaxStep); + if (minMaxStep <= 0) { + minMaxStep = 1; + } + + Math_SmoothStepToS(&red, redTarget, 1, minMaxStep, minMaxStep); + Math_SmoothStepToS(&green, greenTarget, 1, minMaxStep, minMaxStep); + Math_SmoothStepToS(&blue, blueTarget, 1, minMaxStep, minMaxStep); + this->teethR = red; + this->teethG = green; + this->teethB = blue; + return 1; +} + +s32 EnSt_DecrStunTimer(EnSt* this) { + if (this->stunTimer == 0) { + return 0; + } + this->stunTimer--; //! @bug no return but v0 ends up being stunTimer before decrement +} + +/** + * Updates the yaw of the Skulltula, used for the shaking animation right before + * turning, and the actual turning to face away from the player, and then back to + * face the player + */ +void EnSt_UpdateYaw(EnSt* this, GlobalContext* globalCtx) { + u16 yawDir = 0; + Vec3s rot; + s16 yawDiff; + s16 timer; + s16 yawTarget; + + // Shake towards the end of the stun. + if (this->stunTimer != 0) { + if (this->stunTimer < 30) { + if ((this->stunTimer % 2) != 0) { + this->actor.shape.rot.y += 0x800; + } else { + this->actor.shape.rot.y -= 0x800; + } + } + return; + } + + if (this->swayTimer == 0 && this->deathTimer == 0 && this->finishDeathTimer == 0) { + // not swaying or dying + if (this->takeDamageSpinTimer != 0 || this->gaveDamageSpinTimer != 0) { + // Skulltula is doing a spinning animation + this->actor.shape.rot.y += 0x2000; + return; + } + + if (this->actionFunc != EnSt_WaitOnGround) { + // set the timers to turn away or turn towards the player + this->rotAwayTimer = 30; + this->rotTowardsTimer = 0; + } + + if (this->rotAwayTimer != 0) { + // turn away from the player + this->rotAwayTimer--; + if (this->rotAwayTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_ROLL); + this->rotTowardsTimer = 30; + } + } else if (this->rotTowardsTimer != 0) { + // turn towards the player + this->rotTowardsTimer--; + if (this->rotTowardsTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_ROLL); + this->rotAwayTimer = 30; + } + yawDir = 0x8000; + } + + // calculate the new yaw to or away from the player. + rot = this->actor.shape.rot; + yawTarget = (this->actionFunc == EnSt_WaitOnGround ? this->actor.yawTowardsPlayer : this->initalYaw); + yawDiff = rot.y - (yawTarget ^ yawDir); + if (ABS(yawDiff) <= 0x4000) { + Math_SmoothStepToS(&rot.y, yawTarget ^ yawDir, 4, 0x2000, 1); + } else { + rot.y += 0x2000; + } + + this->actor.shape.rot = this->actor.world.rot = rot; + + // Do the shaking animation. + if (yawDir == 0 && this->rotAwayTimer < 0xA) { + timer = this->rotAwayTimer; + } else if (yawDir == 0x8000 && this->rotTowardsTimer < 0xA) { + timer = this->rotTowardsTimer; + } else { + return; + } + + if ((timer % 2) != 0) { + this->actor.shape.rot.y += 0x800; + } else { + this->actor.shape.rot.y -= 0x800; + } + } +} + +/** + * Checks to see if the Skulltula is done bouncing on the ground, + * spawns dust particles as the Skulltula hits the ground + */ +s32 EnSt_IsDoneBouncing(EnSt* this, GlobalContext* globalCtx) { + if (this->actor.velocity.y > 0.0f || this->groundBounces == 0) { + // the Skulltula is moving upwards or the groundBounces is 0 + return false; + } + + if (!(this->actor.bgCheckFlags & 1)) { + // the Skulltula is not on the ground. + return false; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + EnSt_SpawnDust(this, globalCtx, 10); + // creates an elastic bouncing effect, boucing up less for each hit on the ground. + this->actor.velocity.y = 6.0f / (4 - this->groundBounces); + this->groundBounces--; + if (this->groundBounces != 0) { + return false; + } else { + // make sure the Skulltula stays on the ground. + this->actor.velocity.y = 0.0f; + } + return true; +} + +void EnSt_Bob(EnSt* this, GlobalContext* globalCtx) { + f32 ySpeedTarget = 0.5f; + + if ((globalCtx->state.frames & 8) != 0) { + ySpeedTarget *= -1.0f; + } + Math_SmoothStepToF(&this->actor.velocity.y, ySpeedTarget, 0.4f, 1000.0f, 0.0f); +} + +s32 EnSt_IsCloseToPlayer(EnSt* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 yDist; + + if (this->takeDamageSpinTimer != 0) { + // skull is spinning from damage. + return false; + } else if (this->actor.xzDistToPlayer > 160.0f) { + // player is more than 160 xz units from the Skulltula + return false; + } + + yDist = this->actor.world.pos.y - player->actor.world.pos.y; + if (yDist < 0.0f || yDist > 400.0f) { + // player is above the Skulltula or more than 400 units below + // the Skulltula + return false; + } + + if (player->actor.world.pos.y < this->actor.floorHeight) { + // player is below the Skulltula's ground position + return false; + } + return true; +} + +s32 EnSt_IsCloseToInitalPos(EnSt* this) { + f32 velY = this->actor.velocity.y; + f32 checkY = this->actor.world.pos.y + (velY * 2.0f); + + if (checkY >= this->actor.home.pos.y) { + return true; + } + return false; +} + +s32 EnSt_IsCloseToGround(EnSt* this) { + f32 velY = this->actor.velocity.y; + f32 checkY = this->actor.world.pos.y + (velY * 2.0f); + + if (checkY - this->actor.floorHeight <= this->floorHeightOffset) { + return true; + } + return false; +} + +/** + * Does the animation of the Skulltula swaying back and forth after the Skulltula + * has been hit in the front by a sword + */ +void EnSt_Sway(EnSt* this) { + Vec3f amtToTranslate; + Vec3f translatedPos; + f32 swayAmt; + s16 rotAngle; + + if (this->swayTimer != 0) { + + this->swayAngle += 0xA28; + this->swayTimer--; + + if (this->swayTimer == 0) { + this->swayAngle = 0; + } + + swayAmt = this->swayTimer * (7.0f / 15.0f); + rotAngle = Math_SinS(this->swayAngle) * (swayAmt * (65536.0f / 360.0f)); + + if (this->absPrevSwayAngle >= ABS(rotAngle) && this->playSwayFlag == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_WAVE); + this->playSwayFlag = 1; + } + + if (this->absPrevSwayAngle < ABS(rotAngle)) { + this->playSwayFlag = 0; + } + + this->absPrevSwayAngle = ABS(rotAngle); + amtToTranslate.x = Math_SinS(rotAngle) * -200.0f; + amtToTranslate.y = Math_CosS(rotAngle) * -200.0f; + amtToTranslate.z = 0.0f; + Matrix_Push(); + Matrix_Translate(this->ceilingPos.x, this->ceilingPos.y, this->ceilingPos.z, MTXMODE_NEW); + Matrix_RotateY(this->actor.world.rot.y * (M_PI / 32768.0f), MTXMODE_APPLY); + Matrix_MultVec3f(&amtToTranslate, &translatedPos); + Matrix_Pop(); + this->actor.shape.rot.z = -(rotAngle * 2); + this->actor.world.pos.x = translatedPos.x; + this->actor.world.pos.z = translatedPos.z; + } +} + +void EnSt_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSt* this = (EnSt*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 14.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &object_st_Skel_005298, NULL, this->jointTable, this->morphTable, 30); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_0); + this->blureIdx = EnSt_CreateBlureEffect(globalCtx); + EnSt_InitColliders(this, globalCtx); + if (thisx->params == 2) { + this->actor.flags |= ACTOR_FLAG_7; + } + if (this->actor.params == 1) { + this->actor.naviEnemyId = 0x05; + } else { + this->actor.naviEnemyId = 0x04; + } + EnSt_CheckCeilingPos(this, globalCtx); + this->actor.flags |= ACTOR_FLAG_14; + this->actor.flags |= ACTOR_FLAG_24; + EnSt_SetColliderScale(this); + this->actor.gravity = 0.0f; + this->initalYaw = this->actor.world.rot.y; + EnSt_SetupAction(this, EnSt_StartOnCeilingOrGround); +} + +void EnSt_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSt* this = (EnSt*)thisx; + s32 i; + + Effect_Delete(globalCtx, this->blureIdx); + for (i = 0; i < 6; i++) { + Collider_DestroyCylinder(globalCtx, &this->colCylinder[i]); + } + Collider_DestroyJntSph(globalCtx, &this->colSph); +} + +void EnSt_WaitOnCeiling(EnSt* this, GlobalContext* globalCtx) { + if (EnSt_IsCloseToPlayer(this, globalCtx)) { + EnSt_SetDropAnimAndVel(this); + EnSt_SetupAction(this, EnSt_MoveToGround); + } else { + EnSt_Bob(this, globalCtx); + } +} + +/** + * Skulltula is waiting on the ground for the player to move away, or for + * a collider to have contact + */ +void EnSt_WaitOnGround(EnSt* this, GlobalContext* globalCtx) { + if (this->takeDamageSpinTimer != 0) { + this->takeDamageSpinTimer--; + if (this->takeDamageSpinTimer == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_3); + } + } + + if (this->animFrames != 0) { + this->animFrames--; + if (this->animFrames == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_3); + } + } + + if (!EnSt_IsCloseToPlayer(this, globalCtx)) { + // Player is no longer within range, return to ceiling. + EnSt_SetReturnToCeilingAnimation(this); + EnSt_SetupAction(this, EnSt_ReturnToCeiling); + return; + } + + if (DECR(this->sfxTimer) == 0) { + // play the "laugh" sfx every 64 frames. + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_LAUGH); + this->sfxTimer = 64; + } + + // simply bob up and down. + EnSt_Bob(this, globalCtx); +} + +void EnSt_LandOnGround(EnSt* this, GlobalContext* globalCtx) { + if (this->animFrames != 0) { + this->animFrames--; + if (this->animFrames == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_3); + } + } + + if (this->takeDamageSpinTimer != 0) { + this->takeDamageSpinTimer--; + if (this->takeDamageSpinTimer == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_3); + } + } + + this->sfxTimer++; + if (this->sfxTimer == 14) { + // play the sound effect of the Skulltula hitting the ground. + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DOWN_SET); + } + + if ((this->actor.floorHeight + this->floorHeightOffset) < this->actor.world.pos.y) { + // the Skulltula has hit the ground. + this->sfxTimer = 0; + EnSt_SetupAction(this, EnSt_WaitOnGround); + } else { + Math_SmoothStepToF(&this->actor.velocity.y, 2.0f, 0.3f, 1.0f, 0.0f); + } +} + +void EnSt_MoveToGround(EnSt* this, GlobalContext* globalCtx) { + if (this->takeDamageSpinTimer != 0) { + this->takeDamageSpinTimer--; + if (this->takeDamageSpinTimer == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENST_ANIM_5); + } + } + + if (!EnSt_IsCloseToPlayer(this, globalCtx)) { + // the player moved out of range, return to the ceiling. + EnSt_SetReturnToCeilingAnimation(this); + EnSt_SetupAction(this, EnSt_ReturnToCeiling); + } else if (EnSt_IsCloseToGround(this)) { + // The Skulltula has become close to the ground. + EnSt_SpawnBlastEffect(this, globalCtx); + EnSt_SetLandAnimation(this); + EnSt_SetupAction(this, EnSt_LandOnGround); + } else if (DECR(this->sfxTimer) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DOWN); + this->sfxTimer = 3; + } +} + +void EnSt_ReturnToCeiling(EnSt* this, GlobalContext* globalCtx) { + f32 animPctDone = this->skelAnime.curFrame / (this->skelAnime.animLength - 1.0f); + + if (animPctDone == 1.0f) { + EnSt_SetReturnToCeilingAnimation(this); + } + + if (EnSt_IsCloseToPlayer(this, globalCtx)) { + // player came back into range + EnSt_SetDropAnimAndVel(this); + EnSt_SetupAction(this, EnSt_MoveToGround); + } else if (EnSt_IsCloseToInitalPos(this)) { + // the Skulltula is close to the initial postion. + EnSt_SetWaitingAnimation(this); + EnSt_SetupAction(this, EnSt_WaitOnCeiling); + } else { + // accelerate based on the current animation frame. + this->actor.velocity.y = 4.0f * animPctDone; + } +} + +/** + * The Skulltula has been killed, bounce around + */ +void EnSt_BounceAround(EnSt* this, GlobalContext* globalCtx) { + this->actor.colorFilterTimer = this->deathTimer; + func_8002D868(&this->actor); + this->actor.world.rot.x += 0x800; + this->actor.world.rot.z -= 0x800; + this->actor.shape.rot = this->actor.world.rot; + if (EnSt_IsDoneBouncing(this, globalCtx)) { + this->actor.shape.yOffset = 400.0f; + this->actor.speedXZ = 1.0f; + this->actor.gravity = -2.0f; + EnSt_SetupAction(this, EnSt_FinishBouncing); + } else { + Math_SmoothStepToF(&this->actor.shape.yOffset, 400.0f, 0.4f, 10000.0f, 0.0f); + } +} + +/** + * Finish up the bouncing animation, and rotate towards the final position + */ +void EnSt_FinishBouncing(EnSt* this, GlobalContext* globalCtx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (DECR(this->deathTimer) == 0) { + this->actor.velocity = zeroVec; + this->finishDeathTimer = 8; + EnSt_SetupAction(this, EnSt_Die); + return; + } + + if (DECR(this->setTargetYawTimer) == 0) { + this->deathYawTarget = Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos); + this->setTargetYawTimer = 8; + } + + Math_SmoothStepToS(&this->actor.world.rot.x, 0x3FFC, 4, 0x2710, 1); + Math_SmoothStepToS(&this->actor.world.rot.z, 0, 4, 0x2710, 1); + Math_SmoothStepToS(&this->actor.world.rot.y, this->deathYawTarget, 0xA, 0x2710, 1); + + this->actor.shape.rot = this->actor.world.rot; + + func_8002D868(&this->actor); + this->groundBounces = 2; + EnSt_IsDoneBouncing(this, globalCtx); +} + +/** + * Spawn the enemy dying effects, and drop a random item + */ +void EnSt_Die(EnSt* this, GlobalContext* globalCtx) { + if (DECR(this->finishDeathTimer) != 0) { + EnSt_SpawnDeadEffect(this, globalCtx); + } else { + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0xE0); + Actor_Kill(&this->actor); + } +} + +void EnSt_StartOnCeilingOrGround(EnSt* this, GlobalContext* globalCtx) { + if (!EnSt_IsCloseToGround(this)) { + this->rotAwayTimer = 60; + EnSt_SetupAction(this, EnSt_WaitOnCeiling); + EnSt_WaitOnCeiling(this, globalCtx); + } else { + EnSt_SetLandAnimation(this); + EnSt_SetupAction(this, EnSt_LandOnGround); + EnSt_LandOnGround(this, globalCtx); + } +} + +void EnSt_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSt* this = (EnSt*)thisx; + s32 pad; + Color_RGBA8 color = { 0, 0, 0, 0 }; + + if (this->actor.flags & ACTOR_FLAG_15) { + SkelAnime_Update(&this->skelAnime); + } else if (!EnSt_CheckColliders(this, globalCtx)) { + // no collision has been detected. + + if (this->stunTimer == 0) { + SkelAnime_Update(&this->skelAnime); + } + + if (this->swayTimer == 0 && this->stunTimer == 0) { + func_8002D7EC(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + + if ((this->stunTimer == 0) && (this->swayTimer == 0)) { + // run the current action if the Skulltula isn't stunned + // or swaying. + this->actionFunc(this, globalCtx); + } else if (this->stunTimer != 0) { + // decrement the stun timer. + EnSt_DecrStunTimer(this); + } else { + // sway the Skulltula. + EnSt_Sway(this); + } + + EnSt_UpdateYaw(this, globalCtx); + + if (this->actionFunc == EnSt_WaitOnGround) { + if ((globalCtx->state.frames & 0x10) != 0) { + color.r = 255; + } + } + + EnSt_SetTeethColor(this, color.r, color.g, color.b, 8); + EnSt_UpdateCylinders(this, globalCtx); + Actor_SetFocus(&this->actor, 0.0f); + } +} + +s32 EnSt_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dListP, Vec3f* pos, Vec3s* rot, void* thisx) { + EnSt* this = (EnSt*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_st.c", 2260); + switch (limbIndex) { + case 1: + if (this->gaveDamageSpinTimer != 0 && this->swayTimer == 0) { + if (this->gaveDamageSpinTimer >= 2) { + EnSt_AddBlurVertex(this); + } else { + EnSt_AddBlurSpace(this); + } + } + break; + case 4: + // teeth + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, this->teethR, this->teethG, this->teethB, 0); + break; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_st.c", 2295); + return false; +} + +void EnSt_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dListP, Vec3s* rot, void* thisx) { + EnSt* this = (EnSt*)thisx; + + Collider_UpdateSpheres(limbIndex, &this->colSph); +} + +void EnSt_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSt* this = (EnSt*)thisx; + + EnSt_CheckBodyStickHit(this, globalCtx); + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnSt_OverrideLimbDraw, + EnSt_PostLimbDraw, this); +} diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.h b/soh/src/overlays/actors/ovl_En_St/z_en_st.h new file mode 100644 index 000000000..d6feb1106 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.h @@ -0,0 +1,50 @@ +#ifndef Z_EN_ST_H +#define Z_EN_ST_H + +#include "ultra64.h" +#include "global.h" + +struct EnSt; + +typedef void (*EnStActionFunc)(struct EnSt* this, GlobalContext* globalCtx); + +typedef struct EnSt { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnStActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder colCylinder[6]; + /* 0x035C */ ColliderJntSph colSph; + /* 0x037C */ ColliderJntSphElement colSphItems[1]; + /* 0x03BC */ s16 initalYaw; + /* 0x03BE */ s16 deathYawTarget; + /* 0x03C0 */ s16 groundBounces; + /* 0x03C2 */ s16 animFrames; + /* 0x03C4 */ s16 swayTimer; + /* 0x03C6 */ s16 setTargetYawTimer; + /* 0x03C8 */ s16 rotAwayTimer; + /* 0x03CA */ s16 rotTowardsTimer; + /* 0x03CC */ s16 takeDamageSpinTimer; + /* 0x03CE */ s16 stunTimer; + /* 0x03D0 */ s16 invulnerableTimer; + /* 0x03D2 */ s16 sfxTimer; + /* 0x03D4 */ s16 gaveDamageSpinTimer; + /* 0x03D6 */ s16 finishDeathTimer; + /* 0x03D8 */ s16 deathTimer; + /* 0x03DA */ s16 absPrevSwayAngle; + /* 0x03DC */ u8 playSwayFlag; + /* 0x03DD */ u8 teethR; + /* 0x03DE */ u8 teethG; + /* 0x03DF */ u8 teethB; + /* 0x03DD */ char unk_3E0[4]; + /* 0x03E4 */ Vec3f unusedPos; + /* 0x03F0 */ Vec3f ceilingPos; + /* 0x03FC */ char unk_3FC[0x8]; + /* 0x0404 */ s32 blureIdx; + /* 0x0408 */ f32 colliderScale; + /* 0x040C */ f32 floorHeightOffset; + /* 0x0410 */ s16 swayAngle; + /* 0x0412 */ Vec3s jointTable[30]; + /* 0x04C6 */ Vec3s morphTable[30]; +} EnSt; // size = 0x057C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c new file mode 100644 index 000000000..73866b8d2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.c @@ -0,0 +1,413 @@ +/* + * File: z_en_sth.c + * Overlay: ovl_En_Sth + * Description: Uncursed House of Skulltula People + */ + +#include "vt.h" +#include "z_en_sth.h" +#include "objects/object_ahg/object_ahg.h" +#include "objects/object_boj/object_boj.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnSth_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSth_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSth_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSth_Update2(Actor* thisx, GlobalContext* globalCtx); +void EnSth_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnSth_WaitForObjectLoaded(EnSth* this, GlobalContext* globalCtx); +void EnSth_ParentRewardObtainedWait(EnSth* this, GlobalContext* globalCtx); +void EnSth_RewardUnobtainedWait(EnSth* this, GlobalContext* globalCtx); +void EnSth_ChildRewardObtainedWait(EnSth* this, GlobalContext* globalCtx); + +const ActorInit En_Sth_InitVars = { + ACTOR_EN_STH, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnSth), + (ActorFunc)EnSth_Init, + (ActorFunc)EnSth_Destroy, + (ActorFunc)EnSth_Update, + NULL, + NULL, +}; + +#include "overlays/ovl_En_Sth/ovl_En_Sth.h" + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +static s16 sObjectIds[6] = { + OBJECT_AHG, OBJECT_BOJ, OBJECT_BOJ, OBJECT_BOJ, OBJECT_BOJ, OBJECT_BOJ, +}; + +static FlexSkeletonHeader* sSkeletons[6] = { + &object_ahg_Skel_0000F0, + &object_boj_Skel_0000F0, + &object_boj_Skel_0000F0, + &object_boj_Skel_0000F0, + &object_boj_Skel_0000F0, + &object_boj_Skel_0000F0, +}; + +static AnimationHeader* sAnimations[6] = { + &sParentDanceAnim, &sChildDanceAnim, &sChildDanceAnim, &sChildDanceAnim, &sChildDanceAnim, &sChildDanceAnim, +}; + +static EnSthActionFunc sRewardObtainedWaitActions[6] = { + EnSth_ParentRewardObtainedWait, EnSth_ChildRewardObtainedWait, EnSth_ChildRewardObtainedWait, + EnSth_ChildRewardObtainedWait, EnSth_ChildRewardObtainedWait, EnSth_ChildRewardObtainedWait, +}; + +static u16 sEventFlags[6] = { + 0x0000, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, +}; + +static s16 sGetItemIds[6] = { + GI_RUPEE_GOLD, GI_WALLET_ADULT, GI_STONE_OF_AGONY, GI_WALLET_GIANT, GI_BOMBCHUS_10, GI_HEART_PIECE, +}; + +static Vec3f D_80B0B49C = { 700.0f, 400.0f, 0.0f }; + +static Color_RGB8 sTunicColors[6] = { + { 190, 110, 0 }, { 0, 180, 110 }, { 0, 255, 80 }, { 255, 160, 60 }, { 190, 230, 250 }, { 240, 230, 120 }, +}; + +void EnSth_SetupAction(EnSth* this, EnSthActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnSth_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSth* this = (EnSth*)thisx; + + s16 objectId; + s32 params = this->actor.params; + s32 objectBankIdx; + + osSyncPrintf(VT_FGCOL(BLUE) "金スタル屋 no = %d\n" VT_RST, params); // "Gold Skulltula Shop" + if (this->actor.params == 0) { + if (gSaveContext.inventory.gsTokens < 100) { + Actor_Kill(&this->actor); + // "Gold Skulltula Shop I still can't be a human" + osSyncPrintf("金スタル屋 まだ 人間に戻れない \n"); + return; + } + } else if (gSaveContext.inventory.gsTokens < (this->actor.params * 10)) { + Actor_Kill(&this->actor); + // "Gold Skulltula Shop I still can't be a human" + osSyncPrintf(VT_FGCOL(BLUE) "金スタル屋 まだ 人間に戻れない \n" VT_RST); + return; + } + + objectId = sObjectIds[params]; + if (objectId != 1) { + objectBankIdx = Object_GetIndex(&globalCtx->objectCtx, objectId); + } else { + objectBankIdx = 0; + } + + osSyncPrintf("bank_ID = %d\n", objectBankIdx); + if (objectBankIdx < 0) { + ASSERT(0, "0", "../z_en_sth.c", 1564); + } + this->objectBankIdx = objectBankIdx; + this->drawFunc = EnSth_Draw; + Actor_SetScale(&this->actor, 0.01f); + EnSth_SetupAction(this, EnSth_WaitForObjectLoaded); + this->actor.draw = NULL; + this->unk_2B2 = 0; + this->actor.targetMode = 6; +} + +void EnSth_SetupShapeColliderUpdate2AndDraw(EnSth* this, GlobalContext* globalCtx) { + s32 pad; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->actor.update = EnSth_Update2; + this->actor.draw = this->drawFunc; +} + +void EnSth_SetupAfterObjectLoaded(EnSth* this, GlobalContext* globalCtx) { + s32 pad; + s16* params; + + EnSth_SetupShapeColliderUpdate2AndDraw(this, globalCtx); + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objectBankIdx].segment); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, sSkeletons[this->actor.params], NULL, this->jointTable, + this->morphTable, 16); + Animation_PlayLoop(&this->skelAnime, sAnimations[this->actor.params]); + + this->eventFlag = sEventFlags[this->actor.params]; + params = &this->actor.params; + if (gSaveContext.eventChkInf[13] & this->eventFlag) { + EnSth_SetupAction(this, sRewardObtainedWaitActions[*params]); + } else { + EnSth_SetupAction(this, EnSth_RewardUnobtainedWait); + } +} + +void EnSth_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSth* this = (EnSth*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnSth_WaitForObjectLoaded(EnSth* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objectBankIdx)) { + this->actor.objBankIndex = this->objectBankIdx; + this->actionFunc = EnSth_SetupAfterObjectLoaded; + } +} + +void EnSth_FacePlayer(EnSth* this, GlobalContext* globalCtx) { + s32 pad; + s16 diffRot = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(diffRot) <= 0x4000) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 6, 0xFA0, 0x64); + this->actor.world.rot.y = this->actor.shape.rot.y; + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_2AC, this->actor.focus.pos); + } else { + if (diffRot < 0) { + Math_SmoothStepToS(&this->headRot.y, -0x2000, 6, 0x1838, 0x100); + } else { + Math_SmoothStepToS(&this->headRot.y, 0x2000, 6, 0x1838, 0x100); + } + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 0xC, 0x3E8, 0x64); + this->actor.world.rot.y = this->actor.shape.rot.y; + } +} + +void EnSth_LookAtPlayer(EnSth* this, GlobalContext* globalCtx) { + s16 diffRot = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(diffRot) <= 0x4300) && (this->actor.xzDistToPlayer < 100.0f)) { + func_80038290(globalCtx, &this->actor, &this->headRot, &this->unk_2AC, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->headRot.x, 0, 6, 0x1838, 0x64); + Math_SmoothStepToS(&this->headRot.y, 0, 6, 0x1838, 0x64); + Math_SmoothStepToS(&this->unk_2AC.x, 0, 6, 0x1838, 0x64); + Math_SmoothStepToS(&this->unk_2AC.y, 0, 6, 0x1838, 0x64); + } +} + +void EnSth_RewardObtainedTalk(EnSth* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + if (this->actor.params == 0) { + EnSth_SetupAction(this, EnSth_ParentRewardObtainedWait); + } else { + EnSth_SetupAction(this, EnSth_ChildRewardObtainedWait); + } + } + EnSth_FacePlayer(this, globalCtx); +} + +void EnSth_ParentRewardObtainedWait(EnSth* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + EnSth_SetupAction(this, EnSth_RewardObtainedTalk); + } else { + this->actor.textId = 0x23; + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + EnSth_LookAtPlayer(this, globalCtx); +} + +void EnSth_GivePlayerItem(EnSth* this, GlobalContext* globalCtx) { + u16 getItemId = sGetItemIds[this->actor.params]; + + switch (this->actor.params) { + case 1: + case 3: + switch (CUR_UPG_VALUE(UPG_WALLET)) { + case 0: + getItemId = GI_WALLET_ADULT; + break; + + case 1: + getItemId = GI_WALLET_GIANT; + break; + } + break; + } + + func_8002F434(&this->actor, globalCtx, getItemId, 10000.0f, 50.0f); +} + +void EnSth_GiveReward(EnSth* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + EnSth_SetupAction(this, EnSth_RewardObtainedTalk); + gSaveContext.eventChkInf[13] |= this->eventFlag; + } else { + EnSth_GivePlayerItem(this, globalCtx); + } + EnSth_FacePlayer(this, globalCtx); +} + +void EnSth_RewardUnobtainedTalk(EnSth* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + EnSth_SetupAction(this, EnSth_GiveReward); + EnSth_GivePlayerItem(this, globalCtx); + } + EnSth_FacePlayer(this, globalCtx); +} + +void EnSth_RewardUnobtainedWait(EnSth* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + EnSth_SetupAction(this, EnSth_RewardUnobtainedTalk); + } else { + if (this->actor.params == 0) { + this->actor.textId = 0x28; + } else { + this->actor.textId = 0x21; + } + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + EnSth_LookAtPlayer(this, globalCtx); +} + +void EnSth_ChildRewardObtainedWait(EnSth* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + EnSth_SetupAction(this, EnSth_RewardObtainedTalk); + } else { + if (gSaveContext.inventory.gsTokens < 50) { + this->actor.textId = 0x20; + } else { + this->actor.textId = 0x1F; + } + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + EnSth_LookAtPlayer(this, globalCtx); +} + +void EnSth_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSth* this = (EnSth*)thisx; + + this->actionFunc(this, globalCtx); +} + +void EnSth_Update2(Actor* thisx, GlobalContext* globalCtx) { + EnSth* this = (EnSth*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if (SkelAnime_Update(&this->skelAnime)) { + this->skelAnime.curFrame = 0.0f; + } + this->actionFunc(this, globalCtx); + + // Likely an unused blink timer and eye index + if (DECR(this->unk_2B6) == 0) { + this->unk_2B6 = Rand_S16Offset(0x3C, 0x3C); + } + this->unk_2B4 = this->unk_2B6; + if (this->unk_2B4 >= 3) { + this->unk_2B4 = 0; + } +} + +s32 EnSth_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnSth* this = (EnSth*)thisx; + + s32 temp_v1; + + if (limbIndex == 15) { + rot->x += this->headRot.y; + rot->z += this->headRot.x; + *dList = D_80B0A050; + } + + if (this->unk_2B2 & 2) { + this->unk_2B2 &= ~2; + return 0; + } + + if ((limbIndex == 8) || (limbIndex == 10) || (limbIndex == 13)) { + temp_v1 = limbIndex * 0x32; + rot->y += (Math_SinS(globalCtx->state.frames * (temp_v1 + 0x814)) * 200.0f); + rot->z += (Math_CosS(globalCtx->state.frames * (temp_v1 + 0x940)) * 200.0f); + } + return 0; +} + +void EnSth_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnSth* this = (EnSth*)thisx; + + if (limbIndex == 15) { + Matrix_MultVec3f(&D_80B0B49C, &this->actor.focus.pos); + if (this->actor.params != 0) { // Children + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_sth.c", 2079); + + gSPDisplayList(POLY_OPA_DISP++, D_80B0A3C0); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_sth.c", 2081); + } + } +} + +Gfx* EnSth_AllocColorDList(GraphicsContext* globalCtx, u8 envR, u8 envG, u8 envB, u8 envA) { + Gfx* dList; + + dList = Graph_Alloc(globalCtx, 2 * sizeof(Gfx)); + gDPSetEnvColor(dList, envR, envG, envB, envA); + gSPEndDisplayList(dList + 1); + + return dList; +} + +void EnSth_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSth* this = (EnSth*)thisx; + Color_RGB8* envColor1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_sth.c", 2133); + + gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objectBankIdx].segment); + func_800943C8(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, + EnSth_AllocColorDList(globalCtx->state.gfxCtx, sTunicColors[this->actor.params].r, + sTunicColors[this->actor.params].g, sTunicColors[this->actor.params].b, 255)); + + if (this->actor.params == 0) { + gSPSegment(POLY_OPA_DISP++, 0x09, EnSth_AllocColorDList(globalCtx->state.gfxCtx, 190, 110, 0, 255)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x09, EnSth_AllocColorDList(globalCtx->state.gfxCtx, 90, 110, 130, 255)); + } + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnSth_OverrideLimbDraw, EnSth_PostLimbDraw, &this->actor); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_sth.c", 2176); +} diff --git a/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.h b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.h new file mode 100644 index 000000000..ce90fbfdd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sth/z_en_sth.h @@ -0,0 +1,28 @@ +#ifndef Z_EN_STH_H +#define Z_EN_STH_H + +#include "ultra64.h" +#include "global.h" + +struct EnSth; + +typedef void (*EnSthActionFunc)(struct EnSth*, GlobalContext*); + +typedef struct EnSth { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ SkelAnime skelAnime; + /* 0x01DC */ Vec3s jointTable[16]; + /* 0x023C */ Vec3s morphTable[16]; + /* 0x029C */ u16 eventFlag; + /* 0x02A0 */ ActorFunc drawFunc; + /* 0x02A4 */ u8 objectBankIdx; + /* 0x02A6 */ Vec3s headRot; + /* 0x02AC */ Vec3s unk_2AC; + /* 0x02B2 */ u16 unk_2B2; + /* 0x02B4 */ s16 unk_2B4; + /* 0x02B6 */ s16 unk_2B6; + /* 0x02B8 */ EnSthActionFunc actionFunc; +} EnSth; // size = 0x02BC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Stream/z_en_stream.c b/soh/src/overlays/actors/ovl_En_Stream/z_en_stream.c new file mode 100644 index 000000000..82c03b211 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Stream/z_en_stream.c @@ -0,0 +1,145 @@ +/* + * File: z_en_stream.c + * Overlay: ovl_En_Stream + * Description: Water Vortex + */ + +#include "z_en_stream.h" +#include "objects/object_stream/object_stream.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnStream_Init(Actor* thisx, GlobalContext* globalCtx); +void EnStream_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnStream_Update(Actor* thisx, GlobalContext* globalCtx); +void EnStream_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnStream_WaitForPlayer(EnStream* this, GlobalContext* globalCtx); + +const ActorInit En_Stream_InitVars = { + ACTOR_EN_STREAM, + ACTORCAT_BG, + FLAGS, + OBJECT_STREAM, + sizeof(EnStream), + (ActorFunc)EnStream_Init, + (ActorFunc)EnStream_Destroy, + (ActorFunc)EnStream_Update, + (ActorFunc)EnStream_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 20, ICHAIN_STOP), +}; + +void EnStream_SetupAction(EnStream* this, EnStreamActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnStream_Init(Actor* thisx, GlobalContext* globalCtx) { + EnStream* this = (EnStream*)thisx; + + this->unk_150 = thisx->params & 0xFF; + Actor_ProcessInitChain(thisx, sInitChain); + if ((this->unk_150 != 0) && (this->unk_150 == 1)) { + thisx->scale.y = 0.01f; + } + EnStream_SetupAction(this, EnStream_WaitForPlayer); +} + +void EnStream_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +// Checks if the player is in range of the vortex +s32 func_80B0B81C(Vec3f* vortexPosRot, Vec3f* playerPosRot, Vec3f* posDifference, f32 vortexYScale) { + s32 ret = 0; + f32 smallConstant = 28.0f; + f32 upperBounds = 160 * vortexYScale * 50.0f; + f32 lowerBounds = 0 * vortexYScale * 50.0f; + f32 xzDist; + f32 range; + + posDifference->x = playerPosRot->x - vortexPosRot->x; + posDifference->y = playerPosRot->y - vortexPosRot->y; + posDifference->z = playerPosRot->z - vortexPosRot->z; + xzDist = sqrtf(SQ(posDifference->x) + SQ(posDifference->z)); + + if (lowerBounds <= posDifference->y && posDifference->y <= upperBounds) { + posDifference->y -= lowerBounds; + + range = ((75.0f - smallConstant) * (posDifference->y / (upperBounds - lowerBounds))) + 28.0f; + if (xzDist <= range) { + ret = 1; + } + } + + if ((posDifference->y <= lowerBounds) && (xzDist <= 28.0f)) { + ret = 2; + } + + return ret; +} + +void EnStream_SuckPlayer(EnStream* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad48; + Vec3f posDifference; + f32 xzDist; + f32 yDistWithOffset; + s32 pad30; + s32 pad2C; + + if (func_80B0B81C(&this->actor.world.pos, &player->actor.world.pos, &posDifference, this->actor.scale.y) != 0) { + xzDist = sqrtf(SQ(posDifference.x) + SQ(posDifference.z)); + yDistWithOffset = player->actor.world.pos.y - (this->actor.world.pos.y - 90.0f); + player->windDirection = Math_FAtan2F(-posDifference.x, -posDifference.z) * (0x8000 / M_PI); + if (xzDist > 3.0f) { + Math_SmoothStepToF(&player->windSpeed, 3.0f, 0.5f, xzDist, 0.0f); + } else { + player->windSpeed = 0.0f; + Math_SmoothStepToF(&player->actor.world.pos.x, this->actor.world.pos.x, 0.5f, 3.0f, 0.0f); + Math_SmoothStepToF(&player->actor.world.pos.z, this->actor.world.pos.z, 0.5f, 3.0f, 0.0f); + } + if (yDistWithOffset > 0.0f) { + Math_SmoothStepToF(&player->actor.velocity.y, -3.0f, 0.7f, yDistWithOffset, 0.0f); + if (posDifference.y < -70.0f) { + player->stateFlags2 |= 0x80000000; + } + } + } else { + EnStream_SetupAction(this, EnStream_WaitForPlayer); + } +} + +void EnStream_WaitForPlayer(EnStream* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 pad; + Vec3f temp; + + if (func_80B0B81C(&this->actor.world.pos, &player->actor.world.pos, &temp, this->actor.scale.y) != 0) { + EnStream_SetupAction(this, EnStream_SuckPlayer); + } +} + +void EnStream_Update(Actor* thisx, GlobalContext* globalCtx) { + EnStream* this = (EnStream*)thisx; + + this->actionFunc(this, globalCtx); + func_8002F948(thisx, NA_SE_EV_WHIRLPOOL - SFX_FLAG); +} + +void EnStream_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 multipliedFrames; + u32 frames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_stream.c", 295); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_stream.c", 299), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + multipliedFrames = frames * 20; + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frames * 30, -multipliedFrames, 0x40, 0x40, 1, + multipliedFrames, -multipliedFrames, 0x40, 0x40)); + gSPDisplayList(POLY_XLU_DISP++, object_stream_DL_000950); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_stream.c", 310); +} diff --git a/soh/src/overlays/actors/ovl_En_Stream/z_en_stream.h b/soh/src/overlays/actors/ovl_En_Stream/z_en_stream.h new file mode 100644 index 000000000..228fff4de --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Stream/z_en_stream.h @@ -0,0 +1,18 @@ +#ifndef Z_EN_STREAM_H +#define Z_EN_STREAM_H + +#include "ultra64.h" +#include "global.h" + +struct EnStream; + +typedef void (*EnStreamActionFunc)(struct EnStream*, GlobalContext*); + +typedef struct EnStream { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnStreamActionFunc actionFunc; + /* 0x0150 */ s32 unk_150; + /* 0x0154 */ char unk_154[0x4]; +} EnStream; // size = 0x0158 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c new file mode 100644 index 000000000..bd2792f11 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.c @@ -0,0 +1,1018 @@ +#include "z_en_sw.h" +#include "objects/object_st/object_st.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnSw_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSw_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSw_Draw(Actor* thisx, GlobalContext* globalCtx); +s32 func_80B0DFFC(EnSw* this, GlobalContext* globalCtx); +void func_80B0D364(EnSw* this, GlobalContext* globalCtx); +void func_80B0E5E0(EnSw* this, GlobalContext* globalCtx); +void func_80B0D590(EnSw* this, GlobalContext* globalCtx); +void func_80B0E90C(EnSw* this, GlobalContext* globalCtx); +void func_80B0E9BC(EnSw* this, GlobalContext* globalCtx); +void func_80B0E728(EnSw* this, GlobalContext* globalCtx); +void func_80B0DC7C(EnSw* this, GlobalContext* globalCtx); +s32 func_80B0C0CC(EnSw* this, GlobalContext* globalCtx, s32); +void func_80B0D3AC(EnSw* this, GlobalContext* globalCtx); +void func_80B0DB00(EnSw* this, GlobalContext* globalCtx); +void func_80B0D878(EnSw* this, GlobalContext* globalCtx); + +const ActorInit En_Sw_InitVars = { + ACTOR_EN_SW, + ACTORCAT_NPC, + FLAGS, + OBJECT_ST, + sizeof(EnSw), + (ActorFunc)EnSw_Init, + (ActorFunc)EnSw_Destroy, + (ActorFunc)EnSw_Update, + (ActorFunc)EnSw_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphItemsInit[1] = { + { + { ELEMTYPE_UNK0, { 0xFFCFFFFF, 0x00, 0x08 }, { 0xFFC3FFFE, 0x00, 0x00 }, 0x01, 0x05, 0x01 }, + { 2, { { 0, -300, 0 }, 21 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { COLTYPE_HIT6, 0x11, 0x09, 0x39, 0x10, COLSHAPE_JNTSPH }, + 1, + sJntSphItemsInit, +}; + +static CollisionCheckInfoInit2 D_80B0F074 = { 1, 2, 25, 25, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ENSW_ANIM_0, + /* 1 */ ENSW_ANIM_1, + /* 2 */ ENSW_ANIM_2, + /* 3 */ ENSW_ANIM_3 +} EnSwAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &object_st_Anim_000304, 1.0f, 0.0f, -1.0f, 0x01, 0.0f }, + { &object_st_Anim_000304, 1.0f, 0.0f, -1.0f, 0x01, -8.0f }, + { &object_st_Anim_0055A8, 1.0f, 0.0f, -1.0f, 0x01, -8.0f }, + { &object_st_Anim_005B98, 1.0f, 0.0f, -1.0f, 0x01, -8.0f }, +}; + +char D_80B0F630[0x80]; // unused + +void EnSw_CrossProduct(Vec3f* a, Vec3f* b, Vec3f* dst) { + dst->x = (a->y * b->z) - (a->z * b->y); + dst->y = (a->z * b->x) - (a->x * b->z); + dst->z = (a->x * b->y) - (a->y * b->x); +} + +s32 func_80B0BE20(EnSw* this, CollisionPoly* poly) { + Vec3f sp44; + Vec3f sp38; + f32 sp34; + f32 temp_f0; + s32 pad; + + this->actor.floorPoly = poly; + sp44.x = COLPOLY_GET_NORMAL(poly->normal.x); + sp44.y = COLPOLY_GET_NORMAL(poly->normal.y); + sp44.z = COLPOLY_GET_NORMAL(poly->normal.z); + sp34 = Math_FAcosF(DOTXYZ(sp44, this->unk_364)); + EnSw_CrossProduct(&this->unk_364, &sp44, &sp38); + Matrix_RotateAxis(sp34, &sp38, MTXMODE_NEW); + Matrix_MultVec3f(&this->unk_370, &sp38); + this->unk_370 = sp38; + EnSw_CrossProduct(&this->unk_370, &sp44, &this->unk_37C); + temp_f0 = Math3D_Vec3fMagnitude(&this->unk_37C); + if (temp_f0 < 0.001f) { + return 0; + } + this->unk_37C.x = this->unk_37C.x * (1.0f / temp_f0); + this->unk_37C.y = this->unk_37C.y * (1.0f / temp_f0); + this->unk_37C.z = this->unk_37C.z * (1.0f / temp_f0); + this->unk_364 = sp44; + this->unk_3D8.xx = this->unk_370.x; + this->unk_3D8.yx = this->unk_370.y; + this->unk_3D8.zx = this->unk_370.z; + this->unk_3D8.wx = 0.0f; + this->unk_3D8.xy = this->unk_364.x; + this->unk_3D8.yy = this->unk_364.y; + this->unk_3D8.zy = this->unk_364.z; + this->unk_3D8.wy = 0.0f; + this->unk_3D8.xz = this->unk_37C.x; + this->unk_3D8.yz = this->unk_37C.y; + this->unk_3D8.zz = this->unk_37C.z; + this->unk_3D8.wz = 0.0f; + this->unk_3D8.xw = 0.0f; + this->unk_3D8.yw = 0.0f; + this->unk_3D8.zw = 0.0f; + this->unk_3D8.ww = 1.0f; + Matrix_MtxFToYXZRotS(&this->unk_3D8, &this->actor.world.rot, 0); + //! @bug: Does not return. +} + +CollisionPoly* func_80B0C020(GlobalContext* globalCtx, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, s32* arg4) { + CollisionPoly* sp3C; + s32 pad; + + if (!BgCheck_EntityLineTest1(&globalCtx->colCtx, arg1, arg2, arg3, &sp3C, true, true, true, false, arg4)) { + return NULL; + } + + if (func_80041DB8(&globalCtx->colCtx, sp3C, *arg4) & 0x30) { + return NULL; + } + + if (SurfaceType_IsIgnoredByProjectiles(&globalCtx->colCtx, sp3C, *arg4)) { + return NULL; + } + + return sp3C; +} + +s32 func_80B0C0CC(EnSw* this, GlobalContext* globalCtx, s32 arg2) { + CollisionPoly* temp_v0_2; + CollisionPoly* temp_s1; + Vec3f sp9C; + Vec3f sp90; + Vec3f sp84; + Vec3f sp78; + s32 pad; + s32 sp70; + s32 sp6C; + s32 phi_s1; + s32 sp64; + + sp64 = 0; + this->unk_42C = 1; + sp84 = sp78 = this->actor.world.pos; + sp84.x += this->unk_364.x * 18.0f; + sp84.y += this->unk_364.y * 18.0f; + sp84.z += this->unk_364.z * 18.0f; + sp78.x -= this->unk_364.x * 18.0f; + sp78.y -= this->unk_364.y * 18.0f; + sp78.z -= this->unk_364.z * 18.0f; + temp_s1 = func_80B0C020(globalCtx, &sp84, &sp78, &sp90, &sp70); + + if ((temp_s1 != NULL) && (this->unk_360 == 0)) { + sp78.x = sp84.x + (this->unk_37C.x * 24); + sp78.y = sp84.y + (this->unk_37C.y * 24); + sp78.z = sp84.z + (this->unk_37C.z * 24); + temp_v0_2 = func_80B0C020(globalCtx, &sp84, &sp78, &sp9C, &sp6C); + if (temp_v0_2 != NULL) { + if (arg2 == 1) { + func_80B0BE20(this, temp_v0_2); + this->actor.world.pos = sp9C; + this->actor.floorBgId = sp6C; + } + } else { + if (this->actor.floorPoly != temp_s1) { + func_80B0BE20(this, temp_s1); + } + this->actor.world.pos = sp90; + this->actor.floorBgId = sp70; + } + sp64 = 1; + } else { + sp84 = sp78; + for (phi_s1 = 0; phi_s1 < 3; phi_s1++) { + if (phi_s1 == 0) { + sp78.x = sp84.x - (this->unk_37C.x * 24.0f); + sp78.y = sp84.y - (this->unk_37C.y * 24.0f); + if (0) {} + sp78.z = sp84.z - (this->unk_37C.z * 24.0f); + } else if (phi_s1 == 1) { + sp78.x = sp84.x + (this->unk_370.x * 24.0f); + sp78.y = sp84.y + (this->unk_370.y * 24.0f); + sp78.z = sp84.z + (this->unk_370.z * 24.0f); + } else { + sp78.x = sp84.x - (this->unk_370.x * 24.0f); + sp78.y = sp84.y - (this->unk_370.y * 24.0f); + sp78.z = sp84.z - (this->unk_370.z * 24.0f); + } + temp_v0_2 = func_80B0C020(globalCtx, &sp84, &sp78, &sp9C, &sp6C); + if (temp_v0_2 != NULL) { + if (arg2 == 1) { + func_80B0BE20(this, temp_v0_2); + this->actor.world.pos = sp9C; + this->actor.floorBgId = sp6C; + } + sp64 = 1; + break; + } + } + } + + Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x, 8, 0xFA0, 1); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 8, 0xFA0, 1); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->actor.world.rot.z, 8, 0xFA0, 1); + + return sp64; +} + +void EnSw_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSw* this = (EnSw*)thisx; + s32 phi_v0; + Vec3f sp4C = { 0.0f, 0.0f, 0.0f }; + s32 pad; + + if (thisx->params & 0x8000) { + phi_v0 = (((thisx->params - 0x8000) & 0xE000) >> 0xD) + 1; + thisx->params = (thisx->params & 0x1FFF) | (phi_v0 << 0xD); + } + + if (((thisx->params & 0xE000) >> 0xD) > 0) { + phi_v0 = ((thisx->params & 0x1F00) >> 8) - 1; + thisx->params = (thisx->params & 0xE0FF) | (phi_v0 << 8); + } + + // Check to see if this gold skull token has already been retrieved. + if (GET_GS_FLAGS((thisx->params & 0x1F00) >> 8) & (thisx->params & 0xFF)) { + Actor_Kill(&this->actor); + return; + } + + SkelAnime_Init(globalCtx, &this->skelAnime, &object_st_Skel_005298, NULL, this->jointTable, this->morphTable, 30); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENSW_ANIM_0); + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->sphs); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, DamageTable_Get(0xE), &D_80B0F074); + this->actor.scale.x = 0.02f; + + if (((thisx->params & 0xE000) >> 0xD) == 0) { + this->actor.world.rot.x = 0; + this->actor.world.rot.z = 0; + thisx->shape.rot = this->actor.world.rot; + this->unk_484.y = this->actor.world.pos.y; + this->unk_484.x = this->actor.world.pos.x + (Math_SinS(this->actor.world.rot.y) * -60.0f); + this->unk_484.z = this->actor.world.pos.z + (Math_CosS(this->actor.world.rot.y) * -60.0f); + func_80B0DFFC(this, globalCtx); + this->actor.home.pos = this->actor.world.pos; + } else { + this->unk_370.x = Math_SinS(thisx->shape.rot.y + 0x4000); + this->unk_370.y = 0.0f; + this->unk_370.z = Math_CosS(thisx->shape.rot.y + 0x4000); + this->unk_364.x = 0.0f; + this->unk_364.y = 1.0f; + this->unk_364.z = 0.0f; + this->unk_37C.x = Math_SinS(thisx->shape.rot.y); + this->unk_37C.y = 0.0f; + this->unk_37C.z = Math_CosS(thisx->shape.rot.y); + func_80B0C0CC(this, globalCtx, 1); + } + + if (((thisx->params & 0xE000) >> 0xD) >= 3) { + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + switch ((thisx->params & 0xE000) >> 0xD) { + case 3: + case 4: + this->unk_360 = 1; + this->actor.velocity.y = 8.0f; + this->actor.speedXZ = 4.0f; + this->actor.gravity = -1.0f; + case 2: + this->actor.scale.x = 0.0f; + case 1: + this->collider.elements[0].info.toucher.damage *= 2; + this->actor.naviEnemyId = 0x20; + this->actor.colChkInfo.health *= 2; + this->actor.flags &= ~ACTOR_FLAG_0; + break; + default: + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + this->actor.naviEnemyId = 0x1F; + break; + } + + this->unk_38E = Rand_S16Offset(0xF, 0x1E); + Actor_SetScale(&this->actor, this->actor.scale.x); + this->actor.home.pos = this->actor.world.pos; + thisx->shape.rot = this->actor.world.rot; + + if (((thisx->params & 0xE000) >> 0xD) >= 3) { + this->unk_38C = 0x28; + this->unk_394 = 1; + this->actionFunc = func_80B0D364; + } else if (((thisx->params & 0xE000) >> 0xD) == 0) { + this->actionFunc = func_80B0E5E0; + } else { + this->actionFunc = func_80B0D590; + } +} + +void EnSw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSw* this = (EnSw*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +s32 func_80B0C9F0(EnSw* this, GlobalContext* globalCtx) { + s32 phi_v1 = false; + + if (this->actor.xyzDistToPlayerSq < SQ(400.0f) && ((this->actor.params & 0xE000) >> 0xD) == 0 && + globalCtx->actorCtx.unk_02 != 0) { + + this->actor.colChkInfo.damage = this->actor.colChkInfo.health; + phi_v1 = true; + } + + if (this->unk_392 == 0) { + if ((this->collider.base.acFlags & 2) || phi_v1) { + this->collider.base.acFlags &= ~2; + this->unk_392 = 0x10; + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, this->unk_392); + if (Actor_ApplyDamage(&this->actor) != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_DAMAGE); + return true; + } + Enemy_StartFinishingBlow(globalCtx, &this->actor); + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + this->skelAnime.playSpeed = 8.0f; + if ((globalCtx->state.frames & 1) == 0) { + this->unk_420 = 0.1f; + } else { + this->unk_420 = -0.1f; + } + this->unk_394 = 0xA; + this->unk_38A = 1; + this->unk_420 *= 4.0f; + this->actionFunc = func_80B0D878; + } else { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_38A = 2; + this->actor.shape.shadowScale = 16.0f; + this->actor.gravity = -1.0f; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = func_80B0DB00; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_DEAD); + return true; + } + } + + if ((this->unk_390 == 0) && (this->collider.base.atFlags & 2)) { + this->unk_390 = 30; + } + + return false; +} + +void func_80B0CBE8(EnSw* this, GlobalContext* globalCtx) { + if ((((this->actor.params & 0xE000) >> 0xD) > 0) && (this->actionFunc != func_80B0D590)) { + if (this->unk_392 != 0) { + this->unk_392--; + } + } else { + if ((DECR(this->unk_390) == 0) && (this->actor.colChkInfo.health != 0)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if ((DECR(this->unk_392) == 0) && (this->actor.colChkInfo.health != 0)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +s32 func_80B0CCF4(EnSw* this, f32* arg1) { + CollisionPoly* temp_v1; + f32 temp_f0; + Vec3f sp6C; + MtxF sp2C; + + if (this->actor.floorPoly == NULL) { + return false; + } + + temp_v1 = this->actor.floorPoly; + sp6C.x = COLPOLY_GET_NORMAL(temp_v1->normal.x); + sp6C.y = COLPOLY_GET_NORMAL(temp_v1->normal.y); + sp6C.z = COLPOLY_GET_NORMAL(temp_v1->normal.z); + Matrix_RotateAxis(*arg1, &sp6C, MTXMODE_NEW); + Matrix_MultVec3f(&this->unk_370, &sp6C); + this->unk_370 = sp6C; + EnSw_CrossProduct(&this->unk_370, &this->unk_364, &this->unk_37C); + temp_f0 = Math3D_Vec3fMagnitude(&this->unk_37C); + if (temp_f0 < 0.001f) { + return false; + } + temp_f0 = 1.0f / temp_f0; + this->unk_37C.x *= temp_f0; + this->unk_37C.y *= temp_f0; + this->unk_37C.z *= temp_f0; + sp2C.xx = this->unk_370.x; + sp2C.yx = this->unk_370.y; + sp2C.zx = this->unk_370.z; + sp2C.wx = 0.0f; + sp2C.xy = this->unk_364.x; + sp2C.yy = this->unk_364.y; + sp2C.zy = this->unk_364.z; + sp2C.wy = 0.0f; + sp2C.xz = this->unk_37C.x; + sp2C.yz = this->unk_37C.y; + sp2C.zz = this->unk_37C.z; + sp2C.wz = 0.0f; + sp2C.xw = 0.0f; + sp2C.yw = 0.0f; + sp2C.zw = 0.0f; + sp2C.ww = 1.0f; + Matrix_MtxFToYXZRotS(&sp2C, &this->actor.world.rot, 0); + return true; +} + +void func_80B0CEA8(EnSw* this, GlobalContext* globalCtx) { + if (!(this->actor.scale.x < 0.0139999995f)) { + Camera* activeCam = GET_ACTIVE_CAM(globalCtx); + + if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &activeCam->eye) >= 380.0f)) { + Audio_PlayActorSound2(&this->actor, ((this->actor.params & 0xE000) >> 0xD) > 0 ? NA_SE_EN_STALGOLD_ROLL + : NA_SE_EN_STALWALL_ROLL); + } + } +} + +void func_80B0CF44(EnSw* this, GlobalContext* globalCtx, s32 cnt) { + Color_RGBA8 primColor = { 80, 80, 50, 255 }; + Color_RGBA8 envColor = { 100, 100, 80, 0 }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + Vec3f pos; + s16 angle = (Rand_ZeroOne() - 0.5f) * 65536.0f; + s32 i; + + for (i = cnt; i >= 0; i--, angle += (s16)(0x10000 / cnt)) { + accel.x = (Rand_ZeroOne() - 0.5f) * 2.0f; + accel.z = (Rand_ZeroOne() - 0.5f) * 2.0f; + pos.x = this->actor.world.pos.x + (Math_SinS(angle) * 2.0f); + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z + (Math_CosS(angle) * 2.0f); + func_8002836C(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 20, 30, 12); + } +} + +void func_80B0D14C(EnSw* this, GlobalContext* globalCtx, s32 cnt) { + Color_RGBA8 primColor = { 80, 80, 50, 255 }; + Color_RGBA8 envColor = { 100, 100, 80, 0 }; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + Vec3f pos; + s16 angle = (Rand_ZeroOne() - 0.5f) * 65536.0f; + s32 i; + + for (i = cnt; i >= 0; i--, angle += (s16)(0x10000 / cnt)) { + accel.x = (Rand_ZeroOne() - 0.5f) * 2.0f; + accel.z = (Rand_ZeroOne() - 0.5f) * 2.0f; + pos.x = this->actor.world.pos.x + (Math_SinS(angle) * 14.0f); + pos.y = this->actor.world.pos.y; + pos.z = this->actor.world.pos.z + (Math_CosS(angle) * 14.0f); + func_8002836C(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 20, 40, 10); + } +} + +void func_80B0D364(EnSw* this, GlobalContext* globalCtx) { + if (((this->actor.params & 0xE000) >> 0xD) == 4) { + this->unk_38C = 0; + this->actionFunc = func_80B0D3AC; + } else { + this->unk_38C = 10; + this->actionFunc = func_80B0D3AC; + } +} + +void func_80B0D3AC(EnSw* this, GlobalContext* globalCtx) { + if (this->unk_38C != 0) { + if ((this->unk_38C & 4) != 0) { + func_80B0CF44(this, globalCtx, 5); + } + this->unk_38C--; + if (this->unk_38C == 0) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_STALGOLD_UP_CRY); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_DODO_M_UP); + } else { + return; + } + } + + Math_ApproachF(&this->actor.scale.x, 0.02f, 0.2f, 0.01f); + Actor_SetScale(&this->actor, this->actor.scale.x); + this->actor.world.pos.x += this->unk_364.x * this->actor.velocity.y; + this->actor.world.pos.y += this->unk_364.y * this->actor.velocity.y; + this->actor.world.pos.z += this->unk_364.z * this->actor.velocity.y; + this->actor.world.pos.x += this->unk_37C.x * this->actor.speedXZ; + this->actor.world.pos.y += this->unk_37C.y * this->actor.speedXZ; + this->actor.world.pos.z += this->unk_37C.z * this->actor.speedXZ; + this->actor.velocity.y += this->actor.gravity; + this->actor.velocity.y = CLAMP_MIN(this->actor.velocity.y, this->actor.minVelocityY); + + if (this->actor.velocity.y < 0.0f) { + this->unk_360 = 0; + } + + if (func_80B0C0CC(this, globalCtx, 1) == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + func_80B0D14C(this, globalCtx, 8); + this->actor.scale.x = 0.02f; + Actor_SetScale(&this->actor, 0.02f); + this->actionFunc = func_80B0D590; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.gravity = 0.0f; + } +} + +void func_80B0D590(EnSw* this, GlobalContext* globalCtx) { + f32 sp2C; + + if (((this->actor.params & 0xE000) >> 0xD) == 2) { + if (this->actor.scale.x < 0.0139999995f) { + this->collider.elements[0].info.toucherFlags = 0; + this->collider.elements[0].info.bumperFlags = 0; + this->collider.elements[0].info.ocElemFlags = 0; + } + + if (this->actor.scale.x >= 0.0139999995f) { + this->collider.elements[0].info.toucherFlags = 1; + this->collider.elements[0].info.bumperFlags = 1; + this->collider.elements[0].info.ocElemFlags = 1; + } + + Math_ApproachF(&this->actor.scale.x, !IS_DAY ? 0.02f : 0.0f, 0.2f, 0.01f); + Actor_SetScale(&this->actor, this->actor.scale.x); + } + + if (this->unk_38E != 0) { + this->unk_38E--; + if (this->unk_38E == 0) { + func_80B0CEA8(this, globalCtx); + this->unk_420 = ((globalCtx->state.frames % 2) == 0) ? 0.1f : -0.1f; + this->unk_38A = 1; + this->unk_38C = Rand_S16Offset(30, 60); + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + this->unk_38C *= 2; + this->unk_420 *= 2.0f; + } + } + } else { + this->unk_38C--; + if (this->unk_38C == 0) { + this->unk_38E = Rand_S16Offset(15, 30); + this->unk_38A = 0; + this->skelAnime.playSpeed = 0.0f; + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + this->unk_38E /= 2; + } + } else if (this->unk_38A != 0) { + this->unk_38A--; + this->skelAnime.playSpeed = (this->unk_38A == 0) ? 4.0f : 0.0f; + + if (this->skelAnime.playSpeed > 0.0f) { + func_80B0CEA8(this, globalCtx); + } + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + this->skelAnime.playSpeed *= 2.0f; + } + } else { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) == 1) { + this->unk_38A = 2; + } + sp2C = 32768.0f / this->skelAnime.endFrame; + sp2C *= this->skelAnime.curFrame; + sp2C = Math_SinS(sp2C) * this->unk_420; + func_80B0CCF4(this, &sp2C); + this->actor.shape.rot = this->actor.world.rot; + } + } +} + +void func_80B0D878(EnSw* this, GlobalContext* globalCtx) { + Actor* temp_v0; + Vec3f pos; + Vec3f velAndAccel = { 0.0f, 0.5f, 0.0f }; + f32 x; + f32 y; + f32 z; + + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) == 1) { + func_80B0CEA8(this, globalCtx); + } + + func_80B0CCF4(this, &this->unk_420); + this->actor.shape.rot = this->actor.world.rot; + + if ((this->unk_394 == 0) && (this->unk_392 == 0)) { + Audio_PlaySoundGeneral(NA_SE_SY_KINSTA_MARK_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + x = (this->unk_364.x * 10.0f); + y = (this->unk_364.y * 10.0f); + z = (this->unk_364.z * 10.0f); + temp_v0 = + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_SI, this->actor.world.pos.x + x, + this->actor.world.pos.y + y, this->actor.world.pos.z + z, 0, 0, 0, this->actor.params); + if (temp_v0 != NULL) { + temp_v0->parent = NULL; + } + Actor_Kill(&this->actor); + return; + } + + if ((this->unk_392 == 0) && (DECR(this->unk_394) != 0)) { + pos = this->actor.world.pos; + pos.y += 10.0f + ((Rand_ZeroOne() - 0.5f) * 6.0f); + pos.x += (Rand_ZeroOne() - 0.5f) * 32.0f; + pos.z += (Rand_ZeroOne() - 0.5f) * 32.0f; + EffectSsDeadDb_Spawn(globalCtx, &pos, &velAndAccel, &velAndAccel, 42, 0, 255, 255, 255, 255, 255, 0, 0, 1, 9, + true); + } +} + +void func_80B0DB00(EnSw* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->actor); + this->actor.shape.rot.x += 0x1000; + this->actor.shape.rot.z += 0x1000; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 0.0f, 5); + + if ((this->actor.bgCheckFlags & 1) && (!(0.0f <= this->actor.velocity.y))) { + if (this->actor.floorHeight <= BGCHECK_Y_MIN || this->actor.floorHeight >= 32000.0f) { + Actor_Kill(&this->actor); + return; + } + + this->actor.bgCheckFlags &= ~1; + + if (this->unk_38A == 0) { + this->actionFunc = func_80B0DC7C; + this->unk_394 = 10; + } else { + this->actor.velocity.y = ((this->unk_38A--) * 8.0f) * 0.5f; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 16.0f, 0xC, 2.0f, 0x78, 0xA, 0); + } +} + +void func_80B0DC7C(EnSw* this, GlobalContext* globalCtx) { + Vec3f velAndAccel = { 0.0f, 0.5f, 0.0f }; + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + + if (DECR(this->unk_394) != 0) { + pos.y = ((Rand_ZeroOne() - 0.5f) * 6.0f) + (this->actor.world.pos.y + 10.0f); + pos.x = ((Rand_ZeroOne() - 0.5f) * 32.0f) + this->actor.world.pos.x; + pos.z = ((Rand_ZeroOne() - 0.5f) * 32.0f) + this->actor.world.pos.z; + EffectSsDeadDb_Spawn(globalCtx, &pos, &velAndAccel, &velAndAccel, 42, 0, 255, 255, 255, 255, 255, 0, 0, 1, 9, + 1); + this->actor.shape.rot.x += 0x1000; + this->actor.shape.rot.z += 0x1000; + } else { + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, 0x30); + Actor_Kill(&this->actor); + } +} + +s16 func_80B0DE34(EnSw* this, Vec3f* arg1) { + s16 pitch; + s16 yaw; + + yaw = Math_Vec3f_Yaw(&this->actor.world.pos, arg1) - this->actor.wallYaw; + pitch = Math_Vec3f_Pitch(&this->actor.world.pos, arg1) - 0x4000; + return pitch * (yaw >= 0 ? -1 : 1); +} + +s32 func_80B0DEA8(EnSw* this, GlobalContext* globalCtx, s32 arg2) { + Player* player = GET_PLAYER(globalCtx); + CollisionPoly* sp58; + s32 sp54; + Vec3f sp48; + + if (!(player->stateFlags1 & 0x200000) && arg2) { + return false; + } else if (func_8002DDF4(globalCtx) && arg2) { + return false; + } else if (ABS(func_80B0DE34(this, &player->actor.world.pos) - this->actor.shape.rot.z) >= 0x1FC2) { + return false; + } else if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &player->actor.world.pos) >= 130.0f) { + return false; + } else if (!BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &player->actor.world.pos, &sp48, + &sp58, true, false, false, true, &sp54)) { + return true; + } else { + return false; + } +} + +s32 func_80B0DFFC(EnSw* this, GlobalContext* globalCtx) { + s32 pad; + CollisionPoly* sp60; + s32 sp5C; + Vec3f sp50; + s32 sp4C = true; + + if (this->collider.base.ocFlags1 & OC1_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + sp4C = false; + } else if (((globalCtx->state.frames % 4) == 0) && + !BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &this->unk_454, &sp50, &sp60, true, + false, false, true, &sp5C)) { + sp4C = false; + } else if (((globalCtx->state.frames % 4) == 1) && + BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &this->unk_460, &sp50, &sp60, true, + false, false, true, &sp5C)) { + sp4C = false; + } else if (((globalCtx->state.frames % 4) == 2) && + !BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &this->unk_46C, &sp50, &sp60, true, + false, false, true, &sp5C)) { + if (0) {} + sp4C = false; + } else if (((globalCtx->state.frames % 4) == 3) && + BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &this->unk_478, &sp50, &sp60, true, + false, false, true, &sp5C)) { + sp4C = false; + } + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &this->unk_484, &sp50, &this->unk_430, true, + false, false, true, &sp5C)) { + this->actor.wallYaw = Math_FAtan2F(this->unk_430->normal.x, this->unk_430->normal.z) * (0x8000 / M_PI); + this->actor.world.pos = sp50; + this->actor.world.pos.x += 6.0f * Math_SinS(this->actor.world.rot.y); + this->actor.world.pos.z += 6.0f * Math_CosS(this->actor.world.rot.y); + this->unk_434 = sp50; + this->unk_434.x += Math_SinS(this->actor.world.rot.y); + this->unk_434.z += Math_CosS(this->actor.world.rot.y); + } + + return sp4C; +} + +void func_80B0E314(EnSw* this, Vec3f arg1, f32 arg4) { + f32 xDist; + f32 yDist; + f32 zDist; + f32 dist; + f32 xDiff; + f32 yDiff; + f32 zDiff; + + Math_SmoothStepToF(&this->actor.speedXZ, arg4, 0.3f, 100.0f, 0.1f); + xDiff = arg1.x - this->actor.world.pos.x; + yDiff = arg1.y - this->actor.world.pos.y; + zDiff = arg1.z - this->actor.world.pos.z; + dist = sqrtf(SQ(xDiff) + SQ(yDiff) + SQ(zDiff)); + if (dist == 0.0f) { + xDist = yDist = zDist = 0.0f; + } else { + xDist = xDiff / dist; + yDist = yDiff / dist; + zDist = zDiff / dist; + } + xDist *= this->actor.speedXZ; + yDist *= this->actor.speedXZ; + zDist *= this->actor.speedXZ; + this->actor.world.pos.x += xDist; + this->actor.world.pos.y += yDist; + this->actor.world.pos.z += zDist; +} + +s32 func_80B0E430(EnSw* this, f32 arg1, s16 arg2, s32 arg3, GlobalContext* globalCtx) { + Camera* activeCam; + f32 lastFrame = Animation_GetLastFrame(&object_st_Anim_000304); + + if (DECR(this->unk_388) != 0) { + Math_SmoothStepToF(&this->skelAnime.playSpeed, 0.0f, 0.6f, 1000.0f, 0.01f); + return 0; + } + + Math_SmoothStepToF(&this->skelAnime.playSpeed, arg1, 0.6f, 1000.0f, 0.01f); + + if ((arg3 == 1) && (lastFrame < (this->skelAnime.curFrame + this->skelAnime.playSpeed))) { + return 0; + } + + activeCam = GET_ACTIVE_CAM(globalCtx); + + if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &activeCam->eye) < 380.0f) { + if (DECR(this->unk_440) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_ROLL); + this->unk_440 = 4; + } + } else { + this->unk_440 = 0; + } + Math_SmoothStepToS(&this->actor.shape.rot.z, this->unk_444, 4, arg2, arg2); + this->actor.world.rot = this->actor.shape.rot; + if (this->actor.shape.rot.z == this->unk_444) { + return 1; + } + return 0; +} + +void func_80B0E5E0(EnSw* this, GlobalContext* globalCtx) { + s32 pad[2]; + f32 rand; + + if (func_80B0E430(this, 6.0f, 0x3E8, 1, globalCtx)) { + rand = Rand_ZeroOne(); + this->unk_444 = + ((s16)(20000.0f * rand) + 0x2EE0) * (Rand_ZeroOne() >= 0.5f ? 1.0f : -1.0f) + this->actor.world.rot.z; + this->unk_388 = Rand_S16Offset(10, 30); + } + + if ((DECR(this->unk_442) == 0) && (func_80B0DEA8(this, globalCtx, 1))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_LAUGH); + this->unk_442 = 20; + this->actionFunc = func_80B0E728; + } +} + +void func_80B0E728(EnSw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + if (DECR(this->unk_442) != 0) { + if (func_80B0DEA8(this, globalCtx, 1)) { + this->unk_448 = player->actor.world.pos; + this->unk_448.y += 30.0f; + this->unk_444 = func_80B0DE34(this, &this->unk_448); + func_80B0E430(this, 6.0f, (u16)0xFA0, 0, globalCtx); + } else { + this->actionFunc = func_80B0E5E0; + } + } else { + if (!func_80B0DFFC(this, globalCtx)) { + this->unk_442 = Rand_S16Offset(20, 10); + this->unk_444 = func_80B0DE34(this, &this->actor.home.pos); + this->unk_448 = this->actor.home.pos; + this->actionFunc = func_80B0E9BC; + } else { + func_80B0E314(this, this->unk_448, 8.0f); + + if (DECR(this->unk_440) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALWALL_DASH); + this->unk_440 = 4; + } + + if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->unk_448) > 13.0f) || func_8002DDF4(globalCtx)) { + this->actionFunc = func_80B0E90C; + } + } + } +} + +void func_80B0E90C(EnSw* this, GlobalContext* globalCtx) { + s32 pad; + + func_80B0E314(this, this->unk_448, 0.0f); + if (this->actor.speedXZ == 0.0f) { + this->unk_444 = func_80B0DE34(this, &this->actor.home.pos); + this->unk_448 = this->actor.home.pos; + this->actionFunc = func_80B0E9BC; + } +} + +void func_80B0E9BC(EnSw* this, GlobalContext* globalCtx) { + s32 pad; + + if (func_80B0E430(this, 6.0f, 0x3E8, 0, globalCtx)) { + func_80B0E314(this, this->unk_448, 2.0f); + if (!(Math_Vec3f_DistXYZ(&this->actor.world.pos, &this->unk_448) > 4.0f)) { + this->actionFunc = func_80B0E5E0; + } + } +} + +void EnSw_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSw* this = (EnSw*)thisx; + + SkelAnime_Update(&this->skelAnime); + func_80B0C9F0(this, globalCtx); + this->actionFunc(this, globalCtx); + func_80B0CBE8(this, globalCtx); +} + +s32 EnSw_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + Vec3f sp7C = { 1400.0f, -2600.0f, -800.0f }; + Vec3f sp70 = { 1400.0f, -1600.0f, 0.0f }; + Vec3f sp64 = { -1400.0f, -2600.0f, -800.0f }; + Vec3f sp58 = { -1400.0f, -1600.0f, 0.0f }; + Vec3f sp4C = { 0.0, 0.0f, -600.0f }; + EnSw* this = (EnSw*)thisx; + Vec3f sp3C = { 0.0f, 0.0f, 0.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_sw.c", 2084); + + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + switch (limbIndex) { + case 23: + *dList = object_st_DL_004788; + break; + case 8: + *dList = object_st_DL_0046F0; + break; + case 14: + *dList = object_st_DL_004658; + break; + case 11: + *dList = object_st_DL_0045C0; + break; + case 26: + *dList = object_st_DL_004820; + break; + case 20: + *dList = object_st_DL_0048B8; + break; + case 17: + *dList = object_st_DL_004950; + break; + case 29: + *dList = object_st_DL_0049E8; + break; + case 5: + *dList = object_st_DL_003FB0; + break; + case 4: + *dList = object_st_DL_0043D8; + break; + } + } + + if (limbIndex == 1) { + Matrix_MultVec3f(&sp7C, &this->unk_454); + Matrix_MultVec3f(&sp70, &this->unk_460); + Matrix_MultVec3f(&sp64, &this->unk_46C); + Matrix_MultVec3f(&sp58, &this->unk_478); + Matrix_MultVec3f(&sp4C, &this->unk_484); + } + + if (limbIndex == 5) { + Matrix_MultVec3f(&sp3C, &this->actor.focus.pos); + } + + if (limbIndex == 4) { + gDPSetEnvColor(POLY_OPA_DISP++, this->unk_1F4.r, this->unk_1F4.g, this->unk_1F4.b, 0); + } + + Collider_UpdateSpheres(limbIndex, &this->collider); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_sw.c", 2145); + + return false; +} + +void EnSw_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { +} + +void func_80B0EDB8(GlobalContext* globalCtx, Color_RGBA8* arg1, s16 arg2, s16 arg3) { + f32 temp_f2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_sw.c", 2181); + + temp_f2 = (11500.0f / arg3) * (arg3 - arg2); + + if (0.0f == temp_f2) { + temp_f2 = 11500; + } + + POLY_OPA_DISP = Gfx_SetFog2(POLY_OPA_DISP, arg1->r, arg1->g, arg1->b, arg1->a, 0, (s16)temp_f2); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_sw.c", 2197); +} + +void func_80B0EEA4(GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_sw.c", 2205); + + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_sw.c", 2207); +} + +void EnSw_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSw* this = (EnSw*)thisx; + Color_RGBA8 sp30 = { 184, 0, 228, 255 }; + + if (((this->actor.params & 0xE000) >> 0xD) != 0) { + Matrix_RotateX(DEGF_TO_RADF(-80), MTXMODE_APPLY); + if (this->actor.colChkInfo.health != 0) { + Matrix_Translate(0.0f, 0.0f, 200.0f, MTXMODE_APPLY); + } + func_8002EBCC(&this->actor, globalCtx, 0); + } else if (this->actionFunc == func_80B0E728) { + func_80B0EDB8(globalCtx, &sp30, 0x14, 0x1E); + } + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnSw_OverrideLimbDraw, + EnSw_PostLimbDraw, this); + if (this->actionFunc == func_80B0E728) { + func_80B0EEA4(globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.h b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.h new file mode 100644 index 000000000..452fc5e75 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Sw/z_en_sw.h @@ -0,0 +1,52 @@ +#ifndef Z_EN_SW_H +#define Z_EN_SW_H + +#include "ultra64.h" +#include "global.h" + +struct EnSw; + +typedef void (*EnSwActionFunc)(struct EnSw* this, GlobalContext* globalCtx); + +typedef struct EnSw { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnSwActionFunc actionFunc; + /* 0x0194 */ ColliderJntSph collider; + /* 0x01B4 */ ColliderJntSphElement sphs[1]; + /* 0x01F4 */ Color_RGBA8 unk_1F4; + /* 0x01F8 */ Vec3s jointTable[30]; + /* 0x02AC */ Vec3s morphTable[30]; + /* 0x0360 */ u8 unk_360; + /* 0x0364 */ Vec3f unk_364; + /* 0x0370 */ Vec3f unk_370; + /* 0x037C */ Vec3f unk_37C; + /* 0x0388 */ s16 unk_388; + /* 0x038A */ s16 unk_38A; + /* 0x038C */ s16 unk_38C; + /* 0x038E */ s16 unk_38E; + /* 0x0390 */ s16 unk_390; + /* 0x0392 */ s16 unk_392; + /* 0x0394 */ s16 unk_394; + /* 0x0396 */ char unk_396[0x42]; + /* 0x03D8 */ MtxF unk_3D8; + /* 0x0418 */ char unk_418[8]; + /* 0x0420 */ f32 unk_420; + /* 0x0424 */ char unk_424[0x8]; + /* 0x042C */ u8 unk_42C; + /* 0x0430 */ CollisionPoly* unk_430; + /* 0x0434 */ Vec3f unk_434; + /* 0x0440 */ s16 unk_440; + /* 0x0442 */ s16 unk_442; + /* 0x0444 */ s16 unk_444; + /* 0x0446 */ s16 unk_446; + /* 0x0448 */ Vec3f unk_448; + /* 0x0454 */ Vec3f unk_454; + /* 0x0460 */ Vec3f unk_460; + /* 0x046C */ Vec3f unk_46C; + /* 0x0478 */ Vec3f unk_478; + /* 0x0484 */ Vec3f unk_484; + /* 0x0490 */ char unk_490[0x48]; +} EnSw; // size = 0x04D8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.c b/soh/src/overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.c new file mode 100644 index 000000000..f152924e6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.c @@ -0,0 +1,352 @@ +#include "z_en_syateki_itm.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.h" +#include "overlays/actors/ovl_En_Ex_Ruppy/z_en_ex_ruppy.h" +#include "overlays/actors/ovl_En_G_Switch/z_en_g_switch.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + SYATEKI_ROUND_GREEN_APPEAR, + SYATEKI_ROUND_BLUE_SEQUENTIAL, + SYATEKI_ROUND_GREEN_THROW, + SYATEKI_ROUND_BLUE_SIMUL, + SYATEKI_ROUND_RED_LEFT, + SYATEKI_ROUND_RED_RIGHT, + SYATEKI_ROUND_MAX +} EnSyatekItemRound; + +void EnSyatekiItm_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiItm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiItm_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnSyatekiItm_Idle(EnSyatekiItm* this, GlobalContext* globalCtx); +void EnSyatekiItm_StartRound(EnSyatekiItm* this, GlobalContext* globalCtx); +void EnSyatekiItm_SpawnTargets(EnSyatekiItm* this, GlobalContext* globalCtx); +void EnSyatekiItm_CheckTargets(EnSyatekiItm* this, GlobalContext* globalCtx); +void EnSyatekiItm_CleanupGame(EnSyatekiItm* this, GlobalContext* globalCtx); +void EnSyatekiItm_EndGame(EnSyatekiItm* this, GlobalContext* globalCtx); + +const ActorInit En_Syateki_Itm_InitVars = { + ACTOR_EN_SYATEKI_ITM, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnSyatekiItm), + (ActorFunc)EnSyatekiItm_Init, + (ActorFunc)EnSyatekiItm_Destroy, + (ActorFunc)EnSyatekiItm_Update, + NULL, + NULL, +}; + +static Vec3f sGreenAppearHome = { 0.0f, -10.0f, -270.0f }; +static Vec3f sBlueSeqHome1 = { -220.0f, 66.0f, -320.0f }; +static Vec3f sBlueSeqHome2 = { 260.0f, 66.0f, -320.0f }; +static Vec3f sGreenThrowHome = { 0.0f, -10.0f, -270.0f }; +static Vec3f sBlueSimulHome1 = { -220.0f, 66.0f, -320.0f }; +static Vec3f sBlueSimulHome2 = { 260.0f, 66.0f, -320.0f }; +static Vec3f sRedLeftHome1 = { 260.0f, 100.0f, -320.0f }; +static Vec3f sRedLeftHome2 = { 360.0f, 100.0f, -320.0f }; +static Vec3f sRedRightHome1 = { -230.0f, 94.0f, -360.0f }; +static Vec3f sRedRightHome2 = { -400.0f, 94.0f, -360.0f }; +static Vec3f sGreenAppearFinal = { 0.0f, 53.0f, -270.0f }; +static Vec3f sBlueSeqFinal1 = { -60.0f, 63.0f, -320.0f }; +static Vec3f sBlueSeqFinal2 = { 60.0f, 63.0f, -320.0f }; +static Vec3f sGreenThrowFinal = { 0.0f, 0.0f, 0.0f }; +static Vec3f sBlueSimulFinal1 = { -60.0f, 63.0f, -320.0f }; +static Vec3f sBlueSimulFinal2 = { 60.0f, 63.0f, -320.0f }; +static Vec3f sRedLeftFinal1 = { -230.0f, 0.0f, 0.0f }; +static Vec3f sRedLeftFinal2 = { -230.0f, 0.0f, 0.0f }; +static Vec3f sRedRightFinal1 = { 260.0f, 0.0f, 0.0f }; +static Vec3f sRedRightFinal2 = { 260.0f, 0.0f, 0.0f }; +static s16 sTargetColors[] = { 0, 1, 0, 1, 2, 2 }; +static s16 sRupeeTypes[] = { 0, 1, 1, 0, 1, 1, 4, 4, 4, 4 }; +static Vec3f sRupeePos[] = { + { -40.0f, 0.0f, -90.0f }, { -20.0f, 0.0f, -90.0f }, { 0.0f, 0.0f, -90.0f }, { 20.0f, 0.0f, -90.0f }, + { 40.0f, 0.0f, -90.0f }, { -40.0f, 0.0f, -60.0f }, { -20.0f, 0.0f, -60.0f }, { 0.0f, 0.0f, -60.0f }, + { 20.0f, 0.0f, -60.0f }, { 40.0f, 0.0f, -60.0f }, +}; + +void EnSyatekiItm_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnSyatekiItm* this = (EnSyatekiItm*)thisx; + s32 i; + + this->man = (EnSyatekiMan*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_SYATEKI_MAN, + 140.0f, 0.0f, 255.0f, 0, -0x4000, 0, 0); + if (this->man == NULL) { + // "Spawn error" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ エラー原 ☆☆☆☆ \n" VT_RST); + Actor_Kill(&this->actor); + return; + } + for (i = 0; i < 10; i++) { + this->markers[i] = + (EnExRuppy*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_EX_RUPPY, + sRupeePos[i].x, sRupeePos[i].y, sRupeePos[i].z, 0, 0, 0, 4); + if (this->markers[i] == NULL) { + // "Second spawn error" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ エラー原セカンド ☆☆☆☆ \n" VT_RST); + Actor_Kill(&this->actor); + return; + } + this->markers[i]->colorIdx = sRupeeTypes[i]; + } + this->actionFunc = EnSyatekiItm_Idle; +} + +void EnSyatekiItm_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnSyatekiItm_Idle(EnSyatekiItm* this, GlobalContext* globalCtx) { + s32 i; + Player* player = GET_PLAYER(globalCtx); + + if (this->signal == ENSYATEKI_START) { + player->actor.world.pos.x = -12.0f; + player->actor.world.pos.y = 20.0f; + player->actor.world.pos.z = 182.0f; + player->currentYaw = player->actor.world.rot.y = player->actor.shape.rot.y = 0x7F03; + player->actor.world.rot.x = player->actor.shape.rot.x = player->actor.world.rot.z = player->actor.shape.rot.z = + 0; + func_8008EF44(globalCtx, 15); + this->roundNum = this->hitCount = 0; + for (i = 0; i < 6; i++) { + this->roundFlags[i] = false; + } + for (i = 0; i < 10; i++) { + this->markers[i]->galleryFlag = false; + } + this->actionFunc = EnSyatekiItm_StartRound; + } +} + +void EnSyatekiItm_StartRound(EnSyatekiItm* this, GlobalContext* globalCtx) { + s32 i; + s32 j; + Player* player = GET_PLAYER(globalCtx); + + if (this->unkTimer == 0) { + if (LINK_IS_ADULT) { + for (i = 0, j = 0; i < SYATEKI_ROUND_MAX; i++) { + if (this->roundFlags[i]) { + j++; + } + } + if (j >= SYATEKI_ROUND_MAX) { + player->actor.freezeTimer = 10; + this->signal = ENSYATEKI_END; + this->actionFunc = EnSyatekiItm_CleanupGame; + return; + } + i = Rand_ZeroFloat(5.99f); + while (this->roundFlags[i]) { + i = Rand_ZeroFloat(5.99f); + if (1) {} + } + this->roundNum = i + 1; + this->roundFlags[i] = true; + } else { + this->roundNum++; + if (this->roundNum > SYATEKI_ROUND_MAX) { + player->actor.freezeTimer = 10; + this->signal = ENSYATEKI_END; + this->actionFunc = EnSyatekiItm_CleanupGame; + return; + } + } + + this->timer = (this->roundNum == 1) ? 50 : 30; + + func_80078884(NA_SE_SY_FOUND); + this->actionFunc = EnSyatekiItm_SpawnTargets; + } +} + +void EnSyatekiItm_SpawnTargets(EnSyatekiItm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 i; + s32 roundIdx; + + if (globalCtx->shootingGalleryStatus == -1) { + player->actor.freezeTimer = 10; + this->signal = ENSYATEKI_END; + this->actionFunc = EnSyatekiItm_CleanupGame; + return; + } + if (this->timer == 0) { + for (i = 0; i < 2; i++) { + Math_Vec3f_Copy(&this->targetHome[i], &zeroVec); + Math_Vec3f_Copy(&this->targetFinal[i], &zeroVec); + this->targets[i] = NULL; + } + this->numTargets = 2; + this->curMarkers[0] = this->curMarkers[1] = NULL; + roundIdx = this->roundNum - 1; + + switch (roundIdx) { + case SYATEKI_ROUND_GREEN_APPEAR: + Math_Vec3f_Copy(&this->targetHome[0], &sGreenAppearHome); + Math_Vec3f_Copy(&this->targetFinal[0], &sGreenAppearFinal); + this->curMarkers[0] = this->markers[0]; + this->numTargets = 1; + break; + case SYATEKI_ROUND_BLUE_SEQUENTIAL: + Math_Vec3f_Copy(&this->targetHome[0], &sBlueSeqHome1); + Math_Vec3f_Copy(&this->targetHome[1], &sBlueSeqHome2); + Math_Vec3f_Copy(&this->targetFinal[0], &sBlueSeqFinal1); + Math_Vec3f_Copy(&this->targetFinal[1], &sBlueSeqFinal2); + this->curMarkers[0] = this->markers[1]; + this->curMarkers[1] = this->markers[2]; + break; + case SYATEKI_ROUND_GREEN_THROW: + Math_Vec3f_Copy(&this->targetHome[0], &sGreenThrowHome); + Math_Vec3f_Copy(&this->targetFinal[0], &sGreenThrowFinal); + this->curMarkers[0] = this->markers[3]; + this->numTargets = 1; + break; + case SYATEKI_ROUND_BLUE_SIMUL: + Math_Vec3f_Copy(&this->targetHome[0], &sBlueSimulHome1); + Math_Vec3f_Copy(&this->targetHome[1], &sBlueSimulHome2); + Math_Vec3f_Copy(&this->targetFinal[0], &sBlueSimulFinal1); + Math_Vec3f_Copy(&this->targetFinal[1], &sBlueSimulFinal2); + this->curMarkers[0] = this->markers[4]; + this->curMarkers[1] = this->markers[5]; + break; + case SYATEKI_ROUND_RED_LEFT: + Math_Vec3f_Copy(&this->targetHome[0], &sRedLeftHome1); + Math_Vec3f_Copy(&this->targetHome[1], &sRedLeftHome2); + Math_Vec3f_Copy(&this->targetFinal[0], &sRedLeftFinal1); + Math_Vec3f_Copy(&this->targetFinal[1], &sRedLeftFinal2); + this->curMarkers[0] = this->markers[6]; + this->curMarkers[1] = this->markers[7]; + break; + case SYATEKI_ROUND_RED_RIGHT: + Math_Vec3f_Copy(&this->targetHome[0], &sRedRightHome1); + Math_Vec3f_Copy(&this->targetHome[1], &sRedRightHome2); + Math_Vec3f_Copy(&this->targetFinal[0], &sRedRightFinal1); + Math_Vec3f_Copy(&this->targetFinal[1], &sRedRightFinal2); + this->curMarkers[0] = this->markers[8]; + this->curMarkers[1] = this->markers[9]; + break; + } + + for (i = 0; i < this->numTargets; i++) { + this->targets[i] = (EnGSwitch*)Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_G_SWITCH, this->targetHome[i].x, + this->targetHome[i].y, this->targetHome[i].z, 0, 0, 0, (ENGSWITCH_TARGET_RUPEE << 0xC) | 0x3F); + if (this->targets[i] == NULL) { + // "Rupee spawn error" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ルピーでエラー原 ☆☆☆☆ \n" VT_RST); + Actor_Kill(&this->actor); + return; + } + this->targets[i]->index = i; + this->targets[i]->colorIdx = sTargetColors[roundIdx]; + Math_Vec3f_Copy(&this->targets[i]->targetPos, &this->targetFinal[i]); + switch (roundIdx) { + case SYATEKI_ROUND_BLUE_SEQUENTIAL: + if (i == 1) { + this->targets[i]->delayTimer = 60; + } + break; + case SYATEKI_ROUND_GREEN_THROW: + this->targets[i]->actor.velocity.y = 15.0f; + this->targets[i]->actor.gravity = -1.0f; + this->targets[i]->moveMode = GSWITCH_THROW; + break; + case SYATEKI_ROUND_RED_LEFT: + this->targets[i]->actor.velocity.x = -5.0f; + this->targets[i]->moveMode = GSWITCH_LEFT; + break; + case SYATEKI_ROUND_RED_RIGHT: + this->targets[i]->actor.velocity.x = 7.0f; + this->targets[i]->moveMode = GSWITCH_RIGHT; + break; + } + } + this->targetState[0] = this->targetState[1] = ENSYATEKIHIT_NONE; + this->actionFunc = EnSyatekiItm_CheckTargets; + } +} + +void EnSyatekiItm_CheckTargets(EnSyatekiItm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 i; + s16 j; + + if (globalCtx->shootingGalleryStatus == -1) { + player->actor.freezeTimer = 10; + this->signal = ENSYATEKI_END; + this->actionFunc = EnSyatekiItm_CleanupGame; + } else { + for (i = 0, j = 0; i < 2; i++) { + if (this->targetState[i] != ENSYATEKIHIT_NONE) { + if (this->targetState[i] == ENSYATEKIHIT_HIT) { + this->curMarkers[i]->galleryFlag = true; + } + j++; + } + } + if (j == this->numTargets) { + this->actionFunc = EnSyatekiItm_StartRound; + } + } +} + +void EnSyatekiItm_CleanupGame(EnSyatekiItm* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 2; i++) { + if ((this->targetState[i] == ENSYATEKIHIT_NONE) && (this->targets[i] != NULL)) { + Actor_Kill(&this->targets[i]->actor); + } + } + this->actionFunc = EnSyatekiItm_EndGame; +} + +void EnSyatekiItm_EndGame(EnSyatekiItm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + player->actor.freezeTimer = 10; + if (this->signal == ENSYATEKI_RESULTS) { + this->signal = ENSYATEKI_NONE; + this->actionFunc = EnSyatekiItm_Idle; + } + if (this->signal == ENSYATEKI_START) { + // "1 frame attack and defense!" + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(RED) "☆☆☆☆☆ 1フレームの攻防! ☆☆☆☆ \n" VT_RST); + this->signal = ENSYATEKI_NONE; + this->actionFunc = EnSyatekiItm_Idle; + } +} + +void EnSyatekiItm_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSyatekiItm* this = (EnSyatekiItm*)thisx; + + this->actionFunc(this, globalCtx); + + if (this->timer != 0) { + this->timer--; + } + if (this->unkTimer != 0) { + this->unkTimer--; + } + if (BREG(0)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 0, 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h b/soh/src/overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h new file mode 100644 index 000000000..11657fdb0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h @@ -0,0 +1,43 @@ +#ifndef Z_EN_SYATEKI_ITM_H +#define Z_EN_SYATEKI_ITM_H + +#include "ultra64.h" +#include "global.h" + +struct EnSyatekiItm; + +typedef void (*EnSyatekiItmActionFunc)(struct EnSyatekiItm*, GlobalContext*); + +typedef enum { + /* 0 */ ENSYATEKI_NONE, + /* 1 */ ENSYATEKI_START, + /* 2 */ ENSYATEKI_END, + /* 3 */ ENSYATEKI_RESULTS +} EnSyatekiSignal; + +typedef enum { + /* 0 */ ENSYATEKIHIT_NONE, + /* 1 */ ENSYATEKIHIT_MISS, + /* 2 */ ENSYATEKIHIT_HIT +} EnSyatekiHitState; + +typedef struct EnSyatekiItm { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnSyatekiItmActionFunc actionFunc; + /* 0x0150 */ s16 timer; // timer for next round + /* 0x0152 */ s16 unkTimer; // unk timer + /* 0x0154 */ s16 signal; // signal between this and shopkeeper + /* 0x0156 */ s16 hitCount; // total rupees hit + /* 0x0158 */ s16 roundNum; // current round + /* 0x015A */ s16 roundFlags[6]; // flags for each round happening + /* 0x0166 */ s16 targetState[2]; // current state of target rupees + /* 0x016A */ s16 numTargets; // number of target rupees for this round + /* 0x016C */ Vec3f targetHome[2]; // initial position of target rupees + /* 0x0184 */ Vec3f targetFinal[2]; // target position of target rupees + /* 0x019C */ struct EnExRuppy* markers[10]; // marker rupees for hits + /* 0x01C4 */ struct EnGSwitch* targets[2]; // currently spawned target rupees + /* 0x01CC */ struct EnSyatekiMan* man; // shopkeeper + /* 0x01D0 */ struct EnExRuppy* curMarkers[2]; // marker rupees for the current round +} EnSyatekiItm; // size = 0x01D8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c new file mode 100644 index 000000000..24dee1b16 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.c @@ -0,0 +1,512 @@ +#include "z_en_syateki_man.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Syateki_Itm/z_en_syateki_itm.h" +#include "objects/object_ossan/object_ossan.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_27) + +typedef enum { + /* 0 */ SYATEKI_RESULT_NONE, + /* 1 */ SYATEKI_RESULT_WINNER, + /* 2 */ SYATEKI_RESULT_ALMOST, + /* 3 */ SYATEKI_RESULT_FAILURE, + /* 4 */ SYATEKI_RESULT_REFUSE +} EnSyatekiManGameResult; + +typedef enum { + /* 0 */ SYATEKI_TEXT_CHOICE, + /* 1 */ SYATEKI_TEXT_START_GAME, + /* 2 */ SYATEKI_TEXT_NO_RUPEES, + /* 3 */ SYATEKI_TEXT_REFUSE +} EnSyatekiManTextIdx; + +void EnSyatekiMan_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiMan_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiMan_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiMan_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnSyatekiMan_Start(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_SetupIdle(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_Idle(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_Talk(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_StopTalk(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_StartGame(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_WaitForGame(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_EndGame(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_GivePrize(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_FinishPrize(EnSyatekiMan* this, GlobalContext* globalCtx); +void EnSyatekiMan_RestartGame(EnSyatekiMan* this, GlobalContext* globalCtx); + +void EnSyatekiMan_BlinkWait(EnSyatekiMan* this); +void EnSyatekiMan_Blink(EnSyatekiMan* this); + +void EnSyatekiMan_SetBgm(void); + +const ActorInit En_Syateki_Man_InitVars = { + ACTOR_EN_SYATEKI_MAN, + ACTORCAT_NPC, + FLAGS, + OBJECT_OSSAN, + sizeof(EnSyatekiMan), + (ActorFunc)EnSyatekiMan_Init, + (ActorFunc)EnSyatekiMan_Destroy, + (ActorFunc)EnSyatekiMan_Update, + (ActorFunc)EnSyatekiMan_Draw, + NULL, +}; + +static u16 sBgmList[] = { + NA_BGM_GENERAL_SFX, + NA_BGM_NATURE_AMBIENCE, + NA_BGM_FIELD_LOGIC, + NA_BGM_DUNGEON, + NA_BGM_KAKARIKO_ADULT, + NA_BGM_FIELD_LOGIC, + NA_BGM_KAKARIKO_ADULT, + NA_BGM_ENEMY, + NA_BGM_ENEMY, + NA_BGM_ENEMY | 0x800, + NA_BGM_BOSS, + NA_BGM_INSIDE_DEKU_TREE, + NA_BGM_MARKET, + NA_BGM_TITLE, + NA_BGM_LINK_HOUSE, + NA_BGM_GAME_OVER, + NA_BGM_BOSS_CLEAR, + NA_BGM_ITEM_GET | 0x900, + NA_BGM_OPENING_GANON, + NA_BGM_HEART_GET | 0x900, + NA_BGM_OCA_LIGHT, + NA_BGM_JABU_JABU, + NA_BGM_KAKARIKO_KID, + NA_BGM_GREAT_FAIRY, + NA_BGM_ZELDA_THEME, + NA_BGM_FIRE_TEMPLE, + NA_BGM_OPEN_TRE_BOX | 0x900, + NA_BGM_FOREST_TEMPLE, + NA_BGM_COURTYARD, + NA_BGM_GANON_TOWER, + NA_BGM_LONLON, + NA_BGM_GORON_CITY, + NA_BGM_SPIRITUAL_STONE, + NA_BGM_OCA_BOLERO, + NA_BGM_OCA_MINUET, + NA_BGM_OCA_SERENADE, + NA_BGM_OCA_REQUIEM, + NA_BGM_OCA_NOCTURNE, + NA_BGM_MINI_BOSS, + NA_BGM_SMALL_ITEM_GET, + NA_BGM_TEMPLE_OF_TIME, + NA_BGM_EVENT_CLEAR, + NA_BGM_KOKIRI, + NA_BGM_OCA_FAIRY_GET, + NA_BGM_SARIA_THEME, + NA_BGM_SPIRIT_TEMPLE, + NA_BGM_HORSE, + NA_BGM_HORSE_GOAL, + NA_BGM_INGO, + NA_BGM_MEDALLION_GET, + NA_BGM_OCA_SARIA, + NA_BGM_OCA_EPONA, + NA_BGM_OCA_ZELDA, + NA_BGM_OCA_SUNS, + NA_BGM_OCA_TIME, + NA_BGM_OCA_STORM, + NA_BGM_NAVI_OPENING, + NA_BGM_DEKU_TREE_CS, + NA_BGM_WINDMILL, + NA_BGM_HYRULE_CS, + NA_BGM_MINI_GAME, + NA_BGM_SHEIK, + NA_BGM_ZORA_DOMAIN, + NA_BGM_APPEAR, + NA_BGM_ADULT_LINK, + NA_BGM_MASTER_SWORD, + NA_BGM_INTRO_GANON, + NA_BGM_SHOP, + NA_BGM_CHAMBER_OF_SAGES, + NA_BGM_FILE_SELECT, + NA_BGM_ICE_CAVERN, + NA_BGM_DOOR_OF_TIME, + NA_BGM_OWL, + NA_BGM_SHADOW_TEMPLE, + NA_BGM_WATER_TEMPLE, + NA_BGM_BRIDGE_TO_GANONS, + NA_BGM_VARIOUS_SFX, + NA_BGM_OCARINA_OF_TIME, + NA_BGM_OCARINA_OF_TIME, + NA_BGM_GERUDO_VALLEY, + NA_BGM_POTION_SHOP, + NA_BGM_KOTAKE_KOUME, + NA_BGM_VARIOUS_SFX, + NA_BGM_ESCAPE, + NA_BGM_UNDERGROUND, + NA_BGM_GANONDORF_BOSS, + NA_BGM_GANON_BOSS, + NA_BGM_END_DEMO, +}; + +static s16 sTextIds[] = { 0x2B, 0x2E, 0xC8, 0x2D }; + +static s16 sTextBoxCount[] = { TEXT_STATE_CHOICE, TEXT_STATE_EVENT, TEXT_STATE_EVENT, TEXT_STATE_EVENT }; + +void EnSyatekiMan_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSyatekiMan* this = (EnSyatekiMan*)thisx; + + osSyncPrintf("\n\n"); + // "Old man appeared!! Muhohohohohohohon" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 親父登場!!むほほほほほほほーん ☆☆☆☆☆ \n" VT_RST); + this->actor.targetMode = 1; + Actor_SetScale(&this->actor, 0.01f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gObjectOssanSkel, &gObjectOssanAnim_000338, this->jointTable, + this->morphTable, 9); + if (!LINK_IS_ADULT) { + this->headRot.z = 20; + } + this->blinkTimer = 20; + this->eyeState = 0; + this->blinkFunc = EnSyatekiMan_BlinkWait; + this->actor.colChkInfo.cylRadius = 100; + this->actionFunc = EnSyatekiMan_Start; +} + +void EnSyatekiMan_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnSyatekiMan_Start(EnSyatekiMan* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gObjectOssanAnim_000338); + + Animation_Change(&this->skelAnime, &gObjectOssanAnim_000338, 1.0f, 0.0f, (s16)lastFrame, ANIMMODE_LOOP, -10.0f); + this->actionFunc = EnSyatekiMan_SetupIdle; +} + +void EnSyatekiMan_SetupIdle(EnSyatekiMan* this, GlobalContext* globalCtx) { + if (this->gameResult == SYATEKI_RESULT_REFUSE) { + this->textIdx = SYATEKI_TEXT_REFUSE; + } + + this->actor.textId = sTextIds[this->textIdx]; + this->numTextBox = sTextBoxCount[this->textIdx]; + this->actionFunc = EnSyatekiMan_Idle; +} + +void EnSyatekiMan_Idle(EnSyatekiMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = EnSyatekiMan_Talk; + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } +} + +void EnSyatekiMan_Talk(EnSyatekiMan* this, GlobalContext* globalCtx) { + s16 nextState = 0; + + SkelAnime_Update(&this->skelAnime); + if (this->cameraHold) { + globalCtx->shootingGalleryStatus = -2; + } + if ((this->numTextBox == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + if (this->textIdx == SYATEKI_TEXT_CHOICE) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (gSaveContext.rupees >= 20) { + Rupees_ChangeBy(-20); + this->textIdx = SYATEKI_TEXT_START_GAME; + nextState = 1; + } else { + this->textIdx = SYATEKI_TEXT_NO_RUPEES; + nextState = 2; + } + this->actor.textId = sTextIds[this->textIdx]; + this->numTextBox = sTextBoxCount[this->textIdx]; + break; + case 1: + this->actor.textId = sTextIds[SYATEKI_TEXT_REFUSE]; + this->numTextBox = sTextBoxCount[SYATEKI_TEXT_REFUSE]; + nextState = 2; + break; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + } else { + Message_CloseTextbox(globalCtx); + } + switch (nextState) { + case 0: + this->actionFunc = EnSyatekiMan_SetupIdle; + break; + case 1: + this->actionFunc = EnSyatekiMan_StartGame; + break; + case 2: + this->actionFunc = EnSyatekiMan_StopTalk; + break; + } + } +} + +void EnSyatekiMan_StopTalk(EnSyatekiMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->cameraHold) { + globalCtx->shootingGalleryStatus = -2; + } + if ((this->numTextBox == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + if (this->cameraHold) { + OnePointCutscene_EndCutscene(globalCtx, this->csCam); + this->csCam = SUBCAM_NONE; + this->cameraHold = false; + } + Message_CloseTextbox(globalCtx); + this->actionFunc = EnSyatekiMan_SetupIdle; + } +} + +void EnSyatekiMan_StartGame(EnSyatekiMan* this, GlobalContext* globalCtx) { + EnSyatekiItm* gallery; + + SkelAnime_Update(&this->skelAnime); + if (this->cameraHold) { + globalCtx->shootingGalleryStatus = -2; + } + if ((this->numTextBox == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + if (this->cameraHold) { + OnePointCutscene_EndCutscene(globalCtx, this->csCam); + this->csCam = SUBCAM_NONE; + this->cameraHold = false; + } + Message_CloseTextbox(globalCtx); + gallery = ((EnSyatekiItm*)this->actor.parent); + if (gallery->actor.update != NULL) { + gallery->signal = ENSYATEKI_START; + this->actionFunc = EnSyatekiMan_WaitForGame; + } + } +} + +void EnSyatekiMan_WaitForGame(EnSyatekiMan* this, GlobalContext* globalCtx) { + EnSyatekiItm* gallery; + + SkelAnime_Update(&this->skelAnime); + if (1) {} + gallery = ((EnSyatekiItm*)this->actor.parent); + if ((gallery->actor.update != NULL) && (gallery->signal == ENSYATEKI_END)) { + this->csCam = OnePointCutscene_Init(globalCtx, 8002, -99, &this->actor, MAIN_CAM); + switch (gallery->hitCount) { + case 10: + this->gameResult = SYATEKI_RESULT_WINNER; + this->actor.textId = 0x71AF; + break; + case 8: + case 9: + this->gameResult = SYATEKI_RESULT_ALMOST; + this->actor.textId = 0x71AE; + break; + default: + this->gameResult = SYATEKI_RESULT_FAILURE; + this->actor.textId = 0x71AD; + if (globalCtx->shootingGalleryStatus == 15 + 1) { + this->gameResult = SYATEKI_RESULT_REFUSE; + this->actor.textId = 0x2D; + } + break; + } + globalCtx->shootingGalleryStatus = -2; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->actionFunc = EnSyatekiMan_EndGame; + } +} + +void EnSyatekiMan_EndGame(EnSyatekiMan* this, GlobalContext* globalCtx) { + EnSyatekiItm* gallery; + + SkelAnime_Update(&this->skelAnime); + if ((this->numTextBox == Message_GetState(&globalCtx->msgCtx)) && Message_ShouldAdvance(globalCtx)) { + if (this->gameResult != SYATEKI_RESULT_FAILURE) { + OnePointCutscene_EndCutscene(globalCtx, this->csCam); + this->csCam = SUBCAM_NONE; + } + Message_CloseTextbox(globalCtx); + gallery = ((EnSyatekiItm*)this->actor.parent); + if (gallery->actor.update != NULL) { + gallery->signal = ENSYATEKI_RESULTS; + this->textIdx = 0; + switch (this->gameResult) { + case SYATEKI_RESULT_WINNER: + this->tempGallery = this->actor.parent; + this->actor.parent = NULL; + if (!LINK_IS_ADULT) { + if (!(gSaveContext.itemGetInf[0] & 0x2000)) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ Equip_Pachinko ☆☆☆☆☆ %d\n" VT_RST, + CUR_UPG_VALUE(UPG_BULLET_BAG)); + if (CUR_UPG_VALUE(UPG_BULLET_BAG) == 1) { + this->getItemId = GI_BULLET_BAG_40; + } else { + this->getItemId = GI_BULLET_BAG_50; + } + } else { + this->getItemId = GI_RUPEE_PURPLE; + } + } else { + if (!(gSaveContext.itemGetInf[0] & 0x4000)) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ Equip_Bow ☆☆☆☆☆ %d\n" VT_RST, + CUR_UPG_VALUE(UPG_QUIVER)); + switch (CUR_UPG_VALUE(UPG_QUIVER)) { + case 0: + this->getItemId = GI_RUPEE_PURPLE; + break; + case 1: + this->getItemId = GI_QUIVER_40; + break; + case 2: + this->getItemId = GI_QUIVER_50; + break; + } + } else { + this->getItemId = GI_RUPEE_PURPLE; + } + } + func_8002F434(&this->actor, globalCtx, this->getItemId, 2000.0f, 1000.0f); + this->actionFunc = EnSyatekiMan_GivePrize; + break; + case SYATEKI_RESULT_ALMOST: + this->timer = 20; + func_8008EF44(globalCtx, 15); + this->actionFunc = EnSyatekiMan_RestartGame; + break; + default: + if (this->gameResult == SYATEKI_RESULT_REFUSE) { + this->actionFunc = EnSyatekiMan_SetupIdle; + } else { + this->cameraHold = true; + this->actor.textId = sTextIds[this->textIdx]; + this->numTextBox = sTextBoxCount[this->textIdx]; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->actionFunc = EnSyatekiMan_Talk; + } + break; + } + } + } +} + +void EnSyatekiMan_GivePrize(EnSyatekiMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = EnSyatekiMan_FinishPrize; + } else { + func_8002F434(&this->actor, globalCtx, this->getItemId, 2000.0f, 1000.0f); + } +} + +void EnSyatekiMan_FinishPrize(EnSyatekiMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + // "Successful completion" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 正常終了 ☆☆☆☆☆ \n" VT_RST); + if (!LINK_IS_ADULT) { + gSaveContext.itemGetInf[0] |= 0x2000; + } else if ((this->getItemId == GI_QUIVER_40) || (this->getItemId == GI_QUIVER_50)) { + gSaveContext.itemGetInf[0] |= 0x4000; + } + this->gameResult = SYATEKI_RESULT_NONE; + this->actor.parent = this->tempGallery; + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = EnSyatekiMan_SetupIdle; + } +} + +void EnSyatekiMan_RestartGame(EnSyatekiMan* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer == 0) { + EnSyatekiItm* gallery = ((EnSyatekiItm*)this->actor.parent); + + if (gallery->actor.update != NULL) { + gallery->signal = ENSYATEKI_START; + this->gameResult = SYATEKI_RESULT_NONE; + this->actionFunc = EnSyatekiMan_WaitForGame; + // "Let's try again! Baby!" + osSyncPrintf(VT_FGCOL(BLUE) "再挑戦だぜ!ベイビー!" VT_RST "\n", this); + } + } +} + +void EnSyatekiMan_BlinkWait(EnSyatekiMan* this) { + s16 decrBlinkTimer = this->blinkTimer - 1; + + if (decrBlinkTimer != 0) { + this->blinkTimer = decrBlinkTimer; + } else { + this->blinkFunc = EnSyatekiMan_Blink; + } +} + +void EnSyatekiMan_Blink(EnSyatekiMan* this) { + s16 decrBlinkTimer = this->blinkTimer - 1; + + if (decrBlinkTimer != 0) { + this->blinkTimer = decrBlinkTimer; + } else { + s16 nextEyeState = this->eyeState + 1; + + if (nextEyeState >= 3) { + this->eyeState = 0; + this->blinkTimer = 20 + (s32)(Rand_ZeroOne() * 60.0f); + this->blinkFunc = EnSyatekiMan_BlinkWait; + } else { + this->eyeState = nextEyeState; + this->blinkTimer = 1; + } + } +} + +void EnSyatekiMan_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSyatekiMan* this = (EnSyatekiMan*)thisx; + + if (this->timer != 0) { + this->timer--; + } + this->actionFunc(this, globalCtx); + EnSyatekiMan_SetBgm(); + this->blinkFunc(this); + this->actor.focus.pos.y = 70.0f; + Actor_SetFocus(&this->actor, 70.0f); + func_80038290(globalCtx, &this->actor, &this->headRot, &this->bodyRot, this->actor.focus.pos); +} + +s32 EnSyatekiMan_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnSyatekiMan* this = (EnSyatekiMan*)thisx; + s32 turnDirection; + + if (limbIndex == 1) { + rot->x += this->bodyRot.y; + } + if (limbIndex == 8) { + *dList = gObjectOssanEnSyatekiManDL_007E28; + turnDirection = 1; + if (this->gameResult == SYATEKI_RESULT_REFUSE) { + turnDirection = -1; + } + rot->x += this->headRot.y * turnDirection; + rot->z += this->headRot.z; + } + return 0; +} + +void EnSyatekiMan_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnSyatekiMan* this = (EnSyatekiMan*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnSyatekiMan_OverrideLimbDraw, NULL, this); +} + +void EnSyatekiMan_SetBgm(void) { + if (BREG(80)) { + BREG(80) = false; + Audio_QueueSeqCmd(sBgmList[BREG(81)]); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.h b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.h new file mode 100644 index 000000000..d82cdf9c7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Syateki_Man/z_en_syateki_man.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_SYATEKI_MAN_H +#define Z_EN_SYATEKI_MAN_H + +#include "ultra64.h" +#include "global.h" + +struct EnSyatekiMan; + +typedef void (*EnSyatekiManActionFunc) (struct EnSyatekiMan*, GlobalContext*); +typedef void (*EnSyatekiManOtherFunc) (struct EnSyatekiMan*); + +typedef struct EnSyatekiMan { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[9]; + /* 0x01C6 */ Vec3s morphTable[9]; + /* 0x01FC */ EnSyatekiManActionFunc actionFunc; + /* 0x0200 */ Vec3s headRot; + /* 0x0206 */ Vec3s bodyRot; + /* 0x020C */ s16 eyeState; // Unused + /* 0x020E */ s16 blinkTimer; // Unused + /* 0x0210 */ s16 textIdx; + /* 0x0212 */ s16 numTextBox; + /* 0x0214 */ s16 gameResult; + /* 0x0216 */ s16 timer; + /* 0x0218 */ s32 getItemId; + /* 0x021C */ u8 cameraHold; + /* 0x0220 */ Actor* tempGallery; + /* 0x0224 */ EnSyatekiManOtherFunc blinkFunc; // Seems to be part of a blink system with unk_20C and unk_20E, but it's unused. + /* 0x0228 */ s16 csCam; +} EnSyatekiMan; // size = 0x022C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c new file mode 100644 index 000000000..b8beb1341 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.c @@ -0,0 +1,793 @@ +/* + * File: z_en_syateki_niw.c + * Overlay: ovl_En_Syateki_Niw + * Description: Hopping Cucco + */ + +#include "z_en_syateki_niw.h" +#include "objects/object_niw/object_niw.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnSyatekiNiw_Init(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiNiw_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiNiw_Update(Actor* thisx, GlobalContext* globalCtx); +void EnSyatekiNiw_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B11DEC(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B132A8(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B129EC(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B13464(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B123A8(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B11E78(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B12460(EnSyatekiNiw* this, GlobalContext* globalCtx); +void func_80B128D8(EnSyatekiNiw* this, GlobalContext* globalCtx); + +void func_80B131B8(EnSyatekiNiw* this, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4); + +const ActorInit En_Syateki_Niw_InitVars = { + ACTOR_EN_SYATEKI_NIW, + ACTORCAT_PROP, + FLAGS, + OBJECT_NIW, + sizeof(EnSyatekiNiw), + (ActorFunc)EnSyatekiNiw_Init, + (ActorFunc)EnSyatekiNiw_Destroy, + (ActorFunc)EnSyatekiNiw_Update, + (ActorFunc)EnSyatekiNiw_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 10, 20, 4, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 1, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void EnSyatekiNiw_Init(Actor* thisx, GlobalContext* globalCtx) { + EnSyatekiNiw* this = (EnSyatekiNiw*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.flags &= ~ACTOR_FLAG_0; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 25.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gCuccoSkel, &gCuccoAnim, this->jointTable, this->morphTable, 16); + + this->unk_29E = this->actor.params; + if (this->unk_29E < 0) { + this->unk_29E = 0; + } + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + if (this->unk_29E == 0) { + osSyncPrintf("\n\n"); + // "Archery range chicken" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 射的場鶏 ☆☆☆☆☆ \n" VT_RST); + Actor_SetScale(&this->actor, 0.01f); + } else { + osSyncPrintf("\n\n"); + // "Bomb chicken" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ ボムにわ! ☆☆☆☆☆ \n" VT_RST); + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + Actor_SetScale(&this->actor, 0.01f); + } + + this->unk_2DC = this->actor.world.pos; + this->unk_2E8 = this->actor.world.pos; + this->actionFunc = func_80B11DEC; +} + +void EnSyatekiNiw_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnSyatekiNiw* this = (EnSyatekiNiw*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80B11A94(EnSyatekiNiw* this, GlobalContext* globalCtx, s16 arg2) { + if (this->unk_254 == 0) { + if (arg2 == 0) { + this->unk_264 = 0.0f; + } else { + this->unk_264 = -10000.0f; + } + + this->unk_28E += 1; + this->unk_254 = 3; + if (!(this->unk_28E & 1)) { + this->unk_264 = 0.0f; + if (arg2 == 0) { + this->unk_254 = Rand_ZeroFloat(30.0f); + } + } + } + + if (this->unk_258 == 0) { + this->unk_292++; + this->unk_292 &= 1; + switch (arg2) { + case 0: + this->unk_26C = 0.0f; + this->unk_268 = 0.0f; + break; + + case 1: + this->unk_258 = 3; + this->unk_26C = 7000.0f; + this->unk_268 = 7000.0f; + if (this->unk_292 == 0) { + this->unk_26C = 0.0f; + this->unk_268 = 0.0f; + } + break; + + case 2: + this->unk_258 = 2; + this->unk_268 = this->unk_26C = -10000.0f; + this->unk_280 = this->unk_278 = 25000.0f; + this->unk_284 = this->unk_27C = 6000.0f; + if (this->unk_292 == 0) { + this->unk_278 = 8000.0f; + this->unk_280 = 8000.0f; + } + break; + + case 3: + this->unk_258 = 2; + this->unk_278 = 10000.0f; + this->unk_280 = 10000.0f; + if (this->unk_292 == 0) { + this->unk_278 = 3000.0f; + this->unk_280 = 3000.0f; + } + break; + + case 4: + this->unk_254 = this->unk_256 = 5; + break; + + case 5: + this->unk_258 = 5; + this->unk_278 = 14000.0f; + this->unk_280 = 14000.0f; + if (this->unk_292 == 0) { + this->unk_278 = 10000.0f; + this->unk_280 = 10000.0f; + } + break; + } + } + + if (this->unk_264 != this->unk_2BC.x) { + Math_ApproachF(&this->unk_2BC.x, this->unk_264, 0.5f, 4000.0f); + } + + if (this->unk_26C != this->unk_2A4.x) { + Math_ApproachF(&this->unk_2A4.x, this->unk_26C, 0.8f, 7000.0f); + } + + if (this->unk_280 != this->unk_2A4.y) { + Math_ApproachF(&this->unk_2A4.y, this->unk_280, 0.8f, 7000.0f); + } + + if (this->unk_284 != this->unk_2A4.z) { + Math_ApproachF(&this->unk_2A4.z, this->unk_284, 0.8f, 7000.0f); + } + + if (this->unk_268 != this->unk_2B0.x) { + Math_ApproachF(&this->unk_2B0.x, this->unk_268, 0.8f, 7000.0f); + } + + if (this->unk_278 != this->unk_2B0.y) { + Math_ApproachF(&this->unk_2B0.y, this->unk_278, 0.8f, 7000.0f); + } + + if (this->unk_27C != this->unk_2B0.z) { + Math_ApproachF(&this->unk_2B0.z, this->unk_27C, 0.8f, 7000.0f); + } +} + +void func_80B11DEC(EnSyatekiNiw* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gCuccoAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gCuccoAnim), ANIMMODE_LOOP, + -10.0f); + if (this->unk_29E != 0) { + Actor_SetScale(&this->actor, this->unk_2F4); + } + + this->actionFunc = func_80B11E78; +} + +void func_80B11E78(EnSyatekiNiw* this, GlobalContext* globalCtx) { + Vec3f dustVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f dustAccel = { 0.0f, 0.2f, 0.0f }; + Color_RGBA8 dustPrimColor = { 0, 0, 0, 255 }; + Color_RGBA8 dustEnvColor = { 0, 0, 0, 255 }; + Vec3f dustPos; + f32 tmpf2; + f32 sp4C; + f32 sp50; + f32 tmpf1; + s16 sp4A; + + if ((this->unk_29C != 0) && (this->unk_29E == 0) && (this->actor.bgCheckFlags & 1)) { + this->unk_29C = 0; + this->actionFunc = func_80B123A8; + return; + } + + sp4A = 0; + if ((this->unk_25E == 0) && (this->unk_25C == 0)) { + this->unk_294++; + if (this->unk_294 >= 8) { + this->unk_25E = Rand_ZeroFloat(30.0f); + this->unk_294 = Rand_ZeroFloat(3.99f); + + switch (this->unk_29E) { + case 0: + sp50 = Rand_CenteredFloat(100.0f); + if (sp50 < 0.0f) { + sp50 -= 100.0f; + } else { + sp50 += 100.0f; + } + + sp4C = Rand_CenteredFloat(100.0f); + if (sp4C < 0.0f) { + sp4C -= 100.0f; + } else { + sp4C += 100.0f; + } + + this->unk_2E8.x = this->unk_2DC.x + sp50; + this->unk_2E8.z = this->unk_2DC.z + sp4C; + + if (this->unk_2E8.x < -150.0f) { + this->unk_2E8.x = -150.0f; + } + + if (this->unk_2E8.x > 150.0f) { + this->unk_2E8.x = 150.0f; + } + + if (this->unk_2E8.z < -60.0f) { + this->unk_2E8.z = -60.0f; + } + + if (this->unk_2E8.z > -40.0f) { + this->unk_2E8.z = -40.0f; + } + break; + + case 1: + sp50 = Rand_CenteredFloat(50.0f); + if (sp50 < 0.0f) { + sp50 -= 50.0f; + } else { + sp50 += 50.0f; + } + + sp4C = Rand_CenteredFloat(30.0f); + if (sp4C < 0.0f) { + sp4C -= 30.0f; + } else { + sp4C += 30.0f; + } + + this->unk_2E8.x = this->unk_2DC.x + sp50; + this->unk_2E8.z = this->unk_2DC.z + sp4C; + break; + } + } else { + this->unk_25C = 4; + if (this->actor.bgCheckFlags & 1) { + this->actor.velocity.y = 2.5f; + if ((Rand_ZeroFloat(10.0f) < 1.0f) && (this->unk_29E == 0)) { + this->unk_25C = 0xC; + this->actor.velocity.y = 10.0f; + } + } + } + } + if (this->unk_25C != 0) { + sp4A = 1; + Math_ApproachF(&this->actor.world.pos.x, this->unk_2E8.x, 1.0f, this->unk_2C8.y); + Math_ApproachF(&this->actor.world.pos.z, this->unk_2E8.z, 1.0f, this->unk_2C8.y); + Math_ApproachF(&this->unk_2C8.y, 3.0f, 1.0f, 0.3f); + tmpf1 = this->unk_2E8.x - this->actor.world.pos.x; + tmpf2 = this->unk_2E8.z - this->actor.world.pos.z; + + if (fabsf(tmpf1) < 10.0f) { + tmpf1 = 0; + } + + if (fabsf(tmpf2) < 10.0f) { + tmpf2 = 0.0f; + } + + if ((tmpf1 == 0.0f) && (tmpf2 == 0.0f)) { + this->unk_25C = 0; + this->unk_294 = 7; + } + + Math_SmoothStepToS(&this->actor.world.rot.y, Math_FAtan2F(tmpf1, tmpf2) * (0x8000 / M_PI), 3, this->unk_2C8.z, + 0); + Math_ApproachF(&this->unk_2C8.z, 10000.0f, 1.0f, 1000.0f); + } + + if (this->unk_260 == 0) { + func_80B11A94(this, globalCtx, sp4A); + return; + } + + if ((globalCtx->gameplayFrames % 4) == 0) { + dustVelocity.y = Rand_CenteredFloat(5.0f); + dustAccel.y = 0.2f; + dustPos = this->actor.world.pos; + func_8002836C(globalCtx, &dustPos, &dustVelocity, &dustAccel, &dustPrimColor, &dustEnvColor, 600, 40, 30); + } +} + +void func_80B123A8(EnSyatekiNiw* this, GlobalContext* globalCtx) { + Animation_Change(&this->skelAnime, &gCuccoAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gCuccoAnim), ANIMMODE_LOOP, + -10.0f); + this->unk_27C = 6000.0f; + this->unk_288 = -10000.0f; + this->unk_2B0.z = 6000.0f; + this->unk_2B0.y = 10000.0f; + this->actionFunc = func_80B12460; + this->unk_2A4.z = 6000.0f; + this->unk_284 = 6000.0f; + this->unk_2B0.x = -10000.0f; + this->unk_268 = -10000.0f; + this->unk_2A4.y = -10000.0f; + this->unk_2A4.x = -10000.0f; + this->unk_26C = -10000.0f; +} + +void func_80B12460(EnSyatekiNiw* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 phi_f16 = 0.0f; + + player->actor.freezeTimer = 10; + switch (this->unk_29A) { + case 0: + this->unk_296 = 2; + this->unk_2C8.y = 0.0f; + this->unk_29A = 1; + break; + + case 1: + this->actor.speedXZ = 2.0f; + if (this->unk_25C == 0) { + this->unk_25C = 3; + this->actor.velocity.y = 3.5f; + } + + if (this->unk_25A == 0) { + this->unk_298++; + this->unk_298 &= 1; + this->unk_25A = 5; + } + + phi_f16 = (this->unk_298 == 0) ? 5000.0f : -5000.0f; + if (this->actor.world.pos.z > 100.0f) { + this->actor.speedXZ = 2.0f; + this->actor.gravity = -0.3f; + this->actor.velocity.y = 5.0f; + this->unk_29A = 2; + } + break; + + case 2: + if ((player->actor.world.pos.z - 40.0f) < this->actor.world.pos.z) { + this->actor.speedXZ = 0.0f; + } + + if ((this->actor.bgCheckFlags & 1) && (this->actor.world.pos.z > 110.0f)) { + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + this->unk_284 = 0.0f; + this->unk_27C = 0.0f; + this->unk_278 = 0.0f; + this->unk_280 = 0.0f; + this->unk_288 = 0.0f; + this->actor.speedXZ = 0.5f; + this->unk_254 = this->unk_256 = 0; + this->unk_28E = this->unk_290 = 0; + this->unk_296 = 1; + this->unk_29A = 3; + } + break; + + case 3: + if ((player->actor.world.pos.z - 50.0f) < this->actor.world.pos.z) { + this->actor.speedXZ = 0.0f; + this->unk_262 = 0x3C; + this->unk_25A = 0x14; + this->unk_264 = 10000.0f; + this->unk_29A = 4; + } + break; + + case 4: + if (this->unk_25A == 0) { + this->unk_296 = 4; + this->unk_264 = 5000.0f; + this->unk_26C = 0.0f; + this->unk_268 = 0.0f; + this->unk_284 = 0.0f; + this->unk_27C = 0.0f; + this->unk_280 = 14000.0f; + this->unk_278 = 14000.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + this->unk_254 = this->unk_256 = this->unk_25A = 0x1E; + this->unk_29A = 5; + } + break; + + case 5: + if (this->unk_25A == 1) { + this->unk_258 = 0; + this->unk_296 = 5; + this->unk_256 = this->unk_258; + this->unk_254 = this->unk_258; + this->actor.speedXZ = 1.0f; + } + + if ((this->unk_25A == 0) && ((player->actor.world.pos.z - 30.0f) < this->actor.world.pos.z)) { + Audio_PlaySoundGeneral(NA_SE_VO_LI_DOWN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->unk_25E = 0x14; + this->unk_29A = 6; + this->actor.speedXZ = 0.0f; + } + break; + + case 6: + if (this->unk_25E == 1) { + globalCtx->sceneLoadFlag = 0x14; + globalCtx->nextEntranceIndex = gSaveContext.entranceIndex; + globalCtx->shootingGalleryStatus = 0; + player->actor.freezeTimer = 20; + this->unk_25E = 0x14; + this->actionFunc = func_80B128D8; + } + break; + } + + Math_SmoothStepToS(&this->actor.world.rot.y, + (s16)(Math_FAtan2F(player->actor.world.pos.x - this->actor.world.pos.x, + player->actor.world.pos.z - this->actor.world.pos.z) * + (0x8000 / M_PI)) + + phi_f16, + 5, this->unk_2C8.y, 0); + Math_ApproachF(&this->unk_2C8.y, 3000.0f, 1.0f, 500.0f); + if (this->unk_296 == 2) { + this->unk_256 = 10; + this->unk_254 = this->unk_256; + } + + func_80B11A94(this, globalCtx, this->unk_296); +} + +void func_80B128D8(EnSyatekiNiw* this, GlobalContext* globalCtx) { + if (this->unk_25E == 1) { + gSaveContext.timer1State = 0; + } +} + +void func_80B128F8(EnSyatekiNiw* this, GlobalContext* globalCtx) { + s16 sp26; + s16 sp24; + + Actor_SetFocus(&this->actor, this->unk_2D4); + Actor_GetScreenPos(globalCtx, &this->actor, &sp26, &sp24); + if ((this->actor.projectedPos.z > 200.0f) && (this->actor.projectedPos.z < 800.0f) && (sp26 > 0) && + (sp26 < SCREEN_WIDTH) && (sp24 > 0) && (sp24 < SCREEN_HEIGHT)) { + this->actor.speedXZ = 5.0f; + this->unk_298 = Rand_ZeroFloat(1.99f); + this->unk_2D8 = Rand_CenteredFloat(8000.0f) + -10000.0f; + this->unk_262 = 0x1E; + this->unk_25E = 0x64; + this->actionFunc = func_80B129EC; + } +} + +void func_80B129EC(EnSyatekiNiw* this, GlobalContext* globalCtx) { + s32 pad; + f32 phi_f2; + s16 sp2E; + s16 sp2C; + f32 tmpf2; + + Actor_SetFocus(&this->actor, this->unk_2D4); + Actor_GetScreenPos(globalCtx, &this->actor, &sp2E, &sp2C); + if ((this->unk_25E == 0) || (this->actor.projectedPos.z < -70.0f) || (sp2E < 0) || (sp2E > SCREEN_WIDTH) || + (sp2C < 0) || (sp2C > SCREEN_HEIGHT)) { + Actor_Kill(&this->actor); + return; + } + + this->unk_2A0 = 1; + if (this->unk_25C == 0) { + this->unk_298++; + this->unk_298 &= 1; + this->unk_25C = (s16)Rand_CenteredFloat(4.0f) + 5; + if ((Rand_ZeroFloat(5.0f) < 1.0f) && (this->actor.bgCheckFlags & 1)) { + this->actor.velocity.y = 4.0f; + } + } + + phi_f2 = (this->unk_298 == 0) ? 5000.0f : -5000.0f; + tmpf2 = this->unk_2D8 + phi_f2; + Math_SmoothStepToS(&this->actor.world.rot.y, tmpf2, 3, this->unk_2C8.y, 0); + Math_ApproachF(&this->unk_2C8.y, 3000.0f, 1.0f, 500.0f); + func_80B11A94(this, globalCtx, 2); +} + +void func_80B12BA4(EnSyatekiNiw* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + switch (this->unk_29E) { + case 0: + if (this->unk_29C == 0) { + this->unk_262 = 0x1E; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_A); + this->unk_29C = 1; + this->unk_2A0 = 1; + this->actionFunc = func_80B123A8; + this->actor.gravity = -3.0f; + } + break; + + case 1: + this->unk_262 = 0x1E; + this->unk_2F8 = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_A); + this->unk_260 = 100; + this->unk_2A0 = 1; + this->unk_25E = this->unk_260; + break; + } + } +} + +void EnSyatekiNiw_Update(Actor* thisx, GlobalContext* globalCtx) { + EnSyatekiNiw* this = (EnSyatekiNiw*)thisx; + s32 pad; + s16 i; + Vec3f sp90 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp84 = { 0.0f, 0.0f, 0.0f }; + Vec3f sp78; + Vec3f sp6C; + Vec3f sp60; + + if (1) {} + if (1) {} + if (1) {} + + func_80B132A8(this, globalCtx); + this->unk_28C++; + if (this->unk_254 != 0) { + this->unk_254--; + } + + if (this->unk_258 != 0) { + this->unk_258--; + } + + if (this->unk_25A != 0) { + this->unk_25A--; + } + + if (this->unk_25C != 0) { + this->unk_25C--; + } + + if (this->unk_25E != 0) { + this->unk_25E--; + } + + if (this->unk_262 != 0) { + this->unk_262--; + } + + if (this->unk_260 != 0) { + this->unk_260--; + } + + this->actor.shape.rot = this->actor.world.rot; + this->actor.shape.shadowScale = 15.0f; + + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 20.0f, 60.0f, 0x1D); + + if (this->unk_2A0 != 0) { + for (i = 0; i < 20; i++) { + sp78.x = Rand_CenteredFloat(10.0f) + this->actor.world.pos.x; + sp78.y = Rand_CenteredFloat(10.0f) + (this->actor.world.pos.y + 20.0f); + sp78.z = Rand_CenteredFloat(10.0f) + this->actor.world.pos.z; + sp6C.x = Rand_CenteredFloat(3.0f); + sp6C.y = (Rand_ZeroFloat(2.0f) * 0.5f) + 2.0f; + sp6C.z = Rand_CenteredFloat(3.0f); + sp60.z = sp60.x = 0.0f; + sp60.y = -0.15f; + func_80B131B8(this, &sp78, &sp6C, &sp60, Rand_ZeroFloat(8.0f) + 8.0f); + } + + this->unk_2A0 = 0; + } + + func_80B12BA4(this, globalCtx); + if (this->unk_262 == 0) { + if (this->actionFunc == func_80B11E78) { + this->unk_262 = 0x12C; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_N); + } else { + this->unk_262 = 0x1E; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_CHICKEN_CRY_A); + } + } + + i = 0; + switch (this->unk_29E) { + case 0: + if (globalCtx->shootingGalleryStatus != 0) { + i = 1; + } + break; + + case 1: + i = 1; + break; + } + + if (i != 0) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +s32 SyatekiNiw_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnSyatekiNiw* this = (EnSyatekiNiw*)thisx; + Vec3f sp0 = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == 13) { + rot->y += (s16)this->unk_2BC.x; + } + + if (limbIndex == 11) { + rot->x += (s16)this->unk_2B0.z; + rot->y += (s16)this->unk_2B0.y; + rot->z += (s16)this->unk_2B0.x; + } + + if (limbIndex == 7) { + rot->x += (s16)this->unk_2A4.z; + rot->y += (s16)this->unk_2A4.y; + rot->z += (s16)this->unk_2A4.x; + } + + return false; +} + +void EnSyatekiNiw_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnSyatekiNiw* this = (EnSyatekiNiw*)thisx; + Color_RGBA8 sp30 = { 0, 0, 0, 255 }; + + if (this->actionFunc != func_80B128F8) { + func_80093D18(globalCtx->state.gfxCtx); + if (this->unk_260 != 0) { + func_80026230(globalCtx, &sp30, 0, 0x14); + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, SyatekiNiw_OverrideLimbDraw, NULL, this); + func_80026608(globalCtx); + func_80B13464(this, globalCtx); + } +} + +void func_80B131B8(EnSyatekiNiw* this, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4) { + s16 i; + EnSyatekiNiw_1* ptr = &this->unk_348[0]; + + for (i = 0; i < 5; i++, ptr++) { + if (ptr->unk_00 == 0) { + ptr->unk_00 = 1; + ptr->unk_04 = *arg1; + ptr->unk_10 = *arg2; + ptr->unk_1C = *arg3; + ptr->unk_34 = 0; + ptr->unk_2C = (arg4 / 1000.0f); + ptr->unk_28 = (s16)Rand_ZeroFloat(20.0f) + 0x28; + ptr->unk_2A = Rand_ZeroFloat(1000.0f); + return; + } + } +} + +void func_80B132A8(EnSyatekiNiw* this, GlobalContext* globalCtx) { + s16 i; + EnSyatekiNiw_1* ptr = &this->unk_348[0]; + + for (i = 0; i < 5; i++, ptr++) { + if (ptr->unk_00 != 0) { + ptr->unk_04.x += ptr->unk_10.x; + ptr->unk_04.y += ptr->unk_10.y; + ptr->unk_04.z += ptr->unk_10.z; + ptr->unk_34++; + ptr->unk_10.x += ptr->unk_1C.x; + ptr->unk_10.y += ptr->unk_1C.y; + ptr->unk_10.z += ptr->unk_1C.z; + if (ptr->unk_00 == 1) { + ptr->unk_2A++; + Math_ApproachF(&ptr->unk_10.x, 0.0f, 1.0f, 0.05f); + Math_ApproachF(&ptr->unk_10.z, 0.0f, 1.0f, 0.05f); + if (ptr->unk_10.y < -0.5f) { + ptr->unk_10.y = 0.5f; + } + + ptr->unk_30 = (Math_SinS(ptr->unk_2A * 3000) * M_PI) * 0.2f; + if (ptr->unk_28 < ptr->unk_34) { + ptr->unk_00 = 0; + } + } + } + } +} + +void func_80B13464(EnSyatekiNiw* this, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s16 i; + EnSyatekiNiw_1* ptr = &this->unk_348[0]; + u8 flag = 0; + + OPEN_DISPS(gfxCtx, "../z_en_syateki_niw.c", 1234); + + func_80093D84(globalCtx->state.gfxCtx); + + for (i = 0; i < 5; i++, ptr++) { + if (ptr->unk_00 == 1) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gCuccoParticleAppearDL); + flag++; + } + + Matrix_Translate(ptr->unk_04.x, ptr->unk_04.y, ptr->unk_04.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(ptr->unk_2C, ptr->unk_2C, 1.0f, MTXMODE_APPLY); + Matrix_RotateZ(ptr->unk_30, MTXMODE_APPLY); + Matrix_Translate(0.0f, -1000.0f, 0.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_syateki_niw.c", 1251), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gCuccoParticleAliveDL); + } + } + + CLOSE_DISPS(gfxCtx, "../z_en_syateki_niw.c", 1257); +} diff --git a/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h new file mode 100644 index 000000000..6a6398a6f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Syateki_Niw/z_en_syateki_niw.h @@ -0,0 +1,71 @@ +#ifndef Z_EN_SYATEKI_NIW_H +#define Z_EN_SYATEKI_NIW_H + +#include "ultra64.h" +#include "global.h" + +struct EnSyatekiNiw; + +typedef void (*EnSyatekiNiwActionFunc)(struct EnSyatekiNiw*, GlobalContext*); + +typedef struct { + /* 0x00 */ u8 unk_00; + /* 0x0C */ Vec3f unk_04; + /* 0x10 */ Vec3f unk_10; + /* 0x1C */ Vec3f unk_1C; + /* 0x28 */ s16 unk_28; + /* 0x2A */ s16 unk_2A; + /* 0x2C */ f32 unk_2C; + /* 0x30 */ f32 unk_30; + /* 0x34 */ u8 unk_34; +} EnSyatekiNiw_1; // size = 0x38 + +typedef struct EnSyatekiNiw { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[16]; + /* 0x01F0 */ Vec3s morphTable[16]; + /* 0x0250 */ EnSyatekiNiwActionFunc actionFunc; + /* 0x0254 */ s16 unk_254; + /* 0x0256 */ s16 unk_256; + /* 0x0258 */ s16 unk_258; + /* 0x025A */ s16 unk_25A; + /* 0x025C */ s16 unk_25C; + /* 0x025E */ s16 unk_25E; + /* 0x0260 */ s16 unk_260; + /* 0x0262 */ s16 unk_262; + /* 0x0264 */ f32 unk_264; + /* 0x0268 */ f32 unk_268; + /* 0x026C */ f32 unk_26C; + /* 0x0270 */ char unk_270[0x8]; + /* 0x0278 */ f32 unk_278; + /* 0x027C */ f32 unk_27C; + /* 0x0284 */ f32 unk_280; + /* 0x0280 */ f32 unk_284; + /* 0x0288 */ f32 unk_288; + /* 0x028C */ s16 unk_28C; + /* 0x028E */ s16 unk_28E; + /* 0x0290 */ s16 unk_290; + /* 0x0292 */ s16 unk_292; + /* 0x0294 */ s16 unk_294; + /* 0x0296 */ s16 unk_296; + /* 0x0298 */ s16 unk_298; + /* 0x029C */ s16 unk_29A; + /* 0x029C */ s16 unk_29C; + /* 0x029E */ s16 unk_29E; + /* 0x02A0 */ s16 unk_2A0; + /* 0x02A4 */ Vec3f unk_2A4; + /* 0x02B0 */ Vec3f unk_2B0; + /* 0x02BC */ Vec3f unk_2BC; + /* 0x02C8 */ Vec3f unk_2C8; + /* 0x02D4 */ f32 unk_2D4; + /* 0x02D8 */ f32 unk_2D8; + /* 0x02DC */ Vec3f unk_2DC; + /* 0x02E8 */ Vec3f unk_2E8; + /* 0x02F4 */ f32 unk_2F4; + /* 0x02F8 */ u8 unk_2F8; + /* 0x02FC */ ColliderCylinder collider; + /* 0x0348 */ EnSyatekiNiw_1 unk_348[5]; +} EnSyatekiNiw; // size = 0x0460 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c new file mode 100644 index 000000000..17d86e3de --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.c @@ -0,0 +1,1225 @@ +/* + * File: z_en_ta.c + * Overlay: ovl_En_Ta + * Description: Talon + */ + +#include "z_en_ta.h" +#include "vt.h" +#include "objects/object_ta/object_ta.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnTa_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTa_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B14634(EnTa* this, GlobalContext* globalCtx); +void func_80B146F8(EnTa* this, GlobalContext* globalCtx); +void func_80B14754(EnTa* this, GlobalContext* globalCtx); +void func_80B14C18(EnTa* this, GlobalContext* globalCtx); +void func_80B14CAC(EnTa* this, GlobalContext* globalCtx); +void func_80B14D98(EnTa* this, GlobalContext* globalCtx); +void func_80B154FC(EnTa* this, GlobalContext* globalCtx); +void func_80B16504(EnTa* this, GlobalContext* globalCtx); +void func_80B16608(EnTa* this, GlobalContext* globalCtx); +void func_80B166CC(EnTa* this); +void func_80B16700(EnTa* this); +void func_80B167C0(EnTa* this); +void func_80B167FC(EnTa* this); +void func_80B16854(EnTa* this); +void func_80B16938(EnTa* this); + +const ActorInit En_Ta_InitVars = { + ACTOR_EN_TA, + ACTORCAT_NPC, + FLAGS, + OBJECT_TA, + sizeof(EnTa), + (ActorFunc)EnTa_Init, + (ActorFunc)EnTa_Destroy, + (ActorFunc)EnTa_Update, + (ActorFunc)EnTa_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000004, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0, 0, 0 } }, +}; + +void func_80B13AA0(EnTa* this, EnTaActionFunc arg1, EnTaUnkFunc arg2) { + this->actionFunc = arg1; + this->unk_260 = arg2; +} + +void func_80B13AAC(EnTa* this, GlobalContext* globalCtx) { + u16 faceReaction = Text_GetFaceReaction(globalCtx, 24); + + if (gSaveContext.eventInf[0] & 0x400) { + if (gSaveContext.eventInf[0] & 0x100) { + if (gSaveContext.itemGetInf[0] & 4) { + this->actor.textId = 0x2088; + } else { + this->actor.textId = 0x2086; + } + } else { + this->actor.textId = 0x2085; + } + gSaveContext.eventInf[0] &= ~0x100; + } else if (faceReaction == 0) { + if (gSaveContext.infTable[7] & 0x4000) { + if (gSaveContext.itemGetInf[0] & 4) { + this->actor.textId = 0x208B; + } else { + this->actor.textId = 0x207F; + } + } else { + this->actor.textId = 0x207E; + } + } else { + this->actor.textId = faceReaction; + } +} + +void EnTa_Init(Actor* thisx, GlobalContext* globalCtx2) { + EnTa* this = (EnTa*)thisx; + GlobalContext* globalCtx = globalCtx2; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 36.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gTalonSkel, &gTalonStandAnim, this->jointTable, this->morphTable, + 17); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + this->unk_2E0 = 0; + this->unk_2CE = 0; + this->unk_2E2 = 0; + this->blinkTimer = 20; + this->unk_2B0 = func_80B166CC; + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->actor.velocity.y = -4.0f; + this->actor.minVelocityY = -4.0f; + this->actor.gravity = -1.0f; + + switch (this->actor.params) { + case 1: + osSyncPrintf(VT_FGCOL(CYAN) " 追放タロン \n" VT_RST); + if (gSaveContext.eventChkInf[6] & 0x800) { + Actor_Kill(&this->actor); + } else if (!LINK_IS_ADULT) { + Actor_Kill(&this->actor); + } else if (gSaveContext.eventChkInf[6] & 0x400) { + func_80B13AA0(this, func_80B14CAC, func_80B167C0); + this->eyeIndex = 0; + Animation_PlayOnce(&this->skelAnime, &gTalonStandAnim); + this->currentAnimation = &gTalonStandAnim; + } else { + func_80B13AA0(this, func_80B14754, func_80B167FC); + this->eyeIndex = 2; + Animation_PlayOnce(&this->skelAnime, &gTalonSleepAnim); + this->currentAnimation = &gTalonSleepAnim; + this->actor.shape.shadowScale = 54.0f; + } + break; + case 2: + osSyncPrintf(VT_FGCOL(CYAN) " 出戻りタロン \n" VT_RST); + if (!(gSaveContext.eventChkInf[6] & 0x800)) { + Actor_Kill(&this->actor); + } else if (!LINK_IS_ADULT) { + Actor_Kill(&this->actor); + } else if (globalCtx->sceneNum == SCENE_MALON_STABLE && !IS_DAY) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_FGCOL(CYAN) " 夜はいない \n" VT_RST); + } else { + func_80B13AA0(this, func_80B14D98, func_80B167C0); + this->eyeIndex = 0; + Animation_PlayOnce(&this->skelAnime, &gTalonStandAnim); + this->currentAnimation = &gTalonStandAnim; + } + break; + default: + osSyncPrintf(VT_FGCOL(CYAN) " その他のタロン \n" VT_RST); + if (globalCtx->sceneNum == SCENE_SPOT15) { + if (gSaveContext.eventChkInf[1] & 0x10) { + Actor_Kill(&this->actor); + } else if (gSaveContext.eventChkInf[1] & 0x8) { + func_80B13AA0(this, func_80B14C18, func_80B167C0); + this->eyeIndex = 0; + Animation_PlayOnce(&this->skelAnime, &gTalonStandAnim); + this->currentAnimation = &gTalonStandAnim; + } else { + func_80B13AA0(this, func_80B14634, func_80B167FC); + this->eyeIndex = 2; + Animation_PlayOnce(&this->skelAnime, &gTalonSleepAnim); + this->currentAnimation = &gTalonSleepAnim; + this->actor.shape.shadowScale = 54.0f; + } + } else if (globalCtx->sceneNum == SCENE_SOUKO) { + osSyncPrintf(VT_FGCOL(CYAN) " ロンロン牧場の倉庫 の タロン\n" VT_RST); + if (!(gSaveContext.eventChkInf[1] & 0x10)) { + Actor_Kill(&this->actor); + } else if (LINK_IS_ADULT) { + Actor_Kill(&this->actor); + } else { + if (IS_DAY) { + this->actor.flags |= ACTOR_FLAG_4; + this->unk_2C4[0] = this->unk_2C4[1] = this->unk_2C4[2] = 7; + this->superCuccos[0] = (EnNiw*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_NIW, this->actor.world.pos.x + 5.0f, + this->actor.world.pos.y + 3.0f, this->actor.world.pos.z + 26.0f, 0, 0, 0, 0xD); + this->superCuccos[1] = (EnNiw*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_NIW, this->actor.world.pos.x - 20.0f, + this->actor.world.pos.y + 40.0f, this->actor.world.pos.z - 30.0f, 0, 0, 0, 0xD); + this->superCuccos[2] = (EnNiw*)Actor_Spawn( + &globalCtx->actorCtx, globalCtx, ACTOR_EN_NIW, this->actor.world.pos.x + 20.0f, + this->actor.world.pos.y + 40.0f, this->actor.world.pos.z - 30.0f, 0, 0, 0, 0xD); + func_80B13AAC(this, globalCtx); + + if (gSaveContext.eventInf[0] & 0x400) { + func_80B13AA0(this, func_80B16608, func_80B16938); + Animation_Change(&this->skelAnime, &gTalonSitWakeUpAnim, 1.0f, + Animation_GetLastFrame(&gTalonSitWakeUpAnim) - 1.0f, + Animation_GetLastFrame(&gTalonSitWakeUpAnim), ANIMMODE_ONCE, 0.0f); + gSaveContext.eventInf[0] &= ~0x400; + } else { + func_80B13AA0(this, func_80B16504, func_80B16854); + this->eyeIndex = 0; + Animation_PlayOnce(&this->skelAnime, &gTalonSitSleepingAnim); + this->currentAnimation = &gTalonSitSleepingAnim; + } + } else { + func_80B13AA0(this, func_80B146F8, func_80B167FC); + this->eyeIndex = 2; + Animation_PlayOnce(&this->skelAnime, &gTalonSleepAnim); + this->currentAnimation = &gTalonSleepAnim; + this->actor.shape.shadowScale = 54.0f; + } + } + } else { + func_80B13AA0(this, func_80B14634, func_80B167FC); + this->eyeIndex = 2; + Animation_PlayOnce(&this->skelAnime, &gTalonSleepAnim); + this->currentAnimation = &gTalonSleepAnim; + this->actor.shape.shadowScale = 54.0f; + } + break; + } +} + +void func_80B14248(EnTa* this) { + if (this->actor.shape.shadowScale > 36.0f) { + this->actor.shape.shadowScale -= 0.8f; + } +} + +void EnTa_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTa* this = (EnTa*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + + if (this->actor.params != 1 && this->actor.params != 2 && globalCtx->sceneNum == SCENE_SOUKO) { + gSaveContext.timer1State = 0; + } + + if (this->unk_2E0 & 0x200) { + func_800F5B58(); + } +} + +s32 func_80B142F4(EnTa* this, GlobalContext* globalCtx, u16 textId) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + return true; + } + + this->actor.textId = textId; + + if ((ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4300) && + (this->actor.xzDistToPlayer < 100.0f)) { + this->unk_2E0 |= 1; + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + return false; +} + +void func_80B14398(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B13AA0(this, func_80B14754, func_80B167FC); + } +} + +void func_80B143D4(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B13AA0(this, func_80B146F8, func_80B167FC); + } +} + +void func_80B14410(EnTa* this) { + if (!LINK_IS_ADULT) { + func_80B13AA0(this, func_80B14C18, func_80B167C0); + gSaveContext.eventChkInf[1] |= 0x8; + } else { + func_80B13AA0(this, func_80B14CAC, func_80B167C0); + gSaveContext.eventChkInf[6] |= 0x400; + } +} + +void func_80B1448C(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B14410(this); + } + func_80B14248(this); + this->unk_2E0 |= 0x4; +} + +void func_80B144D8(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B14410(this); + this->blinkTimer = 1; + this->unk_2B0 = func_80B16700; + } + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) { + this->eyeIndex = 1; + func_80B13AA0(this, func_80B1448C, func_80B167C0); + } + func_80B14248(this); + this->unk_2E0 |= 4; +} + +void func_80B14570(EnTa* this, GlobalContext* globalCtx) { + this->unk_2E0 |= 4; + + if (this->unk_2CC == 0) { + func_80B13AA0(this, func_80B144D8, func_80B167C0); + this->unk_2CE = 3; + this->unk_2CC = 60; + Animation_PlayOnce(&this->skelAnime, &gTalonWakeUpAnim); + this->currentAnimation = &gTalonStandAnim; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_TA_SURPRISE); + } +} + +void func_80B145F8(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B13AA0(this, func_80B14634, func_80B167FC); + } +} + +void func_80B14634(EnTa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + s32 exchangeItemId = func_8002F368(globalCtx); + + switch (exchangeItemId) { + case EXCH_ITEM_CHICKEN: + player->actor.textId = 0x702B; + func_80B13AA0(this, func_80B14570, func_80B167C0); + this->unk_2CC = 40; + break; + default: + if (exchangeItemId != EXCH_ITEM_NONE) { + player->actor.textId = 0x702A; + } + func_80B13AA0(this, func_80B145F8, func_80B167FC); + break; + } + } else { + this->actor.textId = 0x702A; + func_8002F298(&this->actor, globalCtx, 100.0f, 3); + } +} + +void func_80B146F8(EnTa* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + func_80B13AA0(this, func_80B143D4, func_80B167FC); + } + this->actor.textId = 0x204B; + func_8002F2CC(&this->actor, globalCtx, 100.0f); +} + +void func_80B14754(EnTa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + s32 exchangeItemId = func_8002F368(globalCtx); + + switch (exchangeItemId) { + case EXCH_ITEM_POCKET_CUCCO: + player->actor.textId = 0x702B; + func_80B13AA0(this, func_80B14570, func_80B167C0); + this->unk_2CC = 40; + break; + default: + if (exchangeItemId != EXCH_ITEM_NONE) { + player->actor.textId = 0x5015; + } + func_80B13AA0(this, func_80B14398, func_80B167FC); + break; + } + } else { + this->actor.textId = 0x5015; + func_8002F298(&this->actor, globalCtx, 100.0f, 6); + } +} + +void func_80B14818(EnTa* this, GlobalContext* globalCtx) { + s32 framesMod12 = (s32)globalCtx->state.frames % 12; + + if (framesMod12 == 0 || framesMod12 == 6) { + Audio_PlayActorSound2(&this->actor, NA_SE_PL_WALK_GROUND); + } + if (this->actor.speedXZ < 6.0f) { + this->actor.speedXZ += 0.4f; + } + Actor_MoveForward(&this->actor); +} + +void func_80B14898(EnTa* this, GlobalContext* globalCtx) { + func_80033480(globalCtx, &this->actor.world.pos, 50.0f, 2, 250, 20, 1); + func_80B14818(this, globalCtx); + + if (this->unk_2CC == 0) { + Actor_Kill(&this->actor); + } +} + +void func_80B1490C(EnTa* this, GlobalContext* globalCtx) { + this->actor.world.rot.y += 0xC00; + this->actor.shape.rot.y += 0xC00; + + if (this->unk_2CC == 0) { + func_80B13AA0(this, func_80B14898, func_80B167C0); + this->unk_2CC = 60; + } +} + +void func_80B1496C(EnTa* this, GlobalContext* globalCtx) { + func_80033480(globalCtx, &this->actor.world.pos, 50.0f, 2, 250, 20, 1); + func_80B14818(this, globalCtx); + + if (this->unk_2CC == 0) { + func_80B13AA0(this, func_80B1490C, func_80B167C0); + this->unk_2CC = 5; + } +} + +void func_80B149F4(EnTa* this, GlobalContext* globalCtx) { + this->actor.world.rot.y -= 0xD00; + this->actor.shape.rot.y -= 0xD00; + + if (this->unk_2CC == 0) { + func_80B13AA0(this, func_80B1496C, func_80B167C0); + this->unk_2CC = 65; + } +} + +void func_80B14A54(EnTa* this, GlobalContext* globalCtx) { + func_80033480(globalCtx, &this->actor.world.pos, 50.0f, 2, 250, 20, 1); + func_80B14818(this, globalCtx); + + if (this->unk_2CC == 20) { + Message_CloseTextbox(globalCtx); + } + if (this->unk_2CC == 0) { + this->unk_2CC = 5; + func_80B13AA0(this, func_80B149F4, func_80B167C0); + } +} + +void func_80B14AF4(EnTa* this, GlobalContext* globalCtx) { + this->actor.world.rot.y -= 0xC00; + this->actor.shape.rot.y -= 0xC00; + + if (this->unk_2CC == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_TA_CRY_1); + func_80B13AA0(this, func_80B14A54, func_80B167C0); + this->unk_2CC = 65; + this->actor.flags |= ACTOR_FLAG_4; + } +} + +void func_80B14B6C(EnTa* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) { + OnePointCutscene_Init(globalCtx, 4175, -99, &this->actor, MAIN_CAM); + func_80B13AA0(this, func_80B14AF4, func_80B167C0); + this->unk_2CC = 5; + gSaveContext.eventChkInf[1] |= 0x10; + Animation_PlayOnce(&this->skelAnime, &gTalonRunTransitionAnim); + this->currentAnimation = &gTalonRunAnim; + } + this->unk_2E0 |= 1; +} + +void func_80B14C18(EnTa* this, GlobalContext* globalCtx) { + if (func_80B142F4(this, globalCtx, 0x702C)) { + func_80B13AA0(this, func_80B14B6C, func_80B167C0); + } + func_80B14248(this); +} + +void func_80B14C60(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B13AA0(this, func_80B14CAC, func_80B167C0); + } + this->unk_2E0 |= 1; +} + +void func_80B14CAC(EnTa* this, GlobalContext* globalCtx) { + if (gSaveContext.eventChkInf[1] & 0x100) { + if (func_80B142F4(this, globalCtx, 0x5017)) { + func_80B13AA0(this, func_80B14C60, func_80B167C0); + gSaveContext.eventChkInf[6] |= 0x800; + } + } else if (func_80B142F4(this, globalCtx, 0x5016)) { + func_80B13AA0(this, func_80B14C60, func_80B167C0); + } + func_80B14248(this); +} + +void func_80B14D4C(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B13AA0(this, func_80B14D98, func_80B167C0); + } + this->unk_2E0 |= 1; +} + +void func_80B14D98(EnTa* this, GlobalContext* globalCtx) { + if (func_80B142F4(this, globalCtx, 0x2055)) { + func_80B13AA0(this, func_80B14D4C, func_80B167C0); + } +} + +s32 func_80B14DD8(void) { + if (gSaveContext.rupees < 30) { + return 0; + } else if (!Inventory_HasEmptyBottle()) { + return 1; + } else { + return 2; + } +} + +void func_80B14E28(EnTa* this, GlobalContext* globalCtx) { + Vec3f b; + Vec3f a; + + this->unk_2D0 = Gameplay_CreateSubCamera(globalCtx); + this->unk_2D2 = globalCtx->activeCamera; + Gameplay_ChangeCameraStatus(globalCtx, this->unk_2D2, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_2D0, CAM_STAT_ACTIVE); + + b.x = 1053.0f; + b.y = 11.0f; + b.z = 22.0f; + + a.x = 1053.0f; + a.y = 45.0f; + a.z = -40.0f; + + Gameplay_CameraSetAtEye(globalCtx, this->unk_2D0, &a, &b); +} + +void func_80B14EDC(EnTa* this, GlobalContext* globalCtx) { + Gameplay_ChangeCameraStatus(globalCtx, this->unk_2D2, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, this->unk_2D0); +} + +void func_80B14F20(EnTa* this, EnTaActionFunc arg1) { + func_80B13AA0(this, arg1, func_80B16854); + this->eyeIndex = 2; + Animation_Change(&this->skelAnime, &gTalonSitSleepingAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gTalonSitSleepingAnim), ANIMMODE_ONCE, -5.0f); + this->unk_2E2 = 0; + this->currentAnimation = &gTalonSitSleepingAnim; +} + +void func_80B14FAC(EnTa* this, EnTaActionFunc arg1) { + this->eyeIndex = 1; + func_80B13AA0(this, arg1, func_80B16938); + this->unk_2E0 &= ~0x10; + Animation_Change(&this->skelAnime, &gTalonSitWakeUpAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gTalonSitWakeUpAnim), + ANIMMODE_ONCE, -5.0f); +} + +void func_80B15034(EnTa* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + func_80B14F20(this, func_80B16504); + func_80B13AAC(this, globalCtx); + } + this->unk_2E0 |= 1; +} + +s32 func_80B150AC(EnTa* this, GlobalContext* globalCtx, s32 idx) { + Player* player = GET_PLAYER(globalCtx); + Actor* interactRangeActor; + + if (player->stateFlags1 & 0x800) { + interactRangeActor = player->interactRangeActor; + if (interactRangeActor != NULL && interactRangeActor->id == ACTOR_EN_NIW && + interactRangeActor == &this->superCuccos[idx]->actor) { + return true; + } + } + return false; +} + +void func_80B15100(EnTa* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + s32 unk_2CA; + + Animation_Change(&this->skelAnime, &gTalonSitWakeUpAnim, 1.0f, + Animation_GetLastFrame(&gTalonSitWakeUpAnim) - 1.0f, + Animation_GetLastFrame(&gTalonSitWakeUpAnim), ANIMMODE_ONCE, 10.0f); + this->unk_2E0 &= ~0x10; + Message_CloseTextbox(globalCtx); + unk_2CA = this->unk_2CA; + this->actionFunc = func_80B154FC; + this->superCuccos[unk_2CA]->actor.gravity = 0.1f; + this->superCuccos[unk_2CA]->actor.velocity.y = 0.0f; + this->superCuccos[unk_2CA]->actor.speedXZ = 0.0f; + this->superCuccos[unk_2CA]->actor.parent = NULL; + + if (player->interactRangeActor == &this->superCuccos[unk_2CA]->actor) { + player->interactRangeActor = NULL; + } + if (player->heldActor == &this->superCuccos[unk_2CA]->actor) { + player->heldActor = NULL; + } + player->stateFlags1 &= ~0x800; + this->superCuccos[unk_2CA] = NULL; + } + this->unk_2E0 |= 1; +} + +void func_80B15260(EnTa* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->actionFunc = func_80B15100; + this->actor.flags &= ~ACTOR_FLAG_16; + } else { + func_8002F2CC(&this->actor, globalCtx, 1000.0f); + } + this->unk_2E0 |= 1; +} + +s32 EnTa_GetSuperCuccosCount(EnTa* this, GlobalContext* globalCtx) { + s32 count; + s32 i; + + for (count = 0, i = 0; i < ARRAY_COUNT(this->superCuccos); i++) { + if (this->superCuccos[i] != NULL) { + count++; + } + } + return count; +} + +void func_80B15308(EnTa* this) { + if (this->unk_2E0 & 0x10) { + if (this->unk_2E0 & 0x100) { + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 17.0f, 22.0f, ANIMMODE_ONCE, 0.0f); + this->unk_2E0 &= ~0x100; + } else { + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, -1.0f, 21.0f, 16.0f, ANIMMODE_ONCE, 3.0f); + this->unk_2E0 |= 0x100; + } + this->unk_2E0 &= ~0x10; + } +} + +void func_80B153D4(EnTa* this, GlobalContext* globalCtx) { + func_80B15308(this); + + if (this->unk_2CC == 0) { + if (this->unk_2E0 & 0x80) { + this->unk_2E0 &= ~0x80; + func_80B14EDC(this, globalCtx); + } + } +} + +void func_80B15424(EnTa* this, GlobalContext* globalCtx) { + func_80B15308(this); + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->nextEntranceIndex = 0x5E4; + + if (gSaveContext.eventInf[0] & 0x100) { + globalCtx->fadeTransition = 46; + gSaveContext.nextTransition = 3; + } else { + globalCtx->fadeTransition = 38; + gSaveContext.nextTransition = 2; + } + + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.eventInf[0] |= 0x400; + this->actionFunc = func_80B153D4; + this->unk_2CC = 22; + } +} + +void func_80B154FC(EnTa* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->superCuccos); i++) { + if (this->superCuccos[i] != NULL) { + if (this->superCuccos[i]->actor.gravity > -2.0f) { + this->superCuccos[i]->actor.gravity -= 0.03f; + } + + if (func_80B150AC(this, globalCtx, i)) { + if (this->unk_2C4[i] > 0) { + this->unk_2C4[i]--; + } else { + this->unk_2CA = i; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 8.0f, 29.0f, ANIMMODE_ONCE, -10.0f); + this->unk_2E0 &= ~0x10; + + switch (EnTa_GetSuperCuccosCount(this, globalCtx)) { + case 1: + gSaveContext.timer1State = 0; + func_8002DF54(globalCtx, &this->actor, 1); + + Message_StartTextbox(globalCtx, 0x2084, &this->actor); + this->actionFunc = func_80B15424; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 8.0f, 29.0f, ANIMMODE_ONCE, + -10.0f); + this->unk_2E0 &= ~0x10; + this->unk_2E0 &= ~0x100; + gSaveContext.eventInf[0] |= 0x100; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); + this->unk_2E0 &= ~0x200; + Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET); + return; + case 2: + this->actor.textId = 0x2083; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_TA_CRY_1); + break; + case 3: + this->actor.textId = 0x2082; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_TA_SURPRISE); + break; + } + this->actionFunc = func_80B15260; + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2CC(&this->actor, globalCtx, 1000.0f); + return; + } + } else { + this->unk_2C4[i] = 7; + } + } + } + + if (gSaveContext.timer1Value == 10) { + func_800F5918(); + } + + if (gSaveContext.timer1Value == 0 && !Gameplay_InCsMode(globalCtx)) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); + this->unk_2E0 &= ~0x200; + func_80078884(NA_SE_SY_FOUND); + gSaveContext.timer1State = 0; + func_8002DF54(globalCtx, &this->actor, 1); + Message_StartTextbox(globalCtx, 0x2081, &this->actor); + this->actionFunc = func_80B15424; + func_80B14E28(this, globalCtx); + gSaveContext.eventInf[0] &= ~0x100; + this->unk_2E0 |= 0x80; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 8.0f, 29.0f, ANIMMODE_ONCE, -10.0f); + this->unk_2E0 &= ~0x10; + this->unk_2E0 &= ~0x100; + } + + this->unk_2E0 |= 1; +} + +void func_80B1585C(EnTa* this, GlobalContext* globalCtx) { + s32 i; + + if (this->unk_2CC > 35) { + for (i = 1; i < ARRAY_COUNT(this->superCuccos); i++) { + if (this->superCuccos[i] != NULL) { + Math_SmoothStepToS(&this->superCuccos[i]->actor.world.rot.y, i * -10000 - 3000, 2, 0x800, 0x100); + this->superCuccos[i]->actor.shape.rot.y = this->superCuccos[i]->actor.world.rot.y; + } + } + } else if (this->unk_2CC == 35) { + for (i = 0; i < ARRAY_COUNT(this->superCuccos); i++) { + this->unk_2C4[i] = (s32)(Rand_CenteredFloat(6.0f) + 10.0f); + + if (this->superCuccos[i] != NULL) { + EnNiw* niw = this->superCuccos[i]; + + niw->unk_308 = 1; + niw->actor.gravity = 0.0f; + } + } + } else { + for (i = 0; i < ARRAY_COUNT(this->superCuccos); i++) { + if (this->unk_2CC < 35 - this->unk_2C4[i]) { + if (this->superCuccos[i] != NULL) { + if (this->superCuccos[i]->actor.gravity > -2.0f) { + this->superCuccos[i]->actor.gravity -= 0.03f; + } + } + } + } + } + + if (this->unk_2CC == 0) { + func_80B13AA0(this, func_80B154FC, func_80B16938); + this->unk_2E0 &= ~0x10; + Animation_Change(&this->skelAnime, &gTalonSitWakeUpAnim, 1.0f, + Animation_GetLastFrame(&gTalonSitWakeUpAnim) - 1.0f, + Animation_GetLastFrame(&gTalonSitWakeUpAnim), ANIMMODE_ONCE, 10.0f); + func_8002DF54(globalCtx, &this->actor, 7); + } +} + +void func_80B15AD4(EnTa* this, GlobalContext* globalCtx) { + if (this->unk_2CC == 0 && this->unk_2E0 & 0x20) { + func_80B13AA0(this, func_80B1585C, func_80B16938); + this->unk_2E0 &= ~0x10; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 1.0f, + Animation_GetLastFrame(&gTalonSitHandsUpAnim), ANIMMODE_ONCE, 0.0f); + this->unk_2CC = 50; + func_80088B34(0x1E); + func_800F5ACC(NA_BGM_TIMED_MINI_GAME); + this->unk_2E0 |= 0x200; + Message_CloseTextbox(globalCtx); + func_8002DF54(globalCtx, &this->actor, 1); + } + + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->unk_2E0 |= 0x20; + } + + this->unk_2E0 |= 1; +} + +void func_80B15BF8(EnTa* this, GlobalContext* globalCtx) { + if (this->unk_2E0 & 0x10) { + func_80B13AA0(this, func_80B15AD4, func_80B16938); + this->unk_2E0 &= ~0x10; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 0.0f, 1.0f, ANIMMODE_ONCE, 0.0f); + this->unk_2CC = 5; + } + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->unk_2E0 |= 0x20; + } + this->unk_2E0 |= 1; +} + +void func_80B15CC8(EnTa* this, GlobalContext* globalCtx) { + if (this->unk_2E0 & 0x10) { + func_80B13AA0(this, func_80B15BF8, func_80B16938); + this->unk_2E0 &= ~0x10; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, -1.0f, 29.0f, 0.0f, ANIMMODE_ONCE, 10.0f); + } + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->unk_2E0 |= 0x20; + } + this->unk_2E0 |= 1; +} + +void func_80B15D90(EnTa* this, GlobalContext* globalCtx) { + func_80B13AA0(this, func_80B15CC8, func_80B16938); + this->unk_2E0 &= ~0x10; + Animation_Change(&this->skelAnime, &gTalonSitHandsUpAnim, 1.0f, 8.0f, 29.0f, ANIMMODE_ONCE, -10.0f); + Message_ContinueTextbox(globalCtx, 0x2080); + this->unk_2E0 &= ~0x20; +} + +void func_80B15E28(EnTa* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_80B14F20(this, func_80B16504); + func_80B13AAC(this, globalCtx); + } + this->unk_2E0 |= 1; +} + +void func_80B15E80(EnTa* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->actionFunc = func_80B15E28; + if (!(this->unk_2E0 & 0x2)) { + gSaveContext.itemGetInf[0] |= 4; + } + this->unk_2E0 &= ~0x2; + } else if (this->unk_2E0 & 2) { + func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 50.0f); + } else { + func_8002F434(&this->actor, globalCtx, GI_MILK_BOTTLE, 10000.0f, 50.0f); + } + this->unk_2E0 |= 1; +} + +void func_80B15F54(EnTa* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->unk_2E0 &= ~0x2; + func_80B13AA0(this, func_80B15E80, func_80B16938); + func_8002F434(&this->actor, globalCtx, GI_MILK_BOTTLE, 10000.0f, 50.0f); + } +} + +void func_80B15FE8(EnTa* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + switch (func_80B14DD8()) { + case 0: + Message_ContinueTextbox(globalCtx, 0x85); + func_80B13AA0(this, func_80B15034, func_80B16938); + break; + case 1: + Message_ContinueTextbox(globalCtx, 0x208A); + func_80B13AA0(this, func_80B15E28, func_80B16938); + break; + case 2: + this->unk_2E0 |= 2; + func_80B13AA0(this, func_80B15E80, func_80B16938); + Rupees_ChangeBy(-30); + func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 50.0f); + break; + } + break; + case 1: + if (gSaveContext.rupees < 10) { + Message_ContinueTextbox(globalCtx, 0x85); + func_80B13AA0(this, func_80B15034, func_80B16938); + } else { + Rupees_ChangeBy(-10); + func_80B15D90(this, globalCtx); + } + break; + case 2: + func_80B14F20(this, func_80B16504); + func_80B13AAC(this, globalCtx); + break; + } + } + + if (this->unk_2E0 & 0x10) { + this->unk_2E0 |= 1; + } +} + +void func_80B161C0(EnTa* this, GlobalContext* globalCtx) { + s32 price; + + if (this->actor.textId == 0x2085) { + price = 5; + } else { + price = 10; + } + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (gSaveContext.rupees < price) { + Message_ContinueTextbox(globalCtx, 0x85); + func_80B13AA0(this, func_80B15034, func_80B16938); + } else { + Rupees_ChangeBy(-price); + func_80B15D90(this, globalCtx); + } + break; + case 1: + func_80B14F20(this, func_80B16504); + func_80B13AAC(this, globalCtx); + break; + } + } + + if (this->unk_2E0 & 0x10) { + this->unk_2E0 |= 1; + } +} + +void func_80B162E8(EnTa* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_ContinueTextbox(globalCtx, 0x2087); + func_80B13AA0(this, func_80B15F54, func_80B16938); + } + + if (this->unk_2E0 & 0x10) { + this->unk_2E0 |= 1; + } +} + +void func_80B16364(EnTa* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[7] |= 0x4000; + if (gSaveContext.itemGetInf[0] & 4) { + Message_ContinueTextbox(globalCtx, 0x208B); + func_80B13AA0(this, func_80B15FE8, func_80B16938); + } else { + Message_ContinueTextbox(globalCtx, 0x207F); + func_80B13AA0(this, func_80B161C0, func_80B16938); + } + } + + if (this->unk_2E0 & 0x10) { + this->unk_2E0 |= 1; + } +} + +void func_80B1642C(EnTa* this, GlobalContext* globalCtx) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + if (Inventory_HasEmptyBottle()) { + Message_CloseTextbox(globalCtx); + this->unk_2E0 |= 2; + func_80B13AA0(this, func_80B15E80, func_80B16938); + func_8002F434(&this->actor, globalCtx, GI_MILK, 10000.0f, 50.0f); + } else { + Message_ContinueTextbox(globalCtx, 0x208A); + func_80B13AA0(this, func_80B15E28, func_80B16938); + } + } +} + +void func_80B16504(EnTa* this, GlobalContext* globalCtx) { + u16 faceReaction = Text_GetFaceReaction(globalCtx, 0x18); + + func_80B13AAC(this, globalCtx); + + if (func_80B142F4(this, globalCtx, this->actor.textId)) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_TA_SURPRISE); + + if (faceReaction != 0) { + func_80B14FAC(this, func_80B15E28); + } else { + gSaveContext.infTable[7] |= 0x4000; + + switch (this->actor.textId) { + case 0x207E: + case 0x207F: + func_80B14FAC(this, func_80B161C0); + break; + case 0x208B: + func_80B14FAC(this, func_80B15FE8); + break; + default: + func_80B14FAC(this, func_80B16364); + break; + } + } + } + this->unk_2E0 &= ~1; +} + +void func_80B16608(EnTa* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + switch (this->actor.textId) { + case 0x2085: + this->actionFunc = func_80B161C0; + break; + case 0x2086: + this->actionFunc = func_80B162E8; + break; + case 0x2088: + this->actionFunc = func_80B1642C; + break; + } + this->actor.flags &= ~ACTOR_FLAG_16; + } else { + this->actor.flags |= ACTOR_FLAG_16; + func_8002F2CC(&this->actor, globalCtx, 1000.0f); + } + this->unk_2E0 |= 1; +} + +void func_80B166CC(EnTa* this) { + s16 temp_v0 = this->blinkTimer - 1; + + if (temp_v0 != 0) { + this->blinkTimer = temp_v0; + } else { + this->unk_2B0 = func_80B16700; + } +} + +void func_80B16700(EnTa* this) { + s16 blinkTimer = this->blinkTimer - 1; + + if (blinkTimer != 0) { + this->blinkTimer = blinkTimer; + } else { + s16 nextEyeIndex = this->eyeIndex + 1; + s16 blinkTimer = 3; + + if (nextEyeIndex >= blinkTimer) { + this->eyeIndex = 0; + if (this->unk_2CE > 0) { + this->unk_2CE--; + blinkTimer = 1; + } else { + blinkTimer = (s32)(Rand_ZeroOne() * 60.0f) + 20; + } + this->blinkTimer = blinkTimer; + this->unk_2B0 = func_80B166CC; + } else { + this->eyeIndex = nextEyeIndex; + this->blinkTimer = 1; + } + } +} + +void func_80B167C0(EnTa* this) { + if (SkelAnime_Update(&this->skelAnime)) { + Animation_PlayOnce(&this->skelAnime, this->currentAnimation); + } +} + +void func_80B167FC(EnTa* this) { + if (SkelAnime_Update(&this->skelAnime)) { + Animation_PlayOnce(&this->skelAnime, this->currentAnimation); + Audio_PlayActorSound2(&this->actor, NA_SE_VO_TA_SLEEP); + } + this->unk_2E0 |= 0xC; +} + +void func_80B16854(EnTa* this) { + if (this->unk_2E2 > 0) { + this->unk_2E2--; + } else { + if (SkelAnime_Update(&this->skelAnime)) { + Animation_PlayOnce(&this->skelAnime, this->currentAnimation); + this->unk_2E2 = Rand_ZeroFloat(100.0f) + 100.0f; + } + + if (this->skelAnime.curFrame < 96.0f && this->skelAnime.curFrame >= 53.0f) { + this->eyeIndex = 1; + } else { + this->eyeIndex = 2; + } + this->unk_2E0 |= 8; + } + this->unk_2E0 |= 4; +} + +void func_80B16938(EnTa* this) { + if (!(this->unk_2E0 & 0x10)) { + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_2E0 |= 0x10; + } + this->unk_2E0 |= 8; + } +} + +void EnTa_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTa* this = (EnTa*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->unk_260(this); + this->actionFunc(this, globalCtx); + + if (!(this->unk_2E0 & 4)) { + this->unk_2B0(this); + } + + if (this->unk_2E0 & 1) { + func_80038290(globalCtx, &this->actor, &this->unk_2D4, &this->unk_2DA, this->actor.focus.pos); + } else { + Math_SmoothStepToS(&this->unk_2D4.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2D4.y, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2DA.x, 0, 6, 6200, 100); + Math_SmoothStepToS(&this->unk_2DA.y, 0, 6, 6200, 100); + } + + this->unk_2E0 &= ~0x5; + + if (this->unk_2CC > 0) { + this->unk_2CC--; + } +} + +s32 EnTa_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnTa* this = (EnTa*)thisx; + + switch (limbIndex) { + case 8: + rot->x += this->unk_2DA.y; + rot->y -= this->unk_2DA.x; + break; + case 15: + rot->x += this->unk_2D4.y; + rot->z += this->unk_2D4.x; + break; + } + + if (this->unk_2E0 & 0x8) { + this->unk_2E0 &= ~0x8; + } else if ((limbIndex == 8) || (limbIndex == 10) || (limbIndex == 13)) { + s32 limbIdx50 = limbIndex * 50; + + rot->y += Math_SinS(globalCtx->state.frames * (limbIdx50 + 0x814)) * 200.0f; + rot->z += Math_CosS(globalCtx->state.frames * (limbIdx50 + 0x940)) * 200.0f; + } + + return false; +} + +void EnTa_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f D_80B16E7C = { + 1100.0f, + 1000.0f, + 0.0f, + }; + EnTa* this = (EnTa*)thisx; + + if (limbIndex == 15) { + Matrix_MultVec3f(&D_80B16E7C, &this->actor.focus.pos); + } +} + +void EnTa_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + gTalonEyeOpenTex, + gTalonEyeHalfTex, + gTalonEyeClosedTex, + }; + EnTa* this = (EnTa*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_ta.c", 2381); + + func_800943C8(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x8, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeIndex])); + gSPSegment(POLY_OPA_DISP++, 0x9, SEGMENTED_TO_VIRTUAL(gTalonHeadSkinTex)); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnTa_OverrideLimbDraw, EnTa_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_ta.c", 2400); +} diff --git a/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.h b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.h new file mode 100644 index 000000000..8429ad7ab --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Ta/z_en_ta.h @@ -0,0 +1,39 @@ +#ifndef Z_EN_TA_H +#define Z_EN_TA_H + +#include "ultra64.h" +#include "global.h" + +#include "overlays/actors/ovl_En_Niw/z_en_niw.h" + +struct EnTa; + +typedef void (*EnTaActionFunc)(struct EnTa*, GlobalContext*); +typedef void (*EnTaUnkFunc)(struct EnTa*); + +typedef struct EnTa { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ EnTaActionFunc actionFunc; + /* 0x0260 */ EnTaUnkFunc unk_260; + /* 0x0264 */ ColliderCylinder collider; + /* 0x02B0 */ EnTaUnkFunc unk_2B0; + /* 0x02B4 */ s16 eyeIndex; + /* 0x02B6 */ s16 blinkTimer; + /* 0x02B8 */ EnNiw* superCuccos[3]; + /* 0x02C4 */ s16 unk_2C4[3]; + /* 0x02CA */ u8 unk_2CA; + /* 0x02CC */ s16 unk_2CC; + /* 0x02CE */ s16 unk_2CE; + /* 0x02D0 */ s16 unk_2D0; + /* 0x02D2 */ s16 unk_2D2; + /* 0x02D4 */ Vec3s unk_2D4; + /* 0x02DA */ Vec3s unk_2DA; + /* 0x02E0 */ u16 unk_2E0; + /* 0x02E2 */ s16 unk_2E2; + /* 0x02E4 */ AnimationHeader* currentAnimation; +} EnTa; // size = 0x02E8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c new file mode 100644 index 000000000..71cd47144 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.c @@ -0,0 +1,231 @@ +/* + * File: z_en_takara_man.c + * Overlay: ovl_En_Takara_Man + * Description: Treasure Chest Game Man + */ + +#include "z_en_takara_man.h" +#include "vt.h" +#include "objects/object_ts/object_ts.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_27) + +void EnTakaraMan_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTakaraMan_Reset(Actor* thisx, GlobalContext* globalCtx); +void EnTakaraMan_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTakaraMan_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B176E0(EnTakaraMan* this, GlobalContext* globalCtx); +void func_80B1778C(EnTakaraMan* this, GlobalContext* globalCtx); +void func_80B17B14(EnTakaraMan* this, GlobalContext* globalCtx); +void func_80B17934(EnTakaraMan* this, GlobalContext* globalCtx); +void func_80B17A6C(EnTakaraMan* this, GlobalContext* globalCtx); +void func_80B17AC4(EnTakaraMan* this, GlobalContext* globalCtx); + +const ActorInit En_Takara_Man_InitVars = { + ACTOR_EN_TAKARA_MAN, + ACTORCAT_NPC, + FLAGS, + OBJECT_TS, + sizeof(EnTakaraMan), + (ActorFunc)EnTakaraMan_Init, + NULL, + (ActorFunc)EnTakaraMan_Update, + (ActorFunc)EnTakaraMan_Draw, + (ActorResetFunc)EnTakaraMan_Reset, +}; + +static u8 sTakaraIsInitialized = false; + +void EnTakaraMan_Reset(Actor* thisx, GlobalContext* globalCtx) { + sTakaraIsInitialized = false; +} + +void EnTakaraMan_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTakaraMan* this = (EnTakaraMan*)thisx; + + if (sTakaraIsInitialized) { + Actor_Kill(&this->actor); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ もういてる原 ☆☆☆☆☆ \n" VT_RST); // "Already initialized" + return; + } + + sTakaraIsInitialized = true; + osSyncPrintf("\n\n"); + // "Bun! %x" (needs a better translation) + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ ばぅん! ☆☆☆☆☆ %x\n" VT_RST, globalCtx->actorCtx.flags.chest); + globalCtx->actorCtx.flags.chest = 0; + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] = -1; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ts_Skel_004FE0, &object_ts_Anim_000498, this->jointTable, + this->morphTable, 10); + thisx->focus.pos = thisx->world.pos; + this->pos = thisx->world.pos; + thisx->world.pos.x = 133.0f; + thisx->world.pos.y = -12.0f; + thisx->world.pos.z = 102.0f; + Actor_SetScale(&this->actor, 0.013f); + this->height = 90.0f; + this->originalRoomNum = thisx->room; + thisx->room = -1; + thisx->world.rot.y = thisx->shape.rot.y = -0x4E20; + thisx->targetMode = 1; + this->actionFunc = func_80B176E0; +} + +void func_80B176E0(EnTakaraMan* this, GlobalContext* globalCtx) { + f32 frameCount = Animation_GetLastFrame(&object_ts_Anim_000498); + + Animation_Change(&this->skelAnime, &object_ts_Anim_000498, 1.0f, 0.0f, (s16)frameCount, ANIMMODE_LOOP, -10.0f); + if (!this->unk_214) { + this->actor.textId = 0x6D; + this->dialogState = TEXT_STATE_CHOICE; + } + this->actionFunc = func_80B1778C; +} + +void func_80B1778C(EnTakaraMan* this, GlobalContext* globalCtx) { + s16 absYawDiff; + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + if (Actor_ProcessTalkRequest(&this->actor, globalCtx) && this->dialogState != TEXT_STATE_DONE) { + if (!this->unk_214) { + this->actionFunc = func_80B17934; + } else { + this->actionFunc = func_80B17B14; + } + } else { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (globalCtx->roomCtx.curRoom.num == 6 && !this->unk_21A) { + this->actor.textId = 0x6E; + this->unk_21A = 1; + this->dialogState = TEXT_STATE_DONE; + } + + if (!this->unk_21A && this->unk_214) { + if (Flags_GetSwitch(globalCtx, 0x32)) { + this->actor.textId = 0x84; + this->dialogState = TEXT_STATE_EVENT; + } else { + this->actor.textId = 0x704C; + this->dialogState = TEXT_STATE_DONE; + } + } + + absYawDiff = ABS(yawDiff); + if (absYawDiff < 0x4300) { + if (globalCtx->roomCtx.curRoom.num != this->originalRoomNum) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_218 = 0; + } else { + if (!this->unk_218) { + this->actor.flags |= ACTOR_FLAG_0; + this->unk_218 = 1; + } + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + } + } +} + +void func_80B17934(EnTakaraMan* this, GlobalContext* globalCtx) { + if (this->dialogState == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + switch (globalCtx->msgCtx.choiceIndex) { + case 0: // Yes + if (gSaveContext.rupees >= 10) { + Message_CloseTextbox(globalCtx); + Rupees_ChangeBy(-10); + this->unk_214 = 1; + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, GI_DOOR_KEY, 2000.0f, 1000.0f); + this->actionFunc = func_80B17A6C; + } else { + Message_CloseTextbox(globalCtx); + this->actor.textId = 0x85; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + this->actionFunc = func_80B17B14; + } + break; + case 1: // No + Message_CloseTextbox(globalCtx); + this->actor.textId = 0x2D; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->dialogState = TEXT_STATE_EVENT; + this->actionFunc = func_80B17B14; + break; + } + } +} + +void func_80B17A6C(EnTakaraMan* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actionFunc = func_80B17AC4; + } else { + func_8002F434(&this->actor, globalCtx, GI_DOOR_KEY, 2000.0f, 1000.0f); + } +} + +void func_80B17AC4(EnTakaraMan* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) { + this->actionFunc = func_80B176E0; + } +} + +void func_80B17B14(EnTakaraMan* this, GlobalContext* globalCtx) { + if (this->dialogState == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->actionFunc = func_80B176E0; + } +} + +void EnTakaraMan_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTakaraMan* this = (EnTakaraMan*)thisx; + + if (this->eyeTimer != 0) { + this->eyeTimer--; + } + + Actor_SetFocus(&this->actor, this->height); + func_80038290(globalCtx, &this->actor, &this->unk_22C, &this->unk_232, this->actor.focus.pos); + if (this->eyeTimer == 0) { + this->eyeTextureIdx++; + if (this->eyeTextureIdx >= 2) { + this->eyeTextureIdx = 0; + this->eyeTimer = (s16)Rand_ZeroFloat(60.0f) + 20; + } + } + this->unk_212++; + this->actionFunc(this, globalCtx); +} + +s32 EnTakaraMan_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnTakaraMan* this = (EnTakaraMan*)thisx; + + if (limbIndex == 1) { + rot->x += this->unk_232.y; + } + if (limbIndex == 8) { + rot->x += this->unk_22C.y; + rot->z += this->unk_22C.z; + } + return false; +} + +void EnTakaraMan_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* eyeTextures[] = { + object_ts_Tex_000970, + object_ts_Tex_000D70, + }; + EnTakaraMan* this = (EnTakaraMan*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_takara_man.c", 528); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTextureIdx])); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnTakaraMan_OverrideLimbDraw, NULL, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_takara_man.c", 544); +} diff --git a/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.h b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.h new file mode 100644 index 000000000..dc48850e8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Takara_Man/z_en_takara_man.h @@ -0,0 +1,31 @@ +#ifndef Z_EN_TAKARA_MAN_H +#define Z_EN_TAKARA_MAN_H + +#include "ultra64.h" +#include "global.h" + +struct EnTakaraMan; + +typedef void (*EnTakaraManActionFunc)(struct EnTakaraMan*, GlobalContext*); + +typedef struct EnTakaraMan { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnTakaraManActionFunc actionFunc; + /* 0x0150 */ SkelAnime skelAnime; + /* 0x0194 */ Vec3s jointTable[10]; + /* 0x01D0 */ Vec3s morphTable[10]; + /* 0x020C */ s16 dialogState; + /* 0x020E */ s16 eyeTextureIdx; + /* 0x0210 */ s16 eyeTimer; + /* 0x0212 */ s16 unk_212; + /* 0x0214 */ s16 unk_214; + /* 0x0216 */ s16 originalRoomNum; + /* 0x0218 */ s16 unk_218; + /* 0x021A */ s16 unk_21A; + /* 0x022C */ f32 height; + /* 0x0220 */ Vec3f pos; + /* 0x022C */ Vec3s unk_22C; + /* 0x0232 */ Vec3s unk_232; +} EnTakaraMan; // size = 0x0238 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tana/z_en_tana.c b/soh/src/overlays/actors/ovl_En_Tana/z_en_tana.c new file mode 100644 index 000000000..5b8c660a9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tana/z_en_tana.c @@ -0,0 +1,98 @@ +/* + * File: z_en_tana.c + * Overlay: ovl_En_Tana + * Description: Shop Shelves + */ + +#include "z_en_tana.h" +#include "objects/object_shop_dungen/object_shop_dungen.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnTana_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTana_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTana_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTana_DrawWoodenShelves(Actor* thisx, GlobalContext* globalCtx); +void EnTana_DrawStoneShelves(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Tana_InitVars = { + ACTOR_EN_TANA, + ACTORCAT_PROP, + FLAGS, + OBJECT_SHOP_DUNGEN, + sizeof(EnTana), + (ActorFunc)EnTana_Init, + (ActorFunc)EnTana_Destroy, + (ActorFunc)EnTana_Update, + NULL, + NULL, +}; + +//! @bug A third entry is missing here. When printing the string indexed by `params` for type 2, the +//! next data entry will be dereferenced and print garbage, stopping any future printing. +//! In a non-matching context, this can cause a crash if the next item isn't a valid pointer. +static const char* sShelfTypes[] = { + "木の棚", // "Wooden Shelves" + "石の棚", // "Stone Shelves" + "石の棚", // "Stone Shelves" +}; + +static const ActorFunc sDrawFuncs[] = { + EnTana_DrawWoodenShelves, + EnTana_DrawStoneShelves, + EnTana_DrawStoneShelves, +}; + +static Gfx* sShelfDLists[] = { + gShopDungenWoodenShelvesDL, + gShopDungenStoneShelvesDL, + gShopDungenStoneShelvesDL, +}; + +static void* sStoneTextures[] = { + NULL, + gShopDungenStone1Tex, + gShopDungenStone2Tex, +}; + +void EnTana_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTana* this = (EnTana*)thisx; + + osSyncPrintf("☆☆☆ %s ☆☆☆\n", sShelfTypes[thisx->params]); + Actor_SetScale(thisx, 1.0f); + thisx->flags &= ~ACTOR_FLAG_0; + thisx->draw = sDrawFuncs[thisx->params]; +} + +void EnTana_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnTana_Update(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnTana_DrawWoodenShelves(Actor* thisx, GlobalContext* globalCtx) { + EnTana* this = (EnTana*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tana.c", 148); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_tana.c", 152), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sShelfDLists[thisx->params]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tana.c", 157); +} + +void EnTana_DrawStoneShelves(Actor* thisx, GlobalContext* globalCtx) { + EnTana* this = (EnTana*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tana.c", 163); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sStoneTextures[thisx->params])); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_tana.c", 169), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, sShelfDLists[thisx->params]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tana.c", 174); +} diff --git a/soh/src/overlays/actors/ovl_En_Tana/z_en_tana.h b/soh/src/overlays/actors/ovl_En_Tana/z_en_tana.h new file mode 100644 index 000000000..763d1303c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tana/z_en_tana.h @@ -0,0 +1,13 @@ +#ifndef Z_EN_TANA_H +#define Z_EN_TANA_H + +#include "ultra64.h" +#include "global.h" + +struct EnTana; + +typedef struct EnTana { + /* 0x0000 */ Actor actor; +} EnTana; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.c b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c new file mode 100644 index 000000000..3d6026b25 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.c @@ -0,0 +1,2057 @@ +/* + * File: z_en_test.c + * Overlay: ovl_En_Test + * Description: Stalfos + */ + +#include "z_en_test.h" +#include "objects/object_sk2/object_sk2.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnTest_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTest_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTest_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTest_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnTest_SetupWaitGround(EnTest* this); +void EnTest_SetupWaitAbove(EnTest* this); +void EnTest_SetupJumpBack(EnTest* this); +void EnTest_SetupSlashDownEnd(EnTest* this); +void EnTest_SetupSlashUp(EnTest* this); +void EnTest_SetupJumpslash(EnTest* this); +void EnTest_SetupWalkAndBlock(EnTest* this); +void func_80860EC0(EnTest* this); +void EnTest_SetupSlashDown(EnTest* this); +void func_80860BDC(EnTest* this); +void EnTest_SetupIdleFromBlock(EnTest* this); +void EnTest_SetupRecoil(EnTest* this); +void func_80862398(EnTest* this); +void func_80862154(EnTest* this); +void EnTest_SetupStopAndBlock(EnTest* this); +void func_808627C4(EnTest* this, GlobalContext* globalCtx); + +void EnTest_WaitGround(EnTest* this, GlobalContext* globalCtx); +void EnTest_WaitAbove(EnTest* this, GlobalContext* globalCtx); +void EnTest_Fall(EnTest* this, GlobalContext* globalCtx); +void EnTest_Land(EnTest* this, GlobalContext* globalCtx); +void EnTest_Rise(EnTest* this, GlobalContext* globalCtx); +void EnTest_Idle(EnTest* this, GlobalContext* globalCtx); +void EnTest_WalkAndBlock(EnTest* this, GlobalContext* globalCtx); +void func_80860C24(EnTest* this, GlobalContext* globalCtx); +void func_80860F84(EnTest* this, GlobalContext* globalCtx); +void EnTest_SlashDown(EnTest* this, GlobalContext* globalCtx); +void EnTest_SlashDownEnd(EnTest* this, GlobalContext* globalCtx); +void EnTest_SlashUp(EnTest* this, GlobalContext* globalCtx); +void EnTest_JumpBack(EnTest* this, GlobalContext* globalCtx); +void EnTest_Jumpslash(EnTest* this, GlobalContext* globalCtx); +void EnTest_JumpUp(EnTest* this, GlobalContext* globalCtx); +void EnTest_StopAndBlock(EnTest* this, GlobalContext* globalCtx); +void EnTest_IdleFromBlock(EnTest* this, GlobalContext* globalCtx); +void func_808621D4(EnTest* this, GlobalContext* globalCtx); +void func_80862418(EnTest* this, GlobalContext* globalCtx); +void EnTest_Stunned(EnTest* this, GlobalContext* globalCtx); +void func_808628C8(EnTest* this, GlobalContext* globalCtx); +void func_80862E6C(EnTest* this, GlobalContext* globalCtx); +void func_80863044(EnTest* this, GlobalContext* globalCtx); +void func_8086318C(EnTest* this, GlobalContext* globalCtx); +void EnTest_Recoil(EnTest* this, GlobalContext* globalCtx); +void func_808633E8(EnTest* this, GlobalContext* globalCtx); +void func_80862FA8(EnTest* this, GlobalContext* globalCtx); + +s32 EnTest_ReactToProjectile(GlobalContext* globalCtx, EnTest* this); + +static u8 sJointCopyFlags[] = { + false, // STALFOS_LIMB_NONE + false, // STALFOS_LIMB_ROOT + false, // STALFOS_LIMB_UPPERBODY_ROOT + false, // STALFOS_LIMB_CORE_LOWER_ROOT + true, // STALFOS_LIMB_CORE_UPPER_ROOT + true, // STALFOS_LIMB_NECK_ROOT + true, // STALFOS_LIMB_HEAD_ROOT + true, // STALFOS_LIMB_7 + true, // STALFOS_LIMB_8 + true, // STALFOS_LIMB_JAW_ROOT + true, // STALFOS_LIMB_JAW + true, // STALFOS_LIMB_HEAD + true, // STALFOS_LIMB_NECK_UPPER + true, // STALFOS_LIMB_NECK_LOWER + true, // STALFOS_LIMB_CORE_UPPER + true, // STALFOS_LIMB_CHEST + true, // STALFOS_LIMB_SHOULDER_R_ROOT + true, // STALFOS_LIMB_SHOULDER_ARMOR_R_ROOT + true, // STALFOS_LIMB_SHOULDER_ARMOR_R + true, // STALFOS_LIMB_SHOULDER_L_ROOT + true, // STALFOS_LIMB_SHOULDER_ARMOR_L_ROOT + true, // STALFOS_LIMB_SHOULDER_ARMOR_L + true, // STALFOS_LIMB_ARM_L_ROOT + true, // STALFOS_LIMB_UPPERARM_L_ROOT + true, // STALFOS_LIMB_FOREARM_L_ROOT + true, // STALFOS_LIMB_HAND_L_ROOT + true, // STALFOS_LIMB_HAND_L + true, // STALFOS_LIMB_SHIELD + true, // STALFOS_LIMB_FOREARM_L + true, // STALFOS_LIMB_UPPERARM_L + true, // STALFOS_LIMB_ARM_R_ROOT + true, // STALFOS_LIMB_UPPERARM_R_ROOT + true, // STALFOS_LIMB_FOREARM_R_ROOT + true, // STALFOS_LIMB_HAND_R_ROOT + true, // STALFOS_LIMB_SWORD + true, // STALFOS_LIMB_HAND_R + true, // STALFOS_LIMB_FOREARM_R + true, // STALFOS_LIMB_UPPERARM_R + true, // STALFOS_LIMB_CORE_LOWER + false, // STALFOS_LIMB_LOWERBODY_ROOT + false, // STALFOS_LIMB_WAIST_ROOT + false, // STALFOS_LIMB_LEGS_ROOT + false, // STALFOS_LIMB_LEG_L_ROOT + false, // STALFOS_LIMB_THIGH_L_ROOT + false, // STALFOS_LIMB_LOWERLEG_L_ROOT + false, // STALFOS_LIMB_ANKLE_L_ROOT + false, // STALFOS_LIMB_ANKLE_L + false, // STALFOS_LIMB_FOOT_L_ROOT + false, // STALFOS_LIMB_FOOT_L + false, // STALFOS_LIMB_LOWERLEG_L + false, // STALFOS_LIMB_THIGH_L + false, // STALFOS_LIMB_LEG_R_ROOT + false, // STALFOS_LIMB_THIGH_R_ROOT + false, // STALFOS_LIMB_LOWERLEG_R_ROOT + false, // STALFOS_LIMB_ANKLE_R_ROOT + false, // STALFOS_LIMB_ANKLE_R + false, // STALFOS_LIMB_FOOT_R_ROOT + false, // STALFOS_LIMB_FOOT_R + false, // STALFOS_LIMB_LOWERLEG_R + false, // STALFOS_LIMB_THIGH_R + false, // STALFOS_LIMB_WAIST +}; + +const ActorInit En_Test_InitVars = { + ACTOR_EN_TEST, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_SK2, + sizeof(EnTest), + (ActorFunc)EnTest_Init, + (ActorFunc)EnTest_Destroy, + (ActorFunc)EnTest_Update, + (ActorFunc)EnTest_Draw, + NULL, +}; + +static ColliderCylinderInit sBodyColliderInit = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 25, 65, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sShieldColliderInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC1FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 20, 70, -50, { 0, 0, 0 } }, +}; + +static ColliderQuadInit sSwordColliderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +typedef enum { + /* 0x0 */ STALFOS_DMGEFF_NORMAL, + /* 0x1 */ STALFOS_DMGEFF_STUN, + /* 0x6 */ STALFOS_DMGEFF_FIREMAGIC = 6, + /* 0xD */ STALFOS_DMGEFF_SLING = 0xD, + /* 0xE */ STALFOS_DMGEFF_LIGHT, + /* 0xF */ STALFOS_DMGEFF_FREEZE +} StalfosDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, STALFOS_DMGEFF_STUN), + /* Deku stick */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Slingshot */ DMG_ENTRY(1, STALFOS_DMGEFF_SLING), + /* Explosive */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Boomerang */ DMG_ENTRY(0, STALFOS_DMGEFF_STUN), + /* Normal arrow */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Hammer swing */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Hookshot */ DMG_ENTRY(0, STALFOS_DMGEFF_STUN), + /* Kokiri sword */ DMG_ENTRY(1, STALFOS_DMGEFF_NORMAL), + /* Master sword */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Giant's Knife */ DMG_ENTRY(4, STALFOS_DMGEFF_NORMAL), + /* Fire arrow */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Ice arrow */ DMG_ENTRY(4, STALFOS_DMGEFF_FREEZE), + /* Light arrow */ DMG_ENTRY(2, STALFOS_DMGEFF_LIGHT), + /* Unk arrow 1 */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Unk arrow 2 */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Unk arrow 3 */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Fire magic */ DMG_ENTRY(0, STALFOS_DMGEFF_FIREMAGIC), + /* Ice magic */ DMG_ENTRY(3, STALFOS_DMGEFF_FREEZE), + /* Light magic */ DMG_ENTRY(0, STALFOS_DMGEFF_LIGHT), + /* Shield */ DMG_ENTRY(0, STALFOS_DMGEFF_NORMAL), + /* Mirror Ray */ DMG_ENTRY(0, STALFOS_DMGEFF_NORMAL), + /* Kokiri spin */ DMG_ENTRY(1, STALFOS_DMGEFF_NORMAL), + /* Giant spin */ DMG_ENTRY(4, STALFOS_DMGEFF_NORMAL), + /* Master spin */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Kokiri jump */ DMG_ENTRY(2, STALFOS_DMGEFF_NORMAL), + /* Giant jump */ DMG_ENTRY(8, STALFOS_DMGEFF_NORMAL), + /* Master jump */ DMG_ENTRY(4, STALFOS_DMGEFF_NORMAL), + /* Unknown 1 */ DMG_ENTRY(0, STALFOS_DMGEFF_NORMAL), + /* Unblockable */ DMG_ENTRY(0, STALFOS_DMGEFF_NORMAL), + /* Hammer jump */ DMG_ENTRY(4, STALFOS_DMGEFF_NORMAL), + /* Unknown 2 */ DMG_ENTRY(0, STALFOS_DMGEFF_NORMAL), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x1B, ICHAIN_CONTINUE), ICHAIN_F32(targetArrowOffset, 500, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 15, ICHAIN_CONTINUE), ICHAIN_F32(scale.y, 0, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -1500, ICHAIN_STOP), +}; + +void EnTest_SetupAction(EnTest* this, EnTestActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnTest_Init(Actor* thisx, GlobalContext* globalCtx) { + EffectBlureInit1 slashBlure; + EnTest* this = (EnTest*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + + SkelAnime_Init(globalCtx, &this->skelAnime, &gStalfosSkel, &gStalfosMiddleGuardAnim, this->jointTable, + this->morphTable, STALFOS_LIMB_MAX); + SkelAnime_Init(globalCtx, &this->upperSkelanime, &gStalfosSkel, &gStalfosMiddleGuardAnim, this->upperJointTable, + this->upperMorphTable, STALFOS_LIMB_MAX); + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawFeet, 90.0f); + + this->actor.colChkInfo.cylRadius = 40; + this->actor.colChkInfo.cylHeight = 100; + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 45.0f; + this->actor.colChkInfo.damageTable = &sDamageTable; + + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, &this->actor, &sBodyColliderInit); + + Collider_InitCylinder(globalCtx, &this->shieldCollider); + Collider_SetCylinder(globalCtx, &this->shieldCollider, &this->actor, &sShieldColliderInit); + + Collider_InitQuad(globalCtx, &this->swordCollider); + Collider_SetQuad(globalCtx, &this->swordCollider, &this->actor, &sSwordColliderInit); + + this->actor.colChkInfo.mass = MASS_HEAVY; + this->actor.colChkInfo.health = 10; + + slashBlure.p1StartColor[0] = slashBlure.p1StartColor[1] = slashBlure.p1StartColor[2] = slashBlure.p1StartColor[3] = + slashBlure.p2StartColor[0] = slashBlure.p2StartColor[1] = slashBlure.p2StartColor[2] = + slashBlure.p1EndColor[0] = slashBlure.p1EndColor[1] = slashBlure.p1EndColor[2] = slashBlure.p2EndColor[0] = + slashBlure.p2EndColor[1] = slashBlure.p2EndColor[2] = 255; + + slashBlure.p1EndColor[3] = 0; + slashBlure.p2EndColor[3] = 0; + slashBlure.p2StartColor[3] = 64; + + slashBlure.elemDuration = 4; + slashBlure.unkFlag = 0; + slashBlure.calcMode = 2; + + Effect_Add(globalCtx, &this->effectIndex, EFFECT_BLURE1, 0, 0, &slashBlure); + + if (this->actor.params != STALFOS_TYPE_CEILING) { + EnTest_SetupWaitGround(this); + } else { + EnTest_SetupWaitAbove(this); + } + + if (this->actor.params == STALFOS_TYPE_INVISIBLE) { + this->actor.flags |= ACTOR_FLAG_7; + } +} + +void EnTest_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTest* this = (EnTest*)thisx; + + if ((this->actor.params != STALFOS_TYPE_2) && + !Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f)) { + func_800F5B58(); + } + + Effect_Delete(globalCtx, this->effectIndex); + Collider_DestroyCylinder(globalCtx, &this->shieldCollider); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyQuad(globalCtx, &this->swordCollider); +} + +/** + * If EnTest_ChooseAction failed to pick a new action, this function will unconditionally pick + * a new action as a last resort + */ +void EnTest_ChooseRandomAction(EnTest* this, GlobalContext* globalCtx) { + switch ((u32)(Rand_ZeroOne() * 10.0f)) { + case 0: + case 1: + case 5: + case 6: + if ((this->actor.xzDistToPlayer < 220.0f) && (this->actor.xzDistToPlayer > 170.0f) && + Actor_IsFacingPlayer(&this->actor, 0x71C) && Actor_IsTargeted(globalCtx, &this->actor)) { + EnTest_SetupJumpslash(this); + break; + } + // fallthrough + case 8: + EnTest_SetupWalkAndBlock(this); + break; + + case 3: + case 4: + case 7: + func_808627C4(this, globalCtx); + break; + + case 2: + case 9: + case 10: + EnTest_SetupStopAndBlock(this); + break; + } +} + +void EnTest_ChooseAction(EnTest* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + + yawDiff = ABS(yawDiff); + + if (yawDiff >= 0x61A8) { + switch ((u32)(Rand_ZeroOne() * 10.0f)) { + case 0: + case 3: + case 7: + EnTest_SetupStopAndBlock(this); + break; + + case 1: + case 5: + case 6: + case 8: + func_808627C4(this, globalCtx); + break; + + case 2: + case 4: + case 9: + if (this->actor.params != STALFOS_TYPE_CEILING) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnTest_SetupJumpBack(this); + } + break; + } + } else if (yawDiff <= 0x3E80) { + if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) > 0x3E80) { + if (((globalCtx->gameplayFrames % 2) != 0) && (this->actor.params != STALFOS_TYPE_CEILING)) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnTest_SetupJumpBack(this); + } else if ((this->actor.xzDistToPlayer < 220.0f) && (this->actor.xzDistToPlayer > 170.0f)) { + if (Actor_IsFacingPlayer(&this->actor, 0x71C) && !Actor_IsTargeted(globalCtx, &this->actor)) { + EnTest_SetupJumpslash(this); + } + } else { + EnTest_SetupWalkAndBlock(this); + } + } else { + if (this->actor.xzDistToPlayer < 110.0f) { + if (Rand_ZeroOne() > 0.2f) { + if (player->stateFlags1 & 0x10) { + if (this->actor.isTargeted) { + EnTest_SetupSlashDown(this); + } else { + func_808627C4(this, globalCtx); + } + } else { + EnTest_SetupSlashDown(this); + } + } + } else { + EnTest_ChooseRandomAction(this, globalCtx); + } + } + } else { + EnTest_ChooseRandomAction(this, globalCtx); + } +} + +void EnTest_SetupWaitGround(EnTest* this) { + Animation_PlayLoop(&this->skelAnime, &gStalfosMiddleGuardAnim); + this->unk_7C8 = 0; + this->timer = 15; + this->actor.scale.y = 0.0f; + this->actor.world.pos.y = this->actor.home.pos.y - 3.5f; + this->actor.flags &= ~ACTOR_FLAG_0; + EnTest_SetupAction(this, EnTest_WaitGround); +} + +void EnTest_WaitGround(EnTest* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if ((this->timer == 0) && (ABS(this->actor.yDistToPlayer) < 150.0f)) { + this->unk_7C8 = 3; + EnTest_SetupAction(this, EnTest_Rise); + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + + if (this->actor.params != STALFOS_TYPE_2) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + } else { + if (this->timer != 0) { + this->timer--; + } + + this->actor.world.pos.y = this->actor.home.pos.y - 3.5f; + } +} + +void EnTest_SetupWaitAbove(EnTest* this) { + Animation_PlayLoop(&this->skelAnime, &gStalfosMiddleGuardAnim); + this->unk_7C8 = 0; + this->actor.world.pos.y = this->actor.home.pos.y + 150.0f; + Actor_SetScale(&this->actor, 0.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + EnTest_SetupAction(this, EnTest_WaitAbove); +} + +void EnTest_WaitAbove(EnTest* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + this->actor.world.pos.y = this->actor.home.pos.y + 150.0f; + + if ((this->actor.xzDistToPlayer < 200.0f) && (ABS(this->actor.yDistToPlayer) < 450.0f)) { + EnTest_SetupAction(this, EnTest_Fall); + this->actor.flags |= ACTOR_FLAG_0; + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Actor_SetScale(&this->actor, 0.015f); + } +} + +void EnTest_SetupIdle(EnTest* this) { + Animation_PlayLoop(&this->skelAnime, &gStalfosMiddleGuardAnim); + this->unk_7C8 = 0xA; + this->timer = (Rand_ZeroOne() * 10.0f) + 5.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnTest_SetupAction(this, EnTest_Idle); +} + +void EnTest_Idle(EnTest* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff; + + SkelAnime_Update(&this->skelAnime); + + if (!EnTest_ReactToProjectile(globalCtx, this)) { + yawDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if (this->actor.xzDistToPlayer < 100.0f) { + if ((player->swordState != 0) && (ABS(yawDiff) >= 0x1F40)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (Rand_ZeroOne() > 0.7f && player->swordAnimation != 0x11) { + EnTest_SetupJumpBack(this); + } else { + func_808627C4(this, globalCtx); + } + return; + } + } + + if (this->timer != 0) { + this->timer--; + } else { + if (Actor_IsFacingPlayer(&this->actor, 0x1555)) { + if ((this->actor.xzDistToPlayer < 220.0f) && (this->actor.xzDistToPlayer > 160.0f) && + (Rand_ZeroOne() < 0.3f)) { + if (Actor_IsTargeted(globalCtx, &this->actor)) { + EnTest_SetupJumpslash(this); + } else { + func_808627C4(this, globalCtx); + } + } else { + if (Rand_ZeroOne() > 0.3f) { + EnTest_SetupWalkAndBlock(this); + } else { + func_808627C4(this, globalCtx); + } + } + } else { + if (Rand_ZeroOne() > 0.7f) { + func_80860BDC(this); + } else { + EnTest_ChooseAction(this, globalCtx); + } + } + } + } +} + +void EnTest_Fall(EnTest* this, GlobalContext* globalCtx) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gStalfosLandFromLeapAnim, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->actor.world.pos.y <= this->actor.floorHeight) { + this->skelAnime.playSpeed = 1.0f; + this->unk_7C8 = 0xC; + this->timer = this->unk_7E4 * 0.15f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + EnTest_SetupAction(this, EnTest_Land); + } +} + +void EnTest_Land(EnTest* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnTest_SetupIdle(this); + this->timer = (Rand_ZeroOne() * 10.0f) + 5.0f; + } +} + +void EnTest_SetupWalkAndBlock(EnTest* this) { + Animation_Change(&this->upperSkelanime, &gStalfosBlockWithShieldAnim, 2.0f, 0.0f, + Animation_GetLastFrame(&gStalfosBlockWithShieldAnim), 2, 2.0f); + Animation_PlayLoop(&this->skelAnime, &gStalfosSlowAdvanceAnim); + this->timer = (s16)(Rand_ZeroOne() * 5.0f); + this->unk_7C8 = 0xD; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnTest_SetupAction(this, EnTest_WalkAndBlock); +} + +void EnTest_WalkAndBlock(EnTest* this, GlobalContext* globalCtx) { + s32 pad; + f32 checkDist = 0.0f; + s32 pad1; + s32 prevFrame; + s32 temp_f16; + f32 playSpeed; + Player* player = GET_PLAYER(globalCtx); + s32 absPlaySpeed; + s16 yawDiff; + + if (!EnTest_ReactToProjectile(globalCtx, this)) { + this->timer++; + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + checkDist = 150.0f; + } + + if (this->actor.xzDistToPlayer <= (80.0f + checkDist)) { + Math_SmoothStepToF(&this->actor.speedXZ, -5.0f, 1.0f, 0.8f, 0.0f); + } else if (this->actor.xzDistToPlayer > (110.0f + checkDist)) { + Math_SmoothStepToF(&this->actor.speedXZ, 5.0f, 1.0f, 0.8f, 0.0f); + } + + if (this->actor.speedXZ >= 5.0f) { + this->actor.speedXZ = 5.0f; + } else if (this->actor.speedXZ < -5.0f) { + this->actor.speedXZ = -5.0f; + } + + if ((this->actor.params == STALFOS_TYPE_CEILING) && + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.world.rot.y)) { + this->actor.speedXZ *= -1.0f; + } + + if (ABS(this->actor.speedXZ) < 3.0f) { + Animation_Change(&this->skelAnime, &gStalfosSlowAdvanceAnim, 0.0f, this->skelAnime.curFrame, + Animation_GetLastFrame(&gStalfosSlowAdvanceAnim), 0, -6.0f); + playSpeed = this->actor.speedXZ * 10.0f; + } else { + Animation_Change(&this->skelAnime, &gStalfosFastAdvanceAnim, 0.0f, this->skelAnime.curFrame, + Animation_GetLastFrame(&gStalfosFastAdvanceAnim), 0, -4.0f); + playSpeed = this->actor.speedXZ * 10.0f * 0.02f; + } + + if (this->actor.speedXZ >= 0.0f) { + if (this->unk_7DE == 0) { + this->unk_7DE++; + } + + playSpeed = CLAMP_MAX(playSpeed, 2.5f); + this->skelAnime.playSpeed = playSpeed; + } else { + playSpeed = CLAMP_MIN(playSpeed, -2.5f); + this->skelAnime.playSpeed = playSpeed; + } + + yawDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if ((this->actor.xzDistToPlayer < 100.0f) && (player->swordState != 0)) { + if (ABS(yawDiff) >= 0x1F40) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if ((Rand_ZeroOne() > 0.7f) && (player->swordAnimation != 0x11)) { + EnTest_SetupJumpBack(this); + } else { + EnTest_SetupStopAndBlock(this); + } + + return; + } + } + + prevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + + temp_f16 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + absPlaySpeed = (f32)ABS(this->skelAnime.playSpeed); + + if ((s32)this->skelAnime.curFrame != prevFrame) { + s32 temp_v0_2 = absPlaySpeed + prevFrame; + + if (((temp_v0_2 > 1) && (temp_f16 <= 0)) || ((temp_f16 < 7) && (temp_v0_2 >= 8))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WALK); + } + } + + if ((this->timer % 32) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WARAU); + this->timer += (s16)(Rand_ZeroOne() * 5.0f); + } + + if ((this->actor.xzDistToPlayer < 220.0f) && (this->actor.xzDistToPlayer > 160.0f) && + (Actor_IsFacingPlayer(&this->actor, 0x71C))) { + if (Actor_IsTargeted(globalCtx, &this->actor)) { + if (Rand_ZeroOne() < 0.1f) { + EnTest_SetupJumpslash(this); + return; + } + } else if (player->heldItemActionParam != PLAYER_AP_NONE) { + if (this->actor.isTargeted) { + if ((globalCtx->gameplayFrames % 2) != 0) { + func_808627C4(this, globalCtx); + return; + } + + EnTest_ChooseAction(this, globalCtx); + } else { + func_80860EC0(this); + } + } + } + + if (Rand_ZeroOne() < 0.4f) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + } + + if (!Actor_IsFacingPlayer(&this->actor, 0x11C7)) { + EnTest_SetupIdle(this); + this->timer = (Rand_ZeroOne() * 10.0f) + 10.0f; + return; + } + + if (this->actor.xzDistToPlayer < 110.0f) { + if (Rand_ZeroOne() > 0.2f) { + if (player->stateFlags1 & 0x10) { + if (this->actor.isTargeted) { + EnTest_SetupSlashDown(this); + } else { + func_808627C4(this, globalCtx); + } + } else { + EnTest_SetupSlashDown(this); + } + } else { + EnTest_SetupStopAndBlock(this); + } + } else if (Rand_ZeroOne() < 0.1f) { + this->actor.speedXZ = 5.0f; + } + } +} + +// a variation of sidestep +void func_80860BDC(EnTest* this) { + Animation_PlayLoop(&this->skelAnime, &gStalfosSidestepAnim); + this->unk_7C8 = 0xE; + EnTest_SetupAction(this, func_80860C24); +} + +// a variation of sidestep +void func_80860C24(EnTest* this, GlobalContext* globalCtx) { + s16 yawDiff; + s16 yawChange; + f32 playSpeed; + s32 prevFrame; + s32 temp1; + s32 temp2; + s32 absPlaySpeed; + + if (!EnTest_ReactToProjectile(globalCtx, this)) { + yawDiff = this->actor.yawTowardsPlayer; + yawDiff -= this->actor.shape.rot.y; + + if (yawDiff > 0) { + yawChange = (yawDiff / 42.0f) + 300.0f; + this->actor.shape.rot.y += yawChange * 2; + } else { + yawChange = (yawDiff / 42.0f) - 300.0f; + this->actor.shape.rot.y += yawChange * 2; + } + + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (yawDiff > 0) { + playSpeed = yawChange * 0.02f; + playSpeed = CLAMP_MAX(playSpeed, 1.0f); + this->skelAnime.playSpeed = playSpeed; + } else { + playSpeed = yawChange * 0.02f; + playSpeed = CLAMP_MIN(playSpeed, -1.0f); + this->skelAnime.playSpeed = playSpeed; + } + + prevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + temp1 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + absPlaySpeed = (f32)ABS(this->skelAnime.playSpeed); + + if ((s32)this->skelAnime.curFrame != prevFrame) { + temp2 = absPlaySpeed + prevFrame; + + if (((temp2 > 2) && (temp1 <= 0)) || ((temp1 < 7) && (temp2 >= 9))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WALK); + } + } + + if (Actor_IsFacingPlayer(&this->actor, 0x71C)) { + if (Rand_ZeroOne() > 0.8f) { + if ((Rand_ZeroOne() > 0.7f)) { + func_80860EC0(this); + } else { + EnTest_ChooseAction(this, globalCtx); + } + } else { + EnTest_SetupWalkAndBlock(this); + } + } + } +} + +// a variation of sidestep +void func_80860EC0(EnTest* this) { + Animation_PlayLoop(&this->skelAnime, &gStalfosSidestepAnim); + this->unk_7C8 = 0xF; + this->actor.speedXZ = (Rand_ZeroOne() > 0.5f) ? -0.5f : 0.5f; + this->timer = (s16)((Rand_ZeroOne() * 15.0f) + 25.0f); + this->unk_7EC = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnTest_SetupAction(this, func_80860F84); +} + +// a variation of sidestep +void func_80860F84(EnTest* this, GlobalContext* globalCtx) { + s16 playerYaw180; + s32 pad; + s32 prevFrame; + s32 temp_f16; + s16 yawDiff; + Player* player = GET_PLAYER(globalCtx); + f32 checkDist = 0.0f; + s16 newYaw; + s32 absPlaySpeed; + + if (!EnTest_ReactToProjectile(globalCtx, this)) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3E80; + playerYaw180 = player->actor.shape.rot.y + 0x8000; + + if (this->actor.speedXZ >= 0.0f) { + if (this->actor.speedXZ < 6.0f) { + this->actor.speedXZ += 0.5f; + } else { + this->actor.speedXZ = 6.0f; + } + } else { + if (this->actor.speedXZ > -6.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ = -6.0f; + } + } + + if ((this->actor.bgCheckFlags & 8) || + ((this->actor.params == STALFOS_TYPE_CEILING) && + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.world.rot.y))) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + newYaw = this->actor.shape.rot.y + 0x3FFF; + } else { + newYaw = this->actor.shape.rot.y - 0x3FFF; + } + + newYaw = this->actor.wallYaw - newYaw; + } else { + this->actor.speedXZ *= -0.8f; + newYaw = 0; + } + + if (ABS(newYaw) > 0x4000) { + this->actor.speedXZ *= -0.8f; + + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ += 0.5f; + } + } + } + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + checkDist = 200.0f; + } + + if (this->actor.xzDistToPlayer <= (80.0f + checkDist)) { + Math_SmoothStepToF(&this->unk_7EC, -2.5f, 1.0f, 0.8f, 0.0f); + } else if (this->actor.xzDistToPlayer > (110.0f + checkDist)) { + Math_SmoothStepToF(&this->unk_7EC, 2.5f, 1.0f, 0.8f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_7EC, 0.0f, 1.0f, 6.65f, 0.0f); + } + + if (this->unk_7EC != 0.0f) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->unk_7EC; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->unk_7EC; + } + + this->skelAnime.playSpeed = this->actor.speedXZ * 0.5f; + + prevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + temp_f16 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + absPlaySpeed = (f32)ABS(this->skelAnime.playSpeed); + + if ((s32)this->skelAnime.curFrame != prevFrame) { + s32 temp_v0_2 = absPlaySpeed + prevFrame; + + if (((temp_v0_2 > 1) && (temp_f16 <= 0)) || ((temp_f16 < 7) && (temp_v0_2 >= 8))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WALK); + } + } + + if ((globalCtx->gameplayFrames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WARAU); + } + + yawDiff = playerYaw180 - this->actor.shape.rot.y; + yawDiff = ABS(yawDiff); + + if ((yawDiff > 0x6800) || (this->timer == 0)) { + EnTest_ChooseAction(this, globalCtx); + } else if (this->timer != 0) { + this->timer--; + } + } +} + +void EnTest_SetupSlashDown(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosDownSlashAnim); + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_STAL_WARAU); + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + this->unk_7C8 = 0x10; + this->actor.speedXZ = 0.0f; + EnTest_SetupAction(this, EnTest_SlashDown); + this->swordCollider.info.toucher.damage = 16; + + if (this->unk_7DE != 0) { + this->unk_7DE = 3; + } +} + +void EnTest_SlashDown(EnTest* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + + if ((s32)this->skelAnime.curFrame < 4) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 0); + } + + if ((s32)this->skelAnime.curFrame == 7) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_SAKEBI); + } + + if ((this->skelAnime.curFrame > 7.0f) && (this->skelAnime.curFrame < 11.0f)) { + this->swordState = 1; + } else { + this->swordState = 0; + } + + if (SkelAnime_Update(&this->skelAnime)) { + if ((globalCtx->gameplayFrames % 2) != 0) { + EnTest_SetupSlashDownEnd(this); + } else { + EnTest_SetupSlashUp(this); + } + } +} + +void EnTest_SetupSlashDownEnd(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosRecoverFromDownSlashAnim); + this->unk_7C8 = 0x12; + this->actor.speedXZ = 0.0f; + EnTest_SetupAction(this, EnTest_SlashDownEnd); +} + +void EnTest_SlashDownEnd(EnTest* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yawDiff; + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->swordCollider.base.atFlags & AT_HIT) { + this->swordCollider.base.atFlags &= ~AT_HIT; + if (this->actor.params != STALFOS_TYPE_CEILING) { + EnTest_SetupJumpBack(this); + return; + } + } + + if (Rand_ZeroOne() > 0.7f) { + EnTest_SetupIdle(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + return; + } + + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, &player->actor); + + if (Rand_ZeroOne() > 0.7f) { + if (this->actor.params != STALFOS_TYPE_CEILING) { + EnTest_SetupJumpBack(this); + return; + } + } + + yawDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if (ABS(yawDiff) <= 0x2710) { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(yawDiff) > 0x3E80) && (this->actor.params != STALFOS_TYPE_CEILING)) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnTest_SetupJumpBack(this); + } else if (player->stateFlags1 & 0x10) { + if (this->actor.isTargeted) { + EnTest_SetupSlashDown(this); + } else if ((globalCtx->gameplayFrames % 2) != 0) { + func_808627C4(this, globalCtx); + } else { + EnTest_SetupJumpBack(this); + } + } else { + EnTest_SetupSlashDown(this); + } + } else { + func_808627C4(this, globalCtx); + } + } +} + +void EnTest_SetupSlashUp(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosUpSlashAnim); + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + this->unk_7C8 = 0x11; + this->swordCollider.info.toucher.damage = 16; + this->actor.speedXZ = 0.0f; + EnTest_SetupAction(this, EnTest_SlashUp); + + if (this->unk_7DE != 0) { + this->unk_7DE = 3; + } +} + +void EnTest_SlashUp(EnTest* this, GlobalContext* globalCtx) { + this->actor.speedXZ = 0.0f; + + if ((s32)this->skelAnime.curFrame == 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_SAKEBI); + } + + if ((this->skelAnime.curFrame > 1.0f) && (this->skelAnime.curFrame < 8.0f)) { + this->swordState = 1; + } else { + this->swordState = 0; + } + + if (SkelAnime_Update(&this->skelAnime)) { + EnTest_SetupSlashDown(this); + } +} + +void EnTest_SetupJumpBack(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosJumpBackwardsAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->unk_7C8 = 0x14; + this->timer = 5; + EnTest_SetupAction(this, EnTest_JumpBack); + + if (this->unk_7DE != 0) { + this->unk_7DE = 3; + } + + if (this->actor.params != STALFOS_TYPE_CEILING) { + this->actor.speedXZ = -11.0f; + } else { + this->actor.speedXZ = -7.0f; + } +} + +void EnTest_JumpBack(EnTest* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 1); + + if (this->timer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WARAU); + } else { + this->timer--; + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (!EnTest_ReactToProjectile(globalCtx, this)) { + if (this->actor.xzDistToPlayer <= 100.0f) { + if (Actor_IsFacingPlayer(&this->actor, 0x1555)) { + EnTest_SetupSlashDown(this); + } else { + EnTest_SetupIdle(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } + } else { + if ((this->actor.xzDistToPlayer <= 220.0f) && Actor_IsFacingPlayer(&this->actor, 0xE38)) { + EnTest_SetupJumpslash(this); + } else { + EnTest_SetupIdle(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } + } + this->actor.flags |= ACTOR_FLAG_0; + } + } else if (this->skelAnime.curFrame == (this->skelAnime.endFrame - 4.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } +} + +void EnTest_SetupJumpslash(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosJumpAnim); + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_STAL_WARAU); + this->timer = 0; + this->unk_7C8 = 0x17; + this->actor.velocity.y = 10.0f; + this->actor.speedXZ = 8.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actor.world.rot.y = this->actor.shape.rot.y; + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + EnTest_SetupAction(this, EnTest_Jumpslash); + this->swordCollider.info.toucher.damage = 32; + + if (this->unk_7DE != 0) { + this->unk_7DE = 3; + } +} + +void EnTest_Jumpslash(EnTest* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (this->timer == 0) { + Animation_PlayOnce(&this->skelAnime, &gStalfosJumpslashAnim); + this->timer = 1; + this->swordState = 1; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_SAKEBI); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + } else { + this->actor.speedXZ = 0.0f; + EnTest_SetupIdle(this); + } + } + + if ((this->timer != 0) && (this->skelAnime.curFrame >= 5.0f)) { + this->swordState = 0; + } + + if (this->actor.world.pos.y <= this->actor.floorHeight) { + if (this->actor.speedXZ != 0.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + + this->actor.world.pos.y = this->actor.floorHeight; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } +} + +void EnTest_SetupJumpUp(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosJumpAnim); + this->timer = 0; + this->unk_7C8 = 4; + this->actor.velocity.y = 14.0f; + this->actor.speedXZ = 6.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnTest_SetupAction(this, EnTest_JumpUp); +} + +void EnTest_JumpUp(EnTest* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + SkelAnime_Update(&this->skelAnime); + + if (this->actor.world.pos.y <= this->actor.floorHeight) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.world.pos.y = this->actor.floorHeight; + this->unk_7E4 = -(s32)this->actor.velocity.y; + + if (this->unk_7E4 == 0) { + this->unk_7E4 = 1; + } + + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + this->unk_7C8 = 0xC; + this->timer = 4; + Animation_Change(&this->skelAnime, &gStalfosLandFromLeapAnim, 0.0f, 0.0f, 0.0f, 2, 0.0f); + EnTest_SetupAction(this, EnTest_Land); + } +} + +void EnTest_SetupStopAndBlock(EnTest* this) { + Animation_Change(&this->skelAnime, &gStalfosBlockWithShieldAnim, 2.0f, 0.0f, + Animation_GetLastFrame(&gStalfosBlockWithShieldAnim), 2, 2.0f); + this->unk_7C8 = 0x15; + this->actor.speedXZ = 0.0f; + this->timer = (Rand_ZeroOne() * 10.0f) + 11.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->unk_7DE = 5; + EnTest_SetupAction(this, EnTest_StopAndBlock); +} + +void EnTest_StopAndBlock(EnTest* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if ((ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) > 0x3E80) && + (this->actor.params != STALFOS_TYPE_CEILING) && ((globalCtx->gameplayFrames % 2) != 0)) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnTest_SetupJumpBack(this); + } + + if (this->timer == 0) { + EnTest_SetupIdleFromBlock(this); + } else { + this->timer--; + } +} + +void EnTest_SetupIdleFromBlock(EnTest* this) { + Animation_MorphToLoop(&this->skelAnime, &gStalfosMiddleGuardAnim, -4.0f); + this->unk_7C8 = 0x16; + EnTest_SetupAction(this, EnTest_IdleFromBlock); +} + +void EnTest_IdleFromBlock(EnTest* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.5f, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->skelAnime.morphWeight == 0.0f) { + this->actor.speedXZ = 0.0f; + this->unk_7DE = 0; + + if (!EnTest_ReactToProjectile(globalCtx, this)) { + if (this->actor.xzDistToPlayer < 500.0f) { + EnTest_ChooseAction(this, globalCtx); + } else { + func_808627C4(this, globalCtx); + } + } + } +} + +void func_80862154(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosFlinchFromHitFrontAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_DAMAGE); + this->unk_7C8 = 8; + this->actor.speedXZ = -2.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + EnTest_SetupAction(this, func_808621D4); +} + +void func_808621D4(EnTest* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.1f, 0.0f); + + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.speedXZ = 0.0f; + + if ((this->actor.bgCheckFlags & 8) && ((ABS((s16)(this->actor.wallYaw - this->actor.shape.rot.y)) < 0x38A4) && + (this->actor.xzDistToPlayer < 80.0f))) { + EnTest_SetupJumpUp(this); + } else if (!EnTest_ReactToProjectile(globalCtx, this)) { + EnTest_ChooseAction(this, globalCtx); + } else { + return; + } + } + + if (player->swordState != 0) { + if ((this->actor.bgCheckFlags & 8) && ((ABS((s16)(this->actor.wallYaw - this->actor.shape.rot.y)) < 0x38A4) && + (this->actor.xzDistToPlayer < 80.0f))) { + EnTest_SetupJumpUp(this); + } else if ((Rand_ZeroOne() > 0.7f) && (this->actor.params != STALFOS_TYPE_CEILING) && + (player->swordAnimation != 0x11)) { + EnTest_SetupJumpBack(this); + } else { + EnTest_SetupStopAndBlock(this); + } + + this->unk_7C8 = 8; + } +} + +void func_80862398(EnTest* this) { + Animation_PlayOnce(&this->skelAnime, &gStalfosFlinchFromHitBehindAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_DAMAGE); + this->unk_7C8 = 9; + this->actor.speedXZ = -2.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + EnTest_SetupAction(this, func_80862418); +} + +void func_80862418(EnTest* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.1f, 0.0f); + + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.speedXZ = 0.0f; + + if (!EnTest_ReactToProjectile(globalCtx, this)) { + EnTest_ChooseAction(this, globalCtx); + } else { + return; + } + } + + if (player->swordState != 0) { + if ((this->actor.bgCheckFlags & 8) && ((ABS((s16)(this->actor.wallYaw - this->actor.shape.rot.y)) < 0x38A4) && + (this->actor.xzDistToPlayer < 80.0f))) { + EnTest_SetupJumpUp(this); + } else if ((Rand_ZeroOne() > 0.7f) && (this->actor.params != STALFOS_TYPE_CEILING) && + (player->swordAnimation != 0x11)) { + EnTest_SetupJumpBack(this); + } else { + EnTest_SetupStopAndBlock(this); + } + + this->unk_7C8 = 8; + } +} + +void EnTest_SetupStunned(EnTest* this) { + this->unk_7C8 = 0xB; + this->unk_7DE = 0; + this->swordState = 0; + this->skelAnime.playSpeed = 0.0f; + this->actor.speedXZ = -4.0f; + + if (this->lastDamageEffect == STALFOS_DMGEFF_LIGHT) { + Actor_SetColorFilter(&this->actor, -0x8000, 0x78, 0, 0x50); + } else { + Actor_SetColorFilter(&this->actor, 0, 0x78, 0, 0x50); + + if (this->lastDamageEffect == STALFOS_DMGEFF_FREEZE) { + this->iceTimer = 36; + } else { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gStalfosFlinchFromHitFrontAnim, 0.0f); + } + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + EnTest_SetupAction(this, EnTest_Stunned); +} + +void EnTest_Stunned(EnTest* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 1.0f, 0.0f); + + if (this->actor.colorFilterTimer == 0) { + if (this->actor.colChkInfo.health == 0) { + func_80862FA8(this, globalCtx); + } else if (player->swordState != 0) { + if ((this->actor.bgCheckFlags & 8) && + ((ABS((s16)(this->actor.wallYaw - this->actor.shape.rot.y)) < 0x38A4) && + (this->actor.xzDistToPlayer < 80.0f))) { + EnTest_SetupJumpUp(this); + } else if ((Rand_ZeroOne() > 0.7f) && (player->swordAnimation != 0x11)) { + EnTest_SetupJumpBack(this); + } else { + EnTest_SetupStopAndBlock(this); + } + + this->unk_7C8 = 8; + } else { + this->actor.speedXZ = 0.0f; + if (!EnTest_ReactToProjectile(globalCtx, this)) { + EnTest_ChooseAction(this, globalCtx); + } + } + } +} + +// a variation of sidestep +void func_808627C4(EnTest* this, GlobalContext* globalCtx) { + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + func_80860EC0(this); + return; + } + + Animation_MorphToLoop(&this->skelAnime, &gStalfosSidestepAnim, -2.0f); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + this->actor.speedXZ = ((globalCtx->gameplayFrames % 2) != 0) ? -4.0f : 4.0f; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + this->timer = (Rand_ZeroOne() * 20.0f) + 20.0f; + this->unk_7C8 = 0x18; + EnTest_SetupAction(this, func_808628C8); + this->unk_7EC = 0.0f; +} + +// a variation of sidestep +void func_808628C8(EnTest* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 pad1; + s32 prevFrame; + s32 temp_f16; + s32 pad2; + f32 checkDist = 0.0f; + s16 newYaw; + f32 absPlaySpeed; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xFA0, 1); + + if (this->unk_7DE == 0) { + this->unk_7DE++; + } + + if (this->actor.speedXZ >= 0.0f) { + if (this->actor.speedXZ < 6.0f) { + this->actor.speedXZ += 0.125f; + } else { + this->actor.speedXZ = 6.0f; + } + } else { + if (this->actor.speedXZ > -6.0f) { + this->actor.speedXZ -= 0.125f; + } else { + this->actor.speedXZ = -6.0f; + } + } + + if ((this->actor.bgCheckFlags & 8) || + ((this->actor.params == STALFOS_TYPE_CEILING) && + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y + 0x3FFF))) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + newYaw = (this->actor.shape.rot.y + 0x3FFF); + } else { + newYaw = (this->actor.shape.rot.y - 0x3FFF); + } + + newYaw = this->actor.wallYaw - newYaw; + } else { + this->actor.speedXZ *= -0.8f; + newYaw = 0; + } + + if (ABS(newYaw) > 0x4000) { + this->actor.speedXZ *= -0.8f; + + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ += 0.5f; + } + } + } + + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + checkDist = 200.0f; + } + + if (this->actor.xzDistToPlayer <= (80.0f + checkDist)) { + Math_SmoothStepToF(&this->unk_7EC, -2.5f, 1.0f, 0.8f, 0.0f); + } else if (this->actor.xzDistToPlayer > (110.0f + checkDist)) { + Math_SmoothStepToF(&this->unk_7EC, 2.5f, 1.0f, 0.8f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_7EC, 0.0f, 1.0f, 6.65f, 0.0f); + } + + if (this->unk_7EC != 0.0f) { + this->actor.world.pos.x += (Math_SinS(this->actor.shape.rot.y) * this->unk_7EC); + this->actor.world.pos.z += (Math_CosS(this->actor.shape.rot.y) * this->unk_7EC); + } + + this->skelAnime.playSpeed = this->actor.speedXZ * 0.5f; + prevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + + temp_f16 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + absPlaySpeed = ABS(this->skelAnime.playSpeed); + + if ((this->timer % 32) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WARAU); + } + if ((s32)this->skelAnime.curFrame != prevFrame) { + s32 temp_v0_2 = (s32)absPlaySpeed + prevFrame; + + if (((temp_v0_2 > 1) && (temp_f16 <= 0)) || ((temp_f16 < 7) && (temp_v0_2 >= 8))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_WALK); + } + } + + if (this->timer == 0) { + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + EnTest_SetupIdle(this); + } else if (Actor_IsTargeted(globalCtx, &this->actor)) { + if (!EnTest_ReactToProjectile(globalCtx, this)) { + EnTest_ChooseAction(this, globalCtx); + } + } else if (player->heldItemActionParam != PLAYER_AP_NONE) { + if ((globalCtx->gameplayFrames % 2) != 0) { + EnTest_SetupIdle(this); + } else { + EnTest_SetupWalkAndBlock(this); + } + } else { + EnTest_SetupWalkAndBlock(this); + } + + } else { + this->timer--; + } +} + +void func_80862DBC(EnTest* this, GlobalContext* globalCtx) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_DAMAGE); + this->unk_7C8 = 2; + BodyBreak_Alloc(&this->bodyBreak, 60, globalCtx); + this->actor.home.rot.x = 0; + + if (this->swordState >= 0) { + EffectBlure_AddSpace(Effect_GetByIndex(this->effectIndex)); + this->swordState = -1; + } + + this->actor.flags &= ~ACTOR_FLAG_0; + + if (this->actor.params == STALFOS_TYPE_5) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + } + + EnTest_SetupAction(this, func_80862E6C); +} + +void func_80862E6C(EnTest* this, GlobalContext* globalCtx) { + if (this->actor.child == NULL) { + if (this->actor.home.rot.x == 0) { + this->actor.home.rot.x = this->bodyBreak.count; + } + + if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, this->actor.params + 8)) { + this->actor.child = &this->actor; + } + } else { + if (this->actor.home.rot.x == 0) { + this->actor.colChkInfo.health = 10; + + if (this->actor.params == STALFOS_TYPE_4) { + this->actor.params = -1; + } else { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + } + + this->actor.child = NULL; + this->actor.flags |= ACTOR_FLAG_0; + EnTest_SetupJumpBack(this); + } else if ((this->actor.params == STALFOS_TYPE_5) && + !Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f)) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xD0); + + if (this->actor.parent != NULL) { + this->actor.parent->home.rot.z--; + } + + Actor_Kill(&this->actor); + } + } +} + +void func_80862FA8(EnTest* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gStalfosFallOverBackwardsAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_DEAD); + this->unk_7DE = 0; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.colorFilterTimer = 0; + this->actor.speedXZ = 0.0f; + + if (this->actor.params <= STALFOS_TYPE_CEILING) { + this->unk_7C8 = 5; + EnTest_SetupAction(this, func_80863044); + } else { + func_80862DBC(this, globalCtx); + } +} + +void func_80863044(EnTest* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->timer = (Rand_ZeroOne() * 10.0f) + 10.0f; + this->unk_7C8 = 7; + EnTest_SetupAction(this, func_808633E8); + BodyBreak_Alloc(&this->bodyBreak, 60, globalCtx); + } + + if ((s32)this->skelAnime.curFrame == 15) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } +} + +void func_808630F0(EnTest* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gStalfosFallOverForwardsAnim); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_DEAD); + this->unk_7C8 = 6; + this->actor.colorFilterTimer = 0; + this->unk_7DE = 0; + this->actor.speedXZ = 0.0f; + + if (this->actor.params <= STALFOS_TYPE_CEILING) { + this->actor.flags &= ~ACTOR_FLAG_0; + EnTest_SetupAction(this, func_8086318C); + } else { + func_80862DBC(this, globalCtx); + } +} + +void func_8086318C(EnTest* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + this->timer = (Rand_ZeroOne() * 10.0f) + 10.0f; + this->unk_7C8 = 7; + EnTest_SetupAction(this, func_808633E8); + BodyBreak_Alloc(&this->bodyBreak, 60, globalCtx); + } + + if (((s32)this->skelAnime.curFrame == 10) || ((s32)this->skelAnime.curFrame == 25)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } +} + +void EnTest_SetupRecoil(EnTest* this) { + this->swordState = 0; + this->skelAnime.moveFlags = 2; + this->unk_7C8 = 0x13; + this->skelAnime.playSpeed = -1.0f; + this->skelAnime.startFrame = this->skelAnime.curFrame; + this->skelAnime.endFrame = 0.0f; + EnTest_SetupAction(this, EnTest_Recoil); +} + +void EnTest_Recoil(EnTest* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (Rand_ZeroOne() > 0.7f) { + EnTest_SetupIdle(this); + this->timer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } else if (((globalCtx->gameplayFrames % 2) != 0) && (this->actor.params != STALFOS_TYPE_CEILING)) { + EnTest_SetupJumpBack(this); + } else { + func_808627C4(this, globalCtx); + } + } +} + +void EnTest_Rise(EnTest* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->actor.scale.y < 0.015f) { + this->actor.scale.y += 0.002f; + this->actor.world.pos.y = this->actor.home.pos.y - 3.5f; + } else { + this->actor.world.pos.y = this->actor.home.pos.y; + EnTest_SetupJumpBack(this); + } +} + +void func_808633E8(EnTest* this, GlobalContext* globalCtx) { + this->actor.params = STALFOS_TYPE_1; + + if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, this->actor.params)) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xD0); + + if (this->actor.parent != NULL) { + this->actor.parent->home.rot.z--; + } + + Actor_Kill(&this->actor); + } +} + +void EnTest_UpdateHeadRot(EnTest* this, GlobalContext* globalCtx) { + s16 lookAngle = this->actor.yawTowardsPlayer; + + lookAngle -= (s16)(this->headRot.y + this->actor.shape.rot.y); + + this->headRotOffset.y = CLAMP(lookAngle, -0x7D0, 0x7D0); + this->headRot.y += this->headRotOffset.y; + this->headRot.y = CLAMP(this->headRot.y, -0x382F, 0x382F); +} + +void EnTest_UpdateDamage(EnTest* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->shieldCollider.base.acFlags & AC_BOUNCED) { + this->shieldCollider.base.acFlags &= ~AC_BOUNCED; + this->bodyCollider.base.acFlags &= ~AC_HIT; + + if (this->unk_7C8 >= 0xA) { + this->actor.speedXZ = -4.0f; + } + } else if (this->bodyCollider.base.acFlags & AC_HIT) { + this->bodyCollider.base.acFlags &= ~AC_HIT; + + if ((this->actor.colChkInfo.damageEffect != STALFOS_DMGEFF_SLING) && + (this->actor.colChkInfo.damageEffect != STALFOS_DMGEFF_FIREMAGIC)) { + this->lastDamageEffect = this->actor.colChkInfo.damageEffect; + if (this->swordState >= 1) { + this->swordState = 0; + } + this->unk_7DC = player->unk_845; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Actor_SetDropFlag(&this->actor, &this->bodyCollider.info, false); + Audio_StopSfxByPosAndId(&this->actor.projectedPos, NA_SE_EN_STAL_WARAU); + + if ((this->actor.colChkInfo.damageEffect == STALFOS_DMGEFF_STUN) || + (this->actor.colChkInfo.damageEffect == STALFOS_DMGEFF_FREEZE) || + (this->actor.colChkInfo.damageEffect == STALFOS_DMGEFF_LIGHT)) { + if (this->unk_7C8 != 0xB) { + Actor_ApplyDamage(&this->actor); + EnTest_SetupStunned(this); + } + } else { + if (Actor_IsFacingPlayer(&this->actor, 0x4000)) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + func_80862FA8(this, globalCtx); + } else { + func_80862154(this); + } + } else if (Actor_ApplyDamage(&this->actor) == 0) { + func_808630F0(this, globalCtx); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + func_80862398(this); + } + } + } + } +} + +void EnTest_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTest* this = (EnTest*)thisx; + f32 oldWeight; + u32 floorProperty; + s32 pad; + + EnTest_UpdateDamage(this, globalCtx); + + if (this->actor.colChkInfo.damageEffect != STALFOS_DMGEFF_FIREMAGIC) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 0x1D); + + if (this->actor.params == STALFOS_TYPE_1) { + if (this->actor.world.pos.y <= this->actor.home.pos.y) { + this->actor.world.pos.y = this->actor.home.pos.y; + this->actor.velocity.y = 0.0f; + } + + if (this->actor.floorHeight <= this->actor.home.pos.y) { + this->actor.floorHeight = this->actor.home.pos.y; + } + } else if (this->actor.bgCheckFlags & 2) { + floorProperty = func_80041EA4(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + + if ((floorProperty == 5) || (floorProperty == 0xC) || + func_80041D4C(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId) == 9) { + Actor_Kill(&this->actor); + return; + } + } + + this->actionFunc(this, globalCtx); + + switch (this->unk_7DE) { + case 0: + break; + + case 1: + Animation_Change(&this->upperSkelanime, &gStalfosBlockWithShieldAnim, 2.0f, 0.0f, + Animation_GetLastFrame(&gStalfosBlockWithShieldAnim), 2, 2.0f); + AnimationContext_SetCopyTrue(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->upperSkelanime.jointTable, sJointCopyFlags); + this->unk_7DE++; + break; + + case 2: + SkelAnime_Update(&this->upperSkelanime); + SkelAnime_CopyFrameTableTrue(&this->skelAnime, this->skelAnime.jointTable, + this->upperSkelanime.jointTable, sJointCopyFlags); + break; + + case 3: + this->unk_7DE++; + this->upperSkelanime.morphWeight = 4.0f; + // fallthrough + case 4: + oldWeight = this->upperSkelanime.morphWeight; + this->upperSkelanime.morphWeight -= 1.0f; + + if (this->upperSkelanime.morphWeight <= 0.0f) { + this->unk_7DE = 0; + } + + SkelAnime_InterpFrameTable(this->skelAnime.limbCount, this->upperSkelanime.jointTable, + this->upperSkelanime.jointTable, this->skelAnime.jointTable, + 1.0f - (this->upperSkelanime.morphWeight / oldWeight)); + SkelAnime_CopyFrameTableTrue(&this->skelAnime, this->skelAnime.jointTable, + this->upperSkelanime.jointTable, sJointCopyFlags); + break; + } + + if ((this->actor.colorFilterTimer == 0) && (this->actor.colChkInfo.health != 0)) { + if ((this->unk_7C8 != 0x10) && (this->unk_7C8 != 0x17)) { + EnTest_UpdateHeadRot(this, globalCtx); + } else { + Math_SmoothStepToS(&this->headRot.y, 0, 1, 0x3E8, 0); + } + } + } + + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 45.0f; + + if ((this->actor.colChkInfo.health > 0) || (this->actor.colorFilterTimer != 0)) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + + if ((this->unk_7C8 >= 0xA) && + ((this->actor.colorFilterTimer == 0) || (!(this->actor.colorFilterParams & 0x4000)))) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + + if (this->unk_7DE != 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->shieldCollider.base); + } + } + + if (this->swordState >= 1) { + if (!(this->swordCollider.base.atFlags & AT_BOUNCED)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->swordCollider.base); + } else { + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + EnTest_SetupRecoil(this); + } + } + + if (this->actor.params == STALFOS_TYPE_INVISIBLE) { + if (globalCtx->actorCtx.unk_03 != 0) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_7; + this->actor.shape.shadowDraw = ActorShadow_DrawFeet; + } else { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_7); + this->actor.shape.shadowDraw = NULL; + } + } +} + +s32 EnTest_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnTest* this = (EnTest*)thisx; + s32 pad; + + if (limbIndex == STALFOS_LIMB_HEAD_ROOT) { + rot->x += this->headRot.y; + rot->y -= this->headRot.x; + rot->z += this->headRot.z; + } else if (limbIndex == STALFOS_LIMB_HEAD) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_test.c", 3582); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetEnvColor(POLY_OPA_DISP++, 80 + ABS((s16)(Math_SinS(globalCtx->gameplayFrames * 2000) * 175.0f)), 0, 0, + 255); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_test.c", 3587); + } + + if ((this->actor.params == STALFOS_TYPE_INVISIBLE) && !CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_7)) { + *dList = NULL; + } + + return false; +} + +void EnTest_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f unused1 = { 1100.0f, -700.0f, 0.0f }; + static Vec3f D_80864658 = { 300.0f, 0.0f, 0.0f }; + static Vec3f D_80864664 = { 3400.0f, 0.0f, 0.0f }; + static Vec3f D_80864670 = { 0.0f, 0.0f, 0.0f }; + static Vec3f D_8086467C = { 7000.0f, 1000.0f, 0.0f }; + static Vec3f D_80864688 = { 3000.0f, -2000.0f, -1000.0f }; + static Vec3f D_80864694 = { 3000.0f, -2000.0f, 1000.0f }; + static Vec3f D_808646A0 = { -1300.0f, 1100.0f, 0.0f }; + static Vec3f unused2 = { -3000.0f, 1900.0f, 800.0f }; + static Vec3f unused3 = { -3000.0f, -1100.0f, 800.0f }; + static Vec3f unused4 = { 1900.0f, 1900.0f, 800.0f }; + static Vec3f unused5 = { -3000.0f, -1100.0f, 800.0f }; + static Vec3f unused6 = { 1900.0f, -1100.0f, 800.0f }; + static Vec3f unused7 = { 1900.0f, 1900.0f, 800.0f }; + s32 bodyPart = -1; + Vec3f sp70; + Vec3f sp64; + EnTest* this = (EnTest*)thisx; + s32 pad; + Vec3f sp50; + + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 0, 60, 60, dList, BODYBREAK_OBJECT_DEFAULT); + + if (limbIndex == STALFOS_LIMB_SWORD) { + Matrix_MultVec3f(&D_8086467C, &this->swordCollider.dim.quad[1]); + Matrix_MultVec3f(&D_80864688, &this->swordCollider.dim.quad[0]); + Matrix_MultVec3f(&D_80864694, &this->swordCollider.dim.quad[3]); + Matrix_MultVec3f(&D_808646A0, &this->swordCollider.dim.quad[2]); + + Collider_SetQuadVertices(&this->swordCollider, &this->swordCollider.dim.quad[0], + &this->swordCollider.dim.quad[1], &this->swordCollider.dim.quad[2], + &this->swordCollider.dim.quad[3]); + + Matrix_MultVec3f(&D_80864664, &sp70); + Matrix_MultVec3f(&D_80864670, &sp64); + + if ((this->swordState >= 1) && + ((this->actor.params != STALFOS_TYPE_INVISIBLE) || (globalCtx->actorCtx.unk_03 != 0))) { + EffectBlure_AddVertex(Effect_GetByIndex(this->effectIndex), &sp70, &sp64); + } else if (this->swordState >= 0) { + EffectBlure_AddSpace(Effect_GetByIndex(this->effectIndex)); + this->swordState = -1; + } + + } else if ((limbIndex == STALFOS_LIMB_SHIELD) && (this->unk_7DE != 0)) { + Matrix_MultVec3f(&D_80864670, &sp64); + + this->shieldCollider.dim.pos.x = sp64.x; + this->shieldCollider.dim.pos.y = sp64.y; + this->shieldCollider.dim.pos.z = sp64.z; + } else { + Actor_SetFeetPos(&this->actor, limbIndex, STALFOS_LIMB_FOOT_L, &D_80864658, STALFOS_LIMB_ANKLE_R, &D_80864658); + + if ((limbIndex == STALFOS_LIMB_FOOT_L) || (limbIndex == STALFOS_LIMB_ANKLE_R)) { + if ((this->unk_7C8 == 0x15) || (this->unk_7C8 == 0x16)) { + if (this->actor.speedXZ != 0.0f) { + Matrix_MultVec3f(&D_80864658, &sp64); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &sp64, 10.0f, 1, 8.0f, 0x64, 0xF, 0); + } + } + } + } + + if (this->iceTimer != 0) { + switch (limbIndex) { + case STALFOS_LIMB_HEAD: + bodyPart = 0; + break; + case STALFOS_LIMB_CHEST: + bodyPart = 1; + break; + case STALFOS_LIMB_SWORD: + bodyPart = 2; + break; + case STALFOS_LIMB_SHIELD: + bodyPart = 3; + break; + case STALFOS_LIMB_UPPERARM_R: + bodyPart = 4; + break; + case STALFOS_LIMB_UPPERARM_L: + bodyPart = 5; + break; + case STALFOS_LIMB_WAIST: + bodyPart = 6; + break; + case STALFOS_LIMB_FOOT_L: + bodyPart = 7; + break; + case STALFOS_LIMB_FOOT_R: + bodyPart = 8; + break; + } + + if (bodyPart >= 0) { + Matrix_MultVec3f(&D_80864670, &sp50); + + this->bodyPartsPos[bodyPart].x = sp50.x; + this->bodyPartsPos[bodyPart].y = sp50.y; + this->bodyPartsPos[bodyPart].z = sp50.z; + } + } +} + +void EnTest_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnTest* this = (EnTest*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 1); + + if ((thisx->params <= STALFOS_TYPE_CEILING) || (thisx->child == NULL)) { + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnTest_OverrideLimbDraw, + EnTest_PostLimbDraw, this); + } + + if (this->iceTimer != 0) { + thisx->colorFilterTimer++; + this->iceTimer--; + + if ((this->iceTimer % 4) == 0) { + s32 iceIndex = this->iceTimer >> 2; + + EffectSsEnIce_SpawnFlyingVec3s(globalCtx, thisx, &this->bodyPartsPos[iceIndex], 150, 150, 150, 250, 235, + 245, 255, 1.5f); + } + } +} + +// a variation of sidestep +void func_80864158(EnTest* this, f32 xzSpeed) { + Animation_MorphToLoop(&this->skelAnime, &gStalfosSidestepAnim, -2.0f); + this->actor.speedXZ = xzSpeed; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + this->timer = (Rand_ZeroOne() * 20.0f) + 15.0f; + this->unk_7C8 = 0x18; + EnTest_SetupAction(this, func_808628C8); +} + +/** + * Check if a projectile actor is within 300 units and react accordingly. + * Returns true if the projectile test passes and a new action is performed. + */ +s32 EnTest_ReactToProjectile(GlobalContext* globalCtx, EnTest* this) { + Actor* projectileActor; + s16 yawToProjectile; + s16 wallYawDiff; + s16 touchingWall; + s16 directionFlag; + + projectileActor = Actor_GetProjectileActor(globalCtx, &this->actor, 300.0f); + + if (projectileActor != NULL) { + yawToProjectile = Actor_WorldYawTowardActor(&this->actor, projectileActor) - (u16)this->actor.shape.rot.y; + + if ((u8)(this->actor.bgCheckFlags & 8)) { + wallYawDiff = ((u16)this->actor.wallYaw - (u16)this->actor.shape.rot.y); + touchingWall = true; + } else { + touchingWall = false; + } + + if (Math_Vec3f_DistXYZ(&this->actor.world.pos, &projectileActor->world.pos) < 200.0f) { + if (Actor_IsTargeted(globalCtx, &this->actor) && (projectileActor->id == ACTOR_ARMS_HOOK)) { + EnTest_SetupJumpUp(this); + } else if (ABS(yawToProjectile) < 0x2000) { + EnTest_SetupStopAndBlock(this); + } else if (ABS(yawToProjectile) < 0x6000) { + EnTest_SetupJumpBack(this); + } else { + EnTest_SetupJumpUp(this); + } + + return true; + } + + if (Actor_IsTargeted(globalCtx, &this->actor) && (projectileActor->id == ACTOR_ARMS_HOOK)) { + EnTest_SetupJumpUp(this); + return true; + } + + if ((ABS(yawToProjectile) < 0x2000) || (ABS(yawToProjectile) > 0x6000)) { + directionFlag = globalCtx->gameplayFrames % 2; + + if (touchingWall && (wallYawDiff > 0x2000) && (wallYawDiff < 0x6000)) { + directionFlag = false; + } else if (touchingWall && (wallYawDiff < -0x2000) && (wallYawDiff > -0x6000)) { + directionFlag = true; + } + + if (directionFlag) { + func_80864158(this, 4.0f); + } else { + func_80864158(this, -4.0f); + } + } else if (ABS(yawToProjectile) < 0x6000) { + directionFlag = globalCtx->gameplayFrames % 2; + + if (touchingWall && (ABS(wallYawDiff) > 0x6000)) { + directionFlag = false; + } else if (touchingWall && (ABS(wallYawDiff) < 0x2000)) { + directionFlag = true; + } + + if (directionFlag) { + EnTest_SetupJumpBack(this); + } else { + EnTest_SetupJumpUp(this); + } + } + + return true; + } + + return false; +} diff --git a/soh/src/overlays/actors/ovl_En_Test/z_en_test.h b/soh/src/overlays/actors/ovl_En_Test/z_en_test.h new file mode 100644 index 000000000..93263e236 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Test/z_en_test.h @@ -0,0 +1,114 @@ +#ifndef Z_EN_TEST_H +#define Z_EN_TEST_H + +#include "ultra64.h" +#include "global.h" + +struct EnTest; + +typedef void (*EnTestActionFunc)(struct EnTest*, GlobalContext*); + +typedef enum { + /* 0x00 */ STALFOS_LIMB_NONE, + /* 0x01 */ STALFOS_LIMB_ROOT, + /* 0x02 */ STALFOS_LIMB_UPPERBODY_ROOT, + /* 0x03 */ STALFOS_LIMB_CORE_LOWER_ROOT, + /* 0x04 */ STALFOS_LIMB_CORE_UPPER_ROOT, + /* 0x05 */ STALFOS_LIMB_NECK_ROOT, + /* 0x06 */ STALFOS_LIMB_HEAD_ROOT, + /* 0x07 */ STALFOS_LIMB_7, + /* 0x08 */ STALFOS_LIMB_8, + /* 0x09 */ STALFOS_LIMB_JAW_ROOT, + /* 0x0A */ STALFOS_LIMB_JAW, + /* 0x0B */ STALFOS_LIMB_HEAD, + /* 0x0C */ STALFOS_LIMB_NECK_UPPER, + /* 0x0D */ STALFOS_LIMB_NECK_LOWER, + /* 0x0E */ STALFOS_LIMB_CORE_UPPER, + /* 0x0F */ STALFOS_LIMB_CHEST, + /* 0x10 */ STALFOS_LIMB_SHOULDER_R_ROOT, + /* 0x11 */ STALFOS_LIMB_SHOULDER_ARMOR_R_ROOT, + /* 0x12 */ STALFOS_LIMB_SHOULDER_ARMOR_R, + /* 0x13 */ STALFOS_LIMB_SHOULDER_L_ROOT, + /* 0x14 */ STALFOS_LIMB_SHOULDER_ARMOR_L_ROOT, + /* 0x15 */ STALFOS_LIMB_SHOULDER_ARMOR_L, + /* 0x16 */ STALFOS_LIMB_ARM_L_ROOT, + /* 0x17 */ STALFOS_LIMB_UPPERARM_L_ROOT, + /* 0x18 */ STALFOS_LIMB_FOREARM_L_ROOT, + /* 0x19 */ STALFOS_LIMB_HAND_L_ROOT, + /* 0x1A */ STALFOS_LIMB_HAND_L, + /* 0x1B */ STALFOS_LIMB_SHIELD, + /* 0x1C */ STALFOS_LIMB_FOREARM_L, + /* 0x1D */ STALFOS_LIMB_UPPERARM_L, + /* 0x1E */ STALFOS_LIMB_ARM_R_ROOT, + /* 0x1F */ STALFOS_LIMB_UPPERARM_R_ROOT, + /* 0x20 */ STALFOS_LIMB_FOREARM_R_ROOT, + /* 0x21 */ STALFOS_LIMB_HAND_R_ROOT, + /* 0x22 */ STALFOS_LIMB_SWORD, + /* 0x23 */ STALFOS_LIMB_HAND_R, + /* 0x24 */ STALFOS_LIMB_FOREARM_R, + /* 0x25 */ STALFOS_LIMB_UPPERARM_R, + /* 0x26 */ STALFOS_LIMB_CORE_LOWER, + /* 0x27 */ STALFOS_LIMB_LOWERBODY_ROOT, + /* 0x28 */ STALFOS_LIMB_WAIST_ROOT, + /* 0x29 */ STALFOS_LIMB_LEGS_ROOT, + /* 0x2A */ STALFOS_LIMB_LEG_L_ROOT, + /* 0x2B */ STALFOS_LIMB_THIGH_L_ROOT, + /* 0x2C */ STALFOS_LIMB_LOWERLEG_L_ROOT, + /* 0x2D */ STALFOS_LIMB_ANKLE_L_ROOT, + /* 0x2E */ STALFOS_LIMB_ANKLE_L, + /* 0x2F */ STALFOS_LIMB_FOOT_L_ROOT, + /* 0x30 */ STALFOS_LIMB_FOOT_L, + /* 0x31 */ STALFOS_LIMB_LOWERLEG_L, + /* 0x32 */ STALFOS_LIMB_THIGH_L, + /* 0x33 */ STALFOS_LIMB_LEG_R_ROOT, + /* 0x34 */ STALFOS_LIMB_THIGH_R_ROOT, + /* 0x35 */ STALFOS_LIMB_LOWERLEG_R_ROOT, + /* 0x36 */ STALFOS_LIMB_ANKLE_R_ROOT, + /* 0x37 */ STALFOS_LIMB_ANKLE_R, + /* 0x38 */ STALFOS_LIMB_FOOT_R_ROOT, + /* 0x39 */ STALFOS_LIMB_FOOT_R, + /* 0x3A */ STALFOS_LIMB_LOWERLEG_R, + /* 0x3B */ STALFOS_LIMB_THIGH_R, + /* 0x3C */ STALFOS_LIMB_WAIST, + /* 0x3D */ STALFOS_LIMB_MAX +} StalfosLimb; + +typedef struct EnTest { + /* 0x000 */ Actor actor; + /* 0x14C */ Vec3s bodyPartsPos[10]; + /* 0x188 */ SkelAnime skelAnime; + /* 0x1CC */ Vec3s jointTable[STALFOS_LIMB_MAX]; + /* 0x33A */ Vec3s morphTable[STALFOS_LIMB_MAX]; + /* 0x4A8 */ SkelAnime upperSkelanime; + /* 0x4EC */ Vec3s upperJointTable[STALFOS_LIMB_MAX]; + /* 0x65A */ Vec3s upperMorphTable[STALFOS_LIMB_MAX]; + /* 0x7C8 */ u8 unk_7C8; + /* 0x7CC */ EnTestActionFunc actionFunc; + /* 0x7D0 */ Vec3s headRot; + /* 0x7D6 */ Vec3s headRotOffset; + /* 0x7DC */ u8 unk_7DC; + /* 0x7DD */ char unk_7DD[0x1]; + /* 0x7DE */ u8 unk_7DE; + /* 0x7E0 */ s16 iceTimer; + /* 0x7E2 */ u8 lastDamageEffect; + /* 0x7E4 */ s32 unk_7E4; + /* 0x7E8 */ s32 timer; + /* 0x7EC */ f32 unk_7EC; + /* 0x7F0 */ BodyBreak bodyBreak; + /* 0x808 */ s8 swordState; + /* 0x80C */ s32 effectIndex; + /* 0x810 */ ColliderCylinder bodyCollider; + /* 0x85C */ ColliderQuad swordCollider; + /* 0x8DC */ ColliderCylinder shieldCollider; +} EnTest; // size = 0x928 + +typedef enum { + /* 0 */ STALFOS_TYPE_INVISIBLE, + /* 1 */ STALFOS_TYPE_1, + /* 2 */ STALFOS_TYPE_2, + /* 3 */ STALFOS_TYPE_CEILING, + /* 4 */ STALFOS_TYPE_4, + /* 5 */ STALFOS_TYPE_5 +} StalfosType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tg/z_en_tg.c b/soh/src/overlays/actors/ovl_En_Tg/z_en_tg.c new file mode 100644 index 000000000..73f17668b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tg/z_en_tg.c @@ -0,0 +1,196 @@ +/* + * File: z_en_tg.c + * Overlay: ovl_En_Tg + * Description: Dancing Couple + */ + +#include "z_en_tg.h" +#include "objects/object_mu/object_mu.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnTg_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTg_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTg_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTg_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnTg_SpinIfNotTalking(EnTg* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 64, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +const ActorInit En_Tg_InitVars = { + ACTOR_EN_TG, + ACTORCAT_NPC, + FLAGS, + OBJECT_MU, + sizeof(EnTg), + (ActorFunc)EnTg_Init, + (ActorFunc)EnTg_Destroy, + (ActorFunc)EnTg_Update, + (ActorFunc)EnTg_Draw, + NULL, +}; + +u16 EnTg_GetTextId(GlobalContext* globalCtx, Actor* thisx) { + EnTg* this = (EnTg*)thisx; + u16 temp; + u32 phi; + + // If the player is wearing a mask, return a special reaction text + temp = Text_GetFaceReaction(globalCtx, 0x24); + if (temp != 0) { + return temp; + } + // Use a different set of dialogue in Kakariko Village (Adult) + if (globalCtx->sceneNum == SCENE_SPOT01) { + if (this->nextDialogue % 2 != 0) { + phi = 0x5089; + } else { + phi = 0x508A; + } + return phi; + } else { + if (this->nextDialogue % 2 != 0) { + phi = 0x7025; + } else { + phi = 0x7026; + } + return phi; + } +} + +s16 EnTg_OnTextComplete(GlobalContext* globalCtx, Actor* thisx) { + EnTg* this = (EnTg*)thisx; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + return 1; + case TEXT_STATE_CLOSING: + switch (this->actor.textId) { + case 0x5089: + case 0x508A: + this->nextDialogue++; + break; + case 0x7025: + case 0x7026: + this->actor.params ^= 1; + this->nextDialogue++; + break; + } + return 0; + default: + return 1; + } +} + +void EnTg_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTg* this = (EnTg*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 28.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDancingCoupleSkel, &gDancingCoupleAnim, NULL, NULL, 0); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + this->actor.targetMode = 6; + Actor_SetScale(&this->actor, 0.01f); + this->nextDialogue = globalCtx->state.frames % 2; + this->actionFunc = EnTg_SpinIfNotTalking; +} + +void EnTg_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTg* this = (EnTg*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnTg_SpinIfNotTalking(EnTg* this, GlobalContext* globalCtx) { + if (!this->isTalking) { + this->actor.shape.rot.y += 0x800; + } +} + +void EnTg_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTg* this = (EnTg*)thisx; + s32 pad; + f32 temp; + Vec3s sp2C; + + sp2C.x = this->actor.world.pos.x; + sp2C.y = this->actor.world.pos.y; + sp2C.z = (s16)this->actor.world.pos.z + 3; + this->collider.dim.pos = sp2C; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + SkelAnime_Update(&this->skelAnime); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actionFunc(this, globalCtx); + temp = this->collider.dim.radius + 30.0f; + func_800343CC(globalCtx, &this->actor, &this->isTalking, temp, EnTg_GetTextId, EnTg_OnTextComplete); +} + +s32 EnTg_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + return false; +} + +void EnTg_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnTg* this = (EnTg*)thisx; + Vec3f targetOffset = { 0.0f, 800.0f, 0.0f }; + + if (limbIndex == 9) { + // Place the target point at the guy's head instead of the center of the actor + Matrix_MultVec3f(&targetOffset, &this->actor.focus.pos); + } +} + +Gfx* EnTg_SetColor(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b, u8 a) { + Gfx* displayList = Graph_Alloc(gfxCtx, 2 * sizeof(Gfx)); + + gDPSetEnvColor(displayList, r, g, b, a); + gSPEndDisplayList(displayList + 1); + return displayList; +} + +void EnTg_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnTg* this = (EnTg*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tg.c", 462); + Matrix_Translate(0.0f, 0.0f, -560.0f, MTXMODE_APPLY); + + // Set the guy's shoes and shirt to royal blue + gSPSegment(POLY_OPA_DISP++, 0x08, EnTg_SetColor(globalCtx->state.gfxCtx, 0, 50, 160, 0)); + + // Set the girl's shirt to white + gSPSegment(POLY_OPA_DISP++, 0x09, EnTg_SetColor(globalCtx->state.gfxCtx, 255, 255, 255, 0)); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnTg_OverrideLimbDraw, EnTg_PostLimbDraw, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tg.c", 480); +} diff --git a/soh/src/overlays/actors/ovl_En_Tg/z_en_tg.h b/soh/src/overlays/actors/ovl_En_Tg/z_en_tg.h new file mode 100644 index 000000000..fc6ff7f49 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tg/z_en_tg.h @@ -0,0 +1,21 @@ +#ifndef Z_EN_TG_H +#define Z_EN_TG_H + +#include "ultra64.h" +#include "global.h" + +struct EnTg; + +typedef void (*EnTgActionFunc)(struct EnTg*, GlobalContext*); + +typedef struct EnTg { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnTgActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s16 isTalking; + /* 0x01E2 */ char unk_1E2[0x26]; + /* 0x0208 */ u8 nextDialogue; +} EnTg; // size = 0x020C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c new file mode 100644 index 000000000..092a23cc5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.c @@ -0,0 +1,1013 @@ +/* + * File: z_en_tite.c + * Overlay: ovl_En_Tite + * Description: Tektite + */ + +#include "z_en_tite.h" +#include "overlays/actors/ovl_En_Encount1/z_en_encount1.h" +#include "overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h" +#include "vt.h" +#include "objects/object_tite/object_tite.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +// EnTite_Idle +#define vIdleTimer actionVar1 + +// EnTite_Attack (vQueuedJumps also used by EnTite_MoveTowardPlayer) +#define vAttackState actionVar1 +#define vQueuedJumps actionVar2 + +// EnTite_FlipOnBack +#define vOnBackTimer actionVar1 +#define vLegTwitchTimer actionVar2 + +typedef enum { + /* 0x0 */ TEKTITE_DEATH_CRY, + /* 0x1 */ TEKTITE_UNK_1, + /* 0x2 */ TEKTITE_UNK_2, + /* 0x3 */ TEKTITE_RECOIL, + /* 0x4 */ TEKTITE_UNK_4, + /* 0x5 */ TEKTITE_FALL_APART, + /* 0x6 */ TEKTITE_IDLE, + /* 0x7 */ TEKTITE_STUNNED, + /* 0x8 */ TEKTITE_UNK_8, + /* 0x9 */ TEKTITE_ATTACK, + /* 0xA */ TEKTITE_TURN_TOWARD_PLAYER, + /* 0xB */ TEKTITE_UNK9, + /* 0xC */ TEKTITE_MOVE_TOWARD_PLAYER +} EnTiteAction; + +typedef enum { + /* 0x0 */ TEKTITE_BEGIN_LUNGE, + /* 0x1 */ TEKTITE_MID_LUNGE, + /* 0x2 */ TEKTITE_LANDED, + /* 0x2 */ TEKTITE_SUBMERGED +} EnTiteAttackState; + +typedef enum { + /* 0x0 */ TEKTITE_INITIAL, + /* 0x1 */ TEKTITE_UNFLIPPED, + /* 0x2 */ TEKTITE_FLIPPED +} EnTiteFlipState; + +void EnTite_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTite_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTite_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTite_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnTite_SetupIdle(EnTite* this); +void EnTite_SetupTurnTowardPlayer(EnTite* this); +void EnTite_SetupMoveTowardPlayer(EnTite* this); +void EnTite_SetupDeathCry(EnTite* this); +void EnTite_SetupFlipUpright(EnTite* this); + +void EnTite_Idle(EnTite* this, GlobalContext* globalCtx); +void EnTite_Attack(EnTite* this, GlobalContext* globalCtx); +void EnTite_TurnTowardPlayer(EnTite* this, GlobalContext* globalCtx); +void EnTite_MoveTowardPlayer(EnTite* this, GlobalContext* globalCtx); +void EnTite_Recoil(EnTite* this, GlobalContext* globalCtx); +void EnTite_Stunned(EnTite* this, GlobalContext* globalCtx); +void EnTite_DeathCry(EnTite* this, GlobalContext* globalCtx); +void EnTite_FallApart(EnTite* this, GlobalContext* globalCtx); +void EnTite_FlipOnBack(EnTite* this, GlobalContext* globalCtx); +void EnTite_FlipUpright(EnTite* this, GlobalContext* globalCtx); + +const ActorInit En_Tite_InitVars = { + ACTOR_EN_TITE, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_TITE, + sizeof(EnTite), + (ActorFunc)EnTite_Init, + (ActorFunc)EnTite_Destroy, + (ActorFunc)EnTite_Update, + (ActorFunc)EnTite_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 0, { { 0, 1500, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT6, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static DamageTable sDamageTable[] = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(4, 0xF), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0xE), + /* Ice magic */ DMG_ENTRY(3, 0xF), + /* Light magic */ DMG_ENTRY(0, 0xE), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x45, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(minVelocityY, -40, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -1000, ICHAIN_STOP), +}; + +static AnimationHeader* D_80B1B634[] = { + &object_tite_Anim_00083C, &object_tite_Anim_0004F8, &object_tite_Anim_00069C, NULL, NULL, NULL, +}; + +// Some kind of offset for the position of each tektite foot +static Vec3f sFootOffset = { 2800.0f, -200.0f, 0.0f }; + +// Relative positions to spawn ice chunks when tektite is frozen +static Vec3f sIceChunks[12] = { + { 20.0f, 20.0f, 0.0f }, { 10.0f, 40.0f, 10.0f }, { -10.0f, 40.0f, 10.0f }, { -20.0f, 20.0f, 0.0f }, + { 10.0f, 40.0f, -10.0f }, { -10.0f, 40.0f, -10.0f }, { 0.0f, 20.0f, -20.0f }, { 10.0f, 0.0f, 10.0f }, + { 10.0f, 0.0f, -10.0f }, { 0.0f, 20.0f, 20.0f }, { -10.0f, 0.0f, 10.0f }, { -10.0f, 0.0f, -10.0f }, +}; + +void EnTite_SetupAction(EnTite* this, EnTiteActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnTite_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTite* this = (EnTite*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + thisx->targetMode = 3; + Actor_SetScale(thisx, 0.01f); + SkelAnime_Init(globalCtx, &this->skelAnime, &object_tite_Skel_003A20, &object_tite_Anim_0012E4, this->jointTable, + this->morphTable, 25); + ActorShape_Init(&thisx->shape, -200.0f, ActorShadow_DrawCircle, 70.0f); + this->flipState = TEKTITE_INITIAL; + thisx->colChkInfo.damageTable = sDamageTable; + this->actionVar1 = 0; + this->bodyBreak.val = BODYBREAK_STATUS_FINISHED; + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 20.0f; + thisx->colChkInfo.health = 2; + thisx->colChkInfo.mass = MASS_HEAVY; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, thisx, &sJntSphInit, &this->colliderItem); + this->unk_2DC = 0x1D; + if (this->actor.params == TEKTITE_BLUE) { + this->unk_2DC |= 0x40; // Don't use the actor engine's ripple spawning code + thisx->colChkInfo.health = 4; + thisx->naviEnemyId += 1; + } + EnTite_SetupIdle(this); +} + +void EnTite_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTite* this = (EnTite*)thisx; + EnEncount1* spawner; + + if (thisx->parent != NULL) { + spawner = (EnEncount1*)thisx->parent; + if (spawner->curNumSpawn > 0) { + spawner->curNumSpawn--; + } + osSyncPrintf("\n\n"); + // "Number of simultaneous occurrences" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 同時発生数 ☆☆☆☆☆%d\n" VT_RST, spawner->curNumSpawn); + osSyncPrintf("\n\n"); + } + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnTite_SetupIdle(EnTite* this) { + Animation_MorphToLoop(&this->skelAnime, &object_tite_Anim_0012E4, 4.0f); + this->action = TEKTITE_IDLE; + this->vIdleTimer = Rand_S16Offset(15, 30); + this->actor.speedXZ = 0.0f; + EnTite_SetupAction(this, EnTite_Idle); +} + +void EnTite_Idle(EnTite* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + if (this->actor.params == TEKTITE_BLUE) { + if (this->actor.bgCheckFlags & 0x20) { + // Float on water surface + this->actor.gravity = 0.0f; + Math_SmoothStepToF(&this->actor.velocity.y, 0.0f, 1.0f, 2.0f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToWater, 1.0f, 2.0f, + 0.0f); + } else { + this->actor.gravity = -1.0f; + } + } + if ((this->actor.bgCheckFlags & 3) && (this->actor.velocity.y <= 0.0f)) { + this->actor.velocity.y = 0.0f; + } + if (this->vIdleTimer > 0) { + this->vIdleTimer--; + } else if ((this->actor.xzDistToPlayer < 300.0f) && (this->actor.yDistToPlayer <= 80.0f)) { + EnTite_SetupTurnTowardPlayer(this); + } +} + +void EnTite_SetupAttack(EnTite* this) { + Animation_PlayOnce(&this->skelAnime, &object_tite_Anim_00083C); + this->action = TEKTITE_ATTACK; + this->vAttackState = TEKTITE_BEGIN_LUNGE; + this->vQueuedJumps = Rand_S16Offset(1, 3); + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnTite_SetupAction(this, EnTite_Attack); +} + +void EnTite_Attack(EnTite* this, GlobalContext* globalCtx) { + s16 angleToPlayer; + s32 attackState; + Vec3f ripplePos; + + if (SkelAnime_Update(&this->skelAnime) != 0) { + attackState = this->vAttackState; // for deciding whether to change animation + switch (this->vAttackState) { + case TEKTITE_BEGIN_LUNGE: + // Snap to ground or water, then lunge into the air with some initial speed + this->vAttackState = TEKTITE_MID_LUNGE; + if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) { + if (this->actor.floorHeight > BGCHECK_Y_MIN) { + this->actor.world.pos.y = this->actor.floorHeight; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + } else { + this->actor.world.pos.y += this->actor.yDistToWater; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER); + } + this->actor.velocity.y = 8.0f; + this->actor.gravity = -1.0f; + this->actor.speedXZ = 4.0f; + break; + case TEKTITE_MID_LUNGE: + // Continue trajectory until tektite has negative velocity and has landed on ground/water surface + // Snap to ground/water surface, or if falling fast dip into the water and slow fall speed + this->actor.flags |= ACTOR_FLAG_24; + if ((this->actor.bgCheckFlags & 3) || + ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) { + if (this->actor.velocity.y <= 0.0f) { + this->vAttackState = TEKTITE_LANDED; + if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) { + if (BGCHECK_Y_MIN < this->actor.floorHeight) { + this->actor.world.pos.y = this->actor.floorHeight; + } + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } else { + this->actor.gravity = 0.0f; + if (this->actor.velocity.y < -8.0f) { + ripplePos = this->actor.world.pos; + ripplePos.y += this->actor.yDistToWater; + this->vAttackState++; // TEKTITE_SUBMERGED + this->actor.velocity.y *= 0.75f; + attackState = this->vAttackState; + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 0, 500, 0); + } else { + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } + } + this->actor.world.rot.y = this->actor.shape.rot.y; + } + } + break; + case TEKTITE_LANDED: + // Get ready to begin another lunge if more lunges are queued, otherwise start turning + if (this->vQueuedJumps != 0) { + this->vQueuedJumps--; + this->vAttackState = TEKTITE_BEGIN_LUNGE; + this->collider.base.atFlags &= ~AT_HIT; + } else { + EnTite_SetupTurnTowardPlayer(this); + } + break; + case TEKTITE_SUBMERGED: + // Check if floated to surface + if (this->actor.yDistToWater == 0.0f) { + this->vAttackState = TEKTITE_LANDED; + attackState = this->vAttackState; + } + break; + } + // If switching attack state, change animation (unless tektite is switching between submerged and landed) + if (attackState != this->vAttackState) { + Animation_PlayOnce(&this->skelAnime, D_80B1B634[this->vAttackState]); + } + } + + switch (this->vAttackState) { + case TEKTITE_BEGIN_LUNGE: + // Slightly turn to player and switch to turning/idling action if the player is too far + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 1000, 0); + this->actor.shape.rot.y = this->actor.world.rot.y; + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if ((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f)) { + EnTite_SetupIdle(this); + } else if (ABS(angleToPlayer) >= 9000) { + EnTite_SetupTurnTowardPlayer(this); + } + break; + case TEKTITE_MID_LUNGE: + // Generate sparkles at feet upon landing, set jumping animation and hurtbox and check if hit player + if (this->actor.velocity.y >= 5.0f) { + if (this->actor.bgCheckFlags & 1) { + func_800355B8(globalCtx, &this->frontLeftFootPos); + func_800355B8(globalCtx, &this->frontRightFootPos); + func_800355B8(globalCtx, &this->backRightFootPos); + func_800355B8(globalCtx, &this->backLeftFootPos); + } + } + if (!(this->collider.base.atFlags & AT_HIT) && (this->actor.flags & ACTOR_FLAG_6)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } else { + Player* player = GET_PLAYER(globalCtx); + this->collider.base.atFlags &= ~AT_HIT; + Animation_MorphToLoop(&this->skelAnime, &object_tite_Anim_0012E4, 4.0f); + this->actor.speedXZ = -6.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (&player->actor == this->collider.base.at) { + if (!(this->collider.base.atFlags & AT_BOUNCED)) { + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + } + EnTite_SetupAction(this, EnTite_Recoil); + } + break; + case TEKTITE_LANDED: + // Slightly turn to player + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 1500, 0); + break; + case TEKTITE_SUBMERGED: + // Float up to water surface + Math_SmoothStepToF(&this->actor.velocity.y, 0.0f, 1.0f, 2.0f, 0.0f); + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToWater, 1.0f, 2.0f, + 0.0f); + break; + } + // Create ripples on water surface where tektite feet landed + if (this->actor.bgCheckFlags & 2) { + if (!(this->actor.bgCheckFlags & 0x20)) { + func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1); + } + } + // if landed, kill XZ speed and play appropriate sounds + if (this->actor.params == TEKTITE_BLUE) { + if (this->actor.bgCheckFlags & 0x40) { + this->actor.speedXZ = 0.0f; + if (this->vAttackState == TEKTITE_SUBMERGED) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER2); + } + this->actor.bgCheckFlags &= ~0x40; + } else if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + } else if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } +} + +void EnTite_SetupTurnTowardPlayer(EnTite* this) { + Animation_PlayLoop(&this->skelAnime, &object_tite_Anim_000A14); + this->action = TEKTITE_TURN_TOWARD_PLAYER; + if ((this->actor.bgCheckFlags & 3) || ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) { + if (this->actor.velocity.y <= 0.0f) { + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } + } + EnTite_SetupAction(this, EnTite_TurnTowardPlayer); +} + +void EnTite_TurnTowardPlayer(EnTite* this, GlobalContext* globalCtx) { + s16 angleToPlayer; + s16 turnVelocity; + + if (((this->actor.bgCheckFlags & 3) || + ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) && + (this->actor.velocity.y <= 0.0f)) { + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } + // Calculate turn velocity and animation speed based on angle to player + if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) { + this->actor.world.pos.y += this->actor.yDistToWater; + } + angleToPlayer = Actor_WorldYawTowardActor(&this->actor, &GET_PLAYER(globalCtx)->actor) - this->actor.world.rot.y; + if (angleToPlayer > 0) { + turnVelocity = (angleToPlayer / 42.0f) + 10.0f; + this->actor.world.rot.y += (turnVelocity * 2); + } else { + turnVelocity = (angleToPlayer / 42.0f) - 10.0f; + this->actor.world.rot.y += (turnVelocity * 2); + } + if (angleToPlayer > 0) { + this->skelAnime.playSpeed = turnVelocity * 0.01f; + } else { + this->skelAnime.playSpeed = turnVelocity * 0.01f; + } + + /** + * Play sounds once every animation cycle + */ + SkelAnime_Update(&this->skelAnime); + if (((s16)this->skelAnime.curFrame & 7) == 0) { + if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_WALK_WATER); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_WALK); + } + } + + // Idle if player is far enough away from the tektite, move or attack if almost facing player + this->actor.shape.rot.y = this->actor.world.rot.y; + if ((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f)) { + EnTite_SetupIdle(this); + } else if (Actor_IsFacingPlayer(&this->actor, 3640)) { + if ((this->actor.xzDistToPlayer <= 180.0f) && (this->actor.yDistToPlayer <= 80.0f)) { + EnTite_SetupAttack(this); + } else { + EnTite_SetupMoveTowardPlayer(this); + } + } +} + +void EnTite_SetupMoveTowardPlayer(EnTite* this) { + Animation_PlayLoop(&this->skelAnime, &object_tite_Anim_000C70); + this->action = TEKTITE_MOVE_TOWARD_PLAYER; + this->actor.velocity.y = 10.0f; + this->actor.gravity = -1.0f; + this->actor.speedXZ = 4.0f; + this->vQueuedJumps = Rand_S16Offset(1, 3); + if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + } + EnTite_SetupAction(this, EnTite_MoveTowardPlayer); +} + +/** + * Jumping toward player as a method of travel (different from attacking, has no hitbox) + */ +void EnTite_MoveTowardPlayer(EnTite* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 0.1f, 1.0f, 0.0f); + SkelAnime_Update(&this->skelAnime); + + if (this->actor.bgCheckFlags & 0x42) { + if (!(this->actor.bgCheckFlags & 0x40)) { + func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER); + } + } + + if ((this->actor.bgCheckFlags & 2) || ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x40))) { + if (this->vQueuedJumps != 0) { + this->vQueuedJumps--; + } else { + EnTite_SetupIdle(this); + } + } + + if (((this->actor.bgCheckFlags & 3) || (this->actor.params == TEKTITE_BLUE && (this->actor.bgCheckFlags & 0x60))) && + (this->actor.velocity.y <= 0.0f)) { + // slightly turn toward player upon landing and snap to ground or water. + this->actor.speedXZ = 0.0f; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) { + if (this->actor.floorHeight > BGCHECK_Y_MIN) { + this->actor.world.pos.y = this->actor.floorHeight; + } + } else if (this->actor.bgCheckFlags & 0x40) { + Vec3f ripplePos = this->actor.world.pos; + this->actor.bgCheckFlags &= ~0x40; + ripplePos.y += this->actor.yDistToWater; + this->actor.gravity = 0.0f; + this->actor.velocity.y *= 0.75f; + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 0, 500, 0); + return; + } else { + // If submerged, float to surface + Math_SmoothStepToF(&this->actor.velocity.y, 0.0f, 1.0f, 2.0f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.world.pos.y + this->actor.yDistToWater, 1.0f, 2.0f, + 0.0f); + if (this->actor.yDistToWater != 0.0f) { + // Do not change state until tekite has floated to surface + return; + } + } + + // Idle or turn if player is too far away, otherwise keep jumping + if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f))) { + EnTite_SetupIdle(this); + } else if (((this->actor.xzDistToPlayer <= 180.0f)) && ((this->actor.yDistToPlayer <= 80.0f))) { + if (this->vQueuedJumps <= 0) { + EnTite_SetupTurnTowardPlayer(this); + } else { + this->actor.velocity.y = 10.0f; + this->actor.speedXZ = 4.0f; + this->actor.flags |= ACTOR_FLAG_24; + this->actor.gravity = -1.0f; + if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + } + } + } else { + this->actor.velocity.y = 10.0f; + this->actor.speedXZ = 4.0f; + this->actor.flags |= ACTOR_FLAG_24; + this->actor.gravity = -1.0f; + if ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_JUMP_WATER); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + } + } + // If in midair: + } else { + // Turn slowly toward player + this->actor.flags |= ACTOR_FLAG_24; + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 1000, 0); + if (this->actor.velocity.y >= 6.0f) { + if (this->actor.bgCheckFlags & 1) { + func_800355B8(globalCtx, &this->frontLeftFootPos); + func_800355B8(globalCtx, &this->frontRightFootPos); + func_800355B8(globalCtx, &this->backRightFootPos); + func_800355B8(globalCtx, &this->backLeftFootPos); + } + } + } +} + +void EnTite_SetupRecoil(EnTite* this) { + this->action = TEKTITE_RECOIL; + Animation_MorphToLoop(&this->skelAnime, &object_tite_Anim_0012E4, 4.0f); + this->actor.speedXZ = -6.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.gravity = -1.0f; + EnTite_SetupAction(this, EnTite_Recoil); +} + +/** + * After tektite hits or gets hit, recoils backwards and slides a bit upon landing + */ +void EnTite_Recoil(EnTite* this, GlobalContext* globalCtx) { + s16 angleToPlayer; + + // Snap to ground or water surface upon landing + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + if (((this->actor.bgCheckFlags & 3) || (this->actor.params == TEKTITE_BLUE && (this->actor.bgCheckFlags & 0x20))) && + (this->actor.velocity.y <= 0.0f)) { + if ((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20)) { + if (this->actor.floorHeight > BGCHECK_Y_MIN) { + this->actor.world.pos.y = this->actor.floorHeight; + } + } else { + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + this->actor.world.pos.y += this->actor.yDistToWater; + } + } + + // play sound and generate ripples + if (this->actor.bgCheckFlags & 0x42) { + if (!(this->actor.bgCheckFlags & 0x40)) { + func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } else { + this->actor.bgCheckFlags &= ~0x40; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER2); + } + } + + // If player is far away, idle. Otherwise attack or move + angleToPlayer = (this->actor.yawTowardsPlayer - this->actor.shape.rot.y); + if ((this->actor.speedXZ == 0.0f) && ((this->actor.bgCheckFlags & 1) || ((this->actor.params == TEKTITE_BLUE) && + (this->actor.bgCheckFlags & 0x20)))) { + this->actor.world.rot.y = this->actor.shape.rot.y; + this->collider.base.atFlags &= ~AT_HIT; + if ((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) && + (ABS(this->actor.shape.rot.x) < 4000) && (ABS(this->actor.shape.rot.z) < 4000) && + ((this->actor.bgCheckFlags & 1) || + ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) { + EnTite_SetupIdle(this); + } else if ((this->actor.xzDistToPlayer < 180.0f) && (this->actor.yDistToPlayer <= 80.0f) && + (ABS(angleToPlayer) <= 6000)) { + EnTite_SetupAttack(this); + } else { + EnTite_SetupMoveTowardPlayer(this); + } + } + SkelAnime_Update(&this->skelAnime); +} + +void EnTite_SetupStunned(EnTite* this) { + Animation_Change(&this->skelAnime, &object_tite_Anim_0012E4, 0.0f, 0.0f, + (f32)Animation_GetLastFrame(&object_tite_Anim_0012E4), ANIMMODE_LOOP, 4.0f); + this->action = TEKTITE_STUNNED; + this->actor.speedXZ = -6.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (this->damageEffect == 0xF) { + this->spawnIceTimer = 48; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + EnTite_SetupAction(this, EnTite_Stunned); +} + +/** + * stunned or frozen + */ +void EnTite_Stunned(EnTite* this, GlobalContext* globalCtx) { + s16 angleToPlayer; + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + // Snap to ground or water + if (((this->actor.bgCheckFlags & 3) || + ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20))) && + (this->actor.velocity.y <= 0.0f)) { + if (((this->actor.params != TEKTITE_BLUE) || !(this->actor.bgCheckFlags & 0x20))) { + if (this->actor.floorHeight > BGCHECK_Y_MIN) { + this->actor.world.pos.y = this->actor.floorHeight; + } + } else { + this->actor.velocity.y = 0.0f; + this->actor.gravity = 0.0f; + this->actor.world.pos.y += this->actor.yDistToWater; + } + } + // Play sounds and spawn dirt effects upon landing + if (this->actor.bgCheckFlags & 0x42) { + if (!(this->actor.bgCheckFlags & 0x40)) { + func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } else { + this->actor.bgCheckFlags &= ~0x40; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_LAND_WATER2); + } + } + // Decide on next action based on health, flip state and player distance + angleToPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + if (((this->actor.colorFilterTimer == 0) && (this->actor.speedXZ == 0.0f)) && + ((this->actor.bgCheckFlags & 1) || + ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) { + this->actor.world.rot.y = this->actor.shape.rot.y; + if (this->actor.colChkInfo.health == 0) { + EnTite_SetupDeathCry(this); + } else if (this->flipState == TEKTITE_FLIPPED) { + EnTite_SetupFlipUpright(this); + } else if (((this->actor.xzDistToPlayer > 300.0f) && (this->actor.yDistToPlayer > 80.0f) && + (ABS(this->actor.shape.rot.x) < 4000) && (ABS(this->actor.shape.rot.z) < 4000)) && + ((this->actor.bgCheckFlags & 1) || + ((this->actor.params == TEKTITE_BLUE) && (this->actor.bgCheckFlags & 0x20)))) { + EnTite_SetupIdle(this); + } else if ((this->actor.xzDistToPlayer < 180.0f) && (this->actor.yDistToPlayer <= 80.0f) && + (ABS(angleToPlayer) <= 6000)) { + EnTite_SetupAttack(this); + } else { + EnTite_SetupMoveTowardPlayer(this); + } + } + SkelAnime_Update(&this->skelAnime); +} + +void EnTite_SetupDeathCry(EnTite* this) { + this->action = TEKTITE_DEATH_CRY; + this->actor.colorFilterTimer = 0; + this->actor.speedXZ = 0.0f; + EnTite_SetupAction(this, EnTite_DeathCry); +} + +/** + * First frame of death. Scream in pain and allocate memory for EnPart data + */ +void EnTite_DeathCry(EnTite* this, GlobalContext* globalCtx) { + EffectSsDeadSound_SpawnStationary(globalCtx, &this->actor.projectedPos, NA_SE_EN_TEKU_DEAD, true, + DEADSOUND_REPEAT_MODE_OFF, 40); + this->action = TEKTITE_FALL_APART; + EnTite_SetupAction(this, EnTite_FallApart); + BodyBreak_Alloc(&this->bodyBreak, 24, globalCtx); +} + +/** + * Spawn EnPart and drop items + */ +void EnTite_FallApart(EnTite* this, GlobalContext* globalCtx) { + if (BodyBreak_SpawnParts(&this->actor, &this->bodyBreak, globalCtx, this->actor.params + 0xB)) { + if (this->actor.params == TEKTITE_BLUE) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xE0); + } else { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x40); + } + Actor_Kill(&this->actor); + } +} + +void EnTite_SetupFlipOnBack(EnTite* this) { + + Animation_PlayLoopSetSpeed(&this->skelAnime, &object_tite_Anim_000A14, 1.5f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_REVERSE); + this->flipState = TEKTITE_FLIPPED; + this->vOnBackTimer = 500; + this->actor.speedXZ = 0.0f; + this->actor.gravity = -1.0f; + this->vLegTwitchTimer = (Rand_ZeroOne() * 50.0f); + this->actor.velocity.y = 11.0f; + EnTite_SetupAction(this, EnTite_FlipOnBack); +} + +/** + * During the flip animation and also while idling on back + */ +void EnTite_FlipOnBack(EnTite* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.z, 0x7FFF, 1, 4000, 0); + // randomly reset the leg wiggling animation whenever timer reaches 0 to give illusion of twitching legs + this->vLegTwitchTimer--; + if (this->vLegTwitchTimer == 0) { + this->vLegTwitchTimer = Rand_ZeroOne() * 30.0f; + this->skelAnime.curFrame = Rand_ZeroOne() * 5.0f; + } + SkelAnime_Update(&this->skelAnime); + if (this->actor.bgCheckFlags & 3) { + // Upon landing, spawn dust and make noise + if (this->actor.bgCheckFlags & 2) { + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 0xB, 4.0f, 0, 0, 0); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + this->vOnBackTimer--; + if (this->vOnBackTimer == 0) { + EnTite_SetupFlipUpright(this); + } + } else { + // Gradually increase y offset during flip so that the actor position is at tektite's back instead of feet + if (this->actor.shape.yOffset < 2800.0f) { + this->actor.shape.yOffset += 400.0f; + } + } +} + +void EnTite_SetupFlipUpright(EnTite* this) { + this->flipState = TEKTITE_UNFLIPPED; + this->actionVar1 = 1000; // value unused here and overwritten in SetupIdle + //! @bug flying tektite: water sets gravity to 0 so y velocity will never decrease from 13 + this->actor.velocity.y = 13.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TEKU_REVERSE); + EnTite_SetupAction(this, EnTite_FlipUpright); +} + +void EnTite_FlipUpright(EnTite* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.z, 0, 1, 0xFA0, 0); + SkelAnime_Update(&this->skelAnime); + //! @bug flying tektite: the following condition is never met and tektite stays stuck in this action forever + if (this->actor.bgCheckFlags & 2) { + func_80033480(globalCtx, &this->frontLeftFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->frontRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backRightFootPos, 1.0f, 2, 80, 15, 1); + func_80033480(globalCtx, &this->backLeftFootPos, 1.0f, 2, 80, 15, 1); + this->actor.shape.yOffset = 0.0f; + this->actor.world.pos.y = this->actor.floorHeight; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + EnTite_SetupIdle(this); + } +} + +void EnTite_CheckDamage(Actor* thisx, GlobalContext* globalCtx) { + EnTite* this = (EnTite*)thisx; + + if ((this->collider.base.acFlags & AC_HIT) && (this->action >= TEKTITE_IDLE)) { + this->collider.base.acFlags &= ~AC_HIT; + if (thisx->colChkInfo.damageEffect != 0xE) { // Immune to fire magic + this->damageEffect = thisx->colChkInfo.damageEffect; + Actor_SetDropFlag(thisx, &this->collider.elements[0].info, 0); + // Stun if Tektite hit by nut, boomerang, hookshot, ice arrow or ice magic + if ((thisx->colChkInfo.damageEffect == 1) || (thisx->colChkInfo.damageEffect == 0xF)) { + if (this->action != TEKTITE_STUNNED) { + Actor_SetColorFilter(thisx, 0, 0x78, 0, 0x50); + Actor_ApplyDamage(thisx); + EnTite_SetupStunned(this); + } + // Otherwise apply damage and handle death where necessary + } else { + if ((thisx->colorFilterTimer == 0) || ((thisx->colorFilterParams & 0x4000) == 0)) { + Actor_SetColorFilter(thisx, 0x4000, 0xFF, 0, 8); + Actor_ApplyDamage(thisx); + } + if (thisx->colChkInfo.health == 0) { + EnTite_SetupDeathCry(this); + } else { + // Flip tektite back up if it's on its back + Audio_PlayActorSound2(thisx, NA_SE_EN_TEKU_DAMAGE); + if (this->flipState != TEKTITE_FLIPPED) { + EnTite_SetupRecoil(this); + } else { + EnTite_SetupFlipUpright(this); + } + } + } + } + // If hammer has recently hit the floor and player is close to tektite, flip over + } else if ((thisx->colChkInfo.health != 0) && (globalCtx->actorCtx.unk_02 != 0) && + (thisx->xzDistToPlayer <= 400.0f) && (thisx->bgCheckFlags & 1)) { + if (this->flipState == TEKTITE_FLIPPED) { + EnTite_SetupFlipUpright(this); + } else if ((this->action >= TEKTITE_IDLE) || (this->action >= TEKTITE_IDLE)) { + if (1) {} + EnTite_SetupFlipOnBack(this); + } + } +} + +void EnTite_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTite* this = (EnTite*)thisx; + char pad[0x4]; + CollisionPoly* floorPoly; + WaterBox* waterBox; + f32 waterSurfaceY; + + EnTite_CheckDamage(thisx, globalCtx); + // Stay still if hit by immunity damage type this frame + if (thisx->colChkInfo.damageEffect != 0xE) { + this->actionFunc(this, globalCtx); + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, 25.0f, 40.0f, 20.0f, this->unk_2DC); + // If on water, snap feet to surface and spawn ripples + if ((this->actor.params == TEKTITE_BLUE) && (thisx->bgCheckFlags & 0x20)) { + floorPoly = thisx->floorPoly; + if ((((globalCtx->gameplayFrames % 8) == 0) || (thisx->velocity.y < 0.0f)) && + (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->backRightFootPos.x, + this->backRightFootPos.z, &waterSurfaceY, &waterBox)) && + (this->backRightFootPos.y <= waterSurfaceY)) { + this->backRightFootPos.y = waterSurfaceY; + EffectSsGRipple_Spawn(globalCtx, &this->backRightFootPos, 0, 220, 0); + } + if (((((globalCtx->gameplayFrames + 2) % 8) == 0) || (thisx->velocity.y < 0.0f)) && + (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->backLeftFootPos.x, + this->backLeftFootPos.z, &waterSurfaceY, &waterBox)) && + (this->backLeftFootPos.y <= waterSurfaceY)) { + this->backLeftFootPos.y = waterSurfaceY; + EffectSsGRipple_Spawn(globalCtx, &this->backLeftFootPos, 0, 220, 0); + } + if (((((globalCtx->gameplayFrames + 4) % 8) == 0) || (thisx->velocity.y < 0.0f)) && + (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->frontLeftFootPos.x, + this->frontLeftFootPos.z, &waterSurfaceY, &waterBox)) && + (this->frontLeftFootPos.y <= waterSurfaceY)) { + this->frontLeftFootPos.y = waterSurfaceY; + EffectSsGRipple_Spawn(globalCtx, &this->frontLeftFootPos, 0, 220, 0); + } + if (((((globalCtx->gameplayFrames + 1) % 8) == 0) || (thisx->velocity.y < 0.0f)) && + (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->frontRightFootPos.x, + this->frontRightFootPos.z, &waterSurfaceY, &waterBox)) && + (this->frontRightFootPos.y <= waterSurfaceY)) { + this->frontRightFootPos.y = waterSurfaceY; + EffectSsGRipple_Spawn(globalCtx, &this->frontRightFootPos, 0, 220, 0); + } + thisx->floorPoly = floorPoly; + } + + // If on ground and currently flipped over, set tektite to be fully upside-down + if (thisx->bgCheckFlags & 3) { + func_800359B8(thisx, thisx->shape.rot.y, &thisx->shape.rot); + if (this->flipState >= TEKTITE_FLIPPED) { + thisx->shape.rot.z += 0x7FFF; + } + // Otherwise ensure the tektite is rotating back upright + } else { + Math_SmoothStepToS(&thisx->shape.rot.x, 0, 1, 1000, 0); + if (this->flipState <= TEKTITE_UNFLIPPED) { + Math_SmoothStepToS(&thisx->shape.rot.z, 0, 1, 1000, 0); + if (thisx->shape.yOffset > 0) { + thisx->shape.yOffset -= 400.0f; + } + } + } + } + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 20.0f; + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnTite_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** limbDList, Vec3s* rot, void* thisx) { + EnTite* this = (EnTite*)thisx; + + switch (limbIndex) { + case 8: + Matrix_MultVec3f(&sFootOffset, &this->backRightFootPos); + break; + case 13: + Matrix_MultVec3f(&sFootOffset, &this->frontRightFootPos); + break; + case 18: + Matrix_MultVec3f(&sFootOffset, &this->backLeftFootPos); + break; + case 23: + Matrix_MultVec3f(&sFootOffset, &this->frontLeftFootPos); + break; + } + + BodyBreak_SetInfo(&this->bodyBreak, limbIndex, 0, 24, 24, limbDList, BODYBREAK_OBJECT_DEFAULT); +} + +void EnTite_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnTite* this = (EnTite*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tite.c", 1704); + func_80093D18(globalCtx->state.gfxCtx); + Collider_UpdateSpheres(0, &this->collider); + if (this->actor.params == TEKTITE_BLUE) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001300)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001700)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001900)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001B00)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(object_tite_Tex_001F00)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(object_tite_Tex_002100)); + } + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, NULL, EnTite_PostLimbDraw, + thisx); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tite.c", 1735); + + if (this->spawnIceTimer != 0) { + // Spawn chunks of ice all over the tektite's body + thisx->colorFilterTimer++; + this->spawnIceTimer--; + if ((this->spawnIceTimer & 3) == 0) { + Vec3f iceChunk; + s32 idx = this->spawnIceTimer >> 2; + + iceChunk.x = thisx->world.pos.x + sIceChunks[idx].x; + iceChunk.y = thisx->world.pos.y + sIceChunks[idx].y; + iceChunk.z = thisx->world.pos.z + sIceChunks[idx].z; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &iceChunk, 150, 150, 150, 250, 235, 245, 255, 1.0f); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.h b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.h new file mode 100755 index 000000000..308d0b4c3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tite/z_en_tite.h @@ -0,0 +1,38 @@ +#ifndef Z_EN_TITE_H +#define Z_EN_TITE_H + +#include "ultra64.h" +#include "global.h" + +struct EnTite; + +typedef void (*EnTiteActionFunc)(struct EnTite*, GlobalContext*); + +typedef enum { + /* -2 */ TEKTITE_BLUE = -2, + /* -1 */ TEKTITE_RED +} EnTiteType; + +typedef struct EnTite { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[25]; + /* 0x0226 */ Vec3s morphTable[25]; + /* 0x02BC */ u8 action; + /* 0x02BD */ u8 flipState; + /* 0x02C0 */ EnTiteActionFunc actionFunc; + /* 0x02C4 */ BodyBreak bodyBreak; + /* 0x02DC */ s32 unk_2DC; // flags related to bgCheck drawn effects + /* 0x02E0 */ s16 actionVar1; // Usage depends on current action function + /* 0x02E2 */ u8 actionVar2; // Usage depends on current action function + /* 0x02E3 */ u8 spawnIceTimer; + /* 0x02E4 */ u8 damageEffect; + /* 0x02E8 */ ColliderJntSph collider; + /* 0x0308 */ ColliderJntSphElement colliderItem; + /* 0x0348 */ Vec3f frontLeftFootPos; + /* 0x0354 */ Vec3f frontRightFootPos; + /* 0x0360 */ Vec3f backRightFootPos; + /* 0x036C */ Vec3f backLeftFootPos; +} EnTite; // size = 0x0378 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c new file mode 100644 index 000000000..1559b188c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.c @@ -0,0 +1,740 @@ +/* + * File: z_en_tk.c + * Overlay: ovl_En_Tk + * Description: Dampe NPC from "Dampe's Heart-Pounding Gravedigging Tour" + */ + +#include "z_en_tk.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_tk/object_tk.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnTk_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTk_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTk_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTk_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 EnTk_CheckNextSpot(EnTk* this, GlobalContext* globalCtx); +void EnTk_Rest(EnTk* this, GlobalContext* globalCtx); +void EnTk_Walk(EnTk* this, GlobalContext* globalCtx); +void EnTk_Dig(EnTk* this, GlobalContext* globalCtx); + +const ActorInit En_Tk_InitVars = { + ACTOR_EN_TK, + ACTORCAT_NPC, + FLAGS, + OBJECT_TK, + sizeof(EnTk), + (ActorFunc)EnTk_Init, + (ActorFunc)EnTk_Destroy, + (ActorFunc)EnTk_Update, + (ActorFunc)EnTk_Draw, + NULL, +}; + +void EnTkEff_Create(EnTk* this, Vec3f* pos, Vec3f* speed, Vec3f* accel, u8 duration, f32 size, f32 growth) { + s16 i; + EnTkEff* eff = this->eff; + + for (i = 0; i < ARRAY_COUNT(this->eff); i++) { + if (eff->active != 1) { + eff->size = size; + eff->growth = growth; + eff->timeTotal = eff->timeLeft = duration; + eff->active = 1; + eff->pos = *pos; + eff->accel = *accel; + eff->speed = *speed; + break; + } + eff++; + } +} + +void EnTkEff_Update(EnTk* this) { + s16 i; + EnTkEff* eff; + + eff = this->eff; + for (i = 0; i < ARRAY_COUNT(this->eff); i++) { + if (eff->active != 0) { + eff->timeLeft--; + if (eff->timeLeft == 0) { + eff->active = 0; + } + eff->accel.x = Rand_ZeroOne() * 0.4f - 0.2f; + eff->accel.z = Rand_ZeroOne() * 0.4f - 0.2f; + eff->pos.x += eff->speed.x; + eff->pos.y += eff->speed.y; + eff->pos.z += eff->speed.z; + eff->speed.x += eff->accel.x; + eff->speed.y += eff->accel.y; + eff->speed.z += eff->accel.z; + eff->size += eff->growth; + } + eff++; + } +} + +void EnTkEff_Draw(EnTk* this, GlobalContext* globalCtx) { + static void* dustTextures[] = { + gDust8Tex, gDust7Tex, gDust6Tex, gDust5Tex, gDust4Tex, gDust3Tex, gDust2Tex, gDust1Tex, + }; + + EnTkEff* eff = this->eff; + s16 imageIdx; + s16 gfxSetup; + s16 alpha; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tk_eff.c", 114); + + gfxSetup = 0; + + func_80093D84(globalCtx->state.gfxCtx); + + if (1) {} + + for (i = 0; i < ARRAY_COUNT(this->eff); i++) { + if (eff->active != 0) { + if (gfxSetup == 0) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gSPDisplayList(POLY_XLU_DISP++, gDampeEff1DL); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 60, 20, 0); + gfxSetup = 1; + } + + alpha = eff->timeLeft * (255.0f / eff->timeTotal); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 130, 90, alpha); + + gDPPipeSync(POLY_XLU_DISP++); + Matrix_Translate(eff->pos.x, eff->pos.y, eff->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(eff->size, eff->size, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_tk_eff.c", 140), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + imageIdx = eff->timeLeft * ((f32)ARRAY_COUNT(dustTextures) / eff->timeTotal); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTextures[imageIdx])); + + gSPDisplayList(POLY_XLU_DISP++, gDampeEff2DL); + } + eff++; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tk_eff.c", 154); +} + +s32 EnTkEff_CreateDflt(EnTk* this, Vec3f* pos, u8 duration, f32 size, f32 growth, f32 yAccelMax) { + Vec3f speed = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + + accel.y += Rand_ZeroOne() * yAccelMax; + + EnTkEff_Create(this, pos, &speed, &accel, duration, size, growth); + + return 0; +} + +/** z_en_tk_eff.c ends here probably **/ + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 30, 52, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +void EnTk_RestAnim(EnTk* this, GlobalContext* globalCtx) { + AnimationHeader* anim = &gDampeRestAnim; + + Animation_Change(&this->skelAnime, anim, 1.0f, 0.0f, Animation_GetLastFrame(&gDampeRestAnim), ANIMMODE_LOOP, + -10.0f); + + this->actionCountdown = Rand_S16Offset(60, 60); + this->actor.speedXZ = 0.0f; +} + +void EnTk_WalkAnim(EnTk* this, GlobalContext* globalCtx) { + AnimationHeader* anim = &gDampeWalkAnim; + + Animation_Change(&this->skelAnime, anim, 1.0f, 0.0f, Animation_GetLastFrame(&gDampeRestAnim), ANIMMODE_LOOP, + -10.0f); + + this->actionCountdown = Rand_S16Offset(240, 240); +} + +void EnTk_DigAnim(EnTk* this, GlobalContext* globalCtx) { + AnimationHeader* anim = &gDampeDigAnim; + + Animation_Change(&this->skelAnime, anim, 1.0f, 0.0f, Animation_GetLastFrame(&gDampeDigAnim), ANIMMODE_LOOP, -10.0f); + + if (EnTk_CheckNextSpot(this, globalCtx) >= 0) { + this->validDigHere = 1; + } +} + +void EnTk_UpdateEyes(EnTk* this) { + if (DECR(this->blinkCountdown) == 0) { + this->eyeTextureIdx++; + if (this->eyeTextureIdx > 2) { + this->blinkCycles--; + if (this->blinkCycles < 0) { + this->blinkCountdown = Rand_S16Offset(30, 30); + this->blinkCycles = 2; + if (Rand_ZeroOne() > 0.5f) { + this->blinkCycles++; + } + } + this->eyeTextureIdx = 0; + } + } +} + +s32 EnTk_CheckFacingPlayer(EnTk* this) { + s16 v0; + s16 v1; + + if (this->actor.xyzDistToPlayerSq > 10000.0f) { + return 0; + } + + v0 = this->actor.shape.rot.y; + v0 -= this->h_21E; + v0 -= this->headRot; + + v1 = this->actor.yawTowardsPlayer - v0; + if (ABS(v1) < 0x1554) { + return 1; + } else { + return 0; + } +} + +s32 EnTk_CheckNextSpot(EnTk* this, GlobalContext* globalCtx) { + Actor* prop; + f32 dxz; + f32 dy; + + prop = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + + while (prop != NULL) { + if (prop->id != ACTOR_EN_IT) { + prop = prop->next; + continue; + } + + if (prop == this->currentSpot) { + prop = prop->next; + continue; + } + + dy = prop->world.pos.y - this->actor.floorHeight; + dxz = Actor_WorldDistXZToActor(&this->actor, prop); + if (dxz > 40.0f || dy > 10.0f) { + prop = prop->next; + continue; + } + + this->currentSpot = prop; + return prop->params; + } + + return -1; +} + +void EnTk_CheckCurrentSpot(EnTk* this) { + f32 dxz; + f32 dy; + + if (this->currentSpot != NULL) { + dy = this->currentSpot->world.pos.y - this->actor.floorHeight; + dxz = Actor_WorldDistXZToActor(&this->actor, this->currentSpot); + if (dxz > 40.0f || dy > 10.0f) { + this->currentSpot = NULL; + } + } +} + +f32 EnTk_Step(EnTk* this, GlobalContext* globalCtx) { + f32 stepFrames[] = { 36.0f, 10.0f }; + f32 a1_; + s32 i; + + if (this->skelAnime.curFrame == 0.0f || this->skelAnime.curFrame == 25.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_MORIBLIN_WALK); + } + + if (this->skelAnime.animation != &gDampeWalkAnim) { + return 0.0f; + } + + a1_ = this->skelAnime.curFrame; + for (i = 0; i < ARRAY_COUNT(stepFrames); i++) { + if (a1_ < stepFrames[i] + 12.0f && a1_ >= stepFrames[i]) { + break; + } + } + if (i >= ARRAY_COUNT(stepFrames)) { + return 0.0f; + } else { + a1_ = (0x8000 / 12.0f) * (a1_ - stepFrames[i]); + return Math_SinS(a1_) * 2.0f; + } +} + +s32 EnTk_Orient(EnTk* this, GlobalContext* globalCtx) { + Path* path; + Vec3s* point; + f32 dx; + f32 dz; + + if (this->actor.params < 0) { + return 1; + } + + path = &globalCtx->setupPathList[0]; + point = SEGMENTED_TO_VIRTUAL(path->points); + point += this->currentWaypoint; + + dx = point->x - this->actor.world.pos.x; + dz = point->z - this->actor.world.pos.z; + + Math_SmoothStepToS(&this->actor.shape.rot.y, Math_FAtan2F(dx, dz) * (0x8000 / M_PI), 10, 1000, 1); + this->actor.world.rot = this->actor.shape.rot; + + if (SQ(dx) + SQ(dz) < 10.0f) { + this->currentWaypoint++; + if (this->currentWaypoint >= path->count) { + this->currentWaypoint = 0; + } + + return 0; + } else { + return 1; + } +} + +u16 func_80B1C54C(GlobalContext* globalCtx, Actor* thisx) { + u16 ret; + + ret = Text_GetFaceReaction(globalCtx, 14); + if (ret != 0) { + return ret; + } + + if (gSaveContext.infTable[13] & 0x0200) { + /* "Do you want me to dig here? ..." */ + return 0x5019; + } else { + /* "Hey kid! ..." */ + return 0x5018; + } +} + +s16 func_80B1C5A0(GlobalContext* globalCtx, Actor* thisx) { + s32 ret = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + break; + case TEXT_STATE_CLOSING: + /* "I am the boss of the carpenters ..." (wtf?) */ + if (thisx->textId == 0x5028) { + gSaveContext.infTable[13] |= 0x0100; + } + ret = 0; + break; + case TEXT_STATE_DONE_FADING: + break; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx) && (thisx->textId == 0x5018 || thisx->textId == 0x5019)) { + if (globalCtx->msgCtx.choiceIndex == 1) { + /* "Thanks a lot!" */ + thisx->textId = 0x0084; + } else if (gSaveContext.rupees < 10) { + /* "You don't have enough Rupees!" */ + thisx->textId = 0x0085; + } else { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + Rupees_ChangeBy(-10); + gSaveContext.infTable[13] |= 0x0200; + return 2; + } + Message_ContinueTextbox(globalCtx, thisx->textId); + gSaveContext.infTable[13] |= 0x0200; + } + break; + case TEXT_STATE_EVENT: + if (Message_ShouldAdvance(globalCtx) && (thisx->textId == 0x0084 || thisx->textId == 0x0085)) { + Message_CloseTextbox(globalCtx); + ret = 0; + } + break; + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + break; + } + + return ret; +} + +s32 EnTk_ChooseReward(EnTk* this) { + f32 luck; + s32 reward; + + luck = Rand_ZeroOne(); + + if (luck < 0.4f) { + reward = 0; + } else if (luck < 0.7) { + reward = 1; + } else if (luck < 0.9) { + reward = 2; + } else { + reward = 3; + } + + switch (reward) { + case 0: + if (this->rewardCount[0] < 8) { + this->rewardCount[0] += 1; + return reward; + } + break; + case 1: + if (this->rewardCount[1] < 4) { + this->rewardCount[1] += 1; + return reward; + } + break; + case 2: + if (this->rewardCount[2] < 2) { + this->rewardCount[2] += 1; + return reward; + } + break; + case 3: + if (this->rewardCount[3] < 1) { + this->rewardCount[3] += 1; + return reward; + } + break; + } + + if (this->rewardCount[0] < 8) { + this->rewardCount[0] += 1; + reward = 0; + } else if (this->rewardCount[1] < 4) { + this->rewardCount[1] += 1; + reward = 1; + } else if (this->rewardCount[2] < 2) { + this->rewardCount[2] += 1; + reward = 2; + } else if (this->rewardCount[3] < 1) { + this->rewardCount[3] += 1; + reward = 3; + } else { + reward = 0; + this->rewardCount[0] = 1; + this->rewardCount[1] = 0; + this->rewardCount[2] = 0; + this->rewardCount[3] = 0; + } + + return reward; +} + +void EnTk_DigEff(EnTk* this) { + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + Vec3f speed = { 0.0f, 0.0f, 0.0f }; + Vec3f accel = { 0.0f, 0.3f, 0.0f }; + + if (this->skelAnime.curFrame >= 32.0f && this->skelAnime.curFrame < 40.0f) { + pos.x = (Rand_ZeroOne() - 0.5f) * 12.0f + this->v3f_304.x; + pos.y = (Rand_ZeroOne() - 0.5f) * 8.0f + this->v3f_304.y; + pos.z = (Rand_ZeroOne() - 0.5f) * 12.0f + this->v3f_304.z; + EnTkEff_CreateDflt(this, &pos, 12, 0.2f, 0.1f, 0.0f); + } +} + +void EnTk_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTk* this = (EnTk*)thisx; + s32 pad; + + ActorShape_Init(&this->actor.shape, 0, ActorShadow_DrawCircle, 24.0f); + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gDampeSkel, NULL, this->jointTable, this->morphTable, 18); + Animation_Change(&this->skelAnime, &gDampeRestAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gDampeRestAnim), + ANIMMODE_LOOP, 0.0f); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + + if (gSaveContext.dayTime <= 0xC000 || gSaveContext.dayTime >= 0xE000 || !!LINK_IS_ADULT || + globalCtx->sceneNum != SCENE_SPOT02) { + Actor_Kill(&this->actor); + return; + } + + Actor_SetScale(&this->actor, 0.01f); + + this->actor.targetMode = 6; + this->actor.gravity = -0.1f; + this->currentReward = -1; + this->currentSpot = NULL; + this->actionFunc = EnTk_Rest; +} + +void EnTk_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTk* this = (EnTk*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnTk_Rest(EnTk* this, GlobalContext* globalCtx) { + s16 v1; + s16 a1_; + + if (this->h_1E0 != 0) { + v1 = this->actor.shape.rot.y; + v1 -= this->h_21E; + v1 = this->actor.yawTowardsPlayer - v1; + + if (this->h_1E0 == 2) { + EnTk_DigAnim(this, globalCtx); + this->h_1E0 = 0; + this->actionFunc = EnTk_Dig; + return; + } + + func_800343CC(globalCtx, &this->actor, &this->h_1E0, this->collider.dim.radius + 30.0f, func_80B1C54C, + func_80B1C5A0); + } else if (EnTk_CheckFacingPlayer(this)) { + v1 = this->actor.shape.rot.y; + v1 -= this->h_21E; + v1 = this->actor.yawTowardsPlayer - v1; + + this->actionCountdown = 0; + func_800343CC(globalCtx, &this->actor, &this->h_1E0, this->collider.dim.radius + 30.0f, func_80B1C54C, + func_80B1C5A0); + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + v1 = this->actor.shape.rot.y; + v1 -= this->h_21E; + v1 = this->actor.yawTowardsPlayer - v1; + + this->actionCountdown = 0; + this->h_1E0 = 1; + } else if (DECR(this->actionCountdown) == 0) { + EnTk_WalkAnim(this, globalCtx); + this->actionFunc = EnTk_Walk; + + /*! @bug v1 is uninitialized past this branch */ + } else { + v1 = 0; + } + + a1_ = CLAMP(-v1, 1270, 10730); + Math_SmoothStepToS(&this->headRot, a1_, 6, 1000, 1); +} + +void EnTk_Walk(EnTk* this, GlobalContext* globalCtx) { + if (this->h_1E0 == 2) { + EnTk_DigAnim(this, globalCtx); + this->h_1E0 = 0; + this->actionFunc = EnTk_Dig; + } else { + this->actor.speedXZ = EnTk_Step(this, globalCtx); + EnTk_Orient(this, globalCtx); + Math_SmoothStepToS(&this->headRot, 0, 6, 1000, 1); + EnTk_CheckCurrentSpot(this); + + DECR(this->actionCountdown); + if (EnTk_CheckFacingPlayer(this) || this->actionCountdown == 0) { + EnTk_RestAnim(this, globalCtx); + this->actionFunc = EnTk_Rest; + } + } +} + +void EnTk_Dig(EnTk* this, GlobalContext* globalCtx) { + Vec3f rewardOrigin; + Vec3f rewardPos; + s32 rewardParams[] = { + ITEM00_RUPEE_GREEN, ITEM00_RUPEE_BLUE, ITEM00_RUPEE_RED, ITEM00_RUPEE_PURPLE, ITEM00_HEART_PIECE, + }; + + EnTk_DigEff(this); + + if (this->skelAnime.curFrame == 32.0f) { + /* What's gonna come out? */ + Audio_PlayActorSound2(&this->actor, NA_SE_EV_DIG_UP); + + this->rewardTimer = 0; + + if (this->validDigHere == 1) { + rewardOrigin.x = 0.0f; + rewardOrigin.y = 0.0f; + rewardOrigin.z = -40.0f; + + Matrix_RotateY(this->actor.shape.rot.y, MTXMODE_NEW); + Matrix_MultVec3f(&rewardOrigin, &rewardPos); + + rewardPos.x += this->actor.world.pos.x; + rewardPos.y += this->actor.world.pos.y; + rewardPos.z += this->actor.world.pos.z; + + this->currentReward = EnTk_ChooseReward(this); + if (this->currentReward == 3) { + /* + * Upgrade the purple rupee reward to the heart piece if this + * is the first grand prize dig. + */ + if (!(gSaveContext.itemGetInf[1] & 0x1000)) { + gSaveContext.itemGetInf[1] |= 0x1000; + this->currentReward = 4; + } + } + + Item_DropCollectible(globalCtx, &rewardPos, rewardParams[this->currentReward]); + } + } + + if (this->skelAnime.curFrame >= 32.0f && this->rewardTimer == 10) { + /* Play a reward sound shortly after digging */ + if (this->validDigHere == 0) { + /* Bad dig spot */ + Audio_PlayActorSound2(&this->actor, NA_SE_SY_ERROR); + } else if (this->currentReward == 4) { + /* Heart piece */ + Audio_PlaySoundGeneral(NA_SE_SY_CORRECT_CHIME, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + /* Rupee */ + Audio_PlayActorSound2(&this->actor, NA_SE_SY_TRE_BOX_APPEAR); + } + } + this->rewardTimer++; + + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + if (this->currentReward < 0) { + /* "Nope, nothing here!" */ + Message_StartTextbox(globalCtx, 0x501A, NULL); + } else { + Message_CloseTextbox(globalCtx); + } + + EnTk_RestAnim(this, globalCtx); + + this->currentReward = -1; + this->validDigHere = 0; + this->actionFunc = EnTk_Rest; + } +} + +void EnTk_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTk* this = (EnTk*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + SkelAnime_Update(&this->skelAnime); + + Actor_MoveForward(&this->actor); + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 40.0f, 10.0f, 0.0f, 5); + + this->actionFunc(this, globalCtx); + + EnTkEff_Update(this); + + EnTk_UpdateEyes(this); +} + +void func_80B1D200(GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tk.c", 1188); + + gSPDisplayList(POLY_OPA_DISP++, gDampeShovelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tk.c", 1190); +} + +s32 EnTk_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnTk* this = (EnTk*)thisx; + + switch (limbIndex) { + /* Limb 15 - Head */ + case 15: + this->h_21E = rot->y; + break; + /* Limb 16 - Jaw */ + case 16: + this->h_21E += rot->y; + rot->y += this->headRot; + break; + } + + return false; +} + +void EnTk_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnTk* this = (EnTk*)thisx; + Vec3f sp28 = { 0.0f, 0.0f, 4600.0f }; + Vec3f sp1C = { 0.0f, 0.0f, 0.0f }; + + /* Limb 16 - Jaw */ + if (limbIndex == 16) { + Matrix_MultVec3f(&sp1C, &this->actor.focus.pos); + } + + /* Limb 14 - Neck */ + if (limbIndex == 14) { + Matrix_MultVec3f(&sp28, &this->v3f_304); + func_80B1D200(globalCtx); + } +} + +void EnTk_Draw(Actor* thisx, GlobalContext* globalCtx) { + static void* sEyesSegments[] = { + gDampeEyeOpenTex, + gDampeEyeHalfTex, + gDampeEyeClosedTex, + }; + EnTk* this = (EnTk*)thisx; + + Matrix_Push(); + EnTkEff_Draw(this, globalCtx); + Matrix_Pop(); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tk.c", 1294); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyesSegments[this->eyeTextureIdx])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnTk_OverrideLimbDraw, EnTk_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tk.c", 1312); +} diff --git a/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h new file mode 100644 index 000000000..cf871ce3b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tk/z_en_tk.h @@ -0,0 +1,52 @@ +#ifndef Z_EN_TK_H +#define Z_EN_TK_H + +#include "ultra64.h" +#include "global.h" + +/* Dirt particle effect */ +struct EnTkEff; + +typedef struct EnTkEff { + /* 0x0000 */ u8 active; + /* 0x0001 */ u8 timeLeft; + /* 0x0002 */ u8 timeTotal; + /* 0x0004 */ f32 size; + /* 0x0008 */ f32 growth; + /* 0x000C */ char unk_C[0x8]; + /* 0x0014 */ Vec3f pos; + /* 0x0020 */ Vec3f speed; + /* 0x002C */ Vec3f accel; +} EnTkEff; // size = 0x0038 + +struct EnTk; + +typedef void (*EnTkActionFunc)(struct EnTk*, GlobalContext*); + +typedef struct EnTk { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnTkActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s16 h_1E0; + /* 0x01E2 */ char unk_1E2[0x26]; + /* 0x0208 */ u8 validDigHere; + /* 0x0209 */ u8 rewardCount[4]; + /* 0x0210 */ Actor* currentSpot; + /* 0x0214 */ s32 currentReward; + /* 0x0218 */ s16 blinkCycles; + /* 0x021A */ s16 rewardTimer; + /* 0x021C */ s16 actionCountdown; + /* 0x021E */ s16 h_21E; + /* 0x0220 */ char unk_220[0x2]; + /* 0x0222 */ s16 eyeTextureIdx; + /* 0x0224 */ s16 blinkCountdown; + /* 0x0226 */ s16 headRot; + /* 0x0228 */ s16 currentWaypoint; + /* 0x022A */ Vec3s jointTable[18]; + /* 0x0296 */ Vec3s morphTable[18]; + /* 0x0304 */ Vec3f v3f_304; + /* 0x0310 */ EnTkEff eff[20]; +} EnTk; // size = 0x0770 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Torch/z_en_torch.c b/soh/src/overlays/actors/ovl_En_Torch/z_en_torch.c new file mode 100644 index 000000000..d8bf2f653 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Torch/z_en_torch.c @@ -0,0 +1,41 @@ +/* + * File: z_en_torch.c + * Overlay: ovl_En_Torch + * Description: Spawns a chest with the appropriate contents then unloads. Used in grottos. + */ + +#include "z_en_torch.h" + +#define FLAGS 0 + +void EnTorch_Init(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Torch_InitVars = { + ACTOR_EN_TORCH, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnTorch), + (ActorFunc)EnTorch_Init, + NULL, + NULL, + NULL, + NULL, +}; + +static u8 sChestContents[] = { + GI_RUPEE_BLUE, GI_RUPEE_RED, GI_RUPEE_GOLD, GI_BOMBS_20, GI_BOMBS_1, GI_BOMBS_1, GI_BOMBS_1, GI_BOMBS_1, +}; + +void EnTorch_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTorch* this = (EnTorch*)thisx; + s8 returnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data; + + /* Spawn chest with desired contents. + Contents are passed to en_torch from grotto params via Save Context. */ + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOX, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, + (sChestContents[(returnData >> 0x5) & 0x7] << 0x5) | 0x5000 | (returnData & 0x1F)); + + Actor_Kill(&this->actor); +} diff --git a/soh/src/overlays/actors/ovl_En_Torch/z_en_torch.h b/soh/src/overlays/actors/ovl_En_Torch/z_en_torch.h new file mode 100644 index 000000000..78be1fb2d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Torch/z_en_torch.h @@ -0,0 +1,13 @@ +#ifndef Z_EN_TORCH_H +#define Z_EN_TORCH_H + +#include "ultra64.h" +#include "global.h" + +struct EnTorch; + +typedef struct EnTorch { + /* 0x0000 */ Actor actor; +} EnTorch; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c new file mode 100644 index 000000000..8601b8823 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -0,0 +1,794 @@ +/* + * File: z_en_torch2.c + * Overlay: ovl_En_Torch2 + * Description: Dark Link + */ + +#include "z_en_torch2.h" +#include "objects/object_torch2/object_torch2.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_5) + +typedef enum { + /* 0 */ ENTORCH2_WAIT, + /* 1 */ ENTORCH2_ATTACK, + /* 2 */ ENTORCH2_DEATH, + /* 3 */ ENTORCH2_DAMAGE +} EnTorch2ActionStates; + +typedef enum { + /* 0 */ FORWARD_SLASH_1H, + /* 1 */ FORWARD_SLASH_2H, + /* 2 */ FORWARD_COMBO_1H, + /* 3 */ FORWARD_COMBO_2H, + /* 4 */ RIGHT_SLASH_1H, + /* 5 */ RIGHT_SLASH_2H, + /* 6 */ RIGHT_COMBO_1H, + /* 7 */ RIGHT_COMBO_2H, + /* 8 */ LEFT_SLASH_1H, + /* 9 */ LEFT_SLASH_2H, + /* 10 */ LEFT_COMBO_1H, + /* 11 */ LEFT_COMBO_2H, + /* 12 */ STAB_1H, + /* 13 */ STAB_2H, + /* 14 */ STAB_COMBO_1H, + /* 15 */ STAB_COMBO_2H, + /* 16 */ FLIPSLASH_START, + /* 17 */ JUMPSLASH_START, + /* 18 */ FLIPSLASH_FINISH, + /* 19 */ JUMPSLASH_FINISH, + /* 20 */ BACKSLASH_RIGHT, + /* 21 */ BACKSLASH_LEFT, + /* 22 */ HAMMER_FORWARD, + /* 23 */ HAMMER_SIDE, + /* 24 */ SPIN_ATTACK_1H, + /* 25 */ SPIN_ATTACK_2H, + /* 26 */ BIG_SPIN_1H, + /* 27 */ BIG_SPIN_2H +} PlayerSwordAnimation; + +void EnTorch2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTorch2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTorch2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTorch2_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Torch2_InitVars = { + ACTOR_EN_TORCH2, + ACTORCAT_BOSS, + FLAGS, + OBJECT_TORCH2, + sizeof(Player), + (ActorFunc)EnTorch2_Init, + (ActorFunc)EnTorch2_Destroy, + (ActorFunc)EnTorch2_Update, + (ActorFunc)EnTorch2_Draw, + NULL, +}; + +static f32 sStickTilt = 0.0f; +static s16 sStickAngle = 0; +static f32 sSwordJumpHeight = 0.0f; +static s32 sHoldShieldTimer = 0; +static u8 sZTargetFlag = false; +static u8 sDeathFlag = false; + +static Input sInput; +static u8 sSwordJumpState; +static Vec3f sSpawnPoint; +static u8 sJumpslashTimer; +static u8 sJumpslashFlag; +static u8 sActionState; +static u8 sSwordJumpTimer; +static u8 sCounterState; +static u8 sDodgeRollState; +static u8 sStaggerCount; +static u8 sStaggerTimer; +static s8 sLastSwordAnim; +static u8 sAlpha; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(2, 0xE), + /* Ice magic */ DMG_ENTRY(0, 0x6), + /* Light magic */ DMG_ENTRY(3, 0xD), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +void EnTorch2_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Player* this = (Player*)thisx; + + sInput.cur.button = sInput.press.button = sInput.rel.button = 0; + sInput.cur.stick_x = sInput.cur.stick_y = 0; + this->currentShield = PLAYER_SHIELD_HYLIAN; + this->heldItemActionParam = this->heldItemId = PLAYER_AP_SWORD_MASTER; + Player_SetModelGroup(this, 2); + globalCtx->playerInit(this, globalCtx, &gDarkLinkSkel); + this->actor.naviEnemyId = 0x26; + this->cylinder.base.acFlags = AC_ON | AC_TYPE_PLAYER; + this->swordQuads[0].base.atFlags = this->swordQuads[1].base.atFlags = AT_ON | AT_TYPE_ENEMY; + this->swordQuads[0].base.acFlags = this->swordQuads[1].base.acFlags = AC_ON | AC_HARD | AC_TYPE_PLAYER; + this->swordQuads[0].base.colType = this->swordQuads[1].base.colType = COLTYPE_METAL; + this->swordQuads[0].info.toucher.damage = this->swordQuads[1].info.toucher.damage = 8; + this->swordQuads[0].info.bumperFlags = this->swordQuads[1].info.bumperFlags = BUMP_ON; + this->shieldQuad.base.atFlags = AT_ON | AT_TYPE_ENEMY; + this->shieldQuad.base.acFlags = AC_ON | AC_HARD | AC_TYPE_PLAYER; + this->actor.colChkInfo.damageTable = &sDamageTable; + this->actor.colChkInfo.health = gSaveContext.healthCapacity >> 3; + this->actor.colChkInfo.cylRadius = 60; + this->actor.colChkInfo.cylHeight = 100; + globalCtx->func_11D54(this, globalCtx); + + sActionState = ENTORCH2_WAIT; + sDodgeRollState = 0; + sSwordJumpHeight = 0.0f; + sSwordJumpState = 0; + sJumpslashTimer = 0; + sJumpslashFlag = false; + sCounterState = sStaggerTimer = sStaggerCount = 0; + sLastSwordAnim = 0; + sAlpha = 95; + sSpawnPoint = this->actor.home.pos; +} + +void EnTorch2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + Player* this = (Player*)thisx; + + Effect_Delete(globalCtx, this->swordEffectIndex); + func_800F5B58(); + Collider_DestroyCylinder(globalCtx, &this->cylinder); + Collider_DestroyQuad(globalCtx, &this->swordQuads[0]); + Collider_DestroyQuad(globalCtx, &this->swordQuads[1]); + Collider_DestroyQuad(globalCtx, &this->shieldQuad); +} + +Actor* EnTorch2_GetAttackItem(GlobalContext* globalCtx, Player* this) { + Actor* rangedItem = Actor_GetProjectileActor(globalCtx, &this->actor, 4000.0f); + + if (rangedItem != NULL) { + return rangedItem; + } else { + return func_80033684(globalCtx, &this->actor); + } +} + +s32 EnTorch2_SwingSword(GlobalContext* globalCtx, Input* input, Player* this) { + f32 noAttackChance = 0.0f; + s32 attackDelay = 7; + Player* player = GET_PLAYER(globalCtx); + + if ((this->linearVelocity < 0.0f) || (player->linearVelocity < 0.0f)) { + return 0; + } + if (gSaveContext.health < 0x50) { + attackDelay = 15; + noAttackChance += 0.3f; + } + if (sAlpha != 255) { + noAttackChance += 2.0f; + } + if ((((globalCtx->gameplayFrames & attackDelay) == 0) || (sSwordJumpState != 0)) && + (noAttackChance <= Rand_ZeroOne())) { + if (sSwordJumpState == 0) { + switch ((s32)(Rand_ZeroOne() * 7.0f)) { + case 1: + case 5: + sStickAngle += 0x4000; + sStickTilt = 127.0f; + break; + case 2: + case 6: + sStickAngle -= 0x4000; + sStickTilt = 127.0f; + break; + } + } + input->cur.button = BTN_B; + return 1; + } + return 0; +} + +void EnTorch2_Backflip(Player* this, Input* input, Actor* thisx) { + thisx->world.rot.y = thisx->shape.rot.y = thisx->yawTowardsPlayer; + sStickAngle = thisx->yawTowardsPlayer + 0x8000; + sStickTilt = 127.0f; + sZTargetFlag = true; + input->cur.button = BTN_A; + this->invincibilityTimer = 10; + sCounterState = 0; +} + +void EnTorch2_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Player* player2 = GET_PLAYER(globalCtx2); + Player* player = player2; + Player* this = (Player*)thisx; + Input* input = &sInput; + Camera* camera; + s16 sp66; + u8 staggerThreshold; + s8 stickY; + s32 pad60; + Actor* attackItem; + s16 sp5A; + s16 pad58; + u32 pad54; + f32 sp50; + s16 sp4E; + + sp5A = player->actor.shape.rot.y - this->actor.shape.rot.y; + input->cur.button = 0; + camera = Gameplay_GetCamera(globalCtx, 0); + attackItem = EnTorch2_GetAttackItem(globalCtx, this); + switch (sActionState) { + case ENTORCH2_WAIT: + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->skelAnime.curFrame = 0.0f; + this->skelAnime.playSpeed = 0.0f; + this->actor.world.pos.x = (Math_SinS(this->actor.world.rot.y) * 25.0f) + sSpawnPoint.x; + this->actor.world.pos.z = (Math_CosS(this->actor.world.rot.y) * 25.0f) + sSpawnPoint.z; + if ((this->actor.xzDistToPlayer <= 120.0f) || Actor_IsTargeted(globalCtx, &this->actor) || + (attackItem != NULL)) { + if (attackItem != NULL) { + sDodgeRollState = 1; + sStickAngle = this->actor.yawTowardsPlayer; + sStickTilt = 127.0f; + input->cur.button = BTN_A; + sZTargetFlag = false; + sp66 = camera->camDir.y - sStickAngle; + sInput.cur.stick_x = sStickTilt * Math_SinS(sp66); + stickY = sStickTilt * Math_CosS(sp66); + if (stickY) {} + sInput.cur.stick_y = stickY; + } + func_800F5ACC(NA_BGM_MINI_BOSS); + sActionState = ENTORCH2_ATTACK; + } + break; + + case ENTORCH2_ATTACK: + sStickTilt = 0.0f; + + // Handles Dark Link's sword clanking on Link's sword + + if ((this->swordQuads[0].base.acFlags & AC_BOUNCED) || (this->swordQuads[1].base.acFlags & AC_BOUNCED)) { + this->swordQuads[0].base.acFlags &= ~AC_BOUNCED; + this->swordQuads[1].base.acFlags &= ~AC_BOUNCED; + this->swordQuads[0].base.atFlags |= AT_BOUNCED; + this->swordQuads[1].base.atFlags |= AT_BOUNCED; + this->cylinder.base.acFlags &= ~AC_HIT; + + if (sLastSwordAnim != this->swordAnimation) { + sStaggerCount++; + sLastSwordAnim = this->swordAnimation; + } + /*! @bug + * This code is needed to reset sCounterState, and should run regardless + * of how much health Link has. Without it, sCounterState stays at 2 until + * something else resets it, preventing Dark Link from using his shield and + * creating a hole in his defenses. This also makes Dark Link harder at low + * health, while the other health checks are intended to make him easier. + */ + if ((gSaveContext.health < 0x50) && (sCounterState != 0)) { + sCounterState = 0; + sStaggerTimer = 50; + } + } + if ((sCounterState != 0) && (this->swordState != 0)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->swordQuads[0].base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->swordQuads[1].base); + } + + // Ignores hits when jumping on Link's sword + if ((this->invincibilityTimer < 0) && (sActionState != ENTORCH2_DAMAGE) && + (this->cylinder.base.acFlags & AC_HIT)) { + this->cylinder.base.acFlags &= ~AC_HIT; + } + + // Handles Dark Link rolling to dodge item attacks + + if (sDodgeRollState != 0) { + sStickTilt = 127.0f; + } else if (attackItem != NULL) { + sDodgeRollState = 1; + sStickAngle = this->actor.yawTowardsPlayer; + sStickTilt = 127.0f; + input->cur.button = BTN_A; + } else if (sJumpslashTimer == 0) { + + // Handles Dark Link's initial reaction to jumpslashes + + if (((player->swordState != 0) || (player->actor.velocity.y > -3.0f)) && + (player->swordAnimation == JUMPSLASH_START)) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + + if (globalCtx->gameplayFrames % 2) { + sStickAngle = this->actor.yawTowardsPlayer + 0x4000; + } else { + sStickAngle = this->actor.yawTowardsPlayer - 0x4000; + } + sStickTilt = 127.0f; + sJumpslashTimer = 15; + sJumpslashFlag = false; + input->cur.button |= BTN_A; + + // Handles jumping on Link's sword + + } else if (sSwordJumpState != 0) { + sStickTilt = 0.0f; + player->stateFlags3 |= 4; + Math_SmoothStepToF(&this->actor.world.pos.x, + (Math_SinS(player->actor.shape.rot.y - 0x3E8) * 45.0f) + + player->actor.world.pos.x, + 1.0f, 5.0f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.z, + (Math_CosS(player->actor.shape.rot.y - 0x3E8) * 45.0f) + + player->actor.world.pos.z, + 1.0f, 5.0f, 0.0f); + sSwordJumpTimer--; + if (((u32)sSwordJumpTimer == 0) || ((player->invincibilityTimer > 0) && (this->swordState == 0))) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + input->cur.button = BTN_A; + player->stateFlags3 &= ~4; + sStickTilt = 127.0f; + player->skelAnime.curFrame = 3.0f; + sStickAngle = this->actor.yawTowardsPlayer + 0x8000; + sSwordJumpTimer = sSwordJumpState = 0; + this->actor.flags |= ACTOR_FLAG_0; + } else if (sSwordJumpState == 1) { + if (sSwordJumpTimer < 16) { + EnTorch2_SwingSword(globalCtx, input, this); + sSwordJumpState++; + } else if (sSwordJumpTimer == 19) { + func_800F4190(&this->actor.projectedPos, NA_SE_VO_LI_AUTO_JUMP); + } + } + } else { + // This does nothing, as sHoldShieldTimer is never set. + if (sHoldShieldTimer != 0) { + sHoldShieldTimer--; + input->cur.button = BTN_R; + } + + // Handles Dark Link's reaction to sword attack other than jumpslashes + + if (func_800354B4(globalCtx, &this->actor, 120.0f, 0x7FFF, 0x7FFF, this->actor.world.rot.y)) { + if ((player->swordAnimation == STAB_1H) && (this->actor.xzDistToPlayer < 90.0f)) { + + // Handles the reaction to a one-handed stab. If the conditions are satisfied, + // Dark Link jumps on Link's sword. Otherwise he backflips away. + + if ((this->swordState == 0) && (sCounterState == 0) && (player->invincibilityTimer == 0) && + (player->swordAnimation == STAB_1H) && (this->actor.xzDistToPlayer <= 85.0f) && + Actor_IsTargeted(globalCtx, &this->actor)) { + + sStickTilt = 0.0f; + sSwordJumpState = 1; + player->stateFlags3 |= 4; + this->actor.flags &= ~ACTOR_FLAG_0; + sSwordJumpTimer = 27; + player->swordState = 0; + player->linearVelocity = 0.0f; + this->invincibilityTimer = -7; + this->linearVelocity = 0.0f; + player->skelAnime.curFrame = 2.0f; + LinkAnimation_Update(globalCtx, &player->skelAnime); + sHoldShieldTimer = 0; + input->cur.button = BTN_A; + } else { + EnTorch2_Backflip(this, input, &this->actor); + } + } else { + + // Handles reactions to all other sword attacks + + sStickAngle = thisx->yawTowardsPlayer; + input->cur.button = BTN_B; + + if (player->swordAnimation <= FORWARD_COMBO_2H) { + sStickTilt = 0.0f; + } else if (player->swordAnimation <= RIGHT_COMBO_2H) { + sStickTilt = 127.0f; + sStickAngle += 0x4000; + } else if (player->swordAnimation <= LEFT_COMBO_2H) { + sStickTilt = 127.0f; + sStickAngle -= 0x4000; + } else if (player->swordAnimation <= HAMMER_SIDE) { + input->cur.button = BTN_R; + } else if (player->swordAnimation <= BIG_SPIN_2H) { + EnTorch2_Backflip(this, input, &this->actor); + } else { + EnTorch2_Backflip(this, input, &this->actor); + } + if (!CHECK_BTN_ANY(input->cur.button, BTN_A | BTN_R) && (this->swordState == 0) && + (player->swordState != 0)) { + sCounterState = 1; + } + } + } else { + + // Handles movement and attacks when not reacting to Link's actions + + sStickAngle = thisx->yawTowardsPlayer; + sp50 = 0.0f; + if ((90.0f >= this->actor.xzDistToPlayer) && (this->actor.xzDistToPlayer > 70.0f) && + (ABS(sp5A) >= 0x7800) && (this->actor.isTargeted || !(player->stateFlags1 & 0x00400000))) { + EnTorch2_SwingSword(globalCtx, input, this); + } else if (((this->actor.xzDistToPlayer <= 70.0f) || + ((this->actor.xzDistToPlayer <= 80.0f + sp50) && (player->swordState != 0))) && + (this->swordState == 0)) { + if (!EnTorch2_SwingSword(globalCtx, input, this) && (this->swordState == 0) && + (sCounterState == 0)) { + EnTorch2_Backflip(this, input, &this->actor); + } + } else if (this->actor.xzDistToPlayer <= 50 + sp50) { + sStickTilt = 127.0f; + sStickAngle = this->actor.yawTowardsPlayer; + if (!this->actor.isTargeted) { + Math_SmoothStepToS(&sStickAngle, player->actor.shape.rot.y + 0x7FFF, 1, 0x2328, 0); + } + } else if (this->actor.xzDistToPlayer > 100.0f + sp50) { + if ((player->swordState == 0) || (player->swordAnimation < SPIN_ATTACK_1H) || + (player->swordAnimation > BIG_SPIN_2H) || (this->actor.xzDistToPlayer >= 280.0f)) { + sStickTilt = 127.0f; + sStickAngle = this->actor.yawTowardsPlayer; + if (!this->actor.isTargeted) { + Math_SmoothStepToS(&sStickAngle, player->actor.shape.rot.y + 0x7FFF, 1, 0x2328, 0); + } + } else { + EnTorch2_Backflip(this, input, &this->actor); + } + } else if (((ABS(sp5A) < 0x7800) && (ABS(sp5A) >= 0x3000)) || + !EnTorch2_SwingSword(globalCtx, input, this)) { + sStickAngle = this->actor.yawTowardsPlayer; + sStickTilt = 127.0f; + if (!this->actor.isTargeted) { + Math_SmoothStepToS(&sStickAngle, player->actor.shape.rot.y + 0x7FFF, 1, 0x2328, 0); + } + } + } + } + + // Handles Dark Link's counterattack to jumpslashes + + } else if (sJumpslashFlag && (sAlpha == 255) && (this->actor.velocity.y > 0)) { + input->cur.button |= BTN_B; + } else if (!sJumpslashFlag && (this->actor.bgCheckFlags & 1)) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + sStickAngle = this->actor.yawTowardsPlayer; + if (sAlpha != 255) { + sStickAngle += 0x8000; + sStickTilt = 127.0f; + sZTargetFlag = true; + } + input->cur.button |= BTN_A; + sJumpslashFlag = true; + this->invincibilityTimer = 10; + } + + // Rotates Dark Link's stick angle from Link-relative to camera-relative. + + sp66 = camera->camDir.y - sStickAngle; + sInput.cur.stick_x = sStickTilt * Math_SinS(sp66); + stickY = sStickTilt * Math_CosS(sp66); + if (sAlpha) {} + sInput.cur.stick_y = stickY; + + if ((sAlpha != 255) && ((globalCtx->gameplayFrames % 8) == 0)) { + sAlpha++; + } + break; + + case ENTORCH2_DAMAGE: + this->swordState = 0; + input->cur.stick_x = input->cur.stick_y = 0; + if ((this->invincibilityTimer > 0) && (this->actor.world.pos.y < (this->actor.floorHeight - 160.0f))) { + this->stateFlags3 &= ~1; + this->actor.flags |= ACTOR_FLAG_0; + this->invincibilityTimer = 0; + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = sSpawnPoint.y + 40.0f; + this->actor.world.pos.x = (Math_SinS(player->actor.shape.rot.y) * -120.0f) + player->actor.world.pos.x; + this->actor.world.pos.z = (Math_CosS(player->actor.shape.rot.y) * -120.0f) + player->actor.world.pos.z; + if (Actor_WorldDistXYZToPoint(&this->actor, &sSpawnPoint) > 800.0f) { + sp50 = Rand_ZeroOne() * 20.0f; + sp4E = Rand_CenteredFloat(4000.0f); + this->actor.shape.rot.y = this->actor.world.rot.y = + Math_Vec3f_Yaw(&sSpawnPoint, &player->actor.world.pos); + this->actor.world.pos.x = + (Math_SinS(this->actor.world.rot.y + sp4E) * (25.0f + sp50)) + sSpawnPoint.x; + this->actor.world.pos.z = + (Math_CosS(this->actor.world.rot.y + sp4E) * (25.0f + sp50)) + sSpawnPoint.z; + this->actor.world.pos.y = sSpawnPoint.y; + } else { + this->actor.world.pos.y = this->actor.floorHeight; + } + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + globalCtx->func_11D54(this, globalCtx); + sActionState = ENTORCH2_ATTACK; + sStickTilt = 0.0f; + if (sAlpha != 255) { + sStaggerCount = 0; + sStaggerTimer = 0; + } + } + break; + + case ENTORCH2_DEATH: + if (sAlpha - 13 <= 0) { + sAlpha = 0; + Actor_Kill(&this->actor); + return; + } + sAlpha -= 13; + this->actor.shape.shadowAlpha -= 13; + break; + } + + // Causes Dark Link to shield in place when Link is using magic attacks other than the spin attack + + if ((gSaveContext.unk_13F0 == 3) && (player->swordState == 0 || (player->swordAnimation < SPIN_ATTACK_1H) || + (player->swordAnimation > BIG_SPIN_2H))) { + sStickTilt = 0.0f; + input->cur.stick_x = 0; + input->cur.stick_y = 0; + input->cur.button = BTN_R; + } + + if ((sActionState == ENTORCH2_ATTACK) && (this->actor.xzDistToPlayer <= 610.0f) && sZTargetFlag) { + input->cur.button |= BTN_Z; + } + + // Updates Dark Link's "controller". The conditional seems to cause him to + // stop targeting and hold shield if he's been holding it long enough. + + pad54 = input->prev.button ^ input->cur.button; + input->press.button = input->cur.button & pad54; + if (CHECK_BTN_ANY(input->cur.button, BTN_R)) { + input->cur.button = ((sCounterState == 0) && (this->swordState == 0)) ? BTN_R : input->cur.button ^ BTN_R; + } + input->rel.button = input->prev.button & pad54; + input->prev.button = input->cur.button & (u16) ~(BTN_A | BTN_B); + PadUtils_UpdateRelXY(input); + + input->press.stick_x += (s8)(input->cur.stick_x - input->prev.stick_x); + input->press.stick_y += (s8)(input->cur.stick_y - input->prev.stick_y); + + // Handles Dark Link being damaged + + if ((this->actor.colChkInfo.health == 0) && sDeathFlag) { + this->csMode = 0x18; + this->unk_448 = &player->actor; + this->unk_46A = 1; + sDeathFlag = false; + } + if ((this->invincibilityTimer == 0) && (this->actor.colChkInfo.health != 0) && + (this->cylinder.base.acFlags & AC_HIT) && !(this->stateFlags1 & 0x04000000) && + !(this->swordQuads[0].base.atFlags & AT_HIT) && !(this->swordQuads[1].base.atFlags & AT_HIT)) { + + if (!Actor_ApplyDamage(&this->actor)) { + func_800F5B58(); + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + this->unk_8A1 = 2; + this->unk_8A4 = 6.0f; + this->unk_8A8 = 6.0f; + this->unk_8A0 = this->actor.colChkInfo.damage; + this->unk_8A2 = this->actor.yawTowardsPlayer + 0x8000; + sDeathFlag++; + sActionState = ENTORCH2_DEATH; + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Item_DropCollectibleRandom(globalCtx, &this->actor, &thisx->world.pos, 0xC0); + this->stateFlags3 &= ~4; + } else { + func_800F5ACC(NA_BGM_MINI_BOSS); + if (this->actor.colChkInfo.damageEffect == 1) { + if (sAlpha == 255) { + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + } else { + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0x2000, 0x50); + } + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_8A0 = this->actor.colChkInfo.damage; + this->unk_8A1 = 1; + this->unk_8A8 = 6.0f; + this->unk_8A4 = 8.0f; + this->unk_8A2 = this->actor.yawTowardsPlayer + 0x8000; + Actor_SetDropFlag(&this->actor, &this->cylinder.info, 1); + this->stateFlags3 &= ~4; + this->stateFlags3 |= 1; + sActionState = ENTORCH2_DAMAGE; + if (sAlpha == 255) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0xC); + } else { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0x2000, 0xC); + } + } + } + this->actor.colChkInfo.damage = 0; + this->unk_8A0 = 0; + } + + // Handles being frozen by a deku nut + + if ((this->actor.colorFilterTimer == 0) || (this->actor.colorFilterParams & 0x4000)) { + this->stateFlags3 &= ~4; + } else { + this->stateFlags3 |= 4; + this->stateFlags1 &= ~0x04000000; + this->invincibilityTimer = 0; + input->press.stick_x = input->press.stick_y = 0; + /*! @bug + * Setting cur.button to 0 clears the Z-trigger, causing Dark Link to break his + * lock on Link. If he presses A while not locked on, he'll put his sword away. + * This clears his held item param permanently and makes him unable to attack. + */ + input->cur.button = 0; + input->press.button = 0; + this->linearVelocity = 0.0f; + } + + globalCtx->playerUpdate(this, globalCtx, input); + + /* + * Handles sword clanks and removes their recoil for both Links. Dark Link staggers + * if he's had to counter with enough different sword animations in a row. + */ + if (this->linearVelocity == -18.0f) { + staggerThreshold = (u32)Rand_CenteredFloat(2.0f) + 6; + if (gSaveContext.health < 0x50) { + staggerThreshold = (u32)Rand_CenteredFloat(2.0f) + 3; + } + if (this->actor.xzDistToPlayer > 80.0f) { + this->linearVelocity = 1.2f; + } else if (this->actor.xzDistToPlayer < 70.0f) { + this->linearVelocity = -1.5f; + } else { + this->linearVelocity = 1.0f; + } + if (staggerThreshold < sStaggerCount) { + this->skelAnime.playSpeed *= 0.6f; + func_800F4190(&this->actor.projectedPos, NA_SE_PL_DAMAGE); + sStaggerTimer = 0; + sStaggerCount = 0; + } + } + if (player->linearVelocity == -18.0f) { + if (this->actor.xzDistToPlayer > 80.0f) { + player->linearVelocity = 1.2f; + } else if (this->actor.xzDistToPlayer < 70.0f) { + player->linearVelocity = -1.5f; + } else { + player->linearVelocity = 1.0f; + } + } + /* + * This ensures Dark Link's counter animation mirrors Link's exactly. + */ + if ((sCounterState != 0) && (sCounterState == 1)) { + if (this->swordState == 0) { + sCounterState = 0; + } else { + sCounterState = 2; + this->swordState = 1; + this->skelAnime.curFrame = player->skelAnime.curFrame - player->skelAnime.playSpeed; + this->skelAnime.playSpeed = player->skelAnime.playSpeed; + LinkAnimation_Update(globalCtx, &this->skelAnime); + Collider_ResetQuadAT(globalCtx, &this->swordQuads[0].base); + Collider_ResetQuadAT(globalCtx, &this->swordQuads[1].base); + } + } + if (sStaggerTimer != 0) { + sStaggerTimer--; + if (sStaggerTimer == 0) { + sCounterState = 0; + sStaggerCount = 0; + } + } + if (sDodgeRollState != 0) { + if (sDodgeRollState == 1) { + this->invincibilityTimer = 20; + } + sDodgeRollState = (this->invincibilityTimer > 0) ? 2 : 0; + } + if (this->invincibilityTimer != 0) { + this->cylinder.base.colType = COLTYPE_NONE; + this->cylinder.info.elemType = ELEMTYPE_UNK5; + } else { + this->cylinder.base.colType = COLTYPE_HIT5; + this->cylinder.info.elemType = ELEMTYPE_UNK1; + } + /* + * Handles the jump movement onto Link's sword. Dark Link doesn't move during the + * sword jump. Instead, his shape y-offset is increased (see below). Once the sword + * jump is finished, the offset is added to his position to fix the discrepancy. + */ + if (sSwordJumpState != 0) { + Math_SmoothStepToF(&sSwordJumpHeight, 2630.0f, 1.0f, 2000.0f, 0.0f); + this->actor.velocity.y -= 0.6f; + } else if (sSwordJumpHeight != 0) { + this->actor.world.pos.y += sSwordJumpHeight * 0.01f; + sSwordJumpHeight = 0; + } + if ((sActionState == ENTORCH2_WAIT) || (this->invincibilityTimer < 0)) { + sZTargetFlag = false; + } else { + sZTargetFlag = true; + } + if (sJumpslashTimer != 0) { + sJumpslashTimer--; + } + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 20.0f; + this->actor.shape.yOffset = sSwordJumpHeight; +} + +s32 EnTorch2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + Player* this = (Player*)thisx; + + return func_8008FCC8(globalCtx, limbIndex, dList, pos, rot, &this->actor); +} + +void EnTorch2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + Player* this = (Player*)thisx; + + func_80090D20(globalCtx, limbIndex, dList, rot, &this->actor); +} + +void EnTorch2_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Player* this = (Player*)thisx; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_torch2.c", 1050); + func_80093C80(globalCtx); + func_80093D84(globalCtx->state.gfxCtx); + if (sAlpha == 255) { + gDPSetEnvColor(POLY_OPA_DISP++, 255, 0, 0, sAlpha); + gSPSegment(POLY_OPA_DISP++, 0x0C, D_80116280 + 2); + func_8002EBCC(&this->actor, globalCtx, 0); + func_8002ED80(&this->actor, globalCtx, 0); + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnTorch2_OverrideLimbDraw, EnTorch2_PostLimbDraw, + this, POLY_OPA_DISP); + } else { + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, sAlpha); + gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280); + func_8002EBCC(&this->actor, globalCtx, 0); + func_8002ED80(&this->actor, globalCtx, 0); + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnTorch2_OverrideLimbDraw, EnTorch2_PostLimbDraw, + this, POLY_XLU_DISP); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_torch2.c", 1114); +} diff --git a/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.h b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.h new file mode 100644 index 000000000..d96b08527 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Torch2/z_en_torch2.h @@ -0,0 +1,9 @@ +#ifndef Z_EN_TORCH2_H +#define Z_EN_TORCH2_H + +#include "ultra64.h" +#include "global.h" + +// Uses the Player struct (from z64player.h) + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c new file mode 100644 index 000000000..425e07421 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.c @@ -0,0 +1,415 @@ +/* + * File: z_en_tory.c + * Overlay: ovl_En_Toryo + * Description: Boss Carpenter + */ + +#include "z_en_toryo.h" +#include "objects/object_toryo/object_toryo.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void EnToryo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnToryo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnToryo_Update(Actor* thisx, GlobalContext* globalCtx); +void EnToryo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B20914(EnToryo* this, GlobalContext* globalCtx); +s32 EnToryo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx); +void EnToryo_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx); + +const ActorInit En_Toryo_InitVars = { + ACTOR_EN_TORYO, + ACTORCAT_NPC, + FLAGS, + OBJECT_TORYO, + sizeof(EnToryo), + (ActorFunc)EnToryo_Init, + (ActorFunc)EnToryo_Destroy, + (ActorFunc)EnToryo_Update, + (ActorFunc)EnToryo_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 18, 63, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x0), + /* Deku stick */ DMG_ENTRY(0, 0x0), + /* Slingshot */ DMG_ENTRY(0, 0x0), + /* Explosive */ DMG_ENTRY(0, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x0), + /* Normal arrow */ DMG_ENTRY(0, 0x0), + /* Hammer swing */ DMG_ENTRY(0, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x0), + /* Kokiri sword */ DMG_ENTRY(0, 0x0), + /* Master sword */ DMG_ENTRY(0, 0x0), + /* Giant's Knife */ DMG_ENTRY(0, 0x0), + /* Fire arrow */ DMG_ENTRY(0, 0x0), + /* Ice arrow */ DMG_ENTRY(0, 0x0), + /* Light arrow */ DMG_ENTRY(0, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(0, 0x0), + /* Giant spin */ DMG_ENTRY(0, 0x0), + /* Master spin */ DMG_ENTRY(0, 0x0), + /* Kokiri jump */ DMG_ENTRY(0, 0x0), + /* Giant jump */ DMG_ENTRY(0, 0x0), + /* Master jump */ DMG_ENTRY(0, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(0, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static AnimationSpeedInfo sEnToryoAnimation = { &object_toryo_Anim_000E50, 1.0f, 0, 0 }; + +static Vec3f sMultVec = { 800.0f, 1000.0f, 0.0f }; + +void EnToryo_Init(Actor* thisx, GlobalContext* globalCtx) { + EnToryo* this = (EnToryo*)thisx; + s32 pad; + + switch (globalCtx->sceneNum) { + case SCENE_SPOT09: + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + this->stateFlags |= 1; + } + break; + case SCENE_SPOT01: + if ((LINK_AGE_IN_YEARS == YEARS_CHILD) && IS_DAY) { + this->stateFlags |= 2; + } + break; + case SCENE_KAKARIKO: + if ((LINK_AGE_IN_YEARS == YEARS_CHILD) && IS_NIGHT) { + this->stateFlags |= 4; + } + break; + } + + if ((this->stateFlags & 7) == 0) { + Actor_Kill(&this->actor); + } + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 42.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_toryo_Skel_007150, NULL, this->jointTable, this->morphTable, + 17); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + Animation_Change(&this->skelAnime, sEnToryoAnimation.animation, 1.0f, 0.0f, + Animation_GetLastFrame(sEnToryoAnimation.animation), sEnToryoAnimation.mode, + sEnToryoAnimation.morphFrames); + this->stateFlags |= 8; + this->actor.targetMode = 6; + this->actionFunc = func_80B20914; +} + +void EnToryo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnToryo* this = (EnToryo*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 func_80B203D8(EnToryo* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 ret = 1; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_CLOSING: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_EVENT: + ret = 1; + break; + case TEXT_STATE_CHOICE: + if (Message_ShouldAdvance(globalCtx)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + Message_CloseTextbox(globalCtx); + this->actor.parent = NULL; + player->exchangeItemId = EXCH_ITEM_NONE; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->actor.textId = 0x601B; + ret = 3; + } else { + this->actor.textId = 0x606F; + ret = 2; + } + } + break; + case TEXT_STATE_DONE: + switch (this->actor.textId) { + case 0x5028: + ret = 1; + if (Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[23] |= 4; + ret = 0; + } + break; + case 0x601B: + ret = 1; + if (Message_ShouldAdvance(globalCtx)) { + ret = 4; + } + break; + case 0x606F: + ret = 1; + if (Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[23] |= 2; + ret = 0; + } + break; + case 0x606A: + ret = 1; + if (Message_ShouldAdvance(globalCtx)) { + gSaveContext.infTable[23] |= 1; + ret = 0; + } + break; + case 0x606B: + case 0x606C: + case 0x606D: + case 0x606E: + default: + ret = 1; + if (Message_ShouldAdvance(globalCtx)) { + ret = 0; + } + break; + } + break; + } + return ret; +} + +s32 func_80B205CC(EnToryo* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s32 ret = 5; + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_CLOSING: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_CHOICE: + case TEXT_STATE_EVENT: + ret = 5; + break; + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + ret = 0; + } + break; + } + return ret; +} + +u32 func_80B20634(EnToryo* this, GlobalContext* globalCtx) { + u32 ret; + + if (this->unk_1E0 != 0) { + if (this->unk_1E0 == 10) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + if (gSaveContext.infTable[23] & 2) { + ret = 0x606E; + } else { + ret = 0x606D; + } + } else { + ret = 0x200F; + } + } + //! @bug return value may be unitialized + return ret; +} + +s32 func_80B206A0(EnToryo* this, GlobalContext* globalCtx) { + s32 textId = Text_GetFaceReaction(globalCtx, 0); + s32 ret = textId; + + if (textId == 0) { + if ((this->stateFlags & 1)) { + if ((gSaveContext.eventChkInf[9] & 0xF) == 0xF) { + ret = 0x606C; + } else if ((gSaveContext.infTable[23] & 1)) { + ret = 0x606B; + } else { + ret = 0x606A; + } + } else if ((this->stateFlags & 2)) { + if ((gSaveContext.infTable[23] & 4)) { + ret = 0x5029; + } else { + ret = 0x5028; + } + } else { + ret = textId; + if ((this->stateFlags & 4)) { + ret = 0x506C; + } + } + } + return ret; +} + +void func_80B20768(EnToryo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 sp32; + s16 sp30; + + if (this->unk_1E4 == 3) { + Actor_ProcessTalkRequest(&this->actor, globalCtx); + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E4 = 1; + } + + if (this->unk_1E4 == 1) { + this->unk_1E4 = func_80B203D8(this, globalCtx); + } + + if (this->unk_1E4 == 5) { + this->unk_1E4 = func_80B205CC(this, globalCtx); + return; + } + + if (this->unk_1E4 == 2) { + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E4 = 1; + } + + if (this->unk_1E4 == 4) { + if (Actor_HasParent(&this->actor, globalCtx)) { + this->actor.parent = NULL; + this->unk_1E4 = 5; + } else { + func_8002F434(&this->actor, globalCtx, GI_SWORD_BROKEN, 100.0f, 10.0f); + } + return; + } + + if (this->unk_1E4 == 0) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_1E0 = func_8002F368(globalCtx); + if (this->unk_1E0 != 0) { + player->actor.textId = func_80B20634(this, globalCtx); + this->actor.textId = player->actor.textId; + } + this->unk_1E4 = 1; + return; + } + + Actor_GetScreenPos(globalCtx, &this->actor, &sp32, &sp30); + if ((sp32 >= 0) && (sp32 < 0x141) && (sp30 >= 0) && (sp30 < 0xF1)) { + this->actor.textId = func_80B206A0(this, globalCtx); + func_8002F298(&this->actor, globalCtx, 100.0f, 10); + } + } +} + +void func_80B20914(EnToryo* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + func_80B20768(this, globalCtx); + if (this->unk_1E4 != 0) { + this->stateFlags |= 0x10; + } else { + this->stateFlags &= ~0x10; + } +} + +void EnToryo_Update(Actor* thisx, GlobalContext* globalCtx) { + EnToryo* this = (EnToryo*)thisx; + ColliderCylinder* collider = &this->collider; + Player* player = GET_PLAYER(globalCtx); + f32 rot; + + Collider_UpdateCylinder(thisx, collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, (Collider*)collider); + + this->actionFunc(this, globalCtx); + + if ((this->stateFlags & 8)) { + this->unk_1EC.unk_18.x = player->actor.focus.pos.x; + this->unk_1EC.unk_18.y = player->actor.focus.pos.y; + this->unk_1EC.unk_18.z = player->actor.focus.pos.z; + + if ((this->stateFlags & 0x10)) { + func_80034A14(thisx, &this->unk_1EC, 0, 4); + return; + } + + rot = thisx->yawTowardsPlayer - thisx->shape.rot.y; + if ((rot < 14563.0f) && (rot > -14563.0f)) { + func_80034A14(thisx, &this->unk_1EC, 0, 2); + } else { + func_80034A14(thisx, &this->unk_1EC, 0, 1); + } + } +} + +void EnToryo_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnToryo* this = (EnToryo*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnToryo_OverrideLimbDraw, EnToryo_PostLimbDraw, this); +} + +s32 EnToryo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnToryo* this = (EnToryo*)thisx; + + if ((this->stateFlags & 8)) { + switch (limbIndex) { + case 8: + rot->x += this->unk_1EC.unk_0E.y; + rot->y -= this->unk_1EC.unk_0E.x; + break; + case 15: + rot->x += this->unk_1EC.unk_08.y; + rot->z += this->unk_1EC.unk_08.x; + break; + } + } + return 0; +} + +void EnToryo_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnToryo* this = (EnToryo*)thisx; + + switch (limbIndex) { + case 15: + Matrix_MultVec3f(&sMultVec, &this->actor.focus.pos); + break; + } +} diff --git a/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.h b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.h new file mode 100644 index 000000000..b6963f501 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Toryo/z_en_toryo.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_TORYO_H +#define Z_EN_TORYO_H + +#include "ultra64.h" +#include "global.h" + +struct EnToryo; + +typedef void (*EnToryoActionFunc)(struct EnToryo* this, GlobalContext* globalCtx); + +typedef struct EnToryo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnToryoActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ s32 unk_1E0; + /* 0x01E4 */ s32 unk_1E4; + /* 0x01E8 */ u16 stateFlags; + /* 0x01EA */ s16 unk_1EA; + /* 0x01EC */ struct_80034A14_arg1 unk_1EC; + /* 0x0214 */ Vec3s jointTable[17]; + /* 0x027A */ Vec3s morphTable[17]; +} EnToryo; // size = 0x02E0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c new file mode 100644 index 000000000..8981524fe --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.c @@ -0,0 +1,767 @@ +/* + * File: z_en_tp.c + * Overlay: ovl_En_Tp + * Description: Electric Tailpasaran + */ + +#include "z_en_tp.h" +#include "objects/object_tp/object_tp.h" + +#define FLAGS 0 + +void EnTp_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTp_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTp_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTp_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnTp_Tail_SetupFollowHead(EnTp* this); +void EnTp_Tail_FollowHead(EnTp* this, GlobalContext* globalCtx); +void EnTp_Head_SetupApproachPlayer(EnTp* this); +void EnTp_Head_ApproachPlayer(EnTp* this, GlobalContext* globalCtx); +void EnTp_SetupDie(EnTp* this); +void EnTp_Die(EnTp* this, GlobalContext* globalCtx); +void EnTp_Fragment_SetupFade(EnTp* this); +void EnTp_Fragment_Fade(EnTp* this, GlobalContext* globalCtx); +void EnTp_Head_SetupTakeOff(EnTp* this); +void EnTp_Head_TakeOff(EnTp* this, GlobalContext* globalCtx); +void EnTp_Head_SetupWait(EnTp* this); +void EnTp_Head_Wait(EnTp* this, GlobalContext* globalCtx); +void EnTp_Head_SetupBurrowReturnHome(EnTp* this); +void EnTp_Head_BurrowReturnHome(EnTp* this, GlobalContext* globalCtx); + +typedef enum { + /* 0 */ TAILPASARAN_ACTION_FRAGMENT_FADE, + /* 1 */ TAILPASARAN_ACTION_DIE, + /* 2 */ TAILPASARAN_ACTION_TAIL_FOLLOWHEAD, + /* 4 */ TAILPASARAN_ACTION_HEAD_WAIT = 4, + /* 7 */ TAILPASARAN_ACTION_HEAD_APPROACHPLAYER = 7, + /* 8 */ TAILPASARAN_ACTION_HEAD_TAKEOFF, + /* 9 */ TAILPASARAN_ACTION_HEAD_BURROWRETURNHOME +} TailpasaranAction; + +const ActorInit En_Tp_InitVars = { + ACTOR_EN_TP, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_TP, + sizeof(EnTp), + (ActorFunc)EnTp_Init, + (ActorFunc)EnTp_Destroy, + (ActorFunc)EnTp_Update, + (ActorFunc)EnTp_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x03, 0x08 }, + { 0xFFCFFFFF, 0x01, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 4 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_HIT1, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +typedef enum { + /* 00 */ TAILPASARAN_DMGEFF_NONE, + /* 01 */ TAILPASARAN_DMGEFF_DEKUNUT, + /* 14 */ TAILPASARAN_DMGEFF_SHOCKING = 14, // Kills the Tailpasaran but shocks Player + /* 15 */ TAILPASARAN_DMGEFF_INSULATING // Kills the Tailpasaran and does not shock Player +} TailpasaranDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_DEKUNUT), + /* Deku stick */ DMG_ENTRY(2, TAILPASARAN_DMGEFF_INSULATING), + /* Slingshot */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Explosive */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(1, TAILPASARAN_DMGEFF_INSULATING), + /* Normal arrow */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, TAILPASARAN_DMGEFF_SHOCKING), + /* Hookshot */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Kokiri sword */ DMG_ENTRY(1, TAILPASARAN_DMGEFF_SHOCKING), + /* Master sword */ DMG_ENTRY(2, TAILPASARAN_DMGEFF_SHOCKING), + /* Giant's Knife */ DMG_ENTRY(4, TAILPASARAN_DMGEFF_SHOCKING), + /* Fire arrow */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Ice arrow */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Light arrow */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Unk arrow 1 */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Ice magic */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Light magic */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Shield */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, TAILPASARAN_DMGEFF_SHOCKING), + /* Giant spin */ DMG_ENTRY(4, TAILPASARAN_DMGEFF_SHOCKING), + /* Master spin */ DMG_ENTRY(2, TAILPASARAN_DMGEFF_SHOCKING), + /* Kokiri jump */ DMG_ENTRY(2, TAILPASARAN_DMGEFF_SHOCKING), + /* Giant jump */ DMG_ENTRY(8, TAILPASARAN_DMGEFF_SHOCKING), + /* Master jump */ DMG_ENTRY(4, TAILPASARAN_DMGEFF_SHOCKING), + /* Unknown 1 */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, TAILPASARAN_DMGEFF_SHOCKING), + /* Unknown 2 */ DMG_ENTRY(0, TAILPASARAN_DMGEFF_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 10, ICHAIN_STOP), +}; + +void EnTp_SetupAction(EnTp* this, EnTpActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnTp_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnTp* this = (EnTp*)thisx; + EnTp* now; + EnTp* next; + s32 i; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.targetMode = 3; + this->actor.colChkInfo.damageTable = &sDamageTable; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.14f); + this->unk_150 = 0; + this->actor.colChkInfo.health = 1; + now = this; + this->alpha = 255; + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + + if (this->actor.params <= TAILPASARAN_HEAD) { + this->actor.naviEnemyId = 0x06; + this->timer = 0; + this->collider.base.acFlags |= AC_HARD; + this->collider.elements->dim.modelSphere.radius = this->collider.elements->dim.worldSphere.radius = 8; + EnTp_Head_SetupWait(this); + this->actor.focus.pos = this->actor.world.pos; + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4; + Actor_SetScale(&this->actor, 1.5f); + + for (i = 0; i <= 6; i++) { + next = (EnTp*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_TP, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0 * i); + + if (0 * i) {} // Very fake, but needed to get the s registers right + if (next != NULL) { + now->actor.child = &next->actor; + next->actor.parent = &now->actor; + next->kiraSpawnTimer = i + 1; + next->head = this; + Actor_SetScale(&next->actor, 0.3f); + + if (i == 2) { + next->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4; + next->unk_150 = 1; // Why? + } + + next->timer = next->unk_15C = i * -5; + next->horizontalVariation = 6.0f - (i * 0.75f); + now = next; + if (0 * i) {} + } + } + } else if (this->actor.params == TAILPASARAN_TAIL) { + EnTp_Tail_SetupFollowHead(this); + } else { + EnTp_Fragment_SetupFade(this); + } +} + +void EnTp_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTp* this = (EnTp*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void EnTp_Tail_SetupFollowHead(EnTp* this) { + this->actionIndex = TAILPASARAN_ACTION_TAIL_FOLLOWHEAD; + EnTp_SetupAction(this, EnTp_Tail_FollowHead); +} + +void EnTp_Tail_FollowHead(EnTp* this, GlobalContext* globalCtx) { + s16 angle; + s16 phase; + + if (this->actor.params == TAILPASARAN_TAIL_DYING) { + this->actionIndex = TAILPASARAN_ACTION_DIE; + + if (this->actor.parent == NULL) { + EnTp_SetupDie(this); + } + } else { + if (this->unk_150 != 0) { + this->actor.flags |= ACTOR_FLAG_0; + } + + if (this->head->unk_150 != 0) { + this->actor.speedXZ = this->red = this->actor.velocity.y = this->heightPhase = 0.0f; + if (this->actor.world.pos.y < this->head->actor.home.pos.y) { + this->actor.flags &= ~ACTOR_FLAG_0; + } + + this->actor.world.pos = this->actor.parent->prevPos; + } else { + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.parent->world.pos.y - 4.0f, 1.0f, 1.0f, 0.0f); + angle = this->head->actor.shape.rot.y + 0x4000; + phase = 2000 * (this->head->unk_15C + this->timer); + this->actor.world.pos.x = + this->actor.home.pos.x + Math_SinS(phase) * (Math_SinS(angle) * this->horizontalVariation); + this->actor.world.pos.z = + this->actor.home.pos.z + Math_SinS(phase) * (Math_CosS(angle) * this->horizontalVariation); + } + } +} + +void EnTp_Head_SetupApproachPlayer(EnTp* this) { + this->actionIndex = TAILPASARAN_ACTION_HEAD_APPROACHPLAYER; + this->timer = 200; + EnTp_SetupAction(this, EnTp_Head_ApproachPlayer); +} + +void EnTp_Head_ApproachPlayer(EnTp* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.world.pos.y, player->actor.world.pos.y + 30.0f, 1.0f, 0.5f, 0.0f); + Audio_PlaySoundGeneral(NA_SE_EN_TAIL_FLY - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + if (&player->actor == this->collider.base.at) { + this->timer = 1; + } + } + + if (this->red < 255) { + this->red += 15; + } + + if (Math_CosF(this->heightPhase) == 0.0f) { + this->extraHeightVariation = 2.0f * Rand_ZeroOne(); + } + + this->actor.world.pos.y += Math_CosF(this->heightPhase) * (2.0f + this->extraHeightVariation); + this->heightPhase += 0.2f; + Math_SmoothStepToF(&this->actor.speedXZ, 2.5f, 0.1f, 0.2f, 0.0f); + this->timer--; + + if (this->timer != 0) { + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 750, 0); + this->actor.shape.rot.y = this->actor.world.rot.y; + } else { + EnTp_Head_SetupBurrowReturnHome(this); + } +} + +void EnTp_SetupDie(EnTp* this) { + Actor* now; + + this->timer = 2; + + if (this->actor.params <= TAILPASARAN_HEAD) { + for (now = this->actor.child; now != NULL; now = now->child) { + now->params = TAILPASARAN_TAIL_DYING; + now->colChkInfo.health = 0; + } + + this->timer = 13; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TAIL_DEAD); + } + this->actionIndex = TAILPASARAN_ACTION_DIE; + EnTp_SetupAction(this, EnTp_Die); +} + +/** + * Spawns effects and smaller tail segment-like fragments + */ +void EnTp_Die(EnTp* this, GlobalContext* globalCtx) { + EnTp* now; + s16 i; + s32 pad; + Vec3f effectVelAccel = { 0.0f, 0.5f, 0.0f }; + Vec3f effectPos = { 0.0f, 0.0f, 0.0f }; + + this->timer--; + + if (this->timer <= 0) { + if (this->actor.params == TAILPASARAN_HEAD_DYING) { + effectPos.x = ((Rand_ZeroOne() - 0.5f) * 15.0f) + this->actor.world.pos.x; + effectPos.z = ((Rand_ZeroOne() - 0.5f) * 15.0f) + this->actor.world.pos.z; + effectPos.y = ((Rand_ZeroOne() - 0.5f) * 5.0f) + this->actor.world.pos.y; + EffectSsDeadDb_Spawn(globalCtx, &effectPos, &effectVelAccel, &effectVelAccel, 100, 0, 255, 255, 255, 255, 0, + 0, 255, 1, 9, 1); + + effectPos.x = ((Rand_ZeroOne() - 0.5f) * 15.0f) + this->actor.world.pos.x; + effectPos.z = ((Rand_ZeroOne() - 0.5f) * 15.0f) + this->actor.world.pos.z; + effectPos.y = ((Rand_ZeroOne() - 0.5f) * 5.0f) + this->actor.world.pos.y; + EffectSsDeadDb_Spawn(globalCtx, &effectPos, &effectVelAccel, &effectVelAccel, 100, 0, 255, 255, 255, 255, 0, + 0, 255, 1, 9, 1); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x50); + } else { + for (i = 0; i < 1; i++) { + now = + (EnTp*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_TP, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, TAILPASARAN_FRAGMENT); + + if (now != NULL) { + Actor_SetScale(&now->actor, this->actor.scale.z * 0.5f); + now->red = this->red; + } + } + } + + if (this->actor.child != NULL) { + this->actor.child->parent = NULL; + this->actor.child->params = TAILPASARAN_TAIL_DYING; + this->actor.child->colChkInfo.health = 0; + } + + this->unk_150 = 2; + Actor_Kill(&this->actor); + } +} + +void EnTp_Fragment_SetupFade(EnTp* this) { + this->actionIndex = TAILPASARAN_ACTION_FRAGMENT_FADE; + this->actor.world.pos.x += ((Rand_ZeroOne() - 0.5f) * 5.0f); + this->actor.world.pos.y += ((Rand_ZeroOne() - 0.5f) * 5.0f); + this->actor.world.pos.z += ((Rand_ZeroOne() - 0.5f) * 5.0f); + this->actor.velocity.x = (Rand_ZeroOne() - 0.5f) * 1.5f; + this->actor.velocity.y = (Rand_ZeroOne() - 0.5f) * 1.5f; + this->actor.velocity.z = (Rand_ZeroOne() - 0.5f) * 1.5f; + this->actor.flags &= ~ACTOR_FLAG_0; + EnTp_SetupAction(this, EnTp_Fragment_Fade); +} + +void EnTp_Fragment_Fade(EnTp* this, GlobalContext* globalCtx) { + func_8002D7EC(&this->actor); + this->alpha -= 20; + + if (this->alpha < 20) { + this->alpha = 0; + Actor_Kill(&this->actor); + } +} + +void EnTp_Head_SetupTakeOff(EnTp* this) { + this->timer = (Rand_ZeroOne() * 15.0f) + 40.0f; + this->actionIndex = TAILPASARAN_ACTION_HEAD_TAKEOFF; + EnTp_SetupAction(this, EnTp_Head_TakeOff); +} + +/** + * Flies up and loops around until it makes for Player + */ +void EnTp_Head_TakeOff(EnTp* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToF(&this->actor.speedXZ, 2.5f, 0.1f, 0.2f, 0.0f); + Math_SmoothStepToF(&this->actor.world.pos.y, player->actor.world.pos.y + 85.0f + this->horizontalVariation, 1.0f, + this->actor.speedXZ * 0.25f, 0.0f); + Audio_PlaySoundGeneral(NA_SE_EN_TAIL_FLY - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + if (&player->actor == this->collider.base.at) { + this->unk_15C = 1; + } + } + + if (this->red != 0) { + this->red -= 15; + } + + if (Math_CosF(this->heightPhase) == 0.0f) { + this->extraHeightVariation = Rand_ZeroOne() * 4.0f; + } + + this->actor.world.pos.y += + Math_CosF(this->heightPhase) * ((this->actor.speedXZ * 0.25f) + this->extraHeightVariation); + this->actor.world.rot.y += this->unk_164; + this->heightPhase += 0.2f; + + if (this->timer != 0) { + this->timer--; + } + + Math_SmoothStepToS(&this->actor.world.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->actor.home.pos), 1, 750, + 0); + + if (this->timer == 0) { + EnTp_Head_SetupApproachPlayer(this); + } + + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnTp_Head_SetupWait(EnTp* this) { + this->actionIndex = TAILPASARAN_ACTION_HEAD_WAIT; + this->unk_150 = 0; + this->actor.shape.rot.x = -0x4000; + this->timer = 60; + this->unk_15C = 0; + this->actor.speedXZ = 0.0f; + EnTp_SetupAction(this, EnTp_Head_Wait); +} + +/** + * Awaken and rise from the ground when Player is closer than 200 + */ +void EnTp_Head_Wait(EnTp* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 yaw; + + this->unk_15C--; + + if (this->actor.xzDistToPlayer < 200.0f) { + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + if (&player->actor == this->collider.base.at) { + this->timer = 0; + } + } + + if (this->timer != 0) { + this->timer--; + + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 500, 0); + Math_SmoothStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer, 1, 1500, 0); + + yaw = Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos) + 0x4000; + Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 30.0f, 0.3f, 1.0f, 0.3f); + this->actor.world.pos.x = this->actor.home.pos.x + + (Math_SinS(2000 * this->unk_15C) * (Math_SinS(yaw) * this->horizontalVariation)); + this->actor.world.pos.z = this->actor.home.pos.z + + (Math_SinS(2000 * this->unk_15C) * (Math_CosS(yaw) * this->horizontalVariation)); + } else { + this->actor.shape.rot.x = 0; + this->unk_150 = 1; + EnTp_Head_SetupTakeOff(this); + } + } else { + Math_SmoothStepToS(&this->actor.shape.rot.x, -0x4000, 1, 500, 0); + + if (Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.3f, 1.5f, 0.3f) == 0.0f) { + this->timer = 60; + } else { + yaw = Math_Vec3f_Yaw(&this->actor.home.pos, &player->actor.world.pos); + this->actor.world.pos.x = + this->actor.home.pos.x + (Math_SinS(2000 * this->unk_15C) * (Math_SinS(yaw) * 6.0f)); + this->actor.world.pos.z = + this->actor.home.pos.z + (Math_SinS(2000 * this->unk_15C) * (Math_CosS(yaw) * 6.0f)); + } + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (this->actor.world.pos.y != this->actor.home.pos.y) { + Audio_PlaySoundGeneral(NA_SE_EN_TAIL_FLY - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } +} + +void EnTp_Head_SetupBurrowReturnHome(EnTp* this) { + this->actionIndex = TAILPASARAN_ACTION_HEAD_BURROWRETURNHOME; + this->timer = 0; + EnTp_SetupAction(this, EnTp_Head_BurrowReturnHome); +} + +void EnTp_Head_BurrowReturnHome(EnTp* this, GlobalContext* globalCtx) { + static Vec3f bubbleAccel = { 0.0f, -0.5f, 0.0f }; + static Color_RGBA8 bubblePrimColor = { 255, 255, 255, 255 }; + static Color_RGBA8 bubbleEnvColor = { 150, 150, 150, 0 }; + Vec3f bubbleVelocity; + Vec3f bubblePos; + s32 closeToFloor; + EnTp* now; + s16 temp_v0; // Required to match, usage can maybe be improved + + closeToFloor = false; + temp_v0 = this->timer; + this->unk_15C--; + + if ((temp_v0 != 0) || ((this->actor.home.pos.y - this->actor.world.pos.y) > 60.0f)) { + this->timer = temp_v0 - 1; + temp_v0 = this->timer; + + if (temp_v0 == 0) { + EnTp_Head_SetupWait(this); + + for (now = (EnTp*)this->actor.child; now != NULL; now = (EnTp*)now->actor.child) { + now->unk_15C = now->timer; + } + } else { + if (this->actor.shape.rot.x != -0x4000) { + this->timer = 80; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.pos = this->actor.home.pos; + this->actor.shape.rot.x = -0x4000; + + for (now = (EnTp*)this->actor.child; now != NULL; now = (EnTp*)now->actor.child) { + now->actor.velocity.y = 0.0f; + now->actor.speedXZ = 0.0f; + now->actor.world.pos = this->actor.home.pos; + now->actor.world.pos.y = this->actor.home.pos.y - 80.0f; + } + } + + this->actor.world.pos.y = this->actor.home.pos.y - this->timer; + } + } else { + if (this->actor.shape.rot.x != 0x4000) { + this->actor.shape.rot.x -= 0x400; + } + + if (this->red != 0) { + this->red -= 15; + } + + this->actor.speedXZ = 2.0f * Math_CosS(this->actor.shape.rot.x); + this->actor.velocity.y = Math_SinS(this->actor.shape.rot.x) * -2.0f; + + if ((this->actor.world.pos.y - this->actor.floorHeight) < 20.0f) { + closeToFloor = true; + } + + if (this->actor.world.pos.y != this->actor.home.pos.y) { + Audio_PlaySoundGeneral(NA_SE_EN_TAIL_FLY - SFX_FLAG, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + if (closeToFloor && ((globalCtx->gameplayFrames & 1) != 0)) { + bubblePos = this->actor.world.pos; + bubblePos.y = this->actor.floorHeight; + + bubbleVelocity.x = Rand_CenteredFloat(5.0f); + bubbleVelocity.y = (Rand_ZeroOne() * 3.5f) + 1.5f; + bubbleVelocity.z = Rand_CenteredFloat(5.0f); + + EffectSsDtBubble_SpawnCustomColor(globalCtx, &bubblePos, &bubbleVelocity, &bubbleAccel, &bubblePrimColor, + &bubbleEnvColor, Rand_S16Offset(100, 50), 20, 0); + } + } +} + +void EnTp_UpdateDamage(EnTp* this, GlobalContext* globalCtx) { + s32 phi_s2; + s32 phi_s4; + EnTp* head; // Can eliminate this and just use now, but they're used differently + EnTp* now; + + if ((this->collider.base.acFlags & AC_HIT) && (this->actionIndex >= TAILPASARAN_ACTION_TAIL_FOLLOWHEAD)) { + phi_s4 = phi_s2 = 0; + + if (this->actor.params <= TAILPASARAN_HEAD) { + phi_s2 = 1; + } + + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlagJntSph(&this->actor, &this->collider, 1); + this->damageEffect = this->actor.colChkInfo.damageEffect; + + if (this->actor.colChkInfo.damageEffect != TAILPASARAN_DMGEFF_NONE) { + if (this->actor.colChkInfo.damageEffect == TAILPASARAN_DMGEFF_DEKUNUT) { + phi_s4 = 1; + } + + // Head is invincible + if (phi_s2 == 0) { + Actor_ApplyDamage(&this->actor); + } + + if (this->actor.colChkInfo.health == 0) { + this->actor.flags &= ~ACTOR_FLAG_0; + head = this->head; + + if (head->actor.params <= TAILPASARAN_HEAD) { + EnTp_SetupDie(head); + head->damageEffect = this->actor.colChkInfo.damageEffect; + head->actor.params = TAILPASARAN_HEAD_DYING; + } + } else { + if (phi_s4 != 0) { + this->actor.freezeTimer = 80; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + if (phi_s2 != 0) { + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + } else { + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0x2000, 0x50); + } + } + + for (now = (EnTp*)this->actor.parent; now != NULL; now = (EnTp*)now->actor.parent) { + now->collider.base.acFlags &= ~AC_HIT; + + if (phi_s4 != 0) { + now->actor.freezeTimer = 80; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + + if (phi_s2 != 0) { + Actor_SetColorFilter(&now->actor, 0, 0xFF, 0, 0x50); + } else { + Actor_SetColorFilter(&now->actor, 0, 0xFF, 0x2000, 0x50); + } + } + } + + for (now = (EnTp*)this->actor.child; now != NULL; now = (EnTp*)now->actor.child) { + now->collider.base.acFlags &= ~AC_HIT; + if (phi_s4 != 0) { + now->actor.freezeTimer = 80; + + if (phi_s2 != 0) { + Actor_SetColorFilter(&now->actor, 0, 0xFF, 0, 0x50); + } else { + Actor_SetColorFilter(&now->actor, 0, 0xFF, 0x2000, 0x50); + } + } + } + } + } + } +} + +void EnTp_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnTp* this = (EnTp*)thisx; + Vec3f kiraVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f kiraAccel = { 0.0f, -0.6f, 0.0f }; + Vec3f kiraPos; + Color_RGBA8 kiraPrimColor = { 0, 0, 255, 255 }; + Color_RGBA8 kiraEnvColor = { 0, 0, 0, 0 }; + Player* player = GET_PLAYER(globalCtx); + s16 yawToWall; + + if (player->stateFlags1 & 0x4000000) { // Shielding + this->damageEffect = TAILPASARAN_DMGEFF_NONE; + } + + if (this->actor.colChkInfo.health != 0) { + EnTp_UpdateDamage(this, globalCtx); + } + + this->actionFunc(this, globalCtx); + + if (this->actor.params <= TAILPASARAN_HEAD) { + Actor_MoveForward(&this->actor); + + if (this->actionIndex != TAILPASARAN_ACTION_HEAD_BURROWRETURNHOME) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 15.0f, 10.0f, 5); + } + + // Turn away from wall + if ((this->actor.speedXZ != 0.0f) && (this->actor.bgCheckFlags & 8)) { + yawToWall = this->actor.wallYaw - this->actor.world.rot.y; + + if (ABS(yawToWall) > 0x4000) { + if (yawToWall >= 0) { + this->actor.world.rot.y -= 500; + } else { + this->actor.world.rot.y += 500; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + } + } + + this->actor.shape.rot.z += 0x800; + + if (this->actor.shape.rot.z == 0) { + Audio_PlaySoundGeneral(NA_SE_EN_TAIL_CRY, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + + if (this->actionIndex >= TAILPASARAN_ACTION_TAIL_FOLLOWHEAD) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + if (this->actor.params != TAILPASARAN_TAIL_DYING) { + this->kiraSpawnTimer--; + this->kiraSpawnTimer &= 7; + } + + this->actor.focus.pos = this->actor.world.pos; + + if (this->damageEffect == TAILPASARAN_DMGEFF_SHOCKING) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if ((this->kiraSpawnTimer & 7) == 0) { + kiraPrimColor.r = this->red; + kiraAccel.x = -this->actor.velocity.x * 0.25f; + kiraAccel.y = -this->actor.velocity.y * 0.25f; + kiraAccel.z = -this->actor.velocity.z * 0.25f; + kiraPos.x = ((Rand_ZeroOne() - 0.5f) * 25.0f) + this->actor.world.pos.x; + kiraPos.y = ((Rand_ZeroOne() - 0.5f) * 20.0f) + this->actor.world.pos.y; + kiraPos.z = ((Rand_ZeroOne() - 0.5f) * 25.0f) + this->actor.world.pos.z; + EffectSsKiraKira_SpawnSmall(globalCtx, &kiraPos, &kiraVelocity, &kiraAccel, &kiraPrimColor, &kiraEnvColor); + } + + if ((this->actionIndex >= TAILPASARAN_ACTION_TAIL_FOLLOWHEAD) && (this->actor.colChkInfo.health != 0)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnTp_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnTp* this = (EnTp*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tp.c", 1451); + + if (this->unk_150 != 2) { + if ((thisx->params <= TAILPASARAN_HEAD) || (thisx->params == TAILPASARAN_HEAD_DYING)) { + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_tp.c", 1459), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gTailpasaranHeadDL); + + Matrix_Translate(0.0f, 0.0f, 8.0f, MTXMODE_APPLY); + } else { + func_80093D84(globalCtx->state.gfxCtx); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->red, 0, 255, this->alpha); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetCombineLERP(POLY_XLU_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, + TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, ENVIRONMENT, + TEXEL0, ENVIRONMENT); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gTailpasaranTailSegmentTex)); + gDPPipeSync(POLY_XLU_DISP++); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_tp.c", 1480), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gTailpasaranTailSegmentDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tp.c", 1495); + + if ((thisx->params <= TAILPASARAN_TAIL) || (thisx->params == TAILPASARAN_TAIL_DYING)) { + Collider_UpdateSpheres(0, &this->collider); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.h b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.h new file mode 100644 index 000000000..c466304c7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tp/z_en_tp.h @@ -0,0 +1,39 @@ +#ifndef Z_EN_TP_H +#define Z_EN_TP_H + +#include "ultra64.h" +#include "global.h" + +struct EnTp; + +typedef void (*EnTpActionFunc)(struct EnTp*, GlobalContext*); + +typedef struct EnTp { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 actionIndex; + /* 0x0150 */ s32 unk_150; // Some kind of state indicator + /* 0x0154 */ EnTpActionFunc actionFunc; + /* 0x0158 */ u8 damageEffect; // Used to propagate the effect to the other segments' actors + /* 0x015A */ s16 timer; + /* 0x015C */ s16 unk_15C; // Multipurpose, used to change the horizontal position of tail segments + /* 0x015E */ s16 alpha; // The dying types fade away + /* 0x0160 */ s16 red; + /* 0x0162 */ s16 kiraSpawnTimer; + /* 0x0164 */ s16 unk_164; // Used once, never set to nonzero + /* 0x0168 */ f32 heightPhase; + /* 0x016C */ f32 horizontalVariation; + /* 0x0170 */ f32 extraHeightVariation; + /* 0x0174 */ ColliderJntSph collider; + /* 0x0194 */ ColliderJntSphElement colliderItems[1]; + /* 0x01D4 */ struct EnTp* head; +} EnTp; // size = 0x01D8 + +typedef enum { + /* -1 */ TAILPASARAN_HEAD = -1, // Used when scenes spawn them: code only cares about < 0 + /* 0 */ TAILPASARAN_TAIL, + /* 10 */ TAILPASARAN_FRAGMENT = 10, + /* 11 */ TAILPASARAN_TAIL_DYING, + /* 12 */ TAILPASARAN_HEAD_DYING +} EnTpType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c b/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c new file mode 100644 index 000000000..73c73f7c5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.c @@ -0,0 +1,529 @@ +/* + * File: z_en_tr.c + * Overlay: ovl_En_Tr + * Description: Koume and Kotake + */ + +#include "z_en_tr.h" +#include "objects/object_tr/object_tr.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnTr_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTr_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTr_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTr_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnTr_DoNothing(EnTr* this, GlobalContext* globalCtx); +void EnTr_ShrinkVanish(EnTr* this, GlobalContext* globalCtx); +void EnTr_WaitToReappear(EnTr* this, GlobalContext* globalCtx); +void EnTr_ChooseAction1(EnTr* this, GlobalContext* globalCtx); + +void EnTr_UpdateRotation(EnTr* this, GlobalContext* globalCtx, s32 actionIndex); +void func_80B24038(EnTr* this, GlobalContext* globalCtx, s32 actionIndex); +void EnTr_SetStartPosRot(EnTr* this, GlobalContext* globalCtx, s32 actionIndex); + +const ActorInit En_Tr_InitVars = { + ACTOR_EN_TR, + ACTORCAT_NPC, + FLAGS, + OBJECT_TR, + sizeof(EnTr), + (ActorFunc)EnTr_Init, + (ActorFunc)EnTr_Destroy, + (ActorFunc)EnTr_Update, + (ActorFunc)EnTr_Draw, + NULL, +}; + +// The first elements of these animation arrays are for Koume, the second for Kotake + +static AnimationHeader* unused[] = { + &object_tr_Anim_003FC8, + &object_tr_Anim_001CDC, +}; + +static AnimationHeader* D_80B24368[] = { + &object_tr_Anim_002BC4, + &object_tr_Anim_000BFC, +}; + +static AnimationHeader* D_80B24370[] = { + &object_tr_Anim_0035CC, + &object_tr_Anim_0013CC, +}; + +static AnimationHeader* D_80B24378[] = { + &object_tr_Anim_0049C8, + &object_tr_Anim_0049C8, +}; + +static AnimationHeader* D_80B24380[] = { + &object_tr_Anim_012E1C, + &object_tr_Anim_012E1C, +}; + +static f32 D_80B24388[] = { 0.0f, 20.0f, -30.0f, 20.0f, -20.0f, -20.0f, 30.0f }; + +static f32 D_80B243A4[] = { 0.0f, 30.0f, 0.0f, -30.0f, 30.0f, -30.0f, 0.0f }; + +// Has to be 1-dimensional to match +static Color_RGBA8 D_80B243C0[4] = { + { 255, 200, 0, 255 }, + { 255, 0, 0, 255 }, + { 255, 255, 255, 255 }, + { 0, 0, 255, 255 }, +}; + +static void* sEyeTextures[] = { + object_tr_Tex_0086D8, + object_tr_Tex_0094D8, + object_tr_Tex_0098D8, +}; + +void EnTr_SetupAction(EnTr* this, EnTrActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnTr_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTr* this = (EnTr*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + EnTr_SetupAction(this, EnTr_DoNothing); + this->unk_2D4 = 0; // Set and not used + this->actor.child = NULL; + Actor_SetScale(&this->actor, 0.01f); + + switch (this->actor.params) { + case TR_KOUME: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tr_Skel_011688, &object_tr_Anim_003FC8, + this->jointTable, this->morphTable, 27); + Animation_PlayOnce(&this->skelAnime, &object_tr_Anim_003FC8); + this->animation = NULL; + EnTr_SetupAction(this, EnTr_ChooseAction1); + this->actionIndex = 3; + break; + + case TR_KOTAKE: + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_tr_Skel_00C530, &object_tr_Anim_001CDC, + this->jointTable, this->morphTable, 27); + Animation_PlayOnce(&this->skelAnime, &object_tr_Anim_001CDC); + this->animation = NULL; + EnTr_SetupAction(this, EnTr_ChooseAction1); + this->actionIndex = 2; + break; + + default: + ASSERT(0, "0", "../z_en_tr.c", 277); + break; + } +} + +void EnTr_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnTr_CrySpellcast(EnTr* this, GlobalContext* globalCtx) { + if (this->timer == 11) { + // Both cry in the title screen cutscene, but only Kotake in the in-game cutscene + if ((this->actor.params != TR_KOUME) || (gSaveContext.sceneSetupIndex == 6)) { + Audio_PlaySoundGeneral(NA_SE_EN_TWINROBA_SHOOT_VOICE, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + + if (this->timer > 0) { + this->timer--; + } else if (this->actor.child != NULL) { + this->actor.child = NULL; + } + func_8002F974(&this->actor, NA_SE_EN_TWINROBA_FLY_DEMO - SFX_FLAG); +} + +void EnTr_DoNothing(EnTr* this, GlobalContext* globalCtx) { +} + +void EnTr_ChooseAction2(EnTr* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[this->actionIndex] != NULL) { + switch (globalCtx->csCtx.npcActions[this->actionIndex]->action) { + + case 4: + Actor_SetScale(&this->actor, 0.01f); + EnTr_SetupAction(this, EnTr_ShrinkVanish); + this->timer = 24; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_PO_DEAD2); + break; + + case 6: + Animation_Change(&this->skelAnime, D_80B24380[this->actor.params], 1.0f, 0.0f, + Animation_GetLastFrame(D_80B24380[this->actor.params]), ANIMMODE_ONCE, -5.0f); + EnTr_SetupAction(this, EnTr_CrySpellcast); + this->animation = D_80B24378[this->actor.params]; + this->timer = 39; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, + 0, this->actor.params + 9); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC1); + break; + + default: + func_80B24038(this, globalCtx, this->actionIndex); + EnTr_UpdateRotation(this, globalCtx, this->actionIndex); + break; + } + func_8002F974(&this->actor, NA_SE_EN_TWINROBA_FLY_DEMO - SFX_FLAG); + } + } +} + +void EnTr_FlyKidnapCutscene(EnTr* this, GlobalContext* globalCtx) { + Vec3f originalPos = this->actor.world.pos; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[this->actionIndex] != NULL) { + if (globalCtx->csCtx.npcActions[this->actionIndex]->action == 8) { + func_80B24038(this, globalCtx, this->actionIndex); + this->actor.world.rot.y = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 10, 0x400, 0x100); + this->actor.world.rot.y = this->actor.shape.rot.y; + } else { + EnTr_SetStartPosRot(this, globalCtx, this->actionIndex); + this->actor.world.pos.x += Math_SinS(this->timer) * 150.0f; + this->actor.world.pos.y += -100.0f; + this->actor.world.pos.z += Math_CosS(this->timer) * 150.0f; + + this->actor.shape.rot.y = (s16)(this->timer) + 0x4000; + this->timer += 0x400; + + this->actor.velocity.x = this->actor.world.pos.x - originalPos.x; + this->actor.velocity.y = this->actor.world.pos.y - originalPos.y; + this->actor.velocity.z = this->actor.world.pos.z - originalPos.z; + } + + if (globalCtx->csCtx.frames < 670) { + func_8002F974(&this->actor, NA_SE_EN_TWINROBA_FLY_DEMO - SFX_FLAG); + } + } + } +} + +void func_80B23254(EnTr* this, GlobalContext* globalCtx, s32 arg2, f32 arg3, f32 scale) { + Vec3f pos; + Vec3f velocity; + Vec3f accel; + Vec3f sp58; + Color_RGBA8* primColor; + Color_RGBA8* envColor; + Vec3f cameraEye = GET_ACTIVE_CAM(globalCtx)->eye; + s16 yaw = Math_Vec3f_Yaw(&cameraEye, &this->actor.world.pos); + s16 reversePitch = -Math_Vec3f_Pitch(&cameraEye, &this->actor.world.pos); + f32 sp3C; + + accel.x = accel.z = 0.0f; + sp3C = Math_SinS(yaw); + velocity.x = Math_CosS(reversePitch) * (arg3 * sp3C); + velocity.y = Math_SinS(reversePitch) * arg3; + sp3C = Math_CosS(yaw); + velocity.z = Math_CosS(reversePitch) * (arg3 * sp3C); + accel.y = 0.5f; + + primColor = &D_80B243C0[2 * this->actor.params]; + envColor = &D_80B243C0[2 * this->actor.params + 1]; + + sp58 = this->actor.world.pos; + sp58.x -= velocity.x * 10.0f; + sp58.y -= velocity.y * 10.0f; + sp58.z -= velocity.z * 10.0f; + + pos.x = sp58.x + ((D_80B24388[arg2] * scale) * Math_CosS(yaw)); + pos.y = sp58.y + (D_80B243A4[arg2] * scale); + pos.z = sp58.z - ((D_80B24388[arg2] * scale) * Math_SinS(yaw)); + func_8002829C(globalCtx, &pos, &velocity, &accel, primColor, envColor, (s32)(800.0f * scale), (s32)(80.0f * scale)); +} + +void EnTr_ShrinkVanish(EnTr* this, GlobalContext* globalCtx) { + if (this->timer >= 17) { + this->actor.shape.rot.y = (this->actor.shape.rot.y - (this->timer * 0x28F)) + 0x3D68; + } else { + if (this->timer >= 5) { + Actor_SetScale(&this->actor, this->actor.scale.x * 0.9f); + this->actor.shape.rot.y = (this->actor.shape.rot.y - (this->timer * 0x28F)) + 0x3D68; + } else if (this->timer > 0) { + s32 temp_hi = (this->timer * 2) % 7; + + func_80B23254(this, globalCtx, temp_hi, 5.0f, 0.2f); + func_80B23254(this, globalCtx, (temp_hi + 1) % 7, 5.0f, 0.2f); + Actor_SetScale(&this->actor, this->actor.scale.x * 0.9f); + this->actor.shape.rot.y = (this->actor.shape.rot.y - (this->timer * 0x28F)) + 0x3D68; + } else { + EnTr_SetupAction(this, EnTr_WaitToReappear); + this->actor.draw = NULL; + } + } + + if (this->timer == 4) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BUBLE_DOWN); + } + + if (this->timer > 0) { + this->timer--; + } +} + +void EnTr_Reappear(EnTr* this, GlobalContext* globalCtx) { + if (this->timer >= 31) { + s32 temp_hi = (this->timer * 2) % 7; + + func_80B23254(this, globalCtx, temp_hi, 5.0f, 1.0f); + func_80B23254(this, globalCtx, (temp_hi + 1) % 7, 5.0f, 1.0f); + } else if (this->timer == 30) { + this->actor.draw = EnTr_Draw; + this->actor.shape.rot.y += this->timer * 0x1A6; + } else if (this->timer > 0) { + this->actor.shape.rot.y += this->timer * 0x1A6; + Actor_SetScale(&this->actor, (this->actor.scale.x * 0.8f) + 0.002f); + } else { + EnTr_SetupAction(this, EnTr_ChooseAction2); + Actor_SetScale(&this->actor, 0.01f); + } + + if (this->timer > 0) { + this->timer--; + } + func_8002F974(&this->actor, NA_SE_EN_TWINROBA_FLY_DEMO - SFX_FLAG); +} + +void EnTr_WaitToReappear(EnTr* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if ((globalCtx->csCtx.npcActions[this->actionIndex] != NULL) && + ((globalCtx->csCtx.npcActions[this->actionIndex]->action == 3) || + (globalCtx->csCtx.npcActions[this->actionIndex]->action == 5))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_TRANSFORM); + this->timer = 34; + EnTr_SetStartPosRot(this, globalCtx, this->actionIndex); + EnTr_SetupAction(this, EnTr_Reappear); + Animation_PlayLoop(&this->skelAnime, &object_tr_Anim_0049C8); + this->animation = NULL; + Actor_SetScale(&this->actor, 0.003f); + } + } +} + +void EnTr_TakeOff(EnTr* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(D_80B24378[this->actor.params]); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if ((globalCtx->csCtx.npcActions[this->actionIndex] != NULL) && + (globalCtx->csCtx.npcActions[this->actionIndex]->action == 3)) { + Animation_Change(&this->skelAnime, D_80B24378[this->actor.params], 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP, + -10.0f); + this->animation = NULL; + EnTr_SetupAction(this, EnTr_ChooseAction2); + } + } +} + +void EnTr_TurnLookOverShoulder(EnTr* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(D_80B24368[this->actor.params]); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if ((globalCtx->csCtx.npcActions[this->actionIndex] != NULL) && + (globalCtx->csCtx.npcActions[this->actionIndex]->action == 2)) { + Animation_Change(&this->skelAnime, D_80B24368[this->actor.params], 1.0f, 0.0f, lastFrame, ANIMMODE_ONCE, + -4.0f); + this->animation = D_80B24370[this->actor.params]; + EnTr_SetupAction(this, EnTr_TakeOff); + } + } +} + +void EnTr_ChooseAction1(EnTr* this, GlobalContext* globalCtx) { + u32 frames = globalCtx->gameplayFrames; + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[this->actionIndex] != NULL) { + switch (globalCtx->csCtx.npcActions[this->actionIndex]->action) { + case 1: + EnTr_SetStartPosRot(this, globalCtx, this->actionIndex); + EnTr_SetupAction(this, EnTr_TurnLookOverShoulder); + break; + + case 3: + EnTr_SetStartPosRot(this, globalCtx, this->actionIndex); + EnTr_SetupAction(this, EnTr_ChooseAction2); + Animation_PlayLoop(&this->skelAnime, &object_tr_Anim_0049C8); + this->animation = NULL; + break; + + case 4: + EnTr_SetupAction(this, EnTr_WaitToReappear); + this->actor.draw = NULL; + break; + + case 7: + EnTr_SetupAction(this, EnTr_FlyKidnapCutscene); + Animation_PlayLoop(&this->skelAnime, &object_tr_Anim_0049C8); + this->animation = NULL; + this->timer = + ((this->actor.params != TR_KOUME) ? ((u8)frames * 0x400) + 0x8000 : (u8)frames * 0x400); + break; + } + } + } +} + +void EnTr_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnTr* this = (EnTr*)thisx; + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 5); + this->actionFunc(this, globalCtx); + + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->animation != NULL) { + if ((this->animation == &object_tr_Anim_0035CC) || (this->animation == &object_tr_Anim_0013CC)) { + if (this->actor.params != TR_KOUME) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_LAUGH2); + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_LAUGH); + } + Animation_PlayLoop(&this->skelAnime, this->animation); + } else if (this->animation == &object_tr_Anim_0049C8) { + EnTr_SetupAction(this, EnTr_ChooseAction2); + Animation_Change(&this->skelAnime, &object_tr_Anim_0049C8, 1.0f, 0.0f, + Animation_GetLastFrame(&object_tr_Anim_0049C8), ANIMMODE_LOOP, -5.0f); + } else { + Animation_PlayLoop(&this->skelAnime, this->animation); + } + this->animation = NULL; + } else { + this->skelAnime.curFrame = 0.0f; + } + } + Actor_SetFocus(&this->actor, 0.0f); + + if (DECR(this->blinkTimer) == 0) { + this->blinkTimer = Rand_S16Offset(60, 60); + } + this->eyeIndex = this->blinkTimer; + if (this->eyeIndex >= 3) { + this->eyeIndex = 0; + } +} + +s32 EnTr_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + Vec3f src = { 2300.0f, 0.0f, -600.0f }; + Vec3f dest = { 0.0f, 0.0f, 0.0f }; + EnTr* this = (EnTr*)thisx; + Actor* child = this->actor.child; + + if ((child != NULL) && (limbIndex == 19)) { + Matrix_MultVec3f(&src, &dest); + dest.x -= (10.0f * Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)))); + dest.z -= (10.0f * Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)))); + child->world.pos = dest; + } + return 0; +} + +void EnTr_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnTr* this = (EnTr*)thisx; + + if (1) {} + + if ((globalCtx->csCtx.state == CS_STATE_IDLE) || (globalCtx->csCtx.npcActions[this->actionIndex] == 0)) { + this->actor.shape.shadowDraw = NULL; + } else { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_tr.c", 840); + func_800943C8(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEyeTextures[this->eyeIndex])); + func_8002EBCC(&this->actor, globalCtx, 0); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnTr_OverrideLimbDraw, NULL, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_tr.c", 854); + } +} + +f32 func_80B23FDC(GlobalContext* globalCtx, s32 actionIndex) { + f32 phi_f2 = Environment_LerpWeight(globalCtx->csCtx.npcActions[actionIndex]->endFrame, + globalCtx->csCtx.npcActions[actionIndex]->startFrame, globalCtx->csCtx.frames); + phi_f2 = CLAMP_MAX(phi_f2, 1.0f); + return phi_f2; +} + +void func_80B24038(EnTr* this, GlobalContext* globalCtx, s32 actionIndex) { + Vec3f startPos; + Vec3f endPos; + f32 temp_f0; + f32 temp_f0_2; + f32 phi_f12; + + startPos.x = globalCtx->csCtx.npcActions[actionIndex]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[actionIndex]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[actionIndex]->startPos.z; + + endPos.x = globalCtx->csCtx.npcActions[actionIndex]->endPos.x; + endPos.y = globalCtx->csCtx.npcActions[actionIndex]->endPos.y; + endPos.z = globalCtx->csCtx.npcActions[actionIndex]->endPos.z; + + temp_f0 = func_80B23FDC(globalCtx, actionIndex); + + startPos.x = ((endPos.x - startPos.x) * temp_f0) + startPos.x; + startPos.y = ((endPos.y - startPos.y) * temp_f0) + startPos.y; + startPos.z = ((endPos.z - startPos.z) * temp_f0) + startPos.z; + + endPos.x = (startPos.x - this->actor.world.pos.x) * 0.1f; + endPos.y = (startPos.y - this->actor.world.pos.y) * 0.1f; + endPos.z = (startPos.z - this->actor.world.pos.z) * 0.1f; + + temp_f0_2 = sqrtf(SQ(endPos.x) + SQ(endPos.y) + SQ(endPos.z)); + phi_f12 = CLAMP(temp_f0_2, 0.0f, 20.0f); + + if ((temp_f0_2 != phi_f12) && (temp_f0_2 != 0.0f)) { + endPos.x *= phi_f12 / temp_f0_2; + endPos.y *= phi_f12 / temp_f0_2; + endPos.z *= phi_f12 / temp_f0_2; + } + + Math_StepToF(&this->actor.velocity.x, endPos.x, 1.0f); + Math_StepToF(&this->actor.velocity.y, endPos.y, 1.0f); + Math_StepToF(&this->actor.velocity.z, endPos.z, 1.0f); + func_8002D7EC(&this->actor); +} + +void EnTr_UpdateRotation(EnTr* this, GlobalContext* globalCtx, s32 actionIndex) { + s16 rotY = globalCtx->csCtx.npcActions[actionIndex]->rot.y; + s32 rotDiff = this->actor.world.rot.y - rotY; + s32 rotSign; + + if (rotDiff < 0) { + rotDiff = -rotDiff; + rotSign = 1; + } else { + rotSign = -1; + } + + if (rotDiff >= 0x8000) { + rotSign = -rotSign; + rotDiff = 0x10000 - rotDiff; + } + + rotDiff *= 0.1f; + + this->actor.world.rot.y += rotDiff * rotSign; + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnTr_SetStartPosRot(EnTr* this, GlobalContext* globalCtx, s32 actionIndex) { + Vec3f startPos; + + startPos.x = globalCtx->csCtx.npcActions[actionIndex]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[actionIndex]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[actionIndex]->startPos.z; + + this->actor.world.pos = startPos; + this->actor.world.rot.y = this->actor.shape.rot.y = globalCtx->csCtx.npcActions[actionIndex]->rot.y; +} diff --git a/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.h b/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.h new file mode 100644 index 000000000..4e890b3fb --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tr/z_en_tr.h @@ -0,0 +1,30 @@ +#ifndef Z_EN_TR_H +#define Z_EN_TR_H + +#include "ultra64.h" +#include "global.h" + +struct EnTr; + +typedef void (*EnTrActionFunc)(struct EnTr*, GlobalContext*); + +typedef struct EnTr { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[27]; + /* 0x0232 */ Vec3s morphTable[27]; + /* 0x02D4 */ s16 unk_2D4; + /* 0x02D6 */ u16 timer; // Also used as an angle + /* 0x02D8 */ s16 actionIndex; + /* 0x02DC */ EnTrActionFunc actionFunc; + /* 0x02E0 */ s16 eyeIndex; + /* 0x02E2 */ s16 blinkTimer; + /* 0x02E4 */ AnimationHeader* animation; +} EnTr; // size = 0x02E8 + +typedef enum { + /* 0 */ TR_KOUME, + /* 1 */ TR_KOTAKE +} TwinrovaType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c b/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c new file mode 100644 index 000000000..43bf9b4e5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c @@ -0,0 +1,393 @@ +/* + * File: z_en_trap.c + * Overlay: ovl_En_Trap + * Description: Metal Spike Trap + */ + +#include "z_en_trap.h" +#include "objects/object_trap/object_trap.h" + +#define FLAGS ACTOR_FLAG_4 + +#define BEGIN_MOVE_OUT 65535.0f + +#define DIR_FWD 0 +#define DIR_LEFT 0x4000 +#define DIR_BACK -0x8000 +#define DIR_RIGHT -0x4000 + +// Linear motion +#define vLinearVel upperParams +#define vContinue genericVar2 + +// Circular motion +#define vAngularVel upperParams +#define vAngularPos genericVar1 +#define vRadius genericVar2 + +// Four-way motion +#define vClosestDirection genericVar1 // relative to spike trap's facing angle if moving out, absolute if moving in +#define vMovementMetric genericVar2 + +void EnTrap_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTrap_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTrap_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTrap_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Trap_InitVars = { + ACTOR_EN_TRAP, + ACTORCAT_BG, + FLAGS, + OBJECT_TRAP, + sizeof(EnTrap), + (ActorFunc)EnTrap_Init, + (ActorFunc)EnTrap_Destroy, + (ActorFunc)EnTrap_Update, + (ActorFunc)EnTrap_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_NO_PUSH | OC1_TYPE_1 | OC1_TYPE_2, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0x00001000, 0x00, 0x00 }, TOUCH_NONE, BUMP_ON, OCELEM_ON }, + { 30, 20, 0, { 0, 0, 0 } }, +}; + +void EnTrap_Init(Actor* thisx, GlobalContext* globalCtx) { + f32 trapDist; + f32 trapSpeed; + s16 zSpeed; + s16 xSpeed; + EnTrap* this = (EnTrap*)thisx; + ColliderCylinder* unused = &this->collider; // required to match + + this->upperParams = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + Actor_SetScale(thisx, 0.1f); + thisx->gravity = -2.0f; + if (thisx->params & SPIKETRAP_MODE_LINEAR) { + thisx->speedXZ = this->moveSpeedForwardBack.z = this->upperParams & 0xF; + Audio_PlayActorSound2(thisx, NA_SE_EV_SPINE_TRAP_MOVE); + } else if (thisx->params & SPIKETRAP_MODE_CIRCULAR) { + this->vRadius = (this->upperParams & 0xF) * 40.0f; + this->vAngularVel = ((this->upperParams & 0xF0) + 0x10) << 5; + thisx->world.pos.x = thisx->home.pos.x + (Math_SinS(0) * this->vRadius); + thisx->world.pos.z = thisx->home.pos.z + (Math_CosS(0) * this->vRadius); + } else { // Four-way motion + if (this->upperParams != 0) { + trapDist = (this->upperParams >> 4) * 40; + trapSpeed = (this->upperParams & 0xF); + } else { + trapDist = 200.0f; + trapSpeed = 10.0f; + thisx->params = 0xF; + } + Actor_UpdateBgCheckInfo(globalCtx, thisx, 10.0f, 20.0f, 20.0f, 0x1D); + thisx->home.pos = thisx->world.pos; + this->targetPosLeft.x = thisx->world.pos.x + (trapDist * Math_CosS(thisx->world.rot.y)); + this->targetPosLeft.z = thisx->world.pos.z - (trapDist * Math_SinS(thisx->world.rot.y)); + this->targetPosRight.x = thisx->world.pos.x + (trapDist * Math_CosS(thisx->world.rot.y + 0x8000)); + this->targetPosRight.z = thisx->world.pos.z - (trapDist * Math_SinS(thisx->world.rot.y + 0x8000)); + this->targetPosFwd.x = thisx->world.pos.x + (trapDist * Math_SinS(thisx->world.rot.y)); + this->targetPosFwd.z = thisx->world.pos.z + (trapDist * Math_CosS(thisx->world.rot.y)); + this->targetPosBack.x = thisx->world.pos.x + (trapDist * Math_SinS(thisx->world.rot.y + 0x8000)); + this->targetPosBack.z = thisx->world.pos.z + (trapDist * Math_CosS(thisx->world.rot.y + 0x8000)); + + zSpeed = trapSpeed * Math_CosS(thisx->world.rot.y); + xSpeed = trapSpeed * Math_SinS(thisx->world.rot.y); + zSpeed = ABS(zSpeed); + xSpeed = ABS(xSpeed); + this->moveSpeedLeftRight.x = this->moveSpeedForwardBack.z = zSpeed; + this->moveSpeedLeftRight.z = this->moveSpeedForwardBack.x = xSpeed; + } + thisx->focus.pos = thisx->world.pos; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + thisx->targetMode = 3; + thisx->colChkInfo.mass = 0xFF; +} + +void EnTrap_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTrap* this = (EnTrap*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnTrap_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTrap* this = (EnTrap*)thisx; + Vec3f posTemp; + s16 angleToKnockPlayer; + s16 angleToCollidedActor; + s16 touchingActor; + s16 blockedOnReturn; + s32 pad; + s16 angleToWall; + Vec3f icePos; + Vec3f posAhead; + Vec3f colPoint; // unused return value from function + CollisionPoly* colPoly; // unused return value from function + s32 bgId; // unused return value from function + f32 temp_cond; + + touchingActor = false; + blockedOnReturn = false; + angleToWall = thisx->wallYaw - thisx->world.rot.y; + if (this->collider.base.ocFlags1 & OC1_HIT) { + this->collider.base.ocFlags1 &= ~OC1_HIT; + angleToCollidedActor = + thisx->world.rot.y + Math_Vec3f_Yaw(&this->collider.base.oc->world.pos, &thisx->world.pos); + touchingActor = true; + } + // Freeze the trap if hit by ice arrows: + if ((this->collider.base.acFlags & AC_HIT) != 0) { + icePos = thisx->world.pos; + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetColorFilter(thisx, 0, 250, 0, 250); + icePos.y += 10.0f; + icePos.z += 10.0f; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, thisx, &icePos, 150, 150, 150, 250, 235, 245, 255, 1.8f); + icePos.x += 10.0f; + icePos.z -= 20.0f; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, thisx, &icePos, 150, 150, 150, 250, 235, 245, 255, 1.8f); + icePos.x -= 20.0f; + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, thisx, &icePos, 150, 150, 150, 250, 235, 245, 255, 1.8f); + } + // If not frozen: + if (thisx->colorFilterTimer == 0) { + DECR(this->playerDmgTimer); + // Handles damaging player: + //! @bug there is no yDistToPlayer check for player being below. Therefore hitbox extends down infinitely + if ((thisx->xzDistToPlayer <= 40.0f) && (this->playerDmgTimer == 0) && (thisx->yDistToPlayer <= 20.0f)) { + if (!(thisx->params & (SPIKETRAP_MODE_LINEAR | SPIKETRAP_MODE_CIRCULAR))) { // if in 4-way mode: + if ((s16)(this->vClosestDirection - thisx->yawTowardsPlayer) >= 0) { + angleToKnockPlayer = this->vClosestDirection - 0x4000; + } else { + angleToKnockPlayer = this->vClosestDirection + 0x4000; + } + } else { + angleToKnockPlayer = thisx->yawTowardsPlayer; + } + globalCtx->damagePlayer(globalCtx, -4); + func_8002F7A0(globalCtx, thisx, 6.0f, angleToKnockPlayer, 6.0f); + this->playerDmgTimer = 15; + } + if (thisx->params & SPIKETRAP_MODE_LINEAR) { + this->vContinue = 1.0f; + // If physically touching a wall and wall faces towards spike trap + if ((thisx->bgCheckFlags & 8) && (ABS(angleToWall) >= 0x6000)) { + this->vContinue = 0.0f; + } + // If there is a collision poly between current position and a position 30 units ahead of spike trap + if (this->vContinue != 0.0f) { + posAhead.x = (Math_SinS(thisx->world.rot.y) * 30.0f) + thisx->world.pos.x; + posAhead.z = (Math_CosS(thisx->world.rot.y) * 30.0f) + thisx->world.pos.z; + posAhead.y = thisx->world.pos.y; + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &thisx->world.pos, &posAhead, &colPoint, &colPoly, true, + true, false, true, &bgId) == true) { + this->vContinue = 0.0f; + } + } + // If spike trap is touching an actor which is in the path of the spike trap + if (touchingActor && (this->vContinue != 0.0f)) { + angleToCollidedActor = + Math_Vec3f_Yaw(&thisx->world.pos, &this->collider.base.oc->world.pos) - thisx->world.rot.y; + if (ABS(angleToCollidedActor) < 0x1000) { + this->vContinue = 0.0f; + } + } + // If any of the above three conditions are met, turn around + if (this->vContinue == 0.0f) { + thisx->world.rot.y += 0x8000; + Audio_PlayActorSound2(thisx, NA_SE_EV_SPINE_TRAP_MOVE); + } + } else if (thisx->params & SPIKETRAP_MODE_CIRCULAR) { + temp_cond = Math_SinS(this->vAngularPos); + this->vAngularPos += this->vAngularVel; + // Every full circle make a sound: + if ((temp_cond < 0.0f) && (Math_SinS(this->vAngularPos) >= 0.0f)) { + Audio_PlayActorSound2(thisx, NA_SE_EV_ROUND_TRAP_MOVE); + } + thisx->world.pos.x = (this->vRadius * Math_SinS(this->vAngularPos)) + thisx->home.pos.x; + thisx->world.pos.z = (this->vRadius * Math_CosS(this->vAngularPos)) + thisx->home.pos.z; + thisx->world.pos.y = thisx->floorHeight; + thisx->prevPos = thisx->world.pos; + } else { // 4 way movement + // if moving outwards: + if (this->vMovementMetric != 0.0f) { + switch (this->vClosestDirection) { // movement direction relative to spike trap + case DIR_FWD: + if (!(thisx->params & SPIKETRAP_FOURWAY_FWD_ALLOWED)) { + this->vMovementMetric = 0.0f; + } else if ((thisx->bgCheckFlags & 8) && (ABS(angleToWall) > 0x6000)) { + this->vMovementMetric = 0.0f; + } + if (touchingActor && (this->vMovementMetric != 0.0f) && (ABS(angleToCollidedActor) > 0x6000)) { + this->vMovementMetric = 0.0f; + } + if (this->vMovementMetric != 0.0f) { + if (this->vMovementMetric == BEGIN_MOVE_OUT) { + Audio_PlayActorSound2(thisx, NA_SE_EV_SPINE_TRAP_MOVE); + } + this->vMovementMetric = Math_SmoothStepToF(&thisx->world.pos.z, this->targetPosFwd.z, 1.0f, + this->moveSpeedForwardBack.z, 0.0f); + this->vMovementMetric += Math_SmoothStepToF(&thisx->world.pos.x, this->targetPosFwd.x, 1.0f, + this->moveSpeedForwardBack.x, 0.0f); + } + break; + case DIR_LEFT: + if (!(thisx->params & SPIKETRAP_FOURWAY_LEFT_ALLOWED)) { + this->vMovementMetric = 0.0f; + } else if ((thisx->bgCheckFlags & 8) && (angleToWall < -0x2000) && (angleToWall > -0x6000)) { + this->vMovementMetric = 0.0f; + break; + } + if (touchingActor && (this->vMovementMetric != 0.0f) && (angleToCollidedActor <= -0x2000) && + (angleToCollidedActor > -0x6000)) { + this->vMovementMetric = 0.0f; + break; + } + if (this->vMovementMetric != 0.0f) { + if (this->vMovementMetric == BEGIN_MOVE_OUT) { + Audio_PlayActorSound2(thisx, NA_SE_EV_SPINE_TRAP_MOVE); + } + this->vMovementMetric = Math_SmoothStepToF(&thisx->world.pos.x, this->targetPosLeft.x, 1.0f, + this->moveSpeedLeftRight.x, 0.0f); + this->vMovementMetric += Math_SmoothStepToF(&thisx->world.pos.z, this->targetPosLeft.z, + 1.0f, this->moveSpeedLeftRight.z, 0.0f); + } + break; + case DIR_BACK: + if (!(thisx->params & SPIKETRAP_FOURWAY_BACK_ALLOWED)) { + this->vMovementMetric = 0.0f; + } else if ((thisx->bgCheckFlags & 8) && (ABS(angleToWall) < 0x2000)) { + this->vMovementMetric = 0.0f; + break; + } + if (touchingActor && (this->vMovementMetric != 0.0f) && (ABS(angleToCollidedActor) < 0x2000)) { + this->vMovementMetric = 0.0f; + break; + } + if (this->vMovementMetric != 0.0f) { + if (this->vMovementMetric == BEGIN_MOVE_OUT) { + Audio_PlayActorSound2(thisx, NA_SE_EV_SPINE_TRAP_MOVE); + } + this->vMovementMetric = Math_SmoothStepToF(&thisx->world.pos.z, this->targetPosBack.z, 1.0f, + this->moveSpeedForwardBack.z, 0.0f); + this->vMovementMetric += Math_SmoothStepToF(&thisx->world.pos.x, this->targetPosBack.x, + 1.0f, this->moveSpeedForwardBack.x, 0.0f); + } + break; + case DIR_RIGHT: + if (!(thisx->params & SPIKETRAP_FOURWAY_RIGHT_ALLOWED)) { + this->vMovementMetric = 0.0f; + } else if ((thisx->bgCheckFlags & 8) && (angleToWall > 0x2000) && (angleToWall < 0x6000)) { + this->vMovementMetric = 0.0f; + break; + } + if (touchingActor && (this->vMovementMetric != 0.0f) && (angleToCollidedActor > 0x2000) && + (angleToCollidedActor < 0x6000)) { + this->vMovementMetric = 0.0f; + break; + } + if (this->vMovementMetric != 0.0f) { + if (this->vMovementMetric == BEGIN_MOVE_OUT) { + Audio_PlayActorSound2(thisx, NA_SE_EV_SPINE_TRAP_MOVE); + } + this->vMovementMetric = Math_SmoothStepToF(&thisx->world.pos.x, this->targetPosRight.x, + 1.0f, this->moveSpeedLeftRight.x, 0.0f); + this->vMovementMetric += Math_SmoothStepToF(&thisx->world.pos.z, this->targetPosRight.z, + 1.0f, this->moveSpeedLeftRight.z, 0.0f); + } + break; + } + if (!Actor_TestFloorInDirection(thisx, globalCtx, 50.0f, this->vClosestDirection)) { + this->vMovementMetric = 0.0f; + } + // if in initial position: + } else if ((thisx->world.pos.x == thisx->home.pos.x) && (thisx->world.pos.z == thisx->home.pos.z)) { + // of the available 4-way directions, get the one which is closest to the direction of player: + this->vClosestDirection = ((thisx->yawTowardsPlayer - thisx->world.rot.y) + 0x2000) & 0xC000; + this->vMovementMetric = 0.0f; + if (thisx->xzDistToPlayer < 200.0f) { + this->vMovementMetric = BEGIN_MOVE_OUT; + } + // If returning to origin: + } else { + // Of the four real world compass directions, get the one which is closest to the movement direction of + // the returning spike. Note that this is different from the previous usages of vClosestDirection + this->vClosestDirection = (Math_Vec3f_Yaw(&thisx->world.pos, &thisx->home.pos) + 0x2000) & 0xC000; + switch (this->vClosestDirection) { + case 0: // movement is closest to +z direction + if (thisx->bgCheckFlags & 8) { + if (ABS(thisx->wallYaw) > 0x6000) { + blockedOnReturn = true; + } + } else if (touchingActor && (ABS(angleToCollidedActor) > 0x6000)) { + blockedOnReturn = true; + } + break; + case 0x4000: // movement is closest to +x direction + if (thisx->bgCheckFlags & 8) { + if ((thisx->wallYaw < -0x2000) && (thisx->wallYaw > -0x6000)) { + blockedOnReturn = true; + } + } else if (touchingActor && (angleToCollidedActor < -0x2000) && + (angleToCollidedActor > -0x6000)) { + blockedOnReturn = true; + } + break; + case -0x8000: // movement is closest to -z direction + if (thisx->bgCheckFlags & 8) { + if (ABS(thisx->wallYaw) < 0x2000) { + blockedOnReturn = true; + } + } else if (touchingActor && (ABS(angleToCollidedActor) < 0x2000)) { + blockedOnReturn = true; + } + break; + case -0x4000: // movement is closest to -x direction + if (thisx->bgCheckFlags & 8) { + if ((thisx->wallYaw > 0x2000) && (thisx->wallYaw < 0x6000)) { + blockedOnReturn = true; + } + } else if (touchingActor && (angleToCollidedActor > 0x2000) && + (angleToCollidedActor < 0x6000)) { + blockedOnReturn = true; + } + break; + } + if (!blockedOnReturn) { + Math_SmoothStepToF(&thisx->world.pos.x, thisx->home.pos.x, 1.0f, 3.0f, 0.0f); + Math_SmoothStepToF(&thisx->world.pos.z, thisx->home.pos.z, 1.0f, 3.0f, 0.0f); + } + } + } + Actor_MoveForward(thisx); // Only used by straight line logic + // Adjust position using bgcheck, but do not adjust x, z position if in straight line mode: + if (thisx->params & SPIKETRAP_MODE_LINEAR) { + posTemp = thisx->world.pos; + } + Actor_UpdateBgCheckInfo(globalCtx, thisx, 25.0f, 20.0f, 20.0f, 0x1D); + if (thisx->params & SPIKETRAP_MODE_LINEAR) { + thisx->world.pos.x = posTemp.x; + thisx->world.pos.z = posTemp.z; + } + } + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (thisx->colorFilterTimer == 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnTrap_Draw(Actor* thisx, GlobalContext* globalCtx) { + func_8002EBCC(thisx, globalCtx, 1); + Gfx_DrawDListOpa(globalCtx, gSlidingBladeTrapDL); +} diff --git a/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.h b/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.h new file mode 100644 index 000000000..6f69707ac --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.h @@ -0,0 +1,35 @@ +#ifndef Z_EN_TRAP_H +#define Z_EN_TRAP_H + +#include "ultra64.h" +#include "global.h" + +// Different movement modes (set in params): +#define SPIKETRAP_MODE_LINEAR 0x10 +#define SPIKETRAP_MODE_CIRCULAR 0x20 +#define SPIKETRAP_MODE_FOUR_WAY 0x30 + +// When four-way mode is set, these flags decide on which directions are allowed: +#define SPIKETRAP_FOURWAY_FWD_ALLOWED (1 << 0) +#define SPIKETRAP_FOURWAY_BACK_ALLOWED (1 << 1) +#define SPIKETRAP_FOURWAY_LEFT_ALLOWED (1 << 2) +#define SPIKETRAP_FOURWAY_RIGHT_ALLOWED (1 << 3) + +struct EnTrap; + +typedef struct EnTrap { + /* 0x0000 */ Actor actor; + /* 0x014C */ s32 playerDmgTimer; + /* 0x0150 */ s16 upperParams; + /* 0x0152 */ s16 genericVar1; + /* 0x0154 */ f32 genericVar2; + /* 0x0158 */ Vec3f targetPosLeft; + /* 0x0164 */ Vec3f targetPosRight; + /* 0x0170 */ Vec3f targetPosFwd; + /* 0x017C */ Vec3f targetPosBack; + /* 0x0188 */ Vec3f moveSpeedLeftRight; + /* 0x0194 */ Vec3f moveSpeedForwardBack; + /* 0x01A0 */ ColliderCylinder collider; +} EnTrap; // size = 0x01EC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Tubo_Trap/z_en_tubo_trap.c b/soh/src/overlays/actors/ovl_En_Tubo_Trap/z_en_tubo_trap.c new file mode 100644 index 000000000..51ee17756 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tubo_Trap/z_en_tubo_trap.c @@ -0,0 +1,290 @@ +/* + * File: z_en_tubo_trap.c + * Overlay: ovl_En_Tubo_Trap + * Description: Flying pot enemy + */ + +#include "z_en_tubo_trap.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnTuboTrap_Init(Actor* thisx, GlobalContext* globalCtx); +void EnTuboTrap_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnTuboTrap_Update(Actor* thisx, GlobalContext* globalCtx); +void EnTuboTrap_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnTuboTrap_WaitForProximity(EnTuboTrap* this, GlobalContext* globalCtx); +void EnTuboTrap_Levitate(EnTuboTrap* this, GlobalContext* globalCtx); +void EnTuboTrap_Fly(EnTuboTrap* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { 9, 23, 0, { 0 } }, +}; + +const ActorInit En_Tubo_Trap_InitVars = { + ACTOR_EN_TUBO_TRAP, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(EnTuboTrap), + (ActorFunc)EnTuboTrap_Init, + (ActorFunc)EnTuboTrap_Destroy, + (ActorFunc)EnTuboTrap_Update, + (ActorFunc)EnTuboTrap_Draw, + NULL, +}; + +void EnTuboTrap_Init(Actor* thisx, GlobalContext* globalCtx) { + EnTuboTrap* this = (EnTuboTrap*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 2.0f); + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 壷トラップ ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); // "Urn Trap" + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Actor_SetScale(&this->actor, 0.1f); + this->actionFunc = EnTuboTrap_WaitForProximity; +} + +void EnTuboTrap_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnTuboTrap* this = (EnTuboTrap*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnTuboTrap_DropCollectible(EnTuboTrap* this, GlobalContext* globalCtx) { + s16 params = this->actor.params; + s16 param3FF = (params >> 6) & 0x3FF; + + if (param3FF >= 0 && param3FF < 0x1A) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, param3FF | ((params & 0x3F) << 8)); + } +} + +void EnTuboTrap_SpawnEffectsOnLand(EnTuboTrap* this, GlobalContext* globalCtx) { + f32 rand; + f32 cos; + f32 sin; + Vec3f pos; + Vec3f velocity; + s16 var; + s32 arg5; + s32 i; + Vec3f* actorPos = &this->actor.world.pos; + + for (i = 0, var = 0; i < 15; i++, var += 20000) { + sin = Math_SinS(var); + cos = Math_CosS(var); + pos.x = sin * 8.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = cos * 8.0f; + + velocity.x = pos.x * 0.23f; + velocity.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + velocity.z = pos.z * 0.23f; + + pos.x += actorPos->x; + pos.y += actorPos->y; + pos.z += actorPos->z; + + rand = Rand_ZeroOne(); + if (rand < 0.2f) { + arg5 = 96; + } else if (rand < 0.6f) { + arg5 = 64; + } else { + arg5 = 32; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, actorPos, -240, arg5, 10, 10, 0, + (Rand_ZeroOne() * 65.0f) + 15.0f, 0, 32, 60, KAKERA_COLOR_NONE, + OBJECT_GAMEPLAY_DANGEON_KEEP, gPotFragmentDL); + } + + func_80033480(globalCtx, actorPos, 30.0f, 4, 20, 50, 0); +} + +void EnTuboTrap_SpawnEffectsInWater(EnTuboTrap* this, GlobalContext* globalCtx) { + f32 rand; + f32 cos; + f32 sin; + Vec3f pos; + Vec3f velocity; + s16 var; + s32 arg5; + s32 i; + Vec3f* actorPos = &this->actor.world.pos; + + pos = *actorPos; + pos.y += this->actor.yDistToWater; + + EffectSsGSplash_Spawn(globalCtx, &pos, 0, 0, 0, 400); + + for (i = 0, var = 0; i < 15; i++, var += 20000) { + sin = Math_SinS(var); + cos = Math_CosS(var); + pos.x = sin * 8.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = cos * 8.0f; + + velocity.x = pos.x * 0.20f; + velocity.y = (Rand_ZeroOne() * 4.0f) + 2.0f; + velocity.z = pos.z * 0.20f; + + pos.x += actorPos->x; + pos.y += actorPos->y; + pos.z += actorPos->z; + + rand = Rand_ZeroOne(); + if (rand < 0.2f) { + arg5 = 64; + } else { + arg5 = 32; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, actorPos, -180, arg5, 30, 30, 0, + (Rand_ZeroOne() * 65.0f) + 15.0f, 0, 32, 70, KAKERA_COLOR_NONE, + OBJECT_GAMEPLAY_DANGEON_KEEP, gPotFragmentDL); + } +} + +void EnTuboTrap_HandleImpact(EnTuboTrap* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Player* player2 = GET_PLAYER(globalCtx); + + if ((this->actor.bgCheckFlags & 0x20) && (this->actor.yDistToWater > 15.0f)) { + EnTuboTrap_SpawnEffectsInWater(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_BOMB_DROP_WATER); + EnTuboTrap_DropCollectible(this, globalCtx); + Actor_Kill(&this->actor); + return; + } + + if (this->collider.base.atFlags & AT_BOUNCED) { + this->collider.base.atFlags &= ~AT_BOUNCED; + EnTuboTrap_SpawnEffectsOnLand(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_IT_SHIELD_REFLECT_SW); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN); + EnTuboTrap_DropCollectible(this, globalCtx); + Actor_Kill(&this->actor); + return; + } + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + EnTuboTrap_SpawnEffectsOnLand(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_EXPLOSION); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN); + EnTuboTrap_DropCollectible(this, globalCtx); + Actor_Kill(&this->actor); + return; + } + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~AT_HIT; + if (this->collider.base.at == &player->actor) { + EnTuboTrap_SpawnEffectsOnLand(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &player2->actor.world.pos, 40, NA_SE_PL_BODY_HIT); + EnTuboTrap_DropCollectible(this, globalCtx); + Actor_Kill(&this->actor); + return; + } + } + + if ((this->actor.bgCheckFlags & 8) || (this->actor.bgCheckFlags & 1)) { + EnTuboTrap_SpawnEffectsOnLand(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_POT_BROKEN); + EnTuboTrap_DropCollectible(this, globalCtx); + Actor_Kill(&this->actor); + return; + } +} + +void EnTuboTrap_WaitForProximity(EnTuboTrap* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 targetHeight; + + if (BREG(2) != 0) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ わて ☆☆☆☆☆ %f\n" VT_RST, this->actor.world.pos.y); // "You" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ おいどん ☆☆☆☆☆ %f\n" VT_RST, player->actor.world.pos.y); // "Me" + osSyncPrintf("\n\n"); + } + + if (this->actor.xzDistToPlayer < 200.0f && this->actor.world.pos.y <= player->actor.world.pos.y) { + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ENEMY); + this->actor.flags |= ACTOR_FLAG_0; + targetHeight = 40.0f + -10.0f * gSaveContext.linkAge; + + this->targetY = player->actor.world.pos.y + targetHeight; + if (this->targetY < this->actor.world.pos.y) { + this->targetY = this->actor.world.pos.y + targetHeight; + } + + this->originPos = this->actor.world.pos; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_POT_MOVE_START); + this->actionFunc = EnTuboTrap_Levitate; + } +} + +void EnTuboTrap_Levitate(EnTuboTrap* this, GlobalContext* globalCtx) { + this->actor.shape.rot.y += 5000; + Math_ApproachF(&this->actor.world.pos.y, this->targetY, 0.8f, 3.0f); + + if (fabsf(this->actor.world.pos.y - this->targetY) < 10.0f) { + this->actor.speedXZ = 10.0f; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actionFunc = EnTuboTrap_Fly; + } +} + +void EnTuboTrap_Fly(EnTuboTrap* this, GlobalContext* globalCtx) { + f32 dx = this->originPos.x - this->actor.world.pos.x; + f32 dy = this->originPos.y - this->actor.world.pos.y; + f32 dz = this->originPos.z - this->actor.world.pos.z; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_TUBOOCK_FLY - SFX_FLAG); + + if (240.0f < sqrtf(SQ(dx) + SQ(dy) + SQ(dz))) { + Math_ApproachF(&this->actor.gravity, -3.0f, 0.2f, 0.5f); + } + + this->actor.shape.rot.y += 5000; + EnTuboTrap_HandleImpact(this, globalCtx); +} + +void EnTuboTrap_Update(Actor* thisx, GlobalContext* globalCtx) { + EnTuboTrap* this = (EnTuboTrap*)thisx; + s32 pad; + + this->actionFunc(this, globalCtx); + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 20.0f, 0x1D); + Actor_SetFocus(&this->actor, 0.0f); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void EnTuboTrap_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gPotDL); +} diff --git a/soh/src/overlays/actors/ovl_En_Tubo_Trap/z_en_tubo_trap.h b/soh/src/overlays/actors/ovl_En_Tubo_Trap/z_en_tubo_trap.h new file mode 100644 index 000000000..d15ab4816 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Tubo_Trap/z_en_tubo_trap.h @@ -0,0 +1,19 @@ +#ifndef Z_EN_TUBO_TRAP_H +#define Z_EN_TUBO_TRAP_H + +#include "ultra64.h" +#include "global.h" + +struct EnTuboTrap; + +typedef void (*EnTuboTrapActionFunc)(struct EnTuboTrap*, GlobalContext*); + +typedef struct EnTuboTrap { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnTuboTrapActionFunc actionFunc; + /* 0x0150 */ f32 targetY; + /* 0x0154 */ Vec3f originPos; + /* 0x0160 */ ColliderCylinder collider; +} EnTuboTrap; // size = 0x01AC + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c b/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c new file mode 100644 index 000000000..17a3d392f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.c @@ -0,0 +1,809 @@ +/* + * File: z_en_vali.c + * Overlay: ovl_En_Vali + * Description: Bari (Big Jellyfish) + */ + +#include "z_en_vali.h" +#include "objects/object_vali/object_vali.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4 | ACTOR_FLAG_12) + +void EnVali_Init(Actor* thisx, GlobalContext* globalCtx); +void EnVali_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnVali_Update(Actor* thisx, GlobalContext* globalCtx); +void EnVali_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnVali_SetupLurk(EnVali* this); +void EnVali_SetupDropAppear(EnVali* this); + +void EnVali_Lurk(EnVali* this, GlobalContext* globalCtx); +void EnVali_DropAppear(EnVali* this, GlobalContext* globalCtx); +void EnVali_FloatIdle(EnVali* this, GlobalContext* globalCtx); +void EnVali_Attacked(EnVali* this, GlobalContext* globalCtx); +void EnVali_Retaliate(EnVali* this, GlobalContext* globalCtx); +void EnVali_MoveArmsDown(EnVali* this, GlobalContext* globalCtx); +void EnVali_Burnt(EnVali* this, GlobalContext* globalCtx); +void EnVali_DivideAndDie(EnVali* this, GlobalContext* globalCtx); +void EnVali_Stunned(EnVali* this, GlobalContext* globalCtx); +void EnVali_Frozen(EnVali* this, GlobalContext* globalCtx); +void EnVali_ReturnToLurk(EnVali* this, GlobalContext* globalCtx); + +const ActorInit En_Vali_InitVars = { + ACTOR_EN_VALI, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_VALI, + sizeof(EnVali), + (ActorFunc)EnVali_Init, + (ActorFunc)EnVali_Destroy, + (ActorFunc)EnVali_Update, + (ActorFunc)EnVali_Draw, + NULL, +}; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x07, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT8, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x07, 0x08 }, + { 0xFFCFFFFF, 0x01, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 17, 35, -15, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 2, 18, 32, MASS_HEAVY }; + +typedef enum { + /* 0x0 */ BARI_DMGEFF_NONE, + /* 0x1 */ BARI_DMGEFF_STUN, + /* 0x2 */ BARI_DMGEFF_FIRE, + /* 0x3 */ BARI_DMGEFF_ICE, + /* 0xE */ BARI_DMGEFF_SLINGSHOT = 0xE, + /* 0xF */ BARI_DMGEFF_SWORD +} BariDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, BARI_DMGEFF_STUN), + /* Deku stick */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(0, BARI_DMGEFF_SLINGSHOT), + /* Explosive */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(0, BARI_DMGEFF_STUN), + /* Normal arrow */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Kokiri sword */ DMG_ENTRY(1, BARI_DMGEFF_SWORD), + /* Master sword */ DMG_ENTRY(2, BARI_DMGEFF_SWORD), + /* Giant's Knife */ DMG_ENTRY(4, BARI_DMGEFF_SWORD), + /* Fire arrow */ DMG_ENTRY(4, BARI_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(4, BARI_DMGEFF_ICE), + /* Light arrow */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Unk arrow 1 */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(4, BARI_DMGEFF_FIRE), + /* Ice magic */ DMG_ENTRY(4, BARI_DMGEFF_ICE), + /* Light magic */ DMG_ENTRY(0, BARI_DMGEFF_NONE), + /* Shield */ DMG_ENTRY(0, BARI_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, BARI_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, BARI_DMGEFF_NONE), + /* Giant spin */ DMG_ENTRY(4, BARI_DMGEFF_NONE), + /* Master spin */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Kokiri jump */ DMG_ENTRY(2, BARI_DMGEFF_NONE), + /* Giant jump */ DMG_ENTRY(8, BARI_DMGEFF_NONE), + /* Master jump */ DMG_ENTRY(4, BARI_DMGEFF_NONE), + /* Unknown 1 */ DMG_ENTRY(0, BARI_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, BARI_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, BARI_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, BARI_DMGEFF_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x18, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 5000, ICHAIN_STOP), +}; + +void EnVali_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnVali* this = (EnVali*)thisx; + s32 bgId; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 27.0f); + this->actor.shape.shadowAlpha = 155; + SkelAnime_Init(globalCtx, &this->skelAnime, &gBariSkel, &gBariLurkingAnim, this->jointTable, this->morphTable, + EN_VALI_LIMB_MAX); + + Collider_InitQuad(globalCtx, &this->leftArmCollider); + Collider_SetQuad(globalCtx, &this->leftArmCollider, &this->actor, &sQuadInit); + Collider_InitQuad(globalCtx, &this->rightArmCollider); + Collider_SetQuad(globalCtx, &this->rightArmCollider, &this->actor, &sQuadInit); + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + + EnVali_SetupLurk(this); + + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId, + &this->actor, &this->actor.world.pos); + this->actor.params = BARI_TYPE_NORMAL; + + if (this->actor.floorHeight == BGCHECK_Y_MIN) { + Actor_Kill(&this->actor); + } +} + +void EnVali_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnVali* this = (EnVali*)thisx; + + Collider_DestroyQuad(globalCtx, &this->leftArmCollider); + Collider_DestroyQuad(globalCtx, &this->rightArmCollider); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); +} + +void EnVali_SetupLurk(EnVali* this) { + Animation_PlayLoop(&this->skelAnime, &gBariLurkingAnim); + this->actor.draw = NULL; + this->bodyCollider.base.acFlags &= ~AC_ON; + this->actionFunc = EnVali_Lurk; +} + +void EnVali_SetupDropAppear(EnVali* this) { + this->actor.draw = EnVali_Draw; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.velocity.y = 1.0f; + this->actionFunc = EnVali_DropAppear; +} + +void EnVali_SetupFloatIdle(EnVali* this) { + Animation_MorphToLoop(&this->skelAnime, &gBariWaitingAnim, -3.0f); + this->leftArmCollider.dim.quad[2] = this->leftArmCollider.dim.quad[3] = this->rightArmCollider.dim.quad[2] = + this->rightArmCollider.dim.quad[3] = this->leftArmCollider.dim.quad[0] = this->leftArmCollider.dim.quad[1] = + this->rightArmCollider.dim.quad[0] = this->rightArmCollider.dim.quad[1] = this->actor.world.pos; + + this->leftArmCollider.dim.quad[2].y = this->leftArmCollider.dim.quad[3].y = this->rightArmCollider.dim.quad[2].y = + this->rightArmCollider.dim.quad[3].y = this->leftArmCollider.dim.quad[0].y = + this->leftArmCollider.dim.quad[1].y = this->rightArmCollider.dim.quad[0].y = + this->rightArmCollider.dim.quad[1].y = this->actor.world.pos.y - 10.0f; + + this->actor.flags &= ~ACTOR_FLAG_4; + this->bodyCollider.base.acFlags |= AC_ON; + this->slingshotReactionTimer = 0; + this->floatHomeHeight = this->actor.world.pos.y; + this->actionFunc = EnVali_FloatIdle; +} + +/** + * Used for both touching player/player's shield and being hit with sword. What to do next is determined by params. + */ +void EnVali_SetupAttacked(EnVali* this) { + this->lightningTimer = 20; + this->actor.flags &= ~ACTOR_FLAG_0; + this->bodyCollider.base.acFlags &= ~AC_ON; + this->actionFunc = EnVali_Attacked; +} + +void EnVali_SetupRetaliate(EnVali* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBariRetaliatingAnim, -5.0f); + Actor_SetColorFilter(&this->actor, 0x4000, 150, 0x2000, 30); + this->actor.params = BARI_TYPE_NORMAL; + this->bodyCollider.base.acFlags &= ~AC_ON; + this->actionFunc = EnVali_Retaliate; +} + +void EnVali_SetupMoveArmsDown(EnVali* this) { + Animation_PlayOnce(&this->skelAnime, &gBariMovingArmsDownAnim); + this->actionFunc = EnVali_MoveArmsDown; +} + +void EnVali_SetupBurnt(EnVali* this) { + this->timer = 2; + this->bodyCollider.base.acFlags &= ~AC_ON; + Actor_SetColorFilter(&this->actor, 0x4000, 150, 0x2000, 30); + this->actionFunc = EnVali_Burnt; +} + +void EnVali_SetupDivideAndDie(EnVali* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 3; i++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BILI, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, this->actor.world.rot.y, 0, 0); + + this->actor.world.rot.y += 0x10000 / 3; + } + + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0x50); + this->timer = Rand_S16Offset(10, 10); + this->bodyCollider.base.acFlags &= ~AC_ON; + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EN_BARI_SPLIT); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.draw = NULL; + this->actionFunc = EnVali_DivideAndDie; +} + +void EnVali_SetupStunned(EnVali* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBariWaitingAnim, 10.0f); + this->timer = 80; + this->actor.velocity.y = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 255, 0x2000, 80); + this->bodyCollider.info.bumper.effect = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actor.velocity.y = 1.0f; + this->actionFunc = EnVali_Stunned; +} + +void EnVali_SetupFrozen(EnVali* this) { + this->actor.velocity.y = 0.0f; + Actor_SetColorFilter(&this->actor, 0, 255, 0x2000, 36); + this->bodyCollider.base.acFlags &= ~AC_ON; + this->timer = 36; + this->actionFunc = EnVali_Frozen; +} + +void EnVali_SetupReturnToLurk(EnVali* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gBariLurkingAnim, 10.0f); + this->actor.flags |= ACTOR_FLAG_4; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnVali_ReturnToLurk; +} + +void EnVali_DischargeLightning(EnVali* this, GlobalContext* globalCtx) { + static Color_RGBA8 primColor = { 255, 255, 255, 255 }; + static Color_RGBA8 envColor = { 200, 255, 255, 255 }; + Vec3f pos; + s32 i; + f32 cos; + f32 sin; + s16 yaw; + + for (i = 0; i < 4; i++) { + cos = -Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx))); + sin = Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx))); + if (!((this->lightningTimer + (i << 1)) % 4)) { + yaw = (s16)Rand_CenteredFloat(12288.0f) + (i * 0x4000) + 0x2000; + pos.x = this->actor.world.pos.x + (Math_SinS(yaw) * 12.0f * cos); + pos.y = this->actor.world.pos.y - (Math_CosS(yaw) * 12.0f) + 10.0f; + pos.z = this->actor.world.pos.z + (Math_SinS(yaw) * 12.0f * sin); + + EffectSsLightning_Spawn(globalCtx, &pos, &primColor, &envColor, 17, yaw, 6, 2); + } + } + + func_8002F974(&this->actor, NA_SE_EN_BIRI_SPARK - SFX_FLAG); +} + +void EnVali_Lurk(EnVali* this, GlobalContext* globalCtx) { + if (this->actor.xzDistToPlayer < 150.0f) { + EnVali_SetupDropAppear(this); + } +} + +void EnVali_DropAppear(EnVali* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + this->actor.velocity.y *= 1.5f; + this->actor.velocity.y = CLAMP_MAX(this->actor.velocity.y, 40.0f); + + if (Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, this->actor.velocity.y)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + EnVali_SetupFloatIdle(this); + } +} + +void EnVali_FloatIdle(EnVali* this, GlobalContext* globalCtx) { + s32 curFrame; + + SkelAnime_Update(&this->skelAnime); + + if (this->slingshotReactionTimer != 0) { + this->slingshotReactionTimer--; + } + + curFrame = this->skelAnime.curFrame; + + Math_StepToF(&this->floatHomeHeight, this->actor.floorHeight + 40.0f, 1.2f); + this->actor.world.pos.y = this->floatHomeHeight - (sinf(curFrame * M_PI * 0.0125f) * 8.0f); + + if (this->slingshotReactionTimer) { + this->actor.shape.rot.y += 0x800; + + if (((this->slingshotReactionTimer % 6) == 0) && (curFrame > 15) && (curFrame <= 55)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_ROLL); + } + } else if ((curFrame == 16) || (curFrame == 30) || (curFrame == 42) || (curFrame == 55)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_ROLL); + } + + curFrame = ((curFrame > 40) ? (80 - curFrame) : curFrame); + + this->actor.shape.rot.y += (s16)((curFrame + 4) * 0.4f * (0x10000 / 360.0f)); + if (this->actor.xzDistToPlayer > 250.0f) { + EnVali_SetupReturnToLurk(this); + } +} + +void EnVali_Attacked(EnVali* this, GlobalContext* globalCtx) { + if (this->lightningTimer != 0) { + this->lightningTimer--; + } + + EnVali_DischargeLightning(this, globalCtx); + + if (this->lightningTimer == 0) { + this->actor.flags |= ACTOR_FLAG_0; + this->bodyCollider.base.acFlags |= AC_ON; + if (this->actor.params == BARI_TYPE_SWORD_DAMAGE) { + EnVali_SetupRetaliate(this); + } else { + this->actionFunc = EnVali_FloatIdle; + } + } else if ((this->lightningTimer % 2) != 0) { + this->actor.world.pos.y += 1.0f; + } else { + this->actor.world.pos.y -= 1.0f; + } +} + +void EnVali_Retaliate(EnVali* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.colChkInfo.health != 0) { + EnVali_SetupMoveArmsDown(this); + } else { + EnVali_SetupDivideAndDie(this, globalCtx); + } + } +} + +void EnVali_MoveArmsDown(EnVali* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + EnVali_SetupFloatIdle(this); + } +} + +void EnVali_Burnt(EnVali* this, GlobalContext* globalCtx) { + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + EnVali_SetupDivideAndDie(this, globalCtx); + } +} + +void EnVali_DivideAndDie(EnVali* this, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + s16 scale; + Vec3f pos; + s32 i; + + if (this->timer != 0) { + this->timer--; + } + + for (i = 0; i < 2; i++) { + pos.x = this->actor.world.pos.x + Rand_CenteredFloat(20.0f); + pos.y = this->actor.world.pos.y + Rand_CenteredFloat(8.0f); + pos.z = this->actor.world.pos.z + Rand_CenteredFloat(20.0f); + velocity.y = (Rand_ZeroOne() + 1.0f); + scale = Rand_S16Offset(40, 40); + + if (Rand_ZeroOne() < 0.7f) { + EffectSsDtBubble_SpawnColorProfile(globalCtx, &pos, &velocity, &accel, scale, 25, 2, 1); + } else { + EffectSsDtBubble_SpawnColorProfile(globalCtx, &pos, &velocity, &accel, scale, 25, 0, 1); + } + } + + if (this->timer == 0) { + Actor_Kill(&this->actor); + } +} + +void EnVali_Stunned(EnVali* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->timer != 0) { + this->timer--; + } + + if (this->actor.velocity.y != 0.0f) { + if (Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, this->actor.velocity.y)) { + this->actor.velocity.y = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } else { + this->actor.velocity.y += 1.0f; + } + } + + if (this->timer == 0) { + this->bodyCollider.info.bumper.effect = 1; // Shock? + EnVali_SetupFloatIdle(this); + } +} + +void EnVali_Frozen(EnVali* this, GlobalContext* globalCtx) { + Vec3f pos; + s32 temp_v0; + s32 temp_v1; + + if (this->timer != 0) { + this->timer--; + } + + temp_v1 = this->timer - 20; + this->actor.colorFilterTimer = 36; + + if (temp_v1 > 0) { + temp_v0 = temp_v1 >> 1; + + if ((this->timer % 2) != 0) { + pos.y = this->actor.world.pos.y - 20.0f + (-temp_v0 * 5 + 40); + pos.x = this->actor.world.pos.x + ((temp_v0 & 2) ? 12.0f : -12.0f); + pos.z = this->actor.world.pos.z + ((temp_v0 & 1) ? 12.0f : -12.0f); + + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, &this->actor, &pos, 150, 150, 150, 250, 235, 245, 255, + (Rand_ZeroOne() * 0.2f) + 1.3f); + } + } else if (this->timer == 0) { + this->actor.velocity.y += 1.0f; + if (Math_StepToF(&this->actor.world.pos.y, this->actor.floorHeight, this->actor.velocity.y)) { + EnVali_SetupDivideAndDie(this, globalCtx); + this->actor.colorFilterTimer = 0; + } + } +} + +void EnVali_ReturnToLurk(EnVali* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.home.pos.y, 0.5f, 15.0f, 0.1f) < 0.01f) { + EnVali_SetupLurk(this); + } +} + +void EnVali_UpdateDamage(EnVali* this, GlobalContext* globalCtx) { + if (this->bodyCollider.base.acFlags & AC_HIT) { + this->bodyCollider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->bodyCollider.info, 1); + + if ((this->actor.colChkInfo.damageEffect != BARI_DMGEFF_NONE) || (this->actor.colChkInfo.damage != 0)) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_DEAD); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + this->actor.flags &= ~ACTOR_FLAG_0; + } else if ((this->actor.colChkInfo.damageEffect != BARI_DMGEFF_STUN) && + (this->actor.colChkInfo.damageEffect != BARI_DMGEFF_SLINGSHOT)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BARI_DAMAGE); + } + + if (this->actor.colChkInfo.damageEffect == BARI_DMGEFF_STUN) { + if (this->actionFunc != EnVali_Stunned) { + EnVali_SetupStunned(this); + } + } else if (this->actor.colChkInfo.damageEffect == BARI_DMGEFF_SWORD) { + if (this->actionFunc != EnVali_Stunned) { + Actor_SetColorFilter(&this->actor, 0x4000, 150, 0x2000, 30); + this->actor.params = BARI_TYPE_SWORD_DAMAGE; + EnVali_SetupAttacked(this); + } else { + EnVali_SetupRetaliate(this); + } + } else if (this->actor.colChkInfo.damageEffect == BARI_DMGEFF_FIRE) { + EnVali_SetupBurnt(this); + } else if (this->actor.colChkInfo.damageEffect == BARI_DMGEFF_ICE) { + EnVali_SetupFrozen(this); + } else if (this->actor.colChkInfo.damageEffect == BARI_DMGEFF_SLINGSHOT) { + if (this->slingshotReactionTimer == 0) { + this->slingshotReactionTimer = 20; + } + } else { + EnVali_SetupRetaliate(this); + } + } + } +} + +void EnVali_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnVali* this = (EnVali*)thisx; + + if ((this->bodyCollider.base.atFlags & AT_HIT) || (this->leftArmCollider.base.atFlags & AT_HIT) || + (this->rightArmCollider.base.atFlags & AT_HIT)) { + this->leftArmCollider.base.atFlags &= ~AT_HIT; + this->rightArmCollider.base.atFlags &= ~AT_HIT; + this->bodyCollider.base.atFlags &= ~AT_HIT; + EnVali_SetupAttacked(this); + } + + EnVali_UpdateDamage(this, globalCtx); + this->actionFunc(this, globalCtx); + + if ((this->actionFunc != EnVali_DivideAndDie) && (this->actionFunc != EnVali_Lurk)) { + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + + if (this->actionFunc == EnVali_FloatIdle) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->leftArmCollider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->rightArmCollider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + + if (this->bodyCollider.base.acFlags & AC_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + Actor_SetFocus(&this->actor, 0.0f); + } +} + +// Draw and associated functions + +void EnVali_PulseOutside(EnVali* this, f32 curFrame, Vec3f* scale) { + f32 scaleChange; + + if (this->actionFunc == EnVali_Attacked) { + s32 scalePhase = 20 - (this->lightningTimer % 20); + + if (scalePhase >= 10) { + scalePhase -= 10; + } + + scale->y -= 0.2f * sinf((M_PI / 10) * scalePhase); + } else if (this->actionFunc == EnVali_Retaliate) { + scaleChange = sinf((M_PI / 10) * curFrame); + scale->y -= 0.24f * scaleChange; + scale->x -= 0.13f * scaleChange; + scale->z = scale->x; + } else if (this->actionFunc == EnVali_MoveArmsDown) { + scaleChange = cosf((M_PI / 50) * curFrame); + scale->y -= 0.24f * scaleChange; + scale->x -= 0.13f * scaleChange; + scale->z = scale->x; + } else if (this->actionFunc == EnVali_Stunned) { + scaleChange = sinf((M_PI / 10) * this->timer) * 0.08f; + scale->x += scaleChange; + scale->y -= scaleChange; + scale->z += scaleChange; + } else { + if (curFrame >= 40.0f) { + curFrame -= 40.0f; + } + + scale->y -= 0.2f * sinf((M_PI / 40) * curFrame); + } +} + +void EnVali_PulseInsides(EnVali* this, f32 curFrame, Vec3f* scale) { + f32 scaleChange; + + if (this->actionFunc == EnVali_Attacked) { + s32 scalePhase = 20 - (this->lightningTimer % 20); + + if (scalePhase >= 10) { + scalePhase -= 10; + } + + scale->y -= 0.13f * sinf((M_PI / 10) * scalePhase); + } else if (this->actionFunc == EnVali_Retaliate) { + scaleChange = sinf((M_PI / 10) * curFrame); + scale->y -= 0.18f * scaleChange; + scale->x -= 0.1f * scaleChange; + scale->z = scale->x; + } else if (this->actionFunc == EnVali_MoveArmsDown) { + scaleChange = cosf((M_PI / 50) * curFrame); + scale->y -= 0.18f * scaleChange; + scale->x -= 0.1f * scaleChange; + scale->z = scale->x; + } else if (this->actionFunc == EnVali_Stunned) { + scaleChange = sinf((M_PI / 10) * this->timer) * 0.08f; + scale->x -= scaleChange; + scale->y += scaleChange; + scale->z -= scaleChange; + } else { + if (curFrame >= 40.0f) { + curFrame -= 40.0f; + } + + scale->y -= 0.13f * sinf((M_PI / 40) * curFrame); + } +} + +s32 EnVali_SetArmLength(EnVali* this, f32 curFrame) { + f32 targetArmScale; + + if (this->actionFunc == EnVali_FloatIdle) { + if (curFrame <= 10.0f) { + targetArmScale = curFrame * 0.05f + 1.0f; + } else if (curFrame > 70.0f) { + targetArmScale = (80.0f - curFrame) * 0.05f + 1.0f; + } else { + targetArmScale = 1.5f; + } + } else if (this->actionFunc == EnVali_Retaliate) { + targetArmScale = 1.0f - sinf((M_PI / 10) * curFrame) * 0.35f; + } else if (this->actionFunc == EnVali_MoveArmsDown) { + targetArmScale = 1.0f - cosf((M_PI / 50) * curFrame) * 0.35f; + } else if ((this->actionFunc == EnVali_Attacked) || (this->actionFunc == EnVali_Frozen)) { + targetArmScale = this->armScale; + } else { + targetArmScale = 1.0f; + } + + Math_StepToF(&this->armScale, targetArmScale, 0.1f); + + if (this->armScale == 1.0f) { + return false; + } else { + return true; + } +} + +s32 EnVali_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnVali* this = (EnVali*)thisx; + f32 curFrame; + + if ((limbIndex == EN_VALI_LIMB_NUCLEUS) || (limbIndex == EN_VALI_LIMB_OUTER_HOOD) || + (limbIndex == EN_VALI_LIMB_INNER_HOOD)) { + *dList = NULL; + return false; + } else { + curFrame = this->skelAnime.curFrame; + + if ((limbIndex == EN_VALI_LIMB_LEFT_ARM_BASE) || (limbIndex == EN_VALI_LIMB_RIGHT_ARM_BASE)) { + if (EnVali_SetArmLength(this, curFrame)) { + Matrix_Scale(this->armScale, 1.0f, 1.0f, MTXMODE_APPLY); + } + } + + return false; + } +} + +void EnVali_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + static Vec3f D_80B28970 = { 3000.0f, 0.0f, 0.0f }; + static Vec3f D_80B2897C = { -1000.0f, 0.0f, 0.0f }; + Vec3f sp3C; + Vec3f sp30; + EnVali* this = (EnVali*)thisx; + + if (this->actionFunc == EnVali_FloatIdle) { + if ((limbIndex == EN_VALI_LIMB_LEFT_FOREARM_BASE) || (limbIndex == EN_VALI_LIMB_RIGHT_FOREARM_BASE)) { + Matrix_MultVec3f(&D_80B28970, &sp3C); + Matrix_MultVec3f(&D_80B2897C, &sp30); + + if (limbIndex == EN_VALI_LIMB_LEFT_FOREARM_BASE) { + Collider_SetQuadVertices(&this->leftArmCollider, &sp30, &sp3C, &this->leftArmCollider.dim.quad[0], + &this->leftArmCollider.dim.quad[1]); + } else { + Collider_SetQuadVertices(&this->rightArmCollider, &sp30, &sp3C, &this->rightArmCollider.dim.quad[0], + &this->rightArmCollider.dim.quad[1]); + } + } + } +} + +void EnVali_DrawBody(EnVali* this, GlobalContext* globalCtx) { + MtxF mtx; + f32 cos; + f32 sin; + f32 curFrame; + Vec3f scale = { 1.0f, 1.0f, 1.0f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_vali.c", 1428); + + Matrix_Get(&mtx); + curFrame = this->skelAnime.curFrame; + EnVali_PulseInsides(this, curFrame, &scale); + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vali.c", 1436), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBariInnerHoodDL); + + Matrix_Put(&mtx); + Matrix_RotateY(-this->actor.shape.rot.y * (M_PI / 32768.0f), MTXMODE_APPLY); + + cos = Math_CosS(this->actor.shape.rot.y); + sin = Math_SinS(this->actor.shape.rot.y); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vali.c", 1446), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBariNucleusDL); + + Matrix_Translate((506.0f * cos) + (372.0f * sin), 1114.0f, (372.0f * cos) - (506.0f * sin), MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vali.c", 1455), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBariNucleusDL); + + Matrix_Translate((-964.0f * cos) - (804.0f * sin), -108.0f, (-804.0f * cos) + (964.0f * sin), MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vali.c", 1463), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBariNucleusDL); + + Matrix_Put(&mtx); + + scale.x = scale.y = scale.z = 1.0f; + + EnVali_PulseOutside(this, curFrame, &scale); + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vali.c", 1471), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gBariOuterHoodDL); + + Matrix_Put(&mtx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_vali.c", 1477); +} + +static Gfx D_80B28998[] = { + gsDPSetCombineLERP(1, TEXEL0, SHADE, 0, TEXEL0, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0, TEXEL1, 0, + PRIM_LOD_FRAC, COMBINED), + gsSPEndDisplayList(), +}; + +static Gfx D_80B289A8[] = { + gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, TEXEL0, 0, PRIMITIVE, 0, COMBINED, 0, PRIMITIVE, 0, TEXEL1, 0, + PRIM_LOD_FRAC, COMBINED), + gsSPEndDisplayList(), +}; + +void EnVali_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnVali* this = (EnVali*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_vali.c", 1505); + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TexScroll(globalCtx->state.gfxCtx, 0, (127 - (globalCtx->gameplayFrames * 12)) % 128, 32, 32)); + + if ((this->lightningTimer % 2) != 0) { + gSPSegment(POLY_XLU_DISP++, 0x09, D_80B28998); + } else { + gSPSegment(POLY_XLU_DISP++, 0x09, D_80B289A8); + } + + EnVali_DrawBody(this, globalCtx); + + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnVali_OverrideLimbDraw, EnVali_PostLimbDraw, this, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_vali.c", 1538); +} diff --git a/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.h b/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.h new file mode 100644 index 000000000..9c98ca3fc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vali/z_en_vali.h @@ -0,0 +1,65 @@ +#ifndef Z_EN_VALI_H +#define Z_EN_VALI_H + +#include "ultra64.h" +#include "global.h" + +struct EnVali; + +typedef void (*EnValiActionFunc)(struct EnVali*, GlobalContext*); + +typedef enum { + /* 0x00 */ EN_VALI_LIMB_NONE, + /* 0x01 */ EN_VALI_LIMB_NUCLEUS_BASE, + /* 0x02 */ EN_VALI_LIMB_NUCLEUS, + /* 0x03 */ EN_VALI_LIMB_RIGHT_MANDIBLE_BASE_BASE, + /* 0x04 */ EN_VALI_LIMB_RIGHT_MANDIBLE_BASE, + /* 0x05 */ EN_VALI_LIMB_RIGHT_MANDIBLE, + /* 0x06 */ EN_VALI_LIMB_LEFT_MANDIBLE_BASE_BASE, + /* 0x07 */ EN_VALI_LIMB_LEFT_MANDIBLE_BASE, + /* 0x08 */ EN_VALI_LIMB_LEFT_MANDIBLE, + /* 0x09 */ EN_VALI_LIMB_LEFT_ARM_BASE, + /* 0x0A */ EN_VALI_LIMB_LEFT_UPPER_ARM_BASE, + /* 0x0B */ EN_VALI_LIMB_LEFT_FOREARM_BASE, + /* 0x0C */ EN_VALI_LIMB_LEFT_CLAW_BODY_BASE, + /* 0x0D */ EN_VALI_LIMB_LEFT_CLAW_TIP_BASE, + /* 0x0E */ EN_VALI_LIMB_LEFT_CLAW_TIP, + /* 0x0F */ EN_VALI_LIMB_LEFT_CLAW_BODY, + /* 0x10 */ EN_VALI_LIMB_LEFT_FOREARM, + /* 0x11 */ EN_VALI_LIMB_LEFT_UPPER_ARM, + /* 0x12 */ EN_VALI_LIMB_RIGHT_ARM_BASE, + /* 0x13 */ EN_VALI_LIMB_RIGHT_UPPER_ARM_BASE, + /* 0x14 */ EN_VALI_LIMB_RIGHT_FOREARM_BASE, + /* 0x15 */ EN_VALI_LIMB_RIGHT_CLAW_BODY_BASE, + /* 0x16 */ EN_VALI_LIMB_RIGHT_CLAW_TIP_BASE, + /* 0x17 */ EN_VALI_LIMB_RIGHT_CLAW_TIP, + /* 0x18 */ EN_VALI_LIMB_RIGHT_CLAW_BODY, + /* 0x19 */ EN_VALI_LIMB_RIGHT_FOREARM, + /* 0x1A */ EN_VALI_LIMB_RIGHT_UPPER_ARM, + /* 0x1B */ EN_VALI_LIMB_INNER_HOOD, + /* 0x1C */ EN_VALI_LIMB_OUTER_HOOD, + /* 0x1D */ EN_VALI_LIMB_MAX +} EnValiLimb; + +typedef struct EnVali { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnValiActionFunc actionFunc; + /* 0x0194 */ u8 lightningTimer; + /* 0x0195 */ u8 slingshotReactionTimer; + /* 0x0196 */ s16 timer; + /* 0x0198 */ Vec3s jointTable[EN_VALI_LIMB_MAX]; + /* 0x0246 */ Vec3s morphTable[EN_VALI_LIMB_MAX]; + /* 0x02F4 */ f32 armScale; + /* 0x02F8 */ f32 floatHomeHeight; // Used as a centre for floating when visible (home is used for the lurk height) + /* 0x02FC */ ColliderQuad leftArmCollider; + /* 0x037C */ ColliderQuad rightArmCollider; + /* 0x03FC */ ColliderCylinder bodyCollider; +} EnVali; // size = 0x0448 + +typedef enum { + /* 0 */ BARI_TYPE_NORMAL, + /* 1 */ BARI_TYPE_SWORD_DAMAGE +} EnValiType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Vase/z_en_vase.c b/soh/src/overlays/actors/ovl_En_Vase/z_en_vase.c new file mode 100644 index 000000000..6afb210ef --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vase/z_en_vase.c @@ -0,0 +1,42 @@ +/* + * File: z_en_vase.c + * Overlay: ovl_En_Vase + * Description: An unused, orange pot based on ALTTP. Lacks collision. + */ + +#include "z_en_vase.h" +#include "objects/object_vase/object_vase.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnVase_Init(Actor* thisx, GlobalContext* globalCtx); +void EnVase_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnVase_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Vase_InitVars = { + ACTOR_EN_VASE, + ACTORCAT_PROP, + FLAGS, + OBJECT_VASE, + sizeof(EnVase), + (ActorFunc)EnVase_Init, + (ActorFunc)EnVase_Destroy, + (ActorFunc)Actor_Noop, + (ActorFunc)EnVase_Draw, + NULL, +}; + +void EnVase_Init(Actor* thisx, GlobalContext* globalCtx) { + EnVase* this = (EnVase*)thisx; + + Actor_SetScale(&this->actor, 0.01f); + this->actor.focus.pos = this->actor.world.pos; + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 6.0f); +} + +void EnVase_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnVase_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gUnusedVaseDL); +} diff --git a/soh/src/overlays/actors/ovl_En_Vase/z_en_vase.h b/soh/src/overlays/actors/ovl_En_Vase/z_en_vase.h new file mode 100644 index 000000000..57ee0d577 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vase/z_en_vase.h @@ -0,0 +1,13 @@ +#ifndef Z_EN_VASE_H +#define Z_EN_VASE_H + +#include "ultra64.h" +#include "global.h" + +struct EnVase; + +typedef struct EnVase { + /* 0x0000 */ Actor actor; +} EnVase; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.c b/soh/src/overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.c new file mode 100644 index 000000000..0b7964854 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.c @@ -0,0 +1,326 @@ +/* + * File: z_en_vb_ball.c + * Overlay: ovl_En_Vb_Ball + * Description: Volvagia's rocks and bones + */ + +#include "z_en_vb_ball.h" +#include "objects/object_fd/object_fd.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/actors/ovl_Boss_Fd/z_boss_fd.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EnVbBall_Init(Actor* thisx, GlobalContext* globalCtx); +void EnVbBall_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnVbBall_Update(Actor* thisx, GlobalContext* globalCtx); +void EnVbBall_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit En_Vb_Ball_InitVars = { + 0, + ACTORCAT_BOSS, + FLAGS, + OBJECT_FD, + sizeof(EnVbBall), + (ActorFunc)EnVbBall_Init, + (ActorFunc)EnVbBall_Destroy, + (ActorFunc)EnVbBall_Update, + (ActorFunc)EnVbBall_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK6, + { 0x00100700, 0x00, 0x20 }, + { 0x00100700, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 20, 30, 10, { 0, 0, 0 } }, +}; + +void EnVbBall_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnVbBall* this = (EnVbBall*)thisx; + s32 pad2; + f32 angle; + + if (this->actor.params >= 200) { // Volvagia's bones + this->yRotVel = Rand_CenteredFloat(0x300); + this->xRotVel = Rand_CenteredFloat(0x300); + angle = Math_FAtan2F(this->actor.world.pos.x, this->actor.world.pos.z); + this->actor.velocity.y = Rand_ZeroFloat(3.0f); + this->actor.velocity.x = 2.0f * sinf(angle); + this->actor.velocity.z = 2.0f * cosf(angle); + this->actor.gravity = -0.8f; + } else { // Volvagia's rocks + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Actor_SetScale(&this->actor, this->actor.world.rot.z / 10000.0f); + this->collider.dim.radius = this->actor.scale.y * 3000.0f; + this->collider.dim.height = this->actor.scale.y * 5000.0f; + this->collider.dim.yShift = this->actor.scale.y * -2500.0f; + this->xRotVel = Rand_CenteredFloat(0x2000); + this->yRotVel = Rand_CenteredFloat(0x2000); + this->shadowSize = this->actor.scale.y * 68.0f; + } +} + +void EnVbBall_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnVbBall* this = (EnVbBall*)thisx; + + if (this->actor.params < 200) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnVbBall_SpawnDebris(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* position, Vec3f* velocity, + Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_DEBRIS; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->scale = scale / 1000.0f; + effect->vFdFxRotX = Rand_ZeroFloat(100.0f); + effect->vFdFxRotY = Rand_ZeroFloat(100.0f); + break; + } + } +} + +void EnVbBall_SpawnDust(GlobalContext* globalCtx, BossFdEffect* effect, Vec3f* position, Vec3f* velocity, + Vec3f* acceleration, f32 scale) { + s16 i; + + for (i = 0; i < 180; i++, effect++) { + if (effect->type == BFD_FX_NONE) { + effect->type = BFD_FX_DUST; + effect->pos = *position; + effect->velocity = *velocity; + effect->accel = *acceleration; + effect->timer2 = 0; + effect->scale = scale / 400.0f; + break; + } + } +} + +void EnVbBall_UpdateBones(EnVbBall* this, GlobalContext* globalCtx) { + BossFd* bossFd = (BossFd*)this->actor.parent; + f32 pad2; + f32 pad1; + f32 angle; + s16 i; + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4); + if ((this->actor.bgCheckFlags & 1) && (this->actor.velocity.y <= 0.0f)) { + this->xRotVel = Rand_CenteredFloat((f32)0x4000); + this->yRotVel = Rand_CenteredFloat((f32)0x4000); + angle = Math_FAtan2F(this->actor.world.pos.x, this->actor.world.pos.z); + this->actor.velocity.x = sinf(angle) * 10.0f; + this->actor.velocity.z = cosf(angle) * 10.0f; + this->actor.velocity.y *= -0.5f; + if (this->actor.params & 1) { + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_LAND, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + for (i = 0; i < 10; i++) { + Vec3f dustVel = { 0.0f, 0.0f, 0.0f }; + Vec3f dustAcc = { 0.0f, 0.0f, 0.0f }; + Vec3f dustPos; + + dustVel.x = Rand_CenteredFloat(8.0f); + dustVel.y = Rand_ZeroFloat(1.0f); + dustVel.z = Rand_CenteredFloat(8.0f); + + dustAcc.y = 0.3f; + + dustPos.x = Rand_CenteredFloat(20.0f) + this->actor.world.pos.x; + dustPos.y = this->actor.floorHeight + 10.0f; + dustPos.z = Rand_CenteredFloat(20.0f) + this->actor.world.pos.z; + + EnVbBall_SpawnDust(globalCtx, bossFd->effects, &dustPos, &dustVel, &dustAcc, + Rand_ZeroFloat(80.0f) + 200.0f); + } + } + if (this->actor.world.pos.y < 50.0f) { + Actor_Kill(&this->actor); + } +} + +void EnVbBall_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnVbBall* this = (EnVbBall*)thisx; + BossFd* bossFd = (BossFd*)this->actor.parent; + f32 radius; + f32 pad2; + s16 spawnNum; + s16 i; + + this->unkTimer2++; + if (this->unkTimer1 != 0) { + this->unkTimer1--; + } + this->actor.shape.rot.x += (s16)this->xRotVel; + this->actor.shape.rot.y += (s16)this->yRotVel; + this->actor.velocity.y += -1.0f; + this->actor.gravity = -1.0f; + func_8002D7EC(&this->actor); + if (this->actor.params >= 200) { + EnVbBall_UpdateBones(this, globalCtx); + } else { + Math_ApproachF(&this->shadowOpacity, 175.0f, 1.0f, 40.0f); + radius = this->actor.scale.y * 1700.0f; + this->actor.world.pos.y -= radius; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 50.0f, 100.0f, 4); + this->actor.world.pos.y += radius; + if ((this->actor.bgCheckFlags & 1) && (this->actor.velocity.y <= 0.0f)) { + if ((this->actor.params == 100) || (this->actor.params == 101)) { + Actor_Kill(&this->actor); + if (this->actor.params == 100) { + func_80033E88(&this->actor, globalCtx, 5, 0xA); + } + if (this->actor.params == 100) { + spawnNum = 2; + } else { + spawnNum = 2; + } + for (i = 0; i < spawnNum; i++) { + Vec3f spawnOffset; + EnVbBall* newActor; + f32 xRotVel; + + if (this->actor.params == 100) { + spawnOffset.x = Rand_CenteredFloat(13.0f); + spawnOffset.y = Rand_ZeroFloat(5.0f) + 6.0f; + spawnOffset.z = Rand_CenteredFloat(13); + } else { + spawnOffset.x = Rand_CenteredFloat(10.0f); + spawnOffset.y = Rand_ZeroFloat(3.0f) + 4.0f; + spawnOffset.z = Rand_CenteredFloat(10.0f); + } + newActor = (EnVbBall*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, + ACTOR_EN_VB_BALL, this->actor.world.pos.x + spawnOffset.x, + this->actor.world.pos.y + spawnOffset.y, + this->actor.world.pos.z + spawnOffset.z, 0, 0, + this->actor.world.rot.z * 0.5f, this->actor.params + 1); + if (newActor != NULL) { + if ((i == 0) && (this->actor.params == 100)) { + Audio_PlaySoundGeneral(NA_SE_EN_VALVAISA_ROCK, &newActor->actor.projectedPos, 4, + &D_801333E0, &D_801333E0, &D_801333E8); + } + newActor->actor.parent = this->actor.parent; + newActor->actor.velocity = spawnOffset; + newActor->yRotVel = 0.0f; + xRotVel = sqrtf(SQ(spawnOffset.x) + SQ(spawnOffset.z)); + newActor->xRotVel = 0x1000 / 10.0f * xRotVel; + newActor->actor.shape.rot.y = Math_FAtan2F(spawnOffset.x, spawnOffset.z) * ((f32)0x8000 / M_PI); + newActor->shadowOpacity = 200.0f; + } + } + for (i = 0; i < 15; i++) { + Vec3f debrisVel1 = { 0.0f, 0.0f, 0.0f }; + Vec3f debrisAcc1 = { 0.0f, -1.0f, 0.0f }; + Vec3f debrisPos1; + + debrisVel1.x = Rand_CenteredFloat(25.0f); + debrisVel1.y = Rand_ZeroFloat(5.0f) + 8; + debrisVel1.z = Rand_CenteredFloat(25.0f); + + debrisPos1.x = Rand_CenteredFloat(10.0f) + this->actor.world.pos.x; + debrisPos1.y = Rand_CenteredFloat(10.0f) + this->actor.world.pos.y; + debrisPos1.z = Rand_CenteredFloat(10.0f) + this->actor.world.pos.z; + + EnVbBall_SpawnDebris(globalCtx, bossFd->effects, &debrisPos1, &debrisVel1, &debrisAcc1, + (s16)Rand_ZeroFloat(12.0f) + 15); + } + for (i = 0; i < 10; i++) { + Vec3f dustVel = { 0.0f, 0.0f, 0.0f }; + Vec3f dustAcc = { 0.0f, 0.0f, 0.0f }; + Vec3f dustPos; + + dustVel.x = Rand_CenteredFloat(8.0f); + dustVel.y = Rand_ZeroFloat(1.0f); + dustVel.z = Rand_CenteredFloat(8.0f); + + dustAcc.y = 1.0f / 2; + + dustPos.x = Rand_CenteredFloat(30.0f) + this->actor.world.pos.x; + dustPos.y = Rand_CenteredFloat(30.0f) + this->actor.world.pos.y; + dustPos.z = Rand_CenteredFloat(30.0f) + this->actor.world.pos.z; + + EnVbBall_SpawnDust(globalCtx, bossFd->effects, &dustPos, &dustVel, &dustAcc, + Rand_ZeroFloat(100.0f) + 350.0f); + } + } else { + for (i = 0; i < 5; i++) { + Vec3f debrisVel2 = { 0.0f, 0.0f, 0.0f }; + Vec3f debrisAcc2 = { 0.0f, -1.0f, 0.0f }; + Vec3f debrisPos2; + + debrisVel2.x = Rand_CenteredFloat(10.0f); + debrisVel2.y = Rand_ZeroFloat(3.0f) + 3.0f; + debrisVel2.z = Rand_CenteredFloat(10.0f); + + debrisPos2.x = Rand_CenteredFloat(5.0f) + this->actor.world.pos.x; + debrisPos2.y = Rand_CenteredFloat(5.0f) + this->actor.world.pos.y; + debrisPos2.z = Rand_CenteredFloat(5.0f) + this->actor.world.pos.z; + + EnVbBall_SpawnDebris(globalCtx, bossFd->effects, &debrisPos2, &debrisVel2, &debrisAcc2, + (s16)Rand_ZeroFloat(12.0f) + 15); + } + Actor_Kill(&this->actor); + } + } + if (this->collider.base.atFlags & AT_HIT) { + Player* player = GET_PLAYER(globalCtx); + + this->collider.base.atFlags &= ~AT_HIT; + Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); + } + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void EnVbBall_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnVbBall* this = (EnVbBall*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_vb_ball.c", 604); + if (1) {} // needed for match + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vb_ball.c", 607), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->actor.params >= 200) { + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gVolvagiaRibsDL)); + } else { + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gVolvagiaRockDL)); + func_80094044(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, (s8)this->shadowOpacity); + Matrix_Translate(this->actor.world.pos.x, 100.0f, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(this->shadowSize, 1.0f, this->shadowSize, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vb_ball.c", 626), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gCircleShadowDL)); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_vb_ball.c", 632); +} diff --git a/soh/src/overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.h b/soh/src/overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.h new file mode 100644 index 000000000..8675b440c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vb_Ball/z_en_vb_ball.h @@ -0,0 +1,22 @@ +#ifndef Z_EN_VB_BALL_H +#define Z_EN_VB_BALL_H + +#include "ultra64.h" +#include "global.h" + +struct EnVbBall; + +typedef struct EnVbBall { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[4]; + /* 0x0150 */ s16 unkTimer1; // These count up and down, but nothing uses them. + /* 0x0152 */ s16 unkTimer2; + /* 0x0154 */ char unk_154[4]; + /* 0x0158 */ f32 xRotVel; + /* 0x015C */ f32 yRotVel; + /* 0x0160 */ f32 shadowSize; + /* 0x0164 */ f32 shadowOpacity; + /* 0x0168 */ ColliderCylinder collider; +} EnVbBall; // size = 0x01B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.c b/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.c new file mode 100644 index 000000000..789d15f72 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.c @@ -0,0 +1,892 @@ +/* + * File: z_en_viewer.c + * Overlay: ovl_En_Viewer + * Description: Cutscene Actors + */ + +#include "z_en_viewer.h" +#include "overlays/actors/ovl_En_Ganon_Mant/z_en_ganon_mant.h" +#include "objects/object_zl4/object_zl4.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_horse_zelda/object_horse_zelda.h" +#include "objects/object_horse_ganon/object_horse_ganon.h" +#include "objects/object_im/object_im.h" +#include "objects/object_gndd/object_gndd.h" +#include "objects/object_ganon/object_ganon.h" +#include "objects/object_opening_demo1/object_opening_demo1.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnViewer_Init(Actor* thisx, GlobalContext* globalCtx); +void EnViewer_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnViewer_Update(Actor* thisx, GlobalContext* globalCtx); +void EnViewer_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnViewer_UpdatePosition(EnViewer* this, GlobalContext* globalCtx); +void EnViewer_DrawFireEffects(EnViewer* this2, GlobalContext* globalCtx); +void EnViewer_UpdateGanondorfCape(GlobalContext* globalCtx, EnViewer* this); +void EnViewer_InitImpl(EnViewer* this, GlobalContext* globalCtx); +void EnViewer_UpdateImpl(EnViewer* this, GlobalContext* globalCtx); + +static u8 sHorseSfxPlayed = false; + +const ActorInit En_Viewer_InitVars = { + ACTOR_EN_VIEWER, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnViewer), + (ActorFunc)EnViewer_Init, + (ActorFunc)EnViewer_Destroy, + (ActorFunc)EnViewer_Update, + (ActorFunc)EnViewer_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneScale, 300, ICHAIN_STOP), +}; + +static EnViewerInitData sInitData[] = { + /* ENVIEWER_TYPE_0_HORSE_ZELDA */ + { OBJECT_HORSE_ZELDA, OBJECT_HORSE_ZELDA, 1, 0, ENVIEWER_SHADOW_HORSE, 20, ENVIEWER_DRAW_HORSE, &gHorseZeldaSkel, + &gHorseZeldaGallopingAnim }, + /* ENVIEWER_TYPE_1_IMPA */ + { OBJECT_IM, OBJECT_OPENING_DEMO1, 1, 0, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_IMPA, &gImpaSkel, + &object_opening_demo1_Anim_0029CC }, + /* ENVIEWER_TYPE_2_ZELDA */ + { OBJECT_ZL4, OBJECT_OPENING_DEMO1, 1, 0, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_ZELDA, &gChildZeldaSkel, + &object_opening_demo1_Anim_000450 }, + /* ENVIEWER_TYPE_3_GANONDORF */ + { OBJECT_GNDD, OBJECT_GNDD, 1, -6, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_GANONDORF, &object_gndd_Skel_0119E8, + &object_gndd_Anim_002928 }, + /* ENVIEWER_TYPE_4_HORSE_GANONDORF */ + { OBJECT_HORSE_GANON, OBJECT_HORSE_GANON, 1, 0, ENVIEWER_SHADOW_HORSE, 20, ENVIEWER_DRAW_HORSE, &gHorseGanonSkel, + &gHorseGanonRearingAnim }, + /* ENVIEWER_TYPE_5_GANONDORF */ + { OBJECT_GNDD, OBJECT_GNDD, 1, -6, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_GANONDORF, &object_gndd_Skel_0119E8, + &object_gndd_Anim_0005B4 }, + /* ENVIEWER_TYPE_6_HORSE_GANONDORF */ + { OBJECT_HORSE_GANON, OBJECT_HORSE_GANON, 1, 0, ENVIEWER_SHADOW_HORSE, 20, ENVIEWER_DRAW_HORSE, &gHorseGanonSkel, + &gHorseGanonGallopingAnim }, + /* ENVIEWER_TYPE_7_GANONDORF */ + { OBJECT_GNDD, OBJECT_GNDD, 1, -6, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_GANONDORF, &object_gndd_Skel_0119E8, + &object_gndd_Anim_004260 }, + /* ENVIEWER_TYPE_8_GANONDORF */ + { OBJECT_GNDD, OBJECT_GNDD, 1, -6, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_GANONDORF, &object_gndd_Skel_0119E8, + &object_gndd_Anim_0050A8 }, + /* ENVIEWER_TYPE_9_GANONDORF */ + { OBJECT_GANON, OBJECT_GANON, 1, -6, ENVIEWER_SHADOW_NONE, 10, ENVIEWER_DRAW_GANONDORF, &gDorfSkel, + &object_ganon_Anim_011348 }, +}; + +static EnGanonMant* sGanondorfCape; + +static Vec3f sGanondorfNeckWorldPos; + +void EnViewer_SetupAction(EnViewer* this, EnViewerActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnViewer_Init(Actor* thisx, GlobalContext* globalCtx) { + EnViewer* this = (EnViewer*)thisx; + u8 type; + + Actor_ProcessInitChain(&this->actor, sInitChain); + EnViewer_SetupAction(this, EnViewer_InitImpl); + sHorseSfxPlayed = false; + type = this->actor.params >> 8; + this->unused = 0; + this->state = 0; + this->isVisible = false; + if (type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_5_GANONDORF || type == ENVIEWER_TYPE_7_GANONDORF || + type == ENVIEWER_TYPE_8_GANONDORF || type == ENVIEWER_TYPE_9_GANONDORF) { + sGanondorfCape = (EnGanonMant*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, + ACTOR_EN_GANON_MANT, 0.0f, 0.0f, 0.0f, 0, 0, 0, 35); + } +} + +void EnViewer_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnViewer* this = (EnViewer*)thisx; + + Skin_Free(globalCtx, &this->skin); +} + +void EnViewer_InitAnimGanondorfOrZelda(EnViewer* this, GlobalContext* globalCtx, void* skeletonHeaderSeg, + AnimationHeader* anim) { + s16 type = this->actor.params >> 8; + + if (type == ENVIEWER_TYPE_2_ZELDA || type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_5_GANONDORF || + type == ENVIEWER_TYPE_7_GANONDORF || type == ENVIEWER_TYPE_8_GANONDORF || type == ENVIEWER_TYPE_9_GANONDORF) { + SkelAnime_InitFlex(globalCtx, &this->skin.skelAnime, skeletonHeaderSeg, NULL, NULL, NULL, 0); + } else { + SkelAnime_Init(globalCtx, &this->skin.skelAnime, skeletonHeaderSeg, NULL, NULL, NULL, 0); + } + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animObjBankIndex].segment); + if (type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_7_GANONDORF || type == ENVIEWER_TYPE_8_GANONDORF || + type == ENVIEWER_TYPE_9_GANONDORF) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, anim, 1.0f); + } else { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, anim, 3.0f); + } +} + +void EnViewer_InitAnimImpa(EnViewer* this, GlobalContext* globalCtx, void* skeletonHeaderSeg, AnimationHeader* anim) { + SkelAnime_InitFlex(globalCtx, &this->skin.skelAnime, skeletonHeaderSeg, NULL, NULL, NULL, 0); + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animObjBankIndex].segment); + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, anim, 3.0f); +} + +void EnViewer_InitAnimHorse(EnViewer* this, GlobalContext* globalCtx, void* skeletonHeaderSeg, AnimationHeader* anim) { + u8 type; + + Skin_Init(globalCtx, &this->skin, skeletonHeaderSeg, anim); + type = this->actor.params >> 8; + if (!(type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_4_HORSE_GANONDORF || + type == ENVIEWER_TYPE_7_GANONDORF || type == ENVIEWER_TYPE_8_GANONDORF || + type == ENVIEWER_TYPE_9_GANONDORF)) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, anim, 3.0f); + } else { + Animation_PlayOnceSetSpeed(&this->skin.skelAnime, anim, 1.0f); + } +} + +static EnViewerInitAnimFunc sInitAnimFuncs[] = { + EnViewer_InitAnimGanondorfOrZelda, + EnViewer_InitAnimHorse, + EnViewer_InitAnimGanondorfOrZelda, + EnViewer_InitAnimImpa, +}; + +static ActorShadowFunc sShadowDrawFuncs[] = { + NULL, + ActorShadow_DrawCircle, + ActorShadow_DrawHorse, +}; + +void EnViewer_InitImpl(EnViewer* this, GlobalContext* globalCtx) { + EnViewerInitData* initData = &sInitData[this->actor.params >> 8]; + s32 skelObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, initData->skeletonObject); + + ASSERT(skelObjBankIndex >= 0, "bank_ID >= 0", "../z_en_viewer.c", 576); + + this->animObjBankIndex = Object_GetIndex(&globalCtx->objectCtx, initData->animObject); + ASSERT(this->animObjBankIndex >= 0, "this->anime_bank_ID >= 0", "../z_en_viewer.c", 579); + + if (!Object_IsLoaded(&globalCtx->objectCtx, skelObjBankIndex) || + !Object_IsLoaded(&globalCtx->objectCtx, this->animObjBankIndex)) { + this->actor.flags &= ~ACTOR_FLAG_6; + return; + } + + this->isVisible = true; + this->actor.objBankIndex = skelObjBankIndex; + Actor_SetObjectDependency(globalCtx, &this->actor); + Actor_SetScale(&this->actor, initData->scale / 100.0f); + ActorShape_Init(&this->actor.shape, initData->yOffset * 100, sShadowDrawFuncs[initData->shadowType], + initData->shadowScale); + this->drawFuncIndex = initData->drawType; + sInitAnimFuncs[this->drawFuncIndex](this, globalCtx, initData->skeletonHeaderSeg, initData->anim); + EnViewer_SetupAction(this, EnViewer_UpdateImpl); +} + +static s16 sTimer = 0; + +void EnViewer_UpdateImpl(EnViewer* this, GlobalContext* globalCtx) { + u8 type = this->actor.params >> 8; + u16 csFrames; + s32 animationEnded; + + if (type == ENVIEWER_TYPE_2_ZELDA) { + if (gSaveContext.sceneSetupIndex == 5) { + csFrames = globalCtx->csCtx.frames; + if (csFrames == 792) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_SURPRISE); + } else if (csFrames == 845) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_THROW); + } + } + } else if (type == ENVIEWER_TYPE_7_GANONDORF) { + Actor_SetScale(&this->actor, 0.3f); + this->actor.uncullZoneForward = 10000.0f; + this->actor.uncullZoneScale = 10000.0f; + this->actor.uncullZoneDownward = 10000.0f; + } else if (type == ENVIEWER_TYPE_3_GANONDORF) { + if (gSaveContext.sceneSetupIndex == 4) { + switch (globalCtx->csCtx.frames) { + case 20: + case 59: + case 71: + case 129: + case 140: + case 219: + case 280: + case 320: + case 380: + case 409: + case 438: + Audio_PlaySoundGeneral(NA_SE_SY_DEMO_CUT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + break; + } + } + if (gSaveContext.sceneSetupIndex == 5) { + if (globalCtx->csCtx.frames == 1508) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_ST_LAUGH); + } + if (globalCtx->csCtx.frames == 1545) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DEMO_6K, 32.0f, 101.0f, 1226.0f, + 0, 0, 0, 0xC); + } + } + if (globalCtx->csCtx.frames == 1020) { + Audio_QueueSeqCmd(SEQ_PLAYER_FANFARE << 24 | NA_BGM_OPENING_GANON); + } + if (globalCtx->csCtx.frames == 960) { + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_GROAN, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } else if (type == ENVIEWER_TYPE_6_HORSE_GANONDORF) { + if (gSaveContext.sceneSetupIndex == 5 || gSaveContext.sceneSetupIndex == 10) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_RUN_LEVEL - SFX_FLAG); + } + } else if (type == ENVIEWER_TYPE_4_HORSE_GANONDORF) { + s16 curFrame = this->skin.skelAnime.curFrame; + + if (this->skin.skelAnime.animation == &gHorseGanonRearingAnim) { + if (curFrame == 8) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_HORSE_NEIGH); + } + if (curFrame == 30) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_LAND2); + } + } else if (this->skin.skelAnime.animation == &gHorseGanonIdleAnim) { + if (curFrame == 25) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_SANDDUST); + } + } else if (this->skin.skelAnime.animation == &gHorseGanonGallopingAnim) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_RUN_LEVEL - SFX_FLAG); + } + } + + if (sTimer != 0) { + sTimer--; + } + + EnViewer_UpdatePosition(this, globalCtx); + Actor_MoveForward(&this->actor); // has no effect, speed/velocity and gravity are 0 + + animationEnded = SkelAnime_Update(&this->skin.skelAnime); + if (type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_4_HORSE_GANONDORF) { + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[1] != NULL) { + if (globalCtx->csCtx.npcActions[1]->action == 2 && sTimer == 0) { + if (type == ENVIEWER_TYPE_3_GANONDORF) { + if (this->skin.skelAnime.animation != &object_gndd_Anim_002928) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_gndd_Anim_002928, 1.0f); + } + } else if (this->skin.skelAnime.animation != &gHorseGanonIdleAnim) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &gHorseGanonIdleAnim, 1.0f); + } + } else if (globalCtx->csCtx.npcActions[1]->action == 1) { + sTimer = 100; + if (type == ENVIEWER_TYPE_3_GANONDORF) { + if (this->skin.skelAnime.animation != &object_gndd_Anim_001D28) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_gndd_Anim_001D28, 1.0f); + } + } else if (this->skin.skelAnime.animation != &gHorseGanonRearingAnim) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &gHorseGanonRearingAnim, 1.0f); + } + } else if (type == ENVIEWER_TYPE_3_GANONDORF) { + switch (this->state) { + case 0: + if (globalCtx->csCtx.npcActions[1]->action == 4) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &object_gndd_Anim_000F54, -5.0f); + this->state++; + } + break; + case 1: + if (animationEnded) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_gndd_Anim_0014F4, -5.0f); + this->state++; + } + break; + case 2: + if (globalCtx->csCtx.npcActions[1]->action == 5) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &object_gndd_Anim_0008A0, -5.0f); + this->state++; + } + break; + case 3: + if (animationEnded) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_gndd_Anim_000BC8, -5.0f); + this->state++; + } + break; + case 4: + if (globalCtx->csCtx.npcActions[1]->action == 11) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_gndd_Anim_0014F4, -20.0f); + this->state++; + } + break; + case 5: + if (globalCtx->csCtx.npcActions[1]->action == 8) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_gndd_Anim_002928, -15.0f); + this->state++; + } + break; + case 6: + if (globalCtx->csCtx.npcActions[1]->action == 12) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_VOICE_DEMO); + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_gndd_Anim_0005B4, 3.0f); + this->state++; + } + break; + case 7: + this->state = 0; + break; + } + } else if (this->skin.skelAnime.animation != &gHorseGanonGallopingAnim && + globalCtx->csCtx.npcActions[1]->action == 12) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &gHorseGanonGallopingAnim, 3.0f); + } + } + } else if (type == ENVIEWER_TYPE_1_IMPA) { + if (gSaveContext.sceneSetupIndex == 5) { + if (globalCtx->csCtx.frames == 845) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_ITEM_OCARINA, 4.0f, 81.0f, + 2600.0f, 0, 0, 0, 0); + } + } else { + if (globalCtx->csCtx.frames == 195) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_ITEM_OCARINA, 4.0f, 81.0f, + 2035.0f, 0, 0, 0, 1); + } + } + switch (this->state) { + case 0: + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[0] != NULL && + globalCtx->csCtx.npcActions[0]->action == 6 && + this->skin.skelAnime.animation != &object_opening_demo1_Anim_002574) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_opening_demo1_Anim_002574, 1.5f); + this->state++; + } + break; + case 1: + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[0] != NULL && + globalCtx->csCtx.npcActions[0]->action == 2 && + this->skin.skelAnime.animation != &object_opening_demo1_Anim_0029CC) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_opening_demo1_Anim_0029CC, 3.0f); + this->state++; + } + break; + } + } else if (type == ENVIEWER_TYPE_2_ZELDA) { + if (globalCtx->sceneNum == SCENE_SPOT00) { // Hyrule Field + switch (this->state) { + case 0: + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[0] != NULL && globalCtx->csCtx.npcActions[0]->action == 6 && + this->skin.skelAnime.animation != &object_opening_demo1_Anim_001410) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_opening_demo1_Anim_001410, 1.5f); + this->state++; + } + } + break; + case 1: + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[0] != NULL && globalCtx->csCtx.npcActions[0]->action == 2 && + this->skin.skelAnime.animation != &object_opening_demo1_Anim_000450) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_opening_demo1_Anim_000450, 3.0f); + this->state++; + } + } + break; + } + } else { + Audio_SetBaseFilter(0); + switch (this->state) { + case 0: + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_opening_demo1_Anim_00504C, 1.0f); + this->state++; + break; + case 1: + if (globalCtx->csCtx.npcActions[0]->action == 11) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &object_opening_demo1_Anim_00420C, -5.0f); + this->state++; + } + break; + case 2: + if (animationEnded) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_opening_demo1_Anim_0048FC, -5.0f); + this->state++; + } + break; + case 3: + break; + } + } + } else if (type == ENVIEWER_TYPE_7_GANONDORF) { + switch (this->state) { + case 0: + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[1] != NULL && + globalCtx->csCtx.npcActions[1]->action == 7) { + Audio_PlaySoundGeneral(NA_SE_EN_GANON_LAUGH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Animation_MorphToPlayOnce(&this->skin.skelAnime, &object_gndd_Anim_004534, -5.0f); + this->state++; + } + break; + case 1: + if (animationEnded) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_gndd_Anim_0048B0, -5.0f); + this->state++; + } + break; + } + } else if (type == ENVIEWER_TYPE_8_GANONDORF) { + switch (this->state) { + case 0: + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.npcActions[1] != NULL && globalCtx->csCtx.npcActions[1]->action == 9) { + Animation_PlayLoopSetSpeed(&this->skin.skelAnime, &object_gndd_Anim_0050A8, 1.0f); + this->state++; + } + } + break; + case 1: + if (globalCtx->csCtx.npcActions[1]->action == 10) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &object_gndd_Anim_003284, -10.0f); + this->state++; + } + break; + case 2: + if (animationEnded) { + Animation_MorphToLoop(&this->skin.skelAnime, &object_gndd_Anim_003D84, -5.0f); + this->state++; + } + break; + case 3: + if (globalCtx->csCtx.npcActions[1]->action == 4) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &object_gndd_Anim_003428, -5.0f); + this->state++; + } + break; + default: + this->state = 0; + break; + } + } +} + +void EnViewer_Update(Actor* thisx, GlobalContext* globalCtx) { + EnViewer* this = (EnViewer*)thisx; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->animObjBankIndex].segment); + this->actionFunc(this, globalCtx); +} + +s32 EnViewer_Ganondorf3OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + if (gSaveContext.sceneSetupIndex == 4) { + if (globalCtx->csCtx.frames >= 400) { + if (limbIndex == 5) { + *dList = object_gndd_DL_00E1A8; + } + } + } else { + if (globalCtx->csCtx.frames >= 1510 && globalCtx->csCtx.frames <= 1650) { + if (limbIndex == 5) { + *dList = object_gndd_DL_00E1A8; + } + } + } + return false; +} + +void EnViewer_Ganondorf9PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + if (limbIndex == 11) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1365); + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1370), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_ganon_DL_00BE90)); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1372); + } +} + +void EnViewer_GanondorfPostLimbDrawUpdateCapeVec(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, + void* thisx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + if (limbIndex == 15) { + Matrix_MultVec3f(&zeroVec, &sGanondorfNeckWorldPos); + } +} + +void EnViewer_DrawGanondorf(EnViewer* this, GlobalContext* globalCtx) { + s16 frames = 0; + s16 type; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1405); + type = this->actor.params >> 8; + if (type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_5_GANONDORF || type == ENVIEWER_TYPE_7_GANONDORF || + type == ENVIEWER_TYPE_8_GANONDORF) { + if (gSaveContext.sceneSetupIndex != 4) { + frames = 149; + } + + if (frames + 1127 >= globalCtx->csCtx.frames) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F178)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F178)); + + } else if (frames + 1128 >= globalCtx->csCtx.frames) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F378)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F378)); + + } else if (frames + 1129 >= globalCtx->csCtx.frames) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F578)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F578)); + + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F778)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(&object_gndd_Tex_00F778)); + } + } else if (type == ENVIEWER_TYPE_9_GANONDORF) { + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(&object_ganon_Tex_00A4E0)); + } + + if (type == ENVIEWER_TYPE_9_GANONDORF) { + SkelAnime_DrawFlexOpa(globalCtx, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable, + this->skin.skelAnime.dListCount, NULL, EnViewer_Ganondorf9PostLimbDraw, this); + } else if (type == ENVIEWER_TYPE_3_GANONDORF) { + SkelAnime_DrawFlexOpa(globalCtx, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable, + this->skin.skelAnime.dListCount, EnViewer_Ganondorf3OverrideLimbDraw, + EnViewer_GanondorfPostLimbDrawUpdateCapeVec, this); + EnViewer_UpdateGanondorfCape(globalCtx, this); + } else if (type == ENVIEWER_TYPE_3_GANONDORF || type == ENVIEWER_TYPE_5_GANONDORF || + type == ENVIEWER_TYPE_7_GANONDORF || type == ENVIEWER_TYPE_8_GANONDORF) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[1] != NULL)) { + SkelAnime_DrawFlexOpa(globalCtx, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable, + this->skin.skelAnime.dListCount, NULL, EnViewer_GanondorfPostLimbDrawUpdateCapeVec, + this); + EnViewer_UpdateGanondorfCape(globalCtx, this); + } + } else { + SkelAnime_DrawOpa(globalCtx, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable, NULL, NULL, this); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1511); +} + +void EnViewer_DrawHorse(EnViewer* this, GlobalContext* globalCtx) { + func_800A6330(&this->actor, globalCtx, &this->skin, NULL, true); +} + +s32 EnViewer_ZeldaOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + if (globalCtx->sceneNum == SCENE_SPOT00) { // Hyrule Field + if (limbIndex == 2) { + *dList = gChildZeldaCutsceneDressDL; + } + if (limbIndex == 7) { + *dList = NULL; + } + if (limbIndex == 8) { + *dList = NULL; + } + if (limbIndex == 9) { + *dList = NULL; + } + if (limbIndex == 3) { + *dList = NULL; + } + if (limbIndex == 5) { + *dList = NULL; + } + } + return false; +} + +void EnViewer_ZeldaPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + s32 pad; + + if (globalCtx->sceneNum == SCENE_TOKINOMA) { + if (limbIndex == 16) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1568); + gSPDisplayList(POLY_OPA_DISP++, gChildZeldaOcarinaOfTimeDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1570); + } + } +} + +void EnViewer_DrawZelda(EnViewer* this, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1583); + if (globalCtx->sceneNum == SCENE_SPOT00) { // Hyrule Field + if (globalCtx->csCtx.frames < 771) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeInTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeOutTex)); + } else if (globalCtx->csCtx.frames < 772) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeBlinkTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeBlinkTex)); + } else if (globalCtx->csCtx.frames < 773) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeShutTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeShutTex)); + } else if (globalCtx->csCtx.frames < 791) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeWideTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeWideTex)); + } else if (globalCtx->csCtx.frames < 792) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeBlinkTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeBlinkTex)); + } else if (globalCtx->csCtx.frames < 793) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeShutTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeShutTex)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeInTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeOutTex)); + } + + if (gSaveContext.sceneSetupIndex == 6) { + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gChildZeldaMouthSurprisedTex)); + } else { + if (globalCtx->csCtx.frames < 758) { + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gChildZeldaMouthWorriedTex)); + } else if (globalCtx->csCtx.frames < 848) { + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gChildZeldaMouthSurprisedTex)); + } else { + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gChildZeldaMouthWorriedTex)); + } + } + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeShutTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gChildZeldaEyeShutTex)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(gChildZeldaMouthWorriedTex)); + } + SkelAnime_DrawFlexOpa(globalCtx, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable, + this->skin.skelAnime.dListCount, EnViewer_ZeldaOverrideLimbDraw, EnViewer_ZeldaPostLimbDraw, + this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1690); +} + +s32 EnViewer_ImpaOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + if (limbIndex == 16) { + *dList = gImpaHeadMaskedDL; + } + return false; +} + +void EnViewer_DrawImpa(EnViewer* this, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1717); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gImpaEyeOpenTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gImpaEyeOpenTex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0C, &D_80116280[2]); + SkelAnime_DrawFlexOpa(globalCtx, this->skin.skelAnime.skeleton, this->skin.skelAnime.jointTable, + this->skin.skelAnime.dListCount, EnViewer_ImpaOverrideLimbDraw, NULL, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1740); +} + +static EnViewerDrawFunc sDrawFuncs[] = { + EnViewer_DrawGanondorf, + EnViewer_DrawHorse, + EnViewer_DrawZelda, + EnViewer_DrawImpa, +}; + +void EnViewer_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnViewer* this = (EnViewer*)thisx; + s32 pad; + s16 type; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1760); + if (this->isVisible) { + type = this->actor.params >> 8; + if (type <= ENVIEWER_TYPE_2_ZELDA) { // zelda's horse, impa and zelda + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[0] != NULL) { + func_80093D18(globalCtx->state.gfxCtx); + sDrawFuncs[this->drawFuncIndex](this, globalCtx); + } + } else if ((globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[1] != NULL) || + type == ENVIEWER_TYPE_9_GANONDORF) { + func_80093D18(globalCtx->state.gfxCtx); + sDrawFuncs[this->drawFuncIndex](this, globalCtx); + } + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1784); +} + +void EnViewer_UpdatePosition(EnViewer* this, GlobalContext* globalCtx) { + Vec3f startPos; + Vec3f endPos; + f32 lerpFactor; + s16 type = this->actor.params >> 8; + + if (type <= ENVIEWER_TYPE_2_ZELDA) { // zelda's horse, impa and zelda + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[0] != NULL && + globalCtx->csCtx.frames < globalCtx->csCtx.npcActions[0]->endFrame) { + if (type == ENVIEWER_TYPE_0_HORSE_ZELDA) { + if (!sHorseSfxPlayed) { + sHorseSfxPlayed = true; + Audio_PlaySoundGeneral(NA_SE_EV_HORSE_NEIGH, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_RUN_LEVEL - SFX_FLAG); + } + + startPos.x = globalCtx->csCtx.npcActions[0]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[0]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[0]->startPos.z; + endPos.x = globalCtx->csCtx.npcActions[0]->endPos.x; + endPos.y = globalCtx->csCtx.npcActions[0]->endPos.y; + endPos.z = globalCtx->csCtx.npcActions[0]->endPos.z; + lerpFactor = Environment_LerpWeight(globalCtx->csCtx.npcActions[0]->endFrame, + globalCtx->csCtx.npcActions[0]->startFrame, globalCtx->csCtx.frames); + this->actor.world.pos.x = (endPos.x - startPos.x) * lerpFactor + startPos.x; + this->actor.world.pos.y = (endPos.y - startPos.y) * lerpFactor + startPos.y; + this->actor.world.pos.z = (endPos.z - startPos.z) * lerpFactor + startPos.z; + } + } else { // ganondorf and ganondorf's horse + if (globalCtx->csCtx.state != CS_STATE_IDLE && globalCtx->csCtx.npcActions[1] != NULL && + globalCtx->csCtx.frames < globalCtx->csCtx.npcActions[1]->endFrame) { + startPos.x = globalCtx->csCtx.npcActions[1]->startPos.x; + startPos.y = globalCtx->csCtx.npcActions[1]->startPos.y; + startPos.z = globalCtx->csCtx.npcActions[1]->startPos.z; + endPos.x = globalCtx->csCtx.npcActions[1]->endPos.x; + endPos.y = globalCtx->csCtx.npcActions[1]->endPos.y; + endPos.z = globalCtx->csCtx.npcActions[1]->endPos.z; + lerpFactor = Environment_LerpWeight(globalCtx->csCtx.npcActions[1]->endFrame, + globalCtx->csCtx.npcActions[1]->startFrame, globalCtx->csCtx.frames); + this->actor.world.pos.x = (endPos.x - startPos.x) * lerpFactor + startPos.x; + this->actor.world.pos.y = (endPos.y - startPos.y) * lerpFactor + startPos.y; + this->actor.world.pos.z = (endPos.z - startPos.z) * lerpFactor + startPos.z; + + if (globalCtx->csCtx.npcActions[1]->action == 12) { + s16 yaw = Math_Vec3f_Yaw(&startPos, &endPos); + + Math_SmoothStepToS(&this->actor.world.rot.y, yaw, 0xA, 0x3E8, 1); + Math_SmoothStepToS(&this->actor.shape.rot.y, yaw, 0xA, 0x3E8, 1); + } + + if (type == ENVIEWER_TYPE_9_GANONDORF) { + this->actor.world.rot.x = globalCtx->csCtx.npcActions[1]->urot.x; + this->actor.world.rot.y = globalCtx->csCtx.npcActions[1]->urot.y; + this->actor.world.rot.z = globalCtx->csCtx.npcActions[1]->urot.z; + this->actor.shape.rot.x = globalCtx->csCtx.npcActions[1]->urot.x; + this->actor.shape.rot.y = globalCtx->csCtx.npcActions[1]->urot.y; + this->actor.shape.rot.z = globalCtx->csCtx.npcActions[1]->urot.z; + } + } + if (type == ENVIEWER_TYPE_5_GANONDORF) { + Audio_PlaySoundGeneral(NA_SE_EV_BURNING - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + EnViewer_DrawFireEffects(this, globalCtx); + } + } +} + +void EnViewer_InitFireEffect(EnViewer* this, GlobalContext* globalCtx, s16 i) { + EnViewerFireEffect* eff; + + if ((i % 2) == 0) { + eff = &this->fireEffects[i]; + eff->startPos.x = 100.0f; + eff->startPos.y = -420.0f; + eff->startPos.z = 400.0f; + eff->endPos.x = 100.0f; + eff->endPos.y = -420.0f; + eff->endPos.z = -400.0f; + eff->scale = (Rand_ZeroOne() * 5.0f + 12.0f) * 0.001f; + } else { + eff = &this->fireEffects[i]; + eff->startPos.x = -100.0f; + eff->startPos.y = -420.0f; + eff->startPos.z = 400.0f; + eff->endPos.x = -100.0f; + eff->endPos.y = -420.0f; + eff->endPos.z = -400.0f; + eff->scale = (Rand_ZeroOne() * 5.0f + 12.0f) * 0.001f; + } + if (this) {} +} + +void EnViewer_DrawFireEffects(EnViewer* this2, GlobalContext* globalCtx) { + EnViewer* this = this2; + s16 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 1941); + for (i = 0; i < ARRAY_COUNT(this->fireEffects); i++) { + switch (this->fireEffects[i].state) { + case 0: + EnViewer_InitFireEffect(this, globalCtx, i); + this->fireEffects[i].lerpFactor = (i >> 1) * 0.1f; + this->fireEffects[i].lerpFactorSpeed = 0.01f; + this->fireEffects[i].state++; + break; + case 1: + Math_SmoothStepToF(&this->fireEffects[i].lerpFactor, 1.0f, 1.0f, this->fireEffects[i].lerpFactorSpeed, + this->fireEffects[i].lerpFactorSpeed); + this->fireEffects[i].pos.x = + this->fireEffects[i].startPos.x + + (this->fireEffects[i].endPos.x - this->fireEffects[i].startPos.x) * this->fireEffects[i].lerpFactor; + this->fireEffects[i].pos.y = + this->fireEffects[i].startPos.y + + (this->fireEffects[i].endPos.y - this->fireEffects[i].startPos.y) * this->fireEffects[i].lerpFactor; + this->fireEffects[i].pos.z = + this->fireEffects[i].startPos.z + + (this->fireEffects[i].endPos.z - this->fireEffects[i].startPos.z) * this->fireEffects[i].lerpFactor; + if (this->fireEffects[i].lerpFactor >= 1.0f) { + this->fireEffects[i].state++; + } + break; + case 2: + EnViewer_InitFireEffect(this, globalCtx, i); + this->fireEffects[i].lerpFactor = 0.0f; + this->fireEffects[i].lerpFactorSpeed = 0.01f; + this->fireEffects[i].state--; + break; + } + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Translate(this->fireEffects[i].pos.x, this->fireEffects[i].pos.y, this->fireEffects[i].pos.z, + MTXMODE_NEW); + Matrix_Scale(this->fireEffects[i].scale, this->fireEffects[i].scale, this->fireEffects[i].scale, MTXMODE_APPLY); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + (10 * i - 20 * globalCtx->state.frames) % 512, 32, 128)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 170, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 00, 255); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_viewer.c", 2027), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_viewer.c", 2034); +} + +void EnViewer_UpdateGanondorfCape(GlobalContext* globalCtx, EnViewer* this) { + static s16 yOscillationPhase = 0; + Vec3f forearmModelOffset; + Vec3f forearmWorldOffset; + + if ((this->actor.params >> 8) == ENVIEWER_TYPE_5_GANONDORF) { + if (1) {} + sGanondorfCape->backPush = BREG(54) / 10.0f; + sGanondorfCape->backSwayMagnitude = (BREG(60) + 25) / 100.0f; + sGanondorfCape->sideSwayMagnitude = (BREG(55) - 45) / 10.0f; + sGanondorfCape->minY = -10000.0f; + sGanondorfCape->minDist = 0.0f; + sGanondorfCape->gravity = (BREG(67) - 10) / 10.0f; + forearmModelOffset.x = KREG(16) - 13.0f; + forearmModelOffset.y = KREG(17) + 3.0f + Math_SinS(yOscillationPhase) * KREG(20); + forearmModelOffset.z = KREG(18) - 10.0f; + yOscillationPhase += KREG(19) * 0x1000 + 0x2000; + + Matrix_RotateY((this->actor.shape.rot.y / (f32)0x8000) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&forearmModelOffset, &forearmWorldOffset); + sGanondorfCape->rightForearmPos.x = sGanondorfNeckWorldPos.x + forearmWorldOffset.x; + sGanondorfCape->rightForearmPos.y = sGanondorfNeckWorldPos.y + forearmWorldOffset.y; + sGanondorfCape->rightForearmPos.z = sGanondorfNeckWorldPos.z + forearmWorldOffset.z; + forearmModelOffset.x = -(KREG(16) - 13.0f); + Matrix_MultVec3f(&forearmModelOffset, &forearmWorldOffset); + sGanondorfCape->leftForearmPos.x = sGanondorfNeckWorldPos.x + forearmWorldOffset.x; + sGanondorfCape->leftForearmPos.y = sGanondorfNeckWorldPos.y + forearmWorldOffset.y; + sGanondorfCape->leftForearmPos.z = sGanondorfNeckWorldPos.z + forearmWorldOffset.z; + } +} diff --git a/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h b/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h new file mode 100644 index 000000000..6d797cb34 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Viewer/z_en_viewer.h @@ -0,0 +1,73 @@ +#ifndef Z_EN_VIEWER_H +#define Z_EN_VIEWER_H + +#include "ultra64.h" +#include "global.h" + +struct EnViewer; + +typedef void (*EnViewerActionFunc)(struct EnViewer*, GlobalContext*); +typedef void (*EnViewerDrawFunc)(struct EnViewer*, GlobalContext*); +typedef void (*EnViewerInitAnimFunc)(struct EnViewer*, GlobalContext*, void*, AnimationHeader*); + +typedef enum { + /* 0 */ ENVIEWER_TYPE_0_HORSE_ZELDA, + /* 1 */ ENVIEWER_TYPE_1_IMPA, + /* 2 */ ENVIEWER_TYPE_2_ZELDA, + /* 3 */ ENVIEWER_TYPE_3_GANONDORF, + /* 4 */ ENVIEWER_TYPE_4_HORSE_GANONDORF, + /* 5 */ ENVIEWER_TYPE_5_GANONDORF, + /* 6 */ ENVIEWER_TYPE_6_HORSE_GANONDORF, + /* 7 */ ENVIEWER_TYPE_7_GANONDORF, + /* 8 */ ENVIEWER_TYPE_8_GANONDORF, + /* 9 */ ENVIEWER_TYPE_9_GANONDORF +} EnViewerType; + +typedef enum { + /* 0 */ ENVIEWER_DRAW_GANONDORF, + /* 1 */ ENVIEWER_DRAW_HORSE, + /* 2 */ ENVIEWER_DRAW_ZELDA, + /* 3 */ ENVIEWER_DRAW_IMPA +} EnViewerDrawType; + +typedef enum { + /* 0 */ ENVIEWER_SHADOW_NONE, + /* 1 */ ENVIEWER_SHADOW_CIRCLE, + /* 2 */ ENVIEWER_SHADOW_HORSE +} EnViewerShadowType; + +typedef struct { + /* 0x00 */ s16 skeletonObject; + /* 0x02 */ s16 animObject; + /* 0x04 */ u8 scale; // divided by 100.0f + /* 0x05 */ s8 yOffset; // multiplied by 100 + /* 0x06 */ u8 shadowType; + /* 0x07 */ u8 shadowScale; + /* 0x08 */ u8 drawType; + /* 0x0C */ void* skeletonHeaderSeg; + /* 0x10 */ AnimationHeader* anim; +} EnViewerInitData; // size = 0x14 + +typedef struct { + /* 0x00 */ Vec3f startPos; + /* 0x0C */ Vec3f endPos; + /* 0x18 */ Vec3f pos; + /* 0x24 */ f32 lerpFactorSpeed; + /* 0x28 */ f32 scale; + /* 0x2C */ f32 lerpFactor; + /* 0x30 */ u8 state; +} EnViewerFireEffect; // size = 0x34 + +typedef struct EnViewer { + /* 0x0000 */ Actor actor; + /* 0x014C */ Skin skin; + /* 0x01DC */ s8 animObjBankIndex; + /* 0x01DD */ u8 drawFuncIndex; + /* 0x01E0 */ EnViewerActionFunc actionFunc; + /* 0x01E4 */ u8 unused; + /* 0x01E5 */ u8 state; + /* 0x01E6 */ u8 isVisible; + /* 0x01E8 */ EnViewerFireEffect fireEffects[20]; +} EnViewer; // size = 0x05F8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c new file mode 100644 index 000000000..f222b4c1a --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.c @@ -0,0 +1,557 @@ +/* + * File: z_en_vm.c + * Overlay: ovl_En_Vm + * Description: Beamos + */ + +#include "z_en_vm.h" +#include "objects/object_vm/object_vm.h" +#include "overlays/actors/ovl_En_Bom/z_en_bom.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4) + +void EnVm_Init(Actor* thisx, GlobalContext* globalCtx); +void EnVm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnVm_Update(Actor* thisx, GlobalContext* globalCtx); +void EnVm_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnVm_SetupWait(EnVm* this); +void EnVm_Wait(EnVm* this, GlobalContext* globalCtx); +void EnVm_SetupAttack(EnVm* this); +void EnVm_Attack(EnVm* this, GlobalContext* globalCtx); +void EnVm_Stun(EnVm* this, GlobalContext* globalCtx); +void EnVm_Die(EnVm* this, GlobalContext* globalCtx); + +const ActorInit En_Vm_InitVars = { + ACTOR_EN_VM, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_VM, + sizeof(EnVm), + (ActorFunc)EnVm_Init, + (ActorFunc)EnVm_Destroy, + (ActorFunc)EnVm_Update, + (ActorFunc)EnVm_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 25, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderQuadInit sQuadInit1 = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static ColliderQuadInit sQuadInit2 = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static Vec3f D_80B2EAEC = { 0.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EAF8 = { 0.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EB04 = { 500.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EB10 = { -500.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EB1C = { 0.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EB28 = { 0.0f, 0.0f, 1600.0f }; + +static Vec3f D_80B2EB34 = { 1000.0f, 700.0f, 2000.0f }; + +static Vec3f D_80B2EB40 = { 1000.0f, -700.0f, 2000.0f }; + +static Vec3f D_80B2EB4C = { -1000.0f, 700.0f, 1500.0f }; + +static Vec3f D_80B2EB58 = { -1000.0f, -700.0f, 1500.0f }; + +static Vec3f D_80B2EB64 = { 500.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EB70 = { -500.0f, 0.0f, 0.0f }; + +static Vec3f D_80B2EB7C = { 0.4f, 0.4f, 0.4f }; + +static void* D_80B2EB88[] = { + gEffEnemyDeathFlame1Tex, gEffEnemyDeathFlame2Tex, gEffEnemyDeathFlame3Tex, gEffEnemyDeathFlame4Tex, + gEffEnemyDeathFlame5Tex, gEffEnemyDeathFlame6Tex, gEffEnemyDeathFlame7Tex, gEffEnemyDeathFlame8Tex, + gEffEnemyDeathFlame9Tex, gEffEnemyDeathFlame10Tex, +}; + +void EnVm_SetupAction(EnVm* this, EnVmActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnVm_Init(Actor* thisx, GlobalContext* globalCtx) { + EnVm* this = (EnVm*)thisx; + + SkelAnime_Init(globalCtx, &this->skelAnime, &gBeamosSkel, &gBeamosAnim, this->jointTable, this->morphTable, 11); + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + Collider_InitCylinder(globalCtx, &this->colliderCylinder); + Collider_SetCylinder(globalCtx, &this->colliderCylinder, thisx, &sCylinderInit); + Collider_InitQuad(globalCtx, &this->colliderQuad1); + Collider_SetQuad(globalCtx, &this->colliderQuad1, thisx, &sQuadInit1); + Collider_InitQuad(globalCtx, &this->colliderQuad2); + Collider_SetQuad(globalCtx, &this->colliderQuad2, thisx, &sQuadInit2); + this->beamSightRange = (thisx->params >> 8) * 40.0f; + thisx->params &= 0xFF; + thisx->naviEnemyId = 0x39; + + if (thisx->params == BEAMOS_LARGE) { + thisx->colChkInfo.health = 2; + Actor_SetScale(thisx, 0.014f); + } else { + thisx->colChkInfo.health = 1; + Actor_SetScale(thisx, 0.01f); + } + + EnVm_SetupWait(this); +} + +void EnVm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnVm* this = (EnVm*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->colliderCylinder); +} + +void EnVm_SetupWait(EnVm* this) { + f32 frameCount = Animation_GetLastFrame(&gBeamosAnim); + + Animation_Change(&this->skelAnime, &gBeamosAnim, 1.0f, frameCount, frameCount, ANIMMODE_ONCE, 0.0f); + this->unk_25E = this->unk_260 = 0; + this->unk_21C = 0; + this->timer = 10; + EnVm_SetupAction(this, EnVm_Wait); +} + +void EnVm_Wait(EnVm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + f32 dist; + s16 headRot; + s16 pad; + s16 pitch; + + switch (this->unk_25E) { + case 0: + Math_SmoothStepToS(&this->beamRot.x, 0, 10, 1500, 0); + headRot = this->actor.yawTowardsPlayer - this->headRotY - this->actor.shape.rot.y; + pitch = Math_Vec3f_Pitch(&this->beamPos1, &player->actor.world.pos); + + if (pitch > 0x1B91) { + pitch = 0x1B91; + } + + dist = this->beamSightRange - this->actor.xzDistToPlayer; + + if (this->actor.xzDistToPlayer <= this->beamSightRange && ABS(headRot) <= 0x2710 && pitch >= 0xE38 && + this->actor.yDistToPlayer <= 80.0f && this->actor.yDistToPlayer >= -160.0f) { + Math_SmoothStepToS(&this->beamRot.x, pitch, 10, 0xFA0, 0); + if (Math_SmoothStepToS(&this->headRotY, this->actor.yawTowardsPlayer - this->actor.shape.rot.y, 1, + (ABS((s16)(dist * 180.0f)) / 3) + 0xFA0, 0) <= 5460) { + this->timer--; + if (this->timer == 0) { + this->unk_25E++; + this->skelAnime.curFrame = 0.0f; + this->skelAnime.startFrame = 0.0f; + this->skelAnime.playSpeed = 2.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIMOS_AIM); + } + } + } else { + this->headRotY -= 0x1F4; + } + + SkelAnime_Update(&this->skelAnime); + return; + case 1: + break; + default: + return; + } + + Math_SmoothStepToS(&this->headRotY, this->actor.yawTowardsPlayer - this->actor.shape.rot.y, 1, 0x1F40, 0); + + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_260++; + this->skelAnime.curFrame = 0.0f; + } + + if (this->unk_260 == 2) { + this->beamRot.y = this->actor.yawTowardsPlayer; + this->beamRot.x = Math_Vec3f_Pitch(&this->beamPos1, &player->actor.world.pos); + + if (this->beamRot.x > 0x1B91) { + this->beamRot.x = 0x1B91; + } + + if (this->beamRot.x < 0xAAA) { + this->skelAnime.startFrame = this->skelAnime.curFrame = this->skelAnime.endFrame; + this->unk_25E = this->unk_260 = 0; + this->timer = 10; + this->skelAnime.playSpeed = 1.0f; + } else { + this->skelAnime.curFrame = 6.0f; + EffectSsDeadDd_Spawn(globalCtx, &this->beamPos2, &D_80B2EAEC, &D_80B2EAEC, 150, -25, 0, 0, 255, 0, 255, 255, + 255, 16, 20); + EnVm_SetupAttack(this); + } + } +} + +void EnVm_SetupAttack(EnVm* this) { + Animation_Change(&this->skelAnime, &gBeamosAnim, 3.0f, 3.0f, 7.0f, ANIMMODE_ONCE, 0.0f); + this->timer = 305; + this->beamScale.x = 0.6f; + this->beamSpeed = 40.0f; + this->unk_21C = 1; + this->colliderQuad1.base.atFlags &= ~AT_HIT; + EnVm_SetupAction(this, EnVm_Attack); +} + +void EnVm_Attack(EnVm* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 pitch = Math_Vec3f_Pitch(&this->beamPos1, &player->actor.world.pos); + f32 dist; + Vec3f playerPos; + + if (pitch > 0x1B91) { + pitch = 0x1B91; + } + + if (this->colliderQuad1.base.atFlags & AT_HIT) { + this->colliderQuad1.base.atFlags &= ~AT_HIT; + this->timer = 0; + + if (this->beamScale.x > 0.1f) { + this->beamScale.x = 0.1f; + } + } + + if (this->beamRot.x < 0xAAA || this->timer == 0) { + Math_SmoothStepToF(&this->beamScale.x, 0.0f, 1.0f, 0.03f, 0.0f); + this->unk_260 = 0; + + if (this->beamScale.x == 0.0f) { + this->beamScale.y = this->beamScale.z = 0.0f; + EnVm_SetupWait(this); + } + } else { + if (--this->timer > 300) { + return; + } + + Math_SmoothStepToS(&this->headRotY, -this->actor.shape.rot.y + this->actor.yawTowardsPlayer, 10, 0xDAC, 0); + Math_SmoothStepToS(&this->beamRot.y, this->actor.yawTowardsPlayer, 10, 0xDAC, 0); + Math_SmoothStepToS(&this->beamRot.x, pitch, 10, 0xDAC, 0); + playerPos = player->actor.world.pos; + + if (player->actor.floorHeight > BGCHECK_Y_MIN) { + playerPos.y = player->actor.floorHeight; + } + + dist = Math_Vec3f_DistXYZ(&this->beamPos1, &playerPos); + Math_SmoothStepToF(&this->beamScale.z, dist, 1.0f, this->beamSpeed, 0.0f); + Math_SmoothStepToF(&this->beamScale.x, 0.1f, 1.0f, 0.12f, 0.0f); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIMOS_LAZER - SFX_FLAG); + + if (this->unk_260 > 2) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderQuad1.base); + } + + this->unk_260 = 3; + } + + if (SkelAnime_Update(&this->skelAnime)) { + this->skelAnime.curFrame = this->skelAnime.startFrame; + } +} + +void EnVm_SetupStun(EnVm* this) { + Animation_Change(&this->skelAnime, &gBeamosAnim, -1.0f, Animation_GetLastFrame(&gBeamosAnim), 0.0f, ANIMMODE_ONCE, + 0.0f); + this->unk_260 = 0; + this->timer = 180; + this->unk_25E = this->unk_260; + this->unk_21C = 2; + this->beamScale.z = 0.0f; + this->beamScale.y = 0.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + EnVm_SetupAction(this, EnVm_Stun); +} + +void EnVm_Stun(EnVm* this, GlobalContext* globalCtx) { + if (this->timer == 0) { + if (SkelAnime_Update(&this->skelAnime)) { + this->unk_25E++; + if (this->unk_25E == 3) { + EnVm_SetupWait(this); + } else if (this->unk_25E == 1) { + Animation_Change(&this->skelAnime, &gBeamosAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gBeamosAnim), + ANIMMODE_ONCE, 0.0f); + } else { + this->timer = 10; + this->skelAnime.curFrame = 0.0f; + this->skelAnime.playSpeed = 2.0f; + } + } + } else { + Math_SmoothStepToS(&this->beamRot.x, 0, 10, 0x5DC, 0); + this->timer--; + SkelAnime_Update(&this->skelAnime); + } +} + +void EnVm_SetupDie(EnVm* this) { + Animation_Change(&this->skelAnime, &gBeamosAnim, -1.0f, Animation_GetLastFrame(&gBeamosAnim), 0.0f, ANIMMODE_ONCE, + 0.0f); + this->timer = 33; + this->unk_25E = this->unk_260 = 0; + this->unk_21C = 3; + this->beamScale.z = 0.0f; + this->beamScale.y = 0.0f; + this->actor.shape.yOffset = -5000.0f; + this->actor.world.pos.y += 5000.0f * this->actor.scale.y; + this->actor.velocity.y = 8.0f; + this->actor.gravity = -0.5f; + this->actor.speedXZ = Rand_ZeroOne() + 1.0f; + this->actor.world.rot.y = Rand_CenteredFloat(65535.0f); + EnVm_SetupAction(this, EnVm_Die); +} + +void EnVm_Die(EnVm* this, GlobalContext* globalCtx) { + EnBom* bomb; + + this->beamRot.x += 0x5DC; + this->headRotY += 0x9C4; + Actor_MoveForward(&this->actor); + + if (--this->timer == 0) { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0x6FF, BOMB_BODY); + + if (bomb != NULL) { + bomb->timer = 0; + } + + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xA0); + Actor_Kill(&this->actor); + } +} + +void EnVm_CheckHealth(EnVm* this, GlobalContext* globalCtx) { + EnBom* bomb; + + if (Actor_GetCollidedExplosive(globalCtx, &this->colliderCylinder.base) != NULL) { + this->actor.colChkInfo.health--; + osSyncPrintf("hp down %d\n", this->actor.colChkInfo.health); + } else { + if (!(this->colliderQuad2.base.acFlags & AC_HIT) || this->unk_21C == 2) { + return; + } + this->colliderQuad2.base.acFlags &= ~AC_HIT; + } + + if (this->actor.colChkInfo.health != 0) { + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 8); + EnVm_SetupStun(this); + } else { + bomb = (EnBom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOM, this->actor.world.pos.x, + this->actor.world.pos.y + 20.0f, this->actor.world.pos.z, 0, 0, 0x601, BOMB_BODY); + + if (bomb != NULL) { + bomb->timer = 0; + } + + EnVm_SetupDie(this); + } +} + +void EnVm_Update(Actor* thisx, GlobalContext* globalCtx) { + EnVm* this = (EnVm*)thisx; + CollisionCheckContext* colChkCtx = &globalCtx->colChkCtx; + + if (this->actor.colChkInfo.health != 0) { + EnVm_CheckHealth(this, globalCtx); + } + + if (this->unk_260 == 4) { + EffectSsDeadDs_SpawnStationary(globalCtx, &this->beamPos3, 20, -1, 255, 20); + func_80033480(globalCtx, &this->beamPos3, 6.0f, 1, 120, 20, 1); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIMOS_LAZER_GND - SFX_FLAG); + } + + this->actionFunc(this, globalCtx); + this->beamTexScroll += 0xC; + + if (this->actor.colChkInfo.health != 0 && this->unk_21C != 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIMOS_ROLL_HEAD - SFX_FLAG); + } + + Collider_UpdateCylinder(&this->actor, &this->colliderCylinder); + CollisionCheck_SetOC(globalCtx, colChkCtx, &this->colliderCylinder.base); + + if (this->actor.colorFilterTimer == 0 && this->actor.colChkInfo.health != 0) { + CollisionCheck_SetAC(globalCtx, colChkCtx, &this->colliderCylinder.base); + } + + CollisionCheck_SetAC(globalCtx, colChkCtx, &this->colliderQuad2.base); + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += (6500.0f + this->actor.shape.yOffset) * this->actor.scale.y; +} + +s32 EnVm_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnVm* this = (EnVm*)thisx; + + if (limbIndex == 2) { + rot->x += this->beamRot.x; + rot->y += this->headRotY; + } else if (limbIndex == 10) { + if (this->unk_21C == 3) { + *dList = NULL; + } + } + + return false; +} + +void EnVm_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + EnVm* this = (EnVm*)thisx; + Vec3f sp80 = D_80B2EAF8; + Vec3f sp74 = D_80B2EB04; + Vec3f sp68 = D_80B2EB10; + s32 pad; + Vec3f posResult; + CollisionPoly* poly; + s32 bgId; + f32 dist; + + if (limbIndex == 2) { + Matrix_MultVec3f(&D_80B2EB1C, &this->beamPos1); + Matrix_MultVec3f(&D_80B2EB28, &this->beamPos2); + + if (this->unk_260 >= 3) { + poly = NULL; + sp80.z = (this->beamScale.z + 500.0f) * (this->actor.scale.y * 10000.0f); + Matrix_MultVec3f(&sp80, &this->beamPos3); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->beamPos1, &this->beamPos3, &posResult, &poly, true, + true, false, true, &bgId) == true) { + this->beamScale.z = Math_Vec3f_DistXYZ(&this->beamPos1, &posResult) - 5.0f; + this->unk_260 = 4; + this->beamPos3 = posResult; + } + if (this->beamScale.z != 0.0f) { + dist = 100.0f; + if (this->actor.scale.y > 0.01f) { + dist = 70.0f; + } + sp74.z = sp68.z = Math_Vec3f_DistXYZ(&this->beamPos1, &this->beamPos3) * dist; + Matrix_MultVec3f(&D_80B2EB64, &this->colliderQuad1.dim.quad[3]); + Matrix_MultVec3f(&D_80B2EB70, &this->colliderQuad1.dim.quad[2]); + Matrix_MultVec3f(&sp74, &this->colliderQuad1.dim.quad[1]); + Matrix_MultVec3f(&sp68, &this->colliderQuad1.dim.quad[0]); + Collider_SetQuadVertices(&this->colliderQuad1, &this->colliderQuad1.dim.quad[0], + &this->colliderQuad1.dim.quad[1], &this->colliderQuad1.dim.quad[2], + &this->colliderQuad1.dim.quad[3]); + } + } + Matrix_MultVec3f(&D_80B2EB34, &this->colliderQuad2.dim.quad[1]); + Matrix_MultVec3f(&D_80B2EB40, &this->colliderQuad2.dim.quad[0]); + Matrix_MultVec3f(&D_80B2EB4C, &this->colliderQuad2.dim.quad[3]); + Matrix_MultVec3f(&D_80B2EB58, &this->colliderQuad2.dim.quad[2]); + Collider_SetQuadVertices(&this->colliderQuad2, &this->colliderQuad2.dim.quad[0], + &this->colliderQuad2.dim.quad[1], &this->colliderQuad2.dim.quad[2], + &this->colliderQuad2.dim.quad[3]); + } +} + +void EnVm_Draw(Actor* thisx, GlobalContext* globalCtx2) { + EnVm* this = (EnVm*)thisx; + GlobalContext* globalCtx = globalCtx2; + Vec3f actorPos; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_vm.c", 1014); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + SkelAnime_DrawOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, EnVm_OverrideLimbDraw, + EnVm_PostLimbDraw, this); + actorPos = this->actor.world.pos; + func_80033C30(&actorPos, &D_80B2EB7C, 255, globalCtx); + + if (this->unk_260 >= 3) { + Matrix_Translate(this->beamPos3.x, this->beamPos3.y + 10.0f, this->beamPos3.z, MTXMODE_NEW); + Matrix_Scale(0.8f, 0.8f, 0.8f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vm.c", 1033), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 168); + func_80094BC4(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 255, 0); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_80B2EB88[globalCtx->gameplayFrames % 8])); + gSPDisplayList(POLY_XLU_DISP++, gEffEnemyDeathFlameDL); + Matrix_RotateY(32767.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vm.c", 1044), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_80B2EB88[(globalCtx->gameplayFrames + 4) % 8])); + gSPDisplayList(POLY_XLU_DISP++, gEffEnemyDeathFlameDL); + } + gSPSegment(POLY_OPA_DISP++, 0x08, func_80094E78(globalCtx->state.gfxCtx, 0, this->beamTexScroll)); + Matrix_Translate(this->beamPos1.x, this->beamPos1.y, this->beamPos1.z, MTXMODE_NEW); + Matrix_RotateZYX(this->beamRot.x, this->beamRot.y, this->beamRot.z, MTXMODE_APPLY); + Matrix_Scale(this->beamScale.x * 0.1f, this->beamScale.x * 0.1f, this->beamScale.z * 0.0015f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_vm.c", 1063), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gBeamosLaserDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_vm.c", 1068); +} diff --git a/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.h b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.h new file mode 100644 index 000000000..b7bd00e8c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Vm/z_en_vm.h @@ -0,0 +1,40 @@ +#ifndef Z_EN_VM_H +#define Z_EN_VM_H + +#include "ultra64.h" +#include "global.h" + +struct EnVm; + +typedef void (*EnVmActionFunc)(struct EnVm*, GlobalContext*); + +typedef struct EnVm { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[11]; + /* 0x01D2 */ Vec3s morphTable[11]; + /* 0x0214 */ EnVmActionFunc actionFunc; + /* 0x0218 */ f32 beamSightRange; + /* 0x021C */ s32 unk_21C; + /* 0x0220 */ s32 timer; + /* 0x0224 */ Vec3f beamPos1; + /* 0x0230 */ Vec3f beamPos2; + /* 0x023C */ Vec3f beamPos3; + /* 0x0248 */ Vec3f beamScale; + /* 0x0254 */ Vec3s beamRot; + /* 0x025A */ s16 beamTexScroll; + /* 0x025C */ s16 headRotY; + /* 0x025E */ s16 unk_25E; + /* 0x0260 */ s16 unk_260; + /* 0x0264 */ f32 beamSpeed; + /* 0x0268 */ ColliderCylinder colliderCylinder; + /* 0x02B4 */ ColliderQuad colliderQuad1; + /* 0x0334 */ ColliderQuad colliderQuad2; +} EnVm; // size = 0x03B4 + +typedef enum { + /* 0x00 */ BEAMOS_LARGE, + /* 0x01 */ BEAMOS_SMALL +} BeamosType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.c b/soh/src/overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.c new file mode 100644 index 000000000..0d30c2d4f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.c @@ -0,0 +1,155 @@ +/* + * File: z_en_wall_tubo.c + * Overlay: ovl_En_Wall_Tubo + * Description: Bombchu Bowling Alley Bullseyes/Pits + */ + +#include "z_en_wall_tubo.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Bom_Chu/z_en_bom_chu.h" +#include "overlays/actors/ovl_Bg_Bowl_Wall/z_bg_bowl_wall.h" +#include "overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnWallTubo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWallTubo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWallTubo_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnWallTubo_FindGirl(EnWallTubo* this, GlobalContext* globalCtx); +void EnWallTubo_DetectChu(EnWallTubo* this, GlobalContext* globalCtx); +void EnWallTubo_SetWallFall(EnWallTubo* this, GlobalContext* globalCtx); + +const ActorInit En_Wall_Tubo_InitVars = { + ACTOR_EN_WALL_TUBO, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnWallTubo), + (ActorFunc)EnWallTubo_Init, + (ActorFunc)EnWallTubo_Destroy, + (ActorFunc)EnWallTubo_Update, + NULL, + NULL, +}; + +void EnWallTubo_Init(Actor* thisx, GlobalContext* globalCtx) { + EnWallTubo* this = (EnWallTubo*)thisx; + + osSyncPrintf("\n\n"); + // "Wall Target" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 壁のツボ ☆☆☆☆☆ \n" VT_RST); + this->unk_164 = this->actor.world.pos; + this->actionFunc = EnWallTubo_FindGirl; +} + +void EnWallTubo_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnWallTubo_FindGirl(EnWallTubo* this, GlobalContext* globalCtx) { + Actor* lookForGirl; + + lookForGirl = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + + while (lookForGirl != NULL) { + if (lookForGirl->id != ACTOR_EN_BOM_BOWL_MAN) { + lookForGirl = lookForGirl->next; + } else { + this->chuGirl = (EnBomBowlMan*)lookForGirl; + break; + } + } + + this->actionFunc = EnWallTubo_DetectChu; +} + +void EnWallTubo_DetectChu(EnWallTubo* this, GlobalContext* globalCtx) { + EnBomChu* chu; + s32 pad; + Vec3f effAccel = { 0.0f, 0.1f, 0.0f }; + Vec3f effVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f chuPosDiff; + s16 quakeIndex; + + if (this->chuGirl->minigamePlayStatus != 0) { + if (globalCtx->cameraPtrs[MAIN_CAM]->setting == CAM_SET_CHU_BOWLING) { + chu = (EnBomChu*)globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].head; + + while (chu != NULL) { + if ((&chu->actor == &this->actor) || (chu->actor.id != ACTOR_EN_BOM_CHU)) { + chu = (EnBomChu*)chu->actor.next; + continue; + } + + chuPosDiff.x = chu->actor.world.pos.x - this->actor.world.pos.x; + chuPosDiff.y = chu->actor.world.pos.y - this->actor.world.pos.y; + chuPosDiff.z = chu->actor.world.pos.z - this->actor.world.pos.z; + + if (((fabsf(chuPosDiff.x) < 40.0f) || (BREG(2))) && ((fabsf(chuPosDiff.y) < 40.0f) || (BREG(2))) && + (fabsf(chuPosDiff.z) < 40.0f || (BREG(2)))) { + this->chuGirl->wallStatus[this->actor.params] = 1; + chu->timer = 2; + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + this->timer = 60; + EffectSsBomb2_SpawnLayered(globalCtx, &this->explosionCenter, &effVelocity, &effAccel, 200, 40); + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 1); + Quake_SetSpeed(quakeIndex, 0x7FFF); + Quake_SetQuakeValues(quakeIndex, 100, 0, 0, 0); + Quake_SetCountdown(quakeIndex, 100); + this->actionFunc = EnWallTubo_SetWallFall; + break; + } + + chu = (EnBomChu*)chu->actor.next; + } + } + } +} + +void EnWallTubo_SetWallFall(EnWallTubo* this, GlobalContext* globalCtx) { + BgBowlWall* wall; + Vec3f effAccel = { 0.0f, 0.1f, 0.0f }; + Vec3f effVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f effPos; + + if ((globalCtx->gameplayFrames & 1) == 0) { + effPos.x = this->explosionCenter.x + Rand_CenteredFloat(300.0f); + effPos.y = this->explosionCenter.y + Rand_CenteredFloat(300.0f); + effPos.z = this->explosionCenter.z; + EffectSsBomb2_SpawnLayered(globalCtx, &effPos, &effVelocity, &effAccel, 100, 30); + EffectSsHahen_SpawnBurst(globalCtx, &effPos, 10.0f, 0, 50, 15, 3, HAHEN_OBJECT_DEFAULT, 10, NULL); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_BOMB_EXPLOSION); + } + + if (this->timer == 0) { + wall = (BgBowlWall*)this->actor.parent; + + if ((wall != NULL) && (wall->dyna.actor.update != NULL)) { + wall->isHit = true; + // "You did it field!" (repeated 5 times) + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆ やった原! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆ やった原! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(BLUE) "☆☆☆☆ やった原! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆ やった原! ☆☆☆☆☆ \n" VT_RST); + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆ やった原! ☆☆☆☆☆ \n" VT_RST); + } + + Actor_Kill(&this->actor); + } +} + +void EnWallTubo_Update(Actor* thisx, GlobalContext* globalCtx) { + EnWallTubo* this = (EnWallTubo*)thisx; + + if (this->timer != 0) { + this->timer--; + } + + this->actionFunc(this, globalCtx); + + if (BREG(0)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 0, 0, 255, 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.h b/soh/src/overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.h new file mode 100644 index 000000000..e1de7c881 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wall_Tubo/z_en_wall_tubo.h @@ -0,0 +1,21 @@ +#ifndef Z_EN_WALL_TUBO_H +#define Z_EN_WALL_TUBO_H + +#include "ultra64.h" +#include "global.h" +#include "overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.h" + +struct EnWallTubo; + +typedef void (*EnWallTuboActionFunc)(struct EnWallTubo*, GlobalContext*); + +typedef struct EnWallTubo { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnWallTuboActionFunc actionFunc; + /* 0x0150 */ s16 timer; + /* 0x0154 */ Vec3f explosionCenter; + /* 0x0160 */ EnBomBowlMan* chuGirl; + /* 0x0164 */ Vec3f unk_164; +} EnWallTubo; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c new file mode 100644 index 000000000..a10b80b37 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c @@ -0,0 +1,654 @@ +/* + * File: z_en_wallmas + * Overlay: En_Wallmas + * Description: Wallmaster (Ceiling monster) + */ + +#include "z_en_wallmas.h" +#include "objects/object_wallmaster/object_wallmaster.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +#define TIMER_SCALE ((f32)OS_CLOCK_RATE / 10000000000) +#define DEGREE_60_RAD (60.0f * M_PI / 180.0f) +#define DEGREE_15_RAD (15.0f * M_PI / 180.0f) + +#define DAMAGE_EFFECT_BURN 2 +#define DAMAGE_EFFECT_STUN_WHITE 4 +#define DAMAGE_EFFECT_STUN_BLUE 1 + +void EnWallmas_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWallmas_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWallmas_Update(Actor* thisx, GlobalContext* globalCtx); +void EnWallmas_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnWallmas_TimerInit(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_ProximityOrSwitchInit(EnWallmas* this); +void EnWallmas_WaitToDrop(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Drop(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Land(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Stand(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_JumpToCeiling(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_ReturnToCeiling(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_TakeDamage(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Cooldown(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Die(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_TakePlayer(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_WaitForProximity(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_WaitForSwitchFlag(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Stun(EnWallmas* this, GlobalContext* globalCtx); +void EnWallmas_Walk(EnWallmas* this, GlobalContext* globalCtx); + +const ActorInit En_Wallmas_InitVars = { + ACTOR_EN_WALLMAS, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_WALLMASTER, + sizeof(EnWallmas), + (ActorFunc)EnWallmas_Init, + (ActorFunc)EnWallmas_Destroy, + (ActorFunc)EnWallmas_Update, + (ActorFunc)EnWallmas_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 40, 0, { 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 4, 30, 40, 150 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(0, 0x1), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(4, 0x2), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(4, 0x4), + /* Unk arrow 1 */ DMG_ENTRY(4, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(2, 0x0), + /* Fire magic */ DMG_ENTRY(4, 0x2), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(4, 0x4), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x30, 1), + ICHAIN_F32(targetArrowOffset, 5500, 1), + ICHAIN_F32_DIV1000(gravity, -1500, 0), +}; + +void EnWallmas_Init(Actor* thisx, GlobalContext* globalCtx) { + EnWallmas* this = (EnWallmas*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + ActorShape_Init(&thisx->shape, 0, NULL, 0.5f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gWallmasterSkel, &gWallmasterWaitAnim, this->jointTable, + this->morphTable, 25); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, thisx, &sCylinderInit); + CollisionCheck_SetInfo(&thisx->colChkInfo, &sDamageTable, &sColChkInfoInit); + this->switchFlag = (u8)(thisx->params >> 0x8); + thisx->params = thisx->params & 0xFF; + + if (thisx->params == WMT_FLAG) { + if (Flags_GetSwitch(globalCtx, this->switchFlag) != 0) { + Actor_Kill(thisx); + return; + } + + EnWallmas_ProximityOrSwitchInit(this); + } else if (thisx->params == WMT_PROXIMITY) { + EnWallmas_ProximityOrSwitchInit(this); + } else { + EnWallmas_TimerInit(this, globalCtx); + } +} + +void EnWallmas_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnWallmas* this = (EnWallmas*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnWallmas_TimerInit(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.flags |= ACTOR_FLAG_5; + this->timer = 0x82; + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = player->actor.world.pos.y; + this->actor.floorHeight = player->actor.floorHeight; + this->actor.draw = EnWallmas_Draw; + this->actionFunc = EnWallmas_WaitToDrop; +} + +void EnWallmas_SetupDrop(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + AnimationHeader* objSegChangee = &gWallmasterLungeAnim; + + Animation_Change(&this->skelAnime, objSegChangee, 0.0f, 20.0f, Animation_GetLastFrame(&gWallmasterLungeAnim), + ANIMMODE_ONCE, 0.0f); + + this->yTarget = player->actor.world.pos.y; + this->actor.world.pos.y = player->actor.world.pos.y + 300.0f; + this->actor.world.rot.y = player->actor.shape.rot.y + 0x8000; + this->actor.floorHeight = player->actor.floorHeight; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.flags &= ~ACTOR_FLAG_5; + this->actionFunc = EnWallmas_Drop; +} + +void EnWallmas_SetupLand(EnWallmas* this, GlobalContext* globalCtx) { + AnimationHeader* objSegFrameCount = &gWallmasterJumpAnim; + AnimationHeader* objSegChangee = &gWallmasterJumpAnim; + + Animation_Change(&this->skelAnime, objSegChangee, 1.0f, 41.0f, Animation_GetLastFrame(objSegFrameCount), + ANIMMODE_ONCE, -3.0f); + + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 15.0f, 6, 20.0f, 0x12C, 0x64, 1); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_LAND); + this->actionFunc = EnWallmas_Land; +} + +void EnWallmas_SetupStand(EnWallmas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterStandUpAnim); + this->actionFunc = EnWallmas_Stand; +} + +void EnWallmas_SetupWalk(EnWallmas* this) { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gWallmasterWalkAnim, 3.0f); + this->actionFunc = EnWallmas_Walk; + this->actor.speedXZ = 3.0f; +} + +void EnWallmas_SetupJumpToCeiling(EnWallmas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterStopWalkAnim); + this->actionFunc = EnWallmas_JumpToCeiling; + this->actor.speedXZ = 0.0f; +} +void EnWallmas_SetupReturnToCeiling(EnWallmas* this) { + AnimationHeader* objSegFrameCount = &gWallmasterJumpAnim; + AnimationHeader* objSegChangee = &gWallmasterJumpAnim; + + this->timer = 0; + this->actor.speedXZ = 0.0f; + + Animation_Change(&this->skelAnime, objSegChangee, 3.0f, 0.0f, Animation_GetLastFrame(objSegFrameCount), + ANIMMODE_ONCE, -3.0f); + + this->actionFunc = EnWallmas_ReturnToCeiling; +} + +void EnWallmas_SetupTakeDamage(EnWallmas* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWallmasterDamageAnim, -3.0f); + if (this->collider.info.acHitInfo->toucher.dmgFlags & 0x0001F824) { + this->actor.world.rot.y = this->collider.base.ac->world.rot.y; + } else { + this->actor.world.rot.y = Actor_WorldYawTowardActor(&this->actor, this->collider.base.ac) + 0x8000; + } + + Actor_SetColorFilter(&this->actor, 0x4000, 0xFF, 0, 0x14); + this->actionFunc = EnWallmas_TakeDamage; + this->actor.speedXZ = 5.0f; + this->actor.velocity.y = 10.0f; +} + +void EnWallmas_SetupCooldown(EnWallmas* this) { + Animation_PlayOnce(&this->skelAnime, &gWallmasterRecoverFromDamageAnim); + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actionFunc = EnWallmas_Cooldown; +} + +void EnWallmas_SetupDie(EnWallmas* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + + EffectSsDeadDb_Spawn(globalCtx, &this->actor.world.pos, &zeroVec, &zeroVec, 250, -10, 255, 255, 255, 255, 0, 0, 255, + 1, 9, true); + + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xC0); + this->actionFunc = EnWallmas_Die; +} + +void EnWallmas_SetupTakePlayer(EnWallmas* this, GlobalContext* globalCtx) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWallmasterHoverAnim, -5.0f); + this->timer = -0x1E; + this->actionFunc = EnWallmas_TakePlayer; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + + this->yTarget = this->actor.yDistToPlayer; + func_8002DF38(globalCtx, &this->actor, 0x25); + OnePointCutscene_Init(globalCtx, 9500, 9999, &this->actor, MAIN_CAM); +} + +void EnWallmas_ProximityOrSwitchInit(EnWallmas* this) { + this->timer = 0; + this->actor.draw = NULL; + this->actor.flags &= ~ACTOR_FLAG_0; + if (this->actor.params == WMT_PROXIMITY) { + this->actionFunc = EnWallmas_WaitForProximity; + } else { + this->actionFunc = EnWallmas_WaitForSwitchFlag; + } +} + +void EnWallmas_SetupStun(EnWallmas* this) { + Animation_Change(&this->skelAnime, &gWallmasterJumpAnim, 1.5f, 0, 20.0f, ANIMMODE_ONCE, -3.0f); + + this->actor.speedXZ = 0.0f; + if (this->actor.colChkInfo.damageEffect == 4) { + Actor_SetColorFilter(&this->actor, -0x8000, 0xFF, 0, 0x50); + } else { + Actor_SetColorFilter(&this->actor, 0, 0xFF, 0, 0x50); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + } + + this->timer = 0x50; + this->actionFunc = EnWallmas_Stun; +} + +void EnWallmas_WaitToDrop(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f* playerPos = &player->actor.world.pos; + + this->actor.world.pos = *playerPos; + this->actor.floorHeight = player->actor.floorHeight; + this->actor.floorPoly = player->actor.floorPoly; + + if (this->timer != 0) { + this->timer--; + } + + if ((player->stateFlags1 & 0x100000) || (player->stateFlags1 & 0x8000000) || !(player->actor.bgCheckFlags & 1) || + ((this->actor.params == 1) && (320.0f < Math_Vec3f_DistXZ(&this->actor.home.pos, playerPos)))) { + Audio_StopSfxById(NA_SE_EN_FALL_AIM); + this->timer = 0x82; + } + + if (this->timer == 0x50) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_AIM); + } + + if (this->timer == 0) { + EnWallmas_SetupDrop(this, globalCtx); + } +} + +void EnWallmas_Drop(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (!Player_InCsMode(globalCtx) && !(player->stateFlags2 & 0x10) && (player->invincibilityTimer >= 0) && + (this->actor.xzDistToPlayer < 30.0f) && (this->actor.yDistToPlayer < -5.0f) && + (-(f32)(player->cylinder.dim.height + 10) < this->actor.yDistToPlayer)) { + EnWallmas_SetupTakePlayer(this, globalCtx); + } +} + +void EnWallmas_Land(EnWallmas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnWallmas_SetupStand(this); + } +} + +void EnWallmas_Stand(EnWallmas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnWallmas_SetupWalk(this); + } + + Math_ScaledStepToS(&this->actor.world.rot.y, this->actor.yawTowardsPlayer + 0x8000, 0xB6); +} + +void EnWallmas_Walk(EnWallmas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnWallmas_SetupJumpToCeiling(this); + } + + Math_ScaledStepToS(&this->actor.world.rot.y, (s16)((s32)this->actor.yawTowardsPlayer + 0x8000), 0xB6); + + if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f) || + Animation_OnFrame(&this->skelAnime, 24.0f) || Animation_OnFrame(&this->skelAnime, 36.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_WALK); + } +} + +void EnWallmas_JumpToCeiling(EnWallmas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnWallmas_SetupReturnToCeiling(this); + } +} + +void EnWallmas_ReturnToCeiling(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + SkelAnime_Update(&this->skelAnime); + if (this->skelAnime.curFrame > 20.0f) { + this->timer += 9; + this->actor.world.pos.y = this->actor.world.pos.y + 30.0f; + } + + if (Animation_OnFrame(&this->skelAnime, 20.0f) != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_UP); + } + + if (this->actor.yDistToPlayer < -900.0f) { + if (this->actor.params == WMT_FLAG) { + Actor_Kill(&this->actor); + return; + } + + if (this->actor.params == WMT_TIMER || + Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 200.0f) { + EnWallmas_TimerInit(this, globalCtx); + } else { + EnWallmas_ProximityOrSwitchInit(this); + } + } +} + +void EnWallmas_TakeDamage(EnWallmas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + if (this->actor.colChkInfo.health == 0) { + EnWallmas_SetupDie(this, globalCtx); + } else { + EnWallmas_SetupCooldown(this); + } + } + if (Animation_OnFrame(&this->skelAnime, 13.0f) != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + + Math_StepToF(&this->actor.speedXZ, 0.0f, 0.2f); +} + +void EnWallmas_Cooldown(EnWallmas* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime) != 0) { + EnWallmas_SetupReturnToCeiling(this); + } +} + +void EnWallmas_Die(EnWallmas* this, GlobalContext* globalCtx) { + if (Math_StepToF(&this->actor.scale.x, 0.0f, 0.0015) != 0) { + Actor_SetScale(&this->actor, 0.01f); + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xC0); + Actor_Kill(&this->actor); + } + this->actor.scale.z = this->actor.scale.x; + this->actor.scale.y = this->actor.scale.x; +} + +void EnWallmas_TakePlayer(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Animation_OnFrame(&this->skelAnime, 1.0f) != 0) { + if (!LINK_IS_ADULT) { + func_8002F7DC(&this->actor, NA_SE_VO_LI_DAMAGE_S_KID); + } else { + func_8002F7DC(&this->actor, NA_SE_VO_LI_DAMAGE_S); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_CATCH); + } + if (SkelAnime_Update(&this->skelAnime) != 0) { + player->actor.world.pos.x = this->actor.world.pos.x; + player->actor.world.pos.z = this->actor.world.pos.z; + + if (this->timer < 0) { + this->actor.world.pos.y = this->actor.world.pos.y + 2.0f; + } else { + this->actor.world.pos.y = this->actor.world.pos.y + 10.0f; + } + + if (!LINK_IS_ADULT) { + player->actor.world.pos.y = this->actor.world.pos.y - 30.0f; + } else { + player->actor.world.pos.y = this->actor.world.pos.y - 50.0f; + } + + if (this->timer == -0x1E) { + if (!LINK_IS_ADULT) { + func_8002F7DC(&this->actor, NA_SE_VO_LI_TAKEN_AWAY_KID); + } else { + func_8002F7DC(&this->actor, NA_SE_VO_LI_TAKEN_AWAY); + } + } + if (this->timer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_UP); + } + + this->timer = this->timer + 2; + } else { + Math_StepToF(&this->actor.world.pos.y, player->actor.world.pos.y + (!LINK_IS_ADULT ? 30.0f : 50.0f), 5.0f); + } + + Math_StepToF(&this->actor.world.pos.x, player->actor.world.pos.x, 3.0f); + Math_StepToF(&this->actor.world.pos.z, player->actor.world.pos.z, 3.0f); + + if (this->timer == 0x1E) { + func_80078884(NA_SE_OC_ABYSS); + Gameplay_TriggerRespawn(globalCtx); + } +} + +void EnWallmas_WaitForProximity(EnWallmas* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + if (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 200.0f) { + EnWallmas_TimerInit(this, globalCtx); + } +} + +void EnWallmas_WaitForSwitchFlag(EnWallmas* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->switchFlag) != 0) { + EnWallmas_TimerInit(this, globalCtx); + this->timer = 0x51; + } +} + +void EnWallmas_Stun(EnWallmas* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + if (this->timer != 0) { + this->timer--; + } + + if (this->timer == 0) { + if (this->actor.colChkInfo.health == 0) { + EnWallmas_SetupDie(this, globalCtx); + } else { + EnWallmas_SetupReturnToCeiling(this); + } + } +} + +void EnWallmas_ColUpdate(EnWallmas* this, GlobalContext* globalCtx) { + if ((this->collider.base.acFlags & AC_HIT) != 0) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_DEAD); + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + if (this->actor.colChkInfo.damage != 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FALL_DAMAGE); + } + } + + if ((this->actor.colChkInfo.damageEffect == DAMAGE_EFFECT_STUN_WHITE) || + (this->actor.colChkInfo.damageEffect == DAMAGE_EFFECT_STUN_BLUE)) { + if (this->actionFunc != EnWallmas_Stun) { + EnWallmas_SetupStun(this); + } + } else { + if (this->actor.colChkInfo.damageEffect == DAMAGE_EFFECT_BURN) { + EffectSsFCircle_Spawn(globalCtx, &this->actor, &this->actor.world.pos, 40, 40); + } + + EnWallmas_SetupTakeDamage(this); + } + } + } +} + +void EnWallmas_Update(Actor* thisx, GlobalContext* globalCtx) { + EnWallmas* this = (EnWallmas*)thisx; + char pad[4]; + + EnWallmas_ColUpdate(this, globalCtx); + this->actionFunc(this, globalCtx); + + if ((this->actionFunc == EnWallmas_WaitToDrop) || (this->actionFunc == EnWallmas_WaitForProximity) || + (this->actionFunc == EnWallmas_TakePlayer) || (this->actionFunc == EnWallmas_WaitForSwitchFlag)) { + return; + } + + if ((this->actionFunc != EnWallmas_ReturnToCeiling) && (this->actionFunc != EnWallmas_TakePlayer)) { + Actor_MoveForward(&this->actor); + } + + if (this->actionFunc != EnWallmas_Drop) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 20.0f, 25.0f, 0.0f, 0x1D); + } else if (this->actor.world.pos.y <= this->yTarget) { + this->actor.world.pos.y = this->yTarget; + this->actor.velocity.y = 0.0f; + EnWallmas_SetupLand(this, globalCtx); + } + + if ((this->actionFunc != EnWallmas_Die) && (this->actionFunc != EnWallmas_Drop)) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if ((this->actionFunc != EnWallmas_TakeDamage) && (this->actor.bgCheckFlags & 1) != 0 && + (this->actor.freezeTimer == 0)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + + Actor_SetFocus(&this->actor, 25.0f); + + if (this->actionFunc == EnWallmas_TakeDamage) { + return; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; +} + +void EnWallmas_DrawXlu(EnWallmas* this, GlobalContext* globalCtx) { + s32 pad; + f32 xzScale; + MtxF mf; + + if ((this->actor.floorPoly == NULL) || ((this->timer >= 0x51) && (this->actionFunc != EnWallmas_Stun))) { + return; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_wallmas.c", 1386); + + func_80094044(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 255); + + func_80038A28(this->actor.floorPoly, this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z, + &mf); + Matrix_Mult(&mf, MTXMODE_NEW); + + if ((this->actionFunc != EnWallmas_WaitToDrop) && (this->actionFunc != EnWallmas_ReturnToCeiling) && + (this->actionFunc != EnWallmas_TakePlayer) && (this->actionFunc != EnWallmas_WaitForSwitchFlag)) { + xzScale = this->actor.scale.x * 50.0f; + } else { + xzScale = ((0x50 - this->timer) >= 0x51 ? 0x50 : (0x50 - this->timer)) * TIMER_SCALE; + } + + Matrix_Scale(xzScale, 1.0f, xzScale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_wallmas.c", 1421), G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, gCircleShadowDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_wallmas.c", 1426); +} + +s32 EnWallMas_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnWallmas* this = (EnWallmas*)thisx; + + if (limbIndex == 1) { + if (this->actionFunc != EnWallmas_TakePlayer) { + pos->z -= 1600.0f; + } else { + pos->z -= ((1600.0f * (this->skelAnime.endFrame - this->skelAnime.curFrame)) / this->skelAnime.endFrame); + } + } + return false; +} + +void EnWallMas_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + if (limbIndex == 2) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_wallmas.c", 1478); + + Matrix_Push(); + Matrix_Translate(1600.0f, -700.0f, -1700.0f, MTXMODE_APPLY); + Matrix_RotateY(DEGREE_60_RAD, MTXMODE_APPLY); + Matrix_RotateZ(DEGREE_15_RAD, MTXMODE_APPLY); + Matrix_Scale(2.0f, 2.0f, 2.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_wallmas.c", 1489), G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gWallmasterFingerDL); + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_wallmas.c", 1495); + } +} + +void EnWallmas_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnWallmas* this = (EnWallmas*)thisx; + + if (this->actionFunc != EnWallmas_WaitToDrop) { + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnWallMas_OverrideLimbDraw, EnWallMas_PostLimbDraw, this); + } + + EnWallmas_DrawXlu(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h new file mode 100644 index 000000000..fde6f4bb4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.h @@ -0,0 +1,29 @@ +#ifndef Z_EN_WALLMAS_H +#define Z_EN_WALLMAS_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ WMT_TIMER, + /* 0x01 */ WMT_PROXIMITY, + /* 0x02 */ WMT_FLAG +} WallmasType; + +struct EnWallmas; + +typedef void (*EnWallmasActionFunc)(struct EnWallmas*, GlobalContext*); + +typedef struct EnWallmas { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnWallmasActionFunc actionFunc; + /* 0x0194 */ s16 timer; + /* 0x0196 */ s16 switchFlag; + /* 0x0198 */ Vec3s jointTable[25]; + /* 0x022E */ Vec3s morphTable[25]; + /* 0x02C4 */ f32 yTarget; + /* 0x02C8 */ ColliderCylinder collider; +} EnWallmas; // size = 0x0314 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c b/soh/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c new file mode 100644 index 000000000..67667cb8d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.c @@ -0,0 +1,333 @@ +/* + * File: z_en_weather_tag.c + * Overlay: ovl_En_Weather_Tag + * Description: Proximity Activated Weather Effects + */ + +#include "z_en_weather_tag.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnWeatherTag_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWeatherTag_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWeatherTag_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnWeatherTag_DisabledCloudyHyruleMarket(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledCloudyHyruleMarket(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_DisabledCloudyLonLonRanch(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledCloudyLonLonRanch(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_DisabledCloudySnow(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledCloudySnow(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_DisabledRainLakeHylia(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledRainLakeHylia(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_DisabledCloudyDeathMountain(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledCloudyDeathMountain(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_DisabledCloudyRainThunderKakariko(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledCloudyRainThunderKakariko(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_SetSandstormIntensity(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_DisabledRainThunder(EnWeatherTag* this, GlobalContext* globalCtx); +void EnWeatherTag_EnabledRainThunder(EnWeatherTag* this, GlobalContext* globalCtx); + +#define WEATHER_TAG_RANGE100(x) ((x >> 8) * 100.0f) + +const ActorInit En_Weather_Tag_InitVars = { + ACTOR_EN_WEATHER_TAG, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnWeatherTag), + (ActorFunc)EnWeatherTag_Init, + (ActorFunc)EnWeatherTag_Destroy, + (ActorFunc)EnWeatherTag_Update, + NULL, + NULL, +}; + +void EnWeatherTag_SetupAction(EnWeatherTag* this, EnWeatherTagActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnWeatherTag_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnWeatherTag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnWeatherTag* this = (EnWeatherTag*)thisx; + + this->actor.flags &= ~ACTOR_FLAG_0; + + switch (this->actor.params & 0xF) { + case EN_WEATHER_TAG_TYPE_CLOUDY_MARKET: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ (;o;) About ☆☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ (;o;) くらいよー ☆☆☆☆☆ \n" VT_RST); + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyHyruleMarket); + break; + case EN_WEATHER_TAG_TYPE_CLOUDY_LON_LON_RANCH: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Cloudy (._.) Ah Melancholy ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ くもり (._.) あーあ 憂鬱 ☆☆☆☆☆ \n" VT_RST); + if (Flags_GetEventChkInf(0x18)) { + Actor_Kill(&this->actor); + } + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyLonLonRanch); + break; + case EN_WEATHER_TAG_TYPE_SNOW_ZORAS_DOMAIN: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Yukigafuru You won't come (._.) ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ ゆきがふるー あなたはこないー (._.) ☆☆☆☆☆ \n" VT_RST); + + if (gSaveContext.eventChkInf[4] & 0x400) { + Actor_Kill(&this->actor); + } + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudySnow); + break; + case EN_WEATHER_TAG_TYPE_RAIN_LAKE_HYLIA: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Wow wa wa na wa saa ki ha (^o^) ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ わわわわー なーがーさーきーはー (^o^) ☆☆☆☆☆ \n" VT_RST); + + if (gSaveContext.eventChkInf[4] & 0x400) { + Actor_Kill(&this->actor); + } + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledRainLakeHylia); + break; + case EN_WEATHER_TAG_TYPE_CLOUDY_DEATH_MOUNTAIN: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Cloudy (._.) Ah Melancholy ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ くもり (._.) あーあ 憂鬱 ☆☆☆☆☆ \n" VT_RST); + if (gSaveContext.eventChkInf[4] & 0x200) { + Actor_Kill(&this->actor); + } + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyDeathMountain); + break; + case EN_WEATHER_TAG_TYPE_THUNDERSTORM_KAKARIKO: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Cloudy Rain Thunder (;O;) Uo Melancholy ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ くもり雨雷 (;O;) うおお 憂鬱 ☆☆☆☆☆ \n" VT_RST); + + if (!(gSaveContext.eventChkInf[4] & 0x100) || !(gSaveContext.eventChkInf[4] & 0x200) || + !(gSaveContext.eventChkInf[4] & 0x400) || CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { + Actor_Kill(&this->actor); + } + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyRainThunderKakariko); + break; + case EN_WEATHER_TAG_TYPE_SANDSTORM_INTENSITY: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ The desert becomes thicker ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 砂漠が濃くなります ☆☆☆☆☆ \n" VT_RST); + EnWeatherTag_SetupAction(this, EnWeatherTag_SetSandstormIntensity); + break; + case EN_WEATHER_TAG_TYPE_THUNDERSTORM_GRAVEYARD: + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Wow wa wa na wa saa ki ha (^o^) ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ わわわわー なーがーさーきーはー (^o^) ☆☆☆☆☆ \n" VT_RST); + + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledRainThunder); + break; + } +} + +u8 WeatherTag_CheckEnableWeatherEffect(EnWeatherTag* this, GlobalContext* globalCtx, u8 arg2, u8 arg3, u8 arg4, u8 arg5, + u16 arg6, u8 weatherMode) { + s32 pad; + u8 ret = false; + Player* player = GET_PLAYER(globalCtx); + + if (Actor_WorldDistXZToActor(&player->actor, &this->actor) < WEATHER_TAG_RANGE100(this->actor.params)) { + if ((globalCtx->envCtx.indoors != 0) || !gSkyboxBlendingEnabled || + (globalCtx->skyboxId != SKYBOX_NORMAL_SKY && globalCtx->envCtx.unk_1F == globalCtx->envCtx.unk_20)) { + D_8011FB38 = 1; + if (globalCtx->envCtx.gloomySkyMode == 0 && + (globalCtx->envCtx.indoors != 0 || (globalCtx->envCtx.unk_1F != 1 && globalCtx->envCtx.unk_21 == 0))) { + D_8011FB38 = 0; + if (gWeatherMode != weatherMode) { + gWeatherMode = weatherMode; + if (globalCtx->envCtx.gloomySkyMode == 0) { + globalCtx->envCtx.unk_19 = 1; + globalCtx->envCtx.unk_17 = arg2; + globalCtx->envCtx.unk_18 = arg3; + globalCtx->envCtx.unk_1A = arg6; + globalCtx->envCtx.unk_21 = 1; + globalCtx->envCtx.unk_1F = arg4; + globalCtx->envCtx.unk_20 = arg5; + D_8011FB34 = arg5; + globalCtx->envCtx.unk_24 = arg6; + globalCtx->envCtx.unk_22 = globalCtx->envCtx.unk_24; + } + } + ret = true; + } + } else { + if (gTimeIncrement != 0) { + gSaveContext.dayTime += 0x14; + } + } + } + + return ret; +} + +u8 WeatherTag_CheckRestoreWeather(EnWeatherTag* this, GlobalContext* globalCtx, u8 arg2, u8 arg3, u8 arg4, u8 arg5, + u16 arg6) { + s32 pad; + u8 ret = false; + Player* player = GET_PLAYER(globalCtx); + + if ((WEATHER_TAG_RANGE100(this->actor.params) + 100.0f) < Actor_WorldDistXZToActor(&player->actor, &this->actor)) { + if (globalCtx->envCtx.indoors != 0 || !gSkyboxBlendingEnabled || + (globalCtx->skyboxId != SKYBOX_NORMAL_SKY && globalCtx->envCtx.unk_1F == globalCtx->envCtx.unk_20)) { + D_8011FB38 = 1; + if ((globalCtx->envCtx.gloomySkyMode == 0) && + (globalCtx->envCtx.indoors != 0 || (globalCtx->envCtx.unk_1F != 1 && globalCtx->envCtx.unk_21 == 0))) { + D_8011FB38 = 0; + gWeatherMode = 0; + globalCtx->envCtx.unk_19 = 1; + globalCtx->envCtx.unk_17 = arg2; + globalCtx->envCtx.unk_18 = arg3; + globalCtx->envCtx.unk_1A = arg6; + globalCtx->envCtx.unk_21 = 1; + globalCtx->envCtx.unk_1F = arg4; + globalCtx->envCtx.unk_20 = arg5; + D_8011FB34 = arg5; + globalCtx->envCtx.unk_24 = arg6; + globalCtx->envCtx.unk_22 = globalCtx->envCtx.unk_24; + + ret = true; + } + } else if (gTimeIncrement != 0) { + gSaveContext.dayTime += 0x14; + } + } + return ret; +} + +void EnWeatherTag_DisabledCloudyHyruleMarket(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckEnableWeatherEffect(this, globalCtx, 0, 1, 0, 3, 60, 1)) { + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledCloudyHyruleMarket); + } +} + +void EnWeatherTag_EnabledCloudyHyruleMarket(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckRestoreWeather(this, globalCtx, 1, 0, 3, 0, 60)) { + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyHyruleMarket); + } +} + +void EnWeatherTag_DisabledCloudyLonLonRanch(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckEnableWeatherEffect(this, globalCtx, 0, 1, 0, 2, 100, 2)) { + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledCloudyLonLonRanch); + } +} + +void EnWeatherTag_EnabledCloudyLonLonRanch(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckRestoreWeather(this, globalCtx, 1, 0, 2, 0, 100)) { + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyLonLonRanch); + } +} + +void EnWeatherTag_DisabledCloudyDeathMountain(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckEnableWeatherEffect(this, globalCtx, 0, 1, 0, 2, 60, 2)) { + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledCloudyDeathMountain); + } +} + +void EnWeatherTag_EnabledCloudyDeathMountain(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckRestoreWeather(this, globalCtx, 1, 0, 2, 0, 60)) { + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyLonLonRanch); + } +} + +void EnWeatherTag_DisabledCloudySnow(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckEnableWeatherEffect(this, globalCtx, 0, 1, 0, 2, 60, 3)) { + globalCtx->envCtx.unk_EE[3] = 64; + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledCloudySnow); + } +} + +void EnWeatherTag_EnabledCloudySnow(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckRestoreWeather(this, globalCtx, 1, 0, 2, 0, 60)) { + globalCtx->envCtx.unk_EE[3] = 0; + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudySnow); + } +} + +void EnWeatherTag_DisabledRainLakeHylia(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckEnableWeatherEffect(this, globalCtx, 0, 1, 0, 2, 100, 4)) { + Environment_PlayStormNatureAmbience(globalCtx); + globalCtx->envCtx.unk_EE[0] = 25; + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledRainLakeHylia); + } +} + +void EnWeatherTag_EnabledRainLakeHylia(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckRestoreWeather(this, globalCtx, 1, 0, 2, 0, 100)) { + Environment_StopStormNatureAmbience(globalCtx); + globalCtx->envCtx.unk_EE[0] = 0; + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledRainLakeHylia); + } +} + +void EnWeatherTag_DisabledCloudyRainThunderKakariko(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckEnableWeatherEffect(this, globalCtx, 0, 1, 0, 4, 100, 5)) { + Environment_PlayStormNatureAmbience(globalCtx); + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_ON; + globalCtx->envCtx.unk_EE[0] = 30; + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledCloudyRainThunderKakariko); + } +} + +void EnWeatherTag_EnabledCloudyRainThunderKakariko(EnWeatherTag* this, GlobalContext* globalCtx) { + if (WeatherTag_CheckRestoreWeather(this, globalCtx, 1, 0, 4, 0, 100)) { + Environment_StopStormNatureAmbience(globalCtx); + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_LAST; + globalCtx->envCtx.unk_EE[0] = 0; + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledCloudyRainThunderKakariko); + } +} + +void EnWeatherTag_SetSandstormIntensity(EnWeatherTag* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Actor_WorldDistXZToActor(&player->actor, &this->actor) < WEATHER_TAG_RANGE100(this->actor.params)) { + Math_SmoothStepToS(&globalCtx->envCtx.adjFogNear, -0x50, 1, 2, 1); + Math_SmoothStepToS(&globalCtx->envCtx.adjFogFar, -0x7D0, 1, 50, 1); + } else { + Math_SmoothStepToS(&globalCtx->envCtx.adjFogNear, 0, 1, 1, 1); + Math_SmoothStepToS(&globalCtx->envCtx.adjFogFar, 0, 1, 25, 1); + } +} + +void EnWeatherTag_DisabledRainThunder(EnWeatherTag* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (Actor_WorldDistXZToActor(&player->actor, &this->actor) < WEATHER_TAG_RANGE100(this->actor.params)) { + Environment_PlayStormNatureAmbience(globalCtx); + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_ON; + globalCtx->envCtx.unk_EE[0] = 25; + EnWeatherTag_SetupAction(this, EnWeatherTag_EnabledRainThunder); + } +} + +void EnWeatherTag_EnabledRainThunder(EnWeatherTag* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((WEATHER_TAG_RANGE100(this->actor.params) + 10.0f) < Actor_WorldDistXZToActor(&player->actor, &this->actor)) { + Environment_StopStormNatureAmbience(globalCtx); + globalCtx->envCtx.lightningMode = LIGHTNING_MODE_LAST; + globalCtx->envCtx.unk_EE[0] = 0; + globalCtx->envCtx.unk_EE[1] = 10; + EnWeatherTag_SetupAction(this, EnWeatherTag_DisabledRainThunder); + } +} + +void EnWeatherTag_Update(Actor* thisx, GlobalContext* globalCtx) { + EnWeatherTag* this = (EnWeatherTag*)thisx; + + this->actionFunc(this, globalCtx); + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 255, 0, 255, 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h b/soh/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h new file mode 100644 index 000000000..791bf67f9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Weather_Tag/z_en_weather_tag.h @@ -0,0 +1,28 @@ +#ifndef Z_EN_WEATHER_TAG_H +#define Z_EN_WEATHER_TAG_H + +#include "ultra64.h" +#include "global.h" + +struct EnWeatherTag; + +typedef void (*EnWeatherTagActionFunc)(struct EnWeatherTag*, GlobalContext*); + +typedef struct EnWeatherTag { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnWeatherTagActionFunc actionFunc; + /* 0x0150 */ char unk_150[0x04]; +} EnWeatherTag; // size = 0x0154 + +typedef enum { + /* 0x00 */ EN_WEATHER_TAG_TYPE_CLOUDY_MARKET, + /* 0x01 */ EN_WEATHER_TAG_TYPE_CLOUDY_LON_LON_RANCH, + /* 0x02 */ EN_WEATHER_TAG_TYPE_SNOW_ZORAS_DOMAIN, + /* 0x03 */ EN_WEATHER_TAG_TYPE_RAIN_LAKE_HYLIA, + /* 0x04 */ EN_WEATHER_TAG_TYPE_CLOUDY_DEATH_MOUNTAIN, + /* 0x05 */ EN_WEATHER_TAG_TYPE_THUNDERSTORM_KAKARIKO, + /* 0x06 */ EN_WEATHER_TAG_TYPE_SANDSTORM_INTENSITY, + /* 0x07 */ EN_WEATHER_TAG_TYPE_THUNDERSTORM_GRAVEYARD +} EnWeatherTagType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c b/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c new file mode 100644 index 000000000..3906f0449 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.c @@ -0,0 +1,650 @@ +/* + * File: z_en_weiyer.c + * Overlay: ovl_En_Weiyer + * Description: Stinger (Water) + */ + +#include "z_en_weiyer.h" +#include "objects/object_ei/object_ei.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2) + +void EnWeiyer_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWeiyer_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWeiyer_Update(Actor* thisx, GlobalContext* globalCtx); +void EnWeiyer_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B32804(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B328E8(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B32C2C(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B32D30(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B32E34(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B33018(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B331CC(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B333B8(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B332B4(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B33338(EnWeiyer* this, GlobalContext* globalCtx); +void func_80B3349C(EnWeiyer* this, GlobalContext* globalCtx); + +const ActorInit En_Weiyer_InitVars = { + ACTOR_EN_WEIYER, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_EI, + sizeof(EnWeiyer), + (ActorFunc)EnWeiyer_Init, + (ActorFunc)EnWeiyer_Destroy, + (ActorFunc)EnWeiyer_Update, + (ActorFunc)EnWeiyer_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 16, 10, -6, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 2, 45, 15, 100 }; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, 0x1), + /* Deku stick */ DMG_ENTRY(2, 0x0), + /* Slingshot */ DMG_ENTRY(1, 0x0), + /* Explosive */ DMG_ENTRY(2, 0x0), + /* Boomerang */ DMG_ENTRY(0, 0x1), + /* Normal arrow */ DMG_ENTRY(2, 0x0), + /* Hammer swing */ DMG_ENTRY(2, 0x0), + /* Hookshot */ DMG_ENTRY(2, 0x0), + /* Kokiri sword */ DMG_ENTRY(1, 0x0), + /* Master sword */ DMG_ENTRY(2, 0x0), + /* Giant's Knife */ DMG_ENTRY(4, 0x0), + /* Fire arrow */ DMG_ENTRY(2, 0x0), + /* Ice arrow */ DMG_ENTRY(2, 0x0), + /* Light arrow */ DMG_ENTRY(2, 0x0), + /* Unk arrow 1 */ DMG_ENTRY(2, 0x0), + /* Unk arrow 2 */ DMG_ENTRY(0, 0x0), + /* Unk arrow 3 */ DMG_ENTRY(0, 0x0), + /* Fire magic */ DMG_ENTRY(0, 0x0), + /* Ice magic */ DMG_ENTRY(0, 0x0), + /* Light magic */ DMG_ENTRY(0, 0x0), + /* Shield */ DMG_ENTRY(0, 0x0), + /* Mirror Ray */ DMG_ENTRY(0, 0x0), + /* Kokiri spin */ DMG_ENTRY(1, 0x0), + /* Giant spin */ DMG_ENTRY(4, 0x0), + /* Master spin */ DMG_ENTRY(2, 0x0), + /* Kokiri jump */ DMG_ENTRY(2, 0x0), + /* Giant jump */ DMG_ENTRY(8, 0x0), + /* Master jump */ DMG_ENTRY(4, 0x0), + /* Unknown 1 */ DMG_ENTRY(0, 0x0), + /* Unblockable */ DMG_ENTRY(0, 0x0), + /* Hammer jump */ DMG_ENTRY(4, 0x0), + /* Unknown 2 */ DMG_ENTRY(0, 0x0), +}; +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x19, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 3, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 2500, ICHAIN_STOP), +}; + +void EnWeiyer_Init(Actor* thisx, GlobalContext* globalCtx) { + EnWeiyer* this = (EnWeiyer*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + ActorShape_Init(&this->actor.shape, 1000.0f, ActorShadow_DrawCircle, 65.0f); + SkelAnime_Init(globalCtx, &this->skelAnime, &gStingerSkel, &gStingerIdleAnim, this->jointTable, this->morphTable, + 19); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo(&this->actor.colChkInfo, &sDamageTable, &sColChkInfoInit); + this->actionFunc = func_80B32804; +} + +void EnWeiyer_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnWeiyer* this = (EnWeiyer*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80B32384(EnWeiyer* this) { + this->unk_196 = this->actor.shape.rot.y; + this->unk_27C = (cosf(-M_PI / 8) * 3.0f) + this->actor.world.pos.y; + Animation_MorphToLoop(&this->skelAnime, &gStingerHitAnim, -5.0f); + this->unk_194 = 30; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 2.5f); + this->collider.base.atFlags &= ~AT_ON; + this->unk_280 = this->actor.floorHeight; + this->actionFunc = func_80B328E8; +} + +void func_80B32434(EnWeiyer* this) { + Animation_MorphToLoop(&this->skelAnime, &gStingerHitAnim, -5.0f); + this->collider.base.atFlags |= AT_ON; + this->unk_194 = 0; + this->actor.speedXZ = 5.0f; + this->actionFunc = func_80B32C2C; +} + +void func_80B32494(EnWeiyer* this) { + Animation_Change(&this->skelAnime, &gStingerPopOutAnim, 2.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f); + this->unk_194 = 40; + this->collider.base.atFlags |= AT_ON; + this->actionFunc = func_80B32D30; +} + +void func_80B32508(EnWeiyer* this) { + this->unk_194 = 200; + this->collider.base.atFlags |= AT_ON; + this->skelAnime.playSpeed = 3.0f; + this->actionFunc = func_80B32E34; +} + +void func_80B32538(EnWeiyer* this) { + this->unk_194 = 200; + this->unk_196 = this->actor.yawTowardsPlayer + 0x8000; + this->unk_27C = this->actor.world.pos.y; + this->actor.speedXZ = CLAMP_MAX(this->actor.speedXZ, 4.0f); + this->collider.base.atFlags &= ~AT_ON; + this->skelAnime.playSpeed = 1.0f; + this->actionFunc = func_80B33018; +} + +void func_80B325A0(EnWeiyer* this) { + Animation_Change(&this->skelAnime, &gStingerHitAnim, 2.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -3.0f); + this->unk_194 = 40; + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 3.0f; + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, 0x28); + this->collider.dim.height = sCylinderInit.dim.height; + this->actionFunc = func_80B331CC; +} + +void func_80B32660(EnWeiyer* this) { + Animation_Change(&this->skelAnime, &gStingerPopOutAnim, 2.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f); + this->unk_194 = 80; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.gravity = -1.0f; + this->collider.dim.height = sCylinderInit.dim.height + 15; + Actor_SetColorFilter(&this->actor, 0, 0xC8, 0, 0x50); + this->collider.base.atFlags &= ~AT_ON; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->actionFunc = func_80B333B8; +} + +void func_80B32724(EnWeiyer* this) { + Animation_MorphToLoop(&this->skelAnime, &gStingerHitAnim, -5.0f); + this->unk_194 = 20; + Actor_SetColorFilter(&this->actor, 0x4000, 0xC8, 0, 0x28); + this->collider.base.atFlags &= ~AT_ON; + this->collider.base.acFlags &= ~AC_ON; + this->actor.speedXZ = 3.0f; + this->actionFunc = func_80B332B4; +} + +void func_80B327B0(EnWeiyer* this) { + this->actor.colorFilterParams |= 0x2000; + this->actor.speedXZ = 0.0f; + this->actor.velocity.y = 0.0f; + this->actionFunc = func_80B33338; +} + +void func_80B327D8(EnWeiyer* this) { + this->actor.shape.rot.x = -0x2000; + this->unk_194 = -1; + this->actor.speedXZ = 5.0f; + this->actionFunc = func_80B3349C; +} + +void func_80B32804(EnWeiyer* this, GlobalContext* globalCtx) { + WaterBox* waterBox; + s32 bgId; + + this->actor.world.pos.y += 0.5f; + this->actor.floorHeight = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->actor.floorPoly, &bgId, + &this->actor, &this->actor.world.pos); + + if (!WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, + &this->actor.home.pos.y, &waterBox) || + ((this->actor.home.pos.y - 5.0f) <= this->actor.floorHeight)) { + Actor_Kill(&this->actor); + } else { + this->actor.home.pos.y -= 5.0f; + this->actor.world.pos.y = (this->actor.home.pos.y + this->actor.floorHeight) / 2.0f; + func_80B32384(this); + } +} + +void func_80B328E8(EnWeiyer* this, GlobalContext* globalCtx) { + s32 sp34; + f32 curFrame; + + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x800); + sp34 = Animation_OnFrame(&this->skelAnime, 0.0f); + curFrame = this->skelAnime.curFrame; + Math_StepToF(&this->unk_27C, this->unk_280, 0.5f); + this->actor.world.pos.y = this->unk_27C - cosf((curFrame - 5.0f) * (M_PI / 40)) * 3.0f; + + if (curFrame <= 45.0f) { + Math_StepToF(&this->actor.speedXZ, 1.0f, 0.03f); + } else { + Math_StepToF(&this->actor.speedXZ, 1.3f, 0.03f); + } + + if (this->actor.bgCheckFlags & 8) { + this->unk_196 = this->actor.wallYaw; + this->unk_194 = 30; + } + + if (Math_ScaledStepToS(&this->actor.shape.rot.y, this->unk_196, 182)) { + if (this->unk_194 != 0) { + this->unk_194--; + } + + if (this->unk_194 == 0) { + this->unk_196 = + Rand_S16Offset(0x2000, 0x2000) * ((Rand_ZeroOne() < 0.5f) ? -1 : 1) + this->actor.shape.rot.y; + this->unk_194 = 30; + + if (Rand_ZeroOne() < 0.3333f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_CRY); + } + } + } + + if (this->actor.home.pos.y < this->actor.world.pos.y) { + if (this->actor.home.pos.y < this->actor.floorHeight) { + func_80B32434(this); + } else { + this->actor.world.pos.y = this->actor.home.pos.y; + this->unk_280 = + Rand_ZeroOne() * ((this->actor.home.pos.y - this->actor.floorHeight) / 2.0f) + this->actor.floorHeight; + } + } else { + Player* player = GET_PLAYER(globalCtx); + + if (this->actor.bgCheckFlags & 1) { + this->unk_280 = + this->actor.home.pos.y - Rand_ZeroOne() * ((this->actor.home.pos.y - this->actor.floorHeight) / 2.0f); + } else if (sp34 && (Rand_ZeroOne() < 0.1f)) { + this->unk_280 = + Rand_ZeroOne() * (this->actor.home.pos.y - this->actor.floorHeight) + this->actor.floorHeight; + } + + if ((this->actor.xzDistToPlayer < 400.0f) && (fabsf(this->actor.yDistToPlayer) < 250.0f) && + (player->actor.world.pos.y < (this->actor.home.pos.y + 20.0f))) { + func_80B32508(this); + } + } +} + +void func_80B32C2C(EnWeiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->unk_194 == 0) { + if (Math_ScaledStepToS(&this->actor.shape.rot.x, -0x4000, 0x800)) { + this->actor.shape.rot.z = 0; + this->actor.shape.rot.y += 0x8000; + this->unk_194 = 1; + } else { + this->actor.shape.rot.z = this->actor.shape.rot.x * 2; + } + } else { + Math_ScaledStepToS(&this->actor.shape.rot.x, 0x1800, 0x800); + + if (this->actor.world.pos.y < this->actor.home.pos.y) { + if (this->actor.shape.rot.x > 0) { + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, NULL, NULL, 1, 400); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_SINK); + } + + func_80B32538(this); + } else if (this->actor.bgCheckFlags & 1) { + func_80B32494(this); + } + } +} + +void func_80B32D30(EnWeiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_FLUTTER); + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x800); + Math_StepToF(&this->actor.speedXZ, 0.0f, 1.0f); + + if (this->unk_194 != 0) { + this->unk_194--; + } + + if (this->unk_194 == 0) { + func_80B32434(this); + } else if (this->actor.world.pos.y < this->actor.home.pos.y) { + func_80B32384(this); + } +} + +s16 func_80B32DEC(EnWeiyer* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f vec; + + vec.x = player->actor.world.pos.x; + vec.y = player->actor.world.pos.y + 20.0f; + vec.z = player->actor.world.pos.z; + + return Actor_WorldPitchTowardPoint(&this->actor, &vec); +} + +void func_80B32E34(EnWeiyer* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + SkelAnime_Update(&this->skelAnime); + + if (this->unk_194 != 0) { + this->unk_194--; + } + + if ((this->unk_194 == 0) || ((this->actor.home.pos.y + 20.0f) <= player->actor.world.pos.y) || + (this->collider.base.atFlags & AT_HIT)) { + func_80B32538(this); + } else { + if (Actor_IsFacingPlayer(&this->actor, 0x2800)) { + Math_StepToF(&this->actor.speedXZ, 4.0f, 0.2f); + } else { + Math_StepToF(&this->actor.speedXZ, 1.3f, 0.2f); + } + + if (this->actor.home.pos.y < this->actor.world.pos.y) { + if (this->actor.home.pos.y < this->actor.floorHeight) { + this->actor.shape.rot.x = 0; + func_80B32434(this); + return; + } + + this->actor.world.pos.y = this->actor.home.pos.y; + Math_SmoothStepToS(&this->actor.shape.rot.x, 0x1000, 2, 0x100, 0x40); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.x, func_80B32DEC(this, globalCtx), 2, 0x100, 0x40); + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 2, 0x200, 0x80); + + if ((player->actor.yDistToWater < 50.0f) && (this->actor.yDistToWater < 20.0f) && + Actor_IsFacingPlayer(&this->actor, 0x2000)) { + func_80B327D8(this); + } + } +} + +void func_80B33018(EnWeiyer* this, GlobalContext* globalCtx) { + f32 curFrame; + + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x800); + curFrame = this->skelAnime.curFrame; + Math_StepToF(&this->unk_27C, (this->actor.home.pos.y - this->actor.floorHeight) / 4.0f + this->actor.floorHeight, + 1.0f); + this->actor.world.pos.y = this->unk_27C - cosf((curFrame - 5.0f) * (M_PI / 40)) * 3.0f; + + if (curFrame <= 45.0f) { + Math_StepToF(&this->actor.speedXZ, 1.0f, 0.03f); + } else { + Math_StepToF(&this->actor.speedXZ, 1.3f, 0.03f); + } + + if (this->unk_194 != 0) { + this->unk_194--; + } + + if (this->actor.bgCheckFlags & 8) { + this->unk_196 = this->actor.wallYaw; + } + + if (Math_SmoothStepToS(&this->actor.shape.rot.y, this->unk_196, 2, 0x200, 0x80) == 0) { + this->unk_196 = this->actor.yawTowardsPlayer + 0x8000; + } + + if (this->actor.home.pos.y < this->actor.world.pos.y) { + if (this->actor.home.pos.y < this->actor.floorHeight) { + func_80B32434(this); + } else { + this->actor.world.pos.y = this->actor.home.pos.y; + } + } + + if (this->unk_194 == 0) { + func_80B32384(this); + } +} + +void func_80B331CC(EnWeiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + + if (this->unk_194 != 0) { + this->unk_194--; + } + + if (this->actor.bgCheckFlags & 8) { + this->unk_196 = this->actor.wallYaw; + } else { + this->unk_196 = this->actor.yawTowardsPlayer + 0x8000; + } + + Math_ScaledStepToS(&this->actor.world.rot.y, this->unk_196, 0x38E); + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x200); + this->actor.shape.rot.z = sinf(this->unk_194 * (M_PI / 5)) * 5120.0f; + + if (this->unk_194 == 0) { + this->actor.shape.rot.z = 0; + this->collider.base.acFlags |= AC_ON; + func_80B32384(this); + } +} + +void func_80B332B4(EnWeiyer* this, GlobalContext* globalCtx) { + SkelAnime_Update(&this->skelAnime); + Math_ScaledStepToS(&this->actor.shape.rot.x, -0x4000, 0x400); + this->actor.shape.rot.z += 0x1000; + + if (this->unk_194 != 0) { + this->unk_194--; + } + + if ((this->unk_194 == 0) || (this->actor.bgCheckFlags & 0x10)) { + func_80B327B0(this); + } +} + +void func_80B33338(EnWeiyer* this, GlobalContext* globalCtx) { + this->actor.shape.shadowAlpha = CLAMP_MIN((s16)(this->actor.shape.shadowAlpha - 5), 0); + this->actor.world.pos.y -= 2.0f; + + if (this->actor.shape.shadowAlpha == 0) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xE0); + Actor_Kill(&this->actor); + } +} + +void func_80B333B8(EnWeiyer* this, GlobalContext* globalCtx) { + if (this->unk_194 != 0) { + this->unk_194--; + } + + Math_ScaledStepToS(&this->actor.shape.rot.x, 0, 0x200); + Math_ScaledStepToS(&this->actor.shape.rot.z, 0, 0x200); + SkelAnime_Update(&this->skelAnime); + + if (this->actor.home.pos.y < this->actor.floorHeight) { + if (Animation_OnFrame(&this->skelAnime, 0.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_FLUTTER); + } + + if (this->actor.bgCheckFlags & 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_M_GND); + } + } + + if (this->unk_194 == 0) { + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + this->collider.dim.height = sCylinderInit.dim.height; + func_80B32384(this); + } +} + +void func_80B3349C(EnWeiyer* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 phi_a1; + s32 phi_a0; + + SkelAnime_Update(&this->skelAnime); + + phi_a0 = ((this->actor.home.pos.y + 20.0f) <= player->actor.world.pos.y); + + if (this->unk_194 == -1) { + if (phi_a0 || (this->collider.base.atFlags & AT_HIT)) { + func_80B32538(this); + } else if (this->actor.yDistToWater < 0.0f) { + this->unk_194 = 10; + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, NULL, NULL, 1, 400); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_JUMP); + } + } else { + if (phi_a0 || (this->collider.base.atFlags & AT_HIT)) { + this->unk_194 = 0; + } else if (this->unk_194 != 0) { + this->unk_194--; + } + + if (this->unk_194 == 0) { + phi_a1 = 0x1800; + } else { + phi_a1 = func_80B32DEC(this, globalCtx); + phi_a1 = CLAMP_MIN(phi_a1, 0); + } + + if (this->actor.shape.rot.x < phi_a1) { + Math_ScaledStepToS(&this->actor.shape.rot.x, phi_a1, 0x400); + } + + if (this->actor.bgCheckFlags & 1) { + func_80B32434(this); + } else if ((this->actor.bgCheckFlags & 0x20) && (this->actor.shape.rot.x > 0)) { + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, NULL, NULL, 1, 400); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_OCTAROCK_SINK); + func_80B32538(this); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 8, 0x100, 0x80); + } + } +} + +void func_80B3368C(EnWeiyer* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Actor_SetDropFlag(&this->actor, &this->collider.info, 1); + + if ((this->actor.colChkInfo.damageEffect != 0) || (this->actor.colChkInfo.damage != 0)) { + if (this->actor.colChkInfo.damageEffect == 1) { + if (this->actionFunc != func_80B333B8) { + func_80B32660(this); + } + } else if (Actor_ApplyDamage(&this->actor) == 0) { + Enemy_StartFinishingBlow(globalCtx, &this->actor); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_DEAD); + this->actor.flags &= ~ACTOR_FLAG_0; + func_80B32724(this); + } else { + func_80B325A0(this); + } + } + } +} + +void EnWeiyer_Update(Actor* thisx, GlobalContext* globalCtx) { + EnWeiyer* this = (EnWeiyer*)thisx; + s32 pad; + + this->actor.home.pos.y = this->actor.yDistToWater + this->actor.world.pos.y - 5.0f; + func_80B3368C(this, globalCtx); + this->actionFunc(this, globalCtx); + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actor.world.rot.x = -this->actor.shape.rot.x; + + if ((this->actor.world.rot.x == 0) || (this->actionFunc == func_80B333B8)) { + Actor_MoveForward(&this->actor); + } else { + func_8002D97C(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 30.0f, 45.0f, 7); + Actor_SetFocus(&this->actor, 0.0f); + + if (this->collider.base.atFlags & AT_HIT) { + this->collider.base.atFlags &= ~(AT_ON | AT_HIT); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_EIER_ATTACK); + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + + if (this->collider.base.atFlags & AT_ON) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->collider.base.acFlags & AT_ON) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnWeiyer_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + if (limbIndex == 1) { + pos->z += 2000.0f; + } + + return 0; +} + +void EnWeiyer_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnWeiyer* this = (EnWeiyer*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_weiyer.c", 1193); + + if (this->actionFunc != func_80B33338) { + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, &D_80116280[2]); + gDPSetEnvColor(POLY_OPA_DISP++, 255, 255, 255, 255); + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnWeiyer_OverrideLimbDraw, NULL, &this->actor, POLY_OPA_DISP); + } else { + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, &D_80116280[0]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 255, this->actor.shape.shadowAlpha); + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnWeiyer_OverrideLimbDraw, NULL, &this->actor, POLY_XLU_DISP); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_weiyer.c", 1240); +} diff --git a/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.h b/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.h new file mode 100644 index 000000000..77874b31e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Weiyer/z_en_weiyer.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_WEIYER_H +#define Z_EN_WEIYER_H + +#include "ultra64.h" +#include "global.h" + +struct EnWeiyer; + +typedef void (*EnWeiyerActionFunc)(struct EnWeiyer*, GlobalContext*); + +typedef struct EnWeiyer { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnWeiyerActionFunc actionFunc; + /* 0x0194 */ s16 unk_194; + /* 0x0196 */ s16 unk_196; + /* 0x0198 */ Vec3s jointTable[19]; + /* 0x020A */ Vec3s morphTable[19]; + /* 0x027C */ f32 unk_27C; + /* 0x0280 */ f32 unk_280; + /* 0x0284 */ ColliderCylinder collider; +} EnWeiyer; // size = 0x02D0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c b/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c new file mode 100644 index 000000000..0da5d87bd --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.c @@ -0,0 +1,1492 @@ +/* + * File: z_en_wf.c + * Overlay: ovl_En_Wf + * Description: Wolfos (Normal and White) + */ + +#include "z_en_wf.h" +#include "vt.h" +#include "overlays/actors/ovl_En_Encount1/z_en_encount1.h" +#include "objects/object_wf/object_wf.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnWf_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWf_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWf_Update(Actor* thisx, GlobalContext* globalCtx); +void EnWf_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnWf_SetupWaitToAppear(EnWf* this); +void EnWf_WaitToAppear(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupWait(EnWf* this); +void EnWf_Wait(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupRunAtPlayer(EnWf* this, GlobalContext* globalCtx); +void EnWf_RunAtPlayer(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupSearchForPlayer(EnWf* this); +void EnWf_SearchForPlayer(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupRunAroundPlayer(EnWf* this); +void EnWf_RunAroundPlayer(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupSlash(EnWf* this); +void EnWf_Slash(EnWf* this, GlobalContext* globalCtx); +void EnWf_RecoilFromBlockedSlash(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupBackflipAway(EnWf* this); +void EnWf_BackflipAway(EnWf* this, GlobalContext* globalCtx); +void EnWf_Stunned(EnWf* this, GlobalContext* globalCtx); +void EnWf_Damaged(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupSomersaultAndAttack(EnWf* this); +void EnWf_SomersaultAndAttack(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupBlocking(EnWf* this); +void EnWf_Blocking(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupSidestep(EnWf* this, GlobalContext* globalCtx); +void EnWf_Sidestep(EnWf* this, GlobalContext* globalCtx); +void EnWf_SetupDie(EnWf* this); +void EnWf_Die(EnWf* this, GlobalContext* globalCtx); +s32 EnWf_DodgeRanged(GlobalContext* globalCtx, EnWf* this); + +static ColliderJntSphElementInit sJntSphItemsInit[4] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { WOLFOS_LIMB_FRONT_RIGHT_CLAW, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { WOLFOS_LIMB_FRONT_LEFT_CLAW, { { 0, 0, 0 }, 15 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC1FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { WOLFOS_LIMB_HEAD, { { 800, 0, 0 }, 25 }, 100 }, + }, + { + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFC1FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { WOLFOS_LIMB_THORAX, { { 0, 0, 0 }, 30 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + ARRAY_COUNT(sJntSphItemsInit), + sJntSphItemsInit, +}; + +static ColliderCylinderInit sBodyCylinderInit = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 20, 50, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sTailCylinderInit = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 15, 20, -15, { 0, 0, 0 } }, +}; + +typedef enum { + /* 0 */ ENWF_DMGEFF_NONE, + /* 1 */ ENWF_DMGEFF_STUN, + /* 6 */ ENWF_DMGEFF_ICE_MAGIC = 6, + /* 13 */ ENWF_DMGEFF_LIGHT_MAGIC = 13, + /* 14 */ ENWF_DMGEFF_FIRE, + /* 15 */ ENWF_DMGEFF_UNDEF // used like STUN in the code, but not in the table +} EnWfDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, ENWF_DMGEFF_STUN), + /* Deku stick */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(1, ENWF_DMGEFF_NONE), + /* Explosive */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(0, ENWF_DMGEFF_STUN), + /* Normal arrow */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(0, ENWF_DMGEFF_STUN), + /* Kokiri sword */ DMG_ENTRY(1, ENWF_DMGEFF_NONE), + /* Master sword */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Giant's Knife */ DMG_ENTRY(4, ENWF_DMGEFF_NONE), + /* Fire arrow */ DMG_ENTRY(4, ENWF_DMGEFF_FIRE), + /* Ice arrow */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Light arrow */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Unk arrow 1 */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Unk arrow 2 */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Unk arrow 3 */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Fire magic */ DMG_ENTRY(4, ENWF_DMGEFF_FIRE), + /* Ice magic */ DMG_ENTRY(0, ENWF_DMGEFF_ICE_MAGIC), + /* Light magic */ DMG_ENTRY(3, ENWF_DMGEFF_LIGHT_MAGIC), + /* Shield */ DMG_ENTRY(0, ENWF_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, ENWF_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, ENWF_DMGEFF_NONE), + /* Giant spin */ DMG_ENTRY(4, ENWF_DMGEFF_NONE), + /* Master spin */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Kokiri jump */ DMG_ENTRY(2, ENWF_DMGEFF_NONE), + /* Giant jump */ DMG_ENTRY(8, ENWF_DMGEFF_NONE), + /* Master jump */ DMG_ENTRY(4, ENWF_DMGEFF_NONE), + /* Unknown 1 */ DMG_ENTRY(0, ENWF_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, ENWF_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, ENWF_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, ENWF_DMGEFF_NONE), +}; + +const ActorInit En_Wf_InitVars = { + ACTOR_EN_WF, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_WF, + sizeof(EnWf), + (ActorFunc)EnWf_Init, + (ActorFunc)EnWf_Destroy, + (ActorFunc)EnWf_Update, + (ActorFunc)EnWf_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -3000, ICHAIN_STOP), +}; + +void EnWf_SetupAction(EnWf* this, EnWfActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void EnWf_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnWf* this = (EnWf*)thisx; + + Actor_ProcessInitChain(thisx, sInitChain); + thisx->colChkInfo.damageTable = &sDamageTable; + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawCircle, 0.0f); + thisx->focus.pos = thisx->world.pos; + thisx->colChkInfo.mass = MASS_HEAVY; + thisx->colChkInfo.health = 8; + thisx->colChkInfo.cylRadius = 50; + thisx->colChkInfo.cylHeight = 100; + this->switchFlag = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + this->eyeIndex = 0; + this->unk_2F4 = 10.0f; // Set and not used + + Collider_InitJntSph(globalCtx, &this->colliderSpheres); + Collider_SetJntSph(globalCtx, &this->colliderSpheres, thisx, &sJntSphInit, this->colliderSpheresElements); + Collider_InitCylinder(globalCtx, &this->colliderCylinderBody); + Collider_SetCylinder(globalCtx, &this->colliderCylinderBody, thisx, &sBodyCylinderInit); + Collider_InitCylinder(globalCtx, &this->colliderCylinderTail); + Collider_SetCylinder(globalCtx, &this->colliderCylinderTail, thisx, &sTailCylinderInit); + + if (thisx->params == WOLFOS_NORMAL) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gWolfosNormalSkel, &gWolfosWaitingAnim, this->jointTable, + this->morphTable, WOLFOS_LIMB_MAX); + Actor_SetScale(thisx, 0.0075f); + thisx->naviEnemyId = 0x4C; // Wolfos + } else { // WOLFOS_WHITE + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gWolfosWhiteSkel, &gWolfosWaitingAnim, this->jointTable, + this->morphTable, WOLFOS_LIMB_MAX); + Actor_SetScale(thisx, 0.01f); + this->colliderSpheres.elements[0].info.toucher.damage = this->colliderSpheres.elements[1].info.toucher.damage = + 8; + thisx->naviEnemyId = 0x57; // White Wolfos + } + + EnWf_SetupWaitToAppear(this); + + if ((this->switchFlag != 0xFF) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + Actor_Kill(thisx); + } +} + +void EnWf_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnWf* this = (EnWf*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->colliderSpheres); + Collider_DestroyCylinder(globalCtx, &this->colliderCylinderBody); + Collider_DestroyCylinder(globalCtx, &this->colliderCylinderTail); + + if ((this->actor.params != WOLFOS_NORMAL) && (this->switchFlag != 0xFF)) { + func_800F5B58(); + } + + if (this->actor.parent != NULL) { + EnEncount1* parent = (EnEncount1*)this->actor.parent; + + if (parent->actor.update != NULL) { + if (parent->curNumSpawn > 0) { + parent->curNumSpawn--; + } + + osSyncPrintf("\n\n"); + // "☆☆☆☆☆ Number of concurrent events ☆☆☆☆☆" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 同時発生数 ☆☆☆☆☆%d\n" VT_RST, parent->curNumSpawn); + osSyncPrintf("\n\n"); + } + } +} + +s32 EnWf_ChangeAction(GlobalContext* globalCtx, EnWf* this, s16 mustChoose) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 wallYawDiff; + s16 playerYawDiff; + Actor* explosive; + + wallYawDiff = this->actor.wallYaw - this->actor.shape.rot.y; + wallYawDiff = ABS(wallYawDiff); + playerYawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + playerYawDiff = ABS(playerYawDiff); + + if (func_800354B4(globalCtx, &this->actor, 100.0f, 0x2710, 0x2EE0, this->actor.shape.rot.y)) { + if (player->swordAnimation == 0x11) { + EnWf_SetupBlocking(this); + return true; + } + + if ((globalCtx->gameplayFrames % 2) != 0) { + EnWf_SetupBlocking(this); + return true; + } + } + + if (func_800354B4(globalCtx, &this->actor, 100.0f, 0x5DC0, 0x2AA8, this->actor.shape.rot.y)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if ((this->actor.bgCheckFlags & 8) && (ABS(wallYawDiff) < 0x2EE0) && (this->actor.xzDistToPlayer < 120.0f)) { + EnWf_SetupSomersaultAndAttack(this); + return true; + } else if (player->swordAnimation == 0x11) { + EnWf_SetupBlocking(this); + return true; + } else if ((this->actor.xzDistToPlayer < 80.0f) && (globalCtx->gameplayFrames % 2) != 0) { + EnWf_SetupBlocking(this); + return true; + } else { + EnWf_SetupBackflipAway(this); + return true; + } + } + + explosive = Actor_FindNearby(globalCtx, &this->actor, -1, ACTORCAT_EXPLOSIVE, 80.0f); + + if (explosive != NULL) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (((this->actor.bgCheckFlags & 8) && (wallYawDiff < 0x2EE0)) || (explosive->id == ACTOR_EN_BOM_CHU)) { + if ((explosive->id == ACTOR_EN_BOM_CHU) && (Actor_WorldDistXYZToActor(&this->actor, explosive) < 80.0f) && + (s16)((this->actor.shape.rot.y - explosive->world.rot.y) + 0x8000) < 0x3E80) { + EnWf_SetupSomersaultAndAttack(this); + return true; + } else { + EnWf_SetupSidestep(this, globalCtx); + return true; + } + } else { + EnWf_SetupBackflipAway(this); + return true; + } + } + + if (mustChoose) { + s16 playerFacingAngleDiff; + + if (playerYawDiff >= 0x1B58) { + EnWf_SetupSidestep(this, globalCtx); + return true; + } + + playerFacingAngleDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if ((this->actor.xzDistToPlayer <= 80.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) && + (((globalCtx->gameplayFrames % 8) != 0) || (ABS(playerFacingAngleDiff) < 0x38E0))) { + EnWf_SetupSlash(this); + return true; + } + + EnWf_SetupRunAroundPlayer(this); + return true; + } + return false; +} + +void EnWf_SetupWaitToAppear(EnWf* this) { + Animation_Change(&this->skelAnime, &gWolfosRearingUpFallingOverAnim, 0.5f, 0.0f, 7.0f, ANIMMODE_ONCE_INTERP, 0.0f); + this->actor.world.pos.y = this->actor.home.pos.y - 5.0f; + this->actionTimer = 20; + this->unk_300 = false; + this->action = WOLFOS_ACTION_WAIT_TO_APPEAR; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.scale.y = 0.0f; + this->actor.gravity = 0.0f; + EnWf_SetupAction(this, EnWf_WaitToAppear); +} + +void EnWf_WaitToAppear(EnWf* this, GlobalContext* globalCtx) { + if (this->actionTimer >= 6) { + this->actor.world.pos.y = this->actor.home.pos.y - 5.0f; + + if (this->actor.xzDistToPlayer < 240.0f) { + this->actionTimer = 5; + this->actor.flags |= ACTOR_FLAG_0; + + if ((this->actor.params != WOLFOS_NORMAL) && (this->switchFlag != 0xFF)) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + } + } else if (this->actionTimer != 0) { + this->actor.scale.y += this->actor.scale.x * 0.2f; + this->actor.world.pos.y += 0.5f; + Math_SmoothStepToF(&this->actor.shape.shadowScale, 70.0f, 1.0f, 14.0f, 0.0f); + this->actionTimer--; + + if (this->actionTimer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_APPEAR); + } + } else { // actionTimer == 0 + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.scale.y = this->actor.scale.x; + this->actor.gravity = -2.0f; + EnWf_SetupWait(this); + } + } +} + +void EnWf_SetupWait(EnWf* this) { + Animation_MorphToLoop(&this->skelAnime, &gWolfosWaitingAnim, -4.0f); + this->action = WOLFOS_ACTION_WAIT; + this->actionTimer = (Rand_ZeroOne() * 10.0f) + 2.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnWf_SetupAction(this, EnWf_Wait); +} + +void EnWf_Wait(EnWf* this, GlobalContext* globalCtx) { + Player* player; + s32 pad; + s16 angle; + + player = GET_PLAYER(globalCtx); + SkelAnime_Update(&this->skelAnime); + + if (this->unk_2E2 != 0) { + angle = (this->actor.yawTowardsPlayer - this->actor.shape.rot.y) - this->unk_4D4.y; + + if (ABS(angle) > 0x2000) { + this->unk_2E2--; + return; + } + + this->unk_2E2 = 0; + } + + angle = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + angle = ABS(angle); + + if (!EnWf_DodgeRanged(globalCtx, this)) { + // Only use of unk_2E0: never not zero, so this if block never runs + if (this->unk_2E0 != 0) { + this->unk_2E0--; + + if (angle >= 0x1FFE) { + return; + } + this->unk_2E0 = 0; + } else { + if (EnWf_ChangeAction(globalCtx, this, false)) { + return; + } + } + + angle = player->actor.shape.rot.y - this->actor.shape.rot.y; + angle = ABS(angle); + + if ((this->actor.xzDistToPlayer < 80.0f) && (player->swordState != 0) && (angle >= 0x1F40)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnWf_SetupRunAroundPlayer(this); + } else { + this->actionTimer--; + + if (this->actionTimer == 0) { + if (Actor_IsFacingPlayer(&this->actor, 0x1555)) { + if (Rand_ZeroOne() > 0.3f) { + EnWf_SetupRunAtPlayer(this, globalCtx); + } else { + EnWf_SetupRunAroundPlayer(this); + } + } else { + EnWf_SetupSearchForPlayer(this); + } + if ((globalCtx->gameplayFrames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY); + } + } + } + } +} + +void EnWf_SetupRunAtPlayer(EnWf* this, GlobalContext* globalCtx) { + f32 lastFrame = Animation_GetLastFrame(&gWolfosRunningAnim); + + Animation_Change(&this->skelAnime, &gWolfosRunningAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f); + this->action = WOLFOS_ACTION_RUN_AT_PLAYER; + EnWf_SetupAction(this, EnWf_RunAtPlayer); +} + +void EnWf_RunAtPlayer(EnWf* this, GlobalContext* globalCtx) { + s32 animPrevFrame; + s32 sp58; + s32 pad; + f32 baseRange = 0.0f; + s16 playerFacingAngleDiff; + Player* player = GET_PLAYER(globalCtx); + s32 playSpeed; + + if (!EnWf_DodgeRanged(globalCtx, this)) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0x2EE, 0); + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + baseRange = 150.0f; + } + + if (this->actor.xzDistToPlayer <= (50.0f + baseRange)) { + Math_SmoothStepToF(&this->actor.speedXZ, -8.0f, 1.0f, 1.5f, 0.0f); + } else if ((65.0f + baseRange) < this->actor.xzDistToPlayer) { + Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 6.65f, 0.0f); + } + + this->skelAnime.playSpeed = this->actor.speedXZ * 0.175f; + playerFacingAngleDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + playerFacingAngleDiff = ABS(playerFacingAngleDiff); + + if ((this->actor.xzDistToPlayer < (150.0f + baseRange)) && (player->swordState != 0) && + (playerFacingAngleDiff >= 8000)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (Rand_ZeroOne() > 0.7f) { + EnWf_SetupRunAroundPlayer(this); + return; + } + } + + animPrevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + sp58 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + playSpeed = (f32)ABS(this->skelAnime.playSpeed); + + if (!Actor_IsFacingPlayer(&this->actor, 0x11C7)) { + if (Rand_ZeroOne() > 0.5f) { + EnWf_SetupRunAroundPlayer(this); + } else { + EnWf_SetupWait(this); + } + } else if (this->actor.xzDistToPlayer < (90.0f + baseRange)) { + s16 temp_v1 = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && + ((Rand_ZeroOne() > 0.03f) || ((this->actor.xzDistToPlayer <= 80.0f) && (ABS(temp_v1) < 0x38E0)))) { + EnWf_SetupSlash(this); + } else if (Actor_OtherIsTargeted(globalCtx, &this->actor) && (Rand_ZeroOne() > 0.5f)) { + EnWf_SetupBackflipAway(this); + } else { + EnWf_SetupRunAroundPlayer(this); + } + } + + if (!EnWf_ChangeAction(globalCtx, this, false)) { + if ((globalCtx->gameplayFrames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY); + } + if ((animPrevFrame != (s32)this->skelAnime.curFrame) && (sp58 <= 0) && ((playSpeed + animPrevFrame) > 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_WALK); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 3, 3.0f, 50, 50, 1); + } + } + } +} + +void EnWf_SetupSearchForPlayer(EnWf* this) { + Animation_MorphToLoop(&this->skelAnime, &gWolfosSidesteppingAnim, -4.0f); + this->action = WOLFOS_ACTION_SEARCH_FOR_PLAYER; + EnWf_SetupAction(this, EnWf_SearchForPlayer); +} + +void EnWf_SearchForPlayer(EnWf* this, GlobalContext* globalCtx) { + s16 yawDiff; + s16 phi_v1; + f32 phi_f2; + + if (!EnWf_DodgeRanged(globalCtx, this) && !EnWf_ChangeAction(globalCtx, this, false)) { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + phi_v1 = (yawDiff > 0) ? (yawDiff * 0.25f) + 2000.0f : (yawDiff * 0.25f) - 2000.0f; + this->actor.shape.rot.y += phi_v1; + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (yawDiff > 0) { + phi_f2 = phi_v1 * 0.5f; + phi_f2 = CLAMP_MAX(phi_f2, 1.0f); + } else { + phi_f2 = phi_v1 * 0.5f; + phi_f2 = CLAMP_MIN(phi_f2, -1.0f); + } + + this->skelAnime.playSpeed = -phi_f2; + SkelAnime_Update(&this->skelAnime); + + if (Actor_IsFacingPlayer(&this->actor, 0x1555)) { + if (Rand_ZeroOne() > 0.8f) { + EnWf_SetupRunAroundPlayer(this); + } else { + EnWf_SetupRunAtPlayer(this, globalCtx); + } + } + + if ((globalCtx->gameplayFrames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY); + } + } +} + +void EnWf_SetupRunAroundPlayer(EnWf* this) { + f32 lastFrame = Animation_GetLastFrame(&gWolfosRunningAnim); + + Animation_Change(&this->skelAnime, &gWolfosRunningAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f); + + if (Rand_ZeroOne() > 0.5f) { + this->runAngle = 16000; + } else { + this->runAngle = -16000; + } + + this->skelAnime.playSpeed = this->actor.speedXZ = 6.0f; + this->skelAnime.playSpeed *= 0.175f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->actionTimer = (Rand_ZeroOne() * 30.0f) + 30.0f; + this->action = WOLFOS_ACTION_RUN_AROUND_PLAYER; + this->runSpeed = 0.0f; + + EnWf_SetupAction(this, EnWf_RunAroundPlayer); +} + +void EnWf_RunAroundPlayer(EnWf* this, GlobalContext* globalCtx) { + s16 angle1; + s16 angle2; + s32 pad; + f32 baseRange = 0.0f; + s32 animPrevFrame; + s32 animFrameSpeedDiff; + s32 animSpeed; + Player* player = GET_PLAYER(globalCtx); + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + this->runAngle, 1, 4000, 1); + + if (!EnWf_DodgeRanged(globalCtx, this) && !EnWf_ChangeAction(globalCtx, this, false)) { + this->actor.world.rot.y = this->actor.shape.rot.y; + angle1 = player->actor.shape.rot.y + this->runAngle + 0x8000; + + // Actor_TestFloorInDirection is useless here (see comment below) + if ((this->actor.bgCheckFlags & 8) || + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y)) { + angle2 = (this->actor.bgCheckFlags & 8) + ? (this->actor.wallYaw - this->actor.yawTowardsPlayer) - this->runAngle + : 0; + + // This is probably meant to reverse direction if the edge of a floor is encountered, but does nothing + // unless bgCheckFlags & 8 anyway, since angle2 = 0 otherwise + if (ABS(angle2) > 0x2EE0) { + this->runAngle = -this->runAngle; + } + } + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + baseRange = 150.0f; + } + + if (this->actor.xzDistToPlayer <= (60.0f + baseRange)) { + Math_SmoothStepToF(&this->runSpeed, -4.0f, 1.0f, 1.5f, 0.0f); + } else if ((80.0f + baseRange) < this->actor.xzDistToPlayer) { + Math_SmoothStepToF(&this->runSpeed, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->runSpeed, 0.0f, 1.0f, 6.65f, 0.0f); + } + + if (this->runSpeed != 0.0f) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->runSpeed; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->runSpeed; + } + + if (ABS(this->runSpeed) < ABS(this->actor.speedXZ)) { + this->skelAnime.playSpeed = this->actor.speedXZ * 0.175f; + } else { + this->skelAnime.playSpeed = this->runSpeed * 0.175f; + } + + this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f); + animPrevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + animFrameSpeedDiff = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + animSpeed = (f32)ABS(this->skelAnime.playSpeed); + + if ((animPrevFrame != (s32)this->skelAnime.curFrame) && (animFrameSpeedDiff <= 0) && + (animSpeed + animPrevFrame > 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_WALK); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 3, 3.0f, 50, 50, 1); + } + + if ((globalCtx->gameplayFrames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY); + } + + if ((Math_CosS(angle1 - this->actor.shape.rot.y) < -0.85f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) && + (this->actor.xzDistToPlayer <= 80.0f)) { + EnWf_SetupSlash(this); + } else { + this->actionTimer--; + + if (this->actionTimer == 0) { + if (Actor_OtherIsTargeted(globalCtx, &this->actor) && (Rand_ZeroOne() > 0.5f)) { + EnWf_SetupBackflipAway(this); + } else { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 3.0f) + 1.0f; + } + } + } + } +} + +void EnWf_SetupSlash(EnWf* this) { + Animation_PlayOnce(&this->skelAnime, &gWolfosSlashingAnim); + this->colliderSpheres.base.atFlags &= ~AT_BOUNCED; + this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->action = WOLFOS_ACTION_SLASH; + this->unk_2FA = 0; // Set and not used + this->actionTimer = 7; + this->skelAnime.endFrame = 20.0f; + this->actor.speedXZ = 0.0f; + + EnWf_SetupAction(this, EnWf_Slash); +} + +void EnWf_Slash(EnWf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 shapeAngleDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + s16 yawAngleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + s32 curFrame = this->skelAnime.curFrame; + + shapeAngleDiff = ABS(shapeAngleDiff); + yawAngleDiff = ABS(yawAngleDiff); + this->actor.speedXZ = 0.0f; + + if (((curFrame >= 9) && (curFrame <= 12)) || ((curFrame >= 17) && (curFrame <= 19))) { + if (this->slashStatus == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_ATTACK); + } + + this->slashStatus = 1; + } else { + this->slashStatus = 0; + } + + if (((curFrame == 15) && !Actor_IsTargeted(globalCtx, &this->actor) && + (!Actor_IsFacingPlayer(&this->actor, 0x2000) || (this->actor.xzDistToPlayer >= 100.0f))) || + SkelAnime_Update(&this->skelAnime)) { + if ((curFrame != 15) && (this->actionTimer != 0)) { + this->actor.shape.rot.y += (s16)(3276.0f * (1.5f + (this->actionTimer - 4) * 0.4f)); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 15.0f, 1, 2.0f, 50, 50, 1); + this->actionTimer--; + } else if (!Actor_IsFacingPlayer(&this->actor, 0x1554) && (curFrame != 15)) { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f; + + if (yawAngleDiff > 13000) { + this->unk_2E2 = 7; + } + } else if ((Rand_ZeroOne() > 0.7f) || (this->actor.xzDistToPlayer >= 120.0f)) { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } else { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (Rand_ZeroOne() > 0.7f) { + EnWf_SetupSidestep(this, globalCtx); + } else if (shapeAngleDiff <= 10000) { + if (yawAngleDiff > 16000) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnWf_SetupRunAroundPlayer(this); + } else { + EnWf_ChangeAction(globalCtx, this, true); + } + } else { + EnWf_SetupRunAroundPlayer(this); + } + } + } +} + +void EnWf_SetupRecoilFromBlockedSlash(EnWf* this) { + f32 endFrame = 1.0f; + + if ((s32)this->skelAnime.curFrame >= 16) { + endFrame = 15.0f; + } + + Animation_Change(&this->skelAnime, &gWolfosSlashingAnim, -0.5f, this->skelAnime.curFrame - 1.0f, endFrame, + ANIMMODE_ONCE_INTERP, 0.0f); + this->action = WOLFOS_ACTION_RECOIL_FROM_BLOCKED_SLASH; + this->slashStatus = 0; + EnWf_SetupAction(this, EnWf_RecoilFromBlockedSlash); +} + +void EnWf_RecoilFromBlockedSlash(EnWf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 angle1 = player->actor.shape.rot.y - this->actor.shape.rot.y; + s16 angle2 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + angle1 = ABS(angle1); + angle2 = ABS(angle2); + + if (SkelAnime_Update(&this->skelAnime)) { + if (!Actor_IsFacingPlayer(&this->actor, 0x1554)) { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f; + + if (angle2 > 0x32C8) { + this->unk_2E2 = 30; + } + } else { + if ((Rand_ZeroOne() > 0.7f) || (this->actor.xzDistToPlayer >= 120.0f)) { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f; + } else { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (Rand_ZeroOne() > 0.7f) { + EnWf_SetupSidestep(this, globalCtx); + } else if (angle1 <= 0x2710) { + if (angle2 > 0x3E80) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnWf_SetupRunAroundPlayer(this); + } else { + EnWf_ChangeAction(globalCtx, this, true); + } + } else { + EnWf_SetupRunAroundPlayer(this); + } + } + } + } +} + +void EnWf_SetupBackflipAway(EnWf* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWolfosBackflippingAnim, -3.0f); + this->actor.speedXZ = -6.0f; + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actionTimer = 0; + this->unk_300 = true; + this->action = WOLFOS_ACTION_BACKFLIP_AWAY; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + EnWf_SetupAction(this, EnWf_BackflipAway); +} + +void EnWf_BackflipAway(EnWf* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && (this->actor.xzDistToPlayer < 170.0f) && + (this->actor.xzDistToPlayer > 140.0f) && (Rand_ZeroOne() < 0.2f)) { + EnWf_SetupRunAtPlayer(this, globalCtx); + } else if ((globalCtx->gameplayFrames % 2) != 0) { + EnWf_SetupSidestep(this, globalCtx); + } else { + EnWf_SetupWait(this); + } + } + if ((globalCtx->state.frames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY); + } +} + +void EnWf_SetupStunned(EnWf* this) { + if (this->actor.bgCheckFlags & 1) { + this->actor.speedXZ = 0.0f; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + Animation_PlayOnceSetSpeed(&this->skelAnime, &gWolfosDamagedAnim, 0.0f); + this->action = WOLFOS_ACTION_STUNNED; + EnWf_SetupAction(this, EnWf_Stunned); +} + +void EnWf_Stunned(EnWf* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + + this->unk_300 = false; + } + + if ((this->actor.colorFilterTimer == 0) && (this->actor.bgCheckFlags & 1)) { + if (this->actor.colChkInfo.health == 0) { + EnWf_SetupDie(this); + } else { + EnWf_ChangeAction(globalCtx, this, true); + } + } +} + +void EnWf_SetupDamaged(EnWf* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWolfosDamagedAnim, -4.0f); + + if (this->actor.bgCheckFlags & 1) { + this->unk_300 = false; + this->actor.speedXZ = -4.0f; + } else { + this->unk_300 = true; + } + + this->unk_2E2 = 0; + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_DAMAGE); + this->action = WOLFOS_ACTION_DAMAGED; + EnWf_SetupAction(this, EnWf_Damaged); +} + +void EnWf_Damaged(EnWf* this, GlobalContext* globalCtx) { + s16 angleToWall; + + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + + this->unk_300 = false; + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4500, 0); + + if (!EnWf_ChangeAction(globalCtx, this, false) && SkelAnime_Update(&this->skelAnime)) { + if (this->actor.bgCheckFlags & 1) { + angleToWall = this->actor.wallYaw - this->actor.shape.rot.y; + angleToWall = ABS(angleToWall); + + if ((this->actor.bgCheckFlags & 8) && (ABS(angleToWall) < 12000) && (this->actor.xzDistToPlayer < 120.0f)) { + EnWf_SetupSomersaultAndAttack(this); + } else if (!EnWf_DodgeRanged(globalCtx, this)) { + if ((this->actor.xzDistToPlayer <= 80.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) && + ((globalCtx->gameplayFrames % 8) != 0)) { + EnWf_SetupSlash(this); + } else if (Rand_ZeroOne() > 0.5f) { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 5.0f) + 5.0f; + this->unk_2E2 = 30; + } else { + EnWf_SetupBackflipAway(this); + } + } + } + } +} + +void EnWf_SetupSomersaultAndAttack(EnWf* this) { + f32 lastFrame = Animation_GetLastFrame(&gWolfosBackflippingAnim); + + Animation_Change(&this->skelAnime, &gWolfosBackflippingAnim, -1.0f, lastFrame, 0.0f, ANIMMODE_ONCE, -3.0f); + this->actionTimer = 0; + this->unk_300 = false; + this->action = WOLFOS_ACTION_TURN_TOWARDS_PLAYER; + this->actor.speedXZ = 6.5f; + this->actor.velocity.y = 15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_STAL_JUMP); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnWf_SetupAction(this, EnWf_SomersaultAndAttack); +} + +void EnWf_SomersaultAndAttack(EnWf* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); + + if (this->actor.velocity.y >= 5.0f) { + //! @bug unk_4C8 and unk_4BC are used but not set (presumably intended to be feet positions like other actors) + func_800355B8(globalCtx, &this->unk_4C8); + func_800355B8(globalCtx, &this->unk_4BC); + } + + if (SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & (1 | 2))) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.shape.rot.x = 0; + this->actor.speedXZ = this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = this->actor.floorHeight; + + if (!Actor_OtherIsTargeted(globalCtx, &this->actor)) { + EnWf_SetupSlash(this); + } else { + EnWf_SetupWait(this); + } + } +} + +void EnWf_SetupBlocking(EnWf* this) { + f32 lastFrame = Animation_GetLastFrame(&gWolfosBlockingAnim); + + if (this->slashStatus != 0) { + this->slashStatus = -1; + } + + this->actor.speedXZ = 0.0f; + this->action = WOLFOS_ACTION_BLOCKING; + this->actionTimer = 10; + + Animation_Change(&this->skelAnime, &gWolfosBlockingAnim, 0.0f, 0.0f, lastFrame, ANIMMODE_ONCE_INTERP, -4.0f); + EnWf_SetupAction(this, EnWf_Blocking); +} + +void EnWf_Blocking(EnWf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + if (this->actionTimer != 0) { + this->actionTimer--; + } else { + this->skelAnime.playSpeed = 1.0f; + } + + if (SkelAnime_Update(&this->skelAnime)) { + s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((ABS(yawDiff) <= 0x4000) && (this->actor.xzDistToPlayer < 60.0f) && + (ABS(this->actor.yDistToPlayer) < 50.0f)) { + if (func_800354B4(globalCtx, &this->actor, 100.0f, 10000, 0x4000, this->actor.shape.rot.y)) { + if (player->swordAnimation == 0x11) { + EnWf_SetupBlocking(this); + } else if ((globalCtx->gameplayFrames % 2) != 0) { + EnWf_SetupBlocking(this); + } else { + EnWf_SetupBackflipAway(this); + } + + } else { + s16 angleFacingLink = player->actor.shape.rot.y - this->actor.shape.rot.y; + + if (!Actor_OtherIsTargeted(globalCtx, &this->actor) && + (((globalCtx->gameplayFrames % 2) != 0) || (ABS(angleFacingLink) < 0x38E0))) { + EnWf_SetupSlash(this); + } else { + EnWf_SetupRunAroundPlayer(this); + } + } + } else { + EnWf_SetupRunAroundPlayer(this); + } + } else if (this->actionTimer == 0) { + if (func_800354B4(globalCtx, &this->actor, 100.0f, 10000, 0x4000, this->actor.shape.rot.y)) { + if (player->swordAnimation == 0x11) { + EnWf_SetupBlocking(this); + } else if ((globalCtx->gameplayFrames % 2) != 0) { + EnWf_SetupBlocking(this); + } else { + EnWf_SetupBackflipAway(this); + } + } + } +} + +void EnWf_SetupSidestep(EnWf* this, GlobalContext* globalCtx) { + s16 angle; + Player* player; + f32 lastFrame = Animation_GetLastFrame(&gWolfosRunningAnim); + + Animation_Change(&this->skelAnime, &gWolfosRunningAnim, 1.0f, 0.0f, lastFrame, ANIMMODE_LOOP_INTERP, -4.0f); + + player = GET_PLAYER(globalCtx); + angle = player->actor.shape.rot.y + this->runAngle; + + if (Math_SinS(angle - this->actor.yawTowardsPlayer) > 0.0f) { + this->runAngle = 16000; + } else if (Math_SinS(angle - this->actor.yawTowardsPlayer) < 0.0f) { + this->runAngle = -16000; + } else if (Rand_ZeroOne() > 0.5f) { + this->runAngle = 16000; + } else { + this->runAngle = -16000; + } + + this->skelAnime.playSpeed = this->actor.speedXZ = 6.0f; + this->skelAnime.playSpeed *= 0.175f; + this->actor.world.rot.y = this->actor.shape.rot.y; + this->runSpeed = 0.0f; + this->actionTimer = (Rand_ZeroOne() * 10.0f) + 5.0f; + this->action = WOLFOS_ACTION_SIDESTEP; + + EnWf_SetupAction(this, EnWf_Sidestep); +} + +void EnWf_Sidestep(EnWf* this, GlobalContext* globalCtx) { + s16 angleDiff1; + Player* player = GET_PLAYER(globalCtx); + s32 animPrevFrame; + s32 animFrameSpeedDiff; + s32 animSpeed; + f32 baseRange = 0.0f; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + this->runAngle, 1, 3000, 1); + + // Actor_TestFloorInDirection is useless here (see comment below) + if ((this->actor.bgCheckFlags & 8) || + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, this->actor.shape.rot.y)) { + s16 angle = + (this->actor.bgCheckFlags & 8) ? (this->actor.wallYaw - this->actor.yawTowardsPlayer) - this->runAngle : 0; + + // This is probably meant to reverse direction if the edge of a floor is encountered, but does nothing + // unless bgCheckFlags & 8 anyway, since angle = 0 otherwise + if (ABS(angle) > 0x2EE0) { + this->runAngle = -this->runAngle; + } + } + + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + baseRange = 150.0f; + } + + if (this->actor.xzDistToPlayer <= (60.0f + baseRange)) { + Math_SmoothStepToF(&this->runSpeed, -4.0f, 1.0f, 1.5f, 0.0f); + } else if ((80.0f + baseRange) < this->actor.xzDistToPlayer) { + Math_SmoothStepToF(&this->runSpeed, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->runSpeed, 0.0f, 1.0f, 6.65f, 0.0f); + } + + if (this->runSpeed != 0.0f) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->runSpeed; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->runSpeed; + } + + if (ABS(this->runSpeed) < ABS(this->actor.speedXZ)) { + this->skelAnime.playSpeed = this->actor.speedXZ * 0.175f; + } else { + this->skelAnime.playSpeed = this->runSpeed * 0.175f; + } + + this->skelAnime.playSpeed = CLAMP(this->skelAnime.playSpeed, -3.0f, 3.0f); + + animPrevFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + animFrameSpeedDiff = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + animSpeed = (f32)ABS(this->skelAnime.playSpeed); + + if (!EnWf_ChangeAction(globalCtx, this, false)) { + this->actionTimer--; + + if (this->actionTimer == 0) { + angleDiff1 = player->actor.shape.rot.y - this->actor.yawTowardsPlayer; + angleDiff1 = ABS(angleDiff1); + + if (angleDiff1 >= 0x3A98) { + EnWf_SetupWait(this); + this->actionTimer = (Rand_ZeroOne() * 3.0f) + 1.0f; + } else { + Player* player2 = GET_PLAYER(globalCtx); + s16 angleDiff2 = player2->actor.shape.rot.y - this->actor.yawTowardsPlayer; + + this->actor.world.rot.y = this->actor.shape.rot.y; + + if ((this->actor.xzDistToPlayer <= 80.0f) && !Actor_OtherIsTargeted(globalCtx, &this->actor) && + (((globalCtx->gameplayFrames % 4) == 0) || (ABS(angleDiff2) < 0x38E0))) { + EnWf_SetupSlash(this); + } else { + EnWf_SetupRunAtPlayer(this, globalCtx); + } + } + } + + if ((animPrevFrame != (s32)this->skelAnime.curFrame) && (animFrameSpeedDiff <= 0) && + ((animSpeed + animPrevFrame) > 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_WALK); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->actor.world.pos, 20.0f, 3, 3.0f, 50, 50, 1); + } + + if ((globalCtx->gameplayFrames & 95) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_CRY); + } + } +} + +void EnWf_SetupDie(EnWf* this) { + Animation_MorphToPlayOnce(&this->skelAnime, &gWolfosRearingUpFallingOverAnim, -4.0f); + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (this->actor.bgCheckFlags & 1) { + this->unk_300 = false; + this->actor.speedXZ = -6.0f; + } else { + this->unk_300 = true; + } + + this->action = WOLFOS_ACTION_DIE; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionTimer = this->skelAnime.animLength; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_WOLFOS_DEAD); + EnWf_SetupAction(this, EnWf_Die); +} + +void EnWf_Die(EnWf* this, GlobalContext* globalCtx) { + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + + if (this->actor.bgCheckFlags & 1) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + this->unk_300 = false; + } + + if (SkelAnime_Update(&this->skelAnime)) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, 0xD0); + + if (this->switchFlag != 0xFF) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + + Actor_Kill(&this->actor); + } else { + s32 i; + Vec3f pos; + Vec3f velAndAccel = { 0.0f, 0.5f, 0.0f }; + + this->actionTimer--; + + for (i = ((s32)this->skelAnime.animLength - this->actionTimer) >> 1; i >= 0; i--) { + pos.x = Rand_CenteredFloat(60.0f) + this->actor.world.pos.x; + pos.z = Rand_CenteredFloat(60.0f) + this->actor.world.pos.z; + pos.y = Rand_CenteredFloat(50.0f) + (this->actor.world.pos.y + 20.0f); + EffectSsDeadDb_Spawn(globalCtx, &pos, &velAndAccel, &velAndAccel, 100, 0, 255, 255, 255, 255, 0, 0, 255, 1, + 9, true); + } + } +} + +void func_80B36F40(EnWf* this, GlobalContext* globalCtx) { + if ((this->action == WOLFOS_ACTION_WAIT) && (this->unk_2E2 != 0)) { + this->unk_4D4.y = Math_SinS(this->unk_2E2 * 4200) * 8920.0f; + } else if (this->action != WOLFOS_ACTION_STUNNED) { + if (this->action != WOLFOS_ACTION_SLASH) { + Math_SmoothStepToS(&this->unk_4D4.y, this->actor.yawTowardsPlayer - this->actor.shape.rot.y, 1, 1500, 0); + this->unk_4D4.y = CLAMP(this->unk_4D4.y, -0x3127, 0x3127); + } else { + this->unk_4D4.y = 0; + } + } +} + +void EnWf_UpdateDamage(EnWf* this, GlobalContext* globalCtx) { + if (this->colliderSpheres.base.acFlags & AC_BOUNCED) { + this->colliderSpheres.base.acFlags &= ~(AC_HIT | AC_BOUNCED); + this->colliderCylinderBody.base.acFlags &= ~AC_HIT; + this->colliderCylinderTail.base.acFlags &= ~AC_HIT; + } else if ((this->colliderCylinderBody.base.acFlags & AC_HIT) || + (this->colliderCylinderTail.base.acFlags & AC_HIT)) { + if (this->action >= WOLFOS_ACTION_WAIT) { + s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if ((!(this->colliderCylinderBody.base.acFlags & AC_HIT) && + (this->colliderCylinderTail.base.acFlags & AC_HIT)) || + (ABS(yawDiff) > 19000)) { + this->actor.colChkInfo.damage *= 4; + } + + this->colliderCylinderBody.base.acFlags &= ~AC_HIT; + this->colliderCylinderTail.base.acFlags &= ~AC_HIT; + + if (this->actor.colChkInfo.damageEffect != ENWF_DMGEFF_ICE_MAGIC) { + this->damageEffect = this->actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->actor, &this->colliderCylinderBody.info, 1); + this->slashStatus = 0; + + if ((this->actor.colChkInfo.damageEffect == ENWF_DMGEFF_STUN) || + (this->actor.colChkInfo.damageEffect == ENWF_DMGEFF_UNDEF)) { + if (this->action != WOLFOS_ACTION_STUNNED) { + Actor_SetColorFilter(&this->actor, 0, 120, 0, 80); + Actor_ApplyDamage(&this->actor); + EnWf_SetupStunned(this); + } + } else { // LIGHT_MAGIC, FIRE, NONE + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 8); + + if (this->damageEffect == ENWF_DMGEFF_FIRE) { + this->fireTimer = 40; + } + + if (Actor_ApplyDamage(&this->actor) == 0) { + EnWf_SetupDie(this); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + EnWf_SetupDamaged(this); + } + } + } + } + } +} + +void EnWf_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnWf* this = (EnWf*)thisx; + + EnWf_UpdateDamage(this, globalCtx); + + if (this->actor.colChkInfo.damageEffect != ENWF_DMGEFF_ICE_MAGIC) { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 32.0f, 30.0f, 60.0f, 0x1D); + this->actionFunc(this, globalCtx); + func_80B36F40(this, globalCtx); + } + + if (this->actor.bgCheckFlags & (1 | 2)) { + func_800359B8(&this->actor, this->actor.shape.rot.y, &this->actor.shape.rot); + } else { + Math_SmoothStepToS(&this->actor.shape.rot.x, 0, 1, 1000, 0); + Math_SmoothStepToS(&this->actor.shape.rot.z, 0, 1, 1000, 0); + } + + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderSpheres.base); + + if (this->action >= WOLFOS_ACTION_WAIT) { + if ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000)) { + Collider_UpdateCylinder(&this->actor, &this->colliderCylinderBody); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinderTail.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderCylinderBody.base); + } + } + + if (this->action == WOLFOS_ACTION_BLOCKING) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderSpheres.base); + } + + if (this->slashStatus > 0) { + if (!(this->colliderSpheres.base.atFlags & AT_BOUNCED)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSpheres.base); + } else { + EnWf_SetupRecoilFromBlockedSlash(this); + } + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 25.0f; + + if (this->eyeIndex == 0) { + if ((Rand_ZeroOne() < 0.2f) && ((globalCtx->gameplayFrames % 4) == 0) && (this->actor.colorFilterTimer == 0)) { + this->eyeIndex++; + } + } else { + this->eyeIndex = (this->eyeIndex + 1) & 3; + } +} + +s32 EnWf_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnWf* this = (EnWf*)thisx; + + if ((limbIndex == WOLFOS_LIMB_HEAD) || (limbIndex == WOLFOS_LIMB_EYES)) { + rot->y -= this->unk_4D4.y; + } + + return false; +} + +void EnWf_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f colliderVec = { 1200.0f, 0.0f, 0.0f }; + static Vec3f bodyPartVec = { 0.0f, 0.0f, 0.0f }; + EnWf* this = (EnWf*)thisx; + s32 bodyPartIndex = -1; + + Collider_UpdateSpheres(limbIndex, &this->colliderSpheres); + + if (limbIndex == WOLFOS_LIMB_TAIL) { + Vec3f colliderPos; + + bodyPartIndex = -1; + Matrix_MultVec3f(&colliderVec, &colliderPos); + this->colliderCylinderTail.dim.pos.x = colliderPos.x; + this->colliderCylinderTail.dim.pos.y = colliderPos.y; + this->colliderCylinderTail.dim.pos.z = colliderPos.z; + } + + if ((this->fireTimer != 0) || ((this->actor.colorFilterTimer != 0) && (this->actor.colorFilterParams & 0x4000))) { + switch (limbIndex) { + case WOLFOS_LIMB_EYES: + bodyPartIndex = 0; + break; + case WOLFOS_LIMB_FRONT_RIGHT_LOWER_LEG: + bodyPartIndex = 1; + break; + case WOLFOS_LIMB_FRONT_LEFT_LOWER_LEG: + bodyPartIndex = 2; + break; + case WOLFOS_LIMB_THORAX: + bodyPartIndex = 3; + break; + case WOLFOS_LIMB_ABDOMEN: + bodyPartIndex = 4; + break; + case WOLFOS_LIMB_TAIL: + bodyPartIndex = 5; + break; + case WOLFOS_LIMB_BACK_RIGHT_SHIN: + bodyPartIndex = 6; + break; + case 37: + //! @bug There is no limb with index this large, so bodyPartsPos[7] is uninitialised. Thus a flame will + //! be drawn at 0,0,0 when the Wolfos is on fire. + bodyPartIndex = 7; + break; + case WOLFOS_LIMB_BACK_RIGHT_PASTERN: + bodyPartIndex = 8; + break; + case WOLFOS_LIMB_BACK_LEFT_PAW: + bodyPartIndex = 9; + break; + } + + if (bodyPartIndex >= 0) { + Vec3f bodyPartPos; + + Matrix_MultVec3f(&bodyPartVec, &bodyPartPos); + this->bodyPartsPos[bodyPartIndex].x = bodyPartPos.x; + this->bodyPartsPos[bodyPartIndex].y = bodyPartPos.y; + this->bodyPartsPos[bodyPartIndex].z = bodyPartPos.z; + } + } +} + +static void* sWolfosNormalEyeTextures[] = { gWolfosNormalEyeOpenTex, gWolfosNormalEyeHalfTex, gWolfosNormalEyeNarrowTex, + gWolfosNormalEyeHalfTex }; +static void* sWolfosWhiteEyeTextures[] = { gWolfosWhiteEyeOpenTex, gWolfosWhiteEyeHalfTex, gWolfosWhiteEyeNarrowTex, + gWolfosWhiteEyeHalfTex }; + +void EnWf_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnWf* this = (EnWf*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_wf.c", 2157); + + // This conditional will always evaluate to true, since unk_300 is false whenever action is + // WOLFOS_ACTION_WAIT_TO_APPEAR. + if ((this->action != WOLFOS_ACTION_WAIT_TO_APPEAR) || !this->unk_300) { + func_80093D18(globalCtx->state.gfxCtx); + + if (this->actor.params == WOLFOS_NORMAL) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sWolfosNormalEyeTextures[this->eyeIndex])); + } else { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sWolfosWhiteEyeTextures[this->eyeIndex])); + } + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, EnWf_OverrideLimbDraw, EnWf_PostLimbDraw, &this->actor); + + if (this->fireTimer != 0) { + this->actor.colorFilterTimer++; + if (1) {} + this->fireTimer--; + + if ((this->fireTimer % 4) == 0) { + s32 fireIndex = this->fireTimer >> 2; + + EffectSsEnFire_SpawnVec3s(globalCtx, &this->actor, &this->bodyPartsPos[fireIndex], 75, 0, 0, fireIndex); + } + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_wf.c", 2190); +} + +s32 EnWf_DodgeRanged(GlobalContext* globalCtx, EnWf* this) { + Actor* actor = Actor_GetProjectileActor(globalCtx, &this->actor, 600.0f); + + if (actor != NULL) { + s16 angleToFacing; + s16 pad; + f32 dist; + + angleToFacing = Actor_WorldYawTowardActor(&this->actor, actor) - this->actor.shape.rot.y; + this->actor.world.rot.y = (u16)this->actor.shape.rot.y & 0xFFFF; + dist = Actor_WorldDistXYZToPoint(&this->actor, &actor->world.pos); + + if ((ABS(angleToFacing) < 0x2EE0) && (sqrt(dist) < 400.0)) { + EnWf_SetupBlocking(this); + } else { + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + if ((ABS(angleToFacing) < 0x2000) || (ABS(angleToFacing) > 0x5FFF)) { + EnWf_SetupSidestep(this, globalCtx); + this->actor.speedXZ *= 2.0f; + } else if (ABS(angleToFacing) < 0x5FFF) { + EnWf_SetupBackflipAway(this); + } + } + return true; + } + + return false; +} diff --git a/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.h b/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.h new file mode 100644 index 000000000..b99bb8092 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wf/z_en_wf.h @@ -0,0 +1,91 @@ +#ifndef Z_EN_WF_H +#define Z_EN_WF_H + +#include "ultra64.h" +#include "global.h" + +struct EnWf; + +typedef void (*EnWfActionFunc)(struct EnWf*, GlobalContext*); + +typedef enum { + /* 0 */ WOLFOS_LIMB_NONE, + /* 1 */ WOLFOS_LIMB_ROOT, + /* 2 */ WOLFOS_LIMB_BACK_LEFT_THIGH, + /* 3 */ WOLFOS_LIMB_BACK_LEFT_SHIN, + /* 4 */ WOLFOS_LIMB_BACK_LEFT_PASTERN, + /* 5 */ WOLFOS_LIMB_BACK_LEFT_PAW, + /* 6 */ WOLFOS_LIMB_TAIL, + /* 7 */ WOLFOS_LIMB_ABDOMEN, + /* 8 */ WOLFOS_LIMB_BACK_RIGHT_THIGH, + /* 9 */ WOLFOS_LIMB_BACK_RIGHT_SHIN, + /* 10 */ WOLFOS_LIMB_BACK_RIGHT_PASTERN, + /* 11 */ WOLFOS_LIMB_BACK_RIGHT_PAW, + /* 12 */ WOLFOS_LIMB_THORAX, + /* 13 */ WOLFOS_LIMB_FRONT_RIGHT_UPPER_LEG, + /* 14 */ WOLFOS_LIMB_FRONT_RIGHT_LOWER_LEG, + /* 15 */ WOLFOS_LIMB_FRONT_RIGHT_CLAW, + /* 16 */ WOLFOS_LIMB_HEAD_ROOT, + /* 17 */ WOLFOS_LIMB_HEAD, + /* 18 */ WOLFOS_LIMB_EYES, + /* 19 */ WOLFOS_LIMB_FRONT_LEFT_UPPER_LEG, + /* 20 */ WOLFOS_LIMB_FRONT_LEFT_LOWER_LEG, + /* 21 */ WOLFOS_LIMB_FRONT_LEFT_CLAW, + /* 22 */ WOLFOS_LIMB_MAX +} EnWfLimb; + +typedef enum { + /* 0 */ WOLFOS_ACTION_WAIT_TO_APPEAR, + /* 2 */ WOLFOS_ACTION_DIE = 2, + /* 3 */ WOLFOS_ACTION_DAMAGED, + /* 4 */ WOLFOS_ACTION_TURN_TOWARDS_PLAYER, + /* 5 */ WOLFOS_ACTION_BACKFLIP_AWAY, + /* 6 */ WOLFOS_ACTION_WAIT, + /* 7 */ WOLFOS_ACTION_BLOCKING, + /* 8 */ WOLFOS_ACTION_SLASH, + /* 9 */ WOLFOS_ACTION_RUN_AT_PLAYER, + /* 10 */ WOLFOS_ACTION_SEARCH_FOR_PLAYER, + /* 11 */ WOLFOS_ACTION_RUN_AROUND_PLAYER, + /* 12 */ WOLFOS_ACTION_RECOIL_FROM_BLOCKED_SLASH, + /* 14 */ WOLFOS_ACTION_SIDESTEP = 14, + /* 15 */ WOLFOS_ACTION_STUNNED +} EnWfAction; + +typedef struct EnWf { + /* 0x0000 */ Actor actor; + /* 0x014C */ Vec3s bodyPartsPos[10]; + /* 0x0188 */ SkelAnime skelAnime; + /* 0x01CC */ Vec3s jointTable[WOLFOS_LIMB_MAX]; + /* 0x0250 */ Vec3s morphTable[WOLFOS_LIMB_MAX]; + /* 0x02D4 */ s32 action; // Used instead of checking the actionFunc directly (but also in range comparisons) + /* 0x02D8 */ char unk_2D8[4]; // Unused + /* 0x02DC */ EnWfActionFunc actionFunc; + /* 0x02E0 */ s16 unk_2E0; // Used, but has no effect + /* 0x02E2 */ s16 unk_2E2; + /* 0x02E4 */ s16 fireTimer; + /* 0x02E6 */ u8 damageEffect; + /* 0x02E8 */ s32 actionTimer; // Used to make an action last for a certain amount of time + /* 0x02EC */ f32 runSpeed; + /* 0x02F0 */ char unk_2F0[4]; + /* 0x02F4 */ f32 unk_2F4; // Set and not used + /* 0x02F8 */ s16 slashStatus; // Whether to slash again or not, and whether to cry + /* 0x02FA */ s16 unk_2FA; // Set and not used + /* 0x02FC */ s16 switchFlag; + /* 0x02FE */ s16 runAngle; + /* 0x0300 */ s16 unk_300; // Set, but ineffectual (see comment in Draw) + /* 0x0302 */ u8 eyeIndex; + /* 0x0304 */ ColliderJntSph colliderSpheres; + /* 0x0324 */ ColliderJntSphElement colliderSpheresElements[4]; + /* 0x0424 */ ColliderCylinder colliderCylinderBody; + /* 0x0470 */ ColliderCylinder colliderCylinderTail; + /* 0x04BC */ Vec3f unk_4BC; + /* 0x04C8 */ Vec3f unk_4C8; + /* 0x04D4 */ Vec3s unk_4D4; +} EnWf; // size = 0x04DC + +typedef enum { + /* 0 */ WOLFOS_NORMAL, + /* 1 */ WOLFOS_WHITE +} EnWfType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c b/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c new file mode 100644 index 000000000..17e2feba4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c @@ -0,0 +1,364 @@ +/* + * File: z_en_wonder_item.c + * Overlay: ovl_En_Wonder_Item + * Description: Invisible Collectable; Used in MQ to create "Cow" switches + */ + +#include "z_en_wonder_item.h" +#include "vt.h" + +#define FLAGS 0 + +void EnWonderItem_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWonderItem_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWonderItem_Update(Actor* thisx, GlobalContext* globalCtx); + +void EnWonderItem_MultitagFree(EnWonderItem* this, GlobalContext* globalCtx); +void EnWonderItem_ProximityDrop(EnWonderItem* this, GlobalContext* globalCtx); +void EnWonderItem_InteractSwitch(EnWonderItem* this, GlobalContext* globalCtx); +void EnWonderItem_ProximitySwitch(EnWonderItem* this, GlobalContext* globalCtx); +void EnWonderItem_MultitagOrdered(EnWonderItem* this, GlobalContext* globalCtx); +void EnWonderItem_BombSoldier(EnWonderItem* this, GlobalContext* globalCtx); +void EnWonderItem_RollDrop(EnWonderItem* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 20, 30, 0, { 0, 0, 0 } }, +}; + +const ActorInit En_Wonder_Item_InitVars = { + ACTOR_EN_WONDER_ITEM, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnWonderItem), + (ActorFunc)EnWonderItem_Init, + (ActorFunc)EnWonderItem_Destroy, + (ActorFunc)EnWonderItem_Update, + NULL, + NULL, +}; + +static Vec3f sTagPointsFree[9]; +static Vec3f sTagPointsOrdered[9]; + +void EnWonderItem_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnWonderItem* this = (EnWonderItem*)thisx; + + if ((this->collider.dim.radius != 0) || (this->collider.dim.height != 0)) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnWonderItem_DropCollectible(EnWonderItem* this, GlobalContext* globalCtx, s32 autoCollect) { + static s16 dropTable[] = { + ITEM00_NUTS, ITEM00_HEART_PIECE, ITEM00_MAGIC_LARGE, ITEM00_MAGIC_SMALL, + ITEM00_HEART, ITEM00_ARROWS_SMALL, ITEM00_ARROWS_MEDIUM, ITEM00_ARROWS_LARGE, + ITEM00_RUPEE_GREEN, ITEM00_RUPEE_BLUE, ITEM00_RUPEE_RED, ITEM00_FLEXIBLE, + }; + s32 i; + s32 randomDrop; + + func_80078884(NA_SE_SY_GET_ITEM); + + if (this->dropCount == 0) { + this->dropCount++; + } + for (i = this->dropCount; i > 0; i--) { + if (this->itemDrop < WONDERITEM_DROP_RANDOM) { + if ((this->itemDrop == WONDERITEM_DROP_FLEXIBLE) || !autoCollect) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, dropTable[this->itemDrop]); + } else { + Item_DropCollectible(globalCtx, &this->actor.world.pos, dropTable[this->itemDrop] | 0x8000); + } + } else { + randomDrop = this->itemDrop - WONDERITEM_DROP_RANDOM; + if (!autoCollect) { + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, randomDrop); + } else { + Item_DropCollectibleRandom(globalCtx, NULL, &this->actor.world.pos, randomDrop | 0x8000); + } + } + } + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + Actor_Kill(&this->actor); +} + +void EnWonderItem_Init(Actor* thisx, GlobalContext* globalCtx) { + static u32 collisionTypes[] = { + 0x00000702 /* sword slash */, 0x0001F820 /* arrow */, 0x00000040 /* hammer */, 0x00000008 /* bomb */, + 0x00000004 /* slingshot */, 0x00000010 /* boomerang */, 0x00000080 /* hookshot */, + }; + s32 pad; + s16 colTypeIndex; + EnWonderItem* this = (EnWonderItem*)thisx; + s16 rotZover10; + s16 tagIndex; + + osSyncPrintf("\n\n"); + // "Mysterious mystery, very mysterious" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 不思議不思議まか不思議 \t ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + this->actor.flags &= ~ACTOR_FLAG_0; + + this->wonderMode = (this->actor.params >> 0xB) & 0x1F; + this->itemDrop = (this->actor.params >> 6) & 0x1F; + this->switchFlag = this->actor.params & 0x3F; + if (this->switchFlag == 0x3F) { + this->switchFlag = -1; + } + this->actor.targetMode = 1; + if ((this->switchFlag >= 0) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ You are Shock! ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + Actor_Kill(&this->actor); + return; + } + switch (this->wonderMode) { + case WONDERITEM_MULTITAG_FREE: + this->numTagPoints = this->actor.world.rot.z & 0xF; + rotZover10 = 0; + if (this->actor.world.rot.z >= 10) { + rotZover10 = this->actor.world.rot.z / 10; + this->timerMod = rotZover10 * 20; + } + this->numTagPoints = this->actor.world.rot.z - rotZover10 * 10; + // i.e timerMod = rot.z / 10 seconds, numTagPoints = rot.z % 10 + this->updateFunc = EnWonderItem_MultitagFree; + break; + case WONDERITEM_TAG_POINT_FREE: + tagIndex = this->actor.world.rot.z & 0xFF; + sTagPointsFree[tagIndex] = this->actor.world.pos; + Actor_Kill(&this->actor); + break; + case WONDERITEM_PROXIMITY_DROP: + this->dropCount = this->actor.world.rot.z & 0xFF; + this->updateFunc = EnWonderItem_ProximityDrop; + break; + case WONDERITEM_INTERACT_SWITCH: + colTypeIndex = this->actor.world.rot.z & 0xFF; + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->collider.info.bumper.dmgFlags = collisionTypes[colTypeIndex]; + this->collider.dim.radius = 20; + this->collider.dim.height = 30; + this->updateFunc = EnWonderItem_InteractSwitch; + break; + case WONDERITEM_UNUSED: + break; + case WONDERITEM_MULTITAG_ORDERED: + this->numTagPoints = this->actor.world.rot.z & 0xF; + rotZover10 = 0; + if (this->actor.world.rot.z >= 10) { + rotZover10 = this->actor.world.rot.z / 10; + this->timerMod = rotZover10 * 20; + } + this->numTagPoints = this->actor.world.rot.z - rotZover10 * 10; + // i.e timerMod = rot.z / 10 seconds, numTagPoints = rot.z % 10 + this->updateFunc = EnWonderItem_MultitagOrdered; + break; + case WONDERITEM_TAG_POINT_ORDERED: + tagIndex = this->actor.world.rot.z & 0xFF; + sTagPointsOrdered[tagIndex] = this->actor.world.pos; + Actor_Kill(&this->actor); + break; + case WONDERITEM_PROXIMITY_SWITCH: + this->updateFunc = EnWonderItem_ProximitySwitch; + break; + case WONDERITEM_BOMB_SOLDIER: + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->collider.info.bumper.dmgFlags = 0x00000004; // slingshot + this->unkPos = this->actor.world.pos; + this->collider.dim.radius = 35; + this->collider.dim.height = 75; + this->updateFunc = EnWonderItem_BombSoldier; + break; + case WONDERITEM_ROLL_DROP: + this->dropCount = this->actor.world.rot.z & 0xFF; + this->updateFunc = EnWonderItem_RollDrop; + break; + default: + Actor_Kill(&this->actor); + break; + } +} + +void EnWonderItem_MultitagFree(EnWonderItem* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 prevTagFlags = this->tagFlags; + s32 i; + s32 mask; + + for (i = 0, mask = 1; i < this->numTagPoints; i++, mask <<= 1) { + if (!(prevTagFlags & mask)) { + f32 dx = player->actor.world.pos.x - sTagPointsFree[i].x; + f32 dy = player->actor.world.pos.y - sTagPointsFree[i].y; + f32 dz = player->actor.world.pos.z - sTagPointsFree[i].z; + + if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 50.0f) { + this->tagFlags |= mask; + this->tagCount++; + this->timer = this->timerMod + 81; + return; + } + if (BREG(0) != 0) { + DebugDisplay_AddObject(sTagPointsFree[i].x, sTagPointsFree[i].y, sTagPointsFree[i].z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 0, 255, 0, 255, 4, globalCtx->state.gfxCtx); + } + } + } + if (this->timer == 1) { + Actor_Kill(&this->actor); + return; + } + if (this->tagCount == this->numTagPoints) { + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + EnWonderItem_DropCollectible(this, globalCtx, true); + } +} + +void EnWonderItem_ProximityDrop(EnWonderItem* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((this->actor.xzDistToPlayer < 50.0f) && (fabsf(this->actor.world.pos.y - player->actor.world.pos.y) < 30.0f)) { + EnWonderItem_DropCollectible(this, globalCtx, true); + } +} + +void EnWonderItem_InteractSwitch(EnWonderItem* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + EnWonderItem_DropCollectible(this, globalCtx, false); + } +} + +void EnWonderItem_ProximitySwitch(EnWonderItem* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((this->actor.xzDistToPlayer < 50.0f) && (fabsf(this->actor.world.pos.y - player->actor.world.pos.y) < 30.0f)) { + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + Actor_Kill(&this->actor); + } +} + +void EnWonderItem_MultitagOrdered(EnWonderItem* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 prevTagFlags = this->tagFlags; + s32 i; + s32 mask; + + for (i = 0, mask = 1; i < this->numTagPoints; i++, mask <<= 1) { + if (!(prevTagFlags & mask)) { + f32 dx = player->actor.world.pos.x - sTagPointsOrdered[i].x; + f32 dy = player->actor.world.pos.y - sTagPointsOrdered[i].y; + f32 dz = player->actor.world.pos.z - sTagPointsOrdered[i].z; + + if (sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 50.0f) { + if (prevTagFlags & mask) { + return; + } else if (i == this->nextTag) { + this->tagFlags |= mask; + this->tagCount++; + this->nextTag++; + this->timer = this->timerMod + 81; + return; + } else { + Actor_Kill(&this->actor); + return; + } + } else if (BREG(0) != 0) { + DebugDisplay_AddObject(sTagPointsOrdered[i].x, sTagPointsOrdered[i].y, sTagPointsOrdered[i].z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 0, 0, 255, 255, 4, globalCtx->state.gfxCtx); + } + } + } + if (this->timer == 1) { + Actor_Kill(&this->actor); + return; + } + if (this->tagCount == this->numTagPoints) { + EnWonderItem_DropCollectible(this, globalCtx, true); + } +} + +void EnWonderItem_BombSoldier(EnWonderItem* this, GlobalContext* globalCtx) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_HEISHI2, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.yawTowardsPlayer, 0, + 9) != NULL) { + // "Careless soldier spawned" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ うっかり兵セット完了 ☆☆☆☆☆ \n" VT_RST); + } + if (this->switchFlag >= 0) { + Flags_SetSwitch(globalCtx, this->switchFlag); + } + Actor_Kill(&this->actor); + } +} + +void EnWonderItem_RollDrop(EnWonderItem* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if ((this->actor.xzDistToPlayer < 50.0f) && (player->invincibilityTimer < 0) && + (fabsf(this->actor.world.pos.y - player->actor.world.pos.y) < 30.0f)) { + EnWonderItem_DropCollectible(this, globalCtx, true); + } +} + +void EnWonderItem_Update(Actor* thisx, GlobalContext* globalCtx) { + static s16 debugArrowColors[] = { + 255, 255, 0, 255, 0, 255, 0, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 128, 128, + 128, 128, 128, 0, 128, 0, 128, 0, 128, 0, 128, 0, 0, 0, 128, 0, 0, 0, 128, + }; // These seem to be mistyped. Logically they should be s16[13][3] and be indexed as [colorIndex][i] + s32 pad; + EnWonderItem* this = (EnWonderItem*)thisx; + s32 colorIndex; + + if (this->timer != 0) { + this->timer--; + } + this->updateFunc(this, globalCtx); + + if (this->wonderMode == WONDERITEM_UNUSED) { + Actor_SetFocus(&this->actor, this->unkHeight); + } + if ((this->wonderMode == WONDERITEM_INTERACT_SWITCH) || (this->wonderMode == WONDERITEM_BOMB_SOLDIER)) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + colorIndex = this->wonderMode; + if (this->wonderMode > 12) { + colorIndex = 0; + } + if (BREG(0) != 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, debugArrowColors[colorIndex], debugArrowColors[colorIndex + 1], + debugArrowColors[colorIndex + 2], 255, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.h b/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.h new file mode 100644 index 000000000..fc2f2e816 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.h @@ -0,0 +1,61 @@ +#ifndef Z_EN_WONDER_ITEM_H +#define Z_EN_WONDER_ITEM_H + +#include "ultra64.h" +#include "global.h" + +struct EnWonderItem; + +typedef void (*EnWonderItemUpdateFunc)(struct EnWonderItem*, GlobalContext*); + +typedef struct EnWonderItem { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnWonderItemUpdateFunc updateFunc; + /* 0x0150 */ f32 unkHeight; // sets height of dummied out mode 4 + /* 0x0154 */ s16 wonderMode; + /* 0x0156 */ s16 itemDrop; + /* 0x0158 */ s16 numTagPoints; + /* 0x015A */ s16 dropCount; + /* 0x015C */ s16 timer; + /* 0x015E */ s16 tagFlags; + /* 0x015A */ s16 tagCount; + /* 0x0162 */ s16 switchFlag; + /* 0x0164 */ char unk_164[4]; + /* 0x0168 */ s16 nextTag; + /* 0x016A */ s16 timerMod; + /* 0x016C */ Vec3f unkPos; // set to initial position by mode bomb soldier, then never used. + /* 0x0178 */ char unk_178[8]; + /* 0x0180 */ ColliderCylinder collider; + /* 0x01CC */ char unk_1CC[4]; +} EnWonderItem; // size = 0x01D0 + +typedef enum { + /* 0 */ WONDERITEM_MULTITAG_FREE, + /* 1 */ WONDERITEM_TAG_POINT_FREE, + /* 2 */ WONDERITEM_PROXIMITY_DROP, + /* 3 */ WONDERITEM_INTERACT_SWITCH, + /* 4 */ WONDERITEM_UNUSED, + /* 5 */ WONDERITEM_MULTITAG_ORDERED, + /* 6 */ WONDERITEM_TAG_POINT_ORDERED, + /* 7 */ WONDERITEM_PROXIMITY_SWITCH, + /* 8 */ WONDERITEM_BOMB_SOLDIER, + /* 9 */ WONDERITEM_ROLL_DROP +} EnWonderItemMode; + +typedef enum { + /* 0 */ WONDERITEM_DROP_NUTS, + /* 1 */ WONDERITEM_DROP_HEART_PIECE, + /* 2 */ WONDERITEM_DROP_MAGIC_LARGE, + /* 3 */ WONDERITEM_DROP_MAGIC_SMALL, + /* 4 */ WONDERITEM_DROP_HEART, + /* 5 */ WONDERITEM_DROP_ARROWS_SMALL, + /* 6 */ WONDERITEM_DROP_ARROWS_MEDIUM, + /* 7 */ WONDERITEM_DROP_ARROWS_LARGE, + /* 8 */ WONDERITEM_DROP_GREEN_RUPEE, + /* 9 */ WONDERITEM_DROP_BLUE_RUPEE, + /* A */ WONDERITEM_DROP_RED_RUPEE, + /* B */ WONDERITEM_DROP_FLEXIBLE, + /* C */ WONDERITEM_DROP_RANDOM +} EnWonderItemDrop; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.c b/soh/src/overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.c new file mode 100644 index 000000000..392193447 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.c @@ -0,0 +1,256 @@ +/* + * File: z_en_wonder_talk.c + * Overlay: ovl_En_Wonder_Talk + * Description: Checkable spot (Green Navi) + */ + +#include "z_en_wonder_talk.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_27) + +void EnWonderTalk_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWonderTalk_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWonderTalk_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80B391CC(EnWonderTalk* this, GlobalContext* globalCtx); +void func_80B395F0(EnWonderTalk* this, GlobalContext* globalCtx); +void func_80B3943C(EnWonderTalk* this, GlobalContext* globalCtx); + +const ActorInit En_Wonder_Talk_InitVars = { + ACTOR_EN_WONDER_TALK, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnWonderTalk), + (ActorFunc)EnWonderTalk_Init, + (ActorFunc)EnWonderTalk_Destroy, + (ActorFunc)EnWonderTalk_Update, + NULL, + NULL, +}; + +void EnWonderTalk_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnWonderTalk_Init(Actor* thisx, GlobalContext* globalCtx) { + EnWonderTalk* this = (EnWonderTalk*)thisx; + + osSyncPrintf("\n\n"); + // "Special conversation" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 特殊会話くん ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + + this->unk_150 = (this->actor.params >> 0xB) & 0x1F; + this->unk_152 = (this->actor.params >> 6) & 0x1F; + this->switchFlag = this->actor.params & 0x3F; + if (this->switchFlag == 0x3F) { + this->switchFlag = -1; + } + this->actor.targetMode = 1; + if (this->switchFlag >= 0) { + if (Flags_GetSwitch(globalCtx, this->switchFlag)) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ You are Shock! ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + Actor_Kill(&this->actor); + return; + } + } + this->actionFunc = func_80B391CC; + this->unk_15C = 40.0f; +} + +void func_80B391CC(EnWonderTalk* this, GlobalContext* globalCtx) { + if (this->switchFlag < 0 || !Flags_GetSwitch(globalCtx, this->switchFlag)) { + switch (this->unk_150) { + case 1: + // "Slate GO!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 石板GO! ☆☆☆☆☆ \n" VT_RST); + this->height = 0.0f; + this->unk_15C = 80.0f; + // "Attention coordinates" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 注目座標\t \t☆☆☆☆☆ %f\n" VT_RST, 0.0f); + if (!LINK_IS_ADULT) { + this->actor.textId = 0x7040; + // "Children" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ こども ☆☆☆☆☆ \n" VT_RST); + } else { + // "Adult" + osSyncPrintf(VT_FGCOL(CYAN) " ☆☆☆☆☆ おとな ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x7088; + } + + this->unk_156 = TEXT_STATE_EVENT; + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ this->actor.talk_message ☆☆☆☆☆ %x\n" VT_RST, this->actor.textId); + break; + case 2: + // "Diary start!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ 日記帳スタート! ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x5002; + this->unk_156 = TEXT_STATE_CHOICE; + this->height = 30.0f; + this->unk_15C = 40.0f; + // "Attention coordinates" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 注目座標\t \t☆☆☆☆☆ %f\n" VT_RST, 30.0f); + break; + case 3: + this->actor.textId = 0x501E; + this->unk_156 = TEXT_STATE_EVENT; + this->height = 0.0f; + this->unk_15C = 110.0f; + // "Attention coordinates" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 注目座標\t \t☆☆☆☆☆ %f\n" VT_RST, 0.0f); + break; + case 4: + this->actor.textId = 0x5020; + this->unk_156 = TEXT_STATE_DONE; + this->height = 0.0f; + // "Attention coordinates" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 注目座標\t \t☆☆☆☆☆ %f\n" VT_RST, 0.0f); + this->unk_15C = 120.0f; + if (gSaveContext.eventChkInf[1] & 0x2000) { + Actor_Kill(&this->actor); + } + break; + case 5: + this->actor.textId = 0x501F; + this->unk_156 = TEXT_STATE_EVENT; + this->height = 0.0f; + this->unk_15C = 110.0f; + // "Attention coordinates" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 注目座標\t \t☆☆☆☆☆ %f\n" VT_RST, 0.0f); + break; + default: + this->actor.textId = 0x7072; + this->unk_156 = TEXT_STATE_EVENT; + break; + } + + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ this->actor.talk_message ☆☆☆☆☆ %x\n" VT_RST, this->actor.textId); + this->actionFunc = func_80B3943C; + } +} + +void func_80B3943C(EnWonderTalk* this, GlobalContext* globalCtx) { + s16 yawDiff; + s16 yawDiffTemp; + + this->unk_15A++; + if (this->unk_150 == 4 && (gSaveContext.eventChkInf[1] & 0x2000)) { + Actor_Kill(&this->actor); + return; + } + if (this->switchFlag < 0 || !Flags_GetSwitch(globalCtx, this->switchFlag)) { + if ((Actor_ProcessTalkRequest(&this->actor, globalCtx))) { + if (this->unk_156 != TEXT_STATE_DONE) { + this->actionFunc = func_80B395F0; + } else { + if (this->switchFlag >= 0) { + this->actor.flags &= ~ACTOR_FLAG_0; + Flags_SetSwitch(globalCtx, this->switchFlag); + } + this->actionFunc = func_80B391CC; + } + } else if (!(this->unk_15C < this->actor.xzDistToPlayer)) { + yawDiffTemp = (this->actor.yawTowardsPlayer - this->actor.world.rot.y); + yawDiff = ABS(yawDiffTemp); + + if (yawDiff < 0x4000) { + if (this->unk_15A >= 2) { + osSyncPrintf("\n\n"); + // "Save information" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ セーブ情報\t\t☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + // "Type index" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 種類インデックス\t☆☆☆☆☆ %d\n" VT_RST, this->unk_150); + // "Actual message type" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 実質メッセージ種類 %x\n" VT_RST, this->actor.textId); + // "Specified range" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 指定範囲 %d\n" VT_RST, this->actor.world.rot.z); + osSyncPrintf("\n\n"); + } + this->unk_15A = 0; + func_8002F2CC(&this->actor, globalCtx, this->unk_15C); + } + } + } +} + +void func_80B395F0(EnWonderTalk* this, GlobalContext* globalCtx) { + if (this->unk_156 == Message_GetState(&globalCtx->msgCtx) && Message_ShouldAdvance(globalCtx)) { + if (this->switchFlag >= 0) { + this->actor.flags &= ~ACTOR_FLAG_0; + Flags_SetSwitch(globalCtx, this->switchFlag); + } + switch (this->unk_150) { + case 1: + Message_CloseTextbox(globalCtx); + this->actionFunc = func_80B391CC; + break; + case 2: + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (!LINK_IS_ADULT) { + // "I'm still a child!" + osSyncPrintf(VT_FGCOL(GREEN) " ☆☆☆☆☆ まだコドモなの! ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x5001; + } else { + // "I'm an adult. .. .." + osSyncPrintf(VT_FGCOL(YELLOW) " ☆☆☆☆☆ アダルトなの。。。 ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x5003; + } + break; + case 1: + // "Out!" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆☆☆☆ はずれ! ☆☆☆☆☆ \n" VT_RST); + this->actor.textId = 0x5004; + break; + } + + this->unk_156 = TEXT_STATE_DONE; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->actionFunc = func_80B391CC; + break; + case 3: + Message_CloseTextbox(globalCtx); + if (this->unk_164 == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_POH, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 2); + this->unk_164 = 1; + } + + this->actionFunc = func_80B391CC; + break; + case 5: + Message_CloseTextbox(globalCtx); + if (this->unk_164 == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_POH, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 3); + this->unk_164 = 1; + } + this->actionFunc = func_80B391CC; + break; + } + } +} + +void EnWonderTalk_Update(Actor* thisx, GlobalContext* globalCtx) { + EnWonderTalk* this = (EnWonderTalk*)thisx; + + if (this->unk_158 != 0) { + this->unk_158--; + } + this->actionFunc(this, globalCtx); + Actor_SetFocus(&this->actor, this->height); + + if (BREG(0) != 0) { + if (this->unk_15A != 0) { + if ((this->unk_15A & 1) == 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 10, 10, 10, 255, 4, globalCtx->state.gfxCtx); + } + } else { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 0, 255, 0, 255, 4, globalCtx->state.gfxCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.h b/soh/src/overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.h new file mode 100644 index 000000000..50b370b31 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.h @@ -0,0 +1,25 @@ +#ifndef Z_EN_WONDER_TALK_H +#define Z_EN_WONDER_TALK_H + +#include "ultra64.h" +#include "global.h" + +struct EnWonderTalk; + +typedef void (*EnWonderTalkFunc)(struct EnWonderTalk*, GlobalContext*); + +typedef struct EnWonderTalk { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnWonderTalkFunc actionFunc; + /* 0x0150 */ s16 unk_150; + /* 0x0152 */ s16 unk_152; + /* 0x0154 */ s16 switchFlag; + /* 0x0156 */ s16 unk_156; + /* 0x0158 */ s16 unk_158; + /* 0x0160 */ s16 unk_15A; + /* 0x015C */ f32 unk_15C; + /* 0x0160 */ f32 height; + /* 0x0164 */ u8 unk_164; +} EnWonderTalk; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.c b/soh/src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.c new file mode 100644 index 000000000..8c918ac9c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.c @@ -0,0 +1,292 @@ +/* + * File: z_en_wonder_talk2.c + * Overlay: ovl_En_Wonder_Talk2 + * Description: Dialog spot + */ + +#include "z_en_wonder_talk2.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_27) + +void EnWonderTalk2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWonderTalk2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWonderTalk2_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80B3A10C(EnWonderTalk2* this, GlobalContext* globalCtx); +void func_80B3A4F8(EnWonderTalk2* this, GlobalContext* globalCtx); +void func_80B3A15C(EnWonderTalk2* this, GlobalContext* globalCtx); +void func_80B3A3D4(EnWonderTalk2* this, GlobalContext* globalCtx); +void EnWonderTalk2_DoNothing(EnWonderTalk2* this, GlobalContext* globalCtx); + +const ActorInit En_Wonder_Talk2_InitVars = { + ACTOR_EN_WONDER_TALK2, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnWonderTalk2), + (ActorFunc)EnWonderTalk2_Init, + (ActorFunc)EnWonderTalk2_Destroy, + (ActorFunc)EnWonderTalk2_Update, + NULL, + NULL, +}; + +static s16 D_80B3A8E0[] = { 6, 0, 1, 2, 3, 4, 5 }; + +void EnWonderTalk2_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnWonderTalk2_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnWonderTalk2* this = (EnWonderTalk2*)thisx; + + osSyncPrintf("\n\n"); + // "Transparent message" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 透明メッセージ君 ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + this->baseMsgId = (this->actor.params >> 6) & 0xFF; + if (this->actor.world.rot.z > 0) { + s32 rangeIndex = 0; + s16 rotZmod10 = this->actor.world.rot.z; + + while (rotZmod10 > 10) { + rotZmod10 -= 10; + rangeIndex++; + } + // rangeIndex = rot.z/10 here + this->triggerRange = rotZmod10 * 40.0f; + if (rangeIndex > 6) { + rangeIndex = 0; + } + + this->actor.targetMode = D_80B3A8E0[rangeIndex]; + + osSyncPrintf("\n\n"); + // "originally?" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 元は? ☆☆☆☆☆ %d\n" VT_RST, this->actor.world.rot.z); + // "The range is?" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ レンジは? ☆☆☆☆☆ %d\n" VT_RST, this->actor.targetMode); + // "Is the range?" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ は、範囲わ? ☆☆☆☆☆ %f\n" VT_RST, this->triggerRange); + osSyncPrintf("\n\n"); + osSyncPrintf("\n\n"); + osSyncPrintf("\n\n"); + } + + this->initPos = this->actor.world.pos; + this->switchFlag = (this->actor.params & 0x3F); + this->talkMode = ((this->actor.params >> 0xE) & 3); + + if (this->switchFlag == 0x3F) { + this->switchFlag = -1; + } + if (this->switchFlag >= 0 && Flags_GetSwitch(globalCtx, this->switchFlag)) { + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ You are Shock! ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); + Actor_Kill(&this->actor); + return; + } + if ((this->talkMode == 1) && (globalCtx->sceneNum == SCENE_MEN) && (this->switchFlag != 0x08) && + (this->switchFlag != 0x16) && (this->switchFlag != 0x2F)) { + + this->unk_15A = false; + this->talkMode = 4; + } + if (this->talkMode == 3) { + this->actor.flags &= ~ACTOR_FLAG_27; + this->actionFunc = EnWonderTalk2_DoNothing; + } else { + this->actionFunc = func_80B3A10C; + } +} + +void func_80B3A10C(EnWonderTalk2* this, GlobalContext* globalCtx) { + this->actor.textId = 0x200; + this->actor.textId |= this->baseMsgId; + if (this->talkMode == 1 || this->talkMode == 4) { + this->actionFunc = func_80B3A4F8; + } else { + this->actionFunc = func_80B3A15C; + } +} + +void func_80B3A15C(EnWonderTalk2* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_158++; + if ((this->switchFlag >= 0) && Flags_GetSwitch(globalCtx, this->switchFlag)) { + if (!this->unk_15A) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_15A = true; + } + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if ((this->switchFlag >= 0) && (this->talkMode != 2)) { + Flags_SetSwitch(globalCtx, this->switchFlag); + // "I saved it! All of it!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ セーブしたよ!おもいっきり! %x\n" VT_RST, this->switchFlag); + } + + this->actionFunc = func_80B3A10C; + } else { + s16 yawDiff = ABS((s16)(this->actor.yawTowardsPlayer - this->actor.world.rot.y)); + + if (!((this->actor.xzDistToPlayer > 40.0f + this->triggerRange) || + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) > 100.0f) || (yawDiff >= 0x4000))) { + if (this->unk_158 >= 2) { + osSyncPrintf("\n\n"); + // "Transparent Message Kimi Set" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 透明メッセージ君せっと %x\n" VT_RST, this->actor.params); + // "Save Information" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ セーブ情報 \t %x\n" VT_RST, this->switchFlag); + // "Specified message type" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 指定メッセージ種類 %x\n" VT_RST, this->baseMsgId); + // "Actual message type" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 実質メッセージ種類 %x\n" VT_RST, this->actor.textId); + // "Specified range" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 指定範囲 %d\n" VT_RST, this->actor.world.rot.z); + // "Processing range" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 処理範囲 %f\n" VT_RST, this->triggerRange); + switch (this->talkMode) { + case 0: + // "Normal" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆ 通常 ☆☆ \n" VT_RST); + break; + case 2: + // "Check only" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆ チェックのみ ☆☆ \n" VT_RST); + break; + case 3: + // "Lock only" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆ ロックのみ ☆☆ \n" VT_RST); + break; + } + } + + this->unk_158 = 0; + func_8002F1C4(&this->actor, globalCtx, this->triggerRange + 50.0f, 100.0f, EXCH_ITEM_NONE); + } + } +} + +void func_80B3A3D4(EnWonderTalk2* this, GlobalContext* globalCtx) { + if (BREG(2) != 0) { + // "Oh" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ わー %d\n" VT_RST, Message_GetState(&globalCtx->msgCtx)); + } + + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_EVENT: + case TEXT_STATE_DONE: + if (Message_ShouldAdvance(globalCtx)) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) { + Message_CloseTextbox(globalCtx); + } + } else { + break; + } + case TEXT_STATE_NONE: + if ((this->switchFlag >= 0) && (this->talkMode != 4)) { + Flags_SetSwitch(globalCtx, this->switchFlag); + // "(Forced) I saved it! All of it!" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ (強制)セーブしたよ!おもいっきり! %x\n" VT_RST, this->switchFlag); + } + + if (this->talkMode == 4) { + this->unk_15A = true; + } + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_4); + func_8002DF54(globalCtx, NULL, 7); + this->unk_156 = true; + this->actionFunc = func_80B3A4F8; + break; + } +} + +void func_80B3A4F8(EnWonderTalk2* this, GlobalContext* globalCtx) { + Player* player; + + player = GET_PLAYER(globalCtx); + this->unk_158++; + if (this->switchFlag >= 0 && Flags_GetSwitch(globalCtx, this->switchFlag)) { + if (!this->unk_15A) { + this->actor.flags &= ~ACTOR_FLAG_0; + this->unk_15A = true; + } + } else if ((this->talkMode != 4) || !this->unk_15A) { + if (BREG(2) != 0) { + // "distance" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ きょり %f\n" VT_RST, this->actor.xzDistToPlayer); + } + if (((this->actor.xzDistToPlayer < (40.0f + this->triggerRange)) && + (fabsf(player->actor.world.pos.y - this->actor.world.pos.y) < 100.0f)) && + !Gameplay_InCsMode(globalCtx)) { + if (this->unk_158 >= 2) { + osSyncPrintf("\n\n"); + // "Transparent Message Kimi Seto" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 透明メッセージ君せっと %x\n" VT_RST, this->actor.params); + // "Save Information" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ セーブ情報 \t %x\n" VT_RST, this->switchFlag); + // "Specified message type" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 指定メッセージ種類 %x\n" VT_RST, this->baseMsgId); + // "Real message type" + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ 実質メッセージ種類 %x\n" VT_RST, this->actor.textId); + // "Specified range" + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 指定範囲 %d\n" VT_RST, this->actor.world.rot.z); + // "Processing range" + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ 処理範囲 %f\n" VT_RST, this->triggerRange); + // "What is your range?" + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ レンジは? \t\t %d\n" VT_RST, this->actor.targetMode); + osSyncPrintf("\n\n"); + osSyncPrintf("\n\n"); + switch (this->talkMode) { + case 1: + // "Compulsion" + osSyncPrintf(VT_FGCOL(PURPLE) " ☆☆ 強制 ☆☆ \n" VT_RST); + break; + case 4: + // "Gerudo Training Grounds Forced Check Only" + osSyncPrintf(VT_FGCOL(RED) " ☆☆ ゲルドの修練場強制チェックのみ ☆☆ \n" VT_RST); + break; + } + + osSyncPrintf("\n\n"); + } + this->unk_158 = 0; + if (!this->unk_156) { + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + func_8002DF54(globalCtx, NULL, 8); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_4; + this->actionFunc = func_80B3A3D4; + } + + } else { + this->unk_156 = false; + } + } +} + +void EnWonderTalk2_DoNothing(EnWonderTalk2* this, GlobalContext* globalCtx) { +} + +void EnWonderTalk2_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnWonderTalk2* this = (EnWonderTalk2*)thisx; + + this->actionFunc(this, globalCtx); + this->actor.world.pos.y = this->initPos.y; + + Actor_SetFocus(&this->actor, this->height); + + if (BREG(0) != 0) { + if (this->unk_158 != 0) { + if ((this->unk_158 & 1) == 0) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 70, 70, 70, 255, 4, globalCtx->state.gfxCtx); + } + } else { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, + 1.0f, 1.0f, 0, 0, 255, 255, 4, globalCtx->state.gfxCtx); + } + } +} diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.h b/soh/src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.h new file mode 100644 index 000000000..5e450998e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.h @@ -0,0 +1,26 @@ +#ifndef Z_EN_WONDER_TALK2_H +#define Z_EN_WONDER_TALK2_H + +#include "ultra64.h" +#include "global.h" + +struct EnWonderTalk2; + +typedef void (*EnWonderTalk2Func)(struct EnWonderTalk2*, GlobalContext*); + +typedef struct EnWonderTalk2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnWonderTalk2Func actionFunc; + /* 0x0150 */ s16 baseMsgId; // specified message type + /* 0x0152 */ s16 switchFlag; + /* 0x0154 */ s16 talkMode; + /* 0x0156 */ s16 unk_156; + /* 0x0158 */ s16 unk_158; + /* 0x015A */ u8 unk_15A; + /* 0x015B */ u8 unk_15B; + /* 0x015C */ f32 triggerRange; + /* 0x0160 */ f32 height; + /* 0x0164 */ Vec3f initPos; +} EnWonderTalk2; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c new file mode 100644 index 000000000..36c0ceb9c --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.c @@ -0,0 +1,464 @@ +/* + * File: z_en_wood02.c + * Overlay: ovl_En_Wood02 + * Description: Trees, bushes, leaves + */ + +#include "z_en_wood02.h" +#include "objects/object_wood02/object_wood02.h" + +#define FLAGS 0 + +void EnWood02_Init(Actor* thisx, GlobalContext* globalCtx); +void EnWood02_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnWood02_Update(Actor* thisx, GlobalContext* globalCtx); +void EnWood02_Draw(Actor* thisx, GlobalContext* globalCtx); + +/** + * WOOD_SPAWN_SPAWNER is also used by some individual trees: EnWood02_Update also checks for parent before running any + * despawning code. + * */ +typedef enum { + /* 0 */ WOOD_SPAWN_NORMAL, + /* 1 */ WOOD_SPAWN_SPAWNED, + /* 2 */ WOOD_SPAWN_SPAWNER +} WoodSpawnType; + +typedef enum { + /* 0 */ WOOD_DRAW_TREE_CONICAL, + /* 1 */ WOOD_DRAW_TREE_OVAL, + /* 2 */ WOOD_DRAW_TREE_KAKARIKO_ADULT, + /* 3 */ WOOD_DRAW_BUSH_GREEN, + /* 4 */ WOOD_DRAW_4, // Used for black bushes and green leaves + /* 5 */ WOOD_DRAW_LEAF_YELLOW +} WoodDrawType; + +const ActorInit En_Wood02_InitVars = { + ACTOR_EN_WOOD02, + ACTORCAT_PROP, + FLAGS, + OBJECT_WOOD02, + sizeof(EnWood02), + (ActorFunc)EnWood02_Init, + (ActorFunc)EnWood02_Destroy, + (ActorFunc)EnWood02_Update, + (ActorFunc)EnWood02_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_TREE, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK5, + { 0x00000000, 0x00, 0x00 }, + { 0x0FC0074A, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 18, 60, 0, { 0, 0, 0 } }, +}; + +static f32 sSpawnDistance[] = { 707.0f, 525.0f, 510.0f, 500.0f, 566.0f, 141.0f }; + +static s16 sSpawnAngle[] = { 0x1FFF, 0x4C9E, 0x77F5, 0xA5C9, 0xD6C3, 0xA000 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 5600, ICHAIN_STOP), +}; + +static Gfx* D_80B3BF54[] = { + object_wood02_DL_0078D0, object_wood02_DL_007CA0, object_wood02_DL_0080D0, object_wood02_DL_000090, + object_wood02_DL_000340, object_wood02_DL_000340, object_wood02_DL_000700, +}; + +static Gfx* D_80B3BF70[] = { + object_wood02_DL_007968, + object_wood02_DL_007D38, + object_wood02_DL_0081A8, + NULL, + NULL, + NULL, + object_wood02_DL_007AD0, + object_wood02_DL_007E20, + object_wood02_DL_008350, + object_wood02_DL_000160, + object_wood02_DL_000440, + object_wood02_DL_000700, +}; + +static f32 sSpawnCos; + +static f32 sSpawnSin; + +s32 EnWood02_SpawnZoneCheck(EnWood02* this, GlobalContext* globalCtx, Vec3f* pos) { + f32 phi_f12; + + if (CVar_GetS32("gDisableDrawDistance", 0) != 0) { + return true; + } + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, pos, &this->actor.projectedPos, + &this->actor.projectedW); + + phi_f12 = ((this->actor.projectedW == 0.0f) ? 1000.0f : fabsf(1.0f / this->actor.projectedW)); + + if ((-this->actor.uncullZoneScale < this->actor.projectedPos.z) && + (this->actor.projectedPos.z < (this->actor.uncullZoneForward + this->actor.uncullZoneScale)) && + (((fabsf(this->actor.projectedPos.x) - this->actor.uncullZoneScale) * phi_f12) < 1.0f) && + (((this->actor.projectedPos.y + this->actor.uncullZoneDownward) * phi_f12) > -1.0f) && + (((this->actor.projectedPos.y - this->actor.uncullZoneScale) * phi_f12) < 1.0f)) { + return true; + } + return false; +} + +/** Spawns similar-looking trees or bushes only when the player is sufficiently close. Presumably done this way to keep + * memory usage down in Hyrule Field. */ +void EnWood02_SpawnOffspring(EnWood02* this, GlobalContext* globalCtx) { + EnWood02* childWood; + s16* childSpawnAngle; + Vec3f childPos; + s16 extraRot; + s16 childParams; + s32 i; + + for (i = 4; i >= 0; i--) { + if ((this->unk_14E[i] & 0x7F) == 0) { + extraRot = 0; + if (this->actor.params == WOOD_BUSH_GREEN_LARGE_SPAWNER) { + extraRot = 0x4000; + } + childSpawnAngle = &sSpawnAngle[i]; + sSpawnCos = Math_CosS(*childSpawnAngle + this->actor.world.rot.y + extraRot); + sSpawnSin = Math_SinS(*childSpawnAngle + this->actor.world.rot.y + extraRot); + childPos.x = (sSpawnDistance[i] * sSpawnSin) + this->actor.home.pos.x; + childPos.y = this->actor.home.pos.y; + childPos.z = (sSpawnDistance[i] * sSpawnCos) + this->actor.home.pos.z; + if (EnWood02_SpawnZoneCheck(this, globalCtx, &childPos)) { + if ((this->unk_14E[i] & 0x80) != 0) { + childParams = (0xFF00 | (this->actor.params + 1)); + } else { + childParams = (((this->drawType & 0xF0) << 4) | (this->actor.params + 1)); + } + childWood = (EnWood02*)Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, + ACTOR_EN_WOOD02, childPos.x, childPos.y, childPos.z, + this->actor.world.rot.x, *childSpawnAngle, 0, childParams); + if (childWood != NULL) { + childWood->unk_14E[0] = i; + this->unk_14E[i] |= 1; + childWood->actor.projectedPos = this->actor.projectedPos; + } else { + this->unk_14E[i] &= 0x80; + } + } + } + } +} + +void EnWood02_Init(Actor* thisx, GlobalContext* globalCtx2) { + s16 spawnType; + f32 actorScale; + GlobalContext* globalCtx = globalCtx2; + EnWood02* this = (EnWood02*)thisx; + CollisionPoly* outPoly; + s32 bgId; + f32 floorY; + s16 extraRot; + + spawnType = WOOD_SPAWN_NORMAL; + actorScale = 1.0f; + this->unk_14C = (this->actor.params >> 8) & 0xFF; + + if (this->actor.home.rot.z != 0) { + this->actor.home.rot.z = (this->actor.home.rot.z << 8) | this->unk_14C; + this->unk_14C = -1; + this->actor.world.rot.z = this->actor.shape.rot.z = 0; + } else if (this->unk_14C & 0x80) { + this->unk_14C = -1; + } + + this->actor.params &= 0xFF; + Actor_ProcessInitChain(&this->actor, sInitChain); + + if (this->actor.params <= WOOD_TREE_KAKARIKO_ADULT) { + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + } + + switch (this->actor.params) { + case WOOD_BUSH_GREEN_LARGE_SPAWNER: + case WOOD_BUSH_BLACK_LARGE_SPAWNER: + spawnType = 1; + case WOOD_BUSH_GREEN_LARGE_SPAWNED: + case WOOD_BUSH_BLACK_LARGE_SPAWNED: + spawnType++; + case WOOD_TREE_CONICAL_LARGE: + case WOOD_BUSH_GREEN_LARGE: + case WOOD_BUSH_BLACK_LARGE: + actorScale = 1.5f; + this->actor.uncullZoneForward = 4000.0f; + this->actor.uncullZoneScale = 2000.0f; + this->actor.uncullZoneDownward = 2400.0f; + break; + case WOOD_TREE_CONICAL_SPAWNER: + case WOOD_TREE_OVAL_YELLOW_SPAWNER: + case WOOD_TREE_OVAL_GREEN_SPAWNER: + case WOOD_BUSH_GREEN_SMALL_SPAWNER: + case WOOD_BUSH_BLACK_SMALL_SPAWNER: + spawnType = 1; + case WOOD_TREE_CONICAL_SPAWNED: + case WOOD_TREE_OVAL_YELLOW_SPAWNED: + case WOOD_TREE_OVAL_GREEN_SPAWNED: + case WOOD_BUSH_GREEN_SMALL_SPAWNED: + case WOOD_BUSH_BLACK_SMALL_SPAWNED: + spawnType++; + case WOOD_TREE_CONICAL_MEDIUM: + case WOOD_TREE_OVAL_GREEN: + case WOOD_TREE_KAKARIKO_ADULT: + case WOOD_BUSH_GREEN_SMALL: + case WOOD_BUSH_BLACK_SMALL: + this->actor.uncullZoneForward = 4000.0f; + this->actor.uncullZoneScale = 800.0f; + this->actor.uncullZoneDownward = 1800.0f; + break; + case WOOD_TREE_CONICAL_SMALL: + actorScale = 0.6f; + this->actor.uncullZoneForward = 4000.0f; + this->actor.uncullZoneScale = 400.0f; + this->actor.uncullZoneDownward = 1000.0f; + break; + case WOOD_LEAF_GREEN: + case WOOD_LEAF_YELLOW: + this->unk_14E[0] = 0x4B; + actorScale = 0.02f; + this->actor.velocity.x = Rand_CenteredFloat(6.0f); + this->actor.velocity.z = Rand_CenteredFloat(6.0f); + this->actor.velocity.y = (Rand_ZeroOne() * 1.25f) + -3.1f; + } + + if (this->actor.params <= WOOD_TREE_CONICAL_SPAWNED) { + this->drawType = WOOD_DRAW_TREE_CONICAL; + } else if (this->actor.params <= WOOD_TREE_OVAL_GREEN_SPAWNED) { + this->drawType = WOOD_DRAW_TREE_OVAL; + } else if (this->actor.params <= WOOD_TREE_KAKARIKO_ADULT) { + this->drawType = WOOD_DRAW_TREE_KAKARIKO_ADULT; + } else if (this->actor.params <= WOOD_BUSH_GREEN_LARGE_SPAWNED) { + this->drawType = WOOD_DRAW_BUSH_GREEN; + } else if (this->actor.params <= WOOD_LEAF_GREEN) { // Black bushes and green leaves + this->drawType = WOOD_DRAW_4; + } else { + this->drawType = WOOD_DRAW_LEAF_YELLOW; + } + + Actor_SetScale(&this->actor, actorScale); + this->spawnType = spawnType; + + if (spawnType != WOOD_SPAWN_NORMAL) { + extraRot = 0; + + if (this->actor.params == WOOD_BUSH_GREEN_LARGE_SPAWNER) { + extraRot = 0x4000; + } + + if (spawnType == WOOD_SPAWN_SPAWNER) { + this->drawType |= this->unk_14C << 4; + EnWood02_SpawnOffspring(this, globalCtx); + sSpawnCos = Math_CosS(sSpawnAngle[5] + this->actor.world.rot.y + extraRot); + sSpawnSin = Math_SinS(sSpawnAngle[5] + this->actor.world.rot.y + extraRot); + this->actor.world.pos.x += (sSpawnSin * sSpawnDistance[5]); + this->actor.world.pos.z += (sSpawnCos * sSpawnDistance[5]); + } else { + this->actor.flags |= ACTOR_FLAG_4; + } + + // Snap to floor, or remove if over void + this->actor.world.pos.y += 200.0f; + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &outPoly, &bgId, &this->actor, &this->actor.world.pos); + + if (floorY > BGCHECK_Y_MIN) { + this->actor.world.pos.y = floorY; + } else { + Actor_Kill(&this->actor); + return; + } + } + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + this->actor.home.rot.y = 0; + this->actor.colChkInfo.mass = MASS_IMMOVABLE; +} + +void EnWood02_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnWood02* this = (EnWood02*)thisx; + + if (this->actor.params <= WOOD_TREE_KAKARIKO_ADULT) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void EnWood02_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnWood02* this = (EnWood02*)thisx; + f32 wobbleAmplitude; + u8 new_var; + u8 phi_v0; + s32 pad; + Vec3f dropsSpawnPt; + s32 i; + s32 leavesParams; + + // Despawn extra trees in a group if out of range + if ((this->spawnType == WOOD_SPAWN_SPAWNED) && (this->actor.parent != NULL)) { + if (!(this->actor.flags & ACTOR_FLAG_6)) { + new_var = this->unk_14E[0]; + phi_v0 = 0; + + if (this->unk_14C < 0) { + phi_v0 = 0x80; + } + + ((EnWood02*)this->actor.parent)->unk_14E[new_var] = phi_v0; + Actor_Kill(&this->actor); + return; + } + } else if (this->spawnType == WOOD_SPAWN_SPAWNER) { + EnWood02_SpawnOffspring(this, globalCtx); + } + + if (this->actor.params <= WOOD_TREE_KAKARIKO_ADULT) { + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + Audio_PlayActorSound2(&this->actor, NA_SE_IT_REFLECTION_WOOD); + } + + if (this->actor.home.rot.y != 0) { + dropsSpawnPt = this->actor.world.pos; + dropsSpawnPt.y += 200.0f; + + if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &dropsSpawnPt, this->unk_14C << 4); + } else { + if (this->actor.home.rot.z != 0) { + this->actor.home.rot.z &= 0x1FFF; + this->actor.home.rot.z |= 0xE000; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_SW, dropsSpawnPt.x, dropsSpawnPt.y, + dropsSpawnPt.z, 0, this->actor.world.rot.y, 0, this->actor.home.rot.z); + this->actor.home.rot.z = 0; + } + } + + // Spawn falling leaves + if (this->unk_14C >= -1) { + leavesParams = WOOD_LEAF_GREEN; + + if ((this->actor.params == WOOD_TREE_OVAL_YELLOW_SPAWNER) || + (this->actor.params == WOOD_TREE_OVAL_YELLOW_SPAWNED)) { + leavesParams = WOOD_LEAF_YELLOW; + } + Audio_PlayActorSound2(&this->actor, NA_SE_EV_TREE_SWING); + + for (i = 3; i >= 0; i--) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_WOOD02, dropsSpawnPt.x, dropsSpawnPt.y, + dropsSpawnPt.z, 0, Rand_CenteredFloat(65535.0f), 0, leavesParams); + } + } + this->unk_14C = -0x15; + this->actor.home.rot.y = 0; + } + + if (this->actor.xzDistToPlayer < 600.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } else if (this->actor.params < 0x17) { // Bush + Player* player = GET_PLAYER(globalCtx); + + if (this->unk_14C >= -1) { + if (((player->rideActor == NULL) && (sqrt(this->actor.xyzDistToPlayerSq) < 20.0) && + (player->linearVelocity != 0.0f)) || + ((player->rideActor != NULL) && (sqrt(this->actor.xyzDistToPlayerSq) < 60.0) && + (player->rideActor->speedXZ != 0.0f))) { + if ((this->unk_14C >= 0) && (this->unk_14C < 0x64)) { + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, + ((this->unk_14C << 4) | 0x8000)); + } + this->unk_14C = -0x15; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_TREE_SWING); + } + } + } else { // Leaves + this->unk_14C++; + Math_ApproachF(&this->actor.velocity.x, 0.0f, 1.0f, 5 * 0.01f); + Math_ApproachF(&this->actor.velocity.z, 0.0f, 1.0f, 5 * 0.01f); + func_8002D7EC(&this->actor); + this->actor.shape.rot.z = Math_SinS(3000 * this->unk_14C) * 0x4000; + this->unk_14E[0]--; + + if (this->unk_14E[0] == 0) { + Actor_Kill(&this->actor); + } + } + + // Wobble from impact + if (this->unk_14C < -1) { + this->unk_14C++; + wobbleAmplitude = Math_SinS((this->unk_14C ^ 0xFFFF) * 0x3332) * 250.0f; + this->actor.shape.rot.x = (Math_CosS(this->actor.yawTowardsPlayer - this->actor.shape.rot.y) * wobbleAmplitude); + this->actor.shape.rot.z = (Math_SinS(this->actor.yawTowardsPlayer - this->actor.shape.rot.y) * wobbleAmplitude); + } +} + +void EnWood02_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnWood02* this = (EnWood02*)thisx; + s16 type; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + u8 red; + u8 green; + u8 blue; + + OPEN_DISPS(gfxCtx, "../z_en_wood02.c", 775); + type = this->actor.params; + + if ((type == WOOD_TREE_OVAL_GREEN_SPAWNER) || (type == WOOD_TREE_OVAL_GREEN_SPAWNED) || + (type == WOOD_TREE_OVAL_GREEN) || (type == WOOD_LEAF_GREEN)) { + red = 50; + green = 170; + blue = 70; + } else if ((type == WOOD_TREE_OVAL_YELLOW_SPAWNER) || (type == WOOD_TREE_OVAL_YELLOW_SPAWNED) || + (type == WOOD_LEAF_YELLOW)) { + red = 180; + green = 155; + blue = 0; + } else { + red = green = blue = 255; + } + + func_80093D84(gfxCtx); + + if ((this->actor.params == WOOD_LEAF_GREEN) || (this->actor.params == WOOD_LEAF_YELLOW)) { + func_80093D18(gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, red, green, blue, 127); + Gfx_DrawDListOpa(globalCtx, object_wood02_DL_000700); + } else if (D_80B3BF70[this->drawType & 0xF] != NULL) { + Gfx_DrawDListOpa(globalCtx, D_80B3BF54[this->drawType & 0xF]); + gDPSetEnvColor(POLY_XLU_DISP++, red, green, blue, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_wood02.c", 808), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_80B3BF70[this->drawType & 0xF]); + } else { + func_80093D84(gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_wood02.c", 814), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, D_80B3BF54[this->drawType & 0xF]); + } + + CLOSE_DISPS(gfxCtx, "../z_en_wood02.c", 840); +} diff --git a/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.h b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.h new file mode 100644 index 000000000..85ec12a5b --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Wood02/z_en_wood02.h @@ -0,0 +1,48 @@ +#ifndef Z_EN_WOOD02_H +#define Z_EN_WOOD02_H + +#include "ultra64.h" +#include "global.h" + +struct EnWood02; + +typedef struct EnWood02 { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 unk_14C; + /* 0x014E */ u8 unk_14E[5]; + /* 0x0153 */ u8 spawnType; + /* 0x0154 */ u8 drawType; + /* 0x0158 */ ColliderCylinder collider; +} EnWood02; // size = 0x01A4 + +// Types with SPAWNED in the name are those that can be managed by a spawner, however the actor allows you to spawn them +// on their own without a spawner as well. +typedef enum { + /* 0x00 */ WOOD_TREE_CONICAL_LARGE, + /* 0x01 */ WOOD_TREE_CONICAL_MEDIUM, + /* 0x02 */ WOOD_TREE_CONICAL_SMALL, + /* 0x03 */ WOOD_TREE_CONICAL_SPAWNER, + /* 0x04 */ WOOD_TREE_CONICAL_SPAWNED, + /* 0x05 */ WOOD_TREE_OVAL_GREEN, + /* 0x06 */ WOOD_TREE_OVAL_YELLOW_SPAWNER, + /* 0x07 */ WOOD_TREE_OVAL_YELLOW_SPAWNED, + /* 0x08 */ WOOD_TREE_OVAL_GREEN_SPAWNER, + /* 0x09 */ WOOD_TREE_OVAL_GREEN_SPAWNED, + /* 0x0A */ WOOD_TREE_KAKARIKO_ADULT, + /* 0x0B */ WOOD_BUSH_GREEN_SMALL, + /* 0x0C */ WOOD_BUSH_GREEN_LARGE, + /* 0x0D */ WOOD_BUSH_GREEN_SMALL_SPAWNER, + /* 0x0E */ WOOD_BUSH_GREEN_SMALL_SPAWNED, + /* 0x0F */ WOOD_BUSH_GREEN_LARGE_SPAWNER, + /* 0x10 */ WOOD_BUSH_GREEN_LARGE_SPAWNED, + /* 0x11 */ WOOD_BUSH_BLACK_SMALL, + /* 0x12 */ WOOD_BUSH_BLACK_LARGE, + /* 0x13 */ WOOD_BUSH_BLACK_SMALL_SPAWNER, + /* 0x14 */ WOOD_BUSH_BLACK_SMALL_SPAWNED, + /* 0x15 */ WOOD_BUSH_BLACK_LARGE_SPAWNER, + /* 0x16 */ WOOD_BUSH_BLACK_LARGE_SPAWNED, + /* 0x17 */ WOOD_LEAF_GREEN, + /* 0x18 */ WOOD_LEAF_YELLOW +} WoodType; + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c b/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c new file mode 100644 index 000000000..c64f9acb7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.c @@ -0,0 +1,2436 @@ +/* + * File: z_en_xc.c + * Overlay: ovl_En_Xc + * Description: Sheik + */ + +#include "z_en_xc.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "objects/object_xc/object_xc.h" +#include "scenes/overworld/spot05/spot05_scene.h" +#include "scenes/overworld/spot17/spot17_scene.h" +#include "scenes/indoors/tokinoma/tokinoma_scene.h" +#include "scenes/dungeons/ice_doukutu/ice_doukutu_scene.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnXc_Init(Actor* thisx, GlobalContext* globalCtx); +void EnXc_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnXc_Update(Actor* thisx, GlobalContext* globalCtx); +void EnXc_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnXc_Reset(void); + +void EnXc_DrawNothing(Actor* thisx, GlobalContext* globalCtx); +void EnXc_DrawDefault(Actor* thisx, GlobalContext* globalCtx); +void EnXc_DrawPullingOutHarp(Actor* thisx, GlobalContext* globalCtx); +void EnXc_DrawHarp(Actor* thisx, GlobalContext* globalCtx); +void EnXc_DrawTriforce(Actor* thisx, GlobalContext* globalCtx); +void EnXc_DrawSquintingEyes(Actor* thisx, GlobalContext* globalCtx); + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 25, 80, 0, { 0, 0, 0 } }, +}; + +static void* sEyeTextures[] = { + gSheikEyeOpenTex, + gSheikEyeHalfClosedTex, + gSheikEyeShutTex, +}; + +void EnXc_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); +} + +void EnXc_UpdateCollider(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + Collider* colliderBase = &this->collider.base; + s32 pad[3]; + + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, colliderBase); +} + +void EnXc_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void EnXc_CalculateHeadTurn(EnXc* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->npcInfo.unk_18 = player->actor.world.pos; + this->npcInfo.unk_14 = kREG(16) - 3.0f; + func_80034A14(&this->actor, &this->npcInfo, kREG(17) + 0xC, 2); +} + +void EnXc_SetEyePattern(EnXc* this) { + s32 pad[3]; + s16* blinkTimer = &this->blinkTimer; + s16* eyePattern = &this->eyeIdx; + + if (!DECR(*blinkTimer)) { + *blinkTimer = Rand_S16Offset(60, 60); + } + + *eyePattern = *blinkTimer; + if (*eyePattern >= ARRAY_COUNT(sEyeTextures)) { + *eyePattern = 0; + } +} + +void EnXc_SpawnNut(EnXc* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f* pos = &this->actor.world.pos; + s16 angle = this->actor.shape.rot.y; + f32 x = (Math_SinS(angle) * 30.0f) + pos->x; + f32 y = pos->y + 3.0f; + f32 z = (Math_CosS(angle) * 30.0f) + pos->z; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ARROW, x, y, z, 0xFA0, this->actor.shape.rot.y, 0, + ARROW_CS_NUT); +} + +void EnXc_BgCheck(EnXc* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 4); +} + +s32 EnXc_AnimIsFinished(EnXc* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* EnXc_GetCsCmd(GlobalContext* globalCtx, s32 npcActionIdx) { + CsCmdActorAction* action = NULL; + + if (globalCtx->csCtx.state != 0) { + action = globalCtx->csCtx.npcActions[npcActionIdx]; + } + return action; +} + +s32 EnXc_CompareCsAction(EnXc* this, GlobalContext* globalCtx, u16 action, s32 npcActionIdx) { + CsCmdActorAction* csCmdActorAction = EnXc_GetCsCmd(globalCtx, npcActionIdx); + + if (csCmdActorAction != NULL && csCmdActorAction->action == action) { + return true; + } + return false; +} + +s32 EnXc_CsActionsAreNotEqual(EnXc* this, GlobalContext* globalCtx, u16 action, s32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = EnXc_GetCsCmd(globalCtx, npcActionIdx); + + if (csCmdNPCAction && csCmdNPCAction->action != action) { + return true; + } + return false; +} + +void func_80B3C588(EnXc* this, GlobalContext* globalCtx, u32 npcActionIdx) { + CsCmdActorAction* csCmdNPCAction = EnXc_GetCsCmd(globalCtx, npcActionIdx); + Actor* thisx = &this->actor; + + if (csCmdNPCAction != NULL) { + thisx->world.pos.x = csCmdNPCAction->startPos.x; + thisx->world.pos.y = csCmdNPCAction->startPos.y; + thisx->world.pos.z = csCmdNPCAction->startPos.z; + thisx->world.rot.x = thisx->shape.rot.x = csCmdNPCAction->rot.x; + thisx->world.rot.y = thisx->shape.rot.y = csCmdNPCAction->rot.y; + thisx->world.rot.z = thisx->shape.rot.z = csCmdNPCAction->rot.z; + } +} + +void func_80B3C620(EnXc* this, GlobalContext* globalCtx, s32 npcActionIdx) { + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, npcActionIdx); + Vec3f* xcPos = &this->actor.world.pos; + f32 startX; + f32 startY; + f32 startZ; + f32 endX; + f32 endY; + f32 endZ; + f32 unk; + + if (npcAction != NULL) { + unk = + Environment_LerpWeightAccelDecel(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames, 0, 0); + startX = npcAction->startPos.x; + startY = npcAction->startPos.y; + startZ = npcAction->startPos.z; + endX = npcAction->endPos.x; + endY = npcAction->endPos.y; + endZ = npcAction->endPos.z; + xcPos->x = ((endX - startX) * unk) + startX; + xcPos->y = ((endY - startY) * unk) + startY; + xcPos->z = ((endZ - startZ) * unk) + startZ; + } +} + +void EnXc_ChangeAnimation(EnXc* this, AnimationHeader* animation, u8 mode, f32 morphFrames, s32 reverseFlag) { + s32 pad[2]; + AnimationHeader* animationSeg = SEGMENTED_TO_VIRTUAL(animation); + f32 frameCount = Animation_GetLastFrame(&animationSeg->common); + f32 playbackSpeed; + f32 startFrame; + f32 endFrame; + + if (!reverseFlag) { + startFrame = 0.0f; + endFrame = frameCount; + playbackSpeed = 1.0f; + } else { + startFrame = frameCount; + endFrame = 0.0f; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animationSeg, playbackSpeed, startFrame, endFrame, mode, morphFrames); +} + +void EnXc_CheckAndSetAction(EnXc* this, s32 check, s32 set) { + if (check != this->action) { + this->action = set; + } +} + +void func_80B3C7D4(EnXc* this, s32 action1, s32 action2, s32 action3) { + if (action1 != this->action) { + if (this->action == SHEIK_ACTION_PUT_HARP_AWAY) { + this->action = action2; + } else { + this->action = action3; + } + } +} + +s32 EnXc_NoCutscenePlaying(GlobalContext* globalCtx) { + if (globalCtx->csCtx.state == 0) { + return true; + } + return false; +} + +void func_80B3C820(EnXc* this) { + Animation_Change(&this->skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gSheikIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->action = SHEIK_ACTION_53; +} + +void func_80B3C888(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_NoCutscenePlaying(globalCtx) && this->actor.params == SHEIK_TYPE_4) { + func_80B3C820(this); + } +} + +void func_80B3C8CC(EnXc* this, GlobalContext* globalCtx) { + SkelAnime* skelAnime = &this->skelAnime; + + if (skelAnime->jointTable[0].y >= skelAnime->baseTransl.y) { + skelAnime->moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, skelAnime, 1.0f); + } +} + +void func_80B3C924(EnXc* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_80B3C964(EnXc* this, GlobalContext* globalCtx) { + this->skelAnime.baseTransl = this->skelAnime.jointTable[0]; + this->skelAnime.prevTransl = this->skelAnime.jointTable[0]; + this->skelAnime.moveFlags |= 3; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_80B3C9DC(EnXc* this) { + this->skelAnime.moveFlags &= ~0x3; +} + +void func_80B3C9EC(EnXc* this) { + EnXc_ChangeAnimation(this, &gSheikArmsCrossedIdleAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = SHEIK_ACTION_BLOCK_PEDESTAL; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->unk_30C = 1; +} + +void func_80B3CA38(EnXc* this, GlobalContext* globalCtx) { + // If Player is adult but hasn't learned Minuet of Forest + if (!(gSaveContext.eventChkInf[5] & 1) && LINK_IS_ADULT) { + this->action = SHEIK_ACTION_INIT; + } else { + Actor_Kill(&this->actor); + } +} + +s32 EnXc_MinuetCS(EnXc* this, GlobalContext* globalCtx) { + if (this->actor.params == SHEIK_TYPE_MINUET) { + Player* player = GET_PLAYER(globalCtx); + f32 z = player->actor.world.pos.z; + + if (z < -2225.0f) { + if (!Gameplay_InCsMode(globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gMinuetCs); + gSaveContext.cutsceneTrigger = 1; + gSaveContext.eventChkInf[5] |= 1; + Item_Give(globalCtx, ITEM_SONG_MINUET); + return true; + } + } + return false; + } + return true; +} + +void func_80B3CB58(EnXc* this, GlobalContext* globalCtx) { + // If hasn't learned Bolero and Player is Adult + if (!(gSaveContext.eventChkInf[5] & 2) && LINK_IS_ADULT) { + this->action = SHEIK_ACTION_INIT; + } else { + Actor_Kill(&this->actor); + } +} + +s32 EnXc_BoleroCS(EnXc* this, GlobalContext* globalCtx) { + Player* player; + PosRot* posRot; + + if (this->actor.params == SHEIK_TYPE_BOLERO) { + player = GET_PLAYER(globalCtx); + posRot = &player->actor.world; + if ((posRot->pos.x > -784.0f) && (posRot->pos.x < -584.0f) && (posRot->pos.y > 447.0f) && + (posRot->pos.y < 647.0f) && (posRot->pos.z > -446.0f) && (posRot->pos.z < -246.0f) && + !Gameplay_InCsMode(globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(&gDeathMountainCraterBoleroCs); + gSaveContext.cutsceneTrigger = 1; + gSaveContext.eventChkInf[5] |= 2; + Item_Give(globalCtx, ITEM_SONG_BOLERO); + return true; + } + return false; + } + return true; +} + +void EnXc_SetupSerenadeAction(EnXc* this, GlobalContext* globalCtx) { + // Player is adult and does not have iron boots and has not learned Serenade + if ((!CHECK_OWNED_EQUIP(EQUIP_BOOTS, 1) && !(gSaveContext.eventChkInf[5] & 4)) && LINK_IS_ADULT) { + this->action = SHEIK_ACTION_SERENADE; + osSyncPrintf("水のセレナーデ シーク誕生!!!!!!!!!!!!!!!!!!\n"); + } else { + Actor_Kill(&this->actor); + osSyncPrintf("水のセレナーデ シーク消滅!!!!!!!!!!!!!!!!!!\n"); + } +} + +s32 EnXc_SerenadeCS(EnXc* this, GlobalContext* globalCtx) { + if (this->actor.params == SHEIK_TYPE_SERENADE) { + Player* player = GET_PLAYER(globalCtx); + s32 stateFlags = player->stateFlags1; + + if (CHECK_OWNED_EQUIP(EQUIP_BOOTS, 1) && !(gSaveContext.eventChkInf[5] & 4) && !(stateFlags & 0x20000000) && + !Gameplay_InCsMode(globalCtx)) { + Cutscene_SetSegment(globalCtx, &gIceCavernSerenadeCs); + gSaveContext.cutsceneTrigger = 1; + gSaveContext.eventChkInf[5] |= 4; // Learned Serenade of Water Flag + Item_Give(globalCtx, ITEM_SONG_SERENADE); + osSyncPrintf("ブーツを取った!!!!!!!!!!!!!!!!!!\n"); + return true; + } + osSyncPrintf("はやくブーツを取るべし!!!!!!!!!!!!!!!!!!\n"); + return false; + } + return true; +} + +void EnXc_DoNothing(EnXc* this, GlobalContext* globalCtx) { +} + +void EnXc_SetWalkingSFX(EnXc* this, GlobalContext* globalCtx) { + s32 pad[2]; + u32 sfxId; + s32 pad2; + + if (Animation_OnFrame(&this->skelAnime, 11.0f) || Animation_OnFrame(&this->skelAnime, 23.0f)) { + if (this->actor.bgCheckFlags & 1) { + sfxId = SFX_FLAG; + sfxId += SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + func_80078914(&this->actor.projectedPos, sfxId); + } + } +} + +void EnXc_SetNutThrowSFX(EnXc* this, GlobalContext* globalCtx) { + s32 pad[2]; + u32 sfxId; + s32 pad2; + + if (Animation_OnFrame(&this->skelAnime, 7.0f)) { + if (this->actor.bgCheckFlags & 1) { + sfxId = SFX_FLAG; + sfxId += SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + func_80078914(&this->actor.projectedPos, sfxId); + } + } + if (Animation_OnFrame(&this->skelAnime, 20.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_SK_SHOUT); + } +} + +void EnXc_SetLandingSFX(EnXc* this, GlobalContext* globalCtx) { + u32 sfxId; + s16 sceneNum = globalCtx->sceneNum; + + if ((gSaveContext.sceneSetupIndex != 4) || (sceneNum != SCENE_SPOT11)) { + if (Animation_OnFrame(&this->skelAnime, 11.0f)) { + sfxId = SFX_FLAG; + sfxId += SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + func_80078914(&this->actor.projectedPos, sfxId); + } + } +} + +void EnXc_SetColossusAppearSFX(EnXc* this, GlobalContext* globalCtx) { + static Vec3f sXyzDist; + s16 sceneNum; + + if (gSaveContext.sceneSetupIndex == 4) { + sceneNum = globalCtx->sceneNum; + if (sceneNum == SCENE_SPOT11) { + CutsceneContext* csCtx = &globalCtx->csCtx; + u16 frameCount = csCtx->frames; + f32 wDest[2]; + + if (frameCount == 119) { + Vec3f pos = { -611.0f, 728.0f, -2.0f }; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &pos, &sXyzDist, wDest); + func_80078914(&sXyzDist, NA_SE_EV_JUMP_CONC); + } else if (frameCount == 164) { + Vec3f pos = { -1069.0f, 38.0f, 0.0f }; + s32 pad; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &pos, &sXyzDist, wDest); + func_80078914(&sXyzDist, NA_SE_PL_WALK_CONCRETE); + } + } + } +} + +void func_80B3D118(GlobalContext* globalCtx) { + s16 sceneNum; + + if ((gSaveContext.sceneSetupIndex != 4) || (sceneNum = globalCtx->sceneNum, sceneNum != SCENE_SPOT11)) { + func_800788CC(NA_SE_PL_SKIP); + } +} + +static Vec3f D_80B42DA0; + +static s32 D_80B41D90 = 0; +void EnXc_SetColossusWindSFX(GlobalContext* globalCtx) { + if (gSaveContext.sceneSetupIndex == 4) { + static Vec3f sPos = { 0.0f, 0.0f, 0.0f }; + static f32 sMaxSpeed = 0.0f; + static Vec3f D_80B42DB0; + s32 pad; + s16 sceneNum = globalCtx->sceneNum; + + if (sceneNum == SCENE_SPOT11) { + CutsceneContext* csCtx = &globalCtx->csCtx; + u16 frameCount = csCtx->frames; + + if ((frameCount >= 120) && (frameCount < 164)) { + s32 pad; + Vec3f* eye = &globalCtx->view.eye; + + if (D_80B41D90 != 0) { + f32 speed = Math3D_Vec3f_DistXYZ(&D_80B42DB0, eye) / 7.058922f; + + sMaxSpeed = CLAMP_MIN(sMaxSpeed, speed); + + osSyncPrintf("MAX speed = %f\n", sMaxSpeed); + + speed = CLAMP_MAX(speed, 2.0f); + func_800F436C(&sPos, NA_SE_EV_FLYING_AIR - SFX_FLAG, 0.6f + (0.4f * speed)); + } + + D_80B42DB0.x = eye->x; + D_80B42DB0.y = eye->y; + D_80B42DB0.z = eye->z; + D_80B41D90 = 1; + } + } + } +} + +static s32 sFlameSpawned = false; +void EnXc_SpawnFlame(EnXc* this, GlobalContext* globalCtx) { + + if (!sFlameSpawned) { + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, 0); + f32 xPos = npcAction->startPos.x; + f32 yPos = npcAction->startPos.y; + f32 zPos = npcAction->startPos.z; + + this->flameActor = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_LIGHT, xPos, yPos, zPos, 0, 0, 0, 5); + sFlameSpawned = true; + } +} + +void EnXc_SetupFlamePos(EnXc* this, GlobalContext* globalCtx) { + Vec3f* attachedPos; + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, 0); + + if (this->flameActor != NULL) { + attachedPos = &this->flameActor->world.pos; + if (!this) {} + attachedPos->x = npcAction->startPos.x; + attachedPos->y = npcAction->startPos.y; + attachedPos->z = npcAction->startPos.z; + } +} + +void EnXc_DestroyFlame(EnXc* this) { + if (this->flameActor != NULL) { + Actor_Kill(this->flameActor); + this->flameActor = NULL; + } + Actor_Kill(&this->actor); +} + +static s32 D_80B41DA8 = 1; +void EnXc_InitFlame(EnXc* this, GlobalContext* globalCtx) { + s32 pad; + s16 sceneNum = globalCtx->sceneNum; + + if (sceneNum == SCENE_SPOT17) { + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, 0); + if (npcAction != NULL) { + s32 action = npcAction->action; + + if (D_80B41DA8 != action) { + if (action != 1) { + EnXc_SpawnFlame(this, globalCtx); + } + + if (action == 1) { + EnXc_DestroyFlame(this); + } + + D_80B41DA8 = action; + } + + EnXc_SetupFlamePos(this, globalCtx); + } + } +} + +void func_80B3D48C(EnXc* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + CsCmdActorAction* linkAction = csCtx->linkAction; + s16 yaw; + + if (linkAction != NULL) { + yaw = linkAction->urot.y + 0x8000; + } else { + Player* player = GET_PLAYER(globalCtx); + yaw = player->actor.world.rot.y + 0x8000; + } + + this->actor.shape.rot.y = this->actor.world.rot.y = yaw; +} + +AnimationHeader* EnXc_GetCurrentHarpAnim(GlobalContext* globalCtx, s32 index) { + AnimationHeader* animation = &gSheikPlayingHarp5Anim; + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, index); + + if (npcAction != NULL) { + u16 action = npcAction->action; + + if (action == 11) { + animation = &gSheikPlayingHarp3Anim; + } else if (action == 12) { + animation = &gSheikPlayingHarp2Anim; + } else if (action == 13) { + animation = &gSheikPlayingHarp4Anim; + } else if (action == 23) { + animation = &gSheikPlayingHarpAnim; + } else { + animation = &gSheikPlayingHarp5Anim; + } + } + return animation; +} + +void EnXc_CalcXZAccel(EnXc* this) { + f32 timer = this->timer; + f32* speedXZ = &this->actor.speedXZ; + + if (timer < 9.0f) { + *speedXZ = 0.0f; + } else if (timer < 3.0f) { + *speedXZ = (((kREG(2) * 0.01f) + 1.2f) / 3.0f) * (timer - 9.0f); + } else { + *speedXZ = (kREG(2) * 0.01f) + 1.2f; + } + + Actor_MoveForward(&this->actor); +} + +void func_80B3D644(EnXc* this) { + Actor_MoveForward(&this->actor); +} + +void EnXc_CalcXZSpeed(EnXc* this) { + f32 timer = this->timer; + f32* speedXZ = &this->actor.speedXZ; + + if (timer < 3.0f) { + *speedXZ = (((kREG(2) * 0.01f) + 1.2f) / 3.0f) * (3.0f - timer); + } else { + *speedXZ = 0.0f; + } + Actor_MoveForward(&this->actor); +} + +void func_80B3D6F0(EnXc* this) { + EnXc_CalcXZAccel(this); +} + +void func_80B3D710(EnXc* this) { + Actor_MoveForward(&this->actor); +} + +void func_80B3D730(EnXc* this) { + EnXc_CalcXZSpeed(this); +} + +void func_80B3D750(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_MinuetCS(this, globalCtx) && EnXc_BoleroCS(this, globalCtx)) { + this->action = SHEIK_ACTION_WAIT; + } +} + +void EnXc_SetupFallFromSkyAction(EnXc* this, GlobalContext* globalCtx) { + s32 pad; + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->state != 0) { + CsCmdActorAction* npcAction = csCtx->npcActions[4]; + + if (npcAction && npcAction->action == 2) { + s32 pad; + Vec3f* pos = &this->actor.world.pos; + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSheikFallingFromSkyAnim); + + this->action = SHEIK_ACTION_GRACEFUL_FALL; + this->drawMode = SHEIK_DRAW_DEFAULT; + + pos->x = npcAction->startPos.x; + pos->y = npcAction->startPos.y; + pos->z = npcAction->startPos.z; + + func_80B3D48C(this, globalCtx); + func_80B3C964(this, globalCtx); + Animation_Change(skelAnime, &gSheikFallingFromSkyAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, 0.0f); + func_80B3D118(globalCtx); + } + } +} + +void func_80B3D8A4(EnXc* this, GlobalContext* globalCtx, s32 animFinished) { + if (animFinished) { + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSheikWalkingAnim); + + Animation_Change(skelAnime, &gSheikWalkingAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, -8.0f); + + this->action = SHEIK_ACTION_ACCEL; + this->timer = 0.0f; + + func_80B3C9DC(this); + this->actor.gravity = -((kREG(1) * 0.01f) + 13.0f); + this->actor.minVelocityY = -((kREG(1) * 0.01f) + 13.0f); + } else { + func_80B3C8CC(this, globalCtx); + } +} + +void EnXc_SetupWalkAction(EnXc* this) { + f32* timer = &this->timer; + + *timer += 1.0f; + if (*timer >= 12.0f) { + this->actor.speedXZ = (kREG(2) * 0.01f) + 1.2f; + this->action = SHEIK_ACTION_WALK; + } +} + +void EnXc_SetupHaltAction(EnXc* this) { + SkelAnime* skelAnime = &this->skelAnime; + f32 xzDistToPlayer = this->actor.xzDistToPlayer; + + if (xzDistToPlayer <= (kREG(3) + 95.0f)) { + f32 frameCount = Animation_GetLastFrame(&gSheikIdleAnim); + + Animation_Change(skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, -12.0f); + this->action = SHEIK_ACTION_HALT; + this->timer = 0.0f; + } +} + +void EnXc_SetupStoppedAction(EnXc* this) { + f32* timer = &this->timer; + + *timer += 1.0f; + if (*timer >= 12.0f) { + this->action = SHEIK_ACTION_STOPPED; + this->actor.speedXZ = 0.0f; + } +} + +void func_80B3DAF0(EnXc* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, 4); + u16 action; + + if (npcAction && + (action = npcAction->action, action == 3 || action == 11 || action == 12 || action == 13 || action == 23)) { + f32 frameCount; + + frameCount = Animation_GetLastFrame(&gSheikPullingOutHarpAnim); + Animation_Change(&this->skelAnime, &gSheikPullingOutHarpAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, -4.0f); + this->action = SHEIK_ACTION_7; + this->drawMode = SHEIK_DRAW_PULLING_OUT_HARP; + } +} + +void EnXc_SetupInitialHarpAction(EnXc* this, s32 animFinished) { + SkelAnime* skelAnime; + f32 frameCount; + + if (animFinished) { + skelAnime = &this->skelAnime; + frameCount = Animation_GetLastFrame(&gSheikInitialHarpAnim); + Animation_Change(skelAnime, &gSheikInitialHarpAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_HARP_READY; + this->drawMode = SHEIK_DRAW_HARP; + } +} + +void EnXc_SetupPlayingHarpAction(EnXc* this, GlobalContext* globalCtx, s32 animFinished) { + s32 pad; + SkelAnime* skelAnime; + AnimationHeader* animation; + f32 frameCount; + + if (animFinished) { + skelAnime = &this->skelAnime; + animation = EnXc_GetCurrentHarpAnim(globalCtx, 4); + frameCount = Animation_GetLastFrame(animation); + Animation_Change(skelAnime, animation, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, -8.0f); + this->action = SHEIK_PLAYING_HARP; + this->drawMode = SHEIK_DRAW_HARP; + } +} + +void func_80B3DCA8(EnXc* this, GlobalContext* globalCtx) { + f32 frameCount; + + if (globalCtx->csCtx.state != 0) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[4]; + + if (npcAction != NULL && npcAction->action == 8) { + frameCount = Animation_GetLastFrame(&gSheikInitialHarpAnim); + Animation_Change(&this->skelAnime, &gSheikInitialHarpAnim, 0.0f, frameCount, frameCount, ANIMMODE_LOOP, + -8.0f); + this->action = SHEIK_ACTION_10; + } + } +} + +void EnXc_SetupHarpPutawayAction(EnXc* this, GlobalContext* globalCtx) { + f32 curFrame; + f32 animFrameCount; + + if (EnXc_CompareCsAction(this, globalCtx, 5, 4)) { + curFrame = this->skelAnime.curFrame; + animFrameCount = this->skelAnime.endFrame; + if (curFrame >= animFrameCount) { + Animation_Change(&this->skelAnime, &gSheikInitialHarpAnim, -1.0f, + Animation_GetLastFrame(&gSheikInitialHarpAnim), 0.0f, ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_PUT_HARP_AWAY; + } + } else if (EnXc_CsActionsAreNotEqual(this, globalCtx, 8, 4)) { + EnXc_SetupPlayingHarpAction(this, globalCtx, true); + } +} + +void func_80B3DE00(EnXc* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gSheikPullingOutHarpAnim, -1.0f, + Animation_GetLastFrame(&gSheikPullingOutHarpAnim), 0.0f, ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_12; + this->drawMode = SHEIK_DRAW_PULLING_OUT_HARP; + } +} + +void func_80B3DE78(EnXc* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gSheikIdleAnim), + ANIMMODE_LOOP, 0.0f); + this->action = SHEIK_ACTION_13; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->timer = 0.0f; + } +} + +void EnXc_SetupReverseAccel(EnXc* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != 0) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[4]; + + if (npcAction != NULL && npcAction->action == 4) { + Animation_Change(&this->skelAnime, &gSheikWalkingAnim, -1.0f, Animation_GetLastFrame(&gSheikWalkingAnim), + 0.0f, ANIMMODE_LOOP, -12.0f); + this->action = SHEIK_ACTION_REVERSE_ACCEL; + this->actor.world.rot.y += 0x8000; + this->timer = 0.0f; + } + } +} + +void EnXc_SetupReverseWalkAction(EnXc* this) { + this->timer++; + if (this->timer >= 12.0f) { + this->actor.speedXZ = (kREG(2) * 0.01f) + 1.2f; + this->action = SHEIK_ACTION_REVERSE_WALK; + } +} + +void EnXc_SetupReverseHaltAction(EnXc* this) { + f32 xzDistToPlayer = this->actor.xzDistToPlayer; + + if (xzDistToPlayer >= kREG(5) + 140.0f) { + Animation_Change(&this->skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gSheikIdleAnim), + ANIMMODE_LOOP, -12.0f); + this->action = SHEIK_ACTION_REVERSE_HALT; + this->timer = 0.0f; + } +} + +void EnXc_SetupNutThrow(EnXc* this) { + this->timer++; + if (this->timer >= 12.0f) { + Animation_Change(&this->skelAnime, &gSheikThrowingNutAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gSheikThrowingNutAnim), ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_THROW_NUT; + this->timer = 0.0f; + this->actor.speedXZ = 0.0f; + } +} + +void func_80B3E164(EnXc* this, GlobalContext* globalCtx) { + this->timer++; + if (this->timer >= 30.0f) { + this->action = SHEIK_ACTION_DELETE; + EnXc_SpawnNut(this, globalCtx); + } +} + +void EnXc_SetupDisappear(EnXc* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != 0) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[4]; + + if (npcAction != NULL && npcAction->action == 9) { + s16 sceneNum = globalCtx->sceneNum; + + // Sheik fades away if end of Bolero CS, kill actor otherwise + if (sceneNum == SCENE_SPOT17) { + this->action = SHEIK_ACTION_FADE; + this->drawMode = SHEIK_DRAW_NOTHING; + this->actor.shape.shadowAlpha = 0; + } else { + Actor_Kill(&this->actor); + } + } + } +} + +void EnXc_ActionFunc0(EnXc* this, GlobalContext* globalCtx) { + EnXc_SetColossusAppearSFX(this, globalCtx); + EnXc_SetColossusWindSFX(globalCtx); + func_80B3D750(this, globalCtx); +} + +void EnXc_ActionFunc1(EnXc* this, GlobalContext* globalCtx) { + EnXc_SetColossusAppearSFX(this, globalCtx); + EnXc_SetColossusWindSFX(globalCtx); + EnXc_SetupFallFromSkyAction(this, globalCtx); +} + +void EnXc_GracefulFall(EnXc* this, GlobalContext* globalCtx) { + s32 animFinished = EnXc_AnimIsFinished(this); + + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetLandingSFX(this, globalCtx); + EnXc_SetColossusAppearSFX(this, globalCtx); + EnXc_SetColossusWindSFX(globalCtx); + func_80B3D8A4(this, globalCtx, animFinished); +} + +void EnXc_Accelerate(EnXc* this, GlobalContext* globalCtx) { + EnXc_CalcXZAccel(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_SetupWalkAction(this); +} + +void EnXc_Walk(EnXc* this, GlobalContext* globalCtx) { + func_80B3D644(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_SetupHaltAction(this); +} + +void EnXc_Stopped(EnXc* this, GlobalContext* globalCtx) { + EnXc_CalcXZSpeed(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_SetupStoppedAction(this); +} + +void EnXc_ActionFunc6(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + func_80B3DAF0(this, globalCtx); +} + +void EnXc_ActionFunc7(EnXc* this, GlobalContext* globalCtx) { + s32 animFinished = EnXc_AnimIsFinished(this); + + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupInitialHarpAction(this, animFinished); +} + +void EnXc_ActionFunc8(EnXc* this, GlobalContext* globalCtx) { + s32 animFinished = EnXc_AnimIsFinished(this); + + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupPlayingHarpAction(this, globalCtx, animFinished); +} + +void EnXc_ActionFunc9(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + func_80B3DCA8(this, globalCtx); +} + +void EnXc_ActionFunc10(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupHarpPutawayAction(this, globalCtx); +} + +void EnXc_ActionFunc11(EnXc* this, GlobalContext* globalCtx) { + s32 animFinished = EnXc_AnimIsFinished(this); + + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + func_80B3DE00(this, animFinished); +} + +void EnXc_ActionFunc12(EnXc* this, GlobalContext* globalCtx) { + s32 animFinished = EnXc_AnimIsFinished(this); + + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + func_80B3DE78(this, animFinished); +} + +void EnXc_ActionFunc13(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_InitFlame(this, globalCtx); + EnXc_SetupReverseAccel(this, globalCtx); +} + +void EnXc_ReverseAccelerate(EnXc* this, GlobalContext* globalCtx) { + func_80B3D6F0(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_InitFlame(this, globalCtx); + EnXc_SetupReverseWalkAction(this); +} + +void EnXc_ActionFunc15(EnXc* this, GlobalContext* globalCtx) { + func_80B3D710(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_InitFlame(this, globalCtx); + EnXc_SetupReverseHaltAction(this); +} + +void EnXc_HaltAndWaitToThrowNut(EnXc* this, GlobalContext* globalCtx) { + func_80B3D730(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_InitFlame(this, globalCtx); + EnXc_SetupNutThrow(this); +} + +void EnXc_ThrowNut(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetNutThrowSFX(this, globalCtx); + EnXc_InitFlame(this, globalCtx); + func_80B3E164(this, globalCtx); +} + +void EnXc_Delete(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_InitFlame(this, globalCtx); + EnXc_SetupDisappear(this, globalCtx); +} + +void EnXc_Fade(EnXc* this, GlobalContext* globalCtx) { + EnXc_InitFlame(this, globalCtx); +} + +void func_80B3E87C(Gfx** dList, EnXc* this) { + f32 currentFrame = this->skelAnime.curFrame; + + if (currentFrame >= 34.0f) { + *dList = gSheikHarpDL; + } +} + +s32 EnXc_PullingOutHarpOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnXc* this = (EnXc*)thisx; + + if (limbIndex == 12) { + func_80B3E87C(dList, this); + } + + return 0; +} + +s32 EnXc_HarpOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + EnXc* this = (EnXc*)thisx; + + if (limbIndex == 12) { + *dList = gSheikHarpDL; + } + + return 0; +} + +void EnXc_DrawPullingOutHarp(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + s32 pad; + s16 eyePattern = this->eyeIdx; + void* eyeTexture = sEyeTextures[eyePattern]; + SkelAnime* skelAnime = &this->skelAnime; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad2; + + OPEN_DISPS(gfxCtx, "../z_en_oA2_inSpot05.c", 1444); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 20, 0); + gDPSetEnvColor(POLY_OPA_DISP++, 60, 0, 0, 0); + + func_80093D18(gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnXc_PullingOutHarpOverrideLimbDraw, NULL, this); + CLOSE_DISPS(gfxCtx, "../z_en_oA2_inSpot05.c", 1497); +} + +void EnXc_DrawHarp(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + s32 pad; + s16 eyePattern = this->eyeIdx; + void* eyeTexture = sEyeTextures[eyePattern]; + SkelAnime* skelAnime = &this->skelAnime; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad2; + + OPEN_DISPS(gfxCtx, "../z_en_oA2_inSpot05.c", 1511); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 20, 0); + gDPSetEnvColor(POLY_OPA_DISP++, 60, 0, 0, 0); + + func_80093D18(gfxCtx); + func_8002EBCC(&this->actor, globalCtx, 0); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnXc_HarpOverrideLimbDraw, NULL, this); + CLOSE_DISPS(gfxCtx, "../z_en_oA2_inSpot05.c", 1564); +} + +void func_80B3EBF0(EnXc* this, GlobalContext* globalCtx) { + this->action = SHEIK_ACTION_20; +} + +void func_80B3EC00(EnXc* this) { + this->action = SHEIK_ACTION_21; +} + +void func_80B3EC0C(EnXc* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->state != 0) { + CsCmdActorAction* npcAction = csCtx->npcActions[4]; + + if ((npcAction != NULL) && (npcAction->action != 1)) { + PosRot* posRot = &this->actor.world; + Vec3i* startPos = &npcAction->startPos; + ActorShape* shape = &this->actor.shape; + + posRot->pos.x = startPos->x; + posRot->pos.y = startPos->y; + posRot->pos.z = startPos->z; + + posRot->rot.y = shape->rot.y = npcAction->rot.y; + + this->action = SHEIK_ACTION_22; + this->drawMode = SHEIK_DRAW_DEFAULT; + } + } +} + +void func_80B3EC90(EnXc* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->state != 0) { + CsCmdActorAction* npcAction = csCtx->npcActions[4]; + + if (npcAction != NULL && npcAction->action != 6) { + func_80B3C9EC(this); + } + } +} + +void func_80B3ECD8(EnXc* this) { + this->timer++; + if (this->timer >= 12.0f) { + this->actor.speedXZ = kREG(2) * 0.01f + 1.2f; + this->action = SHEIK_ACTION_24; + } +} + +void EnXc_ActionFunc20(EnXc* this, GlobalContext* globalCtx) { + func_80B3EC00(this); +} + +void EnXc_ActionFunc21(EnXc* this, GlobalContext* globalCtx) { + func_80B3EC0C(this, globalCtx); +} + +void EnXc_ActionFunc22(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + func_80B3EC90(this, globalCtx); +} + +void EnXc_ActionFunc23(EnXc* this, GlobalContext* globalCtx) { + func_80B3D6F0(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + func_80B3ECD8(this); +} + +void EnXc_ActionFunc24(EnXc* this, GlobalContext* globalCtx) { +} + +void EnXc_ActionFunc25(EnXc* this, GlobalContext* globalCtx) { +} + +void EnXc_ActionFunc26(EnXc* this, GlobalContext* globalCtx) { +} + +void EnXc_ActionFunc27(EnXc* this, GlobalContext* globalCtx) { +} + +void EnXc_ActionFunc28(EnXc* this, GlobalContext* globalCtx) { +} + +void func_80B3EE64(EnXc* this, GlobalContext* globalCtx) { + this->action = SHEIK_ACTION_SERENADE; +} + +void func_80B3EE74(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_SerenadeCS(this, globalCtx)) { + this->action = SHEIK_ACTION_30; + } +} + +void func_80B3EEA4(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_30, SHEIK_ACTION_31); +} + +void func_80B3EEC8(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_31, SHEIK_ACTION_32); +} + +void func_80B3EEEC(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_32, SHEIK_ACTION_33); +} + +void func_80B3EF10(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_33, SHEIK_ACTION_34); +} + +void func_80B3EF34(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_34, SHEIK_ACTION_35); +} + +void func_80B3EF58(EnXc* this) { + func_80B3C7D4(this, SHEIK_ACTION_35, SHEIK_ACTION_36, SHEIK_ACTION_34); +} + +void func_80B3EF80(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_36, SHEIK_ACTION_37); +} + +void func_80B3EFA4(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_37, SHEIK_ACTION_38); +} + +void func_80B3EFC8(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_38, SHEIK_ACTION_39); +} + +void func_80B3EFEC(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_39, SHEIK_ACTION_40); +} + +void func_80B3F010(EnXc* this) { + f32 xzDistToPlayer = this->actor.xzDistToPlayer; + + if (kREG(5) + 140.0f <= xzDistToPlayer) { + Animation_Change(&this->skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gSheikIdleAnim), + ANIMMODE_LOOP, -12.0f); + this->action = SHEIK_ACTION_41; + this->timer = 0.0f; + } +} + +void func_80B3F0B8(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_41, SHEIK_ACTION_42); +} + +void func_80B3F0DC(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_42, SHEIK_ACTION_43); +} + +void func_80B3F100(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_43, SHEIK_ACTION_44); +} + +void EnXc_Serenade(EnXc* this, GlobalContext* globalCtx) { + func_80B3EE74(this, globalCtx); +} + +void EnXc_ActionFunc30(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc21(this, globalCtx); + func_80B3EEA4(this); +} + +void EnXc_ActionFunc31(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc6(this, globalCtx); + func_80B3C588(this, globalCtx, 4); + func_80B3EEC8(this); +} + +void EnXc_ActionFunc32(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc7(this, globalCtx); + func_80B3EEEC(this); +} + +void EnXc_ActionFunc33(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc8(this, globalCtx); + func_80B3EF10(this); +} + +void EnXc_ActionFunc34(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc9(this, globalCtx); + func_80B3EF34(this); +} + +void EnXc_ActionFunc35(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc10(this, globalCtx); + func_80B3EF58(this); +} + +void EnXc_ActionFunc36(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc11(this, globalCtx); + func_80B3EF80(this); +} + +void EnXc_ActionFunc37(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc12(this, globalCtx); + func_80B3EFA4(this); +} + +void EnXc_ActionFunc38(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc13(this, globalCtx); + func_80B3EFC8(this); +} + +void EnXc_ActionFunc39(EnXc* this, GlobalContext* globalCtx) { + EnXc_ReverseAccelerate(this, globalCtx); + func_80B3EFEC(this); +} + +void EnXc_ActionFunc40(EnXc* this, GlobalContext* globalCtx) { + func_80B3D710(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + func_80B3F010(this); +} + +void EnXc_ActionFunc41(EnXc* this, GlobalContext* globalCtx) { + EnXc_HaltAndWaitToThrowNut(this, globalCtx); + func_80B3F0B8(this); +} + +void EnXc_ActionFunc42(EnXc* this, GlobalContext* globalCtx) { + EnXc_ThrowNut(this, globalCtx); + func_80B3F0DC(this); +} + +void EnXc_ActionFunc43(EnXc* this, GlobalContext* globalCtx) { + EnXc_Delete(this, globalCtx); + func_80B3F100(this); +} + +void EnXc_ActionFunc44(EnXc* this, GlobalContext* globalCtx) { +} + +void func_80B3F3C8(EnXc* this, GlobalContext* globalCtx) { + this->action = SHEIK_ACTION_45; +} + +void func_80B3F3D8() { + func_800788CC(NA_SE_PL_SKIP); +} + +void EnXc_PlayDiveSFX(Vec3f* src, GlobalContext* globalCtx) { + f32 wDest[2]; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, src, &D_80B42DA0, wDest); + func_80078914(&D_80B42DA0, NA_SE_EV_DIVE_INTO_WATER); +} + +void EnXc_LakeHyliaDive(GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = npcAction = EnXc_GetCsCmd(globalCtx, 0); + + if (npcAction != NULL) { + Vec3f startPos; + + startPos.x = npcAction->startPos.x; + startPos.y = npcAction->startPos.y; + startPos.z = npcAction->startPos.z; + + EffectSsGRipple_Spawn(globalCtx, &startPos, 100, 500, 0); + EffectSsGRipple_Spawn(globalCtx, &startPos, 100, 500, 10); + EffectSsGRipple_Spawn(globalCtx, &startPos, 100, 500, 20); + EffectSsGSplash_Spawn(globalCtx, &startPos, NULL, NULL, 1, 0); + EnXc_PlayDiveSFX(&startPos, globalCtx); + } +} + +void func_80B3F534(GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + u16 frameCount = csCtx->frames; + + if (frameCount == 310) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, -1044.0f, -1243.0f, 7458.0f, 0, 0, 0, + WARP_DESTINATION); + } +} + +static s32 D_80B41DAC = 1; +void func_80B3F59C(EnXc* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, 0); + + if (npcAction != NULL) { + s32 action = npcAction->action; + + if (action != D_80B41DAC) { + switch (action) { + case 2: + func_80B3F3D8(); + break; + case 3: + EnXc_LakeHyliaDive(globalCtx); + break; + default: + break; + } + D_80B41DAC = action; + } + } +} + +void func_80B3F620(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_45, SHEIK_ACTION_46); +} + +void func_80B3F644(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_46, SHEIK_ACTION_47); +} + +void func_80B3F668(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CompareCsAction(this, globalCtx, 4, 4)) { + EnXc_ChangeAnimation(this, &gSheikWalkingAnim, ANIMMODE_LOOP, -12.0f, true); + this->action = SHEIK_ACTION_48; + this->actor.world.rot.y += 0x8000; + this->timer = 0.0f; + } +} + +void func_80B3F6DC(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_48, SHEIK_ACTION_49); +} + +void EnXc_SetupKneelAction(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CompareCsAction(this, globalCtx, 16, 4)) { + EnXc_ChangeAnimation(this, &gSheikKneelingAnim, ANIMMODE_LOOP, 0.0f, false); + this->action = SHEIK_ACTION_KNEEL; + } +} + +void func_80B3F754(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CompareCsAction(this, globalCtx, 22, 4)) { + EnXc_ChangeAnimation(this, &gSheikAnim_01A048, ANIMMODE_LOOP, 0.0f, false); + this->action = SHEIK_ACTION_51; + func_80B3C588(this, globalCtx, 4); + } +} + +void func_80B3F7BC(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CompareCsAction(this, globalCtx, 9, 4)) { + this->action = SHEIK_ACTION_52; + this->drawMode = SHEIK_DRAW_NOTHING; + } +} + +void EnXc_ActionFunc45(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc20(this, globalCtx); + func_80B3F620(this); +} + +void EnXc_ActionFunc46(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc21(this, globalCtx); + func_80B3F644(this); +} + +void EnXc_ActionFunc47(EnXc* this, GlobalContext* globalCtx) { + func_80B3F534(globalCtx); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + func_80B3C588(this, globalCtx, 4); + func_80B3F668(this, globalCtx); +} + +void EnXc_ActionFunc48(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc23(this, globalCtx); + func_80B3F6DC(this); +} + +void EnXc_ActionFunc49(EnXc* this, GlobalContext* globalCtx) { + func_80B3D710(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetWalkingSFX(this, globalCtx); + EnXc_SetupKneelAction(this, globalCtx); +} + +void EnXc_Kneel(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + func_80B3F59C(this, globalCtx); + func_80B3C588(this, globalCtx, 4); + func_80B3F754(this, globalCtx); +} + +void EnXc_ActionFunc51(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + func_80B3F59C(this, globalCtx); + func_80B3C620(this, globalCtx, 4); + func_80B3F7BC(this, globalCtx); +} + +void EnXc_ActionFunc52(EnXc* this, GlobalContext* globalCtx) { + func_80B3F59C(this, globalCtx); +} + +void func_80B3FA08(EnXc* this, GlobalContext* globalCtx) { + this->action = SHEIK_ACTION_53; + this->triforceAngle = kREG(24) + 0x53FC; +} + +void func_80B3FA2C(void) { + func_800F3F3C(1); +} + +void EnXc_PlayTriforceSFX(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + + if (this->unk_2A8) { + s32 pad; + Vec3f src; + Vec3f pos; + Vec3f sp1C = { 0.0f, 0.0f, 0.0f }; + f32 wDest; + + Matrix_MultVec3f(&sp1C, &src); + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &src, &pos, &wDest); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &pos, 80, NA_SE_EV_TRIFORCE_MARK); + this->unk_2A8 = 0; + } +} + +void func_80B3FAE0(EnXc* this) { + if (Animation_OnFrame(&this->skelAnime, 38.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_SK_SHOUT); + func_80B3FA2C(); + } +} + +void EnXc_CalcTriforce(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + + if (EnXc_CompareCsAction(this, globalCtx, 21, 4)) { + this->unk_274 = 1; + if (this->unk_2AC == 0) { + this->unk_2AC = 1; + this->unk_2A8 = 1; + } + } else if (EnXc_CompareCsAction(this, globalCtx, 19, 4)) { + this->unk_274 = 2; + } + if (this->unk_274 != 0) { + f32* timer = &this->timer; + s32* prim = this->triforcePrimColor; + s32* env = this->triforceEnvColor; + f32* scale = this->triforceScale; + + if (this->unk_274 == 1) { + if (*timer < kREG(25) + 40.0f) { + f32 div = *timer / (kREG(25) + 40.0f); + + prim[2] = -85.0f * div + 255; + prim[3] = 255.0f * div; + env[1] = 100.0f * div + 100; + *timer += 1.0f; + } else { + prim[2] = 170; + prim[3] = 255; + env[1] = 200; + } + scale[0] = kREG(19) * 0.1f + 40.0f; + scale[1] = kREG(20) * 0.1f + 40.0f; + scale[2] = kREG(21) * 0.1f + 40.0f; + } else if (this->unk_274 == 2) { + f32 maxTime = (kREG(25) + 40.0f) + (kREG(27) + 90.0f); + + if (*timer < maxTime) { + f32 div = (*timer - (kREG(25) + 40.0f)) / (kREG(27) + 90.0f); + scale[0] = (kREG(19) * 0.1f + 40.0f) + div * ((kREG(26) + 50.0f) * (kREG(19) * 0.1f + 40.0f)); + scale[1] = (kREG(20) * 0.1f + 40.0f) + div * ((kREG(26) + 50.0f) * (kREG(20) * 0.1f + 40.0f)); + scale[2] = (kREG(21) * 0.1f + 40.0f) + div * ((kREG(26) + 50.0f) * (kREG(21) * 0.1f + 40.0f)); + *timer += 1.0f; + } else { + scale[0] = (kREG(19) * 0.1f + 40.0f) * (kREG(26) + 50.0f); + scale[1] = (kREG(20) * 0.1f + 40.0f) * (kREG(26) + 50.0f); + scale[2] = (kREG(21) * 0.1f + 40.0f) * (kREG(26) + 50.0f); + } + this->triforceAngle += (s16)(kREG(28) + 0x2EE0); + } + } +} + +void func_80B3FF0C(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CsActionsAreNotEqual(this, globalCtx, 1, 4)) { + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->state != 0) { + CsCmdActorAction* npcAction = globalCtx->csCtx.npcActions[4]; + + if (npcAction != NULL) { + PosRot* posRot = &this->actor.world; + ActorShape* shape = &this->actor.shape; + Vec3i* startPos = &npcAction->startPos; + + posRot->pos.x = startPos->x; + posRot->pos.y = startPos->y; + posRot->pos.z = startPos->z; + + posRot->rot.y = shape->rot.y = npcAction->rot.y; + } + } + + this->action = SHEIK_ACTION_54; + this->drawMode = SHEIK_DRAW_DEFAULT; + } +} + +void EnXc_SetupShowTriforceAction(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CompareCsAction(this, globalCtx, 10, 4)) { + Animation_Change(&this->skelAnime, &gSheikShowingTriforceOnHandAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gSheikShowingTriforceOnHandAnim), ANIMMODE_ONCE, -8.0f); + this->action = SHEIK_ACTION_SHOW_TRIFORCE; + this->drawMode = SHEIK_DRAW_TRIFORCE; + } +} + +void EnXc_SetupShowTriforceIdleAction(EnXc* this, s32 animFinished) { + if (animFinished) { + Animation_Change(&this->skelAnime, &gSheikShowingTriforceOnHandIdleAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gSheikShowingTriforceOnHandIdleAnim), ANIMMODE_LOOP, 0.0f); + this->action = SHEIK_ACTION_SHOW_TRIFORCE_IDLE; + } +} +void func_80B400AC(EnXc* this, GlobalContext* globalCtx) { + if (EnXc_CompareCsAction(this, globalCtx, 9, 4)) { + Actor_Kill(&this->actor); + } +} + +void EnXc_ActionFunc53(EnXc* this, GlobalContext* globalCtx) { + func_80B3FF0C(this, globalCtx); +} + +void EnXc_ActionFunc54(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupShowTriforceAction(this, globalCtx); + func_80B3C888(this, globalCtx); +} + +void EnXc_ShowTriforce(EnXc* this, GlobalContext* globalCtx) { + s32 animFinished = EnXc_AnimIsFinished(this); + + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_CalcTriforce(&this->actor, globalCtx); + func_80B3FAE0(this); + EnXc_SetupShowTriforceIdleAction(this, animFinished); + func_80B3C888(this, globalCtx); +} + +void EnXc_ShowTriforceIdle(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_CalcTriforce(&this->actor, globalCtx); + func_80B400AC(this, globalCtx); +} + +s32 EnXc_TriforceOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + if (limbIndex == 15) { + *dList = gSheikDL_011620; + } + return 0; +} + +void EnXc_TriforcePostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + s32 pad[2]; + EnXc* this = (EnXc*)thisx; + + if (limbIndex == 15) { + Vec3f vec = { 0.0f, 0.0f, 0.0f }; + EnXc_PlayTriforceSFX(&this->actor, globalCtx); + Matrix_MultVec3f(&vec, &this->handPos); + this->unk_2BC = 1; + } +} + +void EnXc_DrawTriforce(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + s32 pad; + s16 eyeIdx = this->eyeIdx; + void* eyeTexture = sEyeTextures[eyeIdx]; + SkelAnime* skelAnime = &this->skelAnime; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad2; + + OPEN_DISPS(gfxCtx, "../z_en_oA2_inMetamol.c", 565); + if (this->unk_2BC != 0) { + Mtx* mtx = Graph_Alloc(gfxCtx, sizeof(Mtx)); + s32* primColor = this->triforcePrimColor; + s32* envColor = this->triforceEnvColor; + f32* scale = this->triforceScale; + + Matrix_Push(); + Matrix_Translate(kREG(16) + 100.0f, kREG(17) + 4460.0f, kREG(18) + 1190.0f, MTXMODE_APPLY); + Matrix_RotateZYX(kREG(22), kREG(23), this->triforceAngle, MTXMODE_APPLY); + Matrix_Scale(scale[0], scale[1], scale[2], MTXMODE_APPLY); + Matrix_ToMtx(mtx, "../z_en_oA2_inMetamol.c", 602); + Matrix_Pop(); + func_80093D84(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, primColor[2], primColor[3]); + gDPSetEnvColor(POLY_XLU_DISP++, 255, envColor[1], 0, 128); + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gSheikDL_012970); + } + + func_8002EBCC(thisx, globalCtx, 0); + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTexture)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTexture)); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnXc_TriforceOverrideLimbDraw, EnXc_TriforcePostLimbDraw, this); + CLOSE_DISPS(gfxCtx, "../z_en_oA2_inMetamol.c", 668); +} + +void func_80B40590(EnXc* this, GlobalContext* globalCtx) { + this->action = SHEIK_ACTION_NOCTURNE_INIT; + this->drawMode = SHEIK_DRAW_SQUINT; +} + +void EnXc_SetThrownAroundSFX(EnXc* this) { + SkelAnime* skelAnime = &this->skelAnime; + + if (Animation_OnFrame(skelAnime, 9.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_BOUND_GRASS); + func_80078914(&this->actor.projectedPos, NA_SE_VO_SK_CRASH); + } else if (Animation_OnFrame(skelAnime, 26.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_BOUND_GRASS); + } else if (Animation_OnFrame(skelAnime, 28.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_GRASS); + } else if (Animation_OnFrame(skelAnime, 34.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_GRASS); + } +} + +void EnXc_PlayLinkScreamSFX(EnXc* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames == 1455) { + func_800F3F3C(7); + } +} + +void EnXc_SetCrySFX(EnXc* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx = &globalCtx->csCtx; + + if (csCtx->frames == 869) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_SK_CRY_0); + } else if (csCtx->frames == 939) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_SK_CRY_1); + } +} + +void func_80B406F8(Actor* thisx) { + EnXc* this = (EnXc*)thisx; + + this->action = SHEIK_ACTION_NOCTURNE_INIT; + this->drawMode = SHEIK_DRAW_NOTHING; + this->actor.shape.shadowAlpha = 0; +} + +void EnXc_SetupIdleInNocturne(EnXc* this, GlobalContext* globalCtx) { + s32 pad; + ActorShape* actorShape = &this->actor.shape; + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSheikIdleAnim); + + func_80B3C9DC(this); + func_80B3C588(this, globalCtx, 4); + Animation_Change(skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + this->action = SHEIK_ACTION_NOCTURNE_IDLE; + this->drawMode = SHEIK_DRAW_SQUINT; + actorShape->shadowAlpha = 255; +} + +void EnXc_SetupDefenseStance(Actor* thisx) { + EnXc* this = (EnXc*)thisx; + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSheikDefenseStanceAnim); + + Animation_Change(skelAnime, &gSheikDefenseStanceAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, -8.0f); + this->action = SHEIK_ACTION_DEFENSE_STANCE; + this->drawMode = SHEIK_DRAW_DEFAULT; +} + +void EnXc_SetupContortions(EnXc* this, GlobalContext* globalCtx) { + s32 pad; + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSheikIdleAnim); + + Animation_Change(skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + func_80B3C588(this, globalCtx, 4); + func_80B3C964(this, globalCtx); + Animation_Change(skelAnime, &gSheikContortionsAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gSheikContortionsAnim), + ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_CONTORT; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 255; +} + +void EnXc_SetupFallInNocturne(EnXc* this, GlobalContext* globalCtx) { + s32 pad; + SkelAnime* skelAnime = &this->skelAnime; + f32 frameCount = Animation_GetLastFrame(&gSheikIdleAnim); + + Animation_Change(skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + func_80B3C588(this, globalCtx, 4); + func_80B3C964(this, globalCtx); + Animation_Change(skelAnime, &gSheikFallingFromContortionsAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gSheikFallingFromContortionsAnim), ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_NOCTURNE_FALL; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 255; +} + +void EnXc_SetupHittingGroundInNocturne(EnXc* this, GlobalContext* globalCtx) { + s32 pad[3]; + f32 frameCount = Animation_GetLastFrame(&gSheikHittingGroundAnim); + + func_80B3C9DC(this); + func_80B3C588(this, globalCtx, 4); + Animation_Change(&this->skelAnime, &gSheikHittingGroundAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_NOCTURNE_HIT_GROUND; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 255; +} + +void func_80B40A78(EnXc* this, GlobalContext* globalCtx) { + s32 pad[3]; + f32 frameCount = Animation_GetLastFrame(&gSheikHittingGroundAnim); + + func_80B3C9DC(this); + func_80B3C588(this, globalCtx, 4); + Animation_Change(&this->skelAnime, &gSheikHittingGroundAnim, 1.0f, 0.0f, frameCount, ANIMMODE_ONCE, 0.0f); + this->action = SHEIK_ACTION_63; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 255; +} + +void EnXc_SetupKneelInNocturne(EnXc* this, GlobalContext* globalCtx) { + s32 pad[3]; + f32 frameCount = Animation_GetLastFrame(&gSheikKneelingAnim); + + func_80B3C9DC(this); + func_80B3C588(this, globalCtx, 4); + Animation_Change(&this->skelAnime, &gSheikKneelingAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + this->action = SHEIK_ACTION_NOCTURNE_KNEEL; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 255; +} + +void func_80B40BB4(EnXc* this, GlobalContext* globalCtx) { + s32 pad[3]; + f32 frameCount = Animation_GetLastFrame(&gSheikIdleAnim); + func_80B3C9DC(this); + func_80B3C588(this, globalCtx, 4); + Animation_Change(&this->skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + this->action = SHEIK_ACTION_65; + this->drawMode = SHEIK_DRAW_DEFAULT; + this->actor.shape.shadowAlpha = 255; +} + +void func_80B40C50(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_65, SHEIK_ACTION_66); +} + +void func_80B40C74(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_66, SHEIK_ACTION_67); +} + +void func_80B40C98(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_67, SHEIK_ACTION_68); +} + +void func_80B40CBC(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_68, SHEIK_ACTION_69); +} + +void func_80B40CE0(EnXc* this) { + func_80B3C7D4(this, SHEIK_ACTION_69, SHEIK_ACTION_70, SHEIK_ACTION_68); +} + +void func_80B40D08(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_70, SHEIK_ACTION_71); +} + +void func_80B40D2C(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_71, SHEIK_ACTION_72); +} + +void func_80B40D50(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_72, SHEIK_ACTION_NOCTURNE_REVERSE_ACCEL); +} + +void func_80B40D74(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_NOCTURNE_REVERSE_ACCEL, SHEIK_ACTION_NOCTURNE_REVERSE_WALK); +} + +void EnXc_SetupReverseHaltInNocturneCS(EnXc* this) { + f32 xzDistToPlayer = this->actor.xzDistToPlayer; + + if (kREG(5) + 140.0f <= xzDistToPlayer) { + Animation_Change(&this->skelAnime, &gSheikIdleAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gSheikIdleAnim), + ANIMMODE_LOOP, -12.0f); + this->action = SHEIK_ACTION_NOCTURNE_REVERSE_HALT; + this->timer = 0.0f; + } +} + +void func_80B40E40(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_NOCTURNE_REVERSE_HALT, SHEIK_ACTION_NOCTURNE_THROW_NUT); +} + +void func_80B40E64(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_NOCTURNE_THROW_NUT, SHEIK_ACTION_77); +} + +void func_80B40E88(EnXc* this) { + EnXc_CheckAndSetAction(this, SHEIK_ACTION_77, SHEIK_ACTION_78); +} + +s32 EnXc_SetupNocturneState(Actor* thisx, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnXc_GetCsCmd(globalCtx, 4); + + if (npcAction != NULL) { + s32 action = npcAction->action; + EnXc* this = (EnXc*)thisx; + s32 prevAction = this->unk_26C; + + if (action != prevAction) { + switch (action) { + case 1: + func_80B406F8(thisx); + break; + case 6: + EnXc_SetupIdleInNocturne(this, globalCtx); + break; + case 20: + EnXc_SetupDefenseStance(thisx); + break; + case 18: + EnXc_SetupContortions(this, globalCtx); + break; + case 14: + EnXc_SetupFallInNocturne(this, globalCtx); + break; + case 19: + EnXc_SetupHittingGroundInNocturne(this, globalCtx); + break; + case 15: + func_80B40A78(this, globalCtx); + break; + case 16: + EnXc_SetupKneelInNocturne(this, globalCtx); + break; + case 17: + func_80B40BB4(this, globalCtx); + break; + case 9: + Actor_Kill(thisx); + break; + default: + osSyncPrintf("En_Oa2_Stalker_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + + this->unk_26C = action; + return 1; + } + } + return 0; +} + +void EnXc_InitialNocturneAction(EnXc* this, GlobalContext* globalCtx) { + EnXc_SetupNocturneState(&this->actor, globalCtx); +} + +void EnXc_IdleInNocturne(EnXc* this, GlobalContext* globalCtx) { + func_80B3C588(this, globalCtx, 4); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetupNocturneState(&this->actor, globalCtx); +} + +void EnXc_DefenseStance(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupNocturneState(&this->actor, globalCtx); +} + +void EnXc_Contort(EnXc* this, GlobalContext* globalCtx) { + EnXc_SetCrySFX(this, globalCtx); + EnXc_AnimIsFinished(this); + EnXc_SetEyePattern(this); + if (!EnXc_SetupNocturneState(&this->actor, globalCtx)) { + func_80B3C924(this, globalCtx); + EnXc_BgCheck(this, globalCtx); + } +} + +void EnXc_FallInNocturne(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_SetEyePattern(this); + EnXc_SetThrownAroundSFX(this); + if (!EnXc_SetupNocturneState(&this->actor, globalCtx)) { + func_80B3C8CC(this, globalCtx); + EnXc_BgCheck(this, globalCtx); + } +} + +void EnXc_HitGroundInNocturne(EnXc* this, GlobalContext* globalCtx) { + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupNocturneState(&this->actor, globalCtx); +} + +void EnXc_ActionFunc63(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_PlayLinkScreamSFX(this, globalCtx); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupNocturneState(&this->actor, globalCtx); +} + +void EnXc_KneelInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupNocturneState(&this->actor, globalCtx); +} + +void EnXc_ActionFunc65(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc6(this, globalCtx); + func_80B3C588(this, globalCtx, 4); + func_80B40C50(this); +} + +void EnXc_ActionFunc66(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc7(this, globalCtx); + func_80B40C74(this); +} + +void EnXc_ActionFunc67(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc8(this, globalCtx); + func_80B40C98(this); +} + +void EnXc_ActionFunc68(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc9(this, globalCtx); + func_80B40CBC(this); +} + +void EnXc_ActionFunc69(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc10(this, globalCtx); + func_80B40CE0(this); +} + +void EnXc_ActionFunc70(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc11(this, globalCtx); + func_80B40D08(this); +} + +void EnXc_ActionFunc71(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc12(this, globalCtx); + func_80B40D2C(this); +} + +void EnXc_ActionFunc72(EnXc* this, GlobalContext* globalCtx) { + EnXc_ActionFunc13(this, globalCtx); + func_80B40D50(this); +} + +void EnXc_ReverseAccelInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + EnXc_ReverseAccelerate(this, globalCtx); + func_80B40D74(this); +} + +void EnXc_ReverseWalkInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + func_80B3D710(this); + EnXc_AnimIsFinished(this); + EnXc_BgCheck(this, globalCtx); + EnXc_SetEyePattern(this); + EnXc_SetupReverseHaltInNocturneCS(this); +} + +void EnXc_ReverseHaltInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + EnXc_HaltAndWaitToThrowNut(this, globalCtx); + func_80B40E40(this); +} + +void EnXc_ThrowNutInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + EnXc_ThrowNut(this, globalCtx); + func_80B40E64(this); +} + +void EnXc_DeleteInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + EnXc_Delete(this, globalCtx); + func_80B40E88(this); +} + +void EnXc_KillInNocturneCS(EnXc* this, GlobalContext* globalCtx) { + Actor_Kill(&this->actor); +} + +void EnXc_DrawSquintingEyes(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + SkelAnime* skelAnime = &this->skelAnime; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_en_oA2_inStalker.c", 839); + func_80093D18(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gSheikEyeSquintingTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gSheikEyeSquintingTex)); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, NULL, NULL, + NULL); + CLOSE_DISPS(gfxCtx, "../z_en_oA2_inStalker.c", 854); +} + +void EnXc_InitTempleOfTime(EnXc* this, GlobalContext* globalCtx) { + if (LINK_IS_ADULT) { + if (!(gSaveContext.eventChkInf[12] & 0x20)) { + gSaveContext.eventChkInf[12] |= 0x20; + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTempleOfTimeFirstAdultCs); + gSaveContext.cutsceneTrigger = 1; + func_80B3EBF0(this, globalCtx); + } else if (!(gSaveContext.eventChkInf[5] & 0x20) && (gSaveContext.eventChkInf[4] & 0x100)) { + gSaveContext.eventChkInf[5] |= 0x20; + Item_Give(globalCtx, ITEM_SONG_PRELUDE); + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gTempleOfTimePreludeCs); + gSaveContext.cutsceneTrigger = 1; + this->action = SHEIK_ACTION_30; + } else if (!(gSaveContext.eventChkInf[5] & 0x20)) { + func_80B3C9EC(this); + } else { + Actor_Kill(&this->actor); + } + } else { + Actor_Kill(&this->actor); + } +} + +void EnXc_SetupDialogueAction(EnXc* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->action = SHEIK_ACTION_IN_DIALOGUE; + } else { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + if (INV_CONTENT(ITEM_HOOKSHOT) != ITEM_NONE) { + this->actor.textId = 0x7010; + } else { + this->actor.textId = 0x700F; + } + func_8002F2F4(&this->actor, globalCtx); + } +} + +void func_80B41798(EnXc* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->action = SHEIK_ACTION_BLOCK_PEDESTAL; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + } +} + +void EnXc_BlockingPedestalAction(EnXc* this, GlobalContext* globalCtx) { + EnXc_BgCheck(this, globalCtx); + EnXc_UpdateCollider(&this->actor, globalCtx); + EnXc_CalculateHeadTurn(this, globalCtx); + EnXc_AnimIsFinished(this); + EnXc_SetEyePattern(this); + EnXc_SetupDialogueAction(this, globalCtx); +} + +void EnXc_ActionFunc80(EnXc* this, GlobalContext* globalCtx) { + EnXc_BgCheck(this, globalCtx); + EnXc_UpdateCollider(&this->actor, globalCtx); + EnXc_CalculateHeadTurn(this, globalCtx); + EnXc_AnimIsFinished(this); + EnXc_SetEyePattern(this); + func_80B41798(this, globalCtx); +} + +static EnXcActionFunc sActionFuncs[] = { + EnXc_ActionFunc0, + EnXc_ActionFunc1, + EnXc_GracefulFall, + EnXc_Accelerate, + EnXc_Walk, + EnXc_Stopped, + EnXc_ActionFunc6, + EnXc_ActionFunc7, + EnXc_ActionFunc8, + EnXc_ActionFunc9, + EnXc_ActionFunc10, + EnXc_ActionFunc11, + EnXc_ActionFunc12, + EnXc_ActionFunc13, + EnXc_ReverseAccelerate, + EnXc_ActionFunc15, + EnXc_HaltAndWaitToThrowNut, + EnXc_ThrowNut, + EnXc_Delete, + EnXc_Fade, + EnXc_ActionFunc20, + EnXc_ActionFunc21, + EnXc_ActionFunc22, + EnXc_ActionFunc23, + EnXc_ActionFunc24, + EnXc_ActionFunc25, + EnXc_ActionFunc26, + EnXc_ActionFunc27, + EnXc_ActionFunc28, + EnXc_Serenade, + EnXc_ActionFunc30, + EnXc_ActionFunc31, + EnXc_ActionFunc32, + EnXc_ActionFunc33, + EnXc_ActionFunc34, + EnXc_ActionFunc35, + EnXc_ActionFunc36, + EnXc_ActionFunc37, + EnXc_ActionFunc38, + EnXc_ActionFunc39, + EnXc_ActionFunc40, + EnXc_ActionFunc41, + EnXc_ActionFunc42, + EnXc_ActionFunc43, + EnXc_ActionFunc44, + EnXc_ActionFunc45, + EnXc_ActionFunc46, + EnXc_ActionFunc47, + EnXc_ActionFunc48, + EnXc_ActionFunc49, + EnXc_Kneel, + EnXc_ActionFunc51, + EnXc_ActionFunc52, + EnXc_ActionFunc53, + EnXc_ActionFunc54, + EnXc_ShowTriforce, + EnXc_ShowTriforceIdle, + EnXc_InitialNocturneAction, + EnXc_IdleInNocturne, + EnXc_DefenseStance, + EnXc_Contort, + EnXc_FallInNocturne, + EnXc_HitGroundInNocturne, + EnXc_ActionFunc63, + EnXc_KneelInNocturneCS, + EnXc_ActionFunc65, + EnXc_ActionFunc66, + EnXc_ActionFunc67, + EnXc_ActionFunc68, + EnXc_ActionFunc69, + EnXc_ActionFunc70, + EnXc_ActionFunc71, + EnXc_ActionFunc72, + EnXc_ReverseAccelInNocturneCS, + EnXc_ReverseWalkInNocturneCS, + EnXc_ReverseHaltInNocturneCS, + EnXc_ThrowNutInNocturneCS, + EnXc_DeleteInNocturneCS, + EnXc_KillInNocturneCS, + EnXc_BlockingPedestalAction, + EnXc_ActionFunc80, +}; + +void EnXc_Update(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + s32 action = this->action; + + if ((action < 0) || (action >= ARRAY_COUNT(sActionFuncs)) || (sActionFuncs[action] == NULL)) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sActionFuncs[action](this, globalCtx); + } +} + +void EnXc_Init(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gSheikSkel, &gSheikIdleAnim, this->jointTable, this->morphTable, + ARRAY_COUNT(this->jointTable)); + EnXc_InitCollider(thisx, globalCtx); + + switch (this->actor.params) { + case SHEIK_TYPE_1: + func_80B3EBF0(this, globalCtx); + break; + case SHEIK_TYPE_2: // Beta Serenade Cutscene or Learning Prelude + func_80B3EE64(this, globalCtx); + break; + case SHEIK_TYPE_3: + func_80B3F3C8(this, globalCtx); + break; + case SHEIK_TYPE_4: + func_80B3FA08(this, globalCtx); + break; + case SHEIK_TYPE_5: + func_80B40590(this, globalCtx); + break; + case SHEIK_TYPE_MINUET: + func_80B3CA38(this, globalCtx); + break; + case SHEIK_TYPE_BOLERO: + func_80B3CB58(this, globalCtx); + break; + case SHEIK_TYPE_SERENADE: + EnXc_SetupSerenadeAction(this, globalCtx); + break; + case SHEIK_TYPE_9: + EnXc_InitTempleOfTime(this, globalCtx); + break; + case SHEIK_TYPE_0: + EnXc_DoNothing(this, globalCtx); + break; + default: + osSyncPrintf(VT_FGCOL(RED) " En_Oa2 の arg_data がおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + EnXc_DoNothing(this, globalCtx); + } +} + +s32 EnXc_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnXc* this = (EnXc*)thisx; + + if (this->unk_30C != 0) { + if (limbIndex == 9) { + rot->x += this->npcInfo.unk_0E.y; + rot->y -= this->npcInfo.unk_0E.x; + } else if (limbIndex == 16) { + rot->x += this->npcInfo.unk_08.y; + rot->z += this->npcInfo.unk_08.x; + } + } + return 0; +} + +void EnXc_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + if (limbIndex == 16) { + EnXc* this = (EnXc*)thisx; + Vec3f src = { 0.0f, 10.0f, 0.0f }; + Vec3f dest; + + Matrix_MultVec3f(&src, &dest); + this->actor.focus.pos.x = dest.x; + this->actor.focus.pos.y = dest.y; + this->actor.focus.pos.z = dest.z; + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + } +} + +void EnXc_DrawNothing(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnXc_DrawDefault(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnXc* this = (EnXc*)thisx; + s16 eyeIdx = this->eyeIdx; + void* eyeSegment = sEyeTextures[eyeIdx]; + SkelAnime* skelAnime = &this->skelAnime; + GraphicsContext* localGfxCtx = globalCtx->state.gfxCtx; + GraphicsContext* gfxCtx = localGfxCtx; + + OPEN_DISPS(gfxCtx, "../z_en_oA2.c", 1164); + func_8002EBCC(&this->actor, globalCtx, 0); + func_80093D18(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeSegment)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeSegment)); + SkelAnime_DrawFlexOpa(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnXc_OverrideLimbDraw, EnXc_PostLimbDraw, this); + CLOSE_DISPS(gfxCtx, "../z_en_oA2.c", 1207); +} + +static EnXcDrawFunc sDrawFuncs[] = { + EnXc_DrawNothing, EnXc_DrawDefault, EnXc_DrawPullingOutHarp, + EnXc_DrawHarp, EnXc_DrawTriforce, EnXc_DrawSquintingEyes, +}; + +void EnXc_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnXc* this = (EnXc*)thisx; + + if (this->drawMode < 0 || this->drawMode > 5 || sDrawFuncs[this->drawMode] == NULL) { + // "Draw mode is abnormal!!!!!!!!!!!!!!!!!!!!!!!!!" + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + } else { + sDrawFuncs[this->drawMode](thisx, globalCtx); + } +} + +const ActorInit En_Xc_InitVars = { + ACTOR_EN_XC, + ACTORCAT_NPC, + FLAGS, + OBJECT_XC, + sizeof(EnXc), + (ActorFunc)EnXc_Init, + (ActorFunc)EnXc_Destroy, + (ActorFunc)EnXc_Update, + (ActorFunc)EnXc_Draw, + (ActorResetFunc)EnXc_Reset, +}; + +void EnXc_Reset(void) { + D_80B41D90 = 0; + sFlameSpawned = false; + D_80B41DA8 = 1; + D_80B41DAC = 1; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.h b/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.h new file mode 100644 index 000000000..59813e733 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Xc/z_en_xc.h @@ -0,0 +1,145 @@ +#ifndef Z_EN_XC_H +#define Z_EN_XC_H + +#include "ultra64.h" +#include "global.h" + +struct EnXc; + +typedef void (*EnXcActionFunc)(struct EnXc*, GlobalContext*); +typedef void (*EnXcDrawFunc)(struct Actor*, GlobalContext*); + +typedef enum { + /* 0 */ SHEIK_TYPE_0, + /* 1 */ SHEIK_TYPE_1, + /* 2 */ SHEIK_TYPE_2, + /* 3 */ SHEIK_TYPE_3, + /* 4 */ SHEIK_TYPE_4, + /* 5 */ SHEIK_TYPE_5, + /* 6 */ SHEIK_TYPE_MINUET, + /* 7 */ SHEIK_TYPE_BOLERO, + /* 8 */ SHEIK_TYPE_SERENADE, + /* 9 */ SHEIK_TYPE_9 +} EnXcType; + +typedef enum { + /* 0 */ SHEIK_DRAW_NOTHING, + /* 1 */ SHEIK_DRAW_DEFAULT, + /* 2 */ SHEIK_DRAW_PULLING_OUT_HARP, + /* 3 */ SHEIK_DRAW_HARP, + /* 4 */ SHEIK_DRAW_TRIFORCE, + /* 5 */ SHEIK_DRAW_SQUINT +} EnXcDrawMode; + +typedef enum { + /* 00 */ SHEIK_ACTION_INIT, + /* 01 */ SHEIK_ACTION_WAIT, + /* 02 */ SHEIK_ACTION_GRACEFUL_FALL, + /* 03 */ SHEIK_ACTION_ACCEL, + /* 04 */ SHEIK_ACTION_WALK, + /* 05 */ SHEIK_ACTION_HALT, + /* 06 */ SHEIK_ACTION_STOPPED, + /* 07 */ SHEIK_ACTION_7, + /* 08 */ SHEIK_ACTION_HARP_READY, + /* 09 */ SHEIK_PLAYING_HARP, + /* 10 */ SHEIK_ACTION_10, + /* 11 */ SHEIK_ACTION_PUT_HARP_AWAY, + /* 12 */ SHEIK_ACTION_12, + /* 13 */ SHEIK_ACTION_13, + /* 14 */ SHEIK_ACTION_REVERSE_ACCEL, + /* 15 */ SHEIK_ACTION_REVERSE_WALK, + /* 16 */ SHEIK_ACTION_REVERSE_HALT, + /* 17 */ SHEIK_ACTION_THROW_NUT, + /* 18 */ SHEIK_ACTION_DELETE, + /* 19 */ SHEIK_ACTION_FADE, + /* 20 */ SHEIK_ACTION_20, + /* 21 */ SHEIK_ACTION_21, + /* 22 */ SHEIK_ACTION_22, + /* 23 */ SHEIK_ACTION_23, + /* 24 */ SHEIK_ACTION_24, + /* 25 */ SHEIK_ACTION_25, + /* 26 */ SHEIK_ACTION_26, + /* 27 */ SHEIK_ACTION_27, + /* 28 */ SHEIK_ACTION_28, + /* 29 */ SHEIK_ACTION_SERENADE, + /* 30 */ SHEIK_ACTION_30, + /* 31 */ SHEIK_ACTION_31, + /* 32 */ SHEIK_ACTION_32, + /* 33 */ SHEIK_ACTION_33, + /* 34 */ SHEIK_ACTION_34, + /* 35 */ SHEIK_ACTION_35, + /* 36 */ SHEIK_ACTION_36, + /* 37 */ SHEIK_ACTION_37, + /* 38 */ SHEIK_ACTION_38, + /* 39 */ SHEIK_ACTION_39, + /* 40 */ SHEIK_ACTION_40, + /* 41 */ SHEIK_ACTION_41, + /* 42 */ SHEIK_ACTION_42, + /* 43 */ SHEIK_ACTION_43, + /* 44 */ SHEIK_ACTION_44, + /* 45 */ SHEIK_ACTION_45, + /* 46 */ SHEIK_ACTION_46, + /* 47 */ SHEIK_ACTION_47, + /* 48 */ SHEIK_ACTION_48, + /* 49 */ SHEIK_ACTION_49, + /* 50 */ SHEIK_ACTION_KNEEL, + /* 51 */ SHEIK_ACTION_51, + /* 52 */ SHEIK_ACTION_52, + /* 53 */ SHEIK_ACTION_53, + /* 54 */ SHEIK_ACTION_54, + /* 55 */ SHEIK_ACTION_SHOW_TRIFORCE, + /* 56 */ SHEIK_ACTION_SHOW_TRIFORCE_IDLE, + /* 57 */ SHEIK_ACTION_NOCTURNE_INIT, + /* 58 */ SHEIK_ACTION_NOCTURNE_IDLE, + /* 59 */ SHEIK_ACTION_DEFENSE_STANCE, + /* 60 */ SHEIK_ACTION_CONTORT, + /* 61 */ SHEIK_ACTION_NOCTURNE_FALL, + /* 62 */ SHEIK_ACTION_NOCTURNE_HIT_GROUND, + /* 63 */ SHEIK_ACTION_63, + /* 64 */ SHEIK_ACTION_NOCTURNE_KNEEL, + /* 65 */ SHEIK_ACTION_65, + /* 66 */ SHEIK_ACTION_66, + /* 67 */ SHEIK_ACTION_67, + /* 68 */ SHEIK_ACTION_68, + /* 69 */ SHEIK_ACTION_69, + /* 70 */ SHEIK_ACTION_70, + /* 71 */ SHEIK_ACTION_71, + /* 72 */ SHEIK_ACTION_72, + /* 73 */ SHEIK_ACTION_NOCTURNE_REVERSE_ACCEL, + /* 74 */ SHEIK_ACTION_NOCTURNE_REVERSE_WALK, + /* 75 */ SHEIK_ACTION_NOCTURNE_REVERSE_HALT, + /* 76 */ SHEIK_ACTION_NOCTURNE_THROW_NUT, + /* 77 */ SHEIK_ACTION_77, + /* 78 */ SHEIK_ACTION_78, + /* 79 */ SHEIK_ACTION_BLOCK_PEDESTAL, + /* 80 */ SHEIK_ACTION_IN_DIALOGUE +} EnXcAction; + +typedef struct EnXc { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[17]; + /* 0x01F6 */ Vec3s morphTable[17]; + /* 0x025C */ s16 eyeIdx; + /* 0x025E */ s16 blinkTimer; + /* 0x0260 */ s32 action; + /* 0x0264 */ s32 drawMode; + /* 0x0268 */ f32 timer; + /* 0x026C */ s32 unk_26C; + /* 0x0270 */ s32 unk_270; // some sort of flag + /* 0x0274 */ s32 unk_274; + /* 0x0278 */ s32 triforcePrimColor[4]; + /* 0x0288 */ s32 triforceEnvColor[4]; + /* 0x0298 */ f32 triforceScale[3]; + /* 0x02A4 */ s16 triforceAngle; + /* 0x02A8 */ s32 unk_2A8; // sound related + /* 0x02AC */ s32 unk_2AC; // sound related + /* 0x02B0 */ Vec3f handPos; + /* 0x02BC */ s32 unk_2BC; // hand pos related + /* 0x02C0 */ ColliderCylinder collider; + /* 0x030C */ s32 unk_30C; + /* 0x0310 */ Actor* flameActor; + /* 0x0314 */ struct_80034A14_arg1 npcInfo; +} EnXc; // size = 0x033C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Yabusame_Mark/z_en_yabusame_mark.c b/soh/src/overlays/actors/ovl_En_Yabusame_Mark/z_en_yabusame_mark.c new file mode 100644 index 000000000..ff8630bb0 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Yabusame_Mark/z_en_yabusame_mark.c @@ -0,0 +1,221 @@ +/* + * File: z_en_yabusame_mark.c + * Overlay: ovl_En_Yabusame_Mark + * Description: Horseback Archery Target (arrow hitbox) + */ + +#include "z_en_yabusame_mark.h" +#include "vt.h" + +#define FLAGS 0 + +void EnYabusameMark_Init(Actor* thisx, GlobalContext* globalCtx); +void EnYabusameMark_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnYabusameMark_Update(Actor* thisx, GlobalContext* globalCtx); +void func_80B42F74(EnYabusameMark* this, GlobalContext* globalCtx); + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +const ActorInit En_Yabusame_Mark_InitVars = { + ACTOR_EN_YABUSAME_MARK, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EnYabusameMark), + (ActorFunc)EnYabusameMark_Init, + (ActorFunc)EnYabusameMark_Destroy, + (ActorFunc)EnYabusameMark_Update, + NULL, + NULL, +}; + +static Vec3f sCollisionVertices[] = { + { 70.0f, 70.0f, 0.0f }, { 70.0f, -70.0f, 0.0f }, { -70.0f, 70.0f, 0.0f }, { -70.0f, -70.0f, 0.0f }, + { 90.0f, 130.0f, -120.0f }, { -25.0f, -80.0f, -130.0f }, { 90.0f, 130.0f, 120.0f }, { -25.0f, -80.0, 130.0f }, + { 115.0f, 160.0f, -150.0f }, { -50.0f, -140.0f, -160.0f }, { 115.0f, 160.0f, 150.0f }, { -50.0f, -140.0f, 160.0f }, +}; + +static Vec3f sTargetPos[] = { + { 3382.0f, 1734.0f, -4946.0f }, // small, furthest from entrance + { 3360.0f, 1734.0f, 495.0f }, // small, closest to entrance + { 4517.0f, 1682.0f, -1779.0f }, // medium, on the right + { 4517.0f, 1682.0f, -2813.0f }, // medium, on the left + { 4522.0f, 1727.0f, -2296.0f }, // large in the center +}; + +// 0: first ring +// 1: second ring +// 2: outside edge +static f32 sRingDistance[] = { + 20.0f, 40.0f, 60.0f, 777.0f, // small + 40.0f, 80.0f, 120.0f, 777.0f, // medium + 40.0f, 120.0f, 160.0f, 777.0f, // large +}; + +void EnYabusameMark_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnYabusameMark* this = (EnYabusameMark*)thisx; + + Collider_DestroyQuad(globalCtx, &this->collider); +} + +void EnYabusameMark_Init(Actor* thisx, GlobalContext* globalCtx) { + EnYabusameMark* this = (EnYabusameMark*)thisx; + + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ やぶさめまと ☆☆☆☆☆ %x\n" VT_RST, this->actor.params); + this->actor.flags &= ~ACTOR_FLAG_0; + this->typeIndex = this->actor.params; + this->actor.targetMode = 5; + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ 種類インデックス \t ☆☆☆☆☆ %d\n" VT_RST, this->typeIndex); + switch (this->typeIndex) { + case 0: + this->subTypeIndex = 0; + if (this->actor.world.pos.z > 0.0f) { + this->subTypeIndex = 1; + } + break; + case 1: + this->subTypeIndex = 2; + if (this->actor.world.pos.z < -2000.0f) { + this->subTypeIndex = 3; + } + break; + case 2: + this->subTypeIndex = 4; + break; + } + Collider_InitQuad(globalCtx, &this->collider); + Collider_SetQuad(globalCtx, &this->collider, &this->actor, &sQuadInit); + this->worldPos = this->actor.world.pos; + this->actor.flags |= ACTOR_FLAG_4; + if (gSaveContext.sceneSetupIndex != 4) { + Actor_Kill(&this->actor); + return; + } + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 種類 ☆☆☆☆☆ %d\n" VT_RST, this->typeIndex); + osSyncPrintf(VT_FGCOL(CYAN) "☆☆☆☆☆ さらに分類 ☆☆☆☆☆ %d\n" VT_RST, this->subTypeIndex); + this->actionFunc = func_80B42F74; +} + +void func_80B42F74(EnYabusameMark* this, GlobalContext* globalCtx) { + Vec3f effectAccel = { 0.0f, 0.0f, 0.0f }; + Vec3f effectVelocity = { 0.0f, 0.0f, 0.0f }; + Vec3f arrowHitPos; + Vec3f distanceFromCenter; + s32 pad; + s32 scoreIndex; + f32 scoreDistance100; + f32 scoreDistance60; + f32 scoreDistance30; + + if (this->collider.base.acFlags & AC_HIT) { + this->collider.base.acFlags &= ~AC_HIT; + + arrowHitPos.x = this->collider.info.bumper.hitPos.x; + arrowHitPos.y = this->collider.info.bumper.hitPos.y; + arrowHitPos.z = this->collider.info.bumper.hitPos.z; + + effectVelocity.y = 15.0f; + + EffectSsHitMark_SpawnCustomScale(globalCtx, 0, 700, &arrowHitPos); + + scoreIndex = 2; + + scoreDistance100 = sRingDistance[this->typeIndex * 4 + 0]; + scoreDistance60 = sRingDistance[this->typeIndex * 4 + 1]; + scoreDistance30 = sRingDistance[this->typeIndex * 4 + 2]; + + distanceFromCenter.x = fabsf(sTargetPos[this->subTypeIndex].x - arrowHitPos.x); + distanceFromCenter.y = fabsf(sTargetPos[this->subTypeIndex].y - arrowHitPos.y); + distanceFromCenter.z = fabsf(sTargetPos[this->subTypeIndex].z - arrowHitPos.z); + + if (distanceFromCenter.x > scoreDistance100 || distanceFromCenter.y > scoreDistance100 || + distanceFromCenter.z > scoreDistance100) { + scoreIndex = 1; + if (distanceFromCenter.x > scoreDistance60 || distanceFromCenter.y > scoreDistance60 || + distanceFromCenter.z > scoreDistance60) { + scoreIndex = 0; + } + if (distanceFromCenter.x > scoreDistance30 || distanceFromCenter.y > scoreDistance30 || + distanceFromCenter.z > scoreDistance30) { + return; + } + } + + osSyncPrintf("\n\n"); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ posX ☆☆☆☆☆ %f\n" VT_RST, arrowHitPos.x); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ posY ☆☆☆☆☆ %f\n" VT_RST, arrowHitPos.y); + osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ posZ ☆☆☆☆☆ %f\n" VT_RST, arrowHitPos.z); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ hitX ☆☆☆☆☆ %f\n" VT_RST, sTargetPos[this->subTypeIndex].x); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ hitY ☆☆☆☆☆ %f\n" VT_RST, sTargetPos[this->subTypeIndex].y); + osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ hitZ ☆☆☆☆☆ %f\n" VT_RST, sTargetPos[this->subTypeIndex].z); + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 小 ☆☆☆☆☆ %f\n" VT_RST, scoreDistance100); + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ 大 ☆☆☆☆☆ %f\n" VT_RST, scoreDistance60); + osSyncPrintf(VT_FGCOL(PURPLE) "☆☆☆☆☆ point ☆☆☆☆☆ %d\n" VT_RST, scoreIndex); + osSyncPrintf("\n\n"); + + if (scoreIndex == 2) { + Audio_PlayFanfare(NA_BGM_ITEM_GET | 0x900); + } + if (scoreIndex == 1) { + Audio_StopSfxById(NA_SE_SY_TRE_BOX_APPEAR); + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + if (scoreIndex == 0) { + func_80078884(NA_SE_SY_DECIDE); + } + EffectSsExtra_Spawn(globalCtx, &arrowHitPos, &effectVelocity, &effectAccel, 5, scoreIndex); + } +} + +void EnYabusameMark_Update(Actor* thisx, GlobalContext* globalCtx) { + EnYabusameMark* this = (EnYabusameMark*)thisx; + Vec3f* vertexArray; + u32 arrayIndex; + + this->actionFunc(this, globalCtx); + arrayIndex = this->typeIndex * 4; + vertexArray = &sCollisionVertices[arrayIndex]; + + this->vertexA.x = vertexArray[0].x + this->actor.world.pos.x; + this->vertexA.y = vertexArray[0].y + this->actor.world.pos.y; + this->vertexA.z = vertexArray[0].z + this->actor.world.pos.z; + + this->vertexB.x = vertexArray[1].x + this->actor.world.pos.x; + this->vertexB.y = vertexArray[1].y + this->actor.world.pos.y; + this->vertexB.z = vertexArray[1].z + this->actor.world.pos.z; + + this->vertexC.x = vertexArray[2].x + this->actor.world.pos.x; + this->vertexC.y = vertexArray[2].y + this->actor.world.pos.y; + this->vertexC.z = vertexArray[2].z + this->actor.world.pos.z; + + this->vertexD.x = vertexArray[3].x + this->actor.world.pos.x; + this->vertexD.y = vertexArray[3].y + this->actor.world.pos.y; + this->vertexD.z = vertexArray[3].z + this->actor.world.pos.z; + + Collider_SetQuadVertices(&this->collider, &this->vertexA, &this->vertexB, &this->vertexC, &this->vertexD); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (BREG(0)) { + DebugDisplay_AddObject(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 1.0f, 1.0f, + 1.0f, 0, 0xFF, 0, 0xFF, 4, globalCtx->state.gfxCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Yabusame_Mark/z_en_yabusame_mark.h b/soh/src/overlays/actors/ovl_En_Yabusame_Mark/z_en_yabusame_mark.h new file mode 100644 index 000000000..363fd9cc7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Yabusame_Mark/z_en_yabusame_mark.h @@ -0,0 +1,24 @@ +#ifndef Z_EN_YABUSAME_MARK_H +#define Z_EN_YABUSAME_MARK_H + +#include "ultra64.h" +#include "global.h" + +struct EnYabusameMark; + +typedef void (*EnYabusameMarkActionFunc)(struct EnYabusameMark*, GlobalContext*); + +typedef struct EnYabusameMark { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnYabusameMarkActionFunc actionFunc; + /* 0x0150 */ s16 typeIndex; + /* 0x0152 */ s16 subTypeIndex; + /* 0x0154 */ Vec3f worldPos; + /* 0x0160 */ Vec3f vertexA; + /* 0x016C */ Vec3f vertexB; + /* 0x0178 */ Vec3f vertexC; + /* 0x0184 */ Vec3f vertexD; + /* 0x0190 */ ColliderQuad collider; +} EnYabusameMark; // size = 0x0210 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Yukabyun/z_en_yukabyun.c b/soh/src/overlays/actors/ovl_En_Yukabyun/z_en_yukabyun.c new file mode 100644 index 000000000..dc0b72b94 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Yukabyun/z_en_yukabyun.c @@ -0,0 +1,159 @@ +/* + * File: z_en_yukabyun.c + * Overlay: ovl_En_Yukabyun + * Description: Flying floor tile + */ + +#include "z_en_yukabyun.h" +#include "objects/object_yukabyun/object_yukabyun.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnYukabyun_Init(Actor* thisx, GlobalContext* globalCtx); +void EnYukabyun_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnYukabyun_Update(Actor* thisx, GlobalContext* globalCtx); +void EnYukabyun_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B43A94(EnYukabyun* this, GlobalContext* globalCtx); +void func_80B43AD4(EnYukabyun* this, GlobalContext* globalCtx); +void func_80B43B6C(EnYukabyun* this, GlobalContext* globalCtx); + +const ActorInit En_Yukabyun_InitVars = { + ACTOR_EN_YUKABYUN, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_YUKABYUN, + sizeof(EnYukabyun), + (ActorFunc)EnYukabyun_Init, + (ActorFunc)EnYukabyun_Destroy, + (ActorFunc)EnYukabyun_Update, + (ActorFunc)EnYukabyun_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_NO_PUSH | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x04 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_HARD, + BUMP_ON, + OCELEM_ON, + }, + { 28, 8, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 1, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 16, ICHAIN_STOP), +}; + +static void* D_80B43F64[] = { gFloorTileEnemyTopTex, gFloorTileEnemyBottomTex }; + +void EnYukabyun_Init(Actor* thisx, GlobalContext* globalCtx) { + EnYukabyun* this = (EnYukabyun*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 0.4f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actor.params++; + this->unk_152 = 0; + this->unk_150 = (u8)(this->actor.params) * 0xA + 0x14; + this->actionfunc = func_80B43A94; +} + +void EnYukabyun_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnYukabyun* this = (EnYukabyun*)thisx; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80B43A94(EnYukabyun* this, GlobalContext* globalCtx) { + if (this->unk_150 != 0) { + this->unk_150--; + } + if (this->unk_150 == 0) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_12; + this->actionfunc = func_80B43AD4; + } +} + +void func_80B43AD4(EnYukabyun* this, GlobalContext* globalCtx) { + this->unk_150 += 0xA0; + this->actor.shape.rot.y += this->unk_150; + if (this->unk_150 >= 0x2000) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = 10.0f; + this->actionfunc = func_80B43B6C; + } + Math_StepToF(&this->actor.world.pos.y, this->actor.home.pos.y + 30.0f, 1.0f); + func_8002F974(&this->actor, NA_SE_EN_YUKABYUN_FLY - SFX_FLAG); +} + +void func_80B43B6C(EnYukabyun* this, GlobalContext* globalCtx) { + this->actor.shape.rot.y += this->unk_150; + if (this->actor.xzDistToPlayer > 5000.0f) { + Actor_Kill(&this->actor); + return; + } + func_8002F974(&this->actor, NA_SE_EN_YUKABYUN_FLY - SFX_FLAG); +} + +void EnYukabyun_Break(EnYukabyun* this, GlobalContext* globalCtx) { + EffectSsHahen_SpawnBurst(globalCtx, &this->actor.world.pos, 8.0f, 0, 1300, 300, 15, OBJECT_YUKABYUN, 10, + gFloorTileEnemyFragmentDL); + Actor_Kill(&this->actor); +} + +void EnYukabyun_Update(Actor* thisx, GlobalContext* globalCtx) { + EnYukabyun* this = (EnYukabyun*)thisx; + s32 pad; + + if (((this->collider.base.atFlags & AT_HIT) || (this->collider.base.acFlags & AC_HIT) || + ((this->collider.base.ocFlags1 & OC1_HIT) && !(this->collider.base.oc->id == ACTOR_EN_YUKABYUN))) || + ((this->actionfunc == func_80B43B6C) && (this->actor.bgCheckFlags & 8))) { + this->collider.base.atFlags &= ~AT_HIT; + this->collider.base.acFlags &= ~AC_HIT; + this->collider.base.ocFlags1 &= ~OC1_HIT; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_2); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 30, NA_SE_EN_OCTAROCK_ROCK); + this->actionfunc = EnYukabyun_Break; + } + + this->actionfunc(this, globalCtx); + Actor_MoveForward(&this->actor); + + if (!(this->actionfunc == func_80B43A94 || this->actionfunc == EnYukabyun_Break)) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 20.0f, 8.0f, 5); + Collider_UpdateCylinder(&this->actor, &this->collider); + + this->actor.flags |= ACTOR_FLAG_24; + + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Actor_SetFocus(&this->actor, 4.0f); +} + +void EnYukabyun_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnYukabyun* this = (EnYukabyun*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_yukabyun.c", 366); + + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_80B43F64[this->unk_152])); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_yukabyun.c", 373), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFloorTileEnemyDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_yukabyun.c", 378); +} diff --git a/soh/src/overlays/actors/ovl_En_Yukabyun/z_en_yukabyun.h b/soh/src/overlays/actors/ovl_En_Yukabyun/z_en_yukabyun.h new file mode 100644 index 000000000..c1e6b00ae --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Yukabyun/z_en_yukabyun.h @@ -0,0 +1,19 @@ +#ifndef Z_EN_YUKABYUN_H +#define Z_EN_YUKABYUN_H + +#include "ultra64.h" +#include "global.h" + +struct EnYukabyun; + +typedef void (*EnYukabyunActionFunc)(struct EnYukabyun*, GlobalContext*); + +typedef struct EnYukabyun { + /* 0x0000 */ Actor actor; + /* 0x014C */ EnYukabyunActionFunc actionfunc; + /* 0x0150 */ s16 unk_150; + /* 0x0152 */ u8 unk_152; + /* 0x0154 */ ColliderCylinder collider; +} EnYukabyun; // size = 0x01A0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c new file mode 100644 index 000000000..c5ed809db --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.c @@ -0,0 +1,2432 @@ +/* + * File: z_en_zf.c + * Overlay: ovl_En_Zf + * Description: Lizalfos and Dinolfos + */ + +#include "z_en_zf.h" +#include "objects/object_zf/object_zf.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_2 | ACTOR_FLAG_4) + +void EnZf_Init(Actor* thisx, GlobalContext* globalCtx); +void EnZf_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnZf_Update(Actor* thisx, GlobalContext* globalCtx); +void EnZf_Draw(Actor* thisx, GlobalContext* globalCtx); +void EnZf_Reset(void); + +s16 EnZf_FindPlatform(Vec3f* pos, s16 preferredIndex); +void EnZf_SetupDropIn(EnZf* this); +void EnZf_DropIn(EnZf* this, GlobalContext* globalCtx); +void func_80B45384(EnZf* this); +void func_80B4543C(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupApproachPlayer(EnZf* this, GlobalContext* globalCtx); +void EnZf_ApproachPlayer(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupJumpForward(EnZf* this); +void EnZf_JumpForward(EnZf* this, GlobalContext* globalCtx); +void func_80B4604C(EnZf* this); +void func_80B46098(EnZf* this, GlobalContext* globalCtx); +void func_80B462E4(EnZf* this, GlobalContext* globalCtx); +void func_80B463E4(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupSlash(EnZf* this); +void EnZf_Slash(EnZf* this, GlobalContext* globalCtx); +void EnZf_RecoilFromBlockedSlash(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupJumpBack(EnZf* this); +void EnZf_JumpBack(EnZf* this, GlobalContext* globalCtx); +void EnZf_Stunned(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupSheatheSword(EnZf* this, GlobalContext* globalCtx); +void EnZf_SheatheSword(EnZf* this, GlobalContext* globalCtx); +void EnZf_HopAndTaunt(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupHopAway(EnZf* this, GlobalContext* globalCtx); +void EnZf_HopAway(EnZf* this, GlobalContext* globalCtx); +void EnZf_DrawSword(EnZf* this, GlobalContext* globalCtx); +void EnZf_Damaged(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupJumpUp(EnZf* this); +void EnZf_JumpUp(EnZf* this, GlobalContext* globalCtx); +void func_80B483E4(EnZf* this, GlobalContext* globalCtx); +void EnZf_CircleAroundPlayer(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupDie(EnZf* this); +void EnZf_Die(EnZf* this, GlobalContext* globalCtx); +void EnZf_SetupCircleAroundPlayer(EnZf* this, f32 speed); +s32 EnZf_DodgeRangedEngaging(GlobalContext* globalCtx, EnZf* this); +s32 EnZf_DodgeRangedWaiting(GlobalContext* globalCtx, EnZf* this); + +#define PLATFORM_INDEX_DOWNSTAIRS_MIN 0 +#define PLATFORM_INDEX_DOWNSTAIRS_INNER_MAX 5 +#define PLATFORM_INDEX_DOWNSTAIRS_MAX 7 +#define PLATFORM_INDEX_UPSTAIRS_MIN (PLATFORM_INDEX_DOWNSTAIRS_MAX + 1) +#define PLATFORM_INDEX_UPSTAIRS_INNER_MAX 15 + +/** + * Array of platform positions in Dodongo's Cavern miniboss room. + * 0 - 7 : Downstairs + * 8 - 15 : Upstairs inner platforms + * 16 - 23 : Upstairs outer platforms (including several points on the long thin one) + */ +static Vec3f sPlatformPositions[] = { + // Downstairs + { 3560.0f, 100.0f, -1517.0f }, + { 3170.0f, 100.0f, -1767.0f }, + { 3165.0f, 100.0f, -2217.0f }, + { 3563.0f, 100.0f, -2437.0f }, + { 3946.0f, 100.0f, -2217.0f }, + { 3942.0f, 100.0f, -1765.0f }, + // Westmost downstairs two + { 2861.0f, 100.0f, -2394.0f }, + { 2776.0f, 100.0f, -1987.0f }, + + // Upstairs inner + { 4527.0f, 531.0f, -1146.0f }, + { 4442.0f, 531.0f, -1405.0f }, + { 4170.0f, 531.0f, -1395.0f }, + { 4030.0f, 531.0f, -1162.0f }, + { 4010.0f, 531.0f, -883.0f }, + { 4270.0f, 531.0f, -810.0f }, + { 4520.0f, 531.0f, -880.0f }, + { 4260.0f, 531.0f, -1035.0f }, + + // Upstairs outer + { 4757.0f, 531.0f, -1146.0f }, + { 3850.0f, 531.0f, -883.0f }, + { 4380.0f, 531.0f, -690.0f }, + { 4197.0f, 531.0f, -646.0f }, + { 4070.0f, 531.0f, -1575.0f }, + { 3930.0f, 531.0f, -1705.0f }, + { 3780.0f, 531.0f, -1835.0f }, + { 3560.0f, 531.0f, -1985.0f }, +}; + +// These seem to relate to the tagging in/out the minibosses do +static s16 D_80B4A1B0 = 0; +static s16 D_80B4A1B4 = 1; + +const ActorInit En_Zf_InitVars = { + ACTOR_EN_ZF, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_ZF, + sizeof(EnZf), + (ActorFunc)EnZf_Init, + (ActorFunc)EnZf_Destroy, + (ActorFunc)EnZf_Update, + (ActorFunc)EnZf_Draw, + (ActorResetFunc)EnZf_Reset, +}; + +static ColliderCylinderInit sBodyCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 20, 70, 0, { 0, 0, 0 } }, +}; + +static ColliderQuadInit sSwordQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x08 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL | TOUCH_UNK7, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +typedef enum { + /* 0x0 */ ENZF_DMGEFF_NONE, + /* 0x1 */ ENZF_DMGEFF_STUN, + /* 0x6 */ ENZF_DMGEFF_IMMUNE = 6, // Skips damage code, but also skips the top half of Update + /* 0xD */ ENZF_DMGEFF_PROJECTILE = 0xD, // Projectiles that don't have another damageeffect + /* 0xF */ ENZF_DMGEFF_ICE = 0xF +} EnZfDamageEffect; + +static DamageTable sDamageTable = { + /* Deku nut */ DMG_ENTRY(0, ENZF_DMGEFF_STUN), + /* Deku stick */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Slingshot */ DMG_ENTRY(1, ENZF_DMGEFF_PROJECTILE), + /* Explosive */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Boomerang */ DMG_ENTRY(0, ENZF_DMGEFF_STUN), + /* Normal arrow */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Hammer swing */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Hookshot */ DMG_ENTRY(0, ENZF_DMGEFF_STUN), + /* Kokiri sword */ DMG_ENTRY(1, ENZF_DMGEFF_NONE), + /* Master sword */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Giant's Knife */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), + /* Fire arrow */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), + /* Ice arrow */ DMG_ENTRY(4, ENZF_DMGEFF_ICE), + /* Light arrow */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), + /* Unk arrow 1 */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), + /* Unk arrow 2 */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), + /* Unk arrow 3 */ DMG_ENTRY(2, ENZF_DMGEFF_PROJECTILE), + /* Fire magic */ DMG_ENTRY(0, ENZF_DMGEFF_IMMUNE), + /* Ice magic */ DMG_ENTRY(3, ENZF_DMGEFF_ICE), + /* Light magic */ DMG_ENTRY(0, ENZF_DMGEFF_IMMUNE), + /* Shield */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), + /* Mirror Ray */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), + /* Kokiri spin */ DMG_ENTRY(1, ENZF_DMGEFF_NONE), + /* Giant spin */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), + /* Master spin */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Kokiri jump */ DMG_ENTRY(2, ENZF_DMGEFF_NONE), + /* Giant jump */ DMG_ENTRY(8, ENZF_DMGEFF_NONE), + /* Master jump */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), + /* Unknown 1 */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), + /* Unblockable */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), + /* Hammer jump */ DMG_ENTRY(4, ENZF_DMGEFF_NONE), + /* Unknown 2 */ DMG_ENTRY(0, ENZF_DMGEFF_NONE), +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(targetArrowOffset, 2000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 15, ICHAIN_CONTINUE), + ICHAIN_F32_DIV1000(gravity, -3500, ICHAIN_STOP), +}; + +static AnimationHeader* sHoppingAnims[] = { &gZfHopCrouchingAnim, &gZfHopLeapingAnim, &gZfHopLandingAnim }; + +static s32 D_80B4AB30; // Set to 0 and incremented in EnZf_HopAway, but not actually used + +void EnZf_SetupAction(EnZf* this, EnZfActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +/** + * Tests if it will still be on a floor after moving forwards a distance determined by dist, in the shape forward + * direction. If `dist` is 0, it defaults to a dist depending on speed direction, and params. + */ +s32 EnZf_PrimaryFloorCheck(EnZf* this, GlobalContext* globalCtx, f32 dist) { + s16 ret; + s16 curBgCheckFlags; + f32 sin; + f32 cos; + Vec3f curPos; + + if (dist == 0.0f) { + dist = ((this->actor.speedXZ >= 0.0f) ? 1.0f : -1.0f); + dist = ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) ? dist * 45.0f : dist * 30.0f); + } + + // Save currents to restore later + curPos = this->actor.world.pos; + curBgCheckFlags = this->actor.bgCheckFlags; + + sin = Math_SinS(this->actor.world.rot.y) * dist; + cos = Math_CosS(this->actor.world.rot.y) * dist; + + this->actor.world.pos.x += sin; + this->actor.world.pos.z += cos; + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C); + this->actor.world.pos = curPos; + ret = !(this->actor.bgCheckFlags & 1); + this->actor.bgCheckFlags = curBgCheckFlags; + return ret; +} + +/** + * Supplementary floor test. + */ +s16 EnZf_SecondaryFloorCheck(EnZf* this, GlobalContext* globalCtx, f32 dist) { + s16 ret; + s16 curBgCheckFlags; + f32 sin; + f32 cos; + Vec3f curPos; + + if ((this->actor.speedXZ != 0.0f) && EnZf_PrimaryFloorCheck(this, globalCtx, this->actor.speedXZ)) { + return true; + } + + // Save currents to restore later + curPos = this->actor.world.pos; + curBgCheckFlags = this->actor.bgCheckFlags; + + sin = Math_SinS(this->actor.shape.rot.y) * dist; + cos = Math_CosS(this->actor.shape.rot.y) * dist; + + this->actor.world.pos.x += sin; + this->actor.world.pos.z += cos; + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 0x1C); + this->actor.world.pos = curPos; + ret = !(this->actor.bgCheckFlags & 1); + this->actor.bgCheckFlags = curBgCheckFlags; + return ret; +} + +void EnZf_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZf* this = (EnZf*)thisx; + Player* player = GET_PLAYER(globalCtx); + EffectBlureInit1 blureInit; + f32 posDiff; + + Actor_ProcessInitChain(thisx, sInitChain); + thisx->targetMode = 3; + this->clearFlag = (thisx->params & 0xFF00) >> 8; + /* Strip the top byte of params */ + thisx->params &= 0xFF; + + /* Return the params to their original value if they were originally negative, i.e. 0xFFFF or 0xFFFE */ + if (thisx->params & 0x80) { + thisx->params |= 0xFF00; + } + + ActorShape_Init(&thisx->shape, 0.0f, ActorShadow_DrawFeet, 90.0f); + this->unk_3E0 = 0; + thisx->colChkInfo.mass = MASS_HEAVY; + thisx->colChkInfo.damageTable = &sDamageTable; + + blureInit.p1StartColor[0] = blureInit.p1StartColor[1] = blureInit.p1StartColor[2] = blureInit.p1StartColor[3] = + blureInit.p2StartColor[0] = blureInit.p2StartColor[1] = blureInit.p2StartColor[2] = blureInit.p1EndColor[0] = + blureInit.p1EndColor[1] = blureInit.p1EndColor[2] = blureInit.p2EndColor[0] = blureInit.p2EndColor[1] = + blureInit.p2EndColor[2] = 255; + blureInit.p2StartColor[3] = 64; + blureInit.p1EndColor[3] = blureInit.p2EndColor[3] = 0; + blureInit.elemDuration = 8; + blureInit.unkFlag = 0; + blureInit.calcMode = 2; + + Effect_Add(globalCtx, &this->blureIndex, EFFECT_BLURE1, 0, 0, &blureInit); + + Actor_UpdateBgCheckInfo(globalCtx, thisx, 75.0f, 45.0f, 45.0f, 0x1D); + + this->alpha = 255; + thisx->colChkInfo.cylRadius = 40; + thisx->colChkInfo.cylHeight = 100; + Collider_InitCylinder(globalCtx, &this->bodyCollider); + Collider_SetCylinder(globalCtx, &this->bodyCollider, thisx, &sBodyCylinderInit); + Collider_InitQuad(globalCtx, &this->swordCollider); + Collider_SetQuad(globalCtx, &this->swordCollider, thisx, &sSwordQuadInit); + + if (thisx->params == ENZF_TYPE_DINOLFOS) { + thisx->colChkInfo.health = 12; + thisx->naviEnemyId = 0x10; + SkelAnime_Init(globalCtx, &this->skelAnime, &gZfDinolfosSkel, &gZfCryingAnim, this->jointTable, + this->morphTable, ENZF_LIMB_MAX); + } else { // Lizalfos + thisx->colChkInfo.health = 6; + thisx->naviEnemyId = 0x0F; + SkelAnime_Init(globalCtx, &this->skelAnime, &gZfLizalfosSkel, &gZfCryingAnim, this->jointTable, + this->morphTable, ENZF_LIMB_MAX); + } + + if (thisx->params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // Not minibosses + this->homePlatform = this->curPlatform = -1; + D_80B4A1B4 = -1; + this->hopAnimIndex = 1; + if (thisx->params == ENZF_TYPE_LIZALFOS_LONE) { + EnZf_SetupDropIn(this); + } else { // Dinolfos + func_80B45384(this); + } + } else { // Minibosses + posDiff = player->actor.world.pos.y - thisx->world.pos.y; + + if ((ABS(posDiff) <= 100.0f) && !Flags_GetSwitch(globalCtx, this->clearFlag)) { + this->homePlatform = this->curPlatform = EnZf_FindPlatform(&thisx->world.pos, 0); + EnZf_SetupDropIn(this); + D_80B4A1B4 = 1; + } else { + Actor_Kill(thisx); + } + } +} + +void EnZf_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnZf* this = (EnZf*)thisx; + + if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && + (Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_ZF, ACTORCAT_ENEMY, 10000.0f) == NULL)) { + func_800F5B58(); + } + + Effect_Delete(globalCtx, this->blureIndex); + Collider_DestroyCylinder(globalCtx, &this->bodyCollider); + Collider_DestroyQuad(globalCtx, &this->swordCollider); +} + +/** + * Finds the index of the platform position in `sPlatformPositions` that is sufficiently close to `pos`. + * Returns `preferedIndex` if the associated position is close enough. + */ +s16 EnZf_FindPlatform(Vec3f* pos, s16 preferredIndex) { + f32 rangeXZ; + s16 i; + + rangeXZ = 210.0f; + + // Upstairs has a smaller range + if (pos->y >= 420.0f) { + rangeXZ = 110.0f; + } + + if (preferredIndex != -1) { + i = preferredIndex; + if (((sPlatformPositions[i].y - 150.0f) <= pos->y) && (pos->y <= (sPlatformPositions[i].y + 150.0f)) && + ((sPlatformPositions[i].x - rangeXZ) <= pos->x) && (pos->x <= (sPlatformPositions[i].x + rangeXZ)) && + ((sPlatformPositions[i].z - rangeXZ) <= pos->z) && (pos->z <= (sPlatformPositions[i].z + rangeXZ))) { + return preferredIndex; + } + } + + for (i = ARRAY_COUNT(sPlatformPositions) - 1; i > -1; i--) { + if (((sPlatformPositions[i].y - 150.0f) <= pos->y) && (pos->y <= (sPlatformPositions[i].y + 150.0f)) && + ((sPlatformPositions[i].x - rangeXZ) <= pos->x) && (pos->x <= (sPlatformPositions[i].x + rangeXZ)) && + ((sPlatformPositions[i].z - rangeXZ) <= pos->z) && (pos->z <= (sPlatformPositions[i].z + rangeXZ))) { + break; + } + } + return i; +} + +s16 EnZf_FindNextPlatformAwayFromPlayer(Vec3f* pos, s16 curPlatform, s16 arg2, GlobalContext* globalCtx) { + f32 distToCurLoopPlatform; + f32 platformMinDist = 585.0f; + Player* player = GET_PLAYER(globalCtx); + s16 initialPlatform = curPlatform; + f32 playerMaxDist = 400.0f; + f32 smallMaxRange = 99998.0f; + s16 curLoopPlatform = PLATFORM_INDEX_DOWNSTAIRS_INNER_MAX; // Will never retreat to the last two + s16 minIndex = PLATFORM_INDEX_DOWNSTAIRS_MIN; + f32 largeMaxRange = 99999.0f; + s16 altNextPlatform = -1; + s16 nextPlatform = -1; + s16 playerPlatform = EnZf_FindPlatform(&player->actor.world.pos, initialPlatform); + + // Set up search constraints + // Upstairs + if (pos->y > 420.0f) { + minIndex = PLATFORM_INDEX_UPSTAIRS_MIN; + playerMaxDist = 50.0f; + + // Upstairs outer + if (initialPlatform >= PLATFORM_INDEX_UPSTAIRS_INNER_MAX) { + curLoopPlatform = ARRAY_COUNT(sPlatformPositions) - 1; + platformMinDist = 400.0f; + } else { // upstairs inner + curLoopPlatform = PLATFORM_INDEX_UPSTAIRS_INNER_MAX - 1; + platformMinDist = 380.0f; + } + } + + for (; curLoopPlatform >= minIndex; curLoopPlatform--) { + if ((curLoopPlatform == initialPlatform) || (curLoopPlatform == playerPlatform)) { + continue; + } + if ((playerPlatform == -1) && + (Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[curLoopPlatform]) < playerMaxDist)) { + continue; + } + distToCurLoopPlatform = Math_Vec3f_DistXYZ(pos, &sPlatformPositions[curLoopPlatform]); + + if (platformMinDist < distToCurLoopPlatform) { + continue; + } + if (distToCurLoopPlatform < smallMaxRange) { + largeMaxRange = smallMaxRange; + altNextPlatform = nextPlatform; + smallMaxRange = distToCurLoopPlatform; + nextPlatform = curLoopPlatform; + } else if (distToCurLoopPlatform < largeMaxRange) { + largeMaxRange = distToCurLoopPlatform; + altNextPlatform = curLoopPlatform; + } + } + + // These functions have no side effects, so these two calls do nothing + Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[nextPlatform]); + Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[altNextPlatform]); + + if (altNextPlatform > 0) { + s16 nextPlatformToPlayerYaw = + Math_Vec3f_Yaw(pos, &sPlatformPositions[nextPlatform]) - Math_Vec3f_Yaw(pos, &player->actor.world.pos); + + if (ABS(nextPlatformToPlayerYaw) < 0x36B0) { + nextPlatform = altNextPlatform; + } + } + + if (nextPlatform < 0) { + nextPlatform = arg2; + } + return nextPlatform; +} + +s16 EnZf_FindNextPlatformTowardsPlayer(Vec3f* pos, s16 curPlatform, s16 arg2, GlobalContext* globalCtx) { + s16 curLoopPlatform = PLATFORM_INDEX_DOWNSTAIRS_MAX; + s16 minIndex = PLATFORM_INDEX_DOWNSTAIRS_MIN; + Player* player = GET_PLAYER(globalCtx); + s16 nextPlatform = EnZf_FindPlatform(&player->actor.world.pos, -1); + f32 minRange = 500.0f; + f32 smallMaxRange = 99998.0f; + f32 largeMaxRange = 99999.0f; + s16 phi_s2 = curPlatform; + s16 phi_s3 = arg2; + + // Upstairs + if (pos->y > 200.0f) { + curLoopPlatform = ARRAY_COUNT(sPlatformPositions) - 1; + minIndex = PLATFORM_INDEX_UPSTAIRS_MIN; + minRange = 290.0f; + } + + for (; curLoopPlatform >= minIndex; curLoopPlatform--) { + if (minRange < Math_Vec3f_DistXYZ(pos, &sPlatformPositions[curLoopPlatform])) { + continue; + } + if (curLoopPlatform != nextPlatform) { + f32 curPlatformDistToPlayer = + Math_Vec3f_DistXYZ(&player->actor.world.pos, &sPlatformPositions[curLoopPlatform]); + + if (curPlatformDistToPlayer < smallMaxRange) { + largeMaxRange = smallMaxRange; + phi_s3 = phi_s2; + smallMaxRange = curPlatformDistToPlayer; + phi_s2 = curLoopPlatform; + } else if (curPlatformDistToPlayer < largeMaxRange) { + largeMaxRange = curPlatformDistToPlayer; + phi_s3 = curLoopPlatform; + } + } else { + phi_s2 = nextPlatform; + break; + } + } + + if (phi_s3 != nextPlatform) { + nextPlatform = phi_s2; + } else { + nextPlatform = phi_s3; + } + + return nextPlatform; +} + +// Player not targeting this or another EnZf? +s32 EnZf_CanAttack(GlobalContext* globalCtx, EnZf* this) { + Actor* targetedActor; + Player* player = GET_PLAYER(globalCtx); + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + if (player->stateFlags1 & 0x6000) { // Hanging or climbing + return false; + } else { + return true; + } + } else { + if (!Actor_OtherIsTargeted(globalCtx, &this->actor)) { + return true; + } + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + targetedActor = player->unk_664; + if (targetedActor == NULL) { + return false; + } else { + if (targetedActor->category != ACTORCAT_ENEMY) { + return true; + } + if (targetedActor->id != ACTOR_EN_ZF) { + return false; + } else if (targetedActor->colorFilterTimer != 0) { + return true; + } + } + } + } + return false; +} + +void func_80B44DC4(EnZf* this, GlobalContext* globalCtx) { + s16 angleDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (angleDiff < 0) { + angleDiff = -angleDiff; + } + + if (angleDiff >= 0x1B58) { + func_80B483E4(this, globalCtx); + } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 8) != 0) && + EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else { + func_80B45384(this); + } +} + +s32 EnZf_ChooseAction(GlobalContext* globalCtx, EnZf* this) { + s16 angleToWall; + Actor* explosive; + + angleToWall = this->actor.wallYaw - this->actor.shape.rot.y; + angleToWall = ABS(angleToWall); + + if (func_800354B4(globalCtx, &this->actor, 100.0f, 0x5DC0, 0x2AA8, this->actor.shape.rot.y)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if ((this->actor.bgCheckFlags & 8) && (ABS(angleToWall) < 0x2EE0) && (this->actor.xzDistToPlayer < 80.0f)) { + EnZf_SetupJumpUp(this); + return true; + } else if ((this->actor.xzDistToPlayer < 90.0f) && ((globalCtx->gameplayFrames % 2) != 0)) { + EnZf_SetupJumpUp(this); + return true; + } else { + EnZf_SetupJumpBack(this); + return true; + } + } + + explosive = Actor_FindNearby(globalCtx, &this->actor, -1, ACTORCAT_EXPLOSIVE, 80.0f); + + if (explosive != NULL) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + if (((this->actor.bgCheckFlags & 8) && (angleToWall < 0x2EE0)) || (explosive->id == ACTOR_EN_BOM_CHU)) { + if ((explosive->id == ACTOR_EN_BOM_CHU) && (Actor_WorldDistXYZToActor(&this->actor, explosive) < 80.0f) && + ((s16)((this->actor.shape.rot.y - explosive->world.rot.y) + 0x8000) < 0x3E80)) { + EnZf_SetupJumpUp(this); + return true; + } else { + EnZf_SetupCircleAroundPlayer(this, 4.0f); + return true; + } + } else { + EnZf_SetupJumpBack(this); + return true; + } + } + return false; +} + +// Setup functions and action functions + +/** + * Set position 300 units above ground and invisible, fade in and drop to ground, fully solid when on ground + */ +void EnZf_SetupDropIn(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfJumpingAnim, 0.0f, 9.0f, Animation_GetLastFrame(&gZfJumpingAnim), + ANIMMODE_LOOP, 0.0f); + + this->actor.world.pos.y = this->actor.floorHeight + 300.0f; + this->alpha = this->actor.shape.shadowAlpha = 0; + this->unk_3F0 = 10; + this->hopAnimIndex = 1; + this->action = ENZF_ACTION_DROP_IN; + this->actor.bgCheckFlags &= ~2; + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + EnZf_SetupAction(this, EnZf_DropIn); +} + +void EnZf_DropIn(EnZf* this, GlobalContext* globalCtx) { + if (this->unk_3F0 == 1) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + this->actor.flags |= ACTOR_FLAG_0; + + if (this->actor.params == ENZF_TYPE_LIZALFOS_MINIBOSS_A) { + func_800F5ACC(NA_BGM_MINI_BOSS); + } + } + + if (this->unk_3F0 != 0) { + if (this->actor.params != ENZF_TYPE_LIZALFOS_LONE) { + this->unk_3F0--; + } else if (this->actor.xzDistToPlayer <= 160.0f) { + this->unk_3F0 = 0; + this->actor.flags |= ACTOR_FLAG_0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + + this->actor.world.pos.y = this->actor.floorHeight + 300.0f; + } else if (this->alpha < 255) { + this->alpha += 255 / 5; + } + + if ((this->actor.bgCheckFlags & 3) && (this->hopAnimIndex != 0)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ONGND); + Animation_Change(&this->skelAnime, &gZfLandingAnim, 1.0f, 0.0f, 17.0f, ANIMMODE_ONCE, 0.0f); + this->hopAnimIndex = 0; + this->actor.bgCheckFlags &= ~2; + this->actor.world.pos.y = this->actor.floorHeight; + this->actor.velocity.y = 0.0f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + } + + if (SkelAnime_Update(&this->skelAnime)) { + this->alpha = 255; + if (this->actor.params > ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // Only miniboss B + EnZf_SetupSheatheSword(this, globalCtx); + } else { + func_80B45384(this); + } + } + this->actor.shape.shadowAlpha = this->alpha; +} + +// stop? and choose an action +void func_80B45384(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfCryingAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gZfCryingAnim), + ANIMMODE_LOOP_INTERP, -4.0f); + this->action = ENZF_ACTION_3; + this->unk_3F0 = Rand_ZeroOne() * 10.0f + 5.0f; + this->actor.speedXZ = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y; + EnZf_SetupAction(this, func_80B4543C); +} + +void func_80B4543C(EnZf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s16 angleToPlayer = (this->actor.yawTowardsPlayer - this->headRot) - this->actor.shape.rot.y; + + angleToPlayer = ABS(angleToPlayer); + SkelAnime_Update(&this->skelAnime); + + if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + if (this->unk_3F4 != 0) { + this->unk_3F4--; + if (angleToPlayer >= 0x1FFE) { + return; + } + this->unk_3F4 = 0; + + } else if (EnZf_ChooseAction(globalCtx, this)) { + return; + } + } + angleToPlayer = player->actor.shape.rot.y - this->actor.shape.rot.y; + angleToPlayer = ABS(angleToPlayer); + + if ((this->actor.xzDistToPlayer < 100.0f) && (player->swordState != 0) && (angleToPlayer >= 0x1F40)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + func_80B483E4(this, globalCtx); + } else if (this->unk_3F0 != 0) { + this->unk_3F0--; + } else { + if (Actor_IsFacingPlayer(&this->actor, 30 * 0x10000 / 360)) { + if ((this->actor.xzDistToPlayer < 200.0f) && (this->actor.xzDistToPlayer > 100.0f) && + (Rand_ZeroOne() < 0.3f)) { + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + EnZf_SetupJumpForward(this); + } else { + func_80B483E4(this, globalCtx); + } + } else if (Rand_ZeroOne() > 0.3f) { + EnZf_SetupApproachPlayer(this, globalCtx); + } else { + func_80B483E4(this, globalCtx); + } + } else { + func_80B4604C(this); + } + + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + } + } +} + +void EnZf_SetupApproachPlayer(EnZf* this, GlobalContext* globalCtx) { + Animation_MorphToLoop(&this->skelAnime, &gZfWalkingAnim, -4.0f); + this->action = ENZF_ACTION_APPROACH_PLAYER; + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + this->nextPlatform = EnZf_FindNextPlatformTowardsPlayer(&this->actor.world.pos, this->curPlatform, + this->homePlatform, globalCtx); + this->hopAnimIndex = 0; + } + this->actor.speedXZ = 0.0f; + EnZf_SetupAction(this, EnZf_ApproachPlayer); +} + +void EnZf_ApproachPlayer(EnZf* this, GlobalContext* globalCtx) { + s32 sp54; + s32 sp50; + s32 temp; + s16 temp_v1; + s16 sp48 = -1; + f32 sp44 = 350.0f; + f32 sp40 = 0.0f; + Player* player = GET_PLAYER(globalCtx); + s32 sp30; + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + sp48 = EnZf_FindPlatform(&player->actor.world.pos, sp48); + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, sp48); + + if (this->actor.world.pos.y >= 420.0f) { + sp44 = 270.0f; + } + } + + if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { + if (sp48 != this->curPlatform) { + this->nextPlatform = EnZf_FindNextPlatformTowardsPlayer(&this->actor.world.pos, this->curPlatform, + this->homePlatform, globalCtx); + + if ((sp48 < 0) && (this->nextPlatform == this->curPlatform)) { + sp48 = this->curPlatform; + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + } else { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer = + Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->nextPlatform]); + + temp_v1 = this->actor.wallYaw - this->actor.shape.rot.y; + temp_v1 = ABS(temp_v1); + + if ((this->unk_3F8 && (this->actor.speedXZ > 0.0f)) || + ((this->actor.bgCheckFlags & 8) && (temp_v1 >= 0x5C19))) { + if ((Actor_WorldDistXZToPoint(&this->actor, &sPlatformPositions[this->nextPlatform]) < sp44) && + !EnZf_PrimaryFloorCheck(this, globalCtx, 191.9956f)) { + EnZf_SetupJumpForward(this); + + if (this->actor.bgCheckFlags & 8) { + this->actor.velocity.y = 20.0f; + } + + return; + } else { + this->actor.world.rot.y = + Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->curPlatform]); + } + } else { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 1.5f, 0.0f); + } + } + } + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + sp40 = 100.0f; + } + + if (this->actor.xzDistToPlayer <= (70.0f + sp40)) { + Math_SmoothStepToF(&this->actor.speedXZ, -8.0f, 1.0f, 0.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->actor.speedXZ, 8.0f, 1.0f, 0.5f, 0.0f); + } + + this->skelAnime.playSpeed = this->actor.speedXZ * 1.2f; + + temp_v1 = player->actor.shape.rot.y - this->actor.shape.rot.y; + temp_v1 = ABS(temp_v1); + + if ((sp48 == this->curPlatform) && (this->actor.xzDistToPlayer < 150.0f) && (player->swordState != 0) && + (temp_v1 >= 0x1F40)) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (Rand_ZeroOne() > 0.7f) { + func_80B483E4(this, globalCtx); + return; + } + } + + sp54 = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + sp50 = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + sp30 = (f32)ABS(this->skelAnime.playSpeed); + + if (sp48 == this->curPlatform) { + if (!Actor_IsFacingPlayer(&this->actor, 0x11C7)) { + if (Rand_ZeroOne() > 0.5f) { + func_80B462E4(this, globalCtx); + } else { + func_80B45384(this); + } + } else if (this->actor.xzDistToPlayer < 100.0f) { + if ((Rand_ZeroOne() > 0.05f) && EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else if (Rand_ZeroOne() > 0.5f) { + func_80B483E4(this, globalCtx); + } else { + func_80B45384(this); + } + } else { + if (this->unk_3F8) { + func_80B462E4(this, globalCtx); + } else if (Rand_ZeroOne() < 0.1f) { + func_80B45384(this); + } + } + } + + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + if (EnZf_ChooseAction(globalCtx, this)) { + return; + } + + if ((this->actor.xzDistToPlayer < 180.0f) && (this->actor.xzDistToPlayer > 160.0f) && + Actor_IsFacingPlayer(&this->actor, 0x71C)) { + if (Actor_IsTargeted(globalCtx, &this->actor)) { + if (Rand_ZeroOne() < 0.1f) { + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + EnZf_SetupJumpForward(this); + return; + } + } else { + func_80B483E4(this, globalCtx); + return; + } + } + } + + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + + if (sp54 != (s32)this->skelAnime.curFrame) { + temp = sp30 + sp54; + + if (((sp50 < 2) && (temp >= 4)) || ((sp50 < 32) && (temp >= 34))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); + } + } + } +} + +void EnZf_SetupJumpForward(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfJumpingAnim, 1.0f, 0.0f, 3.0f, ANIMMODE_ONCE, -3.0f); + this->unk_3F0 = 0; + this->hopAnimIndex = 1; + this->actor.velocity.y = 15.0f; + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + this->actor.speedXZ = 16.0f; + } else { + this->actor.speedXZ = 10.0f; + } + + this->action = ENZF_ACTION_JUMP_FORWARD; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); + EnZf_SetupAction(this, EnZf_JumpForward); +} + +void EnZf_JumpForward(EnZf* this, GlobalContext* globalCtx) { + if ((this->unk_3F0 != 0) && (this->actor.world.pos.y <= this->actor.floorHeight)) { + this->actor.world.pos.y = this->actor.floorHeight; + this->hopAnimIndex = 0; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->unk_3F0 == 0) { + Animation_Change(&this->skelAnime, &gZfLandingAnim, 3.0f, 0.0f, 17.0f, ANIMMODE_ONCE, -3.0f); + this->unk_3F0 = 10; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); + } else { + this->actor.speedXZ = 0.0f; + this->hopAnimIndex = 0; + EnZf_SetupApproachPlayer(this, globalCtx); + } + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + + if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->actor.bgCheckFlags & 3)) { + if (EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else { + func_80B483E4(this, globalCtx); + } + } +} + +void func_80B4604C(EnZf* this) { + Animation_MorphToLoop(&this->skelAnime, &gZfWalkingAnim, -4.0f); + this->action = ENZF_ACTION_6; + EnZf_SetupAction(this, func_80B46098); +} + +void func_80B46098(EnZf* this, GlobalContext* globalCtx) { + s32 pad; + f32 phi_f2; + Player* player = GET_PLAYER(globalCtx); + s16 temp_v0; + s16 phi_v1; + + if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { + if ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) { + temp_v0 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (temp_v0 > 0) { + phi_v1 = temp_v0 * 0.25f + 2000.0f; + } else { + phi_v1 = temp_v0 * 0.25f - 2000.0f; + } + + this->actor.shape.rot.y += phi_v1; + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (temp_v0 > 0) { + phi_f2 = phi_v1 * 1.5f; + if (phi_f2 > 2.0f) { + phi_f2 = 2.0f; + } + } else { + phi_f2 = phi_v1 * 1.5f; + if (phi_f2 < -2.0f) { + phi_f2 = -2.0f; + } + } + + this->skelAnime.playSpeed = -phi_f2; + SkelAnime_Update(&this->skelAnime); + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + if (this->curPlatform != EnZf_FindPlatform(&player->actor.world.pos, -1)) { + EnZf_SetupApproachPlayer(this, globalCtx); + return; + } + } + + if (Actor_IsFacingPlayer(&this->actor, 30 * 0x10000 / 360)) { + if (Rand_ZeroOne() > 0.8f) { + func_80B462E4(this, globalCtx); + } else { + EnZf_SetupApproachPlayer(this, globalCtx); + } + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + } + } +} + +// Conditional setup function +void func_80B462E4(EnZf* this, GlobalContext* globalCtx) { + if ((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ || + Actor_TestFloorInDirection(&this->actor, globalCtx, 40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF)) || + Actor_TestFloorInDirection(&this->actor, globalCtx, -40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF))) { + Animation_PlayLoop(&this->skelAnime, &gZfSidesteppingAnim); + this->actor.speedXZ = Rand_CenteredFloat(12.0f); + this->actor.world.rot.y = this->actor.shape.rot.y; + this->unk_3F0 = Rand_ZeroOne() * 10.0f + 20.0f; + this->hopAnimIndex = 0; + this->action = ENZF_ACTION_7; + this->unk_408 = 0.0f; + EnZf_SetupAction(this, func_80B463E4); + } else { + EnZf_SetupApproachPlayer(this, globalCtx); + } +} + +void func_80B463E4(EnZf* this, GlobalContext* globalCtx) { + s16 angleBehindPlayer; + s16 phi_v0_3; + s32 pad; + s32 curKeyFrame; + s32 prevKeyFrame; + s32 playSpeed; + Player* player = GET_PLAYER(globalCtx); + f32 baseRange = 0.0f; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); + + if (!EnZf_DodgeRangedEngaging(globalCtx, this) && + ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this))) { + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3A98; + angleBehindPlayer = player->actor.shape.rot.y + 0x8000; + + if (Math_SinS(angleBehindPlayer - this->actor.shape.rot.y) >= 0.0f) { + this->actor.speedXZ -= 0.25f; + if (this->actor.speedXZ < -8.0f) { + this->actor.speedXZ = -8.0f; + } + } else if (Math_SinS(angleBehindPlayer - this->actor.shape.rot.y) < 0.0f) { // Superfluous check + this->actor.speedXZ += 0.25f; + if (this->actor.speedXZ > 8.0f) { + this->actor.speedXZ = 8.0f; + } + } + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + if (this->unk_3F8) { + this->actor.speedXZ = -this->actor.speedXZ; + } + } else if ((this->actor.bgCheckFlags & 8) || + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, + this->actor.shape.rot.y + 0x3FFF)) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + phi_v0_3 = this->actor.shape.rot.y + 0x3FFF; + } else { + phi_v0_3 = this->actor.shape.rot.y - 0x3FFF; + } + phi_v0_3 = this->actor.wallYaw - phi_v0_3; + } else { + this->actor.speedXZ *= -0.8f; + phi_v0_3 = 0; + } + + if (ABS(phi_v0_3) > 0x4000) { + this->actor.speedXZ *= -0.8f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ += 0.5f; + } + } + } + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + baseRange = 100.0f; + } + + if (this->actor.xzDistToPlayer <= (70.0f + baseRange)) { + Math_SmoothStepToF(&this->unk_408, -4.0f, 1.0f, 1.5f, 0.0f); + } else if ((90.0f + baseRange) < this->actor.xzDistToPlayer) { + Math_SmoothStepToF(&this->unk_408, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_408, 0.0f, 1.0f, 5.65f, 0.0f); + } + + if ((this->unk_408 != 0.0f) && !EnZf_SecondaryFloorCheck(this, globalCtx, this->unk_408)) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->unk_408; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->unk_408; + } + + if (ABS(this->actor.speedXZ) >= ABS(this->unk_408)) { + this->skelAnime.playSpeed = this->actor.speedXZ * 0.75f; + } else if (this->skelAnime.playSpeed < 0.0f) { + this->skelAnime.playSpeed = this->unk_408 * -0.75f; + } else { + this->skelAnime.playSpeed = this->unk_408 * 0.75f; + } + + curKeyFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + playSpeed = (f32)ABS(this->skelAnime.playSpeed); + + if (curKeyFrame != (s32)this->skelAnime.curFrame) { + s32 nextKeyFrame = playSpeed + curKeyFrame; + + if (((prevKeyFrame < 14) && (nextKeyFrame > 15)) || ((prevKeyFrame < 27) && (nextKeyFrame > 28))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); + } + } + + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + + if ((Math_CosS(angleBehindPlayer - this->actor.shape.rot.y) < -0.85f) || (this->unk_3F0 == 0)) { + this->actor.world.rot.y = this->actor.shape.rot.y; + + if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0) && + EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else { + func_80B45384(this); + } + } else if (this->unk_3F0 != 0) { + this->unk_3F0--; + } + } +} + +void EnZf_SetupSlash(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfSlashAnim, 1.25f, 0.0f, Animation_GetLastFrame(&gZfSlashAnim), ANIMMODE_ONCE, + -4.0f); + + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + this->skelAnime.playSpeed = 1.75f; + } + + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + this->action = ENZF_ACTION_SLASH; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + this->actor.speedXZ = 0.0f; + EnZf_SetupAction(this, EnZf_Slash); +} + +void EnZf_Slash(EnZf* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 rotDiff; + s16 yawDiff; + + this->actor.speedXZ = 0.0f; + + if ((s32)this->skelAnime.curFrame == 10) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ATTACK); + } + + if (SkelAnime_Update(&this->skelAnime)) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIndex)); + + if ((this->actor.params == ENZF_TYPE_DINOLFOS) && !Actor_IsFacingPlayer(&this->actor, 5460)) { + func_80B45384(this); + this->unk_3F0 = Rand_ZeroOne() * 5.0f + 5.0f; + this->unk_3F4 = Rand_ZeroOne() * 20.0f + 100.0f; + } else if ((Rand_ZeroOne() > 0.7f) || (this->actor.xzDistToPlayer >= 120.0f)) { + func_80B45384(this); + this->unk_3F0 = Rand_ZeroOne() * 5.0f + 5.0f; + } else { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + + if (Rand_ZeroOne() > 0.7f) { + func_80B483E4(this, globalCtx); + } else { + rotDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + rotDiff = ABS(rotDiff); + + if (rotDiff <= 10000) { + yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + yawDiff = ABS(yawDiff); + + if (yawDiff > 16000) { + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + func_80B483E4(this, globalCtx); + } else if (player->stateFlags1 & 0x6010) { + if (this->actor.isTargeted) { + EnZf_SetupSlash(this); + } else { + func_80B483E4(this, globalCtx); + } + } else { + EnZf_SetupSlash(this); + } + } else { + func_80B483E4(this, globalCtx); + } + } + } + } +} + +void EnZf_SetupRecoilFromBlockedSlash(EnZf* this) { + f32 frame = this->skelAnime.curFrame - 3.0f; + + Animation_Change(&this->skelAnime, &gZfSlashAnim, -1.0f, frame, 0.0f, ANIMMODE_ONCE, 0.0f); + this->action = ENZF_ACTION_RECOIL_FROM_BLOCKED_SLASH; + EnZf_SetupAction(this, EnZf_RecoilFromBlockedSlash); +} + +void EnZf_RecoilFromBlockedSlash(EnZf* this, GlobalContext* globalCtx) { + if (SkelAnime_Update(&this->skelAnime)) { + if (Rand_ZeroOne() > 0.7f) { + func_80B45384(this); + } else if ((Rand_ZeroOne() > 0.2f) && EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else { + func_80B483E4(this, globalCtx); + } + } +} + +void EnZf_SetupJumpBack(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfJumpingAnim, -1.0f, 3.0f, 0.0f, ANIMMODE_ONCE, -3.0f); + this->unk_3F0 = 0; + this->hopAnimIndex = 1; + this->action = ENZF_ACTION_JUMP_BACK; + this->actor.velocity.y = 15.0f; + this->actor.speedXZ = -15.0f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); + EnZf_SetupAction(this, EnZf_JumpBack); +} + +void EnZf_JumpBack(EnZf* this, GlobalContext* globalCtx) { + if ((this->unk_3F0 != 0) && (this->actor.world.pos.y <= this->actor.floorHeight)) { + this->actor.world.pos.y = this->actor.floorHeight; + this->hopAnimIndex = 0; + this->actor.velocity.y = 0.0f; + this->actor.speedXZ = 0.0f; + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->unk_3F0 == 0) { + Animation_Change(&this->skelAnime, &gZfLandingAnim, 3.0f, 0.0f, 17.0f, ANIMMODE_ONCE, -3.0f); + this->unk_3F0 = 10; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); + } else if ((globalCtx->gameplayFrames % 2) != 0) { + func_80B483E4(this, globalCtx); + } else { + func_80B45384(this); + } + } + + if ((globalCtx->state.frames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } +} + +void EnZf_SetupStunned(EnZf* this) { + if ((this->actor.bgCheckFlags & 1) && ((this->actor.velocity.y == 0.0f) || (this->actor.velocity.y == -4.0f))) { + this->actor.speedXZ = 0.0f; + this->hopAnimIndex = 0; + } else { + this->hopAnimIndex = 1; + } + + if (this->damageEffect == ENZF_DMGEFF_ICE) { + this->iceTimer = 36; + } else { + Animation_PlayOnceSetSpeed(&this->skelAnime, &gZfKnockedBackAnim, 0.0f); + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_GOMA_JR_FREEZE); + this->action = ENZF_ACTION_STUNNED; + EnZf_SetupAction(this, EnZf_Stunned); +} + +void EnZf_Stunned(EnZf* this, GlobalContext* globalCtx) { + s16 angleToWall; + + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + this->hopAnimIndex = 0; + } + + if ((this->actor.colorFilterTimer == 0) && (this->actor.bgCheckFlags & 1)) { + if (this->actor.colChkInfo.health == 0) { + EnZf_SetupDie(this); + } else if ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) { + if (D_80B4A1B4 != -1) { + func_80B44DC4(this, globalCtx); + } else { + angleToWall = this->actor.wallYaw - this->actor.shape.rot.y; + angleToWall = ABS(angleToWall); + + if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->actor.bgCheckFlags & 8) && + (ABS(angleToWall) < 0x2EE0) && (this->actor.xzDistToPlayer < 90.0f)) { + this->actor.world.rot.y = this->actor.shape.rot.y; + EnZf_SetupJumpUp(this); + } else if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { + if (this->actor.params != ENZF_TYPE_DINOLFOS) { + func_80B44DC4(this, globalCtx); + } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) != 0) && + EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else { + func_80B44DC4(this, globalCtx); + } + } + } + } + } +} + +void EnZf_SetupSheatheSword(EnZf* this, GlobalContext* globalCtx) { + f32 morphFrames = 0.0f; + f32 lastFrame = Animation_GetLastFrame(&gZfSheathingSwordAnim); + + if (this->action <= ENZF_ACTION_DAMAGED) { + morphFrames = -4.0f; + } + + Animation_Change(&this->skelAnime, &gZfSheathingSwordAnim, 2.0f, 0.0f, lastFrame, ANIMMODE_ONCE, morphFrames); + this->action = ENZF_ACTION_SHEATHE_SWORD; + this->actor.speedXZ = 0.0f; + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + this->nextPlatform = + EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnZf_SetupAction(this, EnZf_SheatheSword); +} + +void EnZf_SheatheSword(EnZf* this, GlobalContext* globalCtx) { + s16 yaw = Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->nextPlatform]) + 0x8000; + + Math_SmoothStepToS(&this->actor.world.rot.y, yaw, 1, 1000, 0); + this->actor.shape.rot.y = this->actor.world.rot.y; + + if (SkelAnime_Update(&this->skelAnime)) { + this->actor.world.rot.y = yaw - 0x8000; + EnZf_SetupHopAway(this, globalCtx); + this->swordSheathed = true; + } +} + +void EnZf_SetupHopAndTaunt(EnZf* this) { + this->hopAnimIndex = 0; + Animation_MorphToPlayOnce(&this->skelAnime, sHoppingAnims[0], -4.0f); + this->action = ENZF_ACTION_HOP_AND_TAUNT; + this->actor.speedXZ = 0.0f; + this->unk_40C = 0.0f; + this->unk_408 = 0.0f; + EnZf_SetupAction(this, EnZf_HopAndTaunt); +} + +void EnZf_HopAndTaunt(EnZf* this, GlobalContext* globalCtx) { + f32 lastFrame; + f32 maxDist = 400.0f; + + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.5f, 0.0f); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer + 0x8000, 1, 4000, 0); + + // Upstairs + if (this->actor.world.pos.y >= 420.0f) { + maxDist = 250.0f; + } + + // If player gets too close, run away + if ((this->actor.xzDistToPlayer < maxDist) && (this->hopAnimIndex != 1)) { + this->actor.shape.rot.y = this->actor.world.rot.y; + EnZf_SetupSheatheSword(this, globalCtx); + } else { + if (this->hopAnimIndex != 1) { + EnZf_DodgeRangedWaiting(globalCtx, this); + } + + if (SkelAnime_Update(&this->skelAnime)) { + this->hopAnimIndex++; // move on to next animation + + // Loop back to beginning + if (this->hopAnimIndex >= ARRAY_COUNT(sHoppingAnims)) { + this->hopAnimIndex = 0; + } + + if ((this->unk_408 != 0.0f) || (this->unk_40C != 0.0f)) { + this->hopAnimIndex = 1; + } + + lastFrame = Animation_GetLastFrame(sHoppingAnims[this->hopAnimIndex]); + + switch (this->hopAnimIndex) { + case 0: + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = this->actor.floorHeight; + break; + + case 1: + this->actor.velocity.y = this->unk_40C + 10.0f; + this->actor.speedXZ = this->unk_408; + this->unk_408 = 0.0f; + this->unk_40C = 0.0f; + break; + + case 2: + this->actor.world.pos.y = this->actor.floorHeight; + lastFrame = 3.0f; + break; + + default: + break; + } + + Animation_Change(&this->skelAnime, sHoppingAnims[this->hopAnimIndex], 1.5f, 0.0f, lastFrame, ANIMMODE_ONCE, + 0.0f); + } + + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + } +} + +void EnZf_SetupHopAway(EnZf* this, GlobalContext* globalCtx) { + this->hopAnimIndex = 0; + Animation_PlayOnce(&this->skelAnime, sHoppingAnims[0]); + this->action = ENZF_ACTION_HOP_AWAY; + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + this->nextPlatform = + EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); + EnZf_SetupAction(this, EnZf_HopAway); +} + +void EnZf_HopAway(EnZf* this, GlobalContext* globalCtx) { + f32 sp74; + f32 sp70 = 1.0f; + f32 phi_f20 = 550.0f; + s32 pad; + f32 phi_f20_2; + f32 phi_f0; + s32 pad2; + s16 sp5A; + s32 sp54; + s32 temp_v1_2; + s32 phi_v1; + + sp74 = Actor_WorldDistXZToPoint(&this->actor, &sPlatformPositions[this->nextPlatform]); + sp54 = this->hopAnimIndex; + + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + + // Upstairs + if (this->actor.world.pos.y >= 420.0f) { + phi_f20 = 280.0f; + } + + sp5A = Actor_WorldYawTowardPoint(&this->actor, &sPlatformPositions[this->nextPlatform]); + + switch (this->hopAnimIndex) { + case 0: + this->actor.world.rot.y = sp5A; + this->actor.shape.rot.y = sp5A + 0x8000; + D_80B4AB30 = 0; + this->homePlatform = this->curPlatform; + temp_v1_2 = !EnZf_PrimaryFloorCheck(this, globalCtx, 107.0f); + temp_v1_2 |= !EnZf_PrimaryFloorCheck(this, globalCtx, 220.0f) << 1; + this->hopAnimIndex++; + + switch (temp_v1_2) { + case 1: + case 1 | 2: + this->actor.velocity.y = 12.0f; + if (this->actor.bgCheckFlags & 8) { + this->actor.velocity.y += 8.0f; + } + + this->actor.speedXZ = 8.0f; + break; + + case 2: + this->actor.velocity.y = 15.0f; + this->actor.speedXZ = 20.0f; + break; + + default: // 0 + phi_f20_2 = 107.0f; + phi_f20_2 += 10.0f; + phi_f0 = 8.0f; + phi_f0 += 1.2f; + + for (phi_v1 = 20; phi_v1 >= 0; phi_v1--, phi_f20_2 += 10.0f, phi_f0 += 1.2f) { + + if (!EnZf_PrimaryFloorCheck(this, globalCtx, phi_f20_2)) { + this->actor.speedXZ = phi_f0; + this->actor.velocity.y = 12.0f; + break; + } + } + if (this->actor.speedXZ == 0.0f) { + EnZf_SetupHopAndTaunt(this); + } + } + break; + + case 1: + if ((this->actor.bgCheckFlags & 2) || (this->actor.bgCheckFlags & 1)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ONGND); + this->actor.velocity.y = 0.0f; + this->actor.world.pos.y = this->actor.floorHeight; + this->actor.speedXZ = 0.0f; + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->leftFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + Actor_SpawnFloorDustRing(globalCtx, &this->actor, &this->rightFootPos, 3.0f, 2, 2.0f, 0, 0, 0); + + if (phi_f20 <= this->actor.xzDistToPlayer) { + EnZf_SetupHopAndTaunt(this); + } else if (sp74 < 80.0f) { + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + this->nextPlatform = EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, + this->homePlatform, globalCtx); + } + + this->hopAnimIndex = 0; + sp70 = 2.0f; + } else { + Math_SmoothStepToS(&this->actor.world.rot.y, sp5A, 1, 0xFA0, 0); + this->actor.shape.rot.y = this->actor.world.rot.y + 0x8000; + D_80B4AB30++; + } + break; + + case 2: + if (this->skelAnime.curFrame == this->skelAnime.endFrame) { + this->hopAnimIndex = 0; + } + break; + } + + if (sp54 != this->hopAnimIndex) { + Animation_PlayOnceSetSpeed(&this->skelAnime, sHoppingAnims[this->hopAnimIndex], sp70); + } + + SkelAnime_Update(&this->skelAnime); +} + +void EnZf_SetupDrawSword(EnZf* this, GlobalContext* globalCtx) { + Animation_PlayOnce(&this->skelAnime, &gZfDrawingSwordAnim); + this->actor.world.rot.y += 0x8000; + this->action = ENZF_ACTION_DRAW_SWORD; + this->actor.speedXZ = 0.0f; + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + this->nextPlatform = + EnZf_FindNextPlatformAwayFromPlayer(&this->actor.world.pos, this->curPlatform, this->homePlatform, globalCtx); + EnZf_SetupAction(this, EnZf_DrawSword); +} + +void EnZf_DrawSword(EnZf* this, GlobalContext* globalCtx) { + s16 yawTowardsPlayer = this->actor.yawTowardsPlayer; + + if (this->skelAnime.curFrame >= 26.0f) { + Math_SmoothStepToS(&this->actor.shape.rot.y, yawTowardsPlayer, 1, 6000, 0); + } + + if (SkelAnime_Update(&this->skelAnime)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + this->actor.world.rot.y = yawTowardsPlayer; + this->hopAnimIndex = -1; + func_80B45384(this); + } + + if (this->skelAnime.curFrame == 22.0f) { + this->swordSheathed = false; + } +} + +void EnZf_SetupDamaged(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfKnockedBackAnim, 1.5f, 0.0f, Animation_GetLastFrame(&gZfKnockedBackAnim), + ANIMMODE_ONCE, -4.0f); + + if ((this->actor.bgCheckFlags & 1) && ((this->actor.velocity.y == 0.0f) || (this->actor.velocity.y == -4.0f))) { + this->actor.speedXZ = -4.0f; + this->hopAnimIndex = 0; + } else { + this->hopAnimIndex = 1; + } + + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + this->skelAnime.playSpeed = 4.5f; + } + + if (this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // not miniboss + this->actor.world.rot.y = this->actor.yawTowardsPlayer; + } + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DAMAGE); + this->action = ENZF_ACTION_DAMAGED; + EnZf_SetupAction(this, EnZf_Damaged); +} + +void EnZf_Damaged(EnZf* this, GlobalContext* globalCtx) { + s16 wallYawDiff; + + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + + if (this->actor.bgCheckFlags & 1) { + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ += 0.05f; + } + this->hopAnimIndex = 0; + } + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4500, 0); + + if (((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) && + SkelAnime_Update(&this->skelAnime) && (this->actor.bgCheckFlags & 1)) { + + if (D_80B4A1B4 != -1) { + if (this->damageEffect == ENZF_DMGEFF_PROJECTILE) { + D_80B4A1B0++; + } else { + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (!EnZf_PrimaryFloorCheck(this, globalCtx, 135.0f) && (this->actor.xzDistToPlayer < 90.0f)) { + EnZf_SetupJumpUp(this); + } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0)) { + EnZf_SetupSlash(this); + } else { + func_80B44DC4(this, globalCtx); + } + } + } else { + + wallYawDiff = this->actor.wallYaw - this->actor.shape.rot.y; + wallYawDiff = ABS(wallYawDiff); + + if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->actor.bgCheckFlags & 8) && + (ABS(wallYawDiff) < 12000) && (this->actor.xzDistToPlayer < 90.0f)) { + EnZf_SetupJumpUp(this); + } else if (!EnZf_DodgeRangedEngaging(globalCtx, this)) { + if (this->actor.params != ENZF_TYPE_DINOLFOS) { + this->actor.world.rot.y = this->actor.shape.rot.y; + + if (!EnZf_PrimaryFloorCheck(this, globalCtx, 135.0f) && (this->actor.xzDistToPlayer < 90.0f)) { + EnZf_SetupJumpUp(this); + } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0)) { + EnZf_SetupSlash(this); + } else { + func_80B44DC4(this, globalCtx); + } + } else if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0) && + EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else { + func_80B44DC4(this, globalCtx); + } + } + } + } +} + +void EnZf_SetupJumpUp(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfJumpingAnim, 1.0f, 0.0f, 3.0f, ANIMMODE_ONCE, 0.0f); + this->unk_3F0 = 0; + this->hopAnimIndex = 1; + this->action = ENZF_ACTION_JUMP_UP; + this->actor.velocity.y = 22.0f; + this->actor.speedXZ = 7.5f; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_JUMP); + this->actor.world.rot.y = this->actor.shape.rot.y; + EnZf_SetupAction(this, EnZf_JumpUp); +} + +void EnZf_JumpUp(EnZf* this, GlobalContext* globalCtx) { + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); + if (this->actor.velocity.y >= 5.0f) { + func_800355B8(globalCtx, &this->leftFootPos); + func_800355B8(globalCtx, &this->rightFootPos); + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->unk_3F0 == 0) { + Animation_Change(&this->skelAnime, &gZfSlashAnim, 3.0f, 0.0f, 13.0f, ANIMMODE_ONCE, -4.0f); + this->unk_3F0 = 10; + } else if (this->actor.bgCheckFlags & 3) { + this->actor.velocity.y = 0.0f; + this->actor.world.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsPlayer; + this->actor.speedXZ = 0.0f; + this->actor.world.pos.y = this->actor.floorHeight; + EnZf_SetupSlash(this); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_ATTACK); + this->skelAnime.curFrame = 13.0f; + } + } +} + +// Conditional setup function +void func_80B483E4(EnZf* this, GlobalContext* globalCtx) { + s16 playerRotY; + Player* player; + + if ((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* not miniboss */ || + Actor_TestFloorInDirection(&this->actor, globalCtx, 40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF)) || + Actor_TestFloorInDirection(&this->actor, globalCtx, -40.0f, (s16)(this->actor.shape.rot.y + 0x3FFF))) { + Animation_PlayLoop(&this->skelAnime, &gZfSidesteppingAnim); + player = GET_PLAYER(globalCtx); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 4000, 1); + playerRotY = player->actor.shape.rot.y; + + if (Math_SinS(playerRotY - this->actor.shape.rot.y) >= 0.0f) { + this->actor.speedXZ = -6.0f; + } else if (Math_SinS(playerRotY - this->actor.shape.rot.y) < 0.0f) { // Superfluous check + this->actor.speedXZ = 6.0f; + } + + this->unk_408 = 0.0f; + this->hopAnimIndex = 0; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + this->unk_3F0 = Rand_ZeroOne() * 10.0f + 5.0f; + this->action = ENZF_ACTION_CIRCLE_AROUND_PLAYER; + EnZf_SetupAction(this, EnZf_CircleAroundPlayer); + } else { + EnZf_SetupApproachPlayer(this, globalCtx); + } +} + +void EnZf_CircleAroundPlayer(EnZf* this, GlobalContext* globalCtx) { + s16 playerRot; + s16 phi_v0_4; + Player* player = GET_PLAYER(globalCtx); + s32 curKeyFrame; + s32 prevKeyFrame; + s32 playSpeed; + f32 baseRange = 0.0f; + + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.yawTowardsPlayer, 1, 0xBB8, 1); + playerRot = player->actor.shape.rot.y; + + if (this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) { // miniboss + if (this->unk_3F8) { + this->actor.speedXZ = -this->actor.speedXZ; + } + } else if ((this->actor.bgCheckFlags & 8) || + !Actor_TestFloorInDirection(&this->actor, globalCtx, this->actor.speedXZ, + this->actor.shape.rot.y + 0x3FFF)) { + if (this->actor.bgCheckFlags & 8) { + if (this->actor.speedXZ >= 0.0f) { + phi_v0_4 = this->actor.shape.rot.y + 0x3FFF; + } else { + phi_v0_4 = this->actor.shape.rot.y - 0x3FFF; + } + + phi_v0_4 = this->actor.wallYaw - phi_v0_4; + } else { + this->actor.speedXZ *= -0.8f; + phi_v0_4 = 0; + } + + if (ABS(phi_v0_4) > 0x4000) { + this->actor.speedXZ *= -0.8f; + if (this->actor.speedXZ < 0.0f) { + this->actor.speedXZ -= 0.5f; + } else { + this->actor.speedXZ += 0.5f; + } + } + } + + if (Math_SinS(playerRot - this->actor.shape.rot.y) >= 0.0f) { + this->actor.speedXZ += 0.125f; + } else { + this->actor.speedXZ -= 0.125f; + } + + this->actor.world.rot.y = this->actor.shape.rot.y + 0x4000; + + if (Actor_OtherIsTargeted(globalCtx, &this->actor)) { + baseRange = 100.0f; + } + + if (this->actor.xzDistToPlayer <= (70.0f + baseRange)) { + Math_SmoothStepToF(&this->unk_408, -4.0f, 1.0f, 1.5f, 0.0f); + } else if ((90.0f + baseRange) < this->actor.xzDistToPlayer) { + Math_SmoothStepToF(&this->unk_408, 4.0f, 1.0f, 1.5f, 0.0f); + } else { + Math_SmoothStepToF(&this->unk_408, 0.0f, 1.0f, 5.65f, 0.0f); + } + + if ((this->unk_408 != 0.0f) && !EnZf_SecondaryFloorCheck(this, globalCtx, this->unk_408)) { + this->actor.world.pos.x += Math_SinS(this->actor.shape.rot.y) * this->unk_408; + this->actor.world.pos.z += Math_CosS(this->actor.shape.rot.y) * this->unk_408; + } + + if (ABS(this->actor.speedXZ) >= ABS(this->unk_408)) { + this->skelAnime.playSpeed = -this->actor.speedXZ * 0.75f; + } else if (this->skelAnime.playSpeed < 0.0f) { + this->skelAnime.playSpeed = this->unk_408 * -0.75f; + } else { + this->skelAnime.playSpeed = this->unk_408 * 0.75f; + } + + curKeyFrame = this->skelAnime.curFrame; + SkelAnime_Update(&this->skelAnime); + prevKeyFrame = this->skelAnime.curFrame - ABS(this->skelAnime.playSpeed); + playSpeed = (f32)ABS(this->skelAnime.playSpeed); + + this->curPlatform = EnZf_FindPlatform(&this->actor.world.pos, this->curPlatform); + + if (EnZf_FindPlatform(&player->actor.world.pos, -1) != this->curPlatform) { + this->actor.speedXZ = 0.0f; + + if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && + (D_80B4A1B4 == this->actor.params)) { + EnZf_SetupHopAndTaunt(this); + } else { + EnZf_SetupApproachPlayer(this, globalCtx); + } + } else if ((this->actor.params != ENZF_TYPE_DINOLFOS) || !EnZf_ChooseAction(globalCtx, this)) { + if (this->unk_3F0 == 0) { + phi_v0_4 = player->actor.shape.rot.y - this->actor.shape.rot.y; + + phi_v0_4 = ABS(phi_v0_4); + + if (phi_v0_4 >= 0x3A98) { + if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) && (D_80B4A1B4 == this->actor.params)) { + EnZf_SetupHopAndTaunt(this); + } else { + func_80B45384(this); + this->unk_3F0 = Rand_ZeroOne() * 5.0f + 1.0f; + } + } else if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) && (D_80B4A1B4 == this->actor.params)) { + EnZf_SetupHopAndTaunt(this); + } else { + this->actor.world.rot.y = this->actor.shape.rot.y; + + if ((this->actor.xzDistToPlayer <= 100.0f) && ((globalCtx->gameplayFrames % 4) == 0) && + EnZf_CanAttack(globalCtx, this)) { + EnZf_SetupSlash(this); + } else if ((this->actor.xzDistToPlayer < 280.0f) && (this->actor.xzDistToPlayer > 240.0f) && + !EnZf_PrimaryFloorCheck(this, globalCtx, 191.9956f) && + ((globalCtx->gameplayFrames % 2) == 0)) { + EnZf_SetupJumpForward(this); + } else { + EnZf_SetupApproachPlayer(this, globalCtx); + } + } + } else { + this->unk_3F0--; + } + if (curKeyFrame != (s32)this->skelAnime.curFrame) { + s32 nextKeyFrame = playSpeed + curKeyFrame; + if (((prevKeyFrame < 14) && (nextKeyFrame >= 16)) || ((prevKeyFrame < 27) && (nextKeyFrame >= 29))) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_WALK); + } + } + if ((globalCtx->gameplayFrames & 0x5F) == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + } + } +} + +void EnZf_SetupDie(EnZf* this) { + Animation_Change(&this->skelAnime, &gZfDyingAnim, 1.5f, 0.0f, Animation_GetLastFrame(&gZfDyingAnim), ANIMMODE_ONCE, + -4.0f); + + if ((this->actor.bgCheckFlags & 1) && ((this->actor.velocity.y == 0.0f) || (this->actor.velocity.y == -4.0f))) { + this->actor.speedXZ = 0.0f; + this->hopAnimIndex = 0; + } else { + this->hopAnimIndex = 1; + } + + this->action = ENZF_ACTION_DIE; + this->actor.flags &= ~ACTOR_FLAG_0; + + if (D_80B4A1B4 != -1) { + if (this->actor.prev != NULL) { + ((EnZf*)this->actor.prev)->unk_3F4 = 90; + + if (this->actor.prev->colChkInfo.health < 3) { + this->actor.prev->colChkInfo.health = 3; + } + } else { + ((EnZf*)this->actor.next)->unk_3F4 = 90; + + if (this->actor.next->colChkInfo.health < 3) { + this->actor.next->colChkInfo.health = 3; + } + } + } + + D_80B4A1B0 = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DEAD); + EnZf_SetupAction(this, EnZf_Die); +} + +void EnZf_Die(EnZf* this, GlobalContext* globalCtx) { + + if (this->actor.bgCheckFlags & 2) { + this->actor.speedXZ = 0.0f; + } + + if (this->actor.bgCheckFlags & 1) { + Math_SmoothStepToF(&this->actor.speedXZ, 0.0f, 1.0f, 0.15f, 0.0f); + this->hopAnimIndex = 0; + } + + if (SkelAnime_Update(&this->skelAnime)) { + if (this->actor.category != ACTORCAT_PROP) { + if ((this->actor.params >= ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* miniboss */ && (D_80B4A1B4 == -1)) { + Flags_SetSwitch(globalCtx, this->clearFlag); + func_800F5B58(); + } else { + D_80B4A1B4 = -1; + } + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + } + + if (this->alpha != 0) { + this->actor.shape.shadowAlpha = this->alpha -= 5; + + } else { + Actor_Kill(&this->actor); + } + } else { + s32 curFrame = this->skelAnime.curFrame; + + if ((curFrame == 10) || (curFrame == 18)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_DOWN); + } + } +} + +void EnZf_UpdateHeadRotation(EnZf* this, GlobalContext* globalCtx) { + s16 angleTemp; + + if ((this->actor.params == ENZF_TYPE_DINOLFOS) && (this->action == ENZF_ACTION_3) && (this->unk_3F4 != 0)) { + this->headRot = Math_SinS(this->unk_3F4 * 1400) * 0x2AA8; + } else { + angleTemp = this->actor.yawTowardsPlayer; + angleTemp -= (s16)(this->headRot + this->actor.shape.rot.y); + this->headRotTemp = CLAMP(angleTemp, -0x7D0, 0x7D0); + this->headRot += this->headRotTemp; + this->headRot = CLAMP(this->headRot, -0x1CD7, 0x1CD7); + } +} + +void EnZf_UpdateDamage(EnZf* this, GlobalContext* globalCtx) { + s32 pad; + s16 dropParams; + + if ((this->bodyCollider.base.acFlags & AC_HIT) && (this->action <= ENZF_ACTION_STUNNED)) { + this->bodyCollider.base.acFlags &= ~AC_HIT; + + if (((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* not miniboss */ || + (D_80B4A1B4 != this->actor.params)) && + (this->actor.colChkInfo.damageEffect != ENZF_DMGEFF_IMMUNE)) { + this->damageEffect = this->actor.colChkInfo.damageEffect; + Actor_SetDropFlag(&this->actor, &this->bodyCollider.info, 0); + + if ((this->actor.colChkInfo.damageEffect == ENZF_DMGEFF_STUN) || + (this->actor.colChkInfo.damageEffect == ENZF_DMGEFF_ICE)) { + if (this->action != ENZF_ACTION_STUNNED) { + Actor_SetColorFilter(&this->actor, 0, 120, 0, 80); + Actor_ApplyDamage(&this->actor); + EnZf_SetupStunned(this); + } + } else { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_RIZA_CRY); + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 8); + + if (Actor_ApplyDamage(&this->actor) == 0) { + dropParams = 0x40; + EnZf_SetupDie(this); + + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + dropParams = 0xE0; + } + + Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.world.pos, dropParams); + Enemy_StartFinishingBlow(globalCtx, &this->actor); + } else { + if ((D_80B4A1B4 != -1) && ((this->actor.colChkInfo.health + this->actor.colChkInfo.damage) >= 4) && + (this->actor.colChkInfo.health < 4)) { + this->damageEffect = ENZF_DMGEFF_PROJECTILE; + } + + EnZf_SetupDamaged(this); + } + } + } + } +} + +void EnZf_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZf* this = (EnZf*)thisx; + s32 pad2; + + EnZf_UpdateDamage(this, globalCtx); + if (this->actor.colChkInfo.damageEffect != ENZF_DMGEFF_IMMUNE) { + this->unk_3F8 = false; + if ((this->hopAnimIndex != 1) && (this->action != ENZF_ACTION_HOP_AWAY)) { + if (this->actor.speedXZ != 0.0f) { + this->unk_3F8 = EnZf_PrimaryFloorCheck(this, globalCtx, this->actor.speedXZ * 1.5f); + } + if (!this->unk_3F8) { + this->unk_3F8 = EnZf_PrimaryFloorCheck(this, globalCtx, 0.0f); + } + } + + if (!this->unk_3F8) { + Actor_MoveForward(&this->actor); + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 25.0f, 30.0f, 60.0f, 0x1D); + + if (!(this->actor.bgCheckFlags & 1)) { + this->hopAnimIndex = 1; + } + + this->actionFunc(this, globalCtx); + } + + if (this->actor.colChkInfo.health > 0) { + if ((this->action != ENZF_ACTION_SLASH) && (this->action != ENZF_ACTION_STUNNED)) { + EnZf_UpdateHeadRotation(this, globalCtx); + } + + if ((D_80B4A1B0 != 0) && (D_80B4A1B4 != this->actor.params)) { + EnZf_SetupSheatheSword(this, globalCtx); + D_80B4A1B4 = this->actor.params; + D_80B4A1B0 = 0; + + if (this->actor.prev != NULL) { + ((EnZf*)this->actor.prev)->unk_3F4 = 90; + } else { + ((EnZf*)this->actor.next)->unk_3F4 = 90; + } + } + } + + if (this->action >= ENZF_ACTION_DIE) { + Math_SmoothStepToS(&this->headRot, 0, 1, 2000, 0); + + if (this->action <= ENZF_ACTION_HOP_AND_TAUNT) { + if ((this->unk_3F4 == 1) && (this->actor.bgCheckFlags & 1)) { + if (this->actor.colChkInfo.health > 0) { + EnZf_SetupDrawSword(this, globalCtx); + } + this->unk_3F4--; + } + } + + if (this->unk_3F4 >= 2) { + this->unk_3F4--; + } + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 40.0f; + + if ((this->actor.colChkInfo.health > 0) && (this->alpha == 255)) { + Collider_UpdateCylinder(&this->actor, &this->bodyCollider); + + if ((this->actor.world.pos.y == this->actor.floorHeight) && (this->action <= ENZF_ACTION_DAMAGED)) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + + if ((this->actor.params < ENZF_TYPE_LIZALFOS_MINIBOSS_A) /* not miniboss */ || + (D_80B4A1B4 != this->actor.params)) { + if ((this->actor.colorFilterTimer == 0) || !(this->actor.colorFilterParams & 0x4000)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->bodyCollider.base); + } + } + } + + if ((this->action == ENZF_ACTION_SLASH) && (this->skelAnime.curFrame >= 14.0f) && + (this->skelAnime.curFrame <= 20.0f)) { + if (!(this->swordCollider.base.atFlags & AT_BOUNCED) && !(this->swordCollider.base.acFlags & AC_HIT)) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->swordCollider.base); + } else { + this->swordCollider.base.atFlags &= ~AT_BOUNCED; + this->swordCollider.base.acFlags &= ~AC_HIT; + EnZf_SetupRecoilFromBlockedSlash(this); + } + } +} + +s32 EnZf_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnZf* this = (EnZf*)thisx; + + switch (limbIndex) { + case ENZF_LIMB_HEAD_ROOT: + rot->y -= this->headRot; + break; + case ENZF_LIMB_SWORD: + if (this->swordSheathed) { + *dList = gZfEmptyHandDL; + } + break; + case ENZF_LIMB_SCABBARD: + if (this->swordSheathed) { + *dList = gZfSheathedSwordDL; + } + break; + default: + break; + } + + return false; +} + +void EnZf_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + static Vec3f sUnused = { 1100.0f, -700.0f, 0.0f }; + static Vec3f footOffset = { 300.0f, 0.0f, 0.0f }; + static Vec3f D_80B4A2A4 = { 300.0f, -1700.0f, 0.0f }; // Sword tip? + static Vec3f D_80B4A2B0 = { -600.0f, 300.0f, 0.0f }; // Sword hilt? + static Vec3f swordQuadOffset1 = { 0.0f, 1500.0f, 0.0f }; + static Vec3f swordQuadOffset0 = { -600.0f, -3000.0f, 1000.0f }; + static Vec3f swordQuadOffset3 = { -600.0f, -3000.0f, -1000.0f }; + static Vec3f swordQuadOffset2 = { 1500.0f, -3000.0f, 0.0f }; + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f sp54; + Vec3f sp48; + EnZf* this = (EnZf*)thisx; + s32 bodyPart = -1; + + if (limbIndex == ENZF_LIMB_SWORD) { + Matrix_MultVec3f(&swordQuadOffset1, &this->swordCollider.dim.quad[1]); + Matrix_MultVec3f(&swordQuadOffset0, &this->swordCollider.dim.quad[0]); + Matrix_MultVec3f(&swordQuadOffset3, &this->swordCollider.dim.quad[3]); + Matrix_MultVec3f(&swordQuadOffset2, &this->swordCollider.dim.quad[2]); + Collider_SetQuadVertices(&this->swordCollider, &this->swordCollider.dim.quad[0], + &this->swordCollider.dim.quad[1], &this->swordCollider.dim.quad[2], + &this->swordCollider.dim.quad[3]); + Matrix_MultVec3f(&D_80B4A2A4, &sp54); + Matrix_MultVec3f(&D_80B4A2B0, &sp48); + + if (this->action == ENZF_ACTION_SLASH) { + if (this->skelAnime.curFrame < 14.0f) { + EffectBlure_AddSpace(Effect_GetByIndex(this->blureIndex)); + } else if (this->skelAnime.curFrame < 20.0f) { + EffectBlure_AddVertex(Effect_GetByIndex(this->blureIndex), &sp54, &sp48); + } + } + } else { + Actor_SetFeetPos(&this->actor, limbIndex, ENZF_LIMB_LEFT_FOOT, &footOffset, ENZF_LIMB_RIGHT_FOOT, &footOffset); + } + + switch (limbIndex) { + case ENZF_LIMB_LEFT_FOOT: + Matrix_MultVec3f(&footOffset, &this->leftFootPos); + break; + case ENZF_LIMB_RIGHT_FOOT: + Matrix_MultVec3f(&footOffset, &this->rightFootPos); + break; + } + + if (this->iceTimer != 0) { + switch (limbIndex) { + case ENZF_LIMB_HEAD: + bodyPart = 0; + break; + case ENZF_LIMB_NECK: + bodyPart = 1; + break; + case ENZF_LIMB_CHEST_ARMOR: + bodyPart = 2; + break; + case ENZF_LIMB_RIGHT_FOREARM: + bodyPart = 3; + break; + case ENZF_LIMB_LEFT_FOREARM: + bodyPart = 4; + break; + case ENZF_LIMB_TRUNK: + bodyPart = 5; + break; + case ENZF_LIMB_SWORD_ROOT: + bodyPart = 6; + break; + case ENZF_LIMB_RIGHT_SHIN: + bodyPart = 7; + break; + case ENZF_LIMB_LEFT_SHIN_ROOT: + bodyPart = 8; + break; + default: + break; + } + if (bodyPart >= 0) { + Matrix_MultVec3f(&zeroVec, &this->bodyPartsPos[bodyPart]); + } + } +} + +static Gfx D_80B4A2F8[] = { + gsSPTexture(0x0A00, 0x0A00, 0, G_TX_RENDERTILE, G_ON), + gsSPEndDisplayList(), +}; + +void EnZf_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZf* this = (EnZf*)thisx; + ; // Extra ";" required for matching. Cannot be if (1) {} or the like. Typo? + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zf.c", 3533); + + func_8002EBCC(thisx, globalCtx, 1); + + gSPTexture(D_80B4A2F8, IREG(0), IREG(1), 0, G_TX_RENDERTILE, G_ON); + + gSPSegment(POLY_OPA_DISP++, 0x08, D_80B4A2F8); + + if (this->alpha == 255) { + func_80093D18(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_OPA_DISP++, 0x09, &D_80116280[2]); + + POLY_OPA_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnZf_OverrideLimbDraw, EnZf_PostLimbDraw, this, POLY_OPA_DISP); + + if (this->iceTimer != 0) { + thisx->colorFilterTimer++; + this->iceTimer--; + + if ((this->iceTimer % 4) == 0) { + s32 icePosIndex = this->iceTimer >> 2; + + EffectSsEnIce_SpawnFlyingVec3f(globalCtx, thisx, &this->bodyPartsPos[icePosIndex], 150, 150, 150, 250, + 235, 245, 255, 1.4f); + } + if (1) {} + } + } else { // fades out when dead + func_80093D84(globalCtx->state.gfxCtx); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x09, &D_80116280[0]); + POLY_XLU_DISP = SkelAnime_Draw(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + EnZf_OverrideLimbDraw, EnZf_PostLimbDraw, this, POLY_XLU_DISP); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zf.c", 3601); +} + +void EnZf_SetupCircleAroundPlayer(EnZf* this, f32 speed) { + Animation_MorphToLoop(&this->skelAnime, &gZfSidesteppingAnim, -1.0f); + this->unk_3F0 = Rand_ZeroOne() * 10.0f + 8.0f; + + if (this->actor.params == ENZF_TYPE_DINOLFOS) { + this->actor.speedXZ = 2.0f * speed; + this->unk_3F0 /= 2; + } else { + this->actor.speedXZ = speed; + } + + this->hopAnimIndex = 0; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + this->action = ENZF_ACTION_CIRCLE_AROUND_PLAYER; + EnZf_SetupAction(this, EnZf_CircleAroundPlayer); +} + +s32 EnZf_DodgeRangedEngaging(GlobalContext* globalCtx, EnZf* this) { + Actor* projectileActor; + s16 yawToProjectile; + s16 phi_t0; + s16 phi_v1; + + projectileActor = Actor_GetProjectileActor(globalCtx, &this->actor, 600.0f); + + if (projectileActor != NULL) { + yawToProjectile = + Actor_WorldYawTowardActor(&this->actor, projectileActor) - (s16)(u16)(this->actor.shape.rot.y); + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + + phi_t0 = 0; + + if (EnZf_PrimaryFloorCheck(this, globalCtx, -8.0f)) { + phi_t0 = 1; + } + + if (EnZf_PrimaryFloorCheck(this, globalCtx, 8.0f)) { + phi_t0 |= 2; + } + + this->actor.world.rot.y = this->actor.shape.rot.y; + + if ((((this->actor.xzDistToPlayer < 90.0f) || (phi_t0 == 3)) && + !EnZf_PrimaryFloorCheck(this, globalCtx, 135.0f)) || + (projectileActor->id == ACTOR_ARMS_HOOK)) { + EnZf_SetupJumpUp(this); + return true; + } + + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; + + if (phi_t0 == 0) { + phi_v1 = globalCtx->gameplayFrames % 2; + } else { + phi_v1 = phi_t0; + } + + if ((ABS(yawToProjectile) < 0x2000) || (ABS(yawToProjectile) >= 0x6000)) { + if (phi_v1 & 1) { + EnZf_SetupCircleAroundPlayer(this, 8.0f); + return true; + } + EnZf_SetupCircleAroundPlayer(this, -8.0f); + return true; + } + if (ABS(yawToProjectile) < 0x5FFF) { + if (phi_v1 & 1) { + EnZf_SetupCircleAroundPlayer(this, 4.0f); + return true; + } + EnZf_SetupCircleAroundPlayer(this, -4.0f); + } + return true; + } + return false; +} + +s32 EnZf_DodgeRangedWaiting(GlobalContext* globalCtx, EnZf* this) { + Actor* projectileActor; + s16 yawToProjectile; + s16 phi_t0; + s16 sp1E; + s16 sp1C = 0; + + projectileActor = Actor_GetProjectileActor(globalCtx, &this->actor, 600.0f); + if (projectileActor != NULL) { + yawToProjectile = Actor_WorldYawTowardActor(&this->actor, projectileActor) - (s16)(u16)this->actor.shape.rot.y; + this->actor.world.rot.y = this->actor.shape.rot.y + 0x3FFF; // Set to move sideways + + phi_t0 = 0; + + if (EnZf_PrimaryFloorCheck(this, globalCtx, -70.0f)) { + phi_t0 = 1; + } + + if (EnZf_PrimaryFloorCheck(this, globalCtx, 70.0f)) { + phi_t0 |= 2; + } + + this->actor.speedXZ = 0.0f; + + if ((ABS(yawToProjectile) < 0x2000) || (ABS(yawToProjectile) >= 0x6000)) { + if (phi_t0 == 0) { + if ((globalCtx->gameplayFrames % 2) != 0) { + sp1E = 6; + } else { + sp1E = -6; + } + } else { + switch (phi_t0) { + case 1: + sp1E = 6; + break; + case 2: + sp1E = -6; + break; + case 1 | 2: + sp1C = 5; + sp1E = 0; + break; + } + } + } else if (ABS(yawToProjectile) < 0x5FFF) { + if (phi_t0 == 0) { + if ((globalCtx->gameplayFrames % 2) != 0) { + sp1E = 6; + } else { + sp1E = -6; + } + } else { + switch (phi_t0) { + case 1: + sp1E = 6; + break; + case 2: + sp1E = -6; + break; + case 1 | 2: + sp1C = 10; + sp1E = 0; + break; + } + } + } + + this->unk_408 = sp1E; + this->unk_40C = sp1C; + return true; + } + return false; +} + +void EnZf_Reset(void) { + D_80B4A1B0 = 0; + D_80B4A1B4 = 1; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.h b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.h new file mode 100644 index 000000000..b9b503468 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zf/z_en_zf.h @@ -0,0 +1,124 @@ +#ifndef Z_EN_ZF_H +#define Z_EN_ZF_H + +#include "ultra64.h" +#include "global.h" + +struct EnZf; + +typedef void (*EnZfActionFunc)(struct EnZf*, GlobalContext*); + +typedef enum { + /* -2 */ ENZF_TYPE_DINOLFOS = -2, + /* -1 */ ENZF_TYPE_LIZALFOS_LONE, // Not a miniboss, e.g. Spirit Temple + /* 0 */ ENZF_TYPE_LIZALFOS_MINIBOSS_A, // Pair with B + /* 1 */ ENZF_TYPE_LIZALFOS_MINIBOSS_B // Pair with A +} EnZfType; + +typedef enum { + /* 0 */ ENZF_ACTION_DROP_IN, + /* 3 */ ENZF_ACTION_3 = 3, // stop and choose action? + /* 5 */ ENZF_ACTION_APPROACH_PLAYER = 5, + /* 6 */ ENZF_ACTION_6, + /* 7 */ ENZF_ACTION_7, // more sidestepping? + /* 8 */ ENZF_ACTION_RECOIL_FROM_BLOCKED_SLASH, + /* 9 */ ENZF_ACTION_SLASH, + /* 11 */ ENZF_ACTION_JUMP_BACK = 11, + /* 12 */ ENZF_ACTION_CIRCLE_AROUND_PLAYER, + /* 13 */ ENZF_ACTION_JUMP_FORWARD, + /* 14 */ ENZF_ACTION_STUNNED, + /* 15 */ ENZF_ACTION_DIE, + /* 16 */ ENZF_ACTION_DAMAGED, + /* 18 */ ENZF_ACTION_SHEATHE_SWORD = 18, + /* 19 */ ENZF_ACTION_HOP_AWAY, + /* 20 */ ENZF_ACTION_HOP_AND_TAUNT, + /* 21 */ ENZF_ACTION_DRAW_SWORD, + /* 22 */ ENZF_ACTION_JUMP_UP +} EnZfAction; + +typedef enum { + /* 0x00 */ ENZF_LIMB_NONE, + /* 0x01 */ ENZF_LIMB_ROOT, + /* 0x02 */ ENZF_LIMB_BODY_ROOT, + /* 0x03 */ ENZF_LIMB_UPPER_BODY_ROOT, + /* 0x04 */ ENZF_LIMB_NECK_ROOT, + /* 0x05 */ ENZF_LIMB_HEAD_ROOT, + /* 0x06 */ ENZF_LIMB_JAW_ROOT_ROOT, + /* 0x07 */ ENZF_LIMB_JAW_ROOT, + /* 0x08 */ ENZF_LIMB_JAW, + /* 0x09 */ ENZF_LIMB_HEAD, + /* 0x0A */ ENZF_LIMB_NECK, + /* 0x0B */ ENZF_LIMB_RIGHT_ARM_ROOT, + /* 0x0C */ ENZF_LIMB_RIGHT_UPPER_ARM_ROOT, + /* 0x0D */ ENZF_LIMB_RIGHT_FOREARM_ROOT, + /* 0x0E */ ENZF_LIMB_SWORD_ROOT, + /* 0x0F */ ENZF_LIMB_SWORD, + /* 0x10 */ ENZF_LIMB_RIGHT_FOREARM, + /* 0x11 */ ENZF_LIMB_RIGHT_UPPER_ARM, + /* 0x12 */ ENZF_LIMB_LEFT_ARM_ROOT, + /* 0x13 */ ENZF_LIMB_LEFT_UPPER_ARM_ROOT, + /* 0x14 */ ENZF_LIMB_LEFT_FOREARM_ROOT, + /* 0x15 */ ENZF_LIMB_LEFT_HAND_ROOT, + /* 0x16 */ ENZF_LIMB_LEFT_HAND, + /* 0x17 */ ENZF_LIMB_LEFT_FOREARM, + /* 0x18 */ ENZF_LIMB_LEFT_UPPER_ARM, + /* 0x19 */ ENZF_LIMB_CHEST_ARMOR, + /* 0x1A */ ENZF_LIMB_TAIL_ROOT, + /* 0x1B */ ENZF_LIMB_TAIL_BASE_ROOT, + /* 0x1C */ ENZF_LIMB_TAIL_TIP_ROOT, + /* 0x1D */ ENZF_LIMB_TAIL_TIP, + /* 0x1E */ ENZF_LIMB_TAIL_BASE, + /* 0x1F */ ENZF_LIMB_SCABBARD_ROOT_ROOT, + /* 0x20 */ ENZF_LIMB_SCABBARD_ROOT, + /* 0x21 */ ENZF_LIMB_SCABBARD, + /* 0x22 */ ENZF_LIMB_RIGHT_LEG_ROOT, + /* 0x23 */ ENZF_LIMB_RIGHT_THIGH_ROOT, + /* 0x24 */ ENZF_LIMB_RIGHT_SHIN_ROOT, + /* 0x25 */ ENZF_LIMB_RIGHT_FOOT_ROOT, + /* 0x26 */ ENZF_LIMB_RIGHT_FOOT, + /* 0x27 */ ENZF_LIMB_RIGHT_SHIN, + /* 0x28 */ ENZF_LIMB_RIGHT_THIGH, + /* 0x29 */ ENZF_LIMB_LEFT_LEG_ROOT, + /* 0x2A */ ENZF_LIMB_LEFT_THIGH_ROOT, + /* 0x2B */ ENZF_LIMB_LEFT_SHIN_ROOT, + /* 0x2C */ ENZF_LIMB_LEFT_FOOT_ROOT, + /* 0x2D */ ENZF_LIMB_LEFT_FOOT, + /* 0x2E */ ENZF_LIMB_LEFT_SHIN, + /* 0x2F */ ENZF_LIMB_LEFT_THIGH, + /* 0x30 */ ENZF_LIMB_TRUNK, + /* 0x31 */ ENZF_LIMB_MAX +} EnZfLimb; // used for both Lizalfos and Dinalfos + +typedef struct EnZf { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[ENZF_LIMB_MAX]; + /* 0x02B6 */ Vec3s morphTable[ENZF_LIMB_MAX]; + /* 0x03DC */ s32 action; + /* 0x03E0 */ s32 unk_3E0; // Set but unused + /* 0x03E4 */ s32 hopAnimIndex; + /* 0x03E8 */ EnZfActionFunc actionFunc; + /* 0x03EC */ s16 headRot; + /* 0x03EE */ s16 headRotTemp; + /* 0x03F0 */ s32 unk_3F0; // attack timer? + /* 0x03F4 */ s16 unk_3F4; // tag timer? + /* 0x03F6 */ s16 iceTimer; + /* 0x03F8 */ s16 unk_3F8; // boolean, to do with movement + /* 0x03FA */ s16 swordSheathed; // boolean + /* 0x03FC */ s16 clearFlag; + /* 0x03FE */ s16 curPlatform; + /* 0x0400 */ s16 homePlatform; // Platform to return to, changed by some functions? + /* 0x0402 */ s16 nextPlatform; + /* 0x0404 */ u8 alpha; + /* 0x0408 */ f32 unk_408; // related to XZ speeds + /* 0x040C */ f32 unk_40C; // related to y velocity + /* 0x0410 */ u8 damageEffect; + /* 0x0414 */ s32 blureIndex; + /* 0x0418 */ ColliderCylinder bodyCollider; + /* 0x0464 */ ColliderQuad swordCollider; + /* 0x04E4 */ Vec3f rightFootPos; + /* 0x04F0 */ Vec3f leftFootPos; + /* 0x04FC */ Vec3f bodyPartsPos[9]; +} EnZf; // size = 0x0568 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1.c b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1.c new file mode 100644 index 000000000..0698c119d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1.c @@ -0,0 +1,631 @@ +/* + * File: z_en_zl1.c + * Overlay: ovl_En_Zl1 + * Description: Child Princess Zelda (at window) + */ + +#include "z_en_zl1.h" +#include "objects/object_zl1/object_zl1.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +void EnZl1_Init(Actor* thisx, GlobalContext* globalCtx); +void EnZl1_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnZl1_Update(Actor* thisx, GlobalContext* globalCtx); +void EnZl1_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B4AE18(EnZl1* this); +void func_80B4AF18(EnZl1* this, GlobalContext* globalCtx); +void func_80B4B010(EnZl1* this, GlobalContext* globalCtx); +void func_80B4B240(EnZl1* this, GlobalContext* globalCtx); +void func_80B4B8B4(EnZl1* this, GlobalContext* globalCtx); +void func_80B4BBC4(EnZl1* this, GlobalContext* globalCtx); +void func_80B4BC78(EnZl1* this, GlobalContext* globalCtx); +void func_80B4BF2C(EnZl1* this, GlobalContext* globalCtx); + +extern CutsceneData D_80B4C5D0[]; + +#include "z_en_zl1_camera_data.c" + +const ActorInit En_Zl1_InitVars = { + ACTOR_EN_ZL1, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZL1, + sizeof(EnZl1), + (ActorFunc)EnZl1_Init, + (ActorFunc)EnZl1_Destroy, + (ActorFunc)EnZl1_Update, + (ActorFunc)EnZl1_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 20, 46, 0, { 0, 0, 0 } }, +}; + +static void* D_80B4E61C[] = { + gChildZelda1EyeOpenLookingUpRightTex, + gChildZelda1EyeHalf2Tex, + gChildZelda1EyeClosedTex, + gChildZelda1EyeHalf2Tex, +}; +static void* D_80B4E62C[] = { gChildZelda1MouthNeutralTex }; + +void func_80B4AB40(void) { +} + +void func_80B4AB48(void) { +} + +void EnZl1_Init(Actor* thisx, GlobalContext* globalCtx) { + f32 frameCount; + EnZl1* this = (EnZl1*)thisx; + + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_12118); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gChildZelda1Skel, NULL, NULL, NULL, 0); + Animation_Change(&this->skelAnime, &gChildZelda1Anim_12118, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Actor_SetScale(&this->actor, 0.01f); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 24.0f); + this->actor.targetMode = 0; + + if (gSaveContext.sceneSetupIndex >= 4) { + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_00438); + Animation_Change(&this->skelAnime, &gChildZelda1Anim_00438, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + this->unk_1E6 = 0; + this->actionFunc = func_80B4BC78; + } else if (Flags_GetEventChkInf(9) && Flags_GetEventChkInf(0x25) && Flags_GetEventChkInf(0x37)) { + Actor_Kill(&this->actor); + } else if ((Flags_GetEventChkInf(9) && Flags_GetEventChkInf(0x25)) || + (Flags_GetEventChkInf(9) && Flags_GetEventChkInf(0x37))) { + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_00438); + Animation_Change(&this->skelAnime, &gChildZelda1Anim_00438, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + this->actor.textId = 0x703D; + this->actionFunc = func_80B4AF18; + } else if (Flags_GetEventChkInf(0x40)) { + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_00438); + Animation_Change(&this->skelAnime, &gChildZelda1Anim_00438, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + this->actor.textId = 0x703C; + this->actionFunc = func_80B4AF18; + } else { + this->actor.textId = 0xFFFF; + this->actionFunc = func_80B4B010; + } +} + +void EnZl1_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnZl1* this = (EnZl1*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80B4AE18(EnZl1* this) { + if ((this->skelAnime.animation == &gChildZelda1Anim_10B38) && (this->skelAnime.curFrame < 26.0f)) { + this->unk_1F4 = gChildZelda1EyeOpenLookingRightTex; + this->unk_1F8 = gChildZelda1EyeOpenLookingLeftTex; + this->unk_1FC = 2; + } else { + if (DECR(this->unk_1FC) == 0) { + this->unk_1FC = Rand_S16Offset(0x1E, 0xA); + } + this->unk_1FE = (this->unk_1FC < 4) ? this->unk_1FC : 0; + + this->unk_1F4 = D_80B4E61C[this->unk_1FE]; + this->unk_1F8 = D_80B4E61C[this->unk_1FE]; + this->unk_1EC = D_80B4E62C[this->unk_1F2]; + } +} + +void func_80B4AF18(EnZl1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + func_80038290(globalCtx, &this->actor, &this->unk_200, &this->unk_206, this->actor.focus.pos); + + if (this->unk_1E6 != 0) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->unk_1E6 = 0; + } + } else if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_1E6 = 1; + } else if (this->actor.world.pos.y <= player->actor.world.pos.y) { + func_8002F2F4(&this->actor, globalCtx); + } + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +void func_80B4B010(EnZl1* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad2; + s32 pad3; + s32 pad; + Vec3f vec1 = { -460.0f, 118.0f, 0.0f }; + Vec3f vec2 = { -406.0f, 110.0f, 0.0f }; + Vec3f playerPos = { -398.0f, 84.0f, 0.0f }; + s16 rotDiff; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + Animation_Change(&this->skelAnime, &gChildZelda1Anim_10B38, 1.0f, 0.0f, + Animation_GetLastFrame(&gChildZelda1Anim_10B38), ANIMMODE_ONCE_INTERP, -10.0f); + this->unk_1E8 = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->unk_1E8, CAM_STAT_ACTIVE); + func_800C0808(globalCtx, this->unk_1E8, player, CAM_SET_FREE0); + globalCtx->envCtx.screenFillColor[0] = 255; + globalCtx->envCtx.screenFillColor[1] = 255; + globalCtx->envCtx.screenFillColor[2] = 255; + globalCtx->envCtx.screenFillColor[3] = 24; + globalCtx->envCtx.fillScreen = true; + Gameplay_CameraSetAtEye(globalCtx, this->unk_1E8, &vec1, &vec2); + Gameplay_CameraSetFov(globalCtx, this->unk_1E8, 30.0f); + ShrinkWindow_SetVal(0x20); + Interface_ChangeAlpha(2); + player->actor.world.pos = playerPos; + player->actor.speedXZ = 0.0f; + this->unk_1E2 = 0; + this->actionFunc = func_80B4B240; + Audio_PlayFanfare(NA_BGM_APPEAR); + } else { + if (1) {} // necessary to match + rotDiff = ABS(this->actor.yawTowardsPlayer - this->actor.shape.rot.y); + if ((rotDiff < 0x238E) && !(player->actor.world.pos.y < this->actor.world.pos.y)) { + func_8002F2F4(&this->actor, globalCtx); + } + } +} + +void func_80B4B240(EnZl1* this, GlobalContext* globalCtx) { + Vec3f sp74 = { -427.0f, 108.0, 26.0 }; + Vec3f sp68 = { -340.0f, 108.0f, 98.0f }; + s32 pad; + Vec3f sp58 = { -434.0f, 84.0f, 0.0f }; + u8 sp54[] = { 0x00, 0x00, 0x02 }; + s32 pad2; + Player* player = GET_PLAYER(globalCtx); + AnimationHeader* animHeaderSeg; + MessageContext* msgCtx = &globalCtx->msgCtx; + f32 frameCount; + s32 sp3C = 0; + + switch (this->unk_1E2) { + case 0: + switch ((s16)this->skelAnime.curFrame) { + case 14: + this->unk_1E4 = 0; + break; + case 15: + if (DECR(this->unk_1E4) != 0) { + this->skelAnime.curFrame = 15.0f; + } + break; + case 64: + animHeaderSeg = &gChildZelda1Anim_11348; + sp3C = 1; + this->actor.textId = 0x702E; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_1E2++; + break; + } + break; + case 1: + if ((Message_GetState(msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->envCtx.fillScreen = false; + Gameplay_CameraSetAtEye(globalCtx, this->unk_1E8, &sp74, &sp68); + Gameplay_CameraSetFov(globalCtx, this->unk_1E8, 25.0f); + player->actor.world.pos = sp58; + this->actor.textId = 0x702F; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2++; + } + break; + case 2: + if ((Message_GetState(msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + if (msgCtx->choiceIndex == 0) { + animHeaderSeg = &gChildZelda1Anim_13F10; + sp3C = 2; + this->unk_1E2++; + } else { + animHeaderSeg = &gChildZelda1Anim_116E4; + sp3C = 2; + this->unk_1E2 = 6; + } + } + break; + case 3: + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_13F10); + if (this->skelAnime.curFrame == frameCount) { + animHeaderSeg = &gChildZelda1Anim_143A8; + sp3C = 1; + this->actor.textId = 0x7032; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2++; + } + break; + case 4: + if ((Message_GetState(msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + if (msgCtx->choiceIndex == 0) { + animHeaderSeg = &gChildZelda1Anim_132D8; + sp3C = 2; + this->unk_1E2 = 9; + } else { + this->actor.textId = 0x7034; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2++; + } + } + break; + case 5: + if ((Message_GetState(msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actor.textId = 0x7033; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2--; + } + break; + case 6: + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_116E4); + if (this->skelAnime.curFrame == frameCount) { + animHeaderSeg = &gChildZelda1Anim_12B88; + sp3C = 1; + this->actor.textId = 0x7031; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2++; + } + break; + case 7: + if ((Message_GetState(msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actor.textId = 0x7030; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2++; + } + break; + case 8: + if ((Message_GetState(msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + if (msgCtx->choiceIndex == 0) { + animHeaderSeg = &gChildZelda1Anim_138E0; + sp3C = 2; + this->unk_1E2 = 3; + } else { + this->actor.textId = 0x7031; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2--; + } + } + break; + case 9: + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_132D8); + if (this->skelAnime.curFrame == frameCount) { + animHeaderSeg = &gChildZelda1Anim_00438; + sp3C = 1; + globalCtx->csCtx.segment = D_80B4C5D0; + gSaveContext.cutsceneTrigger = 1; + this->actionFunc = func_80B4B8B4; + this->unk_1E2++; + } + break; + } + if (sp3C != 0) { + frameCount = Animation_GetLastFrame(animHeaderSeg); + Animation_Change(&this->skelAnime, animHeaderSeg, 1.0f, 0.0f, frameCount, sp54[sp3C], -10.0f); + } + func_80038290(globalCtx, &this->actor, &this->unk_200, &this->unk_206, this->actor.focus.pos); +} + +void func_80B4B7F4(CsCmdActorAction* npcAction, Vec3f* pos) { + pos->x = npcAction->startPos.x; + pos->y = npcAction->startPos.y; + pos->z = npcAction->startPos.z; +} + +void func_80B4B834(CsCmdActorAction* npcAction, Vec3f* pos) { + pos->x = npcAction->endPos.x; + pos->y = npcAction->endPos.y; + pos->z = npcAction->endPos.z; +} + +void func_80B4B874(EnZl1* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 1; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_80B4B8B4(EnZl1* this, GlobalContext* globalCtx) { + AnimationHeader* spB0[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &gChildZelda1Anim_12B04, + &gChildZelda1Anim_12118, + &gChildZelda1Anim_10B38, + }; + u8 spA4[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, + }; + Vec3f sp98 = { -421.0f, 143.0f, -5.0f }; + Vec3f sp8C = { -512.0f, 105.0f, -4.0f }; + s32 pad2; + f32 actionLength; + CsCmdActorAction* npcAction; + Vec3f sp74; + Vec3f sp68; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + s32 pad; + f32 frameCount; + Vec3f sp48; + + SkelAnime_Update(&this->skelAnime); + func_80B4B874(this, globalCtx); + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + this->actionFunc = func_80B4BBC4; + return; + } + + npcAction = globalCtx->csCtx.npcActions[0]; + if (npcAction != NULL) { + func_80B4B7F4(npcAction, &sp74); + func_80B4B834(npcAction, &sp68); + if (this->unk_1E6 == 0) { + sp48 = sp74; + this->actor.home.pos = sp48; + this->actor.world.pos = sp48; + } + if (this->unk_1E6 != npcAction->action) { + frameCount = Animation_GetLastFrame(spB0[npcAction->action]); + Animation_Change(&this->skelAnime, spB0[npcAction->action], 1.0f, 0.0f, frameCount, spA4[npcAction->action], + -10.0f); + this->unk_1E6 = npcAction->action; + } + this->actor.velocity = velocity; + if (globalCtx->csCtx.frames < npcAction->endFrame) { + actionLength = npcAction->endFrame - npcAction->startFrame; + this->actor.velocity.x = (sp68.x - sp74.x) / actionLength; + this->actor.velocity.y = (sp68.y - sp74.y) / actionLength; + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + this->actor.velocity.z = (sp68.z - sp74.z) / actionLength; + } + func_80038290(globalCtx, &this->actor, &this->unk_200, &this->unk_206, this->actor.focus.pos); + Gameplay_CameraSetAtEye(globalCtx, this->unk_1E8, &sp98, &sp8C); + Gameplay_CameraSetFov(globalCtx, this->unk_1E8, 70.0f); + } +} + +void func_80B4BBC4(EnZl1* this, GlobalContext* globalCtx) { + s32 pad; + f32 frameCount = Animation_GetLastFrame(&gChildZelda1Anim_00438); + Player* player = GET_PLAYER(globalCtx); + + Animation_Change(&this->skelAnime, &gChildZelda1Anim_00438, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, 0.0f); + func_8002DF54(globalCtx, &this->actor, 1); + func_8002F7DC(&player->actor, NA_SE_VO_LI_SURPRISE_KID); + this->actor.textId = 0x7039; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + this->unk_1E2 = 0; + this->actionFunc = func_80B4BF2C; +} + +void func_80B4BC78(EnZl1* this, GlobalContext* globalCtx) { + AnimationHeader* sp90[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &gChildZelda1Anim_12B04, + &gChildZelda1Anim_12118, + &gChildZelda1Anim_10B38, + }; + u8 sp84[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, + }; + s32 pad2; + f32 actionLength; + Vec3f sp70; + Vec3f sp64; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + CsCmdActorAction* npcAction; + s32 pad; + f32 frameCount; + + if (SkelAnime_Update(&this->skelAnime) && (this->skelAnime.animation == &gChildZelda1Anim_10B38)) { + frameCount = Animation_GetLastFrame(&gChildZelda1Anim_11348); + Animation_Change(&this->skelAnime, &gChildZelda1Anim_11348, 1.0f, 0.0f, frameCount, ANIMMODE_LOOP, -10.0f); + } + func_80B4B874(this, globalCtx); + npcAction = globalCtx->csCtx.npcActions[0]; + if (npcAction != NULL) { + func_80B4B7F4(npcAction, &sp70); + func_80B4B834(npcAction, &sp64); + if (this->unk_1E6 == 0) { + this->actor.world.pos = this->actor.home.pos = sp70; + } + + if (this->unk_1E6 != npcAction->action) { + frameCount = Animation_GetLastFrame(sp90[npcAction->action]); + Animation_Change(&this->skelAnime, sp90[npcAction->action], 1.0f, 0.0f, frameCount, sp84[npcAction->action], + -10.0f); + this->unk_1E6 = npcAction->action; + } + this->actor.velocity = velocity; + if (globalCtx->csCtx.frames < npcAction->endFrame) { + actionLength = npcAction->endFrame - npcAction->startFrame; + this->actor.velocity.x = (sp64.x - sp70.x) / actionLength; + this->actor.velocity.y = (sp64.y - sp70.y) / actionLength; + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + this->actor.velocity.z = (sp64.z - sp70.z) / actionLength; + } + } +} + +void func_80B4BF2C(EnZl1* this, GlobalContext* globalCtx) { + s32 pad; + MessageContext* msgCtx = &globalCtx->msgCtx; + Player* player = GET_PLAYER(globalCtx); + + switch (this->unk_1E2) { + case 0: + if ((Message_GetState(msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + if (msgCtx->choiceIndex == 0) { + this->actor.textId = 0x703B; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2++; + } else { + this->actor.textId = 0x703A; + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_1E2 = 0; + } + } + break; + case 1: + if ((Message_GetState(msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + this->actor.textId = 0xFFFF; + globalCtx->talkWithPlayer(globalCtx, &this->actor); + func_8002F434(&this->actor, globalCtx, GI_LETTER_ZELDA, 120.0f, 10.0f); + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + globalCtx->msgCtx.stateTimer = 4; + this->unk_1E2++; + } else { + break; + } + case 2: + if (Actor_HasParent(&this->actor, globalCtx)) { + Gameplay_CopyCamera(globalCtx, MAIN_CAM, this->unk_1E8); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_ACTIVE); + Gameplay_ClearCamera(globalCtx, this->unk_1E8); + this->actor.parent = NULL; + this->unk_1E2++; + } else { + func_8002F434(&this->actor, globalCtx, GI_LETTER_ZELDA, 120.0f, 10.0f); + } + break; + case 3: + if ((Message_GetState(msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + this->actor.textId = 0x703C; + Message_ContinueTextbox(globalCtx, this->actor.textId); + Flags_SetEventChkInf(0x40); + this->unk_1E2 = 6; + } + break; + case 4: + if (player->actor.world.pos.y < this->actor.world.pos.y) { + break; + } else { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->unk_1E2++; + } else { + func_8002F2F4(&this->actor, globalCtx); + } + } + break; + case 5: + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->unk_1E2--; + } + break; + case 6: + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + func_8002DF54(globalCtx, &this->actor, 7); + Interface_ChangeAlpha(50); + this->actor.flags &= ~ACTOR_FLAG_8; + this->unk_1E2 = 4; + } + break; + } + func_80038290(globalCtx, &this->actor, &this->unk_200, &this->unk_206, this->actor.focus.pos); +} + +void EnZl1_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZl1* this = (EnZl1*)thisx; + + if ((this->actionFunc != func_80B4B8B4) && (this->actionFunc != func_80B4BC78)) { + SkelAnime_Update(&this->skelAnime); + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 5); + this->actionFunc(this, globalCtx); + if (this->actionFunc != func_80B4B8B4) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + Math_SmoothStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x, 0xA, 0x3E8, 1); + Math_SmoothStepToS(&this->actor.shape.rot.y, this->actor.world.rot.y, 0xA, 0x3E8, 1); + Math_SmoothStepToS(&this->actor.shape.rot.z, this->actor.world.rot.z, 0xA, 0x3E8, 1); + func_80B4AE18(this); +} + +s32 EnZl1_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnZl1* this = (EnZl1*)thisx; + + if ((limbIndex == 4) || (limbIndex == 3) || (limbIndex == 6) || (limbIndex == 5)) { + *dList = NULL; + } + + if (limbIndex != 10) { + if (limbIndex == 17) { + rot->x += this->unk_200.y; + rot->y += this->unk_200.z; + rot->z += this->unk_200.x; + } + } else { + rot->x += this->unk_206.y; + rot->y += this->unk_206.x; + rot->z += this->unk_206.z; + } + return 0; +} + +void EnZl1_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + Vec3f vec = { 0.0f, 0.0f, 0.0f }; + EnZl1* this = (EnZl1*)thisx; + + if (limbIndex == 17) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } +} + +void EnZl1_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnZl1* this = (EnZl1*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_girlB.c", 2011); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(this->unk_1F4)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(this->unk_1F8)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(this->unk_1EC)); + + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnZl1_OverrideLimbDraw, EnZl1_PostLimbDraw, this); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_girlB.c", 2046); +} diff --git a/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1.h b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1.h new file mode 100644 index 000000000..59ca2fd9e --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1.h @@ -0,0 +1,33 @@ +#ifndef Z_EN_ZL1_H +#define Z_EN_ZL1_H + +#include "ultra64.h" +#include "global.h" + +struct EnZl1; + +typedef void (*EnZl1ActionFunc)(struct EnZl1*, GlobalContext*); + +typedef struct EnZl1 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnZl1ActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ char unk_1E0[0x2]; + /* 0x01E2 */ s16 unk_1E2; + /* 0x01E4 */ s16 unk_1E4; + /* 0x01E6 */ s16 unk_1E6; + /* 0x01E8 */ s16 unk_1E8; + /* 0x01EA */ char unk_1EA[0x2]; + /* 0x01EC */ void* unk_1EC; + /* 0x01F0 */ char unk_1F0[0x2]; + /* 0x01F2 */ s16 unk_1F2; + /* 0x01F4 */ void* unk_1F4; + /* 0x01F8 */ void* unk_1F8; + /* 0x01FC */ s16 unk_1FC; + /* 0x01FE */ s16 unk_1FE; + /* 0x0200 */ Vec3s unk_200; + /* 0x0206 */ Vec3s unk_206; +} EnZl1; // size = 0x020C + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1_camera_data.c b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1_camera_data.c new file mode 100644 index 000000000..ffdd58496 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1_camera_data.c @@ -0,0 +1,207 @@ +#include "z_en_zl1.h" +#include "z64cutscene_commands.h" + +static CutsceneCameraAngle D_80B4D5C0[] = { + { { -440.0f, 117.0f, 0.0f }, { -490.0f, 120.0f, 0.0f }, 0, 45 }, + { { -484.0f, 122.0f, -29.0f }, { -480.0f, 116.0f, 18.0f }, 0, 80 }, + { { -413.0f, 136.0f, -72.0f }, { -403.0f, 141.0f, -89.0f }, 0, 25 }, + { { -454.0f, 120.0f, 0.0f }, { -434.0f, 121.0f, 0.0f }, 0, 20 }, + { { -454.0f, 120.0f, 0.0f }, { -430.0f, 103.0f, -37.0f }, 0, 20 }, + { { -454.0f, 105.0f, 50.0f }, { -453.0f, 105.0f, 66.0f }, 0, 60 }, + { { -501.0f, 122.0f, 0.0f }, { -449.0f, 119.0f, 0.0f }, 0, 45 }, + { { -462.0f, 121.0f, 0.0f }, { -419.0f, 125.0f, 0.0f }, 0, 20 }, + { { -551.0f, 119.0f, 7.0f }, { -587.0f, 115.0f, 14.0f }, 0, 20 }, + { { -489.0f, 129.0f, 0.0f }, { -470.0f, 128.0f, 0.0f }, 0, 40 }, + { { -525.0f, 126.0f, 0.0f }, { -509.0f, 126.0f, 0.0f }, 0, 10 }, + { { -491.0f, 120.0f, -7.0f }, { -509.0f, 115.0f, -7.0f }, 0, 75 }, + { { -485.0f, 120.0f, -42.0f }, { -484.0f, 120.0f, 10.0f }, 0, 45 }, +}; + +static CutsceneCameraPoint D_80B4D72C[] = { + { 0, 0, 20, 80.79987f, { -484, 122, -29 } }, { 0, 0, 20, 80.79987f, { -484, 122, -29 } }, + { 0, 0, 20, 80.79987f, { -484, 122, -29 } }, { 0, 0, 20, 80.79987f, { -452, 121, -21 } }, + { 0, 0, 20, 80.79987f, { -452, 121, -21 } }, { 0, 0, 20, 80.79987f, { -452, 121, -21 } }, + { -1, 0, 20, 80.79987f, { -452, 121, -21 } }, { -1, 0, 30, 80.79987f, { -452, 121, -21 } }, +}; + +static CutsceneCameraPoint D_80B4D7AC[] = { + { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, + { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, + { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, + { -1, 0, 0, 80.79987f, { -480, 116, 18 } }, { -1, 0, 0, 80.79987f, { -480, 116, 18 } }, +}; + +static CutsceneCameraPoint D_80B4D82C[] = { + { 0, 0, 20, 45.200058f, { -439, 116, 0 } }, { 0, 0, 20, 45.200058f, { -439, 116, 0 } }, + { 0, 0, 20, 50.60008f, { -433, 116, 0 } }, { 0, 0, 20, 55.600098f, { -431, 116, 0 } }, + { 0, 0, 20, 60.000114f, { -427, 116, 0 } }, { 0, 0, 20, 65.000114f, { -424, 116, 0 } }, + { 0, 0, 20, 70.800026f, { -422, 116, 0 } }, { 0, 0, 20, 75.59995f, { -419, 115, 0 } }, + { -1, 0, 20, 75.59995f, { -419, 116, 0 } }, { -1, 0, 30, 75.59995f, { -419, 115, 0 } }, +}; + +static CutsceneCameraPoint D_80B4D8CC[] = { + { 0, 0, 0, 60.400116f, { -480, 114, 0 } }, { 0, 0, 0, 45.200058f, { -480, 114, 0 } }, + { 0, 0, 0, 45.200058f, { -474, 114, 0 } }, { 0, 0, 0, 50.60008f, { -472, 114, 0 } }, + { 0, 0, 0, 55.600098f, { -468, 114, 0 } }, { 0, 0, 0, 60.000114f, { -465, 114, 0 } }, + { 0, 0, 0, 65.000114f, { -463, 114, 0 } }, { 0, 0, 0, 70.800026f, { -460, 114, 0 } }, + { -1, 0, 0, 75.59995f, { -460, 114, 0 } }, { -1, 0, 0, 75.59995f, { -460, 114, 0 } }, +}; + +static CutsceneCameraPoint D_80B4D96C[] = { + { 0, 0, 25, 60.000114f, { -116, 50, 469 } }, { 0, 0, 25, 60.000114f, { -116, 50, 469 } }, + { 0, 0, 25, 60.000114f, { -118, 50, 467 } }, { 0, 0, 25, 60.000114f, { -120, 50, 465 } }, + { 0, 0, 25, 60.000114f, { -123, 50, 464 } }, { 0, 0, 25, 60.000114f, { -126, 50, 463 } }, + { 0, 0, 25, 60.000114f, { -129, 50, 462 } }, { 0, 0, 25, 60.000114f, { -131, 50, 462 } }, + { 0, 0, 25, 60.000114f, { -134, 50, 463 } }, { 0, 0, 25, 60.000114f, { -137, 50, 464 } }, + { 0, 0, 25, 60.000114f, { -140, 50, 466 } }, { 0, 0, 25, 60.000114f, { -140, 50, 466 } }, + { -1, 0, 25, 60.000114f, { -140, 50, 466 } }, { -1, 0, 25, 60.000114f, { -140, 50, 466 } }, +}; + +static CutsceneCameraPoint D_80B4DA4C[] = { + { 0, 0, 0, 20.399963f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { -1, 0, 0, 60.000114f, { -130, 50, 480 } }, { -1, 0, 0, 60.000114f, { -130, 50, 480 } }, +}; + +static CutsceneCameraPoint D_80B4DB2C[] = { + { 0, 0, 30, 20.799965f, { -427, 116, 4 } }, { 0, 0, 30, 20.799965f, { -426, 115, 4 } }, + { 0, 0, 30, 20.799965f, { -429, 115, 7 } }, { 0, 0, 30, 20.799965f, { -428, 114, 12 } }, + { 0, 0, 30, 20.799965f, { -430, 114, 14 } }, { 0, 0, 30, 20.799965f, { -430, 114, 14 } }, + { 0, 0, 30, 20.799965f, { -430, 114, 14 } }, { -1, 0, 30, 20.799965f, { -430, 112, 14 } }, + { -1, 0, 30, 20.799965f, { -430, 112, 14 } }, +}; + +static CutsceneCameraPoint D_80B4DBBC[] = { + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { -1, 0, 0, 20.799965f, { -389, 109, 36 } }, + { -1, 0, 0, 20.799965f, { -389, 109, 36 } }, +}; + +static CutsceneCameraPoint D_80B4DC4C[] = { + { 0, 0, 30, 45.0f, { -496, 119, 0 } }, { 0, 0, 30, 45.0f, { -496, 119, 0 } }, + { 0, 0, 30, 45.0f, { -496, 119, 0 } }, { 0, 0, 15, 45.0f, { -496, 119, 0 } }, + { 0, 0, 15, 45.80006f, { -471, 122, 0 } }, { 0, 0, 15, 45.80006f, { -395, 139, 0 } }, + { 0, 0, 15, 45.80006f, { -193, 183, 0 } }, { 0, 0, 15, 45.80006f, { 29, 232, 0 } }, + { 0, 0, 30, 45.80006f, { 360, 304, 0 } }, { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, + { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, + { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, { -1, 0, 30, 45.80006f, { 429, 328, 0 } }, + { -1, 0, 30, 45.80006f, { 429, 328, 0 } }, +}; + +static CutsceneCameraPoint D_80B4DD3C[] = { + { 0, 0, 0, 45.0f, { -443, 115, 0 } }, { 0, 0, 0, 45.0f, { -443, 115, 0 } }, + { 0, 0, 0, 45.0f, { -443, 115, 0 } }, { 0, 0, 0, 45.0f, { -443, 115, 0 } }, + { 0, 0, 0, 45.80006f, { -420, 133, 0 } }, { 0, 0, 0, 45.80006f, { -344, 150, 0 } }, + { 0, 0, 0, 45.80006f, { -143, 194, 0 } }, { 0, 0, 0, 45.80006f, { 80, 243, 0 } }, + { 0, 0, 0, 45.80006f, { 412, 315, 0 } }, { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, + { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, + { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, { -1, 0, 0, 45.80006f, { 482, 332, 0 } }, + { -1, 0, 0, 45.80006f, { 482, 332, 0 } }, +}; + +static CutsceneCameraPoint D_80B4DE2C[] = { + { 0, 0, 25, 60.600117f, { 66, 404, 425 } }, { 0, 0, 25, 60.400116f, { 66, 404, 425 } }, + { 0, 0, 25, 55.600098f, { 66, 404, 426 } }, { 0, 0, 25, 55.200096f, { 63, 373, 413 } }, + { 0, 0, 25, 50.400078f, { 26, 353, 408 } }, { 0, 0, 25, 50.400078f, { 17, 325, 397 } }, + { 0, 0, 25, 45.40006f, { 17, 326, 397 } }, { 0, 0, 25, 45.200058f, { -136, 177, 259 } }, + { 0, 0, 25, 40.40004f, { -258, 111, 169 } }, { 0, 0, 25, 40.20004f, { -377, 108, 65 } }, + { 0, 0, 25, 35.20002f, { -377, 108, 65 } }, { 0, 0, 25, 35.20002f, { -377, 108, 65 } }, + { -1, 0, 30, 30.2f, { -376, 108, 65 } }, { -1, 0, 30, 30.2f, { -376, 108, 65 } }, +}; + +static CutsceneCameraPoint D_80B4DF0C[] = { + { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, + { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, + { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, { 0, 0, 0, 45.80006f, { 49, 347, 424 } }, + { 0, 0, 0, 45.80006f, { 49, 347, 424 } }, { 0, 0, 0, 30.2f, { -103, 192, 286 } }, + { 0, 0, 0, 30.2f, { -224, 113, 198 } }, { 0, 0, 0, 30.2f, { -345, 109, 96 } }, + { 0, 0, 0, 30.2f, { -345, 109, 96 } }, { 0, 0, 0, 30.2f, { -345, 109, 96 } }, + { -1, 0, 0, 30.2f, { -345, 109, 96 } }, { -1, 0, 0, 30.2f, { -345, 109, 96 } }, +}; + +static CutsceneCameraPoint D_80B4DFEC[] = { + { 0, 0, 10, 30.0f, { -482, 119, 0 } }, { 0, 0, 10, 30.0f, { -482, 119, 0 } }, + { 0, 0, 10, 30.0f, { -482, 119, 0 } }, { 0, 0, 10, 30.800003f, { -482, 119, 0 } }, + { 0, 0, 10, 30.800003f, { -482, 119, 0 } }, { 0, 0, 10, 30.800003f, { -462, 126, 0 } }, + { 0, 0, 30, 30.800003f, { -395, 150, 0 } }, { 0, 0, 30, 30.800003f, { -395, 150, 0 } }, + { -1, 0, 30, 30.800003f, { -395, 150, 0 } }, { -1, 0, 30, 30.800003f, { -395, 150, 0 } }, +}; + +static CutsceneCameraPoint D_80B4E08C[] = { + { 0, 0, 0, 30.0f, { -440, 115, 0 } }, { 0, 0, 0, 30.0f, { -440, 115, 0 } }, + { 0, 0, 0, 30.0f, { -440, 115, 0 } }, { 0, 0, 0, 30.800003f, { -440, 115, 0 } }, + { 0, 0, 0, 30.800003f, { -440, 115, 0 } }, { 0, 0, 0, 30.800003f, { -423, 142, 0 } }, + { 0, 0, 0, 30.800003f, { -358, 171, 0 } }, { 0, 0, 0, 30.800003f, { -358, 171, 0 } }, + { -1, 0, 0, 30.800003f, { -358, 171, 0 } }, { -1, 0, 0, 30.800003f, { -358, 171, 0 } }, +}; + +static CutsceneCameraPoint D_80B4E12C[] = { + { 0, 0, 30, 25.0f, { -551, 119, 7 } }, { 0, 0, 30, 25.0f, { -551, 119, 7 } }, + { 0, 0, 15, 25.0f, { -551, 119, 7 } }, { 0, 0, 15, 60.0f, { -485, 120, -7 } }, + { 0, 0, 30, 60.0f, { -485, 120, -7 } }, { 0, 0, 30, 60.0f, { -485, 120, -7 } }, + { 0, 0, 30, 60.0f, { -485, 118, -5 } }, { -1, 0, 30, 60.0f, { -485, 119, -6 } }, + { -1, 0, 30, 60.0f, { -485, 119, -6 } }, +}; + +static CutsceneCameraPoint D_80B4E1BC[] = { + { 0, 0, 0, 45.200058f, { -587, 115, 14 } }, { 0, 0, 0, 25.400097f, { -587, 115, 14 } }, + { 0, 0, 0, 25.400097f, { -587, 115, 14 } }, { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, + { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, + { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, { -1, 0, 0, 60.20023f, { -521, 116, 0 } }, + { -1, 0, 0, 60.19925f, { -521, 116, 0 } }, +}; + +static CutsceneCameraPoint D_80B4E24C[] = { + { 0, 0, 30, 60.000114f, { 75, 52, 50 } }, { 0, 0, 30, 60.000114f, { 75, 52, 48 } }, + { 0, 0, 60, 60.000114f, { 74, 52, 45 } }, { 0, 0, 60, 60.000114f, { 49, 51, -43 } }, + { 0, 0, 30, 60.000114f, { 49, 51, -43 } }, { 0, 0, 30, 60.000114f, { 49, 51, -43 } }, + { -1, 0, 30, 60.000114f, { 49, 51, -43 } }, { -1, 0, 30, 60.000114f, { 49, 51, -43 } }, +}; + +static CutsceneCameraPoint D_80B4E2CC[] = { + { 0, 0, 0, 60.000114f, { 90, 52, 53 } }, { 0, 0, 0, 60.000114f, { 90, 52, 50 } }, + { 0, 0, 0, 60.000114f, { 90, 52, 45 } }, { 0, 0, 0, 60.000114f, { 65, 51, -44 } }, + { 0, 0, 0, 60.000114f, { 65, 51, -44 } }, { 0, 0, 0, 60.000114f, { 65, 51, -44 } }, + { -1, 0, 0, 60.000114f, { 65, 51, -44 } }, { -1, 0, 0, 60.000114f, { 65, 51, -44 } }, +}; + +static CutsceneCameraPoint D_80B4E34C[] = { + { 0, 0, 31, 60.000114f, { -449, 121, -19 } }, { 0, 0, 30, 60.000114f, { -449, 121, -19 } }, + { 0, 0, 30, 60.000114f, { -449, 121, -19 } }, { 0, 0, 30, 60.000114f, { -456, 110, -17 } }, + { 0, 0, 30, 60.000114f, { -456, 110, -17 } }, { 0, 0, 30, 60.000114f, { -456, 110, -17 } }, + { -1, 0, 30, 60.000114f, { -456, 110, -17 } }, { -1, 0, 30, 60.000114f, { -456, 110, -17 } }, +}; + +static CutsceneCameraPoint D_80B4E3CC[] = { + { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, + { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, + { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, + { -1, 0, 0, 60.000114f, { -441, 107, -22 } }, { -1, 0, 0, 60.000114f, { -441, 107, -22 } }, +}; + +static CutsceneCameraPoint D_80B4E44C[] = { + { 0, 0, 30, 20.799965f, { -331, 110, -91 } }, { 0, 0, 30, 20.799965f, { -331, 110, -91 } }, + { 0, 0, 30, 20.799965f, { -331, 110, -91 } }, { 0, 0, 30, 20.799965f, { -511, 121, 7 } }, + { 0, 0, 30, 20.799965f, { -511, 121, 7 } }, { 0, 0, 30, 20.799965f, { -511, 121, 7 } }, + { -1, 0, 30, 20.799965f, { -511, 121, 7 } }, { -1, 0, 30, 20.799965f, { -511, 121, 7 } }, +}; + +static CutsceneCameraPoint D_80B4E4CC[] = { + { 0, 0, 0, 20.799965f, { -369, 110, -70 } }, { 0, 0, 0, 20.799965f, { -369, 110, -70 } }, + { 0, 0, 0, 20.799965f, { -369, 110, -70 } }, { 0, 0, 0, 20.799965f, { -549, 124, 29 } }, + { 0, 0, 0, 20.799965f, { -549, 124, 29 } }, { 0, 0, 0, 20.799965f, { -549, 124, 29 } }, + { -1, 0, 0, 20.799965f, { -549, 124, 29 } }, { -1, 0, 0, 20.799965f, { -549, 124, 29 } }, +}; + +static CutsceneCameraMove D_80B4E54C[] = { + { D_80B4D72C, D_80B4D7AC, 0 }, { D_80B4D82C, D_80B4D8CC, 0 }, { D_80B4D96C, D_80B4DA4C, 0 }, + { D_80B4DB2C, D_80B4DBBC, 0 }, { D_80B4DC4C, D_80B4DD3C, 0 }, { D_80B4DE2C, D_80B4DF0C, 0 }, + { D_80B4DFEC, D_80B4E08C, 0 }, { D_80B4E12C, D_80B4E1BC, 0 }, { D_80B4E24C, D_80B4E2CC, 0 }, + { D_80B4E34C, D_80B4E3CC, 0 }, { D_80B4E44C, D_80B4E4CC, 0 }, +}; diff --git a/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1_cutscene_data.c new file mode 100644 index 000000000..ccfe0f4c2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl1/z_en_zl1_cutscene_data.c @@ -0,0 +1,248 @@ +#include "z_en_zl1.h" +#include "z64cutscene_commands.h" + +// clang-format off +CutsceneData D_80B4C5D0[] = { + CS_BEGIN_CUTSCENE(28, 3000), + CS_PLAYER_ACTION_LIST(3), + CS_PLAYER_ACTION(0x0005, 400, 1211, 0x0000, 0xC000, 0x0000, -422, 84, 1, -422, 84, 1, 1.1266861702801002e-29f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0001, 1211, 1241, 0x0000, 0xC000, 0x0000, -422, 84, 1, -483, 84, 0, 1.1266861702801002e-29f, 0.0f, 1.401298464324817e-45f), + CS_PLAYER_ACTION(0x0029, 1241, 1311, 0x0000, 0xC000, 0x0000, -483, 84, 0, -483, 84, 0, 1.1266861702801002e-29f, 0.0f, 1.401298464324817e-45f), + CS_NPC_ACTION_LIST(18, 1), + CS_NPC_ACTION(0x0007, 1170, 1316, 0x7477, 0x0000, 0x0000, -485, 84, 0, -469, 85, -55, 0.10958904f, 0.006849315f, -0.10958904f), + CS_UNK_DATA_LIST(0x00000049, 1), + CS_UNK_DATA(0x00010000, 0x0BB80000, 0x00000000, 0x00000000, 0xFFFFFFE8, 0x00000003, 0x00000000, 0xFFFFFFE8, 0x00000003, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(16, 3), + CS_NPC_ACTION(0x0009, 1220, 1310, 0x8000, 0x0000, 0x0000, -890, 90, 150, -890, 90, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x000A, 1310, 1449, 0x0000, 0x0000, 0x0000, -890, 90, 0, -890, 90, 0, 0.0f, 0.0f, 0.0f), + CS_NPC_ACTION(0x0004, 1449, 1457, 0x0000, 0x0000, 0x0000, -890, 90, 0, -890, 90, 0, 0.0f, 0.0f, 0.0f), + CS_MISC_LIST(1), + CS_MISC(0x000C, 1460, 1461, 0x0000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000002, 0xFFFFFFFF, 0x00000000, 0x00000002, 0x00000000, 0x00000000, 0x00000000), + CS_NPC_ACTION_LIST(29, 1), + CS_NPC_ACTION(0x0002, 330, 763, 0x0000, 0x0000, 0x0000, -1250, 150, 0, -1250, 150, 0, 0.0f, 0.0f, 0.0f), + CS_SCENE_TRANS_FX(0x0001, 200, 231), + CS_SCENE_TRANS_FX(0x0005, 230, 271), + CS_SCENE_TRANS_FX(0x0001, 860, 870), + CS_SCENE_TRANS_FX(0x0005, 875, 900), + CS_CAM_EYE_LIST(0, 331), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -447, 128, 1, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -447, 128, 1, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -447, 128, 1, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -447, 128, 1, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -392, 145, 1, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -318, 168, 1, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -222, 198, 1, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -146, 221, 1, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -132, 222, 1, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -66, 267, 1, 0x3632), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -66, 267, 1, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -66, 267, 1, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -66, 267, 1, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.800003f, -66, 267, 1, 0x3533), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.800003f, -66, 267, 1, 0x3336), + CS_CAM_EYE_LIST(230, 1631), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 445, 24, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 259, 24, 0x3632), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1220, 189, 24, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1135, 198, 81, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -1132, 119, 84, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1199, 137, 36, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3336), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x332C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3231), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3232), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x2034), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3331), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3434), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x312C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3136), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3336), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x332C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x200A), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3136), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.600002f, -1218, 127, 22, 0x3336), + CS_CAM_EYE_LIST(810, 1041), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1218, 127, 22, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1218, 127, 22, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1218, 127, 22, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1218, 88, 21, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1208, -52, 23, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1201, -114, 26, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1201, -114, 26, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1201, -114, 26, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1201, -114, 26, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.600006f, -1201, -114, 26, 0x3632), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.600006f, -1201, -114, 26, 0x3639), + CS_CAM_EYE_LIST(870, 1261), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 80.274445f, -59, 160, 320, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -59, 160, 320, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -59, 160, 320, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -59, 160, 320, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -115, 148, 249, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -190, 126, 192, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -286, 105, 135, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -357, 108, 87, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -394, 104, 53, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -394, 104, 53, 0x3632), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -394, 104, 53, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -394, 104, 53, 0x392C), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.074677f, -394, 104, 53, 0x2031), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.074677f, -394, 104, 53, 0x3533), + CS_CAM_EYE_LIST(1160, 1401), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x3639), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 60.0f, -459, 175, 80, 0x392C), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 60.0f, -459, 175, 80, 0x2032), + CS_CAM_EYE_LIST(1260, 1411), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, -461, 133, 0, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, -461, 133, 0, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, -461, 133, 0, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, -461, 133, 0, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 45.199944f, -461, 133, 0, 0x2032), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 45.199944f, -461, 133, 0, 0x3533), + CS_CAM_EYE_LIST(1320, 1531), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x2031), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x3833), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x2C20), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x2032), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x3533), + CS_CAM_EYE(CS_CMD_CONTINUE, 0x00, 0, 10.999838f, -488, 124, -6, 0x3639), + CS_CAM_EYE(CS_CMD_STOP, 0x00, 0, 10.999838f, -488, 124, -6, 0x392C), + CS_CAM_AT_LIST(0, 360), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -506, 110, 1, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -506, 110, 1, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -506, 110, 1, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.800003f, -506, 110, 1, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 25, 60.800003f, -451, 127, 1, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 25, 60.800003f, -380, 149, 1, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -291, 177, 1, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -224, 210, 1, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -213, 230, 1, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -143, 291, 1, 0x3632), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -135, 308, 1, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -127, 319, 1, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -120, 326, 1, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.800003f, -120, 326, 1, 0x3533), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.800003f, -120, 326, 1, 0x3336), + CS_CAM_AT_LIST(230, 1710), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1246, 413, 5, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1245, 227, 5, 0x3632), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1245, 158, 5, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -1180, 178, 50, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.0f, -1178, 131, 51, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1240, 146, 6, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1253, 153, -3, 0x3336), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1253, 153, -3, 0x332C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1253, 153, -3, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1253, 153, -3, 0x3231), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1253, 153, -3, 0x3232), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1252, 152, -2, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1252, 152, -2, 0x2034), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1252, 152, -2, 0x3331), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1252, 152, -2, 0x3434), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1252, 152, -2, 0x312C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1251, 151, -1, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1251, 151, -1, 0x3136), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1251, 151, -1, 0x3336), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1251, 151, -1, 0x332C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 80, 60.600002f, -1251, 151, -1, 0x200A), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600002f, -1251, 151, -1, 0x3136), + CS_CAM_AT(CS_CMD_STOP, 0x00, 80, 60.600002f, -1251, 151, -1, 0x3336), + CS_CAM_AT_LIST(810, 1070), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600006f, -1251, 151, -1, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600006f, -1251, 151, -1, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 20, 60.600006f, -1250, 150, 0, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.600006f, -1241, 125, 5, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.600006f, -1226, -13, 10, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 10, 60.600006f, -1218, -73, 26, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600006f, -1218, -73, 26, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600006f, -1218, -73, 26, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600006f, -1218, -73, 26, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.600006f, -1218, -73, 26, 0x3632), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.600006f, -1218, -73, 26, 0x3639), + CS_CAM_AT_LIST(870, 1290), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -45, 240, 241, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -45, 240, 241, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -56, 219, 224, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -85, 183, 212, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -204, 134, 183, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -280, 116, 125, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -376, 104, 69, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -440, 107, 13, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -467, 110, -25, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -467, 110, -25, 0x3632), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -467, 110, -25, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -467, 110, -25, 0x392C), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.074677f, -467, 110, -25, 0x2031), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.074677f, -467, 110, -25, 0x3533), + CS_CAM_AT_LIST(1160, 1430), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x3639), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 60.0f, -456, 138, 16, 0x392C), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 60.0f, -456, 138, 16, 0x2032), + CS_CAM_AT_LIST(1260, 1440), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.199944f, -535, 133, 0, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.199944f, -535, 133, 0, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.199944f, -535, 133, 0, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.199944f, -535, 133, 0, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 45.199944f, -535, 133, 0, 0x2032), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 45.199944f, -535, 133, 0, 0x3533), + CS_CAM_AT_LIST(1320, 1560), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x2031), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x3833), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x2C20), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x2032), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x3533), + CS_CAM_AT(CS_CMD_CONTINUE, 0x00, 30, 10.999838f, -1349, 124, -6, 0x3639), + CS_CAM_AT(CS_CMD_STOP, 0x00, 30, 10.999838f, -1349, 124, -6, 0x392C), + CS_TEXT_LIST(10), + CS_TEXT_NONE(0, 50), + CS_TEXT_DISPLAY_TEXTBOX(0x7035, 50, 190, 0x0000, 0xFFFF, 0xFFFF), + CS_TEXT_NONE(190, 300), + CS_TEXT_DISPLAY_TEXTBOX(0x7036, 300, 800, 0x0000, 0xFFFF, 0xFFFF), + CS_TEXT_NONE(800, 970), + CS_TEXT_DISPLAY_TEXTBOX(0x7037, 970, 1150, 0x0000, 0xFFFF, 0x7038), + CS_TEXT_NONE(1150, 1190), + CS_TEXT_DISPLAY_TEXTBOX(0x7005, 1190, 1200, 0x0000, 0xFFFF, 0x700B), + CS_TEXT_NONE(1200, 1340), + CS_TEXT_DISPLAY_TEXTBOX(0x7009, 1340, 1420, 0x0000, 0xFFFF, 0xFFFF), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x004E, 230, 231, 0x0000, 0x00000000, 0x00000003, 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000000), + CS_PLAY_BGM_LIST(1), + CS_PLAY_BGM(0x002A, 870, 871, 0x0000, 0x00000000, 0xFFFFFFFE, 0x00000000, 0xFFFFFFFD, 0xFFFFFFFE, 0x00000000, 0xFFFFFFFD), + CS_STOP_BGM_LIST(1), + CS_STOP_BGM(0x0001, 110, 111, 0x0000, 0x00000000, 0xFFFFFFFE, 0x00000000, 0x00000001, 0xFFFFFFFE, 0x00000000, 0x00000001), + CS_END(), +}; +// clang-format on diff --git a/soh/src/overlays/actors/ovl_En_Zl2/z_en_zl2.c b/soh/src/overlays/actors/ovl_En_Zl2/z_en_zl2.c new file mode 100644 index 000000000..d51d72e86 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl2/z_en_zl2.c @@ -0,0 +1,1693 @@ +/* + * File: z_en_zl2.c + * Overlay: ovl_En_Zl2 + * Description: Adult Zelda (Cutscenes) + */ + +#include "z_en_zl2.h" +#include "vt.h" + +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "objects/object_zl2/object_zl2.h" +#include "objects/object_zl2_anime1/object_zl2_anime1.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnZl2_Init(Actor* thisx, GlobalContext* globalCtx); +void EnZl2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnZl2_Update(Actor* thisx, GlobalContext* globalCtx); +void EnZl2_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 func_80B4F45C(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, Gfx** gfx); + +void func_80B50BBC(EnZl2* this, GlobalContext* globalCtx); +void func_80B50BEC(EnZl2* this, GlobalContext* globalCtx); +void func_80B50C40(EnZl2* this, GlobalContext* globalCtx); +void func_80B50CA8(EnZl2* this, GlobalContext* globalCtx); +void func_80B50CFC(EnZl2* this, GlobalContext* globalCtx); +void func_80B50D50(EnZl2* this, GlobalContext* globalCtx); +void func_80B50D94(EnZl2* this, GlobalContext* globalCtx); +void func_80B50DE8(EnZl2* this, GlobalContext* globalCtx); +void func_80B50E3C(EnZl2* this, GlobalContext* globalCtx); +void func_80B50E90(EnZl2* this, GlobalContext* globalCtx); +void func_80B50EE4(EnZl2* this, GlobalContext* globalCtx); +void func_80B50F38(EnZl2* this, GlobalContext* globalCtx); +void func_80B50F8C(EnZl2* this, GlobalContext* globalCtx); +void func_80B50FE8(EnZl2* this, GlobalContext* globalCtx); +void func_80B51034(EnZl2* this, GlobalContext* globalCtx); +void func_80B51080(EnZl2* this, GlobalContext* globalCtx); +void func_80B510CC(EnZl2* this, GlobalContext* globalCtx); +void func_80B51118(EnZl2* this, GlobalContext* globalCtx); +void func_80B51164(EnZl2* this, GlobalContext* globalCtx); +void func_80B511B0(EnZl2* this, GlobalContext* globalCtx); +void func_80B511FC(EnZl2* this, GlobalContext* globalCtx); +void func_80B51250(EnZl2* this, GlobalContext* globalCtx); +void func_80B512B8(EnZl2* this, GlobalContext* globalCtx); +void func_80B51310(EnZl2* this, GlobalContext* globalCtx); +void func_80B51A5C(EnZl2* this, GlobalContext* globalCtx); +void func_80B51A8C(EnZl2* this, GlobalContext* globalCtx); +void func_80B51AE4(EnZl2* this, GlobalContext* globalCtx); +void func_80B51B44(EnZl2* this, GlobalContext* globalCtx); +void func_80B51BA8(EnZl2* this, GlobalContext* globalCtx); +void func_80B51C0C(EnZl2* this, GlobalContext* globalCtx); +void func_80B51C64(EnZl2* this, GlobalContext* globalCtx); +void func_80B51CA8(EnZl2* this, GlobalContext* globalCtx); +void func_80B52068(EnZl2* this, GlobalContext* globalCtx); +void func_80B52098(EnZl2* this, GlobalContext* globalCtx); +void func_80B52108(EnZl2* this, GlobalContext* globalCtx); +void func_80B521A0(EnZl2* this, GlobalContext* globalCtx); +void func_80B523BC(EnZl2* this, GlobalContext* globalCtx); +void func_80B523C8(EnZl2* this, GlobalContext* globalCtx); +void func_80B525D4(EnZl2* this, GlobalContext* globalCtx); + +static void* sEyeTextures[] = { gZelda2EyeOpenTex, gZelda2EyeHalfTex, gZelda2EyeShutTex, + gZelda2Eye03Tex, gZelda2Eye04Tex, gZelda2Eye05Tex, + gZelda2Eye06Tex, gZelda2Eye07Tex, gZelda2Eye08Tex }; + +static void* sMouthTextures[] = { gZelda2MouthSeriousTex, gZelda2MouthHappyTex, gZelda2MouthOpenTex }; + +static EnZl2ActionFunc sActionFuncs[] = { + func_80B521A0, func_80B50BBC, func_80B50BEC, func_80B50C40, func_80B50CA8, func_80B50CFC, + func_80B50D50, func_80B50D94, func_80B50DE8, func_80B50E3C, func_80B50E90, func_80B50EE4, + func_80B50F38, func_80B50F8C, func_80B50FE8, func_80B51034, func_80B51080, func_80B510CC, + func_80B51118, func_80B51164, func_80B511B0, func_80B511FC, func_80B51250, func_80B512B8, + func_80B51310, func_80B51A5C, func_80B51A8C, func_80B51AE4, func_80B51B44, func_80B51BA8, + func_80B51C0C, func_80B51C64, func_80B51CA8, func_80B52068, func_80B52098, func_80B52108, +}; + +static OverrideLimbDraw sOverrideLimbDrawFuncs[] = { + func_80B4F45C, +}; + +static EnZl2DrawFunc sDrawFuncs[] = { + func_80B523BC, + func_80B523C8, + func_80B525D4, +}; + +const ActorInit En_Zl2_InitVars = { + ACTOR_EN_ZL2, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZL2, + sizeof(EnZl2), + (ActorFunc)EnZl2_Init, + (ActorFunc)EnZl2_Destroy, + (ActorFunc)EnZl2_Update, + (ActorFunc)EnZl2_Draw, + NULL, +}; + +void EnZl2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnZl2* this = (EnZl2*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); +} + +void EnZl2_UpdateEyes(EnZl2* this) { + s32 pad[4]; + s16* eyeTexIndex2 = &this->eyeTexIndex2; + s16* blinkTimer = &this->blinkTimer; + s16* eyeTexIndex = &this->eyeTexIndex; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + *eyeTexIndex = *blinkTimer; + if (*eyeTexIndex >= 3) { + *eyeTexIndex = 0; + } + *eyeTexIndex2 = *eyeTexIndex; +} + +void func_80B4EA40(EnZl2* this) { + s16* eyeTexIndex = &this->eyeTexIndex; + f32* unk_27C = &this->unk_27C; + + if (*unk_27C < 3.0f) { + *eyeTexIndex = 0; + } else if (*unk_27C < 6.0f) { + *eyeTexIndex = 1; + } else if (*unk_27C < 9.0f) { + *eyeTexIndex = 2; + } else { + *eyeTexIndex = 5; + this->eyeTexIndex2 = *eyeTexIndex; + return; + } + *unk_27C += 1.0f; + this->eyeTexIndex2 = *eyeTexIndex; +} + +void func_80B4EAF4(EnZl2* this) { + s16* eyeTexIndex = &this->eyeTexIndex; + f32* unk_27C = &this->unk_27C; + + if (*unk_27C < 2.0f) { + *eyeTexIndex = 5; + } else if (*unk_27C < 4.0f) { + *eyeTexIndex = 2; + } else if (*unk_27C < 6.0f) { + *eyeTexIndex = 1; + } else { + EnZl2_UpdateEyes(this); + return; + } + *unk_27C += 1.0f; + this->eyeTexIndex2 = *eyeTexIndex; +} + +void func_80B4EBB8(EnZl2* this) { + s16* eyeTexIndex = &this->eyeTexIndex; + f32* unk_27C = &this->unk_27C; + + if (*unk_27C < 2.0f) { + *eyeTexIndex = 0; + } else if (*unk_27C < 4.0f) { + *eyeTexIndex = 1; + } else { + *eyeTexIndex = 2; + this->eyeTexIndex2 = *eyeTexIndex; + return; + } + *unk_27C += 1.0f; + this->eyeTexIndex2 = *eyeTexIndex; +} + +void func_80B4EC48(EnZl2* this) { + s16* eyeTexIndex = &this->eyeTexIndex; + f32* unk_27C = &this->unk_27C; + + if (*unk_27C < 2.0f) { + *eyeTexIndex = 2; + } else if (*unk_27C < 4.0f) { + *eyeTexIndex = 1; + } else { + EnZl2_UpdateEyes(this); + return; + } + *unk_27C += 1.0f; + this->eyeTexIndex2 = *eyeTexIndex; +} + +void EnZl2_setEyesIndex(EnZl2* this, s16 index) { + this->eyeTexIndex = index; + this->eyeTexIndex2 = this->eyeTexIndex; +} + +void EnZl2_setEyeIndex2(EnZl2* this, s16 index) { + this->eyeTexIndex2 = index; +} + +void EnZl2_setMouthIndex(EnZl2* this, s16 index) { + this->mouthTexIndex = index; +} + +void func_80B4ED2C(EnZl2* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +s32 EnZl2_UpdateSkelAnime(EnZl2* this) { + return SkelAnime_Update(&this->skelAnime); +} + +CsCmdActorAction* EnZl2_GetNpcAction(GlobalContext* globalCtx, s32 idx) { + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + return globalCtx->csCtx.npcActions[idx]; + } + return NULL; +} + +void func_80B4EDB8(EnZl2* this, GlobalContext* globalCtx, s32 arg2) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, arg2); + + if (npcAction != NULL) { + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + } +} + +void func_80B4EE38(EnZl2* this, s16 arg1, s32 arg2) { + s32 phi_a3; + s32 temp_v1; + s32 phi_v0 = arg2; + + if (this->unk_24C != 0) { + temp_v1 = (s16)(arg1 + this->unk_1DC[phi_v0]); + phi_a3 = arg1 - this->unk_20C[phi_v0]; + phi_v0 = this->unk_1AC[phi_v0]; + + if ((s32)fabsf((f32)phi_a3) > 0x8000) { + if (arg1 > 0) { + phi_a3 -= 0x10000; + } else { + phi_a3 += 0x10000; + } + } + if (phi_a3 != 0) { + phi_v0 += (phi_a3 - phi_v0) / 16; + } + if (phi_v0 != 0) { + phi_v0 -= (phi_v0 / 10); + } + if ((s16)(temp_v1 - arg1) != 0) { + phi_v0 -= ((s16)(temp_v1 - arg1) / 50); + } + temp_v1 += phi_v0; + if (((this->unk_1AC[arg2] * phi_v0) <= 0) && ((s16)(temp_v1 - arg1) > -0x64) && + ((s16)(temp_v1 - arg1) < 0x64)) { + temp_v1 = arg1; + phi_v0 = 0; + } + this->unk_1AC[arg2] = phi_v0; + this->unk_1DC[arg2] = temp_v1 - arg1; + } + this->unk_20C[arg2] = arg1; +} + +void func_80B4EF64(EnZl2* this, s16 arg1, s32 arg2) { + s32 temp_t0 = arg2; + s32 temp_t2; + s32 temp_v1; + s32 phi_t1; + s32 phi_v0; + s32 phi_a0; + f32 curFrame; + f32 unk_278; + + if (temp_t0 == 2) { + phi_a0 = 0x3A98; + phi_t1 = 0; + } else if (temp_t0 == 5) { + phi_a0 = 0x32C8; + phi_t1 = 3; + } else if (temp_t0 == 8) { + phi_a0 = 0x2EE0; + phi_t1 = 6; + } else if (temp_t0 == 11) { + phi_a0 = 0x4000; + phi_t1 = 9; + } else if (temp_t0 == 14) { + phi_a0 = 0x4000; + phi_t1 = 12; + } else if (temp_t0 == 17) { + phi_a0 = 0x4000; + phi_t1 = 15; + } else { + phi_a0 = 0x4000; + phi_t1 = 18; + } + + if (this->unk_24C != 0) { + phi_v0 = this->unk_1DC[temp_t0] + arg1; + temp_v1 = (s16)(phi_v0 & 0xFFFF); + temp_t2 = arg1 - this->unk_20C[temp_t0]; + phi_v0 = this->unk_1AC[temp_t0]; + + if ((s32)fabsf((f32)temp_t2) > 0x8000) { + if (arg1 > 0) { + temp_t2 -= 0x10000; + } else { + temp_t2 += 0x10000; + } + } + if (phi_t1 >= 0) { + temp_t2 += (ABS(this->unk_1AC[phi_t1]) / 3); + } + if (temp_t2 != 0) { + phi_v0 += ((temp_t2 - phi_v0) / 16); + } + if (phi_v0 != 0) { + phi_v0 -= phi_v0 / 10; + } + if ((s16)(temp_v1 - phi_a0) != 0) { + phi_v0 -= (s16)(temp_v1 - phi_a0) / 50; + } + temp_v1 += phi_v0; + + if (((this->unk_1AC[arg2] * phi_v0) <= 0) && ((s16)(temp_v1 - phi_a0) > -0x64) && + ((s16)(temp_v1 - phi_a0) < 0x64)) { + temp_v1 = phi_a0; + phi_v0 = 0; + } + + if (arg2 == 2) { + if ((this->action == 5) || (this->action == 30)) { + curFrame = this->skelAnime.curFrame; + unk_278 = this->unk_278; + temp_t0 = (s32)((3500.0f * curFrame) / unk_278) + phi_a0; + if (temp_t0 >= temp_v1) { + temp_v1 = temp_t0; + phi_v0 /= -2; + } + } else if ((this->action == 6) || (this->action == 31)) { + temp_t0 = phi_a0 + 0xDAC; + if (temp_t0 >= temp_v1) { + temp_v1 = temp_t0; + phi_v0 /= -2; + } + } else if (this->action == 20) { + temp_t0 = phi_a0 - 0x3E8; + if (temp_t0 >= temp_v1) { + temp_v1 = temp_t0; + phi_v0 /= -2; + } + } + } + this->unk_1AC[arg2] = phi_v0; + this->unk_1DC[arg2] = temp_v1 - arg1; + } + this->unk_20C[arg2] = arg1; +} + +void func_80B4F230(EnZl2* this, s16 arg1, s32 arg2) { + s32 temp_v1; + s16 temp_t0; + s32 temp_t2; + s32 temp_t3; + s32 phi_v0; + s32 index1AC; + s32 phi_t5; + + if (this->unk_24C != 0) { + temp_v1 = this->unk_1DC[arg2] - arg1; + temp_t0 = temp_v1; + temp_t2 = temp_t0; + temp_t3 = this->unk_1AC[arg2]; + phi_v0 = temp_t3; + temp_t3 = arg1 - this->unk_20C[arg2]; + + if (arg2 == 1) { + index1AC = 0; + phi_t5 = this->unk_1AC[index1AC]; + } else if (arg2 == 4) { + index1AC = 3; + phi_t5 = this->unk_1AC[index1AC]; + } else if (arg2 == 7) { + index1AC = 6; + phi_t5 = this->unk_1AC[index1AC]; + } else if (arg2 == 10) { + index1AC = 9; + phi_t5 = ABS(this->unk_1AC[index1AC]); + } else if (arg2 == 13) { + index1AC = 12; + phi_t5 = ABS(this->unk_1AC[index1AC]); + } else if (arg2 == 16) { + index1AC = 15; + phi_t5 = -ABS(this->unk_1AC[index1AC]); + } else { + index1AC = 18; + phi_t5 = -ABS(this->unk_1AC[index1AC]); + } + + if ((s32)fabsf(temp_t3) > 0x8000) { + if (arg1 > 0) { + temp_t3 -= 0x10000; + } else { + temp_t3 += 0x10000; + } + } + if (index1AC >= 0) { + temp_t3 += phi_t5 / 3; + } + + if (temp_t3 != 0) { + phi_v0 += (temp_t3 - phi_v0) / 16; + } + if (phi_v0 != 0) { + phi_v0 -= phi_v0 / 10; + } + if (temp_t0 != 0) { + phi_v0 -= temp_t0 / 50; + } + temp_v1 += phi_v0; + if (((this->unk_1AC[arg2] * phi_v0) <= 0) && (temp_t2 > -0x64) && (temp_t2 < 0x64)) { + temp_v1 = 0; + phi_v0 = 0; + } + this->unk_1AC[arg2] = phi_v0; + this->unk_1DC[arg2] = arg1 + temp_v1; + } + this->unk_20C[arg2] = arg1; +} + +s32 func_80B4F45C(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + s32 pad; + EnZl2* this = (EnZl2*)thisx; + Mtx* sp74; + MtxF sp34; + Vec3s sp2C; + s16 pad2; + s16* unk_1DC = this->unk_1DC; + + if (limbIndex == 14) { + sp74 = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx) * 7); + gSPSegment((*gfx)++, 0x0C, sp74); + + Matrix_Push(); + Matrix_Translate(pos->x, pos->y, pos->z, MTXMODE_APPLY); + Matrix_RotateZYX(rot->x, rot->y, rot->z, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Translate(362.0f, -133.0f, 0.0f, MTXMODE_APPLY); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 0); + func_80B4F230(this, sp2C.x, 1); + func_80B4EF64(this, sp2C.z, 2); + } + Matrix_RotateZYX(unk_1DC[0] + kREG(31), unk_1DC[1] + kREG(32), unk_1DC[2] + kREG(33), MTXMODE_APPLY); + Matrix_Translate(-188.0f, -184.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[0], "../z_en_zl2.c", 1056); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 3); + func_80B4F230(this, sp2C.x, 4); + } + Matrix_RotateZYX(unk_1DC[3] + kREG(34), unk_1DC[4] + kREG(35), unk_1DC[5] + kREG(36), MTXMODE_APPLY); + Matrix_Translate(-410.0f, -184.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[1], "../z_en_zl2.c", 1100); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 6); + func_80B4F230(this, sp2C.x, 7); + } + Matrix_RotateZYX(unk_1DC[6] + kREG(37), unk_1DC[7] + kREG(38), unk_1DC[8] + kREG(39), MTXMODE_APPLY); + Matrix_Translate(-1019.0f, -26.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[2], "../z_en_zl2.c", 1120); + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(467.0f, 265.0f, 389.0f, MTXMODE_APPLY); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 9); + func_80B4F230(this, sp2C.x, 10); + func_80B4EF64(this, sp2C.z, 11); + } + Matrix_RotateZYX(unk_1DC[9] + kREG(40), unk_1DC[10] + kREG(41), unk_1DC[11] + kREG(42), MTXMODE_APPLY); + Matrix_Translate(-427.0f, -1.0f, -3.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[3], "../z_en_zl2.c", 1145); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 12); + func_80B4F230(this, sp2C.x, 13); + func_80B4EF64(this, sp2C.z, 14); + } + Matrix_RotateZYX(unk_1DC[12] + kREG(43), unk_1DC[13] + kREG(44), unk_1DC[14] + kREG(45), MTXMODE_APPLY); + Matrix_Translate(-446.0f, -52.0f, 84.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[4], "../z_en_zl2.c", 1164); + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(467.0f, 265.0f, -389.0f, MTXMODE_APPLY); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 15); + func_80B4F230(this, sp2C.x, 16); + func_80B4EF64(this, sp2C.z, 17); + } + Matrix_RotateZYX(unk_1DC[15] + kREG(46), unk_1DC[16] + kREG(47), unk_1DC[17] + kREG(48), MTXMODE_APPLY); + Matrix_Translate(-427.0f, -1.0f, 3.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[5], "../z_en_zl2.c", 1189); + Matrix_Get(&sp34); + Matrix_MtxFToYXZRotS(&sp34, &sp2C, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B4EE38(this, sp2C.y, 18); + func_80B4F230(this, sp2C.x, 19); + func_80B4EF64(this, sp2C.z, 20); + } + Matrix_RotateZYX(unk_1DC[18] + kREG(49), unk_1DC[19] + kREG(50), unk_1DC[20] + kREG(51), MTXMODE_APPLY); + Matrix_Translate(-446.0f, -52.0f, -84.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp74[6], "../z_en_zl2.c", 1208); + Matrix_Pop(); + Matrix_Pop(); + this->unk_24C = 1; + } + return false; +} + +void EnZl2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnZl2* this = (EnZl2*)thisx; + s32 pad[2]; + + if (limbIndex == 10) { + if ((this->unk_254 != 0) && (globalCtx->csCtx.frames >= 900)) { + gSPDisplayList((*gfx)++, gZelda2OcarinaDL); + } + + { + Player* player = GET_PLAYER(globalCtx); + Matrix_Push(); + if (player->rightHandType == 0xFF) { + Matrix_Put(&player->shieldMf); + Matrix_Translate(180.0f, 979.0f, -375.0f, MTXMODE_APPLY); + Matrix_RotateZYX(-0x5DE7, -0x53E9, 0x3333, MTXMODE_APPLY); + Matrix_Scale(1.2f, 1.2f, 1.2f, MTXMODE_APPLY); + gSPMatrix((*gfx)++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zl2.c", 1253), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList((*gfx)++, gZelda2OcarinaDL); + } + Matrix_Pop(); + } + } +} + +void func_80B4FCCC(EnZl2* this, GlobalContext* globalCtx) { + s32 unk_274 = this->unk_274; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[unk_274].segment); +} + +void func_80B4FD00(EnZl2* this, AnimationHeader* animation, u8 arg2, f32 transitionRate, s32 arg4) { + f32 frameCount = Animation_GetLastFrame(animation); + f32 playbackSpeed; + f32 unk0; + f32 fc; + + if (arg4 == 0) { + unk0 = 0.0f; + fc = frameCount; + playbackSpeed = 1.0f; + } else { + fc = 0.0f; + unk0 = frameCount; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animation, playbackSpeed, unk0, fc, arg2, transitionRate); +} + +void func_80B4FD90(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_00B5FC, 0, 0.0f, 0); + this->action = 1; +} + +void func_80B4FDD4(EnZl2* this) { + if (Animation_OnFrame(&this->skelAnime, 14.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_PL_WALK_CONCRETE); + } +} + +void func_80B4FE10(GlobalContext* globalCtx) { + if ((globalCtx->csCtx.frames >= 830) && (globalCtx->csCtx.frames < 1081)) { + func_800788CC(NA_SE_EV_EARTHQUAKE - SFX_FLAG); + } +} + +void func_80B4FE48(EnZl2* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EV_GOTO_HEAVEN - SFX_FLAG); +} + +void func_80B4FE6C(EnZl2* this) { + func_80078914(&this->actor.projectedPos, NA_SE_EN_GANON_LAUGH); +} + +void func_80B4FE90(EnZl2* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_SURPRISE); +} + +void func_80B4FEB4(EnZl2* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_PAIN); +} + +void func_80B4FED8(EnZl2* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_CRY_0); +} + +void EnZl2_GiveLightArrows(EnZl2* this, GlobalContext* globalCtx) { + Player* player; + f32 posX; + f32 posY; + f32 posZ; + + if (this->unk_244 == 0) { + player = GET_PLAYER(globalCtx); + posX = player->actor.world.pos.x; + posY = player->actor.world.pos.y + 80.0f; + posZ = player->actor.world.pos.z; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, posX, posY, posZ, 0, 0, 0, 0x17); + Item_Give(globalCtx, ITEM_ARROW_LIGHT); + this->unk_244 = 1; + } +} + +void func_80B4FF84(EnZl2* this, GlobalContext* globalCtx) { + f32 posX; + f32 posY; + f32 posZ; + + if (this->unk_250 == 0) { + posX = this->actor.world.pos.x; + posY = this->actor.world.pos.y; + posZ = this->actor.world.pos.z; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, WARP_YELLOW); + this->unk_250 = 1; + } +} + +void func_80B4FFF0(EnZl2* this, GlobalContext* globalCtx) { + f32 posX; + f32 posY; + f32 posZ; + + if (this->unk_248 == 0) { + posX = this->actor.world.pos.x; + posY = this->actor.world.pos.y + (kREG(5) + -26.0f); + posZ = this->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0x4000, + 0, WARP_PURPLE_CRYSTAL); + this->unk_248 = 1; + } +} + +void func_80B5008C(EnZl2* this) { + Actor* child = this->actor.child; + + if (child != NULL) { + child->world.pos.x = this->actor.world.pos.x; + child->world.pos.y = this->actor.world.pos.y + (kREG(5) + -26.0f); + child->world.pos.z = this->actor.world.pos.z; + } +} + +void func_80B500E0(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + Vec3f* thisPos = &this->actor.world.pos; + f32 startX; + f32 startY; + f32 startZ; + f32 endX; + f32 endY; + f32 endZ; + f32 someFloat; + + if (npcAction != NULL) { + someFloat = + Environment_LerpWeightAccelDecel(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames, 8, 8); + startX = npcAction->startPos.x; + startY = npcAction->startPos.y; + startZ = npcAction->startPos.z; + endX = npcAction->endPos.x; + endY = npcAction->endPos.y; + endZ = npcAction->endPos.z; + thisPos->x = ((endX - startX) * someFloat) + startX; + thisPos->y = ((endY - startY) * someFloat) + startY; + thisPos->z = ((endZ - startZ) * someFloat) + startZ; + } +} + +void func_80B501C4(EnZl2* this, s32 alpha) { + if (this->actor.child != NULL) { + ((DoorWarp1*)this->actor.child)->crystalAlpha = alpha; + } +} + +void func_80B501E8(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + + if (npcAction != NULL) { + this->actor.shape.shadowAlpha = this->alpha = + (1.0f - Environment_LerpWeight(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames)) * + 255.0f; + func_80B501C4(this, this->alpha); + } +} + +void func_80B50260(EnZl2* this, GlobalContext* globalCtx) { + this->action = 1; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80B50278(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + + this->actor.world.pos.x = npcAction->startPos.x; + this->actor.world.pos.y = npcAction->startPos.y; + this->actor.world.pos.z = npcAction->startPos.z; + this->actor.world.rot.y = this->actor.shape.rot.y = npcAction->rot.y; + this->actor.shape.shadowAlpha = 0xFF; + this->action = 2; + this->drawConfig = 1; +} + +void func_80B50304(EnZl2* this, GlobalContext* globalCtx) { + s32 pad[2]; + ActorShape* shape = &this->actor.shape; + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + f32 actionXDelta; + f32 actionZDelta; + + actionXDelta = npcAction->endPos.x - npcAction->startPos.x; + actionZDelta = npcAction->endPos.z - npcAction->startPos.z; + func_80B4FD00(this, &gZelda2Anime1Anim_0003BC, 0, -12.0f, 0); + this->action = 3; + this->drawConfig = 1; + this->unk_23C = 0.0f; + shape->shadowAlpha = 255; + this->actor.world.rot.y = shape->rot.y = Math_FAtan2F(actionXDelta, actionZDelta) * (0x8000 / M_PI); +} + +void func_80B503DC(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + + if ((npcAction != NULL) && (globalCtx->csCtx.frames >= npcAction->endFrame)) { + this->action = 4; + } +} + +void func_80B5042C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_0022D0, 2, -8.0f, 0); + this->action = 5; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_27C = 0.0f; +} + +void func_80B50488(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_002750, 0, 0.0f, 0); + this->action = 6; + this->drawConfig = 1; + } +} + +void func_80B504D4(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_00325C, 2, -8.0f, 0); + this->action = 7; + this->drawConfig = 1; + this->unk_27C = 0.0f; + EnZl2_setMouthIndex(this, 1); + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80B5053C(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_003538, 0, 0.0f, 0); + this->action = 8; + } +} + +void func_80B50580(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_000A50, 2, -8.0f, 0); + this->action = 9; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80B505D4(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_000EB0, 0, 0.0f, 0); + this->action = 10; + } +} + +void func_80B50618(EnZl2* this, GlobalContext* globalCtx) { + EnZl2_GiveLightArrows(this, globalCtx); + this->action = 11; +} + +void func_80B50644(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FF84(this, globalCtx); + this->action = 12; +} + +void func_80B50670(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_00B5FC, 0, -8.0f, 0); + this->action = 13; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80B506C4(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_00AAD4, 2, -8.0f, 0); + this->action = 14; + this->drawConfig = 1; + EnZl2_setEyesIndex(this, 4); + EnZl2_setMouthIndex(this, 2); + this->actor.shape.shadowAlpha = 0xFF; + func_80B4FE90(this); +} + +void func_80B5073C(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_00AFE0, 0, 0.0f, 0); + this->action = 15; + } +} + +void func_80B50780(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_001670, 2, -8.0f, 0); + this->action = 16; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + func_80B4FFF0(this, globalCtx); + EnZl2_setEyesIndex(this, 3); +} + +void func_80B507E8(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_001B48, 0, 0.0f, 0); + this->action = 17; + } +} + +void func_80B5082C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_002B14, 2, -8.0f, 0); + this->action = 18; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80B50880(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_002F30, 0, 0.0f, 0); + this->action = 19; + func_80B4FEB4(this); + } +} + +void func_80B508C8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_001010, 2, -8.0f, 0); + this->action = 20; + this->drawConfig = 1; + EnZl2_setEyesIndex(this, 6); + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80B50928(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_0013A0, 0, 0.0f, 0); + this->action = 21; + func_80B4FED8(this); + } +} + +void func_80B50970(EnZl2* this, GlobalContext* globalCtx) { + this->action = 22; +} + +void func_80B50980(EnZl2* this, GlobalContext* globalCtx) { + this->action = 23; + this->drawConfig = 2; + this->alpha = 255; +} + +void func_80B509A0(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + + if (npcAction != NULL) { + if (globalCtx->csCtx.frames >= npcAction->endFrame) { + this->action = 24; + this->drawConfig = 0; + func_80B4FE6C(this); + } + } +} + +void func_80B50A04(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + s32 newAction; + s32 unk_240; + + if (npcAction != NULL) { + newAction = npcAction->action; + unk_240 = this->unk_240; + if (newAction != unk_240) { + switch (newAction) { + case 1: + func_80B50260(this, globalCtx); + break; + case 2: + func_80B50278(this, globalCtx); + break; + case 3: + func_80B50304(this, globalCtx); + break; + case 4: + func_80B5042C(this, globalCtx); + break; + case 5: + func_80B504D4(this, globalCtx); + break; + case 6: + func_80B50580(this, globalCtx); + break; + case 7: + func_80B50618(this, globalCtx); + break; + case 8: + func_80B50670(this, globalCtx); + break; + case 9: + func_80B506C4(this, globalCtx); + break; + case 10: + func_80B50780(this, globalCtx); + break; + case 11: + func_80B5082C(this, globalCtx); + break; + case 12: + func_80B508C8(this, globalCtx); + break; + case 13: + func_80B50970(this, globalCtx); + break; + case 14: + func_80B50980(this, globalCtx); + break; + case 15: + func_80B50644(this, globalCtx); + break; + default: + osSyncPrintf("En_Zl2_inAgain_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_240 = newAction; + } + } +} + +void func_80B50BBC(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B50A04(this, globalCtx); +} + +void func_80B50BEC(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50C40(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FDD4(this); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B500E0(this, globalCtx); + func_80B503DC(this, globalCtx); +} + +void func_80B50CA8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50CFC(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B4EA40(this); + func_80B50488(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B50D50(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50D94(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B4EAF4(this); + func_80B5053C(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B50DE8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50E3C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + func_80B505D4(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B50E90(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50EE4(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50F38(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50F8C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B50FE8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B5073C(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B51034(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B51080(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B507E8(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B510CC(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B51118(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B50880(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B51164(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B50A04(this, globalCtx); +} + +void func_80B511B0(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE10(globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B50928(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B511FC(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B5008C(this); + func_80B50A04(this, globalCtx); +} + +void func_80B51250(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4FE48(this); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B500E0(this, globalCtx); + func_80B5008C(this); + func_80B50A04(this, globalCtx); +} + +void func_80B512B8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B501E8(this, globalCtx); + func_80B509A0(this, globalCtx); +} + +void func_80B51310(EnZl2* this, GlobalContext* globalCtx) { + Actor* child; + + if (EnZl2_GetNpcAction(globalCtx, 0) == NULL) { + child = this->actor.child; + if (child != NULL) { + Actor_Kill(child); + } + Actor_Kill(&this->actor); + } +} + +void func_80B5135C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_00A15C, 0, 0.0f, 0); + this->action = 25; + this->unk_254 = 1; +} + +void func_80B513A8(EnZl2* this, GlobalContext* globalCtx) { + Player* player; + f32 posX; + f32 posY; + f32 posZ; + + if (this->unk_250 == 0) { + player = GET_PLAYER(globalCtx); + posX = player->actor.world.pos.x; + posY = player->actor.world.pos.y; + posZ = player->actor.world.pos.z; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0, 0, WARP_UNK_7); + this->unk_250 = 1; + } +} + +void func_80B51418(EnZl2* this, GlobalContext* globalCtx) { + EnZl2_UpdateEyes(this); + if (globalCtx->csCtx.frames < 431) { + EnZl2_setMouthIndex(this, 1); + } else { + EnZl2_setMouthIndex(this, 0); + } +} + +void func_80B5146C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4EA40(this); + EnZl2_setMouthIndex(this, 0); +} + +void func_80B5149C(EnZl2* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.frames < 988) { + EnZl2_setEyesIndex(this, 7); + EnZl2_setEyeIndex2(this, 8); + } else { + EnZl2_UpdateEyes(this); + } + EnZl2_setMouthIndex(this, 0); +} + +void func_80B514F8(EnZl2* this, GlobalContext* globalCtx) { + EnZl2_UpdateEyes(this); + if (globalCtx->csCtx.frames < 1190) { + EnZl2_setMouthIndex(this, 1); + } else { + EnZl2_setMouthIndex(this, 0); + } +} + +void func_80B5154C(EnZl2* this, GlobalContext* globalCtx) { + CutsceneContext* csCtx; + + if (this->skelAnime.mode != 0) { + EnZl2_UpdateEyes(this); + } else { + csCtx = &globalCtx->csCtx; + if (csCtx->frames < 0x5F0) { + func_80B4EBB8(this); + } else if (csCtx->frames == 0x5F0) { + this->unk_27C = 0.0f; + } else { + func_80B4EC48(this); + } + } +} + +void func_80B515C4(EnZl2* this) { + this->action = 25; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80B515D8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FD00(this, &gZelda2Anime1Anim_00A15C, 0, -8.0f, 0); + func_80B4EDB8(this, globalCtx, 0); + this->action = 26; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_27C = 0.0f; +} + +void func_80B51644(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_0087B8, 0, -8.0f, 0); + } +} + +void func_80B51678(EnZl2* this) { + func_80B4FD00(this, &gZelda2Anime1Anim_007D0C, 2, -8.0f, 0); + this->action = 27; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_27C = 0.0f; +} + +void func_80B516D0(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_009AD4, 0, -8.0f, 0); + } +} + +void func_80B51704(EnZl2* this) { + func_80B4FD00(this, &gZelda2Anime1Anim_0090D8, 2, -8.0f, 0); + this->action = 28; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_27C = 0.0f; +} + +void func_80B5175C(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_006778, 0, -8.0f, 0); + } +} + +void func_80B51790(EnZl2* this) { + func_80B4FD00(this, &gZelda2Anime1Anim_005F40, 2, -8.0f, 0); + this->action = 29; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; +} + +void func_80B517E0(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_002750, 0, -8.0f, 0); + this->action = 31; + } +} + +void func_80B51824(EnZl2* this) { + func_80B4FD00(this, &gZelda2Anime1Anim_0022D0, 2, -8.0f, 0); + this->action = 30; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_27C = 0.0f; +} + +void func_80B5187C(EnZl2* this, s32 arg1) { + if (arg1 != 0) { + func_80B4FD00(this, &gZelda2Anime1Anim_00A79C, 0, -8.0f, 0); + this->unk_27C = 0.0f; + } +} + +void func_80B518C0(EnZl2* this) { + func_80B4FD00(this, SEGMENTED_TO_VIRTUAL(&gZelda2Anime1Anim_004900), 2, -8.0f, 0); + this->action = 32; + this->drawConfig = 1; + this->actor.shape.shadowAlpha = 0xFF; + this->unk_27C = 0.0f; +} + +void func_80B51948(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + s32 newAction; + s32 unk_240; + + if (npcAction != NULL) { + newAction = npcAction->action; + unk_240 = this->unk_240; + if (newAction != unk_240) { + switch (newAction) { + case 1: + func_80B515C4(this); + break; + case 2: + func_80B515D8(this, globalCtx); + break; + case 16: + func_80B51678(this); + break; + case 17: + func_80B51704(this); + break; + case 18: + func_80B51790(this); + break; + case 4: + func_80B51824(this); + break; + case 20: + func_80B518C0(this); + break; + case 21: + func_80B513A8(this, globalCtx); + break; + default: + osSyncPrintf("En_Zl2_inEnding_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_240 = newAction; + } + } +} + +void func_80B51A5C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B51948(this, globalCtx); +} + +void func_80B51A8C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B51418(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B51948(this, globalCtx); +} + +void func_80B51AE4(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + func_80B51644(this, EnZl2_UpdateSkelAnime(this)); + func_80B51948(this, globalCtx); +} + +void func_80B51B44(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B5149C(this, globalCtx); + func_80B516D0(this, EnZl2_UpdateSkelAnime(this)); + func_80B51948(this, globalCtx); +} + +void func_80B51BA8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B514F8(this, globalCtx); + func_80B5175C(this, EnZl2_UpdateSkelAnime(this)); + func_80B51948(this, globalCtx); +} + +void func_80B51C0C(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B5146C(this, globalCtx); + func_80B517E0(this, EnZl2_UpdateSkelAnime(this)); +} + +void func_80B51C64(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateSkelAnime(this); + func_80B51948(this, globalCtx); +} + +void func_80B51CA8(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + func_80B5154C(this, globalCtx); + func_80B5187C(this, EnZl2_UpdateSkelAnime(this)); + func_80B51948(this, globalCtx); +} + +void func_80B51D0C(EnZl2* this, GlobalContext* globalCtx) { + this->action = 33; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80B51D24(EnZl2* this, GlobalContext* globalCtx) { + s32 pad[2]; + u32 sfxId; + SkelAnime* skelAnime = &this->skelAnime; + + if (Animation_OnFrame(skelAnime, 6.0f) || Animation_OnFrame(skelAnime, 0.0f)) { + if (this->actor.bgCheckFlags & 1) { + sfxId = SFX_FLAG; + sfxId += SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + func_80078914(&this->actor.projectedPos, sfxId); + } + } +} + +void func_80B51DA4(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + Vec3f* thisPos = &this->actor.world.pos; + f32 startX; + f32 startY; + f32 startZ; + f32 endX; + f32 endY; + f32 endZ; + f32 someFloat; + + if (npcAction != NULL) { + someFloat = + Environment_LerpWeightAccelDecel(npcAction->endFrame, npcAction->startFrame, globalCtx->csCtx.frames, 0, 8); + startX = npcAction->startPos.x; + startY = npcAction->startPos.y; + startZ = npcAction->startPos.z; + endX = npcAction->endPos.x; + endY = npcAction->endPos.y; + endZ = npcAction->endPos.z; + thisPos->x = ((endX - startX) * someFloat) + startX; + thisPos->y = ((endY - startY) * someFloat) + startY; + thisPos->z = ((endZ - startZ) * someFloat) + startZ; + if (npcAction->endFrame < globalCtx->csCtx.frames) { + Actor_Kill(&this->actor); + } + } +} + +void func_80B51EA8(EnZl2* this) { + this->action = 33; + this->drawConfig = 0; + this->actor.shape.shadowAlpha = 0; +} + +void func_80B51EBC(EnZl2* this, GlobalContext* globalCtx) { + ActorShape* shape = &this->actor.shape; + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + s32 pad[2]; + + this->actor.world.rot.y = shape->rot.y = npcAction->rot.y; + func_80B4FD00(this, &gZelda2Anime1Anim_00B224, 0, 0.0f, 0); + this->action = 34; + this->drawConfig = 1; + shape->shadowAlpha = 255; +} + +void func_80B51F38(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + + if (npcAction != NULL) { + if (globalCtx->csCtx.frames - 8 >= npcAction->endFrame) { + func_80B4FD00(this, &gZelda2Anime1Anim_00B5FC, 0, -8.0f, 0); + this->action = 35; + } + } +} + +void func_80B51FA8(EnZl2* this, GlobalContext* globalCtx) { + CsCmdActorAction* npcAction = EnZl2_GetNpcAction(globalCtx, 0); + s32 action; + s32 unk_240; + + if (npcAction != NULL) { + action = npcAction->action; + unk_240 = this->unk_240; + if (action != unk_240) { + switch (action) { + case 1: + func_80B51EA8(this); + break; + case 2: + func_80B51EBC(this, globalCtx); + break; + case 14: + Actor_Kill(&this->actor); + break; + default: + osSyncPrintf("En_Zl2_inRunning_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + break; + } + this->unk_240 = action; + } + } +} + +void func_80B52068(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B51FA8(this, globalCtx); +} + +void func_80B52098(EnZl2* this, GlobalContext* globalCtx) { + func_80B4FCCC(this, globalCtx); + func_80B4ED2C(this, globalCtx); + EnZl2_UpdateEyes(this); + EnZl2_UpdateSkelAnime(this); + func_80B51D24(this, globalCtx); + func_80B51F38(this, globalCtx); + func_80B51DA4(this, globalCtx); +} + +void func_80B52108(EnZl2* this, GlobalContext* globalCtx) { +} + +void func_80B52114(EnZl2* this, GlobalContext* globalCtx) { + switch (this->actor.params) { + case 1: + func_80B5135C(this, globalCtx); + break; + case 4: + func_80B51D0C(this, globalCtx); + break; + case 0: + func_80B4FD90(this, globalCtx); + break; + default: + osSyncPrintf(VT_FGCOL(RED) " En_Oa2 の arg_data がおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + func_80B4FD90(this, globalCtx); + } +} + +void func_80B521A0(EnZl2* this, GlobalContext* globalCtx) { + s32 pad; + ObjectContext* objectCtx = &globalCtx->objectCtx; + s32 bankIndex = Object_GetIndex(objectCtx, OBJECT_ZL2_ANIME1); + s32 pad2; + + if (bankIndex < 0) { + osSyncPrintf(VT_FGCOL(RED) "En_Zl2_main_bankアニメーションのバンクを読めない!!!!!!!!!!!!\n" VT_RST); + return; + } + + if (Object_IsLoaded(objectCtx, bankIndex)) { + this->unk_274 = bankIndex; + func_80B4FCCC(this, globalCtx); + this->unk_278 = Animation_GetLastFrame(&gZelda2Anime1Anim_0022D0); + func_80B52114(this, globalCtx); + } +} + +void EnZl2_Update(Actor* thisx, GlobalContext* globalCtx) { + EnZl2* this = (EnZl2*)thisx; + + if (this->action < 0 || this->action >= 0x24 || sActionFuncs[this->action] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void EnZl2_Init(Actor* thisx, GlobalContext* globalCtx) { + EnZl2* this = (EnZl2*)thisx; + ActorShape* shape = &thisx->shape; + s32 pad; + + ActorShape_Init(shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + shape->shadowAlpha = 0; + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZelda2Skel, NULL, NULL, NULL, 0); + + switch (thisx->params) { + case 1: + Audio_SetSoundBanksMute(0x6F); + break; + case 4: + gSaveContext.timer2State = 0; + break; + } +} + +s32 EnZl2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnZl2* this = (EnZl2*)thisx; + + if (this->overrideLimbDrawConfig < 0 || this->overrideLimbDrawConfig > 0 || + sOverrideLimbDrawFuncs[this->overrideLimbDrawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画前処理モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return 0; + } + return sOverrideLimbDrawFuncs[this->overrideLimbDrawConfig](globalCtx, limbIndex, dList, pos, rot, thisx, gfx); +} + +void func_80B523BC(EnZl2* this, GlobalContext* globalCtx) { +} + +void func_80B523C8(EnZl2* this, GlobalContext* globalCtx) { + s32 pad[3]; + s16 eyeTexIndex = this->eyeTexIndex; + s16 eyeTexIndex2 = this->eyeTexIndex2; + void* eyeTex = sEyeTextures[eyeTexIndex]; + void* eyeTex2 = sEyeTextures[eyeTexIndex2]; + SkelAnime* skelAnime = &this->skelAnime; + s16 mouthTexIndex = this->mouthTexIndex; + void* mouthTex = sMouthTextures[mouthTexIndex]; + s32 pad1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zl2.c", 1623); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex2)); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0x0B, &D_80116280[2]); + + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnZl2_OverrideLimbDraw, EnZl2_PostLimbDraw, this, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zl2.c", 1648); +} + +void func_80B525D4(EnZl2* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeTexIndex = this->eyeTexIndex; + void* eyeTex = sEyeTextures[eyeTexIndex]; + s16 mouthTexIndex = this->mouthTexIndex; + SkelAnime* skelAnime = &this->skelAnime; + void* mouthTex = sMouthTextures[mouthTexIndex]; + s32 pad1; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zl2.c", 1663); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 0x0B, &D_80116280[0]); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnZl2_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zl2.c", 1692); +} + +void EnZl2_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnZl2* this = (EnZl2*)thisx; + + if ((this->drawConfig < 0) || (this->drawConfig >= 3) || (sDrawFuncs[this->drawConfig] == NULL)) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_En_Zl2/z_en_zl2.h b/soh/src/overlays/actors/ovl_En_Zl2/z_en_zl2.h new file mode 100644 index 000000000..acc850b9f --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl2/z_en_zl2.h @@ -0,0 +1,43 @@ +#ifndef Z_EN_ZL2_H +#define Z_EN_ZL2_H + +#include "ultra64.h" +#include "global.h" + +struct EnZl2; + +typedef void (*EnZl2ActionFunc)(struct EnZl2*, GlobalContext*); +typedef void (*EnZl2DrawFunc)(struct EnZl2*, GlobalContext*); + +typedef struct EnZl2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ s16 eyeTexIndex; + /* 0x0192 */ s16 blinkTimer; + /* 0x0194 */ s16 eyeTexIndex2; + /* 0x0196 */ char unk_196[0x2]; + /* 0x0198 */ s16 mouthTexIndex; + /* 0x019A */ char unk_19A[0x2]; + /* 0x019C */ s32 action; + /* 0x01A0 */ s32 drawConfig; + /* 0x01A4 */ char unk_1A4[0x4]; + /* 0x01A8 */ s32 alpha; + /* 0x01AC */ s16 unk_1AC[0x18]; // ??? + /* 0x01DC */ s16 unk_1DC[0x18]; // ??? + /* 0x020C */ s16 unk_20C[0x18]; // ??? + /* 0x023C */ f32 unk_23C; + /* 0x0240 */ s32 unk_240; + /* 0x0244 */ s32 unk_244; + /* 0x0248 */ s32 unk_248; + /* 0x024C */ s32 unk_24C; + /* 0x0250 */ s32 unk_250; + /* 0x0254 */ s32 unk_254; + /* 0x0258 */ char unk_258[0xC]; + /* 0x0264 */ s32 overrideLimbDrawConfig; + /* 0x0268 */ char unk_268[0xC]; + /* 0x0274 */ s32 unk_274; + /* 0x0278 */ f32 unk_278; + /* 0x027C */ f32 unk_27C; +} EnZl2; // size = 0x0280 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c new file mode 100644 index 000000000..8ad3a2cef --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.c @@ -0,0 +1,2782 @@ +/* + * File: z_en_zl3.c + * Overlay: ovl_En_Zl3 + * Description: Adult Zelda + */ + +#include "z_en_zl3.h" + +#include "vt.h" +#include "overlays/actors/ovl_En_Encount2/z_en_encount2.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" +#include "objects/object_zl2/object_zl2.h" +#include "objects/object_zl2_anime2/object_zl2_anime2.h" + +#define FLAGS ACTOR_FLAG_4 + +void EnZl3_Init(Actor* thisx, GlobalContext* globalCtx); +void EnZl3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnZl3_Update(Actor* thisx, GlobalContext* globalCtx); +void EnZl3_Draw(Actor* thisx, GlobalContext* globalCtx); +void func_80B59AD0(EnZl3* this, GlobalContext* globalCtx); +void EnZl3_Reset(void); + +static ColliderCylinderInitType1 sCylinderInit = { + { + COLTYPE_HIT0, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 25, 80, 0, { 0, 0, 0 } }, +}; + +static void* sEyeTextures[] = { gZelda2EyeOpenTex, gZelda2EyeHalfTex, gZelda2EyeShutTex, gZelda2Eye03Tex, + gZelda2Eye04Tex, gZelda2Eye05Tex, gZelda2Eye06Tex, NULL }; + +static void* sMouthTextures[] = { gZelda2MouthSeriousTex, gZelda2MouthHappyTex, gZelda2MouthOpenTex }; + +static s32 D_80B5A468 = 0; + +static Vec3f D_80B5A46C = { 0.0f, 0.0f, 0.0f }; + +static Vec3f D_80B5A478 = { 0.0f, 10.0f, 0.0f }; + +static f32 D_80B5A484 = 0.0f; + +static Vec3f D_80B5A488 = { 0.0f, 0.0f, 0.0f }; + +static s32 D_80B5A494 = -1; + +static Vec3f D_80B5A498 = { 148.0f, 260.0f, -87.0f }; + +static Vec3f D_80B5A4A4 = { -12.0f, 260.0f, -147.0f }; + +static Vec3f D_80B5A4B0 = { 42.0f, 260.0f, 13.0f }; + +static u32 D_80B5A4BC = 0; + +void func_80B533B0(Actor* thisx, GlobalContext* globalCtx) { + EnZl3* this = (EnZl3*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &sCylinderInit); +} + +void func_80B533FC(EnZl3* this, GlobalContext* globalCtx) { + ColliderCylinder* collider = &this->collider; + s32 pad[4]; + + Collider_UpdateCylinder(&this->actor, collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &collider->base); +} + +void EnZl3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnZl3* this = (EnZl3*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80B53468(void) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_ESCAPE); +} + +BossGanon2* func_80B53488(EnZl3* this, GlobalContext* globalCtx) { + if (this->ganon == NULL) { + Actor* actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_BOSS].head; + + while (actorIt != NULL) { + if (actorIt->id == ACTOR_BOSS_GANON2) { + this->ganon = (BossGanon2*)actorIt; + break; + } + actorIt = actorIt->next; + } + } + return this->ganon; +} + +void EnZl3_UpdateEyes(EnZl3* this) { + s32 pad[2]; + s16* eyeTexIndex = &this->eyeTexIndex; + s16* blinkTimer = &this->blinkTimer; + + if (DECR(*blinkTimer) == 0) { + *blinkTimer = Rand_S16Offset(60, 60); + } + *eyeTexIndex = *blinkTimer; + if (*eyeTexIndex >= 3) { + *eyeTexIndex = 0; + } +} + +void EnZl3_setEyeIndex(EnZl3* this, s16 index) { + this->eyeTexIndex = index; +} + +void EnZl3_setMouthIndex(EnZl3* this, s16 index) { + this->mouthTexIndex = index; +} + +void func_80B5357C(EnZl3* this, GlobalContext* globalCtx) { + Vec3f* thisPos = &this->actor.world.pos; + Vec3f sp20; + + sp20.x = thisPos->x + ((Rand_ZeroOne() - 0.5f) * 10.0f); + sp20.y = thisPos->y; + sp20.z = thisPos->z + ((Rand_ZeroOne() - 0.5f) * 10.0f); + Item_DropCollectible(globalCtx, &sp20, 3); +} + +void func_80B53614(EnZl3* this, GlobalContext* globalCtx) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_RIVER_SOUND, -442.0f, 4102.0f, -371.0f, 0, 0, 0, 0x12); +} + +void func_80B5366C(EnZl3* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 75.0f, 30.0f, 30.0f, 5); +} + +void func_80B536B4(EnZl3* this) { + this->actor.bgCheckFlags &= ~0x9; +} + +void func_80B536C4(EnZl3* this) { + s32 pad[2]; + Vec3s* vec1 = &this->unk_3F8.unk_08; + Vec3s* vec2 = &this->unk_3F8.unk_0E; + + Math_SmoothStepToS(&vec1->x, 0, 20, 6200, 100); + Math_SmoothStepToS(&vec1->y, 0, 20, 6200, 100); + Math_SmoothStepToS(&vec2->x, 0, 20, 6200, 100); + Math_SmoothStepToS(&vec2->y, 0, 20, 6200, 100); +} + +void func_80B53764(EnZl3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_3F8.unk_18 = player->actor.world.pos; + this->unk_3F8.unk_14 = kREG(16) - 16.0f; + func_80034A14(&this->actor, &this->unk_3F8, kREG(17) + 0xC, 2); +} + +s32 func_80B537E8(EnZl3* this) { + s16 yawTowardsPlayer = this->actor.yawTowardsPlayer; + s16* rotY = &this->actor.world.rot.y; + s16* unk_3D0 = &this->unk_3D0; + s16 retVal; + s16 pad[2]; + + Math_SmoothStepToS(unk_3D0, ABS((s16)(yawTowardsPlayer - *rotY)), 5, 6200, 100); + retVal = Math_SmoothStepToS(rotY, yawTowardsPlayer, 5, *unk_3D0, 100); + this->actor.shape.rot.y = *rotY; + return retVal; +} + +void func_80B538B0(EnZl3* this) { + s16 yawTowardsPlayer = this->actor.yawTowardsPlayer; + s16* rotY = &this->actor.world.rot.y; + + if (ABS((s16)(yawTowardsPlayer - *rotY)) >= 0x1556) { + D_80B5A468 = 1; + } + + if (D_80B5A468 != 0) { + if (!func_80B537E8(this)) { + D_80B5A468 = 0; + } + } else { + this->unk_3D0 = 0; + } +} + +s32 EnZl3_UpdateSkelAnime(EnZl3* this) { + return SkelAnime_Update(&this->skelAnime); +} + +s32 func_80B5396C(EnZl3* this) { + return this->unk_3C8; +} + +void func_80B53974(EnZl3* this, u8 arg1) { + this->unk_3C8 = arg1; +} + +void func_80B53980(EnZl3* thisx, s16 y, s32 idx) { + EnZl3* this = (EnZl3*)thisx; // this function might take thisx + s32 action = this->action; + s16 y2 = y; + s32 yTemp; + f32 curFrame; + f32 unk_3DC; + + if (this->unk_2FC != 0) { + SkelAnime* skelAnime = &this->skelAnime; + s32 temp25C = this->unk_25C[idx]; + s32 temp28C = (s16)(y + this->unk_28C[idx]); + s32 temp2BC = y - this->unk_2BC[idx]; + + if ((s32)fabsf(temp2BC) > 0x8000) { + if (y2 > 0) { + temp2BC -= 0x10000; + } else { + temp2BC += 0x10000; + } + } + + if ((idx != 0 || action != 4) && (temp2BC != 0)) { + temp25C += (temp2BC - temp25C) / 16; + } + + if (temp25C != 0) { + temp25C -= temp25C / 10; + } + + if ((s16)(temp28C - y) != 0) { + temp25C -= (s16)(temp28C - y) / 50; + } + + temp28C += temp25C; + if (((this->unk_25C[idx] * temp25C) <= 0) && (((s16)(temp28C - y) > -0x64) && ((s16)(temp28C - y) < 0x64))) { + temp28C = y; + temp25C = 0; + } + if (idx == 0 && action == 3) { + yTemp = y + -11000; + if (skelAnime->mode == 2) { + curFrame = skelAnime->curFrame; + unk_3DC = this->unk_3DC; + yTemp = (s32)((curFrame / unk_3DC) * -11000) + y; + if (0) {}; + if (temp28C >= yTemp) { + temp28C = yTemp; + if (temp25C > 0) { + temp25C /= -2; + } + } + } else { + if (temp28C >= yTemp) { + temp28C = yTemp; + if (temp25C > 0) { + temp25C /= -2; + } + } + } + } + this->unk_25C[idx] = temp25C; + this->unk_28C[idx] = temp28C - y; + } + this->unk_2BC[idx] = y; +} + +void func_80B53B64(EnZl3* this, s16 z, s32 idx) { + SkelAnime* skelAnime = &this->skelAnime; + s32 action = this->action; + s32 phi_a1; + s32 idx25C; + s16 temp_t1; + s32 temp_a0; + s32 phi_v0; + s32 phi_v1; + s32 phi_v1_2; + + if (idx == 2) { + phi_a1 = 15000; + idx25C = 0; + } else if (idx == 5) { + phi_a1 = 13000; + idx25C = 3; + } else if (idx == 8) { + phi_a1 = 12000; + idx25C = 6; + } else if (idx == 11) { + phi_a1 = 0x4000; + idx25C = 9; + } else if (idx == 14) { + phi_a1 = 0x4000; + idx25C = 12; + } else if (idx == 17) { + phi_a1 = 0x4000; + idx25C = 15; + } else { + phi_a1 = 0x4000; + idx25C = 18; + } + + if (this->unk_2FC != 0) { + phi_v0 = this->unk_25C[idx]; + temp_a0 = (s16)(z + this->unk_28C[idx]); + phi_v1 = z - this->unk_2BC[idx]; + + if ((s32)fabsf(phi_v1) > 0x8000) { + if (z > 0) { + phi_v1 -= 0x10000; + } else { + phi_v1 += 0x10000; + } + } + + if (idx25C >= 0) { + phi_v1 += ABS(this->unk_25C[idx25C]) / 3; + } + + if (idx == 2 && (action == 5 || action == 24)) { + if (phi_v1 != 0) { + phi_v0 -= (phi_v1 - phi_v0) / 10; + } + } else if (idx == 2 && action == 22 && skelAnime->mode == 2) { + if (phi_v1 != 0) { + phi_v0 -= (phi_v1 - phi_v0) / 10; + } + } else if (idx == 2 && (action == 20 || action == 21) && skelAnime->mode == 2) { + if (phi_v1 != 0) { + phi_v0 -= (phi_v1 - phi_v0) / 10; + } + } else { + if (phi_v1 != 0) { + phi_v0 += (phi_v1 - phi_v0) / 16; + } + } + + if (phi_v0 != 0) { + phi_v0 -= phi_v0 / 10; + } + + if ((s16)(temp_a0 - phi_a1) != 0) { + phi_v0 -= (s16)(temp_a0 - phi_a1) / 50; + } + + temp_a0 += phi_v0; + phi_v1 = (s16)(temp_a0 - phi_a1); + + if (((this->unk_25C[idx] * phi_v0) <= 0) && (phi_v1 > -100) && (phi_v1 < 100)) { + temp_a0 = phi_a1; + phi_v0 = 0; + } + + if (idx == 2) { + if (action == 4) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3E0 = this->unk_3E0; + + phi_v1_2 = (s32)(((unk_3E0 - curFrame) / unk_3E0) * -2000.0f) + phi_a1; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } + } else if (action == 5) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3E4 = this->unk_3E4; + + phi_v1_2 = (s32)((curFrame / unk_3E4) * -2000.0f) + phi_a1; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } else { + phi_v1_2 = phi_a1 - 2000; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } + } else if ((action == 20) || (action == 21)) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3F4 = this->unk_3F4; + + if (curFrame <= 42.0f) { + phi_v1_2 = phi_a1 - 2000; + } else { + phi_v1_2 = (s32)((((curFrame - 42.0f) * 6200.0f) / (unk_3F4 - 42.0f)) + -2000.0f) + phi_a1; + } + + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } else { + phi_v1_2 = phi_a1 + 4200; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } + } else if (action == 22) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3EC = this->unk_3EC; + + phi_v1_2 = (s32)(((curFrame / unk_3EC) * -5200.0f) + 4200.0f) + phi_a1; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } else { + phi_v1_2 = phi_a1 - 2000; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } + } else if (action == 23) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3F0 = this->unk_3F0; + + phi_v1_2 = (s32)(((curFrame / unk_3F0) * -7600.0f) + -2000.0f) + phi_a1; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } else { + phi_v1_2 = phi_a1 - 9600; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } + } else if (action == 24) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3E8 = this->unk_3E8; + + phi_v1_2 = (s32)(((curFrame / unk_3E8) * 21000.0f) + -9600.0f) + phi_a1; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } else { + phi_v1_2 = phi_a1 + 11400; + if (phi_v1_2 >= temp_a0) { + temp_a0 = phi_v1_2; + if (phi_v0 < 0) { + phi_v0 /= -2; + } + } + } + } + } else if (idx == 11 || idx == 17) { + if (action == 4) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3E0 = this->unk_3E0; + + phi_v1_2 = (s32)((curFrame / unk_3E0) * -7000.0f) + phi_a1; + if (temp_a0 >= phi_v1_2) { + temp_a0 = phi_v1_2; + if (phi_v0 > 0) { + phi_v0 /= -2; + } + } + } else { + phi_v1_2 = phi_a1 - 7000; + if (temp_a0 >= phi_v1_2) { + temp_a0 = phi_v1_2; + if (phi_v0 > 0) { + phi_v0 /= -2; + } + } + } + } else if (action == 5) { + if (skelAnime->mode == 2) { + f32 curFrame = skelAnime->curFrame; + f32 unk_3E4 = this->unk_3E4; + + phi_v1_2 = (s32)(((unk_3E4 - curFrame) / unk_3E4) * -7000.0f) + phi_a1; + if (temp_a0 >= phi_v1_2) { + temp_a0 = phi_v1_2; + if (phi_v0 > 0) { + phi_v0 /= -2; + } + } + } + } + } + this->unk_25C[idx] = phi_v0; + this->unk_28C[idx] = temp_a0 - z; + } + this->unk_2BC[idx] = z; +} + +void func_80B54360(EnZl3* this, s16 arg1, s32 arg2) { + if (this->unk_2FC != 0) { + s32 temp_v1 = this->unk_28C[arg2] - arg1; + s16 temp_t0 = temp_v1; + s32 temp_t2 = temp_t0; + s32 temp_t3 = this->unk_25C[arg2]; + s32 phi_v0 = temp_t3; + s32 index25C; + s32 phi_t5; + + temp_t3 = arg1 - this->unk_2BC[arg2]; + + if (arg2 == 1) { + index25C = 0; + phi_t5 = this->unk_25C[index25C]; + } else if (arg2 == 4) { + index25C = 3; + phi_t5 = this->unk_25C[index25C]; + } else if (arg2 == 7) { + index25C = 6; + phi_t5 = this->unk_25C[index25C]; + } else if (arg2 == 10) { + index25C = 9; + phi_t5 = ABS(this->unk_25C[index25C]); + } else if (arg2 == 13) { + index25C = 12; + phi_t5 = ABS(this->unk_25C[index25C]); + } else if (arg2 == 16) { + index25C = 15; + phi_t5 = -ABS(this->unk_25C[index25C]); + } else { + index25C = 18; + phi_t5 = -ABS(this->unk_25C[index25C]); + } + + if ((s32)fabsf(temp_t3) > 0x8000) { + if (arg1 > 0) { + temp_t3 -= 0x10000; + } else { + temp_t3 += 0x10000; + } + } + if (index25C >= 0) { + temp_t3 += phi_t5 / 3; + } + + if (temp_t3 != 0) { + phi_v0 += (temp_t3 - phi_v0) / 16; + } + if (phi_v0 != 0) { + phi_v0 -= phi_v0 / 10; + } + if (temp_t0 != 0) { + phi_v0 -= temp_t0 / 50; + } + temp_v1 += phi_v0; + if (((this->unk_25C[arg2] * phi_v0) <= 0) && (temp_t2 > -0x64) && (temp_t2 < 0x64)) { + temp_v1 = 0; + phi_v0 = 0; + } + this->unk_25C[arg2] = phi_v0; + this->unk_28C[arg2] = arg1 + temp_v1; + } + this->unk_2BC[arg2] = arg1; +} + +s32 func_80B5458C(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + s32 pad[3]; + EnZl3* this = (EnZl3*)thisx; + s16* unk_28C = this->unk_28C; + Mtx* sp78; + MtxF sp38; + Vec3s sp30; + Vec3s* unk_3F8_unk_08 = &this->unk_3F8.unk_08; + Vec3s* unk_3F8_unk_0E = &this->unk_3F8.unk_0E; + + if (limbIndex == 14) { + sp78 = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx) * 7); + rot->x += unk_3F8_unk_08->y; + rot->z += unk_3F8_unk_08->x; + gSPSegment((*gfx)++, 0x0C, sp78); + + Matrix_Push(); + Matrix_Translate(pos->x, pos->y, pos->z, MTXMODE_APPLY); + Matrix_RotateZYX(rot->x, rot->y, rot->z, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Translate(362.0f, -133.0f, 0.0f, MTXMODE_APPLY); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B53980(this, sp30.y, 0); + func_80B54360(this, sp30.x, 1); + func_80B53B64(this, sp30.z, 2); + } + Matrix_RotateZYX(unk_28C[0] + kREG(31), unk_28C[1] + kREG(32), unk_28C[2] + kREG(33), MTXMODE_APPLY); + Matrix_Translate(-188.0f, -184.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[0], "../z_en_zl3.c", 1490); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B53980(this, sp30.y, 3); + } + Matrix_RotateZYX(unk_28C[3] + kREG(34), unk_28C[4] + kREG(35), unk_28C[5] + kREG(36), MTXMODE_APPLY); + Matrix_Translate(-410.0f, -184.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[1], "../z_en_zl3.c", 1534); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B54360(this, sp30.x, 7); + } + Matrix_RotateZYX(unk_28C[6] + kREG(37), unk_28C[7] + kREG(38), unk_28C[8] + kREG(39), MTXMODE_APPLY); + Matrix_Translate(-1019.0f, -26.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[2], "../z_en_zl3.c", 1554); + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(467.0f, 265.0f, 389.0f, MTXMODE_APPLY); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B53980(this, sp30.y, 9); + func_80B54360(this, sp30.x, 10); + func_80B53B64(this, sp30.z, 11); + } + Matrix_RotateZYX(unk_28C[9] + kREG(40), unk_28C[10] + kREG(41), unk_28C[11] + kREG(42), MTXMODE_APPLY); + Matrix_Translate(-427.0f, -1.0f, -3.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[3], "../z_en_zl3.c", 1579); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B53980(this, sp30.y, 12); + func_80B54360(this, sp30.x, 13); + func_80B53B64(this, sp30.z, 14); + } + Matrix_RotateZYX(unk_28C[12] + kREG(43), unk_28C[13] + kREG(44), unk_28C[14] + kREG(45), MTXMODE_APPLY); + Matrix_Translate(-446.0f, -52.0f, 84.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[4], "../z_en_zl3.c", 1598); + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(467.0f, 265.0f, -389.0f, MTXMODE_APPLY); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B53980(this, sp30.y, 15); + func_80B54360(this, sp30.x, 16); + func_80B53B64(this, sp30.z, 17); + } + Matrix_RotateZYX(unk_28C[15] + kREG(46), unk_28C[16] + kREG(47), unk_28C[17] + kREG(48), MTXMODE_APPLY); + Matrix_Translate(-427.0f, -1.0f, 3.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[5], "../z_en_zl3.c", 1623); + Matrix_Get(&sp38); + Matrix_MtxFToYXZRotS(&sp38, &sp30, 0); + if (!FrameAdvance_IsEnabled(globalCtx)) { + func_80B53980(this, sp30.y, 18); + func_80B54360(this, sp30.x, 19); + func_80B53B64(this, sp30.z, 20); + } + Matrix_RotateZYX(unk_28C[18] + kREG(49), unk_28C[19] + kREG(50), unk_28C[20] + kREG(51), MTXMODE_APPLY); + Matrix_Translate(-446.0f, -52.0f, -84.0f, MTXMODE_APPLY); + Matrix_ToMtx(&sp78[6], "../z_en_zl3.c", 1642); + Matrix_Pop(); + Matrix_Pop(); + this->unk_2FC = 1; + } else if (limbIndex == 7) { + rot->x += unk_3F8_unk_0E->y; + rot->y -= unk_3F8_unk_0E->x; + } + return false; +} + +void EnZl3_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnZl3* this = (EnZl3*)thisx; + s32 pad; + Vec3f sp34; + s32 pad2; + Vec3f sp24; + Vec3f sp18; + + if (limbIndex == 13) { + sp34 = D_80B5A46C; + Matrix_MultVec3f(&sp34, &this->unk_31C); + } else if (limbIndex == 14) { + sp24 = D_80B5A478; + Matrix_MultVec3f(&sp24, &sp18); + this->actor.focus.pos.x = sp18.x; + this->actor.focus.pos.y = sp18.y; + this->actor.focus.pos.z = sp18.z; + this->actor.focus.rot.x = this->actor.world.rot.x; + this->actor.focus.rot.y = this->actor.world.rot.y; + this->actor.focus.rot.z = this->actor.world.rot.z; + } +} + +s32 func_80B54DB4(EnZl3* this) { + s32 params = this->actor.params >> 8; + + return params & 0xFF; +} + +s32 func_80B54DC4(EnZl3* this) { + s32 params = this->actor.params >> 4; + + return params & 0xF; +} + +s32 func_80B54DD4(EnZl3* this) { + s32 params = this->actor.params; + + return params & 0xF; +} + +void func_80B54DE0(EnZl3* this, GlobalContext* globalCtx) { + s32 idx = this->unk_318; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[idx].segment); +} + +void func_80B54E14(EnZl3* this, AnimationHeader* animation, u8 arg2, f32 transitionRate, s32 arg4) { + f32 frameCount = Animation_GetLastFrame(animation); + f32 playbackSpeed; + f32 unk0; + f32 fc; + + if (arg4 == 0) { + unk0 = 0.0f; + fc = frameCount; + playbackSpeed = 1.0f; + } else { + unk0 = frameCount; + fc = 0.0f; + playbackSpeed = -1.0f; + } + + Animation_Change(&this->skelAnime, animation, playbackSpeed, unk0, fc, arg2, transitionRate); +} + +void func_80B54EA4(EnZl3* this, GlobalContext* globalCtx) { + f32 posX = this->actor.world.pos.x; + f32 posY = this->actor.world.pos.y; + f32 posZ = this->actor.world.pos.z; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_EG, posX, posY, posZ, 0, 0, 0, 0); +} + +void func_80B54EF4(EnZl3* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_PAIN); +} + +void func_80B54F18(EnZl3* this, GlobalContext* globalCtx) { + if (this->unk_2F8 == 0) { + f32 posX = this->actor.world.pos.x; + f32 posY = this->actor.world.pos.y + (kREG(5) + -26.0f); + f32 posZ = this->actor.world.pos.z; + + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_WARP1, posX, posY, posZ, 0, 0x4000, + 0, WARP_PURPLE_CRYSTAL); + this->unk_2F8 = 1; + } +} + +void func_80B54FB4(EnZl3* this, GlobalContext* globalCtx) { + osSyncPrintf("ゼルダ姫のEn_Zl3_Actor_inFinal_Init通すよ!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + func_80B54E14(this, &gZelda2Anime2Anim_008AD0, 0, 0.0f, 0); + EnZl3_setEyeIndex(this, 4); + EnZl3_setMouthIndex(this, 2); + this->action = 1; + this->drawConfig = 1; + func_80B54F18(this, globalCtx); + this->actor.shape.rot.z = 0; + this->unk_3C4 = this->actor.world.rot.z; + this->actor.world.rot.z = this->actor.shape.rot.z; + osSyncPrintf("ゼルダ姫のEn_Zl3_Actor_inFinal_Initは通った!!!!!!!!!!!!!!!!!!!!!!!!!\n"); +} + +void func_80B55054(EnZl3* this) { + if (this->unk_328 != 0) { + Actor* child = this->actor.child; + + if (child != NULL) { + f32* temp_v0 = &this->unk_2EC; + + if (*temp_v0 < 19.0f) { + ((DoorWarp1*)child)->crystalAlpha = (20.0f - *temp_v0) * 12.75f; + *temp_v0 += 1.0f; + } else { + Actor_Kill(child); + this->actor.child = NULL; + } + } + } +} + +void func_80B550F0(EnZl3* this) { + Actor* child = this->actor.child; + + if (child != NULL) { + child->world.pos.x = this->actor.world.pos.x; + child->world.pos.y = this->actor.world.pos.y + (kREG(5) + -26.0f); + child->world.pos.z = this->actor.world.pos.z; + } +} + +void func_80B55144(EnZl3* this) { + f32* fl = &D_80B5A484; + + if (1) {} // necessary to match + + if (*fl < 2.0f) { + *fl += 1.0f; + EnZl3_setEyeIndex(this, 2); + } else if (*fl < 4.0f) { + *fl += 1.0f; + EnZl3_setEyeIndex(this, 1); + } else { + EnZl3_UpdateEyes(this); + } +} + +void func_80B551E0(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_008AD0, 0, 0.0f, 0); + this->action = 1; +} + +void func_80B55220(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_0091D8, 2, 0.0f, 0); + this->action = 2; + EnZl3_setMouthIndex(this, 0); +} + +void func_80B55268(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_0091D8, 2, 0.0f, 0); + this->action = 3; +} + +void func_80B552A8(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_0099A0, 0, 0.0f, 0); + } +} + +void func_80B552DC(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_00A598, 2, -8.0f, 0); + func_80B54EF4(this); + EnZl3_setMouthIndex(this, 2); + this->action = 4; + func_80B53468(); +} + +void func_80B55334(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_00AACC, 0, 0.0f, 0); + } +} + +void func_80B55368(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_00A334, 2, -8.0f, 0); + EnZl3_setMouthIndex(this, 0); + this->action = 5; +} + +void func_80B553B4(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, 0.0f, 0); + } +} + +void func_80B553E8(EnZl3* this, GlobalContext* globalCtx) { + func_80B59AD0(this, globalCtx); +} + +void func_80B55408(EnZl3* this) { + Actor* child = this->actor.child; + + if (child != NULL) { + Actor_Kill(child); + } + Actor_Kill(&this->actor); +} + +void func_80B55444(EnZl3* this, GlobalContext* globalCtx) { + s32 temp_v0 = func_80B5396C(this); + + if (temp_v0 >= 0) { + s32 unk_2F0 = this->unk_2F0; + + if (temp_v0 != unk_2F0) { + switch (temp_v0) { + case 0: + func_80B551E0(this); + break; + case 1: + EnZl3_setEyeIndex(this, 3); + func_80B54EF4(this); + break; + case 3: + func_80B55220(this); + break; + case 4: + func_80B55268(this); + break; + case 5: + func_80B552DC(this); + break; + case 6: + func_80B55368(this); + break; + case 7: + func_80B553E8(this, globalCtx); + break; + case 2: + func_80B55408(this); + break; + case 8: + this->unk_328 = 1; + default: + osSyncPrintf("En_Zl3_inFinal_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_2F0 = temp_v0; + } + } +} + +void func_80B55550(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateSkelAnime(this); + func_80B550F0(this); + func_80B55444(this, globalCtx); +} + +void func_80B555A4(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_setEyeIndex(this, 2); + func_80B550F0(this); + func_80B55054(this); + func_80B55444(this, globalCtx); +} + +void func_80B55604(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + func_80B55144(this); + func_80B552A8(this, EnZl3_UpdateSkelAnime(this)); + func_80B55054(this); + func_80B55444(this, globalCtx); +} + +void func_80B5566C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55334(this, EnZl3_UpdateSkelAnime(this)); + func_80B55444(this, globalCtx); +} + +void func_80B556CC(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B553B4(this, EnZl3_UpdateSkelAnime(this)); + func_80B55444(this, globalCtx); +} + +void func_80B5572C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B55444(this, globalCtx); +} + +void func_80B55780(EnZl3* this, GlobalContext* globalCtx) { + osSyncPrintf("ゼルダ姫のEn_Zl3_Actor_inFinal2_Init通すよ!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + func_80B54E14(this, &gZelda2Anime2Anim_005A0C, 0, 0.0f, 0); + this->action = 7; + this->drawConfig = 1; + osSyncPrintf("ゼルダ姫のEn_Zl3_Actor_inFinal2_Initは通った!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + EnZl3_setMouthIndex(this, 1); + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void func_80B55808(EnZl3* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_PAIN); +} + +void func_80B5582C(EnZl3* this) { + Audio_PlaySoundRandom(&D_80B5A488, NA_SE_VO_Z1_CRY_0, NA_SE_VO_Z1_CRY_1 - NA_SE_VO_Z1_CRY_0 + 1); +} + +void func_80B5585C(EnZl3* this) { + SkelAnime* skelAnime = &this->skelAnime; + + if ((skelAnime->mode == 2) && Animation_OnFrame(skelAnime, 4.0f)) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_PAIN); + } +} + +void func_80B558A8(EnZl3* this) { + s32 pad[4]; + s16 thisRotY = this->actor.world.rot.y; + Vec3f* unk_338 = &this->unk_338; + + *unk_338 = this->unk_32C = this->actor.world.pos; + + unk_338->z += (-1.6074f * Math_CosS(thisRotY)) - (3.1620007f * Math_SinS(thisRotY)); + unk_338->x += (-1.6074f * Math_SinS(thisRotY)) + (3.1620007f * Math_CosS(thisRotY)); + unk_338->y += -0.012199402f; +} + +void func_80B559C4(EnZl3* this) { + Vec3f* thisPos = &this->actor.world.pos; + Vec3f* unk_32C = &this->unk_32C; + Vec3f* unk_338 = &this->unk_338; + f32 temp_f0 = Environment_LerpWeightAccelDecel(Animation_GetLastFrame(&gZelda2Anime2Anim_005248), 0, + (s32)this->skelAnime.curFrame, 3, 3); + + thisPos->x = unk_32C->x + (temp_f0 * (unk_338->x - unk_32C->x)); + thisPos->z = unk_32C->z + (temp_f0 * (unk_338->z - unk_32C->z)); +} + +void func_80B55A58(EnZl3* this, GlobalContext* globalCtx) { + if (globalCtx->activeCamera == MAIN_CAM) { + func_80B537E8(this); + } +} + +void func_80B55A84(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_005A0C, 0, 0.0f, 0); + this->action = 7; +} + +void func_80B55AC4(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_00499C, 2, -8.0f, 0); + this->action = 8; +} + +void func_80B55B04(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_004408, 0, 0.0f, 0); + } +} + +void func_80B55B38(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_006508, 2, -8.0f, 0); + this->action = 9; +} + +void func_80B55B78(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_0061C4, 0, 0.0f, 0); + } +} + +void func_80B55BAC(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_005248, 2, -8.0f, 0); + func_80B558A8(this); + func_80B55808(this); + EnZl3_setMouthIndex(this, 2); + this->action = 10; +} + +void func_80B55C0C(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_0054E0, 0, 0.0f, 0); + this->action = 11; +} + +void func_80B55C4C(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B55C0C(this); + } +} + +void func_80B55C70(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_008684, 2, -8.0f, 0); + this->action = 12; + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; +} + +void func_80B55CCC(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_006F04, 0, 0.0f, 0); + } +} + +void func_80B55D00(EnZl3* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->action = 13; + } else if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4300) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.textId = 0x70D5; + func_8002F2F4(&this->actor, globalCtx); + } else { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; + } +} + +void func_80B55DB0(EnZl3* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; + this->action = 12; + } +} + +void func_80B55E08(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_006AB0, 2, -8.0f, 0); + this->action = 14; +} + +void func_80B55E48(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_008050, 0, 0.0f, 0); + } +} + +void func_80B55E7C(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_007A78, 2, -8.0f, 0); + this->action = 15; +} + +void func_80B55EBC(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_007C84, 0, 0.0f, 0); + } +} + +void func_80B55EF0(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_0082F8, 2, -8.0f, 0); + this->action = 16; + EnZl3_setMouthIndex(this, 0); +} + +void func_80B55F38(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_003FF8, 0, 0.0f, 0); + } +} + +void func_80B55F6C(EnZl3* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->action = 0x12; + } else if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4300) { + BossGanon2* bossGanon2 = func_80B53488(this, globalCtx); + + if ((bossGanon2 != NULL) && (bossGanon2->unk_324 <= (10.0f / 81.0f))) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.textId = 0x7059; + func_8002F2F4(&this->actor, globalCtx); + } + } else { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; + } +} + +void func_80B5604C(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_007664, 2, -8.0f, 0); + this->action = 17; + func_80B5582C(this); +} + +void func_80B56090(EnZl3* this, s32 arg1) { + s32* unk_2F0 = &this->unk_2F0; + + if (func_80B5396C(this) == *unk_2F0) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_003FF8, 0, 0.0f, 0); + this->action = 16; + func_80B53974(this, 7); + this->unk_2F0 = 7; + } + } +} + +void func_80B56108(EnZl3* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; + this->action = 16; + } +} + +void func_80B56160(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_0001D8, 0, 0.0f, 0); + this->action = 19; +} + +void func_80B561A0(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_001110, 2, -8.0f, 0); + this->action = 20; +} + +void func_80B561E0(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_0004F4, 0, 0.0f, 0); + } +} + +void func_80B56214(EnZl3* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + this->action = 21; + } else if (ABS((s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y)) <= 0x4300) { + BossGanon2* bossGanon2 = func_80B53488(this, globalCtx); + + if (bossGanon2 != NULL) { + if (bossGanon2->unk_324 <= (10.0f / 81.0f)) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.flags |= ACTOR_FLAG_0; + this->actor.textId = 0x7059; + func_8002F2F4(&this->actor, globalCtx); + } + } + } else { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; + } +} + +void func_80B562F4(EnZl3* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actor.flags &= ~(ACTOR_FLAG_0 | ACTOR_FLAG_3); + this->actor.flags &= ~ACTOR_FLAG_0; + this->action = 20; + } +} + +void func_80B5634C(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_002348, 2, -8.0f, 0); + this->action = 22; +} + +void func_80B5638C(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_00210C, 0, 0.0f, 0); + } +} + +void func_80B563C0(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_002E54, 2, -8.0f, 0); + this->action = 23; +} + +void func_80B56400(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_002710, 0, 0.0f, 0); + } +} + +void func_80B56434(EnZl3* this) { + func_80B54E14(this, &gZelda2Anime2Anim_001D8C, 2, -8.0f, 0); + this->action = 24; +} + +void func_80B56474(EnZl3* this, s32 arg1) { + if (arg1 != 0) { + func_80B54E14(this, &gZelda2Anime2Anim_0014DC, 0, 0.0f, 0); + } +} + +void func_80B564A8(EnZl3* this, GlobalContext* globalCtx) { + s32 temp_v0; + s32* val = &D_80B5A494; + + temp_v0 = func_80B5396C(this); + + if (*val > 0) { + *val -= 1; + } else if (*val == 0) { + *val -= 1; + if (temp_v0 == 8) { + func_80B5604C(this); + } + } + + if (temp_v0 >= 0) { + s32 unk_2F0 = this->unk_2F0; + + if (temp_v0 != unk_2F0) { + switch (temp_v0) { + case 0: + func_80B55A84(this); + break; + case 1: + func_80B55AC4(this); + break; + case 2: + func_80B55B38(this); + break; + case 3: + func_80B55BAC(this); + break; + case 4: + func_80B55C70(this); + break; + case 5: + func_80B55E08(this); + break; + case 6: + func_80B55E7C(this); + break; + case 7: + func_80B55EF0(this); + break; + case 8: + *val = 10; + break; + case 9: + func_80B56160(this); + break; + case 10: + func_80B561A0(this); + break; + case 11: + func_80B5634C(this); + break; + case 12: + func_80B563C0(this); + break; + case 13: + func_80B56434(this); + break; + case 14: + Actor_Kill(&this->actor); + break; + default: + osSyncPrintf("En_Zl3_inFinal2_Check_DemoMode:そんな動作は無い!!!!!!!!\n"); + } + this->unk_2F0 = temp_v0; + } + } +} + +void func_80B56658(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B564A8(this, globalCtx); +} + +void func_80B566AC(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55B04(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); +} + +void func_80B5670C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55B78(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); +} + +void func_80B5676C(EnZl3* this, GlobalContext* globalCtx) { + s32 something; + + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + something = EnZl3_UpdateSkelAnime(this); + func_80B559C4(this); + func_80B55C4C(this, something); +} + +void func_80B567CC(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B564A8(this, globalCtx); +} + +void func_80B5682C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B55A58(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55CCC(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); + func_80B55D00(this, globalCtx); +} + +void func_80B568B4(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B537E8(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B55DB0(this, globalCtx); +} + +void func_80B5691C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55E48(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); +} + +void func_80B5697C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55EBC(this, EnZl3_UpdateSkelAnime(this)); + func_80B5585C(this); + func_80B564A8(this, globalCtx); +} + +void func_80B569E4(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B537E8(this); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55F38(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); + func_80B55F6C(this, globalCtx); +} + +void func_80B56A68(EnZl3* this, GlobalContext* globalCtx) { + s32 something; + + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B537E8(this); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + something = EnZl3_UpdateSkelAnime(this); + func_80B564A8(this, globalCtx); + func_80B56090(this, something); +} + +void func_80B56AE0(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B537E8(this); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B55F38(this, EnZl3_UpdateSkelAnime(this)); + func_80B56108(this, globalCtx); +} + +void func_80B56B54(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B564A8(this, globalCtx); +} + +void func_80B56BA8(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B561E0(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); + func_80B56214(this, globalCtx); +} + +void func_80B56C24(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B562F4(this, globalCtx); +} + +void func_80B56C84(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B5638C(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); +} + +void func_80B56CE4(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B56400(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); +} + +void func_80B56D44(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B56474(this, EnZl3_UpdateSkelAnime(this)); + func_80B564A8(this, globalCtx); +} + +void func_80B56DA4(EnZl3* this) { + func_800788CC(NA_SE_EV_ZELDA_POWER); +} + +void func_80B56DC8(EnZl3* this) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_PAIN); +} + +void func_80B56DEC(EnZl3* this) { + SkelAnime* skelAnime = &this->skelAnime; + + if ((skelAnime->mode == 2) && Animation_OnFrame(skelAnime, 9.0f) != 0) { + func_80078914(&this->actor.projectedPos, NA_SE_VO_Z1_OPENDOOR); + } +} + +void func_80B56E38(EnZl3* this, GlobalContext* globalCtx) { + s32 pad[2]; + s32 sfxId; + SkelAnime* sp20 = &this->skelAnime; + + if ((Animation_OnFrame(sp20, 6.0f) || Animation_OnFrame(sp20, 0.0f)) && (this->actor.bgCheckFlags & 1)) { + sfxId = 0x800; + sfxId += SurfaceType_GetSfx(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId); + func_80078914(&this->actor.projectedPos, sfxId); + } +} + +void func_80B56EB8(EnZl3* this, GlobalContext* globalCtx) { + Flags_SetSwitch(globalCtx, func_80B54DB4(this)); +} + +s32 func_80B56EE4(EnZl3* this, GlobalContext* globalCtx) { + return Flags_GetSwitch(globalCtx, func_80B54DB4(this)); +} + +void func_80B56F10(EnZl3* this, GlobalContext* globalCtx) { + s32 waypoint; + Path* pathHead = globalCtx->setupPathList; + + if (pathHead != NULL) { + waypoint = func_80B54DC4(this); + pathHead += waypoint; + this->unk_30C = pathHead; + this->unk_310 = pathHead->count; + osSyncPrintf("En_Zl3_Get_path_info レールデータをゲットだぜ = %d!!!!!!!!!!!!!!\n", waypoint); + } else { + osSyncPrintf("En_Zl3_Get_path_info レールデータが無い!!!!!!!!!!!!!!!!!!!!\n"); + } +} + +s32 func_80B56F8C(EnZl3* this, s32 arg1) { + s32 unk_310 = this->unk_310; + + if (unk_310 > arg1) { + return 1; + } + return 0; +} + +Vec3s* func_80B56FAC(EnZl3* this, s32 arg1) { + Vec3s* point; + Path* pathList = this->unk_30C; + + if ((pathList != NULL) && func_80B56F8C(this, arg1)) { + point = &((Vec3s*)SEGMENTED_TO_VIRTUAL(pathList->points))[arg1]; + return point; + } + return NULL; +} + +s32 func_80B57034(EnZl3* this, s32 arg1, s32 arg2) { + Vec3s* vec1 = func_80B56FAC(this, arg1); + Vec3s* vec2 = func_80B56FAC(this, arg2); + + if ((vec2 != NULL) && (vec1 != NULL)) { + f32 xDiff = vec2->x - vec1->x; + f32 zDiff = vec2->z - vec1->z; + + return ((xDiff == 0.0f) && (zDiff == 0.0f)) ? 0 : (s16)(Math_FAtan2F(xDiff, zDiff) * (0x8000 / M_PI)); + } + return 0; +} + +s16 func_80B57104(EnZl3* this, s32 arg1) { + Vec3s* point = func_80B56FAC(this, arg1); + + if (point != NULL) { + f32 xDiff = point->x - this->actor.world.pos.x; + f32 zDiff = point->z - this->actor.world.pos.z; + + if ((xDiff != 0.0f) || (zDiff != 0.0f)) { + return Math_FAtan2F(xDiff, zDiff) * (0x8000 / M_PI); + } + } + return 0; +} + +s32 func_80B571A8(EnZl3* this) { + s32 pad; + s32 unk_314 = this->unk_314; + s32 pad2; + + if (func_80B56F8C(this, unk_314 + 1) == 0) { + return this->actor.shape.rot.y; + } else { + return func_80B57034(this, unk_314, unk_314 + 1); + } +} + +s32 func_80B571FC(EnZl3* this) { + s32 pad; + s32 unk_314 = this->unk_314; + + if (func_80B56F8C(this, unk_314) == 0) { + return this->actor.shape.rot.y; + } else { + return func_80B57104(this, unk_314); + } +} + +void func_80B57240(EnZl3* this) { + s32 temp_a1 = func_80B571FC(this); + s16* rotY = &this->actor.world.rot.y; + + Math_SmoothStepToS(rotY, temp_a1, 2, 6400, 1000); + this->actor.shape.rot.y = *rotY; +} + +void func_80B57298(EnZl3* this) { + s16* rotY = &this->actor.world.rot.y; + s16 temp_a1 = func_80B571A8(this); + + Math_SmoothStepToS(rotY, temp_a1, 2, 6400, 1000); + this->actor.shape.rot.y = *rotY; +} + +u16 func_80B572F0(GlobalContext* globalCtx) { + s16 sceneNum = globalCtx->sceneNum; + u16 ret; + + if (sceneNum == SCENE_GANON_SONOGO) { + ret = 0x71A8; + } else if (sceneNum == SCENE_GANON_FINAL) { + ret = 0x71A9; + } else { + ret = 0x71AB; + } + return ret; +} + +s32 func_80B57324(EnZl3* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + return 1; + } + return 0; +} + +void func_80B57350(EnZl3* this, GlobalContext* globalCtx) { + s16 temp_v0 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + + if (ABS(temp_v0) <= 0x4300) { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->actor.textId = func_80B572F0(globalCtx); + func_8002F2F4(&this->actor, globalCtx); + } +} + +s32 func_80B573C8(EnZl3* this, GlobalContext* globalCtx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + return 1; + } + return 0; +} + +s32 func_80B573FC(EnZl3* this, GlobalContext* globalCtx, f32 arg2) { + Player* player = GET_PLAYER(globalCtx); + f32 playerX = player->actor.world.pos.x; + f32 playerZ = player->actor.world.pos.z; + f32 thisX = this->actor.world.pos.x; + f32 thisZ = this->actor.world.pos.z; + + if (SQ(playerX - thisX) + SQ(playerZ - thisZ) < SQ(arg2)) { + return 1; + } + return 0; +} + +s32 func_80B57458(EnZl3* this, GlobalContext* globalCtx) { + Vec3f* thisPos = &this->actor.world.pos; + f32 thisX = thisPos->x; + f32 thisZ = thisPos->z; + Player* player = GET_PLAYER(globalCtx); + Vec3f* playerPos = &player->actor.world.pos; + s32 pad; + f32 playerX = playerPos->x; + f32 playerZ = playerPos->z; + f32 temp_f12 = playerX - thisX; + f32 temp_f13 = playerZ - thisZ; + s16 temp_v0; + s16 temp_v1 = func_80B571A8(this); + + if (temp_f12 == 0.0f && temp_f13 == 0.0f) { + return 1; + } + + temp_v0 = (s16)(temp_v1 - (s16)(Math_FAtan2F(temp_f12, temp_f13) * (0x8000 / M_PI))); + + if (temp_v0 < 0x1555) { + return 1; + } else if ((temp_v0 < 0x4000) && func_80B573FC(this, globalCtx, 150.0f)) { + return 1; + } else { + return 0; + } +} + +s32 func_80B57564(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B573FC(this, globalCtx, 50.0f) || func_80B57458(this, globalCtx)) { + return 1; + } + return 0; +} + +s32 func_80B575B0(EnZl3* this, GlobalContext* globalCtx) { + return func_80B573FC(this, globalCtx, 150.0f); +} + +s32 func_80B575D0(EnZl3* this, GlobalContext* globalCtx) { + return func_80B573FC(this, globalCtx, 50.0f); +} + +s32 func_80B575F0(EnZl3* this, GlobalContext* globalCtx) { + s16 sceneNum = globalCtx->sceneNum; + + if ((sceneNum == SCENE_GANON_SONOGO) && (func_80B54DB4(this) == 0x26)) { + s32 unk_314 = this->unk_314; + + if (unk_314 == 1) { + return 1; + } + } + return 0; +} + +void func_80B5764C(EnZl3* this, GlobalContext* globalCtx) { + s16 sceneNum = globalCtx->sceneNum; + + if ((sceneNum == SCENE_GANON_SONOGO) && (func_80B54DB4(this) == 0x26)) { + s32 unk_314 = this->unk_314 + 1; + + if ((unk_314 == 1) && !Gameplay_InCsMode(globalCtx)) { + OnePointCutscene_Init(globalCtx, 1000, 40, &this->actor, MAIN_CAM); + } + } +} + +s32 func_80B576C8(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B575F0(this, globalCtx) && (this->unk_3D8 == 0)) { + return 1; + } + return 0; +} + +void func_80B57704(EnZl3* this, GlobalContext* globalCtx) { + s32 unk_3C4 = this->unk_3C4; + + Flags_SetSwitch(globalCtx, unk_3C4); +} + +void func_80B5772C(EnZl3* this, GlobalContext* globalCtx) { + s32 unk_3C4 = this->unk_3C4; + + Flags_UnsetSwitch(globalCtx, unk_3C4); +} + +void func_80B57754(EnZl3* this, GlobalContext* globalCtx) { + if (gSaveContext.unk_13F0 == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_OCEFF_WIPE4, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 1); + func_80B56DA4(this); + } +} + +void func_80B577BC(GlobalContext* globalCtx, Vec3f* vec) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + Vec3f* playerPos = &player->actor.world.pos; + f32 posX = vec->x; + f32 posY = vec->y; + f32 posZ = vec->z; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_TEST, posX, posY, posZ, 0, + (Math_FAtan2F(playerPos->x - posX, playerPos->z - posZ) * (0x8000 / M_PI)), 0, 5); +} + +void func_80B57858(GlobalContext* globalCtx) { + func_80B577BC(globalCtx, &D_80B5A498); + func_80B577BC(globalCtx, &D_80B5A4A4); +} + +s32 func_80B57890(EnZl3* this, GlobalContext* globalCtx) { + s8 pad[2]; + u8 curSpawn = globalCtx->curSpawn; + s16 sceneNum = globalCtx->sceneNum; + s32 result = func_80B54DB4(this); + + if (globalCtx) {} // Needed to match, this if can be almost anywhere and it still matches + + if (sceneNum == SCENE_GANON_SONOGO) { + if ((result == 0x24) && (curSpawn == 0)) { + return 1; + } + if ((result == 0x25) && (curSpawn == 2)) { + return 1; + } + if ((result == 0x26) && (curSpawn == 4)) { + return 1; + } + if ((result == 0x27) && (curSpawn == 6)) { + return 1; + } + if ((result == 0x28) && (curSpawn == 6)) { + return 1; + } + } else if (sceneNum == SCENE_GANON_FINAL) { + if ((result == 0x20) && (curSpawn == 0) && Flags_GetSwitch(globalCtx, 0x37)) { + if ((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO)) { + return 1; + } + } + if ((result == 0x21) && (curSpawn == 2)) { + return 1; + } + if ((result == 0x22) && (curSpawn == 4)) { + return 1; + } + if ((result == 0x23) && (curSpawn == 6)) { + return 1; + } + } else if (sceneNum == SCENE_GANONTIKA_SONOGO) { + if ((result == 0x29) && (curSpawn == 0)) { + return 1; + } + if ((result == 0x2A) && (curSpawn == 0)) { + return 1; + } + } + return 0; +} + +void func_80B57A74(GlobalContext* globalCtx) { + Actor* actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_PROP].head; + //! @bug checks for encount2 in ACTORCAT_PROP but encount2 is in ACTORCAT_ENEMY so this condition is never met + while (actorIt != NULL) { + if (actorIt->id == ACTOR_EN_ENCOUNT2) { + ((EnEncount2*)actorIt)->collapseSpawnerInactive = true; + } + actorIt = actorIt->next; + } +} + +void func_80B57AAC(EnZl3* this, s32 arg1, AnimationHeader* arg2) { + if (arg1 != 0) { + func_80B54E14(this, arg2, 0, -8.0f, 0); + } +} + +void func_80B57AE0(EnZl3* this, GlobalContext* globalCtx) { + s32 pad; + s16 shapeRotY = this->actor.shape.rot.y; + s32 pad2; + Vec3f* unk_354 = &this->unk_354; + Vec3f* unk_348 = &this->unk_348; + Vec3s* temp_v0; + f32 xDiff; + f32 zDiff; + + this->unk_344 = 0; + this->unk_314 += 1; + this->unk_360 = 0.0f; + this->unk_364 = 0.0f; + this->unk_368 = 0.0f; + *unk_348 = this->actor.world.pos; + temp_v0 = func_80B56FAC(this, this->unk_314); + + if (temp_v0 != NULL) { + unk_354->x = temp_v0->x; + unk_354->y = temp_v0->y; + unk_354->z = temp_v0->z; + } else { + unk_354->x = unk_348->x + (Math_SinS(shapeRotY) * 200.0f); + unk_354->y = unk_348->y; + unk_354->z = unk_348->z + (Math_CosS(shapeRotY) * 200.0f); + } + + xDiff = unk_354->x - unk_348->x; + zDiff = unk_354->z - unk_348->z; + this->unk_346 = (s32)(sqrtf(SQ(xDiff) + SQ(zDiff)) / (kREG(6) + 8.0f)); +} + +s32 func_80B57C54(EnZl3* this) { + if (this->unk_344 >= this->unk_346) { + return 1; + } + return 0; +} + +s32 func_80B57C7C(EnZl3* this, GlobalContext* globalCtx) { + return 1; +} + +s32 func_80B57C8C(EnZl3* this) { + return !func_80B56F8C(this, this->unk_314 + 2); +} + +void func_80B57CB4(EnZl3* this, GlobalContext* globalCtx) { + Vec3f* unk_348 = &this->unk_348; + Vec3f* unk_354 = &this->unk_354; + Vec3f* thisPos = &this->actor.world.pos; + f32 temp_f0; + + this->unk_344 += 1; + temp_f0 = Environment_LerpWeightAccelDecel(this->unk_346, 0, this->unk_344, 3, 3); + thisPos->x = unk_348->x + (temp_f0 * (unk_354->x - unk_348->x)); + thisPos->y = (unk_348->y + (temp_f0 * (unk_354->y - unk_348->y))) + this->unk_360; + thisPos->z = unk_348->z + (temp_f0 * (unk_354->z - unk_348->z)); +} + +void func_80B57D60(EnZl3* this, GlobalContext* globalCtx) { + func_80B57240(this); +} + +s32 func_80B57D80(EnZl3* this, GlobalContext* globalCtx) { + s32 pad; + s16* sp32 = &this->actor.shape.rot.y; + struct_80034A14_arg1* unk_3F8 = &this->unk_3F8; + Player* player = GET_PLAYER(globalCtx); + s32 unk_314 = this->unk_314; + s16 temp_v0 = func_80B57104(this, unk_314); + s32 pad2; + s16 phi_v1; + + unk_3F8->unk_18.y = player->actor.world.pos.y; + unk_3F8->unk_18.x = (Math_SinS(temp_v0) * this->actor.xzDistToPlayer) + this->actor.world.pos.x; + unk_3F8->unk_18.z = (Math_CosS(temp_v0) * this->actor.xzDistToPlayer) + this->actor.world.pos.z; + unk_3F8->unk_14 = kREG(16) - 16.0f; + func_80034A14(&this->actor, unk_3F8, kREG(17) + 0xC, 4); + + phi_v1 = ABS(temp_v0 - *sp32); + if (phi_v1 <= 0x320) { + *sp32 = temp_v0; + this->actor.world.rot.y = *sp32; + phi_v1 = 0; + } + this->actor.world.rot.y = *sp32; + return phi_v1; +} + +void func_80B57EAC(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B57324(this, globalCtx)) { + this->action = 26; + } else { + func_80B57350(this, globalCtx); + } +} + +void func_80B57EEC(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B573C8(this, globalCtx)) { + this->action = 27; + } +} + +void func_80B57F1C(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B57D80(this, globalCtx) == 0) { + func_80B54E14(this, &gZelda2Anime2Anim_009BE4, 0, -8.0f, 0); + this->action = 34; + this->unk_314 -= 1; + func_80B57AE0(this, globalCtx); + } +} + +s32 func_80B57F84(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B575D0(this, globalCtx) && func_80B57C7C(this, globalCtx) && !Gameplay_InCsMode(globalCtx)) { + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, -8.0f, 0); + this->action = 36; + this->unk_2EC = 0.0f; + func_80B57A74(globalCtx); + return 1; + } + return 0; +} + +void func_80B58014(EnZl3* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s8 invincibilityTimer = player->invincibilityTimer; + + if (func_80B57324(this, globalCtx)) { + func_80B54E14(this, &gZelda2Anime2Anim_003FF8, 0, -11.0f, 0); + this->action = 29; + func_80B538B0(this); + } else if (func_80B57C8C(this) && func_80B57F84(this, globalCtx)) { + OnePointCutscene_Init(globalCtx, 4000, -99, &this->actor, MAIN_CAM); + this->unk_3D0 = 0; + } else if (func_80B576C8(this, globalCtx) && func_80B575B0(this, globalCtx) && !Gameplay_InCsMode(globalCtx)) { + this->action = 0x1F; + this->unk_3CC = 0.0f; + func_80B537E8(this); + this->unk_3D8 = 1; + OnePointCutscene_Init(globalCtx, 4010, -99, &this->actor, MAIN_CAM); + } else if (!func_80B57C8C(this) && !func_80B576C8(this, globalCtx) && func_80B57564(this, globalCtx)) { + func_80B54E14(this, &gZelda2Anime2Anim_009BE4, 0, -8.0f, 0); + func_80B5764C(this, globalCtx); + this->action = 34; + this->unk_3D0 = 0; + func_80B57AE0(this, globalCtx); + } else if ((invincibilityTimer > 0) || (player->fallDistance >= 0x33)) { + func_80B54E14(this, &gZelda2Anime2Anim_007664, 0, -11.0f, 0); + this->action = 30; + func_80B537E8(this); + func_80B56DC8(this); + } else { + func_80B57350(this, globalCtx); + func_80B538B0(this); + } +} + +void func_80B58214(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B573C8(this, globalCtx)) { + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, -11.0f, 0); + this->action = 28; + this->unk_3D0 = 0; + } +} + +void func_80B58268(EnZl3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s8 invincibilityTimer = player->invincibilityTimer; + + if ((invincibilityTimer <= 0) && (player->fallDistance <= 50)) { + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, -11.0f, 0); + this->action = 28; + this->unk_3D0 = 0; + } +} + +void func_80B582C8(EnZl3* this, GlobalContext* globalCtx) { + f32* unk_3CC = &this->unk_3CC; + s32 pad; + + if (*unk_3CC == kREG(14) + 10.0f) { + *unk_3CC += 1.0f; + func_80B54E14(this, &gZelda2Anime2Anim_008050, 0, -12.0f, 0); + func_80B57704(this, globalCtx); + } else if (*unk_3CC == kREG(15) + 20.0f) { + *unk_3CC += 1.0f; + func_80B56DC8(this); + func_80B54E14(this, &gZelda2Anime2Anim_003FF8, 0, -12.0f, 0); + } else if (*unk_3CC == kREG(16) + 30.0f) { + *unk_3CC += 1.0f; + func_80B57858(globalCtx); + } else if (*unk_3CC == kREG(17) + 40.0f) { + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + *unk_3CC += 1.0f; + } else if (*unk_3CC >= ((kREG(17) + 40.0f) + 1.0f)) { + this->action = 32; + *unk_3CC = 0.0f; + } else { + *unk_3CC += 1.0f; + } +} + +void func_80B584B4(EnZl3* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + s8 invincibilityTimer = player->invincibilityTimer; + Actor* nearbyEnTest = Actor_FindNearby(globalCtx, &this->actor, ACTOR_EN_TEST, ACTORCAT_ENEMY, 8000.0f); + + if (D_80B5A4BC == 0) { + if ((nearbyEnTest == NULL) && (!Gameplay_InCsMode(globalCtx))) { + this->action = 33; + OnePointCutscene_Init(globalCtx, 4011, -99, &this->actor, MAIN_CAM); + } else if (invincibilityTimer > 0) { + func_80B54E14(this, &gZelda2Anime2Anim_003FF8, 0, -12.0f, 0); + D_80B5A4BC = 1; + func_80B56DC8(this); + } + } else { + if ((nearbyEnTest == NULL) && (!Gameplay_InCsMode(globalCtx))) { + func_80B54E14(this, &gZelda2Anime2Anim_007664, 0, -12.0f, 0); + D_80B5A4BC = 0; + this->action = 33; + OnePointCutscene_Init(globalCtx, 4011, -99, &this->actor, MAIN_CAM); + } else if (invincibilityTimer <= 0) { + func_80B54E14(this, &gZelda2Anime2Anim_007664, 0, -12.0f, 0); + D_80B5A4BC = 0; + } + } +} + +void func_80B58624(EnZl3* this, GlobalContext* globalCtx) { + s32 pad[4]; + f32* unk_3CC = &this->unk_3CC; + + if (*unk_3CC == (kREG(18) + 10.0f)) { + *unk_3CC += 1.0f; + func_80B54E14(this, &gZelda2Anime2Anim_008050, 0, -12.0f, 0); + func_80B5772C(this, globalCtx); + } else if (*unk_3CC == kREG(19) + 20.0f) { + *unk_3CC += 1.0f; + this->actor.textId = 0x71AC; + Message_StartTextbox(globalCtx, this->actor.textId, NULL); + func_80B54E14(this, &gZelda2Anime2Anim_003FF8, 0, -12.0f, 0); + } else if (*unk_3CC == ((kREG(19) + 20.0f) + 1.0f)) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + *unk_3CC += 1.0f; + func_80B5357C(this, globalCtx); + func_80B5357C(this, globalCtx); + func_80B5357C(this, globalCtx); + func_80B5357C(this, globalCtx); + func_80B5357C(this, globalCtx); + } + } else { + if (*unk_3CC >= kREG(20) + 30.0f) { + this->action = 28; + func_8005B1A4(GET_ACTIVE_CAM(globalCtx)); + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, -12.0f, 0); + *unk_3CC = 0.0f; + } else { + *unk_3CC += 1.0f; + } + } +} + +void func_80B5884C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54E14(this, &gZelda2Anime2Anim_0038C0, 2, -8.0f, 0); + this->action = 37; + this->unk_36C = 1; +} + +void func_80B58898(EnZl3* this, GlobalContext* globalCtx) { + func_80B54E14(this, &gZelda2Anime2Anim_0038C0, 2, -8.0f, 1); + this->action = 38; + this->unk_374 = 1; +} + +void func_80B588E8(EnZl3* this, GlobalContext* globalCtx) { + func_80B54E14(this, &gZelda2Anime2Anim_009BE4, 0, -8.0f, 0); + func_80B57AE0(this, globalCtx); + this->action = 39; +} + +s32 func_80B58938(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B57C54(this)) { + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, -8.0f, 0); + this->action = 28; + this->unk_3D0 = 0; + return 1; + } + return 0; +} + +s32 func_80B5899C(EnZl3* this, GlobalContext* globalCtx) { + if ((this->actor.bgCheckFlags & 1)) { + Player* player = GET_PLAYER(globalCtx); + s8 invincibilityTimer = player->invincibilityTimer; + + if ((invincibilityTimer > 0) || (player->fallDistance >= 0x33)) { + func_80B54E14(this, &gZelda2Anime2Anim_007664, 2, -11.0f, 0); + this->action = 35; + func_80B56DC8(this); + return 1; + } + } + return 0; +} + +void func_80B58A1C(EnZl3* this, GlobalContext* globalCtx) { + if (!func_80B58938(this, globalCtx)) { + func_80B5899C(this, globalCtx); + } +} + +void func_80B58A50(EnZl3* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s8 invincibilityTimer = player->invincibilityTimer; + + if ((invincibilityTimer <= 0) && (player->fallDistance <= 50)) { + func_80B54E14(this, &gZelda2Anime2Anim_009BE4, 0, -11.0f, 0); + this->action = 34; + } +} + +void func_80B58AAC(EnZl3* this, GlobalContext* globalCtx) { + f32* unk_2EC = &this->unk_2EC; + + *unk_2EC += 1.0f; + if ((*unk_2EC >= kREG(7) + 24.0f) && (this->unk_36C == 0)) { + func_80B57754(this, globalCtx); + func_80B5884C(this, globalCtx); + } else if ((*unk_2EC >= kREG(8) + 50.0f) && (this->unk_370 == 0)) { + func_80B56EB8(this, globalCtx); + this->unk_370 = 1; + } else if ((*unk_2EC >= kREG(9) + 56.0f) && (this->unk_374 == 0)) { + func_80B58898(this, globalCtx); + } else if (*unk_2EC >= kREG(10) + 82.0f) { + func_80B588E8(this, globalCtx); + } +} + +void func_80B58C08(EnZl3* this, GlobalContext* globalCtx) { + s32 pad[2]; + Vec3f* unk_348 = &this->unk_348; + Vec3f* unk_354 = &this->unk_354; + Vec3f* thisPos = &this->actor.world.pos; + s32 unk_344; + s32 unk_346; + s32 sp28; + f32 temp_f0; + + this->unk_344 += 1; + + unk_344 = this->unk_344; + unk_346 = this->unk_346; + sp28 = unk_346 - kREG(11) - 2; + temp_f0 = Environment_LerpWeightAccelDecel(unk_346, 0, unk_344, 3, 0); + + thisPos->x = unk_348->x + (temp_f0 * (unk_354->x - unk_348->x)); + thisPos->y = (unk_348->y + (temp_f0 * (unk_354->y - unk_348->y))) + this->unk_360; + thisPos->z = unk_348->z + (temp_f0 * (unk_354->z - unk_348->z)); + + if ((unk_344 - sp28) >= 0) { + this->alpha = ((f32)(unk_346 - unk_344) / (kREG(11) + 2)) * 255.0f; + this->drawConfig = 2; + } + + if ((unk_346 - unk_344) <= 0) { + Actor_Kill(&this->actor); + } +} + +void func_80B58D50(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B57EAC(this, globalCtx); +} + +void func_80B58DB0(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B57EEC(this, globalCtx); +} + +void func_80B58E10(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + Actor_SetFocus(&this->actor, 60.0f); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B57F1C(this, globalCtx); +} + +void func_80B58E7C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B53764(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58014(this, globalCtx); + func_80B536B4(this); +} + +void func_80B58EF4(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B538B0(this); + func_80B53764(this, globalCtx); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58214(this, globalCtx); +} + +void func_80B58F6C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B537E8(this); + func_80B536C4(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58268(this, globalCtx); +} + +void func_80B58FDC(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B537E8(this); + func_80B536C4(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B582C8(this, globalCtx); +} + +void func_80B5904C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B537E8(this); + func_80B536C4(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B584B4(this, globalCtx); +} + +void func_80B590BC(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B537E8(this); + func_80B536C4(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58624(this, globalCtx); +} + +void func_80B5912C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B536C4(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + func_80B56E38(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B57CB4(this, globalCtx); + func_80B57D60(this, globalCtx); + func_80B58A1C(this, globalCtx); +} + +void func_80B591BC(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B536C4(this); + func_80B538B0(this); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58A50(this, globalCtx); +} + +void func_80B5922C(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B536C4(this); + func_80B57298(this); + Actor_SetFocus(&this->actor, 60.0f); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58AAC(this, globalCtx); +} + +void func_80B592A8(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B536C4(this); + func_80B57298(this); + Actor_SetFocus(&this->actor, 60.0f); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B57AAC(this, EnZl3_UpdateSkelAnime(this), &gZelda2Anime2Anim_003D20); + func_80B56DEC(this); + func_80B58AAC(this, globalCtx); +} + +void func_80B59340(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B536C4(this); + func_80B57298(this); + Actor_SetFocus(&this->actor, 60.0f); + func_80B533FC(this, globalCtx); + func_80B5366C(this, globalCtx); + EnZl3_UpdateEyes(this); + func_80B57AAC(this, EnZl3_UpdateSkelAnime(this), &gZelda2Anime2Anim_009FBC); + func_80B58AAC(this, globalCtx); +} + +void func_80B593D0(EnZl3* this, GlobalContext* globalCtx) { + func_80B54DE0(this, globalCtx); + func_80B536C4(this); + func_80B57298(this); + func_80B5366C(this, globalCtx); + func_80B56E38(this, globalCtx); + Actor_SetFocus(&this->actor, 60.0f); + EnZl3_UpdateEyes(this); + EnZl3_UpdateSkelAnime(this); + func_80B58C08(this, globalCtx); +} + +s32 func_80B5944C(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + if (limbIndex == 14) { + Mtx* mtx = Graph_Alloc(globalCtx->state.gfxCtx, sizeof(Mtx) * 7); + EnZl3* this = (EnZl3*)thisx; + Vec3s* vec = &this->unk_3F8.unk_08; + + gSPSegment(gfx[0]++, 0x0C, mtx); + + rot->x += vec->y; + rot->z += vec->x; + Matrix_Push(); + Matrix_Translate(pos->x, pos->y, pos->z, MTXMODE_APPLY); + Matrix_RotateZYX(rot->x, rot->y, rot->z, MTXMODE_APPLY); + Matrix_Push(); + Matrix_Translate(174.0f, -317.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[0], "../z_en_zl3_inEscape.c", 2471); + Matrix_Translate(-410.0f, -184.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[1], "../z_en_zl3_inEscape.c", 2474); + Matrix_Translate(-1019.0f, -26.0f, 0.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[2], "../z_en_zl3_inEscape.c", 2477); + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(40.0f, 264.0f, 386.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[3], "../z_en_zl3_inEscape.c", 2483); + Matrix_Translate(-446.0f, -52.0f, 84.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[4], "../z_en_zl3_inEscape.c", 2486); + Matrix_Pop(); + Matrix_Push(); + Matrix_Translate(40.0f, 264.0f, -386.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[5], "../z_en_zl3_inEscape.c", 2492); + Matrix_Translate(-446.0f, -52.0f, -84.0f, MTXMODE_APPLY); + Matrix_ToMtx(&mtx[6], "../z_en_zl3_inEscape.c", 2495); + Matrix_Pop(); + Matrix_Pop(); + } + return false; +} + +s32 func_80B59698(EnZl3* this, GlobalContext* globalCtx) { + s32 cond = Flags_GetSwitch(globalCtx, 0x37) && + ((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO)); + + if (cond) { + u8 curSpawn = globalCtx->curSpawn; + + if ((func_80B54DB4(this) == 0x20) && (curSpawn == 0) && + ((gSaveContext.timer2Value <= 0) || (gSaveContext.timer2State == 0))) { + return 1; + } + } + return 0; +} + +s32 func_80B59768(EnZl3* this, GlobalContext* globalCtx) { + s32 cond = Flags_GetSwitch(globalCtx, 0x37) && + ((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO)); + + if (cond) { + u8 curSpawn = globalCtx->curSpawn; + + if ((func_80B54DB4(this) == 0x20) && (curSpawn == 0) && (gSaveContext.timer2Value <= 0)) { + return 1; + } + } + return 0; +} + +void func_80B59828(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B59698(this, globalCtx) || (!func_80B56EE4(this, globalCtx) && func_80B57890(this, globalCtx))) { + s16 newRotY; + + func_80B54E14(this, &gZelda2Anime2Anim_009FBC, 0, 0.0f, 0); + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + func_80B56F10(this, globalCtx); + newRotY = func_80B571A8(this); + this->actor.shape.rot.y = newRotY; + this->actor.world.rot.y = newRotY; + this->unk_3C4 = this->actor.world.rot.z; + this->actor.shape.rot.z = 0; + this->actor.world.rot.z = this->actor.shape.rot.z; + this->action = 28; + this->drawConfig = 1; + } else { + Actor_Kill(&this->actor); + } + + if (func_80B59698(this, globalCtx) != 0) { + func_80088AA0(180); + func_80B53468(); + gSaveContext.healthAccumulator = 320; + Magic_Fill(globalCtx); + if (Flags_GetSwitch(globalCtx, 0x20)) { + Flags_UnsetSwitch(globalCtx, 0x20); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_BG_ZG, -144.0f, 3544.0f, -43.0f, 0, 0x2000, 0, 0x2000); + } + Flags_UnsetSwitch(globalCtx, 0x21); + Flags_UnsetSwitch(globalCtx, 0x22); + Flags_UnsetSwitch(globalCtx, 0x23); + Flags_UnsetSwitch(globalCtx, 0x24); + Flags_UnsetSwitch(globalCtx, 0x25); + Flags_UnsetSwitch(globalCtx, 0x26); + Flags_UnsetSwitch(globalCtx, 0x27); + Flags_UnsetSwitch(globalCtx, 0x28); + Flags_UnsetSwitch(globalCtx, 0x29); + Flags_UnsetSwitch(globalCtx, 0x2A); + } + + if (func_80B54DB4(this) == 0x20) { + s32 cond; + + func_80B54EA4(this, globalCtx); + cond = Flags_GetSwitch(globalCtx, 0x37) && + ((globalCtx->sceneNum == SCENE_GANON_DEMO) || (globalCtx->sceneNum == SCENE_GANON_FINAL) || + (globalCtx->sceneNum == SCENE_GANON_SONOGO) || (globalCtx->sceneNum == SCENE_GANONTIKA_SONOGO)); + if (cond) { + func_80B53614(this, globalCtx); + } + } +} + +void func_80B59A80(EnZl3* this, GlobalContext* globalCtx) { + if (func_80B59768(this, globalCtx)) { + Audio_PlaySoundGeneral(NA_SE_OC_REVENGE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +void func_80B59AD0(EnZl3* this, GlobalContext* globalCtx) { + // todo look into + Actor* thisx = &this->actor; // unused, necessary to use 'this' first to fix regalloc + + Flags_SetSwitch(globalCtx, 0x36); + func_80088AA0(180); + func_80B54EA4(this, globalCtx); + func_80B53614(this, globalCtx); + gSaveContext.eventChkInf[12] &= ~0x80; + func_80B56F10(this, globalCtx); + gSaveContext.healthAccumulator = 320; + Magic_Fill(globalCtx); + this->action = 27; + this->drawConfig = 1; +} + +void func_80B59B6C(EnZl3* this, GlobalContext* globalCtx) { + s32 sp2C = func_80B54DD4(this); + + this->unk_3DC = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_0091D8)); + this->unk_3E0 = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_00A598)); + this->unk_3E4 = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_00A334)); + this->unk_3F4 = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_001110)); + this->unk_3EC = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_002348)); + this->unk_3F0 = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_002E54)); + this->unk_3E8 = Animation_GetLastFrame(SEGMENTED_TO_VIRTUAL(&gZelda2Anime2Anim_001D8C)); + + switch (sp2C) { + case 0: + func_80B54FB4(this, globalCtx); + break; + case 1: + func_80B55780(this, globalCtx); + break; + case 3: + func_80B59828(this, globalCtx); + break; + default: + osSyncPrintf(VT_FGCOL(RED) " En_Oa3 の arg_data がおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + Actor_Kill(&this->actor); + } +} + +void func_80B59DB8(EnZl3* this, GlobalContext* globalCtx) { + s32 pad; + ObjectContext* objCtx = &globalCtx->objectCtx; + s32 objIndex = Object_GetIndex(objCtx, OBJECT_ZL2_ANIME2); + s32 pad2; + + if (objIndex < 0) { + osSyncPrintf(VT_FGCOL(RED) "En_Zl3_main_bankアニメーションのバンクを読めない!!!!!!!!!!!!\n" VT_RST); + return; + } + + if (Object_IsLoaded(objCtx, objIndex)) { + this->unk_318 = objIndex; + func_80B54DE0(this, globalCtx); + func_80B59B6C(this, globalCtx); + } +} + +static EnZl3ActionFunc sActionFuncs[] = { + func_80B59DB8, func_80B55550, func_80B555A4, func_80B55604, func_80B5566C, func_80B556CC, func_80B5572C, + func_80B56658, func_80B566AC, func_80B5670C, func_80B5676C, func_80B567CC, func_80B5682C, func_80B568B4, + func_80B5691C, func_80B5697C, func_80B569E4, func_80B56A68, func_80B56AE0, func_80B56B54, func_80B56BA8, + func_80B56C24, func_80B56C84, func_80B56CE4, func_80B56D44, func_80B58D50, func_80B58DB0, func_80B58E10, + func_80B58E7C, func_80B58EF4, func_80B58F6C, func_80B58FDC, func_80B5904C, func_80B590BC, func_80B5912C, + func_80B591BC, func_80B5922C, func_80B592A8, func_80B59340, func_80B593D0, +}; + +void EnZl3_Update(Actor* thisx, GlobalContext* globalCtx) { + EnZl3* this = (EnZl3*)thisx; + + if (this->action < 0 || this->action >= ARRAY_COUNT(sActionFuncs) || sActionFuncs[this->action] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "メインモードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sActionFuncs[this->action](this, globalCtx); +} + +void EnZl3_Init(Actor* thisx, GlobalContext* globalCtx) { + EnZl3* this = (EnZl3*)thisx; + ActorShape* shape = &this->actor.shape; + s32 pad; + + osSyncPrintf("ゼルダ姫のEn_Zl3_Actor_ct通すよ!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + ActorShape_Init(shape, 0.0f, ActorShadow_DrawCircle, 30.0f); + shape->shadowAlpha = 0; + func_80B533B0(thisx, globalCtx); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZelda2Skel, NULL, this->jointTable, this->morphTable, 15); + + switch (func_80B54DD4(this)) { + case 1: + gSaveContext.timer2State = 0; + break; + case 3: + func_80B59A80(this, globalCtx); + break; + } + + osSyncPrintf("ゼルダ姫のEn_Zl3_Actor_ctは通った!!!!!!!!!!!!!!!!!!!!!!!!!\n"); +} + +static OverrideLimbDraw sOverrideLimbDrawFuncs[] = { + func_80B5458C, + func_80B5944C, +}; + +s32 EnZl3_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnZl3* this = (EnZl3*)thisx; + + if (this->unk_308 < 0 || this->unk_308 >= ARRAY_COUNT(sOverrideLimbDrawFuncs) || + sOverrideLimbDrawFuncs[this->unk_308] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画前処理モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return 0; + } + return sOverrideLimbDrawFuncs[this->unk_308](globalCtx, limbIndex, dList, pos, rot, thisx, gfx); +} + +void func_80B59FE8(EnZl3* this, GlobalContext* globalCtx) { +} + +void func_80B59FF4(EnZl3* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeTexIndex = this->eyeTexIndex; + void* eyeTex = sEyeTextures[eyeTexIndex]; + s16 mouthTexIndex = this->mouthTexIndex; + SkelAnime* skelAnime = &this->skelAnime; + void* mouthTex = sMouthTextures[mouthTexIndex]; + s32 pad2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zl3.c", 2165); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPSegment(POLY_OPA_DISP++, 0x8, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0x9, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_OPA_DISP++, 0xA, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSPSegment(POLY_OPA_DISP++, 0xB, &D_80116280[2]); + + POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnZl3_OverrideLimbDraw, EnZl3_PostLimbDraw, this, POLY_OPA_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zl3.c", 2190); +} + +void func_80B5A1D0(EnZl3* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 eyeTexIndex = this->eyeTexIndex; + void* eyeTex = sEyeTextures[eyeTexIndex]; + s16 mouthTexIndex = this->mouthTexIndex; + SkelAnime* skelAnime = &this->skelAnime; + void* mouthTex = sMouthTextures[mouthTexIndex]; + s32 pad2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zl3.c", 2205); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 8, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 9, SEGMENTED_TO_VIRTUAL(eyeTex)); + gSPSegment(POLY_XLU_DISP++, 10, SEGMENTED_TO_VIRTUAL(mouthTex)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, this->alpha); + gSPSegment(POLY_XLU_DISP++, 11, &D_80116280[0]); + + POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, skelAnime->skeleton, skelAnime->jointTable, skelAnime->dListCount, + EnZl3_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zl3.c", 2234); +} + +static EnZl3DrawFunc sDrawFuncs[] = { + func_80B59FE8, + func_80B59FF4, + func_80B5A1D0, +}; + +void EnZl3_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnZl3* this = (EnZl3*)thisx; + + if (this->drawConfig < 0 || this->drawConfig >= 3 || sDrawFuncs[this->drawConfig] == NULL) { + osSyncPrintf(VT_FGCOL(RED) "描画モードがおかしい!!!!!!!!!!!!!!!!!!!!!!!!!\n" VT_RST); + return; + } + sDrawFuncs[this->drawConfig](this, globalCtx); +} + +const ActorInit En_Zl3_InitVars = { + ACTOR_EN_ZL3, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZL2, + sizeof(EnZl3), + (ActorFunc)EnZl3_Init, + (ActorFunc)EnZl3_Destroy, + (ActorFunc)EnZl3_Update, + (ActorFunc)EnZl3_Draw, + (ActorResetFunc)EnZl3_Reset, +}; + +void EnZl3_Reset(void) { + D_80B5A468 = 0; + D_80B5A494 = -1; + D_80B5A4BC = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.h b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.h new file mode 100644 index 000000000..cf30576c3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl3/z_en_zl3.h @@ -0,0 +1,71 @@ +#ifndef Z_EN_ZL3_H +#define Z_EN_ZL3_H + +#include "ultra64.h" +#include "global.h" + +#include "overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.h" + +struct EnZl3; + +typedef void (*EnZl3ActionFunc)(struct EnZl3*, GlobalContext*); +typedef void (*EnZl3DrawFunc)(struct EnZl3*, GlobalContext*); + +typedef struct EnZl3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ Vec3s jointTable[15]; + /* 0x01EA */ Vec3s morphTable[15]; + /* 0x0244 */ s16 eyeTexIndex; + /* 0x0246 */ s16 blinkTimer; + /* 0x0248 */ s16 mouthTexIndex; + /* 0x024C */ s32 action; + /* 0x0250 */ s32 drawConfig; + /* 0x0254 */ char unk_254[0x4]; + /* 0x0258 */ s32 alpha; + /* 0x025C */ s16 unk_25C[24]; + /* 0x028C */ s16 unk_28C[24]; + /* 0x02BC */ s16 unk_2BC[24]; + /* 0x02EC */ f32 unk_2EC; + /* 0x02F0 */ s32 unk_2F0; + /* 0x02F4 */ char unk_2F4[0x4]; + /* 0x02F8 */ s32 unk_2F8; + /* 0x02FC */ s32 unk_2FC; + /* 0x0300 */ char unk_300[0x8]; + /* 0x0308 */ s32 unk_308; + /* 0x030C */ Path* unk_30C; + /* 0x0310 */ s32 unk_310; + /* 0x0314 */ s32 unk_314; + /* 0x0318 */ s32 unk_318; + /* 0x031C */ Vec3f unk_31C; + /* 0x0328 */ s32 unk_328; + /* 0x032C */ Vec3f unk_32C; + /* 0x0338 */ Vec3f unk_338; + /* 0x0344 */ u16 unk_344; + /* 0x0346 */ u16 unk_346; + /* 0x0348 */ Vec3f unk_348; + /* 0x0354 */ Vec3f unk_354; + /* 0x0360 */ f32 unk_360; + /* 0x0364 */ f32 unk_364; + /* 0x0368 */ f32 unk_368; + /* 0x036C */ s32 unk_36C; + /* 0x0370 */ s32 unk_370; + /* 0x0374 */ s32 unk_374; + /* 0x0378 */ ColliderCylinder collider; + /* 0x03C4 */ s32 unk_3C4; + /* 0x03C8 */ u8 unk_3C8; + /* 0x03CC */ f32 unk_3CC; + /* 0x03D0 */ s16 unk_3D0; + /* 0x03D4 */ BossGanon2* ganon; + /* 0x03D8 */ s32 unk_3D8; + /* 0x03DC */ f32 unk_3DC; + /* 0x03E0 */ f32 unk_3E0; + /* 0x03E4 */ f32 unk_3E4; + /* 0x03E8 */ f32 unk_3E8; + /* 0x03EC */ f32 unk_3EC; + /* 0x03F0 */ f32 unk_3F0; + /* 0x03F4 */ f32 unk_3F4; + /* 0x03F8 */ struct_80034A14_arg1 unk_3F8; +} EnZl3; // size = 0x0420 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c new file mode 100644 index 000000000..06442186d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.c @@ -0,0 +1,1300 @@ +/* + * File: z_en_zl4.c + * Overlay: ovl_En_Zl4 + * Description: Child Princess Zelda + */ + +#include "z_en_zl4.h" +#include "objects/object_zl4/object_zl4.h" +#include "scenes/indoors/nakaniwa/nakaniwa_scene.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4) + +typedef enum { + /* 0 */ ZL4_CS_WAIT, + /* 1 */ ZL4_CS_START, + /* 2 */ ZL4_CS_MEET, + /* 3 */ ZL4_CS_STONE, + /* 4 */ ZL4_CS_NAMES, + /* 5 */ ZL4_CS_LEGEND, + /* 6 */ ZL4_CS_WINDOW, + /* 7 */ ZL4_CS_GANON, + /* 8 */ ZL4_CS_PLAN +} EnZl4CutsceneState; + +typedef enum { + /* 0 */ ZL4_EYES_NEUTRAL, + /* 1 */ ZL4_EYES_SHUT, + /* 2 */ ZL4_EYES_LOOK_LEFT, + /* 3 */ ZL4_EYES_LOOK_RIGHT, + /* 4 */ ZL4_EYES_WIDE, + /* 5 */ ZL4_EYES_SQUINT, + /* 6 */ ZL4_EYES_OPEN +} EnZl4EyeExpression; + +typedef enum { + /* 0 */ ZL4_MOUTH_NEUTRAL, + /* 1 */ ZL4_MOUTH_HAPPY, + /* 2 */ ZL4_MOUTH_WORRIED, + /* 3 */ ZL4_MOUTH_SURPRISED +} EnZl4MouthExpression; + +typedef enum { + /* 0 */ ZL4_EYE_OPEN, + /* 1 */ ZL4_EYE_BLINK, + /* 2 */ ZL4_EYE_SHUT, + /* 3 */ ZL4_EYE_WIDE, + /* 4 */ ZL4_EYE_SQUINT, + /* 5 */ ZL4_EYE_LOOK_OUT, + /* 6 */ ZL4_EYE_LOOK_IN +} EnZl4EyeState; + +void EnZl4_Init(Actor* thisx, GlobalContext* globalCtx); +void EnZl4_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnZl4_Update(Actor* thisx, GlobalContext* globalCtx); +void EnZl4_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnZl4_Cutscene(EnZl4* this, GlobalContext* globalCtx); +void EnZl4_Idle(EnZl4* this, GlobalContext* globalCtx); +void EnZl4_TheEnd(EnZl4* this, GlobalContext* globalCtx); + +const ActorInit En_Zl4_InitVars = { + ACTOR_EN_ZL4, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZL4, + sizeof(EnZl4), + (ActorFunc)EnZl4_Init, + (ActorFunc)EnZl4_Destroy, + (ActorFunc)EnZl4_Update, + (ActorFunc)EnZl4_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 10, 44, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInfoInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +typedef enum { + /* 0 */ ZL4_ANIM_0, + /* 1 */ ZL4_ANIM_1, + /* 2 */ ZL4_ANIM_2, + /* 3 */ ZL4_ANIM_3, + /* 4 */ ZL4_ANIM_4, + /* 5 */ ZL4_ANIM_5, + /* 6 */ ZL4_ANIM_6, + /* 7 */ ZL4_ANIM_7, + /* 8 */ ZL4_ANIM_8, + /* 9 */ ZL4_ANIM_9, + /* 10 */ ZL4_ANIM_10, + /* 11 */ ZL4_ANIM_11, + /* 12 */ ZL4_ANIM_12, + /* 13 */ ZL4_ANIM_13, + /* 14 */ ZL4_ANIM_14, + /* 15 */ ZL4_ANIM_15, + /* 16 */ ZL4_ANIM_16, + /* 17 */ ZL4_ANIM_17, + /* 18 */ ZL4_ANIM_18, + /* 19 */ ZL4_ANIM_19, + /* 20 */ ZL4_ANIM_20, + /* 21 */ ZL4_ANIM_21, + /* 22 */ ZL4_ANIM_22, + /* 23 */ ZL4_ANIM_23, + /* 24 */ ZL4_ANIM_24, + /* 25 */ ZL4_ANIM_25, + /* 26 */ ZL4_ANIM_26, + /* 27 */ ZL4_ANIM_27, + /* 28 */ ZL4_ANIM_28, + /* 29 */ ZL4_ANIM_29, + /* 30 */ ZL4_ANIM_30, + /* 31 */ ZL4_ANIM_31, + /* 32 */ ZL4_ANIM_32, + /* 33 */ ZL4_ANIM_33 +} EnZl4Animation; + +static AnimationInfo sAnimationInfo[] = { + /* 0 */ /* standing idle */ { &gChildZeldaAnim_000654, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 1 */ /* moves to introduce herself */ { &gChildZeldaAnim_00E5C8, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 2 */ /* introducing herself */ { &gChildZeldaAnim_00EBC4, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 3 */ /* turns away from window surprised */ + { &gChildZeldaAnim_010DF8, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 4 */ /* standing with hand in front of mouth */ + { &gChildZeldaAnim_011248, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 5 */ /* surprise, moves hand to mouth */ { &gChildZeldaAnim_011698, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 6 */ /* uncrosses arms, leans toward link with hands together */ + { &gChildZeldaAnim_011B34, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + /* 7 */ /* turns to write letter */ { &gChildZeldaAnim_0125E4, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + /* 8 */ /* writing letter */ { &gChildZeldaAnim_012E58, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 9 */ /* pulls back, looks askew */ { &gChildZeldaAnim_013280, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 10 */ /* looks askew at Link */ { &gChildZeldaAnim_013628, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 11 */ /* crosses arms, looks to the side */ { &gChildZeldaAnim_013A50, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 12 */ /* arms crossed, looking away */ { &gChildZeldaAnim_013EA0, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 13 */ /* turns away, hands behind back, looks up */ + { &gChildZeldaAnim_015F14, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 14 */ /* turns back to link, hands on top of each other */ + { &gChildZeldaAnim_0169B4, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 15 */ /* hands behind back looking up */ { &gChildZeldaAnim_016D08, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 16 */ /* leans toward link, looks askew */ { &gChildZeldaAnim_01726C, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 17 */ /* leaning toward link, looking askew */ + { &gChildZeldaAnim_017818, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + /* 18 */ /* neutral, looking at Link */ { &gChildZeldaAnim_01805C, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 19 */ /* moves towards link, hands clasped */ + { &gChildZeldaAnim_018898, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 20 */ /* facing link, hands clasped */ { &gChildZeldaAnim_01910C, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 21 */ /* look in window */ { &gChildZeldaAnim_019600, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 22 */ /* leans forward, hands together */ { &gChildZeldaAnim_01991C, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, -1.0f }, + /* 23 */ /* turns to link, hands on top of each other */ + { &gChildZeldaAnim_01A2FC, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 24 */ /* stands, hands on top of each other */ + { &gChildZeldaAnim_01AAE0, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 25 */ /* leaning forward, hands together */ { &gChildZeldaAnim_01AE88, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 26 */ /* walks aside, points to window */ { &gChildZeldaAnim_01B874, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + /* 27 */ /* stands pointing at window */ { &gChildZeldaAnim_01BCF0, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 28 */ /* laughs, hands together */ { &gChildZeldaAnim_01C494, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 29 */ /* happy, hands together */ { &gChildZeldaAnim_01C7B0, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -1.0f }, + /* 30 */ /* standing hands behind back looking down*/ + { &gChildZeldaAnim_01CE08, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 31 */ /* cocks head, hands clasped */ { &gChildZeldaAnim_00F0A4, 1.0f, 0.0f, -1.0f, ANIMMODE_ONCE, 0.0f }, + /* 32 */ /* happy, hands clasped */ { &gChildZeldaAnim_00F894, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + /* 33 */ /* transition to standing */ { &gChildZeldaAnim_000654, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, +}; + +#include "z_en_zl4_cutscene_data.c" + +void EnZl4_SetCsCameraAngle(GlobalContext* globalCtx, s16 index) { + Camera* activeCam = GET_ACTIVE_CAM(globalCtx); + + Camera_ChangeSetting(activeCam, CAM_SET_FREE0); + activeCam->at = sCsCameraAngle[index].at; + activeCam->eye = activeCam->eyeNext = sCsCameraAngle[index].eye; + activeCam->roll = sCsCameraAngle[index].roll; + activeCam->fov = sCsCameraAngle[index].fov; +} + +void EnZl4_SetCsCameraMove(GlobalContext* globalCtx, s16 index) { + Camera* activeCam = GET_ACTIVE_CAM(globalCtx); + Player* player = GET_PLAYER(globalCtx); + + Camera_ChangeSetting(activeCam, CAM_SET_CS_0); + Camera_ResetAnim(activeCam); + Camera_SetCSParams(activeCam, sCsCameraMove[index].atPoints, sCsCameraMove[index].eyePoints, player, + sCsCameraMove[index].relativeToPlayer); +} + +u16 EnZl4_GetText(GlobalContext* globalCtx, Actor* thisx) { + u16 faceReaction = Text_GetFaceReaction(globalCtx, 22); + u16 stoneCount; + s16 ret; + + if (faceReaction != 0) { + return faceReaction; + } + + stoneCount = 0; + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { + stoneCount = 1; + } + if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { + stoneCount++; + } + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + stoneCount++; + } + + if (stoneCount > 1) { + ret = 0x703D; + } else { + ret = 0x703C; + } + return ret; +} + +s16 func_80B5B9B0(GlobalContext* globalCtx, Actor* thisx) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + return false; + } + return true; +} + +void EnZl4_UpdateFace(EnZl4* this) { + if (this->blinkTimer > 0) { + this->blinkTimer--; + } else { + this->blinkTimer = 0; + } + if (this->blinkTimer <= 2) { + this->leftEyeState = this->rightEyeState = this->blinkTimer; + } + switch (this->eyeExpression) { + case ZL4_EYES_NEUTRAL: + if (this->blinkTimer == 0) { + this->blinkTimer = Rand_S16Offset(30, 30); + } + break; + case ZL4_EYES_SHUT: + if (this->blinkTimer == 0) { + this->leftEyeState = this->rightEyeState = ZL4_EYE_SHUT; + } + break; + case ZL4_EYES_LOOK_LEFT: + if (this->blinkTimer == 0) { + this->leftEyeState = ZL4_EYE_LOOK_OUT; + this->rightEyeState = ZL4_EYE_LOOK_IN; + } + break; + case ZL4_EYES_LOOK_RIGHT: + if (this->blinkTimer == 0) { + this->leftEyeState = ZL4_EYE_LOOK_IN; + this->rightEyeState = ZL4_EYE_LOOK_OUT; + } + break; + case ZL4_EYES_WIDE: + if (this->blinkTimer == 0) { + this->leftEyeState = this->rightEyeState = ZL4_EYE_WIDE; + } + break; + case ZL4_EYES_SQUINT: + if (this->blinkTimer == 0) { + this->leftEyeState = this->rightEyeState = ZL4_EYE_SQUINT; + } + break; + case ZL4_EYES_OPEN: + if (this->blinkTimer >= 3) { + this->blinkTimer = ZL4_EYE_OPEN; + } + break; + } + switch (this->mouthExpression) { + case ZL4_MOUTH_HAPPY: + this->mouthState = 1; + break; + case ZL4_MOUTH_WORRIED: + this->mouthState = 2; + break; + case ZL4_MOUTH_SURPRISED: + this->mouthState = 3; + break; + default: + this->mouthState = 0; + break; + } +} + +void EnZl4_SetMove(EnZl4* this, GlobalContext* globalCtx) { + this->skelAnime.moveFlags |= 1; + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, 1.0f); +} + +void func_80B5BB78(EnZl4* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_1E0.unk_18 = player->actor.world.pos; + func_80034A14(&this->actor, &this->unk_1E0, 2, 2); +} + +void EnZl4_GetActionStartPos(CsCmdActorAction* action, Vec3f* vec) { + vec->x = action->startPos.x; + vec->y = action->startPos.y; + vec->z = action->startPos.z; +} + +s32 EnZl4_SetupFromLegendCs(EnZl4* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* playerx = &GET_PLAYER(globalCtx)->actor; + s16 rotY; + + func_8002DF54(globalCtx, &this->actor, 8); + playerx->world.pos = this->actor.world.pos; + rotY = this->actor.shape.rot.y; + playerx->world.pos.x += 56.0f * Math_SinS(rotY); + playerx->world.pos.z += 56.0f * Math_CosS(rotY); + + player->linearVelocity = playerx->speedXZ = 0.0f; + + EnZl4_SetCsCameraMove(globalCtx, 5); + ShrinkWindow_SetVal(0x20); + Interface_ChangeAlpha(2); + this->talkTimer2 = 0; + return true; +} + +s32 EnZl4_InMovingAnim(EnZl4* this) { + if ((this->skelAnime.animation == &gChildZeldaAnim_01B874) || + (this->skelAnime.animation == &gChildZeldaAnim_01BCF0) || + (this->skelAnime.animation == &gChildZeldaAnim_0125E4) || + (this->skelAnime.animation == &gChildZeldaAnim_012E58) || + (this->skelAnime.animation == &gChildZeldaAnim_015F14) || + (this->skelAnime.animation == &gChildZeldaAnim_0169B4) || + (this->skelAnime.animation == &gChildZeldaAnim_016D08) || + (this->skelAnime.animation == &gChildZeldaAnim_01805C) || + (this->skelAnime.animation == &gChildZeldaAnim_01A2FC) || + (this->skelAnime.animation == &gChildZeldaAnim_01AAE0) || + (this->skelAnime.animation == &gChildZeldaAnim_01CE08) || + (this->skelAnime.animation == &gChildZeldaAnim_018898) || + (this->skelAnime.animation == &gChildZeldaAnim_01910C) || + (this->skelAnime.animation == &gChildZeldaAnim_00F0A4) || + (this->skelAnime.animation == &gChildZeldaAnim_00F894)) { + return true; + } + return false; +} + +void EnZl4_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZl4* this = (EnZl4*)thisx; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gChildZeldaSkel, NULL, this->jointTable, this->morphTable, 18); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 18.0f); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_21); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->actor.textId = -1; + this->eyeExpression = this->mouthExpression = ZL4_MOUTH_NEUTRAL; + + if (gSaveContext.sceneSetupIndex >= 4) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); + this->actionFunc = EnZl4_TheEnd; + } else if (gSaveContext.eventChkInf[4] & 1) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); + this->actionFunc = EnZl4_Idle; + } else { + if (gSaveContext.entranceIndex != 0x5F0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_21); + this->csState = ZL4_CS_WAIT; + this->talkState = 0; + } else { + EnZl4_SetupFromLegendCs(this, globalCtx); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); + this->csState = ZL4_CS_LEGEND; + this->talkState = 0; + } + this->actionFunc = EnZl4_Cutscene; + } +} + +void EnZl4_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZl4* this = (EnZl4*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +s32 EnZl4_SetNextAnim(EnZl4* this, s32 nextAnim) { + if (!Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + return false; + } + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, nextAnim); + return true; +} + +void EnZl4_ReverseAnimation(EnZl4* this) { + f32 tempFrame = this->skelAnime.startFrame; + + this->skelAnime.startFrame = this->skelAnime.endFrame; + this->skelAnime.curFrame = this->skelAnime.endFrame; + this->skelAnime.endFrame = tempFrame; + this->skelAnime.playSpeed = -1.0f; +} + +s32 EnZl4_CsWaitForPlayer(EnZl4* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Actor* playerx = &GET_PLAYER(globalCtx)->actor; + s16 rotY; + s16 yawDiff; + s16 absYawDiff; + + if (!Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + yawDiff = (f32)this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + absYawDiff = ABS(yawDiff); + if ((playerx->world.pos.y != this->actor.world.pos.y) || (absYawDiff >= 0x3FFC)) { + return false; + } else { + func_8002F2CC(&this->actor, globalCtx, this->collider.dim.radius + 60.0f); + return false; + } + } + playerx->world.pos = this->actor.world.pos; + rotY = this->actor.shape.rot.y; + playerx->world.pos.x += 56.0f * Math_SinS(rotY); + playerx->world.pos.z += 56.0f * Math_CosS(rotY); + playerx->speedXZ = 0.0f; + player->linearVelocity = 0.0f; + return true; +} + +s32 EnZl4_CsMeetPlayer(EnZl4* this, GlobalContext* globalCtx) { + switch (this->talkState) { + case 0: + if (this->skelAnime.curFrame == 50.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_MEET); + } + if (!EnZl4_SetNextAnim(this, ZL4_ANIM_4)) { + break; + } else { + Message_StartTextbox(globalCtx, 0x702E, NULL); + this->talkState++; + } + break; + case 1: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 1); + Message_StartTextbox(globalCtx, 0x702F, NULL); + this->talkTimer2 = 0; + this->talkState++; + } + break; + case 2: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gZeldasCourtyardMeetCs); + gSaveContext.cutsceneTrigger = 1; + EnZl4_SetCsCameraMove(globalCtx, 0); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer2 = 0; + this->talkState++; + } + break; + case 3: + this->talkTimer2++; + if (this->talkTimer2 >= 45) { + Message_StartTextbox(globalCtx, 0x70F9, NULL); + this->talkState++; + } + break; + case 4: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraMove(globalCtx, 1); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer2 = 0; + this->talkState++; + } + break; + case 5: + this->talkTimer2++; + if (this->talkTimer2 >= 10) { + Message_StartTextbox(globalCtx, 0x70FA, NULL); + this->talkState++; + } + break; + case 6: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 2); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_22); + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + this->talkTimer2 = 0; + this->talkState++; + Message_StartTextbox(globalCtx, 0x70FB, NULL); + } + break; + } + return (this->talkState == 7) ? 1 : 0; +} + +s32 EnZl4_CsAskStone(EnZl4* this, GlobalContext* globalCtx) { + switch (this->talkState) { + case 0: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_25)) { + this->talkState++; + } + case 1: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 3); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer1 = 40; + this->talkState = 2; + } + break; + case 2: + if (DECR(this->talkTimer1) == 0) { + Message_StartTextbox(globalCtx, 0x7030, NULL); + this->talkState++; + } + break; + case 3: + if (!((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx))) { + break; + } else if (globalCtx->msgCtx.choiceIndex == 0) { + EnZl4_SetCsCameraAngle(globalCtx, 4); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_28); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_SQUINT; + this->mouthExpression = ZL4_MOUTH_HAPPY; + Message_StartTextbox(globalCtx, 0x7032, NULL); + this->talkState = 7; + } else { + EnZl4_SetCsCameraAngle(globalCtx, 2); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_9); + this->mouthExpression = ZL4_MOUTH_WORRIED; + Message_StartTextbox(globalCtx, 0x7031, NULL); + this->talkState++; + } + break; + case 4: + if (this->skelAnime.curFrame == 16.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_QUESTION); + } + if (EnZl4_SetNextAnim(this, ZL4_ANIM_10)) { + this->talkState++; + } + case 5: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_9); + this->mouthExpression = ZL4_MOUTH_WORRIED; + EnZl4_ReverseAnimation(this); + this->talkState = 6; + } + break; + case 6: + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + EnZl4_SetCsCameraAngle(globalCtx, 3); + Message_StartTextbox(globalCtx, 0x7030, NULL); + this->talkState = 12; + break; + case 12: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_25)) { + this->talkState = 13; + } + case 13: + if (!((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx))) { + break; + } else if (globalCtx->msgCtx.choiceIndex == 0) { + EnZl4_SetCsCameraAngle(globalCtx, 4); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_28); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_SQUINT; + this->mouthExpression = ZL4_MOUTH_HAPPY; + Message_StartTextbox(globalCtx, 0x7032, NULL); + this->talkState = 7; + } else { + EnZl4_SetCsCameraAngle(globalCtx, 2); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_9); + this->mouthExpression = ZL4_MOUTH_WORRIED; + Message_StartTextbox(globalCtx, 0x7031, NULL); + this->talkState = 4; + } + break; + case 7: + if (this->skelAnime.curFrame == 17.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_SMILE_0); + } + if (EnZl4_SetNextAnim(this, ZL4_ANIM_29)) { + this->talkState++; + } + case 8: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraMove(globalCtx, 2); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + Message_StartTextbox(globalCtx, 0x70FC, NULL); + this->talkState = 9; + } + break; + case 9: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 5); + Message_StartTextbox(globalCtx, 0x70FD, NULL); + this->talkState++; + } + break; + case 10: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_5); + this->eyeExpression = ZL4_EYES_OPEN; + this->mouthExpression = ZL4_MOUTH_SURPRISED; + Message_StartTextbox(globalCtx, 0x70FE, NULL); + this->talkState++; + } + break; + } + return (this->talkState == 11) ? 1 : 0; +} + +s32 EnZl4_CsAskName(EnZl4* this, GlobalContext* globalCtx) { + switch (this->talkState) { + case 0: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_4)) { + this->talkState++; + } + break; + case 1: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 6); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_1); + this->blinkTimer = 11; + this->eyeExpression = ZL4_EYES_SQUINT; + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + Message_StartTextbox(globalCtx, 0x70FF, NULL); + this->talkState++; + } + break; + case 2: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_2)) { + this->talkState++; + } + case 3: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_16); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState = 4; + } + break; + case 4: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_17)) { + Message_StartTextbox(globalCtx, 0x2073, NULL); + this->talkState++; + } + break; + case 5: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraMove(globalCtx, 3); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer2 = 0; + this->talkState = 6; + } + break; + case 6: + this->talkTimer2++; + if (this->talkTimer2 >= 15) { + Message_StartTextbox(globalCtx, 0x2074, NULL); + this->talkState++; + } + break; + case 7: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_6); + this->mouthExpression = ZL4_MOUTH_HAPPY; + Message_StartTextbox(globalCtx, 0x2075, NULL); + this->talkState++; + } + break; + case 8: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_25)) { + this->talkState++; + } + case 9: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_StartTextbox(globalCtx, 0x7033, NULL); + this->talkState = 10; + } + break; + case 10: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + if (globalCtx->msgCtx.choiceIndex == 0) { + EnZl4_SetCsCameraMove(globalCtx, 4); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_33); + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer2 = 0; + this->talkState = 15; + } else { + EnZl4_SetCsCameraAngle(globalCtx, 6); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer1 = 20; + this->talkState++; + this->skelAnime.playSpeed = 0.0f; + } + } + break; + case 11: + if (DECR(this->talkTimer1) == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_11); + this->blinkTimer = 11; + this->eyeExpression = ZL4_EYES_LOOK_RIGHT; + this->mouthExpression = ZL4_MOUTH_WORRIED; + Message_StartTextbox(globalCtx, 0x7034, NULL); + this->talkState++; + } + break; + case 12: + if (this->skelAnime.curFrame == 5.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_SIGH_0); + } + if (EnZl4_SetNextAnim(this, ZL4_ANIM_12)) { + this->talkState++; + } + case 13: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_6); + this->blinkTimer = 3; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_HAPPY; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState = 14; + } + break; + case 14: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_25)) { + Message_StartTextbox(globalCtx, 0x7033, NULL); + this->talkState = 10; + } + break; + case 15: + this->talkTimer2++; + if (this->talkTimer2 >= 30) { + Message_StartTextbox(globalCtx, 0x7035, NULL); + this->talkState++; + } + break; + case 16: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState++; + } + case 17: + this->talkTimer2++; + if (this->talkTimer2 == 130) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + globalCtx->nextEntranceIndex = 0xA0; + gSaveContext.nextCutsceneIndex = 0xFFF7; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->fadeTransition = 3; + } + break; + } + if ((this->talkTimer2 == 17) && (this->talkTimer2 > 130)) { + return true; + } + return false; +} + +s32 EnZl4_CsTellLegend(EnZl4* this, GlobalContext* globalCtx) { + Camera* activeCam = GET_ACTIVE_CAM(globalCtx); + + switch (this->talkState) { + case 0: + this->talkTimer2++; + if (this->talkTimer2 >= 60) { + Message_StartTextbox(globalCtx, 0x7037, NULL); + this->talkState++; + } + break; + case 1: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 7); + Message_StartTextbox(globalCtx, 0x2076, NULL); + this->talkState++; + } + break; + case 2: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraMove(globalCtx, 6); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState++; + } + break; + case 3: + if (activeCam->animState == 2) { + Message_StartTextbox(globalCtx, 0x2077, NULL); + this->talkState++; + } + break; + case 4: + if (!((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx))) { + break; + } else if (globalCtx->msgCtx.choiceIndex == 0) { + EnZl4_SetCsCameraAngle(globalCtx, 8); + Message_StartTextbox(globalCtx, 0x7005, NULL); + this->talkState = 9; + } else { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_5); + this->mouthExpression = ZL4_MOUTH_SURPRISED; + Message_StartTextbox(globalCtx, 0x7038, NULL); + this->talkState++; + Audio_PlayActorSound2(&this->actor, NA_SE_VO_Z0_HURRY); + } + break; + case 5: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_4)) { + this->talkState++; + } + case 6: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_33); + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + Message_StartTextbox(globalCtx, 0x7037, NULL); + this->talkState++; + } + break; + case 7: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_StartTextbox(globalCtx, 0x2076, NULL); + this->talkState++; + } + break; + case 8: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_StartTextbox(globalCtx, 0x2077, NULL); + this->talkState = 4; + } + break; + case 9: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_26); + Message_StartTextbox(globalCtx, 0x2078, NULL); + this->talkState++; + } + break; + case 10: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_27)) { + this->talkState++; + } + case 11: + if (!((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx))) { + break; + } else if (globalCtx->msgCtx.choiceIndex == 0) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState = 13; + } else { + Message_StartTextbox(globalCtx, 0x700B, NULL); + this->talkState = 12; + } + break; + case 12: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState = 13; + } + break; + } + return (this->talkState == 13) ? 1 : 0; +} + +s32 EnZl4_CsLookWindow(EnZl4* this, GlobalContext* globalCtx) { + switch (this->talkState) { + case 0: + EnZl4_SetCsCameraMove(globalCtx, 7); + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gZeldasCourtyardWindowCs); + gSaveContext.cutsceneTrigger = 1; + this->talkState++; + break; + case 1: + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.frames == 90) { + globalCtx->csCtx.state = CS_STATE_UNSKIPPABLE_INIT; + } + } else { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gZeldasCourtyardGanonCs); + gSaveContext.cutsceneTrigger = 1; + this->talkState++; + func_8002DF54(globalCtx, &this->actor, 8); + } + break; + case 2: + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + if (globalCtx->csCtx.frames == 209) { + globalCtx->csCtx.state = CS_STATE_UNSKIPPABLE_INIT; + } + } else { + func_800AA000(0.0f, 0xA0, 0xA, 0x28); + func_8002DF54(globalCtx, &this->actor, 1); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_30); + EnZl4_SetCsCameraAngle(globalCtx, 11); + Message_StartTextbox(globalCtx, 0x7039, NULL); + this->talkState++; + } + break; + case 3: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState++; + } + break; + } + return (this->talkState == 4) ? 1 : 0; +} + +s32 EnZl4_CsWarnAboutGanon(EnZl4* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s16 rotY; + + switch (this->talkState) { + case 0: + player->actor.world.pos = this->actor.world.pos; + rotY = this->actor.shape.rot.y - 0x3FFC; + player->actor.world.pos.x += 34.0f * Math_SinS(rotY); + player->actor.world.pos.z += 34.0f * Math_CosS(rotY); + EnZl4_SetCsCameraMove(globalCtx, 8); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_WIDE; + this->mouthExpression = ZL4_MOUTH_WORRIED; + this->talkTimer2 = 0; + this->talkState++; + Message_StartTextbox(globalCtx, 0x2079, NULL); + case 1: + this->talkTimer2++; + if (this->talkTimer2 >= 20) { + this->talkState++; + } + break; + case 2: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraMove(globalCtx, 9); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkTimer2 = 0; + this->talkState++; + } + break; + case 3: + this->talkTimer2++; + if (this->talkTimer2 >= 20) { + Message_StartTextbox(globalCtx, 0x207A, NULL); + this->talkState++; + } + break; + case 4: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 12); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_23); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_SURPRISED; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState++; + } + break; + case 5: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_24)) { + Message_StartTextbox(globalCtx, 0x207B, NULL); + this->talkState++; + } + break; + case 6: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_StartTextbox(globalCtx, 0x703A, NULL); + this->talkState++; + } + break; + case 7: + if (!((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx))) { + break; + } else if (globalCtx->msgCtx.choiceIndex == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_31); + this->blinkTimer = 11; + this->eyeExpression = ZL4_EYES_SQUINT; + this->mouthExpression = ZL4_MOUTH_HAPPY; + Message_StartTextbox(globalCtx, 0x703B, NULL); + this->talkState = 11; + } else { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_13); + this->blinkTimer = 11; + this->eyeExpression = ZL4_EYES_LOOK_LEFT; + this->mouthExpression = ZL4_MOUTH_WORRIED; + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState++; + } + break; + case 8: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_15)) { + this->blinkTimer = 3; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_SURPRISED; + Message_StartTextbox(globalCtx, 0x7073, NULL); + this->talkState++; + } + break; + case 9: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_14); + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState++; + } + break; + case 10: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_24)) { + Message_StartTextbox(globalCtx, 0x703A, NULL); + this->talkState = 7; + } + break; + case 11: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_32)) { + this->talkState++; + } + case 12: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + globalCtx->msgCtx.msgMode = MSGMODE_PAUSED; + this->talkState = 13; + } + break; + } + return (this->talkState == 13) ? 1 : 0; +} + +s32 EnZl4_CsMakePlan(EnZl4* this, GlobalContext* globalCtx) { + switch (this->talkState) { + case 0: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_18); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_WORRIED; + EnZl4_SetCsCameraMove(globalCtx, 10); + this->talkTimer2 = 0; + this->talkState++; + case 1: + this->talkTimer2++; + if (this->talkTimer2 >= 10) { + Message_StartTextbox(globalCtx, 0x7123, NULL); + this->talkState++; + } + break; + case 2: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + EnZl4_SetCsCameraAngle(globalCtx, 13); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_19); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_SURPRISED; + Message_StartTextbox(globalCtx, 0x207C, NULL); + this->talkState++; + } + break; + case 3: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_20)) { + this->talkState++; + } + case 4: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_StartTextbox(globalCtx, 0x207D, NULL); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_7); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_NEUTRAL; + this->talkState = 5; + this->unk_20F = this->lastAction = 0; + } + break; + case 5: + if (EnZl4_SetNextAnim(this, ZL4_ANIM_8)) { + this->talkState++; + } + case 6: + if (!((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx))) { + break; + } else { + Camera_ChangeSetting(GET_ACTIVE_CAM(globalCtx), 1); + this->talkState = 7; + globalCtx->talkWithPlayer(globalCtx, &this->actor); + func_8002F434(&this->actor, globalCtx, GI_LETTER_ZELDA, fabsf(this->actor.xzDistToPlayer) + 1.0f, + fabsf(this->actor.yDistToPlayer) + 1.0f); + globalCtx->msgCtx.stateTimer = 4; + globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING; + } + break; + case 7: + if (Actor_HasParent(&this->actor, globalCtx)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_0); + this->talkState++; + } else { + func_8002F434(&this->actor, globalCtx, GI_LETTER_ZELDA, fabsf(this->actor.xzDistToPlayer) + 1.0f, + fabsf(this->actor.yDistToPlayer) + 1.0f); + } + // no break here is required for matching + } + return (this->talkState == 8) ? 1 : 0; +} + +void EnZl4_Cutscene(EnZl4* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + switch (this->csState) { + case ZL4_CS_WAIT: + if (EnZl4_CsWaitForPlayer(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_START: + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_3); + this->blinkTimer = 0; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_SURPRISED; + Audio_PlayFanfare(NA_BGM_APPEAR); + EnZl4_SetCsCameraAngle(globalCtx, 0); + Interface_ChangeAlpha(2); + ShrinkWindow_SetVal(0x20); + this->talkState = 0; + this->csState++; + break; + case ZL4_CS_MEET: + if (EnZl4_CsMeetPlayer(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_STONE: + if (EnZl4_CsAskStone(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_NAMES: + if (EnZl4_CsAskName(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_LEGEND: + if (EnZl4_CsTellLegend(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_WINDOW: + if (EnZl4_CsLookWindow(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_GANON: + if (EnZl4_CsWarnAboutGanon(this, globalCtx)) { + this->talkState = 0; + this->csState++; + } + break; + case ZL4_CS_PLAN: + if (EnZl4_CsMakePlan(this, globalCtx)) { + func_8002DF54(globalCtx, &this->actor, 7); + gSaveContext.unk_13EE = 0x32; + gSaveContext.eventChkInf[4] |= 1; + this->actionFunc = EnZl4_Idle; + } + break; + } + this->unk_1E0.unk_18 = player->actor.world.pos; + func_80034A14(&this->actor, &this->unk_1E0, 2, (this->csState == ZL4_CS_WINDOW) ? 2 : 1); + if (EnZl4_InMovingAnim(this)) { + EnZl4_SetMove(this, globalCtx); + } +} + +void EnZl4_Idle(EnZl4* this, GlobalContext* globalCtx) { + func_800343CC(globalCtx, &this->actor, &this->unk_1E0.unk_00, this->collider.dim.radius + 60.0f, EnZl4_GetText, + func_80B5B9B0); + func_80B5BB78(this, globalCtx); +} + +void EnZl4_TheEnd(EnZl4* this, GlobalContext* globalCtx) { + s32 animIndex[] = { ZL4_ANIM_0, ZL4_ANIM_0, ZL4_ANIM_0, ZL4_ANIM_0, ZL4_ANIM_0, + ZL4_ANIM_0, ZL4_ANIM_0, ZL4_ANIM_26, ZL4_ANIM_21, ZL4_ANIM_3 }; + CsCmdActorAction* npcAction; + Vec3f pos; + + if (SkelAnime_Update(&this->skelAnime) && (this->skelAnime.animation == &gChildZeldaAnim_010DF8)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ZL4_ANIM_4); + } + if (EnZl4_InMovingAnim(this)) { + EnZl4_SetMove(this, globalCtx); + } + if (globalCtx->csCtx.frames == 100) { + this->eyeExpression = ZL4_EYES_LOOK_LEFT; + } + if (globalCtx->csCtx.frames == 450) { + this->blinkTimer = 3; + this->eyeExpression = ZL4_EYES_NEUTRAL; + this->mouthExpression = ZL4_MOUTH_SURPRISED; + } + npcAction = globalCtx->csCtx.npcActions[0]; + if (npcAction != NULL) { + EnZl4_GetActionStartPos(npcAction, &pos); + if (this->lastAction == 0) { + this->actor.world.pos = this->actor.home.pos = pos; + } + if (this->lastAction != npcAction->action) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animIndex[npcAction->action]); + this->lastAction = npcAction->action; + } + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.z = 0.0f; + } +} + +void EnZl4_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnZl4* this = (EnZl4*)thisx; + + if (this->actionFunc != EnZl4_TheEnd) { + SkelAnime_Update(&this->skelAnime); + } + EnZl4_UpdateFace(this); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + this->actionFunc(this, globalCtx); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); +} + +s32 EnZl4_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx) { + EnZl4* this = (EnZl4*)thisx; + Vec3s sp1C; + + if (limbIndex == 17) { + sp1C = this->unk_1E0.unk_08; + Matrix_Translate(900.0f, 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateX((sp1C.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((sp1C.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-900.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + if (limbIndex == 10) { + sp1C = this->unk_1E0.unk_0E; + Matrix_RotateY((sp1C.y / (f32)0x8000) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((sp1C.x / (f32)0x8000) * M_PI, MTXMODE_APPLY); + } + if ((limbIndex >= 3) && (limbIndex < 7)) { + *dList = NULL; + } + return false; +} + +void EnZl4_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + EnZl4* this = (EnZl4*)thisx; + + if (limbIndex == 17) { + Matrix_MultVec3f(&zeroVec, &this->actor.focus.pos); + } +} + +void EnZl4_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnZl4* this = (EnZl4*)thisx; + void* mouthTex[] = { gChildZeldaMouthNeutralTex, gChildZeldaMouthHappyTex, gChildZeldaMouthWorriedTex, + gChildZeldaMouthSurprisedTex }; + void* eyeTex[] = { + gChildZeldaEyeOpenTex, gChildZeldaEyeBlinkTex, gChildZeldaEyeShutTex, gChildZeldaEyeWideTex, + gChildZeldaEyeSquintTex, gChildZeldaEyeOutTex, gChildZeldaEyeInTex, + }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zl4.c", 2012); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTex[this->rightEyeState])); + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(eyeTex[this->leftEyeState])); + gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(mouthTex[this->mouthState])); + func_80093D18(globalCtx->state.gfxCtx); + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, + EnZl4_OverrideLimbDraw, EnZl4_PostLimbDraw, this); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zl4.c", 2043); +} diff --git a/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.h b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.h new file mode 100644 index 000000000..ace675ddc --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4.h @@ -0,0 +1,34 @@ +#ifndef Z_EN_ZL4_H +#define Z_EN_ZL4_H + +#include "ultra64.h" +#include "global.h" + +struct EnZl4; + +typedef void (*EnZl4ActionFunc)(struct EnZl4*, GlobalContext*); +typedef void (*EnZl4DrawFunc)(struct EnZl4*, GlobalContext*); + +typedef struct EnZl4 { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnZl4ActionFunc actionFunc; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ struct_80034A14_arg1 unk_1E0; + /* 0x0208 */ u8 talkState; + /* 0x0209 */ u8 csState; + /* 0x020A */ u8 leftEyeState; + /* 0x020B */ u8 rightEyeState; + /* 0x020C */ u8 mouthState; + /* 0x020D */ u8 eyeExpression; + /* 0x020E */ u8 mouthExpression; + /* 0x020F */ u8 unk_20F; + /* 0x0210 */ s16 blinkTimer; + /* 0x0212 */ s16 talkTimer1; + /* 0x0214 */ s16 talkTimer2; + /* 0x0216 */ s16 lastAction; + /* 0x0218 */ Vec3s jointTable[18]; + /* 0x0284 */ Vec3s morphTable[18]; +} EnZl4; // size = 0x02F0 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4_cutscene_data.c b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4_cutscene_data.c new file mode 100644 index 000000000..63b8092d3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zl4/z_en_zl4_cutscene_data.c @@ -0,0 +1,186 @@ +#include "z_en_zl4.h" + +static CutsceneCameraAngle sCsCameraAngle[] = { + { { -490.0f, 120.0f, 0.0f }, { -440.0f, 117.0f, 0.0f }, 0, 45 }, + { { -484.0f, 122.0f, -29.0f }, { -480.0f, 116.0f, 18.0f }, 0, 80 }, + { { -413.0f, 136.0f, -72.0f }, { -403.0f, 141.0f, -89.0f }, 0, 25 }, + { { -454.0f, 120.0f, 0.0f }, { -434.0f, 121.0f, 0.0f }, 0, 20 }, + { { -453.0f, 107.0f, -13.0f }, { -440.0f, 102.0f, -25.0f }, 0, 40 }, + { { -454.0f, 105.0f, 50.0f }, { -453.0f, 105.0f, 66.0f }, 0, 60 }, + { { -496.0f, 119.0f, 0.0f }, { -443.0f, 115.0f, 0.0f }, 0, 45 }, + { { -482.0f, 119.0f, 0.0f }, { -440.0f, 115.0f, 0.0f }, 0, 30 }, + { { -551.0f, 119.0f, 7.0f }, { -587.0f, 115.0f, 14.0f }, 0, 25 }, + { { -489.0f, 129.0f, 0.0f }, { -470.0f, 128.0f, 0.0f }, 0, 40 }, + { { -525.0f, 126.0f, 0.0f }, { -509.0f, 126.0f, 0.0f }, 0, 10 }, + { { -491.0f, 120.0f, -7.0f }, { -509.0f, 115.0f, -7.0f }, 0, 75 }, + { { -485.0f, 119.0f, -35.0f }, { -484.0f, 116.0f, 15.0f }, 0, 45 }, + { { -484.0f, 123.0f, -6.0f }, { -435.0f, 122.0f, -11.0f }, 0, 45 }, +}; + +static CutsceneCameraPoint D_80B5EC70[] = { + { 0, 0, 20, 80.79987f, { -484, 122, -29 } }, { 0, 0, 20, 80.79987f, { -484, 122, -29 } }, + { 0, 0, 20, 80.79987f, { -484, 122, -29 } }, { 0, 0, 20, 80.79987f, { -452, 121, -21 } }, + { 0, 0, 20, 80.79987f, { -452, 121, -21 } }, { 0, 0, 20, 80.79987f, { -452, 121, -21 } }, + { -1, 0, 20, 80.79987f, { -452, 121, -21 } }, { -1, 0, 30, 80.79987f, { -452, 121, -21 } }, +}; +static CutsceneCameraPoint D_80B5ECF0[] = { + { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, + { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, + { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, { 0, 0, 0, 80.79987f, { -480, 116, 18 } }, + { -1, 0, 0, 80.79987f, { -480, 116, 18 } }, { -1, 0, 0, 80.79987f, { -480, 116, 18 } }, +}; +static CutsceneCameraPoint D_80B5ED70[] = { + { 0, 0, 20, 45.200058f, { -439, 116, 0 } }, { 0, 0, 20, 45.200058f, { -439, 116, 0 } }, + { 0, 0, 20, 50.60008f, { -433, 116, 0 } }, { 0, 0, 20, 55.600098f, { -431, 116, 0 } }, + { 0, 0, 20, 60.000114f, { -427, 116, 0 } }, { 0, 0, 20, 65.000114f, { -424, 116, 0 } }, + { 0, 0, 20, 70.800026f, { -422, 116, 0 } }, { 0, 0, 20, 75.59995f, { -419, 115, 0 } }, + { -1, 0, 20, 75.59995f, { -419, 116, 0 } }, { -1, 0, 30, 75.59995f, { -419, 115, 0 } }, +}; +static CutsceneCameraPoint D_80B5EE10[] = { + { 0, 0, 0, 60.400116f, { -480, 114, 0 } }, { 0, 0, 0, 45.200058f, { -480, 114, 0 } }, + { 0, 0, 0, 45.200058f, { -474, 114, 0 } }, { 0, 0, 0, 50.60008f, { -472, 114, 0 } }, + { 0, 0, 0, 55.600098f, { -468, 114, 0 } }, { 0, 0, 0, 60.000114f, { -465, 114, 0 } }, + { 0, 0, 0, 65.000114f, { -463, 114, 0 } }, { 0, 0, 0, 70.800026f, { -460, 114, 0 } }, + { -1, 0, 0, 75.59995f, { -460, 114, 0 } }, { -1, 0, 0, 75.59995f, { -460, 114, 0 } }, +}; +static CutsceneCameraPoint D_80B5EEB0[] = { + { 0, 0, 25, 60.000114f, { -116, 50, 469 } }, { 0, 0, 25, 60.000114f, { -116, 50, 469 } }, + { 0, 0, 25, 60.000114f, { -118, 50, 467 } }, { 0, 0, 25, 60.000114f, { -120, 50, 465 } }, + { 0, 0, 25, 60.000114f, { -123, 50, 464 } }, { 0, 0, 25, 60.000114f, { -126, 50, 463 } }, + { 0, 0, 25, 60.000114f, { -129, 50, 462 } }, { 0, 0, 25, 60.000114f, { -131, 50, 462 } }, + { 0, 0, 25, 60.000114f, { -134, 50, 463 } }, { 0, 0, 25, 60.000114f, { -137, 50, 464 } }, + { 0, 0, 25, 60.000114f, { -140, 50, 466 } }, { 0, 0, 25, 60.000114f, { -140, 50, 466 } }, + { -1, 0, 25, 60.000114f, { -140, 50, 466 } }, { -1, 0, 25, 60.000114f, { -140, 50, 466 } }, +}; +static CutsceneCameraPoint D_80B5EF90[] = { + { 0, 0, 0, 20.399963f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, { 0, 0, 0, 60.000114f, { -130, 50, 480 } }, + { -1, 0, 0, 60.000114f, { -130, 50, 480 } }, { -1, 0, 0, 60.000114f, { -130, 50, 480 } }, +}; +static CutsceneCameraPoint D_80B5F070[] = { + { 0, 0, 30, 20.799965f, { -427, 116, 4 } }, { 0, 0, 30, 20.799965f, { -426, 115, 4 } }, + { 0, 0, 30, 20.799965f, { -429, 115, 7 } }, { 0, 0, 30, 20.799965f, { -428, 114, 12 } }, + { 0, 0, 30, 20.799965f, { -430, 114, 14 } }, { 0, 0, 30, 20.799965f, { -430, 114, 14 } }, + { 0, 0, 30, 20.799965f, { -430, 114, 14 } }, { -1, 0, 30, 20.799965f, { -430, 112, 14 } }, + { -1, 0, 30, 20.799965f, { -430, 112, 14 } }, +}; +static CutsceneCameraPoint D_80B5F100[] = { + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, + { 0, 0, 0, 20.799965f, { -389, 109, 36 } }, { -1, 0, 0, 20.799965f, { -389, 109, 36 } }, + { -1, 0, 0, 20.799965f, { -389, 109, 36 } }, +}; +static CutsceneCameraPoint D_80B5F190[] = { + { 0, 0, 30, 45.0f, { -496, 119, 0 } }, { 0, 0, 30, 45.0f, { -496, 119, 0 } }, + { 0, 0, 30, 45.0f, { -496, 119, 0 } }, { 0, 0, 15, 45.0f, { -496, 119, 0 } }, + { 0, 0, 15, 45.80006f, { -471, 122, 0 } }, { 0, 0, 15, 45.80006f, { -395, 139, 0 } }, + { 0, 0, 15, 45.80006f, { -193, 183, 0 } }, { 0, 0, 15, 45.80006f, { 29, 232, 0 } }, + { 0, 0, 30, 45.80006f, { 360, 304, 0 } }, { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, + { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, + { 0, 0, 30, 45.80006f, { 429, 328, 0 } }, { -1, 0, 30, 45.80006f, { 429, 328, 0 } }, + { -1, 0, 30, 45.80006f, { 429, 328, 0 } }, +}; +static CutsceneCameraPoint D_80B5F280[] = { + { 0, 0, 0, 45.0f, { -443, 115, 0 } }, { 0, 0, 0, 45.0f, { -443, 115, 0 } }, + { 0, 0, 0, 45.0f, { -443, 115, 0 } }, { 0, 0, 0, 45.0f, { -443, 115, 0 } }, + { 0, 0, 0, 45.80006f, { -420, 133, 0 } }, { 0, 0, 0, 45.80006f, { -344, 150, 0 } }, + { 0, 0, 0, 45.80006f, { -143, 194, 0 } }, { 0, 0, 0, 45.80006f, { 80, 243, 0 } }, + { 0, 0, 0, 45.80006f, { 412, 315, 0 } }, { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, + { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, + { 0, 0, 0, 45.80006f, { 482, 332, 0 } }, { -1, 0, 0, 45.80006f, { 482, 332, 0 } }, + { -1, 0, 0, 45.80006f, { 482, 332, 0 } }, +}; +static CutsceneCameraPoint D_80B5F370[] = { + { 0, 0, 25, 60.600117f, { 66, 404, 425 } }, { 0, 0, 25, 60.400116f, { 66, 404, 425 } }, + { 0, 0, 25, 55.600098f, { 66, 404, 426 } }, { 0, 0, 25, 55.200096f, { 63, 373, 413 } }, + { 0, 0, 25, 50.400078f, { 26, 353, 408 } }, { 0, 0, 25, 50.400078f, { 17, 325, 397 } }, + { 0, 0, 25, 45.40006f, { 17, 326, 397 } }, { 0, 0, 25, 45.200058f, { -136, 177, 259 } }, + { 0, 0, 25, 40.40004f, { -258, 111, 169 } }, { 0, 0, 25, 40.20004f, { -377, 108, 65 } }, + { 0, 0, 25, 35.20002f, { -377, 108, 65 } }, { 0, 0, 25, 35.20002f, { -377, 108, 65 } }, + { -1, 0, 30, 30.2f, { -376, 108, 65 } }, { -1, 0, 30, 30.2f, { -376, 108, 65 } }, +}; +static CutsceneCameraPoint D_80B5F450[] = { + { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, + { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, + { 0, 0, 0, 45.80006f, { 29, 383, 445 } }, { 0, 0, 0, 45.80006f, { 49, 347, 424 } }, + { 0, 0, 0, 45.80006f, { 49, 347, 424 } }, { 0, 0, 0, 30.2f, { -103, 192, 286 } }, + { 0, 0, 0, 30.2f, { -224, 113, 198 } }, { 0, 0, 0, 30.2f, { -345, 109, 96 } }, + { 0, 0, 0, 30.2f, { -345, 109, 96 } }, { 0, 0, 0, 30.2f, { -345, 109, 96 } }, + { -1, 0, 0, 30.2f, { -345, 109, 96 } }, { -1, 0, 0, 30.2f, { -345, 109, 96 } }, +}; +static CutsceneCameraPoint D_80B5F530[] = { + { 0, 0, 10, 30.0f, { -482, 119, 0 } }, { 0, 0, 10, 30.0f, { -482, 119, 0 } }, + { 0, 0, 10, 30.0f, { -482, 119, 0 } }, { 0, 0, 10, 30.800003f, { -482, 119, 0 } }, + { 0, 0, 10, 30.800003f, { -482, 119, 0 } }, { 0, 0, 10, 30.800003f, { -462, 126, 0 } }, + { 0, 0, 30, 30.800003f, { -395, 150, 0 } }, { 0, 0, 30, 30.800003f, { -395, 150, 0 } }, + { -1, 0, 30, 30.800003f, { -395, 150, 0 } }, { -1, 0, 30, 30.800003f, { -395, 150, 0 } }, +}; +static CutsceneCameraPoint D_80B5F5D0[] = { + { 0, 0, 0, 30.0f, { -440, 115, 0 } }, { 0, 0, 0, 30.0f, { -440, 115, 0 } }, + { 0, 0, 0, 30.0f, { -440, 115, 0 } }, { 0, 0, 0, 30.800003f, { -440, 115, 0 } }, + { 0, 0, 0, 30.800003f, { -440, 115, 0 } }, { 0, 0, 0, 30.800003f, { -423, 142, 0 } }, + { 0, 0, 0, 30.800003f, { -358, 171, 0 } }, { 0, 0, 0, 30.800003f, { -358, 171, 0 } }, + { -1, 0, 0, 30.800003f, { -358, 171, 0 } }, { -1, 0, 0, 30.800003f, { -358, 171, 0 } }, +}; +static CutsceneCameraPoint D_80B5F670[] = { + { 0, 0, 30, 25.0f, { -551, 119, 7 } }, { 0, 0, 30, 25.0f, { -551, 119, 7 } }, + { 0, 0, 15, 25.0f, { -551, 119, 7 } }, { 0, 0, 15, 60.0f, { -485, 120, -7 } }, + { 0, 0, 30, 60.0f, { -485, 120, -7 } }, { 0, 0, 30, 60.0f, { -485, 120, -7 } }, + { 0, 0, 30, 60.0f, { -485, 118, -5 } }, { -1, 0, 30, 60.0f, { -485, 119, -6 } }, + { -1, 0, 30, 60.0f, { -485, 119, -6 } }, +}; +static CutsceneCameraPoint D_80B5F700[] = { + { 0, 0, 0, 45.200058f, { -587, 115, 14 } }, { 0, 0, 0, 25.400097f, { -587, 115, 14 } }, + { 0, 0, 0, 25.400097f, { -587, 115, 14 } }, { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, + { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, + { 0, 0, 0, 60.20023f, { -521, 117, -1 } }, { -1, 0, 0, 60.20023f, { -521, 116, 0 } }, + { -1, 0, 0, 60.19925f, { -521, 116, 0 } }, +}; +static CutsceneCameraPoint D_80B5F790[] = { + { 0, 0, 30, 60.000114f, { 75, 52, 50 } }, { 0, 0, 30, 60.000114f, { 75, 52, 48 } }, + { 0, 0, 60, 60.000114f, { 74, 52, 45 } }, { 0, 0, 60, 60.000114f, { 49, 51, -43 } }, + { 0, 0, 30, 60.000114f, { 49, 51, -43 } }, { 0, 0, 30, 60.000114f, { 49, 51, -43 } }, + { -1, 0, 30, 60.000114f, { 49, 51, -43 } }, { -1, 0, 30, 60.000114f, { 49, 51, -43 } }, +}; +static CutsceneCameraPoint D_80B5F810[] = { + { 0, 0, 0, 60.000114f, { 90, 52, 53 } }, { 0, 0, 0, 60.000114f, { 90, 52, 50 } }, + { 0, 0, 0, 60.000114f, { 90, 52, 45 } }, { 0, 0, 0, 60.000114f, { 65, 51, -44 } }, + { 0, 0, 0, 60.000114f, { 65, 51, -44 } }, { 0, 0, 0, 60.000114f, { 65, 51, -44 } }, + { -1, 0, 0, 60.000114f, { 65, 51, -44 } }, { -1, 0, 0, 60.000114f, { 65, 51, -44 } }, +}; +static CutsceneCameraPoint D_80B5F890[] = { + { 0, 0, 31, 60.000114f, { -449, 121, -19 } }, { 0, 0, 30, 60.000114f, { -449, 121, -19 } }, + { 0, 0, 30, 60.000114f, { -449, 121, -19 } }, { 0, 0, 30, 60.000114f, { -456, 110, -17 } }, + { 0, 0, 30, 60.000114f, { -456, 110, -17 } }, { 0, 0, 30, 60.000114f, { -456, 110, -17 } }, + { -1, 0, 30, 60.000114f, { -456, 110, -17 } }, { -1, 0, 30, 60.000114f, { -456, 110, -17 } }, +}; +static CutsceneCameraPoint D_80B5F910[] = { + { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, + { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, + { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, { 0, 0, 0, 60.000114f, { -441, 107, -22 } }, + { -1, 0, 0, 60.000114f, { -441, 107, -22 } }, { -1, 0, 0, 60.000114f, { -441, 107, -22 } }, +}; +static CutsceneCameraPoint D_80B5F990[] = { + { 0, 0, 30, 20.799965f, { -331, 110, -91 } }, { 0, 0, 30, 20.799965f, { -331, 110, -91 } }, + { 0, 0, 30, 20.799965f, { -331, 110, -91 } }, { 0, 0, 30, 20.799965f, { -511, 121, 7 } }, + { 0, 0, 30, 20.799965f, { -511, 121, 7 } }, { 0, 0, 30, 20.799965f, { -511, 121, 7 } }, + { -1, 0, 30, 20.799965f, { -511, 121, 7 } }, { -1, 0, 30, 20.799965f, { -511, 121, 7 } }, +}; +static CutsceneCameraPoint D_80B5FA10[] = { + { 0, 0, 0, 20.799965f, { -369, 110, -70 } }, { 0, 0, 0, 20.799965f, { -369, 110, -70 } }, + { 0, 0, 0, 20.799965f, { -369, 110, -70 } }, { 0, 0, 0, 20.799965f, { -549, 124, 29 } }, + { 0, 0, 0, 20.799965f, { -549, 124, 29 } }, { 0, 0, 0, 20.799965f, { -549, 124, 29 } }, + { -1, 0, 0, 20.799965f, { -549, 124, 29 } }, { -1, 0, 0, 20.799965f, { -549, 124, 29 } }, +}; + +static CutsceneCameraMove sCsCameraMove[] = { + { D_80B5EC70, D_80B5ECF0, 0 }, { D_80B5ED70, D_80B5EE10, 0 }, { D_80B5EEB0, D_80B5EF90, 0 }, + { D_80B5F070, D_80B5F100, 0 }, { D_80B5F190, D_80B5F280, 0 }, { D_80B5F370, D_80B5F450, 0 }, + { D_80B5F530, D_80B5F5D0, 0 }, { D_80B5F670, D_80B5F700, 0 }, { D_80B5F790, D_80B5F810, 0 }, + { D_80B5F890, D_80B5F910, 0 }, { D_80B5F990, D_80B5FA10, 0 }, +}; diff --git a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c new file mode 100644 index 000000000..b55777484 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.c @@ -0,0 +1,812 @@ +/* + * File: z_en_zo.c + * Overlay: ovl_En_Zo + * Description: Zora + */ + +#include "z_en_zo.h" +#include "objects/object_zo/object_zo.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +typedef enum { + /* 0 */ ENZO_EFFECT_NONE, + /* 1 */ ENZO_EFFECT_RIPPLE, + /* 2 */ ENZO_EFFECT_SPLASH, + /* 3 */ ENZO_EFFECT_BUBBLE +} EnZoEffectType; + +void EnZo_Init(Actor* thisx, GlobalContext* globalCtx); +void EnZo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnZo_Update(Actor* thisx, GlobalContext* globalCtx); +void EnZo_Draw(Actor* thisx, GlobalContext* globalCtx); + +// Actions +void EnZo_Standing(EnZo* this, GlobalContext* globalCtx); +void EnZo_Submerged(EnZo* this, GlobalContext* globalCtx); +void EnZo_Surface(EnZo* this, GlobalContext* globalCtx); +void EnZo_TreadWater(EnZo* this, GlobalContext* globalCtx); +void EnZo_Dive(EnZo* this, GlobalContext* globalCtx); + +void EnZo_Ripple(EnZo* this, Vec3f* pos, f32 scale, f32 targetScale, u8 alpha) { + EnZoEffect* effect; + Vec3f vec = { 0.0f, 0.0f, 0.0f }; + s16 i; + + effect = this->effects; + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_NONE) { + effect->type = ENZO_EFFECT_RIPPLE; + effect->pos = *pos; + effect->scale = scale; + effect->targetScale = targetScale; + effect->color.a = alpha; + break; + } + effect++; + } +} + +void EnZo_Bubble(EnZo* this, Vec3f* pos) { + EnZoEffect* effect; + Vec3f vec = { 0.0f, 0.0f, 0.0f }; + Vec3f vel = { 0.0f, 1.0f, 0.0f }; + s16 i; + f32 waterSurface; + + effect = this->effects; + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (1) {} + if (effect->type == ENZO_EFFECT_NONE) { + waterSurface = this->actor.world.pos.y + this->actor.yDistToWater; + if (!(waterSurface <= pos->y)) { + effect->type = ENZO_EFFECT_BUBBLE; + effect->pos = *pos; + effect->vec = *pos; + effect->vel = vel; + effect->scale = ((Rand_ZeroOne() - 0.5f) * 0.02f) + 0.12f; + break; + } + } + effect++; + } +} + +void EnZo_Splash(EnZo* this, Vec3f* pos, Vec3f* vel, f32 scale) { + EnZoEffect* effect; + Vec3f accel = { 0.0f, -1.0f, 0.0f }; + s16 i; + + effect = this->effects; + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (1) {} + if (effect->type != ENZO_EFFECT_SPLASH) { + effect->type = ENZO_EFFECT_SPLASH; + effect->pos = *pos; + effect->vec = accel; + effect->vel = *vel; + effect->color.a = (Rand_ZeroOne() * 100.0f) + 100.0f; + effect->scale = scale; + break; + } + effect++; + } +} + +void EnZo_UpdateRipples(EnZo* this) { + EnZoEffect* effect = this->effects; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_RIPPLE) { + Math_ApproachF(&effect->scale, effect->targetScale, 0.2f, 0.8f); + if (effect->color.a > 20) { + effect->color.a -= 20; + } else { + effect->color.a = 0; + } + + if (effect->color.a == 0) { + effect->type = ENZO_EFFECT_NONE; + } + } + effect++; + } +} + +void EnZo_UpdateBubbles(EnZo* this) { + EnZoEffect* effect; + f32 waterSurface; + s16 i; + + effect = this->effects; + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_BUBBLE) { + effect->pos.x = ((Rand_ZeroOne() * 0.5f) - 0.25f) + effect->vec.x; + effect->pos.z = ((Rand_ZeroOne() * 0.5f) - 0.25f) + effect->vec.z; + effect->pos.y += effect->vel.y; + + // Bubbles turn into ripples when they reach the surface + waterSurface = this->actor.world.pos.y + this->actor.yDistToWater; + if (waterSurface <= effect->pos.y) { + effect->type = ENZO_EFFECT_NONE; + effect->pos.y = waterSurface; + EnZo_Ripple(this, &effect->pos, 0.06f, 0.12f, 200); + } + } + effect++; + } +} + +void EnZo_UpdateSplashes(EnZo* this) { + EnZoEffect* effect; + f32 waterSurface; + s16 i; + + effect = this->effects; + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_SPLASH) { + effect->pos.x += effect->vel.x; + effect->pos.y += effect->vel.y; + effect->pos.z += effect->vel.z; + + if (effect->vel.y >= -20.0f) { + effect->vel.y += effect->vec.y; + } else { + effect->vel.y = -20.0f; + effect->vec.y = 0.0f; + } + + // Splash particles turn into ripples when they hit the surface + waterSurface = this->actor.world.pos.y + this->actor.yDistToWater; + if (effect->pos.y < waterSurface) { + effect->type = ENZO_EFFECT_NONE; + effect->pos.y = waterSurface; + EnZo_Ripple(this, &effect->pos, 0.06f, 0.12f, 200); + } + } + effect++; + } +} + +void EnZo_DrawRipples(EnZo* this, GlobalContext* globalCtx) { + EnZoEffect* effect; + s16 i; + u8 setup; + + effect = this->effects; + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 217); + setup = false; + func_80093D84(globalCtx->state.gfxCtx); + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_RIPPLE) { + if (!setup) { + if (1) {} + gDPPipeSync(POLY_XLU_DISP++); + gSPDisplayList(POLY_XLU_DISP++, gZoraRipplesMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0); + setup = true; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, effect->color.a); + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, 1.0f, effect->scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 242), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gZoraRipplesModelDL); + } + effect++; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 248); +} + +void EnZo_DrawBubbles(EnZo* this, GlobalContext* globalCtx) { + EnZoEffect* effect = this->effects; + s16 i; + u8 setup; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 260); + setup = false; + func_80093D84(globalCtx->state.gfxCtx); + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_BUBBLE) { + if (!setup) { + if (1) {} + gSPDisplayList(POLY_XLU_DISP++, gZoraBubblesMaterialDL); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 150, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + + setup = true; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 281), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gZoraBubblesModelDL); + } + effect++; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 286); +} + +void EnZo_DrawSplashes(EnZo* this, GlobalContext* globalCtx) { + EnZoEffect* effect; + s16 i; + u8 setup; + + effect = this->effects; + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 298); + setup = false; + func_80093D84(globalCtx->state.gfxCtx); + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + if (effect->type == ENZO_EFFECT_SPLASH) { + if (!setup) { + if (1) {} + gSPDisplayList(POLY_XLU_DISP++, gZoraSplashesMaterialDL); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, 0); + setup = true; + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 180, 180, 180, effect->color.a); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 325), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gZoraSplashesModelDL); + } + effect++; + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zo_eff.c", 331); +} + +void EnZo_TreadWaterRipples(EnZo* this, f32 scale, f32 targetScale, u8 alpha) { + Vec3f pos = { 0.0f, 0.0f, 0.0f }; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + this->actor.yDistToWater; + pos.z = this->actor.world.pos.z; + EnZo_Ripple(this, &pos, scale, targetScale, alpha); +} + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 26, 64, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit2 sColChkInit = { 0, 0, 0, 0, MASS_IMMOVABLE }; + +const ActorInit En_Zo_InitVars = { + ACTOR_EN_ZO, + ACTORCAT_NPC, + FLAGS, + OBJECT_ZO, + sizeof(EnZo), + (ActorFunc)EnZo_Init, + (ActorFunc)EnZo_Destroy, + (ActorFunc)EnZo_Update, + (ActorFunc)EnZo_Draw, + NULL, +}; + +typedef enum { + /* 0 */ ENZO_ANIM_0, + /* 1 */ ENZO_ANIM_1, + /* 2 */ ENZO_ANIM_2, + /* 3 */ ENZO_ANIM_3, + /* 4 */ ENZO_ANIM_4, + /* 5 */ ENZO_ANIM_5, + /* 6 */ ENZO_ANIM_6, + /* 7 */ ENZO_ANIM_7 +} EnZoAnimation; + +static AnimationInfo sAnimationInfo[] = { + { &gZoraIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gZoraIdleAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, 0.0f }, + { &gZoraSurfaceAnim, 0.0f, 1.0f, 1.0f, ANIMMODE_ONCE, 0.0f }, + { &gZoraSurfaceAnim, 1.0f, 1.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gZoraSurfaceAnim, 1.0f, 8.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gZoraThrowRupeesAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gZoraHandsOnHipsTappingFootAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, + { &gZoraOpenArmsAnim, 1.0f, 0.0f, -1.0f, ANIMMODE_LOOP, -8.0f }, +}; + +void EnZo_SpawnSplashes(EnZo* this) { + Vec3f pos; + Vec3f vel; + s32 i; + + // Convert 20 particles into splashes (all of them since there are only 15) + for (i = 0; i < 20; i++) { + f32 speed = Rand_ZeroOne() * 1.5f + 0.5f; + f32 angle = Rand_ZeroOne() * 6.28f; // ~pi * 2 + + vel.y = Rand_ZeroOne() * 3.0f + 3.0f; + + vel.x = sinf(angle) * speed; + vel.z = cosf(angle) * speed; + + pos = this->actor.world.pos; + pos.x += vel.x * 6.0f; + pos.z += vel.z * 6.0f; + pos.y += this->actor.yDistToWater; + EnZo_Splash(this, &pos, &vel, 0.08f); + } +} + +u16 func_80B61024(GlobalContext* globalCtx, Actor* thisx) { + u16 textId; + + textId = Text_GetFaceReaction(globalCtx, 29); + if (textId != 0) { + return textId; + } + + switch (thisx->params & 0x3F) { + case 8: + if (gSaveContext.eventChkInf[3] & 1) { + return 0x402A; + } + break; + + case 6: + return 0x4020; + + case 7: + return 0x4021; + + case 0: + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402D; + } + if (gSaveContext.eventChkInf[3] & 1) { + return 0x4007; + } + break; + + case 1: + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402E; + } + + if (gSaveContext.eventChkInf[3] & 1) { + return (gSaveContext.infTable[18] & 0x10) ? 0x4009 : 0x4008; + } + break; + + case 2: + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402D; + } + if (gSaveContext.eventChkInf[3] & 2) { + return (gSaveContext.infTable[18] & 0x200) ? 0x400B : 0x402F; + } + if (gSaveContext.eventChkInf[3] & 1) { + return 0x400A; + } + break; + + case 3: + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402E; + } + if (gSaveContext.eventChkInf[3] & 1) { + return 0x400C; + } + break; + + case 4: + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402D; + } + + if (gSaveContext.eventChkInf[3] & 8) { + return 0x4010; + } + if (gSaveContext.eventChkInf[3] & 1) { + return 0x400F; + } + break; + + case 5: + if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { + return 0x402E; + } + if (gSaveContext.eventChkInf[3] & 1) { + return 0x4011; + } + break; + } + return 0x4006; +} + +s16 func_80B61298(GlobalContext* globalCtx, Actor* thisx) { + switch (Message_GetState(&globalCtx->msgCtx)) { + case TEXT_STATE_NONE: + case TEXT_STATE_DONE_HAS_NEXT: + case TEXT_STATE_DONE_FADING: + case TEXT_STATE_DONE: + case TEXT_STATE_SONG_DEMO_DONE: + case TEXT_STATE_8: + case TEXT_STATE_9: + return 1; + + case TEXT_STATE_CLOSING: + switch (thisx->textId) { + case 0x4020: + case 0x4021: + return 0; + case 0x4008: + gSaveContext.infTable[18] |= 0x10; + break; + case 0x402F: + gSaveContext.infTable[18] |= 0x200; + break; + } + gSaveContext.eventChkInf[3] |= 1; + return 0; + + case TEXT_STATE_CHOICE: + switch (Message_ShouldAdvance(globalCtx)) { + case 0: + return 1; + default: + if (thisx->textId == 0x400C) { + thisx->textId = (globalCtx->msgCtx.choiceIndex == 0) ? 0x400D : 0x400E; + Message_ContinueTextbox(globalCtx, thisx->textId); + } + break; + } + return 1; + + case TEXT_STATE_EVENT: + switch (Message_ShouldAdvance(globalCtx)) { + case 0: + return 1; + default: + return 2; + } + } + + return 1; +} + +void EnZo_Blink(EnZo* this) { + if (DECR(this->blinkTimer) == 0) { + this->eyeTexture++; + if (this->eyeTexture >= 3) { + this->blinkTimer = Rand_S16Offset(30, 30); + this->eyeTexture = 0; + } + } +} + +void EnZo_Dialog(EnZo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->unk_194.unk_18 = player->actor.world.pos; + if (this->actionFunc == EnZo_Standing) { + // Look down at link if young, look up if old + this->unk_194.unk_14 = !LINK_IS_ADULT ? 10.0f : -10.0f; + } else { + this->unk_194.unk_18.y = this->actor.world.pos.y; + } + func_80034A14(&this->actor, &this->unk_194, 11, this->unk_64C); + if (this->canSpeak == true) { + func_800343CC(globalCtx, &this->actor, &this->unk_194.unk_00, this->dialogRadius, func_80B61024, func_80B61298); + } +} + +s32 EnZo_PlayerInProximity(EnZo* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + Vec3f surfacePos; + f32 yDist; + f32 hDist; + + surfacePos.x = this->actor.world.pos.x; + surfacePos.y = this->actor.world.pos.y + this->actor.yDistToWater; + surfacePos.z = this->actor.world.pos.z; + + hDist = Math_Vec3f_DistXZ(&surfacePos, &player->actor.world.pos); + yDist = fabsf(player->actor.world.pos.y - surfacePos.y); + + if (hDist < 240.0f && yDist < 80.0f) { + return 1; + } + return 0; +} + +void EnZo_SetAnimation(EnZo* this) { + s32 animId = ARRAY_COUNT(sAnimationInfo); + + if (this->skelAnime.animation == &gZoraHandsOnHipsTappingFootAnim || + this->skelAnime.animation == &gZoraOpenArmsAnim) { + if (this->unk_194.unk_00 == 0) { + if (this->actionFunc == EnZo_Standing) { + animId = ENZO_ANIM_0; + } else { + animId = ENZO_ANIM_3; + } + } + } + + if (this->unk_194.unk_00 != 0 && this->actor.textId == 0x4006 && + this->skelAnime.animation != &gZoraHandsOnHipsTappingFootAnim) { + animId = ENZO_ANIM_6; + } + + if (this->unk_194.unk_00 != 0 && this->actor.textId == 0x4007 && this->skelAnime.animation != &gZoraOpenArmsAnim) { + animId = ENZO_ANIM_7; + } + + if (animId != ARRAY_COUNT(sAnimationInfo)) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, animId); + if (animId == ENZO_ANIM_3) { + this->skelAnime.curFrame = this->skelAnime.endFrame; + this->skelAnime.playSpeed = 0.0f; + } + } +} + +void EnZo_Init(Actor* thisx, GlobalContext* globalCtx) { + EnZo* this = (EnZo*)thisx; + + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZoraSkel, NULL, this->jointTable, this->morphTable, 20); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + CollisionCheck_SetInfo2(&this->actor.colChkInfo, NULL, &sColChkInit); + + if (LINK_IS_ADULT && ((this->actor.params & 0x3F) == 8)) { + Actor_Kill(&this->actor); + return; + } + + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENZO_ANIM_2); + Actor_SetScale(&this->actor, 0.01f); + this->actor.targetMode = 6; + this->dialogRadius = this->collider.dim.radius + 30.0f; + this->unk_64C = 1; + this->canSpeak = false; + this->unk_194.unk_00 = 0; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, this->collider.dim.height * 0.5f, this->collider.dim.radius, 0.0f, + 5); + + if (this->actor.yDistToWater < 54.0f || (this->actor.params & 0x3F) == 8) { + this->actor.shape.shadowDraw = ActorShadow_DrawCircle; + this->actor.shape.shadowScale = 24.0f; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENZO_ANIM_1); + this->canSpeak = true; + this->alpha = 255.0f; + this->actionFunc = EnZo_Standing; + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + this->actionFunc = EnZo_Submerged; + } +} + +void EnZo_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EnZo_Standing(EnZo* this, GlobalContext* globalCtx) { + s16 angle; + + func_80034F54(globalCtx, this->unk_656, this->unk_67E, 20); + EnZo_SetAnimation(this); + if (this->unk_194.unk_00 != 0) { + this->unk_64C = 4; + return; + } + + angle = ABS((s16)((f32)this->actor.yawTowardsPlayer - (f32)this->actor.shape.rot.y)); + if (angle < 0x4718) { + if (EnZo_PlayerInProximity(this, globalCtx)) { + this->unk_64C = 2; + } else { + this->unk_64C = 1; + } + } else { + this->unk_64C = 1; + } +} + +void EnZo_Submerged(EnZo* this, GlobalContext* globalCtx) { + if (EnZo_PlayerInProximity(this, globalCtx)) { + this->actionFunc = EnZo_Surface; + this->actor.velocity.y = 4.0f; + } +} + +void EnZo_Surface(EnZo* this, GlobalContext* globalCtx) { + if (this->actor.yDistToWater < 54.0f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_OUT_OF_WATER); + EnZo_SpawnSplashes(this); + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENZO_ANIM_3); + this->actor.flags |= ACTOR_FLAG_0; + this->actionFunc = EnZo_TreadWater; + this->actor.velocity.y = 0.0f; + this->alpha = 255.0f; + } else if (this->actor.yDistToWater < 80.0f) { + Math_ApproachF(&this->actor.velocity.y, 2.0f, 0.4f, 0.6f); + Math_ApproachF(&this->alpha, 255.0f, 0.3f, 10.0f); + } +} + +void EnZo_TreadWater(EnZo* this, GlobalContext* globalCtx) { + func_80034F54(globalCtx, this->unk_656, this->unk_67E, 20); + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + this->canSpeak = true; + this->unk_64C = 4; + this->skelAnime.playSpeed = 0.0f; + } + EnZo_SetAnimation(this); + + Math_ApproachF(&this->actor.velocity.y, this->actor.yDistToWater < 54.0f ? -0.6f : 0.6f, 0.3f, 0.2f); + if (this->rippleTimer != 0) { + this->rippleTimer--; + if ((this->rippleTimer == 3) || (this->rippleTimer == 6)) { + EnZo_TreadWaterRipples(this, 0.2f, 1.0f, 200); + } + } else { + EnZo_TreadWaterRipples(this, 0.2f, 1.0f, 200); + this->rippleTimer = 12; + } + + if (EnZo_PlayerInProximity(this, globalCtx) != 0) { + this->timeToDive = Rand_S16Offset(40, 40); + } else if (DECR(this->timeToDive) == 0) { + f32 startFrame; + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENZO_ANIM_4); + this->canSpeak = false; + this->unk_64C = 1; + this->actionFunc = EnZo_Dive; + startFrame = this->skelAnime.startFrame; + this->skelAnime.startFrame = this->skelAnime.endFrame; + this->skelAnime.curFrame = this->skelAnime.endFrame; + this->skelAnime.endFrame = startFrame; + this->skelAnime.playSpeed = -1.0f; + } +} + +void EnZo_Dive(EnZo* this, GlobalContext* globalCtx) { + if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_DIVE_WATER); + EnZo_SpawnSplashes(this); + this->actor.flags &= ~ACTOR_FLAG_0; + this->actor.velocity.y = -4.0f; + this->skelAnime.playSpeed = 0.0f; + } + + if (this->skelAnime.playSpeed > 0.0f) { + return; + } + + if (this->actor.yDistToWater > 80.0f || this->actor.bgCheckFlags & 1) { + Math_ApproachF(&this->actor.velocity.y, -1.0f, 0.4f, 0.6f); + Math_ApproachF(&this->alpha, 0.0f, 0.3f, 10.0f); + } + + if ((s16)this->alpha == 0) { + Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENZO_ANIM_2); + this->actor.world.pos = this->actor.home.pos; + this->alpha = 0.0f; + this->actionFunc = EnZo_Submerged; + } +} + +void EnZo_Update(Actor* thisx, GlobalContext* globalCtx) { + EnZo* this = (EnZo*)thisx; + u32 pad; + Vec3f pos; + + if ((s32)this->alpha != 0) { + SkelAnime_Update(&this->skelAnime); + EnZo_Blink(this); + } + + Actor_MoveForward(thisx); + Actor_UpdateBgCheckInfo(globalCtx, thisx, this->collider.dim.radius, this->collider.dim.height * 0.25f, 0.0f, 5); + this->actionFunc(this, globalCtx); + EnZo_Dialog(this, globalCtx); + + // Spawn air bubbles + if (globalCtx->state.frames & 8) { + pos = this->actor.world.pos; + + pos.y += (Rand_ZeroOne() - 0.5f) * 10.0f + 18.0f; + pos.x += (Rand_ZeroOne() - 0.5f) * 28.0f; + pos.z += (Rand_ZeroOne() - 0.5f) * 28.0f; + EnZo_Bubble(this, &pos); + } + + if ((s32)this->alpha != 0) { + Collider_UpdateCylinder(thisx, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + EnZo_UpdateRipples(this); + EnZo_UpdateBubbles(this); + EnZo_UpdateSplashes(this); +} + +s32 EnZo_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + EnZo* this = (EnZo*)thisx; + Vec3s vec; + + if (limbIndex == 15) { + Matrix_Translate(1800.0f, 0.0f, 0.0f, MTXMODE_APPLY); + vec = this->unk_194.unk_08; + Matrix_RotateX((vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Translate(-1800.0f, 0.0f, 0.0f, MTXMODE_APPLY); + } + + if (limbIndex == 8) { + vec = this->unk_194.unk_0E; + Matrix_RotateX((-vec.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((vec.x / 32768.0f) * M_PI, MTXMODE_APPLY); + } + + if ((limbIndex == 8) || (limbIndex == 9) || (limbIndex == 12)) { + rot->y += (Math_SinS(this->unk_656[limbIndex]) * 200.0f); + rot->z += (Math_CosS(this->unk_67E[limbIndex]) * 200.0f); + } + + return 0; +} + +void EnZo_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + EnZo* this = (EnZo*)thisx; + Vec3f vec = { 0.0f, 600.0f, 0.0f }; + + if (limbIndex == 15) { + Matrix_MultVec3f(&vec, &this->actor.focus.pos); + } +} + +void EnZo_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnZo* this = (EnZo*)thisx; + void* eyeTextures[] = { gZoraEyeOpenTex, gZoraEyeHalfTex, gZoraEyeClosedTex }; + + Matrix_Push(); + EnZo_DrawRipples(this, globalCtx); + EnZo_DrawBubbles(this, globalCtx); + EnZo_DrawSplashes(this, globalCtx); + Matrix_Pop(); + + if ((s32)this->alpha != 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_zo.c", 1008); + + if (this->alpha == 255.0f) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexture])); + func_80034BA0(globalCtx, &this->skelAnime, EnZo_OverrideLimbDraw, EnZo_PostLimbDraw, thisx, this->alpha); + } else { + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[this->eyeTexture])); + func_80034CC4(globalCtx, &this->skelAnime, EnZo_OverrideLimbDraw, EnZo_PostLimbDraw, thisx, this->alpha); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_zo.c", 1025); + } +} diff --git a/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h new file mode 100644 index 000000000..ba6eebb30 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_Zo/z_en_zo.h @@ -0,0 +1,43 @@ +#ifndef Z_EN_ZO_H +#define Z_EN_ZO_H + +#include "ultra64.h" +#include "global.h" + +struct EnZo; + +typedef struct { + /* 0x00 */ u8 type; + /* 0x04 */ f32 scale; + /* 0x08 */ f32 targetScale; + /* 0x0C */ Color_RGBA8 color; + /* 0x10 */ u32 pad; + /* 0x14 */ Vec3f pos; + /* 0x20 */ Vec3f vel; + /* 0x2C */ Vec3f vec; // Usage specific +} EnZoEffect; // size = 0x38 + +typedef void (*EnZoActionFunc)(struct EnZo*, GlobalContext*); + +typedef struct EnZo { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnime skelAnime; + /* 0x0190 */ EnZoActionFunc actionFunc; + /* 0x0194 */ struct_80034A14_arg1 unk_194; + /* 0x01BC */ ColliderCylinder collider; + /* 0x0208 */ u8 canSpeak; + /* 0x020A */ Vec3s jointTable[20]; + /* 0x0282 */ Vec3s morphTable[20]; + /* 0x02FC */ EnZoEffect effects[15]; + /* 0x0644 */ f32 dialogRadius; + /* 0x0648 */ f32 alpha; + /* 0x064C */ s16 unk_64C; + /* 0x064E */ s16 rippleTimer; + /* 0x0650 */ s16 timeToDive; + /* 0x0652 */ s16 blinkTimer; + /* 0x0654 */ s16 eyeTexture; + /* 0x0656 */ s16 unk_656[20]; + /* 0x067E */ s16 unk_67E[20]; +} EnZo; // size = 0x06A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c new file mode 100644 index 000000000..ce1029165 --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.c @@ -0,0 +1,729 @@ +/* + * File: z_en_fhg.c + * Overlay: ovl_En_fHG + * Description: Phantom Ganon's Horse + */ + +#include "z_en_fhg.h" +#include "objects/object_fhg/object_fhg.h" +#include "overlays/actors/ovl_Door_Shutter/z_door_shutter.h" +#include "overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h" +#include "overlays/actors/ovl_En_Fhg_Fire/z_en_fhg_fire.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 yRot; +} EnfHGPainting; // size = 0x10; + +typedef enum { + /* 0 */ INTRO_WAIT, + /* 1 */ INTRO_START, + /* 2 */ INTRO_FENCE, + /* 3 */ INTRO_BACK, + /* 4 */ INTRO_REVEAL, + /* 5 */ INTRO_CUT, + /* 6 */ INTRO_LAUGH, + /* 7 */ INTRO_TITLE, + /* 8 */ INTRO_RETREAT, + /* 9 */ INTRO_FINISH, + /* 15 */ INTRO_READY = 15 +} EnfHGIntroState; + +void EnfHG_Init(Actor* thisx, GlobalContext* globalCtx); +void EnfHG_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EnfHG_Update(Actor* thisx, GlobalContext* globalCtx); +void EnfHG_Draw(Actor* thisx, GlobalContext* globalCtx); + +void EnfHG_SetupIntro(EnfHG* this, GlobalContext* globalCtx); +void EnfHG_Intro(EnfHG* this, GlobalContext* globalCtx); +void EnfHG_SetupApproach(EnfHG* this, GlobalContext* globalCtx, s16 paintingIndex); +void EnfHG_Approach(EnfHG* this, GlobalContext* globalCtx); +void EnfHG_Attack(EnfHG* this, GlobalContext* globalCtx); +void EnfHG_Damage(EnfHG* this, GlobalContext* globalCtx); +void EnfHG_Retreat(EnfHG* this, GlobalContext* globalCtx); +void EnfHG_Done(EnfHG* this, GlobalContext* globalCtx); + +const ActorInit En_fHG_InitVars = { + ACTOR_EN_FHG, + ACTORCAT_BG, + FLAGS, + OBJECT_FHG, + sizeof(EnfHG), + (ActorFunc)EnfHG_Init, + (ActorFunc)EnfHG_Destroy, + (ActorFunc)EnfHG_Update, + (ActorFunc)EnfHG_Draw, + NULL, +}; + +static EnfHGPainting sPaintings[] = { + { { 0.0f, 60.0f, -315.0f }, 0x0000 }, { { -260.0f, 60.0f, -145.0f }, 0x2AAA }, + { { -260.0f, 60.0f, 165.0f }, 0x5554 }, { { 0.0f, 60.0f, 315.0f }, 0x7FFE }, + { { 260.0f, 60.0f, 155.0f }, 0xAAA8 }, { { 260.0f, 60.0f, -155.0f }, 0xD552 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_S8(naviEnemyId, 0x1A, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1200, ICHAIN_STOP), +}; + +void EnfHG_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + EnfHG* this = (EnfHG*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Flags_SetSwitch(globalCtx, 0x14); + Actor_SetScale(&this->actor, 0.011499999f); + this->actor.gravity = -3.5f; + ActorShape_Init(&this->actor.shape, -2600.0f, NULL, 20.0f); + this->actor.speedXZ = 0.0f; + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + Skin_Init(globalCtx, &this->skin, &gPhantomHorseSkel, &gPhantomHorseRunningAnim); + + if (this->actor.params >= GND_FAKE_BOSS) { + EnfHG_SetupApproach(this, globalCtx, this->actor.params - GND_FAKE_BOSS); + } else { + EnfHG_SetupIntro(this, globalCtx); + } +} + +void EnfHG_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnfHG* this = (EnfHG*)thisx; + + osSyncPrintf("F DT1\n"); + Skin_Free(globalCtx, &this->skin); + osSyncPrintf("F DT2\n"); +} + +void EnfHG_SetupIntro(EnfHG* this, GlobalContext* globalCtx) { + Animation_PlayLoop(&this->skin.skelAnime, &gPhantomHorseIdleAnim); + this->actionFunc = EnfHG_Intro; + this->actor.world.pos.x = GND_BOSSROOM_CENTER_X; + this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y - 267.0f; + this->actor.world.pos.z = GND_BOSSROOM_CENTER_Z; +} + +void EnfHG_Intro(EnfHG* this, GlobalContext* globalCtx) { + static Vec3f audioVec = { 0.0f, 0.0f, 50.0f }; + s32 pad64; + Player* player = GET_PLAYER(globalCtx); + BossGanondrof* bossGnd = (BossGanondrof*)this->actor.parent; + s32 pad58; + s32 pad54; + + if (this->cutsceneState != INTRO_FINISH) { + SkelAnime_Update(&this->skin.skelAnime); + } + switch (this->cutsceneState) { + case INTRO_WAIT: + if ((fabsf(player->actor.world.pos.x - (GND_BOSSROOM_CENTER_X + 0.0f)) < 150.0f) && + (fabsf(player->actor.world.pos.z - (GND_BOSSROOM_CENTER_Z + 0.0f)) < 150.0f)) { + this->cutsceneState = INTRO_READY; + } + break; + case INTRO_READY: + if ((fabsf(player->actor.world.pos.x - (GND_BOSSROOM_CENTER_X + 0.0f)) < 100.0f) && + (fabsf(player->actor.world.pos.z - (GND_BOSSROOM_CENTER_Z + 315.0f)) < 100.0f)) { + this->cutsceneState = INTRO_START; + if (gSaveContext.eventChkInf[7] & 4) { + this->timers[0] = 57; + } + } + break; + case INTRO_START: + if (gSaveContext.eventChkInf[7] & 4) { + if (this->timers[0] == 55) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_SHUTTER, + GND_BOSSROOM_CENTER_X + 0.0f, GND_BOSSROOM_CENTER_Y - 97.0f, + GND_BOSSROOM_CENTER_Z + 308.0f, 0, 0, 0, (SHUTTER_PG_BARS << 6)); + } + if (this->timers[0] == 51) { + Audio_PlayActorSound2(this->actor.child, NA_SE_EV_SPEAR_FENCE); + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } + if (this->timers[0] == 0) { + EnfHG_SetupApproach(this, globalCtx, Rand_ZeroOne() * 5.99f); + this->bossGndSignal = FHG_START_FIGHT; + } + break; + } + func_80064520(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 8); + this->cutsceneCamera = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, this->cutsceneCamera, CAM_STAT_ACTIVE); + this->cutsceneState = INTRO_FENCE; + this->timers[0] = 60; + this->actor.world.pos.y = GND_BOSSROOM_CENTER_Y - 7.0f; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF); + gSaveContext.eventChkInf[7] |= 4; + Flags_SetSwitch(globalCtx, 0x23); + case INTRO_FENCE: + player->actor.world.pos.x = GND_BOSSROOM_CENTER_X + 0.0f; + player->actor.world.pos.y = GND_BOSSROOM_CENTER_Y + 7.0f; + player->actor.world.pos.z = GND_BOSSROOM_CENTER_Z + 155.0f; + player->actor.world.rot.y = player->actor.shape.rot.y = 0; + player->actor.speedXZ = 0.0f; + this->cameraEye.x = GND_BOSSROOM_CENTER_X + 0.0f; + this->cameraEye.y = GND_BOSSROOM_CENTER_Y + 37.0f; + this->cameraEye.z = GND_BOSSROOM_CENTER_Z + 170.0f; + this->cameraAt.x = GND_BOSSROOM_CENTER_X + 0.0f; + this->cameraAt.y = GND_BOSSROOM_CENTER_Y + 47.0f; + this->cameraAt.z = GND_BOSSROOM_CENTER_Z + 315.0f; + if (this->timers[0] == 25) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_DOOR_SHUTTER, + GND_BOSSROOM_CENTER_X + 0.0f, GND_BOSSROOM_CENTER_Y - 97.0f, + GND_BOSSROOM_CENTER_Z + 308.0f, 0, 0, 0, (SHUTTER_PG_BARS << 6)); + } + if (this->timers[0] == 21) { + Audio_PlayActorSound2(this->actor.child, NA_SE_EV_SPEAR_FENCE); + } + if (this->timers[0] == 0) { + this->cutsceneState = INTRO_BACK; + this->timers[0] = 80; + } + break; + case INTRO_BACK: + if (this->timers[0] == 25) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_HORSE_GROAN); + } + if (this->timers[0] == 20) { + func_8002DF54(globalCtx, &this->actor, 9); + } + if (this->timers[0] == 1) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_OPENING_GANON); + } + Math_ApproachF(&this->cameraEye.x, GND_BOSSROOM_CENTER_X + 40.0f, 0.05f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraEye.y, GND_BOSSROOM_CENTER_Y + 37.0f, 0.05f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 80.0f, 0.05f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraAt.x, GND_BOSSROOM_CENTER_X - 100.0f, 0.05f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 47.0f, 0.05f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraAt.z, GND_BOSSROOM_CENTER_Z + 335.0f, 0.05f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, 0.01f); + if (this->timers[0] == 0) { + this->cutsceneState = INTRO_REVEAL; + this->timers[0] = 50; + this->cameraSpeedMod = 0.0f; + } + break; + case INTRO_REVEAL: + Math_ApproachF(&this->cameraEye.x, GND_BOSSROOM_CENTER_X + 70.0f, 0.1f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraEye.y, GND_BOSSROOM_CENTER_Y + 7.0f, 0.1f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 200.0f, 0.1f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraAt.x, GND_BOSSROOM_CENTER_X - 150.0f, 0.1f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 107.0f, 0.1f, this->cameraSpeedMod * 20.0f); + Math_ApproachF(&this->cameraAt.z, GND_BOSSROOM_CENTER_Z - 65.0f, 0.1f, this->cameraSpeedMod * 40.0f); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, 0.05f); + if (this->timers[0] == 5) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_SANDDUST); + } + if (this->timers[0] == 0) { + this->cutsceneState = INTRO_CUT; + this->timers[0] = 50; + this->cameraSpeedMod = 0.0f; + } + break; + case INTRO_CUT: + this->cutsceneState = INTRO_LAUGH; + this->cameraEye.x = GND_BOSSROOM_CENTER_X + 50.0f; + this->cameraEye.y = GND_BOSSROOM_CENTER_Y + 17.0f; + this->cameraEye.z = GND_BOSSROOM_CENTER_Z + 110.0f; + this->cameraAt.x = GND_BOSSROOM_CENTER_X - 150.0f; + this->cameraAt.y = GND_BOSSROOM_CENTER_Y + 207.0f; + this->cameraAt.z = GND_BOSSROOM_CENTER_Z - 155.0f; + this->cameraEyeVel.x = fabsf(this->cameraEye.x - (GND_BOSSROOM_CENTER_X + 20.0f)); + this->cameraEyeVel.y = fabsf(this->cameraEye.y - (GND_BOSSROOM_CENTER_Y + 102.0f)); + this->cameraEyeVel.z = fabsf(this->cameraEye.z - (GND_BOSSROOM_CENTER_Z + 25.0f)); + this->cameraAtVel.x = fabsf(this->cameraAt.x - (GND_BOSSROOM_CENTER_X - 150.0f)); + this->cameraAtVel.y = fabsf(this->cameraAt.y - (GND_BOSSROOM_CENTER_Y + 197.0f)); + this->cameraAtVel.z = fabsf(this->cameraAt.z - (GND_BOSSROOM_CENTER_Z - 65.0f)); + this->timers[0] = 250; + case INTRO_LAUGH: + Math_ApproachF(&this->cameraEye.x, GND_BOSSROOM_CENTER_X + 20.0f, 0.05f, + this->cameraSpeedMod * this->cameraEyeVel.x); + Math_ApproachF(&this->cameraEye.y, GND_BOSSROOM_CENTER_Y + 102.0f, 0.05f, + this->cameraSpeedMod * this->cameraEyeVel.y); + Math_ApproachF(&this->cameraEye.z, GND_BOSSROOM_CENTER_Z + 25.0f, 0.05f, + this->cameraSpeedMod * this->cameraEyeVel.z); + Math_ApproachF(&this->cameraAt.x, GND_BOSSROOM_CENTER_X - 150.0f, 0.05f, + this->cameraSpeedMod * this->cameraAtVel.x); + Math_ApproachF(&this->cameraAt.y, GND_BOSSROOM_CENTER_Y + 197.0f, 0.05f, + this->cameraSpeedMod * this->cameraAtVel.y); + Math_ApproachF(&this->cameraAt.z, GND_BOSSROOM_CENTER_Z - 65.0f, 0.05f, + this->cameraSpeedMod * this->cameraAtVel.z); + Math_ApproachF(&this->cameraSpeedMod, 0.01f, 1.0f, 0.001f); + if ((this->timers[0] == 245) || (this->timers[0] == 3)) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &gPhantomHorseRearingAnim, -8.0f); + this->bossGndSignal = FHG_REAR; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_HORSE_NEIGH); + if (this->timers[0] == 3) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); + } + } + if (this->timers[0] == 192) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_SANDDUST); + } + if (this->timers[0] == 212) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_HORSE_LAND2); + Animation_Change(&this->skin.skelAnime, &gPhantomHorseIdleAnim, 0.3f, 0.0f, 5.0f, ANIMMODE_LOOP_INTERP, + -10.0f); + } + if (this->timers[0] == 90) { + globalCtx->envCtx.unk_BF = 2; + globalCtx->envCtx.unk_D6 = 0x14; + } + if (this->timers[0] == 100) { + this->bossGndSignal = FHG_LIGHTNING; + } + if (this->timers[0] == 60) { + this->bossGndSignal = FHG_RIDE; + } + if (this->timers[0] == 130) { + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x5000FF); + } + if (this->timers[0] == 30) { + bossGnd->work[GND_EYE_STATE] = GND_EYESTATE_BRIGHTEN; + } + if (this->timers[0] == 35) { + func_80078914(&audioVec, NA_SE_EN_FANTOM_EYE); + } + if (this->timers[0] == 130) { + bossGnd->work[GND_EYE_STATE] = GND_EYESTATE_FADE; + func_80078914(&audioVec, NA_SE_EN_FANTOM_ST_LAUGH); + } + if (this->timers[0] == 20) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_BOSS); + } + if (this->timers[0] == 2) { + this->cameraSpeedMod = 0.0f; + this->cutsceneState = INTRO_TITLE; + this->cameraEyeVel.x = fabsf(this->cameraEye.x - (GND_BOSSROOM_CENTER_X + 180.0f)); + this->cameraEyeVel.y = fabsf(this->cameraEye.y - (GND_BOSSROOM_CENTER_Y + 7.0f)); + this->cameraEyeVel.z = fabsf(this->cameraEye.z - (GND_BOSSROOM_CENTER_Z + 140.0f)); + this->timers[0] = 100; + this->timers[1] = 34; + this->cameraAtVel.x = fabsf(this->cameraAt.x - this->actor.world.pos.x); + this->cameraAtVel.y = fabsf(this->cameraAt.y - ((this->actor.world.pos.y + 70.0f) - 20.0f)); + this->cameraAtVel.z = fabsf(this->cameraAt.z - this->actor.world.pos.z); + } + break; + case INTRO_TITLE: + if (this->timers[1] == 1) { + Animation_Change(&this->skin.skelAnime, &gPhantomHorseIdleAnim, 0.5f, 0.0f, + Animation_GetLastFrame(&gPhantomHorseIdleAnim), ANIMMODE_LOOP_INTERP, -3.0f); + } + Math_ApproachF(&this->cameraEye.x, GND_BOSSROOM_CENTER_X + 180.0f, 0.1f, + this->cameraSpeedMod * this->cameraEyeVel.x); + Math_ApproachF(&this->cameraEye.y, GND_BOSSROOM_CENTER_Y + 7.0f, 0.1f, + this->cameraSpeedMod * this->cameraEyeVel.y); + Math_ApproachF(&this->cameraEye.z, this->cameraPanZ + (GND_BOSSROOM_CENTER_Z + 140.0f), 0.1f, + this->cameraSpeedMod * this->cameraEyeVel.z); + Math_ApproachF(&this->cameraPanZ, -100.0f, 0.1f, 1.0f); + Math_ApproachF(&this->cameraAt.x, this->actor.world.pos.x, 0.1f, this->cameraSpeedMod * 10.0f); + Math_ApproachF(&this->cameraAt.y, (this->actor.world.pos.y + 70.0f) - 20.0f, 0.1f, + this->cameraSpeedMod * 10.0f); + Math_ApproachF(&this->cameraAt.z, this->actor.world.pos.z, 0.1f, this->cameraSpeedMod * 10.0f); + Math_ApproachF(&this->actor.world.pos.y, 60.0f, 0.1f, 2.0f); + this->actor.world.pos.y += 2.0f * Math_SinS(this->gallopTimer * 0x5DC); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, 0.05f); + if (this->timers[0] == 75) { + TitleCard_InitBossName(globalCtx, &globalCtx->actorCtx.titleCtx, + SEGMENTED_TO_VIRTUAL(gPhantomGanonTitleCardTex), 160, 180, 128, 40); + } + if (this->timers[0] == 0) { + this->cutsceneState = INTRO_RETREAT; + this->timers[0] = 200; + this->timers[1] = 23; + this->cameraSpeedMod = 0.0f; + Animation_Change(&this->skin.skelAnime, &gPhantomHorseLeapAnim, 1.0f, 0.0f, + Animation_GetLastFrame(&gPhantomHorseLeapAnim), ANIMMODE_ONCE_INTERP, -4.0f); + this->bossGndSignal = FHG_SPUR; + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_HORSE_NEIGH); + } + break; + case INTRO_RETREAT: + if (this->timers[1] == 1) { + Animation_Change(&this->skin.skelAnime, &gPhantomHorseLandAnim, 0.5f, 0.0f, + Animation_GetLastFrame(&gPhantomHorseLandAnim), ANIMMODE_ONCE_INTERP, -3.0f); + this->bossGndSignal = FHG_FINISH; + } + if (this->timers[0] == 170) { + func_8002DF54(globalCtx, &this->actor, 8); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC2); + } + Math_ApproachF(&this->cameraEye.z, this->cameraPanZ + (GND_BOSSROOM_CENTER_Z + 100.0f), 0.1f, + this->cameraSpeedMod * 1.5f); + Math_ApproachF(&this->cameraPanZ, -100.0f, 0.1f, 1.0f); + Math_ApproachF(&this->actor.world.pos.z, GND_BOSSROOM_CENTER_Z + 400.0f - 0.5f, 1.0f, + this->cameraSpeedMod * 10.0f); + Math_ApproachF(&this->cameraSpeedMod, 1.0f, 1.0f, 0.05f); + if ((fabsf(this->actor.world.pos.z - (GND_BOSSROOM_CENTER_Z + 400.0f - 0.5f)) < 300.0f) && + !this->spawnedWarp) { + this->spawnedWarp = true; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + GND_BOSSROOM_CENTER_X + 0.0f, this->actor.world.pos.y + 50.0f, + GND_BOSSROOM_CENTER_Z + 400.0f - 0.5f, 0, this->actor.shape.rot.y, 0, + FHGFIRE_WARP_RETREAT); + this->fhgFireKillWarp = true; + } + Math_ApproachF(&this->cameraAt.x, this->actor.world.pos.x, 0.2f, 50.0f); + Math_ApproachF(&this->cameraAt.z, this->actor.world.pos.z, 0.2f, 50.0f); + osSyncPrintf("TIME %d-------------------------------------------------\n", this->timers[0]); + if (fabsf(this->actor.world.pos.z - (GND_BOSSROOM_CENTER_Z + 400.0f - 0.5f)) < 1.0f) { + globalCtx->envCtx.unk_BF = 0; + globalCtx->envCtx.unk_D6 = 0x14; + this->cutsceneState = INTRO_FINISH; + Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, -3.0f); + this->bossGndSignal = FHG_START_FIGHT; + this->timers[1] = 75; + this->timers[0] = 140; + } + break; + case INTRO_FINISH: + EnfHG_Retreat(this, globalCtx); + Math_ApproachF(&this->cameraEye.z, this->cameraPanZ + (GND_BOSSROOM_CENTER_Z + 100.0f), 0.1f, + this->cameraSpeedMod * 1.5f); + Math_ApproachF(&this->cameraPanZ, -100.0f, 0.1f, 1.0f); + Math_ApproachF(&this->cameraAt.y, (this->actor.world.pos.y + 70.0f) - 20.0f, 0.1f, + this->cameraSpeedMod * 10.0f); + if (this->timers[1] == 0) { + Camera* camera = Gameplay_GetCamera(globalCtx, 0); + + camera->eye = this->cameraEye; + camera->eyeNext = this->cameraEye; + camera->at = this->cameraAt; + func_800C08AC(globalCtx, this->cutsceneCamera, 0); + this->cutsceneCamera = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + this->actionFunc = EnfHG_Retreat; + } + break; + } + if (this->cutsceneCamera != 0) { + Gameplay_CameraSetAtEye(globalCtx, this->cutsceneCamera, &this->cameraAt, &this->cameraEye); + } +} + +void EnfHG_SetupApproach(EnfHG* this, GlobalContext* globalCtx, s16 paintingIndex) { + s16 oppositeIndex[6] = { 3, 4, 5, 0, 1, 2 }; + + Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); + this->actionFunc = EnfHG_Approach; + this->curPainting = paintingIndex; + this->targetPainting = oppositeIndex[this->curPainting]; + + osSyncPrintf("KABE NO 1 = %d\n", this->curPainting); + osSyncPrintf("KABE NO 2 = %d\n", this->targetPainting); + + this->actor.world.pos.x = (1.3f * sPaintings[this->curPainting].pos.x) + (GND_BOSSROOM_CENTER_X - 4.0f); + this->actor.world.pos.y = sPaintings[this->curPainting].pos.y + (GND_BOSSROOM_CENTER_Y + 153.0f); + this->actor.world.pos.z = (1.3f * sPaintings[this->curPainting].pos.z) - -(GND_BOSSROOM_CENTER_Z - 10.0f); + this->actor.world.rot.y = sPaintings[this->curPainting].yRot; + + osSyncPrintf("XP1 = %f\n", this->actor.world.pos.x); + osSyncPrintf("ZP1 = %f\n", this->actor.world.pos.z); + + this->inPaintingPos.x = (sPaintings[this->targetPainting].pos.x * 1.3f) + (GND_BOSSROOM_CENTER_X - 4.0f); + this->inPaintingPos.y = sPaintings[this->targetPainting].pos.y + (GND_BOSSROOM_CENTER_Y + 33.0f); + this->inPaintingPos.z = (sPaintings[this->targetPainting].pos.z * 1.3f) - -(GND_BOSSROOM_CENTER_Z - 10.0f); + this->inPaintingVelX = (fabsf(this->inPaintingPos.x - this->actor.world.pos.x) * 2) * 0.01f; + + if (this->inPaintingVelX < 1.0f) { + this->inPaintingVelX = 1.0f; + } + this->inPaintingVelZ = (fabsf(this->inPaintingPos.z - this->actor.world.pos.z) * 2) * 0.01f; + if (this->inPaintingVelZ < 1.0f) { + this->inPaintingVelZ = 1.0f; + } + + this->timers[0] = 100; + this->actor.scale.x = 0.002f; + this->actor.scale.y = 0.002f; + this->actor.scale.z = 0.001f; + this->approachRate = 0.0f; + + this->warpColorFilterR = globalCtx->lightCtx.fogColor[0]; + this->warpColorFilterG = globalCtx->lightCtx.fogColor[1]; + this->warpColorFilterB = globalCtx->lightCtx.fogColor[2]; + this->warpColorFilterUnk1 = 0.0f; + this->warpColorFilterUnk2 = 0.0f; + this->turnRot = 0; + this->turnTarget = 0; + this->spawnedWarp = false; +} + +void EnfHG_Approach(EnfHG* this, GlobalContext* globalCtx) { + osSyncPrintf("STANDBY !!\n"); + osSyncPrintf("XP2 = %f\n", this->actor.world.pos.x); + osSyncPrintf("ZP2 = %f\n", this->actor.world.pos.z); + if (this->actor.params == GND_REAL_BOSS) { + this->hoofSfxPos.x = this->actor.projectedPos.x / (this->actor.scale.x * 100.0f); + this->hoofSfxPos.y = this->actor.projectedPos.y / (this->actor.scale.x * 100.0f); + this->hoofSfxPos.z = this->actor.projectedPos.z / (this->actor.scale.x * 100.0f); + if ((this->gallopTimer % 8) == 0) { + func_80078914(&this->hoofSfxPos, NA_SE_EV_HORSE_RUN); + } + } + SkelAnime_Update(&this->skin.skelAnime); + Math_ApproachF(&this->actor.scale.x, 0.011499999f, 1.0f, this->approachRate); + Math_ApproachF(&this->approachRate, 0.0002f, 1.0f, 0.0000015f); + Math_ApproachF(&this->actor.world.pos.y, 60.0f, 0.1f, 1.0f); + this->actor.scale.y = this->actor.scale.x; + if (this->timers[0] == 0) { + osSyncPrintf("arg_data ------------------------------------>%d\n", this->actor.params); + if (this->actor.params != GND_REAL_BOSS) { + this->timers[0] = 140; + this->actionFunc = EnfHG_Retreat; + Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); + this->turnTarget = -0x8000; + } else { + this->actionFunc = EnfHG_Attack; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_GANON_HORSE_NEIGH); + this->timers[0] = 40; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + this->actor.world.pos.x, this->actor.world.pos.y + 50.0f, this->actor.world.pos.z, 0, + this->actor.shape.rot.y + 0x8000, 0, FHGFIRE_WARP_EMERGE); + this->fhgFireKillWarp = false; + } + } +} + +void EnfHG_Attack(EnfHG* this, GlobalContext* globalCtx) { + osSyncPrintf("KABE OUT !!\n"); + this->bossGndInPainting = false; + SkelAnime_Update(&this->skin.skelAnime); + if (this->timers[0] != 0) { + Math_ApproachF(&this->actor.scale.z, 0.011499999f, 1.0f, 0.0002f); + if (this->timers[0] == 1) { + this->bossGndSignal = FHG_RAISE_SPEAR; + this->timers[1] = 50; + Animation_MorphToPlayOnce(&this->skin.skelAnime, &gPhantomHorseLeapAnim, 0.0f); + } + Math_ApproachF(&this->warpColorFilterR, 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterG, 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterB, 255.0f, 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterUnk1, -60.0f, 1.0f, 5.0f); + } else { + Math_ApproachF(&this->warpColorFilterR, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterG, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterB, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterUnk1, 0.0f, 1.0f, 5.0f); + if (this->timers[1] == 29) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_MASIC2); + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_VOICE); + } + if (this->hitTimer == 0) { + if (this->timers[1] == 24) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, + this->actor.world.pos.x, (this->actor.world.pos.y + 100.0f) + 25.0f, + this->actor.world.pos.z, 0, 0, 0, FHGFIRE_LIGHTNING_STRIKE); + } + if (this->timers[1] == 45) { + Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseAirAnim, 0.0f); + } + if (this->timers[1] == 38) { + this->bossGndSignal = FHG_LIGHTNING; + } + if (this->timers[1] == 16) { + Animation_MorphToPlayOnce(&this->skin.skelAnime, &gPhantomHorseLandAnim, 0.0f); + this->bossGndSignal = FHG_RESET; + } + } + Math_ApproachF(&this->actor.scale.z, 0.011499999f, 1.0f, 0.002f); + Math_ApproachF(&this->actor.world.pos.x, this->inPaintingPos.x, 1.0f, this->inPaintingVelX); + Math_ApproachF(&this->actor.world.pos.y, 60.0f, 0.1f, 1.0f); + Math_ApproachF(&this->actor.world.pos.z, this->inPaintingPos.z, 1.0f, this->inPaintingVelZ); + } + if (this->hitTimer == 20) { + this->actionFunc = EnfHG_Damage; + this->spawnedWarp = false; + Animation_Change(&this->skin.skelAnime, &gPhantomHorseLandAnim, -1.0f, 0.0f, + Animation_GetLastFrame(&gPhantomHorseLandAnim), ANIMMODE_ONCE, -5.0f); + this->timers[0] = 10; + this->bossGndSignal = FHG_RESET; + this->damageSpeedMod = 1.0f; + } else { + f32 dx = this->actor.world.pos.x - this->inPaintingPos.x; + f32 dz = this->actor.world.pos.z - this->inPaintingPos.z; + f32 dxz = sqrtf(SQ(dx) + SQ(dz)); + + if (dxz < 350.0f) { + this->bossGndInPainting = true; + } + if ((dxz < 300.0f) && !this->spawnedWarp) { + this->spawnedWarp = true; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, this->inPaintingPos.x, + this->actor.world.pos.y + 50.0f, this->inPaintingPos.z, 0, this->actor.shape.rot.y, 0, + FHGFIRE_WARP_RETREAT); + this->fhgFireKillWarp = true; + } + osSyncPrintf("SPD X %f\n", this->inPaintingVelX); + osSyncPrintf("SPD Z %f\n", this->inPaintingVelZ); + osSyncPrintf("X=%f\n", dx); + osSyncPrintf("Z=%f\n", dz); + if (dxz == 0.0f) { + this->timers[0] = 140; + this->actionFunc = EnfHG_Retreat; + Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); + this->bossGndSignal = FHG_RIDE; + } + } +} + +void EnfHG_Damage(EnfHG* this, GlobalContext* globalCtx) { + f32 dx; + f32 dz; + f32 dxz2; + + osSyncPrintf("REVISE !!\n"); + SkelAnime_Update(&this->skin.skelAnime); + Math_ApproachF(&this->warpColorFilterR, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterG, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterB, globalCtx->lightCtx.fogColor[0], 1.0f, 10.0f); + Math_ApproachF(&this->warpColorFilterUnk1, 0.0f, 1.0f, 5.0f); + Math_ApproachF(&this->actor.scale.z, 0.011499999f, 1.0f, 0.002f); + if (this->timers[0] != 0) { + Math_ApproachZeroF(&this->damageSpeedMod, 1.0f, 0.1f); + if (this->timers[0] == 1) { + this->targetPainting = this->curPainting; + this->inPaintingPos.x = (sPaintings[this->targetPainting].pos.x * 1.3f) + (GND_BOSSROOM_CENTER_X - 4.0f); + this->inPaintingPos.y = sPaintings[this->targetPainting].pos.y; + this->inPaintingPos.z = (sPaintings[this->targetPainting].pos.z * 1.3f) - -(GND_BOSSROOM_CENTER_Z - 10.0f); + } + } else { + Math_ApproachF(&this->damageSpeedMod, 1.0f, 1.0f, 0.1f); + } + Math_ApproachF(&this->actor.world.pos.x, this->inPaintingPos.x, 1.0f, this->damageSpeedMod * this->inPaintingVelX); + Math_ApproachF(&this->actor.world.pos.y, 60.0f, 0.1f, 1.0f); + Math_ApproachF(&this->actor.world.pos.z, this->inPaintingPos.z, 1.0f, this->damageSpeedMod * this->inPaintingVelZ); + dx = this->actor.world.pos.x - this->inPaintingPos.x; + dz = this->actor.world.pos.z - this->inPaintingPos.z; + dxz2 = sqrtf(SQ(dx) + SQ(dz)); + if ((dxz2 < 300.0f) && (!this->spawnedWarp)) { + this->spawnedWarp = true; + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_FHG_FIRE, this->inPaintingPos.x, + this->actor.world.pos.y + 50.0f, this->inPaintingPos.z, 0, this->actor.shape.rot.y + 0x8000, + 0, FHGFIRE_WARP_RETREAT); + } + if (dxz2 == 0.0f) { + BossGanondrof* bossGnd = (BossGanondrof*)this->actor.parent; + + this->timers[0] = 140; + this->actionFunc = EnfHG_Retreat; + Animation_MorphToLoop(&this->skin.skelAnime, &gPhantomHorseRunningAnim, 0.0f); + if (bossGnd->actor.colChkInfo.health > 24) { + this->bossGndSignal = FHG_RIDE; + } else { + bossGnd->flyMode = GND_FLY_NEUTRAL; + } + this->turnTarget = -0x8000; + } +} + +void EnfHG_Retreat(EnfHG* this, GlobalContext* globalCtx) { + osSyncPrintf("KABE IN !!\n"); + if (this->turnTarget != 0) { + Math_ApproachS(&this->turnRot, this->turnTarget, 5, 2000); + } + if (this->actor.params == GND_REAL_BOSS) { + this->hoofSfxPos.x = this->actor.projectedPos.x / (this->actor.scale.x * 100.0f); + this->hoofSfxPos.y = this->actor.projectedPos.y / (this->actor.scale.x * 100.0f); + this->hoofSfxPos.z = this->actor.projectedPos.z / (this->actor.scale.x * 100.0f); + if ((this->gallopTimer % 8) == 0) { + func_80078914(&this->hoofSfxPos, NA_SE_EV_HORSE_RUN); + } + } + SkelAnime_Update(&this->skin.skelAnime); + Math_ApproachF(&this->actor.scale.z, 0.001f, 1.0f, 0.001f); + Math_ApproachF(&this->actor.scale.x, 0.002f, 0.05f, 0.0001f); + Math_ApproachF(&this->actor.world.pos.y, 200.0f, 0.05f, 1.0f); + this->actor.scale.y = this->actor.scale.x; + if ((this->timers[0] == 80) && (this->actor.params == GND_REAL_BOSS)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_LAUGH); + } + if (this->timers[0] == 0) { + BossGanondrof* bossGnd = (BossGanondrof*)this->actor.parent; + s16 paintingIdxReal; + s16 paintingIdxFake; + + if (this->actor.params != GND_REAL_BOSS) { + this->killActor = true; + bossGnd->killActor = true; + } else if (bossGnd->flyMode != GND_FLY_PAINTING) { + this->actionFunc = EnfHG_Done; + this->actor.draw = NULL; + } else { + paintingIdxReal = Rand_ZeroOne() * 5.99f; + EnfHG_SetupApproach(this, globalCtx, paintingIdxReal); + do { + paintingIdxFake = Rand_ZeroOne() * 5.99f; + } while (paintingIdxFake == paintingIdxReal); + osSyncPrintf("ac1 = %x `````````````````````````````````````````````````\n", + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_BOSS_GANONDROF, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, + 0, 0, 0, paintingIdxFake + GND_FAKE_BOSS)); + } + } +} + +void EnfHG_Done(EnfHG* this, GlobalContext* globalCtx) { + this->bossGndInPainting = false; +} + +void EnfHG_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + EnfHG* this = (EnfHG*)thisx; + u8 i; + + if (this->killActor) { + Actor_Kill(&this->actor); + return; + } + this->gallopTimer++; + this->bossGndInPainting = true; + for (i = 0; i < 5; i++) { + if (this->timers[i] != 0) { + this->timers[i]--; + } + } + + this->actionFunc(this, globalCtx); + + if (this->hitTimer != 0) { + this->hitTimer--; + } + + this->actor.focus.pos = this->actor.world.pos; + this->actor.focus.pos.y += 70.0f; + this->actor.shape.rot.y = this->actor.world.rot.y; + + this->actor.shape.yOffset = Math_SinS(this->hitTimer * 0x9000) * 700.0f * (this->hitTimer / 20.0f); + this->actor.shape.rot.z = (s16)(Math_SinS(this->hitTimer * 0x7000) * 1500.0f) * (this->hitTimer / 20.0f); +} + +void EnfHG_PostDraw(Actor* thisx, GlobalContext* globalCtx, Skin* skin) { +} + +void EnfHG_Draw(Actor* thisx, GlobalContext* globalCtx) { + EnfHG* this = (EnfHG*)thisx; + BossGanondrof* bossGnd = (BossGanondrof*)this->actor.parent; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_fhg.c", 2439); + func_80093D18(globalCtx->state.gfxCtx); + + POLY_OPA_DISP = ((bossGnd->work[GND_INVINC_TIMER] & 4) && (bossGnd->flyMode == GND_FLY_PAINTING)) + ? Gfx_SetFog(POLY_OPA_DISP, 255, 50, 0, 0, 900, 1099) + : Gfx_SetFog(POLY_OPA_DISP, (u32)this->warpColorFilterR, (u32)this->warpColorFilterG, + (u32)this->warpColorFilterB, 0, (s32)this->warpColorFilterUnk1 + 995, + (s32)this->warpColorFilterUnk2 + 1000); + func_800A6330(&this->actor, globalCtx, &this->skin, EnfHG_PostDraw, SKIN_TRANSFORM_IS_FHG); + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_fhg.c", 2480); +} diff --git a/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.h b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.h new file mode 100644 index 000000000..befbbc49d --- /dev/null +++ b/soh/src/overlays/actors/ovl_En_fHG/z_en_fhg.h @@ -0,0 +1,64 @@ +#ifndef Z_EN_FHG_H +#define Z_EN_FHG_H + +#include "ultra64.h" +#include "global.h" + +struct EnfHG; + +typedef void (*EnfHGActionFunc)(struct EnfHG*, GlobalContext*); + +typedef enum { + /* 0 */ FHG_NO_SIGNAL, + /* 1 */ FHG_RAISE_SPEAR, + /* 2 */ FHG_REAR, + /* 3 */ FHG_LIGHTNING, + /* 4 */ FHG_RESET, + /* 5 */ FHG_RIDE, + /* 10 */ FHG_SPUR = 10, + /* 11 */ FHG_FINISH, + /* -1 */ FHG_START_FIGHT = 255 +} EnfHGSignal; + +typedef struct EnfHG { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 bossGndSignal; + /* 0x014D */ u8 bossGndInPainting; + /* 0x014E */ u8 killActor; + /* 0x014F */ u8 fhgFireKillWarp; + /* 0x0150 */ Vec3f cameraEye; + /* 0x015C */ Vec3f cameraAt; + /* 0x0168 */ Vec3f cameraEyeVel; + /* 0x0174 */ Vec3f cameraAtVel; + /* 0x0180 */ Vec3f hoofSfxPos; + /* 0x018C */ Vec3f inPaintingPos; + /* 0x0198 */ f32 inPaintingVelX; + /* 0x019C */ f32 inPaintingVelZ; + /* 0x0198 */ f32 damageSpeedMod; + /* 0x01A4 */ f32 approachRate; + /* 0x01A8 */ f32 cameraSpeedMod; + /* 0x01AC */ f32 cameraPanZ; + /* 0x01B0 */ char unk_1B0[0x10]; + /* 0x01C0 */ s16 gallopTimer; + /* 0x01C2 */ s16 curPainting; + /* 0x01C4 */ s16 targetPainting; + /* 0x01C6 */ s16 turnTarget; + /* 0x01C8 */ s16 spawnedWarp; + /* 0x01CA */ s16 cutsceneState; + /* 0x01CC */ s16 cutsceneCamera; + /* 0x01CE */ char unk_1CE[6]; + /* 0x01D4 */ s16 timers[5]; + /* 0x01DE */ s16 hitTimer; + /* 0x01E0 */ s16 turnRot; + /* 0x01E2 */ char unk_1E2[6]; + /* 0x01E8 */ f32 warpColorFilterR; + /* 0x01EC */ f32 warpColorFilterG; + /* 0x01F0 */ f32 warpColorFilterB; + /* 0x01F4 */ f32 warpColorFilterUnk1; + /* 0x01F8 */ f32 warpColorFilterUnk2; + /* 0x01FC */ EnfHGActionFunc actionFunc; + /* 0x0200 */ char unk_200[4]; + /* 0x0204 */ Skin skin; +} EnfHG; // size = 0x0294 + +#endif diff --git a/soh/src/overlays/actors/ovl_End_Title/z_end_title.c b/soh/src/overlays/actors/ovl_End_Title/z_end_title.c new file mode 100644 index 000000000..f76ac084d --- /dev/null +++ b/soh/src/overlays/actors/ovl_End_Title/z_end_title.c @@ -0,0 +1,132 @@ +/* + * File: z_end_title.c + * Overlay: ovl_End_Title + * Description: "The End" message + */ + +#include "z_end_title.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void EndTitle_Init(Actor* thisx, GlobalContext* globalCtx); +void EndTitle_Destroy(Actor* thisx, GlobalContext* globalCtx); +void EndTitle_Update(Actor* thisx, GlobalContext* globalCtx); +void EndTitle_DrawFull(Actor* thisx, GlobalContext* globalCtx); +void EndTitle_DrawNintendoLogo(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit End_Title_InitVars = { + ACTOR_END_TITLE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(EndTitle), + (ActorFunc)EndTitle_Init, + (ActorFunc)EndTitle_Destroy, + (ActorFunc)EndTitle_Update, + (ActorFunc)EndTitle_DrawFull, + NULL, +}; + +#include "overlays/ovl_End_Title/ovl_End_Title.h" + +void EndTitle_Init(Actor* thisx, GlobalContext* globalCtx) { + EndTitle* this = (EndTitle*)thisx; + + this->endAlpha = 0; + this->tlozAlpha = 0; + this->ootAlpha = 0; + if (this->actor.params == 1) { + this->actor.draw = EndTitle_DrawNintendoLogo; + } +} + +void EndTitle_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void EndTitle_Update(Actor* thisx, GlobalContext* globalCtx) { +} + +// Used in the castle courtyard +void EndTitle_DrawFull(Actor* thisx, GlobalContext* globalCtx) { + MtxF* mf; + EndTitle* this = (EndTitle*)thisx; + s32 frameCount = globalCtx->csCtx.frames; + Player* player = GET_PLAYER(globalCtx); + + mf = &player->mf_9E0; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_end_title.c", 403); + + // Draw the Triforce on Link's left hand + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Mult(mf, MTXMODE_NEW); + Matrix_Translate(0.0f, 150.0f, 170.0f, MTXMODE_APPLY); + Matrix_Scale(0.13f, 0.13f, 0.13f, MTXMODE_APPLY); + Matrix_RotateX(0xBB8 * M_PI / 0x8000, MTXMODE_APPLY); + Matrix_RotateY(0.0f, MTXMODE_APPLY); + Matrix_RotateZ(0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_end_title.c", 412), G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, sTriforceDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_end_title.c", 417); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_end_title.c", 419); + + // Draw title cards on the screen + if ((frameCount > 890) && (this->endAlpha < 200)) { + this->endAlpha += 7; + } + if ((frameCount > 810) && (this->tlozAlpha < 200)) { + this->tlozAlpha += 15; + } + if ((frameCount > 850) && (this->ootAlpha < 200)) { + this->ootAlpha += 15; + } + + OVERLAY_DISP = func_80093F34(OVERLAY_DISP); + gDPSetTextureLUT(OVERLAY_DISP++, G_TT_NONE); + gDPSetEnvColor(OVERLAY_DISP++, 255, 120, 30, 0); + gDPSetRenderMode(OVERLAY_DISP++, G_RM_PASS, G_RM_XLU_SURF2); + gSPClearGeometryMode(OVERLAY_DISP++, + G_TEXTURE_ENABLE | G_CULL_BACK | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, + COMBINED, 0, 0, 0, COMBINED); + gDPSetPrimColor(OVERLAY_DISP++, 0x00, 0x80, 0, 0, 0, this->endAlpha); + gDPLoadTextureTile(OVERLAY_DISP++, sTheEndTex, G_IM_FMT_IA, G_IM_SIZ_8b, 80, 24, 0, 0, 80 - 1, 24 - 1, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 0, 0, 0, 0); + gSPTextureRectangle(OVERLAY_DISP++, 120 << 2, 90 << 2, 200 << 2, 113 << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0x00, 0x80, 0, 0, 0, this->tlozAlpha); + gDPLoadTextureTile(OVERLAY_DISP++, sTheLegendOfZeldaTex, G_IM_FMT_IA, G_IM_SIZ_8b, 120, 24, 0, 0, 120 - 1, 24 - 1, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 0, 0, 0, 0); + gSPTextureRectangle(OVERLAY_DISP++, 100 << 2, 160 << 2, 220 << 2, 183 << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, + 1 << 10); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0x00, 0x80, 0, 0, 0, this->ootAlpha); + gDPLoadTextureTile(OVERLAY_DISP++, sOcarinaOfTimeTex, G_IM_FMT_IA, G_IM_SIZ_8b, 112, 16, 0, + 0, 112 - 1, 16 - 1, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 0, 0, 0, 0); + gSPTextureRectangle(OVERLAY_DISP++, 104 << 2, 177 << 2, 216 << 2, 192 << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, + 1 << 10); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_end_title.c", 515); +} + +// Used in the Temple of Time +void EndTitle_DrawNintendoLogo(Actor* thisx, GlobalContext* globalCtx) { + EndTitle* this = (EndTitle*)thisx; + s32 pad; + s32 frames = globalCtx->csCtx.frames; + + if ((frames >= 1101) && (this->endAlpha < 255)) { + this->endAlpha += 3; + } + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_end_title.c", 594); + + OVERLAY_DISP = func_80093F34(OVERLAY_DISP); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0x80, 0, 0, 0, this->endAlpha); + gSPDisplayList(OVERLAY_DISP++, sPresentedByNintendoDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_end_title.c", 600); +} diff --git a/soh/src/overlays/actors/ovl_End_Title/z_end_title.h b/soh/src/overlays/actors/ovl_End_Title/z_end_title.h new file mode 100644 index 000000000..9897c0fbf --- /dev/null +++ b/soh/src/overlays/actors/ovl_End_Title/z_end_title.h @@ -0,0 +1,16 @@ +#ifndef Z_END_TITLE_H +#define Z_END_TITLE_H + +#include "ultra64.h" +#include "global.h" + +struct EndTitle; + +typedef struct EndTitle { + /* 0x0000 */ Actor actor; + /* 0x014C */ u8 endAlpha; + /* 0x014D */ u8 tlozAlpha; + /* 0x014E */ u8 ootAlpha; +} EndTitle; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.c b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c new file mode 100644 index 000000000..a103c4826 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -0,0 +1,5789 @@ +/* + * File: z_fishing.c + * Overlay: ovl_Fishing + * Description: Fishing Pond Elements (Owner, Fish, Props, Effects...) + */ + +#include "z_fishing.h" + +#include "overlays/actors/ovl_En_Kanban/z_en_kanban.h" +#include "objects/object_fish/object_fish.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +#define WATER_SURFACE_Y(globalCtx) globalCtx->colCtx.colHeader->waterBoxes->ySurface + +void Fishing_Init(Actor* thisx, GlobalContext* globalCtx); +void Fishing_Destroy(Actor* thisx, GlobalContext* globalCtx); +void Fishing_UpdateFish(Actor* thisx, GlobalContext* globalCtx); +void Fishing_UpdateOwner(Actor* thisx, GlobalContext* globalCtx); +void Fishing_DrawFish(Actor* thisx, GlobalContext* globalCtx); +void Fishing_DrawOwner(Actor* thisx, GlobalContext* globalCtx); + +typedef struct { + /* 0x00 */ u8 unk_00; + /* 0x02 */ Vec3s pos; + /* 0x08 */ u8 unk_08; + /* 0x0C */ f32 unk_0C; +} FishingFishInit; // size = 0x10 + +#define EFFECT_COUNT 130 + +typedef enum { + /* 0x00 */ FS_EFF_NONE, + /* 0x01 */ FS_EFF_RIPPLE, + /* 0x02 */ FS_EFF_DUST_SPLASH, + /* 0x03 */ FS_EFF_WATER_DUST, + /* 0x04 */ FS_EFF_BUBBLE, + /* 0x05 */ FS_EFF_RAIN_DROP, + /* 0x06 */ FS_EFF_OWNER_HAT, + /* 0x07 */ FS_EFF_RAIN_RIPPLE, + /* 0x08 */ FS_EFF_RAIN_SPLASH +} FishingEffectType; + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f vel; + /* 0x18 */ Vec3f accel; + /* 0x24 */ u8 type; + /* 0x25 */ u8 timer; + /* 0x26 */ char unk_26[0x04]; + /* 0x2A */ s16 alpha; + /* 0x2C */ s16 unk_2C; + /* 0x2E */ s16 unk_2E; + /* 0x30 */ f32 unk_30; + /* 0x34 */ f32 unk_34; + /* 0x38 */ f32 unk_38; + /* 0x3C */ f32 unk_3C; +} FishingEffect; // size = 0x40 + +#define POND_PROP_COUNT 140 + +typedef enum { + /* 0x00 */ FS_PROP_NONE, + /* 0x01 */ FS_PROP_REED, + /* 0x02 */ FS_PROP_LILY_PAD, + /* 0x03 */ FS_PROP_ROCK, + /* 0x04 */ FS_PROP_WOOD_POST, + /* 0x23 */ FS_PROP_INIT_STOP = 0x23 +} FishingPropType; + +typedef struct { + /* 0x00 */ u8 type; + /* 0x02 */ Vec3s pos; +} FishingPropInit; // size = 0x08 + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ f32 rotX; + /* 0x10 */ f32 rotY; + /* 0x14 */ f32 reedAngle; + /* 0x18 */ Vec3f projectedPos; + /* 0x24 */ f32 scale; + /* 0x28 */ s16 lilyPadAngle; + /* 0x2C */ f32 lilyPadOffset; + /* 0x30 */ u8 type; + /* 0x32 */ s16 timer; + /* 0x34 */ u8 shouldDraw; + /* 0x38 */ f32 drawDistance; +} FishingProp; // size = 0x3C + +typedef enum { + /* 0x00 */ FS_GROUP_FISH_NONE, + /* 0x01 */ FS_GROUP_FISH_NORMAL +} FishingGroupFishType; + +#define GROUP_FISH_COUNT 60 + +typedef struct { + /* 0x00 */ u8 type; + /* 0x02 */ s16 timer; + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f unk_10; + /* 0x1C */ Vec3f projectedPos; + /* 0x28 */ f32 unk_28; + /* 0x2C */ f32 unk_2C; + /* 0x30 */ f32 unk_30; + /* 0x34 */ f32 unk_34; + /* 0x38 */ f32 unk_38; + /* 0x3C */ s16 unk_3C; + /* 0x3E */ s16 unk_3E; + /* 0x40 */ s16 unk_40; + /* 0x42 */ s16 unk_42; + /* 0x44 */ u8 shouldDraw; +} FishingGroupFish; // size = 0x48 + +#define LINE_SEG_COUNT 200 +#define SINKING_LURE_SEG_COUNT 20 + +const ActorInit Fishing_InitVars = { + ACTOR_FISHING, + ACTORCAT_NPC, + FLAGS, + OBJECT_FISH, + sizeof(Fishing), + (ActorFunc)Fishing_Init, + (ActorFunc)Fishing_Destroy, + (ActorFunc)Fishing_UpdateFish, + (ActorFunc)Fishing_DrawFish, + NULL, +}; + +static f32 D_80B7A650 = 0.0f; + +static u8 D_80B7A654 = 0; + +static f32 D_80B7A658 = 0.0f; + +static Vec3f D_80B7A65C = { 0.0f, 0.0f, 0.0f }; + +static f32 D_80B7A668 = 0.0f; + +static u8 sSinkingLureLocation = 0; + +static f32 D_80B7A670 = 0.0f; + +static u8 D_80B7A674 = true; + +static u16 D_80B7A678 = 0; + +static u8 D_80B7A67C = 0; + +static s32 D_80B7A680 = 0; + +static s16 D_80B7A684 = 0; + +static u8 D_80B7A688 = 0; +static u8 D_80B7A68C = 0; +static u8 D_80B7A690 = 0; + +static s16 D_80B7A694 = 0; + +static Vec3f sFishMouthOffset = { 500.0f, 500.0f, 0.0f }; + +static u8 D_80B7A6A4 = 0; + +static f32 D_80B7A6A8 = 0.0f; +static f32 D_80B7A6AC = 0.0f; +static f32 D_80B7A6B0 = 0.0f; +static f32 D_80B7A6B4 = 0.0f; +static f32 D_80B7A6B8 = 0.0f; +static f32 D_80B7A6BC = 0.0f; +static f32 D_80B7A6C0 = 0.0f; + +static s16 D_80B7A6C4 = 0; +static s16 D_80B7A6C8 = 0; + +static u8 D_80B7A6CC = 0; +static u8 D_80B7A6D0 = 0; +static u8 D_80B7A6D4 = 0; + +static ColliderJntSphElementInit sJntSphElementsInit[12] = { + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, + { + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x00, 0x10 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 30 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_TYPE_ENEMY, + AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_1, + COLSHAPE_JNTSPH, + }, + 12, + sJntSphElementsInit, +}; + +static f32 D_80B7A898 = 0.0f; + +static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; +static Vec3f D_80B7A8A8 = { 0.0f, 0.0f, 2000.0f }; + +static Fishing* sFishingMain; +static u8 D_80B7E074; +static u8 sLinkAge; +static u8 D_80B7E076; +static u8 D_80B7E077; +static f32 D_80B7E078; +static u8 D_80B7E07C; +static u8 D_80B7E07D; +static u8 D_80B7E07E; +static s16 D_80B7E080; +static u8 D_80B7E082; +static u16 D_80B7E084; +static u16 D_80B7E086; +static s8 D_80B7E088; +static Vec3f sOwnerHeadPos; +static Vec3s sEffOwnerHatRot; +static u8 D_80B7E0A2; +static s16 D_80B7E0A4; +static s16 D_80B7E0A6; +static Fishing* sFishingHookedFish; +static s16 D_80B7E0AC; +static s16 D_80B7E0AE; +static s16 D_80B7E0B0; +static s16 D_80B7E0B2; +static s16 D_80B7E0B4; +static u8 D_80B7E0B6; +static Vec3f sLurePos; +static Vec3f D_80B7E0C8; +static Vec3f sLureRot; +static Vec3f D_80B7E0E8; +static Vec3f D_80B7E0F8; +static f32 D_80B7E104; +static f32 D_80B7E108; +static f32 D_80B7E10C; +static f32 D_80B7E110; +static s8 D_80B7E114; +static s16 D_80B7E116; +static u8 D_80B7E118; +static f32 D_80B7E11C; +static u8 D_80B7E120; +static s16 D_80B7E122; +static u8 D_80B7E124; +static Vec3f D_80B7E128; +static f32 D_80B7E134; +static f32 D_80B7E138; +static s16 D_80B7E13C; +static f32 D_80B7E140; +static f32 D_80B7E144; +static f32 D_80B7E148; +static f32 D_80B7E14C; +static s16 D_80B7E150; +static f32 D_80B7E154; +static Vec3f sRodTipPos; +static Vec3f sReelLinePos[LINE_SEG_COUNT]; +static Vec3f sReelLineRot[LINE_SEG_COUNT]; +static Vec3f sReelLineUnk[LINE_SEG_COUNT]; +static Vec3f sLureHookRefPos[2]; +static f32 sLureHookRotY[2]; +static u8 D_80B7FDA8; +static Vec3f sSinkingLurePos[SINKING_LURE_SEG_COUNT]; +static s16 D_80B7FEA0; +static f32 sProjectedW; +static Vec3f sCameraEye; +static Vec3f sCameraAt; +static s16 sCameraId; +static f32 D_80B7FEC8; +static f32 D_80B7FECC; +static f32 D_80B7FED0; +static Vec3f sSinkingLureBasePos; +static f32 D_80B7FEE4; +static s32 sRandSeed0; +static s32 sRandSeed1; +static s32 sRandSeed2; +static FishingProp sPondProps[POND_PROP_COUNT]; +static FishingGroupFish sGroupFishes[GROUP_FISH_COUNT]; +static f32 sFishGroupAngle1; +static f32 sFishGroupAngle2; +static f32 sFishGroupAngle3; +static FishingEffect sFishingEffects[EFFECT_COUNT]; +static Vec3f sStreamSoundProjectedPos; + +void Fishing_SetColliderElement(s32 index, ColliderJntSph* collider, Vec3f* pos, f32 scale) { + collider->elements[index].dim.worldSphere.center.x = pos->x; + collider->elements[index].dim.worldSphere.center.y = pos->y; + collider->elements[index].dim.worldSphere.center.z = pos->z; + collider->elements[index].dim.worldSphere.radius = + collider->elements[index].dim.modelSphere.radius * collider->elements[index].dim.scale * scale * 1.6f; +} + +void Fishing_SeedRand(s32 seed0, s32 seed1, s32 seed2) { + sRandSeed0 = seed0; + sRandSeed1 = seed1; + sRandSeed2 = seed2; +} + +f32 Fishing_RandZeroOne(void) { + f32 rand; + + // Wichmann-Hill algorithm + sRandSeed0 = (sRandSeed0 * 171) % 30269; + sRandSeed1 = (sRandSeed1 * 172) % 30307; + sRandSeed2 = (sRandSeed2 * 170) % 30323; + + rand = (sRandSeed0 / 30269.0f) + (sRandSeed1 / 30307.0f) + (sRandSeed2 / 30323.0f); + while (rand >= 1.0f) { + rand -= 1.0f; + } + + return fabsf(rand); +} + +s16 Fishing_SmoothStepToS(s16* pValue, s16 target, s16 scale, s16 step) { + s16 stepSize; + s16 diff; + + diff = target - *pValue; + stepSize = diff / scale; + + if (stepSize > step) { + stepSize = step; + } + + if (stepSize < -step) { + stepSize = -step; + } + + *pValue += stepSize; + + return stepSize; +} + +void Fishing_SpawnRipple(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos, f32 arg3, f32 arg4, s16 arg5, + s16 countLimit) { + s16 i; + + if ((projectedPos != NULL) && ((projectedPos->z > 500.0f) || (projectedPos->z < 0.0f))) { + return; + } + + for (i = 0; i < countLimit; i++) { + if (effect->type == FS_EFF_NONE) { + effect->type = FS_EFF_RIPPLE; + effect->pos = *pos; + effect->vel = sZeroVec; + effect->accel = sZeroVec; + effect->unk_30 = arg3 * 0.0025f; + effect->unk_34 = arg4 * 0.0025f; + + if (arg3 > 300.0f) { + effect->alpha = 0; + effect->unk_2E = arg5; + effect->unk_2C = 0; + effect->unk_38 = (effect->unk_34 - effect->unk_30) * 0.05f; + } else { + effect->alpha = arg5; + effect->unk_2C = 1; + effect->unk_38 = (effect->unk_34 - effect->unk_30) * 0.1f; + } + break; + } + + effect++; + } +} + +void Fishing_SpawnDustSplash(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos, Vec3f* vel, f32 scale) { + s16 i; + Vec3f accel = { 0.0f, -1.0f, 0.0f }; + + if ((projectedPos != NULL) && ((projectedPos->z > 500.0f) || (projectedPos->z < 0.0f))) { + return; + } + + for (i = 0; i < 100; i++) { + if ((effect->type == FS_EFF_NONE) || (effect->type == FS_EFF_RAIN_DROP) || + (effect->type == FS_EFF_RAIN_RIPPLE) || (effect->type == FS_EFF_RAIN_SPLASH)) { + effect->type = FS_EFF_DUST_SPLASH; + effect->pos = *pos; + effect->vel = *vel; + effect->accel = accel; + effect->alpha = 100 + (s16)Rand_ZeroFloat(100.0f); + effect->unk_30 = scale; + break; + } + + effect++; + } +} + +void Fishing_SpawnWaterDust(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos, f32 scale) { + s16 i; + Vec3f accel = { 0.0f, 0.05f, 0.0f }; + + if ((projectedPos != NULL) && ((projectedPos->z > 500.0f) || (projectedPos->z < 0.0f))) { + return; + } + + for (i = 0; i < 90; i++) { + if (effect->type == FS_EFF_NONE) { + effect->type = FS_EFF_WATER_DUST; + effect->pos = *pos; + effect->vel = sZeroVec; + effect->accel = accel; + effect->alpha = 255; + effect->timer = (s16)Rand_ZeroFloat(100.0f); + effect->unk_30 = scale; + effect->unk_34 = 2.0f * scale; + break; + } + + effect++; + } +} + +void Fishing_SpawnBubble(Vec3f* projectedPos, FishingEffect* effect, Vec3f* pos, f32 scale, u8 arg4) { + s16 i; + Vec3f vel = { 0.0f, 1.0f, 0.0f }; + + if ((projectedPos != NULL) && ((projectedPos->z > 500.0f) || (projectedPos->z < 0.0f))) { + return; + } + + for (i = 0; i < 90; i++) { + if (effect->type == FS_EFF_NONE) { + effect->type = FS_EFF_BUBBLE; + effect->pos = *pos; + effect->vel = vel; + effect->accel = sZeroVec; + effect->timer = (s16)Rand_ZeroFloat(100.0f); + effect->unk_30 = scale; + effect->unk_2C = arg4; + break; + } + + effect++; + } +} + +void Fishing_SpawnRainDrop(FishingEffect* effect, Vec3f* pos, Vec3f* rot) { + s16 i; + Vec3f velSrc; + + velSrc.x = 0.0f; + velSrc.y = 0.0f; + velSrc.z = 300.0f; + + effect += 30; + + for (i = 30; i < EFFECT_COUNT; i++) { + if (effect->type == FS_EFF_NONE) { + effect->type = FS_EFF_RAIN_DROP; + effect->pos = *pos; + effect->accel = sZeroVec; + effect->unk_34 = rot->x; + effect->unk_38 = rot->y; + effect->unk_3C = rot->z; + Matrix_RotateY(rot->y, MTXMODE_NEW); + Matrix_RotateX(rot->x, MTXMODE_APPLY); + Matrix_MultVec3f(&velSrc, &effect->vel); + break; + } + + effect++; + } +} + +static FishingPropInit sPondPropInits[POND_PROP_COUNT + 1] = { + { FS_PROP_ROCK, { 529, -53, -498 } }, + { FS_PROP_ROCK, { 461, -66, -480 } }, + { FS_PROP_ROCK, { 398, -73, -474 } }, + { FS_PROP_ROCK, { -226, -52, -691 } }, + { FS_PROP_ROCK, { -300, -41, -710 } }, + { FS_PROP_ROCK, { -333, -50, -643 } }, + { FS_PROP_ROCK, { -387, -46, -632 } }, + { FS_PROP_ROCK, { -484, -43, -596 } }, + { FS_PROP_ROCK, { -409, -57, -560 } }, + { FS_PROP_WOOD_POST, { 444, -87, -322 } }, + { FS_PROP_WOOD_POST, { 447, -91, -274 } }, + { FS_PROP_WOOD_POST, { 395, -109, -189 } }, + { FS_PROP_REED, { 617, -29, 646 } }, + { FS_PROP_REED, { 698, -26, 584 } }, + { FS_PROP_REED, { 711, -29, 501 } }, + { FS_PROP_REED, { 757, -28, 457 } }, + { FS_PROP_REED, { 812, -29, 341 } }, + { FS_PROP_REED, { 856, -30, 235 } }, + { FS_PROP_REED, { 847, -31, 83 } }, + { FS_PROP_REED, { 900, -26, 119 } }, + { FS_PROP_LILY_PAD, { 861, -22, 137 } }, + { FS_PROP_LILY_PAD, { 836, -22, 150 } }, + { FS_PROP_LILY_PAD, { 829, -22, 200 } }, + { FS_PROP_LILY_PAD, { 788, -22, 232 } }, + { FS_PROP_LILY_PAD, { 803, -22, 319 } }, + { FS_PROP_LILY_PAD, { 756, -22, 348 } }, + { FS_PROP_LILY_PAD, { 731, -22, 377 } }, + { FS_PROP_LILY_PAD, { 700, -22, 392 } }, + { FS_PROP_LILY_PAD, { 706, -22, 351 } }, + { FS_PROP_LILY_PAD, { 677, -22, 286 } }, + { FS_PROP_LILY_PAD, { 691, -22, 250 } }, + { FS_PROP_LILY_PAD, { 744, -22, 290 } }, + { FS_PROP_LILY_PAD, { 766, -22, 201 } }, + { FS_PROP_LILY_PAD, { 781, -22, 128 } }, + { FS_PROP_LILY_PAD, { 817, -22, 46 } }, + { FS_PROP_LILY_PAD, { 857, -22, -50 } }, + { FS_PROP_LILY_PAD, { 724, -22, 110 } }, + { FS_PROP_LILY_PAD, { 723, -22, 145 } }, + { FS_PROP_LILY_PAD, { 728, -22, 202 } }, + { FS_PROP_LILY_PAD, { 721, -22, 237 } }, + { FS_PROP_LILY_PAD, { 698, -22, 312 } }, + { FS_PROP_LILY_PAD, { 660, -22, 349 } }, + { FS_PROP_LILY_PAD, { 662, -22, 388 } }, + { FS_PROP_LILY_PAD, { 667, -22, 432 } }, + { FS_PROP_LILY_PAD, { 732, -22, 429 } }, + { FS_PROP_LILY_PAD, { 606, -22, 366 } }, + { FS_PROP_LILY_PAD, { 604, -22, 286 } }, + { FS_PROP_LILY_PAD, { 620, -22, 217 } }, + { FS_PROP_LILY_PAD, { 663, -22, 159 } }, + { FS_PROP_LILY_PAD, { 682, -22, 73 } }, + { FS_PROP_LILY_PAD, { 777, -22, 83 } }, + { FS_PROP_LILY_PAD, { 766, -22, 158 } }, + { FS_PROP_REED, { 1073, 0, -876 } }, + { FS_PROP_REED, { 970, 0, -853 } }, + { FS_PROP_REED, { 896, 0, -886 } }, + { FS_PROP_REED, { 646, -27, -651 } }, + { FS_PROP_REED, { 597, -29, -657 } }, + { FS_PROP_REED, { 547, -32, -651 } }, + { FS_PROP_REED, { 690, -29, -546 } }, + { FS_PROP_REED, { 720, -29, -490 } }, + { FS_PROP_REED, { -756, -30, -409 } }, + { FS_PROP_REED, { -688, -34, -458 } }, + { FS_PROP_REED, { -613, -34, -581 } }, + { FS_PROP_LILY_PAD, { -593, -22, -479 } }, + { FS_PROP_LILY_PAD, { -602, -22, -421 } }, + { FS_PROP_LILY_PAD, { -664, -22, -371 } }, + { FS_PROP_LILY_PAD, { -708, -22, -316 } }, + { FS_PROP_LILY_PAD, { -718, -22, -237 } }, + { FS_PROP_REED, { -807, -36, -183 } }, + { FS_PROP_REED, { -856, -29, -259 } }, + { FS_PROP_LILY_PAD, { -814, -22, -317 } }, + { FS_PROP_LILY_PAD, { -759, -22, -384 } }, + { FS_PROP_LILY_PAD, { -718, -22, -441 } }, + { FS_PROP_LILY_PAD, { -474, -22, -567 } }, + { FS_PROP_LILY_PAD, { -519, -22, -517 } }, + { FS_PROP_LILY_PAD, { -539, -22, -487 } }, + { FS_PROP_LILY_PAD, { -575, -22, -442 } }, + { FS_PROP_LILY_PAD, { -594, -22, -525 } }, + { FS_PROP_LILY_PAD, { -669, -22, -514 } }, + { FS_PROP_LILY_PAD, { -653, -22, -456 } }, + { FS_PROP_REED, { -663, -28, -606 } }, + { FS_PROP_REED, { -708, -26, -567 } }, + { FS_PROP_REED, { -739, -27, -506 } }, + { FS_PROP_REED, { -752, -28, -464 } }, + { FS_PROP_REED, { -709, -29, -513 } }, + { FS_PROP_LILY_PAD, { -544, -22, -436 } }, + { FS_PROP_LILY_PAD, { -559, -22, -397 } }, + { FS_PROP_LILY_PAD, { -616, -22, -353 } }, + { FS_PROP_LILY_PAD, { -712, -22, -368 } }, + { FS_PROP_LILY_PAD, { -678, -22, -403 } }, + { FS_PROP_LILY_PAD, { -664, -22, -273 } }, + { FS_PROP_LILY_PAD, { -630, -22, -276 } }, + { FS_PROP_LILY_PAD, { -579, -22, -311 } }, + { FS_PROP_LILY_PAD, { -588, -22, -351 } }, + { FS_PROP_LILY_PAD, { -555, -22, -534 } }, + { FS_PROP_LILY_PAD, { -547, -22, -567 } }, + { FS_PROP_LILY_PAD, { -592, -22, -571 } }, + { FS_PROP_LILY_PAD, { -541, -22, -610 } }, + { FS_PROP_LILY_PAD, { -476, -22, -629 } }, + { FS_PROP_LILY_PAD, { -439, -22, -598 } }, + { FS_PROP_LILY_PAD, { -412, -22, -550 } }, + { FS_PROP_LILY_PAD, { -411, -22, -606 } }, + { FS_PROP_LILY_PAD, { -370, -22, -634 } }, + { FS_PROP_LILY_PAD, { -352, -22, -662 } }, + { FS_PROP_LILY_PAD, { -413, -22, -641 } }, + { FS_PROP_LILY_PAD, { -488, -22, -666 } }, + { FS_PROP_LILY_PAD, { -578, -22, -656 } }, + { FS_PROP_LILY_PAD, { -560, -22, -640 } }, + { FS_PROP_LILY_PAD, { -531, -22, -654 } }, + { FS_PROP_LILY_PAD, { -451, -22, -669 } }, + { FS_PROP_LILY_PAD, { -439, -22, -699 } }, + { FS_PROP_LILY_PAD, { -482, -22, -719 } }, + { FS_PROP_LILY_PAD, { -524, -22, -720 } }, + { FS_PROP_LILY_PAD, { -569, -22, -714 } }, + { FS_PROP_REED, { -520, -27, -727 } }, + { FS_PROP_REED, { -572, -28, -686 } }, + { FS_PROP_REED, { -588, -32, -631 } }, + { FS_PROP_REED, { -622, -34, -571 } }, + { FS_PROP_REED, { -628, -36, -510 } }, + { FS_PROP_REED, { -655, -36, -466 } }, + { FS_PROP_REED, { -655, -41, -393 } }, + { FS_PROP_REED, { -661, -47, -328 } }, + { FS_PROP_REED, { -723, -40, -287 } }, + { FS_PROP_REED, { -756, -33, -349 } }, + { FS_PROP_REED, { -755, -43, -210 } }, + { FS_PROP_LILY_PAD, { -770, -22, -281 } }, + { FS_PROP_LILY_PAD, { -750, -22, -313 } }, + { FS_PROP_LILY_PAD, { -736, -22, -341 } }, + { FS_PROP_LILY_PAD, { -620, -22, -418 } }, + { FS_PROP_LILY_PAD, { -601, -22, -371 } }, + { FS_PROP_LILY_PAD, { -635, -22, -383 } }, + { FS_PROP_LILY_PAD, { -627, -22, -311 } }, + { FS_PROP_LILY_PAD, { -665, -22, -327 } }, + { FS_PROP_LILY_PAD, { -524, -22, -537 } }, + { FS_PROP_LILY_PAD, { -514, -22, -579 } }, + { FS_PROP_LILY_PAD, { -512, -22, -623 } }, + { FS_PROP_LILY_PAD, { -576, -22, -582 } }, + { FS_PROP_LILY_PAD, { -600, -22, -608 } }, + { FS_PROP_LILY_PAD, { -657, -22, -531 } }, + { FS_PROP_LILY_PAD, { -641, -22, -547 } }, + { FS_PROP_INIT_STOP, { 0 } }, +}; + +void Fishing_InitPondProps(Fishing* this, GlobalContext* globalCtx) { + FishingProp* prop = &sPondProps[0]; + Vec3f colliderPos; + s16 i; + + Fishing_SeedRand(1, 29100, 9786); + + for (i = 0; i < POND_PROP_COUNT; i++) { + if (sPondPropInits[i].type == FS_PROP_INIT_STOP) { + break; + } + + prop->type = sPondPropInits[i].type; + prop->pos.x = sPondPropInits[i].pos.x; + prop->pos.y = sPondPropInits[i].pos.y; + prop->pos.z = sPondPropInits[i].pos.z; + prop->rotX = 0.0f; + prop->reedAngle = 0.0f; + + prop->timer = Rand_ZeroFloat(100.0f); + prop->drawDistance = 800.0f; + + if (prop->type == FS_PROP_REED) { + prop->scale = (Fishing_RandZeroOne() * 0.25f) + 0.75f; + prop->reedAngle = Rand_ZeroFloat(2 * M_PI); + if (sLinkAge == 1) { + prop->scale *= 0.6f; + } + prop->drawDistance = 1200.0f; + } else if (prop->type == FS_PROP_WOOD_POST) { + prop->scale = 0.08f; + prop->drawDistance = 1200.0f; + colliderPos = prop->pos; + colliderPos.y += 50.0f; + Fishing_SetColliderElement(i, &sFishingMain->collider, &colliderPos, prop->scale * 3.5f); + } else if (prop->type == FS_PROP_LILY_PAD) { + prop->scale = (Fishing_RandZeroOne() * 0.3f) + 0.5f; + prop->rotY = Rand_ZeroFloat(2 * M_PI); + if (sLinkAge == 1) { + if ((i % 4) != 0) { + prop->scale *= 0.6f; + } else { + prop->type = FS_PROP_NONE; + } + } + } else { + prop->scale = (Fishing_RandZeroOne() * 0.1f) + 0.3f; + prop->rotY = Rand_ZeroFloat(2 * M_PI); + prop->drawDistance = 1000.0f; + Fishing_SetColliderElement(i, &sFishingMain->collider, &prop->pos, prop->scale); + } + + prop++; + } +} + +static FishingFishInit sFishInits[] = { + { 0, { 666, -45, 354 }, 38, 0.1f }, { 0, { 681, -45, 240 }, 36, 0.1f }, { 0, { 670, -45, 90 }, 41, 0.05f }, + { 0, { 615, -45, -450 }, 35, 0.2f }, { 0, { 500, -45, -420 }, 39, 0.1f }, { 0, { 420, -45, -550 }, 44, 0.05f }, + { 0, { -264, -45, -640 }, 40, 0.1f }, { 0, { -470, -45, -540 }, 34, 0.2f }, { 0, { -557, -45, -430 }, 54, 0.01f }, + { 0, { -260, -60, -330 }, 47, 0.05f }, { 0, { -500, -60, 330 }, 42, 0.06f }, { 0, { 428, -40, -283 }, 33, 0.2f }, + { 0, { 409, -70, -230 }, 57, 0.0f }, { 0, { 450, -67, -300 }, 63, 0.0f }, { 0, { -136, -65, -196 }, 71, 0.0f }, + { 1, { -561, -35, -547 }, 45, 0.0f }, { 1, { 667, -35, 317 }, 43, 0.0f }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 5, ICHAIN_CONTINUE), + ICHAIN_F32(targetArrowOffset, 0, ICHAIN_STOP), +}; + +void Fishing_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Fishing* this = (Fishing*)thisx; + u16 fishCount; + s16 i; + + Actor_ProcessInitChain(thisx, sInitChain); + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + + if (KREG(5) != 0) { + sLinkAge = 1; + } else { + sLinkAge = gSaveContext.linkAge; + } + + if (thisx->params < 100) { + D_80B7E074 = 0; + sFishingMain = this; + Collider_InitJntSph(globalCtx, &sFishingMain->collider); + Collider_SetJntSph(globalCtx, &sFishingMain->collider, thisx, &sJntSphInit, sFishingMain->colliderElements); + + thisx->params = 1; + + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishingOwnerSkel, &gFishingOwnerAnim, NULL, NULL, 0); + Animation_MorphToLoop(&this->skelAnime, &gFishingOwnerAnim, 0.0f); + + thisx->update = Fishing_UpdateOwner; + thisx->draw = Fishing_DrawOwner; + + thisx->shape.rot.y = -0x6000; + thisx->world.pos.x = 160.0f; + thisx->world.pos.y = -2.0f; + thisx->world.pos.z = 1208.0f; + + Actor_SetScale(thisx, 0.011f); + + thisx->focus.pos = thisx->world.pos; + thisx->focus.pos.y += 75.0f; + thisx->flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + + if (sLinkAge != 1) { + if (HIGH_SCORE(HS_FISHING) & 0x1000) { + D_80B7A688 = 0; + } else { + D_80B7A688 = 1; + } + } else { + D_80B7A688 = 2; + } + + D_80B7A684 = 20; + globalCtx->specialEffects = sFishingEffects; + gTimeIncrement = 1; + D_80B7E0AC = 0; + D_80B7E0A6 = 10; + + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x0100FF); + + if (sLinkAge == 1) { + if ((HIGH_SCORE(HS_FISHING) & 0x7F) != 0) { + D_80B7E078 = HIGH_SCORE(HS_FISHING) & 0x7F; + } else { + D_80B7E078 = 40.0f; + } + } else { + if ((HIGH_SCORE(HS_FISHING) & 0x7F000000) != 0) { + D_80B7E078 = (HIGH_SCORE(HS_FISHING) & 0x7F000000) >> 0x18; + } else { + D_80B7E078 = 45.0f; + } + } + + D_80B7E07D = (HIGH_SCORE(HS_FISHING) & 0xFF0000) >> 0x10; + if ((D_80B7E07D & 7) == 7) { + globalCtx->roomCtx.unk_74[0] = 90; + D_80B7E076 = 1; + } else { + globalCtx->roomCtx.unk_74[0] = 40; + D_80B7E076 = 0; + } + + if (((D_80B7E07D & 7) == 6) || (KREG(3) != 0)) { + D_80B7E077 = 100; + if (KREG(3) != 0) { + KREG(3) = 0; + HIGH_SCORE(HS_FISHING) &= 0xFF00FFFF; + HIGH_SCORE(HS_FISHING) |= 0x60000; + } + } else { + D_80B7E077 = 0; + } + + for (i = 0; i < EFFECT_COUNT; i++) { + sFishingEffects[i].type = FS_EFF_NONE; + } + + for (i = 0; i < POND_PROP_COUNT; i++) { + sPondProps[i].type = FS_PROP_NONE; + } + + sFishGroupAngle1 = 0.7f; + sFishGroupAngle2 = 2.3f; + sFishGroupAngle3 = 4.6f; + + for (i = 0; i < GROUP_FISH_COUNT; i++) { + FishingGroupFish* fish = &sGroupFishes[i]; + + fish->type = FS_GROUP_FISH_NORMAL; + + if (i <= 20) { + fish->unk_10.x = fish->pos.x = sinf(sFishGroupAngle1) * 720.0f; + fish->unk_10.z = fish->pos.z = cosf(sFishGroupAngle1) * 720.0f; + } else if (i <= 40) { + fish->unk_10.x = fish->pos.x = sinf(sFishGroupAngle2) * 720.0f; + fish->unk_10.z = fish->pos.z = cosf(sFishGroupAngle2) * 720.0f; + } else { + fish->unk_10.x = fish->pos.x = sinf(sFishGroupAngle3) * 720.0f; + fish->unk_10.z = fish->pos.z = cosf(sFishGroupAngle3) * 720.0f; + } + + fish->unk_10.y = fish->pos.y = -35.0f; + + fish->timer = Rand_ZeroFloat(100.0f); + + fish->unk_3C = 0; + fish->unk_3E = 0; + fish->unk_40 = 0; + + if (sLinkAge != 1) { + if (((i >= 15) && (i < 20)) || ((i >= 35) && (i < 40)) || ((i >= 55) && (i < 60))) { + fish->type = FS_GROUP_FISH_NONE; + } + } + } + + Fishing_InitPondProps(this, globalCtx); + Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_EN_KANBAN, 53.0f, -17.0f, 982.0f, 0, 0, 0, + ENKANBAN_FISHING); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_FISHING, 0.0f, 0.0f, 0.0f, 0, 0, 0, 200); + + if ((KREG(1) == 1) || ((D_80B7E07D & 3) == 3)) { + if (sLinkAge != 1) { + fishCount = 16; + } else { + fishCount = 17; + } + } else { + fishCount = 15; + } + + for (i = 0; i < fishCount; i++) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_FISHING, sFishInits[i].pos.x, sFishInits[i].pos.y, + sFishInits[i].pos.z, 0, Rand_ZeroFloat(0x10000), 0, 100 + i); + } + } else { + if ((thisx->params < 115) || (thisx->params == 200)) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishingFishSkel, &gFishingFishAnim, NULL, NULL, 0); + Animation_MorphToLoop(&this->skelAnime, &gFishingFishAnim, 0.0f); + } else { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishingLoachSkel, &gFishingLoachAnim, NULL, NULL, 0); + Animation_MorphToLoop(&this->skelAnime, &gFishingLoachAnim, 0.0f); + } + + SkelAnime_Update(&this->skelAnime); + + if (thisx->params == 200) { + this->unk_158 = 100; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, thisx, ACTORCAT_PROP); + thisx->targetMode = 0; + thisx->flags |= ACTOR_FLAG_0 | ACTOR_FLAG_3; + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + } else { + this->unk_158 = 10; + this->unk_15A = 10; + + this->unk_150 = sFishInits[thisx->params - 100].unk_00; + this->unk_1A8 = sFishInits[thisx->params - 100].unk_0C; + this->unk_1AC = sFishInits[thisx->params - 100].unk_08; + + this->unk_1AC += Rand_ZeroFloat(4.99999f); + + if ((this->unk_1AC >= 65.0f) && (Rand_ZeroOne() < 0.05f)) { + this->unk_1AC += Rand_ZeroFloat(7.99999f); + } + + if (KREG(6) != 0) { + this->unk_1AC = KREG(6) + 80.0f; + } + + if (sLinkAge == 1) { + this->unk_1AC *= 0.73f; + } + } + } +} + +void Fishing_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Fishing* this = (Fishing*)thisx; + + SkelAnime_Free(&this->skelAnime, globalCtx); + + if (thisx->params == 200) { + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + } else if (thisx->params == 1) { + Collider_DestroyJntSph(globalCtx, &this->collider); + } +} + +void Fishing_UpdateEffects(FishingEffect* effect, GlobalContext* globalCtx) { + f32 rippleY; + s16 i; + + for (i = 0; i < EFFECT_COUNT; i++) { + if (effect->type) { + effect->timer++; + effect->pos.x += effect->vel.x; + effect->pos.y += effect->vel.y; + effect->pos.z += effect->vel.z; + effect->vel.y += effect->accel.y; + + if (effect->type == FS_EFF_RIPPLE) { + Math_ApproachF(&effect->unk_30, effect->unk_34, 0.2f, effect->unk_38); + + if (effect->unk_2C == 0) { + effect->alpha += 20; + + if (effect->alpha >= effect->unk_2E) { + effect->alpha = effect->unk_2E; + effect->unk_2C++; + } + } else { + effect->alpha -= 8; + + if (effect->alpha <= 0) { + effect->type = FS_EFF_NONE; + } + } + } else if (effect->type == FS_EFF_WATER_DUST) { + Math_ApproachF(&effect->unk_30, effect->unk_34, 0.1f, 0.1f); + effect->alpha -= 10; + + if (effect->pos.y > (WATER_SURFACE_Y(globalCtx) - 5.0f)) { + effect->accel.y = 0.0f; + effect->vel.y = 0.0f; + effect->alpha -= 5; + } + + if (effect->alpha <= 0) { + effect->type = FS_EFF_NONE; + } + } else if (effect->type == FS_EFF_BUBBLE) { + if (effect->unk_2C == 0) { + rippleY = WATER_SURFACE_Y(globalCtx); + } else { + rippleY = 69.0f; + } + + if (effect->pos.y >= rippleY) { + effect->type = FS_EFF_NONE; + + if (Rand_ZeroOne() < 0.3f) { + Vec3f pos = effect->pos; + pos.y = rippleY; + Fishing_SpawnRipple(NULL, globalCtx->specialEffects, &pos, 20.0f, 60.0f, 150, 90); + } + } + } else if (effect->type == FS_EFF_DUST_SPLASH) { + if (effect->vel.y < -20.0f) { + effect->vel.y = -20.0f; + effect->accel.y = 0.0f; + } + + if (effect->pos.y <= WATER_SURFACE_Y(globalCtx)) { + effect->type = FS_EFF_NONE; + if (Rand_ZeroOne() < 0.5f) { + Vec3f pos = effect->pos; + pos.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(NULL, globalCtx->specialEffects, &pos, 40.0f, 110.0f, 150, 90); + } + } + } else if (effect->type == FS_EFF_RAIN_DROP) { + if (effect->pos.y < WATER_SURFACE_Y(globalCtx)) { + f32 sqDistXZ = SQ(effect->pos.x) + SQ(effect->pos.z); + + if (sqDistXZ > SQ(920.0f)) { + effect->pos.y = WATER_SURFACE_Y(globalCtx) + ((sqrtf(sqDistXZ) - 920.0f) * 0.11f); + effect->timer = KREG(17) + 2; + effect->type = FS_EFF_RAIN_SPLASH; + effect->unk_30 = (KREG(18) + 30) * 0.001f; + } else { + effect->pos.y = WATER_SURFACE_Y(globalCtx) + 3.0f; + effect->timer = 0; + if (Rand_ZeroOne() < 0.75f) { + effect->type = FS_EFF_RAIN_RIPPLE; + effect->vel = sZeroVec; + effect->unk_30 = (KREG(18) + 30) * 0.001f; + } else { + effect->type = FS_EFF_NONE; + } + } + + effect->vel = sZeroVec; + } + } else if (effect->type >= FS_EFF_RAIN_RIPPLE) { + effect->unk_30 += (KREG(18) + 30) * 0.001f; + + if (effect->timer >= 6) { + effect->type = FS_EFF_NONE; + } + } else if (effect->type == FS_EFF_OWNER_HAT) { + f32 sqDistXZ; + f32 bottomY; + + effect->unk_30 = 0.010000001f; + + Math_ApproachS(&sEffOwnerHatRot.y, 0, 20, 100); + Math_ApproachS(&sEffOwnerHatRot.x, 0, 20, 100); + Math_ApproachS(&sEffOwnerHatRot.z, -0x4000, 20, 100); + + sqDistXZ = SQ(effect->pos.x) + SQ(effect->pos.z); + bottomY = WATER_SURFACE_Y(globalCtx) + ((sqrtf(sqDistXZ) - 920.0f) * 0.147f); + + if (effect->pos.y > (bottomY - 10.0f)) { + effect->pos.y -= 0.1f; + } + + if ((effect->timer % 16) == 0) { + Vec3f pos = effect->pos; + pos.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(NULL, globalCtx->specialEffects, &pos, 30.0f, 300.0f, 150, 90); + } + + if (effect->unk_2C >= 0) { + effect->unk_2C++; + } + + if (effect->unk_2C == 30) { + Message_StartTextbox(globalCtx, 0x40B3, NULL); + } + + if ((effect->unk_2C >= 100) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT)) { + if (Message_ShouldAdvance(globalCtx) || (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + Message_CloseTextbox(globalCtx); + Rupees_ChangeBy(-50); + effect->unk_2C = -1; + } + } + } + } + + effect++; + } +} + +void Fishing_DrawEffects(FishingEffect* effect, GlobalContext* globalCtx) { + u8 flag = 0; + f32 rotY; + s16 i; + s32 pad; + FishingEffect* firstEffect = effect; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 2271); + + Matrix_Push(); + + gDPPipeSync(POLY_XLU_DISP++); + + for (i = 0; i < 100; i++) { + if (effect->type == FS_EFF_RIPPLE) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingRippleMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->unk_30, 1.0f, effect->unk_30, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2305), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFishingRippleModelDL); + } + effect++; + } + + effect = firstEffect; + flag = 0; + for (i = 0; i < 100; i++) { + if (effect->type == FS_EFF_DUST_SPLASH) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingDustSplashMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, 0); + flag++; + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 180, 180, 180, effect->alpha); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->unk_30, effect->unk_30, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2346), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFishingDustSplashModelDL); + } + effect++; + } + + effect = firstEffect; + flag = 0; + for (i = 0; i < 100; i++) { + if (effect->type == FS_EFF_WATER_DUST) { + if (flag == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFishingWaterDustMaterialDL); + gDPSetEnvColor(POLY_OPA_DISP++, 40, 90, 80, 128); + flag++; + } + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 40, 90, 80, effect->alpha); + + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, effect->timer + (i * 3), + (effect->timer + (i * 3)) * 5, 32, 64, 1, 0, 0, 32, 32)); + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->unk_30, effect->unk_30, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2394), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gFishingWaterDustModelDL); + } + effect++; + } + + effect = firstEffect; + flag = 0; + for (i = 0; i < 100; i++) { + if (effect->type == FS_EFF_BUBBLE) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingBubbleMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 150, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 255); + flag++; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Scale(effect->unk_30, effect->unk_30, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2423), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFishingBubbleModelDL); + } + effect++; + } + + effect = firstEffect + 30; + flag = 0; + for (i = 30; i < EFFECT_COUNT; i++) { + if (effect->type == FS_EFF_RAIN_DROP) { + if (flag == 0) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + gDPSetCombineMode(POLY_XLU_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 150, 255, 255, 30); + flag++; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateY(effect->unk_38, MTXMODE_APPLY); + Matrix_RotateX(effect->unk_34, MTXMODE_APPLY); + Matrix_RotateZ(effect->unk_3C, MTXMODE_APPLY); + Matrix_Scale(0.002f, 1.0f, 0.1f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2467), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFishingRainDropModelDL); + } + effect++; + } + + func_80093D84(globalCtx->state.gfxCtx); + + effect = firstEffect + 30; + flag = 0; + for (i = 30; i < EFFECT_COUNT; i++) { + if (effect->type == FS_EFF_RAIN_RIPPLE) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingRippleMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 155, 155, 155, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 130); + flag++; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_Scale(effect->unk_30, 1.0f, effect->unk_30, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2504), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFishingRippleModelDL); + } + effect++; + } + + effect = firstEffect + 30; + flag = 0; + for (i = 30; i < EFFECT_COUNT; i++) { + if (effect->type == FS_EFF_RAIN_SPLASH) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingRainSplashMaterialDL); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, KREG(19) + 80); + flag++; + } + + if (Rand_ZeroOne() < 0.5f) { + rotY = 0.0f; + } else { + rotY = M_PI; + } + + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateY(rotY, MTXMODE_APPLY); + Matrix_Scale(effect->unk_30, effect->unk_30, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2541), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gFishingRainSplashModelDL); + } + effect++; + } + + effect = firstEffect; + if (effect->type == FS_EFF_OWNER_HAT) { + Matrix_Translate(effect->pos.x, effect->pos.y, effect->pos.z, MTXMODE_NEW); + Matrix_RotateY((sEffOwnerHatRot.y * M_PI) / 32768, MTXMODE_APPLY); + Matrix_RotateX((sEffOwnerHatRot.x * M_PI) / 32768, MTXMODE_APPLY); + Matrix_RotateZ((sEffOwnerHatRot.z * M_PI) / 32768, MTXMODE_APPLY); + Matrix_Scale(effect->unk_30, effect->unk_30, effect->unk_30, MTXMODE_APPLY); + Matrix_Translate(-1250.0f, 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2560), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gFishingOwnerHatDL); + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 2565); +} + +void Fishing_DrawStreamSplash(GlobalContext* globalCtx) { + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 2572); + + gSPSegment(POLY_XLU_DISP++, 0x09, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames * 1, + globalCtx->gameplayFrames * 8, 32, 64, 1, -(globalCtx->gameplayFrames * 2), 0, 16, 16)); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 225, 235, 50); + + Matrix_Translate(670.0f, -24.0f, -600.0f, MTXMODE_NEW); + Matrix_Scale(0.02f, 1.0f, 0.02f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 2598), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFishingStreamSplashDL)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 2613); +} + +s32 func_80B6C2EC(Vec3f* vec) { + if (((vec->x >= 110.0f) && (vec->x <= 150.0f) && (vec->z <= 1400.0f) && (vec->z >= 1160.0f)) || + ((vec->x >= 110.0f) && (vec->x <= 210.0f) && (vec->z <= 1200.0f) && (vec->z >= 1160.0f))) { + if (vec->y <= 42.0f) { + return true; + } + } + + return false; +} + +void Fishing_UpdateLine(GlobalContext* globalCtx, Vec3f* basePos, Vec3f* pos, Vec3f* rot, Vec3f* unk) { + s16 i; + s16 k; + f32 dx; + f32 dy; + f32 dz; + f32 rx; + f32 ry; + f32 dist; + f32 spD8; + s16 temp_s2; + s32 pad; + f32 temp_f20; + Vec3f posSrc = { 0.0f, 0.0f, 0.0f }; + Vec3f posStep; + f32 phi_f18; + Vec3f spA4; + Vec3f sp98; + f32 sp94; + f32 sp90; + f32 sp8C; + f32 sqDistXZ; + f32 temp_f18; + f32 phi_f12; + f32 phi_f2; + + if (D_80B7A6A4 != 0) { + spA4 = *basePos; + sp98 = pos[LINE_SEG_COUNT - 1]; + + sp94 = sp98.x - spA4.x; + sp90 = sp98.y - spA4.y; + sp8C = sp98.z - spA4.z; + + phi_f18 = sqrtf(SQ(sp94) + SQ(sp90) + SQ(sp8C)) * 0.97f; + if (phi_f18 > 1000.0f) { + phi_f18 = 1000.0f; + } + + D_80B7E144 = 200.0f - (phi_f18 * 200.0f * 0.001f); + } + + temp_s2 = D_80B7E144; + posSrc.z = 5.0f; + + for (i = 0; i < LINE_SEG_COUNT; i++) { + if (i <= temp_s2) { + pos[i] = *basePos; + } else if (D_80B7A6A4 != 0) { + temp_f20 = (f32)(i - temp_s2) / (f32)(LINE_SEG_COUNT - temp_s2 + 1); + Math_ApproachF(&pos[i].x, (sp94 * temp_f20) + spA4.x, 1.0f, 20.0f); + Math_ApproachF(&pos[i].y, (sp90 * temp_f20) + spA4.y, 1.0f, 20.0f); + Math_ApproachF(&pos[i].z, (sp8C * temp_f20) + spA4.z, 1.0f, 20.0f); + } + } + + for (i = temp_s2 + 1, k = 0; i < LINE_SEG_COUNT; i++, k++) { + temp_f18 = 2.0f * D_80B7E148; + + dx = (pos + i)->x - (pos + i - 1)->x; + spD8 = (pos + i)->y; + + sqDistXZ = SQ((pos + i)->x) + SQ((pos + i)->z); + + if (sqDistXZ > SQ(920.0f)) { + phi_f12 = ((sqrtf(sqDistXZ) - 920.0f) * 0.11f) + WATER_SURFACE_Y(globalCtx); + } else { + phi_f12 = WATER_SURFACE_Y(globalCtx); + } + + if (D_80B7E0B6 == 2) { + if (spD8 < phi_f12) { + phi_f12 = ((sqrtf(sqDistXZ) - 920.0f) * 0.147f) + WATER_SURFACE_Y(globalCtx); + if (spD8 > phi_f12) { + phi_f2 = (spD8 - phi_f12) * 0.05f; + if (phi_f2 > 0.29999998f) { + phi_f2 = 0.29999998f; + } + if (i >= 100) { + phi_f2 *= (i - 100) * 0.02f; + spD8 -= phi_f2; + } + } + } else { + spD8 -= temp_f18; + } + } else if (i > LINE_SEG_COUNT - 10) { + if (spD8 > phi_f12) { + phi_f2 = (spD8 - phi_f12) * 0.2f; + if (phi_f2 > temp_f18) { + phi_f2 = temp_f18; + } + spD8 -= phi_f2; + } + } else { + if (spD8 > phi_f12) { + spD8 -= temp_f18; + } + } + + if (func_80B6C2EC(&pos[i])) { + spD8 = 42.0f; + } + + dy = spD8 - (pos + i - 1)->y; + dz = (pos + i)->z - (pos + i - 1)->z; + + ry = Math_Atan2F(dz, dx); + dist = sqrtf(SQ(dx) + SQ(dz)); + rx = -Math_Atan2F(dist, dy); + + (rot + i - 1)->y = ry; + (rot + i - 1)->x = rx; + + Matrix_RotateY(ry, MTXMODE_NEW); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_MultVec3f(&posSrc, &posStep); + + (pos + i)->x = (pos + i - 1)->x + posStep.x; + (pos + i)->y = (pos + i - 1)->y + posStep.y; + (pos + i)->z = (pos + i - 1)->z + posStep.z; + } +} + +void Fishing_UpdateLinePos(Vec3f* pos) { + s16 i; + f32 dx; + f32 dy; + f32 dz; + f32 rx; + f32 ry; + f32 dist; + Vec3f posSrc = { 0.0f, 0.0f, 0.0f }; + Vec3f posStep; + s16 min = D_80B7E144; + + posSrc.z = 5.0f; + + for (i = LINE_SEG_COUNT - 2; i > min; i--) { + dx = (pos + i)->x - (pos + i + 1)->x; + dy = (pos + i)->y - (pos + i + 1)->y; + dz = (pos + i)->z - (pos + i + 1)->z; + + ry = Math_Atan2F(dz, dx); + dist = sqrtf(SQ(dx) + SQ(dz)); + rx = -Math_Atan2F(dist, dy); + + Matrix_RotateY(ry, MTXMODE_NEW); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_MultVec3f(&posSrc, &posStep); + + (pos + i)->x = (pos + i + 1)->x + posStep.x; + (pos + i)->y = (pos + i + 1)->y + posStep.y; + (pos + i)->z = (pos + i + 1)->z + posStep.z; + } +} + +void Fishing_DrawLureHook(GlobalContext* globalCtx, Vec3f* pos, Vec3f* refPos, u8 hookIndex) { + f32 dx; + f32 dy; + f32 dz; + f32 rx; + f32 ry; + f32 dist; + f32 offsetY; + Vec3f posSrc = { 0.0f, 0.0f, 1.0f }; + Vec3f posStep; + Player* player = GET_PLAYER(globalCtx); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 2963); + + Matrix_Push(); + + if ((D_80B7A694 == 3) && ((pos->y > WATER_SURFACE_Y(globalCtx)) || ((D_80B7A68C != 0) && hookIndex))) { + offsetY = 0.0f; + } else if (pos->y < WATER_SURFACE_Y(globalCtx)) { + offsetY = -1.0f; + } else { + offsetY = -3.0f; + } + + dx = refPos->x - pos->x; + dy = refPos->y - pos->y + offsetY; + dz = refPos->z - pos->z; + + ry = Math_Atan2F(dz, dx); + dist = sqrtf(SQ(dx) + SQ(dz)); + rx = -Math_Atan2F(dist, dy); + + Matrix_RotateY(ry, MTXMODE_NEW); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_MultVec3f(&posSrc, &posStep); + + refPos->x = pos->x + posStep.x; + refPos->y = pos->y + posStep.y; + refPos->z = pos->z + posStep.z; + + Matrix_Translate(pos->x, pos->y, pos->z, MTXMODE_NEW); + + if ((player->actor.speedXZ == 0.0f) && (D_80B7E138 == 0.0f)) { + Math_ApproachF(&sLureHookRotY[hookIndex], ry, 0.1f, 0.3f); + } else { + sLureHookRotY[hookIndex] = ry; + } + + Matrix_RotateY(sLureHookRotY[hookIndex], MTXMODE_APPLY); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_Scale(0.0039999997f, 0.0039999997f, 0.005f, MTXMODE_APPLY); + Matrix_RotateY(M_PI, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3029), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingLureHookDL); + + Matrix_RotateZ(M_PI / 2, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3034), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingLureHookDL); + + if ((hookIndex == 1) && (D_80B7A68C != 0)) { + Matrix_Scale(2.0f, 2.0f, 2.0f, MTXMODE_APPLY); + Matrix_Translate(250.0f, 0.0f, -1400.0f, MTXMODE_APPLY); + Matrix_Push(); + + if (D_80B7A690 != 0) { + FishingEffect* effect = globalCtx->specialEffects; + MtxF mf; + + Matrix_MultVec3f(&sZeroVec, &effect->pos); + Matrix_Get(&mf); + Matrix_MtxFToYXZRotS(&mf, &sEffOwnerHatRot, 0); + + D_80B7A690 = 0; + D_80B7A68C = 0; + + effect->type = FS_EFF_OWNER_HAT; + effect->unk_2C = 0; + effect->vel = sZeroVec; + effect->accel = sZeroVec; + } + + Matrix_Pop(); + Matrix_Translate(-1250.0f, 0.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateX(M_PI / 2, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3085), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingOwnerHatDL); + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3098); +} + +void Fishing_UpdateSinkingLure(GlobalContext* globalCtx) { + s16 i; + f32 dx; + f32 dy; + f32 dz; + f32 rx; + f32 ry; + f32 dist; + f32 offsetY; + Vec3f posSrc = { 0.0f, 0.0f, 0.0f }; + Vec3f posStep; + Vec3f sp94; + Vec3f sp88; + f32 offsetX; + f32 offsetZ; + Player* player = GET_PLAYER(globalCtx); + + posSrc.z = 0.85f; + + sSinkingLurePos[0] = sLurePos; + + if (D_80B7A6D4 != 0) { + offsetY = -1.0f; + } else if (sLurePos.y < WATER_SURFACE_Y(globalCtx)) { + offsetY = 0.5f; + } else { + offsetY = -5.0f; + } + + if (D_80B7A694 == 5) { + Matrix_RotateY(player->actor.shape.rot.y * (M_PI / 32768), MTXMODE_NEW); + sp94.x = 5.0f; + sp94.y = 0.0f; + sp94.z = 3.0f; + Matrix_MultVec3f(&sp94, &sp88); + } + + for (i = 1; i < SINKING_LURE_SEG_COUNT; i++) { + Vec3f* pos = sSinkingLurePos; + + if ((i < 10) && (D_80B7A694 == 5)) { + offsetX = (10 - i) * sp88.x * 0.1f; + offsetZ = (10 - i) * sp88.z * 0.1f; + } else { + offsetX = offsetZ = 0.0f; + } + + dx = (pos + i)->x - (pos + i - 1)->x + offsetX; + dy = (pos + i)->y - (pos + i - 1)->y + offsetY; + dz = (pos + i)->z - (pos + i - 1)->z + offsetZ; + + ry = Math_Atan2F(dz, dx); + dist = sqrtf(SQ(dx) + SQ(dz)); + rx = -Math_Atan2F(dist, dy); + + Matrix_RotateY(ry, MTXMODE_NEW); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_MultVec3f(&posSrc, &posStep); + + (pos + i)->x = (pos + i - 1)->x + posStep.x; + (pos + i)->y = (pos + i - 1)->y + posStep.y; + (pos + i)->z = (pos + i - 1)->z + posStep.z; + } +} + +static f32 sSinkingLureSizes[] = { + 1.0f, 1.5f, 1.8f, 2.0f, 1.8f, 1.6f, 1.4f, 1.2f, 1.0f, 1.0f, + 0.9f, 0.85f, 0.8f, 0.7f, 0.8f, 1.0f, 1.2f, 1.1f, 1.0f, 0.8f, +}; + +void Fishing_DrawSinkingLure(GlobalContext* globalCtx) { + s16 i; + f32 scale; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3209); + + Fishing_UpdateSinkingLure(globalCtx); + + if (sLurePos.y < WATER_SURFACE_Y(globalCtx)) { + func_80093D18(globalCtx->state.gfxCtx); + + gSPDisplayList(POLY_OPA_DISP++, gFishingSinkingLureSegmentMaterialDL); + + for (i = SINKING_LURE_SEG_COUNT - 1; i >= 0; i--) { + if ((i + D_80B7FEA0) < SINKING_LURE_SEG_COUNT) { + Matrix_Translate(sSinkingLurePos[i].x, sSinkingLurePos[i].y, sSinkingLurePos[i].z, MTXMODE_NEW); + scale = sSinkingLureSizes[i + D_80B7FEA0] * 0.04f; + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3239), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingSinkingLureSegmentModelDL); + } + } + } else { + func_80093D84(globalCtx->state.gfxCtx); + + gSPDisplayList(POLY_XLU_DISP++, gFishingSinkingLureSegmentMaterialDL); + + for (i = SINKING_LURE_SEG_COUNT - 1; i >= 0; i--) { + if ((i + D_80B7FEA0) < SINKING_LURE_SEG_COUNT) { + Matrix_Translate(sSinkingLurePos[i].x, sSinkingLurePos[i].y, sSinkingLurePos[i].z, MTXMODE_NEW); + scale = sSinkingLureSizes[i + D_80B7FEA0] * 0.04f; + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3265), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFishingSinkingLureSegmentModelDL); + } + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3271); +} + +void Fishing_DrawLureAndLine(GlobalContext* globalCtx, Vec3f* linePos, Vec3f* lineRot) { + Vec3f posSrc; + Vec3f posStep; + Vec3f hookPos[2]; + s16 i; + s16 spB4 = D_80B7E144; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3287); + + func_80093D18(globalCtx->state.gfxCtx); + Matrix_Push(); + + if (D_80B7A6D4 != 0) { + Vec3f posTemp = sLurePos; + sLurePos = sSinkingLureBasePos; + Fishing_DrawSinkingLure(globalCtx); + sLurePos = posTemp; + } + + if ((D_80B7A694 == 4) || (D_80B7A694 == 5)) { + sLurePos = sFishingHookedFish->fishMouthPos; + + if ((D_80B7A694 == 5) && (D_80B7E0B6 == 2)) { + Matrix_RotateY(player->actor.shape.rot.y * (M_PI / 32768), MTXMODE_NEW); + posSrc.x = 2.0f; + posSrc.y = 0.0f; + posSrc.z = 0.0f; + Matrix_MultVec3f(&posSrc, &posStep); + sLurePos.x += posStep.x; + sLurePos.z += posStep.z; + } + } else if (D_80B7A694 == 0) { + sLurePos = sReelLinePos[LINE_SEG_COUNT - 1]; + sLureRot.x = sReelLineRot[LINE_SEG_COUNT - 2].x + M_PI; + + if ((player->actor.speedXZ == 0.0f) && (D_80B7E0B0 == 0)) { + Math_ApproachF(&sLureRot.y, sReelLineRot[LINE_SEG_COUNT - 2].y, 0.1f, 0.2f); + } else { + sLureRot.y = sReelLineRot[LINE_SEG_COUNT - 2].y; + } + } + + if (D_80B7E0B6 != 2) { + Matrix_Translate(sLurePos.x, sLurePos.y, sLurePos.z, MTXMODE_NEW); + Matrix_RotateY(sLureRot.y + D_80B7E104, MTXMODE_APPLY); + Matrix_RotateX(sLureRot.x, MTXMODE_APPLY); + Matrix_Scale(0.0039999997f, 0.0039999997f, 0.0039999997f, MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, D_80B7E108, MTXMODE_APPLY); + Matrix_RotateZ(M_PI / 2, MTXMODE_APPLY); + Matrix_RotateY(M_PI / 2, MTXMODE_APPLY); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3369), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingLureFloatDL); + + posSrc.x = -850.0f; + posSrc.y = 0.0f; + posSrc.z = 0.0f; + Matrix_MultVec3f(&posSrc, &D_80B7E0C8); + + posSrc.x = 500.0f; + posSrc.z = -300.0f; + Matrix_MultVec3f(&posSrc, &hookPos[0]); + Fishing_DrawLureHook(globalCtx, &hookPos[0], &sLureHookRefPos[0], 0); + + posSrc.x = 2100.0f; + posSrc.z = -50.0f; + Matrix_MultVec3f(&posSrc, &hookPos[1]); + Fishing_DrawLureHook(globalCtx, &hookPos[1], &sLureHookRefPos[1], 1); + } + + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + + gDPSetCombineMode(POLY_XLU_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 55); + + if ((D_80B7A694 == 4) && ((D_80B7E124 != 0) || (D_80B7E0B6 != 2))) { + f32 rx; + f32 ry; + f32 dist; + f32 dx; + f32 dy; + f32 dz; + + dx = sLurePos.x - sRodTipPos.x; + dy = sLurePos.y - sRodTipPos.y; + dz = sLurePos.z - sRodTipPos.z; + + ry = Math_FAtan2F(dx, dz); + dist = sqrtf(SQ(dx) + SQ(dz)); + rx = -Math_FAtan2F(dy, dist); + + dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) * 0.001f; + + Matrix_Translate(sRodTipPos.x, sRodTipPos.y, sRodTipPos.z, MTXMODE_NEW); + Matrix_RotateY(ry, MTXMODE_APPLY); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_Scale(D_80B7E14C, 1.0f, dist, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3444), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFishingLineModelDL); + } else { + for (i = spB4; i < LINE_SEG_COUNT - 1; i++) { + if ((i == LINE_SEG_COUNT - 3) && (D_80B7E0B6 == 0) && (D_80B7A694 == 3)) { + f32 rx; + f32 ry; + f32 dist; + f32 dx; + f32 dy; + f32 dz; + + dx = D_80B7E0C8.x - (linePos + i)->x; + dy = D_80B7E0C8.y - (linePos + i)->y; + dz = D_80B7E0C8.z - (linePos + i)->z; + + ry = Math_FAtan2F(dx, dz); + dist = sqrtf(SQ(dx) + SQ(dz)); + rx = -Math_FAtan2F(dy, dist); + + dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) * 0.001f; + + Matrix_Translate((linePos + i)->x, (linePos + i)->y, (linePos + i)->z, MTXMODE_NEW); + Matrix_RotateY(ry, MTXMODE_APPLY); + Matrix_RotateX(rx, MTXMODE_APPLY); + Matrix_Scale(D_80B7E14C, 1.0f, dist, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3475), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFishingLineModelDL); + break; + } + + Matrix_Translate((linePos + i)->x, (linePos + i)->y, (linePos + i)->z, MTXMODE_NEW); + Matrix_RotateY((lineRot + i)->y, MTXMODE_APPLY); + Matrix_RotateX((lineRot + i)->x, MTXMODE_APPLY); + Matrix_Scale(D_80B7E14C, 1.0f, 0.005f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3492), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFishingLineModelDL); + } + } + + Matrix_Pop(); + func_80093D84(globalCtx->state.gfxCtx); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3500); +} + +static f32 sRodScales[22] = { + 1.0f, 1.0f, 1.0f, 0.9625f, 0.925f, 0.8875f, 0.85f, 0.8125f, + 0.775f, 0.73749995f, 0.7f, 0.6625f, 0.625f, 0.5875f, 0.54999995f, 0.5125f, + 0.47499996f, 0.4375f, 0.39999998f, 0.36249995f, 0.325f, 0.28749996f, +}; + +static f32 sRodBendRatios[22] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.06f, 0.12f, 0.18f, 0.24f, 0.30f, 0.36f, + 0.42f, 0.48f, 0.54f, 0.60f, 0.60f, 0.5142f, 0.4285f, 0.3428f, 0.2571f, 0.1714f, 0.0857f, +}; + +static Vec3f sRodTipOffset = { 0.0f, 0.0f, 0.0f }; + +void Fishing_DrawRod(GlobalContext* globalCtx) { + s16 i; + f32 spC8; + f32 spC4; + f32 spC0; + Input* input = &globalCtx->state.input[0]; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3600); + + if (D_80B7FDA8 != 0) { + D_80B7FDA8--; + + Math_ApproachF(&D_80B7A6C0, 35.0f, 1.0f, 100.0f); + Math_ApproachF(&D_80B7A6BC, -0.8f, 1.0f, 0.4f); + Math_ApproachS(&player->actor.shape.rot.x, -4000, 2, 15000); + } else { + s16 target = 0; + + if ((D_80B7A694 == 4) && (D_80B7E124 != 0)) { + target = Math_SinS(D_80B7E0AE * 25600) * 1500.0f; + } else { + Math_ApproachZeroF(&D_80B7A6C0, 0.1f, 10.0f); + Math_ApproachZeroF(&D_80B7A6BC, 1.0f, 0.05f); + } + + Math_ApproachS(&player->actor.shape.rot.x, target, 5, 1000); + } + + if ((D_80B7A694 == 3) || (D_80B7A694 == 4)) { + if ((input->rel.stick_x == 0) && (D_80B7A6C4 != 0)) { + D_80B7A6B0 = 0.0f; + } + if ((input->rel.stick_y == 0) && (D_80B7A6C8 != 0)) { + D_80B7A6B4 = 0.0f; + } + + spC8 = player->unk_85C; + Math_SmoothStepToF(&player->unk_85C, input->rel.stick_y * 0.02f, 0.3f, 5.0f, 0.0f); + spC8 = player->unk_85C - spC8; + + spC4 = player->unk_858; + Math_SmoothStepToF(&player->unk_858, input->rel.stick_x * 0.02f, 0.3f, 5.0f, 0.0f); + spC4 = player->unk_858 - spC4; + + if (player->unk_858 > 1.0f) { + player->unk_858 = 1.0f; + } + if (player->unk_85C > 1.0f) { + player->unk_85C = 1.0f; + } + if (player->unk_858 < -1.0f) { + player->unk_858 = -1.0f; + } + if (player->unk_85C < -1.0f) { + player->unk_85C = -1.0f; + } + + Math_ApproachF(&D_80B7A6A8, spC4 * 70.0f * -0.01f, 1.0f, D_80B7A6B0); + Math_ApproachF(&D_80B7A6B0, 1.0f, 1.0f, 0.1f); + Math_ApproachF(&D_80B7A6AC, spC8 * 70.0f * 0.01f, 1.0f, D_80B7A6B4); + Math_ApproachF(&D_80B7A6B4, 1.0f, 1.0f, 0.1f); + Math_ApproachZeroF(&D_80B7A6B8, 1.0f, 0.05f); + } else { + Math_ApproachZeroF(&player->unk_85C, 1.0f, 0.1f); + Math_ApproachZeroF(&player->unk_858, 1.0f, 0.1f); + Math_ApproachF(&D_80B7A6AC, (Math_SinS(D_80B7E0AE * 3000) * 0.025f) + -0.03f, 1.0f, 0.05f); + Math_ApproachZeroF(&D_80B7A6A8, 1.0f, 0.05f); + + if ((D_80B7E0B4 >= 19) && (D_80B7E0B4 <= 24)) { + Math_ApproachF(&D_80B7A6B8, 0.8f, 1.0f, 0.2f); + } else { + Math_ApproachF(&D_80B7A6B8, 0.0f, 1.0f, 0.4f); + } + } + + func_80093D18(globalCtx->state.gfxCtx); + + gSPDisplayList(POLY_OPA_DISP++, gFishingRodMaterialDL); + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 155, 0, 255); + + Matrix_Mult(&player->mf_9E0, MTXMODE_NEW); + + if (sLinkAge != 1) { + Matrix_Translate(0.0f, 400.0f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(0.0f, 230.0f, 0.0f, MTXMODE_APPLY); + } + + if (D_80B7A694 == 5) { + Matrix_RotateY(0.56f * M_PI, MTXMODE_APPLY); + } else { + Matrix_RotateY(0.41f * M_PI, MTXMODE_APPLY); + } + + Matrix_RotateX(-M_PI / 5.0000003f, MTXMODE_APPLY); + Matrix_RotateZ((player->unk_858 * 0.5f) + 3.0f * M_PI / 20.0f, MTXMODE_APPLY); + Matrix_RotateX((D_80B7A6C0 + 20.0f) * 0.01f * M_PI, MTXMODE_APPLY); + Matrix_Scale(0.70000005f, 0.70000005f, 0.70000005f, MTXMODE_APPLY); + + spC0 = (D_80B7A6BC * (((player->unk_85C - 1.0f) * -0.25f) + 0.5f)) + (D_80B7A6AC + D_80B7A6B8); + + Matrix_Translate(0.0f, 0.0f, -1300.0f, MTXMODE_APPLY); + + for (i = 0; i < 22; i++) { + Matrix_RotateY(sRodBendRatios[i] * D_80B7A6A8 * 0.5f, MTXMODE_APPLY); + Matrix_RotateX(sRodBendRatios[i] * spC0 * 0.5f, MTXMODE_APPLY); + + Matrix_Push(); + Matrix_Scale(sRodScales[i], sRodScales[i], 0.52f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 3809), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (i < 5) { + gDPLoadTextureBlock(POLY_OPA_DISP++, gFishingRodSegmentBlackTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 8, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, 3, G_TX_NOLOD, G_TX_NOLOD); + } else if ((i < 8) || ((i % 2) == 0)) { + gDPLoadTextureBlock(POLY_OPA_DISP++, gFishingRodSegmentWhiteTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 8, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, 3, G_TX_NOLOD, G_TX_NOLOD); + } else { + gDPLoadTextureBlock(POLY_OPA_DISP++, gFishingRodSegmentStripTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 8, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, 3, G_TX_NOLOD, G_TX_NOLOD); + } + + gSPDisplayList(POLY_OPA_DISP++, gFishingRodSegmentDL); + + Matrix_Pop(); + Matrix_Translate(0.0f, 0.0f, 500.0f, MTXMODE_APPLY); + + if (i == 21) { + Matrix_MultVec3f(&sRodTipOffset, &sRodTipPos); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 3838); +} + +static Vec3f D_80B7AF94 = { 0.0f, 0.0f, 0.0f }; + +void Fishing_UpdateLure(Fishing* this, GlobalContext* globalCtx) { + f32 spE4; + f32 spE0; + s16 phi_v0; + s16 spDC; + f32 spD8; + f32 spD4; + f32 spD0; + f32 phi_f16; + f32 spC8; + s16 i; + Player* player = GET_PLAYER(globalCtx); + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f spA8; + Vec3f sp9C; + Vec3f sp90; + Input* input = &globalCtx->state.input[0]; + Vec3f sp80; + f32 sp7C; + f32 sp78; + f32 phi_f0; + f32 sp70; + Vec3f sp64; + Vec3f sp58; + s32 pad; + + D_80B7E0AE++; + + if (D_80B7E0B0 != 0) { + D_80B7E0B0--; + } + + if (D_80B7E0B2 != 0) { + D_80B7E0B2--; + } + + if (D_80B7E0B4 != 0) { + D_80B7E0B4--; + } + + if (D_80B7E122 != 0) { + D_80B7E122--; + } + + if (D_80B7E150 != 0) { + D_80B7E150--; + } + + if (D_80B7A6A4 != 0) { + D_80B7A6A4--; + } + + if (D_80B7E0A4 != 0) { + D_80B7E0A4--; + } + + if (D_80B7E114 != 0) { + D_80B7E114--; + } + + if (D_80B7E0AC == 1) { + D_80B7E0AC = 2; + D_80B7E084 = 0; + D_80B7E082 = 0; + D_80B7E0B6 = 0; + + if (((sLinkAge == 1) && (HIGH_SCORE(HS_FISHING) & 0x400)) || + ((sLinkAge != 1) && (HIGH_SCORE(HS_FISHING) & 0x800))) { + sSinkingLureLocation = (u8)Rand_ZeroFloat(3.999f) + 1; + } + + D_80B7E148 = 520.0f; + D_80B7E144 = 195.0f; + + D_80B7A694 = D_80B7E0B6 = D_80B7E0AE = D_80B7E0B0 = D_80B7E0B2 = D_80B7E0B4 = D_80B7E120 = D_80B7E114 = + D_80B7E150 = 0; + D_80B7E104 = D_80B7E154 = D_80B7E108 = 0.0f; + + D_80B7E128 = zeroVec; + + for (i = 0; i < LINE_SEG_COUNT; i++) { + sReelLinePos[i] = zeroVec; + sReelLineRot[i] = zeroVec; + sReelLineUnk[i] = zeroVec; + } + } + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &sLurePos, &D_80B7AF94, &sProjectedW); + + if (D_80B7A694 == 0) { + Math_ApproachF(&D_80B7E108, -800.0f, 1.0f, 20.0f); + } else { + Math_ApproachF(&D_80B7E108, 300.0f, 1.0f, 20.0f); + } + + switch (D_80B7A694) { + case 0: + D_80B7FEA0 = 0; + + if (KREG(14) != 0) { + KREG(14) = 0; + D_80B7E0B6 = 2 - D_80B7E0B6; + if (D_80B7E0B6 != 0) { + D_80B7E082 = 0; + } + } + + Math_ApproachF(&D_80B7E144, 195.0f, 1.0f, 1.0f); + + if (player->stateFlags1 & 0x8000000) { + D_80B7E0B4 = 0; + player->unk_860 = 0; + } + + if (D_80B7E0B4 == 0) { + if ((D_80B7E0B0 == 0) && (player->unk_860 == 1)) { + D_80B7E0B4 = 37; + Message_CloseTextbox(globalCtx); + } + } else { + sLureRot.x = sReelLineRot[LINE_SEG_COUNT - 2].x + M_PI; + sLureRot.y = sReelLineRot[LINE_SEG_COUNT - 2].y; + + if (D_80B7E0B4 == 18) { + D_80B7A694 = 1; + sLurePos = sRodTipPos; + Matrix_RotateY((player->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + sp90.x = 0.0f; + sp90.y = 0.0f; + sp90.z = 25.0f; + Matrix_MultVec3f(&sp90, &D_80B7E0E8); + D_80B7E0E8.y = 15.0f; + D_80B7E0F8.x = D_80B7E0F8.z = 0.0f; + D_80B7E0F8.y = -1.0f; + D_80B7E148 = 0.0f; + D_80B7E0B2 = 5; + D_80B7E11C = 0.5f; + D_80B7E118 = Rand_ZeroFloat(1.9f); + sFishMouthOffset.y = 500.0f; + func_80078914(&D_80B7AF94, NA_SE_IT_SWORD_SWING_HARD); + } + } + break; + + case 1: + spE0 = sLurePos.y; + + sLurePos.x += D_80B7E0E8.x; + sLurePos.y += D_80B7E0E8.y; + sLurePos.z += D_80B7E0E8.z; + + D_80B7E0E8.x += D_80B7E0F8.x; + D_80B7E0E8.y += D_80B7E0F8.y; + D_80B7E0E8.z += D_80B7E0F8.z; + + if (CHECK_BTN_ALL(input->cur.button, BTN_A) || (D_80B7A68C != 0)) { + D_80B7E0E8.x *= 0.9f; + D_80B7E0E8.z *= 0.9f; + if (D_80B7A68C == 0) { + func_80078884(NA_SE_IT_FISHING_REEL_HIGH - SFX_FLAG); + } + } + + spD8 = sLurePos.x - sRodTipPos.x; + spD4 = sLurePos.y - sRodTipPos.y; + spD0 = sLurePos.z - sRodTipPos.z; + + if (D_80B7E0B2 != 0) { + sLureRot.x = sReelLineRot[LINE_SEG_COUNT - 2].x + M_PI; + sLureRot.y = sReelLineRot[LINE_SEG_COUNT - 2].y; + } else { + sLureRot.x = 0.0f; + sLureRot.y = Math_Atan2F(spD0, spD8) + M_PI; + } + + phi_f16 = sqrtf(SQ(spD8) + SQ(spD4) + SQ(spD0)); + if (phi_f16 > 1000.0f) { + phi_f16 = 1000.0f; + } + D_80B7E144 = 200.0f - (phi_f16 * 200.0f * 0.001f); + + spC8 = SQ(sLurePos.x) + SQ(sLurePos.z); + if (spC8 > SQ(920.0f)) { + if ((KREG(56) != 0) || (sLurePos.y > 160.0f) || (sLurePos.x < 80.0f) || (sLurePos.x > 180.0f) || + (sLurePos.z > 1350.0f) || (sLurePos.z < 1100.0f) || (sLurePos.y < 45.0f)) { + sp80 = this->actor.world.pos; + this->actor.prevPos = this->actor.world.pos = sLurePos; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 15.0f, 30.0f, 30.0f, 0x43); + this->actor.world.pos = sp80; + + if (this->actor.bgCheckFlags & 0x10) { + D_80B7E0E8.y = -0.5f; + } + if (this->actor.bgCheckFlags & 8) { + if (D_80B7E0E8.y > 0.0f) { + D_80B7E0E8.y = 0.0f; + } + D_80B7E0E8.x = D_80B7E0E8.z = 0.0f; + } + } else { + if (func_80B6C2EC(&sLurePos) != 0) { + D_80B7A694 = 3; + D_80B7E154 = 0.0f; + } + } + + spE4 = ((sqrtf(spC8) - 920.0f) * 0.11f) + WATER_SURFACE_Y(globalCtx); + if (sLurePos.y <= spE4) { + sLurePos.y = spE4; + D_80B7E0E8.x = D_80B7E0E8.y = D_80B7E0E8.z = 0.0f; + D_80B7A694 = 3; + D_80B7E154 = 0.0; + } else { + Math_ApproachF(&D_80B7E148, 0.0f, 1.0f, 0.05f); + func_80078914(&D_80B7AF94, NA_SE_EN_FANTOM_FLOAT - SFX_FLAG); + } + } else { + spE4 = WATER_SURFACE_Y(globalCtx); + + if (sLurePos.y <= spE4) { + D_80B7A694 = 2; + D_80B7E154 = 0.0f; + D_80B7E0E8.x = D_80B7E0E8.z = 0.0f; + + if (D_80B7E0B6 == 2) { + D_80B7E0A2 = 0; + } else { + D_80B7E0A2 = 10; + } + + if ((sLurePos.y <= spE4) && (spE4 < spE0) && (spE4 == WATER_SURFACE_Y(globalCtx))) { + D_80B7E114 = 10; + func_80078914(&D_80B7AF94, NA_SE_EV_BOMB_DROP_WATER); + D_80B7E0F8.y = 0.0f; + D_80B7E0E8.y *= 0.2f; + + for (i = 0; i < 50; i++) { + sp7C = Rand_ZeroFloat(1.5f) + 0.5f; + sp78 = Rand_ZeroFloat(6.28f); + + sp9C.x = sinf(sp78) * sp7C; + sp9C.z = cosf(sp78) * sp7C; + sp9C.y = Rand_ZeroFloat(3.0f) + 3.0f; + + spA8 = sLurePos; + spA8.x += (sp9C.x * 3.0f); + spA8.y = WATER_SURFACE_Y(globalCtx); + spA8.z += (sp9C.z * 3.0f); + Fishing_SpawnDustSplash(NULL, globalCtx->specialEffects, &spA8, &sp9C, + Rand_ZeroFloat(0.02f) + 0.025f); + } + + spA8 = sLurePos; + spA8.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(NULL, globalCtx->specialEffects, &spA8, 100.0f, 800.0f, 150, 90); + } + } else { + Math_ApproachZeroF(&D_80B7E148, 1.0f, 0.05f); + func_80078914(&D_80B7AF94, NA_SE_EN_FANTOM_FLOAT - SFX_FLAG); + } + } + + sReelLinePos[LINE_SEG_COUNT - 1].x = sLurePos.x; + sReelLinePos[LINE_SEG_COUNT - 1].y = sLurePos.y; + sReelLinePos[LINE_SEG_COUNT - 1].z = sLurePos.z; + + D_80B7E140 = 1.0f; + D_80B7E10C = 0.5f; + break; + + case 2: + if (sLurePos.y <= WATER_SURFACE_Y(globalCtx)) { + sLurePos.y += D_80B7E0E8.y; + + Math_ApproachZeroF(&D_80B7E0E8.y, 1.0f, 1.0f); + + if (D_80B7E0B6 != 2) { + Math_ApproachF(&sLurePos.y, WATER_SURFACE_Y(globalCtx), 0.5f, 1.0f); + } + } + + Math_ApproachF(&D_80B7E148, 2.0f, 1.0f, 0.1f); + + if (D_80B7E0A2 == 0) { + D_80B7A694 = 3; + } else { + D_80B7E0A2--; + } + break; + + case 3: + D_80B7FEA0 = 0; + + if ((D_80B7A68C != 0) && ((SQ(sLurePos.x) + SQ(sLurePos.z)) < SQ(500.0f))) { + D_80B7A690 = 1; + } + + player->unk_860 = 2; + + if (D_80B7E138 < 3.0f) { + spD0 = D_80B7E10C * Math_SinS(D_80B7E0AE * 0x1060); + Math_ApproachF(&sLureRot.x, -M_PI / 6.0f + spD0, 0.3f, D_80B7E110); + Math_ApproachF(&D_80B7E110, 0.5f, 1.0f, 0.02f); + Math_ApproachZeroF(&D_80B7E10C, 1.0f, 0.02f); + } else { + D_80B7E110 = 0.0f; + } + + spDC = 0x4000; + spE4 = WATER_SURFACE_Y(globalCtx); + + spC8 = SQ(sLurePos.x) + SQ(sLurePos.z); + if (spC8 < SQ(920.0f)) { + if (sLurePos.y <= (spE4 + 4.0f)) { + sp70 = 0.0f; + + if (D_80B7E150 == 0) { + if (fabsf(input->rel.stick_x) > 30.0f) { + sp70 = fabsf((input->rel.stick_x - D_80B7A6C4) * (1.0f / 60.0f)); + } else if (fabsf(input->rel.stick_y) > 30.0f) { + sp70 = fabsf((input->rel.stick_y - D_80B7A6C8) * (1.0f / 60.0f)); + } + } + + if (sp70 > 1.0f) { + sp70 = 1.0f; + } + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + sp70 = 0.5f; + } + + if (D_80B7A68C != 0) { + if (sp70 > 0.3f) { + sp70 = 0.3f; + } + } + + if ((sp70 > 0.2f) && (D_80B7E138 < 4.0f)) { + D_80B7E150 = 5; + + if (sp70 > 0.8f) { + D_80B7E120 = 2; + } else { + D_80B7E120 = 1; + } + + sp90.x = player->actor.world.pos.x - sLurePos.x; + sp90.z = player->actor.world.pos.z - sLurePos.z; + sp90.y = Math_Atan2F(sp90.z, sp90.x); + + D_80B7E134 = (sp70 * D_80B7E140) + sp90.y; + D_80B7E140 = D_80B7E140 * -1.0f; + D_80B7E138 = fabsf(sp70) * 6.0f; + sLureRot.x = 0.0f; + D_80B7E10C = 0.5f; + D_80B7E144 += (fabsf(sp70) * (7.5f + (KREG(25) * 0.1f))); + + func_800F436C(&D_80B7AF94, NA_SE_EV_LURE_MOVE_W, (sp70 * 1.999f * 0.25f) + 0.75f); + + if (D_80B7E0B6 == 2) { + D_80B7E128.y = 5.0f * sp70; + sReelLinePos[LINE_SEG_COUNT - 1].y += D_80B7E128.y; + sLurePos.y += D_80B7E128.y; + } + } else if (CHECK_BTN_ALL(input->cur.button, BTN_A)) { + spDC = 0x500; + D_80B7E134 = sReelLineRot[LINE_SEG_COUNT - 2].y + M_PI; + sLureRot.x = 0.0f; + D_80B7E10C = 0.5f; + if (D_80B7E0B6 == 2) { + D_80B7E128.y = 0.2f; + sReelLinePos[LINE_SEG_COUNT - 1].y += D_80B7E128.y; + sLurePos.y += D_80B7E128.y; + } + } + } else { + if (D_80B7E144 > 150.0f) { + sLureRot.x = sReelLineRot[LINE_SEG_COUNT - 2].x + M_PI; + D_80B7E134 = sReelLineRot[LINE_SEG_COUNT - 2].y + M_PI; + D_80B7E144 += 2.0f; + } + } + } else { + spE4 = ((sqrtf(spC8) - 920.0f) * 0.11f) + WATER_SURFACE_Y(globalCtx); + if (sLurePos.y <= spE4) { + sLurePos.y = spE4; + spDC = 0x500; + D_80B7E134 = sReelLineRot[LINE_SEG_COUNT - 2].y + M_PI; + sLureRot.x = 0.0f; + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + D_80B7E144 += 6.0f; + func_80078914(&D_80B7AF94, NA_SE_PL_WALK_SAND); + } + } else { + if (D_80B7E144 > 150.0f) { + sLureRot.x = sReelLineRot[LINE_SEG_COUNT - 2].x + M_PI; + D_80B7E134 = sReelLineRot[LINE_SEG_COUNT - 2].y + M_PI; + D_80B7E144 += 2.0f; + } + } + } + + Math_ApproachZeroF(&D_80B7E138, 1.0f, 0.3f); + Math_ApproachS(&D_80B7E13C, (D_80B7E134 * 32768.0f) / M_PI, 3, spDC); + + sLureRot.y = (D_80B7E13C / 32768.0f) * M_PI; + + sp90.x = 0.0f; + sp90.y = 0.0f; + sp90.z = D_80B7E138; + + Matrix_RotateY(sLureRot.y, MTXMODE_NEW); + + if (D_80B7E0B6 == 2) { + Matrix_MultVec3f(&sp90, &sp64); + D_80B7E128.x = sp64.x; + D_80B7E128.z = sp64.z; + phi_f0 = 10.0f; + } else { + Matrix_MultVec3f(&sp90, &D_80B7E128); + phi_f0 = 0.0f; + } + + D_80B7E104 = 0.0f; + + if ((D_80B7E0B6 == 1) && CHECK_BTN_ALL(input->cur.button, BTN_A)) { + D_80B7E128.y = -2.0f; + + if ((D_80B7E0AE & 1) != 0) { + D_80B7E104 = 0.5f; + } else { + D_80B7E104 = -0.5f; + } + } else if (sReelLinePos[LINE_SEG_COUNT - 1].y < (WATER_SURFACE_Y(globalCtx) + phi_f0)) { + if (D_80B7E0B6 == 2) { + sp58 = this->actor.world.pos; + this->actor.prevPos = this->actor.world.pos = sLurePos; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 15.0f, 30.0f, 30.0f, 0x44); + this->actor.world.pos = sp58; + + D_80B7E128.y += -0.5f; + if (D_80B7E128.y < -1.0f) { + D_80B7E128.y = -1.0f; + } + + if (sLurePos.y < (this->actor.floorHeight + 5.0f)) { + sReelLinePos[LINE_SEG_COUNT - 1].y = sLurePos.y = this->actor.floorHeight + 5.0f; + D_80B7E128.y = 0.0f; + } else { + D_80B7E120 = 1; + } + } else { + D_80B7E128.y = fabsf(sReelLinePos[LINE_SEG_COUNT - 1].y - WATER_SURFACE_Y(globalCtx)) * 0.2f; + if (D_80B7E128.y > 1.5f) { + D_80B7E128.y = 1.5f; + } + } + } + + sReelLinePos[LINE_SEG_COUNT - 1].x += D_80B7E128.x; + sReelLinePos[LINE_SEG_COUNT - 1].y += D_80B7E128.y; + sReelLinePos[LINE_SEG_COUNT - 1].z += D_80B7E128.z; + + if (sReelLinePos[LINE_SEG_COUNT - 1].y > (spE4 + 6.0f)) { + sReelLinePos[LINE_SEG_COUNT - 1].y -= 5.0f; + } + + D_80B7E0E8.x = D_80B7E0E8.y = D_80B7E0E8.z = D_80B7E0F8.y = 0.0f; + + if (CHECK_BTN_ALL(input->cur.button, BTN_A)) { + if (CHECK_BTN_ALL(input->cur.button, BTN_R)) { + D_80B7E144 += 1.5f; + func_80078884(NA_SE_IT_FISHING_REEL_HIGH - SFX_FLAG); + Math_ApproachF(&D_80B7E154, 1000.0f, 1.0f, 2.0f); + } else { + D_80B7E144 += D_80B7E11C; + func_80078884(NA_SE_IT_FISHING_REEL_SLOW - SFX_FLAG); + Math_ApproachF(&D_80B7E154, 1000.0f, 1.0f, 0.2f); + } + + if (sReelLinePos[LINE_SEG_COUNT - 1].y > (WATER_SURFACE_Y(globalCtx) + 4.0f)) { + Math_ApproachF(&D_80B7E148, 3.0f, 1.0f, 0.2f); + } else { + Math_ApproachF(&D_80B7E148, 1.0f, 1.0f, 0.2f); + } + } else { + Math_ApproachF(&D_80B7E148, 2.0f, 1.0f, 0.2f); + } + + Math_ApproachF(&sLurePos.x, sReelLinePos[LINE_SEG_COUNT - 1].x, 1.0f, D_80B7E154); + Math_ApproachF(&sLurePos.y, sReelLinePos[LINE_SEG_COUNT - 1].y, 1.0f, D_80B7E154); + Math_ApproachF(&sLurePos.z, sReelLinePos[LINE_SEG_COUNT - 1].z, 1.0f, D_80B7E154); + + if (D_80B7E138 > 1.0f) { + Math_ApproachF(&D_80B7E154, 1000.0f, 1.0f, 1.0f); + } + + Math_ApproachF(&D_80B7E154, 1000.0f, 1.0f, 0.1f); + + if (D_80B7E144 >= 195.0f) { + D_80B7E144 = 195.0f; + D_80B7A694 = 0; + D_80B7E148 = 520.0f; + D_80B7A6CC = 3; + } + + if ((sLurePos.y <= (WATER_SURFACE_Y(globalCtx) + 4.0f)) && + (sLurePos.y >= (WATER_SURFACE_Y(globalCtx) - 4.0f))) { + + phi_v0 = 63; + if (CHECK_BTN_ALL(input->cur.button, BTN_A) || (D_80B7E138 > 1.0f)) { + phi_v0 = 1; + } + + if ((D_80B7E0AE & phi_v0) == 0) { + spA8 = sLurePos; + spA8.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(NULL, globalCtx->specialEffects, &spA8, 30.0f, 300.0f, 150, 90); + } + } + break; + + case 4: + if (this->unk_157 != 0) { + this->unk_157--; + D_80B7E144 += D_80B7E11C; + } + + if (CHECK_BTN_ALL(input->cur.button, BTN_A)) { + if ((SQ(sLurePos.x) + SQ(sLurePos.z)) > SQ(920.0f)) { + D_80B7E144 += (1.0f + (KREG(65) * 0.1f)); + } else { + D_80B7E144 += D_80B7E11C; + } + func_80078884(NA_SE_IT_FISHING_REEL_SLOW - SFX_FLAG); + } + + if ((D_80B7E0AE & 0x1F) == 0) { + if ((D_80B7E124 != 0) || (D_80B7E0B6 != 2)) { + D_80B7A6A4 = 5; + } + } + + Math_ApproachF(&D_80B7E148, 0.0f, 1.0f, 0.2f); + break; + + case 5: + D_80B7E14C = 0.0005000001f; + sReelLinePos[LINE_SEG_COUNT - 1].x = sLurePos.x; + sReelLinePos[LINE_SEG_COUNT - 1].y = sLurePos.y; + sReelLinePos[LINE_SEG_COUNT - 1].z = sLurePos.z; + D_80B7E148 = 2.0f; + break; + } +} + +s32 func_80B70A2C(Fishing* this, GlobalContext* globalCtx, u8 ignorePosCheck) { + s16 i; + s16 count; + f32 scale; + Vec3f pos; + Vec3f vel; + f32 speedXZ; + f32 angle; + + if ((this->actor.world.pos.y < (WATER_SURFACE_Y(globalCtx) - 10.0f)) && !ignorePosCheck) { + return false; + } + + // Necessary to match + if (this->unk_1AC) {} + + if (this->unk_1AC >= 40.0f) { + count = 40; + scale = 1.2f; + } else { + count = 30; + scale = 1.0f; + } + + for (i = 0; i < count; i++) { + speedXZ = (Rand_ZeroFloat(1.5f) + 0.5f) * scale; + angle = Rand_ZeroFloat(6.28f); + + vel.x = sinf(angle) * speedXZ; + vel.z = cosf(angle) * speedXZ; + vel.y = (Rand_ZeroFloat(3.0f) + 3.0f) * scale; + + pos = this->actor.world.pos; + pos.x += vel.x * 3.0f; + pos.y = WATER_SURFACE_Y(globalCtx); + pos.z += vel.z * 3.0f; + + Fishing_SpawnDustSplash(&this->actor.projectedPos, globalCtx->specialEffects, &pos, &vel, + (Rand_ZeroFloat(0.02f) + 0.025f) * scale); + } + + pos = this->actor.world.pos; + pos.y = WATER_SURFACE_Y(globalCtx); + + Fishing_SpawnRipple(&this->actor.projectedPos, globalCtx->specialEffects, &pos, 100.0f, 800.0f, 150, 90); + + this->unk_151 = 30; + + return true; +} + +void func_80B70CF0(Fishing* this, GlobalContext* globalCtx) { + s16 count; + s16 i; + f32 scale; + Vec3f pos; + Vec3f vel; + f32 speedXZ; + f32 angle; + + // Necessary to match + if (this->unk_1AC) {} + + if (this->unk_1AC >= 45.0f) { + count = 30; + scale = 0.5f; + } else { + count = 20; + scale = 0.3f; + } + + for (i = 0; i < count; i++) { + speedXZ = (Rand_ZeroFloat(1.5f) + 0.5f) * scale; + angle = Rand_ZeroFloat(6.28f); + + vel.x = sinf(angle) * speedXZ; + vel.z = cosf(angle) * speedXZ; + vel.y = Rand_ZeroFloat(2.0f) + 2.0f; + + pos = this->actor.world.pos; + pos.x += (vel.x * 3.0f); + pos.y += (vel.y * 3.0f); + pos.z += (vel.z * 3.0f); + + Fishing_SpawnDustSplash(&this->actor.projectedPos, globalCtx->specialEffects, &pos, &vel, + (Rand_ZeroFloat(0.02f) + 0.025f) * scale); + } +} + +void func_80B70ED4(Fishing* this, Input* input) { + Vec3f sp34; + Vec3f sp28; + f32 sp24; + + sp34.x = sLurePos.x - this->actor.world.pos.x; + sp34.y = sLurePos.y - this->actor.world.pos.y; + sp34.z = sLurePos.z - this->actor.world.pos.z; + + sp24 = SQ(sp34.x) + SQ(sp34.y) + SQ(sp34.z); + + if ((D_80B7A694 == 3) && (this->unk_1A2 == 0) && (D_80B7A68C == 0)) { + Matrix_RotateY((-this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&sp34, &sp28); + + if ((sp28.z > 0.0f) || (this->unk_1AC < 40.0f)) { + if ((this->unk_158 == 7) && (sp24 < SQ(200.0f))) { + this->unk_158 = 4; + this->unk_1B4 = sLurePos; + this->unk_1B0 = 28672.0f; + this->unk_188 = 5.0f; + } else { + if ((CHECK_BTN_ALL(input->cur.button, BTN_A) || (D_80B7E138 > 1.0f)) && (sp24 < SQ(120.0f))) { + this->unk_158 = 2; + this->unk_15E = 0; + this->unk_17A[0] = 0; + this->unk_17A[2] = (s16)Rand_ZeroFloat(100.0f) + 100; + this->unk_1A8 = sFishInits[this->actor.params - 100].unk_0C; + this->unk_1B0 = 0.0f; + } + + if ((this->unk_17A[1] == 0) && (sp24 < SQ(70.0f))) { + this->unk_158 = 2; + this->unk_15E = 0; + this->unk_17A[0] = 0; + this->unk_17A[2] = (s16)Rand_ZeroFloat(100.0f) + 100; + this->unk_1A8 = sFishInits[this->actor.params - 100].unk_0C; + this->unk_1B0 = 0.0f; + } + } + } + } else if ((D_80B7A694 == 4) && (D_80B7E124 != 0) && (sp24 < SQ(100.0f)) && (this->unk_158 >= 10)) { + this->unk_15A = 0; + this->unk_158 = 1; + this->unk_1A4 = 1000; + this->unk_1A2 = 100; + this->unk_17A[1] = 50; + } + + if ((D_80B7E0B6 != 2) && (D_80B7E114 != 0) && (this->unk_1AC > 60.0f) && (sp24 < SQ(30.0f)) && + (this->unk_158 >= 10)) { + this->unk_15A = 0; + this->unk_158 = 1; + this->unk_1A4 = 1000; + this->unk_1A2 = 100; + this->unk_17A[1] = 50; + } +} + +void func_80B71278(Fishing* this, u8 arg1) { + s16 sfxId; + u8 temp; + + if (this->unk_150 == 0) { + temp = this->unk_1AC; + } else { + temp = 2.0f * this->unk_1AC; + } + + if (arg1 == 0) { + if (temp >= 50) { + sfxId = NA_SE_EV_DIVE_INTO_WATER; + } else if (temp >= 40) { + sfxId = NA_SE_EV_BOMB_DROP_WATER; + } else { + sfxId = NA_SE_EV_BOMB_DROP_WATER; + } + } else { + if (temp >= 50) { + sfxId = NA_SE_EV_JUMP_OUT_WATER; + } else if (temp >= 40) { + sfxId = NA_SE_EV_OUT_OF_WATER; + } else { + sfxId = NA_SE_EV_OUT_OF_WATER; + } + } + + Audio_PlayActorSound2(&this->actor, sfxId); +} + +void Fishing_HandleAquariumDialog(Fishing* this, GlobalContext* globalCtx) { + if (sLinkAge == 1) { + if ((HIGH_SCORE(HS_FISHING) & 0x7F) != 0) { + if (HIGH_SCORE(HS_FISHING) & 0x80) { + this->actor.textId = 0x40B1; + } else { + this->actor.textId = 0x4089; + } + } else { + this->actor.textId = 0x40AE; + } + } else { + if ((HIGH_SCORE(HS_FISHING) & 0x7F000000) != 0) { + if (HIGH_SCORE(HS_FISHING) & 0x80000000) { + this->actor.textId = 0x40B1; + } else { + this->actor.textId = 0x4089; + } + } else { + this->actor.textId = 0x40AE; + } + } + + if (this->unk_1D3 == 0) { + if (this->unk_1D4 == 0) { + this->actor.flags |= ACTOR_FLAG_0; + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + D_80B7A678 = D_80B7E078; + this->unk_1D3 = 1; + } else { + func_8002F2F4(&this->actor, globalCtx); + } + } else { + this->unk_1D4--; + this->actor.flags &= ~ACTOR_FLAG_0; + } + } else if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + this->unk_1D3 = 0; + this->unk_1D4 = 20; + } +} + +void Fishing_UpdateFish(Actor* thisx, GlobalContext* globalCtx2) { + s16 i; + s16 sp134 = 10; + f32 sp130; + f32 sp12C; + f32 sp128; + f32 sp124; + f32 multiplier; + f32 sp11C; + f32 sp118; + Vec3f sp10C; + Vec3f sp100; + s16 spFE; + s16 spFC; + s16 spFA; + s16 phi_v0; + s16 spF6; + s16 spF4; + s16 spF2; + s16 spF0; + s16 spEE; + Fishing* this = (Fishing*)thisx; + GlobalContext* globalCtx = globalCtx2; + Player* player = GET_PLAYER(globalCtx); + Input* input = &globalCtx->state.input[0]; + f32 spD8; + f32 phi_f0; + f32 phi_f2; + Vec3f spC4; + Vec3f spB8; + u8 phi_v0_2; + f32 temp_f0; + f32 temp; + s32 pad; + f32 spA4; + u16 spA2; + u8 phi_a1; + + this->actor.uncullZoneForward = 700.0f; + this->actor.uncullZoneScale = 50.0f; + + if (this->unk_150 == 0) { + sp118 = (player->actor.speedXZ * 0.15f) + 0.25f; + } else { + sp118 = (player->actor.speedXZ * 0.3f) + 0.25f; + } + + if ((D_80B7E0B0 != 0) || (sCameraId != 0) || ((player->actor.world.pos.z > 1150.0f) && (this->unk_158 != 100))) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + this->actor.flags |= ACTOR_FLAG_0; + if (D_80B7A694 != 0) { + if (D_80B7E0B2 == 0) { + this->actor.focus.pos = sLurePos; + } else if (D_80B7E0B2 == 1) { + D_80B7A6CC = 1; + D_80B7FED0 = 0.0f; + D_80B7E088 = 2; + } + } + this->actor.focus.pos = this->actor.world.pos; + } + + this->unk_15C++; + + for (i = 0; i < 4; i++) { + if (this->unk_17A[i] != 0) { + this->unk_17A[i]--; + } + } + + if (this->unk_1A4 != 0) { + this->unk_1A4--; + } + + if (this->unk_1A2 != 0) { + this->unk_1A2--; + } + + if (this->unk_1A0 != 0) { + this->unk_1A0--; + } + + if (this->unk_151 != 0) { + this->unk_151--; + } + + Math_ApproachF(&this->unk_198, this->unk_190, 1.0f, 0.2f); + + if (this->unk_158 == 6) { + Math_ApproachF(&this->unk_19C, this->unk_194, 0.2f, 200.0f); + } else { + phi_f0 = 1.0f; + phi_f2 = 1.0f; + if (this->actor.world.pos.y > WATER_SURFACE_Y(globalCtx)) { + phi_f0 = (KREG(64) * 0.1f) + 1.5f; + phi_f2 = 3.0f; + } + Math_ApproachF(&this->unk_19C, this->unk_194 * phi_f0, 1.0f, 500.0f * phi_f2); + } + + Math_ApproachS(&this->unk_170, 0, 5, 0x1F4); + + if (this->unk_150 == 0) { + Actor_SetScale(&this->actor, this->unk_1AC * 15.0f * 0.00001f); + + this->unk_18C += this->unk_198; + + temp = cosf(this->unk_18C); + this->unk_16C = this->unk_16E + (s16)(temp * this->unk_19C); + + temp = cosf(this->unk_18C + -1.2f); + this->unk_176 = this->unk_16E + (s16)(temp * this->unk_19C * 1.6f); + } else { + Actor_SetScale(&this->actor, this->unk_1AC * 65.0f * 0.000001f); + + this->actor.scale.x = this->actor.scale.z * 1.1f; + this->actor.scale.y = this->actor.scale.z * 1.1f; + + this->unk_18C += this->unk_198 * 0.8f; + + for (i = 0; i < 3; i++) { + temp = cosf(this->unk_18C + (i * 2.1f)); + this->unk_1CC[i] = this->unk_16E + (s16)(temp * this->unk_19C * 2.0f); + } + + temp = cosf(this->unk_18C + 0.4f); + this->unk_16C = (this->unk_19C * temp * 2.0f) * 0.6f; + } + + sp130 = this->unk_1B4.x - this->actor.world.pos.x; + sp12C = this->unk_1B4.y - this->actor.world.pos.y; + sp128 = this->unk_1B4.z - this->actor.world.pos.z; + + spFC = Math_Atan2S(sp128, sp130); + sp124 = sqrtf(SQ(sp130) + SQ(sp128)); + + spFE = Math_Atan2S(sp124, sp12C); + sp124 = sqrtf(SQ(sp130) + SQ(sp128) + SQ(sp12C)); + + if ((this->unk_1A0 != 0) && (this->unk_158 != 2) && (this->unk_158 != 3) && (this->unk_158 != 4)) { + if ((this->unk_15C & 0x40) != 0) { + spFC += 0x4000; + } else { + spFC -= 0x4000; + } + if (((this->unk_15C + 0x20) & 0x40) != 0) { + spFE += 0x2000; + } else { + spFE -= 0x2000; + } + } + + switch (this->unk_158) { + case 100: + Fishing_HandleAquariumDialog(this, globalCtx); + + this->actor.uncullZoneForward = 500.0f; + this->actor.uncullZoneScale = 300.0f; + + Lights_PointNoGlowSetInfo(&this->lightInfo, (s16)this->actor.world.pos.x, + (s16)this->actor.world.pos.y + 20.0f, (s16)this->actor.world.pos.z - 50.0f, 255, + 255, 255, 255); + + this->unk_1AC = D_80B7E078; + sp100.y = (f32)Math_SinS(globalCtx->gameplayFrames * 300) * 1; + sp100.z = (f32)Math_SinS(globalCtx->gameplayFrames * 230) * 2; + this->actor.world.pos.x = 130.0f; + this->actor.world.pos.y = 55.0f + sp100.y; + this->actor.world.pos.z = 1300.0f + sp100.z; + this->actor.shape.rot.y = -0x8000; + + if ((this->actor.projectedPos.z < 200.0f) && (this->actor.projectedPos.z > 0.0f)) { + spC4.x = Rand_CenteredFloat(5.0f) + 130.0f; + spC4.y = 40.0f; + spC4.z = Rand_CenteredFloat(5.0f) + 1280.0f; + Fishing_SpawnBubble(NULL, globalCtx->specialEffects, &spC4, Rand_ZeroFloat(0.02f) + 0.03f, 1); + } + + Math_ApproachS(&this->unk_172, (Math_SinS(this->unk_15C * 0x800) * 2500.0f) + 2500.0f, 2, 0x7D0); + Math_ApproachS(&this->unk_174, Math_SinS(this->unk_15C * 0xA00) * 1500.0f, 2, 0x7D0); + + this->unk_190 = 0.3f; + this->unk_194 = 1000.0f / 3.0f; + return; + + case 10: + this->unk_1B4 = this->actor.home.pos; + + Math_ApproachF(&this->actor.speedXZ, 2.0f, 1.0f, 0.5f); + Math_ApproachF(&this->unk_1B0, 4096.0f, 1.0f, 256.0f); + + if (sp124 < 40.0f) { + this->unk_158 = 11; + this->unk_190 = 0.4f; + this->unk_194 = 500.0f; + } + + func_80B70ED4(this, input); + + if (this->actor.xzDistToPlayer < (250.0f * sp118)) { + this->unk_15A = this->unk_158 = 0; + this->unk_1A4 = 1000; + this->unk_1A2 = 200; + this->unk_17A[1] = 50; + } + break; + + case 11: + this->unk_1B4 = this->actor.home.pos; + + Math_ApproachF(&this->actor.speedXZ, 0.0f, 1.0f, 0.05f); + Math_ApproachF(&this->unk_1B0, 0.0f, 1.0f, 256.0f); + + if (sp124 >= 40.0f) { + this->unk_158 = 10; + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + } + func_80B70ED4(this, input); + + if (this->actor.xzDistToPlayer < (250.0f * sp118)) { + this->unk_15A = this->unk_158 = 0; + this->unk_1A4 = 1000; + this->unk_1A2 = 200; + this->unk_17A[1] = 50; + } + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + if ((gSaveContext.dayTime >= 0xC000) && (gSaveContext.dayTime <= 0xC01B)) { + this->unk_158 = 7; + this->unk_17A[3] = (s16)Rand_ZeroFloat(150.0f) + 200; + } + if ((gSaveContext.dayTime >= 0x3AAA) && (gSaveContext.dayTime <= 0x3AC5)) { + this->unk_158 = 7; + this->unk_17A[3] = (s16)Rand_ZeroFloat(150.0f) + 200; + } + } + + if (KREG(15) != 0) { + KREG(15) = 0; + this->unk_158 = 7; + this->unk_17A[3] = (s16)Rand_ZeroFloat(150.0f) + 2000; + } + break; + + case 0: + Math_ApproachF(&this->actor.speedXZ, 1.0f, 1.0f, 0.05f); + Math_ApproachF(&this->unk_1B0, 0.0f, 1.0f, 256.0f); + + if (this->unk_17A[0] == 0) { + if (this->unk_1A4 == 0) { + this->unk_158 = this->unk_15A = 10; + } else { + this->unk_158 = 1; + this->unk_17A[0] = (s16)Rand_ZeroFloat(30.0f) + 10; + this->unk_1B4.x = Rand_CenteredFloat(300.0f); + this->unk_1B4.y = (WATER_SURFACE_Y(globalCtx) - 50.0f) - Rand_ZeroFloat(50.0f); + this->unk_1B4.z = Rand_CenteredFloat(300.0f); + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + } + } + + if (D_80B7E0B6 == 2) { + func_80B70ED4(this, input); + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + } + break; + + case 1: + if (this->unk_150 == 1) { + this->unk_158 = -1; + this->unk_1A4 = 20000; + this->unk_1A2 = 20000; + this->unk_1B4.x = 0.0f; + this->unk_1B4.y = -140.0f; + this->unk_1B4.z = 0.0f; + } else { + Math_ApproachF(&this->unk_1B0, 4096.0f, 1.0f, 256.0f); + + if ((this->actor.xzDistToPlayer < (250.0f * sp118)) || (this->unk_17A[1] != 0)) { + Math_ApproachF(&this->unk_1B0, 8192.0f, 1.0f, 768.0f); + Math_ApproachF(&this->actor.speedXZ, 4.2f, 1.0f, 0.75); + this->unk_190 = 1.2f; + this->unk_194 = 4000.0f; + this->unk_17A[0] = 20; + } else { + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + Math_ApproachF(&this->actor.speedXZ, 1.5f, 1.0f, 0.1f); + } + + if ((this->unk_17A[0] == 0) || (sp124 < 50.0f)) { + this->unk_158 = 0; + this->unk_17A[0] = (s16)Rand_ZeroFloat(30.0f) + 3; + this->unk_190 = 1.0f; + this->unk_194 = 500.0f; + } + + if (D_80B7E0B6 == 2) { + func_80B70ED4(this, input); + } else { + this->actor.flags &= ~ACTOR_FLAG_0; + } + } + break; + + case -1: + Math_ApproachS(&this->unk_166, 0, 0x14, 0x20); + + if ((this->actor.xzDistToPlayer < (250.0f * sp118)) || (this->unk_17A[1] != 0)) { + Math_ApproachF(&this->actor.speedXZ, 3.0f, 1.0f, 0.75); + this->unk_190 = 1.0f; + this->unk_17A[0] = 20; + this->unk_194 = 4000.0f; + Math_ApproachF(&this->unk_1B0, 4096.0f, 1.0f, 256.0f); + + if ((globalCtx->gameplayFrames % 32) == 0) { + this->unk_1B4.x = Rand_CenteredFloat(600.0f); + this->unk_1B4.z = Rand_CenteredFloat(600.0f); + this->unk_1B4.y = -120.0f; + } + } else if (sp124 > 50.0f) { + this->unk_190 = 0.8f; + this->unk_194 = 1500.0f; + Math_ApproachF(&this->actor.speedXZ, 1.0f, 1.0f, 0.1f); + Math_ApproachF(&this->unk_1B0, 2048.0f, 1.0f, 128.0f); + } else { + this->unk_190 = 0.4f; + this->unk_194 = 500.0f; + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 0.02f); + Math_ApproachF(&this->unk_1B0, 0.0f, 1.0f, 256.0f); + } + + if (this->unk_1A4 == 0) { + this->unk_158 = 10; + this->unk_15A = 10; + } else if ((KREG(2) != 0) || (((this->unk_1A4 & 0x7FF) == 0) && (this->unk_1A4 < 15000))) { + KREG(2) = 0; + this->unk_158 = -2; + this->actor.world.rot.x = this->actor.shape.rot.x = 0; + this->unk_1B4.y = WATER_SURFACE_Y(globalCtx) + 10.0f; + this->unk_1B4.x = Rand_ZeroFloat(50.0f); + this->unk_1B4.z = Rand_ZeroFloat(50.0f); + } + + this->actor.flags &= ~ACTOR_FLAG_0; + break; + + case -2: + if ((this->actor.xzDistToPlayer < (250.0f * sp118)) || (this->unk_17A[1] != 0)) { + this->unk_158 = -1; + this->unk_1B4.y = -120.0f; + } else { + this->unk_190 = 0.6f; + this->unk_194 = 1000.0f; + + Math_ApproachS(&this->unk_166, -0x1000, 0x14, 0x100); + + if (this->actor.world.pos.y < (WATER_SURFACE_Y(globalCtx) - 20.0f)) { + Math_ApproachF(&this->actor.speedXZ, 0.5f, 1.0f, 0.1f); + } else { + Math_ApproachZeroF(&this->actor.speedXZ, 1.0f, 0.01f); + + if ((this->actor.speedXZ == 0.0f) || + (this->actor.world.pos.y > (WATER_SURFACE_Y(globalCtx) - 5.0f))) { + this->unk_1B4.x = Rand_ZeroFloat(300.0f); + this->unk_1B4.z = Rand_ZeroFloat(300.0f); + this->unk_1B4.y = this->actor.floorHeight + 10.0f; + this->unk_158 = -25; + this->unk_1B0 = 0.0f; + + spB8 = this->fishMouthPos; + spB8.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(&this->actor.projectedPos, globalCtx->specialEffects, &spB8, 10.0f, 300.0f, + 150, 90); + Fishing_SpawnRipple(&this->actor.projectedPos, globalCtx->specialEffects, &spB8, 30.0f, 400.0f, + 150, 90); + + Audio_PlayActorSound2(&this->actor, NA_SE_PL_CATCH_BOOMERANG); + break; + } + } + + Math_ApproachF(&this->unk_1B0, 2048.0f, 1.0f, 128.0f); + this->actor.flags &= ~ACTOR_FLAG_0; + } + break; + + case -25: + if ((this->actor.xzDistToPlayer < (250.0f * sp118)) || (this->unk_17A[1] != 0)) { + this->unk_158 = -1; + this->unk_1B4.y = -120.0f; + } else { + Math_ApproachS(&this->unk_166, 0x1000, 0x14, 0x6A); + + if (sp124 > 40.0f) { + this->unk_190 = 0.7f; + this->unk_194 = 1200.0f; + Math_ApproachF(&this->actor.speedXZ, 0.5f, 1.0f, 0.01f); + Math_ApproachF(&this->unk_1B0, 2048.0f, 1.0f, 128.0f); + } else { + this->unk_158 = -1; + } + } + break; + + case 2: + if (((this->actor.params + D_80B7E118) & 1) != 0) { + sp10C.x = 10.0f; + } else { + sp10C.x = -10.0f; + } + sp10C.y = 0.0f; + sp10C.z = 0.0f; + Matrix_RotateY(sLureRot.y, MTXMODE_NEW); + Matrix_MultVec3f(&sp10C, &sp100); + + this->unk_1B4.x = sLurePos.x + sp100.x; + this->unk_1B4.z = sLurePos.z + sp100.z; + + if (D_80B7E0B6 == 2) { + this->unk_1B4.y = sLurePos.y; + } else if (this->unk_150 == 0) { + this->unk_1B4.y = sLurePos.y - 15.0f; + } else { + this->unk_1B4.y = sLurePos.y - 5.0f; + } + + if (this->unk_1B4.y <= this->actor.floorHeight) { + this->unk_1B4.y = this->actor.floorHeight + 3.0f; + } + + if ((D_80B7E0B6 != 2) && (this->unk_1B4.y < this->actor.world.pos.y)) { + Math_ApproachF(&this->actor.world.pos.y, this->unk_1B4.y, 0.1f, + (this->actor.world.pos.y - this->unk_1B4.y) * 0.1f); + } + + Math_ApproachF(&this->unk_1B0, 8192.0f, 1.0f, (KREG(16) * 128) + 384.0f); + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + this->unk_1A8 += 0.005f; + } + + if (D_80B7E120 != 0) { + if (D_80B7E120 == 1) { + this->unk_1A8 += 0.01f; + } else { + this->unk_1A8 += 0.05f; + } + D_80B7E120 = 0; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->unk_1A8 += 0.008f; + } + + if (sp124 < ((this->unk_1AC * 0.5f) + 20.0f)) { + if (this->unk_15E == 0) { + this->unk_190 = 1.0f; + this->unk_194 = 500.0f; + this->unk_17A[0] = (s16)Rand_ZeroFloat(10.0f) + 2; + } + Math_ApproachF(&this->actor.speedXZ, -0.2f, 1.0f, 0.1f); + this->unk_15E = 1; + } else { + if (this->unk_15E != 0) { + this->unk_190 = 1.0f; + this->unk_1B0 = 0.0f; + this->unk_194 = 3000.0f; + } + Math_ApproachF(&this->actor.speedXZ, 3.0f, 1.0f, 0.15f); + this->unk_15E = 0; + } + + if (this->unk_1AC >= 60.0f) { + multiplier = 0.3f; + } else if (this->unk_1AC >= 45.0f) { + multiplier = 0.6f; + } else { + multiplier = 1.0f; + } + + if ((gSaveContext.dayTime >= 0xB555) && (gSaveContext.dayTime <= 0xCAAA)) { + multiplier *= 1.75f; + } else if ((gSaveContext.dayTime >= 0x3555) && (gSaveContext.dayTime <= 0x4AAA)) { + multiplier *= 1.5f; + } else if (D_80B7E076 != 0) { + multiplier *= 1.5f; + } else if ((u8)D_80B7A650 != 0) { + multiplier *= 3.0f; + } + + sp11C = 0.03f * multiplier; + if (D_80B7E0B6 == 2) { + sp11C *= 5.0f; + } + + if (((this->unk_17A[0] == 1) || (Rand_ZeroOne() < sp11C)) && + ((Rand_ZeroOne() < (this->unk_1A8 * multiplier)) || ((this->unk_150 + 1) == KREG(69)))) { + if (this->unk_150 == 0) { + this->unk_158 = 3; + this->unk_190 = 1.2f; + this->unk_194 = 5000.0f; + this->unk_17A[0] = Rand_ZeroFloat(10.0f); + } else { + this->unk_158 = -3; + this->unk_190 = 1.0f; + this->unk_194 = 3000.0f; + this->unk_17A[0] = 40; + } + if (D_80B7E0B6 == 2) { + this->unk_188 = Rand_ZeroFloat(1.5f) + 3.0f; + } else { + this->unk_188 = Rand_ZeroFloat(1.5f) + 4.5f; + } + } + + if ((D_80B7A694 != 3) || (this->unk_17A[2] == 0) || + (sqrtf(SQ(this->actor.world.pos.x) + SQ(this->actor.world.pos.z)) > 800.0f)) { + this->unk_158 = this->unk_15A; + this->unk_17A[1] = (s16)Rand_ZeroFloat(30.0f) + 50; + this->unk_17A[0] = (s16)Rand_ZeroFloat(10.0f) + 5; + this->unk_190 = 1.0f; + this->unk_1B0 = 0.0f; + this->unk_194 = 2000.0f; + } + + if (this->actor.xzDistToPlayer < (100.0f * sp118)) { + this->unk_15A = this->unk_158 = 0; + this->unk_1A4 = 1000; + this->unk_1A2 = 200; + this->unk_17A[1] = 50; + } + break; + + case 3: + this->unk_151 = 6; + sp134 = 2; + + if ((((s16)player->actor.world.pos.x + D_80B7E118) & 1) != 0) { + sp10C.x = 30.0f; + } else { + sp10C.x = -30.0f; + } + sp10C.y = 0.0f; + sp10C.z = 30.0f; + + Matrix_RotateY(sLureRot.y, MTXMODE_NEW); + Matrix_MultVec3f(&sp10C, &sp100); + + this->unk_1B4.x = sLurePos.x + sp100.x; + this->unk_1B4.z = sLurePos.z + sp100.z; + this->unk_1B4.y = sLurePos.y - 10.0f; + this->unk_1B0 = 4096.0f; + Math_ApproachF(&this->actor.speedXZ, this->unk_188 * 0.8f, 1.0f, 1.0f); + + if ((D_80B7A694 != 3) || (sLurePos.y > (WATER_SURFACE_Y(globalCtx) + 5.0f)) || + (sqrtf(SQ(sLurePos.x) + SQ(sLurePos.z)) > 800.0f)) { + this->unk_158 = this->unk_15A; + this->unk_17A[0] = 0; + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + } else if ((this->unk_17A[0] == 0) || (sp124 < 30.0f)) { + this->unk_158 = 4; + this->unk_1B4 = sLurePos; + this->unk_1B0 = 16384.0f; + this->unk_190 = 1.2f; + this->unk_194 = 5000.0f; + this->unk_17A[0] = 20; + } + break; + + case 4: + Math_ApproachF(&this->unk_1B0, 16384.0f, 1.0f, 4096.0f); + Math_ApproachS(&this->unk_170, 0x4E20, 4, 0x1388); + + this->unk_151 = 50; + sp134 = 2; + this->unk_1B4 = sLurePos; + Math_ApproachF(&this->actor.speedXZ, this->unk_188, 1.0f, 1.0f); + + if ((D_80B7A694 != 3) || (this->unk_17A[0] == 0) || (sLurePos.y > (WATER_SURFACE_Y(globalCtx) + 5.0f)) || + (sqrtf(SQ(sLurePos.x) + SQ(sLurePos.z)) > 800.0f)) { + + this->unk_17A[0] = 0; + this->unk_158 = this->unk_15A; + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + } else if (sp124 < 10.0f) { + if (func_80B70A2C(this, globalCtx, false)) { + func_80B71278(this, 0); + } + + this->unk_158 = 5; + this->unk_190 = 1.2f; + this->unk_194 = 5000.0f; + this->unk_17A[1] = 150; + this->unk_17A[0] = 0; + this->unk_17A[2] = 0; + this->unk_17A[3] = 120; + + D_80B7A694 = 4; + sFishingHookedFish = this; + sFishMouthOffset.y = 500.0f - Rand_ZeroFloat(400.0f); + + if (D_80B7E0B6 == 2) { + if (this->unk_1AC > 70.0f) { + phi_v0 = (s16)Rand_ZeroFloat(20.0f) + 10; + } else if (this->unk_1AC > 60.0f) { + phi_v0 = (s16)Rand_ZeroFloat(30.0f) + 20; + } else if (this->unk_1AC > 50.0f) { + phi_v0 = (s16)Rand_ZeroFloat(30.0f) + 30; + } else { + phi_v0 = (s16)Rand_ZeroFloat(40.0f) + 40; + } + D_80B7E122 = phi_v0; + D_80B7E0A4 = phi_v0; + func_800A9F6C(0.0f, 60, phi_v0 * 3, 10); + } else { + if (this->unk_1AC > 70.0f) { + phi_v0 = (s16)Rand_ZeroFloat(5.0f) + 10; + } else if (this->unk_1AC > 60.0f) { + phi_v0 = (s16)Rand_ZeroFloat(5.0f) + 15; + } else if (this->unk_1AC > 50.0f) { + phi_v0 = (s16)Rand_ZeroFloat(5.0f) + 17; + } else { + phi_v0 = (s16)Rand_ZeroFloat(5.0f) + 25; + } + D_80B7E122 = phi_v0; + D_80B7E0A4 = phi_v0; + func_800A9F6C(0.0f, 180, phi_v0 * 3, 10); + } + + D_80B7E124 = 0; + D_80B7E116 = 100; + D_80B7E080 = 0; + } + break; + + case -3: + this->unk_151 = 50; + this->unk_1B4 = sLurePos; + Math_ApproachF(&this->actor.speedXZ, 2.0f, 1.0f, 1.0f); + + if ((D_80B7A694 != 3) || (this->unk_17A[0] == 0) || (sLurePos.y > (WATER_SURFACE_Y(globalCtx) + 5.0f)) || + (sqrtf(SQ(sLurePos.x) + SQ(sLurePos.z)) > 800.0f)) { + + this->unk_17A[0] = 0; + this->unk_190 = 1.0f; + this->unk_158 = this->unk_15A; + this->unk_194 = 2000.0f; + } else if (sp124 < 10.0f) { + if (sLurePos.y > (WATER_SURFACE_Y(globalCtx) - 10.0f)) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_JUMP_OUT_WATER); + func_80078884(NA_SE_PL_CATCH_BOOMERANG); + } + + func_80B70A2C(this, globalCtx, false); + this->unk_158 = 5; + this->unk_190 = 1.2f; + this->unk_194 = 5000.0f; + this->unk_17A[1] = 150; + this->unk_17A[0] = 0; + this->unk_17A[2] = 0; + this->unk_17A[3] = 120; + + D_80B7A694 = 4; + sFishingHookedFish = this; + + if (D_80B7E0B6 == 2) { + D_80B7E122 = 30; + D_80B7E0A4 = 100; + func_800A9F6C(0.0f, 60, 90, 10); + } else { + D_80B7E122 = 30; + D_80B7E0A4 = 40; + func_800A9F6C(0.0f, 180, 90, 10); + } + + D_80B7E124 = 0; + D_80B7E116 = 100; + D_80B7E080 = 0; + } + break; + + case 5: + this->actor.uncullZoneForward = 1200.0f; + this->actor.uncullZoneScale = 200.0f; + + D_80B7E080++; + osSyncPrintf("HIT FISH %dcm\n", (u8)this->unk_1AC); + + Math_ApproachS(&this->unk_170, 0x2AF8, 4, 0xBB8); + sFishingHookedFish = this; + Math_ApproachS(&player->actor.shape.rot.y, this->actor.yawTowardsPlayer + 0x8000, 5, 0x500); + + if (D_80B7E124 == 0) { + if ((D_80B7FEA0 < 20) && ((D_80B7E0AE & 3) == 0)) { + D_80B7FEA0++; + if (1) {} + } + } + + if ((D_80B7E122 != 0) && (D_80B7E124 == 0)) { + if (((input->rel.stick_y < -50) && (D_80B7A6C8 > -40)) || CHECK_BTN_ALL(input->press.button, BTN_A)) { + if (input->rel.stick_y < -50) { + temp_f0 = 40.0f - ((this->unk_1AC - 30.0f) * 1.333333f); + if (temp_f0 > 0.0f) { + this->unk_152 = temp_f0; + this->unk_154 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + this->unk_156 = 1; + } + } + + this->unk_198 = 1.7f; + this->unk_19C = 7000.0f; + D_80B7E124 = 1; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_ENEMY | 0x800); + D_80B7E0A6 = 0; + + if (this->unk_150 == 1) { + spA4 = (this->unk_1AC * 3.0f) + 120.0f; + } else { + spA4 = (2.0f * this->unk_1AC) + 120.0f; + } + if (spA4 > 255.0f) { + spA4 = 255.0f; + } + + func_800A9F6C(0.0f, spA4, 120, 5); + D_80B7E0A4 = 40; + D_80B7FDA8 = 10; + func_80078884(NA_SE_IT_FISHING_HIT); + } + } + + if (this->actor.world.pos.y < WATER_SURFACE_Y(globalCtx)) { + if (this->unk_17A[1] > 30) { + phi_v0_2 = 7; + } else { + phi_v0_2 = 0xF; + } + + if (((this->unk_15C & phi_v0_2) == 0) && (Rand_ZeroOne() < 0.75f) && (D_80B7E0A4 == 0)) { + if (this->unk_1AC >= 70.0f) { + spA4 = 255.0f; + } else if (this->unk_1AC >= 60.0f) { + spA4 = 230.0f; + } else if (this->unk_1AC >= 50.0f) { + spA4 = 200.0f; + } else if (this->unk_1AC >= 40.0f) { + spA4 = 170.0f; + } else { + spA4 = 140.0f; + } + + if (phi_v0_2 == 0xF) { + spA4 *= 3.0f / 4.0f; + } + + func_800A9F6C(0.0f, spA4, (s16)Rand_ZeroFloat(5.0f) + 10, 5); + } + + if (this->unk_17A[1] > 30) { + if (this->unk_17A[0] == 0) { + sp10C.x = 0.0f; + sp10C.y = 0.0f; + sp10C.z = 200.0f; + + for (spA2 = 0; spA2 < 100; spA2++) { + Matrix_RotateY(Rand_CenteredFloat(3.0f * M_PI / 4.0f) + + (((this->actor.yawTowardsPlayer + 0x8000) / 32768.0f) * M_PI), + MTXMODE_NEW); + Matrix_MultVec3f(&sp10C, &sp100); + + this->unk_1B4.x = this->actor.world.pos.x + sp100.x; + this->unk_1B4.z = this->actor.world.pos.z + sp100.z; + + if ((SQ(this->unk_1B4.x) + SQ(this->unk_1B4.z)) < SQ(750.0f)) { + break; + } + } + + if ((Rand_ZeroOne() < 0.1f) && (this->unk_17A[3] == 0)) { + if (this->unk_1AC >= 60.0f) { + phi_a1 = 255; + } else if (this->unk_1AC >= 50.0f) { + phi_a1 = 200; + } else { + phi_a1 = 180; + } + func_800A9F6C(0.0f, phi_a1, 90, 2); + this->unk_17A[0] = 20; + this->unk_17A[1] = 100; + this->unk_17A[2] = 20; + this->unk_17A[3] = 100; + this->unk_1B4.y = 300.0f; + D_80B7E0A4 = 0x28; + D_80B7E116 = (s16)Rand_ZeroFloat(30.0f) + 20; + } else { + this->unk_17A[0] = (s16)Rand_ZeroFloat(10.0f) + 3; + this->unk_17A[2] = 0; + this->unk_1B4.y = -70.0f - Rand_ZeroFloat(150.0f); + } + } + + if (this->unk_17A[2] != 0) { + D_80B7E11C = 0.0f; + this->unk_190 = 1.6f; + this->unk_194 = 6000.0f; + Math_ApproachF(&this->actor.speedXZ, 7.5f, 1.0f, 1.0f); + Math_ApproachS(&this->unk_170, 0x4E20, 2, 0xFA0); + } else { + if ((D_80B7E124 == 0) && (D_80B7E0B6 == 2)) { + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + Math_ApproachF(&this->actor.speedXZ, 3.0f, 1.0f, 0.2f); + } else { + this->unk_190 = 1.4f; + this->unk_194 = 5000.0f; + Math_ApproachF(&this->actor.speedXZ, 5.0f, 1.0f, 0.5f); + } + + if (this->unk_150 == 0) { + D_80B7E11C = 1.0f - (this->unk_1AC * 0.00899f); + } else { + D_80B7E11C = 1.0f - (this->unk_1AC * 0.00899f * 1.4f); + } + } + } else { + if (((this->unk_17A[1] & 0xF) == 0) && CHECK_BTN_ALL(input->cur.button, BTN_A) && + (!(this->unk_1AC >= 60.0f) || (D_80B7E080 >= 2000))) { + this->unk_152 = (s16)Rand_ZeroFloat(30.0f) + 15; + this->unk_154 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; + } + + this->unk_190 = 1.0f; + this->unk_194 = 4500.0f; + + if (this->unk_150 == 0) { + D_80B7E11C = 1.3f - (this->unk_1AC * 0.00899f); + } else { + D_80B7E11C = 1.3f - (this->unk_1AC * 0.00899f * 1.4f); + } + + Math_ApproachF(&this->actor.speedXZ, 2.0f, 1.0f, 0.5f); + + if (this->unk_17A[1] == 0) { + this->unk_152 = 0; + + if (D_80B7E080 < 2000) { + this->unk_17A[1] = (s16)Rand_ZeroFloat(50.0f) + 50; + } else if (D_80B7E080 < 3000) { + this->unk_17A[1] = (s16)Rand_ZeroFloat(20.0f) + 30; + } else { + this->unk_17A[1] = (s16)Rand_ZeroFloat(10.0f) + 25; + } + } + } + } + + if (D_80B7E074 != 0) { + D_80B7E11C = 0.0f; + } + + if (D_80B7E124 || (D_80B7E0B6 != 2)) { + if (this->actor.speedXZ < 3.0f) { + if ((D_80B7E0AE & 8) != 0) { + sp100.x = -0.8f; + } else { + sp100.x = -0.75f; + } + } else { + if ((D_80B7E0AE & 4) != 0) { + sp100.x = -0.9f; + } else { + sp100.x = -0.85f; + } + } + + Math_ApproachF(&D_80B7A6C0, 35.0f, 0.1f, 3.5f); + Math_ApproachF(&D_80B7A6BC, sp100.x, 0.3f, 0.1f); + } + + sReelLinePos[LINE_SEG_COUNT - 1] = this->fishMouthPos; + sp10C.x = sReelLinePos[LINE_SEG_COUNT - 1].x - sReelLinePos[LINE_SEG_COUNT - 2].x; + sp10C.y = sReelLinePos[LINE_SEG_COUNT - 1].y - sReelLinePos[LINE_SEG_COUNT - 2].y; + sp10C.z = sReelLinePos[LINE_SEG_COUNT - 1].z - sReelLinePos[LINE_SEG_COUNT - 2].z; + + if ((SQ(sp10C.x) + SQ(sp10C.y) + SQ(sp10C.z)) > SQ(20.0f)) { + Math_ApproachF(&this->actor.world.pos.x, sReelLinePos[LINE_SEG_COUNT - 2].x, 0.2f, + 2.0f * (this->actor.speedXZ * 1.5f)); + Math_ApproachF(&this->actor.world.pos.y, sReelLinePos[LINE_SEG_COUNT - 2].y, 0.2f, + 2.0f * (this->actor.speedXZ * 1.5f) * 5.0f * 0.1f); + Math_ApproachF(&this->actor.world.pos.z, sReelLinePos[LINE_SEG_COUNT - 2].z, 0.2f, + 2.0f * (this->actor.speedXZ * 1.5f)); + } + + if (CHECK_BTN_ALL(input->cur.button, BTN_A) || (input->rel.stick_y < -30)) { + if (D_80B7E116 < 100) { + D_80B7E116++; + } + } else { + if (D_80B7E116 != 0) { + D_80B7E116--; + } + } + + if ((D_80B7A694 < 3) || ((D_80B7E074 != 0) && (D_80B7E080 > 50)) || (D_80B7E080 >= 6000) || + ((D_80B7E122 == 0) && (D_80B7E124 == 0)) || (D_80B7E116 == 0) || + (((D_80B7E0AE & 0x7F) == 0) && (Rand_ZeroOne() < 0.05f) && (D_80B7E0B6 != 2) && (KREG(69) == 0))) { + D_80B7A67C = 20; + + if ((D_80B7E122 == 0) && (D_80B7E124 == 0)) { + D_80B7E086 = 0x4081; + if (((sLinkAge == 1) && (HIGH_SCORE(HS_FISHING) & 0x400)) || + ((sLinkAge != 1) && (HIGH_SCORE(HS_FISHING) & 0x800))) { + D_80B7A67C = 0; + } + } else { + D_80B7E086 = 0x4082; + func_800A9F6C(0.0f, 1, 3, 1); + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x0A00FF); + } + + this->unk_158 = this->unk_15A = 0; + this->unk_1A4 = 10000; + this->unk_1A2 = 500; + this->unk_17A[1] = 50; + this->unk_17A[0] = 0; + this->unk_190 = 1.0f; + this->unk_194 = 3000.0f; + + if (D_80B7A694 == 4) { + D_80B7A694 = 3; + } + + D_80B7E0A6 = 50; + D_80B7E11C = 0.5f; + this->unk_152 = 0; + } else if (this->actor.xzDistToPlayer < (KREG(59) + 50.0f)) { + this->unk_158 = 6; + this->unk_17A[0] = 100; + player->unk_860 = 3; + func_800A9F6C(0.0f, 1, 3, 1); + D_80B7E084++; + func_80064520(globalCtx, &globalCtx->csCtx); + D_80B7A6CC = 100; + D_80B7FEC8 = 45.0f; + D_80B7A694 = 5; + this->unk_190 = 1.0f; + this->unk_194 = 500.0f; + this->unk_19C = 5000.0f; + + if (this->actor.world.pos.y <= WATER_SURFACE_Y(globalCtx)) { + func_80B71278(this, 1); + func_80B70A2C(this, globalCtx, true); + } + goto case_6; + } + break; + + case_6: + case 6: + Math_ApproachS(&this->unk_170, 0x2AF8, 2, 0xFA0); + Math_ApproachF(&D_80B7FEC8, 15.0f, 0.05f, 0.75f); + + sp10C.x = D_80B7FEC8; + if (sLinkAge != 1) { + sp10C.y = 30.0f; + sp10C.z = 55.0f; + } else { + sp10C.y = 10.0f; + sp10C.z = 50.0f; + } + Matrix_RotateY((player->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + Matrix_MultVec3f(&sp10C, &sCameraEye); + + sCameraEye.x += player->actor.world.pos.x; + sCameraEye.y += player->actor.world.pos.y; + sCameraEye.z += player->actor.world.pos.z; + + sCameraAt = player->actor.world.pos; + if (sLinkAge != 1) { + sCameraAt.y += 40.0f; + } else { + sCameraAt.y += 25.0f; + } + + if (this->unk_17A[0] == 90) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_HEART_GET | 0x900); + D_80B7A67C = 40; + + if (this->unk_150 == 0) { + D_80B7A678 = this->unk_1AC; + + if (D_80B7A678 >= 75) { + D_80B7E086 = 0x409F; + } else if (D_80B7A678 >= 50) { + D_80B7E086 = 0x4091; + } else { + D_80B7E086 = 0x4083; + } + } else { + D_80B7A678 = 2.0f * this->unk_1AC; + D_80B7E086 = 0x4099; + } + + this->unk_1D5 = 0; + } + + this->unk_160 = -0x4000; + this->actor.shape.rot.y = player->actor.shape.rot.y + 0x5000; + this->actor.shape.rot.x = this->actor.shape.rot.z = this->unk_162 = this->unk_164 = this->unk_16E = 0; + + sp10C.x = 4.0f; + sp10C.y = -10.0f; + sp10C.z = 5.0f; + Matrix_MultVec3f(&sp10C, &sp100); + Math_ApproachF(&this->actor.world.pos.x, player->bodyPartsPos[15].x + sp100.x, 1.0f, 6.0f); + Math_ApproachF(&this->actor.world.pos.y, player->bodyPartsPos[15].y + sp100.y, 1.0f, 6.0f); + Math_ApproachF(&this->actor.world.pos.z, player->bodyPartsPos[15].z + sp100.z, 1.0f, 6.0f); + + D_80B7E144 = 188.0f; + + if (this->unk_17A[0] <= 50) { + switch (this->unk_1D5) { + case 0: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + if (Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + if (globalCtx->msgCtx.choiceIndex == 0) { + if (D_80B7A670 == 0.0f) { + D_80B7A670 = this->unk_1AC; + D_80B7E07C = this->unk_150; + D_80B7E07E = D_80B7E0B6; + Actor_Kill(&this->actor); + } else if ((this->unk_150 == 0) && (D_80B7E07C == 0) && + ((s16)this->unk_1AC < (s16)D_80B7A670)) { + this->unk_1D5 = 1; + this->unk_17A[0] = 0x3C; + Message_StartTextbox(globalCtx, 0x4098, NULL); + } else { + f32 temp1 = D_80B7A670; + s16 temp2 = D_80B7E07C; + D_80B7A670 = this->unk_1AC; + D_80B7E07C = this->unk_150; + D_80B7E07E = D_80B7E0B6; + this->unk_1AC = temp1; + this->unk_150 = temp2; + } + } + if (this->unk_1D5 == 0) { + D_80B7A694 = 0; + } + } + } + break; + case 1: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + if (Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + if (globalCtx->msgCtx.choiceIndex != 0) { + f32 temp1 = D_80B7A670; + s16 temp2 = D_80B7E07C; + D_80B7A670 = this->unk_1AC; + D_80B7E07E = D_80B7E0B6; + this->unk_1AC = temp1; + this->unk_150 = temp2; + } + D_80B7A694 = 0; + } + } + break; + } + } + + if (D_80B7A694 == 0) { + if (this->actor.update != NULL) { + this->unk_158 = this->unk_15A = 0; + this->unk_1A4 = 10000; + this->unk_1A2 = 500; + this->unk_17A[1] = 50; + this->unk_17A[0] = 0; + this->unk_190 = 1.0f; + this->unk_194 = 2000.0f; + SkelAnime_Free(&this->skelAnime, globalCtx); + + if (this->unk_150 == 0) { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishingFishSkel, &gFishingFishAnim, 0, 0, 0); + Animation_MorphToLoop(&this->skelAnime, &gFishingFishAnim, 0.0f); + } else { + SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gFishingLoachSkel, &gFishingLoachAnim, 0, 0, + 0); + Animation_MorphToLoop(&this->skelAnime, &gFishingLoachAnim, 0.0f); + } + } + + D_80B7E148 = 520.0f; + D_80B7E144 = 195.0f; + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA00FF); + D_80B7E0A6 = 20; + D_80B7A6CC = 3; + } + break; + + case 7: + this->unk_151 = 50; + sp134 = 5; + this->unk_1B0 = 12288.0f; + + if (this->actor.params < 104) { + this->unk_1B4 = sGroupFishes[this->actor.params - 100].pos; + D_80B7A898 = 1; + } else if (this->actor.params < 108) { + this->unk_1B4 = sGroupFishes[this->actor.params - 100 + 16].pos; + D_80B7A898 = 2; + } else { + this->unk_1B4 = sGroupFishes[this->actor.params - 100 + 32].pos; + D_80B7A898 = 3; + } + + Math_ApproachF(&this->actor.speedXZ, 5.0f, 1.0f, 1.0f); + + if (sp124 < 20.0f) { + Math_ApproachS(&this->unk_170, 0x4E20, 2, 0xFA0); + + if ((this->unk_17A[2] == 0) && func_80B70A2C(this, globalCtx, false)) { + func_80B71278(this, Rand_ZeroFloat(1.99f)); + this->unk_17A[2] = (s16)Rand_ZeroFloat(20.0f) + 20; + } + } + + if (this->unk_17A[3] == 0) { + this->unk_158 = 10; + this->unk_15A = 10; + } else { + func_80B70ED4(this, input); + if (this->actor.xzDistToPlayer < (100.0f * sp118)) { + this->unk_15A = this->unk_158 = 0; + this->unk_1A4 = 500; + this->unk_1A2 = 200; + this->unk_17A[1] = 50; + } + } + break; + } + + Math_ApproachS(&this->unk_172, (Math_SinS(this->unk_15C * 0x1000) * 5000.0f) + 5000.0f, 2, 0x7D0); + + if (this->unk_158 != 6) { + if (this->actor.world.pos.y > WATER_SURFACE_Y(globalCtx)) { + this->unk_190 = 1.5f; + this->unk_194 = 5000.0f; + + Math_ApproachS(&this->unk_16E, 0, 5, 0x7D0); + + spF4 = spF0 = spFA = 3; + spF2 = spEE = 0x2000; + + this->unk_17A[2] = 0; + this->unk_184 -= 1.0f; + } else { + Math_ApproachZeroF(&this->unk_184, 1.0f, 2.0f); + if ((this->unk_158 != -1) && (this->unk_158 != -2) && (this->unk_158 != -25)) { + this->unk_166 = 0; + } + + this->unk_168 = this->unk_16A = 0; + spF4 = spF0 = spFA = 4; + spF2 = spEE = 0x2000; + + spF6 = Fishing_SmoothStepToS(&this->actor.world.rot.y, spFC, sp134, this->unk_1B0) * 3.0f; + Math_ApproachS(&this->actor.world.rot.x, spFE, sp134, this->unk_1B0 * 0.5f); + + if (spF6 > 0x1F40) { + spF6 = 0x1F40; + } else if (spF6 < -0x1F40) { + spF6 = -0x1F40; + } + + if (this->actor.speedXZ >= 3.2f) { + Math_ApproachS(&this->unk_16E, spF6, 2, 0x4E20); + } else { + Math_ApproachS(&this->unk_16E, spF6, 3, 0xBB8); + } + + func_8002D908(&this->actor); + } + + func_8002D7EC(&this->actor); + + this->actor.world.pos.y += (this->unk_184 * 1.5f); + + if (1) {} + + if (this->unk_152 != 0) { + this->unk_168 = this->unk_154; + this->unk_152--; + if (this->unk_156 != 0) { + spF0 = 5; + spEE = 0x4000; + } else { + spF0 = 10; + spEE = 0x800; + } + this->unk_166 = -0x500 - this->actor.shape.rot.x; + spF4 = 5; + spF2 = 0x4000; + } else { + this->unk_156 = 0; + } + + Math_ApproachS(&this->unk_160, this->unk_166, spF4, spF2); + Math_ApproachS(&this->unk_162, this->unk_168, spF0, spEE); + Math_ApproachS(&this->unk_164, this->unk_16A, spFA, 0x2000); + + if (this->actor.speedXZ <= 0.5f) { + Math_ApproachS(&this->actor.shape.rot.x, 0, 10, this->unk_178); + Math_ApproachS(&this->unk_178, 0x500, 1, 0x20); + } else { + Math_ApproachS(&this->actor.shape.rot.x, -this->actor.world.rot.x, 10, 0x1000); + this->unk_178 = 0; + } + + this->actor.shape.rot.y = this->actor.world.rot.y; + + if ((this->unk_158 != -1) && (this->unk_158 != -2) && (this->unk_158 != -25)) { + if ((this->actor.world.pos.y > WATER_SURFACE_Y(globalCtx)) && + (this->actor.prevPos.y <= WATER_SURFACE_Y(globalCtx))) { + func_80B70A2C(this, globalCtx, true); + func_80B71278(this, 1); + this->unk_184 = this->actor.velocity.y; + this->actor.velocity.y = 0.0f; + this->unk_16A = Rand_CenteredFloat(32768.0f); + } else if ((this->actor.world.pos.y < WATER_SURFACE_Y(globalCtx)) && + (this->actor.prevPos.y >= WATER_SURFACE_Y(globalCtx))) { + if (this->unk_184 < -5.0f) { + this->unk_184 = -5.0f; + } + this->actor.world.rot.x = -0xFA0; + func_80B70A2C(this, globalCtx, true); + this->unk_1D2 = 20; + func_80B71278(this, 0); + } + } + + if ((this->actor.world.pos.y < WATER_SURFACE_Y(globalCtx)) && + (this->actor.world.pos.y > (WATER_SURFACE_Y(globalCtx) - 10.0f)) && ((this->unk_15C & 1) == 0) && + (this->actor.speedXZ > 0.0f)) { + Vec3f pos = this->actor.world.pos; + pos.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(&this->actor.projectedPos, globalCtx->specialEffects, &pos, 80.0f, 500.0f, 150, 90); + } + + if ((this->actor.speedXZ > 0.0f) || (this->unk_158 == 5)) { + f32 velocityY = this->actor.velocity.y; + + spD8 = this->unk_1AC * 0.1f; + + this->actor.world.pos.y -= spD8; + this->actor.prevPos.y -= spD8; + this->actor.velocity.y = -1.0f; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 30.0f, 30.0f, 100.0f, 0x45); + this->actor.world.pos.y += spD8; + this->actor.prevPos.y += spD8; + + this->actor.velocity.y = velocityY; + + if (this->actor.bgCheckFlags & 8) { + this->unk_1A0 = 20; + } + + if (this->actor.bgCheckFlags & 1) { + if (this->actor.world.pos.y > WATER_SURFACE_Y(globalCtx)) { + this->unk_184 = Rand_ZeroFloat(3.0f) + 3.0f; + this->actor.velocity.x = this->actor.world.pos.x * -0.003f; + this->actor.velocity.z = this->actor.world.pos.z * -0.003f; + + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FISH_LEAP); + func_80B70CF0(this, globalCtx); + + if (Rand_ZeroOne() < 0.5f) { + this->unk_16A = 0x4000; + } else { + this->unk_16A = -0x4000; + } + + if (Rand_ZeroOne() < 0.5f) { + this->unk_166 = 0; + } else { + this->unk_166 = (s16)Rand_CenteredFloat(32.0f) + 0x8000; + } + + this->unk_168 = (s16)Rand_CenteredFloat(16384.0f); + this->unk_190 = 1.0f; + this->unk_194 = 5000.0f; + this->unk_19C = 5000.0f; + } else { + this->unk_184 = 0.0f; + + if ((this->unk_158 == 5) && ((this->unk_15C & 1) == 0)) { + Vec3f pos; + + pos.x = Rand_CenteredFloat(10.0f) + this->actor.world.pos.x; + pos.z = Rand_CenteredFloat(10.0f) + this->actor.world.pos.z; + pos.y = this->actor.floorHeight + 5.0f; + Fishing_SpawnWaterDust(&this->actor.projectedPos, globalCtx->specialEffects, &pos, + (this->unk_1AC * 0.005f) + 0.15f); + } + } + } + } + } + + if (this->unk_1D2 != 0) { + s16 i; + Vec3f pos; + f32 range = (this->unk_1AC * 0.075f) + 10.0f; + + this->unk_1D2--; + + for (i = 0; i < 2; i++) { + pos.x = Rand_CenteredFloat(range) + this->actor.world.pos.x; + pos.y = Rand_CenteredFloat(range) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(range) + this->actor.world.pos.z; + Fishing_SpawnBubble(&this->actor.projectedPos, globalCtx->specialEffects, &pos, + Rand_ZeroFloat(0.035f) + 0.04f, 0); + } + } +} + +s32 Fishing_FishOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + Fishing* this = (Fishing*)thisx; + + if (limbIndex == 0xD) { + rot->z -= this->unk_170 - 11000; + } else if ((limbIndex == 2) || (limbIndex == 3)) { + rot->y += this->unk_16C; + } else if (limbIndex == 4) { + rot->y += this->unk_176; + } else if (limbIndex == 0xE) { + rot->y -= this->unk_172; + } else if (limbIndex == 0xF) { + rot->y += this->unk_172; + } else if (limbIndex == 8) { + rot->y += this->unk_174; + } else if (limbIndex == 9) { + rot->y -= this->unk_174; + } + + return 0; +} + +void Fishing_FishPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + Fishing* this = (Fishing*)thisx; + + if (limbIndex == 0xD) { + Matrix_MultVec3f(&sFishMouthOffset, &this->fishMouthPos); + } +} + +s32 Fishing_LoachOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + Fishing* this = (Fishing*)thisx; + + if (limbIndex == 3) { + rot->y += this->unk_1CC[0]; + } else if (limbIndex == 4) { + rot->y += this->unk_1CC[1]; + } else if (limbIndex == 5) { + rot->y += this->unk_1CC[2]; + } + + return 0; +} + +void Fishing_LoachPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + static Vec3f sLoachMouthOffset = { 500.0f, 500.0f, 0.0f }; + Fishing* this = (Fishing*)thisx; + + if (limbIndex == 0xB) { + Matrix_MultVec3f(&sLoachMouthOffset, &this->fishMouthPos); + } +} + +void Fishing_DrawFish(Actor* thisx, GlobalContext* globalCtx) { + Fishing* this = (Fishing*)thisx; + + func_80093D18(globalCtx->state.gfxCtx); + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateY(((this->unk_162 + this->actor.shape.rot.y) / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateX(((this->unk_160 + this->actor.shape.rot.x) / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ(((this->unk_164 + this->actor.shape.rot.z) / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + + if (this->unk_150 == 0) { + Matrix_RotateY((this->unk_16C * (M_PI / 32768)) - (M_PI / 2), MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, this->unk_16C * 10.0f * 0.01f, MTXMODE_APPLY); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, Fishing_FishOverrideLimbDraw, Fishing_FishPostLimbDraw, this); + } else { + Matrix_Translate(0.0f, 0.0f, 3000.0f, MTXMODE_APPLY); + Matrix_RotateY(this->unk_16C * (M_PI / 32768), MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, -3000.0f, MTXMODE_APPLY); + Matrix_RotateY(-(M_PI / 2), MTXMODE_APPLY); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, Fishing_LoachOverrideLimbDraw, Fishing_LoachPostLimbDraw, + this); + } +} + +void Fishing_HandleReedContact(FishingProp* prop, Vec3f* entityPos) { + f32 dx = prop->pos.x - entityPos->x; + f32 dz = prop->pos.z - entityPos->z; + f32 distXZ = sqrtf(SQ(dx) + SQ(dz)); + + if (distXZ <= 20.0f) { + prop->rotY = Math_Atan2F(dz, dx); + + Math_ApproachF(&prop->rotX, (20.0f - distXZ) * 0.03f, 0.2f, 0.2f); + } +} + +void Fishing_HandleLilyPadContact(FishingProp* prop, Vec3f* entityPos, u8 fishTimer) { + f32 dx = prop->pos.x - entityPos->x; + f32 dz = prop->pos.z - entityPos->z; + f32 distXZ = sqrtf(SQ(dx) + SQ(dz)); + + if (distXZ <= 40.0f) { + Math_ApproachS(&prop->lilyPadAngle, Math_Atan2S(dz, dx), 10, 0x300); + } + + if (fishTimer && (distXZ <= 60.0f)) { + f32 heightTarget = 1.0f; + + if (fishTimer >= 21) { + heightTarget = 1.5f; + } + + Math_ApproachF(&prop->lilyPadOffset, heightTarget, 0.1f, 0.2f); + } +} + +void Fishing_UpdatePondProps(GlobalContext* globalCtx) { + FishingProp* prop = &sPondProps[0]; + Player* player = GET_PLAYER(globalCtx); + Actor* actor; + s16 i; + + for (i = 0; i < POND_PROP_COUNT; i++) { + if (prop->type != FS_PROP_NONE) { + prop->shouldDraw = false; + prop->timer++; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &prop->pos, &prop->projectedPos, &sProjectedW); + + if ((prop->projectedPos.z < prop->drawDistance) && + (fabsf(prop->projectedPos.x) < (100.0f + prop->projectedPos.z))) { + prop->shouldDraw = true; + } + + if ((prop->projectedPos.z < 500.0f) && (fabsf(prop->projectedPos.x) < (100.0f + prop->projectedPos.z))) { + if (prop->type == FS_PROP_REED) { + Fishing_HandleReedContact(prop, &player->actor.world.pos); + + actor = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + while (actor != NULL) { + if (!((actor->id == ACTOR_FISHING) && (actor->params >= 100))) { + actor = actor->next; + } else { + Fishing_HandleReedContact(prop, &actor->world.pos); + actor = actor->next; + } + } + + Math_ApproachZeroF(&prop->rotX, 0.05f, 0.05f); + } else if (prop->type == FS_PROP_LILY_PAD) { + Fishing_HandleLilyPadContact(prop, &player->actor.world.pos, 0); + + actor = globalCtx->actorCtx.actorLists[ACTORCAT_NPC].head; + while (actor != NULL) { + if (!((actor->id == ACTOR_FISHING) && (actor->params >= 100))) { + actor = actor->next; + } else { + Fishing_HandleLilyPadContact(prop, &actor->world.pos, ((Fishing*)actor)->unk_151); + actor = actor->next; + } + } + + Math_ApproachS(&prop->lilyPadAngle, 0, 20, 80); + prop->pos.y = + (Math_SinS(prop->timer * 0x1000) * prop->lilyPadOffset) + (WATER_SURFACE_Y(globalCtx) + 2.0f); + Math_ApproachZeroF(&prop->lilyPadOffset, 0.1f, 0.02f); + } + } + } + + prop++; + } + + if (sCameraId == 0) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &sFishingMain->collider.base); + } +} + +void Fishing_DrawPondProps(GlobalContext* globalCtx) { + u8 flag = 0; + FishingProp* prop = &sPondProps[0]; + s16 i; + s32 pad; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 7704); + + Matrix_Push(); + + for (i = 0; i < POND_PROP_COUNT; i++) { + if (prop->type == FS_PROP_REED) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingReedMaterialDL); + flag++; + } + + if (prop->shouldDraw) { + Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); + Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY); + Matrix_RotateY(prop->rotY, MTXMODE_APPLY); + Matrix_RotateX(prop->rotX, MTXMODE_APPLY); + Matrix_RotateY(prop->reedAngle, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7726), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFishingReedModelDL); + } + } + + prop++; + } + + prop = &sPondProps[0]; + flag = 0; + for (i = 0; i < POND_PROP_COUNT; i++) { + if (prop->type == FS_PROP_WOOD_POST) { + if (flag == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFishingWoodPostMaterialDL); + flag++; + } + + if (prop->shouldDraw) { + Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); + Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7748), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingWoodPostModelDL); + } + } + + prop++; + } + + prop = &sPondProps[0]; + flag = 0; + for (i = 0; i < POND_PROP_COUNT; i++) { + if (prop->type == FS_PROP_LILY_PAD) { + if (flag == 0) { + gSPDisplayList(POLY_XLU_DISP++, gFishingLilyPadMaterialDL); + flag++; + } + + if (prop->shouldDraw) { + Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); + Matrix_Scale(prop->scale, 1.0f, prop->scale, MTXMODE_APPLY); + Matrix_RotateY(prop->lilyPadAngle * (M_PI / 32768), MTXMODE_APPLY); + Matrix_Translate(0.0f, 0.0f, 20.0f, MTXMODE_APPLY); + Matrix_RotateY(prop->rotY, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7774), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gFishingLilyPadModelDL); + } + } + + prop++; + } + + prop = &sPondProps[0]; + flag = 0; + for (i = 0; i < POND_PROP_COUNT; i++) { + if (prop->type == FS_PROP_ROCK) { + if (flag == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFishingRockMaterialDL); + flag++; + } + + if (prop->shouldDraw) { + Matrix_Translate(prop->pos.x, prop->pos.y, prop->pos.z, MTXMODE_NEW); + Matrix_Scale(prop->scale, prop->scale, prop->scale, MTXMODE_APPLY); + Matrix_RotateY(prop->rotY, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 7798), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingRockModelDL); + } + } + + prop++; + } + + Matrix_Pop(); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 7805); +} + +void Fishing_UpdateGroupFishes(GlobalContext* globalCtx) { + s16 groupContactFlags = 0; + Player* player = GET_PLAYER(globalCtx); + FishingGroupFish* fish = &sGroupFishes[0]; + f32 dy; + f32 dx; + f32 dist; + f32 dz; + f32 offset; + s16 groupIndex; + s16 groupFlag; + f32 spD8; + s16 spD6; + s16 spD4; + s16 target; + s16 i; + Vec3f basePos[3]; + Vec3f ripplePos; + Vec3f* refPos; + f32 temp1; + f32 temp2; + + if ((D_80B7E114 != 0) || (D_80B7A694 == 4)) { + refPos = &sLurePos; + } else { + refPos = &player->actor.world.pos; + } + + basePos[0].x = sinf(sFishGroupAngle1) * 720.0f; + basePos[0].y = -35.0f; + basePos[0].z = cosf(sFishGroupAngle1) * 720.0f; + + temp1 = refPos->x - basePos[0].x; + temp2 = refPos->z - basePos[0].z; + + if ((SQ(temp1) + SQ(temp2)) < SQ(50.0f)) { + sFishGroupAngle1 += 0.3f; + groupContactFlags |= 1; + } else if (D_80B7A898 != 0.0f) { + sFishGroupAngle1 += 0.05f; + basePos[0].y = WATER_SURFACE_Y(globalCtx) - 5.0f; + } else { + Math_ApproachF(&sFishGroupAngle1, 0.7f, 1.0f, 0.001f); + } + + basePos[1].x = sinf(sFishGroupAngle2) * 720.0f; + basePos[1].y = -35.0f; + basePos[1].z = cosf(sFishGroupAngle2) * 720.0f; + + temp1 = refPos->x - basePos[1].x; + temp2 = refPos->z - basePos[1].z; + + if ((SQ(temp1) + SQ(temp2)) < SQ(50.0f)) { + sFishGroupAngle2 -= 0.3f; + groupContactFlags |= 2; + } else if (D_80B7A898 != 0.0f) { + sFishGroupAngle2 -= 0.05f; + basePos[1].y = WATER_SURFACE_Y(globalCtx) - 5.0f; + } else { + Math_ApproachF(&sFishGroupAngle2, 2.3f, 1.0f, 0.001f); + } + + basePos[2].x = sinf(sFishGroupAngle3) * 720.0f; + basePos[2].y = -35.0f; + basePos[2].z = cosf(sFishGroupAngle3) * 720.0f; + + temp1 = refPos->x - basePos[2].x; + temp2 = refPos->z - basePos[2].z; + + if ((SQ(temp1) + SQ(temp2)) < SQ(50.0f)) { + sFishGroupAngle3 -= 0.3f; + groupContactFlags |= 4; + } else if (D_80B7A898 != 0.0f) { + sFishGroupAngle3 -= 0.05f; + basePos[2].y = WATER_SURFACE_Y(globalCtx) - 5.0f; + } else { + Math_ApproachF(&sFishGroupAngle3, 4.6f, 1.0f, 0.001f); + } + + if (sLinkAge == 1) { + spD8 = 0.8f; + } else { + spD8 = 1.0f; + } + + for (i = 0; i < GROUP_FISH_COUNT; i++) { + if (fish->type != FS_GROUP_FISH_NONE) { + fish->timer++; + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &fish->pos, &fish->projectedPos, &sProjectedW); + + if ((fish->projectedPos.z < 400.0f) && (fabsf(fish->projectedPos.x) < (100.0f + fish->projectedPos.z))) { + fish->shouldDraw = true; + } else { + fish->shouldDraw = false; + } + + if (i <= 20) { + groupIndex = 0; + groupFlag = 1; + } else if (i <= 40) { + groupIndex = 1; + groupFlag = 2; + } else { + groupIndex = 2; + groupFlag = 4; + } + + dx = fish->unk_10.x - fish->pos.x; + dy = fish->unk_10.y - fish->pos.y; + dz = fish->unk_10.z - fish->pos.z; + spD4 = Math_Atan2S(dz, dx); + dist = sqrtf(SQ(dx) + SQ(dz)); + spD6 = Math_Atan2S(dist, dy); + + if ((dist < 10.0f) || (((fish->timer % 32) == 0) && (Rand_ZeroOne() > 0.5f))) { + fish->unk_10.y = basePos[groupIndex].y + Rand_CenteredFloat(10.0f); + + if (D_80B7A898 != 0.0f) { + fish->unk_10.x = basePos[groupIndex].x + Rand_CenteredFloat(200.0f); + fish->unk_10.z = basePos[groupIndex].z + Rand_CenteredFloat(200.0f); + } else { + fish->unk_10.x = basePos[groupIndex].x + Rand_CenteredFloat(100.0f); + fish->unk_10.z = basePos[groupIndex].z + Rand_CenteredFloat(100.0f); + } + + ripplePos = fish->pos; + ripplePos.y = WATER_SURFACE_Y(globalCtx); + Fishing_SpawnRipple(&fish->projectedPos, globalCtx->specialEffects, &ripplePos, 20.0f, + Rand_ZeroFloat(50.0f) + 100.0f, 150, 90); + + if (fish->unk_28 < 1.5f) { + fish->unk_28 = 1.5f; + } + + fish->unk_34 = 1.5f; + fish->unk_38 = 1.0f; + } + + target = Fishing_SmoothStepToS(&fish->unk_3E, spD4, 5, 0x4000) * 3.0f; + if (target > 0x1F40) { + target = 0x1F40; + } else if (target < -0x1F40) { + target = -0x1F40; + } + + Math_ApproachS(&fish->unk_42, target, 3, 0x1388); + + offset = fish->unk_42 * -0.0001f; + Math_ApproachS(&fish->unk_3C, spD6, 5, 0x4000); + + if (groupContactFlags & groupFlag) { + fish->unk_38 = 1.0f; + fish->unk_28 = 6.0f; + fish->unk_34 = 2.0f; + } + + if (D_80B7A898 != 0.0f) { + fish->unk_38 = 1.0f; + fish->unk_28 = 4.0f; + fish->unk_34 = 2.0f; + } + + Math_ApproachF(&fish->unk_28, 0.75f, 1.0f, 0.05f); + + temp1 = fish->unk_28 * spD8; + temp2 = Math_CosS(fish->unk_3C) * temp1; + + fish->pos.x += temp2 * Math_SinS(fish->unk_3E); + fish->pos.y += temp1 * Math_SinS(fish->unk_3C); + fish->pos.z += temp2 * Math_CosS(fish->unk_3E); + + if (fish->shouldDraw) { + Math_ApproachF(&fish->unk_34, 1.0f, 1.0f, 0.1f); + Math_ApproachF(&fish->unk_38, 0.4f, 1.0f, 0.04f); + fish->unk_30 += fish->unk_34; + fish->unk_2C = (cosf(fish->unk_30) * fish->unk_38) + offset; + } + } + + fish++; + } + + D_80B7A898 = 0.0f; +} + +void Fishing_DrawGroupFishes(GlobalContext* globalCtx) { + u8 flag = 0; + FishingGroupFish* fish = &sGroupFishes[0]; + f32 scale; + s16 i; + s32 pad; + + if (sLinkAge == 1) { + scale = 0.003325f; + } else { + scale = 0.00475f; + } + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 8048); + + for (i = 0; i < GROUP_FISH_COUNT; i++) { + if (fish->type != FS_GROUP_FISH_NONE) { + if (flag == 0) { + gSPDisplayList(POLY_OPA_DISP++, gFishingGroupFishMaterialDL); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 155, 155, 155, 255); + flag++; + } + + if (fish->shouldDraw) { + Matrix_Translate(fish->pos.x, fish->pos.y, fish->pos.z, MTXMODE_NEW); + Matrix_RotateY(((f32)fish->unk_3E * M_PI) / 32768.0f, MTXMODE_APPLY); + Matrix_RotateX((-(f32)fish->unk_3C * M_PI) / 32768.0f, MTXMODE_APPLY); + Matrix_Scale(fish->unk_2C * scale, scale, scale, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 8093), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gFishingGroupFishModelDL); + } + } + fish++; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 8099); +} + +static u16 D_80B7AFB8[] = { 0x4096, 0x408D, 0x408E, 0x408F, 0x4094, 0x4095 }; + +void Fishing_HandleOwnerDialog(Fishing* this, GlobalContext* globalCtx) { + switch (this->unk_15C) { + case 0: + if (D_80B7E0AC == 0) { + if (sLinkAge != 1) { + if ((HIGH_SCORE(HS_FISHING) & 0x100) && !(HIGH_SCORE(HS_FISHING) & 0x200)) { + this->actor.textId = 0x4093; + } else { + this->actor.textId = 0x407B; + } + } else { + this->actor.textId = 0x407B; + } + } else if (D_80B7A68C == 0) { + this->actor.textId = 0x4084; + } else { + this->actor.textId = 0x4097; + } + + if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) { + if (D_80B7E0AC == 0) { + this->unk_15C = 1; + if (sLinkAge != 1) { + HIGH_SCORE(HS_FISHING) |= 0x200; + } else { + HIGH_SCORE(HS_FISHING) |= 0x100; + } + } else { + this->unk_15C = 10; + } + } else { + func_8002F2CC(&this->actor, globalCtx, 100.0f); + } + break; + + case 1: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (gSaveContext.rupees >= 20) { + Rupees_ChangeBy(-20); + if (func_800AA148() == 0) { + this->actor.textId = 0x407C; + } else { + this->actor.textId = 0x407D; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + this->unk_15C = 2; + } else { + Message_ContinueTextbox(globalCtx, 0x407E); + this->unk_15C = 3; + } + break; + case 1: + Message_ContinueTextbox(globalCtx, 0x2D); + this->unk_15C = 3; + break; + } + } + break; + + case 2: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + Message_ContinueTextbox(globalCtx, 0x407F); + this->unk_15C = 4; + } + break; + + case 3: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->unk_15C = 0; + } + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) { + this->unk_15C = 0; + } + break; + + case 4: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + D_80B7A678 = D_80B7E078; + Message_ContinueTextbox(globalCtx, 0x4080); + this->unk_15C = 5; + break; + case 1: + Message_ContinueTextbox(globalCtx, 0x407F); + break; + } + } + break; + + case 5: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + globalCtx->interfaceCtx.unk_260 = 1; + globalCtx->startPlayerFishing(globalCtx); + D_80B7E0AC = 1; + D_80B7A684 = 20; + this->unk_15C = 0; + + if ((HIGH_SCORE(HS_FISHING) & 0xFF0000) < 0xFF0000) { + HIGH_SCORE(HS_FISHING) += 0x10000; + } + } + break; + + case 10: + if (D_80B7A68C != 0) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + Message_ContinueTextbox(globalCtx, 0x40B2); + D_80B7A688 = 1; + D_80B7A68C = 0; + this->unk_15C = 20; + break; + case 1: + this->unk_15C = 0; + break; + } + } + } else { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + if (D_80B7A670 == 0.0f) { + this->actor.textId = 0x408C; + this->unk_15C = 20; + } else if (D_80B7E07C == 0) { + D_80B7A678 = D_80B7A670; + if ((s16)D_80B7E078 < (s16)D_80B7A670) { + if (D_80B7E07E == 2) { + this->actor.textId = 0x40B0; + } else { + this->actor.textId = 0x4086; + } + this->unk_15C = 11; + } else { + this->actor.textId = 0x408B; + this->unk_15C = 20; + } + } else { + this->actor.textId = 0x409B; + this->unk_15C = 11; + } + Message_ContinueTextbox(globalCtx, this->actor.textId); + break; + case 1: + if (D_80B7A680 > 36000) { + D_80B7A680 = 30000; + Message_ContinueTextbox(globalCtx, 0x4088); + } else { + if (D_80B7E076 == 0) { + if (D_80B7E082 == 0) { + D_80B7E082++; + } + } + + if ((D_80B7E0B6 == 2) && (D_80B7AFB8[D_80B7E082] == 0x408D)) { + Message_ContinueTextbox(globalCtx, 0x40AF); + } else { + Message_ContinueTextbox(globalCtx, D_80B7AFB8[D_80B7E082]); + } + + D_80B7E082++; + + if (sLinkAge != 1) { + if (D_80B7E082 >= 6) { + D_80B7E082 = 0; + } + } else { + if (D_80B7E082 >= 4) { + D_80B7E082 = 0; + } + } + } + this->unk_15C = 0; + break; + case 2: + if (D_80B7E084 == 0) { + Message_ContinueTextbox(globalCtx, 0x4085); + } else if (sLinkAge == 1) { + Message_ContinueTextbox(globalCtx, 0x4092); + } + this->unk_15C = 22; + break; + } + } + } + break; + + case 11: + if (((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) && + Message_ShouldAdvance(globalCtx)) { + s32 getItemId; + + Message_CloseTextbox(globalCtx); + + if (D_80B7E07C == 0) { + D_80B7E078 = D_80B7A670; + D_80B7A670 = 0.0f; + + if (sLinkAge == 1) { + f32 temp; + + HIGH_SCORE(HS_FISHING) &= 0xFFFFFF00; + HIGH_SCORE(HS_FISHING) |= (s32)D_80B7E078 & 0x7F; + + temp = (HIGH_SCORE(HS_FISHING) & 0x7F000000) >> 0x18; + if (temp < D_80B7E078) { + HIGH_SCORE(HS_FISHING) &= 0xFFFFFF; + HIGH_SCORE(HS_FISHING) |= ((s32)D_80B7E078 & 0x7F) << 0x18; + + if (D_80B7E07E == 2) { + HIGH_SCORE(HS_FISHING) |= 0x80000000; + } + } + + if (D_80B7E07E == 2) { + HIGH_SCORE(HS_FISHING) |= 0x80; + this->unk_15C = 0; + break; + } + } else { + HIGH_SCORE(HS_FISHING) &= 0xFFFFFF; + HIGH_SCORE(HS_FISHING) |= ((s32)D_80B7E078 & 0x7F) << 0x18; + + if (D_80B7E07E == 2) { + HIGH_SCORE(HS_FISHING) |= 0x80000000; + this->unk_15C = 0; + break; + } + } + + if (D_80B7E078 >= 60.0f) { + getItemId = GI_RUPEE_PURPLE; + } else if (D_80B7E078 >= 50.0f) { + getItemId = GI_RUPEE_RED; + } else if (D_80B7E078 >= 40.0f) { + getItemId = GI_RUPEE_BLUE; + } else { + getItemId = GI_RUPEE_GREEN; + } + + if (sLinkAge == 1) { + if ((D_80B7E078 >= 50.0f) && !(HIGH_SCORE(HS_FISHING) & 0x400)) { + HIGH_SCORE(HS_FISHING) |= 0x400; + getItemId = GI_HEART_PIECE; + sSinkingLureLocation = (u8)Rand_ZeroFloat(3.999f) + 1; + } + } else { + if ((D_80B7E078 >= 60.0f) && !(HIGH_SCORE(HS_FISHING) & 0x800)) { + HIGH_SCORE(HS_FISHING) |= 0x800; + getItemId = GI_SCALE_GOLD; + sSinkingLureLocation = (u8)Rand_ZeroFloat(3.999f) + 1; + } + } + } else { + getItemId = GI_RUPEE_PURPLE; + D_80B7A670 = 0.0f; + } + + this->actor.parent = NULL; + func_8002F434(&this->actor, globalCtx, getItemId, 2000.0f, 1000.0f); + this->unk_15C = 23; + } + break; + + case 20: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + this->unk_15C = 0; + } + break; + + case 21: + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) && Message_ShouldAdvance(globalCtx)) { + Message_CloseTextbox(globalCtx); + + switch (globalCtx->msgCtx.choiceIndex) { + case 0: + this->unk_15C = 0; + break; + case 1: + if (D_80B7E084 == 0) { + Message_ContinueTextbox(globalCtx, 0x4085); + } else if (sLinkAge == 1) { + Message_ContinueTextbox(globalCtx, 0x4092); + } + this->unk_15C = 22; + break; + } + } + break; + + case 22: + if (globalCtx) {} + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + this->unk_15C = 0; + if (D_80B7A68C != 0) { + D_80B7A688 = 1; + D_80B7A68C = 0; + } + D_80B7E0AC = 0; + globalCtx->interfaceCtx.unk_260 = 0; + } + break; + + case 23: + D_80B7A674 = false; + if (Actor_HasParent(&this->actor, globalCtx)) { + this->unk_15C = 24; + } else { + func_8002F434(&this->actor, globalCtx, GI_SCALE_GOLD, 2000.0f, 1000.0f); + } + break; + + case 24: + D_80B7A674 = false; + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) { + if (D_80B7E07C == 0) { + this->unk_15C = 0; + } else { + Message_StartTextbox(globalCtx, 0x409C, NULL); + this->unk_15C = 20; + } + } + break; + } +} + +static s16 D_80B7AFC4[] = { 0, 1, 2, 2, 1 }; + +static Vec3f sStreamSoundPos = { 670.0f, 0.0f, -600.0f }; + +static Vec3s sSinkingLureLocationPos[] = { + { -364, -30, -269 }, + { 1129, 3, -855 }, + { -480, 0, -1055 }, + { 553, -48, -508 }, +}; + +void Fishing_UpdateOwner(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Fishing* this = (Fishing*)thisx; + Vec3f sp114; + Vec3f sp108; + Vec3f spFC; + s16 headRotTarget; + s16 playerShadowAlpha; + f32 target; + f32 camAtFraction; + f32 lureDistXZ; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + Input* input = &globalCtx->state.input[0]; + + if (0) { + // Strings existing only in rodata + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("plays %x\n"); + osSyncPrintf("ys %x\n"); + osSyncPrintf(VT_RST); + } + + playerShadowAlpha = player->actor.shape.shadowAlpha; + + if ((SQ(player->actor.world.pos.x) + SQ(player->actor.world.pos.z)) < SQ(920.0f)) { + Math_ApproachS(&playerShadowAlpha, 0, 1, 40); + } else { + Math_ApproachS(&playerShadowAlpha, 200, 1, 40); + } + + player->actor.shape.shadowAlpha = playerShadowAlpha; + + SkelAnime_Update(&this->skelAnime); + + if ((D_80B7A684 != 0) || (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) { + this->actor.flags &= ~ACTOR_FLAG_0; + } else { + this->actor.flags |= ACTOR_FLAG_0 | ACTOR_FLAG_5; + } + + if ((this->actor.xzDistToPlayer < 120.0f) || (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE)) { + headRotTarget = this->actor.shape.rot.y - this->actor.yawTowardsPlayer; + } else { + headRotTarget = 0; + } + + if (headRotTarget > 0x2710) { + headRotTarget = 0x2710; + } else if (headRotTarget < -0x2710) { + headRotTarget = -0x2710; + } + + Math_ApproachS(&this->unk_164, headRotTarget, 3, 0x1388); + + if (((globalCtx->gameplayFrames % 32) == 0) && (Rand_ZeroOne() < 0.3f)) { + this->unk_162 = 4; + } + + this->unk_160 = D_80B7AFC4[this->unk_162]; + + if (this->unk_162 != 0) { + this->unk_162--; + } + + if (D_80B7A684 != 0) { + D_80B7A684--; + } + + if ((D_80B7A68C == 0) && (D_80B7E0B6 != 2) && (D_80B7A694 > 0) && (D_80B7A688 == 1) && (D_80B7A684 == 0)) { + f32 dx = sOwnerHeadPos.x - sLurePos.x; + f32 dy = sOwnerHeadPos.y - sLurePos.y; + f32 dz = sOwnerHeadPos.z - sLurePos.z; + + if ((sqrtf(SQ(dx) + SQ(dy) + SQ(dz)) < 25.0f) || (KREG(77) > 0)) { + KREG(77) = 0; + D_80B7A688 = 0; + D_80B7A68C = 1; + Message_StartTextbox(globalCtx, 0x4087, NULL); + } + } + + if (D_80B7A688 == 0) { + HIGH_SCORE(HS_FISHING) |= 0x1000; + } else if (D_80B7A688 == 1) { + HIGH_SCORE(HS_FISHING) &= ~0x1000; + } + + if (KREG(77) < 0) { + KREG(77) = 0; + D_80B7A690 = 1; + } + + if (D_80B7A67C != 0) { + D_80B7A67C--; + if (D_80B7A67C == 0) { + Message_StartTextbox(globalCtx, D_80B7E086, NULL); + } + } + + Fishing_HandleOwnerDialog(this, globalCtx); + + D_80B7E14C = 0.0015f; + D_80B7A680++; + + if ((D_80B7E0AC != 0) && D_80B7A674) { + Fishing_UpdateLure(this, globalCtx); + } + + Fishing_UpdateEffects(globalCtx->specialEffects, globalCtx); + Fishing_UpdatePondProps(globalCtx); + Fishing_UpdateGroupFishes(globalCtx); + + if ((D_80B7E0AC != 0) && (D_80B7A6CC == 0) && (player->actor.world.pos.z > 1360.0f) && + (fabsf(player->actor.world.pos.x) < 25.0f)) { + player->actor.world.pos.z = 1360.0f; + player->actor.speedXZ = 0.0f; + + if (D_80B7A6D0 == 0) { + D_80B7A6CC = 10; + } + } + + if ((sSinkingLureLocation != 0) && + (fabsf(player->actor.world.pos.x - sSinkingLureLocationPos[sSinkingLureLocation - 1].x) < 25.0f) && + (fabsf(player->actor.world.pos.y - sSinkingLureLocationPos[sSinkingLureLocation - 1].y) < 10.0f) && + (fabsf(player->actor.world.pos.z - sSinkingLureLocationPos[sSinkingLureLocation - 1].z) < 25.0f)) { + sSinkingLureLocation = 0; + D_80B7A6CC = 20; + func_800A9F6C(0.0f, 150, 10, 10); + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x1400FF); + } + + if (KREG(0) != 0) { + KREG(0) = 0; + D_80B7E0B6 = 0; + D_80B7A6CC = 20; + func_800A9F6C(0.0f, 150, 10, 10); + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x1400FF); + } + + if (D_80B7A6D0 != 0) { + D_80B7A6D0--; + } + + switch (D_80B7A6CC) { + case 0: + break; + + case 1: { + Camera* camera; + + sCameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCameraId, CAM_STAT_ACTIVE); + camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + sCameraEye.x = camera->eye.x; + sCameraEye.y = camera->eye.y; + sCameraEye.z = camera->eye.z; + sCameraAt.x = camera->at.x; + sCameraAt.y = camera->at.y; + sCameraAt.z = camera->at.z; + D_80B7A6CC = 2; + Interface_ChangeAlpha(12); + D_80B7FECC = 0.0f; + // fallthrough + } + + case 2: + ShrinkWindow_SetVal(0x1B); + + spFC.x = sLurePos.x - player->actor.world.pos.x; + spFC.z = sLurePos.z - player->actor.world.pos.z; + lureDistXZ = sqrtf(SQ(spFC.x) + SQ(spFC.z)); + Matrix_RotateY(Math_Atan2F(spFC.z, spFC.x), MTXMODE_NEW); + + sp114.x = 0.0f; + sp114.y = 0.0f; + sp114.z = 100.0f; + Matrix_MultVec3f(&sp114, &spFC); + + if (D_80B7A694 == 1) { + camAtFraction = 0.2f; + } else { + camAtFraction = 0.1f; + } + + Math_ApproachF(&sCameraAt.x, sLurePos.x, camAtFraction, fabsf(spFC.x) * D_80B7FECC); + Math_ApproachF(&sCameraAt.y, sLurePos.y, camAtFraction, 50.0f * D_80B7FECC); + Math_ApproachF(&sCameraAt.z, sLurePos.z, camAtFraction, fabsf(spFC.z) * D_80B7FECC); + + sp114.x = 0.0f - D_80B7FED0; + if (sLinkAge != 1) { + sp114.y = 80.0f; + } else { + sp114.y = 55.0f; + } + sp114.z = -80.0f; + + Matrix_MultVec3f(&sp114, &sp108); + sp108.x += player->actor.world.pos.x; + sp108.y += player->actor.world.pos.y; + sp108.z += player->actor.world.pos.z; + + Math_ApproachF(&D_80B7FED0, 30.0f, 0.1f, 0.4f); + + if (CHECK_BTN_ALL(input->press.button, BTN_Z)) { + if ((D_80B7E088 >= 0) && (D_80B7E122 == 0)) { + D_80B7E088++; + + if (D_80B7E088 >= 4) { + D_80B7E088 = 0; + } + + if ((D_80B7E088 == 0) || (D_80B7E088 == 3)) { + func_80078884(NA_SE_SY_CAMERA_ZOOM_DOWN); + } else { + func_80078884(NA_SE_SY_CAMERA_ZOOM_UP); + } + } + } + + if (D_80B7A694 >= 3) { + if (lureDistXZ < 110.0f) { + D_80B7E088 = -1; + } else if ((lureDistXZ > 300.0f) && (D_80B7E088 < 0)) { + D_80B7E088 = 0; + } + } + + if (D_80B7E088 > 0) { + f32 dist; + f32 offset; + f32 factor; + + dist = sqrtf(SQ(spFC.x) + SQ(spFC.z)) * 0.001f; + if (dist > 1.0f) { + dist = 1.0f; + } + if (D_80B7E088 == 2) { + offset = 0.3f; + } else { + offset = 0.1f; + } + factor = 0.4f + offset + (dist * 0.4f); + + sp108.x += (sLurePos.x - sp108.x) * factor; + sp108.y += ((sLurePos.y - sp108.y) * factor) + 20.0f; + sp108.z += (sLurePos.z - sp108.z) * factor; + D_80B7E14C = 0.0005000001f; + } + + sp114.x = 0.0f; + sp114.y = 0.0f; + sp114.z = 100.0f; + Matrix_MultVec3f(&sp114, &spFC); + + Math_ApproachF(&sCameraEye.x, sp108.x, 0.3f, fabsf(spFC.x) * D_80B7FECC); + Math_ApproachF(&sCameraEye.y, sp108.y, 0.3f, 20.0f * D_80B7FECC); + Math_ApproachF(&sCameraEye.z, sp108.z, 0.3f, fabsf(spFC.z) * D_80B7FECC); + break; + + case 3: { + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + camera->eye = sCameraEye; + camera->eyeNext = sCameraEye; + camera->at = sCameraAt; + func_800C08AC(globalCtx, sCameraId, 0); + func_80064534(globalCtx, &globalCtx->csCtx); + D_80B7A6CC = 0; + sCameraId = 0; + Environment_EnableUnderwaterLights(globalCtx, 0); + globalCtx->envCtx.adjFogNear = 0; + player->unk_860 = -5; + D_80B7E0B0 = 5; + break; + } + + case 10: { + Camera* camera; + + func_80064520(globalCtx, &globalCtx->csCtx); + sCameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCameraId, CAM_STAT_ACTIVE); + func_8002DF54(globalCtx, &this->actor, 5); + camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + sCameraEye.x = camera->eye.x; + sCameraEye.y = camera->eye.y; + sCameraEye.z = camera->eye.z; + sCameraAt.x = camera->at.x; + sCameraAt.y = camera->at.y; + sCameraAt.z = camera->at.z; + Message_StartTextbox(globalCtx, 0x409E, NULL); + D_80B7A6CC = 11; + func_800A9F6C(0.0f, 150, 10, 10); + // fallthrough + } + + case 11: + player->actor.world.pos.z = 1360.0f; + player->actor.speedXZ = 0.0f; + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + camera->eye = sCameraEye; + camera->eyeNext = sCameraEye; + camera->at = sCameraAt; + func_800C08AC(globalCtx, sCameraId, 0); + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + D_80B7A6CC = 0; + sCameraId = 0; + D_80B7A6D0 = 30; + Environment_EnableUnderwaterLights(globalCtx, 0); + globalCtx->envCtx.adjFogNear = 0; + } + break; + + case 20: { + Camera* camera; + + func_80064520(globalCtx, &globalCtx->csCtx); + sCameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, MAIN_CAM, CAM_STAT_WAIT); + Gameplay_ChangeCameraStatus(globalCtx, sCameraId, CAM_STAT_ACTIVE); + func_8002DF54(globalCtx, &this->actor, 5); + camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + sCameraEye.x = camera->eye.x; + sCameraEye.y = camera->eye.y; + sCameraEye.z = camera->eye.z; + sCameraAt.x = camera->at.x; + sCameraAt.y = camera->at.y; + sCameraAt.z = camera->at.z; + Message_StartTextbox(globalCtx, 0x409A, NULL); + D_80B7A6CC = 21; + D_80B7FEC8 = 45.0f; + D_80B7A6D0 = 10; + // fallthrough + } + + case 21: + if ((D_80B7A6D0 == 0) && Message_ShouldAdvance(globalCtx)) { + D_80B7A6CC = 22; + D_80B7A6D0 = 40; + func_8002DF54(globalCtx, &this->actor, 0x1C); + D_80B7FEE4 = 0.0f; + } + break; + + case 22: + if (D_80B7A6D0 == 30) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_ITEM_GET | 0x900); + } + + D_80B7A6D4 = 1; + + Math_ApproachF(&D_80B7FEE4, 71.0f, 0.5f, 3.0f); + Matrix_RotateY((player->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_NEW); + + sp114.x = Math_SinS(globalCtx->gameplayFrames * 0x1000); + sp114.y = D_80B7FEE4; + sp114.z = -5.0f; + if (sLinkAge == 1) { + sp114.y -= 20.0f; + } + + Matrix_MultVec3f(&sp114, &sp108); + + sSinkingLureBasePos.x = player->actor.world.pos.x + sp108.x; + sSinkingLureBasePos.y = player->actor.world.pos.y + sp108.y; + sSinkingLureBasePos.z = player->actor.world.pos.z + sp108.z; + + Math_ApproachF(&D_80B7FEC8, 15.0f, 0.1f, 0.75f); + + sp114.x = D_80B7FEC8 - 15.0f; + + if (sLinkAge != 1) { + sp114.y = 60.0f; + sp114.z = -30.0f; + } else { + sp114.y = 40.0f; + sp114.z = -35.0f; + } + + Matrix_MultVec3f(&sp114, &sCameraEye); + sCameraEye.x += player->actor.world.pos.x; + sCameraEye.y += player->actor.world.pos.y; + sCameraEye.z += player->actor.world.pos.z; + + sCameraAt = player->actor.world.pos; + if (sLinkAge != 1) { + sCameraAt.y += 62.0f; + } else { + sCameraAt.y += 40.0f; + } + + if (D_80B7A6D0 == 0) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) || + (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE)) { + if (Message_ShouldAdvance(globalCtx)) { + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + + Message_CloseTextbox(globalCtx); + if (globalCtx->msgCtx.choiceIndex == 0) { + D_80B7E0B6 = 2; + D_80B7E082 = 0; + } + + camera->eye = sCameraEye; + camera->eyeNext = sCameraEye; + camera->at = sCameraAt; + func_800C08AC(globalCtx, sCameraId, 0); + func_80064534(globalCtx, &globalCtx->csCtx); + func_8002DF54(globalCtx, &this->actor, 7); + D_80B7A6CC = 0; + sCameraId = 0; + player->unk_860 = -5; + D_80B7E0B0 = 5; + D_80B7A6D4 = 0; + D_80B7E0A6 = 20; + Environment_EnableUnderwaterLights(globalCtx, 0); + globalCtx->envCtx.adjFogNear = 0; + } + } + } + break; + + case 100: + break; + } + + if (sCameraId != 0) { + Gameplay_CameraSetAtEye(globalCtx, sCameraId, &sCameraAt, &sCameraEye); + Math_ApproachF(&D_80B7FECC, 1.0f, 1.0f, 0.02f); + + if (sCameraEye.y <= (WATER_SURFACE_Y(globalCtx) + 1.0f)) { + Environment_EnableUnderwaterLights(globalCtx, 1); + if (D_80B7E076 != 0) { + globalCtx->envCtx.adjFogNear = -0xB2; + } else { + globalCtx->envCtx.adjFogNear = -0x2E; + } + } else { + Environment_EnableUnderwaterLights(globalCtx, 0); + globalCtx->envCtx.adjFogNear = 0; + } + } + + if ((player->actor.floorHeight < (WATER_SURFACE_Y(globalCtx) - 3.0f)) && + (player->actor.world.pos.y < (player->actor.floorHeight + 3.0f)) && (player->actor.speedXZ > 1.0f) && + ((globalCtx->gameplayFrames % 2) == 0)) { + Vec3f pos; + + pos.x = Rand_CenteredFloat(20.0f) + player->actor.world.pos.x; + pos.z = Rand_CenteredFloat(20.0f) + player->actor.world.pos.z; + pos.y = player->actor.floorHeight + 5.0f; + Fishing_SpawnWaterDust(NULL, globalCtx->specialEffects, &pos, 0.5f); + } + + if ((player->actor.floorHeight < WATER_SURFACE_Y(globalCtx)) && + (player->actor.floorHeight > (WATER_SURFACE_Y(globalCtx) - 10.0f)) && (player->actor.speedXZ >= 4.0f) && + ((globalCtx->gameplayFrames % 4) == 0)) { + s16 i; + + for (i = 0; i < 10; i++) { + Vec3f pos; + Vec3f vel; + f32 speedXZ; + f32 angle; + + speedXZ = Rand_ZeroFloat(1.5f) + 1.5f; + angle = Rand_ZeroFloat(6.28f); + + vel.x = sinf(angle) * speedXZ; + vel.z = cosf(angle) * speedXZ; + vel.y = Rand_ZeroFloat(3.0f) + 2.0f; + + pos = player->actor.world.pos; + pos.x += 2.0f * vel.x; + pos.y = WATER_SURFACE_Y(globalCtx); + pos.z += 2.0f * vel.z; + Fishing_SpawnDustSplash(NULL, globalCtx->specialEffects, &pos, &vel, Rand_ZeroFloat(0.01f) + 0.020000001f); + } + } + + if (sREG(15) != 0) { + if (D_80B7A654 != (sREG(15) - 1)) { + if (D_80B7A654 == 0) { + globalCtx->envCtx.gloomySkyMode = 1; + } else { + globalCtx->envCtx.gloomySkyMode = 2; + } + } + + D_80B7A654 = sREG(15) - 1; + } + + if (sREG(14) == 1) { + globalCtx->envCtx.gloomySkyMode = 1; + } + if (sREG(14) == -1) { + globalCtx->envCtx.gloomySkyMode = 2; + } + + sREG(14) = 0; + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("zelda_time %x\n", ((void)0, gSaveContext.dayTime)); + osSyncPrintf(VT_RST); + + if (D_80B7E077 >= 2) { + D_80B7E077--; + } + + if ((D_80B7E077 == 1) && (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) && + ((D_80B7A680 & 0xFFF) == 0xFFF)) { + D_80B7E077 = 200; + + if (Rand_ZeroOne() < 0.5f) { + D_80B7A654 = (u8)Rand_ZeroFloat(10.0f) + 5; + globalCtx->envCtx.gloomySkyMode = 1; + } else { + D_80B7A654 = 0; + globalCtx->envCtx.gloomySkyMode = 2; + } + } + + Math_ApproachF(&D_80B7A650, D_80B7A654, 1.0f, 0.05f); + + if (D_80B7A650 > 0.0f) { + target = (D_80B7A650 * 0.03f) + 0.8f; + if (target > 1.2f) { + target = 1.2f; + } + Math_ApproachF(&D_80B7A668, target, 1.0f, 0.01f); + } + + target = (10.0f - D_80B7A650) * 150.1f; + if (target < 0.0f) { + target = 0.0f; + } + if (1) {} + if (1) {} + Math_ApproachF(&D_80B7A65C.z, target, 1.0f, 5.0f); + + if (D_80B7A65C.z < 1500.0f) { + func_800F436C(&D_80B7A65C, NA_SE_EV_RAIN - SFX_FLAG, D_80B7A668); + } + + if (D_80B7A654 != 0) { + Math_ApproachF(&D_80B7A658, -200.0f, 1.0f, 2.0f); + } else { + Math_ApproachZeroF(&D_80B7A658, 1.0f, 2.0f); + } + + globalCtx->envCtx.adjLight1Color[0] = globalCtx->envCtx.adjLight1Color[1] = globalCtx->envCtx.adjLight1Color[2] = + D_80B7A658; + + if ((u8)D_80B7A650 > 0) { + s32 pad; + Camera* camera = Gameplay_GetCamera(globalCtx, MAIN_CAM); + s16 i; + s32 pad1; + Vec3f pos; + Vec3f rot; + Vec3f projectedPos; + s32 pad2; + + rot.x = M_PI / 2.0f + 0.1f; + rot.y = 1.0f; + rot.z = (Camera_GetInputDirYaw(camera) * -(M_PI / 32768)) + rot.y; + + for (i = 0; i < (u8)D_80B7A650; i++) { + pos.x = Rand_CenteredFloat(700.0f) + globalCtx->view.eye.x; + pos.y = (Rand_ZeroFloat(100.0f) + 150.0f) - 170.0f; + pos.z = Rand_CenteredFloat(700.0f) + globalCtx->view.eye.z; + + if (pos.z < 1160.0f) { + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &pos, &projectedPos, &sProjectedW); + + if (projectedPos.z < 0.0f) { + i--; + } else { + Fishing_SpawnRainDrop(globalCtx->specialEffects, &pos, &rot); + } + } + } + } + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &sStreamSoundPos, &sStreamSoundProjectedPos, + &sProjectedW); + + func_80078914(&sStreamSoundProjectedPos, NA_SE_EV_WATER_WALL - SFX_FLAG); + + gSaveContext.minigameScore = (SQ((f32)D_80B7A678) * 0.0036f) + 0.5f; + + if (BREG(26) != 0) { + BREG(26) = 0; + Message_StartTextbox(globalCtx, 0x407B + BREG(27), NULL); + } + + osSyncPrintf("HI_SCORE = %x\n", HIGH_SCORE(HS_FISHING)); +} + +s32 Fishing_OwnerOverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, + void* thisx) { + Fishing* this = (Fishing*)thisx; + + if (limbIndex == 8) { // Head + rot->x -= this->unk_164; + } + + return 0; +} + +void Fishing_OwnerPostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx) { + if (limbIndex == 8) { // Head + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 9134); + Matrix_MultVec3f(&sZeroVec, &sOwnerHeadPos); + + if (D_80B7A688 == 1) { + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gFishingOwnerHatDL)); + } else if (D_80B7A688 == 2) { + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gFishingOwnerHairDL)); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 9142); + } +} + +static void* sFishingOwnerEyeTexs[] = { + gFishingOwnerEyeOpenTex, + gFishingOwnerEyeHalfTex, + gFishingOwnerEyeClosedTex, +}; + +void Fishing_DrawOwner(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + Fishing* this = (Fishing*)thisx; + Input* input = &globalCtx->state.input[0]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 9156); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + if ((this->actor.projectedPos.z < 1500.0f) && + (fabsf(this->actor.projectedPos.x) < (100.0f + this->actor.projectedPos.z))) { + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sFishingOwnerEyeTexs[this->unk_160])); + + SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, + this->skelAnime.dListCount, Fishing_OwnerOverrideLimbDraw, Fishing_OwnerPostLimbDraw, + this); + } + + Fishing_DrawPondProps(globalCtx); + Fishing_DrawEffects(globalCtx->specialEffects, globalCtx); + Fishing_DrawGroupFishes(globalCtx); + Fishing_DrawStreamSplash(globalCtx); + + if (D_80B7E0A6 != 0) { + D_80B7E0A6--; + + if (D_80B7E0A6 == 0) { + if (sLinkAge != 1) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KAKARIKO_ADULT); + } else { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KAKARIKO_KID); + } + + if (sLinkAge != 1) { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KAKARIKO_ADULT); + } else { + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_KAKARIKO_KID); + } + } + } + + if ((D_80B7E0AC != 0) && D_80B7A674) { + Fishing_DrawRod(globalCtx); + Fishing_UpdateLinePos(sReelLinePos); + Fishing_UpdateLine(globalCtx, &sRodTipPos, sReelLinePos, sReelLineRot, sReelLineUnk); + Fishing_DrawLureAndLine(globalCtx, sReelLinePos, sReelLineRot); + + D_80B7A6C4 = input->rel.stick_x; + D_80B7A6C8 = input->rel.stick_y; + } + + D_80B7A674 = true; + + Matrix_Translate(130.0f, 40.0f, 1300.0f, MTXMODE_NEW); + Matrix_Scale(0.08f, 0.12f, 0.14f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 9297), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_fishing.c", 9298), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gFishingAquariumBottomDL); + gSPDisplayList(POLY_XLU_DISP++, gFishingAquariumContainerDL); + + if ((D_80B7E0AC != 0) && (D_80B7E0B6 == 2)) { + Fishing_DrawSinkingLure(globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_fishing.c", 9305); +} diff --git a/soh/src/overlays/actors/ovl_Fishing/z_fishing.h b/soh/src/overlays/actors/ovl_Fishing/z_fishing.h new file mode 100644 index 000000000..58483652c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Fishing/z_fishing.h @@ -0,0 +1,63 @@ +#ifndef Z_FISHING_H +#define Z_FISHING_H + +#include "ultra64.h" +#include "global.h" + +struct Fishing; + +typedef struct Fishing { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[0x004]; + /* 0x0150 */ u8 unk_150; + /* 0x0151 */ u8 unk_151; + /* 0x0152 */ u8 unk_152; + /* 0x0154 */ s16 unk_154; + /* 0x0156 */ u8 unk_156; + /* 0x0157 */ u8 unk_157; + /* 0x0158 */ s16 unk_158; + /* 0x015A */ s16 unk_15A; + /* 0x015C */ s16 unk_15C; + /* 0x015E */ s16 unk_15E; + /* 0x0160 */ s16 unk_160; + /* 0x0162 */ s16 unk_162; + /* 0x0164 */ s16 unk_164; + /* 0x0166 */ s16 unk_166; + /* 0x0168 */ s16 unk_168; + /* 0x016A */ s16 unk_16A; + /* 0x016C */ s16 unk_16C; + /* 0x016E */ s16 unk_16E; + /* 0x0170 */ s16 unk_170; + /* 0x0172 */ s16 unk_172; + /* 0x0174 */ s16 unk_174; + /* 0x0176 */ s16 unk_176; + /* 0x0178 */ s16 unk_178; + /* 0x017A */ s16 unk_17A[4]; + /* 0x0184 */ f32 unk_184; + /* 0x0188 */ f32 unk_188; + /* 0x018C */ f32 unk_18C; + /* 0x0190 */ f32 unk_190; + /* 0x0194 */ f32 unk_194; + /* 0x0198 */ f32 unk_198; + /* 0x019C */ f32 unk_19C; + /* 0x01A0 */ s16 unk_1A0; + /* 0x01A2 */ s16 unk_1A2; + /* 0x01A4 */ s16 unk_1A4; + /* 0x01A8 */ f32 unk_1A8; + /* 0x01AC */ f32 unk_1AC; + /* 0x01B0 */ f32 unk_1B0; + /* 0x01B4 */ Vec3f unk_1B4; + /* 0x01C0 */ Vec3f fishMouthPos; + /* 0x01CC */ s16 unk_1CC[3]; + /* 0x01D2 */ u8 unk_1D2; + /* 0x01D3 */ u8 unk_1D3; + /* 0x01D4 */ u8 unk_1D4; + /* 0x01D5 */ u8 unk_1D5; + /* 0x01D8 */ SkelAnime skelAnime; + /* 0x021C */ LightNode* lightNode; + /* 0x0220 */ LightInfo lightInfo; + /* 0x0230 */ ColliderJntSph collider; + /* 0x0250 */ ColliderJntSphElement colliderElements[12]; +} Fishing; // size = 0x0550 + +#endif diff --git a/soh/src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.c b/soh/src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.c new file mode 100644 index 000000000..e9a5e9181 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.c @@ -0,0 +1,111 @@ +/* + * File: z_item_b_heart.c + * Overlay: ovl_Item_B_Heart + * Description: Heart Container + */ + +#include "z_item_b_heart.h" +#include "objects/object_gi_hearts/object_gi_hearts.h" + +#define FLAGS 0 + +void ItemBHeart_Init(Actor* thisx, GlobalContext* globalCtx); +void ItemBHeart_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ItemBHeart_Update(Actor* thisx, GlobalContext* globalCtx); +void ItemBHeart_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B85264(ItemBHeart* this, GlobalContext* globalCtx); + +const ActorInit Item_B_Heart_InitVars = { + ACTOR_ITEM_B_HEART, + ACTORCAT_MISC, + FLAGS, + OBJECT_GI_HEARTS, + sizeof(ItemBHeart), + (ActorFunc)ItemBHeart_Init, + (ActorFunc)ItemBHeart_Destroy, + (ActorFunc)ItemBHeart_Update, + (ActorFunc)ItemBHeart_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 0, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), +}; + +void ItemBHeart_Init(Actor* thisx, GlobalContext* globalCtx) { + ItemBHeart* this = (ItemBHeart*)thisx; + + if (Flags_GetCollectible(globalCtx, 0x1F)) { + Actor_Kill(&this->actor); + } else { + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.8f); + } +} + +void ItemBHeart_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void ItemBHeart_Update(Actor* thisx, GlobalContext* globalCtx) { + ItemBHeart* this = (ItemBHeart*)thisx; + + func_80B85264(this, globalCtx); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 0.0f, 0.0f, 0.0f, 4); + if (Actor_HasParent(&this->actor, globalCtx)) { + Flags_SetCollectible(globalCtx, 0x1F); + Actor_Kill(&this->actor); + } else { + func_8002F434(&this->actor, globalCtx, GI_HEART_CONTAINER_2, 30.0f, 40.0f); + } +} + +void func_80B85264(ItemBHeart* this, GlobalContext* globalCtx) { + f32 yOffset; + + this->unk_164++; + yOffset = (Math_SinS(this->unk_164 * 0x60C) * 5.0f) + 20.0f; + Math_ApproachF(&this->actor.world.pos.y, this->actor.home.pos.y + yOffset, 0.1f, this->unk_158); + Math_ApproachF(&this->unk_158, 2.0f, 1.0f, 0.1f); + this->actor.shape.rot.y += 0x400; + + Math_ApproachF(&this->actor.scale.x, 0.4f, 0.1f, 0.01f); + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; +} + +void ItemBHeart_Draw(Actor* thisx, GlobalContext* globalCtx) { + ItemBHeart* this = (ItemBHeart*)thisx; + Actor* actorIt; + u8 flag = false; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_item_b_heart.c", 506); + + actorIt = globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + + while (actorIt != NULL) { + if ((actorIt->id == ACTOR_DOOR_WARP1) && (actorIt->projectedPos.z > this->actor.projectedPos.z)) { + flag = true; + break; + } + actorIt = actorIt->next; + } + + if (flag) { + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_item_b_heart.c", 551), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gGiHeartBorderDL); + gSPDisplayList(POLY_XLU_DISP++, gGiHeartContainerDL); + } else { + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_item_b_heart.c", 557), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gGiHeartBorderDL); + gSPDisplayList(POLY_OPA_DISP++, gGiHeartContainerDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_item_b_heart.c", 561); +} diff --git a/soh/src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h b/soh/src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h new file mode 100644 index 000000000..432a7ef49 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_B_Heart/z_item_b_heart.h @@ -0,0 +1,18 @@ +#ifndef Z_ITEM_B_HEART_H +#define Z_ITEM_B_HEART_H + +#include "ultra64.h" +#include "global.h" + +struct ItemBHeart; + +typedef struct ItemBHeart { + /* 0x0000 */ Actor actor; + /* 0x014C */ char unk_14C[0xC]; + /* 0x0158 */ f32 unk_158; + /* 0x015C */ char unk_15C[0x8]; + /* 0x0164 */ s16 unk_164; + /* 0x0166 */ char unk_166[0x6]; +} ItemBHeart; // size = 0x016C + +#endif diff --git a/soh/src/overlays/actors/ovl_Item_Etcetera/z_item_etcetera.c b/soh/src/overlays/actors/ovl_Item_Etcetera/z_item_etcetera.c new file mode 100644 index 000000000..e66d38632 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Etcetera/z_item_etcetera.c @@ -0,0 +1,218 @@ +/* + * File: z_item_etcetera.c + * Overlay: ovl_Item_Etcetera + * Description: Collectible Items + */ + +#include "z_item_etcetera.h" + +#define FLAGS ACTOR_FLAG_4 + +void ItemEtcetera_Init(Actor* thisx, GlobalContext* globalCtx); +void ItemEtcetera_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ItemEtcetera_Update(Actor* thisx, GlobalContext* globalCtx); +void ItemEtcetera_DrawThroughLens(Actor* thisx, GlobalContext* globalCtx); +void ItemEtcetera_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B857D0(ItemEtcetera* this, GlobalContext* globalCtx); +void func_80B85824(ItemEtcetera* this, GlobalContext* globalCtx); +void func_80B858B4(ItemEtcetera* this, GlobalContext* globalCtx); +void ItemEtcetera_SpawnSparkles(ItemEtcetera* this, GlobalContext* globalCtx); +void ItemEtcetera_MoveFireArrowDown(ItemEtcetera* this, GlobalContext* globalCtx); +void func_80B85B28(ItemEtcetera* this, GlobalContext* globalCtx); +void ItemEtcetera_UpdateFireArrow(ItemEtcetera* this, GlobalContext* globalCtx); + +const ActorInit Item_Etcetera_InitVars = { + ACTOR_ITEM_ETCETERA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ItemEtcetera), + (ActorFunc)ItemEtcetera_Init, + (ActorFunc)ItemEtcetera_Destroy, + (ActorFunc)ItemEtcetera_Update, + NULL, + NULL, +}; + +static s16 sObjectIds[] = { + OBJECT_GI_BOTTLE, OBJECT_GI_BOTTLE_LETTER, OBJECT_GI_SHIELD_2, OBJECT_GI_ARROWCASE, OBJECT_GI_SCALE, + OBJECT_GI_SCALE, OBJECT_GI_KEY, OBJECT_GI_M_ARROW, OBJECT_GI_RUPY, OBJECT_GI_RUPY, + OBJECT_GI_RUPY, OBJECT_GI_RUPY, OBJECT_GI_HEARTS, OBJECT_GI_KEY, +}; + +// Indexes passed to the item table in z_draw.c +static s16 sDrawItemIndexes[] = { + GID_BOTTLE, GID_LETTER_RUTO, GID_SHIELD_HYLIAN, GID_QUIVER_40, GID_SCALE_SILVER, + GID_SCALE_GOLDEN, GID_KEY_SMALL, GID_ARROW_FIRE, GID_RUPEE_GREEN, GID_RUPEE_BLUE, + GID_RUPEE_RED, GID_RUPEE_PURPLE, GID_HEART_PIECE, GID_KEY_SMALL, +}; + +static s16 sGetItemIds[] = { + GI_BOTTLE, GI_LETTER_RUTO, GI_SHIELD_HYLIAN, GI_QUIVER_40, GI_SCALE_SILVER, GI_SCALE_GOLD, GI_KEY_SMALL, + GI_ARROW_FIRE, GI_NONE, GI_NONE, GI_NONE, GI_NONE, GI_NONE, GI_NONE, +}; + +void ItemEtcetera_SetupAction(ItemEtcetera* this, ItemEtceteraActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ItemEtcetera_Init(Actor* thisx, GlobalContext* globalCtx) { + ItemEtcetera* this = (ItemEtcetera*)thisx; + s32 pad; + s32 type; + s32 objBankIndex; + + type = this->actor.params & 0xFF; + osSyncPrintf("no = %d\n", type); + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectIds[type]); + osSyncPrintf("bank_ID = %d\n", objBankIndex); + if (objBankIndex < 0) { + ASSERT(0, "0", "../z_item_etcetera.c", 241); + } else { + this->objBankIndex = objBankIndex; + } + this->giDrawId = sDrawItemIndexes[type]; + this->getItemId = sGetItemIds[type]; + this->futureActionFunc = func_80B85824; + this->drawFunc = ItemEtcetera_Draw; + Actor_SetScale(&this->actor, 0.25f); + ItemEtcetera_SetupAction(this, func_80B857D0); + switch (type) { + case ITEM_ETC_LETTER: + Actor_SetScale(&this->actor, 0.5f); + this->futureActionFunc = func_80B858B4; + if (gSaveContext.eventChkInf[3] & 2) { + Actor_Kill(&this->actor); + } + break; + case ITEM_ETC_ARROW_FIRE: + this->futureActionFunc = ItemEtcetera_UpdateFireArrow; + Actor_SetScale(&this->actor, 0.5f); + this->actor.draw = NULL; + this->actor.shape.yOffset = 50.0f; + break; + case ITEM_ETC_RUPEE_GREEN_CHEST_GAME: + case ITEM_ETC_RUPEE_BLUE_CHEST_GAME: + case ITEM_ETC_RUPEE_RED_CHEST_GAME: + case ITEM_ETC_RUPEE_PURPLE_CHEST_GAME: + case ITEM_ETC_HEART_PIECE_CHEST_GAME: + case ITEM_ETC_KEY_SMALL_CHEST_GAME: + Actor_SetScale(&this->actor, 0.5f); + this->futureActionFunc = func_80B85B28; + this->drawFunc = ItemEtcetera_DrawThroughLens; + this->actor.world.pos.y += 15.0f; + break; + } +} + +void ItemEtcetera_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_80B857D0(ItemEtcetera* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex)) { + this->actor.objBankIndex = this->objBankIndex; + this->actor.draw = this->drawFunc; + this->actionFunc = this->futureActionFunc; + } +} + +void func_80B85824(ItemEtcetera* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + if ((this->actor.params & 0xFF) == 1) { + gSaveContext.eventChkInf[3] |= 2; + Flags_SetSwitch(globalCtx, 0xB); + } + Actor_Kill(&this->actor); + } else { + func_8002F434(&this->actor, globalCtx, this->getItemId, 30.0f, 50.0f); + } +} + +void func_80B858B4(ItemEtcetera* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + if ((this->actor.params & 0xFF) == 1) { + gSaveContext.eventChkInf[3] |= 2; + Flags_SetSwitch(globalCtx, 0xB); + } + Actor_Kill(&this->actor); + } else { + if (0) {} // Necessary to match + func_8002F434(&this->actor, globalCtx, this->getItemId, 30.0f, 50.0f); + if ((globalCtx->gameplayFrames & 0xD) == 0) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 0.0f, 0.0f, 10.0f, 0.13f); + } + } +} + +void ItemEtcetera_SpawnSparkles(ItemEtcetera* this, GlobalContext* globalCtx) { + static Vec3f velocity = { 0.0f, 0.2f, 0.0f }; + static Vec3f accel = { 0.0f, 0.05f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 0 }; + static Color_RGBA8 envColor = { 255, 50, 50, 0 }; + Vec3f pos; + + velocity.x = Rand_CenteredFloat(3.0f); + velocity.z = Rand_CenteredFloat(3.0f); + velocity.y = -0.05f; + accel.y = -0.025f; + pos.x = Rand_CenteredFloat(12.0f) + this->actor.world.pos.x; + pos.y = (Rand_ZeroOne() * 6.0f) + this->actor.world.pos.y; + pos.z = Rand_CenteredFloat(12.0f) + this->actor.world.pos.z; + EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &velocity, &accel, &primColor, &envColor, 5000, 16); +} + +void ItemEtcetera_MoveFireArrowDown(ItemEtcetera* this, GlobalContext* globalCtx) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 0.0f, 5); + Actor_MoveForward(&this->actor); + if (!(this->actor.bgCheckFlags & 1)) { + ItemEtcetera_SpawnSparkles(this, globalCtx); + } + this->actor.shape.rot.y += 0x400; + func_80B85824(this, globalCtx); +} + +void func_80B85B28(ItemEtcetera* this, GlobalContext* globalCtx) { + if (Flags_GetTreasure(globalCtx, (this->actor.params >> 8) & 0x1F)) { + Actor_Kill(&this->actor); + } +} + +void ItemEtcetera_UpdateFireArrow(ItemEtcetera* this, GlobalContext* globalCtx) { + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.npcActions[0] != NULL)) { + LOG_NUM("(game_play->demo_play.npcdemopnt[0]->dousa)", globalCtx->csCtx.npcActions[0]->action, + "../z_item_etcetera.c", 441); + if (globalCtx->csCtx.npcActions[0]->action == 2) { + this->actor.draw = ItemEtcetera_Draw; + this->actor.gravity = -0.1f; + this->actor.minVelocityY = -4.0f; + this->actionFunc = ItemEtcetera_MoveFireArrowDown; + } + } else { + this->actor.gravity = -0.1f; + this->actor.minVelocityY = -4.0f; + this->actionFunc = ItemEtcetera_MoveFireArrowDown; + } +} + +void ItemEtcetera_Update(Actor* thisx, GlobalContext* globalCtx) { + ItemEtcetera* this = (ItemEtcetera*)thisx; + this->actionFunc(this, globalCtx); +} + +void ItemEtcetera_DrawThroughLens(Actor* thisx, GlobalContext* globalCtx) { + ItemEtcetera* this = (ItemEtcetera*)thisx; + if (globalCtx->actorCtx.unk_03 != 0) { + func_8002EBCC(&this->actor, globalCtx, 0); + func_8002ED80(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, this->giDrawId); + } +} + +void ItemEtcetera_Draw(Actor* thisx, GlobalContext* globalCtx) { + ItemEtcetera* this = (ItemEtcetera*)thisx; + + func_8002EBCC(&this->actor, globalCtx, 0); + func_8002ED80(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, this->giDrawId); +} diff --git a/soh/src/overlays/actors/ovl_Item_Etcetera/z_item_etcetera.h b/soh/src/overlays/actors/ovl_Item_Etcetera/z_item_etcetera.h new file mode 100644 index 000000000..d72c64ea9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Etcetera/z_item_etcetera.h @@ -0,0 +1,38 @@ +#ifndef Z_ITEM_ETC_H +#define Z_ITEM_ETC_H + +#include "ultra64.h" +#include "global.h" + +struct ItemEtcetera; + +typedef void (*ItemEtceteraActionFunc)(struct ItemEtcetera*, GlobalContext*); + +typedef struct ItemEtcetera { + /* 0x0000 */ Actor actor; + /* 0x014C */ ItemEtceteraActionFunc futureActionFunc; + /* 0x0150 */ s16 giDrawId; + /* 0x0152 */ s16 getItemId; + /* 0x0154 */ u8 objBankIndex; + /* 0x0158 */ ActorFunc drawFunc; + /* 0x015C */ ItemEtceteraActionFunc actionFunc; +} ItemEtcetera; // size = 0x0160 + +typedef enum { + /* 0x00 */ ITEM_ETC_BOTTLE, + /* 0x01 */ ITEM_ETC_LETTER, + /* 0x02 */ ITEM_ETC_SHIELD_HYLIAN, + /* 0x03 */ ITEM_ETC_QUIVER, + /* 0x04 */ ITEM_ETC_SCALE_SILVER, + /* 0x05 */ ITEM_ETC_SCALE_GOLD, + /* 0x06 */ ITEM_ETC_KEY_SMALL, + /* 0x07 */ ITEM_ETC_ARROW_FIRE, + /* 0x08 */ ITEM_ETC_RUPEE_GREEN_CHEST_GAME, + /* 0x09 */ ITEM_ETC_RUPEE_BLUE_CHEST_GAME, + /* 0x0A */ ITEM_ETC_RUPEE_RED_CHEST_GAME, + /* 0x0B */ ITEM_ETC_RUPEE_PURPLE_CHEST_GAME, + /* 0x0C */ ITEM_ETC_HEART_PIECE_CHEST_GAME, + /* 0x0D */ ITEM_ETC_KEY_SMALL_CHEST_GAME +} ItemEtceteraType; + +#endif diff --git a/soh/src/overlays/actors/ovl_Item_Inbox/z_item_inbox.c b/soh/src/overlays/actors/ovl_Item_Inbox/z_item_inbox.c new file mode 100644 index 000000000..8f68e811a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Inbox/z_item_inbox.c @@ -0,0 +1,59 @@ +/* + * File: z_item_inbox.c + * Overlay: ovl_Item_Inbox + * Description: Zelda's magic effect when opening gates in castle collapse + */ + +#include "z_item_inbox.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void ItemInbox_Init(Actor* thisx, GlobalContext* globalCtx); +void ItemInbox_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ItemInbox_Update(Actor* thisx, GlobalContext* globalCtx); +void ItemInbox_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ItemInbox_Wait(ItemInbox* this, GlobalContext* globalCtx); + +const ActorInit Item_Inbox_InitVars = { + ACTOR_ITEM_INBOX, + ACTORCAT_NPC, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ItemInbox), + (ActorFunc)ItemInbox_Init, + (ActorFunc)ItemInbox_Destroy, + (ActorFunc)ItemInbox_Update, + (ActorFunc)ItemInbox_Draw, + NULL, +}; + +void ItemInbox_Init(Actor* thisx, GlobalContext* globalCtx) { + ItemInbox* this = (ItemInbox*)thisx; + + this->actionFunc = ItemInbox_Wait; + Actor_SetScale(&this->actor, 0.2); +} + +void ItemInbox_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void ItemInbox_Wait(ItemInbox* this, GlobalContext* globalCtx) { + if (Flags_GetTreasure(globalCtx, (this->actor.params >> 8) & 0x1F)) { + Actor_Kill(&this->actor); + } +} + +void ItemInbox_Update(Actor* thisx, GlobalContext* globalCtx) { + ItemInbox* this = (ItemInbox*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ItemInbox_Draw(Actor* thisx, GlobalContext* globalCtx) { + ItemInbox* this = (ItemInbox*)thisx; + + func_8002EBCC(&this->actor, globalCtx, 0); + func_8002ED80(&this->actor, globalCtx, 0); + GetItem_Draw(globalCtx, this->actor.params & 0xFF); +} diff --git a/soh/src/overlays/actors/ovl_Item_Inbox/z_item_inbox.h b/soh/src/overlays/actors/ovl_Item_Inbox/z_item_inbox.h new file mode 100644 index 000000000..0845653ab --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Inbox/z_item_inbox.h @@ -0,0 +1,16 @@ +#ifndef Z_ITEM_INBOX_H +#define Z_ITEM_INBOX_H + +#include "ultra64.h" +#include "global.h" + +struct ItemInbox; + +typedef void (*ItemInboxActionFunc)(struct ItemInbox*, GlobalContext*); + +typedef struct ItemInbox { + /* 0x0000 */ Actor actor; + /* 0x014C */ ItemInboxActionFunc actionFunc; +} ItemInbox; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Item_Ocarina/z_item_ocarina.c b/soh/src/overlays/actors/ovl_Item_Ocarina/z_item_ocarina.c new file mode 100644 index 000000000..c44ea58e7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Ocarina/z_item_ocarina.c @@ -0,0 +1,204 @@ +/* + * File: z_item_ocarina.c + * Overlay: ovl_Item_Ocarina + * Description: Ocarina of Time + */ + +#include "z_item_ocarina.h" +#include "scenes/overworld/spot00/spot00_scene.h" + +#define FLAGS ACTOR_FLAG_4 + +void ItemOcarina_Init(Actor* thisx, GlobalContext* globalCtx); +void ItemOcarina_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ItemOcarina_Update(Actor* thisx, GlobalContext* globalCtx); +void ItemOcarina_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ItemOcarina_GetThrown(ItemOcarina* this, GlobalContext* globalCtx); +void ItemOcarina_Fly(ItemOcarina* this, GlobalContext* globalCtx); +void ItemOcarina_WaitInWater(ItemOcarina* this, GlobalContext* globalCtx); +void ItemOcarina_StartSoTCutscene(ItemOcarina* this, GlobalContext* globalCtx); +void func_80B864EC(ItemOcarina* this, GlobalContext* globalCtx); +void func_80B865E0(ItemOcarina* this, GlobalContext* globalCtx); +void ItemOcarina_DoNothing(ItemOcarina* this, GlobalContext* globalCtx); + +const ActorInit Item_Ocarina_InitVars = { + ACTOR_ITEM_OCARINA, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GI_OCARINA, + sizeof(ItemOcarina), + (ActorFunc)ItemOcarina_Init, + (ActorFunc)ItemOcarina_Destroy, + (ActorFunc)ItemOcarina_Update, + (ActorFunc)ItemOcarina_Draw, + NULL, +}; + +void ItemOcarina_SetupAction(ItemOcarina* this, ItemOcarinaActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ItemOcarina_Init(Actor* thisx, GlobalContext* globalCtx) { + ItemOcarina* this = (ItemOcarina*)thisx; + s32 params = thisx->params; + + ActorShape_Init(&this->actor.shape, 0, 0, 0); + Actor_SetScale(&this->actor, 0.1f); + + switch (params) { + case 0: + ItemOcarina_SetupAction(this, ItemOcarina_GetThrown); + break; + case 1: + ItemOcarina_SetupAction(this, func_80B865E0); + break; + case 2: + ItemOcarina_SetupAction(this, ItemOcarina_DoNothing); + break; + case 3: + ItemOcarina_SetupAction(this, ItemOcarina_WaitInWater); + if (!(gSaveContext.eventChkInf[8] & 1) || (gSaveContext.eventChkInf[4] & 8)) { + Actor_Kill(thisx); + return; + } + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ELF_MSG2, 299.0f, -140.0f, 884.0f, 0, 4, 1, 0x3800); + Actor_SetScale(thisx, 0.2f); + break; + default: + Actor_Kill(thisx); + return; + } + + LOG_NUM("no", params, "../z_item_ocarina.c", 210); + this->spinRotOffset = 0x400; +} + +void ItemOcarina_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void ItemOcarina_Fly(ItemOcarina* this, GlobalContext* globalCtx) { + Vec3f ripplePos; + + func_8002D7EC(&this->actor); + this->actor.shape.rot.x += this->spinRotOffset * 2; + this->actor.shape.rot.y += this->spinRotOffset * 3; + + if (this->actor.minVelocityY < this->actor.velocity.y) { + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + } + + if (globalCtx->csCtx.frames == 881) { + this->actor.world.pos.x = 250.0f; + this->actor.world.pos.y = 60.0f; + this->actor.world.pos.z = 1075.0f; + this->actor.velocity.x = 1.0f; + this->actor.velocity.y = -5.0f; + this->actor.velocity.z = -7.0f; + } + + if (globalCtx->csCtx.frames == 897) { + EffectSsGRipple_Spawn(globalCtx, &this->actor.world.pos, 100, 500, 0); + EffectSsGSplash_Spawn(globalCtx, &this->actor.world.pos, 0, 0, 1, 0); + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.gravity = -0.1f; + this->actor.minVelocityY = -0.5f; + this->spinRotOffset = 0; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_BOMB_DROP_WATER); + } + + // landed in water + if (globalCtx->csCtx.frames == 906) { + ripplePos.x = 274.0f; + ripplePos.y = -60.0f; + ripplePos.z = 907.0f; + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 100, 500, 0); + } +} + +void ItemOcarina_GetThrown(ItemOcarina* this, GlobalContext* globalCtx) { + this->actor.gravity = -0.3f; + this->actor.minVelocityY = -5.0f; + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 6.0f; + this->actor.velocity.z = 0.0f; + ItemOcarina_SetupAction(this, ItemOcarina_Fly); +} + +void func_80B864EC(ItemOcarina* this, GlobalContext* globalCtx) { + func_8002D7EC(&this->actor); + this->actor.shape.rot.x += this->spinRotOffset * 2; + this->actor.shape.rot.y += this->spinRotOffset * 3; + + if (this->actor.minVelocityY < this->actor.velocity.y) { + this->actor.velocity.y += this->actor.gravity; + + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + } + + if (globalCtx->csCtx.frames == 220) { + this->actor.world.pos.x = 144.0f; + this->actor.world.pos.y = 80.0f; + this->actor.world.pos.z = 1686.0f; + this->actor.velocity.x = 1.0f; + this->actor.velocity.y = 2.0f; + this->actor.velocity.z = -7.0f; + this->actor.gravity = -0.15f; + this->actor.minVelocityY = -5.0f; + } +} + +void func_80B865E0(ItemOcarina* this, GlobalContext* globalCtx) { + this->actor.gravity = -0.3f; + this->actor.minVelocityY = -5.0f; + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 4.0f; + this->actor.velocity.z = 6.0f; + ItemOcarina_SetupAction(this, func_80B864EC); +} + +void ItemOcarina_DoNothing(ItemOcarina* this, GlobalContext* globalCtx) { +} + +void ItemOcarina_StartSoTCutscene(ItemOcarina* this, GlobalContext* globalCtx) { + if (Actor_TextboxIsClosing(&this->actor, globalCtx)) { + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gHyruleFieldZeldaSongOfTimeCs); + gSaveContext.cutsceneTrigger = 1; + } +} + +void ItemOcarina_WaitInWater(ItemOcarina* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + gSaveContext.eventChkInf[4] |= 8; + Flags_SetSwitch(globalCtx, 3); + this->actionFunc = ItemOcarina_StartSoTCutscene; + this->actor.draw = NULL; + } else { + func_8002F434(&this->actor, globalCtx, GI_OCARINA_OOT, 30.0f, 50.0f); + + if ((globalCtx->gameplayFrames & 13) == 0) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 0.0f, 0.0f, 10.0f, 0.13f); + } + } +} + +void ItemOcarina_Update(Actor* thisx, GlobalContext* globalCtx) { + ItemOcarina* this = (ItemOcarina*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ItemOcarina_Draw(Actor* thisx, GlobalContext* globalCtx) { + ItemOcarina* this = (ItemOcarina*)thisx; + + func_8002EBCC(thisx, globalCtx, 0); + func_8002ED80(thisx, globalCtx, 0); + GetItem_Draw(globalCtx, GID_OCARINA_TIME); +} diff --git a/soh/src/overlays/actors/ovl_Item_Ocarina/z_item_ocarina.h b/soh/src/overlays/actors/ovl_Item_Ocarina/z_item_ocarina.h new file mode 100644 index 000000000..f653ae443 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Ocarina/z_item_ocarina.h @@ -0,0 +1,17 @@ +#ifndef Z_ITEM_OCARINA_H +#define Z_ITEM_OCARINA_H + +#include "ultra64.h" +#include "global.h" + +struct ItemOcarina; + +typedef void (*ItemOcarinaActionFunc)(struct ItemOcarina*, GlobalContext*); + +typedef struct ItemOcarina { + /* 0x0000 */ Actor actor; + /* 0x014C */ ItemOcarinaActionFunc actionFunc; + /* 0x0150 */ s16 spinRotOffset; +} ItemOcarina; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Item_Shield/z_item_shield.c b/soh/src/overlays/actors/ovl_Item_Shield/z_item_shield.c new file mode 100644 index 000000000..ed0d23bcb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Shield/z_item_shield.c @@ -0,0 +1,230 @@ +/* + * File: z_item_shield.c + * Overlay: ovl_Item_Shield + * Description: Deku Shield + */ + +#include "vt.h" +#include "z_item_shield.h" +#include "objects/object_link_child/object_link_child.h" + +#define FLAGS ACTOR_FLAG_4 + +void ItemShield_Init(Actor* thisx, GlobalContext* globalCtx); +void ItemShield_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ItemShield_Update(Actor* thisx, GlobalContext* globalCtx); +void ItemShield_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B86F68(ItemShield* this, GlobalContext* globalCtx); +void func_80B86BC8(ItemShield* this, GlobalContext* globalCtx); + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000004, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 15, 15, 0, { 0, 0, 0 } }, +}; + +const ActorInit Item_Shield_InitVars = { + ACTOR_ITEM_SHIELD, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_LINK_CHILD, + sizeof(ItemShield), + (ActorFunc)ItemShield_Init, + (ActorFunc)ItemShield_Destroy, + (ActorFunc)ItemShield_Update, + (ActorFunc)ItemShield_Draw, + NULL, +}; + +static Color_RGBA8 unused = { 255, 255, 0, 255 }; +static Color_RGBA8 unused2 = { 255, 0, 0, 255 }; + +void ItemShield_SetupAction(ItemShield* this, ItemShieldActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ItemShield_Init(Actor* thisx, GlobalContext* globalCtx) { + ItemShield* this = (ItemShield*)thisx; + s32 i; + + this->timer = 0; + this->unk_19C = 0; + + switch (this->actor.params) { + case 0: + ActorShape_Init(&this->actor.shape, 1400.0f, NULL, 0.0f); + this->actor.shape.rot.x = 0x4000; + ItemShield_SetupAction(this, func_80B86BC8); + break; + + case 1: + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + ItemShield_SetupAction(this, func_80B86F68); + this->unk_19C |= 2; + for (i = 0; i < 8; i++) { + this->unk_19E[i] = 1 + 2 * i; + this->unk_1A8[i].x = Rand_CenteredFloat(10.0f); + this->unk_1A8[i].y = Rand_CenteredFloat(10.0f); + this->unk_1A8[i].z = Rand_CenteredFloat(10.0f); + } + break; + } + + Actor_SetScale(&this->actor, 0.01f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + osSyncPrintf(VT_FGCOL(GREEN) "Item_Shild %d \n" VT_RST, this->actor.params); +} + +void ItemShield_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ItemShield* this = (ItemShield*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void func_80B86AC8(ItemShield* this, GlobalContext* globalCtx) { + Actor_MoveForward(&this->actor); + if (Actor_HasParent(&this->actor, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + func_8002F434(&this->actor, globalCtx, GI_SHIELD_DEKU, 30.0f, 50.0f); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 0.0f, 5); + if (this->actor.bgCheckFlags & 1) { + this->timer--; + if (this->timer < 60) { + if (this->timer & 1) { + this->unk_19C |= 2; + } else { + this->unk_19C &= ~2; + } + } + if (this->timer == 0) { + Actor_Kill(&this->actor); + } + } +} + +void func_80B86BC8(ItemShield* this, GlobalContext* globalCtx) { + if (Actor_HasParent(&this->actor, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + func_8002F434(&this->actor, globalCtx, GI_SHIELD_DEKU, 30.0f, 50.0f); + if (this->collider.base.acFlags & AC_HIT) { + ItemShield_SetupAction(this, func_80B86AC8); + this->actor.velocity.y = 4.0f; + this->actor.minVelocityY = -4.0f; + this->actor.gravity = -0.8f; + this->actor.speedXZ = 0.0f; + this->timer = 160; + } else { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void func_80B86CA8(ItemShield* this, GlobalContext* globalCtx) { + static Vec3f D_80B871F4 = { 0.0f, 0.0f, 0.0f }; + static f32 D_80B87200[] = { 0.3f, 0.6f, 0.9f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 0.85f, 0.7f, 0.55f, 0.4f, 0.25f, 0.1f, 0.0f }; + static f32 D_80B87240[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.8f, + 0.6f, 0.4f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }; + s32 i; + s32 temp; + + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 10.0f, 10.0f, 0.0f, 5); + this->actor.shape.yOffset = ABS(Math_SinS(this->actor.shape.rot.x)) * 1500.0f; + + for (i = 0; i < 8; i++) { + temp = 15 - this->unk_19E[i]; + D_80B871F4.x = this->unk_1A8[i].x; + D_80B871F4.y = this->unk_1A8[i].y + (this->actor.shape.yOffset * 0.01f) + (D_80B87200[temp] * -10.0f * 0.2f); + D_80B871F4.z = this->unk_1A8[i].z; + EffectSsFireTail_SpawnFlame(globalCtx, &this->actor, &D_80B871F4, D_80B87200[temp] * 0.2f, -1, + D_80B87240[temp]); + if (this->unk_19E[i] != 0) { + this->unk_19E[i]--; + } else if (this->timer > 16) { + this->unk_19E[i] = 15; + this->unk_1A8[i].x = Rand_CenteredFloat(15.0f); + this->unk_1A8[i].y = Rand_CenteredFloat(10.0f); + this->unk_1A8[i].z = Rand_CenteredFloat(15.0f); + } + } + if (this->actor.bgCheckFlags & 1) { + this->unk_198 -= this->actor.shape.rot.x >> 1; + this->unk_198 -= this->unk_198 >> 2; + this->actor.shape.rot.x += this->unk_198; + if ((this->timer >= 8) && (this->timer < 24)) { + Actor_SetScale(&this->actor, (this->timer - 8) * 0.000625f); + } + if (this->timer != 0) { + this->timer--; + } else { + Actor_Kill(&this->actor); + } + } +} + +void func_80B86F68(ItemShield* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + MtxF* shield = &player->shieldMf; + + this->actor.world.pos.x = shield->xw; + this->actor.world.pos.y = shield->yw; + this->actor.world.pos.z = shield->zw; + this->unk_19C &= ~2; + + this->actor.shape.rot.y = Math_Atan2S(-shield->zz, -shield->xz); + this->actor.shape.rot.x = Math_Atan2S(-shield->yz, sqrtf(shield->zz * shield->zz + shield->xz * shield->xz)); + + if (ABS(this->actor.shape.rot.x) > 0x4000) { + this->unk_19C |= 1; + } + + ItemShield_SetupAction(this, func_80B86CA8); + + this->actor.velocity.y = 4.0; + this->actor.minVelocityY = -4.0; + this->actor.gravity = -0.8; + this->unk_198 = 0; + this->timer = 70; + this->actor.speedXZ = 0; +} + +void ItemShield_Update(Actor* thisx, GlobalContext* globalCtx) { + ItemShield* this = (ItemShield*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ItemShield_Draw(Actor* thisx, GlobalContext* globalCtx) { + ItemShield* this = (ItemShield*)thisx; + + if (!(this->unk_19C & 2)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_item_shield.c", 457); + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_item_shield.c", 460), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gLinkChildDekuShieldDL)); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_item_shield.c", 465); + } +} diff --git a/soh/src/overlays/actors/ovl_Item_Shield/z_item_shield.h b/soh/src/overlays/actors/ovl_Item_Shield/z_item_shield.h new file mode 100644 index 000000000..7daf0d013 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Item_Shield/z_item_shield.h @@ -0,0 +1,22 @@ +#ifndef Z_ITEM_SHIELD_H +#define Z_ITEM_SHIELD_H + +#include "ultra64.h" +#include "global.h" + +struct ItemShield; + +typedef void (*ItemShieldActionFunc)(struct ItemShield*, GlobalContext*); + +typedef struct ItemShield { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ s16 unk_198; + /* 0x019A */ s16 timer; + /* 0x019C */ s16 unk_19C; + /* 0x019E */ u8 unk_19E[8]; + /* 0x01A8 */ Vec3f unk_1A8[8]; + /* 0x0208 */ ItemShieldActionFunc actionFunc; +} ItemShield; // size = 0x020C + +#endif diff --git a/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c b/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c new file mode 100644 index 000000000..7d173f10b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c @@ -0,0 +1,284 @@ +/* + * File: z_magic_dark.c + * Overlay: ovl_Magic_Dark + * Description: Nayru's Love + */ + +#include "z_magic_dark.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void MagicDark_Init(Actor* thisx, GlobalContext* globalCtx); +void MagicDark_Destroy(Actor* thisx, GlobalContext* globalCtx); +void MagicDark_OrbUpdate(Actor* thisx, GlobalContext* globalCtx); +void MagicDark_OrbDraw(Actor* thisx, GlobalContext* globalCtx); +void MagicDark_DiamondUpdate(Actor* thisx, GlobalContext* globalCtx); +void MagicDark_DiamondDraw(Actor* thisx, GlobalContext* globalCtx); + +void MagicDark_DimLighting(GlobalContext* globalCtx, f32 intensity); + +const ActorInit Magic_Dark_InitVars = { + ACTOR_MAGIC_DARK, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(MagicDark), + (ActorFunc)MagicDark_Init, + (ActorFunc)MagicDark_Destroy, + (ActorFunc)MagicDark_OrbUpdate, + (ActorFunc)MagicDark_OrbDraw, + NULL, +}; + +#include "overlays/ovl_Magic_Dark/ovl_Magic_Dark.h" + +// unused +static Color_RGBA8 D_80B88B10[] = { { 50, 100, 150, 200 }, { 255, 200, 150, 100 } }; + +void MagicDark_Init(Actor* thisx, GlobalContext* globalCtx) { + MagicDark* this = (MagicDark*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if (!LINK_IS_ADULT) { + this->scale = 0.4f; + } else { + this->scale = 0.6f; + } + + thisx->world.pos = player->actor.world.pos; + Actor_SetScale(&this->actor, 0.0f); + thisx->room = -1; + + if (gSaveContext.nayrusLoveTimer != 0) { + thisx->update = MagicDark_DiamondUpdate; + thisx->draw = MagicDark_DiamondDraw; + thisx->scale.x = thisx->scale.z = this->scale * 1.6f; + thisx->scale.y = this->scale * 0.8f; + this->timer = 0; + this->primAlpha = 0; + } else { + this->timer = 0; + gSaveContext.nayrusLoveTimer = 0; + } +} + +void MagicDark_Destroy(Actor* thisx, GlobalContext* globalCtx) { + if (gSaveContext.nayrusLoveTimer == 0) { + func_800876C8(globalCtx); + } +} + +void MagicDark_DiamondUpdate(Actor* thisx, GlobalContext* globalCtx) { + MagicDark* this = (MagicDark*)thisx; + u8 phi_a0; + Player* player = GET_PLAYER(globalCtx); + s16 pad; + s16 nayrusLoveTimer = gSaveContext.nayrusLoveTimer; + s32 msgMode = globalCtx->msgCtx.msgMode; + + if (1) {} + + if ((msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) || (msgMode == MSGMODE_SONG_PLAYED)) { + Actor_Kill(thisx); + return; + } + + if (nayrusLoveTimer >= 1200) { + player->invincibilityTimer = 0; + gSaveContext.nayrusLoveTimer = 0; + Actor_Kill(thisx); + return; + } + + player->invincibilityTimer = -100; + thisx->scale.x = thisx->scale.z = this->scale; + + if (this->timer < 20) { + thisx->scale.x = thisx->scale.z = (1.6f - (this->timer * 0.03f)) * this->scale; + thisx->scale.y = ((this->timer * 0.01f) + 0.8f) * this->scale; + } else { + thisx->scale.x = thisx->scale.z = this->scale; + thisx->scale.y = this->scale; + } + + thisx->scale.x *= 1.3f; + thisx->scale.z *= 1.3f; + + phi_a0 = (this->timer < 20) ? (this->timer * 12) : 255; + + if (nayrusLoveTimer >= 1180) { + this->primAlpha = 15595 - (nayrusLoveTimer * 13); + if (nayrusLoveTimer & 1) { + this->primAlpha = this->primAlpha >> 1; + } + } else if (nayrusLoveTimer >= 1100) { + this->primAlpha = (u8)(nayrusLoveTimer << 7) + 127; + } else { + this->primAlpha = 255; + } + + if (this->primAlpha > phi_a0) { + this->primAlpha = phi_a0; + } + + thisx->world.rot.y += 0x3E8; + thisx->shape.rot.y = thisx->world.rot.y + Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)); + this->timer++; + gSaveContext.nayrusLoveTimer = nayrusLoveTimer + 1; + + if (nayrusLoveTimer < 1100) { + func_8002F974(thisx, NA_SE_PL_MAGIC_SOUL_NORMAL - SFX_FLAG); + } else { + func_8002F974(thisx, NA_SE_PL_MAGIC_SOUL_FLASH - SFX_FLAG); + } +} + +void MagicDark_DimLighting(GlobalContext* globalCtx, f32 intensity) { + s32 i; + f32 colorScale; + f32 fogScale; + + if (globalCtx->roomCtx.curRoom.unk_03 != 5) { + intensity = CLAMP_MIN(intensity, 0.0f); + intensity = CLAMP_MAX(intensity, 1.0f); + fogScale = intensity - 0.2f; + + if (intensity < 0.2f) { + fogScale = 0.0f; + } + + globalCtx->envCtx.adjFogNear = (850.0f - globalCtx->envCtx.lightSettings.fogNear) * fogScale; + + if (intensity == 0.0f) { + for (i = 0; i < ARRAY_COUNT(globalCtx->envCtx.adjFogColor); i++) { + globalCtx->envCtx.adjFogColor[i] = 0; + } + } else { + colorScale = intensity * 5.0f; + + if (colorScale > 1.0f) { + colorScale = 1.0f; + } + + for (i = 0; i < ARRAY_COUNT(globalCtx->envCtx.adjFogColor); i++) { + globalCtx->envCtx.adjFogColor[i] = -(s16)(globalCtx->envCtx.lightSettings.fogColor[i] * colorScale); + } + } + } +} + +void MagicDark_OrbUpdate(Actor* thisx, GlobalContext* globalCtx) { + MagicDark* this = (MagicDark*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + func_8002F974(&this->actor, NA_SE_PL_MAGIC_SOUL_BALL - SFX_FLAG); + if (this->timer < 35) { + MagicDark_DimLighting(globalCtx, this->timer * (1 / 45.0f)); + Math_SmoothStepToF(&thisx->scale.x, this->scale * (1 / 12.000001f), 0.05f, 0.01f, 0.0001f); + Actor_SetScale(&this->actor, thisx->scale.x); + } else if (this->timer < 55) { + Actor_SetScale(&this->actor, thisx->scale.x * 0.9f); + Math_SmoothStepToF(&this->orbOffset.y, player->bodyPartsPos[0].y, 0.5f, 3.0f, 1.0f); + if (this->timer > 48) { + MagicDark_DimLighting(globalCtx, (54 - this->timer) * 0.2f); + } + } else { + thisx->update = MagicDark_DiamondUpdate; + thisx->draw = MagicDark_DiamondDraw; + thisx->scale.x = thisx->scale.z = this->scale * 1.6f; + thisx->scale.y = this->scale * 0.8f; + this->timer = 0; + this->primAlpha = 0; + } + + this->timer++; +} + +void MagicDark_DiamondDraw(Actor* thisx, GlobalContext* globalCtx) { + MagicDark* this = (MagicDark*)thisx; + s32 pad; + u16 gameplayFrames = globalCtx->gameplayFrames; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_magic_dark.c", 525); + + func_80093D84(globalCtx->state.gfxCtx); + + { + Player* player = GET_PLAYER(globalCtx); + f32 heightDiff; + + this->actor.world.pos.x = player->bodyPartsPos[0].x; + this->actor.world.pos.z = player->bodyPartsPos[0].z; + heightDiff = player->bodyPartsPos[0].y - this->actor.world.pos.y; + if (heightDiff < -2.0f) { + this->actor.world.pos.y = player->bodyPartsPos[0].y + 2.0f; + } else if (heightDiff > 2.0f) { + this->actor.world.pos.y = player->bodyPartsPos[0].y - 2.0f; + } + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + Matrix_RotateY(this->actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_magic_dark.c", 553), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 255, 255, (s32)(this->primAlpha * 0.6f) & 0xFF); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, 128); + gSPDisplayList(POLY_XLU_DISP++, sDiamondMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, gameplayFrames * 2, gameplayFrames * -4, 32, 32, 1, + 0, gameplayFrames * -16, 64, 32)); + gSPDisplayList(POLY_XLU_DISP++, sDiamondModelDL); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_magic_dark.c", 570); +} + +void MagicDark_OrbDraw(Actor* thisx, GlobalContext* globalCtx) { + MagicDark* this = (MagicDark*)thisx; + Vec3f pos; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + f32 sp6C = globalCtx->state.frames & 0x1F; + + if (this->timer < 32) { + pos.x = (player->bodyPartsPos[12].x + player->bodyPartsPos[15].x) * 0.5f; + pos.y = (player->bodyPartsPos[12].y + player->bodyPartsPos[15].y) * 0.5f; + pos.z = (player->bodyPartsPos[12].z + player->bodyPartsPos[15].z) * 0.5f; + if (this->timer > 20) { + pos.y += (this->timer - 20) * 1.4f; + } + this->orbOffset = pos; + } else if (this->timer < 130) { + pos = this->orbOffset; + } else { + return; + } + + pos.x -= (this->actor.scale.x * 300.0f * Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx))) * + Math_CosS(Camera_GetCamDirPitch(GET_ACTIVE_CAM(globalCtx)))); + pos.y -= (this->actor.scale.x * 300.0f * Math_SinS(Camera_GetCamDirPitch(GET_ACTIVE_CAM(globalCtx)))); + pos.z -= (this->actor.scale.x * 300.0f * Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx))) * + Math_CosS(Camera_GetCamDirPitch(GET_ACTIVE_CAM(globalCtx)))); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_magic_dark.c", 619); + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 170, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 150, 255, 255); + Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_Push(); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_magic_dark.c", 632), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + Matrix_RotateZ(sp6C * (M_PI / 32), MTXMODE_APPLY); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + Matrix_Pop(); + Matrix_RotateZ(-sp6C * (M_PI / 32), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_magic_dark.c", 639), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_magic_dark.c", 643); +} diff --git a/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.h b/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.h new file mode 100644 index 000000000..3d7ef8229 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.h @@ -0,0 +1,18 @@ +#ifndef Z_MAGIC_DARK_H +#define Z_MAGIC_DARK_H + +#include "ultra64.h" +#include "global.h" + +struct MagicDark; + +typedef struct MagicDark { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 timer; + /* 0x014E */ u8 primAlpha; + /* 0x0150 */ Vec3f orbOffset; + /* 0x015C */ f32 scale; + /* 0x0160 */ char unk_160[0x4]; +} MagicDark; // size = 0x0164 + +#endif diff --git a/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c b/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c new file mode 100644 index 000000000..21ed9f585 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c @@ -0,0 +1,263 @@ +/* + * File: z_magic_fire.c + * Overlay: ovl_Magic_Fire + * Description: Din's Fire + */ + +#include "z_magic_fire.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void MagicFire_Init(Actor* thisx, GlobalContext* globalCtx); +void MagicFire_Destroy(Actor* thisx, GlobalContext* globalCtx); +void MagicFire_Update(Actor* thisx, GlobalContext* globalCtx); +void MagicFire_Draw(Actor* thisx, GlobalContext* globalCtx); + +void MagicFire_UpdateBeforeCast(Actor* thisx, GlobalContext* globalCtx); + +typedef enum { + /* 0x00 */ DF_ACTION_INITIALIZE, + /* 0x01 */ DF_ACTION_EXPAND_SLOWLY, + /* 0x02 */ DF_ACTION_STOP_EXPANDING, + /* 0x03 */ DF_ACTION_EXPAND_QUICKLY +} MagicFireAction; + +typedef enum { + /* 0x00 */ DF_SCREEN_TINT_NONE, + /* 0x01 */ DF_SCREEN_TINT_FADE_IN, + /* 0x02 */ DF_SCREEN_TINT_MAINTAIN, + /* 0x03 */ DF_SCREEN_TINT_FADE_OUT, + /* 0x04 */ DF_SCREEN_TINT_FINISHED +} MagicFireScreenTint; + +const ActorInit Magic_Fire_InitVars = { + ACTOR_MAGIC_FIRE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(MagicFire), + (ActorFunc)MagicFire_Init, + (ActorFunc)MagicFire_Destroy, + (ActorFunc)MagicFire_Update, + (ActorFunc)MagicFire_Draw, + NULL, +}; + +#include "overlays/ovl_Magic_Fire/ovl_Magic_Fire.h" + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00020000, 0x00, 0x01 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_NONE, + OCELEM_NONE, + }, + { 9, 9, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F(scale, 0, ICHAIN_STOP), +}; + +static u8 sVertexIndices[] = { + 3, 4, 5, 6, 7, 8, 9, 10, 16, 17, 18, 19, 25, 26, 27, 32, 35, 36, 37, 38, + 39, 45, 46, 47, 52, 53, 54, 59, 60, 61, 67, 68, 69, 70, 71, 72, 0, 1, 11, 12, + 14, 20, 21, 23, 28, 30, 33, 34, 40, 41, 43, 48, 50, 55, 57, 62, 64, 65, 73, 74, +}; + +void MagicFire_Init(Actor* thisx, GlobalContext* globalCtx) { + MagicFire* this = (MagicFire*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->action = 0; + this->screenTintBehaviour = 0; + this->actionTimer = 0; + this->alphaMultiplier = -3.0f; + Actor_SetScale(&this->actor, 0.0f); + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); + this->actor.update = MagicFire_UpdateBeforeCast; + this->actionTimer = 20; + this->actor.room = -1; +} + +void MagicFire_Destroy(Actor* thisx, GlobalContext* globalCtx) { + func_800876C8(globalCtx); +} + +void MagicFire_UpdateBeforeCast(Actor* thisx, GlobalContext* globalCtx) { + MagicFire* this = (MagicFire*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if ((globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) || + (globalCtx->msgCtx.msgMode == MSGMODE_SONG_PLAYED)) { + Actor_Kill(&this->actor); + return; + } + if (this->actionTimer > 0) { + this->actionTimer--; + } else { + this->actor.update = MagicFire_Update; + func_8002F7DC(&player->actor, NA_SE_PL_MAGIC_FIRE); + } + this->actor.world.pos = player->actor.world.pos; +} + +void MagicFire_Update(Actor* thisx, GlobalContext* globalCtx) { + MagicFire* this = (MagicFire*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 pad; + + if (1) {} + this->actor.world.pos = player->actor.world.pos; + if ((globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) || + (globalCtx->msgCtx.msgMode == MSGMODE_SONG_PLAYED)) { + Actor_Kill(&this->actor); + return; + } + if (this->action == DF_ACTION_EXPAND_SLOWLY) { + this->collider.info.toucher.damage = this->actionTimer + 25; + } else if (this->action == DF_ACTION_STOP_EXPANDING) { + this->collider.info.toucher.damage = this->actionTimer; + } + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.dim.radius = (this->actor.scale.x * 325.0f); + this->collider.dim.height = (this->actor.scale.y * 450.0f); + this->collider.dim.yShift = (this->actor.scale.y * -225.0f); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + switch (this->action) { + case DF_ACTION_INITIALIZE: + this->actionTimer = 30; + this->actor.scale.x = this->actor.scale.y = this->actor.scale.z = 0.0f; + this->actor.world.rot.x = this->actor.world.rot.y = this->actor.world.rot.z = 0; + this->actor.shape.rot.x = this->actor.shape.rot.y = this->actor.shape.rot.z = 0; + this->alphaMultiplier = 0.0f; + this->scalingSpeed = 0.08f; + this->action++; + break; + case DF_ACTION_EXPAND_SLOWLY: // Fire sphere slowly expands out of player for 30 frames + Math_StepToF(&this->alphaMultiplier, 1.0f, 1.0f / 30.0f); + if (this->actionTimer > 0) { + Math_SmoothStepToF(&this->actor.scale.x, 0.4f, this->scalingSpeed, 0.1f, 0.001f); + this->actor.scale.y = this->actor.scale.z = this->actor.scale.x; + } else { + this->actionTimer = 25; + this->action++; + } + break; + case DF_ACTION_STOP_EXPANDING: // Sphere stops expanding and maintains size for 25 frames + if (this->actionTimer <= 0) { + this->actionTimer = 15; + this->action++; + this->scalingSpeed = 0.05f; + } + break; + case DF_ACTION_EXPAND_QUICKLY: // Sphere beings to grow again and quickly expands out until killed + this->alphaMultiplier -= 8.0f / 119.00001f; + this->actor.scale.x += this->scalingSpeed; + this->actor.scale.y += this->scalingSpeed; + this->actor.scale.z += this->scalingSpeed; + if (this->alphaMultiplier <= 0.0f) { + this->action = 0; + Actor_Kill(&this->actor); + } + break; + } + switch (this->screenTintBehaviour) { + case DF_SCREEN_TINT_NONE: + if (this->screenTintBehaviourTimer <= 0) { + this->screenTintBehaviourTimer = 20; + this->screenTintBehaviour = DF_SCREEN_TINT_FADE_IN; + } + break; + case DF_SCREEN_TINT_FADE_IN: + this->screenTintIntensity = 1.0f - (this->screenTintBehaviourTimer / 20.0f); + if (this->screenTintBehaviourTimer <= 0) { + this->screenTintBehaviourTimer = 45; + this->screenTintBehaviour = DF_SCREEN_TINT_MAINTAIN; + } + break; + case DF_SCREEN_TINT_MAINTAIN: + if (this->screenTintBehaviourTimer <= 0) { + this->screenTintBehaviourTimer = 5; + this->screenTintBehaviour = DF_SCREEN_TINT_FADE_OUT; + } + break; + case DF_SCREEN_TINT_FADE_OUT: + this->screenTintIntensity = (this->screenTintBehaviourTimer / 5.0f); + if (this->screenTintBehaviourTimer <= 0) { + this->screenTintBehaviour = DF_SCREEN_TINT_FINISHED; + } + break; + } + if (this->actionTimer > 0) { + this->actionTimer--; + } + if (this->screenTintBehaviourTimer > 0) { + this->screenTintBehaviourTimer--; + } +} + +void MagicFire_Draw(Actor* thisx, GlobalContext* globalCtx) { + MagicFire* this = (MagicFire*)thisx; + s32 pad1; + u32 gameplayFrames = globalCtx->gameplayFrames; + s32 pad2; + s32 i; + u8 alpha; + + if (this->action > 0) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_magic_fire.c", 682); + POLY_XLU_DISP = func_800937C0(POLY_XLU_DISP); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (u8)(s32)(60 * this->screenTintIntensity), + (u8)(s32)(20 * this->screenTintIntensity), (u8)(s32)(0 * this->screenTintIntensity), + (u8)(s32)(120 * this->screenTintIntensity)); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_DISABLE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_DISABLE); + gDPFillRectangle(POLY_XLU_DISP++, 0, 0, 319, 239); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, 255, 200, 0, (u8)(this->alphaMultiplier * 255)); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, (u8)(this->alphaMultiplier * 255)); + Matrix_Scale(0.15f, 0.15f, 0.15f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_magic_fire.c", 715), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_XLU_DISP++); + gSPTexture(POLY_XLU_DISP++, 0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON); + gDPSetTextureLUT(POLY_XLU_DISP++, G_TT_NONE); + gDPLoadTextureBlock(POLY_XLU_DISP++, sTex, G_IM_FMT_I, G_IM_SIZ_8b, 64, 64, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, 6, 6, 15, G_TX_NOLOD); + gDPSetTile(POLY_XLU_DISP++, G_IM_FMT_I, G_IM_SIZ_8b, 8, 0, 1, 0, G_TX_NOMIRROR | G_TX_WRAP, 6, 14, + G_TX_NOMIRROR | G_TX_WRAP, 6, 14); + gDPSetTileSize(POLY_XLU_DISP++, 1, 0, 0, 252, 252); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (gameplayFrames * 2) % 512, + 511 - ((gameplayFrames * 5) % 512), 64, 64, 1, (gameplayFrames * 2) % 256, + 255 - ((gameplayFrames * 20) % 256), 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, sModelDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_magic_fire.c", 750); + + alpha = (s32)(this->alphaMultiplier * 255); + Vtx* vertices = ResourceMgr_LoadVtxByName(sSphereVtx); + for (i = 0; i < 36; i++) { + vertices[sVertexIndices[i]].n.a = alpha; + } + + alpha = (s32)(this->alphaMultiplier * 76); + for (i = 36; i < 60; i++) { + vertices[sVertexIndices[i]].n.a = alpha; + } + } +} diff --git a/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.h b/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.h new file mode 100644 index 000000000..4a9cb5188 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.h @@ -0,0 +1,21 @@ +#ifndef Z_MAGIC_FIRE_H +#define Z_MAGIC_FIRE_H + +#include "ultra64.h" +#include "global.h" + +struct MagicFire; + +typedef struct MagicFire { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ f32 alphaMultiplier; + /* 0x019C */ f32 screenTintIntensity; + /* 0x01A0 */ f32 scalingSpeed; + /* 0x01A4 */ s16 action; + /* 0x01A6 */ s16 screenTintBehaviour; + /* 0x01A8 */ s16 actionTimer; + /* 0x01AA */ s16 screenTintBehaviourTimer; +} MagicFire; // size = 0x01AC + +#endif diff --git a/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c b/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c new file mode 100644 index 000000000..c14397c3e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c @@ -0,0 +1,183 @@ +/* + * File: z_magic_wind.c + * Overlay: ovl_Magic_Wind + * Description: Farore's Wind + */ + +#include "z_magic_wind.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void MagicWind_Init(Actor* thisx, GlobalContext* globalCtx); +void MagicWind_Destroy(Actor* thisx, GlobalContext* globalCtx); +void MagicWind_Update(Actor* thisx, GlobalContext* globalCtx); +void MagicWind_Draw(Actor* thisx, GlobalContext* globalCtx); + +void MagicWind_Shrink(MagicWind* this, GlobalContext* globalCtx); +void MagicWind_WaitForTimer(MagicWind* this, GlobalContext* globalCtx); +void MagicWind_FadeOut(MagicWind* this, GlobalContext* globalCtx); +void MagicWind_WaitAtFullSize(MagicWind* this, GlobalContext* globalCtx); +void MagicWind_Grow(MagicWind* this, GlobalContext* globalCtx); + +const ActorInit Magic_Wind_InitVars = { + ACTOR_MAGIC_WIND, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(MagicWind), + (ActorFunc)MagicWind_Init, + (ActorFunc)MagicWind_Destroy, + (ActorFunc)MagicWind_Update, + (ActorFunc)MagicWind_Draw, + NULL, +}; + +#include "overlays/ovl_Magic_Wind/ovl_Magic_Wind.h" + +static u8 sAlphaUpdVals[] = { + 0x00, 0x03, 0x04, 0x07, 0x09, 0x0A, 0x0D, 0x0F, 0x11, 0x12, 0x15, 0x16, 0x19, 0x1B, 0x1C, 0x1F, 0x21, 0x23, +}; + +void MagicWind_SetupAction(MagicWind* this, MagicWindFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void MagicWind_Init(Actor* thisx, GlobalContext* globalCtx) { + MagicWind* this = (MagicWind*)thisx; + Player* player = GET_PLAYER(globalCtx); + + if (SkelCurve_Init(globalCtx, &this->skelCurve, &sSkel, &sAnim) == 0) { + // "Magic_Wind_Actor_ct (): Construct failed" + osSyncPrintf("Magic_Wind_Actor_ct():コンストラクト失敗\n"); + } + this->actor.room = -1; + switch (this->actor.params) { + case 0: + SkelCurve_SetAnim(&this->skelCurve, &sAnim, 0.0f, 60.0f, 0.0f, 1.0f); + this->timer = 29; + MagicWind_SetupAction(this, MagicWind_WaitForTimer); + break; + case 1: + SkelCurve_SetAnim(&this->skelCurve, &sAnim, 60.0f, 0.0f, 60.0f, -1.0f); + MagicWind_SetupAction(this, MagicWind_Shrink); + // "Means start" + LOG_STRING("表示開始", "../z_magic_wind.c", 486); + func_8002F7DC(&player->actor, NA_SE_PL_MAGIC_WIND_WARP); + break; + } +} + +void MagicWind_Destroy(Actor* thisx, GlobalContext* globalCtx) { + MagicWind* this = (MagicWind*)thisx; + SkelCurve_Destroy(globalCtx, &this->skelCurve); + func_800876C8(globalCtx); + // "wipe out" + LOG_STRING("消滅", "../z_magic_wind.c", 505); +} + +void MagicWind_UpdateAlpha(f32 alpha) { + s32 i; + + Vtx* vtx = ResourceMgr_LoadVtxByName(sCylinderVtx); + + for (i = 0; i < ARRAY_COUNT(sAlphaUpdVals); i++) { + vtx[sAlphaUpdVals[i]].n.a = alpha * 255.0f; + } +} + +void MagicWind_WaitForTimer(MagicWind* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (this->timer > 0) { + this->timer--; + return; + } + + // "Means start" + LOG_STRING("表示開始", "../z_magic_wind.c", 539); + func_8002F7DC(&player->actor, NA_SE_PL_MAGIC_WIND_NORMAL); + MagicWind_UpdateAlpha(1.0f); + MagicWind_SetupAction(this, MagicWind_Grow); + SkelCurve_Update(globalCtx, &this->skelCurve); +} + +void MagicWind_Grow(MagicWind* this, GlobalContext* globalCtx) { + if (SkelCurve_Update(globalCtx, &this->skelCurve)) { + MagicWind_SetupAction(this, MagicWind_WaitAtFullSize); + this->timer = 50; + } +} + +void MagicWind_WaitAtFullSize(MagicWind* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + } else { + MagicWind_SetupAction(this, MagicWind_FadeOut); + this->timer = 30; + } +} + +void MagicWind_FadeOut(MagicWind* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + MagicWind_UpdateAlpha((f32)this->timer * (1.0f / 30.0f)); + this->timer--; + } else { + Actor_Kill(&this->actor); + } +} + +void MagicWind_Shrink(MagicWind* this, GlobalContext* globalCtx) { + if (SkelCurve_Update(globalCtx, &this->skelCurve)) { + Actor_Kill(&this->actor); + } +} + +void MagicWind_Update(Actor* thisx, GlobalContext* globalCtx) { + MagicWind* this = (MagicWind*)thisx; + if (globalCtx->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK || + globalCtx->msgCtx.msgMode == MSGMODE_SONG_PLAYED) { + Actor_Kill(thisx); + return; + } + + this->actionFunc(this, globalCtx); +} + +s32 MagicWind_OverrideLimbDraw(GlobalContext* globalCtx, SkelAnimeCurve* skelCurve, s32 limbIndex, void* thisx) { + MagicWind* this = (MagicWind*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_magic_wind.c", 615); + + if (limbIndex == 1) { + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (globalCtx->state.frames * 9) & 0xFF, + 0xFF - ((globalCtx->state.frames * 0xF) & 0xFF), 0x40, 0x40, 1, + (globalCtx->state.frames * 0xF) & 0xFF, + 0xFF - ((globalCtx->state.frames * 0x1E) & 0xFF), 0x40, 0x40)); + + } else if (limbIndex == 2) { + gSPSegment(POLY_XLU_DISP++, 9, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, (globalCtx->state.frames * 3) & 0xFF, + 0xFF - ((globalCtx->state.frames * 5) & 0xFF), 0x40, 0x40, 1, + (globalCtx->state.frames * 6) & 0xFF, + 0xFF - ((globalCtx->state.frames * 0xA) & 0xFF), 0x40, 0x40)); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_magic_wind.c", 646); + + return true; +} + +void MagicWind_Draw(Actor* thisx, GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MagicWind* this = (MagicWind*)thisx; + + OPEN_DISPS(gfxCtx, "../z_magic_wind.c", 661); + + if (this->actionFunc != MagicWind_WaitForTimer) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 25); + SkelCurve_Draw(thisx, globalCtx, &this->skelCurve, MagicWind_OverrideLimbDraw, NULL, 1, NULL); + } + + CLOSE_DISPS(gfxCtx, "../z_magic_wind.c", 673); +} diff --git a/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h b/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h new file mode 100644 index 000000000..25b3e7d70 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.h @@ -0,0 +1,18 @@ +#ifndef Z_MAGIC_WIND_H +#define Z_MAGIC_WIND_H + +#include "ultra64.h" +#include "global.h" + +struct MagicWind; + +typedef void (*MagicWindFunc)(struct MagicWind* this, GlobalContext* globalCtx); + +typedef struct MagicWind { + /* 0x0000 */ Actor actor; + /* 0x014C */ SkelAnimeCurve skelCurve; + /* 0x016C */ s16 timer; + /* 0x0170 */ MagicWindFunc actionFunc; +} MagicWind; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c new file mode 100644 index 000000000..d0f59a259 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.c @@ -0,0 +1,597 @@ +/* + * File: z_mir_ray.c + * Overlay: ovl_Mir_Ray + * Description: Reflectable Light Beam and reflections + */ + +#include "z_mir_ray.h" +#include "objects/object_mir_ray/object_mir_ray.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5) + +void MirRay_Init(Actor* thisx, GlobalContext* globalCtx); +void MirRay_Destroy(Actor* thisx, GlobalContext* globalCtx); +void MirRay_Update(Actor* thisx, GlobalContext* globalCtx); +void MirRay_Draw(Actor* thisx, GlobalContext* globalCtx); + +s32 MirRay_CheckInFrustum(Vec3f* vecA, Vec3f* vecB, f32 pointx, f32 pointy, f32 pointz, s16 radiusA, s16 radiusB); + +// Locations of light beams in sMirRayData +typedef enum { + /* 0 */ MIRRAY_SPIRIT_BOMBCHUIWAROOM_DOWNLIGHT, + /* 1 */ MIRRAY_SPIRIT_SUNBLOCKROOM_DOWNLIGHT, + /* 2 */ MIRRAY_SPIRIT_SINGLECOBRAROOM_DOWNLIGHT, + /* 3 */ MIRRAY_SPIRIT_ARMOSROOM_DOWNLIGHT, + /* 4 */ MIRRAY_SPIRIT_TOPROOM_DOWNLIGHT, + /* 5 */ MIRRAY_SPIRIT_TOPROOM_CEILINGMIRROR, + /* 6 */ MIRRAY_SPIRIT_SINGLECOBRAROOM_COBRA, + /* 7 */ MIRRAY_SPIRIT_TOPROOM_COBRA1, + /* 8 */ MIRRAY_SPIRIT_TOPROOM_COBRA2, + /* 9 */ MIRRAY_GANONSCASTLE_SPIRITTRIAL_DOWNLIGHT +} MirRayBeamLocations; + +const ActorInit Mir_Ray_InitVars = { + ACTOR_MIR_RAY, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_MIR_RAY, + sizeof(MirRay), + (ActorFunc)MirRay_Init, + (ActorFunc)MirRay_Destroy, + (ActorFunc)MirRay_Update, + (ActorFunc)MirRay_Draw, + NULL, +}; + +static u8 D_80B8E670 = 0; + +static ColliderQuadInit sQuadInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK0, + { 0x00200000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00200000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { 0, { { 0, 0, 0 }, 50 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_NONE, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static MirRayDataEntry sMirRayData[] = { + { { -1160, 686, -880 }, { -920, 480, -889 }, 30, 50, 1.0f, 50, 150, 0.8f, 255, 255, 255, 0x02 }, + { { -1856, 1092, -190 }, { -1703, 841, -186 }, 30, 70, 0.88f, 54, 150, 0.8f, 255, 255, 255, 0x02 }, + { { 1367, 738, -860 }, { 1091, 476, -860 }, 30, 85, 0.0f, 0, 150, 0.8f, 255, 255, 255, 0x00 }, + { { 2200, 1103, -220 }, { 2040, 843, -220 }, 30, 60, 0.0f, 0, 150, 0.8f, 255, 255, 255, 0x01 }, + { { -560, 2169, -310 }, { -560, 1743, -310 }, 30, 70, 0.0f, 0, 150, 0.8f, 255, 255, 255, 0x00 }, + { { 60, 1802, -1090 }, { 60, 973, -1090 }, 30, 70, 0.0f, 0, 150, 0.9f, 255, 255, 255, 0x0D }, + { { 1140, 480, -860 }, { 1140, 480, -860 }, 30, 30, 1.0f, 10, 100, 0.9f, 255, 255, 255, 0x0E }, + { { -560, 1743, -310 }, { -560, 1743, -310 }, 30, 30, 0.0f, 0, 100, 0.94f, 255, 255, 255, 0x0C }, + { { 60, 1743, -310 }, { 60, 1743, -310 }, 30, 30, 0.0f, 0, 100, 0.94f, 255, 255, 255, 0x0C }, + { { -1174, 448, 1194 }, { -1174, 148, 1194 }, 50, 100, 1.0f, 50, 150, 0.8f, 255, 255, 255, 0x03 } +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 0, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void MirRay_SetupCollider(MirRay* this) { + Vec3f colliderOffset; + MirRayDataEntry* dataEntry = &sMirRayData[this->actor.params]; + + colliderOffset.x = (this->poolPt.x - this->sourcePt.x) * dataEntry->unk_10; + colliderOffset.y = (this->poolPt.y - this->sourcePt.y) * dataEntry->unk_10; + colliderOffset.z = (this->poolPt.z - this->sourcePt.z) * dataEntry->unk_10; + this->colliderSph.elements[0].dim.worldSphere.center.x = colliderOffset.x + this->sourcePt.x; + this->colliderSph.elements[0].dim.worldSphere.center.y = colliderOffset.y + this->sourcePt.y; + this->colliderSph.elements[0].dim.worldSphere.center.z = colliderOffset.z + this->sourcePt.z; + this->colliderSph.elements[0].dim.worldSphere.radius = dataEntry->unk_14 * this->colliderSph.elements->dim.scale; +} + +// Set up a light point between source point and reflection point. Reflection point is the pool point (for windows) or +// at the player position (for mirrors) +void MirRay_MakeShieldLight(MirRay* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + MirRayDataEntry* dataEntry = &sMirRayData[this->actor.params]; + Vec3f reflectionPt; + Vec3s lightPt; + + if (MirRay_CheckInFrustum(&this->sourcePt, &this->poolPt, player->actor.world.pos.x, + player->actor.world.pos.y + 30.0f, player->actor.world.pos.z, this->sourceEndRad, + this->poolEndRad)) { + + if (dataEntry->params & 8) { // Light beams from mirrors + Math_Vec3f_Diff(&player->actor.world.pos, &this->sourcePt, &reflectionPt); + } else { // Light beams from windows + Math_Vec3f_Diff(&this->poolPt, &this->sourcePt, &reflectionPt); + } + + lightPt.x = (dataEntry->unk_18 * reflectionPt.x) + this->sourcePt.x; + lightPt.y = (dataEntry->unk_18 * reflectionPt.y) + this->sourcePt.y; + lightPt.z = (dataEntry->unk_18 * reflectionPt.z) + this->sourcePt.z; + + // Fade up + Math_StepToS(&this->lightPointRad, dataEntry->lgtPtMaxRad, 6); + Lights_PointNoGlowSetInfo(&this->lightInfo, lightPt.x, lightPt.y, lightPt.z, dataEntry->color.r, + dataEntry->color.g, dataEntry->color.b, this->lightPointRad); + } else { + // Fade down + Math_StepToS(&this->lightPointRad, 0, 6); + Lights_PointSetColorAndRadius(&this->lightInfo, dataEntry->color.r, dataEntry->color.g, dataEntry->color.b, + this->lightPointRad); + } +} + +void MirRay_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + MirRay* this = (MirRay*)thisx; + MirRayDataEntry* dataEntry = &sMirRayData[this->actor.params]; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + // "Generation of reflectable light!" + osSyncPrintf("反射用 光の発生!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + LOG_NUM("this->actor.arg_data", this->actor.params, "../z_mir_ray.c", 518); + + if (this->actor.params >= 0xA) { + // "Reflected light generation failure" + LOG_STRING("反射光 発生失敗", "../z_mir_ray.c", 521); + Actor_Kill(&this->actor); + } + + this->sourcePt.x = dataEntry->sourcePoint.x; + this->sourcePt.y = dataEntry->sourcePoint.y; + this->sourcePt.z = dataEntry->sourcePoint.z; + this->sourceEndRad = dataEntry->sourceEndRadius; + + this->poolPt.x = dataEntry->poolPoint.x; + this->poolPt.y = dataEntry->poolPoint.y; + this->poolPt.z = dataEntry->poolPoint.z; + this->poolEndRad = dataEntry->poolEndRadius; + + Lights_PointNoGlowSetInfo(&this->lightInfo, this->sourcePt.x, this->sourcePt.y, this->sourcePt.z, 255, 255, 255, + 100); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + + this->shieldCorners[0].x = -536.0f; + this->shieldCorners[0].y = -939.0f; + + this->shieldCorners[1].x = -1690.0f; + this->shieldCorners[1].y = 0.0f; + + this->shieldCorners[2].x = -536.0f; + this->shieldCorners[2].y = 938.0f; + + this->shieldCorners[3].x = 921.0f; + this->shieldCorners[3].y = 0.0f; + + this->shieldCorners[4].x = 758.0f; + this->shieldCorners[4].y = 800.0f; + + this->shieldCorners[5].x = 758.0f; + this->shieldCorners[5].y = -800.0f; + + if (dataEntry->params & 2) { + Collider_InitJntSph(globalCtx, &this->colliderSph); + Collider_SetJntSph(globalCtx, &this->colliderSph, &this->actor, &sJntSphInit, &this->colliderSphItem); + if (!(dataEntry->params & 4)) { // Beams not from mirrors + MirRay_SetupCollider(this); + } + } + + Collider_InitQuad(globalCtx, &this->shieldRay); + Collider_SetQuad(globalCtx, &this->shieldRay, &this->actor, &sQuadInit); + + // Spirit Temple top room mirrors + if ((this->actor.params == 5) || (this->actor.params == 7) || (this->actor.params == 8)) { + this->actor.room = -1; + } +} + +void MirRay_Destroy(Actor* thisx, GlobalContext* globalCtx) { + MirRay* this = (MirRay*)thisx; + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); + + if (sMirRayData[this->actor.params].params & 2) { + Collider_DestroyJntSph(globalCtx, &this->colliderSph); + } + + Collider_DestroyQuad(globalCtx, &this->shieldRay); +} + +void MirRay_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + MirRay* this = (MirRay*)thisx; + Player* player = GET_PLAYER(globalCtx); + + D_80B8E670 = 0; + + if (!this->unLit) { + if (sMirRayData[this->actor.params].params & 2) { + if (sMirRayData[this->actor.params].params & 4) { // Beams from mirrors + MirRay_SetupCollider(this); + } + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderSph.base); + } + if (this->reflectIntensity > 0.0f) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->shieldRay.base); + } + MirRay_MakeShieldLight(this, globalCtx); + + if (this->reflectIntensity > 0.0f) { + func_8002F8F0(&player->actor, NA_SE_IT_SHIELD_BEAM - SFX_FLAG); + } + } +} + +void MirRay_SetIntensity(MirRay* this, GlobalContext* globalCtx) { + f32 sp4C[3]; + f32 temp_f0; + f32 temp_f0_2; + f32 temp_f2_2; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + MtxF* shieldMtx = &player->shieldMf; + + this->reflectIntensity = 0.0f; + + if (MirRay_CheckInFrustum(&this->sourcePt, &this->poolPt, shieldMtx->xw, shieldMtx->yw, shieldMtx->zw, + this->sourceEndRad, this->poolEndRad)) { + + temp_f0 = sqrtf(SQ(shieldMtx->zz) + (SQ(shieldMtx->xz) + SQ(shieldMtx->yz))); + + if (temp_f0 == 0.0f) { + this->reflectRange = 1.0f; + } else { + this->reflectRange = 1.0f / temp_f0; + } + + // If light beam is adirectional, always reflect, else only reflect if shield is pointing in correct direction + if (sMirRayData[this->actor.params].params & 1) { + this->reflectIntensity = 1.0f; + } else { + sp4C[0] = this->poolPt.x - this->sourcePt.x; + sp4C[1] = this->poolPt.y - this->sourcePt.y; + sp4C[2] = this->poolPt.z - this->sourcePt.z; + + temp_f2_2 = -shieldMtx->xz * sp4C[0] - shieldMtx->yz * sp4C[1] - shieldMtx->zz * sp4C[2]; + + if (temp_f2_2 < 0.0f) { + temp_f0_2 = sqrtf(SQ(sp4C[0]) + SQ(sp4C[1]) + SQ(sp4C[2])); + if ((temp_f0 != 0.0f) && (temp_f0_2 != 0.0f)) { + this->reflectIntensity = -temp_f2_2 / (temp_f0 * temp_f0_2); + } + } + } + } +} + +// Draws six images, one for each corner of the shield, by finding the intersection of a line segment from the corner +// perpendicular to the shield with the nearest collision (if any). +void MirRay_SetupReflectionPolys(MirRay* this, GlobalContext* globalCtx, MirRayShieldReflection* reflection) { + Player* player = GET_PLAYER(globalCtx); + MtxF* shieldMtx; + s32 i; + Vec3f posA; + Vec3f posB; + Vec3f posResult; + CollisionPoly* outPoly; + Vec3f sp60; + + shieldMtx = &player->shieldMf; + + sp60.x = -((*shieldMtx).xz * this->reflectRange) * this->reflectIntensity * 400.0f; + sp60.y = -((*shieldMtx).yz * this->reflectRange) * this->reflectIntensity * 400.0f; + sp60.z = -((*shieldMtx).zz * this->reflectRange) * this->reflectIntensity * 400.0f; + + sp60 = sp60; // Need something involving sp60 or the whole function changes + + for (i = 0; i < 6; i++) { + posA.x = ((*shieldMtx).xw + (this->shieldCorners[i].x * (*shieldMtx).xx)) + + (this->shieldCorners[i].y * (*shieldMtx).xy); + posA.y = ((*shieldMtx).yw + (this->shieldCorners[i].x * (*shieldMtx).yx)) + + (this->shieldCorners[i].y * (*shieldMtx).yy); + posA.z = ((*shieldMtx).zw + (this->shieldCorners[i].x * (*shieldMtx).zx)) + + (this->shieldCorners[i].y * (*shieldMtx).zy); + posB.x = sp60.x + posA.x; + posB.y = sp60.y + posA.y; + posB.z = sp60.z + posA.z; + if (BgCheck_AnyLineTest1(&globalCtx->colCtx, &posA, &posB, &posResult, &outPoly, 1)) { + reflection[i].reflectionPoly = outPoly; + } else { + reflection[i].reflectionPoly = NULL; + } + } +} + +// Remove reflections that are in the same position and are sufficiently near to the same plane +void MirRay_RemoveSimilarReflections(MirRayShieldReflection* reflection) { + s32 i; + s32 j; + + for (i = 0; i < 6; i++) { + for (j = i + 1; j < 6; j++) { + if (reflection[i].reflectionPoly != NULL) { + if ((reflection[j].reflectionPoly != NULL) && + (ABS(reflection[i].reflectionPoly->normal.x - reflection[j].reflectionPoly->normal.x) < 100) && + (ABS(reflection[i].reflectionPoly->normal.y - reflection[j].reflectionPoly->normal.y) < 100) && + (ABS(reflection[i].reflectionPoly->normal.z - reflection[j].reflectionPoly->normal.z) < 100) && + (reflection[i].reflectionPoly->dist == reflection[j].reflectionPoly->dist)) { + reflection[j].reflectionPoly = NULL; + } + } + } + } +} + +// Creates the reflected beam's collider (to interact with objects) and places and orients the shield images +void MirRay_ReflectedBeam(MirRay* this, GlobalContext* globalCtx, MirRayShieldReflection* reflection) { + Player* player = GET_PLAYER(globalCtx); + s32 i; + f32 temp_f0; + Vec3f vecB; + Vec3f vecD; + Vec3f sp118; + Vec3f sp10C; + Vec3f sp100; + Vec3f intersection; + Vec3f spE8; + Vec3f normalVec; + MtxF* shieldMtx; + Vec3f vecA; + Vec3f vecC; + MirRayShieldReflection* currentReflection; + + shieldMtx = &player->shieldMf; + + spE8.x = -(shieldMtx->xz * this->reflectRange) * this->reflectIntensity * 400.0f; + spE8.y = -(shieldMtx->yz * this->reflectRange) * this->reflectIntensity * 400.0f; + spE8.z = -(shieldMtx->zz * this->reflectRange) * this->reflectIntensity * 400.0f; + + vecB.x = shieldMtx->xw; + vecB.y = shieldMtx->yw; + vecB.z = shieldMtx->zw; + + vecD.x = spE8.x + vecB.x; + vecD.y = spE8.y + vecB.y; + vecD.z = spE8.z + vecB.z; + + vecA.x = vecB.x + (shieldMtx->xx * 300.0f); + vecA.y = vecB.y + (shieldMtx->yx * 300.0f); + vecA.z = vecB.z + (shieldMtx->zx * 300.0f); + + vecC.x = vecD.x + (shieldMtx->xx * 300.0f); + vecC.y = vecD.y + (shieldMtx->yx * 300.0f); + vecC.z = vecD.z + (shieldMtx->zx * 300.0f); + + Collider_SetQuadVertices(&this->shieldRay, &vecA, &vecB, &vecC, &vecD); + + for (i = 0; i < 6; i++) { + currentReflection = &reflection[i]; + if (currentReflection->reflectionPoly != NULL) { + normalVec.x = COLPOLY_GET_NORMAL(currentReflection->reflectionPoly->normal.x); + normalVec.y = COLPOLY_GET_NORMAL(currentReflection->reflectionPoly->normal.y); + normalVec.z = COLPOLY_GET_NORMAL(currentReflection->reflectionPoly->normal.z); + + if (Math3D_LineSegVsPlane(normalVec.x, normalVec.y, normalVec.z, currentReflection->reflectionPoly->dist, + &vecB, &vecD, &sp118, 1)) { + + currentReflection->pos.x = sp118.x; + currentReflection->pos.y = sp118.y; + currentReflection->pos.z = sp118.z; + + temp_f0 = sqrtf(SQ(sp118.x - vecB.x) + SQ(sp118.y - vecB.y) + SQ(sp118.z - vecB.z)); + + if (temp_f0 < (this->reflectIntensity * 600.0f)) { + currentReflection->opacity = 200; + } else { + currentReflection->opacity = (s32)(800.0f - temp_f0); + } + + spE8 = spE8; // Required to match + + sp10C.x = (shieldMtx->xx * 100.0f) + vecB.x; + sp10C.y = (shieldMtx->yx * 100.0f) + vecB.y; + sp10C.z = (shieldMtx->zx * 100.0f) + vecB.z; + + sp100.x = (spE8.x * 4.0f) + sp10C.x; + sp100.y = (spE8.y * 4.0f) + sp10C.y; + sp100.z = (spE8.z * 4.0f) + sp10C.z; + + normalVec = normalVec; // Required to match + + currentReflection->mtx.zw = 0.0f; + + if (1) {} + if (1) {} + if (1) {} + if (1) {} // All four required to match + + currentReflection->mtx.xx = currentReflection->mtx.yy = currentReflection->mtx.zz = + currentReflection->mtx.ww = 1.0f; + currentReflection->mtx.yx = currentReflection->mtx.zx = currentReflection->mtx.wx = + currentReflection->mtx.xy = currentReflection->mtx.zy = currentReflection->mtx.wy = + currentReflection->mtx.xz = currentReflection->mtx.yz = currentReflection->mtx.wz = + currentReflection->mtx.xw = currentReflection->mtx.yw = currentReflection->mtx.zw; + + if (Math3D_LineSegVsPlane(normalVec.x, normalVec.y, normalVec.z, + currentReflection->reflectionPoly->dist, &sp10C, &sp100, &intersection, 1)) { + currentReflection->mtx.xx = intersection.x - sp118.x; + currentReflection->mtx.yx = intersection.y - sp118.y; + currentReflection->mtx.zx = intersection.z - sp118.z; + } + + sp10C.x = (shieldMtx->xy * 100.0f) + vecB.x; + sp10C.y = (shieldMtx->yy * 100.0f) + vecB.y; + sp10C.z = (shieldMtx->zy * 100.0f) + vecB.z; + + sp100.x = (spE8.x * 4.0f) + sp10C.x; + sp100.y = (spE8.y * 4.0f) + sp10C.y; + sp100.z = (spE8.z * 4.0f) + sp10C.z; + + if (Math3D_LineSegVsPlane(normalVec.x, normalVec.y, normalVec.z, + currentReflection->reflectionPoly->dist, &sp10C, &sp100, &intersection, 1)) { + currentReflection->mtx.xy = intersection.x - sp118.x; + currentReflection->mtx.yy = intersection.y - sp118.y; + currentReflection->mtx.zy = intersection.z - sp118.z; + } + } else { + currentReflection->reflectionPoly = NULL; + } + } + } +} + +void MirRay_Draw(Actor* thisx, GlobalContext* globalCtx) { + MirRay* this = (MirRay*)thisx; + Player* player = GET_PLAYER(globalCtx); + s32 i; + MirRayShieldReflection reflection[6]; + s32 temp; + + this->reflectIntensity = 0.0f; + if ((D_80B8E670 == 0) && !this->unLit && Player_HasMirrorShieldSetToDraw(globalCtx)) { + Matrix_Mult(&player->shieldMf, MTXMODE_NEW); + MirRay_SetIntensity(this, globalCtx); + if (!(this->reflectIntensity <= 0.0f)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_mir_ray.c", 966); + + func_80093D84(globalCtx->state.gfxCtx); + Matrix_Scale(1.0f, 1.0f, this->reflectIntensity * 5.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_mir_ray.c", 972), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 150, (s16)(temp = this->reflectIntensity * 100.0f)); + gSPDisplayList(POLY_XLU_DISP++, gShieldBeamGlowDL); + MirRay_SetupReflectionPolys(this, globalCtx, reflection); + MirRay_RemoveSimilarReflections(reflection); + MirRay_ReflectedBeam(this, globalCtx, reflection); + + if (reflection[0].reflectionPoly == NULL) { + reflection[0].opacity = 0; + } + for (i = 1; i < 6; i++) { + if (reflection[i].reflectionPoly != NULL) { + if (reflection[0].opacity < reflection[i].opacity) { + reflection[0].opacity = reflection[i].opacity; + } + } + } + for (i = 0; i < 6; i++) { + if (reflection[i].reflectionPoly != NULL) { + Matrix_Translate(reflection[i].pos.x, reflection[i].pos.y, reflection[i].pos.z, MTXMODE_NEW); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + Matrix_Mult(&reflection[i].mtx, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_mir_ray.c", 1006), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_DECAL2); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 150, reflection[0].opacity); + gSPDisplayList(POLY_XLU_DISP++, gShieldBeamImageDL); + } + } + + D_80B8E670 = 1; + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_mir_ray.c", 1027); + } + } +} + +// Computes if the Point (pointx, pointy, pointz) lies within the right conical frustum with one end centred at vecA +// with radius radiusA, the other at vecB with radius radiusB +s32 MirRay_CheckInFrustum(Vec3f* vecA, Vec3f* vecB, f32 pointx, f32 pointy, f32 pointz, s16 radiusA, s16 radiusB) { + f32 coneRadius; + f32 closestPtx; + f32 closestPty; + f32 closestPtz; + Vec3f vecdiff; + f32 dist; + Vec3f sp5C; + Vec3f sp50; + Vec3f sp44; + + vecdiff.x = vecB->x - vecA->x; + vecdiff.y = vecB->y - vecA->y; + vecdiff.z = vecB->z - vecA->z; + if (1) {} + dist = SQ(vecdiff.x) + SQ(vecdiff.y) + SQ(vecdiff.z); + + if (dist == 0.0f) { + return 0; + } + + dist = + (((pointx - vecA->x) * vecdiff.x) + ((pointy - vecA->y) * vecdiff.y) + ((pointz - vecA->z) * vecdiff.z)) / dist; + + // Closest point on line A-B to Point + closestPtx = (vecdiff.x * dist) + vecA->x; + closestPty = (vecdiff.y * dist) + vecA->y; + closestPtz = (vecdiff.z * dist) + vecA->z; + + // Diameter of the double cone on the perpendicular plane through the closest point + coneRadius = ((radiusB - radiusA) * dist) + radiusA; + + // If the Point is within the bounding double cone, check if it is in the frustum by checking whether it is between + // the bounding planes + if ((SQ(closestPtx - pointx) + SQ(closestPty - pointy) + SQ(closestPtz - pointz)) <= SQ(coneRadius)) { + if (1) {} + + // Stores the vector difference again + Math_Vec3f_Diff(vecB, vecA, &sp5C); + + sp50.x = pointx - vecA->x; + sp50.y = pointy - vecA->y; + sp50.z = pointz - vecA->z; + + if (Math3D_Cos(&sp5C, &sp50) < 0.0f) { + return 0; + } + + sp44.x = pointx - vecB->x; + sp44.y = pointy - vecB->y; + sp44.z = pointz - vecB->z; + + if (Math3D_Cos(&sp5C, &sp44) > 0.0f) { + return 0; + } + return 1; + } + return 0; +} diff --git a/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.h b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.h new file mode 100644 index 000000000..0f645cb4f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Mir_Ray/z_mir_ray.h @@ -0,0 +1,47 @@ +#ifndef Z_MIR_RAY_H +#define Z_MIR_RAY_H + +#include "ultra64.h" +#include "global.h" + +struct MirRay; + +typedef struct { + /* 0x00 */ Vec3s sourcePoint; + /* 0x06 */ Vec3s poolPoint; // point at center of light pool on floor for windows and BigMirror, same as source point for Cobra Mirror + /* 0x0C */ s16 sourceEndRadius; // Radius of beam frustum at the source end + /* 0x0E */ s16 poolEndRadius; // Radius of beam frustum at the pool end + /* 0x10 */ f32 unk_10; // placement of collider center along beam + /* 0x14 */ s16 unk_14; // collider radius before scaled + /* 0x16 */ s16 lgtPtMaxRad; // light point max radius + /* 0x18 */ f32 unk_18; // placement of light point between source and reflection point (pool point for windows, player for mirrors) + /* 0x1C */ Color_RGB8 color; + /* 0x1F */ u8 params; +} MirRayDataEntry; // size = 0x20 + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ MtxF mtx; + /* 0x4C */ CollisionPoly* reflectionPoly; + /* 0x50 */ u8 opacity; +} MirRayShieldReflection; // size = 0x54 + +typedef struct MirRay { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderJntSph colliderSph; + /* 0x016C */ ColliderJntSphElement colliderSphItem; + /* 0x01AC */ ColliderQuad shieldRay; + /* 0x022C */ f32 reflectIntensity; // Reflection occurs if it is positive, brightness depends on it + /* 0x0230 */ Vec3f shieldCorners[6]; + /* 0x0278 */ f32 reflectRange; + /* 0x027C */ Vec3f sourcePt; + /* 0x0288 */ Vec3f poolPt; + /* 0x0294 */ s16 sourceEndRad; + /* 0x0296 */ s16 poolEndRad; + /* 0x0298 */ s16 lightPointRad; + /* 0x029C */ LightNode* lightNode; + /* 0x02A0 */ LightInfo lightInfo; + /* 0x02AE */ u8 unLit; // Conditioned on. set in Cobra? +} MirRay; // size = 0x02B0 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Bean/z_obj_bean.c b/soh/src/overlays/actors/ovl_Obj_Bean/z_obj_bean.c new file mode 100644 index 000000000..53b0a1565 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Bean/z_obj_bean.c @@ -0,0 +1,946 @@ +/* + * File: z_obj_bean.c + * Overlay: ovl_Obj_Bean + * Description: Bean plant spot + */ + +#include "z_obj_bean.h" +#include "objects/object_mamenoki/object_mamenoki.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_22 + +void ObjBean_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjBean_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjBean_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjBean_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjBean_WaitForPlayer(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_Fly(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_SetupFly(ObjBean* this); +void ObjBean_WaitForWater(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_SetupWaitForWater(ObjBean* this); +void ObjBean_SetupGrowWaterPhase1(ObjBean* this); +void ObjBean_GrowWaterPhase1(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_GrowWaterPhase2(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_SetupGrowWaterPhase2(ObjBean* this); +void ObjBean_GrowWaterPhase3(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_SetupGrowWaterPhase3(ObjBean* this); +void ObjBean_SetupGrown(ObjBean* this); +void ObjBean_FlattenLeaves(ObjBean* this); +void ObjBean_Grown(ObjBean* this); +void ObjBean_LeavesStill(ObjBean* this); +void ObjBean_SetupShakeLeaves(ObjBean* this); +void ObjBean_ShakeLeaves(ObjBean* this); +void ObjBean_SetupWaitForBean(ObjBean* this); +void ObjBean_WaitForBean(ObjBean* this, GlobalContext* globalCtx); +void func_80B8FE3C(ObjBean* this, GlobalContext* globalCtx); +void func_80B8FE00(ObjBean* this); +void func_80B8FE6C(ObjBean* this); +void func_80B8FEAC(ObjBean* this, GlobalContext* globalCtx); +void func_80B8FF50(ObjBean* this); +void ObjBean_SetupGrowWaterPhase4(ObjBean* this); +void func_80B8FF8C(ObjBean* this, GlobalContext* globalCtx); +void func_80B90050(ObjBean* this, GlobalContext* globalCtx); +void func_80B90010(ObjBean* this); +void func_80B908EC(ObjBean* this); +void func_80B90918(ObjBean* this, GlobalContext* globalCtx); +void func_80B90970(ObjBean* this); +void func_80B909B0(ObjBean* this, GlobalContext* globalCtx); +void func_80B909F8(ObjBean* this); +void func_80B90A34(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_SetupWaitForPlayer(ObjBean* this); +void ObjBean_GrowWaterPhase4(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_GrowWaterPhase5(ObjBean* this, GlobalContext* globalCtx); +void ObjBean_SetupGrowWaterPhase5(ObjBean* this); +void ObjBean_SetupShakeLeavesFast(ObjBean* this); +void ObjBean_ShakeLeavesFast(ObjBean* this); +void ObjBean_Grow(ObjBean* this); +void ObjBean_SetupGrow(ObjBean* this); +void ObjBean_SetupWaitForStepOff(ObjBean* this); +void ObjBean_WaitForStepOff(ObjBean* this, GlobalContext* globalCtx); + +#define BEAN_STATE_DRAW_LEAVES (1 << 0) +#define BEAN_STATE_DRAW_SOIL (1 << 1) +#define BEAN_STATE_DRAW_PLANT (1 << 2) +#define BEAN_STATE_DRAW_STALK (1 << 3) +#define BEAN_STATE_COLLIDER_SET (1 << 4) +#define BEAN_STATE_DYNAPOLY_SET (1 << 5) +#define BEAN_STATE_BEEN_WATERED (1 << 6) +#define BEAN_STATE_PLAYER_ON_TOP (1 << 7) + +static ObjBean* D_80B90E30 = NULL; + +const ActorInit Obj_Bean_InitVars = { + ACTOR_OBJ_BEAN, + ACTORCAT_BG, + FLAGS, + OBJECT_MAMENOKI, + sizeof(ObjBean), + (ActorFunc)ObjBean_Init, + (ActorFunc)ObjBean_Destroy, + (ActorFunc)ObjBean_Update, + (ActorFunc)ObjBean_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 64, 30, -31, { 0, 0, 0 } }, +}; + +typedef struct { + f32 velocity; + f32 accel; +} BeenSpeedInfo; + +static BeenSpeedInfo sBeanSpeeds[] = { + { 3.0f, 0.3f }, + { 10.0f, 0.5f }, + { 30.0f, 0.5f }, + { 3.0f, 0.3f }, +}; + +static Gfx* sBreakDlists[] = { gCuttableShrubStalkDL, gCuttableShrubTipDL }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1600, ICHAIN_STOP), +}; + +void ObjBean_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + ObjBean* this = (ObjBean*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); +} + +void ObjBean_InitDynaPoly(ObjBean* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader; + s32 pad2; + + colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_obj_bean.c", 374, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void ObjBean_FindFloor(ObjBean* this, GlobalContext* globalCtx) { + Vec3f vec; + s32 sp20; + + vec.x = this->dyna.actor.world.pos.x; + vec.y = this->dyna.actor.world.pos.y + 29.999998f; + vec.z = this->dyna.actor.world.pos.z; + this->dyna.actor.floorHeight = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->dyna.actor.floorPoly, &sp20, &this->dyna.actor, &vec); +} + +void func_80B8EBC8(ObjBean* this) { + this->unk_1B6.x = this->unk_1B6.y = this->unk_1B6.z = 0; + this->unk_1E4 = 0.0f; +} + +void ObjBean_UpdatePosition(ObjBean* this) { + f32 temp_f20; + + this->unk_1B6.x += 0xB6; + this->unk_1B6.y += 0xFB; + this->unk_1B6.z += 0x64; + + Math_StepToF(&this->unk_1E4, 2.0f, 0.1f); + temp_f20 = Math_SinS(this->unk_1B6.x * 3); + this->posOffsetX = (Math_SinS(((this->unk_1B6.y * 3))) + temp_f20) * this->unk_1E4; + temp_f20 = Math_CosS(this->unk_1B6.x * 4); + this->posOffsetZ = (Math_CosS((this->unk_1B6.y * 4)) + temp_f20) * this->unk_1E4; + temp_f20 = Math_SinS(this->unk_1B6.z * 5); + + this->dyna.actor.scale.x = this->dyna.actor.scale.z = + ((Math_SinS((this->unk_1B6.y * 8)) * 0.01f) + (temp_f20 * 0.06f) + 1.07f) * 0.1f; + + this->dyna.actor.scale.y = ((Math_CosS(((this->unk_1B6.z * 10))) * 0.2f) + 1.0f) * 0.1f; + temp_f20 = Math_SinS(this->unk_1B6.x * 3); + this->dyna.actor.shape.rot.y = + (Math_SinS((s16)(this->unk_1B6.z * 2)) * 2100.0f) + ((f32)this->dyna.actor.home.rot.y + (temp_f20 * 1000.0f)); +} + +void func_80B8EDF4(ObjBean* this) { + this->unk_1B6.x = this->unk_1B6.y = this->unk_1B6.z = 0; + + Actor_SetScale(&this->dyna.actor, 0.0f); +} + +void func_80B8EE24(ObjBean* this) { + this->unk_1B6.x += 0x384; + if (this->unk_1B6.x > 0x5FFF) { + this->unk_1B6.x = 0x5FFF; + } + this->unk_1B6.y += 0x258; + if (this->unk_1B6.y > 0x4000) { + this->unk_1B6.y = 0x4000; + } + this->dyna.actor.scale.y = Math_SinS(this->unk_1B6.x) * 0.16970563f; + + this->dyna.actor.scale.x = this->dyna.actor.scale.z = Math_SinS(this->unk_1B6.y) * 0.10700001f; + + Math_StepToF(&this->posOffsetX, 0.0f, 0.1f); + Math_StepToF(&this->posOffsetZ, 0.0f, 0.1f); + Math_ScaledStepToS(&this->dyna.actor.shape.rot.y, this->dyna.actor.home.rot.y, 0x64); +} + +void ObjBean_Move(ObjBean* this) { + this->dyna.actor.world.pos.x = this->pathPoints.x + this->posOffsetX; + this->dyna.actor.world.pos.y = this->pathPoints.y; + this->dyna.actor.world.pos.z = this->pathPoints.z + this->posOffsetZ; +} + +void ObjBean_SetDrawMode(ObjBean* this, u8 drawFlag) { + this->stateFlags &= + ~(BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_PLANT | BEAN_STATE_DRAW_STALK | BEAN_STATE_DRAW_SOIL); + this->stateFlags |= drawFlag; +} + +void ObjBean_SetupPathCount(ObjBean* this, GlobalContext* globalCtx) { + this->pathCount = globalCtx->setupPathList[(this->dyna.actor.params >> 8) & 0x1F].count - 1; + this->currentPointIndex = 0; + this->nextPointIndex = 1; +} + +void ObjBean_SetupPath(ObjBean* this, GlobalContext* globalCtx) { + Path* path = &globalCtx->setupPathList[(this->dyna.actor.params >> 8) & 0x1F]; + Math_Vec3s_ToVec3f(&this->pathPoints, SEGMENTED_TO_VIRTUAL(path->points)); +} + +void ObjBean_FollowPath(ObjBean* this, GlobalContext* globalCtx) { + Path* path; + Vec3f acell; + Vec3f pathPointsFloat; + f32 speed; + Vec3s* nextPathPoint; + Vec3s* currentPoint; + Vec3s* sp4C; + Vec3f sp40; + Vec3f sp34; + f32 sp30; + f32 mag; + + Math_StepToF(&this->dyna.actor.speedXZ, sBeanSpeeds[this->unk_1F6].velocity, sBeanSpeeds[this->unk_1F6].accel); + path = &globalCtx->setupPathList[(this->dyna.actor.params >> 8) & 0x1F]; + nextPathPoint = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[this->nextPointIndex]; + + Math_Vec3s_ToVec3f(&pathPointsFloat, nextPathPoint); + + Math_Vec3f_Diff(&pathPointsFloat, &this->pathPoints, &acell); + mag = Math3D_Vec3fMagnitude(&acell); + speed = CLAMP_MIN(this->dyna.actor.speedXZ, 0.5f); + if (speed > mag) { + currentPoint = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[this->currentPointIndex]; + + Math_Vec3f_Copy(&this->pathPoints, &pathPointsFloat); + this->currentPointIndex = this->nextPointIndex; + + if (this->pathCount <= this->currentPointIndex) { + this->nextPointIndex = 0; + } else { + this->nextPointIndex++; + } + sp4C = &((Vec3s*)SEGMENTED_TO_VIRTUAL(path->points))[this->nextPointIndex]; + Math_Vec3s_DiffToVec3f(&sp40, nextPathPoint, currentPoint); + Math_Vec3s_DiffToVec3f(&sp34, sp4C, nextPathPoint); + if (Math3D_CosOut(&sp40, &sp34, &sp30)) { + this->dyna.actor.speedXZ = 0.0f; + } else { + this->dyna.actor.speedXZ *= (sp30 + 1.0f) * 0.5f; + } + } else { + Math_Vec3f_Scale(&acell, this->dyna.actor.speedXZ / mag); + this->pathPoints.x += acell.x; + this->pathPoints.y += acell.y; + this->pathPoints.z += acell.z; + } +} + +s32 ObjBean_CheckForHorseTrample(ObjBean* this, GlobalContext* globalCtx) { + Actor* currentActor = globalCtx->actorCtx.actorLists[ACTORCAT_BG].head; + + while (currentActor != NULL) { + if ((currentActor->id == ACTOR_EN_HORSE) && + (Math3D_Vec3fDistSq(¤tActor->world.pos, &this->dyna.actor.world.pos) < 10000.0f)) { + return true; + } + currentActor = currentActor->next; + } + + return false; +} + +void ObjBean_Break(ObjBean* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + f32 temp_f20; + s16 angle; + s32 scale; + s32 i; + s16 gravity; + s16 arg5; + + angle = 0; + for (i = 0; i < 36; i++) { + angle += 0x4E20; + temp_f20 = Rand_ZeroOne() * 60.0f; + + pos.x = (Math_SinS(angle) * temp_f20) + this->dyna.actor.world.pos.x; + pos.y = this->dyna.actor.world.pos.y; + pos.z = (Math_CosS(angle) * temp_f20) + this->dyna.actor.world.pos.z; + + velocity.x = Math_SinS(angle) * 3.5f; + velocity.y = Rand_ZeroOne() * 13.0f; + velocity.z = Math_CosS(angle) * 3.5f; + + velocity.x += this->dyna.actor.world.pos.x - this->dyna.actor.prevPos.x; + velocity.y += this->dyna.actor.world.pos.y - this->dyna.actor.prevPos.y; + velocity.z += this->dyna.actor.world.pos.z - this->dyna.actor.prevPos.z; + + scale = (s32)(Rand_ZeroOne() * 180.0f) + 30; + if (scale < 90) { + if (Rand_ZeroOne() < 0.1f) { + gravity = -80; + arg5 = 96; + } else { + gravity = -80; + arg5 = 64; + } + } else { + gravity = -100; + arg5 = 64; + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, gravity, arg5, 40, 3, 0, scale, 0, 0, + (s16)((scale >> 3) + 40), -1, 1, sBreakDlists[i & 1]); + } +} + +void ObjBean_UpdateLeaves(ObjBean* this) { + Math_StepToS(&this->unk_1C2, this->unk_1C4, this->unk_1C6); + Math_StepToS(&this->unk_1C8, this->unk_1CA, this->unk_1CC); + this->unk_1CE += this->unk_1C8; + this->leafRotFactor = 6372.0f - Math_SinS(this->unk_1CE) * (f32)this->unk_1C2; + this->dyna.actor.scale.y = Math_SinS(this->leafRotFactor) * 0.17434467f; + this->dyna.actor.scale.x = this->dyna.actor.scale.z = Math_CosS(this->leafRotFactor) * 0.12207746f; +} + +void ObjBean_SetupLeavesStill(ObjBean* this) { + this->transformFunc = ObjBean_LeavesStill; + this->unk_1C0 = Rand_S16Offset(12, 40); + this->unk_1C4 = Rand_S16Offset(0xC8, 0x190); + this->unk_1C6 = 0x14; + this->unk_1CA = Rand_S16Offset(0x64, 0x320); + this->unk_1CC = 0x14; +} + +void ObjBean_LeavesStill(ObjBean* this) { + this->unk_1C0--; + ObjBean_UpdateLeaves(this); + if (this->unk_1C0 < 0) { + ObjBean_SetupShakeLeaves(this); + } +} + +void ObjBean_SetupShakeLeaves(ObjBean* this) { + this->transformFunc = ObjBean_ShakeLeaves; + this->unk_1C0 = Rand_S16Offset(30, 4); + this->unk_1C4 = Rand_S16Offset(0x7D0, 0x3E8); + this->unk_1C6 = 0xC8; + this->unk_1CA = Rand_S16Offset(0x36B0, 0x1770); + this->unk_1CC = 0xFA0; + this->leafRotFactor = 0x18E4; +} + +void ObjBean_ShakeLeaves(ObjBean* this) { + this->unk_1C0 += -1; + if (this->unk_1C0 == 14) { + this->unk_1C4 = Rand_S16Offset(0xC8, 0x190); + this->unk_1CA = Rand_S16Offset(0x64, 0x1F4); + this->unk_1CC = 0x7D0; + } + ObjBean_UpdateLeaves(this); + if (this->unk_1C0 < 0) { + ObjBean_SetupLeavesStill(this); + } +} + +void ObjBean_SetupShakeLeavesFast(ObjBean* this) { + this->transformFunc = ObjBean_ShakeLeavesFast; + this->unk_1C0 = 0x28; + this->unk_1C4 = 0xBB8; + this->unk_1C6 = 0x12C; + this->unk_1CA = 0x3A98; + this->unk_1CC = 0xFA0; + this->leafRotFactor = 0x18E4; +} + +void ObjBean_ShakeLeavesFast(ObjBean* this) { + this->unk_1C0 += -1; + if (Rand_ZeroOne() < 0.1f) { + this->unk_1C4 = Rand_S16Offset(0x898, 0x3E8); + this->unk_1CA = Rand_S16Offset(0x2EE0, 0x1F40); + } + ObjBean_UpdateLeaves(this); + if ((s32)this->unk_1C0 < 0) { + ObjBean_SetupGrow(this); + } +} + +void ObjBean_SetupGrow(ObjBean* this) { + this->transformFunc = ObjBean_Grow; +} + +void ObjBean_Grow(ObjBean* this) { + Math_StepToS(&this->leafRotFactor, 0x33E9, 0x168); + this->dyna.actor.scale.y = Math_SinS(this->leafRotFactor) * 0.17434467f; + this->dyna.actor.scale.x = this->dyna.actor.scale.z = Math_CosS(this->leafRotFactor) * 0.12207746f; + ; +} + +void ObjBean_SetupFlattenLeaves(ObjBean* this) { + this->transformFunc = ObjBean_FlattenLeaves; + this->leafRotFactor = 0x33E9; +} + +void ObjBean_FlattenLeaves(ObjBean* this) { + this->leafRotFactor -= 0x960; + this->dyna.actor.scale.y = Math_SinS(this->leafRotFactor) * 0.17434467f; + this->dyna.actor.scale.x = this->dyna.actor.scale.z = Math_CosS(this->leafRotFactor) * 0.12207746f; + + if (this->leafRotFactor < 0x18E4) { + ObjBean_SetupGrown(this); + } +} + +void ObjBean_SetupGrown(ObjBean* this) { + this->transformFunc = ObjBean_Grown; + this->unk_1C2 = 0xBB8; + this->unk_1C4 = 0; + this->unk_1C6 = 0xC8; + this->unk_1C8 = 0x3E80; + this->unk_1CA = 0x1F4; + this->unk_1CC = 0; + this->unk_1C0 = 0x10; +} + +void ObjBean_Grown(ObjBean* this) { + this->unk_1C0--; + if (this->unk_1C0 == 6) { + this->unk_1CC = 0x7D0; + } + ObjBean_UpdateLeaves(this); + if (this->unk_1C2 <= 0) { + ObjBean_SetupLeavesStill(this); + } +} + +void ObjBean_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 path; + s32 linkAge; + ObjBean* this = (ObjBean*)thisx; + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) || (mREG(1) == 1)) { + path = (this->dyna.actor.params >> 8) & 0x1F; + if (path == 0x1F) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "No path data?" + osSyncPrintf("パスデータが無い?(%s %d)(arg_data %xH)\n", "../z_obj_bean.c", 909, + this->dyna.actor.params); + osSyncPrintf(VT_RST); + Actor_Kill(&this->dyna.actor); + return; + } + if (globalCtx->setupPathList[path].count < 3) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "Incorrect number of path data" + osSyncPrintf("パスデータ数が不正(%s %d)(arg_data %xH)\n", "../z_obj_bean.c", 921, + this->dyna.actor.params); + osSyncPrintf(VT_RST); + Actor_Kill(&this->dyna.actor); + return; + } + ObjBean_SetupPathCount(this, globalCtx); + ObjBean_SetupPath(this, globalCtx); + ObjBean_Move(this); + ObjBean_SetupWaitForPlayer(this); + + ObjBean_InitDynaPoly(this, globalCtx, &gMagicBeanPlatformCol, DPM_UNK3); + this->stateFlags |= BEAN_STATE_DYNAPOLY_SET; + ObjBean_InitCollider(&this->dyna.actor, globalCtx); + this->stateFlags |= BEAN_STATE_COLLIDER_SET; + + ActorShape_Init(&this->dyna.actor.shape, 0.0f, ActorShadow_DrawCircle, 8.8f); + ObjBean_FindFloor(this, globalCtx); + this->unk_1F6 = this->dyna.actor.home.rot.z & 3; + } else { + Actor_Kill(&this->dyna.actor); + return; + } + } else if ((Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F) != 0) || (mREG(1) == 1)) { + ObjBean_SetupWaitForWater(this); + } else { + ObjBean_SetupWaitForBean(this); + } + this->dyna.actor.world.rot.z = this->dyna.actor.home.rot.z = this->dyna.actor.shape.rot.z = 0; + // "Magic bean tree lift" + osSyncPrintf("(魔法の豆の木リフト)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void ObjBean_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjBean* this = (ObjBean*)thisx; + + if (this->stateFlags & BEAN_STATE_DYNAPOLY_SET) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + if (this->stateFlags & BEAN_STATE_COLLIDER_SET) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } + if (D_80B90E30 == this) { + D_80B90E30 = NULL; + } +} + +void ObjBean_SetupWaitForBean(ObjBean* this) { + this->actionFunc = ObjBean_WaitForBean; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES); + this->dyna.actor.textId = 0x2F; +} + +void ObjBean_WaitForBean(ObjBean* this, GlobalContext* globalCtx) { + if (Actor_ProcessTalkRequest(&this->dyna.actor, globalCtx)) { + if (func_8002F368(globalCtx) == EXCH_ITEM_BEAN) { + func_80B8FE00(this); + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + } + } else { + func_8002F298(&this->dyna.actor, globalCtx, 40.0f, EXCH_ITEM_BEAN); + } +} + +void func_80B8FE00(ObjBean* this) { + this->actionFunc = func_80B8FE3C; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES); + this->timer = 60; +} + +// Link is looking at the soft soil +void func_80B8FE3C(ObjBean* this, GlobalContext* globalCtx) { + if (this->timer <= 0) { + func_80B8FE6C(this); + } +} + +void func_80B8FE6C(ObjBean* this) { + this->actionFunc = func_80B8FEAC; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_SOIL); + Actor_SetScale(&this->dyna.actor, 0.01f); +} + +// The leaves are visable and growing +void func_80B8FEAC(ObjBean* this, GlobalContext* globalCtx) { + s32 temp_v1 = true; + + temp_v1 &= Math_StepToF(&this->dyna.actor.scale.y, 0.16672663f, 0.01f); + temp_v1 &= Math_StepToF(&this->dyna.actor.scale.x, 0.03569199f, 0.00113f); + + this->dyna.actor.scale.z = this->dyna.actor.scale.x; + if (temp_v1) { + if (this->timer <= 0) { + func_80B8FF50(this); + } + } else { + this->timer = 1; + } + func_8002F974(&this->dyna.actor, NA_SE_PL_PLANT_GROW_UP - SFX_FLAG); +} + +void func_80B8FF50(ObjBean* this) { + this->actionFunc = func_80B8FF8C; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_SOIL); + this->unk_1B6.x = 0x33E9; +} + +void func_80B8FF8C(ObjBean* this, GlobalContext* globalCtx) { + this->unk_1B6.x -= 0x960; + this->dyna.actor.scale.y = Math_SinS(this->unk_1B6.x) * 0.17434467f; + this->dyna.actor.scale.x = this->dyna.actor.scale.z = Math_CosS(this->unk_1B6.x) * 0.12207746f; + if (this->unk_1B6.x < 0x18E4) { + func_80B90010(this); + } +} + +void func_80B90010(ObjBean* this) { + this->actionFunc = func_80B90050; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_SOIL); + this->unk_1B6.x = 0; + this->unk_1B6.y = 0xBB8; +} + +// Control is returned to the player and the leaves start to flatten out +void func_80B90050(ObjBean* this, GlobalContext* globalCtx) { + s16 temp_a0; + f32 temp_f2; + + this->unk_1B6.x += 0x3E80; + this->unk_1B6.y += -0xC8; + temp_a0 = 6372.0f - Math_SinS(this->unk_1B6.x) * this->unk_1B6.y; + + this->dyna.actor.scale.y = Math_SinS(temp_a0) * 0.17434467f; + this->dyna.actor.scale.x = this->dyna.actor.scale.z = Math_CosS(temp_a0) * 0.12207746f; + if (this->unk_1B6.y < 0) { + ObjBean_SetupWaitForWater(this); + } +} + +void ObjBean_SetupWaitForWater(ObjBean* this) { + this->actionFunc = ObjBean_WaitForWater; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_SOIL); + Actor_SetScale(&this->dyna.actor, 0.1f); + ObjBean_SetupLeavesStill(this); +} + +void ObjBean_WaitForWater(ObjBean* this, GlobalContext* globalCtx) { + this->transformFunc(this); + + if (!(this->stateFlags & BEAN_STATE_BEEN_WATERED) && Flags_GetEnv(globalCtx, 5) && (D_80B90E30 == NULL) && + (this->dyna.actor.xzDistToPlayer < 50.0f)) { + ObjBean_SetupGrowWaterPhase1(this); + D_80B90E30 = this; + OnePointCutscene_Init(globalCtx, 2210, -99, &this->dyna.actor, MAIN_CAM); + this->dyna.actor.flags |= ACTOR_FLAG_4; + return; + } + + if ((D_80B90E30 == this) && !Flags_GetEnv(globalCtx, 5)) { + D_80B90E30 = NULL; + if (D_80B90E30) {} + } +} + +void ObjBean_SetupGrowWaterPhase1(ObjBean* this) { + this->actionFunc = ObjBean_GrowWaterPhase1; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_SOIL); + ObjBean_SetupShakeLeavesFast(this); + this->timer = 50; +} + +// Camera moves and leaves move quickly +void ObjBean_GrowWaterPhase1(ObjBean* this, GlobalContext* globalCtx) { + this->transformFunc(this); + if (this->timer <= 0) { + ObjBean_SetupGrowWaterPhase2(this); + } +} + +void ObjBean_SetupGrowWaterPhase2(ObjBean* this) { + this->actionFunc = ObjBean_GrowWaterPhase2; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_SOIL | BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_STALK); + this->stalkSizeMultiplier = 0.0f; +} + +// BeanStalk is visable and is growing +void ObjBean_GrowWaterPhase2(ObjBean* this, GlobalContext* globalCtx) { + this->transformFunc(this); + this->stalkSizeMultiplier += 0.001f; + this->dyna.actor.shape.rot.y = this->dyna.actor.home.rot.y + (s16)(this->stalkSizeMultiplier * 700000.0f); + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + this->stalkSizeMultiplier * 800.0f; + if (this->stalkSizeMultiplier >= 0.1f) { // 100 Frames + ObjBean_SetupGrowWaterPhase3(this); + } + func_8002F974(&this->dyna.actor, NA_SE_PL_PLANT_TALLER - SFX_FLAG); +} + +void ObjBean_SetupGrowWaterPhase3(ObjBean* this) { + this->actionFunc = ObjBean_GrowWaterPhase3; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_SOIL | BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_STALK); + this->timer = 60; +} + +// Fully grown and drops items +void ObjBean_GrowWaterPhase3(ObjBean* this, GlobalContext* globalCtx) { + s32 i; + Vec3f itemDropPos; + + this->transformFunc(this); + if (this->timer == 40) { + ObjBean_SetupFlattenLeaves(this); + } else if (this->timer == 30) { + if (!(this->stateFlags & BEAN_STATE_BEEN_WATERED)) { + itemDropPos.x = this->dyna.actor.world.pos.x; + itemDropPos.y = this->dyna.actor.world.pos.y - 25.0f; + itemDropPos.z = this->dyna.actor.world.pos.z; + for (i = 0; i < 3; i++) { + Item_DropCollectible(globalCtx, &itemDropPos, ITEM00_FLEXIBLE); + } + this->stateFlags |= BEAN_STATE_BEEN_WATERED; + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BUTTERFRY_TO_FAIRY); + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } else if (this->timer <= 0) { + ObjBean_SetupGrowWaterPhase4(this); + } +} + +void ObjBean_SetupGrowWaterPhase4(ObjBean* this) { + this->actionFunc = ObjBean_GrowWaterPhase4; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_SOIL | BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_STALK); + ObjBean_SetupGrow(this); +} + +// Return control back to the player and start to shrink back down +void ObjBean_GrowWaterPhase4(ObjBean* this, GlobalContext* globalCtx) { + this->transformFunc(this); + this->stalkSizeMultiplier -= 0.001f; + this->dyna.actor.shape.rot.y = this->dyna.actor.home.rot.y + (s16)(this->stalkSizeMultiplier * 700000.0f); + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + (this->stalkSizeMultiplier * 800.0f); + if (this->stalkSizeMultiplier <= 0.0f) { + this->stalkSizeMultiplier = 0.0f; + this->dyna.actor.shape.rot.y = this->dyna.actor.home.rot.y; + ObjBean_SetupGrowWaterPhase5(this); + } +} + +void ObjBean_SetupGrowWaterPhase5(ObjBean* this) { + this->actionFunc = ObjBean_GrowWaterPhase5; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_LEAVES | BEAN_STATE_DRAW_SOIL); + this->timer = 30; +} + +void ObjBean_GrowWaterPhase5(ObjBean* this, GlobalContext* globalCtx) { + this->transformFunc(this); + if (this->timer <= 0) { + func_80B8FF50(this); + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + } +} + +void ObjBean_SetupWaitForPlayer(ObjBean* this) { + this->actionFunc = ObjBean_WaitForPlayer; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_PLANT); +} + +void ObjBean_WaitForPlayer(ObjBean* this, GlobalContext* globalCtx) { + if (func_8004356C(&this->dyna)) { // Player is standing on + ObjBean_SetupFly(this); + if (globalCtx->sceneNum == SCENE_SPOT10) { // Lost woods + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_BEAN_LOST_WOODS); + } else { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_BEAN_GENERIC); + } + } + ObjBean_UpdatePosition(this); +} + +void ObjBean_SetupFly(ObjBean* this) { + this->actionFunc = ObjBean_Fly; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_PLANT); + this->dyna.actor.speedXZ = 0.0f; + this->dyna.actor.flags |= ACTOR_FLAG_4; // Never stop updating +} + +void ObjBean_Fly(ObjBean* this, GlobalContext* globalCtx) { + Camera* camera; + + ObjBean_FollowPath(this, globalCtx); + if (this->currentPointIndex == this->pathCount) { + ObjBean_SetupPathCount(this, globalCtx); + ObjBean_SetupPath(this, globalCtx); + ObjBean_SetupWaitForStepOff(this); + + this->dyna.actor.flags &= ~ACTOR_FLAG_4; // Never stop updating (disable) + camera = globalCtx->cameraPtrs[MAIN_CAM]; + + if ((camera->setting == CAM_SET_BEAN_LOST_WOODS) || (camera->setting == CAM_SET_BEAN_GENERIC)) { + Camera_ChangeSetting(camera, CAM_SET_NORMAL0); + } + + } else if (func_8004356C(&this->dyna) != 0) { // Player is on top + + func_8002F974(&this->dyna.actor, NA_SE_PL_PLANT_MOVE - SFX_FLAG); + + if (globalCtx->sceneNum == SCENE_SPOT10) { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_BEAN_LOST_WOODS); + } else { + Camera_ChangeSetting(globalCtx->cameraPtrs[MAIN_CAM], CAM_SET_BEAN_GENERIC); + } + } else if (this->stateFlags & BEAN_STATE_PLAYER_ON_TOP) { + camera = globalCtx->cameraPtrs[MAIN_CAM]; + + if ((camera->setting == CAM_SET_BEAN_LOST_WOODS) || (camera->setting == CAM_SET_BEAN_GENERIC)) { + Camera_ChangeSetting(camera, CAM_SET_NORMAL0); + } + } + + ObjBean_UpdatePosition(this); +} + +void ObjBean_SetupWaitForStepOff(ObjBean* this) { + this->actionFunc = ObjBean_WaitForStepOff; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_PLANT); +} + +void ObjBean_WaitForStepOff(ObjBean* this, GlobalContext* globalCtx) { + if (!func_80043590(&this->dyna)) { + ObjBean_SetupWaitForPlayer(this); + } + ObjBean_UpdatePosition(this); +} + +void func_80B908EC(ObjBean* this) { + this->actionFunc = func_80B90918; + ObjBean_SetDrawMode(this, 0); +} + +void func_80B90918(ObjBean* this, GlobalContext* globalCtx) { + if (!func_8004356C(&this->dyna)) { + ObjBean_SetupPathCount(this, globalCtx); + ObjBean_SetupPath(this, globalCtx); + ObjBean_Move(this); + func_80B90970(this); + } +} + +void func_80B90970(ObjBean* this) { + this->actionFunc = func_80B909B0; + ObjBean_SetDrawMode(this, 0); + this->timer = 100; + func_80B8EDF4(this); +} + +void func_80B909B0(ObjBean* this, GlobalContext* globalCtx) { + if (ObjBean_CheckForHorseTrample(this, globalCtx)) { + this->timer = 100; + } else if (this->timer <= 0) { + func_80B909F8(this); + } +} + +void func_80B909F8(ObjBean* this) { + this->actionFunc = func_80B90A34; + ObjBean_SetDrawMode(this, BEAN_STATE_DRAW_PLANT); + this->timer = 30; +} + +void func_80B90A34(ObjBean* this, GlobalContext* globalCtx) { + s32 trampled = ObjBean_CheckForHorseTrample(this, globalCtx); + + func_80B8EE24(this); + if (trampled) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } + if ((this->timer <= 0) && (!trampled)) { + func_80B8EBC8(this); + ObjBean_SetupWaitForPlayer(this); + } +} +void ObjBean_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjBean* this = (ObjBean*)thisx; + + if (this->timer > 0) { + this->timer--; + } + + this->actionFunc(this, globalCtx); + + if (this->stateFlags & BEAN_STATE_DRAW_PLANT) { + ObjBean_Move(this); + if (this->dyna.actor.xzDistToPlayer < 150.0f) { + this->collider.dim.radius = this->dyna.actor.scale.x * 640.0f + 0.5f; + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + ObjBean_FindFloor(this, globalCtx); + + this->dyna.actor.shape.shadowDraw = ActorShadow_DrawCircle; + this->dyna.actor.shape.shadowScale = this->dyna.actor.scale.x * 88.0f; + + if (ObjBean_CheckForHorseTrample(this, globalCtx)) { + osSyncPrintf(VT_FGCOL(CYAN)); + // "Horse and bean tree lift collision" + osSyncPrintf("馬と豆の木リフト衝突!!!\n"); + osSyncPrintf(VT_RST); + ObjBean_Break(this, globalCtx); + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + func_80B908EC(this); + } + } else { + this->dyna.actor.shape.shadowDraw = NULL; + } + Actor_SetFocus(&this->dyna.actor, 6.0f); + if (this->stateFlags & BEAN_STATE_DYNAPOLY_SET) { + if (func_8004356C(&this->dyna)) { + this->stateFlags |= BEAN_STATE_PLAYER_ON_TOP; + } else { + this->stateFlags &= ~BEAN_STATE_PLAYER_ON_TOP; + } + } +} + +void ObjBean_DrawSoftSoilSpot(ObjBean* this, GlobalContext* globalCtx) { + Matrix_Translate(this->dyna.actor.home.pos.x, this->dyna.actor.home.pos.y, this->dyna.actor.home.pos.z, + MTXMODE_NEW); + Matrix_RotateY(this->dyna.actor.home.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + Gfx_DrawDListOpa(globalCtx, gMagicBeanSoftSoilDL); +} + +void ObjBean_DrawBeanstalk(ObjBean* this, GlobalContext* globalCtx) { + Matrix_Translate(this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + MTXMODE_NEW); + Matrix_RotateY(this->dyna.actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Scale(0.1f, this->stalkSizeMultiplier, 0.1f, MTXMODE_APPLY); + Gfx_DrawDListOpa(globalCtx, gMagicBeanStemDL); +} + +void ObjBean_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjBean* this = (ObjBean*)thisx; + + if (this->stateFlags & BEAN_STATE_DRAW_SOIL) { + Gfx_DrawDListOpa(globalCtx, gMagicBeanSeedlingDL); + } + if (this->stateFlags & BEAN_STATE_DRAW_PLANT) { + Gfx_DrawDListOpa(globalCtx, gMagicBeanPlatformDL); + } + if (this->stateFlags & BEAN_STATE_DRAW_LEAVES) { + ObjBean_DrawSoftSoilSpot(this, globalCtx); + } + if (this->stateFlags & BEAN_STATE_DRAW_STALK) { + ObjBean_DrawBeanstalk(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Obj_Bean/z_obj_bean.h b/soh/src/overlays/actors/ovl_Obj_Bean/z_obj_bean.h new file mode 100644 index 000000000..aca116f15 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Bean/z_obj_bean.h @@ -0,0 +1,41 @@ +#ifndef Z_OBJ_BEAN_H +#define Z_OBJ_BEAN_H + +#include "ultra64.h" +#include "global.h" + +struct ObjBean; + +typedef void (*ObjBeanActionFunc)(struct ObjBean*, GlobalContext*); +typedef void (*ObjBeanTransformFunc)(struct ObjBean*); + +typedef struct ObjBean { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjBeanActionFunc actionFunc; + /* 0x0168 */ ColliderCylinder collider; + /* 0x01B4 */ s16 timer; + /* 0x01B6 */ Vec3s unk_1B6; + /* 0x01BC */ ObjBeanTransformFunc transformFunc; + /* 0x01C0 */ s16 unk_1C0; + /* 0x01C2 */ s16 unk_1C2; + /* 0x01C4 */ s16 unk_1C4; + /* 0x01C6 */ s16 unk_1C6; + /* 0x01C8 */ s16 unk_1C8; + /* 0x01CA */ s16 unk_1CA; + /* 0x01CC */ s16 unk_1CC; + /* 0x01CE */ s16 unk_1CE; + /* 0x01D0 */ s16 leafRotFactor; + /* 0x01D2 */ u16 unk_1D2; + /* 0x01D4 */ f32 stalkSizeMultiplier; + /* 0x01D8 */ Vec3f pathPoints; + /* 0x01E4 */ f32 unk_1E4; + /* 0x01E8 */ f32 posOffsetX; + /* 0x01EC */ f32 posOffsetZ; + /* 0x01F0 */ s16 pathCount; + /* 0x01F2 */ s16 currentPointIndex; + /* 0x01F4 */ s16 nextPointIndex; + /* 0x01F6 */ u8 unk_1F6; + /* 0x01F7 */ u8 stateFlags; +} ObjBean; // size = 0x01F8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Blockstop/z_obj_blockstop.c b/soh/src/overlays/actors/ovl_Obj_Blockstop/z_obj_blockstop.c new file mode 100644 index 000000000..660fa144b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Blockstop/z_obj_blockstop.c @@ -0,0 +1,65 @@ +/* + * File: z_obj_blockstop.c + * Overlay: ovl_Obj_Blockstop + * Description: Stops blocks and sets relevant flags when the block is in position. + */ + +#include "z_obj_blockstop.h" +#include "overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h" + +#define FLAGS 0 + +void ObjBlockstop_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjBlockstop_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjBlockstop_Update(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Obj_Blockstop_InitVars = { + ACTOR_OBJ_BLOCKSTOP, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjBlockstop), + (ActorFunc)ObjBlockstop_Init, + (ActorFunc)ObjBlockstop_Destroy, + (ActorFunc)ObjBlockstop_Update, + NULL, + NULL, +}; + +void ObjBlockstop_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjBlockstop* this = (ObjBlockstop*)thisx; + + if (Flags_GetSwitch(globalCtx, this->actor.params)) { + Actor_Kill(&this->actor); + } else { + this->actor.world.pos.y++; + } +} + +void ObjBlockstop_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void ObjBlockstop_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjBlockstop* this = (ObjBlockstop*)thisx; + DynaPolyActor* dynaPolyActor; + Vec3f sp4C; + s32 bgId; + s32 pad; + + if (BgCheck_EntityLineTest2(&globalCtx->colCtx, &this->actor.home.pos, &this->actor.world.pos, &sp4C, + &this->actor.floorPoly, false, false, true, true, &bgId, &this->actor)) { + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, bgId); + + if (dynaPolyActor != NULL && dynaPolyActor->actor.id == ACTOR_OBJ_OSHIHIKI) { + if ((dynaPolyActor->actor.params & 0x000F) == PUSHBLOCK_HUGE_START_ON || + (dynaPolyActor->actor.params & 0x000F) == PUSHBLOCK_HUGE_START_OFF) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } else { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + + Flags_SetSwitch(globalCtx, this->actor.params); + Actor_Kill(&this->actor); + } + } +} diff --git a/soh/src/overlays/actors/ovl_Obj_Blockstop/z_obj_blockstop.h b/soh/src/overlays/actors/ovl_Obj_Blockstop/z_obj_blockstop.h new file mode 100644 index 000000000..f6ff149e9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Blockstop/z_obj_blockstop.h @@ -0,0 +1,13 @@ +#ifndef Z_OBJ_BLOCKSTOP_H +#define Z_OBJ_BLOCKSTOP_H + +#include "ultra64.h" +#include "global.h" + +struct ObjBlockstop; + +typedef struct ObjBlockstop { + /* 0x0000 */ Actor actor; +} ObjBlockstop; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Bombiwa/z_obj_bombiwa.c b/soh/src/overlays/actors/ovl_Obj_Bombiwa/z_obj_bombiwa.c new file mode 100644 index 000000000..0ea3eeaa4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Bombiwa/z_obj_bombiwa.c @@ -0,0 +1,148 @@ +/* + * File: z_obj_bombiwa.c + * Overlay: ovl_Obj_Bombiwa + * Description: Round, brown, breakable boulder + */ + +#include "z_obj_bombiwa.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/object_bombiwa/object_bombiwa.h" + +#define FLAGS 0 + +void ObjBombiwa_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjBombiwa_InitCollision(Actor* thisx, GlobalContext* globalCtx); +void ObjBombiwa_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjBombiwa_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjBombiwa_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjBombiwa_Break(ObjBombiwa* this, GlobalContext* globalCtx); + +const ActorInit Obj_Bombiwa_InitVars = { + ACTOR_OBJ_BOMBIWA, + ACTORCAT_PROP, + FLAGS, + OBJECT_BOMBIWA, + sizeof(ObjBombiwa), + (ActorFunc)ObjBombiwa_Init, + (ActorFunc)ObjBombiwa_Destroy, + (ActorFunc)ObjBombiwa_Update, + (ActorFunc)ObjBombiwa_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC1FFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 55, 70, 0, { 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 350, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +static s16 sEffectScales[] = { + 17, 14, 10, 8, 7, 5, 3, 2, +}; + +void ObjBombiwa_InitCollision(Actor* thisx, GlobalContext* globalCtx) { + ObjBombiwa* this = (ObjBombiwa*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); +} + +void ObjBombiwa_Init(Actor* thisx, GlobalContext* globalCtx) { + Actor_ProcessInitChain(thisx, sInitChain); + ObjBombiwa_InitCollision(thisx, globalCtx); + if ((Flags_GetSwitch(globalCtx, thisx->params & 0x3F) != 0)) { + Actor_Kill(thisx); + } else { + CollisionCheck_SetInfo(&thisx->colChkInfo, NULL, &sColChkInfoInit); + if (thisx->shape.rot.y == 0) { + s16 rand = (s16)Rand_ZeroFloat(65536.0f); + + thisx->world.rot.y = rand; + thisx->shape.rot.y = rand; + } + thisx->shape.yOffset = -200.0f; + thisx->world.pos.y = thisx->home.pos.y + 20.0f; + } +} + +void ObjBombiwa_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjBombiwa* this = (ObjBombiwa*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void ObjBombiwa_Break(ObjBombiwa* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + Gfx* dlist; + s16 arg5; + s16 scale; + s32 i; + + dlist = object_bombiwa_DL_0009E0; + for (i = 0; i < ARRAY_COUNT(sEffectScales); i++) { + pos.x = ((Rand_ZeroOne() - 0.5f) * 10.0f) + this->actor.home.pos.x; + pos.y = ((Rand_ZeroOne() * 5.0f) + this->actor.home.pos.y) + 8.0f; + pos.z = ((Rand_ZeroOne() - 0.5f) * 10.0f) + this->actor.home.pos.z; + velocity.x = (Rand_ZeroOne() - 0.5f) * 15.0f; + velocity.y = (Rand_ZeroOne() * 16.0f) + 5.0f; + velocity.z = (Rand_ZeroOne() - 0.5f) * 15.0f; + scale = sEffectScales[i]; + arg5 = (scale >= 11) ? 37 : 33; + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -400, arg5, 10, 2, 0, scale, 1, 0, 80, KAKERA_COLOR_NONE, + OBJECT_BOMBIWA, dlist); + } + func_80033480(globalCtx, &this->actor.world.pos, 60.0f, 8, 100, 160, 1); +} + +void ObjBombiwa_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjBombiwa* this = (ObjBombiwa*)thisx; + s32 pad; + + if ((func_80033684(globalCtx, &this->actor) != NULL) || + ((this->collider.base.acFlags & AC_HIT) && (this->collider.info.acHitInfo->toucher.dmgFlags & 0x40000040))) { + ObjBombiwa_Break(this, globalCtx); + Flags_SetSwitch(globalCtx, this->actor.params & 0x3F); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 80, NA_SE_EV_WALL_BROKEN); + if (((this->actor.params >> 0xF) & 1) != 0) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + } + Actor_Kill(&this->actor); + } else { + this->collider.base.acFlags &= ~AC_HIT; + if (this->actor.xzDistToPlayer < 800.0f) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +void ObjBombiwa_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, object_bombiwa_DL_0009E0); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Bombiwa/z_obj_bombiwa.h b/soh/src/overlays/actors/ovl_Obj_Bombiwa/z_obj_bombiwa.h new file mode 100644 index 000000000..33597276b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Bombiwa/z_obj_bombiwa.h @@ -0,0 +1,14 @@ +#ifndef Z_OBJ_BOMBIWA_H +#define Z_OBJ_BOMBIWA_H + +#include "ultra64.h" +#include "global.h" + +struct ObjBombiwa; + +typedef struct ObjBombiwa { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; +} ObjBombiwa; // size = 0x0198 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c b/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c new file mode 100644 index 000000000..039fa6ef5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.c @@ -0,0 +1,228 @@ +/* + * File: z_obj_comb.c + * Overlay: ovl_Obj_Comb + * Description: Beehive + */ + +#include "z_obj_comb.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS 0 + +void ObjComb_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjComb_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjComb_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjComb_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjComb_Break(ObjComb* this, GlobalContext* globalCtx); +void ObjComb_ChooseItemDrop(ObjComb* this, GlobalContext* globalCtx); +void ObjComb_SetupWait(ObjComb* this); +void ObjComb_Wait(ObjComb* this, GlobalContext* globalCtx); + +const ActorInit Obj_Comb_InitVars = { + ACTOR_OBJ_COMB, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(ObjComb), + (ActorFunc)ObjComb_Init, + (ActorFunc)ObjComb_Destroy, + (ActorFunc)ObjComb_Update, + (ActorFunc)ObjComb_Draw, + NULL, +}; + +static ColliderJntSphElementInit sJntSphElementsInit[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4001FFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 15 }, 100 }, + }, +}; + +static ColliderJntSphInit sJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_PLAYER, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sJntSphElementsInit, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 900, ICHAIN_STOP), +}; + +void ObjComb_Break(ObjComb* this, GlobalContext* globalCtx) { + Vec3f pos1; + Vec3f pos; + Vec3f velocity; + Gfx* dlist = gFieldBeehiveFragmentDL; + s16 scale; + s16 angle = 0; + s16 gravity; + u8 arg5; + u8 arg6; + f32 rand1; + f32 rand2; + s32 i; + + for (i = 0; i < 31; i++) { + angle += 0x4E20; + rand1 = Rand_ZeroOne() * 10.0f; + + pos1.x = Math_SinS(angle) * rand1; + pos1.y = (i - 15) * 0.7f; + pos1.z = Math_CosS(angle) * rand1; + + Math_Vec3f_Sum(&pos1, &this->actor.world.pos, &pos); + + velocity.x = (Rand_ZeroOne() - 0.5f) + pos1.x * 0.5f; + velocity.y = (Rand_ZeroOne() - 0.5f) + pos1.y * 0.6f; + velocity.z = (Rand_ZeroOne() - 0.5f) + pos1.z * 0.5f; + + scale = Rand_ZeroOne() * 72.0f + 25.0f; + + if (scale < 40) { + gravity = -200; + arg6 = 40; + } else if (scale < 70) { + gravity = -280; + arg6 = 30; + } else { + gravity = -340; + arg6 = 20; + } + + rand2 = Rand_ZeroOne(); + + if (rand2 < 0.1f) { + arg5 = 96; + } else if (rand2 < 0.8f) { + arg5 = 64; + } else { + arg5 = 32; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, gravity, arg5, arg6, 4, 0, scale, 0, 0, 80, + KAKERA_COLOR_NONE, OBJECT_GAMEPLAY_FIELD_KEEP, dlist); + } + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y - 10.0f; + pos.z = this->actor.world.pos.z; + func_80033480(globalCtx, &pos, 40.0f, 6, 70, 60, 1); +} + +void ObjComb_ChooseItemDrop(ObjComb* this, GlobalContext* globalCtx) { + s16 params = this->actor.params & 0x1F; + + if ((params > 0) || (params < 0x1A)) { + if (params == 6) { + if (Flags_GetCollectible(globalCtx, (this->actor.params >> 8) & 0x3F)) { + params = -1; + } else { + params = (params | (((this->actor.params >> 8) & 0x3F) << 8)); + } + } else if (Rand_ZeroOne() < 0.5f) { + params = -1; + } + if (params >= 0) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, params); + } + } +} + +void ObjComb_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjComb* this = (ObjComb*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sJntSphInit, this->colliderItems); + ObjComb_SetupWait(this); +} + +void ObjComb_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjComb* this = (ObjComb*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void ObjComb_SetupWait(ObjComb* this) { + this->actionFunc = ObjComb_Wait; +} + +void ObjComb_Wait(ObjComb* this, GlobalContext* globalCtx) { + s32 dmgFlags; + + this->unk_1B0 -= 50; + if (this->unk_1B0 < 0) { + this->unk_1B0 = 0; + } + + if ((this->collider.base.acFlags & AC_HIT) != 0) { + this->collider.base.acFlags &= ~AC_HIT; + dmgFlags = this->collider.elements[0].info.acHitInfo->toucher.dmgFlags; + if (dmgFlags & 0x4001F866) { + this->unk_1B0 = 1500; + } else { + ObjComb_Break(this, globalCtx); + ObjComb_ChooseItemDrop(this, globalCtx); + Actor_Kill(&this->actor); + } + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + + if (this->actor.update != NULL) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void ObjComb_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjComb* this = (ObjComb*)thisx; + + this->unk_1B2 += 0x2EE0; + this->actionFunc(this, globalCtx); + this->actor.shape.rot.x = Math_SinS(this->unk_1B2) * this->unk_1B0 + this->actor.home.rot.x; +} + +void ObjComb_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjComb* this = (ObjComb*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_comb.c", 369); + + func_80093D18(globalCtx->state.gfxCtx); + + Matrix_Translate(this->actor.world.pos.x, this->actor.world.pos.y + (118.0f * this->actor.scale.y), + this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateY(this->actor.shape.rot.y * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(this->actor.shape.rot.x * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(this->actor.shape.rot.z * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_Translate(0, -(this->actor.scale.y * 118.0f), 0, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_comb.c", 394), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, gFieldBeehiveDL); + + Collider_UpdateSpheres(0, &this->collider); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_comb.c", 402); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.h b/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.h new file mode 100644 index 000000000..bb690b0b6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Comb/z_obj_comb.h @@ -0,0 +1,20 @@ +#ifndef Z_OBJ_COMB_H +#define Z_OBJ_COMB_H + +#include "ultra64.h" +#include "global.h" + +struct ObjComb; + +typedef void (*ObjCombActionFunc)(struct ObjComb*, GlobalContext*); + +typedef struct ObjComb { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjCombActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItems[1]; + /* 0x01B0 */ s16 unk_1B0; + /* 0x01B2 */ s16 unk_1B2; +} ObjComb; // size = 0x01B4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Dekujr/z_obj_dekujr.c b/soh/src/overlays/actors/ovl_Obj_Dekujr/z_obj_dekujr.c new file mode 100644 index 000000000..ba1bdf87d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Dekujr/z_obj_dekujr.c @@ -0,0 +1,171 @@ +/* + * File: z_obj_dekujr.c + * Overlay: ovl_Obj_Dekujr + * Description: Deku Tree Sprout + */ + +#include "z_obj_dekujr.h" +#include "objects/object_dekujr/object_dekujr.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void ObjDekujr_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjDekujr_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjDekujr_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjDekujr_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjDekujr_ComeUp(ObjDekujr* this, GlobalContext* globalCtx); + +const ActorInit Obj_Dekujr_InitVars = { + ACTOR_OBJ_DEKUJR, + ACTORCAT_NPC, + FLAGS, + OBJECT_DEKUJR, + sizeof(ObjDekujr), + (ActorFunc)ObjDekujr_Init, + (ActorFunc)ObjDekujr_Destroy, + (ActorFunc)ObjDekujr_Update, + (ActorFunc)ObjDekujr_Draw, + NULL, +}; + +static ColliderCylinderInitToActor sCylinderInit = { + { + NULL, + 0x00, + 0x00, + 0x39, + COLSHAPE_CYLINDER, + }, + { 0x02, { 0x00000000, 0x00, 0x00 }, { 0xFFCFFFFF, 0x00, 0x00 }, 0x00, 0x00, 0x01 }, + { 60, 80, 0, { 0, 0, 0 } }, +}; + +void ObjDekujr_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjDekujr* this = (ObjDekujr*)thisx; + s32 pad; + + if (gSaveContext.cutsceneIndex < 0xFFF0) { + if (!LINK_IS_ADULT) { + Actor_Kill(thisx); + return; + } + this->unk_19C = 2; + this->unk_19B = 0; + } else { + this->unk_19C = 0; + this->unk_19B = 1; + } + if (!CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { + Actor_Kill(thisx); + } else { + ActorShape_Init(&thisx->shape, 0.0f, NULL, 0.0f); + Collider_InitCylinder(globalCtx, &this->collider); + sCylinderInit.base.actor = thisx; + Collider_SetCylinderToActor(globalCtx, &this->collider, &sCylinderInit); + thisx->colChkInfo.mass = MASS_IMMOVABLE; + thisx->textId = func_80037C30(globalCtx, 0xF); + Actor_SetScale(thisx, 0.4f); + } +} + +void ObjDekujr_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void ObjDekujr_SetInitialPos(CsCmdActorAction* npcAction, Vec3f* initPos) { + initPos->x = npcAction->startPos.x; + initPos->y = npcAction->startPos.y; + initPos->z = npcAction->startPos.z; +} + +void ObjDekujr_SetFinalPos(CsCmdActorAction* npcAction, Vec3f* finalPos) { + finalPos->x = npcAction->endPos.x; + finalPos->y = npcAction->endPos.y; + finalPos->z = npcAction->endPos.z; +} + +void ObjDekujr_ComeUp(ObjDekujr* this, GlobalContext* globalCtx) { + CsCmdActorAction* csCmdNPCAction; + Vec3f initPos; + Vec3f finalPos; + Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + f32 actionLength; + f32 gravity; + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + this->unk_19C = 2; + this->unk_19B = 0; + } else { + if (globalCtx->csCtx.frames == 351) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_COME_UP_DEKU_JR); + } + csCmdNPCAction = globalCtx->csCtx.npcActions[1]; + if (csCmdNPCAction != NULL) { + ObjDekujr_SetInitialPos(csCmdNPCAction, &initPos); + ObjDekujr_SetFinalPos(csCmdNPCAction, &finalPos); + if (this->unk_19C == 0) { + this->actor.world.pos = initPos; + this->unk_19C = 1; + } + this->actor.shape.rot.x = csCmdNPCAction->urot.x; + this->actor.shape.rot.y = csCmdNPCAction->urot.y; + this->actor.shape.rot.z = csCmdNPCAction->urot.z; + this->actor.velocity = velocity; + if (csCmdNPCAction->endFrame >= globalCtx->csCtx.frames) { + actionLength = csCmdNPCAction->endFrame - csCmdNPCAction->startFrame; + this->actor.velocity.x = (finalPos.x - initPos.x) / actionLength; + gravity = this->actor.gravity; + this->actor.velocity.y = (finalPos.y - initPos.y) / actionLength; + this->actor.velocity.y += gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } + this->actor.velocity.z = (finalPos.z - initPos.z) / actionLength; + } + } + } +} + +void ObjDekujr_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjDekujr* this = (ObjDekujr*)thisx; + s32 pad; + + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if ((gSaveContext.cutsceneIndex >= 0xFFF0) && (this->unk_19B == 0)) { + this->unk_19C = 0; + this->unk_19B = 1; + } + if (this->unk_19B == 1) { + ObjDekujr_ComeUp(this, globalCtx); + this->actor.world.pos.x += this->actor.velocity.x; + this->actor.world.pos.y += this->actor.velocity.y; + this->actor.world.pos.z += this->actor.velocity.z; + } else { + func_80037D98(globalCtx, &this->actor, 0xF, &this->unk_1A0); + Actor_SetFocus(&this->actor, 40.0f); + } +} + +void ObjDekujr_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 frameCount; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_dekujr.c", 370); + + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_dekujr.c", 379), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_dekujr_DL_0030D0); + + frameCount = globalCtx->state.frames; + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, frameCount % 128, 0, 32, 32, 1, frameCount % 128, 0, 32, 32)); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_dekujr.c", 399), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_dekujr_DL_0032D8); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_dekujr.c", 409); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Dekujr/z_obj_dekujr.h b/soh/src/overlays/actors/ovl_Obj_Dekujr/z_obj_dekujr.h new file mode 100644 index 000000000..88818776a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Dekujr/z_obj_dekujr.h @@ -0,0 +1,19 @@ +#ifndef Z_OBJ_DEKUJR_H +#define Z_OBJ_DEKUJR_H + +#include "ultra64.h" +#include "global.h" + +struct ObjDekujr; + +typedef struct ObjDekujr { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ char unk_198[0x3]; + /* 0x019B */ u8 unk_19B; + /* 0x019C */ u8 unk_19C; + /* 0x019D */ char unk_19D[0x3]; + /* 0x01A0 */ s32 unk_1A0; +} ObjDekujr; // size = 0x01A4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Elevator/z_obj_elevator.c b/soh/src/overlays/actors/ovl_Obj_Elevator/z_obj_elevator.c new file mode 100644 index 000000000..b7b9cbdc1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Elevator/z_obj_elevator.c @@ -0,0 +1,126 @@ +/* + * File: z_obj_elevator.c + * Overlay: Obj_Elevator + * Description: Stone Elevator + */ + +#include "z_obj_elevator.h" +#include "objects/object_d_elevator/object_d_elevator.h" + +#define FLAGS 0 + +void ObjElevator_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjElevator_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjElevator_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjElevator_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B92C5C(ObjElevator* this); +void func_80B92C80(ObjElevator* this, GlobalContext* globalCtx); +void func_80B92D20(ObjElevator* this); +void func_80B92D44(ObjElevator* this, GlobalContext* globalCtx); + +const ActorInit Obj_Elevator_InitVars = { + ACTOR_OBJ_ELEVATOR, + ACTORCAT_BG, + FLAGS, + OBJECT_D_ELEVATOR, + sizeof(ObjElevator), + (ActorFunc)ObjElevator_Init, + (ActorFunc)ObjElevator_Destroy, + (ActorFunc)ObjElevator_Update, + (ActorFunc)ObjElevator_Draw, + NULL, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 600, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2000, ICHAIN_STOP), +}; + +static f32 sScales[] = { 0.1f, 0.05f }; + +void ObjElevator_SetupAction(ObjElevator* this, ObjElevatorActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void func_80B92B08(ObjElevator* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flag) { + s16 pad1; + CollisionHeader* colHeader = NULL; + s16 pad2; + Actor* thisx = &this->dyna.actor; + + DynaPolyActor_Init(&this->dyna, flag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, thisx, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_obj_elevator.c", 136, + thisx->id, thisx->params); + } +} + +void ObjElevator_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjElevator* this = (ObjElevator*)thisx; + f32 temp_f0; + + func_80B92B08(this, globalCtx, &object_d_elevator_Col_000360, DPM_PLAYER); + Actor_SetScale(thisx, sScales[thisx->params & 1]); + Actor_ProcessInitChain(thisx, sInitChain); + temp_f0 = (thisx->params >> 8) & 0xF; + this->unk_16C = temp_f0 + temp_f0; + func_80B92C5C(this); + osSyncPrintf("(Dungeon Elevator)(arg_data 0x%04x)\n", thisx->params); +} + +void ObjElevator_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjElevator* this = (ObjElevator*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80B92C5C(ObjElevator* this) { + ObjElevator_SetupAction(this, func_80B92C80); +} + +void func_80B92C80(ObjElevator* this, GlobalContext* globalCtx) { + f32 sub; + Actor* thisx = &this->dyna.actor; + + if ((this->dyna.unk_160 & 2) && !(this->unk_170 & 2)) { + sub = thisx->world.pos.y - thisx->home.pos.y; + if (fabsf(sub) < 0.1f) { + this->unk_168 = thisx->home.pos.y + ((thisx->params >> 0xC) & 0xF) * 80.0f; + } else { + this->unk_168 = thisx->home.pos.y; + } + func_80B92D20(this); + } +} + +void func_80B92D20(ObjElevator* this) { + ObjElevator_SetupAction(this, func_80B92D44); +} + +void func_80B92D44(ObjElevator* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + if (fabsf(Math_SmoothStepToF(&thisx->world.pos.y, this->unk_168, 1.0f, this->unk_16C, 0.0f)) < 0.001f) { + Audio_PlayActorSound2(thisx, NA_SE_EV_FOOT_SWITCH); + func_80B92C5C(this); + } else { + Audio_PlayActorSound2(thisx, NA_SE_EV_STONE_STATUE_OPEN - SFX_FLAG); + } +} + +void ObjElevator_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjElevator* this = (ObjElevator*)thisx; + + if (this->actionFunc) { + this->actionFunc(this, globalCtx); + } + this->unk_170 = this->dyna.unk_160; +} + +void ObjElevator_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, object_d_elevator_DL_000180); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Elevator/z_obj_elevator.h b/soh/src/overlays/actors/ovl_Obj_Elevator/z_obj_elevator.h new file mode 100644 index 000000000..735355f50 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Elevator/z_obj_elevator.h @@ -0,0 +1,19 @@ +#ifndef Z_OBJ_ELEVATOR_H +#define Z_OBJ_ELEVATOR_H + +#include "ultra64.h" +#include "global.h" + +struct ObjElevator; + +typedef void (*ObjElevatorActionFunc)(struct ObjElevator*, GlobalContext*); + +typedef struct ObjElevator { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjElevatorActionFunc actionFunc; + /* 0x0168 */ f32 unk_168; + /* 0x016C */ f32 unk_16C; + /* 0x0170 */ u8 unk_170; +} ObjElevator; // size = 0x0174 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.c b/soh/src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.c new file mode 100644 index 000000000..547c5a1f2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.c @@ -0,0 +1,208 @@ +/* + * File: z_obj_hamishi.c + * Overlay: ovl_Obj_Hamishi + * Description: Bronze Boulder + */ + +#include "z_obj_hamishi.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS 0 + +void ObjHamishi_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjHamishi_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjHamishi_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjHamishi_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Obj_Hamishi_InitVars = { + ACTOR_OBJ_HAMISHI, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(ObjHamishi), + (ActorFunc)ObjHamishi_Init, + (ActorFunc)ObjHamishi_Destroy, + (ActorFunc)ObjHamishi_Update, + (ActorFunc)ObjHamishi_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4FC1FFF6, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 50, 70, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE }; + +static s16 sEffectScales[] = { + 145, 135, 115, 85, 75, 53, 45, 40, 35, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 250, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 500, ICHAIN_STOP), +}; + +void ObjHamishi_InitCollision(Actor* thisx, GlobalContext* globalCtx) { + ObjHamishi* this = (ObjHamishi*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); +} + +void ObjHamishi_Shake(ObjHamishi* this) { + if (this->shakeFrames > 0) { + this->shakeFrames--; + this->shakePosPhase += 5000; + this->shakeRotPhase += 0xE10; + + Math_StepToF(&this->shakePosSize, 0.0f, 0.15f); + Math_StepToF(&this->shakeRotSize, 0.0f, 40.0f); + + this->actor.world.pos.x = this->actor.home.pos.x + (Math_SinS(this->shakePosPhase * 4) * this->shakePosSize); + this->actor.world.pos.z = this->actor.home.pos.z + (Math_CosS(this->shakePosPhase * 7) * this->shakePosSize); + this->actor.shape.rot.x = + this->actor.home.rot.x + (s16)(Math_SinS(this->shakeRotPhase * 4) * this->shakeRotSize); + this->actor.shape.rot.z = + this->actor.home.rot.z + (s16)(Math_CosS(this->shakeRotPhase * 7) * this->shakeRotSize); + } else { + Math_StepToF(&this->actor.world.pos.x, this->actor.home.pos.x, 1.0f); + Math_StepToF(&this->actor.world.pos.z, this->actor.home.pos.z, 1.0f); + Math_ScaledStepToS(&this->actor.shape.rot.x, this->actor.home.rot.x, 0xBB8); + Math_ScaledStepToS(&this->actor.shape.rot.z, this->actor.home.rot.z, 0xBB8); + } +} + +void ObjHamishi_Break(ObjHamishi* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f velocity; + Vec3f pos; + s16 phi_s0 = 1000; + s16 gravity; + s16 phi_v0; + f32 temp_f20; + f32 temp_f22; + s32 i; + + for (i = 0; i < ARRAY_COUNT(sEffectScales); i++) { + phi_s0 += 20000; + + temp_f20 = Rand_ZeroOne() * 10.0f; + pos.x = (Math_SinS(phi_s0) * temp_f20) + this->actor.world.pos.x; + pos.y = (Rand_ZeroOne() * 40.0f) + this->actor.world.pos.y + 5.0f; + pos.z = (Math_CosS(phi_s0) * temp_f20) + this->actor.world.pos.z; + + temp_f20 = (Rand_ZeroOne() * 10.0f) + 2.0f; + velocity.x = Math_SinS(phi_s0) * temp_f20; + temp_f22 = Rand_ZeroOne(); + velocity.y = (Rand_ZeroOne() * i * 2.5f) + (temp_f22 * 15.0f); + velocity.z = Math_CosS(phi_s0) * temp_f20; + + if (i == 0) { + phi_v0 = 41; + gravity = -450; + } else if (i < 4) { + phi_v0 = 37; + gravity = -380; + } else { + phi_v0 = 69; + gravity = -320; + } + + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &this->actor.world.pos, gravity, phi_v0, 30, 5, 0, + sEffectScales[i], 3, 0, 70, 1, OBJECT_GAMEPLAY_FIELD_KEEP, gSilverRockFragmentsDL); + } + + func_80033480(globalCtx, &this->actor.world.pos, 140.0f, 6, 180, 90, 1); + func_80033480(globalCtx, &this->actor.world.pos, 140.0f, 12, 80, 90, 1); +} + +void ObjHamishi_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjHamishi* this = (ObjHamishi*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + this->actor.uncullZoneForward += 1000.0f; + } + if (this->actor.shape.rot.y == 0) { + this->actor.shape.rot.y = this->actor.world.rot.y = this->actor.home.rot.y = Rand_ZeroFloat(65536.0f); + } + + ObjHamishi_InitCollision(&this->actor, globalCtx); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + + if (Flags_GetSwitch(globalCtx, this->actor.params & 0x3F)) { + Actor_Kill(&this->actor); + return; + } + + this->actor.shape.yOffset = 80.0f; +} + +void ObjHamishi_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjHamishi* this = (ObjHamishi*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void ObjHamishi_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjHamishi* this = (ObjHamishi*)thisx; + CollisionCheckContext* colChkCtx = &globalCtx->colChkCtx; + + ObjHamishi_Shake(this); + + if ((this->collider.base.acFlags & AC_HIT) && (this->collider.info.acHitInfo->toucher.dmgFlags & 0x40000040)) { + this->collider.base.acFlags &= ~AC_HIT; + this->hitCount++; + if (this->hitCount < 2) { + this->shakeFrames = 15; + this->shakePosSize = 2.0f; + this->shakeRotSize = 400.0f; + } else { + ObjHamishi_Break(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 40, NA_SE_EV_WALL_BROKEN); + Flags_SetSwitch(globalCtx, this->actor.params & 0x3F); + Actor_Kill(&this->actor); + } + } else { + this->collider.base.acFlags &= ~AC_HIT; + + if (this->actor.xzDistToPlayer < 600.0f) { + CollisionCheck_SetAC(globalCtx, colChkCtx, &this->collider.base); + CollisionCheck_SetOC(globalCtx, colChkCtx, &this->collider.base); + } + } +} + +void ObjHamishi_Draw(Actor* thisx, GlobalContext* globalCtx) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_hamishi.c", 399); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_hamishi.c", 404), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 170, 130, 255); + gSPDisplayList(POLY_OPA_DISP++, gSilverRockDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_hamishi.c", 411); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.h b/soh/src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.h new file mode 100644 index 000000000..a897177f6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.h @@ -0,0 +1,20 @@ +#ifndef Z_OBJ_HAMISHI_H +#define Z_OBJ_HAMISHI_H + +#include "ultra64.h" +#include "global.h" + +struct ObjHamishi; + +typedef struct ObjHamishi { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ f32 shakePosSize; + /* 0x019C */ f32 shakeRotSize; + /* 0x01A0 */ s16 shakeFrames; + /* 0x01A2 */ s16 shakePosPhase; + /* 0x01A4 */ s16 shakeRotPhase; + /* 0x01A6 */ s16 hitCount; +} ObjHamishi; // size = 0x01A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Hana/z_obj_hana.c b/soh/src/overlays/actors/ovl_Obj_Hana/z_obj_hana.c new file mode 100644 index 000000000..5d6e6eb73 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Hana/z_obj_hana.c @@ -0,0 +1,113 @@ +/* + * File: z_obj_hana.c + * Overlay: Obj_Hana + * Description: Grave Flower + */ + +#include "z_obj_hana.h" +#include "objects/gameplay_field_keep/gameplay_field_keep.h" + +#define FLAGS 0 + +void ObjHana_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjHana_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjHana_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjHana_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Obj_Hana_InitVars = { + ACTOR_OBJ_HANA, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_FIELD_KEEP, + sizeof(ObjHana), + (ActorFunc)ObjHana_Init, + (ActorFunc)ObjHana_Destroy, + (ActorFunc)ObjHana_Update, + (ActorFunc)ObjHana_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_NONE, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_NONE, + OCELEM_ON, + }, + { 8, 10, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE }; + +typedef struct { + /* 0x00 */ Gfx* dList; + /* 0x04 */ f32 scale; + /* 0x08 */ f32 yOffset; + /* 0x0C */ s16 radius; + /* 0x0E */ s16 height; +} HanaParams; // size = 0x10 + +static HanaParams sHanaParams[] = { + { gHanaDL, 0.01f, 0.0f, -1, 0 }, + { gFieldKakeraDL, 0.1f, 58.0f, 10, 18 }, + { gFieldBushDL, 0.4f, 0.0f, 12, 44 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 10, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 900, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 60, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), +}; + +void ObjHana_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjHana* this = (ObjHana*)thisx; + s16 type = this->actor.params & 3; + HanaParams* params = &sHanaParams[type]; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetScale(&this->actor, params->scale); + this->actor.shape.yOffset = params->yOffset; + if (params->radius >= 0) { + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.dim.radius = params->radius; + this->collider.dim.height = params->height; + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + } + + if (type == 2 && (gSaveContext.eventChkInf[4] & 1)) { + Actor_Kill(&this->actor); + } +} + +void ObjHana_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjHana* this = (ObjHana*)thisx; + + if (sHanaParams[this->actor.params & 3].radius >= 0) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void ObjHana_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjHana* this = (ObjHana*)thisx; + + if (sHanaParams[this->actor.params & 3].radius >= 0 && this->actor.xzDistToPlayer < 400.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void ObjHana_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, sHanaParams[thisx->params & 3].dList); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Hana/z_obj_hana.h b/soh/src/overlays/actors/ovl_Obj_Hana/z_obj_hana.h new file mode 100644 index 000000000..7fdd3bfa8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Hana/z_obj_hana.h @@ -0,0 +1,14 @@ +#ifndef Z_OBJ_HANA_H +#define Z_OBJ_HANA_H + +#include "ultra64.h" +#include "global.h" + +struct ObjHana; + +typedef struct ObjHana { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; +} ObjHana; // size = 0x0198 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Hsblock/z_obj_hsblock.c b/soh/src/overlays/actors/ovl_Obj_Hsblock/z_obj_hsblock.c new file mode 100644 index 000000000..38330bcd6 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Hsblock/z_obj_hsblock.c @@ -0,0 +1,172 @@ +/* + * File: z_obj_hsblock.c + * Overlay: ovl_Obj_Hsblock + * Description: Stone Hookshot Target + */ + +#include "z_obj_hsblock.h" +#include "objects/object_d_hsblock/object_d_hsblock.h" + +#define FLAGS 0 + +void ObjHsblock_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjHsblock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjHsblock_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjHsblock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B93DF4(ObjHsblock* this, GlobalContext* globalCtx); +void func_80B93E5C(ObjHsblock* this, GlobalContext* globalCtx); + +void func_80B93D90(ObjHsblock* this); +void func_80B93DB0(ObjHsblock* this); +void func_80B93E38(ObjHsblock* this); + +const ActorInit Obj_Hsblock_InitVars = { + ACTOR_OBJ_HSBLOCK, + ACTORCAT_BG, + FLAGS, + OBJECT_D_HSBLOCK, + sizeof(ObjHsblock), + (ActorFunc)ObjHsblock_Init, + (ActorFunc)ObjHsblock_Destroy, + (ActorFunc)ObjHsblock_Update, + (ActorFunc)ObjHsblock_Draw, + NULL, +}; + +static f32 D_80B940C0[] = { 85.0f, 85.0f, 0.0f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2000, ICHAIN_STOP), +}; + +static CollisionHeader* sCollisionHeaders[] = { &gHookshotTargetCol, &gHookshotTargetCol, &gHookshotPostCol }; + +static Color_RGB8 sFireTempleColor = { 165, 125, 55 }; + +static Gfx* sDLists[] = { gHookshotPostDL, gHookshotPostDL, gHookshotTargetDL }; + +void ObjHsblock_SetupAction(ObjHsblock* this, ObjHsblockActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void func_80B93B68(ObjHsblock* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlags) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2[2]; + + DynaPolyActor_Init(&this->dyna, moveFlags); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_obj_hsblock.c", 163, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void func_80B93BF0(ObjHsblock* this, GlobalContext* globalCtx) { + if ((this->dyna.actor.params >> 5) & 1) { + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_OBJ_ICE_POLY, + this->dyna.actor.world.pos.x, this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, + this->dyna.actor.world.rot.x, this->dyna.actor.world.rot.y, this->dyna.actor.world.rot.z, 1); + } +} + +void ObjHsblock_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjHsblock* this = (ObjHsblock*)thisx; + + func_80B93B68(this, globalCtx, sCollisionHeaders[thisx->params & 3], DPM_UNK); + Actor_ProcessInitChain(thisx, sInitChain); + func_80B93BF0(this, globalCtx); + + switch (thisx->params & 3) { + case 0: + case 2: + func_80B93D90(this); + break; + case 1: + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0x3F)) { + func_80B93D90(this); + } else { + func_80B93DB0(this); + } + } + + mREG(13) = 255; + mREG(14) = 255; + mREG(15) = 255; +} + +void ObjHsblock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjHsblock* this = (ObjHsblock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80B93D90(ObjHsblock* this) { + ObjHsblock_SetupAction(this, NULL); +} + +void func_80B93DB0(ObjHsblock* this) { + this->dyna.actor.flags |= ACTOR_FLAG_4; + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y - 105.0f; + ObjHsblock_SetupAction(this, func_80B93DF4); +} + +void func_80B93DF4(ObjHsblock* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + func_80B93E38(this); + } +} + +void func_80B93E38(ObjHsblock* this) { + ObjHsblock_SetupAction(this, func_80B93E5C); +} + +void func_80B93E5C(ObjHsblock* this, GlobalContext* globalCtx) { + Math_SmoothStepToF(&this->dyna.actor.velocity.y, 16.0f, 0.1f, 0.8f, 0.0f); + if (fabsf(Math_SmoothStepToF(&this->dyna.actor.world.pos.y, this->dyna.actor.home.pos.y, 0.3f, + this->dyna.actor.velocity.y, 0.3f)) < 0.001f) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y; + func_80B93D90(this); + this->dyna.actor.flags &= ~ACTOR_FLAG_4; + } +} + +void ObjHsblock_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjHsblock* this = (ObjHsblock*)thisx; + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } + Actor_SetFocus(thisx, D_80B940C0[thisx->params & 3]); +} + +void ObjHsblock_Draw(Actor* thisx, GlobalContext* globalCtx) { + Color_RGB8* color; + Color_RGB8 defaultColor; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_hsblock.c", 365); + + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_hsblock.c", 369), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (globalCtx->sceneNum == SCENE_HIDAN) { + color = &sFireTempleColor; + } else { + defaultColor.r = mREG(13); + defaultColor.g = mREG(14); + defaultColor.b = mREG(15); + color = &defaultColor; + } + + gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 255); + gSPDisplayList(POLY_OPA_DISP++, sDLists[thisx->params & 3]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_hsblock.c", 399); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Hsblock/z_obj_hsblock.h b/soh/src/overlays/actors/ovl_Obj_Hsblock/z_obj_hsblock.h new file mode 100644 index 000000000..ee43e0cad --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Hsblock/z_obj_hsblock.h @@ -0,0 +1,16 @@ +#ifndef Z_OBJ_HSBLOCK_H +#define Z_OBJ_HSBLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct ObjHsblock; + +typedef void (*ObjHsblockActionFunc)(struct ObjHsblock*, GlobalContext*); + +typedef struct ObjHsblock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjHsblockActionFunc actionFunc; +} ObjHsblock; // size = 0x0168 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Ice_Poly/z_obj_ice_poly.c b/soh/src/overlays/actors/ovl_Obj_Ice_Poly/z_obj_ice_poly.c new file mode 100644 index 000000000..c725baf00 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Ice_Poly/z_obj_ice_poly.c @@ -0,0 +1,210 @@ +/* + * File: z_obj_ice_poly.c + * Overlay: ovl_Obj_Ice_Poly + * Description: Ice / Frozen Actors + */ + +#include "z_obj_ice_poly.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +void ObjIcePoly_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjIcePoly_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjIcePoly_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjIcePoly_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjIcePoly_Idle(ObjIcePoly* this, GlobalContext* globalCtx); +void ObjIcePoly_Melt(ObjIcePoly* this, GlobalContext* globalCtx); + +const ActorInit Obj_Ice_Poly_InitVars = { + ACTOR_OBJ_ICE_POLY, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjIcePoly), + (ActorFunc)ObjIcePoly_Init, + (ActorFunc)ObjIcePoly_Destroy, + (ActorFunc)ObjIcePoly_Update, + (ActorFunc)ObjIcePoly_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInitIce = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_ENEMY, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0xFFCFFFFF, 0x02, 0x00 }, + { 0x00020800, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 50, 120, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylinderInitHard = { + { + COLTYPE_HARD, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x4E01F7F6, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 50, 120, 0, { 0, 0, 0 } }, +}; + +static f32 sScale[] = { 0.5f, 1.0f, 1.5f }; +static s16 sOffsetY[] = { -25, 0, -20 }; +static Color_RGBA8 sColorWhite = { 250, 250, 250, 255 }; +static Color_RGBA8 sColorGray = { 180, 180, 180, 255 }; + +void ObjIcePoly_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjIcePoly* this = (ObjIcePoly*)thisx; + + this->unk_151 = (thisx->params >> 8) & 0xFF; + thisx->params &= 0xFF; + if (thisx->params < 0 || thisx->params >= 3) { + Actor_Kill(thisx); + return; + } + Actor_SetScale(thisx, sScale[thisx->params]); + thisx->world.pos.y = sOffsetY[thisx->params] + thisx->home.pos.y; + Collider_InitCylinder(globalCtx, &this->colliderIce); + Collider_SetCylinder(globalCtx, &this->colliderIce, thisx, &sCylinderInitIce); + Collider_InitCylinder(globalCtx, &this->colliderHard); + Collider_SetCylinder(globalCtx, &this->colliderHard, thisx, &sCylinderInitHard); + Collider_UpdateCylinder(thisx, &this->colliderIce); + Collider_UpdateCylinder(thisx, &this->colliderHard); + thisx->colChkInfo.mass = MASS_IMMOVABLE; + this->alpha = 255; + this->colliderIce.dim.radius *= thisx->scale.x; + this->colliderIce.dim.height *= thisx->scale.y; + this->colliderHard.dim.radius *= thisx->scale.x; + this->colliderHard.dim.height *= thisx->scale.y; + Actor_SetFocus(thisx, thisx->scale.y * 30.0f); + this->actionFunc = ObjIcePoly_Idle; +} + +void ObjIcePoly_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjIcePoly* this = (ObjIcePoly*)thisx; + + if ((this->actor.params >= 0) && (this->actor.params < 3)) { + Collider_DestroyCylinder(globalCtx, &this->colliderIce); + Collider_DestroyCylinder(globalCtx, &this->colliderHard); + } +} + +void ObjIcePoly_Idle(ObjIcePoly* this, GlobalContext* globalCtx) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + s32 pad; + Vec3f pos; + + if (this->colliderIce.base.acFlags & AC_HIT) { + this->meltTimer = -this->colliderIce.info.acHitInfo->toucher.damage; + this->actor.focus.rot.y = this->actor.yawTowardsPlayer; + OnePointCutscene_Init(globalCtx, 5120, 40, &this->actor, MAIN_CAM); + this->actionFunc = ObjIcePoly_Melt; + } else if (this->actor.parent != NULL) { + this->actor.parent->freezeTimer = 40; + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->colliderIce.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderIce.base); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderIce.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderHard.base); + } else { + Actor_Kill(&this->actor); + } + pos.x = this->actor.world.pos.x + this->actor.scale.x * (Rand_S16Offset(15, 15) * (Rand_ZeroOne() < 0.5f ? -1 : 1)); + pos.y = this->actor.world.pos.y + this->actor.scale.y * Rand_S16Offset(10, 90); + pos.z = this->actor.world.pos.z + this->actor.scale.z * (Rand_S16Offset(15, 15) * (Rand_ZeroOne() < 0.5f ? -1 : 1)); + if ((globalCtx->gameplayFrames % 7) == 0) { + EffectSsKiraKira_SpawnDispersed(globalCtx, &pos, &zeroVec, &zeroVec, &sColorWhite, &sColorGray, 2000, 5); + } +} + +void ObjIcePoly_Melt(ObjIcePoly* this, GlobalContext* globalCtx) { + Vec3f accel; + Vec3f vel; + Vec3f pos; + s32 i; + + accel.x = 0.0f; + accel.y = this->actor.scale.y; + accel.z = 0.0f; + vel.x = 0.0f; + vel.y = this->actor.scale.y; + vel.z = 0.0f; + + for (i = 0; i < 2; i++) { + pos.x = + this->actor.world.pos.x + this->actor.scale.x * (Rand_S16Offset(20, 20) * (Rand_ZeroOne() < 0.5f ? -1 : 1)); + pos.y = this->actor.world.pos.y + this->actor.scale.y * Rand_ZeroOne() * 50.0f; + pos.z = + this->actor.world.pos.z + this->actor.scale.x * (Rand_S16Offset(20, 20) * (Rand_ZeroOne() < 0.5f ? -1 : 1)); + func_8002829C(globalCtx, &pos, &vel, &accel, &sColorWhite, &sColorGray, + Rand_S16Offset(0x15E, 0x64) * this->actor.scale.x, this->actor.scale.x * 20.0f); + } + if (this->meltTimer < 0) { + if (this->actor.parent != NULL) { + this->actor.parent->freezeTimer = 40; + } + this->meltTimer++; + if (this->meltTimer == 0) { + this->meltTimer = 40; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_ICE_MELT); + } + } else { + if (this->meltTimer != 0) { + this->meltTimer--; + } + this->actor.scale.y = sScale[this->actor.params] * (0.5f + (this->meltTimer * 0.0125f)); + this->alpha -= 6; + if (this->meltTimer == 0) { + Actor_Kill(&this->actor); + } + } +} + +void ObjIcePoly_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjIcePoly* this = (ObjIcePoly*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ObjIcePoly_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjIcePoly* this = (ObjIcePoly*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_ice_poly.c", 421); + func_80093D84(globalCtx->state.gfxCtx); + func_8002ED80(&this->actor, globalCtx, 0); + Matrix_RotateZYX(0x500, 0, -0x500, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_ice_poly.c", 428), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, globalCtx->gameplayFrames % 0x100, 0x20, 0x10, 1, 0, + (globalCtx->gameplayFrames * 2) % 0x100, 0x40, 0x20)); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 50, 100, this->alpha); + gSPDisplayList(POLY_XLU_DISP++, gEffIceFragment3DL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_ice_poly.c", 444); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Ice_Poly/z_obj_ice_poly.h b/soh/src/overlays/actors/ovl_Obj_Ice_Poly/z_obj_ice_poly.h new file mode 100644 index 000000000..cb1c3e5bf --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Ice_Poly/z_obj_ice_poly.h @@ -0,0 +1,21 @@ +#ifndef Z_OBJ_ICE_POLY_H +#define Z_OBJ_ICE_POLY_H + +#include "ultra64.h" +#include "global.h" + +struct ObjIcePoly; + +typedef void (*ObjIcePolyActionFunc)(struct ObjIcePoly*, GlobalContext*); + +typedef struct ObjIcePoly { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjIcePolyActionFunc actionFunc; + /* 0x0150 */ u8 alpha; + /* 0x0151 */ u8 unk_151; // Unused. Probably intended to be a switch flag. + /* 0x0152 */ s16 meltTimer; + /* 0x0154 */ ColliderCylinder colliderIce; + /* 0x01A0 */ ColliderCylinder colliderHard; +} ObjIcePoly; // size = 0x01EC + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c b/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c new file mode 100644 index 000000000..09a0f20ee --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c @@ -0,0 +1,288 @@ +/* + * File: z_obj_kibako.c + * Overlay: ovl_Obj_Kibako + * Description: Small wooden box + */ + +#include "z_obj_kibako.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_26) + +void ObjKibako_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjKibako_SetupIdle(ObjKibako* this); +void ObjKibako_Idle(ObjKibako* this, GlobalContext* globalCtx); +void ObjKibako_SetupHeld(ObjKibako* this); +void ObjKibako_Held(ObjKibako* this, GlobalContext* globalCtx); +void ObjKibako_SetupThrown(ObjKibako* this); +void ObjKibako_Thrown(ObjKibako* this, GlobalContext* globalCtx); + +const ActorInit Obj_Kibako_InitVars = { + ACTOR_OBJ_KIBAKO, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(ObjKibako), + (ActorFunc)ObjKibako_Init, + (ActorFunc)ObjKibako_Destroy, + (ActorFunc)ObjKibako_Update, + (ActorFunc)ObjKibako_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000002, 0x00, 0x01 }, + { 0x4FC00748, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 12, 27, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sCCInfoInit = { 0, 12, 60, MASS_HEAVY }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 60, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void ObjKibako_SpawnCollectible(ObjKibako* this, GlobalContext* globalCtx) { + s16 collectible; + + collectible = this->actor.params & 0x1F; + if ((collectible >= 0) && (collectible <= 0x19)) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, + collectible | (((this->actor.params >> 8) & 0x3F) << 8)); + } +} + +void ObjKibako_ApplyGravity(ObjKibako* this) { + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } +} + +void ObjKibako_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + ObjKibako* this = (ObjKibako*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); +} + +void ObjKibako_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjKibako* this = (ObjKibako*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + this->actor.gravity = -1.2f; + this->actor.minVelocityY = -13.0f; + ObjKibako_InitCollider(&this->actor, globalCtx); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sCCInfoInit); + ObjKibako_SetupIdle(this); + // "wooden box" + osSyncPrintf("(dungeon keep 木箱)(arg_data 0x%04x)\n", this->actor.params); +} + +void ObjKibako_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjKibako* this = (ObjKibako*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void ObjKibako_AirBreak(ObjKibako* this, GlobalContext* globalCtx) { + s16 angle; + s32 i; + Vec3f* breakPos = &this->actor.world.pos; + Vec3f pos; + Vec3f velocity; + + for (i = 0, angle = 0; i < 12; i++, angle += 0x4E20) { + f32 sn = Math_SinS(angle); + f32 cs = Math_CosS(angle); + f32 temp_rand; + s16 phi_s0; + + pos.x = sn * 16.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = cs * 16.0f; + velocity.x = pos.x * 0.2f; + velocity.y = (Rand_ZeroOne() * 6.0f) + 2.0f; + velocity.z = pos.z * 0.2f; + pos.x += breakPos->x; + pos.y += breakPos->y; + pos.z += breakPos->z; + temp_rand = Rand_ZeroOne(); + if (temp_rand < 0.1f) { + phi_s0 = 0x60; + } else if (temp_rand < 0.7f) { + phi_s0 = 0x40; + } else { + phi_s0 = 0x20; + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, breakPos, -200, phi_s0, 10, 10, 0, + (Rand_ZeroOne() * 30.0f) + 10.0f, 0, 32, 60, KAKERA_COLOR_NONE, + OBJECT_GAMEPLAY_DANGEON_KEEP, gSmallWoodenBoxFragmentDL); + } + func_80033480(globalCtx, &this->actor.world.pos, 40.0f, 3, 50, 140, 1); +} + +void ObjKibako_WaterBreak(ObjKibako* this, GlobalContext* globalCtx) { + s16 angle; + s32 i; + Vec3f* breakPos = &this->actor.world.pos; + Vec3f pos; + Vec3f velocity; + + pos = *breakPos; + pos.y += this->actor.yDistToWater; + EffectSsGSplash_Spawn(globalCtx, &pos, NULL, NULL, 0, 500); + + for (i = 0, angle = 0; i < 12; i++, angle += 0x4E20) { + f32 sn = Math_SinS(angle); + f32 cs = Math_CosS(angle); + f32 temp_rand; + s16 phi_s0; + + pos.x = sn * 16.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = cs * 16.0f; + velocity.x = pos.x * 0.18f; + velocity.y = (Rand_ZeroOne() * 4.0f) + 2.0f; + velocity.z = pos.z * 0.18f; + pos.x += breakPos->x; + pos.y += breakPos->y; + pos.z += breakPos->z; + temp_rand = Rand_ZeroOne(); + phi_s0 = (temp_rand < 0.2f) ? 0x40 : 0x20; + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, breakPos, -180, phi_s0, 30, 30, 0, + (Rand_ZeroOne() * 30.0f) + 10.0f, 0, 32, 70, KAKERA_COLOR_NONE, + OBJECT_GAMEPLAY_DANGEON_KEEP, gSmallWoodenBoxFragmentDL); + } +} + +void ObjKibako_SetupIdle(ObjKibako* this) { + this->actionFunc = ObjKibako_Idle; + this->actor.colChkInfo.mass = MASS_HEAVY; +} + +void ObjKibako_Idle(ObjKibako* this, GlobalContext* globalCtx) { + s32 pad; + + if (Actor_HasParent(&this->actor, globalCtx)) { + ObjKibako_SetupHeld(this); + } else if ((this->actor.bgCheckFlags & 0x20) && (this->actor.yDistToWater > 19.0f)) { + ObjKibako_WaterBreak(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_WOODBOX_BREAK); + ObjKibako_SpawnCollectible(this, globalCtx); + Actor_Kill(&this->actor); + } else if (this->collider.base.acFlags & AC_HIT) { + ObjKibako_AirBreak(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_WOODBOX_BREAK); + ObjKibako_SpawnCollectible(this, globalCtx); + Actor_Kill(&this->actor); + } else { + Actor_MoveForward(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 19.0f, 20.0f, 0.0f, 5); + if (!(this->collider.base.ocFlags1 & OC1_TYPE_PLAYER) && (this->actor.xzDistToPlayer > 28.0f)) { + this->collider.base.ocFlags1 |= OC1_TYPE_PLAYER; + } + if (this->actor.xzDistToPlayer < 600.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actor.xzDistToPlayer < 180.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + if (this->actor.xzDistToPlayer < 100.0f) { + func_8002F580(&this->actor, globalCtx); + } + } +} + +void ObjKibako_SetupHeld(ObjKibako* this) { + this->actionFunc = ObjKibako_Held; + this->actor.room = -1; + func_8002F7DC(&this->actor, NA_SE_PL_PULL_UP_WOODBOX); +} + +void ObjKibako_Held(ObjKibako* this, GlobalContext* globalCtx) { + if (Actor_HasNoParent(&this->actor, globalCtx)) { + this->actor.room = globalCtx->roomCtx.curRoom.num; + if (fabsf(this->actor.speedXZ) < 0.1f) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_PUT_DOWN_WOODBOX); + ObjKibako_SetupIdle(this); + this->collider.base.ocFlags1 &= ~OC1_TYPE_PLAYER; + } else { + ObjKibako_SetupThrown(this); + ObjKibako_ApplyGravity(this); + func_8002D7EC(&this->actor); + } + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 19.0f, 20.0f, 0.0f, 5); + } +} + +void ObjKibako_SetupThrown(ObjKibako* this) { + this->actor.velocity.x = Math_SinS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.velocity.z = Math_CosS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.colChkInfo.mass = 240; + this->actionFunc = ObjKibako_Thrown; +} + +void ObjKibako_Thrown(ObjKibako* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + + if ((this->actor.bgCheckFlags & 0xB) || (this->collider.base.atFlags & AT_HIT)) { + ObjKibako_AirBreak(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_WOODBOX_BREAK); + ObjKibako_SpawnCollectible(this, globalCtx); + Actor_Kill(&this->actor); + } else if (this->actor.bgCheckFlags & 0x40) { + ObjKibako_WaterBreak(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_WOODBOX_BREAK); + ObjKibako_SpawnCollectible(this, globalCtx); + Actor_Kill(&this->actor); + } else { + ObjKibako_ApplyGravity(this); + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 19.0f, 20.0f, 0.0f, 5); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void ObjKibako_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjKibako* this = (ObjKibako*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ObjKibako_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjKibako* this = (ObjKibako*)thisx; + + Gfx_DrawDListOpa(globalCtx, gSmallWoodenBoxDL); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.h b/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.h new file mode 100644 index 000000000..1e5d3d48b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.h @@ -0,0 +1,17 @@ +#ifndef Z_OBJ_KIBAKO_H +#define Z_OBJ_KIBAKO_H + +#include "ultra64.h" +#include "global.h" + +struct ObjKibako; + +typedef void (*ObjKibakoActionFunc)(struct ObjKibako*, GlobalContext*); + +typedef struct ObjKibako { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjKibakoActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; +} ObjKibako; // size = 0x019C + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Kibako2/z_obj_kibako2.c b/soh/src/overlays/actors/ovl_Obj_Kibako2/z_obj_kibako2.c new file mode 100644 index 000000000..930550e58 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Kibako2/z_obj_kibako2.c @@ -0,0 +1,180 @@ +/* + * File: z_obj_kibako2.c + * Overlay: ovl_Obj_Kibako2 + * Description: Large crate + */ + +#include "z_obj_kibako2.h" +#include "objects/object_kibako2/object_kibako2.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" + +#define FLAGS 0 + +void ObjKibako2_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako2_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako2_Draw(Actor* thisx, GlobalContext* globalCtx); +void ObjKibako2_Idle(ObjKibako2* this, GlobalContext* globalCtx); +void ObjKibako2_Kill(ObjKibako2* this, GlobalContext* globalCtx); + +const ActorInit Obj_Kibako2_InitVars = { + ACTOR_OBJ_KIBAKO2, + ACTORCAT_BG, + FLAGS, + OBJECT_KIBAKO2, + sizeof(ObjKibako2), + (ActorFunc)ObjKibako2_Init, + (ActorFunc)ObjKibako2_Destroy, + (ActorFunc)ObjKibako2_Update, + (ActorFunc)ObjKibako2_Draw, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 31, 48, 0, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 3000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void ObjKibako2_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + ObjKibako2* this = (ObjKibako2*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit); + Collider_UpdateCylinder(&this->dyna.actor, &this->collider); +} + +void ObjKibako2_Break(ObjKibako2* this, GlobalContext* globalCtx) { + s32 pad[2]; + Vec3f* thisPos; + Vec3f pos; + Vec3f velocity; + s16 angle; + s32 i; + + thisPos = &this->dyna.actor.world.pos; + for (i = 0, angle = 0; i < 0x10; i++, angle += 0x4E20) { + f32 sn = Math_SinS(angle); + f32 cs = Math_CosS(angle); + f32 temp_rand; + s32 phi_s0; + + temp_rand = Rand_ZeroOne() * 30.0f; + pos.x = sn * temp_rand; + pos.y = (Rand_ZeroOne() * 10.0f) + 2.0f; + pos.z = cs * temp_rand; + velocity.x = pos.x * 0.2f; + velocity.y = (Rand_ZeroOne() * 10.0f) + 2.0f; + velocity.z = pos.z * 0.2f; + pos.x += thisPos->x; + pos.y += thisPos->y; + pos.z += thisPos->z; + temp_rand = Rand_ZeroOne(); + if (temp_rand < 0.05f) { + phi_s0 = 0x60; + } else if (temp_rand < 0.7f) { + phi_s0 = 0x40; + } else { + phi_s0 = 0x20; + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &pos, -200, phi_s0, 28, 2, 0, (Rand_ZeroOne() * 30.0f) + 5.0f, + 0, 0, 70, KAKERA_COLOR_NONE, OBJECT_KIBAKO2, gLargeCrateFragmentDL); + } + func_80033480(globalCtx, thisPos, 90.0f, 6, 100, 160, 1); +} + +void ObjKibako2_SpawnCollectible(ObjKibako2* this, GlobalContext* globalCtx) { + s16 itemDropped; + s16 collectibleFlagTemp; + + collectibleFlagTemp = this->collectibleFlag; + itemDropped = this->dyna.actor.home.rot.x; + if (itemDropped >= 0 && itemDropped < 0x1A) { + Item_DropCollectible(globalCtx, &this->dyna.actor.world.pos, itemDropped | (collectibleFlagTemp << 8)); + } +} + +void ObjKibako2_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjKibako2* this = (ObjKibako2*)thisx; + s16 pad; + CollisionHeader* colHeader = NULL; + u32 bgId; + + DynaPolyActor_Init(&this->dyna, 0); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + ObjKibako2_InitCollider(thisx, globalCtx); + CollisionHeader_GetVirtual(&gLargeCrateCol, &colHeader); + bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + this->collectibleFlag = this->dyna.actor.home.rot.z & 0x3F; + this->dyna.bgId = bgId; + this->actionFunc = ObjKibako2_Idle; + this->dyna.actor.home.rot.z = this->dyna.actor.world.rot.z = this->dyna.actor.shape.rot.z = + this->dyna.actor.world.rot.x = this->dyna.actor.shape.rot.x = 0; + // "Wooden box (stationary)" + osSyncPrintf("木箱(据置)(arg %04xH)(item %04xH %d)\n", this->dyna.actor.params, this->collectibleFlag, + this->dyna.actor.home.rot.x); +} + +void ObjKibako2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjKibako2* this = (ObjKibako2*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void ObjKibako2_Idle(ObjKibako2* this, GlobalContext* globalCtx) { + if ((this->collider.base.acFlags & AC_HIT) || (this->dyna.actor.home.rot.z != 0) || + func_80033684(globalCtx, &this->dyna.actor) != NULL) { + ObjKibako2_Break(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 20, NA_SE_EV_WOODBOX_BREAK); + this->dyna.actor.flags |= ACTOR_FLAG_4; + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + this->dyna.actor.draw = NULL; + this->actionFunc = ObjKibako2_Kill; + } else if (this->dyna.actor.xzDistToPlayer < 600.0f) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void ObjKibako2_Kill(ObjKibako2* this, GlobalContext* globalCtx) { + s16 params = this->dyna.actor.params; + + if ((params & 0x8000) == 0) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_SW, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, this->dyna.actor.shape.rot.y, 0, + params | 0x8000); + } + ObjKibako2_SpawnCollectible(this, globalCtx); + Actor_Kill(&this->dyna.actor); +} + +void ObjKibako2_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjKibako2* this = (ObjKibako2*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ObjKibako2_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gLargeCrateDL); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Kibako2/z_obj_kibako2.h b/soh/src/overlays/actors/ovl_Obj_Kibako2/z_obj_kibako2.h new file mode 100644 index 000000000..04b980a99 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Kibako2/z_obj_kibako2.h @@ -0,0 +1,18 @@ +#ifndef Z_OBJ_KIBAKO2_H +#define Z_OBJ_KIBAKO2_H + +#include "ultra64.h" +#include "global.h" + +struct ObjKibako2; + +typedef void (*ObjKibako2ActionFunc)(struct ObjKibako2*, GlobalContext*); + +typedef struct ObjKibako2 { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ColliderCylinder collider; + /* 0x01B0 */ ObjKibako2ActionFunc actionFunc; + /* 0x01B4 */ s16 collectibleFlag; +} ObjKibako2; // size = 0x01B8 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Lift/z_obj_lift.c b/soh/src/overlays/actors/ovl_Obj_Lift/z_obj_lift.c new file mode 100644 index 000000000..3c6aaaf52 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Lift/z_obj_lift.c @@ -0,0 +1,222 @@ +/* + * File: z_obj_lift.c + * Overlay: ovl_Obj_Lift + * Description: Square, collapsing platform + */ + +#include "z_obj_lift.h" +#include "objects/object_d_lift/object_d_lift.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" + +#define FLAGS ACTOR_FLAG_4 + +void ObjLift_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjLift_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjLift_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjLift_Draw(Actor* thisx, GlobalContext* globalCtx); + +void func_80B9651C(ObjLift* this); +void func_80B9664C(ObjLift* this); +void func_80B967C0(ObjLift* this); + +void func_80B96560(ObjLift* this, GlobalContext* globalCtx); +void func_80B96678(ObjLift* this, GlobalContext* globalCtx); +void func_80B96840(ObjLift* this, GlobalContext* globalCtx); + +const ActorInit Obj_Lift_InitVars = { + ACTOR_OBJ_LIFT, + ACTORCAT_BG, + FLAGS, + OBJECT_D_LIFT, + sizeof(ObjLift), + (ActorFunc)ObjLift_Init, + (ActorFunc)ObjLift_Destroy, + (ActorFunc)ObjLift_Update, + (ActorFunc)ObjLift_Draw, + NULL, +}; + +static s16 sFallTimerDurations[] = { 0, 10, 20, 30, 40, 50, 60 }; + +typedef struct { + /* 0x00 */ s16 x; + /* 0x02 */ s16 z; +} ObjLiftFramgentScale; // size = 0x4 + +static ObjLiftFramgentScale sFragmentScales[] = { + { 120, -120 }, { 120, 0 }, { 120, 120 }, { 0, -120 }, { 0, 0 }, + { 0, 120 }, { -120, -120 }, { -120, 0 }, { -120, 120 }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32_DIV1000(gravity, -600, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(minVelocityY, -15000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2000, ICHAIN_STOP), +}; + +static f32 sScales[] = { 0.1f, 0.05f }; +static f32 sMaxFallDistances[] = { -18.0f, -9.0f }; + +void ObjLift_SetupAction(ObjLift* this, ObjLiftActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void ObjLift_InitDynaPoly(ObjLift* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 flags) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, flags); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_obj_lift.c", 188, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void func_80B96160(ObjLift* this, GlobalContext* globalCtx) { + Vec3f pos; + Vec3f velocity; + Vec3f* temp_s3; + s32 pad0; + s32 i; + + temp_s3 = &this->dyna.actor.world.pos; + + for (i = 0; i < ARRAY_COUNT(sFragmentScales); i++) { + pos.x = sFragmentScales[i].x * this->dyna.actor.scale.x + temp_s3->x; + pos.y = temp_s3->y; + pos.z = sFragmentScales[i].z * this->dyna.actor.scale.z + temp_s3->z; + velocity.x = sFragmentScales[i].x * this->dyna.actor.scale.x * 0.8f; + velocity.y = Rand_ZeroOne() * 10.0f + 6.0f; + velocity.z = sFragmentScales[i].z * this->dyna.actor.scale.z * 0.8f; + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, temp_s3, -256, (Rand_ZeroOne() < 0.5f) ? 64 : 32, 15, 15, 0, + (Rand_ZeroOne() * 50.0f + 50.0f) * this->dyna.actor.scale.x, 0, 32, 50, KAKERA_COLOR_NONE, + OBJECT_D_LIFT, gCollapsingPlatformDL); + } + + if (((this->dyna.actor.params >> 1) & 1) == 0) { + func_80033480(globalCtx, &this->dyna.actor.world.pos, 120.0f, 12, 120, 100, 1); + } else if (((this->dyna.actor.params >> 1) & 1) == 1) { + func_80033480(globalCtx, &this->dyna.actor.world.pos, 60.0f, 8, 60, 100, 1); + } +} + +void ObjLift_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjLift* this = (ObjLift*)thisx; + + ObjLift_InitDynaPoly(this, globalCtx, &gCollapsingPlatformCol, DPM_PLAYER); + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 2) & 0x3F)) { + Actor_Kill(&this->dyna.actor); + return; + } + + Actor_SetScale(&this->dyna.actor, sScales[(this->dyna.actor.params >> 1) & 1]); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->unk168.x = Rand_ZeroOne() * 65535.5f; + this->unk168.y = Rand_ZeroOne() * 65535.5f; + this->unk168.z = Rand_ZeroOne() * 65535.5f; + func_80B9651C(this); + osSyncPrintf("(Dungeon Lift)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void ObjLift_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjLift* this = (ObjLift*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void func_80B9651C(ObjLift* this) { + this->timer = sFallTimerDurations[(this->dyna.actor.params >> 8) & 7]; + ObjLift_SetupAction(this, func_80B96560); +} + +void func_80B96560(ObjLift* this, GlobalContext* globalCtx) { + s32 pad; + s32 quakeIndex; + + if (func_8004356C(&this->dyna)) { + if (this->timer <= 0) { + if (((this->dyna.actor.params >> 8) & 7) == 7) { + func_80B967C0(this); + } else { + quakeIndex = Quake_Add(GET_ACTIVE_CAM(globalCtx), 1); + Quake_SetSpeed(quakeIndex, 10000); + Quake_SetQuakeValues(quakeIndex, 2, 0, 0, 0); + Quake_SetCountdown(quakeIndex, 20); + func_80B9664C(this); + } + } + } else { + this->timer = sFallTimerDurations[(this->dyna.actor.params >> 8) & 7]; + } +} + +void func_80B9664C(ObjLift* this) { + this->timer = 20; + ObjLift_SetupAction(this, func_80B96678); +} + +void func_80B96678(ObjLift* this, GlobalContext* globalCtx) { + if (this->timer <= 0) { + func_80B967C0(this); + } else { + this->unk168.x += 10000; + this->dyna.actor.world.rot.x = (s16)(Math_SinS(this->unk168.x) * 300.0f) + this->dyna.actor.home.rot.x; + this->dyna.actor.world.rot.z = (s16)(Math_CosS(this->unk168.x) * 300.0f) + this->dyna.actor.home.rot.z; + this->dyna.actor.shape.rot.x = this->dyna.actor.world.rot.x; + this->dyna.actor.shape.rot.z = this->dyna.actor.world.rot.z; + this->unk168.y += 18000; + this->dyna.actor.world.pos.y = Math_SinS(this->unk168.y) + this->dyna.actor.home.pos.y; + this->unk168.z += 18000; + this->dyna.actor.world.pos.x = Math_SinS(this->unk168.z) * 3.0f + this->dyna.actor.home.pos.x; + this->dyna.actor.world.pos.z = Math_CosS(this->unk168.z) * 3.0f + this->dyna.actor.home.pos.z; + } + + if ((this->timer & 3) == 3) { + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 16, NA_SE_EV_BLOCK_SHAKE); + } +} + +void func_80B967C0(ObjLift* this) { + ObjLift_SetupAction(this, func_80B96840); + Math_Vec3f_Copy(&this->dyna.actor.world.pos, &this->dyna.actor.home.pos); + this->dyna.actor.shape.rot = this->dyna.actor.world.rot = this->dyna.actor.home.rot; +} + +void func_80B96840(ObjLift* this, GlobalContext* globalCtx) { + s32 pad; + s32 bgId; + Vec3f sp2C; + + Actor_MoveForward(&this->dyna.actor); + Math_Vec3f_Copy(&sp2C, &this->dyna.actor.prevPos); + sp2C.y += sMaxFallDistances[(this->dyna.actor.params >> 1) & 1]; + this->dyna.actor.floorHeight = + BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &this->dyna.actor.floorPoly, &bgId, &this->dyna.actor, &sp2C); + + if ((this->dyna.actor.floorHeight - this->dyna.actor.world.pos.y) >= + (sMaxFallDistances[(this->dyna.actor.params >> 1) & 1] - 0.001f)) { + func_80B96160(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->dyna.actor.world.pos, 20, NA_SE_EV_BOX_BREAK); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 2) & 0x3F); + Actor_Kill(&this->dyna.actor); + } +} + +void ObjLift_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjLift* this = (ObjLift*)thisx; + + if (this->timer > 0) { + this->timer--; + } + + this->actionFunc(this, globalCtx); +} + +void ObjLift_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gCollapsingPlatformDL); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Lift/z_obj_lift.h b/soh/src/overlays/actors/ovl_Obj_Lift/z_obj_lift.h new file mode 100644 index 000000000..0bbf06a1f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Lift/z_obj_lift.h @@ -0,0 +1,18 @@ +#ifndef Z_OBJ_LIFT_H +#define Z_OBJ_LIFT_H + +#include "ultra64.h" +#include "global.h" + +struct ObjLift; + +typedef void (*ObjLiftActionFunc)(struct ObjLift*, GlobalContext*); + +typedef struct ObjLift { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjLiftActionFunc actionFunc; + /* 0x0168 */ Vec3s unk168; + /* 0x016E */ s16 timer; +} ObjLift; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c b/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c new file mode 100644 index 000000000..6561103de --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.c @@ -0,0 +1,502 @@ +/* + * File: z_obj_lightswitch.c + * Overlay: ovl_Obj_Lightswitch + * Description: Sun Emblem Trigger (Spirit Temple) + */ + +#include "z_obj_lightswitch.h" +#include "vt.h" +#include "overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h" +#include "objects/object_lightswitch/object_lightswitch.h" + +#define FLAGS ACTOR_FLAG_4 + +typedef enum { + /* 0x00 */ FACE_EYES_CLOSED, + /* 0x01 */ FACE_EYES_OPEN, + /* 0x02 */ FACE_EYES_OPEN_SMILING +} FaceTextureIndex; + +void ObjLightswitch_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjLightswitch_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjLightswitch_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjLightswitch_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjLightswitch_SetupOff(ObjLightswitch* this); +void ObjLightswitch_Off(ObjLightswitch* this, GlobalContext* globalCtx); +void ObjLightswitch_SetupTurnOn(ObjLightswitch* this); +void ObjLightswitch_TurnOn(ObjLightswitch* this, GlobalContext* globalCtx); +void ObjLightswitch_SetupOn(ObjLightswitch* this); +void ObjLightswitch_On(ObjLightswitch* this, GlobalContext* globalCtx); +void ObjLightswitch_SetupTurnOff(ObjLightswitch* this); +void ObjLightswitch_TurnOff(ObjLightswitch* this, GlobalContext* globalCtx); +void ObjLightswitch_SetupDisappearDelay(ObjLightswitch* this); +void ObjLightswitch_DisappearDelay(ObjLightswitch* this, GlobalContext* globalCtx); +void ObjLightswitch_SetupDisappear(ObjLightswitch* this); +void ObjLightswitch_Disappear(ObjLightswitch* this, GlobalContext* globalCtx); + +const ActorInit Obj_Lightswitch_InitVars = { + ACTOR_OBJ_LIGHTSWITCH, + ACTORCAT_SWITCH, + FLAGS, + OBJECT_LIGHTSWITCH, + sizeof(ObjLightswitch), + (ActorFunc)ObjLightswitch_Init, + (ActorFunc)ObjLightswitch_Destroy, + (ActorFunc)ObjLightswitch_Update, + (ActorFunc)ObjLightswitch_Draw, + NULL, +}; + +static ColliderJntSphElementInit sColliderJntSphElementInit[] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00200000, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 0, 0 }, 19 }, 100 }, + }, +}; +static ColliderJntSphInit sColliderJntSphInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + sColliderJntSphElementInit, +}; + +static CollisionCheckInfoInit sColChkInfoInit = { 0, 12, 60, MASS_IMMOVABLE }; + +static void* sFaceTextures[] = { object_lightswitch_Tex_000C20, object_lightswitch_Tex_000420, + object_lightswitch_Tex_001420 }; + +static Vec3f D_80B97F68 = { -1707.0f, 843.0f, -180.0f }; +static Vec3f D_80B97F74 = { 0.0f, 0.0f, 0.0f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1000, ICHAIN_STOP), +}; + +void ObjLightswitch_InitCollider(ObjLightswitch* this, GlobalContext* globalCtx) { + s32 pad; + + Collider_InitJntSph(globalCtx, &this->collider); + Collider_SetJntSph(globalCtx, &this->collider, &this->actor, &sColliderJntSphInit, this->colliderItems); + Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, + this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y), + this->actor.world.pos.z, &this->actor.shape.rot); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + Collider_UpdateSpheres(0, &this->collider); +} + +void ObjLightswitch_SetSwitchFlag(ObjLightswitch* this, GlobalContext* globalCtx) { + Actor* thisx = &this->actor; // required + s32 type; + + if (!Flags_GetSwitch(globalCtx, this->actor.params >> 8 & 0x3F)) { + type = this->actor.params >> 4 & 3; + + Flags_SetSwitch(globalCtx, this->actor.params >> 8 & 0x3F); + + if (type == OBJLIGHTSWITCH_TYPE_1) { + OnePointCutscene_AttentionSetSfx(globalCtx, thisx, NA_SE_SY_TRE_BOX_APPEAR); + } else if (type == OBJLIGHTSWITCH_TYPE_BURN) { + OnePointCutscene_AttentionSetSfx(globalCtx, thisx, NA_SE_SY_ERROR); + } else { + OnePointCutscene_AttentionSetSfx(globalCtx, thisx, NA_SE_SY_CORRECT_CHIME); + } + } +} + +void ObjLightswitch_ClearSwitchFlag(ObjLightswitch* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, this->actor.params >> 8 & 0x3F)) { + Flags_UnsetSwitch(globalCtx, this->actor.params >> 8 & 0x3F); + + if ((this->actor.params >> 4 & 3) == OBJLIGHTSWITCH_TYPE_1) { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->actor, NA_SE_SY_TRE_BOX_APPEAR); + } + } +} + +void ObjLightswitch_SpawnDisappearEffects(ObjLightswitch* this, GlobalContext* globalCtx) { + Vec3f pos; + f32 s = Math_SinS(this->actor.shape.rot.y); + f32 c = Math_CosS(this->actor.shape.rot.y); + f32 x; + f32 y; + f32 z; + s32 pad; + + if (this->alpha >= (100 << 6)) { + x = (CLAMP_MAX((1.0f - 1.0f / (255 << 6) * this->alpha) * 400.0f, 60.0f) - 30.0f + 30.0f) * Rand_ZeroOne(); + y = x - 30.0f; + if (x > 30.0f) { + x = 30.0f; + } else { + x = 900.0f - (y * y); + if (x < 100.0f) { + x = 100.0f; + } + x = sqrtf(x); + } + x = 2.0f * (x * (Rand_ZeroOne() - 0.5f)); + z = (30.0f - fabsf(x)) * 0.5f + 10.0f * Rand_ZeroOne(); + pos.x = this->actor.world.pos.x + ((z * s) + (x * c)); + pos.y = this->actor.world.pos.y + y + 10.0f; + pos.z = this->actor.world.pos.z + ((z * c) - (x * s)); + EffectSsDeadDb_Spawn(globalCtx, &pos, &D_80B97F74, &D_80B97F74, 100, 0, 255, 255, 160, 160, 255, 0, 0, 1, 9, + true); + } +} + +void ObjLightswitch_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjLightswitch* this = (ObjLightswitch*)thisx; + s32 switchFlagSet = Flags_GetSwitch(globalCtx, this->actor.params >> 8 & 0x3F); + s32 removeSelf = false; + + Actor_ProcessInitChain(&this->actor, sInitChain); + Actor_SetFocus(&this->actor, 0.0f); + if (switchFlagSet) { + if ((this->actor.params >> 4 & 3) == OBJLIGHTSWITCH_TYPE_BURN) { + removeSelf = true; + } else { + ObjLightswitch_SetupOn(this); + } + } else { + ObjLightswitch_SetupOff(this); + } + if ((this->actor.params & 1) == 1) { + if (switchFlagSet) { + Math_Vec3f_Copy(&this->actor.world.pos, &D_80B97F68); + Math_Vec3f_Copy(&this->actor.home.pos, &D_80B97F68); + } + this->actor.shape.rot.x = -0x4000; + this->actor.shape.rot.z = 0; + this->actor.world.rot.x = this->actor.home.rot.x = this->actor.shape.rot.x; + this->actor.world.rot.z = this->actor.home.rot.z = this->actor.shape.rot.z; + this->actor.flags |= ACTOR_FLAG_5; + if (Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_OBJ_OSHIHIKI, + this->actor.home.pos.x, this->actor.home.pos.y, this->actor.home.pos.z, 0, + this->actor.home.rot.y, 0, (0xFF << 8) | PUSHBLOCK_SMALL_START_ON) == NULL) { + osSyncPrintf(VT_COL(RED, WHITE)); + // "Push-pull block occurrence failure" + osSyncPrintf("押引ブロック発生失敗(%s %d)(arg_data 0x%04x)\n", "../z_obj_lightswitch.c", 452, + this->actor.params); + osSyncPrintf(VT_RST); + removeSelf = true; + } + } + ObjLightswitch_InitCollider(this, globalCtx); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sColChkInfoInit); + if (removeSelf) { + Actor_Kill(&this->actor); + } + // "Light switch" + osSyncPrintf("(光スイッチ)(arg_data 0x%04x)\n", this->actor.params); +} + +void ObjLightswitch_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjLightswitch* this = (ObjLightswitch*)thisx; + + Collider_DestroyJntSph(globalCtx, &this->collider); +} + +void ObjLightswitch_SetupOff(ObjLightswitch* this) { + this->actionFunc = ObjLightswitch_Off; + this->faceTextureIndex = FACE_EYES_CLOSED; + this->color[0] = 155 << 6; + this->color[1] = 125 << 6; + this->color[2] = 255 << 6; + this->alpha = 255 << 6; +} + +void ObjLightswitch_Off(ObjLightswitch* this, GlobalContext* globalCtx) { + switch (this->actor.params >> 4 & 3) { + case OBJLIGHTSWITCH_TYPE_STAY_ON: + case OBJLIGHTSWITCH_TYPE_2: + if (this->collider.base.acFlags & AC_HIT) { + ObjLightswitch_SetupTurnOn(this); + ObjLightswitch_SetSwitchFlag(this, globalCtx); + } + break; + case OBJLIGHTSWITCH_TYPE_1: + if ((this->collider.base.acFlags & AC_HIT) && !(this->prevFrameACflags & AC_HIT)) { + ObjLightswitch_SetupTurnOn(this); + ObjLightswitch_SetSwitchFlag(this, globalCtx); + } + break; + case OBJLIGHTSWITCH_TYPE_BURN: + if (this->collider.base.acFlags & AC_HIT) { + ObjLightswitch_SetupDisappearDelay(this); + ObjLightswitch_SetSwitchFlag(this, globalCtx); + } + break; + } +} + +void ObjLightswitch_SetupTurnOn(ObjLightswitch* this) { + this->actionFunc = ObjLightswitch_TurnOn; + this->toggleDelay = 100; + this->timer = 0; + this->faceTextureIndex = FACE_EYES_CLOSED; +} + +void ObjLightswitch_TurnOn(ObjLightswitch* this, GlobalContext* globalCtx) { + if (func_8005B198() == this->actor.category || this->toggleDelay <= 0) { + if (this->timer == 0) { + Audio_PlayActorSound2(&this->actor, NA_SE_EV_TRIFORCE_FLASH); + } + + this->timer++; + + Math_StepToS(&this->flameRingRotSpeed, -0xAA, 0xA); + this->flameRingRot += this->flameRingRotSpeed; + + this->color[0] = this->timer * (((255 - 155) << 6) / 20) + (155 << 6); + this->color[1] = this->timer * (((255 - 125) << 6) / 20) + (125 << 6); + + if (this->timer >= 20) { + ObjLightswitch_SetupOn(this); + } else if (this->timer == 15) { + this->faceTextureIndex = FACE_EYES_OPEN; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FOOT_SWITCH); + } + } +} + +void ObjLightswitch_SetupOn(ObjLightswitch* this) { + this->actionFunc = ObjLightswitch_On; + this->faceTextureIndex = FACE_EYES_OPEN_SMILING; + + this->color[0] = 255 << 6; + this->color[1] = 255 << 6; + this->color[2] = 255 << 6; + this->alpha = 255 << 6; + + this->flameRingRotSpeed = -0xAA; + this->timer = 0; +} + +void ObjLightswitch_On(ObjLightswitch* this, GlobalContext* globalCtx) { + switch (this->actor.params >> 4 & 3) { + case OBJLIGHTSWITCH_TYPE_STAY_ON: + if (!Flags_GetSwitch(globalCtx, this->actor.params >> 8 & 0x3F)) { + ObjLightswitch_SetupTurnOff(this); + } + break; + case OBJLIGHTSWITCH_TYPE_1: + if (this->collider.base.acFlags & AC_HIT && !(this->prevFrameACflags & AC_HIT)) { + ObjLightswitch_SetupTurnOff(this); + ObjLightswitch_ClearSwitchFlag(this, globalCtx); + } + break; + case OBJLIGHTSWITCH_TYPE_2: + if (!(this->collider.base.acFlags & AC_HIT)) { + if (this->timer >= 7) { + ObjLightswitch_SetupTurnOff(this); + ObjLightswitch_ClearSwitchFlag(this, globalCtx); + } else { + this->timer++; + } + } else { + this->timer = 0; + } + break; + } + this->flameRingRot += this->flameRingRotSpeed; +} + +void ObjLightswitch_SetupTurnOff(ObjLightswitch* this) { + this->actionFunc = ObjLightswitch_TurnOff; + this->toggleDelay = 100; + this->timer = 20; + this->faceTextureIndex = FACE_EYES_OPEN; +} + +void ObjLightswitch_TurnOff(ObjLightswitch* this, GlobalContext* globalCtx) { + if ((this->actor.params >> 4 & 3) != OBJLIGHTSWITCH_TYPE_1 || func_8005B198() == this->actor.category || + this->toggleDelay <= 0) { + this->timer--; + + Math_StepToS(&this->flameRingRotSpeed, 0, 0xA); + this->flameRingRot += this->flameRingRotSpeed; + + this->color[0] = this->timer * (((255 - 155) << 6) / 20) + (155 << 6); + this->color[1] = this->timer * (((255 - 125) << 6) / 20) + (125 << 6); + + if (this->timer <= 0) { + ObjLightswitch_SetupOff(this); + } else if (this->timer == 15) { + this->faceTextureIndex = FACE_EYES_CLOSED; + Audio_PlayActorSound2(&this->actor, NA_SE_EV_FOOT_SWITCH); + } + } +} + +void ObjLightswitch_SetupDisappearDelay(ObjLightswitch* this) { + this->actionFunc = ObjLightswitch_DisappearDelay; + this->toggleDelay = 100; +} + +void ObjLightswitch_DisappearDelay(ObjLightswitch* this, GlobalContext* globalCtx) { + if (func_8005B198() == this->actor.category || this->toggleDelay <= 0) { + ObjLightswitch_SetupDisappear(this); + } +} + +void ObjLightswitch_SetupDisappear(ObjLightswitch* this) { + this->actionFunc = ObjLightswitch_Disappear; + this->alpha = 255 << 6; +} + +void ObjLightswitch_Disappear(ObjLightswitch* this, GlobalContext* globalCtx) { + this->alpha -= 200; + ObjLightswitch_SpawnDisappearEffects(this, globalCtx); + if (this->alpha < 0) { + Actor_Kill(&this->actor); + } +} + +void ObjLightswitch_Update(Actor* thisx, GlobalContext* globalCtx2) { + ObjLightswitch* this = (ObjLightswitch*)thisx; + GlobalContext* globalCtx = globalCtx2; + + if (this->toggleDelay > 0) { + this->toggleDelay--; + } + + this->actionFunc(this, globalCtx); + + if (this->actor.update != NULL) { + if ((this->actor.params & 1) == 1) { + this->actor.world.pos.x = this->actor.child->world.pos.x; + this->actor.world.pos.y = this->actor.child->world.pos.y + 60.0f; + this->actor.world.pos.z = this->actor.child->world.pos.z; + Actor_SetFocus(&this->actor, 0.0f); + } + + this->prevFrameACflags = this->collider.base.acFlags; + this->collider.base.acFlags &= ~AC_HIT; + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void ObjLightswitch_DrawOpa(ObjLightswitch* this, GlobalContext* globalCtx) { + Actor* child; + Vec3f pos; + Vec3s rot; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 809); + func_80093D18(globalCtx->state.gfxCtx); + + gDPSetEnvColor(POLY_OPA_DISP++, (u8)(this->color[0] >> 6), (u8)(this->color[1] >> 6), (u8)(this->color[2] >> 6), + (u8)(this->alpha >> 6)); + gSPSegment(POLY_OPA_DISP++, 0x09, &D_80116280[2]); + + if ((this->actor.params & 1) == 1) { + child = this->actor.child; + this->actor.world.pos.x = child->world.pos.x; + this->actor.world.pos.y = child->world.pos.y + 60.0f; + this->actor.world.pos.z = child->world.pos.z; + Math_Vec3f_Copy(&pos, &this->actor.world.pos); + Matrix_SetTranslateRotateYXZ(pos.x, pos.y, pos.z, &this->actor.shape.rot); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + } else { + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + this->actor.shape.yOffset * this->actor.scale.y; + pos.z = this->actor.world.pos.z; + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 841), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sFaceTextures[this->faceTextureIndex])); + gSPDisplayList(POLY_OPA_DISP++, object_lightswitch_DL_000260); + + rot.x = this->actor.shape.rot.x; + rot.y = this->actor.shape.rot.y; + rot.z = this->actor.shape.rot.z + this->flameRingRot; + Matrix_SetTranslateRotateYXZ(pos.x, pos.y, pos.z, &rot); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 859), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_lightswitch_DL_000398); + + rot.z = this->actor.shape.rot.z - this->flameRingRot; + Matrix_SetTranslateRotateYXZ(pos.x, pos.y, pos.z, &rot); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 873), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, object_lightswitch_DL_000408); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 878); +} + +void ObjLightswitch_DrawXlu(ObjLightswitch* this, GlobalContext* globalCtx) { + s32 pad; + Vec3f sp68; + Vec3s sp60; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 890); + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetEnvColor(POLY_XLU_DISP++, (u8)(this->color[0] >> 6), (u8)(this->color[1] >> 6), (u8)(this->color[2] >> 6), + (u8)(this->alpha >> 6)); + gSPSegment(POLY_XLU_DISP++, 0x09, D_80116280); + + sp68.x = this->actor.world.pos.x; + sp68.y = this->actor.world.pos.y + (this->actor.shape.yOffset * this->actor.scale.y); + sp68.z = this->actor.world.pos.z; + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 912), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sFaceTextures[this->faceTextureIndex])); + gSPDisplayList(POLY_XLU_DISP++, object_lightswitch_DL_000260); + + sp60.x = this->actor.shape.rot.x; + sp60.y = this->actor.shape.rot.y; + sp60.z = this->actor.shape.rot.z + this->flameRingRot; + + Matrix_SetTranslateRotateYXZ(sp68.x, sp68.y, sp68.z, &sp60); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 930), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_lightswitch_DL_000398); + + sp60.z = this->actor.shape.rot.z - this->flameRingRot; + Matrix_SetTranslateRotateYXZ(sp68.x, sp68.y, sp68.z, &sp60); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 944), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, object_lightswitch_DL_000408); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_lightswitch.c", 949); +} + +void ObjLightswitch_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjLightswitch* this = (ObjLightswitch*)thisx; + s32 alpha = this->alpha >> 6 & 0xFF; + + if ((this->actor.params & 1) == 1) { + Collider_UpdateSpheres(0, &this->collider); + } + + if ((this->actor.params >> 4 & 3) == OBJLIGHTSWITCH_TYPE_BURN && (alpha > 0 || alpha < 255)) { + ObjLightswitch_DrawXlu(this, globalCtx); + } else { + ObjLightswitch_DrawOpa(this, globalCtx); + } +} diff --git a/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h b/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h new file mode 100644 index 000000000..eace5a905 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Lightswitch/z_obj_lightswitch.h @@ -0,0 +1,33 @@ +#ifndef Z_OBJ_LIGHTSWITCH_H +#define Z_OBJ_LIGHTSWITCH_H + +#include "ultra64.h" +#include "global.h" + +struct ObjLightswitch; + +typedef void (*ObjLightswitchActionFunc)(struct ObjLightswitch*, GlobalContext*); + +typedef enum { + /* 0 */ OBJLIGHTSWITCH_TYPE_STAY_ON, // doesn't turn off unless the switch flag is cleared some other way + /* 1 */ OBJLIGHTSWITCH_TYPE_1, // turns on and off + /* 2 */ OBJLIGHTSWITCH_TYPE_2, // turns on and off + /* 3 */ OBJLIGHTSWITCH_TYPE_BURN // disappears when turned on +} ObjLightswitch_Type; + +typedef struct ObjLightswitch { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjLightswitchActionFunc actionFunc; + /* 0x0150 */ ColliderJntSph collider; + /* 0x0170 */ ColliderJntSphElement colliderItems[1]; + /* 0x01B0 */ s16 timer; // collision-related threshold and controls animation/logic when turning on/off + /* 0x01B2 */ s16 toggleDelay; // timer ticking down used for delaying tuning on/off or disappearing, can be bypassed + /* 0x01B4 */ s16 faceTextureIndex; // texture used by the center part of the sun + /* 0x01B6 */ s16 color[3]; // rgb, (0-255 color component) << 6 + /* 0x01BC */ s16 alpha; // (0-255 alpha) << 6 + /* 0x01BE */ s16 flameRingRot; + /* 0x01C0 */ s16 flameRingRotSpeed; + /* 0x01C2 */ u8 prevFrameACflags; +} ObjLightswitch; // size = 0x01C4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c b/soh/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c new file mode 100644 index 000000000..fb1c1e140 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.c @@ -0,0 +1,69 @@ +/* + * File: z_obj_makekinsuta.c + * Overlay: ovl_Obj_Makekinsuta + * Description: Skulltula Sprouting from Bean Spot + */ + +#include "z_obj_makekinsuta.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +void ObjMakekinsuta_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjMakekinsuta_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80B98320(ObjMakekinsuta* this, GlobalContext* globalCtx); +void ObjMakekinsuta_DoNothing(ObjMakekinsuta* this, GlobalContext* globalCtx); + +const ActorInit Obj_Makekinsuta_InitVars = { + ACTOR_OBJ_MAKEKINSUTA, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjMakekinsuta), + (ActorFunc)ObjMakekinsuta_Init, + (ActorFunc)Actor_Noop, + (ActorFunc)ObjMakekinsuta_Update, + NULL, + NULL, +}; + +void ObjMakekinsuta_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjMakekinsuta* this = (ObjMakekinsuta*)thisx; + + if ((this->actor.params & 0x6000) == 0x4000) { + osSyncPrintf(VT_FGCOL(BLUE)); + // "Gold Star Enemy(arg_data %x)" + osSyncPrintf("金スタ発生敵(arg_data %x)\n", this->actor.params); + osSyncPrintf(VT_RST); + } else { + osSyncPrintf(VT_COL(YELLOW, BLACK)); + // "Invalid Argument (arg_data %x)(%s %d)" + osSyncPrintf("引数不正 (arg_data %x)(%s %d)\n", this->actor.params, "../z_obj_makekinsuta.c", 119); + osSyncPrintf(VT_RST); + } + this->actionFunc = func_80B98320; +} + +void func_80B98320(ObjMakekinsuta* this, GlobalContext* globalCtx) { + if (this->unk_152 != 0) { + if (this->timer >= 60 && !func_8002DEEC(GET_PLAYER(globalCtx))) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_SW, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, (this->actor.params | 0x8000)); + this->actionFunc = ObjMakekinsuta_DoNothing; + } else { + this->timer++; + } + } else { + this->timer = 0; + } +} + +void ObjMakekinsuta_DoNothing(ObjMakekinsuta* this, GlobalContext* globalCtx) { +} + +void ObjMakekinsuta_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjMakekinsuta* this = (ObjMakekinsuta*)thisx; + + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h b/soh/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h new file mode 100644 index 000000000..3d7ebd086 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Makekinsuta/z_obj_makekinsuta.h @@ -0,0 +1,18 @@ +#ifndef Z_OBJ_MAKEKINSUTA_H +#define Z_OBJ_MAKEKINSUTA_H + +#include "ultra64.h" +#include "global.h" + +struct ObjMakekinsuta; + +typedef void (*ObjMakekinsutaActionFunc)(struct ObjMakekinsuta*, GlobalContext*); + +typedef struct ObjMakekinsuta { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjMakekinsutaActionFunc actionFunc; + /* 0x150 */ s16 timer; + /* 0x152 */ s16 unk_152; +} ObjMakekinsuta; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Makeoshihiki/z_obj_makeoshihiki.c b/soh/src/overlays/actors/ovl_Obj_Makeoshihiki/z_obj_makeoshihiki.c new file mode 100644 index 000000000..50704ed41 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Makeoshihiki/z_obj_makeoshihiki.c @@ -0,0 +1,131 @@ +/* + * File: z_obj_makeoshihiki.c + * Overlay: ovl_Obj_Makeoshihiki + * Description: Push Block puzzle (Hardcoded) + */ + +#include "z_obj_makeoshihiki.h" +#include "overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_5 + +void ObjMakeoshihiki_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjMakeoshihiki_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Obj_Makeoshihiki_InitVars = { + ACTOR_OBJ_MAKEOSHIHIKI, ACTORCAT_PROP, FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, sizeof(ObjMakeoshihiki), (ActorFunc)ObjMakeoshihiki_Init, + (ActorFunc)Actor_Noop, (ActorFunc)Actor_Noop, (ActorFunc)ObjMakeoshihiki_Draw, +}; + +typedef struct { + /* 0x00 */ Vec3f posVecs[3]; + /* 0x24 */ u8 unk_24[3]; + /* 0x27 */ u8 color; + /* 0x28 */ u8 type; + /* 0x2A */ s16 rotY; +} BlockConfig; // size = 0x2C + +static BlockConfig sBlocks[] = { + { { { 660.0f, 460.0f, 660.0f }, { 660.0f, 457.0f, 540.0f }, { 780.0f, 454.0f, 540.0f } }, + 0x00, + 0x00, + 0x03, + 0xFF, + PUSHBLOCK_LARGE_START_ON, + 0x0000 }, + { { { -605.0f, -820.0f, -290.0f }, { -365.0f, -905.0f, -290.0f }, { -365.0f, -905.0f, -290.0f } }, + 0x00, + 0x03, + 0x00, + 0xFF, + PUSHBLOCK_SMALL_START_ON, + 0x0000 } +}; + +static u32 sFlags[3][2] = { { 0, 0 }, { 1, 0 }, { 0, 1 } }; + +static void (*sFlagSwitchFuncs[])(GlobalContext* globalCtx, s32 flag) = { Flags_UnsetSwitch, Flags_SetSwitch }; + +void ObjMakeoshihiki_Init(Actor* thisx, GlobalContext* globalCtx) { + BlockConfig* block = &sBlocks[thisx->home.rot.z & 1]; + s32 typeIdx; + Vec3f* spawnPos; + + if (!((thisx->params >> 6) & 1) && Flags_GetSwitch(globalCtx, thisx->params & 0x3F)) { + typeIdx = 1; + } else if (!((thisx->params >> 0xE) & 1) && Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0x3F)) { + typeIdx = 2; + } else { + typeIdx = 0; + } + + spawnPos = &block->posVecs[typeIdx]; + + if (Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_OBJ_OSHIHIKI, spawnPos->x, spawnPos->y, + spawnPos->z, 0, block->rotY, 0, + ((block->color << 6) & 0xC0) | (block->type & 0xF) | 0xFF00) == NULL) { + // "Push-pull block failure" + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("Error : 押し引きブロック発生失敗(%s %d)\n", "../z_obj_makeoshihiki.c", 194); + osSyncPrintf(VT_RST); + Actor_Kill(thisx); + return; + } + if (block->unk_24[typeIdx] & 2) { + ((ObjOshihiki*)thisx->child)->cantMove = true; + } + thisx->world.rot.z = thisx->shape.rot.z = 0; + osSyncPrintf("(%s)(arg_data %04xF)(angleZ %d)\n", "../z_obj_makeoshihiki.c", thisx->params, thisx->home.rot.z); +} + +void ObjMakeoshihiki_Draw(Actor* thisx, GlobalContext* globalCtx) { + BlockConfig* block = &sBlocks[thisx->home.rot.z & 1]; + s32 i; + s32 sfxCond1; + s32 sfxCond2; + s32 cond; + s32 cond2; + + for (i = 0; i < 3; i++) { + if (Math3D_Vec3fDistSq(&thisx->child->world.pos, &block->posVecs[i]) < 0.001f) { + if (block->unk_24[i] & 1) { + if ((thisx->params >> 6) & 1) { + sfxCond1 = false; + } else { + if (Flags_GetSwitch(globalCtx, thisx->params & 0x3F)) { + cond = true; + } else { + cond = false; + } + sfxCond1 = sFlags[i][0] ^ cond; + } + + if ((thisx->params >> 0xE) & 1) { + sfxCond2 = false; + } else { + if (Flags_GetSwitch(globalCtx, (thisx->params >> 8) & 0x3F)) { + cond2 = true; + } else { + cond2 = false; + } + sfxCond2 = sFlags[i][1] ^ cond2; + } + + if (sfxCond1 || sfxCond2) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + } + + sFlagSwitchFuncs[sFlags[i][0]](globalCtx, thisx->params & 0x3F); + sFlagSwitchFuncs[sFlags[i][1]](globalCtx, (thisx->params >> 8) & 0x3F); + + if (block->unk_24[i] & 2) { + ((ObjOshihiki*)thisx->child)->cantMove = true; + } + + break; + } + } +} diff --git a/soh/src/overlays/actors/ovl_Obj_Makeoshihiki/z_obj_makeoshihiki.h b/soh/src/overlays/actors/ovl_Obj_Makeoshihiki/z_obj_makeoshihiki.h new file mode 100644 index 000000000..16dc3ba47 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Makeoshihiki/z_obj_makeoshihiki.h @@ -0,0 +1,13 @@ +#ifndef Z_OBJ_MAKEOSHIHIKI_H +#define Z_OBJ_MAKEOSHIHIKI_H + +#include "ultra64.h" +#include "global.h" + +struct ObjMakeoshihiki; + +typedef struct ObjMakeoshihiki { + /* 0x0000 */ Actor actor; +} ObjMakeoshihiki; // size = 0x014C + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Mure/z_obj_mure.c b/soh/src/overlays/actors/ovl_Obj_Mure/z_obj_mure.c new file mode 100644 index 000000000..86883f08e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Mure/z_obj_mure.c @@ -0,0 +1,418 @@ +/* + * File: z_obj_mure.c + * Overlay: ovl_Obj_Mure + * Description: Spawns Fish, Bug, Butterfly + */ + +#include "z_obj_mure.h" + +#define FLAGS 0 + +void ObjMure_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjMure_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjMure_Update(Actor* thisx, GlobalContext* globalCtx); + +void ObjMure_InitialAction(ObjMure* this, GlobalContext* globalCtx); +void ObjMure_CulledState(ObjMure* this, GlobalContext* globalCtx); +void ObjMure_ActiveState(ObjMure* this, GlobalContext* globalCtx); + +s32 ObjMure_GetMaxChildSpawns(ObjMure* this); + +const ActorInit Obj_Mure_InitVars = { + ACTOR_OBJ_MURE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjMure), + (ActorFunc)ObjMure_Init, + (ActorFunc)ObjMure_Destroy, + (ActorFunc)ObjMure_Update, + NULL, + NULL, +}; + +typedef enum { + /* 0 */ OBJMURE_TYPE_GRASS, + /* 1 */ OBJMURE_TYPE_UNDEFINED, + /* 2 */ OBJMURE_TYPE_FISH, + /* 3 */ OBJMURE_TYPE_BUGS, + /* 4 */ OBJMURE_TYPE_BUTTERFLY +} ObjMureType; + +typedef enum { + /* 0 */ OBJMURE_CHILD_STATE_0, + /* 1 */ OBJMURE_CHILD_STATE_1, // Dead + /* 2 */ OBJMURE_CHILD_STATE_2 +} ObjMureChildState; + +static f32 sZClip[] = { 1600.0f, 1600.0f, 1000.0f, 1000.0f, 1000.0f }; + +static s32 sMaxChildSpawns[] = { 12, 9, 8, 0 }; + +static s16 sSpawnActorIds[] = { ACTOR_EN_KUSA, 0, ACTOR_EN_FISH, ACTOR_EN_INSECT, ACTOR_EN_BUTTE }; + +static s16 sSpawnParams[] = { 0, 2, -1, 0, -1 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 200, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1200, ICHAIN_STOP), +}; + +s32 ObjMure_SetCullingImpl(Actor* thisx, GlobalContext* globalCtx) { + ObjMure* this = (ObjMure*)thisx; + s32 result; + + switch (this->type) { + case OBJMURE_TYPE_FISH: + case OBJMURE_TYPE_BUGS: + case OBJMURE_TYPE_BUTTERFLY: + Actor_ProcessInitChain(&this->actor, sInitChain); + result = true; + break; + default: + // "Error : Culling is not set.(%s %d)(arg_data 0x%04x)" + osSyncPrintf("Error : カリングの設定がされていません。(%s %d)(arg_data 0x%04x)\n", "../z_obj_mure.c", 204, + this->actor.params); + return false; + } + return result; +} + +s32 ObjMure_SetCulling(Actor* thisx, GlobalContext* globalCtx) { + if (!ObjMure_SetCullingImpl(thisx, globalCtx)) { + return false; + } + return true; +} + +void ObjMure_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjMure* this = (ObjMure*)thisx; + + this->chNum = (thisx->params >> 0xC) & 0x0F; + this->ptn = (thisx->params >> 8) & 0x07; + this->svNum = (thisx->params >> 5) & 0x03; + this->type = thisx->params & 0x1F; + + if (this->ptn >= 4) { + osSyncPrintf("Error 群れな敵 (%s %d)(arg_data 0x%04x)\n", "../z_obj_mure.c", 237, thisx->params); + Actor_Kill(&this->actor); + return; + } else if (this->type >= 5) { + osSyncPrintf("Error 群れな敵 (%s %d)(arg_data 0x%04x)\n", "../z_obj_mure.c", 245, thisx->params); + Actor_Kill(&this->actor); + return; + } else if (!ObjMure_SetCulling(thisx, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + this->actionFunc = ObjMure_InitialAction; + osSyncPrintf("群れな敵 (arg_data 0x%04x)(chNum(%d) ptn(%d) svNum(%d) type(%d))\n", thisx->params, this->chNum, + this->ptn, this->svNum, this->type); + if (ObjMure_GetMaxChildSpawns(this) <= 0) { + osSyncPrintf("Warning : 個体数が設定されていません(%s %d)(arg_data 0x%04x)\n", "../z_obj_mure.c", 268, + thisx->params); + } +} + +void ObjMure_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +s32 ObjMure_GetMaxChildSpawns(ObjMure* this) { + if (this->chNum == 0) { + return sMaxChildSpawns[this->ptn]; + } + return this->chNum; +} + +void ObjMure_GetSpawnPos(Vec3f* outPos, Vec3f* inPos, s32 ptn, s32 idx) { + if (ptn >= 4) { + osSyncPrintf("おかしなの (%s %d)\n", "../z_obj_mure.c", 307); + } + *outPos = *inPos; +} + +void ObjMure_SpawnActors0(ObjMure* this, GlobalContext* globalCtx) { + ActorContext* ac; + s32 i; + Vec3f pos; + s32 pad; + s32 maxChildren = ObjMure_GetMaxChildSpawns(this); + + for (i = 0; i < maxChildren; i++) { + if (this->children[i] != NULL) { + // "Error: I already have a child(%s %d)(arg_data 0x%04x)" + osSyncPrintf("Error : 既に子供がいる(%s %d)(arg_data 0x%04x)\n", "../z_obj_mure.c", 333, + this->actor.params); + } + switch (this->childrenStates[i]) { + case OBJMURE_CHILD_STATE_1: + break; + case OBJMURE_CHILD_STATE_2: + ac = &globalCtx->actorCtx; + ObjMure_GetSpawnPos(&pos, &this->actor.world.pos, this->ptn, i); + this->children[i] = + Actor_Spawn(ac, globalCtx, sSpawnActorIds[this->type], pos.x, pos.y, pos.z, this->actor.world.rot.x, + this->actor.world.rot.y, this->actor.world.rot.z, sSpawnParams[this->type]); + if (this->children[i] != NULL) { + this->children[i]->flags |= ACTOR_FLAG_ENKUSA_CUT; + this->children[i]->room = this->actor.room; + } else { + osSyncPrintf("warning 発生失敗 (%s %d)\n", "../z_obj_mure.c", 359); + } + break; + default: + ac = &globalCtx->actorCtx; + ObjMure_GetSpawnPos(&pos, &this->actor.world.pos, this->ptn, i); + this->children[i] = + Actor_Spawn(ac, globalCtx, sSpawnActorIds[this->type], pos.x, pos.y, pos.z, this->actor.world.rot.x, + this->actor.world.rot.y, this->actor.world.rot.z, sSpawnParams[this->type]); + if (this->children[i] != NULL) { + this->children[i]->room = this->actor.room; + } else { + osSyncPrintf("warning 発生失敗 (%s %d)\n", "../z_obj_mure.c", 382); + } + break; + } + } +} + +void ObjMure_SpawnActors1(ObjMure* this, GlobalContext* globalCtx) { + ActorContext* ac = (ActorContext*)globalCtx; // fake match + Actor* actor = &this->actor; + Vec3f spawnPos; + s32 maxChildren = ObjMure_GetMaxChildSpawns(this); + s32 i; + + for (i = 0; i < maxChildren; i++) { + if (this->children[i] != NULL) { + osSyncPrintf("Error : 既に子供がいる(%s %d)(arg_data 0x%04x)\n", "../z_obj_mure.c", 407, actor->params); + } + ac = &globalCtx->actorCtx; + ObjMure_GetSpawnPos(&spawnPos, &actor->world.pos, this->ptn, i); + this->children[i] = Actor_Spawn(ac, globalCtx, sSpawnActorIds[this->type], spawnPos.x, spawnPos.y, spawnPos.z, + actor->world.rot.x, actor->world.rot.y, actor->world.rot.z, + (this->type == 4 && i == 0) ? 1 : sSpawnParams[this->type]); + if (this->children[i] != NULL) { + this->childrenStates[i] = OBJMURE_CHILD_STATE_0; + this->children[i]->room = actor->room; + } else { + this->childrenStates[i] = OBJMURE_CHILD_STATE_1; + osSyncPrintf("warning 発生失敗 (%s %d)\n", "../z_obj_mure.c", 438); + } + } +} + +void ObjMure_SpawnActors(ObjMure* this, GlobalContext* globalCtx) { + switch (this->svNum) { + case 0: + ObjMure_SpawnActors0(this, globalCtx); + break; + case 1: + ObjMure_SpawnActors1(this, globalCtx); + break; + } +} + +void ObjMure_KillActorsImpl(ObjMure* this, GlobalContext* globalCtx) { + s32 maxChildren = ObjMure_GetMaxChildSpawns(this); + s32 i; + + for (i = 0; i < maxChildren; i++) { + switch (this->childrenStates[i]) { + case OBJMURE_CHILD_STATE_1: + this->children[i] = NULL; + break; + case OBJMURE_CHILD_STATE_2: + if (this->children[i] != NULL) { + Actor_Kill(this->children[i]); + this->children[i] = NULL; + } + break; + default: + if (this->children[i] != NULL) { + if (Actor_HasParent(this->children[i], globalCtx)) { + this->children[i] = NULL; + } else { + Actor_Kill(this->children[i]); + this->children[i] = NULL; + } + } + break; + } + } +} + +void ObjMure_KillActors(ObjMure* this, GlobalContext* globalCtx) { + ObjMure_KillActorsImpl(this, globalCtx); +} + +void ObjMure_CheckChildren(ObjMure* this, GlobalContext* globalCtx) { + s32 maxChildren = ObjMure_GetMaxChildSpawns(this); + s32 i; + + for (i = 0; i < maxChildren; i++) { + if (this->children[i] != NULL) { + if (this->childrenStates[i] == OBJMURE_CHILD_STATE_0) { + if (this->children[i]->update != NULL) { + if (this->children[i]->flags & ACTOR_FLAG_ENKUSA_CUT) { + this->childrenStates[i] = OBJMURE_CHILD_STATE_2; + } + } else { + this->childrenStates[i] = OBJMURE_CHILD_STATE_1; + this->children[i] = NULL; + } + } else if (this->childrenStates[i] == OBJMURE_CHILD_STATE_2 && this->children[i]->update == NULL) { + this->childrenStates[i] = OBJMURE_CHILD_STATE_1; + this->children[i] = NULL; + } + } + } +} + +void ObjMure_InitialAction(ObjMure* this, GlobalContext* globalCtx) { + this->actionFunc = ObjMure_CulledState; +} + +void ObjMure_CulledState(ObjMure* this, GlobalContext* globalCtx) { + if (fabsf(this->actor.projectedPos.z) < sZClip[this->type] || CVar_GetS32("gDisableDrawDistance", 0) != 0) { + this->actionFunc = ObjMure_ActiveState; + this->actor.flags |= ACTOR_FLAG_4; + ObjMure_SpawnActors(this, globalCtx); + } +} + +void ObjMure_SetFollowTargets(ObjMure* this, f32 randMax) { + s32 index; + s32 maxChildren = ObjMure_GetMaxChildSpawns(this); + s32 i; + + for (i = 0; i < maxChildren; i++) { + if (this->children[i] != NULL) { + this->children[i]->child = NULL; + if (Rand_ZeroOne() <= randMax) { + index = Rand_ZeroOne() * (maxChildren - 0.5f); + if (i != index) { + this->children[i]->child = this->children[index]; + } + } + } + } +} + +/** + * Selects a child that will follow after the player + * `idx1` is the index + 1 of the child that will follow the player. If `idx1` is zero, no actor will follow the player + */ +void ObjMure_SetChildToFollowPlayer(ObjMure* this, s32 idx1) { + s32 maxChildren = ObjMure_GetMaxChildSpawns(this); + s32 i; + s32 i2; + s32 j; + + for (i = 0, i2 = 0; i < maxChildren; i++) { + if (this->children[i] != NULL) { + if (i2 < idx1) { + i2++; + this->children[i]->child = this->children[i]; + for (j = 0; j < maxChildren; j++) { + if (i != j && this->children[j]->child == this->children[i]) { + this->children[j]->child = NULL; + } + } + } else if (this->children[i]->child == this->children[i]) { + this->children[i]->child = NULL; + } + } + } +} + +// Fish, Bugs +void ObjMure_GroupBehavior0(ObjMure* this, GlobalContext* globalCtx) { + if (this->unk_1A4 <= 0) { + if (this->unk_1A6) { + this->unk_1A6 = false; + ObjMure_SetFollowTargets(this, (Rand_ZeroOne() * 0.5f) + 0.1f); + if (this->actor.xzDistToPlayer < 60.0f) { + this->unk_1A4 = (s16)(Rand_ZeroOne() * 5.5f) + 4; + } else { + this->unk_1A4 = (s16)(Rand_ZeroOne() * 40.5f) + 4; + } + } else { + this->unk_1A6 = true; + if (this->actor.xzDistToPlayer < 60.0f) { + this->unk_1A4 = (s16)(Rand_ZeroOne() * 10.5f) + 4; + ObjMure_SetFollowTargets(this, (Rand_ZeroOne() * 0.2f) + 0.8f); + } else { + this->unk_1A4 = (s16)(Rand_ZeroOne() * 10.5f) + 4; + ObjMure_SetFollowTargets(this, (Rand_ZeroOne() * 0.2f) + 0.6f); + } + } + } + if (this->actor.xzDistToPlayer < 120.0f) { + this->unk_1A8++; + } else { + this->unk_1A8 = 0; + } + if (this->unk_1A8 >= 80) { + ObjMure_SetChildToFollowPlayer(this, 1); + } else { + ObjMure_SetChildToFollowPlayer(this, 0); + } +} + +// Butterflies +void ObjMure_GroupBehavior1(ObjMure* this, GlobalContext* globalCtx) { + s32 maxChildren; + s32 i; + + if (this->unk_1A4 <= 0) { + if (this->unk_1A6) { + this->unk_1A6 = false; + ObjMure_SetFollowTargets(this, Rand_ZeroOne() * 0.2f); + if (this->actor.xzDistToPlayer < 60.0f) { + this->unk_1A4 = (s16)(Rand_ZeroOne() * 5.5f) + 4; + } else { + this->unk_1A4 = (s16)(Rand_ZeroOne() * 40.5f) + 4; + } + } else { + this->unk_1A6 = true; + ObjMure_SetFollowTargets(this, Rand_ZeroOne() * 0.7f); + this->unk_1A4 = (s16)(Rand_ZeroOne() * 10.5f) + 4; + } + } + + maxChildren = ObjMure_GetMaxChildSpawns(this); + for (i = 0; i < maxChildren; i++) { + if (this->children[i] != NULL) { + if (this->children[i]->child != NULL && this->children[i]->child->update == NULL) { + this->children[i]->child = NULL; + } + } + } +} + +static ObjMureActionFunc sTypeGroupBehaviorFunc[] = { + NULL, NULL, ObjMure_GroupBehavior0, ObjMure_GroupBehavior0, ObjMure_GroupBehavior1, +}; + +void ObjMure_ActiveState(ObjMure* this, GlobalContext* globalCtx) { + ObjMure_CheckChildren(this, globalCtx); + if (sZClip[this->type] + 40.0f <= fabsf(this->actor.projectedPos.z) && + CVar_GetS32("gDisableDrawDistance", 1) != 0) { + this->actionFunc = ObjMure_CulledState; + this->actor.flags &= ~ACTOR_FLAG_4; + ObjMure_KillActors(this, globalCtx); + } else if (sTypeGroupBehaviorFunc[this->type] != NULL) { + sTypeGroupBehaviorFunc[this->type](this, globalCtx); + } +} + +void ObjMure_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjMure* this = (ObjMure*)thisx; + + if (this->unk_1A4 > 0) { + this->unk_1A4--; + } + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Mure/z_obj_mure.h b/soh/src/overlays/actors/ovl_Obj_Mure/z_obj_mure.h new file mode 100644 index 000000000..e7c83d94b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Mure/z_obj_mure.h @@ -0,0 +1,27 @@ +#ifndef Z_OBJ_MURE_H +#define Z_OBJ_MURE_H + +#include "ultra64.h" +#include "global.h" + +struct ObjMure; + +typedef void (*ObjMureActionFunc)(struct ObjMure*, GlobalContext*); + +#define OBJMURE_MAX_SPAWNS 15 + +typedef struct ObjMure { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjMureActionFunc actionFunc; + /* 0x0150 */ s16 chNum; + /* 0x0152 */ s16 ptn; + /* 0x0154 */ s16 svNum; + /* 0x0156 */ s16 type; + /* 0x0158 */ Actor* children[OBJMURE_MAX_SPAWNS]; + /* 0x0194 */ u8 childrenStates[OBJMURE_MAX_SPAWNS]; + /* 0x01A4 */ s16 unk_1A4; + /* 0x01A6 */ s16 unk_1A6; + /* 0x01A8 */ s16 unk_1A8; +} ObjMure; // size = 0x01AC + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Mure2/z_obj_mure2.c b/soh/src/overlays/actors/ovl_Obj_Mure2/z_obj_mure2.c new file mode 100644 index 000000000..27c597b3a --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Mure2/z_obj_mure2.c @@ -0,0 +1,229 @@ +/* + * File: z_obj_mure2.c + * Overlay: ovl_Obj_Mure2 + * Description: Rock/Bush groups + */ + +#include "z_obj_mure2.h" + +#define FLAGS 0 + +typedef void (*ObjMure2SetPosFunc)(Vec3f* vec, ObjMure2* this); + +typedef struct { + s16 radius; + s16 angle; +} Mure2sScatteredShrubInfo; + +void ObjMure2_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjMure2_Update(Actor* thisx, GlobalContext* globalCtx); + +void ObjMure2_SetPosShrubCircle(Vec3f* vec, ObjMure2* this); +void ObjMure2_SetPosShrubScattered(Vec3f* vec, ObjMure2* this); +void ObjMure2_SetPosRockCircle(Vec3f* vec, ObjMure2* this); +void ObjMure2_Wait(ObjMure2* this, GlobalContext* globalCtx); +void func_80B9A668(ObjMure2* this, GlobalContext* globalCtx); +void func_80B9A6F8(ObjMure2* this, GlobalContext* globalCtx); +void ObjMure2_SetupWait(ObjMure2* this); +void func_80B9A658(ObjMure2* this); +void func_80B9A6E8(ObjMure2* this); + +const ActorInit Obj_Mure2_InitVars = { + ACTOR_OBJ_MURE2, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjMure2), + (ActorFunc)ObjMure2_Init, + (ActorFunc)Actor_Noop, + (ActorFunc)ObjMure2_Update, + NULL, + NULL, +}; + +static f32 sDistSquared1[] = { SQ(1600.0f), SQ(1600.0f), SQ(1600.0f) }; + +static f32 sDistSquared2[] = { SQ(1705.0f), SQ(1705.0f), SQ(1705.0f) }; + +static s16 D_80B9A818[] = { 9, 12, 8 }; + +static s16 sActorSpawnIDs[] = { ACTOR_EN_KUSA, ACTOR_EN_KUSA, ACTOR_EN_ISHI }; + +void ObjMure2_SetPosShrubCircle(Vec3f* vec, ObjMure2* this) { + s32 i; + + Math_Vec3f_Copy(vec, &this->actor.world.pos); + for (i = 1; i < D_80B9A818[this->actor.params & 3]; i++) { + Math_Vec3f_Copy(vec + i, &this->actor.world.pos); + (vec + i)->x += (80.0f * Math_SinS((i - 1) * 0x2000)); + (vec + i)->z += (80.0f * Math_CosS((i - 1) * 0x2000)); + } +} + +static Mure2sScatteredShrubInfo sScatteredShrubInfo[] = { + { 40, 0x0666 }, { 40, 0x2CCC }, { 40, 0x5999 }, { 40, 0x8666 }, { 20, 0xC000 }, { 80, 0x1333 }, + { 80, 0x4000 }, { 80, 0x6CCC }, { 80, 0x9333 }, { 80, 0xACCC }, { 80, 0xC666 }, { 60, 0xE000 }, +}; + +void ObjMure2_SetPosShrubScattered(Vec3f* vec, ObjMure2* this) { + s32 i; + + for (i = 0; i < D_80B9A818[this->actor.params & 3]; i++) { + Math_Vec3f_Copy(vec + i, &this->actor.world.pos); + (vec + i)->x += (sScatteredShrubInfo[i].radius * Math_CosS(sScatteredShrubInfo[i].angle)); + (vec + i)->z -= (sScatteredShrubInfo[i].radius * Math_SinS(sScatteredShrubInfo[i].angle)); + } +} + +void ObjMure2_SetPosRockCircle(Vec3f* vec, ObjMure2* this) { + s32 i; + + for (i = 0; i < D_80B9A818[this->actor.params & 3]; i++) { + Math_Vec3f_Copy(vec + i, &this->actor.world.pos); + (vec + i)->x += (80.0f * Math_SinS(i * 0x2000)); + (vec + i)->z += (80.0f * Math_CosS(i * 0x2000)); + } +} + +void ObjMure2_SetActorSpawnParams(s16* params, ObjMure2* this) { + static s16 actorSpawnParams[] = { 0, 0, 0 }; + s16 dropTable = (this->actor.params >> 8) & 0xF; + + if (dropTable >= 13) { + dropTable = 0; + } + *params = actorSpawnParams[this->actor.params & 3] & 0xF0FF; + *params |= (dropTable << 8); +} + +void ObjMure2_SpawnActors(ObjMure2* this, GlobalContext* globalCtx) { + static ObjMure2SetPosFunc setPosFunc[] = { + ObjMure2_SetPosShrubCircle, + ObjMure2_SetPosShrubScattered, + ObjMure2_SetPosRockCircle, + }; + s32 actorNum = this->actor.params & 3; + s32 i; + Vec3f spawnPos[12]; + s16 params; + + setPosFunc[actorNum](spawnPos, this); + ObjMure2_SetActorSpawnParams(¶ms, this); + + for (i = 0; i < D_80B9A818[actorNum]; i++) { + if (this->actorSpawnPtrList[i] != NULL) { + // "Warning : I already have a child (%s %d)(arg_data 0x%04x)" + osSyncPrintf("Warning : 既に子供がいる(%s %d)(arg_data 0x%04x)\n", "../z_obj_mure2.c", 269, + this->actor.params); + continue; + } + + if (((this->currentActorNum >> i) & 1) == 0) { + this->actorSpawnPtrList[i] = + Actor_Spawn(&globalCtx->actorCtx, globalCtx, sActorSpawnIDs[actorNum], spawnPos[i].x, spawnPos[i].y, + spawnPos[i].z, this->actor.world.rot.x, 0, this->actor.world.rot.z, params); + if (this->actorSpawnPtrList[i] != NULL) { + this->actorSpawnPtrList[i]->room = this->actor.room; + } + } + } +} + +void ObjMure2_CleanupAndDie(ObjMure2* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < D_80B9A818[this->actor.params & 3]; i++) { + if (((this->currentActorNum >> i) & 1) == 0) { + if (this->actorSpawnPtrList[i] != NULL) { + if (Actor_HasParent(this->actorSpawnPtrList[i], globalCtx)) { + this->currentActorNum |= (1 << i); + } else { + Actor_Kill(this->actorSpawnPtrList[i]); + } + this->actorSpawnPtrList[i] = NULL; + } + } else { + this->actorSpawnPtrList[i] = NULL; + } + } +} + +void func_80B9A534(ObjMure2* this) { + s32 i; + + for (i = 0; i < D_80B9A818[this->actor.params & 3]; i++) { + if (this->actorSpawnPtrList[i] != NULL && (((this->currentActorNum >> i) & 1) == 0) && + (this->actorSpawnPtrList[i]->update == NULL)) { + this->currentActorNum |= (1 << i); + this->actorSpawnPtrList[i] = NULL; + } + } +} + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 2100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 100, ICHAIN_STOP), +}; + +void ObjMure2_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjMure2* this = (ObjMure2*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + if (globalCtx->csCtx.state != CS_STATE_IDLE) { + this->actor.uncullZoneForward += 1200.0f; + } + ObjMure2_SetupWait(this); +} + +void ObjMure2_SetupWait(ObjMure2* this) { + this->actionFunc = ObjMure2_Wait; +} + +void ObjMure2_Wait(ObjMure2* this, GlobalContext* globalCtx) { + func_80B9A658(this); +} + +void func_80B9A658(ObjMure2* this) { + this->actionFunc = func_80B9A668; +} + +void func_80B9A668(ObjMure2* this, GlobalContext* globalCtx) { + if (Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z) < + (sDistSquared1[this->actor.params & 3] * this->unk_184) || + CVar_GetS32("gDisableDrawDistance", 0) != 0) { + this->actor.flags |= ACTOR_FLAG_4; + ObjMure2_SpawnActors(this, globalCtx); + func_80B9A6E8(this); + } +} + +void func_80B9A6E8(ObjMure2* this) { + this->actionFunc = func_80B9A6F8; +} + +void func_80B9A6F8(ObjMure2* this, GlobalContext* globalCtx) { + func_80B9A534(this); + + if (CVar_GetS32("gDisableDrawDistance", 0) != 0) { + return; + } + + if ((sDistSquared2[this->actor.params & 3] * this->unk_184) <= + Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z)) { + this->actor.flags &= ~ACTOR_FLAG_4; + ObjMure2_CleanupAndDie(this, globalCtx); + func_80B9A658(this); + } +} + +void ObjMure2_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjMure2* this = (ObjMure2*)thisx; + + if (globalCtx->csCtx.state == CS_STATE_IDLE) { + this->unk_184 = 1.0f; + } else { + this->unk_184 = 4.0f; + } + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Mure2/z_obj_mure2.h b/soh/src/overlays/actors/ovl_Obj_Mure2/z_obj_mure2.h new file mode 100644 index 000000000..c89e0139f --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Mure2/z_obj_mure2.h @@ -0,0 +1,19 @@ +#ifndef Z_OBJ_MURE2_H +#define Z_OBJ_MURE2_H + +#include "ultra64.h" +#include "global.h" + +struct ObjMure2; + +typedef void (*ObjMure2ActionFunc)(struct ObjMure2*, GlobalContext*); + +typedef struct ObjMure2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjMure2ActionFunc actionFunc; + /* 0x0150 */ Actor* actorSpawnPtrList[12]; // pointers to all of the actors spawned by the parent + /* 0x0180 */ u16 currentActorNum; // used to keep track of of the index to actorSpawnPtrList + /* 0x0184 */ f32 unk_184; // some sort of distance +} ObjMure2; // size = 0x0188 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Mure3/z_obj_mure3.c b/soh/src/overlays/actors/ovl_Obj_Mure3/z_obj_mure3.c new file mode 100644 index 000000000..d99e72563 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Mure3/z_obj_mure3.c @@ -0,0 +1,198 @@ +/* + * File: z_obj_mure3.c + * Overlay: ovl_Obj_Mure3 + * Description: Tower of Rupees + */ + +#include "z_obj_mure3.h" + +#define FLAGS 0 + +void ObjMure3_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjMure3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjMure3_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80B9AF24(ObjMure3* this); +void func_80B9AF34(ObjMure3* this, GlobalContext* globalCtx); +void func_80B9AF54(ObjMure3* this); +void func_80B9AF64(ObjMure3* this, GlobalContext* globalCtx); +void func_80B9AFEC(ObjMure3* this); +void func_80B9AFFC(ObjMure3* this, GlobalContext* globalCtx); + +const ActorInit Obj_Mure3_InitVars = { + ACTOR_OBJ_MURE3, + ACTORCAT_BG, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjMure3), + (ActorFunc)ObjMure3_Init, + (ActorFunc)ObjMure3_Destroy, + (ActorFunc)ObjMure3_Update, + NULL, + NULL, +}; + +static s16 sRupeeCounts[] = { 5, 5, 7, 0 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 100, ICHAIN_STOP), +}; + +void func_80B9A9D0(ObjMure3* this, GlobalContext* globalCtx) { + s32 i; + Vec3f spawnPos; + + Math_Vec3f_Copy(&spawnPos, &this->actor.world.pos); + for (i = 0; i < 5; i++, spawnPos.y += 20.0f) { + if (!((this->unk_16C >> i) & 1)) { + this->unk_150[i] = Item_DropCollectible2(globalCtx, &spawnPos, 0x4000 | ITEM00_RUPEE_BLUE); + if (this->unk_150[i] != NULL) { + this->unk_150[i]->actor.room = this->actor.room; + } + } + } +} + +void func_80B9AA90(ObjMure3* this, GlobalContext* globalCtx) { + s32 i; + Vec3f spawnPos; + f32 sn = Math_SinS(this->actor.world.rot.y); + f32 cos = Math_CosS(this->actor.world.rot.y); + f32 radius; + + spawnPos.y = this->actor.world.pos.y; + + for (i = 0, radius = -40.0f; i < 5; i++, radius += 20.0f) { + if (!((this->unk_16C >> i) & 1)) { + spawnPos.x = this->actor.world.pos.x + (sn * radius); + spawnPos.z = this->actor.world.pos.z + (cos * radius); + this->unk_150[i] = Item_DropCollectible2(globalCtx, &spawnPos, 0x4000 | ITEM00_RUPEE_GREEN); + if (this->unk_150[i] != NULL) { + this->unk_150[i]->actor.room = this->actor.room; + } + } + } +} + +void func_80B9ABA0(ObjMure3* this, GlobalContext* globalCtx) { + s32 i; + Vec3f spawnPos; + s16 yRot; + + spawnPos.y = this->actor.world.pos.y; + yRot = this->actor.world.rot.y; + for (i = 0; i < 6; i++) { + if (!((this->unk_16C >> i) & 1)) { + spawnPos.x = (Math_SinS(yRot) * 40.0f) + this->actor.world.pos.x; + spawnPos.z = (Math_CosS(yRot) * 40.0f) + this->actor.world.pos.z; + this->unk_150[i] = Item_DropCollectible2(globalCtx, &spawnPos, 0x4000 | ITEM00_RUPEE_GREEN); + if (this->unk_150[i] != NULL) { + this->unk_150[i]->actor.room = this->actor.room; + } + } + yRot += 0x2AAA; + } + if (!((this->unk_16C >> 6) & 1)) { + spawnPos.x = this->actor.world.pos.x; + spawnPos.z = this->actor.world.pos.z; + this->unk_150[6] = Item_DropCollectible2(globalCtx, &spawnPos, 0x4000 | ITEM00_RUPEE_RED); + if (this->unk_150[6] != NULL) { + this->unk_150[6]->actor.room = this->actor.room; + } + } +} + +void func_80B9ACE4(ObjMure3* this, GlobalContext* globalCtx) { + s16 count = sRupeeCounts[(this->actor.params >> 13) & 7]; + s32 i; + + for (i = 0; i < count; i++) { + EnItem00** collectible = &this->unk_150[i]; + + if (!((this->unk_16C >> i) & 1) && (*collectible != NULL)) { + if (Actor_HasParent(&(*collectible)->actor, globalCtx) || ((*collectible)->actor.update == NULL)) { + this->unk_16C |= (1 << i); + } else { + Actor_Kill(&(*collectible)->actor); + } + } + *collectible = NULL; + } +} + +void func_80B9ADCC(ObjMure3* this, GlobalContext* globalCtx) { + s16 count = sRupeeCounts[(this->actor.params >> 13) & 7]; + s32 i; + + for (i = 0; i < count; i++) { + EnItem00** collectible = &this->unk_150[i]; + + if ((*collectible != NULL) && !((this->unk_16C >> i) & 1)) { + if (Actor_HasParent(&(*collectible)->actor, globalCtx)) { + Flags_SetSwitch(globalCtx, this->actor.params & 0x3F); + } + if ((*collectible)->actor.update == NULL) { + this->unk_16C |= (1 << i); + this->unk_150[i] = NULL; + } + } + } +} + +void ObjMure3_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjMure3* this = (ObjMure3*)thisx; + + if (Flags_GetSwitch(globalCtx, this->actor.params & 0x3F)) { + Actor_Kill(&this->actor); + return; + } + Actor_ProcessInitChain(&this->actor, sInitChain); + func_80B9AF24(this); +} + +void ObjMure3_Destroy(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_80B9AF24(ObjMure3* this) { + this->actionFunc = func_80B9AF34; +} + +void func_80B9AF34(ObjMure3* this, GlobalContext* globalCtx) { + func_80B9AF54(this); +} + +void func_80B9AF54(ObjMure3* this) { + this->actionFunc = func_80B9AF64; +} + +void func_80B9AF64(ObjMure3* this, GlobalContext* globalCtx) { + static ObjMure3SpawnFunc spawnFuncs[] = { func_80B9A9D0, func_80B9AA90, func_80B9ABA0 }; + + if (Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z) < SQ(1150.0f)) { + this->actor.flags |= ACTOR_FLAG_4; + spawnFuncs[(this->actor.params >> 13) & 7](this, globalCtx); + func_80B9AFEC(this); + } +} + +void func_80B9AFEC(ObjMure3* this) { + this->actionFunc = func_80B9AFFC; +} + +void func_80B9AFFC(ObjMure3* this, GlobalContext* globalCtx) { + func_80B9ADCC(this, globalCtx); + if (Math3D_Dist1DSq(this->actor.projectedPos.x, this->actor.projectedPos.z) >= SQ(1450.0f)) { + this->actor.flags &= ~ACTOR_FLAG_4; + func_80B9ACE4(this, globalCtx); + func_80B9AF54(this); + } +} + +void ObjMure3_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjMure3* this = (ObjMure3*)thisx; + + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Mure3/z_obj_mure3.h b/soh/src/overlays/actors/ovl_Obj_Mure3/z_obj_mure3.h new file mode 100644 index 000000000..b4e71c1b9 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Mure3/z_obj_mure3.h @@ -0,0 +1,19 @@ +#ifndef Z_OBJ_MURE3_H +#define Z_OBJ_MURE3_H + +#include "ultra64.h" +#include "global.h" + +struct ObjMure3; + +typedef void (*ObjMure3ActionFunc)(struct ObjMure3*, GlobalContext*); +typedef void (*ObjMure3SpawnFunc)(struct ObjMure3*, GlobalContext*); + +typedef struct ObjMure3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjMure3ActionFunc actionFunc; + /* 0x0150 */ EnItem00* unk_150[7]; + /* 0x016C */ u16 unk_16C; +} ObjMure3; // size = 0x0170 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c b/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c new file mode 100644 index 000000000..76e285881 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.c @@ -0,0 +1,686 @@ +/* + * File: z_obj_oshihiki.c + * Overlay: ovl_Obj_Oshihiki + * Description: Push Block + */ + +#include "z_obj_oshihiki.h" +#include "overlays/actors/ovl_Obj_Switch/z_obj_switch.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" + +#define FLAGS ACTOR_FLAG_4 + +void ObjOshihiki_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjOshihiki_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjOshihiki_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjOshihiki_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjOshihiki_SetupOnScene(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_OnScene(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_SetupOnActor(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_OnActor(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_SetupPush(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_Push(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_SetupFall(ObjOshihiki* this, GlobalContext* globalCtx); +void ObjOshihiki_Fall(ObjOshihiki* this, GlobalContext* globalCtx); + +const ActorInit Obj_Oshihiki_InitVars = { + ACTOR_OBJ_OSHIHIKI, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(ObjOshihiki), + (ActorFunc)ObjOshihiki_Init, + (ActorFunc)ObjOshihiki_Destroy, + (ActorFunc)ObjOshihiki_Update, + (ActorFunc)ObjOshihiki_Draw, + NULL, +}; + +static f32 sScales[] = { + (1 / 10.0f), (1 / 6.0f), (1 / 5.0f), (1 / 3.0f), (1 / 10.0f), (1 / 6.0f), (1 / 5.0f), (1 / 3.0f), +}; + +static Color_RGB8 sColors[][4] = { + { { 110, 86, 40 }, { 110, 86, 40 }, { 110, 86, 40 }, { 110, 86, 40 } }, // deku tree + { { 106, 120, 110 }, { 104, 80, 20 }, { 0, 0, 0 }, { 0, 0, 0 } }, // dodongos cavern + { { 142, 99, 86 }, { 72, 118, 96 }, { 0, 0, 0 }, { 0, 0, 0 } }, // forest temple + { { 210, 150, 80 }, { 210, 170, 80 }, { 0, 0, 0 }, { 0, 0, 0 } }, // fire temple + { { 102, 144, 182 }, { 176, 167, 100 }, { 100, 167, 100 }, { 117, 97, 96 } }, // water temple + { { 232, 210, 176 }, { 232, 210, 176 }, { 232, 210, 176 }, { 232, 210, 176 } }, // spirit temple + { { 135, 125, 95 }, { 135, 125, 95 }, { 135, 125, 95 }, { 135, 125, 95 } }, // shadow temple + { { 255, 255, 255 }, { 255, 255, 255 }, { 255, 255, 255 }, { 255, 255, 255 } }, // ganons castle + { { 232, 210, 176 }, { 232, 210, 176 }, { 232, 210, 176 }, { 232, 210, 176 } }, // gerudo training grounds +}; + +static s16 sScenes[] = { + SCENE_YDAN, SCENE_DDAN, SCENE_BMORI1, SCENE_HIDAN, SCENE_MIZUSIN, + SCENE_JYASINZOU, SCENE_HAKADAN, SCENE_GANON, SCENE_MEN, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 500, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP), +}; + +// The vertices and center of the bottom face +static Vec3f sColCheckPoints[5] = { + { 29.99f, 1.01f, -29.99f }, { -29.99f, 1.01f, -29.99f }, { -29.99f, 1.01f, 29.99f }, + { 29.99f, 1.01f, 29.99f }, { 0.0f, 1.01f, 0.0f }, +}; + +static Vec2f sFaceVtx[] = { + { -30.0f, 0.0f }, + { 30.0f, 0.0f }, + { -30.0f, 60.0f }, + { 30.0f, 60.0f }, +}; + +static Vec2f sFaceDirection[] = { + { 1.0f, 1.0f }, + { -1.0f, 1.0f }, + { 1.0f, -1.0f }, + { -1.0f, -1.0f }, +}; + +void ObjOshihiki_InitDynapoly(ObjOshihiki* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG registration failure" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_obj_oshihiki.c", 280, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void ObjOshihiki_RotateXZ(Vec3f* out, Vec3f* in, f32 sn, f32 cs) { + out->x = (in->z * sn) + (in->x * cs); + out->y = in->y; + out->z = (in->z * cs) - (in->x * sn); +} + +s32 ObjOshihiki_StrongEnough(ObjOshihiki* this) { + s32 strength; + + if (this->cantMove) { + return 0; + } + strength = Player_GetStrength(); + switch (this->dyna.actor.params & 0xF) { + case PUSHBLOCK_SMALL_START_ON: + case PUSHBLOCK_MEDIUM_START_ON: + case PUSHBLOCK_SMALL_START_OFF: + case PUSHBLOCK_MEDIUM_START_OFF: + return 1; + break; + case PUSHBLOCK_LARGE_START_ON: + case PUSHBLOCK_LARGE_START_OFF: + return strength >= PLAYER_STR_BRACELET; + break; + case PUSHBLOCK_HUGE_START_ON: + case PUSHBLOCK_HUGE_START_OFF: + return strength >= PLAYER_STR_SILVER_G; + break; + } + return 0; +} + +void ObjOshihiki_ResetFloors(ObjOshihiki* this) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(this->floorBgIds); i++) { + this->floorBgIds[i] = BGCHECK_SCENE; + } +} + +ObjOshihiki* ObjOshihiki_GetBlockUnder(ObjOshihiki* this, GlobalContext* globalCtx) { + DynaPolyActor* dynaPolyActor; + + if ((this->floorBgIds[this->highestFloor] != BGCHECK_SCENE) && + (fabsf(this->dyna.actor.floorHeight - this->dyna.actor.world.pos.y) < 0.001f)) { + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->floorBgIds[this->highestFloor]); + if ((dynaPolyActor != NULL) && (dynaPolyActor->actor.id == ACTOR_OBJ_OSHIHIKI)) { + return (ObjOshihiki*)dynaPolyActor; + } + } + return NULL; +} + +void ObjOshihiki_UpdateInitPos(ObjOshihiki* this) { + if (this->dyna.actor.home.pos.x < this->dyna.actor.world.pos.x) { + while ((this->dyna.actor.world.pos.x - this->dyna.actor.home.pos.x) >= 20.0f) { + this->dyna.actor.home.pos.x += 20.0f; + } + } else { + while ((this->dyna.actor.home.pos.x - this->dyna.actor.world.pos.x) >= 20.0f) { + this->dyna.actor.home.pos.x -= 20.0f; + } + } + if (this->dyna.actor.home.pos.z < this->dyna.actor.world.pos.z) { + while ((this->dyna.actor.world.pos.z - this->dyna.actor.home.pos.z) >= 20.0f) { + this->dyna.actor.home.pos.z += 20.0f; + } + } else { + while ((this->dyna.actor.home.pos.z - this->dyna.actor.world.pos.z) >= 20.0f) { + this->dyna.actor.home.pos.z -= 20.0f; + } + } +} + +s32 ObjOshihiki_NoSwitchPress(ObjOshihiki* this, DynaPolyActor* dyna, GlobalContext* globalCtx) { + s16 dynaSwitchFlag; + + if (dyna == NULL) { + return 1; + } else if (dyna->actor.id == ACTOR_OBJ_SWITCH) { + dynaSwitchFlag = (dyna->actor.params >> 8) & 0x3F; + switch (dyna->actor.params & 0x33) { + case 0x20: // Normal blue switch + if ((dynaSwitchFlag == ((this->dyna.actor.params >> 8) & 0x3F)) && + Flags_GetSwitch(globalCtx, dynaSwitchFlag)) { + return 0; + } + break; + case 0x30: // Inverse blue switch + if ((dynaSwitchFlag == ((this->dyna.actor.params >> 8) & 0x3F)) && + !Flags_GetSwitch(globalCtx, dynaSwitchFlag)) { + return 0; + } + break; + } + } + return 1; +} + +void ObjOshihiki_CheckType(ObjOshihiki* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xF) { + case PUSHBLOCK_SMALL_START_ON: + case PUSHBLOCK_MEDIUM_START_ON: + case PUSHBLOCK_LARGE_START_ON: + case PUSHBLOCK_HUGE_START_ON: + case PUSHBLOCK_SMALL_START_OFF: + case PUSHBLOCK_MEDIUM_START_OFF: + case PUSHBLOCK_LARGE_START_OFF: + case PUSHBLOCK_HUGE_START_OFF: + ObjOshihiki_InitDynapoly(this, globalCtx, &gPushBlockCol, 1); + break; + default: + // "Error : type cannot be determined" + osSyncPrintf("Error : タイプが判別できない(%s %d)(arg_data 0x%04x)\n", "../z_obj_oshihiki.c", 444, + this->dyna.actor.params); + break; + } +} + +void ObjOshihiki_SetScale(ObjOshihiki* this, GlobalContext* globalCtx) { + Actor_SetScale(&this->dyna.actor, sScales[this->dyna.actor.params & 0xF]); +} + +void ObjOshihiki_SetTexture(ObjOshihiki* this, GlobalContext* globalCtx) { + switch (this->dyna.actor.params & 0xF) { + case PUSHBLOCK_SMALL_START_ON: + case PUSHBLOCK_MEDIUM_START_ON: + case PUSHBLOCK_SMALL_START_OFF: + case PUSHBLOCK_MEDIUM_START_OFF: + this->texture = gPushBlockSilverTex; + break; + case PUSHBLOCK_LARGE_START_ON: + case PUSHBLOCK_LARGE_START_OFF: + this->texture = gPushBlockBaseTex; + break; + case PUSHBLOCK_HUGE_START_ON: + case PUSHBLOCK_HUGE_START_OFF: + this->texture = gPushBlockGrayTex; + break; + } +} + +void ObjOshihiki_SetColor(ObjOshihiki* this, GlobalContext* globalCtx) { + Color_RGB8* src; + Color_RGB8* color = &this->color; + s16 paramsColorIdx; + s32 i; + + paramsColorIdx = (this->dyna.actor.params >> 6) & 3; + + for (i = 0; i < ARRAY_COUNT(sScenes); i++) { + if (sScenes[i] == globalCtx->sceneNum) { + break; + } + } + + if (i >= ARRAY_COUNT(sColors)) { + // "Error : scene_data_ID cannot be determined" + osSyncPrintf("Error : scene_data_ID が判別できない。(%s %d)\n", "../z_obj_oshihiki.c", 579); + color->r = color->g = color->b = 255; + } else { + src = &sColors[i][paramsColorIdx]; + color->r = src->r; + color->g = src->g; + color->b = src->b; + } +} + +void ObjOshihiki_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjOshihiki* this = (ObjOshihiki*)thisx; + + ObjOshihiki_CheckType(this, globalCtx); + + if ((((this->dyna.actor.params >> 8) & 0xFF) >= 0) && (((this->dyna.actor.params >> 8) & 0xFF) <= 0x3F)) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8) & 0x3F)) { + switch (this->dyna.actor.params & 0xF) { + case PUSHBLOCK_SMALL_START_ON: + case PUSHBLOCK_MEDIUM_START_ON: + case PUSHBLOCK_LARGE_START_ON: + case PUSHBLOCK_HUGE_START_ON: + Actor_Kill(&this->dyna.actor); + return; + } + } else { + switch (this->dyna.actor.params & 0xF) { + case PUSHBLOCK_SMALL_START_OFF: + case PUSHBLOCK_MEDIUM_START_OFF: + case PUSHBLOCK_LARGE_START_OFF: + case PUSHBLOCK_HUGE_START_OFF: + Actor_Kill(&this->dyna.actor); + return; + } + } + } + + ObjOshihiki_SetScale(this, globalCtx); + ObjOshihiki_SetTexture(this, globalCtx); + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; + ObjOshihiki_SetColor(this, globalCtx); + ObjOshihiki_ResetFloors(this); + ObjOshihiki_SetupOnActor(this, globalCtx); + // "(dungeon keep push-pull block)" + osSyncPrintf("(dungeon keep 押し引きブロック)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void ObjOshihiki_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjOshihiki* this = (ObjOshihiki*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +void ObjOshihiki_SetFloors(ObjOshihiki* this, GlobalContext* globalCtx) { + s32 i; + + for (i = 0; i < 5; i++) { + Vec3f colCheckPoint; + Vec3f colCheckOffset; + CollisionPoly** floorPoly; + s32* floorBgId; + + colCheckOffset.x = sColCheckPoints[i].x * (this->dyna.actor.scale.x * 10.0f); + colCheckOffset.y = sColCheckPoints[i].y * (this->dyna.actor.scale.y * 10.0f); + colCheckOffset.z = sColCheckPoints[i].z * (this->dyna.actor.scale.z * 10.0f); + ObjOshihiki_RotateXZ(&colCheckPoint, &colCheckOffset, this->yawSin, this->yawCos); + colCheckPoint.x += this->dyna.actor.world.pos.x; + colCheckPoint.y += this->dyna.actor.prevPos.y; + colCheckPoint.z += this->dyna.actor.world.pos.z; + + floorPoly = &this->floorPolys[i]; + floorBgId = &this->floorBgIds[i]; + this->floorHeights[i] = BgCheck_EntityRaycastFloor6(&globalCtx->colCtx, floorPoly, floorBgId, &this->dyna.actor, + &colCheckPoint, 0.0f); + } +} + +s16 ObjOshihiki_GetHighestFloor(ObjOshihiki* this) { + s16 highestFloor = 0; + s16 temp = 1; + f32 phi_f0 = this->floorHeights[temp]; + + if (phi_f0 > this->floorHeights[highestFloor]) { + highestFloor = temp; + } else if ((this->floorBgIds[temp] == BGCHECK_SCENE) && ((phi_f0 - this->floorHeights[highestFloor]) > -0.001f)) { + highestFloor = temp; + } + if (this->floorHeights[temp + 1] > this->floorHeights[highestFloor]) { + highestFloor = temp + 1; + } else if ((this->floorBgIds[temp + 1] == BGCHECK_SCENE) && + ((this->floorHeights[temp + 1] - this->floorHeights[highestFloor]) > -0.001f)) { + highestFloor = temp + 1; + } + if (this->floorHeights[temp + 2] > this->floorHeights[highestFloor]) { + highestFloor = temp + 2; + } else if ((this->floorBgIds[temp + 2] == BGCHECK_SCENE) && + ((this->floorHeights[temp + 2] - this->floorHeights[highestFloor]) > -0.001f)) { + highestFloor = temp + 2; + } + if (this->floorHeights[temp + 3] > this->floorHeights[highestFloor]) { + highestFloor = temp + 3; + } else if ((this->floorBgIds[temp + 3] == BGCHECK_SCENE) && + ((this->floorHeights[temp + 3] - this->floorHeights[highestFloor]) > -0.001f)) { + highestFloor = temp + 3; + } + return highestFloor; +} + +void ObjOshihiki_SetGround(ObjOshihiki* this, GlobalContext* globalCtx) { + ObjOshihiki_ResetFloors(this); + ObjOshihiki_SetFloors(this, globalCtx); + this->highestFloor = ObjOshihiki_GetHighestFloor(this); + this->dyna.actor.floorHeight = this->floorHeights[this->highestFloor]; +} + +s32 ObjOshihiki_CheckFloor(ObjOshihiki* this, GlobalContext* globalCtx) { + ObjOshihiki_SetGround(this, globalCtx); + + if ((this->dyna.actor.floorHeight - this->dyna.actor.world.pos.y) >= -0.001f) { + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + return 1; + } + + return 0; +} + +s32 ObjOshihiki_CheckGround(ObjOshihiki* this, GlobalContext* globalCtx) { + if (this->dyna.actor.world.pos.y <= BGCHECK_Y_MIN + 10.0f) { + // "Warning : Push-pull block fell too much" + osSyncPrintf("Warning : 押し引きブロック落ちすぎた(%s %d)(arg_data 0x%04x)\n", "../z_obj_oshihiki.c", 809, + this->dyna.actor.params); + Actor_Kill(&this->dyna.actor); + return 0; + } + if ((this->dyna.actor.floorHeight - this->dyna.actor.world.pos.y) >= -0.001f) { + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + return 1; + } + return 0; +} + +s32 ObjOshihiki_CheckWall(GlobalContext* globalCtx, s16 angle, f32 direction, ObjOshihiki* this) { + f32 maxDist = ((direction >= 0.0f) ? 1.0f : -1.0f) * (300.0f * this->dyna.actor.scale.x + 20.0f - 0.5f); + f32 sn = Math_SinS(angle); + f32 cs = Math_CosS(angle); + s32 i; + + for (i = 0; i < 4; i++) { + Vec3f faceVtx; + Vec3f faceVtxNext; + Vec3f posResult; + Vec3f faceVtxOffset; + s32 bgId; + CollisionPoly* outPoly; + + faceVtxOffset.x = (sFaceVtx[i].x * this->dyna.actor.scale.x * 10.0f) + sFaceDirection[i].x; + faceVtxOffset.y = (sFaceVtx[i].y * this->dyna.actor.scale.y * 10.0f) + sFaceDirection[i].y; + faceVtxOffset.z = 0.0f; + ObjOshihiki_RotateXZ(&faceVtx, &faceVtxOffset, sn, cs); + faceVtx.x += this->dyna.actor.world.pos.x; + faceVtx.y += this->dyna.actor.world.pos.y; + faceVtx.z += this->dyna.actor.world.pos.z; + faceVtxNext.x = faceVtx.x + maxDist * sn; + faceVtxNext.y = faceVtx.y; + faceVtxNext.z = faceVtx.z + maxDist * cs; + if (BgCheck_EntityLineTest3(&globalCtx->colCtx, &faceVtx, &faceVtxNext, &posResult, &outPoly, true, false, + false, true, &bgId, &this->dyna.actor, 0.0f)) { + return true; + } + } + return false; +} + +s32 ObjOshihiki_MoveWithBlockUnder(ObjOshihiki* this, GlobalContext* globalCtx) { + s32 pad; + ObjOshihiki* blockUnder = ObjOshihiki_GetBlockUnder(this, globalCtx); + + if ((blockUnder != NULL) && (blockUnder->stateFlags & PUSHBLOCK_SETUP_PUSH) && + !ObjOshihiki_CheckWall(globalCtx, blockUnder->dyna.unk_158, blockUnder->direction, this)) { + this->blockUnder = blockUnder; + } + + if ((this->stateFlags & PUSHBLOCK_MOVE_UNDER) && (this->blockUnder != NULL)) { + if (this->blockUnder->stateFlags & PUSHBLOCK_PUSH) { + this->underDistX = this->blockUnder->dyna.actor.world.pos.x - this->blockUnder->dyna.actor.prevPos.x; + this->underDistZ = this->blockUnder->dyna.actor.world.pos.z - this->blockUnder->dyna.actor.prevPos.z; + this->dyna.actor.world.pos.x += this->underDistX; + this->dyna.actor.world.pos.z += this->underDistZ; + ObjOshihiki_UpdateInitPos(this); + return true; + } else if (!(this->blockUnder->stateFlags & PUSHBLOCK_SETUP_PUSH)) { + this->blockUnder = NULL; + } + } + return false; +} + +void ObjOshihiki_SetupOnScene(ObjOshihiki* this, GlobalContext* globalCtx) { + this->stateFlags |= PUSHBLOCK_SETUP_ON_SCENE; + this->actionFunc = ObjOshihiki_OnScene; + this->dyna.actor.gravity = 0.0f; + this->dyna.actor.velocity.x = this->dyna.actor.velocity.y = this->dyna.actor.velocity.z = 0.0f; +} + +void ObjOshihiki_OnScene(ObjOshihiki* this, GlobalContext* globalCtx) { + s32 pad; + Player* player = GET_PLAYER(globalCtx); + + this->stateFlags |= PUSHBLOCK_ON_SCENE; + if ((this->timer <= 0) && (fabsf(this->dyna.unk_150) > 0.001f)) { + if (ObjOshihiki_StrongEnough(this) && + !ObjOshihiki_CheckWall(globalCtx, this->dyna.unk_158, this->dyna.unk_150, this)) { + this->direction = this->dyna.unk_150; + ObjOshihiki_SetupPush(this, globalCtx); + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } +} + +void ObjOshihiki_SetupOnActor(ObjOshihiki* this, GlobalContext* globalCtx) { + this->stateFlags |= PUSHBLOCK_SETUP_ON_ACTOR; + this->actionFunc = ObjOshihiki_OnActor; + this->dyna.actor.velocity.x = this->dyna.actor.velocity.y = this->dyna.actor.velocity.z = 0.0f; + this->dyna.actor.gravity = -1.0f; +} + +void ObjOshihiki_OnActor(ObjOshihiki* this, GlobalContext* globalCtx) { + s32 bgId; + Player* player = GET_PLAYER(globalCtx); + DynaPolyActor* dynaPolyActor; + + this->stateFlags |= PUSHBLOCK_ON_ACTOR; + Actor_MoveForward(&this->dyna.actor); + + if (ObjOshihiki_CheckFloor(this, globalCtx)) { + bgId = this->floorBgIds[this->highestFloor]; + if (bgId == BGCHECK_SCENE) { + ObjOshihiki_SetupOnScene(this, globalCtx); + } else { + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, bgId); + if (dynaPolyActor != NULL) { + func_800434A8(dynaPolyActor); + func_80043538(dynaPolyActor); + + if ((this->timer <= 0) && (fabsf(this->dyna.unk_150) > 0.001f)) { + if (ObjOshihiki_StrongEnough(this) && ObjOshihiki_NoSwitchPress(this, dynaPolyActor, globalCtx) && + !ObjOshihiki_CheckWall(globalCtx, this->dyna.unk_158, this->dyna.unk_150, this)) { + + this->direction = this->dyna.unk_150; + ObjOshihiki_SetupPush(this, globalCtx); + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + } else { + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + } + } else { + ObjOshihiki_SetupOnScene(this, globalCtx); + } + } + } else { + bgId = this->floorBgIds[this->highestFloor]; + if (bgId == BGCHECK_SCENE) { + ObjOshihiki_SetupFall(this, globalCtx); + } else { + dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, bgId); + + if ((dynaPolyActor != NULL) && (dynaPolyActor->unk_15C & 1)) { + func_800434A8(dynaPolyActor); + func_80043538(dynaPolyActor); + this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; + } else { + ObjOshihiki_SetupFall(this, globalCtx); + } + } + } +} + +void ObjOshihiki_SetupPush(ObjOshihiki* this, GlobalContext* globalCtx) { + this->stateFlags |= PUSHBLOCK_SETUP_PUSH; + this->actionFunc = ObjOshihiki_Push; + this->dyna.actor.gravity = 0.0f; +} + +void ObjOshihiki_Push(ObjOshihiki* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + Player* player = GET_PLAYER(globalCtx); + f32 pushDistSigned; + s32 stopFlag; + + this->pushSpeed += 0.5f; + this->stateFlags |= PUSHBLOCK_PUSH; + this->pushSpeed = CLAMP_MAX(this->pushSpeed, 2.0f); + stopFlag = Math_StepToF(&this->pushDist, 20.0f, this->pushSpeed); + pushDistSigned = ((this->direction >= 0.0f) ? 1.0f : -1.0f) * this->pushDist; + thisx->world.pos.x = thisx->home.pos.x + (pushDistSigned * this->yawSin); + thisx->world.pos.z = thisx->home.pos.z + (pushDistSigned * this->yawCos); + + if (!ObjOshihiki_CheckFloor(this, globalCtx)) { + thisx->home.pos.x = thisx->world.pos.x; + thisx->home.pos.z = thisx->world.pos.z; + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + this->pushDist = 0.0f; + this->pushSpeed = 0.0f; + ObjOshihiki_SetupFall(this, globalCtx); + } else if (stopFlag) { + player = GET_PLAYER(globalCtx); + if (ObjOshihiki_CheckWall(globalCtx, this->dyna.unk_158, this->dyna.unk_150, this)) { + Audio_PlayActorSound2(thisx, NA_SE_EV_BLOCK_BOUND); + } + + thisx->home.pos.x = thisx->world.pos.x; + thisx->home.pos.z = thisx->world.pos.z; + player->stateFlags2 &= ~0x10; + this->dyna.unk_150 = 0.0f; + this->pushDist = 0.0f; + this->pushSpeed = 0.0f; + this->timer = 10; + if (this->floorBgIds[this->highestFloor] == BGCHECK_SCENE) { + ObjOshihiki_SetupOnScene(this, globalCtx); + } else { + ObjOshihiki_SetupOnActor(this, globalCtx); + } + } + Audio_PlayActorSound2(thisx, NA_SE_EV_ROCK_SLIDE - SFX_FLAG); +} + +void ObjOshihiki_SetupFall(ObjOshihiki* this, GlobalContext* globalCtx) { + this->stateFlags |= PUSHBLOCK_SETUP_FALL; + this->dyna.actor.velocity.x = this->dyna.actor.velocity.y = this->dyna.actor.velocity.z = 0.0f; + this->dyna.actor.gravity = -1.0f; + ObjOshihiki_SetGround(this, globalCtx); + this->actionFunc = ObjOshihiki_Fall; +} + +void ObjOshihiki_Fall(ObjOshihiki* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + this->stateFlags |= PUSHBLOCK_FALL; + if (fabsf(this->dyna.unk_150) > 0.001f) { + this->dyna.unk_150 = 0.0f; + player->stateFlags2 &= ~0x10; + } + Actor_MoveForward(&this->dyna.actor); + if (ObjOshihiki_CheckGround(this, globalCtx)) { + if (this->floorBgIds[this->highestFloor] == BGCHECK_SCENE) { + ObjOshihiki_SetupOnScene(this, globalCtx); + } else { + ObjOshihiki_SetupOnActor(this, globalCtx); + } + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_BLOCK_BOUND); + Audio_PlayActorSound2(&this->dyna.actor, + SurfaceType_GetSfx(&globalCtx->colCtx, this->floorPolys[this->highestFloor], + this->floorBgIds[this->highestFloor]) + + SFX_FLAG); + } +} + +void ObjOshihiki_Update(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjOshihiki* this = (ObjOshihiki*)thisx; + + this->stateFlags &= + ~(PUSHBLOCK_SETUP_FALL | PUSHBLOCK_FALL | PUSHBLOCK_SETUP_PUSH | PUSHBLOCK_PUSH | PUSHBLOCK_SETUP_ON_ACTOR | + PUSHBLOCK_ON_ACTOR | PUSHBLOCK_SETUP_ON_SCENE | PUSHBLOCK_ON_SCENE); + this->stateFlags |= PUSHBLOCK_MOVE_UNDER; + + if (this->timer > 0) { + this->timer--; + } + + this->dyna.actor.world.rot.y = this->dyna.unk_158; + + this->yawSin = Math_SinS(this->dyna.actor.world.rot.y); + this->yawCos = Math_CosS(this->dyna.actor.world.rot.y); + + if (this->actionFunc != NULL) { + this->actionFunc(this, globalCtx); + } +} + +void ObjOshihiki_Draw(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjOshihiki* this = (ObjOshihiki*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_oshihiki.c", 1289); + if (ObjOshihiki_MoveWithBlockUnder(this, globalCtx)) { + Matrix_Translate(this->underDistX * 10.0f, 0.0f, this->underDistZ * 10.0f, MTXMODE_APPLY); + } + this->stateFlags &= ~PUSHBLOCK_MOVE_UNDER; + func_80093D18(globalCtx->state.gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(this->texture)); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_oshihiki.c", 1308), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_MEN: + gDPSetEnvColor(POLY_OPA_DISP++, this->color.r, this->color.g, this->color.b, 255); + break; + default: + gDPSetEnvColor(POLY_OPA_DISP++, mREG(13), mREG(14), mREG(15), 255); + break; + } + + gSPDisplayList(POLY_OPA_DISP++, gPushBlockDL); + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_oshihiki.c", 1334); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h b/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h new file mode 100644 index 000000000..33098f64b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Oshihiki/z_obj_oshihiki.h @@ -0,0 +1,54 @@ +#ifndef Z_OBJ_OSHIHIKI_H +#define Z_OBJ_OSHIHIKI_H + +#include "ultra64.h" +#include "global.h" + +struct ObjOshihiki; + +typedef enum { + /* 0 */ PUSHBLOCK_SMALL_START_ON, + /* 1 */ PUSHBLOCK_MEDIUM_START_ON, + /* 2 */ PUSHBLOCK_LARGE_START_ON, + /* 3 */ PUSHBLOCK_HUGE_START_ON, + /* 4 */ PUSHBLOCK_SMALL_START_OFF, + /* 5 */ PUSHBLOCK_MEDIUM_START_OFF, + /* 6 */ PUSHBLOCK_LARGE_START_OFF, + /* 7 */ PUSHBLOCK_HUGE_START_OFF +} PushBlockType; + +#define PUSHBLOCK_ON_SCENE (1 << 0) +#define PUSHBLOCK_SETUP_ON_SCENE (1 << 1) +#define PUSHBLOCK_ON_ACTOR (1 << 2) +#define PUSHBLOCK_SETUP_ON_ACTOR (1 << 3) +#define PUSHBLOCK_PUSH (1 << 4) +#define PUSHBLOCK_SETUP_PUSH (1 << 5) +#define PUSHBLOCK_FALL (1 << 6) +#define PUSHBLOCK_SETUP_FALL (1 << 7) +#define PUSHBLOCK_MOVE_UNDER (1 << 8) + +typedef void (*ObjOshihikiActionFunc)(struct ObjOshihiki*, GlobalContext*); + +typedef struct ObjOshihiki { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjOshihikiActionFunc actionFunc; + /* 0x0168 */ u16 stateFlags; + /* 0x016A */ s16 timer; + /* 0x016C */ f32 yawSin; + /* 0x0170 */ f32 yawCos; + /* 0x0174 */ f32 pushSpeed; + /* 0x0178 */ f32 pushDist; + /* 0x017C */ f32 direction; + /* 0x0180 */ s32 floorBgIds[5]; + /* 0x0194 */ CollisionPoly* floorPolys[5]; + /* 0x01A8 */ f32 floorHeights[5]; + /* 0x01BC */ s16 highestFloor; + /* 0x01BE */ u8 cantMove; + /* 0x01C0 */ struct ObjOshihiki* blockUnder; + /* 0x01C4 */ f32 underDistX; + /* 0x01C8 */ f32 underDistZ; + /* 0x01CC */ void* texture; + /* 0x01D0 */ Color_RGB8 color; +} ObjOshihiki; // size = 0x01D4 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c b/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c new file mode 100644 index 000000000..decb553b7 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.c @@ -0,0 +1,89 @@ +/* + * File: z_obj_roomtimer.c + * Overlay: ovl_Obj_Roomtimer + * Description: Starts Timer 1 with a value specified in params + */ + +#include "z_obj_roomtimer.h" + +#define FLAGS ACTOR_FLAG_4 + +void ObjRoomtimer_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjRoomtimer_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjRoomtimer_Update(Actor* thisx, GlobalContext* globalCtx); + +void func_80B9D054(ObjRoomtimer* this, GlobalContext* globalCtx); +void func_80B9D0B0(ObjRoomtimer* this, GlobalContext* globalCtx); + +const ActorInit Obj_Roomtimer_InitVars = { + ACTOR_OBJ_ROOMTIMER, + ACTORCAT_ENEMY, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjRoomtimer), + (ActorFunc)ObjRoomtimer_Init, + (ActorFunc)ObjRoomtimer_Destroy, + (ActorFunc)ObjRoomtimer_Update, + (ActorFunc)NULL, + NULL, +}; + +void ObjRoomtimer_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjRoomtimer* this = (ObjRoomtimer*)thisx; + s16 params = this->actor.params; + + this->switchFlag = (params >> 10) & 0x3F; + this->actor.params = params & 0x3FF; + params = this->actor.params; + + if (params != 0x3FF) { + if (params > 600) { + this->actor.params = 600; + } else { + this->actor.params = params; + } + } + + this->actionFunc = func_80B9D054; +} + +void ObjRoomtimer_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjRoomtimer* this = (ObjRoomtimer*)thisx; + + if ((this->actor.params != 0x3FF) && (gSaveContext.timer1Value > 0)) { + gSaveContext.timer1State = 10; + } +} + +void func_80B9D054(ObjRoomtimer* this, GlobalContext* globalCtx) { + if (this->actor.params != 0x3FF) { + func_80088B34(this->actor.params); + } + + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_PROP); + this->actionFunc = func_80B9D0B0; +} + +void func_80B9D0B0(ObjRoomtimer* this, GlobalContext* globalCtx) { + if (Flags_GetTempClear(globalCtx, this->actor.room)) { + if (this->actor.params != 0x3FF) { + gSaveContext.timer1State = 10; + } + Flags_SetClear(globalCtx, this->actor.room); + Flags_SetSwitch(globalCtx, this->switchFlag); + func_80078884(NA_SE_SY_CORRECT_CHIME); + Actor_Kill(&this->actor); + } else { + if ((this->actor.params != 0x3FF) && (gSaveContext.timer1Value == 0)) { + Audio_PlaySoundGeneral(NA_SE_OC_ABYSS, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + Gameplay_TriggerVoidOut(globalCtx); + Actor_Kill(&this->actor); + } + } +} + +void ObjRoomtimer_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjRoomtimer* this = (ObjRoomtimer*)thisx; + + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.h b/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.h new file mode 100644 index 000000000..6c31a11b5 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Roomtimer/z_obj_roomtimer.h @@ -0,0 +1,18 @@ +#ifndef Z_OBJ_ROOMTIMER_H +#define Z_OBJ_ROOMTIMER_H + +#include "ultra64.h" +#include "global.h" +#include "z64.h" + +struct ObjRoomtimer; + +typedef void (*ObjRoomtimerActionFunc)(struct ObjRoomtimer*, GlobalContext*); + +typedef struct ObjRoomtimer { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjRoomtimerActionFunc actionFunc; + /* 0x0150 */ u32 switchFlag; +} ObjRoomtimer; // size = 0x0154 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Switch/z_obj_switch.c b/soh/src/overlays/actors/ovl_Obj_Switch/z_obj_switch.c new file mode 100644 index 000000000..1adc45d72 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Switch/z_obj_switch.c @@ -0,0 +1,795 @@ +/* + * File: z_obj_switch.c + * Overlay: ovl_Obj_Switch + * Description: Switches + */ + +#include "z_obj_switch.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "vt.h" + +#define FLAGS ACTOR_FLAG_4 + +// type: (this->dyna.actor.params & 7) +// subtype: (this->dyna.actor.params >> 4 & 7) +// switch flag: (this->dyna.actor.params >> 8 & 0x3F) +// frozen: this->dyna.actor.params >> 7 & 1 + +void ObjSwitch_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjSwitch_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjSwitch_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjSwitch_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjSwitch_FloorUpInit(ObjSwitch* this); +void ObjSwitch_FloorUp(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_FloorPressInit(ObjSwitch* this); +void ObjSwitch_FloorPress(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_FloorDownInit(ObjSwitch* this); +void ObjSwitch_FloorDown(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_FloorReleaseInit(ObjSwitch* this); +void ObjSwitch_FloorRelease(ObjSwitch* this, GlobalContext* globalCtx); + +void ObjSwitch_EyeFrozenInit(ObjSwitch* this); +void ObjSwitch_EyeInit(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_EyeOpenInit(ObjSwitch* this); +void ObjSwitch_EyeOpen(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_EyeClosingInit(ObjSwitch* this); +void ObjSwitch_EyeClosing(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_EyeClosedInit(ObjSwitch* this); +void ObjSwitch_EyeClosed(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_EyeOpeningInit(ObjSwitch* this); +void ObjSwitch_EyeOpening(ObjSwitch* this, GlobalContext* globalCtx); + +void ObjSwitch_CrystalOffInit(ObjSwitch* this); +void ObjSwitch_CrystalOff(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_CrystalTurnOnInit(ObjSwitch* this); +void ObjSwitch_CrystalTurnOn(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_CrystalOnInit(ObjSwitch* this); +void ObjSwitch_CrystalOn(ObjSwitch* this, GlobalContext* globalCtx); +void ObjSwitch_CrystalTurnOffInit(ObjSwitch* this); +void ObjSwitch_CrystalTurnOff(ObjSwitch* this, GlobalContext* globalCtx); + +const ActorInit Obj_Switch_InitVars = { + ACTOR_OBJ_SWITCH, + ACTORCAT_SWITCH, + FLAGS, + OBJECT_GAMEPLAY_DANGEON_KEEP, + sizeof(ObjSwitch), + (ActorFunc)ObjSwitch_Init, + (ActorFunc)ObjSwitch_Destroy, + (ActorFunc)ObjSwitch_Update, + (ActorFunc)ObjSwitch_Draw, + NULL, +}; + +static f32 sHeights[] = { 10, 10, 0, 30, 30 }; + +static ColliderTrisElementInit D_80B9EC34[2] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { -20.0f, 19.0f, -20.0f }, { -20.0f, 19.0f, 20.0f }, { 20.0f, 19.0f, 20.0f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x40000040, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 20.0f, 19.0f, 20.0f }, { 20.0f, 19.0f, -20.0f }, { -20.0f, 19.0f, -20.0f } } }, + }, +}; + +static ColliderTrisInit sRustyFloorTrisInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + D_80B9EC34, +}; + +static ColliderTrisElementInit D_80B9ECBC[2] = { + { + { + ELEMTYPE_UNK4, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 23.0f, 8.5f }, { -23.0f, 0.0f, 8.5f }, { 0.0f, -23.0f, 8.5f } } }, + }, + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x0001F824, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 23.0f, 8.5f }, { 0.0f, -23.0f, 8.5f }, { 23.0f, 0.0f, 8.5f } } }, + }, +}; + +static ColliderTrisInit trisColliderEye = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_TRIS, + }, + 2, + D_80B9ECBC, +}; + +static ColliderJntSphElementInit D_80B9ED44[1] = { + { + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0xEFC1FFFE, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 0, { { 0, 300, 0 }, 20 }, 100 }, + }, +}; + +static ColliderJntSphInit sCyrstalJntSphereInit = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_JNTSPH, + }, + 1, + D_80B9ED44, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 100, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 2000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 400, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 2000, ICHAIN_STOP), +}; + +void ObjSwitch_RotateY(Vec3f* dest, Vec3f* src, s16 angle) { + f32 s = Math_SinS(angle); + f32 c = Math_CosS(angle); + + dest->x = src->z * s + src->x * c; + dest->y = src->y; + dest->z = src->z * c - src->x * s; +} + +void ObjSwitch_InitDynapoly(ObjSwitch* this, GlobalContext* globalCtx, CollisionHeader* collision, s32 moveFlag) { + s32 pad; + CollisionHeader* colHeader = NULL; + s32 pad2; + + DynaPolyActor_Init(&this->dyna, moveFlag); + CollisionHeader_GetVirtual(collision, &colHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + if (this->dyna.bgId == BG_ACTOR_MAX) { + // "Warning : move BG registration failure" + osSyncPrintf("Warning : move BG 登録失敗(%s %d)(name %d)(arg_data 0x%04x)\n", "../z_obj_switch.c", 531, + this->dyna.actor.id, this->dyna.actor.params); + } +} + +void ObjSwitch_InitJntSphCollider(ObjSwitch* this, GlobalContext* globalCtx, ColliderJntSphInit* colliderJntSphInit) { + ColliderJntSph* colliderJntSph = &this->jntSph.col; + + Collider_InitJntSph(globalCtx, colliderJntSph); + Collider_SetJntSph(globalCtx, colliderJntSph, &this->dyna.actor, colliderJntSphInit, this->jntSph.items); + Matrix_SetTranslateRotateYXZ(this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y + + this->dyna.actor.shape.yOffset * this->dyna.actor.scale.y, + this->dyna.actor.world.pos.z, &this->dyna.actor.shape.rot); + Matrix_Scale(this->dyna.actor.scale.x, this->dyna.actor.scale.y, this->dyna.actor.scale.z, MTXMODE_APPLY); + Collider_UpdateSpheres(0, colliderJntSph); +} + +void ObjSwitch_InitTrisCollider(ObjSwitch* this, GlobalContext* globalCtx, ColliderTrisInit* colliderTrisInit) { + ColliderTris* colliderTris = &this->tris.col; + s32 i; + s32 j; + Vec3f pos[3]; + + Collider_InitTris(globalCtx, colliderTris); + Collider_SetTris(globalCtx, colliderTris, &this->dyna.actor, colliderTrisInit, this->tris.items); + + for (i = 0; i < 2; i++) { + for (j = 0; j < 3; j++) { + ObjSwitch_RotateY(&pos[j], &colliderTrisInit->elements[i].dim.vtx[j], this->dyna.actor.home.rot.y); + Math_Vec3f_Sum(&pos[j], &this->dyna.actor.world.pos, &pos[j]); + } + + Collider_SetTrisVertices(colliderTris, i, &pos[0], &pos[1], &pos[2]); + } +} + +Actor* ObjSwitch_SpawnIce(ObjSwitch* this, GlobalContext* globalCtx) { + Actor* thisx = &this->dyna.actor; + + return Actor_SpawnAsChild(&globalCtx->actorCtx, thisx, globalCtx, ACTOR_OBJ_ICE_POLY, thisx->world.pos.x, + thisx->world.pos.y, thisx->world.pos.z, thisx->world.rot.x, thisx->world.rot.y, + thisx->world.rot.z, (this->dyna.actor.params >> 8 & 0x3F) << 8); +} + +void ObjSwitch_SetOn(ObjSwitch* this, GlobalContext* globalCtx) { + s32 pad; + s32 subType; + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + this->cooldownOn = false; + } else { + subType = (this->dyna.actor.params >> 4 & 7); + Flags_SetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F)); + + if (subType == 0 || subType == 4) { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->dyna.actor, NA_SE_SY_CORRECT_CHIME); + } else { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); + } + + this->cooldownOn = true; + } +} + +void ObjSwitch_SetOff(ObjSwitch* this, GlobalContext* globalCtx) { + this->cooldownOn = false; + + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + Flags_UnsetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F)); + + if ((this->dyna.actor.params >> 4 & 7) == 1) { + OnePointCutscene_AttentionSetSfx(globalCtx, &this->dyna.actor, NA_SE_SY_TRE_BOX_APPEAR); + this->cooldownOn = true; + } + } +} + +void ObjSwitch_UpdateTwoTexScrollXY(ObjSwitch* this) { + this->x1TexScroll = (this->x1TexScroll - 1) & 0x7F; + this->y1TexScroll = (this->y1TexScroll + 1) & 0x7F; + this->x2TexScroll = (this->x2TexScroll + 1) & 0x7F; + this->y2TexScroll = (this->y2TexScroll - 1) & 0x7F; +} + +void ObjSwitch_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjSwitch* this = (ObjSwitch*)thisx; + s32 switchFlagSet; + s32 type; + + switchFlagSet = Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F)); + type = (this->dyna.actor.params & 7); + + if (type == OBJSWITCH_TYPE_FLOOR || type == OBJSWITCH_TYPE_FLOOR_RUSTY) { + ObjSwitch_InitDynapoly(this, globalCtx, &gFloorSwitchCol, DPM_PLAYER); + } + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + if (type == OBJSWITCH_TYPE_FLOOR || type == OBJSWITCH_TYPE_FLOOR_RUSTY) { + this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + 1.0f; + } + + Actor_SetFocus(&this->dyna.actor, sHeights[type]); + + if (type == OBJSWITCH_TYPE_FLOOR_RUSTY) { + ObjSwitch_InitTrisCollider(this, globalCtx, &sRustyFloorTrisInit); + } else if (type == OBJSWITCH_TYPE_EYE) { + ObjSwitch_InitTrisCollider(this, globalCtx, &trisColliderEye); + } else if (type == OBJSWITCH_TYPE_CRYSTAL || type == OBJSWITCH_TYPE_CRYSTAL_TARGETABLE) { + ObjSwitch_InitJntSphCollider(this, globalCtx, &sCyrstalJntSphereInit); + } + + if (type == OBJSWITCH_TYPE_CRYSTAL_TARGETABLE) { + this->dyna.actor.flags |= ACTOR_FLAG_0; + this->dyna.actor.targetMode = 4; + } + + this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; + + if ((this->dyna.actor.params >> 7 & 1) && (ObjSwitch_SpawnIce(this, globalCtx) == NULL)) { + osSyncPrintf(VT_FGCOL(RED)); + osSyncPrintf("Error : 氷発生失敗 (%s %d)\n", "../z_obj_switch.c", 732); + osSyncPrintf(VT_RST); + this->dyna.actor.params &= ~0x80; + } + + if (this->dyna.actor.params >> 7 & 1) { + ObjSwitch_EyeFrozenInit(this); + } else if (type == OBJSWITCH_TYPE_FLOOR || type == OBJSWITCH_TYPE_FLOOR_RUSTY) { + if (switchFlagSet) { + ObjSwitch_FloorDownInit(this); + } else { + ObjSwitch_FloorUpInit(this); + } + } else if (type == OBJSWITCH_TYPE_EYE) { + if (switchFlagSet) { + ObjSwitch_EyeClosedInit(this); + } else { + ObjSwitch_EyeOpenInit(this); + } + } else if (type == OBJSWITCH_TYPE_CRYSTAL || type == OBJSWITCH_TYPE_CRYSTAL_TARGETABLE) { + if (switchFlagSet) { + ObjSwitch_CrystalOnInit(this); + } else { + ObjSwitch_CrystalOffInit(this); + } + } + + osSyncPrintf("(Dungeon switch)(arg_data 0x%04x)\n", this->dyna.actor.params); +} + +void ObjSwitch_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjSwitch* this = (ObjSwitch*)thisx; + + switch ((this->dyna.actor.params & 7)) { + case OBJSWITCH_TYPE_FLOOR: + case OBJSWITCH_TYPE_FLOOR_RUSTY: + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + break; + } + + switch ((this->dyna.actor.params & 7)) { + case OBJSWITCH_TYPE_FLOOR_RUSTY: + case OBJSWITCH_TYPE_EYE: + Collider_DestroyTris(globalCtx, &this->tris.col); + break; + case OBJSWITCH_TYPE_CRYSTAL: + case OBJSWITCH_TYPE_CRYSTAL_TARGETABLE: + Collider_DestroyJntSph(globalCtx, &this->jntSph.col); + break; + } +} + +void ObjSwitch_FloorUpInit(ObjSwitch* this) { + this->dyna.actor.scale.y = 33.0f / 200.0f; + this->actionFunc = ObjSwitch_FloorUp; +} + +void ObjSwitch_FloorUp(ObjSwitch* this, GlobalContext* globalCtx) { + if ((this->dyna.actor.params & 7) == OBJSWITCH_TYPE_FLOOR_RUSTY) { + if (this->tris.col.base.acFlags & AC_HIT) { + ObjSwitch_FloorPressInit(this); + ObjSwitch_SetOn(this, globalCtx); + this->tris.col.base.acFlags &= ~AC_HIT; + } else { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->tris.col.base); + } + } else { + switch ((this->dyna.actor.params >> 4 & 7)) { + case OBJSWITCH_SUBTYPE_FLOOR_0: + if (func_8004356C(&this->dyna)) { + ObjSwitch_FloorPressInit(this); + ObjSwitch_SetOn(this, globalCtx); + } + break; + case OBJSWITCH_SUBTYPE_FLOOR_1: + if ((this->dyna.unk_160 & 2) && !(this->unk_17F & 2)) { + ObjSwitch_FloorPressInit(this); + ObjSwitch_SetOn(this, globalCtx); + } + break; + case OBJSWITCH_SUBTYPE_FLOOR_2: + if (func_800435B4(&this->dyna)) { + ObjSwitch_FloorPressInit(this); + ObjSwitch_SetOn(this, globalCtx); + } + break; + case OBJSWITCH_SUBTYPE_FLOOR_3: + if (func_800435B4(&this->dyna)) { + ObjSwitch_FloorPressInit(this); + ObjSwitch_SetOff(this, globalCtx); + } + break; + } + } +} + +void ObjSwitch_FloorPressInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_FloorPress; + this->cooldownTimer = 100; +} + +void ObjSwitch_FloorPress(ObjSwitch* this, GlobalContext* globalCtx) { + if ((this->dyna.actor.params >> 4 & 7) == OBJSWITCH_SUBTYPE_FLOOR_3 || !this->cooldownOn || + func_8005B198() == this->dyna.actor.category || this->cooldownTimer <= 0) { + this->dyna.actor.scale.y -= 99.0f / 2000.0f; + if (this->dyna.actor.scale.y <= 33.0f / 2000.0f) { + ObjSwitch_FloorDownInit(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 120, 20, 10); + } + } +} + +void ObjSwitch_FloorDownInit(ObjSwitch* this) { + this->dyna.actor.scale.y = 33.0f / 2000.0f; + this->releaseTimer = 6; + this->actionFunc = ObjSwitch_FloorDown; +} + +void ObjSwitch_FloorDown(ObjSwitch* this, GlobalContext* globalCtx) { + switch ((this->dyna.actor.params >> 4 & 7)) { + case OBJSWITCH_SUBTYPE_FLOOR_0: + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + ObjSwitch_FloorReleaseInit(this); + } + break; + case OBJSWITCH_SUBTYPE_FLOOR_1: + if ((this->dyna.unk_160 & 2) && !(this->unk_17F & 2)) { + ObjSwitch_FloorReleaseInit(this); + ObjSwitch_SetOff(this, globalCtx); + } + break; + case OBJSWITCH_SUBTYPE_FLOOR_2: + case OBJSWITCH_SUBTYPE_FLOOR_3: + if (!func_800435B4(&this->dyna) && !Player_InCsMode(globalCtx)) { + if (this->releaseTimer <= 0) { + ObjSwitch_FloorReleaseInit(this); + if ((this->dyna.actor.params >> 4 & 7) == OBJSWITCH_SUBTYPE_FLOOR_2) { + ObjSwitch_SetOff(this, globalCtx); + } else { + ObjSwitch_SetOn(this, globalCtx); + } + } + } else { + this->releaseTimer = 6; + } + break; + } +} + +void ObjSwitch_FloorReleaseInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_FloorRelease; + this->cooldownTimer = 100; +} + +void ObjSwitch_FloorRelease(ObjSwitch* this, GlobalContext* globalCtx) { + s16 subType = (this->dyna.actor.params >> 4 & 7); + + if (((subType != OBJSWITCH_SUBTYPE_FLOOR_1) && (subType != OBJSWITCH_SUBTYPE_FLOOR_3)) || !this->cooldownOn || + func_8005B198() == this->dyna.actor.category || this->cooldownTimer <= 0) { + this->dyna.actor.scale.y += 99.0f / 2000.0f; + if (this->dyna.actor.scale.y >= 33.0f / 200.0f) { + ObjSwitch_FloorUpInit(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + if (subType == OBJSWITCH_SUBTYPE_FLOOR_1) { + func_800AA000(this->dyna.actor.xyzDistToPlayerSq, 120, 20, 10); + } + } + } +} + +s32 ObjSwitch_EyeIsHit(ObjSwitch* this) { + Actor* collidingActor; + s16 yawDiff; + + if ((this->tris.col.base.acFlags & AC_HIT) && !(this->unk_17F & 2)) { + collidingActor = this->tris.col.base.ac; + if (collidingActor != NULL) { + yawDiff = collidingActor->world.rot.y - this->dyna.actor.shape.rot.y; + if (ABS(yawDiff) > 0x5000) { + return 1; + } + } + } + return 0; +} + +void ObjSwitch_EyeFrozenInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_EyeInit; +} + +void ObjSwitch_EyeInit(ObjSwitch* this, GlobalContext* globalCtx) { + if (Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + ObjSwitch_EyeClosedInit(this); + } else { + ObjSwitch_EyeOpenInit(this); + } +} + +void ObjSwitch_EyeOpenInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_EyeOpen; + this->eyeTexIndex = 0; +} + +void ObjSwitch_EyeOpen(ObjSwitch* this, GlobalContext* globalCtx) { + if (ObjSwitch_EyeIsHit(this) || (this->dyna.actor.params >> 7 & 1)) { + ObjSwitch_EyeClosingInit(this); + ObjSwitch_SetOn(this, globalCtx); + this->dyna.actor.params &= ~0x80; + } +} + +void ObjSwitch_EyeClosingInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_EyeClosing; + this->cooldownTimer = 100; +} + +void ObjSwitch_EyeClosing(ObjSwitch* this, GlobalContext* globalCtx) { + if (!this->cooldownOn || func_8005B198() == this->dyna.actor.category || this->cooldownTimer <= 0) { + this->eyeTexIndex++; + if (this->eyeTexIndex >= 3) { + ObjSwitch_EyeClosedInit(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + } + } +} + +void ObjSwitch_EyeClosedInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_EyeClosed; + this->eyeTexIndex = 3; +} + +void ObjSwitch_EyeClosed(ObjSwitch* this, GlobalContext* globalCtx) { + switch ((this->dyna.actor.params >> 4 & 7)) { + case OBJSWITCH_SUBTYPE_EYE_0: + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + ObjSwitch_EyeOpeningInit(this); + this->dyna.actor.params &= ~0x80; + } + break; + case OBJSWITCH_SUBTYPE_EYE_1: + if (ObjSwitch_EyeIsHit(this) || (this->dyna.actor.params >> 7 & 1)) { + ObjSwitch_EyeOpeningInit(this); + ObjSwitch_SetOff(this, globalCtx); + this->dyna.actor.params &= ~0x80; + } + break; + } +} + +void ObjSwitch_EyeOpeningInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_EyeOpening; + this->cooldownTimer = 100; +} + +void ObjSwitch_EyeOpening(ObjSwitch* this, GlobalContext* globalCtx) { + if ((this->dyna.actor.params >> 4 & 7) != OBJSWITCH_SUBTYPE_EYE_1 || !this->cooldownOn || + func_8005B198() == this->dyna.actor.category || this->cooldownTimer <= 0) { + this->eyeTexIndex--; + if (this->eyeTexIndex <= 0) { + ObjSwitch_EyeOpenInit(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_FOOT_SWITCH); + } + } +} + +void ObjSwitch_CrystalOffInit(ObjSwitch* this) { + this->crystalColor.r = 0; + this->crystalColor.g = 0; + this->crystalColor.b = 0; + this->crystalSubtype1texture = gCrstalSwitchRedTex; + this->actionFunc = ObjSwitch_CrystalOff; +} + +void ObjSwitch_CrystalOff(ObjSwitch* this, GlobalContext* globalCtx) { + switch ((this->dyna.actor.params >> 4 & 7)) { + case OBJSWITCH_SUBTYPE_CRYSTAL_0: + if ((this->jntSph.col.base.acFlags & AC_HIT) && this->disableAcTimer <= 0) { + this->disableAcTimer = 10; + ObjSwitch_SetOn(this, globalCtx); + ObjSwitch_CrystalTurnOnInit(this); + } + break; + case OBJSWITCH_SUBTYPE_CRYSTAL_4: + if (((this->jntSph.col.base.acFlags & AC_HIT) && this->disableAcTimer <= 0) || + Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + this->disableAcTimer = 10; + ObjSwitch_SetOn(this, globalCtx); + ObjSwitch_CrystalTurnOnInit(this); + } + break; + case OBJSWITCH_SUBTYPE_CRYSTAL_1: + if ((this->jntSph.col.base.acFlags & AC_HIT) && !(this->unk_17F & 2) && this->disableAcTimer <= 0) { + this->disableAcTimer = 10; + ObjSwitch_SetOn(this, globalCtx); + ObjSwitch_CrystalTurnOnInit(this); + } + ObjSwitch_UpdateTwoTexScrollXY(this); + break; + } +} + +void ObjSwitch_CrystalTurnOnInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_CrystalTurnOn; + this->cooldownTimer = 100; +} + +void ObjSwitch_CrystalTurnOn(ObjSwitch* this, GlobalContext* globalCtx) { + if (!this->cooldownOn || func_8005B198() == this->dyna.actor.category || this->cooldownTimer <= 0) { + ObjSwitch_CrystalOnInit(this); + if ((this->dyna.actor.params >> 4 & 7) == OBJSWITCH_SUBTYPE_CRYSTAL_1) { + ObjSwitch_UpdateTwoTexScrollXY(this); + } + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_DIAMOND_SWITCH); + } +} + +void ObjSwitch_CrystalOnInit(ObjSwitch* this) { + this->crystalColor.r = 255; + this->crystalColor.g = 255; + this->crystalColor.b = 255; + this->crystalSubtype1texture = gCrstalSwitchBlueTex; + this->actionFunc = ObjSwitch_CrystalOn; +} + +void ObjSwitch_CrystalOn(ObjSwitch* this, GlobalContext* globalCtx) { + switch ((this->dyna.actor.params >> 4 & 7)) { + case OBJSWITCH_SUBTYPE_CRYSTAL_0: + case OBJSWITCH_SUBTYPE_CRYSTAL_4: + if (!Flags_GetSwitch(globalCtx, (this->dyna.actor.params >> 8 & 0x3F))) { + ObjSwitch_CrystalTurnOffInit(this); + } + break; + case OBJSWITCH_SUBTYPE_CRYSTAL_1: + if ((this->jntSph.col.base.acFlags & AC_HIT) && !(this->unk_17F & 2) && this->disableAcTimer <= 0) { + this->disableAcTimer = 10; + globalCtx = globalCtx; + ObjSwitch_CrystalTurnOffInit(this); + ObjSwitch_SetOff(this, globalCtx); + } + break; + } + ObjSwitch_UpdateTwoTexScrollXY(this); +} + +void ObjSwitch_CrystalTurnOffInit(ObjSwitch* this) { + this->actionFunc = ObjSwitch_CrystalTurnOff; + this->cooldownTimer = 100; +} + +void ObjSwitch_CrystalTurnOff(ObjSwitch* this, GlobalContext* globalCtx) { + if ((this->dyna.actor.params >> 4 & 7) != OBJSWITCH_SUBTYPE_CRYSTAL_1 || !this->cooldownOn || + func_8005B198() == this->dyna.actor.category || this->cooldownTimer <= 0) { + ObjSwitch_CrystalOffInit(this); + ObjSwitch_UpdateTwoTexScrollXY(this); + Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_DIAMOND_SWITCH); + } +} + +void ObjSwitch_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjSwitch* this = (ObjSwitch*)thisx; + + if (this->releaseTimer > 0) { + this->releaseTimer--; + } + if (this->cooldownTimer > 0) { + this->cooldownTimer--; + } + + this->actionFunc(this, globalCtx); + + switch ((this->dyna.actor.params & 7)) { + case OBJSWITCH_TYPE_FLOOR: + case OBJSWITCH_TYPE_FLOOR_RUSTY: + this->unk_17F = this->dyna.unk_160; + break; + case OBJSWITCH_TYPE_EYE: + this->unk_17F = this->tris.col.base.acFlags; + this->tris.col.base.acFlags &= ~AC_HIT; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->tris.col.base); + break; + case OBJSWITCH_TYPE_CRYSTAL: + case OBJSWITCH_TYPE_CRYSTAL_TARGETABLE: + if (!Player_InCsMode(globalCtx) && this->disableAcTimer > 0) { + this->disableAcTimer--; + } + this->unk_17F = this->jntSph.col.base.acFlags; + this->jntSph.col.base.acFlags &= ~AC_HIT; + if (this->disableAcTimer <= 0) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->jntSph.col.base); + } + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->jntSph.col.base); + break; + } +} + +void ObjSwitch_DrawFloor(ObjSwitch* this, GlobalContext* globalCtx) { + static Gfx* floorSwitchDLists[] = { gFloorSwitch1DL, gFloorSwitch3DL, gFloorSwitch2DL, gFloorSwitch2DL }; + + Gfx_DrawDListOpa(globalCtx, floorSwitchDLists[(this->dyna.actor.params >> 4 & 7)]); +} + +void ObjSwitch_DrawFloorRusty(ObjSwitch* this, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, gRustyFloorSwitchDL); +} + +void ObjSwitch_DrawEye(ObjSwitch* this, GlobalContext* globalCtx) { + static void* eyeTextures[][4] = { + { gEyeSwitchGoldOpenTex, gEyeSwitchGoldOpeningTex, gEyeSwitchGoldClosingTex, gEyeSwitchGoldClosedTex }, + { gEyeSwitchSilverOpenTex, gEyeSwitchSilverHalfTex, gEyeSwitchSilverClosedTex, gEyeSwitchSilverClosedTex }, + }; + static Gfx* eyeDlists[] = { gEyeSwitch1DL, gEyeSwitch2DL }; + s32 pad; + s32 subType = (this->dyna.actor.params >> 4 & 7); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1459); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1462), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(eyeTextures[subType][this->eyeTexIndex])); + gSPDisplayList(POLY_OPA_DISP++, eyeDlists[subType]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1471); +} + +void ObjSwitch_DrawCrystal(ObjSwitch* this, GlobalContext* globalCtx) { + static Gfx* xluDLists[] = { gCrystalSwitchCoreXluDL, gCrystalSwitchDiamondXluDL, NULL, NULL, + gCrystalSwitchCoreXluDL }; + static Gfx* opaDLists[] = { gCrystalSwitchCoreOpaDL, gCrystalSwitchDiamondOpaDL, NULL, NULL, + gCrystalSwitchCoreOpaDL }; + s32 pad1; + s32 pad2; + s32 subType; + + subType = (this->dyna.actor.params >> 4 & 7); + func_8002ED80(&this->dyna.actor, globalCtx, 0); + + if (1) {} + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1494); + + func_80093D84(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1497), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, xluDLists[subType]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1502); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1507); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1511), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (subType == OBJSWITCH_SUBTYPE_CRYSTAL_1) { + gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(this->crystalSubtype1texture)); + } + + gDPSetEnvColor(POLY_OPA_DISP++, this->crystalColor.r, this->crystalColor.g, this->crystalColor.b, 128); + gSPSegment(POLY_OPA_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->x1TexScroll, this->y1TexScroll, 0x20, 0x20, 1, + this->x2TexScroll, this->y2TexScroll, 0x20, 0x20)); + gSPDisplayList(POLY_OPA_DISP++, opaDLists[subType]); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_switch.c", 1533); +} + +static ObjSwitchActionFunc sDrawFuncs[] = { + ObjSwitch_DrawFloor, ObjSwitch_DrawFloorRusty, ObjSwitch_DrawEye, ObjSwitch_DrawCrystal, ObjSwitch_DrawCrystal, +}; + +void ObjSwitch_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjSwitch* this = (ObjSwitch*)thisx; + + sDrawFuncs[(this->dyna.actor.params & 7)](this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h b/soh/src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h new file mode 100644 index 000000000..3a44c8af2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Switch/z_obj_switch.h @@ -0,0 +1,68 @@ +#ifndef Z_OBJ_SWITCH_H +#define Z_OBJ_SWITCH_H + +#include "ultra64.h" +#include "global.h" + +struct ObjSwitch; + +typedef void (*ObjSwitchActionFunc)(struct ObjSwitch*, GlobalContext*); + +typedef enum { + /* 0 */ OBJSWITCH_TYPE_FLOOR, + /* 1 */ OBJSWITCH_TYPE_FLOOR_RUSTY, + /* 2 */ OBJSWITCH_TYPE_EYE, + /* 3 */ OBJSWITCH_TYPE_CRYSTAL, + /* 4 */ OBJSWITCH_TYPE_CRYSTAL_TARGETABLE +} ObjSwitchType; + +typedef enum { + /* 0 */ OBJSWITCH_SUBTYPE_FLOOR_0, + /* 1 */ OBJSWITCH_SUBTYPE_FLOOR_1, + /* 2 */ OBJSWITCH_SUBTYPE_FLOOR_2, + /* 3 */ OBJSWITCH_SUBTYPE_FLOOR_3 +} ObjSwitchSubTypeFloor; + +typedef enum { + /* 0 */ OBJSWITCH_SUBTYPE_EYE_0, + /* 1 */ OBJSWITCH_SUBTYPE_EYE_1 +} ObjSwitchSubTypeEye; + +typedef enum { + /* 0 */ OBJSWITCH_SUBTYPE_CRYSTAL_0, + /* 1 */ OBJSWITCH_SUBTYPE_CRYSTAL_1, + /* 4 */ OBJSWITCH_SUBTYPE_CRYSTAL_4 = 4 +} ObjSwitchSubTypeCrystal; + +typedef struct { + /* 0x00 */ ColliderJntSph col; + /* 0x20 */ ColliderJntSphElement items[2]; +} ObjSwitchJntSph; + +typedef struct { + /* 0x00 */ ColliderTris col; + /* 0x20 */ ColliderTrisElement items[2]; +} ObjSwitchTris; + +typedef struct ObjSwitch { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjSwitchActionFunc actionFunc; + /* 0x0168 */ s16 releaseTimer; // used for SUBTYPE_FLOOR_2 and SUBTYPE_FLOOR_3 + /* 0x016A */ s16 disableAcTimer; + /* 0x016C */ s16 cooldownTimer; + /* 0x016E */ u8 cooldownOn; + /* 0x0170 */ s16 eyeTexIndex; + /* 0x0174 */ void* crystalSubtype1texture; + /* 0x0178 */ u8 x1TexScroll; + /* 0x0179 */ u8 y1TexScroll; + /* 0x017A */ u8 x2TexScroll; + /* 0x017B */ u8 y2TexScroll; + /* 0x017C */ Color_RGB8 crystalColor; + /* 0x017F */ u8 unk_17F; // used for different purposes between floor and eye switch + union { + /* 0x0180 */ ObjSwitchJntSph jntSph; + /* 0x0180 */ ObjSwitchTris tris; + }; +} ObjSwitch; // size = 0x0258 + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.c b/soh/src/overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.c new file mode 100644 index 000000000..f2e252f90 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.c @@ -0,0 +1,308 @@ +/* + * File: z_obj_syokudai.c + * Overlay: ovl_Obj_Syokudai + * Description: Torch + */ + +#include "z_obj_syokudai.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_syokudai/object_syokudai.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_10) + +void ObjSyokudai_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjSyokudai_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjSyokudai_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjSyokudai_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Obj_Syokudai_InitVars = { + ACTOR_OBJ_SYOKUDAI, + ACTORCAT_PROP, + FLAGS, + OBJECT_SYOKUDAI, + sizeof(ObjSyokudai), + (ActorFunc)ObjSyokudai_Init, + (ActorFunc)ObjSyokudai_Destroy, + (ActorFunc)ObjSyokudai_Update, + (ActorFunc)ObjSyokudai_Draw, + NULL, +}; + +static ColliderCylinderInit sCylInitStand = { + { + COLTYPE_METAL, + AT_NONE, + AC_ON | AC_HARD | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00100000, 0x00, 0x00 }, + { 0xEE01FFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON | BUMP_HOOKABLE, + OCELEM_ON, + }, + { 12, 45, 0, { 0, 0, 0 } }, +}; + +static ColliderCylinderInit sCylInitFlame = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_NONE, + OC2_NONE, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK2, + { 0x00000000, 0x00, 0x00 }, + { 0x00020820, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_NONE, + }, + { 15, 45, 45, { 0, 0, 0 } }, +}; + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 1000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 4000, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), +}; + +static s32 sLitTorchCount; + +void ObjSyokudai_Init(Actor* thisx, GlobalContext* globalCtx) { + static u8 sColTypesStand[] = { 0x09, 0x0B, 0x0B }; + s32 pad; + ObjSyokudai* this = (ObjSyokudai*)thisx; + s32 torchType = this->actor.params & 0xF000; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ActorShape_Init(&this->actor.shape, 0.0f, NULL, 0.0f); + + Collider_InitCylinder(globalCtx, &this->colliderStand); + Collider_SetCylinder(globalCtx, &this->colliderStand, &this->actor, &sCylInitStand); + this->colliderStand.base.colType = sColTypesStand[this->actor.params >> 0xC]; + + Collider_InitCylinder(globalCtx, &this->colliderFlame); + Collider_SetCylinder(globalCtx, &this->colliderFlame, &this->actor, &sCylInitFlame); + + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + + Lights_PointGlowSetInfo(&this->lightInfo, this->actor.world.pos.x, this->actor.world.pos.y + 70.0f, + this->actor.world.pos.z, 255, 255, 180, -1); + this->lightNode = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo); + + if ((this->actor.params & 0x400) || ((torchType != 2) && Flags_GetSwitch(globalCtx, this->actor.params & 0x3F))) { + this->litTimer = -1; + } + + this->flameTexScroll = (s32)(Rand_ZeroOne() * 20.0f); + sLitTorchCount = 0; + Actor_SetFocus(&this->actor, 60.0f); +} + +void ObjSyokudai_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjSyokudai* this = (ObjSyokudai*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->colliderStand); + Collider_DestroyCylinder(globalCtx, &this->colliderFlame); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode); +} + +void ObjSyokudai_Update(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjSyokudai* this = (ObjSyokudai*)thisx; + s32 torchCount = (this->actor.params >> 6) & 0xF; + s32 switchFlag = this->actor.params & 0x3F; + s32 torchType = this->actor.params & 0xF000; + s32 litTimeScale; + WaterBox* dummy; + f32 waterSurface; + s32 lightRadius = -1; + u8 brightness = 0; + Player* player; + EnArrow* arrow; + s32 interactionType; + u32 dmgFlags; + Vec3f tipToFlame; + s32 pad; + s32 pad2; + + litTimeScale = torchCount; + if (torchCount == 10) { + torchCount = 24; + } + if (WaterBox_GetSurfaceImpl(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, + &waterSurface, &dummy) && + ((waterSurface - this->actor.world.pos.y) > 52.0f)) { + this->litTimer = 0; + if (torchType == 1) { + Flags_UnsetSwitch(globalCtx, switchFlag); + if (torchCount != 0) { + this->litTimer = 1; + } + } + } else { + player = GET_PLAYER(globalCtx); + interactionType = 0; + if (this->actor.params & 0x400) { + this->litTimer = -1; + } + if (torchCount != 0) { + if (Flags_GetSwitch(globalCtx, switchFlag)) { + if (this->litTimer == 0) { + this->litTimer = -1; + if (torchType == 0) { + OnePointCutscene_Attention(globalCtx, &this->actor); + } + } else if (this->litTimer > 0) { + this->litTimer = -1; + } + } else if (this->litTimer < 0) { + this->litTimer = 20; + } + } + if (this->colliderFlame.base.acFlags & AC_HIT) { + dmgFlags = this->colliderFlame.info.acHitInfo->toucher.dmgFlags; + if (dmgFlags & 0x20820) { + interactionType = 1; + } + } else if (player->heldItemActionParam == PLAYER_AP_STICK) { + Math_Vec3f_Diff(&player->swordInfo[0].tip, &this->actor.world.pos, &tipToFlame); + tipToFlame.y -= 67.0f; + if ((SQ(tipToFlame.x) + SQ(tipToFlame.y) + SQ(tipToFlame.z)) < SQ(20.0f)) { + interactionType = -1; + } + } + if (interactionType != 0) { + if (this->litTimer != 0) { + if (interactionType < 0) { + if (player->unk_860 == 0) { + player->unk_860 = 210; + Audio_PlaySoundGeneral(NA_SE_EV_FLAME_IGNITION, &this->actor.projectedPos, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + } else if (player->unk_860 < 200) { + player->unk_860 = 200; + } + } else if (dmgFlags & 0x20) { + arrow = (EnArrow*)this->colliderFlame.base.ac; + if ((arrow->actor.update != NULL) && (arrow->actor.id == ACTOR_EN_ARROW)) { + arrow->actor.params = 0; + arrow->collider.info.toucher.dmgFlags = 0x800; + } + } + if ((0 <= this->litTimer) && (this->litTimer < (50 * litTimeScale + 100)) && (torchType != 0)) { + this->litTimer = 50 * litTimeScale + 100; + } + } else if ((torchType != 0) && (((interactionType > 0) && (dmgFlags & 0x20800)) || + ((interactionType < 0) && (player->unk_860 != 0)))) { + + if ((interactionType < 0) && (player->unk_860 < 200)) { + player->unk_860 = 200; + } + if (torchCount == 0) { + this->litTimer = -1; + if (torchType != 2) { + Flags_SetSwitch(globalCtx, switchFlag); + OnePointCutscene_Attention(globalCtx, &this->actor); + } + } else { + sLitTorchCount++; + if (sLitTorchCount >= torchCount) { + Flags_SetSwitch(globalCtx, switchFlag); + OnePointCutscene_Attention(globalCtx, &this->actor); + this->litTimer = -1; + } else { + this->litTimer = (litTimeScale * 50) + 110; + } + } + Audio_PlaySoundGeneral(NA_SE_EV_FLAME_IGNITION, &this->actor.projectedPos, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } + + Collider_UpdateCylinder(&this->actor, &this->colliderStand); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->colliderStand.base); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderStand.base); + + Collider_UpdateCylinder(&this->actor, &this->colliderFlame); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->colliderFlame.base); + + if (this->litTimer > 0) { + this->litTimer--; + if ((this->litTimer == 0) && (torchType != 0)) { + sLitTorchCount--; + } + } + if (this->litTimer != 0) { + if ((this->litTimer < 0) || (this->litTimer >= 20)) { + lightRadius = 200; + } else { + lightRadius = (this->litTimer * 200.0f) / 20.0f; + } + brightness = (u8)(Rand_ZeroOne() * 127.0f) + 128; + func_8002F974(&this->actor, NA_SE_EV_TORCH - SFX_FLAG); + } + Lights_PointSetColorAndRadius(&this->lightInfo, brightness, brightness, 0, lightRadius); + this->flameTexScroll++; +} + +void ObjSyokudai_Draw(Actor* thisx, GlobalContext* globalCtx) { + static Gfx* displayLists[] = { gGoldenTorchDL, gTimedTorchDL, gWoodenTorchDL }; + s32 pad; + ObjSyokudai* this = (ObjSyokudai*)thisx; + s32 timerMax; + + timerMax = (((this->actor.params >> 6) & 0xF) * 50) + 100; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_syokudai.c", 707); + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_syokudai.c", 714), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_OPA_DISP++, displayLists[(u16)this->actor.params >> 0xC]); + + if (this->litTimer != 0) { + f32 flameScale = 1.0f; + + if (this->litTimer > timerMax) { + flameScale = (timerMax - this->litTimer + 10) / 10.0f; + } else if ((this->litTimer > 0) && (this->litTimer < 20)) { + flameScale = this->litTimer / 20.0f; + } + flameScale *= 0.0027f; + + func_80093D84(globalCtx->state.gfxCtx); + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + (this->flameTexScroll * -20) & 0x1FF, 0x20, 0x80)); + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 0, 255); + + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + + Matrix_Translate(0.0f, 52.0f, 0.0f, MTXMODE_APPLY); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) - this->actor.shape.rot.y + 0x8000) * + (M_PI / 0x8000), + MTXMODE_APPLY); + Matrix_Scale(flameScale, flameScale, flameScale, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_syokudai.c", 745), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_syokudai.c", 749); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.h b/soh/src/overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.h new file mode 100644 index 000000000..8c4e1b7d1 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.h @@ -0,0 +1,19 @@ +#ifndef Z_OBJ_SYOKUDAI_H +#define Z_OBJ_SYOKUDAI_H + +#include "ultra64.h" +#include "global.h" + +struct ObjSyokudai; + +typedef struct ObjSyokudai { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder colliderStand; + /* 0x0198 */ ColliderCylinder colliderFlame; + /* 0x01E4 */ s16 litTimer; + /* 0x01E6 */ u8 flameTexScroll; + /* 0x01E8 */ LightNode* lightNode; + /* 0x01EC */ LightInfo lightInfo; +} ObjSyokudai; // size = 0x01FC + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Timeblock/z_obj_timeblock.c b/soh/src/overlays/actors/ovl_Obj_Timeblock/z_obj_timeblock.c new file mode 100644 index 000000000..37cdb3fb4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Timeblock/z_obj_timeblock.c @@ -0,0 +1,348 @@ +/* + * File: z_obj_timeblock.c + * Overlay: ovl_Obj_Timeblock + * Description: Song of Time Block + */ + +#include "z_obj_timeblock.h" +#include "objects/object_timeblock/object_timeblock.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4 | ACTOR_FLAG_25 | ACTOR_FLAG_27) + +void ObjTimeblock_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjTimeblock_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjTimeblock_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjTimeblock_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjTimeblock_SetupNormal(ObjTimeblock* this); +void ObjTimeblock_SetupAltBehaviorVisible(ObjTimeblock* this); +void ObjTimeblock_SetupAltBehaviourNotVisible(ObjTimeblock* this); + +s32 ObjTimeblock_WaitForOcarina(ObjTimeblock* this, GlobalContext* globalCtx); +s32 ObjTimeblock_WaitForSong(ObjTimeblock* this, GlobalContext* globalCtx); +void ObjTimeblock_DoNothing(ObjTimeblock* this, GlobalContext* globalCtx); +void ObjTimeblock_Normal(ObjTimeblock* this, GlobalContext* globalCtx); +void ObjTimeblock_AltBehaviorVisible(ObjTimeblock* this, GlobalContext* globalCtx); +void ObjTimeblock_AltBehaviourNotVisible(ObjTimeblock* this, GlobalContext* globalCtx); + +const ActorInit Obj_Timeblock_InitVars = { + ACTOR_OBJ_TIMEBLOCK, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_TIMEBLOCK, + sizeof(ObjTimeblock), + (ActorFunc)ObjTimeblock_Init, + (ActorFunc)ObjTimeblock_Destroy, + (ActorFunc)ObjTimeblock_Update, + (ActorFunc)ObjTimeblock_Draw, + NULL, +}; + +typedef struct { + /* 0x00 */ f32 scale; + /* 0x04 */ f32 height; + /* 0x08 */ s16 demoEffectParams; +} ObjTimeblockSizeOptions; // size = 0x0C + +static ObjTimeblockSizeOptions sSizeOptions[] = { + { 1.0, 60.0, 0x0018 }, + { 0.60, 40.0, 0x0019 }, +}; + +static f32 sRanges[] = { 60.0, 100.0, 140.0, 180.0, 220.0, 260.0, 300.0, 300.0 }; + +static InitChainEntry sInitChain[] = { + ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP), +}; + +static Color_RGB8 sPrimColors[] = { + { 100, 120, 140 }, { 80, 140, 200 }, { 100, 150, 200 }, { 100, 200, 240 }, + { 80, 110, 140 }, { 70, 160, 225 }, { 80, 100, 130 }, { 100, 110, 190 }, +}; + +u32 ObjTimeblock_CalculateIsVisible(ObjTimeblock* this) { + if (!((this->dyna.actor.params >> 10) & 1)) { + if (this->unk_177 == 0) { + return this->unk_175; + } else { + u8 temp = ((this->dyna.actor.params >> 15) & 1) ? true : false; + + if (this->unk_177 == 1) { + return this->unk_174 ^ temp; + } else { + u8 linkIsYoung = (LINK_AGE_IN_YEARS == YEARS_CHILD) ? true : false; + + return this->unk_174 ^ temp ^ linkIsYoung; + } + } + } else { + return (((this->dyna.actor.params >> 15) & 1) ? true : false) ^ this->unk_174; + } +} + +void ObjTimeblock_SpawnDemoEffect(ObjTimeblock* this, GlobalContext* globalCtx) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, 0, 0, + sSizeOptions[(this->dyna.actor.params >> 8) & 1].demoEffectParams); +} + +void ObjTimeblock_ToggleSwitchFlag(GlobalContext* globalCtx, s32 flag) { + if (Flags_GetSwitch(globalCtx, flag)) { + Flags_UnsetSwitch(globalCtx, flag); + } else { + Flags_SetSwitch(globalCtx, flag); + } +} + +void ObjTimeblock_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjTimeblock* this = (ObjTimeblock*)thisx; + s32 pad; + CollisionHeader* colHeader = NULL; + + DynaPolyActor_Init(&this->dyna, DPM_UNK); + this->dyna.actor.world.rot.z = this->dyna.actor.shape.rot.z = 0; + + CollisionHeader_GetVirtual(&gSongOfTimeBlockCol, &colHeader); + + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader); + + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + Actor_SetScale(&this->dyna.actor, sSizeOptions[(this->dyna.actor.params >> 8) & 1].scale); + + if ((this->dyna.actor.params >> 6) & 1) { + this->unk_177 = 0; + } else { + this->unk_177 = ((this->dyna.actor.params & 0x3F) < 0x38) ? 2 : 1; + } + + this->songObserverFunc = ObjTimeblock_WaitForOcarina; + + Actor_SetFocus(&this->dyna.actor, sSizeOptions[(this->dyna.actor.params >> 8) & 1].height); + + this->unk_174 = (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) ? true : false; + this->unk_175 = ((this->dyna.actor.params >> 15) & 1) ? true : false; + this->isVisible = ObjTimeblock_CalculateIsVisible(this); + + if (!((this->dyna.actor.params >> 10) & 1)) { + ObjTimeblock_SetupNormal(this); + } else if (this->isVisible) { + ObjTimeblock_SetupAltBehaviorVisible(this); + } else { + ObjTimeblock_SetupAltBehaviourNotVisible(this); + } + + // "Block of time" + osSyncPrintf("時のブロック ( %04xH save:%d color:%d range:%d move:%d)\n", (u16)this->dyna.actor.params, + this->unk_177, this->dyna.actor.home.rot.z & 7, (this->dyna.actor.params >> 11) & 7, + (this->dyna.actor.params >> 10) & 1); +} + +void ObjTimeblock_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + ObjTimeblock* this = (ObjTimeblock*)thisx; + + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); +} + +u8 ObjTimeblock_PlayerIsInRange(ObjTimeblock* this, GlobalContext* globalCtx) { + if (this->isVisible && func_80043590(&this->dyna)) { + return false; + } + + if (this->dyna.actor.xzDistToPlayer <= sRanges[(this->dyna.actor.params >> 11) & 7]) { + Vec3f distance; + f32 blockSize; + + func_8002DBD0(&this->dyna.actor, &distance, &GET_PLAYER(globalCtx)->actor.world.pos); + blockSize = this->dyna.actor.scale.x * 50.0f + 6.0f; + // Return true if player's xz position is not inside the block + if (blockSize < fabsf(distance.x) || blockSize < fabsf(distance.z)) { + return true; + } + } + + return false; +} + +s32 ObjTimeblock_WaitForOcarina(ObjTimeblock* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (ObjTimeblock_PlayerIsInRange(this, globalCtx)) { + if (player->stateFlags2 & 0x1000000) { + func_8010BD58(globalCtx, OCARINA_ACTION_FREE_PLAY); + this->songObserverFunc = ObjTimeblock_WaitForSong; + } else { + player->stateFlags2 |= 0x800000; + } + } + return false; +} + +s32 ObjTimeblock_WaitForSong(ObjTimeblock* this, GlobalContext* globalCtx) { + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + this->songObserverFunc = ObjTimeblock_WaitForOcarina; + } + if (globalCtx->msgCtx.lastPlayedSong == OCARINA_SONG_TIME) { + if (this->unk_172 == 254) { + this->songEndTimer = 110; + } else { + this->songEndTimer--; + if (this->songEndTimer == 0) { + return true; + } + } + } + return false; +} + +void ObjTimeblock_SetupDoNothing(ObjTimeblock* this) { + this->actionFunc = ObjTimeblock_DoNothing; +} + +void ObjTimeblock_DoNothing(ObjTimeblock* this, GlobalContext* globalCtx) { +} + +void ObjTimeblock_SetupNormal(ObjTimeblock* this) { + this->actionFunc = ObjTimeblock_Normal; +} + +void ObjTimeblock_Normal(ObjTimeblock* this, GlobalContext* globalCtx) { + u32 newIsVisible; + + if (this->songObserverFunc(this, globalCtx) && this->demoEffectTimer <= 0) { + ObjTimeblock_SpawnDemoEffect(this, globalCtx); + this->demoEffectTimer = 160; + + // Possibly points the camera to this actor + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + // "◯◯◯◯ Time Block Attention Camera (frame counter %d)\n" + osSyncPrintf("◯◯◯◯ Time Block 注目カメラ (frame counter %d)\n", globalCtx->state.frames); + + this->demoEffectFirstPartTimer = 12; + + if (this->unk_177 == 0) { + this->dyna.actor.params ^= 0x8000; + } else { + ObjTimeblock_ToggleSwitchFlag(globalCtx, this->dyna.actor.params & 0x3F); + } + } + + this->unk_172 = globalCtx->msgCtx.lastPlayedSong; + if (this->demoEffectFirstPartTimer > 0) { + this->demoEffectFirstPartTimer--; + if (this->demoEffectFirstPartTimer == 0) { + if (this->unk_177 == 0) { + this->unk_175 = ((this->dyna.actor.params >> 15) & 1) ? true : false; + } else { + this->unk_174 = (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) ? true : false; + } + } + } + + newIsVisible = ObjTimeblock_CalculateIsVisible(this); + if (this->unk_177 == 1 && newIsVisible != this->isVisible) { + ObjTimeblock_SetupDoNothing(this); + } + this->isVisible = newIsVisible; + + if (this->demoEffectTimer == 50) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } +} + +void func_80BA06AC(ObjTimeblock* this, GlobalContext* globalCtx) { + s32 switchFlag = this->dyna.actor.params & 0x3F; + + this->unk_172 = globalCtx->msgCtx.lastPlayedSong; + + if (this->demoEffectFirstPartTimer > 0 && --this->demoEffectFirstPartTimer == 0) { + this->unk_174 = (Flags_GetSwitch(globalCtx, switchFlag)) ? true : false; + } + + this->isVisible = ObjTimeblock_CalculateIsVisible(this); + this->unk_176 = (Flags_GetSwitch(globalCtx, switchFlag)) ? true : false; +} + +void ObjTimeblock_SetupAltBehaviorVisible(ObjTimeblock* this) { + this->actionFunc = ObjTimeblock_AltBehaviorVisible; +} + +void ObjTimeblock_AltBehaviorVisible(ObjTimeblock* this, GlobalContext* globalCtx) { + if (this->songObserverFunc(this, globalCtx) && this->demoEffectTimer <= 0) { + this->demoEffectFirstPartTimer = 12; + ObjTimeblock_SpawnDemoEffect(this, globalCtx); + this->demoEffectTimer = 160; + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + // "Time Block Attention Camera (frame counter)" + osSyncPrintf("◯◯◯◯ Time Block 注目カメラ (frame counter %d)\n", globalCtx->state.frames); + ObjTimeblock_ToggleSwitchFlag(globalCtx, this->dyna.actor.params & 0x3F); + } + + func_80BA06AC(this, globalCtx); + + if (this->demoEffectTimer == 50) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } + + if (!this->isVisible && this->demoEffectTimer <= 0) { + ObjTimeblock_SetupAltBehaviourNotVisible(this); + } +} + +void ObjTimeblock_SetupAltBehaviourNotVisible(ObjTimeblock* this) { + this->actionFunc = ObjTimeblock_AltBehaviourNotVisible; +} + +void ObjTimeblock_AltBehaviourNotVisible(ObjTimeblock* this, GlobalContext* globalCtx) { + s32 switchFlag = this->dyna.actor.params & 0x3F; + s8 switchFlagIsSet = (Flags_GetSwitch(globalCtx, switchFlag)) ? true : false; + + if (this->unk_176 ^ switchFlagIsSet && switchFlagIsSet ^ (((this->dyna.actor.params >> 15) & 1) ? true : false)) { + if (this->demoEffectTimer <= 0) { + ObjTimeblock_SpawnDemoEffect(this, globalCtx); + this->demoEffectTimer = 160; + } + this->demoEffectFirstPartTimer = 12; + } + + func_80BA06AC(this, globalCtx); + + if (this->isVisible && this->demoEffectTimer <= 0) { + ObjTimeblock_SetupAltBehaviorVisible(this); + } +} + +void ObjTimeblock_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjTimeblock* this = (ObjTimeblock*)thisx; + + this->actionFunc(this, globalCtx); + + if (this->demoEffectTimer > 0) { + this->demoEffectTimer--; + } + + if (this->isVisible) { + func_8003EC50(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } else { + func_8003EBF8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void ObjTimeblock_Draw(Actor* thisx, GlobalContext* globalCtx) { + if (((ObjTimeblock*)thisx)->isVisible) { + Color_RGB8* primColor = &sPrimColors[thisx->home.rot.z & 7]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_timeblock.c", 762); + + func_80093D18(globalCtx->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_timeblock.c", 766), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, primColor->r, primColor->g, primColor->b, 255); + gSPDisplayList(POLY_OPA_DISP++, gSongOfTimeBlockDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_timeblock.c", 772); + } +} diff --git a/soh/src/overlays/actors/ovl_Obj_Timeblock/z_obj_timeblock.h b/soh/src/overlays/actors/ovl_Obj_Timeblock/z_obj_timeblock.h new file mode 100644 index 000000000..7b890b1cb --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Timeblock/z_obj_timeblock.h @@ -0,0 +1,27 @@ +#ifndef Z_OBJ_TIMEBLOCK_H +#define Z_OBJ_TIMEBLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct ObjTimeblock; + +typedef s32 (*ObjTimeblockSongObserverFunc)(struct ObjTimeblock*, GlobalContext*); +typedef void (*ObjTimeblockActionFunc)(struct ObjTimeblock*, GlobalContext*); + +typedef struct ObjTimeblock { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjTimeblockActionFunc actionFunc; + /* 0x0168 */ ObjTimeblockSongObserverFunc songObserverFunc; + /* 0x016C */ s16 demoEffectTimer; + /* 0x016E */ s16 songEndTimer; + /* 0x0170 */ s16 demoEffectFirstPartTimer; + /* 0x0172 */ u16 unk_172; + /* 0x0174 */ u8 unk_174; + /* 0x0175 */ u8 unk_175; + /* 0x0176 */ u8 unk_176; + /* 0x0177 */ u8 unk_177; + /* 0x0178 */ u8 isVisible; +} ObjTimeblock; // size = 0x017C + +#endif diff --git a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c new file mode 100644 index 000000000..aaecfec6d --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c @@ -0,0 +1,339 @@ +/* + * File: z_obj_tsubo.c + * Overlay: ovl_Obj_Tsubo + * Description: Breakable pot + */ + +#include "z_obj_tsubo.h" +#include "overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h" +#include "objects/gameplay_dangeon_keep/gameplay_dangeon_keep.h" +#include "objects/object_tsubo/object_tsubo.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_23) + +void ObjTsubo_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjTsubo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjTsubo_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjTsubo_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjTsubo_SpawnCollectible(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_ApplyGravity(ObjTsubo* this); +s32 ObjTsubo_SnapToFloor(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_InitCollider(Actor* thisx, GlobalContext* globalCtx); +void ObjTsubo_AirBreak(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_WaterBreak(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_SetupWaitForObject(ObjTsubo* this); +void ObjTsubo_WaitForObject(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_SetupIdle(ObjTsubo* this); +void ObjTsubo_Idle(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_SetupLiftedUp(ObjTsubo* this); +void ObjTsubo_LiftedUp(ObjTsubo* this, GlobalContext* globalCtx); +void ObjTsubo_SetupThrown(ObjTsubo* this); +void ObjTsubo_Thrown(ObjTsubo* this, GlobalContext* globalCtx); + +static s16 D_80BA1B50 = 0; +static s16 D_80BA1B54 = 0; +static s16 D_80BA1B58 = 0; +static s16 D_80BA1B5C = 0; + +const ActorInit Obj_Tsubo_InitVars = { + ACTOR_OBJ_TSUBO, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjTsubo), + (ActorFunc)ObjTsubo_Init, + (ActorFunc)ObjTsubo_Destroy, + (ActorFunc)ObjTsubo_Update, + NULL, + NULL, +}; + +static s16 sObjectIds[] = { OBJECT_GAMEPLAY_DANGEON_KEEP, OBJECT_TSUBO }; + +static Gfx* D_80BA1B84[] = { gPotDL, object_tsubo_DL_0017C0 }; + +static Gfx* D_80BA1B8C[] = { gPotFragmentDL, object_tsubo_DL_001960 }; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_HARD, + AT_ON | AT_TYPE_PLAYER, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_2, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000002, 0x00, 0x01 }, + { 0x4FC1FFFE, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_ON, + }, + { 9, 26, 0, { 0, 0, 0 } }, +}; + +static CollisionCheckInfoInit sColChkInfoInit[] = { 0, 12, 60, MASS_IMMOVABLE }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32_DIV1000(gravity, -1200, ICHAIN_CONTINUE), ICHAIN_F32_DIV1000(minVelocityY, -20000, ICHAIN_CONTINUE), + ICHAIN_VEC3F_DIV1000(scale, 150, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneForward, 900, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 100, ICHAIN_CONTINUE), ICHAIN_F32(uncullZoneDownward, 800, ICHAIN_STOP), +}; + +void ObjTsubo_SpawnCollectible(ObjTsubo* this, GlobalContext* globalCtx) { + s16 dropParams = this->actor.params & 0x1F; + + if ((dropParams >= ITEM00_RUPEE_GREEN) && (dropParams <= ITEM00_BOMBS_SPECIAL)) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, + (dropParams | (((this->actor.params >> 9) & 0x3F) << 8))); + } +} + +void ObjTsubo_ApplyGravity(ObjTsubo* this) { + this->actor.velocity.y += this->actor.gravity; + if (this->actor.velocity.y < this->actor.minVelocityY) { + this->actor.velocity.y = this->actor.minVelocityY; + } +} + +s32 ObjTsubo_SnapToFloor(ObjTsubo* this, GlobalContext* globalCtx) { + CollisionPoly* floorPoly; + Vec3f pos; + s32 bgID; + f32 floorY; + + pos.x = this->actor.world.pos.x; + pos.y = this->actor.world.pos.y + 20.0f; + pos.z = this->actor.world.pos.z; + floorY = BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &floorPoly, &bgID, &this->actor, &pos); + if (floorY > BGCHECK_Y_MIN) { + this->actor.world.pos.y = floorY; + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + return true; + } else { + osSyncPrintf("地面に付着失敗\n"); + return false; + } +} + +void ObjTsubo_InitCollider(Actor* thisx, GlobalContext* globalCtx) { + ObjTsubo* this = (ObjTsubo*)thisx; + + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + Collider_UpdateCylinder(&this->actor, &this->collider); +} + +void ObjTsubo_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjTsubo* this = (ObjTsubo*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + ObjTsubo_InitCollider(&this->actor, globalCtx); + CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, sColChkInfoInit); + if (!ObjTsubo_SnapToFloor(this, globalCtx)) { + Actor_Kill(&this->actor); + return; + } + this->objTsuboBankIndex = Object_GetIndex(&globalCtx->objectCtx, sObjectIds[(this->actor.params >> 8) & 1]); + if (this->objTsuboBankIndex < 0) { + osSyncPrintf("Error : バンク危険! (arg_data 0x%04x)(%s %d)\n", this->actor.params, "../z_obj_tsubo.c", 410); + Actor_Kill(&this->actor); + } else { + ObjTsubo_SetupWaitForObject(this); + osSyncPrintf("(dungeon keep 壷)(arg_data 0x%04x)\n", this->actor.params); + } +} + +void ObjTsubo_Destroy(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjTsubo* this = (ObjTsubo*)thisx; + + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +void ObjTsubo_AirBreak(ObjTsubo* this, GlobalContext* globalCtx) { + s32 pad; + f32 rand; + s16 angle; + Vec3f pos; + Vec3f velocity; + f32 sins; + f32 coss; + s32 arg5; + s32 i; + + for (i = 0, angle = 0; i < 15; i++, angle += 0x4E20) { + sins = Math_SinS(angle); + coss = Math_CosS(angle); + pos.x = sins * 8.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = coss * 8.0f; + velocity.x = pos.x * 0.23f; + velocity.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + velocity.z = pos.z * 0.23f; + Math_Vec3f_Sum(&pos, &this->actor.world.pos, &pos); + rand = Rand_ZeroOne(); + if (rand < 0.2f) { + arg5 = 96; + } else if (rand < 0.6f) { + arg5 = 64; + } else { + arg5 = 32; + } + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &this->actor.world.pos, -240, arg5, 10, 10, 0, + (Rand_ZeroOne() * 95.0f) + 15.0f, 0, 32, 60, KAKERA_COLOR_NONE, + sObjectIds[(this->actor.params >> 8) & 1], D_80BA1B8C[(this->actor.params >> 8) & 1]); + } + func_80033480(globalCtx, &this->actor.world.pos, 30.0f, 4, 20, 50, 1); +} + +void ObjTsubo_WaterBreak(ObjTsubo* this, GlobalContext* globalCtx) { + s32 pad[2]; + s16 angle; + Vec3f pos = this->actor.world.pos; + Vec3f velocity; + s32 phi_s0; + s32 i; + + pos.y += this->actor.yDistToWater; + EffectSsGSplash_Spawn(globalCtx, &pos, NULL, NULL, 0, 400); + for (i = 0, angle = 0; i < 15; i++, angle += 0x4E20) { + f32 sins = Math_SinS(angle); + f32 coss = Math_CosS(angle); + + pos.x = sins * 8.0f; + pos.y = (Rand_ZeroOne() * 5.0f) + 2.0f; + pos.z = coss * 8.0f; + velocity.x = pos.x * 0.2f; + velocity.y = (Rand_ZeroOne() * 4.0f) + 2.0f; + velocity.z = pos.z * 0.2f; + Math_Vec3f_Sum(&pos, &this->actor.world.pos, &pos); + phi_s0 = (Rand_ZeroOne() < .2f) ? 64 : 32; + EffectSsKakera_Spawn(globalCtx, &pos, &velocity, &this->actor.world.pos, -180, phi_s0, 30, 30, 0, + (Rand_ZeroOne() * 95.0f) + 15.0f, 0, 32, 70, KAKERA_COLOR_NONE, + sObjectIds[(this->actor.params >> 8) & 1], D_80BA1B8C[(this->actor.params >> 8) & 1]); + } +} + +void ObjTsubo_SetupWaitForObject(ObjTsubo* this) { + this->actionFunc = ObjTsubo_WaitForObject; +} + +void ObjTsubo_WaitForObject(ObjTsubo* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->objTsuboBankIndex)) { + this->actor.draw = ObjTsubo_Draw; + this->actor.objBankIndex = this->objTsuboBankIndex; + ObjTsubo_SetupIdle(this); + this->actor.flags &= ~ACTOR_FLAG_4; + } +} + +void ObjTsubo_SetupIdle(ObjTsubo* this) { + this->actionFunc = ObjTsubo_Idle; +} + +void ObjTsubo_Idle(ObjTsubo* this, GlobalContext* globalCtx) { + s32 pad; + s16 temp_v0; + s32 phi_v1; + + if (Actor_HasParent(&this->actor, globalCtx)) { + ObjTsubo_SetupLiftedUp(this); + } else if ((this->actor.bgCheckFlags & 0x20) && (this->actor.yDistToWater > 15.0f)) { + ObjTsubo_WaterBreak(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_POT_BROKEN); + ObjTsubo_SpawnCollectible(this, globalCtx); + Actor_Kill(&this->actor); + } else if ((this->collider.base.acFlags & AC_HIT) && + (this->collider.info.acHitInfo->toucher.dmgFlags & 0x4FC1FFFC)) { + ObjTsubo_AirBreak(this, globalCtx); + ObjTsubo_SpawnCollectible(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_POT_BROKEN); + Actor_Kill(&this->actor); + } else { + if (this->actor.xzDistToPlayer < 600.0f) { + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.base.acFlags &= ~AC_HIT; + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + if (this->actor.xzDistToPlayer < 150.0f) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } + if (this->actor.xzDistToPlayer < 100.0f) { + temp_v0 = this->actor.yawTowardsPlayer - GET_PLAYER(globalCtx)->actor.world.rot.y; + phi_v1 = ABS(temp_v0); + if (phi_v1 >= 0x5556) { + // GI_NONE in this case allows the player to lift the actor + func_8002F434(&this->actor, globalCtx, GI_NONE, 30.0f, 30.0f); + } + } + } +} + +void ObjTsubo_SetupLiftedUp(ObjTsubo* this) { + this->actionFunc = ObjTsubo_LiftedUp; + this->actor.room = -1; + func_8002F7DC(&this->actor, NA_SE_PL_PULL_UP_POT); + this->actor.flags |= ACTOR_FLAG_4; +} + +void ObjTsubo_LiftedUp(ObjTsubo* this, GlobalContext* globalCtx) { + if (Actor_HasNoParent(&this->actor, globalCtx)) { + this->actor.room = globalCtx->roomCtx.curRoom.num; + ObjTsubo_SetupThrown(this); + ObjTsubo_ApplyGravity(this); + func_8002D7EC(&this->actor); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 15.0f, 0.0f, 0x85); + } +} + +void ObjTsubo_SetupThrown(ObjTsubo* this) { + this->actor.velocity.x = Math_SinS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.velocity.z = Math_CosS(this->actor.world.rot.y) * this->actor.speedXZ; + this->actor.colChkInfo.mass = 240; + D_80BA1B50 = (Rand_ZeroOne() - 0.7f) * 2800.0f; + D_80BA1B58 = (Rand_ZeroOne() - 0.5f) * 2000.0f; + D_80BA1B54 = 0; + D_80BA1B5C = 0; + this->actionFunc = ObjTsubo_Thrown; +} + +void ObjTsubo_Thrown(ObjTsubo* this, GlobalContext* globalCtx) { + s32 pad[2]; + + if ((this->actor.bgCheckFlags & 0xB) || (this->collider.base.atFlags & AT_HIT)) { + ObjTsubo_AirBreak(this, globalCtx); + ObjTsubo_SpawnCollectible(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_POT_BROKEN); + Actor_Kill(&this->actor); + } else if (this->actor.bgCheckFlags & 0x40) { + ObjTsubo_WaterBreak(this, globalCtx); + ObjTsubo_SpawnCollectible(this, globalCtx); + SoundSource_PlaySfxAtFixedWorldPos(globalCtx, &this->actor.world.pos, 20, NA_SE_EV_POT_BROKEN); + Actor_Kill(&this->actor); + } else { + ObjTsubo_ApplyGravity(this); + func_8002D7EC(&this->actor); + Math_StepToS(&D_80BA1B54, D_80BA1B50, 0x64); + Math_StepToS(&D_80BA1B5C, D_80BA1B58, 0x64); + this->actor.shape.rot.x += D_80BA1B54; + this->actor.shape.rot.y += D_80BA1B5C; + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 5.0f, 15.0f, 0.0f, 0x85); + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } +} + +void ObjTsubo_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjTsubo* this = (ObjTsubo*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ObjTsubo_Draw(Actor* thisx, GlobalContext* globalCtx) { + Gfx_DrawDListOpa(globalCtx, D_80BA1B84[(thisx->params >> 8) & 1]); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.h b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.h new file mode 100644 index 000000000..c8abce650 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.h @@ -0,0 +1,19 @@ +#ifndef Z_OBJ_TSUBO_H +#define Z_OBJ_TSUBO_H + +#include "ultra64.h" +#include "global.h" + +struct ObjTsubo; + +typedef void (*ObjTsuboActionFunc)(struct ObjTsubo*, GlobalContext*); + +typedef struct ObjTsubo { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjTsuboActionFunc actionFunc; + /* 0x0150 */ ColliderCylinder collider; + /* 0x019C */ s8 objTsuboBankIndex; +} ObjTsubo; // size = 0x01A0 + +#endif + diff --git a/soh/src/overlays/actors/ovl_Obj_Warp2block/z_obj_warp2block.c b/soh/src/overlays/actors/ovl_Obj_Warp2block/z_obj_warp2block.c new file mode 100644 index 000000000..97e1aede2 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Warp2block/z_obj_warp2block.c @@ -0,0 +1,320 @@ +/* + * File: z_obj_warp2block.c + * Overlay: ovl_Obj_Warp2Block + * Description: Navi Infospot (Green, Time Block) + */ + +#include "z_obj_warp2block.h" +#include "objects/object_timeblock/object_timeblock.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_4 | ACTOR_FLAG_25 | ACTOR_FLAG_27) + +void ObjWarp2block_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjWarp2block_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjWarp2block_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjWarp2block_Draw(Actor* thisx, GlobalContext* globalCtx); + +void ObjWarp2block_Spawn(ObjWarp2block* this, GlobalContext* globalCtx); +s32 func_80BA1ECC(ObjWarp2block* this, GlobalContext* globalCtx); +void ObjWarp2block_SwapWithChild(ObjWarp2block* this, GlobalContext* globalCtx); +s32 func_80BA2218(ObjWarp2block* this, GlobalContext* globalCtx); +s32 func_80BA228C(ObjWarp2block* this, GlobalContext* globalCtx); +s32 func_80BA2304(ObjWarp2block* this, GlobalContext* globalCtx); +void ObjWarp2block_SetInactive(ObjWarp2block* this); +void ObjWarp2block_DoNothing(ObjWarp2block* this, GlobalContext* globalCtx); +void func_80BA24E8(ObjWarp2block* this); +void func_80BA24F8(ObjWarp2block* this, GlobalContext* globalCtx); +void func_80BA2600(ObjWarp2block* this); +void func_80BA2610(ObjWarp2block* this, GlobalContext* globalCtx); + +const ActorInit Obj_Warp2block_InitVars = { + ACTOR_OBJ_WARP2BLOCK, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_TIMEBLOCK, + sizeof(ObjWarp2block), + (ActorFunc)ObjWarp2block_Init, + (ActorFunc)ObjWarp2block_Destroy, + (ActorFunc)ObjWarp2block_Update, + (ActorFunc)ObjWarp2block_Draw, + NULL, +}; + +typedef struct { + /* 0x00 */ f32 scale; + /* 0x04 */ f32 focus; + /* 0x08 */ s16 params; +} Warp2BlockSpawnData; // size = 0x0C + +static Warp2BlockSpawnData sSpawnData[] = { + { 1.0f, 60.0f, 0x0018 }, + { 0.6f, 40.0f, 0x0019 }, +}; + +static f32 sDistances[] = { 60.0f, 100.0f, 140.0f, 180.0f, 220.0f, 260.0f, 300.0f, 300.0f }; + +static InitChainEntry sInitChain[] = { + ICHAIN_F32(uncullZoneForward, 1800, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneScale, 300, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneDownward, 1500, ICHAIN_STOP), +}; + +static Color_RGB8 sColors[] = { + { 100, 120, 140 }, { 80, 140, 200 }, { 100, 150, 200 }, { 100, 200, 240 }, + { 80, 110, 140 }, { 70, 160, 225 }, { 80, 100, 130 }, { 100, 110, 190 }, +}; + +void ObjWarp2block_Spawn(ObjWarp2block* this, GlobalContext* globalCtx) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->dyna.actor.world.pos.x, + this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, 0, 0, + sSpawnData[(this->dyna.actor.params >> 8) & 1].params); + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_EFFECT, this->dyna.actor.child->world.pos.x, + this->dyna.actor.child->world.pos.y, this->dyna.actor.child->world.pos.z, 0, 0, 0, + sSpawnData[(this->dyna.actor.child->params >> 8) & 1].params); +} + +s32 func_80BA1ECC(ObjWarp2block* this, GlobalContext* globalCtx) { + s32 pad; + Actor* temp_a3; + Player* player; + Vec3f sp20; + f32 temp_f2; + + if (func_80043590(&this->dyna)) { + return 0; + } + + temp_a3 = this->dyna.actor.child; + player = GET_PLAYER(globalCtx); + if ((this->dyna.actor.xzDistToPlayer <= sDistances[(((this->dyna.actor.params >> 0xB) & 7))]) || + (temp_a3->xzDistToPlayer <= sDistances[(((temp_a3->params >> 0xB) & 7))])) { + + func_8002DBD0(&this->dyna.actor, &sp20, &player->actor.world.pos); + temp_f2 = (this->dyna.actor.scale.x * 50.0f) + 6.0f; + + if (!(temp_f2 < fabsf(sp20.x)) && !(temp_f2 < fabsf(sp20.z))) { + return 0; + } + + func_8002DBD0(temp_a3, &sp20, &player->actor.world.pos); + temp_f2 = (temp_a3->scale.x * 50.0f) + 6.0f; + + if (!(temp_f2 < fabsf(sp20.x)) && !(temp_f2 < fabsf(sp20.z))) { + return 0; + } + } else { + return 0; + } + + return 1; +} + +void ObjWarp2block_SwapWithChild(ObjWarp2block* this, GlobalContext* globalCtx) { + Vec3f tempVec; + Vec3s tempRot; + s32 temp; + + Math_Vec3f_Copy(&tempVec, &this->dyna.actor.world.pos); + Math_Vec3f_Copy(&this->dyna.actor.world.pos, &this->dyna.actor.child->world.pos); + Math_Vec3f_Copy(&this->dyna.actor.child->world.pos, &tempVec); + + temp = this->dyna.actor.world.rot.y; + this->dyna.actor.world.rot.y = this->dyna.actor.child->world.rot.y; + this->dyna.actor.child->world.rot.y = temp; + + temp = this->dyna.actor.shape.rot.y; + this->dyna.actor.shape.rot.y = this->dyna.actor.child->shape.rot.y; + this->dyna.actor.child->shape.rot.y = temp; + + temp = this->dyna.actor.home.rot.z; + this->dyna.actor.home.rot.z = this->dyna.actor.child->home.rot.z; + this->dyna.actor.child->home.rot.z = temp; + + Math_Vec3f_Copy(&tempVec, &this->dyna.actor.scale); + Math_Vec3f_Copy(&this->dyna.actor.scale, &this->dyna.actor.child->scale); + Math_Vec3f_Copy(&this->dyna.actor.child->scale, &tempVec); + + Math_Vec3f_Copy(&tempVec, &this->dyna.actor.focus.pos); + Math_Vec3f_Copy(&this->dyna.actor.focus.pos, &this->dyna.actor.child->focus.pos); + Math_Vec3f_Copy(&this->dyna.actor.child->focus.pos, &tempVec); + + tempRot = this->dyna.actor.focus.rot; + this->dyna.actor.focus.rot = this->dyna.actor.child->focus.rot; + this->dyna.actor.child->focus.rot = tempRot; + + temp = this->dyna.actor.params & 0x7FFF; + this->dyna.actor.params = (this->dyna.actor.params & 0x8000) | (this->dyna.actor.child->params & 0x7FFF); + this->dyna.actor.child->params = (this->dyna.actor.child->params & 0x8000) | (temp & 0x7FFF); + + if (Math3D_Vec3fDistSq(&this->dyna.actor.world.pos, &this->dyna.actor.home.pos) < 0.01f) { + Flags_UnsetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + } else { + Flags_SetSwitch(globalCtx, this->dyna.actor.params & 0x3F); + } +} + +s32 func_80BA2218(ObjWarp2block* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + + if (func_80BA1ECC(this, globalCtx)) { + if (player->stateFlags2 & 0x1000000) { + func_8010BD58(globalCtx, OCARINA_ACTION_FREE_PLAY); + this->func_168 = func_80BA228C; + } else { + player->stateFlags2 |= 0x800000; + } + } + + return 0; +} + +s32 func_80BA228C(ObjWarp2block* this, GlobalContext* globalCtx) { + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + this->func_168 = func_80BA2218; + } + + if (globalCtx->msgCtx.lastPlayedSong == OCARINA_SONG_TIME) { + if (this->unk_172 == 0xFE) { + this->unk_16E = 0x6E; + } else { + this->unk_16E--; + if (this->unk_16E == 0) { + return 1; + } + } + } + return 0; +} + +s32 func_80BA2304(ObjWarp2block* this, GlobalContext* globalCtx) { + s32 ret = this->func_168(this, globalCtx); + + this->unk_172 = globalCtx->msgCtx.lastPlayedSong; + + return ret; +} + +void ObjWarp2block_Init(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + ObjWarp2block* this = (ObjWarp2block*)thisx; + CollisionHeader* collisionHeader; + + collisionHeader = NULL; + this->dyna.actor.world.rot.z = this->dyna.actor.shape.rot.z = 0; + Actor_ProcessInitChain(&this->dyna.actor, sInitChain); + + Actor_SetScale(&this->dyna.actor, sSpawnData[(this->dyna.actor.params >> 8) & 1].scale); + this->func_168 = func_80BA2218; + Actor_SetFocus(&this->dyna.actor, sSpawnData[(this->dyna.actor.params >> 8) & 1].focus); + + if ((this->dyna.actor.params >> 0xF) & 1) { + func_80BA24E8(this); + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + this->dyna.actor.draw = NULL; + } + DynaPolyActor_Init(&this->dyna, 0); + CollisionHeader_GetVirtual(&gSongOfTimeBlockCol, &collisionHeader); + this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, collisionHeader); + } else { + ObjWarp2block_SetInactive(this); + } + + osSyncPrintf("時のブロック(ワープ2) ( %04xH color:%d range:%d)\n", this->dyna.actor.params & 0xFFFF, + this->dyna.actor.home.rot.z & 7, (this->dyna.actor.params >> 0xB) & 7); +} + +void ObjWarp2block_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ObjWarp2block* this = (ObjWarp2block*)thisx; + if ((this->dyna.actor.params >> 0xF) & 1) { + DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId); + } +} + +void ObjWarp2block_SetInactive(ObjWarp2block* this) { + this->actionFunc = ObjWarp2block_DoNothing; + this->dyna.actor.draw = NULL; +} + +void ObjWarp2block_DoNothing(ObjWarp2block* this, GlobalContext* globalCtx) { +} + +void func_80BA24E8(ObjWarp2block* this) { + this->actionFunc = func_80BA24F8; +} + +void func_80BA24F8(ObjWarp2block* this, GlobalContext* globalCtx) { + Actor* current = globalCtx->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; + + while (current != NULL) { + if (current->id == ACTOR_OBJ_WARP2BLOCK && !((current->params >> 0xF) & 1) && + ((this->dyna.actor.params & 0x3F) == (current->params & 0x3F))) { + this->dyna.actor.child = current; + if (Flags_GetSwitch(globalCtx, this->dyna.actor.params & 0x3F)) { + ObjWarp2block_SwapWithChild(this, globalCtx); + this->dyna.actor.draw = ObjWarp2block_Draw; + } + func_80BA2600(this); + return; + } + + current = current->next; + } + + this->unk_174++; + if (this->unk_174 > 60) { + osSyncPrintf(VT_COL(RED, WHITE)); + osSyncPrintf("Error : 時のブロック(ワープ2)が対でセットされていません(%s %d)\n", "../z_obj_warp2block.c", 505); + osSyncPrintf(VT_RST); + Actor_Kill(&this->dyna.actor); + } +} + +void func_80BA2600(ObjWarp2block* this) { + this->actionFunc = func_80BA2610; +} + +void func_80BA2610(ObjWarp2block* this, GlobalContext* globalCtx) { + if ((func_80BA2304(this, globalCtx) != 0) && (this->unk_16C <= 0)) { + ObjWarp2block_Spawn(this, globalCtx); + this->unk_16C = 0xA0; + OnePointCutscene_Attention(globalCtx, &this->dyna.actor); + this->unk_170 = 0xC; + } + + if (this->unk_170 > 0) { + this->unk_170--; + if (this->unk_170 == 0) { + ObjWarp2block_SwapWithChild(this, globalCtx); + } + } + if (this->unk_16C == 0x32) { + func_80078884(NA_SE_SY_TRE_BOX_APPEAR); + } +} + +void ObjWarp2block_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjWarp2block* this = (ObjWarp2block*)thisx; + + this->actionFunc(this, globalCtx); + if (this->unk_16C > 0) { + this->unk_16C--; + } +} + +void ObjWarp2block_Draw(Actor* thisx, GlobalContext* globalCtx) { + Color_RGB8* sp44; + + sp44 = &sColors[thisx->home.rot.z & 7]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_obj_warp2block.c", 584); + func_80093D18(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_obj_warp2block.c", 588), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, sp44->r, sp44->g, sp44->b, 255); + gSPDisplayList(POLY_OPA_DISP++, gSongOfTimeBlockDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_obj_warp2block.c", 594); +} diff --git a/soh/src/overlays/actors/ovl_Obj_Warp2block/z_obj_warp2block.h b/soh/src/overlays/actors/ovl_Obj_Warp2block/z_obj_warp2block.h new file mode 100644 index 000000000..9b9dd991e --- /dev/null +++ b/soh/src/overlays/actors/ovl_Obj_Warp2block/z_obj_warp2block.h @@ -0,0 +1,23 @@ +#ifndef Z_OBJ_WARP2BLOCK_H +#define Z_OBJ_WARP2BLOCK_H + +#include "ultra64.h" +#include "global.h" + +struct ObjWarp2block; + +typedef void (*ObjWarp2blockActionFunc)(struct ObjWarp2block*, GlobalContext*); +typedef s32 (*ObjWarp2blockFunc168)(struct ObjWarp2block*, GlobalContext*); + +typedef struct ObjWarp2block { + /* 0x0000 */ DynaPolyActor dyna; + /* 0x0164 */ ObjWarp2blockActionFunc actionFunc; + /* 0x0168 */ ObjWarp2blockFunc168 func_168; + /* 0x016C */ s16 unk_16C; + /* 0x016E */ s16 unk_16E; + /* 0x0170 */ s16 unk_170; + /* 0x0172 */ u16 unk_172; + /* 0x0174 */ s16 unk_174; +} ObjWarp2block; // size = 0x0178 + +#endif diff --git a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c new file mode 100644 index 000000000..237f1e926 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.c @@ -0,0 +1,944 @@ +/* + * File: z_object_kankyo.c + * Overlay: ovl_Object_Kankyo + * Description: Environmental Effects + */ + +#include "z_object_kankyo.h" +#include "objects/object_demo_kekkai/object_demo_kekkai.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_spot02_objects/object_spot02_objects.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) + +void ObjectKankyo_Init(Actor* thisx, GlobalContext* globalCtx); +void ObjectKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ObjectKankyo_Update(Actor* thisx, GlobalContext* globalCtx); +void ObjectKankyo_Draw(Actor* thisx, GlobalContext* globalCtx); +void ObjectKankyo_Reset(void); + +void ObjectKankyo_SetupAction(ObjectKankyo* this, ObjectKankyoActionFunc func); +void ObjectKankyo_Fairies(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_SunGraveSparkInit(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_Snow(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_Lightning(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_InitBeams(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_WaitForSunGraveSparkObject(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_SunGraveSpark(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_WaitForBeamObject(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_Beams(ObjectKankyo* this, GlobalContext* globalCtx); + +void ObjectKankyo_DrawFairies(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_DrawSnow(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_DrawLightning(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_DrawSunGraveSpark(ObjectKankyo* this, GlobalContext* globalCtx); +void ObjectKankyo_DrawBeams(ObjectKankyo* this, GlobalContext* globalCtx); + +static void* sEffLightningTextures[] = { + gEffLightning1Tex, gEffLightning2Tex, gEffLightning3Tex, gEffLightning4Tex, + gEffLightning5Tex, gEffLightning6Tex, gEffLightning7Tex, gEffLightning8Tex, +}; + +static void* D_80BA5900[] = { + gEffSunGraveSpark1Tex, gEffSunGraveSpark2Tex, gEffSunGraveSpark3Tex, gEffSunGraveSpark4Tex, + gEffSunGraveSpark5Tex, gEffSunGraveSpark6Tex, gEffSunGraveSpark7Tex, gEffSunGraveSpark8Tex, +}; + +const ActorInit Object_Kankyo_InitVars = { + ACTOR_OBJECT_KANKYO, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ObjectKankyo), + (ActorFunc)ObjectKankyo_Init, + (ActorFunc)ObjectKankyo_Destroy, + (ActorFunc)ObjectKankyo_Update, + (ActorFunc)ObjectKankyo_Draw, + (ActorResetFunc)ObjectKankyo_Reset, +}; + +static u8 sIsSpawned = false; +static s16 sTrailingFairies = 0; + +void ObjectKankyo_SetupAction(ObjectKankyo* this, ObjectKankyoActionFunc action) { + this->actionFunc = action; +} + +void ObjectKankyo_Init(Actor* thisx, GlobalContext* globalCtx) { + ObjectKankyo* this = (ObjectKankyo*)thisx; + s32 pad; + s16 i; + + for (i = 0; i < ARRAY_COUNT(this->effects); i++) { + this->effects[i].state = 0; + } + + this->actor.room = -1; + switch (this->actor.params) { + case 0: + if (!sIsSpawned) { + ObjectKankyo_SetupAction(this, ObjectKankyo_Fairies); + sIsSpawned = true; + } else { + Actor_Kill(&this->actor); + } + break; + + case 3: + if (!sIsSpawned) { + ObjectKankyo_SetupAction(this, ObjectKankyo_Snow); + sIsSpawned = true; + } else { + Actor_Kill(&this->actor); + } + break; + + case 2: + ObjectKankyo_SetupAction(this, ObjectKankyo_Lightning); + break; + + case 4: + this->effects[0].alpha = 0; + this->effects[0].amplitude = 0.0f; + Actor_ChangeCategory(globalCtx, &globalCtx->actorCtx, &this->actor, ACTORCAT_ITEMACTION); + this->requiredObjectLoaded = false; + ObjectKankyo_SetupAction(this, ObjectKankyo_SunGraveSparkInit); + break; + + case 5: + this->effects[0].alpha = 0; + this->effects[0].amplitude = 0.0f; + + for (i = 0; i < 6; i++) { + this->effects[i].size = 0.1f; + } + + // Check which beams are disabled + if (Flags_GetEventChkInf(0xBB)) { + this->effects[0].size = 0.0f; + } + if (Flags_GetEventChkInf(0xBC)) { + this->effects[1].size = 0.0f; + } + if (Flags_GetEventChkInf(0xBD)) { + this->effects[2].size = 0.0f; + } + if (Flags_GetEventChkInf(0xBE)) { + this->effects[3].size = 0.0f; + } + if (Flags_GetEventChkInf(0xBF)) { + this->effects[4].size = 0.0f; + } + if (Flags_GetEventChkInf(0xAD)) { + this->effects[5].size = 0.0f; + } + + if (gSaveContext.cutsceneTrigger != 0) { + if (gSaveContext.entranceIndex == 0x0538) { + this->effects[0].size = 0.1f; + } + if (gSaveContext.entranceIndex == 0x053C) { + this->effects[1].size = 0.1f; + } + if (gSaveContext.entranceIndex == 0x0540) { + this->effects[2].size = 0.1f; + } + if (gSaveContext.entranceIndex == 0x0544) { + this->effects[3].size = 0.1f; + } + if (gSaveContext.entranceIndex == 0x0548) { + this->effects[4].size = 0.1f; + } + if (gSaveContext.entranceIndex == 0x054C) { + this->effects[5].size = 0.1f; + } + } + + this->requiredObjectLoaded = false; + ObjectKankyo_SetupAction(this, ObjectKankyo_InitBeams); + break; + } +} + +void ObjectKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx) { + Actor_Kill(thisx); +} + +void ObjectKankyo_Snow(ObjectKankyo* this, GlobalContext* globalCtx) { +} + +void ObjectKankyo_Fairies(ObjectKankyo* this, GlobalContext* globalCtx) { + static Vec3f sSoundPos = { 0.0f, 0.0f, 0.0f }; + Player* player; + f32 dist; + s32 playerMoved; + f32 dx; + f32 dy; + f32 dz; + f32 viewForwardsX; + f32 viewForwardsY; + f32 viewForwardsZ; + f32 maxDist; + f32 baseX; + f32 baseY; + f32 baseZ; + Vec3f vec1 = { 0.0f, 0.0f, 0.0f }; + Vec3f vec2 = { 0.0f, 0.0f, 0.0f }; + f32 random; + s16 i; + Vec3f viewForwards; + + player = GET_PLAYER(globalCtx); + + if (globalCtx->sceneNum == SCENE_SPOT04 && gSaveContext.sceneSetupIndex == 7) { + dist = Math3D_Vec3f_DistXYZ(&this->prevEyePos, &globalCtx->view.eye); + + this->prevEyePos.x = globalCtx->view.eye.x; + this->prevEyePos.y = globalCtx->view.eye.y; + this->prevEyePos.z = globalCtx->view.eye.z; + + dist /= 30.0f; + if (dist > 1.0f) { + dist = 1.0f; + } + + func_800F436C(&sSoundPos, NA_SE_EV_NAVY_FLY - SFX_FLAG, (0.4f * dist) + 0.6f); + switch (globalCtx->csCtx.frames) { + case 473: + func_800788CC(NA_SE_VO_NA_HELLO_3); + break; + + case 583: + func_800F4524(&D_801333D4, NA_SE_VO_NA_HELLO_2, 32); + break; + + case 763: + func_80078884(NA_SE_EV_NAVY_CRASH - SFX_FLAG); + break; + + case 771: + func_80078884(NA_SE_VO_RT_THROW); + break; + } + } + + if (globalCtx->envCtx.unk_EE[3] < 64 && + (gSaveContext.entranceIndex != 0x00EE || gSaveContext.sceneSetupIndex != 4 || globalCtx->envCtx.unk_EE[3])) { + globalCtx->envCtx.unk_EE[3] += 16; + } + + for (i = 0; i < globalCtx->envCtx.unk_EE[3]; i++) { + // spawn in front of the camera + dx = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + dy = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + dz = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); + + viewForwards.x = dx / dist; + viewForwards.y = dy / dist; + viewForwards.z = dz / dist; + + viewForwardsX = viewForwards.x; + viewForwardsY = viewForwards.y; + viewForwardsZ = viewForwards.z; + + switch (this->effects[i].state) { + case 0: // init + this->effects[i].base.x = globalCtx->view.eye.x + viewForwardsX * 80.0f; + this->effects[i].base.y = globalCtx->view.eye.y + viewForwardsY * 80.0f; + this->effects[i].base.z = globalCtx->view.eye.z + viewForwardsZ * 80.0f; + + this->effects[i].pos.x = (Rand_ZeroOne() - 0.5f) * 160.0f; + this->effects[i].pos.y = 30.0f; + this->effects[i].pos.z = (Rand_ZeroOne() - 0.5f) * 160.0f; + this->effects[i].targetSpeed = Rand_ZeroOne() * 1.6f + 0.5f; + this->effects[i].alpha = 0; + this->effects[i].alphaTimer = Rand_ZeroOne() * 65535.0f; + this->effects[i].size = 0.1f; + this->effects[i].dirPhase.x = Rand_ZeroOne() * 360.0f; + this->effects[i].dirPhase.y = Rand_ZeroOne() * 360.0f; + this->effects[i].dirPhase.z = Rand_ZeroOne() * 360.0f; + this->effects[i].state++; + this->effects[i].timer = 0; + break; + + case 1: // blinking fairies / inactive fairy trails + case 2: // fairy trails + this->effects[i].alphaTimer++; + baseX = globalCtx->view.eye.x + viewForwards.x * 80.0f; + baseY = globalCtx->view.eye.y + viewForwards.y * 80.0f; + baseZ = globalCtx->view.eye.z + viewForwards.z * 80.0f; + + this->effects[i].prevPos.x = this->effects[i].pos.x; + this->effects[i].prevPos.y = this->effects[i].pos.y; + this->effects[i].prevPos.z = this->effects[i].pos.z; + + playerMoved = true; + // y velocity is set to -4 when the player is on the ground + if (player->actor.velocity.x + player->actor.velocity.y + player->actor.velocity.z == -4.0f) { + playerMoved = false; + this->effects[i].timer++; + } else { + this->effects[i].timer = 0; + } + + if (this->effects[i].state == 1) { + // the first 32 fairies are invisible until the player stands still + if (i < 32 && !playerMoved && this->effects[i].timer > 256) { + this->effects[i].timer = 0; + if (Rand_ZeroOne() < 0.5f) { + this->effects[i].angleVel = (s16)(Rand_ZeroOne() * 200.0f) + 200; + } else { + this->effects[i].angleVel = -((s16)(Rand_ZeroOne() * 200.0f) + 200); + } + + this->effects[i].flightRadius = (s16)(Rand_ZeroOne() * 50.0f) + 15; + + // uniformly scales the length and height of the wave that the lead fairy flies in + // lower numbers have a larger amplitude and period + this->effects[i].amplitude = (Rand_ZeroOne() * 10.0f + 10.0f) * 0.01f; + + random = Rand_ZeroOne(); + if (random < 0.2f) { + sTrailingFairies = 1; + } else if (random < 0.2f) { + // unreachable + sTrailingFairies = 3; + } else if (random < 0.4f) { + sTrailingFairies = 7; + } else { + sTrailingFairies = 15; + } + + if ((i & sTrailingFairies) == 0) { + this->effects[i].pos.y = 0.0f; + } + + this->effects[i].state = 2; + this->effects[i].targetSpeed = 0.0f; + } + + Math_SmoothStepToF(&this->effects[i].size, 0.1f, 0.10f, 0.001f, 0.00001f); + Math_SmoothStepToF(&this->effects[i].speed, this->effects[i].targetSpeed, 0.5f, 0.2f, 0.02f); + + this->effects[i].pos.x += sinf(this->effects[i].dirPhase.x) * this->effects[i].speed; + this->effects[i].pos.y += sinf(this->effects[i].dirPhase.y) * this->effects[i].speed; + this->effects[i].pos.z += sinf(this->effects[i].dirPhase.z) * this->effects[i].speed; + + switch ((i >> 1) & 3) { + case 0: + this->effects[i].dirPhase.x += 0.008f; + this->effects[i].dirPhase.y += 0.05f * Rand_ZeroOne(); + this->effects[i].dirPhase.z += 0.015f; + break; + + case 1: + this->effects[i].dirPhase.x += 0.01f * Rand_ZeroOne(); + this->effects[i].dirPhase.y += 0.05f * Rand_ZeroOne(); + this->effects[i].dirPhase.z += 0.005f * Rand_ZeroOne(); + break; + + case 2: + this->effects[i].dirPhase.x += 0.01f * Rand_ZeroOne(); + this->effects[i].dirPhase.y += 0.4f * Rand_ZeroOne(); + this->effects[i].dirPhase.z += 0.004f * Rand_ZeroOne(); + break; + + case 3: + this->effects[i].dirPhase.x += 0.01 * Rand_ZeroOne(); + this->effects[i].dirPhase.y += 0.08f * Rand_ZeroOne(); + this->effects[i].dirPhase.z += 0.05f * Rand_ZeroOne(); + break; + } + } else if (this->effects[i].state == 2) { + // scatter when the player moves or after a long enough time + if (playerMoved || this->effects[i].timer > 1280) { + this->effects[i].timer = 0; + this->effects[i].state = 1; + this->effects[i].speed = 1.5f; + this->effects[i].targetSpeed = Rand_ZeroOne() * 1.6f + 0.5f; + } + + if ((i & sTrailingFairies) == 0) { // leader fairy + Math_SmoothStepToF(&this->effects[i].size, 0.25f, 0.1f, 0.001f, 0.00001f); + + // move the center of the flight path to player's position + Math_SmoothStepToF(&this->effects[i].base.x, player->actor.world.pos.x, 0.5f, 1.0f, 0.2f); + Math_SmoothStepToF(&this->effects[i].base.y, player->actor.world.pos.y + 50.0f, 0.5f, 1.0f, + 0.2f); + Math_SmoothStepToF(&this->effects[i].base.z, player->actor.world.pos.z, 0.5f, 1.0f, 0.2f); + + // results unused + Math_SmoothStepToF(&this->effects[i].pos.x, + Math_SinS(this->effects[i].angle - 0x8000) * this->effects[i].flightRadius, + 0.5f, 2.0f, 0.2f); + Math_SmoothStepToF(&this->effects[i].pos.z, + Math_CosS(this->effects[i].angle - 0x8000) * this->effects[i].flightRadius, + 0.5f, 2.0f, 0.2f); + + // the lead fairy's y position approximately follows a sine wave with `amplitude` as angular + // frequency and `1 / amplitude` as amplitude + this->effects[i].angle += this->effects[i].angleVel; + this->effects[i].pos.y += sinf(this->effects[i].dirPhase.y); + + this->effects[i].dirPhase.x += 0.2f * Rand_ZeroOne(); + this->effects[i].dirPhase.y += this->effects[i].amplitude; + this->effects[i].dirPhase.z += 0.1f * Rand_ZeroOne(); + + // circle around the player + this->effects[i].pos.x = + Math_SinS(this->effects[i].angle - 0x8000) * this->effects[i].flightRadius; + this->effects[i].pos.z = + Math_CosS(this->effects[i].angle - 0x8000) * this->effects[i].flightRadius; + } else { // trailing fairy + Math_SmoothStepToF(&this->effects[i].size, 0.1f, 0.10f, 0.001f, 0.00001f); + Math_SmoothStepToF(&this->effects[i].speed, 1.5f, 0.5f, 0.1f, 0.0002f); + + // follow previous fairy, translate their position to be relative to our home + this->effects[i].pos.x = + this->effects[i - 1].prevPos.x + (this->effects[i - 1].base.x - this->effects[i].base.x); + this->effects[i].pos.y = + this->effects[i - 1].prevPos.y + (this->effects[i - 1].base.y - this->effects[i].base.y); + this->effects[i].pos.z = + this->effects[i - 1].prevPos.z + (this->effects[i - 1].base.z - this->effects[i].base.z); + } + } + + if (this->effects[i].state != 2) { + maxDist = 130.0f; + if (this->effects[i].base.x + this->effects[i].pos.x - baseX > maxDist || + this->effects[i].base.x + this->effects[i].pos.x - baseX < -maxDist || + this->effects[i].base.y + this->effects[i].pos.y - baseY > maxDist || + this->effects[i].base.y + this->effects[i].pos.y - baseY < -maxDist || + this->effects[i].base.z + this->effects[i].pos.z - baseZ > maxDist || + this->effects[i].base.z + this->effects[i].pos.z - baseZ < -maxDist) { + + // when a fairy moves off screen, wrap around to the other side + if (this->effects[i].base.x + this->effects[i].pos.x - baseX > maxDist) { + this->effects[i].base.x = baseX - maxDist; + this->effects[i].pos.x = 0.0f; + } + if (this->effects[i].base.x + this->effects[i].pos.x - baseX < -maxDist) { + this->effects[i].base.x = baseX + maxDist; + this->effects[i].pos.x = 0.0f; + } + if (this->effects[i].base.y + this->effects[i].pos.y - baseY > 50.0f) { + this->effects[i].base.y = baseY - 50.0f; + this->effects[i].pos.y = 0.0f; + } + if (this->effects[i].base.y + this->effects[i].pos.y - baseY < -50.0f) { + this->effects[i].base.y = baseY + 50.0f; + this->effects[i].pos.y = 0.0f; + } + if (this->effects[i].base.z + this->effects[i].pos.z - baseZ > maxDist) { + this->effects[i].base.z = baseZ - maxDist; + this->effects[i].pos.z = 0.0f; + } + if (this->effects[i].base.z + this->effects[i].pos.z - baseZ < -maxDist) { + this->effects[i].base.z = baseZ + maxDist; + this->effects[i].pos.z = 0.0f; + } + } + } + break; + + case 3: // reset, never reached + this->effects[i].state = 0; + break; + } + } +} + +void ObjectKankyo_Update(Actor* thisx, GlobalContext* globalCtx) { + ObjectKankyo* this = (ObjectKankyo*)thisx; + + this->actionFunc(this, globalCtx); +} + +void ObjectKankyo_Draw(Actor* thisx, GlobalContext* globalCtx) { + ObjectKankyo* this = (ObjectKankyo*)thisx; + + switch (this->actor.params) { + case 0: + ObjectKankyo_DrawFairies(this, globalCtx); + break; + + case 2: + ObjectKankyo_DrawLightning(this, globalCtx); + break; + + case 3: + ObjectKankyo_DrawSnow(this, globalCtx); + break; + + case 4: + ObjectKankyo_DrawSunGraveSpark(this, globalCtx); + break; + + case 5: + ObjectKankyo_DrawBeams(this, globalCtx); + break; + } +} + +void ObjectKankyo_DrawFairies(ObjectKankyo* this2, GlobalContext* globalCtx2) { + ObjectKankyo* this = this2; + GlobalContext* globalCtx = globalCtx2; + f32 alphaScale; + Vec3f vec1 = { 0.0f, 0.0f, 0.0f }; + Vec3f vec2 = { 0.0f, 0.0f, 0.0f }; + s16 i; + + if (!(globalCtx->cameraPtrs[0]->unk_14C & 0x100)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 807); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x14); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gSunTex)); + gSPDisplayList(POLY_XLU_DISP++, gKokiriDustMoteTextureLoadDL); + + for (i = 0; i < globalCtx->envCtx.unk_EE[3]; i++) { + Matrix_Translate(this->effects[i].base.x + this->effects[i].pos.x, + this->effects[i].base.y + this->effects[i].pos.y, + this->effects[i].base.z + this->effects[i].pos.z, MTXMODE_NEW); + + // scale when fading in or out + alphaScale = this->effects[i].alpha / 50.0f; + if (alphaScale > 1.0f) { + alphaScale = 1.0f; + } + + Matrix_Scale(this->effects[i].size * alphaScale, this->effects[i].size * alphaScale, + this->effects[i].size * alphaScale, MTXMODE_APPLY); + if (i < 32) { + if (this->effects[i].state != 2) { + if (this->effects[i].alpha > 0) { + this->effects[i].alpha--; + } + } else { + if (this->effects[i].alpha < 100) { + this->effects[i].alpha++; + } + } + } else { + if (this->effects[i].state != 2) { + if ((this->effects[i].alphaTimer & 0x1F) < 16) { + if (this->effects[i].alpha < 235) { + this->effects[i].alpha += 20; + } + } else { + if (this->effects[i].alpha > 20) { + this->effects[i].alpha -= 20; + } + } + } else { + // unreachable + if ((this->effects[i].alphaTimer & 0xF) < 8) { + if (this->effects[i].alpha < 255) { + this->effects[i].alpha += 100; + } + } else { + if (this->effects[i].alpha > 10) { + this->effects[i].alpha -= 10; + } + } + } + } + + gDPPipeSync(POLY_XLU_DISP++); + + switch (i & 1) { + case 0: + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, this->effects[i].alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 250, 180, 0, this->effects[i].alpha); + break; + + case 1: + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, this->effects[i].alpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, this->effects[i].alpha); + break; + } + + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + Matrix_RotateZ(DEG_TO_RAD(globalCtx->state.frames * 20.0f), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 913), G_MTX_LOAD); + gSPDisplayList(POLY_XLU_DISP++, gKokiriDustMoteDL); + } + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 922); + } +} + +void ObjectKankyo_DrawSnow(ObjectKankyo* this2, GlobalContext* globalCtx2) { + ObjectKankyo* this = this2; + GlobalContext* globalCtx = globalCtx2; + f32 dist; + f32 dx; + f32 dy; + f32 dz; + f32 maxDist; + f32 temp; + f32 baseX; + f32 baseY; + f32 baseZ; + Vec3f vec1 = { 0.0f, 0.0f, 0.0f }; + Vec3f vec2 = { 0.0f, 0.0f, 0.0f }; + s16 i; + s32 pad; + s32 pad2; + + if (!(globalCtx->cameraPtrs[0]->unk_14C & 0x100)) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 958); + if (globalCtx->envCtx.unk_EE[2] < globalCtx->envCtx.unk_EE[3]) { + if (globalCtx->state.frames % 16 == 0) { + globalCtx->envCtx.unk_EE[2] += 2; + } + } else if (globalCtx->envCtx.unk_EE[2] > globalCtx->envCtx.unk_EE[3]) { + if (globalCtx->state.frames % 16 == 0) { + globalCtx->envCtx.unk_EE[2] -= 2; + } + } + + for (i = 0; i < globalCtx->envCtx.unk_EE[2]; i++) { + switch (this->effects[i].state) { + case 0: + // spawn in front of the camera + dx = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + dy = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + dz = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); + + // fake + temp = dz / dist; + this->effects[i].base.x = globalCtx->view.eye.x + dx / dist * 80.0f; + this->effects[i].base.y = globalCtx->view.eye.y + dy / dist * 80.0f; + this->effects[i].base.z = globalCtx->view.eye.z + temp * 80.0f; + + this->effects[i].pos.x = (Rand_ZeroOne() - 0.5f) * 160.0f; + this->effects[i].pos.y = 80.0f; + this->effects[i].pos.z = (Rand_ZeroOne() - 0.5f) * 160.0f; + if (this->effects[i].base.y + this->effects[i].pos.y < 50.0f) { + this->effects[i].base.y = 50.0f; + } + this->effects[i].speed = Rand_ZeroOne() * 5.0f + 0.5f; + this->effects[i].dirPhase.x = Rand_ZeroOne() * 360.0f; + this->effects[i].dirPhase.z = Rand_ZeroOne() * 360.0f; + this->effects[i].state++; + break; + + case 1: + dx = globalCtx->view.lookAt.x - globalCtx->view.eye.x; + dy = globalCtx->view.lookAt.y - globalCtx->view.eye.y; + dz = globalCtx->view.lookAt.z - globalCtx->view.eye.z; + dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); + + baseX = globalCtx->view.eye.x + dx / dist * 80.0f; + baseY = globalCtx->view.eye.y + dy / dist * 80.0f; + baseZ = globalCtx->view.eye.z + dz / dist * 80.0f; + + this->effects[i].dirPhase.x += 0.049999997f * Rand_ZeroOne(); + this->effects[i].dirPhase.z += 0.049999997f * Rand_ZeroOne(); + this->effects[i].pos.x += sinf(this->effects[i].dirPhase.x * 0.01f); + this->effects[i].pos.z += cosf(this->effects[i].dirPhase.z * 0.01f); + this->effects[i].pos.y += -this->effects[i].speed; + + if (this->effects[i].base.y + this->effects[i].pos.y < this->actor.world.pos.y || + this->effects[i].base.y + this->effects[i].pos.y < globalCtx->view.eye.y - 150.0f) { + this->effects[i].state++; + } + + maxDist = 80; + if (this->effects[i].base.x + this->effects[i].pos.x - baseX > maxDist || + this->effects[i].base.x + this->effects[i].pos.x - baseX < -maxDist || + this->effects[i].base.y + this->effects[i].pos.y - baseY > maxDist || + this->effects[i].base.y + this->effects[i].pos.y - baseY < -maxDist || + this->effects[i].base.z + this->effects[i].pos.z - baseZ > maxDist || + this->effects[i].base.z + this->effects[i].pos.z - baseZ < -maxDist) { + + // when off screen, wrap around to the other side + if (this->effects[i].base.x + this->effects[i].pos.x - baseX > maxDist) { + this->effects[i].base.x = baseX - maxDist; + this->effects[i].pos.x = 0.0f; + } + if (this->effects[i].base.x + this->effects[i].pos.x - baseX < -maxDist) { + this->effects[i].base.x = baseX + maxDist; + this->effects[i].pos.x = 0.0f; + } + if (this->effects[i].base.z + this->effects[i].pos.z - baseZ > maxDist) { + this->effects[i].base.z = baseZ - maxDist; + this->effects[i].pos.z = 0.0f; + } + if (this->effects[i].base.z + this->effects[i].pos.z - baseZ < -maxDist) { + this->effects[i].base.z = baseZ + maxDist; + this->effects[i].pos.z = 0.0f; + } + } + break; + + case 2: + this->effects[i].state = 0; + break; + } + + if (1) {} + if (1) {} + Matrix_Translate(this->effects[i].base.x + this->effects[i].pos.x, + this->effects[i].base.y + this->effects[i].pos.y, + this->effects[i].base.z + this->effects[i].pos.z, MTXMODE_NEW); + Matrix_Scale(0.05f, 0.05f, 0.05f, MTXMODE_APPLY); + gDPPipeSync(POLY_XLU_DISP++); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 200, 200, 180); + gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 200, 180); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1107), + G_MTX_LOAD); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(gDust5Tex)); + + func_80094C50(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_MODELVIEW | G_MTX_NOPUSH | G_MTX_MUL); + + gDPPipeSync(POLY_XLU_DISP++); + + gSPDisplayList(POLY_XLU_DISP++, gEffDustDL); + + gDPPipeSync(POLY_XLU_DISP++); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1127); + } +} + +void ObjectKankyo_Lightning(ObjectKankyo* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != 0 && globalCtx->csCtx.npcActions[0] != NULL) { + switch (this->effects[0].state) { + case 0: + this->effects[0].timer = 0; + if (globalCtx->csCtx.npcActions[0]->action == 2) { + this->effects[0].state++; + } + break; + + case 1: + if (++this->effects[0].timer >= 7) { + this->effects[0].state++; + } + break; + + case 2: + if (globalCtx->csCtx.npcActions[0]->action == 1) { + this->effects[0].state = 0; + } + break; + } + } +} + +void ObjectKankyo_DrawLightning(ObjectKankyo* this, GlobalContext* globalCtx) { + s32 pad; + s32 pad2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1182); + + if (this->effects[0].state == 1) { + Matrix_Translate(globalCtx->csCtx.npcActions[0]->startPos.x, globalCtx->csCtx.npcActions[0]->startPos.y, + globalCtx->csCtx.npcActions[0]->startPos.z, MTXMODE_NEW); + Matrix_RotateX(DEG_TO_RAD(20.0f), MTXMODE_APPLY); + Matrix_RotateZ(DEG_TO_RAD(20.0f), MTXMODE_APPLY); + Matrix_Scale(2.0f, 5.0f, 2.0f, MTXMODE_APPLY); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, 128); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, 128); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1213), G_MTX_LOAD); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sEffLightningTextures[this->effects[0].timer])); + func_80094C50(globalCtx->state.gfxCtx); + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_MODELVIEW | G_MTX_NOPUSH | G_MTX_MUL); + gDPPipeSync(POLY_XLU_DISP++); + gSPDisplayList(POLY_XLU_DISP++, gEffLightningDL); + gDPPipeSync(POLY_XLU_DISP++); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1233); +} + +void ObjectKankyo_SunGraveSparkInit(ObjectKankyo* this, GlobalContext* globalCtx) { + s32 objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_SPOT02_OBJECTS); + + if (objBankIndex < 0) { + ASSERT(0, "0", "../z_object_kankyo.c", 1251); + } else { + this->requiredObjBankIndex = objBankIndex; + } + ObjectKankyo_SetupAction(this, ObjectKankyo_WaitForSunGraveSparkObject); +} + +void ObjectKankyo_WaitForSunGraveSparkObject(ObjectKankyo* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->requiredObjBankIndex)) { + this->requiredObjectLoaded = true; + this->effects[0].alpha = 0; + this->actor.objBankIndex = this->requiredObjBankIndex; + this->effects[0].size = 7.0f; + ObjectKankyo_SetupAction(this, ObjectKankyo_SunGraveSpark); + } +} + +void ObjectKankyo_SunGraveSpark(ObjectKankyo* this, GlobalContext* globalCtx) { + if (globalCtx->csCtx.state != 0) { + if (globalCtx->csCtx.npcActions[1] != NULL && globalCtx->csCtx.npcActions[1]->action == 2) { + Audio_PlayActorSound2(&this->actor, NA_SE_EN_BIRI_SPARK - SFX_FLAG); + if ((s16)this->effects[0].alpha + 20 > 255) { + this->effects[0].alpha = 255; + } else { + this->effects[0].alpha += 20; + } + Math_SmoothStepToF(&this->effects[0].size, 1.8f, 0.5f, 0.28f, 0.01f); + } + } +} + +void ObjectKankyo_DrawSunGraveSpark(ObjectKankyo* this2, GlobalContext* globalCtx2) { + ObjectKankyo* this = this2; + GlobalContext* globalCtx = globalCtx2; + Vec3f start; + Vec3f end; + f32 weight; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1324); + if (globalCtx->csCtx.state != 0) { + if (globalCtx->csCtx.npcActions[1] != NULL && globalCtx->csCtx.npcActions[1]->action == 2 && + this->requiredObjectLoaded) { + // apparently, light waves with larger amplitudes look brighter, so the name 'amplitude' kind of works here + if (this->effects[0].state == 0) { + this->effects[0].amplitude += 1.0f / 7.0f; + if (this->effects[0].amplitude >= 1.0f) { + this->effects[0].amplitude = 1.0f; + this->effects[0].state++; + } + } else { + this->effects[0].amplitude -= 1.0f / 7.0f; + if (this->effects[0].amplitude <= 0.1f) { + this->effects[0].amplitude = 0.0f; + this->effects[0].state = 0; + } + } + + if (++this->effects[0].timer > 7) { + this->effects[0].timer = 0; + } + + start.x = globalCtx->csCtx.npcActions[1]->startPos.x; + start.y = globalCtx->csCtx.npcActions[1]->startPos.y; + start.z = globalCtx->csCtx.npcActions[1]->startPos.z; + + end.x = globalCtx->csCtx.npcActions[1]->endPos.x; + end.y = globalCtx->csCtx.npcActions[1]->endPos.y; + end.z = globalCtx->csCtx.npcActions[1]->endPos.z; + + weight = Environment_LerpWeight(globalCtx->csCtx.npcActions[1]->endFrame, + globalCtx->csCtx.npcActions[1]->startFrame, globalCtx->csCtx.frames); + Matrix_Translate((end.x - start.x) * weight + start.x, (end.y - start.y) * weight + start.y, + (end.z - start.z) * weight + start.z, MTXMODE_NEW); + Matrix_Scale(this->effects[0].size, this->effects[0].size, this->effects[0].size, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (u8)(105 * this->effects[0].amplitude) + 150, 255, + (u8)(105 * this->effects[0].amplitude) + 150, this->effects[0].alpha); + gDPSetEnvColor(POLY_XLU_DISP++, (u8)(155 * this->effects[0].amplitude) + 100, + (u8)(255 * this->effects[0].amplitude), 255 - (u8)(255 * this->effects[0].amplitude), + this->effects[0].alpha); + + Matrix_Mult(&globalCtx->billboardMtxF, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1416), + G_MTX_LOAD); + + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(D_80BA5900[this->effects[0].timer])); + gDPPipeSync(POLY_XLU_DISP++); + + gSPDisplayList(POLY_XLU_DISP++, object_spot02_objects_DL_009620); + gDPPipeSync(POLY_XLU_DISP++); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1432); +} + +void ObjectKankyo_InitBeams(ObjectKankyo* this, GlobalContext* globalCtx) { + s32 objectIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_DEMO_KEKKAI); + + if (objectIndex < 0) { + ASSERT(0, "0", "../z_object_kankyo.c", 1449); + } else { + this->requiredObjBankIndex = objectIndex; + } + ObjectKankyo_SetupAction(this, ObjectKankyo_WaitForBeamObject); +} + +void ObjectKankyo_WaitForBeamObject(ObjectKankyo* this, GlobalContext* globalCtx) { + if (Object_IsLoaded(&globalCtx->objectCtx, this->requiredObjBankIndex)) { + this->requiredObjectLoaded = true; + this->actor.objBankIndex = this->requiredObjBankIndex; + ObjectKankyo_SetupAction(this, ObjectKankyo_Beams); + } +} + +void ObjectKankyo_Beams(ObjectKankyo* this, GlobalContext* globalCtx) { + u8 i; + + if (globalCtx->csCtx.state != 0) { + for (i = 0; i < 6; i++) { + if (globalCtx->csCtx.npcActions[i + 1] != NULL && globalCtx->csCtx.npcActions[i + 1]->action == 2) { + if (this->effects[i].size == 0.1f) { + func_800F3F3C(11); + } + Math_ApproachZeroF(&this->effects[i].size, 0.1f, 0.1f); + } + } + } +} + +void ObjectKankyo_DrawBeams(ObjectKankyo* this2, GlobalContext* globalCtx2) { + static Color_RGB8 sBeamPrimColors[] = { + { 255, 255, 170 }, { 170, 255, 255 }, { 255, 170, 255 }, + { 255, 255, 170 }, { 255, 255, 170 }, { 255, 255, 170 }, + }; + static Color_RGB8 sBeamEnvColors[] = { + { 0, 200, 0 }, { 0, 50, 255 }, { 100, 0, 200 }, { 200, 0, 0 }, { 200, 255, 0 }, { 255, 120, 0 }, + }; + ObjectKankyo* this = this2; + GlobalContext* globalCtx = globalCtx2; + s16 i; + f32 beamX[] = { 430.0f, 860.0f, 430.0f, -426.0f, -862.0f, -440.0f }; + f32 beamY[] = { 551.0f, 551.0f, 551.0f, 554.0f, 551.0f, 547.0f }; + f32 beamZ[] = { -96.0f, -840.0f, -1585.0f, -1578.0f, -840.0f, -78.0f }; + f32 beamYaw[] = { 29.9f, 90.0f, 150.0f, 30.0f, 90.0f, -30.1f }; + f32 beamPitch[] = { 103.4f, 103.8f, 103.6f, -103.4f, -103.5f, 103.5f }; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1539); + + if (this->requiredObjectLoaded) { + for (i = 0; i < 6; i++) { + if (this->effects[i].size > 0.001f) { + Matrix_Translate(beamX[i], beamY[i], beamZ[i], MTXMODE_NEW); + Matrix_RotateY(DEG_TO_RAD(beamYaw[i]), MTXMODE_APPLY); + Matrix_RotateX(DEG_TO_RAD(beamPitch[i]), MTXMODE_APPLY); + Matrix_Scale(this->effects[i].size, 0.1f, this->effects[i].size, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 128, sBeamPrimColors[i].r, sBeamPrimColors[i].g, + sBeamPrimColors[i].b, 128); + gDPSetEnvColor(POLY_XLU_DISP++, sBeamEnvColors[i].r, sBeamEnvColors[i].g, sBeamEnvColors[i].b, 128); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1586), + G_MTX_LOAD); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->state.frames * 5, + globalCtx->state.frames * 10, 32, 64, 1, globalCtx->state.frames * 5, + globalCtx->state.frames * 10, 32, 64)); + gSPDisplayList(POLY_XLU_DISP++, gDemoKekkaiDL_005FF0); + } + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_object_kankyo.c", 1607); +} + +void ObjectKankyo_Reset(void) { + sIsSpawned = false; + sTrailingFairies = 0; +} \ No newline at end of file diff --git a/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h new file mode 100644 index 000000000..9b9a69975 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Object_Kankyo/z_object_kankyo.h @@ -0,0 +1,38 @@ +#ifndef Z_OBJECT_KANKYO_H +#define Z_OBJECT_KANKYO_H + +#include "ultra64.h" +#include "global.h" + +struct ObjectKankyo; + +typedef void (*ObjectKankyoActionFunc)(struct ObjectKankyo*, GlobalContext*); + +typedef struct ObjectKankyoEffect { + /* 0x00 */ u8 state; + /* 0x04 */ Vec3f pos; // relative to base + /* 0x10 */ Vec3f prevPos; + /* 0x1C */ Vec3f base; + /* 0x28 */ Vec3f dirPhase; // input to sin/cos for movement direction + /* 0x34 */ f32 speed; + /* 0x38 */ f32 targetSpeed; + /* 0x3C */ u16 alphaTimer; + /* 0x3E */ u16 angle; + /* 0x40 */ u8 alpha; + /* 0x44 */ f32 size; + /* 0x48 */ u16 angleVel; + /* 0x4A */ u16 flightRadius; + /* 0x4C */ f32 amplitude; + /* 0x50 */ u16 timer; +} ObjectKankyoEffect; // size = 0x54 + +typedef struct ObjectKankyo { + /* 0x0000 */ Actor actor; + /* 0x014C */ ObjectKankyoEffect effects[64]; + /* 0x164C */ Vec3f prevEyePos; + /* 0x1658 */ u8 requiredObjBankIndex; + /* 0x1659 */ u8 requiredObjectLoaded; + /* 0x165C */ ObjectKankyoActionFunc actionFunc; +} ObjectKankyo; // size = 0x1660 + +#endif diff --git a/soh/src/overlays/actors/ovl_Oceff_Spot/z_oceff_spot.c b/soh/src/overlays/actors/ovl_Oceff_Spot/z_oceff_spot.c new file mode 100644 index 000000000..9f588f190 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Spot/z_oceff_spot.c @@ -0,0 +1,165 @@ +/* + * File: z_oceff_spot.c + * Overlay: ovl_Oceff_Spot + * Description: Sun's Song Effect + */ + +#include "z_oceff_spot.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void OceffSpot_Init(Actor* thisx, GlobalContext* globalCtx); +void OceffSpot_Destroy(Actor* thisx, GlobalContext* globalCtx); +void OceffSpot_Update(Actor* thisx, GlobalContext* globalCtx); +void OceffSpot_Draw(Actor* thisx, GlobalContext* globalCtx); + +void OceffSpot_GrowCylinder(OceffSpot* this, GlobalContext* globalCtx); + +const ActorInit Oceff_Spot_InitVars = { + ACTOR_OCEFF_SPOT, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(OceffSpot), + (ActorFunc)OceffSpot_Init, + (ActorFunc)OceffSpot_Destroy, + (ActorFunc)OceffSpot_Update, + (ActorFunc)OceffSpot_Draw, + NULL, +}; + +#include "overlays/ovl_Oceff_Spot/ovl_Oceff_Spot.h" + +static InitChainEntry sInitChain[] = { + ICHAIN_VEC3F_DIV1000(scale, 0, ICHAIN_CONTINUE), + ICHAIN_F32(uncullZoneForward, 1500, ICHAIN_STOP), +}; + +void OceffSpot_SetupAction(OceffSpot* this, OceffSpotActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void OceffSpot_Init(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + OceffSpot* this = (OceffSpot*)thisx; + + Actor_ProcessInitChain(&this->actor, sInitChain); + OceffSpot_SetupAction(this, OceffSpot_GrowCylinder); + + Lights_PointNoGlowSetInfo(&this->lightInfo1, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0); + this->lightNode1 = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo1); + + Lights_PointNoGlowSetInfo(&this->lightInfo2, this->actor.world.pos.x, this->actor.world.pos.y, + this->actor.world.pos.z, 0, 0, 0, 0); + this->lightNode2 = LightContext_InsertLight(globalCtx, &globalCtx->lightCtx, &this->lightInfo2); + if (YREG(15)) { + this->actor.scale.y = 2.4f; + } else { + this->actor.scale.y = 0.3f; + } + + this->unk_174 = 0.0f; +} + +void OceffSpot_Destroy(Actor* thisx, GlobalContext* globalCtx) { + s32 pad; + OceffSpot* this = (OceffSpot*)thisx; + Player* player = GET_PLAYER(globalCtx); + + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode1); + LightContext_RemoveLight(globalCtx, &globalCtx->lightCtx, this->lightNode2); + func_800876C8(globalCtx); + if ((gSaveContext.nayrusLoveTimer != 0) && (globalCtx->actorCtx.actorLists[ACTORCAT_PLAYER].length != 0)) { + player->stateFlags3 |= 0x40; + } +} + +void OceffSpot_End(OceffSpot* this, GlobalContext* globalCtx) { + if (this->unk_174 > 0.0f) { + this->unk_174 -= 0.05f; + } else { + Actor_Kill(&this->actor); + if (gTimeIncrement != 400 && globalCtx->msgCtx.unk_E40E == 0 && (gSaveContext.eventInf[0] & 0xF) != 1) { + if (globalCtx->msgCtx.ocarinaAction != OCARINA_ACTION_CHECK_NOWARP_DONE || + globalCtx->msgCtx.ocarinaMode != OCARINA_MODE_08) { + gSaveContext.sunsSongState = SUNSSONG_START; + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Sun's Song Flag" + osSyncPrintf("z_oceff_spot 太陽の歌フラグ\n"); + osSyncPrintf(VT_RST); + } + } else { + globalCtx->msgCtx.ocarinaMode = OCARINA_MODE_04; + osSyncPrintf(VT_FGCOL(YELLOW)); + // "Ocarina End" + osSyncPrintf("z_oceff_spot オカリナ終了\n"); + osSyncPrintf(VT_RST); + } + } +} + +void OceffSpot_Wait(OceffSpot* this, GlobalContext* globalCtx) { + if (this->timer > 0) { + this->timer--; + } else { + OceffSpot_SetupAction(this, OceffSpot_End); + } +} + +void OceffSpot_GrowCylinder(OceffSpot* this, GlobalContext* globalCtx) { + if (this->unk_174 < 1.0f) { + this->unk_174 += 0.05f; + } else { + OceffSpot_SetupAction(this, OceffSpot_Wait); + this->timer = 60; + } +} + +void OceffSpot_Update(Actor* thisx, GlobalContext* globalCtx) { + OceffSpot* this = (OceffSpot*)thisx; + s32 pad; + Player* player = GET_PLAYER(globalCtx); + f32 temp; + + temp = (1.0f - cosf(this->unk_174 * M_PI)) * 0.5f; + this->actionFunc(this, globalCtx); + + this->actor.scale.z = 0.42f * temp; + this->actor.scale.x = 0.42f * temp; + + this->actor.world.pos = player->actor.world.pos; + this->actor.world.pos.y += 5.0f; + + temp = (2.0f - this->unk_174) * this->unk_174; + Environment_AdjustLights(globalCtx, temp * 0.5F, 880.0f, 0.2f, 0.9f); + + Lights_PointNoGlowSetInfo(&this->lightInfo1, (s16)this->actor.world.pos.x, (s16)this->actor.world.pos.y + 55.0f, + (s16)this->actor.world.pos.z, (s32)(255.0f * temp), (s32)(255.0f * temp), + (s32)(200.0f * temp), (s16)(100.0f * temp)); + + Lights_PointNoGlowSetInfo(&this->lightInfo2, + (s16)this->actor.world.pos.x + Math_SinS(player->actor.shape.rot.y) * 20.0f, + (s16)this->actor.world.pos.y + 20.0f, + (s16)this->actor.world.pos.z + Math_CosS(player->actor.shape.rot.y) * 20.0f, + (s32)(255.0f * temp), (s32)(255.0f * temp), (s32)(200.0f * temp), (s16)(100.0f * temp)); +} + +void OceffSpot_Draw(Actor* thisx, GlobalContext* globalCtx) { + OceffSpot* this = (OceffSpot*)thisx; + u32 scroll = globalCtx->state.frames & 0xFFFF; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_spot.c", 466); + + func_80093D84(globalCtx->state.gfxCtx); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_oceff_spot.c", 469), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, sCylinderMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 2, scroll * (-2), 32, 32, 1, + 0, scroll * (-8), 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, sCylinderModelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_spot.c", 485); +} diff --git a/soh/src/overlays/actors/ovl_Oceff_Spot/z_oceff_spot.h b/soh/src/overlays/actors/ovl_Oceff_Spot/z_oceff_spot.h new file mode 100644 index 000000000..a63890e53 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Spot/z_oceff_spot.h @@ -0,0 +1,22 @@ +#ifndef Z_OCEFF_SPOT_H +#define Z_OCEFF_SPOT_H + +#include "ultra64.h" +#include "global.h" + +struct OceffSpot; + +typedef void (*OceffSpotActionFunc)(struct OceffSpot*, GlobalContext*); + +typedef struct OceffSpot { + /* 0x0000 */ Actor actor; + /* 0x014C */ LightNode* lightNode1; + /* 0x0150 */ LightInfo lightInfo1; + /* 0x0160 */ LightNode* lightNode2; + /* 0x0164 */ LightInfo lightInfo2; + /* 0x0174 */ f32 unk_174; + /* 0x0178 */ u16 timer; + /* 0x017C */ OceffSpotActionFunc actionFunc; +} OceffSpot; // size = 0x0180 + +#endif diff --git a/soh/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c b/soh/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c new file mode 100644 index 000000000..b311753b3 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c @@ -0,0 +1,177 @@ +/* + * File: z_oceff_storm.c + * Overlay: ovl_Oceff_Storm + * Description: Song of Storms Effect + */ + +#include "z_oceff_storm.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25) + +void OceffStorm_Init(Actor* thisx, GlobalContext* globalCtx); +void OceffStorm_Destroy(Actor* thisx, GlobalContext* globalCtx); +void OceffStorm_Update(Actor* thisx, GlobalContext* globalCtx); +void OceffStorm_Draw(Actor* thisx, GlobalContext* globalCtx); + +void OceffStorm_Draw2(Actor* thisx, GlobalContext* globalCtx); + +void OceffStorm_DefaultAction(OceffStorm* this, GlobalContext* globalCtx); +void OceffStorm_UnkAction(OceffStorm* this, GlobalContext* globalCtx); + +const ActorInit Oceff_Storm_InitVars = { + ACTOR_OCEFF_STORM, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(OceffStorm), + (ActorFunc)OceffStorm_Init, + (ActorFunc)OceffStorm_Destroy, + (ActorFunc)OceffStorm_Update, + (ActorFunc)OceffStorm_Draw, + NULL, +}; + +void OceffStorm_SetupAction(OceffStorm* this, OceffStormActionFunc actionFunc) { + this->actionFunc = actionFunc; +} + +void OceffStorm_Init(Actor* thisx, GlobalContext* globalCtx) { + OceffStorm* this = (OceffStorm*)thisx; + OceffStorm_SetupAction(this, OceffStorm_DefaultAction); + this->posYOffAdd = 0; + this->counter = 0; + this->primColorAlpha = 0; + this->vtxAlpha = 0; + this->actor.scale.y = 0.0f; + this->actor.scale.z = 0.0f; + this->actor.scale.x = 0.0f; + this->posYOff = this->posYOffAdd; + + if (this->actor.params == 1) { + OceffStorm_SetupAction(this, OceffStorm_UnkAction); + this->actor.draw = OceffStorm_Draw2; + } else { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_OKARINA_EFFECT, this->actor.world.pos.x, + this->actor.world.pos.y - 30.0f, this->actor.world.pos.z, 0, 0, 0, 1); + } +} + +void OceffStorm_Destroy(Actor* thisx, GlobalContext* globalCtx) { + OceffStorm* this = (OceffStorm*)thisx; + Player* player = GET_PLAYER(globalCtx); + + func_800876C8(globalCtx); + if (gSaveContext.nayrusLoveTimer != 0) { + player->stateFlags3 |= 0x40; + } +} + +void OceffStorm_DefaultAction(OceffStorm* this, GlobalContext* globalCtx) { + if (this->counter < 20) { + this->primColorAlpha = (s8)(this->counter * 5.0f); + } else if (this->counter > 80) { + this->primColorAlpha = (s8)((100 - this->counter) * 5.0f); + } else { + this->primColorAlpha = 100; + } + + if (this->counter < 10 || this->counter >= 90) { + this->vtxAlpha = 0; + } else { + if (this->counter <= 65) { + if (this->vtxAlpha <= 200) { + this->vtxAlpha += 10; + } + this->actor.scale.x = this->actor.scale.z = 0.4f; + this->actor.scale.y = 0.3f; + } else if (this->counter > 65) { + this->vtxAlpha = (90 - this->counter) * 10; + } else { + this->vtxAlpha = 255; + this->actor.scale.x = this->actor.scale.z = 0.4f; + } + } + + if (this->counter > 60) { + this->actor.world.pos.y += this->posYOff * 0.01f; + this->posYOff += this->posYOffAdd; + this->posYOffAdd += 10; + } + + if (this->counter < 100) { + this->counter++; + } else { + Actor_Kill(&this->actor); + } +} + +void OceffStorm_UnkAction(OceffStorm* this, GlobalContext* globalCtx) { + if (this->primColorAlpha < 100) { + this->primColorAlpha += 5; + } + //! @bug Actor_Kill is never called so the actor will stay alive forever +} + +void OceffStorm_Update(Actor* thisx, GlobalContext* globalCtx) { + OceffStorm* this = (OceffStorm*)thisx; + Player* player = GET_PLAYER(globalCtx); + + this->actor.world.pos = player->actor.world.pos; + this->actor.shape.rot.y = Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)); + this->actionFunc(this, globalCtx); +} + +#include "overlays/ovl_Oceff_Storm/ovl_Oceff_Storm.h" + +void OceffStorm_Draw2(Actor* thisx, GlobalContext* globalCtx) { + u32 scroll = globalCtx->state.frames & 0xFFF; + OceffStorm* this = (OceffStorm*)thisx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_storm.c", 449); + + gDPPipeSync(POLY_XLU_DISP++); + + if (1) {} + + POLY_XLU_DISP = func_80093F34(POLY_XLU_DISP); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 200, 200, 150, this->primColorAlpha); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 8, scroll * 4, 64, 64, 1, + scroll * 4, scroll * 4, 64, 64)); + gSPTextureRectangle(POLY_XLU_DISP++, 0, 0, (SCREEN_WIDTH << 2), (SCREEN_HEIGHT << 2), G_TX_RENDERTILE, 0, 0, 140, + (1 << 15) | (31 << 10) | 884); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_storm.c", 477); +} + +void OceffStorm_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 scroll = globalCtx->state.frames & 0xFFF; + OceffStorm* this = (OceffStorm*)thisx; + Vtx* vtxPtr = ResourceMgr_LoadVtxByName(sCylinderVtx); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_storm.c", 486); + + func_80093D84(globalCtx->state.gfxCtx); + + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 200, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 0, 128); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE); + + vtxPtr[0].v.cn[3] = vtxPtr[6].v.cn[3] = vtxPtr[16].v.cn[3] = vtxPtr[25].v.cn[3] = this->vtxAlpha >> 1; + vtxPtr[10].v.cn[3] = vtxPtr[22].v.cn[3] = this->vtxAlpha; + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_oceff_storm.c", 498), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPDisplayList(POLY_XLU_DISP++, sCylinderMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 4, (0 - scroll) * 8, 32, 32, + 1, scroll * 8, (0 - scroll) * 12, 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, sCylinderModelDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_storm.c", 512); + + OceffStorm_Draw2(&this->actor, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.h b/soh/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.h new file mode 100644 index 000000000..a566d5896 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.h @@ -0,0 +1,21 @@ +#ifndef Z_OCEFF_STORM_H +#define Z_OCEFF_STORM_H + +#include "ultra64.h" +#include "global.h" + +struct OceffStorm; + +typedef void (*OceffStormActionFunc)(struct OceffStorm*, GlobalContext*); + +typedef struct OceffStorm { + /* 0x0000 */ Actor actor; + /* 0x014C */ u16 counter; + /* 0x014E */ u8 primColorAlpha; + /* 0x014F */ u8 vtxAlpha; + /* 0x0150 */ s16 posYOff; + /* 0x0152 */ s16 posYOffAdd; + /* 0x0154 */ OceffStormActionFunc actionFunc; +} OceffStorm; // size = 0x0158 + +#endif diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe/z_oceff_wipe.c b/soh/src/overlays/actors/ovl_Oceff_Wipe/z_oceff_wipe.c new file mode 100644 index 000000000..2dae0149b --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe/z_oceff_wipe.c @@ -0,0 +1,129 @@ +/* + * File: z_oceff_wipe.c + * Overlay: ovl_Oceff_Wipe + * Description: Zelda's Lullaby and Song of Time Ocarina Effect + */ + +#include "z_oceff_wipe.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void OceffWipe_Init(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe_Destroy(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe_Update(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Oceff_Wipe_InitVars = { + ACTOR_OCEFF_WIPE, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(OceffWipe), + (ActorFunc)OceffWipe_Init, + (ActorFunc)OceffWipe_Destroy, + (ActorFunc)OceffWipe_Update, + (ActorFunc)OceffWipe_Draw, + NULL, +}; + +void OceffWipe_Init(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe* this = (OceffWipe*)thisx; + + Actor_SetScale(&this->actor, 0.1f); + this->timer = 0; + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + osSyncPrintf(VT_FGCOL(CYAN) " WIPE arg_data = %d\n" VT_RST, this->actor.params); +} + +void OceffWipe_Destroy(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe* this = (OceffWipe*)thisx; + Player* player = GET_PLAYER(globalCtx); + + func_800876C8(globalCtx); + if (gSaveContext.nayrusLoveTimer != 0) { + player->stateFlags3 |= 0x40; + } +} + +void OceffWipe_Update(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe* this = (OceffWipe*)thisx; + + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + if (this->timer < 100) { + this->timer++; + } else { + Actor_Kill(&this->actor); + } +} + +#include "overlays/ovl_Oceff_Wipe/ovl_Oceff_Wipe.h" + +static u8 sAlphaIndices[] = { + 0x01, 0x10, 0x22, 0x01, 0x20, 0x12, 0x01, 0x20, 0x12, 0x01, + 0x10, 0x22, 0x01, 0x20, 0x12, 0x01, 0x12, 0x21, 0x01, 0x02, +}; + +void OceffWipe_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 scroll = globalCtx->state.frames & 0xFF; + OceffWipe* this = (OceffWipe*)thisx; + f32 z; + s32 pad; + u8 alphaTable[3]; + s32 i; + Vec3f eye; + Vtx* vtxPtr; + Vec3f vec; + + eye = GET_ACTIVE_CAM(globalCtx)->eye; + Camera_GetSkyboxOffset(&vec, GET_ACTIVE_CAM(globalCtx)); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe.c", 346); + + if (this->timer < 32) { + z = Math_SinS(this->timer << 9) * 1400; + } else { + z = 1400; + } + + if (this->timer >= 80) { + alphaTable[0] = 0; + alphaTable[1] = (0x64 - this->timer) * 8; + alphaTable[2] = (0x64 - this->timer) * 12; + } else { + alphaTable[0] = 0; + alphaTable[1] = 0xA0; + alphaTable[2] = 0xFF; + } + + for (i = 0; i < 20; i++) { + vtxPtr = ResourceMgr_LoadVtxByName(sFrustumVtx); + vtxPtr[i * 2 + 0].v.cn[3] = alphaTable[(sAlphaIndices[i] & 0xF0) >> 4]; + vtxPtr[i * 2 + 1].v.cn[3] = alphaTable[sAlphaIndices[i] & 0xF]; + } + + func_80093D84(globalCtx->state.gfxCtx); + + Matrix_Translate(eye.x + vec.x, eye.y + vec.y, eye.z + vec.z, MTXMODE_NEW); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Translate(0.0f, 0.0f, -z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_oceff_wipe.c", 375), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->actor.params != OCEFF_WIPE_ZL) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 170, 255, 255, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 150, 255, 128); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 0, 255, 128); + } + + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0 - scroll, scroll * (-2), 32, 32, 1, + 0 - scroll, scroll * (-2), 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, sFrustumDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe.c", 398); +} diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe/z_oceff_wipe.h b/soh/src/overlays/actors/ovl_Oceff_Wipe/z_oceff_wipe.h new file mode 100644 index 000000000..0b26239ba --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe/z_oceff_wipe.h @@ -0,0 +1,19 @@ +#ifndef Z_OCEFF_WIPE_H +#define Z_OCEFF_WIPE_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ OCEFF_WIPE_ZL, + /* 0x01 */ OCEFF_WIPE_SOT +} OceffWipeType; + +struct OceffWipe; + +typedef struct OceffWipe { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 timer; +} OceffWipe; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe2/z_oceff_wipe2.c b/soh/src/overlays/actors/ovl_Oceff_Wipe2/z_oceff_wipe2.c new file mode 100644 index 000000000..4f80b4b8c --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe2/z_oceff_wipe2.c @@ -0,0 +1,111 @@ +/* + * File: z_oceff_wipe2.c + * Overlay: ovl_Oceff_Wipe2 + * Description: Epona's Song Effect + */ + +#include "z_oceff_wipe2.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void OceffWipe2_Init(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe2_Destroy(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe2_Update(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe2_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Oceff_Wipe2_InitVars = { + ACTOR_OCEFF_WIPE2, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(OceffWipe2), + (ActorFunc)OceffWipe2_Init, + (ActorFunc)OceffWipe2_Destroy, + (ActorFunc)OceffWipe2_Update, + (ActorFunc)OceffWipe2_Draw, + NULL, +}; + +void OceffWipe2_Init(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe2* this = (OceffWipe2*)thisx; + + Actor_SetScale(&this->actor, 0.1f); + this->timer = 0; + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + osSyncPrintf(VT_FGCOL(CYAN) " WIPE2 arg_data = %d\n" VT_RST, this->actor.params); +} + +void OceffWipe2_Destroy(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe2* this = (OceffWipe2*)thisx; + Player* player = GET_PLAYER(globalCtx); + + func_800876C8(globalCtx); + if (gSaveContext.nayrusLoveTimer != 0) { + player->stateFlags3 |= 0x40; + } +} + +void OceffWipe2_Update(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe2* this = (OceffWipe2*)thisx; + + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + if (this->timer < 100) { + this->timer++; + } else { + Actor_Kill(&this->actor); + } +} + +#include "overlays/ovl_Oceff_Wipe2/ovl_Oceff_Wipe2.h" + +void OceffWipe2_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 scroll = globalCtx->state.frames & 0xFF; + OceffWipe2* this = (OceffWipe2*)thisx; + f32 z; + u8 alpha; + s32 pad[2]; + Vec3f eye; + Vtx* vtxPtr; + Vec3f vec; + + eye = GET_ACTIVE_CAM(globalCtx)->eye; + Camera_GetSkyboxOffset(&vec, GET_ACTIVE_CAM(globalCtx)); + if (this->timer < 32) { + z = Math_SinS(this->timer << 9) * 1330; + } else { + z = 1330; + } + + vtxPtr = ResourceMgr_LoadVtxByName(sFrustumVtx); + if (this->timer >= 80) { + alpha = 12 * (100 - this->timer); + } else { + alpha = 255; + } + + vtxPtr[1].v.cn[3] = vtxPtr[3].v.cn[3] = vtxPtr[5].v.cn[3] = vtxPtr[7].v.cn[3] = vtxPtr[9].v.cn[3] = + vtxPtr[11].v.cn[3] = vtxPtr[13].v.cn[3] = vtxPtr[15].v.cn[3] = vtxPtr[16].v.cn[3] = vtxPtr[18].v.cn[3] = + vtxPtr[20].v.cn[3] = alpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe2.c", 390); + + func_80093D84(globalCtx->state.gfxCtx); + + Matrix_Translate(eye.x + vec.x, eye.y + vec.y, eye.z + vec.z, MTXMODE_NEW); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Translate(0.0f, 0.0f, -z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_oceff_wipe2.c", 400), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 100, 0, 128); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 6, scroll * (-6), 64, 64, 1, + scroll * (-6), 0, 64, 64)); + gSPDisplayList(POLY_XLU_DISP++, sFrustumDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe2.c", 417); +} diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe2/z_oceff_wipe2.h b/soh/src/overlays/actors/ovl_Oceff_Wipe2/z_oceff_wipe2.h new file mode 100644 index 000000000..c7f9c7b87 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe2/z_oceff_wipe2.h @@ -0,0 +1,14 @@ +#ifndef Z_OCEFF_WIPE2_H +#define Z_OCEFF_WIPE2_H + +#include "ultra64.h" +#include "global.h" + +struct OceffWipe2; + +typedef struct OceffWipe2 { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 timer; +} OceffWipe2; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe3/z_oceff_wipe3.c b/soh/src/overlays/actors/ovl_Oceff_Wipe3/z_oceff_wipe3.c new file mode 100644 index 000000000..ed1012078 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe3/z_oceff_wipe3.c @@ -0,0 +1,112 @@ +/* + * File: z_oceff_wipe3.c + * Overlay: ovl_Oceff_Wipe3 + * Description: Saria's Song Effect + */ + +#include "z_oceff_wipe3.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void OceffWipe3_Init(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe3_Destroy(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe3_Update(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe3_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Oceff_Wipe3_InitVars = { + ACTOR_OCEFF_WIPE3, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(OceffWipe3), + (ActorFunc)OceffWipe3_Init, + (ActorFunc)OceffWipe3_Destroy, + (ActorFunc)OceffWipe3_Update, + (ActorFunc)OceffWipe3_Draw, + NULL, +}; + +#include "overlays/ovl_Oceff_Wipe3/ovl_Oceff_Wipe3.h" + +void OceffWipe3_Init(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe3* this = (OceffWipe3*)thisx; + + Actor_SetScale(&this->actor, 0.1f); + this->counter = 0; + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + // it's actually WIPE3... + osSyncPrintf(VT_FGCOL(CYAN) " WIPE2 arg_data = %d\n" VT_RST, this->actor.params); +} + +void OceffWipe3_Destroy(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe3* this = (OceffWipe3*)thisx; + Player* player = GET_PLAYER(globalCtx); + + func_800876C8(globalCtx); + if (gSaveContext.nayrusLoveTimer != 0) { + player->stateFlags3 |= 0x40; + } +} + +void OceffWipe3_Update(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe3* this = (OceffWipe3*)thisx; + + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + if (this->counter < 100) { + this->counter++; + } else { + Actor_Kill(&this->actor); + } +} + +void OceffWipe3_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 scroll = globalCtx->state.frames & 0xFFF; + OceffWipe3* this = (OceffWipe3*)thisx; + f32 z; + u8 alpha; + s32 pad[2]; + Vec3f eye; + Vtx* vtxPtr; + Vec3f vec; + + eye = GET_ACTIVE_CAM(globalCtx)->eye; + Camera_GetSkyboxOffset(&vec, GET_ACTIVE_CAM(globalCtx)); + if (this->counter < 32) { + z = Math_SinS(this->counter << 9) * 1330; + } else { + z = 1330; + } + + vtxPtr = ResourceMgr_LoadVtxByName(sFrustumVtx); + if (this->counter >= 80) { + alpha = 12 * (100 - this->counter); + } else { + alpha = 255; + } + + vtxPtr[1].v.cn[3] = vtxPtr[3].v.cn[3] = vtxPtr[5].v.cn[3] = vtxPtr[7].v.cn[3] = vtxPtr[9].v.cn[3] = + vtxPtr[11].v.cn[3] = vtxPtr[13].v.cn[3] = vtxPtr[15].v.cn[3] = vtxPtr[17].v.cn[3] = vtxPtr[19].v.cn[3] = + vtxPtr[21].v.cn[3] = alpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe3.c", 343); + + func_80093D84(globalCtx->state.gfxCtx); + + Matrix_Translate(eye.x + vec.x, eye.y + vec.y, eye.z + vec.z, MTXMODE_NEW); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Translate(0.0f, 0.0f, -z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_oceff_wipe3.c", 353), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 170, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 100, 200, 0, 128); + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 12, scroll * (-12), 64, 64, 1, + scroll * 8, scroll * (-8), 64, 64)); + gSPDisplayList(POLY_XLU_DISP++, sFrustumDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe3.c", 370); +} diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe3/z_oceff_wipe3.h b/soh/src/overlays/actors/ovl_Oceff_Wipe3/z_oceff_wipe3.h new file mode 100644 index 000000000..a5bc2bbe4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe3/z_oceff_wipe3.h @@ -0,0 +1,14 @@ +#ifndef Z_OCEFF_WIPE3_H +#define Z_OCEFF_WIPE3_H + +#include "ultra64.h" +#include "global.h" + +struct OceffWipe3; + +typedef struct OceffWipe3 { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 counter; +} OceffWipe3; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe4/z_oceff_wipe4.c b/soh/src/overlays/actors/ovl_Oceff_Wipe4/z_oceff_wipe4.c new file mode 100644 index 000000000..500933a26 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe4/z_oceff_wipe4.c @@ -0,0 +1,111 @@ +/* + * File: z_oceff_wipe4.c + * Overlay: ovl_Oceff_Wipe4 + * Description: Scarecrow's Song and an unused Ocarina Effect + */ + +#include "z_oceff_wipe4.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_25) + +void OceffWipe4_Init(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe4_Destroy(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe4_Update(Actor* thisx, GlobalContext* globalCtx); +void OceffWipe4_Draw(Actor* thisx, GlobalContext* globalCtx); + +const ActorInit Oceff_Wipe4_InitVars = { + ACTOR_OCEFF_WIPE4, + ACTORCAT_ITEMACTION, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(OceffWipe4), + (ActorFunc)OceffWipe4_Init, + (ActorFunc)OceffWipe4_Destroy, + (ActorFunc)OceffWipe4_Update, + (ActorFunc)OceffWipe4_Draw, + NULL, +}; + +void OceffWipe4_Init(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe4* this = (OceffWipe4*)thisx; + + Actor_SetScale(&this->actor, 0.1f); + this->timer = 0; + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + osSyncPrintf(VT_FGCOL(CYAN) " WIPE4 arg_data = %d\n" VT_RST, this->actor.params); +} + +void OceffWipe4_Destroy(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe4* this = (OceffWipe4*)thisx; + + func_800876C8(globalCtx); +} + +void OceffWipe4_Update(Actor* thisx, GlobalContext* globalCtx) { + OceffWipe4* this = (OceffWipe4*)thisx; + + this->actor.world.pos = GET_ACTIVE_CAM(globalCtx)->eye; + if (this->timer < 50) { + this->timer++; + } else { + Actor_Kill(&this->actor); + } +} + +#include "overlays/ovl_Oceff_Wipe4/ovl_Oceff_Wipe4.h" + +void OceffWipe4_Draw(Actor* thisx, GlobalContext* globalCtx) { + u32 scroll = globalCtx->state.frames & 0xFFF; + OceffWipe4* this = (OceffWipe4*)thisx; + f32 z; + u8 alpha; + s32 pad[2]; + Vec3f eye; + Vtx* vtxPtr; + Vec3f vec; + + eye = GET_ACTIVE_CAM(globalCtx)->eye; + Camera_GetSkyboxOffset(&vec, GET_ACTIVE_CAM(globalCtx)); + if (this->timer < 16) { + z = Math_SinS(this->timer * 1024) * 1330.0f; + } else { + z = 1330.0f; + } + + vtxPtr = ResourceMgr_LoadVtxByName(sFrustumVtx); + if (this->timer >= 30) { + alpha = 12 * (50 - this->timer); + } else { + alpha = 255; + } + + vtxPtr[1].v.cn[3] = vtxPtr[3].v.cn[3] = vtxPtr[5].v.cn[3] = vtxPtr[7].v.cn[3] = vtxPtr[9].v.cn[3] = + vtxPtr[11].v.cn[3] = vtxPtr[13].v.cn[3] = vtxPtr[15].v.cn[3] = vtxPtr[17].v.cn[3] = vtxPtr[19].v.cn[3] = + vtxPtr[21].v.cn[3] = alpha; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe4.c", 314); + + func_80093D84(globalCtx->state.gfxCtx); + + Matrix_Translate(eye.x + vec.x, eye.y + vec.y, eye.z + vec.z, MTXMODE_NEW); + Matrix_Scale(0.1f, 0.1f, 0.1f, MTXMODE_APPLY); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_Translate(0.0f, 0.0f, -z, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_oceff_wipe4.c", 324), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->actor.params == OCEFF_WIPE4_UNUSED) { + gSPDisplayList(POLY_XLU_DISP++, sUnusedMaterialDL); + } else { + gSPDisplayList(POLY_XLU_DISP++, sMaterialDL); + } + + gSPDisplayList(POLY_XLU_DISP++, sMaterial2DL); + gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, scroll * 2, scroll * (-2), 32, 64, 1, + scroll * (-1), scroll, 32, 32)); + gSPDisplayListOffset(POLY_XLU_DISP++, sMaterial2DL, 11); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_oceff_wipe4.c", 344); +} diff --git a/soh/src/overlays/actors/ovl_Oceff_Wipe4/z_oceff_wipe4.h b/soh/src/overlays/actors/ovl_Oceff_Wipe4/z_oceff_wipe4.h new file mode 100644 index 000000000..618728934 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Oceff_Wipe4/z_oceff_wipe4.h @@ -0,0 +1,19 @@ +#ifndef Z_OCEFF_WIPE4_H +#define Z_OCEFF_WIPE4_H + +#include "ultra64.h" +#include "global.h" + +typedef enum { + /* 0x00 */ OCEFF_WIPE4_SCARECROWS, + /* 0x01 */ OCEFF_WIPE4_UNUSED +} OceffWipe4Type; + +struct OceffWipe4; + +typedef struct OceffWipe4 { + /* 0x0000 */ Actor actor; + /* 0x014C */ s16 timer; +} OceffWipe4; // size = 0x0150 + +#endif diff --git a/soh/src/overlays/actors/ovl_Shot_Sun/z_shot_sun.c b/soh/src/overlays/actors/ovl_Shot_Sun/z_shot_sun.c new file mode 100644 index 000000000..5d3f27cc4 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Shot_Sun/z_shot_sun.c @@ -0,0 +1,200 @@ +/* + * File: z_shot_sun.c + * Overlay: ovl_Shot_Sun + * Description: Lake Hylia Sun hitbox and Song of Storms Fairy spawner + */ + +#include "z_shot_sun.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "scenes/overworld/spot06/spot06_scene.h" +#include "vt.h" + +#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3) + +void ShotSun_Init(Actor* thisx, GlobalContext* globalCtx); +void ShotSun_Destroy(Actor* thisx, GlobalContext* globalCtx); +void ShotSun_Update(Actor* thisx, GlobalContext* globalCtx); + +void ShotSun_SpawnFairy(ShotSun* this, GlobalContext* globalCtx); +void ShotSun_TriggerFairy(ShotSun* this, GlobalContext* globalCtx); +void func_80BADF0C(ShotSun* this, GlobalContext* globalCtx); +void ShotSun_UpdateHyliaSun(ShotSun* this, GlobalContext* globalCtx); + +const ActorInit Shot_Sun_InitVars = { + ACTOR_SHOT_SUN, + ACTORCAT_PROP, + FLAGS, + OBJECT_GAMEPLAY_KEEP, + sizeof(ShotSun), + (ActorFunc)ShotSun_Init, + (ActorFunc)ShotSun_Destroy, + (ActorFunc)ShotSun_Update, + NULL, + NULL, +}; + +static ColliderCylinderInit sCylinderInit = { + { + COLTYPE_NONE, + AT_NONE, + AC_ON | AC_TYPE_PLAYER, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_1, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK0, + { 0x00000000, 0x00, 0x00 }, + { 0x00000020, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 30, 60, 0, { 0, 0, 0 } }, +}; + +void ShotSun_Init(Actor* thisx, GlobalContext* globalCtx) { + ShotSun* this = (ShotSun*)thisx; + s32 params; + + // "Ocarina secret occurrence" + osSyncPrintf("%d ---- オカリナの秘密発生!!!!!!!!!!!!!\n", this->actor.params); + params = this->actor.params & 0xFF; + if (params == 0x40 || params == 0x41) { + this->unk_1A4 = 0; + this->actor.flags |= ACTOR_FLAG_4; + this->actor.flags |= ACTOR_FLAG_25; + this->actionFunc = func_80BADF0C; + this->actor.flags |= ACTOR_FLAG_27; + } else { + Collider_InitCylinder(globalCtx, &this->collider); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sCylinderInit); + this->actionFunc = ShotSun_UpdateHyliaSun; + this->actor.flags &= ~ACTOR_FLAG_0; + } +} + +void ShotSun_Destroy(Actor* thisx, GlobalContext* globalCtx) { + ShotSun* this = (ShotSun*)thisx; + s32 params = this->actor.params & 0xFF; + + if (params != 0x40 && params != 0x41) { + Collider_DestroyCylinder(globalCtx, &this->collider); + } +} + +void ShotSun_SpawnFairy(ShotSun* this, GlobalContext* globalCtx) { + s32 params = this->actor.params & 0xFF; + s32 fairyType; + + if (this->timer > 0) { + this->timer--; + } else { + switch (params) { + case 0x40: + fairyType = FAIRY_HEAL_BIG; + break; + case 0x41: + fairyType = FAIRY_HEAL_BIG; + break; + } + + //! @bug fairyType may be uninitialized + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, this->actor.home.pos.x, this->actor.home.pos.y, + this->actor.home.pos.z, 0, 0, 0, fairyType); + + Actor_Kill(&this->actor); + } +} + +void ShotSun_TriggerFairy(ShotSun* this, GlobalContext* globalCtx) { + if ((func_8005B198() == this->actor.category) || (this->timer != 0)) { + this->actionFunc = ShotSun_SpawnFairy; + this->timer = 50; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_KANKYO, this->actor.home.pos.x, this->actor.home.pos.y, + this->actor.home.pos.z, 0, 0, 0, 0x11); + + func_80078914(&this->actor.projectedPos, NA_SE_EV_TRE_BOX_APPEAR); + } +} + +void func_80BADF0C(ShotSun* this, GlobalContext* globalCtx) { + Player* player = GET_PLAYER(globalCtx); + s32 pad; + s32 params = this->actor.params & 0xFF; + + if (Math3D_Vec3fDistSq(&this->actor.world.pos, &player->actor.world.pos) > 22500.0f) { + this->unk_1A4 = 0; + } else { + if (this->unk_1A4 == 0) { + if (!(player->stateFlags2 & 0x1000000)) { + player->stateFlags2 |= 0x800000; + return; + } else { + this->unk_1A4 = 1; + } + } + if (this->unk_1A4 == 1) { + func_8010BD58(globalCtx, OCARINA_ACTION_FREE_PLAY); + this->unk_1A4 = 2; + } else if (this->unk_1A4 == 2 && globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + if ((params == 0x40 && globalCtx->msgCtx.lastPlayedSong == OCARINA_SONG_SUNS) || + (params == 0x41 && globalCtx->msgCtx.lastPlayedSong == OCARINA_SONG_STORMS)) { + this->actionFunc = ShotSun_TriggerFairy; + OnePointCutscene_Attention(globalCtx, &this->actor); + this->timer = 0; + } else { + this->unk_1A4 = 0; + } + this->unk_1A4 = 0; + } + } +} + +void ShotSun_UpdateHyliaSun(ShotSun* this, GlobalContext* globalCtx) { + Vec3s cylinderPos; + Player* player = GET_PLAYER(globalCtx); + EnItem00* collectible; + s32 pad; + Vec3f spawnPos; + + if (this->collider.base.acFlags & AC_HIT) { + func_80078884(NA_SE_SY_CORRECT_CHIME); + osSyncPrintf(VT_FGCOL(CYAN) "SHOT_SUN HIT!!!!!!!\n" VT_RST); + if (INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_NONE) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_ETCETERA, 700.0f, -800.0f, 7261.0f, 0, 0, 0, 7); + globalCtx->csCtx.segment = SEGMENTED_TO_VIRTUAL(gLakeHyliaFireArrowsCS); + if (1) {} + gSaveContext.cutsceneTrigger = 1; + } else { + spawnPos.x = 700.0f; + spawnPos.y = -800.0f; + spawnPos.z = 7261.0f; + + collectible = Item_DropCollectible(globalCtx, &spawnPos, ITEM00_MAGIC_LARGE); + if (collectible != NULL) { + collectible->unk_15A = 6000; + collectible->actor.speedXZ = 0.0f; + } + } + Actor_Kill(&this->actor); + } else { + if (!(this->actor.xzDistToPlayer > 120.0f) && gSaveContext.dayTime >= 0x4555 && gSaveContext.dayTime < 0x5000) { + cylinderPos.x = player->bodyPartsPos[7].x + globalCtx->envCtx.sunPos.x * (1.0f / 6.0f); + cylinderPos.y = player->bodyPartsPos[7].y - 30.0f + globalCtx->envCtx.sunPos.y * (1.0f / 6.0f); + cylinderPos.z = player->bodyPartsPos[7].z + globalCtx->envCtx.sunPos.z * (1.0f / 6.0f); + + this->hitboxPos = cylinderPos; + + Collider_SetCylinderPosition(&this->collider, &cylinderPos); + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + } + } +} + +void ShotSun_Update(Actor* thisx, GlobalContext* globalCtx) { + ShotSun* this = (ShotSun*)thisx; + + this->actionFunc(this, globalCtx); +} diff --git a/soh/src/overlays/actors/ovl_Shot_Sun/z_shot_sun.h b/soh/src/overlays/actors/ovl_Shot_Sun/z_shot_sun.h new file mode 100644 index 000000000..19fd705f8 --- /dev/null +++ b/soh/src/overlays/actors/ovl_Shot_Sun/z_shot_sun.h @@ -0,0 +1,20 @@ +#ifndef Z_SHOT_SUN_H +#define Z_SHOT_SUN_H + +#include "ultra64.h" +#include "global.h" + +struct ShotSun; + +typedef void (*ShotSunActionFunc)(struct ShotSun*, GlobalContext*); + +typedef struct ShotSun { + /* 0x0000 */ Actor actor; + /* 0x014C */ ColliderCylinder collider; + /* 0x0198 */ ShotSunActionFunc actionFunc; + /* 0x019C */ Vec3s hitboxPos; + /* 0x01A2 */ s16 timer; // Frames until fairy spawns + /* 0x01A4 */ u8 unk_1A4; +} ShotSun; // size = 0x01A8 + +#endif diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c new file mode 100644 index 000000000..d02788a8e --- /dev/null +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -0,0 +1,14809 @@ +/* + * File: z_player.c + * Overlay: ovl_player_actor + * Description: Link + */ + +#include "ultra64.h" +#include "global.h" + +#include "overlays/actors/ovl_Bg_Heavy_Block/z_bg_heavy_block.h" +#include "overlays/actors/ovl_Door_Shutter/z_door_shutter.h" +#include "overlays/actors/ovl_En_Boom/z_en_boom.h" +#include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "overlays/actors/ovl_En_Box/z_en_box.h" +#include "overlays/actors/ovl_En_Door/z_en_door.h" +#include "overlays/actors/ovl_En_Elf/z_en_elf.h" +#include "overlays/actors/ovl_En_Fish/z_en_fish.h" +#include "overlays/actors/ovl_En_Horse/z_en_horse.h" +#include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_link_child/object_link_child.h" + +typedef struct { + /* 0x00 */ u8 itemId; + /* 0x01 */ u8 field; // various bit-packed data + /* 0x02 */ s8 gi; // defines the draw id and chest opening animation + /* 0x03 */ u8 textId; + /* 0x04 */ u16 objectId; +} GetItemEntry; // size = 0x06 + +#define GET_ITEM(itemId, objectId, drawId, textId, field, chestAnim) \ + { itemId, field, (chestAnim != CHEST_ANIM_SHORT ? 1 : -1) * (drawId + 1), textId, objectId } + +#define CHEST_ANIM_SHORT 0 +#define CHEST_ANIM_LONG 1 + +#define GET_ITEM_NONE \ + { ITEM_NONE, 0, 0, 0, OBJECT_INVALID } + +typedef enum { + /* 0x00 */ KNOB_ANIM_ADULT_L, + /* 0x01 */ KNOB_ANIM_CHILD_L, + /* 0x02 */ KNOB_ANIM_ADULT_R, + /* 0x03 */ KNOB_ANIM_CHILD_R +} KnobDoorAnim; + +typedef struct { + /* 0x00 */ u8 itemId; + /* 0x02 */ s16 actorId; +} ExplosiveInfo; // size = 0x04 + +typedef struct { + /* 0x00 */ s16 actorId; + /* 0x02 */ u8 itemId; + /* 0x03 */ u8 actionParam; + /* 0x04 */ u8 textId; +} BottleCatchInfo; // size = 0x06 + +typedef struct { + /* 0x00 */ s16 actorId; + /* 0x02 */ s16 actorParams; +} BottleDropInfo; // size = 0x04 + +typedef struct { + /* 0x00 */ s8 damage; + /* 0x01 */ u8 unk_01; + /* 0x02 */ u8 unk_02; + /* 0x03 */ u8 unk_03; + /* 0x04 */ u16 sfxId; +} FallImpactInfo; // size = 0x06 + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 yaw; +} SpecialRespawnInfo; // size = 0x10 + +typedef struct { + /* 0x00 */ u16 sfxId; + /* 0x02 */ s16 field; +} struct_80832924; // size = 0x04 + +typedef struct { + /* 0x00 */ u16 unk_00; + /* 0x02 */ s16 unk_02; +} struct_808551A4; // size = 0x04 + +typedef struct { + /* 0x00 */ LinkAnimationHeader* anim; + /* 0x04 */ u8 unk_04; +} struct_808540F4; // size = 0x08 + +typedef struct { + /* 0x00 */ LinkAnimationHeader* unk_00; + /* 0x04 */ LinkAnimationHeader* unk_04; + /* 0x08 */ u8 unk_08; + /* 0x09 */ u8 unk_09; +} struct_80854554; // size = 0x0C + +typedef struct { + /* 0x00 */ LinkAnimationHeader* unk_00; + /* 0x04 */ LinkAnimationHeader* unk_04; + /* 0x08 */ LinkAnimationHeader* unk_08; + /* 0x0C */ u8 unk_0C; + /* 0x0D */ u8 unk_0D; +} struct_80854190; // size = 0x10 + +typedef struct { + /* 0x00 */ LinkAnimationHeader* anim; + /* 0x04 */ f32 unk_04; + /* 0x04 */ f32 unk_08; +} struct_80854578; // size = 0x0C + +typedef struct { + /* 0x00 */ s8 type; + /* 0x04 */ union { + void* ptr; + void (*func)(GlobalContext*, Player*, CsCmdActorAction*); + }; +} struct_80854B18; // size = 0x08 + +typedef struct { + /* 0x00 */ s16 unk_00; + /* 0x02 */ s16 unk_02; + /* 0x04 */ s16 unk_04; + /* 0x06 */ s16 unk_06; + /* 0x08 */ s16 unk_08; +} struct_80858AC8; // size = 0x0A + +void func_80833770(GlobalContext* globalCtx, Player* this); +void func_80833790(GlobalContext* globalCtx, Player* this); +void func_8083379C(GlobalContext* globalCtx, Player* this); +void func_8083377C(GlobalContext* globalCtx, Player* this); +void func_808337D4(GlobalContext* globalCtx, Player* this); +void func_80833910(GlobalContext* globalCtx, Player* this); +void func_80833984(GlobalContext* globalCtx, Player* this); +void func_8083399C(GlobalContext* globalCtx, Player* this, s8 actionParam); +s32 func_8083485C(Player* this, GlobalContext* globalCtx); +s32 func_808349DC(Player* this, GlobalContext* globalCtx); +s32 func_80834A2C(Player* this, GlobalContext* globalCtx); +s32 func_80834B5C(Player* this, GlobalContext* globalCtx); +s32 func_80834C74(Player* this, GlobalContext* globalCtx); +s32 func_8083501C(Player* this, GlobalContext* globalCtx); +s32 func_808351D4(Player* this, GlobalContext* globalCtx); +s32 func_808353D8(Player* this, GlobalContext* globalCtx); +s32 func_80835588(Player* this, GlobalContext* globalCtx); +s32 func_808356E8(Player* this, GlobalContext* globalCtx); +s32 func_80835800(Player* this, GlobalContext* globalCtx); +s32 func_80835884(Player* this, GlobalContext* globalCtx); +s32 func_808358F0(Player* this, GlobalContext* globalCtx); +s32 func_808359FC(Player* this, GlobalContext* globalCtx); +s32 func_80835B60(Player* this, GlobalContext* globalCtx); +s32 func_80835C08(Player* this, GlobalContext* globalCtx); +void func_80835F44(GlobalContext* globalCtx, Player* this, s32 item); +void func_80839F90(Player* this, GlobalContext* globalCtx); +s32 func_80838A14(Player* this, GlobalContext* globalCtx); +s32 func_80839800(Player* this, GlobalContext* globalCtx); +s32 func_8083B040(Player* this, GlobalContext* globalCtx); +s32 func_8083B998(Player* this, GlobalContext* globalCtx); +s32 func_8083B644(Player* this, GlobalContext* globalCtx); +s32 func_8083BDBC(Player* this, GlobalContext* globalCtx); +s32 func_8083C1DC(Player* this, GlobalContext* globalCtx); +s32 func_8083C2B0(Player* this, GlobalContext* globalCtx); +s32 func_8083C544(Player* this, GlobalContext* globalCtx); +s32 func_8083C61C(GlobalContext* globalCtx, Player* this); +void func_8083CA20(GlobalContext* globalCtx, Player* this); +void func_8083CA54(GlobalContext* globalCtx, Player* this); +void func_8083CA9C(GlobalContext* globalCtx, Player* this); +s32 func_8083E0FC(Player* this, GlobalContext* globalCtx); +s32 func_8083E5A8(Player* this, GlobalContext* globalCtx); +s32 func_8083EB44(Player* this, GlobalContext* globalCtx); +s32 func_8083F7BC(Player* this, GlobalContext* globalCtx); +void func_80840450(Player* this, GlobalContext* globalCtx); +void func_808407CC(Player* this, GlobalContext* globalCtx); +void func_80840BC8(Player* this, GlobalContext* globalCtx); +void func_80840DE4(Player* this, GlobalContext* globalCtx); +void func_808414F8(Player* this, GlobalContext* globalCtx); +void func_8084170C(Player* this, GlobalContext* globalCtx); +void func_808417FC(Player* this, GlobalContext* globalCtx); +void func_8084193C(Player* this, GlobalContext* globalCtx); +void func_80841BA8(Player* this, GlobalContext* globalCtx); +void func_80842180(Player* this, GlobalContext* globalCtx); +void func_8084227C(Player* this, GlobalContext* globalCtx); +void func_8084279C(Player* this, GlobalContext* globalCtx); +void func_808423EC(Player* this, GlobalContext* globalCtx); +void func_8084251C(Player* this, GlobalContext* globalCtx); +void func_80843188(Player* this, GlobalContext* globalCtx); +void func_808435C4(Player* this, GlobalContext* globalCtx); +void func_8084370C(Player* this, GlobalContext* globalCtx); +void func_8084377C(Player* this, GlobalContext* globalCtx); +void func_80843954(Player* this, GlobalContext* globalCtx); +void func_80843A38(Player* this, GlobalContext* globalCtx); +void func_80843CEC(Player* this, GlobalContext* globalCtx); +void func_8084411C(Player* this, GlobalContext* globalCtx); +void func_80844708(Player* this, GlobalContext* globalCtx); +void func_80844A44(Player* this, GlobalContext* globalCtx); +void func_80844AF4(Player* this, GlobalContext* globalCtx); +void func_80844E68(Player* this, GlobalContext* globalCtx); +void func_80845000(Player* this, GlobalContext* globalCtx); +void func_80845308(Player* this, GlobalContext* globalCtx); +void func_80845668(Player* this, GlobalContext* globalCtx); +void func_808458D0(Player* this, GlobalContext* globalCtx); +void func_80845CA4(Player* this, GlobalContext* globalCtx); +void func_80845EF8(Player* this, GlobalContext* globalCtx); +void func_80846050(Player* this, GlobalContext* globalCtx); +void func_80846120(Player* this, GlobalContext* globalCtx); +void func_80846260(Player* this, GlobalContext* globalCtx); +void func_80846358(Player* this, GlobalContext* globalCtx); +void func_80846408(Player* this, GlobalContext* globalCtx); +void func_808464B0(Player* this, GlobalContext* globalCtx); +void func_80846578(Player* this, GlobalContext* globalCtx); +void func_80846648(GlobalContext* globalCtx, Player* this); +void func_80846660(GlobalContext* globalCtx, Player* this); +void func_808467D4(GlobalContext* globalCtx, Player* this); +void func_808468A8(GlobalContext* globalCtx, Player* this); +void func_808468E8(GlobalContext* globalCtx, Player* this); +void func_80846978(GlobalContext* globalCtx, Player* this); +void func_808469BC(GlobalContext* globalCtx, Player* this); +void func_80846A68(GlobalContext* globalCtx, Player* this); +void func_8084B1D8(Player* this, GlobalContext* globalCtx); +void func_8084B530(Player* this, GlobalContext* globalCtx); +void func_8084B78C(Player* this, GlobalContext* globalCtx); +void func_8084B898(Player* this, GlobalContext* globalCtx); +void func_8084B9E4(Player* this, GlobalContext* globalCtx); +void func_8084BBE4(Player* this, GlobalContext* globalCtx); +void func_8084BDFC(Player* this, GlobalContext* globalCtx); +void func_8084BF1C(Player* this, GlobalContext* globalCtx); +void Player_UpdateCommon(Player* this, GlobalContext* globalCtx, Input* input); +void func_8084C5F8(Player* this, GlobalContext* globalCtx); +void func_8084C760(Player* this, GlobalContext* globalCtx); +void func_8084C81C(Player* this, GlobalContext* globalCtx); +void func_8084CC98(Player* this, GlobalContext* globalCtx); +void func_8084D3E4(Player* this, GlobalContext* globalCtx); +void func_8084D610(Player* this, GlobalContext* globalCtx); +void func_8084D7C4(Player* this, GlobalContext* globalCtx); +void func_8084D84C(Player* this, GlobalContext* globalCtx); +void func_8084DAB4(Player* this, GlobalContext* globalCtx); +void func_8084DC48(Player* this, GlobalContext* globalCtx); +void func_8084E1EC(Player* this, GlobalContext* globalCtx); +void func_8084E30C(Player* this, GlobalContext* globalCtx); +void func_8084E368(Player* this, GlobalContext* globalCtx); +void func_8084E3C4(Player* this, GlobalContext* globalCtx); +void func_8084E604(Player* this, GlobalContext* globalCtx); +void func_8084E6D4(Player* this, GlobalContext* globalCtx); +void func_8084E9AC(Player* this, GlobalContext* globalCtx); +void func_8084EAC0(Player* this, GlobalContext* globalCtx); +void func_8084ECA4(Player* this, GlobalContext* globalCtx); +void func_8084EED8(Player* this, GlobalContext* globalCtx); +void func_8084EFC0(Player* this, GlobalContext* globalCtx); +void func_8084F104(Player* this, GlobalContext* globalCtx); +void func_8084F390(Player* this, GlobalContext* globalCtx); +void func_8084F608(Player* this, GlobalContext* globalCtx); +void func_8084F698(Player* this, GlobalContext* globalCtx); +void func_8084F710(Player* this, GlobalContext* globalCtx); +void func_8084F88C(Player* this, GlobalContext* globalCtx); +void func_8084F9A0(Player* this, GlobalContext* globalCtx); +void func_8084F9C0(Player* this, GlobalContext* globalCtx); +void func_8084FA54(Player* this, GlobalContext* globalCtx); +void func_8084FB10(Player* this, GlobalContext* globalCtx); +void func_8084FBF4(Player* this, GlobalContext* globalCtx); +s32 func_8084FCAC(Player* this, GlobalContext* globalCtx); +void func_8084FF7C(Player* this); +void func_8085002C(Player* this); +s32 func_80850224(Player* this, GlobalContext* globalCtx); +void func_808502D0(Player* this, GlobalContext* globalCtx); +void func_808505DC(Player* this, GlobalContext* globalCtx); +void func_8085063C(Player* this, GlobalContext* globalCtx); +void func_8085076C(Player* this, GlobalContext* globalCtx); +void func_808507F4(Player* this, GlobalContext* globalCtx); +void func_80850AEC(Player* this, GlobalContext* globalCtx); +void func_80850C68(Player* this, GlobalContext* globalCtx); +void func_80850E84(Player* this, GlobalContext* globalCtx); +void func_80851008(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851030(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851050(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851094(GlobalContext* globalCtx, Player* this, void* anim); +void func_808510B4(GlobalContext* globalCtx, Player* this, void* anim); +void func_808510D4(GlobalContext* globalCtx, Player* this, void* anim); +void func_808510F4(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851114(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851134(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851154(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851174(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851194(GlobalContext* globalCtx, Player* this, void* anim); +void func_808511B4(GlobalContext* globalCtx, Player* this, void* anim); +void func_808511D4(GlobalContext* globalCtx, Player* this, void* anim); +void func_808511FC(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851248(GlobalContext* globalCtx, Player* this, void* anim); +void func_80851294(GlobalContext* globalCtx, Player* this, void* anim); +void func_808512E0(GlobalContext* globalCtx, Player* this, void* arg2); +void func_80851368(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808513BC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808514C0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_8085157C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808515A4(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851688(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851750(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851788(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851828(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808518DC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_8085190C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851998(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808519C0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808519EC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851A50(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851B90(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851BE8(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851CA4(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851D2C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851D80(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851DEC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851E28(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851E64(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851E90(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851ECC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851F84(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80851FB0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852048(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852080(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852174(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808521B8(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808521F4(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852234(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_8085225C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852280(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852358(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852388(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852298(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852328(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852480(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852450(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808524B0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808524D0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852514(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852544(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852554(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852564(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808525C0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852608(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852648(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808526EC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_8085283C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808528C8(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852944(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_808529D0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852C50(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2); +void func_80852E14(Player* this, GlobalContext* globalCtx); +s32 Player_IsDroppingFish(GlobalContext* globalCtx); +s32 Player_StartFishing(GlobalContext* globalCtx); +s32 func_80852F38(GlobalContext* globalCtx, Player* this); +s32 func_80852FFC(GlobalContext* globalCtx, Actor* actor, s32 csMode); +void func_80853080(Player* this, GlobalContext* globalCtx); +s32 Player_InflictDamage(GlobalContext* globalCtx, s32 damage); +void func_80853148(GlobalContext* globalCtx, Actor* actor); + +// .bss part 1 +static s32 D_80858AA0; +static s32 D_80858AA4; +static Vec3f D_80858AA8; +static Input* sControlInput; + +// .data + +static u8 D_80853410[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +static PlayerAgeProperties sAgeProperties[] = { + { + 56.0f, + 90.0f, + 1.0f, + 111.0f, + 70.0f, + 79.4f, + 59.0f, + 41.0f, + 19.0f, + 36.0f, + 44.8f, + 56.0f, + 68.0f, + 70.0f, + 18.0f, + 15.0f, + 70.0f, + { 9, 4671, 359 }, + { + { 8, 4694, 380 }, + { 9, 6122, 359 }, + { 8, 4694, 380 }, + { 9, 6122, 359 }, + }, + { + { 9, 6122, 359 }, + { 9, 7693, 380 }, + { 9, 6122, 359 }, + { 9, 7693, 380 }, + }, + { + { 8, 4694, 380 }, + { 9, 6122, 359 }, + }, + { + { -1592, 4694, 380 }, + { -1591, 6122, 359 }, + }, + 0, + 0x80, + &gPlayerAnim_002718, + &gPlayerAnim_002720, + &gPlayerAnim_002838, + &gPlayerAnim_002E70, + &gPlayerAnim_002E78, + { &gPlayerAnim_002E80, &gPlayerAnim_002E88, &gPlayerAnim_002D90, &gPlayerAnim_002D98 }, + { &gPlayerAnim_002D70, &gPlayerAnim_002D78 }, + { &gPlayerAnim_002E50, &gPlayerAnim_002E58 }, + { &gPlayerAnim_002E68, &gPlayerAnim_002E60 }, + }, + { + 40.0f, + 60.0f, + 11.0f / 17.0f, + 71.0f, + 50.0f, + 47.0f, + 39.0f, + 27.0f, + 19.0f, + 22.0f, + 29.6f, + 32.0f, + 48.0f, + 70.0f * (11.0f / 17.0f), + 14.0f, + 12.0f, + 55.0f, + { -24, 3565, 876 }, + { + { -24, 3474, 862 }, + { -24, 4977, 937 }, + { 8, 4694, 380 }, + { 9, 6122, 359 }, + }, + { + { -24, 4977, 937 }, + { -24, 6495, 937 }, + { 9, 6122, 359 }, + { 9, 7693, 380 }, + }, + { + { 8, 4694, 380 }, + { 9, 6122, 359 }, + }, + { + { -1592, 4694, 380 }, + { -1591, 6122, 359 }, + }, + 0x20, + 0, + &gPlayerAnim_002318, + &gPlayerAnim_002360, + &gPlayerAnim_0023A8, + &gPlayerAnim_0023E0, + &gPlayerAnim_0023E8, + { &gPlayerAnim_0023F0, &gPlayerAnim_0023F8, &gPlayerAnim_002D90, &gPlayerAnim_002D98 }, + { &gPlayerAnim_002D70, &gPlayerAnim_002D78 }, + { &gPlayerAnim_0023C0, &gPlayerAnim_0023C8 }, + { &gPlayerAnim_0023D8, &gPlayerAnim_0023D0 }, + }, +}; + +static u32 D_808535D0 = false; +static f32 D_808535D4 = 0.0f; +static s16 D_808535D8 = 0; +static s16 D_808535DC = 0; +static s32 D_808535E0 = 0; +static s32 D_808535E4 = 0; +static f32 D_808535E8 = 1.0f; +static f32 D_808535EC = 1.0f; +static u32 D_808535F0 = 0; +static u32 D_808535F4 = 0; +static s16 D_808535F8 = 0; +static s16 D_808535FC = 0; +static f32 D_80853600 = 0.0f; +static s32 D_80853604 = 0; +static s32 D_80853608 = 0; +static s32 D_8085360C = 0; +static s16 D_80853610 = 0; +static s32 D_80853614 = 0; +static s32 D_80853618 = 0; + +static u16 D_8085361C[] = { + NA_SE_VO_LI_SWEAT, + NA_SE_VO_LI_SNEEZE, + NA_SE_VO_LI_RELAX, + NA_SE_VO_LI_FALL_L, +}; + +static GetItemEntry sGetItemTable[] = { + GET_ITEM(ITEM_BOMBS_5, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_NUTS_5, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMBCHU, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOW, OBJECT_GI_BOW, GID_BOW, 0x31, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SLINGSHOT, OBJECT_GI_PACHINKO, GID_SLINGSHOT, 0x30, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOOMERANG, OBJECT_GI_BOOMERANG, GID_BOOMERANG, 0x35, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_STICK, OBJECT_GI_STICK, GID_STICK, 0x37, 0x0D, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_HOOKSHOT, OBJECT_GI_HOOKSHOT, GID_HOOKSHOT, 0x36, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_LONGSHOT, OBJECT_GI_HOOKSHOT, GID_LONGSHOT, 0x4F, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_LENS, OBJECT_GI_GLASSES, GID_LENS, 0x39, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_LETTER_ZELDA, OBJECT_GI_LETTER, GID_LETTER_ZELDA, 0x69, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_OCARINA_TIME, OBJECT_GI_OCARINA, GID_OCARINA_TIME, 0x3A, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_HAMMER, OBJECT_GI_HAMMER, GID_HAMMER, 0x38, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_COJIRO, OBJECT_GI_NIWATORI, GID_COJIRO, 0x02, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOTTLE, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x42, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_POTION_RED, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_POTION_GREEN, OBJECT_GI_LIQUID, GID_POTION_GREEN, 0x44, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_POTION_BLUE, OBJECT_GI_LIQUID, GID_POTION_BLUE, 0x45, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_FAIRY, OBJECT_GI_BOTTLE, GID_BOTTLE, 0x46, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MILK_BOTTLE, OBJECT_GI_MILK, GID_MILK, 0x98, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_LETTER_RUTO, OBJECT_GI_BOTTLE_LETTER, GID_LETTER_RUTO, 0x99, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BEAN, OBJECT_GI_BEAN, GID_BEAN, 0x48, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_MASK_SKULL, OBJECT_GI_SKJ_MASK, GID_MASK_SKULL, 0x10, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_SPOOKY, OBJECT_GI_REDEAD_MASK, GID_MASK_SPOOKY, 0x11, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_CHICKEN, OBJECT_GI_NIWATORI, GID_CHICKEN, 0x48, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_KEATON, OBJECT_GI_KI_TAN_MASK, GID_MASK_KEATON, 0x12, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_BUNNY, OBJECT_GI_RABIT_MASK, GID_MASK_BUNNY, 0x13, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_TRUTH, OBJECT_GI_TRUTH_MASK, GID_MASK_TRUTH, 0x17, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_POCKET_EGG, OBJECT_GI_EGG, GID_EGG, 0x01, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_POCKET_CUCCO, OBJECT_GI_NIWATORI, GID_CHICKEN, 0x48, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_ODD_MUSHROOM, OBJECT_GI_MUSHROOM, GID_ODD_MUSHROOM, 0x03, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_ODD_POTION, OBJECT_GI_POWDER, GID_ODD_POTION, 0x04, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SAW, OBJECT_GI_SAW, GID_SAW, 0x05, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SWORD_BROKEN, OBJECT_GI_BROKENSWORD, GID_SWORD_BROKEN, 0x08, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_PRESCRIPTION, OBJECT_GI_PRESCRIPTION, GID_PRESCRIPTION, 0x09, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_FROG, OBJECT_GI_FROG, GID_FROG, 0x0D, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_EYEDROPS, OBJECT_GI_EYE_LOTION, GID_EYEDROPS, 0x0E, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_CLAIM_CHECK, OBJECT_GI_TICKETSTONE, GID_CLAIM_CHECK, 0x0A, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SWORD_KOKIRI, OBJECT_GI_SWORD_1, GID_SWORD_KOKIRI, 0xA4, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SWORD_BGS, OBJECT_GI_LONGSWORD, GID_SWORD_BGS, 0x4B, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SHIELD_DEKU, OBJECT_GI_SHIELD_1, GID_SHIELD_DEKU, 0x4C, 0xA0, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_SHIELD_HYLIAN, OBJECT_GI_SHIELD_2, GID_SHIELD_HYLIAN, 0x4D, 0xA0, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_SHIELD_MIRROR, OBJECT_GI_SHIELD_3, GID_SHIELD_MIRROR, 0x4E, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_TUNIC_GORON, OBJECT_GI_CLOTHES, GID_TUNIC_GORON, 0x50, 0xA0, CHEST_ANIM_LONG), + GET_ITEM(ITEM_TUNIC_ZORA, OBJECT_GI_CLOTHES, GID_TUNIC_ZORA, 0x51, 0xA0, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOOTS_IRON, OBJECT_GI_BOOTS_2, GID_BOOTS_IRON, 0x53, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOOTS_HOVER, OBJECT_GI_HOVERBOOTS, GID_BOOTS_HOVER, 0x54, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_QUIVER_40, OBJECT_GI_ARROWCASE, GID_QUIVER_40, 0x56, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_QUIVER_50, OBJECT_GI_ARROWCASE, GID_QUIVER_50, 0x57, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOMB_BAG_20, OBJECT_GI_BOMBPOUCH, GID_BOMB_BAG_20, 0x58, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOMB_BAG_30, OBJECT_GI_BOMBPOUCH, GID_BOMB_BAG_30, 0x59, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BOMB_BAG_40, OBJECT_GI_BOMBPOUCH, GID_BOMB_BAG_40, 0x5A, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_GAUNTLETS_SILVER, OBJECT_GI_GLOVES, GID_GAUNTLETS_SILVER, 0x5B, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_GAUNTLETS_GOLD, OBJECT_GI_GLOVES, GID_GAUNTLETS_GOLD, 0x5C, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SCALE_SILVER, OBJECT_GI_SCALE, GID_SCALE_SILVER, 0xCD, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SCALE_GOLDEN, OBJECT_GI_SCALE, GID_SCALE_GOLDEN, 0xCE, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_STONE_OF_AGONY, OBJECT_GI_MAP, GID_STONE_OF_AGONY, 0x68, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_GERUDO_CARD, OBJECT_GI_GERUDO, GID_GERUDO_CARD, 0x7B, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_OCARINA_FAIRY, OBJECT_GI_OCARINA_0, GID_OCARINA_FAIRY, 0x3A, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SEEDS, OBJECT_GI_SEED, GID_SEEDS, 0xDC, 0x50, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_HEART_CONTAINER, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xC6, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_HEART_PIECE_2, OBJECT_GI_HEARTS, GID_HEART_PIECE, 0xC2, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_KEY_BOSS, OBJECT_GI_BOSSKEY, GID_KEY_BOSS, 0xC7, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_COMPASS, OBJECT_GI_COMPASS, GID_COMPASS, 0x67, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_DUNGEON_MAP, OBJECT_GI_MAP, GID_DUNGEON_MAP, 0x66, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0x60, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_MAGIC_SMALL, OBJECT_GI_MAGICPOT, GID_MAGIC_SMALL, 0x52, 0x6F, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_MAGIC_LARGE, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0x52, 0x6E, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_WALLET_ADULT, OBJECT_GI_PURSE, GID_WALLET_ADULT, 0x5E, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_WALLET_GIANT, OBJECT_GI_PURSE, GID_WALLET_GIANT, 0x5F, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_WEIRD_EGG, OBJECT_GI_EGG, GID_EGG, 0x9A, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_HEART, OBJECT_GI_HEART, GID_HEART, 0x55, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_ARROWS_SMALL, OBJECT_GI_ARROW, GID_ARROWS_SMALL, 0xE6, 0x48, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_ARROWS_MEDIUM, OBJECT_GI_ARROW, GID_ARROWS_MEDIUM, 0xE6, 0x49, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_ARROWS_LARGE, OBJECT_GI_ARROW, GID_ARROWS_LARGE, 0xE6, 0x4A, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_GREEN, OBJECT_GI_RUPY, GID_RUPEE_GREEN, 0x6F, 0x00, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_BLUE, OBJECT_GI_RUPY, GID_RUPEE_BLUE, 0xCC, 0x01, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_RED, OBJECT_GI_RUPY, GID_RUPEE_RED, 0xF0, 0x02, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_HEART_CONTAINER, OBJECT_GI_HEARTS, GID_HEART_CONTAINER, 0xC6, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MILK, OBJECT_GI_MILK, GID_MILK, 0x98, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_GORON, OBJECT_GI_GOLONMASK, GID_MASK_GORON, 0x14, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_ZORA, OBJECT_GI_ZORAMASK, GID_MASK_ZORA, 0x15, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_MASK_GERUDO, OBJECT_GI_GERUDOMASK, GID_MASK_GERUDO, 0x16, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BRACELET, OBJECT_GI_BRACELET, GID_BRACELET, 0x79, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_RUPEE_PURPLE, OBJECT_GI_RUPY, GID_RUPEE_PURPLE, 0xF1, 0x14, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_GOLD, OBJECT_GI_RUPY, GID_RUPEE_GOLD, 0xF2, 0x13, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_SWORD_BGS, OBJECT_GI_LONGSWORD, GID_SWORD_BGS, 0x0C, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_ARROW_FIRE, OBJECT_GI_M_ARROW, GID_ARROW_FIRE, 0x70, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_ARROW_ICE, OBJECT_GI_M_ARROW, GID_ARROW_ICE, 0x71, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_ARROW_LIGHT, OBJECT_GI_M_ARROW, GID_ARROW_LIGHT, 0x72, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_SKULL_TOKEN, OBJECT_GI_SUTARU, GID_SKULL_TOKEN, 0xB4, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_DINS_FIRE, OBJECT_GI_GODDESS, GID_DINS_FIRE, 0xAD, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_FARORES_WIND, OBJECT_GI_GODDESS, GID_FARORES_WIND, 0xAE, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_NAYRUS_LOVE, OBJECT_GI_GODDESS, GID_NAYRUS_LOVE, 0xAF, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BULLET_BAG_30, OBJECT_GI_DEKUPOUCH, GID_BULLET_BAG, 0x07, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BULLET_BAG_40, OBJECT_GI_DEKUPOUCH, GID_BULLET_BAG, 0x07, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_STICKS_5, OBJECT_GI_STICK, GID_STICK, 0x37, 0x0D, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_STICKS_10, OBJECT_GI_STICK, GID_STICK, 0x37, 0x0D, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_NUTS_5, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_NUTS_10, OBJECT_GI_NUTS, GID_NUTS, 0x34, 0x0C, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMB, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMBS_10, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMBS_20, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMBS_30, OBJECT_GI_BOMB_1, GID_BOMB, 0x32, 0x59, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_SEEDS_30, OBJECT_GI_SEED, GID_SEEDS, 0xDC, 0x50, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMBCHUS_5, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BOMBCHUS_20, OBJECT_GI_BOMB_2, GID_BOMBCHU, 0x33, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_FISH, OBJECT_GI_FISH, GID_FISH, 0x47, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BUG, OBJECT_GI_INSECT, GID_BUG, 0x7A, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BLUE_FIRE, OBJECT_GI_FIRE, GID_BLUE_FIRE, 0x5D, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_POE, OBJECT_GI_GHOST, GID_POE, 0x97, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_BIG_POE, OBJECT_GI_GHOST, GID_BIG_POE, 0xF9, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_KEY_SMALL, OBJECT_GI_KEY, GID_KEY_SMALL, 0xF3, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_GREEN, OBJECT_GI_RUPY, GID_RUPEE_GREEN, 0xF4, 0x00, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_BLUE, OBJECT_GI_RUPY, GID_RUPEE_BLUE, 0xF5, 0x01, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_RED, OBJECT_GI_RUPY, GID_RUPEE_RED, 0xF6, 0x02, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_RUPEE_PURPLE, OBJECT_GI_RUPY, GID_RUPEE_PURPLE, 0xF7, 0x14, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_HEART_PIECE_2, OBJECT_GI_HEARTS, GID_HEART_PIECE, 0xFA, 0x80, CHEST_ANIM_LONG), + GET_ITEM(ITEM_STICK_UPGRADE_20, OBJECT_GI_STICK, GID_STICK, 0x90, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_STICK_UPGRADE_30, OBJECT_GI_STICK, GID_STICK, 0x91, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_NUT_UPGRADE_30, OBJECT_GI_NUTS, GID_NUTS, 0xA7, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_NUT_UPGRADE_40, OBJECT_GI_NUTS, GID_NUTS, 0xA8, 0x80, CHEST_ANIM_SHORT), + GET_ITEM(ITEM_BULLET_BAG_50, OBJECT_GI_DEKUPOUCH, GID_BULLET_BAG_50, 0x6C, 0x80, CHEST_ANIM_LONG), + GET_ITEM_NONE, + GET_ITEM_NONE, +}; + +static LinkAnimationHeader* D_80853914[PLAYER_ANIMGROUP_MAX][PLAYER_ANIMTYPE_MAX] = { + /* PLAYER_ANIMGROUP_0 */ + { &gPlayerAnim_003240, &gPlayerAnim_003238, &gPlayerAnim_003238, &gPlayerAnim_002BE0, &gPlayerAnim_003240, + &gPlayerAnim_003240 }, + /* PLAYER_ANIMGROUP_1 */ + { &gPlayerAnim_003290, &gPlayerAnim_003268, &gPlayerAnim_003268, &gPlayerAnim_002BF8, &gPlayerAnim_003290, + &gPlayerAnim_003290 }, + /* PLAYER_ANIMGROUP_2 */ + { &gPlayerAnim_003140, &gPlayerAnim_002B38, &gPlayerAnim_003138, &gPlayerAnim_002B40, &gPlayerAnim_003140, + &gPlayerAnim_003140 }, + /* PLAYER_ANIMGROUP_3 */ + { &gPlayerAnim_002E98, &gPlayerAnim_0029E8, &gPlayerAnim_002E98, &gPlayerAnim_0029F0, &gPlayerAnim_002E98, + &gPlayerAnim_002E98 }, + /* PLAYER_ANIMGROUP_4 */ + { &gPlayerAnim_002FB0, &gPlayerAnim_002FA8, &gPlayerAnim_002FB0, &gPlayerAnim_002A40, &gPlayerAnim_002FB0, + &gPlayerAnim_002FB0 }, + /* PLAYER_ANIMGROUP_5 */ + { &gPlayerAnim_003220, &gPlayerAnim_002590, &gPlayerAnim_002590, &gPlayerAnim_002BC0, &gPlayerAnim_003220, + &gPlayerAnim_003220 }, + /* PLAYER_ANIMGROUP_6 */ + { &gPlayerAnim_003230, &gPlayerAnim_0025D0, &gPlayerAnim_0025D0, &gPlayerAnim_002BD0, &gPlayerAnim_003230, + &gPlayerAnim_003230 }, + /* PLAYER_ANIMGROUP_7 */ + { &gPlayerAnim_002BB0, &gPlayerAnim_0031F8, &gPlayerAnim_0031F8, &gPlayerAnim_002BB0, &gPlayerAnim_002BB0, + &gPlayerAnim_002BB0 }, + /* PLAYER_ANIMGROUP_8 */ + { &gPlayerAnim_003088, &gPlayerAnim_002A70, &gPlayerAnim_002A70, &gPlayerAnim_003088, &gPlayerAnim_003088, + &gPlayerAnim_003088 }, + /* PLAYER_ANIMGROUP_9 */ + { &gPlayerAnim_002750, &gPlayerAnim_002748, &gPlayerAnim_002748, &gPlayerAnim_002750, &gPlayerAnim_002750, + &gPlayerAnim_002750 }, + /* PLAYER_ANIMGROUP_10 */ + { &gPlayerAnim_002330, &gPlayerAnim_002330, &gPlayerAnim_002330, &gPlayerAnim_002330, &gPlayerAnim_002330, + &gPlayerAnim_002330 }, + /* PLAYER_ANIMGROUP_11 */ + { &gPlayerAnim_002760, &gPlayerAnim_002758, &gPlayerAnim_002758, &gPlayerAnim_002760, &gPlayerAnim_002760, + &gPlayerAnim_002760 }, + /* PLAYER_ANIMGROUP_12 */ + { &gPlayerAnim_002338, &gPlayerAnim_002338, &gPlayerAnim_002338, &gPlayerAnim_002338, &gPlayerAnim_002338, + &gPlayerAnim_002338 }, + /* PLAYER_ANIMGROUP_13 */ + { &gPlayerAnim_002E08, &gPlayerAnim_002E00, &gPlayerAnim_002E00, &gPlayerAnim_002E08, &gPlayerAnim_002E08, + &gPlayerAnim_002E08 }, + /* PLAYER_ANIMGROUP_14 */ + { &gPlayerAnim_003028, &gPlayerAnim_003020, &gPlayerAnim_003020, &gPlayerAnim_003028, &gPlayerAnim_003028, + &gPlayerAnim_003028 }, + /* PLAYER_ANIMGROUP_15 */ + { &gPlayerAnim_003170, &gPlayerAnim_003168, &gPlayerAnim_003168, &gPlayerAnim_003170, &gPlayerAnim_003170, + &gPlayerAnim_003170 }, + /* PLAYER_ANIMGROUP_16 */ + { &gPlayerAnim_003038, &gPlayerAnim_003030, &gPlayerAnim_003030, &gPlayerAnim_002A68, &gPlayerAnim_003038, + &gPlayerAnim_003038 }, + /* PLAYER_ANIMGROUP_17 */ + { &gPlayerAnim_002FC0, &gPlayerAnim_002FB8, &gPlayerAnim_002FB8, &gPlayerAnim_002FC8, &gPlayerAnim_002FC0, + &gPlayerAnim_002FC0 }, + /* PLAYER_ANIMGROUP_18 */ + { &gPlayerAnim_003278, &gPlayerAnim_003270, &gPlayerAnim_003270, &gPlayerAnim_002BE8, &gPlayerAnim_003278, + &gPlayerAnim_003278 }, + /* PLAYER_ANIMGROUP_19 */ + { &gPlayerAnim_003288, &gPlayerAnim_003280, &gPlayerAnim_003280, &gPlayerAnim_002BF0, &gPlayerAnim_003288, + &gPlayerAnim_003288 }, + /* PLAYER_ANIMGROUP_20 */ + { &gPlayerAnim_002EB8, &gPlayerAnim_002EA0, &gPlayerAnim_002EA0, &gPlayerAnim_002EB8, &gPlayerAnim_0026C8, + &gPlayerAnim_002EB8 }, + /* PLAYER_ANIMGROUP_21 */ + { &gPlayerAnim_002ED8, &gPlayerAnim_002ED0, &gPlayerAnim_002ED0, &gPlayerAnim_002ED8, &gPlayerAnim_0026D0, + &gPlayerAnim_002ED8 }, + /* PLAYER_ANIMGROUP_22 */ + { &gPlayerAnim_002EB0, &gPlayerAnim_002EA8, &gPlayerAnim_002EA8, &gPlayerAnim_002EB0, &gPlayerAnim_002EB0, + &gPlayerAnim_002EB0 }, + /* PLAYER_ANIMGROUP_23 */ + { &gPlayerAnim_003190, &gPlayerAnim_003188, &gPlayerAnim_003188, &gPlayerAnim_002B68, &gPlayerAnim_003190, + &gPlayerAnim_003190 }, + /* PLAYER_ANIMGROUP_24 */ + { &gPlayerAnim_003178, &gPlayerAnim_002568, &gPlayerAnim_002568, &gPlayerAnim_002B58, &gPlayerAnim_003178, + &gPlayerAnim_003178 }, + /* PLAYER_ANIMGROUP_25 */ + { &gPlayerAnim_003180, &gPlayerAnim_002570, &gPlayerAnim_002570, &gPlayerAnim_002B60, &gPlayerAnim_003180, + &gPlayerAnim_003180 }, + /* PLAYER_ANIMGROUP_26 */ + { &gPlayerAnim_002D60, &gPlayerAnim_002D58, &gPlayerAnim_002D58, &gPlayerAnim_002D60, &gPlayerAnim_002D60, + &gPlayerAnim_002D60 }, + /* PLAYER_ANIMGROUP_27 */ + { &gPlayerAnim_002BB8, &gPlayerAnim_003218, &gPlayerAnim_003218, &gPlayerAnim_002BB8, &gPlayerAnim_002BB8, + &gPlayerAnim_002BB8 }, + /* PLAYER_ANIMGROUP_28 */ + { &gPlayerAnim_002BC8, &gPlayerAnim_003228, &gPlayerAnim_003228, &gPlayerAnim_002BC8, &gPlayerAnim_002BC8, + &gPlayerAnim_002BC8 }, + /* PLAYER_ANIMGROUP_29 */ + { &gPlayerAnim_0031C8, &gPlayerAnim_0031C0, &gPlayerAnim_0031C0, &gPlayerAnim_0031C8, &gPlayerAnim_0031C8, + &gPlayerAnim_0031C8 }, + /* PLAYER_ANIMGROUP_30 */ + { &gPlayerAnim_003118, &gPlayerAnim_003110, &gPlayerAnim_003110, &gPlayerAnim_003118, &gPlayerAnim_003118, + &gPlayerAnim_003118 }, + /* PLAYER_ANIMGROUP_31 */ + { &gPlayerAnim_002DE8, &gPlayerAnim_002DE8, &gPlayerAnim_002DE8, &gPlayerAnim_002DE8, &gPlayerAnim_002DE8, + &gPlayerAnim_002DE8 }, + /* PLAYER_ANIMGROUP_32 */ + { &gPlayerAnim_002E30, &gPlayerAnim_002E18, &gPlayerAnim_002E18, &gPlayerAnim_002E30, &gPlayerAnim_002E30, + &gPlayerAnim_002E30 }, + /* PLAYER_ANIMGROUP_33 */ + { &gPlayerAnim_002E40, &gPlayerAnim_002E38, &gPlayerAnim_002E38, &gPlayerAnim_002E40, &gPlayerAnim_002E40, + &gPlayerAnim_002E40 }, + /* PLAYER_ANIMGROUP_34 */ + { &gPlayerAnim_002E28, &gPlayerAnim_002E20, &gPlayerAnim_002E20, &gPlayerAnim_002E28, &gPlayerAnim_002E28, + &gPlayerAnim_002E28 }, + /* PLAYER_ANIMGROUP_35 */ + { &gPlayerAnim_0030C8, &gPlayerAnim_0030C0, &gPlayerAnim_0030C0, &gPlayerAnim_0030C8, &gPlayerAnim_0030C8, + &gPlayerAnim_0030C8 }, + /* PLAYER_ANIMGROUP_36 */ + { &gPlayerAnim_0030D8, &gPlayerAnim_0030D0, &gPlayerAnim_0030D0, &gPlayerAnim_0030D8, &gPlayerAnim_0030D8, + &gPlayerAnim_0030D8 }, + /* PLAYER_ANIMGROUP_37 */ + { &gPlayerAnim_0030B8, &gPlayerAnim_0030B0, &gPlayerAnim_0030B0, &gPlayerAnim_0030B8, &gPlayerAnim_0030B8, + &gPlayerAnim_0030B8 }, + /* PLAYER_ANIMGROUP_38 */ + { &gPlayerAnim_002F20, &gPlayerAnim_002F18, &gPlayerAnim_002F18, &gPlayerAnim_002F20, &gPlayerAnim_002F20, + &gPlayerAnim_002F20 }, + /* PLAYER_ANIMGROUP_39 */ + { &gPlayerAnim_002FF0, &gPlayerAnim_002FE8, &gPlayerAnim_002FE8, &gPlayerAnim_002FF0, &gPlayerAnim_002FF0, + &gPlayerAnim_002FF0 }, + /* PLAYER_ANIMGROUP_40 */ + { &gPlayerAnim_003010, &gPlayerAnim_003008, &gPlayerAnim_003008, &gPlayerAnim_003010, &gPlayerAnim_003010, + &gPlayerAnim_003010 }, + /* PLAYER_ANIMGROUP_41 */ + { &gPlayerAnim_003000, &gPlayerAnim_002FF8, &gPlayerAnim_002FF8, &gPlayerAnim_003000, &gPlayerAnim_003000, + &gPlayerAnim_003000 }, + /* PLAYER_ANIMGROUP_42 */ + { &gPlayerAnim_002EF0, &gPlayerAnim_002EE8, &gPlayerAnim_002EE8, &gPlayerAnim_002EF8, &gPlayerAnim_002EF0, + &gPlayerAnim_002EF0 }, + /* PLAYER_ANIMGROUP_43 */ + { &gPlayerAnim_0031E0, &gPlayerAnim_0031D8, &gPlayerAnim_0031D8, &gPlayerAnim_0031E8, &gPlayerAnim_0031E0, + &gPlayerAnim_0031E0 }, + /* PLAYER_ANIMGROUP_44 */ + { &gPlayerAnim_003468, &gPlayerAnim_003438, &gPlayerAnim_003438, &gPlayerAnim_003468, &gPlayerAnim_003468, + &gPlayerAnim_003468 }, +}; + +static LinkAnimationHeader* D_80853D4C[][3] = { + { &gPlayerAnim_002A28, &gPlayerAnim_002A38, &gPlayerAnim_002A30 }, + { &gPlayerAnim_002950, &gPlayerAnim_002960, &gPlayerAnim_002958 }, + { &gPlayerAnim_0029D0, &gPlayerAnim_0029E0, &gPlayerAnim_0029D8 }, + { &gPlayerAnim_002988, &gPlayerAnim_002998, &gPlayerAnim_002990 }, +}; + +static LinkAnimationHeader* D_80853D7C[][2] = { + { &gPlayerAnim_003248, &gPlayerAnim_003200 }, { &gPlayerAnim_003258, &gPlayerAnim_003210 }, + { &gPlayerAnim_003250, &gPlayerAnim_003208 }, { &gPlayerAnim_003250, &gPlayerAnim_003208 }, + { &gPlayerAnim_003430, &gPlayerAnim_0033F0 }, { &gPlayerAnim_003430, &gPlayerAnim_0033F0 }, + { &gPlayerAnim_003430, &gPlayerAnim_0033F0 }, { &gPlayerAnim_0033F8, &gPlayerAnim_0033D0 }, + { &gPlayerAnim_003400, &gPlayerAnim_0033D8 }, { &gPlayerAnim_003420, &gPlayerAnim_003420 }, + { &gPlayerAnim_003408, &gPlayerAnim_0033E0 }, { &gPlayerAnim_003410, &gPlayerAnim_0033E8 }, + { &gPlayerAnim_003418, &gPlayerAnim_003418 }, { &gPlayerAnim_003428, &gPlayerAnim_003428 } +}; + +static struct_80832924 D_80853DEC[] = { + { NA_SE_VO_LI_SNEEZE, -0x2008 }, +}; + +static struct_80832924 D_80853DF0[] = { + { NA_SE_VO_LI_SWEAT, -0x2012 }, +}; + +static struct_80832924 D_80853DF4[] = { + { NA_SE_VO_LI_BREATH_REST, -0x200D }, +}; + +static struct_80832924 D_80853DF8[] = { + { NA_SE_VO_LI_BREATH_REST, -0x200A }, +}; + +static struct_80832924 D_80853DFC[] = { + { NA_SE_PL_CALM_HIT, 0x82C }, { NA_SE_PL_CALM_HIT, 0x830 }, { NA_SE_PL_CALM_HIT, 0x834 }, + { NA_SE_PL_CALM_HIT, 0x838 }, { NA_SE_PL_CALM_HIT, -0x83C }, +}; + +static struct_80832924 D_80853E10[] = { + { 0, 0x4019 }, { 0, 0x401E }, { 0, 0x402C }, { 0, 0x4030 }, { 0, 0x4034 }, { 0, -0x4038 }, +}; + +static struct_80832924 D_80853E28[] = { + { NA_SE_IT_SHIELD_POSTURE, 0x810 }, + { NA_SE_IT_SHIELD_POSTURE, 0x814 }, + { NA_SE_IT_SHIELD_POSTURE, -0x846 }, +}; + +static struct_80832924 D_80853E34[] = { + { NA_SE_IT_HAMMER_SWING, 0x80A }, + { NA_SE_VO_LI_AUTO_JUMP, 0x200A }, + { NA_SE_IT_SWORD_SWING, 0x816 }, + { NA_SE_VO_LI_SWORD_N, -0x2016 }, +}; + +static struct_80832924 D_80853E44[] = { + { NA_SE_IT_SWORD_SWING, 0x827 }, + { NA_SE_VO_LI_SWORD_N, -0x2027 }, +}; + +static struct_80832924 D_80853E4C[] = { + { NA_SE_VO_LI_RELAX, -0x2014 }, +}; + +static struct_80832924* D_80853E50[] = { + D_80853DEC, D_80853DF0, D_80853DF4, D_80853DF8, D_80853DFC, D_80853E10, + D_80853E28, D_80853E34, D_80853E44, D_80853E4C, NULL, +}; + +static u8 D_80853E7C[] = { + 0, 0, 1, 1, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 3, 3, 4, 4, 8, 8, 5, 5, 6, 6, 7, 7, 9, 9, 0, +}; + +// Used to map item IDs to action params +static s8 sItemActionParams[] = { + PLAYER_AP_STICK, + PLAYER_AP_NUT, + PLAYER_AP_BOMB, + PLAYER_AP_BOW, + PLAYER_AP_BOW_FIRE, + PLAYER_AP_DINS_FIRE, + PLAYER_AP_SLINGSHOT, + PLAYER_AP_OCARINA_FAIRY, + PLAYER_AP_OCARINA_TIME, + PLAYER_AP_BOMBCHU, + PLAYER_AP_HOOKSHOT, + PLAYER_AP_LONGSHOT, + PLAYER_AP_BOW_ICE, + PLAYER_AP_FARORES_WIND, + PLAYER_AP_BOOMERANG, + PLAYER_AP_LENS, + PLAYER_AP_BEAN, + PLAYER_AP_HAMMER, + PLAYER_AP_BOW_LIGHT, + PLAYER_AP_NAYRUS_LOVE, + PLAYER_AP_BOTTLE, + PLAYER_AP_BOTTLE_POTION_RED, + PLAYER_AP_BOTTLE_POTION_GREEN, + PLAYER_AP_BOTTLE_POTION_BLUE, + PLAYER_AP_BOTTLE_FAIRY, + PLAYER_AP_BOTTLE_FISH, + PLAYER_AP_BOTTLE_MILK, + PLAYER_AP_BOTTLE_LETTER, + PLAYER_AP_BOTTLE_FIRE, + PLAYER_AP_BOTTLE_BUG, + PLAYER_AP_BOTTLE_BIG_POE, + PLAYER_AP_BOTTLE_MILK_HALF, + PLAYER_AP_BOTTLE_POE, + PLAYER_AP_WEIRD_EGG, + PLAYER_AP_CHICKEN, + PLAYER_AP_LETTER_ZELDA, + PLAYER_AP_MASK_KEATON, + PLAYER_AP_MASK_SKULL, + PLAYER_AP_MASK_SPOOKY, + PLAYER_AP_MASK_BUNNY, + PLAYER_AP_MASK_GORON, + PLAYER_AP_MASK_ZORA, + PLAYER_AP_MASK_GERUDO, + PLAYER_AP_MASK_TRUTH, + PLAYER_AP_SWORD_MASTER, + PLAYER_AP_POCKET_EGG, + PLAYER_AP_POCKET_CUCCO, + PLAYER_AP_COJIRO, + PLAYER_AP_ODD_MUSHROOM, + PLAYER_AP_ODD_POTION, + PLAYER_AP_SAW, + PLAYER_AP_SWORD_BROKEN, + PLAYER_AP_PRESCRIPTION, + PLAYER_AP_FROG, + PLAYER_AP_EYEDROPS, + PLAYER_AP_CLAIM_CHECK, + PLAYER_AP_BOW_FIRE, + PLAYER_AP_BOW_ICE, + PLAYER_AP_BOW_LIGHT, + PLAYER_AP_SWORD_KOKIRI, + PLAYER_AP_SWORD_MASTER, + PLAYER_AP_SWORD_BGS, +}; + +static s32(*D_80853EDC[])(Player* this, GlobalContext* globalCtx) = { + func_8083485C, func_8083485C, func_8083485C, func_808349DC, func_808349DC, func_808349DC, func_8083485C, + func_8083485C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_8083501C, + func_8083501C, func_8083501C, func_8083501C, func_8083501C, func_808356E8, func_808356E8, func_80835800, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, func_8083485C, + func_8083485C, func_8083485C, func_8083485C, func_8083485C, +}; + +static void (*D_80853FE8[])(GlobalContext* globalCtx, Player* this) = { + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_8083377C, + func_80833790, func_8083379C, func_8083379C, func_8083379C, func_8083379C, func_8083379C, func_8083379C, + func_8083379C, func_8083379C, func_80833910, func_80833910, func_808337D4, func_808337D4, func_80833984, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, func_80833770, + func_80833770, func_80833770, func_80833770, func_80833770, +}; + +typedef enum { + /* 0 */ PLAYER_D_808540F4_0, + /* 1 */ PLAYER_D_808540F4_1, + /* 2 */ PLAYER_D_808540F4_2, + /* 3 */ PLAYER_D_808540F4_3, + /* 4 */ PLAYER_D_808540F4_4, + /* 5 */ PLAYER_D_808540F4_5, + /* 6 */ PLAYER_D_808540F4_6, + /* 7 */ PLAYER_D_808540F4_7, + /* 8 */ PLAYER_D_808540F4_8, + /* 9 */ PLAYER_D_808540F4_9, + /* 10 */ PLAYER_D_808540F4_10, + /* 11 */ PLAYER_D_808540F4_11, + /* 12 */ PLAYER_D_808540F4_12, + /* 13 */ PLAYER_D_808540F4_13, + /* 14 */ PLAYER_D_808540F4_MAX +} PlayerD_808540F4Index; + +static struct_808540F4 D_808540F4[PLAYER_D_808540F4_MAX] = { + /* PLAYER_D_808540F4_0 */ { &gPlayerAnim_002F50, 12 }, + /* PLAYER_D_808540F4_1 */ { &gPlayerAnim_003080, 6 }, + /* PLAYER_D_808540F4_2 */ { &gPlayerAnim_002C68, 8 }, + /* PLAYER_D_808540F4_3 */ { &gPlayerAnim_003090, 8 }, + /* PLAYER_D_808540F4_4 */ { &gPlayerAnim_002A20, 8 }, + /* PLAYER_D_808540F4_5 */ { &gPlayerAnim_002F30, 10 }, + /* PLAYER_D_808540F4_6 */ { &gPlayerAnim_002C58, 7 }, + /* PLAYER_D_808540F4_7 */ { &gPlayerAnim_002C60, 11 }, + /* PLAYER_D_808540F4_8 */ { &gPlayerAnim_002F50, 12 }, + /* PLAYER_D_808540F4_9 */ { &gPlayerAnim_003078, 4 }, + /* PLAYER_D_808540F4_10 */ { &gPlayerAnim_003058, 4 }, + /* PLAYER_D_808540F4_11 */ { &gPlayerAnim_002F38, 4 }, + /* PLAYER_D_808540F4_12 */ { &gPlayerAnim_0024E0, 5 }, + /* PLAYER_D_808540F4_13 */ { &gPlayerAnim_002F48, 13 }, +}; + +static s8 D_80854164[PLAYER_ANIMTYPE_MAX][PLAYER_ANIMTYPE_MAX] = { + { PLAYER_D_808540F4_8, -PLAYER_D_808540F4_5, -PLAYER_D_808540F4_3, -PLAYER_D_808540F4_6, PLAYER_D_808540F4_8, + PLAYER_D_808540F4_11 }, + { PLAYER_D_808540F4_5, PLAYER_D_808540F4_0, -PLAYER_D_808540F4_1, PLAYER_D_808540F4_4, PLAYER_D_808540F4_5, + PLAYER_D_808540F4_9 }, + { PLAYER_D_808540F4_3, PLAYER_D_808540F4_1, PLAYER_D_808540F4_0, PLAYER_D_808540F4_2, PLAYER_D_808540F4_3, + PLAYER_D_808540F4_9 }, + { PLAYER_D_808540F4_6, -PLAYER_D_808540F4_4, -PLAYER_D_808540F4_2, PLAYER_D_808540F4_7, PLAYER_D_808540F4_6, + PLAYER_D_808540F4_10 }, + { PLAYER_D_808540F4_8, -PLAYER_D_808540F4_5, -PLAYER_D_808540F4_3, -PLAYER_D_808540F4_6, PLAYER_D_808540F4_8, + PLAYER_D_808540F4_11 }, + { PLAYER_D_808540F4_8, -PLAYER_D_808540F4_5, -PLAYER_D_808540F4_3, -PLAYER_D_808540F4_6, PLAYER_D_808540F4_8, + PLAYER_D_808540F4_11 }, +}; + +static ExplosiveInfo sExplosiveInfos[] = { + { ITEM_BOMB, ACTOR_EN_BOM }, + { ITEM_BOMBCHU, ACTOR_EN_BOM_CHU }, +}; + +static struct_80854190 D_80854190[] = { + { &gPlayerAnim_002A80, &gPlayerAnim_002A90, &gPlayerAnim_002A88, 1, 4 }, + { &gPlayerAnim_0028C0, &gPlayerAnim_0028C8, &gPlayerAnim_002498, 1, 4 }, + { &gPlayerAnim_002A98, &gPlayerAnim_002AA0, &gPlayerAnim_002540, 0, 5 }, + { &gPlayerAnim_0028D0, &gPlayerAnim_0028D8, &gPlayerAnim_0024A0, 1, 7 }, + { &gPlayerAnim_002968, &gPlayerAnim_002970, &gPlayerAnim_0024C0, 1, 4 }, + { &gPlayerAnim_002880, &gPlayerAnim_002888, &gPlayerAnim_002478, 0, 5 }, + { &gPlayerAnim_002978, &gPlayerAnim_002980, &gPlayerAnim_0024C8, 2, 8 }, + { &gPlayerAnim_002890, &gPlayerAnim_002898, &gPlayerAnim_002480, 3, 8 }, + { &gPlayerAnim_0029A0, &gPlayerAnim_0029A8, &gPlayerAnim_0024D0, 0, 4 }, + { &gPlayerAnim_0028A0, &gPlayerAnim_0028A8, &gPlayerAnim_002488, 0, 5 }, + { &gPlayerAnim_0029B0, &gPlayerAnim_0029B8, &gPlayerAnim_0024D8, 0, 6 }, + { &gPlayerAnim_0028B0, &gPlayerAnim_0028B8, &gPlayerAnim_002490, 1, 5 }, + { &gPlayerAnim_002AA8, &gPlayerAnim_002AB0, &gPlayerAnim_002548, 0, 3 }, + { &gPlayerAnim_0028E0, &gPlayerAnim_0028E8, &gPlayerAnim_0024A8, 0, 3 }, + { &gPlayerAnim_002AB8, &gPlayerAnim_002AC0, &gPlayerAnim_002550, 1, 9 }, + { &gPlayerAnim_0028F0, &gPlayerAnim_0028F8, &gPlayerAnim_0024B0, 1, 8 }, + { &gPlayerAnim_002A60, &gPlayerAnim_002A50, &gPlayerAnim_002A50, 1, 10 }, + { &gPlayerAnim_002900, &gPlayerAnim_002910, &gPlayerAnim_002910, 1, 11 }, + { &gPlayerAnim_002A50, &gPlayerAnim_002A58, &gPlayerAnim_002A58, 1, 2 }, + { &gPlayerAnim_002910, &gPlayerAnim_002908, &gPlayerAnim_002908, 1, 2 }, + { &gPlayerAnim_002B80, &gPlayerAnim_002B88, &gPlayerAnim_002B88, 1, 5 }, + { &gPlayerAnim_002B70, &gPlayerAnim_002B78, &gPlayerAnim_002B78, 1, 4 }, + { &gPlayerAnim_002C40, &gPlayerAnim_002C50, &gPlayerAnim_002C48, 3, 10 }, + { &gPlayerAnim_002C70, &gPlayerAnim_002C80, &gPlayerAnim_002C78, 2, 11 }, + { &gPlayerAnim_002B28, &gPlayerAnim_002B30, &gPlayerAnim_002560, 0, 12 }, + { &gPlayerAnim_002940, &gPlayerAnim_002948, &gPlayerAnim_0024B8, 0, 15 }, + { &gPlayerAnim_0029C0, &gPlayerAnim_0029C8, &gPlayerAnim_002560, 0, 16 }, + { &gPlayerAnim_0029C0, &gPlayerAnim_0029C8, &gPlayerAnim_0024B8, 0, 16 }, +}; + +static LinkAnimationHeader* D_80854350[] = { + &gPlayerAnim_002AE8, + &gPlayerAnim_002920, +}; + +static LinkAnimationHeader* D_80854358[] = { + &gPlayerAnim_002AE0, + &gPlayerAnim_002920, +}; + +static LinkAnimationHeader* D_80854360[] = { + &gPlayerAnim_002AF0, + &gPlayerAnim_002928, +}; + +static LinkAnimationHeader* D_80854368[] = { + &gPlayerAnim_002AF8, + &gPlayerAnim_002930, +}; + +static LinkAnimationHeader* D_80854370[] = { + &gPlayerAnim_002B00, + &gPlayerAnim_002938, +}; + +static LinkAnimationHeader* D_80854378[] = { + &gPlayerAnim_002AD8, + &gPlayerAnim_002918, +}; + +static u8 D_80854380[2] = { 0x18, 0x19 }; +static u8 D_80854384[2] = { 0x1A, 0x1B }; + +static u16 D_80854388[] = { BTN_B, BTN_CLEFT, BTN_CDOWN, BTN_CRIGHT }; + +static u8 sMagicSpellCosts[] = { 12, 24, 24, 12, 24, 12 }; + +static u16 D_80854398[] = { NA_SE_IT_BOW_DRAW, NA_SE_IT_SLING_DRAW, NA_SE_IT_HOOKSHOT_READY }; + +static u8 sMagicArrowCosts[] = { 4, 4, 8 }; + +static LinkAnimationHeader* D_808543A4[] = { + &gPlayerAnim_0025C0, + &gPlayerAnim_0025C8, +}; + +static LinkAnimationHeader* D_808543AC[] = { + &gPlayerAnim_002580, + &gPlayerAnim_002588, +}; + +static LinkAnimationHeader* D_808543B4[] = { + &gPlayerAnim_002510, + &gPlayerAnim_002518, +}; + +static LinkAnimationHeader* D_808543BC[] = { + &gPlayerAnim_002510, + &gPlayerAnim_002520, +}; + +static LinkAnimationHeader* D_808543C4[] = { + &gPlayerAnim_002EC0, + &gPlayerAnim_002A08, +}; + +static LinkAnimationHeader* D_808543CC[] = { + &gPlayerAnim_0026F0, + &gPlayerAnim_002CC8, +}; + +static LinkAnimationHeader* D_808543D4[] = { + &gPlayerAnim_0026C0, + &gPlayerAnim_002CC0, +}; + +// return type can't be void due to regalloc in func_8084FCAC +s32 func_80832210(Player* this) { + this->actor.speedXZ = 0.0f; + this->linearVelocity = 0.0f; +} + +// return type can't be void due to regalloc in func_8083F72C +s32 func_80832224(Player* this) { + func_80832210(this); + this->unk_6AD = 0; +} + +s32 func_8083224C(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_8); +} + +void func_80832264(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime, anim); +} + +void func_80832284(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_PlayLoop(globalCtx, &this->skelAnime, anim); +} + +void func_808322A4(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_PlayLoopSetSpeed(globalCtx, &this->skelAnime, anim, 2.0f / 3.0f); +} + +void func_808322D0(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, anim, 2.0f / 3.0f); +} + +void func_808322FC(Player* this) { + this->actor.shape.rot.y += this->skelAnime.jointTable[1].y; + this->skelAnime.jointTable[1].y = 0; +} + +void func_80832318(Player* this) { + this->stateFlags2 &= ~PLAYER_STATE2_17; + this->swordState = 0; + this->swordInfo[0].active = this->swordInfo[1].active = this->swordInfo[2].active = 0; +} + +void func_80832340(GlobalContext* globalCtx, Player* this) { + Camera* camera; + + if (this->unk_46C != SUBCAM_NONE) { + camera = globalCtx->cameraPtrs[this->unk_46C]; + if ((camera != NULL) && (camera->csId == 1100)) { + OnePointCutscene_EndCutscene(globalCtx, this->unk_46C); + this->unk_46C = SUBCAM_NONE; + } + } + + this->stateFlags2 &= ~(PLAYER_STATE2_10 | PLAYER_STATE2_11); +} + +void func_808323B4(GlobalContext* globalCtx, Player* this) { + Actor* heldActor = this->heldActor; + + if ((heldActor != NULL) && !Player_HoldsHookshot(this)) { + this->actor.child = NULL; + this->heldActor = NULL; + this->interactRangeActor = NULL; + heldActor->parent = NULL; + this->stateFlags1 &= ~PLAYER_STATE1_11; + } + + if (Player_GetExplosiveHeld(this) >= 0) { + func_8083399C(globalCtx, this, PLAYER_AP_NONE); + this->heldItemId = ITEM_NONE_FE; + } +} + +void func_80832440(GlobalContext* globalCtx, Player* this) { + if ((this->stateFlags1 & PLAYER_STATE1_11) && (this->heldActor == NULL)) { + if (this->interactRangeActor != NULL) { + if (this->getItemId == GI_NONE) { + this->stateFlags1 &= ~PLAYER_STATE1_11; + this->interactRangeActor = NULL; + } + } + else { + this->stateFlags1 &= ~PLAYER_STATE1_11; + } + } + + func_80832318(this); + this->unk_6AD = 0; + + func_80832340(globalCtx, this); + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + + this->stateFlags1 &= ~(PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_20 | PLAYER_STATE1_21); + this->stateFlags2 &= ~(PLAYER_STATE2_4 | PLAYER_STATE2_7 | PLAYER_STATE2_18); + + this->actor.shape.rot.x = 0; + this->actor.shape.yOffset = 0.0f; + + this->unk_845 = this->unk_844 = 0; +} + +s32 func_80832528(GlobalContext* globalCtx, Player* this) { + if (this->heldItemActionParam >= PLAYER_AP_FISHING_POLE) { + func_80835F44(globalCtx, this, ITEM_NONE); + return 1; + } + else { + return 0; + } +} + +void func_80832564(GlobalContext* globalCtx, Player* this) { + func_80832440(globalCtx, this); + func_808323B4(globalCtx, this); +} + +s32 func_80832594(Player* this, s32 arg1, s32 arg2) { + s16 temp = this->unk_A80 - D_808535D8; + + this->unk_850 += arg1 + (s16)(ABS(temp) * fabsf(D_808535D4) * 2.5415802156203426e-06f); + + if (CHECK_BTN_ANY(sControlInput->press.button, BTN_A | BTN_B)) { + this->unk_850 += 5; + } + + return this->unk_850 > arg2; +} + +void func_80832630(GlobalContext* globalCtx) { + if (globalCtx->actorCtx.freezeFlashTimer == 0) { + globalCtx->actorCtx.freezeFlashTimer = 1; + } +} + +void func_8083264C(Player* this, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + if (this->actor.category == ACTORCAT_PLAYER) { + func_800AA000(arg4, arg1, arg2, arg3); + } +} + +void func_80832698(Player* this, u16 sfxId) { + if (this->actor.category == ACTORCAT_PLAYER) { + func_8002F7DC(&this->actor, sfxId + this->ageProperties->unk_92); + } + else { + func_800F4190(&this->actor.projectedPos, sfxId); + } +} + +void func_808326F0(Player* this) { + u16* entry = &D_8085361C[0]; + s32 i; + + for (i = 0; i < 4; i++) { + Audio_StopSfxById((u16)(*entry + this->ageProperties->unk_92)); + entry++; + } +} + +u16 func_8083275C(Player* this, u16 sfxId) { + return sfxId + this->unk_89E; +} + +void func_80832770(Player* this, u16 sfxId) { + func_8002F7DC(&this->actor, func_8083275C(this, sfxId)); +} + +u16 func_808327A4(Player* this, u16 sfxId) { + return sfxId + this->unk_89E + this->ageProperties->unk_94; +} + +void func_808327C4(Player* this, u16 sfxId) { + func_8002F7DC(&this->actor, func_808327A4(this, sfxId)); +} + +void func_808327F8(Player* this, f32 arg1) { + s32 sfxId; + + if (this->currentBoots == PLAYER_BOOTS_IRON) { + sfxId = NA_SE_PL_WALK_HEAVYBOOTS; + } + else { + sfxId = func_808327A4(this, NA_SE_PL_WALK_GROUND); + } + + func_800F4010(&this->actor.projectedPos, sfxId, arg1); +} + +void func_80832854(Player* this) { + s32 sfxId; + + if (this->currentBoots == PLAYER_BOOTS_IRON) { + sfxId = NA_SE_PL_JUMP_HEAVYBOOTS; + } + else { + sfxId = func_808327A4(this, NA_SE_PL_JUMP); + } + + func_8002F7DC(&this->actor, sfxId); +} + +void func_808328A0(Player* this) { + s32 sfxId; + + if (this->currentBoots == PLAYER_BOOTS_IRON) { + sfxId = NA_SE_PL_LAND_HEAVYBOOTS; + } + else { + sfxId = func_808327A4(this, NA_SE_PL_LAND); + } + + func_8002F7DC(&this->actor, sfxId); +} + +void func_808328EC(Player* this, u16 sfxId) { + func_8002F7DC(&this->actor, sfxId); + this->stateFlags2 |= PLAYER_STATE2_3; +} + +void func_80832924(Player* this, struct_80832924* entry) { + s32 data; + s32 flags; + u32 cont; + s32 pad; + + do { + data = ABS(entry->field); + flags = data & 0x7800; + if (LinkAnimation_OnFrame(&this->skelAnime, fabsf(data & 0x7FF))) { + if (flags == 0x800) { + func_8002F7DC(&this->actor, entry->sfxId); + } + else if (flags == 0x1000) { + func_80832770(this, entry->sfxId); + } + else if (flags == 0x1800) { + func_808327C4(this, entry->sfxId); + } + else if (flags == 0x2000) { + func_80832698(this, entry->sfxId); + } + else if (flags == 0x2800) { + func_808328A0(this); + } + else if (flags == 0x3000) { + func_808327F8(this, 6.0f); + } + else if (flags == 0x3800) { + func_80832854(this); + } + else if (flags == 0x4000) { + func_808327F8(this, 0.0f); + } + else if (flags == 0x4800) { + func_800F4010(&this->actor.projectedPos, this->ageProperties->unk_94 + NA_SE_PL_WALK_LADDER, 0.0f); + } + } + cont = (entry->field >= 0); + entry++; + } while (cont); +} + +void func_80832B0C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, 0.0f, Animation_GetLastFrame(anim), ANIMMODE_ONCE, + -6.0f); +} + +void func_80832B78(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 2.0f / 3.0f, 0.0f, Animation_GetLastFrame(anim), + ANIMMODE_ONCE, -6.0f); +} + +void func_80832BE8(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -6.0f); +} + +void func_80832C2C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, 0.0f, 0.0f, ANIMMODE_ONCE, 0.0f); +} + +void func_80832C6C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, 0.0f, 0.0f, ANIMMODE_LOOP, -16.0f); +} + +s32 func_80832CB0(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, anim); + return 1; + } + else { + return 0; + } +} + +void func_80832CFC(Player* this) { + this->skelAnime.prevTransl = this->skelAnime.baseTransl; + this->skelAnime.prevRot = this->actor.shape.rot.y; +} + +void func_80832D20(Player* this) { + func_80832CFC(this); + this->skelAnime.prevTransl.x *= this->ageProperties->unk_08; + this->skelAnime.prevTransl.y *= this->ageProperties->unk_08; + this->skelAnime.prevTransl.z *= this->ageProperties->unk_08; +} + +void func_80832DB0(Player* this) { + this->skelAnime.jointTable[1].y = 0; +} + +void func_80832DBC(Player* this) { + if (this->skelAnime.moveFlags != 0) { + func_808322FC(this); + this->skelAnime.jointTable[0].x = this->skelAnime.baseTransl.x; + this->skelAnime.jointTable[0].z = this->skelAnime.baseTransl.z; + if (this->skelAnime.moveFlags & 8) { + if (this->skelAnime.moveFlags & 2) { + this->skelAnime.jointTable[0].y = this->skelAnime.prevTransl.y; + } + } + else { + this->skelAnime.jointTable[0].y = this->skelAnime.baseTransl.y; + } + func_80832CFC(this); + this->skelAnime.moveFlags = 0; + } +} + +void func_80832E48(Player* this, s32 flags) { + Vec3f pos; + + this->skelAnime.moveFlags = flags; + this->skelAnime.prevTransl = this->skelAnime.baseTransl; + SkelAnime_UpdateTranslation(&this->skelAnime, &pos, this->actor.shape.rot.y); + + if (flags & 1) { + if (!LINK_IS_ADULT) { + pos.x *= 0.64f; + pos.z *= 0.64f; + } + this->actor.world.pos.x += pos.x * this->actor.scale.x; + this->actor.world.pos.z += pos.z * this->actor.scale.z; + } + + if (flags & 2) { + if (!(flags & 4)) { + pos.y *= this->ageProperties->unk_08; + } + this->actor.world.pos.y += pos.y * this->actor.scale.y; + } + + func_808322FC(this); +} + +void func_80832F54(GlobalContext* globalCtx, Player* this, s32 flags) { + if (flags & 0x200) { + func_80832D20(this); + } + else if ((flags & 0x100) || (this->skelAnime.moveFlags != 0)) { + func_80832CFC(this); + } + else { + this->skelAnime.prevTransl = this->skelAnime.jointTable[0]; + this->skelAnime.prevRot = this->actor.shape.rot.y; + } + + this->skelAnime.moveFlags = flags; + func_80832210(this); + AnimationContext_DisableQueue(globalCtx); +} + +void func_80832FFC(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, s32 flags, f32 playbackSpeed) { + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, anim, playbackSpeed); + func_80832F54(globalCtx, this, flags); +} + +void func_8083303C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, s32 flags) { + func_80832FFC(globalCtx, this, anim, flags, 1.0f); +} + +void func_80833064(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, s32 flags) { + func_80832FFC(globalCtx, this, anim, flags, 2.0f / 3.0f); +} + +void func_8083308C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + func_80833064(globalCtx, this, anim, 0x1C); +} + +void func_808330AC(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, s32 flags, f32 playbackSpeed) { + LinkAnimation_PlayLoopSetSpeed(globalCtx, &this->skelAnime, anim, playbackSpeed); + func_80832F54(globalCtx, this, flags); +} + +void func_808330EC(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, s32 flags) { + func_808330AC(globalCtx, this, anim, flags, 1.0f); +} + +void func_80833114(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, s32 flags) { + func_808330AC(globalCtx, this, anim, flags, 2.0f / 3.0f); +} + +void func_8083313C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + func_80833114(globalCtx, this, anim, 0x1C); +} + +void func_8083315C(GlobalContext* globalCtx, Player* this) { + s8 phi_v1; + s8 phi_v0; + + this->unk_A7C = D_808535D4; + this->unk_A80 = D_808535D8; + + func_80077D10(&D_808535D4, &D_808535D8, sControlInput); + + D_808535DC = Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx)) + D_808535D8; + + this->unk_846 = (this->unk_846 + 1) % 4; + + if (D_808535D4 < 55.0f) { + phi_v0 = -1; + phi_v1 = -1; + } + else { + phi_v1 = (u16)(D_808535D8 + 0x2000) >> 9; + phi_v0 = (u16)((s16)(D_808535DC - this->actor.shape.rot.y) + 0x2000) >> 14; + } + + this->unk_847[this->unk_846] = phi_v1; + this->unk_84B[this->unk_846] = phi_v0; +} + +void func_8083328C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* linkAnim) { + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, linkAnim, D_808535E8); +} + +s32 func_808332B8(Player* this) { + return (this->stateFlags1 & PLAYER_STATE1_27) && (this->currentBoots != PLAYER_BOOTS_IRON); +} + +s32 func_808332E4(Player* this) { + return (this->stateFlags1 & PLAYER_STATE1_24); +} + +void func_808332F4(Player* this, GlobalContext* globalCtx) { + GetItemEntry* giEntry = &sGetItemTable[this->getItemId - 1]; + + this->unk_862 = ABS(giEntry->gi); +} + +static LinkAnimationHeader* func_80833338(Player* this) { + return D_80853914[PLAYER_ANIMGROUP_0][this->modelAnimType]; +} + +s32 func_80833350(Player* this) { + LinkAnimationHeader** entry; + s32 i; + + if (func_80833338(this) != this->skelAnime.animation) { + for (i = 0, entry = &D_80853D7C[0][0]; i < 28; i++, entry++) { + if (this->skelAnime.animation == *entry) { + return i + 1; + } + } + return 0; + } + + return -1; +} + +void func_808333FC(Player* this, s32 arg1) { + if (D_80853E7C[arg1] != 0) { + func_80832924(this, D_80853E50[D_80853E7C[arg1] - 1]); + } +} + +LinkAnimationHeader* func_80833438(Player* this) { + if (this->unk_890 != 0) { + return D_80853914[PLAYER_ANIMGROUP_3][this->modelAnimType]; + } + else if (!(this->stateFlags1 & (PLAYER_STATE1_27 | PLAYER_STATE1_29)) && + (this->currentBoots == PLAYER_BOOTS_IRON)) { + return D_80853914[PLAYER_ANIMGROUP_4][this->modelAnimType]; + } + else { + return D_80853914[PLAYER_ANIMGROUP_2][this->modelAnimType]; + } +} + +s32 func_808334B4(Player* this) { + return func_808332E4(this) && (this->unk_834 != 0); +} + +LinkAnimationHeader* func_808334E4(Player* this) { + if (func_808334B4(this)) { + return &gPlayerAnim_002638; + } + else { + return D_80853914[PLAYER_ANIMGROUP_6][this->modelAnimType]; + } +} + +LinkAnimationHeader* func_80833528(Player* this) { + if (func_808334B4(this)) { + return &gPlayerAnim_002630; + } + else { + return D_80853914[PLAYER_ANIMGROUP_5][this->modelAnimType]; + } +} + +LinkAnimationHeader* func_8083356C(Player* this) { + if (func_8002DD78(this)) { + return &gPlayerAnim_0026E8; + } + else { + return D_80853914[PLAYER_ANIMGROUP_23][this->modelAnimType]; + } +} + +LinkAnimationHeader* func_808335B0(Player* this) { + if (func_808334B4(this)) { + return &gPlayerAnim_002620; + } + else { + return D_80853914[PLAYER_ANIMGROUP_25][this->modelAnimType]; + } +} + +LinkAnimationHeader* func_808335F4(Player* this) { + if (func_808334B4(this)) { + return &gPlayerAnim_002618; + } + else { + return D_80853914[PLAYER_ANIMGROUP_24][this->modelAnimType]; + } +} + +void func_80833638(Player* this, PlayerFunc82C arg1) { + this->func_82C = arg1; + this->unk_836 = 0; + this->unk_830 = 0.0f; + func_808326F0(this); +} + +void func_80833664(GlobalContext* globalCtx, Player* this, s8 actionParam) { + LinkAnimationHeader* current = this->skelAnime.animation; + LinkAnimationHeader** iter = &D_80853914[0][this->modelAnimType]; + u32 i; + + this->stateFlags1 &= ~(PLAYER_STATE1_3 | PLAYER_STATE1_24); + + for (i = 0; i < ARRAY_COUNT(D_80853914); i++) { + if (current == *iter) { + break; + } + iter += ARRAY_COUNT(D_80853914[0]); + } + + func_8083399C(globalCtx, this, actionParam); + + if (i < ARRAY_COUNT(D_80853914)) { + // fake match + // surely D_80853914 isn't a 1D array... unless? + this->skelAnime.animation = D_80853914[0][i * ARRAY_COUNT(D_80853914[0]) + this->modelAnimType]; + } +} + +s8 Player_ItemToActionParam(s32 item) { + if (item >= ITEM_NONE_FE) { + return PLAYER_AP_NONE; + } + else if (item == ITEM_LAST_USED) { + return PLAYER_AP_LAST_USED; + } + else if (item == ITEM_FISHING_POLE) { + return PLAYER_AP_FISHING_POLE; + } + else { + return sItemActionParams[item]; + } +} + +void func_80833770(GlobalContext* globalCtx, Player* this) { +} + +void func_8083377C(GlobalContext* globalCtx, Player* this) { + this->unk_85C = 1.0f; +} + +void func_80833790(GlobalContext* globalCtx, Player* this) { +} + +void func_8083379C(GlobalContext* globalCtx, Player* this) { + this->stateFlags1 |= PLAYER_STATE1_3; + + if (this->heldItemActionParam != PLAYER_AP_SLINGSHOT) { + this->unk_860 = -1; + } + else { + this->unk_860 = -2; + } +} + +void func_808337D4(GlobalContext* globalCtx, Player* this) { + s32 explosiveType; + ExplosiveInfo* explosiveInfo; + Actor* spawnedActor; + + if (this->stateFlags1 & PLAYER_STATE1_11) { + func_80832528(globalCtx, this); + return; + } + + explosiveType = Player_GetExplosiveHeld(this); + explosiveInfo = &sExplosiveInfos[explosiveType]; + + spawnedActor = Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, explosiveInfo->actorId, + this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, + this->actor.shape.rot.y, 0, 0); + if (spawnedActor != NULL) { + if ((explosiveType != 0) && (globalCtx->bombchuBowlingStatus != 0)) { + globalCtx->bombchuBowlingStatus--; + if (globalCtx->bombchuBowlingStatus == 0) { + globalCtx->bombchuBowlingStatus = -1; + } + } + else { + Inventory_ChangeAmmo(explosiveInfo->itemId, -1); + } + + this->interactRangeActor = spawnedActor; + this->heldActor = spawnedActor; + this->getItemId = GI_NONE; + this->unk_3BC.y = spawnedActor->shape.rot.y - this->actor.shape.rot.y; + this->stateFlags1 |= PLAYER_STATE1_11; + } +} + +void func_80833910(GlobalContext* globalCtx, Player* this) { + this->stateFlags1 |= PLAYER_STATE1_3; + this->unk_860 = -3; + + this->heldActor = + Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_ARMS_HOOK, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, 0); +} + +void func_80833984(GlobalContext* globalCtx, Player* this) { + this->stateFlags1 |= PLAYER_STATE1_24; +} + +void func_8083399C(GlobalContext* globalCtx, Player* this, s8 actionParam) { + this->unk_860 = 0; + this->unk_85C = 0.0f; + this->unk_858 = 0.0f; + + this->heldItemActionParam = this->itemActionParam = actionParam; + this->modelGroup = this->nextModelGroup; + + this->stateFlags1 &= ~(PLAYER_STATE1_3 | PLAYER_STATE1_24); + + D_80853FE8[actionParam](globalCtx, this); + + Player_SetModelGroup(this, this->modelGroup); +} + +void func_80833A20(Player* this, s32 newSwordState) { + u16 itemSfx; + u16 voiceSfx; + + if (this->swordState == 0) { + if ((this->heldItemActionParam == PLAYER_AP_SWORD_BGS) && (gSaveContext.swordHealth > 0.0f)) { + itemSfx = NA_SE_IT_HAMMER_SWING; + } + else { + itemSfx = NA_SE_IT_SWORD_SWING; + } + + voiceSfx = NA_SE_VO_LI_SWORD_N; + if (this->heldItemActionParam == PLAYER_AP_HAMMER) { + itemSfx = NA_SE_IT_HAMMER_SWING; + } + else if (this->swordAnimation >= 0x18) { + itemSfx = 0; + voiceSfx = NA_SE_VO_LI_SWORD_L; + } + else if (this->unk_845 >= 3) { + itemSfx = NA_SE_IT_SWORD_SWING_HARD; + voiceSfx = NA_SE_VO_LI_SWORD_L; + } + + if (itemSfx != 0) { + func_808328EC(this, itemSfx); + } + + if ((this->swordAnimation < 0x10) || (this->swordAnimation >= 0x14)) { + func_80832698(this, voiceSfx); + } + } + + this->swordState = newSwordState; +} + +s32 func_80833B2C(Player* this) { + if (this->stateFlags1 & (PLAYER_STATE1_16 | PLAYER_STATE1_17 | PLAYER_STATE1_30)) { + return 1; + } + else { + return 0; + } +} + +s32 func_80833B54(Player* this) { + if ((this->unk_664 != NULL) && CHECK_FLAG_ALL(this->unk_664->flags, ACTOR_FLAG_0 | ACTOR_FLAG_2)) { + this->stateFlags1 |= PLAYER_STATE1_4; + return 1; + } + + if (this->stateFlags1 & PLAYER_STATE1_4) { + this->stateFlags1 &= ~PLAYER_STATE1_4; + if (this->linearVelocity == 0.0f) { + this->currentYaw = this->actor.shape.rot.y; + } + } + + return 0; +} + +s32 func_80833BCC(Player* this) { + return func_8008E9C4(this) || func_80833B2C(this); +} + +s32 func_80833C04(Player* this) { + return func_80833B54(this) || func_80833B2C(this); +} + +void func_80833C3C(Player* this) { + this->unk_870 = this->unk_874 = 0.0f; +} + +s32 func_80833C50(Player* this, s32 item) { + if ((item < ITEM_NONE_FE) && (Player_ItemToActionParam(item) == this->itemActionParam)) { + return 1; + } + else { + return 0; + } +} + +s32 func_80833C98(s32 item1, s32 actionParam) { + if ((item1 < ITEM_NONE_FE) && (Player_ItemToActionParam(item1) == actionParam)) { + return 1; + } + else { + return 0; + } +} + +s32 func_80833CDC(GlobalContext* globalCtx, s32 index) { + if (index >= 4) { + return ITEM_NONE; + } + else if (globalCtx->bombchuBowlingStatus != 0) { + return (globalCtx->bombchuBowlingStatus > 0) ? ITEM_BOMBCHU : ITEM_NONE; + } + else if (index == 0) { + return B_BTN_ITEM; + } + else if (index == 1) { + return C_BTN_ITEM(0); + } + else if (index == 2) { + return C_BTN_ITEM(1); + } + else { + return C_BTN_ITEM(2); + } +} + +void func_80833DF8(Player* this, GlobalContext* globalCtx) { + s32 maskActionParam; + s32 item; + s32 i; + + if (this->currentMask != PLAYER_MASK_NONE) { + maskActionParam = this->currentMask - 1 + PLAYER_AP_MASK_KEATON; + if (!func_80833C98(C_BTN_ITEM(0), maskActionParam) && !func_80833C98(C_BTN_ITEM(1), maskActionParam) && + !func_80833C98(C_BTN_ITEM(2), maskActionParam)) { + this->currentMask = PLAYER_MASK_NONE; + } + } + + if (!(this->stateFlags1 & (PLAYER_STATE1_11 | PLAYER_STATE1_29)) && !func_8008F128(this)) { + if (this->itemActionParam >= PLAYER_AP_FISHING_POLE) { + if (!func_80833C50(this, B_BTN_ITEM) && !func_80833C50(this, C_BTN_ITEM(0)) && + !func_80833C50(this, C_BTN_ITEM(1)) && !func_80833C50(this, C_BTN_ITEM(2))) { + func_80835F44(globalCtx, this, ITEM_NONE); + return; + } + } + + for (i = 0; i < ARRAY_COUNT(D_80854388); i++) { + if (CHECK_BTN_ALL(sControlInput->press.button, D_80854388[i])) { + break; + } + } + + item = func_80833CDC(globalCtx, i); + if (item >= ITEM_NONE_FE) { + for (i = 0; i < ARRAY_COUNT(D_80854388); i++) { + if (CHECK_BTN_ALL(sControlInput->cur.button, D_80854388[i])) { + break; + } + } + + item = func_80833CDC(globalCtx, i); + if ((item < ITEM_NONE_FE) && (Player_ItemToActionParam(item) == this->heldItemActionParam)) { + D_80853618 = true; + } + } + else { + this->heldItemButton = i; + func_80835F44(globalCtx, this, item); + } + } +} + +void func_808340DC(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + f32 frameCount; + f32 startFrame; + f32 endFrame; + f32 playSpeed; + s32 sp38; + s8 sp37; + s32 nextAnimType; + + sp37 = Player_ItemToActionParam(this->heldItemId); + func_80833638(this, func_80834A2C); + + nextAnimType = gPlayerModelTypes[this->nextModelGroup][PLAYER_MODELGROUPENTRY_ANIM]; + sp38 = D_80854164[gPlayerModelTypes[this->modelGroup][PLAYER_MODELGROUPENTRY_ANIM]][nextAnimType]; + if ((sp37 == PLAYER_AP_BOTTLE) || (sp37 == PLAYER_AP_BOOMERANG) || + ((sp37 == PLAYER_AP_NONE) && + ((this->heldItemActionParam == PLAYER_AP_BOTTLE) || (this->heldItemActionParam == PLAYER_AP_BOOMERANG)))) { + sp38 = (sp37 == PLAYER_AP_NONE) ? -PLAYER_D_808540F4_13 : PLAYER_D_808540F4_13; + } + + this->unk_15A = ABS(sp38); + + anim = D_808540F4[this->unk_15A].anim; + if ((anim == &gPlayerAnim_002F30) && (this->currentShield == PLAYER_SHIELD_NONE)) { + anim = &gPlayerAnim_002F40; + } + + frameCount = Animation_GetLastFrame(anim); + endFrame = frameCount; + + if (sp38 >= 0) { + playSpeed = 1.2f; + startFrame = 0.0f; + } + else { + endFrame = 0.0f; + playSpeed = -1.2f; + startFrame = frameCount; + } + + if (sp37 != PLAYER_AP_NONE) { + playSpeed *= 2.0f; + } + + LinkAnimation_Change(globalCtx, &this->skelAnime2, anim, playSpeed, startFrame, endFrame, ANIMMODE_ONCE, 0.0f); + + this->stateFlags1 &= ~PLAYER_STATE1_8; +} + +void func_80834298(Player* this, GlobalContext* globalCtx) { + if ((this->actor.category == ACTORCAT_PLAYER) && !(this->stateFlags1 & PLAYER_STATE1_8) && + ((this->heldItemActionParam == this->itemActionParam) || (this->stateFlags1 & PLAYER_STATE1_22)) && + (gSaveContext.health != 0) && (globalCtx->csCtx.state == CS_STATE_IDLE) && (this->csMode == 0) && + (globalCtx->shootingGalleryStatus == 0) && (globalCtx->activeCamera == MAIN_CAM) && + (globalCtx->sceneLoadFlag != 0x14) && (gSaveContext.timer1State != 10)) { + func_80833DF8(this, globalCtx); + } + + if (this->stateFlags1 & PLAYER_STATE1_8) { + func_808340DC(this, globalCtx); + } +} + +s32 func_80834380(GlobalContext* globalCtx, Player* this, s32* itemPtr, s32* typePtr) { + if (LINK_IS_ADULT) { + *itemPtr = ITEM_BOW; + if (this->stateFlags1 & PLAYER_STATE1_23) { + *typePtr = ARROW_NORMAL_HORSE; + } + else { + *typePtr = this->heldItemActionParam - 6; + } + } + else { + *itemPtr = ITEM_SLINGSHOT; + *typePtr = ARROW_SEED; + } + + if (gSaveContext.minigameState == 1) { + return globalCtx->interfaceCtx.hbaAmmo; + } + else if (globalCtx->shootingGalleryStatus != 0) { + return globalCtx->shootingGalleryStatus; + } + else { + return AMMO(*itemPtr); + } +} + +s32 func_8083442C(Player* this, GlobalContext* globalCtx) { + s32 item; + s32 arrowType; + s32 magicArrowType; + + if ((this->heldItemActionParam >= PLAYER_AP_BOW_FIRE) && (this->heldItemActionParam <= PLAYER_AP_BOW_0E) && + (gSaveContext.unk_13F0 != 0)) { + func_80078884(NA_SE_SY_ERROR); + } + else { + func_80833638(this, func_808351D4); + + this->stateFlags1 |= PLAYER_STATE1_9; + this->unk_834 = 14; + + if (this->unk_860 >= 0) { + func_8002F7DC(&this->actor, D_80854398[ABS(this->unk_860) - 1]); + + if (!Player_HoldsHookshot(this) && (func_80834380(globalCtx, this, &item, &arrowType) > 0)) { + magicArrowType = arrowType - ARROW_FIRE; + + if (this->unk_860 >= 0) { + if ((magicArrowType >= 0) && (magicArrowType <= 2) && + !func_80087708(globalCtx, sMagicArrowCosts[magicArrowType], 0)) { + arrowType = ARROW_NORMAL; + } + + this->heldActor = Actor_SpawnAsChild( + &globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ARROW, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, arrowType); + } + } + } + + return 1; + } + + return 0; +} + +void func_80834594(GlobalContext* globalCtx, Player* this) { + if (this->heldItemActionParam != PLAYER_AP_NONE) { + if (func_8008F2BC(this, this->heldItemActionParam) >= 0) { + func_808328EC(this, NA_SE_IT_SWORD_PUTAWAY); + } + else { + func_808328EC(this, NA_SE_PL_CHANGE_ARMS); + } + } + + func_80835F44(globalCtx, this, this->heldItemId); + + if (func_8008F2BC(this, this->heldItemActionParam) >= 0) { + func_808328EC(this, NA_SE_IT_SWORD_PICKOUT); + } + else if (this->heldItemActionParam != PLAYER_AP_NONE) { + func_808328EC(this, NA_SE_PL_CHANGE_ARMS); + } +} + +void func_80834644(GlobalContext* globalCtx, Player* this) { + if (func_80834A2C == this->func_82C) { + func_80834594(globalCtx, this); + } + + func_80833638(this, D_80853EDC[this->heldItemActionParam]); + this->unk_834 = 0; + this->unk_6AC = 0; + func_808323B4(globalCtx, this); + this->stateFlags1 &= ~PLAYER_STATE1_8; +} + +LinkAnimationHeader* func_808346C4(GlobalContext* globalCtx, Player* this) { + func_80833638(this, func_80834B5C); + func_808323B4(globalCtx, this); + + if (this->unk_870 < 0.5f) { + return D_808543A4[Player_HoldsTwoHandedWeapon(this)]; + } + else { + return D_808543AC[Player_HoldsTwoHandedWeapon(this)]; + } +} + +s32 func_80834758(GlobalContext* globalCtx, Player* this) { + LinkAnimationHeader* anim; + f32 frame; + + if (!(this->stateFlags1 & (PLAYER_STATE1_22 | PLAYER_STATE1_23 | PLAYER_STATE1_29)) && + (globalCtx->shootingGalleryStatus == 0) && (this->heldItemActionParam == this->itemActionParam) && + (this->currentShield != PLAYER_SHIELD_NONE) && !Player_IsChildWithHylianShield(this) && func_80833BCC(this) && + CHECK_BTN_ALL(sControlInput->cur.button, BTN_R)) { + + anim = func_808346C4(globalCtx, this); + frame = Animation_GetLastFrame(anim); + LinkAnimation_Change(globalCtx, &this->skelAnime2, anim, 1.0f, frame, frame, ANIMMODE_ONCE, 0.0f); + func_8002F7DC(&this->actor, NA_SE_IT_SHIELD_POSTURE); + + return 1; + } + else { + return 0; + } +} + +s32 func_8083485C(Player* this, GlobalContext* globalCtx) { + if (func_80834758(globalCtx, this)) { + return 1; + } + else { + return 0; + } +} + +void func_80834894(Player* this) { + func_80833638(this, func_80834C74); + + if (this->itemActionParam < 0) { + func_8008EC70(this); + } + + Animation_Reverse(&this->skelAnime2); + func_8002F7DC(&this->actor, NA_SE_IT_SHIELD_REMOVE); +} + +void func_808348EC(GlobalContext* globalCtx, Player* this) { + struct_808540F4* ptr = &D_808540F4[this->unk_15A]; + f32 frame; + + frame = ptr->unk_04; + frame = (this->skelAnime2.playSpeed < 0.0f) ? frame - 1.0f : frame; + + if (LinkAnimation_OnFrame(&this->skelAnime2, frame)) { + func_80834594(globalCtx, this); + } + + func_80833B54(this); +} + +s32 func_8083499C(Player* this, GlobalContext* globalCtx) { + if (this->stateFlags1 & PLAYER_STATE1_8) { + func_808340DC(this, globalCtx); + } + else { + return 0; + } + + return 1; +} + +s32 func_808349DC(Player* this, GlobalContext* globalCtx) { + if (func_80834758(globalCtx, this) || func_8083499C(this, globalCtx)) { + return 1; + } + else { + return 0; + } +} + +s32 func_80834A2C(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime2) || + ((Player_ItemToActionParam(this->heldItemId) == this->heldItemActionParam) && + (D_80853614 = (D_80853614 || + ((this->modelAnimType != PLAYER_ANIMTYPE_3) && (globalCtx->shootingGalleryStatus == 0)))))) { + func_80833638(this, D_80853EDC[this->heldItemActionParam]); + this->unk_834 = 0; + this->unk_6AC = 0; + D_80853618 = D_80853614; + return this->func_82C(this, globalCtx); + } + + if (func_80833350(this) != 0) { + func_808348EC(globalCtx, this); + func_80832264(globalCtx, this, func_80833338(this)); + this->unk_6AC = 0; + } + else { + func_808348EC(globalCtx, this); + } + + return 1; +} + +s32 func_80834B5C(Player* this, GlobalContext* globalCtx) { + LinkAnimation_Update(globalCtx, &this->skelAnime2); + + if (!CHECK_BTN_ALL(sControlInput->cur.button, BTN_R)) { + func_80834894(this); + return 1; + } + else { + this->stateFlags1 |= PLAYER_STATE1_22; + Player_SetModelsForHoldingShield(this); + return 1; + } +} + +s32 func_80834BD4(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + f32 frame; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + anim = func_808346C4(globalCtx, this); + frame = Animation_GetLastFrame(anim); + LinkAnimation_Change(globalCtx, &this->skelAnime2, anim, 1.0f, frame, frame, ANIMMODE_ONCE, 0.0f); + } + + this->stateFlags1 |= PLAYER_STATE1_22; + Player_SetModelsForHoldingShield(this); + + return 1; +} + +s32 func_80834C74(Player* this, GlobalContext* globalCtx) { + D_80853614 = D_80853618; + + if (D_80853614 || LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + func_80833638(this, D_80853EDC[this->heldItemActionParam]); + LinkAnimation_PlayLoop(globalCtx, &this->skelAnime2, D_80853914[PLAYER_ANIMGROUP_0][this->modelAnimType]); + this->unk_6AC = 0; + this->func_82C(this, globalCtx); + return 0; + } + + return 1; +} + +s32 func_80834D2C(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + + if (this->heldItemActionParam != PLAYER_AP_BOOMERANG) { + if (!func_8083442C(this, globalCtx)) { + return 0; + } + + if (!Player_HoldsHookshot(this)) { + anim = &gPlayerAnim_0026A0; + } + else { + anim = &gPlayerAnim_002CA0; + } + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, anim); + } + else { + func_80833638(this, func_80835884); + this->unk_834 = 10; + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, &gPlayerAnim_002628); + } + + if (this->stateFlags1 & PLAYER_STATE1_23) { + func_80832284(globalCtx, this, &gPlayerAnim_003380); + } + else if ((this->actor.bgCheckFlags & 1) && !func_80833B54(this)) { + func_80832284(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_0][this->modelAnimType]); + } + + return 1; +} + +s32 func_80834E44(GlobalContext* globalCtx) { + return (globalCtx->shootingGalleryStatus > 0) && CHECK_BTN_ALL(sControlInput->press.button, BTN_B); +} + +s32 func_80834E7C(GlobalContext* globalCtx) { + return (globalCtx->shootingGalleryStatus != 0) && + ((globalCtx->shootingGalleryStatus < 0) || + CHECK_BTN_ANY(sControlInput->cur.button, BTN_A | BTN_B | BTN_CUP | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN)); +} + +s32 func_80834EB8(Player* this, GlobalContext* globalCtx) { + if ((this->unk_6AD == 0) || (this->unk_6AD == 2)) { + if (func_80833BCC(this) || (Camera_CheckValidMode(Gameplay_GetCamera(globalCtx, 0), 7) == 0)) { + return 1; + } + this->unk_6AD = 2; + } + + return 0; +} + +s32 func_80834F2C(Player* this, GlobalContext* globalCtx) { + if ((this->doorType == PLAYER_DOORTYPE_NONE) && !(this->stateFlags1 & PLAYER_STATE1_25)) { + if (D_80853614 || func_80834E44(globalCtx)) { + if (func_80834D2C(this, globalCtx)) { + return func_80834EB8(this, globalCtx); + } + } + } + + return 0; +} + +s32 func_80834FBC(Player* this) { + if (this->actor.child != NULL) { + if (this->heldActor == NULL) { + this->heldActor = this->actor.child; + func_8083264C(this, 255, 10, 250, 0); + func_8002F7DC(&this->actor, NA_SE_IT_HOOKSHOT_RECEIVE); + } + + return 1; + } + + return 0; +} + +s32 func_8083501C(Player* this, GlobalContext* globalCtx) { + if (this->unk_860 >= 0) { + this->unk_860 = -this->unk_860; + } + + if ((!Player_HoldsHookshot(this) || func_80834FBC(this)) && !func_80834758(globalCtx, this) && + !func_80834F2C(this, globalCtx)) { + return 0; + } + else + { + this->unk_6AD = 2; // OTRTODO: THIS IS A BAD IDEA BUT IT FIXES THE HORSE FIRST PERSON? + } + + return 1; +} + +s32 func_808350A4(GlobalContext* globalCtx, Player* this) { + s32 item; + s32 arrowType; + + if (this->heldActor != NULL) { + if (!Player_HoldsHookshot(this)) { + func_80834380(globalCtx, this, &item, &arrowType); + + if (gSaveContext.minigameState == 1) { + globalCtx->interfaceCtx.hbaAmmo--; + } + else if (globalCtx->shootingGalleryStatus != 0) { + globalCtx->shootingGalleryStatus--; + } + else { + Inventory_ChangeAmmo(item, -1); + } + + if (globalCtx->shootingGalleryStatus == 1) { + globalCtx->shootingGalleryStatus = -10; + } + + func_8083264C(this, 150, 10, 150, 0); + } + else { + func_8083264C(this, 255, 20, 150, 0); + } + + this->unk_A73 = 4; + this->heldActor->parent = NULL; + this->actor.child = NULL; + this->heldActor = NULL; + + return 1; + } + + return 0; +} + +static u16 D_808543DC[] = { NA_SE_IT_BOW_FLICK, NA_SE_IT_SLING_FLICK }; + +s32 func_808351D4(Player* this, GlobalContext* globalCtx) { + s32 sp2C; + + if (!Player_HoldsHookshot(this)) { + sp2C = 0; + } + else { + sp2C = 1; + } + + Math_ScaledStepToS(&this->unk_6C0, 1200, 400); + this->unk_6AE |= 0x100; + + if ((this->unk_836 == 0) && (func_80833350(this) == 0) && (this->skelAnime.animation == &gPlayerAnim_0026E8)) { + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, D_808543CC[sp2C]); + this->unk_836 = -1; + } + else if (LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + LinkAnimation_PlayLoop(globalCtx, &this->skelAnime2, D_808543D4[sp2C]); + this->unk_836 = 1; + } + else if (this->unk_836 == 1) { + this->unk_836 = 2; + } + + if (this->unk_834 > 10) { + this->unk_834--; + } + + func_80834EB8(this, globalCtx); + + if ((this->unk_836 > 0) && ((this->unk_860 < 0) || (!D_80853618 && !func_80834E7C(globalCtx)))) { + func_80833638(this, func_808353D8); + if (this->unk_860 >= 0) { + if (sp2C == 0) { + if (!func_808350A4(globalCtx, this)) { + func_8002F7DC(&this->actor, D_808543DC[ABS(this->unk_860) - 1]); + } + } + else if (this->actor.bgCheckFlags & 1) { + func_808350A4(globalCtx, this); + } + } + this->unk_834 = 10; + func_80832210(this); + } + else { + this->stateFlags1 |= PLAYER_STATE1_9; + } + + return 1; +} + +s32 func_808353D8(Player* this, GlobalContext* globalCtx) { + LinkAnimation_Update(globalCtx, &this->skelAnime2); + + if (Player_HoldsHookshot(this) && !func_80834FBC(this)) { + return 1; + } + + if (!func_80834758(globalCtx, this) && + (D_80853614 || ((this->unk_860 < 0) && D_80853618) || func_80834E44(globalCtx))) { + this->unk_860 = ABS(this->unk_860); + + if (func_8083442C(this, globalCtx)) { + if (Player_HoldsHookshot(this)) { + this->unk_836 = 1; + } + else { + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, &gPlayerAnim_0026B8); + } + } + } + else { + if (this->unk_834 != 0) { + this->unk_834--; + } + + if (func_80833BCC(this) || (this->unk_6AD != 0) || (this->stateFlags1 & PLAYER_STATE1_20)) { + if (this->unk_834 == 0) { + this->unk_834++; + } + return 1; + } + + if (Player_HoldsHookshot(this)) { + func_80833638(this, func_8083501C); + } + else { + func_80833638(this, func_80835588); + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, &gPlayerAnim_0026B0); + } + + this->unk_834 = 0; + } + + return 1; +} + +s32 func_80835588(Player* this, GlobalContext* globalCtx) { + if (!(this->actor.bgCheckFlags & 1) || LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + func_80833638(this, func_8083501C); + } + + return 1; +} + +void func_808355DC(Player* this) { + this->stateFlags1 |= PLAYER_STATE1_17; + + if (!(this->skelAnime.moveFlags & 0x80) && (this->actor.bgCheckFlags & 0x200) && (D_80853608 < 0x2000)) { + this->currentYaw = this->actor.shape.rot.y = this->actor.wallYaw + 0x8000; + } + + this->targetYaw = this->actor.shape.rot.y; +} + +s32 func_80835644(GlobalContext* globalCtx, Player* this, Actor* arg2) { + if (arg2 == NULL) { + func_80832564(globalCtx, this); + func_80839F90(this, globalCtx); + return 1; + } + + return 0; +} + +void func_80835688(Player* this, GlobalContext* globalCtx) { + if (!func_80835644(globalCtx, this, this->heldActor)) { + func_80833638(this, func_808356E8); + LinkAnimation_PlayLoop(globalCtx, &this->skelAnime2, &gPlayerAnim_002E10); + } +} + +s32 func_808356E8(Player* this, GlobalContext* globalCtx) { + Actor* heldActor = this->heldActor; + + if (heldActor == NULL) { + func_80834644(globalCtx, this); + } + + if (func_80834758(globalCtx, this)) { + return 1; + } + + if (this->stateFlags1 & PLAYER_STATE1_11) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + LinkAnimation_PlayLoop(globalCtx, &this->skelAnime2, &gPlayerAnim_002E10); + } + + if ((heldActor->id == ACTOR_EN_NIW) && (this->actor.velocity.y <= 0.0f)) { + this->actor.minVelocityY = -2.0f; + this->actor.gravity = -0.5f; + this->fallStartHeight = this->actor.world.pos.y; + } + + return 1; + } + + return func_8083485C(this, globalCtx); +} + +void func_808357E8(Player* this, Gfx** dLists) { + this->leftHandDLists = &dLists[gSaveContext.linkAge]; +} + +s32 func_80835800(Player* this, GlobalContext* globalCtx) { + if (func_80834758(globalCtx, this)) { + return 1; + } + + if (this->stateFlags1 & PLAYER_STATE1_25) { + func_80833638(this, func_80835B60); + } + else if (func_80834F2C(this, globalCtx)) { + return 1; + } + + return 0; +} + +s32 func_80835884(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + func_80833638(this, func_808358F0); + LinkAnimation_PlayLoop(globalCtx, &this->skelAnime2, &gPlayerAnim_002638); + } + + func_80834EB8(this, globalCtx); + + return 1; +} + +s32 func_808358F0(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* animSeg = this->skelAnime.animation; + + if ((func_808334E4(this) == animSeg) || (func_80833528(this) == animSeg) || (func_808335B0(this) == animSeg) || + (func_808335F4(this) == animSeg)) { + AnimationContext_SetCopyAll(globalCtx, this->skelAnime.limbCount, this->skelAnime2.jointTable, + this->skelAnime.jointTable); + } + else { + LinkAnimation_Update(globalCtx, &this->skelAnime2); + } + + func_80834EB8(this, globalCtx); + + if (!D_80853618) { + func_80833638(this, func_808359FC); + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, + (this->unk_870 < 0.5f) ? &gPlayerAnim_002608 : &gPlayerAnim_002600); + } + + return 1; +} + +s32 func_808359FC(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + func_80833638(this, func_80835B60); + this->unk_834 = 0; + } + else if (LinkAnimation_OnFrame(&this->skelAnime2, 6.0f)) { + f32 posX = (Math_SinS(this->actor.shape.rot.y) * 10.0f) + this->actor.world.pos.x; + f32 posZ = (Math_CosS(this->actor.shape.rot.y) * 10.0f) + this->actor.world.pos.z; + s32 yaw = (this->unk_664 != NULL) ? this->actor.shape.rot.y + 14000 : this->actor.shape.rot.y; + EnBoom* boomerang = + (EnBoom*)Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_BOOM, posX, this->actor.world.pos.y + 30.0f, + posZ, this->actor.focus.rot.x, yaw, 0, 0); + + this->boomerangActor = &boomerang->actor; + if (boomerang != NULL) { + boomerang->moveTo = this->unk_664; + boomerang->returnTimer = 20; + this->stateFlags1 |= PLAYER_STATE1_25; + if (!func_8008E9C4(this)) { + func_808355DC(this); + } + this->unk_A73 = 4; + func_8002F7DC(&this->actor, NA_SE_IT_BOOMERANG_THROW); + func_80832698(this, NA_SE_VO_LI_SWORD_N); + } + } + + return 1; +} + +s32 func_80835B60(Player* this, GlobalContext* globalCtx) { + if (func_80834758(globalCtx, this)) { + return 1; + } + + if (!(this->stateFlags1 & PLAYER_STATE1_25)) { + func_80833638(this, func_80835C08); + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, &gPlayerAnim_0025F8); + func_808357E8(this, D_80125EF8); + func_8002F7DC(&this->actor, NA_SE_PL_CATCH_BOOMERANG); + func_80832698(this, NA_SE_VO_LI_SWORD_N); + return 1; + } + + return 0; +} + +s32 func_80835C08(Player* this, GlobalContext* globalCtx) { + if (!func_80835800(this, globalCtx) && LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + func_80833638(this, func_80835800); + } + + return 1; +} + +s32 func_80835C58(GlobalContext* globalCtx, Player* this, PlayerFunc674 func, s32 flags) { + if (func == this->func_674) { + return 0; + } + + if (func_8084E3C4 == this->func_674) { + Audio_OcaSetInstrument(0); + this->stateFlags2 &= ~(PLAYER_STATE2_24 | PLAYER_STATE2_25); + } + else if (func_808507F4 == this->func_674) { + func_80832340(globalCtx, this); + } + + this->func_674 = func; + + if ((this->itemActionParam != this->heldItemActionParam) && + (!(flags & 1) || !(this->stateFlags1 & PLAYER_STATE1_22))) { + func_8008EC70(this); + } + + if (!(flags & 1) && (!(this->stateFlags1 & PLAYER_STATE1_11))) { + func_80834644(globalCtx, this); + this->stateFlags1 &= ~PLAYER_STATE1_22; + } + + func_80832DBC(this); + this->stateFlags1 &= ~(PLAYER_STATE1_2 | PLAYER_STATE1_6 | PLAYER_STATE1_26 | PLAYER_STATE1_28 | PLAYER_STATE1_29 | + PLAYER_STATE1_31); + this->stateFlags2 &= ~(PLAYER_STATE2_19 | PLAYER_STATE2_27 | PLAYER_STATE2_28); + this->stateFlags3 &= ~(PLAYER_STATE3_1 | PLAYER_STATE3_3 | PLAYER_STATE3_7); + this->unk_84F = 0; + this->unk_850 = 0; + this->unk_6AC = 0; + func_808326F0(this); + + return 1; +} + +void func_80835DAC(GlobalContext* globalCtx, Player* this, PlayerFunc674 func, s32 flags) { + s32 temp; + + temp = this->skelAnime.moveFlags; + this->skelAnime.moveFlags = 0; + func_80835C58(globalCtx, this, func, flags); + this->skelAnime.moveFlags = temp; +} + +void func_80835DE4(GlobalContext* globalCtx, Player* this, PlayerFunc674 func, s32 flags) { + s32 temp; + + if (this->itemActionParam >= 0) { + temp = this->itemActionParam; + this->itemActionParam = this->heldItemActionParam; + func_80835C58(globalCtx, this, func, flags); + this->itemActionParam = temp; + Player_SetModels(this, Player_ActionToModelGroup(this, this->itemActionParam)); + } +} + +void func_80835E44(GlobalContext* globalCtx, s16 camSetting) { + if (!func_800C0CB8(globalCtx)) { + if (camSetting == CAM_SET_SCENE_TRANSITION) { + Interface_ChangeAlpha(2); + } + } + else { + Camera_ChangeSetting(Gameplay_GetCamera(globalCtx, 0), camSetting); + } +} + +void func_80835EA4(GlobalContext* globalCtx, s32 arg1) { + func_80835E44(globalCtx, CAM_SET_TURN_AROUND); + Camera_SetCameraData(Gameplay_GetCamera(globalCtx, 0), 4, 0, 0, arg1, 0, 0); +} + +void func_80835EFC(Player* this) { + if (Player_HoldsHookshot(this)) { + Actor* heldActor = this->heldActor; + + if (heldActor != NULL) { + Actor_Kill(heldActor); + this->actor.child = NULL; + this->heldActor = NULL; + } + } +} + +void func_80835F44(GlobalContext* globalCtx, Player* this, s32 item) { + s8 actionParam; + s32 temp; + s32 nextAnimType; + + actionParam = Player_ItemToActionParam(item); + + if (((this->heldItemActionParam == this->itemActionParam) && + (!(this->stateFlags1 & PLAYER_STATE1_22) || (Player_ActionToSword(actionParam) != 0) || + (actionParam == PLAYER_AP_NONE))) || + ((this->itemActionParam < 0) && + ((Player_ActionToSword(actionParam) != 0) || (actionParam == PLAYER_AP_NONE)))) { + + if ((actionParam == PLAYER_AP_NONE) || !(this->stateFlags1 & PLAYER_STATE1_27) || + ((this->actor.bgCheckFlags & 1) && + ((actionParam == PLAYER_AP_HOOKSHOT) || (actionParam == PLAYER_AP_LONGSHOT)))) { + + if ((globalCtx->bombchuBowlingStatus == 0) && + (((actionParam == PLAYER_AP_STICK) && (AMMO(ITEM_STICK) == 0)) || + ((actionParam == PLAYER_AP_BEAN) && (AMMO(ITEM_BEAN) == 0)) || + (temp = Player_ActionToExplosive(this, actionParam), + ((temp >= 0) && ((AMMO(sExplosiveInfos[temp].itemId) == 0) || + (globalCtx->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].length >= 3)))))) { + func_80078884(NA_SE_SY_ERROR); + return; + } + + if (actionParam == PLAYER_AP_LENS) { + if (func_80087708(globalCtx, 0, 3)) { + if (globalCtx->actorCtx.unk_03 != 0) { + func_800304B0(globalCtx); + } + else { + globalCtx->actorCtx.unk_03 = 1; + } + func_80078884((globalCtx->actorCtx.unk_03 != 0) ? NA_SE_SY_GLASSMODE_ON : NA_SE_SY_GLASSMODE_OFF); + } + else { + func_80078884(NA_SE_SY_ERROR); + } + return; + } + + if (actionParam == PLAYER_AP_NUT) { + if (AMMO(ITEM_NUT) != 0) { + func_8083C61C(globalCtx, this); + } + else { + func_80078884(NA_SE_SY_ERROR); + } + return; + } + + temp = Player_ActionToMagicSpell(this, actionParam); + if (temp >= 0) { + if (((actionParam == PLAYER_AP_FARORES_WIND) && (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0)) || + ((gSaveContext.unk_13F4 != 0) && (gSaveContext.unk_13F0 == 0) && + (gSaveContext.magic >= sMagicSpellCosts[temp]))) { + this->itemActionParam = actionParam; + this->unk_6AD = 4; + } + else { + func_80078884(NA_SE_SY_ERROR); + } + return; + } + + if (actionParam >= PLAYER_AP_MASK_KEATON) { + if (this->currentMask != PLAYER_MASK_NONE) { + this->currentMask = PLAYER_MASK_NONE; + } + else { + this->currentMask = actionParam - PLAYER_AP_MASK_KEATON + 1; + } + func_808328EC(this, NA_SE_PL_CHANGE_ARMS); + return; + } + + if (((actionParam >= PLAYER_AP_OCARINA_FAIRY) && (actionParam <= PLAYER_AP_OCARINA_TIME)) || + (actionParam >= PLAYER_AP_BOTTLE_FISH)) { + if (!func_8008E9C4(this) || + ((actionParam >= PLAYER_AP_BOTTLE_POTION_RED) && (actionParam <= PLAYER_AP_BOTTLE_FAIRY))) { + func_8002D53C(globalCtx, &globalCtx->actorCtx.titleCtx); + this->unk_6AD = 4; + this->itemActionParam = actionParam; + } + return; + } + + if ((actionParam != this->heldItemActionParam) || + ((this->heldActor == 0) && (Player_ActionToExplosive(this, actionParam) >= 0))) { + this->nextModelGroup = Player_ActionToModelGroup(this, actionParam); + nextAnimType = gPlayerModelTypes[this->nextModelGroup][PLAYER_MODELGROUPENTRY_ANIM]; + if ((this->heldItemActionParam >= 0) && (Player_ActionToMagicSpell(this, actionParam) < 0) && + (item != this->heldItemId) && + (D_80854164[gPlayerModelTypes[this->modelGroup][PLAYER_MODELGROUPENTRY_ANIM]][nextAnimType] != + PLAYER_D_808540F4_0)) { + this->heldItemId = item; + this->stateFlags1 |= PLAYER_STATE1_8; + } + else { + func_80835EFC(this); + func_808323B4(globalCtx, this); + func_80833664(globalCtx, this, actionParam); + } + return; + } + + D_80853614 = D_80853618 = true; + } + } +} + +void func_80836448(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + s32 cond = func_808332B8(this); + + func_80832564(globalCtx, this); + + func_80835C58(globalCtx, this, cond ? func_8084E368 : func_80843CEC, 0); + + this->stateFlags1 |= PLAYER_STATE1_7; + + func_80832264(globalCtx, this, anim); + if (anim == &gPlayerAnim_002878) { + this->skelAnime.endFrame = 84.0f; + } + + func_80832224(this); + func_80832698(this, NA_SE_VO_LI_DOWN); + + if (this->actor.category == ACTORCAT_PLAYER) { + func_800F47BC(); + + if (Inventory_ConsumeFairy(globalCtx)) { + globalCtx->gameOverCtx.state = GAMEOVER_REVIVE_START; + this->unk_84F = 1; + } + else { + globalCtx->gameOverCtx.state = GAMEOVER_DEATH_START; + func_800F6AB0(0); + Audio_PlayFanfare(NA_BGM_GAME_OVER); + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + } + + OnePointCutscene_Init(globalCtx, 9806, cond ? 120 : 60, &this->actor, MAIN_CAM); + ShrinkWindow_SetVal(0x20); + } +} + +s32 func_808365C8(Player* this) { + return (!(func_808458D0 == this->func_674) || + ((this->stateFlags1 & PLAYER_STATE1_8) && + ((this->heldItemId == ITEM_LAST_USED) || (this->heldItemId == ITEM_NONE)))) && + (!(func_80834A2C == this->func_82C) || + (Player_ItemToActionParam(this->heldItemId) == this->heldItemActionParam)); +} + +s32 func_80836670(Player* this, GlobalContext* globalCtx) { + if (!(this->stateFlags1 & PLAYER_STATE1_23) && (this->actor.parent != NULL) && Player_HoldsHookshot(this)) { + func_80835C58(globalCtx, this, func_80850AEC, 1); + this->stateFlags3 |= PLAYER_STATE3_7; + func_80832264(globalCtx, this, &gPlayerAnim_002C90); + func_80832F54(globalCtx, this, 0x9B); + func_80832224(this); + this->currentYaw = this->actor.shape.rot.y; + this->actor.bgCheckFlags &= ~1; + this->hoverBootsTimer = 0; + this->unk_6AE |= 0x43; + func_80832698(this, NA_SE_VO_LI_LASH); + return 1; + } + + if (func_808365C8(this)) { + func_80834298(this, globalCtx); + if (func_8084E604 == this->func_674) { + return 1; + } + } + + if (!this->func_82C(this, globalCtx)) { + return 0; + } + + if (this->unk_830 != 0.0f) { + if ((func_80833350(this) == 0) || (this->linearVelocity != 0.0f)) { + AnimationContext_SetCopyFalse(globalCtx, this->skelAnime.limbCount, this->skelAnime2.jointTable, + this->skelAnime.jointTable, D_80853410); + } + Math_StepToF(&this->unk_830, 0.0f, 0.25f); + AnimationContext_SetInterp(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->skelAnime2.jointTable, 1.0f - this->unk_830); + } + else if ((func_80833350(this) == 0) || (this->linearVelocity != 0.0f)) { + AnimationContext_SetCopyTrue(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->skelAnime2.jointTable, D_80853410); + } + else { + AnimationContext_SetCopyAll(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->skelAnime2.jointTable); + } + + return 1; +} + +s32 func_80836898(GlobalContext* globalCtx, Player* this, PlayerFuncA74 func) { + this->func_A74 = func; + func_80835C58(globalCtx, this, func_808458D0, 0); + this->stateFlags2 |= PLAYER_STATE2_6; + return func_80832528(globalCtx, this); +} + +void func_808368EC(Player* this, GlobalContext* globalCtx) { + s16 previousYaw = this->actor.shape.rot.y; + + if (!(this->stateFlags2 & (PLAYER_STATE2_5 | PLAYER_STATE2_6))) { + if ((this->unk_664 != NULL) && + ((globalCtx->actorCtx.targetCtx.unk_4B != 0) || (this->actor.category != ACTORCAT_PLAYER))) { + Math_ScaledStepToS(&this->actor.shape.rot.y, + Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk_664->focus.pos), 4000); + } + else if ((this->stateFlags1 & PLAYER_STATE1_17) && + !(this->stateFlags2 & (PLAYER_STATE2_5 | PLAYER_STATE2_6))) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->targetYaw, 4000); + } + } + else if (!(this->stateFlags2 & PLAYER_STATE2_6)) { + Math_ScaledStepToS(&this->actor.shape.rot.y, this->currentYaw, 2000); + } + + this->unk_87C = this->actor.shape.rot.y - previousYaw; +} + +s32 func_808369C8(s16* pValue, s16 arg1, s16 arg2, s16 arg3, s16 arg4, s16 arg5) { + s16 temp1; + s16 temp2; + s16 temp3; + + temp1 = temp2 = arg4 - *pValue; + temp2 = CLAMP(temp2, -arg5, arg5); + *pValue += (s16)(temp1 - temp2); + + Math_ScaledStepToS(pValue, arg1, arg2); + + temp3 = *pValue; + if (*pValue < -arg3) { + *pValue = -arg3; + } + else if (*pValue > arg3) { + *pValue = arg3; + } + return temp3 - *pValue; +} + +s32 func_80836AB8(Player* this, s32 arg1) { + s16 sp36; + s16 var; + + var = this->actor.shape.rot.y; + if (arg1 != 0) { + var = this->actor.focus.rot.y; + this->unk_6BC = this->actor.focus.rot.x; + this->unk_6AE |= 0x41; + } + else { + func_808369C8(&this->unk_6BC, + func_808369C8(&this->unk_6B6, this->actor.focus.rot.x, 600, 10000, this->actor.focus.rot.x, 0), + 200, 4000, this->unk_6B6, 10000); + sp36 = this->actor.focus.rot.y - var; + func_808369C8(&sp36, 0, 200, 24000, this->unk_6BE, 8000); + var = this->actor.focus.rot.y - sp36; + func_808369C8(&this->unk_6B8, sp36 - this->unk_6BE, 200, 8000, sp36, 8000); + func_808369C8(&this->unk_6BE, sp36, 200, 8000, this->unk_6B8, 8000); + this->unk_6AE |= 0xD9; + } + + return var; +} + +void func_80836BEC(Player* this, GlobalContext* globalCtx) { + s32 sp1C = 0; + s32 zTrigPressed = CHECK_BTN_ALL(sControlInput->cur.button, BTN_Z); + Actor* actorToTarget; + s32 pad; + s32 holdTarget; + s32 cond; + + if (!zTrigPressed) { + this->stateFlags1 &= ~PLAYER_STATE1_30; + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) || (this->csMode != 0) || + (this->stateFlags1 & (PLAYER_STATE1_7 | PLAYER_STATE1_29)) || (this->stateFlags3 & PLAYER_STATE3_7)) { + this->unk_66C = 0; + } + else if (zTrigPressed || (this->stateFlags2 & PLAYER_STATE2_13) || (this->unk_684 != NULL)) { + if (this->unk_66C <= 5) { + this->unk_66C = 5; + } + else { + this->unk_66C--; + } + } + else if (this->stateFlags1 & PLAYER_STATE1_17) { + this->unk_66C = 0; + } + else if (this->unk_66C != 0) { + this->unk_66C--; + } + + if (this->unk_66C >= 6) { + sp1C = 1; + } + + cond = func_8083224C(globalCtx); + if (cond || (this->unk_66C != 0) || (this->stateFlags1 & (PLAYER_STATE1_12 | PLAYER_STATE1_25))) { + if (!cond) { + if (!(this->stateFlags1 & PLAYER_STATE1_25) && + ((this->heldItemActionParam != PLAYER_AP_FISHING_POLE) || (this->unk_860 == 0)) && + CHECK_BTN_ALL(sControlInput->press.button, BTN_Z)) { + + if (this->actor.category == ACTORCAT_PLAYER) { + actorToTarget = globalCtx->actorCtx.targetCtx.arrowPointedActor; + } + else { + actorToTarget = &GET_PLAYER(globalCtx)->actor; + } + + holdTarget = (gSaveContext.zTargetSetting != 0) || (this->actor.category != ACTORCAT_PLAYER); + this->stateFlags1 |= PLAYER_STATE1_15; + + if ((actorToTarget != NULL) && !(actorToTarget->flags & ACTOR_FLAG_27)) { + if ((actorToTarget == this->unk_664) && (this->actor.category == ACTORCAT_PLAYER)) { + actorToTarget = globalCtx->actorCtx.targetCtx.unk_94; + } + + if (actorToTarget != this->unk_664) { + if (!holdTarget) { + this->stateFlags2 |= PLAYER_STATE2_13; + } + this->unk_664 = actorToTarget; + this->unk_66C = 15; + this->stateFlags2 &= ~(PLAYER_STATE2_1 | PLAYER_STATE2_21); + } + else { + if (!holdTarget) { + func_8008EDF0(this); + } + } + + this->stateFlags1 &= ~PLAYER_STATE1_30; + } + else { + if (!(this->stateFlags1 & (PLAYER_STATE1_17 | PLAYER_STATE1_30))) { + func_808355DC(this); + } + } + } + + if (this->unk_664 != NULL) { + if ((this->actor.category == ACTORCAT_PLAYER) && (this->unk_664 != this->unk_684) && + func_8002F0C8(this->unk_664, this, sp1C)) { + func_8008EDF0(this); + this->stateFlags1 |= PLAYER_STATE1_30; + } + else if (this->unk_664 != NULL) { + this->unk_664->targetPriority = 40; + } + } + else if (this->unk_684 != NULL) { + this->unk_664 = this->unk_684; + } + } + + if (this->unk_664 != NULL) { + this->stateFlags1 &= ~(PLAYER_STATE1_16 | PLAYER_STATE1_17); + if ((this->stateFlags1 & PLAYER_STATE1_11) || + !CHECK_FLAG_ALL(this->unk_664->flags, ACTOR_FLAG_0 | ACTOR_FLAG_2)) { + this->stateFlags1 |= PLAYER_STATE1_16; + } + } + else { + if (this->stateFlags1 & PLAYER_STATE1_17) { + this->stateFlags2 &= ~PLAYER_STATE2_13; + } + else { + func_8008EE08(this); + } + } + } + else { + func_8008EE08(this); + } +} + +s32 func_80836FAC(GlobalContext* globalCtx, Player* this, f32* arg2, s16* arg3, f32 arg4) { + f32 temp_f2; + f32 temp_f0; + f32 temp_f14; + f32 temp_f12; + + if ((this->unk_6AD != 0) || (globalCtx->sceneLoadFlag == 0x14) || (this->stateFlags1 & PLAYER_STATE1_0)) { + *arg2 = 0.0f; + *arg3 = this->actor.shape.rot.y; + } + else { + *arg2 = D_808535D4; + *arg3 = D_808535D8; + + if (arg4 != 0.0f) { + *arg2 -= 20.0f; + if (*arg2 < 0.0f) { + *arg2 = 0.0f; + } + else { + temp_f2 = 1.0f - Math_CosS(*arg2 * 450.0f); + *arg2 = ((temp_f2 * temp_f2) * 30.0f) + 7.0f; + } + } + else { + *arg2 *= 0.8f; + } + + if (D_808535D4 != 0.0f) { + temp_f0 = Math_SinS(this->unk_898); + temp_f12 = this->unk_880; + temp_f14 = CLAMP(temp_f0, 0.0f, 0.6f); + + if (this->unk_6C4 != 0.0f) { + temp_f12 = temp_f12 - (this->unk_6C4 * 0.008f); + if (temp_f12 < 2.0f) { + temp_f12 = 2.0f; + } + } + + *arg2 = (*arg2 * 0.14f) - (8.0f * temp_f14 * temp_f14); + *arg2 = CLAMP(*arg2, 0.0f, temp_f12); + + return 1; + } + } + + return 0; +} + +s32 func_8083721C(Player* this) { + return Math_StepToF(&this->linearVelocity, 0.0f, REG(43) / 100.0f); +} + +s32 func_80837268(Player* this, f32* arg1, s16* arg2, f32 arg3, GlobalContext* globalCtx) { + if (!func_80836FAC(globalCtx, this, arg1, arg2, arg3)) { + *arg2 = this->actor.shape.rot.y; + + if (this->unk_664 != NULL) { + if ((globalCtx->actorCtx.targetCtx.unk_4B != 0) && !(this->stateFlags2 & PLAYER_STATE2_6)) { + *arg2 = Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk_664->focus.pos); + return 0; + } + } + else if (func_80833B2C(this)) { + *arg2 = this->targetYaw; + } + + return 0; + } + else { + *arg2 += Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx)); + return 1; + } +} + +static s8 D_808543E0[] = { 13, 2, 4, 9, 10, 11, 8, -7 }; +static s8 D_808543E8[] = { 13, 1, 2, 5, 3, 4, 9, 10, 11, 7, 8, -6 }; +static s8 D_808543F4[] = { 13, 1, 2, 3, 4, 9, 10, 11, 8, 7, -6 }; +static s8 D_80854400[] = { 13, 2, 4, 9, 10, 11, 8, -7 }; +static s8 D_80854408[] = { 13, 2, 4, 9, 10, 11, 12, 8, -7 }; +static s8 D_80854414[] = { -7 }; +static s8 D_80854418[] = { 0, 11, 1, 2, 3, 5, 4, 9, 8, 7, -6 }; +static s8 D_80854424[] = { 0, 11, 1, 2, 3, 12, 5, 4, 9, 8, 7, -6 }; +static s8 D_80854430[] = { 13, 1, 2, 3, 12, 5, 4, 9, 10, 11, 8, 7, -6 }; +static s8 D_80854440[] = { 10, 8, -7 }; +static s8 D_80854444[] = { 0, 12, 5, -4 }; + +static s32(*D_80854448[])(Player* this, GlobalContext* globalCtx) = { + func_8083B998, func_80839800, func_8083E5A8, func_8083E0FC, func_8083B644, func_8083F7BC, func_8083C1DC, + func_80850224, func_8083C544, func_8083EB44, func_8083BDBC, func_8083C2B0, func_80838A14, func_8083B040, +}; + +s32 func_80837348(GlobalContext* globalCtx, Player* this, s8* arg2, s32 arg3) { + s32 i; + + if (!(this->stateFlags1 & (PLAYER_STATE1_0 | PLAYER_STATE1_7 | PLAYER_STATE1_29))) { + if (arg3 != 0) { + D_808535E0 = func_80836670(this, globalCtx); + if (func_8084E604 == this->func_674) { + return 1; + } + } + + if (func_8008F128(this)) { + this->unk_6AE |= 0x41; + return 1; + } + + if (!(this->stateFlags1 & PLAYER_STATE1_8) && (func_80834A2C != this->func_82C)) { + while (*arg2 >= 0) { + if (D_80854448[*arg2](this, globalCtx)) { + return 1; + } + arg2++; + } + + if (D_80854448[-(*arg2)](this, globalCtx)) { + return 1; + } + } + } + + return 0; +} + +s32 func_808374A0(GlobalContext* globalCtx, Player* this, SkelAnime* skelAnime, f32 arg3) { + f32 sp24; + s16 sp22; + + if ((skelAnime->endFrame - arg3) <= skelAnime->curFrame) { + if (func_80837348(globalCtx, this, D_80854418, 1)) { + return 0; + } + + if (func_80837268(this, &sp24, &sp22, 0.018f, globalCtx)) { + return 1; + } + } + + return -1; +} + +void func_80837530(GlobalContext* globalCtx, Player* this, s32 arg2) { + if (arg2 != 0) { + this->unk_858 = 0.0f; + } + else { + this->unk_858 = 0.5f; + } + + this->stateFlags1 |= PLAYER_STATE1_12; + + if (this->actor.category == ACTORCAT_PLAYER) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_M_THUNDER, this->bodyPartsPos[PLAYER_BODYPART_WAIST].x, + this->bodyPartsPos[PLAYER_BODYPART_WAIST].y, this->bodyPartsPos[PLAYER_BODYPART_WAIST].z, 0, 0, 0, + Player_GetSwordHeld(this) | arg2); + } +} + +s32 func_808375D8(Player* this) { + s8 sp3C[4]; + s8* iter; + s8* iter2; + s8 temp1; + s8 temp2; + s32 i; + + if ((this->heldItemActionParam == PLAYER_AP_STICK) || Player_HoldsBrokenKnife(this)) { + return 0; + } + + iter = &this->unk_847[0]; + iter2 = &sp3C[0]; + for (i = 0; i < 4; i++, iter++, iter2++) { + if ((*iter2 = *iter) < 0) { + return 0; + } + *iter2 *= 2; + } + + temp1 = sp3C[0] - sp3C[1]; + if (ABS(temp1) < 10) { + return 0; + } + + iter2 = &sp3C[1]; + for (i = 1; i < 3; i++, iter2++) { + temp2 = *iter2 - *(iter2 + 1); + if ((ABS(temp2) < 10) || (temp2 * temp1 < 0)) { + return 0; + } + } + + return 1; +} + +void func_80837704(GlobalContext* globalCtx, Player* this) { + LinkAnimationHeader* anim; + + if ((this->swordAnimation >= 4) && (this->swordAnimation < 8)) { + anim = D_80854358[Player_HoldsTwoHandedWeapon(this)]; + } + else { + anim = D_80854350[Player_HoldsTwoHandedWeapon(this)]; + } + + func_80832318(this); + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, 8.0f, Animation_GetLastFrame(anim), ANIMMODE_ONCE, + -9.0f); + func_80837530(globalCtx, this, 0x200); +} + +void func_808377DC(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_80844E68, 1); + func_80837704(globalCtx, this); +} + +static s8 D_80854480[] = { 12, 4, 4, 8 }; +static s8 D_80854484[] = { 22, 23, 22, 23 }; + +s32 func_80837818(Player* this) { + s32 sp1C = this->unk_84B[this->unk_846]; + s32 sp18; + + if (this->heldItemActionParam == PLAYER_AP_HAMMER) { + if (sp1C < 0) { + sp1C = 0; + } + sp18 = D_80854484[sp1C]; + this->unk_845 = 0; + } + else { + if (func_808375D8(this)) { + sp18 = 24; + } + else { + if (sp1C < 0) { + if (func_80833BCC(this)) { + sp18 = 0; + } + else { + sp18 = 4; + } + } + else { + sp18 = D_80854480[sp1C]; + if (sp18 == 12) { + this->stateFlags2 |= PLAYER_STATE2_30; + if (!func_80833BCC(this)) { + sp18 = 0; + } + } + } + if (this->heldItemActionParam == PLAYER_AP_STICK) { + sp18 = 0; + } + } + if (Player_HoldsTwoHandedWeapon(this)) { + sp18++; + } + } + + return sp18; +} + +void func_80837918(Player* this, s32 quadIndex, u32 flags) { + this->swordQuads[quadIndex].info.toucher.dmgFlags = flags; + + if (flags == 2) { + this->swordQuads[quadIndex].info.toucherFlags = TOUCH_ON | TOUCH_NEAREST | TOUCH_SFX_WOOD; + } + else { + this->swordQuads[quadIndex].info.toucherFlags = TOUCH_ON | TOUCH_NEAREST; + } +} + +static u32 D_80854488[][2] = { + { 0x00000200, 0x08000000 }, { 0x00000100, 0x02000000 }, { 0x00000400, 0x04000000 }, + { 0x00000002, 0x08000000 }, { 0x00000040, 0x40000000 }, +}; + +void func_80837948(GlobalContext* globalCtx, Player* this, s32 arg2) { + s32 pad; + u32 flags; + s32 temp; + + func_80835C58(globalCtx, this, func_808502D0, 0); + this->unk_844 = 8; + if ((arg2 < 18) || (arg2 >= 20)) { + func_80832318(this); + } + + if ((arg2 != this->swordAnimation) || !(this->unk_845 < 3)) { + this->unk_845 = 0; + } + + this->unk_845++; + if (this->unk_845 >= 3) { + arg2 += 2; + } + + this->swordAnimation = arg2; + + func_808322D0(globalCtx, this, D_80854190[arg2].unk_00); + if ((arg2 != 16) && (arg2 != 17)) { + func_80832F54(globalCtx, this, 0x209); + } + + this->currentYaw = this->actor.shape.rot.y; + + if (Player_HoldsBrokenKnife(this)) { + temp = 1; + } + else { + temp = Player_GetSwordHeld(this) - 1; + } + + if ((arg2 >= 16) && (arg2 < 20)) { + flags = D_80854488[temp][1]; + } + else { + flags = D_80854488[temp][0]; + } + + func_80837918(this, 0, flags); + func_80837918(this, 1, flags); +} + +void func_80837AE0(Player* this, s32 timer) { + if (this->invincibilityTimer >= 0) { + this->invincibilityTimer = timer; + this->unk_88F = 0; + } +} + +void func_80837AFC(Player* this, s32 timer) { + if (this->invincibilityTimer > timer) { + this->invincibilityTimer = timer; + } + this->unk_88F = 0; +} + +s32 func_80837B18(GlobalContext* globalCtx, Player* this, s32 damage) { + if ((this->invincibilityTimer != 0) || (this->actor.category != ACTORCAT_PLAYER)) { + return 1; + } + + return Health_ChangeBy(globalCtx, damage); +} + +void func_80837B60(Player* this) { + this->skelAnime.prevTransl = this->skelAnime.jointTable[0]; + func_80832E48(this, 3); +} + +void func_80837B9C(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084411C, 0); + func_80832284(globalCtx, this, &gPlayerAnim_003040); + this->unk_850 = 1; + if (this->unk_6AD != 3) { + this->unk_6AD = 0; + } +} + +static LinkAnimationHeader* D_808544B0[] = { + &gPlayerAnim_002F80, &gPlayerAnim_002F78, &gPlayerAnim_002DE0, &gPlayerAnim_002DD8, + &gPlayerAnim_002F70, &gPlayerAnim_002528, &gPlayerAnim_002DC8, &gPlayerAnim_0024F0, +}; + +void func_80837C0C(GlobalContext* globalCtx, Player* this, s32 arg2, f32 arg3, f32 arg4, s16 arg5, s32 arg6) { + LinkAnimationHeader* sp2C = NULL; + LinkAnimationHeader** sp28; + + if (this->stateFlags1 & PLAYER_STATE1_13) { + func_80837B60(this); + } + + this->unk_890 = 0; + + func_8002F7DC(&this->actor, NA_SE_PL_DAMAGE); + + if (!func_80837B18(globalCtx, this, 0 - this->actor.colChkInfo.damage)) { + this->stateFlags2 &= ~PLAYER_STATE2_7; + if (!(this->actor.bgCheckFlags & 1) && !(this->stateFlags1 & PLAYER_STATE1_27)) { + func_80837B9C(this, globalCtx); + } + return; + } + + func_80837AE0(this, arg6); + + if (arg2 == 3) { + func_80835C58(globalCtx, this, func_8084FB10, 0); + + sp2C = &gPlayerAnim_002FD0; + + func_80832224(this); + func_8083264C(this, 255, 10, 40, 0); + + func_8002F7DC(&this->actor, NA_SE_PL_FREEZE_S); + func_80832698(this, NA_SE_VO_LI_FREEZE); + } + else if (arg2 == 4) { + func_80835C58(globalCtx, this, func_8084FBF4, 0); + + func_8083264C(this, 255, 80, 150, 0); + + func_808322A4(globalCtx, this, &gPlayerAnim_002F00); + func_80832224(this); + + this->unk_850 = 20; + } + else { + arg5 -= this->actor.shape.rot.y; + if (this->stateFlags1 & PLAYER_STATE1_27) { + func_80835C58(globalCtx, this, func_8084E30C, 0); + func_8083264C(this, 180, 20, 50, 0); + + this->linearVelocity = 4.0f; + this->actor.velocity.y = 0.0f; + + sp2C = &gPlayerAnim_003320; + + func_80832698(this, NA_SE_VO_LI_DAMAGE_S); + } + else if ((arg2 == 1) || (arg2 == 2) || !(this->actor.bgCheckFlags & 1) || + (this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_21))) { + func_80835C58(globalCtx, this, func_8084377C, 0); + + this->stateFlags3 |= PLAYER_STATE3_1; + + func_8083264C(this, 255, 20, 150, 0); + func_80832224(this); + + if (arg2 == 2) { + this->unk_850 = 4; + + this->actor.speedXZ = 3.0f; + this->linearVelocity = 3.0f; + this->actor.velocity.y = 6.0f; + + func_80832C2C(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_3][this->modelAnimType]); + func_80832698(this, NA_SE_VO_LI_DAMAGE_S); + } + else { + this->actor.speedXZ = arg3; + this->linearVelocity = arg3; + this->actor.velocity.y = arg4; + + if (ABS(arg5) > 0x4000) { + sp2C = &gPlayerAnim_002F58; + } + else { + sp2C = &gPlayerAnim_002DB0; + } + + if ((this->actor.category != ACTORCAT_PLAYER) && (this->actor.colChkInfo.health == 0)) { + func_80832698(this, NA_SE_VO_BL_DOWN); + } + else { + func_80832698(this, NA_SE_VO_LI_FALL_L); + } + } + + this->hoverBootsTimer = 0; + this->actor.bgCheckFlags &= ~1; + } + else { + if ((this->linearVelocity > 4.0f) && !func_8008E9C4(this)) { + this->unk_890 = 20; + func_8083264C(this, 120, 20, 10, 0); + func_80832698(this, NA_SE_VO_LI_DAMAGE_S); + return; + } + + sp28 = D_808544B0; + + func_80835C58(globalCtx, this, func_8084370C, 0); + func_80833C3C(this); + + if (this->actor.colChkInfo.damage < 5) { + func_8083264C(this, 120, 20, 10, 0); + } + else { + func_8083264C(this, 180, 20, 100, 0); + this->linearVelocity = 23.0f; + sp28 += 4; + } + + if (ABS(arg5) <= 0x4000) { + sp28 += 2; + } + + if (func_8008E9C4(this)) { + sp28 += 1; + } + + sp2C = *sp28; + + func_80832698(this, NA_SE_VO_LI_DAMAGE_S); + } + + this->actor.shape.rot.y += arg5; + this->currentYaw = this->actor.shape.rot.y; + this->actor.world.rot.y = this->actor.shape.rot.y; + if (ABS(arg5) > 0x4000) { + this->actor.shape.rot.y += 0x8000; + } + } + + func_80832564(globalCtx, this); + + this->stateFlags1 |= PLAYER_STATE1_26; + + if (sp2C != NULL) { + func_808322D0(globalCtx, this, sp2C); + } +} + +s32 func_80838144(s32 arg0) { + s32 temp = arg0 - 2; + + if ((temp >= 0) && (temp < 2)) { + return temp; + } + else { + return -1; + } +} + +s32 func_8083816C(s32 arg0) { + return (arg0 == 4) || (arg0 == 7) || (arg0 == 12); +} + +void func_8083819C(Player* this, GlobalContext* globalCtx) { + if (this->currentShield == PLAYER_SHIELD_DEKU) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_ITEM_SHIELD, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 1); + Inventory_DeleteEquipment(globalCtx, EQUIP_SHIELD); + Message_StartTextbox(globalCtx, 0x305F, NULL); + } +} + +void func_8083821C(Player* this) { + s32 i; + + // clang-format off + for (i = 0; i < 18; i++) { this->flameTimers[i] = Rand_S16Offset(0, 200); } + // clang-format on + + this->isBurning = true; +} + +void func_80838280(Player* this) { + if (this->actor.colChkInfo.acHitEffect == 1) { + func_8083821C(this); + } + func_80832698(this, NA_SE_VO_LI_FALL_L); +} + +void func_808382BC(Player* this) { + if ((this->invincibilityTimer >= 0) && (this->invincibilityTimer < 20)) { + this->invincibilityTimer = 20; + } +} + +s32 func_808382DC(Player* this, GlobalContext* globalCtx) { + s32 pad; + s32 sp68 = false; + s32 sp64; + + if (this->unk_A86 != 0) { + if (!Player_InBlockingCsMode(globalCtx, this)) { + Player_InflictDamage(globalCtx, -16); + this->unk_A86 = 0; + } + } + else { + sp68 = ((Player_GetHeight(this) - 8.0f) < (this->unk_6C4 * this->actor.scale.y)); + + if (sp68 || (this->actor.bgCheckFlags & 0x100) || (D_808535E4 == 9) || (this->stateFlags2 & PLAYER_STATE2_31)) { + func_80832698(this, NA_SE_VO_LI_DAMAGE_S); + + if (sp68) { + Gameplay_TriggerRespawn(globalCtx); + func_800994A0(globalCtx); + } + else { + // Special case for getting crushed in Forest Temple's Checkboard Ceiling Hall or Shadow Temple's + // Falling Spike Trap Room, to respawn the player in a specific place + if (((globalCtx->sceneNum == SCENE_BMORI1) && (globalCtx->roomCtx.curRoom.num == 15)) || + ((globalCtx->sceneNum == SCENE_HAKADAN) && (globalCtx->roomCtx.curRoom.num == 10))) { + static SpecialRespawnInfo checkboardCeilingRespawn = { { 1992.0f, 403.0f, -3432.0f }, 0 }; + static SpecialRespawnInfo fallingSpikeTrapRespawn = { { 1200.0f, -1343.0f, 3850.0f }, 0 }; + SpecialRespawnInfo* respawnInfo; + + if (globalCtx->sceneNum == SCENE_BMORI1) { + respawnInfo = &checkboardCeilingRespawn; + } + else { + respawnInfo = &fallingSpikeTrapRespawn; + } + + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_DOWN, 0xDFF); + gSaveContext.respawn[RESPAWN_MODE_DOWN].pos = respawnInfo->pos; + gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw = respawnInfo->yaw; + } + + Gameplay_TriggerVoidOut(globalCtx); + } + + func_80832698(this, NA_SE_VO_LI_TAKEN_AWAY); + globalCtx->unk_11DE9 = 1; + func_80078884(NA_SE_OC_ABYSS); + } + else if ((this->unk_8A1 != 0) && ((this->unk_8A1 >= 2) || (this->invincibilityTimer == 0))) { + u8 sp5C[] = { 2, 1, 1 }; + + func_80838280(this); + + if (this->unk_8A1 == 3) { + this->shockTimer = 40; + } + + this->actor.colChkInfo.damage += this->unk_8A0; + func_80837C0C(globalCtx, this, sp5C[this->unk_8A1 - 1], this->unk_8A4, this->unk_8A8, this->unk_8A2, 20); + } + else { + sp64 = (this->shieldQuad.base.acFlags & AC_BOUNCED) != 0; + + //! @bug The second set of conditions here seems intended as a way for Link to "block" hits by rolling. + // However, `Collider.atFlags` is a byte so the flag check at the end is incorrect and cannot work. + // Additionally, `Collider.atHit` can never be set while already colliding as AC, so it's also bugged. + // This behavior was later fixed in MM, most likely by removing both the `atHit` and `atFlags` checks. + if (sp64 || ((this->invincibilityTimer < 0) && (this->cylinder.base.acFlags & AC_HIT) && + (this->cylinder.info.atHit != NULL) && (this->cylinder.info.atHit->atFlags & 0x20000000))) { + + func_8083264C(this, 180, 20, 100, 0); + + if (!Player_IsChildWithHylianShield(this)) { + if (this->invincibilityTimer >= 0) { + LinkAnimationHeader* anim; + s32 sp54 = func_80843188 == this->func_674; + + if (!func_808332B8(this)) { + func_80835C58(globalCtx, this, func_808435C4, 0); + } + + if (!(this->unk_84F = sp54)) { + func_80833638(this, func_80834BD4); + + if (this->unk_870 < 0.5f) { + anim = D_808543BC[Player_HoldsTwoHandedWeapon(this)]; + } + else { + anim = D_808543B4[Player_HoldsTwoHandedWeapon(this)]; + } + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, anim); + } + else { + func_80832264(globalCtx, this, D_808543C4[Player_HoldsTwoHandedWeapon(this)]); + } + } + + if (!(this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_21))) { + this->linearVelocity = -18.0f; + this->currentYaw = this->actor.shape.rot.y; + } + } + + if (sp64 && (this->shieldQuad.info.acHitInfo->toucher.effect == 1)) { + func_8083819C(this, globalCtx); + } + + return 0; + } + + if ((this->unk_A87 != 0) || (this->invincibilityTimer > 0) || (this->stateFlags1 & PLAYER_STATE1_26) || + (this->csMode != 0) || (this->swordQuads[0].base.atFlags & AT_HIT) || + (this->swordQuads[1].base.atFlags & AT_HIT)) { + return 0; + } + + if (this->cylinder.base.acFlags & AC_HIT) { + Actor* ac = this->cylinder.base.ac; + s32 sp4C; + + if (ac->flags & ACTOR_FLAG_24) { + func_8002F7DC(&this->actor, NA_SE_PL_BODY_HIT); + } + + if (this->stateFlags1 & PLAYER_STATE1_27) { + sp4C = 0; + } + else if (this->actor.colChkInfo.acHitEffect == 2) { + sp4C = 3; + } + else if (this->actor.colChkInfo.acHitEffect == 3) { + sp4C = 4; + } + else if (this->actor.colChkInfo.acHitEffect == 4) { + sp4C = 1; + } + else { + func_80838280(this); + sp4C = 0; + } + + func_80837C0C(globalCtx, this, sp4C, 4.0f, 5.0f, Actor_WorldYawTowardActor(ac, &this->actor), 20); + } + else if (this->invincibilityTimer != 0) { + return 0; + } + else { + static u8 D_808544F4[] = { 120, 60 }; + s32 sp48 = func_80838144(D_808535E4); + + if (((this->actor.wallPoly != NULL) && + SurfaceType_IsWallDamage(&globalCtx->colCtx, this->actor.wallPoly, this->actor.wallBgId)) || + ((sp48 >= 0) && + SurfaceType_IsWallDamage(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId) && + (this->unk_A79 >= D_808544F4[sp48])) || + ((sp48 >= 0) && + ((this->currentTunic != PLAYER_TUNIC_GORON) || (this->unk_A79 >= D_808544F4[sp48])))) { + this->unk_A79 = 0; + this->actor.colChkInfo.damage = 4; + func_80837C0C(globalCtx, this, 0, 4.0f, 5.0f, this->actor.shape.rot.y, 20); + } + else { + return 0; + } + } + } + } + + return 1; +} + +void func_80838940(Player* this, LinkAnimationHeader* anim, f32 arg2, GlobalContext* globalCtx, u16 sfxId) { + func_80835C58(globalCtx, this, func_8084411C, 1); + + if (anim != NULL) { + func_808322D0(globalCtx, this, anim); + } + + this->actor.velocity.y = arg2 * D_808535E8; + this->hoverBootsTimer = 0; + this->actor.bgCheckFlags &= ~1; + + func_80832854(this); + func_80832698(this, sfxId); + + this->stateFlags1 |= PLAYER_STATE1_18; +} + +void func_808389E8(Player* this, LinkAnimationHeader* anim, f32 arg2, GlobalContext* globalCtx) { + func_80838940(this, anim, arg2, globalCtx, NA_SE_VO_LI_SWORD_N); +} + +s32 func_80838A14(Player* this, GlobalContext* globalCtx) { + s32 sp3C; + LinkAnimationHeader* sp38; + f32 sp34; + f32 temp; + f32 sp2C; + f32 sp28; + f32 sp24; + + if (!(this->stateFlags1 & PLAYER_STATE1_11) && (this->unk_88C >= 2) && + (!(this->stateFlags1 & PLAYER_STATE1_27) || (this->ageProperties->unk_14 > this->wallHeight))) { + sp3C = 0; + + if (func_808332B8(this)) { + if (this->actor.yDistToWater < 50.0f) { + if ((this->unk_88C < 2) || (this->wallHeight > this->ageProperties->unk_10)) { + return 0; + } + } + else if ((this->currentBoots != PLAYER_BOOTS_IRON) || (this->unk_88C > 2)) { + return 0; + } + } + else if (!(this->actor.bgCheckFlags & 1) || + ((this->ageProperties->unk_14 <= this->wallHeight) && (this->stateFlags1 & PLAYER_STATE1_27))) { + return 0; + } + + if ((this->actor.wallBgId != BGCHECK_SCENE) && (D_808535F0 & 0x40)) { + if (this->unk_88D >= 6) { + this->stateFlags2 |= PLAYER_STATE2_2; + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { + sp3C = 1; + } + } + } + else if ((this->unk_88D >= 6) || CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { + sp3C = 1; + } + + if (sp3C != 0) { + func_80835C58(globalCtx, this, func_80845668, 0); + + this->stateFlags1 |= PLAYER_STATE1_18; + + sp34 = this->wallHeight; + + if (this->ageProperties->unk_14 <= sp34) { + sp38 = &gPlayerAnim_002D48; + this->linearVelocity = 1.0f; + } + else { + sp2C = COLPOLY_GET_NORMAL(this->actor.wallPoly->normal.x); + sp28 = COLPOLY_GET_NORMAL(this->actor.wallPoly->normal.z); + sp24 = this->wallDistance + 0.5f; + + this->stateFlags1 |= PLAYER_STATE1_14; + + if (func_808332B8(this)) { + sp38 = &gPlayerAnim_0032E8; + sp34 -= (60.0f * this->ageProperties->unk_08); + this->stateFlags1 &= ~PLAYER_STATE1_27; + } + else if (this->ageProperties->unk_18 <= sp34) { + sp38 = &gPlayerAnim_002D40; + sp34 -= (59.0f * this->ageProperties->unk_08); + } + else { + sp38 = &gPlayerAnim_002D38; + sp34 -= (41.0f * this->ageProperties->unk_08); + } + + this->actor.shape.yOffset -= sp34 * 100.0f; + + this->actor.world.pos.x -= sp24 * sp2C; + this->actor.world.pos.y += this->wallHeight; + this->actor.world.pos.z -= sp24 * sp28; + + func_80832224(this); + } + + this->actor.bgCheckFlags |= 1; + + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, sp38, 1.3f); + AnimationContext_DisableQueue(globalCtx); + + this->actor.shape.rot.y = this->currentYaw = this->actor.wallYaw + 0x8000; + + return 1; + } + } + else if ((this->actor.bgCheckFlags & 1) && (this->unk_88C == 1) && (this->unk_88D >= 3)) { + temp = (this->wallHeight * 0.08f) + 5.5f; + func_808389E8(this, &gPlayerAnim_002FE0, temp, globalCtx); + this->linearVelocity = 2.5f; + + return 1; + } + + return 0; +} + +void func_80838E70(GlobalContext* globalCtx, Player* this, f32 arg2, s16 arg3) { + func_80835C58(globalCtx, this, func_80845CA4, 0); + func_80832440(globalCtx, this); + + this->unk_84F = 1; + this->unk_850 = 1; + + this->unk_450.x = (Math_SinS(arg3) * arg2) + this->actor.world.pos.x; + this->unk_450.z = (Math_CosS(arg3) * arg2) + this->actor.world.pos.z; + + func_80832264(globalCtx, this, func_80833338(this)); +} + +void func_80838F18(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084D610, 0); + func_80832C6C(globalCtx, this, &gPlayerAnim_003328); +} + +void func_80838F5C(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084F88C, 0); + + this->stateFlags1 |= PLAYER_STATE1_29 | PLAYER_STATE1_31; + + Camera_ChangeSetting(Gameplay_GetCamera(globalCtx, 0), CAM_SET_FREE0); +} + +s32 func_80838FB8(GlobalContext* globalCtx, Player* this) { + if ((globalCtx->sceneLoadFlag == 0) && (this->stateFlags1 & PLAYER_STATE1_31)) { + func_80838F5C(globalCtx, this); + func_80832284(globalCtx, this, &gPlayerAnim_003040); + func_80832698(this, NA_SE_VO_LI_FALL_S); + func_800788CC(NA_SE_OC_SECRET_WARP_IN); + return 1; + } + + return 0; +} + +s16 D_808544F8[] = { + 0x045B, // DMT from Magic Fairy Fountain + 0x0482, // DMC from Double Defense Fairy Fountain + 0x0340, // Hyrule Castle from Dins Fire Fairy Fountain + 0x044B, // Kakariko from Potion Shop + 0x02A2, // Market (child day) from Potion Shop + 0x0201, // Kakariko from Bazaar + 0x03B8, // Market (child day) from Bazaar + 0x04EE, // Kakariko from House of Skulltulas + 0x03C0, // Back Alley (day) from Bombchu Shop + 0x0463, // Kakariko from Shooting Gallery + 0x01CD, // Market (child day) from Shooting Gallery + 0x0394, // Zoras Fountain from Farores Wind Fairy Fountain + 0x0340, // Hyrule Castle from Dins Fire Fairy Fountain + 0x057C, // Desert Colossus from Nayrus Love Fairy Fountain +}; + +u8 D_80854514[] = { 11, 9, 3, 5, 7, 0 }; + +s32 func_80839034(GlobalContext* globalCtx, Player* this, CollisionPoly* poly, u32 bgId) { + s32 sp3C; + s32 temp; + s32 sp34; + f32 linearVel; + s32 yaw; + + if (this->actor.category == ACTORCAT_PLAYER) { + sp3C = 0; + + if (!(this->stateFlags1 & PLAYER_STATE1_7) && (globalCtx->sceneLoadFlag == 0) && (this->csMode == 0) && + !(this->stateFlags1 & PLAYER_STATE1_0) && + (((poly != NULL) && (sp3C = SurfaceType_GetSceneExitIndex(&globalCtx->colCtx, poly, bgId), sp3C != 0)) || + (func_8083816C(D_808535E4) && (this->unk_A7A == 12)))) { + + sp34 = this->unk_A84 - (s32)this->actor.world.pos.y; + + if (!(this->stateFlags1 & (PLAYER_STATE1_23 | PLAYER_STATE1_27 | PLAYER_STATE1_29)) && + !(this->actor.bgCheckFlags & 1) && (sp34 < 100) && (D_80853600 > 100.0f)) { + return 0; + } + + if (sp3C == 0) { + Gameplay_TriggerVoidOut(globalCtx); + func_800994A0(globalCtx); + } + else { + globalCtx->nextEntranceIndex = globalCtx->setupExitList[sp3C - 1]; + if (globalCtx->nextEntranceIndex == 0x7FFF) { + gSaveContext.respawnFlag = 2; + globalCtx->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_RETURN].entranceIndex; + globalCtx->fadeTransition = 3; + gSaveContext.nextTransition = 3; + } + else if (globalCtx->nextEntranceIndex >= 0x7FF9) { + globalCtx->nextEntranceIndex = + D_808544F8[D_80854514[globalCtx->nextEntranceIndex - 0x7FF9] + globalCtx->curSpawn]; + func_800994A0(globalCtx); + } + else { + if (SurfaceType_GetSlope(&globalCtx->colCtx, poly, bgId) == 2) { + gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = globalCtx->nextEntranceIndex; + Gameplay_TriggerVoidOut(globalCtx); + gSaveContext.respawnFlag = -2; + } + gSaveContext.unk_13C3 = 1; + func_800994A0(globalCtx); + } + globalCtx->sceneLoadFlag = 0x14; + } + + if (!(this->stateFlags1 & (PLAYER_STATE1_23 | PLAYER_STATE1_29)) && + !(this->stateFlags2 & PLAYER_STATE2_18) && !func_808332B8(this) && + (temp = func_80041D4C(&globalCtx->colCtx, poly, bgId), (temp != 10)) && + ((sp34 < 100) || (this->actor.bgCheckFlags & 1))) { + + if (temp == 11) { + func_800788CC(NA_SE_OC_SECRET_HOLE_OUT); + func_800F6964(5); + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + } + else { + linearVel = this->linearVelocity; + + if (linearVel < 0.0f) { + this->actor.world.rot.y += 0x8000; + linearVel = -linearVel; + } + + if (linearVel > R_RUN_SPEED_LIMIT / 100.0f) { + gSaveContext.entranceSpeed = R_RUN_SPEED_LIMIT / 100.0f; + } + else { + gSaveContext.entranceSpeed = linearVel; + } + + if (D_808535F4 != 0) { + yaw = D_808535FC; + } + else { + yaw = this->actor.world.rot.y; + } + func_80838E70(globalCtx, this, 400.0f, yaw); + } + } + else { + if (!(this->actor.bgCheckFlags & 1)) { + func_80832210(this); + } + } + + this->stateFlags1 |= PLAYER_STATE1_0 | PLAYER_STATE1_29; + + func_80835E44(globalCtx, 0x2F); + + return 1; + } + else { + if (globalCtx->sceneLoadFlag == 0) { + + if ((this->actor.world.pos.y < -4000.0f) || + (((this->unk_A7A == 5) || (this->unk_A7A == 12)) && + ((D_80853600 < 100.0f) || (this->fallDistance > 400.0f) || + ((globalCtx->sceneNum != SCENE_HAKADAN) && (this->fallDistance > 200.0f)))) || + ((globalCtx->sceneNum == SCENE_GANON_FINAL) && (this->fallDistance > 320.0f))) { + + if (this->actor.bgCheckFlags & 1) { + if (this->unk_A7A == 5) { + Gameplay_TriggerRespawn(globalCtx); + } + else { + Gameplay_TriggerVoidOut(globalCtx); + } + globalCtx->fadeTransition = 4; + func_80078884(NA_SE_OC_ABYSS); + } + else { + func_80838F5C(globalCtx, this); + this->unk_850 = 9999; + if (this->unk_A7A == 5) { + this->unk_84F = -1; + } + else { + this->unk_84F = 1; + } + } + } + + this->unk_A84 = this->actor.world.pos.y; + } + } + } + + return 0; +} + +void func_808395DC(Player* this, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3) { + f32 cos = Math_CosS(this->actor.shape.rot.y); + f32 sin = Math_SinS(this->actor.shape.rot.y); + + arg3->x = arg1->x + ((arg2->x * cos) + (arg2->z * sin)); + arg3->y = arg1->y + arg2->y; + arg3->z = arg1->z + ((arg2->z * cos) - (arg2->x * sin)); +} + +Actor* Player_SpawnFairy(GlobalContext* globalCtx, Player* this, Vec3f* arg2, Vec3f* arg3, s32 type) { + Vec3f pos; + + func_808395DC(this, arg2, arg3, &pos); + + return Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ELF, pos.x, pos.y, pos.z, 0, 0, 0, type); +} + +f32 func_808396F4(GlobalContext* globalCtx, Player* this, Vec3f* arg2, Vec3f* arg3, CollisionPoly** arg4, s32* arg5) { + func_808395DC(this, &this->actor.world.pos, arg2, arg3); + + return BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, arg4, arg5, arg3); +} + +f32 func_8083973C(GlobalContext* globalCtx, Player* this, Vec3f* arg2, Vec3f* arg3) { + CollisionPoly* sp24; + s32 sp20; + + return func_808396F4(globalCtx, this, arg2, arg3, &sp24, &sp20); +} + +s32 func_80839768(GlobalContext* globalCtx, Player* this, Vec3f* arg2, CollisionPoly** arg3, s32* arg4, Vec3f* arg5) { + Vec3f sp44; + Vec3f sp38; + + sp44.x = this->actor.world.pos.x; + sp44.y = this->actor.world.pos.y + arg2->y; + sp44.z = this->actor.world.pos.z; + + func_808395DC(this, &this->actor.world.pos, arg2, &sp38); + + return BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp44, &sp38, arg5, arg3, true, false, false, true, arg4); +} + +s32 func_80839800(Player* this, GlobalContext* globalCtx) { + DoorShutter* doorShutter; + EnDoor* door; // Can also be DoorKiller* + s32 doorDirection; + f32 sp78; + f32 sp74; + Actor* doorActor; + f32 sp6C; + s32 pad3; + s32 frontRoom; + Actor* attachedActor; + LinkAnimationHeader* sp5C; + CollisionPoly* sp58; + Vec3f sp4C; + + if ((this->doorType != PLAYER_DOORTYPE_NONE) && + (!(this->stateFlags1 & PLAYER_STATE1_11) || + ((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)))) { + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (func_8084F9A0 == this->func_674)) { + doorActor = this->doorActor; + + if (this->doorType <= PLAYER_DOORTYPE_AJAR) { + doorActor->textId = 0xD0; + func_80853148(globalCtx, doorActor); + return 0; + } + + doorDirection = this->doorDirection; + sp78 = Math_CosS(doorActor->shape.rot.y); + sp74 = Math_SinS(doorActor->shape.rot.y); + + if (this->doorType == PLAYER_DOORTYPE_SLIDING) { + doorShutter = (DoorShutter*)doorActor; + + this->currentYaw = doorShutter->dyna.actor.home.rot.y; + if (doorDirection > 0) { + this->currentYaw -= 0x8000; + } + this->actor.shape.rot.y = this->currentYaw; + + if (this->linearVelocity <= 0.0f) { + this->linearVelocity = 0.1f; + } + + func_80838E70(globalCtx, this, 50.0f, this->actor.shape.rot.y); + + this->unk_84F = 0; + this->unk_447 = this->doorType; + this->stateFlags1 |= PLAYER_STATE1_29; + + this->unk_450.x = this->actor.world.pos.x + ((doorDirection * 20.0f) * sp74); + this->unk_450.z = this->actor.world.pos.z + ((doorDirection * 20.0f) * sp78); + this->unk_45C.x = this->actor.world.pos.x + ((doorDirection * -120.0f) * sp74); + this->unk_45C.z = this->actor.world.pos.z + ((doorDirection * -120.0f) * sp78); + + doorShutter->unk_164 = 1; + func_80832224(this); + + if (this->doorTimer != 0) { + this->unk_850 = 0; + func_80832B0C(globalCtx, this, func_80833338(this)); + this->skelAnime.endFrame = 0.0f; + } + else { + this->linearVelocity = 0.1f; + } + + if (doorShutter->dyna.actor.category == ACTORCAT_DOOR) { + this->unk_46A = globalCtx->transiActorCtx.list[(u16)doorShutter->dyna.actor.params >> 10] + .sides[(doorDirection > 0) ? 0 : 1] + .effects; + + func_800304B0(globalCtx); + } + } + else { + // This actor can be either EnDoor or DoorKiller. + // Don't try to access any struct vars other than `animStyle` and `playerIsOpening`! These two variables + // are common across the two actors' structs however most other variables are not! + door = (EnDoor*)doorActor; + + door->animStyle = (doorDirection < 0.0f) ? (LINK_IS_ADULT ? KNOB_ANIM_ADULT_L : KNOB_ANIM_CHILD_L) + : (LINK_IS_ADULT ? KNOB_ANIM_ADULT_R : KNOB_ANIM_CHILD_R); + + if (door->animStyle == KNOB_ANIM_ADULT_L) { + sp5C = D_80853914[PLAYER_ANIMGROUP_9][this->modelAnimType]; + } + else if (door->animStyle == KNOB_ANIM_CHILD_L) { + sp5C = D_80853914[PLAYER_ANIMGROUP_10][this->modelAnimType]; + } + else if (door->animStyle == KNOB_ANIM_ADULT_R) { + sp5C = D_80853914[PLAYER_ANIMGROUP_11][this->modelAnimType]; + } + else { + sp5C = D_80853914[PLAYER_ANIMGROUP_12][this->modelAnimType]; + } + + func_80835C58(globalCtx, this, func_80845EF8, 0); + func_80832528(globalCtx, this); + + if (doorDirection < 0) { + this->actor.shape.rot.y = doorActor->shape.rot.y; + } + else { + this->actor.shape.rot.y = doorActor->shape.rot.y - 0x8000; + } + + this->currentYaw = this->actor.shape.rot.y; + + sp6C = (doorDirection * 22.0f); + this->actor.world.pos.x = doorActor->world.pos.x + sp6C * sp74; + this->actor.world.pos.z = doorActor->world.pos.z + sp6C * sp78; + + func_8083328C(globalCtx, this, sp5C); + + if (this->doorTimer != 0) { + this->skelAnime.endFrame = 0.0f; + } + + func_80832224(this); + func_80832F54(globalCtx, this, 0x28F); + + if (doorActor->parent != NULL) { + doorDirection = -doorDirection; + } + + door->playerIsOpening = 1; + + if (this->doorType != PLAYER_DOORTYPE_FAKE) { + this->stateFlags1 |= PLAYER_STATE1_29; + func_800304B0(globalCtx); + + if (((doorActor->params >> 7) & 7) == 3) { + sp4C.x = doorActor->world.pos.x - (sp6C * sp74); + sp4C.y = doorActor->world.pos.y + 10.0f; + sp4C.z = doorActor->world.pos.z - (sp6C * sp78); + + BgCheck_EntityRaycastFloor1(&globalCtx->colCtx, &sp58, &sp4C); + + if (func_80839034(globalCtx, this, sp58, BGCHECK_SCENE)) { + gSaveContext.entranceSpeed = 2.0f; + gSaveContext.entranceSound = NA_SE_OC_DOOR_OPEN; + } + } + else { + Camera_ChangeDoorCam(Gameplay_GetCamera(globalCtx, 0), doorActor, + globalCtx->transiActorCtx.list[(u16)doorActor->params >> 10] + .sides[(doorDirection > 0) ? 0 : 1] + .effects, + 0, 38.0f * D_808535EC, 26.0f * D_808535EC, 10.0f * D_808535EC); + } + } + } + + if ((this->doorType != PLAYER_DOORTYPE_FAKE) && (doorActor->category == ACTORCAT_DOOR)) { + frontRoom = globalCtx->transiActorCtx.list[(u16)doorActor->params >> 10] + .sides[(doorDirection > 0) ? 0 : 1] + .room; + + if ((frontRoom >= 0) && (frontRoom != globalCtx->roomCtx.curRoom.num)) { + func_8009728C(globalCtx, &globalCtx->roomCtx, frontRoom); + } + } + + doorActor->room = globalCtx->roomCtx.curRoom.num; + + if (((attachedActor = doorActor->child) != NULL) || ((attachedActor = doorActor->parent) != NULL)) { + attachedActor->room = globalCtx->roomCtx.curRoom.num; + } + + return 1; + } + } + + return 0; +} + +void func_80839E88(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + + func_80835C58(globalCtx, this, func_80840450, 1); + + if (this->unk_870 < 0.5f) { + anim = func_808334E4(this); + this->unk_870 = 0.0f; + } + else { + anim = func_80833528(this); + this->unk_870 = 1.0f; + } + + this->unk_874 = this->unk_870; + func_80832284(globalCtx, this, anim); + this->currentYaw = this->actor.shape.rot.y; +} + +void func_80839F30(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_808407CC, 1); + func_80832B0C(globalCtx, this, func_80833338(this)); + this->currentYaw = this->actor.shape.rot.y; +} + +void func_80839F90(Player* this, GlobalContext* globalCtx) { + if (func_8008E9C4(this)) { + func_80839E88(this, globalCtx); + } + else if (func_80833B2C(this)) { + func_80839F30(this, globalCtx); + } + else { + func_80853080(this, globalCtx); + } +} + +void func_80839FFC(Player* this, GlobalContext* globalCtx) { + PlayerFunc674 func; + + if (func_8008E9C4(this)) { + func = func_80840450; + } + else if (func_80833B2C(this)) { + func = func_808407CC; + } + else { + func = func_80840BC8; + } + + func_80835C58(globalCtx, this, func, 1); +} + +void func_8083A060(Player* this, GlobalContext* globalCtx) { + func_80839FFC(this, globalCtx); + if (func_8008E9C4(this)) { + this->unk_850 = 1; + } +} + +void func_8083A098(Player* this, LinkAnimationHeader* anim, GlobalContext* globalCtx) { + func_8083A060(this, globalCtx); + func_8083328C(globalCtx, this, anim); +} + +s32 func_8083A0D4(Player* this) { + return (this->interactRangeActor != NULL) && (this->heldActor == NULL); +} + +void func_8083A0F4(GlobalContext* globalCtx, Player* this) { + if (func_8083A0D4(this)) { + Actor* interactRangeActor = this->interactRangeActor; + s32 interactActorId = interactRangeActor->id; + + if (interactActorId == ACTOR_BG_TOKI_SWD) { + this->interactRangeActor->parent = &this->actor; + func_80835C58(globalCtx, this, func_8084F608, 0); + this->stateFlags1 |= PLAYER_STATE1_29; + } + else { + LinkAnimationHeader* anim; + + if (interactActorId == ACTOR_BG_HEAVY_BLOCK) { + func_80835C58(globalCtx, this, func_80846120, 0); + this->stateFlags1 |= PLAYER_STATE1_29; + anim = &gPlayerAnim_002F98; + } + else if ((interactActorId == ACTOR_EN_ISHI) && ((interactRangeActor->params & 0xF) == 1)) { + func_80835C58(globalCtx, this, func_80846260, 0); + anim = &gPlayerAnim_0032B0; + } + else if (((interactActorId == ACTOR_EN_BOMBF) || (interactActorId == ACTOR_EN_KUSA)) && + (Player_GetStrength() <= PLAYER_STR_NONE)) { + func_80835C58(globalCtx, this, func_80846408, 0); + this->actor.world.pos.x = + (Math_SinS(interactRangeActor->yawTowardsPlayer) * 20.0f) + interactRangeActor->world.pos.x; + this->actor.world.pos.z = + (Math_CosS(interactRangeActor->yawTowardsPlayer) * 20.0f) + interactRangeActor->world.pos.z; + this->currentYaw = this->actor.shape.rot.y = interactRangeActor->yawTowardsPlayer + 0x8000; + anim = &gPlayerAnim_003060; + } + else { + func_80835C58(globalCtx, this, func_80846050, 0); + anim = D_80853914[PLAYER_ANIMGROUP_13][this->modelAnimType]; + } + + func_80832264(globalCtx, this, anim); + } + } + else { + func_80839F90(this, globalCtx); + this->stateFlags1 &= ~PLAYER_STATE1_11; + } +} + +void func_8083A2F8(GlobalContext* globalCtx, Player* this) { + func_80835DAC(globalCtx, this, func_8084B530, 0); + + this->stateFlags1 |= PLAYER_STATE1_6 | PLAYER_STATE1_29; + + if (this->actor.textId != 0) { + Message_StartTextbox(globalCtx, this->actor.textId, this->targetActor); + this->unk_664 = this->targetActor; + } +} + +void func_8083A360(GlobalContext* globalCtx, Player* this) { + func_80835DAC(globalCtx, this, func_8084CC98, 0); +} + +void func_8083A388(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084B78C, 0); +} + +void func_8083A3B0(GlobalContext* globalCtx, Player* this) { + s32 sp1C = this->unk_850; + s32 sp18 = this->unk_84F; + + func_80835DAC(globalCtx, this, func_8084BF1C, 0); + this->actor.velocity.y = 0.0f; + + this->unk_850 = sp1C; + this->unk_84F = sp18; +} + +void func_8083A40C(GlobalContext* globalCtx, Player* this) { + func_80835DAC(globalCtx, this, func_8084C760, 0); +} + +void func_8083A434(GlobalContext* globalCtx, Player* this) { + func_80835DAC(globalCtx, this, func_8084E6D4, 0); + + this->stateFlags1 |= PLAYER_STATE1_10 | PLAYER_STATE1_29; + + if (this->getItemId == GI_HEART_CONTAINER_2) { + this->unk_850 = 20; + } + else if (this->getItemId >= 0) { + this->unk_850 = 1; + } + else { + this->getItemId = -this->getItemId; + } +} + +s32 func_8083A4A8(Player* this, GlobalContext* globalCtx) { + s16 yawDiff; + LinkAnimationHeader* anim; + f32 temp; + + yawDiff = this->currentYaw - this->actor.shape.rot.y; + + if ((ABS(yawDiff) < 0x1000) && (this->linearVelocity > 4.0f)) { + anim = &gPlayerAnim_003148; + } + else { + anim = &gPlayerAnim_002FE0; + } + + if (this->linearVelocity > (IREG(66) / 100.0f)) { + temp = IREG(67) / 100.0f; + } + else { + temp = (IREG(68) / 100.0f) + ((IREG(69) * this->linearVelocity) / 1000.0f); + } + + func_80838940(this, anim, temp, globalCtx, NA_SE_VO_LI_AUTO_JUMP); + this->unk_850 = 1; + + return 1; +} + +void func_8083A5C4(GlobalContext* globalCtx, Player* this, CollisionPoly* arg2, f32 arg3, LinkAnimationHeader* arg4) { + f32 sp24 = COLPOLY_GET_NORMAL(arg2->normal.x); + f32 sp20 = COLPOLY_GET_NORMAL(arg2->normal.z); + + func_80835C58(globalCtx, this, func_8084BBE4, 0); + func_80832564(globalCtx, this); + func_80832264(globalCtx, this, arg4); + + this->actor.world.pos.x -= (arg3 + 1.0f) * sp24; + this->actor.world.pos.z -= (arg3 + 1.0f) * sp20; + this->actor.shape.rot.y = this->currentYaw = Math_Atan2S(sp20, sp24); + + func_80832224(this); + func_80832CFC(this); +} + +s32 func_8083A6AC(Player* this, GlobalContext* globalCtx) { + CollisionPoly* sp84; + s32 sp80; + Vec3f sp74; + Vec3f sp68; + f32 temp1; + + if ((this->actor.yDistToWater < -80.0f) && (ABS(this->unk_898) < 2730) && (ABS(this->unk_89A) < 2730)) { + sp74.x = this->actor.prevPos.x - this->actor.world.pos.x; + sp74.z = this->actor.prevPos.z - this->actor.world.pos.z; + + temp1 = sqrtf(SQ(sp74.x) + SQ(sp74.z)); + if (temp1 != 0.0f) { + temp1 = 5.0f / temp1; + } + else { + temp1 = 0.0f; + } + + sp74.x = this->actor.prevPos.x + (sp74.x * temp1); + sp74.y = this->actor.world.pos.y; + sp74.z = this->actor.prevPos.z + (sp74.z * temp1); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &this->actor.world.pos, &sp74, &sp68, &sp84, true, false, false, + true, &sp80) && + (ABS(sp84->normal.y) < 600)) { + f32 nx = COLPOLY_GET_NORMAL(sp84->normal.x); + f32 ny = COLPOLY_GET_NORMAL(sp84->normal.y); + f32 nz = COLPOLY_GET_NORMAL(sp84->normal.z); + f32 sp54; + s32 sp50; + + sp54 = Math3D_UDistPlaneToPos(nx, ny, nz, sp84->dist, &this->actor.world.pos); + + sp50 = D_80853604 == 6; + if (!sp50 && (func_80041DB8(&globalCtx->colCtx, sp84, sp80) & 8)) { + sp50 = 1; + } + + func_8083A5C4(globalCtx, this, sp84, sp54, sp50 ? &gPlayerAnim_002D88 : &gPlayerAnim_002F10); + + if (sp50) { + func_80836898(globalCtx, this, func_8083A3B0); + + this->currentYaw += 0x8000; + this->actor.shape.rot.y = this->currentYaw; + + this->stateFlags1 |= PLAYER_STATE1_21; + func_80832F54(globalCtx, this, 0x9F); + + this->unk_850 = -1; + this->unk_84F = sp50; + } + else { + this->stateFlags1 |= PLAYER_STATE1_13; + this->stateFlags1 &= ~PLAYER_STATE1_17; + } + + func_8002F7DC(&this->actor, NA_SE_PL_SLIPDOWN); + func_80832698(this, NA_SE_VO_LI_HANG); + return 1; + } + } + + return 0; +} + +void func_8083A9B8(Player* this, LinkAnimationHeader* anim, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084BDFC, 0); + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, anim, 1.3f); +} + +static Vec3f D_8085451C = { 0.0f, 0.0f, 100.0f }; + +void func_8083AA10(Player* this, GlobalContext* globalCtx) { + s32 sp5C; + CollisionPoly* sp58; + s32 sp54; + WaterBox* sp50; + Vec3f sp44; + f32 sp40; + f32 sp3C; + + this->fallDistance = this->fallStartHeight - (s32)this->actor.world.pos.y; + + if (!(this->stateFlags1 & (PLAYER_STATE1_27 | PLAYER_STATE1_29)) && !(this->actor.bgCheckFlags & 1)) { + if (!func_80838FB8(globalCtx, this)) { + if (D_80853604 == 8) { + this->actor.world.pos.x = this->actor.prevPos.x; + this->actor.world.pos.z = this->actor.prevPos.z; + return; + } + + if (!(this->stateFlags3 & PLAYER_STATE3_1) && !(this->skelAnime.moveFlags & 0x80) && + (func_8084411C != this->func_674) && (func_80844A44 != this->func_674)) { + + if ((D_80853604 == 7) || (this->swordState != 0)) { + Math_Vec3f_Copy(&this->actor.world.pos, &this->actor.prevPos); + func_80832210(this); + return; + } + + if (this->hoverBootsTimer != 0) { + this->actor.velocity.y = 1.0f; + D_80853604 = 9; + return; + } + + sp5C = (s16)(this->currentYaw - this->actor.shape.rot.y); + + func_80835C58(globalCtx, this, func_8084411C, 1); + func_80832440(globalCtx, this); + + this->unk_89E = this->unk_A82; + + if ((this->actor.bgCheckFlags & 4) && !(this->stateFlags1 & PLAYER_STATE1_27) && (D_80853604 != 6) && + (D_80853604 != 9) && (D_80853600 > 20.0f) && (this->swordState == 0) && (ABS(sp5C) < 0x2000) && + (this->linearVelocity > 3.0f)) { + + if ((D_80853604 == 11) && !(this->stateFlags1 & PLAYER_STATE1_11)) { + + sp40 = func_808396F4(globalCtx, this, &D_8085451C, &sp44, &sp58, &sp54); + sp3C = this->actor.world.pos.y; + + if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, sp44.x, sp44.z, &sp3C, &sp50) && + ((sp3C - sp40) > 50.0f)) { + func_808389E8(this, &gPlayerAnim_003158, 6.0f, globalCtx); + func_80835C58(globalCtx, this, func_80844A44, 0); + return; + } + } + + func_8083A4A8(this, globalCtx); + return; + } + + if ((D_80853604 == 9) || (D_80853600 <= this->ageProperties->unk_34) || + !func_8083A6AC(this, globalCtx)) { + func_80832284(globalCtx, this, &gPlayerAnim_003040); + return; + } + } + } + } + else { + this->fallStartHeight = this->actor.world.pos.y; + } +} + +s32 func_8083AD4C(GlobalContext* globalCtx, Player* this) { + s32 cameraMode; + + if (this->unk_6AD == 2) { + if (func_8002DD6C(this)) { + if (LINK_IS_ADULT) { + cameraMode = CAM_MODE_BOWARROW; + } + else { + cameraMode = CAM_MODE_SLINGSHOT; + } + } + else { + cameraMode = CAM_MODE_BOOMERANG; + } + } + else { + cameraMode = CAM_MODE_FIRSTPERSON; + } + + return Camera_ChangeMode(Gameplay_GetCamera(globalCtx, 0), cameraMode); +} + +s32 func_8083ADD4(GlobalContext* globalCtx, Player* this) { + if (this->unk_6AD == 3) { + func_80835C58(globalCtx, this, func_80852E14, 0); + if (this->unk_46A != 0) { + this->stateFlags1 |= PLAYER_STATE1_29; + } + func_80832318(this); + return 1; + } + else { + return 0; + } +} + +void func_8083AE40(Player* this, s16 objectId) { + s32 pad; + u32 size; + + if (objectId != OBJECT_INVALID) { + this->giObjectLoading = true; + osCreateMesgQueue(&this->giObjectLoadQueue, &this->giObjectLoadMsg, 1); + + size = gObjectTable[objectId].vromEnd - gObjectTable[objectId].vromStart; + + LOG_HEX("size", size, "../z_player.c", 9090); + ASSERT(size <= 1024 * 8, "size <= 1024 * 8", "../z_player.c", 9091); + + DmaMgr_SendRequest2(&this->giObjectDmaRequest, (u32)this->giObjectSegment, gObjectTable[objectId].vromStart, + size, 0, &this->giObjectLoadQueue, NULL, "../z_player.c", 9099); + } +} + +void func_8083AF44(GlobalContext* globalCtx, Player* this, s32 magicSpell) { + func_80835DE4(globalCtx, this, func_808507F4, 0); + + this->unk_84F = magicSpell - 3; + func_80087708(globalCtx, sMagicSpellCosts[magicSpell], 4); + + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, &gPlayerAnim_002D28, 0.83f); + + if (magicSpell == 5) { + this->unk_46C = OnePointCutscene_Init(globalCtx, 1100, -101, NULL, MAIN_CAM); + } + else { + func_80835EA4(globalCtx, 10); + } +} + +void func_8083B010(Player* this) { + this->actor.focus.rot.x = this->actor.focus.rot.z = this->unk_6B6 = this->unk_6B8 = this->unk_6BA = this->unk_6BC = + this->unk_6BE = this->unk_6C0 = 0; + + this->actor.focus.rot.y = this->actor.shape.rot.y; +} + +static u8 D_80854528[] = { + GI_LETTER_ZELDA, GI_WEIRD_EGG, GI_CHICKEN, GI_BEAN, GI_POCKET_EGG, GI_POCKET_CUCCO, + GI_COJIRO, GI_ODD_MUSHROOM, GI_ODD_POTION, GI_SAW, GI_SWORD_BROKEN, GI_PRESCRIPTION, + GI_FROG, GI_EYEDROPS, GI_CLAIM_CHECK, GI_MASK_SKULL, GI_MASK_SPOOKY, GI_MASK_KEATON, + GI_MASK_BUNNY, GI_MASK_TRUTH, GI_MASK_GORON, GI_MASK_ZORA, GI_MASK_GERUDO, GI_LETTER_RUTO, + GI_LETTER_RUTO, GI_LETTER_RUTO, GI_LETTER_RUTO, GI_LETTER_RUTO, GI_LETTER_RUTO, +}; + +static LinkAnimationHeader* D_80854548[] = { + &gPlayerAnim_002F88, + &gPlayerAnim_002690, + &gPlayerAnim_003198, +}; + +s32 func_8083B040(Player* this, GlobalContext* globalCtx) { + s32 sp2C; + s32 sp28; + GetItemEntry* giEntry; + Actor* targetActor; + + if ((this->unk_6AD != 0) && + (func_808332B8(this) || (this->actor.bgCheckFlags & 1) || (this->stateFlags1 & PLAYER_STATE1_23))) { + + if (!func_8083ADD4(globalCtx, this)) { + if (this->unk_6AD == 4) { + sp2C = Player_ActionToMagicSpell(this, this->itemActionParam); + if (sp2C >= 0) { + if ((sp2C != 3) || (gSaveContext.respawn[RESPAWN_MODE_TOP].data <= 0)) { + func_8083AF44(globalCtx, this, sp2C); + } + else { + func_80835C58(globalCtx, this, func_8085063C, 1); + this->stateFlags1 |= PLAYER_STATE1_28 | PLAYER_STATE1_29; + func_80832264(globalCtx, this, func_80833338(this)); + func_80835EA4(globalCtx, 4); + } + + func_80832224(this); + return 1; + } + + sp2C = this->itemActionParam - PLAYER_AP_LETTER_ZELDA; + if ((sp2C >= 0) || + (sp28 = Player_ActionToBottle(this, this->itemActionParam) - 1, + ((sp28 >= 0) && (sp28 < 6) && + ((this->itemActionParam > PLAYER_AP_BOTTLE_POE) || + ((this->targetActor != NULL) && + (((this->itemActionParam == PLAYER_AP_BOTTLE_POE) && (this->exchangeItemId == EXCH_ITEM_POE)) || + (this->exchangeItemId == EXCH_ITEM_BLUE_FIRE))))))) { + + if ((globalCtx->actorCtx.titleCtx.delayTimer == 0) && (globalCtx->actorCtx.titleCtx.alpha == 0)) { + func_80835DE4(globalCtx, this, func_8084F104, 0); + + if (sp2C >= 0) { + giEntry = &sGetItemTable[D_80854528[sp2C] - 1]; + func_8083AE40(this, giEntry->objectId); + } + + this->stateFlags1 |= PLAYER_STATE1_6 | PLAYER_STATE1_28 | PLAYER_STATE1_29; + + if (sp2C >= 0) { + sp2C = sp2C + 1; + } + else { + sp2C = sp28 + 0x18; + } + + targetActor = this->targetActor; + + if ((targetActor != NULL) && + ((this->exchangeItemId == sp2C) || (this->exchangeItemId == EXCH_ITEM_BLUE_FIRE) || + ((this->exchangeItemId == EXCH_ITEM_POE) && + (this->itemActionParam == PLAYER_AP_BOTTLE_BIG_POE)) || + ((this->exchangeItemId == EXCH_ITEM_BEAN) && + (this->itemActionParam == PLAYER_AP_BOTTLE_BUG))) && + ((this->exchangeItemId != EXCH_ITEM_BEAN) || (this->itemActionParam == PLAYER_AP_BEAN))) { + if (this->exchangeItemId == EXCH_ITEM_BEAN) { + Inventory_ChangeAmmo(ITEM_BEAN, -1); + func_80835DE4(globalCtx, this, func_8084279C, 0); + this->stateFlags1 |= PLAYER_STATE1_29; + this->unk_850 = 0x50; + this->unk_84F = -1; + } + targetActor->flags |= ACTOR_FLAG_8; + this->unk_664 = this->targetActor; + } + else if (sp2C == EXCH_ITEM_LETTER_RUTO) { + this->unk_84F = 1; + this->actor.textId = 0x4005; + func_80835EA4(globalCtx, 1); + } + else { + this->unk_84F = 2; + this->actor.textId = 0xCF; + func_80835EA4(globalCtx, 4); + } + + this->actor.flags |= ACTOR_FLAG_8; + this->exchangeItemId = sp2C; + + if (this->unk_84F < 0) { + func_80832B0C(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_32][this->modelAnimType]); + } + else { + func_80832264(globalCtx, this, D_80854548[this->unk_84F]); + } + + func_80832224(this); + } + return 1; + } + + sp2C = Player_ActionToBottle(this, this->itemActionParam); + if (sp2C >= 0) { + if (sp2C == 0xC) { + func_80835DE4(globalCtx, this, func_8084EED8, 0); + func_808322D0(globalCtx, this, &gPlayerAnim_002650); + func_80835EA4(globalCtx, 3); + } + else if ((sp2C > 0) && (sp2C < 4)) { + func_80835DE4(globalCtx, this, func_8084EFC0, 0); + func_808322D0(globalCtx, this, &gPlayerAnim_002688); + func_80835EA4(globalCtx, (sp2C == 1) ? 1 : 5); + } + else { + func_80835DE4(globalCtx, this, func_8084EAC0, 0); + func_80832B78(globalCtx, this, &gPlayerAnim_002668); + func_80835EA4(globalCtx, 2); + } + } + else { + func_80835DE4(globalCtx, this, func_8084E3C4, 0); + func_808322D0(globalCtx, this, &gPlayerAnim_0030A0); + this->stateFlags2 |= PLAYER_STATE2_27; + func_80835EA4(globalCtx, (this->unk_6A8 != NULL) ? 0x5B : 0x5A); + if (this->unk_6A8 != NULL) { + this->stateFlags2 |= PLAYER_STATE2_25; + Camera_SetParam(Gameplay_GetCamera(globalCtx, 0), 8, this->unk_6A8); + } + } + } + else if (func_8083AD4C(globalCtx, this)) { + if (!(this->stateFlags1 & PLAYER_STATE1_23)) { + func_80835C58(globalCtx, this, func_8084B1D8, 1); + this->unk_850 = 13; + func_8083B010(this); + } + this->stateFlags1 |= PLAYER_STATE1_20; + func_80078884(NA_SE_SY_CAMERA_ZOOM_UP); + func_80832210(this); + return 1; + } + else { + this->unk_6AD = 0; + func_80078884(NA_SE_SY_ERROR); + return 0; + } + + this->stateFlags1 |= PLAYER_STATE1_28 | PLAYER_STATE1_29; + } + + func_80832224(this); + return 1; + } + + return 0; +} + +s32 func_8083B644(Player* this, GlobalContext* globalCtx) { + Actor* sp34 = this->targetActor; + Actor* sp30 = this->unk_664; + Actor* sp2C = NULL; + s32 sp28 = 0; + s32 sp24; + + sp24 = (sp30 != NULL) && (CHECK_FLAG_ALL(sp30->flags, ACTOR_FLAG_0 | ACTOR_FLAG_18) || (sp30->naviEnemyId != 0xFF)); + + if (sp24 || (this->naviTextId != 0)) { + sp28 = (this->naviTextId < 0) && ((ABS(this->naviTextId) & 0xFF00) != 0x200); + if (sp28 || !sp24) { + sp2C = this->naviActor; + if (sp28) { + sp30 = NULL; + sp34 = NULL; + } + } + else { + sp2C = sp30; + } + } + + if ((sp34 != NULL) || (sp2C != NULL)) { + if ((sp30 == NULL) || (sp30 == sp34) || (sp30 == sp2C)) { + if (!(this->stateFlags1 & PLAYER_STATE1_11) || + ((this->heldActor != NULL) && (sp28 || (sp34 == this->heldActor) || (sp2C == this->heldActor) || + ((sp34 != NULL) && (sp34->flags & ACTOR_FLAG_16))))) { + if ((this->actor.bgCheckFlags & 1) || (this->stateFlags1 & PLAYER_STATE1_23) || + (func_808332B8(this) && !(this->stateFlags2 & PLAYER_STATE2_10))) { + + if (sp34 != NULL) { + this->stateFlags2 |= PLAYER_STATE2_1; + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) || (sp34->flags & ACTOR_FLAG_16)) { + sp2C = NULL; + } + else if (sp2C == NULL) { + return 0; + } + } + + if (sp2C != NULL) { + if (!sp28) { + this->stateFlags2 |= PLAYER_STATE2_21; + } + + if (!CHECK_BTN_ALL(sControlInput->press.button, BTN_CUP) && !sp28) { + return 0; + } + + sp34 = sp2C; + this->targetActor = NULL; + + if (sp28 || !sp24) { + if (this->naviTextId >= 0) { + sp2C->textId = this->naviTextId; + } + else { + sp2C->textId = -this->naviTextId; + } + } + else { + if (sp2C->naviEnemyId != 0xFF) { + sp2C->textId = sp2C->naviEnemyId + 0x600; + } + } + } + + this->currentMask = D_80858AA4; + func_80853148(globalCtx, sp34); + return 1; + } + } + } + } + + return 0; +} + +s32 func_8083B8F4(Player* this, GlobalContext* globalCtx) { + if (!(this->stateFlags1 & (PLAYER_STATE1_11 | PLAYER_STATE1_23)) && + Camera_CheckValidMode(Gameplay_GetCamera(globalCtx, 0), 6)) { + if ((this->actor.bgCheckFlags & 1) || + (func_808332B8(this) && (this->actor.yDistToWater < this->ageProperties->unk_2C))) { + this->unk_6AD = 1; + return 1; + } + } + + return 0; +} + +s32 func_8083B998(Player* this, GlobalContext* globalCtx) { + if (this->unk_6AD != 0) { + func_8083B040(this, globalCtx); + return 1; + } + + if ((this->unk_664 != NULL) && + (CHECK_FLAG_ALL(this->unk_664->flags, ACTOR_FLAG_0 | ACTOR_FLAG_18) || (this->unk_664->naviEnemyId != 0xFF))) { + this->stateFlags2 |= PLAYER_STATE2_21; + } + else if ((this->naviTextId == 0) && !func_8008E9C4(this) && CHECK_BTN_ALL(sControlInput->press.button, BTN_CUP) && + (YREG(15) != 0x10) && (YREG(15) != 0x20) && !func_8083B8F4(this, globalCtx)) { + func_80078884(NA_SE_SY_ERROR); + } + + return 0; +} + +void func_8083BA90(GlobalContext* globalCtx, Player* this, s32 arg2, f32 xzVelocity, f32 yVelocity) { + func_80837948(globalCtx, this, arg2); + func_80835C58(globalCtx, this, func_80844AF4, 0); + + this->stateFlags3 |= PLAYER_STATE3_1; + + this->currentYaw = this->actor.shape.rot.y; + this->linearVelocity = xzVelocity; + this->actor.velocity.y = yVelocity; + + this->actor.bgCheckFlags &= ~1; + this->hoverBootsTimer = 0; + + func_80832854(this); + func_80832698(this, NA_SE_VO_LI_SWORD_L); +} + +s32 func_8083BB20(Player* this) { + if (!(this->stateFlags1 & PLAYER_STATE1_22) && (Player_GetSwordHeld(this) != 0)) { + if (D_80853614 || + ((this->actor.category != ACTORCAT_PLAYER) && CHECK_BTN_ALL(sControlInput->press.button, BTN_B))) { + return 1; + } + } + + return 0; +} + +s32 func_8083BBA0(Player* this, GlobalContext* globalCtx) { + if (func_8083BB20(this) && (D_808535E4 != 7)) { + func_8083BA90(globalCtx, this, 17, 3.0f, 4.5f); + return 1; + } + + return 0; +} + +void func_8083BC04(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80844708, 0); + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_16][this->modelAnimType], + 1.25f * D_808535E8); +} + +s32 func_8083BC7C(Player* this, GlobalContext* globalCtx) { + if ((this->unk_84B[this->unk_846] == 0) && (D_808535E4 != 7)) { + func_8083BC04(this, globalCtx); + return 1; + } + + return 0; +} + +void func_8083BCD0(Player* this, GlobalContext* globalCtx, s32 arg2) { + func_80838940(this, D_80853D4C[arg2][0], !(arg2 & 1) ? 5.8f : 3.5f, globalCtx, NA_SE_VO_LI_SWORD_N); + + if (arg2) {} + + this->unk_850 = 1; + this->unk_84F = arg2; + + this->currentYaw = this->actor.shape.rot.y + (arg2 << 0xE); + this->linearVelocity = !(arg2 & 1) ? 6.0f : 8.5f; + + this->stateFlags2 |= PLAYER_STATE2_19; + + func_8002F7DC(&this->actor, ((arg2 << 0xE) == 0x8000) ? NA_SE_PL_ROLL : NA_SE_PL_SKIP); +} + +s32 func_8083BDBC(Player* this, GlobalContext* globalCtx) { + s32 sp2C; + + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && (globalCtx->roomCtx.curRoom.unk_03 != 2) && + (D_808535E4 != 7) && + (SurfaceType_GetSlope(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId) != 1)) { + sp2C = this->unk_84B[this->unk_846]; + + if (sp2C <= 0) { + if (func_80833BCC(this)) { + if (this->actor.category != ACTORCAT_PLAYER) { + if (sp2C < 0) { + func_808389E8(this, &gPlayerAnim_002FE0, REG(69) / 100.0f, globalCtx); + } + else { + func_8083BC04(this, globalCtx); + } + } + else { + if (Player_GetSwordHeld(this) && func_808365C8(this)) { + func_8083BA90(globalCtx, this, 17, 5.0f, 5.0f); + } + else { + func_8083BC04(this, globalCtx); + } + } + return 1; + } + } + else { + func_8083BCD0(this, globalCtx, sp2C); + return 1; + } + } + + return 0; +} + +void func_8083BF50(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + f32 sp30; + + sp30 = this->unk_868 - 3.0f; + if (sp30 < 0.0f) { + sp30 += 29.0f; + } + + if (sp30 < 14.0f) { + anim = D_80853914[PLAYER_ANIMGROUP_18][this->modelAnimType]; + sp30 = 11.0f - sp30; + if (sp30 < 0.0f) { + sp30 = 1.375f * -sp30; + } + sp30 /= 11.0f; + } + else { + anim = D_80853914[PLAYER_ANIMGROUP_19][this->modelAnimType]; + sp30 = 26.0f - sp30; + if (sp30 < 0.0f) { + sp30 = 2 * -sp30; + } + sp30 /= 12.0f; + } + + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, 0.0f, Animation_GetLastFrame(anim), ANIMMODE_ONCE, + 4.0f * sp30); + this->currentYaw = this->actor.shape.rot.y; +} + +void func_8083C0B8(Player* this, GlobalContext* globalCtx) { + func_80839FFC(this, globalCtx); + func_8083BF50(this, globalCtx); +} + +void func_8083C0E8(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80840BC8, 1); + func_80832264(globalCtx, this, func_80833338(this)); + this->currentYaw = this->actor.shape.rot.y; +} + +void func_8083C148(Player* this, GlobalContext* globalCtx) { + if (!(this->stateFlags3 & PLAYER_STATE3_7)) { + func_8083B010(this); + if (this->stateFlags1 & PLAYER_STATE1_27) { + func_80838F18(globalCtx, this); + } + else { + func_80839F90(this, globalCtx); + } + if (this->unk_6AD < 4) { + this->unk_6AD = 0; + } + } + + this->stateFlags1 &= ~(PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_20); +} + +s32 func_8083C1DC(Player* this, GlobalContext* globalCtx) { + if (!func_80833B54(this) && (D_808535E0 == 0) && !(this->stateFlags1 & PLAYER_STATE1_23) && + CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { + if (func_8083BC7C(this, globalCtx)) { + return 1; + } + if ((this->unk_837 == 0) && (this->heldItemActionParam >= PLAYER_AP_SWORD_MASTER)) { + func_80835F44(globalCtx, this, ITEM_NONE); + } + else { + this->stateFlags2 ^= PLAYER_STATE2_20; + } + } + + return 0; +} + +s32 func_8083C2B0(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + f32 frame; + + if ((globalCtx->shootingGalleryStatus == 0) && (this->currentShield != PLAYER_SHIELD_NONE) && + CHECK_BTN_ALL(sControlInput->cur.button, BTN_R) && + (Player_IsChildWithHylianShield(this) || (!func_80833B2C(this) && (this->unk_664 == NULL)))) { + + func_80832318(this); + func_808323B4(globalCtx, this); + + if (func_80835C58(globalCtx, this, func_80843188, 0)) { + this->stateFlags1 |= PLAYER_STATE1_22; + + if (!Player_IsChildWithHylianShield(this)) { + Player_SetModelsForHoldingShield(this); + anim = D_80853914[PLAYER_ANIMGROUP_20][this->modelAnimType]; + } + else { + anim = &gPlayerAnim_002400; + } + + if (anim != this->skelAnime.animation) { + if (func_8008E9C4(this)) { + this->unk_86C = 1.0f; + } + else { + this->unk_86C = 0.0f; + func_80833C3C(this); + } + this->unk_6BC = this->unk_6BE = this->unk_6C0 = 0; + } + + frame = Animation_GetLastFrame(anim); + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, frame, frame, ANIMMODE_ONCE, 0.0f); + + if (Player_IsChildWithHylianShield(this)) { + func_80832F54(globalCtx, this, 4); + } + + func_8002F7DC(&this->actor, NA_SE_IT_SHIELD_POSTURE); + } + + return 1; + } + + return 0; +} + +s32 func_8083C484(Player* this, f32* arg1, s16* arg2) { + s16 yaw = this->currentYaw - *arg2; + + if (ABS(yaw) > 0x6000) { + if (func_8083721C(this)) { + *arg1 = 0.0f; + *arg2 = this->currentYaw; + } + else { + return 1; + } + } + + return 0; +} + +void func_8083C50C(Player* this) { + if ((this->unk_844 > 0) && !CHECK_BTN_ALL(sControlInput->cur.button, BTN_B)) { + this->unk_844 = -this->unk_844; + } +} + +s32 func_8083C544(Player* this, GlobalContext* globalCtx) { + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_B)) { + if (!(this->stateFlags1 & PLAYER_STATE1_22) && (Player_GetSwordHeld(this) != 0) && (this->unk_844 == 1) && + (this->heldItemActionParam != PLAYER_AP_STICK)) { + if ((this->heldItemActionParam != PLAYER_AP_SWORD_BGS) || (gSaveContext.swordHealth > 0.0f)) { + func_808377DC(globalCtx, this); + return 1; + } + } + } + else { + func_8083C50C(this); + } + + return 0; +} + +s32 func_8083C61C(GlobalContext* globalCtx, Player* this) { + if ((globalCtx->roomCtx.curRoom.unk_03 != 2) && (this->actor.bgCheckFlags & 1) && (AMMO(ITEM_NUT) != 0)) { + func_80835C58(globalCtx, this, func_8084E604, 0); + func_80832264(globalCtx, this, &gPlayerAnim_003048); + this->unk_6AD = 0; + return 1; + } + + return 0; +} + +static struct_80854554 D_80854554[] = { + { &gPlayerAnim_002648, &gPlayerAnim_002640, 2, 3 }, + { &gPlayerAnim_002680, &gPlayerAnim_002678, 5, 3 }, +}; + +s32 func_8083C6B8(GlobalContext* globalCtx, Player* this) { + Vec3f sp24; + + if (D_80853614) { + if (Player_GetBottleHeld(this) >= 0) { + func_80835C58(globalCtx, this, func_8084ECA4, 0); + + if (this->actor.yDistToWater > 12.0f) { + this->unk_850 = 1; + } + + func_808322D0(globalCtx, this, D_80854554[this->unk_850].unk_00); + + func_8002F7DC(&this->actor, NA_SE_IT_SWORD_SWING); + func_80832698(this, NA_SE_VO_LI_AUTO_JUMP); + return 1; + } + + if (this->heldItemActionParam == PLAYER_AP_FISHING_POLE) { + sp24 = this->actor.world.pos; + sp24.y += 50.0f; + + if (!(this->actor.bgCheckFlags & 1) || (this->actor.world.pos.z > 1300.0f) || + BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &sp24, 20.0f)) { + func_80078884(NA_SE_SY_ERROR); + return 0; + } + + func_80835C58(globalCtx, this, func_80850C68, 0); + this->unk_860 = 1; + func_80832210(this); + func_80832264(globalCtx, this, &gPlayerAnim_002C30); + return 1; + } + else { + return 0; + } + } + + return 0; +} + +void func_8083C858(Player* this, GlobalContext* globalCtx) { + PlayerFunc674 func; + + if (func_80833BCC(this)) { + func = func_8084227C; + } + else { + func = func_80842180; + } + + func_80835C58(globalCtx, this, func, 1); + func_80832BE8(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_2][this->modelAnimType]); + + this->unk_89C = 0; + this->unk_864 = this->unk_868 = 0.0f; +} + +void func_8083C8DC(Player* this, GlobalContext* globalCtx, s16 arg2) { + this->actor.shape.rot.y = this->currentYaw = arg2; + func_8083C858(this, globalCtx); +} + +s32 func_8083C910(GlobalContext* globalCtx, Player* this, f32 arg2) { + WaterBox* sp2C; + f32 sp28; + + sp28 = this->actor.world.pos.y; + if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, this->actor.world.pos.x, this->actor.world.pos.z, &sp28, + &sp2C) != 0) { + sp28 -= this->actor.world.pos.y; + if (this->ageProperties->unk_24 <= sp28) { + func_80835C58(globalCtx, this, func_8084D7C4, 0); + func_80832C6C(globalCtx, this, &gPlayerAnim_0032F0); + this->stateFlags1 |= PLAYER_STATE1_27 | PLAYER_STATE1_29; + this->unk_850 = 20; + this->linearVelocity = 2.0f; + Player_SetBootData(globalCtx, this); + return 0; + } + } + + func_80838E70(globalCtx, this, arg2, this->actor.shape.rot.y); + this->stateFlags1 |= PLAYER_STATE1_29; + return 1; +} + +void func_8083CA20(GlobalContext* globalCtx, Player* this) { + if (func_8083C910(globalCtx, this, 180.0f)) { + this->unk_850 = -20; + } +} + +void func_8083CA54(GlobalContext* globalCtx, Player* this) { + this->linearVelocity = 2.0f; + gSaveContext.entranceSpeed = 2.0f; + if (func_8083C910(globalCtx, this, 120.0f)) { + this->unk_850 = -15; + } +} + +void func_8083CA9C(GlobalContext* globalCtx, Player* this) { + if (gSaveContext.entranceSpeed < 0.1f) { + gSaveContext.entranceSpeed = 0.1f; + } + + this->linearVelocity = gSaveContext.entranceSpeed; + + if (func_8083C910(globalCtx, this, 800.0f)) { + this->unk_850 = -80 / this->linearVelocity; + if (this->unk_850 < -20) { + this->unk_850 = -20; + } + } +} + +void func_8083CB2C(Player* this, s16 yaw, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_808414F8, 1); + LinkAnimation_CopyJointToMorph(globalCtx, &this->skelAnime); + this->unk_864 = this->unk_868 = 0.0f; + this->currentYaw = yaw; +} + +void func_8083CB94(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80840DE4, 1); + func_80832BE8(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_1][this->modelAnimType]); +} + +void func_8083CBF0(Player* this, s16 yaw, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_808423EC, 1); + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_0024F8, 2.2f, 0.0f, + Animation_GetLastFrame(&gPlayerAnim_0024F8), ANIMMODE_ONCE, -6.0f); + this->linearVelocity = 8.0f; + this->currentYaw = yaw; +} + +void func_8083CC9C(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084193C, 1); + func_80832BE8(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_25][this->modelAnimType]); + this->unk_868 = 0.0f; +} + +void func_8083CD00(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084251C, 1); + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, &gPlayerAnim_0024E8, 2.0f); +} + +void func_8083CD54(GlobalContext* globalCtx, Player* this, s16 yaw) { + this->currentYaw = yaw; + func_80835C58(globalCtx, this, func_80841BA8, 1); + this->unk_87E = 1200; + this->unk_87E *= D_808535E8; + LinkAnimation_Change(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_26][this->modelAnimType], 1.0f, 0.0f, + 0.0f, ANIMMODE_LOOP, -6.0f); +} + +void func_8083CE0C(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + + func_80835C58(globalCtx, this, func_80840BC8, 1); + + if (this->unk_870 < 0.5f) { + anim = D_80853914[PLAYER_ANIMGROUP_28][this->modelAnimType]; + } + else { + anim = D_80853914[PLAYER_ANIMGROUP_27][this->modelAnimType]; + } + func_80832264(globalCtx, this, anim); + + this->currentYaw = this->actor.shape.rot.y; +} + +void func_8083CEAC(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80840450, 1); + func_80832B0C(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_7][this->modelAnimType]); + this->unk_850 = 1; +} + +void func_8083CF10(Player* this, GlobalContext* globalCtx) { + if (this->linearVelocity != 0.0f) { + func_8083C858(this, globalCtx); + } + else { + func_8083CE0C(this, globalCtx); + } +} + +void func_8083CF5C(Player* this, GlobalContext* globalCtx) { + if (this->linearVelocity != 0.0f) { + func_8083C858(this, globalCtx); + } + else { + func_80839F90(this, globalCtx); + } +} + +s32 func_8083CFA8(GlobalContext* globalCtx, Player* this, f32 arg2, s32 splashScale) { + f32 sp3C = fabsf(arg2); + WaterBox* sp38; + f32 sp34; + Vec3f splashPos; + s32 splashType; + + if (sp3C > 2.0f) { + splashPos.x = this->bodyPartsPos[PLAYER_BODYPART_WAIST].x; + splashPos.z = this->bodyPartsPos[PLAYER_BODYPART_WAIST].z; + sp34 = this->actor.world.pos.y; + if (WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, splashPos.x, splashPos.z, &sp34, &sp38)) { + if ((sp34 - this->actor.world.pos.y) < 100.0f) { + splashType = (sp3C <= 10.0f) ? 0 : 1; + splashPos.y = sp34; + EffectSsGSplash_Spawn(globalCtx, &splashPos, NULL, NULL, splashType, splashScale); + return 1; + } + } + } + + return 0; +} + +void func_8083D0A8(GlobalContext* globalCtx, Player* this, f32 arg2) { + this->stateFlags1 |= PLAYER_STATE1_18; + this->stateFlags1 &= ~PLAYER_STATE1_27; + + func_80832340(globalCtx, this); + if (func_8083CFA8(globalCtx, this, arg2, 500)) { + func_8002F7DC(&this->actor, NA_SE_EV_JUMP_OUT_WATER); + } + + Player_SetBootData(globalCtx, this); +} + +s32 func_8083D12C(GlobalContext* globalCtx, Player* this, Input* arg2) { + if (!(this->stateFlags1 & PLAYER_STATE1_10) && !(this->stateFlags2 & PLAYER_STATE2_10)) { + if ((arg2 == NULL) || (CHECK_BTN_ALL(arg2->press.button, BTN_A) && (ABS(this->unk_6C2) < 12000) && + (this->currentBoots != PLAYER_BOOTS_IRON))) { + + func_80835C58(globalCtx, this, func_8084DC48, 0); + func_80832264(globalCtx, this, &gPlayerAnim_003308); + + this->unk_6C2 = 0; + this->stateFlags2 |= PLAYER_STATE2_10; + this->actor.velocity.y = 0.0f; + + if (arg2 != NULL) { + this->stateFlags2 |= PLAYER_STATE2_11; + func_8002F7DC(&this->actor, NA_SE_PL_DIVE_BUBBLE); + } + + return 1; + } + } + + if ((this->stateFlags1 & PLAYER_STATE1_10) || (this->stateFlags2 & PLAYER_STATE2_10)) { + if (this->actor.velocity.y > 0.0f) { + if (this->actor.yDistToWater < this->ageProperties->unk_30) { + + this->stateFlags2 &= ~PLAYER_STATE2_10; + + if (arg2 != NULL) { + func_80835C58(globalCtx, this, func_8084E1EC, 1); + + if (this->stateFlags1 & PLAYER_STATE1_10) { + this->stateFlags1 |= PLAYER_STATE1_10 | PLAYER_STATE1_11 | PLAYER_STATE1_29; + } + + this->unk_850 = 2; + } + + func_80832340(globalCtx, this); + func_80832B0C(globalCtx, this, + (this->stateFlags1 & PLAYER_STATE1_11) ? &gPlayerAnim_003318 : &gPlayerAnim_003300); + + if (func_8083CFA8(globalCtx, this, this->actor.velocity.y, 500)) { + func_8002F7DC(&this->actor, NA_SE_PL_FACE_UP); + } + + return 1; + } + } + } + + return 0; +} + +void func_8083D330(GlobalContext* globalCtx, Player* this) { + func_80832284(globalCtx, this, &gPlayerAnim_0032F0); + this->unk_6C2 = 16000; + this->unk_850 = 1; +} + +void func_8083D36C(GlobalContext* globalCtx, Player* this) { + if ((this->currentBoots != PLAYER_BOOTS_IRON) || !(this->actor.bgCheckFlags & 1)) { + func_80832564(globalCtx, this); + + if ((this->currentBoots != PLAYER_BOOTS_IRON) && (this->stateFlags2 & PLAYER_STATE2_10)) { + this->stateFlags2 &= ~PLAYER_STATE2_10; + func_8083D12C(globalCtx, this, 0); + this->unk_84F = 1; + } + else if (func_80844A44 == this->func_674) { + func_80835C58(globalCtx, this, func_8084DC48, 0); + func_8083D330(globalCtx, this); + } + else { + func_80835C58(globalCtx, this, func_8084D610, 1); + func_80832B0C(globalCtx, this, (this->actor.bgCheckFlags & 1) ? &gPlayerAnim_003330 : &gPlayerAnim_0032E0); + } + } + + if (!(this->stateFlags1 & PLAYER_STATE1_27) || (this->actor.yDistToWater < this->ageProperties->unk_2C)) { + if (func_8083CFA8(globalCtx, this, this->actor.velocity.y, 500)) { + func_8002F7DC(&this->actor, NA_SE_EV_DIVE_INTO_WATER); + + if (this->fallDistance > 800.0f) { + func_80832698(this, NA_SE_VO_LI_CLIMB_END); + } + } + } + + this->stateFlags1 |= PLAYER_STATE1_27; + this->stateFlags2 |= PLAYER_STATE2_10; + this->stateFlags1 &= ~(PLAYER_STATE1_18 | PLAYER_STATE1_19); + this->unk_854 = 0.0f; + + Player_SetBootData(globalCtx, this); +} + +void func_8083D53C(GlobalContext* globalCtx, Player* this) { + if (this->actor.yDistToWater < this->ageProperties->unk_2C) { + Audio_SetBaseFilter(0); + this->unk_840 = 0; + } + else { + Audio_SetBaseFilter(0x20); + if (this->unk_840 < 300) { + this->unk_840++; + } + } + + if ((func_80845668 != this->func_674) && (func_8084BDFC != this->func_674)) { + if (this->ageProperties->unk_2C < this->actor.yDistToWater) { + if (!(this->stateFlags1 & PLAYER_STATE1_27) || + (!((this->currentBoots == PLAYER_BOOTS_IRON) && (this->actor.bgCheckFlags & 1)) && + (func_8084E30C != this->func_674) && (func_8084E368 != this->func_674) && + (func_8084D610 != this->func_674) && (func_8084D84C != this->func_674) && + (func_8084DAB4 != this->func_674) && (func_8084DC48 != this->func_674) && + (func_8084E1EC != this->func_674) && (func_8084D7C4 != this->func_674))) { + func_8083D36C(globalCtx, this); + return; + } + } + else if ((this->stateFlags1 & PLAYER_STATE1_27) && (this->actor.yDistToWater < this->ageProperties->unk_24)) { + if ((this->skelAnime.moveFlags == 0) && (this->currentBoots != PLAYER_BOOTS_IRON)) { + func_8083CD54(globalCtx, this, this->actor.shape.rot.y); + } + func_8083D0A8(globalCtx, this, this->actor.velocity.y); + } + } +} + +void func_8083D6EC(GlobalContext* globalCtx, Player* this) { + Vec3f ripplePos; + f32 temp1; + f32 temp2; + f32 temp3; + f32 temp4; + + this->actor.minVelocityY = -20.0f; + this->actor.gravity = REG(68) / 100.0f; + + if (func_8083816C(D_808535E4)) { + temp1 = fabsf(this->linearVelocity) * 20.0f; + temp3 = 0.0f; + + if (D_808535E4 == 4) { + if (this->unk_6C4 > 1300.0f) { + temp2 = this->unk_6C4; + } + else { + temp2 = 1300.0f; + } + if (this->currentBoots == PLAYER_BOOTS_HOVER) { + temp1 += temp1; + } + else if (this->currentBoots == PLAYER_BOOTS_IRON) { + temp1 *= 0.3f; + } + } + else { + temp2 = 20000.0f; + if (this->currentBoots != PLAYER_BOOTS_HOVER) { + temp1 += temp1; + } + else if ((D_808535E4 == 7) || (this->currentBoots == PLAYER_BOOTS_IRON)) { + temp1 = 0; + } + } + + if (this->currentBoots != PLAYER_BOOTS_HOVER) { + temp3 = (temp2 - this->unk_6C4) * 0.02f; + temp3 = CLAMP(temp3, 0.0f, 300.0f); + if (this->currentBoots == PLAYER_BOOTS_IRON) { + temp3 += temp3; + } + } + + this->unk_6C4 += temp3 - temp1; + this->unk_6C4 = CLAMP(this->unk_6C4, 0.0f, temp2); + + this->actor.gravity -= this->unk_6C4 * 0.004f; + } + else { + this->unk_6C4 = 0.0f; + } + + if (this->actor.bgCheckFlags & 0x20) { + if (this->actor.yDistToWater < 50.0f) { + temp4 = fabsf(this->bodyPartsPos[PLAYER_BODYPART_WAIST].x - this->unk_A88.x) + + fabsf(this->bodyPartsPos[PLAYER_BODYPART_WAIST].y - this->unk_A88.y) + + fabsf(this->bodyPartsPos[PLAYER_BODYPART_WAIST].z - this->unk_A88.z); + if (temp4 > 4.0f) { + temp4 = 4.0f; + } + this->unk_854 += temp4; + + if (this->unk_854 > 15.0f) { + this->unk_854 = 0.0f; + + ripplePos.x = (Rand_ZeroOne() * 10.0f) + this->actor.world.pos.x; + ripplePos.y = this->actor.world.pos.y + this->actor.yDistToWater; + ripplePos.z = (Rand_ZeroOne() * 10.0f) + this->actor.world.pos.z; + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 100, 500, 0); + + if ((this->linearVelocity > 4.0f) && !func_808332B8(this) && + ((this->actor.world.pos.y + this->actor.yDistToWater) < + this->bodyPartsPos[PLAYER_BODYPART_WAIST].y)) { + func_8083CFA8(globalCtx, this, 20.0f, + (fabsf(this->linearVelocity) * 50.0f) + (this->actor.yDistToWater * 5.0f)); + } + } + } + + if (this->actor.yDistToWater > 40.0f) { + s32 numBubbles = 0; + s32 i; + + if ((this->actor.velocity.y > -1.0f) || (this->actor.bgCheckFlags & 1)) { + if (Rand_ZeroOne() < 0.2f) { + numBubbles = 1; + } + } + else { + numBubbles = this->actor.velocity.y * -2.0f; + } + + for (i = 0; i < numBubbles; i++) { + EffectSsBubble_Spawn(globalCtx, &this->actor.world.pos, 20.0f, 10.0f, 20.0f, 0.13f); + } + } + } +} + +s32 func_8083DB98(Player* this, s32 arg1) { + Actor* unk_664 = this->unk_664; + Vec3f sp30; + s16 sp2E; + s16 sp2C; + + sp30.x = this->actor.world.pos.x; + sp30.y = this->bodyPartsPos[PLAYER_BODYPART_HEAD].y + 3.0f; + sp30.z = this->actor.world.pos.z; + sp2E = Math_Vec3f_Pitch(&sp30, &unk_664->focus.pos); + sp2C = Math_Vec3f_Yaw(&sp30, &unk_664->focus.pos); + Math_SmoothStepToS(&this->actor.focus.rot.y, sp2C, 4, 10000, 0); + Math_SmoothStepToS(&this->actor.focus.rot.x, sp2E, 4, 10000, 0); + this->unk_6AE |= 2; + + return func_80836AB8(this, arg1); +} + +static Vec3f D_8085456C = { 0.0f, 100.0f, 40.0f }; + +void func_8083DC54(Player* this, GlobalContext* globalCtx) { + s16 sp46; + s16 temp2; + f32 temp1; + Vec3f sp34; + + if (this->unk_664 != NULL) { + if (func_8002DD78(this) || func_808334B4(this)) { + func_8083DB98(this, 1); + } + else { + func_8083DB98(this, 0); + } + return; + } + + if (D_808535E4 == 11) { + Math_SmoothStepToS(&this->actor.focus.rot.x, -20000, 10, 4000, 800); + } + else { + sp46 = 0; + temp1 = func_8083973C(globalCtx, this, &D_8085456C, &sp34); + if (temp1 > BGCHECK_Y_MIN) { + temp2 = Math_Atan2S(40.0f, this->actor.world.pos.y - temp1); + sp46 = CLAMP(temp2, -4000, 4000); + } + this->actor.focus.rot.y = this->actor.shape.rot.y; + Math_SmoothStepToS(&this->actor.focus.rot.x, sp46, 14, 4000, 30); + } + + func_80836AB8(this, func_8002DD78(this) || func_808334B4(this)); +} + +void func_8083DDC8(Player* this, GlobalContext* globalCtx) { + s16 temp1; + s16 temp2; + + if (!func_8002DD78(this) && !func_808334B4(this) && (this->linearVelocity > 5.0f)) { + temp1 = this->linearVelocity * 200.0f; + temp2 = (s16)(this->currentYaw - this->actor.shape.rot.y) * this->linearVelocity * 0.1f; + temp1 = CLAMP(temp1, -4000, 4000); + temp2 = CLAMP(-temp2, -4000, 4000); + Math_ScaledStepToS(&this->unk_6BC, temp1, 900); + this->unk_6B6 = -(f32)this->unk_6BC * 0.5f; + Math_ScaledStepToS(&this->unk_6BA, temp2, 300); + Math_ScaledStepToS(&this->unk_6C0, temp2, 200); + this->unk_6AE |= 0x168; + } + else { + func_8083DC54(this, globalCtx); + } +} + +void func_8083DF68(Player* this, f32 arg1, s16 arg2) { + Math_AsymStepToF(&this->linearVelocity, arg1, REG(19) / 100.0f, 1.5f); + Math_ScaledStepToS(&this->currentYaw, arg2, REG(27)); +} + +void func_8083DFE0(Player* this, f32* arg1, s16* arg2) { + s16 yawDiff = this->currentYaw - *arg2; + + if (this->swordState == 0) { + this->linearVelocity = CLAMP(this->linearVelocity, -(R_RUN_SPEED_LIMIT / 100.0f), (R_RUN_SPEED_LIMIT / 100.0f)); + } + + if (ABS(yawDiff) > 0x6000) { + if (Math_StepToF(&this->linearVelocity, 0.0f, 1.0f)) { + this->currentYaw = *arg2; + } + } + else { + Math_AsymStepToF(&this->linearVelocity, *arg1, 0.05f, 0.1f); + Math_ScaledStepToS(&this->currentYaw, *arg2, 200); + } +} + +static struct_80854578 D_80854578[] = { + { &gPlayerAnim_003398, 35.17f, 6.6099997f }, + { &gPlayerAnim_0033A8, -34.16f, 7.91f }, +}; + +s32 func_8083E0FC(Player* this, GlobalContext* globalCtx) { + EnHorse* rideActor = (EnHorse*)this->rideActor; + f32 unk_04; + f32 unk_08; + f32 sp38; + f32 sp34; + s32 temp; + + if ((rideActor != NULL) && CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { + sp38 = Math_CosS(rideActor->actor.shape.rot.y); + sp34 = Math_SinS(rideActor->actor.shape.rot.y); + + func_80836898(globalCtx, this, func_8083A360); + + this->stateFlags1 |= PLAYER_STATE1_23; + this->actor.bgCheckFlags &= ~0x20; + + if (this->mountSide < 0) { + temp = 0; + } + else { + temp = 1; + } + + unk_04 = D_80854578[temp].unk_04; + unk_08 = D_80854578[temp].unk_08; + this->actor.world.pos.x = + rideActor->actor.world.pos.x + rideActor->riderPos.x + ((unk_04 * sp38) + (unk_08 * sp34)); + this->actor.world.pos.z = + rideActor->actor.world.pos.z + rideActor->riderPos.z + ((unk_08 * sp38) - (unk_04 * sp34)); + + this->unk_878 = rideActor->actor.world.pos.y - this->actor.world.pos.y; + this->currentYaw = this->actor.shape.rot.y = rideActor->actor.shape.rot.y; + + Actor_MountHorse(globalCtx, this, &rideActor->actor); + func_80832264(globalCtx, this, D_80854578[temp].anim); + func_80832F54(globalCtx, this, 0x9B); + this->actor.parent = this->rideActor; + func_80832224(this); + func_800304B0(globalCtx); + return 1; + } + + return 0; +} + +void func_8083E298(CollisionPoly* arg0, Vec3f* arg1, s16* arg2) { + arg1->x = COLPOLY_GET_NORMAL(arg0->normal.x); + arg1->y = COLPOLY_GET_NORMAL(arg0->normal.y); + arg1->z = COLPOLY_GET_NORMAL(arg0->normal.z); + + *arg2 = Math_Atan2S(arg1->z, arg1->x); +} + +static LinkAnimationHeader* D_80854590[] = { + &gPlayerAnim_002EE0, + &gPlayerAnim_0031D0, +}; + +s32 func_8083E318(GlobalContext* globalCtx, Player* this, CollisionPoly* arg2) { + s32 pad; + s16 sp4A; + Vec3f sp3C; + s16 sp3A; + f32 temp1; + f32 temp2; + s16 temp3; + + if (!Player_InBlockingCsMode(globalCtx, this) && (func_8084F390 != this->func_674) && + (SurfaceType_GetSlope(&globalCtx->colCtx, arg2, this->actor.floorBgId) == 1)) { + sp4A = Math_Atan2S(this->actor.velocity.z, this->actor.velocity.x); + func_8083E298(arg2, &sp3C, &sp3A); + temp3 = sp3A - sp4A; + + if (ABS(temp3) > 16000) { + temp1 = (1.0f - sp3C.y) * 40.0f; + temp2 = (temp1 * temp1) * 0.015f; + if (temp2 < 1.2f) { + temp2 = 1.2f; + } + this->windDirection = sp3A; + Math_StepToF(&this->windSpeed, temp1, temp2); + } + else { + func_80835C58(globalCtx, this, func_8084F390, 0); + func_80832564(globalCtx, this); + if (D_80853610 >= 0) { + this->unk_84F = 1; + } + func_80832BE8(globalCtx, this, D_80854590[this->unk_84F]); + this->linearVelocity = sqrtf(SQ(this->actor.velocity.x) + SQ(this->actor.velocity.z)); + this->currentYaw = sp4A; + return 1; + } + } + + return 0; +} + +// unknown data (unused) +static s32 D_80854598[] = { + 0xFFDB0871, 0xF8310000, 0x00940470, 0xF3980000, 0xFFB504A9, 0x0C9F0000, 0x08010402, +}; + +void func_8083E4C4(GlobalContext* globalCtx, Player* this, GetItemEntry* giEntry) { + s32 sp1C = giEntry->field & 0x1F; + + if (!(giEntry->field & 0x80)) { + Item_DropCollectible(globalCtx, &this->actor.world.pos, sp1C | 0x8000); + if ((sp1C != 4) && (sp1C != 8) && (sp1C != 9) && (sp1C != 0xA) && (sp1C != 0) && (sp1C != 1) && (sp1C != 2) && + (sp1C != 0x14) && (sp1C != 0x13)) { + Item_Give(globalCtx, giEntry->itemId); + } + } + else { + Item_Give(globalCtx, giEntry->itemId); + } + + func_80078884((this->getItemId < 0) ? NA_SE_SY_GET_BOXITEM : NA_SE_SY_GET_ITEM); +} + +s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) { + Actor* interactedActor; + + if (iREG(67) || (((interactedActor = this->interactRangeActor) != NULL) && + func_8002D53C(globalCtx, &globalCtx->actorCtx.titleCtx))) { + if (iREG(67) || (this->getItemId > GI_NONE)) { + if (iREG(67)) { + this->getItemId = iREG(68); + } + + if (this->getItemId < GI_MAX) { + GetItemEntry* giEntry = &sGetItemTable[this->getItemId - 1]; + + if ((interactedActor != &this->actor) && !iREG(67)) { + interactedActor->parent = &this->actor; + } + + iREG(67) = false; + + if ((Item_CheckObtainability(giEntry->itemId) == ITEM_NONE) || (globalCtx->sceneNum == SCENE_BOWLING)) { + func_808323B4(globalCtx, this); + func_8083AE40(this, giEntry->objectId); + + if (!(this->stateFlags2 & PLAYER_STATE2_10) || (this->currentBoots == PLAYER_BOOTS_IRON)) { + func_80836898(globalCtx, this, func_8083A434); + func_808322D0(globalCtx, this, &gPlayerAnim_002788); + func_80835EA4(globalCtx, 9); + } + + this->stateFlags1 |= PLAYER_STATE1_10 | PLAYER_STATE1_11 | PLAYER_STATE1_29; + func_80832224(this); + return 1; + } + + func_8083E4C4(globalCtx, this, giEntry); + this->getItemId = GI_NONE; + } + } + else if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && !(this->stateFlags1 & PLAYER_STATE1_11) && + !(this->stateFlags2 & PLAYER_STATE2_10)) { + if (this->getItemId != GI_NONE) { + GetItemEntry* giEntry = &sGetItemTable[-this->getItemId - 1]; + EnBox* chest = (EnBox*)interactedActor; + + if (giEntry->itemId != ITEM_NONE) { + if (((Item_CheckObtainability(giEntry->itemId) == ITEM_NONE) && (giEntry->field & 0x40)) || + ((Item_CheckObtainability(giEntry->itemId) != ITEM_NONE) && (giEntry->field & 0x20))) { + this->getItemId = -GI_RUPEE_BLUE; + giEntry = &sGetItemTable[GI_RUPEE_BLUE - 1]; + } + } + + func_80836898(globalCtx, this, func_8083A434); + this->stateFlags1 |= PLAYER_STATE1_10 | PLAYER_STATE1_11 | PLAYER_STATE1_29; + func_8083AE40(this, giEntry->objectId); + this->actor.world.pos.x = + chest->dyna.actor.world.pos.x - (Math_SinS(chest->dyna.actor.shape.rot.y) * 29.4343f); + this->actor.world.pos.z = + chest->dyna.actor.world.pos.z - (Math_CosS(chest->dyna.actor.shape.rot.y) * 29.4343f); + this->currentYaw = this->actor.shape.rot.y = chest->dyna.actor.shape.rot.y; + func_80832224(this); + + if ((giEntry->itemId != ITEM_NONE) && (giEntry->gi >= 0) && + (Item_CheckObtainability(giEntry->itemId) == ITEM_NONE)) { + func_808322D0(globalCtx, this, this->ageProperties->unk_98); + func_80832F54(globalCtx, this, 0x28F); + chest->unk_1F4 = 1; + Camera_ChangeSetting(Gameplay_GetCamera(globalCtx, 0), CAM_SET_SLOW_CHEST_CS); + } + else { + func_80832264(globalCtx, this, &gPlayerAnim_002DF8); + chest->unk_1F4 = -1; + } + + return 1; + } + + if ((this->heldActor == NULL) || Player_HoldsHookshot(this)) { + if ((interactedActor->id == ACTOR_BG_TOKI_SWD) && LINK_IS_ADULT) { + s32 sp24 = this->itemActionParam; + + this->itemActionParam = PLAYER_AP_NONE; + this->modelAnimType = PLAYER_ANIMTYPE_0; + this->heldItemActionParam = this->itemActionParam; + func_80836898(globalCtx, this, func_8083A0F4); + + if (sp24 == PLAYER_AP_SWORD_MASTER) { + this->nextModelGroup = Player_ActionToModelGroup(this, PLAYER_AP_LAST_USED); + func_8083399C(globalCtx, this, PLAYER_AP_LAST_USED); + } + else { + func_80835F44(globalCtx, this, ITEM_LAST_USED); + } + } + else { + s32 strength = Player_GetStrength(); + + if ((interactedActor->id == ACTOR_EN_ISHI) && ((interactedActor->params & 0xF) == 1) && + (strength < PLAYER_STR_SILVER_G)) { + return 0; + } + + func_80836898(globalCtx, this, func_8083A0F4); + } + + func_80832224(this); + this->stateFlags1 |= PLAYER_STATE1_11; + return 1; + } + } + } + + return 0; +} + +void func_8083EA94(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80846578, 1); + func_80832264(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_29][this->modelAnimType]); +} + +s32 func_8083EAF0(Player* this, Actor* actor) { + if ((actor != NULL) && !(actor->flags & ACTOR_FLAG_23) && + ((this->linearVelocity < 1.1f) || (actor->id == ACTOR_EN_BOM_CHU))) { + return 0; + } + + return 1; +} + +s32 func_8083EB44(Player* this, GlobalContext* globalCtx) { + if ((this->stateFlags1 & PLAYER_STATE1_11) && (this->heldActor != NULL) && + CHECK_BTN_ANY(sControlInput->press.button, BTN_A | BTN_B | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN)) { + if (!func_80835644(globalCtx, this, this->heldActor)) { + if (!func_8083EAF0(this, this->heldActor)) { + func_80835C58(globalCtx, this, func_808464B0, 1); + func_80832264(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_30][this->modelAnimType]); + } + else { + func_8083EA94(this, globalCtx); + } + } + return 1; + } + + return 0; +} + +s32 func_8083EC18(Player* this, GlobalContext* globalCtx, u32 arg2) { + if (this->wallHeight >= 79.0f) { + if (!(this->stateFlags1 & PLAYER_STATE1_27) || (this->currentBoots == PLAYER_BOOTS_IRON) || + (this->actor.yDistToWater < this->ageProperties->unk_2C)) { + s32 sp8C = (arg2 & 8) ? 2 : 0; + + if ((sp8C != 0) || (arg2 & 2) || + func_80041E4C(&globalCtx->colCtx, this->actor.wallPoly, this->actor.wallBgId)) { + f32 phi_f20; + CollisionPoly* sp84 = this->actor.wallPoly; + f32 sp80; + f32 sp7C; + f32 phi_f12; + f32 phi_f14; + + phi_f20 = phi_f12 = 0.0f; + + if (sp8C != 0) { + sp80 = this->actor.world.pos.x; + sp7C = this->actor.world.pos.z; + } + else { + Vec3f sp50[3]; + s32 i; + f32 sp48; + Vec3f* sp44 = &sp50[0]; + s32 pad; + + CollisionPoly_GetVerticesByBgId(sp84, this->actor.wallBgId, &globalCtx->colCtx, sp50); + + sp80 = phi_f12 = sp44->x; + sp7C = phi_f14 = sp44->z; + phi_f20 = sp44->y; + for (i = 1; i < 3; i++) { + sp44++; + if (sp80 > sp44->x) { + sp80 = sp44->x; + } + else if (phi_f12 < sp44->x) { + phi_f12 = sp44->x; + } + + if (sp7C > sp44->z) { + sp7C = sp44->z; + } + else if (phi_f14 < sp44->z) { + phi_f14 = sp44->z; + } + + if (phi_f20 > sp44->y) { + phi_f20 = sp44->y; + } + } + + sp80 = (sp80 + phi_f12) * 0.5f; + sp7C = (sp7C + phi_f14) * 0.5f; + + phi_f12 = ((this->actor.world.pos.x - sp80) * COLPOLY_GET_NORMAL(sp84->normal.z)) - + ((this->actor.world.pos.z - sp7C) * COLPOLY_GET_NORMAL(sp84->normal.x)); + sp48 = this->actor.world.pos.y - phi_f20; + + phi_f20 = ((f32)(s32)((sp48 / 15.000000223517418) + 0.5) * 15.000000223517418) - sp48; + phi_f12 = fabsf(phi_f12); + } + + if (phi_f12 < 8.0f) { + f32 sp3C = COLPOLY_GET_NORMAL(sp84->normal.x); + f32 sp38 = COLPOLY_GET_NORMAL(sp84->normal.z); + f32 sp34 = this->wallDistance; + LinkAnimationHeader* sp30; + + func_80836898(globalCtx, this, func_8083A3B0); + this->stateFlags1 |= PLAYER_STATE1_21; + this->stateFlags1 &= ~PLAYER_STATE1_27; + + if ((sp8C != 0) || (arg2 & 2)) { + if ((this->unk_84F = sp8C) != 0) { + if (this->actor.bgCheckFlags & 1) { + sp30 = &gPlayerAnim_002D80; + } + else { + sp30 = &gPlayerAnim_002D68; + } + sp34 = (this->ageProperties->unk_38 - 1.0f) - sp34; + } + else { + sp30 = this->ageProperties->unk_A4; + sp34 = sp34 - 1.0f; + } + this->unk_850 = -2; + this->actor.world.pos.y += phi_f20; + this->actor.shape.rot.y = this->currentYaw = this->actor.wallYaw + 0x8000; + } + else { + sp30 = this->ageProperties->unk_A8; + this->unk_850 = -4; + this->actor.shape.rot.y = this->currentYaw = this->actor.wallYaw; + } + + this->actor.world.pos.x = (sp34 * sp3C) + sp80; + this->actor.world.pos.z = (sp34 * sp38) + sp7C; + func_80832224(this); + Math_Vec3f_Copy(&this->actor.prevPos, &this->actor.world.pos); + func_80832264(globalCtx, this, sp30); + func_80832F54(globalCtx, this, 0x9F); + + return 1; + } + } + } + } + + return 0; +} + +void func_8083F070(Player* this, LinkAnimationHeader* anim, GlobalContext* globalCtx) { + func_80835DAC(globalCtx, this, func_8084C5F8, 0); + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, anim, (4.0f / 3.0f)); +} + +s32 func_8083F0C8(Player* this, GlobalContext* globalCtx, u32 arg2) { + CollisionPoly* wallPoly; + Vec3f wallVertices[3]; + f32 tempX; + f32 temp; + f32 tempZ; + f32 maxWallZ; + s32 i; + + if (!LINK_IS_ADULT && !(this->stateFlags1 & PLAYER_STATE1_27) && (arg2 & 0x30)) { + wallPoly = this->actor.wallPoly; + CollisionPoly_GetVerticesByBgId(wallPoly, this->actor.wallBgId, &globalCtx->colCtx, wallVertices); + + // compute min and max x/z of wall vertices + tempX = temp = wallVertices[0].x; + tempZ = maxWallZ = wallVertices[0].z; + for (i = 1; i < 3; i++) { + if (tempX > wallVertices[i].x) { + tempX = wallVertices[i].x; + } + else if (temp < wallVertices[i].x) { + temp = wallVertices[i].x; + } + + if (tempZ > wallVertices[i].z) { + tempZ = wallVertices[i].z; + } + else if (maxWallZ < wallVertices[i].z) { + maxWallZ = wallVertices[i].z; + } + } + + // average min and max x/z of wall vertices + tempX = (tempX + temp) * 0.5f; + tempZ = (tempZ + maxWallZ) * 0.5f; + + temp = ((this->actor.world.pos.x - tempX) * COLPOLY_GET_NORMAL(wallPoly->normal.z)) - + ((this->actor.world.pos.z - tempZ) * COLPOLY_GET_NORMAL(wallPoly->normal.x)); + + if (fabsf(temp) < 8.0f) { + this->stateFlags2 |= PLAYER_STATE2_16; + + if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { + f32 wallPolyNormX = COLPOLY_GET_NORMAL(wallPoly->normal.x); + f32 wallPolyNormZ = COLPOLY_GET_NORMAL(wallPoly->normal.z); + f32 wallDistance = this->wallDistance; + + func_80836898(globalCtx, this, func_8083A40C); + this->stateFlags2 |= PLAYER_STATE2_18; + this->actor.shape.rot.y = this->currentYaw = this->actor.wallYaw + 0x8000; + this->actor.world.pos.x = tempX + (wallDistance * wallPolyNormX); + this->actor.world.pos.z = tempZ + (wallDistance * wallPolyNormZ); + func_80832224(this); + this->actor.prevPos = this->actor.world.pos; + func_80832264(globalCtx, this, &gPlayerAnim_002708); + func_80832F54(globalCtx, this, 0x9D); + + return 1; + } + } + } + + return 0; +} + +s32 func_8083F360(GlobalContext* globalCtx, Player* this, f32 arg1, f32 arg2, f32 arg3, f32 arg4) { + CollisionPoly* wallPoly; + s32 sp78; + Vec3f sp6C; + Vec3f sp60; + Vec3f sp54; + f32 yawCos; + f32 yawSin; + s32 temp; + f32 temp1; + f32 temp2; + + yawCos = Math_CosS(this->actor.shape.rot.y); + yawSin = Math_SinS(this->actor.shape.rot.y); + + sp6C.x = this->actor.world.pos.x + (arg4 * yawSin); + sp6C.z = this->actor.world.pos.z + (arg4 * yawCos); + sp60.x = this->actor.world.pos.x + (arg3 * yawSin); + sp60.z = this->actor.world.pos.z + (arg3 * yawCos); + sp60.y = sp6C.y = this->actor.world.pos.y + arg1; + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp6C, &sp60, &sp54, &this->actor.wallPoly, true, false, false, + true, &sp78)) { + wallPoly = this->actor.wallPoly; + + this->actor.bgCheckFlags |= 0x200; + this->actor.wallBgId = sp78; + + D_808535F0 = func_80041DB8(&globalCtx->colCtx, wallPoly, sp78); + + temp1 = COLPOLY_GET_NORMAL(wallPoly->normal.x); + temp2 = COLPOLY_GET_NORMAL(wallPoly->normal.z); + temp = Math_Atan2S(-temp2, -temp1); + Math_ScaledStepToS(&this->actor.shape.rot.y, temp, 800); + + this->currentYaw = this->actor.shape.rot.y; + this->actor.world.pos.x = sp54.x - (Math_SinS(this->actor.shape.rot.y) * arg2); + this->actor.world.pos.z = sp54.z - (Math_CosS(this->actor.shape.rot.y) * arg2); + + return 1; + } + + this->actor.bgCheckFlags &= ~0x200; + + return 0; +} + +s32 func_8083F524(GlobalContext* globalCtx, Player* this) { + return func_8083F360(globalCtx, this, 26.0f, this->ageProperties->unk_38 + 5.0f, 30.0f, 0.0f); +} + +s32 func_8083F570(Player* this, GlobalContext* globalCtx) { + s16 temp; + + if ((this->linearVelocity != 0.0f) && (this->actor.bgCheckFlags & 8) && (D_808535F0 & 0x30)) { + + temp = this->actor.shape.rot.y - this->actor.wallYaw; + if (this->linearVelocity < 0.0f) { + temp += 0x8000; + } + + if (ABS(temp) > 0x4000) { + func_80835C58(globalCtx, this, func_8084C81C, 0); + + if (this->linearVelocity > 0.0f) { + this->actor.shape.rot.y = this->actor.wallYaw + 0x8000; + func_80832264(globalCtx, this, &gPlayerAnim_002700); + func_80832F54(globalCtx, this, 0x9D); + OnePointCutscene_Init(globalCtx, 9601, 999, NULL, MAIN_CAM); + } + else { + this->actor.shape.rot.y = this->actor.wallYaw; + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_002708, -1.0f, + Animation_GetLastFrame(&gPlayerAnim_002708), 0.0f, ANIMMODE_ONCE, 0.0f); + func_80832F54(globalCtx, this, 0x9D); + OnePointCutscene_Init(globalCtx, 9602, 999, NULL, MAIN_CAM); + } + + this->currentYaw = this->actor.shape.rot.y; + func_80832210(this); + + return 1; + } + } + + return 0; +} + +void func_8083F72C(Player* this, LinkAnimationHeader* anim, GlobalContext* globalCtx) { + if (!func_80836898(globalCtx, this, func_8083A388)) { + func_80835C58(globalCtx, this, func_8084B78C, 0); + } + + func_80832264(globalCtx, this, anim); + func_80832224(this); + + this->actor.shape.rot.y = this->currentYaw = this->actor.wallYaw + 0x8000; +} + +s32 func_8083F7BC(Player* this, GlobalContext* globalCtx) { + DynaPolyActor* wallPolyActor; + + if (!(this->stateFlags1 & PLAYER_STATE1_11) && (this->actor.bgCheckFlags & 0x200) && (D_80853608 < 0x3000)) { + + if (((this->linearVelocity > 0.0f) && func_8083EC18(this, globalCtx, D_808535F0)) || + func_8083F0C8(this, globalCtx, D_808535F0)) { + return 1; + } + + if (!func_808332B8(this) && ((this->linearVelocity == 0.0f) || !(this->stateFlags2 & PLAYER_STATE2_2)) && + (D_808535F0 & 0x40) && (this->actor.bgCheckFlags & 1) && (this->wallHeight >= 39.0f)) { + + this->stateFlags2 |= PLAYER_STATE2_0; + + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_A)) { + + if ((this->actor.wallBgId != BGCHECK_SCENE) && + ((wallPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.wallBgId)) != NULL)) { + + if (wallPolyActor->actor.id == ACTOR_BG_HEAVY_BLOCK) { + if (Player_GetStrength() < PLAYER_STR_GOLD_G) { + return 0; + } + + func_80836898(globalCtx, this, func_8083A0F4); + this->stateFlags1 |= PLAYER_STATE1_11; + this->interactRangeActor = &wallPolyActor->actor; + this->getItemId = GI_NONE; + this->currentYaw = this->actor.wallYaw + 0x8000; + func_80832224(this); + + return 1; + } + + this->unk_3C4 = &wallPolyActor->actor; + } + else { + this->unk_3C4 = NULL; + } + + func_8083F72C(this, &gPlayerAnim_0030F8, globalCtx); + + return 1; + } + } + } + + return 0; +} + +s32 func_8083F9D0(GlobalContext* globalCtx, Player* this) { + if ((this->actor.bgCheckFlags & 0x200) && + ((this->stateFlags2 & PLAYER_STATE2_4) || CHECK_BTN_ALL(sControlInput->cur.button, BTN_A))) { + DynaPolyActor* wallPolyActor = NULL; + + if (this->actor.wallBgId != BGCHECK_SCENE) { + wallPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.wallBgId); + } + + if (&wallPolyActor->actor == this->unk_3C4) { + if (this->stateFlags2 & PLAYER_STATE2_4) { + return 1; + } + else { + return 0; + } + } + } + + func_80839FFC(this, globalCtx); + func_80832264(globalCtx, this, &gPlayerAnim_003100); + this->stateFlags2 &= ~PLAYER_STATE2_4; + return 1; +} + +void func_8083FAB8(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084B898, 0); + this->stateFlags2 |= PLAYER_STATE2_4; + func_80832264(globalCtx, this, &gPlayerAnim_0030F0); +} + +void func_8083FB14(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084B9E4, 0); + this->stateFlags2 |= PLAYER_STATE2_4; + func_80832264(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_35][this->modelAnimType]); +} + +void func_8083FB7C(Player* this, GlobalContext* globalCtx) { + this->stateFlags1 &= ~(PLAYER_STATE1_21 | PLAYER_STATE1_27); + func_80837B9C(this, globalCtx); + this->linearVelocity = -0.4f; +} + +s32 func_8083FBC0(Player* this, GlobalContext* globalCtx) { + if (!CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && (this->actor.bgCheckFlags & 0x200) && + ((D_808535F0 & 8) || (D_808535F0 & 2) || + func_80041E4C(&globalCtx->colCtx, this->actor.wallPoly, this->actor.wallBgId))) { + return 0; + } + + func_8083FB7C(this, globalCtx); + func_80832698(this, NA_SE_VO_LI_AUTO_JUMP); + return 1; +} + +s32 func_8083FC68(Player* this, f32 arg1, s16 arg2) { + f32 sp1C = (s16)(arg2 - this->actor.shape.rot.y); + f32 temp; + + if (this->unk_664 != NULL) { + func_8083DB98(this, func_8002DD78(this) || func_808334B4(this)); + } + + temp = fabsf(sp1C) / 32768.0f; + + if (arg1 > (((temp * temp) * 50.0f) + 6.0f)) { + return 1; + } + else if (arg1 > (((1.0f - temp) * 10.0f) + 6.8f)) { + return -1; + } + + return 0; +} + +s32 func_8083FD78(Player* this, f32* arg1, s16* arg2, GlobalContext* globalCtx) { + s16 sp2E = *arg2 - this->targetYaw; + u16 sp2C = ABS(sp2E); + + if ((func_8002DD78(this) || func_808334B4(this)) && (this->unk_664 == NULL)) { + *arg1 *= Math_SinS(sp2C); + + if (*arg1 != 0.0f) { + *arg2 = (((sp2E >= 0) ? 1 : -1) << 0xE) + this->actor.shape.rot.y; + } + else { + *arg2 = this->actor.shape.rot.y; + } + + if (this->unk_664 != NULL) { + func_8083DB98(this, 1); + } + else { + Math_SmoothStepToS(&this->actor.focus.rot.x, sControlInput->rel.stick_y * 240.0f, 14, 4000, 30); + func_80836AB8(this, 1); + } + } + else { + if (this->unk_664 != NULL) { + return func_8083FC68(this, *arg1, *arg2); + } + else { + func_8083DC54(this, globalCtx); + if ((*arg1 != 0.0f) && (sp2C < 6000)) { + return 1; + } + else if (*arg1 > Math_SinS((0x4000 - (sp2C >> 1))) * 200.0f) { + return -1; + } + } + } + + return 0; +} + +s32 func_8083FFB8(Player* this, f32* arg1, s16* arg2) { + s16 temp1 = *arg2 - this->actor.shape.rot.y; + u16 temp2 = ABS(temp1); + f32 temp3 = Math_CosS(temp2); + + *arg1 *= temp3; + + if (*arg1 != 0.0f) { + if (temp3 > 0) { + return 1; + } + else { + return -1; + } + } + + return 0; +} + +s32 func_80840058(Player* this, f32* arg1, s16* arg2, GlobalContext* globalCtx) { + func_8083DC54(this, globalCtx); + + if ((*arg1 != 0.0f) || (ABS(this->unk_87C) > 400)) { + s16 temp1 = *arg2 - Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx)); + u16 temp2 = (ABS(temp1) - 0x2000) & 0xFFFF; + + if ((temp2 < 0x4000) || (this->unk_87C != 0)) { + return -1; + } + else { + return 1; + } + } + + return 0; +} + +void func_80840138(Player* this, f32 arg1, s16 arg2) { + s16 temp = arg2 - this->actor.shape.rot.y; + + if (arg1 > 0.0f) { + if (temp < 0) { + this->unk_874 = 0.0f; + } + else { + this->unk_874 = 1.0f; + } + } + + Math_StepToF(&this->unk_870, this->unk_874, 0.3f); +} + +void func_808401B0(GlobalContext* globalCtx, Player* this) { + LinkAnimation_BlendToJoint(globalCtx, &this->skelAnime, func_808334E4(this), this->unk_868, func_80833528(this), + this->unk_868, this->unk_870, this->blendTable); +} + +s32 func_8084021C(f32 arg0, f32 arg1, f32 arg2, f32 arg3) { + f32 temp; + + if ((arg3 == 0.0f) && (arg1 > 0.0f)) { + arg3 = arg2; + } + + temp = (arg0 + arg1) - arg3; + + if (((temp * arg1) >= 0.0f) && (((temp - arg1) * arg1) < 0.0f)) { + return 1; + } + + return 0; +} + +void func_8084029C(Player* this, f32 arg1) { + f32 updateScale = R_UPDATE_RATE * 0.5f; + + arg1 *= updateScale; + if (arg1 < -7.25) { + arg1 = -7.25; + } + else if (arg1 > 7.25f) { + arg1 = 7.25f; + } + + if (1) {} + + if ((this->currentBoots == PLAYER_BOOTS_HOVER) && !(this->actor.bgCheckFlags & 1) && (this->hoverBootsTimer != 0)) { + func_8002F8F0(&this->actor, NA_SE_PL_HOBBERBOOTS_LV - SFX_FLAG); + } + else if (func_8084021C(this->unk_868, arg1, 29.0f, 10.0f) || func_8084021C(this->unk_868, arg1, 29.0f, 24.0f)) { + func_808327F8(this, this->linearVelocity); + if (this->linearVelocity > 4.0f) { + this->stateFlags2 |= PLAYER_STATE2_3; + } + } + + this->unk_868 += arg1; + + if (this->unk_868 < 0.0f) { + this->unk_868 += 29.0f; + } + else if (this->unk_868 >= 29.0f) { + this->unk_868 -= 29.0f; + } +} + +void func_80840450(Player* this, GlobalContext* globalCtx) { + f32 sp44; + s16 sp42; + s32 temp1; + u32 temp2; + s16 temp3; + s32 temp4; + + if (this->stateFlags3 & PLAYER_STATE3_3) { + if (Player_GetSwordHeld(this)) { + this->stateFlags2 |= PLAYER_STATE2_5 | PLAYER_STATE2_6; + } + else { + this->stateFlags3 &= ~PLAYER_STATE3_3; + } + } + + if (this->unk_850 != 0) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832DBC(this); + func_80832284(globalCtx, this, func_808334E4(this)); + this->unk_850 = 0; + this->stateFlags3 &= ~PLAYER_STATE3_3; + } + func_80833C3C(this); + } + else { + func_808401B0(globalCtx, this); + } + + func_8083721C(this); + + if (!func_80837348(globalCtx, this, D_808543E0, 1)) { + if (!func_80833B54(this) && (!func_80833B2C(this) || (func_80834B5C != this->func_82C))) { + func_8083CF10(this, globalCtx); + return; + } + + func_80837268(this, &sp44, &sp42, 0.0f, globalCtx); + + temp1 = func_8083FC68(this, sp44, sp42); + + if (temp1 > 0) { + func_8083C8DC(this, globalCtx, sp42); + return; + } + + if (temp1 < 0) { + func_8083CBF0(this, sp42, globalCtx); + return; + } + + if (sp44 > 4.0f) { + func_8083CC9C(this, globalCtx); + return; + } + + func_8084029C(this, (this->linearVelocity * 0.3f) + 1.0f); + func_80840138(this, sp44, sp42); + + temp2 = this->unk_868; + if ((temp2 < 6) || ((temp2 - 0xE) < 6)) { + Math_StepToF(&this->linearVelocity, 0.0f, 1.5f); + return; + } + + temp3 = sp42 - this->currentYaw; + temp4 = ABS(temp3); + + if (temp4 > 0x4000) { + if (Math_StepToF(&this->linearVelocity, 0.0f, 1.5f)) { + this->currentYaw = sp42; + } + return; + } + + Math_AsymStepToF(&this->linearVelocity, sp44 * 0.3f, 2.0f, 1.5f); + + if (!(this->stateFlags3 & PLAYER_STATE3_3)) { + Math_ScaledStepToS(&this->currentYaw, sp42, temp4 * 0.1f); + } + } +} + +void func_808407CC(Player* this, GlobalContext* globalCtx) { + f32 sp3C; + s16 sp3A; + s32 temp1; + s16 temp2; + s32 temp3; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832DBC(this); + func_80832264(globalCtx, this, func_80833338(this)); + } + + func_8083721C(this); + + if (!func_80837348(globalCtx, this, D_808543E8, 1)) { + if (func_80833B54(this)) { + func_8083CEAC(this, globalCtx); + return; + } + + if (!func_80833B2C(this)) { + func_80835DAC(globalCtx, this, func_80840BC8, 1); + this->currentYaw = this->actor.shape.rot.y; + return; + } + + if (func_80834B5C == this->func_82C) { + func_8083CEAC(this, globalCtx); + return; + } + + func_80837268(this, &sp3C, &sp3A, 0.0f, globalCtx); + + temp1 = func_8083FD78(this, &sp3C, &sp3A, globalCtx); + + if (temp1 > 0) { + func_8083C8DC(this, globalCtx, sp3A); + return; + } + + if (temp1 < 0) { + func_8083CB2C(this, sp3A, globalCtx); + return; + } + + if (sp3C > 4.9f) { + func_8083CC9C(this, globalCtx); + func_80833C3C(this); + return; + } + if (sp3C != 0.0f) { + func_8083CB94(this, globalCtx); + return; + } + + temp2 = sp3A - this->actor.shape.rot.y; + temp3 = ABS(temp2); + + if (temp3 > 800) { + func_8083CD54(globalCtx, this, sp3A); + } + } +} + +void func_808409CC(GlobalContext* globalCtx, Player* this) { + LinkAnimationHeader* anim; + LinkAnimationHeader** animPtr; + s32 heathIsCritical; + s32 sp38; + s32 sp34; + + if ((this->unk_664 != NULL) || + (!(heathIsCritical = HealthMeter_IsCritical()) && ((this->unk_6AC = (this->unk_6AC + 1) & 1) != 0))) { + this->stateFlags2 &= ~PLAYER_STATE2_28; + anim = func_80833338(this); + } + else { + this->stateFlags2 |= PLAYER_STATE2_28; + if (this->stateFlags1 & PLAYER_STATE1_11) { + anim = func_80833338(this); + } + else { + sp38 = globalCtx->roomCtx.curRoom.unk_02; + if (heathIsCritical) { + if (this->unk_6AC >= 0) { + sp38 = 7; + this->unk_6AC = -1; + } + else { + sp38 = 8; + } + } + else { + sp34 = Rand_ZeroOne() * 5.0f; + if (sp34 < 4) { + if (((sp34 != 0) && (sp34 != 3)) || ((this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD) && + ((sp34 == 3) || Player_GetSwordHeld(this)))) { + if ((sp34 == 0) && Player_HoldsTwoHandedWeapon(this)) { + sp34 = 4; + } + sp38 = sp34 + 9; + } + } + } + animPtr = &D_80853D7C[sp38][0]; + if (this->modelAnimType != PLAYER_ANIMTYPE_1) { + animPtr = &D_80853D7C[sp38][1]; + } + anim = *animPtr; + } + } + + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, (2.0f / 3.0f) * D_808535E8, 0.0f, + Animation_GetLastFrame(anim), ANIMMODE_ONCE, -6.0f); +} + +void func_80840BC8(Player* this, GlobalContext* globalCtx) { + s32 sp44; + s32 sp40; + f32 sp3C; + s16 sp3A; + s16 temp; + + sp44 = func_80833350(this); + sp40 = LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (sp44 > 0) { + func_808333FC(this, sp44 - 1); + } + + if (sp40 != 0) { + if (this->unk_850 != 0) { + if (DECR(this->unk_850) == 0) { + this->skelAnime.endFrame = this->skelAnime.animLength - 1.0f; + } + this->skelAnime.jointTable[0].y = (this->skelAnime.jointTable[0].y + ((this->unk_850 & 1) * 0x50)) - 0x28; + } + else { + func_80832DBC(this); + func_808409CC(globalCtx, this); + } + } + + func_8083721C(this); + + if (this->unk_850 == 0) { + if (!func_80837348(globalCtx, this, D_80854418, 1)) { + if (func_80833B54(this)) { + func_8083CEAC(this, globalCtx); + return; + } + + if (func_80833B2C(this)) { + func_80839F30(this, globalCtx); + return; + } + + func_80837268(this, &sp3C, &sp3A, 0.018f, globalCtx); + + if (sp3C != 0.0f) { + func_8083C8DC(this, globalCtx, sp3A); + return; + } + + temp = sp3A - this->actor.shape.rot.y; + if (ABS(temp) > 800) { + func_8083CD54(globalCtx, this, sp3A); + return; + } + + Math_ScaledStepToS(&this->actor.shape.rot.y, sp3A, 1200); + this->currentYaw = this->actor.shape.rot.y; + if (func_80833338(this) == this->skelAnime.animation) { + func_8083DC54(this, globalCtx); + } + } + } +} + +void func_80840DE4(Player* this, GlobalContext* globalCtx) { + f32 frames; + f32 coeff; + f32 sp44; + s16 sp42; + s32 temp1; + s16 temp2; + s32 temp3; + s32 direction; + + this->skelAnime.mode = 0; + LinkAnimation_SetUpdateFunction(&this->skelAnime); + + this->skelAnime.animation = func_8083356C(this); + + if (this->skelAnime.animation == &gPlayerAnim_0026E8) { + frames = 24.0f; + coeff = -(MREG(95) / 100.0f); + } + else { + frames = 29.0f; + coeff = MREG(95) / 100.0f; + } + + this->skelAnime.animLength = frames; + this->skelAnime.endFrame = frames - 1.0f; + + if ((s16)(this->currentYaw - this->actor.shape.rot.y) >= 0) { + direction = 1; + } + else { + direction = -1; + } + + this->skelAnime.playSpeed = direction * (this->linearVelocity * coeff); + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (LinkAnimation_OnFrame(&this->skelAnime, 0.0f) || LinkAnimation_OnFrame(&this->skelAnime, frames * 0.5f)) { + func_808327F8(this, this->linearVelocity); + } + + if (!func_80837348(globalCtx, this, D_808543F4, 1)) { + if (func_80833B54(this)) { + func_8083CEAC(this, globalCtx); + return; + } + + if (!func_80833B2C(this)) { + func_80853080(this, globalCtx); + return; + } + + func_80837268(this, &sp44, &sp42, 0.0f, globalCtx); + temp1 = func_8083FD78(this, &sp44, &sp42, globalCtx); + + if (temp1 > 0) { + func_8083C8DC(this, globalCtx, sp42); + return; + } + + if (temp1 < 0) { + func_8083CB2C(this, sp42, globalCtx); + return; + } + + if (sp44 > 4.9f) { + func_8083CC9C(this, globalCtx); + func_80833C3C(this); + return; + } + + if ((sp44 == 0.0f) && (this->linearVelocity == 0.0f)) { + func_80839F30(this, globalCtx); + return; + } + + temp2 = sp42 - this->currentYaw; + temp3 = ABS(temp2); + + if (temp3 > 0x4000) { + if (Math_StepToF(&this->linearVelocity, 0.0f, 1.5f)) { + this->currentYaw = sp42; + } + return; + } + + Math_AsymStepToF(&this->linearVelocity, sp44 * 0.4f, 1.5f, 1.5f); + Math_ScaledStepToS(&this->currentYaw, sp42, temp3 * 0.1f); + } +} + +void func_80841138(Player* this, GlobalContext* globalCtx) { + f32 temp1; + f32 temp2; + + if (this->unk_864 < 1.0f) { + temp1 = R_UPDATE_RATE * 0.5f; + func_8084029C(this, REG(35) / 1000.0f); + LinkAnimation_LoadToJoint(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_31][this->modelAnimType], + this->unk_868); + this->unk_864 += 1 * temp1; + if (this->unk_864 >= 1.0f) { + this->unk_864 = 1.0f; + } + temp1 = this->unk_864; + } + else { + temp2 = this->linearVelocity - (REG(48) / 100.0f); + if (temp2 < 0.0f) { + temp1 = 1.0f; + func_8084029C(this, (REG(35) / 1000.0f) + ((REG(36) / 1000.0f) * this->linearVelocity)); + LinkAnimation_LoadToJoint(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_31][this->modelAnimType], + this->unk_868); + } + else { + temp1 = (REG(37) / 1000.0f) * temp2; + if (temp1 < 1.0f) { + func_8084029C(this, (REG(35) / 1000.0f) + ((REG(36) / 1000.0f) * this->linearVelocity)); + } + else { + temp1 = 1.0f; + func_8084029C(this, 1.2f + ((REG(38) / 1000.0f) * temp2)); + } + LinkAnimation_LoadToMorph(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_31][this->modelAnimType], + this->unk_868); + LinkAnimation_LoadToJoint(globalCtx, &this->skelAnime, &gPlayerAnim_002DD0, + this->unk_868 * (16.0f / 29.0f)); + } + } + + if (temp1 < 1.0f) { + LinkAnimation_InterpJointMorph(globalCtx, &this->skelAnime, 1.0f - temp1); + } +} + +void func_8084140C(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084170C, 1); + func_80832B0C(globalCtx, this, &gPlayerAnim_002DA0); +} + +s32 func_80841458(Player* this, f32* arg1, s16* arg2, GlobalContext* globalCtx) { + if (this->linearVelocity > 6.0f) { + func_8084140C(this, globalCtx); + return 1; + } + + if (*arg1 != 0.0f) { + if (func_8083721C(this)) { + *arg1 = 0.0f; + *arg2 = this->currentYaw; + } + else { + return 1; + } + } + + return 0; +} + +void func_808414F8(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + s32 sp2C; + s16 sp2A; + + func_80841138(this, globalCtx); + + if (!func_80837348(globalCtx, this, D_80854400, 1)) { + if (!func_80833C04(this)) { + func_8083C8DC(this, globalCtx, this->currentYaw); + return; + } + + func_80837268(this, &sp34, &sp32, 0.0f, globalCtx); + sp2C = func_8083FD78(this, &sp34, &sp32, globalCtx); + + if (sp2C >= 0) { + if (!func_80841458(this, &sp34, &sp32, globalCtx)) { + if (sp2C != 0) { + func_8083C858(this, globalCtx); + } + else if (sp34 > 4.9f) { + func_8083CC9C(this, globalCtx); + } + else { + func_8083CB94(this, globalCtx); + } + } + } + else { + sp2A = sp32 - this->currentYaw; + + Math_AsymStepToF(&this->linearVelocity, sp34 * 1.5f, 1.5f, 2.0f); + Math_ScaledStepToS(&this->currentYaw, sp32, sp2A * 0.1f); + + if ((sp34 == 0.0f) && (this->linearVelocity == 0.0f)) { + func_80839F30(this, globalCtx); + } + } + } +} + +void func_808416C0(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_808417FC, 1); + func_80832264(globalCtx, this, &gPlayerAnim_002DA8); +} + +void func_8084170C(Player* this, GlobalContext* globalCtx) { + s32 sp34; + f32 sp30; + s16 sp2E; + + sp34 = LinkAnimation_Update(globalCtx, &this->skelAnime); + func_8083721C(this); + + if (!func_80837348(globalCtx, this, D_80854400, 1)) { + func_80837268(this, &sp30, &sp2E, 0.0f, globalCtx); + + if (this->linearVelocity == 0.0f) { + this->currentYaw = this->actor.shape.rot.y; + + if (func_8083FD78(this, &sp30, &sp2E, globalCtx) > 0) { + func_8083C858(this, globalCtx); + } + else if ((sp30 != 0.0f) || (sp34 != 0)) { + func_808416C0(this, globalCtx); + } + } + } +} + +void func_808417FC(Player* this, GlobalContext* globalCtx) { + s32 sp1C; + + sp1C = LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (!func_80837348(globalCtx, this, D_80854400, 1)) { + if (sp1C != 0) { + func_80839F30(this, globalCtx); + } + } +} + +void func_80841860(GlobalContext* globalCtx, Player* this) { + f32 frame; + // fake match? see func_80833664 + LinkAnimationHeader* sp38 = D_80853914[0][this->modelAnimType + PLAYER_ANIMGROUP_24 * ARRAY_COUNT(D_80853914[0])]; + LinkAnimationHeader* sp34 = D_80853914[0][this->modelAnimType + PLAYER_ANIMGROUP_25 * ARRAY_COUNT(D_80853914[0])]; + + this->skelAnime.animation = sp38; + + func_8084029C(this, (REG(30) / 1000.0f) + ((REG(32) / 1000.0f) * this->linearVelocity)); + + frame = this->unk_868 * (16.0f / 29.0f); + LinkAnimation_BlendToJoint(globalCtx, &this->skelAnime, sp34, frame, sp38, frame, this->unk_870, this->blendTable); +} + +void func_8084193C(Player* this, GlobalContext* globalCtx) { + f32 sp3C; + s16 sp3A; + s32 temp1; + s16 temp2; + s32 temp3; + + func_80841860(globalCtx, this); + + if (!func_80837348(globalCtx, this, D_80854408, 1)) { + if (!func_80833C04(this)) { + func_8083C858(this, globalCtx); + return; + } + + func_80837268(this, &sp3C, &sp3A, 0.0f, globalCtx); + + if (func_80833B2C(this)) { + temp1 = func_8083FD78(this, &sp3C, &sp3A, globalCtx); + } + else { + temp1 = func_8083FC68(this, sp3C, sp3A); + } + + if (temp1 > 0) { + func_8083C858(this, globalCtx); + return; + } + + if (temp1 < 0) { + if (func_80833B2C(this)) { + func_8083CB2C(this, sp3A, globalCtx); + } + else { + func_8083CBF0(this, sp3A, globalCtx); + } + return; + } + + if ((this->linearVelocity < 3.6f) && (sp3C < 4.0f)) { + if (!func_8008E9C4(this) && func_80833B2C(this)) { + func_8083CB94(this, globalCtx); + } + else { + func_80839F90(this, globalCtx); + } + return; + } + + func_80840138(this, sp3C, sp3A); + + temp2 = sp3A - this->currentYaw; + temp3 = ABS(temp2); + + if (temp3 > 0x4000) { + if (Math_StepToF(&this->linearVelocity, 0.0f, 3.0f) != 0) { + this->currentYaw = sp3A; + } + return; + } + + sp3C *= 0.9f; + Math_AsymStepToF(&this->linearVelocity, sp3C, 2.0f, 3.0f); + Math_ScaledStepToS(&this->currentYaw, sp3A, temp3 * 0.1f); + } +} + +void func_80841BA8(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (Player_HoldsTwoHandedWeapon(this)) { + AnimationContext_SetLoadFrame(globalCtx, func_80833338(this), 0, this->skelAnime.limbCount, + this->skelAnime.morphTable); + AnimationContext_SetCopyTrue(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->skelAnime.morphTable, D_80853410); + } + + func_80837268(this, &sp34, &sp32, 0.018f, globalCtx); + + if (!func_80837348(globalCtx, this, D_80854414, 1)) { + if (sp34 != 0.0f) { + this->actor.shape.rot.y = sp32; + func_8083C858(this, globalCtx); + } + else if (Math_ScaledStepToS(&this->actor.shape.rot.y, sp32, this->unk_87E)) { + func_8083C0E8(this, globalCtx); + } + + this->currentYaw = this->actor.shape.rot.y; + } +} + +void func_80841CC4(Player* this, s32 arg1, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + s16 target; + f32 rate; + + if (ABS(D_80853610) < 3640) { + target = 0; + } + else { + target = CLAMP(D_80853610, -10922, 10922); + } + + Math_ScaledStepToS(&this->unk_89C, target, 400); + + if ((this->modelAnimType == PLAYER_ANIMTYPE_3) || ((this->unk_89C == 0) && (this->unk_6C4 <= 0.0f))) { + if (arg1 == 0) { + LinkAnimation_LoadToJoint(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_1][this->modelAnimType], + this->unk_868); + } + else { + LinkAnimation_LoadToMorph(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_1][this->modelAnimType], + this->unk_868); + } + return; + } + + if (this->unk_89C != 0) { + rate = this->unk_89C / 10922.0f; + } + else { + rate = this->unk_6C4 * 0.0006f; + } + + rate *= fabsf(this->linearVelocity) * 0.5f; + + if (rate > 1.0f) { + rate = 1.0f; + } + + if (rate < 0.0f) { + anim = &gPlayerAnim_002E48; + rate = -rate; + } + else { + anim = &gPlayerAnim_002E90; + } + + if (arg1 == 0) { + LinkAnimation_BlendToJoint(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_1][this->modelAnimType], + this->unk_868, anim, this->unk_868, rate, this->blendTable); + } + else { + LinkAnimation_BlendToMorph(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_1][this->modelAnimType], + this->unk_868, anim, this->unk_868, rate, this->blendTable); + } +} + +void func_80841EE4(Player* this, GlobalContext* globalCtx) { + f32 temp1; + f32 temp2; + + if (this->unk_864 < 1.0f) { + temp1 = R_UPDATE_RATE * 0.5f; + + func_8084029C(this, REG(35) / 1000.0f); + LinkAnimation_LoadToJoint(globalCtx, &this->skelAnime, D_80853914[PLAYER_ANIMGROUP_1][this->modelAnimType], + this->unk_868); + + this->unk_864 += 1 * temp1; + if (this->unk_864 >= 1.0f) { + this->unk_864 = 1.0f; + } + + temp1 = this->unk_864; + } + else { + temp2 = this->linearVelocity - (REG(48) / 100.0f); + + if (temp2 < 0.0f) { + temp1 = 1.0f; + func_8084029C(this, (REG(35) / 1000.0f) + ((REG(36) / 1000.0f) * this->linearVelocity)); + + func_80841CC4(this, 0, globalCtx); + } + else { + temp1 = (REG(37) / 1000.0f) * temp2; + if (temp1 < 1.0f) { + func_8084029C(this, (REG(35) / 1000.0f) + ((REG(36) / 1000.0f) * this->linearVelocity)); + } + else { + temp1 = 1.0f; + func_8084029C(this, 1.2f + ((REG(38) / 1000.0f) * temp2)); + } + + func_80841CC4(this, 1, globalCtx); + + LinkAnimation_LoadToJoint(globalCtx, &this->skelAnime, func_80833438(this), + this->unk_868 * (20.0f / 29.0f)); + } + } + + if (temp1 < 1.0f) { + LinkAnimation_InterpJointMorph(globalCtx, &this->skelAnime, 1.0f - temp1); + } +} + +void func_80842180(Player* this, GlobalContext* globalCtx) { + f32 sp2C; + s16 sp2A; + + this->stateFlags2 |= PLAYER_STATE2_5; + func_80841EE4(this, globalCtx); + + if (!func_80837348(globalCtx, this, D_80854424, 1)) { + if (func_80833C04(this)) { + func_8083C858(this, globalCtx); + return; + } + + func_80837268(this, &sp2C, &sp2A, 0.018f, globalCtx); + + if (!func_8083C484(this, &sp2C, &sp2A)) { + func_8083DF68(this, sp2C, sp2A); + func_8083DDC8(this, globalCtx); + + if ((this->linearVelocity == 0.0f) && (sp2C == 0.0f)) { + func_8083C0B8(this, globalCtx); + } + } + } +} + +void func_8084227C(Player* this, GlobalContext* globalCtx) { + f32 sp2C; + s16 sp2A; + + this->stateFlags2 |= PLAYER_STATE2_5; + func_80841EE4(this, globalCtx); + + if (!func_80837348(globalCtx, this, D_80854430, 1)) { + if (!func_80833C04(this)) { + func_8083C858(this, globalCtx); + return; + } + + func_80837268(this, &sp2C, &sp2A, 0.0f, globalCtx); + + if (!func_8083C484(this, &sp2C, &sp2A)) { + if ((func_80833B2C(this) && (sp2C != 0.0f) && (func_8083FD78(this, &sp2C, &sp2A, globalCtx) <= 0)) || + (!func_80833B2C(this) && (func_8083FC68(this, sp2C, sp2A) <= 0))) { + func_80839F90(this, globalCtx); + return; + } + + func_8083DF68(this, sp2C, sp2A); + func_8083DDC8(this, globalCtx); + + if ((this->linearVelocity == 0) && (sp2C == 0)) { + func_80839F90(this, globalCtx); + } + } + } +} + +void func_808423EC(Player* this, GlobalContext* globalCtx) { + s32 sp34; + f32 sp30; + s16 sp2E; + + sp34 = LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (!func_80837348(globalCtx, this, D_80854408, 1)) { + if (!func_80833C04(this)) { + func_8083C858(this, globalCtx); + return; + } + + func_80837268(this, &sp30, &sp2E, 0.0f, globalCtx); + + if ((this->skelAnime.morphWeight == 0.0f) && (this->skelAnime.curFrame > 5.0f)) { + func_8083721C(this); + + if ((this->skelAnime.curFrame > 10.0f) && (func_8083FC68(this, sp30, sp2E) < 0)) { + func_8083CBF0(this, sp2E, globalCtx); + return; + } + + if (sp34 != 0) { + func_8083CD00(this, globalCtx); + } + } + } +} + +void func_8084251C(Player* this, GlobalContext* globalCtx) { + s32 sp34; + f32 sp30; + s16 sp2E; + + sp34 = LinkAnimation_Update(globalCtx, &this->skelAnime); + + func_8083721C(this); + + if (!func_80837348(globalCtx, this, D_80854440, 1)) { + func_80837268(this, &sp30, &sp2E, 0.0f, globalCtx); + + if (this->linearVelocity == 0.0f) { + this->currentYaw = this->actor.shape.rot.y; + + if (func_8083FC68(this, sp30, sp2E) > 0) { + func_8083C858(this, globalCtx); + return; + } + + if ((sp30 != 0.0f) || (sp34 != 0)) { + func_80839F90(this, globalCtx); + } + } + } +} + +void func_8084260C(Vec3f* src, Vec3f* dest, f32 arg2, f32 arg3, f32 arg4) { + dest->x = (Rand_ZeroOne() * arg3) + src->x; + dest->y = (Rand_ZeroOne() * arg4) + (src->y + arg2); + dest->z = (Rand_ZeroOne() * arg3) + src->z; +} + +static Vec3f D_808545B4 = { 0.0f, 0.0f, 0.0f }; +static Vec3f D_808545C0 = { 0.0f, 0.0f, 0.0f }; + +s32 func_8084269C(GlobalContext* globalCtx, Player* this) { + Vec3f sp2C; + + if ((this->unk_89E == 0) || (this->unk_89E == 1)) { + func_8084260C(&this->actor.shape.feetPos[FOOT_LEFT], &sp2C, + this->actor.floorHeight - this->actor.shape.feetPos[FOOT_LEFT].y, 7.0f, 5.0f); + func_800286CC(globalCtx, &sp2C, &D_808545B4, &D_808545C0, 50, 30); + func_8084260C(&this->actor.shape.feetPos[FOOT_RIGHT], &sp2C, + this->actor.floorHeight - this->actor.shape.feetPos[FOOT_RIGHT].y, 7.0f, 5.0f); + func_800286CC(globalCtx, &this->actor.shape.feetPos[FOOT_RIGHT], &D_808545B4, &D_808545C0, 50, 30); + return 1; + } + + return 0; +} + +void func_8084279C(Player* this, GlobalContext* globalCtx) { + func_80832CB0(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_33][this->modelAnimType]); + + if (DECR(this->unk_850) == 0) { + if (!func_8083B040(this, globalCtx)) { + func_8083A098(this, D_80853914[PLAYER_ANIMGROUP_34][this->modelAnimType], globalCtx); + } + + this->actor.flags &= ~ACTOR_FLAG_8; + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + } +} + +s32 func_8084285C(Player* this, f32 arg1, f32 arg2, f32 arg3) { + if ((arg1 <= this->skelAnime.curFrame) && (this->skelAnime.curFrame <= arg3)) { + func_80833A20(this, (arg2 <= this->skelAnime.curFrame) ? 1 : -1); + return 1; + } + + func_80832318(this); + return 0; +} + +s32 func_808428D8(Player* this, GlobalContext* globalCtx) { + if (!Player_IsChildWithHylianShield(this) && Player_GetSwordHeld(this) && D_80853614) { + func_80832264(globalCtx, this, &gPlayerAnim_002EC8); + this->unk_84F = 1; + this->swordAnimation = 0xC; + this->currentYaw = this->actor.shape.rot.y + this->unk_6BE; + return 1; + } + + return 0; +} + +s32 func_80842964(Player* this, GlobalContext* globalCtx) { + return func_8083B040(this, globalCtx) || func_8083B644(this, globalCtx) || func_8083E5A8(this, globalCtx); +} + +void func_808429B4(GlobalContext* globalCtx, s32 speed, s32 y, s32 countdown) { + s32 quakeIdx = Quake_Add(Gameplay_GetCamera(globalCtx, 0), 3); + + Quake_SetSpeed(quakeIdx, speed); + Quake_SetQuakeValues(quakeIdx, y, 0, 0, 0); + Quake_SetCountdown(quakeIdx, countdown); +} + +void func_80842A28(GlobalContext* globalCtx, Player* this) { + func_808429B4(globalCtx, 27767, 7, 20); + globalCtx->actorCtx.unk_02 = 4; + func_8083264C(this, 255, 20, 150, 0); + func_8002F7DC(&this->actor, NA_SE_IT_HAMMER_HIT); +} + +void func_80842A88(GlobalContext* globalCtx, Player* this) { + Inventory_ChangeAmmo(ITEM_STICK, -1); + func_80835F44(globalCtx, this, ITEM_NONE); +} + +s32 func_80842AC4(GlobalContext* globalCtx, Player* this) { + if ((this->heldItemActionParam == PLAYER_AP_STICK) && (this->unk_85C > 0.5f)) { + if (AMMO(ITEM_STICK) != 0) { + EffectSsStick_Spawn(globalCtx, &this->bodyPartsPos[PLAYER_BODYPART_R_HAND], + this->actor.shape.rot.y + 0x8000); + this->unk_85C = 0.5f; + func_80842A88(globalCtx, this); + func_8002F7DC(&this->actor, NA_SE_IT_WOODSTICK_BROKEN); + } + + return 1; + } + + return 0; +} + +s32 func_80842B7C(GlobalContext* globalCtx, Player* this) { + if (this->heldItemActionParam == PLAYER_AP_SWORD_BGS) { + if (!gSaveContext.bgsFlag && (gSaveContext.swordHealth > 0.0f)) { + if ((gSaveContext.swordHealth -= 1.0f) <= 0.0f) { + EffectSsStick_Spawn(globalCtx, &this->bodyPartsPos[PLAYER_BODYPART_R_HAND], + this->actor.shape.rot.y + 0x8000); + func_800849EC(globalCtx); + func_8002F7DC(&this->actor, NA_SE_IT_MAJIN_SWORD_BROKEN); + } + } + + return 1; + } + + return 0; +} + +void func_80842CF0(GlobalContext* globalCtx, Player* this) { + func_80842AC4(globalCtx, this); + func_80842B7C(globalCtx, this); +} + +static LinkAnimationHeader* D_808545CC[] = { + &gPlayerAnim_002B10, + &gPlayerAnim_002B20, + &gPlayerAnim_002B08, + &gPlayerAnim_002B18, +}; + +void func_80842D20(GlobalContext* globalCtx, Player* this) { + s32 pad; + s32 sp28; + + if (func_80843188 != this->func_674) { + func_80832440(globalCtx, this); + func_80835C58(globalCtx, this, func_808505DC, 0); + + if (func_8008E9C4(this)) { + sp28 = 2; + } + else { + sp28 = 0; + } + + func_808322D0(globalCtx, this, D_808545CC[Player_HoldsTwoHandedWeapon(this) + sp28]); + } + + func_8083264C(this, 180, 20, 100, 0); + this->linearVelocity = -18.0f; + func_80842CF0(globalCtx, this); +} + +s32 func_80842DF4(GlobalContext* globalCtx, Player* this) { + f32 phi_f2; + CollisionPoly* sp78; + s32 sp74; + Vec3f sp68; + Vec3f sp5C; + Vec3f sp50; + s32 temp1; + s32 sp48; + + if (this->swordState > 0) { + if (this->swordAnimation < 0x18) { + if (!(this->swordQuads[0].base.atFlags & AT_BOUNCED) && !(this->swordQuads[1].base.atFlags & AT_BOUNCED)) { + if (this->skelAnime.curFrame >= 2.0f) { + + phi_f2 = Math_Vec3f_DistXYZAndStoreDiff(&this->swordInfo[0].tip, &this->swordInfo[0].base, &sp50); + if (phi_f2 != 0.0f) { + phi_f2 = (phi_f2 + 10.0f) / phi_f2; + } + + sp68.x = this->swordInfo[0].tip.x + (sp50.x * phi_f2); + sp68.y = this->swordInfo[0].tip.y + (sp50.y * phi_f2); + sp68.z = this->swordInfo[0].tip.z + (sp50.z * phi_f2); + + if (BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp68, &this->swordInfo[0].tip, &sp5C, &sp78, true, + false, false, true, &sp74) && + !SurfaceType_IsIgnoredByEntities(&globalCtx->colCtx, sp78, sp74) && + (func_80041D4C(&globalCtx->colCtx, sp78, sp74) != 6) && + (func_8002F9EC(globalCtx, &this->actor, sp78, sp74, &sp5C) == 0)) { + + if (this->heldItemActionParam == PLAYER_AP_HAMMER) { + func_80832630(globalCtx); + func_80842A28(globalCtx, this); + func_80842D20(globalCtx, this); + return 1; + } + + if (this->linearVelocity >= 0.0f) { + sp48 = func_80041F10(&globalCtx->colCtx, sp78, sp74); + + if (sp48 == 0xA) { + CollisionCheck_SpawnShieldParticlesWood(globalCtx, &sp5C, &this->actor.projectedPos); + } + else { + CollisionCheck_SpawnShieldParticles(globalCtx, &sp5C); + if (sp48 == 0xB) { + func_8002F7DC(&this->actor, NA_SE_IT_WALL_HIT_SOFT); + } + else { + func_8002F7DC(&this->actor, NA_SE_IT_WALL_HIT_HARD); + } + } + + func_80842CF0(globalCtx, this); + this->linearVelocity = -14.0f; + func_8083264C(this, 180, 20, 100, 0); + } + } + } + } + else { + func_80842D20(globalCtx, this); + func_80832630(globalCtx); + return 1; + } + } + + temp1 = (this->swordQuads[0].base.atFlags & AT_HIT) || (this->swordQuads[1].base.atFlags & AT_HIT); + + if (temp1) { + if (this->swordAnimation < 0x18) { + Actor* at = this->swordQuads[temp1 ? 1 : 0].base.at; + + if ((at != NULL) && (at->id != ACTOR_EN_KANBAN)) { + func_80832630(globalCtx); + } + } + + if ((func_80842AC4(globalCtx, this) == 0) && (this->heldItemActionParam != PLAYER_AP_HAMMER)) { + func_80842B7C(globalCtx, this); + + if (this->actor.colChkInfo.atHitEffect == 1) { + this->actor.colChkInfo.damage = 8; + func_80837C0C(globalCtx, this, 4, 0.0f, 0.0f, this->actor.shape.rot.y, 20); + return 1; + } + } + } + } + + return 0; +} + +void func_80843188(Player* this, GlobalContext* globalCtx) { + f32 sp54; + f32 sp50; + s16 sp4E; + s16 sp4C; + s16 sp4A; + s16 sp48; + s16 sp46; + f32 sp40; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (!Player_IsChildWithHylianShield(this)) { + func_80832284(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_21][this->modelAnimType]); + } + this->unk_850 = 1; + this->unk_84F = 0; + } + + if (!Player_IsChildWithHylianShield(this)) { + this->stateFlags1 |= PLAYER_STATE1_22; + func_80836670(this, globalCtx); + this->stateFlags1 &= ~PLAYER_STATE1_22; + } + + func_8083721C(this); + + if (this->unk_850 != 0) { + sp54 = sControlInput->rel.stick_y * 100; + sp50 = sControlInput->rel.stick_x * -120; + sp4E = this->actor.shape.rot.y - Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx)); + + sp40 = Math_CosS(sp4E); + sp4C = (Math_SinS(sp4E) * sp50) + (sp54 * sp40); + sp40 = Math_CosS(sp4E); + sp4A = (sp50 * sp40) - (Math_SinS(sp4E) * sp54); + + if (sp4C > 3500) { + sp4C = 3500; + } + + sp48 = ABS(sp4C - this->actor.focus.rot.x) * 0.25f; + if (sp48 < 100) { + sp48 = 100; + } + + sp46 = ABS(sp4A - this->unk_6BE) * 0.25f; + if (sp46 < 50) { + sp46 = 50; + } + + Math_ScaledStepToS(&this->actor.focus.rot.x, sp4C, sp48); + this->unk_6BC = this->actor.focus.rot.x; + Math_ScaledStepToS(&this->unk_6BE, sp4A, sp46); + + if (this->unk_84F != 0) { + if (!func_80842DF4(globalCtx, this)) { + if (this->skelAnime.curFrame < 2.0f) { + func_80833A20(this, 1); + } + } + else { + this->unk_850 = 1; + this->unk_84F = 0; + } + } + else if (!func_80842964(this, globalCtx)) { + if (func_8083C2B0(this, globalCtx)) { + func_808428D8(this, globalCtx); + } + else { + this->stateFlags1 &= ~PLAYER_STATE1_22; + func_80832318(this); + + if (Player_IsChildWithHylianShield(this)) { + func_8083A060(this, globalCtx); + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_002400, 1.0f, + Animation_GetLastFrame(&gPlayerAnim_002400), 0.0f, ANIMMODE_ONCE, 0.0f); + func_80832F54(globalCtx, this, 4); + } + else { + if (this->itemActionParam < 0) { + func_8008EC70(this); + } + func_8083A098(this, D_80853914[PLAYER_ANIMGROUP_22][this->modelAnimType], globalCtx); + } + + func_8002F7DC(&this->actor, NA_SE_IT_SHIELD_REMOVE); + return; + } + } + else { + return; + } + } + + this->stateFlags1 |= PLAYER_STATE1_22; + Player_SetModelsForHoldingShield(this); + + this->unk_6AE |= 0xC1; +} + +void func_808435C4(Player* this, GlobalContext* globalCtx) { + s32 temp; + LinkAnimationHeader* anim; + f32 frames; + + func_8083721C(this); + + if (this->unk_84F == 0) { + D_808535E0 = func_80836670(this, globalCtx); + if ((func_80834B5C == this->func_82C) || (func_808374A0(globalCtx, this, &this->skelAnime2, 4.0f) > 0)) { + func_80835C58(globalCtx, this, func_80840450, 1); + } + } + else { + temp = func_808374A0(globalCtx, this, &this->skelAnime, 4.0f); + if ((temp != 0) && ((temp > 0) || LinkAnimation_Update(globalCtx, &this->skelAnime))) { + func_80835C58(globalCtx, this, func_80843188, 1); + this->stateFlags1 |= PLAYER_STATE1_22; + Player_SetModelsForHoldingShield(this); + anim = D_80853914[PLAYER_ANIMGROUP_20][this->modelAnimType]; + frames = Animation_GetLastFrame(anim); + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, 1.0f, frames, frames, ANIMMODE_ONCE, 0.0f); + } + } +} + +void func_8084370C(Player* this, GlobalContext* globalCtx) { + s32 sp1C; + + func_8083721C(this); + + sp1C = func_808374A0(globalCtx, this, &this->skelAnime, 16.0f); + if ((sp1C != 0) && (LinkAnimation_Update(globalCtx, &this->skelAnime) || (sp1C > 0))) { + func_80839F90(this, globalCtx); + } +} + +void func_8084377C(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5 | PLAYER_STATE2_6; + + func_808382BC(this); + + if (!(this->stateFlags1 & PLAYER_STATE1_29) && (this->unk_850 == 0) && (this->unk_8A1 != 0)) { + s16 temp = this->actor.shape.rot.y - this->unk_8A2; + + this->currentYaw = this->actor.shape.rot.y = this->unk_8A2; + this->linearVelocity = this->unk_8A4; + + if (ABS(temp) > 0x4000) { + this->actor.shape.rot.y = this->unk_8A2 + 0x8000; + } + + if (this->actor.velocity.y < 0.0f) { + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + } + } + + if (LinkAnimation_Update(globalCtx, &this->skelAnime) && (this->actor.bgCheckFlags & 1)) { + if (this->unk_850 != 0) { + this->unk_850--; + if (this->unk_850 == 0) { + func_80853080(this, globalCtx); + } + } + else if ((this->stateFlags1 & PLAYER_STATE1_29) || + (!(this->cylinder.base.acFlags & AC_HIT) && (this->unk_8A1 == 0))) { + if (this->stateFlags1 & PLAYER_STATE1_29) { + this->unk_850++; + } + else { + func_80835C58(globalCtx, this, func_80843954, 0); + this->stateFlags1 |= PLAYER_STATE1_26; + } + + func_80832264(globalCtx, this, + (this->currentYaw != this->actor.shape.rot.y) ? &gPlayerAnim_002F60 : &gPlayerAnim_002DB8); + func_80832698(this, NA_SE_VO_LI_FREEZE); + } + } + + if (this->actor.bgCheckFlags & 2) { + func_80832770(this, NA_SE_PL_BOUND); + } +} + +void func_80843954(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5 | PLAYER_STATE2_6; + func_808382BC(this); + + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime) && (this->linearVelocity == 0.0f)) { + if (this->stateFlags1 & PLAYER_STATE1_29) { + this->unk_850++; + } + else { + func_80835C58(globalCtx, this, func_80843A38, 0); + this->stateFlags1 |= PLAYER_STATE1_26; + } + + func_808322D0(globalCtx, this, + (this->currentYaw != this->actor.shape.rot.y) ? &gPlayerAnim_002F68 : &gPlayerAnim_002DC0); + this->currentYaw = this->actor.shape.rot.y; + } +} + +static struct_80832924 D_808545DC[] = { + { 0, 0x4014 }, + { 0, -0x401E }, +}; + +void func_80843A38(Player* this, GlobalContext* globalCtx) { + s32 sp24; + + this->stateFlags2 |= PLAYER_STATE2_5; + func_808382BC(this); + + if (this->stateFlags1 & PLAYER_STATE1_29) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + } + else { + sp24 = func_808374A0(globalCtx, this, &this->skelAnime, 16.0f); + if ((sp24 != 0) && (LinkAnimation_Update(globalCtx, &this->skelAnime) || (sp24 > 0))) { + func_80839F90(this, globalCtx); + } + } + + func_80832924(this, D_808545DC); +} + +static Vec3f D_808545E4 = { 0.0f, 0.0f, 5.0f }; + +void func_80843AE8(GlobalContext* globalCtx, Player* this) { + if (this->unk_850 != 0) { + if (this->unk_850 > 0) { + this->unk_850--; + if (this->unk_850 == 0) { + if (this->stateFlags1 & PLAYER_STATE1_27) { + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_003328, 1.0f, 0.0f, + Animation_GetLastFrame(&gPlayerAnim_003328), ANIMMODE_ONCE, -16.0f); + } + else { + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_002878, 1.0f, 99.0f, + Animation_GetLastFrame(&gPlayerAnim_002878), ANIMMODE_ONCE, 0.0f); + } + gSaveContext.healthAccumulator = 0x140; + this->unk_850 = -1; + } + } + else if (gSaveContext.healthAccumulator == 0) { + this->stateFlags1 &= ~PLAYER_STATE1_7; + if (this->stateFlags1 & PLAYER_STATE1_27) { + func_80838F18(globalCtx, this); + } + else { + func_80853080(this, globalCtx); + } + this->unk_A87 = 20; + func_80837AFC(this, -20); + func_800F47FC(); + } + } + else if (this->unk_84F != 0) { + this->unk_850 = 60; + Player_SpawnFairy(globalCtx, this, &this->actor.world.pos, &D_808545E4, FAIRY_REVIVE_DEATH); + func_8002F7DC(&this->actor, NA_SE_EV_FIATY_HEAL - SFX_FLAG); + OnePointCutscene_Init(globalCtx, 9908, 125, &this->actor, MAIN_CAM); + } + else if (globalCtx->gameOverCtx.state == GAMEOVER_DEATH_WAIT_GROUND) { + globalCtx->gameOverCtx.state = GAMEOVER_DEATH_DELAY_MENU; + } +} + +static struct_80832924 D_808545F0[] = { + { NA_SE_PL_BOUND, 0x103C }, + { 0, 0x408C }, + { 0, 0x40A4 }, + { 0, -0x40AA }, +}; + +void func_80843CEC(Player* this, GlobalContext* globalCtx) { + if (this->currentTunic != PLAYER_TUNIC_GORON) { + if ((globalCtx->roomCtx.curRoom.unk_02 == 3) || (D_808535E4 == 9) || + ((func_80838144(D_808535E4) >= 0) && + !SurfaceType_IsWallDamage(&globalCtx->colCtx, this->actor.floorPoly, this->actor.floorBgId))) { + func_8083821C(this); + } + } + + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->actor.category == ACTORCAT_PLAYER) { + func_80843AE8(globalCtx, this); + } + return; + } + + if (this->skelAnime.animation == &gPlayerAnim_002878) { + func_80832924(this, D_808545F0); + } + else if (this->skelAnime.animation == &gPlayerAnim_002F08) { + if (LinkAnimation_OnFrame(&this->skelAnime, 88.0f)) { + func_80832770(this, NA_SE_PL_BOUND); + } + } +} + +void func_80843E14(Player* this, u16 sfxId) { + func_80832698(this, sfxId); + + if ((this->heldActor != NULL) && (this->heldActor->id == ACTOR_EN_RU1)) { + Audio_PlayActorSound2(this->heldActor, NA_SE_VO_RT_FALL); + } +} + +static FallImpactInfo D_80854600[] = { + { -8, 180, 40, 100, NA_SE_VO_LI_LAND_DAMAGE_S }, + { -16, 255, 140, 150, NA_SE_VO_LI_LAND_DAMAGE_S }, +}; + +s32 func_80843E64(GlobalContext* globalCtx, Player* this) { + s32 sp34; + + if ((D_808535E4 == 6) || (D_808535E4 == 9)) { + sp34 = 0; + } + else { + sp34 = this->fallDistance; + } + + Math_StepToF(&this->linearVelocity, 0.0f, 1.0f); + + this->stateFlags1 &= ~(PLAYER_STATE1_18 | PLAYER_STATE1_19); + + if (sp34 >= 400) { + s32 impactIndex; + FallImpactInfo* impactInfo; + + if (this->fallDistance < 800) { + impactIndex = 0; + } + else { + impactIndex = 1; + } + + impactInfo = &D_80854600[impactIndex]; + + if (Player_InflictDamage(globalCtx, impactInfo->damage)) { + return -1; + } + + func_80837AE0(this, 40); + func_808429B4(globalCtx, 32967, 2, 30); + func_8083264C(this, impactInfo->unk_01, impactInfo->unk_02, impactInfo->unk_03, 0); + func_8002F7DC(&this->actor, NA_SE_PL_BODY_HIT); + func_80832698(this, impactInfo->sfxId); + + return impactIndex + 1; + } + + if (sp34 > 200) { + sp34 *= 2; + + if (sp34 > 255) { + sp34 = 255; + } + + func_8083264C(this, (u8)sp34, (u8)(sp34 * 0.1f), (u8)sp34, 0); + + if (D_808535E4 == 6) { + func_80832698(this, NA_SE_VO_LI_CLIMB_END); + } + } + + func_808328A0(this); + + return 0; +} + +void func_8084409C(GlobalContext* globalCtx, Player* this, f32 speedXZ, f32 velocityY) { + Actor* heldActor = this->heldActor; + + if (!func_80835644(globalCtx, this, heldActor)) { + heldActor->world.rot.y = this->actor.shape.rot.y; + heldActor->speedXZ = speedXZ; + heldActor->velocity.y = velocityY; + func_80834644(globalCtx, this); + func_8002F7DC(&this->actor, NA_SE_PL_THROW); + func_80832698(this, NA_SE_VO_LI_SWORD_N); + } +} + +void func_8084411C(Player* this, GlobalContext* globalCtx) { + f32 sp4C; + s16 sp4A; + + if (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 40) { + this->actor.gravity = 0.0f; + } + else if (func_8008E9C4(this)) { + this->actor.gravity = -1.2f; + } + + func_80837268(this, &sp4C, &sp4A, 0.0f, globalCtx); + + if (!(this->actor.bgCheckFlags & 1)) { + if (this->stateFlags1 & PLAYER_STATE1_11) { + Actor* heldActor = this->heldActor; + + if (!func_80835644(globalCtx, this, heldActor) && (heldActor->id == ACTOR_EN_NIW) && + CHECK_BTN_ANY(sControlInput->press.button, BTN_A | BTN_B | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN)) { + func_8084409C(globalCtx, this, this->linearVelocity + 2.0f, this->actor.velocity.y + 2.0f); + } + } + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (!(this->stateFlags2 & PLAYER_STATE2_19)) { + func_8083DFE0(this, &sp4C, &sp4A); + } + + func_80836670(this, globalCtx); + + if (((this->stateFlags2 & PLAYER_STATE2_19) && (this->unk_84F == 2)) || !func_8083BBA0(this, globalCtx)) { + if (this->actor.velocity.y < 0.0f) { + if (this->unk_850 >= 0) { + if ((this->actor.bgCheckFlags & 8) || (this->unk_850 == 0) || (this->fallDistance > 0)) { + if ((D_80853600 > 800.0f) || (this->stateFlags1 & PLAYER_STATE1_2)) { + func_80843E14(this, NA_SE_VO_LI_FALL_S); + this->stateFlags1 &= ~PLAYER_STATE1_2; + } + + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_003020, 1.0f, 0.0f, 0.0f, + ANIMMODE_ONCE, 8.0f); + this->unk_850 = -1; + } + } + else { + if ((this->unk_850 == -1) && (this->fallDistance > 120.0f) && (D_80853600 > 280.0f)) { + this->unk_850 = -2; + func_80843E14(this, NA_SE_VO_LI_FALL_L); + } + + if ((this->actor.bgCheckFlags & 0x200) && !(this->stateFlags2 & PLAYER_STATE2_19) && + !(this->stateFlags1 & (PLAYER_STATE1_11 | PLAYER_STATE1_27)) && (this->linearVelocity > 0.0f)) { + if ((this->wallHeight >= 150.0f) && (this->unk_84B[this->unk_846] == 0)) { + func_8083EC18(this, globalCtx, D_808535F0); + } + else if ((this->unk_88C >= 2) && (this->wallHeight < 150.0f) && + (((this->actor.world.pos.y - this->actor.floorHeight) + this->wallHeight) > + (70.0f * this->ageProperties->unk_08))) { + AnimationContext_DisableQueue(globalCtx); + if (this->stateFlags1 & PLAYER_STATE1_2) { + func_80832698(this, NA_SE_VO_LI_HOOKSHOT_HANG); + } + else { + func_80832698(this, NA_SE_VO_LI_HANG); + } + this->actor.world.pos.y += this->wallHeight; + func_8083A5C4(globalCtx, this, this->actor.wallPoly, this->wallDistance, + D_80853914[PLAYER_ANIMGROUP_39][this->modelAnimType]); + this->actor.shape.rot.y = this->currentYaw += 0x8000; + this->stateFlags1 |= PLAYER_STATE1_13; + } + } + } + } + } + } + else { + LinkAnimationHeader* anim = D_80853914[PLAYER_ANIMGROUP_14][this->modelAnimType]; + s32 sp3C; + + if (this->stateFlags2 & PLAYER_STATE2_19) { + if (func_8008E9C4(this)) { + anim = D_80853D4C[this->unk_84F][2]; + } + else { + anim = D_80853D4C[this->unk_84F][1]; + } + } + else if (this->skelAnime.animation == &gPlayerAnim_003148) { + anim = &gPlayerAnim_003150; + } + else if (func_8008E9C4(this)) { + anim = &gPlayerAnim_002538; + func_80833C3C(this); + } + else if (this->fallDistance <= 80) { + anim = D_80853914[PLAYER_ANIMGROUP_15][this->modelAnimType]; + } + else if ((this->fallDistance < 800) && (this->unk_84B[this->unk_846] == 0) && + !(this->stateFlags1 & PLAYER_STATE1_11)) { + func_8083BC04(this, globalCtx); + return; + } + + sp3C = func_80843E64(globalCtx, this); + + if (sp3C > 0) { + func_8083A098(this, D_80853914[PLAYER_ANIMGROUP_14][this->modelAnimType], globalCtx); + this->skelAnime.endFrame = 8.0f; + if (sp3C == 1) { + this->unk_850 = 10; + } + else { + this->unk_850 = 20; + } + } + else if (sp3C == 0) { + func_8083A098(this, anim, globalCtx); + } + } +} + +static struct_80832924 D_8085460C[] = { + { NA_SE_VO_LI_SWORD_N, 0x2001 }, + { NA_SE_PL_WALK_GROUND, 0x1806 }, + { NA_SE_PL_ROLL, 0x806 }, + { 0, -0x2812 }, +}; + +void func_80844708(Player* this, GlobalContext* globalCtx) { + Actor* cylinderOc; + s32 temp; + s32 sp44; + DynaPolyActor* wallPolyActor; + s32 pad; + f32 sp38; + s16 sp36; + + this->stateFlags2 |= PLAYER_STATE2_5; + + cylinderOc = NULL; + sp44 = LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (LinkAnimation_OnFrame(&this->skelAnime, 8.0f)) { + func_80837AFC(this, -10); + } + + if (func_80842964(this, globalCtx) == 0) { + if (this->unk_850 != 0) { + Math_StepToF(&this->linearVelocity, 0.0f, 2.0f); + + temp = func_808374A0(globalCtx, this, &this->skelAnime, 5.0f); + if ((temp != 0) && ((temp > 0) || sp44)) { + func_8083A060(this, globalCtx); + } + } + else { + if (this->linearVelocity >= 7.0f) { + if (((this->actor.bgCheckFlags & 0x200) && (D_8085360C < 0x2000)) || + ((this->cylinder.base.ocFlags1 & OC1_HIT) && + (cylinderOc = this->cylinder.base.oc, + ((cylinderOc->id == ACTOR_EN_WOOD02) && + (ABS((s16)(this->actor.world.rot.y - cylinderOc->yawTowardsPlayer)) > 0x6000))))) { + + if (cylinderOc != NULL) { + cylinderOc->home.rot.y = 1; + } + else if (this->actor.wallBgId != BGCHECK_SCENE) { + wallPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.wallBgId); + if ((wallPolyActor != NULL) && (wallPolyActor->actor.id == ACTOR_OBJ_KIBAKO2)) { + wallPolyActor->actor.home.rot.z = 1; + } + } + + func_80832264(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_17][this->modelAnimType]); + this->linearVelocity = -this->linearVelocity; + func_808429B4(globalCtx, 33267, 3, 12); + func_8083264C(this, 255, 20, 150, 0); + func_8002F7DC(&this->actor, NA_SE_PL_BODY_HIT); + func_80832698(this, NA_SE_VO_LI_CLIMB_END); + this->unk_850 = 1; + return; + } + } + + if ((this->skelAnime.curFrame < 15.0f) || !func_80850224(this, globalCtx)) { + if (this->skelAnime.curFrame >= 20.0f) { + func_8083A060(this, globalCtx); + return; + } + + func_80837268(this, &sp38, &sp36, 0.018f, globalCtx); + + sp38 *= 1.5f; + if ((sp38 < 3.0f) || (this->unk_84B[this->unk_846] != 0)) { + sp38 = 3.0f; + } + + func_8083DF68(this, sp38, this->actor.shape.rot.y); + + if (func_8084269C(globalCtx, this)) { + func_8002F8F0(&this->actor, NA_SE_PL_ROLL_DUST - SFX_FLAG); + } + + func_80832924(this, D_8085460C); + } + } + } +} + +void func_80844A44(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, &gPlayerAnim_003160); + } + + Math_StepToF(&this->linearVelocity, 0.0f, 0.05f); + + if (this->actor.bgCheckFlags & 1) { + this->actor.colChkInfo.damage = 0x10; + func_80837C0C(globalCtx, this, 1, 4.0f, 5.0f, this->actor.shape.rot.y, 20); + } +} + +void func_80844AF4(Player* this, GlobalContext* globalCtx) { + f32 sp2C; + s16 sp2A; + + this->stateFlags2 |= PLAYER_STATE2_5; + + this->actor.gravity = -1.2f; + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (!func_80842DF4(globalCtx, this)) { + func_8084285C(this, 6.0f, 7.0f, 99.0f); + + if (!(this->actor.bgCheckFlags & 1)) { + func_80837268(this, &sp2C, &sp2A, 0.0f, globalCtx); + func_8083DFE0(this, &sp2C, &this->currentYaw); + return; + } + + if (func_80843E64(globalCtx, this) >= 0) { + this->swordAnimation += 2; + func_80837948(globalCtx, this, this->swordAnimation); + this->unk_845 = 3; + func_808328A0(this); + } + } +} + +s32 func_80844BE4(Player* this, GlobalContext* globalCtx) { + s32 temp; + + if (func_8083ADD4(globalCtx, this)) { + this->stateFlags2 |= PLAYER_STATE2_17; + } + else { + if (!CHECK_BTN_ALL(sControlInput->cur.button, BTN_B)) { + if ((this->unk_858 >= 0.85f) || func_808375D8(this)) { + temp = D_80854384[Player_HoldsTwoHandedWeapon(this)]; + } + else { + temp = D_80854380[Player_HoldsTwoHandedWeapon(this)]; + } + + func_80837948(globalCtx, this, temp); + func_80837AFC(this, -8); + + this->stateFlags2 |= PLAYER_STATE2_17; + if (this->unk_84B[this->unk_846] == 0) { + this->stateFlags2 |= PLAYER_STATE2_30; + } + } + else { + return 0; + } + } + + return 1; +} + +void func_80844CF8(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80845000, 1); +} + +void func_80844D30(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80845308, 1); +} + +void func_80844D68(Player* this, GlobalContext* globalCtx) { + func_80839FFC(this, globalCtx); + func_80832318(this); + func_80832B0C(globalCtx, this, D_80854368[Player_HoldsTwoHandedWeapon(this)]); + this->currentYaw = this->actor.shape.rot.y; +} + +void func_80844DC8(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80844E68, 1); + this->unk_868 = 0.0f; + func_80832284(globalCtx, this, D_80854360[Player_HoldsTwoHandedWeapon(this)]); + this->unk_850 = 1; +} + +void func_80844E3C(Player* this) { + Math_StepToF(&this->unk_858, 1.0f, 0.02f); +} + +void func_80844E68(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + s32 temp; + + this->stateFlags1 |= PLAYER_STATE1_12; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832DBC(this); + func_808355DC(this); + this->stateFlags1 &= ~PLAYER_STATE1_17; + func_80832284(globalCtx, this, D_80854360[Player_HoldsTwoHandedWeapon(this)]); + this->unk_850 = -1; + } + + func_8083721C(this); + + if (!func_80842964(this, globalCtx) && (this->unk_850 != 0)) { + func_80844E3C(this); + + if (this->unk_850 < 0) { + if (this->unk_858 >= 0.1f) { + this->unk_845 = 0; + this->unk_850 = 1; + } + else if (!CHECK_BTN_ALL(sControlInput->cur.button, BTN_B)) { + func_80844D68(this, globalCtx); + } + } + else if (!func_80844BE4(this, globalCtx)) { + func_80837268(this, &sp34, &sp32, 0.0f, globalCtx); + + temp = func_80840058(this, &sp34, &sp32, globalCtx); + if (temp > 0) { + func_80844CF8(this, globalCtx); + } + else if (temp < 0) { + func_80844D30(this, globalCtx); + } + } + } +} + +void func_80845000(Player* this, GlobalContext* globalCtx) { + s16 temp1; + s32 temp2; + f32 sp5C; + f32 sp58; + f32 sp54; + s16 sp52; + s32 temp4; + s16 temp5; + s32 sp44; + + temp1 = this->currentYaw - this->actor.shape.rot.y; + temp2 = ABS(temp1); + + sp5C = fabsf(this->linearVelocity); + sp58 = sp5C * 1.5f; + + this->stateFlags1 |= PLAYER_STATE1_12; + + if (sp58 < 1.5f) { + sp58 = 1.5f; + } + + sp58 = ((temp2 < 0x4000) ? -1.0f : 1.0f) * sp58; + + func_8084029C(this, sp58); + + sp58 = CLAMP(sp5C * 0.5f, 0.5f, 1.0f); + + LinkAnimation_BlendToJoint(globalCtx, &this->skelAnime, D_80854360[Player_HoldsTwoHandedWeapon(this)], 0.0f, + D_80854370[Player_HoldsTwoHandedWeapon(this)], this->unk_868 * (21.0f / 29.0f), sp58, + this->blendTable); + + if (!func_80842964(this, globalCtx) && !func_80844BE4(this, globalCtx)) { + func_80844E3C(this); + func_80837268(this, &sp54, &sp52, 0.0f, globalCtx); + + temp4 = func_80840058(this, &sp54, &sp52, globalCtx); + + if (temp4 < 0) { + func_80844D30(this, globalCtx); + return; + } + + if (temp4 == 0) { + sp54 = 0.0f; + sp52 = this->currentYaw; + } + + temp5 = sp52 - this->currentYaw; + sp44 = ABS(temp5); + + if (sp44 > 0x4000) { + if (Math_StepToF(&this->linearVelocity, 0.0f, 1.0f)) { + this->currentYaw = sp52; + } + return; + } + + Math_AsymStepToF(&this->linearVelocity, sp54 * 0.2f, 1.0f, 0.5f); + Math_ScaledStepToS(&this->currentYaw, sp52, sp44 * 0.1f); + + if ((sp54 == 0.0f) && (this->linearVelocity == 0.0f)) { + func_80844DC8(this, globalCtx); + } + } +} + +void func_80845308(Player* this, GlobalContext* globalCtx) { + f32 sp5C; + f32 sp58; + f32 sp54; + s16 sp52; + s32 temp4; + s16 temp5; + s32 sp44; + + sp5C = fabsf(this->linearVelocity); + + this->stateFlags1 |= PLAYER_STATE1_12; + + if (sp5C == 0.0f) { + sp5C = ABS(this->unk_87C) * 0.0015f; + if (sp5C < 400.0f) { + sp5C = 0.0f; + } + func_8084029C(this, ((this->unk_87C >= 0) ? 1 : -1) * sp5C); + } + else { + sp58 = sp5C * 1.5f; + if (sp58 < 1.5f) { + sp58 = 1.5f; + } + func_8084029C(this, sp58); + } + + sp58 = CLAMP(sp5C * 0.5f, 0.5f, 1.0f); + + LinkAnimation_BlendToJoint(globalCtx, &this->skelAnime, D_80854360[Player_HoldsTwoHandedWeapon(this)], 0.0f, + D_80854378[Player_HoldsTwoHandedWeapon(this)], this->unk_868 * (21.0f / 29.0f), sp58, + this->blendTable); + + if (!func_80842964(this, globalCtx) && !func_80844BE4(this, globalCtx)) { + func_80844E3C(this); + func_80837268(this, &sp54, &sp52, 0.0f, globalCtx); + + temp4 = func_80840058(this, &sp54, &sp52, globalCtx); + + if (temp4 > 0) { + func_80844CF8(this, globalCtx); + return; + } + + if (temp4 == 0) { + sp54 = 0.0f; + sp52 = this->currentYaw; + } + + temp5 = sp52 - this->currentYaw; + sp44 = ABS(temp5); + + if (sp44 > 0x4000) { + if (Math_StepToF(&this->linearVelocity, 0.0f, 1.0f)) { + this->currentYaw = sp52; + } + return; + } + + Math_AsymStepToF(&this->linearVelocity, sp54 * 0.2f, 1.0f, 0.5f); + Math_ScaledStepToS(&this->currentYaw, sp52, sp44 * 0.1f); + + if ((sp54 == 0.0f) && (this->linearVelocity == 0.0f) && (sp5C == 0.0f)) { + func_80844DC8(this, globalCtx); + } + } +} + +void func_80845668(Player* this, GlobalContext* globalCtx) { + s32 sp3C; + f32 temp1; + s32 temp2; + f32 temp3; + + this->stateFlags2 |= PLAYER_STATE2_5; + sp3C = LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (this->skelAnime.animation == &gPlayerAnim_002D48) { + this->linearVelocity = 1.0f; + + if (LinkAnimation_OnFrame(&this->skelAnime, 8.0f)) { + temp1 = this->wallHeight; + + if (temp1 > this->ageProperties->unk_0C) { + temp1 = this->ageProperties->unk_0C; + } + + if (this->stateFlags1 & PLAYER_STATE1_27) { + temp1 *= 0.085f; + } + else { + temp1 *= 0.072f; + } + + if (!LINK_IS_ADULT) { + temp1 += 1.0f; + } + + func_80838940(this, NULL, temp1, globalCtx, NA_SE_VO_LI_AUTO_JUMP); + this->unk_850 = -1; + return; + } + } + else { + temp2 = func_808374A0(globalCtx, this, &this->skelAnime, 4.0f); + + if (temp2 == 0) { + this->stateFlags1 &= ~(PLAYER_STATE1_14 | PLAYER_STATE1_18); + return; + } + + if ((sp3C != 0) || (temp2 > 0)) { + func_8083C0E8(this, globalCtx); + this->stateFlags1 &= ~(PLAYER_STATE1_14 | PLAYER_STATE1_18); + return; + } + + temp3 = 0.0f; + + if (this->skelAnime.animation == &gPlayerAnim_0032E8) { + if (LinkAnimation_OnFrame(&this->skelAnime, 30.0f)) { + func_8083D0A8(globalCtx, this, 10.0f); + } + temp3 = 50.0f; + } + else if (this->skelAnime.animation == &gPlayerAnim_002D40) { + temp3 = 30.0f; + } + else if (this->skelAnime.animation == &gPlayerAnim_002D38) { + temp3 = 16.0f; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, temp3)) { + func_808328A0(this); + func_80832698(this, NA_SE_VO_LI_CLIMB_END); + } + + if ((this->skelAnime.animation == &gPlayerAnim_002D38) || (this->skelAnime.curFrame > 5.0f)) { + if (this->unk_850 == 0) { + func_80832854(this); + this->unk_850 = 1; + } + Math_StepToF(&this->actor.shape.yOffset, 0.0f, 150.0f); + } + } +} + +void func_808458D0(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5 | PLAYER_STATE2_6; + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (((this->stateFlags1 & PLAYER_STATE1_11) && (this->heldActor != NULL) && (this->getItemId == GI_NONE)) || + !func_80836670(this, globalCtx)) { + this->func_A74(globalCtx, this); + } +} + +s32 func_80845964(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2, f32 arg3, s16 arg4, s32 arg5) { + if ((arg5 != 0) && (this->linearVelocity == 0.0f)) { + return LinkAnimation_Update(globalCtx, &this->skelAnime); + } + + if (arg5 != 2) { + f32 sp34 = R_UPDATE_RATE * 0.5f; + f32 selfDistX = arg2->endPos.x - this->actor.world.pos.x; + f32 selfDistZ = arg2->endPos.z - this->actor.world.pos.z; + f32 sp28 = sqrtf(SQ(selfDistX) + SQ(selfDistZ)) / sp34; + s32 sp24 = (arg2->endFrame - globalCtx->csCtx.frames) + 1; + + arg4 = Math_Atan2S(selfDistZ, selfDistX); + + if (arg5 == 1) { + f32 distX = arg2->endPos.x - arg2->startPos.x; + f32 distZ = arg2->endPos.z - arg2->startPos.z; + s32 temp = (((sqrtf(SQ(distX) + SQ(distZ)) / sp34) / (arg2->endFrame - arg2->startFrame)) / 1.5f) * 4.0f; + + if (temp >= sp24) { + arg4 = this->actor.shape.rot.y; + arg3 = 0.0f; + } + else { + arg3 = sp28 / ((sp24 - temp) + 1); + } + } + else { + arg3 = sp28 / sp24; + } + } + + this->stateFlags2 |= PLAYER_STATE2_5; + func_80841EE4(this, globalCtx); + func_8083DF68(this, arg3, arg4); + + if ((arg3 == 0.0f) && (this->linearVelocity == 0.0f)) { + func_8083BF50(this, globalCtx); + } + + return 0; +} + +s32 func_80845BA0(GlobalContext* arg0, Player* arg1, f32* arg2, s32 arg3) { + f32 dx = arg1->unk_450.x - arg1->actor.world.pos.x; + f32 dz = arg1->unk_450.z - arg1->actor.world.pos.z; + s32 sp2C = sqrtf(SQ(dx) + SQ(dz)); + s16 yaw = Math_Vec3f_Yaw(&arg1->actor.world.pos, &arg1->unk_450); + + if (sp2C < arg3) { + *arg2 = 0.0f; + yaw = arg1->actor.shape.rot.y; + } + + if (func_80845964(arg0, arg1, NULL, *arg2, yaw, 2)) { + return 0; + } + + return sp2C; +} + +s32 func_80845C68(GlobalContext* globalCtx, s32 arg1) { + if (arg1 == 0) { + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_DOWN, 0xDFF); + } + gSaveContext.respawn[RESPAWN_MODE_DOWN].data = 0; + return arg1; +} + +void func_80845CA4(Player* this, GlobalContext* globalCtx) { + f32 sp3C; + s32 temp; + f32 sp34; + s32 sp30; + s32 pad; + + if (!func_8083B040(this, globalCtx)) { + if (this->unk_850 == 0) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (DECR(this->doorTimer) == 0) { + this->linearVelocity = 0.1f; + this->unk_850 = 1; + } + } + else if (this->unk_84F == 0) { + sp3C = 5.0f * D_808535E8; + + if (func_80845BA0(globalCtx, this, &sp3C, -1) < 30) { + this->unk_84F = 1; + this->stateFlags1 |= PLAYER_STATE1_29; + + this->unk_450.x = this->unk_45C.x; + this->unk_450.z = this->unk_45C.z; + } + } + else { + sp34 = 5.0f; + sp30 = 20; + + if (this->stateFlags1 & PLAYER_STATE1_0) { + sp34 = gSaveContext.entranceSpeed; + + if (D_808535F4 != 0) { + this->unk_450.x = (Math_SinS(D_808535FC) * 400.0f) + this->actor.world.pos.x; + this->unk_450.z = (Math_CosS(D_808535FC) * 400.0f) + this->actor.world.pos.z; + } + } + else if (this->unk_850 < 0) { + this->unk_850++; + + sp34 = gSaveContext.entranceSpeed; + sp30 = -1; + } + + temp = func_80845BA0(globalCtx, this, &sp34, sp30); + + if ((this->unk_850 == 0) || + ((temp == 0) && (this->linearVelocity == 0.0f) && (Gameplay_GetCamera(globalCtx, 0)->unk_14C & 0x10))) { + + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + func_80845C68(globalCtx, gSaveContext.respawn[RESPAWN_MODE_DOWN].data); + + if (!func_8083B644(this, globalCtx)) { + func_8083CF5C(this, globalCtx); + } + } + } + } + + if (this->stateFlags1 & PLAYER_STATE1_11) { + func_80836670(this, globalCtx); + } +} + +void func_80845EF8(Player* this, GlobalContext* globalCtx) { + s32 sp2C; + + this->stateFlags2 |= PLAYER_STATE2_5; + sp2C = LinkAnimation_Update(globalCtx, &this->skelAnime); + + func_80836670(this, globalCtx); + + if (sp2C) { + if (this->unk_850 == 0) { + if (DECR(this->doorTimer) == 0) { + this->unk_850 = 1; + this->skelAnime.endFrame = this->skelAnime.animLength - 1.0f; + } + } + else { + func_8083C0E8(this, globalCtx); + if (globalCtx->roomCtx.prevRoom.num >= 0) { + func_80097534(globalCtx, &globalCtx->roomCtx); + } + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + Gameplay_SetupRespawnPoint(globalCtx, 0, 0xDFF); + } + return; + } + + if (!(this->stateFlags1 & PLAYER_STATE1_29) && LinkAnimation_OnFrame(&this->skelAnime, 15.0f)) { + globalCtx->func_11D54(this, globalCtx); + } +} + +void func_80846050(Player* this, GlobalContext* globalCtx) { + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80839F90(this, globalCtx); + func_80835688(this, globalCtx); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 4.0f)) { + Actor* interactRangeActor = this->interactRangeActor; + + if (!func_80835644(globalCtx, this, interactRangeActor)) { + this->heldActor = interactRangeActor; + this->actor.child = interactRangeActor; + interactRangeActor->parent = &this->actor; + interactRangeActor->bgCheckFlags &= 0xFF00; + this->unk_3BC.y = interactRangeActor->shape.rot.y - this->actor.shape.rot.y; + } + return; + } + + Math_ScaledStepToS(&this->unk_3BC.y, 0, 4000); +} + +static struct_80832924 D_8085461C[] = { + { NA_SE_VO_LI_SWORD_L, 0x2031 }, + { NA_SE_VO_LI_SWORD_N, -0x20E6 }, +}; + +void func_80846120(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime) && (this->unk_850++ > 20)) { + if (!func_8083B040(this, globalCtx)) { + func_8083A098(this, &gPlayerAnim_002FA0, globalCtx); + } + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 41.0f)) { + BgHeavyBlock* heavyBlock = (BgHeavyBlock*)this->interactRangeActor; + + this->heldActor = &heavyBlock->dyna.actor; + this->actor.child = &heavyBlock->dyna.actor; + heavyBlock->dyna.actor.parent = &this->actor; + func_8002DBD0(&heavyBlock->dyna.actor, &heavyBlock->unk_164, &this->leftHandPos); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 229.0f)) { + Actor* heldActor = this->heldActor; + + heldActor->speedXZ = Math_SinS(heldActor->shape.rot.x) * 40.0f; + heldActor->velocity.y = Math_CosS(heldActor->shape.rot.x) * 40.0f; + heldActor->gravity = -2.0f; + heldActor->minVelocityY = -30.0f; + func_808323B4(globalCtx, this); + return; + } + + func_80832924(this, D_8085461C); +} + +void func_80846260(Player* this, GlobalContext* globalCtx) { + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, &gPlayerAnim_0032C0); + this->unk_850 = 1; + return; + } + + if (this->unk_850 == 0) { + if (LinkAnimation_OnFrame(&this->skelAnime, 27.0f)) { + Actor* interactRangeActor = this->interactRangeActor; + + this->heldActor = interactRangeActor; + this->actor.child = interactRangeActor; + interactRangeActor->parent = &this->actor; + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 25.0f)) { + func_80832698(this, NA_SE_VO_LI_SWORD_L); + return; + } + + } + else if (CHECK_BTN_ANY(sControlInput->press.button, BTN_A | BTN_B | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN)) { + func_80835C58(globalCtx, this, func_80846358, 1); + func_80832264(globalCtx, this, &gPlayerAnim_0032B8); + } +} + +void func_80846358(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80839F90(this, globalCtx); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 6.0f)) { + Actor* heldActor = this->heldActor; + + heldActor->world.rot.y = this->actor.shape.rot.y; + heldActor->speedXZ = 10.0f; + heldActor->velocity.y = 20.0f; + func_80834644(globalCtx, this); + func_8002F7DC(&this->actor, NA_SE_PL_THROW); + func_80832698(this, NA_SE_VO_LI_SWORD_N); + } +} + +void func_80846408(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, &gPlayerAnim_003070); + this->unk_850 = 15; + return; + } + + if (this->unk_850 != 0) { + this->unk_850--; + if (this->unk_850 == 0) { + func_8083A098(this, &gPlayerAnim_003068, globalCtx); + this->stateFlags1 &= ~PLAYER_STATE1_11; + func_80832698(this, NA_SE_VO_LI_DAMAGE_S); + } + } +} + +void func_808464B0(Player* this, GlobalContext* globalCtx) { + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80839F90(this, globalCtx); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 4.0f)) { + Actor* heldActor = this->heldActor; + + if (!func_80835644(globalCtx, this, heldActor)) { + heldActor->velocity.y = 0.0f; + heldActor->speedXZ = 0.0f; + func_80834644(globalCtx, this); + if (heldActor->id == ACTOR_EN_BOM_CHU) { + func_8083B8F4(this, globalCtx); + } + } + } +} + +void func_80846578(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime) || + ((this->skelAnime.curFrame >= 8.0f) && func_80837268(this, &sp34, &sp32, 0.018f, globalCtx))) { + func_80839F90(this, globalCtx); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 3.0f)) { + func_8084409C(globalCtx, this, this->linearVelocity + 8.0f, 12.0f); + } +} + +static ColliderCylinderInit D_80854624 = { + { + COLTYPE_HIT5, + AT_NONE, + AC_ON | AC_TYPE_ENEMY, + OC1_ON | OC1_TYPE_ALL, + OC2_TYPE_PLAYER, + COLSHAPE_CYLINDER, + }, + { + ELEMTYPE_UNK1, + { 0x00000000, 0x00, 0x00 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_NONE, + BUMP_ON, + OCELEM_ON, + }, + { 12, 60, 0, { 0, 0, 0 } }, +}; + +static ColliderQuadInit D_80854650 = { + { + COLTYPE_NONE, + AT_ON | AT_TYPE_PLAYER, + AC_NONE, + OC1_NONE, + OC2_TYPE_PLAYER, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK2, + { 0x00000100, 0x00, 0x01 }, + { 0xFFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_NONE, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +static ColliderQuadInit D_808546A0 = { + { + COLTYPE_METAL, + AT_ON | AT_TYPE_PLAYER, + AC_ON | AC_HARD | AC_TYPE_ENEMY, + OC1_NONE, + OC2_TYPE_PLAYER, + COLSHAPE_QUAD, + }, + { + ELEMTYPE_UNK2, + { 0x00100000, 0x00, 0x00 }, + { 0xDFCFFFFF, 0x00, 0x00 }, + TOUCH_ON | TOUCH_SFX_NORMAL, + BUMP_ON, + OCELEM_NONE, + }, + { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, +}; + +void func_8084663C(Actor* thisx, GlobalContext* globalCtx) { +} + +void func_80846648(GlobalContext* globalCtx, Player* this) { + this->actor.update = func_8084663C; + this->actor.draw = NULL; +} + +void func_80846660(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084F710, 0); + if ((globalCtx->sceneNum == SCENE_SPOT06) && (gSaveContext.sceneSetupIndex >= 4)) { + this->unk_84F = 1; + } + this->stateFlags1 |= PLAYER_STATE1_29; + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_003298, 2.0f / 3.0f, 0.0f, 24.0f, ANIMMODE_ONCE, + 0.0f); + this->actor.world.pos.y += 800.0f; +} + +static u8 D_808546F0[] = { ITEM_SWORD_MASTER, ITEM_SWORD_KOKIRI }; + +void func_80846720(GlobalContext* globalCtx, Player* this, s32 arg2) { + s32 item = D_808546F0[(void)0, gSaveContext.linkAge]; + s32 actionParam = sItemActionParams[item]; + + func_80835EFC(this); + func_808323B4(globalCtx, this); + + this->heldItemId = item; + this->nextModelGroup = Player_ActionToModelGroup(this, actionParam); + + func_8083399C(globalCtx, this, actionParam); + func_80834644(globalCtx, this); + + if (arg2 != 0) { + func_8002F7DC(&this->actor, NA_SE_IT_SWORD_PICKOUT); + } +} + +static Vec3f D_808546F4 = { -1.0f, 69.0f, 20.0f }; + +void func_808467D4(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084E9AC, 0); + this->stateFlags1 |= PLAYER_STATE1_29; + Math_Vec3f_Copy(&this->actor.world.pos, &D_808546F4); + this->currentYaw = this->actor.shape.rot.y = -0x8000; + LinkAnimation_Change(globalCtx, &this->skelAnime, this->ageProperties->unk_A0, 2.0f / 3.0f, 0.0f, 0.0f, + ANIMMODE_ONCE, 0.0f); + func_80832F54(globalCtx, this, 0x28F); + if (LINK_IS_ADULT) { + func_80846720(globalCtx, this, 0); + } + this->unk_850 = 20; +} + +void func_808468A8(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084F9A0, 0); + func_80832F54(globalCtx, this, 0x9B); +} + +void func_808468E8(GlobalContext* globalCtx, Player* this) { + func_808389E8(this, &gPlayerAnim_002FE0, 12.0f, globalCtx); + func_80835C58(globalCtx, this, func_8084F9C0, 0); + this->stateFlags1 |= PLAYER_STATE1_29; + this->fallStartHeight = this->actor.world.pos.y; + OnePointCutscene_Init(globalCtx, 5110, 40, &this->actor, MAIN_CAM); +} + +void func_80846978(GlobalContext* globalCtx, Player* this) { + func_80837C0C(globalCtx, this, 1, 2.0f, 2.0f, this->actor.shape.rot.y + 0x8000, 0); +} + +void func_808469BC(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084F698, 0); + this->actor.draw = NULL; + this->stateFlags1 |= PLAYER_STATE1_29; +} + +static s16 D_80854700[] = { ACTOR_MAGIC_WIND, ACTOR_MAGIC_DARK, ACTOR_MAGIC_FIRE }; + +Actor* func_80846A00(GlobalContext* globalCtx, Player* this, s32 arg2) { + return Actor_Spawn(&globalCtx->actorCtx, globalCtx, D_80854700[arg2], this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); +} + +void func_80846A68(GlobalContext* globalCtx, Player* this) { + this->actor.draw = NULL; + func_80835C58(globalCtx, this, func_8085076C, 0); + this->stateFlags1 |= PLAYER_STATE1_29; +} + +static InitChainEntry D_80854708[] = { + ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP), +}; + +static EffectBlureInit2 D_8085470C = { + 0, 8, 0, { 255, 255, 255, 255 }, { 255, 255, 255, 64 }, { 255, 255, 255, 0 }, { 255, 255, 255, 0 }, 4, + 0, 2, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, +}; + +static Vec3s D_80854730 = { -57, 3377, 0 }; + +void Player_InitCommon(Player* this, GlobalContext* globalCtx, FlexSkeletonHeader* skelHeader) { + this->ageProperties = &sAgeProperties[gSaveContext.linkAge]; + Actor_ProcessInitChain(&this->actor, D_80854708); + this->swordEffectIndex = TOTAL_EFFECT_COUNT; + this->currentYaw = this->actor.world.rot.y; + func_80834644(globalCtx, this); + + SkelAnime_InitLink(globalCtx, &this->skelAnime, skelHeader, D_80853914[PLAYER_ANIMGROUP_0][this->modelAnimType], 9, + this->jointTable, this->morphTable, PLAYER_LIMB_MAX); + this->skelAnime.baseTransl = D_80854730; + SkelAnime_InitLink(globalCtx, &this->skelAnime2, skelHeader, func_80833338(this), 9, this->jointTable2, + this->morphTable2, PLAYER_LIMB_MAX); + this->skelAnime2.baseTransl = D_80854730; + + Effect_Add(globalCtx, &this->swordEffectIndex, EFFECT_BLURE2, 0, 0, &D_8085470C); + ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawFeet, this->ageProperties->unk_04); + this->unk_46C = SUBCAM_NONE; + Collider_InitCylinder(globalCtx, &this->cylinder); + Collider_SetCylinder(globalCtx, &this->cylinder, &this->actor, &D_80854624); + Collider_InitQuad(globalCtx, &this->swordQuads[0]); + Collider_SetQuad(globalCtx, &this->swordQuads[0], &this->actor, &D_80854650); + Collider_InitQuad(globalCtx, &this->swordQuads[1]); + Collider_SetQuad(globalCtx, &this->swordQuads[1], &this->actor, &D_80854650); + Collider_InitQuad(globalCtx, &this->shieldQuad); + Collider_SetQuad(globalCtx, &this->shieldQuad, &this->actor, &D_808546A0); +} + +static void (*D_80854738[])(GlobalContext* globalCtx, Player* this) = { + func_80846648, func_808467D4, func_80846660, func_808468A8, func_808468E8, func_808469BC, + func_80846A68, func_80846978, func_8083CA54, func_8083CA54, func_8083CA54, func_8083CA54, + func_8083CA54, func_8083CA20, func_8083CA54, func_8083CA9C, +}; + +static Vec3f D_80854778 = { 0.0f, 50.0f, 0.0f }; + +void Player_Init(Actor* thisx, GlobalContext* globalCtx2) { + Player* this = (Player*)thisx; + GlobalContext* globalCtx = globalCtx2; + SceneTableEntry* scene = globalCtx->loadedScene; + u32 titleFileSize; + s32 initMode; + s32 sp50; + s32 sp4C; + + globalCtx->shootingGalleryStatus = globalCtx->bombchuBowlingStatus = 0; + + globalCtx->playerInit = Player_InitCommon; + globalCtx->playerUpdate = Player_UpdateCommon; + globalCtx->isPlayerDroppingFish = Player_IsDroppingFish; + globalCtx->startPlayerFishing = Player_StartFishing; + globalCtx->grabPlayer = func_80852F38; + globalCtx->startPlayerCutscene = func_80852FFC; + globalCtx->func_11D54 = func_80853080; + globalCtx->damagePlayer = Player_InflictDamage; + globalCtx->talkWithPlayer = func_80853148; + + thisx->room = -1; + this->ageProperties = &sAgeProperties[gSaveContext.linkAge]; + this->itemActionParam = this->heldItemActionParam = -1; + this->heldItemId = ITEM_NONE; + + func_80835F44(globalCtx, this, ITEM_NONE); + Player_SetEquipmentData(globalCtx, this); + this->prevBoots = this->currentBoots; + Player_InitCommon(this, globalCtx, gPlayerSkelHeaders[((void)0, gSaveContext.linkAge)]); + this->giObjectSegment = (void*)(((u32)ZeldaArena_MallocDebug(0x3008, "../z_player.c", 17175) + 8) & ~0xF); + + sp50 = gSaveContext.respawnFlag; + + if (sp50 != 0) { + if (sp50 == -3) { + thisx->params = gSaveContext.respawn[RESPAWN_MODE_RETURN].playerParams; + } + else { + if ((sp50 == 1) || (sp50 == -1)) { + this->unk_A86 = -2; + } + + if (sp50 < 0) { + sp4C = 0; + } + else { + sp4C = sp50 - 1; + Math_Vec3f_Copy(&thisx->world.pos, &gSaveContext.respawn[sp50 - 1].pos); + Math_Vec3f_Copy(&thisx->home.pos, &thisx->world.pos); + Math_Vec3f_Copy(&thisx->prevPos, &thisx->world.pos); + this->fallStartHeight = thisx->world.pos.y; + this->currentYaw = thisx->shape.rot.y = gSaveContext.respawn[sp4C].yaw; + thisx->params = gSaveContext.respawn[sp4C].playerParams; + } + + globalCtx->actorCtx.flags.tempSwch = gSaveContext.respawn[sp4C].tempSwchFlags & 0xFFFFFF; + globalCtx->actorCtx.flags.tempCollect = gSaveContext.respawn[sp4C].tempCollectFlags; + } + } + + if ((sp50 == 0) || (sp50 < -1)) { + titleFileSize = scene->titleFile.vromEnd - scene->titleFile.vromStart; + if ((titleFileSize != 0) && gSaveContext.showTitleCard) { + if ((gSaveContext.sceneSetupIndex < 4) && + (gEntranceTable[((void)0, gSaveContext.entranceIndex) + ((void)0, gSaveContext.sceneSetupIndex)].field & + 0x4000) && + ((globalCtx->sceneNum != SCENE_DDAN) || (gSaveContext.eventChkInf[11] & 1)) && + ((globalCtx->sceneNum != SCENE_NIGHT_SHOP) || (gSaveContext.eventChkInf[2] & 0x20))) { + TitleCard_InitPlaceName(globalCtx, &globalCtx->actorCtx.titleCtx, this->giObjectSegment, 160, 120, 144, + 24, 20); + } + } + gSaveContext.showTitleCard = true; + } + + if (func_80845C68(globalCtx, (sp50 == 2) ? 1 : 0) == 0) { + gSaveContext.respawn[RESPAWN_MODE_DOWN].playerParams = (thisx->params & 0xFF) | 0xD00; + } + + gSaveContext.respawn[RESPAWN_MODE_DOWN].data = 1; + + if (globalCtx->sceneNum <= SCENE_GANONTIKA_SONOGO) { + gSaveContext.infTable[26] |= gBitFlags[globalCtx->sceneNum]; + } + + initMode = (thisx->params & 0xF00) >> 8; + if ((initMode == 5) || (initMode == 6)) { + if (gSaveContext.cutsceneIndex >= 0xFFF0) { + initMode = 13; + } + } + + D_80854738[initMode](globalCtx, this); + + if (initMode != 0) { + if ((gSaveContext.gameMode == 0) || (gSaveContext.gameMode == 3)) { + this->naviActor = Player_SpawnFairy(globalCtx, this, &thisx->world.pos, &D_80854778, FAIRY_NAVI); + if (gSaveContext.dogParams != 0) { + gSaveContext.dogParams |= 0x8000; + } + } + } + + if (gSaveContext.nayrusLoveTimer != 0) { + gSaveContext.unk_13F0 = 3; + func_80846A00(globalCtx, this, 1); + this->stateFlags3 &= ~PLAYER_STATE3_6; + } + + if (gSaveContext.entranceSound != 0) { + Audio_PlayActorSound2(&this->actor, ((void)0, gSaveContext.entranceSound)); + gSaveContext.entranceSound = 0; + } + + Map_SavePlayerInitialInfo(globalCtx); + MREG(64) = 0; +} + +void func_808471F4(s16* pValue) { + s16 step; + + step = (ABS(*pValue) * 100.0f) / 1000.0f; + step = CLAMP(step, 400, 4000); + + Math_ScaledStepToS(pValue, 0, step); +} + +void func_80847298(Player* this) { + s16 sp26; + + if (!(this->unk_6AE & 2)) { + sp26 = this->actor.focus.rot.y - this->actor.shape.rot.y; + func_808471F4(&sp26); + this->actor.focus.rot.y = this->actor.shape.rot.y + sp26; + } + + if (!(this->unk_6AE & 1)) { + func_808471F4(&this->actor.focus.rot.x); + } + + if (!(this->unk_6AE & 8)) { + func_808471F4(&this->unk_6B6); + } + + if (!(this->unk_6AE & 0x40)) { + func_808471F4(&this->unk_6BC); + } + + if (!(this->unk_6AE & 4)) { + func_808471F4(&this->actor.focus.rot.z); + } + + if (!(this->unk_6AE & 0x10)) { + func_808471F4(&this->unk_6B8); + } + + if (!(this->unk_6AE & 0x20)) { + func_808471F4(&this->unk_6BA); + } + + if (!(this->unk_6AE & 0x80)) { + if (this->unk_6B0 != 0) { + func_808471F4(&this->unk_6B0); + } + else { + func_808471F4(&this->unk_6BE); + } + } + + if (!(this->unk_6AE & 0x100)) { + func_808471F4(&this->unk_6C0); + } + + this->unk_6AE = 0; +} + +static f32 D_80854784[] = { 120.0f, 240.0f, 360.0f }; + +static u8 sDiveDoActions[] = { DO_ACTION_1, DO_ACTION_2, DO_ACTION_3, DO_ACTION_4, + DO_ACTION_5, DO_ACTION_6, DO_ACTION_7, DO_ACTION_8 }; + +void func_808473D4(GlobalContext* globalCtx, Player* this) { + if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) && (this->actor.category == ACTORCAT_PLAYER)) { + Actor* heldActor = this->heldActor; + Actor* interactRangeActor = this->interactRangeActor; + s32 sp24; + s32 sp20 = this->unk_84B[this->unk_846]; + s32 sp1C = func_808332B8(this); + s32 doAction = DO_ACTION_NONE; + + if (!Player_InBlockingCsMode(globalCtx, this)) { + if (this->stateFlags1 & PLAYER_STATE1_20) { + doAction = DO_ACTION_RETURN; + } + else if ((this->heldItemActionParam == PLAYER_AP_FISHING_POLE) && (this->unk_860 != 0)) { + if (this->unk_860 == 2) { + doAction = DO_ACTION_REEL; + } + } + else if ((func_8084E3C4 != this->func_674) && !(this->stateFlags2 & PLAYER_STATE2_18)) { + if ((this->doorType != PLAYER_DOORTYPE_NONE) && + (!(this->stateFlags1 & PLAYER_STATE1_11) || + ((heldActor != NULL) && (heldActor->id == ACTOR_EN_RU1)))) { + doAction = DO_ACTION_OPEN; + } + else if ((!(this->stateFlags1 & PLAYER_STATE1_11) || (heldActor == NULL)) && + (interactRangeActor != NULL) && + ((!sp1C && (this->getItemId == GI_NONE)) || + ((this->getItemId < 0) && !(this->stateFlags1 & PLAYER_STATE1_27)))) { + if (this->getItemId < 0) { + doAction = DO_ACTION_OPEN; + } + else if ((interactRangeActor->id == ACTOR_BG_TOKI_SWD) && LINK_IS_ADULT) { + doAction = DO_ACTION_DROP; + } + else { + doAction = DO_ACTION_GRAB; + } + } + else if (!sp1C && (this->stateFlags2 & PLAYER_STATE2_0)) { + doAction = DO_ACTION_GRAB; + } + else if ((this->stateFlags2 & PLAYER_STATE2_2) || + (!(this->stateFlags1 & PLAYER_STATE1_23) && (this->rideActor != NULL))) { + doAction = DO_ACTION_CLIMB; + } + else if ((this->stateFlags1 & PLAYER_STATE1_23) && !EN_HORSE_CHECK_4((EnHorse*)this->rideActor) && + (func_8084D3E4 != this->func_674)) { + if ((this->stateFlags2 & PLAYER_STATE2_1) && (this->targetActor != NULL)) { + if (this->targetActor->category == ACTORCAT_NPC) { + doAction = DO_ACTION_SPEAK; + } + else { + doAction = DO_ACTION_CHECK; + } + } + else if (!func_8002DD78(this) && !(this->stateFlags1 & PLAYER_STATE1_20)) { + doAction = DO_ACTION_FASTER; + } + } + else if ((this->stateFlags2 & PLAYER_STATE2_1) && (this->targetActor != NULL)) { + if (this->targetActor->category == ACTORCAT_NPC) { + doAction = DO_ACTION_SPEAK; + } + else { + doAction = DO_ACTION_CHECK; + } + } + else if ((this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_21)) || + ((this->stateFlags1 & PLAYER_STATE1_23) && (this->stateFlags2 & PLAYER_STATE2_22))) { + doAction = DO_ACTION_DOWN; + } + else if (this->stateFlags2 & PLAYER_STATE2_16) { + doAction = DO_ACTION_ENTER; + } + else if ((this->stateFlags1 & PLAYER_STATE1_11) && (this->getItemId == GI_NONE) && + (heldActor != NULL)) { + if ((this->actor.bgCheckFlags & 1) || (heldActor->id == ACTOR_EN_NIW)) { + if (func_8083EAF0(this, heldActor) == 0) { + doAction = DO_ACTION_DROP; + } + else { + doAction = DO_ACTION_THROW; + } + } + } + else if (!(this->stateFlags1 & PLAYER_STATE1_27) && func_8083A0D4(this) && + (this->getItemId < GI_MAX)) { + doAction = DO_ACTION_GRAB; + } + else if (this->stateFlags2 & PLAYER_STATE2_11) { + sp24 = (D_80854784[CUR_UPG_VALUE(UPG_SCALE)] - this->actor.yDistToWater) / 40.0f; + sp24 = CLAMP(sp24, 0, 7); + doAction = sDiveDoActions[sp24]; + } + else if (sp1C && !(this->stateFlags2 & PLAYER_STATE2_10)) { + doAction = DO_ACTION_DIVE; + } + else if (!sp1C && (!(this->stateFlags1 & PLAYER_STATE1_22) || func_80833BCC(this) || + !Player_IsChildWithHylianShield(this))) { + if ((!(this->stateFlags1 & PLAYER_STATE1_14) && (sp20 <= 0) && + (func_8008E9C4(this) || + ((D_808535E4 != 7) && + (func_80833B2C(this) || ((globalCtx->roomCtx.curRoom.unk_03 != 2) && + !(this->stateFlags1 & PLAYER_STATE1_22) && (sp20 == 0))))))) { + doAction = DO_ACTION_ATTACK; + } + else if ((globalCtx->roomCtx.curRoom.unk_03 != 2) && func_80833BCC(this) && (sp20 > 0)) { + doAction = DO_ACTION_JUMP; + } + else if ((this->heldItemActionParam >= PLAYER_AP_SWORD_MASTER) || + ((this->stateFlags2 & PLAYER_STATE2_20) && + (globalCtx->actorCtx.targetCtx.arrowPointedActor == NULL))) { + doAction = DO_ACTION_PUTAWAY; + } + } + } + } + + if (doAction != DO_ACTION_PUTAWAY) { + this->unk_837 = 20; + } + else if (this->unk_837 != 0) { + doAction = DO_ACTION_NONE; + this->unk_837--; + } + + Interface_SetDoAction(globalCtx, doAction); + + if (this->stateFlags2 & PLAYER_STATE2_21) { + if (this->unk_664 != NULL) { + Interface_SetNaviCall(globalCtx, 0x1E); + } + else { + Interface_SetNaviCall(globalCtx, 0x1D); + } + Interface_SetNaviCall(globalCtx, 0x1E); + } + else { + Interface_SetNaviCall(globalCtx, 0x1F); + } + } +} + +s32 func_80847A78(Player* this) { + s32 cond; + + if ((this->currentBoots == PLAYER_BOOTS_HOVER) && (this->hoverBootsTimer != 0)) { + this->hoverBootsTimer--; + } + else { + this->hoverBootsTimer = 0; + } + + cond = (this->currentBoots == PLAYER_BOOTS_HOVER) && + ((this->actor.yDistToWater >= 0.0f) || (func_80838144(D_808535E4) >= 0) || func_8083816C(D_808535E4)); + + if (cond && (this->actor.bgCheckFlags & 1) && (this->hoverBootsTimer != 0)) { + this->actor.bgCheckFlags &= ~1; + } + + if (this->actor.bgCheckFlags & 1) { + if (!cond) { + this->hoverBootsTimer = 19; + } + return 0; + } + + D_808535E4 = 0; + this->unk_898 = this->unk_89A = D_80853610 = 0; + + return 1; +} + +static Vec3f D_80854798 = { 0.0f, 18.0f, 0.0f }; + +void func_80847BA0(GlobalContext* globalCtx, Player* this) { + u8 spC7 = 0; + CollisionPoly* spC0; + Vec3f spB4; + f32 spB0; + f32 spAC; + f32 spA8; + u32 spA4; + + D_80853604 = this->unk_A7A; + + if (this->stateFlags2 & PLAYER_STATE2_18) { + spB0 = 10.0f; + spAC = 15.0f; + spA8 = 30.0f; + } + else { + spB0 = this->ageProperties->unk_38; + spAC = 26.0f; + spA8 = this->ageProperties->unk_00; + } + + if (this->stateFlags1 & (PLAYER_STATE1_29 | PLAYER_STATE1_31)) { + if (this->stateFlags1 & PLAYER_STATE1_31) { + this->actor.bgCheckFlags &= ~1; + spA4 = 0x38; + } + else if ((this->stateFlags1 & PLAYER_STATE1_0) && ((this->unk_A84 - (s32)this->actor.world.pos.y) >= 100)) { + spA4 = 0x39; + } + else if (!(this->stateFlags1 & PLAYER_STATE1_0) && + ((func_80845EF8 == this->func_674) || (func_80845CA4 == this->func_674))) { + this->actor.bgCheckFlags &= ~0x208; + spA4 = 0x3C; + } + else { + spA4 = 0x3F; + } + } + else { + spA4 = 0x3F; + } + + if (this->stateFlags3 & PLAYER_STATE3_0) { + spA4 &= ~6; + } + + if (spA4 & 4) { + this->stateFlags3 |= PLAYER_STATE3_4; + } + + Math_Vec3f_Copy(&spB4, &this->actor.world.pos); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, spAC, spB0, spA8, spA4); + + if (this->actor.bgCheckFlags & 0x10) { + this->actor.velocity.y = 0.0f; + } + + D_80853600 = this->actor.world.pos.y - this->actor.floorHeight; + D_808535F4 = 0; + + spC0 = this->actor.floorPoly; + + if (spC0 != NULL) { + this->unk_A7A = func_80041EA4(&globalCtx->colCtx, spC0, this->actor.floorBgId); + this->unk_A82 = this->unk_89E; + + if (this->actor.bgCheckFlags & 0x20) { + if (this->actor.yDistToWater < 20.0f) { + this->unk_89E = 4; + } + else { + this->unk_89E = 5; + } + } + else { + if (this->stateFlags2 & PLAYER_STATE2_9) { + this->unk_89E = 1; + } + else { + this->unk_89E = SurfaceType_GetSfx(&globalCtx->colCtx, spC0, this->actor.floorBgId); + } + } + + if (this->actor.category == ACTORCAT_PLAYER) { + Audio_SetCodeReverb(SurfaceType_GetEcho(&globalCtx->colCtx, spC0, this->actor.floorBgId)); + + if (this->actor.floorBgId == BGCHECK_SCENE) { + func_80074CE8(globalCtx, + SurfaceType_GetLightSettingIndex(&globalCtx->colCtx, spC0, this->actor.floorBgId)); + } + else { + func_80043508(&globalCtx->colCtx, this->actor.floorBgId); + } + } + + D_808535F4 = SurfaceType_GetConveyorSpeed(&globalCtx->colCtx, spC0, this->actor.floorBgId); + if (D_808535F4 != 0) { + D_808535F8 = SurfaceType_IsConveyor(&globalCtx->colCtx, spC0, this->actor.floorBgId); + if (((D_808535F8 == 0) && (this->actor.yDistToWater > 20.0f) && + (this->currentBoots != PLAYER_BOOTS_IRON)) || + ((D_808535F8 != 0) && (this->actor.bgCheckFlags & 1))) { + D_808535FC = SurfaceType_GetConveyorDirection(&globalCtx->colCtx, spC0, this->actor.floorBgId) << 10; + } + else { + D_808535F4 = 0; + } + } + } + + func_80839034(globalCtx, this, spC0, this->actor.floorBgId); + + this->actor.bgCheckFlags &= ~0x200; + + if (this->actor.bgCheckFlags & 8) { + CollisionPoly* spA0; + s32 sp9C; + s16 sp9A; + s32 pad; + + D_80854798.y = 18.0f; + D_80854798.z = this->ageProperties->unk_38 + 10.0f; + + if (!(this->stateFlags2 & PLAYER_STATE2_18) && + func_80839768(globalCtx, this, &D_80854798, &spA0, &sp9C, &D_80858AA8)) { + this->actor.bgCheckFlags |= 0x200; + if (this->actor.wallPoly != spA0) { + this->actor.wallPoly = spA0; + this->actor.wallBgId = sp9C; + this->actor.wallYaw = Math_Atan2S(spA0->normal.z, spA0->normal.x); + } + } + + sp9A = this->actor.shape.rot.y - (s16)(this->actor.wallYaw + 0x8000); + + D_808535F0 = func_80041DB8(&globalCtx->colCtx, this->actor.wallPoly, this->actor.wallBgId); + + D_80853608 = ABS(sp9A); + + sp9A = this->currentYaw - (s16)(this->actor.wallYaw + 0x8000); + + D_8085360C = ABS(sp9A); + + spB0 = D_8085360C * 0.00008f; + if (!(this->actor.bgCheckFlags & 1) || spB0 >= 1.0f) { + this->unk_880 = R_RUN_SPEED_LIMIT / 100.0f; + } + else { + spAC = (R_RUN_SPEED_LIMIT / 100.0f * spB0); + this->unk_880 = spAC; + if (spAC < 0.1f) { + this->unk_880 = 0.1f; + } + } + + if ((this->actor.bgCheckFlags & 0x200) && (D_80853608 < 0x3000)) { + CollisionPoly* wallPoly = this->actor.wallPoly; + + if (ABS(wallPoly->normal.y) < 600) { + f32 sp8C = COLPOLY_GET_NORMAL(wallPoly->normal.x); + f32 sp88 = COLPOLY_GET_NORMAL(wallPoly->normal.y); + f32 sp84 = COLPOLY_GET_NORMAL(wallPoly->normal.z); + f32 wallHeight; + CollisionPoly* sp7C; + CollisionPoly* sp78; + s32 sp74; + Vec3f sp68; + f32 sp64; + f32 sp60; + s32 temp3; + + this->wallDistance = Math3D_UDistPlaneToPos(sp8C, sp88, sp84, wallPoly->dist, &this->actor.world.pos); + + spB0 = this->wallDistance + 10.0f; + sp68.x = this->actor.world.pos.x - (spB0 * sp8C); + sp68.z = this->actor.world.pos.z - (spB0 * sp84); + sp68.y = this->actor.world.pos.y + this->ageProperties->unk_0C; + + sp64 = BgCheck_EntityRaycastFloor1(&globalCtx->colCtx, &sp7C, &sp68); + wallHeight = sp64 - this->actor.world.pos.y; + this->wallHeight = wallHeight; + + if ((this->wallHeight < 18.0f) || + BgCheck_EntityCheckCeiling(&globalCtx->colCtx, &sp60, &this->actor.world.pos, + (sp64 - this->actor.world.pos.y) + 20.0f, &sp78, &sp74, &this->actor)) { + this->wallHeight = 399.96002f; + } + else { + D_80854798.y = (sp64 + 5.0f) - this->actor.world.pos.y; + + if (func_80839768(globalCtx, this, &D_80854798, &sp78, &sp74, &D_80858AA8) && + (temp3 = this->actor.wallYaw - Math_Atan2S(sp78->normal.z, sp78->normal.x), + ABS(temp3) < 0x4000) && + !func_80041E18(&globalCtx->colCtx, sp78, sp74)) { + this->wallHeight = 399.96002f; + } + else if (func_80041DE4(&globalCtx->colCtx, wallPoly, this->actor.wallBgId) == 0) { + if (this->ageProperties->unk_1C <= this->wallHeight) { + if (ABS(sp7C->normal.y) > 28000) { + if (this->ageProperties->unk_14 <= this->wallHeight) { + spC7 = 4; + } + else if (this->ageProperties->unk_18 <= this->wallHeight) { + spC7 = 3; + } + else { + spC7 = 2; + } + } + } + else { + spC7 = 1; + } + } + } + } + } + } + else { + this->unk_880 = R_RUN_SPEED_LIMIT / 100.0f; + this->unk_88D = 0; + this->wallHeight = 0.0f; + } + + if (spC7 == this->unk_88C) { + if ((this->linearVelocity != 0.0f) && (this->unk_88D < 100)) { + this->unk_88D++; + } + } + else { + this->unk_88C = spC7; + this->unk_88D = 0; + } + + if (this->actor.bgCheckFlags & 1) { + D_808535E4 = func_80041D4C(&globalCtx->colCtx, spC0, this->actor.floorBgId); + + if (!func_80847A78(this)) { + f32 sp58; + f32 sp54; + f32 sp50; + f32 sp4C; + s32 pad2; + f32 sp44; + s32 pad3; + + if (this->actor.floorBgId != BGCHECK_SCENE) { + func_800434C8(&globalCtx->colCtx, this->actor.floorBgId); + } + + sp58 = COLPOLY_GET_NORMAL(spC0->normal.x); + sp54 = 1.0f / COLPOLY_GET_NORMAL(spC0->normal.y); + sp50 = COLPOLY_GET_NORMAL(spC0->normal.z); + + sp4C = Math_SinS(this->currentYaw); + sp44 = Math_CosS(this->currentYaw); + + this->unk_898 = Math_Atan2S(1.0f, (-(sp58 * sp4C) - (sp50 * sp44)) * sp54); + this->unk_89A = Math_Atan2S(1.0f, (-(sp58 * sp44) - (sp50 * sp4C)) * sp54); + + sp4C = Math_SinS(this->actor.shape.rot.y); + sp44 = Math_CosS(this->actor.shape.rot.y); + + D_80853610 = Math_Atan2S(1.0f, (-(sp58 * sp4C) - (sp50 * sp44)) * sp54); + + func_8083E318(globalCtx, this, spC0); + } + } + else { + func_80847A78(this); + } + + if (this->unk_A7B == D_808535E4) { + this->unk_A79++; + } + else { + this->unk_A7B = D_808535E4; + this->unk_A79 = 0; + } +} + +void Player_UpdateCamAndSeqModes(GlobalContext* globalCtx, Player* this) { + u8 seqMode; + s32 pad; + Actor* unk_664; + s32 camMode; + + if (this->actor.category == ACTORCAT_PLAYER) { + seqMode = SEQ_MODE_DEFAULT; + + if (this->csMode != 0) { + Camera_ChangeMode(Gameplay_GetCamera(globalCtx, 0), CAM_MODE_NORMAL); + } + else if (!(this->stateFlags1 & PLAYER_STATE1_20)) { + if ((this->actor.parent != NULL) && (this->stateFlags3 & PLAYER_STATE3_7)) { + camMode = CAM_MODE_HOOKSHOT; + Camera_SetParam(Gameplay_GetCamera(globalCtx, 0), 8, this->actor.parent); + } + else if (func_8084377C == this->func_674) { + camMode = CAM_MODE_STILL; + } + else if (this->stateFlags2 & PLAYER_STATE2_8) { + camMode = CAM_MODE_PUSHPULL; + } + else if ((unk_664 = this->unk_664) != NULL) { + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_8)) { + camMode = CAM_MODE_TALK; + } + else if (this->stateFlags1 & PLAYER_STATE1_16) { + if (this->stateFlags1 & PLAYER_STATE1_25) { + camMode = CAM_MODE_FOLLOWBOOMERANG; + } + else { + camMode = CAM_MODE_FOLLOWTARGET; + } + } + else { + camMode = CAM_MODE_BATTLE; + } + Camera_SetParam(Gameplay_GetCamera(globalCtx, 0), 8, unk_664); + } + else if (this->stateFlags1 & PLAYER_STATE1_12) { + camMode = CAM_MODE_CHARGE; + } + else if (this->stateFlags1 & PLAYER_STATE1_25) { + camMode = CAM_MODE_FOLLOWBOOMERANG; + Camera_SetParam(Gameplay_GetCamera(globalCtx, 0), 8, this->boomerangActor); + } + else if (this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_14)) { + if (func_80833B2C(this)) { + camMode = CAM_MODE_HANGZ; + } + else { + camMode = CAM_MODE_HANG; + } + } + else if (this->stateFlags1 & (PLAYER_STATE1_17 | PLAYER_STATE1_30)) { + if (func_8002DD78(this) || func_808334B4(this)) { + camMode = CAM_MODE_BOWARROWZ; + } + else if (this->stateFlags1 & PLAYER_STATE1_21) { + camMode = CAM_MODE_CLIMBZ; + } + else { + camMode = CAM_MODE_TARGET; + } + } + else if (this->stateFlags1 & (PLAYER_STATE1_18 | PLAYER_STATE1_21)) { + if ((func_80845668 == this->func_674) || (this->stateFlags1 & PLAYER_STATE1_21)) { + camMode = CAM_MODE_CLIMB; + } + else { + camMode = CAM_MODE_JUMP; + } + } + else if (this->stateFlags1 & PLAYER_STATE1_19) { + camMode = CAM_MODE_FREEFALL; + } + else if ((this->swordState != 0) && (this->swordAnimation >= 0) && (this->swordAnimation < 0x18)) { + camMode = CAM_MODE_STILL; + } + else { + camMode = CAM_MODE_NORMAL; + if ((this->linearVelocity == 0.0f) && + (!(this->stateFlags1 & PLAYER_STATE1_23) || (this->rideActor->speedXZ == 0.0f))) { + // not moving + seqMode = SEQ_MODE_STILL; + } + } + + Camera_ChangeMode(Gameplay_GetCamera(globalCtx, 0), camMode); + } + else { + // First person mode + seqMode = SEQ_MODE_STILL; + } + + if (globalCtx->actorCtx.targetCtx.bgmEnemy != NULL) { + seqMode = SEQ_MODE_ENEMY; + Audio_SetBgmEnemyVolume(sqrtf(globalCtx->actorCtx.targetCtx.bgmEnemy->xyzDistToPlayerSq)); + } + + if (globalCtx->sceneNum != SCENE_TURIBORI) { + Audio_SetSequenceMode(seqMode); + } + } +} + +static Vec3f D_808547A4 = { 0.0f, 0.5f, 0.0f }; +static Vec3f D_808547B0 = { 0.0f, 0.5f, 0.0f }; + +static Color_RGBA8 D_808547BC = { 255, 255, 100, 255 }; +static Color_RGBA8 D_808547C0 = { 255, 50, 0, 0 }; + +void func_80848A04(GlobalContext* globalCtx, Player* this) { + f32 temp; + + if (this->unk_85C == 0.0f) { + func_80835F44(globalCtx, this, 0xFF); + return; + } + + temp = 1.0f; + if (DECR(this->unk_860) == 0) { + Inventory_ChangeAmmo(ITEM_STICK, -1); + this->unk_860 = 1; + temp = 0.0f; + this->unk_85C = temp; + } + else if (this->unk_860 > 200) { + temp = (210 - this->unk_860) / 10.0f; + } + else if (this->unk_860 < 20) { + temp = this->unk_860 / 20.0f; + this->unk_85C = temp; + } + + func_8002836C(globalCtx, &this->swordInfo[0].tip, &D_808547A4, &D_808547B0, &D_808547BC, &D_808547C0, temp * 200.0f, + 0, 8); +} + +void func_80848B44(GlobalContext* globalCtx, Player* this) { + Vec3f shockPos; + Vec3f* randBodyPart; + s32 shockScale; + + this->shockTimer--; + this->unk_892 += this->shockTimer; + + if (this->unk_892 > 20) { + shockScale = this->shockTimer * 2; + this->unk_892 -= 20; + + if (shockScale > 40) { + shockScale = 40; + } + + randBodyPart = this->bodyPartsPos + (s32)Rand_ZeroFloat(ARRAY_COUNT(this->bodyPartsPos) - 0.1f); + shockPos.x = (Rand_CenteredFloat(5.0f) + randBodyPart->x) - this->actor.world.pos.x; + shockPos.y = (Rand_CenteredFloat(5.0f) + randBodyPart->y) - this->actor.world.pos.y; + shockPos.z = (Rand_CenteredFloat(5.0f) + randBodyPart->z) - this->actor.world.pos.z; + + EffectSsFhgFlash_SpawnShock(globalCtx, &this->actor, &shockPos, shockScale, FHGFLASH_SHOCK_PLAYER); + func_8002F8F0(&this->actor, NA_SE_PL_SPARK - SFX_FLAG); + } +} + +void func_80848C74(GlobalContext* globalCtx, Player* this) { + s32 spawnedFlame; + u8* timerPtr; + s32 timerStep; + f32 flameScale; + f32 flameIntensity; + s32 dmgCooldown; + s32 i; + s32 sp58; + s32 sp54; + + if (this->currentTunic == PLAYER_TUNIC_GORON) { + sp54 = 20; + } + else { + sp54 = (s32)(this->linearVelocity * 0.4f) + 1; + } + + spawnedFlame = false; + timerPtr = this->flameTimers; + + if (this->stateFlags2 & PLAYER_STATE2_3) { + sp58 = 100; + } + else { + sp58 = 0; + } + + func_8083819C(this, globalCtx); + + for (i = 0; i < PLAYER_BODYPART_MAX; i++, timerPtr++) { + timerStep = sp58 + sp54; + + if (*timerPtr <= timerStep) { + *timerPtr = 0; + } + else { + spawnedFlame = true; + *timerPtr -= timerStep; + + if (*timerPtr > 20.0f) { + flameIntensity = (*timerPtr - 20.0f) * 0.01f; + flameScale = CLAMP(flameIntensity, 0.19999999f, 0.2f); + } + else { + flameScale = *timerPtr * 0.01f; + } + + flameIntensity = (*timerPtr - 25.0f) * 0.02f; + flameIntensity = CLAMP(flameIntensity, 0.0f, 1.0f); + EffectSsFireTail_SpawnFlameOnPlayer(globalCtx, flameScale, i, flameIntensity); + } + } + + if (spawnedFlame) { + func_8002F7DC(&this->actor, NA_SE_EV_TORCH - SFX_FLAG); + + if (globalCtx->sceneNum == SCENE_JYASINBOSS) { + dmgCooldown = 0; + } + else { + dmgCooldown = 7; + } + + if ((dmgCooldown & globalCtx->gameplayFrames) == 0) { + Player_InflictDamage(globalCtx, -1); + } + } + else { + this->isBurning = false; + } +} + +void func_80848EF8(Player* this) { + if (CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)) { + f32 temp = 200000.0f - (this->unk_6A4 * 5.0f); + + if (temp < 0.0f) { + temp = 0.0f; + } + + this->unk_6A0 += temp; + if (this->unk_6A0 > 4000000.0f) { + this->unk_6A0 = 0.0f; + func_8083264C(this, 120, 20, 10, 0); + } + } +} + +static s8 D_808547C4[] = { + 0, 3, 3, 5, 4, 8, 9, 13, 14, 15, 16, 17, 18, -22, 23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 34, -35, + 30, 36, 38, -39, -40, -41, 42, 43, 45, 46, 0, 0, 0, 67, 48, 47, -50, 51, -52, -53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, -65, -66, 68, 11, 69, 70, 71, 8, 8, 72, 73, 78, 79, 80, 89, 90, 91, 92, 77, 19, 94, +}; + +static Vec3f D_80854814 = { 0.0f, 0.0f, 200.0f }; + +static f32 D_80854820[] = { 2.0f, 4.0f, 7.0f }; +static f32 D_8085482C[] = { 0.5f, 1.0f, 3.0f }; + +void Player_UpdateCommon(Player* this, GlobalContext* globalCtx, Input* input) { + s32 pad; + + sControlInput = input; + + if (this->unk_A86 < 0) { + this->unk_A86++; + if (this->unk_A86 == 0) { + this->unk_A86 = 1; + func_80078884(NA_SE_OC_REVENGE); + } + } + + Math_Vec3f_Copy(&this->actor.prevPos, &this->actor.home.pos); + + if (this->unk_A73 != 0) { + this->unk_A73--; + } + + if (this->unk_88E != 0) { + this->unk_88E--; + } + + if (this->unk_A87 != 0) { + this->unk_A87--; + } + + if (this->invincibilityTimer < 0) { + this->invincibilityTimer++; + } + else if (this->invincibilityTimer > 0) { + this->invincibilityTimer--; + } + + if (this->unk_890 != 0) { + this->unk_890--; + } + + func_808473D4(globalCtx, this); + func_80836BEC(this, globalCtx); + + if ((this->heldItemActionParam == PLAYER_AP_STICK) && (this->unk_860 != 0)) { + func_80848A04(globalCtx, this); + } + else if ((this->heldItemActionParam == PLAYER_AP_FISHING_POLE) && (this->unk_860 < 0)) { + this->unk_860++; + } + + if (this->shockTimer != 0) { + func_80848B44(globalCtx, this); + } + + if (this->isBurning) { + func_80848C74(globalCtx, this); + } + + if ((this->stateFlags3 & PLAYER_STATE3_6) && (gSaveContext.nayrusLoveTimer != 0) && (gSaveContext.unk_13F0 == 0)) { + gSaveContext.unk_13F0 = 3; + func_80846A00(globalCtx, this, 1); + this->stateFlags3 &= ~PLAYER_STATE3_6; + } + + if (this->stateFlags2 & PLAYER_STATE2_15) { + if (!(this->actor.bgCheckFlags & 1)) { + func_80832210(this); + Actor_MoveForward(&this->actor); + } + + func_80847BA0(globalCtx, this); + } + else { + f32 temp_f0; + f32 phi_f12; + + if (this->currentBoots != this->prevBoots) { + if (this->currentBoots == PLAYER_BOOTS_IRON) { + if (this->stateFlags1 & PLAYER_STATE1_27) { + func_80832340(globalCtx, this); + if (this->ageProperties->unk_2C < this->actor.yDistToWater) { + this->stateFlags2 |= PLAYER_STATE2_10; + } + } + } + else { + if (this->stateFlags1 & PLAYER_STATE1_27) { + if ((this->prevBoots == PLAYER_BOOTS_IRON) || (this->actor.bgCheckFlags & 1)) { + func_8083D36C(globalCtx, this); + this->stateFlags2 &= ~PLAYER_STATE2_10; + } + } + } + + this->prevBoots = this->currentBoots; + } + + if ((this->actor.parent == NULL) && (this->stateFlags1 & PLAYER_STATE1_23)) { + this->actor.parent = this->rideActor; + func_8083A360(globalCtx, this); + this->stateFlags1 |= PLAYER_STATE1_23; + func_80832264(globalCtx, this, &gPlayerAnim_0033B8); + func_80832F54(globalCtx, this, 0x9B); + this->unk_850 = 99; + } + + if (this->unk_844 == 0) { + this->unk_845 = 0; + } + else if (this->unk_844 < 0) { + this->unk_844++; + } + else { + this->unk_844--; + } + + Math_ScaledStepToS(&this->unk_6C2, 0, 400); + func_80032CB4(this->unk_3A8, 20, 80, 6); + + this->actor.shape.face = this->unk_3A8[0] + ((globalCtx->gameplayFrames & 32) ? 0 : 3); + + if (this->currentMask == PLAYER_MASK_BUNNY) { + func_8085002C(this); + } + + if (func_8002DD6C(this) != 0) { + func_8084FF7C(this); + } + + if (!(this->skelAnime.moveFlags & 0x80)) { + if (((this->actor.bgCheckFlags & 1) && (D_808535E4 == 5) && (this->currentBoots != PLAYER_BOOTS_IRON)) || + ((this->currentBoots == PLAYER_BOOTS_HOVER) && + !(this->stateFlags1 & (PLAYER_STATE1_27 | PLAYER_STATE1_29)))) { + f32 sp70 = this->linearVelocity; + s16 sp6E = this->currentYaw; + s16 yawDiff = this->actor.world.rot.y - sp6E; + s32 pad; + + if ((ABS(yawDiff) > 0x6000) && (this->actor.speedXZ != 0.0f)) { + sp70 = 0.0f; + sp6E += 0x8000; + } + + if (Math_StepToF(&this->actor.speedXZ, sp70, 0.35f) && (sp70 == 0.0f)) { + this->actor.world.rot.y = this->currentYaw; + } + + if (this->linearVelocity != 0.0f) { + s32 phi_v0; + + phi_v0 = (fabsf(this->linearVelocity) * 700.0f) - (fabsf(this->actor.speedXZ) * 100.0f); + phi_v0 = CLAMP(phi_v0, 0, 1350); + + Math_ScaledStepToS(&this->actor.world.rot.y, sp6E, phi_v0); + } + + if ((this->linearVelocity == 0.0f) && (this->actor.speedXZ != 0.0f)) { + func_800F4138(&this->actor.projectedPos, 0xD0, this->actor.speedXZ); + } + } + else { + this->actor.speedXZ = this->linearVelocity; + this->actor.world.rot.y = this->currentYaw; + } + + func_8002D868(&this->actor); + + if ((this->windSpeed != 0.0f) && !Player_InCsMode(globalCtx) && + !(this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_21)) && + (func_80845668 != this->func_674) && (func_808507F4 != this->func_674)) { + this->actor.velocity.x += this->windSpeed * Math_SinS(this->windDirection); + this->actor.velocity.z += this->windSpeed * Math_CosS(this->windDirection); + } + + func_8002D7EC(&this->actor); + func_80847BA0(globalCtx, this); + } + else { + D_808535E4 = 0; + this->unk_A7A = 0; + + if (!(this->stateFlags1 & PLAYER_STATE1_0) && (this->stateFlags1 & PLAYER_STATE1_23)) { + EnHorse* rideActor = (EnHorse*)this->rideActor; + CollisionPoly* sp5C; + s32 sp58; + Vec3f sp4C; + + if (!(rideActor->actor.bgCheckFlags & 1)) { + func_808396F4(globalCtx, this, &D_80854814, &sp4C, &sp5C, &sp58); + } + else { + sp5C = rideActor->actor.floorPoly; + sp58 = rideActor->actor.floorBgId; + } + + if ((sp5C != NULL) && func_80839034(globalCtx, this, sp5C, sp58)) { + if (DREG(25) != 0) { + DREG(25) = 0; + } + else { + AREG(6) = 1; + } + } + } + + D_808535F4 = 0; + this->windSpeed = 0.0f; + } + + if ((D_808535F4 != 0) && (this->currentBoots != PLAYER_BOOTS_IRON)) { + f32 sp48; + + D_808535F4--; + + if (D_808535F8 == 0) { + sp48 = D_80854820[D_808535F4]; + + if (!(this->stateFlags1 & PLAYER_STATE1_27)) { + sp48 *= 0.25f; + } + } + else { + sp48 = D_8085482C[D_808535F4]; + } + + Math_StepToF(&this->windSpeed, sp48, sp48 * 0.1f); + + Math_ScaledStepToS(&this->windDirection, D_808535FC, + ((this->stateFlags1 & PLAYER_STATE1_27) ? 400.0f : 800.0f) * sp48); + } + else if (this->windSpeed != 0.0f) { + Math_StepToF(&this->windSpeed, 0.0f, (this->stateFlags1 & PLAYER_STATE1_27) ? 0.5f : 1.0f); + } + + if (!Player_InBlockingCsMode(globalCtx, this) && !(this->stateFlags2 & PLAYER_STATE2_18)) { + func_8083D53C(globalCtx, this); + + if ((this->actor.category == ACTORCAT_PLAYER) && (gSaveContext.health == 0)) { + if (this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_21)) { + func_80832440(globalCtx, this); + func_80837B9C(this, globalCtx); + } + else if ((this->actor.bgCheckFlags & 1) || (this->stateFlags1 & PLAYER_STATE1_27)) { + func_80836448(globalCtx, this, + func_808332B8(this) ? &gPlayerAnim_003310 + : (this->shockTimer != 0) ? &gPlayerAnim_002F08 + : &gPlayerAnim_002878); + } + } + else { + if ((this->actor.parent == NULL) && + ((globalCtx->sceneLoadFlag == 0x14) || (this->unk_A87 != 0) || !func_808382DC(this, globalCtx))) { + func_8083AA10(this, globalCtx); + } + else { + this->fallStartHeight = this->actor.world.pos.y; + } + func_80848EF8(this); + } + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (this->csMode != 6) && + !(this->stateFlags1 & PLAYER_STATE1_23) && !(this->stateFlags2 & PLAYER_STATE2_7) && + (this->actor.category == ACTORCAT_PLAYER)) { + CsCmdActorAction* linkActionCsCmd = globalCtx->csCtx.linkAction; + + if ((linkActionCsCmd != NULL) && (D_808547C4[linkActionCsCmd->action] != 0)) { + func_8002DF54(globalCtx, NULL, 6); + func_80832210(this); + } + else if ((this->csMode == 0) && !(this->stateFlags2 & PLAYER_STATE2_10) && + (globalCtx->csCtx.state != CS_STATE_UNSKIPPABLE_INIT)) { + func_8002DF54(globalCtx, NULL, 0x31); + func_80832210(this); + } + } + + if (this->csMode != 0) { + if ((this->csMode != 7) || + !(this->stateFlags1 & (PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_21 | PLAYER_STATE1_26))) { + this->unk_6AD = 3; + } + else if (func_80852E14 != this->func_674) { + func_80852944(globalCtx, this, NULL); + } + } + else { + this->prevCsMode = 0; + } + + func_8083D6EC(globalCtx, this); + + if ((this->unk_664 == NULL) && (this->naviTextId == 0)) { + this->stateFlags2 &= ~(PLAYER_STATE2_1 | PLAYER_STATE2_21); + } + + this->stateFlags1 &= ~(PLAYER_STATE1_1 | PLAYER_STATE1_9 | PLAYER_STATE1_12 | PLAYER_STATE1_22); + this->stateFlags2 &= ~(PLAYER_STATE2_0 | PLAYER_STATE2_2 | PLAYER_STATE2_3 | PLAYER_STATE2_5 | PLAYER_STATE2_6 | + PLAYER_STATE2_8 | PLAYER_STATE2_9 | PLAYER_STATE2_12 | PLAYER_STATE2_14 | + PLAYER_STATE2_16 | PLAYER_STATE2_22 | PLAYER_STATE2_26); + this->stateFlags3 &= ~PLAYER_STATE3_4; + + func_80847298(this); + func_8083315C(globalCtx, this); + + if (this->stateFlags1 & PLAYER_STATE1_27) { + D_808535E8 = 0.5f; + } + else { + D_808535E8 = 1.0f; + } + + D_808535EC = 1.0f / D_808535E8; + D_80853614 = D_80853618 = 0; + D_80858AA4 = this->currentMask; + + if (!(this->stateFlags3 & PLAYER_STATE3_2)) { + this->func_674(this, globalCtx); + } + + Player_UpdateCamAndSeqModes(globalCtx, this); + + if (this->skelAnime.moveFlags & 8) { + AnimationContext_SetMoveActor(globalCtx, &this->actor, &this->skelAnime, + (this->skelAnime.moveFlags & 4) ? 1.0f : this->ageProperties->unk_08); + } + + func_808368EC(this, globalCtx); + + if (CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_8)) { + this->targetActorDistance = 0.0f; + } + else { + this->targetActor = NULL; + this->targetActorDistance = FLT_MAX; + this->exchangeItemId = EXCH_ITEM_NONE; + } + + if (!(this->stateFlags1 & PLAYER_STATE1_11)) { + this->interactRangeActor = NULL; + this->getItemDirection = 0x6000; + } + + if (this->actor.parent == NULL) { + this->rideActor = NULL; + } + + this->naviTextId = 0; + + if (!(this->stateFlags2 & PLAYER_STATE2_25)) { + this->unk_6A8 = NULL; + } + + this->stateFlags2 &= ~PLAYER_STATE2_23; + this->unk_6A4 = FLT_MAX; + + temp_f0 = this->actor.world.pos.y - this->actor.prevPos.y; + + this->doorType = PLAYER_DOORTYPE_NONE; + this->unk_8A1 = 0; + this->unk_684 = NULL; + + phi_f12 = + ((this->bodyPartsPos[PLAYER_BODYPART_L_FOOT].y + this->bodyPartsPos[PLAYER_BODYPART_R_FOOT].y) * 0.5f) + + temp_f0; + temp_f0 += this->bodyPartsPos[PLAYER_BODYPART_HEAD].y + 10.0f; + + this->cylinder.dim.height = temp_f0 - phi_f12; + + if (this->cylinder.dim.height < 0) { + phi_f12 = temp_f0; + this->cylinder.dim.height = -this->cylinder.dim.height; + } + + this->cylinder.dim.yShift = phi_f12 - this->actor.world.pos.y; + + if (this->stateFlags1 & PLAYER_STATE1_22) { + this->cylinder.dim.height = this->cylinder.dim.height * 0.8f; + } + + Collider_UpdateCylinder(&this->actor, &this->cylinder); + + if (!(this->stateFlags2 & PLAYER_STATE2_14)) { + if (!(this->stateFlags1 & (PLAYER_STATE1_7 | PLAYER_STATE1_13 | PLAYER_STATE1_14 | PLAYER_STATE1_23))) { + CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->cylinder.base); + } + + if (!(this->stateFlags1 & (PLAYER_STATE1_7 | PLAYER_STATE1_26)) && (this->invincibilityTimer <= 0)) { + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->cylinder.base); + + if (this->invincibilityTimer < 0) { + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->cylinder.base); + } + } + } + + AnimationContext_SetNextQueue(globalCtx); + } + + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + Math_Vec3f_Copy(&this->unk_A88, &this->bodyPartsPos[PLAYER_BODYPART_WAIST]); + + if (this->stateFlags1 & (PLAYER_STATE1_7 | PLAYER_STATE1_28 | PLAYER_STATE1_29)) { + this->actor.colChkInfo.mass = MASS_IMMOVABLE; + } + else { + this->actor.colChkInfo.mass = 50; + } + + this->stateFlags3 &= ~PLAYER_STATE3_2; + + Collider_ResetCylinderAC(globalCtx, &this->cylinder.base); + + Collider_ResetQuadAT(globalCtx, &this->swordQuads[0].base); + Collider_ResetQuadAT(globalCtx, &this->swordQuads[1].base); + + Collider_ResetQuadAC(globalCtx, &this->shieldQuad.base); + Collider_ResetQuadAT(globalCtx, &this->shieldQuad.base); +} + +static Vec3f D_80854838 = { 0.0f, 0.0f, -30.0f }; + +void Player_Update(Actor* thisx, GlobalContext* globalCtx) { + static Vec3f sDogSpawnPos; + Player* this = (Player*)thisx; + s32 dogParams; + s32 pad; + Input sp44; + Actor* dog; + + if (func_8084FCAC(this, globalCtx)) { + if (gSaveContext.dogParams < 0) { + if (Object_GetIndex(&globalCtx->objectCtx, OBJECT_DOG) < 0) { + gSaveContext.dogParams = 0; + } + else { + gSaveContext.dogParams &= 0x7FFF; + func_808395DC(this, &this->actor.world.pos, &D_80854838, &sDogSpawnPos); + dogParams = gSaveContext.dogParams; + + dog = Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_DOG, sDogSpawnPos.x, sDogSpawnPos.y, + sDogSpawnPos.z, 0, this->actor.shape.rot.y, 0, dogParams | 0x8000); + if (dog != NULL) { + dog->room = 0; + } + } + } + + if ((this->interactRangeActor != NULL) && (this->interactRangeActor->update == NULL)) { + this->interactRangeActor = NULL; + } + + if ((this->heldActor != NULL) && (this->heldActor->update == NULL)) { + func_808323B4(globalCtx, this); + } + + if (this->stateFlags1 & (PLAYER_STATE1_5 | PLAYER_STATE1_29)) { + memset(&sp44, 0, sizeof(sp44)); + } + else { + sp44 = globalCtx->state.input[0]; + if (this->unk_88E != 0) { + sp44.cur.button &= ~(BTN_A | BTN_B | BTN_CUP); + sp44.press.button &= ~(BTN_A | BTN_B | BTN_CUP); + } + } + + Player_UpdateCommon(this, globalCtx, &sp44); + } + + MREG(52) = this->actor.world.pos.x; + MREG(53) = this->actor.world.pos.y; + MREG(54) = this->actor.world.pos.z; + MREG(55) = this->actor.world.rot.y; +} + +static struct_80858AC8 D_80858AC8; +static Vec3s D_80858AD8[25]; + +static Gfx* sMaskDlists[PLAYER_MASK_MAX - 1] = { + gLinkChildKeatonMaskDL, gLinkChildSkullMaskDL, gLinkChildSpookyMaskDL, gLinkChildBunnyHoodDL, + gLinkChildGoronMaskDL, gLinkChildZoraMaskDL, gLinkChildGerudoMaskDL, gLinkChildMaskOfTruthDL, +}; + +static Vec3s D_80854864 = { 0, 0, 0 }; + +void Player_DrawGameplay(GlobalContext* globalCtx, Player* this, s32 lod, Gfx* cullDList, + OverrideLimbDrawOpa overrideLimbDraw) { + static s32 D_8085486C = 255; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player.c", 19228); + + gSPSegment(POLY_OPA_DISP++, 0x0C, cullDList); + gSPSegment(POLY_XLU_DISP++, 0x0C, cullDList); + + func_8008F470(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, lod, + this->currentTunic, this->currentBoots, this->actor.shape.face, overrideLimbDraw, + func_80090D20, this); + + if ((overrideLimbDraw == func_80090014) && (this->currentMask != PLAYER_MASK_NONE)) { + Mtx* sp70 = Graph_Alloc(globalCtx->state.gfxCtx, 2 * sizeof(Mtx)); + + if (this->currentMask == PLAYER_MASK_BUNNY) { + Vec3s sp68; + + gSPSegment(POLY_OPA_DISP++, 0x0B, sp70); + + sp68.x = D_80858AC8.unk_02 + 0x3E2; + sp68.y = D_80858AC8.unk_04 + 0xDBE; + sp68.z = D_80858AC8.unk_00 - 0x348A; + Matrix_SetTranslateRotateYXZ(97.0f, -1203.0f, -240.0f, &sp68); + Matrix_ToMtx(sp70++, "../z_player.c", 19273); + + sp68.x = D_80858AC8.unk_02 - 0x3E2; + sp68.y = -0xDBE - D_80858AC8.unk_04; + sp68.z = D_80858AC8.unk_00 - 0x348A; + Matrix_SetTranslateRotateYXZ(97.0f, -1203.0f, 240.0f, &sp68); + Matrix_ToMtx(sp70, "../z_player.c", 19279); + } + + gSPDisplayList(POLY_OPA_DISP++, sMaskDlists[this->currentMask - 1]); + } + + if ((this->currentBoots == PLAYER_BOOTS_HOVER) && !(this->actor.bgCheckFlags & 1) && + !(this->stateFlags1 & PLAYER_STATE1_23) && (this->hoverBootsTimer != 0)) { + s32 sp5C; + s32 hoverBootsTimer = this->hoverBootsTimer; + + if (this->hoverBootsTimer < 19) { + if (hoverBootsTimer >= 15) { + D_8085486C = (19 - hoverBootsTimer) * 51.0f; + } + else if (hoverBootsTimer < 19) { + sp5C = hoverBootsTimer; + + if (sp5C > 9) { + sp5C = 9; + } + + D_8085486C = (-sp5C * 4) + 36; + D_8085486C = D_8085486C * D_8085486C; + D_8085486C = (s32)((Math_CosS(D_8085486C) * 100.0f) + 100.0f) + 55.0f; + D_8085486C = D_8085486C * (sp5C * (1.0f / 9.0f)); + } + + Matrix_SetTranslateRotateYXZ(this->actor.world.pos.x, this->actor.world.pos.y + 2.0f, + this->actor.world.pos.z, &D_80854864); + Matrix_Scale(4.0f, 4.0f, 4.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_player.c", 19317), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 16, 32, 1, 0, + (globalCtx->gameplayFrames * -15) % 128, 16, 32)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 255, D_8085486C); + gDPSetEnvColor(POLY_XLU_DISP++, 120, 90, 30, 128); + gSPDisplayList(POLY_XLU_DISP++, gHoverBootsCircleDL); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player.c", 19328); +} + +void Player_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Player* this = (Player*)thisx; + + Vec3f pos; + Vec3s rot; + f32 scale; + + // OTRTODO: This is crashing randomly, so its temporarily been disabled + // return; + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + pos.x = 2.0f; + pos.y = -130.0f; + pos.z = -150.0f; + scale = 0.046f; + } else if (CUR_EQUIP_VALUE(EQUIP_SWORD) != 2) { + pos.x = 25.0f; + pos.y = -228.0f; + pos.z = 60.0f; + scale = 0.056f; + } else { + pos.x = 20.0f; + pos.y = -180.0f; + pos.z = -40.0f; + scale = 0.047f; + } + + rot.y = 32300; + rot.x = rot.z = 0; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_player.c", 19346); + + + if (!(this->stateFlags2 & PLAYER_STATE2_29)) { + OverrideLimbDrawOpa overrideLimbDraw = func_80090014; + s32 lod; + s32 pad; + + if ((this->csMode != 0) || (func_8008E9C4(this) && 0) || (this->actor.projectedPos.z < 160.0f)) { + lod = 0; + } + else { + lod = 1; + } + + if (CVar_GetS32("gDisableLOD", 0) != 0) + lod = 0; + + func_80093C80(globalCtx); + func_80093D84(globalCtx->state.gfxCtx); + + if (this->invincibilityTimer > 0) { + this->unk_88F += CLAMP(50 - this->invincibilityTimer, 8, 40); + POLY_OPA_DISP = + Gfx_SetFog2(POLY_OPA_DISP, 255, 0, 0, 0, 0, 4000 - (s32)(Math_CosS(this->unk_88F * 256) * 2000.0f)); + } + + func_8002EBCC(&this->actor, globalCtx, 0); + func_8002ED80(&this->actor, globalCtx, 0); + + if (this->unk_6AD != 0) { + Vec3f projectedHeadPos; + + SkinMatrix_Vec3fMtxFMultXYZ(&globalCtx->viewProjectionMtxF, &this->actor.focus.pos, &projectedHeadPos); + if (projectedHeadPos.z < -4.0f) { + overrideLimbDraw = func_800902F0; + } + } + else if (this->stateFlags2 & PLAYER_STATE2_18) { + if (this->actor.projectedPos.z < 0.0f) { + overrideLimbDraw = func_80090440; + } + } + + if (this->stateFlags2 & PLAYER_STATE2_26) { + f32 sp78 = ((u16)(globalCtx->gameplayFrames * 600) * M_PI) / 0x8000; + f32 sp74 = ((u16)(globalCtx->gameplayFrames * 1000) * M_PI) / 0x8000; + + Matrix_Push(); + this->actor.scale.y = -this->actor.scale.y; + Matrix_SetTranslateRotateYXZ( + this->actor.world.pos.x, + (this->actor.floorHeight + (this->actor.floorHeight - this->actor.world.pos.y)) + + (this->actor.shape.yOffset * this->actor.scale.y), + this->actor.world.pos.z, &this->actor.shape.rot); + Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY); + Matrix_RotateX(sp78, MTXMODE_APPLY); + Matrix_RotateY(sp74, MTXMODE_APPLY); + Matrix_Scale(1.1f, 0.95f, 1.05f, MTXMODE_APPLY); + Matrix_RotateY(-sp74, MTXMODE_APPLY); + Matrix_RotateX(-sp78, MTXMODE_APPLY); + Player_DrawGameplay(globalCtx, this, lod, gCullFrontDList, overrideLimbDraw); + this->actor.scale.y = -this->actor.scale.y; + Matrix_Pop(); + } + + gSPClearGeometryMode(POLY_OPA_DISP++, G_CULL_BOTH); + gSPClearGeometryMode(POLY_XLU_DISP++, G_CULL_BOTH); + + Player_DrawGameplay(globalCtx, this, lod, gCullBackDList, overrideLimbDraw); + + if (this->invincibilityTimer > 0) { + POLY_OPA_DISP = Gameplay_SetFog(globalCtx, POLY_OPA_DISP); + } + + if (this->stateFlags2 & PLAYER_STATE2_14) { + f32 scale = (this->unk_84F >> 1) * 22.0f; + + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (0 - globalCtx->gameplayFrames) % 128, 32, 32, 1, + 0, (globalCtx->gameplayFrames * -2) % 128, 32, 32)); + + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_player.c", 19459), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 50, 100, 255); + gSPDisplayList(POLY_XLU_DISP++, gEffIceFragment3DL); + } + + if (this->unk_862 > 0) { + Player_DrawGetItem(globalCtx, this); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_player.c", 19473); +} + +void Player_Destroy(Actor* thisx, GlobalContext* globalCtx) { + Player* this = (Player*)thisx; + + Effect_Delete(globalCtx, this->swordEffectIndex); + + Collider_DestroyCylinder(globalCtx, &this->cylinder); + Collider_DestroyQuad(globalCtx, &this->swordQuads[0]); + Collider_DestroyQuad(globalCtx, &this->swordQuads[1]); + Collider_DestroyQuad(globalCtx, &this->shieldQuad); + + func_800876C8(globalCtx); + + gSaveContext.linkAge = globalCtx->linkAgeOnLoad; +} + +s16 func_8084ABD8(GlobalContext* globalCtx, Player* this, s32 arg2, s16 arg3) { + s32 temp1; + s16 temp2; + s16 temp3; + + if (!func_8002DD78(this) && !func_808334B4(this) && (arg2 == 0)) { + temp2 = sControlInput->rel.stick_y * 240.0f; + Math_SmoothStepToS(&this->actor.focus.rot.x, temp2, 14, 4000, 30); + + temp2 = sControlInput->rel.stick_x * -16.0f; + temp2 = CLAMP(temp2, -3000, 3000); + this->actor.focus.rot.y += temp2; + } + else { + temp1 = (this->stateFlags1 & PLAYER_STATE1_23) ? 3500 : 14000; + temp3 = ((sControlInput->rel.stick_y >= 0) ? 1 : -1) * + (s32)((1.0f - Math_CosS(sControlInput->rel.stick_y * 200)) * 1500.0f); + this->actor.focus.rot.x += temp3; + + if (fabsf(sControlInput->cur.gyro_x) > 0.01f) { + this->actor.focus.rot.x -= (sControlInput->cur.gyro_x) * 750.0f; + } + + this->actor.focus.rot.x = CLAMP(this->actor.focus.rot.x, -temp1, temp1); + + temp1 = 19114; + temp2 = this->actor.focus.rot.y - this->actor.shape.rot.y; + temp3 = ((sControlInput->rel.stick_x >= 0) ? 1 : -1) * + (s32)((1.0f - Math_CosS(sControlInput->rel.stick_x * 200)) * -1500.0f); + temp2 += temp3; + + this->actor.focus.rot.y = CLAMP(temp2, -temp1, temp1) + this->actor.shape.rot.y; + + if (fabsf(sControlInput->cur.gyro_y) > 0.01f) { + this->actor.focus.rot.y += (sControlInput->cur.gyro_y) * 750.0f; + } + } + + this->unk_6AE |= 2; + return func_80836AB8(this, (globalCtx->shootingGalleryStatus != 0) || func_8002DD78(this) || func_808334B4(this)) - + arg3; +} + +void func_8084AEEC(Player* this, f32* arg1, f32 arg2, s16 arg3) { + f32 temp1; + f32 temp2; + + temp1 = this->skelAnime.curFrame - 10.0f; + + temp2 = (R_RUN_SPEED_LIMIT / 100.0f) * 0.8f; + if (*arg1 > temp2) { + *arg1 = temp2; + } + + if ((0.0f < temp1) && (temp1 < 10.0f)) { + temp1 *= 6.0f; + } + else { + temp1 = 0.0f; + arg2 = 0.0f; + } + + Math_AsymStepToF(arg1, arg2 * 0.8f, temp1, (fabsf(*arg1) * 0.02f) + 0.05f); + Math_ScaledStepToS(&this->currentYaw, arg3, 1600); +} + +void func_8084B000(Player* this) { + f32 phi_f18; + f32 phi_f16; + f32 phi_f14; + f32 yDistToWater; + + phi_f14 = -5.0f; + + phi_f16 = this->ageProperties->unk_28; + if (this->actor.velocity.y < 0.0f) { + phi_f16 += 1.0f; + } + + if (this->actor.yDistToWater < phi_f16) { + if (this->actor.velocity.y <= 0.0f) { + phi_f16 = 0.0f; + } + else { + phi_f16 = this->actor.velocity.y * 0.5f; + } + phi_f18 = -0.1f - phi_f16; + } + else { + if (!(this->stateFlags1 & PLAYER_STATE1_7) && (this->currentBoots == PLAYER_BOOTS_IRON) && + (this->actor.velocity.y >= -3.0f)) { + phi_f18 = -0.2f; + } + else { + phi_f14 = 2.0f; + if (this->actor.velocity.y >= 0.0f) { + phi_f16 = 0.0f; + } + else { + phi_f16 = this->actor.velocity.y * -0.3f; + } + phi_f18 = phi_f16 + 0.1f; + } + + yDistToWater = this->actor.yDistToWater; + if (yDistToWater > 100.0f) { + this->stateFlags2 |= PLAYER_STATE2_10; + } + } + + this->actor.velocity.y += phi_f18; + + if (((this->actor.velocity.y - phi_f14) * phi_f18) > 0) { + this->actor.velocity.y = phi_f14; + } + + this->actor.gravity = 0.0f; +} + +void func_8084B158(GlobalContext* globalCtx, Player* this, Input* input, f32 arg3) { + f32 temp; + + if ((input != NULL) && CHECK_BTN_ANY(input->press.button, BTN_A | BTN_B)) { + temp = 1.0f; + } + else { + temp = 0.5f; + } + + temp *= arg3; + + if (temp < 1.0f) { + temp = 1.0f; + } + + this->skelAnime.playSpeed = temp; + LinkAnimation_Update(globalCtx, &this->skelAnime); +} + +void func_8084B1D8(Player* this, GlobalContext* globalCtx) { + if (this->stateFlags1 & PLAYER_STATE1_27) { + func_8084B000(this); + func_8084AEEC(this, &this->linearVelocity, 0, this->actor.shape.rot.y); + } + else { + func_8083721C(this); + } + + if ((this->unk_6AD == 2) && (func_8002DD6C(this) || func_808332E4(this))) { + func_80836670(this, globalCtx); + } + + if ((this->csMode != 0) || (this->unk_6AD == 0) || (this->unk_6AD >= 4) || func_80833B54(this) || + (this->unk_664 != NULL) || !func_8083AD4C(globalCtx, this) || + (((this->unk_6AD == 2) && (CHECK_BTN_ANY(sControlInput->press.button, BTN_A | BTN_B | BTN_R) || + func_80833B2C(this) || (!func_8002DD78(this) && !func_808334B4(this)))) || + ((this->unk_6AD == 1) && + CHECK_BTN_ANY(sControlInput->press.button, + BTN_A | BTN_B | BTN_R | BTN_CUP | BTN_CLEFT | BTN_CRIGHT | BTN_CDOWN)))) { + func_8083C148(this, globalCtx); + func_80078884(NA_SE_SY_CAMERA_ZOOM_UP); + } + else if ((DECR(this->unk_850) == 0) || (this->unk_6AD != 2)) { + if (func_8008F128(this)) { + this->unk_6AE |= 0x43; + } + else { + this->actor.shape.rot.y = func_8084ABD8(globalCtx, this, 0, 0); + } + } + + this->currentYaw = this->actor.shape.rot.y; +} + +s32 func_8084B3CC(GlobalContext* globalCtx, Player* this) { + if (globalCtx->shootingGalleryStatus != 0) { + func_80832564(globalCtx, this); + func_80835C58(globalCtx, this, func_8084FA54, 0); + + if (!func_8002DD6C(this) || Player_HoldsHookshot(this)) { + func_80835F44(globalCtx, this, 3); + } + + this->stateFlags1 |= PLAYER_STATE1_20; + func_80832264(globalCtx, this, func_80833338(this)); + func_80832210(this); + func_8083B010(this); + return 1; + } + + return 0; +} + +void func_8084B498(Player* this) { + this->itemActionParam = + (INV_CONTENT(ITEM_OCARINA_FAIRY) == ITEM_OCARINA_FAIRY) ? PLAYER_AP_OCARINA_FAIRY : PLAYER_AP_OCARINA_TIME; +} + +s32 func_8084B4D4(GlobalContext* globalCtx, Player* this) { + if (this->stateFlags3 & PLAYER_STATE3_5) { + this->stateFlags3 &= ~PLAYER_STATE3_5; + func_8084B498(this); + this->unk_6AD = 4; + func_8083B040(this, globalCtx); + return 1; + } + + return 0; +} + +void func_8084B530(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5; + func_80836670(this, globalCtx); + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actor.flags &= ~ACTOR_FLAG_8; + + if (!CHECK_FLAG_ALL(this->targetActor->flags, ACTOR_FLAG_0 | ACTOR_FLAG_2)) { + this->stateFlags2 &= ~PLAYER_STATE2_13; + } + + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + + if (!func_8084B4D4(globalCtx, this) && !func_8084B3CC(globalCtx, this) && !func_8083ADD4(globalCtx, this)) { + if ((this->targetActor != this->interactRangeActor) || !func_8083E5A8(this, globalCtx)) { + if (this->stateFlags1 & PLAYER_STATE1_23) { + s32 sp24 = this->unk_850; + func_8083A360(globalCtx, this); + this->unk_850 = sp24; + } + else if (func_808332B8(this)) { + func_80838F18(globalCtx, this); + } + else { + func_80853080(this, globalCtx); + } + } + } + + this->unk_88E = 10; + return; + } + + if (this->stateFlags1 & PLAYER_STATE1_23) { + func_8084CC98(this, globalCtx); + } + else if (func_808332B8(this)) { + func_8084D610(this, globalCtx); + } + else if (!func_8008E9C4(this) && LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->skelAnime.moveFlags != 0) { + func_80832DBC(this); + if ((this->targetActor->category == ACTORCAT_NPC) && + (this->heldItemActionParam != PLAYER_AP_FISHING_POLE)) { + func_808322D0(globalCtx, this, &gPlayerAnim_0031A0); + } + else { + func_80832284(globalCtx, this, func_80833338(this)); + } + } + else { + func_808322A4(globalCtx, this, &gPlayerAnim_0031A8); + } + } + + if (this->unk_664 != NULL) { + this->currentYaw = this->actor.shape.rot.y = func_8083DB98(this, 0); + } +} + +void func_8084B78C(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + s32 temp; + + this->stateFlags2 |= PLAYER_STATE2_0 | PLAYER_STATE2_6 | PLAYER_STATE2_8; + func_8083F524(globalCtx, this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (!func_8083F9D0(globalCtx, this)) { + func_80837268(this, &sp34, &sp32, 0.0f, globalCtx); + temp = func_8083FFB8(this, &sp34, &sp32); + if (temp > 0) { + func_8083FAB8(this, globalCtx); + } + else if (temp < 0) { + func_8083FB14(this, globalCtx); + } + } + } +} + +void func_8084B840(GlobalContext* globalCtx, Player* this, f32 arg2) { + if (this->actor.wallBgId != BGCHECK_SCENE) { + DynaPolyActor* dynaPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.wallBgId); + + if (dynaPolyActor != NULL) { + func_8002DFA4(dynaPolyActor, arg2, this->actor.world.rot.y); + } + } +} + +static struct_80832924 D_80854870[] = { + { NA_SE_PL_SLIP, 0x1003 }, + { NA_SE_PL_SLIP, -0x1015 }, +}; + +void func_8084B898(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + s32 temp; + + this->stateFlags2 |= PLAYER_STATE2_0 | PLAYER_STATE2_6 | PLAYER_STATE2_8; + + if (func_80832CB0(globalCtx, this, &gPlayerAnim_003108)) { + this->unk_850 = 1; + } + else if (this->unk_850 == 0) { + if (LinkAnimation_OnFrame(&this->skelAnime, 11.0f)) { + func_80832698(this, NA_SE_VO_LI_PUSH); + } + } + + func_80832924(this, D_80854870); + func_8083F524(globalCtx, this); + + if (!func_8083F9D0(globalCtx, this)) { + func_80837268(this, &sp34, &sp32, 0.0f, globalCtx); + temp = func_8083FFB8(this, &sp34, &sp32); + if (temp < 0) { + func_8083FB14(this, globalCtx); + } + else if (temp == 0) { + func_8083F72C(this, &gPlayerAnim_0030E0, globalCtx); + } + else { + this->stateFlags2 |= PLAYER_STATE2_4; + } + } + + if (this->stateFlags2 & PLAYER_STATE2_4) { + func_8084B840(globalCtx, this, 2.0f); + this->linearVelocity = 2.0f; + } +} + +static struct_80832924 D_80854878[] = { + { NA_SE_PL_SLIP, 0x1004 }, + { NA_SE_PL_SLIP, -0x1018 }, +}; + +static Vec3f D_80854880 = { 0.0f, 26.0f, -40.0f }; + +void func_8084B9E4(Player* this, GlobalContext* globalCtx) { + LinkAnimationHeader* anim; + f32 sp70; + s16 sp6E; + s32 temp1; + Vec3f sp5C; + f32 temp2; + CollisionPoly* sp54; + s32 sp50; + Vec3f sp44; + Vec3f sp38; + + anim = D_80853914[PLAYER_ANIMGROUP_36][this->modelAnimType]; + this->stateFlags2 |= PLAYER_STATE2_0 | PLAYER_STATE2_6 | PLAYER_STATE2_8; + + if (func_80832CB0(globalCtx, this, anim)) { + this->unk_850 = 1; + } + else { + if (this->unk_850 == 0) { + if (LinkAnimation_OnFrame(&this->skelAnime, 11.0f)) { + func_80832698(this, NA_SE_VO_LI_PUSH); + } + } + else { + func_80832924(this, D_80854878); + } + } + + func_8083F524(globalCtx, this); + + if (!func_8083F9D0(globalCtx, this)) { + func_80837268(this, &sp70, &sp6E, 0.0f, globalCtx); + temp1 = func_8083FFB8(this, &sp70, &sp6E); + if (temp1 > 0) { + func_8083FAB8(this, globalCtx); + } + else if (temp1 == 0) { + func_8083F72C(this, D_80853914[PLAYER_ANIMGROUP_37][this->modelAnimType], globalCtx); + } + else { + this->stateFlags2 |= PLAYER_STATE2_4; + } + } + + if (this->stateFlags2 & PLAYER_STATE2_4) { + temp2 = func_8083973C(globalCtx, this, &D_80854880, &sp5C) - this->actor.world.pos.y; + if (fabsf(temp2) < 20.0f) { + sp44.x = this->actor.world.pos.x; + sp44.z = this->actor.world.pos.z; + sp44.y = sp5C.y; + if (!BgCheck_EntityLineTest1(&globalCtx->colCtx, &sp44, &sp5C, &sp38, &sp54, true, false, false, true, + &sp50)) { + func_8084B840(globalCtx, this, -2.0f); + return; + } + } + this->stateFlags2 &= ~PLAYER_STATE2_4; + } +} + +void func_8084BBE4(Player* this, GlobalContext* globalCtx) { + f32 sp3C; + s16 sp3A; + LinkAnimationHeader* anim; + f32 temp; + + this->stateFlags2 |= PLAYER_STATE2_6; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + // clang-format off + anim = (this->unk_84F > 0) ? &gPlayerAnim_002F28 : D_80853914[PLAYER_ANIMGROUP_40][this->modelAnimType]; func_80832284(globalCtx, this, anim); + // clang-format on + } + else if (this->unk_84F == 0) { + if (this->skelAnime.animation == &gPlayerAnim_002F10) { + temp = 11.0f; + } + else { + temp = 1.0f; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, temp)) { + func_80832770(this, NA_SE_PL_WALK_GROUND); + if (this->skelAnime.animation == &gPlayerAnim_002F10) { + this->unk_84F = 1; + } + else { + this->unk_84F = -1; + } + } + } + + Math_ScaledStepToS(&this->actor.shape.rot.y, this->currentYaw, 0x800); + + if (this->unk_84F != 0) { + func_80837268(this, &sp3C, &sp3A, 0.0f, globalCtx); + if (this->unk_847[this->unk_846] >= 0) { + if (this->unk_84F > 0) { + anim = D_80853914[PLAYER_ANIMGROUP_38][this->modelAnimType]; + } + else { + anim = D_80853914[PLAYER_ANIMGROUP_41][this->modelAnimType]; + } + func_8083A9B8(this, anim, globalCtx); + return; + } + + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_A) || (this->actor.shape.feetFloorFlags != 0)) { + func_80837B60(this); + if (this->unk_84F < 0) { + this->linearVelocity = -0.8f; + } + else { + this->linearVelocity = 0.8f; + } + func_80837B9C(this, globalCtx); + this->stateFlags1 &= ~(PLAYER_STATE1_13 | PLAYER_STATE1_14); + } + } +} + +void func_8084BDFC(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_6; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832E48(this, 1); + func_8083C0E8(this, globalCtx); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, this->skelAnime.endFrame - 6.0f)) { + func_808328A0(this); + } + else if (LinkAnimation_OnFrame(&this->skelAnime, this->skelAnime.endFrame - 34.0f)) { + this->stateFlags1 &= ~(PLAYER_STATE1_13 | PLAYER_STATE1_14); + func_8002F7DC(&this->actor, NA_SE_PL_CLIMB_CLIFF); + func_80832698(this, NA_SE_VO_LI_CLIMB_END); + } +} + +void func_8084BEE4(Player* this) { + func_8002F7DC(&this->actor, (this->unk_84F != 0) ? NA_SE_PL_WALK_WALL : NA_SE_PL_WALK_LADDER); +} + +void func_8084BF1C(Player* this, GlobalContext* globalCtx) { + static Vec3f D_8085488C = { 0.0f, 0.0f, 26.0f }; + s32 sp84; + s32 sp80; + f32 phi_f0; + f32 phi_f2; + Vec3f sp6C; + s32 sp68; + Vec3f sp5C; + f32 temp_f0; + LinkAnimationHeader* anim1; + LinkAnimationHeader* anim2; + + sp84 = sControlInput->rel.stick_y; + sp80 = sControlInput->rel.stick_x; + + this->fallStartHeight = this->actor.world.pos.y; + this->stateFlags2 |= PLAYER_STATE2_6; + + if ((this->unk_84F != 0) && (ABS(sp84) < ABS(sp80))) { + phi_f0 = ABS(sp80) * 0.0325f; + sp84 = 0; + } + else { + phi_f0 = ABS(sp84) * 0.05f; + sp80 = 0; + } + + if (phi_f0 < 1.0f) { + phi_f0 = 1.0f; + } + else if (phi_f0 > 3.35f) { + phi_f0 = 3.35f; + } + + if (this->skelAnime.playSpeed >= 0.0f) { + phi_f2 = 1.0f; + } + else { + phi_f2 = -1.0f; + } + + this->skelAnime.playSpeed = phi_f2 * phi_f0; + + if (this->unk_850 >= 0) { + if ((this->actor.wallPoly != NULL) && (this->actor.wallBgId != BGCHECK_SCENE)) { + DynaPolyActor* wallPolyActor = DynaPoly_GetActor(&globalCtx->colCtx, this->actor.wallBgId); + if (wallPolyActor != NULL) { + Math_Vec3f_Diff(&wallPolyActor->actor.world.pos, &wallPolyActor->actor.prevPos, &sp6C); + Math_Vec3f_Sum(&this->actor.world.pos, &sp6C, &this->actor.world.pos); + } + } + + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 26.0f, 6.0f, this->ageProperties->unk_00, 7); + func_8083F360(globalCtx, this, 26.0f, this->ageProperties->unk_3C, 50.0f, -20.0f); + } + + if ((this->unk_850 < 0) || !func_8083FBC0(this, globalCtx)) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime) != 0) { + if (this->unk_850 < 0) { + this->unk_850 = ABS(this->unk_850) & 1; + return; + } + + if (sp84 != 0) { + sp68 = this->unk_84F + this->unk_850; + + if (sp84 > 0) { + D_8085488C.y = this->ageProperties->unk_40; + temp_f0 = func_8083973C(globalCtx, this, &D_8085488C, &sp5C); + + if (this->actor.world.pos.y < temp_f0) { + if (this->unk_84F != 0) { + this->actor.world.pos.y = temp_f0; + this->stateFlags1 &= ~PLAYER_STATE1_21; + func_8083A5C4(globalCtx, this, this->actor.wallPoly, this->ageProperties->unk_3C, + &gPlayerAnim_003000); + this->currentYaw += 0x8000; + this->actor.shape.rot.y = this->currentYaw; + func_8083A9B8(this, &gPlayerAnim_003000, globalCtx); + this->stateFlags1 |= PLAYER_STATE1_14; + } + else { + func_8083F070(this, this->ageProperties->unk_CC[this->unk_850], globalCtx); + } + } + else { + this->skelAnime.prevTransl = this->ageProperties->unk_4A[sp68]; + func_80832264(globalCtx, this, this->ageProperties->unk_AC[sp68]); + } + } + else { + if ((this->actor.world.pos.y - this->actor.floorHeight) < 15.0f) { + if (this->unk_84F != 0) { + func_8083FB7C(this, globalCtx); + } + else { + if (this->unk_850 != 0) { + this->skelAnime.prevTransl = this->ageProperties->unk_44; + } + func_8083F070(this, this->ageProperties->unk_C4[this->unk_850], globalCtx); + this->unk_850 = 1; + } + } + else { + sp68 ^= 1; + this->skelAnime.prevTransl = this->ageProperties->unk_62[sp68]; + anim1 = this->ageProperties->unk_AC[sp68]; + LinkAnimation_Change(globalCtx, &this->skelAnime, anim1, -1.0f, Animation_GetLastFrame(anim1), + 0.0f, ANIMMODE_ONCE, 0.0f); + } + } + this->unk_850 ^= 1; + } + else { + if ((this->unk_84F != 0) && (sp80 != 0)) { + anim2 = this->ageProperties->unk_BC[this->unk_850]; + + if (sp80 > 0) { + this->skelAnime.prevTransl = this->ageProperties->unk_7A[this->unk_850]; + func_80832264(globalCtx, this, anim2); + } + else { + this->skelAnime.prevTransl = this->ageProperties->unk_86[this->unk_850]; + LinkAnimation_Change(globalCtx, &this->skelAnime, anim2, -1.0f, Animation_GetLastFrame(anim2), + 0.0f, ANIMMODE_ONCE, 0.0f); + } + } + else { + this->stateFlags2 |= PLAYER_STATE2_12; + } + } + + return; + } + } + + if (this->unk_850 < 0) { + if (((this->unk_850 == -2) && + (LinkAnimation_OnFrame(&this->skelAnime, 14.0f) || LinkAnimation_OnFrame(&this->skelAnime, 29.0f))) || + ((this->unk_850 == -4) && + (LinkAnimation_OnFrame(&this->skelAnime, 22.0f) || LinkAnimation_OnFrame(&this->skelAnime, 35.0f) || + LinkAnimation_OnFrame(&this->skelAnime, 49.0f) || LinkAnimation_OnFrame(&this->skelAnime, 55.0f)))) { + func_8084BEE4(this); + } + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, (this->skelAnime.playSpeed > 0.0f) ? 20.0f : 0.0f)) { + func_8084BEE4(this); + } +} + +static f32 D_80854898[] = { 10.0f, 20.0f }; +static f32 D_808548A0[] = { 40.0f, 50.0f }; + +static struct_80832924 D_808548A8[] = { + { NA_SE_PL_WALK_LADDER, 0x80A }, + { NA_SE_PL_WALK_LADDER, 0x814 }, + { NA_SE_PL_WALK_LADDER, -0x81E }, +}; + +void func_8084C5F8(Player* this, GlobalContext* globalCtx) { + s32 temp; + f32* sp38; + CollisionPoly* sp34; + s32 sp30; + Vec3f sp24; + + this->stateFlags2 |= PLAYER_STATE2_6; + + temp = func_808374A0(globalCtx, this, &this->skelAnime, 4.0f); + + if (temp == 0) { + this->stateFlags1 &= ~PLAYER_STATE1_21; + return; + } + + if ((temp > 0) || LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083C0E8(this, globalCtx); + this->stateFlags1 &= ~PLAYER_STATE1_21; + return; + } + + sp38 = D_80854898; + + if (this->unk_850 != 0) { + func_80832924(this, D_808548A8); + sp38 = D_808548A0; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, sp38[0]) || LinkAnimation_OnFrame(&this->skelAnime, sp38[1])) { + sp24.x = this->actor.world.pos.x; + sp24.y = this->actor.world.pos.y + 20.0f; + sp24.z = this->actor.world.pos.z; + if (BgCheck_EntityRaycastFloor3(&globalCtx->colCtx, &sp34, &sp30, &sp24) != 0.0f) { + this->unk_89E = func_80041F10(&globalCtx->colCtx, sp34, sp30); + func_808328A0(this); + } + } +} + +static struct_80832924 D_808548B4[] = { + { 0, 0x3028 }, { 0, 0x3030 }, { 0, 0x3038 }, { 0, 0x3040 }, { 0, 0x3048 }, + { 0, 0x3050 }, { 0, 0x3058 }, { 0, 0x3060 }, { 0, -0x3068 }, +}; + +void func_8084C760(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_6; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (!(this->stateFlags1 & PLAYER_STATE1_0)) { + if (this->skelAnime.moveFlags != 0) { + this->skelAnime.moveFlags = 0; + return; + } + + if (!func_8083F570(this, globalCtx)) { + this->linearVelocity = sControlInput->rel.stick_y * 0.03f; + } + } + return; + } + + func_80832924(this, D_808548B4); +} + +static struct_80832924 D_808548D8[] = { + { 0, 0x300A }, { 0, 0x3012 }, { 0, 0x301A }, { 0, 0x3022 }, { 0, 0x3034 }, + { 0, 0x303C }, { 0, 0x3044 }, { 0, 0x304C }, { 0, -0x3054 }, +}; + +void func_8084C81C(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_6; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083C0E8(this, globalCtx); + this->stateFlags2 &= ~PLAYER_STATE2_18; + return; + } + + func_80832924(this, D_808548D8); +} + +static Vec3f D_808548FC[] = { + { 40.0f, 0.0f, 0.0f }, + { -40.0f, 0.0f, 0.0f }, +}; + +static Vec3f D_80854914[] = { + { 60.0f, 20.0f, 0.0f }, + { -60.0f, 20.0f, 0.0f }, +}; + +static Vec3f D_8085492C[] = { + { 60.0f, -20.0f, 0.0f }, + { -60.0f, -20.0f, 0.0f }, +}; + +s32 func_8084C89C(GlobalContext* globalCtx, Player* this, s32 arg2, f32* arg3) { + EnHorse* rideActor = (EnHorse*)this->rideActor; + f32 sp50; + f32 sp4C; + Vec3f sp40; + Vec3f sp34; + CollisionPoly* sp30; + s32 sp2C; + + sp50 = rideActor->actor.world.pos.y + 20.0f; + sp4C = rideActor->actor.world.pos.y - 20.0f; + + *arg3 = func_8083973C(globalCtx, this, &D_808548FC[arg2], &sp40); + + return (sp4C < *arg3) && (*arg3 < sp50) && + !func_80839768(globalCtx, this, &D_80854914[arg2], &sp30, &sp2C, &sp34) && + !func_80839768(globalCtx, this, &D_8085492C[arg2], &sp30, &sp2C, &sp34); +} + +s32 func_8084C9BC(Player* this, GlobalContext* globalCtx) { + EnHorse* rideActor = (EnHorse*)this->rideActor; + s32 sp38; + f32 sp34; + + if (this->unk_850 < 0) { + this->unk_850 = 99; + } + else { + sp38 = (this->mountSide < 0) ? 0 : 1; + if (!func_8084C89C(globalCtx, this, sp38, &sp34)) { + sp38 ^= 1; + if (!func_8084C89C(globalCtx, this, sp38, &sp34)) { + return 0; + } + else { + this->mountSide = -this->mountSide; + } + } + + if ((globalCtx->csCtx.state == CS_STATE_IDLE) && (globalCtx->transitionMode == 0) && + (EN_HORSE_CHECK_1(rideActor) || EN_HORSE_CHECK_4(rideActor))) { + this->stateFlags2 |= PLAYER_STATE2_22; + + if (EN_HORSE_CHECK_1(rideActor) || + (EN_HORSE_CHECK_4(rideActor) && CHECK_BTN_ALL(sControlInput->press.button, BTN_A))) { + rideActor->actor.child = NULL; + func_80835DAC(globalCtx, this, func_8084D3E4, 0); + this->unk_878 = sp34 - rideActor->actor.world.pos.y; + func_80832264(globalCtx, this, (this->mountSide < 0) ? &gPlayerAnim_003390 : &gPlayerAnim_0033A0); + return 1; + } + } + } + + return 0; +} + +void func_8084CBF4(Player* this, f32 arg1, f32 arg2) { + f32 temp; + f32 dir; + + if ((this->unk_878 != 0.0f) && (arg2 <= this->skelAnime.curFrame)) { + if (arg1 < fabsf(this->unk_878)) { + if (this->unk_878 >= 0.0f) { + dir = 1; + } + else { + dir = -1; + } + temp = dir * arg1; + } + else { + temp = this->unk_878; + } + this->actor.world.pos.y += temp; + this->unk_878 -= temp; + } +} + +static LinkAnimationHeader* D_80854944[] = { + &gPlayerAnim_003370, + &gPlayerAnim_003368, + &gPlayerAnim_003380, + &gPlayerAnim_003358, + &gPlayerAnim_003338, + &gPlayerAnim_003348, + &gPlayerAnim_003350, + NULL, + NULL, +}; + +static LinkAnimationHeader* D_80854968[] = { + &gPlayerAnim_003388, + &gPlayerAnim_003388, + &gPlayerAnim_003388, + &gPlayerAnim_003360, + &gPlayerAnim_003340, + &gPlayerAnim_003340, + &gPlayerAnim_003340, + NULL, + NULL, +}; + +static LinkAnimationHeader* D_8085498C[] = { + &gPlayerAnim_0033C8, + &gPlayerAnim_0033B8, + &gPlayerAnim_0033C0, +}; + +static u8 D_80854998[2][2] = { + { 32, 58 }, + { 25, 42 }, +}; + +static Vec3s D_8085499C = { -69, 7146, -266 }; + +static struct_80832924 D_808549A4[] = { + { NA_SE_PL_CALM_HIT, 0x830 }, { NA_SE_PL_CALM_HIT, 0x83A }, { NA_SE_PL_CALM_HIT, 0x844 }, + { NA_SE_PL_CALM_PAT, 0x85C }, { NA_SE_PL_CALM_PAT, 0x86E }, { NA_SE_PL_CALM_PAT, 0x87E }, + { NA_SE_PL_CALM_PAT, 0x884 }, { NA_SE_PL_CALM_PAT, -0x888 }, +}; + +void func_8084CC98(Player* this, GlobalContext* globalCtx) { + EnHorse* rideActor = (EnHorse*)this->rideActor; + u8* arr; + + this->stateFlags2 |= PLAYER_STATE2_6; + + func_8084CBF4(this, 1.0f, 10.0f); + + if (this->unk_850 == 0) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + this->skelAnime.animation = &gPlayerAnim_0033B8; + this->unk_850 = 99; + return; + } + + arr = D_80854998[(this->mountSide < 0) ? 0 : 1]; + + if (LinkAnimation_OnFrame(&this->skelAnime, arr[0])) { + func_8002F7DC(&this->actor, NA_SE_PL_CLIMB_CLIFF); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, arr[1])) { + func_8002DE74(globalCtx, this); + func_8002F7DC(&this->actor, NA_SE_PL_SIT_ON_HORSE); + return; + } + + return; + } + + func_8002DE74(globalCtx, this); + this->skelAnime.prevTransl = D_8085499C; + + if ((rideActor->animationIdx != this->unk_850) && ((rideActor->animationIdx >= 2) || (this->unk_850 >= 2))) { + if ((this->unk_850 = rideActor->animationIdx) < 2) { + f32 rand = Rand_ZeroOne(); + s32 temp = 0; + + this->unk_850 = 1; + + if (rand < 0.1f) { + temp = 2; + } + else if (rand < 0.2f) { + temp = 1; + } + func_80832264(globalCtx, this, D_8085498C[temp]); + } + else { + this->skelAnime.animation = D_80854944[this->unk_850 - 2]; + Animation_SetMorph(globalCtx, &this->skelAnime, 8.0f); + if (this->unk_850 < 4) { + func_80834644(globalCtx, this); + this->unk_84F = 0; + } + } + } + + if (this->unk_850 == 1) { + if ((D_808535E0 != 0) || func_8083224C(globalCtx)) { + func_80832264(globalCtx, this, &gPlayerAnim_0033C8); + } + else if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + this->unk_850 = 99; + } + else if (this->skelAnime.animation == &gPlayerAnim_0033B8) { + func_80832924(this, D_808549A4); + } + } + else { + this->skelAnime.curFrame = rideActor->curFrame; + LinkAnimation_AnimateFrame(globalCtx, &this->skelAnime); + } + + AnimationContext_SetCopyAll(globalCtx, this->skelAnime.limbCount, this->skelAnime.morphTable, + this->skelAnime.jointTable); + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) || (this->csMode != 0)) { + if (this->csMode == 7) { + this->csMode = 0; + } + this->unk_6AD = 0; + this->unk_84F = 0; + } + else if ((this->unk_850 < 2) || (this->unk_850 >= 4)) { + D_808535E0 = func_80836670(this, globalCtx); + if (D_808535E0 != 0) { + this->unk_84F = 0; + } + } + + this->actor.world.pos.x = rideActor->actor.world.pos.x + rideActor->riderPos.x; + this->actor.world.pos.y = (rideActor->actor.world.pos.y + rideActor->riderPos.y) - 27.0f; + this->actor.world.pos.z = rideActor->actor.world.pos.z + rideActor->riderPos.z; + + this->currentYaw = this->actor.shape.rot.y = rideActor->actor.shape.rot.y; + + if ((this->csMode != 0) || + (!func_8083224C(globalCtx) && ((rideActor->actor.speedXZ != 0.0f) || !func_8083B644(this, globalCtx)) && + !func_8083C1DC(this, globalCtx))) { + if (D_808535E0 == 0) { + if (this->unk_84F != 0) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime2)) { + rideActor->stateFlags &= ~ENHORSE_FLAG_8; + this->unk_84F = 0; + } + + if (this->skelAnime2.animation == &gPlayerAnim_0033B0) { + if (LinkAnimation_OnFrame(&this->skelAnime2, 23.0f)) { + func_8002F7DC(&this->actor, NA_SE_IT_LASH); + func_80832698(this, NA_SE_VO_LI_LASH); + } + + AnimationContext_SetCopyAll(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->skelAnime2.jointTable); + } + else { + if (LinkAnimation_OnFrame(&this->skelAnime2, 10.0f)) { + func_8002F7DC(&this->actor, NA_SE_IT_LASH); + func_80832698(this, NA_SE_VO_LI_LASH); + } + + AnimationContext_SetCopyTrue(globalCtx, this->skelAnime.limbCount, this->skelAnime.jointTable, + this->skelAnime2.jointTable, D_80853410); + } + } + else { + LinkAnimationHeader* anim = NULL; + + if (EN_HORSE_CHECK_3(rideActor)) { + anim = &gPlayerAnim_0033B0; + } + else if (EN_HORSE_CHECK_2(rideActor)) { + if ((this->unk_850 >= 2) && (this->unk_850 != 99)) { + anim = D_80854968[this->unk_850 - 2]; + } + } + + if (anim != NULL) { + LinkAnimation_PlayOnce(globalCtx, &this->skelAnime2, anim); + this->unk_84F = 1; + } + } + } + + if (this->stateFlags1 & PLAYER_STATE1_20) { + if (!func_8083AD4C(globalCtx, this) || CHECK_BTN_ANY(sControlInput->press.button, BTN_A) || + func_80833BCC(this)) { + this->unk_6AD = 0; + this->stateFlags1 &= ~PLAYER_STATE1_20; + } + else { + this->unk_6BE = func_8084ABD8(globalCtx, this, 1, -5000) - this->actor.shape.rot.y; + this->unk_6BE += 5000; + this->unk_6B0 = -5000; + } + return; + } + + if ((this->csMode != 0) || (!func_8084C9BC(this, globalCtx) && !func_8083B040(this, globalCtx))) { + if (this->unk_664 != NULL) { + if (func_8002DD78(this) != 0) { + this->unk_6BE = func_8083DB98(this, 1) - this->actor.shape.rot.y; + this->unk_6BE = CLAMP(this->unk_6BE, -0x4AAA, 0x4AAA); + this->actor.focus.rot.y = this->actor.shape.rot.y + this->unk_6BE; + this->unk_6BE += 5000; + this->unk_6AE |= 0x80; + } + else { + func_8083DB98(this, 0); + } + } + else { + if (func_8002DD78(this) != 0) { + this->unk_6BE = func_8084ABD8(globalCtx, this, 1, -5000) - this->actor.shape.rot.y; + this->unk_6BE += 5000; + this->unk_6B0 = -5000; + } + } + } + } +} + +static struct_80832924 D_808549C4[] = { + { 0, 0x2800 }, + { NA_SE_PL_GET_OFF_HORSE, 0x80A }, + { NA_SE_PL_SLIPDOWN, -0x819 }, +}; + +void func_8084D3E4(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_6; + func_8084CBF4(this, 1.0f, 10.0f); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + EnHorse* rideActor = (EnHorse*)this->rideActor; + + func_8083C0E8(this, globalCtx); + this->stateFlags1 &= ~PLAYER_STATE1_23; + this->actor.parent = NULL; + AREG(6) = 0; + + if (Flags_GetEventChkInf(0x18) || (DREG(1) != 0)) { + gSaveContext.horseData.pos.x = rideActor->actor.world.pos.x; + gSaveContext.horseData.pos.y = rideActor->actor.world.pos.y; + gSaveContext.horseData.pos.z = rideActor->actor.world.pos.z; + gSaveContext.horseData.angle = rideActor->actor.shape.rot.y; + } + } + else { + Camera_ChangeSetting(Gameplay_GetCamera(globalCtx, 0), CAM_SET_NORMAL0); + + if (this->mountSide < 0) { + D_808549C4[0].field = 0x2828; + } + else { + D_808549C4[0].field = 0x281D; + } + func_80832924(this, D_808549C4); + } +} + +static struct_80832924 D_808549D0[] = { + { NA_SE_PL_SWIM, -0x800 }, +}; + +void func_8084D530(Player* this, f32* arg1, f32 arg2, s16 arg3) { + func_8084AEEC(this, arg1, arg2, arg3); + func_80832924(this, D_808549D0); +} + +void func_8084D574(GlobalContext* globalCtx, Player* this, s16 arg2) { + func_80835C58(globalCtx, this, func_8084D84C, 0); + this->actor.shape.rot.y = this->currentYaw = arg2; + func_80832C6C(globalCtx, this, &gPlayerAnim_0032F0); +} + +void func_8084D5CC(GlobalContext* globalCtx, Player* this) { + func_80835C58(globalCtx, this, func_8084DAB4, 0); + func_80832C6C(globalCtx, this, &gPlayerAnim_0032F0); +} + +void func_8084D610(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + + func_80832CB0(globalCtx, this, &gPlayerAnim_003328); + func_8084B000(this); + + if (!func_8083224C(globalCtx) && !func_80837348(globalCtx, this, D_80854444, 1) && + !func_8083D12C(globalCtx, this, sControlInput)) { + if (this->unk_6AD != 1) { + this->unk_6AD = 0; + } + + if (this->currentBoots == PLAYER_BOOTS_IRON) { + sp34 = 0.0f; + sp32 = this->actor.shape.rot.y; + + if (this->actor.bgCheckFlags & 1) { + func_8083A098(this, D_80853914[PLAYER_ANIMGROUP_15][this->modelAnimType], globalCtx); + func_808328A0(this); + } + } + else { + func_80837268(this, &sp34, &sp32, 0.0f, globalCtx); + + if (sp34 != 0.0f) { + s16 temp = this->actor.shape.rot.y - sp32; + + if ((ABS(temp) > 0x6000) && !Math_StepToF(&this->linearVelocity, 0.0f, 1.0f)) { + return; + } + + if (func_80833C04(this)) { + func_8084D5CC(globalCtx, this); + } + else { + func_8084D574(globalCtx, this, sp32); + } + } + } + + func_8084AEEC(this, &this->linearVelocity, sp34, sp32); + } +} + +void func_8084D7C4(Player* this, GlobalContext* globalCtx) { + if (!func_8083B040(this, globalCtx)) { + this->stateFlags2 |= PLAYER_STATE2_5; + + func_8084B158(globalCtx, this, NULL, this->linearVelocity); + func_8084B000(this); + + if (DECR(this->unk_850) == 0) { + func_80838F18(globalCtx, this); + } + } +} + +void func_8084D84C(Player* this, GlobalContext* globalCtx) { + f32 sp34; + s16 sp32; + s16 temp; + + this->stateFlags2 |= PLAYER_STATE2_5; + + func_8084B158(globalCtx, this, sControlInput, this->linearVelocity); + func_8084B000(this); + + if (!func_80837348(globalCtx, this, D_80854444, 1) && !func_8083D12C(globalCtx, this, sControlInput)) { + func_80837268(this, &sp34, &sp32, 0.0f, globalCtx); + + temp = this->actor.shape.rot.y - sp32; + if ((sp34 == 0.0f) || (ABS(temp) > 0x6000) || (this->currentBoots == PLAYER_BOOTS_IRON)) { + func_80838F18(globalCtx, this); + } + else if (func_80833C04(this)) { + func_8084D5CC(globalCtx, this); + } + + func_8084D530(this, &this->linearVelocity, sp34, sp32); + } +} + +s32 func_8084D980(GlobalContext* globalCtx, Player* this, f32* arg2, s16* arg3) { + LinkAnimationHeader* anim; + s16 temp1; + s32 temp2; + + temp1 = this->currentYaw - *arg3; + + if (ABS(temp1) > 0x6000) { + anim = &gPlayerAnim_003328; + + if (Math_StepToF(&this->linearVelocity, 0.0f, 1.0f)) { + this->currentYaw = *arg3; + } + else { + *arg2 = 0.0f; + *arg3 = this->currentYaw; + } + } + else { + temp2 = func_8083FD78(this, arg2, arg3, globalCtx); + + if (temp2 > 0) { + anim = &gPlayerAnim_0032F0; + } + else if (temp2 < 0) { + anim = &gPlayerAnim_0032D8; + } + else if ((temp1 = this->actor.shape.rot.y - *arg3) > 0) { + anim = &gPlayerAnim_0032D0; + } + else { + anim = &gPlayerAnim_0032C8; + } + } + + if (anim != this->skelAnime.animation) { + func_80832C6C(globalCtx, this, anim); + return 1; + } + + return 0; +} + +void func_8084DAB4(Player* this, GlobalContext* globalCtx) { + f32 sp2C; + s16 sp2A; + + func_8084B158(globalCtx, this, sControlInput, this->linearVelocity); + func_8084B000(this); + + if (!func_80837348(globalCtx, this, D_80854444, 1) && !func_8083D12C(globalCtx, this, sControlInput)) { + func_80837268(this, &sp2C, &sp2A, 0.0f, globalCtx); + + if (sp2C == 0.0f) { + func_80838F18(globalCtx, this); + } + else if (!func_80833C04(this)) { + func_8084D574(globalCtx, this, sp2A); + } + else { + func_8084D980(globalCtx, this, &sp2C, &sp2A); + } + + func_8084D530(this, &this->linearVelocity, sp2C, sp2A); + } +} + +void func_8084DBC4(GlobalContext* globalCtx, Player* this, f32 arg2) { + f32 sp2C; + s16 sp2A; + + func_80837268(this, &sp2C, &sp2A, 0.0f, globalCtx); + func_8084AEEC(this, &this->linearVelocity, sp2C * 0.5f, sp2A); + func_8084AEEC(this, &this->actor.velocity.y, arg2, this->currentYaw); +} + +void func_8084DC48(Player* this, GlobalContext* globalCtx) { + f32 sp2C; + + this->stateFlags2 |= PLAYER_STATE2_5; + this->actor.gravity = 0.0f; + func_80836670(this, globalCtx); + + if (!func_8083B040(this, globalCtx)) { + if (this->currentBoots == PLAYER_BOOTS_IRON) { + func_80838F18(globalCtx, this); + return; + } + + if (this->unk_84F == 0) { + if (this->unk_850 == 0) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime) || + ((this->skelAnime.curFrame >= 22.0f) && !CHECK_BTN_ALL(sControlInput->cur.button, BTN_A))) { + func_8083D330(globalCtx, this); + } + else if (LinkAnimation_OnFrame(&this->skelAnime, 20.0f) != 0) { + this->actor.velocity.y = -2.0f; + } + + func_8083721C(this); + return; + } + + func_8084B158(globalCtx, this, sControlInput, this->actor.velocity.y); + this->unk_6C2 = 16000; + + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_A) && !func_8083E5A8(this, globalCtx) && + !(this->actor.bgCheckFlags & 1) && (this->actor.yDistToWater < D_80854784[CUR_UPG_VALUE(UPG_SCALE)])) { + func_8084DBC4(globalCtx, this, -2.0f); + } + else { + this->unk_84F++; + func_80832C6C(globalCtx, this, &gPlayerAnim_003328); + } + } + else if (this->unk_84F == 1) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_8084B000(this); + + if (this->unk_6C2 < 10000) { + this->unk_84F++; + this->unk_850 = this->actor.yDistToWater; + func_80832C6C(globalCtx, this, &gPlayerAnim_0032F0); + } + } + else if (!func_8083D12C(globalCtx, this, sControlInput)) { + sp2C = (this->unk_850 * 0.018f) + 4.0f; + + if (this->stateFlags1 & PLAYER_STATE1_11) { + sControlInput = NULL; + } + + func_8084B158(globalCtx, this, sControlInput, fabsf(this->actor.velocity.y)); + Math_ScaledStepToS(&this->unk_6C2, -10000, 800); + + if (sp2C > 8.0f) { + sp2C = 8.0f; + } + + func_8084DBC4(globalCtx, this, sp2C); + } + } +} + +void func_8084DF6C(GlobalContext* globalCtx, Player* this) { + this->unk_862 = 0; + this->stateFlags1 &= ~(PLAYER_STATE1_10 | PLAYER_STATE1_11); + this->getItemId = GI_NONE; + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); +} + +void func_8084DFAC(GlobalContext* globalCtx, Player* this) { + func_8084DF6C(globalCtx, this); + func_808322FC(this); + func_8083C0E8(this, globalCtx); + this->currentYaw = this->actor.shape.rot.y; +} + +s32 func_8084DFF4(GlobalContext* globalCtx, Player* this) { + GetItemEntry* giEntry; + s32 temp1; + s32 temp2; + + if (this->getItemId == GI_NONE) { + return 1; + } + + if (this->unk_84F == 0) { + giEntry = &sGetItemTable[this->getItemId - 1]; + this->unk_84F = 1; + + Message_StartTextbox(globalCtx, giEntry->textId, &this->actor); + Item_Give(globalCtx, giEntry->itemId); + + if (((this->getItemId >= GI_RUPEE_GREEN) && (this->getItemId <= GI_RUPEE_RED)) || + ((this->getItemId >= GI_RUPEE_PURPLE) && (this->getItemId <= GI_RUPEE_GOLD)) || + ((this->getItemId >= GI_RUPEE_GREEN_LOSE) && (this->getItemId <= GI_RUPEE_PURPLE_LOSE)) || + (this->getItemId == GI_HEART)) { + Audio_PlaySoundGeneral(NA_SE_SY_GET_BOXITEM, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + else { + if ((this->getItemId == GI_HEART_CONTAINER_2) || (this->getItemId == GI_HEART_CONTAINER) || + ((this->getItemId == GI_HEART_PIECE) && + ((gSaveContext.inventory.questItems & 0xF0000000) == 0x40000000))) { + temp1 = NA_BGM_HEART_GET | 0x900; + } + else { + temp1 = temp2 = (this->getItemId == GI_HEART_PIECE) ? NA_BGM_SMALL_ITEM_GET : NA_BGM_ITEM_GET | 0x900; + } + Audio_PlayFanfare(temp1); + } + } + else { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + if (this->getItemId == GI_GAUNTLETS_SILVER) { + globalCtx->nextEntranceIndex = 0x0123; + globalCtx->sceneLoadFlag = 0x14; + gSaveContext.nextCutsceneIndex = 0xFFF1; + globalCtx->fadeTransition = 0xF; + this->stateFlags1 &= ~PLAYER_STATE1_29; + func_80852FFC(globalCtx, NULL, 8); + } + this->getItemId = GI_NONE; + } + } + + return 0; +} + +void func_8084E1EC(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (!(this->stateFlags1 & PLAYER_STATE1_10) || func_8084DFF4(globalCtx, this)) { + func_8084DF6C(globalCtx, this); + func_80838F18(globalCtx, this); + func_80832340(globalCtx, this); + } + } + else { + if ((this->stateFlags1 & PLAYER_STATE1_10) && LinkAnimation_OnFrame(&this->skelAnime, 10.0f)) { + func_808332F4(this, globalCtx); + func_80832340(globalCtx, this); + func_80835EA4(globalCtx, 8); + } + else if (LinkAnimation_OnFrame(&this->skelAnime, 5.0f)) { + func_80832698(this, NA_SE_VO_LI_BREATH_DRINK); + } + } + + func_8084B000(this); + func_8084AEEC(this, &this->linearVelocity, 0.0f, this->actor.shape.rot.y); +} + +void func_8084E30C(Player* this, GlobalContext* globalCtx) { + func_8084B000(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80838F18(globalCtx, this); + } + + func_8084AEEC(this, &this->linearVelocity, 0.0f, this->actor.shape.rot.y); +} + +void func_8084E368(Player* this, GlobalContext* globalCtx) { + func_8084B000(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80843AE8(globalCtx, this); + } + + func_8084AEEC(this, &this->linearVelocity, 0.0f, this->actor.shape.rot.y); +} + +static s16 D_808549D4[] = { 0x0600, 0x04F6, 0x0604, 0x01F1, 0x0568, 0x05F4 }; + +void func_8084E3C4(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_808322A4(globalCtx, this, &gPlayerAnim_0030A8); + this->unk_850 = 1; + if (this->stateFlags2 & (PLAYER_STATE2_23 | PLAYER_STATE2_25)) { + this->stateFlags2 |= PLAYER_STATE2_24; + } + else { + func_8010BD58(globalCtx, OCARINA_ACTION_FREE_PLAY); + } + return; + } + + if (this->unk_850 == 0) { + return; + } + + if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_04) { + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + + if ((this->targetActor != NULL) && (this->targetActor == this->unk_6A8)) { + func_80853148(globalCtx, this->targetActor); + } + else if (this->naviTextId < 0) { + this->targetActor = this->naviActor; + this->naviActor->textId = -this->naviTextId; + func_80853148(globalCtx, this->targetActor); + } + else if (!func_8083B040(this, globalCtx)) { + func_8083A098(this, &gPlayerAnim_003098, globalCtx); + } + + this->stateFlags2 &= ~(PLAYER_STATE2_23 | PLAYER_STATE2_24 | PLAYER_STATE2_25); + this->unk_6A8 = NULL; + } + else if (globalCtx->msgCtx.ocarinaMode == OCARINA_MODE_02) { + gSaveContext.respawn[RESPAWN_MODE_RETURN].entranceIndex = D_808549D4[globalCtx->msgCtx.lastPlayedSong]; + gSaveContext.respawn[RESPAWN_MODE_RETURN].playerParams = 0x5FF; + gSaveContext.respawn[RESPAWN_MODE_RETURN].data = globalCtx->msgCtx.lastPlayedSong; + + this->csMode = 0; + this->stateFlags1 &= ~PLAYER_STATE1_29; + + func_80852FFC(globalCtx, NULL, 8); + globalCtx->mainCamera.unk_14C &= ~8; + + this->stateFlags1 |= PLAYER_STATE1_28 | PLAYER_STATE1_29; + this->stateFlags2 |= PLAYER_STATE2_27; + + if (Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_KANKYO, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0xF) == NULL) { + Environment_WarpSongLeave(globalCtx); + } + + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; + } +} + +void func_8084E604(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083A098(this, &gPlayerAnim_003050, globalCtx); + } + else if (LinkAnimation_OnFrame(&this->skelAnime, 3.0f)) { + Inventory_ChangeAmmo(ITEM_NUT, -1); + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_ARROW, this->bodyPartsPos[PLAYER_BODYPART_R_HAND].x, + this->bodyPartsPos[PLAYER_BODYPART_R_HAND].y, this->bodyPartsPos[PLAYER_BODYPART_R_HAND].z, 4000, + this->actor.shape.rot.y, 0, ARROW_NUT); + func_80832698(this, NA_SE_VO_LI_SWORD_N); + } + + func_8083721C(this); +} + +static struct_80832924 D_808549E0[] = { + { 0, 0x3857 }, + { NA_SE_VO_LI_CLIMB_END, 0x2057 }, + { NA_SE_VO_LI_AUTO_JUMP, 0x2045 }, + { 0, -0x287B }, +}; + +void func_8084E6D4(Player* this, GlobalContext* globalCtx) { + s32 cond; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_850 != 0) { + if (this->unk_850 >= 2) { + this->unk_850--; + } + + if (func_8084DFF4(globalCtx, this) && (this->unk_850 == 1)) { + cond = ((this->targetActor != NULL) && (this->exchangeItemId < 0)) || + (this->stateFlags3 & PLAYER_STATE3_5); + + if (cond || (gSaveContext.healthAccumulator == 0)) { + if (cond) { + func_8084DF6C(globalCtx, this); + this->exchangeItemId = EXCH_ITEM_NONE; + + if (func_8084B4D4(globalCtx, this) == 0) { + func_80853148(globalCtx, this->targetActor); + } + } + else { + func_8084DFAC(globalCtx, this); + } + } + } + } + else { + func_80832DBC(this); + + if (this->getItemId == GI_ICE_TRAP) { + this->stateFlags1 &= ~(PLAYER_STATE1_10 | PLAYER_STATE1_11); + + if (this->getItemId != GI_ICE_TRAP) { + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_CLEAR_TAG, this->actor.world.pos.x, + this->actor.world.pos.y + 100.0f, this->actor.world.pos.z, 0, 0, 0, 0); + func_8083C0E8(this, globalCtx); + } + else { + this->actor.colChkInfo.damage = 0; + func_80837C0C(globalCtx, this, 3, 0.0f, 0.0f, 0, 20); + } + return; + } + + if (this->skelAnime.animation == &gPlayerAnim_002DF8) { + func_808322D0(globalCtx, this, &gPlayerAnim_002788); + } + else { + func_808322D0(globalCtx, this, &gPlayerAnim_002780); + } + + this->unk_850 = 2; + func_80835EA4(globalCtx, 9); + } + } + else { + if (this->unk_850 == 0) { + if (!LINK_IS_ADULT) { + func_80832924(this, D_808549E0); + } + return; + } + + if (this->skelAnime.animation == &gPlayerAnim_002788) { + Math_ScaledStepToS(&this->actor.shape.rot.y, Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000, 4000); + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 21.0f)) { + func_808332F4(this, globalCtx); + } + } +} + +static struct_80832924 D_808549F0[] = { + { NA_SE_IT_MASTER_SWORD_SWING, -0x83C }, +}; + +void func_8084E988(Player* this) { + func_80832924(this, D_808549F0); +} + +static struct_80832924 D_808549F4[] = { + { NA_SE_VO_LI_AUTO_JUMP, 0x2005 }, + { 0, -0x280F }, +}; + +void func_8084E9AC(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_84F == 0) { + if (DECR(this->unk_850) == 0) { + this->unk_84F = 1; + this->skelAnime.endFrame = this->skelAnime.animLength - 1.0f; + } + } + else { + func_8083C0E8(this, globalCtx); + } + } + else { + if (LINK_IS_ADULT && LinkAnimation_OnFrame(&this->skelAnime, 158.0f)) { + func_80832698(this, NA_SE_VO_LI_SWORD_N); + return; + } + + if (!LINK_IS_ADULT) { + func_80832924(this, D_808549F4); + } + else { + func_8084E988(this); + } + } +} + +static u8 D_808549FC[] = { + 0x01, 0x03, 0x02, 0x04, 0x04, +}; + +void func_8084EAC0(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_850 == 0) { + if (this->itemActionParam == PLAYER_AP_BOTTLE_POE) { + s32 rand = Rand_S16Offset(-1, 3); + + if (rand == 0) { + rand = 3; + } + + if ((rand < 0) && (gSaveContext.health <= 0x10)) { + rand = 3; + } + + if (rand < 0) { + Health_ChangeBy(globalCtx, -0x10); + } + else { + gSaveContext.healthAccumulator = rand * 0x10; + } + } + else { + s32 sp28 = D_808549FC[this->itemActionParam - PLAYER_AP_BOTTLE_POTION_RED]; + + if (sp28 & 1) { + gSaveContext.healthAccumulator = 0x140; + } + + if (sp28 & 2) { + Magic_Fill(globalCtx); + } + + if (sp28 & 4) { + gSaveContext.healthAccumulator = 0x50; + } + } + + func_808322A4(globalCtx, this, &gPlayerAnim_002670); + this->unk_850 = 1; + return; + } + + func_8083C0E8(this, globalCtx); + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + } + else if (this->unk_850 == 1) { + if ((gSaveContext.healthAccumulator == 0) && (gSaveContext.unk_13F0 != 9)) { + func_80832B78(globalCtx, this, &gPlayerAnim_002660); + this->unk_850 = 2; + Player_UpdateBottleHeld(globalCtx, this, ITEM_BOTTLE, PLAYER_AP_BOTTLE); + } + func_80832698(this, NA_SE_VO_LI_DRINK - SFX_FLAG); + } + else if ((this->unk_850 == 2) && LinkAnimation_OnFrame(&this->skelAnime, 29.0f)) { + func_80832698(this, NA_SE_VO_LI_BREATH_DRINK); + } +} + +static BottleCatchInfo D_80854A04[] = { + { ACTOR_EN_ELF, ITEM_FAIRY, 0x2A, 0x46 }, + { ACTOR_EN_FISH, ITEM_FISH, 0x1F, 0x47 }, + { ACTOR_EN_ICE_HONO, ITEM_BLUE_FIRE, 0x20, 0x5D }, + { ACTOR_EN_INSECT, ITEM_BUG, 0x21, 0x7A }, +}; + +void func_8084ECA4(Player* this, GlobalContext* globalCtx) { + struct_80854554* sp24; + BottleCatchInfo* catchInfo; + s32 temp; + s32 i; + + sp24 = &D_80854554[this->unk_850]; + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_84F != 0) { + if (this->unk_850 == 0) { + Message_StartTextbox(globalCtx, D_80854A04[this->unk_84F - 1].textId, &this->actor); + Audio_PlayFanfare(NA_BGM_ITEM_GET | 0x900); + this->unk_850 = 1; + } + else if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->unk_84F = 0; + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + } + } + else { + func_8083C0E8(this, globalCtx); + } + } + else { + if (this->unk_84F == 0) { + temp = this->skelAnime.curFrame - sp24->unk_08; + + if (temp >= 0) { + if (sp24->unk_09 >= temp) { + if (this->unk_850 != 0) { + if (temp == 0) { + func_8002F7DC(&this->actor, NA_SE_IT_SCOOP_UP_WATER); + } + } + + if (this->interactRangeActor != NULL) { + catchInfo = &D_80854A04[0]; + for (i = 0; i < 4; i++, catchInfo++) { + if (this->interactRangeActor->id == catchInfo->actorId) { + break; + } + } + + if (i < 4) { + this->unk_84F = i + 1; + this->unk_850 = 0; + this->stateFlags1 |= PLAYER_STATE1_28 | PLAYER_STATE1_29; + this->interactRangeActor->parent = &this->actor; + Player_UpdateBottleHeld(globalCtx, this, catchInfo->itemId, ABS(catchInfo->actionParam)); + func_808322D0(globalCtx, this, sp24->unk_04); + func_80835EA4(globalCtx, 4); + } + } + } + } + } + } + + if (this->skelAnime.curFrame <= 7.0f) { + this->stateFlags1 |= PLAYER_STATE1_1; + } +} + +static Vec3f D_80854A1C = { 0.0f, 0.0f, 5.0f }; + +void func_8084EED8(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083C0E8(this, globalCtx); + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 37.0f)) { + Player_SpawnFairy(globalCtx, this, &this->leftHandPos, &D_80854A1C, FAIRY_REVIVE_BOTTLE); + Player_UpdateBottleHeld(globalCtx, this, ITEM_BOTTLE, PLAYER_AP_BOTTLE); + func_8002F7DC(&this->actor, NA_SE_EV_BOTTLE_CAP_OPEN); + func_8002F7DC(&this->actor, NA_SE_EV_FIATY_HEAL - SFX_FLAG); + } + else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { + gSaveContext.healthAccumulator = 0x140; + } +} + +static BottleDropInfo D_80854A28[] = { + { ACTOR_EN_FISH, FISH_DROPPED }, + { ACTOR_EN_ICE_HONO, 0 }, + { ACTOR_EN_INSECT, 2 }, +}; + +static struct_80832924 D_80854A34[] = { + { NA_SE_VO_LI_AUTO_JUMP, 0x2026 }, + { NA_SE_EV_BOTTLE_CAP_OPEN, -0x828 }, +}; + +void func_8084EFC0(Player* this, GlobalContext* globalCtx) { + func_8083721C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083C0E8(this, globalCtx); + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + return; + } + + if (LinkAnimation_OnFrame(&this->skelAnime, 76.0f)) { + BottleDropInfo* dropInfo = &D_80854A28[this->itemActionParam - PLAYER_AP_BOTTLE_FISH]; + + Actor_Spawn(&globalCtx->actorCtx, globalCtx, dropInfo->actorId, + (Math_SinS(this->actor.shape.rot.y) * 5.0f) + this->leftHandPos.x, this->leftHandPos.y, + (Math_CosS(this->actor.shape.rot.y) * 5.0f) + this->leftHandPos.z, 0x4000, this->actor.shape.rot.y, + 0, dropInfo->actorParams); + + Player_UpdateBottleHeld(globalCtx, this, ITEM_BOTTLE, PLAYER_AP_BOTTLE); + return; + } + + func_80832924(this, D_80854A34); +} + +static struct_80832924 D_80854A3C[] = { + { NA_SE_PL_PUT_OUT_ITEM, -0x81E }, +}; + +void func_8084F104(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_850 < 0) { + func_8083C0E8(this, globalCtx); + } + else if (this->exchangeItemId == EXCH_ITEM_NONE) { + Actor* targetActor = this->targetActor; + + this->unk_862 = 0; + if (targetActor->textId != 0xFFFF) { + this->actor.flags |= ACTOR_FLAG_8; + } + + func_80853148(globalCtx, targetActor); + } + else { + GetItemEntry* giEntry = &sGetItemTable[D_80854528[this->exchangeItemId - 1] - 1]; + + if (this->itemActionParam >= PLAYER_AP_LETTER_ZELDA) { + if (giEntry->gi >= 0) { + this->unk_862 = giEntry->gi; + } + else { + this->unk_862 = -giEntry->gi; + } + } + + if (this->unk_850 == 0) { + Message_StartTextbox(globalCtx, this->actor.textId, &this->actor); + + if ((this->itemActionParam == PLAYER_AP_CHICKEN) || (this->itemActionParam == PLAYER_AP_POCKET_CUCCO)) { + func_8002F7DC(&this->actor, NA_SE_EV_CHICKEN_CRY_M); + } + + this->unk_850 = 1; + } + else if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + this->actor.flags &= ~ACTOR_FLAG_8; + this->unk_862 = 0; + + if (this->unk_84F == 1) { + func_80832264(globalCtx, this, &gPlayerAnim_002698); + this->unk_850 = -1; + } + else { + func_8083C0E8(this, globalCtx); + } + + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + } + } + } + else if (this->unk_850 >= 0) { + func_80832924(this, D_80854A3C); + } + + if ((this->unk_84F == 0) && (this->unk_664 != NULL)) { + this->currentYaw = this->actor.shape.rot.y = func_8083DB98(this, 0); + } +} + +void func_8084F308(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5 | PLAYER_STATE2_6; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, &gPlayerAnim_003128); + } + + if (func_80832594(this, 0, 100)) { + func_80839F90(this, globalCtx); + this->stateFlags2 &= ~PLAYER_STATE2_7; + } +} + +void func_8084F390(Player* this, GlobalContext* globalCtx) { + CollisionPoly* floorPoly; + f32 sp50; + f32 sp4C; + f32 sp48; + s16 sp46; + s16 sp44; + Vec3f sp38; + + this->stateFlags2 |= PLAYER_STATE2_5 | PLAYER_STATE2_6; + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_8084269C(globalCtx, this); + func_800F4138(&this->actor.projectedPos, NA_SE_PL_SLIP_LEVEL - SFX_FLAG, this->actor.speedXZ); + + if (func_8083B040(this, globalCtx) == 0) { + floorPoly = this->actor.floorPoly; + + if (floorPoly == NULL) { + func_80837B9C(this, globalCtx); + return; + } + + func_8083E298(floorPoly, &sp38, &sp46); + + sp44 = sp46; + if (this->unk_84F != 0) { + sp44 = sp46 + 0x8000; + } + + if (this->linearVelocity < 0) { + sp46 += 0x8000; + } + + sp50 = (1.0f - sp38.y) * 40.0f; + sp50 = CLAMP(sp50, 0, 10.0f); + sp4C = (sp50 * sp50) * 0.015f; + sp48 = sp38.y * 0.01f; + + if (SurfaceType_GetSlope(&globalCtx->colCtx, floorPoly, this->actor.floorBgId) != 1) { + sp50 = 0; + sp48 = sp38.y * 10.0f; + } + + if (sp4C < 1.0f) { + sp4C = 1.0f; + } + + if (Math_AsymStepToF(&this->linearVelocity, sp50, sp4C, sp48) && (sp50 == 0)) { + LinkAnimationHeader* anim; + if (this->unk_84F == 0) { + anim = D_80853914[PLAYER_ANIMGROUP_42][this->modelAnimType]; + } + else { + anim = D_80853914[PLAYER_ANIMGROUP_43][this->modelAnimType]; + } + func_8083A098(this, anim, globalCtx); + } + + Math_SmoothStepToS(&this->currentYaw, sp46, 10, 4000, 800); + Math_ScaledStepToS(&this->actor.shape.rot.y, sp44, 2000); + } +} + +void func_8084F608(Player* this, GlobalContext* globalCtx) { + if ((DECR(this->unk_850) == 0) && func_8083ADD4(globalCtx, this)) { + func_80852280(globalCtx, this, NULL); + func_80835C58(globalCtx, this, func_80852E14, 0); + func_80852E14(this, globalCtx); + } +} + +void func_8084F698(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_8084F608, 0); + this->unk_850 = 40; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_DEMO_KANKYO, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0x10); +} + +void func_8084F710(Player* this, GlobalContext* globalCtx) { + s32 pad; + + if ((this->unk_84F != 0) && (globalCtx->csCtx.frames < 0x131)) { + this->actor.gravity = 0.0f; + this->actor.velocity.y = 0.0f; + } + else if (D_80853600 < 150.0f) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_850 == 0) { + if (this->actor.bgCheckFlags & 1) { + this->skelAnime.endFrame = this->skelAnime.animLength - 1.0f; + func_808328A0(this); + this->unk_850 = 1; + } + } + else { + if ((globalCtx->sceneNum == SCENE_SPOT04) && func_8083ADD4(globalCtx, this)) { + return; + } + func_80853080(this, globalCtx); + } + } + Math_SmoothStepToF(&this->actor.velocity.y, 2.0f, 0.3f, 8.0f, 0.5f); + } + + if ((globalCtx->sceneNum == SCENE_KENJYANOMA) && func_8083ADD4(globalCtx, this)) { + return; + } + + if ((globalCtx->csCtx.state != CS_STATE_IDLE) && (globalCtx->csCtx.linkAction != NULL)) { + f32 sp28 = this->actor.world.pos.y; + func_808529D0(globalCtx, this, globalCtx->csCtx.linkAction); + this->actor.world.pos.y = sp28; + } +} + +void func_8084F88C(Player* this, GlobalContext* globalCtx) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if ((this->unk_850++ > 8) && (globalCtx->sceneLoadFlag == 0)) { + + if (this->unk_84F != 0) { + if (globalCtx->sceneNum == 9) { + Gameplay_TriggerRespawn(globalCtx); + globalCtx->nextEntranceIndex = 0x0088; + } + else if (this->unk_84F < 0) { + Gameplay_TriggerRespawn(globalCtx); + } + else { + Gameplay_TriggerVoidOut(globalCtx); + } + + globalCtx->fadeTransition = 4; + func_80078884(NA_SE_OC_ABYSS); + } + else { + globalCtx->fadeTransition = 2; + gSaveContext.nextTransition = 2; + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = 0xFF; + } + + globalCtx->sceneLoadFlag = 0x14; + } +} + +void func_8084F9A0(Player* this, GlobalContext* globalCtx) { + func_80839800(this, globalCtx); +} + +void func_8084F9C0(Player* this, GlobalContext* globalCtx) { + this->actor.gravity = -1.0f; + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (this->actor.velocity.y < 0.0f) { + func_80837B9C(this, globalCtx); + } + else if (this->actor.velocity.y < 6.0f) { + Math_StepToF(&this->linearVelocity, 3.0f, 0.5f); + } +} + +void func_8084FA54(Player* this, GlobalContext* globalCtx) { + this->unk_6AD = 2; + + func_8083AD4C(globalCtx, this); + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_80836670(this, globalCtx); + + this->unk_6BE = func_8084ABD8(globalCtx, this, 1, 0) - this->actor.shape.rot.y; + this->unk_6AE |= 0x80; + + if (globalCtx->shootingGalleryStatus < 0) { + globalCtx->shootingGalleryStatus++; + if (globalCtx->shootingGalleryStatus == 0) { + func_8083C148(this, globalCtx); + } + } +} + +void func_8084FB10(Player* this, GlobalContext* globalCtx) { + if (this->unk_84F >= 0) { + if (this->unk_84F < 6) { + this->unk_84F++; + } + + if (func_80832594(this, 1, 100)) { + this->unk_84F = -1; + EffectSsIcePiece_SpawnBurst(globalCtx, &this->actor.world.pos, this->actor.scale.x); + func_8002F7DC(&this->actor, NA_SE_PL_ICE_BROKEN); + } + else { + this->stateFlags2 |= PLAYER_STATE2_14; + } + + if ((globalCtx->gameplayFrames % 4) == 0) { + Player_InflictDamage(globalCtx, -1); + } + } + else { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80839F90(this, globalCtx); + func_80837AFC(this, -20); + } + } +} + +void func_8084FBF4(Player* this, GlobalContext* globalCtx) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_808382BC(this); + + if (((this->unk_850 % 25) != 0) || func_80837B18(globalCtx, this, -1)) { + if (DECR(this->unk_850) == 0) { + func_80839F90(this, globalCtx); + } + } + + this->shockTimer = 40; + func_8002F8F0(&this->actor, NA_SE_VO_LI_TAKEN_AWAY - SFX_FLAG + this->ageProperties->unk_92); +} + +s32 func_8084FCAC(Player* this, GlobalContext* globalCtx) { + sControlInput = &globalCtx->state.input[0]; + + if (CVar_GetS32("gDebugEnabled", 0) && ((CHECK_BTN_ALL(sControlInput->cur.button, BTN_A | BTN_L | BTN_R) && + CHECK_BTN_ALL(sControlInput->press.button, BTN_B)) || + (CHECK_BTN_ALL(sControlInput->cur.button, BTN_L) && CHECK_BTN_ALL(sControlInput->press.button, BTN_DRIGHT)))) { + + D_808535D0 ^= 1; + + if (D_808535D0) { + Camera_ChangeMode(Gameplay_GetCamera(globalCtx, 0), CAM_MODE_BOWARROWZ); + } + } + + if (D_808535D0) { + f32 speed; + + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_R)) { + speed = 100.0f; + } + else { + speed = 20.0f; + } + + func_8006375C(3, 2, "DEBUG MODE"); + + if (!CHECK_BTN_ALL(sControlInput->cur.button, BTN_L)) { + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_B)) { + this->actor.world.pos.y += speed; + } + else if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_A)) { + this->actor.world.pos.y -= speed; + } + + if (CHECK_BTN_ANY(sControlInput->cur.button, BTN_DUP | BTN_DLEFT | BTN_DDOWN | BTN_DRIGHT)) { + s16 angle; + s16 temp; + + angle = temp = Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx)); + + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DDOWN)) { + angle = temp + 0x8000; + } + else if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DLEFT)) { + angle = temp + 0x4000; + } + else if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DRIGHT)) { + angle = temp - 0x4000; + } + + this->actor.world.pos.x += speed * Math_SinS(angle); + this->actor.world.pos.z += speed * Math_CosS(angle); + } + } + + func_80832210(this); + + this->actor.gravity = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.x = 0.0f; + + if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_L) && CHECK_BTN_ALL(sControlInput->press.button, BTN_DLEFT)) { + Flags_SetTempClear(globalCtx, globalCtx->roomCtx.curRoom.num); + } + + Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); + + return 0; + } + + return 1; +} + +void func_8084FF7C(Player* this) { + this->unk_858 += this->unk_85C; + this->unk_85C -= this->unk_858 * 5.0f; + this->unk_85C *= 0.3f; + + if (ABS(this->unk_85C) < 0.00001f) { + this->unk_85C = 0.0f; + if (ABS(this->unk_858) < 0.00001f) { + this->unk_858 = 0.0f; + } + } +} + +void func_8085002C(Player* this) { + s32 pad; + s16 sp2A; + s16 sp28; + s16 sp26; + + D_80858AC8.unk_06 -= D_80858AC8.unk_06 >> 3; + D_80858AC8.unk_08 -= D_80858AC8.unk_08 >> 3; + D_80858AC8.unk_06 += -D_80858AC8.unk_00 >> 2; + D_80858AC8.unk_08 += -D_80858AC8.unk_02 >> 2; + + sp26 = this->actor.world.rot.y - this->actor.shape.rot.y; + + sp28 = (s32)(this->actor.speedXZ * -200.0f * Math_CosS(sp26) * (Rand_CenteredFloat(2.0f) + 10.0f)) & 0xFFFF; + sp2A = (s32)(this->actor.speedXZ * 100.0f * Math_SinS(sp26) * (Rand_CenteredFloat(2.0f) + 10.0f)) & 0xFFFF; + + D_80858AC8.unk_06 += sp28 >> 2; + D_80858AC8.unk_08 += sp2A >> 2; + + if (D_80858AC8.unk_06 > 6000) { + D_80858AC8.unk_06 = 6000; + } + else if (D_80858AC8.unk_06 < -6000) { + D_80858AC8.unk_06 = -6000; + } + + if (D_80858AC8.unk_08 > 6000) { + D_80858AC8.unk_08 = 6000; + } + else if (D_80858AC8.unk_08 < -6000) { + D_80858AC8.unk_08 = -6000; + } + + D_80858AC8.unk_00 += D_80858AC8.unk_06; + D_80858AC8.unk_02 += D_80858AC8.unk_08; + + if (D_80858AC8.unk_00 < 0) { + D_80858AC8.unk_04 = D_80858AC8.unk_00 >> 1; + } + else { + D_80858AC8.unk_04 = 0; + } +} + +s32 func_80850224(Player* this, GlobalContext* globalCtx) { + if (func_8083C6B8(globalCtx, this) == 0) { + if (func_8083BB20(this) != 0) { + s32 sp24 = func_80837818(this); + + func_80837948(globalCtx, this, sp24); + + if (sp24 >= 0x18) { + this->stateFlags2 |= PLAYER_STATE2_17; + func_80837530(globalCtx, this, 0); + return 1; + } + } + else { + return 0; + } + } + + return 1; +} + +static Vec3f D_80854A40 = { 0.0f, 40.0f, 45.0f }; + +void func_808502D0(Player* this, GlobalContext* globalCtx) { + struct_80854190* sp44 = &D_80854190[this->swordAnimation]; + + this->stateFlags2 |= PLAYER_STATE2_5; + + if (!func_80842DF4(globalCtx, this)) { + func_8084285C(this, 0.0f, sp44->unk_0C, sp44->unk_0D); + + if ((this->stateFlags2 & PLAYER_STATE2_30) && (this->heldItemActionParam != PLAYER_AP_HAMMER) && + LinkAnimation_OnFrame(&this->skelAnime, 0.0f)) { + this->linearVelocity = 15.0f; + this->stateFlags2 &= ~PLAYER_STATE2_30; + } + + if (this->linearVelocity > 12.0f) { + func_8084269C(globalCtx, this); + } + + Math_StepToF(&this->linearVelocity, 0.0f, 5.0f); + func_8083C50C(this); + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (!func_80850224(this, globalCtx)) { + u8 sp43 = this->skelAnime.moveFlags; + LinkAnimationHeader* sp3C; + + if (func_8008E9C4(this)) { + sp3C = sp44->unk_08; + } + else { + sp3C = sp44->unk_04; + } + + func_80832318(this); + this->skelAnime.moveFlags = 0; + + if ((sp3C == &gPlayerAnim_002908) && (this->modelAnimType != PLAYER_ANIMTYPE_3)) { + sp3C = &gPlayerAnim_002AC8; + } + + func_8083A098(this, sp3C, globalCtx); + + this->skelAnime.moveFlags = sp43; + this->stateFlags3 |= PLAYER_STATE3_3; + } + } + else if (this->heldItemActionParam == PLAYER_AP_HAMMER) { + if ((this->swordAnimation == 0x16) || (this->swordAnimation == 0x13)) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f shockwavePos; + f32 sp2C; + + shockwavePos.y = func_8083973C(globalCtx, this, &D_80854A40, &shockwavePos); + sp2C = this->actor.world.pos.y - shockwavePos.y; + + Math_ScaledStepToS(&this->actor.focus.rot.x, Math_Atan2S(45.0f, sp2C), 800); + func_80836AB8(this, 1); + + if ((((this->swordAnimation == 0x16) && LinkAnimation_OnFrame(&this->skelAnime, 7.0f)) || + ((this->swordAnimation == 0x13) && LinkAnimation_OnFrame(&this->skelAnime, 2.0f))) && + (sp2C > -40.0f) && (sp2C < 40.0f)) { + func_80842A28(globalCtx, this); + EffectSsBlast_SpawnWhiteShockwave(globalCtx, &shockwavePos, &zeroVec, &zeroVec); + } + } + } + } +} + +void func_808505DC(Player* this, GlobalContext* globalCtx) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_8083721C(this); + + if (this->skelAnime.curFrame >= 6.0f) { + func_80839FFC(this, globalCtx); + } +} + +void func_8085063C(Player* this, GlobalContext* globalCtx) { + this->stateFlags2 |= PLAYER_STATE2_5; + + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_80836670(this, globalCtx); + + if (this->unk_850 == 0) { + Message_StartTextbox(globalCtx, 0x3B, &this->actor); + this->unk_850 = 1; + return; + } + + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CLOSING) { + s32 respawnData = gSaveContext.respawn[RESPAWN_MODE_TOP].data; + + if (globalCtx->msgCtx.choiceIndex == 0) { + gSaveContext.respawnFlag = 3; + globalCtx->sceneLoadFlag = 0x14; + globalCtx->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_TOP].entranceIndex; + globalCtx->fadeTransition = 5; + func_80088AF0(globalCtx); + return; + } + + if (globalCtx->msgCtx.choiceIndex == 1) { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = -respawnData; + gSaveContext.fw.set = 0; + func_80078914(&gSaveContext.respawn[RESPAWN_MODE_TOP].pos, NA_SE_PL_MAGIC_WIND_VANISH); + } + + func_80853080(this, globalCtx); + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + } +} + +void func_8085076C(Player* this, GlobalContext* globalCtx) { + s32 respawnData = gSaveContext.respawn[RESPAWN_MODE_TOP].data; + + if (this->unk_850 > 20) { + this->actor.draw = Player_Draw; + this->actor.world.pos.y += 60.0f; + func_80837B9C(this, globalCtx); + return; + } + + if (this->unk_850++ == 20) { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = respawnData + 1; + func_80078914(&gSaveContext.respawn[RESPAWN_MODE_TOP].pos, NA_SE_PL_MAGIC_WIND_WARP); + } +} + +static LinkAnimationHeader* D_80854A58[] = { + &gPlayerAnim_002CF8, + &gPlayerAnim_002CE0, + &gPlayerAnim_002D10, +}; + +static LinkAnimationHeader* D_80854A64[] = { + &gPlayerAnim_002D00, + &gPlayerAnim_002CE8, + &gPlayerAnim_002D18, +}; + +static LinkAnimationHeader* D_80854A70[] = { + &gPlayerAnim_002D08, + &gPlayerAnim_002CF0, + &gPlayerAnim_002D20, +}; + +static u8 D_80854A7C[] = { 70, 10, 10 }; + +static struct_80832924 D_80854A80[] = { + { NA_SE_PL_SKIP, 0x814 }, + { NA_SE_VO_LI_SWORD_N, 0x2014 }, + { 0, -0x301A }, +}; + +static struct_80832924 D_80854A8C[][2] = { + { + { 0, 0x4014 }, + { NA_SE_VO_LI_MAGIC_FROL, -0x201E }, + }, + { + { 0, 0x4014 }, + { NA_SE_VO_LI_MAGIC_NALE, -0x202C }, + }, + { + { NA_SE_VO_LI_MAGIC_ATTACK, 0x2014 }, + { NA_SE_IT_SWORD_SWING_HARD, -0x814 }, + }, +}; + +void func_808507F4(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_84F < 0) { + if ((this->itemActionParam == PLAYER_AP_NAYRUS_LOVE) || (gSaveContext.unk_13F0 == 0)) { + func_80839FFC(this, globalCtx); + func_8005B1A4(Gameplay_GetCamera(globalCtx, 0)); + } + } + else { + if (this->unk_850 == 0) { + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, D_80854A58[this->unk_84F], 0.83f); + + if (func_80846A00(globalCtx, this, this->unk_84F) != NULL) { + this->stateFlags1 |= PLAYER_STATE1_28 | PLAYER_STATE1_29; + if ((this->unk_84F != 0) || (gSaveContext.respawn[RESPAWN_MODE_TOP].data <= 0)) { + gSaveContext.unk_13F0 = 1; + } + } + else { + func_800876C8(globalCtx); + } + } + else { + LinkAnimation_PlayLoopSetSpeed(globalCtx, &this->skelAnime, D_80854A64[this->unk_84F], 0.83f); + + if (this->unk_84F == 0) { + this->unk_850 = -10; + } + } + + this->unk_850++; + } + } + else { + if (this->unk_850 < 0) { + this->unk_850++; + + if (this->unk_850 == 0) { + gSaveContext.respawn[RESPAWN_MODE_TOP].data = 1; + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_TOP, 0x6FF); + gSaveContext.fw.set = 1; + gSaveContext.fw.pos.x = gSaveContext.respawn[RESPAWN_MODE_DOWN].pos.x; + gSaveContext.fw.pos.y = gSaveContext.respawn[RESPAWN_MODE_DOWN].pos.y; + gSaveContext.fw.pos.z = gSaveContext.respawn[RESPAWN_MODE_DOWN].pos.z; + gSaveContext.fw.yaw = gSaveContext.respawn[RESPAWN_MODE_DOWN].yaw; + gSaveContext.fw.playerParams = 0x6FF; + gSaveContext.fw.entranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex; + gSaveContext.fw.roomIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].roomIndex; + gSaveContext.fw.tempSwchFlags = gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags; + gSaveContext.fw.tempCollectFlags = gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags; + this->unk_850 = 2; + } + } + else if (this->unk_84F >= 0) { + if (this->unk_850 == 0) { + func_80832924(this, D_80854A80); + } + else if (this->unk_850 == 1) { + func_80832924(this, D_80854A8C[this->unk_84F]); + if ((this->unk_84F == 2) && LinkAnimation_OnFrame(&this->skelAnime, 30.0f)) { + this->stateFlags1 &= ~(PLAYER_STATE1_28 | PLAYER_STATE1_29); + } + } + else if (D_80854A7C[this->unk_84F] < this->unk_850++) { + LinkAnimation_PlayOnceSetSpeed(globalCtx, &this->skelAnime, D_80854A70[this->unk_84F], 0.83f); + this->currentYaw = this->actor.shape.rot.y; + this->unk_84F = -1; + } + } + } + + func_8083721C(this); +} + +void func_80850AEC(Player* this, GlobalContext* globalCtx) { + f32 temp; + + this->stateFlags2 |= PLAYER_STATE2_5; + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, &gPlayerAnim_002C98); + } + + Math_Vec3f_Sum(&this->actor.world.pos, &this->actor.velocity, &this->actor.world.pos); + + if (func_80834FBC(this)) { + Math_Vec3f_Copy(&this->actor.prevPos, &this->actor.world.pos); + func_80847BA0(globalCtx, this); + + temp = this->actor.world.pos.y - this->actor.floorHeight; + if (temp > 20.0f) { + temp = 20.0f; + } + + this->actor.world.rot.x = this->actor.shape.rot.x = 0; + this->actor.world.pos.y -= temp; + this->linearVelocity = 1.0f; + this->actor.velocity.y = 0.0f; + func_80837B9C(this, globalCtx); + this->stateFlags2 &= ~PLAYER_STATE2_10; + this->actor.bgCheckFlags |= 1; + this->stateFlags1 |= PLAYER_STATE1_2; + return; + } + + if ((this->skelAnime.animation != &gPlayerAnim_002C90) || (4.0f <= this->skelAnime.curFrame)) { + this->actor.gravity = 0.0f; + Math_ScaledStepToS(&this->actor.shape.rot.x, this->actor.world.rot.x, 0x800); + func_8083264C(this, 100, 2, 100, 0); + } +} + +void func_80850C68(Player* this, GlobalContext* globalCtx) { + if ((this->unk_850 != 0) && ((this->unk_858 != 0.0f) || (this->unk_85C != 0.0f))) { + f32 updateScale = R_UPDATE_RATE * 0.5f; + + this->skelAnime.curFrame += this->skelAnime.playSpeed * updateScale; + if (this->skelAnime.curFrame >= this->skelAnime.animLength) { + this->skelAnime.curFrame -= this->skelAnime.animLength; + } + + LinkAnimation_BlendToJoint(globalCtx, &this->skelAnime, &gPlayerAnim_002C38, this->skelAnime.curFrame, + (this->unk_858 < 0.0f) ? &gPlayerAnim_002C18 : &gPlayerAnim_002C20, 5.0f, + fabsf(this->unk_858), this->blendTable); + LinkAnimation_BlendToMorph(globalCtx, &this->skelAnime, &gPlayerAnim_002C38, this->skelAnime.curFrame, + (this->unk_85C < 0.0f) ? &gPlayerAnim_002C28 : &gPlayerAnim_002C10, 5.0f, + fabsf(this->unk_85C), D_80858AD8); + LinkAnimation_InterpJointMorph(globalCtx, &this->skelAnime, 0.5f); + } + else if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + this->unk_860 = 2; + func_80832284(globalCtx, this, &gPlayerAnim_002C38); + this->unk_850 = 1; + } + + func_8083721C(this); + + if (this->unk_860 == 0) { + func_80853080(this, globalCtx); + } + else if (this->unk_860 == 3) { + func_80835C58(globalCtx, this, func_80850E84, 0); + func_80832B0C(globalCtx, this, &gPlayerAnim_002C00); + } +} + +void func_80850E84(Player* this, GlobalContext* globalCtx) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime) && (this->unk_860 == 0)) { + func_8083A098(this, &gPlayerAnim_002C08, globalCtx); + } +} + +static void (*D_80854AA4[])(GlobalContext*, Player*, void*) = { + NULL, func_80851008, func_80851030, func_80851094, func_808510B4, func_808510D4, func_808510F4, + func_80851114, func_80851134, func_80851154, func_80851174, func_808511D4, func_808511FC, func_80851294, + func_80851050, func_80851194, func_808511B4, func_80851248, func_808512E0, +}; + +static struct_80832924 D_80854AF0[] = { + { 0, 0x2822 }, + { NA_SE_PL_CALM_HIT, 0x82D }, + { NA_SE_PL_CALM_HIT, 0x833 }, + { NA_SE_PL_CALM_HIT, -0x840 }, +}; + +static struct_80832924 D_80854B00[] = { + { NA_SE_VO_LI_SURPRISE, 0x2003 }, { 0, 0x300F }, { 0, 0x3018 }, { 0, 0x301E }, { NA_SE_VO_LI_FALL_L, -0x201F }, +}; + +static struct_80832924 D_80854B14[] = { + { 0, -0x300A }, +}; + +static struct_80854B18 D_80854B18[] = { + { 0, NULL }, + { -1, func_808515A4 }, + { 2, &gPlayerAnim_002790 }, + { 0, NULL }, + { 0, NULL }, + { 3, &gPlayerAnim_002740 }, + { 0, NULL }, + { 0, NULL }, + { -1, func_808515A4 }, + { 2, &gPlayerAnim_002778 }, + { -1, func_80851788 }, + { 3, &gPlayerAnim_002860 }, + { -1, func_808518DC }, + { 7, &gPlayerAnim_002348 }, + { 5, &gPlayerAnim_002350 }, + { 5, &gPlayerAnim_002358 }, + { 5, &gPlayerAnim_0023B0 }, + { 7, &gPlayerAnim_0023B8 }, + { -1, func_808519EC }, + { 2, &gPlayerAnim_002728 }, + { 2, &gPlayerAnim_002738 }, + { 0, NULL }, + { -1, func_80851B90 }, + { 3, &gPlayerAnim_0027A8 }, + { 9, &gPlayerAnim_002DB0 }, + { 2, &gPlayerAnim_002DC0 }, + { -1, func_80851D2C }, + { 2, &gPlayerAnim_003098 }, + { 3, &gPlayerAnim_002780 }, + { -1, func_808515A4 }, + { 2, &gPlayerAnim_003088 }, + { 0, NULL }, + { 0, NULL }, + { 5, &gPlayerAnim_002320 }, + { -1, func_80851368 }, + { -1, func_80851E64 }, + { 5, &gPlayerAnim_002328 }, + { 16, &gPlayerAnim_002F90 }, + { -1, func_80851F84 }, + { -1, func_80851E90 }, + { 6, &gPlayerAnim_002410 }, + { 6, &gPlayerAnim_002418 }, + { -1, func_80852080 }, + { 5, &gPlayerAnim_002390 }, + { -1, func_808521F4 }, + { -1, func_8085225C }, + { -1, func_80852280 }, + { 5, &gPlayerAnim_0023A0 }, + { 5, &gPlayerAnim_002368 }, + { -1, func_808515A4 }, + { 5, &gPlayerAnim_002370 }, + { 5, &gPlayerAnim_0027B0 }, + { 5, &gPlayerAnim_0027B8 }, + { 5, &gPlayerAnim_0027C0 }, + { 3, &gPlayerAnim_002768 }, + { 3, &gPlayerAnim_0027D8 }, + { 4, &gPlayerAnim_0027E0 }, + { 3, &gPlayerAnim_002380 }, + { 3, &gPlayerAnim_002828 }, + { 6, &gPlayerAnim_002470 }, + { 6, &gPlayerAnim_0032A8 }, + { 14, &gPlayerAnim_0032A0 }, + { 3, &gPlayerAnim_0032A0 }, + { 5, &gPlayerAnim_002AE8 }, + { 16, &gPlayerAnim_002450 }, + { 15, &gPlayerAnim_002460 }, + { 15, &gPlayerAnim_002458 }, + { 3, &gPlayerAnim_002440 }, + { 3, &gPlayerAnim_002438 }, + { 3, &gPlayerAnim_002C88 }, + { 6, &gPlayerAnim_003450 }, + { 6, &gPlayerAnim_003448 }, + { 6, &gPlayerAnim_003460 }, + { 6, &gPlayerAnim_003440 }, + { 3, &gPlayerAnim_002798 }, + { 3, &gPlayerAnim_002818 }, + { 4, &gPlayerAnim_002848 }, + { 3, &gPlayerAnim_002850 }, + { 3, &gPlayerAnim_0034E0 }, + { 3, &gPlayerAnim_0034D8 }, + { 6, &gPlayerAnim_0034C8 }, + { 3, &gPlayerAnim_003470 }, + { 3, &gPlayerAnim_003478 }, + { 3, &gPlayerAnim_0034C0 }, + { 3, &gPlayerAnim_003480 }, + { 3, &gPlayerAnim_003490 }, + { 3, &gPlayerAnim_003488 }, + { 3, &gPlayerAnim_003498 }, + { 3, &gPlayerAnim_0034B0 }, + { -1, func_808524B0 }, + { 3, &gPlayerAnim_003420 }, + { -1, func_80852544 }, + { -1, func_80852564 }, + { 3, &gPlayerAnim_003250 }, + { -1, func_80852608 }, + { 3, &gPlayerAnim_002810 }, + { 3, &gPlayerAnim_002838 }, + { 3, &gPlayerAnim_002CD0 }, + { 3, &gPlayerAnim_002CD8 }, + { 3, &gPlayerAnim_002868 }, + { 3, &gPlayerAnim_0027E8 }, + { 3, &gPlayerAnim_0027F8 }, + { 3, &gPlayerAnim_002800 }, +}; + +static struct_80854B18 D_80854E50[] = { + { 0, NULL }, + { -1, func_808514C0 }, + { -1, func_8085157C }, + { -1, func_80851998 }, + { -1, func_808519C0 }, + { 11, NULL }, + { -1, func_80852C50 }, + { -1, func_80852944 }, + { -1, func_80851688 }, + { -1, func_80851750 }, + { -1, func_80851828 }, + { -1, func_808521B8 }, + { -1, func_8085190C }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { 18, D_80854AF0 }, + { 11, NULL }, + { -1, func_80851A50 }, + { 12, &gPlayerAnim_002730 }, + { 11, NULL }, + { 0, NULL }, + { -1, func_80851BE8 }, + { 11, NULL }, + { -1, func_80851CA4 }, + { 11, NULL }, + { 17, &gPlayerAnim_0030A8 }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { -1, func_80851D80 }, + { -1, func_80851DEC }, + { -1, func_80851E28 }, + { 18, D_80854B00 }, + { -1, func_808513BC }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { -1, func_80851ECC }, + { -1, func_80851FB0 }, + { -1, func_80852048 }, + { -1, func_80852174 }, + { 13, &gPlayerAnim_002398 }, + { -1, func_80852234 }, + { 0, NULL }, + { 0, NULL }, + { 11, NULL }, + { -1, func_80852450 }, + { -1, func_80851688 }, + { -1, func_80852298 }, + { 13, &gPlayerAnim_0027D0 }, + { -1, func_80852480 }, + { 13, &gPlayerAnim_0027C8 }, + { -1, func_80852328 }, + { 11, NULL }, + { 11, NULL }, + { 12, &gPlayerAnim_002388 }, + { -1, func_80852358 }, + { 11, NULL }, + { 18, D_80854B14 }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { -1, func_80852388 }, + { 17, &gPlayerAnim_002450 }, + { 12, &gPlayerAnim_002448 }, + { 12, &gPlayerAnim_002450 }, + { 11, NULL }, + { -1, func_808526EC }, + { 17, &gPlayerAnim_003468 }, + { -1, func_808526EC }, + { 17, &gPlayerAnim_003468 }, + { 12, &gPlayerAnim_0027A0 }, + { 12, &gPlayerAnim_002820 }, + { 11, NULL }, + { 12, &gPlayerAnim_002858 }, + { 12, &gPlayerAnim_0034D0 }, + { 13, &gPlayerAnim_0034F0 }, + { 12, &gPlayerAnim_0034E8 }, + { 12, &gPlayerAnim_0034A8 }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { -1, func_80852648 }, + { 11, NULL }, + { 12, &gPlayerAnim_0034A0 }, + { -1, func_808524D0 }, + { -1, func_80852514 }, + { -1, func_80852554 }, + { -1, func_808525C0 }, + { 11, NULL }, + { 11, NULL }, + { 11, NULL }, + { -1, func_8085283C }, + { -1, func_808528C8 }, + { -1, func_808528C8 }, + { 12, &gPlayerAnim_002870 }, + { 12, &gPlayerAnim_0027F0 }, + { 12, &gPlayerAnim_002808 }, + { 12, &gPlayerAnim_002450 }, +}; + +void func_80850ED8(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + func_80832DB0(this); + func_80832B0C(globalCtx, this, anim); + func_80832210(this); +} + +void func_80850F1C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + func_80832DB0(this); + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, (2.0f / 3.0f), 0.0f, Animation_GetLastFrame(anim), + ANIMMODE_ONCE, -8.0f); + func_80832210(this); +} + +void func_80850F9C(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim) { + func_80832DB0(this); + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, (2.0f / 3.0f), 0.0f, 0.0f, ANIMMODE_LOOP, -8.0f); + func_80832210(this); +} + +void func_80851008(GlobalContext* globalCtx, Player* this, void* anim) { + func_80832210(this); +} + +void func_80851030(GlobalContext* globalCtx, Player* this, void* anim) { + func_80850ED8(globalCtx, this, anim); +} + +void func_80851050(GlobalContext* globalCtx, Player* this, void* anim) { + func_80832DB0(this); + func_80832C2C(globalCtx, this, anim); + func_80832210(this); +} + +void func_80851094(GlobalContext* globalCtx, Player* this, void* anim) { + func_80850F1C(globalCtx, this, anim); +} + +void func_808510B4(GlobalContext* globalCtx, Player* this, void* anim) { + func_80850F9C(globalCtx, this, anim); +} + +void func_808510D4(GlobalContext* globalCtx, Player* this, void* anim) { + func_8083308C(globalCtx, this, anim); +} + +void func_808510F4(GlobalContext* globalCtx, Player* this, void* anim) { + func_8083303C(globalCtx, this, anim, 0x9C); +} + +void func_80851114(GlobalContext* globalCtx, Player* this, void* anim) { + func_8083313C(globalCtx, this, anim); +} + +void func_80851134(GlobalContext* globalCtx, Player* this, void* anim) { + func_808330EC(globalCtx, this, anim, 0x9C); +} + +void func_80851154(GlobalContext* globalCtx, Player* this, void* anim) { + func_80832264(globalCtx, this, anim); +} + +void func_80851174(GlobalContext* globalCtx, Player* this, void* anim) { + func_80832284(globalCtx, this, anim); +} + +void func_80851194(GlobalContext* globalCtx, Player* this, void* anim) { + func_808322D0(globalCtx, this, anim); +} + +void func_808511B4(GlobalContext* globalCtx, Player* this, void* anim) { + func_808322A4(globalCtx, this, anim); +} + +void func_808511D4(GlobalContext* globalCtx, Player* this, void* anim) { + LinkAnimation_Update(globalCtx, &this->skelAnime); +} + +void func_808511FC(GlobalContext* globalCtx, Player* this, void* anim) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80850F9C(globalCtx, this, anim); + this->unk_850 = 1; + } +} + +void func_80851248(GlobalContext* globalCtx, Player* this, void* anim) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832DBC(this); + func_808322A4(globalCtx, this, anim); + } +} + +void func_80851294(GlobalContext* globalCtx, Player* this, void* anim) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083313C(globalCtx, this, anim); + this->unk_850 = 1; + } +} + +void func_808512E0(GlobalContext* globalCtx, Player* this, void* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_80832924(this, arg2); +} + +void func_80851314(Player* this) { + if ((this->unk_448 == NULL) || (this->unk_448->update == NULL)) { + this->unk_448 = NULL; + } + + this->unk_664 = this->unk_448; + + if (this->unk_664 != NULL) { + this->actor.shape.rot.y = func_8083DB98(this, 0); + } +} + +void func_80851368(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->stateFlags1 |= PLAYER_STATE1_27; + this->stateFlags2 |= PLAYER_STATE2_10; + this->stateFlags1 &= ~(PLAYER_STATE1_18 | PLAYER_STATE1_19); + + func_80832284(globalCtx, this, &gPlayerAnim_0032F0); +} + +void func_808513BC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->actor.gravity = 0.0f; + + if (this->unk_84F == 0) { + if (func_8083D12C(globalCtx, this, NULL)) { + this->unk_84F = 1; + } + else { + func_8084B158(globalCtx, this, NULL, fabsf(this->actor.velocity.y)); + Math_ScaledStepToS(&this->unk_6C2, -10000, 800); + func_8084AEEC(this, &this->actor.velocity.y, 4.0f, this->currentYaw); + } + return; + } + + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + if (this->unk_84F == 1) { + func_80832C6C(globalCtx, this, &gPlayerAnim_003328); + } + else { + func_80832284(globalCtx, this, &gPlayerAnim_003328); + } + } + + func_8084B000(this); + func_8084AEEC(this, &this->linearVelocity, 0.0f, this->actor.shape.rot.y); +} + +void func_808514C0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80851314(this); + + if (func_808332B8(this)) { + func_808513BC(globalCtx, this, 0); + return; + } + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (func_8008F128(this) || (this->stateFlags1 & PLAYER_STATE1_11)) { + func_80836670(this, globalCtx); + return; + } + + if ((this->interactRangeActor != NULL) && (this->interactRangeActor->textId == 0xFFFF)) { + func_8083E5A8(this, globalCtx); + } +} + +void func_8085157C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); +} + +void func_808515A4(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimationHeader* anim; + + if (func_808332B8(this)) { + func_80851368(globalCtx, this, 0); + return; + } + + anim = D_80853914[PLAYER_ANIMGROUP_44][this->modelAnimType]; + + if ((this->unk_446 == 6) || (this->unk_446 == 0x2E)) { + func_80832264(globalCtx, this, anim); + } + else { + func_80832DB0(this); + LinkAnimation_Change(globalCtx, &this->skelAnime, anim, (2.0f / 3.0f), 0.0f, Animation_GetLastFrame(anim), + ANIMMODE_LOOP, -4.0f); + } + + func_80832210(this); +} + +void func_80851688(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (func_8084B3CC(globalCtx, this) == 0) { + if ((this->csMode == 0x31) && (globalCtx->csCtx.state == CS_STATE_IDLE)) { + func_8002DF54(globalCtx, NULL, 7); + return; + } + + if (func_808332B8(this) != 0) { + func_808513BC(globalCtx, this, 0); + return; + } + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (func_8008F128(this) || (this->stateFlags1 & PLAYER_STATE1_11)) { + func_80836670(this, globalCtx); + } + } +} + +static struct_80832924 D_80855188[] = { + { 0, 0x302A }, + { 0, -0x3030 }, +}; + +void func_80851750(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_80832924(this, D_80855188); +} + +void func_80851788(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->stateFlags1 &= ~PLAYER_STATE1_25; + + this->currentYaw = this->actor.shape.rot.y = this->actor.world.rot.y = + Math_Vec3f_Yaw(&this->actor.world.pos, &this->unk_450); + + if (this->linearVelocity <= 0.0f) { + this->linearVelocity = 0.1f; + } + else if (this->linearVelocity > 2.5f) { + this->linearVelocity = 2.5f; + } +} + +void func_80851828(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + f32 sp1C = 2.5f; + + func_80845BA0(globalCtx, this, &sp1C, 10); + + if (globalCtx->sceneNum == SCENE_BDAN_BOSS) { + if (this->unk_850 == 0) { + if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_NONE) { + return; + } + } + else { + if (Message_GetState(&globalCtx->msgCtx) != TEXT_STATE_NONE) { + return; + } + } + } + + this->unk_850++; + if (this->unk_850 > 20) { + this->csMode = 0xB; + } +} + +void func_808518DC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_8083CEAC(this, globalCtx); +} + +void func_8085190C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80851314(this); + + if (this->unk_850 != 0) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80832284(globalCtx, this, func_808334E4(this)); + this->unk_850 = 0; + } + + func_80833C3C(this); + } + else { + func_808401B0(globalCtx, this); + } +} + +void func_80851998(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80845964(globalCtx, this, arg2, 0.0f, 0, 0); +} + +void func_808519C0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80845964(globalCtx, this, arg2, 0.0f, 0, 1); +} + +// unused +static LinkAnimationHeader* D_80855190[] = { + &gPlayerAnim_002720, + &gPlayerAnim_002360, +}; + +static Vec3f D_80855198 = { -1.0f, 70.0f, 20.0f }; + +void func_808519EC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + Math_Vec3f_Copy(&this->actor.world.pos, &D_80855198); + this->actor.shape.rot.y = -0x8000; + func_808322D0(globalCtx, this, this->ageProperties->unk_9C); + func_80832F54(globalCtx, this, 0x28F); +} + +static struct_808551A4 D_808551A4[] = { + { NA_SE_IT_SWORD_PUTAWAY_STN, 0 }, + { NA_SE_IT_SWORD_STICK_STN, NA_SE_VO_LI_SWORD_N }, +}; + +static struct_80832924 D_808551AC[] = { + { 0, 0x401D }, + { 0, -0x4027 }, +}; + +void func_80851A50(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + struct_808551A4* sp2C; + Gfx** dLists; + + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if ((LINK_IS_ADULT && LinkAnimation_OnFrame(&this->skelAnime, 70.0f)) || + (!LINK_IS_ADULT && LinkAnimation_OnFrame(&this->skelAnime, 87.0f))) { + sp2C = &D_808551A4[gSaveContext.linkAge]; + this->interactRangeActor->parent = &this->actor; + + if (!LINK_IS_ADULT) { + dLists = D_80125DE8; + } + else { + dLists = D_80125E18; + } + this->leftHandDLists = &dLists[gSaveContext.linkAge]; + + func_8002F7DC(&this->actor, sp2C->unk_00); + if (!LINK_IS_ADULT) { + func_80832698(this, sp2C->unk_02); + } + } + else if (LINK_IS_ADULT) { + if (LinkAnimation_OnFrame(&this->skelAnime, 66.0f)) { + func_80832698(this, NA_SE_VO_LI_SWORD_L); + } + } + else { + func_80832924(this, D_808551AC); + } +} + +void func_80851B90(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_002860, -(2.0f / 3.0f), 12.0f, 12.0f, ANIMMODE_ONCE, + 0.0f); +} + +static struct_80832924 D_808551B4[] = { + { 0, -0x281E }, +}; + +void func_80851BE8(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + + this->unk_850++; + + if (this->unk_850 >= 180) { + if (this->unk_850 == 180) { + LinkAnimation_Change(globalCtx, &this->skelAnime, &gPlayerAnim_003298, (2.0f / 3.0f), 10.0f, + Animation_GetLastFrame(&gPlayerAnim_003298), ANIMMODE_ONCE, -8.0f); + } + func_80832924(this, D_808551B4); + } +} + +void func_80851CA4(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime) && (this->unk_850 == 0) && (this->actor.bgCheckFlags & 1)) { + func_80832264(globalCtx, this, &gPlayerAnim_002DB8); + this->unk_850 = 1; + } + + if (this->unk_850 != 0) { + func_8083721C(this); + } +} + +void func_80851D2C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80850F1C(globalCtx, this, &gPlayerAnim_0030A0); + func_8084B498(this); + Player_SetModels(this, Player_ActionToModelGroup(this, this->itemActionParam)); +} + +static struct_80832924 D_808551B8[] = { + { NA_SE_IT_SWORD_PICKOUT, -0x80C }, +}; + +void func_80851D80(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (LinkAnimation_OnFrame(&this->skelAnime, 6.0f)) { + func_80846720(globalCtx, this, 0); + } + else { + func_80832924(this, D_808551B8); + } +} + +void func_80851DEC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + Math_StepToS(&this->actor.shape.face, 0, 1); +} + +void func_80851E28(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + Math_StepToS(&this->actor.shape.face, 2, 1); +} + +void func_80851E64(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80833064(globalCtx, this, &gPlayerAnim_003318, 0x98); +} + +void func_80851E90(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_8083303C(globalCtx, this, &gPlayerAnim_002408, 0x9C); + func_80832698(this, NA_SE_VO_LI_GROAN); +} + +void func_80851ECC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_808330EC(globalCtx, this, &gPlayerAnim_002428, 0x9C); + } +} + +void func_80851F14(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, struct_80832924* arg3) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_808322A4(globalCtx, this, anim); + this->unk_850 = 1; + } + else if (this->unk_850 == 0) { + func_80832924(this, arg3); + } +} + +void func_80851F84(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->actor.shape.shadowDraw = NULL; + func_80851134(globalCtx, this, &gPlayerAnim_002420); +} + +static struct_80832924 D_808551BC[] = { + { NA_SE_VO_LI_RELAX, 0x2023 }, + { NA_SE_PL_SLIPDOWN, 0x8EC }, + { NA_SE_PL_SLIPDOWN, -0x900 }, +}; + +void func_80851FB0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_808330EC(globalCtx, this, &gPlayerAnim_002430, 0x9C); + this->unk_850 = 1; + } + else if (this->unk_850 == 0) { + func_80832924(this, D_808551BC); + if (LinkAnimation_OnFrame(&this->skelAnime, 240.0f)) { + this->actor.shape.shadowDraw = ActorShadow_DrawFeet; + } + } +} + +static struct_80832924 D_808551C8[] = { + { NA_SE_PL_LAND_LADDER, 0x843 }, + { 0, 0x4854 }, + { 0, 0x485A }, + { 0, -0x4860 }, +}; + +void func_80852048(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_80832924(this, D_808551C8); +} + +void func_80852080(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80833064(globalCtx, this, &gPlayerAnim_002340, 0x9D); + func_80832698(this, NA_SE_VO_LI_FALL_L); +} + +void func_808520BC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + f32 startX = arg2->startPos.x; + f32 startY = arg2->startPos.y; + f32 startZ = arg2->startPos.z; + f32 distX = (arg2->endPos.x - startX); + f32 distY = (arg2->endPos.y - startY); + f32 distZ = (arg2->endPos.z - startZ); + f32 sp4 = (f32)(globalCtx->csCtx.frames - arg2->startFrame) / (f32)(arg2->endFrame - arg2->startFrame); + + this->actor.world.pos.x = distX * sp4 + startX; + this->actor.world.pos.y = distY * sp4 + startY; + this->actor.world.pos.z = distZ * sp4 + startZ; +} + +static struct_80832924 D_808551D8[] = { + { NA_SE_PL_BOUND, 0x1014 }, + { NA_SE_PL_BOUND, -0x101E }, +}; + +void func_80852174(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_808520BC(globalCtx, this, arg2); + LinkAnimation_Update(globalCtx, &this->skelAnime); + func_80832924(this, D_808551D8); +} + +void func_808521B8(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (arg2 != NULL) { + func_808520BC(globalCtx, this, arg2); + } + LinkAnimation_Update(globalCtx, &this->skelAnime); +} + +void func_808521F4(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80832B0C(globalCtx, this, D_80853914[PLAYER_ANIMGROUP_44][this->modelAnimType]); + func_80832210(this); +} + +void func_80852234(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); +} + +void func_8085225C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80832F54(globalCtx, this, 0x98); +} + +void func_80852280(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->actor.draw = Player_Draw; +} + +void func_80852298(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8083313C(globalCtx, this, &gPlayerAnim_002378); + this->unk_850 = 1; + } + else if (this->unk_850 == 0) { + if (LinkAnimation_OnFrame(&this->skelAnime, 10.0f)) { + func_80846720(globalCtx, this, 1); + } + } +} + +static struct_80832924 D_808551E0[] = { + { 0, 0x300A }, + { 0, -0x3018 }, +}; + +void func_80852328(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80851F14(globalCtx, this, &gPlayerAnim_002770, D_808551E0); +} + +static struct_80832924 D_808551E8[] = { + { 0, 0x400F }, + { 0, -0x4023 }, +}; + +void func_80852358(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80851F14(globalCtx, this, &gPlayerAnim_002830, D_808551E8); +} + +void func_80852388(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_808322A4(globalCtx, this, &gPlayerAnim_002468); + this->unk_850 = 1; + } + + if ((this->unk_850 != 0) && (globalCtx->csCtx.frames >= 900)) { + this->rightHandType = PLAYER_MODELTYPE_LH_OPEN; + } + else { + this->rightHandType = PLAYER_MODELTYPE_RH_FF; + } +} + +void func_80852414(GlobalContext* globalCtx, Player* this, LinkAnimationHeader* anim, struct_80832924* arg3) { + func_80851294(globalCtx, this, anim); + if (this->unk_850 == 0) { + func_80832924(this, arg3); + } +} + +static struct_80832924 D_808551F0[] = { + { 0, 0x300F }, + { 0, -0x3021 }, +}; + +void func_80852450(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80852414(globalCtx, this, &gPlayerAnim_002378, D_808551F0); +} + +static struct_80832924 D_808551F8[] = { + { NA_SE_PL_KNOCK, -0x84E }, +}; + +void func_80852480(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80852414(globalCtx, this, &gPlayerAnim_0027D0, D_808551F8); +} + +void func_808524B0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80837704(globalCtx, this); +} + +void func_808524D0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + sControlInput->press.button |= BTN_B; + + func_80844E68(this, globalCtx); +} + +void func_80852514(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80844E68(this, globalCtx); +} + +void func_80852544(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { +} + +void func_80852554(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { +} + +void func_80852564(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->stateFlags3 |= PLAYER_STATE3_1; + this->linearVelocity = 2.0f; + this->actor.velocity.y = -1.0f; + + func_80832264(globalCtx, this, &gPlayerAnim_002DB0); + func_80832698(this, NA_SE_VO_LI_FALL_L); +} + +static void (*D_808551FC[])(Player* this, GlobalContext* globalCtx) = { + func_8084377C, + func_80843954, + func_80843A38, +}; + +void func_808525C0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + D_808551FC[this->unk_850](this, globalCtx); +} + +void func_80852608(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + func_80846720(globalCtx, this, 0); + func_808322D0(globalCtx, this, &gPlayerAnim_002838); +} + +void func_80852648(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + LinkAnimation_Update(globalCtx, &this->skelAnime); + + if (LinkAnimation_OnFrame(&this->skelAnime, 10.0f)) { + this->heldItemActionParam = this->itemActionParam = PLAYER_AP_NONE; + this->heldItemId = ITEM_NONE; + this->modelGroup = this->nextModelGroup = Player_ActionToModelGroup(this, PLAYER_AP_NONE); + this->leftHandDLists = D_80125E08; + Inventory_ChangeEquipment(EQUIP_SWORD, 2); + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; + Inventory_DeleteEquipment(globalCtx, 0); + } +} + +static LinkAnimationHeader* D_80855208[] = { + &gPlayerAnim_0034B8, + &gPlayerAnim_003458, +}; + +static Vec3s D_80855210[2][2] = { + { { -200, 700, 100 }, { 800, 600, 800 } }, + { { -200, 500, 0 }, { 600, 400, 600 } }, +}; + +void func_808526EC(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + static Color_RGBA8 primColor = { 255, 255, 255, 0 }; + static Color_RGBA8 envColor = { 0, 128, 128, 0 }; + s32 age = gSaveContext.linkAge; + Vec3f sparklePos; + Vec3f sp34; + Vec3s* ptr; + + func_80851294(globalCtx, this, D_80855208[age]); + + if (this->rightHandType != PLAYER_MODELTYPE_RH_FF) { + this->rightHandType = PLAYER_MODELTYPE_RH_FF; + return; + } + + ptr = D_80855210[gSaveContext.linkAge]; + + sp34.x = ptr[0].x + Rand_CenteredFloat(ptr[1].x); + sp34.y = ptr[0].y + Rand_CenteredFloat(ptr[1].y); + sp34.z = ptr[0].z + Rand_CenteredFloat(ptr[1].z); + + SkinMatrix_Vec3fMtxFMultXYZ(&this->shieldMf, &sp34, &sparklePos); + + EffectSsKiraKira_SpawnDispersed(globalCtx, &sparklePos, &zeroVec, &zeroVec, &primColor, &envColor, 600, -10); +} + +void func_8085283C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_80852944(globalCtx, this, arg2); + } + else if (this->unk_850 == 0) { + Item_Give(globalCtx, ITEM_SWORD_MASTER); + func_80846720(globalCtx, this, 0); + } + else { + func_8084E988(this); + } +} + +void func_808528C8(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (LinkAnimation_Update(globalCtx, &this->skelAnime)) { + func_8084285C(this, 0.0f, 99.0f, this->skelAnime.endFrame - 8.0f); + } + + if (this->heldItemActionParam != PLAYER_AP_SWORD_MASTER) { + func_80846720(globalCtx, this, 1); + } +} + +void func_80852944(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + if (func_808332B8(this)) { + func_80838F18(globalCtx, this); + func_80832340(globalCtx, this); + } + else { + func_8083C148(this, globalCtx); + if (!func_8083B644(this, globalCtx)) { + func_8083E5A8(this, globalCtx); + } + } + + this->csMode = 0; + this->unk_6AD = 0; +} + +void func_808529D0(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + this->actor.world.pos.x = arg2->startPos.x; + this->actor.world.pos.y = arg2->startPos.y; + if ((globalCtx->sceneNum == SCENE_SPOT04) && !LINK_IS_ADULT) { + this->actor.world.pos.y -= 1.0f; + } + this->actor.world.pos.z = arg2->startPos.z; + this->currentYaw = this->actor.shape.rot.y = arg2->rot.y; +} + +void func_80852A54(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + f32 dx = arg2->startPos.x - (s32)this->actor.world.pos.x; + f32 dy = arg2->startPos.y - (s32)this->actor.world.pos.y; + f32 dz = arg2->startPos.z - (s32)this->actor.world.pos.z; + f32 dist = sqrtf(SQ(dx) + SQ(dy) + SQ(dz)); + s16 yawDiff = arg2->rot.y - this->actor.shape.rot.y; + + if ((this->linearVelocity == 0.0f) && ((dist > 50.0f) || (ABS(yawDiff) > 0x4000))) { + func_808529D0(globalCtx, this, arg2); + } + + this->skelAnime.moveFlags = 0; + func_80832DB0(this); +} + +void func_80852B4C(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2, struct_80854B18* arg3) { + if (arg3->type > 0) { + D_80854AA4[arg3->type](globalCtx, this, arg3->ptr); + } + else if (arg3->type < 0) { + arg3->func(globalCtx, this, arg2); + } + + if ((D_80858AA0 & 4) && !(this->skelAnime.moveFlags & 4)) { + this->skelAnime.morphTable[0].y /= this->ageProperties->unk_08; + D_80858AA0 = 0; + } +} + +void func_80852C0C(GlobalContext* globalCtx, Player* this, s32 csMode) { + if ((csMode != 1) && (csMode != 8) && (csMode != 0x31) && (csMode != 7)) { + func_808323B4(globalCtx, this); + } +} + +void func_80852C50(GlobalContext* globalCtx, Player* this, CsCmdActorAction* arg2) { + CsCmdActorAction* linkCsAction = globalCtx->csCtx.linkAction; + s32 pad; + s32 sp24; + + if (globalCtx->csCtx.state == CS_STATE_UNSKIPPABLE_INIT) { + func_8002DF54(globalCtx, NULL, 7); + this->unk_446 = 0; + func_80832210(this); + return; + } + + if (linkCsAction == NULL) { + this->actor.flags &= ~ACTOR_FLAG_6; + return; + } + + if (this->unk_446 != linkCsAction->action) { + sp24 = D_808547C4[linkCsAction->action]; + if (sp24 >= 0) { + if ((sp24 == 3) || (sp24 == 4)) { + func_80852A54(globalCtx, this, linkCsAction); + } + else { + func_808529D0(globalCtx, this, linkCsAction); + } + } + + D_80858AA0 = this->skelAnime.moveFlags; + + func_80832DBC(this); + osSyncPrintf("TOOL MODE=%d\n", sp24); + func_80852C0C(globalCtx, this, ABS(sp24)); + func_80852B4C(globalCtx, this, linkCsAction, &D_80854B18[ABS(sp24)]); + + this->unk_850 = 0; + this->unk_84F = 0; + this->unk_446 = linkCsAction->action; + } + + sp24 = D_808547C4[this->unk_446]; + func_80852B4C(globalCtx, this, linkCsAction, &D_80854E50[ABS(sp24)]); +} + +void func_80852E14(Player* this, GlobalContext* globalCtx) { + if (this->csMode != this->prevCsMode) { + D_80858AA0 = this->skelAnime.moveFlags; + + func_80832DBC(this); + this->prevCsMode = this->csMode; + osSyncPrintf("DEMO MODE=%d\n", this->csMode); + func_80852C0C(globalCtx, this, this->csMode); + func_80852B4C(globalCtx, this, NULL, &D_80854B18[this->csMode]); + } + + func_80852B4C(globalCtx, this, NULL, &D_80854E50[this->csMode]); +} + +s32 Player_IsDroppingFish(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + return (func_8084EFC0 == this->func_674) && (this->itemActionParam == PLAYER_AP_BOTTLE_FISH); +} + +s32 Player_StartFishing(GlobalContext* globalCtx) { + Player* this = GET_PLAYER(globalCtx); + + func_80832564(globalCtx, this); + func_80835F44(globalCtx, this, ITEM_FISHING_POLE); + return 1; +} + +s32 func_80852F38(GlobalContext* globalCtx, Player* this) { + if (!Player_InBlockingCsMode(globalCtx, this) && (this->invincibilityTimer >= 0) && !func_8008F128(this) && + !(this->stateFlags3 & PLAYER_STATE3_7)) { + func_80832564(globalCtx, this); + func_80835C58(globalCtx, this, func_8084F308, 0); + func_80832264(globalCtx, this, &gPlayerAnim_003120); + this->stateFlags2 |= PLAYER_STATE2_7; + func_80832224(this); + func_80832698(this, NA_SE_VO_LI_HELD); + return true; + } + + return false; +} + +// Sets up player cutscene +s32 func_80852FFC(GlobalContext* globalCtx, Actor* actor, s32 csMode) { + Player* this = GET_PLAYER(globalCtx); + + if (!Player_InBlockingCsMode(globalCtx, this)) { + func_80832564(globalCtx, this); + func_80835C58(globalCtx, this, func_80852E14, 0); + this->csMode = csMode; + this->unk_448 = actor; + func_80832224(this); + return 1; + } + + return 0; +} + +void func_80853080(Player* this, GlobalContext* globalCtx) { + func_80835C58(globalCtx, this, func_80840BC8, 1); + func_80832B0C(globalCtx, this, func_80833338(this)); + this->currentYaw = this->actor.shape.rot.y; +} + +s32 Player_InflictDamage(GlobalContext* globalCtx, s32 damage) { + Player* this = GET_PLAYER(globalCtx); + + if (!Player_InBlockingCsMode(globalCtx, this) && !func_80837B18(globalCtx, this, damage)) { + this->stateFlags2 &= ~PLAYER_STATE2_7; + return 1; + } + + return 0; +} + +// Start talking with the given actor +void func_80853148(GlobalContext* globalCtx, Actor* actor) { + Player* this = GET_PLAYER(globalCtx); + s32 pad; + + if ((this->targetActor != NULL) || (actor == this->naviActor) || + CHECK_FLAG_ALL(actor->flags, ACTOR_FLAG_0 | ACTOR_FLAG_18)) { + actor->flags |= ACTOR_FLAG_8; + } + + this->targetActor = actor; + this->exchangeItemId = EXCH_ITEM_NONE; + + if (actor->textId == 0xFFFF) { + func_8002DF54(globalCtx, actor, 1); + actor->flags |= ACTOR_FLAG_8; + func_80832528(globalCtx, this); + } + else { + if (this->actor.flags & ACTOR_FLAG_8) { + this->actor.textId = 0; + } + else { + this->actor.flags |= ACTOR_FLAG_8; + this->actor.textId = actor->textId; + } + + if (this->stateFlags1 & PLAYER_STATE1_23) { + s32 sp24 = this->unk_850; + + func_80832528(globalCtx, this); + func_8083A2F8(globalCtx, this); + + this->unk_850 = sp24; + } + else { + if (func_808332B8(this)) { + func_80836898(globalCtx, this, func_8083A2F8); + func_80832C6C(globalCtx, this, &gPlayerAnim_003328); + } + else if ((actor->category != ACTORCAT_NPC) || (this->heldItemActionParam == PLAYER_AP_FISHING_POLE)) { + func_8083A2F8(globalCtx, this); + + if (!func_8008E9C4(this)) { + if ((actor != this->naviActor) && (actor->xzDistToPlayer < 40.0f)) { + func_808322D0(globalCtx, this, &gPlayerAnim_002DF0); + } + else { + func_80832284(globalCtx, this, func_80833338(this)); + } + } + } + else { + func_80836898(globalCtx, this, func_8083A2F8); + func_808322D0(globalCtx, this, + (actor->xzDistToPlayer < 40.0f) ? &gPlayerAnim_002DF0 : &gPlayerAnim_0031A0); + } + + if (this->skelAnime.animation == &gPlayerAnim_002DF0) { + func_80832F54(globalCtx, this, 0x19); + } + + func_80832224(this); + } + + this->stateFlags1 |= PLAYER_STATE1_6 | PLAYER_STATE1_29; + } + + if ((this->naviActor == this->targetActor) && ((this->targetActor->textId & 0xFF00) != 0x200)) { + this->naviActor->flags |= ACTOR_FLAG_8; + func_80835EA4(globalCtx, 0xB); + } +} \ No newline at end of file diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.c b/soh/src/overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.c new file mode 100644 index 000000000..b3671e9e7 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.c @@ -0,0 +1,88 @@ +/* + * File: z_eff_ss_blast.c + * Overlay: ovl_Effect_Ss_Blast + * Description: Shockwave Effect + */ + +#include "z_eff_ss_blast.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rAlphaTarget regs[8] +#define rScale regs[9] +#define rScaleStep regs[10] +#define rScaleStepDecay regs[11] + +u32 EffectSsBlast_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsBlast_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsBlast_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Blast_InitVars = { + EFFECT_SS_BLAST, + EffectSsBlast_Init, +}; + +u32 EffectSsBlast_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsBlastParams* initParams = (EffectSsBlastParams*)initParamsx; + + this->pos = initParams->pos; + this->pos.y += 5.0f; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffShockwaveDL); + this->life = initParams->life; + this->draw = EffectSsBlast_Draw; + this->update = EffectSsBlast_Update; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rEnvColorA = initParams->envColor.a; + this->rAlphaTarget = initParams->primColor.a / initParams->life; + this->rScale = initParams->scale; + this->rScaleStep = initParams->scaleStep; + this->rScaleStepDecay = initParams->sclaeStepDecay; + return 1; +} + +void EffectSsBlast_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mf; + s32 pad; + f32 radius; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_blast.c", 170); + + radius = this->rScale * 0.0025f; + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + func_800BFCB8(globalCtx, &mf, &this->pos); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, this->rPrimColorA); + Matrix_Put(&mf); + Matrix_Scale(radius, radius, radius, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_blast.c", 199), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_blast.c", 204); +} + +void EffectSsBlast_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + Math_StepToS(&this->rPrimColorA, 0, this->rAlphaTarget); + this->rScale += this->rScaleStep; + + if (this->rScaleStep != 0) { + this->rScaleStep -= this->rScaleStepDecay; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.h b/soh/src/overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.h new file mode 100644 index 000000000..a22d3092d --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Blast/z_eff_ss_blast.h @@ -0,0 +1,19 @@ +#ifndef Z_EFF_SS_BLAST_H +#define Z_EFF_SS_BLAST_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ Color_RGBA8 primColor; + /* 0x28 */ Color_RGBA8 envColor; + /* 0x2C */ s16 scale; + /* 0x2E */ s16 scaleStep; + /* 0x30 */ s16 sclaeStepDecay; + /* 0x32 */ s16 life; +} EffectSsBlastParams; // size = 0x34 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.c b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.c new file mode 100644 index 000000000..6410907b6 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.c @@ -0,0 +1,95 @@ +/* + * File: z_eff_ss_bomb.c + * Overlay: ovl_Effect_Ss_Bomb + * Description: Bomb Blast. Unused in the orignal game. + */ + +#include "z_eff_ss_bomb.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] +#define rTexIdx regs[1] + +u32 EffectSsBomb_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsBomb_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsBomb_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Bomb_InitVars = { + EFFECT_SS_BOMB, + EffectSsBomb_Init, +}; + +u32 EffectSsBomb_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsBombInitParams* initParams = (EffectSsBombInitParams*)initParamsx; + + Math_Vec3f_Copy(&this->pos, &initParams->pos); + Math_Vec3f_Copy(&this->velocity, &initParams->velocity); + Math_Vec3f_Copy(&this->accel, &initParams->accel); + this->gfx = SEGMENTED_TO_VIRTUAL(gEffBombExplosion1DL); + this->life = 20; + this->draw = EffectSsBomb_Draw; + this->update = EffectSsBomb_Update; + this->rScale = 100; + this->rTexIdx = 0; + + return 1; +} + +void EffectSsBomb_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* explosionTextures[] = { + gEffBombExplosion1Tex, + gEffBombExplosion2Tex, + gEffBombExplosion3Tex, + gEffBombExplosion4Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + Mtx* mtx; + s32 pad; + f32 scale; + s16 color; + + if (1) {} + + OPEN_DISPS(gfxCtx, "../z_eff_ss_bomb.c", 168); + + scale = this->rScale / 100.0f; + + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(explosionTextures[this->rTexIdx])); + gDPPipeSync(POLY_XLU_DISP++); + func_80094C50(gfxCtx); + color = this->life * 12.75f; + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, color, color, color, color); + gDPPipeSync(POLY_XLU_DISP++); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + gDPPipeSync(POLY_XLU_DISP++); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_bomb.c", 214); +} + +void EffectSsBomb_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if ((this->life < 21) && (this->life > 16)) { + this->rTexIdx = (20 - this->life); + } else { + this->rScale += 0; + this->rTexIdx = 3; + } + + this->accel.x = ((Rand_ZeroOne() * 0.4f) - 0.2f); + this->accel.z = ((Rand_ZeroOne() * 0.4f) - 0.2f); +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.h b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.h new file mode 100644 index 000000000..1f7abc2f9 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb/z_eff_ss_bomb.h @@ -0,0 +1,13 @@ +#ifndef Z_EFF_SS_BOMB_H +#define Z_EFF_SS_BOMB_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; +} EffectSsBombInitParams; // size = 0x24 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.c b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.c new file mode 100644 index 000000000..6ed20efdd --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.c @@ -0,0 +1,198 @@ +/* + * File: z_eff_ss_bomb2.c + * Overlay: ovl_Effect_Ss_Bomb2 + * Description: Bomb Blast + */ + +#include "z_eff_ss_bomb2.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] +#define rTexIdx regs[1] +#define rPrimColorR regs[2] +#define rPrimColorG regs[3] +#define rPrimColorB regs[4] +#define rPrimColorA regs[5] +#define rEnvColorR regs[6] +#define rEnvColorG regs[7] +#define rEnvColorB regs[8] +#define rScaleStep regs[9] +#define rDepth regs[10] + +u32 EffectSsBomb2_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsBomb2_DrawFade(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsBomb2_DrawLayered(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsBomb2_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Bomb2_InitVars = { + EFFECT_SS_BOMB2, + EffectSsBomb2_Init, +}; + +static EffectSsDrawFunc sDrawFuncs[] = { + EffectSsBomb2_DrawFade, + EffectSsBomb2_DrawLayered, +}; + +u32 EffectSsBomb2_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + + EffectSsBomb2InitParams* initParams = (EffectSsBomb2InitParams*)initParamsx; + + Math_Vec3f_Copy(&this->pos, &initParams->pos); + Math_Vec3f_Copy(&this->velocity, &initParams->velocity); + Math_Vec3f_Copy(&this->accel, &initParams->accel); + this->gfx = SEGMENTED_TO_VIRTUAL(gEffBombExplosion1DL); + this->life = 24; + this->update = EffectSsBomb2_Update; + this->draw = sDrawFuncs[initParams->drawMode]; + this->rScale = initParams->scale; + this->rScaleStep = initParams->scaleStep; + this->rPrimColorR = 255; + this->rPrimColorG = 255; + this->rPrimColorB = 255; + this->rPrimColorA = 255; + this->rEnvColorR = 0; + this->rEnvColorG = 0; + this->rEnvColorB = 200; + + return 1; +} + +// unused in the original game. looks like EffectSsBomb but with color +void EffectSsBomb2_DrawFade(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* textures[] = { + gEffBombExplosion1Tex, gEffBombExplosion2Tex, gEffBombExplosion3Tex, gEffBombExplosion4Tex, + gEffBombExplosion5Tex, gEffBombExplosion6Tex, gEffBombExplosion7Tex, gEffBombExplosion8Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + Mtx* mtx; + s32 pad; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_bomb2.c", 298); + + scale = this->rScale * 0.01f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80094BC4(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, 0); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(textures[this->rTexIdx])); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + if (1) {} + if (1) {} + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_bomb2.c", 345); +} + +void EffectSsBomb2_DrawLayered(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* textures[] = { + gEffBombExplosion1Tex, gEffBombExplosion2Tex, gEffBombExplosion3Tex, gEffBombExplosion4Tex, + gEffBombExplosion5Tex, gEffBombExplosion6Tex, gEffBombExplosion7Tex, gEffBombExplosion8Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + MtxF mtx2F; + Mtx* mtx2; + Mtx* mtx; + s32 pad[3]; + f32 scale; + f32 depth; + f32 layer2Scale = 0.925f; + s32 i; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_bomb2.c", 386); + + depth = this->rDepth; + scale = this->rScale * 0.01f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + mtx2 = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx2 != NULL) { + func_80094BC4(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, 0); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(textures[this->rTexIdx])); + gSPDisplayList(POLY_XLU_DISP++, gEffBombExplosion2DL); + gSPDisplayList(POLY_XLU_DISP++, gEffBombExplosion3DL); + + Matrix_MtxToMtxF(mtx2, &mtx2F); + Matrix_Put(&mtx2F); + + for (i = 1; i >= 0; i--) { + Matrix_Translate(0.0f, 0.0f, depth, MTXMODE_APPLY); + Matrix_RotateZ((this->life * 0.02f) + 180.0f, MTXMODE_APPLY); + Matrix_Scale(layer2Scale, layer2Scale, layer2Scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_ss_bomb2.c", 448), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffBombExplosion3DL); + layer2Scale -= 0.15f; + } + } + } + + if (1) {} + if (1) {} + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_bomb2.c", 456); +} + +void EffectSsBomb2_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 divisor; + + this->rTexIdx = (23 - this->life) / 3; + this->rScale += this->rScaleStep; + + if (this->rScaleStep == 30) { + this->rDepth += 4.0f; + } else { + this->rDepth += 2.0f; + } + + if ((this->life < 23) && (this->life > 13)) { + divisor = this->life - 13; + this->rPrimColorR = func_80027DD4(this->rPrimColorR, 255, divisor); + this->rPrimColorG = func_80027DD4(this->rPrimColorG, 255, divisor); + this->rPrimColorB = func_80027DD4(this->rPrimColorB, 150, divisor); + this->rPrimColorA = func_80027DD4(this->rPrimColorA, 255, divisor); + this->rEnvColorR = func_80027DD4(this->rEnvColorR, 150, divisor); + this->rEnvColorG = func_80027DD4(this->rEnvColorG, 0, divisor); + this->rEnvColorB = func_80027DD4(this->rEnvColorB, 0, divisor); + } else if ((this->life < 14) && (this->life > -1)) { + divisor = this->life + 1; + this->rPrimColorR = func_80027DD4(this->rPrimColorR, 50, divisor); + this->rPrimColorG = func_80027DD4(this->rPrimColorG, 50, divisor); + this->rPrimColorB = func_80027DD4(this->rPrimColorB, 50, divisor); + this->rPrimColorA = func_80027DD4(this->rPrimColorA, 150, divisor); + this->rEnvColorR = func_80027DD4(this->rEnvColorR, 10, divisor); + this->rEnvColorG = func_80027DD4(this->rEnvColorG, 10, divisor); + this->rEnvColorB = func_80027DD4(this->rEnvColorB, 10, divisor); + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h new file mode 100644 index 000000000..454303b50 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bomb2/z_eff_ss_bomb2.h @@ -0,0 +1,16 @@ +#ifndef Z_EFF_SS_BOMB2_H +#define Z_EFF_SS_BOMB2_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ s16 scaleStep; + /* 0x28 */ u8 drawMode; +} EffectSsBomb2InitParams; // size = 0x30 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.c b/soh/src/overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.c new file mode 100644 index 000000000..47610698a --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.c @@ -0,0 +1,84 @@ +/* + * File: z_eff_ss_bubble.c + * Overlay: ovl_Effect_Ss_Bubble + * Description: + */ + +#include "z_eff_ss_bubble.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] + +u32 EffectSsBubble_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsBubble_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsBubble_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Bubble_InitVars = { + EFFECT_SS_BUBBLE, + EffectSsBubble_Init, +}; + +u32 EffectSsBubble_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsBubbleInitParams* initParams = (EffectSsBubbleInitParams*)initParamsx; + + //! @bug Rand_ZeroOne in the macro means a random number is generated for both parts of the macro. + // In the base game this works out because both addresses are segment 4, but it may break if + // the addresses were changed to refer to different segments + this->gfx = SEGMENTED_TO_VIRTUAL(Rand_ZeroOne() < 0.5f ? gEffBubble1Tex : gEffBubble2Tex); + this->pos.x = ((Rand_ZeroOne() - 0.5f) * initParams->xzPosRandScale) + initParams->pos.x; + this->pos.y = (((Rand_ZeroOne() - 0.5f) * initParams->yPosRandScale) + initParams->yPosOffset) + initParams->pos.y; + this->pos.z = ((Rand_ZeroOne() - 0.5f) * initParams->xzPosRandScale) + initParams->pos.z; + Math_Vec3f_Copy(&this->vec, &this->pos); + this->life = 1; + this->rScale = (((Rand_ZeroOne() * 0.5f) + 1.0f) * initParams->scale) * 100; + this->draw = EffectSsBubble_Draw; + this->update = EffectSsBubble_Update; + + return 1; +} + +void EffectSsBubble_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 scale = this->rScale / 100.0f; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_bubble.c", 154); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_bubble.c", 167), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 150, 150, 150, 0); + gSPSegment(POLY_OPA_DISP++, 0x08, this->gfx); + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gEffBubbleDL)); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_bubble.c", 179); +} + +void EffectSsBubble_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + WaterBox* waterBox; + f32 waterSurfaceY; + Vec3f ripplePos; + + waterSurfaceY = this->pos.y; + + // kill bubble if it's out of range of a water box + if (!WaterBox_GetSurface1(globalCtx, &globalCtx->colCtx, this->pos.x, this->pos.z, &waterSurfaceY, &waterBox)) { + this->life = -1; + return; + } + + if (waterSurfaceY < this->pos.y) { + ripplePos.x = this->pos.x; + ripplePos.y = waterSurfaceY; + ripplePos.z = this->pos.z; + EffectSsGRipple_Spawn(globalCtx, &ripplePos, 0, 80, 0); + this->life = -1; + } else { + this->life++; + this->pos.x = ((Rand_ZeroOne() * 0.5f) - 0.25f) + this->vec.x; + this->accel.y = (Rand_ZeroOne() - 0.3f) * 0.2f; + this->pos.z = ((Rand_ZeroOne() * 0.5f) - 0.25f) + this->vec.z; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.h b/soh/src/overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.h new file mode 100644 index 000000000..c8a77cfb3 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Bubble/z_eff_ss_bubble.h @@ -0,0 +1,15 @@ +#ifndef Z_EFF_SS_BUBBLE_H +#define Z_EFF_SS_BUBBLE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ f32 yPosOffset; + /* 0x10 */ f32 yPosRandScale; + /* 0x14 */ f32 xzPosRandScale; + /* 0x18 */ f32 scale; +} EffectSsBubbleInitParams; // size = 0x1C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.c b/soh/src/overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.c new file mode 100644 index 000000000..2bae49547 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.c @@ -0,0 +1,126 @@ +/* + * File: z_eff_ss_d_fire.c + * Overlay: ovl_Effect_Ss_D_Fire + * Description: Dodongo Fire + */ + +#include "z_eff_ss_d_fire.h" +#include "objects/object_dodongo/object_dodongo.h" + +#define rScale regs[0] +#define rTexIdx regs[1] +#define rPrimColorR regs[2] +#define rPrimColorG regs[3] +#define rPrimColorB regs[4] +#define rPrimColorA regs[5] +#define rFadeDelay regs[6] +#define rScaleStep regs[9] +#define rObjBankIdx regs[10] +#define rYAccelStep regs[11] // has no effect due to how it's implemented + +u32 EffectSsDFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsDFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_D_Fire_InitVars = { + EFFECT_SS_D_FIRE, + EffectSsDFire_Init, +}; + +u32 EffectSsDFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsDFireInitParams* initParams = (EffectSsDFireInitParams*)initParamsx; + s32 objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_DODONGO); + + if (objBankIndex >= 0) { + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->gfx = SEGMENTED_TO_VIRTUAL(gDodongoFireDL); + this->life = initParams->life; + this->rScale = initParams->scale; + this->rScaleStep = initParams->scaleStep; + this->rYAccelStep = 0; + this->rObjBankIdx = objBankIndex; + this->draw = EffectSsDFire_Draw; + this->update = EffectSsDFire_Update; + this->rTexIdx = ((s16)(globalCtx->state.frames % 4) ^ 3); + this->rPrimColorR = 255; + this->rPrimColorG = 255; + this->rPrimColorB = 50; + this->rPrimColorA = initParams->alpha; + this->rFadeDelay = initParams->fadeDelay; + + return 1; + } + + return 0; +} + +static void* sTextures[] = { gDodongoFire0Tex, gDodongoFire1Tex, gDodongoFire2Tex, gDodongoFire3Tex }; + +void EffectSsDFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + s32 pad; + void* object; + Mtx* mtx; + f32 scale; + + object = globalCtx->objectCtx.status[this->rObjBankIdx].segment; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_d_fire.c", 276); + + if (Object_GetIndex(&globalCtx->objectCtx, OBJECT_DODONGO) > -1) { + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x06, object); + scale = this->rScale / 100.0f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80094BC4(gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTextures[this->rTexIdx])); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_d_fire.c", 330); +} + +void EffectSsDFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rTexIdx++; + this->rTexIdx &= 3; + this->rScale += this->rScaleStep; + + if (this->rFadeDelay >= this->life) { + this->rPrimColorA -= 5; + if (this->rPrimColorA < 0) { + this->rPrimColorA = 0; + } + } else { + this->rPrimColorA += 15; + if (this->rPrimColorA > 255) { + this->rPrimColorA = 255; + } + } + + if (this->accel.y < 0.0f) { + this->accel.y += this->rYAccelStep * 0.01f; + } + + if (this->life <= 0) { + this->rYAccelStep += 0; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.h b/soh/src/overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.h new file mode 100644 index 000000000..44f891a2e --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_D_Fire/z_eff_ss_d_fire.h @@ -0,0 +1,18 @@ +#ifndef Z_EFF_SS_D_FIRE_H +#define Z_EFF_SS_D_FIRE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ s16 scaleStep; + /* 0x28 */ s16 alpha; + /* 0x2A */ s16 fadeDelay; + /* 0x2C */ s32 life; +} EffectSsDFireInitParams; // size = 0x30 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.c b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.c new file mode 100644 index 000000000..10b3d540a --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.c @@ -0,0 +1,137 @@ +/* + * File: z_eff_ss_dead_db.c + * Overlay: ovl_Effect_Ss_Dead_Db + * Description: Flames and sound used when an enemy dies + */ + +#include "z_eff_ss_dead_db.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] +#define rTextIdx regs[1] +#define rPrimColorR regs[2] +#define rPrimColorG regs[3] +#define rPrimColorB regs[4] +#define rPrimColorA regs[5] +#define rEnvColorR regs[6] +#define rEnvColorG regs[7] +#define rEnvColorB regs[8] +#define rScaleStep regs[9] +#define rPlaySound regs[10] +#define rReg11 regs[11] + +u32 EffectSsDeadDb_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDeadDb_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsDeadDb_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Dead_Db_InitVars = { + EFFECT_SS_DEAD_DB, + EffectSsDeadDb_Init, +}; + +u32 EffectSsDeadDb_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsDeadDbInitParams* initParams = (EffectSsDeadDbInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffEnemyDeathFlameDL); + this->life = initParams->unk_34; + this->flags = 4; + this->rScaleStep = initParams->scaleStep; + this->rReg11 = initParams->unk_34; + this->draw = EffectSsDeadDb_Draw; + this->update = EffectSsDeadDb_Update; + this->rScale = initParams->scale; + this->rTextIdx = 0; + this->rPlaySound = initParams->playSound; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + + return 1; +} + +static void* sTextures[] = { + gEffEnemyDeathFlame1Tex, gEffEnemyDeathFlame2Tex, gEffEnemyDeathFlame3Tex, gEffEnemyDeathFlame4Tex, + gEffEnemyDeathFlame5Tex, gEffEnemyDeathFlame6Tex, gEffEnemyDeathFlame7Tex, gEffEnemyDeathFlame8Tex, + gEffEnemyDeathFlame9Tex, gEffEnemyDeathFlame10Tex, +}; + +void EffectSsDeadDb_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + Mtx* mtx; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_dead_db.c", 201); + + scale = this->rScale * 0.01f; + + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, scale); + SkinMatrix_MtxFMtxFMult(&mfTrans, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80094BC4(gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTextures[this->rTextIdx])); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_dead_db.c", 247); +} + +void EffectSsDeadDb_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + f32 w; + f32 pad; + + this->rTextIdx = (f32)((this->rReg11 - this->life) * 9) / this->rReg11; + this->rScale += this->rScaleStep; + + this->rPrimColorR -= 10; + if (this->rPrimColorR < 0) { + this->rPrimColorR = 0; + } + + this->rPrimColorG -= 10; + if (this->rPrimColorG < 0) { + this->rPrimColorG = 0; + } + + this->rPrimColorB -= 10; + if (this->rPrimColorB < 0) { + this->rPrimColorB = 0; + } + + this->rEnvColorR -= 10; + if (this->rEnvColorR < 0) { + this->rEnvColorR = 0; + } + + this->rEnvColorG -= 10; + if (this->rEnvColorG < 0) { + this->rEnvColorG = 0; + } + + this->rEnvColorB -= 10; + if (this->rEnvColorB < 0) { + this->rEnvColorB = 0; + } + + if (this->rPlaySound && (this->rTextIdx == 1)) { + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->pos, &this->vec, &w); + Audio_PlaySoundGeneral(NA_SE_EN_EXTINCT, &this->vec, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.h b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.h new file mode 100644 index 000000000..69f890b8f --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Db/z_eff_ss_dead_db.h @@ -0,0 +1,20 @@ +#ifndef Z_EFF_SS_DEAD_DB_H +#define Z_EFF_SS_DEAD_DB_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ s16 scaleStep; + /* 0x28 */ Color_RGBA8 primColor; + /* 0x2C */ Color_RGBA8 envColor; + /* 0x30 */ s16 unused; + /* 0x34 */ s32 unk_34; + /* 0x38 */ s16 playSound; +} EffectSsDeadDbInitParams; // size = 0x3C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.c b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.c new file mode 100644 index 000000000..b99d68e5b --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.c @@ -0,0 +1,143 @@ +/* + * File: z_eff_ss_dead_dd.c + * Overlay: ovl_Effect_Ss_Dead_Dd + * Description: + */ + +#include "z_eff_ss_dead_dd.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] +#define rPrimColorR regs[2] +#define rPrimColorG regs[3] +#define rPrimColorB regs[4] +#define rAlpha regs[5] +#define rEnvColorR regs[6] +#define rEnvColorG regs[7] +#define rEnvColorB regs[8] +#define rScaleStep regs[9] +#define rAlphaStep regs[10] +#define rAlphaMode regs[11] // if mode is 0 alpha decreases over time, otherwise it increases + +u32 EffectSsDeadDd_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDeadDd_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsDeadDd_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Dead_Dd_InitVars = { + EFFECT_SS_DEAD_DD, + EffectSsDeadDd_Init, +}; + +u32 EffectSsDeadDd_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + s32 i; + EffectSsDeadDdInitParams* initParams = (EffectSsDeadDdInitParams*)initParamsx; + + if (initParams->type == 0) { + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = initParams->life; + this->rScaleStep = initParams->scaleStep; + this->rAlphaMode = initParams->alphaStep; + + if (initParams->alphaStep != 0) { + this->rAlphaStep = initParams->alphaStep; + } else { + this->rAlphaStep = initParams->alpha / initParams->life; + } + + this->draw = EffectSsDeadDd_Draw; + this->update = EffectSsDeadDd_Update; + this->rScale = initParams->scale; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rAlpha = initParams->alpha; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + + } else if (initParams->type == 1) { + this->life = initParams->life; + this->rScaleStep = initParams->scaleStep; + this->rAlphaMode = 0; + this->rAlphaStep = 155 / initParams->life; + this->rScale = initParams->scale; + this->rPrimColorR = 255; + this->rPrimColorG = 255; + this->rPrimColorB = 155; + this->rAlpha = 155; + this->rEnvColorR = 250; + this->rEnvColorG = 180; + this->rEnvColorB = 0; + this->draw = EffectSsDeadDd_Draw; + this->update = EffectSsDeadDd_Update; + + for (i = initParams->randIter; i > 0; i--) { + this->pos.x = ((Rand_ZeroOne() - 0.5f) * initParams->randPosScale) + initParams->pos.x; + this->pos.y = ((Rand_ZeroOne() - 0.5f) * initParams->randPosScale) + initParams->pos.y; + this->pos.z = ((Rand_ZeroOne() - 0.5f) * initParams->randPosScale) + initParams->pos.z; + this->accel.x = this->velocity.x = (Rand_ZeroOne() - 0.5f) * 2.0f; + this->accel.y = this->velocity.y = (Rand_ZeroOne() - 0.5f) * 2.0f; + this->accel.z = this->velocity.z = (Rand_ZeroOne() - 0.5f) * 2.0f; + } + } else { + osSyncPrintf("Effect_SS_Dd_disp_mode():mode_swが変です。\n"); + return 0; + } + + return 1; +} + +void EffectSsDeadDd_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + Mtx* mtx; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_dead_dd.c", 214); + + scale = this->rScale * 0.01f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, scale); + SkinMatrix_MtxFMtxFMult(&mfTrans, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + func_80094BC4(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, this->rAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rAlpha); + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(POLY_XLU_DISP++, SEG_ADDR(1, 0), G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gDPSetCombineLERP(POLY_XLU_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + gSPDisplayList(POLY_XLU_DISP++, gLensFlareCircleDL); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_dead_dd.c", 259); +} + +void EffectSsDeadDd_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + + this->rScale += this->rScaleStep; + + if (this->rScale < 0) { + this->rScale = 0; + } + + if (this->rAlphaMode != 0) { + this->rAlpha += this->rAlphaStep; + if (this->rAlpha > 255) { + this->rAlpha = 255; + } + } else { + if (this->rAlpha < this->rAlphaStep) { + this->rAlpha = 0; + } else { + this->rAlpha -= this->rAlphaStep; + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.h b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.h new file mode 100644 index 000000000..3b3281cde --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Dd/z_eff_ss_dead_dd.h @@ -0,0 +1,23 @@ +#ifndef Z_EFF_SS_DEAD_DD_H +#define Z_EFF_SS_DEAD_DD_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ s16 scaleStep; + /* 0x28 */ Color_RGB8 primColor; + /* 0x2A */ u8 alpha; + /* 0x2C */ Color_RGB8 envColor; + /* 0x30 */ s16 alphaStep; + /* 0x34 */ s32 life; + /* 0x38 */ f32 randPosScale; + /* 0x3C */ s32 randIter; + /* 0x40 */ u8 type; +} EffectSsDeadDdInitParams; // size = 0x44 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.c b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.c new file mode 100644 index 000000000..01c08c501 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.c @@ -0,0 +1,124 @@ +/* + * File: z_eff_ss_dead_ds.c + * Overlay: ovl_Effect_Ss_Dead_Ds + * Description: Burn mark on the floor + */ + +#include "z_eff_ss_dead_ds.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] +#define rTimer regs[1] +#define rRoll regs[2] +#define rPitch regs[3] +#define rYaw regs[4] +#define rAlpha regs[5] +#define rScaleStep regs[9] +#define rAlphaStep regs[10] +#define rHalfOfLife regs[11] + +u32 EffectSsDeadDs_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDeadDs_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsDeadDs_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Dead_Ds_InitVars = { + EFFECT_SS_DEAD_DS, + EffectSsDeadDs_Init, +}; + +u32 EffectSsDeadDs_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsDeadDsInitParams* initParams = (EffectSsDeadDsInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = initParams->life; + this->rScaleStep = initParams->scaleStep; + this->rHalfOfLife = initParams->life / 2; + this->rAlphaStep = initParams->alpha / this->rHalfOfLife; + this->draw = EffectSsDeadDs_Draw; + this->update = EffectSsDeadDs_Update; + this->rScale = initParams->scale; + this->rAlpha = initParams->alpha; + this->rTimer = 0; + + return 1; +} + +void EffectSsDeadDs_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 pad; + f32 scale; + s32 pad1; + s32 pad2; + MtxF mf; + f32 temp; + Vec3f pos; + CollisionPoly* floorPoly; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead_ds.c", 157); + + scale = this->rScale * 0.01f; + func_80094BC4(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, this->rAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 0, 0); + pos = this->pos; + + if (this->rTimer == 0) { + Vec3s rpy; + Vec3f sp44; + + sp44.x = pos.x - this->velocity.x; + sp44.y = pos.y - this->velocity.y; + sp44.z = pos.z - this->velocity.z; + + if (BgCheck_EntitySphVsWall1(&globalCtx->colCtx, &this->pos, &pos, &sp44, 1.5f, &floorPoly, 1.0f)) { + func_80038A28(floorPoly, this->pos.x, this->pos.y, this->pos.z, &mf); + Matrix_Put(&mf); + } else { + pos.y++; + temp = BgCheck_EntityRaycastFloor1(&globalCtx->colCtx, &floorPoly, &pos); + + if (floorPoly != NULL) { + func_80038A28(floorPoly, this->pos.x, temp + 1.5f, this->pos.z, &mf); + Matrix_Put(&mf); + } else { + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Get(&mf); + } + } + + Matrix_MtxFToZYXRotS(&mf, &rpy, 0); + this->rRoll = rpy.x; + this->rPitch = rpy.y; + this->rYaw = rpy.z; + this->pos.y = mf.yw; + this->rTimer++; + } + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_RotateZYX(this->rRoll, this->rPitch, this->rYaw, MTXMODE_APPLY); + Matrix_RotateX(1.57f, MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_ss_dead_ds.c", 246), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetCombineLERP(POLY_XLU_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + gSPDisplayList(POLY_XLU_DISP++, gLensFlareCircleDL); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_dead_ds.c", 255); +} + +void EffectSsDeadDs_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if (this->life < this->rHalfOfLife) { + + this->rScale += this->rScaleStep; + if (this->rScale < 0) { + this->rScale = 0; + } + + this->rAlpha -= this->rAlphaStep; + if (this->rAlpha < 0) { + this->rAlpha = 0; + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.h b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.h new file mode 100644 index 000000000..d6a9b9afb --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Ds/z_eff_ss_dead_ds.h @@ -0,0 +1,17 @@ +#ifndef Z_EFF_SS_DEAD_DS_H +#define Z_EFF_SS_DEAD_DS_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ s16 scaleStep; + /* 0x28 */ s16 alpha; + /* 0x2C */ s32 life; +} EffectSsDeadDsInitParams; // size = 0x30 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.c b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.c new file mode 100644 index 000000000..b15f991a2 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.c @@ -0,0 +1,49 @@ +/* + * File: z_eff_ss_dead_sound.c + * Overlay: ovl_Effect_Ss_Dead_Sound + * Description: Plays a sound effect + */ + +#include "z_eff_ss_dead_sound.h" + +#define rSfxId regs[10] +#define rRepeatMode regs[11] // sound is replayed every update. unused in the original game + +u32 EffectSsDeadSound_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDeadSound_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Dead_Sound_InitVars = { + EFFECT_SS_DEAD_SOUND, + EffectSsDeadSound_Init, +}; + +u32 EffectSsDeadSound_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsDeadSoundInitParams* initParams = (EffectSsDeadSoundInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->flags = 2; + this->life = initParams->life; + this->draw = NULL; + this->update = EffectSsDeadSound_Update; + this->rRepeatMode = initParams->repeatMode; + this->rSfxId = initParams->sfxId; + osSyncPrintf("コンストラクター3\n"); // "constructor 3" + + return 1; +} + +void EffectSsDeadSound_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + switch (this->rRepeatMode) { + case DEADSOUND_REPEAT_MODE_OFF: + this->rRepeatMode--; // decrement to 0 so sound only plays once + break; + case DEADSOUND_REPEAT_MODE_ON: + break; + default: + return; + } + + Audio_PlaySoundGeneral(this->rSfxId, &this->pos, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h new file mode 100644 index 000000000..624159f3d --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dead_Sound/z_eff_ss_dead_sound.h @@ -0,0 +1,21 @@ +#ifndef Z_EFF_SS_DEAD_SOUND_H +#define Z_EFF_SS_DEAD_SOUND_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ u16 sfxId; + /* 0x26 */ s16 lowerPriority; + /* 0x28 */ s16 repeatMode; + /* 0x26 */ s16 unused2; + /* 0x2C */ s32 life; +} EffectSsDeadSoundInitParams; // size = 0x30 + +#define DEADSOUND_REPEAT_MODE_OFF 1 +#define DEADSOUND_REPEAT_MODE_ON 2 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.c b/soh/src/overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.c new file mode 100644 index 000000000..677b1d782 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.c @@ -0,0 +1,107 @@ +/* + * File: z_eff_ss_dt_bubble.c + * Overlay: ovl_Effect_Ss_Dt_Bubble + * Description: Bubbles (a random mix of translucent and opaque) + */ + +#include "z_eff_ss_dt_bubble.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rRandXZ regs[8] +#define rScale regs[9] +#define rLifespan regs[10] + +u32 EffectSsDtBubble_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDtBubble_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsDtBubble_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +static Color_RGBA8 sPrimColors[] = { { 255, 255, 100, 255 }, { 150, 255, 255, 255 }, { 100, 255, 255, 255 } }; +static Color_RGBA8 sEnvColors[] = { { 170, 0, 0, 255 }, { 0, 100, 0, 255 }, { 0, 0, 255, 255 } }; + +EffectSsInit Effect_Ss_Dt_Bubble_InitVars = { + EFFECT_SS_DT_BUBBLE, + EffectSsDtBubble_Init, +}; + +u32 EffectSsDtBubble_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsDtBubbleInitParams* initParams = (EffectSsDtBubbleInitParams*)initParamsx; + + //! @bug Rand_ZeroOne in the macro means a random number is generated for both parts of the macro. + // In the base game this works out because both addresses are segment 4, but it may break if + // the addresses were changed to refer to different segments + this->gfx = SEGMENTED_TO_VIRTUAL(Rand_ZeroOne() < 0.5f ? gEffBubble1Tex : gEffBubble2Tex); + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = initParams->life; + + if (!initParams->customColor) { + this->rPrimColorR = sPrimColors[initParams->colorProfile].r; + this->rPrimColorG = sPrimColors[initParams->colorProfile].g; + this->rPrimColorB = sPrimColors[initParams->colorProfile].b; + this->rPrimColorA = sPrimColors[initParams->colorProfile].a; + this->rEnvColorR = sEnvColors[initParams->colorProfile].r; + this->rEnvColorG = sEnvColors[initParams->colorProfile].g; + this->rEnvColorB = sEnvColors[initParams->colorProfile].b; + this->rEnvColorA = sEnvColors[initParams->colorProfile].a; + } else { + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rEnvColorA = initParams->envColor.a; + } + + this->rRandXZ = initParams->randXZ; + this->rScale = initParams->scale; + this->rLifespan = initParams->life; + this->draw = EffectSsDtBubble_Draw; + this->update = EffectSsDtBubble_Update; + + return 1; +} + +void EffectSsDtBubble_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_dt_bubble.c", 201); + + scale = this->rScale * 0.004f; + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_dt_bubble.c", 213), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093C14(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + (this->rPrimColorA * this->life) / this->rLifespan); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, + (this->rEnvColorA * this->life) / this->rLifespan); + gSPSegment(POLY_XLU_DISP++, 0x08, this->gfx); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gEffBubbleDL)); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_dt_bubble.c", 236); +} + +void EffectSsDtBubble_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + f32 rand; + + if (this->rRandXZ == 1) { + rand = Rand_ZeroOne(); + this->pos.x += (rand * 2.0f) - 1.0f; + + rand = Rand_ZeroOne(); + this->pos.z += (rand * 2.0f) - 1.0f; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.h b/soh/src/overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.h new file mode 100644 index 000000000..d7629b3b4 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dt_Bubble/z_eff_ss_dt_bubble.h @@ -0,0 +1,20 @@ +#ifndef Z_EFF_SS_DT_BUBBLE_H +#define Z_EFF_SS_DT_BUBBLE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ Color_RGBA8 primColor; + /* 0x28 */ Color_RGBA8 envColor; + /* 0x2C */ s16 scale; + /* 0x2E */ s16 life; + /* 0x30 */ s16 colorProfile; + /* 0x32 */ s16 randXZ; // randomly moves in the xz plane if true + /* 0x34 */ u8 customColor; +} EffectSsDtBubbleInitParams; // size = 0x3C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.c b/soh/src/overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.c new file mode 100644 index 000000000..e205df31a --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.c @@ -0,0 +1,189 @@ +/* + * File: z_eff_ss_dust.c + * Overlay: ovl_Effect_Ss_Dust + * Description: Dust Particle Effect + */ + +#include "z_eff_ss_dust.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rTexIdx regs[8] // this reg is also used to set specific colors in the fire update function +#define rScale regs[9] +#define rScaleStep regs[10] +#define rDrawFlags regs[11] +#define rLifespan regs[12] + +u32 EffectSsDust_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsDust_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsBlast_UpdateFire(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsDust_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Dust_InitVars = { + EFFECT_SS_DUST, + EffectSsDust_Init, +}; + +static EffectSsUpdateFunc sUpdateFuncs[] = { + EffectSsDust_Update, + EffectSsBlast_UpdateFire, +}; + +u32 EffectSsDust_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + s32 randColorOffset; + EffectSsDustInitParams* initParams = (EffectSsDustInitParams*)initParamsx; + + Math_Vec3f_Copy(&this->pos, &initParams->pos); + Math_Vec3f_Copy(&this->velocity, &initParams->velocity); + Math_Vec3f_Copy(&this->accel, &initParams->accel); + this->gfx = SEGMENTED_TO_VIRTUAL(gEffDustDL); + this->life = initParams->life; + this->update = sUpdateFuncs[initParams->updateMode]; + this->draw = EffectSsDust_Draw; + + if (initParams->drawFlags & 4) { + randColorOffset = Rand_ZeroOne() * 20.0f - 10.0f; + this->rPrimColorR = initParams->primColor.r + randColorOffset; + this->rPrimColorG = initParams->primColor.g + randColorOffset; + this->rPrimColorB = initParams->primColor.b + randColorOffset; + this->rEnvColorR = initParams->envColor.r + randColorOffset; + this->rEnvColorG = initParams->envColor.g + randColorOffset; + this->rEnvColorB = initParams->envColor.b + randColorOffset; + } else { + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + } + + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorA = initParams->envColor.a; + this->rTexIdx = 0; + this->rScale = initParams->scale; + this->rScaleStep = initParams->scaleStep; + this->rLifespan = initParams->life; + this->rDrawFlags = initParams->drawFlags; + + return 1; +} + +void EffectSsDust_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* dustTextures[] = { + gDust1Tex, gDust2Tex, gDust3Tex, gDust4Tex, gDust5Tex, gDust6Tex, gDust7Tex, gDust8Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + s32 pad; + Mtx* mtx; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_dust.c", 321); + + scale = this->rScale * 0.0025f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPPipeSync(POLY_XLU_DISP++); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(dustTextures[this->rTexIdx])); + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + gDPPipeSync(POLY_XLU_DISP++); + + if (this->rDrawFlags & 1) { + gDPSetCombineLERP(POLY_XLU_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, PRIMITIVE, 0, TEXEL0, 0, + COMBINED, 0, SHADE, 0, 0, 0, 0, COMBINED); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_FOG_SHADE_A, G_RM_ZB_CLD_SURF2); + gSPSetGeometryMode(POLY_XLU_DISP++, G_FOG | G_LIGHTING); + } else if (this->rDrawFlags & 2) { + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_ZB_CLD_SURF2); + gSPClearGeometryMode(POLY_XLU_DISP++, G_FOG | G_LIGHTING); + } else { + gSPClearGeometryMode(POLY_XLU_DISP++, G_LIGHTING); + } + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_dust.c", 389); +} + +void EffectSsDust_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + this->accel.z = (Rand_ZeroOne() * 0.4f) - 0.2f; + + if ((this->life <= this->rLifespan) && (this->life >= (this->rLifespan - 7))) { + if (this->rLifespan >= 5) { + this->rTexIdx = this->rLifespan - this->life; + } else { + this->rTexIdx = ((this->rLifespan - this->life) * (8 / this->rLifespan)); + } + } else { + this->rTexIdx = 7; + } + + this->rScale += this->rScaleStep; +} + +// this update mode is unused in the original game +void EffectSsBlast_UpdateFire(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + this->accel.z = (Rand_ZeroOne() * 0.4f) - 0.2f; + + switch (this->rTexIdx) { + case 0: + this->rPrimColorR = 255; + this->rPrimColorG = 150; + this->rPrimColorB = 0; + this->rEnvColorR = 150; + this->rEnvColorG = 50; + this->rEnvColorB = 0; + break; + case 1: + this->rPrimColorR = 200; + this->rPrimColorG = 50; + this->rPrimColorB = 0; + this->rEnvColorR = 100; + this->rEnvColorG = 0; + this->rEnvColorB = 0; + break; + case 2: + this->rPrimColorR = 50; + this->rPrimColorG = 0; + this->rPrimColorB = 0; + this->rEnvColorR = 0; + this->rEnvColorG = 0; + this->rEnvColorB = 0; + break; + case 3: + this->rPrimColorR = 50; + this->rEnvColorR = this->rPrimColorG = this->rEnvColorG = this->rPrimColorB = this->rEnvColorB = 0; + break; + } + + if (this->rTexIdx < 7) { + this->rTexIdx++; + } + + this->rScale += this->rScaleStep; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.h b/soh/src/overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.h new file mode 100644 index 000000000..a56bc38f5 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Dust/z_eff_ss_dust.h @@ -0,0 +1,20 @@ +#ifndef Z_EFF_SS_DUST_H +#define Z_EFF_SS_DUST_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ Color_RGBA8 primColor; + /* 0x28 */ Color_RGBA8 envColor; + /* 0x2C */ s16 scale; + /* 0x2E */ s16 scaleStep; + /* 0x30 */ s16 life; + /* 0x32 */ u16 drawFlags; + /* 0x34 */ u8 updateMode; +} EffectSsDustInitParams; // size = 0x38 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.c b/soh/src/overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.c new file mode 100644 index 000000000..587678b37 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.c @@ -0,0 +1,150 @@ +/* + * File: z_eff_ss_en_fire.c + * Overlay: ovl_Effect_Ss_En_Fire + * Description: + */ + +#include "z_eff_ss_en_fire.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScaleMax regs[0] +#define rScale regs[1] +#define rLifespan regs[2] +#define rUnused regs[3] +#define rPitch regs[4] +#define rYaw regs[5] +#define rReg6 regs[6] +#define rBodyPart regs[7] +#define rFlags regs[8] +#define rScroll regs[9] + +u32 EffectSsEnFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsEnFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsEnFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_En_Fire_InitVars = { + EFFECT_SS_EN_FIRE, + EffectSsEnFire_Init, +}; + +u32 EffectSsEnFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsEnFireInitParams* initParams = (EffectSsEnFireInitParams*)initParamsx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + this->pos = initParams->pos; + this->velocity = this->accel = zeroVec; + this->life = 20; + this->rLifespan = this->life; + this->actor = initParams->actor; + this->rScroll = Rand_ZeroOne() * 20.0f; + this->draw = EffectSsEnFire_Draw; + this->update = EffectSsEnFire_Update; + this->rUnused = -15; + + if (initParams->bodyPart < 0) { + this->rYaw = Math_Vec3f_Yaw(&initParams->actor->world.pos, &initParams->pos) - initParams->actor->shape.rot.y; + this->rPitch = + Math_Vec3f_Pitch(&initParams->actor->world.pos, &initParams->pos) - initParams->actor->shape.rot.x; + this->vec.z = Math_Vec3f_DistXYZ(&initParams->pos, &initParams->actor->world.pos); + } + + this->rScaleMax = initParams->scale; + + if ((initParams->unk_12 & 0x8000) != 0) { + this->rScale = initParams->scale; + } else { + this->rScale = 0; + } + + this->rReg6 = initParams->unk_12 & 0x7FFF; + this->rBodyPart = initParams->bodyPart; + this->rFlags = initParams->flags; + + return 1; +} + +void EffectSsEnFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 scale; + s16 camYaw; + s32 pad[3]; + s16 redGreen; + + OPEN_DISPS(gfxCtx, "../z_eff_en_fire.c", 169); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + camYaw = (Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000); + Matrix_RotateY(camYaw * (M_PI / 0x8000), MTXMODE_APPLY); + + scale = Math_SinS(this->life * 0x333) * (this->rScale * 0.00005f); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_en_fire.c", 180), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + redGreen = this->life - 5; + + if (redGreen < 0) { + redGreen = 0; + } + + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, redGreen * 12.7f, 0, 0, 0); + gDPSetPrimColor(POLY_XLU_DISP++, 0x0, 0x80, redGreen * 12.7f, redGreen * 12.7f, 0, 255); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, (this->rScroll * -0x14) & 0x1FF, + 0x20, 0x80)); + + if (((this->rFlags & 0x7FFF) != 0) || (this->life < 18)) { + gSPDisplayList(POLY_XLU_DISP++, gEffFire2DL); + } else { + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_en_fire.c", 213); +} + +typedef struct { + /* 0x000 */ Actor actor; + /* 0x14C */ Vec3f firePos[10]; +} FireActorF; + +typedef struct { + /* 0x000 */ Actor actor; + /* 0x14C */ Vec3s firePos[10]; +} FireActorS; + +void EffectSsEnFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + + this->rScroll++; + + if (this->actor != NULL) { + if (this->actor->colorFilterTimer >= 22) { + this->life++; + } + if (this->actor->update != NULL) { + Math_SmoothStepToS(&this->rScale, this->rScaleMax, 1, this->rScaleMax >> 3, 0); + + if (this->rBodyPart < 0) { + Matrix_Translate(this->actor->world.pos.x, this->actor->world.pos.y, this->actor->world.pos.z, + MTXMODE_NEW); + Matrix_RotateY((this->rYaw + this->actor->shape.rot.y) * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX((this->rPitch + this->actor->shape.rot.x) * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_MultVec3f(&this->vec, &this->pos); + } else { + if ((this->rFlags & 0x8000)) { + this->pos.x = ((FireActorS*)this->actor)->firePos[this->rBodyPart].x; + this->pos.y = ((FireActorS*)this->actor)->firePos[this->rBodyPart].y; + this->pos.z = ((FireActorS*)this->actor)->firePos[this->rBodyPart].z; + } else { + this->pos.x = ((FireActorF*)this->actor)->firePos[this->rBodyPart].x; + this->pos.y = ((FireActorF*)this->actor)->firePos[this->rBodyPart].y; + this->pos.z = ((FireActorF*)this->actor)->firePos[this->rBodyPart].z; + } + } + } else if (this->rReg6 != 0) { + this->life = 0; + } else { + this->actor = NULL; + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.h b/soh/src/overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.h new file mode 100644 index 000000000..bf8839be9 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_En_Fire/z_eff_ss_en_fire.h @@ -0,0 +1,16 @@ +#ifndef Z_EFF_SS_EN_FIRE_H +#define Z_EFF_SS_EN_FIRE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Actor* actor; + /* 0x04 */ Vec3f pos; + /* 0x10 */ s16 scale; + /* 0x12 */ s16 unk_12; + /* 0x14 */ s16 flags; + /* 0x16 */ s16 bodyPart; +} EffectSsEnFireInitParams; // size = 0x18 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.c b/soh/src/overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.c new file mode 100644 index 000000000..59bec0fad --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.c @@ -0,0 +1,165 @@ +/* + * File: z_eff_ss_en_ice.c + * Overlay: ovl_Effect_Ss_En_Ice + * Description: Ice clumps + */ + +#include "z_eff_ss_en_ice.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rLifespan regs[0] +#define rYaw regs[1] +#define rPitch regs[2] +#define rRotSpeed regs[3] +#define rPrimColorR regs[4] +#define rPrimColorG regs[5] +#define rPrimColorB regs[6] +#define rPrimColorA regs[7] +#define rEnvColorR regs[8] +#define rEnvColorG regs[9] +#define rEnvColorB regs[10] +#define rAlphaMode regs[11] +#define rScale regs[12] + +u32 EffectSsEnIce_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsEnIce_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsEnIce_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsEnIce_UpdateFlying(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_En_Ice_InitVars = { + EFFECT_SS_EN_ICE, + EffectSsEnIce_Init, +}; + +u32 EffectSsEnIce_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsEnIceInitParams* initParams = (EffectSsEnIceInitParams*)initParamsx; + + if (initParams->type == 0) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + this->pos = initParams->pos; + this->vec.x = this->pos.x - initParams->actor->world.pos.x; + this->vec.y = this->pos.y - initParams->actor->world.pos.y; + this->vec.z = this->pos.z - initParams->actor->world.pos.z; + this->velocity = zeroVec; + this->accel = zeroVec; + this->life = 10; + this->actor = initParams->actor; + this->draw = EffectSsEnIce_Draw; + this->update = EffectSsEnIce_UpdateFlying; + this->rScale = initParams->scale * 100.0f; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rAlphaMode = 1; + this->rPitch = Rand_CenteredFloat(65536.0f); + } else if (initParams->type == 1) { + this->pos = initParams->pos; + this->vec = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = initParams->life; + this->draw = EffectSsEnIce_Draw; + this->update = EffectSsEnIce_Update; + this->rLifespan = initParams->life; + this->rScale = initParams->scale * 100.0f; + this->rYaw = Math_Atan2S(initParams->velocity.z, initParams->velocity.x); + this->rPitch = 0; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rAlphaMode = 0; + } else { + osSyncPrintf("Effect_Ss_En_Ice_ct():pid->mode_swがエラーです。\n"); + return 0; + } + + return 1; +} + +void EffectSsEnIce_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale; + Vec3f hiliteLightDir; + u32 gameplayFrames; + f32 alpha; + + scale = this->rScale * 0.01f; + gameplayFrames = globalCtx->gameplayFrames; + + OPEN_DISPS(gfxCtx, "../z_eff_en_ice.c", 235); + + if (this->rAlphaMode != 0) { + alpha = this->life * 12; + } else { + if ((this->rLifespan > 0) && (this->life < (this->rLifespan >> 1))) { + alpha = ((this->life * 2.0f) / this->rLifespan); + alpha *= 255.0f; + } else { + alpha = 255.0f; + } + } + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_RotateY(this->rYaw * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(this->rPitch * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_en_ice.c", 261), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + hiliteLightDir.x = 89.8f; + hiliteLightDir.y = 0.0f; + hiliteLightDir.z = 89.8f; + + func_80093D84(globalCtx->state.gfxCtx); + func_8002EB44(&this->pos, &globalCtx->view.eye, &hiliteLightDir, globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, gameplayFrames & 0xFF, 0x20, 0x10, 1, 0, + (gameplayFrames * 2) & 0xFF, 0x40, 0x20)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, (u32)alpha); + gSPDisplayList(POLY_XLU_DISP++, gEffIceFragment2DL); + + CLOSE_DISPS(gfxCtx, "../z_eff_en_ice.c", 294); +} + +void EffectSsEnIce_UpdateFlying(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s16 rand; + + if ((this->actor != NULL) && (this->actor->update != NULL)) { + if ((this->life >= 9) && (this->actor->colorFilterTimer != 0) && (!(this->actor->colorFilterParams & 0xC000))) { + this->pos.x = this->actor->world.pos.x + this->vec.x; + this->pos.y = this->actor->world.pos.y + this->vec.y; + this->pos.z = this->actor->world.pos.z + this->vec.z; + this->life++; + } else if (this->life == 9) { + this->accel.x = Math_SinS(Math_Vec3f_Yaw(&this->actor->world.pos, &this->pos)) * (Rand_ZeroOne() + 1.0f); + this->accel.z = Math_CosS(Math_Vec3f_Yaw(&this->actor->world.pos, &this->pos)) * (Rand_ZeroOne() + 1.0f); + this->accel.y = -1.5f; + this->velocity.y = 5.0f; + } + } else { + if (this->life >= 9) { + rand = Rand_CenteredFloat(65535.0f); + this->accel.x = Math_SinS(rand) * (Rand_ZeroOne() + 1.0f); + this->accel.z = Math_CosS(rand) * (Rand_ZeroOne() + 1.0f); + this->life = 8; + this->accel.y = -1.5f; + this->velocity.y = 5.0f; + } + } +} + +void EffectSsEnIce_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rPitch += this->rRotSpeed; // rRotSpeed is not initialized so this does nothing +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.h b/soh/src/overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.h new file mode 100644 index 000000000..985e37b84 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_En_Ice/z_eff_ss_en_ice.h @@ -0,0 +1,19 @@ +#ifndef Z_EFF_SS_EN_ICE_H +#define Z_EFF_SS_EN_ICE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Actor* actor; + /* 0x04 */ Vec3f pos; + /* 0x10 */ f32 scale; + /* 0x14 */ Vec3f velocity; + /* 0x20 */ Vec3f accel; + /* 0x2C */ Color_RGBA8 primColor; + /* 0x30 */ Color_RGBA8 envColor; + /* 0x34 */ s32 life; + /* 0x38 */ s16 type; +} EffectSsEnIceInitParams; // size = 0x3C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c b/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c new file mode 100644 index 000000000..fe68b956c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.c @@ -0,0 +1,92 @@ +/* + * File: z_eff_ss_extra.c + * Overlay: ovl_Effect_Ss_Extra + * Description: Minigame Score Points + */ + +#include "z_eff_ss_extra.h" +#include "objects/object_yabusame_point/object_yabusame_point.h" + +#define rObjBankIdx regs[0] +#define rTimer regs[1] +#define rScoreIdx regs[2] +#define rScale regs[3] + +u32 EffectSsExtra_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsExtra_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsExtra_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +static s16 sScores[] = { 30, 60, 100 }; + +EffectSsInit Effect_Ss_Extra_InitVars = { + EFFECT_SS_EXTRA, + EffectSsExtra_Init, +}; + +u32 EffectSsExtra_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsExtraInitParams* initParams = (EffectSsExtraInitParams*)initParamsx; + s32 pad; + s32 objBankIndex; + u32 oldSeg6; + + objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_YABUSAME_POINT); + + if ((objBankIndex >= 0) && Object_IsLoaded(&globalCtx->objectCtx, objBankIndex)) { + oldSeg6 = gSegments[6]; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[objBankIndex].segment); + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->draw = EffectSsExtra_Draw; + this->update = EffectSsExtra_Update; + this->life = 50; + this->rScoreIdx = initParams->scoreIdx; + this->rScale = initParams->scale; + this->rTimer = 5; + this->rObjBankIdx = objBankIndex; + gSegments[6] = oldSeg6; + + return 1; + } + + return 0; +} + +static void* sTextures[] = { + object_yabusame_point_Tex_000000, + object_yabusame_point_Tex_000480, + object_yabusame_point_Tex_000900, +}; + +void EffectSsExtra_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 pad; + f32 scale = this->rScale / 100.0f; + void* object = globalCtx->objectCtx.status[this->rObjBankIdx].segment; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_extra.c", 168); + + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x06, object); + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_ss_extra.c", 186), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTextures[this->rScoreIdx])); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(object_yabusame_point_DL_000DC0)); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_extra.c", 194); +} + +void EffectSsExtra_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if (this->rTimer != 0) { + this->rTimer -= 1; + } else { + this->velocity.y = 0.0f; + } + + if (this->rTimer == 1) { + globalCtx->interfaceCtx.unk_23C = sScores[this->rScoreIdx]; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.h b/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.h new file mode 100644 index 000000000..cc05775c2 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Extra/z_eff_ss_extra.h @@ -0,0 +1,21 @@ +#ifndef Z_EFF_SS_EXTRA_H +#define Z_EFF_SS_EXTRA_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ s16 scoreIdx; +} EffectSsExtraInitParams; // size = 0x28 + +typedef enum { + /* 0 */ EXTRA_SCORE_30, + /* 1 */ EXTRA_SCORE_60, + /* 2 */ EXTRA_SCORE_100 +} ExtraScoreIdx; + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.c b/soh/src/overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.c new file mode 100644 index 000000000..6faac64c9 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.c @@ -0,0 +1,95 @@ +/* + * File: z_eff_ss_fcircle.c + * Overlay: ovl_Effect_Ss_Fcircle + * Description: Fire Circle + */ + +#include "z_eff_ss_fcircle.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rUnused regs[3] // probably supposed to be an alpha +#define rRadius regs[8] +#define rHeight regs[9] +#define rYaw regs[10] +#define rScale regs[11] + +u32 EffectSsFcircle_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsFcircle_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsFcircle_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Fcircle_InitVars = { + EFFECT_SS_FCIRCLE, + EffectSsFcircle_Init, +}; + +u32 EffectSsFcircle_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsFcircleInitParams* initParams = (EffectSsFcircleInitParams*)initParamsx; + + this->pos = initParams->pos; + this->actor = initParams->actor; + this->vec.x = initParams->pos.x - initParams->actor->world.pos.x; + this->vec.y = initParams->pos.y - initParams->actor->world.pos.y; + this->vec.z = initParams->pos.z - initParams->actor->world.pos.z; + this->gfx = gEffFireCircleDL; + this->life = 20; + this->draw = EffectSsFcircle_Draw; + this->update = EffectSsFcircle_Update; + this->rUnused = 255; + this->rRadius = initParams->radius; + this->rHeight = initParams->height; + this->rYaw = initParams->actor->shape.rot.y; + + return 1; +} + +void EffectSsFcircle_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 yScale; + f32 xzScale; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_fcircle.c", 149); + + scale = (this->rScale * (0.5f + (this->life * 0.025f))) * 0.01f; + yScale = (this->rHeight * 0.001f) * scale; + xzScale = (this->rRadius * 0.001f) * scale; + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(xzScale, yScale, xzScale, MTXMODE_APPLY); + Matrix_RotateY(this->rYaw * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_fcircle.c", 163), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, globalCtx->gameplayFrames % 128, 0, 32, 64, 1, 0, + ((globalCtx->gameplayFrames) * -0xF) % 256, 32, 64)); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 220, 0, (this->life * 12.75f)); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 0); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_fcircle.c", 186); +} + +void EffectSsFcircle_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + Actor* actor = this->actor; + + if (actor != NULL) { + if (actor->update != NULL) { + this->pos.x = actor->world.pos.x + this->vec.x; + this->pos.y = actor->world.pos.y + this->vec.y; + this->pos.z = actor->world.pos.z + this->vec.z; + this->rYaw = actor->shape.rot.y; + + if (actor->colorFilterTimer > 20) { + this->life = 20; + } else { + this->life = actor->colorFilterTimer; + } + + Math_StepToS(&this->rScale, 100, 20); + } else { + this->actor = NULL; + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.h b/soh/src/overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.h new file mode 100644 index 000000000..01709c6aa --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Fcircle/z_eff_ss_fcircle.h @@ -0,0 +1,14 @@ +#ifndef Z_EFF_SS_FCIRCLE_H +#define Z_EFF_SS_FCIRCLE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Actor* actor; + /* 0x04 */ Vec3f pos; + /* 0x10 */ s16 radius; + /* 0x12 */ s16 height; +} EffectSsFcircleInitParams; // size = 0x14 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.c b/soh/src/overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.c new file mode 100644 index 000000000..6b204d60f --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.c @@ -0,0 +1,255 @@ +/* + * File: z_eff_ss_fhg_flash.c + * Overlay: ovl_Effect_Ss_Fhg_Flash + * Description: Shock and Light Ball Effect + */ + +#include "z_eff_ss_fhg_flash.h" +#include "overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.h" +#include "objects/object_fhg/object_fhg.h" + +#define rAlpha regs[0] +#define rObjBankIdx regs[2] +#define rXZRot regs[3] +#define rParam regs[4] +#define rScale regs[8] + +u32 EffectSsFhgFlash_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsFhgFlash_DrawLightBall(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsFhgFlash_UpdateLightBall(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsFhgFlash_DrawShock(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsFhgFlash_UpdateShock(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Fhg_Flash_InitVars = { + EFFECT_SS_FHG_FLASH, + EffectSsFhgFlash_Init, +}; + +static UNK_TYPE D_809A5178[258]; +static Gfx D_809A5100[15]; + +u32 EffectSsFhgFlash_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsFhgFlashInitParams* initParams = (EffectSsFhgFlashInitParams*)initParamsx; + s32 pad; + s32 objBankIdx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + Vec3f sp34 = { 0.0f, -1000.0f, 0.0f }; + void* oldSeg6; + + if (initParams->type == FHGFLASH_LIGHTBALL) { + objBankIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_FHG); + + if ((objBankIdx > -1) && Object_IsLoaded(&globalCtx->objectCtx, objBankIdx)) { + oldSeg6 = gSegments[6]; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[objBankIdx].segment); + this->rObjBankIdx = objBankIdx; + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->rParam = initParams->param; + this->life = 100; + this->rScale = initParams->scale; + this->rAlpha = 255; + this->draw = EffectSsFhgFlash_DrawLightBall; + this->update = EffectSsFhgFlash_UpdateLightBall; + this->gfx = SEGMENTED_TO_VIRTUAL(gPhantomEnergyBallDL); + gSegments[6] = oldSeg6; + } else { + osSyncPrintf("Effect_Ss_Fhg_Flash_ct():pffd->modeエラー\n"); + return 0; + } + } else { + this->actor = initParams->actor; + this->velocity = this->accel = zeroVec; + this->life = (s16)(Rand_ZeroOne() * 10.0f) + 111; + this->rScale = (s16)Rand_ZeroFloat(initParams->scale) + initParams->scale; + this->rAlpha = 255; + this->draw = EffectSsFhgFlash_DrawShock; + this->update = EffectSsFhgFlash_UpdateShock; + this->rParam = initParams->param; + + if (initParams->param != FHGFLASH_SHOCK_NO_ACTOR) { + this->pos = sp34; + this->gfx = SEGMENTED_TO_VIRTUAL(D_809A5100); + } else { + this->pos = initParams->pos; + this->gfx = SEGMENTED_TO_VIRTUAL(D_809A5100); + } + } + return 1; +} + +static Color_RGB8 sColors[] = { + { 165, 255, 61 }, { 0, 255, 255 }, { 255, 40, 0 }, { 255, 255, 0 }, { 0, 0, 255 }, + { 255, 0, 255 }, { 255, 150, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, +}; + +void EffectSsFhgFlash_DrawLightBall(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale; + void* object; + + scale = this->rScale / 100.0f; + object = globalCtx->objectCtx.status[this->rObjBankIdx].segment; + + OPEN_DISPS(gfxCtx, "../z_eff_fhg_flash.c", 268); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x06, object); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, this->rAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, sColors[this->rParam].r, sColors[this->rParam].g, sColors[this->rParam].b, 0); + gDPPipeSync(POLY_XLU_DISP++); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + Matrix_RotateZ((this->rXZRot / 32768.0f) * 3.1416f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_fhg_flash.c", 326), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_fhg_flash.c", 330); +} + +void EffectSsFhgFlash_DrawShock(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale; + + scale = this->rScale / 100.0f; + + OPEN_DISPS(gfxCtx, "../z_eff_fhg_flash.c", 346); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + + if (this->rParam != FHGFLASH_SHOCK_NO_ACTOR) { + func_80094044(globalCtx->state.gfxCtx); + Matrix_RotateX((this->rXZRot / 32768.0f) * 1.1416f, MTXMODE_APPLY); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_DECAL2); + } else { + func_80093D84(globalCtx->state.gfxCtx); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + gDPSetRenderMode(POLY_XLU_DISP++, G_RM_PASS, G_RM_AA_ZB_XLU_SURF2); + } + + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, this->rAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 155, 0); + Matrix_RotateZ((this->rXZRot / 32768.0f) * 3.1416f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_fhg_flash.c", 395), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_fhg_flash.c", 399); +} + +void EffectSsFhgFlash_UpdateLightBall(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s16 rand = (Rand_ZeroOne() * 20000.0f); + + this->rXZRot = (this->rXZRot + rand) + 0x4000; + + if (this->rScale > 0) { + this->rScale -= 10; + + if (this->rScale <= 0) { + this->rScale = 0; + this->life = 0; + } + } + + if (this->rAlpha > 0) { + this->rAlpha -= 10; + + if (this->rAlpha <= 0) { + this->rAlpha = 0; + } + } +} + +void EffectSsFhgFlash_UpdateShock(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s16 randBodypart; + Player* player; + BossGanondrof* phantomGanon; + s16 rand; + + rand = (Rand_ZeroOne() * 20000.0f); + this->rXZRot = (this->rXZRot + rand) + 0x4000; + + if (this->rParam == FHGFLASH_SHOCK_PLAYER) { + player = GET_PLAYER(globalCtx); + randBodypart = Rand_ZeroFloat(17.9f); + this->pos.x = player->bodyPartsPos[randBodypart].x + Rand_CenteredFloat(10.0f); + this->pos.y = player->bodyPartsPos[randBodypart].y + Rand_CenteredFloat(15.0f); + this->pos.z = player->bodyPartsPos[randBodypart].z + Rand_CenteredFloat(10.0f); + } else if (this->rParam == FHGFLASH_SHOCK_PG) { + phantomGanon = (BossGanondrof*)this->actor; + randBodypart = Rand_ZeroFloat(23.9f); + this->pos.x = phantomGanon->bodyPartsPos[randBodypart].x + Rand_CenteredFloat(15.0f); + this->pos.y = phantomGanon->bodyPartsPos[randBodypart].y + Rand_CenteredFloat(20.0f); + this->pos.z = phantomGanon->bodyPartsPos[randBodypart].z + Rand_CenteredFloat(15.0f); + } + + if (this->life < 100) { + this->rAlpha -= 50; + + if (this->rAlpha < 0) { + this->rAlpha = 0; + this->life = 0; + } + } +} + +static Vtx D_809A50C0[4] = { + VTX(-10, -10, 0, 0, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(10, -10, 0, 1024, 1024, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(10, 10, 0, 1024, 0, 0xFF, 0xFF, 0xFF, 0xFF), + VTX(-10, 10, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF), +}; + +static Gfx D_809A5100[15] = { + gsDPPipeSync(), + gsDPSetTextureLUT(G_TT_NONE), + gsSPTexture(0xFFFF, 0xFFFF, 0, G_TX_RENDERTILE, G_ON), + gsDPLoadTextureBlock(D_809A5178, G_IM_FMT_I, G_IM_SIZ_8b, 32, 32, 0, G_TX_MIRROR | G_TX_WRAP, + G_TX_MIRROR | G_TX_WRAP, 5, 5, G_TX_NOLOD, G_TX_NOLOD), + gsDPSetCombineLERP(PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, COMBINED, 0, 0, 0, + COMBINED), + gsSPClearGeometryMode(G_CULL_BACK | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR), + gsSPVertex(D_809A50C0, 4, 0), + gsSP2Triangles(0, 1, 2, 0, 0, 2, 3, 0), + gsSPEndDisplayList(), +}; + +static UNK_TYPE D_809A5178[258] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x005B3000, 0x00000000, 0x00000000, + 0x00000000, 0x000B0000, 0x07000000, 0x00000000, 0x00000000, 0x005BB64B, 0x3A000000, 0x00000000, 0x00000000, + 0x005B0000, 0x00000000, 0x00000000, 0x00000000, 0x001E00B6, 0xFF5B0000, 0x00000000, 0x00000000, 0x00251F0C, + 0x07000000, 0x0A000000, 0x00000000, 0x00000000, 0xB6FF0000, 0x00000000, 0x00000000, 0x00255B00, 0x071F1E14, + 0x0A000000, 0x00000000, 0x00000000, 0x00457350, 0x00000000, 0x00000000, 0x00295B8C, 0x5B5B0000, 0x00000000, + 0x00000000, 0x00000000, 0x00455C39, 0x0F000000, 0x00000000, 0x0000A1FF, 0x5B000000, 0x00000000, 0x00000000, + 0x00000000, 0x005B5B00, 0x00000000, 0x00000000, 0x005B311C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x5BB60000, 0x00000000, 0x00000000, 0x5BB63100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFB60000, + 0x00000000, 0x00000046, 0xEA310000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0072B646, 0x00000000, + 0x0000B6A1, 0x81000000, 0x00000000, 0x00000000, 0x00000000, 0x000C0000, 0x00172E19, 0xBDAB5D41, 0x366BEAEA, + 0x81000000, 0x00000000, 0x00000000, 0x00000000, 0x06000000, 0x00000074, 0xFFFF0500, 0x0A2342B6, 0xFF000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x005B0000, 0x03030100, 0x00FF0700, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00020503, 0x00000000, 0x00466200, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00076200, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00002962, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00003EA1, 0x62000000, 0x00000000, 0x00000000, 0x0000002E, 0xB6350000, + 0x00000000, 0x00000000, 0x000000EA, 0x46000000, 0x00000000, 0x00000000, 0x00002EFF, 0xFF5A0000, 0x00000000, + 0x00000000, 0x000000FF, 0xFF001700, 0x00000000, 0x00000000, 0x01015BFF, 0xA1A10000, 0x00000000, 0x00000000, + 0x00030046, 0x97732100, 0x00000000, 0x00000000, 0x040CB65E, 0x4A5B4600, 0x00000000, 0x00000003, 0x0C0E8C46, + 0x1C035C00, 0x00000000, 0x00000017, 0x134F5B00, 0x00000046, 0x46000000, 0x00000000, 0x34D20000, 0x0401005B, + 0x00000000, 0x00000000, 0x5BB60000, 0x00000000, 0x30FCB600, 0x0000A1E7, 0x00000001, 0x0100141E, 0x5B000000, + 0x00000000, 0x00000000, 0x00000000, 0x0000A1FF, 0x5B46FF00, 0x00000100, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x000000FC, 0xFFA13100, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xB6000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h b/soh/src/overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h new file mode 100644 index 000000000..4a56d0b9f --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h @@ -0,0 +1,42 @@ +#ifndef Z_EFF_SS_FHGFLASH_H +#define Z_EFF_SS_FHGFLASH_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; + /* 0x26 */ u8 param; + /* 0x28 */ Actor* actor; + /* 0x2C */ u8 type; +} EffectSsFhgFlashInitParams; // size = 0x30 + +typedef enum { + /* 0x00 */ FHGFLASH_LIGHTBALL, + /* 0x01 */ FHGFLASH_SHOCK +} FhgFlashType; + +typedef enum { + /* 0x00 */ FHGFLASH_LIGHTBALL_GREEN, + /* 0x01 */ FHGFLASH_LIGHTBALL_LIGHTBLUE, + /* 0x02 */ FHGFLASH_LIGHTBALL_RED, + /* 0x03 */ FHGFLASH_LIGHTBALL_YELLOW, + /* 0x04 */ FHGFLASH_LIGHTBALL_BLUE, + /* 0x05 */ FHGFLASH_LIGHTBALL_PURPLE, + /* 0x06 */ FHGFLASH_LIGHTBALL_ORANGE, + /* 0x07 */ FHGFLASH_LIGHTBALL_WHITE1, + /* 0x08 */ FHGFLASH_LIGHTBALL_WHITE2 +} FhgFlashLightBallParam; + +typedef enum { + /* 0x00 */ FHGFLASH_SHOCK_NO_ACTOR, + /* 0x01 */ FHGFLASH_SHOCK_PLAYER, + /* 0x02 */ FHGFLASH_SHOCK_PG +} FhgFlashLightningParam; + + + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.c b/soh/src/overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.c new file mode 100644 index 000000000..c0e00027c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.c @@ -0,0 +1,142 @@ +/* + * File: z_eff_ss_fire_tail.c + * Overlay: ovl_Effect_Ss_Fire_Tail + * Description: Fire (burned by something) + */ + +#include "z_eff_ss_fire_tail.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rScale regs[0] +#define rLifespan regs[1] +#define rReg2 regs[2] +#define rReg3 regs[3] +#define rPrimColorR regs[4] +#define rPrimColorG regs[5] +#define rPrimColorB regs[6] +#define rEnvColorR regs[7] +#define rEnvColorG regs[8] +#define rEnvColorB regs[9] +#define rReg10 regs[10] +#define rBodyPart regs[11] +#define rType regs[12] + +u32 EffectSsFireTail_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsFireTail_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsFireTail_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Fire_Tail_InitVars = { + EFFECT_SS_FIRE_TAIL, + EffectSsFireTail_Init, +}; + +u32 EffectSsFireTail_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsFireTailInitParams* initParams = (EffectSsFireTailInitParams*)initParamsx; + + this->pos = initParams->pos; + this->vec = initParams->unk_14; + this->velocity.x = 0.0f; + this->velocity.y = 0.0f; + this->velocity.z = 0.0f; + this->accel.x = 0.0f; + this->accel.y = 0.0f; + this->accel.z = 0.0f; + this->life = initParams->life; + this->actor = initParams->actor; + this->draw = EffectSsFireTail_Draw; + this->update = EffectSsFireTail_Update; + this->rScale = initParams->scale * 1000.0f; + this->rLifespan = initParams->life; + this->rReg2 = -0xA; + this->rReg3 = -0xF; + if (initParams->unk_20 == 0) { + initParams->unk_20 = 1; + } + this->rReg10 = initParams->unk_20; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rBodyPart = initParams->bodyPart; + this->rType = initParams->type; + + return 1; +} + +void EffectSsFireTail_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + s16 yaw; + Vec3f scale; + f32 temp1; + f32 temp2; + f32 dist; + + OPEN_DISPS(gfxCtx, "../z_eff_fire_tail.c", 182); + + scale.x = scale.y = scale.z = 0.0f; + + if (this->actor != NULL) { + + this->vec = this->actor->velocity; + + if (this->rBodyPart < 0) { + Matrix_Translate(this->pos.x + this->actor->world.pos.x, this->pos.y + this->actor->world.pos.y, + this->pos.z + this->actor->world.pos.z, MTXMODE_NEW); + } else { + Player* player = GET_PLAYER(globalCtx); + s16 bodyPart = this->rBodyPart; + + this->pos.x = + player->bodyPartsPos[bodyPart].x - (Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx))) * 5.0f); + this->pos.y = player->bodyPartsPos[bodyPart].y; + this->pos.z = + player->bodyPartsPos[bodyPart].z - (Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx))) * 5.0f); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + } + } else { + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + } + + yaw = Math_Vec3f_Yaw(&scale, &this->vec) - Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)); + temp1 = fabsf(Math_CosS(yaw)); + temp2 = Math_SinS(yaw); + dist = Math_Vec3f_DistXZ(&scale, &this->vec) / (this->rReg10 * 0.1f); + Matrix_RotateY((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(globalCtx)) + 0x8000) * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateZ(temp2 * this->rReg2 * dist * (M_PI / 180.0f), MTXMODE_APPLY); + temp2 = 1.0f - ((f32)(this->life + 1) / this->rLifespan); + temp2 = 1.0f - SQ(temp2); + scale.x = scale.y = scale.z = temp2 * (this->rScale * 0.000010000001f); + Matrix_Scale(scale.x, scale.y, scale.z, MTXMODE_APPLY); + temp1 = (this->rReg3 * 0.01f * temp1 * dist) + 1.0f; + + if (temp1 < 0.1f) { + temp1 = 0.1f; + } + + Matrix_Scale(1.0f, temp1, 1.0f / temp1, MTXMODE_APPLY); + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_fire_tail.c", 238), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, 0); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 32, 64, 1, 0, + (globalCtx->state.frames * -0x14) & 0x1FF, 32, 128)); + + if (this->rType != 0) { + gSPDisplayList(POLY_XLU_DISP++, gEffFire2DL); + } else { + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_fire_tail.c", 273); +} + +void EffectSsFireTail_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rScale *= 0.9f; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.h b/soh/src/overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.h new file mode 100644 index 000000000..424d769f1 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Fire_Tail/z_eff_ss_fire_tail.h @@ -0,0 +1,20 @@ +#ifndef Z_EFF_SS_FIRE_TAIL_H +#define Z_EFF_SS_FIRE_TAIL_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Actor* actor; + /* 0x04 */ Vec3f pos; + /* 0x10 */ f32 scale; + /* 0x14 */ Vec3f unk_14; + /* 0x20 */ s16 unk_20; + /* 0x22 */ Color_RGBA8 primColor; + /* 0x26 */ Color_RGBA8 envColor; + /* 0x2A */ s16 type; + /* 0x2C */ s16 bodyPart; + /* 0x30 */ s32 life; +} EffectSsFireTailInitParams; // size = 0x + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.c b/soh/src/overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.c new file mode 100644 index 000000000..e910da2b1 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.c @@ -0,0 +1,57 @@ +/* + * File: z_eff_ss_g_fire.c + * Overlay: ovl_Effect_Ss_G_Fire + * Description: Flame Footprints + */ + +#include "z_eff_ss_g_fire.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +u32 EffectSsGFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsGFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_G_Fire_InitVars = { + EFFECT_SS_G_FIRE, + EffectSsGFire_Init, +}; + +u32 EffectSsGFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsGFireInitParams* initParams = (EffectSsGFireInitParams*)initParamsx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + this->velocity = this->accel = zeroVec; + this->pos = initParams->pos; + this->draw = EffectSsGFire_Draw; + this->update = EffectSsGFire_Update; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffFireFootprintDL); + this->life = 8; + this->flags = 0; + this->rgScale = 200; + this->rgTexIdx = 0; + this->rgTexIdxStep = 50; + this->rgPrimColorR = 255; + this->rgPrimColorG = 220; + this->rgPrimColorB = 80; + this->rgPrimColorA = 255; + this->rgEnvColorR = 130; + this->rgEnvColorG = 30; + this->rgEnvColorB = 0; + this->rgEnvColorA = 0; + + return 1; +} + +void EffectSsGFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + void* fireFootprintTextures[] = { + gEffFireFootprint1Tex, gEffFireFootprint2Tex, gEffFireFootprint3Tex, gEffFireFootprint4Tex, + gEffFireFootprint5Tex, gEffFireFootprint6Tex, gEffFireFootprint7Tex, gEffFireFootprint8Tex, + }; + s16 texIdx = (this->rgTexIdx / 100) % 7; + + EffectSs_DrawGEffect(globalCtx, this, fireFootprintTextures[texIdx]); +} + +void EffectSsGFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rgTexIdx += this->rgTexIdxStep; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.h b/soh/src/overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.h new file mode 100644 index 000000000..1fb6af2f2 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Fire/z_eff_ss_g_fire.h @@ -0,0 +1,11 @@ +#ifndef Z_EFF_SS_G_FIRE_H +#define Z_EFF_SS_G_FIRE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; +} EffectSsGFireInitParams; // size = 0xC + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.c b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.c new file mode 100644 index 000000000..6392fd3fc --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.c @@ -0,0 +1,61 @@ +/* + * File: z_eff_ss_g_magma.c + * Overlay: ovl_Effect_Ss_G_Magma + * Description: Magma Bubbles + */ + +#include "z_eff_ss_g_magma.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +u32 EffectSsGMagma_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsGMagma_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGMagma_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_G_Magma_InitVars = { + EFFECT_SS_G_MAGMA, + EffectSsGMagma_Init, +}; + +u32 EffectSsGMagma_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsGMagmaInitParams* initParams = (EffectSsGMagmaInitParams*)initParamsx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + this->velocity = this->accel = zeroVec; + this->pos = initParams->pos; + this->draw = EffectSsGMagma_Draw; + this->update = EffectSsGMagma_Update; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffMagmaBubbleDL); + this->life = 16; + this->rgScale = (s16)(Rand_ZeroOne() * 100.0f) + 200; + this->rgTexIdx = 0; + this->rgTexIdxStep = 50; + this->rgPrimColorR = 255; + this->rgPrimColorG = 255; + this->rgPrimColorB = 0; + this->rgPrimColorA = 255; + this->rgEnvColorR = 255; + this->rgEnvColorG = 0; + this->rgEnvColorB = 0; + this->rgEnvColorA = 0; + + return 1; +} + +static void* sTextures[] = { + gEffMagmaBubble1Tex, gEffMagmaBubble2Tex, gEffMagmaBubble3Tex, gEffMagmaBubble4Tex, + gEffMagmaBubble5Tex, gEffMagmaBubble6Tex, gEffMagmaBubble7Tex, gEffMagmaBubble8Tex, +}; + +void EffectSsGMagma_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s16 texIdx = this->rgTexIdx / 100; + + if (texIdx > 7) { + texIdx = 7; + } + + EffectSs_DrawGEffect(globalCtx, this, sTextures[texIdx]); +} + +void EffectSsGMagma_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rgTexIdx += this->rgTexIdxStep; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.h b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.h new file mode 100644 index 000000000..a63e567e5 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma/z_eff_ss_g_magma.h @@ -0,0 +1,11 @@ +#ifndef Z_EFF_SS_G_MAGMA_H +#define Z_EFF_SS_G_MAGMA_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; +} EffectSsGMagmaInitParams; // size = 0xC + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.c b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.c new file mode 100644 index 000000000..6d0071a24 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.c @@ -0,0 +1,131 @@ +/* + * File: z_eff_ss_g_magma2.c + * Overlay: ovl_Effect_Ss_G_Magma2 + * Description: + */ + +#include "z_eff_ss_g_magma2.h" +#include "objects/object_kingdodongo/object_kingdodongo.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorA regs[2] +#define rEnvColorR regs[3] +#define rEnvColorG regs[4] +#define rEnvColorA regs[5] +#define rTexIdx regs[6] +#define rTimer regs[7] +#define rUpdateRate regs[8] +#define rDrawMode regs[9] +#define rObjBankIdx regs[10] +#define rScale regs[11] + +u32 EffectSsGMagma2_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsGMagma2_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGMagma2_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +static void* sTextures[] = { + object_kingdodongo_Tex_02E4E0, object_kingdodongo_Tex_02E8E0, object_kingdodongo_Tex_02ECE0, + object_kingdodongo_Tex_02F0E0, object_kingdodongo_Tex_02F4E0, object_kingdodongo_Tex_02F8E0, + object_kingdodongo_Tex_02FCE0, object_kingdodongo_Tex_0300E0, object_kingdodongo_Tex_0304E0, + object_kingdodongo_Tex_0308E0, object_kingdodongo_Tex_0308E0, object_kingdodongo_Tex_0308E0, + object_kingdodongo_Tex_0308E0, +}; + +EffectSsInit Effect_Ss_G_Magma2_InitVars = { + EFFECT_SS_G_MAGMA2, + EffectSsGMagma2_Init, +}; + +u32 EffectSsGMagma2_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + s32 objBankIndex = Object_GetIndex(&globalCtx->objectCtx, OBJECT_KINGDODONGO); + s32 pad; + + if ((objBankIndex >= 0) && Object_IsLoaded(&globalCtx->objectCtx, objBankIndex)) { + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + EffectSsGMagma2InitParams* initParams = (EffectSsGMagma2InitParams*)initParamsx; + + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[objBankIndex].segment); + this->rObjBankIdx = objBankIndex; + this->pos = initParams->pos; + this->velocity = zeroVec; + this->accel = zeroVec; + this->life = 100; + this->draw = EffectSsGMagma2_Draw; + this->update = EffectSsGMagma2_Update; + this->gfx = SEGMENTED_TO_VIRTUAL(object_kingdodongo_DL_025A90); + this->rTexIdx = 0; + this->rDrawMode = initParams->drawMode; + this->rUpdateRate = initParams->updateRate; + this->rScale = initParams->scale; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorA = initParams->envColor.a; + + return 1; + } + + return 0; +} + +void EffectSsGMagma2_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale; + void* object; + + scale = this->rScale / 100.0f; + object = globalCtx->objectCtx.status[this->rObjBankIdx].segment; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_g_magma2.c", 261); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x06, object); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_g_magma2.c", 282), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if (this->rDrawMode == 0) { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0x3D); + } else { + POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 0); + } + + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, 0, this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, 0, this->rEnvColorA); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTextures[this->rTexIdx])); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_g_magma2.c", 311); +} + +void EffectSsGMagma2_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rTimer += this->rUpdateRate; + + if (this->rTimer >= 10) { + this->rTimer -= 10; + this->rTexIdx++; + + if (this->rTexIdx >= 10) { + this->life = 0; + } + + if (this->rDrawMode == 0) { + this->rPrimColorG -= 26; + + if (this->rPrimColorG <= 0) { + this->rPrimColorG = 0; + } + + this->rEnvColorR -= 26; + + if (this->rEnvColorR <= 0) { + this->rEnvColorR = 0; + } + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.h b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.h new file mode 100644 index 000000000..d1993832c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Magma2/z_eff_ss_g_magma2.h @@ -0,0 +1,16 @@ +#ifndef Z_EFF_SS_G_MAGMA2_H +#define Z_EFF_SS_G_MAGMA2_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Color_RGBA8 primColor; + /* 0x10 */ Color_RGBA8 envColor; + /* 0x14 */ s16 updateRate; + /* 0x16 */ s16 drawMode; + /* 0x18 */ s16 scale; +} EffectSsGMagma2InitParams; // size = 0x1C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.c b/soh/src/overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.c new file mode 100644 index 000000000..cb9bba2c3 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.c @@ -0,0 +1,129 @@ +/* + * File: z_eff_ss_g_ripple.c + * Overlay: ovl_Effect_Ss_G_Ripple + * Description: Water Ripple + */ + +#include "z_eff_ss_g_ripple.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rWaterBoxNum regs[0] +#define rRadius regs[1] +#define rRadiusMax regs[2] +#define rPrimColorR regs[3] +#define rPrimColorG regs[4] +#define rPrimColorB regs[5] +#define rPrimColorA regs[6] +#define rEnvColorR regs[7] +#define rEnvColorG regs[8] +#define rEnvColorB regs[9] +#define rEnvColorA regs[10] +#define rLifespan regs[11] + +u32 EffectSsGRipple_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsGRipple_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGRipple_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_G_Ripple_InitVars = { + EFFECT_SS_G_RIPPLE, + EffectSsGRipple_Init, +}; + +u32 EffectSsGRipple_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + s32 pad; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + WaterBox* waterBox; + EffectSsGRippleInitParams* initParams = (EffectSsGRippleInitParams*)initParamsx; + + waterBox = NULL; + this->velocity = this->accel = zeroVec; + this->pos = initParams->pos; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffWaterRippleDL); + this->life = initParams->life + 20; + this->flags = 0; + this->draw = EffectSsGRipple_Draw; + this->update = EffectSsGRipple_Update; + this->rRadius = initParams->radius; + this->rRadiusMax = initParams->radiusMax; + this->rLifespan = initParams->life; + this->rPrimColorR = 255; + this->rPrimColorG = 255; + this->rPrimColorB = 255; + this->rPrimColorA = 255; + this->rEnvColorR = 255; + this->rEnvColorG = 255; + this->rEnvColorB = 255; + this->rEnvColorA = 255; + this->rWaterBoxNum = WaterBox_GetSurface2(globalCtx, &globalCtx->colCtx, &initParams->pos, 3.0f, &waterBox); + + return 1; +} + +void EffectSsGRipple_DrawRipple(GlobalContext* globalCtx, EffectSs* this, void* segment) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 radius; + s32 pad; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + Mtx* mtx; + f32 yPos; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_g_ripple.c", 199); + + if (globalCtx) {} + + radius = this->rRadius * 0.0025f; + + if ((this->rWaterBoxNum != -1) && (this->rWaterBoxNum < globalCtx->colCtx.colHeader->numWaterBoxes)) { + yPos = (this->rWaterBoxNum + globalCtx->colCtx.colHeader->waterBoxes)->ySurface; + } else { + yPos = this->pos.y; + } + + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, yPos, this->pos.z); + SkinMatrix_SetScale(&mfScale, radius, radius, radius); + SkinMatrix_MtxFMtxFMult(&mfTrans, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80094BC4(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_g_ripple.c", 247); +} + +void EffectSsGRipple_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if (this->rLifespan == 0) { + EffectSsGRipple_DrawRipple(globalCtx, this, gEffWaterRippleTex); + } +} + +void EffectSsGRipple_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + f32 radius; + f32 primAlpha; + f32 envAlpha; + + if (DECR(this->rLifespan) == 0) { + radius = this->rRadius; + Math_SmoothStepToF(&radius, this->rRadiusMax, 0.2f, 30.0f, 1.0f); + this->rRadius = radius; + + primAlpha = this->rPrimColorA; + envAlpha = this->rEnvColorA; + + Math_SmoothStepToF(&primAlpha, 0.0f, 0.2f, 15.0f, 7.0f); + Math_SmoothStepToF(&envAlpha, 0.0f, 0.2f, 15.0f, 7.0f); + + this->rPrimColorA = primAlpha; + this->rEnvColorA = envAlpha; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.h b/soh/src/overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.h new file mode 100644 index 000000000..afe8e89e1 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Ripple/z_eff_ss_g_ripple.h @@ -0,0 +1,14 @@ +#ifndef Z_EFF_SS_G_RIPPLE_H +#define Z_EFF_SS_G_RIPPLE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 radius; + /* 0x0E */ s16 radiusMax; + /* 0x10 */ s16 life; +} EffectSsGRippleInitParams; // size = 0x14 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.c b/soh/src/overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.c new file mode 100644 index 000000000..f3a362ef9 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.c @@ -0,0 +1,143 @@ +/* + * File: z_eff_ss_g_spk.c + * Overlay: ovl_Effect_Ss_G_Spk + * Description: Sparks + */ + +#include "z_eff_ss_g_spk.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rTexIdx regs[8] +#define rScale regs[9] +#define rScaleStep regs[10] + +u32 EffectSsGSpk_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsGSpk_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGSpk_UpdateNoAccel(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGSpk_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_G_Spk_InitVars = { + EFFECT_SS_G_SPK, + EffectSsGSpk_Init, +}; + +u32 EffectSsGSpk_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsGSpkInitParams* initParams = (EffectSsGSpkInitParams*)initParamsx; + + Math_Vec3f_Copy(&this->pos, &initParams->pos); + Math_Vec3f_Copy(&this->velocity, &initParams->velocity); + Math_Vec3f_Copy(&this->accel, &initParams->accel); + this->gfx = SEGMENTED_TO_VIRTUAL(gEffSparkDL); + + if (initParams->updateMode == 0) { + this->life = 10; + this->vec.x = initParams->pos.x - initParams->actor->world.pos.x; + this->vec.y = initParams->pos.y - initParams->actor->world.pos.y; + this->vec.z = initParams->pos.z - initParams->actor->world.pos.z; + this->update = EffectSsGSpk_Update; + } else { + this->life = 5; + this->update = EffectSsGSpk_UpdateNoAccel; + } + + this->draw = EffectSsGSpk_Draw; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rEnvColorA = initParams->envColor.a; + this->rTexIdx = 0; + this->rScale = initParams->scale; + this->rScaleStep = initParams->scaleStep; + this->actor = initParams->actor; + + return 1; +} + +void EffectSsGSpk_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* sparkTextures[] = { + gEffSpark1Tex, + gEffSpark2Tex, + gEffSpark3Tex, + gEffSpark4Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + Mtx* mtx; + f32 scale; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_g_spk.c", 208); + + scale = this->rScale * 0.0025f; + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sparkTextures[this->rTexIdx])); + func_80094BC4(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + if (1) {} + if (1) {} + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_g_spk.c", 255); +} + +void EffectSsGSpk_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + + this->accel.x = (Rand_ZeroOne() - 0.5f) * 3.0f; + this->accel.z = (Rand_ZeroOne() - 0.5f) * 3.0f; + + if (this->actor != NULL) { + if ((this->actor->category == ACTORCAT_EXPLOSIVE) && (this->actor->update != NULL)) { + this->pos.x = this->actor->world.pos.x + this->vec.x; + this->pos.y = this->actor->world.pos.y + this->vec.y; + this->pos.z = this->actor->world.pos.z + this->vec.z; + } + } + + this->vec.x += this->accel.x; + this->vec.z += this->accel.z; + + this->rTexIdx++; + this->rTexIdx &= 3; + this->rScale += this->rScaleStep; +} + +// this update mode is unused in the original game +// with this update mode, the sparks dont move randomly in the xz plane, appearing to be on top of each other +void EffectSsGSpk_UpdateNoAccel(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if (this->actor != NULL) { + if ((this->actor->category == ACTORCAT_EXPLOSIVE) && (this->actor->update != NULL)) { + this->pos.x += (Math_SinS(this->actor->world.rot.y) * this->actor->speedXZ); + this->pos.z += (Math_CosS(this->actor->world.rot.y) * this->actor->speedXZ); + } + } + + this->rTexIdx++; + this->rTexIdx &= 3; + this->rScale += this->rScaleStep; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.h b/soh/src/overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.h new file mode 100644 index 000000000..a4a22cef1 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Spk/z_eff_ss_g_spk.h @@ -0,0 +1,19 @@ +#ifndef Z_EFF_SS_G_SPK_H +#define Z_EFF_SS_G_SPK_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Actor* actor; + /* 0x04 */ Vec3f pos; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ Color_RGBA8 primColor; + /* 0x2C */ Color_RGBA8 envColor; + /* 0x30 */ s16 scale; + /* 0x32 */ s16 scaleStep; + /* 0x34 */ u8 updateMode; +} EffectSsGSpkInitParams; // size = 0x38 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.c b/soh/src/overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.c new file mode 100644 index 000000000..cb2fd316c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.c @@ -0,0 +1,139 @@ +/* + * File: z_eff_ss_g_splash.c + * Overlay: ovl_Effect_Ss_G_Splash + * Description: Splash + */ + +#include "z_eff_ss_g_splash.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +//! @bug the reuse of regs[11] means that EffectSs_DrawGEffect will treat the type as an object bank index +// this ends up having no effect because the texture provided does not use segment 6 +#define rType regs[11] + +u32 EffectSsGSplash_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParams); +void EffectSsGSplash_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsGSplash_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_G_Splash_InitVars = { + EFFECT_SS_G_SPLASH, + EffectSsGSplash_Init, +}; + +u32 EffectSsGSplash_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsGSplashInitParams* initParams = (EffectSsGSplashInitParams*)initParamsx; + Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; + + this->velocity = this->accel = zeroVec; + this->pos = initParams->pos; + this->draw = EffectSsGSplash_Draw; + this->update = EffectSsGSplash_Update; + + if (initParams->scale == 0) { + initParams->scale = 600; + } + + this->gfx = SEGMENTED_TO_VIRTUAL(gEffWaterSplashDL); + this->life = 8; + this->rgScale = initParams->scale; + this->rgTexIdx = 0; + this->rgTexIdxStep = 100; + + if (initParams->customColor) { + this->rgPrimColorR = initParams->primColor.r; + this->rgPrimColorG = initParams->primColor.g; + this->rgPrimColorB = initParams->primColor.b; + this->rgPrimColorA = initParams->primColor.a; + this->rgEnvColorR = initParams->envColor.r; + this->rgEnvColorG = initParams->envColor.g; + this->rgEnvColorB = initParams->envColor.b; + this->rgEnvColorA = initParams->envColor.a; + this->rType = initParams->type; + } else { + switch (initParams->type) { + case 0: + this->rgPrimColorR = 255; + this->rgPrimColorG = 255; + this->rgPrimColorB = 255; + this->rgPrimColorA = 200; + this->rgEnvColorR = 255; + this->rgEnvColorG = 255; + this->rgEnvColorB = 255; + this->rgEnvColorA = 200; + this->rType = 0; + break; + case 1: + this->rgPrimColorR = 255; + this->rgPrimColorG = 255; + this->rgPrimColorB = 255; + this->rgPrimColorA = 255; + this->rgEnvColorR = 255; + this->rgEnvColorG = 255; + this->rgEnvColorB = 255; + this->rgEnvColorA = 255; + this->rType = 1; + break; + case 2: + this->rgPrimColorR = 255; + this->rgPrimColorG = 255; + this->rgPrimColorB = 255; + this->rgPrimColorA = 200; + this->rgEnvColorR = 255; + this->rgEnvColorG = 255; + this->rgEnvColorB = 255; + this->rgEnvColorA = 200; + this->rType = 2; + break; + } + } + return 1; +} + +void EffectSsGSplash_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* waterSplashTextures[] = { + gEffWaterSplash1Tex, gEffWaterSplash2Tex, gEffWaterSplash3Tex, gEffWaterSplash4Tex, + gEffWaterSplash5Tex, gEffWaterSplash6Tex, gEffWaterSplash7Tex, gEffWaterSplash8Tex, + }; + s16 texIdx; + + switch (this->rType) { + case 0: + texIdx = this->rgTexIdx / 100; + if (texIdx > 7) { + texIdx = 7; + } + EffectSs_DrawGEffect(globalCtx, this, waterSplashTextures[texIdx]); + break; + + case 1: + texIdx = this->rgTexIdx / 100; + if (texIdx > 7) { + texIdx = 7; + } + EffectSs_DrawGEffect(globalCtx, this, waterSplashTextures[texIdx]); + break; + + case 2: + texIdx = this->rgTexIdx / 100; + if (texIdx > 7) { + texIdx = 7; + } + EffectSs_DrawGEffect(globalCtx, this, waterSplashTextures[texIdx]); + break; + + default: + break; + } +} + +void EffectSsGSplash_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + Vec3f newSplashPos; + + if ((this->rType == 1) && (this->life == 5)) { + newSplashPos = this->pos; + newSplashPos.y += ((this->rgScale * 20) * 0.002f); + EffectSsGSplash_Spawn(globalCtx, &newSplashPos, 0, 0, 2, this->rgScale / 2); + } + + this->rgTexIdx += this->rgTexIdxStep; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.h b/soh/src/overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.h new file mode 100644 index 000000000..b761ce332 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_G_Splash/z_eff_ss_g_splash.h @@ -0,0 +1,16 @@ +#ifndef Z_EFF_SS_G_SPLASH_H +#define Z_EFF_SS_G_SPLASH_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ u8 type; + /* 0x0D */ u8 customColor; + /* 0x0E */ s16 scale; + /* 0x10 */ Color_RGBA8 primColor; + /* 0x14 */ Color_RGBA8 envColor; +} EffectSsGSplashInitParams; // size = 0x18 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.c b/soh/src/overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.c new file mode 100644 index 000000000..643b3749b --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.c @@ -0,0 +1,132 @@ +/* + * File: z_eff_ss_hahen.c + * Overlay: ovl_Effect_Ss_Hahen + * Description: Fragments + */ + +#include "z_eff_ss_hahen.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPitch regs[0] +#define rYaw regs[1] +#define rUnused regs[2] +#define rScale regs[3] +#define rObjId regs[4] +#define rObjBankIdx regs[5] +#define rMinLife regs[6] + +u32 EffectSsHahen_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsHahen_DrawGray(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsHahen_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsHahen_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Hahen_InitVars = { + EFFECT_SS_HAHEN, + EffectSsHahen_Init, +}; + +void EffectSsHahen_CheckForObject(EffectSs* this, GlobalContext* globalCtx) { + if (((this->rObjBankIdx = Object_GetIndex(&globalCtx->objectCtx, this->rObjId)) < 0) || + !Object_IsLoaded(&globalCtx->objectCtx, this->rObjBankIdx)) { + this->life = -1; + this->draw = NULL; + } +} + +u32 EffectSsHahen_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsHahenInitParams* initParams = (EffectSsHahenInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = 200; + + if (initParams->dList != NULL) { + this->gfx = initParams->dList; + this->rObjId = initParams->objId; + EffectSsHahen_CheckForObject(this, globalCtx); + } else { + this->gfx = SEGMENTED_TO_VIRTUAL(gEffFragments1DL); + this->rObjId = -1; + } + + if ((this->rObjId == OBJECT_HAKA_OBJECTS) && (this->gfx == gEffFragments2DL)) { + this->draw = EffectSsHahen_DrawGray; + } else { + this->draw = EffectSsHahen_Draw; + } + + this->update = EffectSsHahen_Update; + this->rUnused = initParams->unused; + this->rScale = initParams->scale; + this->rPitch = Rand_ZeroOne() * 314.0f; + this->rYaw = Rand_ZeroOne() * 314.0f; + this->rMinLife = 200 - initParams->life; + + return 1; +} + +void EffectSsHahen_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale = this->rScale * 0.001f; + + OPEN_DISPS(gfxCtx, "../z_eff_hahen.c", 208); + + if (this->rObjId != -1) { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->rObjBankIdx].segment); + } + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_RotateY(this->rYaw * 0.01f, MTXMODE_APPLY); + Matrix_RotateX(this->rPitch * 0.01f, MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_hahen.c", 228), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(globalCtx->state.gfxCtx); + gSPDisplayList(POLY_OPA_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_hahen.c", 236); +} + +// in the original game this function is hardcoded to be used only by the skull pots in Shadow Temple +void EffectSsHahen_DrawGray(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale = this->rScale * 0.001f; + + OPEN_DISPS(gfxCtx, "../z_eff_hahen.c", 253); + + if (this->rObjId != -1) { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->rObjBankIdx].segment); + } + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_RotateY(this->rYaw * 0.01f, MTXMODE_APPLY); + Matrix_RotateX(this->rPitch * 0.01f, MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_hahen.c", 271), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(globalCtx->state.gfxCtx); + gDPSetCombineLERP(POLY_OPA_DISP++, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, SHADE, 0, PRIMITIVE, 0, SHADE, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x0, 0x01, 100, 100, 120, 255); + gSPDisplayList(POLY_OPA_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_hahen.c", 288); +} + +void EffectSsHahen_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + Player* player = GET_PLAYER(globalCtx); + + this->rPitch += 55; + this->rYaw += 10; + + if ((this->pos.y <= player->actor.floorHeight) && (this->life < this->rMinLife)) { + this->life = 0; + } + + if (this->rObjId != -1) { + EffectSsHahen_CheckForObject(this, globalCtx); + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h b/soh/src/overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h new file mode 100644 index 000000000..990699431 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Hahen/z_eff_ss_hahen.h @@ -0,0 +1,20 @@ +#ifndef Z_EFF_SS_HAHEN_H +#define Z_EFF_SS_HAHEN_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ Gfx* dList; + /* 0x28 */ s16 unused; + /* 0x2A */ s16 scale; + /* 0x2C */ s16 objId; + /* 0x2C */ s16 life; +} EffectSsHahenInitParams; // size = 0x30 + +#define HAHEN_OBJECT_DEFAULT -1 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.c b/soh/src/overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.c new file mode 100644 index 000000000..7a626e406 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.c @@ -0,0 +1,122 @@ +/* + * File: z_eff_ss_hitmark.c + * Overlay: ovl_Effect_Ss_HitMark + * Description: Hit Marks + */ + +#include "z_eff_ss_hitmark.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rTexIdx regs[0] +#define rType regs[1] +#define rPrimColorR regs[2] +#define rPrimColorG regs[3] +#define rPrimColorB regs[4] +#define rEnvColorR regs[5] +#define rEnvColorG regs[6] +#define rEnvColorB regs[7] +#define rScale regs[8] + +u32 EffectSsHitMark_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsHitMark_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsHitMark_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +static Color_RGB8 sColors[] = { + { 255, 255, 255 }, { 255, 255, 0 }, { 255, 255, 255 }, { 255, 0, 0 }, { 255, 200, 100 }, { 200, 150, 0 }, + { 150, 100, 0 }, { 100, 50, 0 }, { 255, 255, 255 }, { 255, 0, 0 }, { 255, 255, 0 }, { 255, 0, 0 }, + { 255, 255, 255 }, { 0, 255, 200 }, { 255, 255, 255 }, { 150, 0, 255 }, +}; + +static void* sTextures[] = { + gEffHitMark1Tex, gEffHitMark2Tex, gEffHitMark3Tex, gEffHitMark4Tex, gEffHitMark5Tex, gEffHitMark6Tex, + gEffHitMark7Tex, gEffHitMark8Tex, gEffHitMark9Tex, gEffHitMark10Tex, gEffHitMark11Tex, gEffHitMark12Tex, + gEffHitMark13Tex, gEffHitMark14Tex, gEffHitMark15Tex, gEffHitMark16Tex, gEffHitMark17Tex, gEffHitMark18Tex, + gEffHitMark19Tex, gEffHitMark20Tex, gEffHitMark21Tex, gEffHitMark22Tex, gEffHitMark23Tex, gEffHitMark24Tex, + gEffHitMark1Tex, gEffHitMark2Tex, gEffHitMark3Tex, gEffHitMark4Tex, gEffHitMark5Tex, gEffHitMark6Tex, + gEffHitMark7Tex, gEffHitMark8Tex, +}; + +EffectSsInit Effect_Ss_HitMark_InitVars = { + EFFECT_SS_HITMARK, + EffectSsHitMark_Init, +}; + +u32 EffectSsHitMark_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + s32 colorIdx; + EffectSsHitMarkInitParams* initParams = (EffectSsHitMarkInitParams*)initParamsx; + this->pos = initParams->pos; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffHitMarkDL); + + if (initParams->type == EFFECT_HITMARK_DUST) { + this->life = 16; + } else { + this->life = 8; + } + + this->draw = EffectSsHitMark_Draw; + this->update = EffectSsHitMark_Update; + colorIdx = initParams->type * 4; + this->rTexIdx = 0; + this->rType = initParams->type; + this->rPrimColorR = sColors[colorIdx].r; + this->rPrimColorG = sColors[colorIdx].g; + this->rPrimColorB = sColors[colorIdx].b; + this->rEnvColorR = sColors[colorIdx + 1].r; + this->rEnvColorG = sColors[colorIdx + 1].g; + this->rEnvColorB = sColors[colorIdx + 1].b; + this->rScale = initParams->scale; + + return 1; +} + +void EffectSsHitMark_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfTrans; + MtxF mfScale; + MtxF mfResult; + MtxF mfTrans11DA0; + Mtx* mtx; + f32 scale; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_hitmark.c", 297); + + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + scale = this->rScale / 100.0f; + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfScale, &mfResult); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sTextures[(this->rType * 8) + (this->rTexIdx)])); + func_80094C50(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, 255); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, 0); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + CLOSE_DISPS(gfxCtx, "../z_eff_ss_hitmark.c", 341); +} + +void EffectSsHitMark_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 colorIdx; + + if (this->rType == EFFECT_HITMARK_DUST) { + this->rTexIdx = (15 - this->life) / 2; + } else { + this->rTexIdx = 7 - this->life; + } + + if (this->rTexIdx != 0) { + colorIdx = this->rType * 4 + 2; + this->rPrimColorR = func_80027DD4(this->rPrimColorR, sColors[colorIdx].r, this->life + 1); + this->rPrimColorG = func_80027DD4(this->rPrimColorG, sColors[colorIdx].g, this->life + 1); + this->rPrimColorB = func_80027DD4(this->rPrimColorB, sColors[colorIdx].b, this->life + 1); + this->rEnvColorR = func_80027DD4(this->rEnvColorR, sColors[colorIdx + 1].r, this->life + 1); + this->rEnvColorG = func_80027DD4(this->rEnvColorG, sColors[colorIdx + 1].g, this->life + 1); + this->rEnvColorB = func_80027DD4(this->rEnvColorB, sColors[colorIdx + 1].b, this->life + 1); + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h b/soh/src/overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h new file mode 100644 index 000000000..74f8e7f80 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h @@ -0,0 +1,20 @@ +#ifndef Z_EFF_SS_HITMARK_H +#define Z_EFF_SS_HITMARK_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ s32 type; + /* 0x04 */ s16 scale; + /* 0x08 */ Vec3f pos; +} EffectSsHitMarkInitParams; // size = 0x14 + +typedef enum { + EFFECT_HITMARK_WHITE, + EFFECT_HITMARK_DUST, + EFFECT_HITMARK_RED, + EFFECT_HITMARK_METAL +} EffectSsHitmarkType; + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.c b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.c new file mode 100644 index 000000000..8b5808bc4 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.c @@ -0,0 +1,86 @@ +/* + * File: z_eff_ss_ice_piece.c + * Overlay: ovl_Effect_Ss_Ice_Piece + * Description: Ice Shards + */ + +#include "z_eff_ss_ice_piece.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rLifespan regs[0] +#define rYaw regs[1] +#define rPitch regs[2] +#define rRotSpeed regs[3] +#define rScale regs[4] + +u32 EffectSsIcePiece_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsIcePiece_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsIcePiece_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Ice_Piece_InitVars = { + EFFECT_SS_ICE_PIECE, + EffectSsIcePiece_Init, +}; + +u32 EffectSsIcePiece_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsIcePieceInitParams* initParams = (EffectSsIcePieceInitParams*)initParamsx; + + this->pos = initParams->pos; + this->vec = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = initParams->life; + this->draw = EffectSsIcePiece_Draw; + this->update = EffectSsIcePiece_Update; + this->rLifespan = initParams->life; + this->rScale = initParams->scale * 100.0f; + this->rYaw = Math_Atan2S(initParams->velocity.z, initParams->velocity.x); + this->rPitch = 0; + this->rRotSpeed = + ((fabsf(initParams->velocity.x) + fabsf(initParams->velocity.y)) * 100.0f) * (Rand_ZeroFloat(1.0f) + 0.5f); + + return 1; +} + +void EffectSsIcePiece_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale; + u32 frames; + f32 alpha; + + scale = this->rScale * 0.01f; + frames = globalCtx->state.frames; + + OPEN_DISPS(gfxCtx, "../z_eff_ice_piece.c", 161); + + if ((this->rLifespan > 0) && (this->life < (this->rLifespan >> 1))) { + alpha = ((this->life * 2.0f) / this->rLifespan); + alpha *= 255.0f; + } else { + alpha = 255.0f; + } + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + Matrix_RotateY(this->rYaw * (M_PI / 0x8000), MTXMODE_APPLY); + Matrix_RotateX(this->rPitch * (M_PI / 0x8000), MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ice_piece.c", 185), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 50, 100, (s32)alpha & 0xFF); + func_8003435C(&this->pos, globalCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (1 * frames) % 256, 0x20, 0x10, 1, 0, (2 * frames) % 256, + 0x40, 0x20)); + gSPDisplayList(POLY_XLU_DISP++, gEffIceFragment1DL); + + CLOSE_DISPS(gfxCtx, "../z_eff_ice_piece.c", 209); +} + +void EffectSsIcePiece_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rPitch += this->rRotSpeed; + this->velocity.x = this->velocity.x * 0.85f; + this->velocity.y = this->velocity.y * 0.85f; + this->velocity.z = this->velocity.z * 0.85f; +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.h b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.h new file mode 100644 index 000000000..2a697d74e --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Piece/z_eff_ss_ice_piece.h @@ -0,0 +1,15 @@ +#ifndef Z_EFF_SS_ICE_PIECE_H +#define Z_EFF_SS_ICE_PIECE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ f32 scale; + /* 0x10 */ Vec3f velocity; + /* 0x1C */ Vec3f accel; + /* 0x28 */ s32 life; +} EffectSsIcePieceInitParams; // size = 0xC + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.c b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.c new file mode 100644 index 000000000..002a05486 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.c @@ -0,0 +1,107 @@ +/* + * File: z_eff_ss_ice_smoke.c + * Overlay: ovl_Effect_Ss_Ice_Smoke + * Description: Ice Smoke + */ + +#include "z_eff_ss_ice_smoke.h" +#include "objects/object_fz/object_fz.h" + +#define rObjBankIdx regs[0] +#define rAlpha regs[1] +#define rScale regs[2] + +u32 EffectSsIceSmoke_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsIceSmoke_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsIceSmoke_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Ice_Smoke_InitVars = { + EFFECT_SS_ICE_SMOKE, + EffectSsIceSmoke_Init, +}; + +u32 EffectSsIceSmoke_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsIceSmokeInitParams* initParams = (EffectSsIceSmokeInitParams*)initParamsx; + s32 pad; + s32 objBankIdx; + void* oldSeg6; + + objBankIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_FZ); + + if ((objBankIdx > -1) && Object_IsLoaded(&globalCtx->objectCtx, objBankIdx)) { + oldSeg6 = gSegments[6]; + gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[objBankIdx].segment); + Math_Vec3f_Copy(&this->pos, &initParams->pos); + Math_Vec3f_Copy(&this->velocity, &initParams->velocity); + Math_Vec3f_Copy(&this->accel, &initParams->accel); + this->rObjBankIdx = objBankIdx; + this->rAlpha = 0; + this->rScale = initParams->scale; + this->life = 50; + this->draw = EffectSsIceSmoke_Draw; + this->update = EffectSsIceSmoke_Update; + gSegments[6] = oldSeg6; + + return 1; + } + + osSyncPrintf("Effect_SS_Ice_Smoke_ct():バンク Object_Bank_Fzが有りません。\n"); + + return 0; +} + +void EffectSsIceSmoke_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 pad; + void* object; + Mtx* mtx; + f32 scale; + s32 objBankIdx; + + object = globalCtx->objectCtx.status[this->rObjBankIdx].segment; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_ice_smoke.c", 155); + + objBankIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_FZ); + + if ((objBankIdx > -1) && Object_IsLoaded(&globalCtx->objectCtx, objBankIdx)) { + gDPPipeSync(POLY_XLU_DISP++); + func_80093D84(globalCtx->state.gfxCtx); + gSegments[6] = VIRTUAL_TO_PHYSICAL(object); + gSPSegment(POLY_XLU_DISP++, 0x06, object); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFreezardSteamStartDL)); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 195, 235, 235, this->rAlpha); + gSPSegment( + POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, this->life * 3, this->life * 15, 32, 64, 1, 0, 0, 32, 32)); + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + scale = this->rScale * 0.0001f; + Matrix_Scale(scale, scale, 1.0f, MTXMODE_APPLY); + + mtx = Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_ss_ice_smoke.c", 196); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gFreezardSteamDL)); + } + } else { + this->life = -1; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_eff_ss_ice_smoke.c", 210); +} + +void EffectSsIceSmoke_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 objBankIdx; + + objBankIdx = Object_GetIndex(&globalCtx->objectCtx, OBJECT_FZ); + + if ((objBankIdx > -1) && Object_IsLoaded(&globalCtx->objectCtx, objBankIdx)) { + if (this->rAlpha < 100) { + this->rAlpha += 10; + } + } else { + this->life = -1; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.h b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.h new file mode 100644 index 000000000..08fc6f745 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Ice_Smoke/z_eff_ss_ice_smoke.h @@ -0,0 +1,14 @@ +#ifndef Z_EFF_SS_ICE_SMOKE_H +#define Z_EFF_SS_ICE_SMOKE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; +} EffectSsIceSmokeInitParams; // size = 0x28 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.c b/soh/src/overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.c new file mode 100644 index 000000000..237ecb963 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.c @@ -0,0 +1,110 @@ +/* + * File: z_eff_ss_k_fire.c + * Overlay: ovl_Effect_Ss_K_Fire + * Description: + */ + +#include "z_eff_ss_k_fire.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rAlpha regs[0] +#define rScroll regs[2] +#define rType regs[3] +#define rYScale regs[4] +#define rXZScale regs[5] +#define rScaleMax regs[6] + +u32 EffectSsKFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsKFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsKFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_K_Fire_InitVars = { + EFFECT_SS_K_FIRE, + EffectSsKFire_Init, +}; + +u32 EffectSsKFire_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsKFireInitParams* initParams = (EffectSsKFireInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = 100; + this->rScaleMax = initParams->scaleMax; + this->rAlpha = 255; + this->rScroll = (s16)Rand_ZeroFloat(5.0f) - 0x19; + this->rType = initParams->type; + this->draw = EffectSsKFire_Draw; + this->update = EffectSsKFire_Update; + + return 1; +} + +void EffectSsKFire_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 xzScale; + f32 yScale; + + xzScale = this->rXZScale / 10000.0f; + yScale = this->rYScale / 10000.0f; + + OPEN_DISPS(gfxCtx, "../z_eff_k_fire.c", 152); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(xzScale, yScale, xzScale, MTXMODE_APPLY); + func_80093D84(globalCtx->state.gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, 0, 0x20, 0x40, 1, 0, + globalCtx->state.frames * this->rScroll, 0x20, 0x80)); + + if (this->rType >= 100) { + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 0, this->rAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 10, 0, 0); + } else { + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 255, this->rAlpha); + gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 255, 0); + } + + if (1) {} + + gDPPipeSync(POLY_XLU_DISP++); + Matrix_ReplaceRotation(&globalCtx->billboardMtxF); + + if ((index & 1) != 0) { + Matrix_RotateY(M_PI, MTXMODE_APPLY); + } + + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_eff_k_fire.c", 215), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gEffFire1DL); + + CLOSE_DISPS(gfxCtx, "../z_eff_k_fire.c", 220); +} + +void EffectSsKFire_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if (this->rXZScale < this->rScaleMax) { + this->rXZScale += 4; + this->rYScale += 4; + + if (this->rXZScale > this->rScaleMax) { + this->rXZScale = this->rScaleMax; + + if (this->rType != 3) { + this->rYScale = this->rScaleMax; + } + } + } else { + if (this->rAlpha > 0) { + this->rAlpha -= 10; + if (this->rAlpha <= 0) { + this->rAlpha = 0; + this->life = 0; + } + } + } + + if (this->rType == 3) { + this->rYScale++; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.h b/soh/src/overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.h new file mode 100644 index 000000000..e8b2c617c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_K_Fire/z_eff_ss_k_fire.h @@ -0,0 +1,15 @@ +#ifndef Z_EFF_SS_K_FIRE_H +#define Z_EFF_SS_K_FIRE_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scaleMax; + /* 0x26 */ u8 type; +} EffectSsKFireInitParams; // size = 0x28 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.c b/soh/src/overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.c new file mode 100644 index 000000000..b1ef28263 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.c @@ -0,0 +1,421 @@ +/* + * File: z_eff_ss_kakera.c + * Overlay: ovl_Effect_Ss_Kakera + * Description: Fragments. Appearance is determined by the supplied display list. + */ + +#include "z_eff_ss_kakera.h" + +#define rReg0 regs[0] +#define rGravity regs[1] +#define rPitch regs[2] +#define rYaw regs[3] +#define rReg4 regs[4] +#define rReg5 regs[5] +#define rReg6 regs[6] +#define rScale regs[7] +#define rReg8 regs[8] +#define rReg9 regs[9] +#define rObjId regs[10] +#define rObjBankIdx regs[11] +#define rColorIdx regs[12] + +u32 EffectSsKakera_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsKakera_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsKakera_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +void func_809A9BA8(EffectSs* this, GlobalContext* globalCtx); + +EffectSsInit Effect_Ss_Kakera_InitVars = { + EFFECT_SS_KAKERA, + EffectSsKakera_Init, +}; + +u32 EffectSsKakera_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsKakeraInitParams* initParams = (EffectSsKakeraInitParams*)initParamsx; + s32 objId; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->life = initParams->life; + this->priority = 101; + + if (initParams->dList != NULL) { + this->gfx = initParams->dList; + objId = initParams->objId; + + if (objId == OBJECT_GAMEPLAY_KEEP || objId == OBJECT_GAMEPLAY_FIELD_KEEP || + objId == OBJECT_GAMEPLAY_DANGEON_KEEP) { + this->rObjId = KAKERA_OBJECT_DEFAULT; + } else { + this->rObjId = initParams->objId; + func_809A9BA8(this, globalCtx); + } + + } else { + osSyncPrintf("shape_modelがNULL\n"); + LogUtils_HungupThread("../z_eff_kakera.c", 178); + } + + this->draw = EffectSsKakera_Draw; + this->update = EffectSsKakera_Update; + this->vec = initParams->unk_18; + this->rReg0 = initParams->unk_2C; + this->rGravity = initParams->gravity; + this->rPitch = Rand_ZeroOne() * 32767.0f; + this->rYaw = Rand_ZeroOne() * 32767.0f; + this->rReg4 = initParams->unk_26; + this->rReg5 = initParams->unk_28; + this->rReg6 = initParams->unk_2A; + this->rScale = initParams->scale; + this->rReg8 = initParams->unk_30; + this->rReg9 = initParams->unk_32; + this->rColorIdx = initParams->colorIdx; + + return 1; +} + +f32 func_809A9818(f32 arg0, f32 arg1) { + f32 temp_f2; + + if (arg1 < 0.0f) { + osSyncPrintf("範囲がマイナス!!(randomD_sectionUniformity)\n"); + } + + temp_f2 = Rand_ZeroOne() * arg1; + return ((temp_f2 * 2.0f) - arg1) + arg0; +} + +void EffectSsKakera_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static Color_RGB8 colors[] = { { 255, 255, 255 }, { 235, 170, 130 } }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + f32 scale; + s32 colorIdx; + + scale = this->rScale / 256.0f; + colorIdx = this->rColorIdx; + + OPEN_DISPS(gfxCtx, "../z_eff_kakera.c", 241); + + if (this->rObjId != KAKERA_OBJECT_DEFAULT) { + if ((((this->rReg4 >> 7) & 1) << 7) == 0x80) { + gSPSegment(POLY_XLU_DISP++, 0x06, globalCtx->objectCtx.status[this->rObjBankIdx].segment); + } else { + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->rObjBankIdx].segment); + } + } + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_RotateY(this->rYaw * 0.01f, MTXMODE_APPLY); + Matrix_RotateX(this->rPitch * 0.01f, MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + + if ((((this->rReg4 >> 7) & 1) << 7) == 0x80) { + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_kakera.c", 268), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D84(globalCtx->state.gfxCtx); + + if (colorIdx >= 0) { + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, colors[colorIdx].r, colors[colorIdx].g, colors[colorIdx].b, 255); + } + + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } else { + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_kakera.c", 286), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(globalCtx->state.gfxCtx); + + if (colorIdx >= 0) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, colors[colorIdx].r, colors[colorIdx].g, colors[colorIdx].b, 255); + } + + gSPDisplayList(POLY_OPA_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_kakera.c", 302); +} + +void func_809A9BA8(EffectSs* this, GlobalContext* globalCtx) { + this->rObjBankIdx = Object_GetIndex(&globalCtx->objectCtx, this->rObjId); + + if ((this->rObjBankIdx < 0) || !Object_IsLoaded(&globalCtx->objectCtx, this->rObjBankIdx)) { + this->life = 0; + this->draw = NULL; + } +} + +void func_809A9C10(EffectSs* this) { + f32 temp_f14; + f32 temp_f12; + f32 temp_f16; + f32 temp_f2; + f32 temp_f18; + f32 temp_f20; + f32 temp_f0; + + temp_f18 = this->rReg5 / 1024.0f; + temp_f20 = this->rReg6 / 1024.0f; + temp_f14 = (this->rReg9 / 1024.0f) * 4.0f; + + temp_f2 = this->velocity.x - func_809A9818(0.0f, temp_f14); + temp_f16 = this->velocity.y - func_809A9818(0.0f, temp_f14); + temp_f12 = this->velocity.z - func_809A9818(0.0f, temp_f14); + + if (temp_f2 > 0.0f) { + this->velocity.x -= ((temp_f2 * temp_f18) + (SQ(temp_f2) * temp_f20)); + } else { + this->velocity.x -= ((temp_f2 * temp_f18) - (SQ(temp_f2) * temp_f20)); + } + + if (temp_f16 > 0.0f) { + temp_f0 = temp_f16 * temp_f18; + temp_f2 = SQ(temp_f16) * temp_f20; + this->velocity.y -= (temp_f0 + temp_f2); + } else { + temp_f0 = temp_f16 * temp_f18; + temp_f2 = SQ(temp_f16) * temp_f20; + this->velocity.y -= (temp_f0 - temp_f2); + } + + if (temp_f12 > 0.0f) { + this->velocity.z -= (temp_f0 + temp_f2); + } else { + this->velocity.z -= (temp_f0 - temp_f2); + } +} + +void func_809A9DC0(EffectSs* this) { + this->accel.x = this->accel.y = this->accel.z = 0.0f; +} + +f32 func_809A9DD8(f32 arg0, s32 arg1) { + return 1.0f; +} + +static f32 D_809AA530[] = { + 1.0f, 100.0f, 40.0f, 5.0f, 100.0f, 40.0f, 5.0f, 100.0f, 40.0f, 5.0f, +}; + +f32 func_809A9DEC(f32 arg0, s32 arg1) { + if (D_809AA530[arg1] < arg0) { + return D_809AA530[arg1] / arg0; + } else { + return 1.0f; + } +} + +f32 func_809A9E28(f32 arg0, s32 arg1) { + f32 temp = SQ(arg0); + + if (D_809AA530[arg1] < temp) { + return D_809AA530[arg1] / temp; + } else { + return 1.0f; + } +} + +f32 func_809A9E68(f32 arg0, s32 arg1) { + return func_809A9E28(arg0, arg1); +} + +s32 func_809A9E88(EffectSs* this, Vec3f* diff, f32 dist) { + static f32 D_809AA558[] = { 0.05f, 1.0f }; + s32 temp_v0; + f32 phi_f0; + + temp_v0 = this->rReg0 & 3; + + if (temp_v0 != 0) { + + if (dist > 1.0f) { + phi_f0 = 1.0f / dist; + } else { + phi_f0 = 1.0f; + } + + this->accel.x += ((D_809AA558[temp_v0 - 1] * diff->z) * phi_f0); + this->accel.z -= ((D_809AA558[temp_v0 - 1] * diff->x) * phi_f0); + } + + return 1; +} + +s32 func_809A9F10(EffectSs* this, Vec3f* diff, f32 dist) { + static f32 D_809AA560[] = { 4.0f, 0.1f, 0.3f, 0.9f, -0.1f, -0.3f, -0.9f }; + s32 temp_v0; + + temp_v0 = (this->rReg0 >> 2) & 7; + + if (temp_v0 != 0) { + this->accel.y += D_809AA560[temp_v0]; + } + + return 1; +} + +s32 func_809A9F4C(EffectSs* this, Vec3f* diff, f32 dist) { + static f32 D_809AA57C[] = { 0.1f, 1.0f, 6.0f }; + s32 temp_v0; + f32 phi_f0; + + temp_v0 = (this->rReg0 >> 5) & 3; + + if (temp_v0 != 0) { + + if (dist > 1.0f) { + phi_f0 = 1.0f / dist; + } else { + phi_f0 = 1.0f; + } + + this->accel.x -= ((diff->x * D_809AA57C[temp_v0 - 1]) * phi_f0); + this->accel.z -= ((diff->z * D_809AA57C[temp_v0 - 1]) * phi_f0); + } + + return 1; +} + +s32 func_809A9FD8(EffectSs* this, Vec3f* diff, f32 dist) { + static f32 (*D_809AA588[])(f32 dist, s32 arg1) = { + func_809A9DD8, func_809A9DEC, func_809A9DEC, func_809A9DEC, func_809A9E28, + func_809A9E28, func_809A9E28, func_809A9E68, func_809A9E68, func_809A9E68, + }; + f32 temp_f0; + s32 temp_a1; + + temp_a1 = (this->rReg0 >> 7) & 0xF; + temp_f0 = D_809AA588[temp_a1](dist, temp_a1); + temp_f0 = func_809A9818(temp_f0, (this->rReg9 * temp_f0) / 1024.0f); + + this->accel.x *= temp_f0; + this->accel.y *= temp_f0; + this->accel.z *= temp_f0; + + this->accel.x += temp_f0 * 0.01f; + this->accel.y += temp_f0 * 0.01f; + this->accel.z += temp_f0 * 0.01f; + + return 1; +} + +s32 func_809AA0B8(EffectSs* this, Vec3f* diff, f32 dist) { + this->accel.y += this->rGravity / 256.0f; + + return 1; +} + +s32 func_809AA0EC(EffectSs* this) { + Vec3f diff; + f32 dist; + + func_809A9DC0(this); + + diff.x = this->pos.x - this->vec.x; + diff.y = this->pos.y - this->vec.y; + diff.z = this->pos.z - this->vec.z; + + dist = sqrtf(SQ(diff.x) + SQ(diff.y) + SQ(diff.z)); + + if (dist > 1000.0f) { + return 0; + } + + if (this->rReg0 != 0) { + if (!func_809A9E88(this, &diff, dist)) { + return false; + } + + if (!func_809A9F10(this, &diff, dist)) { + return false; + } + + if (!func_809A9F4C(this, &diff, dist)) { + return false; + } + + if (!func_809A9FD8(this, &diff, dist)) { + return false; + } + } + + if (!func_809AA0B8(this, &diff, dist)) { + return false; + } + + return true; +} + +void func_809AA230(EffectSs* this, GlobalContext* globalCtx) { + static f32 D_809AA5B0[] = { 10.0f, 20.0f, 40.0f }; + Player* player = GET_PLAYER(globalCtx); + + if (this->rReg8 == 0) { + if ((((this->rReg4 >> 4) & 1) * 0x10) == 0x10) { + if (this->pos.y <= (player->actor.floorHeight - ((this->rReg4 >> 2) & 3))) { + this->rReg9 = 0; + this->rReg0 = 0; + this->rReg4 &= ~0x60; + this->accel.x = this->accel.y = this->accel.z = 0.0f; + this->velocity.x = this->velocity.y = this->velocity.z = 0.0f; + this->rReg5 = this->rReg9; + this->rGravity = this->rReg9; + } + } else { + if (this->pos.y <= ((player->actor.floorHeight - ((this->rReg4 >> 2) & 3)) - 600.0f)) { + this->life = 0; + } + } + } else { + switch (this->rReg4 & 3) { + case 0: + this->rReg8 = 0; + break; + case 1: + if (this->velocity.y < 0.0f) { + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &this->pos, D_809AA5B0[(this->rReg4 >> 2) & 3])) { + this->velocity.x *= func_809A9818(0.9f, 0.2f); + this->velocity.y *= -0.8f; + this->velocity.z *= func_809A9818(0.9f, 0.2f); + + if (this->rReg8 > 0) { + this->rReg8 -= 1; + } + } + } + break; + case 2: + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &this->pos, D_809AA5B0[(this->rReg4 >> 2) & 3])) {} + break; + } + } +} + +void EffectSsKakera_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + switch (((this->rReg4 >> 5) & 3) << 5) { + case 0x20: + this->rPitch += 0xB; + this->rYaw += 3; + break; + case 0x40: + this->rPitch += 0x41; + this->rYaw += 0xB; + break; + case 0x60: + this->rPitch += 0x9B; + this->rYaw += 0x1F; + break; + } + + func_809A9C10(this); + + if (!func_809AA0EC(this)) { + this->life = 0; + } + + func_809AA230(this, globalCtx); + + if (this->rObjId != KAKERA_OBJECT_DEFAULT) { + func_809A9BA8(this, globalCtx); + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h b/soh/src/overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h new file mode 100644 index 000000000..17a4c8da1 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Kakera/z_eff_ss_kakera.h @@ -0,0 +1,33 @@ +#ifndef Z_EFF_SS_KAKERA_H +#define Z_EFF_SS_KAKERA_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f unk_18; + /* 0x24 */ s16 gravity; + /* 0x26 */ s16 unk_26; + /* 0x28 */ s16 unk_28; + /* 0x2A */ s16 unk_2A; + /* 0x2C */ s16 unk_2C; + /* 0x2E */ s16 scale; + /* 0x30 */ s16 unk_30; + /* 0x32 */ s16 unk_32; + /* 0x34 */ s32 life; + /* 0x38 */ s16 colorIdx; + /* 0x3A */ s16 objId; + /* 0x3C */ Gfx* dList; +} EffectSsKakeraInitParams; // size = 0x40 + +#define KAKERA_OBJECT_DEFAULT -1 + +typedef enum { + /* -1 */ KAKERA_COLOR_NONE = -1, + /* 0 */ KAKERA_COLOR_WHITE, + /* 1 */ KAKERA_COLOR_BROWN +} KakeraColorIndex; + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.c b/soh/src/overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.c new file mode 100644 index 000000000..535d4e7b5 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.c @@ -0,0 +1,152 @@ +/* + * File: z_eff_ss_kirakira.c + * Overlay: ovl_Effect_Ss_KiraKira + * Description: Sparkles + */ + +#include "z_eff_ss_kirakira.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rRotSpeed regs[0] +#define rYaw regs[1] +#define rPrimColorR regs[2] +#define rPrimColorG regs[3] +#define rPrimColorB regs[4] +#define rPrimColorA regs[5] +#define rEnvColorR regs[6] +#define rEnvColorG regs[7] +#define rEnvColorB regs[8] +#define rEnvColorA regs[9] +#define rAlphaStep regs[10] +#define rScale regs[11] +#define rLifespan regs[12] + +u32 EffectSsKiraKira_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsKiraKira_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void func_809AABF0(GlobalContext* globalCtx, u32 index, EffectSs* this); +void func_809AACAC(GlobalContext* globalCtx, u32 index, EffectSs* this); +void func_809AAD6C(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_KiraKira_InitVars = { + EFFECT_SS_KIRAKIRA, + EffectSsKiraKira_Init, +}; + +u32 EffectSsKiraKira_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsKiraKiraInitParams* initParams = (EffectSsKiraKiraInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + + if ((this->life = initParams->life) < 0) { + this->life = -this->life; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffSparklesDL); + this->update = func_809AAD6C; + this->rEnvColorA = initParams->scale; + this->rScale = 0; + } else { + this->gfx = SEGMENTED_TO_VIRTUAL(gEffSparklesDL); + + if (initParams->updateMode == 0) { + this->update = func_809AABF0; + } else { + this->update = func_809AACAC; + } + + this->rEnvColorA = initParams->envColor.a; + this->rScale = initParams->scale; + } + + this->draw = EffectSsKiraKira_Draw; + this->rRotSpeed = initParams->rotSpeed; + this->rYaw = initParams->yaw; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rAlphaStep = initParams->alphaStep; + this->rLifespan = initParams->life; + + return 1; +} + +void EffectSsKiraKira_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx; + f32 scale; + s32 pad; + MtxF mfTrans; + MtxF mfRotY; + MtxF mfScale; + MtxF mfTrans11DA0; + MtxF mfTrans11DA0RotY; + MtxF mfResult; + Mtx* mtx; + + scale = this->rScale / 10000.0f; + gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_kirakira.c", 257); + + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + SkinMatrix_SetRotateZYX(&mfRotY, 0, 0, this->rYaw); + SkinMatrix_SetScale(&mfScale, scale, scale, 1.0f); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfRotY, &mfTrans11DA0RotY); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0RotY, &mfScale, &mfResult); + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093C14(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + (((s8)((55.0f / this->rLifespan) * this->life) + 200))); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_kirakira.c", 301); +} + +void func_809AABF0(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->accel.x = (Rand_ZeroOne() * 0.4f) - 0.2f; + this->accel.z = (Rand_ZeroOne() * 0.4f) - 0.2f; + this->rEnvColorA += this->rAlphaStep; + + if (this->rEnvColorA < 0) { + this->rEnvColorA = 0; + this->rAlphaStep = -this->rAlphaStep; + } else if (this->rEnvColorA > 255) { + this->rEnvColorA = 255; + this->rAlphaStep = -this->rAlphaStep; + } + + this->rYaw += this->rRotSpeed; +} + +void func_809AACAC(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->velocity.x *= 0.95f; + this->velocity.z *= 0.95f; + this->accel.x = Rand_CenteredFloat(0.2f); + this->accel.z = Rand_CenteredFloat(0.2f); + this->rEnvColorA += this->rAlphaStep; + + if (this->rEnvColorA < 0) { + this->rEnvColorA = 0; + this->rAlphaStep = -this->rAlphaStep; + } else if (this->rEnvColorA > 255) { + this->rEnvColorA = 255; + this->rAlphaStep = -this->rAlphaStep; + } + + this->rYaw += this->rRotSpeed; +} + +void func_809AAD6C(GlobalContext* globalCtx, u32 index, EffectSs* this) { + this->rScale = this->rEnvColorA * Math_SinS((32768.0f / this->rLifespan) * this->life); +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.h b/soh/src/overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.h new file mode 100644 index 000000000..290fec68d --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_KiraKira/z_eff_ss_kirakira.h @@ -0,0 +1,21 @@ +#ifndef Z_EFF_SS_KIRAKIRA_H +#define Z_EFF_SS_KIRAKIRA_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ Color_RGBA8 primColor; + /* 0x28 */ Color_RGBA8 envColor; + /* 0x2C */ s16 alphaStep; + /* 0x2E */ s16 scale; + /* 0x30 */ s32 life; + /* 0x34 */ s16 rotSpeed; + /* 0x36 */ s16 yaw; + /* 0x38 */ u8 updateMode; +} EffectSsKiraKiraInitParams; // size = 0x3C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.c b/soh/src/overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.c new file mode 100644 index 000000000..746ffbd93 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.c @@ -0,0 +1,143 @@ +/* + * File: z_eff_ss_lightning.c + * Overlay: ovl_Effect_Ss_Lightning + * Description: Lightning + */ + +#include "z_eff_ss_lightning.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rNumBolts regs[8] +#define rScale regs[9] +#define rYaw regs[10] +#define rLifespan regs[11] + +u32 EffectSsLightning_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsLightning_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsLightning_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Lightning_InitVars = { + EFFECT_SS_LIGHTNING, + EffectSsLightning_Init, +}; + +u32 EffectSsLightning_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsLightningInitParams* initParams = (EffectSsLightningInitParams*)initParamsx; + + this->pos = initParams->pos; + this->gfx = SEGMENTED_TO_VIRTUAL(gEffLightningDL); + this->life = initParams->life; + this->draw = EffectSsLightning_Draw; + this->update = EffectSsLightning_Update; + this->rPrimColorR = initParams->primColor.r; + this->rPrimColorG = initParams->primColor.g; + this->rPrimColorB = initParams->primColor.b; + this->rPrimColorA = initParams->primColor.a; + this->rEnvColorR = initParams->envColor.r; + this->rEnvColorG = initParams->envColor.g; + this->rEnvColorB = initParams->envColor.b; + this->rEnvColorA = initParams->envColor.a; + this->rNumBolts = initParams->numBolts; + this->rScale = initParams->scale; + this->rYaw = initParams->yaw; + this->rLifespan = initParams->life; + + return 1; +} + +void EffectSsLightning_NewLightning(GlobalContext* globalCtx, Vec3f* pos, s16 yaw, EffectSs* this) { + EffectSs newLightning; + + EffectSs_Delete(&newLightning); + newLightning = *this; + newLightning.pos = *pos; + newLightning.rNumBolts--; + newLightning.rYaw = yaw; + newLightning.life = newLightning.rLifespan; + + EffectSs_Insert(globalCtx, &newLightning); +} + +void EffectSsLightning_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* lightningTextures[] = { + gEffLightning1Tex, gEffLightning2Tex, gEffLightning3Tex, gEffLightning4Tex, + gEffLightning5Tex, gEffLightning6Tex, gEffLightning7Tex, gEffLightning8Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + MtxF mfResult; + MtxF mfTrans; + MtxF mfScale; + MtxF mfRotate; + MtxF mfTrans11DA0; + MtxF mfTrans11DA0Rotate; + Mtx* mtx; + f32 yScale; + s16 texIdx; + f32 xzScale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_lightning.c", 233); + + yScale = this->rScale * 0.01f; + texIdx = this->rLifespan - this->life; + + if (texIdx > 7) { + texIdx = 7; + } + + SkinMatrix_SetTranslate(&mfTrans, this->pos.x, this->pos.y, this->pos.z); + xzScale = yScale * 0.6f; + SkinMatrix_SetScale(&mfScale, xzScale, yScale, xzScale); + SkinMatrix_SetRotateZYX(&mfRotate, this->vec.x, this->vec.y, this->rYaw); + SkinMatrix_MtxFMtxFMult(&mfTrans, &globalCtx->billboardMtxF, &mfTrans11DA0); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0, &mfRotate, &mfTrans11DA0Rotate); + SkinMatrix_MtxFMtxFMult(&mfTrans11DA0Rotate, &mfScale, &mfResult); + + gSPMatrix(POLY_XLU_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + mtx = SkinMatrix_MtxFToNewMtx(gfxCtx, &mfResult); + + if (mtx != NULL) { + gSPMatrix(POLY_XLU_DISP++, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80094C50(gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(lightningTextures[texIdx])); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, + this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gSPDisplayList(POLY_XLU_DISP++, this->gfx); + } + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_lightning.c", 281); +} + +void EffectSsLightning_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 pad; + Vec3f pos; + s16 yaw; + f32 scale; + + if ((this->rNumBolts != 0) && ((this->life + 1) == this->rLifespan)) { + + yaw = this->rYaw + (((Rand_ZeroOne() < 0.5f) ? -1 : 1) * ((s16)((Rand_ZeroOne() * 3640.0f)) + 0xE38)); + + scale = (this->rScale * 0.01f) * 80.0f; + pos.y = this->pos.y + (Math_SinS(this->rYaw - 0x4000) * scale); + + scale = Math_CosS(this->rYaw - 0x4000) * scale; + pos.x = this->pos.x - (Math_CosS(Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx))) * scale); + pos.z = this->pos.z + (Math_SinS(Camera_GetInputDirYaw(GET_ACTIVE_CAM(globalCtx))) * scale); + + EffectSsLightning_NewLightning(globalCtx, &pos, yaw, this); + + if (Rand_ZeroOne() < 0.1f) { + EffectSsLightning_NewLightning(globalCtx, &pos, (this->rYaw * 2) - yaw, this); + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.h b/soh/src/overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.h new file mode 100644 index 000000000..758f2cce6 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Lightning/z_eff_ss_lightning.h @@ -0,0 +1,17 @@ +#ifndef Z_EFF_SS_LIGHTNING_H +#define Z_EFF_SS_LIGHTNING_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Color_RGBA8 primColor; + /* 0x10 */ Color_RGBA8 envColor; + /* 0x14 */ s16 scale; + /* 0x16 */ s16 yaw; + /* 0x18 */ s16 life; + /* 0x1A */ s16 numBolts; +} EffectSsLightningInitParams; // size = 0x1C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.c b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.c new file mode 100644 index 000000000..b8bd67b9c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.c @@ -0,0 +1,120 @@ +/* + * File: z_eff_ss_sibuki.c + * Overlay: ovl_Effect_Ss_Sibuki + * Description: Bubbles (only used by gohma and gohmalings in the original game) + */ + +#include "z_eff_ss_sibuki.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rMoveDelay regs[8] +#define rDirection regs[9] +#define rScale regs[10] + +u32 EffectSsSibuki_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsSibuki_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsSibuki_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Sibuki_InitVars = { + EFFECT_SS_SIBUKI, + EffectSsSibuki_Init, +}; + +u32 EffectSsSibuki_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsSibukiInitParams* initParams = (EffectSsSibukiInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + + if (KREG(2) != 0) { + this->gfx = SEGMENTED_TO_VIRTUAL(gEffBubble2Tex); + } else { + this->gfx = SEGMENTED_TO_VIRTUAL(gEffBubble1Tex); + } + + this->life = ((s16)((Rand_ZeroOne() * (500.0f + KREG(64))) * 0.01f)) + KREG(65) + 10; + this->rMoveDelay = initParams->moveDelay + 1; + this->draw = EffectSsSibuki_Draw; + this->update = EffectSsSibuki_Update; + this->rDirection = initParams->direction; + this->rScale = initParams->scale; + this->rPrimColorR = 100; + this->rPrimColorG = 100; + this->rPrimColorB = 100; + this->rPrimColorA = 100; + this->rEnvColorR = 255; + this->rEnvColorG = 255; + this->rEnvColorB = 255; + this->rEnvColorA = 255; + + return 1; +} + +void EffectSsSibuki_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 scale = this->rScale / 100.0f; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_sibuki.c", 163); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_sibuki.c", 176), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, this->rPrimColorA); + gDPSetEnvColor(POLY_OPA_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gSPSegment(POLY_OPA_DISP++, 0x08, this->gfx); + gSPDisplayList(POLY_OPA_DISP++, SEGMENTED_TO_VIRTUAL(gEffBubbleDL)); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_sibuki.c", 198); +} + +void EffectSsSibuki_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 pad[3]; + f32 xzVelScale; + s16 yaw; + Player* player = GET_PLAYER(globalCtx); + + if (this->pos.y <= player->actor.floorHeight) { + this->life = 0; + } + + if (this->rMoveDelay != 0) { + this->rMoveDelay--; + + if (this->rMoveDelay == 0) { + yaw = Camera_GetInputDirYaw(Gameplay_GetCamera(globalCtx, 0)); + xzVelScale = ((200.0f + KREG(20)) * 0.01f) + ((0.1f * Rand_ZeroOne()) * (KREG(23) + 20.0f)); + + if (this->rDirection != 0) { + xzVelScale *= -1.0f; + } + + this->velocity.x = Math_CosS(yaw) * xzVelScale; + this->velocity.z = -Math_SinS(yaw) * xzVelScale; + + this->velocity.y = ((700.0f + KREG(21)) * 0.01f) + ((0.1f * Rand_ZeroOne()) * (KREG(24) + 20.0f)); + this->accel.y = ((-100.0f + KREG(22)) * 0.01f) + ((0.1f * Rand_ZeroOne()) * KREG(25)); + + if (KREG(3) != 0) { + this->velocity.x *= (KREG(3) * 0.01f); + this->velocity.y *= (KREG(3) * 0.01f); + this->velocity.z *= (KREG(3) * 0.01f); + this->accel.y *= (KREG(4) * 0.01f); + } + } + } else { + if (this->rScale != 0) { + this->rScale = (this->rScale - KREG(26)) - 3; + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.h b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.h new file mode 100644 index 000000000..321fd334e --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki/z_eff_ss_sibuki.h @@ -0,0 +1,16 @@ +#ifndef Z_EFF_SS_SIBUKI_H +#define Z_EFF_SS_SIBUKI_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 moveDelay; + /* 0x26 */ s16 direction; + /* 0x28 */ s16 scale; +} EffectSsSibukiInitParams; // size = 0x2C + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.c b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.c new file mode 100644 index 000000000..78bcfd1c8 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.c @@ -0,0 +1,81 @@ +/* + * File: z_eff_ss_sibuki2.c + * Overlay: ovl_Effect_Ss_Sibuki2 + * Description: Unfinished and unused bubble effect + */ + +#include "z_eff_ss_sibuki2.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rPrimColorR regs[0] +#define rPrimColorG regs[1] +#define rPrimColorB regs[2] +#define rPrimColorA regs[3] +#define rEnvColorR regs[4] +#define rEnvColorG regs[5] +#define rEnvColorB regs[6] +#define rEnvColorA regs[7] +#define rTexIdx regs[8] +#define rScale regs[9] + +u32 EffectSsSibuki2_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsSibuki2_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsSibuki2_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Sibuki2_InitVars = { + EFFECT_SS_SIBUKI2, + EffectSsSibuki2_Init, +}; + +u32 EffectSsSibuki2_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsSibuki2InitParams* initParams = (EffectSsSibuki2InitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->life = 10; + this->draw = EffectSsSibuki2_Draw; + this->update = EffectSsSibuki2_Update; + this->rScale = initParams->scale; + this->rPrimColorR = 255; + this->rPrimColorG = 255; + this->rPrimColorB = 255; + this->rPrimColorA = 255; + this->rEnvColorR = 100; + this->rEnvColorG = 100; + this->rEnvColorB = 100; + this->rEnvColorA = 255; + this->rTexIdx = 0; + + return 1; +} + +void EffectSsSibuki2_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + static void* bubbleTextures[] = { + gEffUnusedBubbles1Tex, gEffUnusedBubbles1Tex, gEffUnusedBubbles2Tex, + gEffUnusedBubbles3Tex, gEffUnusedBubbles4Tex, gEffUnusedBubbles5Tex, + gEffUnusedBubbles6Tex, gEffUnusedBubbles7Tex, gEffUnusedBubbles8Tex, + }; + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + f32 scale = this->rScale / 100.0f; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_sibuki2.c", 158); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_sibuki2.c", 171), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(gfxCtx); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, this->rPrimColorR, this->rPrimColorG, this->rPrimColorB, this->rPrimColorA); + gDPSetEnvColor(POLY_XLU_DISP++, this->rEnvColorR, this->rEnvColorG, this->rEnvColorB, this->rEnvColorA); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(bubbleTextures[this->rTexIdx])); + gSPDisplayList(POLY_XLU_DISP++, SEGMENTED_TO_VIRTUAL(gEffUnusedBubblesDL)); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_sibuki2.c", 198); +} + +void EffectSsSibuki2_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if (this->rTexIdx < 8) { + this->rTexIdx++; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.h b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.h new file mode 100644 index 000000000..c0201fba0 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Sibuki2/z_eff_ss_sibuki2.h @@ -0,0 +1,14 @@ +#ifndef Z_EFF_SS_SIBUKI2_H +#define Z_EFF_SS_SIBUKI2_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 scale; +} EffectSsSibuki2InitParams; // size = 0x28 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.c b/soh/src/overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.c new file mode 100644 index 000000000..babae2517 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.c @@ -0,0 +1,55 @@ +/* + * File: z_eff_ss_solder_srch_ball.c + * Overlay: ovl_Effect_Ss_Solder_Srch_Ball + * Description: Vision sphere for courtyard guards + */ + +#include "z_eff_ss_solder_srch_ball.h" + +#define rUnused regs[1] + +u32 EffectSsSolderSrchBall_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsSolderSrchBall_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Solder_Srch_Ball_InitVars = { + EFFECT_SS_SOLDER_SRCH_BALL, + EffectSsSolderSrchBall_Init, +}; + +u32 EffectSsSolderSrchBall_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsSolderSrchBallInitParams* initParams = (EffectSsSolderSrchBallInitParams*)initParamsx; + + this->pos = initParams->pos; + this->velocity = initParams->velocity; + this->accel = initParams->accel; + this->update = EffectSsSolderSrchBall_Update; + this->life = 100; + this->rUnused = initParams->unused; + this->actor = initParams->linkDetected; // actor field was incorrectly used as a pointer to something else + return 1; +} + +void EffectSsSolderSrchBall_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + s32 pad; + f32 playerPosDiffX; + f32 playerPosDiffY; + f32 playerPosDiffZ; + s16* linkDetected; + Player* player = GET_PLAYER(globalCtx); + + linkDetected = this->actor; + + playerPosDiffX = player->actor.world.pos.x - this->pos.x; + playerPosDiffY = player->actor.world.pos.y - this->pos.y; + playerPosDiffZ = player->actor.world.pos.z - this->pos.z; + + if (!BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &this->pos, 30.0f)) { + if (sqrtf(SQ(playerPosDiffX) + SQ(playerPosDiffY) + SQ(playerPosDiffZ)) < 70.0f) { + *linkDetected = true; + } + } else { + if (this->life > 1) { + this->life = 1; + } + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.h b/soh/src/overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.h new file mode 100644 index 000000000..e29ca6e3f --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Solder_Srch_Ball/z_eff_ss_solder_srch_ball.h @@ -0,0 +1,15 @@ +#ifndef Z_EFF_SS_SOLDERSRCHBALL_H +#define Z_EFF_SS_SOLDERSRCHBALL_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ Vec3f velocity; + /* 0x18 */ Vec3f accel; + /* 0x24 */ s16 unused; + /* 0x28 */ s16* linkDetected; +} EffectSsSolderSrchBallInitParams; + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.c b/soh/src/overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.c new file mode 100644 index 000000000..7683469fe --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.c @@ -0,0 +1,78 @@ +/* + * File: z_eff_ss_stick.c + * Overlay: ovl_Effect_Ss_Stick + * Description: Broken stick as child, broken sword as adult + */ + +#include "z_eff_ss_stick.h" +#include "objects/object_link_boy/object_link_boy.h" +#include "objects/object_link_child/object_link_child.h" + +#define rObjBankIdx regs[0] +#define rYaw regs[1] + +u32 EffectSsStick_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsStick_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsStick_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Stick_InitVars = { + EFFECT_SS_STICK, + EffectSsStick_Init, +}; + +typedef struct { + /* 0x00 */ s16 objectID; + /* 0x04 */ Gfx* displayList; +} StickDrawInfo; + +u32 EffectSsStick_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + StickDrawInfo drawInfo[] = { + { OBJECT_LINK_BOY, gLinkAdultBrokenGiantsKnifeBladeDL }, // adult, broken sword + { OBJECT_LINK_CHILD, gLinkChildLinkDekuStickDL }, // child, broken stick + }; + StickDrawInfo* ageInfoEntry = gSaveContext.linkAge + drawInfo; + EffectSsStickInitParams* initParams = (EffectSsStickInitParams*)initParamsx; + + this->rObjBankIdx = Object_GetIndex(&globalCtx->objectCtx, ageInfoEntry->objectID); + this->gfx = ageInfoEntry->displayList; + this->vec = this->pos = initParams->pos; + this->rYaw = initParams->yaw; + this->velocity.x = Math_SinS(initParams->yaw) * 6.0f; + this->velocity.z = Math_CosS(initParams->yaw) * 6.0f; + this->life = 20; + this->draw = EffectSsStick_Draw; + this->update = EffectSsStick_Update; + this->velocity.y = 26.0f; + this->accel.y = -4.0f; + + return 1; +} + +void EffectSsStick_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + s32 pad; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_stick.c", 153); + + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + + if (!LINK_IS_ADULT) { + Matrix_Scale(0.01f, 0.0025f, 0.01f, MTXMODE_APPLY); + Matrix_RotateZYX(0, this->rYaw, 0, MTXMODE_APPLY); + } else { + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + Matrix_RotateZYX(0, this->rYaw, globalCtx->state.frames * 10000, MTXMODE_APPLY); + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_stick.c", 176), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80093D18(gfxCtx); + gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->rObjBankIdx].segment); + gSPSegment(POLY_OPA_DISP++, 0x0C, gCullBackDList); + gSPDisplayList(POLY_OPA_DISP++, this->gfx); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_stick.c", 188); +} + +void EffectSsStick_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.h b/soh/src/overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.h new file mode 100644 index 000000000..a3df5491c --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Stick/z_eff_ss_stick.h @@ -0,0 +1,12 @@ +#ifndef Z_EFF_SS_STICK_H +#define Z_EFF_SS_STICK_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x0C */ s16 yaw; +} EffectSsStickInitParams; // size = 0x10 + +#endif diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.c b/soh/src/overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.c new file mode 100644 index 000000000..9f666f105 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.c @@ -0,0 +1,81 @@ +/* + * File: z_eff_ss_stone1.c + * Overlay: ovl_Effect_Ss_Stone1 + * Description: + */ + +#include "z_eff_ss_stone1.h" +#include "objects/gameplay_keep/gameplay_keep.h" + +#define rReg0 regs[0] + +u32 EffectSsStone1_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx); +void EffectSsStone1_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this); +void EffectSsStone1_Update(GlobalContext* globalCtx, u32 index, EffectSs* this); + +EffectSsInit Effect_Ss_Stone1_InitVars = { + EFFECT_SS_STONE1, + EffectSsStone1_Init, +}; + +typedef struct { + /* 0x00 */ void* texture; + /* 0x04 */ Color_RGBA8 primColor; + /* 0x08 */ Color_RGBA8 envColor; +} EffStoneDrawInfo; + +static EffStoneDrawInfo sDrawInfo[] = { + { gUnknownEffStone8Tex, { 200, 0, 0, 255 }, { 0, 0, 0, 255 } }, + { gUnknownEffStone7Tex, { 255, 100, 0, 255 }, { 100, 0, 0, 255 } }, + { gUnknownEffStone6Tex, { 255, 200, 0, 255 }, { 200, 0, 0, 255 } }, + { gUnknownEffStone5Tex, { 255, 255, 0, 255 }, { 255, 0, 0, 255 } }, + { gUnknownEffStone4Tex, { 255, 255, 150, 255 }, { 255, 150, 0, 255 } }, + { gUnknownEffStone3Tex, { 255, 255, 255, 255 }, { 255, 255, 0, 255 } }, + { gUnknownEffStone2Tex, { 255, 255, 255, 255 }, { 0, 255, 0, 255 } }, + { gUnknownEffStone1Tex, { 255, 255, 255, 255 }, { 0, 255, 255, 255 } }, +}; + +u32 EffectSsStone1_Init(GlobalContext* globalCtx, u32 index, EffectSs* this, void* initParamsx) { + EffectSsStone1InitParams* initParams = (EffectSsStone1InitParams*)initParamsx; + Vec3f pos = initParams->pos; + + this->pos = pos; + this->vec = pos; + this->life = 8; + this->rReg0 = initParams->unk_C; + this->draw = EffectSsStone1_Draw; + this->update = EffectSsStone1_Update; + + return 1; +} + +void EffectSsStone1_Draw(GlobalContext* globalCtx, u32 index, EffectSs* this) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + EffStoneDrawInfo* drawParams = &sDrawInfo[this->life]; + Vec3f mfVec; + f32 mfW; + f32 scale; + + OPEN_DISPS(gfxCtx, "../z_eff_ss_stone1.c", 154); + + SkinMatrix_Vec3fMtxFMultXYZW(&globalCtx->viewProjectionMtxF, &this->pos, &mfVec, &mfW); + scale = (mfW < 1500.0f) ? 3.0f : (mfW / 1500.0f) * 3.0f; + Matrix_Translate(this->pos.x, this->pos.y, this->pos.z, MTXMODE_NEW); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_eff_ss_stone1.c", 168), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + func_80094C50(gfxCtx); + gSPSegment(POLY_XLU_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(drawParams->texture)); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, drawParams->primColor.r, drawParams->primColor.g, drawParams->primColor.b, + 255); + gDPSetEnvColor(POLY_XLU_DISP++, drawParams->envColor.r, drawParams->envColor.g, drawParams->envColor.b, 255); + gSPDisplayList(POLY_XLU_DISP++, gUnknownEffStoneDL); + + CLOSE_DISPS(gfxCtx, "../z_eff_ss_stone1.c", 183); +} + +void EffectSsStone1_Update(GlobalContext* globalCtx, u32 index, EffectSs* this) { + if ((this->life == 6) && (this->rReg0 != 0)) { + iREG(50) = 0; + } +} diff --git a/soh/src/overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.h b/soh/src/overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.h new file mode 100644 index 000000000..680345014 --- /dev/null +++ b/soh/src/overlays/effects/ovl_Effect_Ss_Stone1/z_eff_ss_stone1.h @@ -0,0 +1,12 @@ +#ifndef Z_EFF_SS_STONE1_H +#define Z_EFF_SS_STONE1_H + +#include "ultra64.h" +#include "global.h" + +typedef struct { + /* 0x00 */ Vec3f pos; + /* 0x00 */ s32 unk_C; +} EffectSsStone1InitParams; // size = 0x + +#endif diff --git a/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h b/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h new file mode 100644 index 000000000..3590d3048 --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_file_choose/file_choose.h @@ -0,0 +1,219 @@ +#ifndef _FILE_CHOOSE_H_ +#define _FILE_CHOOSE_H_ + +#include "ultra64.h" +#include "global.h" +#include "vt.h" + + +#define GET_NEWF(sramCtx, slotNum, index) (sramCtx->readBuff[gSramSlotOffsets[slotNum] + OFFSETOF(SaveContext, newf[index])]) + +#define SLOT_OCCUPIED(sramCtx, slotNum) \ + ((GET_NEWF(sramCtx, slotNum, 0) == 'Z') || \ + (GET_NEWF(sramCtx, slotNum, 1) == 'E') || \ + (GET_NEWF(sramCtx, slotNum, 2) == 'L') || \ + (GET_NEWF(sramCtx, slotNum, 3) == 'D') || \ + (GET_NEWF(sramCtx, slotNum, 4) == 'A') || \ + (GET_NEWF(sramCtx, slotNum, 5) == 'Z')) + +// Init mode: Initial setup as the file select is starting up, fades and slides in various menu elements +// Config mode: Handles the bulk of the file select, various configuration tasks like picking a file, copy/erase, and the options menu +// Select mode: Displays the selected file with various details about it, and allows the player to confirm and open it +typedef enum { + /* 0 */ FS_MENU_MODE_INIT, + /* 1 */ FS_MENU_MODE_CONFIG, + /* 2 */ FS_MENU_MODE_SELECT +} MenuMode; + +typedef enum { + /* 00 */ CM_FADE_IN_START, + /* 01 */ CM_FADE_IN_END, + /* 02 */ CM_MAIN_MENU, + /* 03 */ CM_SETUP_COPY_SOURCE, + /* 04 */ CM_SELECT_COPY_SOURCE, + /* 05 */ CM_SETUP_COPY_DEST_1, + /* 06 */ CM_SETUP_COPY_DEST_2, + /* 07 */ CM_SELECT_COPY_DEST, + /* 08 */ CM_EXIT_TO_COPY_SOURCE_1, + /* 09 */ CM_EXIT_TO_COPY_SOURCE_2, + /* 10 */ CM_SETUP_COPY_CONFIRM_1, + /* 11 */ CM_SETUP_COPY_CONFIRM_2, + /* 12 */ CM_COPY_CONFIRM, + /* 13 */ CM_RETURN_TO_COPY_DEST, + /* 14 */ CM_COPY_ANIM_1, + /* 15 */ CM_COPY_ANIM_2, + /* 16 */ CM_COPY_ANIM_3, + /* 17 */ CM_COPY_ANIM_4, + /* 18 */ CM_COPY_ANIM_5, + /* 19 */ CM_COPY_RETURN_MAIN, + /* 20 */ CM_SETUP_ERASE_SELECT, + /* 21 */ CM_ERASE_SELECT, + /* 22 */ CM_SETUP_ERASE_CONFIRM_1, + /* 23 */ CM_SETUP_ERASE_CONFIRM_2, + /* 24 */ CM_ERASE_CONFIRM, + /* 25 */ CM_EXIT_TO_ERASE_SELECT_1, + /* 26 */ CM_EXIT_TO_ERASE_SELECT_2, + /* 27 */ CM_ERASE_ANIM_1, + /* 28 */ CM_ERASE_ANIM_2, + /* 29 */ CM_ERASE_ANIM_3, + /* 30 */ CM_EXIT_ERASE_TO_MAIN, + /* 31 */ CM_UNUSED_31, + /* 32 */ CM_ROTATE_TO_NAME_ENTRY, + /* 33 */ CM_NAME_ENTRY, + /* 34 */ CM_START_NAME_ENTRY, + /* 35 */ CM_NAME_ENTRY_TO_MAIN, + /* 36 */ CM_MAIN_TO_OPTIONS, + /* 37 */ CM_OPTIONS_MENU, + /* 38 */ CM_START_OPTIONS, + /* 39 */ CM_OPTIONS_TO_MAIN, + /* 40 */ CM_UNUSED_DELAY +} ConfigMode; + +typedef enum { + /* 0 */ SM_FADE_MAIN_TO_SELECT, + /* 1 */ SM_MOVE_FILE_TO_TOP, + /* 2 */ SM_FADE_IN_FILE_INFO, + /* 3 */ SM_CONFIRM_FILE, + /* 4 */ SM_FADE_OUT_FILE_INFO, + /* 5 */ SM_MOVE_FILE_TO_SLOT, + /* 6 */ SM_FADE_OUT, + /* 7 */ SM_LOAD_GAME +} SelectMode; + +typedef enum { + /* 0 */ FS_TITLE_SELECT_FILE, // "Please select a file." + /* 1 */ FS_TITLE_OPEN_FILE, // "Open this file?" + /* 2 */ FS_TITLE_COPY_FROM, // "Copy which file?" + /* 3 */ FS_TITLE_COPY_TO, // "Copy to which file?" + /* 4 */ FS_TITLE_COPY_CONFIRM, // "Are you sure?" + /* 5 */ FS_TITLE_COPY_COMPLETE, // "File copied." + /* 6 */ FS_TITLE_ERASE_FILE, // "Erase which file?" + /* 7 */ FS_TITLE_ERASE_CONFIRM, // "Are you sure?" + /* 8 */ FS_TITLE_ERASE_COMPLETE // "File erased." +} TitleLabel; + +typedef enum { + /* -1 */ FS_WARNING_NONE = -1, + /* 0 */ FS_WARNING_NO_FILE_COPY, // "No file to copy." + /* 1 */ FS_WARNING_NO_FILE_ERASE, // "No file to erase." + /* 2 */ FS_WARNING_NO_EMPTY_FILES, // "There is no empty file." + /* 3 */ FS_WARNING_FILE_EMPTY, // "This is an empty file." + /* 4 */ FS_WARNING_FILE_IN_USE // "This file is in use." +} WarningLabel; + +typedef enum { + /* 0 */ FS_BTN_MAIN_FILE_1, + /* 1 */ FS_BTN_MAIN_FILE_2, + /* 2 */ FS_BTN_MAIN_FILE_3, + /* 3 */ FS_BTN_MAIN_COPY, + /* 4 */ FS_BTN_MAIN_ERASE, + /* 5 */ FS_BTN_MAIN_OPTIONS +} MainMenuButtonIndex; + +typedef enum { + /* 0 */ FS_BTN_COPY_FILE_1, + /* 1 */ FS_BTN_COPY_FILE_2, + /* 2 */ FS_BTN_COPY_FILE_3, + /* 3 */ FS_BTN_COPY_QUIT +} CopyMenuButtonIndex; + +typedef enum { + /* 0 */ FS_BTN_ERASE_FILE_1, + /* 1 */ FS_BTN_ERASE_FILE_2, + /* 2 */ FS_BTN_ERASE_FILE_3, + /* 3 */ FS_BTN_ERASE_QUIT +} EraseMenuButtonIndex; + +typedef enum { + /* 0 */ FS_BTN_SELECT_FILE_1, + /* 1 */ FS_BTN_SELECT_FILE_2, + /* 2 */ FS_BTN_SELECT_FILE_3, + /* 3 */ FS_BTN_SELECT_YES, + /* 4 */ FS_BTN_SELECT_QUIT +} SelectMenuButtonIndex; + +typedef enum { + /* 0 */ FS_BTN_CONFIRM_YES, + /* 1 */ FS_BTN_CONFIRM_QUIT +} ConfirmButtonIndex; + +typedef enum { + /* 0 */ FS_BTN_ACTION_COPY, + /* 1 */ FS_BTN_ACTION_ERASE +} ActionButtonIndex; + +typedef enum { + /* 0 */ FS_SETTING_AUDIO, + /* 1 */ FS_SETTING_TARGET +} SettingIndex; + +typedef enum { + /* 0 */ FS_AUDIO_STEREO, + /* 1 */ FS_AUDIO_MONO, + /* 2 */ FS_AUDIO_HEADSET, + /* 3 */ FS_AUDIO_SURROUND +} AudioOption; + +typedef enum { + /* 0 */ FS_CHAR_PAGE_HIRA, + /* 1 */ FS_CHAR_PAGE_KATA, + /* 2 */ FS_CHAR_PAGE_ENG +} CharPage; + +typedef enum { + /* 00 */ FS_KBD_BTN_HIRA, + /* 01 */ FS_KBD_BTN_KATA, + /* 02 */ FS_KBD_BTN_ENG, + /* 03 */ FS_KBD_BTN_BACKSPACE, + /* 04 */ FS_KBD_BTN_END, + /* 99 */ FS_KBD_BTN_NONE = 99 +} KeyboardButton; + +void FileChoose_SetupCopySource(GameState* thisx); +void FileChoose_SelectCopySource(GameState* thisx); +void FileChoose_SetupCopyDest1(GameState* thisx); +void FileChoose_SetupCopyDest2(GameState* thisx); +void FileChoose_SelectCopyDest(GameState* thisx); +void FileChoose_ExitToCopySource1(GameState* thisx); +void FileChoose_ExitToCopySource2(GameState* thisx); +void FileChoose_SetupCopyConfirm1(GameState* thisx); +void FileChoose_SetupCopyConfirm2(GameState* thisx); +void FileChoose_CopyConfirm(GameState* thisx); +void FileChoose_ReturnToCopyDest(GameState* thisx); +void FileChoose_CopyAnim1(GameState* thisx); +void FileChoose_CopyAnim2(GameState* thisx); +void FileChoose_CopyAnim3(GameState* thisx); +void FileChoose_CopyAnim4(GameState* thisx); +void FileChoose_CopyAnim5(GameState* thisx); + +void FileChoose_ExitCopyToMain(GameState* thisx); +void FileChoose_SetupEraseSelect(GameState* thisx); +void FileChoose_EraseSelect(GameState* thisx); +void FileChoose_SetupEraseConfirm1(GameState* thisx); +void FileChoose_SetupEraseConfirm2(GameState* thisx); +void FileChoose_EraseConfirm(GameState* thisx); +void FileChoose_ExitToEraseSelect1(GameState* thisx); +void FileChoose_ExitToEraseSelect2(GameState* thisx); +void FileChoose_EraseAnim1(GameState* thisx); +void FileChoose_EraseAnim2(GameState* thisx); +void FileChoose_EraseAnim3(GameState* thisx); +void FileChoose_ExitEraseToMain(GameState* thisx); + +void FileChoose_UpdateKeyboardCursor(GameState* thisx); +void FileChoose_StartNameEntry(GameState* thisx); +void FileChoose_UpdateOptionsMenu(GameState* thisx); +void FileChoose_StartOptions(GameState* thisx); + +void FileChoose_InitModeDraw(GameState* thisx); +void FileChoose_ConfigModeDraw(GameState* thisx); +void FileChoose_SelectModeDraw(GameState* thisx); + +void FileChoose_PulsateCursor(GameState* thisx); +void FileChoose_DrawOptions(GameState* thisx); + +void FileChoose_DrawNameEntry(GameState* thisx); +void FileChoose_DrawCharacter(GraphicsContext* gfxCtx, void* texture, s16 vtx); + +extern s16 D_808123F0[]; + +#endif diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c new file mode 100644 index 000000000..ca5154567 --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -0,0 +1,1897 @@ +#include "file_choose.h" + +#include + +#include "textures/title_static/title_static.h" +#include "textures/parameter_static/parameter_static.h" + +static s16 sUnused = 106; + +static s16 sScreenFillAlpha = 255; + +static Gfx sScreenFillSetupDL[] = { + gsDPPipeSync(), + gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | + G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH), + gsDPSetOtherMode(G_AD_DISABLE | G_CD_MAGICSQ | G_CK_NONE | G_TC_FILT | G_TF_BILERP | G_TT_NONE | G_TL_TILE | + G_TD_CLAMP | G_TP_NONE | G_CYC_1CYCLE | G_PM_1PRIMITIVE, + G_AC_NONE | G_ZS_PIXEL | G_RM_CLD_SURF | G_RM_CLD_SURF2), + gsDPSetCombineMode(G_CC_PRIMITIVE, G_CC_PRIMITIVE), + gsSPEndDisplayList(), +}; + +static s16 sFileInfoBoxPartWidths[] = { 36, 36, 36, 36, 24 }; + +static s16 sWindowContentColors[2][3] = { + { 100, 150, 255 }, // blue + { 100, 100, 100 }, // gray +}; + +void FileChoose_SetView(FileChooseContext* this, f32 eyeX, f32 eyeY, f32 eyeZ) { + Vec3f eye; + Vec3f lookAt; + Vec3f up; + + lookAt.x = lookAt.y = lookAt.z = 0.0f; + up.x = up.z = 0.0f; + + eye.x = eyeX; + eye.y = eyeY; + eye.z = eyeZ; + + up.y = 1.0f; + + func_800AA358(&this->view, &eye, &lookAt, &up); + func_800AAA50(&this->view, 0x7F); +} + +Gfx* FileChoose_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, s16 point) +{ + if (ResourceMgr_OTRSigCheck(texture)) + texture = ResourceMgr_LoadTexByName(texture); + + gDPLoadTextureBlock(gfx++, texture, G_IM_FMT_IA, G_IM_SIZ_8b, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(gfx++, point, point + 2, point + 3, point + 1, 0); + + return gfx; +} + +void FileChoose_InitModeUpdate(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + if (this->menuMode == FS_MENU_MODE_INIT) { + this->menuMode = FS_MENU_MODE_CONFIG; + this->configMode = CM_FADE_IN_START; + this->nextTitleLabel = FS_TITLE_OPEN_FILE; + osSyncPrintf("Sram Start─Load 》》》》》 "); + Sram_VerifyAndLoadAllSaves(this, &this->sramCtx); + osSyncPrintf("終了!!!\n"); + } +} + +void FileChoose_InitModeDraw(GameState* thisx) { +} + +/** + * Fade in the menu window and title label. + * If a file is occupied fade in the name, name box, and connector. + * Fade in the copy erase and options button according to the window alpha. + */ +void FileChoose_FadeInMenuElements(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + + this->titleAlpha[0] += VREG(1); + this->windowAlpha += VREG(2); + + for (i = 0; i < 3; i++) { + this->fileButtonAlpha[i] = this->windowAlpha; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->windowAlpha; + this->connectorAlpha[i] += VREG(1); + if (this->connectorAlpha[i] >= 255) { + this->connectorAlpha[i] = 255; + } + } + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->optionButtonAlpha = this->windowAlpha; +} + +/** + * Converts a numerical value to ones-tens-hundreds digits + */ +void FileChoose_SplitNumber(u16 value, s16* hundreds, s16* tens, s16* ones) { + *hundreds = 0; + *tens = 0; + *ones = value; + + while (true) { + if ((*ones - 100) < 0) { + break; + } + (*hundreds)++; + *ones -= 100; + } + + while (true) { + if ((*ones - 10) < 0) { + break; + } + (*tens)++; + *ones -= 10; + } +} + +/** + * Reduce the alpha of the black screen fill to create a fade in effect. + * Additionally, slide the window from the right to the center of the screen. + * Update function for `CM_FADE_IN_START` + */ +void FileChoose_StartFadeIn(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + FileChoose_FadeInMenuElements(&this->state); + sScreenFillAlpha -= 40; + this->windowPosX -= 20; + + if (this->windowPosX <= -94) { + this->windowPosX = -94; + this->configMode = CM_FADE_IN_END; + sScreenFillAlpha = 0; + } +} + +/** + * Finish fading in the remaining menu elements. + * Fade in the controls text at the bottom of the screen. + * Update function for `CM_FADE_IN_END` + */ +void FileChoose_FinishFadeIn(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->controlsAlpha += VREG(1); + FileChoose_FadeInMenuElements(&this->state); + + if (this->titleAlpha[0] >= 255) { + this->titleAlpha[0] = 255; + this->controlsAlpha = 255; + this->windowAlpha = 200; + this->configMode = CM_MAIN_MENU; + } +} + +/** + * Update the cursor and wait for the player to select a button to change menus accordingly. + * If an empty file is selected, enter the name entry config mode. + * If an occupied file is selected, enter the `Select` menu mode. + * If copy, erase, or options is selected, set config mode accordingly. + * Lastly, set any warning labels if appropriate. + * Update function for `CM_MAIN_MENU` + */ +void FileChoose_UpdateMainMenu(GameState* thisx) { + static u8 emptyName[] = { 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E, 0x3E }; + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + Input* input = &this->state.input[0]; + + if (CHECK_BTN_ALL(input->press.button, BTN_START) || CHECK_BTN_ALL(input->press.button, BTN_A)) { + if (this->buttonIndex <= FS_BTN_MAIN_FILE_3) { + osSyncPrintf("REGCK_ALL[%x]=%x,%x,%x,%x,%x,%x\n", this->buttonIndex, + GET_NEWF(sramCtx, this->buttonIndex, 0), GET_NEWF(sramCtx, this->buttonIndex, 1), + GET_NEWF(sramCtx, this->buttonIndex, 2), GET_NEWF(sramCtx, this->buttonIndex, 3), + GET_NEWF(sramCtx, this->buttonIndex, 4), GET_NEWF(sramCtx, this->buttonIndex, 5)); + + if (!SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->configMode = CM_ROTATE_TO_NAME_ENTRY; + this->kbdButton = FS_KBD_BTN_NONE; + this->charPage = FS_CHAR_PAGE_ENG; + this->kbdX = 0; + this->kbdY = 0; + this->charIndex = 0; + this->charBgAlpha = 0; + this->newFileNameCharCount = 0; + this->nameEntryBoxPosX = 120; + this->nameEntryBoxAlpha = 0; + memcpy(&this->fileNames[this->buttonIndex][0], &emptyName, 8); + } else if (this->n64ddFlags[this->buttonIndex] == this->n64ddFlag) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->actionTimer = 8; + this->selectMode = SM_FADE_MAIN_TO_SELECT; + this->selectedFileIndex = this->buttonIndex; + this->menuMode = FS_MENU_MODE_SELECT; + this->nextTitleLabel = FS_TITLE_OPEN_FILE; + } else if (!this->n64ddFlags[this->buttonIndex]) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + if (this->warningLabel == FS_WARNING_NONE) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->prevConfigMode = this->configMode; + + if (this->buttonIndex == FS_BTN_MAIN_COPY) { + this->configMode = CM_SETUP_COPY_SOURCE; + this->nextTitleLabel = FS_TITLE_COPY_FROM; + } else if (this->buttonIndex == FS_BTN_MAIN_ERASE) { + this->configMode = CM_SETUP_ERASE_SELECT; + this->nextTitleLabel = FS_TITLE_ERASE_FILE; + } else { + this->configMode = CM_MAIN_TO_OPTIONS; + this->kbdButton = 0; + this->kbdX = 0; + this->kbdY = 0; + this->charBgAlpha = 0; + this->newFileNameCharCount = 0; + this->nameEntryBoxPosX = 120; + } + + this->actionTimer = 8; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + } else { + if (ABS(this->stickRelY) > 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + if (this->stickRelY > 30) { + this->buttonIndex--; + if (this->buttonIndex < FS_BTN_MAIN_FILE_1) { + this->buttonIndex = FS_BTN_MAIN_OPTIONS; + } + } else { + this->buttonIndex++; + if (this->buttonIndex > FS_BTN_MAIN_OPTIONS) { + this->buttonIndex = FS_BTN_MAIN_FILE_1; + } + } + } + + if (this->buttonIndex == FS_BTN_MAIN_COPY) { + if (!SLOT_OCCUPIED(sramCtx, 0) && !SLOT_OCCUPIED(sramCtx, 1) && !SLOT_OCCUPIED(sramCtx, 2)) { + this->warningButtonIndex = this->buttonIndex; + this->warningLabel = FS_WARNING_NO_FILE_COPY; + this->emptyFileTextAlpha = 255; + } else if (SLOT_OCCUPIED(sramCtx, 0) && SLOT_OCCUPIED(sramCtx, 1) && SLOT_OCCUPIED(sramCtx, 2)) { + this->warningButtonIndex = this->buttonIndex; + this->warningLabel = FS_WARNING_NO_EMPTY_FILES; + this->emptyFileTextAlpha = 255; + } else { + this->warningLabel = FS_WARNING_NONE; + } + } else if (this->buttonIndex == FS_BTN_MAIN_ERASE) { + if (!SLOT_OCCUPIED(sramCtx, 0) && !SLOT_OCCUPIED(sramCtx, 1) && !SLOT_OCCUPIED(sramCtx, 2)) { + this->warningButtonIndex = this->buttonIndex; + this->warningLabel = FS_WARNING_NO_FILE_ERASE; + this->emptyFileTextAlpha = 255; + } else { + this->warningLabel = FS_WARNING_NONE; + } + } else { + this->warningLabel = FS_WARNING_NONE; + } + } +} + +/** + * Update function for `CM_UNUSED_31` + */ +void FileChoose_UnusedCM31(GameState* thisx) { +} + +/** + * Delay the next config mode from running until `XREG(73)` reaches 254. + * Because the timer increments by 2, the delay is 127 frames (assuming the value was not changed by reg editor). + * Unused in the final game, was possibly used for debugging. + * Update function for `CM_UNUSED_DELAY` + */ +void FileChoose_UnusedCMDelay(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + XREG(73) += 2; + + if (XREG(73) == 254) { + this->configMode = this->nextConfigMode; + XREG(73) = 0; + } +} + +/** + * Rotate the window from the main menu to the name entry menu. + * Update function for `CM_ROTATE_TO_NAME_ENTRY` + */ +void FileChoose_RotateToNameEntry(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->windowRot += VREG(16); + + if (this->windowRot >= 314.0f) { + this->windowRot = 314.0f; + this->configMode = CM_START_NAME_ENTRY; + } +} + +/** + * Rotate the window from the main menu to the options menu. + * Update function for `CM_MAIN_TO_OPTIONS` + */ +void FileChoose_RotateToOptions(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->windowRot += VREG(16); + + if (this->windowRot >= 314.0f) { + this->windowRot = 314.0f; + this->configMode = CM_START_OPTIONS; + } +} + +/** + * Rotate the window from the options menu to the main menu. + * Update function for `CM_NAME_ENTRY_TO_MAIN` and `CM_OPTIONS_TO_MAIN` + */ +void FileChoose_RotateToMain(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->windowRot += VREG(16); + + if (this->windowRot >= 628.0f) { + this->windowRot = 0.0f; + this->configMode = CM_MAIN_MENU; + } +} + +static void (*gConfigModeUpdateFuncs[])(GameState*) = { + FileChoose_StartFadeIn, FileChoose_FinishFadeIn, + FileChoose_UpdateMainMenu, FileChoose_SetupCopySource, + FileChoose_SelectCopySource, FileChoose_SetupCopyDest1, + FileChoose_SetupCopyDest2, FileChoose_SelectCopyDest, + FileChoose_ExitToCopySource1, FileChoose_ExitToCopySource2, + FileChoose_SetupCopyConfirm1, FileChoose_SetupCopyConfirm2, + FileChoose_CopyConfirm, FileChoose_ReturnToCopyDest, + FileChoose_CopyAnim1, FileChoose_CopyAnim2, + FileChoose_CopyAnim3, FileChoose_CopyAnim4, + FileChoose_CopyAnim5, FileChoose_ExitCopyToMain, + FileChoose_SetupEraseSelect, FileChoose_EraseSelect, + FileChoose_SetupEraseConfirm1, FileChoose_SetupEraseConfirm2, + FileChoose_EraseConfirm, FileChoose_ExitToEraseSelect1, + FileChoose_ExitToEraseSelect2, FileChoose_EraseAnim1, + FileChoose_EraseAnim2, FileChoose_EraseAnim3, + FileChoose_ExitEraseToMain, FileChoose_UnusedCM31, + FileChoose_RotateToNameEntry, FileChoose_UpdateKeyboardCursor, + FileChoose_StartNameEntry, FileChoose_RotateToMain, + FileChoose_RotateToOptions, FileChoose_UpdateOptionsMenu, + FileChoose_StartOptions, FileChoose_RotateToMain, + FileChoose_UnusedCMDelay, +}; + +/** + * Updates the alpha of the cursor to make it pulsate. + * On the debug rom, this function also handles switching languages with controller 3. + */ +void FileChoose_PulsateCursor(GameState* thisx) { + static s16 cursorAlphaTargets[] = { 70, 200 }; + FileChooseContext* this = (FileChooseContext*)thisx; + s16 alphaStep; + SramContext* sramCtx = &this->sramCtx; + Input* debugInput = &this->state.input[2]; + + if (CHECK_BTN_ALL(debugInput->press.button, BTN_DLEFT)) { + sramCtx->readBuff[SRAM_HEADER_LANGUAGE] = gSaveContext.language = LANGUAGE_ENG; + *((u8*)0x80000002) = LANGUAGE_ENG; + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, 3, OS_WRITE); + osSyncPrintf("1:read_buff[]=%x, %x, %x, %x\n", sramCtx->readBuff[SRAM_HEADER_SOUND], + sramCtx->readBuff[SRAM_HEADER_ZTARGET], sramCtx->readBuff[SRAM_HEADER_LANGUAGE], + sramCtx->readBuff[SRAM_HEADER_MAGIC]); + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + osSyncPrintf("read_buff[]=%x, %x, %x, %x\n", sramCtx->readBuff[SRAM_HEADER_SOUND], + sramCtx->readBuff[SRAM_HEADER_ZTARGET], sramCtx->readBuff[SRAM_HEADER_LANGUAGE], + sramCtx->readBuff[SRAM_HEADER_MAGIC]); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DUP)) { + sramCtx->readBuff[SRAM_HEADER_LANGUAGE] = gSaveContext.language = LANGUAGE_GER; + *((u8*)0x80000002) = LANGUAGE_GER; + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, 3, OS_WRITE); + osSyncPrintf("1:read_buff[]=%x, %x, %x, %x\n", sramCtx->readBuff[SRAM_HEADER_SOUND], + sramCtx->readBuff[SRAM_HEADER_ZTARGET], sramCtx->readBuff[SRAM_HEADER_LANGUAGE], + sramCtx->readBuff[SRAM_HEADER_MAGIC]); + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + osSyncPrintf("read_buff[]=%x, %x, %x, %x\n", sramCtx->readBuff[SRAM_HEADER_SOUND], + sramCtx->readBuff[SRAM_HEADER_ZTARGET], sramCtx->readBuff[SRAM_HEADER_LANGUAGE], + sramCtx->readBuff[SRAM_HEADER_MAGIC]); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DRIGHT)) { + sramCtx->readBuff[SRAM_HEADER_LANGUAGE] = gSaveContext.language = LANGUAGE_FRA; + *((u8*)0x80000002) = LANGUAGE_FRA; + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, 3, OS_WRITE); + osSyncPrintf("1:read_buff[]=%x, %x, %x, %x\n", sramCtx->readBuff[SRAM_HEADER_SOUND], + sramCtx->readBuff[SRAM_HEADER_ZTARGET], sramCtx->readBuff[SRAM_HEADER_LANGUAGE], + sramCtx->readBuff[SRAM_HEADER_MAGIC]); + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + osSyncPrintf("read_buff[]=%x, %x, %x, %x\n", sramCtx->readBuff[SRAM_HEADER_SOUND], + sramCtx->readBuff[SRAM_HEADER_ZTARGET], sramCtx->readBuff[SRAM_HEADER_LANGUAGE], + sramCtx->readBuff[SRAM_HEADER_MAGIC]); + } + + alphaStep = ABS(this->highlightColor[3] - cursorAlphaTargets[this->highlightPulseDir]) / XREG(35); + + if (this->highlightColor[3] >= cursorAlphaTargets[this->highlightPulseDir]) { + this->highlightColor[3] -= alphaStep; + } else { + this->highlightColor[3] += alphaStep; + } + + XREG(35)--; + + if (XREG(35) == 0) { + this->highlightColor[3] = cursorAlphaTargets[this->highlightPulseDir]; + XREG(35) = XREG(36 + this->highlightPulseDir); + this->highlightPulseDir ^= 1; + } +} + +void FileChoose_ConfigModeUpdate(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + gConfigModeUpdateFuncs[this->configMode](&this->state); +} + +void FileChoose_SetWindowVtx(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 i; + s16 j; + s16 x; + s16 tmp; + s16 tmp2; + s16 tmp3; + + this->windowVtx = Graph_Alloc(this->state.gfxCtx, sizeof(Vtx) * 80); + tmp = this->windowPosX - 90; + + for (x = 0, i = 0; i < 4; i++) { + tmp += 0x40; + tmp2 = (i == 3) ? 0x30 : 0x40; + + for (j = 0, tmp3 = 0x50; j < 5; j++, x += 4, tmp3 -= 0x20) { + this->windowVtx[x].v.ob[0] = this->windowVtx[x + 2].v.ob[0] = tmp; + + this->windowVtx[x + 1].v.ob[0] = this->windowVtx[x + 3].v.ob[0] = tmp + tmp2; + + this->windowVtx[x].v.ob[1] = this->windowVtx[x + 1].v.ob[1] = tmp3; + + this->windowVtx[x + 2].v.ob[1] = this->windowVtx[x + 3].v.ob[1] = tmp3 - 0x20; + + this->windowVtx[x].v.ob[2] = this->windowVtx[x + 1].v.ob[2] = this->windowVtx[x + 2].v.ob[2] = + this->windowVtx[x + 3].v.ob[2] = 0; + + this->windowVtx[x].v.flag = this->windowVtx[x + 1].v.flag = this->windowVtx[x + 2].v.flag = + this->windowVtx[x + 3].v.flag = 0; + + this->windowVtx[x].v.tc[0] = this->windowVtx[x].v.tc[1] = this->windowVtx[x + 1].v.tc[1] = + this->windowVtx[x + 2].v.tc[0] = 0; + + this->windowVtx[x + 1].v.tc[0] = this->windowVtx[x + 3].v.tc[0] = tmp2 * 0x20; + + this->windowVtx[x + 2].v.tc[1] = this->windowVtx[x + 3].v.tc[1] = 0x400; + + this->windowVtx[x].v.cn[0] = this->windowVtx[x + 2].v.cn[0] = this->windowVtx[x].v.cn[1] = + this->windowVtx[x + 2].v.cn[1] = this->windowVtx[x].v.cn[2] = this->windowVtx[x + 2].v.cn[2] = + this->windowVtx[x + 1].v.cn[0] = this->windowVtx[x + 3].v.cn[0] = this->windowVtx[x + 1].v.cn[1] = + this->windowVtx[x + 3].v.cn[1] = this->windowVtx[x + 1].v.cn[2] = + this->windowVtx[x + 3].v.cn[2] = this->windowVtx[x].v.cn[3] = + this->windowVtx[x + 2].v.cn[3] = this->windowVtx[x + 1].v.cn[3] = + this->windowVtx[x + 3].v.cn[3] = 255; + } + } +} + +static s16 D_80812818[] = { 0x001A, 0x000A, 0x000A, 0x000A }; +static s16 D_80812820[] = { 0x0020, 0x000C, 0x000C, 0x000C }; +static s16 D_80812828[] = { 0x0010, 0x000C, 0x000C, 0x000C }; +static s16 D_80812830[] = { 0x0040, 0x0054, 0x0068, 0x0274, 0x0278, 0x027C }; +static s16 D_8081283C[] = { 0x0040, 0x0054, 0x0068, 0x0278 }; +static s16 D_80812844[] = { 0x0274, 0x0278 }; +static s16 D_80812848[] = { 0x0274, 0x0278 }; + +void FileChoose_SetWindowContentVtx(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 phi_t2; + s16 phi_t0; + s16 phi_t5; + s16 phi_a1; + s16 phi_ra; + s16 temp_t1; + SramContext* sramCtx = &this->sramCtx; + + this->windowContentVtx = Graph_Alloc(this->state.gfxCtx, 0x288 * sizeof(Vtx)); + + for (phi_t2 = 0; phi_t2 < 0x288; phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = 0x12C; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0x10; + + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = 0; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x10; + + this->windowContentVtx[phi_t2].v.ob[2] = this->windowContentVtx[phi_t2 + 1].v.ob[2] = + this->windowContentVtx[phi_t2 + 2].v.ob[2] = this->windowContentVtx[phi_t2 + 3].v.ob[2] = 0; + + this->windowContentVtx[phi_t2].v.flag = this->windowContentVtx[phi_t2 + 1].v.flag = + this->windowContentVtx[phi_t2 + 2].v.flag = this->windowContentVtx[phi_t2 + 3].v.flag = 0; + + this->windowContentVtx[phi_t2].v.tc[0] = this->windowContentVtx[phi_t2].v.tc[1] = + this->windowContentVtx[phi_t2 + 1].v.tc[1] = this->windowContentVtx[phi_t2 + 2].v.tc[0] = 0; + + this->windowContentVtx[phi_t2 + 1].v.tc[0] = this->windowContentVtx[phi_t2 + 2].v.tc[1] = + this->windowContentVtx[phi_t2 + 3].v.tc[0] = this->windowContentVtx[phi_t2 + 3].v.tc[1] = 0x200; + + this->windowContentVtx[phi_t2].v.cn[0] = this->windowContentVtx[phi_t2 + 1].v.cn[0] = + this->windowContentVtx[phi_t2 + 2].v.cn[0] = this->windowContentVtx[phi_t2 + 3].v.cn[0] = + this->windowContentVtx[phi_t2].v.cn[1] = this->windowContentVtx[phi_t2 + 1].v.cn[1] = + this->windowContentVtx[phi_t2 + 2].v.cn[1] = this->windowContentVtx[phi_t2 + 3].v.cn[1] = + this->windowContentVtx[phi_t2].v.cn[2] = this->windowContentVtx[phi_t2 + 1].v.cn[2] = + this->windowContentVtx[phi_t2 + 2].v.cn[2] = this->windowContentVtx[phi_t2 + 3].v.cn[2] = + this->windowContentVtx[phi_t2].v.cn[3] = this->windowContentVtx[phi_t2 + 1].v.cn[3] = + this->windowContentVtx[phi_t2 + 2].v.cn[3] = + this->windowContentVtx[phi_t2 + 3].v.cn[3] = 0xFF; + } + + this->windowContentVtx[0].v.ob[0] = this->windowContentVtx[2].v.ob[0] = this->windowPosX; + this->windowContentVtx[1].v.ob[0] = this->windowContentVtx[3].v.ob[0] = this->windowContentVtx[0].v.ob[0] + 0x80; + this->windowContentVtx[0].v.ob[1] = this->windowContentVtx[1].v.ob[1] = 0x48; + this->windowContentVtx[2].v.ob[1] = this->windowContentVtx[3].v.ob[1] = this->windowContentVtx[0].v.ob[1] - 0x10; + this->windowContentVtx[1].v.tc[0] = this->windowContentVtx[3].v.tc[0] = 0x1000; + + for (phi_a1 = 0, phi_t2 = 4; phi_a1 < 3; phi_a1++) { + phi_t0 = this->windowPosX - 6; + + for (phi_t5 = 0; phi_t5 < 5; phi_t5++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + sFileInfoBoxPartWidths[phi_t5]; + + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = + this->fileNamesY[phi_a1] + 0x2C; + + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x38; + + this->windowContentVtx[phi_t2 + 1].v.tc[0] = this->windowContentVtx[phi_t2 + 3].v.tc[0] = + sFileInfoBoxPartWidths[phi_t5] << 5; + this->windowContentVtx[phi_t2 + 2].v.tc[1] = this->windowContentVtx[phi_t2 + 3].v.tc[1] = 0x700; + phi_t0 += sFileInfoBoxPartWidths[phi_t5]; + } + } + + phi_t0 = this->windowPosX - 6; + phi_ra = 0x2C; + + for (phi_t5 = 0; phi_t5 < 3; phi_t5++, phi_t2 += 20, phi_ra -= 0x10) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0x40; + + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = + this->buttonYOffsets[phi_t5] + phi_ra; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x10; + + this->windowContentVtx[phi_t2 + 1].v.tc[0] = this->windowContentVtx[phi_t2 + 3].v.tc[0] = 0x800; + + this->windowContentVtx[phi_t2 + 4].v.ob[0] = this->windowContentVtx[phi_t2 + 6].v.ob[0] = phi_t0 + 0x40; + + this->windowContentVtx[phi_t2 + 5].v.ob[0] = this->windowContentVtx[phi_t2 + 7].v.ob[0] = + this->windowContentVtx[phi_t2 + 4].v.ob[0] + 0x6C; + + this->windowContentVtx[phi_t2 + 4].v.ob[1] = this->windowContentVtx[phi_t2 + 5].v.ob[1] = + this->buttonYOffsets[phi_t5] + phi_ra; + + this->windowContentVtx[phi_t2 + 6].v.ob[1] = this->windowContentVtx[phi_t2 + 7].v.ob[1] = + this->windowContentVtx[phi_t2 + 4].v.ob[1] - 0x10; + + this->windowContentVtx[phi_t2 + 5].v.tc[0] = this->windowContentVtx[phi_t2 + 7].v.tc[0] = 0xD80; + + if ((this->configMode == CM_COPY_ANIM_2) && (phi_t5 == this->copyDestFileIndex)) { + temp_t1 = this->fileNamesY[phi_t5] + 0x2C; + } else if (((this->configMode == CM_COPY_ANIM_3) || (this->configMode == CM_COPY_ANIM_4)) && + (phi_t5 == this->copyDestFileIndex)) { + temp_t1 = this->buttonYOffsets[phi_t5] + phi_ra; + } else { + temp_t1 = phi_ra + this->buttonYOffsets[phi_t5] + this->fileNamesY[phi_t5]; + } + + this->windowContentVtx[phi_t2 + 8].v.ob[0] = this->windowContentVtx[phi_t2 + 10].v.ob[0] = phi_t0 + 0xA8; + + this->windowContentVtx[phi_t2 + 9].v.ob[0] = this->windowContentVtx[phi_t2 + 11].v.ob[0] = + this->windowContentVtx[phi_t2 + 8].v.ob[0] + 0x2C; + + this->windowContentVtx[phi_t2 + 8].v.ob[1] = this->windowContentVtx[phi_t2 + 9].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 10].v.ob[1] = this->windowContentVtx[phi_t2 + 11].v.ob[1] = + this->windowContentVtx[phi_t2 + 8].v.ob[1] - 0x10; + + this->windowContentVtx[phi_t2 + 9].v.tc[0] = this->windowContentVtx[phi_t2 + 11].v.tc[0] = 0x580; + this->windowContentVtx[phi_t2 + 12].v.ob[0] = this->windowContentVtx[phi_t2 + 14].v.ob[0] = phi_t0 + 0x34; + this->windowContentVtx[phi_t2 + 13].v.ob[0] = this->windowContentVtx[phi_t2 + 15].v.ob[0] = + this->windowContentVtx[phi_t2 + 12].v.ob[0] + 0x18; + + this->windowContentVtx[phi_t2 + 12].v.ob[1] = this->windowContentVtx[phi_t2 + 13].v.ob[1] = + this->buttonYOffsets[phi_t5] + phi_ra; + + this->windowContentVtx[phi_t2 + 14].v.ob[1] = this->windowContentVtx[phi_t2 + 15].v.ob[1] = + this->windowContentVtx[phi_t2 + 12].v.ob[1] - 0x10; + + this->windowContentVtx[phi_t2 + 13].v.tc[0] = this->windowContentVtx[phi_t2 + 15].v.tc[0] = 0x300; + this->windowContentVtx[phi_t2 + 16].v.ob[0] = this->windowContentVtx[phi_t2 + 18].v.ob[0] = phi_t0 + 0x9C; + this->windowContentVtx[phi_t2 + 17].v.ob[0] = this->windowContentVtx[phi_t2 + 19].v.ob[0] = + this->windowContentVtx[phi_t2 + 16].v.ob[0] + 0x18; + + this->windowContentVtx[phi_t2 + 16].v.ob[1] = this->windowContentVtx[phi_t2 + 17].v.ob[1] = + this->buttonYOffsets[phi_t5] + phi_ra; + + this->windowContentVtx[phi_t2 + 18].v.ob[1] = this->windowContentVtx[phi_t2 + 19].v.ob[1] = + this->windowContentVtx[phi_t2 + 16].v.ob[1] - 0x10; + + this->windowContentVtx[phi_t2 + 17].v.tc[0] = this->windowContentVtx[phi_t2 + 19].v.tc[0] = 0x300; + } + + phi_ra = 0x2C; + + for (phi_t5 = 0; phi_t5 < 3; phi_t5++, phi_ra -= WREG(38)) { + if (SLOT_OCCUPIED(sramCtx, phi_t5)) { + phi_t0 = this->windowPosX - WREG(39); + + if ((this->configMode == 0xF) && (phi_t5 == this->copyDestFileIndex)) { + temp_t1 = this->fileNamesY[phi_t5] + 0x2C; + } else if (((this->configMode == CM_COPY_ANIM_3) || (this->configMode == CM_COPY_ANIM_4)) && + (phi_t5 == this->copyDestFileIndex)) { + temp_t1 = this->buttonYOffsets[phi_t5] + phi_ra; + } else { + temp_t1 = phi_ra + this->buttonYOffsets[phi_t5] + this->fileNamesY[phi_t5]; + } + + temp_t1 += 2; + + for (phi_a1 = 0; phi_a1 < 8; phi_a1++, phi_t2 += 4, phi_t0 += WREG(40)) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = + WREG(41) + phi_t0 + 0x40; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + WREG(42); + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1 - 3; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - WREG(43); + } + + phi_t0 = this->windowPosX - 14; + temp_t1 -= 0x16; + + for (phi_a1 = 0; phi_a1 < 4; phi_a1++, phi_t2 += 4) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + D_80812820[phi_a1]; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - D_80812828[phi_a1]; + phi_t0 += D_80812818[phi_a1]; + } + + this->windowContentVtx[phi_t2 - 15].v.tc[0] = this->windowContentVtx[phi_t2 - 13].v.tc[0] = 0x400; + + phi_t0 = this->windowPosX + 63; + temp_t1 += 4; + + for (phi_a1 = 0; phi_a1 < 20; phi_a1++, phi_t2 += 4, phi_t0 += 9) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0xA; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0xA; + + if (phi_a1 == 9) { + phi_t0 = this->windowPosX + 54; + temp_t1 -= 8; + } + } + + phi_t0 = this->windowPosX + 4; + temp_t1 -= 0xA; + + for (phi_a1 = 0; phi_a1 < 10; phi_a1++, phi_t2 += 4, phi_t0 += 0x10) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0x10; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = temp_t1; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x10; + } + } else { + phi_t2 += 0xA8; + } + } + + phi_t0 = this->windowPosX - 6; + phi_ra = -0xC; + + for (phi_t5 = 0; phi_t5 < 2; phi_t5++, phi_t2 += 4, phi_ra -= 0x10) { + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0x40; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = + this->buttonYOffsets[phi_t5 + 3] + phi_ra; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x10; + this->windowContentVtx[phi_t2 + 1].v.tc[0] = this->windowContentVtx[phi_t2 + 3].v.tc[0] = 0x800; + } + + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = phi_t0; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0x40; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = + this->buttonYOffsets[5] - 0x34; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x10; + this->windowContentVtx[phi_t2 + 1].v.tc[0] = this->windowContentVtx[phi_t2 + 3].v.tc[0] = 0x800; + + phi_t2 += 4; + + if (((this->menuMode == FS_MENU_MODE_CONFIG) && (this->configMode >= CM_MAIN_MENU)) || + ((this->menuMode == FS_MENU_MODE_SELECT) && (this->selectMode == SM_CONFIRM_FILE))) { + if (this->menuMode == FS_MENU_MODE_CONFIG) { + if ((this->configMode == CM_SELECT_COPY_SOURCE) || (this->configMode == CM_SELECT_COPY_DEST) || + (this->configMode == CM_ERASE_SELECT)) { + phi_t5 = D_8081283C[this->buttonIndex]; + } else if ((this->configMode == CM_ERASE_CONFIRM) || (this->configMode == CM_COPY_CONFIRM)) { + phi_t5 = D_80812844[this->buttonIndex]; + } else { + phi_t5 = D_80812830[this->buttonIndex]; + } + } else { + phi_t5 = D_80812848[this->confirmButtonIndex]; + } + + this->windowContentVtx[phi_t2].v.ob[0] = this->windowContentVtx[phi_t2 + 2].v.ob[0] = this->windowPosX - 0xA; + this->windowContentVtx[phi_t2 + 1].v.ob[0] = this->windowContentVtx[phi_t2 + 3].v.ob[0] = + this->windowContentVtx[phi_t2].v.ob[0] + 0x48; + this->windowContentVtx[phi_t2].v.ob[1] = this->windowContentVtx[phi_t2 + 1].v.ob[1] = + this->windowContentVtx[phi_t5].v.ob[1] + 4; + this->windowContentVtx[phi_t2 + 2].v.ob[1] = this->windowContentVtx[phi_t2 + 3].v.ob[1] = + this->windowContentVtx[phi_t2].v.ob[1] - 0x18; + this->windowContentVtx[phi_t2 + 1].v.tc[0] = this->windowContentVtx[phi_t2 + 3].v.tc[0] = 0x900; + this->windowContentVtx[phi_t2 + 2].v.tc[1] = this->windowContentVtx[phi_t2 + 3].v.tc[1] = 0x300; + } + + this->windowContentVtx[phi_t2 + 4].v.ob[0] = this->windowContentVtx[phi_t2 + 6].v.ob[0] = this->windowPosX + 0x3A; + this->windowContentVtx[phi_t2 + 5].v.ob[0] = this->windowContentVtx[phi_t2 + 7].v.ob[0] = + this->windowContentVtx[phi_t2 + 4].v.ob[0] + 0x80; + this->windowContentVtx[phi_t2 + 4].v.ob[1] = this->windowContentVtx[phi_t2 + 5].v.ob[1] = + this->windowContentVtx[D_80812830[this->warningButtonIndex]].v.ob[1]; + this->windowContentVtx[phi_t2 + 6].v.ob[1] = this->windowContentVtx[phi_t2 + 7].v.ob[1] = + this->windowContentVtx[phi_t2 + 4].v.ob[1] - 0x10; + this->windowContentVtx[phi_t2 + 5].v.tc[0] = this->windowContentVtx[phi_t2 + 7].v.tc[0] = 0x1000; +} + +static u16 D_8081284C[] = { 0x007C, 0x0124, 0x01CC }; + +static void* sQuestItemTextures[] = { + gFileSelKokiriEmeraldTex, gFileSelGoronRubyTex, gFileSelZoraSapphireTex, + gFileSelForestMedallionTex, gFileSelFireMedallionTex, gFileSelWaterMedallionTex, + gFileSelSpiritMedallionTex, gFileSelShadowMedallionTex, gFileSelLightMedallionTex, +}; + +static s16 sQuestItemRed[] = { 255, 255, 255, 0, 255, 0, 255, 200, 200 }; +static s16 sQuestItemGreen[] = { 255, 255, 255, 255, 60, 100, 130, 50, 200 }; +static s16 sQuestItemBlue[] = { 255, 255, 255, 0, 0, 255, 0, 255, 0 }; +static s16 sQuestItemFlags[] = { 0x0012, 0x0013, 0x0014, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005 }; +static s16 sNamePrimColors[2][3] = { { 255, 255, 255 }, { 100, 100, 100 } }; +static void* sHeartTextures[] = { gHeartFullTex, gDefenseHeartFullTex }; +static s16 sHeartPrimColors[2][3] = { { 255, 70, 50 }, { 200, 0, 0 } }; +static s16 sHeartEnvColors[2][3] = { { 50, 40, 60 }, { 255, 255, 255 } }; + +void FileChoose_DrawFileInfo(GameState* thisx, s16 fileIndex, s16 isActive) { + FileChooseContext* this = (FileChooseContext*)thisx; + Font* sp54 = &this->font; + s32 heartType; + s16 i; + s16 vtxOffset; + s16 j; + s16 deathCountSplit[3]; + + if (1) {} + + OPEN_DISPS(this->state.gfxCtx, "../z_file_choose.c", 1709); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + // draw file name + if (this->nameAlpha[fileIndex] != 0) { + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex]], 32, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, sNamePrimColors[isActive][0], sNamePrimColors[isActive][1], + sNamePrimColors[isActive][2], this->nameAlpha[fileIndex]); + + for (i = 0, vtxOffset = 0; vtxOffset < 0x20; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, + sp54->fontBuf + this->fileNames[fileIndex][i] * FONT_CHAR_TEX_SIZE, vtxOffset); + } + } + + if ((fileIndex == this->selectedFileIndex) || (fileIndex == this->copyDestFileIndex)) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, 255, 255, 255, this->fileInfoAlpha[fileIndex]); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex]] + 0x24, 12, 0); + + FileChoose_SplitNumber(this->deaths[fileIndex], &deathCountSplit[0], &deathCountSplit[1], &deathCountSplit[2]); + + // draw death count + for (i = 0, vtxOffset = 0; i < 3; i++, vtxOffset += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, sp54->fontBuf + deathCountSplit[i] * FONT_CHAR_TEX_SIZE, + vtxOffset); + } + + gDPPipeSync(POLY_OPA_DISP++); + + heartType = (this->defense[fileIndex] == 0) ? 0 : 1; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, sHeartPrimColors[heartType][0], sHeartPrimColors[heartType][1], + sHeartPrimColors[heartType][2], this->fileInfoAlpha[fileIndex]); + gDPSetEnvColor(POLY_OPA_DISP++, sHeartEnvColors[heartType][0], sHeartEnvColors[heartType][1], + sHeartEnvColors[heartType][2], 255); + + i = this->healthCapacities[fileIndex] / 0x10; + + // draw hearts + for (vtxOffset = 0, j = 0; j < i; j++, vtxOffset += 4) { + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex] + vtxOffset] + 0x30, 4, 0); + + POLY_OPA_DISP = FileChoose_QuadTextureIA8(POLY_OPA_DISP, sHeartTextures[heartType], 0x10, 0x10, 0); + } + + gDPPipeSync(POLY_OPA_DISP++); + + // draw quest items + for (vtxOffset = 0, j = 0; j < 9; j++, vtxOffset += 4) { + if (this->questItems[fileIndex] & gBitFlags[sQuestItemFlags[j]]) { + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[D_8081284C[fileIndex] + vtxOffset] + 0x80, 4, 0); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0x00, 0x00, sQuestItemRed[j], sQuestItemGreen[j], sQuestItemBlue[j], + this->fileInfoAlpha[fileIndex]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + if (j < 3) { + gDPLoadTextureBlock(POLY_OPA_DISP++, sQuestItemTextures[j], G_IM_FMT_RGBA, G_IM_SIZ_32b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + } else { + POLY_OPA_DISP = FileChoose_QuadTextureIA8(POLY_OPA_DISP, sQuestItemTextures[j], 0x10, 0x10, 0); + } + } + } + } + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_choose.c", 1797); +} + +static void* sFileInfoBoxTextures[] = { + gFileSelFileInfoBox1Tex, gFileSelFileInfoBox2Tex, gFileSelFileInfoBox3Tex, + gFileSelFileInfoBox4Tex, gFileSelFileInfoBox5Tex, +}; + +static void* sTitleLabels[3][9] = { + { gFileSelPleaseSelectAFileENGTex, gFileSelOpenThisFileENGTex, gFileSelCopyWhichFileENGTex, + gFileSelCopyToWhichFileENGTex, gFileSelAreYouSureENGTex, gFileSelFileCopiedENGTex, gFileSelEraseWhichFileENGTex, + gFileSelAreYouSure2ENGTex, gFileSelFileErasedENGTex }, + { gFileSelPleaseSelectAFileGERTex, gFileSelOpenThisFileGERTex, gFileSelWhichFile1GERTex, + gFileSelCopyToWhichFileGERTex, gFileSelAreYouSureGERTex, gFileSelFileCopiedGERTex, gFileSelWhichFile2GERTex, + gFileSelAreYouSure2GERTex, gFileSelFileErasedGERTex }, + { gFileSelPleaseSelectAFileFRATex, gFileSelOpenThisFileFRATex, gFileSelCopyWhichFileFRATex, + gFileSelCopyToWhichFileFRATex, gFileSelAreYouSureFRATex, gFileSelFileCopiedFRATex, gFileSelEraseWhichFileFRATex, + gFileSelAreYouSure2FRATex, gFileSelFileErasedFRATex } +}; + +static void* sWarningLabels[3][5] = { + { gFileSelNoFileToCopyENGTex, gFileSelNoFileToEraseENGTex, gFileSelNoEmptyFileENGTex, gFileSelFileEmptyENGTex, + gFileSelFileInUseENGTex }, + { gFileSelNoFileToCopyGERTex, gFileSelNoFileToEraseGERTex, gFileSelNoEmptyFileGERTex, gFileSelFileEmptyGERTex, + gFileSelFileInUseGERTex }, + { gFileSelNoFileToCopyFRATex, gFileSelNoFileToEraseFRATex, gFileSelNoEmptyFileFRATex, gFileSelFileEmptyFRATex, + gFileSelFileInUseFRATex }, +}; + +static void* sFileButtonTextures[3][3] = { + { gFileSelFile1ButtonENGTex, gFileSelFile2ButtonENGTex, gFileSelFile3ButtonENGTex }, + { gFileSelFile1ButtonGERTex, gFileSelFile2ButtonGERTex, gFileSelFile3ButtonGERTex }, + { gFileSelFile1ButtonFRATex, gFileSelFile2ButtonFRATex, gFileSelFile3ButtonFRATex }, +}; + +static void* sActionButtonTextures[3][4] = { + { gFileSelCopyButtonENGTex, gFileSelEraseButtonENGTex, gFileSelYesButtonENGTex, gFileSelQuitButtonENGTex }, + { gFileSelCopyButtonGERTex, gFileSelEraseButtonGERTex, gFileSelYesButtonGERTex, gFileSelQuitButtonGERTex }, + { gFileSelCopyButtonFRATex, gFileSelEraseButtonFRATex, gFileSelYesButtonFRATex, gFileSelQuitButtonFRATex }, +}; + +static void* sOptionsButtonTextures[] = { + gFileSelOptionsButtonENGTex, + gFileSelOptionsButtonGERTex, + gFileSelOptionsButtonENGTex, +}; + +/** + * Draw most window contents including buttons, labels, and icons. + * Does not include anything from the keyboard and settings windows. + */ +void FileChoose_DrawWindowContents(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 fileIndex; + s16 temp; + s16 i; + s16 quadVtxIndex; + s16 isActive; + s16 pad; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_choose.c", 1940); + + // draw title label + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gSPVertex(POLY_OPA_DISP++, this->windowContentVtx, 4, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, sTitleLabels[gSaveContext.language][this->titleLabel], G_IM_FMT_IA, + G_IM_SIZ_8b, 128, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + // draw next title label + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[1]); + gDPLoadTextureBlock(POLY_OPA_DISP++, sTitleLabels[gSaveContext.language][this->nextTitleLabel], G_IM_FMT_IA, + G_IM_SIZ_8b, 128, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + temp = 4; + + gDPPipeSync(POLY_OPA_DISP++); + + // draw file info box (large box when a file is selected) + for (fileIndex = 0; fileIndex < 3; fileIndex++, temp += 20) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->fileInfoAlpha[fileIndex]); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[temp], 20, 0); + + for (quadVtxIndex = 0, i = 0; i < 5; i++, quadVtxIndex += 4) { + gDPLoadTextureBlock(POLY_OPA_DISP++, sFileInfoBoxTextures[i], G_IM_FMT_IA, G_IM_SIZ_16b, + sFileInfoBoxPartWidths[i], 56, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, quadVtxIndex, quadVtxIndex + 2, quadVtxIndex + 3, quadVtxIndex + 1, 0); + } + } + + for (i = 0; i < 3; i++, temp += 20) { + // draw file button + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[temp], 20, 0); + + isActive = ((this->n64ddFlag == this->n64ddFlags[i]) || (this->nameBoxAlpha[i] == 0)) ? 0 : 1; + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, sWindowContentColors[isActive][0], sWindowContentColors[isActive][1], + sWindowContentColors[isActive][2], this->fileButtonAlpha[i]); + gDPLoadTextureBlock(POLY_OPA_DISP++, sFileButtonTextures[gSaveContext.language][i], G_IM_FMT_IA, G_IM_SIZ_16b, + 64, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + // draw file name box + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, sWindowContentColors[isActive][0], sWindowContentColors[isActive][1], + sWindowContentColors[isActive][2], this->nameBoxAlpha[i]); + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelNameBoxTex, G_IM_FMT_IA, G_IM_SIZ_16b, 108, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 4, 6, 7, 5, 0); + + // draw disk label for 64DD + if (this->n64ddFlags[i]) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, sWindowContentColors[isActive][0], sWindowContentColors[isActive][1], + sWindowContentColors[isActive][2], this->nameAlpha[i]); + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelDISKButtonTex, G_IM_FMT_IA, G_IM_SIZ_16b, 44, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 8, 10, 11, 9, 0); + } + + // draw connectors + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, sWindowContentColors[isActive][0], sWindowContentColors[isActive][1], + sWindowContentColors[isActive][2], this->connectorAlpha[i]); + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelConnectorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 24, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 12, 14, 15, 13, 0); + + if (this->n64ddFlags[i]) { + gSP1Quadrangle(POLY_OPA_DISP++, 16, 18, 19, 17, 0); + } + } + + // draw file info + for (fileIndex = 0; fileIndex < 3; fileIndex++) { + isActive = ((this->n64ddFlag == this->n64ddFlags[fileIndex]) || (this->nameBoxAlpha[fileIndex] == 0)) ? 0 : 1; + FileChoose_DrawFileInfo(&this->state, fileIndex, isActive); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gSPVertex(POLY_OPA_DISP++, &this->windowContentVtx[0x274], 20, 0); + + // draw primary action buttons (copy/erase) + for (quadVtxIndex = 0, i = 0; i < 2; i++, quadVtxIndex += 4) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->actionButtonAlpha[i]); + gDPLoadTextureBlock(POLY_OPA_DISP++, sActionButtonTextures[gSaveContext.language][i], G_IM_FMT_IA, G_IM_SIZ_16b, + 64, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, quadVtxIndex, quadVtxIndex + 2, quadVtxIndex + 3, quadVtxIndex + 1, 0); + } + + gDPPipeSync(POLY_OPA_DISP++); + + // draw confirm buttons (yes/quit) + for (quadVtxIndex = 0, i = 0; i < 2; i++, quadVtxIndex += 4) { + temp = this->confirmButtonTexIndices[i]; + + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->confirmButtonAlpha[i]); + gDPLoadTextureBlock(POLY_OPA_DISP++, sActionButtonTextures[gSaveContext.language][temp], G_IM_FMT_IA, + G_IM_SIZ_16b, 64, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, quadVtxIndex, quadVtxIndex + 2, quadVtxIndex + 3, quadVtxIndex + 1, 0); + } + + // draw options button + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->optionButtonAlpha); + gDPLoadTextureBlock(POLY_OPA_DISP++, sOptionsButtonTextures[gSaveContext.language], G_IM_FMT_IA, G_IM_SIZ_16b, 64, + 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 8, 10, 11, 9, 0); + + // draw highlight over currently selected button + if (((this->menuMode == FS_MENU_MODE_CONFIG) && + ((this->configMode == CM_MAIN_MENU) || (this->configMode == CM_SELECT_COPY_SOURCE) || + (this->configMode == CM_SELECT_COPY_DEST) || (this->configMode == CM_COPY_CONFIRM) || + (this->configMode == CM_ERASE_SELECT) || (this->configMode == CM_ERASE_CONFIRM))) || + ((this->menuMode == FS_MENU_MODE_SELECT) && (this->selectMode == SM_CONFIRM_FILE))) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->highlightColor[0], this->highlightColor[1], + this->highlightColor[2], this->highlightColor[3]); + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelBigButtonHighlightTex, G_IM_FMT_I, G_IM_SIZ_8b, 72, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 12, 14, 15, 13, 0); + } + + // draw warning labels + if (this->warningLabel > FS_WARNING_NONE) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->emptyFileTextAlpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, sWarningLabels[gSaveContext.language][this->warningLabel], G_IM_FMT_IA, + G_IM_SIZ_8b, 128, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 16, 18, 19, 17, 0); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2198); +} + +void FileChoose_ConfigModeDraw(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + f32 eyeX; + f32 eyeY; + f32 eyeZ; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2218); + gDPPipeSync(POLY_OPA_DISP++); + + eyeX = 1000.0f * Math_CosS(ZREG(11)) - 1000.0f * Math_SinS(ZREG(11)); + eyeY = ZREG(13); + eyeZ = 1000.0f * Math_SinS(ZREG(11)) + 1000.0f * Math_CosS(ZREG(11)); + + FileChoose_SetView(this, eyeX, eyeY, eyeZ); + SkyboxDraw_Draw(&this->skyboxCtx, this->state.gfxCtx, 1, this->envCtx.skyboxBlend, eyeX, eyeY, eyeZ); + gDPSetTextureLUT(POLY_OPA_DISP++, G_TT_NONE); + ZREG(11) += ZREG(10); + Environment_UpdateSkybox(NULL, SKYBOX_NORMAL_SKY, &this->envCtx, &this->skyboxCtx); + gDPPipeSync(POLY_OPA_DISP++); + func_800949A8(this->state.gfxCtx); + FileChoose_SetView(this, 0.0f, 0.0f, 64.0f); + FileChoose_SetWindowVtx(&this->state); + FileChoose_SetWindowContentVtx(&this->state); + + if ((this->configMode != CM_NAME_ENTRY) && (this->configMode != CM_START_NAME_ENTRY)) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->windowAlpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + Matrix_Translate(0.0f, 0.0f, -93.6f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + + if (this->windowRot != 0) { + Matrix_RotateX(this->windowRot / 100.0f, MTXMODE_APPLY); + } + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_choose.c", 2282), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[0], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow1DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[32], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow2DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[64], 16, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow3DL); + + gDPPipeSync(POLY_OPA_DISP++); + + FileChoose_DrawWindowContents(&this->state); + } + + // draw name entry menu + if ((this->configMode >= CM_ROTATE_TO_NAME_ENTRY) && (this->configMode <= CM_NAME_ENTRY_TO_MAIN)) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->windowAlpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + Matrix_Translate(0.0f, 0.0f, -93.6f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX((this->windowRot - 314.0f) / 100.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_choose.c", 2316), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[0], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow1DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[32], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow2DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[64], 16, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow3DL); + + gDPPipeSync(POLY_OPA_DISP++); + + FileChoose_DrawNameEntry(&this->state); + } + + // draw options menu + if ((this->configMode >= CM_MAIN_TO_OPTIONS) && (this->configMode <= CM_OPTIONS_TO_MAIN)) { + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->windowAlpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + Matrix_Translate(0.0f, 0.0f, -93.6f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX((this->windowRot - 314.0f) / 100.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_choose.c", 2337), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[0], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow1DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[32], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow2DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[64], 16, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow3DL); + + gDPPipeSync(POLY_OPA_DISP++); + + FileChoose_DrawOptions(&this->state); + } + + gDPPipeSync(POLY_OPA_DISP++); + FileChoose_SetView(this, 0.0f, 0.0f, 64.0f); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2352); +} + +/** + * Fade out the main menu elements to transition to select mode. + * Update function for `SM_FADE_MAIN_TO_SELECT` + */ +void FileChoose_FadeMainToSelect(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + + for (i = 0; i < 3; i++) { + if (i != this->buttonIndex) { + this->fileButtonAlpha[i] -= 25; + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->optionButtonAlpha = this->fileButtonAlpha[i]; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameAlpha[i] = this->nameBoxAlpha[i] = this->fileButtonAlpha[i]; + this->connectorAlpha[i] -= 31; + } + } + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->actionTimer = 8; + this->selectMode++; + this->confirmButtonIndex = FS_BTN_CONFIRM_YES; + } +} + +/** + * Moves the selected file to the top of the window. + * Update function for `SM_MOVE_FILE_TO_TOP` + */ +void FileChoose_MoveSelectedFileToTop(GameState* thisx) { + static s16 fileYOffsets[] = { 0, 16, 32 }; // amount to move by to reach the top of the screen + FileChooseContext* this = (FileChooseContext*)thisx; + s16 yStep; + + yStep = ABS(this->buttonYOffsets[this->buttonIndex] - fileYOffsets[this->buttonIndex]) / this->actionTimer; + this->buttonYOffsets[this->buttonIndex] += yStep; + this->actionTimer--; + + if ((this->actionTimer == 0) || (this->buttonYOffsets[this->buttonIndex] == fileYOffsets[this->buttonIndex])) { + this->buttonYOffsets[FS_BTN_SELECT_YES] = this->buttonYOffsets[FS_BTN_SELECT_QUIT] = -24; + this->actionTimer = 8; + this->selectMode++; + } +} + +/** + * Fade in the file info for the selected file. + * Update function for `SM_FADE_IN_FILE_INFO` + */ +void FileChoose_FadeInFileInfo(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->fileInfoAlpha[this->buttonIndex] += 25; + this->nameBoxAlpha[this->buttonIndex] -= 50; + + if (this->nameBoxAlpha[this->buttonIndex] <= 0) { + this->nameBoxAlpha[this->buttonIndex] = 0; + } + + this->actionTimer--; + + if (this->actionTimer == 0) { + this->fileInfoAlpha[this->buttonIndex] = 200; + this->actionTimer = 8; + this->selectMode++; + } + + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] = this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] = + this->fileInfoAlpha[this->buttonIndex]; +} + +/** + * Update the cursor and handle the option that the player picks for confirming the selected file. + * Update function for `SM_CONFIRM_FILE` + */ +void FileChoose_ConfirmFile(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Input* input = &this->state.input[0]; + + if (CHECK_BTN_ALL(input->press.button, BTN_START) || (CHECK_BTN_ALL(input->press.button, BTN_A))) { + if (this->confirmButtonIndex == FS_BTN_CONFIRM_YES) { + func_800AA000(300.0f, 180, 20, 100); + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->selectMode = SM_FADE_OUT; + func_800F6964(0xF); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->selectMode++; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->selectMode++; + } else if (ABS(this->stickRelY) >= 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->confirmButtonIndex ^= 1; + } +} + +/** + * Fade out the file info for the selected file before returning to the main menu. + * Update function for `SM_FADE_OUT_FILE_INFO` + */ +void FileChoose_FadeOutFileInfo(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->fileInfoAlpha[this->buttonIndex] -= 25; + this->nameBoxAlpha[this->buttonIndex] += 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->buttonYOffsets[FS_BTN_SELECT_YES] = this->buttonYOffsets[FS_BTN_SELECT_QUIT] = 0; + this->nameBoxAlpha[this->buttonIndex] = 200; + this->fileInfoAlpha[this->buttonIndex] = 0; + this->nextTitleLabel = FS_TITLE_SELECT_FILE; + this->actionTimer = 8; + this->selectMode++; + } + + this->confirmButtonAlpha[0] = this->confirmButtonAlpha[1] = this->fileInfoAlpha[this->buttonIndex]; +} + +/** + * Move the selected file back to the slot position then go to config mode for the main menu. + * Update function for `SM_MOVE_FILE_TO_SLOT` + */ +void FileChoose_MoveSelectedFileToSlot(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 yStep; + s16 i; + + yStep = ABS(this->buttonYOffsets[this->buttonIndex]) / this->actionTimer; + this->buttonYOffsets[this->buttonIndex] -= yStep; + + if (this->buttonYOffsets[this->buttonIndex] <= 0) { + this->buttonYOffsets[this->buttonIndex] = 0; + } + + for (i = 0; i < 3; i++) { + if (i != this->buttonIndex) { + this->fileButtonAlpha[i] += 25; + + if (this->fileButtonAlpha[i] >= 200) { + this->fileButtonAlpha[i] = 200; + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->optionButtonAlpha = this->fileButtonAlpha[i]; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + this->connectorAlpha[i] += 31; + } + } + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->titleLabel = this->nextTitleLabel; + this->actionTimer = 8; + this->menuMode = FS_MENU_MODE_CONFIG; + this->configMode = CM_MAIN_MENU; + this->nextConfigMode = CM_MAIN_MENU; + this->selectMode = SM_FADE_MAIN_TO_SELECT; + } +} + +/** + * Fill the screen with black to fade out. + * Update function for `SM_FADE_OUT` + */ +void FileChoose_FadeOut(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + sScreenFillAlpha += VREG(10); + + if (sScreenFillAlpha >= 255) { + sScreenFillAlpha = 255; + this->selectMode++; + } +} + +/** + * Load the save for the appropriate file and start the game. + * Note: On Debug ROM, File 1 will go to Map Select. + * Update function for `SM_LOAD_GAME` + */ +void FileChoose_LoadGame(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + u16 swordEquipMask; + s32 pad; + + if (this->buttonIndex == FS_BTN_SELECT_FILE_1 && CVar_GetS32("gDebugEnabled", 0)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + gSaveContext.fileNum = this->buttonIndex; + Sram_OpenSave(&this->sramCtx); + gSaveContext.gameMode = 0; + SET_NEXT_GAMESTATE(&this->state, Select_Init, SelectContext); + this->state.running = false; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + gSaveContext.fileNum = this->buttonIndex; + Sram_OpenSave(&this->sramCtx); + gSaveContext.gameMode = 0; + SET_NEXT_GAMESTATE(&this->state, Gameplay_Init, GlobalContext); + this->state.running = false; + } + + gSaveContext.respawn[0].entranceIndex = -1; + gSaveContext.respawnFlag = 0; + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = 0xFF; + gSaveContext.showTitleCard = true; + gSaveContext.dogParams = 0; + gSaveContext.timer1State = 0; + gSaveContext.timer2State = 0; + gSaveContext.eventInf[0] = 0; + gSaveContext.eventInf[1] = 0; + gSaveContext.eventInf[2] = 0; + gSaveContext.eventInf[3] = 0; + gSaveContext.unk_13EE = 0x32; + gSaveContext.nayrusLoveTimer = 0; + gSaveContext.healthAccumulator = 0; + gSaveContext.unk_13F0 = 0; + gSaveContext.unk_13F2 = 0; + gSaveContext.forcedSeqId = NA_BGM_GENERAL_SFX; + gSaveContext.skyboxTime = 0; + gSaveContext.nextTransition = 0xFF; + gSaveContext.nextCutsceneIndex = 0xFFEF; + gSaveContext.cutsceneTrigger = 0; + gSaveContext.chamberCutsceneNum = 0; + gSaveContext.nextDayTime = 0xFFFF; + gSaveContext.unk_13C3 = 0; + + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = gSaveContext.buttonStatus[4] = BTN_ENABLED; + + gSaveContext.unk_13E7 = gSaveContext.unk_13E8 = gSaveContext.unk_13EA = gSaveContext.unk_13EC = + gSaveContext.unk_13F4 = 0; + + gSaveContext.unk_13F6 = gSaveContext.magic; + gSaveContext.magic = 0; + gSaveContext.magicLevel = gSaveContext.magic; + + if (1) {} + + osSyncPrintf(VT_FGCOL(GREEN)); + osSyncPrintf("Z_MAGIC_NOW_NOW=%d MAGIC_NOW=%d\n", ((void)0, gSaveContext.unk_13F6), gSaveContext.magic); + osSyncPrintf(VT_RST); + + gSaveContext.naviTimer = 0; + + // SWORDLESS LINK IS BACK BABY + if (CVar_GetS32("gSwordlessLink", 0) != 0) + { + if ((gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KOKIRI) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_MASTER) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_BGS) && + (gSaveContext.equips.buttonItems[0] != ITEM_SWORD_KNIFE)) { + + gSaveContext.equips.buttonItems[0] = ITEM_NONE; + swordEquipMask = _byteswap_ushort(gEquipMasks[EQUIP_SWORD]) & gSaveContext.equips.equipment; + gSaveContext.equips.equipment &= gEquipNegMasks[EQUIP_SWORD]; + gSaveContext.inventory.equipment ^= (gBitFlags[swordEquipMask - 1] << _byteswap_ushort(gEquipShifts[EQUIP_SWORD])); + } + } +} + +static void (*gSelectModeUpdateFuncs[])(GameState*) = { + FileChoose_FadeMainToSelect, FileChoose_MoveSelectedFileToTop, FileChoose_FadeInFileInfo, FileChoose_ConfirmFile, + FileChoose_FadeOutFileInfo, FileChoose_MoveSelectedFileToSlot, FileChoose_FadeOut, FileChoose_LoadGame, +}; + +void FileChoose_SelectModeUpdate(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + gSelectModeUpdateFuncs[this->selectMode](&this->state); +} + +void FileChoose_SelectModeDraw(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + f32 eyeX; + f32 eyeY; + f32 eyeZ; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2753); + + gDPPipeSync(POLY_OPA_DISP++); + + eyeX = 1000.0f * Math_CosS(ZREG(11)) - 1000.0f * Math_SinS(ZREG(11)); + eyeY = ZREG(13); + eyeZ = 1000.0f * Math_SinS(ZREG(11)) + 1000.0f * Math_CosS(ZREG(11)); + + FileChoose_SetView(this, eyeX, eyeY, eyeZ); + SkyboxDraw_Draw(&this->skyboxCtx, this->state.gfxCtx, 1, this->envCtx.skyboxBlend, eyeX, eyeY, eyeZ); + gDPSetTextureLUT(POLY_OPA_DISP++, G_TT_NONE); + ZREG(11) += ZREG(10); + Environment_UpdateSkybox(NULL, SKYBOX_NORMAL_SKY, &this->envCtx, &this->skyboxCtx); + gDPPipeSync(POLY_OPA_DISP++); + func_800949A8(this->state.gfxCtx); + FileChoose_SetView(this, 0.0f, 0.0f, 64.0f); + FileChoose_SetWindowVtx(&this->state); + FileChoose_SetWindowContentVtx(&this->state); + + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->windowAlpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + Matrix_Translate(0.0f, 0.0f, -93.6f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(this->windowRot / 100.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_choose.c", 2810), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[0], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow1DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[32], 32, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow2DL); + + gSPVertex(POLY_OPA_DISP++, &this->windowVtx[64], 16, 0); + gSPDisplayList(POLY_OPA_DISP++, gFileSelWindow3DL); + + FileChoose_DrawWindowContents(&this->state); + gDPPipeSync(POLY_OPA_DISP++); + FileChoose_SetView(this, 0.0f, 0.0f, 64.0f); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2834); +} + +static void (*gFileSelectDrawFuncs[])(GameState*) = { + FileChoose_InitModeDraw, + FileChoose_ConfigModeDraw, + FileChoose_SelectModeDraw, +}; + +static void (*gFileSelectUpdateFuncs[])(GameState*) = { + FileChoose_InitModeUpdate, + FileChoose_ConfigModeUpdate, + FileChoose_SelectModeUpdate, +}; + +void FileChoose_Main(GameState* thisx) { + static void* controlsTextures[] = { + gFileSelControlsENGTex, + gFileSelControlsGERTex, + gFileSelControlsFRATex, + }; + FileChooseContext* this = (FileChooseContext*)thisx; + Input* input = &this->state.input[0]; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_choose.c", 2898); + + this->n64ddFlag = 0; + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + gSPSegment(POLY_OPA_DISP++, 0x01, this->staticSegment); + gSPSegment(POLY_OPA_DISP++, 0x02, this->parameterSegment); + + func_80095248(this->state.gfxCtx, 0, 0, 0); + + this->stickRelX = input->rel.stick_x; + this->stickRelY = input->rel.stick_y; + + if (this->stickRelX < -30) { + if (this->stickXDir == -1) { + this->inputTimerX--; + if (this->inputTimerX < 0) { + this->inputTimerX = 2; + } else { + this->stickRelX = 0; + } + } else { + this->inputTimerX = 10; + this->stickXDir = -1; + } + } else if (this->stickRelX > 30) { + if (this->stickXDir == 1) { + this->inputTimerX--; + if (this->inputTimerX < 0) { + this->inputTimerX = 2; + } else { + this->stickRelX = 0; + } + } else { + this->inputTimerX = 10; + this->stickXDir = 1; + } + } else { + this->stickXDir = 0; + } + + if (this->stickRelY < -30) { + if (this->stickYDir == -1) { + this->inputTimerY -= 1; + if (this->inputTimerY < 0) { + this->inputTimerY = 2; + } else { + this->stickRelY = 0; + } + } else { + this->inputTimerY = 10; + this->stickYDir = -1; + } + } else if (this->stickRelY > 30) { + if (this->stickYDir == 1) { + this->inputTimerY -= 1; + if (this->inputTimerY < 0) { + this->inputTimerY = 2; + } else { + this->stickRelY = 0; + } + } else { + this->inputTimerY = 10; + this->stickYDir = 1; + } + } else { + this->stickYDir = 0; + } + + this->emptyFileTextAlpha = 0; + + FileChoose_PulsateCursor(&this->state); + gFileSelectUpdateFuncs[this->menuMode](&this->state); + gFileSelectDrawFuncs[this->menuMode](&this->state); + + // do not draw controls text in the options menu + if ((this->configMode <= CM_NAME_ENTRY_TO_MAIN) || (this->configMode >= CM_UNUSED_DELAY)) { + func_800944C4(this->state.gfxCtx); + + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 100, 255, 255, this->controlsAlpha); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, controlsTextures[gSaveContext.language], G_IM_FMT_IA, G_IM_SIZ_8b, 144, 16, + 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gSPTextureRectangle(POLY_OPA_DISP++, 0x0168, 0x0330, 0x03A8, 0x0370, G_TX_RENDERTILE, 0, 0, 0x0400, 0x0400); + } + + gDPPipeSync(POLY_OPA_DISP++); + gSPDisplayList(POLY_OPA_DISP++, sScreenFillSetupDL); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 0, 0, sScreenFillAlpha); + gDPFillRectangle(POLY_OPA_DISP++, 0, 0, gScreenWidth - 1, gScreenHeight - 1); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_choose.c", 3035); +} + +void FileChoose_InitContext(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + EnvironmentContext* envCtx = &this->envCtx; + SramContext* sramCtx = &this->sramCtx; + + Sram_Alloc(&this->state, sramCtx); + + ZREG(7) = 32; + ZREG(8) = 22; + ZREG(9) = 20; + ZREG(10) = -10; + ZREG(11) = 0; + ZREG(12) = 1000; + ZREG(13) = -700; + ZREG(14) = 164; + ZREG(15) = 104; + ZREG(16) = 160; + ZREG(17) = 100; + ZREG(18) = 162; + ZREG(19) = 152; + ZREG(20) = 214; + + XREG(13) = 580; + XREG(14) = 400; + XREG(35) = 20; + XREG(36) = 20; + XREG(37) = 20; + XREG(43) = 8; + XREG(44) = -78; + XREG(45) = 0; + XREG(46) = 0; + XREG(47) = 0; + XREG(48) = 0; + XREG(49) = 3; + XREG(50) = 8; + XREG(51) = 8; + XREG(52) = 10; + XREG(73) = 0; + + VREG(0) = 14; + VREG(1) = 5; + VREG(2) = 4; + VREG(4) = 1; + VREG(5) = 6; + VREG(6) = 2; + VREG(7) = 6; + VREG(8) = 80; + + sScreenFillAlpha = 255; + + VREG(10) = 10; + VREG(11) = 30; + VREG(12) = -100; + VREG(13) = -85; + VREG(14) = 4; + VREG(16) = 25; + VREG(17) = 1; + VREG(18) = 1; + VREG(20) = 92; + VREG(21) = 171; + VREG(22) = 11; + VREG(23) = 10; + VREG(24) = 26; + VREG(25) = 2; + VREG(26) = 1; + VREG(27) = 0; + VREG(28) = 0; + VREG(29) = 160; + VREG(30) = 64; + VREG(31) = 154; + VREG(32) = 152; + VREG(33) = 106; + + WREG(38) = 16; + WREG(39) = 9; + WREG(40) = 10; + WREG(41) = 14; + WREG(42) = 11; + WREG(43) = 12; + + this->menuMode = FS_MENU_MODE_INIT; + + this->buttonIndex = this->selectMode = this->selectedFileIndex = this->copyDestFileIndex = + this->confirmButtonIndex = 0; + + this->confirmButtonTexIndices[0] = 2; + this->confirmButtonTexIndices[1] = 3; + this->titleLabel = FS_TITLE_SELECT_FILE; + this->nextTitleLabel = FS_TITLE_OPEN_FILE; + this->highlightPulseDir = 1; + this->unk_1CAAC = 0xC; + this->highlightColor[0] = 155; + this->highlightColor[1] = 255; + this->highlightColor[2] = 255; + this->highlightColor[3] = 70; + this->configMode = CM_FADE_IN_START; + this->windowRot = 0.0f; + this->stickXDir = this->inputTimerX = 0; + this->stickYDir = this->inputTimerY = 0; + this->kbdX = this->kbdY = this->charIndex = 0; + this->kbdButton = FS_KBD_BTN_NONE; + + this->windowColor[0] = 100; + this->windowColor[1] = 150; + this->windowColor[2] = 255; + + this->windowAlpha = this->titleAlpha[0] = this->titleAlpha[1] = this->fileButtonAlpha[0] = + this->fileButtonAlpha[1] = this->fileButtonAlpha[2] = this->nameBoxAlpha[0] = this->nameBoxAlpha[1] = + this->nameBoxAlpha[2] = this->nameAlpha[0] = this->nameAlpha[1] = this->nameAlpha[2] = + this->connectorAlpha[0] = this->connectorAlpha[1] = this->connectorAlpha[2] = this->fileInfoAlpha[0] = + this->fileInfoAlpha[1] = this->fileInfoAlpha[2] = this->actionButtonAlpha[FS_BTN_ACTION_COPY] = + this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] = + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] = this->optionButtonAlpha = + this->nameEntryBoxAlpha = this->controlsAlpha = this->emptyFileTextAlpha = 0; + + this->windowPosX = 6; + this->actionTimer = 8; + this->warningLabel = FS_WARNING_NONE; + + this->warningButtonIndex = this->buttonYOffsets[0] = this->buttonYOffsets[1] = this->buttonYOffsets[2] = + this->buttonYOffsets[3] = this->buttonYOffsets[4] = this->buttonYOffsets[5] = this->fileNamesY[0] = + this->fileNamesY[1] = this->fileNamesY[2] = 0; + + this->unk_1CAD6[0] = 0; + this->unk_1CAD6[1] = 3; + this->unk_1CAD6[2] = 6; + this->unk_1CAD6[3] = 8; + this->unk_1CAD6[4] = 10; + + ShrinkWindow_SetVal(0); + + gSaveContext.skyboxTime = 0; + gSaveContext.dayTime = 0; + + Skybox_Init(&this->state, &this->skyboxCtx, SKYBOX_NORMAL_SKY); + + gTimeIncrement = 10; + + envCtx->unk_19 = 0; + envCtx->unk_1A = 0; + envCtx->unk_21 = 0; + envCtx->unk_22 = 0; + envCtx->skyboxDmaState = SKYBOX_DMA_INACTIVE; + envCtx->skybox1Index = 99; + envCtx->skybox2Index = 99; + envCtx->unk_1F = 0; + envCtx->unk_20 = 0; + envCtx->unk_BD = 0; + envCtx->unk_17 = 2; + envCtx->skyboxDisabled = 0; + envCtx->skyboxBlend = 0; + envCtx->unk_84 = 0.0f; + envCtx->unk_88 = 0.0f; + + Environment_UpdateSkybox(NULL, SKYBOX_NORMAL_SKY, &this->envCtx, &this->skyboxCtx); + + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = gSaveContext.buttonStatus[4] = BTN_ENABLED; + + this->n64ddFlags[0] = this->n64ddFlags[1] = this->n64ddFlags[2] = this->defense[0] = this->defense[1] = + this->defense[2] = 0; + + SsSram_ReadWrite(OS_K1_TO_PHYSICAL(0xA8000000), sramCtx->readBuff, SRAM_SIZE, OS_READ); + + gSaveContext.language = sramCtx->readBuff[SRAM_HEADER_LANGUAGE]; + + if (gSaveContext.language >= LANGUAGE_MAX) { + sramCtx->readBuff[SRAM_HEADER_LANGUAGE] = gSaveContext.language = LANGUAGE_ENG; + } +} + +void FileChoose_Destroy(GameState* thisx) { +} + +void FileChoose_Init(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + size_t size = (u32)_title_staticSegmentRomEnd - (u32)_title_staticSegmentRomStart; + s32 pad; + + SREG(30) = 1; + osSyncPrintf("SIZE=%x\n", size); + + this->staticSegment = GameState_Alloc(&this->state, size, "../z_file_choose.c", 3392); + ASSERT(this->staticSegment != NULL, "this->staticSegment != NULL", "../z_file_choose.c", 3393); + DmaMgr_SendRequest1(this->staticSegment, (u32)_title_staticSegmentRomStart, size, "../z_file_choose.c", 3394); + + size = (u32)_parameter_staticSegmentRomEnd - (u32)_parameter_staticSegmentRomStart; + this->parameterSegment = GameState_Alloc(&this->state, size, "../z_file_choose.c", 3398); + ASSERT(this->parameterSegment != NULL, "this->parameterSegment != NULL", "../z_file_choose.c", 3399); + DmaMgr_SendRequest1(this->parameterSegment, (u32)_parameter_staticSegmentRomStart, size, "../z_file_choose.c", + 3400); + + Matrix_Init(&this->state); + View_Init(&this->view, this->state.gfxCtx); + this->state.main = FileChoose_Main; + this->state.destroy = FileChoose_Destroy; + FileChoose_InitContext(&this->state); + Font_LoadOrderedFont(&this->font); + Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); + func_800F5E18(SEQ_PLAYER_BGM_MAIN, NA_BGM_FILE_SELECT, 0, 7, 1); +} diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_copy_erase.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_copy_erase.c new file mode 100644 index 000000000..5d13c408d --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_copy_erase.c @@ -0,0 +1,1073 @@ +#include "file_choose.h" + +// when choosing a file to copy or erase, the 6 main menu buttons are placed at these offsets +static s16 sChooseFileYOffsets[] = { -48, -48, -48, -24, -24, 0 }; + +static s16 D_8081248C[3][3] = { + { 0, -48, -48 }, + { -64, 16, -48 }, + { -64, -64, 32 }, +}; + +static s16 sEraseDelayTimer = 15; + +/** + * Move buttons into place for the select source screen and fade in the proper labels. + * Update function for `CM_SETUP_COPY_SOURCE` + */ +void FileChoose_SetupCopySource(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 yStep; + s16 i; + + for (i = 0; i < 5; i++) { + yStep = (ABS(this->buttonYOffsets[i] - sChooseFileYOffsets[i])) / this->actionTimer; + + if (this->buttonYOffsets[i] >= sChooseFileYOffsets[i]) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] -= 25; + this->actionButtonAlpha[FS_BTN_ACTION_ERASE] -= 25; + this->optionButtonAlpha -= 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] += 25; + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->actionTimer = 8; + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->optionButtonAlpha = 0; + + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] = 200; + this->titleLabel = this->nextTitleLabel; + + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->buttonIndex = FS_BTN_COPY_QUIT; + this->configMode++; + } +} + +/** + * Allow the player to select a file to copy or exit back to the main menu. + * Update function for `CM_SELECT_COPY_SOURCE` + */ +void FileChoose_SelectCopySource(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + Input* input = &this->state.input[0]; + + if (((this->buttonIndex == FS_BTN_COPY_QUIT) && CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) || + CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->actionTimer = 8; + this->buttonIndex = FS_BTN_MAIN_COPY; + this->nextTitleLabel = FS_TITLE_SELECT_FILE; + this->configMode = CM_COPY_RETURN_MAIN; + this->warningLabel = FS_WARNING_NONE; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) { + if (SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + this->actionTimer = 8; + this->selectedFileIndex = this->buttonIndex; + this->configMode = CM_SETUP_COPY_DEST_1; + this->nextTitleLabel = FS_TITLE_COPY_TO; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + if (ABS(this->stickRelY) >= 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + if (this->stickRelY >= 30) { + this->buttonIndex--; + + if (this->buttonIndex < FS_BTN_COPY_FILE_1) { + this->buttonIndex = FS_BTN_COPY_QUIT; + } + } else { + this->buttonIndex++; + + if (this->buttonIndex > FS_BTN_COPY_QUIT) { + this->buttonIndex = FS_BTN_COPY_FILE_1; + } + } + } + + if (this->buttonIndex != FS_BTN_COPY_QUIT) { + if (!SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + this->warningLabel = FS_WARNING_FILE_EMPTY; + this->warningButtonIndex = this->buttonIndex; + this->emptyFileTextAlpha = 255; + } else { + this->warningLabel = FS_WARNING_NONE; + } + } + } +} + +/** + * Move the menu buttons into place for the copy destination selection and switch titles. + * Update function for `CM_SETUP_COPY_DEST_1` + */ +void FileChoose_SetupCopyDest1(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 yStep; + s16 i; + + for (i = 0; i < 3; i++) { + yStep = ABS(this->buttonYOffsets[i] - D_8081248C[this->buttonIndex][i]) / this->actionTimer; + + if (D_8081248C[this->buttonIndex][i] >= this->buttonYOffsets[i]) { + this->buttonYOffsets[i] += yStep; + } else { + this->buttonYOffsets[i] -= yStep; + } + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->nameBoxAlpha[this->buttonIndex] -= 25; + + this->actionTimer--; + if (this->actionTimer == 0) { + this->buttonYOffsets[this->buttonIndex] = D_8081248C[this->buttonIndex][this->buttonIndex]; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->actionTimer = 8; + this->configMode++; + } +} + +/** + * Show the file info of the file selected to copy from. + * Update function for `CM_SETUP_COPY_DEST_2` + */ +void FileChoose_SetupCopyDest2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->nameBoxAlpha[this->buttonIndex] -= 25; + this->fileInfoAlpha[this->buttonIndex] += 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->nameBoxAlpha[this->buttonIndex] = 0; + this->fileInfoAlpha[this->buttonIndex] = 200; + this->buttonIndex = FS_BTN_COPY_QUIT; + this->actionTimer = 8; + this->configMode = CM_SELECT_COPY_DEST; + } +} + +/** + * Allow the player to select a slot to copy to or exit to the copy select screen. + * Update function for `CM_SELECT_COPY_DEST` + */ +void FileChoose_SelectCopyDest(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + Input* input = &this->state.input[0]; + + if (((this->buttonIndex == FS_BTN_COPY_QUIT) && CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) || + CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->buttonIndex = this->selectedFileIndex; + this->nextTitleLabel = FS_TITLE_COPY_FROM; + this->actionTimer = 8; + this->configMode = CM_EXIT_TO_COPY_SOURCE_1; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) { + if (!SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + this->copyDestFileIndex = this->buttonIndex; + this->nextTitleLabel = FS_TITLE_COPY_CONFIRM; + this->actionTimer = 8; + this->configMode = CM_SETUP_COPY_CONFIRM_1; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + + if (ABS(this->stickRelY) >= 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + if (this->stickRelY >= 30) { + this->buttonIndex--; + + if ((this->buttonIndex == this->selectedFileIndex)) { + this->buttonIndex--; + + if (this->buttonIndex < FS_BTN_COPY_FILE_1) { + this->buttonIndex = FS_BTN_COPY_QUIT; + } + } else { + if (this->buttonIndex < FS_BTN_COPY_FILE_1) { + this->buttonIndex = FS_BTN_COPY_QUIT; + } + } + } else { + this->buttonIndex++; + + if (this->buttonIndex > FS_BTN_COPY_QUIT) { + this->buttonIndex = FS_BTN_COPY_FILE_1; + } + + if (this->buttonIndex == this->selectedFileIndex) { + this->buttonIndex++; + } + } + } + + if (this->buttonIndex != FS_BTN_COPY_QUIT) { + if (SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + this->warningLabel = FS_WARNING_FILE_IN_USE; + this->warningButtonIndex = this->buttonIndex; + this->emptyFileTextAlpha = 255; + } else { + this->warningLabel = FS_WARNING_NONE; + } + } + } +} + +/** + * Fade out file info, bring back the name box, and get ready to return to copy source screen. + * Update function for `CM_EXIT_TO_COPY_SOURCE_1` + */ +void FileChoose_ExitToCopySource1(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->fileInfoAlpha[this->buttonIndex] -= 25; + this->nameBoxAlpha[this->buttonIndex] += 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->nextTitleLabel = FS_TITLE_COPY_FROM; + this->nameBoxAlpha[this->buttonIndex] = 200; + this->fileInfoAlpha[this->buttonIndex] = 0; + this->actionTimer = 8; + this->configMode++; + } +} + +/** + * Move the buttons back into place and return to copy source select. + * Update function for `CM_EXIT_TO_COPY_SOURCE_2` + */ +void FileChoose_ExitToCopySource2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + for (i = 0; i < 3; i++) { + yStep = ABS(this->buttonYOffsets[i] - sChooseFileYOffsets[i]) / this->actionTimer; + + if (this->buttonYOffsets[i] >= sChooseFileYOffsets[i]) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->buttonIndex = 3; + this->configMode = CM_SELECT_COPY_SOURCE; + } +} + +/** + * Rearrange buttons on the screen to prepare for copy confirmation. + * Update function for `CM_SETUP_COPY_CONFIRM_1` + */ +void FileChoose_SetupCopyConfirm1(GameState* thisx) { + static s16 D_808124A4[] = { -56, -40, -24, 0 }; + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + + for (i = 0; i < 3; i++) { + if ((i != this->copyDestFileIndex) && (i != this->selectedFileIndex)) { + this->fileButtonAlpha[i] -= 25; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->connectorAlpha[i] -= 31; + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + } + } else { + if (this->copyDestFileIndex == i) { + yStep = ABS(this->buttonYOffsets[i] - D_808124A4[i]) / this->actionTimer; + this->buttonYOffsets[i] += yStep; + + if (this->buttonYOffsets[i] >= D_808124A4[i]) { + this->buttonYOffsets[i] = D_808124A4[i]; + } + } + } + } + + this->actionTimer--; + + if (this->actionTimer == 0) { + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->actionTimer = 8; + this->configMode++; + } +} + +/** + * Fade in the 'Yes' button before allowing the player to decide. + * Update function for `CM_SETUP_COPY_CONFIRM_2` + */ +void FileChoose_SetupCopyConfirm2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] += 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->configMode = CM_COPY_CONFIRM; + this->buttonIndex = FS_BTN_CONFIRM_QUIT; + } +} + +/** + * Allow the player to confirm the copy, or quit back to the destination select. + * If yes is selected, the actual copy occurs in this function before moving on to the animation. + * Update function for `CM_COPY_CONFIRM` + */ +void FileChoose_CopyConfirm(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + Input* input = &this->state.input[0]; + u16 dayTime; + + if (((this->buttonIndex != FS_BTN_CONFIRM_YES) && CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) || + CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->actionTimer = 8; + this->nextTitleLabel = FS_TITLE_COPY_TO; + this->configMode = CM_RETURN_TO_COPY_DEST; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) { + dayTime = gSaveContext.dayTime; + Sram_CopySave(this, sramCtx); + gSaveContext.dayTime = dayTime; + this->fileInfoAlpha[this->copyDestFileIndex] = this->nameAlpha[this->copyDestFileIndex] = 0; + this->nextTitleLabel = FS_TITLE_COPY_COMPLETE; + this->actionTimer = 8; + this->configMode = CM_COPY_ANIM_1; + func_800AA000(300.0f, 0xB4, 0x14, 0x64); + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (ABS(this->stickRelY) >= 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->buttonIndex ^= 1; + } +} + +/** + * Move buttons back in place and return to copy destination select. + * Update function for `CM_RETURN_TO_COPY_DEST` + */ +void FileChoose_ReturnToCopyDest(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] -= 25; + + for (i = 0; i < 3; i++) { + if ((i != this->copyDestFileIndex) && (i != this->selectedFileIndex)) { + this->fileButtonAlpha[i] += 25; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + this->connectorAlpha[i] += 31; + } + } + + yStep = ABS(this->buttonYOffsets[i] - D_8081248C[this->selectedFileIndex][i]) / this->actionTimer; + + if (D_8081248C[this->selectedFileIndex][i] >= this->buttonYOffsets[i]) { + this->buttonYOffsets[i] += yStep; + } else { + this->buttonYOffsets[i] -= yStep; + } + } + + this->actionTimer--; + + if (this->actionTimer == 0) { + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->actionTimer = 8; + this->buttonIndex = FS_BTN_COPY_QUIT; + this->configMode = CM_SELECT_COPY_DEST; + } +} + +/** + * Hide title + * Update function for `CM_COPY_ANIM_1` + */ +void FileChoose_CopyAnim1(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->titleAlpha[0] -= 31; + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] -= 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] -= 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->titleAlpha[0] = 0; + this->actionTimer = 8; + this->configMode++; + osSyncPrintf("connect_alpha=%d decision_alpha[%d]=%d\n", this->connectorAlpha[this->copyDestFileIndex], + this->copyDestFileIndex, this->fileInfoAlpha[this->copyDestFileIndex]); + } +} + +/** + * Move a copy of the file window down and fade in the file info. + * Update function for `CM_COPY_ANIM_2` + */ +void FileChoose_CopyAnim2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 yStep; + + this->fileInfoAlpha[this->copyDestFileIndex] += 25; + this->nameAlpha[this->copyDestFileIndex] += 25; + this->titleAlpha[1] += 31; + yStep = ABS(this->fileNamesY[this->copyDestFileIndex] + 56) / this->actionTimer; + this->fileNamesY[this->copyDestFileIndex] -= yStep; + + if (this->fileNamesY[this->copyDestFileIndex] <= -56) { + this->fileNamesY[this->copyDestFileIndex] = -56; + } + + this->actionTimer--; + + if (this->actionTimer == 0) { + this->actionTimer = 90; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->configMode++; + } +} + +/** + * Play sound to indicate that the copy has completed. Wait for a timer or for + * the player to press a button before moving on. + * Update function for `CM_COPY_ANIM_3` + */ +void FileChoose_CopyAnim3(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Input* input = &this->state.input[0]; + + if (this->actionTimer == 75) { + this->connectorAlpha[this->copyDestFileIndex] = 255; + Audio_PlaySoundGeneral(NA_SE_EV_DIAMOND_SWITCH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + this->actionTimer--; + + if (this->actionTimer < 74) { + if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_B | BTN_START) || (this->actionTimer == 0)) { + this->actionTimer = 8; + this->nextTitleLabel = FS_TITLE_SELECT_FILE; + this->configMode++; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } +} + +/** + * Fade out the info boxes for both files and bring in their name boxes. Fade out title. + * Update function for `CM_COPY_ANIM_4` + */ +void FileChoose_CopyAnim4(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->fileInfoAlpha[this->selectedFileIndex] -= 25; + this->fileInfoAlpha[this->copyDestFileIndex] -= 25; + this->nameBoxAlpha[this->selectedFileIndex] += 25; + this->nameBoxAlpha[this->copyDestFileIndex] += 25; + this->titleAlpha[0] -= 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->fileNamesY[this->copyDestFileIndex] = this->buttonYOffsets[3] = 0; + this->actionTimer = 8; + this->titleAlpha[0] = 0; + this->configMode++; + } +} + +/** + * Restore all buttons and labels back to their original place and go back to the main menu. + * Update function for `CM_COPY_ANIM_5` + */ +void FileChoose_CopyAnim5(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + for (i = 0; i < 5; i++) { + yStep = ABS(this->buttonYOffsets[i]) / this->actionTimer; + + if (this->buttonYOffsets[i] >= 0) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + for (i = 0; i < 3; i++) { + if (i != this->buttonIndex) { + this->fileButtonAlpha[i] += 25; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + this->connectorAlpha[i] += 31; + } + } + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] += 25; + this->actionButtonAlpha[FS_BTN_ACTION_ERASE] += 25; + this->optionButtonAlpha += 25; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + for (i = 0; i < 3; i++) { + this->connectorAlpha[i] = 0; + this->fileButtonAlpha[i] = 200; + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->connectorAlpha[i]; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->connectorAlpha[i] = 255; + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + } + } + + this->fileNamesY[this->selectedFileIndex] = 0; + this->highlightColor[3] = 70; + this->highlightPulseDir = 1; + XREG(35) = XREG(36); + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->configMode = CM_MAIN_MENU; + } +} + +/** + * Exit from the copy source screen to the main menu. Return all buttons and labels to their original place. + * Update function for `CM_COPY_RETURN_MAIN` + */ +void FileChoose_ExitCopyToMain(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 i; + s16 yStep; + + for (i = 0; i < 5; i++) { + yStep = ABS(this->buttonYOffsets[i]) / this->actionTimer; + + if (this->buttonYOffsets[i] >= 0) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] += 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] -= 25; + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = 200; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] = 0; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->configMode = CM_MAIN_MENU; + } + + this->optionButtonAlpha = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->actionButtonAlpha[FS_BTN_ACTION_COPY]; +} + +/** + * Move buttons into place for the erase select screen and fade in the proper labels. + * Update function for `CM_SETUP_ERASE_SELECT` + */ +void FileChoose_SetupEraseSelect(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 i; + s16 yStep; + + for (i = 0; i < 5; i++) { + yStep = ABS(this->buttonYOffsets[i] - sChooseFileYOffsets[i]) / this->actionTimer; + + if (this->buttonYOffsets[i] >= sChooseFileYOffsets[i]) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] -= 50; + this->actionButtonAlpha[FS_BTN_ACTION_ERASE] -= 50; + this->optionButtonAlpha -= 50; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] += 25; + + if (this->actionButtonAlpha[FS_BTN_ACTION_COPY] <= 0) { + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->optionButtonAlpha = 0; + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->highlightColor[3] = 70; + this->highlightPulseDir = 1; + XREG(35) = XREG(36); + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->optionButtonAlpha = 0; + this->confirmButtonAlpha[1] = 200; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->buttonIndex = FS_BTN_ERASE_QUIT; + this->configMode++; + } +} + +/** + * Allow the player to select a file to erase or exit back to the main menu. + * Update function for `CM_ERASE_SELECT` + */ +void FileChoose_EraseSelect(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + Input* input = &this->state.input[0]; + + if (((this->buttonIndex == FS_BTN_COPY_QUIT) && CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) || + CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->buttonIndex = FS_BTN_MAIN_ERASE; + this->actionTimer = 8; + this->nextTitleLabel = FS_TITLE_SELECT_FILE; + this->configMode = CM_EXIT_ERASE_TO_MAIN; + this->warningLabel = FS_WARNING_NONE; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) { + if (SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + this->actionTimer = 8; + this->selectedFileIndex = this->buttonIndex; + this->configMode = CM_SETUP_ERASE_CONFIRM_1; + this->nextTitleLabel = FS_TITLE_ERASE_CONFIRM; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + if (ABS(this->stickRelY) >= 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + if (this->stickRelY >= 30) { + this->buttonIndex--; + if (this->buttonIndex < FS_BTN_ERASE_FILE_1) { + this->buttonIndex = FS_BTN_ERASE_QUIT; + } + } else { + this->buttonIndex++; + if (this->buttonIndex > FS_BTN_ERASE_QUIT) { + this->buttonIndex = FS_BTN_ERASE_FILE_1; + } + } + } + + if (this->buttonIndex != FS_BTN_ERASE_QUIT) { + if (!SLOT_OCCUPIED(sramCtx, this->buttonIndex)) { + this->warningLabel = FS_WARNING_FILE_EMPTY; + this->warningButtonIndex = this->buttonIndex; + this->emptyFileTextAlpha = 255; + } else { + this->warningLabel = FS_WARNING_NONE; + } + } else { + this->warningLabel = FS_WARNING_NONE; + } + } +} + +/** + * ... + * Update function for `CM_SETUP_ERASE_CONFIRM_1` + */ +void FileChoose_SetupEraseConfirm1(GameState* thisx) { + static s16 D_808124AC[] = { 0, 16, 32 }; + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + for (i = 0; i < 3; i++) { + if (i != this->buttonIndex) { + this->fileButtonAlpha[i] -= 25; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->connectorAlpha[i] -= 31; + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + } + } else { + this->nameBoxAlpha[i] -= 25; + } + } + this->titleAlpha[0] -= 15; + this->titleAlpha[1] += 15; + + yStep = ABS(this->buttonYOffsets[this->buttonIndex] - D_808124AC[this->buttonIndex]) / this->actionTimer; + + if (this->buttonYOffsets[this->buttonIndex] >= D_808124AC[this->buttonIndex]) { + this->buttonYOffsets[this->buttonIndex] -= yStep; + } else { + this->buttonYOffsets[this->buttonIndex] += yStep; + } + + this->actionTimer--; + + if (this->actionTimer == 0) { + for (i = 0; i < 3; i++) { + if (i != this->buttonIndex) { + this->fileButtonAlpha[i] = 0; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->connectorAlpha[i] = 0; + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i] = 0; + } + } else { + this->nameBoxAlpha[i] = 0; + } + } + + this->buttonYOffsets[this->buttonIndex] = D_808124AC[this->buttonIndex]; + this->actionTimer = 8; + this->configMode++; + } +} + +/** + * Show the file info of the file selected to erase. + * Update function for `CM_SETUP_ERASE_CONFIRM_2` + */ +void FileChoose_SetupEraseConfirm2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] += 25; + this->titleAlpha[0] -= 15; + this->titleAlpha[1] += 15; + this->fileInfoAlpha[this->buttonIndex] += 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->actionTimer = 8; + this->titleLabel = this->nextTitleLabel; + this->fileInfoAlpha[this->buttonIndex] = this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] = 200; + this->buttonIndex = FS_BTN_ERASE_FILE_2; + this->configMode = CM_ERASE_CONFIRM; + } +} + +/** + * Allow the player to confirm their chioce to erase or return back to erase select. + * Update function for `CM_ERASE_CONFIRM` + */ +void FileChoose_EraseConfirm(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Input* input = &this->state.input[0]; + + if (((this->buttonIndex != FS_BTN_CONFIRM_YES) && CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) || + CHECK_BTN_ALL(input->press.button, BTN_B)) { + this->buttonIndex = this->selectedFileIndex; + this->nextTitleLabel = FS_TITLE_ERASE_FILE; + this->configMode = CM_EXIT_TO_ERASE_SELECT_1; + this->actionTimer = 8; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_START)) { + this->n64ddFlags[this->selectedFileIndex] = this->connectorAlpha[this->selectedFileIndex] = 0; + Audio_PlaySoundGeneral(NA_SE_EV_DIAMOND_SWITCH, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->actionTimer = 8; + this->configMode = CM_ERASE_ANIM_1; + this->nextTitleLabel = FS_TITLE_ERASE_COMPLETE; + func_800AA000(200.0f, 0xFF, 0x14, 0x96); + sEraseDelayTimer = 15; + } else if (ABS(this->stickRelY) >= 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->buttonIndex ^= 1; + } +} + +/** + * Fade out file info, bring back the name box, and get ready to return to erase select screen. + * Update function for `CM_EXIT_TO_ERASE_SELECT_1` + */ +void FileChoose_ExitToEraseSelect1(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->fileInfoAlpha[this->buttonIndex] -= 25; + this->nameBoxAlpha[this->buttonIndex] += 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] -= 25; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->actionTimer = 8; + this->fileInfoAlpha[this->buttonIndex] = 0; + this->configMode++; + } +} + +/** + * Move the buttons back into place and return to erase select. + * Update function for `CM_EXIT_TO_ERASE_SELECT_2` + */ +void FileChoose_ExitToEraseSelect2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + yStep = ABS(this->buttonYOffsets[this->buttonIndex] - sChooseFileYOffsets[this->buttonIndex]) / this->actionTimer; + + if (this->buttonYOffsets[this->buttonIndex] >= sChooseFileYOffsets[this->buttonIndex]) { + this->buttonYOffsets[this->buttonIndex] -= yStep; + } else { + this->buttonYOffsets[this->buttonIndex] += yStep; + } + + for (i = 0; i < 3; i++) { + if (i != this->buttonIndex) { + this->fileButtonAlpha[i] += 25; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + this->connectorAlpha[i] += 31; + } + } + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->buttonYOffsets[this->buttonIndex] = sChooseFileYOffsets[this->buttonIndex]; + this->actionTimer = 8; + this->buttonIndex = FS_BTN_ERASE_QUIT; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->configMode = CM_ERASE_SELECT; + } +} + +/** + * Wait for an initial delay, then start fading out the selected file. + * The actual file deletion occurs in this function + * Update function for `CM_ERASE_ANIM_1` + */ +void FileChoose_EraseAnim1(GameState* thisx) { + static s16 D_80813800; + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + + if (sEraseDelayTimer == 0) { + if (this->actionTimer == 8) { + D_80813800 = 1; + } + + if (this->actionTimer != 0) { + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->fileInfoAlpha[this->selectedFileIndex] -= 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_YES] -= 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] -= 25; + } + + this->fileNamesY[this->selectedFileIndex] -= D_80813800; + this->actionTimer--; + D_80813800 += 2; + + if (this->actionTimer == 0) { + Sram_EraseSave(this, sramCtx); + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = this->connectorAlpha[this->selectedFileIndex] = 0; + + // probably a fake match, there should be a better chained assignment + this->confirmButtonAlpha[0] = this->confirmButtonAlpha[1] = 0; + if (1) {} + this->fileInfoAlpha[this->selectedFileIndex] = this->nameBoxAlpha[this->selectedFileIndex] = + this->confirmButtonAlpha[1]; + + this->configMode++; + this->actionTimer = 90; + } + } else { + sEraseDelayTimer--; + + if (sEraseDelayTimer == 0) { + Audio_PlaySoundGeneral(NA_SE_OC_ABYSS, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } +} + +/** + * Wait for a delay timer or for the palyer to press a button before returning to the main menu. + * Update function for `CM_ERASE_ANIM_2` + */ +void FileChoose_EraseAnim2(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Input* input = &this->state.input[0]; + + if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_B | BTN_START) || (--this->actionTimer == 0)) { + this->buttonYOffsets[3] = 0; + this->actionTimer = 8; + this->nextTitleLabel = FS_TITLE_SELECT_FILE; + this->configMode++; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CLOSE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } +} + +/** + * Exit from the erase animation to the main menu. Return all buttons and labels to their original place. + * Update function for `CM_ERASE_ANIM_3` + */ +void FileChoose_EraseAnim3(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + s16 i; + s16 yStep; + + for (i = 0; i < 5; i++) { + yStep = ABS(this->buttonYOffsets[i]) / this->actionTimer; + + if (this->buttonYOffsets[i] >= 0) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + for (i = 0; i < 3; i++) { + this->fileButtonAlpha[i] += 25; + + if (SLOT_OCCUPIED(sramCtx, i)) { + this->nameBoxAlpha[i] = this->nameAlpha[i] = this->fileButtonAlpha[i]; + this->connectorAlpha[i] += 31; + } + } + + if (this->fileButtonAlpha[this->selectedFileIndex] >= 200) { + this->fileButtonAlpha[this->selectedFileIndex] = 200; + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->fileNamesY[this->selectedFileIndex] = 0; + this->highlightColor[3] = 70; + this->highlightPulseDir = 1; + XREG(35) = XREG(36); + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = 200; + this->confirmButtonAlpha[0] = this->confirmButtonAlpha[1] = 0; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->configMode = CM_MAIN_MENU; + } + + this->optionButtonAlpha = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->actionButtonAlpha[FS_BTN_ACTION_COPY]; +} + +/** + * Exit from the erase select screen to the main menu. Return all buttons and labels to their original place. + * Update function for `CM_EXIT_ERASE_TO_MAIN` + */ +void FileChoose_ExitEraseToMain(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 i; + s16 yStep; + + for (i = 0; i < 5; i++) { + yStep = ABS(this->buttonYOffsets[i]) / this->actionTimer; + + if (this->buttonYOffsets[i] >= 0) { + this->buttonYOffsets[i] -= yStep; + } else { + this->buttonYOffsets[i] += yStep; + } + } + + this->actionButtonAlpha[FS_BTN_ACTION_COPY] += 25; + this->actionButtonAlpha[FS_BTN_ACTION_ERASE] += 25; + this->optionButtonAlpha += 25; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] -= 50; + + if (this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] <= 0) { + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] = 0; + } + + this->titleAlpha[0] -= 31; + this->titleAlpha[1] += 31; + this->actionTimer--; + + if (this->actionTimer == 0) { + this->highlightColor[3] = 70; + this->highlightPulseDir = 1; + XREG(35) = XREG(36); + this->actionButtonAlpha[FS_BTN_ACTION_COPY] = 200; + this->confirmButtonAlpha[FS_BTN_CONFIRM_QUIT] = 0; + this->titleLabel = this->nextTitleLabel; + this->titleAlpha[0] = 255; + this->titleAlpha[1] = 0; + this->configMode = CM_MAIN_MENU; + } + + this->optionButtonAlpha = this->actionButtonAlpha[FS_BTN_ACTION_ERASE] = + this->actionButtonAlpha[FS_BTN_ACTION_COPY]; +} diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c new file mode 100644 index 000000000..73aa5e6d1 --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_PAL.c @@ -0,0 +1,994 @@ +#include "file_choose.h" +#include "textures/title_static/title_static.h" +#include "assets/overlays/ovl_File_Choose/ovl_file_choose.h" + +static s16 D_808124C0[] = { + 0x0002, 0x0003, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0002, 0x0001, 0x0001, 0x0004, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, + 0x0002, 0x0002, 0x0004, 0x0003, 0x0002, 0x0004, 0x0001, 0x0002, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, + 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, +}; + +static s16 D_80812544[] = { + 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0004, 0x0002, 0x0002, 0x0002, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0002, 0x0002, 0x0004, 0x0003, 0x0002, 0x0004, 0x0001, + 0x0002, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, + 0x0003, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0001, 0x0003, +}; + +void FileChoose_DrawCharacter(GraphicsContext* gfxCtx, void* texture, s16 vtx) { + OPEN_DISPS(gfxCtx, "../z_file_nameset_PAL.c", 110); + + gDPLoadTextureBlock_4b(POLY_OPA_DISP++, texture, G_IM_FMT_I, 16, 16, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + + CLOSE_DISPS(gfxCtx, "../z_file_nameset_PAL.c", 119); +} + +void FileChoose_SetKeyboardVtx(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 val; + s16 phi_t2; + s16 phi_t0; + s16 phi_t3; + s16 phi_s1; + s16 phi_t1; + s16 phi_s2; + + this->keyboardVtx = Graph_Alloc(this->state.gfxCtx, sizeof(Vtx) * 4 * 5 * 13); + + phi_s1 = 0x26; + + for (phi_t2 = 0, phi_s2 = 0, phi_t3 = 0; phi_s2 < 5; phi_s2++) { + phi_t0 = -0x60; + + for (phi_t1 = 0; phi_t1 < 13; phi_t1++, phi_t3 += 4, phi_t2++) { + this->keyboardVtx[phi_t3].v.ob[0] = this->keyboardVtx[phi_t3 + 2].v.ob[0] = D_80812544[phi_t2] + phi_t0; + + this->keyboardVtx[phi_t3 + 1].v.ob[0] = this->keyboardVtx[phi_t3 + 3].v.ob[0] = + D_80812544[phi_t2] + phi_t0 + 12; + + this->keyboardVtx[phi_t3].v.ob[1] = this->keyboardVtx[phi_t3 + 1].v.ob[1] = phi_s1; + + this->keyboardVtx[phi_t3 + 2].v.ob[1] = this->keyboardVtx[phi_t3 + 3].v.ob[1] = phi_s1 - 12; + + this->keyboardVtx[phi_t3].v.ob[2] = this->keyboardVtx[phi_t3 + 1].v.ob[2] = + this->keyboardVtx[phi_t3 + 2].v.ob[2] = this->keyboardVtx[phi_t3 + 3].v.ob[2] = 0; + + this->keyboardVtx[phi_t3].v.flag = this->keyboardVtx[phi_t3 + 1].v.flag = + this->keyboardVtx[phi_t3 + 2].v.flag = this->keyboardVtx[phi_t3 + 3].v.flag = 0; + + this->keyboardVtx[phi_t3].v.tc[0] = this->keyboardVtx[phi_t3].v.tc[1] = + this->keyboardVtx[phi_t3 + 1].v.tc[1] = this->keyboardVtx[phi_t3 + 2].v.tc[0] = 0; + + this->keyboardVtx[phi_t3 + 1].v.tc[0] = this->keyboardVtx[phi_t3 + 2].v.tc[1] = + this->keyboardVtx[phi_t3 + 3].v.tc[0] = this->keyboardVtx[phi_t3 + 3].v.tc[1] = 0x200; + + this->keyboardVtx[phi_t3].v.cn[0] = this->keyboardVtx[phi_t3 + 1].v.cn[0] = + this->keyboardVtx[phi_t3 + 2].v.cn[0] = this->keyboardVtx[phi_t3 + 3].v.cn[0] = + this->keyboardVtx[phi_t3].v.cn[1] = this->keyboardVtx[phi_t3 + 1].v.cn[1] = + this->keyboardVtx[phi_t3 + 2].v.cn[1] = this->keyboardVtx[phi_t3 + 3].v.cn[1] = + this->keyboardVtx[phi_t3].v.cn[2] = this->keyboardVtx[phi_t3 + 1].v.cn[2] = + this->keyboardVtx[phi_t3 + 2].v.cn[2] = this->keyboardVtx[phi_t3 + 3].v.cn[2] = + this->keyboardVtx[phi_t3].v.cn[3] = this->keyboardVtx[phi_t3 + 1].v.cn[3] = + this->keyboardVtx[phi_t3 + 2].v.cn[3] = this->keyboardVtx[phi_t3 + 3].v.cn[3] = + 255; + + phi_t0 += 0x10; + } + + phi_s1 -= 0x10; + } +} + +static void* sNameLabelTextures[] = { gFileSelNameENGTex, gFileSelNameENGTex, gFileSelNameFRATex }; + +static void* sBackspaceEndTextures[][2] = { + { gFileSelBackspaceButtonTex, gFileSelENDButtonENGTex }, + { gFileSelBackspaceButtonTex, gFileSelENDButtonGERTex }, + { gFileSelBackspaceButtonTex, gFileSelENDButtonFRATex }, +}; + +static u16 sBackspaceEndWidths[] = { 28, 44 }; + +static s16 D_808125EC[] = { + 0xFFE2, 0xFFF0, 0xFFFA, 0x0004, 0x000E, 0x0018, 0x0022, 0x002C, 0x0036, 0xFFF0, 0xFFF0, +}; + +static s16 D_80812604[] = { + 0x0048, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, 0x0045, +}; + +/** + * Set vertices used by all elements of the name entry screen that are NOT the keyboard. + * This includes the cursor highlight, the name entry plate and characters, and the buttons. + */ +void FileChoose_SetNameEntryVtx(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Font* font = &this->font; + s16 phi_s0; + s16 phi_t1; + u8 temp; + s16 phi_v0; + + if (1) {} + if (1) {} + + OPEN_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 205); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gSPVertex(POLY_OPA_DISP++, D_80811BB0, 24, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, sNameLabelTextures[gSaveContext.language], G_IM_FMT_IA, G_IM_SIZ_8b, 56, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + gDPPipeSync(POLY_OPA_DISP++); + + phi_s0 = 0x10; + for (phi_t1 = 0; phi_t1 < 2; phi_t1++, phi_s0 += 4) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], 255); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, sBackspaceEndTextures[gSaveContext.language][phi_t1], G_IM_FMT_IA, + G_IM_SIZ_16b, sBackspaceEndWidths[phi_t1], 16, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, phi_s0, phi_s0 + 2, phi_s0 + 3, phi_s0 + 1, 0); + } + + this->nameEntryVtx = Graph_Alloc(this->state.gfxCtx, 44 * sizeof(Vtx)); + + for (phi_s0 = 0, phi_t1 = 0; phi_t1 < 44; phi_t1 += 4, phi_s0++) { + if ((phi_s0 > 0) && (phi_s0 < 9)) { + temp = this->fileNames[this->buttonIndex][phi_s0 - 1]; + + this->nameEntryVtx[phi_t1].v.ob[0] = this->nameEntryVtx[phi_t1 + 2].v.ob[0] = + D_808125EC[phi_s0] + this->nameEntryBoxPosX + D_808124C0[temp]; + + this->nameEntryVtx[phi_t1 + 1].v.ob[0] = this->nameEntryVtx[phi_t1 + 3].v.ob[0] = + this->nameEntryVtx[phi_t1].v.ob[0] + 0xA; + } else { + this->nameEntryVtx[phi_t1].v.ob[0] = this->nameEntryVtx[phi_t1 + 2].v.ob[0] = + D_808125EC[phi_s0] + this->nameEntryBoxPosX; + + this->nameEntryVtx[phi_t1 + 1].v.ob[0] = this->nameEntryVtx[phi_t1 + 3].v.ob[0] = + this->nameEntryVtx[phi_t1].v.ob[0] + 0xA; + } + + this->nameEntryVtx[phi_t1].v.ob[1] = this->nameEntryVtx[phi_t1 + 1].v.ob[1] = D_80812604[phi_s0]; + + this->nameEntryVtx[phi_t1 + 2].v.ob[1] = this->nameEntryVtx[phi_t1 + 3].v.ob[1] = + this->nameEntryVtx[phi_t1].v.ob[1] - 0xA; + + this->nameEntryVtx[phi_t1].v.ob[2] = this->nameEntryVtx[phi_t1 + 1].v.ob[2] = + this->nameEntryVtx[phi_t1 + 2].v.ob[2] = this->nameEntryVtx[phi_t1 + 3].v.ob[2] = 0; + + this->nameEntryVtx[phi_t1].v.flag = this->nameEntryVtx[phi_t1 + 1].v.flag = + this->nameEntryVtx[phi_t1 + 2].v.flag = this->nameEntryVtx[phi_t1 + 3].v.flag = 0; + + this->nameEntryVtx[phi_t1].v.tc[0] = this->nameEntryVtx[phi_t1].v.tc[1] = + this->nameEntryVtx[phi_t1 + 1].v.tc[1] = this->nameEntryVtx[phi_t1 + 2].v.tc[0] = 0; + + this->nameEntryVtx[phi_t1 + 1].v.tc[0] = this->nameEntryVtx[phi_t1 + 2].v.tc[1] = + this->nameEntryVtx[phi_t1 + 3].v.tc[0] = this->nameEntryVtx[phi_t1 + 3].v.tc[1] = 0x200; + + this->nameEntryVtx[phi_t1].v.cn[0] = this->nameEntryVtx[phi_t1 + 1].v.cn[0] = + this->nameEntryVtx[phi_t1 + 2].v.cn[0] = this->nameEntryVtx[phi_t1 + 3].v.cn[0] = + this->nameEntryVtx[phi_t1].v.cn[1] = this->nameEntryVtx[phi_t1 + 1].v.cn[1] = + this->nameEntryVtx[phi_t1 + 2].v.cn[1] = this->nameEntryVtx[phi_t1 + 3].v.cn[1] = + this->nameEntryVtx[phi_t1].v.cn[2] = this->nameEntryVtx[phi_t1 + 1].v.cn[2] = + this->nameEntryVtx[phi_t1 + 2].v.cn[2] = this->nameEntryVtx[phi_t1 + 3].v.cn[2] = + this->nameEntryVtx[phi_t1].v.cn[3] = this->nameEntryVtx[phi_t1 + 1].v.cn[3] = + this->nameEntryVtx[phi_t1 + 2].v.cn[3] = this->nameEntryVtx[phi_t1 + 3].v.cn[3] = + 0xFF; + } + + this->nameEntryVtx[1].v.ob[0] = this->nameEntryVtx[3].v.ob[0] = this->nameEntryVtx[0].v.ob[0] + 0x6C; + this->nameEntryVtx[2].v.ob[1] = this->nameEntryVtx[3].v.ob[1] = this->nameEntryVtx[0].v.ob[1] - 0x10; + this->nameEntryVtx[1].v.tc[0] = this->nameEntryVtx[3].v.tc[0] = 0xD80; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->windowColor[0], this->windowColor[1], this->windowColor[2], + this->nameEntryBoxAlpha); + gSPVertex(POLY_OPA_DISP++, this->nameEntryVtx, 4, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelNameBoxTex, G_IM_FMT_IA, G_IM_SIZ_16b, 108, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + gSPVertex(POLY_OPA_DISP++, this->nameEntryVtx + 4, 32, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->nameEntryBoxAlpha); + + for (phi_v0 = 0, phi_s0 = 0; phi_s0 < 0x20; phi_s0 += 4, phi_v0++) { + FileChoose_DrawCharacter(this->state.gfxCtx, + font->fontBuf + this->fileNames[this->buttonIndex][phi_v0] * FONT_CHAR_TEX_SIZE, + phi_s0); + } + + this->nameEntryVtx[0x25].v.tc[0] = this->nameEntryVtx[0x26].v.tc[1] = this->nameEntryVtx[0x27].v.tc[0] = + this->nameEntryVtx[0x27].v.tc[1] = this->nameEntryVtx[0x29].v.tc[0] = this->nameEntryVtx[0x2A].v.tc[1] = + this->nameEntryVtx[0x2B].v.tc[0] = this->nameEntryVtx[0x2B].v.tc[1] = 0x300; + + if ((this->kbdButton == 0) || (this->kbdButton == 1) || (this->kbdButton == 4)) { + this->nameEntryVtx[0x29].v.tc[0] = this->nameEntryVtx[0x2B].v.tc[0] = 0x700; + } else if ((this->kbdButton == 2) || (this->kbdButton == 3)) { + this->nameEntryVtx[0x29].v.tc[0] = this->nameEntryVtx[0x2B].v.tc[0] = 0x500; + } + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 307); +} + +void FileChoose_DrawKeyboard(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Font* font = &this->font; + s16 i = 0; + s16 tmp; + s16 vtx = 0; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 324); + + func_800949A8(this->state.gfxCtx); + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_2CYCLE); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_PASS, G_RM_XLU_SURF2); + gDPSetCombineLERP(POLY_OPA_DISP++, 0, 0, 0, PRIMITIVE, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, 0, 0, 0, COMBINED, 0, + 0, 0, COMBINED); + gDPSetPrimColor(POLY_OPA_DISP++, 0, this->charBgAlpha, 255, 255, 255, 255); + + while (vtx < 0x100) { + gSPVertex(POLY_OPA_DISP++, &this->keyboardVtx[vtx], 32, 0); + + for (tmp = 0; tmp < 32; i++, tmp += 4) { + FileChoose_DrawCharacter(this->state.gfxCtx, font->fontBuf + D_808123F0[i] * FONT_CHAR_TEX_SIZE, tmp); + } + + vtx += 32; + } + + gSPVertex(POLY_OPA_DISP++, &this->keyboardVtx[0x100], 4, 0); + FileChoose_DrawCharacter(this->state.gfxCtx, font->fontBuf + D_808123F0[i] * FONT_CHAR_TEX_SIZE, 0); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 347); +} + +void FileChoose_DrawNameEntry(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + Font* font = &this->font; + Input* input = &this->state.input[0]; + s16 i; + s16 tmp; + u16 dayTime; + s16 validName; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 368); + + FileChoose_SetKeyboardVtx(&this->state); + FileChoose_SetNameEntryVtx(&this->state); + FileChoose_PulsateCursor(&this->state); + + tmp = (this->newFileNameCharCount * 4) + 4; + this->nameEntryVtx[36].v.ob[0] = this->nameEntryVtx[38].v.ob[0] = this->nameEntryVtx[tmp].v.ob[0] - 6; + this->nameEntryVtx[37].v.ob[0] = this->nameEntryVtx[39].v.ob[0] = this->nameEntryVtx[36].v.ob[0] + 24; + this->nameEntryVtx[36].v.ob[1] = this->nameEntryVtx[37].v.ob[1] = this->nameEntryVtx[tmp].v.ob[1] + 7; + this->nameEntryVtx[38].v.ob[1] = this->nameEntryVtx[39].v.ob[1] = this->nameEntryVtx[36].v.ob[1] - 24; + + Vtx* vertices = ResourceMgr_LoadVtxByName(D_80811BB0); + + if ((this->kbdButton == FS_KBD_BTN_HIRA) || (this->kbdButton == FS_KBD_BTN_KATA) || + (this->kbdButton == FS_KBD_BTN_END)) { + if (this->kbdX != this->kbdButton) { + osSyncPrintf("014 xpos=%d contents=%d\n", this->kbdX, this->kbdButton); + } + + this->nameEntryVtx[40].v.ob[0] = this->nameEntryVtx[42].v.ob[0] = vertices[(this->kbdX + 1) * 4].v.ob[0] - 4; + this->nameEntryVtx[41].v.ob[0] = this->nameEntryVtx[43].v.ob[0] = this->nameEntryVtx[40].v.ob[0] + 52; + this->nameEntryVtx[40].v.ob[1] = this->nameEntryVtx[41].v.ob[1] = vertices[(this->kbdX + 1) * 4].v.ob[1] + 4; + + } else if ((this->kbdButton == FS_KBD_BTN_ENG) || (this->kbdButton == FS_KBD_BTN_BACKSPACE)) { + if (this->kbdX != this->kbdButton) { + osSyncPrintf("23 xpos=%d contents=%d\n", this->kbdX, this->kbdButton); + } + this->nameEntryVtx[40].v.ob[0] = this->nameEntryVtx[42].v.ob[0] = vertices[(this->kbdX + 1) * 4].v.ob[0] - 4; + this->nameEntryVtx[41].v.ob[0] = this->nameEntryVtx[43].v.ob[0] = this->nameEntryVtx[40].v.ob[0] + 40; + this->nameEntryVtx[40].v.ob[1] = this->nameEntryVtx[41].v.ob[1] = vertices[(this->kbdX + 1) * 4].v.ob[1] + 4; + } else { + if (this->charIndex >= 65) { + osSyncPrintf("mjp=%d xpos=%d ypos=%d name_contents=%d\n", this->charIndex, this->kbdX, this->kbdY, + this->kbdButton); + } + + this->nameEntryVtx[40].v.ob[0] = this->nameEntryVtx[42].v.ob[0] = + this->keyboardVtx[this->charIndex * 4].v.ob[0] - D_80812544[this->charIndex] - 6; + this->nameEntryVtx[41].v.ob[0] = this->nameEntryVtx[43].v.ob[0] = this->nameEntryVtx[40].v.ob[0] + 24; + this->nameEntryVtx[40].v.ob[1] = this->nameEntryVtx[41].v.ob[1] = + this->keyboardVtx[this->charIndex * 4].v.ob[1] + 6; + } + + this->nameEntryVtx[42].v.ob[1] = this->nameEntryVtx[43].v.ob[1] = this->nameEntryVtx[40].v.ob[1] - 24; + + gSPVertex(POLY_OPA_DISP++, &this->nameEntryVtx[36], 8, 0); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, this->highlightColor[0], this->highlightColor[1], this->highlightColor[2], + this->highlightColor[3]); + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelCharHighlightTex, G_IM_FMT_I, G_IM_SIZ_8b, 24, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + if ((this->kbdButton == FS_KBD_BTN_HIRA) || (this->kbdButton == FS_KBD_BTN_KATA) || + (this->kbdButton == FS_KBD_BTN_END)) { + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelMediumButtonHighlightTex, G_IM_FMT_I, G_IM_SIZ_8b, 56, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + } else if ((this->kbdButton == FS_KBD_BTN_ENG) || (this->kbdButton == FS_KBD_BTN_BACKSPACE)) { + gDPLoadTextureBlock(POLY_OPA_DISP++, gFileSelSmallButtonHighlightTex, G_IM_FMT_I, G_IM_SIZ_8b, 40, 24, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + } + + gSP1Quadrangle(POLY_OPA_DISP++, 4, 6, 7, 5, 0); + + FileChoose_DrawKeyboard(&this->state); + gDPPipeSync(POLY_OPA_DISP++); + func_800949A8(this->state.gfxCtx); + + gDPSetCombineLERP(POLY_OPA_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + + if (this->configMode == CM_NAME_ENTRY) { + if (CHECK_BTN_ALL(input->press.button, BTN_START)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + // place cursor on END button + this->kbdY = 5; + this->kbdX = 4; + } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + if ((this->newFileNameCharCount == 7) && (this->fileNames[this->buttonIndex][7] != 0x3E)) { + for (i = this->newFileNameCharCount; i < 7; i++) { + this->fileNames[this->buttonIndex][i] = this->fileNames[this->buttonIndex][i + 1]; + } + + this->fileNames[this->buttonIndex][i] = 0x3E; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_S, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } else { + this->newFileNameCharCount--; + + if (this->newFileNameCharCount < 0) { + this->newFileNameCharCount = 0; + this->configMode = CM_NAME_ENTRY_TO_MAIN; + } else { + for (i = this->newFileNameCharCount; i < 7; i++) { + this->fileNames[this->buttonIndex][i] = this->fileNames[this->buttonIndex][i + 1]; + } + + this->fileNames[this->buttonIndex][i] = 0x3E; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_S, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } else { + if (this->charPage <= FS_CHAR_PAGE_ENG) { + if (this->kbdY != 5) { + // draw the character the cursor is hovering over in yellow + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 0, 255); + gSPVertex(POLY_OPA_DISP++, &this->keyboardVtx[this->charIndex * 4], 4, 0); + + FileChoose_DrawCharacter(this->state.gfxCtx, + font->fontBuf + D_808123F0[this->charIndex] * FONT_CHAR_TEX_SIZE, 0); + + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_S, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + this->fileNames[this->buttonIndex][this->newFileNameCharCount] = D_808123F0[this->charIndex]; + this->newFileNameCharCount++; + + if (this->newFileNameCharCount > 7) { + this->newFileNameCharCount = 7; + } + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_A) && (this->charPage != this->kbdButton)) { + if (this->kbdButton == FS_KBD_BTN_BACKSPACE) { + if ((this->newFileNameCharCount == 7) && (this->fileNames[this->buttonIndex][7] != 0x3E)) { + for (i = this->newFileNameCharCount; i < 7; i++) { + this->fileNames[this->buttonIndex][i] = this->fileNames[this->buttonIndex][i + 1]; + } + + this->fileNames[this->buttonIndex][i] = 0x3E; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_S, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } else { + this->newFileNameCharCount--; + + if (this->newFileNameCharCount < 0) { + this->newFileNameCharCount = 0; + } + + for (i = this->newFileNameCharCount; i < 7; i++) { + this->fileNames[this->buttonIndex][i] = this->fileNames[this->buttonIndex][i + 1]; + } + + this->fileNames[this->buttonIndex][i] = 0x3E; + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_S, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } else if (this->kbdButton == FS_KBD_BTN_END) { + validName = false; + + for (i = 0; i < 8; i++) { + if (this->fileNames[this->buttonIndex][i] != 0x3E) { + validName = true; + break; + } + } + + if (validName) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + gSaveContext.fileNum = this->buttonIndex; + dayTime = ((void)0, gSaveContext.dayTime); + Sram_InitSave(this, &this->sramCtx); + gSaveContext.dayTime = dayTime; + this->configMode = CM_NAME_ENTRY_TO_MAIN; + this->nameBoxAlpha[this->buttonIndex] = this->nameAlpha[this->buttonIndex] = 200; + this->connectorAlpha[this->buttonIndex] = 255; + func_800AA000(300.0f, 0xB4, 0x14, 0x64); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } + + if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->newFileNameCharCount++; + + if (this->newFileNameCharCount > 7) { + this->newFileNameCharCount = 7; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->newFileNameCharCount--; + + if (this->newFileNameCharCount < 0) { + this->newFileNameCharCount = 0; + } + } + } + } + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineMode(POLY_OPA_DISP++, G_CC_MODULATEIDECALA, G_CC_MODULATEIDECALA); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 550); +} + +/** + * Fade in the name entry box and slide it to the center of the screen from the right side. + * After the name entry box is in place, init the keyboard/cursor and change modes. + * Update function for `CM_START_NAME_ENTRY` + */ +void FileChoose_StartNameEntry(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->nameEntryBoxAlpha += 25; + + if (this->nameEntryBoxAlpha >= 255) { + this->nameEntryBoxAlpha = 255; + } + + this->nameEntryBoxPosX -= 30; + + if (this->nameEntryBoxPosX <= 0) { + this->nameEntryBoxPosX = 0; + this->nameEntryBoxAlpha = 255; + this->kbdX = 0; + this->kbdY = 0; + this->kbdButton = 99; + this->configMode = CM_NAME_ENTRY; + } +} + +/** + * Update the keyboard cursor and play sounds at the appropriate times. + * There are many special cases for warping the cursor depending on where + * the cursor currently is. + * Update function for `CM_NAME_ENTRY` + */ +void FileChoose_UpdateKeyboardCursor(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + s16 prevKbdX; + + this->kbdButton = 99; + + if (this->kbdY != 5) { + if (this->stickRelX < -30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->charIndex--; + this->kbdX--; + if (this->kbdX < 0) { + this->kbdX = 12; + this->charIndex = (this->kbdY * 13) + this->kbdX; + } + } else if (this->stickRelX > 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->charIndex++; + this->kbdX++; + if (this->kbdX > 12) { + this->kbdX = 0; + this->charIndex = (this->kbdY * 13) + this->kbdX; + } + } + } else { + if (this->stickRelX < -30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->kbdX--; + if (this->kbdX < 3) { + this->kbdX = 4; + } + } else if (this->stickRelX > 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->kbdX++; + if (this->kbdX > 4) { + this->kbdX = 3; + } + } + } + + if (this->stickRelY > 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->kbdY--; + + if (this->kbdY < 0) { + // dont go to bottom row + if (this->kbdX < 8) { + this->kbdY = 4; + this->charIndex = (s32)(this->kbdX + 52); + } else { + this->kbdY = 5; + this->charIndex += 52; + prevKbdX = this->kbdX; + + if (this->kbdX < 10) { + this->kbdX = 3; + } else if (this->kbdX < 13) { + this->kbdX = 4; + } + + this->unk_1CAD6[this->kbdX] = prevKbdX; + } + } else { + this->charIndex -= 13; + + if (this->kbdY == 4) { + this->charIndex = 52; + this->kbdX = this->unk_1CAD6[this->kbdX]; + this->charIndex += this->kbdX; + } + } + } else if (this->stickRelY < -30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->kbdY++; + + if (this->kbdY > 5) { + this->kbdY = 0; + this->kbdX = this->unk_1CAD6[this->kbdX]; + this->charIndex = this->kbdX; + } else { + this->charIndex += 13; + + if (this->kbdY == 5) { + if (this->kbdX < 8) { + this->kbdY = 0; + this->charIndex = this->kbdX; + } else { + prevKbdX = this->kbdX; + + if (this->kbdX < 3) { + this->kbdX = 0; + } else if (this->kbdX < 6) { + this->kbdX = 1; + } else if (this->kbdX < 8) { + this->kbdX = 2; + } else if (this->kbdX < 10) { + this->kbdX = 3; + } else if (this->kbdX < 13) { + this->kbdX = 4; + } + + this->unk_1CAD6[this->kbdX] = prevKbdX; + } + } + } + } + + if (this->kbdY == 5) { + this->kbdButton = this->kbdX; + } +} + +/** + * This function is mostly a copy paste of `FileChoose_StartNameEntry`. + * The name entry box fades and slides in even though it is not visible. + * After this is complete, change to the options config mode. + * Update function for `CM_START_OPTIONS` + */ +void FileChoose_StartOptions(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + + this->nameEntryBoxAlpha += 25; + + if (this->nameEntryBoxAlpha >= 255) { + this->nameEntryBoxAlpha = 255; + } + + this->nameEntryBoxPosX -= 30; + + if (this->nameEntryBoxPosX <= 0) { + this->nameEntryBoxPosX = 0; + this->nameEntryBoxAlpha = 255; + this->configMode = CM_OPTIONS_MENU; + } +} + +static u8 sSelectedSetting; + +/** + * Update the cursor and appropriate settings for the options menu. + * If the player presses B, write the selected options to the SRAM header + * and set config mode to rotate back to the main menu. + * Update function for `CM_OPTIONS_MENU` + */ +void FileChoose_UpdateOptionsMenu(GameState* thisx) { + FileChooseContext* this = (FileChooseContext*)thisx; + SramContext* sramCtx = &this->sramCtx; + Input* input = &this->state.input[0]; + + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->configMode = CM_OPTIONS_TO_MAIN; + sramCtx->readBuff[0] = gSaveContext.audioSetting; + sramCtx->readBuff[1] = gSaveContext.zTargetSetting; + osSyncPrintf("SAVE"); + Sram_WriteSramHeader(sramCtx); + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("sram->read_buff[2] = J_N = %x\n", sramCtx->readBuff[2]); + osSyncPrintf("sram->read_buff[2] = J_N = %x\n", &sramCtx->readBuff[2]); + osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting); + osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting); + osSyncPrintf("Na_SetSoundOutputMode = %d\n", gSaveContext.audioSetting); + osSyncPrintf(VT_RST); + func_800F6700(gSaveContext.audioSetting); + osSyncPrintf("終了\n"); + return; + } + + if (this->stickRelX < -30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + if (sSelectedSetting == FS_SETTING_AUDIO) { + gSaveContext.audioSetting--; + + // because audio setting is unsigned, can't check for < 0 + if (gSaveContext.audioSetting > 0xF0) { + gSaveContext.audioSetting = FS_AUDIO_SURROUND; + } + } else { + gSaveContext.zTargetSetting ^= 1; + } + } else if (this->stickRelX > 30) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + if (sSelectedSetting == FS_SETTING_AUDIO) { + gSaveContext.audioSetting++; + + if (gSaveContext.audioSetting > FS_AUDIO_SURROUND) { + gSaveContext.audioSetting = FS_AUDIO_STEREO; + } + } else { + gSaveContext.zTargetSetting ^= 1; + } + } + + if ((this->stickRelY < -30) || (this->stickRelY > 30)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sSelectedSetting ^= 1; + } else if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + Audio_PlaySoundGeneral(NA_SE_SY_FSEL_DECIDE_L, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + sSelectedSetting ^= 1; + } +} + +typedef struct { + /* 0x00 */ void* texture[3]; + /* 0x0C */ u16 width[3]; + /* 0x12 */ u16 height; +} OptionsMenuTextureInfo; // size = 0x14 + +static OptionsMenuTextureInfo gOptionsMenuHeaders[] = { + { + { gFileSelOptionsENGTex, gFileSelOptionsGERTex, gFileSelOptionsENGTex }, + { 128, 128, 128 }, + 16, + }, + { + { gFileSelSOUNDENGTex, gFileSelSOUNDENGTex, gFileSelSOUNDFRATex }, + { 64, 64, 64 }, + 16, + }, + { + { gFileSelLTargetingENGTex, gFileSelLTargetingGERTex, gFileSelLTargetingFRATex }, + { 64, 144, 64 }, + 16, + }, + { + { gFileSelCheckBrightnessENGTex, gFileSelCheckBrightnessGERTex, gFileSelCheckBrightnessFRATex }, + { 128, 128, 128 }, + 16, + }, +}; + +static OptionsMenuTextureInfo gOptionsMenuSettings[] = { + { + { gFileSelStereoENGTex, gFileSelStereoENGTex, gFileSelStereoFRATex }, + { 48, 48, 48 }, + 16, + }, + { + { gFileSelMonoENGTex, gFileSelMonoENGTex, gFileSelMonoENGTex }, + { 48, 48, 48 }, + 16, + }, + { + { gFileSelHeadsetENGTex, gFileSelHeadsetGERTex, gFileSelHeadsetFRATex }, + { 48, 48, 48 }, + 16, + }, + { + { gFileSelSurroundENGTex, gFileSelSurroundENGTex, gFileSelSurroundENGTex }, + { 48, 48, 48 }, + 16, + }, + { + { gFileSelSwitchENGTex, gFileSelSwitchGERTex, gFileSelSwitchFRATex }, + { 48, 80, 48 }, + 16, + }, + { + { gFileSelHoldENGTex, gFileSelHoldGERTex, gFileSelHoldFRATex }, + { 48, 80, 48 }, + 16, + }, +}; + +void FileChoose_DrawOptionsImpl(GameState* thisx) { + static s16 cursorPrimRed = 255; + static s16 cursorPrimGreen = 255; + static s16 cursorPrimBlue = 255; + static s16 cursorEnvRed = 0; + static s16 cursorEnvGreen = 0; + static s16 cursorEnvBlue = 0; + static s16 cursorPulseDir = 1; + static s16 cursorFlashTimer = 20; + static s16 cursorPrimColors[][3] = { + { 255, 255, 255 }, + { 0, 255, 255 }, + }; + static s16 cursorEnvColors[][3] = { + { 0, 0, 0 }, + { 0, 150, 150 }, + }; + FileChooseContext* this = (FileChooseContext*)thisx; + s16 cursorRed; + s16 cursorGreen; + s16 cursorBlue; + s16 i; + s16 j; + s16 vtx; + + OPEN_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 848); + + cursorRed = ABS(cursorPrimRed - cursorPrimColors[cursorPulseDir][0]) / cursorFlashTimer; + cursorGreen = ABS(cursorPrimGreen - cursorPrimColors[cursorPulseDir][1]) / cursorFlashTimer; + cursorBlue = ABS(cursorPrimBlue - cursorPrimColors[cursorPulseDir][2]) / cursorFlashTimer; + + if (cursorPrimRed >= cursorPrimColors[cursorPulseDir][0]) { + cursorPrimRed -= cursorRed; + } else { + cursorPrimRed += cursorRed; + } + + if (cursorPrimGreen >= cursorPrimColors[cursorPulseDir][1]) { + cursorPrimGreen -= cursorGreen; + } else { + cursorPrimGreen += cursorGreen; + } + + if (cursorPrimBlue >= cursorPrimColors[cursorPulseDir][2]) { + cursorPrimBlue -= cursorBlue; + } else { + cursorPrimBlue += cursorBlue; + } + + cursorRed = ABS(cursorEnvRed - cursorEnvColors[cursorPulseDir][0]) / cursorFlashTimer; + cursorGreen = ABS(cursorEnvGreen - cursorEnvColors[cursorPulseDir][1]) / cursorFlashTimer; + cursorBlue = ABS(cursorEnvBlue - cursorEnvColors[cursorPulseDir][2]) / cursorFlashTimer; + + if (cursorEnvRed >= cursorEnvColors[cursorPulseDir][0]) { + cursorEnvRed -= cursorRed; + } else { + cursorEnvRed += cursorRed; + } + + if (cursorEnvGreen >= cursorEnvColors[cursorPulseDir][1]) { + cursorEnvGreen -= cursorGreen; + } else { + cursorEnvGreen += cursorGreen; + } + + if (cursorEnvBlue >= cursorEnvColors[cursorPulseDir][2]) { + cursorEnvBlue -= cursorBlue; + } else { + cursorEnvBlue += cursorBlue; + } + + if (--cursorFlashTimer == 0) { + cursorPrimRed = cursorPrimColors[cursorPulseDir][0]; + cursorPrimGreen = cursorPrimColors[cursorPulseDir][1]; + cursorPrimBlue = cursorPrimColors[cursorPulseDir][2]; + + cursorEnvRed = cursorEnvColors[cursorPulseDir][0]; + cursorEnvGreen = cursorEnvColors[cursorPulseDir][1]; + cursorEnvBlue = cursorEnvColors[cursorPulseDir][2]; + + cursorFlashTimer = 20; + + if (++cursorPulseDir > 1) { + cursorPulseDir = 0; + } + } + + if (gSaveContext.language == LANGUAGE_GER) { + gSPVertex(POLY_OPA_DISP++, D_80811E30, 32, 0); + } else { + gSPVertex(POLY_OPA_DISP++, D_80811D30, 32, 0); + } + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + + for (i = 0, vtx = 0; i < 4; i++, vtx += 4) { + gDPLoadTextureBlock(POLY_OPA_DISP++, gOptionsMenuHeaders[i].texture[gSaveContext.language], G_IM_FMT_IA, + G_IM_SIZ_8b, gOptionsMenuHeaders[i].width[gSaveContext.language], + gOptionsMenuHeaders[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + } + + if (gSaveContext.language == LANGUAGE_GER) { + gSPVertex(POLY_OPA_DISP++, D_80812130, 32, 0); + } else { + gSPVertex(POLY_OPA_DISP++, D_80811F30, 32, 0); + } + + for (i = 0, vtx = 0; i < 4; i++, vtx += 4) { + gDPPipeSync(POLY_OPA_DISP++); + if (i == gSaveContext.audioSetting) { + if (sSelectedSetting == FS_SETTING_AUDIO) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, cursorPrimRed, cursorPrimGreen, cursorPrimBlue, + this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, cursorEnvRed, cursorEnvGreen, cursorEnvBlue, 255); + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + } + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 120, 120, 120, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + } + + gDPLoadTextureBlock(POLY_OPA_DISP++, gOptionsMenuSettings[i].texture[gSaveContext.language], G_IM_FMT_IA, + G_IM_SIZ_8b, gOptionsMenuSettings[i].width[gSaveContext.language], + gOptionsMenuHeaders[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + } + + for (; i < 6; i++, vtx += 4) { + gDPPipeSync(POLY_OPA_DISP++); + + if (i == (gSaveContext.zTargetSetting + 4)) { + if (sSelectedSetting != FS_SETTING_AUDIO) { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, cursorPrimRed, cursorPrimGreen, cursorPrimBlue, + this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, cursorEnvRed, cursorEnvGreen, cursorEnvBlue, 0xFF); + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + } + } else { + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 120, 120, 120, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + } + + gDPLoadTextureBlock(POLY_OPA_DISP++, gOptionsMenuSettings[i].texture[gSaveContext.language], G_IM_FMT_IA, + G_IM_SIZ_8b, gOptionsMenuSettings[i].width[gSaveContext.language], + gOptionsMenuHeaders[i].height, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + } + + gDPPipeSync(POLY_OPA_DISP++); + + // check brightness bars + gDPLoadTextureBlock_4b(POLY_OPA_DISP++, gFileSelBrightnessCheckTex, G_IM_FMT_IA, 96, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 55, 55, 55, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 40, 40, 40, 255); + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + + vtx += 4; + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 30, 30, 30, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255); + gSP1Quadrangle(POLY_OPA_DISP++, vtx, vtx + 2, vtx + 3, vtx + 1, 0); + + vtx += 4; + + // blue divider lines + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 0, 255, 255, this->titleAlpha[0]); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + + gDPLoadTextureBlock_4b(POLY_OPA_DISP++, gFileSelOptionsDividerTex, G_IM_FMT_IA, 256, 2, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + Matrix_Push(); + Matrix_Translate(0.0f, 0.1f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_nameset_PAL.c", 1009), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPVertex(POLY_OPA_DISP++, gOptionsDividerTopVtx, 4, 0); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + Matrix_Pop(); + + Matrix_Push(); + Matrix_Translate(0.0f, 0.2f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_nameset_PAL.c", 1021), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gSPVertex(POLY_OPA_DISP++, gOptionsDividerMiddleVtx, 4, 0); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + Matrix_Pop(); + + Matrix_Push(); + Matrix_Translate(0.0f, 0.4f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_file_nameset_PAL.c", 1033), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPVertex(POLY_OPA_DISP++, gOptionsDividerBottomVtx, 4, 0); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + Matrix_Pop(); + + CLOSE_DISPS(this->state.gfxCtx, "../z_file_nameset_PAL.c", 1040); +} + +void FileChoose_DrawOptions(GameState* thisx) { + FileChoose_DrawOptionsImpl(thisx); +} diff --git a/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_data.c b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_data.c new file mode 100644 index 000000000..cb4c95d7f --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_file_choose/z_file_nameset_data.c @@ -0,0 +1,11 @@ +#include "file_choose.h" + +#include "assets/overlays/ovl_File_Choose/ovl_file_choose.h" + +s16 D_808123F0[] = { + 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, + 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, + 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x0000, 0x0040, 0x003F, 0x003E, +}; diff --git a/soh/src/overlays/gamestates/ovl_opening/z_opening.c b/soh/src/overlays/gamestates/ovl_opening/z_opening.c new file mode 100644 index 000000000..fa8ce9060 --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_opening/z_opening.c @@ -0,0 +1,41 @@ +/* + * File: z_opening.c + * Overlay: ovl_opening + * Description: Initializes the game into the title screen + */ + +#include "global.h" + +void Opening_SetupTitleScreen(OpeningContext* this) { + gSaveContext.gameMode = 1; + this->state.running = false; + gSaveContext.linkAge = 0; + Sram_InitDebugSave(); + gSaveContext.cutsceneIndex = 0xFFF3; + gSaveContext.sceneSetupIndex = 7; + SET_NEXT_GAMESTATE(&this->state, Gameplay_Init, GlobalContext); +} + +void func_80803C5C(OpeningContext* this) { +} + +void Opening_Main(GameState* thisx) { + OpeningContext* this = (OpeningContext*)thisx; + + func_80095248(this->state.gfxCtx, 0, 0, 0); + Opening_SetupTitleScreen(this); + func_80803C5C(this); +} + +void Opening_Destroy(GameState* thisx) { +} + +void Opening_Init(GameState* thisx) { + OpeningContext* this = (OpeningContext*)thisx; + + R_UPDATE_RATE = 1; + Matrix_Init(&this->state); + View_Init(&this->view, this->state.gfxCtx); + this->state.main = Opening_Main; + this->state.destroy = Opening_Destroy; +} diff --git a/soh/src/overlays/gamestates/ovl_select/z_select.c b/soh/src/overlays/gamestates/ovl_select/z_select.c new file mode 100644 index 000000000..98aaa4a63 --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_select/z_select.c @@ -0,0 +1,633 @@ +/* + * File: z_select.c + * Overlay: ovl_select + * Description: Debug Scene Select Menu + */ + +#include "ultra64.h" +#include "global.h" +#include "vt.h" +#include "alloca.h" + +void Select_LoadTitle(SelectContext* this) { + this->state.running = false; + SET_NEXT_GAMESTATE(&this->state, Title_Init, TitleContext); +} + +void Select_LoadGame(SelectContext* this, s32 entranceIndex) { + osSyncPrintf(VT_FGCOL(BLUE)); + osSyncPrintf("\n\n\nFILE_NO=%x\n\n\n", gSaveContext.fileNum); + osSyncPrintf(VT_RST); + if (gSaveContext.fileNum == 0xFF) { + Sram_InitDebugSave(); + gSaveContext.unk_13F6 = gSaveContext.magic; + gSaveContext.magic = 0; + gSaveContext.unk_13F4 = 0; + gSaveContext.magicLevel = gSaveContext.magic; + } + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = gSaveContext.buttonStatus[4] = BTN_ENABLED; + gSaveContext.unk_13E7 = gSaveContext.unk_13E8 = gSaveContext.unk_13EA = gSaveContext.unk_13EC = 0; + Audio_QueueSeqCmd(SEQ_PLAYER_BGM_MAIN << 24 | NA_BGM_STOP); + gSaveContext.entranceIndex = entranceIndex; + + gSaveContext.respawnFlag = 0; + gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = -1; + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = 0xFF; + gSaveContext.showTitleCard = true; + gWeatherMode = 0; + this->state.running = false; + SET_NEXT_GAMESTATE(&this->state, Gameplay_Init, GlobalContext); +} + +static SceneSelectEntry sScenes[] = { + { " 1:Hyrule Field", Select_LoadGame, 0x00CD }, + { " 2:Kakariko Village", Select_LoadGame, 0x00DB }, + { " 3:Graveyard", Select_LoadGame, 0x00E4 }, + { " 4:Zora's River", Select_LoadGame, 0x00EA }, + { " 5:Kokiri Forest", Select_LoadGame, 0x00EE }, + { " 6:Sacred Forest Meadow", Select_LoadGame, 0x00FC }, + { " 7:Lake Hylia", Select_LoadGame, 0x0102 }, + { " 8:Zora's Domain", Select_LoadGame, 0x0108 }, + { " 9:Zora's Fountain", Select_LoadGame, 0x010E }, + { "10:Gerudo Valley", Select_LoadGame, 0x0117 }, + { "11:Lost Woods", Select_LoadGame, 0x011E }, + { "12:Desert Colossus", Select_LoadGame, 0x0123 }, + { "13:Gerudo's Fortress", Select_LoadGame, 0x0129 }, + { "14:Haunted Wasteland", Select_LoadGame, 0x0130 }, + { "15:Hyrule Castle", Select_LoadGame, 0x0138 }, + { "16:Death Mountain Trail", Select_LoadGame, 0x013D }, + { "17:Death Mountain Crater", Select_LoadGame, 0x0147 }, + { "18:Goron City", Select_LoadGame, 0x014D }, + { "19:Lon Lon Ranch", Select_LoadGame, 0x0157 }, + { "20:Temple Of Time", Select_LoadGame, 0x0053 }, + { "21:Chamber of Sages", Select_LoadGame, 0x006B }, + { "22:Shooting Gallery", Select_LoadGame, 0x003B }, + { "23:Castle Courtyard Game", Select_LoadGame, 0x007A }, + { "24:Grave 1", Select_LoadGame, 0x031C }, + { "25:Grave 2", Select_LoadGame, 0x004B }, + { "26:Royal Family's Tomb", Select_LoadGame, 0x002D }, + { "27:Great Fairy's Fountain (Din)", Select_LoadGame, 0x0315 }, + { "28:Great Fairy's Fountain (Farore)", Select_LoadGame, 0x036D }, + { "29:Great Fairy's Fountain (Nayru)", Select_LoadGame, 0x0371 }, + { "30:Ganon's Tower - Collapsing", Select_LoadGame, 0x043F }, + { "31:Castle Courtyard", Select_LoadGame, 0x0400 }, + { "32:Fishing Pond", Select_LoadGame, 0x045F }, + { "33:Bombchu Bowling Alley", Select_LoadGame, 0x0507 }, + { "34:Lon Lon Ranch House", Select_LoadGame, 0x004F }, + { "35:Lon Lon Ranch Silo", Select_LoadGame, 0x05D0 }, + { "36:Guard House", Select_LoadGame, 0x007E }, + { "37:Potion Shop", Select_LoadGame, 0x0072 }, + { "38:Treasure Chest Game", Select_LoadGame, 0x0063 }, + { "39:House Of Skulltula", Select_LoadGame, 0x0550 }, + { "40:Entrance to Market", Select_LoadGame, 0x0033 }, + { "41:Market", Select_LoadGame, 0x00B1 }, + { "42:Back Alley", Select_LoadGame, 0x00AD }, + { "43:Temple of Time Exterior", Select_LoadGame, 0x0171 }, + { "44:Link's House", Select_LoadGame, 0x00BB }, + { "45:Kakariko House 1", Select_LoadGame, 0x02FD }, + { "46:Back Alley House 1", Select_LoadGame, 0x043B }, + { "47:House of the Know-it-All Brothers", Select_LoadGame, 0x00C9 }, + { "48:House of Twins", Select_LoadGame, 0x009C }, + { "49:Mido's House", Select_LoadGame, 0x0433 }, + { "50:Saria's House", Select_LoadGame, 0x0437 }, + { "51:Stable", Select_LoadGame, 0x02F9 }, + { "52:Grave Keeper's Hut", Select_LoadGame, 0x030D }, + { "53:Dog Lady's House", Select_LoadGame, 0x0398 }, + { "54:Impa's House", Select_LoadGame, 0x039C }, + { "55:Lakeside Laboratory", Select_LoadGame, 0x0043 }, + { "56:Running Man's Tent", Select_LoadGame, 0x03A0 }, + { "57:Bazaar", Select_LoadGame, 0x00B7 }, + { "58:Kokiri Shop", Select_LoadGame, 0x00C1 }, + { "59:Goron Shop", Select_LoadGame, 0x037C }, + { "60:Zora Shop", Select_LoadGame, 0x0380 }, + { "61:Closed Shop", Select_LoadGame, 0x0384 }, + { "62:Potion Shop", Select_LoadGame, 0x0388 }, + { "63:Bombchu Shop ", Select_LoadGame, 0x0390 }, + { "64:Happy Mask Shop", Select_LoadGame, 0x0530 }, + { "65:Gerudo Training Ground", Select_LoadGame, 0x0008 }, + { "66:Inside the Deku Tree", Select_LoadGame, 0x0000 }, + { "67:Gohma's Lair", Select_LoadGame, 0x040F }, + { "68:Dodongo's Cavern", Select_LoadGame, 0x0004 }, + { "69:King Dodongo's Lair", Select_LoadGame, 0x040B }, + { "70:Inside Jabu-Jabu's Belly", Select_LoadGame, 0x0028 }, + { "71:Barinade's Lair", Select_LoadGame, 0x0301 }, + { "72:Forest Temple", Select_LoadGame, 0x0169 }, + { "73:Phantom Ganon's Lair", Select_LoadGame, 0x000C }, + { "74:Bottom of the Well", Select_LoadGame, 0x0098 }, + { "75:Shadow Temple", Select_LoadGame, 0x0037 }, + { "76:Bongo Bongo's Lair", Select_LoadGame, 0x0413 }, + { "77:Fire Temple", Select_LoadGame, 0x0165 }, + { "78:Volvagia's Lair", Select_LoadGame, 0x0305 }, + { "79:Water Temple", Select_LoadGame, 0x0010 }, + { "80:Morpha's Lair", Select_LoadGame, 0x0417 }, + { "81:Spirit Temple", Select_LoadGame, 0x0082 }, + { "82:Iron Knuckle's Lair", Select_LoadGame, 0x008D }, + { "83:Twinrova's Lair", Select_LoadGame, 0x05EC }, + { "84:Stairs to Ganondorf's Lair", Select_LoadGame, 0x041B }, + { "85:Ganondorf's Lair", Select_LoadGame, 0x041F }, + { "86:Ice Cavern", Select_LoadGame, 0x0088 }, + { "87:Dampé Grave Relay Game", Select_LoadGame, 0x044F }, + { "88:Inside Ganon's Castle", Select_LoadGame, 0x0467 }, + { "89:Ganon's Lair", Select_LoadGame, 0x0517 }, + { "90:Escaping Ganon's Castle 1", Select_LoadGame, 0x0179 }, + { "91:Escaping Ganon's Castle 2", Select_LoadGame, 0x01B5 }, + { "92:Escaping Ganon's Castle 3", Select_LoadGame, 0x03DC }, + { "93:Escaping Ganon's Castle 4", Select_LoadGame, 0x03E4 }, + { "94:Escaping Ganon's Castle 5", Select_LoadGame, 0x056C }, + { "95:Thieves' Hideout 1-2", Select_LoadGame, 0x0486 }, + { "96:Thieves' Hideout 3-4 9-10", Select_LoadGame, 0x048E }, + { "97:Thieves' Hideout 5-6", Select_LoadGame, 0x0496 }, + { "98:Thieves' Hideout 7-8", Select_LoadGame, 0x049E }, + { "99:Thieves' Hideout 11-12", Select_LoadGame, 0x04AE }, + { "100:Thieves' Hideout 13", Select_LoadGame, 0x0570 }, + { "101:Grotto 0", Select_LoadGame, 0x003F }, + { "102:Grotto 1", Select_LoadGame, 0x0598 }, + { "103:Grotto 2", Select_LoadGame, 0x059C }, + { "104:Grotto 3", Select_LoadGame, 0x05A0 }, + { "105:Grotto 4", Select_LoadGame, 0x05A4 }, + { "106:Grotto 5", Select_LoadGame, 0x05A8 }, + { "107:Grotto 6", Select_LoadGame, 0x05AC }, + { "108:Grotto 7", Select_LoadGame, 0x05B0 }, + { "109:Grotto 8", Select_LoadGame, 0x05B4 }, + { "110:Grotto 9", Select_LoadGame, 0x05B8 }, + { "111:Grotto 10", Select_LoadGame, 0x05BC }, + { "112:Grotto 11", Select_LoadGame, 0x05C0 }, + { "113:Grotto 12", Select_LoadGame, 0x05C4 }, + { "114:Grotto 13", Select_LoadGame, 0x05FC }, + { "115:Goddess Cutscene Environment", Select_LoadGame, 0x00A0 }, + { "116:Test Room", Select_LoadGame, 0x0520 }, + { "117:SRD Map", Select_LoadGame, 0x0018 }, + { "118:Test Map", Select_LoadGame, 0x0094 }, + { "119:Treasure Chest Warp", Select_LoadGame, 0x0024 }, + { "120:Stalfos Miniboss Room", Select_LoadGame, 0x001C }, + { "121:Stalfos Boss Room", Select_LoadGame, 0x0020 }, + { "122:Dark Link Room", Select_LoadGame, 0x0047 }, + { "123:Shooting Gallery Duplicate", Select_LoadGame, 0x02EA }, + { "124:depth test", Select_LoadGame, 0x00B6 }, + { "125:Hyrule Garden Game (Broken)", Select_LoadGame, 0x0076 }, + { "title", Select_LoadTitle, 0x0000 }, +}; + +void Select_UpdateMenu(SelectContext* this) { + Input* input = &this->state.input[0]; + s32 pad; + SceneSelectEntry* selectedScene; + + if (this->verticalInputAccumulator == 0) { + if (CHECK_BTN_ALL(input->press.button, BTN_A) || CHECK_BTN_ALL(input->press.button, BTN_START)) { + selectedScene = &this->scenes[this->currentScene]; + if (selectedScene->loadFunc != NULL) { + selectedScene->loadFunc(this, selectedScene->entranceIndex); + } + } + + if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + gSaveContext.linkAge = 1; + } else { + gSaveContext.linkAge = 0; + } + } + + if (CHECK_BTN_ALL(input->press.button, BTN_Z)) { + if (gSaveContext.cutsceneIndex == 0x8000) { + gSaveContext.cutsceneIndex = 0; + } else if (gSaveContext.cutsceneIndex == 0) { + gSaveContext.cutsceneIndex = 0xFFF0; + } else if (gSaveContext.cutsceneIndex == 0xFFF0) { + gSaveContext.cutsceneIndex = 0xFFF1; + } else if (gSaveContext.cutsceneIndex == 0xFFF1) { + gSaveContext.cutsceneIndex = 0xFFF2; + } else if (gSaveContext.cutsceneIndex == 0xFFF2) { + gSaveContext.cutsceneIndex = 0xFFF3; + } else if (gSaveContext.cutsceneIndex == 0xFFF3) { + gSaveContext.cutsceneIndex = 0xFFF4; + } else if (gSaveContext.cutsceneIndex == 0xFFF4) { + gSaveContext.cutsceneIndex = 0xFFF5; + } else if (gSaveContext.cutsceneIndex == 0xFFF5) { + gSaveContext.cutsceneIndex = 0xFFF6; + } else if (gSaveContext.cutsceneIndex == 0xFFF6) { + gSaveContext.cutsceneIndex = 0xFFF7; + } else if (gSaveContext.cutsceneIndex == 0xFFF7) { + gSaveContext.cutsceneIndex = 0xFFF8; + } else if (gSaveContext.cutsceneIndex == 0xFFF8) { + gSaveContext.cutsceneIndex = 0xFFF9; + } else if (gSaveContext.cutsceneIndex == 0xFFF9) { + gSaveContext.cutsceneIndex = 0xFFFA; + } else if (gSaveContext.cutsceneIndex == 0xFFFA) { + gSaveContext.cutsceneIndex = 0x8000; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_R)) { + if (gSaveContext.cutsceneIndex == 0x8000) { + gSaveContext.cutsceneIndex = 0xFFFA; + } else if (gSaveContext.cutsceneIndex == 0) { + gSaveContext.cutsceneIndex = 0x8000; + } else if (gSaveContext.cutsceneIndex == 0xFFF0) { + gSaveContext.cutsceneIndex = 0; + } else if (gSaveContext.cutsceneIndex == 0xFFF1) { + gSaveContext.cutsceneIndex = 0xFFF0; + } else if (gSaveContext.cutsceneIndex == 0xFFF2) { + gSaveContext.cutsceneIndex = 0xFFF1; + } else if (gSaveContext.cutsceneIndex == 0xFFF3) { + gSaveContext.cutsceneIndex = 0xFFF2; + } else if (gSaveContext.cutsceneIndex == 0xFFF4) { + gSaveContext.cutsceneIndex = 0xFFF3; + } else if (gSaveContext.cutsceneIndex == 0xFFF5) { + gSaveContext.cutsceneIndex = 0xFFF4; + } else if (gSaveContext.cutsceneIndex == 0xFFF6) { + gSaveContext.cutsceneIndex = 0xFFF5; + } else if (gSaveContext.cutsceneIndex == 0xFFF7) { + gSaveContext.cutsceneIndex = 0xFFF6; + } else if (gSaveContext.cutsceneIndex == 0xFFF8) { + gSaveContext.cutsceneIndex = 0xFFF7; + } else if (gSaveContext.cutsceneIndex == 0xFFF9) { + gSaveContext.cutsceneIndex = 0xFFF8; + } else if (gSaveContext.cutsceneIndex == 0xFFFA) { + gSaveContext.cutsceneIndex = 0xFFF9; + } + } + + gSaveContext.nightFlag = 0; + if (gSaveContext.cutsceneIndex == 0) { + gSaveContext.nightFlag = 1; + } + + // user can change "opt", but it doesn't do anything + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + this->opt--; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + this->opt++; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + if (this->lockUp == true) { + this->timerUp = 0; + } + if (this->timerUp == 0) { + this->timerUp = 20; + this->lockUp = true; + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->verticalInput = R_UPDATE_RATE; + } + } + + if (CHECK_BTN_ALL(input->cur.button, BTN_DUP) && this->timerUp == 0) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->verticalInput = R_UPDATE_RATE * 3; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + if (this->lockDown == true) { + this->timerDown = 0; + } + if (this->timerDown == 0) { + this->timerDown = 20; + this->lockDown = true; + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->verticalInput = -R_UPDATE_RATE; + } + } + + if (CHECK_BTN_ALL(input->cur.button, BTN_DDOWN) && (this->timerDown == 0)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->verticalInput = -R_UPDATE_RATE * 3; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT) || CHECK_BTN_ALL(input->cur.button, BTN_DLEFT)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->verticalInput = R_UPDATE_RATE; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT) || CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT)) { + Audio_PlaySoundGeneral(NA_SE_IT_SWORD_IMPACT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + this->verticalInput = -R_UPDATE_RATE; + } + } + + if (CHECK_BTN_ALL(input->press.button, BTN_L)) { + this->pageDownIndex++; + this->pageDownIndex = + (this->pageDownIndex + ARRAY_COUNT(this->pageDownStops)) % ARRAY_COUNT(this->pageDownStops); + this->currentScene = this->topDisplayedScene = this->pageDownStops[this->pageDownIndex]; + } + + this->verticalInputAccumulator += this->verticalInput; + + if (this->verticalInputAccumulator < -7) { + this->verticalInput = 0; + this->verticalInputAccumulator = 0; + + this->currentScene++; + this->currentScene = (this->currentScene + this->count) % this->count; + + if (this->currentScene == ((this->topDisplayedScene + this->count + 19) % this->count)) { + this->topDisplayedScene++; + this->topDisplayedScene = (this->topDisplayedScene + this->count) % this->count; + } + } + + if (this->verticalInputAccumulator > 7) { + this->verticalInput = 0; + this->verticalInputAccumulator = 0; + + if (this->currentScene == this->topDisplayedScene) { + this->topDisplayedScene -= 2; + this->topDisplayedScene = (this->topDisplayedScene + this->count) % this->count; + } + + this->currentScene--; + this->currentScene = (this->currentScene + this->count) % this->count; + + if (this->currentScene == ((this->topDisplayedScene + this->count) % this->count)) { + this->topDisplayedScene--; + this->topDisplayedScene = (this->topDisplayedScene + this->count) % this->count; + } + } + + this->currentScene = (this->currentScene + this->count) % this->count; + this->topDisplayedScene = (this->topDisplayedScene + this->count) % this->count; + + dREG(80) = this->currentScene; + dREG(81) = this->topDisplayedScene; + dREG(82) = this->pageDownIndex; + + if (this->timerUp != 0) { + this->timerUp--; + } + + if (this->timerUp == 0) { + this->lockUp = false; + } + + if (this->timerDown != 0) { + this->timerDown--; + } + + if (this->timerDown == 0) { + this->lockDown = false; + } +} + +void Select_PrintMenu(SelectContext* this, GfxPrint* printer) { + s32 scene; + s32 i; + char* name; + + GfxPrint_SetColor(printer, 255, 155, 150, 255); + GfxPrint_SetPos(printer, 12, 2); + GfxPrint_Printf(printer, "ZELDA MAP SELECT"); + GfxPrint_SetColor(printer, 255, 255, 255, 255); + + for (i = 0; i < 20; i++) { + GfxPrint_SetPos(printer, 9, i + 4); + + scene = (this->topDisplayedScene + i + this->count) % this->count; + if (scene == this->currentScene) { + GfxPrint_SetColor(printer, 255, 20, 20, 255); + } else { + GfxPrint_SetColor(printer, 200, 200, 55, 255); + } + + name = this->scenes[scene].name; + if (name == NULL) { + name = "**Null**"; + } + + GfxPrint_Printf(printer, "%s", name); + }; + + GfxPrint_SetColor(printer, 155, 55, 150, 255); + GfxPrint_SetPos(printer, 20, 26); + GfxPrint_Printf(printer, "OPT=%d", this->opt); +} + +static const char* sLoadingMessages[] = { + // "Please wait a minute" + GFXP_HIRAGANA "シバラクオマチクダサイ", + // "Hold on a sec" + GFXP_HIRAGANA "チョット マッテネ", + // "Wait a moment" + GFXP_KATAKANA "ウェイト ア モーメント", + // "Loading" + GFXP_KATAKANA "ロード" GFXP_HIRAGANA "チュウ", + // "Now working" + GFXP_HIRAGANA "ナウ ワーキング", + // "Now creating" + GFXP_HIRAGANA "イマ ツクッテマス", + // "It's not broken" + GFXP_HIRAGANA "コショウジャナイヨ", + // "Coffee Break" + GFXP_KATAKANA "コーヒー ブレイク", + // "Please set B side" + GFXP_KATAKANA "Bメンヲセットシテクダサイ", + // "Be patient, now" + GFXP_HIRAGANA "ジット" GFXP_KATAKANA "ガマン" GFXP_HIRAGANA "ノ" GFXP_KATAKANA "コ" GFXP_HIRAGANA "デアッタ", + // "Please wait just a minute" + GFXP_HIRAGANA "イマシバラクオマチクダサイ", + // "Don't worry, don't worry. Take a break, take a break." + GFXP_HIRAGANA "アワテナイアワテナイ。ヒトヤスミヒトヤスミ。", +}; + +void Select_PrintLoadingMessage(SelectContext* this, GfxPrint* printer) { + s32 randomMsg; + + GfxPrint_SetPos(printer, 10, 15); + GfxPrint_SetColor(printer, 255, 255, 255, 255); + randomMsg = Rand_ZeroOne() * ARRAY_COUNT(sLoadingMessages); + GfxPrint_Printf(printer, "%s", sLoadingMessages[randomMsg]); +} + +static const char* sAgeLabels[] = { + GFXP_HIRAGANA "17(ワカモノ)", // "17(young)" + GFXP_HIRAGANA "5(ワカスギ)", // "5(very young)" +}; + +void Select_PrintAgeSetting(SelectContext* this, GfxPrint* printer, s32 age) { + GfxPrint_SetPos(printer, 4, 26); + GfxPrint_SetColor(printer, 255, 255, 55, 255); + GfxPrint_Printf(printer, "Age:%s", sAgeLabels[age]); +} + +void Select_PrintCutsceneSetting(SelectContext* this, GfxPrint* printer, u16 csIndex) { + char* label; + + GfxPrint_SetPos(printer, 4, 25); + GfxPrint_SetColor(printer, 255, 255, 55, 255); + + switch (csIndex) { + case 0: + label = GFXP_HIRAGANA " ヨル " GFXP_KATAKANA "ゴロン"; + gSaveContext.dayTime = 0; + break; + case 0x8000: + // clang-format off + gSaveContext.dayTime = 0x8000; label = GFXP_HIRAGANA "オヒル " GFXP_KATAKANA "ジャラ"; + // clang-format on + break; + case 0xFFF0: + // clang-format off + gSaveContext.dayTime = 0x8000; label = "デモ00"; + // clang-format on + break; + case 0xFFF1: + label = "デモ01"; + break; + case 0xFFF2: + label = "デモ02"; + break; + case 0xFFF3: + label = "デモ03"; + break; + case 0xFFF4: + label = "デモ04"; + break; + case 0xFFF5: + label = "デモ05"; + break; + case 0xFFF6: + label = "デモ06"; + break; + case 0xFFF7: + label = "デモ07"; + break; + case 0xFFF8: + label = "デモ08"; + break; + case 0xFFF9: + label = "デモ09"; + break; + case 0xFFFA: + label = "デモ0A"; + break; + }; + + gSaveContext.skyboxTime = gSaveContext.dayTime; + GfxPrint_Printf(printer, "Stage:" GFXP_KATAKANA "%s", label); +} + +void Select_DrawMenu(SelectContext* this) { + GraphicsContext* gfxCtx = this->state.gfxCtx; + GfxPrint* printer; + + OPEN_DISPS(gfxCtx, "../z_select.c", 930); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + func_80095248(gfxCtx, 0, 0, 0); + SET_FULLSCREEN_VIEWPORT(&this->view); + func_800AAA50(&this->view, 0xF); + func_80094140(gfxCtx); + + printer = alloca(sizeof(GfxPrint)); + GfxPrint_Init(printer); + GfxPrint_Open(printer, POLY_OPA_DISP); + Select_PrintMenu(this, printer); + Select_PrintAgeSetting(this, printer, ((void)0, gSaveContext.linkAge)); + Select_PrintCutsceneSetting(this, printer, ((void)0, gSaveContext.cutsceneIndex)); + POLY_OPA_DISP = GfxPrint_Close(printer); + GfxPrint_Destroy(printer); + + CLOSE_DISPS(gfxCtx, "../z_select.c", 966); +} + +void Select_DrawLoadingScreen(SelectContext* this) { + GraphicsContext* gfxCtx = this->state.gfxCtx; + GfxPrint* printer; + + OPEN_DISPS(gfxCtx, "../z_select.c", 977); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + func_80095248(gfxCtx, 0, 0, 0); + SET_FULLSCREEN_VIEWPORT(&this->view); + func_800AAA50(&this->view, 0xF); + func_80094140(gfxCtx); + + printer = alloca(sizeof(GfxPrint)); + GfxPrint_Init(printer); + GfxPrint_Open(printer, POLY_OPA_DISP); + Select_PrintLoadingMessage(this, printer); + POLY_OPA_DISP = GfxPrint_Close(printer); + GfxPrint_Destroy(printer); + + CLOSE_DISPS(gfxCtx, "../z_select.c", 1006); +} + +void Select_Draw(SelectContext* this) { + GraphicsContext* gfxCtx = this->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_select.c", 1013); + + gSPSegment(POLY_OPA_DISP++, 0x00, NULL); + func_80095248(gfxCtx, 0, 0, 0); + SET_FULLSCREEN_VIEWPORT(&this->view); + func_800AAA50(&this->view, 0xF); + + if (!this->state.running) { + Select_DrawLoadingScreen(this); + } else { + Select_DrawMenu(this); + } + + CLOSE_DISPS(gfxCtx, "../z_select.c", 1037); +} + +void Select_Main(GameState* thisx) { + SelectContext* this = (SelectContext*)thisx; + + Select_UpdateMenu(this); + Select_Draw(this); +} + +void Select_Destroy(GameState* thisx) { + osSyncPrintf("%c", '\a'); // ASCII BEL character, plays an alert tone + // "view_cleanup will hang, so it won't be called" + osSyncPrintf("*** view_cleanupはハングアップするので、呼ばない ***\n"); +} + +void Select_Init(GameState* thisx) { + SelectContext* this = (SelectContext*)thisx; + size_t size; + s32 pad; + + this->state.main = Select_Main; + this->state.destroy = Select_Destroy; + this->scenes = sScenes; + this->topDisplayedScene = 0; + this->currentScene = 0; + this->pageDownStops[0] = 0; // Hyrule Field + this->pageDownStops[1] = 19; // Temple Of Time + this->pageDownStops[2] = 37; // Treasure Chest Game + this->pageDownStops[3] = 51; // Gravekeeper's Hut + this->pageDownStops[4] = 59; // Zora Shop + this->pageDownStops[5] = 73; // Bottom of the Well + this->pageDownStops[6] = 91; // Escaping Ganon's Tower 3 + this->pageDownIndex = 0; + this->opt = 0; + this->count = ARRAY_COUNT(sScenes); + View_Init(&this->view, this->state.gfxCtx); + this->view.flags = (0x08 | 0x02); + this->verticalInputAccumulator = 0; + this->verticalInput = 0; + this->timerUp = 0; + this->timerDown = 0; + this->lockUp = 0; + this->lockDown = 0; + this->unk_234 = 0; + + size = (uintptr_t)_z_select_staticSegmentRomEnd - (uintptr_t)_z_select_staticSegmentRomStart; + + if ((dREG(80) >= 0) && (dREG(80) < this->count)) { + this->currentScene = dREG(80); + this->topDisplayedScene = dREG(81); + this->pageDownIndex = dREG(82); + } + R_UPDATE_RATE = 1; + #ifndef _MSC_VER + this->staticSegment = GameState_Alloc(&this->state, size, "../z_select.c", 1114); + DmaMgr_SendRequest1(this->staticSegment, _z_select_staticSegmentRomStart, size, "../z_select.c", 1115); + #endif + gSaveContext.cutsceneIndex = 0x8000; + gSaveContext.linkAge = 1; +} diff --git a/soh/src/overlays/gamestates/ovl_title/z_title.c b/soh/src/overlays/gamestates/ovl_title/z_title.c new file mode 100644 index 000000000..e45cdf27b --- /dev/null +++ b/soh/src/overlays/gamestates/ovl_title/z_title.c @@ -0,0 +1,249 @@ +/* + * File: z_title.c + * Overlay: ovl_title + * Description: Displays the Nintendo Logo + */ + +#define NORMAL_GAMEPLAY + +#include "global.h" +#include "alloca.h" +#include "textures/nintendo_rogo_static/nintendo_rogo_static.h" +#include + +char* quote; + +void Title_PrintBuildInfo(Gfx** gfxp) { + Gfx* g; + //GfxPrint* printer; + GfxPrint printer; + + g = *gfxp; + g = func_8009411C(g); + //printer = alloca(sizeof(GfxPrint)); + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, g); + GfxPrint_SetColor(&printer, 255, 155, 255, 255); + GfxPrint_SetPos(&printer, 12, 20); + +#ifdef _MSC_VER + GfxPrint_Printf(&printer, "MSVC SHIP"); +#else + GfxPrint_Printf(printer, "GCC SHIP"); +#endif + + GfxPrint_SetColor(&printer, 255, 255, 255, 255); + GfxPrint_SetPos(&printer, 2, 22); + GfxPrint_Printf(&printer, quote); + GfxPrint_SetPos(&printer, 1, 25); + GfxPrint_Printf(&printer, "Build Date:%s", gBuildDate); + GfxPrint_SetPos(&printer, 3, 26); + GfxPrint_Printf(&printer, "%s", gBuildTeam); + GfxPrint_SetPos(&printer, 3, 28); + GfxPrint_Printf(&printer, "Release Version: %s", gBuildVersion); + g = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); + *gfxp = g; +} + +const char* quotes[11] = { + "My boy! This peace is what all true warriors strive for!", + "Hmm. How can we help?", + "Zelda! Duke Onkled is under attack by the evil forces of Ganon!", + "I'm going to Gamelon to aid him.", + "I'll take the Triforce of Courage to protect me.", + "If you don't hear from me in a month, send Link.", + "Enough! My ship sails in the morning.", + "I wonder what's for dinner.", + "You've saved me!", + "After you've scrubbed all the floors in Hyrule, then we can talk about mercy! Take him away!", + "Waaaahahahohohahahahahahaha" +}; + +char* SetQuote() { + srand(time(NULL)); + int randomQuote = rand() % 11; + return quotes[randomQuote]; +} + +// Note: In other rom versions this function also updates unk_1D4, coverAlpha, addAlpha, visibleDuration to calculate +// the fade-in/fade-out + the duration of the n64 logo animation +void Title_Calc(TitleContext* this) { +#ifdef NORMAL_GAMEPLAY + if ((this->coverAlpha == 0) && (this->visibleDuration != 0)) { + this->visibleDuration--; + this->unk_1D4--; + if (this->unk_1D4 == 0) { + this->unk_1D4 = 400; + } + } else { + this->coverAlpha += this->addAlpha; + if (this->coverAlpha <= 0) { + this->coverAlpha = 0; + this->addAlpha = 3; + } else if (this->coverAlpha >= 0xFF) { + this->coverAlpha = 0xFF; + this->exit = 1; + } + } + this->uls = this->ult & 0x7F; + this->ult++; + + if (gSkipLogoTest || gLoadFileSelect) { + this->exit = 1; + } + +#else + this->exit = 1; +#endif +} + +void Title_SetupView(TitleContext* this, f32 x, f32 y, f32 z) { + View* view = &this->view; + Vec3f eye; + Vec3f lookAt; + Vec3f up; + + eye.x = x; + eye.y = y; + eye.z = z; + up.x = up.z = 0.0f; + lookAt.x = lookAt.y = lookAt.z = 0.0f; + up.y = 1.0f; + + func_800AA460(view, 30.0f, 10.0f, 12800.0f); + func_800AA358(view, &eye, &lookAt, &up); + func_800AAA50(view, 0xF); +} + +void Title_Draw(TitleContext* this) { + static s16 sTitleRotY = 0; + static Lights1 sTitleLights = gdSPDefLights1(0x64, 0x64, 0x64, 0xFF, 0xFF, 0xFF, 0x45, 0x45, 0x45); + + u16 y; + u16 idx; + s32 pad1; + Vec3f v3; + Vec3f v1; + Vec3f v2; + s32 pad2[2]; + + OPEN_DISPS(this->state.gfxCtx, "../z_title.c", 395); + + v3.x = 69; + v3.y = 69; + v3.z = 69; + v2.x = -4949.148; + v2.y = 4002.5417; + v1.x = 0; + v1.y = 0; + v1.z = 0; + v2.z = 1119.0837; + + char* n64LogoTex = ResourceMgr_LoadTexByName(nintendo_rogo_static_Tex_000000); + + func_8002EABC(&v1, &v2, &v3, this->state.gfxCtx); + gSPSetLights1(POLY_OPA_DISP++, sTitleLights); + Title_SetupView(this, 0, 150.0, 300.0); + func_80093D18(this->state.gfxCtx); + Matrix_Translate(-53.0, -5.0, 0, MTXMODE_NEW); + Matrix_Scale(1.0, 1.0, 1.0, MTXMODE_APPLY); + Matrix_RotateZYX(0, sTitleRotY, 0, MTXMODE_APPLY); + + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(this->state.gfxCtx, "../z_title.c", 424), G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, gNintendo64LogoDL); + func_800944C4(this->state.gfxCtx); + gDPPipeSync(POLY_OPA_DISP++); + gDPSetCycleType(POLY_OPA_DISP++, G_CYC_2CYCLE); + gDPSetRenderMode(POLY_OPA_DISP++, G_RM_XLU_SURF2, G_RM_OPA_CI | CVG_DST_WRAP); + gDPSetCombineLERP(POLY_OPA_DISP++, TEXEL1, PRIMITIVE, ENV_ALPHA, TEXEL0, 0, 0, 0, TEXEL0, PRIMITIVE, ENVIRONMENT, + COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 170, 255, 255, 255); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 255, 128); + + gDPLoadMultiBlock(POLY_OPA_DISP++, ResourceMgr_LoadTexByName(nintendo_rogo_static_Tex_001800), 0x100, 1, G_IM_FMT_I, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 5, 5, 2, 11); + + for (idx = 0, y = 94; idx < 16; idx++, y += 2) + { + gDPLoadTextureBlock(POLY_OPA_DISP++, &n64LogoTex[0x180 * idx], G_IM_FMT_I, + G_IM_SIZ_8b, 192, 2, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetTileSize(POLY_OPA_DISP++, 1, this->uls, (this->ult & 0x7F) - idx * 4, 0, 0); + gSPTextureRectangle(POLY_OPA_DISP++, 388, y << 2, 1156, (y + 2) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } + + Environment_FillScreen(this->state.gfxCtx, 0, 0, 0, (s16)this->coverAlpha, FILL_SCREEN_XLU); + + sTitleRotY += 300; + + CLOSE_DISPS(this->state.gfxCtx, "../z_title.c", 483); +} + +void Title_Main(GameState* thisx) { + TitleContext* this = (TitleContext*)thisx; + + OPEN_DISPS(this->state.gfxCtx, "../z_title.c", 494); + + gSPSegment(POLY_OPA_DISP++, 0, NULL); + gSPSegment(POLY_OPA_DISP++, 1, this->staticSegment); + func_80095248(this->state.gfxCtx, 0, 0, 0); + Title_Calc(this); + Title_Draw(this); + + if (1) { + Gfx* gfx = POLY_OPA_DISP; + s32 pad; + + Title_PrintBuildInfo(&gfx); + POLY_OPA_DISP = gfx; + } + + if (this->exit) { + gSaveContext.seqId = (u8)NA_BGM_DISABLED; + gSaveContext.natureAmbienceId = 0xFF; + gSaveContext.gameMode = 1; + this->state.running = false; + + if (gLoadFileSelect) + SET_NEXT_GAMESTATE(&this->state, FileChoose_Init, FileChooseContext); + else + SET_NEXT_GAMESTATE(&this->state, Opening_Init, OpeningContext); + } + + CLOSE_DISPS(this->state.gfxCtx, "../z_title.c", 541); +} + +void Title_Destroy(GameState* thisx) { + TitleContext* this = (TitleContext*)thisx; + + Sram_InitSram(&this->state, &this->sramCtx); +} + +void Title_Init(GameState* thisx) { + //u32 size = 0; + TitleContext* this = (TitleContext*)thisx; + + quote = SetQuote(); + + //this->staticSegment = GameState_Alloc(&this->state, size, "../z_title.c", 611); + osSyncPrintf("z_title.c\n"); + //ASSERT(this->staticSegment != NULL, "this->staticSegment != NULL", "../z_title.c", 614); + + //ResourceMgr_CacheDirectory("nintendo_rogo_static*"); + + R_UPDATE_RATE = 1; + Matrix_Init(&this->state); + View_Init(&this->view, this->state.gfxCtx); + this->state.main = Title_Main; + this->state.destroy = Title_Destroy; + this->exit = false; + gSaveContext.fileNum = 0xFF; + Sram_Alloc(&this->state, &this->sramCtx); + this->ult = 0; + this->unk_1D4 = 0x14; + this->coverAlpha = 255; + this->addAlpha = -3; + this->visibleDuration = 0x3C; +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c new file mode 100644 index 000000000..46be1154f --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_collect.c @@ -0,0 +1,671 @@ +#include "z_kaleido_scope.h" +#include "textures/parameter_static/parameter_static.h" +#include "textures/icon_item_static/icon_item_static.h" + +extern const char* digitTextures[]; + +void KaleidoScope_DrawQuestStatus(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { + static s16 D_8082A070[][4] = { + { 255, 0, 0, 255 }, + { 255, 70, 0, 150 }, + { 255, 70, 0, 150 }, + { 255, 0, 0, 255 }, + }; + static s16 D_8082A090[][3] = { + { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, + { 0, 60, 0 }, { 90, 0, 0 }, { 0, 40, 110 }, { 80, 40, 0 }, { 70, 0, 90 }, { 90, 90, 0 }, + }; + static s16 D_8082A0D8[] = { 255, 255, 255, 255, 255, 255 }; + static s16 D_8082A0E4[] = { 255, 255, 255, 255, 255, 255 }; + static s16 D_8082A0F0[] = { 150, 150, 150, 150, 150, 150 }; + static s16 D_8082A0FC = 20; + static s16 D_8082A100 = 0; + static s16 D_8082A104 = 0; + static s16 D_8082A108 = 0; + static s16 D_8082A10C = 0; + static s16 D_8082A110 = 0; + static s16 D_8082A114 = 20; + static s16 D_8082A118 = 0; + static s16 D_8082A11C = 0; + static s16 D_8082A120 = 0; + static u8 D_8082A124[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + static void* D_8082A130[] = { + gOcarinaATex, gOcarinaCDownTex, gOcarinaCRightTex, gOcarinaCLeftTex, gOcarinaCUpTex, + }; + static u16 D_8082A144[] = { + 0xFFCC, 0xFFCC, 0xFFCC, 0xFFCC, 0xFFCC, + }; + static s16 D_8082A150[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + static s16 D_8082A164[] = { + 150, 255, 100, 255, 255, 255, 255, 255, 255, 255, 255, 255, + }; + static s16 D_8082A17C[] = { + 255, 80, 150, 160, 100, 240, 255, 255, 255, 255, 255, 255, + }; + static s16 D_8082A194[] = { + 100, 40, 255, 0, 255, 100, 255, 255, 255, 255, 255, 255, + }; + static s8 D_8082A1AC[][4] = { + { 0x05, 0x01, 0x05, 0xFE }, { 0x00, 0x02, 0x02, 0xFE }, { 0xFF, 0x13, 0x03, 0x01 }, { 0x04, 0x02, 0x11, 0x02 }, + { 0x05, 0x03, 0x18, 0x05 }, { 0xFF, 0xFF, 0x04, 0x00 }, { 0x0C, 0xFF, 0xFD, 0x07 }, { 0x0D, 0xFF, 0x06, 0x08 }, + { 0x0E, 0xFF, 0x07, 0x09 }, { 0x0F, 0xFF, 0x08, 0x0A }, { 0x10, 0xFF, 0x09, 0x0B }, { 0x11, 0xFF, 0x0A, 0x12 }, + { 0x17, 0x06, 0xFD, 0x0D }, { 0x17, 0x07, 0x0C, 0x0E }, { 0x17, 0x08, 0x0D, 0x0F }, { 0x18, 0x09, 0x0E, 0x10 }, + { 0x18, 0x0A, 0x0F, 0x11 }, { 0x18, 0x0B, 0x10, 0x03 }, { 0x02, 0xFF, 0x0B, 0x13 }, { 0x02, 0xFF, 0x12, 0x14 }, + { 0x02, 0xFF, 0x13, 0xFE }, { 0xFF, 0x17, 0xFD, 0x16 }, { 0xFF, 0x17, 0x15, 0x18 }, { 0x15, 0x0C, 0xFD, 0x18 }, + { 0xFF, 0x10, 0x16, 0x04 }, { 0x00, 0x00, 0x00, 0x00 }, + }; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Input* input = &globalCtx->state.input[0]; + s16 sp226; + s16 sp224; + s16 sp222; + s16 sp220; + s16 phi_s0; + s16 phi_s3; + s16 sp21A; + s16 sp218; + s16 sp216; + s16 pad1; + s16 phi_v1; + s16 pad2; + s16 phi_s0_2; + s16 sp208[3]; + + OPEN_DISPS(gfxCtx, "../z_kaleido_collect.c", 248); + + if ((!pauseCtx->unk_1E4 || (pauseCtx->unk_1E4 == 5) || (pauseCtx->unk_1E4 == 8)) && + (pauseCtx->pageIndex == PAUSE_QUEST)) { + pauseCtx->cursorColorSet = 0; + + if (pauseCtx->cursorSpecialPos == 0) { + pauseCtx->nameColorSet = 0; + + if ((pauseCtx->state != 6) || ((pauseCtx->stickRelX == 0) && (pauseCtx->stickRelY == 0))) { + sp216 = pauseCtx->cursorSlot[PAUSE_QUEST]; + } else { + phi_s3 = pauseCtx->cursorPoint[PAUSE_QUEST]; + + if (pauseCtx->stickRelX < -30) { + phi_s0 = D_8082A1AC[phi_s3][2]; + if (phi_s0 == -3) { + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + pauseCtx->unk_1E4 = 0; + } else { + while (phi_s0 >= 0) { + if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { + break; + } + phi_s0 = D_8082A1AC[phi_s0][2]; + } + } + } else if (pauseCtx->stickRelX > 30) { + phi_s0 = D_8082A1AC[phi_s3][3]; + if (phi_s0 == -2) { + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + pauseCtx->unk_1E4 = 0; + } else { + while (phi_s0 >= 0) { + if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { + break; + } + phi_s0 = D_8082A1AC[phi_s0][3]; + } + } + } + + if (pauseCtx->stickRelY < -30) { + phi_s0 = D_8082A1AC[phi_s3][1]; + while (phi_s0 >= 0) { + if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { + break; + } + phi_s0 = D_8082A1AC[phi_s0][1]; + } + } else if (pauseCtx->stickRelY > 30) { + phi_s0 = D_8082A1AC[phi_s3][0]; + while (phi_s0 >= 0) { + if ((s16)KaleidoScope_UpdateQuestStatusPoint(pauseCtx, phi_s0) != 0) { + break; + } + phi_s0 = D_8082A1AC[phi_s0][0]; + } + } + + if (phi_s3 != pauseCtx->cursorPoint[PAUSE_QUEST]) { + pauseCtx->unk_1E4 = 0; + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + if (pauseCtx->cursorPoint[PAUSE_QUEST] != 0x18) { + if (CHECK_QUEST_ITEM(pauseCtx->cursorPoint[PAUSE_QUEST])) { + if (pauseCtx->cursorPoint[PAUSE_QUEST] < 6) { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x66; + osSyncPrintf("000 ccc=%d\n", phi_s0_2); + } else if (pauseCtx->cursorPoint[PAUSE_QUEST] < 0x12) { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x54; + osSyncPrintf("111 ccc=%d\n", phi_s0_2); + } else { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x5A; + osSyncPrintf("222 ccc=%d (%d, %d, %d)\n", phi_s0_2, pauseCtx->cursorPoint[PAUSE_QUEST], + 0x12, 0x6C); + } + } else { + phi_s0_2 = PAUSE_ITEM_NONE; + osSyncPrintf("999 ccc=%d (%d, %d)\n", PAUSE_ITEM_NONE, pauseCtx->cursorPoint[PAUSE_QUEST], + 0x18); + } + } else { + if ((gSaveContext.inventory.questItems & 0xF0000000) != 0) { + phi_s0_2 = 0x72; + } else { + phi_s0_2 = PAUSE_ITEM_NONE; + } + osSyncPrintf("888 ccc=%d (%d, %d, %x)\n", phi_s0_2, pauseCtx->cursorPoint[PAUSE_QUEST], 0x72, + gSaveContext.inventory.questItems & 0xF0000000); + } + + sp216 = pauseCtx->cursorPoint[PAUSE_QUEST]; + pauseCtx->cursorItem[pauseCtx->pageIndex] = phi_s0_2; + pauseCtx->cursorSlot[pauseCtx->pageIndex] = sp216; + } + + KaleidoScope_SetCursorVtx(pauseCtx, sp216 * 4, pauseCtx->questVtx); + + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->cursorSpecialPos == 0)) { + if ((sp216 >= QUEST_SONG_MINUET) && (sp216 < QUEST_KOKIRI_EMERALD)) { + if (CHECK_QUEST_ITEM(pauseCtx->cursorPoint[PAUSE_QUEST])) { + sp216 = pauseCtx->cursorSlot[PAUSE_QUEST]; + pauseCtx->ocarinaSongIdx = gOcarinaSongItemMap[sp216 - QUEST_SONG_MINUET]; + D_8082A120 = 10; + + for (phi_s3 = 0; phi_s3 < 8; phi_s3++) { + D_8082A124[phi_s3] = 0xFF; + D_8082A150[phi_s3] = 0; + } + + D_8082A11C = 0; + Audio_OcaSetInstrument(1); + func_800ECC04((1 << pauseCtx->ocarinaSongIdx) + 0x8000); + pauseCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + pauseCtx->ocarinaStaff->pos = 0; + pauseCtx->ocarinaStaff->state = 0xFF; + VREG(21) = -62; + VREG(22) = -56; + VREG(23) = -49; + VREG(24) = -46; + VREG(25) = -41; + pauseCtx->unk_1E4 = 8; + Audio_OcaSetInstrument(0); + } + } + } else if (pauseCtx->unk_1E4 == 5) { + if ((pauseCtx->stickRelX != 0) || (pauseCtx->stickRelY != 0)) { + pauseCtx->unk_1E4 = 0; + Audio_OcaSetInstrument(0); + } + } else if (pauseCtx->unk_1E4 == 8) { + if (CHECK_BTN_ALL(input->press.button, BTN_A) && (sp216 >= QUEST_SONG_MINUET) && + (sp216 < QUEST_KOKIRI_EMERALD)) { + pauseCtx->unk_1E4 = 9; + D_8082A120 = 10; + } + } + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + if (pauseCtx->stickRelX > 30) { + pauseCtx->cursorPoint[PAUSE_QUEST] = 0x15; + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + sp216 = pauseCtx->cursorPoint[PAUSE_QUEST]; + KaleidoScope_SetCursorVtx(pauseCtx, sp216 * 4, pauseCtx->questVtx); + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (CHECK_QUEST_ITEM(pauseCtx->cursorPoint[PAUSE_QUEST])) { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x5A; + } else { + phi_s0_2 = PAUSE_ITEM_NONE; + } + sp216 = pauseCtx->cursorPoint[PAUSE_QUEST]; + pauseCtx->cursorItem[pauseCtx->pageIndex] = phi_s0_2; + pauseCtx->cursorSlot[pauseCtx->pageIndex] = sp216; + } + } else { + if (pauseCtx->stickRelX < -30) { + pauseCtx->cursorPoint[PAUSE_QUEST] = 0; + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + sp216 = pauseCtx->cursorPoint[PAUSE_QUEST]; + KaleidoScope_SetCursorVtx(pauseCtx, sp216 * 4, pauseCtx->questVtx); + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + if (CHECK_QUEST_ITEM(pauseCtx->cursorPoint[PAUSE_QUEST])) { + if (pauseCtx->cursorPoint[PAUSE_QUEST] < 6) { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x66; + } else if (pauseCtx->cursorPoint[PAUSE_QUEST] < 0xC) { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x4E; + } else { + phi_s0_2 = pauseCtx->cursorPoint[PAUSE_QUEST] + 0x69; + } + } else { + phi_s0_2 = PAUSE_ITEM_NONE; + } + sp216 = pauseCtx->cursorPoint[PAUSE_QUEST]; + pauseCtx->cursorItem[pauseCtx->pageIndex] = phi_s0_2; + pauseCtx->cursorSlot[pauseCtx->pageIndex] = sp216; + } + } + + } else { + if (pauseCtx->unk_1E4 == 9) { + pauseCtx->cursorColorSet = 8; + + if (--D_8082A120 == 0) { + for (phi_s3 = 0; phi_s3 < 8; phi_s3++) { + D_8082A124[phi_s3] = 0xFF; + D_8082A150[phi_s3] = 0; + } + + D_8082A11C = 0; + VREG(21) = -62; + VREG(22) = -56; + VREG(23) = -49; + VREG(24) = -46; + VREG(25) = -41; + sp216 = pauseCtx->cursorSlot[PAUSE_QUEST]; + Audio_OcaSetInstrument(1); + Audio_OcaSetInstrument(1); + pauseCtx->ocarinaSongIdx = gOcarinaSongItemMap[sp216 - QUEST_SONG_MINUET]; + Audio_OcaSetSongPlayback(pauseCtx->ocarinaSongIdx + 1, 1); + pauseCtx->unk_1E4 = 2; + pauseCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + pauseCtx->ocarinaStaff->pos = 0; + sp216 = pauseCtx->cursorSlot[PAUSE_QUEST]; + KaleidoScope_SetCursorVtx(pauseCtx, sp216 * 4, pauseCtx->questVtx); + } + } else { + sp216 = pauseCtx->cursorSlot[PAUSE_QUEST]; + KaleidoScope_SetCursorVtx(pauseCtx, sp216 * 4, pauseCtx->questVtx); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + D_8082A0FC--; + + for (sp218 = 0, sp21A = 0; sp218 < 6; sp218++, sp21A += 4) { + if ((D_8082A100 != 1) && (D_8082A100 != 3)) { + phi_v1 = (D_8082A100 != 0) ? sp218 + 6 : sp218; + + if (D_8082A0FC != 0) { + sp226 = ABS(D_8082A0D8[sp218] - D_8082A090[phi_v1][0]) / D_8082A0FC; + sp224 = ABS(D_8082A0E4[sp218] - D_8082A090[phi_v1][1]) / D_8082A0FC; + sp222 = ABS(D_8082A0F0[sp218] - D_8082A090[phi_v1][2]) / D_8082A0FC; + if (D_8082A0D8[sp218] >= D_8082A090[phi_v1][0]) { + D_8082A0D8[sp218] -= sp226; + } else { + D_8082A0D8[sp218] += sp226; + } + if (D_8082A0E4[sp218] >= D_8082A090[phi_v1][1]) { + D_8082A0E4[sp218] -= sp224; + } else { + D_8082A0E4[sp218] += sp224; + } + if (D_8082A0F0[sp218] >= D_8082A090[phi_v1][2]) { + D_8082A0F0[sp218] -= sp222; + } else { + D_8082A0F0[sp218] += sp222; + } + } else { + D_8082A0D8[sp218] = D_8082A090[phi_v1][0]; + D_8082A0E4[sp218] = D_8082A090[phi_v1][1]; + D_8082A0F0[sp218] = D_8082A090[phi_v1][2]; + } + } + + if (CHECK_QUEST_ITEM(sp218)) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, D_8082A0D8[sp218], D_8082A0E4[sp218], D_8082A0F0[sp218], 0); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + + KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gItemIcons[ITEM_MEDALLION_FOREST + sp218], 24, 24, 0); + } + } + + if (D_8082A0FC == 0) { + D_8082A0FC = ZREG(61 + D_8082A100); + if (++D_8082A100 >= 4) { + D_8082A100 = 0; + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 255); + + gDPLoadTextureBlock(POLY_KAL_DISP++, gSongNoteTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + for (sp218 = 0; sp218 < QUEST_KOKIRI_EMERALD - QUEST_SONG_MINUET; sp218++, sp21A += 4) { + if (CHECK_QUEST_ITEM(sp218 + QUEST_SONG_MINUET)) { + if ((sp218 + QUEST_SONG_MINUET) == sp216) { + pauseCtx->questVtx[sp21A + 0].v.ob[0] = pauseCtx->questVtx[sp21A + 2].v.ob[0] = + pauseCtx->questVtx[sp21A + 0].v.ob[0] - 2; + + pauseCtx->questVtx[sp21A + 1].v.ob[0] = pauseCtx->questVtx[sp21A + 3].v.ob[0] = + pauseCtx->questVtx[sp21A + 1].v.ob[0] + 4; + + pauseCtx->questVtx[sp21A + 0].v.ob[1] = pauseCtx->questVtx[sp21A + 1].v.ob[1] = + pauseCtx->questVtx[sp21A + 0].v.ob[1] + 2; + + pauseCtx->questVtx[sp21A + 2].v.ob[1] = pauseCtx->questVtx[sp21A + 3].v.ob[1] = + pauseCtx->questVtx[sp21A + 2].v.ob[1] - 4; + } + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, D_8082A164[sp218], D_8082A17C[sp218], D_8082A194[sp218], + pauseCtx->alpha); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 255); + + for (sp218 = 0; sp218 < 3; sp218++, sp21A += 4) { + if (CHECK_QUEST_ITEM(sp218 + 0x12)) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gItemIcons[ITEM_KOKIRI_EMERALD + sp218], 24, 24, 0); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + for (sp218 = 0; sp218 < 3; sp218++, sp21A += 4) { + if (CHECK_QUEST_ITEM(sp218 + 0x15)) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gItemIcons[ITEM_STONE_OF_AGONY + sp218], 24, 24, 0); + } + } + + sp226 = ABS(D_8082A104 - D_8082A070[D_8082A118][0]) / D_8082A114; + sp224 = ABS(D_8082A108 - D_8082A070[D_8082A118][1]) / D_8082A114; + sp222 = ABS(D_8082A10C - D_8082A070[D_8082A118][2]) / D_8082A114; + sp220 = ABS(D_8082A110 - D_8082A070[D_8082A118][3]) / D_8082A114; + if (D_8082A104 >= D_8082A070[D_8082A118][0]) { + D_8082A104 -= sp226; + } else { + D_8082A104 += sp226; + } + if (D_8082A108 >= D_8082A070[D_8082A118][1]) { + D_8082A108 -= sp224; + } else { + D_8082A108 += sp224; + } + if (D_8082A10C >= D_8082A070[D_8082A118][2]) { + D_8082A10C -= sp222; + } else { + D_8082A10C += sp222; + } + if (D_8082A110 >= D_8082A070[D_8082A118][3]) { + D_8082A110 -= sp220; + } else { + D_8082A110 += sp220; + } + + if (--D_8082A114 == 0) { + D_8082A104 = D_8082A070[D_8082A118][0]; + D_8082A108 = D_8082A070[D_8082A118][1]; + D_8082A10C = D_8082A070[D_8082A118][2]; + D_8082A110 = D_8082A070[D_8082A118][3]; + D_8082A114 = ZREG(24 + D_8082A118); + if (++D_8082A118 >= 4) { + D_8082A118 = 0; + } + } + + if ((gSaveContext.inventory.questItems >> 0x1C) != 0) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + if ((pauseCtx->state == 4) || (pauseCtx->state == 0x12)) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, D_8082A070[0][0], D_8082A070[0][1], D_8082A070[0][2], + pauseCtx->alpha); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, D_8082A104, D_8082A108, D_8082A10C, D_8082A110); + } + + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 255); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8( + POLY_KAL_DISP, gItemIcons[0x79 + (((gSaveContext.inventory.questItems & 0xF0000000) & 0xF0000000) >> 0x1C)], + 48, 48, 0); + } + + if (pauseCtx->state == 6) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + sp21A += 4; + if ((pauseCtx->cursorSpecialPos == 0) && (sp216 >= 6) && (sp216 < 0x12)) { + if ((pauseCtx->unk_1E4 < 3) || (pauseCtx->unk_1E4 == 5) || (pauseCtx->unk_1E4 == 8)) { + if (pauseCtx->cursorItem[pauseCtx->pageIndex] != PAUSE_ITEM_NONE) { + pauseCtx->cursorColorSet = 8; + if ((pauseCtx->unk_1E4 >= 2) && (pauseCtx->unk_1E4 < 7)) { + pauseCtx->cursorColorSet = 0; + } + } + } + } + + if (pauseCtx->unk_1E4 == 2) { + pauseCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + + if (pauseCtx->ocarinaStaff->pos != 0) { + if (D_8082A11C + 1 == pauseCtx->ocarinaStaff->pos) { + D_8082A11C++; + D_8082A124[pauseCtx->ocarinaStaff->pos - 1] = pauseCtx->ocarinaStaff->noteIdx; + } + + for (sp218 = 0, phi_s3 = 0; sp218 < 8; sp218++, phi_s3 += 4, sp21A += 4) { + if (D_8082A124[sp218] == 0xFF) { + break; + } + + if (D_8082A150[sp218] != 255) { + D_8082A150[sp218] += VREG(50); + if (D_8082A150[sp218] >= 255) { + D_8082A150[sp218] = 255; + } + } + + pauseCtx->questVtx[sp21A + 0].v.ob[1] = pauseCtx->questVtx[sp21A + 1].v.ob[1] = + VREG(21 + D_8082A124[sp218]); + + pauseCtx->questVtx[sp21A + 2].v.ob[1] = pauseCtx->questVtx[sp21A + 3].v.ob[1] = + pauseCtx->questVtx[sp21A + 0].v.ob[1] - 12; + + gDPPipeSync(POLY_KAL_DISP++); + + if (D_8082A124[sp218] == 0) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 80, 255, 150, D_8082A150[sp218]); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 50, D_8082A150[sp218]); + } + + gDPSetEnvColor(POLY_KAL_DISP++, 10, 10, 10, 0); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, D_8082A130[D_8082A124[sp218]], G_IM_FMT_IA, G_IM_SIZ_8b, 16, + 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + } + } else if (((pauseCtx->unk_1E4 >= 4) && (pauseCtx->unk_1E4 <= 6)) || (pauseCtx->unk_1E4 == 8)) { + sp224 = pauseCtx->ocarinaSongIdx; + sp226 = gOcarinaSongNotes[sp224].len; + + for (sp218 = sp21A, phi_s3 = 0; phi_s3 < sp226; phi_s3++, sp21A += 4) { + pauseCtx->questVtx[sp21A + 0].v.ob[1] = pauseCtx->questVtx[sp21A + 1].v.ob[1] = + VREG(21 + gOcarinaSongNotes[sp224].notesIdx[phi_s3]); + + pauseCtx->questVtx[sp21A + 2].v.ob[1] = pauseCtx->questVtx[sp21A + 3].v.ob[1] = + pauseCtx->questVtx[sp21A + 0].v.ob[1] - 12; + + gDPPipeSync(POLY_KAL_DISP++); + + if (pauseCtx->unk_1E4 == 8) { + if (gOcarinaSongNotes[sp224].notesIdx[phi_s3] == 0) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 80, 255, 150, 200); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 50, 200); + } + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 150, 150, 150, 150); + } + + gDPSetEnvColor(POLY_KAL_DISP++, 10, 10, 10, 0); + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, D_8082A130[gOcarinaSongNotes[sp224].notesIdx[phi_s3]], G_IM_FMT_IA, + G_IM_SIZ_8b, 16, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + + if (pauseCtx->unk_1E4 != 8) { + pauseCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + + if (pauseCtx->ocarinaStaff->pos != 0) { + if (D_8082A11C == (pauseCtx->ocarinaStaff->pos - 1)) { + if ((pauseCtx->ocarinaStaff->noteIdx >= OCARINA_NOTE_A) && + (pauseCtx->ocarinaStaff->noteIdx <= OCARINA_NOTE_C_UP)) { + D_8082A124[pauseCtx->ocarinaStaff->pos - 1] = pauseCtx->ocarinaStaff->noteIdx; + D_8082A124[pauseCtx->ocarinaStaff->pos] = 0xFF; + D_8082A11C++; + } + } + } + + sp21A = sp218 + 32; + phi_s3 = 0; + for (; phi_s3 < 8; phi_s3++, sp21A += 4) { + if (D_8082A124[phi_s3] == 0xFF) { + continue; + } + + if (D_8082A150[phi_s3] != 255) { + D_8082A150[phi_s3] += VREG(50); + if (D_8082A150[phi_s3] >= 255) { + D_8082A150[phi_s3] = 255; + } + } + pauseCtx->questVtx[sp21A + 0].v.ob[1] = pauseCtx->questVtx[sp21A + 1].v.ob[1] = + VREG(21 + D_8082A124[phi_s3]); + + pauseCtx->questVtx[sp21A + 2].v.ob[1] = pauseCtx->questVtx[sp21A + 3].v.ob[1] = + pauseCtx->questVtx[sp21A + 0].v.ob[1] - 12; + + gDPPipeSync(POLY_KAL_DISP++); + + if (D_8082A124[phi_s3] == 0) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 80, 255, 150, D_8082A150[phi_s3]); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 50, D_8082A150[phi_s3]); + } + + gDPSetEnvColor(POLY_KAL_DISP++, 10, 10, 10, 0); + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[sp21A], 4, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, D_8082A130[D_8082A124[phi_s3]], G_IM_FMT_IA, G_IM_SIZ_8b, 16, + 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, + G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + + if (pauseCtx->unk_1E4 == 4) { + for (phi_s3 = 0; phi_s3 < 8; phi_s3++) { + D_8082A124[phi_s3] = 0xFF; + D_8082A150[phi_s3] = 0; + } + + D_8082A11C = 0; + Audio_OcaSetInstrument(1); + func_800ECC04((1 << pauseCtx->ocarinaSongIdx) + 0x8000); + pauseCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + pauseCtx->ocarinaStaff->pos = 0; + pauseCtx->ocarinaStaff->state = 0xFE; + pauseCtx->unk_1E4 = 5; + } + } + } + } + + if (CHECK_QUEST_ITEM(QUEST_SKULL_TOKEN)) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); + + sp208[0] = sp208[1] = 0; + sp208[2] = gSaveContext.inventory.gsTokens; + + while (sp208[2] >= 100) { + sp208[0]++; + sp208[2] -= 100; + } + + while (sp208[2] >= 10) { + sp208[1]++; + sp208[2] -= 10; + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->questVtx[164], 24, 0); + + for (phi_s3 = 0, sp218 = 0, sp21A = 0; phi_s3 < 2; phi_s3++) { + if (phi_s3 == 0) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 0, pauseCtx->alpha); + } else if (gSaveContext.inventory.gsTokens == 100) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 200, 50, 50, pauseCtx->alpha); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + } + + phi_s0 = 0; + for (sp21A = 0; sp21A < 3; sp21A++, sp218 += 4) { + if ((sp21A >= 2) || (sp208[sp21A] != 0) || (phi_s0 != 0)) { + gDPLoadTextureBlock(POLY_KAL_DISP++, digitTextures[sp208[sp21A]], G_IM_FMT_I, + G_IM_SIZ_8b, 8, 16, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, sp218, sp218 + 2, sp218 + 3, sp218 + 1, 0); + + phi_s0 = 1; + } + } + } + } + + CLOSE_DISPS(gfxCtx, "../z_kaleido_collect.c", 863); +} + +s32 KaleidoScope_UpdateQuestStatusPoint(PauseContext* pauseCtx, s32 point) { + pauseCtx->cursorPoint[PAUSE_QUEST] = point; + + return 1; +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c new file mode 100644 index 000000000..87c9629dc --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -0,0 +1,650 @@ +#include "z_kaleido_scope.h" +#include "textures/parameter_static/parameter_static.h" + +// Positions of each input section in the editor +static u16 sSectionPositions[][2] = { + { 64, 15 }, { 144, 15 }, { 170, 15 }, { 78, 35 }, { 104, 35 }, { 130, 35 }, { 156, 35 }, { 182, 35 }, + { 208, 35 }, { 78, 50 }, { 104, 50 }, { 130, 50 }, { 156, 50 }, { 182, 50 }, { 208, 50 }, { 78, 65 }, + { 104, 65 }, { 130, 65 }, { 156, 65 }, { 182, 65 }, { 208, 65 }, { 78, 80 }, { 104, 80 }, { 130, 80 }, + { 156, 80 }, { 182, 80 }, { 208, 80 }, { 78, 98 }, { 88, 98 }, { 98, 98 }, { 108, 98 }, { 118, 98 }, + { 128, 98 }, { 138, 98 }, { 148, 98 }, { 158, 98 }, { 168, 98 }, { 178, 98 }, { 188, 98 }, { 198, 98 }, + { 208, 98 }, { 218, 98 }, { 228, 98 }, { 238, 98 }, { 78, 115 }, { 90, 115 }, { 102, 115 }, { 114, 115 }, + { 126, 115 }, { 138, 115 }, { 150, 115 }, { 162, 115 }, { 202, 115 }, { 214, 115 }, { 226, 115 }, { 238, 115 }, + { 78, 132 }, { 90, 132 }, { 102, 132 }, { 114, 132 }, { 126, 132 }, { 138, 132 }, { 150, 132 }, { 162, 132 }, + { 174, 132 }, { 186, 132 }, { 198, 132 }, { 210, 132 }, { 78, 149 }, { 90, 149 }, { 102, 149 }, { 114, 149 }, + { 126, 149 }, { 138, 149 }, { 78, 166 }, { 90, 166 }, { 102, 166 }, { 114, 166 }, { 126, 166 }, { 138, 166 }, + { 150, 166 }, { 162, 166 }, { 174, 166 }, { 186, 166 }, { 198, 166 }, { 210, 166 }, { 210, 149 }, { 222, 149 }, + { 234, 149 }, { 78, 185 }, { 90, 185 }, { 145, 185 }, { 210, 185 }, +}; + +// First section of each row in the editor (starting from the top) +static u16 sRowFirstSections[] = { + 0x00, 0x03, 0x1B, 0x2C, 0x34, 0x38, 0x44, 0x4A, 0x56, 0x59, 0x5C, +}; + +// Maximum value of each upgrade type +static u8 sMaxUpgradeValues[] = { + 3, 3, 3, 2, 2, 3, 3, 3, +}; + +// Item ID corresponding to each slot, aside from bottles and trade items +static s16 sSlotItems[] = { + ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_BOW, ITEM_ARROW_FIRE, ITEM_DINS_FIRE, + ITEM_SLINGSHOT, ITEM_OCARINA_FAIRY, ITEM_BOMBCHU, ITEM_HOOKSHOT, ITEM_ARROW_ICE, ITEM_FARORES_WIND, + ITEM_BOOMERANG, ITEM_LENS, ITEM_BEAN, ITEM_HAMMER, ITEM_ARROW_LIGHT, ITEM_NAYRUS_LOVE, +}; + +void KaleidoScope_DrawDebugEditorText(Gfx** gfxp) { + GfxPrint printer; + s32 pad[2]; + + GfxPrint_Init(&printer); + GfxPrint_Open(&printer, *gfxp); + + GfxPrint_SetPos(&printer, 4, 2); + GfxPrint_SetColor(&printer, 255, 60, 0, 255); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "ルピー"); // "Rupee" + GfxPrint_SetPos(&printer, 15, 2); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "ハート"); // "Heart" + GfxPrint_SetPos(&printer, 26, 3); + GfxPrint_Printf(&printer, "%s", "/4"); + GfxPrint_SetPos(&printer, 4, 5); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "アイテム"); // "Item" + GfxPrint_SetPos(&printer, 4, 13); + GfxPrint_Printf(&printer, "%s", "KEY"); + GfxPrint_SetPos(&printer, 4, 15); + GfxPrint_Printf(&printer, "%s", GFXP_HIRAGANA "ソウビ"); // "Equipment" + GfxPrint_SetPos(&printer, 23, 14); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "ケン"); // "Sword" + GfxPrint_SetPos(&printer, 23, 15); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "タテ"); // "Shield" + GfxPrint_SetPos(&printer, 4, 17); + GfxPrint_Printf(&printer, "%s", "MAP"); + GfxPrint_SetPos(&printer, 4, 19); + GfxPrint_Printf(&printer, "%s", GFXP_HIRAGANA "フウイン"); // "Seal" + GfxPrint_SetPos(&printer, 20, 19); + GfxPrint_Printf(&printer, "%s", GFXP_HIRAGANA "セイレイセキ"); // "Spiritual Stone" + GfxPrint_SetPos(&printer, 4, 21); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "オカリナ"); // "Ocarina" + GfxPrint_SetPos(&printer, 4, 24); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "コレクト"); // "Collect" + GfxPrint_SetPos(&printer, 14, 24); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "キンスタ"); // "Skulltula" + GfxPrint_SetPos(&printer, 23, 24); + GfxPrint_Printf(&printer, "%s", GFXP_KATAKANA "カケラ"); // "Gold Token" + GfxPrint_SetPos(&printer, 28, 24); + GfxPrint_Printf(&printer, "%s", "/4"); + + *gfxp = GfxPrint_Close(&printer); + GfxPrint_Destroy(&printer); +} + +extern const char* digitTextures[]; + +void KaleidoScope_DrawDigit(GlobalContext* globalCtx, s32 digit, s32 rectLeft, s32 rectTop) { + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_debug.c", 208); + + gDPLoadTextureBlock(POLY_KAL_DISP++, digitTextures[digit], G_IM_FMT_I, G_IM_SIZ_8b, 8, 16, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSPTextureRectangle(POLY_KAL_DISP++, rectLeft << 2, rectTop << 2, (rectLeft + 8) << 2, (rectTop + 16) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_debug.c", 220); +} + +void KaleidoScope_DrawDebugEditor(GlobalContext* globalCtx) { + static s16 curSection = 0; + static s16 curRow = 0; + static s32 prevDBtnInput = 0; + static s32 heldDBtnTimer = 0; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Input* input = &globalCtx->state.input[0]; + Gfx* gfx; + Gfx* gfxRef; + s16 spD8[4]; + s16 slot; + s16 i; + s16 j; + s16 x; + s16 y; + s32 dBtnInput = input->cur.button & (BTN_DUP | BTN_DDOWN | BTN_DLEFT | BTN_DRIGHT); + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_debug.c", 402); + + pauseCtx->stickRelX = input->rel.stick_x; + pauseCtx->stickRelY = input->rel.stick_y; + + func_800944C4_KAL(globalCtx->state.gfxCtx); + + gDPSetRenderMode(POLY_KAL_DISP++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 0, 220); + gDPFillRectangle(POLY_KAL_DISP++, 24, 12, 298, 228); + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, 0, 0, 0, PRIMITIVE, TEXEL0, 0, PRIMITIVE, 0, 0, 0, 0, PRIMITIVE, TEXEL0, 0, + PRIMITIVE, 0); + + gfxRef = POLY_KAL_DISP; + gfx = Graph_GfxPlusOne(gfxRef); + gSPDisplayList(OVERLAY_DISP++, gfx); + + KaleidoScope_DrawDebugEditorText(&gfx); + + gSPEndDisplayList(gfx++); + Graph_BranchDlist(gfxRef, gfx); + POLY_KAL_DISP = gfx; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 0, 0, 255); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); + + // Current Health Quarter (X / 4) + KaleidoScope_DrawDigit(globalCtx, (gSaveContext.health % 0x10) / 4, 194, 15); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + + // Rupees + spD8[0] = spD8[1] = spD8[2] = 0; + spD8[3] = gSaveContext.rupees; + while (spD8[3] >= 1000) { + spD8[0]++; + spD8[3] -= 1000; + } + + while (spD8[3] >= 100) { + spD8[1]++; + spD8[3] -= 100; + } + + while (spD8[3] >= 10) { + spD8[2]++; + spD8[3] -= 10; + } + + for (i = 0, x = 68; i < 4; i++, x += 10) { + KaleidoScope_DrawDigit(globalCtx, spD8[i], x, 15); + } + + // Health capacity + spD8[2] = 0; + spD8[3] = gSaveContext.healthCapacity / 0x10; + while (spD8[3] >= 10) { + spD8[2]++; + spD8[3] -= 10; + } + + KaleidoScope_DrawDigit(globalCtx, spD8[2], 146, 15); + KaleidoScope_DrawDigit(globalCtx, spD8[3], 156, 15); + + // Health + spD8[2] = 0; + spD8[3] = gSaveContext.health / 0x10; + while (spD8[3] >= 10) { + spD8[2]++; + spD8[3] -= 10; + } + + KaleidoScope_DrawDigit(globalCtx, spD8[2], 172, 15); + KaleidoScope_DrawDigit(globalCtx, spD8[3], 182, 15); + + // Inventory + for (slot = 0, i = 0, y = 35; i < 4; i++, y += 15) { + for (j = 0, x = 78; j < 6; j++, slot++, x += 26) { + spD8[2] = 0; + + if ((slot <= SLOT_BOW) || (slot == SLOT_SLINGSHOT) || (slot == SLOT_BOMBCHU) || (slot == SLOT_BEAN)) { + spD8[3] = AMMO(gAmmoItems[slot]); + } else if (slot == SLOT_OCARINA) { + spD8[3] = gSaveContext.inventory.items[slot]; + } else { + spD8[3] = gSaveContext.inventory.items[slot]; + } + + if (spD8[3] != ITEM_NONE) { + while (spD8[3] >= 10) { + spD8[2]++; + spD8[3] -= 10; + } + } else { + spD8[2] = spD8[3] = 0; + } + + KaleidoScope_DrawDigit(globalCtx, spD8[2], x, y); + KaleidoScope_DrawDigit(globalCtx, spD8[3], x + 10, y); + } + } + + // Keys + for (spD8[1] = 78, i = 0; i < 17; i++) { + spD8[2] = 0; + + if ((spD8[3] = gSaveContext.inventory.dungeonKeys[i]) >= 0) { + while (spD8[3] >= 10) { + spD8[2]++; + spD8[3] -= 10; + } + } else { + spD8[2] = spD8[3] = 0; + } + + KaleidoScope_DrawDigit(globalCtx, spD8[3], spD8[1], 98); + spD8[1] += 10; + } + + // Upgrades + for (spD8[1] = 78, i = 0; i < 8; i++, spD8[1] += 12) { + KaleidoScope_DrawDigit(globalCtx, CUR_UPG_VALUE(i), spD8[1], 115); + } + + // Equipment + for (spD8[1] = 202, i = 0; i < 4; i++, spD8[1] += 12) { + KaleidoScope_DrawDigit(globalCtx, ALL_EQUIP_VALUE(i), spD8[1], 115); + } + + // Dungeon Items + for (spD8[1] = 78, i = 0; i < 12; i++, spD8[1] += 12) { + spD8[2] = gSaveContext.inventory.dungeonItems[i] & gEquipMasks[0]; + KaleidoScope_DrawDigit(globalCtx, spD8[2], spD8[1], 132); + } + + // Medallions + for (spD8[1] = 78, i = 0; i < 6; i++, spD8[1] += 12) { + spD8[2] = 0; + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST + i)) { + spD8[2] = 1; + } + KaleidoScope_DrawDigit(globalCtx, spD8[2], spD8[1], 149); + } + + // Spiritual Stones + for (spD8[1] = 210, i = 0; i < 3; i++, spD8[1] += 12) { + spD8[2] = 0; + if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD + i)) { + spD8[2] = 1; + } + KaleidoScope_DrawDigit(globalCtx, spD8[2], spD8[1], 149); + } + + // Songs + for (spD8[1] = 78, i = 0; i < 12; i++, spD8[1] += 12) { + spD8[2] = 0; + if (CHECK_QUEST_ITEM(QUEST_SONG_MINUET + i)) { + spD8[2] = 1; + } + KaleidoScope_DrawDigit(globalCtx, spD8[2], spD8[1], 166); + } + + // Other Quest Items + for (spD8[1] = 78, i = 0; i < 2; i++, spD8[1] += 12) { + spD8[2] = 0; + if (CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY + i)) { + spD8[2] = 1; + } + KaleidoScope_DrawDigit(globalCtx, spD8[2], spD8[1], 185); + } + + // GS Tokens + spD8[3] = gSaveContext.inventory.gsTokens; + spD8[1] = 0; + spD8[2] = 0; + while (spD8[3] >= 100) { + spD8[1]++; + spD8[3] -= 100; + } + + while (spD8[3] >= 10) { + spD8[2]++; + spD8[3] -= 10; + } + + KaleidoScope_DrawDigit(globalCtx, spD8[1], 145, 185); + KaleidoScope_DrawDigit(globalCtx, spD8[2], 155, 185); + KaleidoScope_DrawDigit(globalCtx, spD8[3], 165, 185); + + // Heart Pieces (X / 4) + KaleidoScope_DrawDigit(globalCtx, ((gSaveContext.inventory.questItems & 0xF0000000) & 0xF0000000) >> 0x1C, 210, + 185); + + // Handles navigating the menu to different sections with the D-Pad + // When the same direction is held, registers the input periodically based on a timer + if (dBtnInput == prevDBtnInput) { + heldDBtnTimer--; + if (heldDBtnTimer < 0) { + heldDBtnTimer = 1; + } else { + dBtnInput ^= prevDBtnInput; + } + } else { + prevDBtnInput = dBtnInput; + heldDBtnTimer = 16; + } + + if (CHECK_BTN_ANY(dBtnInput, BTN_DDOWN)) { + if ((u32)++curRow > 10) { + curRow = 0; + } + curSection = sRowFirstSections[curRow]; + } else if (CHECK_BTN_ANY(dBtnInput, BTN_DUP)) { + if (--curRow < 0) { + curRow = 22; + } + curSection = sRowFirstSections[curRow]; + } else if (CHECK_BTN_ANY(dBtnInput, BTN_DLEFT)) { + if (--curSection < 0) { + curSection = 0x5C; + } + } else if (CHECK_BTN_ANY(dBtnInput, BTN_DRIGHT)) { + if (++curSection > 0x5C) { + curSection = 0; + } + } + + // Handles the logic to change values based on the selected section + switch (curSection) { + case 0: + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + gSaveContext.rupees -= 100; + if (gSaveContext.rupees < 0) { + gSaveContext.rupees = 0; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + gSaveContext.rupees += 100; + if (gSaveContext.rupees >= 9999) { + gSaveContext.rupees = 9999; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + gSaveContext.rupees--; + if (gSaveContext.rupees < 0) { + gSaveContext.rupees = 0; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + gSaveContext.rupees++; + if (gSaveContext.rupees >= 9999) { + gSaveContext.rupees = 9999; + } + } + break; + + case 1: + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + gSaveContext.healthCapacity -= 0x10; + if (gSaveContext.healthCapacity < 0x30) { + gSaveContext.healthCapacity = 0x30; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || + CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + gSaveContext.healthCapacity += 0x10; + if (gSaveContext.healthCapacity >= 0x140) { + gSaveContext.healthCapacity = 0x140; + } + } + break; + + case 2: + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + Health_ChangeBy(globalCtx, -4); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + Health_ChangeBy(globalCtx, 4); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Health_ChangeBy(globalCtx, -0x10); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + Health_ChangeBy(globalCtx, 0x10); + } + break; + + case 0x5C: + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if ((((gSaveContext.inventory.questItems & 0xF0000000) & 0xF0000000) >> 0x1C) != 0) { + gSaveContext.inventory.questItems -= 0x10000000; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || + CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if ((gSaveContext.inventory.questItems & 0xF0000000) <= 0x40000000) { + gSaveContext.inventory.questItems += 0x10000000; + } + } + break; + + default: + if (curSection < 0x1B) { + i = curSection - 3; + if ((i <= SLOT_BOW) || (i == SLOT_SLINGSHOT) || (i == SLOT_BOMBCHU) || (i == SLOT_BEAN)) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Inventory_DeleteItem(gAmmoItems[i], SLOT(gAmmoItems[i])); + AMMO(gAmmoItems[i]) = 0; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (i != INV_CONTENT(gAmmoItems[i])) { + INV_CONTENT(gAmmoItems[i]) = gAmmoItems[i]; + } + AMMO(gAmmoItems[i])++; + if (AMMO(gAmmoItems[i]) > 99) { + AMMO(gAmmoItems[i]) = 99; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + AMMO(gAmmoItems[i])--; + if (AMMO(gAmmoItems[i]) < 0) { + AMMO(gAmmoItems[i]) = 0; + } + } + } else if (i == SLOT_OCARINA) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Inventory_DeleteItem(ITEM_OCARINA_FAIRY, SLOT(ITEM_OCARINA_FAIRY)); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_OCARINA_FAIRY; + } else if ((gSaveContext.inventory.items[i] >= ITEM_OCARINA_FAIRY) && + (gSaveContext.inventory.items[i] < ITEM_OCARINA_TIME)) { + gSaveContext.inventory.items[i]++; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_OCARINA_TIME; + } else if ((gSaveContext.inventory.items[i] > ITEM_OCARINA_FAIRY) && + (gSaveContext.inventory.items[i] <= ITEM_OCARINA_TIME)) { + gSaveContext.inventory.items[i]--; + } + } + } else if (i == SLOT_HOOKSHOT) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Inventory_DeleteItem(ITEM_HOOKSHOT, SLOT(ITEM_HOOKSHOT)); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_HOOKSHOT; + } else if ((gSaveContext.inventory.items[i] >= ITEM_HOOKSHOT) && + (gSaveContext.inventory.items[i] < ITEM_LONGSHOT)) { + gSaveContext.inventory.items[i]++; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_LONGSHOT; + } else if ((gSaveContext.inventory.items[i] > ITEM_HOOKSHOT) && + (gSaveContext.inventory.items[i] <= ITEM_LONGSHOT)) { + gSaveContext.inventory.items[i]--; + } + } + } else if (i == SLOT_TRADE_ADULT) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Inventory_DeleteItem(ITEM_POCKET_EGG, SLOT(ITEM_POCKET_EGG)); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_POCKET_EGG; + } else if ((gSaveContext.inventory.items[i] >= ITEM_POCKET_EGG) && + (gSaveContext.inventory.items[i] < ITEM_CLAIM_CHECK)) { + gSaveContext.inventory.items[i]++; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_CLAIM_CHECK; + } else if ((gSaveContext.inventory.items[i] > ITEM_POCKET_EGG) && + (gSaveContext.inventory.items[i] <= ITEM_CLAIM_CHECK)) { + gSaveContext.inventory.items[i]--; + } + } + } else if (i == SLOT_TRADE_CHILD) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Inventory_DeleteItem(ITEM_WEIRD_EGG, SLOT(ITEM_WEIRD_EGG)); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_WEIRD_EGG; + } else if ((gSaveContext.inventory.items[i] >= ITEM_WEIRD_EGG) && + (gSaveContext.inventory.items[i] < ITEM_SOLD_OUT)) { + gSaveContext.inventory.items[i]++; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_SOLD_OUT; + } else if ((gSaveContext.inventory.items[i] > ITEM_WEIRD_EGG) && + (gSaveContext.inventory.items[i] <= ITEM_SOLD_OUT)) { + gSaveContext.inventory.items[i]--; + } + } + } else if ((i >= SLOT_BOTTLE_1) && (i <= SLOT_BOTTLE_4)) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + Inventory_DeleteItem(ITEM_BOTTLE + i - SLOT_BOTTLE_1, SLOT(ITEM_BOTTLE) + i - SLOT_BOTTLE_1); + } else if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_BOTTLE; + } else if ((gSaveContext.inventory.items[i] >= ITEM_BOTTLE) && + (gSaveContext.inventory.items[i] <= ITEM_MILK_HALF)) { + gSaveContext.inventory.items[i]++; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_POE; + } else if ((gSaveContext.inventory.items[i] >= ITEM_POTION_RED) && + (gSaveContext.inventory.items[i] <= ITEM_POE)) { + gSaveContext.inventory.items[i]--; + } + } + } else if (i < 0x1B) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT) || + CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || + CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (i == SLOT_TRADE_ADULT) { + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = ITEM_BEAN; + } else { + Inventory_DeleteItem(ITEM_BEAN, SLOT(ITEM_BEAN)); + } + } else { + j = sSlotItems[i]; + osSyncPrintf("i=%d j=%d\n", i, j); + if (gSaveContext.inventory.items[i] == ITEM_NONE) { + gSaveContext.inventory.items[i] = j; + } else { + Inventory_DeleteItem(j, i); + } + } + } + } + } else if (curSection < 0x2C) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + i = curSection - 0x1B; + gSaveContext.inventory.dungeonKeys[i]--; + if (gSaveContext.inventory.dungeonKeys[i] < 0) { + gSaveContext.inventory.dungeonKeys[i] = -1; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || + CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + i = curSection - 0x1B; + if (gSaveContext.inventory.dungeonKeys[i] < 0) { + gSaveContext.inventory.dungeonKeys[i] = 1; + } else { + gSaveContext.inventory.dungeonKeys[i]++; + if (gSaveContext.inventory.dungeonKeys[i] >= 9) { + gSaveContext.inventory.dungeonKeys[i] = 9; + } + } + } + } else { + if (curSection < 0x38) { + i = curSection - 0x2C; + if ((curSection >= 0x2C) && (curSection < 0x34)) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || + CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + if (CUR_UPG_VALUE(i) != 0) { + Inventory_ChangeUpgrade(i, CUR_UPG_VALUE(i) - 1); + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || + CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + if (CUR_UPG_VALUE(i) < sMaxUpgradeValues[i]) { + Inventory_ChangeUpgrade(i, CUR_UPG_VALUE(i) + 1); + } + } + } else { + i = curSection - 0x34; + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + gSaveContext.inventory.equipment ^= (1 << gEquipShifts[i]); + } + if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + gSaveContext.inventory.equipment ^= (2 << gEquipShifts[i]); + } + if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + gSaveContext.inventory.equipment ^= (4 << gEquipShifts[i]); + } + if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { + gSaveContext.inventory.equipment ^= (8 << gEquipShifts[i]); + } + } + } else if (curSection < 0x44) { + i = curSection - 0x38; + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + gSaveContext.inventory.dungeonItems[i] ^= 4; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + gSaveContext.inventory.dungeonItems[i] ^= 2; + } + if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + gSaveContext.inventory.dungeonItems[i] ^= 1; + } + } else if (curSection == 0x5B) { + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + gSaveContext.inventory.gsTokens++; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN) || + CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + gSaveContext.inventory.gsTokens--; + if (gSaveContext.inventory.gsTokens <= 0) { + gSaveContext.inventory.gsTokens = 0; + } + } + } else if (curSection < 0x5C) { + i = curSection - 0x44; + if (CHECK_BTN_ALL(input->press.button, BTN_CUP) || CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + gSaveContext.inventory.questItems ^= gBitFlags[i]; + } + } + } + break; + } + + // Draws a highlight on the selected section + gDPPipeSync(POLY_KAL_DISP++); + gDPSetRenderMode(POLY_KAL_DISP++, G_RM_XLU_SURF, G_RM_XLU_SURF2); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 200, 120); + + if (curSection == 0) { + gDPFillRectangle(POLY_KAL_DISP++, sSectionPositions[curSection][0], sSectionPositions[curSection][1], + sSectionPositions[curSection][0] + 45, sSectionPositions[curSection][1] + 16); + } else if ((curSection >= 0x1B) || (curSection == 0x5B)) { + gDPFillRectangle(POLY_KAL_DISP++, sSectionPositions[curSection][0] - 2, sSectionPositions[curSection][1], + sSectionPositions[curSection][0] + 14, sSectionPositions[curSection][1] + 16); + } else { + gDPFillRectangle(POLY_KAL_DISP++, sSectionPositions[curSection][0] - 4, sSectionPositions[curSection][1], + sSectionPositions[curSection][0] + 24, sSectionPositions[curSection][1] + 16); + } + + // Handles exiting the inventory editor with the L button + // The editor is opened with `debugState` set to 1, and becomes closable after a frame once `debugState` is set to 2 + if (pauseCtx->debugState == 1) { + pauseCtx->debugState = 2; + } else if ((pauseCtx->debugState == 2) && CHECK_BTN_ALL(input->press.button, BTN_L)) { + pauseCtx->debugState = 0; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_debug.c", 861); +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c new file mode 100644 index 000000000..3c236637b --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -0,0 +1,612 @@ +#include "z_kaleido_scope.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/parameter_static/parameter_static.h" + +static u8 sChildUpgrades[] = { UPG_BULLET_BAG, UPG_BOMB_BAG, UPG_STRENGTH, UPG_SCALE }; +static u8 sAdultUpgrades[] = { UPG_QUIVER, UPG_BOMB_BAG, UPG_STRENGTH, UPG_SCALE }; + +static u8 sChildUpgradeItemBases[] = { ITEM_BULLET_BAG_30, ITEM_BOMB_BAG_20, ITEM_BRACELET, ITEM_SCALE_SILVER }; +static u8 sAdultUpgradeItemBases[] = { ITEM_QUIVER_30, ITEM_BOMB_BAG_20, ITEM_BRACELET, ITEM_SCALE_SILVER }; + +static u8 sUpgradeItemOffsets[] = { 0x00, 0x03, 0x06, 0x09 }; + +static u8 sEquipmentItemOffsets[] = { + 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x00, 0x09, 0x0A, 0x0B, +}; + +static s16 sEquipTimer = 0; + +void KaleidoScope_DrawEquipmentImage(GlobalContext* globalCtx, void* source, u32 width, u32 height) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + u8* curTexture; + s32 vtxIndex; + s32 textureCount; + s32 textureHeight; + s32 remainingSize; + s32 textureSize; + s32 pad; + s32 i; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_equipment.c", 68); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + curTexture = source; + remainingSize = width * height * 2; + textureHeight = 4096 / (width * 2); + textureSize = width * textureHeight * 2; + textureCount = remainingSize / textureSize; + if ((remainingSize % textureSize) != 0) { + textureCount += 1; + } + + vtxIndex = 80; + + gDPSetTileCustom(POLY_KAL_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, textureHeight, 0, G_TX_NOMIRROR | G_TX_CLAMP, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + remainingSize -= textureSize; + + textureCount = 1; + + // VERTEX Y EXTEND + pauseCtx->equipVtx[vtxIndex + 2].v.ob[1] -= 80; + pauseCtx->equipVtx[vtxIndex + 3].v.ob[1] -= 80; + + for (i = 0; i < textureCount; i++) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->equipVtx[vtxIndex], 4, 0); + + extern int fbTest; + gDPSetTextureImage(POLY_KAL_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, curTexture); + + gDPLoadSync(POLY_KAL_DISP++); + gDPLoadTile(POLY_KAL_DISP++, G_TX_LOADTILE, 0, 0, (width - 1) << 2, (textureHeight - 1) << 2); + + gDPSetTextureImageFB(POLY_KAL_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, fbTest); + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + + curTexture += textureSize; + + if ((remainingSize - textureSize) < 0) { + if (remainingSize > 0) { + textureHeight = remainingSize / (s32)(width * 2); + remainingSize -= textureSize; + + gDPSetTileCustom(POLY_KAL_DISP++, G_IM_FMT_RGBA, G_IM_SIZ_16b, width, textureHeight, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } + } else { + remainingSize -= textureSize; + } + + vtxIndex += 4; + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_equipment.c", 122); +} + +void KaleidoScope_DrawPlayerWork(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Vec3f pos; + Vec3s rot; + f32 scale; + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + pos.x = 2.0f; + pos.y = -130.0f; + pos.z = -150.0f; + scale = 0.046f; + } else if (CUR_EQUIP_VALUE(EQUIP_SWORD) != 2 && !CVar_GetS32("gPauseTriforce", 0)) { + pos.x = 25.0f; + pos.y = -228.0f; + pos.z = 60.0f; + scale = 0.056f; + } else { + pos.x = 20.0f; + pos.y = -180.0f; + pos.z = -40.0f; + scale = 0.047f; + } + + rot.y = 32300; + rot.x = rot.z = 0; + extern int fbTest; + gsSPSetFB(globalCtx->state.gfxCtx->polyOpa.p++, fbTest); + func_8009214C(globalCtx, pauseCtx->playerSegment, &pauseCtx->playerSkelAnime, &pos, &rot, scale, + CUR_EQUIP_VALUE(EQUIP_SWORD), CUR_EQUIP_VALUE(EQUIP_TUNIC) - 1, CUR_EQUIP_VALUE(EQUIP_SHIELD), + CUR_EQUIP_VALUE(EQUIP_BOOTS) - 1); + gsSPResetFB(globalCtx->state.gfxCtx->polyOpa.p++, fbTest); +} + +void KaleidoScope_DrawEquipment(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Input* input = &globalCtx->state.input[0]; + u16 i; + u16 j; + u16 k; + u16 bit; + u16 temp; + u16 point; + u16 rowStart; + u16 pad; + s16 cursorMoveResult; + u16 cursorItem; + u16 cursorSlot; + s16 cursorPoint; + s16 cursorX; + s16 cursorY; + volatile s16 oldCursorPoint; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_equipment.c", 219); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, ZREG(39), ZREG(40), ZREG(41), pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, ZREG(43), ZREG(44), ZREG(45), 0); + + for (i = 0, j = 64; i < 4; i++, j += 4) { + if (CUR_EQUIP_VALUE(i) != 0) { + gDPPipeSync(POLY_KAL_DISP++); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->equipVtx[j], 4, 0); + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, gEquippedItemOutlineTex, 32, 32, 0); + } + } + + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_EQUIP)) { + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_EQUIP]; + pauseCtx->cursorColorSet = 0; + + if (pauseCtx->cursorSpecialPos == 0) { + pauseCtx->nameColorSet = 0; + + cursorItem = pauseCtx->cursorItem[PAUSE_EQUIP]; + if ((cursorItem >= ITEM_SWORD_KOKIRI) && (cursorItem <= ITEM_BOOTS_HOVER)) { + pauseCtx->cursorColorSet = 8; + } + + cursorPoint = pauseCtx->cursorPoint[PAUSE_EQUIP]; + cursorX = pauseCtx->cursorX[PAUSE_EQUIP]; + cursorY = pauseCtx->cursorY[PAUSE_EQUIP]; + + cursorMoveResult = 0; + while (cursorMoveResult == 0) { + if (pauseCtx->stickRelX < -30) { + if (pauseCtx->cursorX[PAUSE_EQUIP] != 0) { + pauseCtx->cursorX[PAUSE_EQUIP] -= 1; + pauseCtx->cursorPoint[PAUSE_EQUIP] -= 1; + + if (pauseCtx->cursorX[PAUSE_EQUIP] == 0) { + if (pauseCtx->cursorY[PAUSE_EQUIP] == 0) { + if (CUR_UPG_VALUE(UPG_BULLET_BAG) != 0) { + cursorMoveResult = 1; + } + } else { + if (CUR_UPG_VALUE(pauseCtx->cursorY[PAUSE_EQUIP]) != 0) { + cursorMoveResult = 1; + } + } + } else { + if (gBitFlags[pauseCtx->cursorPoint[PAUSE_EQUIP] - 1] & gSaveContext.inventory.equipment) { + cursorMoveResult = 2; + } + } + } else { + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] += 1; + + if (pauseCtx->cursorY[PAUSE_EQUIP] >= 4) { + pauseCtx->cursorY[PAUSE_EQUIP] = 0; + } + + pauseCtx->cursorPoint[PAUSE_EQUIP] = + pauseCtx->cursorX[PAUSE_EQUIP] + (pauseCtx->cursorY[PAUSE_EQUIP] * 4); + + if (pauseCtx->cursorPoint[PAUSE_EQUIP] >= 16) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = pauseCtx->cursorX[PAUSE_EQUIP]; + } + + if (cursorY == pauseCtx->cursorY[PAUSE_EQUIP]) { + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + cursorMoveResult = 3; + } + } + } else if (pauseCtx->stickRelX > 30) { + if (pauseCtx->cursorX[PAUSE_EQUIP] < 3) { + pauseCtx->cursorX[PAUSE_EQUIP] += 1; + pauseCtx->cursorPoint[PAUSE_EQUIP] += 1; + + if (pauseCtx->cursorX[PAUSE_EQUIP] == 0) { + if (CUR_UPG_VALUE(pauseCtx->cursorY[PAUSE_EQUIP]) != 0) { + cursorMoveResult = 1; + } + } else { + if (gBitFlags[pauseCtx->cursorPoint[PAUSE_EQUIP] - 1] & gSaveContext.inventory.equipment) { + cursorMoveResult = 2; + } + } + } else { + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] += 1; + + if (pauseCtx->cursorY[PAUSE_EQUIP] >= 4) { + pauseCtx->cursorY[PAUSE_EQUIP] = 0; + } + + pauseCtx->cursorPoint[PAUSE_EQUIP] = + pauseCtx->cursorX[PAUSE_EQUIP] + (pauseCtx->cursorY[PAUSE_EQUIP] * 4); + + if (pauseCtx->cursorPoint[PAUSE_EQUIP] >= 16) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = pauseCtx->cursorX[PAUSE_EQUIP]; + } + + if (cursorY == pauseCtx->cursorY[PAUSE_EQUIP]) { + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + cursorMoveResult = 3; + } + } + } else { + cursorMoveResult = 4; + } + } + + cursorPoint = pauseCtx->cursorPoint[PAUSE_EQUIP]; + cursorY = pauseCtx->cursorY[PAUSE_EQUIP]; + + if (cursorMoveResult) {} + + cursorMoveResult = 0; + while (cursorMoveResult == 0) { + if (pauseCtx->stickRelY > 30) { + if (pauseCtx->cursorY[PAUSE_EQUIP] != 0) { + pauseCtx->cursorY[PAUSE_EQUIP] -= 1; + pauseCtx->cursorPoint[PAUSE_EQUIP] -= 4; + + if (pauseCtx->cursorX[PAUSE_EQUIP] == 0) { + if (pauseCtx->cursorY[PAUSE_EQUIP] == 0) { + if (CUR_UPG_VALUE(UPG_BULLET_BAG) != 0) { + cursorMoveResult = 1; + } + } else if (CUR_UPG_VALUE(pauseCtx->cursorY[PAUSE_EQUIP]) != 0) { + cursorMoveResult = 1; + } + } else if (gBitFlags[pauseCtx->cursorPoint[PAUSE_EQUIP] - 1] & + gSaveContext.inventory.equipment) { + cursorMoveResult = 2; + } + } else { + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + cursorMoveResult = 3; + } + } else if (pauseCtx->stickRelY < -30) { + if (pauseCtx->cursorY[PAUSE_EQUIP] < 3) { + pauseCtx->cursorY[PAUSE_EQUIP] += 1; + pauseCtx->cursorPoint[PAUSE_EQUIP] += 4; + + if (pauseCtx->cursorX[PAUSE_EQUIP] == 0) { + if (CUR_UPG_VALUE(pauseCtx->cursorY[PAUSE_EQUIP]) != 0) { + cursorMoveResult = 1; + } + } else if (gBitFlags[pauseCtx->cursorPoint[PAUSE_EQUIP] - 1] & + gSaveContext.inventory.equipment) { + cursorMoveResult = 2; + } + } else { + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + cursorMoveResult = 3; + } + } else { + cursorMoveResult = 4; + } + } + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + if (pauseCtx->stickRelX > 30) { + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + cursorPoint = cursorX = cursorY = 0; + while (true) { + if (cursorX == 0) { + if (cursorY == 0) { + if (CUR_UPG_VALUE(UPG_BULLET_BAG) != 0) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + break; + } + } else if (CUR_UPG_VALUE(cursorY) != 0) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + break; + } + } else if (gBitFlags[cursorPoint - 1] & gSaveContext.inventory.equipment) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + break; + } + + cursorY = cursorY + 1; + cursorPoint = cursorPoint + 4; + if (cursorY < 4) { + continue; + } + + cursorY = 0; + cursorPoint = cursorX + 1; + cursorX = cursorPoint; + if (cursorX < 4) { + continue; + } + + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + break; + } + } + } else { + if (pauseCtx->stickRelX < -30) { + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + cursorPoint = cursorX = 3; + cursorY = 0; + while (true) { + if (cursorX == 0) { + if (CUR_UPG_VALUE(cursorY) != 0) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + break; + } + } else if (gBitFlags[cursorPoint - 1] & gSaveContext.inventory.equipment) { + pauseCtx->cursorPoint[PAUSE_EQUIP] = cursorPoint; + pauseCtx->cursorX[PAUSE_EQUIP] = cursorX; + pauseCtx->cursorY[PAUSE_EQUIP] = cursorY; + break; + } + + cursorY = cursorY + 1; + cursorPoint = cursorPoint + 4; + if (cursorY < 4) { + continue; + } + + cursorY = 0; + cursorPoint = cursorX - 1; + cursorX = cursorPoint; + if (cursorX >= 0) { + continue; + } + + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + break; + } + } + } + + if (pauseCtx->cursorX[PAUSE_EQUIP] == 0) { + pauseCtx->cursorColorSet = 0; + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + if ((pauseCtx->cursorY[PAUSE_EQUIP] == 0) && (CUR_UPG_VALUE(UPG_BULLET_BAG) != 0)) { + cursorItem = ITEM_BULLET_BAG_30 + CUR_UPG_VALUE(UPG_BULLET_BAG) - 1; + } else { + cursorItem = ITEM_QUIVER_30 + sUpgradeItemOffsets[pauseCtx->cursorY[PAUSE_EQUIP]] + + CUR_UPG_VALUE(pauseCtx->cursorY[PAUSE_EQUIP]) - 1; + osSyncPrintf("H_arrowcase_1 + non_equip_item_table = %d\n", cursorItem); + } + } else { + if ((pauseCtx->cursorY[PAUSE_EQUIP] == 0) && (CUR_UPG_VALUE(UPG_QUIVER) == 0)) { + cursorItem = ITEM_BULLET_BAG_30 + CUR_UPG_VALUE(UPG_BULLET_BAG) - 1; + } else { + cursorItem = ITEM_QUIVER_30 + sUpgradeItemOffsets[pauseCtx->cursorY[PAUSE_EQUIP]] + + CUR_UPG_VALUE(pauseCtx->cursorY[PAUSE_EQUIP]) - 1; + osSyncPrintf("大人 H_arrowcase_1 + non_equip_item_table = %d\n", cursorItem); + } + } + } else { + cursorItem = ITEM_SWORD_KOKIRI + sEquipmentItemOffsets[pauseCtx->cursorPoint[PAUSE_EQUIP]]; + osSyncPrintf("ccc=%d\n", cursorItem); + + if (pauseCtx->cursorSpecialPos == 0) { + pauseCtx->cursorColorSet = 8; + } + } + + if ((pauseCtx->cursorY[PAUSE_EQUIP] == 0) && (pauseCtx->cursorX[PAUSE_EQUIP] == 3)) { + if (gSaveContext.bgsFlag != 0) { + cursorItem = ITEM_HEART_PIECE_2; + } else if (gBitFlags[3] & gSaveContext.inventory.equipment) { + cursorItem = ITEM_SWORD_KNIFE; + } + } + + cursorSlot = pauseCtx->cursorPoint[PAUSE_EQUIP]; + + pauseCtx->cursorItem[PAUSE_EQUIP] = cursorItem; + pauseCtx->cursorSlot[PAUSE_EQUIP] = cursorSlot; + + osSyncPrintf("kscope->select_name[Display_Equipment] = %d\n", pauseCtx->cursorItem[PAUSE_EQUIP]); + + if (!((gEquipAgeReqs[pauseCtx->cursorY[PAUSE_EQUIP]][pauseCtx->cursorX[PAUSE_EQUIP]] == 9) || + (gEquipAgeReqs[pauseCtx->cursorY[PAUSE_EQUIP]][pauseCtx->cursorX[PAUSE_EQUIP]] == + ((void)0, gSaveContext.linkAge)))) { + pauseCtx->nameColorSet = 1; + } + + if (pauseCtx->cursorItem[PAUSE_EQUIP] == ITEM_BRACELET) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + pauseCtx->nameColorSet = 0; + } else { + pauseCtx->nameColorSet = 1; + } + } + + if ((pauseCtx->cursorX[PAUSE_EQUIP] == 0) && (pauseCtx->cursorY[PAUSE_EQUIP] == 0)) { + if (LINK_AGE_IN_YEARS != YEARS_CHILD) { + if ((cursorItem >= ITEM_BULLET_BAG_30) && (cursorItem <= ITEM_BULLET_BAG_50)) { + pauseCtx->nameColorSet = 1; + } else { + pauseCtx->nameColorSet = 0; + } + } else { + pauseCtx->nameColorSet = 0; + } + } + + KaleidoScope_SetCursorVtx(pauseCtx, cursorSlot * 4, pauseCtx->equipVtx); + + if ((pauseCtx->cursorSpecialPos == 0) && (cursorItem != PAUSE_ITEM_NONE) && (pauseCtx->state == 6) && + (pauseCtx->unk_1E4 == 0) && CHECK_BTN_ALL(input->press.button, BTN_A) && + (pauseCtx->cursorX[PAUSE_EQUIP] != 0)) { + + if ((gEquipAgeReqs[pauseCtx->cursorY[PAUSE_EQUIP]][pauseCtx->cursorX[PAUSE_EQUIP]] == 9) || + (gEquipAgeReqs[pauseCtx->cursorY[PAUSE_EQUIP]][pauseCtx->cursorX[PAUSE_EQUIP]] == + ((void)0, gSaveContext.linkAge))) { + Inventory_ChangeEquipment(pauseCtx->cursorY[PAUSE_EQUIP], pauseCtx->cursorX[PAUSE_EQUIP]); + + if (pauseCtx->cursorY[PAUSE_EQUIP] == 0) { + gSaveContext.infTable[29] = 0; + gSaveContext.equips.buttonItems[0] = cursorItem; + + if ((pauseCtx->cursorX[PAUSE_EQUIP] == 3) && (gSaveContext.bgsFlag != 0)) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + gSaveContext.swordHealth = 8; + } else { + if (gSaveContext.equips.buttonItems[0] == ITEM_HEART_PIECE_2) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + } + if ((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_BGS) && (gSaveContext.bgsFlag == 0) && + (gBitFlags[3] & gSaveContext.inventory.equipment)) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_KNIFE; + } + } + + Interface_LoadItemIcon1(globalCtx, 0); + } + + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->unk_1E4 = 7; + sEquipTimer = 10; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_EQUIP]) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else if ((pauseCtx->unk_1E4 == 7) && (pauseCtx->pageIndex == PAUSE_EQUIP)) { + KaleidoScope_SetCursorVtx(pauseCtx, pauseCtx->cursorSlot[PAUSE_EQUIP] * 4, pauseCtx->equipVtx); + pauseCtx->cursorColorSet = 8; + + sEquipTimer--; + if (sEquipTimer == 0) { + pauseCtx->unk_1E4 = 0; + } + } + + for (rowStart = 0, i = 0, point = 4; i < 4; i++, rowStart += 4, point += 16) { + + for (k = 0, temp = rowStart + 1, bit = rowStart, j = point; k < 3; k++, bit++, j += 4, temp++) { + + if ((gBitFlags[bit] & gSaveContext.inventory.equipment) && (pauseCtx->cursorSpecialPos == 0)) { + if ((gEquipAgeReqs[i][k + 1] == 9) || (gEquipAgeReqs[i][k + 1] == ((void)0, gSaveContext.linkAge))) { + if (temp == cursorSlot) { + pauseCtx->equipVtx[j].v.ob[0] = pauseCtx->equipVtx[j + 2].v.ob[0] = + pauseCtx->equipVtx[j].v.ob[0] - 2; + pauseCtx->equipVtx[j + 1].v.ob[0] = pauseCtx->equipVtx[j + 3].v.ob[0] = + pauseCtx->equipVtx[j + 1].v.ob[0] + 4; + pauseCtx->equipVtx[j].v.ob[1] = pauseCtx->equipVtx[j + 1].v.ob[1] = + pauseCtx->equipVtx[j].v.ob[1] + 2; + pauseCtx->equipVtx[j + 2].v.ob[1] = pauseCtx->equipVtx[j + 3].v.ob[1] = + pauseCtx->equipVtx[j + 2].v.ob[1] - 4; + } + } + } + } + } + + func_800949A8(globalCtx->state.gfxCtx); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + for (rowStart = 0, j = 0, temp = 0, i = 0; i < 4; i++, rowStart += 4, j += 16) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->equipVtx[j], 16, 0); + + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + point = CUR_UPG_VALUE(sChildUpgrades[i]); + if (1) {} + if ((point != 0) && (CUR_UPG_VALUE(sChildUpgrades[i]) != 0)) { + KaleidoScope_DrawQuadTextureRGBA32(globalCtx->state.gfxCtx, + gItemIcons[sChildUpgradeItemBases[i] + point - 1], 32, 32, 0); + } + } else { + if ((i == 0) && (CUR_UPG_VALUE(sAdultUpgrades[i]) == 0)) { + KaleidoScope_DrawQuadTextureRGBA32( + globalCtx->state.gfxCtx, + gItemIcons[sChildUpgradeItemBases[i] + CUR_UPG_VALUE(sChildUpgrades[i]) - 1], 32, 32, 0); + } else if (CUR_UPG_VALUE(sAdultUpgrades[i]) != 0) { + KaleidoScope_DrawQuadTextureRGBA32( + globalCtx->state.gfxCtx, + gItemIcons[sAdultUpgradeItemBases[i] + CUR_UPG_VALUE(sAdultUpgrades[i]) - 1], 32, 32, 0); + } + } + + for (k = 0, bit = rowStart, point = 4; k < 3; k++, point += 4, temp++, bit++) { + + if (((u32)i == 0) && (k == 2) && (gSaveContext.bgsFlag != 0)) { + KaleidoScope_DrawQuadTextureRGBA32(globalCtx->state.gfxCtx, gBiggoronSwordIconTex, 32, 32, point); + } else if ((i == 0) && (k == 2) && (gBitFlags[bit + 1] & gSaveContext.inventory.equipment)) { + KaleidoScope_DrawQuadTextureRGBA32(globalCtx->state.gfxCtx, gBrokenGiantsKnifeIconTex, 32, 32, point); + } else if (gBitFlags[bit] & gSaveContext.inventory.equipment) { + KaleidoScope_DrawQuadTextureRGBA32(globalCtx->state.gfxCtx, gItemIcons[ITEM_SWORD_KOKIRI + temp], 32, 32, point); + } + } + } + + KaleidoScope_DrawPlayerWork(globalCtx); + + //if ((pauseCtx->unk_1E4 == 7) && (sEquipTimer == 10)) { + //KaleidoScope_SetupPlayerPreRender(globalCtx); + //} + + if ((pauseCtx->unk_1E4 == 7) && (sEquipTimer == 9)) { + //! @bug: This function shouldn't take any arguments + //KaleidoScope_ProcessPlayerPreRender(globalCtx); + } + + // gSPInvalidateTexCache(POLY_KAL_DISP++, 0); + gSPInvalidateTexCache(POLY_KAL_DISP++, pauseCtx->iconItemSegment); + //gSPInvalidateTexCache(POLY_KAL_DISP++, pauseCtx->iconItem24Segment); + gSPInvalidateTexCache(POLY_KAL_DISP++, pauseCtx->nameSegment); + gSPInvalidateTexCache(POLY_KAL_DISP++, globalCtx->interfaceCtx.mapSegment); + + //gSPSegment(POLY_KAL_DISP++, 0x07, pauseCtx->playerSegment); + gSPSegment(POLY_KAL_DISP++, 0x08, pauseCtx->iconItemSegment); + gSPSegment(POLY_KAL_DISP++, 0x09, pauseCtx->iconItem24Segment); + gSPSegment(POLY_KAL_DISP++, 0x0A, pauseCtx->nameSegment); + gSPSegment(POLY_KAL_DISP++, 0x0B, globalCtx->interfaceCtx.mapSegment); + //gSPSegment(POLY_KAL_DISP++, 0x0C, pauseCtx->iconItemAltSegment); + + func_800949A8_KAL(globalCtx->state.gfxCtx); + KaleidoScope_DrawEquipmentImage(globalCtx, pauseCtx->playerSegment, 64, 112); + + if (gUpgradeMasks[0]) {} + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_equipment.c", 609); +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c new file mode 100644 index 000000000..bb42ff711 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -0,0 +1,813 @@ +#include "z_kaleido_scope.h" +#include "textures/parameter_static/parameter_static.h" + +u8 gAmmoItems[] = { + ITEM_STICK, ITEM_NUT, ITEM_BOMB, ITEM_BOW, ITEM_NONE, ITEM_NONE, ITEM_SLINGSHOT, ITEM_NONE, + ITEM_BOMBCHU, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_BEAN, ITEM_NONE, +}; + +static s16 sEquipState = 0; +static s16 sEquipAnimTimer = 0; +static s16 sEquipMoveTimer = 10; + +static s16 sAmmoVtxOffset[] = { + 0, 2, 4, 6, 99, 99, 8, 99, 99, 10, 99, 99, 99, 99, 99, 99, 12, +}; + +extern const char* _gAmmoDigit0Tex[]; + +void KaleidoScope_DrawAmmoCount(PauseContext* pauseCtx, GraphicsContext* gfxCtx, s16 item) { + s16 ammo; + s16 i; + + OPEN_DISPS(gfxCtx, "../z_kaleido_item.c", 69); + + ammo = AMMO(item); + + gDPPipeSync(POLY_KAL_DISP++); + + if (!((gSlotAgeReqs[SLOT(item)] == 9) || gSlotAgeReqs[SLOT(item)] == ((void)0, gSaveContext.linkAge))) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 100, 100, 100, pauseCtx->alpha); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + if (ammo == 0) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 130, 130, 130, pauseCtx->alpha); + } else if ((item == ITEM_BOMB && AMMO(item) == CUR_CAPACITY(UPG_BOMB_BAG)) || + (item == ITEM_BOW && AMMO(item) == CUR_CAPACITY(UPG_QUIVER)) || + (item == ITEM_SLINGSHOT && AMMO(item) == CUR_CAPACITY(UPG_BULLET_BAG)) || + (item == ITEM_STICK && AMMO(item) == CUR_CAPACITY(UPG_STICKS)) || + (item == ITEM_NUT && AMMO(item) == CUR_CAPACITY(UPG_NUTS)) || (item == ITEM_BOMBCHU && ammo == 50) || + (item == ITEM_BEAN && ammo == 15)) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 120, 255, 0, pauseCtx->alpha); + } + } + + for (i = 0; ammo >= 10; i++) { + ammo -= 10; + } + + gDPPipeSync(POLY_KAL_DISP++); + + if (i != 0) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[item] + 27) * 4], 4, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, ((u8*)_gAmmoDigit0Tex[i]), G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[(sAmmoVtxOffset[item] + 28) * 4], 4, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, ((u8*)_gAmmoDigit0Tex[ammo]), G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + + CLOSE_DISPS(gfxCtx, "../z_kaleido_item.c", 116); +} + +void KaleidoScope_SetCursorVtx(PauseContext* pauseCtx, u16 index, Vtx* vtx) { + pauseCtx->cursorVtx[0].v.ob[0] = vtx[index].v.ob[0]; + pauseCtx->cursorVtx[0].v.ob[1] = vtx[index].v.ob[1]; + KaleidoScope_UpdateCursorSize(pauseCtx); // OTRTODO Why is this needed? +} + +void KaleidoScope_SetItemCursorVtx(PauseContext* pauseCtx) { + KaleidoScope_SetCursorVtx(pauseCtx, pauseCtx->cursorSlot[PAUSE_ITEM] * 4, pauseCtx->itemVtx); +} + +void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx) { + static s16 magicArrowEffectsR[] = { 255, 100, 255 }; + static s16 magicArrowEffectsG[] = { 0, 100, 255 }; + static s16 magicArrowEffectsB[] = { 0, 255, 100 }; + Input* input = &globalCtx->state.input[0]; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + u16 i; + u16 j; + u16 cursorItem; + u16 cursorSlot = 0; + u16 index; + s16 cursorPoint; + s16 cursorX; + s16 cursorY; + s16 oldCursorPoint; + s16 moveCursorResult; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_item.c", 234); + + func_800949A8(globalCtx->state.gfxCtx); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + pauseCtx->cursorColorSet = 0; + pauseCtx->nameColorSet = 0; + + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_ITEM)) { + moveCursorResult = 0; + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_ITEM]; + + cursorItem = pauseCtx->cursorItem[PAUSE_ITEM]; + cursorSlot = pauseCtx->cursorSlot[PAUSE_ITEM]; + + if (pauseCtx->cursorSpecialPos == 0) { + pauseCtx->cursorColorSet = 4; + + if (cursorItem == PAUSE_ITEM_NONE) { + pauseCtx->stickRelX = 40; + } + + if (ABS(pauseCtx->stickRelX) > 30) { + cursorPoint = pauseCtx->cursorPoint[PAUSE_ITEM]; + cursorX = pauseCtx->cursorX[PAUSE_ITEM]; + cursorY = pauseCtx->cursorY[PAUSE_ITEM]; + + osSyncPrintf("now=%d ccc=%d\n", cursorPoint, cursorItem); + + // Seem necessary to match + if (pauseCtx->cursorX[PAUSE_ITEM]) {} + if (gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]]) {} + + while (moveCursorResult == 0) { + if (pauseCtx->stickRelX < -30) { + if (pauseCtx->cursorX[PAUSE_ITEM] != 0) { + pauseCtx->cursorX[PAUSE_ITEM] -= 1; + pauseCtx->cursorPoint[PAUSE_ITEM] -= 1; + + if (gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]] != ITEM_NONE) { + moveCursorResult = 1; + } + } else { + pauseCtx->cursorX[PAUSE_ITEM] = cursorX; + pauseCtx->cursorY[PAUSE_ITEM] += 1; + + if (pauseCtx->cursorY[PAUSE_ITEM] >= 4) { + pauseCtx->cursorY[PAUSE_ITEM] = 0; + } + + pauseCtx->cursorPoint[PAUSE_ITEM] = + pauseCtx->cursorX[PAUSE_ITEM] + (pauseCtx->cursorY[PAUSE_ITEM] * 6); + + if (pauseCtx->cursorPoint[PAUSE_ITEM] >= 24) { + pauseCtx->cursorPoint[PAUSE_ITEM] = pauseCtx->cursorX[PAUSE_ITEM]; + } + + if (cursorY == pauseCtx->cursorY[PAUSE_ITEM]) { + pauseCtx->cursorX[PAUSE_ITEM] = cursorX; + pauseCtx->cursorPoint[PAUSE_ITEM] = cursorPoint; + + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + + moveCursorResult = 2; + } + } + } else if (pauseCtx->stickRelX > 30) { + if (pauseCtx->cursorX[PAUSE_ITEM] < 5) { + pauseCtx->cursorX[PAUSE_ITEM] += 1; + pauseCtx->cursorPoint[PAUSE_ITEM] += 1; + + if (gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]] != ITEM_NONE) { + moveCursorResult = 1; + } + } else { + pauseCtx->cursorX[PAUSE_ITEM] = cursorX; + pauseCtx->cursorY[PAUSE_ITEM] += 1; + + if (pauseCtx->cursorY[PAUSE_ITEM] >= 4) { + pauseCtx->cursorY[PAUSE_ITEM] = 0; + } + + pauseCtx->cursorPoint[PAUSE_ITEM] = + pauseCtx->cursorX[PAUSE_ITEM] + (pauseCtx->cursorY[PAUSE_ITEM] * 6); + + if (pauseCtx->cursorPoint[PAUSE_ITEM] >= 24) { + pauseCtx->cursorPoint[PAUSE_ITEM] = pauseCtx->cursorX[PAUSE_ITEM]; + } + + if (cursorY == pauseCtx->cursorY[PAUSE_ITEM]) { + pauseCtx->cursorX[PAUSE_ITEM] = cursorX; + pauseCtx->cursorPoint[PAUSE_ITEM] = cursorPoint; + + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + + moveCursorResult = 2; + } + } + } + } + + if (moveCursorResult == 1) { + cursorItem = gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]]; + } + + osSyncPrintf("【X cursor=%d(%) (cur_xpt=%d)(ok_fg=%d)(ccc=%d)(key_angle=%d)】 ", + pauseCtx->cursorPoint[PAUSE_ITEM], pauseCtx->cursorX[PAUSE_ITEM], moveCursorResult, + cursorItem, pauseCtx->cursorSpecialPos); + } + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + if (pauseCtx->stickRelX > 30) { + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + cursorPoint = cursorX = cursorY = 0; + while (true) { + if (gSaveContext.inventory.items[cursorPoint] != ITEM_NONE) { + pauseCtx->cursorPoint[PAUSE_ITEM] = cursorPoint; + pauseCtx->cursorX[PAUSE_ITEM] = cursorX; + pauseCtx->cursorY[PAUSE_ITEM] = cursorY; + moveCursorResult = 1; + break; + } + + cursorY = cursorY + 1; + cursorPoint = cursorPoint + 6; + if (cursorY < 4) { + continue; + } + + cursorY = 0; + cursorPoint = cursorX + 1; + cursorX = cursorPoint; + if (cursorX < 6) { + continue; + } + + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + break; + } + } + } else { + if (pauseCtx->stickRelX < -30) { + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + + cursorPoint = cursorX = 5; + cursorY = 0; + while (true) { + if (gSaveContext.inventory.items[cursorPoint] != ITEM_NONE) { + pauseCtx->cursorPoint[PAUSE_ITEM] = cursorPoint; + pauseCtx->cursorX[PAUSE_ITEM] = cursorX; + pauseCtx->cursorY[PAUSE_ITEM] = cursorY; + moveCursorResult = 1; + break; + } + + cursorY = cursorY + 1; + cursorPoint = cursorPoint + 6; + if (cursorY < 4) { + continue; + } + + cursorY = 0; + cursorPoint = cursorX - 1; + cursorX = cursorPoint; + if (cursorX >= 0) { + continue; + } + + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + break; + } + } + } + + if (pauseCtx->cursorSpecialPos == 0) { + if (cursorItem != PAUSE_ITEM_NONE) { + if (ABS(pauseCtx->stickRelY) > 30) { + moveCursorResult = 0; + + cursorPoint = pauseCtx->cursorPoint[PAUSE_ITEM]; + cursorY = pauseCtx->cursorY[PAUSE_ITEM]; + while (moveCursorResult == 0) { + if (pauseCtx->stickRelY > 30) { + if (pauseCtx->cursorY[PAUSE_ITEM] != 0) { + pauseCtx->cursorY[PAUSE_ITEM] -= 1; + pauseCtx->cursorPoint[PAUSE_ITEM] -= 6; + + if (gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]] != ITEM_NONE) { + moveCursorResult = 1; + } + } else { + pauseCtx->cursorY[PAUSE_ITEM] = cursorY; + pauseCtx->cursorPoint[PAUSE_ITEM] = cursorPoint; + + moveCursorResult = 2; + } + } else if (pauseCtx->stickRelY < -30) { + if (pauseCtx->cursorY[PAUSE_ITEM] < 3) { + pauseCtx->cursorY[PAUSE_ITEM] += 1; + pauseCtx->cursorPoint[PAUSE_ITEM] += 6; + + if (gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]] != ITEM_NONE) { + moveCursorResult = 1; + } + } else { + pauseCtx->cursorY[PAUSE_ITEM] = cursorY; + pauseCtx->cursorPoint[PAUSE_ITEM] = cursorPoint; + + moveCursorResult = 2; + } + } + } + + cursorPoint = PAUSE_ITEM; + osSyncPrintf("【Y cursor=%d(%) (cur_ypt=%d)(ok_fg=%d)(ccc=%d)】 ", + pauseCtx->cursorPoint[cursorPoint], pauseCtx->cursorY[PAUSE_ITEM], moveCursorResult, + cursorItem); + } + } + + cursorSlot = pauseCtx->cursorPoint[PAUSE_ITEM]; + + pauseCtx->cursorColorSet = 4; + + if (moveCursorResult == 1) { + cursorItem = gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]]; + } else if (moveCursorResult != 2) { + cursorItem = gSaveContext.inventory.items[pauseCtx->cursorPoint[PAUSE_ITEM]]; + } + + pauseCtx->cursorItem[PAUSE_ITEM] = cursorItem; + pauseCtx->cursorSlot[PAUSE_ITEM] = cursorSlot; + + if (!((gSlotAgeReqs[cursorSlot] == 9) || (gSlotAgeReqs[cursorSlot] == ((void)0, gSaveContext.linkAge)))) { + pauseCtx->nameColorSet = 1; + } + + if (cursorItem != PAUSE_ITEM_NONE) { + index = cursorSlot * 4; // required to match? + KaleidoScope_SetCursorVtx(pauseCtx, index, pauseCtx->itemVtx); + + if ((pauseCtx->debugState == 0) && (pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0)) { + if (CHECK_BTN_ANY(input->press.button, BTN_CLEFT | BTN_CDOWN | BTN_CRIGHT)) { + if (((gSlotAgeReqs[cursorSlot] == 9) || + (gSlotAgeReqs[cursorSlot] == ((void)0, gSaveContext.linkAge))) && + (cursorItem != ITEM_SOLD_OUT)) { + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + pauseCtx->equipTargetCBtn = 0; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + pauseCtx->equipTargetCBtn = 1; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + pauseCtx->equipTargetCBtn = 2; + } + + pauseCtx->equipTargetItem = cursorItem; + pauseCtx->equipTargetSlot = cursorSlot; + pauseCtx->unk_1E4 = 3; + pauseCtx->equipAnimX = pauseCtx->itemVtx[index].v.ob[0] * 10; + pauseCtx->equipAnimY = pauseCtx->itemVtx[index].v.ob[1] * 10; + pauseCtx->equipAnimAlpha = 255; + sEquipAnimTimer = 0; + sEquipState = 3; + sEquipMoveTimer = 10; + if ((pauseCtx->equipTargetItem == ITEM_ARROW_FIRE) || + (pauseCtx->equipTargetItem == ITEM_ARROW_ICE) || + (pauseCtx->equipTargetItem == ITEM_ARROW_LIGHT)) { + index = 0; + if (pauseCtx->equipTargetItem == ITEM_ARROW_ICE) { + index = 1; + } + if (pauseCtx->equipTargetItem == ITEM_ARROW_LIGHT) { + index = 2; + } + Audio_PlaySoundGeneral(NA_SE_SY_SET_FIRE_ARROW + index, &D_801333D4, 4, &D_801333E0, + &D_801333E0, &D_801333E8); + pauseCtx->equipTargetItem = 0xBF + index; + sEquipState = 0; + pauseCtx->equipAnimAlpha = 0; + sEquipMoveTimer = 6; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } else { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + } + } + } + } else { + pauseCtx->cursorVtx[0].v.ob[0] = pauseCtx->cursorVtx[2].v.ob[0] = pauseCtx->cursorVtx[1].v.ob[0] = + pauseCtx->cursorVtx[3].v.ob[0] = 0; + + pauseCtx->cursorVtx[0].v.ob[1] = pauseCtx->cursorVtx[1].v.ob[1] = pauseCtx->cursorVtx[2].v.ob[1] = + pauseCtx->cursorVtx[3].v.ob[1] = -200; + } + } else { + pauseCtx->cursorItem[PAUSE_ITEM] = PAUSE_ITEM_NONE; + } + + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_ITEM]) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else if ((pauseCtx->unk_1E4 == 3) && (pauseCtx->pageIndex == PAUSE_ITEM)) { + KaleidoScope_SetCursorVtx(pauseCtx, cursorSlot * 4, pauseCtx->itemVtx); + pauseCtx->cursorColorSet = 4; + } + + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); + + for (i = 0, j = 24 * 4; i < 3; i++, j += 4) { + if (gSaveContext.equips.buttonItems[i + 1] != ITEM_NONE) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[j], 4, 0); + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, gEquippedItemOutlineTex, 32, 32, 0); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + for (i = j = 0; i < 24; i++, j += 4) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + if (gSaveContext.inventory.items[i] != ITEM_NONE) { + if ((pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_ITEM) && (pauseCtx->cursorSpecialPos == 0)) { + if ((gSlotAgeReqs[i] == 9) || (gSlotAgeReqs[i] == ((void)0, gSaveContext.linkAge))) { + if ((sEquipState == 2) && (i == 3)) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, magicArrowEffectsR[pauseCtx->equipTargetItem - 0xBF], + magicArrowEffectsG[pauseCtx->equipTargetItem - 0xBF], + magicArrowEffectsB[pauseCtx->equipTargetItem - 0xBF], pauseCtx->alpha); + + pauseCtx->itemVtx[j + 0].v.ob[0] = pauseCtx->itemVtx[j + 2].v.ob[0] = + pauseCtx->itemVtx[j + 0].v.ob[0] - 2; + + pauseCtx->itemVtx[j + 1].v.ob[0] = pauseCtx->itemVtx[j + 3].v.ob[0] = + pauseCtx->itemVtx[j + 0].v.ob[0] + 32; + + pauseCtx->itemVtx[j + 0].v.ob[1] = pauseCtx->itemVtx[j + 1].v.ob[1] = + pauseCtx->itemVtx[j + 0].v.ob[1] + 2; + + pauseCtx->itemVtx[j + 2].v.ob[1] = pauseCtx->itemVtx[j + 3].v.ob[1] = + pauseCtx->itemVtx[j + 0].v.ob[1] - 32; + } else if (i == cursorSlot) { + pauseCtx->itemVtx[j + 0].v.ob[0] = pauseCtx->itemVtx[j + 2].v.ob[0] = + pauseCtx->itemVtx[j + 0].v.ob[0] - 2; + + pauseCtx->itemVtx[j + 1].v.ob[0] = pauseCtx->itemVtx[j + 3].v.ob[0] = + pauseCtx->itemVtx[j + 0].v.ob[0] + 32; + + pauseCtx->itemVtx[j + 0].v.ob[1] = pauseCtx->itemVtx[j + 1].v.ob[1] = + pauseCtx->itemVtx[j + 0].v.ob[1] + 2; + + pauseCtx->itemVtx[j + 2].v.ob[1] = pauseCtx->itemVtx[j + 3].v.ob[1] = + pauseCtx->itemVtx[j + 0].v.ob[1] - 32; + } + } + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->itemVtx[j + 0], 4, 0); + KaleidoScope_DrawQuadTextureRGBA32(globalCtx->state.gfxCtx, gItemIcons[gSaveContext.inventory.items[i]], 32, + 32, 0); + } + } + + if (pauseCtx->cursorSpecialPos == 0) { + KaleidoScope_DrawCursor(globalCtx, PAUSE_ITEM); + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + for (i = 0; i < 15; i++) { + if ((gAmmoItems[i] != ITEM_NONE) && (gSaveContext.inventory.items[i] != ITEM_NONE)) { + KaleidoScope_DrawAmmoCount(pauseCtx, globalCtx->state.gfxCtx, gSaveContext.inventory.items[i]); + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_item.c", 516); +} + +static s16 sCButtonPosX[] = { 66, 90, 114 }; +static s16 sCButtonPosY[] = { 110, 92, 110 }; + +void KaleidoScope_UpdateItemEquip(GlobalContext* globalCtx) { + static s16 D_8082A488 = 0; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Vtx* bowItemVtx; + u16 offsetX; + u16 offsetY; + + if (sEquipState == 0) { + pauseCtx->equipAnimAlpha += 14; + if (pauseCtx->equipAnimAlpha > 255) { + pauseCtx->equipAnimAlpha = 254; + sEquipState++; + } + sEquipAnimTimer = 5; + return; + } + + if (sEquipState == 2) { + D_8082A488--; + + if (D_8082A488 == 0) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + sEquipMoveTimer = 6; + WREG(90) = 320; + WREG(87) = WREG(91); + sEquipState++; + Audio_PlaySoundGeneral(NA_SE_SY_SYNTH_MAGIC_ARROW, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + return; + } + + if (sEquipState == 1) { + bowItemVtx = &pauseCtx->itemVtx[12]; + offsetX = ABS(pauseCtx->equipAnimX - bowItemVtx->v.ob[0] * 10) / sEquipMoveTimer; + offsetY = ABS(pauseCtx->equipAnimY - bowItemVtx->v.ob[1] * 10) / sEquipMoveTimer; + } else { + offsetX = ABS(pauseCtx->equipAnimX - OTRGetRectDimensionFromRightEdge(sCButtonPosX[pauseCtx->equipTargetCBtn]) * 10) / sEquipMoveTimer; + offsetY = ABS(pauseCtx->equipAnimY - sCButtonPosY[pauseCtx->equipTargetCBtn] * 10) / sEquipMoveTimer; + } + + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipAnimAlpha < 254)) { + pauseCtx->equipAnimAlpha += 14; + if (pauseCtx->equipAnimAlpha > 255) { + pauseCtx->equipAnimAlpha = 254; + } + sEquipAnimTimer = 5; + return; + } + + if (sEquipAnimTimer == 0) { + WREG(90) -= WREG(87) / sEquipMoveTimer; + WREG(87) -= WREG(87) / sEquipMoveTimer; + + if (sEquipState == 1) { + if (pauseCtx->equipAnimX >= (pauseCtx->itemVtx[12].v.ob[0] * 10)) { + pauseCtx->equipAnimX -= offsetX; + } else { + pauseCtx->equipAnimX += offsetX; + } + + if (pauseCtx->equipAnimY >= (pauseCtx->itemVtx[12].v.ob[1] * 10)) { + pauseCtx->equipAnimY -= offsetY; + } else { + pauseCtx->equipAnimY += offsetY; + } + } else { + if (pauseCtx->equipAnimX >= OTRGetRectDimensionFromRightEdge(sCButtonPosX[pauseCtx->equipTargetCBtn]) * 10) { + pauseCtx->equipAnimX -= offsetX; + } else { + pauseCtx->equipAnimX += offsetX; + } + + if (pauseCtx->equipAnimY >= sCButtonPosY[pauseCtx->equipTargetCBtn] * 10) { + pauseCtx->equipAnimY -= offsetY; + } else { + pauseCtx->equipAnimY += offsetY; + } + } + + sEquipMoveTimer--; + + if (sEquipMoveTimer == 0) { + if (sEquipState == 1) { + sEquipState++; + D_8082A488 = 4; + return; + } + + osSyncPrintf("\n================================\n"); + + if (pauseCtx->equipTargetCBtn == 0) { + + if (pauseCtx->equipTargetSlot == gSaveContext.equips.cButtonSlots[1]) { + if (gSaveContext.equips.buttonItems[1] != ITEM_NONE) { + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1) && + ((gSaveContext.equips.buttonItems[1] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT)))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } else { + gSaveContext.equips.buttonItems[2] = gSaveContext.equips.buttonItems[1]; + gSaveContext.equips.cButtonSlots[1] = gSaveContext.equips.cButtonSlots[0]; + Interface_LoadItemIcon2(globalCtx, 2); + } + } else { + gSaveContext.equips.buttonItems[2] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[1] = SLOT_NONE; + } + } else if (pauseCtx->equipTargetSlot == gSaveContext.equips.cButtonSlots[2]) { + if (gSaveContext.equips.buttonItems[1] != ITEM_NONE) { + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1) && + ((gSaveContext.equips.buttonItems[1] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT)))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } else { + gSaveContext.equips.buttonItems[3] = gSaveContext.equips.buttonItems[1]; + gSaveContext.equips.cButtonSlots[2] = gSaveContext.equips.cButtonSlots[0]; + Interface_LoadItemIcon2(globalCtx, 3); + } + } else { + gSaveContext.equips.buttonItems[3] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[2] = SLOT_NONE; + } + } + + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1)) { + if ((gSaveContext.equips.buttonItems[1] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } + } else if (pauseCtx->equipTargetItem == ITEM_BOW) { + if ((gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT)) { + gSaveContext.equips.buttonItems[2] = gSaveContext.equips.buttonItems[1]; + gSaveContext.equips.cButtonSlots[1] = gSaveContext.equips.cButtonSlots[0]; + Interface_LoadItemIcon2(globalCtx, 2); + } else if ((gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT)) { + gSaveContext.equips.buttonItems[3] = gSaveContext.equips.buttonItems[1]; + gSaveContext.equips.cButtonSlots[2] = gSaveContext.equips.cButtonSlots[0]; + Interface_LoadItemIcon2(globalCtx, 3); + } + } + + gSaveContext.equips.buttonItems[1] = pauseCtx->equipTargetItem; + gSaveContext.equips.cButtonSlots[0] = pauseCtx->equipTargetSlot; + Interface_LoadItemIcon1(globalCtx, 1); + + osSyncPrintf("C左sl_item_no=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetItem, + gSaveContext.equips.buttonItems[1], gSaveContext.equips.buttonItems[2], + gSaveContext.equips.buttonItems[3]); + osSyncPrintf("C左sl_number=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetSlot, + gSaveContext.equips.cButtonSlots[0], gSaveContext.equips.cButtonSlots[1], + gSaveContext.equips.cButtonSlots[2]); + } else if (pauseCtx->equipTargetCBtn == 1) { + osSyncPrintf("C下sl_item_no=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetItem, + gSaveContext.equips.buttonItems[1], gSaveContext.equips.buttonItems[2], + gSaveContext.equips.buttonItems[3]); + osSyncPrintf("C下sl_number=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetSlot, + gSaveContext.equips.cButtonSlots[0], gSaveContext.equips.cButtonSlots[1], + gSaveContext.equips.cButtonSlots[2]); + + if (pauseCtx->equipTargetSlot == gSaveContext.equips.cButtonSlots[0]) { + if (gSaveContext.equips.buttonItems[2] != ITEM_NONE) { + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1) && + ((gSaveContext.equips.buttonItems[2] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT)))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } else { + gSaveContext.equips.buttonItems[1] = gSaveContext.equips.buttonItems[2]; + gSaveContext.equips.cButtonSlots[0] = gSaveContext.equips.cButtonSlots[1]; + Interface_LoadItemIcon2(globalCtx, 1); + } + } else { + gSaveContext.equips.buttonItems[1] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[0] = SLOT_NONE; + } + } else if (pauseCtx->equipTargetSlot == gSaveContext.equips.cButtonSlots[2]) { + if (gSaveContext.equips.buttonItems[2] != ITEM_NONE) { + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1) && + ((gSaveContext.equips.buttonItems[2] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT)))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } else { + gSaveContext.equips.buttonItems[3] = gSaveContext.equips.buttonItems[2]; + gSaveContext.equips.cButtonSlots[2] = gSaveContext.equips.cButtonSlots[1]; + Interface_LoadItemIcon2(globalCtx, 3); + } + } else { + gSaveContext.equips.buttonItems[3] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[2] = SLOT_NONE; + } + } + + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1)) { + if ((gSaveContext.equips.buttonItems[2] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } + } else if (pauseCtx->equipTargetItem == ITEM_BOW) { + if ((gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT)) { + gSaveContext.equips.buttonItems[1] = gSaveContext.equips.buttonItems[2]; + Interface_LoadItemIcon2(globalCtx, 1); + } else if ((gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT)) { + gSaveContext.equips.buttonItems[3] = gSaveContext.equips.buttonItems[2]; + Interface_LoadItemIcon2(globalCtx, 3); + } + } + + gSaveContext.equips.buttonItems[2] = pauseCtx->equipTargetItem; + gSaveContext.equips.cButtonSlots[1] = pauseCtx->equipTargetSlot; + Interface_LoadItemIcon1(globalCtx, 2); + + osSyncPrintf("C下sl_item_no=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetItem, + gSaveContext.equips.buttonItems[1], gSaveContext.equips.buttonItems[2], + gSaveContext.equips.buttonItems[3]); + osSyncPrintf("C下sl_number=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetSlot, + gSaveContext.equips.cButtonSlots[0], gSaveContext.equips.cButtonSlots[1], + gSaveContext.equips.cButtonSlots[2]); + } else { + osSyncPrintf("C右sl_item_no=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetItem, + gSaveContext.equips.buttonItems[1], gSaveContext.equips.buttonItems[2], + gSaveContext.equips.buttonItems[3]); + osSyncPrintf("C右sl_number=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetSlot, + gSaveContext.equips.cButtonSlots[0], gSaveContext.equips.cButtonSlots[1], + gSaveContext.equips.cButtonSlots[2]); + + if (pauseCtx->equipTargetSlot == gSaveContext.equips.cButtonSlots[0]) { + if (gSaveContext.equips.buttonItems[3] != ITEM_NONE) { + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1) && + ((gSaveContext.equips.buttonItems[3] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT)))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } else { + gSaveContext.equips.buttonItems[1] = gSaveContext.equips.buttonItems[3]; + gSaveContext.equips.cButtonSlots[0] = gSaveContext.equips.cButtonSlots[2]; + Interface_LoadItemIcon2(globalCtx, 1); + } + } else { + gSaveContext.equips.buttonItems[1] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[0] = SLOT_NONE; + } + } else if (pauseCtx->equipTargetSlot == gSaveContext.equips.cButtonSlots[1]) { + if (gSaveContext.equips.buttonItems[3] != ITEM_NONE) { + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1) && + ((gSaveContext.equips.buttonItems[3] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT)))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } else { + gSaveContext.equips.buttonItems[2] = gSaveContext.equips.buttonItems[3]; + gSaveContext.equips.cButtonSlots[1] = gSaveContext.equips.cButtonSlots[2]; + Interface_LoadItemIcon2(globalCtx, 2); + } + } else { + gSaveContext.equips.buttonItems[2] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[1] = SLOT_NONE; + } + } + + if ((pauseCtx->equipTargetItem >= 0xBF) && (pauseCtx->equipTargetItem <= 0xC1)) { + if ((gSaveContext.equips.buttonItems[3] == ITEM_BOW) || + ((gSaveContext.equips.buttonItems[3] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[3] <= ITEM_BOW_ARROW_LIGHT))) { + pauseCtx->equipTargetItem -= 0xBF - ITEM_BOW_ARROW_FIRE; + pauseCtx->equipTargetSlot = SLOT_BOW; + } + } else if (pauseCtx->equipTargetItem == ITEM_BOW) { + if ((gSaveContext.equips.buttonItems[1] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[1] <= ITEM_BOW_ARROW_LIGHT)) { + gSaveContext.equips.buttonItems[1] = gSaveContext.equips.buttonItems[3]; + Interface_LoadItemIcon2(globalCtx, 1); + } else if ((gSaveContext.equips.buttonItems[2] >= ITEM_BOW_ARROW_FIRE) && + (gSaveContext.equips.buttonItems[2] <= ITEM_BOW_ARROW_LIGHT)) { + gSaveContext.equips.buttonItems[2] = gSaveContext.equips.buttonItems[3]; + Interface_LoadItemIcon2(globalCtx, 2); + } + } + + gSaveContext.equips.buttonItems[3] = pauseCtx->equipTargetItem; + gSaveContext.equips.cButtonSlots[2] = pauseCtx->equipTargetSlot; + Interface_LoadItemIcon1(globalCtx, 3); + + osSyncPrintf("C右sl_item_no=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetItem, + gSaveContext.equips.buttonItems[1], gSaveContext.equips.buttonItems[2], + gSaveContext.equips.buttonItems[3]); + osSyncPrintf("C右sl_number=%d (1)=%d (2)=%d (3)=%d\n", pauseCtx->equipTargetSlot, + gSaveContext.equips.cButtonSlots[0], gSaveContext.equips.cButtonSlots[1], + gSaveContext.equips.cButtonSlots[2]); + } + + pauseCtx->unk_1E4 = 0; + sEquipMoveTimer = 10; + WREG(90) = 320; + WREG(87) = WREG(91); + } + } else { + sEquipAnimTimer--; + if (sEquipAnimTimer == 0) { + pauseCtx->equipAnimAlpha = 255; + } + } +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c new file mode 100644 index 000000000..af5c2b424 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_map_PAL.c @@ -0,0 +1,729 @@ +#include "z_kaleido_scope.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/icon_item_nes_static/icon_item_nes_static.h" +#include "textures/icon_item_ger_static/icon_item_ger_static.h" +#include "textures/icon_item_fra_static/icon_item_fra_static.h" +#include "textures/icon_item_field_static/icon_item_field_static.h" +#include "textures/icon_item_dungeon_static/icon_item_dungeon_static.h" +#include "textures/icon_item_nes_static/icon_item_nes_static.h" + +void KaleidoScope_DrawDungeonMap(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { + static void* dungeonItemTexs[] = { + gBossKeyIconTex, + gCompassIconTex, + gDungeonMapIconTex, + }; + static void* dungeonTitleTexs[] = { + gPauseDekuTitleENGTex, gPauseDodongoTitleENGTex, gPauseJabuTitleENGTex, gPauseForestTitleENGTex, + gPauseFireTitleENGTex, gPauseWaterTitleENGTex, gPauseSpiritTitleENGTex, gPauseShadowTitleENGTex, + gPauseBotWTitleENGTex, gPauseIceCavernTitleENGTex, + }; + static void* floorIconTexs[] = { + gDungeonMapBlankFloorButtonTex, gDungeonMap8FButtonTex, gDungeonMap7FButtonTex, gDungeonMap6FButtonTex, + gDungeonMap5FButtonTex, gDungeonMap4FButtonTex, gDungeonMap3FButtonTex, gDungeonMap2FButtonTex, + gDungeonMap1FButtonTex, gDungeonMapB1ButtonTex, gDungeonMapB2ButtonTex, gDungeonMapB3ButtonTex, + gDungeonMapB4ButtonTex, gDungeonMapB5ButtonTex, gDungeonMapB6ButtonTex, gDungeonMapB7ButtonTex, + gDungeonMapB8ButtonTex, + }; + static u16 mapBgPulseColors[][3] = { + { 0 / 8, 80 / 8, 255 / 8 }, + { 0 / 8, 200 / 8, 140 / 8 }, + }; + static s16 mapBgPulseR = 0 / 8; + static s16 mapBgPulseG = 200 / 8; + static s16 mapBgPulseB = 140 / 8; + static u16 mapBgPulseTimer = 20; + static u16 mapBgPulseStage = 0; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 i; + s16 j; + s16 oldCursorPoint; + s16 stepR; + s16 stepG; + s16 stepB; + u16 rgba16; + + OPEN_DISPS(gfxCtx, "../z_kaleido_map_PAL.c", 123); + + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_MAP)) { + pauseCtx->cursorColorSet = 0; + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_MAP]; + + if (pauseCtx->cursorSpecialPos == 0) { + if (pauseCtx->stickRelX > 30) { + if (pauseCtx->cursorX[PAUSE_MAP] != 0) { + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + } else { + pauseCtx->cursorX[PAUSE_MAP] = 1; + pauseCtx->cursorPoint[PAUSE_MAP] = 0; + if (!CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, gSaveContext.mapIndex)) { + pauseCtx->cursorPoint[PAUSE_MAP]++; + if (!CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, gSaveContext.mapIndex)) { + pauseCtx->cursorPoint[PAUSE_MAP]++; + if (!CHECK_DUNGEON_ITEM(DUNGEON_MAP, gSaveContext.mapIndex)) { + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + } + } + } + } + } else if (pauseCtx->stickRelX < -30) { + if (pauseCtx->cursorX[PAUSE_MAP] == 0) { + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + } else { + pauseCtx->cursorX[PAUSE_MAP] = 0; + pauseCtx->cursorPoint[PAUSE_MAP] = pauseCtx->dungeonMapSlot; + osSyncPrintf("kscope->cursor_point=%d\n", pauseCtx->cursorPoint[PAUSE_MAP]); + R_MAP_TEX_INDEX = + R_MAP_TEX_INDEX_BASE + + gMapData->floorTexIndexOffset[gSaveContext.mapIndex][pauseCtx->cursorPoint[PAUSE_MAP] - 3]; + KaleidoScope_UpdateDungeonMap(globalCtx); + } + } + + if (pauseCtx->cursorPoint[PAUSE_MAP] < 3) { + if (pauseCtx->stickRelY > 30) { + if (pauseCtx->cursorPoint[PAUSE_MAP] != 0) { + for (i = pauseCtx->cursorPoint[PAUSE_MAP] - 1; i >= 0; i--) { + if (CHECK_DUNGEON_ITEM(i, gSaveContext.mapIndex)) { + pauseCtx->cursorPoint[PAUSE_MAP] = i; + break; + } + } + } + } else { + if (pauseCtx->stickRelY < -30) { + if (pauseCtx->cursorPoint[PAUSE_MAP] != 2) { + for (i = pauseCtx->cursorPoint[PAUSE_MAP] + 1; i < 3; i++) { + if (CHECK_DUNGEON_ITEM(i, gSaveContext.mapIndex)) { + pauseCtx->cursorPoint[PAUSE_MAP] = i; + break; + } + } + } + } + } + } else { + if (pauseCtx->stickRelY > 30) { + if (pauseCtx->cursorPoint[PAUSE_MAP] >= 4) { + for (i = pauseCtx->cursorPoint[PAUSE_MAP] - 3 - 1; i >= 0; i--) { + if ((gSaveContext.sceneFlags[gSaveContext.mapIndex].floors & gBitFlags[i]) || + (CHECK_DUNGEON_ITEM(DUNGEON_MAP, gSaveContext.mapIndex) && + (gMapData->floorID[interfaceCtx->unk_25A][i] != 0))) { + pauseCtx->cursorPoint[PAUSE_MAP] = i + 3; + break; + } + } + } + } else if (pauseCtx->stickRelY < -30) { + if (pauseCtx->cursorPoint[PAUSE_MAP] != 10) { + for (i = pauseCtx->cursorPoint[PAUSE_MAP] - 3 + 1; i < 11; i++) { + if ((gSaveContext.sceneFlags[gSaveContext.mapIndex].floors & gBitFlags[i]) || + (CHECK_DUNGEON_ITEM(DUNGEON_MAP, gSaveContext.mapIndex) && + (gMapData->floorID[interfaceCtx->unk_25A][i] != 0))) { + pauseCtx->cursorPoint[PAUSE_MAP] = i + 3; + break; + } + } + } + } + + i = R_MAP_TEX_INDEX; + R_MAP_TEX_INDEX = + R_MAP_TEX_INDEX_BASE + + gMapData->floorTexIndexOffset[gSaveContext.mapIndex][pauseCtx->cursorPoint[PAUSE_MAP] - 3]; + pauseCtx->dungeonMapSlot = pauseCtx->cursorPoint[PAUSE_MAP]; + if (i != R_MAP_TEX_INDEX) { + KaleidoScope_UpdateDungeonMap(globalCtx); + } + } + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + if (pauseCtx->stickRelX > 30) { + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_MAP] = pauseCtx->dungeonMapSlot; + pauseCtx->cursorX[PAUSE_MAP] = 0; + j = 72 + (pauseCtx->cursorSlot[PAUSE_MAP] * 4); + KaleidoScope_SetCursorVtx(pauseCtx, j, pauseCtx->mapPageVtx); + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } else { + if (pauseCtx->stickRelX < -30) { + pauseCtx->nameDisplayTimer = 0; + pauseCtx->cursorSpecialPos = 0; + pauseCtx->cursorX[PAUSE_MAP] = 1; + pauseCtx->cursorPoint[PAUSE_MAP] = 0; + if (!CHECK_DUNGEON_ITEM(DUNGEON_KEY_BOSS, gSaveContext.mapIndex)) { + pauseCtx->cursorPoint[PAUSE_MAP]++; + if (!CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, gSaveContext.mapIndex)) { + pauseCtx->cursorPoint[PAUSE_MAP]++; + if (!CHECK_DUNGEON_ITEM(DUNGEON_MAP, gSaveContext.mapIndex)) { + pauseCtx->cursorX[PAUSE_MAP] = 0; + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_MAP] = + pauseCtx->dungeonMapSlot; + R_MAP_TEX_INDEX = + R_MAP_TEX_INDEX_BASE + + gMapData + ->floorTexIndexOffset[gSaveContext.mapIndex][pauseCtx->cursorPoint[PAUSE_MAP] - 3]; + KaleidoScope_UpdateDungeonMap(globalCtx); + } + } + } else { + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_MAP]; + } + + osSyncPrintf("kscope->cursor_point====%d\n", pauseCtx->cursorPoint[PAUSE_MAP]); + j = 72 + (pauseCtx->cursorSlot[PAUSE_MAP] * 4); + KaleidoScope_SetCursorVtx(pauseCtx, j, pauseCtx->mapPageVtx); + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_MAP]) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + if (pauseCtx->cursorSpecialPos == 0) { + if (pauseCtx->cursorPoint[PAUSE_MAP] < 3) { + pauseCtx->cursorItem[PAUSE_MAP] = ITEM_KEY_BOSS + pauseCtx->cursorPoint[PAUSE_MAP]; + } else { + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + } + + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_MAP]; + + j = 72 + (pauseCtx->cursorSlot[PAUSE_MAP] * 4); + KaleidoScope_SetCursorVtx(pauseCtx, j, pauseCtx->mapPageVtx); + + if (pauseCtx->cursorX[PAUSE_MAP] == 0) { + pauseCtx->mapPageVtx[j + 0].v.ob[0] = pauseCtx->mapPageVtx[j + 2].v.ob[0] = + pauseCtx->mapPageVtx[j + 0].v.ob[0] - 2; + pauseCtx->mapPageVtx[j + 1].v.ob[0] = pauseCtx->mapPageVtx[j + 3].v.ob[0] = + pauseCtx->mapPageVtx[j + 1].v.ob[0] + 4; + pauseCtx->mapPageVtx[j + 0].v.ob[1] = pauseCtx->mapPageVtx[j + 1].v.ob[1] = + pauseCtx->mapPageVtx[j + 0].v.ob[1] + 2; + pauseCtx->mapPageVtx[j + 2].v.ob[1] = pauseCtx->mapPageVtx[j + 3].v.ob[1] = + pauseCtx->mapPageVtx[j + 2].v.ob[1] - 4; + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[68], 16, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, dungeonTitleTexs[gSaveContext.mapIndex], G_IM_FMT_IA, G_IM_SIZ_8b, 96, 16, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + for (i = 0, j = 4; i < 3; i++, j += 4) { + if (CHECK_DUNGEON_ITEM(i, gSaveContext.mapIndex)) { + gDPLoadTextureBlock(POLY_KAL_DISP++, dungeonItemTexs[i], G_IM_FMT_RGBA, G_IM_SIZ_32b, 24, 24, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 200, pauseCtx->alpha); + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[84], 32, 0); + + for (i = j = 0; i < 8; i++, j += 4) { + if ((gSaveContext.sceneFlags[gSaveContext.mapIndex].floors & gBitFlags[i]) || + CHECK_DUNGEON_ITEM(DUNGEON_MAP, gSaveContext.mapIndex)) { + if (i != (pauseCtx->dungeonMapSlot - 3)) { + gDPLoadTextureBlock(POLY_KAL_DISP++, floorIconTexs[gMapData->floorID[interfaceCtx->unk_25A][i]], + G_IM_FMT_IA, G_IM_SIZ_8b, 24, 16, 0, G_TX_WRAP | G_TX_NOMIRROR, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } + } + } + + j = (pauseCtx->dungeonMapSlot - 3) * 4; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 150, 150, 255, pauseCtx->alpha); + + gDPLoadTextureBlock(POLY_KAL_DISP++, + floorIconTexs[gMapData->floorID[interfaceCtx->unk_25A][pauseCtx->dungeonMapSlot - 3]], + G_IM_FMT_IA, G_IM_SIZ_8b, 24, 16, 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + + pauseCtx->mapPageVtx[124].v.ob[0] = pauseCtx->mapPageVtx[126].v.ob[0] = pauseCtx->mapPageVtx[124].v.ob[0] + 2; + pauseCtx->mapPageVtx[125].v.ob[0] = pauseCtx->mapPageVtx[127].v.ob[0] = pauseCtx->mapPageVtx[124].v.ob[0] + 19; + pauseCtx->mapPageVtx[124].v.ob[1] = pauseCtx->mapPageVtx[125].v.ob[1] = pauseCtx->mapPageVtx[124].v.ob[1] - 2; + pauseCtx->mapPageVtx[126].v.ob[1] = pauseCtx->mapPageVtx[127].v.ob[1] = pauseCtx->mapPageVtx[124].v.ob[1] - 19; + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[116], 12, 0); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + pauseCtx->mapPageVtx[116].v.ob[1] = pauseCtx->mapPageVtx[117].v.ob[1] = pauseCtx->offsetY - (VREG(30) * 14) + 49; + pauseCtx->mapPageVtx[118].v.ob[1] = pauseCtx->mapPageVtx[119].v.ob[1] = pauseCtx->mapPageVtx[116].v.ob[1] - 16; + + gDPLoadTextureBlock(POLY_KAL_DISP++, gDungeonMapLinkHeadTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 16, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + + if (CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, gSaveContext.mapIndex) && + (gMapData->skullFloorIconY[gSaveContext.mapIndex] != -99)) { + pauseCtx->mapPageVtx[120].v.ob[1] = pauseCtx->mapPageVtx[121].v.ob[1] = + gMapData->skullFloorIconY[gSaveContext.mapIndex] + pauseCtx->offsetY; + pauseCtx->mapPageVtx[122].v.ob[1] = pauseCtx->mapPageVtx[123].v.ob[1] = pauseCtx->mapPageVtx[120].v.ob[1] - 16; + + gDPLoadTextureBlock(POLY_KAL_DISP++, gDungeonMapSkullTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 16, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 4, 6, 7, 5, 0); + } + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + if (GET_GS_FLAGS(gSaveContext.mapIndex) == gAreaGsFlags[gSaveContext.mapIndex]) { + KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gGoldSkulltulaIconTex, 24, 24, 8); + } + + if ((globalCtx->sceneNum >= SCENE_YDAN) && (globalCtx->sceneNum <= SCENE_TAKARAYA)) { + stepR = (mapBgPulseR - mapBgPulseColors[mapBgPulseStage][0]) / mapBgPulseTimer; + stepG = (mapBgPulseG - mapBgPulseColors[mapBgPulseStage][1]) / mapBgPulseTimer; + stepB = (mapBgPulseB - mapBgPulseColors[mapBgPulseStage][2]) / mapBgPulseTimer; + mapBgPulseR -= stepR; + mapBgPulseG -= stepG; + mapBgPulseB -= stepB; + + rgba16 = ((mapBgPulseR & 0x1F) << 11) | ((mapBgPulseG & 0x1F) << 6) | ((mapBgPulseB & 0x1F) << 1) | 1; + interfaceCtx->mapPalette[28] = (rgba16 & 0xFF00) >> 8; + interfaceCtx->mapPalette[29] = rgba16 & 0xFF; + + mapBgPulseTimer--; + if (mapBgPulseTimer == 0) { + mapBgPulseStage ^= 1; + mapBgPulseTimer = 20; + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalette); + gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16); + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60], 8, 0); + + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegment, G_IM_FMT_CI, 48, 85, 0, G_TX_WRAP | G_TX_NOMIRROR, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegment + 0x800, G_IM_FMT_CI, 48, 85, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 4, 6, 7, 5, 0); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_BILERP); + + CLOSE_DISPS(gfxCtx, "../z_kaleido_map_PAL.c", 388); +} + +void KaleidoScope_DrawWorldMap(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { + static void* cloudTexs[] = { + gWorldMapCloud16Tex, gWorldMapCloud15Tex, gWorldMapCloud14Tex, gWorldMapCloud13Tex, + gWorldMapCloud12Tex, gWorldMapCloud11Tex, gWorldMapCloud10Tex, gWorldMapCloud9Tex, + gWorldMapCloud8Tex, gWorldMapCloud7Tex, gWorldMapCloud6Tex, gWorldMapCloud5Tex, + gWorldMapCloud4Tex, gWorldMapCloud3Tex, gWorldMapCloud2Tex, gWorldMapCloud1Tex, + }; + static u16 cloudFlagNums[] = { + 0x05, 0x00, 0x13, 0x0E, 0x0F, 0x01, 0x02, 0x10, 0x12, 0x03, 0x07, 0x08, 0x09, 0x0C, 0x0B, 0x06, + }; + static s16 pointPulsePrimColor[] = { 0, 0, 255 }; + static s16 pointPrimColors[][3] = { + { 0, 0, 255 }, + { 255, 255, 0 }, + }; + static s16 pointPulseEnvColor[] = { 255, 255, 0 }; + static s16 pointEnvColors[][3] = { + { 255, 255, 0 }, + { 0, 0, 255 }, + }; + static s16 pointPulseStage = 1; + static s16 pointPulseTimer = 20; + static s16 D_8082A5B8[] = { 64, 64, 64, 28 }; + static s16 areaBoxPosX[] = { + -41, 19, 44, 40, 49, 51, -49, 83, 80, -67, 50, -109, -76, -86, -10, -6, 19, 24, 11, -17, 37, -6, + }; + static s16 areaBoxWidths[] = { + 96, 32, 32, 48, 48, 32, 48, 32, 32, 32, 16, 32, 32, 16, 32, 32, 32, 32, 32, 32, 16, 32, + }; + static s16 areaBoxPosY[] = { + 30, 36, 35, 26, 7, 11, -31, 30, 38, 23, 2, 42, 40, 32, 38, 50, 57, 58, 56, 12, 36, 50, + }; + static s16 areaBoxHeights[] = { + 59, 19, 13, 19, 38, 17, 38, 17, 13, 26, 16, 26, 26, 16, 19, 17, 26, 13, 17, 17, 16, 17, + }; + static void* areaBoxTexs[] = { + gWorldMapAreaBox7Tex, gWorldMapAreaBox1Tex, gWorldMapAreaBox4Tex, gWorldMapAreaBox6Tex, gWorldMapAreaBox2Tex, + gWorldMapAreaBox3Tex, gWorldMapAreaBox2Tex, gWorldMapAreaBox3Tex, gWorldMapAreaBox4Tex, gWorldMapAreaBox5Tex, + gWorldMapAreaBox8Tex, gWorldMapAreaBox5Tex, gWorldMapAreaBox5Tex, gWorldMapAreaBox8Tex, gWorldMapAreaBox1Tex, + gWorldMapAreaBox3Tex, gWorldMapAreaBox5Tex, gWorldMapAreaBox4Tex, gWorldMapAreaBox3Tex, gWorldMapAreaBox3Tex, + gWorldMapAreaBox8Tex, gWorldMapAreaBox3Tex, + }; + static void* currentPosTitleTexs[] = { + gPauseCurrentPositionENGTex, + gPauseCurrentPositionGERTex, + gPauseCurrentPositionFRATex, + }; + static u16 D_8082A6D4 = 0; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 i; + s16 j; + s16 t; + s16 k; + s16 oldCursorPoint; + s16 stepR; + s16 stepG; + s16 stepB; + + OPEN_DISPS(gfxCtx, "../z_kaleido_map_PAL.c", 556); + + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0) && (pauseCtx->pageIndex == PAUSE_MAP)) { + pauseCtx->cursorColorSet = 0; + oldCursorPoint = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + + if (pauseCtx->cursorSpecialPos == 0) { + if (pauseCtx->stickRelX > 30) { + D_8082A6D4 = 0; + + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] > 11) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 11; + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_RIGHT); + break; + } + } while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0); + } else if (pauseCtx->stickRelX < -30) { + D_8082A6D4 = 0; + + do { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + if (pauseCtx->cursorPoint[PAUSE_WORLD_MAP] < 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 0; + KaleidoScope_MoveCursorToSpecialPos(globalCtx, PAUSE_CURSOR_PAGE_LEFT); + break; + } + } while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0); + } else { + D_8082A6D4++; + } + + pauseCtx->cursorItem[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP] + 0x1F; + KaleidoScope_SetCursorVtx(pauseCtx, pauseCtx->cursorSlot[PAUSE_MAP] * 4, pauseCtx->mapPageVtx); + } else { + pauseCtx->cursorItem[PAUSE_MAP] = gSaveContext.worldMapArea + 0x18; + if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + if (pauseCtx->stickRelX > 30) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 0; + pauseCtx->cursorSpecialPos = 0; + + while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]++; + } + + pauseCtx->cursorItem[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP] + 0x1F; + KaleidoScope_SetCursorVtx(pauseCtx, pauseCtx->cursorSlot[PAUSE_MAP] * 4, pauseCtx->mapPageVtx); + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + D_8082A6D4 = 0; + } + } else { + if (pauseCtx->stickRelX < -30) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP] = 11; + pauseCtx->cursorSpecialPos = 0; + + while (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorPoint[PAUSE_WORLD_MAP]--; + } + + pauseCtx->cursorItem[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP]; + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_WORLD_MAP] + 0x1F; + KaleidoScope_SetCursorVtx(pauseCtx, pauseCtx->cursorSlot[PAUSE_MAP] * 4, pauseCtx->mapPageVtx); + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + D_8082A6D4 = 0; + } + } + } + + if (pauseCtx->worldMapPoints[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]] == 0) { + pauseCtx->cursorItem[PAUSE_MAP] = PAUSE_ITEM_NONE; + } + + if (oldCursorPoint != pauseCtx->cursorPoint[PAUSE_WORLD_MAP]) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + + if (HREG(15) == 0) { + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT); + + gDPLoadTLUT_pal256(POLY_KAL_DISP++, gWorldMapImageTLUT); + gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[188], 32, 0); + + for (j = t = i = 0; i < 8; i++, t++, j += 4) { + gDPLoadTextureBlock(POLY_KAL_DISP++, (u8*)ResourceMgr_LoadTexByName(gWorldMapImageTex) + t * 216 * 9, G_IM_FMT_CI, G_IM_SIZ_8b, 216, 9, + 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[220], 28, 0); + + for (j = i = 0; i < 6; i++, t++, j += 4) + { + gDPLoadTextureBlock(POLY_KAL_DISP++, (u8*)ResourceMgr_LoadTexByName(gWorldMapImageTex) + t * 216 * 9, G_IM_FMT_CI, G_IM_SIZ_8b, 216, 9, + 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } + + gDPLoadTextureBlock(POLY_KAL_DISP++, (u8*)ResourceMgr_LoadTexByName(gWorldMapImageTex) + t * 216 * 9, G_IM_FMT_CI, G_IM_SIZ_8b, 216, 2, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } else if (HREG(15) == 1) { + Gfx* sp1CC = POLY_KAL_DISP; + void* mapImage = gWorldMapImageTex; + + // gSPLoadUcodeL(sp1CC++, rspS2DEX)? + //gSPLoadUcodeEx(sp1CC++, OS_K0_TO_PHYSICAL(D_80113070), OS_K0_TO_PHYSICAL(D_801579A0), 0x800); + + func_8009638C(&sp1CC, mapImage, gWorldMapImageTLUT, 216, 128, G_IM_FMT_CI, G_IM_SIZ_8b, 0x8000, 256, + HREG(13) / 100.0f, HREG(14) / 100.0f); + + // gSPLoadUcode(sp1CC++, SysUcode_GetUCode(), SysUcode_GetUCodeData())? + gSPLoadUcodeEx(sp1CC++, SysUcode_GetUCode(), SysUcode_GetUCodeData(), 0x800); + + POLY_KAL_DISP = sp1CC; + } + + if (HREG(15) == 2) { + HREG(15) = 1; + HREG(14) = 6100; + HREG(13) = 5300; + } + + if (ZREG(38) == 0) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_BILERP); + + func_800949A8(gfxCtx); + + gDPSetCombineLERP(POLY_KAL_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 235, 235, 235, pauseCtx->alpha); + + for (k = 0; k < 15; k += 8) { + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60 + k * 4], 32, 0); + + for (j = i = 0; i < 8; i++, j += 4) { + if (!(gSaveContext.worldMapAreaData & gBitFlags[cloudFlagNums[k + i]])) { + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, cloudTexs[k + i], G_IM_FMT_I, D_8082AAEC[k + i], + D_8082AB2C[k + i], 0, G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, + G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } + } + } + } + + if (gSaveContext.worldMapArea < 22) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_POINT); + + pauseCtx->mapPageVtx[172].v.ob[0] = pauseCtx->mapPageVtx[174].v.ob[0] = + areaBoxPosX[((void)0, gSaveContext.worldMapArea)]; + + pauseCtx->mapPageVtx[173].v.ob[0] = pauseCtx->mapPageVtx[175].v.ob[0] = + pauseCtx->mapPageVtx[172].v.ob[0] + areaBoxWidths[((void)0, gSaveContext.worldMapArea)]; + + pauseCtx->mapPageVtx[172].v.ob[1] = pauseCtx->mapPageVtx[173].v.ob[1] = + areaBoxPosY[((void)0, gSaveContext.worldMapArea)] + pauseCtx->offsetY; + + pauseCtx->mapPageVtx[174].v.ob[1] = pauseCtx->mapPageVtx[175].v.ob[1] = + pauseCtx->mapPageVtx[172].v.ob[1] - areaBoxHeights[((void)0, gSaveContext.worldMapArea)]; + + pauseCtx->mapPageVtx[173].v.tc[0] = pauseCtx->mapPageVtx[175].v.tc[0] = + areaBoxWidths[((void)0, gSaveContext.worldMapArea)] << 5; + + pauseCtx->mapPageVtx[174].v.tc[1] = pauseCtx->mapPageVtx[175].v.tc[1] = + areaBoxHeights[((void)0, gSaveContext.worldMapArea)] << 5; + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[172], 4, 0); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 100, 255, 255, pauseCtx->alpha); + + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, areaBoxTexs[((void)0, (gSaveContext.worldMapArea))], G_IM_FMT_IA, + areaBoxWidths[((void)0, (gSaveContext.worldMapArea))], + areaBoxHeights[((void)0, (gSaveContext.worldMapArea))], 0, G_TX_WRAP | G_TX_NOMIRROR, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_BILERP); + } + + stepR = ABS(pointPulsePrimColor[0] - pointPrimColors[pointPulseStage][0]) / pointPulseTimer; + stepG = ABS(pointPulsePrimColor[1] - pointPrimColors[pointPulseStage][1]) / pointPulseTimer; + stepB = ABS(pointPulsePrimColor[2] - pointPrimColors[pointPulseStage][2]) / pointPulseTimer; + if (pointPulsePrimColor[0] >= pointPrimColors[pointPulseStage][0]) { + pointPulsePrimColor[0] -= stepR; + } else { + pointPulsePrimColor[0] += stepR; + } + if (pointPulsePrimColor[1] >= pointPrimColors[pointPulseStage][1]) { + pointPulsePrimColor[1] -= stepG; + } else { + pointPulsePrimColor[1] += stepG; + } + if (pointPulsePrimColor[2] >= pointPrimColors[pointPulseStage][2]) { + pointPulsePrimColor[2] -= stepB; + } else { + pointPulsePrimColor[2] += stepB; + } + + stepR = ABS(pointPulseEnvColor[0] - pointEnvColors[pointPulseStage][0]) / pointPulseTimer; + stepG = ABS(pointPulseEnvColor[1] - pointEnvColors[pointPulseStage][1]) / pointPulseTimer; + stepB = ABS(pointPulseEnvColor[2] - pointEnvColors[pointPulseStage][2]) / pointPulseTimer; + if (pointPulseEnvColor[0] >= pointEnvColors[pointPulseStage][0]) { + pointPulseEnvColor[0] -= stepR; + } else { + pointPulseEnvColor[0] += stepR; + } + if (pointPulseEnvColor[1] >= pointEnvColors[pointPulseStage][1]) { + pointPulseEnvColor[1] -= stepG; + } else { + pointPulseEnvColor[1] += stepG; + } + if (pointPulseEnvColor[2] >= pointEnvColors[pointPulseStage][2]) { + pointPulseEnvColor[2] -= stepB; + } else { + pointPulseEnvColor[2] += stepB; + } + + pointPulseTimer--; + if (pointPulseTimer == 0) { + pointPulsePrimColor[0] = pointPrimColors[pointPulseStage][0]; + pointPulsePrimColor[1] = pointPrimColors[pointPulseStage][1]; + pointPulsePrimColor[2] = pointPrimColors[pointPulseStage][2]; + pointPulseEnvColor[0] = pointEnvColors[pointPulseStage][0]; + pointPulseEnvColor[1] = pointEnvColors[pointPulseStage][1]; + pointPulseEnvColor[2] = pointEnvColors[pointPulseStage][2]; + pointPulseStage ^= 1; + pointPulseTimer = 20; + } + + func_800949A8(gfxCtx); + + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + gDPLoadTextureBlock(POLY_KAL_DISP++, gWorldMapDotTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, G_TX_WRAP | G_TX_NOMIRROR, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + for (j = i = 0; i < 12; i++, t++, j += 4) { + if (pauseCtx->worldMapPoints[i] != 0) { + gDPPipeSync(POLY_KAL_DISP++); + + if (pauseCtx->worldMapPoints[i] == 1) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, pointPrimColors[0][0], pointPrimColors[0][1], + pointPrimColors[0][2], pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, pointEnvColors[0][0], pointEnvColors[0][1], pointEnvColors[0][2], 0); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, pointPulsePrimColor[0], pointPulsePrimColor[1], + pointPulsePrimColor[2], pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, pointPulseEnvColor[0], pointPulseEnvColor[1], pointPulseEnvColor[2], 0); + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[124 + i * 4], 4, 0); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + } + + if (pauseCtx->cursorSpecialPos == 0) { + KaleidoScope_DrawCursor(globalCtx, PAUSE_MAP); + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[176], 16, 0); + + if (pauseCtx->tradeQuestLocation != 0xFF) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, pointPulsePrimColor[0], 0, pauseCtx->alpha); + + gDPLoadTextureBlock(POLY_KAL_DISP++, gWorldMapArrowTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 0, 2, 3, 1, 0); + } + + if (gSaveContext.worldMapArea < 22) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 150, 255, 255, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); + + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, pauseCtx->nameSegment + 0x400, 80, 32, 4); + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, 0, + PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 0, 0, 0, pauseCtx->alpha); + + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, currentPosTitleTexs[gSaveContext.language], G_IM_FMT_I, 64, 8, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSP1Quadrangle(POLY_KAL_DISP++, 8, 10, 11, 9, 0); + + gDPPipeSync(POLY_KAL_DISP++); + + CLOSE_DISPS(gfxCtx, "../z_kaleido_map_PAL.c", 874); +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_prompt.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_prompt.c new file mode 100644 index 000000000..c16404fb2 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_prompt.c @@ -0,0 +1,34 @@ +#include "z_kaleido_scope.h" + +static s16 D_8082A6E0[] = { 100, 255 }; + +void KaleidoScope_UpdatePrompt(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + Input* input = &globalCtx->state.input[0]; + s8 relStickX = input->rel.stick_x; + s16 step; + + if (((pauseCtx->state == 7) && (pauseCtx->unk_1EC == 1)) || (pauseCtx->state == 0xE) || (pauseCtx->state == 0x10)) { + if ((pauseCtx->promptChoice == 0) && (relStickX >= 30)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->promptChoice = 4; + } else if ((pauseCtx->promptChoice != 0) && (relStickX <= -30)) { + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->promptChoice = 0; + } + + step = ABS(VREG(61) - D_8082A6E0[VREG(62)]) / VREG(63); + if (VREG(61) >= D_8082A6E0[VREG(62)]) { + VREG(61) -= step; + } else { + VREG(61) += step; + } + + VREG(63)--; + if (VREG(63) == 0) { + VREG(61) = D_8082A6E0[VREG(62)]; + VREG(63) = VREG(60) + VREG(62); + VREG(62) ^= 1; + } + } +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h new file mode 100644 index 000000000..928ceadd7 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h @@ -0,0 +1,38 @@ +#ifndef Z_KALEIDO_SCOPE_H +#define Z_KALEIDO_SCOPE_H + +#include "ultra64.h" +#include "global.h" + +extern u8 gAmmoItems[]; +extern s16 D_8082AAEC[]; +extern s16 D_8082AB2C[]; +extern u8 gSlotAgeReqs[]; +extern u8 gEquipAgeReqs[][4]; +extern u8 gAreaGsFlags[]; + +void KaleidoScope_DrawQuestStatus(GlobalContext* globalCtx, GraphicsContext* gfxCtx); +s32 KaleidoScope_UpdateQuestStatusPoint(PauseContext* pauseCtx, s32 point); +void KaleidoScope_DrawDebugEditor(GlobalContext* globalCtx); +void KaleidoScope_DrawPlayerWork(GlobalContext* globalCtx); +void KaleidoScope_DrawEquipment(GlobalContext* globalCtx); +void KaleidoScope_SetCursorVtx(PauseContext* pauseCtx, u16 index, Vtx* vtx); +void KaleidoScope_DrawItemSelect(GlobalContext* globalCtx); +void KaleidoScope_UpdateItemEquip(GlobalContext* globalCtx); +void KaleidoScope_DrawDungeonMap(GlobalContext* globalCtx, GraphicsContext* gfxCtx); +void KaleidoScope_DrawWorldMap(GlobalContext* globalCtx, GraphicsContext* gfxCtx); +void KaleidoScope_UpdatePrompt(GlobalContext* globalCtx); +Gfx* KaleidoScope_QuadTextureIA4(Gfx* gfx, void* texture, s16 width, s16 height, u16 point); +Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point); +void KaleidoScope_MoveCursorToSpecialPos(GlobalContext* globalCtx, u16 specialPos); +void KaleidoScope_DrawQuadTextureRGBA32(GraphicsContext* gfxCtx, void* texture, u16 width, u16 height, u16 point); +void KaleidoScope_ProcessPlayerPreRender(); +void KaleidoScope_SetupPlayerPreRender(GlobalContext* globalCtx); +void KaleidoScope_DrawCursor(GlobalContext* globalCtx, u16 pageIndex); +void KaleidoScope_UpdateDungeonMap(GlobalContext* globalCtx); + +void PauseMapMark_Draw(GlobalContext* globalCtx); + +void KaleidoScope_UpdateCursorSize(PauseContext* pauseCtx); + +#endif diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c new file mode 100644 index 000000000..a5c160700 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -0,0 +1,4072 @@ +#include "z_kaleido_scope.h" +#include + +#include "textures/item_name_static/item_name_static.h" +#include "textures/icon_item_static/icon_item_static.h" +#include "textures/icon_item_24_static/icon_item_24_static.h" +#include "textures/icon_item_nes_static/icon_item_nes_static.h" +#include "textures/icon_item_ger_static/icon_item_ger_static.h" +#include "textures/icon_item_fra_static/icon_item_fra_static.h" +#include "textures/icon_item_gameover_static/icon_item_gameover_static.h" +#include "textures/icon_item_gameover_static/icon_item_gameover_static.h" +#include "textures/map_name_static/map_name_static.h" +#include "textures/map_48x85_static/map_48x85_static.h" +#include "vt.h" +#include "SohHooks.h" + +static void* sEquipmentFRATexs[] = { + gPauseEquipment00FRATex, gPauseEquipment01Tex, gPauseEquipment02Tex, gPauseEquipment03Tex, gPauseEquipment04Tex, + gPauseEquipment10FRATex, gPauseEquipment11Tex, gPauseEquipment12Tex, gPauseEquipment13Tex, gPauseEquipment14Tex, + gPauseEquipment20FRATex, gPauseEquipment21Tex, gPauseEquipment22Tex, gPauseEquipment23Tex, gPauseEquipment24Tex, +}; +static void* sSelectItemFRATexs[] = { + gPauseSelectItem00FRATex, gPauseSelectItem01Tex, gPauseSelectItem02Tex, gPauseSelectItem03Tex, + gPauseSelectItem04Tex, gPauseSelectItem10FRATex, gPauseSelectItem11Tex, gPauseSelectItem12Tex, + gPauseSelectItem13Tex, gPauseSelectItem14Tex, gPauseSelectItem20FRATex, gPauseSelectItem21Tex, + gPauseSelectItem22Tex, gPauseSelectItem23Tex, gPauseSelectItem24Tex, +}; +static void* sMapFRATexs[] = { + gPauseMap00Tex, gPauseMap01Tex, gPauseMap02Tex, gPauseMap03Tex, gPauseMap04Tex, + gPauseMap10FRATex, gPauseMap11Tex, gPauseMap12Tex, gPauseMap13Tex, gPauseMap14Tex, + gPauseMap20Tex, gPauseMap21Tex, gPauseMap22Tex, gPauseMap23Tex, gPauseMap24Tex, +}; +static void* sQuestStatusFRATexs[] = { + gPauseQuestStatus00Tex, gPauseQuestStatus01Tex, gPauseQuestStatus02Tex, gPauseQuestStatus03Tex, + gPauseQuestStatus04Tex, gPauseQuestStatus10FRATex, gPauseQuestStatus11Tex, gPauseQuestStatus12Tex, + gPauseQuestStatus13Tex, gPauseQuestStatus14Tex, gPauseQuestStatus20Tex, gPauseQuestStatus21Tex, + gPauseQuestStatus22Tex, gPauseQuestStatus23Tex, gPauseQuestStatus24Tex, +}; +static void* sSaveFRATexs[] = { + gPauseSave00FRATex, gPauseSave01Tex, gPauseSave02Tex, gPauseSave03Tex, gPauseSave04Tex, + gPauseSave10FRATex, gPauseSave11Tex, gPauseSave12Tex, gPauseSave13Tex, gPauseSave14Tex, + gPauseSave20FRATex, gPauseSave21Tex, gPauseSave22Tex, gPauseSave23Tex, gPauseSave24Tex, +}; + +static void* sEquipmentGERTexs[] = { + gPauseEquipment00GERTex, gPauseEquipment01Tex, gPauseEquipment02Tex, gPauseEquipment03Tex, gPauseEquipment04Tex, + gPauseEquipment10GERTex, gPauseEquipment11Tex, gPauseEquipment12Tex, gPauseEquipment13Tex, gPauseEquipment14Tex, + gPauseEquipment20GERTex, gPauseEquipment21Tex, gPauseEquipment22Tex, gPauseEquipment23Tex, gPauseEquipment24Tex, +}; +static void* sSelectItemGERTexs[] = { + gPauseSelectItem00GERTex, gPauseSelectItem01Tex, gPauseSelectItem02Tex, gPauseSelectItem03Tex, + gPauseSelectItem04Tex, gPauseSelectItem10GERTex, gPauseSelectItem11Tex, gPauseSelectItem12Tex, + gPauseSelectItem13Tex, gPauseSelectItem14Tex, gPauseSelectItem20GERTex, gPauseSelectItem21Tex, + gPauseSelectItem22Tex, gPauseSelectItem23Tex, gPauseSelectItem24Tex, +}; +static void* sMapGERTexs[] = { + gPauseMap00Tex, gPauseMap01Tex, gPauseMap02Tex, gPauseMap03Tex, gPauseMap04Tex, + gPauseMap10GERTex, gPauseMap11Tex, gPauseMap12Tex, gPauseMap13Tex, gPauseMap14Tex, + gPauseMap20Tex, gPauseMap21Tex, gPauseMap22Tex, gPauseMap23Tex, gPauseMap24Tex, +}; +static void* sQuestStatusGERTexs[] = { + gPauseQuestStatus00Tex, gPauseQuestStatus01Tex, gPauseQuestStatus02Tex, gPauseQuestStatus03Tex, + gPauseQuestStatus04Tex, gPauseQuestStatus10GERTex, gPauseQuestStatus11Tex, gPauseQuestStatus12Tex, + gPauseQuestStatus13Tex, gPauseQuestStatus14Tex, gPauseQuestStatus20Tex, gPauseQuestStatus21Tex, + gPauseQuestStatus22Tex, gPauseQuestStatus23Tex, gPauseQuestStatus24Tex, +}; +static void* sSaveGERTexs[] = { + gPauseSave00Tex, gPauseSave01Tex, gPauseSave02Tex, gPauseSave03Tex, gPauseSave04Tex, + gPauseSave10GERTex, gPauseSave11Tex, gPauseSave12Tex, gPauseSave13Tex, gPauseSave14Tex, + gPauseSave20GERTex, gPauseSave21Tex, gPauseSave22Tex, gPauseSave23Tex, gPauseSave24Tex, +}; + +static void* sEquipmentENGTexs[] = { + gPauseEquipment00Tex, gPauseEquipment01Tex, gPauseEquipment02Tex, gPauseEquipment03Tex, gPauseEquipment04Tex, + gPauseEquipment10ENGTex, gPauseEquipment11Tex, gPauseEquipment12Tex, gPauseEquipment13Tex, gPauseEquipment14Tex, + gPauseEquipment20Tex, gPauseEquipment21Tex, gPauseEquipment22Tex, gPauseEquipment23Tex, gPauseEquipment24Tex, +}; +static void* sSelectItemENGTexs[] = { + gPauseSelectItem00ENGTex, gPauseSelectItem01Tex, gPauseSelectItem02Tex, gPauseSelectItem03Tex, + gPauseSelectItem04Tex, gPauseSelectItem10ENGTex, gPauseSelectItem11Tex, gPauseSelectItem12Tex, + gPauseSelectItem13Tex, gPauseSelectItem14Tex, gPauseSelectItem20ENGTex, gPauseSelectItem21Tex, + gPauseSelectItem22Tex, gPauseSelectItem23Tex, gPauseSelectItem24Tex, +}; +static void* sMapENGTexs[] = { + gPauseMap00Tex, gPauseMap01Tex, gPauseMap02Tex, gPauseMap03Tex, gPauseMap04Tex, + gPauseMap10ENGTex, gPauseMap11Tex, gPauseMap12Tex, gPauseMap13Tex, gPauseMap14Tex, + gPauseMap20Tex, gPauseMap21Tex, gPauseMap22Tex, gPauseMap23Tex, gPauseMap24Tex, +}; +static void* sQuestStatusENGTexs[] = { + gPauseQuestStatus00ENGTex, gPauseQuestStatus01Tex, gPauseQuestStatus02Tex, gPauseQuestStatus03Tex, + gPauseQuestStatus04Tex, gPauseQuestStatus10ENGTex, gPauseQuestStatus11Tex, gPauseQuestStatus12Tex, + gPauseQuestStatus13Tex, gPauseQuestStatus14Tex, gPauseQuestStatus20ENGTex, gPauseQuestStatus21Tex, + gPauseQuestStatus22Tex, gPauseQuestStatus23Tex, gPauseQuestStatus24Tex, +}; +static void* sSaveENGTexs[] = { + gPauseSave00Tex, gPauseSave01Tex, gPauseSave02Tex, gPauseSave03Tex, gPauseSave04Tex, + gPauseSave10ENGTex, gPauseSave11Tex, gPauseSave12Tex, gPauseSave13Tex, gPauseSave14Tex, + gPauseSave20Tex, gPauseSave21Tex, gPauseSave22Tex, gPauseSave23Tex, gPauseSave24Tex, +}; + +static void* sGameOverTexs[] = { + gPauseSave00Tex, gPauseSave01Tex, gPauseSave02Tex, gPauseSave03Tex, gPauseSave04Tex, + gPauseGameOver10Tex, gPauseSave11Tex, gPauseSave12Tex, gPauseSave13Tex, gPauseSave14Tex, + gPauseSave20Tex, gPauseSave21Tex, gPauseSave22Tex, gPauseSave23Tex, gPauseSave24Tex, +}; + +static void* sDungeonMapTexs[] = { + gDekuTreePauseScreenMapFloor3LeftTex, + gDekuTreePauseScreenMapFloor3RightTex, + gDekuTreePauseScreenMapFloor2LeftTex, + gDekuTreePauseScreenMapFloor2RightTex, + gDekuTreePauseScreenMapFloor1LeftTex, + gDekuTreePauseScreenMapFloor1RightTex, + gDekuTreePauseScreenMapBasement1LeftTex, + gDekuTreePauseScreenMapBasement1RightTex, + gDekuTreePauseScreenMapBasement2LeftTex, + gDekuTreePauseScreenMapBasement2RightTex, + gDodongosCavernPauseScreenMapFloor2LeftTex, + gDodongosCavernPauseScreenMapFloor2RightTex, + gDodongosCavernPauseScreenMapFloor1LeftTex, + gDodongosCavernPauseScreenMapFloor1RightTex, + gJabuPauseScreenMapFloor1LeftTex, + gJabuPauseScreenMapFloor1RightTex, + gJabuPauseScreenMapBasement1LeftTex, + gJabuPauseScreenMapBasement1RightTex, + gForestTemplePauseScreenMapFloor2LeftTex, + gForestTemplePauseScreenMapFloor2RightTex, + gForestTemplePauseScreenMapFloor1LeftTex, + gForestTemplePauseScreenMapFloor1RightTex, + gForestTemplePauseScreenMapBasement1LeftTex, + gForestTemplePauseScreenMapBasement1RightTex, + gForestTemplePauseScreenMapBasement2LeftTex, + gForestTemplePauseScreenMapBasement2RightTex, + gFireTemplePauseScreenMapFloor5LeftTex, + gFireTemplePauseScreenMapFloor5RightTex, + gFireTemplePauseScreenMapFloor4LeftTex, + gFireTemplePauseScreenMapFloor4RightTex, + gFireTemplePauseScreenMapFloor3LeftTex, + gFireTemplePauseScreenMapFloor3RightTex, + gFireTemplePauseScreenMapFloor2LeftTex, + gFireTemplePauseScreenMapFloor2RightTex, + gFireTemplePauseScreenMapFloor1LeftTex, + gFireTemplePauseScreenMapFloor1RightTex, + gWaterTemplePauseScreenMapFloor3LeftTex, + gWaterTemplePauseScreenMapFloor3RightTex, + gWaterTemplePauseScreenMapFloor2LeftTex, + gWaterTemplePauseScreenMapFloor2RightTex, + gWaterTemplePauseScreenMapFloor1LeftTex, + gWaterTemplePauseScreenMapFloor1RightTex, + gWaterTemplePauseScreenMapBasement1LeftTex, + gWaterTemplePauseScreenMapBasement1RightTex, + gSpiritTemplePauseScreenMapFloor4LeftTex, + gSpiritTemplePauseScreenMapFloor4RightTex, + gSpiritTemplePauseScreenMapFloor3LeftTex, + gSpiritTemplePauseScreenMapFloor3RightTex, + gSpiritTemplePauseScreenMapFloor2LeftTex, + gSpiritTemplePauseScreenMapFloor2RightTex, + gSpiritTemplePauseScreenMapFloor1LeftTex, + gSpiritTemplePauseScreenMapFloor1RightTex, + gShadowTemplePauseScreenMapBasement1LeftTex, + gShadowTemplePauseScreenMapBasement1RightTex, + gShadowTemplePauseScreenMapBasement2LeftTex, + gShadowTemplePauseScreenMapBasement2RightTex, + gShadowTemplePauseScreenMapBasement3LeftTex, + gShadowTemplePauseScreenMapBasement3RightTex, + gShadowTemplePauseScreenMapBasement4LeftTex, + gShadowTemplePauseScreenMapBasement4RightTex, + gBottomOfTheWellPauseScreenMapBasement1LeftTex, + gBottomOfTheWellPauseScreenMapBasement1RightTex, + gBottomOfTheWellPauseScreenMapBasement2LeftTex, + gBottomOfTheWellPauseScreenMapBasement2RightTex, + gBottomOfTheWellPauseScreenMapBasement3LeftTex, + gBottomOfTheWellPauseScreenMapBasement3RightTex, + gIceCavernPauseScreenMapFloor1LeftTex, + gIceCavernPauseScreenMapFloor1RightTex, +}; + +static void* sEquipmentTexs[] = { + sEquipmentENGTexs, + sEquipmentGERTexs, + sEquipmentFRATexs, +}; + +static void* sSelectItemTexs[] = { + sSelectItemENGTexs, + sSelectItemGERTexs, + sSelectItemFRATexs, +}; + +static void* sMapTexs[] = { + sMapENGTexs, + sMapGERTexs, + sMapFRATexs, +}; + +static void* sQuestStatusTexs[] = { + sQuestStatusENGTexs, + sQuestStatusGERTexs, + sQuestStatusFRATexs, +}; + +static void* sSaveTexs[] = { + sSaveENGTexs, + sSaveGERTexs, + sSaveFRATexs, +}; + +static void* iconNameTextures[] = +{ + gDekuStickItemNameENGTex, + gDekuNutItemNameENGTex, + gBombItemNameENGTex, + gFairyBowItemNameENGTex, + gFireArrowItemNameENGTex, + gDinsFireItemNameENGTex, + gFairySlingshotItemNameENGTex, + gFairyOcarinaItemNameENGTex, + gOcarinaOfTimeItemNameENGTex, + gBombchuItemNameENGTex, + gHookshotItemNameENGTex, + gLongshotItemNameENGTex, + gIceArrowItemNameENGTex, + gFaroresWindItemNameENGTex, + gBoomerangItemNameENGTex, + gLensItemNameENGTex, + gMagicBeansItemNameENGTex, + gMegatonHammerItemNameENGTex, + gLightArrowItemNameENGTex, + gNayrusLoveItemNameENGTex, + gEmptyBottleItemNameENGTex, + gRedPotionItemNameENGTex, + gGreenPotionItemNameENGTex, + gBluePotionItemNameENGTex, + gBottledFairyItemNameENGTex, + gFishItemNameENGTex, + gFullMilkItemNameENGTex, + gRutosLetterItemNameENGTex, + gBlueFireItemNameENGTex, + gBugItemNameENGTex, + gBigPoeItemNameENGTex, + gHalfMilkItemNameENGTex, + gPoeItemNameENGTex, + gWeirdEggItemNameENGTex, + gCuccoItemNameENGTex, + gZeldasLetterItemNameENGTex, + gKeatonMaskItemNameENGTex, + gSkullMaskItemNameENGTex, + gSpookyMaskItemNameENGTex, + gBunnyHoodItemNameENGTex, + gGoronMaskItemNameENGTex, + gZoraMaskItemNameENGTex, + gGerudoMaskItemNameENGTex, + gMaskofTruthItemNameENGTex, + gSOLDOUTItemNameENGTex, + gPocketEggItemNameENGTex, + gPocketCuccoItemNameENGTex, + gCojiroItemNameENGTex, + gOddMushroomItemNameENGTex, + gOddPotionItemNameENGTex, + gPoachersSawItemNameENGTex, + gBrokenGoronsSwordItemNameENGTex, + gPrescriptionItemNameENGTex, + gEyeBallFrogItemNameENGTex, + gEyeDropsItemNameENGTex, + gClaimCheckItemNameENGTex, + gUnusedWindMedallionItemName1JPNTex, + gUnusedFireMedallionItemName1JPNTex, + gUnusedIceMedallionItemName1JPNTex, + gKokiriSwordItemNameENGTex, + gMasterSwordItemNameENGTex, + gGiantsKnifeItemNameENGTex, + gDekuShieldItemNameENGTex, + gHylianShieldItemNameENGTex, + gMirrorShieldItemNameENGTex, + gKokiriTunicItemNameENGTex, + gGoronTunicItemNameENGTex, + gZoraTunicItemNameENGTex, + gKokiriBootsItemNameENGTex, + gIronBootsItemNameENGTex, + gHoverBootsItemNameENGTex, + gBulletBag30ItemNameENGTex, + gBulletBag40ItemNameENGTex, + gBulletBag50ItemNameENGTex, + gQuiver30ItemNameENGTex, + gQuiver40ItemNameENGTex, + gQuiver50ItemNameENGTex, + gBombBag20ItemNameENGTex, + gBombBag30ItemNameENGTex, + gBombBag40ItemNameENGTex, + gGoronsBraceletItemNameENGTex, + gSilverGauntletsItemNameENGTex, + gGoldenGauntletsItemNameENGTex, + gSilverScaleItemNameENGTex, + gGoldenScaleItemNameENGTex, + gBrokenGiantsKnifeItemNameENGTex, + gUnusedBossKeyItemName1JPNTex, + gUnusedBossKeyItemName2JPNTex, + gUnusedBossKeyItemName3JPNTex, + gUnusedBossKeyItemName4JPNTex, + gMinuetOfForestItemNameENGTex, + gBoleroOfFireItemNameENGTex, + gSerenadeOfWaterItemNameENGTex, + gRequiemOfSpiritItemNameENGTex, + gNocturneOfShadowItemNameENGTex, + gPreludeOfLightItemNameENGTex, + gZeldasLullabyItemNameENGTex, + gEponasSongItemNameENGTex, + gSariasSongItemNameENGTex, + gSunsSongItemNameENGTex, + gSongOfTimeItemNameENGTex, + gSongOfStormsItemNameENGTex, + gForestMedallionItemNameENGTex, + gFireMedallionItemNameENGTex, + gWaterMedallionItemNameENGTex, + gSpiritMedallionItemNameENGTex, + gShadowMedallionItemNameENGTex, + gLightMedallionItemNameENGTex, + gKokiriEmeraldItemNameENGTex, + gGoronsRubyItemNameENGTex, + gZorasSapphireItemNameENGTex, + gStoneofAgonyItemNameENGTex, + gGerudosCardItemNameENGTex, + gGoldSkulltulaItemNameENGTex, + gPieceOfHeartItemNameENGTex, + gUnusedPieceOfHeartItemName1JPNTex, + gUnusedBigKeyItemNameENGTex, + gCompassItemNameENGTex, + gDungeonMapItemNameENGTex, + gUnusedBossKeyItemName5JPNTex, + gUnusedBossKeyItemName6JPNTex, + gUnusedBossKeyItemName7JPNTex, + gBiggoronsSwordItemNameENGTex, + gDekuStickItemNameGERTex, + gDekuNutItemNameGERTex, + gBombItemNameItemNameGERTex, + gFairyBowItemNameGERTex, + gFireArrowItemNameUnk2GERTex, + gDinsFireItemNameGERTex, + gFairySlingshotItemNameGERTex, + gFairyOcarinaItemNameGERTex, + gOcarinaOfTimeItemNameGERTex, + gBombchuItemNameGERTex, + gHookshotItemNameGERTex, + gLongshotItemNameGERTex, + gIceArrowItemNameGERTex, + gFaroresWindItemNameGERTex, + gBoomerangItemNameGERTex, + gLensItemNameGERTex, + gMagicBeansItemNameGERTex, + gMegatonHammerItemNameGERTex, + gLightArrowItemNameGERTex, + gNayrusLoveItemNameGERTex, + gEmptyBottleItemNameGERTex, + gRedPotionItemNameGERTex, + gGreenPotionItemNameGERTex, + gBluePotionItemNameGERTex, + gBottledFairyItemNameGERTex, + gFishItemNameGERTex, + gFullMilkItemNameGERTex, + gRutosLetterItemNameGERTex, + gBlueFireItemNameGERTex, + gBugItemNameGERTex, + gBigPoeItemNameGERTex, + gHalfMilkItemNameGERTex, + gPoeItemNameGERTex, + gWeirdEggItemNameGERTex, + gCuccoItemNameGERTex, + gZeldasLetterItemNameGERTex, + gKeatonMaskItemNameGERTex, + gSkullMaskItemNameGERTex, + gSpookyMaskItemNameGERTex, + gBunnyHoodItemNameGERTex, + gGoronMaskItemNameGERTex, + gZoraMaskItemNameGERTex, + gGerudoMaskItemNameGERTex, + gMaskofTruthItemNameGERTex, + gSOLDOUTItemNameGERTex, + gPocketEggItemNameGERTex, + gPocketCuccoItemNameGERTex, + gCojiroItemNameGERTex, + gOddMushroomItemNameGERTex, + gOddPotionItemNameGERTex, + gPoachersSawItemNameGERTex, + gBrokenGoronsSwordItemNameGERTex, + gPrescriptionItemNameGERTex, + gEyeBallFrogItemNameGERTex, + gEyeDropsItemNameGERTex, + gClaimCheckItemNameGERTex, + gUnusedWindMedallionItemName2JPNTex, + gUnusedFireMedallionItemName2JPNTex, + gUnusedIceMedallionItemName2JPNTex, + gKokiriSwordItemNameGERTex, + gMasterSwordItemNameGERTex, + gGiantsKnifeItemNameGERTex, + gDekuShieldItemNameGERTex, + gHylianShieldItemNameGERTex, + gMirrorShieldItemNameGERTex, + gKokiriTunicItemNameGERTex, + gGoronTunicItemNameGERTex, + gZoraTunicItemNameGERTex, + gKokiriBootsItemNameGERTex, + gIronBootsItemNameGERTex, + gHoverBootsItemNameGERTex, + gBulletBag30ItemNameGERTex, + gBulletBag40ItemNameGERTex, + gBulletBag50ItemNameGERTex, + gQuiver30ItemNameGERTex, + gQuiver40ItemNameGERTex, + gQuiver50ItemNameGERTex, + gBombBag20ItemNameGERTex, + gBombBag30ItemNameGERTex, + gBombBag40ItemNameGERTex, + gGoronsBraceletItemNameGERTex, + gSilverGauntletsItemNameGERTex, + gGoldenGauntletsItemNameGERTex, + gSilverScaleItemNameGERTex, + gGoldenScaleItemNameGERTex, + gBrokenGiantsKnifeItemNameGERTex, + gUnusedBossKeyItemName8JPNTex, + gUnusedBossKeyItemName9JPNTex, + gUnusedBossKeyItemName10JPNTex, + gUnusedBossKeyItemName11JPNTex, + gMinuetOfForestItemNameGERTex, + gBoleroOfFireItemNameGERTex, + gSerenadeOfWaterItemNameGERTex, + gRequiemOfSpiritItemNameGERTex, + gNocturneOfShadowItemNameGERTex, + gPreludeOfLightItemNameGERTex, + gZeldasLullabyItemNameGERTex, + gEponasSongItemNameGERTex, + gSariasSongItemNameGERTex, + gSunsSongItemNameGERTex, + gSongOfTimeItemNameGERTex, + gSongOfStormsItemNameGERTex, + gForestMedallionItemNameGERTex, + gFireMedallionItemNameGERTex, + gWaterMedallionItemNameGERTex, + gSpiritMedallionItemNameGERTex, + gShadowMedallionItemNameGERTex, + gLightMedallionItemNameGERTex, + gKokiriEmeraldItemNameGERTex, + gGoronsRubyItemNameGERTex, + gZorasSapphireItemNameGERTex, + gStoneofAgonyItemNameGERTex, + gGerudosCardItemNameGERTex, + gGoldSkulltulaItemNameGERTex, + gHeartContainerItemNameGERTex, + gUnusedPieceOfHeartItemName2JPNTex, + gBigKeyItemNameGERTex, + gCompassItemNameGERTex, + gDungeonMapItemNameGERTex, + gUnusedBossKeyItemName12JPNTex, + gUnusedBossKeyItemName13JPNTex, + gUnusedBossKeyItemName14JPNTex, + gBiggoronsSwordItemNameGERTex, + gDekuStickItemNameFRATex, + gDekuNutItemNameFRATex, + gBombItemNameFRATex, + gFairyBowItemNameFRATex, + gFireArrowItemNameFRATex, + gDinsFireItemNameFRATex, + gFairySlingshotItemNameFRATex, + gFairyOcarinaItemNameFRATex, + gOcarinaOfTimeItemNameFRATex, + gBombchuItemNameFRATex, + gHookshotItemNameFRATex, + gLongshotItemNameFRATex, + gIceArrowItemNameFRATex, + gFaroresWindItemNameFRATex, + gBoomerangItemNameFRATex, + gLensItemNameFRATex, + gMagicBeansItemNameFRATex, + gMegatonHammerItemNameFRATex, + gLightArrowItemNameFRATex, + gNayrusLoveItemNameFRATex, + gEmptyBottleItemNameFRATex, + gRedPotionItemNameFRATex, + gGreenPotionItemNameFRATex, + gBluePotionItemNameFRATex, + gBottledFairyItemNameFRATex, + gFishItemNameFRATex, + gFullMilkItemNameFRATex, + gRutosLetterItemNameFRATex, + gBlueFireItemNameFRATex, + gBugItemNameFRATex, + gBigPoeItemNameFRATex, + gHalfMilkItemNameFRATex, + gPoeItemNameFRATex, + gWeirdEggItemNameFRATex, + gCuccoItemNameFRATex, + gZeldasLetterItemNameFRATex, + gKeatonMaskItemNameFRATex, + gSkullMaskItemNameFRATex, + gSpookyMaskItemNameFRATex, + gBunnyHoodItemNameFRATex, + gGoronMaskItemNameFRATex, + gZoraMaskItemNameFRATex, + gGerudoMaskItemNameFRATex, + gMaskofTruthItemNameFRATex, + gSOLDOUTItemNameFRATex, + gPocketEggItemNameFRATex, + gPocketCuccoItemNameFRATex, + gCojiroItemNameFRATex, + gOddMushroomItemNameFRATex, + gOddPotionItemNameFRATex, + gPoachersSawItemNameFRATex, + gBrokenGoronsSwordItemNameFRATex, + gPrescriptionItemNameFRATex, + gEyeBallFrogItemNameFRATex, + gEyeDropsItemNameFRATex, + gClaimCheckItemNameFRATex, + gUnusedWindMedallionItemName3JPNTex, + gUnusedFireMedallionItemName3JPNTex, + gUnusedIceMedallionItemName3JPNTex, + gKokiriSwordItemNameFRATex, + gMasterSwordItemNameFRATex, + gGiantsKnifeItemNameFRATex, + gDekuShieldItemNameFRATex, + gHylianShieldItemNameFRATex, + gMirrorShieldItemNameFRATex, + gKokiriTunicItemNameFRATex, + gGoronTunicItemNameFRATex, + gZoraTunicItemNameFRATex, + gKokiriBootsItemNameFRATex, + gIronBootsItemNameFRATex, + gHoverBootsItemNameFRATex, + gBulletBag30ItemNameFRATex, + gBulletBag40ItemNameFRATex, + gBulletBag50ItemNameFRATex, + gQuiver30ItemNameFRATex, + gQuiver40ItemNameFRATex, + gQuiver50ItemNameFRATex, + gBombBag20ItemNameFRATex, + gBombBag30ItemNameFRATex, + gBombBag40ItemNameFRATex, + gGoronsBraceletItemNameFRATex, + gSilverGauntletsItemNameFRATex, + gGoldenGauntletsItemNameFRATex, + gSilverScaleItemNameFRATex, + gGoldenScaleItemNameFRATex, + gBrokenGiantsKnifeItemNameFRATex, + gUnusedBossKeyItemName15JPNTex, + gUnusedBossKeyItemName16JPNTex, + gUnusedBossKeyItemName17JPNTex, + gUnusedBossKeyItemName18JPNTex, + gMinuetOfForestItemNameFRATex, + gBoleroOfFireItemNameFRATex, + gSerenadeOfWaterItemNameFRATex, + gRequiemOfSpiritItemNameFRATex, + gNocturneOfShadowItemNameFRATex, + gPreludeOfLightItemNameFRATex, + gZeldasLullabyItemNameFRATex, + gEponasSongItemNameFRATex, + gSariasSongItemNameFRATex, + gSunsSongItemNameFRATex, + gSongOfTimeItemNameFRATex, + gSongOfStormsItemNameFRATex, + gForestMedallionItemNameFRATex, + gFireMedallionItemNameFRATex, + gWaterMedallionItemNameFRATex, + gSpiritMedallionItemNameFRATex, + gShadowMedallionItemNameFRATex, + gLightMedallionItemNameFRATex, + gKokiriEmeraldItemNameFRATex, + gGoronsRubyItemNameFRATex, + gZorasSapphireItemNameFRATex, + gStoneofAgonyItemNameFRATex, + gGerudosCardItemNameFRATex, + gGoldSkulltulaItemNameFRATex, + gHeartContainerItemNameFRATex, + gUnusedPieceOfHeartItemName3JPNTex, + gBossKeyItemNameFRATex, + gCompassItemNameFRATex, + gDungeonMapItemNameFRATex, + gUnusedBossKeyItemName19JPNTex, + gUnusedBossKeyItemName20JPNTex, + gUnusedBossKeyItemName21JPNTex, + gBiggoronsSwordItemNameFRATex, + gBiggoronsSwordItemNameFRATex, +}; + +static void* mapNameTextures[] = +{ + gHauntedWastelandPointNameENGTex, + gGerudosFortressPointNameENGTex, + gGerudoValleyPointNameENGTex, + gHyliaLakesidePointNameENGTex, + gLonLonRanchPointNameENGTex, + gMarketPointNameENGTex, + gHyruleFieldPointNameENGTex, + gDeathMountainPointNameENGTex, + gKakarikoVillagePointNameENGTex, + gLostWoodsPointNameENGTex, + gKokiriForestPointNameENGTex, + gZorasDomainPointNameENGTex, + gHauntedWastelandPointNameGERTex, + gGerudosFortressPointNameGERTex, + gGerudoValleyPointNameGERTex, + gHyliaLakesidePointNameGERTex, + gLonLonRanchPointNameGERTex, + gMarketPointNameGERTex, + gHyruleFieldPointNameGERTex, + gDeathMountainPointNameGERTex, + gKakarikoVillagePointNameGERTex, + gLostWoodsPointNameGERTex, + gKokiriForestPointNameGERTex, + gZorasDomainPointNameGERTex, + gHauntedWastelandPointNameFRATex, + gGerudosFortressPointNameFRATex, + gGerudoValleyPointNameFRATex, + gHyliaLakesidePointNameFRATex, + gLonLonRanchPointNameFRATex, + gMarketPointNameFRATex, + gHyruleFieldPointNameFRATex, + gDeathMountainPointNameFRATex, + gKakarikoVillagePointNameFRATex, + gLostWoodsPointNameFRATex, + gKokiriForestPointNameFRATex, + gZorasDomainPointNameFRATex, + gHyruleFieldPositionNameENGTex, + gKakarikoVillagePositionNameENGTex, + gGraveyardPositionNameENGTex, + gZorasRiverPositionNameENGTex, + gKokiriForestPositionNameENGTex, + gSacredForestMeadowPositionNameENGTex, + gLakeHyliaPositionNameENGTex, + gZorasDomainPositionNameENGTex, + gZorasFountainPositionNameENGTex, + gGerudoValleyPositionNameENGTex, + gLostWoodsPositionNameENGTex, + gDesertColossusPositionNameENGTex, + gGerudosFortressPositionNameENGTex, + gHauntedWastelandPositionNameENGTex, + gMarketPositionNameENGTex, + gHyruleCastlePositionNameENGTex, + gDeathMountainTrailPositionNameENGTex, + gDeathMountainCraterPositionNameENGTex, + gGoronCityPositionNameENGTex, + gLonLonRanchPositionNameENGTex, + gQuestionMarkPositionNameENGTex, + gGanonsCastlePositionNameENGTex, + gHyruleFieldPositionNameGERTex, + gKakarikoVillagePositionNameGERTex, + gGraveyardPositionNameGERTex, + gZorasRiverPositionNameGERTex, + gKokiriForestPositionNameGERTex, + gSacredForestMeadowPositionNameGERTex, + gLakeHyliaPositionNameGERTex, + gZorasDomainPositionNameGERTex, + gZorasFountainPositionNameGERTex, + gGerudoValleyPositionNameGERTex, + gLostWoodsPositionNameGERTex, + gDesertColossusPositionNameGERTex, + gGerudosFortressPositionNameGERTex, + gHauntedWastelandPositionNameGERTex, + gMarketPositionNameGERTex, + gHyruleCastlePositionNameGERTex, + gDeathMountainTrailPositionNameGERTex, + gDeathMountainCraterPositionNameGERTex, + gGoronCityPositionNameGERTex, + gLonLonRanchPositionNameGERTex, + gQuestionMarkPositionNameGERTex, + gGanonsCastlePositionNameGERTex, + gHyruleFieldPositionNameFRATex, + gKakarikoVillagePositionNameFRATex, + gGraveyardPositionNameFRATex, + gZorasRiverPositionNameFRATex, + gKokiriForestPositionNameFRATex, + gSacredForestMeadowPositionNameFRATex, + gLakeHyliaPositionNameFRATex, + gZorasDomainPositionNameFRATex, + gZorasFountainPositionNameFRATex, + gGerudoValleyPositionNameFRATex, + gLostWoodsPositionNameFRATex, + gDesertColossusPositionNameFRATex, + gGerudosFortressPositionNameFRATex, + gHauntedWastelandPositionNameFRATex, + gMarketPositionNameFRATex, + gHyruleCastlePositionNameFRATex, + gDeathMountainTrailPositionNameFRATex, + gDeathMountainCraterPositionNameFRATex, + gGoronCityPositionNameFRATex, + gLonLonRanchPositionNameFRATex, + gQuestionMarkPositionNameFRATex, + gGanonsCastlePositionNameFRATex, +}; + +s16 D_8082AAEC[] = { + 32, 112, 32, 48, 32, 32, 32, 48, 32, 64, 32, 48, 48, 48, 48, 64, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 80, 64, +}; + +s16 D_8082AB2C[] = { + 24, 72, 13, 22, 19, 20, 19, 27, 14, 26, 22, 21, 49, 32, 45, 60, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 32, 8, +}; + +static u8 D_8082AB6C[][5] = { + { BTN_ENABLED, BTN_DISABLED, BTN_DISABLED, BTN_DISABLED, BTN_ENABLED }, + { BTN_ENABLED, BTN_ENABLED, BTN_ENABLED, BTN_ENABLED, BTN_DISABLED }, + { BTN_ENABLED, BTN_DISABLED, BTN_DISABLED, BTN_DISABLED, BTN_DISABLED }, + { BTN_ENABLED, BTN_DISABLED, BTN_DISABLED, BTN_DISABLED, BTN_ENABLED }, + { BTN_ENABLED, BTN_DISABLED, BTN_DISABLED, BTN_DISABLED, BTN_ENABLED }, + { BTN_ENABLED, BTN_ENABLED, BTN_ENABLED, BTN_ENABLED, BTN_DISABLED }, +}; + +static s16 D_8082AB8C = 0; +static s16 D_8082AB90 = 0; +static s16 D_8082AB94 = 0; +static s16 D_8082AB98 = 255; + +static s16 D_8082AB9C = 255; +static s16 D_8082ABA0 = 0; +static s16 D_8082ABA4 = 0; + +static s16 sInDungeonScene = false; + +static f32 D_8082ABAC[] = { + -4.0f, 4.0f, 4.0f, 4.0f, 4.0f, -4.0f, -4.0f, -4.0f, +}; + +static f32 D_8082ABCC[] = { + -4.0f, -4.0f, -4.0f, 4.0f, 4.0f, 4.0f, 4.0f, -4.0f, +}; + +static u16 D_8082ABEC[] = { + PAUSE_MAP, PAUSE_EQUIP, PAUSE_QUEST, PAUSE_ITEM, PAUSE_EQUIP, PAUSE_MAP, PAUSE_ITEM, PAUSE_QUEST, +}; + +u8 gSlotAgeReqs[] = { + 1, 9, 9, 0, 0, 9, 1, 9, 9, 0, 0, 9, 1, 9, 1, 0, 0, 9, 9, 9, 9, 9, 0, 1, +}; + +u8 gEquipAgeReqs[][4] = { + { 0, 1, 0, 0 }, + { 9, 1, 9, 0 }, + { 0, 9, 0, 0 }, + { 9, 9, 0, 0 }, +}; + +u8 gItemAgeReqs[] = { + 1, 9, 9, 0, 0, 9, 1, 9, 9, 9, 0, 0, 0, 9, 1, 9, 1, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 1, 9, 0, 9, 0, 0, 9, 0, 0, 1, 1, 1, 0, 0, 0, 9, 9, 9, 1, 0, 0, 9, 9, 0, +}; + +u8 gAreaGsFlags[] = { + 0x0F, 0x1F, 0x0F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x07, 0x07, 0x03, + 0x0F, 0x07, 0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0x1F, 0x0F, 0x03, 0x0F, +}; + +static void* sCursorTexs[] = { + gPauseMenuCursorTopLeftTex, + gPauseMenuCursorTopRightTex, + gPauseMenuCursorBottomLeftTex, + gPauseMenuCursorBottomRightTex, +}; + +static s16 sCursorColors[][3] = { + { 255, 255, 255 }, + { 255, 255, 0 }, + { 0, 255, 50 }, +}; + +static void* sSavePromptTexs[] = { + gPauseSavePromptENGTex, + gPauseSavePromptGERTex, + gPauseSavePromptFRATex, +}; + +static void* sSaveConfirmationTexs[] = { + gPauseSaveConfirmationENGTex, + gPauseSaveConfirmationGERTex, + gPauseSaveConfirmationFRATex, +}; + +static void* sContinuePromptTexs[] = { + gContinuePlayingENGTex, + gContinuePlayingGERTex, + gContinuePlayingFRATex, +}; + +static void* sPromptChoiceTexs[][2] = { + { gPauseYesENGTex, gPauseNoENGTex }, + { gPauseYesGERTex, gPauseNoGERTex }, + { gPauseYesFRATex, gPauseNoFRATex }, +}; + +static u8 D_808321A8[5]; +static PreRender sPlayerPreRender; +static void* sPreRenderCvg; +extern int fbTest; + +// OTRTODO: This function is no longer used. We could probably remove it. +void KaleidoScope_SetupPlayerPreRender(GlobalContext* globalCtx) { + Gfx* gfx; + Gfx* gfxRef; + void* fbuf; + static Gfx testBuffer[2048]; + + //return; + + fbuf = globalCtx->state.gfxCtx->curFrameBuffer; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 496); + + gfx = &testBuffer[0]; + + PreRender_SetValues(&sPlayerPreRender, 64, 112, fbuf, NULL); + func_800C1F20(&sPlayerPreRender, &gfx); + func_800C20B4(&sPlayerPreRender, &gfx); + + gSPEndDisplayList(gfx++); + gSPDisplayList(POLY_KAL_DISP++, &testBuffer[0]); + + SREG(33) |= 1; + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 509); +} +//OTRTODO - Player on pause +#if 1 +void KaleidoScope_ProcessPlayerPreRender(void) { + //Sleep_Msec(50); + PreRender_Calc(&sPlayerPreRender); + PreRender_Destroy(&sPlayerPreRender); +} +#endif + +Gfx* KaleidoScope_QuadTextureIA4(Gfx* gfx, void* texture, s16 width, s16 height, u16 point) { + gDPLoadTextureBlock_4b(gfx++, texture, G_IM_FMT_IA, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(gfx++, point, point + 2, point + 3, point + 1, 0); + + return gfx; +} + +Gfx* KaleidoScope_QuadTextureIA8(Gfx* gfx, void* texture, s16 width, s16 height, u16 point) { + gDPLoadTextureBlock(gfx++, texture, G_IM_FMT_IA, G_IM_SIZ_8b, width, height, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(gfx++, point, point + 2, point + 3, point + 1, 0); + + return gfx; +} + +void KaleidoScope_OverridePalIndexCI4(u8* texture, ptrdiff_t size, s32 targetIndex, s32 newIndex) { + s32 i; + + targetIndex &= 0xF; + newIndex &= 0xF; + + if ((size == 0) || (targetIndex == newIndex) || (texture == NULL)) { + return; + } + + for (i = 0; i < size; i++) { + s32 index1; + s32 index2; + + index1 = index2 = texture[i]; + + index1 = (index1 >> 4) & 0xF; + index2 = index2 & 0xF; + + if (index1 == targetIndex) { + index1 = newIndex; + } + + if (index2 == targetIndex) { + index2 = newIndex; + } + + texture[i] = (index1 << 4) | index2; + } +} + +void KaleidoScope_MoveCursorToSpecialPos(GlobalContext* globalCtx, u16 specialPos) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + + pauseCtx->cursorSpecialPos = specialPos; + pauseCtx->pageSwitchTimer = 0; + + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); +} + +void KaleidoScope_DrawQuadTextureRGBA32(GraphicsContext* gfxCtx, void* texture, u16 width, u16 height, u16 point) { + OPEN_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 748); + + gDPLoadTextureBlock(POLY_KAL_DISP++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, width, height, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_KAL_DISP++, point, point + 2, point + 3, point + 1, 0); + + CLOSE_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 758); +} + +void KaleidoScope_SetDefaultCursor(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 s; + s16 i; + + switch (pauseCtx->pageIndex) { + case PAUSE_ITEM: + s = pauseCtx->cursorSlot[PAUSE_ITEM]; + if (gSaveContext.inventory.items[s] == ITEM_NONE) { + i = s + 1; + while (true) { + if (gSaveContext.inventory.items[i] != ITEM_NONE) { + break; + } + i++; + if (i >= 24) { + i = 0; + } + if (i == s) { + pauseCtx->cursorItem[PAUSE_ITEM] = pauseCtx->namedItem = PAUSE_ITEM_NONE; + return; + } + } + pauseCtx->cursorItem[PAUSE_ITEM] = gSaveContext.inventory.items[i]; + pauseCtx->cursorSlot[PAUSE_ITEM] = i; + } + break; + case PAUSE_MAP: + case PAUSE_QUEST: + case PAUSE_EQUIP: + break; + } +} + +void KaleidoScope_SwitchPage(PauseContext* pauseCtx, u8 pt) { + pauseCtx->unk_1E4 = 1; + pauseCtx->unk_1EA = 0; + + if (!pt) { + pauseCtx->mode = pauseCtx->pageIndex * 2 + 1; + Audio_PlaySoundGeneral(NA_SE_SY_WIN_SCROLL_LEFT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->cursorSpecialPos = PAUSE_CURSOR_PAGE_RIGHT; + } else { + pauseCtx->mode = pauseCtx->pageIndex * 2; + Audio_PlaySoundGeneral(NA_SE_SY_WIN_SCROLL_RIGHT, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->cursorSpecialPos = PAUSE_CURSOR_PAGE_LEFT; + } + + gSaveContext.buttonStatus[1] = D_8082AB6C[pauseCtx->pageIndex + pt][1]; + gSaveContext.buttonStatus[2] = D_8082AB6C[pauseCtx->pageIndex + pt][2]; + gSaveContext.buttonStatus[3] = D_8082AB6C[pauseCtx->pageIndex + pt][3]; + gSaveContext.buttonStatus[4] = D_8082AB6C[pauseCtx->pageIndex + pt][4]; + + osSyncPrintf("kscope->kscp_pos+pt = %d\n", pauseCtx->pageIndex + pt); + + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); +} + +void KaleidoScope_HandlePageToggles(PauseContext* pauseCtx, Input* input) { + if (CVar_GetS32("gDebugEnabled", 0) && (pauseCtx->debugState == 0) && CHECK_BTN_ALL(input->press.button, BTN_L)) { + pauseCtx->debugState = 1; + return; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_R)) { + KaleidoScope_SwitchPage(pauseCtx, 2); + return; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_Z)) { + KaleidoScope_SwitchPage(pauseCtx, 0); + return; + } + + if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + if (pauseCtx->stickRelX < -30) { + pauseCtx->pageSwitchTimer++; + if ((pauseCtx->pageSwitchTimer >= 10) || (pauseCtx->pageSwitchTimer == 0)) { + KaleidoScope_SwitchPage(pauseCtx, 0); + } + } else { + pauseCtx->pageSwitchTimer = -1; + } + } else if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT) { + if (pauseCtx->stickRelX > 30) { + pauseCtx->pageSwitchTimer++; + if ((pauseCtx->pageSwitchTimer >= 10) || (pauseCtx->pageSwitchTimer == 0)) { + KaleidoScope_SwitchPage(pauseCtx, 2); + } + } else { + pauseCtx->pageSwitchTimer = -1; + } + } +} + +void KaleidoScope_DrawCursor(GlobalContext* globalCtx, u16 pageIndex) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + u16 temp; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 955); + + temp = pauseCtx->unk_1E4; + + if ((((pauseCtx->unk_1E4 == 0) || (temp == 8)) && (pauseCtx->state == 6)) || + ((pauseCtx->pageIndex == PAUSE_QUEST) && ((temp < 3) || (temp == 5) || (temp == 8)))) { + + if (pauseCtx->pageIndex == pageIndex) { + s16 i; + s16 j; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, sCursorColors[pauseCtx->cursorColorSet >> 2][0], + sCursorColors[pauseCtx->cursorColorSet >> 2][1], + sCursorColors[pauseCtx->cursorColorSet >> 2][2], 255); + gDPSetEnvColor(POLY_KAL_DISP++, D_8082AB8C, D_8082AB90, D_8082AB94, 255); + gSPVertex(POLY_KAL_DISP++, pauseCtx->cursorVtx, 16, 0); + + for (i = j = 0; i < 4; i++, j += 4) { + gDPLoadTextureBlock_4b(POLY_KAL_DISP++, sCursorTexs[i], G_IM_FMT_IA, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(POLY_KAL_DISP++, j, j + 2, j + 3, j + 1, 0); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 255); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 985); +} + +Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, void** textures) { + s32 i; + s32 j; + + gSPVertex(gfx++, vertices, 32, 0); + + i = 0; + j = 0; + while (j < 32) { + gDPPipeSync(gfx++); + gDPLoadTextureBlock(gfx++, textures[i], G_IM_FMT_IA, G_IM_SIZ_8b, 80, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(gfx++, j, j + 2, j + 3, j + 1, 0); + + j += 4; + i++; + } + + gSPVertex(gfx++, vertices + 32, 28, 0); + + j = 0; + while (j < 28) { + gDPPipeSync(gfx++); + gDPLoadTextureBlock(gfx++, textures[i], G_IM_FMT_IA, G_IM_SIZ_8b, 80, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + gSP1Quadrangle(gfx++, j, j + 2, j + 3, j + 1, 0); + + j += 4; + i++; + } + + return gfx; +} + +void KaleidoScope_DrawPages(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { + static s16 D_8082ACF4[][3] = { + { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 255, 255, 0 }, { 0, 0, 0 }, + { 0, 0, 0 }, { 255, 255, 0 }, { 0, 255, 50 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 255, 50 }, + }; + static s16 D_8082AD3C = 20; + static s16 D_8082AD40 = 0; + static s16 D_8082AD44 = 0; + static s16 D_8082AD48 = 0; + static s16 D_8082AD4C = 0; + static s16 D_8082AD50 = 0; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 stepR; + s16 stepG; + s16 stepB; + + OPEN_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 1100); + + if ((pauseCtx->state < 8) || (pauseCtx->state > 0x11)) { + if (pauseCtx->state != 7) { + stepR = ABS(D_8082AB8C - D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][0]) / D_8082AD3C; + stepG = ABS(D_8082AB90 - D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][1]) / D_8082AD3C; + stepB = ABS(D_8082AB94 - D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][2]) / D_8082AD3C; + if (D_8082AB8C >= D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][0]) { + D_8082AB8C -= stepR; + } else { + D_8082AB8C += stepR; + } + if (D_8082AB90 >= D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][1]) { + D_8082AB90 -= stepG; + } else { + D_8082AB90 += stepG; + } + if (D_8082AB94 >= D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][2]) { + D_8082AB94 -= stepB; + } else { + D_8082AB94 += stepB; + } + + D_8082AD3C--; + if (D_8082AD3C == 0) { + D_8082AB8C = D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][0]; + D_8082AB90 = D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][1]; + D_8082AB94 = D_8082ACF4[pauseCtx->cursorColorSet + D_8082AD40][2]; + D_8082AD3C = ZREG(28 + D_8082AD40); + D_8082AD40++; + if (D_8082AD40 >= 4) { + D_8082AD40 = 0; + } + } + + if (pauseCtx->stickRelX < -30) { + if (D_8082AD4C == -1) { + if (--D_8082AD44 < 0) { + D_8082AD44 = XREG(6); + } else { + pauseCtx->stickRelX = 0; + } + } else { + D_8082AD44 = XREG(8); + D_8082AD4C = -1; + } + } else if (pauseCtx->stickRelX > 30) { + if (D_8082AD4C == 1) { + if (--D_8082AD44 < 0) { + D_8082AD44 = XREG(6); + } else { + pauseCtx->stickRelX = 0; + } + } else { + D_8082AD44 = XREG(8); + D_8082AD4C = 1; + } + } else { + D_8082AD4C = 0; + } + + if (pauseCtx->stickRelY < -30) { + if (D_8082AD50 == -1) { + if (--D_8082AD48 < 0) { + D_8082AD48 = XREG(6); + } else { + pauseCtx->stickRelY = 0; + } + } else { + D_8082AD48 = XREG(8); + D_8082AD50 = -1; + } + } else if (pauseCtx->stickRelY > 30) { + if (D_8082AD50 == 1) { + if (--D_8082AD48 < 0) { + D_8082AD48 = XREG(6); + } else { + pauseCtx->stickRelY = 0; + } + } else { + D_8082AD48 = XREG(8); + D_8082AD50 = 1; + } + } else { + D_8082AD50 = 0; + } + } + + if (pauseCtx->pageIndex) { // pageIndex != PAUSE_ITEM + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + Matrix_Translate(0.0f, (f32)WREG(2) / 100.0f, -(f32)WREG(3) / 100.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(-pauseCtx->unk_1F4 / 100.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1173), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->itemPageVtx, + sSelectItemTexs[gSaveContext.language]); + + KaleidoScope_DrawItemSelect(globalCtx); + } + + if (pauseCtx->pageIndex != PAUSE_EQUIP) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + Matrix_Translate(-(f32)WREG(3) / 100.0f, (f32)WREG(2) / 100.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateZ(pauseCtx->unk_1F8 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(1.57f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1196), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->equipPageVtx, + sEquipmentTexs[gSaveContext.language]); + + KaleidoScope_DrawEquipment(globalCtx); + } + + if (pauseCtx->pageIndex != PAUSE_QUEST) { + gDPPipeSync(POLY_KAL_DISP++); + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_BILERP); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + Matrix_Translate(0.0f, (f32)WREG(2) / 100.0f, (f32)WREG(3) / 100.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(pauseCtx->unk_200 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(3.14f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1220), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->questPageVtx, + sQuestStatusTexs[gSaveContext.language]); + + KaleidoScope_DrawQuestStatus(globalCtx, gfxCtx); + } + + if (pauseCtx->pageIndex != PAUSE_MAP) { + gDPPipeSync(POLY_KAL_DISP++); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + Matrix_Translate((f32)WREG(3) / 100.0f, (f32)WREG(2) / 100.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateZ(-pauseCtx->unk_1FC / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(-1.57f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1243), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = + KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->mapPageVtx, sMapTexs[gSaveContext.language]); + + if (sInDungeonScene) { + KaleidoScope_DrawDungeonMap(globalCtx, gfxCtx); + func_800949A8(gfxCtx); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + if (CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, gSaveContext.mapIndex)) { + PauseMapMark_Draw(globalCtx); + } + } else { + KaleidoScope_DrawWorldMap(globalCtx, gfxCtx); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + switch (pauseCtx->pageIndex) { + case PAUSE_ITEM: + Matrix_Translate(0.0f, (f32)WREG(2) / 100.0f, -(f32)WREG(3) / 100.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(-pauseCtx->unk_1F4 / 100.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1281), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->itemPageVtx, + sSelectItemTexs[gSaveContext.language]); + + KaleidoScope_DrawItemSelect(globalCtx); + break; + + case PAUSE_MAP: + Matrix_Translate((f32)WREG(3) / 100.0f, (f32)WREG(2) / 100.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateZ(-pauseCtx->unk_1FC / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(-1.57f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1303), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = + KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->mapPageVtx, sMapTexs[gSaveContext.language]); + + if (sInDungeonScene) { + KaleidoScope_DrawDungeonMap(globalCtx, gfxCtx); + func_800949A8(gfxCtx); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + if (pauseCtx->cursorSpecialPos == 0) { + KaleidoScope_DrawCursor(globalCtx, PAUSE_MAP); + } + + if (CHECK_DUNGEON_ITEM(DUNGEON_COMPASS, gSaveContext.mapIndex)) { + PauseMapMark_Draw(globalCtx); + } + } else { + KaleidoScope_DrawWorldMap(globalCtx, gfxCtx); + } + break; + + case PAUSE_QUEST: + gDPSetTextureFilter(POLY_KAL_DISP++, G_TF_BILERP); + + Matrix_Translate(0.0f, (f32)WREG(2) / 100.0f, (f32)WREG(3) / 100.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(pauseCtx->unk_200 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(3.14f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1343), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->questPageVtx, + sQuestStatusTexs[gSaveContext.language]); + + KaleidoScope_DrawQuestStatus(globalCtx, gfxCtx); + + if (pauseCtx->cursorSpecialPos == 0) { + KaleidoScope_DrawCursor(globalCtx, PAUSE_QUEST); + } + break; + + case PAUSE_EQUIP: + Matrix_Translate(-(f32)WREG(3) / 100.0f, (f32)WREG(2) / 100.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateZ(pauseCtx->unk_1F8 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(1.57f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1367), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->equipPageVtx, + sEquipmentTexs[gSaveContext.language]); + + KaleidoScope_DrawEquipment(globalCtx); + + if (pauseCtx->cursorSpecialPos == 0) { + KaleidoScope_DrawCursor(globalCtx, PAUSE_EQUIP); + } + break; + } + } + + func_800949A8(gfxCtx); + + if ((pauseCtx->state == 7) || ((pauseCtx->state >= 8) && (pauseCtx->state < 0x12))) { + KaleidoScope_UpdatePrompt(globalCtx); + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + + if (!pauseCtx->pageIndex) { // pageIndex == PAUSE_ITEM + pauseCtx->unk_1F4 = pauseCtx->unk_204 + 314.0f; + + Matrix_Translate(0.0f, (f32)WREG(2) / 100.0f, -pauseCtx->unk_1F0 / 10.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(-pauseCtx->unk_204 / 100.0f, MTXMODE_APPLY); + } else if (pauseCtx->pageIndex == PAUSE_MAP) { + pauseCtx->unk_1FC = pauseCtx->unk_204 + 314.0f; + + Matrix_Translate(pauseCtx->unk_1F0 / 10.0f, (f32)WREG(2) / 100.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateZ(-pauseCtx->unk_204 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(-1.57f, MTXMODE_APPLY); + } else if (pauseCtx->pageIndex == PAUSE_QUEST) { + pauseCtx->unk_200 = pauseCtx->unk_204 + 314.0f; + + Matrix_Translate(0.0f, (f32)WREG(2) / 100.0f, pauseCtx->unk_1F0 / 10.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateX(pauseCtx->unk_204 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(3.14f, MTXMODE_APPLY); + } else { + pauseCtx->unk_1F8 = pauseCtx->unk_204 + 314.0f; + + Matrix_Translate(-pauseCtx->unk_1F0 / 10.0f, (f32)WREG(2) / 100.0f, 0.0f, MTXMODE_NEW); + Matrix_Scale(0.78f, 0.78f, 0.78f, MTXMODE_APPLY); + Matrix_RotateZ(pauseCtx->unk_204 / 100.0f, MTXMODE_APPLY); + Matrix_RotateY(1.57f, MTXMODE_APPLY); + } + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(gfxCtx, "../z_kaleido_scope_PAL.c", 1424), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + if ((pauseCtx->state >= 8) && (pauseCtx->state <= 0x11)) { + POLY_KAL_DISP = KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->saveVtx, sGameOverTexs); + } else { + POLY_KAL_DISP = + KaleidoScope_DrawPageSections(POLY_KAL_DISP, pauseCtx->saveVtx, sSaveTexs[gSaveContext.language]); + } + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->saveVtx[60], 32, 0); + + if (((pauseCtx->state == 7) && (pauseCtx->unk_1EC < 4)) || (pauseCtx->state == 0xE)) { + POLY_KAL_DISP = + KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sSavePromptTexs[gSaveContext.language], 152, 16, 0); + + gDPSetCombineLERP(POLY_KAL_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, TEXEL0, + 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 100, 255, 100, VREG(61)); + + if (pauseCtx->promptChoice == 0) { + gSPDisplayList(POLY_KAL_DISP++, gPromptCursorLeftDL); + } else { + gSPDisplayList(POLY_KAL_DISP++, gPromptCursorRightDL); + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + POLY_KAL_DISP = + KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][0], 48, 16, 12); + + POLY_KAL_DISP = + KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][1], 48, 16, 16); + } else if ((pauseCtx->state != 7) || (pauseCtx->unk_1EC < 4)) { + if ((pauseCtx->state != 0xF) && ((pauseCtx->state == 0x10) || (pauseCtx->state == 0x11))) { + POLY_KAL_DISP = + KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sContinuePromptTexs[gSaveContext.language], 152, 16, 0); + + gDPSetCombineLERP(POLY_KAL_DISP++, 1, 0, PRIMITIVE, 0, TEXEL0, 0, PRIMITIVE, 0, 1, 0, PRIMITIVE, 0, + TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 100, 255, 100, VREG(61)); + + if (pauseCtx->promptChoice == 0) { + gSPDisplayList(POLY_KAL_DISP++, gPromptCursorLeftDL); + } else { + gSPDisplayList(POLY_KAL_DISP++, gPromptCursorRightDL); + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + + POLY_KAL_DISP = + KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][0], 48, 16, 12); + + POLY_KAL_DISP = + KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, sPromptChoiceTexs[gSaveContext.language][1], 48, 16, 16); + } + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + if ((pauseCtx->state != 0x10) && (pauseCtx->state != 0x11)) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 0, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); + } + } + + CLOSE_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 1577); +} + +void KaleidoScope_DrawInfoPanel(GlobalContext* globalCtx) { + static void* D_8082AD54[3] = { + gPauseToEquipENGTex, + gPauseToEquipGERTex, + gPauseToEquipFRATex, + }; + static void* D_8082AD60[3] = { + gPauseToDecideENGTex, + gPauseToDecideGERTex, + gPauseToDecideFRATex, + }; + static void* D_8082AD6C[3] = { + gPauseToPlayMelodyENGTex, + gPauseToPlayMelodyGERTex, + gPauseToPlayMelodyFRATex, + }; + static void* D_8082AD78[][3] = { + { gPauseToEquipmentENGTex, gPauseToEquipmentGERTex, gPauseToEquipmentFRATex }, + { gPauseToSelectItemENGTex, gPauseToSelectItemGERTex, gPauseToSelectItemFRATex }, + { gPauseToMapENGTex, gPauseToMapGERTex, gPauseToMapFRATex }, + { gPauseToQuestStatusENGTex, gPauseToQuestStatusGERTex, gPauseToQuestStatusFRATex }, + }; + static void* D_8082ADA8[][3] = { + { gPauseToMapENGTex, gPauseToMapGERTex, gPauseToMapFRATex }, + { gPauseToQuestStatusENGTex, gPauseToQuestStatusGERTex, gPauseToQuestStatusFRATex }, + { gPauseToEquipmentENGTex, gPauseToEquipmentGERTex, gPauseToEquipmentFRATex }, + { gPauseToSelectItemENGTex, gPauseToSelectItemGERTex, gPauseToSelectItemFRATex }, + }; + static u16 D_8082ADD8[3] = { 56, 88, 80 }; + static u16 D_8082ADE0[3] = { 64, 88, 72 }; + static u16 D_8082ADE8[3] = { 80, 104, 112 }; + static s16 D_8082ADF0[][4] = { + { 180, 210, 255, 220 }, + { 100, 100, 150, 220 }, + }; + static s16 D_8082AE00 = 20; + static s16 D_8082AE04 = 0; + static s16 D_8082AE08[] = { + 10, 16, 16, 17, 12, 13, 18, 17, 17, 19, 13, 21, 20, 21, 14, 15, 15, 15, 11, 14, + }; + static s16 D_8082AE30[] = { + 21, 20, 19, 18, 11, 14, 10, 15, 16, 13, 12, 17, + }; + static s16 D_808321A0; + static s16 D_808321A2; + static s16 D_808321A4; + static s16 D_808321A6; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 stepR; + s16 stepG; + s16 stepB; + s16 stepA; + s16 temp; + s16 i; + s16 j; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 1676); + + stepR = ABS(D_808321A0 - D_8082ADF0[D_8082AE04][0]) / D_8082AE00; + stepG = ABS(D_808321A2 - D_8082ADF0[D_8082AE04][1]) / D_8082AE00; + stepB = ABS(D_808321A4 - D_8082ADF0[D_8082AE04][2]) / D_8082AE00; + stepA = ABS(D_808321A6 - D_8082ADF0[D_8082AE04][3]) / D_8082AE00; + if (D_808321A0 >= D_8082ADF0[D_8082AE04][0]) { + D_808321A0 -= stepR; + } else { + D_808321A0 += stepR; + } + if (D_808321A2 >= D_8082ADF0[D_8082AE04][1]) { + D_808321A2 -= stepG; + } else { + D_808321A2 += stepG; + } + if (D_808321A4 >= D_8082ADF0[D_8082AE04][2]) { + D_808321A4 -= stepB; + } else { + D_808321A4 += stepB; + } + if (D_808321A6 >= D_8082ADF0[D_8082AE04][3]) { + D_808321A6 -= stepA; + } else { + D_808321A6 += stepA; + } + + D_8082AE00--; + if (D_8082AE00 == 0) { + D_808321A0 = D_8082ADF0[D_8082AE04][0]; + D_808321A2 = D_8082ADF0[D_8082AE04][1]; + D_808321A4 = D_8082ADF0[D_8082AE04][2]; + D_808321A6 = D_8082ADF0[D_8082AE04][3]; + D_8082AE00 = ZREG(28); + D_8082AE04 ^= 1; + } + + temp = pauseCtx->infoPanelOffsetY - 76; + for (j = 0, i = 0; i < 7; i++, j += 4) { + pauseCtx->infoPanelVtx[j + 0].v.ob[0] = pauseCtx->infoPanelVtx[j + 2].v.ob[0] = -72; + + pauseCtx->infoPanelVtx[j + 1].v.ob[0] = pauseCtx->infoPanelVtx[j + 3].v.ob[0] = 0; + + pauseCtx->infoPanelVtx[j + 0].v.ob[1] = pauseCtx->infoPanelVtx[j + 1].v.ob[1] = temp; + + pauseCtx->infoPanelVtx[j + 2].v.ob[1] = pauseCtx->infoPanelVtx[j + 3].v.ob[1] = temp - 24; + + pauseCtx->infoPanelVtx[j + 0].v.ob[2] = pauseCtx->infoPanelVtx[j + 1].v.ob[2] = + pauseCtx->infoPanelVtx[j + 2].v.ob[2] = pauseCtx->infoPanelVtx[j + 3].v.ob[2] = 0; + + pauseCtx->infoPanelVtx[j + 0].v.flag = pauseCtx->infoPanelVtx[j + 1].v.flag = + pauseCtx->infoPanelVtx[j + 2].v.flag = pauseCtx->infoPanelVtx[j + 3].v.flag = 0; + + pauseCtx->infoPanelVtx[j + 0].v.tc[0] = pauseCtx->infoPanelVtx[j + 0].v.tc[1] = + pauseCtx->infoPanelVtx[j + 1].v.tc[1] = pauseCtx->infoPanelVtx[j + 2].v.tc[0] = 0; + + pauseCtx->infoPanelVtx[j + 1].v.tc[0] = pauseCtx->infoPanelVtx[j + 3].v.tc[0] = 0x900; + + pauseCtx->infoPanelVtx[j + 2].v.tc[1] = pauseCtx->infoPanelVtx[j + 3].v.tc[1] = 0x300; + + pauseCtx->infoPanelVtx[j + 0].v.cn[0] = pauseCtx->infoPanelVtx[j + 2].v.cn[0] = + pauseCtx->infoPanelVtx[j + 0].v.cn[1] = pauseCtx->infoPanelVtx[j + 2].v.cn[1] = + pauseCtx->infoPanelVtx[j + 0].v.cn[2] = pauseCtx->infoPanelVtx[j + 2].v.cn[2] = + pauseCtx->infoPanelVtx[j + 1].v.cn[0] = pauseCtx->infoPanelVtx[j + 3].v.cn[0] = + pauseCtx->infoPanelVtx[j + 1].v.cn[1] = pauseCtx->infoPanelVtx[j + 3].v.cn[1] = + pauseCtx->infoPanelVtx[j + 1].v.cn[2] = pauseCtx->infoPanelVtx[j + 3].v.cn[2] = 200; + + pauseCtx->infoPanelVtx[j + 0].v.cn[3] = pauseCtx->infoPanelVtx[j + 2].v.cn[3] = + pauseCtx->infoPanelVtx[j + 1].v.cn[3] = pauseCtx->infoPanelVtx[j + 3].v.cn[3] = pauseCtx->alpha; + } + + pauseCtx->infoPanelVtx[4].v.ob[0] = pauseCtx->infoPanelVtx[6].v.ob[0] = pauseCtx->infoPanelVtx[0].v.ob[0] + 72; + + pauseCtx->infoPanelVtx[5].v.ob[0] = pauseCtx->infoPanelVtx[7].v.ob[0] = pauseCtx->infoPanelVtx[4].v.ob[0] + 72; + + if ((pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) && (pauseCtx->unk_1E4 == 0)) { + pauseCtx->infoPanelVtx[8].v.ob[0] = pauseCtx->infoPanelVtx[10].v.ob[0] = WREG(16); + + pauseCtx->infoPanelVtx[9].v.ob[0] = pauseCtx->infoPanelVtx[11].v.ob[0] = pauseCtx->infoPanelVtx[8].v.ob[0] + 24; + + pauseCtx->infoPanelVtx[8].v.ob[1] = pauseCtx->infoPanelVtx[9].v.ob[1] = WREG(18); + + pauseCtx->infoPanelVtx[10].v.ob[1] = pauseCtx->infoPanelVtx[11].v.ob[1] = + pauseCtx->infoPanelVtx[8].v.ob[1] - 32; + } else { + pauseCtx->infoPanelVtx[8].v.ob[0] = pauseCtx->infoPanelVtx[10].v.ob[0] = WREG(16) + 3; + + pauseCtx->infoPanelVtx[9].v.ob[0] = pauseCtx->infoPanelVtx[11].v.ob[0] = pauseCtx->infoPanelVtx[8].v.ob[0] + 18; + + pauseCtx->infoPanelVtx[8].v.ob[1] = pauseCtx->infoPanelVtx[9].v.ob[1] = WREG(18) - 3; + + pauseCtx->infoPanelVtx[10].v.ob[1] = pauseCtx->infoPanelVtx[11].v.ob[1] = + pauseCtx->infoPanelVtx[8].v.ob[1] - 26; + } + + if ((pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT) && (pauseCtx->unk_1E4 == 0)) { + pauseCtx->infoPanelVtx[12].v.ob[0] = pauseCtx->infoPanelVtx[14].v.ob[0] = WREG(17); + + pauseCtx->infoPanelVtx[13].v.ob[0] = pauseCtx->infoPanelVtx[15].v.ob[0] = + pauseCtx->infoPanelVtx[12].v.ob[0] + 24; + + pauseCtx->infoPanelVtx[12].v.ob[1] = pauseCtx->infoPanelVtx[13].v.ob[1] = WREG(18); + + pauseCtx->infoPanelVtx[14].v.ob[1] = pauseCtx->infoPanelVtx[15].v.ob[1] = + pauseCtx->infoPanelVtx[12].v.ob[1] - 32; + } else { + pauseCtx->infoPanelVtx[12].v.ob[0] = pauseCtx->infoPanelVtx[14].v.ob[0] = WREG(17) + 3; + + pauseCtx->infoPanelVtx[13].v.ob[0] = pauseCtx->infoPanelVtx[15].v.ob[0] = + pauseCtx->infoPanelVtx[12].v.ob[0] + 18; + + pauseCtx->infoPanelVtx[12].v.ob[1] = pauseCtx->infoPanelVtx[13].v.ob[1] = WREG(18) - 3; + + pauseCtx->infoPanelVtx[14].v.ob[1] = pauseCtx->infoPanelVtx[15].v.ob[1] = + pauseCtx->infoPanelVtx[12].v.ob[1] - 26; + } + + pauseCtx->infoPanelVtx[9].v.tc[0] = pauseCtx->infoPanelVtx[11].v.tc[0] = pauseCtx->infoPanelVtx[13].v.tc[0] = + pauseCtx->infoPanelVtx[15].v.tc[0] = 0x300; + + pauseCtx->infoPanelVtx[10].v.tc[1] = pauseCtx->infoPanelVtx[11].v.tc[1] = pauseCtx->infoPanelVtx[14].v.tc[1] = + pauseCtx->infoPanelVtx[15].v.tc[1] = 0x400; + + gDPSetCombineMode(POLY_KAL_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + + Matrix_Translate(0.0f, 0.0f, -144.0f, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 1755), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 90, 100, 130, 255); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->infoPanelVtx[0], 16, 0); + + gSPDisplayList(POLY_KAL_DISP++, gItemNamePanelDL); + + if ((pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) && (pauseCtx->unk_1E4 == 0)) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, D_808321A0, D_808321A2, D_808321A4, D_808321A6); + } + + gSPDisplayList(POLY_KAL_DISP++, gLButtonIconDL); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 180, 210, 255, 220); + + if ((pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_RIGHT) && (pauseCtx->unk_1E4 == 0)) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, D_808321A0, D_808321A2, D_808321A4, D_808321A6); + } + + gSPDisplayList(POLY_KAL_DISP++, gRButtonIconDL); + + if (pauseCtx->cursorSpecialPos != 0) { + j = (pauseCtx->cursorSpecialPos * 4) - 32; + pauseCtx->cursorVtx[0].v.ob[0] = pauseCtx->infoPanelVtx[j].v.ob[0]; + pauseCtx->cursorVtx[0].v.ob[1] = pauseCtx->infoPanelVtx[j].v.ob[1]; + KaleidoScope_UpdateCursorSize(pauseCtx); + KaleidoScope_DrawCursor(globalCtx, pauseCtx->pageIndex); + } + + temp = pauseCtx->infoPanelOffsetY - 80; + pauseCtx->infoPanelVtx[16].v.ob[1] = pauseCtx->infoPanelVtx[17].v.ob[1] = temp; + + pauseCtx->infoPanelVtx[18].v.ob[1] = pauseCtx->infoPanelVtx[19].v.ob[1] = pauseCtx->infoPanelVtx[16].v.ob[1] - 16; + + pauseCtx->infoPanelVtx[18].v.tc[1] = pauseCtx->infoPanelVtx[19].v.tc[1] = 0x200; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(POLY_KAL_DISP++, 20, 30, 40, 0); + + if ((pauseCtx->state == 6) && (pauseCtx->namedItem != PAUSE_ITEM_NONE) && (pauseCtx->nameDisplayTimer < WREG(89)) && + (!pauseCtx->unk_1E4 || (pauseCtx->unk_1E4 == 2) || ((pauseCtx->unk_1E4 >= 4) && (pauseCtx->unk_1E4 <= 7)) || + (pauseCtx->unk_1E4 == 8)) && + (pauseCtx->cursorSpecialPos == 0)) { + if (!pauseCtx->unk_1E4 || (pauseCtx->unk_1E4 == 2) || ((pauseCtx->unk_1E4 >= 4) && (pauseCtx->unk_1E4 <= 7)) || + (pauseCtx->unk_1E4 == 8)) { + pauseCtx->infoPanelVtx[16].v.ob[0] = pauseCtx->infoPanelVtx[18].v.ob[0] = -63; + + pauseCtx->infoPanelVtx[17].v.ob[0] = pauseCtx->infoPanelVtx[19].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + 128; + + pauseCtx->infoPanelVtx[17].v.tc[0] = pauseCtx->infoPanelVtx[19].v.tc[0] = 0x1000; + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->infoPanelVtx[16], 4, 0); + + if (pauseCtx->nameColorSet == 1) { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 70, 70, 70, 255); + } else { + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + } + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA4(POLY_KAL_DISP, pauseCtx->nameSegment, 128, 16, 0); + } + + if (pauseCtx->pageIndex == PAUSE_MAP && CVar_GetS32("gDebugEnabled", 0) != 0) { + if (YREG(7) != 0) { + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("キンスタ数(%d) Get_KIN_STA=%x (%x) (%x)\n", YREG(6), GET_GS_FLAGS(YREG(6)), + gAreaGsFlags[YREG(6)], gSaveContext.gsFlags[YREG(6) >> 2]); + osSyncPrintf(VT_RST); + + YREG(7) = 0; + SET_GS_FLAGS(D_8082AE30[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]], gAreaGsFlags[D_8082AE30[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]]); + } + } + + if ((pauseCtx->pageIndex == PAUSE_MAP) && !sInDungeonScene) { + if (GET_GS_FLAGS(D_8082AE30[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]) == + gAreaGsFlags[D_8082AE30[pauseCtx->cursorPoint[PAUSE_WORLD_MAP]]]) { + + pauseCtx->infoPanelVtx[24].v.ob[0] = pauseCtx->infoPanelVtx[26].v.ob[0] = -74; + + pauseCtx->infoPanelVtx[25].v.ob[0] = pauseCtx->infoPanelVtx[27].v.ob[0] = + pauseCtx->infoPanelVtx[24].v.ob[0] + 19; + + pauseCtx->infoPanelVtx[24].v.ob[1] = pauseCtx->infoPanelVtx[25].v.ob[1] = + pauseCtx->infoPanelVtx[24].v.ob[1] - 2; + + pauseCtx->infoPanelVtx[26].v.ob[1] = pauseCtx->infoPanelVtx[27].v.ob[1] = + pauseCtx->infoPanelVtx[24].v.ob[1] - 19; + + pauseCtx->infoPanelVtx[25].v.tc[0] = pauseCtx->infoPanelVtx[27].v.tc[0] = 0x300; + + gDPPipeSync(POLY_KAL_DISP++); + gSPVertex(POLY_KAL_DISP++, &pauseCtx->infoPanelVtx[24], 4, 0); + + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 0); + + KaleidoScope_DrawQuadTextureRGBA32(globalCtx->state.gfxCtx, gGoldSkulltulaIconTex, 24, 24, 0); + } + } + } else if ((pauseCtx->unk_1E4 < 3) || (pauseCtx->unk_1E4 == 7) || (pauseCtx->unk_1E4 == 8)) { + pauseCtx->infoPanelVtx[20].v.ob[1] = pauseCtx->infoPanelVtx[21].v.ob[1] = temp; + + pauseCtx->infoPanelVtx[22].v.ob[1] = pauseCtx->infoPanelVtx[23].v.ob[1] = + pauseCtx->infoPanelVtx[20].v.ob[1] - 16; + + pauseCtx->infoPanelVtx[22].v.tc[1] = pauseCtx->infoPanelVtx[23].v.tc[1] = 0x200; + + gSPVertex(POLY_KAL_DISP++, &pauseCtx->infoPanelVtx[16], 8, 0); + + if (pauseCtx->state == 7) { + pauseCtx->infoPanelVtx[16].v.ob[0] = pauseCtx->infoPanelVtx[18].v.ob[0] = WREG(61 + gSaveContext.language); + + pauseCtx->infoPanelVtx[17].v.ob[0] = pauseCtx->infoPanelVtx[19].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + 24; + + pauseCtx->infoPanelVtx[20].v.ob[0] = pauseCtx->infoPanelVtx[22].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + WREG(52 + gSaveContext.language); + + pauseCtx->infoPanelVtx[21].v.ob[0] = pauseCtx->infoPanelVtx[23].v.ob[0] = + pauseCtx->infoPanelVtx[20].v.ob[0] + D_8082ADE0[gSaveContext.language]; + + pauseCtx->infoPanelVtx[17].v.tc[0] = pauseCtx->infoPanelVtx[19].v.tc[0] = 0x300; + + pauseCtx->infoPanelVtx[21].v.tc[0] = pauseCtx->infoPanelVtx[23].v.tc[0] = D_8082ADE0[gSaveContext.language] + << 5; + + gSPDisplayList(POLY_KAL_DISP++, gAButtonIconDL); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, D_8082AD60[gSaveContext.language], + D_8082ADE0[gSaveContext.language], 16, 4); + } else if (pauseCtx->cursorSpecialPos != 0) { + if ((pauseCtx->state == 6) && (pauseCtx->unk_1E4 == 0)) { + pauseCtx->infoPanelVtx[16].v.ob[0] = pauseCtx->infoPanelVtx[18].v.ob[0] = -63; + + pauseCtx->infoPanelVtx[17].v.ob[0] = pauseCtx->infoPanelVtx[19].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + 128; + + pauseCtx->infoPanelVtx[17].v.tc[0] = pauseCtx->infoPanelVtx[19].v.tc[0] = 0x1000; + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 200, 0, 255); + + if (pauseCtx->cursorSpecialPos == PAUSE_CURSOR_PAGE_LEFT) { + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8( + POLY_KAL_DISP, D_8082AD78[pauseCtx->pageIndex][gSaveContext.language], 128, 16, 0); + } else { + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8( + POLY_KAL_DISP, D_8082ADA8[pauseCtx->pageIndex][gSaveContext.language], 128, 16, 0); + } + } + } else { + if (!pauseCtx->pageIndex) { // pageIndex == PAUSE_ITEM + pauseCtx->infoPanelVtx[16].v.ob[0] = pauseCtx->infoPanelVtx[18].v.ob[0] = + WREG(49 + gSaveContext.language); + + pauseCtx->infoPanelVtx[17].v.ob[0] = pauseCtx->infoPanelVtx[19].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + 48; + + pauseCtx->infoPanelVtx[20].v.ob[0] = pauseCtx->infoPanelVtx[22].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + WREG(58 + gSaveContext.language); + + pauseCtx->infoPanelVtx[21].v.ob[0] = pauseCtx->infoPanelVtx[23].v.ob[0] = + pauseCtx->infoPanelVtx[20].v.ob[0] + D_8082ADD8[gSaveContext.language]; + + pauseCtx->infoPanelVtx[17].v.tc[0] = pauseCtx->infoPanelVtx[19].v.tc[0] = 0x600; + + pauseCtx->infoPanelVtx[21].v.tc[0] = pauseCtx->infoPanelVtx[23].v.tc[0] = + D_8082ADD8[gSaveContext.language] << 5; + + gSPDisplayList(POLY_KAL_DISP++, gCButtonIconsDL); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, D_8082AD54[gSaveContext.language], + D_8082ADD8[gSaveContext.language], 16, 4); + } else if ((pauseCtx->pageIndex == PAUSE_MAP) && sInDungeonScene) { + + } else if ((pauseCtx->pageIndex == PAUSE_QUEST) && (pauseCtx->cursorSlot[PAUSE_QUEST] >= 6) && + (pauseCtx->cursorSlot[PAUSE_QUEST] <= 0x11)) { + if (pauseCtx->namedItem != PAUSE_ITEM_NONE) { + pauseCtx->infoPanelVtx[16].v.ob[0] = pauseCtx->infoPanelVtx[18].v.ob[0] = + WREG(55 + gSaveContext.language); + + pauseCtx->infoPanelVtx[17].v.ob[0] = pauseCtx->infoPanelVtx[19].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + 24; + + pauseCtx->infoPanelVtx[20].v.ob[0] = pauseCtx->infoPanelVtx[22].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + WREG(52 + gSaveContext.language); + + if (gSaveContext.language == LANGUAGE_GER) { + pauseCtx->infoPanelVtx[20].v.ob[0] = pauseCtx->infoPanelVtx[22].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] - 99; + } + + pauseCtx->infoPanelVtx[21].v.ob[0] = pauseCtx->infoPanelVtx[23].v.ob[0] = + pauseCtx->infoPanelVtx[20].v.ob[0] + D_8082ADE8[gSaveContext.language]; + + pauseCtx->infoPanelVtx[17].v.tc[0] = pauseCtx->infoPanelVtx[19].v.tc[0] = 0x300; + + pauseCtx->infoPanelVtx[21].v.tc[0] = pauseCtx->infoPanelVtx[23].v.tc[0] = + D_8082ADE8[gSaveContext.language] << 5; + + gSPDisplayList(POLY_KAL_DISP++, gAButtonIconDL); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, D_8082AD6C[gSaveContext.language], + D_8082ADE8[gSaveContext.language], 16, 4); + } + } else if (pauseCtx->pageIndex == PAUSE_EQUIP) { + pauseCtx->infoPanelVtx[16].v.ob[0] = pauseCtx->infoPanelVtx[18].v.ob[0] = + WREG(64 + gSaveContext.language); + + pauseCtx->infoPanelVtx[17].v.ob[0] = pauseCtx->infoPanelVtx[19].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + 24; + + pauseCtx->infoPanelVtx[20].v.ob[0] = pauseCtx->infoPanelVtx[22].v.ob[0] = + pauseCtx->infoPanelVtx[16].v.ob[0] + WREG(52 + gSaveContext.language); + + pauseCtx->infoPanelVtx[21].v.ob[0] = pauseCtx->infoPanelVtx[23].v.ob[0] = + pauseCtx->infoPanelVtx[20].v.ob[0] + D_8082ADD8[gSaveContext.language]; + + pauseCtx->infoPanelVtx[17].v.tc[0] = pauseCtx->infoPanelVtx[19].v.tc[0] = 0x300; + + pauseCtx->infoPanelVtx[21].v.tc[0] = pauseCtx->infoPanelVtx[23].v.tc[0] = + D_8082ADD8[gSaveContext.language] << 5; + + gSPDisplayList(POLY_KAL_DISP++, gAButtonIconDL); + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + + POLY_KAL_DISP = KaleidoScope_QuadTextureIA8(POLY_KAL_DISP, D_8082AD54[gSaveContext.language], + D_8082ADD8[gSaveContext.language], 16, 4); + } + } + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 2032); +} + +void KaleidoScope_UpdateNamePanel(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + u16 sp2A; + + if ((pauseCtx->namedItem != pauseCtx->cursorItem[pauseCtx->pageIndex]) || + ((pauseCtx->pageIndex == PAUSE_MAP) && (pauseCtx->cursorSpecialPos != 0))) { + + pauseCtx->namedItem = pauseCtx->cursorItem[pauseCtx->pageIndex]; + sp2A = pauseCtx->namedItem; + + osCreateMesgQueue(&pauseCtx->loadQueue, &pauseCtx->loadMsg, 1); + + if (pauseCtx->namedItem != PAUSE_ITEM_NONE) { + if ((pauseCtx->pageIndex == PAUSE_MAP) && !sInDungeonScene) { + if (gSaveContext.language) { + sp2A += 12; + } + if (gSaveContext.language == LANGUAGE_FRA) { + sp2A += 12; + } + + memcpy(pauseCtx->nameSegment, ResourceMgr_LoadTexByName(mapNameTextures[sp2A]), 0x400); + } else { + osSyncPrintf("zoom_name=%d\n", pauseCtx->namedItem); + + if (gSaveContext.language) { + sp2A += 123; + } + if (gSaveContext.language == LANGUAGE_FRA) { + sp2A += 123; + } + + osSyncPrintf("J_N=%d point=%d\n", gSaveContext.language, sp2A); + + memcpy(pauseCtx->nameSegment, ResourceMgr_LoadTexByName(iconNameTextures[sp2A]), 0x400); + } + + pauseCtx->nameDisplayTimer = 0; + } + } else if (pauseCtx->nameColorSet == 0) { + if (((pauseCtx->pageIndex == PAUSE_QUEST) && (pauseCtx->cursorSlot[PAUSE_QUEST] >= 6) && + (pauseCtx->cursorSlot[PAUSE_QUEST] <= 0x11) && (pauseCtx->unk_1E4 == 8)) || + (pauseCtx->pageIndex == PAUSE_ITEM) || + ((pauseCtx->pageIndex == PAUSE_EQUIP) && (pauseCtx->cursorX[PAUSE_EQUIP] != 0))) { + if (pauseCtx->namedItem != ITEM_SOLD_OUT) { + pauseCtx->nameDisplayTimer++; + if (pauseCtx->nameDisplayTimer > WREG(88)) { + pauseCtx->nameDisplayTimer = 0; + } + } + } else { + pauseCtx->nameDisplayTimer = 0; + } + } else { + pauseCtx->nameDisplayTimer = 0; + } +} + +void func_808237B4(GlobalContext* globalCtx, Input* input) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s32 cond = false; + s32 mode; + + if (ZREG(13) && !CHECK_BTN_ALL(input->press.button, BTN_L)) { + cond = true; + } + + if (!cond) { + mode = pauseCtx->mode; + pauseCtx->eye.x += D_8082ABAC[mode]; + pauseCtx->eye.z += D_8082ABCC[mode]; + + if (pauseCtx->unk_1EA < 32) { + WREG(16) -= WREG(25) / WREG(6); + WREG(17) -= WREG(26) / WREG(6); + } else { + WREG(16) += WREG(25) / WREG(6); + WREG(17) += WREG(26) / WREG(6); + } + + pauseCtx->unk_1EA += 4; + + if (pauseCtx->unk_1EA == 64) { + pauseCtx->unk_1EA = 0; + pauseCtx->pageIndex = D_8082ABEC[pauseCtx->mode]; + pauseCtx->unk_1E4 = 0; + } + } +} + +void KaleidoScope_SetView(PauseContext* pauseCtx, f32 x, f32 y, f32 z) { + Vec3f eye; + Vec3f lookAt; + Vec3f up; + + eye.x = x; + eye.y = y; + eye.z = z; + lookAt.x = lookAt.y = lookAt.z = 0.0f; + up.x = up.z = 0.0f; + up.y = 1.0f; + + func_800AA358(&pauseCtx->view, &eye, &lookAt, &up); + func_800AAA50(&pauseCtx->view, 127); +} + +static u8 D_8082AE48[][4] = { + { 10, 70, 70, 10 }, { 10, 90, 90, 10 }, { 80, 140, 140, 80 }, + { 80, 120, 120, 80 }, { 80, 140, 140, 80 }, { 50, 110, 110, 50 }, +}; +static u8 D_8082AE60[][4] = { + { 50, 100, 100, 50 }, { 50, 100, 100, 50 }, { 40, 60, 60, 40 }, + { 80, 120, 120, 80 }, { 40, 60, 60, 40 }, { 50, 110, 110, 50 }, +}; +static u8 D_8082AE78[][4] = { + { 80, 130, 130, 80 }, { 40, 60, 60, 40 }, { 30, 60, 60, 30 }, + { 50, 70, 70, 50 }, { 30, 60, 60, 30 }, { 50, 110, 110, 50 }, +}; + +static s16 D_8082AE90[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AE94[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AE98[] = { + 0xFFDC, 0x000C, 0xFFEE, 0x0046, 0x0046, 0x0046, 0xFFA8, 0xFFA8, 0xFFA8, + 0xFFA8, 0xFFA8, 0xFFA8, 0xFFA8, 0xFFA8, 0xFF96, 0xFFC2, 0xFFD8, 0x0000, +}; +static s16 D_8082AEBC[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AEC0[] = { + 0x002F, 0xFFCF, 0xFFEF, 0xFFF1, 0xFFF7, 0x0018, 0x002B, 0x000E, 0x0009, 0x0026, 0x0052, + 0x0047, 0xFFB4, 0xFFA9, 0xFF94, 0xFFCA, 0xFFA3, 0xFFBD, 0xFFC8, 0xFFDF, 0xFFF6, 0x0001, + 0x000E, 0x0018, 0x0023, 0x003A, 0x004A, 0x0059, 0x0000, 0xFFC6, 0x0013, 0x001C, +}; +static s16 D_8082AF00[] = { + 0xFFB4, 0xFFC6, 0x000A, 0xFFC6, 0x000A, 0x0000, +}; +static s16 D_8082AF0C[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AF10[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AF14[] = { + 0x0030, 0x0030, 0x0060, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, + 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0010, 0x0010, 0x0018, 0x0000, +}; +static s16 D_8082AF38[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AF3C[] = { + 0x0098, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, +}; +static s16 D_8082AF48[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AF4C[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AF50[] = { + 0x001C, 0x001C, 0x002E, 0x001C, 0xFFFE, 0xFFE0, 0x0032, 0x0024, 0x0016, + 0x0008, 0xFFFA, 0xFFEC, 0xFFDE, 0xFFD0, 0x0012, 0x0012, 0x0032, 0x0000, +}; +static s16 D_8082AF74[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AF78[] = { + 0x000F, 0x0028, 0x000B, 0x002D, 0x0034, 0x0025, 0x0024, 0x0039, 0x0036, 0x0021, 0x001F, + 0x002D, 0x0020, 0x002A, 0x0031, 0xFFF6, 0x001F, 0x001B, 0x000F, 0xFFCF, 0x0008, 0x0026, + 0x0007, 0x002F, 0x001E, 0x0001, 0xFFF7, 0x0019, 0x0000, 0x0001, 0xFFE0, 0xFFE6, +}; +static s16 D_8082AFB8[] = { + 0x0024, 0x000A, 0x000A, 0xFFFA, 0xFFFA, 0x0000, +}; +static s16 D_8082AFC4[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AFC8[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AFCC[] = { + 0x0055, 0x0055, 0x0010, 0x0018, 0x0018, 0x0018, 0x0010, 0x0010, 0x0010, + 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0018, 0x0000, +}; +static s16 D_8082AFF0[] = { + 0x0000, + 0x0000, +}; +static s16 D_8082AFF4[] = { + 0x0010, 0x0030, 0x0030, 0x0010, 0x0010, 0x0000, +}; + +static s16* D_8082B000[] = { + D_8082AE90, D_8082AE94, D_8082AE98, D_8082AEBC, D_8082AEC0, D_8082AF00, +}; + +static s16* D_8082B018[] = { + D_8082AF0C, D_8082AF10, D_8082AF14, D_8082AF38, D_8082AAEC, D_8082AF3C, +}; + +static s16* D_8082B030[] = { + D_8082AF48, D_8082AF4C, D_8082AF50, D_8082AF74, D_8082AF78, D_8082AFB8, +}; + +static s16* D_8082B048[] = { + D_8082AFC4, D_8082AFC8, D_8082AFCC, D_8082AFF0, D_8082AB2C, D_8082AFF4, +}; + +static s16 D_8082B060[] = { + 0xFFC6, 0x000B, 0x001E, 0x001E, 0x000F, 0x0026, 0xFFC2, 0x003C, 0x003D, 0xFFB2, 0xFED4, + 0xFFAA, 0xFFBF, 0xFED4, 0xFED4, 0xFFEB, 0x000E, 0x000D, 0x0014, 0xFFDE, 0xFED4, 0x0000, +}; + +static s16 D_8082B08C[] = { + 0x0059, 0x0014, 0x000E, 0x0023, 0x0020, 0x0011, 0x0032, 0x0010, 0x0015, 0x0014, 0xFFFF, + 0x0020, 0x0010, 0xFFFF, 0xFFFF, 0x0013, 0x0013, 0x0015, 0x0010, 0x0014, 0xFFFF, 0x0000, +}; + +static s16 D_8082B0B8[] = { + 0x0001, 0x000F, 0x0014, 0x0009, 0xFFE2, 0xFFEF, 0xFFDE, 0x000F, 0x001E, 0x0001, 0xFED4, + 0x002A, 0x0007, 0xFED4, 0xFED4, 0x0018, 0x0024, 0x0035, 0x0025, 0xFFF3, 0xFED4, 0x0000, +}; + +static s16 D_8082B0E4[] = { + 0x0024, 0x000F, 0x0010, 0x0017, 0x0017, 0x0010, 0x0018, 0x000D, 0x0011, 0x0012, 0x0001, + 0x0019, 0x000D, 0x0001, 0x0001, 0x000D, 0x0015, 0x000F, 0x000D, 0x000C, 0x0001, 0x0000, +}; + +s16 func_80823A0C(GlobalContext* globalCtx, Vtx* vtx, s16 arg2, s16 arg3) { + static s16 D_8082B110 = 0; + static s16 D_8082B114 = 1; + static s16 D_8082B118 = 0; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16* ptr1; + s16* ptr2; + s16* ptr3; + s16* ptr4; + s16 phi_s2; + s16 phi_t0; + s16 phi_a1; + s16 phi_a2; + s16 phi_t3; + s16 phi_t1; + + phi_t0 = -200; + + for (phi_t1 = 0, phi_t3 = 0; phi_t3 < 3; phi_t3++) { + phi_t0 += 80; + + for (phi_a1 = 80, phi_a2 = 0; phi_a2 < 5; phi_a2++, phi_t1 += 4, phi_a1 -= 32) { + vtx[phi_t1 + 0].v.ob[0] = vtx[phi_t1 + 2].v.ob[0] = phi_t0; + + vtx[phi_t1 + 1].v.ob[0] = vtx[phi_t1 + 3].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] + 80; + + vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = phi_a1 + pauseCtx->offsetY; + + vtx[phi_t1 + 2].v.ob[1] = vtx[phi_t1 + 3].v.ob[1] = vtx[phi_t1 + 0].v.ob[1] - 32; + + vtx[phi_t1 + 0].v.ob[2] = vtx[phi_t1 + 1].v.ob[2] = vtx[phi_t1 + 2].v.ob[2] = vtx[phi_t1 + 3].v.ob[2] = 0; + + vtx[phi_t1 + 0].v.flag = 0; + vtx[phi_t1 + 1].v.flag = 0; + vtx[phi_t1 + 2].v.flag = 0; + vtx[phi_t1 + 3].v.flag = 0; + + vtx[phi_t1 + 0].v.tc[0] = vtx[phi_t1 + 0].v.tc[1] = vtx[phi_t1 + 1].v.tc[1] = vtx[phi_t1 + 2].v.tc[0] = 0; + + vtx[phi_t1 + 1].v.tc[0] = vtx[phi_t1 + 3].v.tc[0] = 0xA00; + + vtx[phi_t1 + 2].v.tc[1] = vtx[phi_t1 + 3].v.tc[1] = 0x400; + + vtx[phi_t1 + 0].v.cn[0] = vtx[phi_t1 + 2].v.cn[0] = D_8082AE48[arg2][phi_t3 + 0]; + + vtx[phi_t1 + 0].v.cn[1] = vtx[phi_t1 + 2].v.cn[1] = D_8082AE60[arg2][phi_t3 + 0]; + + vtx[phi_t1 + 0].v.cn[2] = vtx[phi_t1 + 2].v.cn[2] = D_8082AE78[arg2][phi_t3 + 0]; + + vtx[phi_t1 + 1].v.cn[0] = vtx[phi_t1 + 3].v.cn[0] = D_8082AE48[arg2][phi_t3 + 1]; + + vtx[phi_t1 + 1].v.cn[1] = vtx[phi_t1 + 3].v.cn[1] = D_8082AE60[arg2][phi_t3 + 1]; + + vtx[phi_t1 + 1].v.cn[2] = vtx[phi_t1 + 3].v.cn[2] = D_8082AE78[arg2][phi_t3 + 1]; + + vtx[phi_t1 + 0].v.cn[3] = vtx[phi_t1 + 2].v.cn[3] = vtx[phi_t1 + 1].v.cn[3] = vtx[phi_t1 + 3].v.cn[3] = + pauseCtx->alpha; + } + } + + phi_s2 = phi_t1; + + if (arg3 != 0) { + ptr1 = D_8082B000[arg2]; + ptr2 = D_8082B018[arg2]; + ptr3 = D_8082B030[arg2]; + ptr4 = D_8082B048[arg2]; + + for (phi_t3 = 0; phi_t3 < arg3; phi_t3++, phi_t1 += 4) { + vtx[phi_t1 + 2].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] = ptr1[phi_t3]; + + vtx[phi_t1 + 1].v.ob[0] = vtx[phi_t1 + 3].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] + ptr2[phi_t3]; + + if (!((pauseCtx->state >= 8) && (pauseCtx->state <= 0x11))) { + vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = ptr3[phi_t3] + pauseCtx->offsetY; + } else { + vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = YREG(60 + phi_t3) + pauseCtx->offsetY; + } + + vtx[phi_t1 + 2].v.ob[1] = vtx[phi_t1 + 3].v.ob[1] = vtx[phi_t1 + 0].v.ob[1] - ptr4[phi_t3]; + + vtx[phi_t1 + 0].v.ob[2] = vtx[phi_t1 + 1].v.ob[2] = vtx[phi_t1 + 2].v.ob[2] = vtx[phi_t1 + 3].v.ob[2] = 0; + + vtx[phi_t1 + 0].v.flag = vtx[phi_t1 + 1].v.flag = vtx[phi_t1 + 2].v.flag = vtx[phi_t1 + 3].v.flag = 0; + + vtx[phi_t1 + 0].v.tc[0] = vtx[phi_t1 + 0].v.tc[1] = vtx[phi_t1 + 1].v.tc[1] = vtx[phi_t1 + 2].v.tc[0] = 0; + + vtx[phi_t1 + 1].v.tc[0] = vtx[phi_t1 + 3].v.tc[0] = ptr2[phi_t3] << 5; + + vtx[phi_t1 + 2].v.tc[1] = vtx[phi_t1 + 3].v.tc[1] = ptr4[phi_t3] << 5; + + vtx[phi_t1 + 0].v.cn[0] = vtx[phi_t1 + 2].v.cn[0] = vtx[phi_t1 + 0].v.cn[1] = vtx[phi_t1 + 2].v.cn[1] = + vtx[phi_t1 + 0].v.cn[2] = vtx[phi_t1 + 2].v.cn[2] = vtx[phi_t1 + 1].v.cn[0] = vtx[phi_t1 + 3].v.cn[0] = + vtx[phi_t1 + 1].v.cn[1] = vtx[phi_t1 + 3].v.cn[1] = vtx[phi_t1 + 1].v.cn[2] = + vtx[phi_t1 + 3].v.cn[2] = 255; + + vtx[phi_t1 + 0].v.cn[3] = vtx[phi_t1 + 2].v.cn[3] = vtx[phi_t1 + 1].v.cn[3] = vtx[phi_t1 + 3].v.cn[3] = + pauseCtx->alpha; + } + + if (arg2 == 4) { + phi_t1 -= 12; + + phi_t3 = gSaveContext.worldMapArea; + + vtx[phi_t1 + 0].v.ob[0] = vtx[phi_t1 + 2].v.ob[0] = D_8082B060[phi_t3]; + + if (phi_t3) {} + + vtx[phi_t1 + 1].v.ob[0] = vtx[phi_t1 + 3].v.ob[0] = vtx[phi_t1 + 0].v.ob[0] + D_8082B08C[phi_t3]; + + vtx[phi_t1 + 0].v.ob[1] = vtx[phi_t1 + 1].v.ob[1] = D_8082B0B8[phi_t3] + pauseCtx->offsetY; + + vtx[phi_t1 + 2].v.ob[1] = vtx[phi_t1 + 3].v.ob[1] = vtx[phi_t1 + 0].v.ob[1] - D_8082B0E4[phi_t3]; + + phi_t1 += 12; + + if (pauseCtx->tradeQuestLocation != 0xFF) { + if (D_8082B114 == 0) { + D_8082B118++; + switch (D_8082B118) { + case 1: + D_8082B110 = 3; + D_8082B114 = 8; + break; + case 2: + D_8082B110 = 0; + D_8082B114 = 6; + D_8082B118 = 0; + break; + } + } else { + D_8082B114--; + } + + phi_t3 = phi_s2 + (pauseCtx->tradeQuestLocation * 4) + 64; + phi_a2 = phi_s2 + 116; + + vtx[phi_a2 + 0].v.ob[0] = vtx[phi_a2 + 2].v.ob[0] = vtx[phi_t3 + 0].v.ob[0]; + + vtx[phi_a2 + 1].v.ob[0] = vtx[phi_a2 + 3].v.ob[0] = vtx[phi_a2 + 0].v.ob[0] + 8; + + vtx[phi_a2 + 0].v.ob[1] = vtx[phi_a2 + 1].v.ob[1] = vtx[phi_t3 + 0].v.ob[1] - D_8082B110 + 10; + + vtx[phi_a2 + 0].v.ob[2] = vtx[phi_a2 + 1].v.ob[2] = vtx[phi_a2 + 2].v.ob[2] = vtx[phi_a2 + 3].v.ob[2] = + 0; + + vtx[phi_a2 + 2].v.ob[1] = vtx[phi_a2 + 3].v.ob[1] = vtx[phi_a2 + 0].v.ob[1] - 8; + + vtx[phi_a2 + 0].v.flag = vtx[phi_a2 + 1].v.flag = vtx[phi_a2 + 2].v.flag = vtx[phi_a2 + 3].v.flag = 0; + + vtx[phi_t1].v.tc[0] = vtx[phi_t1].v.tc[1] = vtx[phi_a2 + 1].v.tc[1] = vtx[phi_a2 + 2].v.tc[0] = 0; + + vtx[phi_a2 + 1].v.tc[0] = vtx[phi_a2 + 3].v.tc[0] = 0x100; + + vtx[phi_a2 + 2].v.tc[1] = vtx[phi_a2 + 3].v.tc[1] = 0x100; + + vtx[phi_a2 + 0].v.cn[0] = vtx[phi_a2 + 2].v.cn[0] = vtx[phi_a2 + 0].v.cn[1] = vtx[phi_a2 + 2].v.cn[1] = + vtx[phi_a2 + 0].v.cn[2] = vtx[phi_a2 + 2].v.cn[2] = vtx[phi_a2 + 1].v.cn[0] = + vtx[phi_a2 + 3].v.cn[0] = vtx[phi_a2 + 1].v.cn[1] = vtx[phi_a2 + 3].v.cn[1] = + vtx[phi_a2 + 1].v.cn[2] = vtx[phi_a2 + 3].v.cn[2] = 255; + + vtx[phi_a2 + 0].v.cn[3] = vtx[phi_a2 + 2].v.cn[3] = vtx[phi_a2 + 1].v.cn[3] = vtx[phi_a2 + 3].v.cn[3] = + pauseCtx->alpha; + } + } + } + + return phi_t1; +} + +static s16 D_8082B11C[] = { 0, 4, 8, 12, 24, 32, 56 }; + +static s16 D_8082B12C[] = { -114, 12, 44, 76 }; + +static u8 D_8082B134[] = { 1, 5, 9, 13 }; + +static s16 D_8082B138[] = { + 74, 74, 46, 18, 18, 46, -108, -90, -72, -54, -36, -18, -108, -90, -72, -54, + -36, -18, 20, 46, 72, -110, -86, -110, -54, -98, -86, -74, -62, -50, -38, -26, + -14, -98, -86, -74, -62, -50, -38, -26, -14, -88, -81, -72, -90, -83, -74, +}; + +static s16 D_8082B198[] = { + 38, 6, -12, 6, 38, 56, -20, -20, -20, -20, -20, -20, 2, 2, 2, 2, 2, 2, -46, -46, -46, 58, 58, 34, + 58, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, -52, 34, 34, 34, 36, 36, 36, +}; + +static s16 D_8082B1F8[] = { + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 48, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, +}; + +void KaleidoScope_InitVertices(GlobalContext* globalCtx, GraphicsContext* gfxCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + s16 phi_t1; + s16 phi_t2; + s16 phi_t2_2; + s16 phi_t3; + s16 phi_t4; + s16 phi_t5; + + pauseCtx->offsetY = 0; + + if ((pauseCtx->state == 4) || (pauseCtx->state >= 0x12) || + ((pauseCtx->state == 7) && ((pauseCtx->unk_1EC == 2) || (pauseCtx->unk_1EC == 5))) || + ((pauseCtx->state >= 8) && (pauseCtx->state <= 0xD))) { + pauseCtx->offsetY = 80; + } + + pauseCtx->itemPageVtx = Graph_Alloc(gfxCtx, 60 * sizeof(Vtx)); + func_80823A0C(globalCtx, pauseCtx->itemPageVtx, 0, 0); + + pauseCtx->equipPageVtx = Graph_Alloc(gfxCtx, 60 * sizeof(Vtx)); + func_80823A0C(globalCtx, pauseCtx->equipPageVtx, 1, 0); + + if (!sInDungeonScene) { + pauseCtx->mapPageVtx = Graph_Alloc(gfxCtx, 248 * sizeof(Vtx)); + phi_t3 = func_80823A0C(globalCtx, pauseCtx->mapPageVtx, 4, 32); + + for (phi_t2 = 0, phi_t5 = 58; phi_t2 < 15; phi_t2++, phi_t3 += 4, phi_t5 -= 9) { + pauseCtx->mapPageVtx[phi_t3 + 2].v.ob[0] = -108; + pauseCtx->mapPageVtx[phi_t3 + 0].v.ob[0] = pauseCtx->mapPageVtx[phi_t3 + 2].v.ob[0]; + + pauseCtx->mapPageVtx[phi_t3 + 1].v.ob[0] = pauseCtx->mapPageVtx[phi_t3 + 3].v.ob[0] = + pauseCtx->mapPageVtx[phi_t3 + 0].v.ob[0] + 216; + + pauseCtx->mapPageVtx[phi_t3 + 0].v.ob[1] = pauseCtx->mapPageVtx[phi_t3 + 1].v.ob[1] = + phi_t5 + pauseCtx->offsetY; + + pauseCtx->mapPageVtx[phi_t3 + 2].v.ob[1] = pauseCtx->mapPageVtx[phi_t3 + 3].v.ob[1] = + pauseCtx->mapPageVtx[phi_t3 + 0].v.ob[1] - 9; + + pauseCtx->mapPageVtx[phi_t3 + 0].v.ob[2] = pauseCtx->mapPageVtx[phi_t3 + 1].v.ob[2] = + pauseCtx->mapPageVtx[phi_t3 + 2].v.ob[2] = pauseCtx->mapPageVtx[phi_t3 + 3].v.ob[2] = 0; + + pauseCtx->mapPageVtx[phi_t3 + 0].v.flag = pauseCtx->mapPageVtx[phi_t3 + 1].v.flag = + pauseCtx->mapPageVtx[phi_t3 + 2].v.flag = pauseCtx->mapPageVtx[phi_t3 + 3].v.flag = 0; + + pauseCtx->mapPageVtx[phi_t3 + 0].v.tc[0] = pauseCtx->mapPageVtx[phi_t3 + 0].v.tc[1] = + pauseCtx->mapPageVtx[phi_t3 + 1].v.tc[1] = pauseCtx->mapPageVtx[phi_t3 + 2].v.tc[0] = 0; + + pauseCtx->mapPageVtx[phi_t3 + 1].v.tc[0] = pauseCtx->mapPageVtx[phi_t3 + 3].v.tc[0] = 0x1B00; + + pauseCtx->mapPageVtx[phi_t3 + 2].v.tc[1] = pauseCtx->mapPageVtx[phi_t3 + 3].v.tc[1] = 0x120; + + pauseCtx->mapPageVtx[phi_t3 + 0].v.cn[0] = pauseCtx->mapPageVtx[phi_t3 + 2].v.cn[0] = + pauseCtx->mapPageVtx[phi_t3 + 0].v.cn[1] = pauseCtx->mapPageVtx[phi_t3 + 2].v.cn[1] = + pauseCtx->mapPageVtx[phi_t3 + 0].v.cn[2] = pauseCtx->mapPageVtx[phi_t3 + 2].v.cn[2] = + pauseCtx->mapPageVtx[phi_t3 + 1].v.cn[0] = pauseCtx->mapPageVtx[phi_t3 + 3].v.cn[0] = + pauseCtx->mapPageVtx[phi_t3 + 1].v.cn[1] = pauseCtx->mapPageVtx[phi_t3 + 3].v.cn[1] = + pauseCtx->mapPageVtx[phi_t3 + 1].v.cn[2] = pauseCtx->mapPageVtx[phi_t3 + 3].v.cn[2] = + pauseCtx->mapPageVtx[phi_t3 + 0].v.cn[3] = + pauseCtx->mapPageVtx[phi_t3 + 2].v.cn[3] = + pauseCtx->mapPageVtx[phi_t3 + 1].v.cn[3] = + pauseCtx->mapPageVtx[phi_t3 + 3].v.cn[3] = pauseCtx->alpha; + } + + pauseCtx->mapPageVtx[phi_t3 - 2].v.ob[1] = pauseCtx->mapPageVtx[phi_t3 - 1].v.ob[1] = + pauseCtx->mapPageVtx[phi_t3 - 4].v.ob[1] - 2; + + pauseCtx->mapPageVtx[phi_t3 - 2].v.tc[1] = pauseCtx->mapPageVtx[phi_t3 - 1].v.tc[1] = 0x40; + } else { + pauseCtx->mapPageVtx = Graph_Alloc(gfxCtx, 128 * sizeof(Vtx)); + func_80823A0C(globalCtx, pauseCtx->mapPageVtx, 2, 17); + } + + pauseCtx->questPageVtx = Graph_Alloc(gfxCtx, 60 * sizeof(Vtx)); + func_80823A0C(globalCtx, pauseCtx->questPageVtx, 3, 0); + + pauseCtx->cursorVtx = Graph_Alloc(gfxCtx, 20 * sizeof(Vtx)); + + for (phi_t2 = 0; phi_t2 < 20; phi_t2++) { + pauseCtx->cursorVtx[phi_t2].v.ob[0] = pauseCtx->cursorVtx[phi_t2].v.ob[1] = + pauseCtx->cursorVtx[phi_t2].v.ob[2] = 0; + + pauseCtx->cursorVtx[phi_t2].v.flag = 0; + + pauseCtx->cursorVtx[phi_t2].v.tc[0] = pauseCtx->cursorVtx[phi_t2].v.tc[1] = 0; + + pauseCtx->cursorVtx[phi_t2].v.cn[0] = pauseCtx->cursorVtx[phi_t2].v.cn[1] = + pauseCtx->cursorVtx[phi_t2].v.cn[2] = pauseCtx->cursorVtx[phi_t2].v.cn[3] = 255; + } + + pauseCtx->cursorVtx[1].v.tc[0] = pauseCtx->cursorVtx[2].v.tc[1] = pauseCtx->cursorVtx[3].v.tc[0] = + pauseCtx->cursorVtx[3].v.tc[1] = pauseCtx->cursorVtx[5].v.tc[0] = pauseCtx->cursorVtx[6].v.tc[1] = + pauseCtx->cursorVtx[7].v.tc[0] = pauseCtx->cursorVtx[7].v.tc[1] = pauseCtx->cursorVtx[9].v.tc[0] = + pauseCtx->cursorVtx[10].v.tc[1] = pauseCtx->cursorVtx[11].v.tc[0] = pauseCtx->cursorVtx[11].v.tc[1] = + pauseCtx->cursorVtx[13].v.tc[0] = pauseCtx->cursorVtx[14].v.tc[1] = + pauseCtx->cursorVtx[15].v.tc[0] = pauseCtx->cursorVtx[15].v.tc[1] = 0x200; + + pauseCtx->cursorVtx[17].v.tc[0] = pauseCtx->cursorVtx[18].v.tc[1] = pauseCtx->cursorVtx[19].v.tc[0] = + pauseCtx->cursorVtx[19].v.tc[1] = 0x400; + + pauseCtx->itemVtx = Graph_Alloc(gfxCtx, 164 * sizeof(Vtx)); + + for (phi_t4 = 0, phi_t2 = 0, phi_t5 = 58; phi_t4 < 4; phi_t4++, phi_t5 -= 32) { + for (phi_t1 = -96, phi_t3 = 0; phi_t3 < 6; phi_t3++, phi_t2 += 4, phi_t1 += 32) { + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 2].v.ob[0] = phi_t1 + 2; + + pauseCtx->itemVtx[phi_t2 + 1].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] + 0x1C; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[1] = + phi_t5 + pauseCtx->offsetY - 2; + + pauseCtx->itemVtx[phi_t2 + 2].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] - 0x1C; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[2] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[2] = + pauseCtx->itemVtx[phi_t2 + 2].v.ob[2] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[2] = 0; + + pauseCtx->itemVtx[phi_t2 + 0].v.flag = pauseCtx->itemVtx[phi_t2 + 1].v.flag = + pauseCtx->itemVtx[phi_t2 + 2].v.flag = pauseCtx->itemVtx[phi_t2 + 3].v.flag = 0; + + pauseCtx->itemVtx[phi_t2 + 0].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 0].v.tc[1] = + pauseCtx->itemVtx[phi_t2 + 1].v.tc[1] = pauseCtx->itemVtx[phi_t2 + 2].v.tc[0] = 0; + + pauseCtx->itemVtx[phi_t2 + 1].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 2].v.tc[1] = + pauseCtx->itemVtx[phi_t2 + 3].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 3].v.tc[1] = 0x400; + + pauseCtx->itemVtx[phi_t2 + 0].v.cn[0] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[0] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[0] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.cn[1] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[1] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[1] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.cn[2] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[2] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[2] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[2] = 255; + + pauseCtx->itemVtx[phi_t2 + 0].v.cn[3] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[3] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[3] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[3] = 255; + } + } + + for (phi_t3 = 1; phi_t3 < 4; phi_t3++, phi_t2 += 4) { + if (gSaveContext.equips.cButtonSlots[phi_t3 - 1] != ITEM_NONE) { + phi_t4 = gSaveContext.equips.cButtonSlots[phi_t3 - 1] * 4; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 2].v.ob[0] = + pauseCtx->itemVtx[phi_t4].v.ob[0] - 2; + + pauseCtx->itemVtx[phi_t2 + 1].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] + 32; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[1] = + pauseCtx->itemVtx[phi_t4].v.ob[1] + 2; + + pauseCtx->itemVtx[phi_t2 + 2].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] - 32; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[2] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[2] = + pauseCtx->itemVtx[phi_t2 + 2].v.ob[2] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[2] = 0; + + pauseCtx->itemVtx[phi_t2 + 0].v.flag = pauseCtx->itemVtx[phi_t2 + 1].v.flag = + pauseCtx->itemVtx[phi_t2 + 2].v.flag = pauseCtx->itemVtx[phi_t2 + 3].v.flag = 0; + + pauseCtx->itemVtx[phi_t2 + 0].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 0].v.tc[1] = + pauseCtx->itemVtx[phi_t2 + 1].v.tc[1] = pauseCtx->itemVtx[phi_t2 + 2].v.tc[0] = 0; + + pauseCtx->itemVtx[phi_t2 + 1].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 2].v.tc[1] = + pauseCtx->itemVtx[phi_t2 + 3].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 3].v.tc[1] = 0x400; + + pauseCtx->itemVtx[phi_t2 + 0].v.cn[0] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[0] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[0] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.cn[1] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[1] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[1] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.cn[2] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[2] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[2] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[2] = 255; + + pauseCtx->itemVtx[phi_t2 + 0].v.cn[3] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[3] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[3] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[3] = pauseCtx->alpha; + } else { + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 2].v.ob[0] = -300; + + pauseCtx->itemVtx[phi_t2 + 1].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] + 32; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[1] = 300; + + pauseCtx->itemVtx[phi_t2 + 2].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] - 32; + } + } + + for (phi_t2 = 108, phi_t3 = 0; phi_t3 < 7; phi_t3++) { + phi_t4 = D_8082B11C[phi_t3]; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 2].v.ob[0] = + pauseCtx->itemVtx[phi_t4].v.ob[0]; + + pauseCtx->itemVtx[phi_t2 + 1].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] + 8; + + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[1] = + pauseCtx->itemVtx[phi_t4].v.ob[1] - 22; + + pauseCtx->itemVtx[phi_t2 + 2].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1] - 8; + + pauseCtx->itemVtx[phi_t2 + 4].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 6].v.ob[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[0] + 6; + + pauseCtx->itemVtx[phi_t2 + 5].v.ob[0] = pauseCtx->itemVtx[phi_t2 + 7].v.ob[0] = + pauseCtx->itemVtx[phi_t2 + 4].v.ob[0] + 8; + + pauseCtx->itemVtx[phi_t2 + 4].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 5].v.ob[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.ob[1]; + + pauseCtx->itemVtx[phi_t2 + 6].v.ob[1] = pauseCtx->itemVtx[phi_t2 + 7].v.ob[1] = + pauseCtx->itemVtx[phi_t2 + 4].v.ob[1] - 8; + + for (phi_t4 = 0; phi_t4 < 2; phi_t4++, phi_t2 += 4) { + pauseCtx->itemVtx[phi_t2 + 0].v.ob[2] = pauseCtx->itemVtx[phi_t2 + 1].v.ob[2] = + pauseCtx->itemVtx[phi_t2 + 2].v.ob[2] = pauseCtx->itemVtx[phi_t2 + 3].v.ob[2] = 0; + + pauseCtx->itemVtx[phi_t2 + 0].v.flag = pauseCtx->itemVtx[phi_t2 + 1].v.flag = + pauseCtx->itemVtx[phi_t2 + 2].v.flag = pauseCtx->itemVtx[phi_t2 + 3].v.flag = 0; + + pauseCtx->itemVtx[phi_t2 + 0].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 0].v.tc[1] = + pauseCtx->itemVtx[phi_t2 + 1].v.tc[1] = pauseCtx->itemVtx[phi_t2 + 2].v.tc[0] = 0; + + pauseCtx->itemVtx[phi_t2 + 1].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 2].v.tc[1] = + pauseCtx->itemVtx[phi_t2 + 3].v.tc[0] = pauseCtx->itemVtx[phi_t2 + 3].v.tc[1] = 0x100; + + pauseCtx->itemVtx[phi_t2 + 0].v.cn[0] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[0] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[0] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[0] = + pauseCtx->itemVtx[phi_t2 + 0].v.cn[1] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[1] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[1] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[1] = + pauseCtx->itemVtx[phi_t2 + 0].v.cn[2] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[2] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[2] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[2] = 255; + + pauseCtx->itemVtx[phi_t2 + 0].v.cn[3] = pauseCtx->itemVtx[phi_t2 + 1].v.cn[3] = + pauseCtx->itemVtx[phi_t2 + 2].v.cn[3] = pauseCtx->itemVtx[phi_t2 + 3].v.cn[3] = pauseCtx->alpha; + } + } + + pauseCtx->equipVtx = Graph_Alloc(gfxCtx, 112 * sizeof(Vtx)); + + for (phi_t4 = 0, phi_t2 = 0, phi_t5 = 58; phi_t2 < 4; phi_t2++, phi_t5 -= 32) { + for (phi_t3 = 0; phi_t3 < 4; phi_t3++, phi_t4 += 4) { + pauseCtx->equipVtx[phi_t4 + 0].v.ob[0] = pauseCtx->equipVtx[phi_t4 + 2].v.ob[0] = D_8082B12C[phi_t3] + 2; + + pauseCtx->equipVtx[phi_t4 + 1].v.ob[0] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[0] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[0] + 28; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 1].v.ob[1] = + phi_t5 + pauseCtx->offsetY - 2; + + pauseCtx->equipVtx[phi_t4 + 2].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] - 28; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[2] = pauseCtx->equipVtx[phi_t4 + 1].v.ob[2] = + pauseCtx->equipVtx[phi_t4 + 2].v.ob[2] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[2] = 0; + + pauseCtx->equipVtx[phi_t4 + 0].v.flag = pauseCtx->equipVtx[phi_t4 + 1].v.flag = + pauseCtx->equipVtx[phi_t4 + 2].v.flag = pauseCtx->equipVtx[phi_t4 + 3].v.flag = 0; + + pauseCtx->equipVtx[phi_t4 + 0].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 0].v.tc[1] = + pauseCtx->equipVtx[phi_t4 + 1].v.tc[1] = pauseCtx->equipVtx[phi_t4 + 2].v.tc[0] = 0; + + pauseCtx->equipVtx[phi_t4 + 1].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 2].v.tc[1] = + pauseCtx->equipVtx[phi_t4 + 3].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 3].v.tc[1] = 0x400; + + pauseCtx->equipVtx[phi_t4 + 0].v.cn[0] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[0] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[0] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[0] = + pauseCtx->equipVtx[phi_t4 + 0].v.cn[1] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[1] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[1] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.cn[2] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[2] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[2] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[2] = 255; + + pauseCtx->equipVtx[phi_t4 + 0].v.cn[3] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[3] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[3] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[3] = pauseCtx->alpha; + } + } + + for (phi_t3 = 0; phi_t3 < 4; phi_t3++, phi_t4 += 4) { + if (CUR_EQUIP_VALUE(phi_t3) != 0) { + phi_t2 = (CUR_EQUIP_VALUE(phi_t3) + D_8082B134[phi_t3] - 1) * 4; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[0] = pauseCtx->equipVtx[phi_t4 + 2].v.ob[0] = + pauseCtx->equipVtx[phi_t2].v.ob[0] - 2; + + pauseCtx->equipVtx[phi_t4 + 1].v.ob[0] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[0] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[0] + 32; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 1].v.ob[1] = + pauseCtx->equipVtx[phi_t2].v.ob[1] + 2; + + pauseCtx->equipVtx[phi_t4 + 2].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] - 32; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[2] = pauseCtx->equipVtx[phi_t4 + 1].v.ob[2] = + pauseCtx->equipVtx[phi_t4 + 2].v.ob[2] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[2] = 0; + + pauseCtx->equipVtx[phi_t4 + 0].v.flag = pauseCtx->equipVtx[phi_t4 + 1].v.flag = + pauseCtx->equipVtx[phi_t4 + 2].v.flag = pauseCtx->equipVtx[phi_t4 + 3].v.flag = 0; + + pauseCtx->equipVtx[phi_t4 + 0].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 0].v.tc[1] = + pauseCtx->equipVtx[phi_t4 + 1].v.tc[1] = pauseCtx->equipVtx[phi_t4 + 2].v.tc[0] = 0; + + pauseCtx->equipVtx[phi_t4 + 1].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 2].v.tc[1] = + pauseCtx->equipVtx[phi_t4 + 3].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 3].v.tc[1] = 0x400; + + pauseCtx->equipVtx[phi_t4 + 0].v.cn[0] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[0] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[0] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[0] = + pauseCtx->equipVtx[phi_t4 + 0].v.cn[1] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[1] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[1] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.cn[2] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[2] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[2] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[2] = 255; + + pauseCtx->equipVtx[phi_t4 + 0].v.cn[3] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[3] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[3] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[3] = pauseCtx->alpha; + } + } + + phi_t1 = 112; + phi_t5 = 50; + while (true) { + pauseCtx->equipVtx[phi_t4 + 0].v.ob[0] = pauseCtx->equipVtx[phi_t4 + 2].v.ob[0] = -64; + + pauseCtx->equipVtx[phi_t4 + 1].v.ob[0] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[0] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[0] + 64; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 1].v.ob[1] = phi_t5 + pauseCtx->offsetY; + + pauseCtx->equipVtx[phi_t4 + 2].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] - 32; + + pauseCtx->equipVtx[phi_t4 + 0].v.ob[2] = pauseCtx->equipVtx[phi_t4 + 1].v.ob[2] = + pauseCtx->equipVtx[phi_t4 + 2].v.ob[2] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[2] = 0; + + pauseCtx->equipVtx[phi_t4 + 0].v.flag = pauseCtx->equipVtx[phi_t4 + 1].v.flag = + pauseCtx->equipVtx[phi_t4 + 2].v.flag = pauseCtx->equipVtx[phi_t4 + 3].v.flag = 0; + + pauseCtx->equipVtx[phi_t4 + 0].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 0].v.tc[1] = + pauseCtx->equipVtx[phi_t4 + 1].v.tc[1] = pauseCtx->equipVtx[phi_t4 + 2].v.tc[0] = 0; + + pauseCtx->equipVtx[phi_t4 + 1].v.tc[0] = pauseCtx->equipVtx[phi_t4 + 3].v.tc[0] = 0x800; + + pauseCtx->equipVtx[phi_t4 + 2].v.tc[1] = pauseCtx->equipVtx[phi_t4 + 3].v.tc[1] = 0x400; + + pauseCtx->equipVtx[phi_t4 + 0].v.cn[0] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[0] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[0] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[0] = + pauseCtx->equipVtx[phi_t4 + 0].v.cn[1] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[1] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[1] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.cn[2] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[2] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[2] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[2] = 255; + + pauseCtx->equipVtx[phi_t4 + 0].v.cn[3] = pauseCtx->equipVtx[phi_t4 + 1].v.cn[3] = + pauseCtx->equipVtx[phi_t4 + 2].v.cn[3] = pauseCtx->equipVtx[phi_t4 + 3].v.cn[3] = pauseCtx->alpha; + + phi_t1 -= 32; + phi_t5 -= 32; + if (phi_t1 < 0) { + pauseCtx->equipVtx[phi_t4 + 2].v.ob[1] = pauseCtx->equipVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->equipVtx[phi_t4 + 0].v.ob[1] - 0x10; + + pauseCtx->equipVtx[phi_t4 + 2].v.tc[1] = pauseCtx->equipVtx[phi_t4 + 3].v.tc[1] = 0x200; + break; + } + + phi_t4 += 4; + } + + pauseCtx->questVtx = Graph_Alloc(gfxCtx, 188 * sizeof(Vtx)); + + for (phi_t4 = 0, phi_t3 = 0; phi_t3 < 47; phi_t3++, phi_t4 += 4) { + phi_t2_2 = D_8082B1F8[phi_t3]; + + if ((phi_t3 < 6) || (phi_t3 >= 41)) { + pauseCtx->questVtx[phi_t4 + 0].v.ob[0] = pauseCtx->questVtx[phi_t4 + 2].v.ob[0] = D_8082B138[phi_t3]; + + pauseCtx->questVtx[phi_t4 + 1].v.ob[0] = pauseCtx->questVtx[phi_t4 + 3].v.ob[0] = + pauseCtx->questVtx[phi_t4 + 0].v.ob[0] + D_8082B1F8[phi_t3]; + + pauseCtx->questVtx[phi_t4 + 0].v.ob[1] = pauseCtx->questVtx[phi_t4 + 1].v.ob[1] = + D_8082B198[phi_t3] + pauseCtx->offsetY; + + pauseCtx->questVtx[phi_t4 + 2].v.ob[1] = pauseCtx->questVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->questVtx[phi_t4 + 0].v.ob[1] - D_8082B1F8[phi_t3]; + + if (phi_t3 >= 41) { + pauseCtx->questVtx[phi_t4 + 1].v.ob[0] = pauseCtx->questVtx[phi_t4 + 3].v.ob[0] = + pauseCtx->questVtx[phi_t4 + 0].v.ob[0] + 8; + + pauseCtx->questVtx[phi_t4 + 0].v.ob[1] = pauseCtx->questVtx[phi_t4 + 1].v.ob[1] = + D_8082B198[phi_t3] + pauseCtx->offsetY - 6; + + pauseCtx->questVtx[phi_t4 + 2].v.ob[1] = pauseCtx->questVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->questVtx[phi_t4 + 0].v.ob[1] - 16; + + phi_t2_2 = 8; + } + } else { + if ((phi_t3 >= 6) && (phi_t3 <= 17)) { + phi_t2_2 = 16; + } + + pauseCtx->questVtx[phi_t4 + 0].v.ob[0] = pauseCtx->questVtx[phi_t4 + 2].v.ob[0] = D_8082B138[phi_t3] + 2; + + pauseCtx->questVtx[phi_t4 + 1].v.ob[0] = pauseCtx->questVtx[phi_t4 + 3].v.ob[0] = + pauseCtx->questVtx[phi_t4 + 0].v.ob[0] + phi_t2_2 - 4; + + pauseCtx->questVtx[phi_t4 + 0].v.ob[1] = pauseCtx->questVtx[phi_t4 + 1].v.ob[1] = + D_8082B198[phi_t3] + pauseCtx->offsetY - 2; + + pauseCtx->questVtx[phi_t4 + 2].v.ob[1] = pauseCtx->questVtx[phi_t4 + 3].v.ob[1] = + pauseCtx->questVtx[phi_t4 + 0].v.ob[1] - D_8082B1F8[phi_t3] + 4; + } + + pauseCtx->questVtx[phi_t4 + 0].v.ob[2] = pauseCtx->questVtx[phi_t4 + 1].v.ob[2] = + pauseCtx->questVtx[phi_t4 + 2].v.ob[2] = pauseCtx->questVtx[phi_t4 + 3].v.ob[2] = 0; + + pauseCtx->questVtx[phi_t4 + 0].v.flag = pauseCtx->questVtx[phi_t4 + 1].v.flag = + pauseCtx->questVtx[phi_t4 + 2].v.flag = pauseCtx->questVtx[phi_t4 + 3].v.flag = 0; + + pauseCtx->questVtx[phi_t4 + 0].v.tc[0] = pauseCtx->questVtx[phi_t4 + 0].v.tc[1] = + pauseCtx->questVtx[phi_t4 + 1].v.tc[1] = pauseCtx->questVtx[phi_t4 + 2].v.tc[0] = 0; + + pauseCtx->questVtx[phi_t4 + 1].v.tc[0] = pauseCtx->questVtx[phi_t4 + 3].v.tc[0] = phi_t2_2 << 5; + pauseCtx->questVtx[phi_t4 + 2].v.tc[1] = pauseCtx->questVtx[phi_t4 + 3].v.tc[1] = D_8082B1F8[phi_t3] << 5; + + pauseCtx->questVtx[phi_t4 + 0].v.cn[0] = pauseCtx->questVtx[phi_t4 + 1].v.cn[0] = + pauseCtx->questVtx[phi_t4 + 2].v.cn[0] = pauseCtx->questVtx[phi_t4 + 3].v.cn[0] = + pauseCtx->questVtx[phi_t4 + 0].v.cn[1] = pauseCtx->questVtx[phi_t4 + 1].v.cn[1] = + pauseCtx->questVtx[phi_t4 + 2].v.cn[1] = pauseCtx->questVtx[phi_t4 + 3].v.cn[1] = + pauseCtx->questVtx[phi_t4 + 0].v.cn[2] = pauseCtx->questVtx[phi_t4 + 1].v.cn[2] = + pauseCtx->questVtx[phi_t4 + 2].v.cn[2] = pauseCtx->questVtx[phi_t4 + 3].v.cn[2] = 255; + + pauseCtx->questVtx[phi_t4 + 0].v.cn[3] = pauseCtx->questVtx[phi_t4 + 1].v.cn[3] = + pauseCtx->questVtx[phi_t4 + 2].v.cn[3] = pauseCtx->questVtx[phi_t4 + 3].v.cn[3] = pauseCtx->alpha; + } + + pauseCtx->infoPanelVtx = Graph_Alloc(gfxCtx, 28 * sizeof(Vtx)); + + pauseCtx->saveVtx = Graph_Alloc(gfxCtx, 80 * sizeof(Vtx)); + func_80823A0C(globalCtx, pauseCtx->saveVtx, 5, 5); +} + +void KaleidoScope_DrawGameOver(GlobalContext* globalCtx) { + GraphicsContext* gfxCtx = globalCtx->state.gfxCtx; + + OPEN_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 3122); + + func_800944C4(gfxCtx); + + gDPSetCycleType(POLY_KAL_DISP++, G_CYC_2CYCLE); + gDPSetRenderMode(POLY_KAL_DISP++, G_RM_PASS, G_RM_XLU_SURF2); + gDPSetCombineLERP(POLY_KAL_DISP++, TEXEL1, TEXEL0, PRIM_LOD_FRAC, TEXEL0, 0, 0, 0, TEXEL0, PRIMITIVE, ENVIRONMENT, + COMBINED, ENVIRONMENT, COMBINED, 0, PRIMITIVE, 0); + + gDPSetPrimColor(POLY_KAL_DISP++, 0, 80, D_8082AB8C, D_8082AB90, D_8082AB94, D_8082AB98); + gDPSetEnvColor(POLY_KAL_DISP++, D_8082AB9C, D_8082ABA0, D_8082ABA4, 255); + + VREG(89) -= 2; + + gDPLoadMultiBlock(POLY_KAL_DISP++, gGameOverP1Tex, 0, 0, G_IM_FMT_IA, G_IM_SIZ_8b, 64, 32, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gDPLoadMultiBlock(POLY_KAL_DISP++, gGameOverMaskTex, 256, 1, G_IM_FMT_IA, G_IM_SIZ_8b, 64, 32, 0, + G_TX_WRAP | G_TX_NOMIRROR, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, 5, G_TX_NOLOD, G_TX_NOLOD); + + gDPSetTileSize(POLY_KAL_DISP++, 1, 0, VREG(89) & 0x7F, 252, (VREG(89) & 0x7F) + 0x7C); + + gSPTextureRectangle(POLY_KAL_DISP++, VREG(87) << 2, VREG(88) << 2, (VREG(87) + 64) << 2, (VREG(88) + 32) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + gDPLoadMultiBlock(POLY_KAL_DISP++, gGameOverP2Tex, 0, 0, G_IM_FMT_IA, G_IM_SIZ_8b, 64, 32, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + + gSPTextureRectangle(POLY_KAL_DISP++, (VREG(87) + 64) << 2, VREG(88) << 2, (VREG(87) + 128) << 2, + (VREG(88) + 32) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + gDPLoadMultiBlock(POLY_KAL_DISP++, gGameOverP3Tex, 0, 0, G_IM_FMT_IA, G_IM_SIZ_8b, 64, 32, 0, + G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSPTextureRectangle(POLY_KAL_DISP++, (VREG(87) + 128) << 2, VREG(88) << 2, (VREG(87) + 192) << 2, + (VREG(88) + 32) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + + CLOSE_DISPS(gfxCtx, "../z_kaleido_scope_PAL.c", 3169); +} + +void KaleidoScope_Draw(GlobalContext* globalCtx) { + Input* input = &globalCtx->state.input[0]; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 3188); + + pauseCtx->stickRelX = input->rel.stick_x; + pauseCtx->stickRelY = input->rel.stick_y; + + //gSPSegment(POLY_KAL_DISP++, 0x02, interfaceCtx->parameterSegment); + gSPSegment(POLY_KAL_DISP++, 0x07, pauseCtx->playerSegment); + //gSPSegment(POLY_KAL_DISP++, 0x08, pauseCtx->iconItemSegment); + //gSPSegment(POLY_KAL_DISP++, 0x09, pauseCtx->iconItem24Segment); + gSPSegment(POLY_KAL_DISP++, 0x0A, pauseCtx->nameSegment); + //gSPSegment(POLY_KAL_DISP++, 0x0C, pauseCtx->iconItemAltSegment); + //gSPSegment(POLY_KAL_DISP++, 0x0D, pauseCtx->iconItemLangSegment); + + if (pauseCtx->debugState == 0) + { + KaleidoScope_SetView(pauseCtx, pauseCtx->eye.x, pauseCtx->eye.y, pauseCtx->eye.z); + + func_800949A8_KAL(globalCtx->state.gfxCtx); + KaleidoScope_InitVertices(globalCtx, globalCtx->state.gfxCtx); + KaleidoScope_DrawPages(globalCtx, globalCtx->state.gfxCtx); + + func_800949A8_KAL(globalCtx->state.gfxCtx); + gDPSetCombineLERP(POLY_KAL_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + + KaleidoScope_SetView(pauseCtx, 0.0f, 0.0f, 64.0f); + + if (!((pauseCtx->state >= 8) && (pauseCtx->state <= 0x11))) { + KaleidoScope_DrawInfoPanel(globalCtx); + } + } + + if ((pauseCtx->state >= 0xB) && (pauseCtx->state <= 0x11)) { + KaleidoScope_DrawGameOver(globalCtx); + } + + if ((pauseCtx->debugState == 1) || (pauseCtx->debugState == 2)) { + KaleidoScope_DrawDebugEditor(globalCtx); + } + + func_800AAA50(&globalCtx->view, 15); + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_kaleido_scope_PAL.c", 3254); +} + +uint32_t _bswap32(uint32_t a) +{ + a = ((a & 0x000000FF) << 24) | + ((a & 0x0000FF00) << 8) | + ((a & 0x00FF0000) >> 8) | + ((a & 0xFF000000) >> 24); + return a; +} + +void KaleidoScope_GrayOutTextureRGBA32(u32* texture, u16 pixelCount) { + u32 rgb; + u16 gray; + u16 i; + + bind_hook( GRAYOUT_TEXTURE); + init_hook(2, + (struct HookParameter){ .name = "texture", .parameter = &texture }, + (struct HookParameter){ .name = "pixelCount", .parameter = &pixelCount } + ); + if (!call_hook(0)) + return; + + texture = ResourceMgr_LoadTexByName(texture); + + for (i = 0; i < pixelCount; i++) { + uint32_t px = texture[i]; + if ((px & 0xFFFFFF00) != 0) { + u8 a = (px & 0xFF000000) >> 24; + u8 b = (px & 0x00FF0000) >> 16; + u8 g = (px & 0x0000FF00) >> 8; + u8 r = (px & 0x000000FF) >> 0; + gray = (r + g + b) / 7; + + r = gray; + g = gray; + b = gray; + + texture[i] = (a << 24) + (b << 16) + (g << 8) + (r << 0); + } + } +} + +void func_808265BC(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + + pauseCtx->eye.x += D_8082ABAC[pauseCtx->mode] * ZREG(46); + pauseCtx->eye.z += D_8082ABCC[pauseCtx->mode] * ZREG(46); + pauseCtx->unk_1EA += 4 * ZREG(46); + + if (pauseCtx->unk_1EA == (64 * ZREG(47))) { + func_80084BF4(globalCtx, 1); + gSaveContext.buttonStatus[0] = D_8082AB6C[pauseCtx->pageIndex][0]; + gSaveContext.buttonStatus[1] = D_8082AB6C[pauseCtx->pageIndex][1]; + gSaveContext.buttonStatus[2] = D_8082AB6C[pauseCtx->pageIndex][2]; + gSaveContext.buttonStatus[3] = D_8082AB6C[pauseCtx->pageIndex][3]; + gSaveContext.buttonStatus[4] = D_8082AB6C[pauseCtx->pageIndex][4]; + pauseCtx->pageIndex = D_8082ABEC[pauseCtx->mode]; + pauseCtx->unk_1E4 = 0; + pauseCtx->state++; + pauseCtx->alpha = 255; + Interface_LoadActionLabelB(globalCtx, DO_ACTION_SAVE); + } else if (pauseCtx->unk_1EA == 64) { + pauseCtx->pageIndex = D_8082ABEC[pauseCtx->mode]; + pauseCtx->mode = (u16)(pauseCtx->pageIndex * 2) + 1; + } +} + +void KaleidoScope_UpdateCursorSize(PauseContext* pauseCtx) { + s32 temp1; + s32 temp2; + s32 temp3; + s32 temp4; + + if (pauseCtx->cursorSpecialPos == 0) { + temp1 = -1; + temp2 = 1; + temp3 = 14; + temp4 = 14; + if (pauseCtx->pageIndex == PAUSE_MAP) { + if (!sInDungeonScene) { + temp1 = -6; + temp2 = 6; + temp3 = 4; + temp4 = 4; + } else if (pauseCtx->cursorSlot[pauseCtx->pageIndex] >= 3) { + temp1 = -6; + temp2 = 5; + temp4 = 7; + temp3 = 19; + } else { + temp1 = -3; + temp2 = 3; + temp3 = 13; + temp4 = 13; + } + } else if (pauseCtx->pageIndex == PAUSE_QUEST) { + temp1 = -4; + temp2 = 4; + temp3 = 12; + temp4 = 12; + if (pauseCtx->cursorSlot[pauseCtx->pageIndex] == 0x18) { + temp1 = -2; + temp2 = 2; + temp3 = 32; + temp4 = 32; + } else if (pauseCtx->cursorSlot[pauseCtx->pageIndex] == 0x17) { + temp1 = -4; + temp2 = 4; + temp4 = 13; + temp3 = 34; + } else if (pauseCtx->cursorSlot[pauseCtx->pageIndex] < 6) { + temp1 = -1; + temp2 = 1; + temp3 = 10; + temp4 = 10; + } else if ((pauseCtx->cursorSlot[pauseCtx->pageIndex] >= 6) && + (pauseCtx->cursorSlot[pauseCtx->pageIndex] < 0x12)) { + temp1 = -5; + temp2 = 3; + temp3 = 8; + temp4 = 8; + } + } + } else { + temp1 = -4; + temp2 = 4; + temp3 = 16; + temp4 = 16; + } + + pauseCtx->cursorVtx[0].v.ob[0] = pauseCtx->cursorVtx[2].v.ob[0] = pauseCtx->cursorVtx[0].v.ob[0] + temp1; + pauseCtx->cursorVtx[1].v.ob[0] = pauseCtx->cursorVtx[3].v.ob[0] = pauseCtx->cursorVtx[0].v.ob[0] + 16; + pauseCtx->cursorVtx[0].v.ob[1] = pauseCtx->cursorVtx[1].v.ob[1] = pauseCtx->cursorVtx[0].v.ob[1] + temp2; + pauseCtx->cursorVtx[2].v.ob[1] = pauseCtx->cursorVtx[3].v.ob[1] = pauseCtx->cursorVtx[0].v.ob[1] - 16; + + pauseCtx->cursorVtx[4].v.ob[0] = pauseCtx->cursorVtx[6].v.ob[0] = pauseCtx->cursorVtx[0].v.ob[0] + temp3; + pauseCtx->cursorVtx[5].v.ob[0] = pauseCtx->cursorVtx[7].v.ob[0] = pauseCtx->cursorVtx[4].v.ob[0] + 16; + pauseCtx->cursorVtx[4].v.ob[1] = pauseCtx->cursorVtx[5].v.ob[1] = pauseCtx->cursorVtx[0].v.ob[1]; + pauseCtx->cursorVtx[6].v.ob[1] = pauseCtx->cursorVtx[7].v.ob[1] = pauseCtx->cursorVtx[4].v.ob[1] - 16; + + pauseCtx->cursorVtx[8].v.ob[0] = pauseCtx->cursorVtx[10].v.ob[0] = pauseCtx->cursorVtx[0].v.ob[0]; + pauseCtx->cursorVtx[9].v.ob[0] = pauseCtx->cursorVtx[11].v.ob[0] = pauseCtx->cursorVtx[8].v.ob[0] + 16; + pauseCtx->cursorVtx[8].v.ob[1] = pauseCtx->cursorVtx[9].v.ob[1] = pauseCtx->cursorVtx[0].v.ob[1] - temp4; + pauseCtx->cursorVtx[10].v.ob[1] = pauseCtx->cursorVtx[11].v.ob[1] = pauseCtx->cursorVtx[8].v.ob[1] - 16; + + pauseCtx->cursorVtx[12].v.ob[0] = pauseCtx->cursorVtx[14].v.ob[0] = pauseCtx->cursorVtx[0].v.ob[0] + temp3; + pauseCtx->cursorVtx[13].v.ob[0] = pauseCtx->cursorVtx[15].v.ob[0] = pauseCtx->cursorVtx[12].v.ob[0] + 16; + pauseCtx->cursorVtx[12].v.ob[1] = pauseCtx->cursorVtx[13].v.ob[1] = pauseCtx->cursorVtx[0].v.ob[1] - temp4; + pauseCtx->cursorVtx[14].v.ob[1] = pauseCtx->cursorVtx[15].v.ob[1] = pauseCtx->cursorVtx[12].v.ob[1] - 16; +} + +void KaleidoScope_LoadDungeonMap(GlobalContext* globalCtx) { + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + memcpy(interfaceCtx->mapSegment, ResourceMgr_LoadTexByName(sDungeonMapTexs[R_MAP_TEX_INDEX]), 0x800); + memcpy(interfaceCtx->mapSegment + 0x800, ResourceMgr_LoadTexByName(sDungeonMapTexs[R_MAP_TEX_INDEX + 1]), 0x800); +} + +void KaleidoScope_UpdateDungeonMap(GlobalContext* globalCtx) { + PauseContext* pauseCtx = &globalCtx->pauseCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + + osSyncPrintf("MAP DMA = %d\n", globalCtx->interfaceCtx.mapPaletteIndex); + + KaleidoScope_LoadDungeonMap(globalCtx); + Map_SetFloorPalettesData(globalCtx, pauseCtx->dungeonMapSlot - 3); + + if ((globalCtx->sceneNum >= SCENE_YDAN) && (globalCtx->sceneNum <= SCENE_TAKARAYA)) { + if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) { + KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment, 2040, interfaceCtx->mapPaletteIndex, 14); + } + } + + if ((globalCtx->sceneNum >= SCENE_YDAN) && (globalCtx->sceneNum <= SCENE_TAKARAYA)) { + if ((VREG(30) + 3) == pauseCtx->cursorPoint[PAUSE_MAP]) { + KaleidoScope_OverridePalIndexCI4(interfaceCtx->mapSegment + 0x800, 2040, interfaceCtx->mapPaletteIndex, 14); + } + } +} + +void KaleidoScope_Update(GlobalContext* globalCtx) +{ + static s16 D_8082B258 = 0; + static s16 D_8082B25C = 10; + static s16 D_8082B260 = 0; + PauseContext* pauseCtx = &globalCtx->pauseCtx; + InterfaceContext* interfaceCtx = &globalCtx->interfaceCtx; + GameOverContext* gameOverCtx = &globalCtx->gameOverCtx; + Player* player = GET_PLAYER(globalCtx); + Input* input = &globalCtx->state.input[0]; + size_t size; + size_t size0; + size_t size1; + size_t size2; + u16 i; + s16 stepR; + s16 stepG; + s16 stepB; + s16 stepA; + s32 pad; + + if ((R_PAUSE_MENU_MODE >= 3) && (((pauseCtx->state >= 4) && (pauseCtx->state <= 7)) || + ((pauseCtx->state >= 0xA) && (pauseCtx->state <= 0x12)))) { + + if ((!pauseCtx->unk_1E4 || (pauseCtx->unk_1E4 == 8)) && (pauseCtx->state == 6)) { + pauseCtx->stickRelX = input->rel.stick_x; + pauseCtx->stickRelY = input->rel.stick_y; + KaleidoScope_UpdateCursorSize(&globalCtx->pauseCtx); + KaleidoScope_HandlePageToggles(pauseCtx, input); + } else if ((pauseCtx->pageIndex == PAUSE_QUEST) && ((pauseCtx->unk_1E4 < 3) || (pauseCtx->unk_1E4 == 5))) { + KaleidoScope_UpdateCursorSize(&globalCtx->pauseCtx); + } + + if (pauseCtx->state == 6) { + KaleidoScope_UpdateNamePanel(globalCtx); + } + } + + switch (pauseCtx->state) { + case 3: + D_808321A8[0] = gSaveContext.buttonStatus[0]; + D_808321A8[1] = gSaveContext.buttonStatus[1]; + D_808321A8[2] = gSaveContext.buttonStatus[2]; + D_808321A8[3] = gSaveContext.buttonStatus[3]; + D_808321A8[4] = gSaveContext.buttonStatus[4]; + + pauseCtx->cursorX[PAUSE_MAP] = 0; + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_MAP] = pauseCtx->dungeonMapSlot = + VREG(30) + 3; + + WREG(16) = -175; + WREG(17) = 155; + + pauseCtx->unk_204 = -314.0f; + + pauseCtx->playerSegment = (void*)(((uintptr_t)globalCtx->objectCtx.spaceStart + 0x30) & ~0x3F); + + size1 = func_80091738(globalCtx, pauseCtx->playerSegment, &pauseCtx->playerSkelAnime); + osSyncPrintf("プレイヤー size1=%x\n", size1); + + pauseCtx->iconItemSegment = (void*)(((uintptr_t)pauseCtx->playerSegment + size1 + 0xF) & ~0xF); + + size0 = (uintptr_t)_icon_item_staticSegmentRomEnd - (uintptr_t)_icon_item_staticSegmentRomStart; + osSyncPrintf("icon_item size0=%x\n", size0); + DmaMgr_SendRequest1(pauseCtx->iconItemSegment, (uintptr_t)_icon_item_staticSegmentRomStart, size0, + "../z_kaleido_scope_PAL.c", 3662); + + gSegments[8] = VIRTUAL_TO_PHYSICAL(pauseCtx->iconItemSegment); + + for (i = 0; i < ARRAY_COUNTU(gItemAgeReqs); i++) { + if ((gItemAgeReqs[i] != 9) && (gItemAgeReqs[i] != ((void)0, gSaveContext.linkAge))) + { + gSPInvalidateTexCache(globalCtx->state.gfxCtx->polyKal.p++, ResourceMgr_LoadTexByName(gItemIcons[i])); + KaleidoScope_GrayOutTextureRGBA32(SEGMENTED_TO_VIRTUAL(gItemIcons[i]), 0x400); + } + } + + pauseCtx->iconItem24Segment = (void*)(((uintptr_t)pauseCtx->iconItemSegment + size0 + 0xF) & ~0xF); + +#if 1 + //size = (uintptr_t)_icon_item_24_staticSegmentRomEnd - (uintptr_t)_icon_item_24_staticSegmentRomStart; + //osSyncPrintf("icon_item24 size=%x\n", size); + //DmaMgr_SendRequest1(pauseCtx->iconItem24Segment, (uintptr_t)_icon_item_24_staticSegmentRomStart, size, + //"../z_kaleido_scope_PAL.c", 3675); + + pauseCtx->iconItemAltSegment = (void*)(((uintptr_t)pauseCtx->iconItem24Segment + size + 0xF) & ~0xF); +#endif + + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: +#if 1 + sInDungeonScene = true; + //size2 = (uintptr_t)_icon_item_dungeon_staticSegmentRomEnd - (uintptr_t)_icon_item_dungeon_staticSegmentRomStart; + //osSyncPrintf("icon_item_dungeon dungeon-size2=%x\n", size2); + //DmaMgr_SendRequest1(pauseCtx->iconItemAltSegment, (uintptr_t)_icon_item_dungeon_staticSegmentRomStart, + //size2, "../z_kaleido_scope_PAL.c", 3712); +#endif + + interfaceCtx->mapPalette[28] = 6; + interfaceCtx->mapPalette[29] = 99; + KaleidoScope_UpdateDungeonMap(globalCtx); + break; + + default: +#if 1 + sInDungeonScene = false; + //size2 = (uintptr_t)_icon_item_field_staticSegmentRomEnd - (uintptr_t)_icon_item_field_staticSegmentRomStart; + //osSyncPrintf("icon_item_field field-size2=%x\n", size2); + //DmaMgr_SendRequest1(pauseCtx->iconItemAltSegment, (uintptr_t)_icon_item_field_staticSegmentRomStart, + //size2, "../z_kaleido_scope_PAL.c", 3726); +#endif + break; + } + + // OTRTODO: LANGUAGE SUPPORT +#if 1 + pauseCtx->iconItemLangSegment = (void*)(((uintptr_t)pauseCtx->iconItemAltSegment + size2 + 0xF) & ~0xF); + + if (gSaveContext.language == LANGUAGE_ENG) { + //size = (uintptr_t)_icon_item_nes_staticSegmentRomEnd - (uintptr_t)_icon_item_nes_staticSegmentRomStart; + //osSyncPrintf("icon_item_dungeon dungeon-size=%x\n", size); + //DmaMgr_SendRequest1(pauseCtx->iconItemLangSegment, _icon_item_nes_staticSegmentRomStart, size, + //"../z_kaleido_scope_PAL.c", 3739); + } else if (gSaveContext.language == LANGUAGE_GER) { + //size = (uintptr_t)_icon_item_ger_staticSegmentRomEnd - (uintptr_t)_icon_item_ger_staticSegmentRomStart; + //osSyncPrintf("icon_item_dungeon dungeon-size=%x\n", size); + //DmaMgr_SendRequest1(pauseCtx->iconItemLangSegment, (uintptr_t)_icon_item_ger_staticSegmentRomStart, size, + //"../z_kaleido_scope_PAL.c", 3746); + } else { + //size = (uintptr_t)_icon_item_fra_staticSegmentRomEnd - (uintptr_t)_icon_item_fra_staticSegmentRomStart; + //osSyncPrintf("icon_item_dungeon dungeon-size=%x\n", size); + //DmaMgr_SendRequest1(pauseCtx->iconItemLangSegment, (uintptr_t)_icon_item_fra_staticSegmentRomStart, size, + //"../z_kaleido_scope_PAL.c", 3753); + } +#endif + + //pauseCtx->nameSegment = (void*)(((uintptr_t)pauseCtx->iconItemLangSegment + size + 0xF) & ~0xF); + pauseCtx->nameSegment = malloc(0x400 + 0xA00); // OTRTODO: GET RID OF THIS + + osSyncPrintf("サイズ=%x\n", size2 + size1 + size0 + size); + osSyncPrintf("item_name I_N_PT=%x\n", 0x800); + Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE); + osSyncPrintf("サイズ=%x\n", size2 + size1 + size0 + size + 0x800); + + if (((void)0, gSaveContext.worldMapArea) < 22) { + if (gSaveContext.language == LANGUAGE_ENG) { + memcpy(pauseCtx->nameSegment + 0x400, ResourceMgr_LoadTexByName(mapNameTextures[36 + gSaveContext.worldMapArea]), 0xA00); + } else if (gSaveContext.language == LANGUAGE_GER) { + DmaMgr_SendRequest1(pauseCtx->nameSegment + 0x400, + (uintptr_t)_map_name_staticSegmentRomStart + + (((void)0, gSaveContext.worldMapArea) * 0xA00) + 0x16C00, + 0xA00, "../z_kaleido_scope_PAL.c", 3780); + } else { + DmaMgr_SendRequest1(pauseCtx->nameSegment + 0x400, + (uintptr_t)_map_name_staticSegmentRomStart + + (((void)0, gSaveContext.worldMapArea) * 0xA00) + 0x24800, + 0xA00, "../z_kaleido_scope_PAL.c", 3784); + } + } + // OTRTODO - player on pause + #if 1 + sPreRenderCvg = (void*)(((uintptr_t)pauseCtx->nameSegment + 0x400 + 0xA00 + 0xF) & ~0xF); + + PreRender_Init(&sPlayerPreRender); + PreRender_SetValuesSave(&sPlayerPreRender, 64, 112, pauseCtx->playerSegment, NULL, sPreRenderCvg); + + KaleidoScope_DrawPlayerWork(globalCtx); + //KaleidoScope_SetupPlayerPreRender(globalCtx); + #endif + for (i = 0; i < ARRAY_COUNT(pauseCtx->worldMapPoints); i++) { + pauseCtx->worldMapPoints[i] = 0; + } + + if (CHECK_QUEST_ITEM(QUEST_GERUDO_CARD)) { + pauseCtx->worldMapPoints[0] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { + pauseCtx->worldMapPoints[0] = 1; + } + + if (INV_CONTENT(ITEM_LONGSHOT) == ITEM_LONGSHOT) { + pauseCtx->worldMapPoints[1] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_GERUDO_CARD)) { + pauseCtx->worldMapPoints[1] = 1; + } + + if (gSaveContext.eventChkInf[11] & 4) { + pauseCtx->worldMapPoints[2] = 1; + } + + if (INV_CONTENT(ITEM_LONGSHOT) == ITEM_LONGSHOT) { + pauseCtx->worldMapPoints[2] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_GERUDO_CARD)) { + pauseCtx->worldMapPoints[2] = 1; + } + + if (CUR_UPG_VALUE(UPG_SCALE)) { + pauseCtx->worldMapPoints[3] = 1; + } + + if (CHECK_OWNED_EQUIP(EQUIP_BOOTS, 1)) { + pauseCtx->worldMapPoints[3] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER)) { + pauseCtx->worldMapPoints[3] = 1; + } + + if (gSaveContext.eventChkInf[0] & 0x200) { + pauseCtx->worldMapPoints[4] = 1; + } + + if (INV_CONTENT(ITEM_OCARINA_FAIRY) != ITEM_NONE) { + pauseCtx->worldMapPoints[4] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_SONG_EPONA)) { + pauseCtx->worldMapPoints[4] = 1; + } + + if (gSaveContext.eventChkInf[6] & 0x400) { + pauseCtx->worldMapPoints[4] = 2; + } + + if (gSaveContext.eventChkInf[1] & 0x100) { + pauseCtx->worldMapPoints[4] = 1; + } + + if (gSaveContext.eventChkInf[0] & 0x200) { + pauseCtx->worldMapPoints[5] = 2; + } + + if (gSaveContext.eventChkInf[4] & 1) { + pauseCtx->worldMapPoints[5] = 1; + } + + if (INV_CONTENT(ITEM_OCARINA_TIME) == ITEM_OCARINA_TIME) { + pauseCtx->worldMapPoints[5] = 2; + } + + if (gSaveContext.eventChkInf[4] & 0x20) { + pauseCtx->worldMapPoints[5] = 1; + } + + if (INV_CONTENT(ITEM_ARROW_LIGHT) == ITEM_ARROW_LIGHT) { + pauseCtx->worldMapPoints[5] = 2; + } + + if (gSaveContext.eventChkInf[0] & 0x200) { + pauseCtx->worldMapPoints[6] = 1; + } + + if (gSaveContext.eventChkInf[4] & 1) { + pauseCtx->worldMapPoints[7] = 2; + } + + if (gSaveContext.eventChkInf[2] & 0x20) { + pauseCtx->worldMapPoints[7] = 1; + } + + if (INV_CONTENT(ITEM_HOOKSHOT) == ITEM_HOOKSHOT) { + pauseCtx->worldMapPoints[7] = 2; + } + + if (gSaveContext.eventChkInf[4] & 0x200) { + pauseCtx->worldMapPoints[7] = 1; + } + + if (gBitFlags[1] & gSaveContext.worldMapAreaData) { + pauseCtx->worldMapPoints[8] = 1; + } + + if (CHECK_QUEST_ITEM(QUEST_SONG_LULLABY)) { + pauseCtx->worldMapPoints[8] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_SONG_SUN)) { + pauseCtx->worldMapPoints[8] = 1; + } + + if (gSaveContext.eventChkInf[4] & 0x20) { + pauseCtx->worldMapPoints[8] = 2; + } + + if (INV_CONTENT(ITEM_HOOKSHOT) == ITEM_HOOKSHOT) { + pauseCtx->worldMapPoints[8] = 1; + } + + if (CHECK_QUEST_ITEM(QUEST_SONG_STORMS)) { + pauseCtx->worldMapPoints[8] = 2; + } + + if (gSaveContext.eventChkInf[6] & 0x80) { + pauseCtx->worldMapPoints[8] = 1; + } + + if (gSaveContext.eventChkInf[10] & 0x400) { + pauseCtx->worldMapPoints[8] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { + pauseCtx->worldMapPoints[8] = 1; + } + + if (gBitFlags[10] & gSaveContext.worldMapAreaData) { + pauseCtx->worldMapPoints[9] = 1; + } + + if (gSaveContext.eventChkInf[0] & 0x8000) { + pauseCtx->worldMapPoints[9] = 2; + } + + if (CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) { + pauseCtx->worldMapPoints[9] = 1; + } + + if (INV_CONTENT(ITEM_HOOKSHOT) == ITEM_HOOKSHOT) { + pauseCtx->worldMapPoints[9] = 2; + } + + if (gSaveContext.eventChkInf[4] & 0x100) { + pauseCtx->worldMapPoints[9] = 1; + } + + pauseCtx->worldMapPoints[10] = 2; + + if (gSaveContext.eventChkInf[0] & 0x200) { + pauseCtx->worldMapPoints[10] = 1; + } + + if (gSaveContext.eventChkInf[6] & 0x4000) { + pauseCtx->worldMapPoints[10] = 2; + } + + if (gSaveContext.eventChkInf[0] & 0x8000) { + pauseCtx->worldMapPoints[10] = 1; + } + + if (CHECK_QUEST_ITEM(QUEST_SONG_LULLABY)) { + pauseCtx->worldMapPoints[11] = 1; + } + + if (gSaveContext.eventChkInf[2] & 0x20) { + pauseCtx->worldMapPoints[11] = 2; + } + + if (gSaveContext.eventChkInf[3] & 0x80) { + pauseCtx->worldMapPoints[11] = 1; + } + + if (INV_CONTENT(ITEM_HOOKSHOT) == ITEM_HOOKSHOT) { + pauseCtx->worldMapPoints[11] = 2; + } + + if (CHECK_OWNED_EQUIP(EQUIP_BOOTS, 1)) { + pauseCtx->worldMapPoints[11] = 1; + } + + pauseCtx->tradeQuestLocation = 0xFF; + + i = INV_CONTENT(ITEM_TRADE_ADULT); + if (LINK_AGE_IN_YEARS == YEARS_ADULT) { + if ((i <= ITEM_POCKET_CUCCO) || (i == ITEM_ODD_MUSHROOM)) { + pauseCtx->tradeQuestLocation = 8; + } + if ((i == ITEM_COJIRO) || (i == ITEM_ODD_POTION)) { + pauseCtx->tradeQuestLocation = 9; + } + if (i == ITEM_SAW) { + pauseCtx->tradeQuestLocation = 2; + } + if ((i == ITEM_SWORD_BROKEN) || (i == ITEM_EYEDROPS)) { + pauseCtx->tradeQuestLocation = 7; + } + if (i == ITEM_PRESCRIPTION) { + pauseCtx->tradeQuestLocation = 11; + } + if (i == ITEM_FROG) { + pauseCtx->tradeQuestLocation = 3; + } + if ((i == ITEM_CLAIM_CHECK) && (gSaveContext.bgsFlag == 0)) { + pauseCtx->tradeQuestLocation = 7; + } + } + + pauseCtx->state = 4; + break; + + case 4: + if (pauseCtx->unk_1F4 == 160.0f) { + KaleidoScope_SetDefaultCursor(globalCtx); + //OTRTODO - Player on pause + //KaleidoScope_ProcessPlayerPreRender(); + } + + pauseCtx->unk_1F4 = pauseCtx->unk_1F8 = pauseCtx->unk_1FC = pauseCtx->unk_200 -= 160.0f / WREG(6); + pauseCtx->infoPanelOffsetY += 40 / WREG(6); + interfaceCtx->startAlpha += 255 / WREG(6); + WREG(16) += WREG(25) / WREG(6); + WREG(17) += WREG(26) / WREG(6); + XREG(5) += 150 / WREG(6); + pauseCtx->alpha += (u16)(255 / (WREG(6) + WREG(4))); + + if (pauseCtx->unk_1F4 == 0) { + interfaceCtx->startAlpha = 255; + WREG(2) = 0; + pauseCtx->state = 5; + } + + func_808265BC(globalCtx); + break; + + case 5: + pauseCtx->alpha += (u16)(255 / (WREG(6) + WREG(4))); + func_808265BC(globalCtx); + if (pauseCtx->state == 6) { + KaleidoScope_UpdateNamePanel(globalCtx); + } + break; + + case 6: + switch (pauseCtx->unk_1E4) { + case 0: + if (CHECK_BTN_ALL(input->press.button, BTN_START)) { + Interface_SetDoAction(globalCtx, DO_ACTION_NONE); + pauseCtx->state = 0x12; + WREG(2) = -6240; + func_800F64E0(0); + } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + pauseCtx->mode = 0; + pauseCtx->promptChoice = 0; + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_DISABLED; + gSaveContext.buttonStatus[4] = BTN_ENABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + pauseCtx->unk_1EC = 0; + pauseCtx->state = 7; + } + break; + + case 1: + func_808237B4(globalCtx, globalCtx->state.input); + break; + + case 2: + pauseCtx->ocarinaStaff = Audio_OcaGetDisplayingStaff(); + if (pauseCtx->ocarinaStaff->state == 0) { + pauseCtx->unk_1E4 = 4; + Audio_OcaSetInstrument(0); + } + break; + + case 3: + KaleidoScope_UpdateItemEquip(globalCtx); + break; + + case 4: + break; + + case 5: + pauseCtx->ocarinaStaff = Audio_OcaGetPlayingStaff(); + + if (CHECK_BTN_ALL(input->press.button, BTN_START)) { + Audio_OcaSetInstrument(0); + Interface_SetDoAction(globalCtx, DO_ACTION_NONE); + pauseCtx->state = 0x12; + WREG(2) = -6240; + func_800F64E0(0); + pauseCtx->unk_1E4 = 0; + break; + } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + Audio_OcaSetInstrument(0); + pauseCtx->unk_1E4 = 0; + pauseCtx->mode = 0; + pauseCtx->promptChoice = 0; + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_DISABLED; + gSaveContext.buttonStatus[4] = BTN_ENABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + pauseCtx->unk_1EC = 0; + pauseCtx->state = 7; + } else if (pauseCtx->ocarinaStaff->state == pauseCtx->ocarinaSongIdx) { + Audio_PlaySoundGeneral(NA_SE_SY_TRE_BOX_APPEAR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + D_8082B258 = 0; + D_8082B25C = 30; + pauseCtx->unk_1E4 = 6; + } else if (pauseCtx->ocarinaStaff->state == 0xFF) { + Audio_PlaySoundGeneral(NA_SE_SY_OCARINA_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + D_8082B258 = 4; + D_8082B25C = 20; + pauseCtx->unk_1E4 = 6; + } + break; + + case 6: + D_8082B25C--; + if (D_8082B25C == 0) { + pauseCtx->unk_1E4 = D_8082B258; + if (pauseCtx->unk_1E4 == 0) { + Audio_OcaSetInstrument(0); + } + } + break; + + case 7: + break; + + case 8: + if (CHECK_BTN_ALL(input->press.button, BTN_START)) { + Audio_OcaSetInstrument(0); + Interface_SetDoAction(globalCtx, DO_ACTION_NONE); + pauseCtx->state = 0x12; + WREG(2) = -6240; + func_800F64E0(0); + pauseCtx->unk_1E4 = 0; + } else if (CHECK_BTN_ALL(input->press.button, BTN_B)) { + Audio_OcaSetInstrument(0); + pauseCtx->unk_1E4 = 0; + pauseCtx->mode = 0; + pauseCtx->promptChoice = 0; + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_DISABLED; + gSaveContext.buttonStatus[4] = BTN_ENABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + pauseCtx->unk_1EC = 0; + pauseCtx->state = 7; + } + break; + + case 9: + break; + + default: + pauseCtx->unk_1E4 = 0; + break; + } + break; + + case 7: + switch (pauseCtx->unk_1EC) { + case 0: + pauseCtx->unk_204 -= 314.0f / WREG(6); + WREG(16) -= WREG(25) / WREG(6); + WREG(17) -= WREG(26) / WREG(6); + if (pauseCtx->unk_204 <= -628.0f) { + pauseCtx->unk_204 = -628.0f; + pauseCtx->unk_1EC = 1; + } + break; + + case 1: + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + if (pauseCtx->promptChoice != 0) { + Interface_SetDoAction(globalCtx, DO_ACTION_NONE); + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + pauseCtx->unk_1EC = 2; + WREG(2) = -6240; + YREG(8) = pauseCtx->unk_204; + func_800F64E0(0); + } else { + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Gameplay_SaveSceneFlags(globalCtx); + gSaveContext.savedSceneNum = globalCtx->sceneNum; + Sram_WriteSave(&globalCtx->sramCtx); + pauseCtx->unk_1EC = 4; + D_8082B25C = 3; + } + } else if (CHECK_BTN_ALL(input->press.button, BTN_START) || + CHECK_BTN_ALL(input->press.button, BTN_B)) { + Interface_SetDoAction(globalCtx, DO_ACTION_NONE); + pauseCtx->unk_1EC = 2; + WREG(2) = -6240; + YREG(8) = pauseCtx->unk_204; + func_800F64E0(0); + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + } + break; + + case 4: + if (CHECK_BTN_ALL(input->press.button, BTN_B) || CHECK_BTN_ALL(input->press.button, BTN_A) || + CHECK_BTN_ALL(input->press.button, BTN_START) || (--D_8082B25C == 0)) { + Interface_SetDoAction(globalCtx, DO_ACTION_NONE); + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(50); + pauseCtx->unk_1EC = 5; + WREG(2) = -6240; + YREG(8) = pauseCtx->unk_204; + func_800F64E0(0); + } + break; + + case 3: + case 6: + pauseCtx->unk_204 += 314.0f / WREG(6); + WREG(16) += WREG(25) / WREG(6); + WREG(17) += WREG(26) / WREG(6); + if (pauseCtx->unk_204 >= -314.0f) { + pauseCtx->state = 6; + pauseCtx->unk_1EC = 0; + pauseCtx->unk_1F4 = pauseCtx->unk_1F8 = pauseCtx->unk_1FC = pauseCtx->unk_200 = 0.0f; + pauseCtx->unk_204 = -314.0f; + } + break; + + case 2: + case 5: + if (pauseCtx->unk_204 != (YREG(8) + 160.0f)) { + pauseCtx->unk_1F4 = pauseCtx->unk_1F8 = pauseCtx->unk_1FC = pauseCtx->unk_200 += + 160.0f / WREG(6); + pauseCtx->unk_204 += 160.0f / WREG(6); + pauseCtx->infoPanelOffsetY -= 40 / WREG(6); + WREG(16) -= WREG(25) / WREG(6); + WREG(17) -= WREG(26) / WREG(6); + XREG(5) -= 150 / WREG(6); + pauseCtx->alpha -= (u16)(255 / WREG(6)); + if (pauseCtx->unk_204 == (YREG(8) + 160.0f)) { + pauseCtx->alpha = 0; + } + } else { + pauseCtx->debugState = 0; + pauseCtx->state = 0x13; + pauseCtx->unk_1F4 = pauseCtx->unk_1F8 = pauseCtx->unk_1FC = pauseCtx->unk_200 = 160.0f; + pauseCtx->namedItem = PAUSE_ITEM_NONE; + pauseCtx->unk_1E4 = 0; + pauseCtx->unk_204 = -434.0f; + } + break; + } + break; + + case 0xA: + pauseCtx->cursorSlot[PAUSE_MAP] = pauseCtx->cursorPoint[PAUSE_MAP] = pauseCtx->dungeonMapSlot = + VREG(30) + 3; + WREG(16) = -175; + WREG(17) = 155; + pauseCtx->unk_204 = -434.0f; + Interface_ChangeAlpha(1); + +#if 1 + pauseCtx->iconItemSegment = (void*)(((uintptr_t)globalCtx->objectCtx.spaceStart + 0x30) & ~0x3F); + size0 = (uintptr_t)_icon_item_staticSegmentRomEnd - (uintptr_t)_icon_item_staticSegmentRomStart; + osSyncPrintf("icon_item size0=%x\n", size0); + DmaMgr_SendRequest1(pauseCtx->iconItemSegment, (uintptr_t)_icon_item_staticSegmentRomStart, size0, + "../z_kaleido_scope_PAL.c", 4356); + + pauseCtx->iconItem24Segment = (void*)(((uintptr_t)pauseCtx->iconItemSegment + size0 + 0xF) & ~0xF); + //size = (uintptr_t)_icon_item_24_staticSegmentRomEnd - (uintptr_t)_icon_item_24_staticSegmentRomStart; + osSyncPrintf("icon_item24 size=%x\n", size); + //DmaMgr_SendRequest1(pauseCtx->iconItem24Segment, (uintptr_t)_icon_item_24_staticSegmentRomStart, size, + //"../z_kaleido_scope_PAL.c", 4363); + + pauseCtx->iconItemAltSegment = (void*)(((uintptr_t)pauseCtx->iconItem24Segment + size + 0xF) & ~0xF); + //size2 = (uintptr_t)_icon_item_gameover_staticSegmentRomEnd - (uintptr_t)_icon_item_gameover_staticSegmentRomStart; + //osSyncPrintf("icon_item_dungeon gameover-size2=%x\n", size2); + //DmaMgr_SendRequest1(pauseCtx->iconItemAltSegment, (uintptr_t)_icon_item_gameover_staticSegmentRomStart, size2, + //"../z_kaleido_scope_PAL.c", 4370); + + pauseCtx->iconItemLangSegment = (void*)(((uintptr_t)pauseCtx->iconItemAltSegment + size2 + 0xF) & ~0xF); + + /* + if (gSaveContext.language == LANGUAGE_ENG) { + size = (uintptr_t)_icon_item_nes_staticSegmentRomEnd - (uintptr_t)_icon_item_nes_staticSegmentRomStart; + osSyncPrintf("icon_item_dungeon dungeon-size=%x\n", size); + DmaMgr_SendRequest1(pauseCtx->iconItemLangSegment, (uintptr_t)_icon_item_nes_staticSegmentRomStart, size, + "../z_kaleido_scope_PAL.c", 4379); + } else if (gSaveContext.language == LANGUAGE_GER) { + size = (uintptr_t)_icon_item_ger_staticSegmentRomEnd - (uintptr_t)_icon_item_ger_staticSegmentRomStart; + osSyncPrintf("icon_item_dungeon dungeon-size=%x\n", size); + DmaMgr_SendRequest1(pauseCtx->iconItemLangSegment, (uintptr_t)_icon_item_ger_staticSegmentRomStart, size, + "../z_kaleido_scope_PAL.c", 4386); + } else { + size = (uintptr_t)_icon_item_fra_staticSegmentRomEnd - (uintptr_t)_icon_item_fra_staticSegmentRomStart; + osSyncPrintf("icon_item_dungeon dungeon-size=%x\n", size); + DmaMgr_SendRequest1(pauseCtx->iconItemLangSegment, (uintptr_t)_icon_item_fra_staticSegmentRomStart, size, + "../z_kaleido_scope_PAL.c", 4393); + } + */ +#endif + + D_8082AB8C = 255; + D_8082AB90 = 130; + D_8082AB94 = 0; + D_8082AB98 = 0; + D_8082AB9C = 30; + D_8082ABA0 = 0; + D_8082ABA4 = 0; + D_8082B260 = 30; + VREG(88) = 98; + pauseCtx->promptChoice = 0; + pauseCtx->state++; + break; + + case 0xB: + stepR = ABS(D_8082AB8C - 30) / D_8082B260; + stepG = ABS(D_8082AB90) / D_8082B260; + stepB = ABS(D_8082AB94) / D_8082B260; + stepA = ABS(D_8082AB98 - 255) / D_8082B260; + if (D_8082AB8C >= 30) { + D_8082AB8C -= stepR; + } else { + D_8082AB8C += stepR; + } + if (D_8082AB90 >= 0) { + D_8082AB90 -= stepG; + } else { + D_8082AB90 += stepG; + } + if (D_8082AB94 >= 0) { + D_8082AB94 -= stepB; + } else { + D_8082AB94 += stepB; + } + if (D_8082AB98 >= 255) { + D_8082AB98 -= stepA; + } else { + D_8082AB98 += stepA; + } + + stepR = ABS(D_8082AB9C - 255) / D_8082B260; + stepG = ABS(D_8082ABA0 - 130) / D_8082B260; + stepB = ABS(D_8082ABA4) / D_8082B260; + if (D_8082AB9C >= 255) { + D_8082AB9C -= stepR; + } else { + D_8082AB9C += stepR; + } + if (D_8082ABA0 >= 130) { + D_8082ABA0 -= stepG; + } else { + D_8082ABA0 += stepG; + } + if (D_8082ABA4 >= 0) { + D_8082ABA4 -= stepB; + } else { + D_8082ABA4 += stepB; + } + + D_8082B260--; + if (D_8082B260 == 0) { + D_8082AB8C = 30; + D_8082AB90 = 0; + D_8082AB94 = 0; + D_8082AB98 = 255; + + D_8082AB9C = 255; + D_8082ABA0 = 130; + D_8082ABA4 = 0; + + pauseCtx->state++; + D_8082B260 = 40; + } + break; + + case 0xC: + D_8082B260--; + if (D_8082B260 == 0) { + pauseCtx->state = 0xD; + } + break; + + case 0xD: + pauseCtx->unk_1F4 = pauseCtx->unk_1F8 = pauseCtx->unk_1FC = pauseCtx->unk_200 = pauseCtx->unk_204 -= + 160.0f / WREG(6); + pauseCtx->infoPanelOffsetY += 40 / WREG(6); + interfaceCtx->startAlpha += 255 / WREG(6); + VREG(88) -= 3; + WREG(16) += WREG(25) / WREG(6); + WREG(17) += WREG(26) / WREG(6); + XREG(5) += 150 / WREG(6); + pauseCtx->alpha += (u16)(255 / (WREG(6) + WREG(4))); + if (pauseCtx->unk_204 < -628.0f) { + pauseCtx->unk_204 = -628.0f; + interfaceCtx->startAlpha = 255; + VREG(88) = 66; + WREG(2) = 0; + pauseCtx->alpha = 255; + pauseCtx->state = 0xE; + gSaveContext.deaths++; + if (gSaveContext.deaths > 999) { + gSaveContext.deaths = 999; + } + } + osSyncPrintf("kscope->angle_s = %f\n", pauseCtx->unk_204); + break; + + case 0xE: + if (CHECK_BTN_ALL(input->press.button, BTN_A)) { + if (pauseCtx->promptChoice != 0) { + pauseCtx->promptChoice = 0; + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + pauseCtx->state = 0x10; + gameOverCtx->state++; + } else { + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + pauseCtx->promptChoice = 0; + Gameplay_SaveSceneFlags(globalCtx); + gSaveContext.savedSceneNum = globalCtx->sceneNum; + Sram_WriteSave(&globalCtx->sramCtx); + pauseCtx->state = 0xF; + D_8082B25C = 3; + } + } + break; + + case 0xF: + D_8082B25C--; + if (D_8082B25C == 0) { + pauseCtx->state = 0x10; + gameOverCtx->state++; + } else if ((D_8082B25C <= 80) && + (CHECK_BTN_ALL(input->press.button, BTN_A) || CHECK_BTN_ALL(input->press.button, BTN_START))) { + pauseCtx->state = 0x10; + gameOverCtx->state++; + func_800F64E0(0); + } + break; + + case 0x10: + if (CHECK_BTN_ALL(input->press.button, BTN_A) || CHECK_BTN_ALL(input->press.button, BTN_START)) { + if (pauseCtx->promptChoice == 0) { + Audio_PlaySoundGeneral(NA_SE_SY_PIECE_OF_HEART, &D_801333D4, 4, &D_801333E0, &D_801333E0, + &D_801333E8); + Gameplay_SaveSceneFlags(globalCtx); + + switch (gSaveContext.entranceIndex) { + case 0x0000: + case 0x0004: + case 0x0028: + case 0x0169: + case 0x0165: + case 0x0010: + case 0x0082: + case 0x0037: + case 0x041B: + case 0x0008: + case 0x0088: + case 0x0486: + case 0x0098: + case 0x0467: + case 0x0179: + break; + case 0x040F: + gSaveContext.entranceIndex = 0x0000; + break; + case 0x040B: + gSaveContext.entranceIndex = 0x0004; + break; + case 0x0301: + gSaveContext.entranceIndex = 0x0028; + break; + case 0x000C: + gSaveContext.entranceIndex = 0x0169; + break; + case 0x0305: + gSaveContext.entranceIndex = 0x0165; + break; + case 0x0417: + gSaveContext.entranceIndex = 0x0010; + break; + case 0x008D: + gSaveContext.entranceIndex = 0x0082; + break; + case 0x0413: + gSaveContext.entranceIndex = 0x0037; + break; + case 0x041F: + gSaveContext.entranceIndex = 0x041B; + break; + } + } else { + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); + } + + pauseCtx->state = 0x11; + } + break; + + case 0x11: + if (interfaceCtx->unk_244 != 255) { + interfaceCtx->unk_244 += 10; + if (interfaceCtx->unk_244 >= 255) { + interfaceCtx->unk_244 = 255; + pauseCtx->state = 0; + R_UPDATE_RATE = 3; + R_PAUSE_MENU_MODE = 0; + func_800981B8(&globalCtx->objectCtx); + func_800418D0(&globalCtx->colCtx, globalCtx); + if (pauseCtx->promptChoice == 0) { + Gameplay_TriggerRespawn(globalCtx); + gSaveContext.respawnFlag = -2; + gSaveContext.nextTransition = 2; + gSaveContext.health = 0x30; + Audio_QueueSeqCmd(0xF << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0xA); + gSaveContext.healthAccumulator = 0; + gSaveContext.unk_13F0 = 0; + gSaveContext.unk_13F2 = 0; + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("MAGIC_NOW=%d ", gSaveContext.magic); + osSyncPrintf("Z_MAGIC_NOW_NOW=%d → ", gSaveContext.unk_13F6); + gSaveContext.unk_13F4 = 0; + gSaveContext.unk_13F6 = gSaveContext.magic; + gSaveContext.magicLevel = gSaveContext.magic = 0; + osSyncPrintf("MAGIC_NOW=%d ", gSaveContext.magic); + osSyncPrintf("Z_MAGIC_NOW_NOW=%d\n", gSaveContext.unk_13F6); + osSyncPrintf(VT_RST); + } else { + globalCtx->state.running = 0; + SET_NEXT_GAMESTATE(&globalCtx->state, Opening_Init, OpeningContext); + } + } + } + break; + + case 0x12: + if (pauseCtx->unk_1F4 != 160.0f) { + pauseCtx->unk_1F4 = pauseCtx->unk_1F8 = pauseCtx->unk_1FC = pauseCtx->unk_200 += 160.0f / WREG(6); + pauseCtx->infoPanelOffsetY -= 40 / WREG(6); + interfaceCtx->startAlpha -= 255 / WREG(6); + WREG(16) -= WREG(25) / WREG(6); + WREG(17) -= WREG(26) / WREG(6); + XREG(5) -= 150 / WREG(6); + pauseCtx->alpha -= (u16)(255 / WREG(6)); + if (pauseCtx->unk_1F4 == 160.0f) { + pauseCtx->alpha = 0; + } + } else { + pauseCtx->debugState = 0; + pauseCtx->state = 0x13; + pauseCtx->unk_200 = 160.0f; + pauseCtx->unk_1FC = 160.0f; + pauseCtx->unk_1F8 = 160.0f; + pauseCtx->unk_1F4 = 160.0f; + pauseCtx->namedItem = PAUSE_ITEM_NONE; + globalCtx->interfaceCtx.startAlpha = 0; + } + break; + + case 0x13: + pauseCtx->state = 0; + R_UPDATE_RATE = 3; + R_PAUSE_MENU_MODE = 0; + + ResourceMgr_DirtyDirectory("textures/icon_item_24_static*"); + ResourceMgr_DirtyDirectory("textures/icon_item_static*"); + CVar_SetS32("gPauseTriforce", 0); + //ResourceMgr_InvalidateCache(); + + func_800981B8(&globalCtx->objectCtx); + func_800418D0(&globalCtx->colCtx, globalCtx); + + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + Map_InitData(globalCtx, globalCtx->interfaceCtx.mapRoomNum); + break; + } + + gSaveContext.buttonStatus[0] = D_808321A8[0]; + gSaveContext.buttonStatus[1] = D_808321A8[1]; + gSaveContext.buttonStatus[2] = D_808321A8[2]; + gSaveContext.buttonStatus[3] = D_808321A8[3]; + gSaveContext.buttonStatus[4] = D_808321A8[4]; + interfaceCtx->unk_1FA = interfaceCtx->unk_1FC = 0; + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("i=%d LAST_TIME_TYPE=%d\n", i, gSaveContext.unk_13EE); + gSaveContext.unk_13EA = 0; + Interface_ChangeAlpha(gSaveContext.unk_13EE); + player->targetActor = NULL; + Player_SetEquipmentData(globalCtx, player); + osSyncPrintf(VT_RST); + break; + } +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c new file mode 100644 index 000000000..19f6f5f08 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark.c @@ -0,0 +1,169 @@ +#include "z_kaleido_scope.h" +#include "textures/parameter_static/parameter_static.h" + +typedef struct { + /* 0x00 */ void* texture; + /* 0x04 */ u32 imageFormat; + /* 0x08 */ u32 imageSize; + /* 0x0C */ u32 textureWidth; + /* 0x10 */ u32 textureHeight; + /* 0x14 */ u32 rectWidth; + /* 0x18 */ u32 rectHeight; + /* 0x1C */ u32 dsdx; + /* 0x20 */ u32 dtdy; +} PauseMapMarkInfo; // size = 0x24 + +static PauseMapMarkInfo sMapMarkInfoTable[] = { + { gMapChestIconTex, G_IM_FMT_RGBA, G_IM_SIZ_16b, 8, 8, 32, 32, 1 << 10, 1 << 10 }, + { gMapBossIconTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 8, 32, 32, 1 << 10, 1 << 10 }, +}; + +static const u32 sBaseImageSizes[] = { 0, 1, 2, 3 }; +static const u32 sLoadBlockImageSizes[] = { 2, 2, 2, 3 }; +static const u32 sIncrImageSizes[] = { 3, 1, 0, 0 }; +static const u32 sShiftImageSizes[] = { 2, 1, 0, 0 }; +static const u32 sBytesImageSizes[] = { 0, 1, 2, 4 }; +static const u32 sLineBytesImageSizes[] = { 0, 1, 2, 2 }; + +#define G_IM_SIZ_MARK sBaseImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_LOAD_BLOCK sLoadBlockImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_INCR sIncrImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_SHIFT sShiftImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_BYTES sBytesImageSizes[markInfo->imageSize] +#define G_IM_SIZ_MARK_LINE_BYTES sLineBytesImageSizes[markInfo->imageSize] + +extern PauseMapMarksData gPauseMapMarkDataTable[]; + +void PauseMapMark_Init(GlobalContext* globalCtx) { + gBossMarkState = 0; + gBossMarkScale = 1.0f; + gLoadedPauseMarkDataTable = gPauseMapMarkDataTable; +} + +void PauseMapMark_Clear(GlobalContext* globalCtx) { + gLoadedPauseMarkDataTable = NULL; +} + +void PauseMapMark_DrawForDungeon(GlobalContext* globalCtx) { + PauseMapMarkData* mapMarkData; + PauseMapMarkPoint* markPoint; + PauseMapMarkInfo* markInfo; + f32 scale; + s32 i = 0; + + mapMarkData = &gLoadedPauseMarkDataTable[R_MAP_TEX_INDEX >> 1][i]; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_lmap_mark.c", 182); + + while (true) { + if (mapMarkData->markType == PAUSE_MAP_MARK_NONE) { + break; + } + + if ((mapMarkData->markType == PAUSE_MAP_MARK_BOSS) && (globalCtx->sceneNum >= SCENE_YDAN_BOSS) && + (globalCtx->sceneNum <= SCENE_GANON_FINAL)) { + if (gBossMarkState == 0) { + Math_ApproachF(&gBossMarkScale, 1.5f, 1.0f, 0.041f); + if (gBossMarkScale == 1.5f) { + gBossMarkState = 1; + } + } else { + Math_ApproachF(&gBossMarkScale, 1.0f, 1.0f, 0.041f); + if (gBossMarkScale == 1.0f) { + gBossMarkState = 0; + } + } + scale = gBossMarkScale; + } else { + scale = 1.0f; + } + + Matrix_Push(); + + if ((globalCtx->pauseCtx.state == 4) || (globalCtx->pauseCtx.state >= 0x12)) { + Matrix_Translate(-36.0f, 101.0f, 0.0f, MTXMODE_APPLY); + } else { + Matrix_Translate(-36.0f, 21.0f, 0.0f, MTXMODE_APPLY); + } + + gDPPipeSync(POLY_KAL_DISP++); + gDPSetPrimColor(POLY_KAL_DISP++, 0, 0, 255, 255, 255, 255); + gDPSetEnvColor(POLY_KAL_DISP++, 0, 0, 0, 255); + + markPoint = &mapMarkData->points[0]; + for (i = 0; i < mapMarkData->count; i++) { + s32 display; + + if (mapMarkData->markType == PAUSE_MAP_MARK_CHEST) { + if (Flags_GetTreasure(globalCtx, markPoint->chestFlag)) { + display = false; + } else { + switch (globalCtx->sceneNum) { + case SCENE_YDAN_BOSS: + case SCENE_DDAN_BOSS: + case SCENE_BDAN_BOSS: + case SCENE_MORIBOSSROOM: + case SCENE_FIRE_BS: + case SCENE_MIZUSIN_BS: + case SCENE_JYASINBOSS: + case SCENE_HAKADAN_BS: + display = false; + break; + default: + display = true; + break; + } + } + } else { + display = true; + } + + if (display) { + markInfo = &sMapMarkInfoTable[mapMarkData->markType]; + + gDPPipeSync(POLY_KAL_DISP++); + gDPLoadTextureBlock(POLY_KAL_DISP++, markInfo->texture, markInfo->imageFormat, G_IM_SIZ_MARK, + markInfo->textureWidth, markInfo->textureHeight, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + Matrix_Push(); + Matrix_Translate(GREG(92) + markPoint->x, GREG(93) + markPoint->y, 0.0f, MTXMODE_APPLY); + Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); + gSPMatrix(POLY_KAL_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_lmap_mark.c", 272), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + Matrix_Pop(); + + gSPVertex(POLY_KAL_DISP++, mapMarkData->vtx, mapMarkData->vtxCount, 0); + gSP1Quadrangle(POLY_KAL_DISP++, 1, 3, 2, 0, 0); + } + + markPoint++; + } + + mapMarkData++; + Matrix_Pop(); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_lmap_mark.c", 286); +} + +void PauseMapMark_Draw(GlobalContext* globalCtx) { + PauseMapMark_Init(globalCtx); + + switch (globalCtx->sceneNum) { + case SCENE_YDAN: + case SCENE_DDAN: + case SCENE_BDAN: + case SCENE_BMORI1: + case SCENE_HIDAN: + case SCENE_MIZUSIN: + case SCENE_JYASINZOU: + case SCENE_HAKADAN: + case SCENE_HAKADANCH: + case SCENE_ICE_DOUKUTO: + PauseMapMark_DrawForDungeon(globalCtx); + break; + } + + PauseMapMark_Clear(globalCtx); +} diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark_data.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark_data.c new file mode 100644 index 000000000..df96fc876 --- /dev/null +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_lmap_mark_data.c @@ -0,0 +1,531 @@ +#include "z_kaleido_scope.h" + +static const Vtx sMarkBossVtx[] = { + VTX(-4, 4, 0, 0, 0, 255, 255, 255, 255), + VTX(-4, -4, 0, 0, 256, 255, 255, 255, 255), + VTX(4, 4, 0, 256, 0, 255, 255, 255, 255), + VTX(4, -4, 0, 256, 256, 255, 255, 255, 255), +}; + +static const Vtx sMarkChestVtx[] = { + VTX(-4, 4, 0, 0, 0, 255, 255, 255, 255), + VTX(-4, -4, 0, 0, 256, 255, 255, 255, 255), + VTX(4, 4, 0, 256, 0, 255, 255, 255, 255), + VTX(4, -4, 0, 256, 256, 255, 255, 255, 255), +}; + +PauseMapMarksData gPauseMapMarkDataTable[] = { + // Deku Tree map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 2, + { + { 2, 40.0f, -33.0f }, + { 6, 49.0f, -42.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Deku Tree map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 1, 48.0f, -63.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Deku Tree map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 3, 84.0f, -39.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Deku Tree map 3 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 3, + { + { 0, 46.0f, -59.0f }, + { 4, 77.0f, -26.0f }, + { 5, 65.0f, -61.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Deku Tree map 4 + { + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 55.0f, 0.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Dodongo's Cavern map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 3, + { + { 2, 55.0f, -36.0f }, + { 3, 54.0f, -51.0f }, + { 5, 13.0f, -61.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Dodongo's Cavern map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 3, + { + { 0, 47.0f, -40.0f }, + { 1, 51.0f, -3.0f }, + { 4, 47.0f, -47.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 23.0f, -25.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Jabu-Jabu's Belly map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 5, + { + { 3, 48.0f, -68.0f }, + { 5, 50.0f, -66.0f }, + { 7, 55.0f, -50.0f }, + { 9, 58.0f, 1.0f }, + { 10, 62.0f, -45.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 65.0f, -37.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Jabu-Jabu's Belly map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 6, + { + { 0, 37.0f, -49.0f }, + { 1, 65.0f, -38.0f }, + { 2, 52.0f, -48.0f }, + { 4, 46.0f, -36.0f }, + { 6, 59.0f, -41.0f }, + { 8, 52.0f, -26.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Forest Temple map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 6, + { + { 3, 53.0f, -64.0f }, + { 5, 65.0f, -9.0f }, + { 12, 49.0f, -1.0f }, + { 13, 40.0f, 0.0f }, + { 14, 18.0f, -2.0f }, + { 15, 59.0f, 0.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Forest Temple map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 4, + { + { 0, 49.0f, -1.0f }, + { 1, 71.0f, -13.0f }, + { 2, 11.0f, -25.0f }, + { 6, 84.0f, -16.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Forest Temple map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 9, 65.0f, -30.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Forest Temple map 3 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 11, 41.0f, -24.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 50.0f, -11.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Fire Temple map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 5, 24.0f, -40.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Fire Temple map 1 + { + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Fire Temple map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 3, + { + { 3, 75.0f, -47.0f }, + { 6, 72.0f, -51.0f }, + { 8, 65.0f, -12.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Fire Temple map 3 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 11, 78.0f, -35.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Fire Temple map 4 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 5, + { + { 1, 67.0f, -58.0f }, + { 2, 48.0f, -30.0f }, + { 4, 63.0f, -14.0f }, + { 7, 36.0f, -45.0f }, + { 12, 47.0f, -26.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 26.0f, -34.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Water Temple map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 2, 88.0f, -60.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 62.0f, -23.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Water Temple map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 0, 88.0f, -60.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Water Temple map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 2, + { + { 1, 88.0f, -60.0f }, + { 5, 49.0f, -43.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Water Temple map 3 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 6, 75.0f, -65.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Spirit Temple map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 18, 46.0f, -30.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Spirit Temple map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 5, + { + { 1, 23.0f, -33.0f }, + { 2, 56.0f, -11.0f }, + { 5, 83.0f, -25.0f }, + { 24, 84.0f, -39.0f }, + { 25, 74.0f, -37.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 47.0f, 0.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Spirit Temple map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 5, + { + { 3, 46.0f, -20.0f }, + { 6, 28.0f, -19.0f }, + { 12, 25.0f, -25.0f }, + { 15, 50.0f, -13.0f }, + { 28, 48.0f, -29.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Spirit Temple map 3 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 9, + { + { 0, 14.0f, -24.0f }, + { 4, 55.0f, -14.0f }, + { 7, 78.0f, -2.0f }, + { 8, 14.0f, -16.0f }, + { 26, 42.0f, -43.0f }, + { 27, 50.0f, -43.0f }, + { 29, 25.0f, -35.0f }, + { 30, 42.0f, -36.0f }, + { 31, 50.0f, -36.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Shadow Temple map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 2, + { + { 1, 41.0f, -17.0f }, + { 7, 27.0f, -24.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Shadow Temple map 1 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 2, + { + { 2, 81.0f, -20.0f }, + { 3, 74.0f, -37.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Shadow Temple map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 2, + { + { 12, 96.0f, -51.0f }, + { 16, 46.0f, -42.0f }, + { 22, 96.0f, -55.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Shadow Temple map 3 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 12, + { + { 4, 43.0f, -66.0f }, + { 5, 37.0f, -66.0f }, + { 6, 33.0f, -72.0f }, + { 8, 85.0f, -18.0f }, + { 9, 61.0f, -42.0f }, + { 10, 15.0f, -4.0f }, + { 11, 25.0f, -4.0f }, + { 13, 19.0f, -29.0f }, + { 14, 78.0f, -15.0f }, + { 15, 60.0f, -70.0f }, + { 21, 92.0f, -29.0f }, + { 20, 87.0f, -20.0f }, + } }, + { PAUSE_MAP_MARK_BOSS, + 23, + sMarkBossVtx, + 4, + 1, + { + { -1, 31.0f, -45.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Bottom of the Well map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 2, + { + { 2, 84.0f, -38.0f }, + { 3, 57.0f, -18.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Bottom of the Well map 1 + { + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Bottom of the Well map 2 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 1, + { + { 1, 72.0f, -32.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, + // Ice Cavern map 0 + { + { PAUSE_MAP_MARK_CHEST, + 23, + sMarkChestVtx, + 4, + 3, + { + { 0, 66.0f, -2.0f }, + { 1, 77.0f, -46.0f }, + { 2, 27.0f, -45.0f }, + } }, + { PAUSE_MAP_MARK_NONE, 0, NULL, 0, 0, { 0 } }, + }, +}; diff --git a/soh/src/overlays/misc/ovl_map_mark_data/z_map_mark_data.c b/soh/src/overlays/misc/ovl_map_mark_data/z_map_mark_data.c new file mode 100644 index 000000000..013b8b7b3 --- /dev/null +++ b/soh/src/overlays/misc/ovl_map_mark_data/z_map_mark_data.c @@ -0,0 +1,1457 @@ +#include "global.h" + +static MapMarkData sMapMarkDekuTree[] = { + // Deku Tree minimap 0 + { + { MAP_MARK_CHEST, + 1, + { + { 3, 71, 50 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 2 + { + { MAP_MARK_CHEST, + 2, + { + { 1, 64, 62 }, + { 5, 71, 69 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 3 + { + { MAP_MARK_CHEST, + 1, + { + { 4, 76, 37 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 4 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 7 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 8 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 9 + { + { MAP_MARK_BOSS, + 1, + { + { -1, 50, 23 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 10 + { + { MAP_MARK_CHEST, + 2, + { + { 2, 46, 50 }, + { 6, 58, 60 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 11 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Deku Tree minimap 12 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkDodongosCavern[] = { + // Dodongo's Cavern minimap 0 + { + { MAP_MARK_CHEST, + 1, + { + { 8, 22, 32 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 2 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 3 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 4 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 7 + { + { MAP_MARK_BOSS, + 1, + { + { -1, 37, 49 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 8 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 9 + { + { MAP_MARK_CHEST, + 2, + { + { 4, 58, 40 }, + { 6, 65, 64 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 10 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 11 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 12 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 13 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 14 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 15 + { + { MAP_MARK_CHEST, + 1, + { + { 5, 68, 64 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 16 + { + { MAP_MARK_CHEST, + 1, + { + { 10, 31, 27 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 17 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Dodongo's Cavern minimap 18 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkJabuJabuBelly[] = { + // Jabu-Jabu's Belly minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 2 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 3 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 4 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 5 + { + { MAP_MARK_BOSS, + 1, + { + { -1, 67, 32 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 7 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 8 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 9 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 74, 57 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 10 + { + { MAP_MARK_CHEST, + 1, + { + { 2, 59, 57 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 11 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 12 + { + { MAP_MARK_CHEST, + 1, + { + { 4, 68, 51 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 13 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 14 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 15 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Jabu-Jabu's Belly minimap 16 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkForestTemple[] = { + // Forest Temple minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 2 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 3 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 4 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 6 + { + { MAP_MARK_CHEST, + 1, + { + { 0, 72, 60 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 7 + { + { MAP_MARK_CHEST, + 1, + { + { 5, 76, 38 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 8 + { + { MAP_MARK_CHEST, + 1, + { + { 9, 59, 66 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 9 + { + { MAP_MARK_CHEST, + 1, + { + { 9, 15, 58 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 10 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 69, 56 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 11 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 12 + { + { MAP_MARK_CHEST, + 1, + { + { 13, 80, 54 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 13 + { + { MAP_MARK_CHEST, + 1, + { + { 15, 49, 50 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 14 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 15 + { + { MAP_MARK_CHEST, + 1, + { + { 7, 61, 61 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 16 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 17 + { + { MAP_MARK_CHEST, + 1, + { + { 11, 39, 63 }, + } }, + { MAP_MARK_BOSS, + 1, + { + { -1, 53, 5 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 18 + { + { MAP_MARK_CHEST, + 1, + { + { 2, 66, 57 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 19 + { + { MAP_MARK_CHEST, + 1, + { + { 14, 64, 31 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 20 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 21 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 22 + { + { MAP_MARK_CHEST, + 1, + { + { 3, 63, 56 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 23 + { + { MAP_MARK_CHEST, + 1, + { + { 12, 69, 59 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 24 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 25 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Forest Temple minimap 26 + { + { MAP_MARK_CHEST, + 1, + { + { 4, 73, 54 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkFireTemple[] = { + // Fire Temple minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 2 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 53, 70 }, + } }, + { MAP_MARK_BOSS, + 1, + { + { -1, 40, 47 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 3 + { + { MAP_MARK_CHEST, + 1, + { + { 0, 71, 60 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 4 + { + { MAP_MARK_CHEST, + 1, + { + { 11, 59, 46 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 5 + { + { MAP_MARK_CHEST, + 2, + { + { 6, 57, 73 }, + { 3, 66, 71 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 7 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 8 + { + { MAP_MARK_CHEST, + 1, + { + { 13, 72, 38 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 9 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 10 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 11 + { + { MAP_MARK_CHEST, + 1, + { + { 9, 80, 61 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 12 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 13 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 14 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 15 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 16 + { + { MAP_MARK_CHEST, + 1, + { + { 10, 71, 36 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 17 + { + { MAP_MARK_CHEST, + 1, + { + { 12, 77, 58 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 18 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 19 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 20 + { + { MAP_MARK_CHEST, + 1, + { + { 4, 78, 59 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 21 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 22 + { + { MAP_MARK_CHEST, + 1, + { + { 2, 71, 68 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 23 + { + { MAP_MARK_CHEST, + 1, + { + { 8, 66, 58 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 24 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 25 + { + { MAP_MARK_CHEST, + 1, + { + { 7, 77, 54 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 26 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 27 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 28 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 29 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 30 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 31 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 32 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 33 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 34 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 35 + { + { MAP_MARK_CHEST, + 1, + { + { 5, 49, 62 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 36 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Fire Temple minimap 37 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkWaterTemple[] = { + // Water Temple minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 2 + { + { MAP_MARK_CHEST, + 1, + { + { 6, 79, 68 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 3 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 4 + { + { MAP_MARK_CHEST, + 1, + { + { 9, 81, 62 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 7 + { + { MAP_MARK_CHEST, + 1, + { + { 7, 74, 62 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 8 + { + { MAP_MARK_CHEST, + 1, + { + { 10, 57, 66 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 9 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 10 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 11 + { + { MAP_MARK_BOSS, + 1, + { + { -1, 77, 40 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 12 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 13 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 14 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 15 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 16 + { + { MAP_MARK_CHEST, + 1, + { + { 5, 76, 64 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 17 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 18 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 74, 61 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 19 + { + { MAP_MARK_CHEST, + 1, + { + { 2, 74, 63 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 20 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 21 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 22 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 23 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 24 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 25 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 26 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 27 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 28 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 29 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 30 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 31 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 32 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 33 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 34 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 35 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 36 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 37 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 38 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 39 + { + { MAP_MARK_CHEST, + 1, + { + { 0, 74, 64 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 40 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 41 + { + { MAP_MARK_CHEST, + 1, + { + { 8, 75, 67 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 42 + { + { MAP_MARK_CHEST, + 1, + { + { 3, 82, 65 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Water Temple minimap 43 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkSpiritTemple[] = { + // Spirit Temple minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 2 + { + { MAP_MARK_CHEST, + 1, + { + { 0, 78, 43 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 3 + { + { MAP_MARK_CHEST, + 1, + { + { 8, 69, 40 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 4 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 5 + { + { MAP_MARK_CHEST, + 2, + { + { 2, 43, 50 }, + { 3, 56, 54 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 7 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 8 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 70, 67 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 9 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 10 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 11 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 12 + { + { MAP_MARK_CHEST, + 1, + { + { 7, 70, 58 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 13 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 14 + { + { MAP_MARK_CHEST, + 1, + { + { 4, 68, 42 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 15 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 16 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 17 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 18 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 19 + { + { MAP_MARK_CHEST, + 2, + { + { 20, 75, 58 }, + { 21, 83, 58 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 20 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 21 + { + { MAP_MARK_CHEST, + 1, + { + { 5, 71, 55 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 22 + { + { MAP_MARK_CHEST, + 1, + { + { 10, 72, 49 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 23 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 24 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 25 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 26 + { + { MAP_MARK_CHEST, + 1, + { + { 18, 50, 41 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 27 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 28 + { + { MAP_MARK_CHEST, + 2, + { + { 6, 73, 49 }, + { 12, 79, 55 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 29 + { + { MAP_MARK_CHEST, + 1, + { + { 15, 77, 42 }, + } }, + { MAP_MARK_BOSS, + 1, + { + { -1, 57, 23 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 30 + { + { MAP_MARK_CHEST, + 2, + { + { 13, 77, 63 }, + { 14, 77, 68 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 31 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Spirit Temple minimap 32 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkShadowTemple[] = { + // Shadow Temple minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 1 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 77, 64 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 2 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 3 + { + { MAP_MARK_BOSS, + 1, + { + { -1, 77, 76 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 4 + { + { MAP_MARK_CHEST, + 1, + { + { 7, 76, 65 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 6 + { + { MAP_MARK_CHEST, + 1, + { + { 2, 83, 67 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 7 + { + { MAP_MARK_CHEST, + 1, + { + { 3, 76, 67 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 8 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 9 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 10 + { + { MAP_MARK_CHEST, + 3, + { + { 4, 78, 62 }, + { 5, 74, 62 }, + { 6, 71, 68 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 11 + { + { MAP_MARK_CHEST, + 1, + { + { 9, 77, 64 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 12 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 13 + { + { MAP_MARK_CHEST, + 2, + { + { 10, 71, 65 }, + { 11, 80, 65 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 14 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 15 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 16 + { + { MAP_MARK_CHEST, + 2, + { + { 12, 87, 64 }, + { 22, 87, 68 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 17 + { + { MAP_MARK_CHEST, + 1, + { + { 13, 77, 66 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 18 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 19 + { + { MAP_MARK_CHEST, + 1, + { + { 21, 78, 66 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 20 + { + { MAP_MARK_CHEST, + 2, + { + { 8, 76, 66 }, + { 20, 78, 68 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 21 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 22 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 23 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 24 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 25 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Shadow Temple minimap 26 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkBottomWell[] = { + // Bottom of the Well minimap 0 + { + { MAP_MARK_CHEST, + 6, + { + { 1, 56, 26 }, + { 4, 39, 3 }, + { 5, 69, 30 }, + { 8, 53, 30 }, + { 12, 83, 16 }, + { 14, 67, 26 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 2 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 3 + { + { MAP_MARK_CHEST, + 1, + { + { 10, 79, 67 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 4 + { + { MAP_MARK_CHEST, + 2, + { + { 3, 73, 62 }, + { 20, 81, 62 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 7 + { + { MAP_MARK_CHEST, + 3, + { + { 2, 59, 28 }, + { 9, 36, 19 }, + { 16, 62, 38 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 8 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Bottom of the Well minimap 9 + { + { MAP_MARK_CHEST, + 1, + { + { 7, 75, 66 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +static MapMarkData sMapMarkIceCavern[] = { + // Ice Cavern minimap 0 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 1 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 2 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 3 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 4 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 5 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 6 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 7 + { + { MAP_MARK_CHEST, + 1, + { + { 2, 71, 59 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 8 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 9 + { + { MAP_MARK_CHEST, + 1, + { + { 0, 48, 36 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 10 + { + { MAP_MARK_NONE, 0, { 0 } }, + }, + // Ice Cavern minimap 11 + { + { MAP_MARK_CHEST, + 1, + { + { 1, 73, 67 }, + } }, + { MAP_MARK_NONE, 0, { 0 } }, + }, +}; + +MapMarkData* gMapMarkDataTable[] = { + sMapMarkDekuTree, sMapMarkDodongosCavern, sMapMarkJabuJabuBelly, sMapMarkForestTemple, sMapMarkFireTemple, + sMapMarkWaterTemple, sMapMarkSpiritTemple, sMapMarkShadowTemple, sMapMarkBottomWell, sMapMarkIceCavern, +};